]>
Commit | Line | Data |
---|---|---|
4d3d0e42 AJ |
1 | /* |
2 | * Copyright (C) 2016 IBM Corp. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | */ | |
9 | ||
10 | #include <linux/mfd/syscon.h> | |
11 | #include <linux/platform_device.h> | |
12 | #include <linux/slab.h> | |
13 | #include <linux/string.h> | |
14 | #include "../core.h" | |
15 | #include "pinctrl-aspeed.h" | |
16 | ||
7d29ed88 AJ |
17 | static const char *const aspeed_pinmux_ips[] = { |
18 | [ASPEED_IP_SCU] = "SCU", | |
19 | [ASPEED_IP_GFX] = "GFX", | |
20 | [ASPEED_IP_LPC] = "LPC", | |
21 | }; | |
22 | ||
4d3d0e42 AJ |
23 | int aspeed_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) |
24 | { | |
25 | struct aspeed_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev); | |
26 | ||
27 | return pdata->ngroups; | |
28 | } | |
29 | ||
30 | const char *aspeed_pinctrl_get_group_name(struct pinctrl_dev *pctldev, | |
31 | unsigned int group) | |
32 | { | |
33 | struct aspeed_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev); | |
34 | ||
35 | return pdata->groups[group].name; | |
36 | } | |
37 | ||
38 | int aspeed_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, | |
39 | unsigned int group, const unsigned int **pins, | |
40 | unsigned int *npins) | |
41 | { | |
42 | struct aspeed_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev); | |
43 | ||
44 | *pins = &pdata->groups[group].pins[0]; | |
45 | *npins = pdata->groups[group].npins; | |
46 | ||
47 | return 0; | |
48 | } | |
49 | ||
50 | void aspeed_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev, | |
51 | struct seq_file *s, unsigned int offset) | |
52 | { | |
53 | seq_printf(s, " %s", dev_name(pctldev->dev)); | |
54 | } | |
55 | ||
56 | int aspeed_pinmux_get_fn_count(struct pinctrl_dev *pctldev) | |
57 | { | |
58 | struct aspeed_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev); | |
59 | ||
60 | return pdata->nfunctions; | |
61 | } | |
62 | ||
63 | const char *aspeed_pinmux_get_fn_name(struct pinctrl_dev *pctldev, | |
64 | unsigned int function) | |
65 | { | |
66 | struct aspeed_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev); | |
67 | ||
68 | return pdata->functions[function].name; | |
69 | } | |
70 | ||
71 | int aspeed_pinmux_get_fn_groups(struct pinctrl_dev *pctldev, | |
72 | unsigned int function, | |
73 | const char * const **groups, | |
74 | unsigned int * const num_groups) | |
75 | { | |
76 | struct aspeed_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev); | |
77 | ||
78 | *groups = pdata->functions[function].groups; | |
79 | *num_groups = pdata->functions[function].ngroups; | |
80 | ||
81 | return 0; | |
82 | } | |
83 | ||
84 | static inline void aspeed_sig_desc_print_val( | |
85 | const struct aspeed_sig_desc *desc, bool enable, u32 rv) | |
86 | { | |
7d29ed88 AJ |
87 | pr_debug("Want %s%X[0x%08X]=0x%X, got 0x%X from 0x%08X\n", |
88 | aspeed_pinmux_ips[desc->ip], desc->reg, | |
4d3d0e42 AJ |
89 | desc->mask, enable ? desc->enable : desc->disable, |
90 | (rv & desc->mask) >> __ffs(desc->mask), rv); | |
91 | } | |
92 | ||
93 | /** | |
94 | * Query the enabled or disabled state of a signal descriptor | |
95 | * | |
96 | * @desc: The signal descriptor of interest | |
97 | * @enabled: True to query the enabled state, false to query disabled state | |
7d29ed88 | 98 | * @regmap: The IP block's regmap instance |
4d3d0e42 | 99 | * |
b75dd872 | 100 | * Return: 1 if the descriptor's bitfield is configured to the state |
7d29ed88 AJ |
101 | * selected by @enabled, 0 if not, and less than zero if an unrecoverable |
102 | * failure occurred | |
4d3d0e42 AJ |
103 | * |
104 | * Evaluation of descriptor state is non-trivial in that it is not a binary | |
105 | * outcome: The bitfields can be greater than one bit in size and thus can take | |
106 | * a value that is neither the enabled nor disabled state recorded in the | |
107 | * descriptor (typically this means a different function to the one of interest | |
108 | * is enabled). Thus we must explicitly test for either condition as required. | |
109 | */ | |
7d29ed88 | 110 | static int aspeed_sig_desc_eval(const struct aspeed_sig_desc *desc, |
4d3d0e42 AJ |
111 | bool enabled, struct regmap *map) |
112 | { | |
7d29ed88 | 113 | int ret; |
4d3d0e42 AJ |
114 | unsigned int raw; |
115 | u32 want; | |
116 | ||
7d29ed88 AJ |
117 | if (!map) |
118 | return -ENODEV; | |
119 | ||
120 | ret = regmap_read(map, desc->reg, &raw); | |
121 | if (ret) | |
122 | return ret; | |
4d3d0e42 AJ |
123 | |
124 | aspeed_sig_desc_print_val(desc, enabled, raw); | |
125 | want = enabled ? desc->enable : desc->disable; | |
126 | ||
127 | return ((raw & desc->mask) >> __ffs(desc->mask)) == want; | |
128 | } | |
129 | ||
130 | /** | |
131 | * Query the enabled or disabled state for a mux function's signal on a pin | |
132 | * | |
133 | * @expr: An expression controlling the signal for a mux function on a pin | |
134 | * @enabled: True to query the enabled state, false to query disabled state | |
7d29ed88 | 135 | * @maps: The list of regmap instances |
4d3d0e42 | 136 | * |
b75dd872 | 137 | * Return: 1 if the expression composed by @enabled evaluates true, 0 if not, |
7d29ed88 | 138 | * and less than zero if an unrecoverable failure occurred. |
4d3d0e42 AJ |
139 | * |
140 | * A mux function is enabled or disabled if the function's signal expression | |
141 | * for each pin in the function's pin group evaluates true for the desired | |
142 | * state. An signal expression evaluates true if all of its associated signal | |
143 | * descriptors evaluate true for the desired state. | |
144 | * | |
145 | * If an expression's state is described by more than one bit, either through | |
146 | * multi-bit bitfields in a single signal descriptor or through multiple signal | |
147 | * descriptors of a single bit then it is possible for the expression to be in | |
148 | * neither the enabled nor disabled state. Thus we must explicitly test for | |
149 | * either condition as required. | |
150 | */ | |
7d29ed88 AJ |
151 | static int aspeed_sig_expr_eval(const struct aspeed_sig_expr *expr, |
152 | bool enabled, struct regmap * const *maps) | |
4d3d0e42 AJ |
153 | { |
154 | int i; | |
7d29ed88 | 155 | int ret; |
4d3d0e42 AJ |
156 | |
157 | for (i = 0; i < expr->ndescs; i++) { | |
158 | const struct aspeed_sig_desc *desc = &expr->descs[i]; | |
159 | ||
7d29ed88 AJ |
160 | ret = aspeed_sig_desc_eval(desc, enabled, maps[desc->ip]); |
161 | if (ret <= 0) | |
162 | return ret; | |
4d3d0e42 AJ |
163 | } |
164 | ||
7d29ed88 | 165 | return 1; |
4d3d0e42 AJ |
166 | } |
167 | ||
168 | /** | |
169 | * Configure a pin's signal by applying an expression's descriptor state for | |
170 | * all descriptors in the expression. | |
171 | * | |
172 | * @expr: The expression associated with the function whose signal is to be | |
173 | * configured | |
174 | * @enable: true to enable an function's signal through a pin's signal | |
175 | * expression, false to disable the function's signal | |
7d29ed88 | 176 | * @maps: The list of regmap instances for pinmux register access. |
4d3d0e42 | 177 | * |
b75dd872 | 178 | * Return: 0 if the expression is configured as requested and a negative error |
7d29ed88 | 179 | * code otherwise |
4d3d0e42 | 180 | */ |
7d29ed88 AJ |
181 | static int aspeed_sig_expr_set(const struct aspeed_sig_expr *expr, |
182 | bool enable, struct regmap * const *maps) | |
4d3d0e42 | 183 | { |
7d29ed88 | 184 | int ret; |
4d3d0e42 | 185 | int i; |
4d3d0e42 AJ |
186 | |
187 | for (i = 0; i < expr->ndescs; i++) { | |
188 | const struct aspeed_sig_desc *desc = &expr->descs[i]; | |
189 | u32 pattern = enable ? desc->enable : desc->disable; | |
7d29ed88 AJ |
190 | u32 val = (pattern << __ffs(desc->mask)); |
191 | ||
192 | if (!maps[desc->ip]) | |
193 | return -ENODEV; | |
4d3d0e42 AJ |
194 | |
195 | /* | |
196 | * Strap registers are configured in hardware or by early-boot | |
197 | * firmware. Treat them as read-only despite that we can write | |
198 | * them. This may mean that certain functions cannot be | |
199 | * deconfigured and is the reason we re-evaluate after writing | |
200 | * all descriptor bits. | |
201 | */ | |
7d29ed88 AJ |
202 | if ((desc->reg == HW_STRAP1 || desc->reg == HW_STRAP2) && |
203 | desc->ip == ASPEED_IP_SCU) | |
4d3d0e42 AJ |
204 | continue; |
205 | ||
7d29ed88 AJ |
206 | ret = regmap_update_bits(maps[desc->ip], desc->reg, |
207 | desc->mask, val); | |
4d3d0e42 | 208 | |
7d29ed88 | 209 | if (ret) |
55956035 | 210 | return ret; |
4d3d0e42 AJ |
211 | } |
212 | ||
7d29ed88 AJ |
213 | ret = aspeed_sig_expr_eval(expr, enable, maps); |
214 | if (ret < 0) | |
215 | return ret; | |
216 | ||
217 | if (!ret) | |
218 | return -EPERM; | |
219 | ||
220 | return 0; | |
4d3d0e42 AJ |
221 | } |
222 | ||
7d29ed88 AJ |
223 | static int aspeed_sig_expr_enable(const struct aspeed_sig_expr *expr, |
224 | struct regmap * const *maps) | |
4d3d0e42 | 225 | { |
7d29ed88 AJ |
226 | int ret; |
227 | ||
228 | ret = aspeed_sig_expr_eval(expr, true, maps); | |
229 | if (ret < 0) | |
230 | return ret; | |
231 | ||
232 | if (!ret) | |
233 | return aspeed_sig_expr_set(expr, true, maps); | |
5366f146 | 234 | |
7d29ed88 | 235 | return 0; |
4d3d0e42 AJ |
236 | } |
237 | ||
7d29ed88 AJ |
238 | static int aspeed_sig_expr_disable(const struct aspeed_sig_expr *expr, |
239 | struct regmap * const *maps) | |
4d3d0e42 | 240 | { |
7d29ed88 AJ |
241 | int ret; |
242 | ||
243 | ret = aspeed_sig_expr_eval(expr, true, maps); | |
244 | if (ret < 0) | |
245 | return ret; | |
246 | ||
247 | if (ret) | |
248 | return aspeed_sig_expr_set(expr, false, maps); | |
5366f146 | 249 | |
7d29ed88 | 250 | return 0; |
4d3d0e42 AJ |
251 | } |
252 | ||
253 | /** | |
254 | * Disable a signal on a pin by disabling all provided signal expressions. | |
255 | * | |
256 | * @exprs: The list of signal expressions (from a priority level on a pin) | |
7d29ed88 | 257 | * @maps: The list of regmap instances for pinmux register access. |
4d3d0e42 | 258 | * |
b75dd872 | 259 | * Return: 0 if all expressions are disabled, otherwise a negative error code |
4d3d0e42 | 260 | */ |
7d29ed88 AJ |
261 | static int aspeed_disable_sig(const struct aspeed_sig_expr **exprs, |
262 | struct regmap * const *maps) | |
4d3d0e42 | 263 | { |
7d29ed88 | 264 | int ret = 0; |
4d3d0e42 AJ |
265 | |
266 | if (!exprs) | |
267 | return true; | |
268 | ||
7d29ed88 AJ |
269 | while (*exprs && !ret) { |
270 | ret = aspeed_sig_expr_disable(*exprs, maps); | |
4d3d0e42 AJ |
271 | exprs++; |
272 | } | |
273 | ||
7d29ed88 | 274 | return ret; |
4d3d0e42 AJ |
275 | } |
276 | ||
277 | /** | |
278 | * Search for the signal expression needed to enable the pin's signal for the | |
279 | * requested function. | |
280 | * | |
281 | * @exprs: List of signal expressions (haystack) | |
282 | * @name: The name of the requested function (needle) | |
283 | * | |
b75dd872 AJ |
284 | * Return: A pointer to the signal expression whose function tag matches the |
285 | * provided name, otherwise NULL. | |
4d3d0e42 AJ |
286 | * |
287 | */ | |
288 | static const struct aspeed_sig_expr *aspeed_find_expr_by_name( | |
289 | const struct aspeed_sig_expr **exprs, const char *name) | |
290 | { | |
291 | while (*exprs) { | |
292 | if (strcmp((*exprs)->function, name) == 0) | |
293 | return *exprs; | |
294 | exprs++; | |
295 | } | |
296 | ||
297 | return NULL; | |
298 | } | |
299 | ||
300 | static char *get_defined_attribute(const struct aspeed_pin_desc *pdesc, | |
301 | const char *(*get)( | |
302 | const struct aspeed_sig_expr *)) | |
303 | { | |
304 | char *found = NULL; | |
305 | size_t len = 0; | |
306 | const struct aspeed_sig_expr ***prios, **funcs, *expr; | |
307 | ||
308 | prios = pdesc->prios; | |
309 | ||
310 | while ((funcs = *prios)) { | |
311 | while ((expr = *funcs)) { | |
312 | const char *str = get(expr); | |
313 | size_t delta = strlen(str) + 2; | |
314 | char *expanded; | |
315 | ||
316 | expanded = krealloc(found, len + delta + 1, GFP_KERNEL); | |
317 | if (!expanded) { | |
318 | kfree(found); | |
319 | return expanded; | |
320 | } | |
321 | ||
322 | found = expanded; | |
323 | found[len] = '\0'; | |
324 | len += delta; | |
325 | ||
326 | strcat(found, str); | |
327 | strcat(found, ", "); | |
328 | ||
329 | funcs++; | |
330 | } | |
331 | prios++; | |
332 | } | |
333 | ||
334 | if (len < 2) { | |
335 | kfree(found); | |
336 | return NULL; | |
337 | } | |
338 | ||
339 | found[len - 2] = '\0'; | |
340 | ||
341 | return found; | |
342 | } | |
343 | ||
344 | static const char *aspeed_sig_expr_function(const struct aspeed_sig_expr *expr) | |
345 | { | |
346 | return expr->function; | |
347 | } | |
348 | ||
349 | static char *get_defined_functions(const struct aspeed_pin_desc *pdesc) | |
350 | { | |
351 | return get_defined_attribute(pdesc, aspeed_sig_expr_function); | |
352 | } | |
353 | ||
354 | static const char *aspeed_sig_expr_signal(const struct aspeed_sig_expr *expr) | |
355 | { | |
356 | return expr->signal; | |
357 | } | |
358 | ||
359 | static char *get_defined_signals(const struct aspeed_pin_desc *pdesc) | |
360 | { | |
361 | return get_defined_attribute(pdesc, aspeed_sig_expr_signal); | |
362 | } | |
363 | ||
364 | int aspeed_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int function, | |
365 | unsigned int group) | |
366 | { | |
367 | int i; | |
7d29ed88 | 368 | int ret; |
4d3d0e42 AJ |
369 | const struct aspeed_pinctrl_data *pdata = |
370 | pinctrl_dev_get_drvdata(pctldev); | |
371 | const struct aspeed_pin_group *pgroup = &pdata->groups[group]; | |
372 | const struct aspeed_pin_function *pfunc = | |
373 | &pdata->functions[function]; | |
374 | ||
375 | for (i = 0; i < pgroup->npins; i++) { | |
376 | int pin = pgroup->pins[i]; | |
377 | const struct aspeed_pin_desc *pdesc = pdata->pins[pin].drv_data; | |
378 | const struct aspeed_sig_expr *expr = NULL; | |
379 | const struct aspeed_sig_expr **funcs; | |
380 | const struct aspeed_sig_expr ***prios; | |
381 | ||
7d29ed88 AJ |
382 | pr_debug("Muxing pin %d for %s\n", pin, pfunc->name); |
383 | ||
4d3d0e42 AJ |
384 | if (!pdesc) |
385 | return -EINVAL; | |
386 | ||
387 | prios = pdesc->prios; | |
388 | ||
389 | if (!prios) | |
390 | continue; | |
391 | ||
392 | /* Disable functions at a higher priority than that requested */ | |
393 | while ((funcs = *prios)) { | |
394 | expr = aspeed_find_expr_by_name(funcs, pfunc->name); | |
395 | ||
396 | if (expr) | |
397 | break; | |
398 | ||
7d29ed88 AJ |
399 | ret = aspeed_disable_sig(funcs, pdata->maps); |
400 | if (ret) | |
401 | return ret; | |
4d3d0e42 AJ |
402 | |
403 | prios++; | |
404 | } | |
405 | ||
406 | if (!expr) { | |
407 | char *functions = get_defined_functions(pdesc); | |
408 | char *signals = get_defined_signals(pdesc); | |
409 | ||
410 | pr_warn("No function %s found on pin %s (%d). Found signal(s) %s for function(s) %s\n", | |
411 | pfunc->name, pdesc->name, pin, signals, | |
412 | functions); | |
413 | kfree(signals); | |
414 | kfree(functions); | |
415 | ||
416 | return -ENXIO; | |
417 | } | |
418 | ||
7d29ed88 AJ |
419 | ret = aspeed_sig_expr_enable(expr, pdata->maps); |
420 | if (ret) | |
421 | return ret; | |
4d3d0e42 AJ |
422 | } |
423 | ||
424 | return 0; | |
425 | } | |
426 | ||
427 | static bool aspeed_expr_is_gpio(const struct aspeed_sig_expr *expr) | |
428 | { | |
429 | /* | |
430 | * The signal type is GPIO if the signal name has "GPIO" as a prefix. | |
431 | * strncmp (rather than strcmp) is used to implement the prefix | |
432 | * requirement. | |
433 | * | |
434 | * expr->signal might look like "GPIOT3" in the GPIO case. | |
435 | */ | |
436 | return strncmp(expr->signal, "GPIO", 4) == 0; | |
437 | } | |
438 | ||
439 | static bool aspeed_gpio_in_exprs(const struct aspeed_sig_expr **exprs) | |
440 | { | |
441 | if (!exprs) | |
442 | return false; | |
443 | ||
444 | while (*exprs) { | |
445 | if (aspeed_expr_is_gpio(*exprs)) | |
446 | return true; | |
447 | exprs++; | |
448 | } | |
449 | ||
450 | return false; | |
451 | } | |
452 | ||
453 | int aspeed_gpio_request_enable(struct pinctrl_dev *pctldev, | |
454 | struct pinctrl_gpio_range *range, | |
455 | unsigned int offset) | |
456 | { | |
7d29ed88 | 457 | int ret; |
4d3d0e42 AJ |
458 | const struct aspeed_pinctrl_data *pdata = |
459 | pinctrl_dev_get_drvdata(pctldev); | |
460 | const struct aspeed_pin_desc *pdesc = pdata->pins[offset].drv_data; | |
461 | const struct aspeed_sig_expr ***prios, **funcs, *expr; | |
462 | ||
463 | if (!pdesc) | |
464 | return -EINVAL; | |
465 | ||
466 | prios = pdesc->prios; | |
467 | ||
468 | if (!prios) | |
469 | return -ENXIO; | |
470 | ||
471 | /* Disable any functions of higher priority than GPIO */ | |
472 | while ((funcs = *prios)) { | |
473 | if (aspeed_gpio_in_exprs(funcs)) | |
474 | break; | |
475 | ||
7d29ed88 AJ |
476 | ret = aspeed_disable_sig(funcs, pdata->maps); |
477 | if (ret) | |
478 | return ret; | |
4d3d0e42 AJ |
479 | |
480 | prios++; | |
481 | } | |
482 | ||
483 | if (!funcs) { | |
484 | char *signals = get_defined_signals(pdesc); | |
485 | ||
486 | pr_warn("No GPIO signal type found on pin %s (%d). Found: %s\n", | |
487 | pdesc->name, offset, signals); | |
488 | kfree(signals); | |
489 | ||
490 | return -ENXIO; | |
491 | } | |
492 | ||
493 | expr = *funcs; | |
494 | ||
495 | /* | |
496 | * Disabling all higher-priority expressions is enough to enable the | |
497 | * lowest-priority signal type. As such it has no associated | |
498 | * expression. | |
499 | */ | |
500 | if (!expr) | |
501 | return 0; | |
502 | ||
503 | /* | |
504 | * If GPIO is not the lowest priority signal type, assume there is only | |
505 | * one expression defined to enable the GPIO function | |
506 | */ | |
7d29ed88 | 507 | return aspeed_sig_expr_enable(expr, pdata->maps); |
4d3d0e42 AJ |
508 | } |
509 | ||
510 | int aspeed_pinctrl_probe(struct platform_device *pdev, | |
511 | struct pinctrl_desc *pdesc, | |
512 | struct aspeed_pinctrl_data *pdata) | |
513 | { | |
514 | struct device *parent; | |
515 | struct pinctrl_dev *pctl; | |
516 | ||
517 | parent = pdev->dev.parent; | |
518 | if (!parent) { | |
519 | dev_err(&pdev->dev, "No parent for syscon pincontroller\n"); | |
520 | return -ENODEV; | |
521 | } | |
522 | ||
7d29ed88 AJ |
523 | pdata->maps[ASPEED_IP_SCU] = syscon_node_to_regmap(parent->of_node); |
524 | if (IS_ERR(pdata->maps[ASPEED_IP_SCU])) { | |
4d3d0e42 | 525 | dev_err(&pdev->dev, "No regmap for syscon pincontroller parent\n"); |
7d29ed88 | 526 | return PTR_ERR(pdata->maps[ASPEED_IP_SCU]); |
4d3d0e42 AJ |
527 | } |
528 | ||
529 | pctl = pinctrl_register(pdesc, &pdev->dev, pdata); | |
530 | ||
531 | if (IS_ERR(pctl)) { | |
532 | dev_err(&pdev->dev, "Failed to register pinctrl\n"); | |
533 | return PTR_ERR(pctl); | |
534 | } | |
535 | ||
536 | platform_set_drvdata(pdev, pdata); | |
537 | ||
538 | return 0; | |
539 | } |