}
}
+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_info info;
int index;
* - ACPI_ACTIVE_HIGH == GPIO_ACTIVE_HIGH
*/
if (lookup->info.gpioint) {
+ lookup->info.flags = GPIOD_IN;
lookup->info.polarity = agpio->polarity;
lookup->info.triggering = agpio->triggering;
+ } else {
+ lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio);
}
}
struct gpio_desc *acpi_find_gpio(struct device *dev,
const char *con_id,
unsigned int idx,
- enum gpiod_flags flags,
+ enum gpiod_flags *dflags,
enum gpio_lookup_flags *lookupflags)
{
struct acpi_device *adev = ACPI_COMPANION(dev);
struct acpi_gpio_info info;
struct gpio_desc *desc;
char propname[32];
+ int err;
int i;
/* Try first from _DSD */
}
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");
return ERR_PTR(-ENOENT);
}
if (info.polarity == 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;
}
* used to translate from the GPIO offset in the resource to the Linux IRQ
* 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.
*/
int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
{
int idx, i;
unsigned int irq_flags;
+ int ret;
for (i = 0, idx = 0; idx <= index; i++) {
struct acpi_gpio_info info;
return PTR_ERR(desc);
if (info.gpioint && idx++ == index) {
+ char label[32];
int irq;
if (IS_ERR(desc))
if (irq < 0)
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,
info.polarity);
desc = of_find_gpio(dev, con_id, idx, &lookupflags);
} else if (ACPI_COMPANION(dev)) {
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);
}
}
struct acpi_gpio_info 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;
+ ret = acpi_gpio_update_gpiod_flags(&dflags, info.flags);
+ if (ret)
+ pr_debug("Override GPIO initialization flags\n");
+ }
}
if (IS_ERR(desc))
/**
* 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
* @polarity: interrupt polarity as provided by ACPI
* @triggering: triggering type as provided by ACPI
*/
struct acpi_gpio_info {
+ enum gpiod_flags flags;
bool gpioint;
int polarity;
int triggering;
void acpi_gpiochip_request_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,
const char *con_id,
unsigned int idx,
- enum gpiod_flags flags,
+ enum gpiod_flags *dflags,
enum gpio_lookup_flags *lookupflags);
struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
const char *propname, int index,
static inline void
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 *
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)
{
return ERR_PTR(-ENOENT);