@@ -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
==========
@@ -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;
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@linaro.org> --- Documentation/driver-api/pin-control-acpi.rst | 97 ++++++++++++ drivers/pinctrl/pinctrl-acpi.c | 147 ++++++++++++++++++ 2 files changed, 244 insertions(+)