]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
gpio: sim: use fwnode-based GPIO hogs
authorBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Mon, 9 Mar 2026 12:42:39 +0000 (13:42 +0100)
committerBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Mon, 16 Mar 2026 08:51:06 +0000 (09:51 +0100)
Convert gpio-sim to using software nodes for setting up simulated hogs
instead of legacy machine hogs.

Reviewed-by: Linus Walleij <linusw@kernel.org>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://patch.msgid.link/20260309-gpio-hog-fwnode-v2-3-4e61f3dbf06a@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
drivers/gpio/gpio-sim.c

index 13b87c8e6d0caf62ce311dad409a9b4d3f091caa..51bcbdd91b4b027b7a340971a11cce5280ca1295 100644 (file)
@@ -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);
 }