Commit a31f5c3a authored by Andy Shevchenko's avatar Andy Shevchenko Committed by Linus Walleij

gpio: acpi: Override GPIO initialization flags

This allows ACPI GPIO code to modify flags based on
ACPI GpioIo() / GpioInt() resources.
Signed-off-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Tested-by: default avatarJarkko Nikula <jarkko.nikula@linux.intel.com>
Reviewed-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent 2eca25af
...@@ -448,6 +448,34 @@ acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio) ...@@ -448,6 +448,34 @@ acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio)
} }
} }
int
acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update)
{
int ret = 0;
/*
* Check if the BIOS has IoRestriction with explicitly set direction
* and update @flags accordingly. Otherwise use whatever caller asked
* for.
*/
if (update & GPIOD_FLAGS_BIT_DIR_SET) {
enum gpiod_flags diff = *flags ^ update;
/*
* Check if caller supplied incompatible GPIO initialization
* flags.
*
* Return %-EINVAL to notify that firmware has different
* settings and we are going to use them.
*/
if (((*flags & GPIOD_FLAGS_BIT_DIR_SET) && (diff & GPIOD_FLAGS_BIT_DIR_OUT)) ||
((*flags & GPIOD_FLAGS_BIT_DIR_OUT) && (diff & GPIOD_FLAGS_BIT_DIR_VAL)))
ret = -EINVAL;
*flags = update;
}
return ret;
}
struct acpi_gpio_lookup { struct acpi_gpio_lookup {
struct acpi_gpio_info info; struct acpi_gpio_info info;
int index; int index;
...@@ -485,8 +513,11 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) ...@@ -485,8 +513,11 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data)
* - ACPI_ACTIVE_HIGH == GPIO_ACTIVE_HIGH * - ACPI_ACTIVE_HIGH == GPIO_ACTIVE_HIGH
*/ */
if (lookup->info.gpioint) { if (lookup->info.gpioint) {
lookup->info.flags = GPIOD_IN;
lookup->info.polarity = agpio->polarity; lookup->info.polarity = agpio->polarity;
lookup->info.triggering = agpio->triggering; lookup->info.triggering = agpio->triggering;
} else {
lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio);
} }
} }
...@@ -613,13 +644,14 @@ static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, ...@@ -613,13 +644,14 @@ static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev,
struct gpio_desc *acpi_find_gpio(struct device *dev, struct gpio_desc *acpi_find_gpio(struct device *dev,
const char *con_id, const char *con_id,
unsigned int idx, unsigned int idx,
enum gpiod_flags flags, enum gpiod_flags *dflags,
enum gpio_lookup_flags *lookupflags) enum gpio_lookup_flags *lookupflags)
{ {
struct acpi_device *adev = ACPI_COMPANION(dev); struct acpi_device *adev = ACPI_COMPANION(dev);
struct acpi_gpio_info info; struct acpi_gpio_info info;
struct gpio_desc *desc; struct gpio_desc *desc;
char propname[32]; char propname[32];
int err;
int i; int i;
/* Try first from _DSD */ /* Try first from _DSD */
...@@ -650,7 +682,7 @@ struct gpio_desc *acpi_find_gpio(struct device *dev, ...@@ -650,7 +682,7 @@ struct gpio_desc *acpi_find_gpio(struct device *dev,
} }
if (info.gpioint && if (info.gpioint &&
(flags == GPIOD_OUT_LOW || flags == GPIOD_OUT_HIGH)) { (*dflags == GPIOD_OUT_LOW || *dflags == GPIOD_OUT_HIGH)) {
dev_dbg(dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n"); dev_dbg(dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n");
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
} }
...@@ -658,6 +690,10 @@ struct gpio_desc *acpi_find_gpio(struct device *dev, ...@@ -658,6 +690,10 @@ struct gpio_desc *acpi_find_gpio(struct device *dev,
if (info.polarity == GPIO_ACTIVE_LOW) if (info.polarity == GPIO_ACTIVE_LOW)
*lookupflags |= GPIO_ACTIVE_LOW; *lookupflags |= GPIO_ACTIVE_LOW;
err = acpi_gpio_update_gpiod_flags(dflags, info.flags);
if (err)
dev_dbg(dev, "Override GPIO initialization flags\n");
return desc; return desc;
} }
...@@ -711,12 +747,16 @@ struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode, ...@@ -711,12 +747,16 @@ struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
* used to translate from the GPIO offset in the resource to the Linux IRQ * used to translate from the GPIO offset in the resource to the Linux IRQ
* number. * number.
* *
* The function is idempotent, though each time it runs it will configure GPIO
* pin direction according to the flags in GpioInt resource.
*
* Return: Linux IRQ number (>%0) on success, negative errno on failure. * Return: Linux IRQ number (>%0) on success, negative errno on failure.
*/ */
int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
{ {
int idx, i; int idx, i;
unsigned int irq_flags; unsigned int irq_flags;
int ret;
for (i = 0, idx = 0; idx <= index; i++) { for (i = 0, idx = 0; idx <= index; i++) {
struct acpi_gpio_info info; struct acpi_gpio_info info;
...@@ -729,6 +769,7 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) ...@@ -729,6 +769,7 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
return PTR_ERR(desc); return PTR_ERR(desc);
if (info.gpioint && idx++ == index) { if (info.gpioint && idx++ == index) {
char label[32];
int irq; int irq;
if (IS_ERR(desc)) if (IS_ERR(desc))
...@@ -738,6 +779,11 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) ...@@ -738,6 +779,11 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
if (irq < 0) if (irq < 0)
return irq; return irq;
snprintf(label, sizeof(label), "GpioInt() %d", index);
ret = gpiod_configure_flags(desc, label, 0, info.flags);
if (ret < 0)
return ret;
irq_flags = acpi_dev_get_irq_type(info.triggering, irq_flags = acpi_dev_get_irq_type(info.triggering,
info.polarity); info.polarity);
......
...@@ -3286,7 +3286,7 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, ...@@ -3286,7 +3286,7 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
desc = of_find_gpio(dev, con_id, idx, &lookupflags); desc = of_find_gpio(dev, con_id, idx, &lookupflags);
} else if (ACPI_COMPANION(dev)) { } else if (ACPI_COMPANION(dev)) {
dev_dbg(dev, "using ACPI for GPIO lookup\n"); dev_dbg(dev, "using ACPI for GPIO lookup\n");
desc = acpi_find_gpio(dev, con_id, idx, flags, &lookupflags); desc = acpi_find_gpio(dev, con_id, idx, &flags, &lookupflags);
} }
} }
...@@ -3367,8 +3367,12 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, ...@@ -3367,8 +3367,12 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
struct acpi_gpio_info info; struct acpi_gpio_info info;
desc = acpi_node_get_gpiod(fwnode, propname, index, &info); desc = acpi_node_get_gpiod(fwnode, propname, index, &info);
if (!IS_ERR(desc)) if (!IS_ERR(desc)) {
active_low = info.polarity == GPIO_ACTIVE_LOW; active_low = info.polarity == GPIO_ACTIVE_LOW;
ret = acpi_gpio_update_gpiod_flags(&dflags, info.flags);
if (ret)
pr_debug("Override GPIO initialization flags\n");
}
} }
if (IS_ERR(desc)) if (IS_ERR(desc))
......
...@@ -75,11 +75,13 @@ struct gpio_device { ...@@ -75,11 +75,13 @@ struct gpio_device {
/** /**
* struct acpi_gpio_info - ACPI GPIO specific information * struct acpi_gpio_info - ACPI GPIO specific information
* @flags: GPIO initialization flags
* @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo
* @polarity: interrupt polarity as provided by ACPI * @polarity: interrupt polarity as provided by ACPI
* @triggering: triggering type as provided by ACPI * @triggering: triggering type as provided by ACPI
*/ */
struct acpi_gpio_info { struct acpi_gpio_info {
enum gpiod_flags flags;
bool gpioint; bool gpioint;
int polarity; int polarity;
int triggering; int triggering;
...@@ -121,10 +123,13 @@ void acpi_gpiochip_remove(struct gpio_chip *chip); ...@@ -121,10 +123,13 @@ void acpi_gpiochip_remove(struct gpio_chip *chip);
void acpi_gpiochip_request_interrupts(struct gpio_chip *chip); void acpi_gpiochip_request_interrupts(struct gpio_chip *chip);
void acpi_gpiochip_free_interrupts(struct gpio_chip *chip); void acpi_gpiochip_free_interrupts(struct gpio_chip *chip);
int acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags,
enum gpiod_flags update);
struct gpio_desc *acpi_find_gpio(struct device *dev, struct gpio_desc *acpi_find_gpio(struct device *dev,
const char *con_id, const char *con_id,
unsigned int idx, unsigned int idx,
enum gpiod_flags flags, enum gpiod_flags *dflags,
enum gpio_lookup_flags *lookupflags); enum gpio_lookup_flags *lookupflags);
struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode, struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
const char *propname, int index, const char *propname, int index,
...@@ -143,9 +148,15 @@ acpi_gpiochip_request_interrupts(struct gpio_chip *chip) { } ...@@ -143,9 +148,15 @@ acpi_gpiochip_request_interrupts(struct gpio_chip *chip) { }
static inline void static inline void
acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { } acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { }
static inline int
acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update)
{
return 0;
}
static inline struct gpio_desc * static inline struct gpio_desc *
acpi_find_gpio(struct device *dev, const char *con_id, acpi_find_gpio(struct device *dev, const char *con_id,
unsigned int idx, enum gpiod_flags flags, unsigned int idx, enum gpiod_flags *dflags,
enum gpio_lookup_flags *lookupflags) enum gpio_lookup_flags *lookupflags)
{ {
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
......
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