From: Bartosz Golaszewski Date: Mon, 9 Mar 2026 12:42:39 +0000 (+0100) Subject: gpio: sim: use fwnode-based GPIO hogs X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5cfbd0eb784f19436b5d5a9a7e0dca862619739a;p=thirdparty%2Fkernel%2Flinux.git gpio: sim: use fwnode-based GPIO hogs Convert gpio-sim to using software nodes for setting up simulated hogs instead of legacy machine hogs. Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260309-gpio-hog-fwnode-v2-3-4e61f3dbf06a@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index 13b87c8e6d0ca..51bcbdd91b4b0 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); }