diff options
Diffstat (limited to 'drivers/gpio/gpiolib.c')
-rw-r--r-- | drivers/gpio/gpiolib.c | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 224abdc4b095..9263c7b359b7 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -70,6 +70,8 @@ static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; #define GPIO_OFFSET_VALID(chip, offset) (offset >= 0 && offset < chip->ngpio) +static DEFINE_MUTEX(gpio_lookup_lock); +static LIST_HEAD(gpio_lookup_list); static LIST_HEAD(gpio_chips); #ifdef CONFIG_GPIO_SYSFS @@ -2192,6 +2194,207 @@ void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) } EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep); +/** + * gpiod_add_table() - register GPIO device consumers + * @table: array of consumers to register + * @num: number of consumers in table + */ +void gpiod_add_table(struct gpiod_lookup *table, size_t size) +{ + mutex_lock(&gpio_lookup_lock); + + while (size--) { + list_add_tail(&table->list, &gpio_lookup_list); + table++; + } + + mutex_unlock(&gpio_lookup_lock); +} + +/* + * Caller must have a acquired gpio_lookup_lock + */ +static struct gpio_chip *find_chip_by_name(const char *name) +{ + struct gpio_chip *chip = NULL; + + list_for_each_entry(chip, &gpio_lookup_list, list) { + if (chip->label == NULL) + continue; + if (!strcmp(chip->label, name)) + break; + } + + return chip; +} + +#ifdef CONFIG_OF +static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, + unsigned int idx, unsigned long *flags) +{ + char prop_name[32]; /* 32 is max size of property name */ + enum of_gpio_flags of_flags; + struct gpio_desc *desc; + + if (con_id) + snprintf(prop_name, 32, "%s-gpios", con_id); + else + snprintf(prop_name, 32, "gpios"); + + desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx, + &of_flags); + + if (IS_ERR(desc)) + return desc; + + if (of_flags & OF_GPIO_ACTIVE_LOW) + *flags |= GPIOF_ACTIVE_LOW; + + return desc; +} +#else +static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, + unsigned int idx, unsigned long *flags) +{ + return ERR_PTR(-ENODEV); +} +#endif + +static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, + unsigned int idx, unsigned long *flags) +{ + const char *dev_id = dev ? dev_name(dev) : NULL; + struct gpio_desc *desc = ERR_PTR(-ENODEV); + unsigned int match, best = 0; + struct gpiod_lookup *p; + + mutex_lock(&gpio_lookup_lock); + + list_for_each_entry(p, &gpio_lookup_list, list) { + match = 0; + + if (p->dev_id) { + if (!dev_id || strcmp(p->dev_id, dev_id)) + continue; + + match += 2; + } + + if (p->con_id) { + if (!con_id || strcmp(p->con_id, con_id)) + continue; + + match += 1; + } + + if (p->idx != idx) + continue; + + if (match > best) { + struct gpio_chip *chip; + + chip = find_chip_by_name(p->chip_label); + + if (!chip) { + dev_warn(dev, "cannot find GPIO chip %s\n", + p->chip_label); + continue; + } + + if (chip->ngpio >= p->chip_hwnum) { + dev_warn(dev, "GPIO chip %s has %d GPIOs\n", + chip->label, chip->ngpio); + continue; + } + + desc = gpio_to_desc(chip->base + p->chip_hwnum); + *flags = p->flags; + + if (match != 3) + best = match; + else + break; + } + } + + mutex_unlock(&gpio_lookup_lock); + + return desc; +} + +/** + * gpio_get - obtain a GPIO for a given GPIO function + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * + * Return the GPIO descriptor corresponding to the function con_id of device + * dev, or an IS_ERR() condition if an error occured. + */ +struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id) +{ + return gpiod_get_index(dev, con_id, 0); +} +EXPORT_SYMBOL_GPL(gpiod_get); + +/** + * gpiod_get_index - obtain a GPIO from a multi-index GPIO function + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @idx: index of the GPIO to obtain in the consumer + * + * This variant of gpiod_get() allows to access GPIOs other than the first + * defined one for functions that define several GPIOs. + * + * Return a valid GPIO descriptor, or an IS_ERR() condition in case of error. + */ +struct gpio_desc *__must_check gpiod_get_index(struct device *dev, + const char *con_id, + unsigned int idx) +{ + struct gpio_desc *desc; + int status; + unsigned long flags = 0; + + dev_dbg(dev, "GPIO lookup for consumer %s\n", con_id); + + /* Using device tree? */ + if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) { + dev_dbg(dev, "using device tree for GPIO lookup\n"); + desc = of_find_gpio(dev, con_id, idx, &flags); + } else { + dev_dbg(dev, "using lookup tables for GPIO lookup"); + desc = gpiod_find(dev, con_id, idx, &flags); + } + + if (IS_ERR(desc)) { + dev_warn(dev, "lookup for GPIO %s failed\n", con_id); + return desc; + } + + status = gpiod_request(desc, con_id); + + if (status < 0) + return ERR_PTR(status); + + if (flags & GPIOF_ACTIVE_LOW) + set_bit(FLAG_ACTIVE_LOW, &desc->flags); + + return desc; +} +EXPORT_SYMBOL_GPL(gpiod_get_index); + +/** + * gpiod_put - dispose of a GPIO descriptor + * @desc: GPIO descriptor to dispose of + * + * No descriptor can be used after gpiod_put() has been called on it. + */ +void gpiod_put(struct gpio_desc *desc) +{ + gpiod_free(desc); +} +EXPORT_SYMBOL_GPL(gpiod_put); + #ifdef CONFIG_DEBUG_FS static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) |