aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pinctrl/pinctrl-sunxi.c
diff options
context:
space:
mode:
authorMaxime Ripard <maxime.ripard@free-electrons.com>2013-01-18 16:30:34 -0500
committerLinus Walleij <linus.walleij@linaro.org>2013-01-22 07:51:15 -0500
commit0e37f88d9ad800f5dd94c9fc9dc304b4e9cb7d2c (patch)
tree592b7e3812dc6c06db0dc8ecf59e52dbf8984b4f /drivers/pinctrl/pinctrl-sunxi.c
parent9931faca02c604c22335f5a935a501bb2ace6e20 (diff)
ARM: sunxi: Add pinctrl driver for Allwinner SoCs
The Allwinner SoCs have an IP module that handle both the muxing and the GPIOs. This IP has 8 banks of 32 bits, with a number of pins actually useful for each of these banks varying from one to another, and depending on the SoC used on the board. This driver only implements the pinctrl part, the gpio part will come eventually. Acked-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Olof Johansson <olof@lixom.net> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/pinctrl/pinctrl-sunxi.c')
-rw-r--r--drivers/pinctrl/pinctrl-sunxi.c548
1 files changed, 548 insertions, 0 deletions
diff --git a/drivers/pinctrl/pinctrl-sunxi.c b/drivers/pinctrl/pinctrl-sunxi.c
new file mode 100644
index 000000000000..1a81613e8f77
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-sunxi.c
@@ -0,0 +1,548 @@
1/*
2 * Allwinner A1X SoCs pinctrl driver.
3 *
4 * Copyright (C) 2012 Maxime Ripard
5 *
6 * Maxime Ripard <maxime.ripard@free-electrons.com>
7 *
8 * This file is licensed under the terms of the GNU General Public
9 * License version 2. This program is licensed "as is" without any
10 * warranty of any kind, whether express or implied.
11 */
12
13#include <linux/io.h>
14#include <linux/module.h>
15#include <linux/of.h>
16#include <linux/of_address.h>
17#include <linux/of_device.h>
18#include <linux/pinctrl/consumer.h>
19#include <linux/pinctrl/machine.h>
20#include <linux/pinctrl/pinctrl.h>
21#include <linux/pinctrl/pinconf-generic.h>
22#include <linux/pinctrl/pinmux.h>
23#include <linux/platform_device.h>
24#include <linux/slab.h>
25
26#include "core.h"
27#include "pinctrl-sunxi.h"
28
29static struct sunxi_pinctrl_group *
30sunxi_pinctrl_find_group_by_name(struct sunxi_pinctrl *pctl, const char *group)
31{
32 int i;
33
34 for (i = 0; i < pctl->ngroups; i++) {
35 struct sunxi_pinctrl_group *grp = pctl->groups + i;
36
37 if (!strcmp(grp->name, group))
38 return grp;
39 }
40
41 return NULL;
42}
43
44static struct sunxi_pinctrl_function *
45sunxi_pinctrl_find_function_by_name(struct sunxi_pinctrl *pctl,
46 const char *name)
47{
48 struct sunxi_pinctrl_function *func = pctl->functions;
49 int i;
50
51 for (i = 0; i < pctl->nfunctions; i++) {
52 if (!func[i].name)
53 break;
54
55 if (!strcmp(func[i].name, name))
56 return func + i;
57 }
58
59 return NULL;
60}
61
62static struct sunxi_desc_function *
63sunxi_pinctrl_desc_find_function_by_name(struct sunxi_pinctrl *pctl,
64 const char *pin_name,
65 const char *func_name)
66{
67 int i;
68
69 for (i = 0; i < pctl->desc->npins; i++) {
70 const struct sunxi_desc_pin *pin = pctl->desc->pins + i;
71
72 if (!strcmp(pin->pin.name, pin_name)) {
73 struct sunxi_desc_function *func = pin->functions;
74
75 while (func->name) {
76 if (!strcmp(func->name, func_name))
77 return func;
78
79 func++;
80 }
81 }
82 }
83
84 return NULL;
85}
86
87static int sunxi_pctrl_get_groups_count(struct pinctrl_dev *pctldev)
88{
89 struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
90
91 return pctl->ngroups;
92}
93
94static const char *sunxi_pctrl_get_group_name(struct pinctrl_dev *pctldev,
95 unsigned group)
96{
97 struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
98
99 return pctl->groups[group].name;
100}
101
102static int sunxi_pctrl_get_group_pins(struct pinctrl_dev *pctldev,
103 unsigned group,
104 const unsigned **pins,
105 unsigned *num_pins)
106{
107 struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
108
109 *pins = (unsigned *)&pctl->groups[group].pin;
110 *num_pins = 1;
111
112 return 0;
113}
114
115static int sunxi_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
116 struct device_node *node,
117 struct pinctrl_map **map,
118 unsigned *num_maps)
119{
120 struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
121 unsigned long *pinconfig;
122 struct property *prop;
123 const char *function;
124 const char *group;
125 int ret, nmaps, i = 0;
126 u32 val;
127
128 *map = NULL;
129 *num_maps = 0;
130
131 ret = of_property_read_string(node, "allwinner,function", &function);
132 if (ret) {
133 dev_err(pctl->dev,
134 "missing allwinner,function property in node %s\n",
135 node->name);
136 return -EINVAL;
137 }
138
139 nmaps = of_property_count_strings(node, "allwinner,pins") * 2;
140 if (nmaps < 0) {
141 dev_err(pctl->dev,
142 "missing allwinner,pins property in node %s\n",
143 node->name);
144 return -EINVAL;
145 }
146
147 *map = kmalloc(nmaps * sizeof(struct pinctrl_map), GFP_KERNEL);
148 if (!map)
149 return -ENOMEM;
150
151 of_property_for_each_string(node, "allwinner,pins", prop, group) {
152 struct sunxi_pinctrl_group *grp =
153 sunxi_pinctrl_find_group_by_name(pctl, group);
154 int j = 0, configlen = 0;
155
156 if (!grp) {
157 dev_err(pctl->dev, "unknown pin %s", group);
158 continue;
159 }
160
161 if (!sunxi_pinctrl_desc_find_function_by_name(pctl,
162 grp->name,
163 function)) {
164 dev_err(pctl->dev, "unsupported function %s on pin %s",
165 function, group);
166 continue;
167 }
168
169 (*map)[i].type = PIN_MAP_TYPE_MUX_GROUP;
170 (*map)[i].data.mux.group = group;
171 (*map)[i].data.mux.function = function;
172
173 i++;
174
175 (*map)[i].type = PIN_MAP_TYPE_CONFIGS_GROUP;
176 (*map)[i].data.configs.group_or_pin = group;
177
178 if (of_find_property(node, "allwinner,drive", NULL))
179 configlen++;
180 if (of_find_property(node, "allwinner,pull", NULL))
181 configlen++;
182
183 pinconfig = kzalloc(configlen * sizeof(*pinconfig), GFP_KERNEL);
184
185 if (!of_property_read_u32(node, "allwinner,drive", &val)) {
186 u16 strength = (val + 1) * 10;
187 pinconfig[j++] =
188 pinconf_to_config_packed(PIN_CONFIG_DRIVE_STRENGTH,
189 strength);
190 }
191
192 if (!of_property_read_u32(node, "allwinner,pull", &val)) {
193 enum pin_config_param pull = PIN_CONFIG_END;
194 if (val == 1)
195 pull = PIN_CONFIG_BIAS_PULL_UP;
196 else if (val == 2)
197 pull = PIN_CONFIG_BIAS_PULL_DOWN;
198 pinconfig[j++] = pinconf_to_config_packed(pull, 0);
199 }
200
201 (*map)[i].data.configs.configs = pinconfig;
202 (*map)[i].data.configs.num_configs = configlen;
203
204 i++;
205 }
206
207 *num_maps = nmaps;
208
209 return 0;
210}
211
212static void sunxi_pctrl_dt_free_map(struct pinctrl_dev *pctldev,
213 struct pinctrl_map *map,
214 unsigned num_maps)
215{
216 int i;
217
218 for (i = 0; i < num_maps; i++) {
219 if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP)
220 kfree(map[i].data.configs.configs);
221 }
222
223 kfree(map);
224}
225
226static struct pinctrl_ops sunxi_pctrl_ops = {
227 .dt_node_to_map = sunxi_pctrl_dt_node_to_map,
228 .dt_free_map = sunxi_pctrl_dt_free_map,
229 .get_groups_count = sunxi_pctrl_get_groups_count,
230 .get_group_name = sunxi_pctrl_get_group_name,
231 .get_group_pins = sunxi_pctrl_get_group_pins,
232};
233
234static int sunxi_pconf_group_get(struct pinctrl_dev *pctldev,
235 unsigned group,
236 unsigned long *config)
237{
238 struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
239
240 *config = pctl->groups[group].config;
241
242 return 0;
243}
244
245static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev,
246 unsigned group,
247 unsigned long config)
248{
249 struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
250 struct sunxi_pinctrl_group *g = &pctl->groups[group];
251 u32 val, mask;
252 u16 strength;
253 u8 dlevel;
254
255 switch (pinconf_to_config_param(config)) {
256 case PIN_CONFIG_DRIVE_STRENGTH:
257 strength = pinconf_to_config_argument(config);
258 if (strength > 40)
259 return -EINVAL;
260 /*
261 * We convert from mA to what the register expects:
262 * 0: 10mA
263 * 1: 20mA
264 * 2: 30mA
265 * 3: 40mA
266 */
267 dlevel = strength / 10 - 1;
268 val = readl(pctl->membase + sunxi_dlevel_reg(g->pin));
269 mask = DLEVEL_PINS_MASK << sunxi_dlevel_offset(g->pin);
270 writel((val & ~mask) | dlevel << sunxi_dlevel_offset(g->pin),
271 pctl->membase + sunxi_dlevel_reg(g->pin));
272 break;
273 case PIN_CONFIG_BIAS_PULL_UP:
274 val = readl(pctl->membase + sunxi_pull_reg(g->pin));
275 mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin);
276 writel((val & ~mask) | 1 << sunxi_pull_offset(g->pin),
277 pctl->membase + sunxi_pull_reg(g->pin));
278 break;
279 case PIN_CONFIG_BIAS_PULL_DOWN:
280 val = readl(pctl->membase + sunxi_pull_reg(g->pin));
281 mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin);
282 writel((val & ~mask) | 2 << sunxi_pull_offset(g->pin),
283 pctl->membase + sunxi_pull_reg(g->pin));
284 break;
285 default:
286 break;
287 }
288
289 /* cache the config value */
290 g->config = config;
291
292 return 0;
293}
294
295static struct pinconf_ops sunxi_pconf_ops = {
296 .pin_config_group_get = sunxi_pconf_group_get,
297 .pin_config_group_set = sunxi_pconf_group_set,
298};
299
300static int sunxi_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev)
301{
302 struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
303
304 return pctl->nfunctions;
305}
306
307static const char *sunxi_pmx_get_func_name(struct pinctrl_dev *pctldev,
308 unsigned function)
309{
310 struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
311
312 return pctl->functions[function].name;
313}
314
315static int sunxi_pmx_get_func_groups(struct pinctrl_dev *pctldev,
316 unsigned function,
317 const char * const **groups,
318 unsigned * const num_groups)
319{
320 struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
321
322 *groups = pctl->functions[function].groups;
323 *num_groups = pctl->functions[function].ngroups;
324
325 return 0;
326}
327
328static void sunxi_pmx_set(struct pinctrl_dev *pctldev,
329 unsigned pin,
330 u8 config)
331{
332 struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
333
334 u32 val = readl(pctl->membase + sunxi_mux_reg(pin));
335 u32 mask = MUX_PINS_MASK << sunxi_mux_offset(pin);
336 writel((val & ~mask) | config << sunxi_mux_offset(pin),
337 pctl->membase + sunxi_mux_reg(pin));
338}
339
340static int sunxi_pmx_enable(struct pinctrl_dev *pctldev,
341 unsigned function,
342 unsigned group)
343{
344 struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
345 struct sunxi_pinctrl_group *g = pctl->groups + group;
346 struct sunxi_pinctrl_function *func = pctl->functions + function;
347 struct sunxi_desc_function *desc =
348 sunxi_pinctrl_desc_find_function_by_name(pctl,
349 g->name,
350 func->name);
351
352 if (!desc)
353 return -EINVAL;
354
355 sunxi_pmx_set(pctldev, g->pin, desc->muxval);
356
357 return 0;
358}
359
360static struct pinmux_ops sunxi_pmx_ops = {
361 .get_functions_count = sunxi_pmx_get_funcs_cnt,
362 .get_function_name = sunxi_pmx_get_func_name,
363 .get_function_groups = sunxi_pmx_get_func_groups,
364 .enable = sunxi_pmx_enable,
365};
366
367static struct pinctrl_desc sunxi_pctrl_desc = {
368 .confops = &sunxi_pconf_ops,
369 .pctlops = &sunxi_pctrl_ops,
370 .pmxops = &sunxi_pmx_ops,
371};
372
373static struct of_device_id sunxi_pinctrl_match[] = {
374 {}
375};
376MODULE_DEVICE_TABLE(of, sunxi_pinctrl_match);
377
378static int sunxi_pinctrl_add_function(struct sunxi_pinctrl *pctl,
379 const char *name)
380{
381 struct sunxi_pinctrl_function *func = pctl->functions;
382
383 while (func->name) {
384 /* function already there */
385 if (strcmp(func->name, name) == 0) {
386 func->ngroups++;
387 return -EEXIST;
388 }
389 func++;
390 }
391
392 func->name = name;
393 func->ngroups = 1;
394
395 pctl->nfunctions++;
396
397 return 0;
398}
399
400static int sunxi_pinctrl_build_state(struct platform_device *pdev)
401{
402 struct sunxi_pinctrl *pctl = platform_get_drvdata(pdev);
403 int i;
404
405 pctl->ngroups = pctl->desc->npins;
406
407 /* Allocate groups */
408 pctl->groups = devm_kzalloc(&pdev->dev,
409 pctl->ngroups * sizeof(*pctl->groups),
410 GFP_KERNEL);
411 if (!pctl->groups)
412 return -ENOMEM;
413
414 for (i = 0; i < pctl->desc->npins; i++) {
415 const struct sunxi_desc_pin *pin = pctl->desc->pins + i;
416 struct sunxi_pinctrl_group *group = pctl->groups + i;
417
418 group->name = pin->pin.name;
419 group->pin = pin->pin.number;
420 }
421
422 /*
423 * We suppose that we won't have any more functions than pins,
424 * we'll reallocate that later anyway
425 */
426 pctl->functions = devm_kzalloc(&pdev->dev,
427 pctl->desc->npins * sizeof(*pctl->functions),
428 GFP_KERNEL);
429 if (!pctl->functions)
430 return -ENOMEM;
431
432 /* Count functions and their associated groups */
433 for (i = 0; i < pctl->desc->npins; i++) {
434 const struct sunxi_desc_pin *pin = pctl->desc->pins + i;
435 struct sunxi_desc_function *func = pin->functions;
436
437 while (func->name) {
438 sunxi_pinctrl_add_function(pctl, func->name);
439 func++;
440 }
441 }
442
443 pctl->functions = krealloc(pctl->functions,
444 pctl->nfunctions * sizeof(*pctl->functions),
445 GFP_KERNEL);
446
447 for (i = 0; i < pctl->desc->npins; i++) {
448 const struct sunxi_desc_pin *pin = pctl->desc->pins + i;
449 struct sunxi_desc_function *func = pin->functions;
450
451 while (func->name) {
452 struct sunxi_pinctrl_function *func_item;
453 const char **func_grp;
454
455 func_item = sunxi_pinctrl_find_function_by_name(pctl,
456 func->name);
457 if (!func_item)
458 return -EINVAL;
459
460 if (!func_item->groups) {
461 func_item->groups =
462 devm_kzalloc(&pdev->dev,
463 func_item->ngroups * sizeof(*func_item->groups),
464 GFP_KERNEL);
465 if (!func_item->groups)
466 return -ENOMEM;
467 }
468
469 func_grp = func_item->groups;
470 while (*func_grp)
471 func_grp++;
472
473 *func_grp = pin->pin.name;
474 func++;
475 }
476 }
477
478 return 0;
479}
480
481static int sunxi_pinctrl_probe(struct platform_device *pdev)
482{
483 struct device_node *node = pdev->dev.of_node;
484 const struct of_device_id *device;
485 struct pinctrl_pin_desc *pins;
486 struct sunxi_pinctrl *pctl;
487 int i, ret;
488
489 pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL);
490 if (!pctl)
491 return -ENOMEM;
492 platform_set_drvdata(pdev, pctl);
493
494 pctl->membase = of_iomap(node, 0);
495 if (!pctl->membase)
496 return -ENOMEM;
497
498 device = of_match_device(sunxi_pinctrl_match, &pdev->dev);
499 if (!device)
500 return -ENODEV;
501
502 pctl->desc = (struct sunxi_pinctrl_desc *)device->data;
503
504 ret = sunxi_pinctrl_build_state(pdev);
505 if (ret) {
506 dev_err(&pdev->dev, "dt probe failed: %d\n", ret);
507 return ret;
508 }
509
510 pins = devm_kzalloc(&pdev->dev,
511 pctl->desc->npins * sizeof(*pins),
512 GFP_KERNEL);
513 if (!pins)
514 return -ENOMEM;
515
516 for (i = 0; i < pctl->desc->npins; i++)
517 pins[i] = pctl->desc->pins[i].pin;
518
519 sunxi_pctrl_desc.name = dev_name(&pdev->dev);
520 sunxi_pctrl_desc.owner = THIS_MODULE;
521 sunxi_pctrl_desc.pins = pins;
522 sunxi_pctrl_desc.npins = pctl->desc->npins;
523 pctl->dev = &pdev->dev;
524 pctl->pctl_dev = pinctrl_register(&sunxi_pctrl_desc,
525 &pdev->dev, pctl);
526 if (!pctl->pctl_dev) {
527 dev_err(&pdev->dev, "couldn't register pinctrl driver\n");
528 return -EINVAL;
529 }
530
531 dev_info(&pdev->dev, "initialized sunXi pin control driver\n");
532
533 return 0;
534}
535
536static struct platform_driver sunxi_pinctrl_driver = {
537 .probe = sunxi_pinctrl_probe,
538 .driver = {
539 .name = "sunxi-pinctrl",
540 .owner = THIS_MODULE,
541 .of_match_table = sunxi_pinctrl_match,
542 },
543};
544module_platform_driver(sunxi_pinctrl_driver);
545
546MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com");
547MODULE_DESCRIPTION("Allwinner A1X pinctrl driver");
548MODULE_LICENSE("GPL");