diff options
Diffstat (limited to 'drivers/gpio/gpiolib-acpi.c')
-rw-r--r-- | drivers/gpio/gpiolib-acpi.c | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index c0929d938ced..c4919966453d 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c | |||
@@ -712,3 +712,87 @@ void acpi_gpiochip_remove(struct gpio_chip *chip) | |||
712 | acpi_detach_data(handle, acpi_gpio_chip_dh); | 712 | acpi_detach_data(handle, acpi_gpio_chip_dh); |
713 | kfree(acpi_gpio); | 713 | kfree(acpi_gpio); |
714 | } | 714 | } |
715 | |||
716 | static unsigned int acpi_gpio_package_count(const union acpi_object *obj) | ||
717 | { | ||
718 | const union acpi_object *element = obj->package.elements; | ||
719 | const union acpi_object *end = element + obj->package.count; | ||
720 | unsigned int count = 0; | ||
721 | |||
722 | while (element < end) { | ||
723 | if (element->type == ACPI_TYPE_LOCAL_REFERENCE) | ||
724 | count++; | ||
725 | |||
726 | element++; | ||
727 | } | ||
728 | return count; | ||
729 | } | ||
730 | |||
731 | static int acpi_find_gpio_count(struct acpi_resource *ares, void *data) | ||
732 | { | ||
733 | unsigned int *count = data; | ||
734 | |||
735 | if (ares->type == ACPI_RESOURCE_TYPE_GPIO) | ||
736 | *count += ares->data.gpio.pin_table_length; | ||
737 | |||
738 | return 1; | ||
739 | } | ||
740 | |||
741 | /** | ||
742 | * acpi_gpio_count - return the number of GPIOs associated with a | ||
743 | * device / function or -ENOENT if no GPIO has been | ||
744 | * assigned to the requested function. | ||
745 | * @dev: GPIO consumer, can be NULL for system-global GPIOs | ||
746 | * @con_id: function within the GPIO consumer | ||
747 | */ | ||
748 | int acpi_gpio_count(struct device *dev, const char *con_id) | ||
749 | { | ||
750 | struct acpi_device *adev = ACPI_COMPANION(dev); | ||
751 | const union acpi_object *obj; | ||
752 | const struct acpi_gpio_mapping *gm; | ||
753 | int count = -ENOENT; | ||
754 | int ret; | ||
755 | char propname[32]; | ||
756 | unsigned int i; | ||
757 | |||
758 | /* Try first from _DSD */ | ||
759 | for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { | ||
760 | if (con_id && strcmp(con_id, "gpios")) | ||
761 | snprintf(propname, sizeof(propname), "%s-%s", | ||
762 | con_id, gpio_suffixes[i]); | ||
763 | else | ||
764 | snprintf(propname, sizeof(propname), "%s", | ||
765 | gpio_suffixes[i]); | ||
766 | |||
767 | ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY, | ||
768 | &obj); | ||
769 | if (ret == 0) { | ||
770 | if (obj->type == ACPI_TYPE_LOCAL_REFERENCE) | ||
771 | count = 1; | ||
772 | else if (obj->type == ACPI_TYPE_PACKAGE) | ||
773 | count = acpi_gpio_package_count(obj); | ||
774 | } else if (adev->driver_gpios) { | ||
775 | for (gm = adev->driver_gpios; gm->name; gm++) | ||
776 | if (strcmp(propname, gm->name) == 0) { | ||
777 | count = gm->size; | ||
778 | break; | ||
779 | } | ||
780 | } | ||
781 | if (count >= 0) | ||
782 | break; | ||
783 | } | ||
784 | |||
785 | /* Then from plain _CRS GPIOs */ | ||
786 | if (count < 0) { | ||
787 | struct list_head resource_list; | ||
788 | unsigned int crs_count = 0; | ||
789 | |||
790 | INIT_LIST_HEAD(&resource_list); | ||
791 | acpi_dev_get_resources(adev, &resource_list, | ||
792 | acpi_find_gpio_count, &crs_count); | ||
793 | acpi_dev_free_resource_list(&resource_list); | ||
794 | if (crs_count > 0) | ||
795 | count = crs_count; | ||
796 | } | ||
797 | return count; | ||
798 | } | ||