Thread (17 messages) 17 messages, 5 authors, 2012-11-02

[PATCH RESEND 1/5 v6] gpio: Add a block GPIO API to gpiolib

From: Linus Walleij <hidden>
Date: 2012-10-31 14:06:53
Also in: lkml

On Sun, Oct 28, 2012 at 9:46 PM, Roland Stigge [off-list ref] wrote:

Just a quick few things I spotted:
+struct gpio_block *gpio_block_create(unsigned *gpios, size_t size,
+                                    const char *name)
+{
+       struct gpio_block *block;
+       struct gpio_block_chip *gbc;
+       struct gpio_remap *remap;
+       void *tmp;
+       int i;
+
+       if (size < 1 || size > sizeof(unsigned long) * 8)
+               return ERR_PTR(-EINVAL);
If the most GPIOs in a block is BITS_PER_LONG, why is the
latter clause not size > BITS_PER_LONG?

Maybe there was some discussion I missed, anyway it deserves
a comment because this looks completely arbitrary.
+       for (i = 0; i < size; i++)
+               if (!gpio_is_valid(gpios[i]))
+                       return ERR_PTR(-EINVAL);
+
+       block = kzalloc(sizeof(struct gpio_block), GFP_KERNEL);
+       if (!block)
+               return ERR_PTR(-ENOMEM);
If these were instead glued to a struct device you could do
devm_kzalloc() here and have it garbage collected.

This is why
https://blueprints.launchpad.net/linux-linaro/+spec/gpiochip-to-dev
needs to happen. (Sorry for hyperlinking.)
+       block->name = name;
+       block->ngpio = size;
+       block->gpio = kzalloc(sizeof(*block->gpio) * size, GFP_KERNEL);
+       if (!block->gpio)
+               goto err1;
+
+       memcpy(block->gpio, gpios, sizeof(*block->gpio) * size);
+
+       for (i = 0; i < size; i++) {
+               struct gpio_chip *gc = gpio_to_chip(gpios[i]);
+               int bit = gpios[i] - gc->base;
+               int index = gpio_block_chip_index(block, gc);
+
+               if (index < 0) {
+                       block->nchip++;
+                       tmp = krealloc(block->gbc,
+                                      sizeof(struct gpio_block_chip) *
+                                      block->nchip, GFP_KERNEL);
Don't do this dynamic array business. Use a linked list instead.
+                       if (!tmp) {
+                               kfree(block->gbc);
+                               goto err2;
+                       }
+                       block->gbc = tmp;
+                       gbc = &block->gbc[block->nchip - 1];
So this becomes a list traversal and lookup instead.
+                       gbc->gc = gc;
+                       gbc->remap = NULL;
+                       gbc->nremap = 0;
+                       gbc->mask = 0;
+               } else {
+                       gbc = &block->gbc[index];
So find it in the list instead.
+               }
+               /* represents the mask necessary on calls to the driver's
+                * .get_block() and .set_block()
+                */
+               gbc->mask |= BIT(bit);
+
+               /* collect gpios that are specified together, represented by
+                * neighboring bits
+                *
+                * Note that even though in setting remap is given a negative
+                * index, the next lines guard that the potential out-of-bounds
+                * pointer is not dereferenced when out of bounds.
+                */

/*
 * Maybe I'm a bit picky on comment style but I prefer
 * that the first line of a multi-line comment is blank.
 * Applies everywhere.
 */
+               remap = &gbc->remap[gbc->nremap - 1];
+               if (!gbc->nremap || (bit - i != remap->offset)) {
+                       gbc->nremap++;
+                       tmp = krealloc(gbc->remap,
+                                             sizeof(struct gpio_remap) *
+                                             gbc->nremap, GFP_KERNEL);
I don't like this dynamic array either.
Basic pattern: wherever there is a krealloc, use a list.

If lists aren't efficient enough, use some other datastructure, rbtree or
whatever.
+                       if (!tmp) {
+                               kfree(gbc->remap);
+                               goto err3;
+                       }
+                       gbc->remap = tmp;
+                       remap = &gbc->remap[gbc->nremap - 1];
+                       remap->offset = bit - i;
+                       remap->mask = 0;
+               }
+
+               /* represents the mask necessary for bit reordering between
+                * gpio_block (i.e. as specified on gpio_block_get() and
+                * gpio_block_set()) and gpio_chip domain (i.e. as specified on
+                * the driver's .set_block() and .get_block())
+                */
+               remap->mask |= BIT(i);
+       }
+
+       return block;
+err3:
+       for (i = 0; i < block->nchip - 1; i++)
+               kfree(block->gbc[i].remap);
+       kfree(block->gbc);
+err2:
+       kfree(block->gpio);
+err1:
+       kfree(block);
+       return ERR_PTR(-ENOMEM);
+}
+EXPORT_SYMBOL_GPL(gpio_block_create);
+
+void gpio_block_free(struct gpio_block *block)
+{
+       int i;
+
+       for (i = 0; i < block->nchip; i++)
+               kfree(block->gbc[i].remap);
+       kfree(block->gpio);
+       kfree(block->gbc);
+       kfree(block);
+}
+EXPORT_SYMBOL_GPL(gpio_block_free);
So if we only had a real struct device  inside the gpiochip all
this boilerplate would go away, as devm_* take care of releasing
the resources.
+unsigned long gpio_block_get(const struct gpio_block *block)
+{
+       struct gpio_block_chip *gbc;
+       int i, j;
+       unsigned long values = 0;
+
+       for (i = 0; i < block->nchip; i++) {
+               unsigned long remapped = 0;
+
+               gbc = &block->gbc[i];
+
+               if (gbc->gc->get_block) {
+                       remapped = gbc->gc->get_block(gbc->gc, gbc->mask);
+               } else {
+                       /* emulate */
+                       for_each_set_bit(j, &gbc->mask, BITS_PER_LONG)
+                               remapped |= gbc->gc->get(gbc->gc,
+                                               gbc->gc->base + j) << j;
+               }
+
+               for (j = 0; j < gbc->nremap; j++) {
+                       struct gpio_remap *gr = &gbc->remap[j];
+
+                       values |= (remapped >> gr->offset) & gr->mask;
+               }
+       }
+
+       return values;
+}
+EXPORT_SYMBOL_GPL(gpio_block_get);
+
+void gpio_block_set(struct gpio_block *block, unsigned long values)
+{
+       struct gpio_block_chip *gbc;
+       int i, j;
+
+       for (i = 0; i < block->nchip; i++) {
+               unsigned long remapped = 0;
+
+               gbc = &block->gbc[i];
+
+               for (j = 0; j < gbc->nremap; j++) {
+                       struct gpio_remap *gr = &gbc->remap[j];
+
+                       remapped |= (values & gr->mask) << gr->offset;
+               }
+               if (gbc->gc->set_block) {
+                       gbc->gc->set_block(gbc->gc, gbc->mask, remapped);
+               } else {
+                       /* emulate */
+                       for_each_set_bit(j, &gbc->mask, BITS_PER_LONG)
+                               gbc->gc->set(gbc->gc, gbc->gc->base + j,
+                                            (remapped >> j) & 1);
+               }
+       }
+}
+EXPORT_SYMBOL_GPL(gpio_block_set);
+
+struct gpio_block *gpio_block_find_by_name(const char *name)
+{
+       struct gpio_block *i;
+
+       list_for_each_entry(i, &gpio_block_list, list)
+               if (!strcmp(i->name, name))
+                       return i;
And here is a list anyway, I'm getting confused of this partitioning between
using dynamic arrays and lists. Just use a list please. This part looks
good.

Yours,
Linus Walleij
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help