Add support for following ACPI pin resources - PinGroups - PinGroupFunction - PinGroupConfig PinGroupFunction and PinGroupConfig resources are processed by pinctrl-acpi and maps the group to pins using PinGroups resources before invoking the acpi_node_to_map callback to map the resources to struct pinctrl_map. Signed-off-by: Niyas Sait <niyas.sait@xxxxxxxxxx> --- Documentation/driver-api/pin-control-acpi.rst | 97 ++++++++++++ drivers/pinctrl/pinctrl-acpi.c | 147 ++++++++++++++++++ 2 files changed, 244 insertions(+) diff --git a/Documentation/driver-api/pin-control-acpi.rst b/Documentation/driver-api/pin-control-acpi.rst index 5d179ba90e8f..9a427d76a581 100644 --- a/Documentation/driver-api/pin-control-acpi.rst +++ b/Documentation/driver-api/pin-control-acpi.rst @@ -109,6 +109,99 @@ Example 1 : I2C controller SDA/SCL muxed with display controller GPIO pin } } + +Example 2 : Pin muxing and configuration described with pin groups +================================================================== + +The configuration is similar to example 1 but described using pin group resources + +.. code-block:: text + + // + // Description: GPIO + // + Device (GPI0) + { + Name (_HID, "PNPFFFE") + Name (_UID, 0x0) + Method (_STA) + { + Return(0xf) + } + Method (_CRS, 0x0, NotSerialized) + { + Name (RBUF, ResourceTemplate() + { + Memory32Fixed(ReadWrite, 0x4FE00000, 0x20) + Interrupt(ResourceConsumer, Level, ActiveHigh, Shared) {0x54} + PinGroup("group1", ResourceProducer) {2, 3} + + }) + Return(RBUF) + } + } + + // + // Description: I2C controller 1 + // + Device (I2C1) + { + Name (_HID, "PNPFFFF") + Name (_UID, 0x0) + Method (_STA) + { + Return(0xf) + } + Method (_CRS, 0x0, NotSerialized) + { + Name (RBUF, ResourceTemplate() + { + Memory32Fixed(ReadWrite, 0x4F800000, 0x20) + Interrupt(ResourceConsumer, Level, ActiveHigh, Shared) {0x55} + // Set function I2C1 for SDA/SCL pins + PinGroupFunction(Exclusive, 0x5, "\\_SB.GPI0, 0, "group1", ResourceConsumer, ) + // Configure 10k Pull up for SDA/SCL pins + PinGroupConfig(Exclusive, 0x01, 10000, "\\_SB.GPI0 ", 0, "group1", ResourceConsumer, ) + }) + Return(RBUF) + } + } + + // + // Description: Physical display panel + // + Device (DISP) + { + Name (_HID, "PNPFFFD") + Name (_UID, 0x0) + Method (_STA) + { + Return(0xf) + } + Method (_CRS, 0x0, NotSerialized) + { + Name (RBUF, ResourceTemplate() + { + Memory32Fixed(ReadWrite, 0x4F900000, 0x20) + Interrupt(ResourceConsumer, Level, ActiveHigh, Shared) {0x57} + // Set function GPIO for pin group group1 + PinGroupFunction(Exclusive, 0x1, "\\_SB.GPI0 ", 0, "group1", + ˓ResourceConsumer, ) + // Configure 20k Pull down + PinGroupConfig (Exclusive, 0x02, 20000, "\\_SB.GPI0 ", 0, "group1", + ˓ResourceConsumer, ) + //Enable Schmitt-trigger + PinGroupConfig (Exclusive, 0x0D, 1, "\\_SB.GPI0 ", 0, "group1", + ˓ResourceConsumer, ) + //Set slew rate to custom value 3 + PinGroupConfig (Exclusive, 0x0B, 3, "\\_SB.GPI0 ", 0, "group1", + ˓ResourceConsumer, ) + }) + Return(RBUF) + } + } + } + Notes for pin controller device driver developers ================================================= @@ -191,6 +284,10 @@ acpi_node_to_map to map them to struct pinctrl_map. The above ACPI resources wou generate two struct pinctrl_acpi_resource descriptors, one for each pin, with list of configs to apply for each pin. +ACPI pin resources can be described at group level as described in example 2 above. +There is no change to the internal pinctrl ACPI interface due to this. ACPI pinctrl +subsystem will resolve all of the groups defined in AML to pins using PinGroup resources. + References ========== diff --git a/drivers/pinctrl/pinctrl-acpi.c b/drivers/pinctrl/pinctrl-acpi.c index a510ff38c60d..bb1f05c96a65 100644 --- a/drivers/pinctrl/pinctrl-acpi.c +++ b/drivers/pinctrl/pinctrl-acpi.c @@ -55,6 +55,18 @@ struct pinctrl_acpi_map { size_t num_maps; }; +/** + * struct pin_groups_lookup_info - context to use for pin group look up + * @group: group to use for look up + * @pins: populated pin array for the group + * @npins: number of pins found for the group + */ +struct pin_groups_lookup_info { + const char *group; + unsigned int *pins; + size_t npins; +}; + static void acpi_free_map(struct pinctrl_dev *pctldev, struct pinctrl_map *map, unsigned int num_maps) { @@ -395,6 +407,129 @@ static int process_pin_config(struct list_head *config_maps, return 0; } +static int find_pin_group_cb(struct acpi_resource *ares, void *data) +{ + struct acpi_resource_pin_group *ares_pin_group; + struct pin_groups_lookup_info *info = data; + int i; + + ares_pin_group = &ares->data.pin_group; + + if (ares->type != ACPI_RESOURCE_TYPE_PIN_GROUP) + return 1; + + if (strcmp(ares_pin_group->resource_label.string_ptr, info->group)) + return 1; + + info->npins = ares_pin_group->pin_table_length; + info->pins = kcalloc(info->npins, sizeof(*info->pins), GFP_KERNEL); + if (!info->pins) + return -ENOMEM; + + for (i = 0; i < ares_pin_group->pin_table_length; i++) + info->pins[i] = ares_pin_group->pin_table[i]; + + return 1; +} + +static int get_pins_in_acpi_pin_group(struct acpi_device *adev, + char *group_name, unsigned int **pins, + size_t *npins) +{ + struct pin_groups_lookup_info info = { .group = group_name }; + struct list_head res_list; + int ret; + + INIT_LIST_HEAD(&res_list); + ret = acpi_dev_get_resources(adev, &res_list, find_pin_group_cb, &info); + if (ret < 0) + return ret; + + acpi_dev_free_resource_list(&res_list); + + *pins = info.pins; + *npins = info.npins; + + return 0; +} + +static int process_pin_group_config(struct pinctrl *p, + struct list_head *config_maps, + struct acpi_resource_pin_group_config *ares) +{ + struct acpi_device *adev; + struct pinctrl_dev *pctl_dev; + unsigned int config; + int ret; + size_t npins; + unsigned int *pins; + char *pinctrl_acpi; + char *group; + + ret = acpi_to_generic_pin_config(ares->pin_config_type, + ares->pin_config_value, &config); + if (ret < 0) + return ret; + + pinctrl_acpi = ares->resource_source.string_ptr; + pctl_dev = get_pinctrl_dev_from_acpi_name(pinctrl_acpi); + if (!pctl_dev) { + dev_err(p->dev, "pctldev with ACPI name '%s' not found\n", + pinctrl_acpi); + return -ENXIO; + } + + adev = ACPI_COMPANION(pctl_dev->dev); + group = ares->resource_source_label.string_ptr; + ret = get_pins_in_acpi_pin_group(adev, group, &pins, &npins); + if (ret < 0) + return ret; + + for (int i = 0; i < npins; i++) { + ret = add_to_config_map(config_maps, pinctrl_acpi, pins[i], + config); + if (ret < 0) + break; + } + + kfree(pins); + + return ret; +} + +static int +process_pin_group_function(struct pinctrl *p, + struct acpi_resource_pin_group_function *ares) +{ + struct pinctrl_dev *pctl_dev; + struct acpi_device *adev; + unsigned int *pins; + char *pinctrl_acpi; + char *group; + size_t npins; + int ret; + + pinctrl_acpi = ares->resource_source.string_ptr; + pctl_dev = get_pinctrl_dev_from_acpi_name(pinctrl_acpi); + if (!pctl_dev) { + dev_err(p->dev, "pctldev with ACPI name '%s' not found\n", + pinctrl_acpi); + return -ENXIO; + } + + adev = ACPI_COMPANION(pctl_dev->dev); + group = ares->resource_source_label.string_ptr; + ret = get_pins_in_acpi_pin_group(adev, group, &pins, &npins); + if (ret < 0) + return ret; + + ret = acpi_pin_function_to_pinctrl_map(p, pinctrl_acpi, pins, npins, + ares->function_number); + + kfree(pins); + + return ret; +} static int parse_acpi_pin_function_resources(struct acpi_resource *ares, void *data) @@ -408,6 +543,12 @@ static int parse_acpi_pin_function_resources(struct acpi_resource *ares, if (ret < 0) return ret; break; + case ACPI_RESOURCE_TYPE_PIN_GROUP_FUNCTION: + ret = process_pin_group_function( + p, &ares->data.pin_group_function); + if (ret < 0) + return ret; + break; } return 1; @@ -426,6 +567,12 @@ static int parse_acpi_pin_config_resources(struct acpi_resource *ares, if (ret < 0) return ret; break; + case ACPI_RESOURCE_TYPE_PIN_GROUP_CONFIG: + ret = process_pin_group_config(info->pctrl, &info->config_maps, + &ares->data.pin_group_config); + if (ret < 0) + return ret; + break; } return 1; -- 2.25.1