@@ -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);
}
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@canonical.com> --- drivers/gpio/gpio-aggregator.c | 84 ++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 3 deletions(-)