Skip to content

Commit e7f9ff5

Browse files
dtorBartosz Golaszewski
authored andcommitted
gpiolib: add support for software nodes
Now that static device properties understand notion of child nodes and references, let's teach gpiolib to handle them: - GPIOs are represented as a references to software nodes representing gpiochip - references must have 2 arguments - GPIO number within the chip and GPIO flags (GPIO_ACTIVE_LOW/GPIO_ACTIVE_HIGH, etc) - a new PROPERTY_ENTRY_GPIO() macro is supplied to ensure the above - name of the software node representing gpiochip must match label of the gpiochip, as we use it to locate gpiochip structure at runtime The following illustrates use of software nodes to describe a "System" button that is currently specified via use of gpio_keys_platform_data in arch/mips/alchemy/board-mtx1.c. It follows bindings specified in Documentation/devicetree/bindings/input/gpio-keys.yaml. static const struct software_node mxt1_gpiochip2_node = { .name = "alchemy-gpio2", }; static const struct property_entry mtx1_gpio_button_props[] = { PROPERTY_ENTRY_U32("linux,code", BTN_0), PROPERTY_ENTRY_STRING("label", "System button"), PROPERTY_ENTRY_GPIO("gpios", &mxt1_gpiochip2_node, 7, GPIO_ACTIVE_LOW), { } }; Similarly, arch/arm/mach-tegra/board-paz00.c can be converted to: static const struct software_node tegra_gpiochip_node = { .name = "tegra-gpio", }; static struct property_entry wifi_rfkill_prop[] __initdata = { PROPERTY_ENTRY_STRING("name", "wifi_rfkill"), PROPERTY_ENTRY_STRING("type", "wlan"), PROPERTY_ENTRY_GPIO("reset-gpios", &tegra_gpiochip_node, 25, GPIO_ACTIVE_HIGH); PROPERTY_ENTRY_GPIO("shutdown-gpios", &tegra_gpiochip_node, 85, GPIO_ACTIVE_HIGH); { }, }; static struct platform_device wifi_rfkill_device = { .name = "rfkill_gpio", .id = -1, }; ... software_node_register(&tegra_gpiochip_node); device_create_managed_software_node(&wifi_rfkill_device.dev, wifi_rfkill_prop, NULL); Acked-by: Linus Walleij <[email protected]> Reviewed-by: Andy Shevchenko <[email protected]> Signed-off-by: Dmitry Torokhov <[email protected]> Signed-off-by: Bartosz Golaszewski <[email protected]>
1 parent 8eb1f71 commit e7f9ff5

File tree

5 files changed

+156
-0
lines changed

5 files changed

+156
-0
lines changed

drivers/gpio/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ obj-$(CONFIG_OF_GPIO) += gpiolib-of.o
1010
obj-$(CONFIG_GPIO_CDEV) += gpiolib-cdev.o
1111
obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o
1212
obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o
13+
obj-$(CONFIG_GPIOLIB) += gpiolib-swnode.o
1314

1415
# Device drivers. Generally keep list sorted alphabetically
1516
obj-$(CONFIG_GPIO_REGMAP) += gpio-regmap.o

drivers/gpio/gpiolib-swnode.c

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// SPDX-License-Identifier: GPL-2.0+
2+
/*
3+
* Software Node helpers for the GPIO API
4+
*
5+
* Copyright 2022 Google LLC
6+
*/
7+
#include <linux/err.h>
8+
#include <linux/errno.h>
9+
#include <linux/gpio/consumer.h>
10+
#include <linux/gpio/driver.h>
11+
#include <linux/kernel.h>
12+
#include <linux/printk.h>
13+
#include <linux/property.h>
14+
#include <linux/string.h>
15+
16+
#include "gpiolib.h"
17+
#include "gpiolib-swnode.h"
18+
19+
static void swnode_format_propname(const char *con_id, char *propname,
20+
size_t max_size)
21+
{
22+
/*
23+
* Note we do not need to try both -gpios and -gpio suffixes,
24+
* as, unlike OF and ACPI, we can fix software nodes to conform
25+
* to the proper binding.
26+
*/
27+
if (con_id)
28+
snprintf(propname, max_size, "%s-gpios", con_id);
29+
else
30+
strscpy(propname, "gpios", max_size);
31+
}
32+
33+
static int swnode_gpiochip_match_name(struct gpio_chip *chip, void *data)
34+
{
35+
return !strcmp(chip->label, data);
36+
}
37+
38+
static struct gpio_chip *swnode_get_chip(struct fwnode_handle *fwnode)
39+
{
40+
const struct software_node *chip_node;
41+
struct gpio_chip *chip;
42+
43+
chip_node = to_software_node(fwnode);
44+
if (!chip_node || !chip_node->name)
45+
return ERR_PTR(-EINVAL);
46+
47+
chip = gpiochip_find((void *)chip_node->name, swnode_gpiochip_match_name);
48+
return chip ?: ERR_PTR(-EPROBE_DEFER);
49+
}
50+
51+
struct gpio_desc *swnode_find_gpio(struct fwnode_handle *fwnode,
52+
const char *con_id, unsigned int idx,
53+
unsigned long *flags)
54+
{
55+
const struct software_node *swnode;
56+
struct fwnode_reference_args args;
57+
struct gpio_chip *chip;
58+
struct gpio_desc *desc;
59+
char propname[32]; /* 32 is max size of property name */
60+
int error;
61+
62+
swnode = to_software_node(fwnode);
63+
if (!swnode)
64+
return ERR_PTR(-EINVAL);
65+
66+
swnode_format_propname(con_id, propname, sizeof(propname));
67+
68+
/*
69+
* We expect all swnode-described GPIOs have GPIO number and
70+
* polarity arguments, hence nargs is set to 2.
71+
*/
72+
error = fwnode_property_get_reference_args(fwnode, propname, NULL, 2, idx, &args);
73+
if (error) {
74+
pr_debug("%s: can't parse '%s' property of node '%pfwP[%d]'\n",
75+
__func__, propname, fwnode, idx);
76+
return ERR_PTR(error);
77+
}
78+
79+
chip = swnode_get_chip(args.fwnode);
80+
fwnode_handle_put(args.fwnode);
81+
if (IS_ERR(chip))
82+
return ERR_CAST(chip);
83+
84+
desc = gpiochip_get_desc(chip, args.args[0]);
85+
*flags = args.args[1]; /* We expect native GPIO flags */
86+
87+
pr_debug("%s: parsed '%s' property of node '%pfwP[%d]' - status (%d)\n",
88+
__func__, propname, fwnode, idx, PTR_ERR_OR_ZERO(desc));
89+
90+
return desc;
91+
}
92+
93+
/**
94+
* swnode_gpio_count - count the GPIOs associated with a device / function
95+
* @fwnode: firmware node of the GPIO consumer, can be %NULL for
96+
* system-global GPIOs
97+
* @con_id: function within the GPIO consumer
98+
*
99+
* Return:
100+
* The number of GPIOs associated with a device / function or %-ENOENT,
101+
* if no GPIO has been assigned to the requested function.
102+
*/
103+
int swnode_gpio_count(const struct fwnode_handle *fwnode, const char *con_id)
104+
{
105+
struct fwnode_reference_args args;
106+
char propname[32];
107+
int count;
108+
109+
swnode_format_propname(con_id, propname, sizeof(propname));
110+
111+
/*
112+
* This is not very efficient, but GPIO lists usually have only
113+
* 1 or 2 entries.
114+
*/
115+
count = 0;
116+
while (fwnode_property_get_reference_args(fwnode, propname, NULL, 0,
117+
count, &args) == 0) {
118+
fwnode_handle_put(args.fwnode);
119+
count++;
120+
}
121+
122+
return count ?: -ENOENT;
123+
}

drivers/gpio/gpiolib-swnode.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
3+
#ifndef GPIOLIB_SWNODE_H
4+
#define GPIOLIB_SWNODE_H
5+
6+
struct fwnode_handle;
7+
struct gpio_desc;
8+
9+
struct gpio_desc *swnode_find_gpio(struct fwnode_handle *fwnode,
10+
const char *con_id, unsigned int idx,
11+
unsigned long *flags);
12+
int swnode_gpio_count(const struct fwnode_handle *fwnode, const char *con_id);
13+
14+
#endif /* GPIOLIB_SWNODE_H */

drivers/gpio/gpiolib.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "gpiolib.h"
2727
#include "gpiolib-of.h"
2828
#include "gpiolib-acpi.h"
29+
#include "gpiolib-swnode.h"
2930
#include "gpiolib-cdev.h"
3031
#include "gpiolib-sysfs.h"
3132

@@ -3870,6 +3871,10 @@ static struct gpio_desc *gpiod_find_by_fwnode(struct fwnode_handle *fwnode,
38703871
dev_dbg(consumer, "using ACPI '%pfw' for '%s' GPIO lookup\n",
38713872
fwnode, con_id);
38723873
desc = acpi_find_gpio(fwnode, con_id, idx, flags, lookupflags);
3874+
} else if (is_software_node(fwnode)) {
3875+
dev_dbg(consumer, "using swnode '%pfw' for '%s' GPIO lookup\n",
3876+
fwnode, con_id);
3877+
desc = swnode_find_gpio(fwnode, con_id, idx, lookupflags);
38733878
}
38743879

38753880
return desc;
@@ -3987,6 +3992,8 @@ int gpiod_count(struct device *dev, const char *con_id)
39873992
count = of_gpio_get_count(dev, con_id);
39883993
else if (is_acpi_node(fwnode))
39893994
count = acpi_gpio_count(dev, con_id);
3995+
else if (is_software_node(fwnode))
3996+
count = swnode_gpio_count(fwnode, con_id);
39903997

39913998
if (count < 0)
39923999
count = platform_gpio_count(dev, con_id);

include/linux/gpio/property.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// SPDX-License-Identifier: GPL-2.0+
2+
#ifndef __LINUX_GPIO_PROPERTY_H
3+
#define __LINUX_GPIO_PROPERTY_H
4+
5+
#include <dt-bindings/gpio/gpio.h> /* for GPIO_* flags */
6+
#include <linux/property.h>
7+
8+
#define PROPERTY_ENTRY_GPIO(_name_, _chip_node_, _idx_, _flags_) \
9+
PROPERTY_ENTRY_REF(_name_, _chip_node_, _idx_, _flags_)
10+
11+
#endif /* __LINUX_GPIO_PROPERTY_H */

0 commit comments

Comments
 (0)