diff options
Diffstat (limited to 'drivers/gpio/gpiolib-acpi.c')
-rw-r--r-- | drivers/gpio/gpiolib-acpi.c | 117 |
1 files changed, 103 insertions, 14 deletions
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 05c6275da224..ba98bb59a58f 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c | |||
@@ -287,9 +287,45 @@ void acpi_gpiochip_free_interrupts(struct gpio_chip *chip) | |||
287 | } | 287 | } |
288 | } | 288 | } |
289 | 289 | ||
290 | int acpi_dev_add_driver_gpios(struct acpi_device *adev, | ||
291 | const struct acpi_gpio_mapping *gpios) | ||
292 | { | ||
293 | if (adev && gpios) { | ||
294 | adev->driver_gpios = gpios; | ||
295 | return 0; | ||
296 | } | ||
297 | return -EINVAL; | ||
298 | } | ||
299 | EXPORT_SYMBOL_GPL(acpi_dev_add_driver_gpios); | ||
300 | |||
301 | static bool acpi_get_driver_gpio_data(struct acpi_device *adev, | ||
302 | const char *name, int index, | ||
303 | struct acpi_reference_args *args) | ||
304 | { | ||
305 | const struct acpi_gpio_mapping *gm; | ||
306 | |||
307 | if (!adev->driver_gpios) | ||
308 | return false; | ||
309 | |||
310 | for (gm = adev->driver_gpios; gm->name; gm++) | ||
311 | if (!strcmp(name, gm->name) && gm->data && index < gm->size) { | ||
312 | const struct acpi_gpio_params *par = gm->data + index; | ||
313 | |||
314 | args->adev = adev; | ||
315 | args->args[0] = par->crs_entry_index; | ||
316 | args->args[1] = par->line_index; | ||
317 | args->args[2] = par->active_low; | ||
318 | args->nargs = 3; | ||
319 | return true; | ||
320 | } | ||
321 | |||
322 | return false; | ||
323 | } | ||
324 | |||
290 | struct acpi_gpio_lookup { | 325 | struct acpi_gpio_lookup { |
291 | struct acpi_gpio_info info; | 326 | struct acpi_gpio_info info; |
292 | int index; | 327 | int index; |
328 | int pin_index; | ||
293 | struct gpio_desc *desc; | 329 | struct gpio_desc *desc; |
294 | int n; | 330 | int n; |
295 | }; | 331 | }; |
@@ -303,13 +339,24 @@ static int acpi_find_gpio(struct acpi_resource *ares, void *data) | |||
303 | 339 | ||
304 | if (lookup->n++ == lookup->index && !lookup->desc) { | 340 | if (lookup->n++ == lookup->index && !lookup->desc) { |
305 | const struct acpi_resource_gpio *agpio = &ares->data.gpio; | 341 | const struct acpi_resource_gpio *agpio = &ares->data.gpio; |
342 | int pin_index = lookup->pin_index; | ||
343 | |||
344 | if (pin_index >= agpio->pin_table_length) | ||
345 | return 1; | ||
306 | 346 | ||
307 | lookup->desc = acpi_get_gpiod(agpio->resource_source.string_ptr, | 347 | lookup->desc = acpi_get_gpiod(agpio->resource_source.string_ptr, |
308 | agpio->pin_table[0]); | 348 | agpio->pin_table[pin_index]); |
309 | lookup->info.gpioint = | 349 | lookup->info.gpioint = |
310 | agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT; | 350 | agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT; |
311 | lookup->info.active_low = | 351 | |
312 | agpio->polarity == ACPI_ACTIVE_LOW; | 352 | /* |
353 | * ActiveLow is only specified for GpioInt resource. If | ||
354 | * GpioIo is used then the only way to set the flag is | ||
355 | * to use _DSD "gpios" property. | ||
356 | */ | ||
357 | if (lookup->info.gpioint) | ||
358 | lookup->info.active_low = | ||
359 | agpio->polarity == ACPI_ACTIVE_LOW; | ||
313 | } | 360 | } |
314 | 361 | ||
315 | return 1; | 362 | return 1; |
@@ -317,40 +364,79 @@ static int acpi_find_gpio(struct acpi_resource *ares, void *data) | |||
317 | 364 | ||
318 | /** | 365 | /** |
319 | * acpi_get_gpiod_by_index() - get a GPIO descriptor from device resources | 366 | * acpi_get_gpiod_by_index() - get a GPIO descriptor from device resources |
320 | * @dev: pointer to a device to get GPIO from | 367 | * @adev: pointer to a ACPI device to get GPIO from |
368 | * @propname: Property name of the GPIO (optional) | ||
321 | * @index: index of GpioIo/GpioInt resource (starting from %0) | 369 | * @index: index of GpioIo/GpioInt resource (starting from %0) |
322 | * @info: info pointer to fill in (optional) | 370 | * @info: info pointer to fill in (optional) |
323 | * | 371 | * |
324 | * Function goes through ACPI resources for @dev and based on @index looks | 372 | * Function goes through ACPI resources for @adev and based on @index looks |
325 | * up a GpioIo/GpioInt resource, translates it to the Linux GPIO descriptor, | 373 | * up a GpioIo/GpioInt resource, translates it to the Linux GPIO descriptor, |
326 | * and returns it. @index matches GpioIo/GpioInt resources only so if there | 374 | * and returns it. @index matches GpioIo/GpioInt resources only so if there |
327 | * are total %3 GPIO resources, the index goes from %0 to %2. | 375 | * are total %3 GPIO resources, the index goes from %0 to %2. |
328 | * | 376 | * |
377 | * If @propname is specified the GPIO is looked using device property. In | ||
378 | * that case @index is used to select the GPIO entry in the property value | ||
379 | * (in case of multiple). | ||
380 | * | ||
329 | * If the GPIO cannot be translated or there is an error an ERR_PTR is | 381 | * If the GPIO cannot be translated or there is an error an ERR_PTR is |
330 | * returned. | 382 | * returned. |
331 | * | 383 | * |
332 | * Note: if the GPIO resource has multiple entries in the pin list, this | 384 | * Note: if the GPIO resource has multiple entries in the pin list, this |
333 | * function only returns the first. | 385 | * function only returns the first. |
334 | */ | 386 | */ |
335 | struct gpio_desc *acpi_get_gpiod_by_index(struct device *dev, int index, | 387 | struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, |
388 | const char *propname, int index, | ||
336 | struct acpi_gpio_info *info) | 389 | struct acpi_gpio_info *info) |
337 | { | 390 | { |
338 | struct acpi_gpio_lookup lookup; | 391 | struct acpi_gpio_lookup lookup; |
339 | struct list_head resource_list; | 392 | struct list_head resource_list; |
340 | struct acpi_device *adev; | 393 | bool active_low = false; |
341 | acpi_handle handle; | ||
342 | int ret; | 394 | int ret; |
343 | 395 | ||
344 | if (!dev) | 396 | if (!adev) |
345 | return ERR_PTR(-EINVAL); | ||
346 | |||
347 | handle = ACPI_HANDLE(dev); | ||
348 | if (!handle || acpi_bus_get_device(handle, &adev)) | ||
349 | return ERR_PTR(-ENODEV); | 397 | return ERR_PTR(-ENODEV); |
350 | 398 | ||
351 | memset(&lookup, 0, sizeof(lookup)); | 399 | memset(&lookup, 0, sizeof(lookup)); |
352 | lookup.index = index; | 400 | lookup.index = index; |
353 | 401 | ||
402 | if (propname) { | ||
403 | struct acpi_reference_args args; | ||
404 | |||
405 | dev_dbg(&adev->dev, "GPIO: looking up %s\n", propname); | ||
406 | |||
407 | memset(&args, 0, sizeof(args)); | ||
408 | ret = acpi_dev_get_property_reference(adev, propname, | ||
409 | index, &args); | ||
410 | if (ret) { | ||
411 | bool found = acpi_get_driver_gpio_data(adev, propname, | ||
412 | index, &args); | ||
413 | if (!found) | ||
414 | return ERR_PTR(ret); | ||
415 | } | ||
416 | |||
417 | /* | ||
418 | * The property was found and resolved so need to | ||
419 | * lookup the GPIO based on returned args instead. | ||
420 | */ | ||
421 | adev = args.adev; | ||
422 | if (args.nargs >= 2) { | ||
423 | lookup.index = args.args[0]; | ||
424 | lookup.pin_index = args.args[1]; | ||
425 | /* | ||
426 | * 3rd argument, if present is used to | ||
427 | * specify active_low. | ||
428 | */ | ||
429 | if (args.nargs >= 3) | ||
430 | active_low = !!args.args[2]; | ||
431 | } | ||
432 | |||
433 | dev_dbg(&adev->dev, "GPIO: _DSD returned %s %zd %llu %llu %llu\n", | ||
434 | dev_name(&adev->dev), args.nargs, | ||
435 | args.args[0], args.args[1], args.args[2]); | ||
436 | } else { | ||
437 | dev_dbg(&adev->dev, "GPIO: looking up %d in _CRS\n", index); | ||
438 | } | ||
439 | |||
354 | INIT_LIST_HEAD(&resource_list); | 440 | INIT_LIST_HEAD(&resource_list); |
355 | ret = acpi_dev_get_resources(adev, &resource_list, acpi_find_gpio, | 441 | ret = acpi_dev_get_resources(adev, &resource_list, acpi_find_gpio, |
356 | &lookup); | 442 | &lookup); |
@@ -359,8 +445,11 @@ struct gpio_desc *acpi_get_gpiod_by_index(struct device *dev, int index, | |||
359 | 445 | ||
360 | acpi_dev_free_resource_list(&resource_list); | 446 | acpi_dev_free_resource_list(&resource_list); |
361 | 447 | ||
362 | if (lookup.desc && info) | 448 | if (lookup.desc && info) { |
363 | *info = lookup.info; | 449 | *info = lookup.info; |
450 | if (active_low) | ||
451 | info->active_low = active_low; | ||
452 | } | ||
364 | 453 | ||
365 | return lookup.desc ? lookup.desc : ERR_PTR(-ENOENT); | 454 | return lookup.desc ? lookup.desc : ERR_PTR(-ENOENT); |
366 | } | 455 | } |