Parse GpioInt/GpioIo ACPI resources and use the pin configuration information to generate pin controller maps. These maps are associated with the "default" state, only if this state is defined in the _DSD of the acpi device. Signed-off-by: Irina Tirdea <irina.tirdea@xxxxxxxxx> --- Documentation/acpi/pinctrl-properties.txt | 8 ++ drivers/pinctrl/acpi.c | 229 ++++++++++++++++++++++++++++++ 2 files changed, 237 insertions(+) diff --git a/Documentation/acpi/pinctrl-properties.txt b/Documentation/acpi/pinctrl-properties.txt index e93aaaf..8243190 100644 --- a/Documentation/acpi/pinctrl-properties.txt +++ b/Documentation/acpi/pinctrl-properties.txt @@ -265,6 +265,14 @@ same way as it is done for device tree [5]. The format for the data subnode is: configname - name of the pin configuration property configval - value of the pin configuration property +== GpioInt()/GpioIo() _CRS resources == + +If the device has any GpioInt/GpioIo _CRS ACPI resources, they are parsed so +that the pin configuration information is used (e.g. PullUp/PullDown/PullNone). +The pin configuration from GpioInt/GpioIo will be associated with the "default" +state, only if such a state is defined in the _DSD of the device. If no +"default" state is defined, the GPIO configuration from _CRS will be ignored. + == References == [1] Documentation/pinctrl.txt diff --git a/drivers/pinctrl/acpi.c b/drivers/pinctrl/acpi.c index bed1d88..7199c3d 100644 --- a/drivers/pinctrl/acpi.c +++ b/drivers/pinctrl/acpi.c @@ -140,6 +140,224 @@ static int acpi_remember_or_free_map(struct pinctrl *p, const char *statename, return pinctrl_register_map(map, num_maps, false); } +#ifdef CONFIG_GENERIC_PINCONF +struct acpi_gpio_lookup { + unsigned int index; + bool found; + unsigned int n; + struct pinctrl_map *map; + unsigned num_maps; + unsigned reserved_maps; + struct pinctrl_dev **pctldevs; +}; + +/* For now we only handle acpi pin config values */ +#define ACPI_MAX_CFGS 1 + +static int acpi_parse_gpio_config(const struct acpi_resource_gpio *agpio, + unsigned long **configs, + unsigned int *nconfigs) +{ + enum pin_config_param param; + int ret; + + /* Parse configs from GpioInt/GpioIo ACPI resource */ + *nconfigs = 0; + *configs = kcalloc(ACPI_MAX_CFGS, sizeof(*configs), GFP_KERNEL); + if (!*configs) + return -ENOMEM; + + /* For now, only parse pin_config */ + switch (agpio->pin_config) { + case ACPI_PIN_CONFIG_DEFAULT: + param = PIN_CONFIG_BIAS_PULL_PIN_DEFAULT; + break; + case ACPI_PIN_CONFIG_PULLUP: + param = PIN_CONFIG_BIAS_PULL_UP; + break; + case ACPI_PIN_CONFIG_PULLDOWN: + param = PIN_CONFIG_BIAS_PULL_DOWN; + break; + case ACPI_PIN_CONFIG_NOPULL: + param = PIN_CONFIG_BIAS_DISABLE; + break; + default: + ret = -EINVAL; + goto exit_free; + } + *configs[*nconfigs] = pinconf_to_config_packed(param, + param == PIN_CONFIG_BIAS_DISABLE ? 0 : 1); + (*nconfigs)++; + + return 0; + +exit_free: + kfree(*configs); + return ret; +} + +static int acpi_gpio_to_map(struct acpi_resource *ares, void *data) +{ + struct pinctrl_dev *pctldev, **new_pctldevs; + struct acpi_gpio_lookup *lookup = data; + const struct acpi_resource_gpio *agpio; + acpi_handle pctrl_handle = NULL; + unsigned int nconfigs, i; + unsigned long *configs; + acpi_status status; + const char *pin; + int ret; + + if (ares->type != ACPI_RESOURCE_TYPE_GPIO) + return 1; + if (lookup->n++ != lookup->index || lookup->found) + return 1; + + agpio = &ares->data.gpio; + + /* Get configs from ACPI GPIO resource */ + ret = acpi_parse_gpio_config(agpio, &configs, &nconfigs); + if (ret) + return ret; + + /* Get pinctrl reference from GPIO resource */ + status = acpi_get_handle(NULL, agpio->resource_source.string_ptr, + &pctrl_handle); + if (ACPI_FAILURE(status) || !pctrl_handle) { + ret = -EINVAL; + goto exit_free_configs; + } + + /* Find the pin controller */ + pctldev = get_pinctrl_dev_from_acpi(pctrl_handle); + if (!pctldev) { + ret = -EINVAL; + goto exit_free_configs; + } + + /* Allocate space for maps and pinctrl_dev references */ + ret = pinctrl_utils_reserve_map(pctldev, &lookup->map, + &lookup->reserved_maps, + &lookup->num_maps, + agpio->pin_table_length); + if (ret < 0) + goto exit_free_configs; + + new_pctldevs = krealloc(lookup->pctldevs, + sizeof(*new_pctldevs) * lookup->reserved_maps, + GFP_KERNEL); + if (!new_pctldevs) { + ret = -ENOMEM; + goto exit_free_configs; + } + lookup->pctldevs = new_pctldevs; + + /* For each GPIO pin */ + for (i = 0; i < agpio->pin_table_length; i++) { + pin = pin_get_name(pctldev, agpio->pin_table[i]); + if (!pin) { + ret = -EINVAL; + goto exit_free_configs; + } + lookup->pctldevs[lookup->num_maps] = pctldev; + ret = pinctrl_utils_add_map_configs(pctldev, &lookup->map, + &lookup->reserved_maps, + &lookup->num_maps, pin, + configs, nconfigs, + PIN_MAP_TYPE_CONFIGS_PIN); + if (ret < 0) + goto exit_free_configs; + } + + lookup->found = true; + kfree(configs); + return 1; + +exit_free_configs: + kfree(configs); + return ret; +} + +static int acpi_parse_gpio_res(struct pinctrl *p, + struct pinctrl_map **map, + unsigned *num_maps, + struct pinctrl_dev ***pctldevs) +{ + struct acpi_gpio_lookup lookup; + struct list_head res_list; + struct acpi_device *adev; + unsigned int index; + int ret; + + adev = ACPI_COMPANION(p->dev); + + *map = NULL; + *num_maps = 0; + memset(&lookup, 0, sizeof(lookup)); + + /* Parse all GpioInt/GpioIo resources in _CRS and extract pin conf */ + for (index = 0; ; index++) { + lookup.index = index; + lookup.n = 0; + lookup.found = false; + + INIT_LIST_HEAD(&res_list); + ret = acpi_dev_get_resources(adev, &res_list, acpi_gpio_to_map, + &lookup); + if (ret < 0) + goto exit_free; + acpi_dev_free_resource_list(&res_list); + if (!lookup.found) + break; + } + + *map = lookup.map; + *num_maps = lookup.num_maps; + *pctldevs = lookup.pctldevs; + + return 0; + +exit_free: + pinctrl_utils_free_map(NULL, lookup.map, lookup.num_maps); + kfree(lookup.pctldevs); + return ret; +} + +static int acpi_parse_gpio_resources(struct pinctrl *p, char *statename) +{ + struct pinctrl_dev **pctldevs; + struct pinctrl_map *map; + unsigned num_maps; + unsigned int i; + int ret; + + ret = acpi_parse_gpio_res(p, &map, &num_maps, &pctldevs); + if (ret) + return ret; + + /* Add maps one by one since pinctrl devices might be different */ + for (i = 0; i < num_maps; i++) { + ret = acpi_remember_or_free_map(p, statename, pctldevs[i], + &map[i], 1); + if (ret < 0) + goto exit_free; + } + + kfree(pctldevs); + return 0; + +exit_free: + pinctrl_utils_free_map(NULL, map, num_maps); + kfree(pctldevs); + return ret; +} +#else +static inline int acpi_parse_gpio_resources(struct pinctrl *p, char *statename) +{ + return 0; +} +#endif + static int acpi_remember_dummy_state(struct pinctrl *p, const char *statename) { struct pinctrl_map *map; @@ -273,6 +491,17 @@ int pinctrl_acpi_to_map(struct pinctrl *p) } statename = statenames[state].string.pointer; + /* + * Parse any GpioInt/GpioIo resources and + * associate them with the 'default' state. + */ + if (!strcmp(statename, PINCTRL_STATE_DEFAULT)) { + ret = acpi_parse_gpio_resources(p, statename); + if (ret) + dev_err(p->dev, + "Could not parse GPIO resources\n"); + } + /* Retrieve the pinctrl-* property */ propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state); ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_PACKAGE, -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html