diff options
-rw-r--r-- | drivers/gpio/gpiolib.c | 38 |
1 files changed, 35 insertions, 3 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 6b50e05bc489..0e6c9bd34f53 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -184,11 +184,42 @@ struct gpio_chip *gpio_to_chip(unsigned gpio) } /* dynamic allocation of GPIOs, e.g. on a hotplugged device */ -static int gpiochip_find_base(int ngpio) +static int gpiochip_find_base(struct gpio_chip *req_chip) { struct gpio_chip *chip; - int base = ARCH_NR_GPIOS - ngpio; + int ngpio = req_chip->ngpio; + int base = -1; + +#ifdef CONFIG_OF + if (req_chip->of_node) { + int gpio_nr; + + gpio_nr = of_alias_get_id(req_chip->of_node, "gpio"); + if (gpio_nr >= 0) { + int start_gpio = gpio_nr; + int end_gpio = gpio_nr + req_chip->ngpio; + + list_for_each_entry(chip, &gpio_chips, list) { + if (chip->base > end_gpio) + continue; + + if ((chip->base < start_gpio) && + ((chip->base + chip->ngpio) < start_gpio)) + continue; + pr_err("GPIO %d to %d is already allocated\n", + start_gpio, end_gpio); + gpio_nr = -1; + break; + } + } + base = gpio_nr; + } +#endif + + if (base >= 0) + goto found; + base = ARCH_NR_GPIOS - ngpio; list_for_each_entry_reverse(chip, &gpio_chips, list) { /* found a free space? */ if (chip->base + chip->ngpio <= base) @@ -198,6 +229,7 @@ static int gpiochip_find_base(int ngpio) base = chip->base - ngpio; } +found: if (gpio_is_valid(base)) { pr_debug("%s: found new base at %d\n", __func__, base); return base; @@ -1184,7 +1216,7 @@ int gpiochip_add(struct gpio_chip *chip) spin_lock_irqsave(&gpio_lock, flags); if (base < 0) { - base = gpiochip_find_base(chip->ngpio); + base = gpiochip_find_base(chip); if (base < 0) { status = base; goto unlock; |