Commit d1d564ec authored by Bartosz Golaszewski's avatar Bartosz Golaszewski
Browse files

gpio: move hogs into GPIO core



Refactor line hogging code by moving the parts duplicated in
gpiolib-acpi-core.c and gpiolib-of.c into gpiolib.c, leaving just the
OF-specific bits in the latter.

This makes fwnode the primary API for setting up hogs and allows to use
software nodes in addition to ACPI and OF nodes.

Reviewed-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
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-2-4e61f3dbf06a@oss.qualcomm.com


Signed-off-by: default avatarBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
parent bbee90e7
Loading
Loading
Loading
Loading
+0 −70
Original line number Diff line number Diff line
@@ -1220,75 +1220,6 @@ static void acpi_gpiochip_free_regions(struct acpi_gpio_chip *achip)
	}
}

static struct gpio_desc *
acpi_gpiochip_parse_own_gpio(struct acpi_gpio_chip *achip,
			     struct fwnode_handle *fwnode,
			     const char **name,
			     unsigned long *lflags,
			     enum gpiod_flags *dflags)
{
	struct gpio_chip *chip = achip->chip;
	struct gpio_desc *desc;
	u32 gpios[2];
	int ret;

	*lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
	*dflags = GPIOD_ASIS;
	*name = NULL;

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

	desc = gpiochip_get_desc(chip, gpios[0]);
	if (IS_ERR(desc))
		return desc;

	if (gpios[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 ERR_PTR(-EINVAL);

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

	return desc;
}

static void acpi_gpiochip_scan_gpios(struct acpi_gpio_chip *achip)
{
	struct gpio_chip *chip = achip->chip;

	device_for_each_child_node_scoped(chip->parent, fwnode) {
		unsigned long lflags;
		enum gpiod_flags dflags;
		struct gpio_desc *desc;
		const char *name;
		int ret;

		if (!fwnode_property_present(fwnode, "gpio-hog"))
			continue;

		desc = acpi_gpiochip_parse_own_gpio(achip, fwnode, &name,
						    &lflags, &dflags);
		if (IS_ERR(desc))
			continue;

		ret = gpiod_hog(desc, name, lflags, dflags);
		if (ret) {
			dev_err(chip->parent, "Failed to hog GPIO\n");
			return;
		}
	}
}

void acpi_gpiochip_add(struct gpio_chip *chip)
{
	struct acpi_gpio_chip *acpi_gpio;
@@ -1321,7 +1252,6 @@ void acpi_gpiochip_add(struct gpio_chip *chip)
	}

	acpi_gpiochip_request_regions(acpi_gpio);
	acpi_gpiochip_scan_gpios(acpi_gpio);
	acpi_dev_clear_dependencies(adev);
}

+16 −127
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
#include <linux/device.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/fwnode.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -712,139 +713,26 @@ struct gpio_desc *of_find_gpio(struct device_node *np, const char *con_id,
	return desc;
}

/**
 * of_parse_own_gpio() - Get a GPIO hog descriptor, names and flags for GPIO API
 * @np:		device node to get GPIO from
 * @chip:	GPIO chip whose hog is parsed
 * @idx:	Index of the GPIO to parse
 * @name:	GPIO line name
 * @lflags:	bitmask of gpio_lookup_flags GPIO_* values - returned from
 *		of_find_gpio() or of_parse_own_gpio()
 * @dflags:	gpiod_flags - optional GPIO initialization flags
 *
 * Returns:
 * GPIO descriptor to use with Linux GPIO API, or one of the errno
 * value on the error condition.
 */
static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
					   struct gpio_chip *chip,
					   unsigned int idx, const char **name,
					   unsigned long *lflags,
					   enum gpiod_flags *dflags)
int of_gpiochip_get_lflags(struct gpio_chip *chip,
			   struct fwnode_reference_args *gpiospec,
			   unsigned long *lflags)
{
	struct device_node *chip_np;
	enum of_gpio_flags xlate_flags;
	struct of_phandle_args gpiospec;
	struct of_phandle_args args;
	struct gpio_desc *desc;
	unsigned int i;
	u32 tmp;
	int ret;

	chip_np = dev_of_node(&chip->gpiodev->dev);
	if (!chip_np)
		return ERR_PTR(-EINVAL);

	xlate_flags = 0;
	*lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
	*dflags = GPIOD_ASIS;

	ret = of_property_read_u32(chip_np, "#gpio-cells", &tmp);
	if (ret)
		return ERR_PTR(ret);
	args.np = to_of_node(gpiospec->fwnode);
	args.args_count = gpiospec->nargs;

	gpiospec.np = chip_np;
	gpiospec.args_count = tmp;

	for (i = 0; i < tmp; i++) {
		ret = of_property_read_u32_index(np, "gpios", idx * tmp + i,
						 &gpiospec.args[i]);
		if (ret)
			return ERR_PTR(ret);
	}
	for (int i = 0; i < args.args_count; i++)
		args.args[i] = gpiospec->args[i];

	desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, &xlate_flags);
	desc = of_xlate_and_get_gpiod_flags(chip, &args, &xlate_flags);
	if (IS_ERR(desc))
		return desc;
		return PTR_ERR(desc);

	*lflags = of_convert_gpio_flags(xlate_flags);

	if (of_property_read_bool(np, "input"))
		*dflags |= GPIOD_IN;
	else if (of_property_read_bool(np, "output-low"))
		*dflags |= GPIOD_OUT_LOW;
	else if (of_property_read_bool(np, "output-high"))
		*dflags |= GPIOD_OUT_HIGH;
	else {
		pr_warn("GPIO line %d (%pOFn): no hogging state specified, bailing out\n",
			desc_to_gpio(desc), np);
		return ERR_PTR(-EINVAL);
	}

	if (name && of_property_read_string(np, "line-name", name))
		*name = np->name;

	return desc;
}

/**
 * of_gpiochip_add_hog - Add all hogs in a hog device node
 * @chip:	gpio chip to act on
 * @hog:	device node describing the hogs
 *
 * Returns:
 * 0 on success, or negative errno on failure.
 */
static int of_gpiochip_add_hog(struct gpio_chip *chip, struct device_node *hog)
{
	enum gpiod_flags dflags;
	struct gpio_desc *desc;
	unsigned long lflags;
	const char *name;
	unsigned int i;
	int ret;

	for (i = 0;; i++) {
		desc = of_parse_own_gpio(hog, chip, i, &name, &lflags, &dflags);
		if (IS_ERR(desc))
			break;

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

#ifdef CONFIG_OF_DYNAMIC
		WRITE_ONCE(desc->hog, hog);
#endif
	}

	return 0;
}

/**
 * of_gpiochip_scan_gpios - Scan gpio-controller for gpio definitions
 * @chip:	gpio chip to act on
 *
 * This is only used by of_gpiochip_add to request/set GPIO initial
 * configuration.
 *
 * Returns:
 * 0 on success, or negative errno on failure.
 */
static int of_gpiochip_scan_gpios(struct gpio_chip *chip)
{
	int ret;

	for_each_available_child_of_node_scoped(dev_of_node(&chip->gpiodev->dev), np) {
		if (!of_property_read_bool(np, "gpio-hog"))
			continue;

		ret = of_gpiochip_add_hog(chip, np);
		if (ret < 0)
			return ret;

		of_node_set_flag(np, OF_POPULATED);
	}

	return 0;
}

@@ -899,7 +787,7 @@ static int of_gpio_notify(struct notifier_block *nb, unsigned long action,
		if (!gdev)
			return NOTIFY_DONE;	/* not for us */

		ret = of_gpiochip_add_hog(gpio_device_get_chip(gdev), rd->dn);
		ret = gpiochip_add_hog(gpio_device_get_chip(gdev), of_fwnode_handle(rd->dn));
		if (ret < 0) {
			pr_err("%s: failed to add hogs for %pOF\n", __func__,
			       rd->dn);
@@ -1178,9 +1066,10 @@ int of_gpiochip_add(struct gpio_chip *chip)

	of_node_get(np);

	ret = of_gpiochip_scan_gpios(chip);
	if (ret)
		of_node_put(np);
	for_each_available_child_of_node_scoped(np, child) {
		if (of_property_read_bool(child, "gpio-hog"))
			of_node_set_flag(child, OF_POPULATED);
	}

	return ret;
}
+10 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@

struct device_node;
struct fwnode_handle;
struct fwnode_reference_args;

struct gpio_chip;
struct gpio_desc;
@@ -24,6 +25,9 @@ int of_gpiochip_add(struct gpio_chip *gc);
void of_gpiochip_remove(struct gpio_chip *gc);
bool of_gpiochip_instance_match(struct gpio_chip *gc, unsigned int index);
int of_gpio_count(const struct fwnode_handle *fwnode, const char *con_id);
int of_gpiochip_get_lflags(struct gpio_chip *chip,
			   struct fwnode_reference_args *gpiospec,
			   unsigned long *lflags);
#else
static inline struct gpio_desc *of_find_gpio(struct device_node *np,
					     const char *con_id,
@@ -44,6 +48,12 @@ static inline int of_gpio_count(const struct fwnode_handle *fwnode,
{
	return 0;
}
static inline int of_gpiochip_get_lflags(struct gpio_chip *chip,
					 struct fwnode_reference_args *gpiospec,
					 unsigned long *lflags)
{
	return -ENOENT;
}
#endif /* CONFIG_OF_GPIO */

extern struct notifier_block gpio_of_notifier;
+96 −2
Original line number Diff line number Diff line
@@ -948,7 +948,7 @@ static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog)
			  __func__, gc->label, hog->chip_hwnum, rv);
}

static void machine_gpiochip_add(struct gpio_chip *gc)
static void gpiochip_machine_hog_lines(struct gpio_chip *gc)
{
	struct gpiod_hog *hog;

@@ -960,6 +960,98 @@ static void machine_gpiochip_add(struct gpio_chip *gc)
	}
}

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;
	struct gpio_desc *desc;
	unsigned long lflags;
	const char *name;
	int ret, argc;
	u32 gpios[3]; /* We support up to three-cell bindings. */
	u32 cells;

	lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
	dflags = GPIOD_ASIS;
	name = NULL;

	argc = fwnode_property_count_u32(fwnode, "gpios");
	if (argc < 0)
		return argc;
	if (argc > 3)
		return -EINVAL;

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

	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;

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

		ret = of_gpiochip_get_lflags(gc, &gpiospec, &lflags);
		if (ret)
			return ret;
	} else {
		/*
		 * GPIO_ACTIVE_LOW is currently the only lookup flag
		 * supported for non-OF firmware nodes.
		 */
		if (gpios[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]);
	if (IS_ERR(desc))
		return PTR_ERR(desc);

	return gpiod_hog(desc, name, lflags, dflags);
}

static int gpiochip_hog_lines(struct gpio_chip *gc)
{
	int ret;

	device_for_each_child_node_scoped(&gc->gpiodev->dev, fwnode) {
		if (!fwnode_property_present(fwnode, "gpio-hog"))
			continue;

		ret = gpiochip_add_hog(gc, fwnode);
		if (ret)
			return ret;
	}

	gpiochip_machine_hog_lines(gc);

	return 0;
}

static void gpiochip_setup_devs(void)
{
	struct gpio_device *gdev;
@@ -1209,7 +1301,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,

	acpi_gpiochip_add(gc);

	machine_gpiochip_add(gc);
	ret = gpiochip_hog_lines(gc);
	if (ret)
		goto err_remove_of_chip;

	ret = gpiochip_irqchip_init_valid_mask(gc);
	if (ret)
+3 −0
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@

#define GPIOCHIP_NAME	"gpiochip"

struct fwnode_handle;

/**
 * struct gpio_device - internal state container for GPIO devices
 * @dev: the GPIO device struct
@@ -274,6 +276,7 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce);
int gpiod_hog(struct gpio_desc *desc, const char *name,
		unsigned long lflags, enum gpiod_flags dflags);
int gpiochip_add_hog(struct gpio_chip *gc, struct fwnode_handle *fwnode);
int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev);
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, unsigned int hwnum);
const char *gpiod_get_label(struct gpio_desc *desc);