Commit 5cfbd0eb authored by Bartosz Golaszewski's avatar Bartosz Golaszewski
Browse files

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: default avatarLinus Walleij <linusw@kernel.org>
Reviewed-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://patch.msgid.link/20260309-gpio-hog-fwnode-v2-3-4e61f3dbf06a@oss.qualcomm.com


Signed-off-by: default avatarBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
parent d1d564ec
Loading
Loading
Loading
Loading
+56 −106
Original line number Diff line number Diff line
@@ -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,13 +929,13 @@ 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);
		ret = gpio_sim_bank_add_hogs(bank);
		if (ret) {
			gpio_sim_remove_swnode_recursive(swnode);
			return ret;
		}
	}

	pdevinfo.name = "gpio-sim";
	pdevinfo.fwnode = swnode;
@@ -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);
}