Commit | Line | Data |
---|---|---|
ca5481c6 PM |
1 | /* |
2 | * SuperH Pin Function Controller pinmux support. | |
3 | * | |
4 | * Copyright (C) 2012 Paul Mundt | |
5 | * | |
6 | * This file is subject to the terms and conditions of the GNU General Public | |
7 | * License. See the file "COPYING" in the main directory of this archive | |
8 | * for more details. | |
9 | */ | |
54407110 | 10 | |
c6193eac | 11 | #define DRV_NAME "sh-pfc" |
ca5481c6 | 12 | |
1724acfd | 13 | #include <linux/device.h> |
90efde22 | 14 | #include <linux/err.h> |
ca5481c6 PM |
15 | #include <linux/init.h> |
16 | #include <linux/module.h> | |
fe1c9a82 | 17 | #include <linux/of.h> |
ca5481c6 | 18 | #include <linux/pinctrl/consumer.h> |
fe1c9a82 | 19 | #include <linux/pinctrl/machine.h> |
ca5481c6 | 20 | #include <linux/pinctrl/pinconf.h> |
ca5481c6 | 21 | #include <linux/pinctrl/pinconf-generic.h> |
90efde22 LP |
22 | #include <linux/pinctrl/pinctrl.h> |
23 | #include <linux/pinctrl/pinmux.h> | |
90efde22 LP |
24 | #include <linux/slab.h> |
25 | #include <linux/spinlock.h> | |
ca5481c6 | 26 | |
f9165132 | 27 | #include "core.h" |
c58d9c1b LP |
28 | #include "../core.h" |
29 | #include "../pinconf.h" | |
f9165132 | 30 | |
1a0039dc LP |
31 | struct sh_pfc_pin_config { |
32 | u32 type; | |
33 | }; | |
34 | ||
ca5481c6 PM |
35 | struct sh_pfc_pinctrl { |
36 | struct pinctrl_dev *pctl; | |
dcc427e1 | 37 | struct pinctrl_desc pctl_desc; |
dcc427e1 | 38 | |
ca5481c6 PM |
39 | struct sh_pfc *pfc; |
40 | ||
3d8d9f1d | 41 | struct pinctrl_pin_desc *pins; |
1a0039dc | 42 | struct sh_pfc_pin_config *configs; |
ca5481c6 PM |
43 | }; |
44 | ||
e3f805e8 | 45 | static int sh_pfc_get_groups_count(struct pinctrl_dev *pctldev) |
ca5481c6 | 46 | { |
e3f805e8 PM |
47 | struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); |
48 | ||
3d8d9f1d | 49 | return pmx->pfc->info->nr_groups; |
ca5481c6 PM |
50 | } |
51 | ||
e3f805e8 | 52 | static const char *sh_pfc_get_group_name(struct pinctrl_dev *pctldev, |
ca5481c6 PM |
53 | unsigned selector) |
54 | { | |
e3f805e8 PM |
55 | struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); |
56 | ||
3d8d9f1d | 57 | return pmx->pfc->info->groups[selector].name; |
ca5481c6 PM |
58 | } |
59 | ||
3d8d9f1d | 60 | static int sh_pfc_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector, |
ca5481c6 PM |
61 | const unsigned **pins, unsigned *num_pins) |
62 | { | |
e3f805e8 PM |
63 | struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); |
64 | ||
3d8d9f1d LP |
65 | *pins = pmx->pfc->info->groups[selector].pins; |
66 | *num_pins = pmx->pfc->info->groups[selector].nr_pins; | |
e3f805e8 PM |
67 | |
68 | return 0; | |
ca5481c6 PM |
69 | } |
70 | ||
fdd85ec3 PM |
71 | static void sh_pfc_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, |
72 | unsigned offset) | |
73 | { | |
74 | seq_printf(s, "%s", DRV_NAME); | |
75 | } | |
76 | ||
3a8d63d4 | 77 | #ifdef CONFIG_OF |
12f3ad8d LP |
78 | static int sh_pfc_map_add_config(struct pinctrl_map *map, |
79 | const char *group_or_pin, | |
80 | enum pinctrl_map_type type, | |
81 | unsigned long *configs, | |
82 | unsigned int num_configs) | |
83 | { | |
84 | unsigned long *cfgs; | |
85 | ||
86 | cfgs = kmemdup(configs, num_configs * sizeof(*cfgs), | |
87 | GFP_KERNEL); | |
88 | if (cfgs == NULL) | |
89 | return -ENOMEM; | |
90 | ||
91 | map->type = type; | |
92 | map->data.configs.group_or_pin = group_or_pin; | |
93 | map->data.configs.configs = cfgs; | |
94 | map->data.configs.num_configs = num_configs; | |
95 | ||
96 | return 0; | |
97 | } | |
98 | ||
fe1c9a82 LP |
99 | static int sh_pfc_dt_subnode_to_map(struct device *dev, struct device_node *np, |
100 | struct pinctrl_map **map, | |
101 | unsigned int *num_maps, unsigned int *index) | |
102 | { | |
103 | struct pinctrl_map *maps = *map; | |
104 | unsigned int nmaps = *num_maps; | |
105 | unsigned int idx = *index; | |
12f3ad8d | 106 | unsigned int num_configs; |
fe1c9a82 | 107 | const char *function = NULL; |
12f3ad8d | 108 | unsigned long *configs; |
fe1c9a82 | 109 | struct property *prop; |
12f3ad8d LP |
110 | unsigned int num_groups; |
111 | unsigned int num_pins; | |
fe1c9a82 | 112 | const char *group; |
12f3ad8d | 113 | const char *pin; |
fe1c9a82 LP |
114 | int ret; |
115 | ||
116 | /* Parse the function and configuration properties. At least a function | |
117 | * or one configuration must be specified. | |
118 | */ | |
119 | ret = of_property_read_string(np, "renesas,function", &function); | |
120 | if (ret < 0 && ret != -EINVAL) { | |
121 | dev_err(dev, "Invalid function in DT\n"); | |
122 | return ret; | |
123 | } | |
124 | ||
12f3ad8d LP |
125 | ret = pinconf_generic_parse_dt_config(np, &configs, &num_configs); |
126 | if (ret < 0) | |
127 | return ret; | |
128 | ||
129 | if (!function && num_configs == 0) { | |
130 | dev_err(dev, | |
131 | "DT node must contain at least a function or config\n"); | |
fe1c9a82 LP |
132 | goto done; |
133 | } | |
134 | ||
12f3ad8d LP |
135 | /* Count the number of pins and groups and reallocate mappings. */ |
136 | ret = of_property_count_strings(np, "renesas,pins"); | |
137 | if (ret == -EINVAL) { | |
138 | num_pins = 0; | |
139 | } else if (ret < 0) { | |
140 | dev_err(dev, "Invalid pins list in DT\n"); | |
141 | goto done; | |
142 | } else { | |
143 | num_pins = ret; | |
144 | } | |
145 | ||
fe1c9a82 | 146 | ret = of_property_count_strings(np, "renesas,groups"); |
12f3ad8d LP |
147 | if (ret == -EINVAL) { |
148 | num_groups = 0; | |
149 | } else if (ret < 0) { | |
fe1c9a82 LP |
150 | dev_err(dev, "Invalid pin groups list in DT\n"); |
151 | goto done; | |
12f3ad8d LP |
152 | } else { |
153 | num_groups = ret; | |
fe1c9a82 LP |
154 | } |
155 | ||
12f3ad8d LP |
156 | if (!num_pins && !num_groups) { |
157 | dev_err(dev, "No pin or group provided in DT node\n"); | |
fe1c9a82 LP |
158 | ret = -ENODEV; |
159 | goto done; | |
160 | } | |
161 | ||
12f3ad8d LP |
162 | if (function) |
163 | nmaps += num_groups; | |
164 | if (configs) | |
165 | nmaps += num_pins + num_groups; | |
fe1c9a82 LP |
166 | |
167 | maps = krealloc(maps, sizeof(*maps) * nmaps, GFP_KERNEL); | |
168 | if (maps == NULL) { | |
169 | ret = -ENOMEM; | |
170 | goto done; | |
171 | } | |
172 | ||
173 | *map = maps; | |
174 | *num_maps = nmaps; | |
175 | ||
176 | /* Iterate over pins and groups and create the mappings. */ | |
177 | of_property_for_each_string(np, "renesas,groups", prop, group) { | |
12f3ad8d LP |
178 | if (function) { |
179 | maps[idx].type = PIN_MAP_TYPE_MUX_GROUP; | |
180 | maps[idx].data.mux.group = group; | |
181 | maps[idx].data.mux.function = function; | |
182 | idx++; | |
183 | } | |
184 | ||
185 | if (configs) { | |
186 | ret = sh_pfc_map_add_config(&maps[idx], group, | |
187 | PIN_MAP_TYPE_CONFIGS_GROUP, | |
188 | configs, num_configs); | |
189 | if (ret < 0) | |
190 | goto done; | |
191 | ||
192 | idx++; | |
193 | } | |
fe1c9a82 LP |
194 | } |
195 | ||
12f3ad8d LP |
196 | if (!configs) { |
197 | ret = 0; | |
198 | goto done; | |
199 | } | |
200 | ||
201 | of_property_for_each_string(np, "renesas,pins", prop, pin) { | |
202 | ret = sh_pfc_map_add_config(&maps[idx], pin, | |
203 | PIN_MAP_TYPE_CONFIGS_PIN, | |
204 | configs, num_configs); | |
205 | if (ret < 0) | |
206 | goto done; | |
207 | ||
208 | idx++; | |
209 | } | |
fe1c9a82 LP |
210 | |
211 | done: | |
212 | *index = idx; | |
12f3ad8d | 213 | kfree(configs); |
fe1c9a82 LP |
214 | return ret; |
215 | } | |
216 | ||
217 | static void sh_pfc_dt_free_map(struct pinctrl_dev *pctldev, | |
218 | struct pinctrl_map *map, unsigned num_maps) | |
219 | { | |
12f3ad8d LP |
220 | unsigned int i; |
221 | ||
222 | if (map == NULL) | |
223 | return; | |
224 | ||
225 | for (i = 0; i < num_maps; ++i) { | |
226 | if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP || | |
227 | map[i].type == PIN_MAP_TYPE_CONFIGS_PIN) | |
228 | kfree(map[i].data.configs.configs); | |
229 | } | |
230 | ||
fe1c9a82 LP |
231 | kfree(map); |
232 | } | |
233 | ||
234 | static int sh_pfc_dt_node_to_map(struct pinctrl_dev *pctldev, | |
235 | struct device_node *np, | |
236 | struct pinctrl_map **map, unsigned *num_maps) | |
237 | { | |
238 | struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | |
239 | struct device *dev = pmx->pfc->dev; | |
240 | struct device_node *child; | |
241 | unsigned int index; | |
242 | int ret; | |
243 | ||
244 | *map = NULL; | |
245 | *num_maps = 0; | |
246 | index = 0; | |
247 | ||
248 | for_each_child_of_node(np, child) { | |
249 | ret = sh_pfc_dt_subnode_to_map(dev, child, map, num_maps, | |
250 | &index); | |
251 | if (ret < 0) | |
252 | goto done; | |
253 | } | |
254 | ||
255 | /* If no mapping has been found in child nodes try the config node. */ | |
256 | if (*num_maps == 0) { | |
257 | ret = sh_pfc_dt_subnode_to_map(dev, np, map, num_maps, &index); | |
258 | if (ret < 0) | |
259 | goto done; | |
260 | } | |
261 | ||
262 | if (*num_maps) | |
263 | return 0; | |
264 | ||
265 | dev_err(dev, "no mapping found in node %s\n", np->full_name); | |
266 | ret = -EINVAL; | |
267 | ||
268 | done: | |
269 | if (ret < 0) | |
270 | sh_pfc_dt_free_map(pctldev, *map, *num_maps); | |
271 | ||
272 | return ret; | |
273 | } | |
3a8d63d4 | 274 | #endif /* CONFIG_OF */ |
fe1c9a82 | 275 | |
fe330ce8 | 276 | static const struct pinctrl_ops sh_pfc_pinctrl_ops = { |
e3f805e8 PM |
277 | .get_groups_count = sh_pfc_get_groups_count, |
278 | .get_group_name = sh_pfc_get_group_name, | |
ca5481c6 | 279 | .get_group_pins = sh_pfc_get_group_pins, |
fdd85ec3 | 280 | .pin_dbg_show = sh_pfc_pin_dbg_show, |
3a8d63d4 | 281 | #ifdef CONFIG_OF |
fe1c9a82 LP |
282 | .dt_node_to_map = sh_pfc_dt_node_to_map, |
283 | .dt_free_map = sh_pfc_dt_free_map, | |
3a8d63d4 | 284 | #endif |
ca5481c6 PM |
285 | }; |
286 | ||
d93a891f PM |
287 | static int sh_pfc_get_functions_count(struct pinctrl_dev *pctldev) |
288 | { | |
289 | struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | |
290 | ||
3d8d9f1d | 291 | return pmx->pfc->info->nr_functions; |
d93a891f PM |
292 | } |
293 | ||
294 | static const char *sh_pfc_get_function_name(struct pinctrl_dev *pctldev, | |
295 | unsigned selector) | |
296 | { | |
297 | struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | |
298 | ||
3d8d9f1d | 299 | return pmx->pfc->info->functions[selector].name; |
d93a891f | 300 | } |
ca5481c6 | 301 | |
3d8d9f1d LP |
302 | static int sh_pfc_get_function_groups(struct pinctrl_dev *pctldev, |
303 | unsigned selector, | |
ca5481c6 PM |
304 | const char * const **groups, |
305 | unsigned * const num_groups) | |
306 | { | |
d93a891f PM |
307 | struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); |
308 | ||
3d8d9f1d LP |
309 | *groups = pmx->pfc->info->functions[selector].groups; |
310 | *num_groups = pmx->pfc->info->functions[selector].nr_groups; | |
d93a891f | 311 | |
ca5481c6 PM |
312 | return 0; |
313 | } | |
314 | ||
3d8d9f1d | 315 | static int sh_pfc_func_enable(struct pinctrl_dev *pctldev, unsigned selector, |
ca5481c6 PM |
316 | unsigned group) |
317 | { | |
3d8d9f1d LP |
318 | struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); |
319 | struct sh_pfc *pfc = pmx->pfc; | |
320 | const struct sh_pfc_pin_group *grp = &pfc->info->groups[group]; | |
321 | unsigned long flags; | |
322 | unsigned int i; | |
b705c054 | 323 | int ret = 0; |
3d8d9f1d LP |
324 | |
325 | spin_lock_irqsave(&pfc->lock, flags); | |
326 | ||
9fddc4a5 LP |
327 | for (i = 0; i < grp->nr_pins; ++i) { |
328 | int idx = sh_pfc_get_pin_index(pfc, grp->pins[i]); | |
329 | struct sh_pfc_pin_config *cfg = &pmx->configs[idx]; | |
330 | ||
331 | if (cfg->type != PINMUX_TYPE_NONE) { | |
332 | ret = -EBUSY; | |
333 | goto done; | |
334 | } | |
335 | } | |
336 | ||
3d8d9f1d | 337 | for (i = 0; i < grp->nr_pins; ++i) { |
b705c054 LP |
338 | ret = sh_pfc_config_mux(pfc, grp->mux[i], PINMUX_TYPE_FUNCTION); |
339 | if (ret < 0) | |
340 | break; | |
3d8d9f1d LP |
341 | } |
342 | ||
9fddc4a5 | 343 | done: |
3d8d9f1d LP |
344 | spin_unlock_irqrestore(&pfc->lock, flags); |
345 | return ret; | |
ca5481c6 PM |
346 | } |
347 | ||
3d8d9f1d | 348 | static void sh_pfc_func_disable(struct pinctrl_dev *pctldev, unsigned selector, |
ca5481c6 PM |
349 | unsigned group) |
350 | { | |
9fddc4a5 LP |
351 | struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); |
352 | struct sh_pfc *pfc = pmx->pfc; | |
353 | const struct sh_pfc_pin_group *grp = &pfc->info->groups[group]; | |
354 | unsigned long flags; | |
355 | unsigned int i; | |
356 | ||
357 | spin_lock_irqsave(&pfc->lock, flags); | |
358 | ||
359 | for (i = 0; i < grp->nr_pins; ++i) { | |
360 | int idx = sh_pfc_get_pin_index(pfc, grp->pins[i]); | |
361 | struct sh_pfc_pin_config *cfg = &pmx->configs[idx]; | |
362 | ||
363 | cfg->type = PINMUX_TYPE_NONE; | |
364 | } | |
365 | ||
366 | spin_unlock_irqrestore(&pfc->lock, flags); | |
ca5481c6 PM |
367 | } |
368 | ||
369 | static int sh_pfc_gpio_request_enable(struct pinctrl_dev *pctldev, | |
370 | struct pinctrl_gpio_range *range, | |
371 | unsigned offset) | |
372 | { | |
373 | struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | |
374 | struct sh_pfc *pfc = pmx->pfc; | |
1a0039dc LP |
375 | int idx = sh_pfc_get_pin_index(pfc, offset); |
376 | struct sh_pfc_pin_config *cfg = &pmx->configs[idx]; | |
ca5481c6 | 377 | unsigned long flags; |
1a0039dc | 378 | int ret; |
ca5481c6 PM |
379 | |
380 | spin_lock_irqsave(&pfc->lock, flags); | |
381 | ||
9fddc4a5 | 382 | if (cfg->type != PINMUX_TYPE_NONE) { |
9a643c9a LP |
383 | dev_err(pfc->dev, |
384 | "Pin %u is busy, can't configure it as GPIO.\n", | |
385 | offset); | |
9fddc4a5 LP |
386 | ret = -EBUSY; |
387 | goto done; | |
d93a891f | 388 | } |
ca5481c6 | 389 | |
e3c47051 LP |
390 | if (!pfc->gpio) { |
391 | /* If GPIOs are handled externally the pin mux type need to be | |
392 | * set to GPIO here. | |
393 | */ | |
394 | const struct sh_pfc_pin *pin = &pfc->info->pins[idx]; | |
395 | ||
396 | ret = sh_pfc_config_mux(pfc, pin->enum_id, PINMUX_TYPE_GPIO); | |
397 | if (ret < 0) | |
398 | goto done; | |
399 | } | |
400 | ||
9fddc4a5 LP |
401 | cfg->type = PINMUX_TYPE_GPIO; |
402 | ||
ca5481c6 PM |
403 | ret = 0; |
404 | ||
9fddc4a5 | 405 | done: |
ca5481c6 PM |
406 | spin_unlock_irqrestore(&pfc->lock, flags); |
407 | ||
408 | return ret; | |
409 | } | |
410 | ||
411 | static void sh_pfc_gpio_disable_free(struct pinctrl_dev *pctldev, | |
412 | struct pinctrl_gpio_range *range, | |
413 | unsigned offset) | |
414 | { | |
9fddc4a5 LP |
415 | struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); |
416 | struct sh_pfc *pfc = pmx->pfc; | |
417 | int idx = sh_pfc_get_pin_index(pfc, offset); | |
418 | struct sh_pfc_pin_config *cfg = &pmx->configs[idx]; | |
419 | unsigned long flags; | |
420 | ||
421 | spin_lock_irqsave(&pfc->lock, flags); | |
422 | cfg->type = PINMUX_TYPE_NONE; | |
423 | spin_unlock_irqrestore(&pfc->lock, flags); | |
ca5481c6 PM |
424 | } |
425 | ||
426 | static int sh_pfc_gpio_set_direction(struct pinctrl_dev *pctldev, | |
427 | struct pinctrl_gpio_range *range, | |
428 | unsigned offset, bool input) | |
429 | { | |
430 | struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | |
0d00f00a LP |
431 | struct sh_pfc *pfc = pmx->pfc; |
432 | int new_type = input ? PINMUX_TYPE_INPUT : PINMUX_TYPE_OUTPUT; | |
433 | int idx = sh_pfc_get_pin_index(pfc, offset); | |
0d00f00a | 434 | const struct sh_pfc_pin *pin = &pfc->info->pins[idx]; |
9fddc4a5 | 435 | struct sh_pfc_pin_config *cfg = &pmx->configs[idx]; |
0d00f00a | 436 | unsigned long flags; |
6dc9b455 | 437 | unsigned int dir; |
0d00f00a | 438 | int ret; |
ca5481c6 | 439 | |
6dc9b455 LP |
440 | /* Check if the requested direction is supported by the pin. Not all SoC |
441 | * provide pin config data, so perform the check conditionally. | |
442 | */ | |
443 | if (pin->configs) { | |
444 | dir = input ? SH_PFC_PIN_CFG_INPUT : SH_PFC_PIN_CFG_OUTPUT; | |
445 | if (!(pin->configs & dir)) | |
446 | return -EINVAL; | |
447 | } | |
448 | ||
0d00f00a LP |
449 | spin_lock_irqsave(&pfc->lock, flags); |
450 | ||
9fddc4a5 | 451 | ret = sh_pfc_config_mux(pfc, pin->enum_id, new_type); |
0d00f00a LP |
452 | if (ret < 0) |
453 | goto done; | |
454 | ||
455 | cfg->type = new_type; | |
456 | ||
457 | done: | |
458 | spin_unlock_irqrestore(&pfc->lock, flags); | |
0d00f00a | 459 | return ret; |
ca5481c6 PM |
460 | } |
461 | ||
fe330ce8 | 462 | static const struct pinmux_ops sh_pfc_pinmux_ops = { |
d93a891f PM |
463 | .get_functions_count = sh_pfc_get_functions_count, |
464 | .get_function_name = sh_pfc_get_function_name, | |
ca5481c6 | 465 | .get_function_groups = sh_pfc_get_function_groups, |
3d8d9f1d LP |
466 | .enable = sh_pfc_func_enable, |
467 | .disable = sh_pfc_func_disable, | |
ca5481c6 PM |
468 | .gpio_request_enable = sh_pfc_gpio_request_enable, |
469 | .gpio_disable_free = sh_pfc_gpio_disable_free, | |
470 | .gpio_set_direction = sh_pfc_gpio_set_direction, | |
471 | }; | |
472 | ||
c58d9c1b LP |
473 | /* Check whether the requested parameter is supported for a pin. */ |
474 | static bool sh_pfc_pinconf_validate(struct sh_pfc *pfc, unsigned int _pin, | |
475 | enum pin_config_param param) | |
476 | { | |
477 | int idx = sh_pfc_get_pin_index(pfc, _pin); | |
478 | const struct sh_pfc_pin *pin = &pfc->info->pins[idx]; | |
479 | ||
480 | switch (param) { | |
481 | case PIN_CONFIG_BIAS_DISABLE: | |
482 | return true; | |
483 | ||
484 | case PIN_CONFIG_BIAS_PULL_UP: | |
485 | return pin->configs & SH_PFC_PIN_CFG_PULL_UP; | |
486 | ||
487 | case PIN_CONFIG_BIAS_PULL_DOWN: | |
488 | return pin->configs & SH_PFC_PIN_CFG_PULL_DOWN; | |
489 | ||
490 | default: | |
491 | return false; | |
492 | } | |
493 | } | |
494 | ||
934cb02b | 495 | static int sh_pfc_pinconf_get(struct pinctrl_dev *pctldev, unsigned _pin, |
ca5481c6 PM |
496 | unsigned long *config) |
497 | { | |
fdd85ec3 PM |
498 | struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); |
499 | struct sh_pfc *pfc = pmx->pfc; | |
c58d9c1b LP |
500 | enum pin_config_param param = pinconf_to_config_param(*config); |
501 | unsigned long flags; | |
502 | unsigned int bias; | |
503 | ||
504 | if (!sh_pfc_pinconf_validate(pfc, _pin, param)) | |
505 | return -ENOTSUPP; | |
506 | ||
507 | switch (param) { | |
508 | case PIN_CONFIG_BIAS_DISABLE: | |
509 | case PIN_CONFIG_BIAS_PULL_UP: | |
510 | case PIN_CONFIG_BIAS_PULL_DOWN: | |
511 | if (!pfc->info->ops || !pfc->info->ops->get_bias) | |
512 | return -ENOTSUPP; | |
513 | ||
514 | spin_lock_irqsave(&pfc->lock, flags); | |
515 | bias = pfc->info->ops->get_bias(pfc, _pin); | |
516 | spin_unlock_irqrestore(&pfc->lock, flags); | |
517 | ||
518 | if (bias != param) | |
519 | return -EINVAL; | |
520 | ||
521 | *config = 0; | |
522 | break; | |
d93a891f | 523 | |
c58d9c1b LP |
524 | default: |
525 | return -ENOTSUPP; | |
526 | } | |
d93a891f | 527 | |
fdd85ec3 | 528 | return 0; |
ca5481c6 PM |
529 | } |
530 | ||
c58d9c1b | 531 | static int sh_pfc_pinconf_set(struct pinctrl_dev *pctldev, unsigned _pin, |
03b054e9 | 532 | unsigned long *configs, unsigned num_configs) |
ca5481c6 | 533 | { |
fdd85ec3 | 534 | struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); |
c58d9c1b | 535 | struct sh_pfc *pfc = pmx->pfc; |
03b054e9 | 536 | enum pin_config_param param; |
c58d9c1b | 537 | unsigned long flags; |
03b054e9 | 538 | unsigned int i; |
fdd85ec3 | 539 | |
03b054e9 SY |
540 | for (i = 0; i < num_configs; i++) { |
541 | param = pinconf_to_config_param(configs[i]); | |
fdd85ec3 | 542 | |
03b054e9 | 543 | if (!sh_pfc_pinconf_validate(pfc, _pin, param)) |
c58d9c1b LP |
544 | return -ENOTSUPP; |
545 | ||
03b054e9 SY |
546 | switch (param) { |
547 | case PIN_CONFIG_BIAS_PULL_UP: | |
548 | case PIN_CONFIG_BIAS_PULL_DOWN: | |
549 | case PIN_CONFIG_BIAS_DISABLE: | |
550 | if (!pfc->info->ops || !pfc->info->ops->set_bias) | |
551 | return -ENOTSUPP; | |
c58d9c1b | 552 | |
03b054e9 SY |
553 | spin_lock_irqsave(&pfc->lock, flags); |
554 | pfc->info->ops->set_bias(pfc, _pin, param); | |
555 | spin_unlock_irqrestore(&pfc->lock, flags); | |
c58d9c1b | 556 | |
03b054e9 SY |
557 | break; |
558 | ||
559 | default: | |
560 | return -ENOTSUPP; | |
561 | } | |
562 | } /* for each config */ | |
c58d9c1b LP |
563 | |
564 | return 0; | |
fdd85ec3 PM |
565 | } |
566 | ||
c58d9c1b | 567 | static int sh_pfc_pinconf_group_set(struct pinctrl_dev *pctldev, unsigned group, |
03b054e9 SY |
568 | unsigned long *configs, |
569 | unsigned num_configs) | |
fdd85ec3 | 570 | { |
c58d9c1b LP |
571 | struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); |
572 | const unsigned int *pins; | |
573 | unsigned int num_pins; | |
574 | unsigned int i; | |
575 | ||
576 | pins = pmx->pfc->info->groups[group].pins; | |
577 | num_pins = pmx->pfc->info->groups[group].nr_pins; | |
578 | ||
579 | for (i = 0; i < num_pins; ++i) | |
03b054e9 | 580 | sh_pfc_pinconf_set(pctldev, pins[i], configs, num_configs); |
c58d9c1b LP |
581 | |
582 | return 0; | |
ca5481c6 PM |
583 | } |
584 | ||
fe330ce8 | 585 | static const struct pinconf_ops sh_pfc_pinconf_ops = { |
c58d9c1b LP |
586 | .is_generic = true, |
587 | .pin_config_get = sh_pfc_pinconf_get, | |
588 | .pin_config_set = sh_pfc_pinconf_set, | |
589 | .pin_config_group_set = sh_pfc_pinconf_group_set, | |
590 | .pin_config_config_dbg_show = pinconf_generic_dump_config, | |
ca5481c6 PM |
591 | }; |
592 | ||
63d57383 LP |
593 | /* PFC ranges -> pinctrl pin descs */ |
594 | static int sh_pfc_map_pins(struct sh_pfc *pfc, struct sh_pfc_pinctrl *pmx) | |
ca5481c6 | 595 | { |
63d57383 LP |
596 | unsigned int i; |
597 | ||
acac8ed5 | 598 | /* Allocate and initialize the pins and configs arrays. */ |
3d8d9f1d LP |
599 | pmx->pins = devm_kzalloc(pfc->dev, |
600 | sizeof(*pmx->pins) * pfc->info->nr_pins, | |
1724acfd | 601 | GFP_KERNEL); |
3d8d9f1d | 602 | if (unlikely(!pmx->pins)) |
ca5481c6 | 603 | return -ENOMEM; |
ca5481c6 | 604 | |
1a0039dc LP |
605 | pmx->configs = devm_kzalloc(pfc->dev, |
606 | sizeof(*pmx->configs) * pfc->info->nr_pins, | |
607 | GFP_KERNEL); | |
608 | if (unlikely(!pmx->configs)) | |
609 | return -ENOMEM; | |
610 | ||
acac8ed5 LP |
611 | for (i = 0; i < pfc->info->nr_pins; ++i) { |
612 | const struct sh_pfc_pin *info = &pfc->info->pins[i]; | |
613 | struct sh_pfc_pin_config *cfg = &pmx->configs[i]; | |
614 | struct pinctrl_pin_desc *pin = &pmx->pins[i]; | |
63d57383 | 615 | |
acac8ed5 LP |
616 | /* If the pin number is equal to -1 all pins are considered */ |
617 | pin->number = info->pin != (u16)-1 ? info->pin : i; | |
618 | pin->name = info->name; | |
619 | cfg->type = PINMUX_TYPE_NONE; | |
ca5481c6 PM |
620 | } |
621 | ||
acac8ed5 | 622 | return 0; |
ca5481c6 PM |
623 | } |
624 | ||
c6193eac | 625 | int sh_pfc_register_pinctrl(struct sh_pfc *pfc) |
ca5481c6 | 626 | { |
c6193eac | 627 | struct sh_pfc_pinctrl *pmx; |
acac8ed5 | 628 | int ret; |
ca5481c6 | 629 | |
1724acfd | 630 | pmx = devm_kzalloc(pfc->dev, sizeof(*pmx), GFP_KERNEL); |
c6193eac LP |
631 | if (unlikely(!pmx)) |
632 | return -ENOMEM; | |
633 | ||
c6193eac LP |
634 | pmx->pfc = pfc; |
635 | pfc->pinctrl = pmx; | |
ca5481c6 | 636 | |
acac8ed5 LP |
637 | ret = sh_pfc_map_pins(pfc, pmx); |
638 | if (ret < 0) | |
639 | return ret; | |
ca5481c6 | 640 | |
dcc427e1 LP |
641 | pmx->pctl_desc.name = DRV_NAME; |
642 | pmx->pctl_desc.owner = THIS_MODULE; | |
643 | pmx->pctl_desc.pctlops = &sh_pfc_pinctrl_ops; | |
644 | pmx->pctl_desc.pmxops = &sh_pfc_pinmux_ops; | |
645 | pmx->pctl_desc.confops = &sh_pfc_pinconf_ops; | |
3d8d9f1d | 646 | pmx->pctl_desc.pins = pmx->pins; |
63d57383 | 647 | pmx->pctl_desc.npins = pfc->info->nr_pins; |
dcc427e1 LP |
648 | |
649 | pmx->pctl = pinctrl_register(&pmx->pctl_desc, pfc->dev, pmx); | |
fd9d05b0 WY |
650 | if (pmx->pctl == NULL) |
651 | return -EINVAL; | |
ca5481c6 | 652 | |
ca5481c6 | 653 | return 0; |
ca5481c6 PM |
654 | } |
655 | ||
c6193eac | 656 | int sh_pfc_unregister_pinctrl(struct sh_pfc *pfc) |
ca5481c6 | 657 | { |
c6193eac | 658 | struct sh_pfc_pinctrl *pmx = pfc->pinctrl; |
ca5481c6 | 659 | |
ca5481c6 PM |
660 | pinctrl_unregister(pmx->pctl); |
661 | ||
c6193eac | 662 | pfc->pinctrl = NULL; |
ca5481c6 PM |
663 | return 0; |
664 | } |