gpiolib: consolidate GPIO lookups
Ensure that all paths to obtain/look up GPIOD from generic consumer-visible APIs go through the new gpiod_find_and_request() helper, so that we can easily extend it with support for new firmware mechanisms. The only exception is OF-specific [devm_]gpiod_get_from_of_node() API that is still being used by a couple of drivers and will be removed as soon as patches converting them to use generic fwnode/device APIs are accepted. Acked-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
This commit is contained in:
parent
b7452d670f
commit
8eb1f71e7a
3 changed files with 77 additions and 178 deletions
|
@ -1024,45 +1024,6 @@ struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode,
|
||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* acpi_node_get_gpiod() - get a GPIO descriptor from ACPI resources
|
|
||||||
* @fwnode: pointer to an ACPI firmware node to get the GPIO information from
|
|
||||||
* @propname: Property name of the GPIO
|
|
||||||
* @index: index of GpioIo/GpioInt resource (starting from %0)
|
|
||||||
* @lflags: bitmask of gpio_lookup_flags GPIO_* values
|
|
||||||
* @dflags: gpiod initialization flags
|
|
||||||
*
|
|
||||||
* If @fwnode is an ACPI device object, call acpi_get_gpiod_by_index() for it.
|
|
||||||
* Otherwise (i.e. it is a data-only non-device object), use the property-based
|
|
||||||
* GPIO lookup to get to the GPIO resource with the relevant information and use
|
|
||||||
* that to obtain the GPIO descriptor to return.
|
|
||||||
*
|
|
||||||
* If the GPIO cannot be translated or there is an error an ERR_PTR is
|
|
||||||
* returned.
|
|
||||||
*/
|
|
||||||
struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
|
|
||||||
const char *propname, int index,
|
|
||||||
unsigned long *lflags,
|
|
||||||
enum gpiod_flags *dflags)
|
|
||||||
{
|
|
||||||
struct acpi_gpio_info info;
|
|
||||||
struct acpi_device *adev;
|
|
||||||
struct gpio_desc *desc;
|
|
||||||
|
|
||||||
adev = to_acpi_device_node(fwnode);
|
|
||||||
if (adev)
|
|
||||||
desc = acpi_get_gpiod_by_index(adev, propname, index, &info);
|
|
||||||
else
|
|
||||||
desc = acpi_get_gpiod_from_data(fwnode, propname, index, &info);
|
|
||||||
|
|
||||||
if (!IS_ERR(desc)) {
|
|
||||||
acpi_gpio_update_gpiod_flags(dflags, &info);
|
|
||||||
acpi_gpio_update_gpiod_lookup_flags(lflags, &info);
|
|
||||||
}
|
|
||||||
|
|
||||||
return desc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* acpi_dev_gpio_irq_wake_get_by() - Find GpioInt and translate it to Linux IRQ number
|
* acpi_dev_gpio_irq_wake_get_by() - Find GpioInt and translate it to Linux IRQ number
|
||||||
* @adev: pointer to a ACPI device to get IRQ from
|
* @adev: pointer to a ACPI device to get IRQ from
|
||||||
|
|
|
@ -24,10 +24,6 @@ struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode,
|
||||||
unsigned int idx,
|
unsigned int idx,
|
||||||
enum gpiod_flags *dflags,
|
enum gpiod_flags *dflags,
|
||||||
unsigned long *lookupflags);
|
unsigned long *lookupflags);
|
||||||
struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
|
|
||||||
const char *propname, int index,
|
|
||||||
unsigned long *lflags,
|
|
||||||
enum gpiod_flags *dflags);
|
|
||||||
|
|
||||||
int acpi_gpio_count(struct device *dev, const char *con_id);
|
int acpi_gpio_count(struct device *dev, const char *con_id);
|
||||||
#else
|
#else
|
||||||
|
@ -49,12 +45,6 @@ acpi_find_gpio(struct fwnode_handle *fwnode, const char *con_id,
|
||||||
{
|
{
|
||||||
return ERR_PTR(-ENOENT);
|
return ERR_PTR(-ENOENT);
|
||||||
}
|
}
|
||||||
static inline struct gpio_desc *
|
|
||||||
acpi_node_get_gpiod(struct fwnode_handle *fwnode, const char *propname,
|
|
||||||
int index, unsigned long *lflags, enum gpiod_flags *dflags)
|
|
||||||
{
|
|
||||||
return ERR_PTR(-ENXIO);
|
|
||||||
}
|
|
||||||
static inline int acpi_gpio_count(struct device *dev, const char *con_id)
|
static inline int acpi_gpio_count(struct device *dev, const char *con_id)
|
||||||
{
|
{
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
|
@ -366,7 +366,7 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc)
|
||||||
static int devprop_gpiochip_set_names(struct gpio_chip *chip)
|
static int devprop_gpiochip_set_names(struct gpio_chip *chip)
|
||||||
{
|
{
|
||||||
struct gpio_device *gdev = chip->gpiodev;
|
struct gpio_device *gdev = chip->gpiodev;
|
||||||
struct fwnode_handle *fwnode = dev_fwnode(&gdev->dev);
|
const struct fwnode_handle *fwnode = dev_fwnode(&gdev->dev);
|
||||||
const char **names;
|
const char **names;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
int count;
|
int count;
|
||||||
|
@ -3853,58 +3853,84 @@ static int platform_gpio_count(struct device *dev, const char *con_id)
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static struct gpio_desc *gpiod_find_by_fwnode(struct fwnode_handle *fwnode,
|
||||||
* fwnode_get_named_gpiod - obtain a GPIO from firmware node
|
struct device *consumer,
|
||||||
* @fwnode: handle of the firmware node
|
const char *con_id,
|
||||||
* @propname: name of the firmware property representing the GPIO
|
unsigned int idx,
|
||||||
* @index: index of the GPIO to obtain for the consumer
|
enum gpiod_flags *flags,
|
||||||
* @dflags: GPIO initialization flags
|
unsigned long *lookupflags)
|
||||||
* @label: label to attach to the requested GPIO
|
|
||||||
*
|
|
||||||
* This function can be used for drivers that get their configuration
|
|
||||||
* from opaque firmware.
|
|
||||||
*
|
|
||||||
* The function properly finds the corresponding GPIO using whatever is the
|
|
||||||
* underlying firmware interface and then makes sure that the GPIO
|
|
||||||
* descriptor is requested before it is returned to the caller.
|
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
* On successful request the GPIO pin is configured in accordance with
|
|
||||||
* provided @dflags.
|
|
||||||
*
|
|
||||||
* In case of error an ERR_PTR() is returned.
|
|
||||||
*/
|
|
||||||
static struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
|
|
||||||
const char *propname, int index,
|
|
||||||
enum gpiod_flags dflags,
|
|
||||||
const char *label)
|
|
||||||
{
|
{
|
||||||
unsigned long lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
|
struct gpio_desc *desc = ERR_PTR(-ENOENT);
|
||||||
struct gpio_desc *desc = ERR_PTR(-ENODEV);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (is_of_node(fwnode)) {
|
if (is_of_node(fwnode)) {
|
||||||
desc = gpiod_get_from_of_node(to_of_node(fwnode),
|
dev_dbg(consumer, "using DT '%pfw' for '%s' GPIO lookup\n",
|
||||||
propname, index,
|
fwnode, con_id);
|
||||||
dflags,
|
desc = of_find_gpio(to_of_node(fwnode), con_id, idx, lookupflags);
|
||||||
label);
|
|
||||||
return desc;
|
|
||||||
} else if (is_acpi_node(fwnode)) {
|
} else if (is_acpi_node(fwnode)) {
|
||||||
desc = acpi_node_get_gpiod(fwnode, propname, index,
|
dev_dbg(consumer, "using ACPI '%pfw' for '%s' GPIO lookup\n",
|
||||||
&lflags, &dflags);
|
fwnode, con_id);
|
||||||
if (IS_ERR(desc))
|
desc = acpi_find_gpio(fwnode, con_id, idx, flags, lookupflags);
|
||||||
return desc;
|
|
||||||
} else {
|
|
||||||
return ERR_PTR(-EINVAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Currently only ACPI takes this path */
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct gpio_desc *gpiod_find_and_request(struct device *consumer,
|
||||||
|
struct fwnode_handle *fwnode,
|
||||||
|
const char *con_id,
|
||||||
|
unsigned int idx,
|
||||||
|
enum gpiod_flags flags,
|
||||||
|
const char *label,
|
||||||
|
bool platform_lookup_allowed)
|
||||||
|
{
|
||||||
|
struct gpio_desc *desc = ERR_PTR(-ENOENT);
|
||||||
|
unsigned long lookupflags;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!IS_ERR_OR_NULL(fwnode))
|
||||||
|
desc = gpiod_find_by_fwnode(fwnode, consumer, con_id, idx,
|
||||||
|
&flags, &lookupflags);
|
||||||
|
|
||||||
|
if (gpiod_not_found(desc) && platform_lookup_allowed) {
|
||||||
|
/*
|
||||||
|
* Either we are not using DT or ACPI, or their lookup did not
|
||||||
|
* return a result. In that case, use platform lookup as a
|
||||||
|
* fallback.
|
||||||
|
*/
|
||||||
|
dev_dbg(consumer, "using lookup tables for GPIO lookup\n");
|
||||||
|
desc = gpiod_find(consumer, con_id, idx, &lookupflags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_ERR(desc)) {
|
||||||
|
dev_dbg(consumer, "No GPIO consumer %s found\n", con_id);
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If a connection label was passed use that, else attempt to use
|
||||||
|
* the device name as label
|
||||||
|
*/
|
||||||
ret = gpiod_request(desc, label);
|
ret = gpiod_request(desc, label);
|
||||||
if (ret)
|
if (ret) {
|
||||||
|
if (!(ret == -EBUSY && flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE))
|
||||||
return ERR_PTR(ret);
|
return ERR_PTR(ret);
|
||||||
|
|
||||||
ret = gpiod_configure_flags(desc, propname, lflags, dflags);
|
/*
|
||||||
|
* This happens when there are several consumers for
|
||||||
|
* the same GPIO line: we just return here without
|
||||||
|
* further initialization. It is a bit of a hack.
|
||||||
|
* This is necessary to support fixed regulators.
|
||||||
|
*
|
||||||
|
* FIXME: Make this more sane and safe.
|
||||||
|
*/
|
||||||
|
dev_info(consumer,
|
||||||
|
"nonexclusive access to GPIO for %s\n", con_id);
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gpiod_configure_flags(desc, con_id, lookupflags, flags);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
dev_dbg(consumer, "setup of GPIO %s failed\n", con_id);
|
||||||
gpiod_put(desc);
|
gpiod_put(desc);
|
||||||
return ERR_PTR(ret);
|
return ERR_PTR(ret);
|
||||||
}
|
}
|
||||||
|
@ -3937,29 +3963,12 @@ static struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
|
||||||
* In case of error an ERR_PTR() is returned.
|
* In case of error an ERR_PTR() is returned.
|
||||||
*/
|
*/
|
||||||
struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode,
|
struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode,
|
||||||
const char *con_id, int index,
|
const char *con_id,
|
||||||
|
int index,
|
||||||
enum gpiod_flags flags,
|
enum gpiod_flags flags,
|
||||||
const char *label)
|
const char *label)
|
||||||
{
|
{
|
||||||
struct gpio_desc *desc;
|
return gpiod_find_and_request(NULL, fwnode, con_id, index, flags, label, false);
|
||||||
char prop_name[32]; /* 32 is max size of property name */
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
|
|
||||||
if (con_id)
|
|
||||||
snprintf(prop_name, sizeof(prop_name), "%s-%s",
|
|
||||||
con_id, gpio_suffixes[i]);
|
|
||||||
else
|
|
||||||
snprintf(prop_name, sizeof(prop_name), "%s",
|
|
||||||
gpio_suffixes[i]);
|
|
||||||
|
|
||||||
desc = fwnode_get_named_gpiod(fwnode, prop_name, index, flags,
|
|
||||||
label);
|
|
||||||
if (!gpiod_not_found(desc))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return desc;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(fwnode_gpiod_get_index);
|
EXPORT_SYMBOL_GPL(fwnode_gpiod_get_index);
|
||||||
|
|
||||||
|
@ -4113,72 +4122,11 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
|
||||||
unsigned int idx,
|
unsigned int idx,
|
||||||
enum gpiod_flags flags)
|
enum gpiod_flags flags)
|
||||||
{
|
{
|
||||||
unsigned long lookupflags = GPIO_LOOKUP_FLAGS_DEFAULT;
|
|
||||||
struct gpio_desc *desc = NULL;
|
|
||||||
int ret;
|
|
||||||
/* Maybe we have a device name, maybe not */
|
|
||||||
const char *devname = dev ? dev_name(dev) : "?";
|
|
||||||
struct fwnode_handle *fwnode = dev ? dev_fwnode(dev) : NULL;
|
struct fwnode_handle *fwnode = dev ? dev_fwnode(dev) : NULL;
|
||||||
|
const char *devname = dev ? dev_name(dev) : "?";
|
||||||
|
const char *label = con_id ?: devname;
|
||||||
|
|
||||||
dev_dbg(dev, "GPIO lookup for consumer %s\n", con_id);
|
return gpiod_find_and_request(dev, fwnode, con_id, idx, flags, label, true);
|
||||||
|
|
||||||
/* Using device tree? */
|
|
||||||
if (is_of_node(fwnode)) {
|
|
||||||
dev_dbg(dev, "using device tree for GPIO lookup\n");
|
|
||||||
desc = of_find_gpio(to_of_node(fwnode),
|
|
||||||
con_id, idx, &lookupflags);
|
|
||||||
} else if (is_acpi_node(fwnode)) {
|
|
||||||
dev_dbg(dev, "using ACPI for GPIO lookup\n");
|
|
||||||
desc = acpi_find_gpio(fwnode,
|
|
||||||
con_id, idx, &flags, &lookupflags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Either we are not using DT or ACPI, or their lookup did not return
|
|
||||||
* a result. In that case, use platform lookup as a fallback.
|
|
||||||
*/
|
|
||||||
if (!desc || gpiod_not_found(desc)) {
|
|
||||||
dev_dbg(dev, "using lookup tables for GPIO lookup\n");
|
|
||||||
desc = gpiod_find(dev, con_id, idx, &lookupflags);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IS_ERR(desc)) {
|
|
||||||
dev_dbg(dev, "No GPIO consumer %s found\n", con_id);
|
|
||||||
return desc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If a connection label was passed use that, else attempt to use
|
|
||||||
* the device name as label
|
|
||||||
*/
|
|
||||||
ret = gpiod_request(desc, con_id ?: devname);
|
|
||||||
if (ret) {
|
|
||||||
if (!(ret == -EBUSY && flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE))
|
|
||||||
return ERR_PTR(ret);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This happens when there are several consumers for
|
|
||||||
* the same GPIO line: we just return here without
|
|
||||||
* further initialization. It is a bit of a hack.
|
|
||||||
* This is necessary to support fixed regulators.
|
|
||||||
*
|
|
||||||
* FIXME: Make this more sane and safe.
|
|
||||||
*/
|
|
||||||
dev_info(dev, "nonexclusive access to GPIO for %s\n", con_id ?: devname);
|
|
||||||
return desc;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = gpiod_configure_flags(desc, con_id, lookupflags, flags);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_dbg(dev, "setup of GPIO %s failed\n", con_id);
|
|
||||||
gpiod_put(desc);
|
|
||||||
return ERR_PTR(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
blocking_notifier_call_chain(&desc->gdev->notifier,
|
|
||||||
GPIOLINE_CHANGED_REQUESTED, desc);
|
|
||||||
|
|
||||||
return desc;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(gpiod_get_index);
|
EXPORT_SYMBOL_GPL(gpiod_get_index);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue