Skip to content

Commit 4372257

Browse files
ConchuODlinusw
authored andcommitted
pinctrl: add generic functions + pins mapper
Add a generic function to allow creation of groups and functions at runtime based on devicetree content, before setting up mux mappings. It works similarly to pinconf_generic_dt_node_to_map(), and therefore parses pinconf properties and maps those too, allowing it to be used as the dt_node_to_map member of the pinctrl_ops struct. Signed-off-by: Conor Dooley <conor.dooley@microchip.com> Signed-off-by: Linus Walleij <linusw@kernel.org>
1 parent 95c1762 commit 4372257

4 files changed

Lines changed: 212 additions & 0 deletions

File tree

drivers/pinctrl/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ config GENERIC_PINCONF
2525
bool
2626
select PINCONF
2727

28+
config GENERIC_PINCTRL
29+
bool
30+
depends on GENERIC_PINCONF
31+
depends on GENERIC_PINCTRL_GROUPS
32+
depends on GENERIC_PINMUX_FUNCTIONS
33+
2834
config DEBUG_PINCTRL
2935
bool "Debug PINCTRL calls"
3036
depends on DEBUG_KERNEL

drivers/pinctrl/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ obj-y += core.o pinctrl-utils.o
77
obj-$(CONFIG_PINMUX) += pinmux.o
88
obj-$(CONFIG_PINCONF) += pinconf.o
99
obj-$(CONFIG_GENERIC_PINCONF) += pinconf-generic.o
10+
obj-$(CONFIG_GENERIC_PINCTRL) += pinctrl-generic.o
1011
obj-$(CONFIG_OF) += devicetree.o
1112

1213
obj-$(CONFIG_PINCTRL_AMD) += pinctrl-amd.o

drivers/pinctrl/pinconf.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,3 +160,19 @@ pinconf_generic_parse_dt_pinmux(struct device_node *np, struct device *dev,
160160
return -ENOTSUPP;
161161
}
162162
#endif
163+
164+
#if defined(CONFIG_GENERIC_PINCTRL) && defined (CONFIG_OF)
165+
int pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctldev,
166+
struct device_node *np,
167+
struct pinctrl_map **maps,
168+
unsigned int *num_maps);
169+
#else
170+
static inline int
171+
pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctldev,
172+
struct device_node *np,
173+
struct pinctrl_map **maps,
174+
unsigned int *num_maps)
175+
{
176+
return -ENOTSUPP;
177+
}
178+
#endif

drivers/pinctrl/pinctrl-generic.c

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
3+
#define pr_fmt(fmt) "generic pinconfig core: " fmt
4+
5+
#include <linux/array_size.h>
6+
#include <linux/device.h>
7+
#include <linux/module.h>
8+
#include <linux/of.h>
9+
#include <linux/slab.h>
10+
11+
#include <linux/pinctrl/pinconf-generic.h>
12+
#include <linux/pinctrl/pinconf.h>
13+
#include <linux/pinctrl/pinctrl.h>
14+
15+
#include "core.h"
16+
#include "pinconf.h"
17+
#include "pinctrl-utils.h"
18+
#include "pinmux.h"
19+
20+
static int pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_dev *pctldev,
21+
struct device_node *parent,
22+
struct device_node *np,
23+
struct pinctrl_map **maps,
24+
unsigned int *num_maps,
25+
unsigned int *num_reserved_maps,
26+
const char **group_names,
27+
unsigned int ngroups)
28+
{
29+
struct device *dev = pctldev->dev;
30+
const char **functions;
31+
const char *group_name;
32+
unsigned long *configs;
33+
unsigned int num_configs, pin, *pins;
34+
int npins, ret, reserve = 1;
35+
36+
npins = of_property_count_u32_elems(np, "pins");
37+
38+
if (npins < 1) {
39+
dev_err(dev, "invalid pinctrl group %pOFn.%pOFn %d\n",
40+
parent, np, npins);
41+
return npins;
42+
}
43+
44+
group_name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn.%pOFn", parent, np);
45+
if (!group_name)
46+
return -ENOMEM;
47+
48+
group_names[ngroups] = group_name;
49+
50+
pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL);
51+
if (!pins)
52+
return -ENOMEM;
53+
54+
functions = devm_kcalloc(dev, npins, sizeof(*functions), GFP_KERNEL);
55+
if (!functions)
56+
return -ENOMEM;
57+
58+
for (int i = 0; i < npins; i++) {
59+
ret = of_property_read_u32_index(np, "pins", i, &pin);
60+
if (ret)
61+
return ret;
62+
63+
pins[i] = pin;
64+
65+
ret = of_property_read_string(np, "function", &functions[i]);
66+
if (ret)
67+
return ret;
68+
}
69+
70+
ret = pinctrl_utils_reserve_map(pctldev, maps, num_reserved_maps, num_maps, reserve);
71+
if (ret)
72+
return ret;
73+
74+
ret = pinctrl_utils_add_map_mux(pctldev, maps, num_reserved_maps, num_maps, group_name,
75+
parent->name);
76+
if (ret < 0)
77+
return ret;
78+
79+
ret = pinctrl_generic_add_group(pctldev, group_name, pins, npins, functions);
80+
if (ret < 0)
81+
return dev_err_probe(dev, ret, "failed to add group %s: %d\n",
82+
group_name, ret);
83+
84+
ret = pinconf_generic_parse_dt_config(np, pctldev, &configs, &num_configs);
85+
if (ret)
86+
return dev_err_probe(dev, ret, "failed to parse pin config of group %s\n",
87+
group_name);
88+
89+
if (num_configs == 0)
90+
return 0;
91+
92+
ret = pinctrl_utils_reserve_map(pctldev, maps, num_reserved_maps, num_maps, reserve);
93+
if (ret)
94+
return ret;
95+
96+
ret = pinctrl_utils_add_map_configs(pctldev, maps, num_reserved_maps, num_maps, group_name,
97+
configs,
98+
num_configs, PIN_MAP_TYPE_CONFIGS_GROUP);
99+
kfree(configs);
100+
if (ret)
101+
return ret;
102+
103+
return 0;
104+
};
105+
106+
/*
107+
* For platforms that do not define groups or functions in the driver, but
108+
* instead use the devicetree to describe them. This function will, unlike
109+
* pinconf_generic_dt_node_to_map() etc which rely on driver defined groups
110+
* and functions, create them in addition to parsing pinconf properties and
111+
* adding mappings.
112+
*/
113+
int pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctldev,
114+
struct device_node *np,
115+
struct pinctrl_map **maps,
116+
unsigned int *num_maps)
117+
{
118+
struct device *dev = pctldev->dev;
119+
struct device_node *child_np;
120+
const char **group_names;
121+
unsigned int num_reserved_maps = 0;
122+
int ngroups = 0;
123+
int ret;
124+
125+
*maps = NULL;
126+
*num_maps = 0;
127+
128+
/*
129+
* Check if this is actually the pins node, or a parent containing
130+
* multiple pins nodes.
131+
*/
132+
if (!of_property_present(np, "pins"))
133+
goto parent;
134+
135+
group_names = devm_kcalloc(dev, 1, sizeof(*group_names), GFP_KERNEL);
136+
if (!group_names)
137+
return -ENOMEM;
138+
139+
ret = pinctrl_generic_pins_function_dt_subnode_to_map(pctldev, np, np,
140+
maps, num_maps,
141+
&num_reserved_maps,
142+
group_names,
143+
ngroups);
144+
if (ret) {
145+
pinctrl_utils_free_map(pctldev, *maps, *num_maps);
146+
return dev_err_probe(dev, ret, "error figuring out mappings for %s\n", np->name);
147+
}
148+
149+
ret = pinmux_generic_add_function(pctldev, np->name, group_names, 1, NULL);
150+
if (ret < 0) {
151+
pinctrl_utils_free_map(pctldev, *maps, *num_maps);
152+
return dev_err_probe(dev, ret, "error adding function %s\n", np->name);
153+
}
154+
155+
return 0;
156+
157+
parent:
158+
for_each_available_child_of_node(np, child_np)
159+
ngroups += 1;
160+
161+
group_names = devm_kcalloc(dev, ngroups, sizeof(*group_names), GFP_KERNEL);
162+
if (!group_names)
163+
return -ENOMEM;
164+
165+
ngroups = 0;
166+
for_each_available_child_of_node_scoped(np, child_np) {
167+
ret = pinctrl_generic_pins_function_dt_subnode_to_map(pctldev, np, child_np,
168+
maps, num_maps,
169+
&num_reserved_maps,
170+
group_names,
171+
ngroups);
172+
if (ret) {
173+
pinctrl_utils_free_map(pctldev, *maps, *num_maps);
174+
return dev_err_probe(dev, ret, "error figuring out mappings for %s\n",
175+
np->name);
176+
}
177+
178+
ngroups++;
179+
}
180+
181+
ret = pinmux_generic_add_function(pctldev, np->name, group_names, ngroups, NULL);
182+
if (ret < 0) {
183+
pinctrl_utils_free_map(pctldev, *maps, *num_maps);
184+
return dev_err_probe(dev, ret, "error adding function %s\n", np->name);
185+
}
186+
187+
return 0;
188+
}
189+
EXPORT_SYMBOL_GPL(pinctrl_generic_pins_function_dt_node_to_map);

0 commit comments

Comments
 (0)