Commit ad824783 authored by Alexandre Courbot's avatar Alexandre Courbot Committed by Linus Walleij

gpio: better lookup method for platform GPIOs

Change the format of the platform GPIO lookup tables to make them less
confusing and improve lookup efficiency.

The previous format was a single linked-list that required to compare
the device name and function ID of every single GPIO defined for each
lookup. Switch that to a list of per-device tables, so that the lookup
can be done in two steps, omitting the GPIOs that are not relevant for a
particular device.

The matching rules are now defined as follows:
- The device name must match *exactly*, and can be NULL for GPIOs not
  assigned to a particular device,
- If the function ID in the lookup table is NULL, the con_id argument of
  gpiod_get() will not be used for lookup. However, if it is defined, it
  must match exactly.
- The index must always match.
Signed-off-by: default avatarAlexandre Courbot <acourbot@nvidia.com>
Acked-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent bdc54ef4
...@@ -72,10 +72,11 @@ where ...@@ -72,10 +72,11 @@ where
- chip_label is the label of the gpiod_chip instance providing the GPIO - chip_label is the label of the gpiod_chip instance providing the GPIO
- chip_hwnum is the hardware number of the GPIO within the chip - chip_hwnum is the hardware number of the GPIO within the chip
- dev_id is the identifier of the device that will make use of this GPIO. If - dev_id is the identifier of the device that will make use of this GPIO. It
NULL, the GPIO will be available to all devices. can be NULL, in which case it will be matched for calls to gpiod_get()
with a NULL device.
- con_id is the name of the GPIO function from the device point of view. It - con_id is the name of the GPIO function from the device point of view. It
can be NULL. can be NULL, in which case it will match any function.
- idx is the index of the GPIO within the function. - idx is the index of the GPIO within the function.
- flags is defined to specify the following properties: - flags is defined to specify the following properties:
* GPIOF_ACTIVE_LOW - to configure the GPIO as active-low * GPIOF_ACTIVE_LOW - to configure the GPIO as active-low
...@@ -86,18 +87,23 @@ In the future, these flags might be extended to support more properties. ...@@ -86,18 +87,23 @@ In the future, these flags might be extended to support more properties.
Note that GPIO_LOOKUP() is just a shortcut to GPIO_LOOKUP_IDX() where idx = 0. Note that GPIO_LOOKUP() is just a shortcut to GPIO_LOOKUP_IDX() where idx = 0.
A lookup table can then be defined as follows: A lookup table can then be defined as follows, with an empty entry defining its
end:
struct gpiod_lookup gpios_table[] = { struct gpiod_lookup_table gpios_table = {
GPIO_LOOKUP_IDX("gpio.0", 15, "foo.0", "led", 0, GPIO_ACTIVE_HIGH), .dev_id = "foo.0",
GPIO_LOOKUP_IDX("gpio.0", 16, "foo.0", "led", 1, GPIO_ACTIVE_HIGH), .table = {
GPIO_LOOKUP_IDX("gpio.0", 17, "foo.0", "led", 2, GPIO_ACTIVE_HIGH), GPIO_LOOKUP_IDX("gpio.0", 15, "led", 0, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("gpio.0", 1, "foo.0", "power", GPIO_ACTIVE_LOW), GPIO_LOOKUP_IDX("gpio.0", 16, "led", 1, GPIO_ACTIVE_HIGH),
}; GPIO_LOOKUP_IDX("gpio.0", 17, "led", 2, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("gpio.0", 1, "power", GPIO_ACTIVE_LOW),
{ },
},
};
And the table can be added by the board code as follows: And the table can be added by the board code as follows:
gpiod_add_table(gpios_table, ARRAY_SIZE(gpios_table)); gpiod_add_lookup_table(&gpios_table);
The driver controlling "foo.0" will then be able to obtain its GPIOs as follows: The driver controlling "foo.0" will then be able to obtain its GPIOs as follows:
......
...@@ -2260,18 +2260,14 @@ void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) ...@@ -2260,18 +2260,14 @@ void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep); EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
/** /**
* gpiod_add_table() - register GPIO device consumers * gpiod_add_lookup_table() - register GPIO device consumers
* @table: array of consumers to register * @table: table of consumers to register
* @num: number of consumers in table
*/ */
void gpiod_add_table(struct gpiod_lookup *table, size_t size) void gpiod_add_lookup_table(struct gpiod_lookup_table *table)
{ {
mutex_lock(&gpio_lookup_lock); mutex_lock(&gpio_lookup_lock);
while (size--) { list_add_tail(&table->list, &gpio_lookup_list);
list_add_tail(&table->list, &gpio_lookup_list);
table++;
}
mutex_unlock(&gpio_lookup_lock); mutex_unlock(&gpio_lookup_lock);
} }
...@@ -2327,72 +2323,84 @@ static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id, ...@@ -2327,72 +2323,84 @@ static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id,
return desc; return desc;
} }
static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev)
unsigned int idx,
enum gpio_lookup_flags *flags)
{ {
const char *dev_id = dev ? dev_name(dev) : NULL; const char *dev_id = dev ? dev_name(dev) : NULL;
struct gpio_desc *desc = ERR_PTR(-ENODEV); struct gpiod_lookup_table *table;
unsigned int match, best = 0;
struct gpiod_lookup *p;
mutex_lock(&gpio_lookup_lock); mutex_lock(&gpio_lookup_lock);
list_for_each_entry(p, &gpio_lookup_list, list) { list_for_each_entry(table, &gpio_lookup_list, list) {
match = 0; if (table->dev_id && dev_id) {
/*
* Valid strings on both ends, must be identical to have
* a match
*/
if (!strcmp(table->dev_id, dev_id))
goto found;
} else {
/*
* One of the pointers is NULL, so both must be to have
* a match
*/
if (dev_id == table->dev_id)
goto found;
}
}
table = NULL;
if (p->dev_id) { found:
if (!dev_id || strcmp(p->dev_id, dev_id)) mutex_unlock(&gpio_lookup_lock);
continue; return table;
}
match += 2; static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id,
} unsigned int idx,
enum gpio_lookup_flags *flags)
{
struct gpio_desc *desc = ERR_PTR(-ENODEV);
struct gpiod_lookup_table *table;
struct gpiod_lookup *p;
if (p->con_id) { table = gpiod_find_lookup_table(dev);
if (!con_id || strcmp(p->con_id, con_id)) if (!table)
continue; return desc;
match += 1; for (p = &table->table[0]; p->chip_label; p++) {
} struct gpio_chip *chip;
/* idx must always match exactly */
if (p->idx != idx) if (p->idx != idx)
continue; continue;
if (match > best) { /* If the lookup entry has a con_id, require exact match */
struct gpio_chip *chip; if (p->con_id && (!con_id || strcmp(p->con_id, con_id)))
continue;
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) { chip = find_chip_by_name(p->chip_label);
dev_warn(dev, "GPIO chip %s has %d GPIOs\n",
chip->label, chip->ngpio);
continue;
}
desc = gpio_to_desc(chip->base + p->chip_hwnum); if (!chip) {
*flags = p->flags; dev_warn(dev, "cannot find GPIO chip %s\n",
p->chip_label);
continue;
}
if (match != 3) if (chip->ngpio <= p->chip_hwnum) {
best = match; dev_warn(dev, "GPIO chip %s has %d GPIOs\n",
else chip->label, chip->ngpio);
break; continue;
} }
}
mutex_unlock(&gpio_lookup_lock); desc = gpiochip_offset_to_desc(chip, p->chip_hwnum);
*flags = p->flags;
}
return desc; return desc;
} }
/** /**
* gpio_get - obtain a GPIO for a given GPIO function * gpio_get - obtain a GPIO for a given GPIO function
* @dev: GPIO consumer * @dev: GPIO consumer, can be NULL for system-global GPIOs
* @con_id: function within the GPIO consumer * @con_id: function within the GPIO consumer
* *
* Return the GPIO descriptor corresponding to the function con_id of device * Return the GPIO descriptor corresponding to the function con_id of device
......
...@@ -141,7 +141,6 @@ enum gpio_lookup_flags { ...@@ -141,7 +141,6 @@ enum gpio_lookup_flags {
* platform data. * platform data.
*/ */
struct gpiod_lookup { struct gpiod_lookup {
struct list_head list;
/* /*
* name of the chip the GPIO belongs to * name of the chip the GPIO belongs to
*/ */
...@@ -150,10 +149,6 @@ struct gpiod_lookup { ...@@ -150,10 +149,6 @@ struct gpiod_lookup {
* hardware number (i.e. relative to the chip) of the GPIO * hardware number (i.e. relative to the chip) of the GPIO
*/ */
u16 chip_hwnum; u16 chip_hwnum;
/*
* name of device that can claim this GPIO
*/
const char *dev_id;
/* /*
* name of the GPIO from the device's point of view * name of the GPIO from the device's point of view
*/ */
...@@ -168,28 +163,32 @@ struct gpiod_lookup { ...@@ -168,28 +163,32 @@ struct gpiod_lookup {
enum gpio_lookup_flags flags; enum gpio_lookup_flags flags;
}; };
struct gpiod_lookup_table {
struct list_head list;
const char *dev_id;
struct gpiod_lookup table[];
};
/* /*
* Simple definition of a single GPIO under a con_id * Simple definition of a single GPIO under a con_id
*/ */
#define GPIO_LOOKUP(_chip_label, _chip_hwnum, _dev_id, _con_id, _flags) \ #define GPIO_LOOKUP(_chip_label, _chip_hwnum, _con_id, _flags) \
GPIO_LOOKUP_IDX(_chip_label, _chip_hwnum, _dev_id, _con_id, 0, _flags) GPIO_LOOKUP_IDX(_chip_label, _chip_hwnum, _con_id, 0, _flags)
/* /*
* Use this macro if you need to have several GPIOs under the same con_id. * Use this macro if you need to have several GPIOs under the same con_id.
* Each GPIO needs to use a different index and can be accessed using * Each GPIO needs to use a different index and can be accessed using
* gpiod_get_index() * gpiod_get_index()
*/ */
#define GPIO_LOOKUP_IDX(_chip_label, _chip_hwnum, _dev_id, _con_id, _idx, \ #define GPIO_LOOKUP_IDX(_chip_label, _chip_hwnum, _con_id, _idx, _flags) \
_flags) \
{ \ { \
.chip_label = _chip_label, \ .chip_label = _chip_label, \
.chip_hwnum = _chip_hwnum, \ .chip_hwnum = _chip_hwnum, \
.dev_id = _dev_id, \
.con_id = _con_id, \ .con_id = _con_id, \
.idx = _idx, \ .idx = _idx, \
.flags = _flags, \ .flags = _flags, \
} }
void gpiod_add_table(struct gpiod_lookup *table, size_t size); void gpiod_add_lookup_table(struct gpiod_lookup_table *table);
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment