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 | |
74677701 | 98 | * @map: 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. | |
c825676b RA |
201 | * |
202 | * Port D and port E GPIO loopback modes are the only exception | |
203 | * as those are commonly used with front-panel buttons to allow | |
204 | * normal operation of the host when the BMC is powered off or | |
205 | * fails to boot. Once the BMC has booted, the loopback mode | |
206 | * must be disabled for the BMC to control host power-on and | |
207 | * reset. | |
4d3d0e42 | 208 | */ |
c825676b RA |
209 | if (desc->ip == ASPEED_IP_SCU && desc->reg == HW_STRAP1 && |
210 | !(desc->mask & (BIT(21) | BIT(22)))) | |
211 | continue; | |
212 | ||
213 | if (desc->ip == ASPEED_IP_SCU && desc->reg == HW_STRAP2) | |
4d3d0e42 AJ |
214 | continue; |
215 | ||
d0d88b5c | 216 | /* On AST2500, Set bits in SCU70 are cleared from SCU7C */ |
1865af21 | 217 | if (desc->ip == ASPEED_IP_SCU && desc->reg == HW_STRAP1) { |
5241bd16 AJ |
218 | unsigned int rev_id; |
219 | ||
1865af21 YL |
220 | ret = regmap_read(maps[ASPEED_IP_SCU], |
221 | HW_REVISION_ID, &rev_id); | |
222 | if (ret < 0) | |
223 | return ret; | |
224 | ||
5241bd16 AJ |
225 | if (0x04 == (rev_id >> 24)) { |
226 | u32 value = ~val & desc->mask; | |
227 | ||
228 | if (value) { | |
229 | ret = regmap_write(maps[desc->ip], | |
230 | HW_REVISION_ID, value); | |
231 | if (ret < 0) | |
232 | return ret; | |
233 | } | |
234 | } | |
235 | } | |
236 | ||
237 | ret = regmap_update_bits(maps[desc->ip], desc->reg, | |
238 | desc->mask, val); | |
4d3d0e42 | 239 | |
7d29ed88 | 240 | if (ret) |
55956035 | 241 | return ret; |
4d3d0e42 AJ |
242 | } |
243 | ||
7d29ed88 AJ |
244 | ret = aspeed_sig_expr_eval(expr, enable, maps); |
245 | if (ret < 0) | |
246 | return ret; | |
247 | ||
248 | if (!ret) | |
249 | return -EPERM; | |
250 | ||
251 | return 0; | |
4d3d0e42 AJ |
252 | } |
253 | ||
7d29ed88 AJ |
254 | static int aspeed_sig_expr_enable(const struct aspeed_sig_expr *expr, |
255 | struct regmap * const *maps) | |
4d3d0e42 | 256 | { |
7d29ed88 AJ |
257 | int ret; |
258 | ||
259 | ret = aspeed_sig_expr_eval(expr, true, maps); | |
260 | if (ret < 0) | |
261 | return ret; | |
262 | ||
263 | if (!ret) | |
264 | return aspeed_sig_expr_set(expr, true, maps); | |
5366f146 | 265 | |
7d29ed88 | 266 | return 0; |
4d3d0e42 AJ |
267 | } |
268 | ||
7d29ed88 AJ |
269 | static int aspeed_sig_expr_disable(const struct aspeed_sig_expr *expr, |
270 | struct regmap * const *maps) | |
4d3d0e42 | 271 | { |
7d29ed88 AJ |
272 | int ret; |
273 | ||
274 | ret = aspeed_sig_expr_eval(expr, true, maps); | |
275 | if (ret < 0) | |
276 | return ret; | |
277 | ||
278 | if (ret) | |
279 | return aspeed_sig_expr_set(expr, false, maps); | |
5366f146 | 280 | |
7d29ed88 | 281 | return 0; |
4d3d0e42 AJ |
282 | } |
283 | ||
284 | /** | |
285 | * Disable a signal on a pin by disabling all provided signal expressions. | |
286 | * | |
287 | * @exprs: The list of signal expressions (from a priority level on a pin) | |
7d29ed88 | 288 | * @maps: The list of regmap instances for pinmux register access. |
4d3d0e42 | 289 | * |
b75dd872 | 290 | * Return: 0 if all expressions are disabled, otherwise a negative error code |
4d3d0e42 | 291 | */ |
7d29ed88 AJ |
292 | static int aspeed_disable_sig(const struct aspeed_sig_expr **exprs, |
293 | struct regmap * const *maps) | |
4d3d0e42 | 294 | { |
7d29ed88 | 295 | int ret = 0; |
4d3d0e42 AJ |
296 | |
297 | if (!exprs) | |
298 | return true; | |
299 | ||
7d29ed88 AJ |
300 | while (*exprs && !ret) { |
301 | ret = aspeed_sig_expr_disable(*exprs, maps); | |
4d3d0e42 AJ |
302 | exprs++; |
303 | } | |
304 | ||
7d29ed88 | 305 | return ret; |
4d3d0e42 AJ |
306 | } |
307 | ||
308 | /** | |
309 | * Search for the signal expression needed to enable the pin's signal for the | |
310 | * requested function. | |
311 | * | |
312 | * @exprs: List of signal expressions (haystack) | |
313 | * @name: The name of the requested function (needle) | |
314 | * | |
b75dd872 AJ |
315 | * Return: A pointer to the signal expression whose function tag matches the |
316 | * provided name, otherwise NULL. | |
4d3d0e42 AJ |
317 | * |
318 | */ | |
319 | static const struct aspeed_sig_expr *aspeed_find_expr_by_name( | |
320 | const struct aspeed_sig_expr **exprs, const char *name) | |
321 | { | |
322 | while (*exprs) { | |
323 | if (strcmp((*exprs)->function, name) == 0) | |
324 | return *exprs; | |
325 | exprs++; | |
326 | } | |
327 | ||
328 | return NULL; | |
329 | } | |
330 | ||
331 | static char *get_defined_attribute(const struct aspeed_pin_desc *pdesc, | |
332 | const char *(*get)( | |
333 | const struct aspeed_sig_expr *)) | |
334 | { | |
335 | char *found = NULL; | |
336 | size_t len = 0; | |
337 | const struct aspeed_sig_expr ***prios, **funcs, *expr; | |
338 | ||
339 | prios = pdesc->prios; | |
340 | ||
341 | while ((funcs = *prios)) { | |
342 | while ((expr = *funcs)) { | |
343 | const char *str = get(expr); | |
344 | size_t delta = strlen(str) + 2; | |
345 | char *expanded; | |
346 | ||
347 | expanded = krealloc(found, len + delta + 1, GFP_KERNEL); | |
348 | if (!expanded) { | |
349 | kfree(found); | |
350 | return expanded; | |
351 | } | |
352 | ||
353 | found = expanded; | |
354 | found[len] = '\0'; | |
355 | len += delta; | |
356 | ||
357 | strcat(found, str); | |
358 | strcat(found, ", "); | |
359 | ||
360 | funcs++; | |
361 | } | |
362 | prios++; | |
363 | } | |
364 | ||
365 | if (len < 2) { | |
366 | kfree(found); | |
367 | return NULL; | |
368 | } | |
369 | ||
370 | found[len - 2] = '\0'; | |
371 | ||
372 | return found; | |
373 | } | |
374 | ||
375 | static const char *aspeed_sig_expr_function(const struct aspeed_sig_expr *expr) | |
376 | { | |
377 | return expr->function; | |
378 | } | |
379 | ||
380 | static char *get_defined_functions(const struct aspeed_pin_desc *pdesc) | |
381 | { | |
382 | return get_defined_attribute(pdesc, aspeed_sig_expr_function); | |
383 | } | |
384 | ||
385 | static const char *aspeed_sig_expr_signal(const struct aspeed_sig_expr *expr) | |
386 | { | |
387 | return expr->signal; | |
388 | } | |
389 | ||
390 | static char *get_defined_signals(const struct aspeed_pin_desc *pdesc) | |
391 | { | |
392 | return get_defined_attribute(pdesc, aspeed_sig_expr_signal); | |
393 | } | |
394 | ||
395 | int aspeed_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int function, | |
396 | unsigned int group) | |
397 | { | |
398 | int i; | |
7d29ed88 | 399 | int ret; |
4d3d0e42 AJ |
400 | const struct aspeed_pinctrl_data *pdata = |
401 | pinctrl_dev_get_drvdata(pctldev); | |
402 | const struct aspeed_pin_group *pgroup = &pdata->groups[group]; | |
403 | const struct aspeed_pin_function *pfunc = | |
404 | &pdata->functions[function]; | |
405 | ||
406 | for (i = 0; i < pgroup->npins; i++) { | |
407 | int pin = pgroup->pins[i]; | |
408 | const struct aspeed_pin_desc *pdesc = pdata->pins[pin].drv_data; | |
409 | const struct aspeed_sig_expr *expr = NULL; | |
410 | const struct aspeed_sig_expr **funcs; | |
411 | const struct aspeed_sig_expr ***prios; | |
412 | ||
7d29ed88 AJ |
413 | pr_debug("Muxing pin %d for %s\n", pin, pfunc->name); |
414 | ||
4d3d0e42 AJ |
415 | if (!pdesc) |
416 | return -EINVAL; | |
417 | ||
418 | prios = pdesc->prios; | |
419 | ||
420 | if (!prios) | |
421 | continue; | |
422 | ||
423 | /* Disable functions at a higher priority than that requested */ | |
424 | while ((funcs = *prios)) { | |
425 | expr = aspeed_find_expr_by_name(funcs, pfunc->name); | |
426 | ||
427 | if (expr) | |
428 | break; | |
429 | ||
7d29ed88 AJ |
430 | ret = aspeed_disable_sig(funcs, pdata->maps); |
431 | if (ret) | |
432 | return ret; | |
4d3d0e42 AJ |
433 | |
434 | prios++; | |
435 | } | |
436 | ||
437 | if (!expr) { | |
438 | char *functions = get_defined_functions(pdesc); | |
439 | char *signals = get_defined_signals(pdesc); | |
440 | ||
441 | pr_warn("No function %s found on pin %s (%d). Found signal(s) %s for function(s) %s\n", | |
442 | pfunc->name, pdesc->name, pin, signals, | |
443 | functions); | |
444 | kfree(signals); | |
445 | kfree(functions); | |
446 | ||
447 | return -ENXIO; | |
448 | } | |
449 | ||
7d29ed88 AJ |
450 | ret = aspeed_sig_expr_enable(expr, pdata->maps); |
451 | if (ret) | |
452 | return ret; | |
4d3d0e42 AJ |
453 | } |
454 | ||
455 | return 0; | |
456 | } | |
457 | ||
458 | static bool aspeed_expr_is_gpio(const struct aspeed_sig_expr *expr) | |
459 | { | |
460 | /* | |
461 | * The signal type is GPIO if the signal name has "GPIO" as a prefix. | |
462 | * strncmp (rather than strcmp) is used to implement the prefix | |
463 | * requirement. | |
464 | * | |
465 | * expr->signal might look like "GPIOT3" in the GPIO case. | |
466 | */ | |
467 | return strncmp(expr->signal, "GPIO", 4) == 0; | |
468 | } | |
469 | ||
470 | static bool aspeed_gpio_in_exprs(const struct aspeed_sig_expr **exprs) | |
471 | { | |
472 | if (!exprs) | |
473 | return false; | |
474 | ||
475 | while (*exprs) { | |
476 | if (aspeed_expr_is_gpio(*exprs)) | |
477 | return true; | |
478 | exprs++; | |
479 | } | |
480 | ||
481 | return false; | |
482 | } | |
483 | ||
484 | int aspeed_gpio_request_enable(struct pinctrl_dev *pctldev, | |
485 | struct pinctrl_gpio_range *range, | |
486 | unsigned int offset) | |
487 | { | |
7d29ed88 | 488 | int ret; |
4d3d0e42 AJ |
489 | const struct aspeed_pinctrl_data *pdata = |
490 | pinctrl_dev_get_drvdata(pctldev); | |
491 | const struct aspeed_pin_desc *pdesc = pdata->pins[offset].drv_data; | |
492 | const struct aspeed_sig_expr ***prios, **funcs, *expr; | |
493 | ||
494 | if (!pdesc) | |
495 | return -EINVAL; | |
496 | ||
497 | prios = pdesc->prios; | |
498 | ||
499 | if (!prios) | |
500 | return -ENXIO; | |
501 | ||
502 | /* Disable any functions of higher priority than GPIO */ | |
503 | while ((funcs = *prios)) { | |
504 | if (aspeed_gpio_in_exprs(funcs)) | |
505 | break; | |
506 | ||
7d29ed88 AJ |
507 | ret = aspeed_disable_sig(funcs, pdata->maps); |
508 | if (ret) | |
509 | return ret; | |
4d3d0e42 AJ |
510 | |
511 | prios++; | |
512 | } | |
513 | ||
514 | if (!funcs) { | |
515 | char *signals = get_defined_signals(pdesc); | |
516 | ||
517 | pr_warn("No GPIO signal type found on pin %s (%d). Found: %s\n", | |
518 | pdesc->name, offset, signals); | |
519 | kfree(signals); | |
520 | ||
521 | return -ENXIO; | |
522 | } | |
523 | ||
524 | expr = *funcs; | |
525 | ||
526 | /* | |
527 | * Disabling all higher-priority expressions is enough to enable the | |
528 | * lowest-priority signal type. As such it has no associated | |
529 | * expression. | |
530 | */ | |
531 | if (!expr) | |
532 | return 0; | |
533 | ||
534 | /* | |
535 | * If GPIO is not the lowest priority signal type, assume there is only | |
536 | * one expression defined to enable the GPIO function | |
537 | */ | |
7d29ed88 | 538 | return aspeed_sig_expr_enable(expr, pdata->maps); |
4d3d0e42 AJ |
539 | } |
540 | ||
541 | int aspeed_pinctrl_probe(struct platform_device *pdev, | |
542 | struct pinctrl_desc *pdesc, | |
543 | struct aspeed_pinctrl_data *pdata) | |
544 | { | |
545 | struct device *parent; | |
546 | struct pinctrl_dev *pctl; | |
547 | ||
548 | parent = pdev->dev.parent; | |
549 | if (!parent) { | |
550 | dev_err(&pdev->dev, "No parent for syscon pincontroller\n"); | |
551 | return -ENODEV; | |
552 | } | |
553 | ||
7d29ed88 AJ |
554 | pdata->maps[ASPEED_IP_SCU] = syscon_node_to_regmap(parent->of_node); |
555 | if (IS_ERR(pdata->maps[ASPEED_IP_SCU])) { | |
4d3d0e42 | 556 | dev_err(&pdev->dev, "No regmap for syscon pincontroller parent\n"); |
7d29ed88 | 557 | return PTR_ERR(pdata->maps[ASPEED_IP_SCU]); |
4d3d0e42 AJ |
558 | } |
559 | ||
560 | pctl = pinctrl_register(pdesc, &pdev->dev, pdata); | |
561 | ||
562 | if (IS_ERR(pctl)) { | |
563 | dev_err(&pdev->dev, "Failed to register pinctrl\n"); | |
564 | return PTR_ERR(pctl); | |
565 | } | |
566 | ||
567 | platform_set_drvdata(pdev, pdata); | |
568 | ||
569 | return 0; | |
570 | } | |
7f354fd1 AJ |
571 | |
572 | static inline bool pin_in_config_range(unsigned int offset, | |
573 | const struct aspeed_pin_config *config) | |
574 | { | |
575 | return offset >= config->pins[0] && offset <= config->pins[1]; | |
576 | } | |
577 | ||
578 | static inline const struct aspeed_pin_config *find_pinconf_config( | |
579 | const struct aspeed_pinctrl_data *pdata, | |
580 | unsigned int offset, | |
581 | enum pin_config_param param) | |
582 | { | |
583 | unsigned int i; | |
584 | ||
585 | for (i = 0; i < pdata->nconfigs; i++) { | |
586 | if (param == pdata->configs[i].param && | |
587 | pin_in_config_range(offset, &pdata->configs[i])) | |
588 | return &pdata->configs[i]; | |
589 | } | |
590 | ||
591 | return NULL; | |
592 | } | |
593 | ||
594 | /** | |
595 | * @param: pinconf configuration parameter | |
596 | * @arg: The supported argument for @param, or -1 if any value is supported | |
74677701 | 597 | * @val: The register value to write to configure @arg for @param |
7f354fd1 AJ |
598 | * |
599 | * The map is to be used in conjunction with the configuration array supplied | |
600 | * by the driver implementation. | |
601 | */ | |
602 | struct aspeed_pin_config_map { | |
603 | enum pin_config_param param; | |
604 | s32 arg; | |
605 | u32 val; | |
606 | }; | |
607 | ||
608 | enum aspeed_pin_config_map_type { MAP_TYPE_ARG, MAP_TYPE_VAL }; | |
609 | ||
610 | /* Aspeed consistently both: | |
611 | * | |
612 | * 1. Defines "disable bits" for internal pull-downs | |
613 | * 2. Uses 8mA or 16mA drive strengths | |
614 | */ | |
615 | static const struct aspeed_pin_config_map pin_config_map[] = { | |
616 | { PIN_CONFIG_BIAS_PULL_DOWN, 0, 1 }, | |
617 | { PIN_CONFIG_BIAS_PULL_DOWN, -1, 0 }, | |
618 | { PIN_CONFIG_BIAS_DISABLE, -1, 1 }, | |
619 | { PIN_CONFIG_DRIVE_STRENGTH, 8, 0 }, | |
620 | { PIN_CONFIG_DRIVE_STRENGTH, 16, 1 }, | |
621 | }; | |
622 | ||
623 | static const struct aspeed_pin_config_map *find_pinconf_map( | |
624 | enum pin_config_param param, | |
625 | enum aspeed_pin_config_map_type type, | |
626 | s64 value) | |
627 | { | |
628 | int i; | |
629 | ||
630 | for (i = 0; i < ARRAY_SIZE(pin_config_map); i++) { | |
631 | const struct aspeed_pin_config_map *elem; | |
632 | bool match; | |
633 | ||
634 | elem = &pin_config_map[i]; | |
635 | ||
636 | switch (type) { | |
637 | case MAP_TYPE_ARG: | |
638 | match = (elem->arg == -1 || elem->arg == value); | |
639 | break; | |
640 | case MAP_TYPE_VAL: | |
641 | match = (elem->val == value); | |
642 | break; | |
643 | } | |
644 | ||
645 | if (param == elem->param && match) | |
646 | return elem; | |
647 | } | |
648 | ||
649 | return NULL; | |
650 | } | |
651 | ||
652 | int aspeed_pin_config_get(struct pinctrl_dev *pctldev, unsigned int offset, | |
653 | unsigned long *config) | |
654 | { | |
655 | const enum pin_config_param param = pinconf_to_config_param(*config); | |
656 | const struct aspeed_pin_config_map *pmap; | |
657 | const struct aspeed_pinctrl_data *pdata; | |
658 | const struct aspeed_pin_config *pconf; | |
659 | unsigned int val; | |
660 | int rc = 0; | |
661 | u32 arg; | |
662 | ||
663 | pdata = pinctrl_dev_get_drvdata(pctldev); | |
664 | pconf = find_pinconf_config(pdata, offset, param); | |
665 | if (!pconf) | |
666 | return -ENOTSUPP; | |
667 | ||
668 | rc = regmap_read(pdata->maps[ASPEED_IP_SCU], pconf->reg, &val); | |
669 | if (rc < 0) | |
670 | return rc; | |
671 | ||
672 | pmap = find_pinconf_map(param, MAP_TYPE_VAL, | |
673 | (val & BIT(pconf->bit)) >> pconf->bit); | |
674 | ||
675 | if (!pmap) | |
676 | return -EINVAL; | |
677 | ||
678 | if (param == PIN_CONFIG_DRIVE_STRENGTH) | |
679 | arg = (u32) pmap->arg; | |
680 | else if (param == PIN_CONFIG_BIAS_PULL_DOWN) | |
681 | arg = !!pmap->arg; | |
682 | else | |
683 | arg = 1; | |
684 | ||
685 | if (!arg) | |
686 | return -EINVAL; | |
687 | ||
688 | *config = pinconf_to_config_packed(param, arg); | |
689 | ||
690 | return 0; | |
691 | } | |
692 | ||
693 | int aspeed_pin_config_set(struct pinctrl_dev *pctldev, unsigned int offset, | |
694 | unsigned long *configs, unsigned int num_configs) | |
695 | { | |
696 | const struct aspeed_pinctrl_data *pdata; | |
697 | unsigned int i; | |
698 | int rc = 0; | |
699 | ||
700 | pdata = pinctrl_dev_get_drvdata(pctldev); | |
701 | ||
702 | for (i = 0; i < num_configs; i++) { | |
703 | const struct aspeed_pin_config_map *pmap; | |
704 | const struct aspeed_pin_config *pconf; | |
705 | enum pin_config_param param; | |
706 | unsigned int val; | |
707 | u32 arg; | |
708 | ||
709 | param = pinconf_to_config_param(configs[i]); | |
710 | arg = pinconf_to_config_argument(configs[i]); | |
711 | ||
712 | pconf = find_pinconf_config(pdata, offset, param); | |
713 | if (!pconf) | |
714 | return -ENOTSUPP; | |
715 | ||
716 | pmap = find_pinconf_map(param, MAP_TYPE_ARG, arg); | |
717 | ||
27d91e80 | 718 | if (WARN_ON(!pmap)) |
7f354fd1 AJ |
719 | return -EINVAL; |
720 | ||
721 | val = pmap->val << pconf->bit; | |
722 | ||
723 | rc = regmap_update_bits(pdata->maps[ASPEED_IP_SCU], pconf->reg, | |
724 | BIT(pconf->bit), val); | |
725 | ||
726 | if (rc < 0) | |
727 | return rc; | |
728 | ||
729 | pr_debug("%s: Set SCU%02X[%d]=%d for param %d(=%d) on pin %d\n", | |
730 | __func__, pconf->reg, pconf->bit, pmap->val, | |
731 | param, arg, offset); | |
732 | } | |
733 | ||
734 | return 0; | |
735 | } | |
736 | ||
737 | int aspeed_pin_config_group_get(struct pinctrl_dev *pctldev, | |
738 | unsigned int selector, | |
739 | unsigned long *config) | |
740 | { | |
741 | const unsigned int *pins; | |
742 | unsigned int npins; | |
743 | int rc; | |
744 | ||
745 | rc = aspeed_pinctrl_get_group_pins(pctldev, selector, &pins, &npins); | |
746 | if (rc < 0) | |
747 | return rc; | |
748 | ||
749 | if (!npins) | |
750 | return -ENODEV; | |
751 | ||
752 | rc = aspeed_pin_config_get(pctldev, pins[0], config); | |
753 | ||
754 | return rc; | |
755 | } | |
756 | ||
757 | int aspeed_pin_config_group_set(struct pinctrl_dev *pctldev, | |
758 | unsigned int selector, | |
759 | unsigned long *configs, | |
760 | unsigned int num_configs) | |
761 | { | |
762 | const unsigned int *pins; | |
763 | unsigned int npins; | |
764 | int rc; | |
765 | int i; | |
766 | ||
767 | pr_debug("%s: Fetching pins for group selector %d\n", | |
768 | __func__, selector); | |
769 | rc = aspeed_pinctrl_get_group_pins(pctldev, selector, &pins, &npins); | |
770 | if (rc < 0) | |
771 | return rc; | |
772 | ||
773 | for (i = 0; i < npins; i++) { | |
774 | rc = aspeed_pin_config_set(pctldev, pins[i], configs, | |
775 | num_configs); | |
776 | if (rc < 0) | |
777 | return rc; | |
778 | } | |
779 | ||
780 | return 0; | |
781 | } |