diff options
Diffstat (limited to 'drivers/gpio/gpiolib.c')
-rw-r--r-- | drivers/gpio/gpiolib.c | 110 |
1 files changed, 68 insertions, 42 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 3346abd29b52..5c1ba879f889 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -182,39 +182,62 @@ EXPORT_SYMBOL_GPL(gpiod_get_direction); /* * Add a new chip to the global chips list, keeping the list of chips sorted - * by base order. + * by range(means [base, base + ngpio - 1]) order. * * Return -EBUSY if the new chip overlaps with some other chip's integer * space. */ static int gpiochip_add_to_list(struct gpio_chip *chip) { - struct list_head *pos; - struct gpio_chip *_chip; - int err = 0; + struct gpio_chip *iterator; + struct gpio_chip *previous = NULL; - /* find where to insert our chip */ - list_for_each(pos, &gpio_chips) { - _chip = list_entry(pos, struct gpio_chip, list); - /* shall we insert before _chip? */ - if (_chip->base >= chip->base + chip->ngpio) - break; + if (list_empty(&gpio_chips)) { + list_add_tail(&chip->list, &gpio_chips); + return 0; } - /* are we stepping on the chip right before? */ - if (pos != &gpio_chips && pos->prev != &gpio_chips) { - _chip = list_entry(pos->prev, struct gpio_chip, list); - if (_chip->base + _chip->ngpio > chip->base) { - dev_err(chip->dev, - "GPIO integer space overlap, cannot add chip\n"); - err = -EBUSY; + list_for_each_entry(iterator, &gpio_chips, list) { + if (iterator->base >= chip->base + chip->ngpio) { + /* + * Iterator is the first GPIO chip so there is no + * previous one + */ + if (!previous) { + goto found; + } else { + /* + * We found a valid range(means + * [base, base + ngpio - 1]) between previous + * and iterator chip. + */ + if (previous->base + previous->ngpio + <= chip->base) + goto found; + } } + previous = iterator; + } + + /* + * We are beyond the last chip in the list and iterator now + * points to the head. + * Let iterator point to the last chip in the list. + */ + + iterator = list_last_entry(&gpio_chips, struct gpio_chip, list); + if (iterator->base + iterator->ngpio <= chip->base) { + list_add(&chip->list, &iterator->list); + return 0; } - if (!err) - list_add_tail(&chip->list, pos); + dev_err(chip->parent, + "GPIO integer space overlap, cannot add chip\n"); + return -EBUSY; - return err; +found: + list_add_tail(&chip->list, &iterator->list); + return 0; } /** @@ -252,7 +275,7 @@ static struct gpio_desc *gpio_name_to_desc(const char * const name) * Takes the names from gc->names and checks if they are all unique. If they * are, they are assigned to their gpio descriptors. * - * Returns -EEXIST if one of the names is already used for a different GPIO. + * Warning if one of the names is already used for a different GPIO. */ static int gpiochip_set_desc_names(struct gpio_chip *gc) { @@ -267,7 +290,7 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc) gpio = gpio_name_to_desc(gc->names[i]); if (gpio) - dev_warn(gc->dev, "Detected name collision for " + dev_warn(gc->parent, "Detected name collision for " "GPIO name '%s'\n", gc->names[i]); } @@ -280,7 +303,7 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc) } /** - * gpiochip_add() - register a gpio_chip + * gpiochip_add_data() - register a gpio_chip * @chip: the chip to register, with chip->base initialized * Context: potentially before irqs will work * @@ -288,15 +311,15 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc) * because the chip->base is invalid or already associated with a * different chip. Otherwise it returns zero as a success code. * - * When gpiochip_add() is called very early during boot, so that GPIOs - * can be freely used, the chip->dev device must be registered before + * When gpiochip_add_data() is called very early during boot, so that GPIOs + * can be freely used, the chip->parent device must be registered before * the gpio framework's arch_initcall(). Otherwise sysfs initialization * for GPIOs will fail rudely. * * If chip->base is negative, this requests dynamic assignment of * a range of valid GPIOs. */ -int gpiochip_add(struct gpio_chip *chip) +int gpiochip_add_data(struct gpio_chip *chip, void *data) { unsigned long flags; int status = 0; @@ -308,6 +331,13 @@ int gpiochip_add(struct gpio_chip *chip) if (!descs) return -ENOMEM; + chip->data = data; + + if (chip->ngpio == 0) { + chip_err(chip, "tried to insert a GPIO chip with zero lines\n"); + return -EINVAL; + } + spin_lock_irqsave(&gpio_lock, flags); if (base < 0) { @@ -348,8 +378,8 @@ int gpiochip_add(struct gpio_chip *chip) INIT_LIST_HEAD(&chip->pin_ranges); #endif - if (!chip->owner && chip->dev && chip->dev->driver) - chip->owner = chip->dev->driver->owner; + if (!chip->owner && chip->parent && chip->parent->driver) + chip->owner = chip->parent->driver->owner; status = gpiochip_set_desc_names(chip); if (status) @@ -389,7 +419,7 @@ err_free_descs: chip->label ? : "generic"); return status; } -EXPORT_SYMBOL_GPL(gpiochip_add); +EXPORT_SYMBOL_GPL(gpiochip_add_data); /** * gpiochip_remove() - unregister a gpio_chip @@ -424,7 +454,8 @@ void gpiochip_remove(struct gpio_chip *chip) spin_unlock_irqrestore(&gpio_lock, flags); if (requested) - dev_crit(chip->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n"); + dev_crit(chip->parent, + "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n"); kfree(chip->desc); chip->desc = NULL; @@ -659,7 +690,7 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) * gpiochip, providing an irq domain to translate the local IRQs to * global irqs in the gpiolib core, and making sure that the gpiochip * is passed as chip data to all related functions. Driver callbacks - * need to use container_of() to get their local state containers back + * need to use gpiochip_get_data() to get their local state containers back * from the gpiochip passed as chip data. An irqdomain will be stored * in the gpiochip that shall be used by the driver to handle IRQ number * translation. The gpiochip will need to be initialized and registered @@ -683,15 +714,16 @@ int _gpiochip_irqchip_add(struct gpio_chip *gpiochip, if (!gpiochip || !irqchip) return -EINVAL; - if (!gpiochip->dev) { + if (!gpiochip->parent) { pr_err("missing gpiochip .dev parent pointer\n"); return -EINVAL; } - of_node = gpiochip->dev->of_node; + of_node = gpiochip->parent->of_node; #ifdef CONFIG_OF_GPIO /* * If the gpiochip has an assigned OF node this takes precedence - * FIXME: get rid of this and use gpiochip->dev->of_node everywhere + * FIXME: get rid of this and use gpiochip->parent->of_node + * everywhere */ if (gpiochip->of_node) of_node = gpiochip->of_node; @@ -1279,13 +1311,7 @@ static int _gpiod_get_raw_value(const struct gpio_desc *desc) chip = desc->chip; offset = gpio_chip_hwgpio(desc); value = chip->get ? chip->get(chip, offset) : -EIO; - /* - * FIXME: fix all drivers to clamp to [0,1] or return negative, - * then change this to: - * value = value < 0 ? value : !!value; - * so we can properly propagate error codes. - */ - value = !!value; + value = value < 0 ? value : !!value; trace_gpio_value(desc_to_gpio(desc), 1, value); return value; } @@ -2512,7 +2538,7 @@ static int gpiolib_seq_show(struct seq_file *s, void *v) seq_printf(s, "%sGPIOs %d-%d", (char *)s->private, chip->base, chip->base + chip->ngpio - 1); - dev = chip->dev; + dev = chip->parent; if (dev) seq_printf(s, ", %s/%s", dev->bus ? dev->bus->name : "no-bus", dev_name(dev)); |