1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Marvell Berlin SoC pinctrl core driver |
4 | * |
5 | * Copyright (C) 2014 Marvell Technology Group Ltd. |
6 | * |
7 | * Antoine Ténart <antoine.tenart@free-electrons.com> |
8 | */ |
9 | |
10 | #include <linux/io.h> |
11 | #include <linux/mfd/syscon.h> |
12 | #include <linux/module.h> |
13 | #include <linux/of.h> |
14 | #include <linux/of_address.h> |
15 | #include <linux/of_device.h> |
16 | #include <linux/pinctrl/pinctrl.h> |
17 | #include <linux/pinctrl/pinmux.h> |
18 | #include <linux/platform_device.h> |
19 | #include <linux/regmap.h> |
20 | #include <linux/slab.h> |
21 | |
22 | #include "../core.h" |
23 | #include "../pinctrl-utils.h" |
24 | #include "berlin.h" |
25 | |
26 | struct berlin_pinctrl { |
27 | struct regmap *regmap; |
28 | struct device *dev; |
29 | const struct berlin_pinctrl_desc *desc; |
30 | struct berlin_pinctrl_function *functions; |
31 | unsigned nfunctions; |
32 | struct pinctrl_dev *pctrl_dev; |
33 | }; |
34 | |
35 | static int berlin_pinctrl_get_group_count(struct pinctrl_dev *pctrl_dev) |
36 | { |
37 | struct berlin_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev: pctrl_dev); |
38 | |
39 | return pctrl->desc->ngroups; |
40 | } |
41 | |
42 | static const char *berlin_pinctrl_get_group_name(struct pinctrl_dev *pctrl_dev, |
43 | unsigned group) |
44 | { |
45 | struct berlin_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev: pctrl_dev); |
46 | |
47 | return pctrl->desc->groups[group].name; |
48 | } |
49 | |
50 | static int berlin_pinctrl_dt_node_to_map(struct pinctrl_dev *pctrl_dev, |
51 | struct device_node *node, |
52 | struct pinctrl_map **map, |
53 | unsigned *num_maps) |
54 | { |
55 | struct berlin_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev: pctrl_dev); |
56 | struct property *prop; |
57 | const char *function_name, *group_name; |
58 | unsigned reserved_maps = 0; |
59 | int ret, ngroups; |
60 | |
61 | *map = NULL; |
62 | *num_maps = 0; |
63 | |
64 | ret = of_property_read_string(np: node, propname: "function" , out_string: &function_name); |
65 | if (ret) { |
66 | dev_err(pctrl->dev, |
67 | "missing function property in node %pOFn\n" , node); |
68 | return -EINVAL; |
69 | } |
70 | |
71 | ngroups = of_property_count_strings(np: node, propname: "groups" ); |
72 | if (ngroups < 0) { |
73 | dev_err(pctrl->dev, |
74 | "missing groups property in node %pOFn\n" , node); |
75 | return -EINVAL; |
76 | } |
77 | |
78 | ret = pinctrl_utils_reserve_map(pctldev: pctrl_dev, map, reserved_maps: &reserved_maps, |
79 | num_maps, reserve: ngroups); |
80 | if (ret) { |
81 | dev_err(pctrl->dev, "can't reserve map: %d\n" , ret); |
82 | return ret; |
83 | } |
84 | |
85 | of_property_for_each_string(node, "groups" , prop, group_name) { |
86 | ret = pinctrl_utils_add_map_mux(pctldev: pctrl_dev, map, reserved_maps: &reserved_maps, |
87 | num_maps, group: group_name, |
88 | function: function_name); |
89 | if (ret) { |
90 | dev_err(pctrl->dev, "can't add map: %d\n" , ret); |
91 | return ret; |
92 | } |
93 | } |
94 | |
95 | return 0; |
96 | } |
97 | |
98 | static const struct pinctrl_ops berlin_pinctrl_ops = { |
99 | .get_groups_count = berlin_pinctrl_get_group_count, |
100 | .get_group_name = berlin_pinctrl_get_group_name, |
101 | .dt_node_to_map = berlin_pinctrl_dt_node_to_map, |
102 | .dt_free_map = pinctrl_utils_free_map, |
103 | }; |
104 | |
105 | static int berlin_pinmux_get_functions_count(struct pinctrl_dev *pctrl_dev) |
106 | { |
107 | struct berlin_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev: pctrl_dev); |
108 | |
109 | return pctrl->nfunctions; |
110 | } |
111 | |
112 | static const char *berlin_pinmux_get_function_name(struct pinctrl_dev *pctrl_dev, |
113 | unsigned function) |
114 | { |
115 | struct berlin_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev: pctrl_dev); |
116 | |
117 | return pctrl->functions[function].name; |
118 | } |
119 | |
120 | static int berlin_pinmux_get_function_groups(struct pinctrl_dev *pctrl_dev, |
121 | unsigned function, |
122 | const char * const **groups, |
123 | unsigned * const num_groups) |
124 | { |
125 | struct berlin_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev: pctrl_dev); |
126 | |
127 | *groups = pctrl->functions[function].groups; |
128 | *num_groups = pctrl->functions[function].ngroups; |
129 | |
130 | return 0; |
131 | } |
132 | |
133 | static struct berlin_desc_function * |
134 | berlin_pinctrl_find_function_by_name(struct berlin_pinctrl *pctrl, |
135 | const struct berlin_desc_group *group, |
136 | const char *fname) |
137 | { |
138 | struct berlin_desc_function *function = group->functions; |
139 | |
140 | while (function->name) { |
141 | if (!strcmp(function->name, fname)) |
142 | return function; |
143 | |
144 | function++; |
145 | } |
146 | |
147 | return NULL; |
148 | } |
149 | |
150 | static int berlin_pinmux_set(struct pinctrl_dev *pctrl_dev, |
151 | unsigned function, |
152 | unsigned group) |
153 | { |
154 | struct berlin_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev: pctrl_dev); |
155 | const struct berlin_desc_group *group_desc = pctrl->desc->groups + group; |
156 | struct berlin_pinctrl_function *func = pctrl->functions + function; |
157 | struct berlin_desc_function *function_desc = |
158 | berlin_pinctrl_find_function_by_name(pctrl, group: group_desc, |
159 | fname: func->name); |
160 | u32 mask, val; |
161 | |
162 | if (!function_desc) |
163 | return -EINVAL; |
164 | |
165 | mask = GENMASK(group_desc->lsb + group_desc->bit_width - 1, |
166 | group_desc->lsb); |
167 | val = function_desc->muxval << group_desc->lsb; |
168 | regmap_update_bits(map: pctrl->regmap, reg: group_desc->offset, mask, val); |
169 | |
170 | return 0; |
171 | } |
172 | |
173 | static const struct pinmux_ops berlin_pinmux_ops = { |
174 | .get_functions_count = &berlin_pinmux_get_functions_count, |
175 | .get_function_name = &berlin_pinmux_get_function_name, |
176 | .get_function_groups = &berlin_pinmux_get_function_groups, |
177 | .set_mux = &berlin_pinmux_set, |
178 | }; |
179 | |
180 | static int berlin_pinctrl_add_function(struct berlin_pinctrl *pctrl, |
181 | const char *name) |
182 | { |
183 | struct berlin_pinctrl_function *function = pctrl->functions; |
184 | |
185 | while (function->name) { |
186 | if (!strcmp(function->name, name)) { |
187 | function->ngroups++; |
188 | return -EEXIST; |
189 | } |
190 | function++; |
191 | } |
192 | |
193 | function->name = name; |
194 | function->ngroups = 1; |
195 | |
196 | pctrl->nfunctions++; |
197 | |
198 | return 0; |
199 | } |
200 | |
201 | static int berlin_pinctrl_build_state(struct platform_device *pdev) |
202 | { |
203 | struct berlin_pinctrl *pctrl = platform_get_drvdata(pdev); |
204 | const struct berlin_desc_group *desc_group; |
205 | const struct berlin_desc_function *desc_function; |
206 | int i, max_functions = 0; |
207 | |
208 | pctrl->nfunctions = 0; |
209 | |
210 | for (i = 0; i < pctrl->desc->ngroups; i++) { |
211 | desc_group = pctrl->desc->groups + i; |
212 | /* compute the maximum number of functions a group can have */ |
213 | max_functions += 1 << (desc_group->bit_width + 1); |
214 | } |
215 | |
216 | /* we will reallocate later */ |
217 | pctrl->functions = kcalloc(n: max_functions, |
218 | size: sizeof(*pctrl->functions), GFP_KERNEL); |
219 | if (!pctrl->functions) |
220 | return -ENOMEM; |
221 | |
222 | /* register all functions */ |
223 | for (i = 0; i < pctrl->desc->ngroups; i++) { |
224 | desc_group = pctrl->desc->groups + i; |
225 | desc_function = desc_group->functions; |
226 | |
227 | while (desc_function->name) { |
228 | berlin_pinctrl_add_function(pctrl, name: desc_function->name); |
229 | desc_function++; |
230 | } |
231 | } |
232 | |
233 | pctrl->functions = krealloc(objp: pctrl->functions, |
234 | new_size: pctrl->nfunctions * sizeof(*pctrl->functions), |
235 | GFP_KERNEL); |
236 | if (!pctrl->functions) |
237 | return -ENOMEM; |
238 | |
239 | /* map functions to theirs groups */ |
240 | for (i = 0; i < pctrl->desc->ngroups; i++) { |
241 | desc_group = pctrl->desc->groups + i; |
242 | desc_function = desc_group->functions; |
243 | |
244 | while (desc_function->name) { |
245 | struct berlin_pinctrl_function |
246 | *function = pctrl->functions; |
247 | const char **groups; |
248 | bool found = false; |
249 | |
250 | while (function->name) { |
251 | if (!strcmp(desc_function->name, function->name)) { |
252 | found = true; |
253 | break; |
254 | } |
255 | function++; |
256 | } |
257 | |
258 | if (!found) { |
259 | kfree(objp: pctrl->functions); |
260 | return -EINVAL; |
261 | } |
262 | |
263 | if (!function->groups) { |
264 | function->groups = |
265 | devm_kcalloc(dev: &pdev->dev, |
266 | n: function->ngroups, |
267 | size: sizeof(char *), |
268 | GFP_KERNEL); |
269 | |
270 | if (!function->groups) { |
271 | kfree(objp: pctrl->functions); |
272 | return -ENOMEM; |
273 | } |
274 | } |
275 | |
276 | groups = function->groups; |
277 | while (*groups) |
278 | groups++; |
279 | |
280 | *groups = desc_group->name; |
281 | |
282 | desc_function++; |
283 | } |
284 | } |
285 | |
286 | return 0; |
287 | } |
288 | |
289 | static struct pinctrl_desc berlin_pctrl_desc = { |
290 | .name = "berlin-pinctrl" , |
291 | .pctlops = &berlin_pinctrl_ops, |
292 | .pmxops = &berlin_pinmux_ops, |
293 | .owner = THIS_MODULE, |
294 | }; |
295 | |
296 | int berlin_pinctrl_probe_regmap(struct platform_device *pdev, |
297 | const struct berlin_pinctrl_desc *desc, |
298 | struct regmap *regmap) |
299 | { |
300 | struct device *dev = &pdev->dev; |
301 | struct berlin_pinctrl *pctrl; |
302 | int ret; |
303 | |
304 | pctrl = devm_kzalloc(dev, size: sizeof(*pctrl), GFP_KERNEL); |
305 | if (!pctrl) |
306 | return -ENOMEM; |
307 | |
308 | platform_set_drvdata(pdev, data: pctrl); |
309 | |
310 | pctrl->regmap = regmap; |
311 | pctrl->dev = &pdev->dev; |
312 | pctrl->desc = desc; |
313 | |
314 | ret = berlin_pinctrl_build_state(pdev); |
315 | if (ret) { |
316 | dev_err(dev, "cannot build driver state: %d\n" , ret); |
317 | return ret; |
318 | } |
319 | |
320 | pctrl->pctrl_dev = devm_pinctrl_register(dev, pctldesc: &berlin_pctrl_desc, |
321 | driver_data: pctrl); |
322 | if (IS_ERR(ptr: pctrl->pctrl_dev)) { |
323 | dev_err(dev, "failed to register pinctrl driver\n" ); |
324 | return PTR_ERR(ptr: pctrl->pctrl_dev); |
325 | } |
326 | |
327 | return 0; |
328 | } |
329 | |
330 | int berlin_pinctrl_probe(struct platform_device *pdev, |
331 | const struct berlin_pinctrl_desc *desc) |
332 | { |
333 | struct device *dev = &pdev->dev; |
334 | struct device_node *parent_np = of_get_parent(node: dev->of_node); |
335 | struct regmap *regmap = syscon_node_to_regmap(np: parent_np); |
336 | |
337 | of_node_put(node: parent_np); |
338 | if (IS_ERR(ptr: regmap)) |
339 | return PTR_ERR(ptr: regmap); |
340 | |
341 | return berlin_pinctrl_probe_regmap(pdev, desc, regmap); |
342 | } |
343 | |