On Thu, Nov 10, 2022 at 07:12:58PM +0000, Niyas Sait wrote: > Add support for following acpi pin resources > > - PinFunction > - PinConfig > - PinGroupFunction > - PinGroupConfig > > Pinctrl-acpi parses the acpi table and generates list of pin ACPI > descriptors that can be used by pin controller to set and config pin. > > Descriptors are grouped by pin number or group name and contains list > of functions or configs to apply. > > Pin config types from acpi are converted to generic pin config types ACPI (ditto everywhere). > and passed through the descriptor. > > Signed-off-by: Niyas Sait <niyas.sait@xxxxxxxxxx> > --- > drivers/pinctrl/core.c | 19 +- > drivers/pinctrl/core.h | 3 + > drivers/pinctrl/pinctrl-acpi.c | 391 ++++++++++++++++++++++++++++++++ > drivers/pinctrl/pinctrl-acpi.h | 28 +++ > include/linux/pinctrl/pinctrl.h | 15 ++ > 5 files changed, 452 insertions(+), 4 deletions(-) > > diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c > index ffe39336fcac..03770ac66d48 100644 > --- a/drivers/pinctrl/core.c > +++ b/drivers/pinctrl/core.c > @@ -25,6 +25,7 @@ > #include <linux/pinctrl/consumer.h> > #include <linux/pinctrl/pinctrl.h> > #include <linux/pinctrl/machine.h> > +#include <linux/acpi.h> > > #ifdef CONFIG_GPIOLIB > #include "../gpio/gpiolib.h" > @@ -35,7 +36,7 @@ > #include "devicetree.h" > #include "pinmux.h" > #include "pinconf.h" > - > +#include "pinctrl-acpi.h" > > static bool pinctrl_dummy_state; > > @@ -1042,9 +1043,15 @@ static struct pinctrl *create_pinctrl(struct device *dev, > return ERR_PTR(-ENOMEM); > p->dev = dev; > INIT_LIST_HEAD(&p->states); > - INIT_LIST_HEAD(&p->dt_maps); > > - ret = pinctrl_dt_to_map(p, pctldev); > + if (!ACPI_COMPANION(dev)) { > + INIT_LIST_HEAD(&p->dt_maps); > + ret = pinctrl_dt_to_map(p, pctldev); > + } else { > + INIT_LIST_HEAD(&p->acpi_maps); > + ret = pinctrl_acpi_to_map(p); > + } > + > if (ret < 0) { > kfree(p); > return ERR_PTR(ret); > @@ -1168,7 +1175,11 @@ static void pinctrl_free(struct pinctrl *p, bool inlist) > kfree(state); > } > > - pinctrl_dt_free_maps(p); > + if (!ACPI_COMPANION(p->dev)) { > + pinctrl_dt_free_maps(p); > + } else { > + pinctrl_acpi_free_maps(p); > + } You don't need the {} > > if (inlist) > list_del(&p->node); > diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h > index 840103c40c14..28f2f9d518d4 100644 > --- a/drivers/pinctrl/core.h > +++ b/drivers/pinctrl/core.h > @@ -72,6 +72,8 @@ struct pinctrl_dev { > * @state: the current state > * @dt_maps: the mapping table chunks dynamically parsed from device tree for > * this device, if any > + * @acpi_maps: the mapping table chunks dynamically parsed from acpi for this > + * device, if any > * @users: reference count > */ > struct pinctrl { > @@ -80,6 +82,7 @@ struct pinctrl { > struct list_head states; > struct pinctrl_state *state; > struct list_head dt_maps; > + struct list_head acpi_maps; > struct kref users; > }; > > diff --git a/drivers/pinctrl/pinctrl-acpi.c b/drivers/pinctrl/pinctrl-acpi.c > index 75e59fe22387..9777577aefd6 100644 > --- a/drivers/pinctrl/pinctrl-acpi.c > +++ b/drivers/pinctrl/pinctrl-acpi.c > @@ -10,6 +10,397 @@ > #include <linux/list.h> > > #include "pinctrl-acpi.h" > +#include "core.h" > + > +/** > + * struct pinctrl_acpi_map - mapping table chunk parsed from device tree Parsed from ACPI namespace? > + * @node: list node for struct pinctrl's @acpi_maps field > + * @pctldev: the pin controller that allocated this struct, and will free it > + * @map: the mapping table entries > + * @num_maps: number of mapping table entries > + */ > +struct pinctrl_acpi_map { > + struct list_head node; > + struct pinctrl_dev *pctldev; > + struct pinctrl_map *map; > + unsigned int num_maps; size_t? > +}; > + > +static void acpi_free_map(struct pinctrl_dev *pctldev, > + struct pinctrl_map *map, unsigned int num_maps) > +{ > + int i; > + > + for (i = 0; i < num_maps; ++i) { > + kfree_const(map[i].dev_name); > + map[i].dev_name = NULL; > + } > + > + if (pctldev) { > + const struct pinctrl_ops *ops = pctldev->desc->pctlops; > + > + if (ops->acpi_free_map) > + ops->acpi_free_map(pctldev, map, num_maps); > + > + } else { > + /* There is no pctldev for PIN_MAP_TYPE_DUMMY_STATE */ > + kfree(map); > + } > +} > + kernel-doc? > +void pinctrl_acpi_free_maps(struct pinctrl *p) > +{ > + struct pinctrl_acpi_map *acpi_map, *n1; Why not 'tmp' instead of 'n1'? > + > + list_for_each_entry_safe(acpi_map, n1, &p->acpi_maps, node) { > + pinctrl_unregister_mappings(acpi_map->map); > + list_del(&acpi_map->node); > + acpi_free_map(acpi_map->pctldev, acpi_map->map, > + acpi_map->num_maps); > + kfree(acpi_map); > + } > +} > + > +static int acpi_remember_or_free_map(struct pinctrl *p, const char *statename, > + struct pinctrl_dev *pctldev, > + struct pinctrl_map *map, unsigned num_maps) > +{ > + int i; > + struct pinctrl_acpi_map *acpi_map; > + > + /* Initialize common mapping table entry fields */ > + for (i = 0; i < num_maps; i++) { > + const char *devname; > + > + devname = kstrdup_const(dev_name(p->dev), GFP_KERNEL); > + if (!devname) > + goto err_free_map; > + > + map[i].dev_name = devname; > + map[i].name = statename; > + if (pctldev) > + map[i].ctrl_dev_name = dev_name(pctldev->dev); > + } > + > + /* Remember the converted mapping table entries */ > + acpi_map = kzalloc(sizeof(*acpi_map), GFP_KERNEL); > + if (!acpi_map) > + goto err_free_map; > + > + acpi_map->pctldev = pctldev; > + acpi_map->map = map; > + acpi_map->num_maps = num_maps; > + list_add_tail(&acpi_map->node, &p->acpi_maps); > + > + return pinctrl_register_mappings(map, num_maps); > + > +err_free_map: > + acpi_free_map(pctldev, map, num_maps); > + return -ENOMEM; > +} > + > +/* Convert raw acpi device references to device name */ raw ACPI device or raw acpi_device. > +static const char *acpi_node_to_device_name(char *acpi_node) Can this be const? > +{ > + acpi_status status; > + acpi_handle handle; > + struct acpi_device *controller_device; > + > + status = acpi_get_handle(NULL, acpi_node, &handle); > + Drop the empty line. > + if (ACPI_FAILURE(status)) > + return NULL; > + > + controller_device = acpi_bus_get_acpi_device(handle); Ditto. > + > + if (!controller_device) > + return NULL; > + > + return acpi_dev_name(controller_device); > +} > + > +/* Map acpi pin configuration types to pinctrl general configuration type */ > +static unsigned map_acpi_conf_to_general_conf(unsigned param, unsigned value) > +{ > + switch (param) { > + case ACPI_PIN_CONFIG_DEFAULT: > + return pinconf_to_config_packed(ACPI_PIN_CONFIG_DEFAULT, 0); > + case ACPI_PIN_CONFIG_BIAS_PULL_UP: > + return pinconf_to_config_packed(PIN_CONFIG_BIAS_PULL_UP_OHMS, value); > + case ACPI_PIN_CONFIG_BIAS_PULL_DOWN: > + return pinconf_to_config_packed(PIN_CONFIG_BIAS_PULL_DOWN_OHMS, value); > + case ACPI_PIN_CONFIG_BIAS_DEFAULT: > + return pinconf_to_config_packed(PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 0); > + case ACPI_PIN_CONFIG_BIAS_DISABLE: > + return pinconf_to_config_packed(PIN_CONFIG_BIAS_DISABLE, 0); > + case ACPI_PIN_CONFIG_BIAS_HIGH_IMPEDANCE: > + return pinconf_to_config_packed(PIN_CONFIG_BIAS_HIGH_IMPEDANCE, 0); > + case ACPI_PIN_CONFIG_BIAS_BUS_HOLD: > + return pinconf_to_config_packed(PIN_CONFIG_BIAS_BUS_HOLD, 0); > + case ACPI_PIN_CONFIG_DRIVE_OPEN_DRAIN: > + return pinconf_to_config_packed(PIN_CONFIG_DRIVE_OPEN_DRAIN, 0); > + case ACPI_PIN_CONFIG_DRIVE_OPEN_SOURCE: > + return pinconf_to_config_packed(PIN_CONFIG_DRIVE_OPEN_SOURCE, 0); > + case ACPI_PIN_CONFIG_DRIVE_PUSH_PULL: > + return pinconf_to_config_packed(PIN_CONFIG_DRIVE_PUSH_PULL, 0); > + case ACPI_PIN_CONFIG_DRIVE_STRENGTH: > + return pinconf_to_config_packed(PIN_CONFIG_DRIVE_STRENGTH, value); > + case ACPI_PIN_CONFIG_SLEW_RATE: > + return pinconf_to_config_packed(PIN_CONFIG_SLEW_RATE, value); > + case ACPI_PIN_CONFIG_INPUT_DEBOUNCE: > + return pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE, value); > + case ACPI_PIN_CONFIG_INPUT_SCHMITT_TRIGGER: > + return pinconf_to_config_packed(PIN_CONFIG_INPUT_SCHMITT_ENABLE, value); > + default: > + pr_warn("PINCTRL: ACPI pin configuration type (%d) not handled\n", param); Don't you have any dev * pointer that you can use here instead of pr_warn()? > + return pinconf_to_config_packed(ACPI_PIN_CONFIG_DEFAULT, 0); > + } > +} > + > +struct pinctrl_acpi_controller_map { > + char *pinctrl_dev; const char *? > + struct list_head list; > + struct list_head pin_group_maps; > +}; > + > +/* Add pin/group function and configuration descriptor to internal map */ > +static int add_pin_group_node(struct list_head *acpi_map_head, > + char *pinctrl_dev, > + char *group, > + unsigned pin, > + bool is_config, > + unsigned config_func, > + void *vendor_data) > +{ > + struct pinctrl_acpi_controller_map *acpi_controller_map = NULL; > + struct pinctrl_acpi_controller_map *acpi_controller_map_iter = NULL; > + struct pinctrl_acpi_pin_group_map *pin_group_map = NULL; > + struct pinctrl_acpi_pin_group_map *pin_group_map_iter = NULL; > + struct pinctrl_acpi_pin_group_info *info = NULL; Do you need to initialize them all? > + bool group_pin_match; > + > + /* Find the pin controller specific list to use to add the descriptor */ > + list_for_each_entry(acpi_controller_map_iter, acpi_map_head, list) { > + if (!strcmp(acpi_controller_map_iter->pinctrl_dev, pinctrl_dev)) { > + acpi_controller_map = acpi_controller_map_iter; > + break; > + } > + } > + > + /* If this is the first entry for the pin controller, allocate an entry */ > + if (!acpi_controller_map) { > + acpi_controller_map = kzalloc(sizeof(struct pinctrl_acpi_controller_map), GFP_KERNEL); > + Drop the empty line. > + if (!acpi_controller_map) > + return -ENOMEM; > + > + acpi_controller_map->pinctrl_dev = pinctrl_dev; > + INIT_LIST_HEAD(&acpi_controller_map->list); > + INIT_LIST_HEAD(&acpi_controller_map->pin_group_maps); > + list_add(&acpi_controller_map->list, acpi_map_head); > + } > + > + /* Find the group/pin specific node from the descriptor list */ > + list_for_each_entry(pin_group_map_iter, &acpi_controller_map->pin_group_maps, list) { > + if (group) > + group_pin_match = !strcmp(pin_group_map_iter->group, group); > + else > + group_pin_match = (pin == pin_group_map_iter->pin); Empty line > + if (pin_group_map_iter->is_config == is_config && group_pin_match) { > + pin_group_map = pin_group_map_iter; > + break; > + } > + } > + > + if (!pin_group_map) { > + pin_group_map = kzalloc(sizeof(struct pinctrl_acpi_pin_group_map), GFP_KERNEL); > + Drop the empty line ;-) > + if (!pin_group_map) > + return -ENOMEM; > + > + pin_group_map->group = group; > + pin_group_map->pin = pin; > + pin_group_map->is_config = is_config; > + INIT_LIST_HEAD(&pin_group_map->list); > + INIT_LIST_HEAD(&pin_group_map->info); > + list_add(&pin_group_map->list, &acpi_controller_map->pin_group_maps); > + } > + > + /* Allocate descriptor and add the pin configuration/function info */ > + info = kzalloc(sizeof(struct pinctrl_acpi_pin_group_info), GFP_KERNEL); Drop the empty line. > + > + if (!info) > + return -ENOMEM; > + > + info->config_func = config_func; > + info->vendor_data = vendor_data; > + INIT_LIST_HEAD(&info->list); > + list_add(&info->list, &pin_group_map->info); > + > + return 0; > +} > + > +static int pinctrl_acpi_populate_pin_group_map(struct acpi_resource *ares, void *data) > +{ > + struct acpi_resource_pin_function *ares_pin_function; > + struct acpi_resource_pin_config *ares_pin_config; > + struct acpi_resource_pin_group_function *ares_pin_group_function; > + struct acpi_resource_pin_group_config *ares_pin_group_config; > + struct list_head *acpi_map_head = data; > + int i; > + int ret; > + unsigned int config; > + char *pinctrl_dev; > + char *group; > + unsigned int pin; > + void *vendor_data; > + unsigned int func; That's huge amount of variables. I wonder if this can be reduced with helper functions etc? > + > + switch (ares->type) { > + case ACPI_RESOURCE_TYPE_PIN_FUNCTION: > + ares_pin_function = &ares->data.pin_function; > + vendor_data = ares_pin_function->vendor_data; > + pinctrl_dev = ares_pin_function->resource_source.string_ptr; > + group = NULL; > + func = ares_pin_function->function_number; > + config = map_acpi_conf_to_general_conf(ares_pin_function->pin_config, 0); > + > + for (i = 0; i < ares_pin_function->pin_table_length; i++) { > + Drop the empty line > + ret = add_pin_group_node(acpi_map_head, pinctrl_dev, group, > + ares_pin_function->pin_table[i], false, func, vendor_data); > + > + if (ret < 0) > + return ret; > + > + ret = add_pin_group_node(acpi_map_head, pinctrl_dev, group, > + ares_pin_function->pin_table[i], true, config, vendor_data); > + > + if (ret < 0) > + return ret; > + } > + break; Add empty line (Ditto everywhere below) > + case ACPI_RESOURCE_TYPE_PIN_CONFIG: > + ares_pin_config = &ares->data.pin_config; > + pinctrl_dev = ares_pin_config->resource_source.string_ptr; > + group = NULL; > + func = 0; > + > + config = map_acpi_conf_to_general_conf( > + ares_pin_config->pin_config_type, > + ares_pin_config->pin_config_value); > + > + vendor_data = ares_pin_config->vendor_data; > + > + for (i = 0; i < ares_pin_function->pin_table_length; i++) { > + pin = ares_pin_config->pin_table[i]; > + > + ret = add_pin_group_node(acpi_map_head, pinctrl_dev, > + group, pin, true, config, vendor_data); > + if (ret < 0) > + return ret; > + } > + break; > + case ACPI_RESOURCE_TYPE_PIN_GROUP_FUNCTION: > + ares_pin_group_function = &ares->data.pin_group_function; > + vendor_data = ares_pin_group_function->vendor_data; > + pinctrl_dev = ares_pin_group_function->resource_source.string_ptr; > + group = ares_pin_group_function->resource_source_label.string_ptr; > + pin = 0; > + func = ares_pin_group_function->function_number; > + ret = add_pin_group_node(acpi_map_head, pinctrl_dev, > + group, pin, false, func, vendor_data); > + if (ret < 0) > + return ret; > + > + break; > + case ACPI_RESOURCE_TYPE_PIN_GROUP_CONFIG: > + ares_pin_group_config = &ares->data.pin_group_config; > + vendor_data = ares_pin_group_config->vendor_data; > + pinctrl_dev = ares_pin_group_config->resource_source.string_ptr; > + group = ares_pin_group_config->resource_source_label.string_ptr; > + pin = 0; > + > + config = map_acpi_conf_to_general_conf( > + ares_pin_group_config->pin_config_type, > + ares_pin_group_config->pin_config_value); > + > + ret = add_pin_group_node(acpi_map_head, pinctrl_dev, group, > + pin, true, config, vendor_data); > + if (ret < 0) > + return ret; > + > + break; > + } > + return 1; > +} > + > +static int pinctrl_acpi_get_pin_group_map(struct acpi_device *adev, struct list_head *pin_group_root) > +{ > + struct list_head res_list; > + int ret; > + > + INIT_LIST_HEAD(&res_list); > + > + ret = acpi_dev_get_resources(adev, &res_list, > + pinctrl_acpi_populate_pin_group_map, > + pin_group_root); > + Weird formatting. > + acpi_dev_free_resource_list(&res_list); > + > + return ret; > +} > + > +/* Decode and register acpi pinctrl related properties to pinctrl system */ Kernel-doc > +int pinctrl_acpi_to_map(struct pinctrl *p) > +{ > + int num_maps; > + int ret; > + struct acpi_device *adev; > + struct list_head pin_group_list; > + struct pinctrl_map *new_map; > + struct pinctrl_dev *pctldev; > + const struct pinctrl_ops *ops; > + struct pinctrl_acpi_controller_map *controller_map; > + > + adev = ACPI_COMPANION(p->dev); > + if (!adev) > + return -ENODEV; > + > + /* list to hold the pin/group descriptors generated, grouped by pin controller, pin/group name*/ > + INIT_LIST_HEAD(&pin_group_list); > + > + ret = pinctrl_acpi_get_pin_group_map(adev, &pin_group_list); > + if (ret < 0) > + return ret; > + > + /* Iterate over descriptor for each pin controller and invoke the driver function */ > + list_for_each_entry(controller_map, &pin_group_list, list) { > + const char *pinctrl_dev_name = acpi_node_to_device_name(controller_map->pinctrl_dev); > + > + pctldev = get_pinctrl_dev_from_devname(pinctrl_dev_name); > + ops = pctldev->desc->pctlops; > + if (!ops->acpi_node_to_map) { > + dev_err(p->dev, "pctldev %s doesn't support ACPI\n", > + dev_name(pctldev->dev)); > + return -ENODEV; > + } > + ret = ops->acpi_node_to_map(pctldev, &controller_map->pin_group_maps, &new_map, &num_maps); > + if (ret < 0) { > + return ret; > + } else if (num_maps == 0) { > + dev_info(p->dev, "there is not valid maps for pin controller %s\n", pinctrl_dev_name); > + return 0; > + } > + > + ret = acpi_remember_or_free_map(p, "default", pctldev, new_map, num_maps); > + if (ret < 0) { > + dev_info(p->dev, "Failed to register maps\n"); > + return ret; > + } > + } > + return 0; > +} > > static int pinctrl_acpi_populate_group_desc(struct acpi_resource *ares, void *data) > { > diff --git a/drivers/pinctrl/pinctrl-acpi.h b/drivers/pinctrl/pinctrl-acpi.h > index 1a0c751a7594..4ed45b22257c 100644 > --- a/drivers/pinctrl/pinctrl-acpi.h > +++ b/drivers/pinctrl/pinctrl-acpi.h > @@ -12,11 +12,39 @@ struct pinctrl_acpi_group_desc { > struct list_head list; > }; kernel-doc > > +struct pinctrl_acpi_pin_group_map { > + const char *group; > + unsigned int pin; > + bool is_config; > + struct list_head info; indent > + struct list_head list; > +}; > + kernel-doc > +struct pinctrl_acpi_pin_group_info { > + unsigned config_func; > + void *vendor_data; indent > + struct list_head list; > +}; > + > #ifdef CONFIG_ACPI > int pinctrl_acpi_get_pin_groups(struct acpi_device *adev, struct list_head *group_desc_list); > + > +int pinctrl_acpi_to_map(struct pinctrl *p); > + > +void pinctrl_acpi_free_maps(struct pinctrl *p); > #else > int pinctrl_acpi_get_pin_groups(struct acpi_device *adev, struct list_head *group_desc_list) > { > return -ENODEV; > } > + > +int pinctrl_acpi_to_map(struct pinctrl *p) > +{ > + return -ENODEV; > +} > + > +void pinctrl_acpi_free_maps(struct pinctrl *p) > +{ > + > +} static inline for these. Try also to compile with CONFIG_ACPI=n to see what warnings you get. > #endif > diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h > index 70b45d28e7a9..99a087888c0d 100644 > --- a/include/linux/pinctrl/pinctrl.h > +++ b/include/linux/pinctrl/pinctrl.h > @@ -84,6 +84,15 @@ struct pinctrl_gpio_range { > * allocated members of the mapping table entries themselves. This > * function is optional, and may be omitted for pinctrl drivers that do > * not support device tree. > + * @acpi_node_to_map: process acpi pin related properties, and create ACPI > + * mapping table entries for it. These are returned through the @map and > + * @num_maps output parameters. This function is optional, and may be > + * omitted for pinctrl drivers that do not support acpi. ACPI > + * @acpi_free_map: free mapping table entries created via @dt_node_to_map. The dt_node_to_map? Isn't that acpi_node_to_map? > + * top-level @map pointer must be freed, along with any dynamically > + * allocated members of the mapping table entries themselves. This > + * function is optional, and may be omitted for pinctrl drivers that do > + * not support acpi. ACPI > */ > struct pinctrl_ops { > int (*get_groups_count) (struct pinctrl_dev *pctldev); > @@ -100,6 +109,12 @@ struct pinctrl_ops { > struct pinctrl_map **map, unsigned *num_maps); > void (*dt_free_map) (struct pinctrl_dev *pctldev, > struct pinctrl_map *map, unsigned num_maps); > + int (*acpi_node_to_map) (struct pinctrl_dev *pctldev, > + struct list_head *info_list, > + struct pinctrl_map **map, unsigned *num_maps); > + void (*acpi_free_map) (struct pinctrl_dev *pctldev, > + struct pinctrl_map *map, unsigned num_maps); > + > }; > > /** > -- > 2.25.1