/** * struct acpi_gpio_event - ACPI GPIO event handler data * * @node: list-entry of the events list of the struct acpi_gpio_chip * @handle: handle of ACPI method to execute when the IRQ triggers * @handler: handler function to pass to request_irq() when requesting the IRQ * @pin: GPIO pin number on the struct gpio_chip * @irq: Linux IRQ number for the event, for request_irq() / free_irq() * @irqflags: flags to pass to request_irq() when requesting the IRQ * @irq_is_wake: If the ACPI flags indicate the IRQ is a wakeup source * @irq_requested:True if request_irq() has been done * @desc: struct gpio_desc for the GPIO pin for this event
*/ struct acpi_gpio_event { struct list_head node;
acpi_handle handle;
irq_handler_t handler; unsignedint pin; unsignedint irq; unsignedlong irqflags; bool irq_is_wake; bool irq_requested; struct gpio_desc *desc;
};
struct acpi_gpio_chip { /* * ACPICA requires that the first field of the context parameter * passed to acpi_install_address_space_handler() is large enough * to hold struct acpi_connection_info.
*/ struct acpi_connection_info conn_info; struct list_head conns; struct mutex conn_lock; struct gpio_chip *chip; struct list_head events; struct list_head deferred_req_irqs_list_entry;
};
/** * struct acpi_gpio_info - ACPI GPIO specific information * @adev: reference to ACPI device which consumes GPIO resource * @flags: GPIO initialization flags * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo * @wake_capable: wake capability as provided by ACPI * @pin_config: pin bias as provided by ACPI * @polarity: interrupt polarity as provided by ACPI * @triggering: triggering type as provided by ACPI * @debounce: debounce timeout as provided by ACPI * @quirks: Linux specific quirks as provided by struct acpi_gpio_mapping
*/ struct acpi_gpio_info { struct acpi_device *adev; enum gpiod_flags flags; bool gpioint; bool wake_capable; int pin_config; int polarity; int triggering; unsignedint debounce; unsignedint quirks;
};
staticint acpi_gpiochip_find(struct gpio_chip *gc, constvoid *data)
{ /* First check the actual GPIO device */ if (device_match_acpi_handle(&gc->gpiodev->dev, data)) returntrue;
/* * When the ACPI device is artificially split to the banks of GPIOs, * where each of them is represented by a separate GPIO device, * the firmware node of the physical device may not be shared among * the banks as they may require different values for the same property, * e.g., number of GPIOs in a certain bank. In such case the ACPI handle * of a GPIO device is NULL and can not be used. Hence we have to check * the parent device to be sure that there is no match before bailing * out.
*/ if (gc->parent) return device_match_acpi_handle(gc->parent, data);
returnfalse;
}
/** * acpi_get_gpiod() - Translate ACPI GPIO pin to GPIO descriptor usable with GPIO API * @path: ACPI GPIO controller full path name, (e.g. "\\_SB.GPO1") * @pin: ACPI GPIO pin number (0-based, controller-relative) * * Returns: * GPIO descriptor to use with Linux generic GPIO API. * If the GPIO cannot be translated or there is an error an ERR_PTR is * returned. * * Specifically returns %-EPROBE_DEFER if the referenced GPIO * controller does not have GPIO chip registered at the moment. This is to * support probe deferral.
*/ staticstruct gpio_desc *acpi_get_gpiod(char *path, unsignedint pin)
{
acpi_handle handle;
acpi_status status;
status = acpi_get_handle(NULL, path, &handle); if (ACPI_FAILURE(status)) return ERR_PTR(-ENODEV);
/** * acpi_gpio_get_io_resource - Fetch details of an ACPI resource if it is a GPIO * I/O resource or return False if not. * @ares: Pointer to the ACPI resource to fetch * @agpio: Pointer to a &struct acpi_resource_gpio to store the output pointer * * Returns: * %true if GpioIo resource is found, %false otherwise.
*/ bool acpi_gpio_get_io_resource(struct acpi_resource *ares, struct acpi_resource_gpio **agpio)
{ struct acpi_resource_gpio *gpio;
if (ares->type != ACPI_RESOURCE_TYPE_GPIO) returnfalse;
gpio = &ares->data.gpio; if (gpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_IO) returnfalse;
ret = request_threaded_irq(event->irq, NULL, event->handler,
event->irqflags | IRQF_ONESHOT, "ACPI:Event", event); if (ret) {
dev_err(parent, "Failed to setup interrupt handler for %d\n", event->irq); return;
}
if (event->irq_is_wake)
enable_irq_wake(event->irq);
event->irq_requested = true;
/* Make sure we trigger the initial state of edge-triggered IRQs */ if (acpi_gpio_need_run_edge_events_on_boot() &&
(event->irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))) {
value = gpiod_get_raw_value_cansleep(event->desc); if (((event->irqflags & IRQF_TRIGGER_RISING) && value == 1) ||
((event->irqflags & IRQF_TRIGGER_FALLING) && value == 0))
event->handler(event->irq, event);
}
}
staticenum gpiod_flags
acpi_gpio_to_gpiod_flags(conststruct acpi_resource_gpio *agpio, int polarity)
{ /* GpioInt() implies input configuration */ if (agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT) return GPIOD_IN;
switch (agpio->io_restriction) { case ACPI_IO_RESTRICT_INPUT: return GPIOD_IN; case ACPI_IO_RESTRICT_OUTPUT: /* * ACPI GPIO resources don't contain an initial value for the * GPIO. Therefore we deduce that value from the pull field * and the polarity instead. If the pin is pulled up we assume * default to be high, if it is pulled down we assume default * to be low, otherwise we leave pin untouched. For active low * polarity values will be switched. See also * Documentation/firmware-guide/acpi/gpio-properties.rst.
*/ switch (agpio->pin_config) { case ACPI_PIN_CONFIG_PULLUP: return polarity == GPIO_ACTIVE_LOW ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH; case ACPI_PIN_CONFIG_PULLDOWN: return polarity == GPIO_ACTIVE_LOW ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW; default: break;
} break; default: break;
}
/* * Assume that the BIOS has configured the direction and pull * accordingly.
*/ return GPIOD_ASIS;
}
/* ACPI uses hundredths of milliseconds units */
ret = gpio_set_debounce_timeout(desc, agpio->debounce_timeout * 10); if (ret)
dev_warn(chip->parent, "Failed to set debounce-timeout for pin 0x%04X, err %d\n",
pin, ret);
/** * acpi_gpiochip_request_interrupts() - Register isr for gpio chip ACPI events * @chip: GPIO chip * * ACPI5 platforms can use GPIO signaled ACPI events. These GPIO interrupts are * handled by ACPI event methods which need to be called from the GPIO * chip's interrupt handler. acpi_gpiochip_request_interrupts() finds out which * GPIO pins have ACPI event methods and assigns interrupt handlers that calls * the ACPI event methods for those pins.
*/ void acpi_gpiochip_request_interrupts(struct gpio_chip *chip)
{ struct acpi_gpio_chip *acpi_gpio;
acpi_handle handle;
acpi_status status;
if (!chip->parent || !chip->to_irq) return;
handle = ACPI_HANDLE(chip->parent); if (!handle) return;
status = acpi_get_data(handle, acpi_gpio_chip_dh, (void **)&acpi_gpio); if (ACPI_FAILURE(status)) return;
if (acpi_quirk_skip_gpio_event_handlers()) return;
/* * 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 = (*flags & ~mask) | (update & mask);
} return ret;
}
staticint acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, struct acpi_gpio_info *info)
{ struct device *dev = &info->adev->dev; enum gpiod_flags old = *flags; int ret;
ret = __acpi_gpio_update_gpiod_flags(&old, info->flags); if (info->quirks & ACPI_GPIO_QUIRK_NO_IO_RESTRICTION) { if (ret)
dev_warn(dev, FW_BUG "GPIO not in correct mode, fixing\n");
} else { if (ret)
dev_dbg(dev, "Override GPIO initialization flags\n");
*flags = old;
}
ret = __acpi_node_get_property_reference(fwnode, propname, index, 3, &args); if (ret) { struct acpi_device *adev;
adev = to_acpi_device_node(fwnode); if (!acpi_get_driver_gpio_data(adev, propname, index, &args, &quirks)) return ret;
} /* * The property was found and resolved, so need to lookup the GPIO based * on returned args.
*/ if (!to_acpi_device_node(args.fwnode)) return -EINVAL; if (args.nargs != 3) return -EPROTO;
/** * acpi_get_gpiod_by_index() - get a GPIO descriptor from device resources * @adev: pointer to a ACPI device to get GPIO from * @propname: Property name of the GPIO (optional) * @lookup: pointer to struct acpi_gpio_lookup to fill in * * Function goes through ACPI resources for @adev and based on @lookup.index looks * up a GpioIo/GpioInt resource, translates it to the Linux GPIO descriptor, * and returns it. @lookup.index matches GpioIo/GpioInt resources only so if there * are total 3 GPIO resources, the index goes from 0 to 2. * * If @propname is specified the GPIO is looked using device property. In * that case @index is used to select the GPIO entry in the property value * (in case of multiple). * * Returns: * 0 on success, negative errno on failure. * * The @lookup is filled with GPIO descriptor to use with Linux generic GPIO API. * If the GPIO cannot be translated an error will be returned. * * Note: if the GPIO resource has multiple entries in the pin list, this * function only returns the first.
*/ staticint acpi_get_gpiod_by_index(struct acpi_device *adev, constchar *propname, struct acpi_gpio_lookup *lookup)
{ struct acpi_gpio_params *params = &lookup->params; struct acpi_gpio_info *info = lookup->info; int ret;
if (propname) {
dev_dbg(&adev->dev, "GPIO: looking up %s\n", propname);
ret = acpi_gpio_property_lookup(acpi_fwnode_handle(adev), propname, lookup); if (ret) return ret;
dev_dbg(&adev->dev, "GPIO: _DSD returned %s %u %u %u\n",
dev_name(&info->adev->dev),
params->crs_entry_index, params->line_index, params->active_low);
} else {
dev_dbg(&adev->dev, "GPIO: looking up %u in _CRS\n", params->crs_entry_index);
info->adev = adev;
}
return acpi_gpio_resource_lookup(lookup);
}
/** * acpi_get_gpiod_from_data() - get a GPIO descriptor from ACPI data node * @fwnode: pointer to an ACPI firmware node to get the GPIO information from * @propname: Property name of the GPIO * @lookup: pointer to struct acpi_gpio_lookup to fill in * * This function uses the property-based GPIO lookup to get to the GPIO * resource with the relevant information from a data-only ACPI firmware node * and uses that to obtain the GPIO descriptor to return. * * Returns: * 0 on success, negative errno on failure. * * The @lookup is filled with GPIO descriptor to use with Linux generic GPIO API. * If the GPIO cannot be translated an error will be returned.
*/ staticint acpi_get_gpiod_from_data(struct fwnode_handle *fwnode, constchar *propname, struct acpi_gpio_lookup *lookup)
{ int ret;
if (!is_acpi_data_node(fwnode)) return -ENODEV;
if (!propname) return -EINVAL;
ret = acpi_gpio_property_lookup(fwnode, propname, lookup); if (ret) return ret;
return acpi_gpio_resource_lookup(lookup);
}
staticbool acpi_can_fallback_to_crs(struct acpi_device *adev, constchar *con_id)
{ /* If there is no ACPI device, there is no _CRS to fall back to */ if (!adev) returnfalse;
/* Never allow fallback if the device has properties */ if (acpi_dev_has_props(adev) || adev->driver_gpios) returnfalse;
/* Try first from _DSD */
for_each_gpio_property_name(propname, con_id) { if (adev)
ret = acpi_get_gpiod_by_index(adev, propname, &lookup); else
ret = acpi_get_gpiod_from_data(fwnode, propname, &lookup); if (ret) continue;
desc = lookup.desc; if (PTR_ERR(desc) == -EPROBE_DEFER) return desc;
if (!IS_ERR(desc)) return desc;
}
/* Then from plain _CRS GPIOs */ if (can_fallback) {
ret = acpi_get_gpiod_by_index(adev, NULL, &lookup); if (ret) return ERR_PTR(ret);
/* ACPI uses hundredths of milliseconds units */
ret = gpio_set_debounce_timeout(desc, info.debounce * 10); if (ret) return ERR_PTR(ret);
return desc;
}
/** * 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 * @con_id: optional name of GpioInt resource * @index: index of GpioInt resource (starting from %0) * @wake_capable: Set to true if the IRQ is wake capable * * If the device has one or more GpioInt resources, this function can be * 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. * * The function takes optional @con_id parameter. If the resource has * a @con_id in a property, then only those will be taken into account. * * The GPIO is considered wake capable if the GpioInt resource specifies * SharedAndWake or ExclusiveAndWake. * * Returns: * Linux IRQ number (> 0) on success, negative errno on failure.
*/ int acpi_dev_gpio_irq_wake_get_by(struct acpi_device *adev, constchar *con_id, int index, bool *wake_capable)
{ struct fwnode_handle *fwnode = acpi_fwnode_handle(adev); int idx, i; unsignedint irq_flags; int ret;
for (i = 0, idx = 0; idx <= index; i++) { struct acpi_gpio_info info = {}; struct gpio_desc *desc;
/* Ignore -EPROBE_DEFER, it only matters if idx matches */
desc = __acpi_find_gpio(fwnode, con_id, i, true, &info); if (IS_ERR(desc) && PTR_ERR(desc) != -EPROBE_DEFER) return PTR_ERR(desc);
if (info.gpioint && idx++ == index) { unsignedlong lflags = GPIO_LOOKUP_FLAGS_DEFAULT; enum gpiod_flags dflags = GPIOD_ASIS; char label[32]; int irq;
if (IS_ERR(desc)) return PTR_ERR(desc);
irq = gpiod_to_irq(desc); if (irq < 0) return irq;
/* * If the IRQ is not already in use then set type * if specified and different than the current one.
*/ if (can_request_irq(irq, irq_flags)) { if (irq_flags != IRQ_TYPE_NONE &&
irq_flags != irq_get_trigger_type(irq))
irq_set_irq_type(irq, irq_flags);
} else {
dev_dbg(&adev->dev, "IRQ %d already in use\n", irq);
}
/* avoid suspend issues with GPIOs when systems are using S3 */ if (wake_capable && acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)
*wake_capable = info.wake_capable;
status = acpi_buffer_to_resource(achip->conn_info.connection,
achip->conn_info.length, &ares); if (ACPI_FAILURE(status)) return status;
if (WARN_ON(ares->type != ACPI_RESOURCE_TYPE_GPIO)) {
ACPI_FREE(ares); return AE_BAD_PARAMETER;
}
agpio = &ares->data.gpio;
if (WARN_ON(agpio->io_restriction == ACPI_IO_RESTRICT_INPUT &&
function == ACPI_WRITE)) {
ACPI_FREE(ares); return AE_BAD_PARAMETER;
}
length = min_t(u16, agpio->pin_table_length, pin_index + bits); for (i = pin_index; i < length; ++i) { unsignedint pin = agpio->pin_table[i]; struct acpi_gpio_connection *conn; struct gpio_desc *desc; bool found;
mutex_lock(&achip->conn_lock);
found = false;
list_for_each_entry(conn, &achip->conns, node) { if (conn->pin == pin) {
found = true;
desc = conn->desc; break;
}
}
/* * The same GPIO can be shared between operation region and * event but only if the access here is ACPI_READ. In that * case we "borrow" the event GPIO instead.
*/ if (!found && agpio->shareable == ACPI_SHARED &&
function == ACPI_READ) { struct acpi_gpio_event *event;
list_for_each_entry(event, &achip->events, node) { if (event->pin == pin) {
desc = event->desc;
found = true; break;
}
}
}
if (!found) {
desc = acpi_request_own_gpiod(chip, agpio, i, "ACPI:OpRegion"); if (IS_ERR(desc)) {
mutex_unlock(&achip->conn_lock);
status = AE_ERROR; goto out;
}
conn = kzalloc(sizeof(*conn), GFP_KERNEL); if (!conn) {
gpiochip_free_own_desc(desc);
mutex_unlock(&achip->conn_lock);
status = AE_NO_MEMORY; goto out;
}
while (element < end) { switch (element->type) { case ACPI_TYPE_LOCAL_REFERENCE:
element += 3;
fallthrough; case ACPI_TYPE_INTEGER:
element++;
count++; break;
if (ares->type == ACPI_RESOURCE_TYPE_GPIO)
*count += ares->data.gpio.pin_table_length;
return 1;
}
/** * acpi_gpio_count - count the GPIOs associated with a firmware node / function * @fwnode: firmware node of the GPIO consumer * @con_id: function within the GPIO consumer * * Returns: * The number of GPIOs associated with a firmware node / function or %-ENOENT, * if no GPIO has been assigned to the requested function.
*/ int acpi_gpio_count(conststruct fwnode_handle *fwnode, constchar *con_id)
{ struct acpi_device *adev = to_acpi_device_node(fwnode); constunion acpi_object *obj; conststruct acpi_gpio_mapping *gm; int count = -ENOENT; int ret; char propname[32];
/* Try first from _DSD */
for_each_gpio_property_name(propname, con_id) {
ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY, &obj); if (ret == 0) { if (obj->type == ACPI_TYPE_LOCAL_REFERENCE)
count = 1; elseif (obj->type == ACPI_TYPE_PACKAGE)
count = acpi_gpio_package_count(obj);
} elseif (adev->driver_gpios) { for (gm = adev->driver_gpios; gm->name; gm++) if (strcmp(propname, gm->name) == 0) {
count = gm->size; break;
}
} if (count > 0) break;
}
/* Then from plain _CRS GPIOs */ if (count < 0) { struct list_head resource_list; unsignedint crs_count = 0;
if (!acpi_can_fallback_to_crs(adev, con_id)) return count;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.