Merge branch 'ib-gpio-aggregator' into devel

This commit is contained in:
Linus Walleij 2020-05-18 10:13:36 +02:00
commit a0d50aa935
11 changed files with 748 additions and 36 deletions

View file

@ -0,0 +1,111 @@
.. SPDX-License-Identifier: GPL-2.0-only
GPIO Aggregator
===============
The GPIO Aggregator provides a mechanism to aggregate GPIOs, and expose them as
a new gpio_chip. This supports the following use cases.
Aggregating GPIOs using Sysfs
-----------------------------
GPIO controllers are exported to userspace using /dev/gpiochip* character
devices. Access control to these devices is provided by standard UNIX file
system permissions, on an all-or-nothing basis: either a GPIO controller is
accessible for a user, or it is not.
The GPIO Aggregator provides access control for a set of one or more GPIOs, by
aggregating them into a new gpio_chip, which can be assigned to a group or user
using standard UNIX file ownership and permissions. Furthermore, this
simplifies and hardens exporting GPIOs to a virtual machine, as the VM can just
grab the full GPIO controller, and no longer needs to care about which GPIOs to
grab and which not, reducing the attack surface.
Aggregated GPIO controllers are instantiated and destroyed by writing to
write-only attribute files in sysfs.
/sys/bus/platform/drivers/gpio-aggregator/
"new_device" ...
Userspace may ask the kernel to instantiate an aggregated GPIO
controller by writing a string describing the GPIOs to
aggregate to the "new_device" file, using the format
.. code-block:: none
[<gpioA>] [<gpiochipB> <offsets>] ...
Where:
"<gpioA>" ...
is a GPIO line name,
"<gpiochipB>" ...
is a GPIO chip label, and
"<offsets>" ...
is a comma-separated list of GPIO offsets and/or
GPIO offset ranges denoted by dashes.
Example: Instantiate a new GPIO aggregator by aggregating GPIO
line 19 of "e6052000.gpio" and GPIO lines 20-21 of
"e6050000.gpio" into a new gpio_chip:
.. code-block:: sh
$ echo 'e6052000.gpio 19 e6050000.gpio 20-21' > new_device
"delete_device" ...
Userspace may ask the kernel to destroy an aggregated GPIO
controller after use by writing its device name to the
"delete_device" file.
Example: Destroy the previously-created aggregated GPIO
controller, assumed to be "gpio-aggregator.0":
.. code-block:: sh
$ echo gpio-aggregator.0 > delete_device
Generic GPIO Driver
-------------------
The GPIO Aggregator can also be used as a generic driver for a simple
GPIO-operated device described in DT, without a dedicated in-kernel driver.
This is useful in industrial control, and is not unlike e.g. spidev, which
allows the user to communicate with an SPI device from userspace.
Binding a device to the GPIO Aggregator is performed either by modifying the
gpio-aggregator driver, or by writing to the "driver_override" file in Sysfs.
Example: If "door" is a GPIO-operated device described in DT, using its own
compatible value::
door {
compatible = "myvendor,mydoor";
gpios = <&gpio2 19 GPIO_ACTIVE_HIGH>,
<&gpio2 20 GPIO_ACTIVE_LOW>;
gpio-line-names = "open", "lock";
};
it can be bound to the GPIO Aggregator by either:
1. Adding its compatible value to ``gpio_aggregator_dt_ids[]``,
2. Binding manually using "driver_override":
.. code-block:: sh
$ echo gpio-aggregator > /sys/bus/platform/devices/door/driver_override
$ echo door > /sys/bus/platform/drivers/gpio-aggregator/bind
After that, a new gpiochip "door" has been created:
.. code-block:: sh
$ gpioinfo door
gpiochip12 - 2 lines:
line 0: "open" unused input active-high
line 1: "lock" unused input active-high

View file

@ -7,6 +7,7 @@ gpio
.. toctree::
:maxdepth: 1
gpio-aggregator
sysfs
.. only:: subproject and html

View file

@ -113,13 +113,15 @@ files that desire to do so need to include the following header::
GPIOs are mapped by the means of tables of lookups, containing instances of the
gpiod_lookup structure. Two macros are defined to help declaring such mappings::
GPIO_LOOKUP(chip_label, chip_hwnum, con_id, flags)
GPIO_LOOKUP_IDX(chip_label, chip_hwnum, con_id, idx, flags)
GPIO_LOOKUP(key, chip_hwnum, con_id, flags)
GPIO_LOOKUP_IDX(key, chip_hwnum, con_id, idx, flags)
where
- 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
- key is either the label of the gpiod_chip instance providing the GPIO, or
the GPIO line name
- chip_hwnum is the hardware number of the GPIO within the chip, or U16_MAX
to indicate that key is a GPIO line name
- con_id is the name of the GPIO function from the device point of view. It
can be NULL, in which case it will match any function.
- idx is the index of the GPIO within the function.
@ -135,7 +137,10 @@ where
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:
1. GPIO line names are not guaranteed to be globally unique, so the first
match found will be used.
2. GPIO_LOOKUP() is just a shortcut to GPIO_LOOKUP_IDX() where idx = 0.
A lookup table can then be defined as follows, with an empty entry defining its
end. The 'dev_id' field of the table is the identifier of the device that will

View file

@ -7228,6 +7228,13 @@ F: Documentation/firmware-guide/acpi/gpio-properties.rst
F: drivers/gpio/gpiolib-acpi.c
F: drivers/gpio/gpiolib-acpi.h
GPIO AGGREGATOR
M: Geert Uytterhoeven <geert+renesas@glider.be>
L: linux-gpio@vger.kernel.org
S: Supported
F: Documentation/admin-guide/gpio/gpio-aggregator.rst
F: drivers/gpio/gpio-aggregator.c
GPIO IR Transmitter
M: Sean Young <sean@mess.org>
L: linux-media@vger.kernel.org

View file

@ -1541,6 +1541,18 @@ config GPIO_VIPERBOARD
endmenu
config GPIO_AGGREGATOR
tristate "GPIO Aggregator"
help
Say yes here to enable the GPIO Aggregator, which provides a way to
aggregate existing GPIO lines into a new virtual GPIO chip.
This can serve the following purposes:
- Assign permissions for a collection of GPIO lines to a user,
- Export a collection of GPIO lines to a virtual machine,
- Provide a generic driver for a GPIO-operated device in an
industrial control context, to be operated from userspace using
the GPIO chardev interface.
config GPIO_MOCKUP
tristate "GPIO Testing Driver"
select IRQ_SIM

View file

@ -25,6 +25,7 @@ obj-$(CONFIG_GPIO_74XX_MMIO) += gpio-74xx-mmio.o
obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o
obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o
obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o
obj-$(CONFIG_GPIO_AGGREGATOR) += gpio-aggregator.o
obj-$(CONFIG_GPIO_ALTERA_A10SR) += gpio-altera-a10sr.o
obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o
obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o

View file

@ -0,0 +1,568 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// GPIO Aggregator
//
// Copyright (C) 2019-2020 Glider bv
#define DRV_NAME "gpio-aggregator"
#define pr_fmt(fmt) DRV_NAME ": " fmt
#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/ctype.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/machine.h>
#include <linux/idr.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/overflow.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/string.h>
/*
* GPIO Aggregator sysfs interface
*/
struct gpio_aggregator {
struct gpiod_lookup_table *lookups;
struct platform_device *pdev;
char args[];
};
static DEFINE_MUTEX(gpio_aggregator_lock); /* protects idr */
static DEFINE_IDR(gpio_aggregator_idr);
static char *get_arg(char **args)
{
char *start = *args, *end;
start = skip_spaces(start);
if (!*start)
return NULL;
if (*start == '"') {
/* Quoted arg */
end = strchr(++start, '"');
if (!end)
return ERR_PTR(-EINVAL);
} else {
/* Unquoted arg */
for (end = start; *end && !isspace(*end); end++) ;
}
if (*end)
*end++ = '\0';
*args = end;
return start;
}
static bool isrange(const char *s)
{
size_t n;
if (IS_ERR_OR_NULL(s))
return false;
while (1) {
n = strspn(s, "0123456789");
if (!n)
return false;
s += n;
switch (*s++) {
case '\0':
return true;
case '-':
case ',':
break;
default:
return false;
}
}
}
static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key,
int hwnum, unsigned int *n)
{
struct gpiod_lookup_table *lookups;
lookups = krealloc(aggr->lookups, struct_size(lookups, table, *n + 2),
GFP_KERNEL);
if (!lookups)
return -ENOMEM;
lookups->table[*n] =
(struct gpiod_lookup)GPIO_LOOKUP_IDX(key, hwnum, NULL, *n, 0);
(*n)++;
memset(&lookups->table[*n], 0, sizeof(lookups->table[*n]));
aggr->lookups = lookups;
return 0;
}
static int aggr_parse(struct gpio_aggregator *aggr)
{
unsigned int first_index, last_index, i, n = 0;
char *name, *offsets, *first, *last, *next;
char *args = aggr->args;
int error;
for (name = get_arg(&args), offsets = get_arg(&args); name;
offsets = get_arg(&args)) {
if (IS_ERR(name)) {
pr_err("Cannot get GPIO specifier: %pe\n", name);
return PTR_ERR(name);
}
if (!isrange(offsets)) {
/* Named GPIO line */
error = aggr_add_gpio(aggr, name, U16_MAX, &n);
if (error)
return error;
name = offsets;
continue;
}
/* GPIO chip + offset(s) */
for (first = offsets; *first; first = next) {
next = strchrnul(first, ',');
if (*next)
*next++ = '\0';
last = strchr(first, '-');
if (last)
*last++ = '\0';
if (kstrtouint(first, 10, &first_index)) {
pr_err("Cannot parse GPIO index %s\n", first);
return -EINVAL;
}
if (!last) {
last_index = first_index;
} else if (kstrtouint(last, 10, &last_index)) {
pr_err("Cannot parse GPIO index %s\n", last);
return -EINVAL;
}
for (i = first_index; i <= last_index; i++) {
error = aggr_add_gpio(aggr, name, i, &n);
if (error)
return error;
}
}
name = get_arg(&args);
}
if (!n) {
pr_err("No GPIOs specified\n");
return -EINVAL;
}
return 0;
}
static ssize_t new_device_store(struct device_driver *driver, const char *buf,
size_t count)
{
struct gpio_aggregator *aggr;
struct platform_device *pdev;
int res, id;
/* kernfs guarantees string termination, so count + 1 is safe */
aggr = kzalloc(sizeof(*aggr) + count + 1, GFP_KERNEL);
if (!aggr)
return -ENOMEM;
memcpy(aggr->args, buf, count + 1);
aggr->lookups = kzalloc(struct_size(aggr->lookups, table, 1),
GFP_KERNEL);
if (!aggr->lookups) {
res = -ENOMEM;
goto free_ga;
}
mutex_lock(&gpio_aggregator_lock);
id = idr_alloc(&gpio_aggregator_idr, aggr, 0, 0, GFP_KERNEL);
mutex_unlock(&gpio_aggregator_lock);
if (id < 0) {
res = id;
goto free_table;
}
aggr->lookups->dev_id = kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, id);
if (!aggr->lookups->dev_id) {
res = -ENOMEM;
goto remove_idr;
}
res = aggr_parse(aggr);
if (res)
goto free_dev_id;
gpiod_add_lookup_table(aggr->lookups);
pdev = platform_device_register_simple(DRV_NAME, id, NULL, 0);
if (IS_ERR(pdev)) {
res = PTR_ERR(pdev);
goto remove_table;
}
aggr->pdev = pdev;
return count;
remove_table:
gpiod_remove_lookup_table(aggr->lookups);
free_dev_id:
kfree(aggr->lookups->dev_id);
remove_idr:
mutex_lock(&gpio_aggregator_lock);
idr_remove(&gpio_aggregator_idr, id);
mutex_unlock(&gpio_aggregator_lock);
free_table:
kfree(aggr->lookups);
free_ga:
kfree(aggr);
return res;
}
static DRIVER_ATTR_WO(new_device);
static void gpio_aggregator_free(struct gpio_aggregator *aggr)
{
platform_device_unregister(aggr->pdev);
gpiod_remove_lookup_table(aggr->lookups);
kfree(aggr->lookups->dev_id);
kfree(aggr->lookups);
kfree(aggr);
}
static ssize_t delete_device_store(struct device_driver *driver,
const char *buf, size_t count)
{
struct gpio_aggregator *aggr;
unsigned int id;
int error;
if (!str_has_prefix(buf, DRV_NAME "."))
return -EINVAL;
error = kstrtouint(buf + strlen(DRV_NAME "."), 10, &id);
if (error)
return error;
mutex_lock(&gpio_aggregator_lock);
aggr = idr_remove(&gpio_aggregator_idr, id);
mutex_unlock(&gpio_aggregator_lock);
if (!aggr)
return -ENOENT;
gpio_aggregator_free(aggr);
return count;
}
static DRIVER_ATTR_WO(delete_device);
static struct attribute *gpio_aggregator_attrs[] = {
&driver_attr_new_device.attr,
&driver_attr_delete_device.attr,
NULL,
};
ATTRIBUTE_GROUPS(gpio_aggregator);
static int __exit gpio_aggregator_idr_remove(int id, void *p, void *data)
{
gpio_aggregator_free(p);
return 0;
}
static void __exit gpio_aggregator_remove_all(void)
{
mutex_lock(&gpio_aggregator_lock);
idr_for_each(&gpio_aggregator_idr, gpio_aggregator_idr_remove, NULL);
idr_destroy(&gpio_aggregator_idr);
mutex_unlock(&gpio_aggregator_lock);
}
/*
* GPIO Forwarder
*/
struct gpiochip_fwd {
struct gpio_chip chip;
struct gpio_desc **descs;
union {
struct mutex mlock; /* protects tmp[] if can_sleep */
spinlock_t slock; /* protects tmp[] if !can_sleep */
};
unsigned long tmp[]; /* values and descs for multiple ops */
};
static int gpio_fwd_get_direction(struct gpio_chip *chip, unsigned int offset)
{
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
return gpiod_get_direction(fwd->descs[offset]);
}
static int gpio_fwd_direction_input(struct gpio_chip *chip, unsigned int offset)
{
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
return gpiod_direction_input(fwd->descs[offset]);
}
static int gpio_fwd_direction_output(struct gpio_chip *chip,
unsigned int offset, int value)
{
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
return gpiod_direction_output(fwd->descs[offset], value);
}
static int gpio_fwd_get(struct gpio_chip *chip, unsigned int offset)
{
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
return gpiod_get_value(fwd->descs[offset]);
}
static int gpio_fwd_get_multiple(struct gpio_chip *chip, unsigned long *mask,
unsigned long *bits)
{
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
unsigned long *values, flags = 0;
struct gpio_desc **descs;
unsigned int i, j = 0;
int error;
if (chip->can_sleep)
mutex_lock(&fwd->mlock);
else
spin_lock_irqsave(&fwd->slock, flags);
/* Both values bitmap and desc pointers are stored in tmp[] */
values = &fwd->tmp[0];
descs = (void *)&fwd->tmp[BITS_TO_LONGS(fwd->chip.ngpio)];
bitmap_clear(values, 0, fwd->chip.ngpio);
for_each_set_bit(i, mask, fwd->chip.ngpio)
descs[j++] = fwd->descs[i];
error = gpiod_get_array_value(j, descs, NULL, values);
if (!error) {
j = 0;
for_each_set_bit(i, mask, fwd->chip.ngpio)
__assign_bit(i, bits, test_bit(j++, values));
}
if (chip->can_sleep)
mutex_unlock(&fwd->mlock);
else
spin_unlock_irqrestore(&fwd->slock, flags);
return error;
}
static void gpio_fwd_set(struct gpio_chip *chip, unsigned int offset, int value)
{
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
gpiod_set_value(fwd->descs[offset], value);
}
static void gpio_fwd_set_multiple(struct gpio_chip *chip, unsigned long *mask,
unsigned long *bits)
{
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
unsigned long *values, flags = 0;
struct gpio_desc **descs;
unsigned int i, j = 0;
if (chip->can_sleep)
mutex_lock(&fwd->mlock);
else
spin_lock_irqsave(&fwd->slock, flags);
/* Both values bitmap and desc pointers are stored in tmp[] */
values = &fwd->tmp[0];
descs = (void *)&fwd->tmp[BITS_TO_LONGS(fwd->chip.ngpio)];
for_each_set_bit(i, mask, fwd->chip.ngpio) {
__assign_bit(j, values, test_bit(i, bits));
descs[j++] = fwd->descs[i];
}
gpiod_set_array_value(j, descs, NULL, values);
if (chip->can_sleep)
mutex_unlock(&fwd->mlock);
else
spin_unlock_irqrestore(&fwd->slock, flags);
}
static int gpio_fwd_set_config(struct gpio_chip *chip, unsigned int offset,
unsigned long config)
{
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
return gpiod_set_config(fwd->descs[offset], config);
}
/**
* gpiochip_fwd_create() - Create a new GPIO forwarder
* @dev: Parent device pointer
* @ngpios: Number of GPIOs in the forwarder.
* @descs: Array containing the GPIO descriptors to forward to.
* This array must contain @ngpios entries, and must not be deallocated
* before the forwarder has been destroyed again.
*
* This function creates a new gpiochip, which forwards all GPIO operations to
* the passed GPIO descriptors.
*
* Return: An opaque object pointer, or an ERR_PTR()-encoded negative error
* code on failure.
*/
static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev,
unsigned int ngpios,
struct gpio_desc *descs[])
{
const char *label = dev_name(dev);
struct gpiochip_fwd *fwd;
struct gpio_chip *chip;
unsigned int i;
int error;
fwd = devm_kzalloc(dev, struct_size(fwd, tmp,
BITS_TO_LONGS(ngpios) + ngpios), GFP_KERNEL);
if (!fwd)
return ERR_PTR(-ENOMEM);
chip = &fwd->chip;
/*
* If any of the GPIO lines are sleeping, then the entire forwarder
* will be sleeping.
* If any of the chips support .set_config(), then the forwarder will
* support setting configs.
*/
for (i = 0; i < ngpios; i++) {
struct gpio_chip *parent = gpiod_to_chip(descs[i]);
dev_dbg(dev, "%u => gpio-%d\n", i, desc_to_gpio(descs[i]));
if (gpiod_cansleep(descs[i]))
chip->can_sleep = true;
if (parent && parent->set_config)
chip->set_config = gpio_fwd_set_config;
}
chip->label = label;
chip->parent = dev;
chip->owner = THIS_MODULE;
chip->get_direction = gpio_fwd_get_direction;
chip->direction_input = gpio_fwd_direction_input;
chip->direction_output = gpio_fwd_direction_output;
chip->get = gpio_fwd_get;
chip->get_multiple = gpio_fwd_get_multiple;
chip->set = gpio_fwd_set;
chip->set_multiple = gpio_fwd_set_multiple;
chip->base = -1;
chip->ngpio = ngpios;
fwd->descs = descs;
if (chip->can_sleep)
mutex_init(&fwd->mlock);
else
spin_lock_init(&fwd->slock);
error = devm_gpiochip_add_data(dev, chip, fwd);
if (error)
return ERR_PTR(error);
return fwd;
}
/*
* GPIO Aggregator platform device
*/
static int gpio_aggregator_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct gpio_desc **descs;
struct gpiochip_fwd *fwd;
int i, n;
n = gpiod_count(dev, NULL);
if (n < 0)
return n;
descs = devm_kmalloc_array(dev, n, sizeof(*descs), GFP_KERNEL);
if (!descs)
return -ENOMEM;
for (i = 0; i < n; i++) {
descs[i] = devm_gpiod_get_index(dev, NULL, i, GPIOD_ASIS);
if (IS_ERR(descs[i]))
return PTR_ERR(descs[i]);
}
fwd = gpiochip_fwd_create(dev, n, descs);
if (IS_ERR(fwd))
return PTR_ERR(fwd);
platform_set_drvdata(pdev, fwd);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id gpio_aggregator_dt_ids[] = {
/*
* Add GPIO-operated devices controlled from userspace below,
* or use "driver_override" in sysfs
*/
{},
};
MODULE_DEVICE_TABLE(of, gpio_aggregator_dt_ids);
#endif
static struct platform_driver gpio_aggregator_driver = {
.probe = gpio_aggregator_probe,
.driver = {
.name = DRV_NAME,
.groups = gpio_aggregator_groups,
.of_match_table = of_match_ptr(gpio_aggregator_dt_ids),
},
};
static int __init gpio_aggregator_init(void)
{
return platform_driver_register(&gpio_aggregator_driver);
}
module_init(gpio_aggregator_init);
static void __exit gpio_aggregator_exit(void)
{
gpio_aggregator_remove_all();
platform_driver_unregister(&gpio_aggregator_driver);
}
module_exit(gpio_aggregator_exit);
MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>");
MODULE_DESCRIPTION("GPIO Aggregator");
MODULE_LICENSE("GPL v2");

View file

@ -4622,7 +4622,7 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id,
if (!table)
return desc;
for (p = &table->table[0]; p->chip_label; p++) {
for (p = &table->table[0]; p->key; p++) {
struct gpio_chip *gc;
/* idx must always match exactly */
@ -4633,18 +4633,30 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id,
if (p->con_id && (!con_id || strcmp(p->con_id, con_id)))
continue;
gc = find_chip_by_name(p->chip_label);
if (p->chip_hwnum == U16_MAX) {
desc = gpio_name_to_desc(p->key);
if (desc) {
*flags = p->flags;
return desc;
}
dev_warn(dev, "cannot find GPIO line %s, deferring\n",
p->key);
return ERR_PTR(-EPROBE_DEFER);
}
gc = find_chip_by_name(p->key);
if (!gc) {
/*
* As the lookup table indicates a chip with
* p->chip_label should exist, assume it may
* p->key should exist, assume it may
* still appear later and let the interested
* consumer be probed again or let the Deferred
* Probe infrastructure handle the error.
*/
dev_warn(dev, "cannot find GPIO chip %s, deferring\n",
p->chip_label);
p->key);
return ERR_PTR(-EPROBE_DEFER);
}
@ -4675,7 +4687,7 @@ static int platform_gpio_count(struct device *dev, const char *con_id)
if (!table)
return -ENOENT;
for (p = &table->table[0]; p->chip_label; p++) {
for (p = &table->table[0]; p->key; p++) {
if ((con_id && p->con_id && !strcmp(con_id, p->con_id)) ||
(!con_id && !p->con_id))
count++;

View file

@ -1439,9 +1439,9 @@ static int i801_add_mux(struct i801_priv *priv)
return -ENOMEM;
lookup->dev_id = "i2c-mux-gpio";
for (i = 0; i < mux_config->n_gpios; i++) {
lookup->table[i].chip_label = mux_config->gpio_chip;
lookup->table[i].chip_hwnum = mux_config->gpios[i];
lookup->table[i].con_id = "mux";
lookup->table[i] = (struct gpiod_lookup)
GPIO_LOOKUP(mux_config->gpio_chip,
mux_config->gpios[i], "mux", 0);
}
gpiod_add_lookup_table(lookup);
priv->lookup = lookup;

View file

@ -1145,22 +1145,14 @@ static int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm,
return -ENOMEM;
lookup->dev_id = "i2c-gpio";
if (iic->pin_sda < 32)
lookup->table[0].chip_label = "SM501-LOW";
else
lookup->table[0].chip_label = "SM501-HIGH";
lookup->table[0].chip_hwnum = iic->pin_sda % 32;
lookup->table[0].con_id = NULL;
lookup->table[0].idx = 0;
lookup->table[0].flags = GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN;
if (iic->pin_scl < 32)
lookup->table[1].chip_label = "SM501-LOW";
else
lookup->table[1].chip_label = "SM501-HIGH";
lookup->table[1].chip_hwnum = iic->pin_scl % 32;
lookup->table[1].con_id = NULL;
lookup->table[1].idx = 1;
lookup->table[1].flags = GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN;
lookup->table[0] = (struct gpiod_lookup)
GPIO_LOOKUP_IDX(iic->pin_sda < 32 ? "SM501-LOW" : "SM501-HIGH",
iic->pin_sda % 32, NULL, 0,
GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN);
lookup->table[1] = (struct gpiod_lookup)
GPIO_LOOKUP_IDX(iic->pin_scl < 32 ? "SM501-LOW" : "SM501-HIGH",
iic->pin_scl % 32, NULL, 1,
GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN);
gpiod_add_lookup_table(lookup);
icd = dev_get_platdata(&pdev->dev);

View file

@ -20,8 +20,11 @@ enum gpio_lookup_flags {
/**
* struct gpiod_lookup - lookup table
* @chip_label: name of the chip the GPIO belongs to
* @chip_hwnum: hardware number (i.e. relative to the chip) of the GPIO
* @key: either the name of the chip the GPIO belongs to, or the GPIO line name
* Note that GPIO line names are not guaranteed to be globally unique,
* so this will use the first match found!
* @chip_hwnum: hardware number (i.e. relative to the chip) of the GPIO, or
* U16_MAX to indicate that @key is a GPIO line name
* @con_id: name of the GPIO from the device's point of view
* @idx: index of the GPIO in case several GPIOs share the same name
* @flags: bitmask of gpio_lookup_flags GPIO_* values
@ -30,7 +33,7 @@ enum gpio_lookup_flags {
* functions using platform data.
*/
struct gpiod_lookup {
const char *chip_label;
const char *key;
u16 chip_hwnum;
const char *con_id;
unsigned int idx;
@ -63,17 +66,17 @@ struct gpiod_hog {
/*
* Simple definition of a single GPIO under a con_id
*/
#define GPIO_LOOKUP(_chip_label, _chip_hwnum, _con_id, _flags) \
GPIO_LOOKUP_IDX(_chip_label, _chip_hwnum, _con_id, 0, _flags)
#define GPIO_LOOKUP(_key, _chip_hwnum, _con_id, _flags) \
GPIO_LOOKUP_IDX(_key, _chip_hwnum, _con_id, 0, _flags)
/*
* 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
* gpiod_get_index()
*/
#define GPIO_LOOKUP_IDX(_chip_label, _chip_hwnum, _con_id, _idx, _flags) \
#define GPIO_LOOKUP_IDX(_key, _chip_hwnum, _con_id, _idx, _flags) \
{ \
.chip_label = _chip_label, \
.key = _key, \
.chip_hwnum = _chip_hwnum, \
.con_id = _con_id, \
.idx = _idx, \