Previously, there was no way to assign names to GPIO lines exported through the aggregator when the device was created via sysfs. Allow users to set custom line names via a 'name' attribute and expose them using swnode. Signed-off-by: Koichiro Den <koichiro.den@xxxxxxxxxxxxx> --- drivers/gpio/gpio-aggregator.c | 84 ++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index ec102453817b..692d90246674 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -61,6 +61,8 @@ struct gpio_aggregator_line { /* Line index within the aggregator device */ unsigned int idx; + /* Custom name for the virtual line */ + const char *name; /* GPIO chip label or line name */ const char *key; /* Can be negative to indicate lookup by line name */ @@ -530,10 +532,40 @@ to_gpio_aggregator_line(struct config_item *item) return container_of(group, struct gpio_aggregator_line, group); } +static struct fwnode_handle *aggr_make_device_swnode(struct gpio_aggregator *aggr) +{ + const char **line_names __free(kfree) = NULL; + struct property_entry properties[2]; + struct gpio_aggregator_line *line; + size_t num_lines; + int n = 0; + + memset(properties, 0, sizeof(properties)); + + num_lines = aggr_count_lines(aggr); + if (num_lines == 0) + return NULL; + + line_names = kcalloc(num_lines, sizeof(*line_names), GFP_KERNEL); + if (!line_names) + return ERR_PTR(-ENOMEM); + + /* The list is always sorted as new elements are inserted in order. */ + list_for_each_entry(line, &aggr->list_head, entry) + line_names[n++] = line->name ?: ""; + + properties[0] = PROPERTY_ENTRY_STRING_ARRAY_LEN( + "gpio-line-names", + line_names, num_lines); + + return fwnode_create_software_node(properties, NULL); +} + static int aggr_activate(struct gpio_aggregator *aggr) { struct platform_device_info pdevinfo; struct gpio_aggregator_line *line; + struct fwnode_handle *swnode; unsigned int n = 0; int ret = 0; @@ -545,9 +577,14 @@ static int aggr_activate(struct gpio_aggregator *aggr) if (!aggr->lookups) return -ENOMEM; + swnode = aggr_make_device_swnode(aggr); + if (IS_ERR(swnode)) + goto err_remove_lookups; + memset(&pdevinfo, 0, sizeof(pdevinfo)); pdevinfo.name = DRV_NAME; pdevinfo.id = aggr->id; + pdevinfo.fwnode = swnode; /* The list is always sorted as new elements are inserted in order. */ list_for_each_entry(line, &aggr->list_head, entry) { @@ -559,7 +596,7 @@ static int aggr_activate(struct gpio_aggregator *aggr) */ if (!line->key || line->idx != n) { ret = -EINVAL; - goto err_remove_lookups; + goto err_remove_swnode; } if (line->offset < 0) @@ -567,13 +604,13 @@ static int aggr_activate(struct gpio_aggregator *aggr) else ret = aggr_add_gpio(aggr, line->key, line->offset, &n); if (ret) - goto err_remove_lookups; + goto err_remove_swnode; } aggr->lookups->dev_id = kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, aggr->id); if (!aggr->lookups->dev_id) { ret = -ENOMEM; - goto err_remove_lookups; + goto err_remove_swnode; } gpiod_add_lookup_table(aggr->lookups); @@ -587,6 +624,8 @@ static int aggr_activate(struct gpio_aggregator *aggr) err_remove_lookup_table: kfree(aggr->lookups->dev_id); gpiod_remove_lookup_table(aggr->lookups); +err_remove_swnode: + fwnode_remove_software_node(swnode); err_remove_lookups: kfree(aggr->lookups); @@ -658,6 +697,43 @@ gpio_aggr_line_key_store(struct config_item *item, const char *page, } CONFIGFS_ATTR(gpio_aggr_line_, key); +static ssize_t +gpio_aggr_line_name_show(struct config_item *item, char *page) +{ + struct gpio_aggregator_line *line = to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr = line->parent; + + guard(mutex)(&aggr->lock); + + return sysfs_emit(page, "%s\n", line->name ?: ""); +} + +static ssize_t +gpio_aggr_line_name_store(struct config_item *item, const char *page, + size_t count) +{ + struct gpio_aggregator_line *line = to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr = line->parent; + + char *name __free(kfree) = kstrndup(skip_spaces(page), count, + GFP_KERNEL); + if (!name) + return -ENOMEM; + + strim(name); + + guard(mutex)(&aggr->lock); + + if (aggr_is_active(aggr)) + return -EBUSY; + + kfree(line->name); + line->name = no_free_ptr(name); + + return count; +} +CONFIGFS_ATTR(gpio_aggr_line_, name); + static ssize_t gpio_aggr_line_offset_show(struct config_item *item, char *page) { @@ -707,6 +783,7 @@ CONFIGFS_ATTR(gpio_aggr_line_, offset); static struct configfs_attribute *gpio_aggr_line_attrs[] = { &gpio_aggr_line_attr_key, + &gpio_aggr_line_attr_name, &gpio_aggr_line_attr_offset, NULL }; @@ -795,6 +872,7 @@ gpio_aggr_line_release(struct config_item *item) aggr_line_del(aggr, line); kfree(line->key); + kfree(line->name); kfree(line); } -- 2.45.2