mirror of
https://github.com/torvalds/linux.git
synced 2026-05-12 16:18:45 +02:00
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: Mika Westerberg <mika.westerberg@linux.intel.com> 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-2-4e61f3dbf06a@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
This commit is contained in:
parent
bbee90e750
commit
d1d564ec49
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
args.np = to_of_node(gpiospec->fwnode);
|
||||
args.args_count = gpiospec->nargs;
|
||||
|
||||
xlate_flags = 0;
|
||||
*lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
|
||||
*dflags = GPIOD_ASIS;
|
||||
for (int i = 0; i < args.args_count; i++)
|
||||
args.args[i] = gpiospec->args[i];
|
||||
|
||||
ret = of_property_read_u32(chip_np, "#gpio-cells", &tmp);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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,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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user