[PATCH v2 3/6] gpio: sim: use fwnode-based GPIO hogs

From: Bartosz Golaszewski

Date: Mon Mar 09 2026 - 08:45:30 EST


Convert gpio-sim to using software nodes for setting up simulated hogs
instead of legacy machine hogs.

Reviewed-by: Linus Walleij <linusw@xxxxxxxxxx>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@xxxxxxxxxxxxxxx>
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@xxxxxxxxxxxxxxxx>
---
drivers/gpio/gpio-sim.c | 162 +++++++++++++++++-------------------------------
1 file changed, 56 insertions(+), 106 deletions(-)

diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c
index 13b87c8e6d0caf62ce311dad409a9b4d3f091caa..51bcbdd91b4b027b7a340971a11cce5280ca1295 100644
--- a/drivers/gpio/gpio-sim.c
+++ b/drivers/gpio/gpio-sim.c
@@ -40,6 +40,7 @@

#define GPIO_SIM_NGPIO_MAX 1024
#define GPIO_SIM_PROP_MAX 5 /* Max 4 properties + sentinel. */
+#define GPIO_SIM_HOG_PROP_MAX 5
#define GPIO_SIM_NUM_ATTRS 3 /* value, pull and sentinel */

static DEFINE_IDA(gpio_sim_ida);
@@ -561,8 +562,6 @@ struct gpio_sim_device {
*/
struct mutex lock;

- struct gpiod_hog *hogs;
-
struct list_head bank_list;
};

@@ -774,102 +773,6 @@ static void gpio_sim_set_reserved_ranges(struct gpio_sim_bank *bank,
}
}

-static void gpio_sim_remove_hogs(struct gpio_sim_device *dev)
-{
- struct gpiod_hog *hog;
-
- if (!dev->hogs)
- return;
-
- gpiod_remove_hogs(dev->hogs);
-
- for (hog = dev->hogs; hog->chip_label; hog++) {
- kfree(hog->chip_label);
- kfree(hog->line_name);
- }
-
- kfree(dev->hogs);
- dev->hogs = NULL;
-}
-
-static int gpio_sim_add_hogs(struct gpio_sim_device *dev)
-{
- unsigned int num_hogs = 0, idx = 0;
- struct gpio_sim_bank *bank;
- struct gpio_sim_line *line;
- struct gpiod_hog *hog;
-
- list_for_each_entry(bank, &dev->bank_list, siblings) {
- list_for_each_entry(line, &bank->line_list, siblings) {
- if (line->offset >= bank->num_lines)
- continue;
-
- if (line->hog)
- num_hogs++;
- }
- }
-
- if (!num_hogs)
- return 0;
-
- /* Allocate one more for the sentinel. */
- dev->hogs = kzalloc_objs(*dev->hogs, num_hogs + 1);
- if (!dev->hogs)
- return -ENOMEM;
-
- list_for_each_entry(bank, &dev->bank_list, siblings) {
- list_for_each_entry(line, &bank->line_list, siblings) {
- if (line->offset >= bank->num_lines)
- continue;
-
- if (!line->hog)
- continue;
-
- hog = &dev->hogs[idx++];
-
- /*
- * We need to make this string manually because at this
- * point the device doesn't exist yet and so dev_name()
- * is not available.
- */
- if (gpio_sim_bank_has_label(bank))
- hog->chip_label = kstrdup(bank->label,
- GFP_KERNEL);
- else
- hog->chip_label = kasprintf(GFP_KERNEL,
- "gpio-sim.%u:%pfwP",
- dev->id,
- bank->swnode);
- if (!hog->chip_label) {
- gpio_sim_remove_hogs(dev);
- return -ENOMEM;
- }
-
- /*
- * We need to duplicate this because the hog config
- * item can be removed at any time (and we can't block
- * it) and gpiolib doesn't make a deep copy of the hog
- * data.
- */
- if (line->hog->name) {
- hog->line_name = kstrdup(line->hog->name,
- GFP_KERNEL);
- if (!hog->line_name) {
- gpio_sim_remove_hogs(dev);
- return -ENOMEM;
- }
- }
-
- hog->chip_hwnum = line->offset;
- hog->dflags = line->hog->dir;
- }
- }
-
- gpiod_add_hogs(dev->hogs);
-
- return 0;
-}
-
static struct fwnode_handle *
gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank,
struct fwnode_handle *parent)
@@ -917,12 +820,61 @@ gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank,
return fwnode_create_software_node(properties, parent);
}

+static int gpio_sim_bank_add_hogs(struct gpio_sim_bank *bank)
+{
+ struct property_entry properties[GPIO_SIM_HOG_PROP_MAX];
+ struct fwnode_handle *swnode;
+ struct gpio_sim_line *line;
+ struct gpio_sim_hog *hog;
+ unsigned int idx;
+ u32 gpios[2];
+
+ list_for_each_entry(line, &bank->line_list, siblings) {
+ if (!line->hog)
+ continue;
+
+ hog = line->hog;
+
+ gpios[0] = line->offset;
+ gpios[1] = 0;
+
+ memset(properties, 0, sizeof(properties));
+
+ idx = 0;
+ properties[idx++] = PROPERTY_ENTRY_BOOL("gpio-hog");
+ properties[idx++] = PROPERTY_ENTRY_U32_ARRAY("gpios", gpios);
+ properties[idx++] = PROPERTY_ENTRY_STRING("line-name", hog->name);
+
+ switch (hog->dir) {
+ case GPIOD_IN:
+ properties[idx++] = PROPERTY_ENTRY_BOOL("input");
+ break;
+ case GPIOD_OUT_HIGH:
+ properties[idx++] = PROPERTY_ENTRY_BOOL("output-high");
+ break;
+ case GPIOD_OUT_LOW:
+ properties[idx++] = PROPERTY_ENTRY_BOOL("output-low");
+ break;
+ default:
+ /* Would have been validated at configfs store. */
+ WARN(1, "Unexpected hog direction value: %d", hog->dir);
+ return -EINVAL;
+ }
+
+ swnode = fwnode_create_software_node(properties, bank->swnode);
+ if (IS_ERR(swnode))
+ return PTR_ERR(swnode);
+ }
+
+ return 0;
+}
+
static void gpio_sim_remove_swnode_recursive(struct fwnode_handle *swnode)
{
struct fwnode_handle *child;

fwnode_for_each_child_node(swnode, child)
- fwnode_remove_software_node(child);
+ gpio_sim_remove_swnode_recursive(child);

fwnode_remove_software_node(swnode);
}
@@ -977,12 +929,12 @@ static int gpio_sim_device_activate(struct gpio_sim_device *dev)
gpio_sim_remove_swnode_recursive(swnode);
return ret;
}
- }

- ret = gpio_sim_add_hogs(dev);
- if (ret) {
- gpio_sim_remove_swnode_recursive(swnode);
- return ret;
+ ret = gpio_sim_bank_add_hogs(bank);
+ if (ret) {
+ gpio_sim_remove_swnode_recursive(swnode);
+ return ret;
+ }
}

pdevinfo.name = "gpio-sim";
@@ -991,7 +943,6 @@ static int gpio_sim_device_activate(struct gpio_sim_device *dev)

ret = dev_sync_probe_register(&dev->probe_data, &pdevinfo);
if (ret) {
- gpio_sim_remove_hogs(dev);
gpio_sim_remove_swnode_recursive(swnode);
return ret;
}
@@ -1007,7 +958,6 @@ static void gpio_sim_device_deactivate(struct gpio_sim_device *dev)

swnode = dev_fwnode(&dev->probe_data.pdev->dev);
dev_sync_probe_unregister(&dev->probe_data);
- gpio_sim_remove_hogs(dev);
gpio_sim_remove_swnode_recursive(swnode);
}


--
2.47.3