Commit 802c51a8 authored by Bartosz Golaszewski's avatar Bartosz Golaszewski
Browse files

gpiolib: fix hogs with multiple lines



After moving GPIO hog handling into GPIOLIB core, we accidentally stopped
supporting devicetree hog definitions with multiple lines like so:

	hog {
		gpio-hog;
		gpios = <3 0>, <4 GPIO_ACTIVE_LOW>;
		output-high;
		line-name = "foo";
	};

Restore this functionality to fix reported regressions.

Fixes: d1d564ec ("gpio: move hogs into GPIO core")
Reported-by: default avatarGeert Uytterhoeven <geert@linux-m68k.org>
Closes: https://lore.kernel.org/all/CAMuHMdX6RuZXAozrF5m625ZepJTVVr4pcyKczSk12MedWvoejw@mail.gmail.com/


Tested-by: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
Link: https://patch.msgid.link/20260330-gpio-hogs-multiple-v3-1-175c3839ad9f@oss.qualcomm.com


Signed-off-by: default avatarBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
parent af475c16
Loading
Loading
Loading
Loading
+54 −38
Original line number Diff line number Diff line
@@ -938,12 +938,18 @@ int gpiochip_add_hog(struct gpio_chip *gc, struct fwnode_handle *fwnode)
	struct fwnode_handle *gc_node = dev_fwnode(&gc->gpiodev->dev);
	struct fwnode_reference_args gpiospec;
	enum gpiod_flags dflags;
	const char *name = NULL;
	struct gpio_desc *desc;
	unsigned int num_hogs;
	unsigned long lflags;
	const char *name;
	int ret, argc;
	u32 gpios[3]; /* We support up to three-cell bindings. */
	u32 cells;
	/*
	 * For devicetree-based systems, this needs to be defined in bindings
	 * and there's no real default value. For other firmware descriptions
	 * it makes the most sense to use 2 cells for the GPIO offset and
	 * request flags.
	 */
	u32 cells = 2;

	lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
	dflags = GPIOD_ASIS;
@@ -952,30 +958,46 @@ int gpiochip_add_hog(struct gpio_chip *gc, struct fwnode_handle *fwnode)
	argc = fwnode_property_count_u32(fwnode, "gpios");
	if (argc < 0)
		return argc;
	if (argc > 3)

	ret = fwnode_property_read_u32(gc_node, "#gpio-cells", &cells);
	if (ret && is_of_node(fwnode))
		return ret;
	if (argc % cells)
		return -EINVAL;

	num_hogs = argc / cells;

	u32 *gpios __free(kfree) = kzalloc_objs(*gpios, argc);
	if (!gpios)
		return -ENOMEM;

	ret = fwnode_property_read_u32_array(fwnode, "gpios", gpios, argc);
	if (ret < 0)
		return ret;

	if (fwnode_property_present(fwnode, "input"))
		dflags |= GPIOD_IN;
	else if (fwnode_property_present(fwnode, "output-low"))
		dflags |= GPIOD_OUT_LOW;
	else if (fwnode_property_present(fwnode, "output-high"))
		dflags |= GPIOD_OUT_HIGH;
	else
		return -EINVAL;

	fwnode_property_read_string(fwnode, "line-name", &name);

	for (unsigned int i = 0; i < num_hogs; i++) {
		if (is_of_node(fwnode)) {
			/*
			 * OF-nodes need some additional special handling for
			 * translating of devicetree flags.
			 */
		ret = fwnode_property_read_u32(gc_node, "#gpio-cells", &cells);
		if (ret)
			return ret;
		if (!ret && argc != cells)
			return -EINVAL;

			memset(&gpiospec, 0, sizeof(gpiospec));
			gpiospec.fwnode = fwnode;
		gpiospec.nargs = argc;
			gpiospec.nargs = cells;

		for (int i = 0; i < argc; i++)
			gpiospec.args[i] = gpios[i];
			for (unsigned int j = 0; j < cells; j++)
				gpiospec.args[j] = gpios[i * cells + j];

			ret = of_gpiochip_get_lflags(gc, &gpiospec, &lflags);
			if (ret)
@@ -985,26 +1007,20 @@ int gpiochip_add_hog(struct gpio_chip *gc, struct fwnode_handle *fwnode)
			 * GPIO_ACTIVE_LOW is currently the only lookup flag
			 * supported for non-OF firmware nodes.
			 */
		if (gpios[1])
			if (gpios[i * cells + 1])
				lflags |= GPIO_ACTIVE_LOW;
		}

	if (fwnode_property_present(fwnode, "input"))
		dflags |= GPIOD_IN;
	else if (fwnode_property_present(fwnode, "output-low"))
		dflags |= GPIOD_OUT_LOW;
	else if (fwnode_property_present(fwnode, "output-high"))
		dflags |= GPIOD_OUT_HIGH;
	else
		return -EINVAL;

	fwnode_property_read_string(fwnode, "line-name", &name);

	desc = gpiochip_get_desc(gc, gpios[0]);
		desc = gpiochip_get_desc(gc, gpios[i * cells]);
		if (IS_ERR(desc))
			return PTR_ERR(desc);

	return gpiod_hog(desc, name, lflags, dflags);
		ret = gpiod_hog(desc, name, lflags, dflags);
		if (ret)
			return ret;
	}

	return 0;
}

static int gpiochip_hog_lines(struct gpio_chip *gc)