gpiolib: add gpiod_get() and gpiod_put() functions

Add gpiod_get(), gpiod_get_index() and gpiod_put() functions that
provide safer management of GPIOs.

These functions put the GPIO framework in line with the conventions of
other frameworks in the kernel, and help ensure every GPIO is declared
properly and valid while it is used.

Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
Alexandre Courbot
2013-10-17 10:21:38 -07:00
committed by Linus Walleij
parent af8b6375a8
commit bae48da237
4 changed files with 357 additions and 0 deletions

View File

@@ -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)