diff options
Diffstat (limited to 'arch/arm/mach-tegra/pinmux.c')
-rw-r--r-- | arch/arm/mach-tegra/pinmux.c | 1056 |
1 files changed, 1056 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/pinmux.c b/arch/arm/mach-tegra/pinmux.c new file mode 100644 index 00000000000..245aa627fdf --- /dev/null +++ b/arch/arm/mach-tegra/pinmux.c | |||
@@ -0,0 +1,1056 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-tegra/pinmux.c | ||
3 | * | ||
4 | * Copyright (C) 2010 Google, Inc. | ||
5 | * Copyright (C) 2011 NVIDIA Corporation. | ||
6 | * | ||
7 | * This software is licensed under the terms of the GNU General Public | ||
8 | * License version 2, as published by the Free Software Foundation, and | ||
9 | * may be copied, distributed, and modified under those terms. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <linux/init.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/errno.h> | ||
22 | #include <linux/spinlock.h> | ||
23 | #include <linux/io.h> | ||
24 | |||
25 | #include <mach/iomap.h> | ||
26 | #include <mach/pinmux.h> | ||
27 | |||
28 | #define HSM_EN(reg) (((reg) >> 2) & 0x1) | ||
29 | #define SCHMT_EN(reg) (((reg) >> 3) & 0x1) | ||
30 | #define LPMD(reg) (((reg) >> 4) & 0x3) | ||
31 | #define DRVDN(reg, offset) (((reg) >> offset) & 0x1f) | ||
32 | #define DRVUP(reg, offset) (((reg) >> offset) & 0x1f) | ||
33 | #define SLWR(reg, offset) (((reg) >> offset) & 0x3) | ||
34 | #define SLWF(reg, offset) (((reg) >> offset) & 0x3) | ||
35 | |||
36 | static const struct tegra_pingroup_desc *const pingroups = tegra_soc_pingroups; | ||
37 | static const struct tegra_drive_pingroup_desc *const drive_pingroups = tegra_soc_drive_pingroups; | ||
38 | static const int *gpio_to_pingroups_map = gpio_to_pingroup; | ||
39 | |||
40 | static char *tegra_mux_names[TEGRA_MAX_MUX] = { | ||
41 | #define TEGRA_MUX(mux) [TEGRA_MUX_##mux] = #mux, | ||
42 | TEGRA_MUX_LIST | ||
43 | #undef TEGRA_MUX | ||
44 | [TEGRA_MUX_SAFE] = "<safe>", | ||
45 | }; | ||
46 | |||
47 | static const char *tegra_drive_names[TEGRA_MAX_DRIVE] = { | ||
48 | [TEGRA_DRIVE_DIV_8] = "DIV_8", | ||
49 | [TEGRA_DRIVE_DIV_4] = "DIV_4", | ||
50 | [TEGRA_DRIVE_DIV_2] = "DIV_2", | ||
51 | [TEGRA_DRIVE_DIV_1] = "DIV_1", | ||
52 | }; | ||
53 | |||
54 | static const char *tegra_slew_names[TEGRA_MAX_SLEW] = { | ||
55 | [TEGRA_SLEW_FASTEST] = "FASTEST", | ||
56 | [TEGRA_SLEW_FAST] = "FAST", | ||
57 | [TEGRA_SLEW_SLOW] = "SLOW", | ||
58 | [TEGRA_SLEW_SLOWEST] = "SLOWEST", | ||
59 | }; | ||
60 | |||
61 | static DEFINE_SPINLOCK(mux_lock); | ||
62 | |||
63 | static const char *pingroup_name(enum tegra_pingroup pg) | ||
64 | { | ||
65 | if (pg < 0 || pg >= TEGRA_MAX_PINGROUP) | ||
66 | return "<UNKNOWN>"; | ||
67 | |||
68 | return pingroups[pg].name; | ||
69 | } | ||
70 | |||
71 | static const char *func_name(enum tegra_mux_func func) | ||
72 | { | ||
73 | if (func == TEGRA_MUX_RSVD1) | ||
74 | return "RSVD1"; | ||
75 | |||
76 | if (func == TEGRA_MUX_RSVD2) | ||
77 | return "RSVD2"; | ||
78 | |||
79 | if (func == TEGRA_MUX_RSVD3) | ||
80 | return "RSVD3"; | ||
81 | |||
82 | if (func == TEGRA_MUX_RSVD4) | ||
83 | return "RSVD4"; | ||
84 | |||
85 | if (func == TEGRA_MUX_INVALID) | ||
86 | return "INVALID"; | ||
87 | |||
88 | if (func < 0 || func >= TEGRA_MAX_MUX) | ||
89 | return "<UNKNOWN>"; | ||
90 | |||
91 | return tegra_mux_names[func]; | ||
92 | } | ||
93 | |||
94 | |||
95 | static const char *tri_name(unsigned long val) | ||
96 | { | ||
97 | return val ? "TRISTATE" : "NORMAL"; | ||
98 | } | ||
99 | |||
100 | static const char *pupd_name(unsigned long val) | ||
101 | { | ||
102 | switch (val) { | ||
103 | case 0: | ||
104 | return "NORMAL"; | ||
105 | |||
106 | case 1: | ||
107 | return "PULL_DOWN"; | ||
108 | |||
109 | case 2: | ||
110 | return "PULL_UP"; | ||
111 | |||
112 | default: | ||
113 | return "RSVD"; | ||
114 | } | ||
115 | } | ||
116 | |||
117 | #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) | ||
118 | static const char *lock_name(unsigned long val) | ||
119 | { | ||
120 | switch (val) { | ||
121 | case TEGRA_PIN_LOCK_DEFAULT: | ||
122 | return "LOCK_DEFUALT"; | ||
123 | |||
124 | case TEGRA_PIN_LOCK_DISABLE: | ||
125 | return "LOCK_DISABLE"; | ||
126 | |||
127 | case TEGRA_PIN_LOCK_ENABLE: | ||
128 | return "LOCK_ENABLE"; | ||
129 | default: | ||
130 | return "LOCK_DEFAULT"; | ||
131 | } | ||
132 | } | ||
133 | |||
134 | static const char *od_name(unsigned long val) | ||
135 | { | ||
136 | switch (val) { | ||
137 | case TEGRA_PIN_OD_DEFAULT: | ||
138 | return "OD_DEFAULT"; | ||
139 | |||
140 | case TEGRA_PIN_OD_DISABLE: | ||
141 | return "OD_DISABLE"; | ||
142 | |||
143 | case TEGRA_PIN_OD_ENABLE: | ||
144 | return "OD_ENABLE"; | ||
145 | default: | ||
146 | return "OD_DEFAULT"; | ||
147 | } | ||
148 | } | ||
149 | |||
150 | static const char *ioreset_name(unsigned long val) | ||
151 | { | ||
152 | switch (val) { | ||
153 | case TEGRA_PIN_IO_RESET_DEFAULT: | ||
154 | return "IO_RESET_DEFAULT"; | ||
155 | |||
156 | case TEGRA_PIN_IO_RESET_DISABLE: | ||
157 | return "IO_RESET_DISABLE"; | ||
158 | |||
159 | case TEGRA_PIN_IO_RESET_ENABLE: | ||
160 | return "IO_RESET_ENABLE"; | ||
161 | default: | ||
162 | return "IO_RESET_DEFAULT"; | ||
163 | } | ||
164 | } | ||
165 | #endif | ||
166 | |||
167 | #if defined(TEGRA_PINMUX_HAS_IO_DIRECTION) | ||
168 | static const char *io_name(unsigned long val) | ||
169 | { | ||
170 | switch (val) { | ||
171 | case 0: | ||
172 | return "OUTPUT"; | ||
173 | |||
174 | case 1: | ||
175 | return "INPUT"; | ||
176 | |||
177 | default: | ||
178 | return "RSVD"; | ||
179 | } | ||
180 | } | ||
181 | #endif | ||
182 | |||
183 | static inline unsigned long pg_readl(unsigned long offset) | ||
184 | { | ||
185 | return readl(IO_TO_VIRT(TEGRA_APB_MISC_BASE) + offset); | ||
186 | } | ||
187 | |||
188 | static inline void pg_writel(unsigned long value, unsigned long offset) | ||
189 | { | ||
190 | writel(value, IO_TO_VIRT(TEGRA_APB_MISC_BASE) + offset); | ||
191 | } | ||
192 | |||
193 | int tegra_pinmux_get_pingroup(int gpio_nr) | ||
194 | { | ||
195 | return gpio_to_pingroups_map[gpio_nr]; | ||
196 | } | ||
197 | EXPORT_SYMBOL_GPL(tegra_pinmux_get_pingroup); | ||
198 | |||
199 | static int tegra_pinmux_set_func(const struct tegra_pingroup_config *config) | ||
200 | { | ||
201 | int mux = -1; | ||
202 | int i; | ||
203 | int find = 0; | ||
204 | unsigned long reg; | ||
205 | unsigned long flags; | ||
206 | enum tegra_pingroup pg = config->pingroup; | ||
207 | enum tegra_mux_func func = config->func; | ||
208 | |||
209 | if (pg < 0 || pg >= TEGRA_MAX_PINGROUP) | ||
210 | return -ERANGE; | ||
211 | |||
212 | if (pingroups[pg].mux_reg <= 0) | ||
213 | return -EINVAL; | ||
214 | |||
215 | if (func == TEGRA_MUX_INVALID) { | ||
216 | pr_err("The pingroup %s is not recommended for option %s\n", | ||
217 | pingroup_name(pg), func_name(func)); | ||
218 | WARN_ON(1); | ||
219 | return -EINVAL; | ||
220 | } | ||
221 | |||
222 | if (func < 0) | ||
223 | return -ERANGE; | ||
224 | |||
225 | if (func == TEGRA_MUX_SAFE) | ||
226 | func = pingroups[pg].func_safe; | ||
227 | |||
228 | if (func & TEGRA_MUX_RSVD) { | ||
229 | for (i = 0; i < 4; i++) { | ||
230 | if (pingroups[pg].funcs[i] & TEGRA_MUX_RSVD) | ||
231 | mux = i; | ||
232 | |||
233 | if (pingroups[pg].funcs[i] == func) { | ||
234 | mux = i; | ||
235 | find = 1; | ||
236 | break; | ||
237 | } | ||
238 | } | ||
239 | } else { | ||
240 | for (i = 0; i < 4; i++) { | ||
241 | if (pingroups[pg].funcs[i] == func) { | ||
242 | mux = i; | ||
243 | find = 1; | ||
244 | break; | ||
245 | } | ||
246 | } | ||
247 | } | ||
248 | |||
249 | if (mux < 0) { | ||
250 | pr_err("The pingroup %s is not supported option %s\n", | ||
251 | pingroup_name(pg), func_name(func)); | ||
252 | WARN_ON(1); | ||
253 | return -EINVAL; | ||
254 | } | ||
255 | |||
256 | if (!find) | ||
257 | pr_warn("The pingroup %s was configured to %s instead of %s\n", | ||
258 | pingroup_name(pg), func_name(pingroups[pg].funcs[mux]), | ||
259 | func_name(func)); | ||
260 | |||
261 | spin_lock_irqsave(&mux_lock, flags); | ||
262 | |||
263 | reg = pg_readl(pingroups[pg].mux_reg); | ||
264 | reg &= ~(0x3 << pingroups[pg].mux_bit); | ||
265 | reg |= mux << pingroups[pg].mux_bit; | ||
266 | #if defined(TEGRA_PINMUX_HAS_IO_DIRECTION) | ||
267 | reg &= ~(0x1 << 5); | ||
268 | reg |= ((config->io & 0x1) << 5); | ||
269 | #endif | ||
270 | pg_writel(reg, pingroups[pg].mux_reg); | ||
271 | |||
272 | spin_unlock_irqrestore(&mux_lock, flags); | ||
273 | |||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | int tegra_pinmux_get_func(enum tegra_pingroup pg) | ||
278 | { | ||
279 | int mux = -1; | ||
280 | unsigned long reg; | ||
281 | unsigned long flags; | ||
282 | |||
283 | if (pg < 0 || pg >= TEGRA_MAX_PINGROUP) | ||
284 | return -ERANGE; | ||
285 | |||
286 | if (pingroups[pg].mux_reg <= 0) | ||
287 | return -EINVAL; | ||
288 | |||
289 | spin_lock_irqsave(&mux_lock, flags); | ||
290 | |||
291 | reg = pg_readl(pingroups[pg].mux_reg); | ||
292 | mux = (reg >> pingroups[pg].mux_bit) & 0x3; | ||
293 | |||
294 | spin_unlock_irqrestore(&mux_lock, flags); | ||
295 | |||
296 | return mux; | ||
297 | } | ||
298 | |||
299 | int tegra_pinmux_set_tristate(enum tegra_pingroup pg, | ||
300 | enum tegra_tristate tristate) | ||
301 | { | ||
302 | unsigned long reg; | ||
303 | unsigned long flags; | ||
304 | |||
305 | if (pg < 0 || pg >= TEGRA_MAX_PINGROUP) | ||
306 | return -ERANGE; | ||
307 | |||
308 | if (pingroups[pg].tri_reg <= 0) | ||
309 | return -EINVAL; | ||
310 | |||
311 | spin_lock_irqsave(&mux_lock, flags); | ||
312 | |||
313 | reg = pg_readl(pingroups[pg].tri_reg); | ||
314 | reg &= ~(0x1 << pingroups[pg].tri_bit); | ||
315 | if (tristate) | ||
316 | reg |= 1 << pingroups[pg].tri_bit; | ||
317 | pg_writel(reg, pingroups[pg].tri_reg); | ||
318 | |||
319 | spin_unlock_irqrestore(&mux_lock, flags); | ||
320 | |||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | int tegra_pinmux_set_io(enum tegra_pingroup pg, | ||
325 | enum tegra_pin_io input) | ||
326 | { | ||
327 | #if defined(TEGRA_PINMUX_HAS_IO_DIRECTION) | ||
328 | unsigned long io; | ||
329 | |||
330 | if (pg < 0 || pg >= TEGRA_MAX_PINGROUP) | ||
331 | return -ERANGE; | ||
332 | |||
333 | io = pg_readl(pingroups[pg].mux_reg); | ||
334 | if (input) | ||
335 | io |= 0x20; | ||
336 | else | ||
337 | io &= ~(1 << 5); | ||
338 | pg_writel(io, pingroups[pg].mux_reg); | ||
339 | #endif | ||
340 | return 0; | ||
341 | } | ||
342 | EXPORT_SYMBOL_GPL(tegra_pinmux_set_io); | ||
343 | |||
344 | #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) | ||
345 | static int tegra_pinmux_set_lock(enum tegra_pingroup pg, | ||
346 | enum tegra_pin_lock lock) | ||
347 | { | ||
348 | unsigned long reg; | ||
349 | unsigned long flags; | ||
350 | |||
351 | if (pg < 0 || pg >= TEGRA_MAX_PINGROUP) | ||
352 | return -ERANGE; | ||
353 | |||
354 | if (pingroups[pg].mux_reg <= 0) | ||
355 | return -EINVAL; | ||
356 | |||
357 | if ((lock == TEGRA_PIN_LOCK_DEFAULT) || (pingroups[pg].lock_bit < 0)) | ||
358 | return 0; | ||
359 | |||
360 | spin_lock_irqsave(&mux_lock, flags); | ||
361 | |||
362 | reg = pg_readl(pingroups[pg].mux_reg); | ||
363 | reg &= ~(0x1 << pingroups[pg].lock_bit); | ||
364 | if (lock == TEGRA_PIN_LOCK_ENABLE) | ||
365 | reg |= (0x1 << pingroups[pg].lock_bit); | ||
366 | |||
367 | pg_writel(reg, pingroups[pg].mux_reg); | ||
368 | |||
369 | spin_unlock_irqrestore(&mux_lock, flags); | ||
370 | return 0; | ||
371 | } | ||
372 | |||
373 | static int tegra_pinmux_set_od(enum tegra_pingroup pg, | ||
374 | enum tegra_pin_od od) | ||
375 | { | ||
376 | unsigned long reg; | ||
377 | unsigned long flags; | ||
378 | |||
379 | if (pg < 0 || pg >= TEGRA_MAX_PINGROUP) | ||
380 | return -ERANGE; | ||
381 | |||
382 | if (pingroups[pg].mux_reg <= 0) | ||
383 | return -EINVAL; | ||
384 | |||
385 | if ((od == TEGRA_PIN_OD_DEFAULT) || (pingroups[pg].od_bit < 0)) | ||
386 | return 0; | ||
387 | |||
388 | spin_lock_irqsave(&mux_lock, flags); | ||
389 | |||
390 | reg = pg_readl(pingroups[pg].mux_reg); | ||
391 | reg &= ~(0x1 << pingroups[pg].od_bit); | ||
392 | if (od == TEGRA_PIN_OD_ENABLE) | ||
393 | reg |= 1 << pingroups[pg].od_bit; | ||
394 | |||
395 | pg_writel(reg, pingroups[pg].mux_reg); | ||
396 | |||
397 | spin_unlock_irqrestore(&mux_lock, flags); | ||
398 | |||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | static int tegra_pinmux_set_ioreset(enum tegra_pingroup pg, | ||
403 | enum tegra_pin_ioreset ioreset) | ||
404 | { | ||
405 | unsigned long reg; | ||
406 | unsigned long flags; | ||
407 | |||
408 | if (pg < 0 || pg >= TEGRA_MAX_PINGROUP) | ||
409 | return -ERANGE; | ||
410 | |||
411 | if (pingroups[pg].mux_reg <= 0) | ||
412 | return -EINVAL; | ||
413 | |||
414 | if ((ioreset == TEGRA_PIN_IO_RESET_DEFAULT) || (pingroups[pg].ioreset_bit < 0)) | ||
415 | return 0; | ||
416 | |||
417 | spin_lock_irqsave(&mux_lock, flags); | ||
418 | |||
419 | reg = pg_readl(pingroups[pg].mux_reg); | ||
420 | reg &= ~(0x1 << pingroups[pg].ioreset_bit); | ||
421 | if (ioreset == TEGRA_PIN_IO_RESET_ENABLE) | ||
422 | reg |= 1 << pingroups[pg].ioreset_bit; | ||
423 | |||
424 | pg_writel(reg, pingroups[pg].mux_reg); | ||
425 | |||
426 | spin_unlock_irqrestore(&mux_lock, flags); | ||
427 | |||
428 | return 0; | ||
429 | } | ||
430 | #endif | ||
431 | |||
432 | int tegra_pinmux_set_pullupdown(enum tegra_pingroup pg, | ||
433 | enum tegra_pullupdown pupd) | ||
434 | { | ||
435 | unsigned long reg; | ||
436 | unsigned long flags; | ||
437 | |||
438 | if (pg < 0 || pg >= TEGRA_MAX_PINGROUP) | ||
439 | return -ERANGE; | ||
440 | |||
441 | if (pingroups[pg].pupd_reg <= 0) | ||
442 | return -EINVAL; | ||
443 | |||
444 | if (pupd != TEGRA_PUPD_NORMAL && | ||
445 | pupd != TEGRA_PUPD_PULL_DOWN && | ||
446 | pupd != TEGRA_PUPD_PULL_UP) | ||
447 | return -EINVAL; | ||
448 | |||
449 | |||
450 | spin_lock_irqsave(&mux_lock, flags); | ||
451 | |||
452 | reg = pg_readl(pingroups[pg].pupd_reg); | ||
453 | reg &= ~(0x3 << pingroups[pg].pupd_bit); | ||
454 | reg |= pupd << pingroups[pg].pupd_bit; | ||
455 | pg_writel(reg, pingroups[pg].pupd_reg); | ||
456 | |||
457 | spin_unlock_irqrestore(&mux_lock, flags); | ||
458 | |||
459 | return 0; | ||
460 | } | ||
461 | |||
462 | static void tegra_pinmux_config_pingroup(const struct tegra_pingroup_config *config) | ||
463 | { | ||
464 | enum tegra_pingroup pingroup = config->pingroup; | ||
465 | enum tegra_mux_func func = config->func; | ||
466 | enum tegra_pullupdown pupd = config->pupd; | ||
467 | enum tegra_tristate tristate = config->tristate; | ||
468 | #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) | ||
469 | enum tegra_pin_lock lock = config->lock; | ||
470 | enum tegra_pin_od od = config->od; | ||
471 | enum tegra_pin_ioreset ioreset = config->ioreset; | ||
472 | #endif | ||
473 | int err; | ||
474 | |||
475 | if (pingroups[pingroup].mux_reg > 0) { | ||
476 | err = tegra_pinmux_set_func(config); | ||
477 | if (err < 0) | ||
478 | pr_err("pinmux: can't set pingroup %s func to %s: %d\n", | ||
479 | pingroup_name(pingroup), func_name(func), err); | ||
480 | } | ||
481 | |||
482 | if (pingroups[pingroup].pupd_reg > 0) { | ||
483 | err = tegra_pinmux_set_pullupdown(pingroup, pupd); | ||
484 | if (err < 0) | ||
485 | pr_err("pinmux: can't set pingroup %s pullupdown to %s: %d\n", | ||
486 | pingroup_name(pingroup), pupd_name(pupd), err); | ||
487 | } | ||
488 | |||
489 | if (pingroups[pingroup].tri_reg > 0) { | ||
490 | err = tegra_pinmux_set_tristate(pingroup, tristate); | ||
491 | if (err < 0) | ||
492 | pr_err("pinmux: can't set pingroup %s tristate to %s: %d\n", | ||
493 | pingroup_name(pingroup), tri_name(func), err); | ||
494 | } | ||
495 | |||
496 | #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) | ||
497 | if (pingroups[pingroup].mux_reg > 0) { | ||
498 | err = tegra_pinmux_set_lock(pingroup, lock); | ||
499 | if (err < 0) | ||
500 | pr_err("pinmux: can't set pingroup %s lock to %s: %d\n", | ||
501 | pingroup_name(pingroup), lock_name(func), err); | ||
502 | } | ||
503 | |||
504 | if (pingroups[pingroup].mux_reg > 0) { | ||
505 | err = tegra_pinmux_set_od(pingroup, od); | ||
506 | if (err < 0) | ||
507 | pr_err("pinmux: can't set pingroup %s od to %s: %d\n", | ||
508 | pingroup_name(pingroup), od_name(func), err); | ||
509 | } | ||
510 | |||
511 | if (pingroups[pingroup].mux_reg > 0) { | ||
512 | err = tegra_pinmux_set_ioreset(pingroup, ioreset); | ||
513 | if (err < 0) | ||
514 | pr_err("pinmux: can't set pingroup %s ioreset to %s: %d\n", | ||
515 | pingroup_name(pingroup), ioreset_name(func), err); | ||
516 | } | ||
517 | #endif | ||
518 | } | ||
519 | |||
520 | void tegra_pinmux_config_table(const struct tegra_pingroup_config *config, int len) | ||
521 | { | ||
522 | int i; | ||
523 | |||
524 | for (i = 0; i < len; i++) | ||
525 | tegra_pinmux_config_pingroup(&config[i]); | ||
526 | } | ||
527 | EXPORT_SYMBOL(tegra_pinmux_config_table); | ||
528 | |||
529 | static const char *drive_pinmux_name(enum tegra_drive_pingroup pg) | ||
530 | { | ||
531 | if (pg < 0 || pg >= TEGRA_MAX_DRIVE_PINGROUP) | ||
532 | return "<UNKNOWN>"; | ||
533 | |||
534 | return drive_pingroups[pg].name; | ||
535 | } | ||
536 | |||
537 | static const char *enable_name(unsigned long val) | ||
538 | { | ||
539 | return val ? "ENABLE" : "DISABLE"; | ||
540 | } | ||
541 | |||
542 | static const char *drive_name(unsigned long val) | ||
543 | { | ||
544 | if (val >= TEGRA_MAX_DRIVE) | ||
545 | return "<UNKNOWN>"; | ||
546 | |||
547 | return tegra_drive_names[val]; | ||
548 | } | ||
549 | |||
550 | static const char *slew_name(unsigned long val) | ||
551 | { | ||
552 | if (val >= TEGRA_MAX_SLEW) | ||
553 | return "<UNKNOWN>"; | ||
554 | |||
555 | return tegra_slew_names[val]; | ||
556 | } | ||
557 | |||
558 | static int tegra_drive_pinmux_set_hsm(enum tegra_drive_pingroup pg, | ||
559 | enum tegra_hsm hsm) | ||
560 | { | ||
561 | unsigned long flags; | ||
562 | u32 reg; | ||
563 | if (pg < 0 || pg >= TEGRA_MAX_DRIVE_PINGROUP) | ||
564 | return -ERANGE; | ||
565 | |||
566 | if (hsm != TEGRA_HSM_ENABLE && hsm != TEGRA_HSM_DISABLE) | ||
567 | return -EINVAL; | ||
568 | |||
569 | spin_lock_irqsave(&mux_lock, flags); | ||
570 | |||
571 | reg = pg_readl(drive_pingroups[pg].reg); | ||
572 | if (hsm == TEGRA_HSM_ENABLE) | ||
573 | reg |= (1 << 2); | ||
574 | else | ||
575 | reg &= ~(1 << 2); | ||
576 | pg_writel(reg, drive_pingroups[pg].reg); | ||
577 | |||
578 | spin_unlock_irqrestore(&mux_lock, flags); | ||
579 | |||
580 | return 0; | ||
581 | } | ||
582 | |||
583 | static int tegra_drive_pinmux_set_schmitt(enum tegra_drive_pingroup pg, | ||
584 | enum tegra_schmitt schmitt) | ||
585 | { | ||
586 | unsigned long flags; | ||
587 | u32 reg; | ||
588 | if (pg < 0 || pg >= TEGRA_MAX_DRIVE_PINGROUP) | ||
589 | return -ERANGE; | ||
590 | |||
591 | if (schmitt != TEGRA_SCHMITT_ENABLE && schmitt != TEGRA_SCHMITT_DISABLE) | ||
592 | return -EINVAL; | ||
593 | |||
594 | spin_lock_irqsave(&mux_lock, flags); | ||
595 | |||
596 | reg = pg_readl(drive_pingroups[pg].reg); | ||
597 | if (schmitt == TEGRA_SCHMITT_ENABLE) | ||
598 | reg |= (1 << 3); | ||
599 | else | ||
600 | reg &= ~(1 << 3); | ||
601 | pg_writel(reg, drive_pingroups[pg].reg); | ||
602 | |||
603 | spin_unlock_irqrestore(&mux_lock, flags); | ||
604 | |||
605 | return 0; | ||
606 | } | ||
607 | |||
608 | static int tegra_drive_pinmux_set_drive(enum tegra_drive_pingroup pg, | ||
609 | enum tegra_drive drive) | ||
610 | { | ||
611 | unsigned long flags; | ||
612 | u32 reg; | ||
613 | if (pg < 0 || pg >= TEGRA_MAX_DRIVE_PINGROUP) | ||
614 | return -ERANGE; | ||
615 | |||
616 | if (drive < 0 || drive >= TEGRA_MAX_DRIVE) | ||
617 | return -EINVAL; | ||
618 | |||
619 | spin_lock_irqsave(&mux_lock, flags); | ||
620 | |||
621 | reg = pg_readl(drive_pingroups[pg].reg); | ||
622 | reg &= ~(0x3 << 4); | ||
623 | reg |= drive << 4; | ||
624 | pg_writel(reg, drive_pingroups[pg].reg); | ||
625 | |||
626 | spin_unlock_irqrestore(&mux_lock, flags); | ||
627 | |||
628 | return 0; | ||
629 | } | ||
630 | |||
631 | static int tegra_drive_pinmux_set_pull_down(enum tegra_drive_pingroup pg, | ||
632 | enum tegra_pull_strength pull_down) | ||
633 | { | ||
634 | unsigned long flags; | ||
635 | u32 reg; | ||
636 | |||
637 | if (pg < 0 || pg >= TEGRA_MAX_DRIVE_PINGROUP) | ||
638 | return -ERANGE; | ||
639 | |||
640 | if (pull_down < 0 || pull_down >= TEGRA_MAX_PULL) | ||
641 | return -EINVAL; | ||
642 | |||
643 | spin_lock_irqsave(&mux_lock, flags); | ||
644 | |||
645 | reg = pg_readl(drive_pingroups[pg].reg); | ||
646 | reg &= ~(drive_pingroups[pg].drvdown_mask << | ||
647 | drive_pingroups[pg].drvdown_offset); | ||
648 | reg |= pull_down << drive_pingroups[pg].drvdown_offset; | ||
649 | pg_writel(reg, drive_pingroups[pg].reg); | ||
650 | |||
651 | spin_unlock_irqrestore(&mux_lock, flags); | ||
652 | |||
653 | return 0; | ||
654 | } | ||
655 | |||
656 | static int tegra_drive_pinmux_set_pull_up(enum tegra_drive_pingroup pg, | ||
657 | enum tegra_pull_strength pull_up) | ||
658 | { | ||
659 | unsigned long flags; | ||
660 | u32 reg; | ||
661 | |||
662 | if (pg < 0 || pg >= TEGRA_MAX_DRIVE_PINGROUP) | ||
663 | return -ERANGE; | ||
664 | |||
665 | if (pull_up < 0 || pull_up >= TEGRA_MAX_PULL) | ||
666 | return -EINVAL; | ||
667 | |||
668 | spin_lock_irqsave(&mux_lock, flags); | ||
669 | |||
670 | reg = pg_readl(drive_pingroups[pg].reg); | ||
671 | reg &= ~(drive_pingroups[pg].drvup_mask << | ||
672 | drive_pingroups[pg].drvup_offset); | ||
673 | reg |= pull_up << drive_pingroups[pg].drvup_offset; | ||
674 | pg_writel(reg, drive_pingroups[pg].reg); | ||
675 | |||
676 | spin_unlock_irqrestore(&mux_lock, flags); | ||
677 | |||
678 | return 0; | ||
679 | } | ||
680 | |||
681 | static int tegra_drive_pinmux_set_slew_rising(enum tegra_drive_pingroup pg, | ||
682 | enum tegra_slew slew_rising) | ||
683 | { | ||
684 | unsigned long flags; | ||
685 | u32 reg; | ||
686 | if (pg < 0 || pg >= TEGRA_MAX_DRIVE_PINGROUP) | ||
687 | return -ERANGE; | ||
688 | |||
689 | if (slew_rising < 0 || slew_rising >= TEGRA_MAX_SLEW) | ||
690 | return -EINVAL; | ||
691 | |||
692 | spin_lock_irqsave(&mux_lock, flags); | ||
693 | |||
694 | reg = pg_readl(drive_pingroups[pg].reg); | ||
695 | reg &= ~(drive_pingroups[pg].slewrise_mask << | ||
696 | drive_pingroups[pg].slewrise_offset); | ||
697 | reg |= slew_rising << drive_pingroups[pg].slewrise_offset; | ||
698 | pg_writel(reg, drive_pingroups[pg].reg); | ||
699 | |||
700 | spin_unlock_irqrestore(&mux_lock, flags); | ||
701 | |||
702 | return 0; | ||
703 | } | ||
704 | |||
705 | static int tegra_drive_pinmux_set_slew_falling(enum tegra_drive_pingroup pg, | ||
706 | enum tegra_slew slew_falling) | ||
707 | { | ||
708 | unsigned long flags; | ||
709 | u32 reg; | ||
710 | if (pg < 0 || pg >= TEGRA_MAX_DRIVE_PINGROUP) | ||
711 | return -ERANGE; | ||
712 | |||
713 | if (slew_falling < 0 || slew_falling >= TEGRA_MAX_SLEW) | ||
714 | return -EINVAL; | ||
715 | |||
716 | spin_lock_irqsave(&mux_lock, flags); | ||
717 | |||
718 | reg = pg_readl(drive_pingroups[pg].reg); | ||
719 | reg &= ~(drive_pingroups[pg].slewfall_mask << | ||
720 | drive_pingroups[pg].slewfall_offset); | ||
721 | reg |= slew_falling << drive_pingroups[pg].slewfall_offset; | ||
722 | pg_writel(reg, drive_pingroups[pg].reg); | ||
723 | |||
724 | spin_unlock_irqrestore(&mux_lock, flags); | ||
725 | |||
726 | return 0; | ||
727 | } | ||
728 | |||
729 | static void tegra_drive_pinmux_config_pingroup(enum tegra_drive_pingroup pingroup, | ||
730 | enum tegra_hsm hsm, | ||
731 | enum tegra_schmitt schmitt, | ||
732 | enum tegra_drive drive, | ||
733 | enum tegra_pull_strength pull_down, | ||
734 | enum tegra_pull_strength pull_up, | ||
735 | enum tegra_slew slew_rising, | ||
736 | enum tegra_slew slew_falling) | ||
737 | { | ||
738 | int err; | ||
739 | |||
740 | err = tegra_drive_pinmux_set_hsm(pingroup, hsm); | ||
741 | if (err < 0) | ||
742 | pr_err("pinmux: can't set pingroup %s hsm to %s: %d\n", | ||
743 | drive_pinmux_name(pingroup), | ||
744 | enable_name(hsm), err); | ||
745 | |||
746 | err = tegra_drive_pinmux_set_schmitt(pingroup, schmitt); | ||
747 | if (err < 0) | ||
748 | pr_err("pinmux: can't set pingroup %s schmitt to %s: %d\n", | ||
749 | drive_pinmux_name(pingroup), | ||
750 | enable_name(schmitt), err); | ||
751 | |||
752 | err = tegra_drive_pinmux_set_drive(pingroup, drive); | ||
753 | if (err < 0) | ||
754 | pr_err("pinmux: can't set pingroup %s drive to %s: %d\n", | ||
755 | drive_pinmux_name(pingroup), | ||
756 | drive_name(drive), err); | ||
757 | |||
758 | err = tegra_drive_pinmux_set_pull_down(pingroup, pull_down); | ||
759 | if (err < 0) | ||
760 | pr_err("pinmux: can't set pingroup %s pull down to %d: %d\n", | ||
761 | drive_pinmux_name(pingroup), | ||
762 | pull_down, err); | ||
763 | |||
764 | err = tegra_drive_pinmux_set_pull_up(pingroup, pull_up); | ||
765 | if (err < 0) | ||
766 | pr_err("pinmux: can't set pingroup %s pull up to %d: %d\n", | ||
767 | drive_pinmux_name(pingroup), | ||
768 | pull_up, err); | ||
769 | |||
770 | err = tegra_drive_pinmux_set_slew_rising(pingroup, slew_rising); | ||
771 | if (err < 0) | ||
772 | pr_err("pinmux: can't set pingroup %s rising slew to %s: %d\n", | ||
773 | drive_pinmux_name(pingroup), | ||
774 | slew_name(slew_rising), err); | ||
775 | |||
776 | err = tegra_drive_pinmux_set_slew_falling(pingroup, slew_falling); | ||
777 | if (err < 0) | ||
778 | pr_err("pinmux: can't set pingroup %s falling slew to %s: %d\n", | ||
779 | drive_pinmux_name(pingroup), | ||
780 | slew_name(slew_falling), err); | ||
781 | } | ||
782 | |||
783 | void tegra_drive_pinmux_config_table(struct tegra_drive_pingroup_config *config, | ||
784 | int len) | ||
785 | { | ||
786 | int i; | ||
787 | |||
788 | for (i = 0; i < len; i++) | ||
789 | tegra_drive_pinmux_config_pingroup(config[i].pingroup, | ||
790 | config[i].hsm, | ||
791 | config[i].schmitt, | ||
792 | config[i].drive, | ||
793 | config[i].pull_down, | ||
794 | config[i].pull_up, | ||
795 | config[i].slew_rising, | ||
796 | config[i].slew_falling); | ||
797 | } | ||
798 | |||
799 | void tegra_pinmux_set_safe_pinmux_table(const struct tegra_pingroup_config *config, | ||
800 | int len) | ||
801 | { | ||
802 | int i; | ||
803 | struct tegra_pingroup_config c; | ||
804 | |||
805 | for (i = 0; i < len; i++) { | ||
806 | int err; | ||
807 | c = config[i]; | ||
808 | if (c.pingroup < 0 || c.pingroup >= TEGRA_MAX_PINGROUP) { | ||
809 | WARN_ON(1); | ||
810 | continue; | ||
811 | } | ||
812 | c.func = pingroups[c.pingroup].func_safe; | ||
813 | err = tegra_pinmux_set_func(&c); | ||
814 | if (err < 0) | ||
815 | pr_err("%s: tegra_pinmux_set_func returned %d setting " | ||
816 | "%s to %s\n", __func__, err, | ||
817 | pingroup_name(c.pingroup), func_name(c.func)); | ||
818 | } | ||
819 | } | ||
820 | |||
821 | void tegra_pinmux_config_pinmux_table(const struct tegra_pingroup_config *config, | ||
822 | int len) | ||
823 | { | ||
824 | int i; | ||
825 | |||
826 | for (i = 0; i < len; i++) { | ||
827 | int err; | ||
828 | if (config[i].pingroup < 0 || | ||
829 | config[i].pingroup >= TEGRA_MAX_PINGROUP) { | ||
830 | WARN_ON(1); | ||
831 | continue; | ||
832 | } | ||
833 | err = tegra_pinmux_set_func(&config[i]); | ||
834 | if (err < 0) | ||
835 | pr_err("%s: tegra_pinmux_set_func returned %d setting " | ||
836 | "%s to %s\n", __func__, err, | ||
837 | pingroup_name(config[i].pingroup), | ||
838 | func_name(config[i].func)); | ||
839 | } | ||
840 | } | ||
841 | |||
842 | void tegra_pinmux_config_tristate_table(const struct tegra_pingroup_config *config, | ||
843 | int len, enum tegra_tristate tristate) | ||
844 | { | ||
845 | int i; | ||
846 | int err; | ||
847 | enum tegra_pingroup pingroup; | ||
848 | |||
849 | for (i = 0; i < len; i++) { | ||
850 | pingroup = config[i].pingroup; | ||
851 | if (pingroups[pingroup].tri_reg > 0) { | ||
852 | err = tegra_pinmux_set_tristate(pingroup, tristate); | ||
853 | if (err < 0) | ||
854 | pr_err("pinmux: can't set pingroup %s tristate" | ||
855 | " to %s: %d\n", pingroup_name(pingroup), | ||
856 | tri_name(tristate), err); | ||
857 | } | ||
858 | } | ||
859 | } | ||
860 | |||
861 | void tegra_pinmux_config_pullupdown_table(const struct tegra_pingroup_config *config, | ||
862 | int len, enum tegra_pullupdown pupd) | ||
863 | { | ||
864 | int i; | ||
865 | int err; | ||
866 | enum tegra_pingroup pingroup; | ||
867 | |||
868 | for (i = 0; i < len; i++) { | ||
869 | pingroup = config[i].pingroup; | ||
870 | if (pingroups[pingroup].pupd_reg > 0) { | ||
871 | err = tegra_pinmux_set_pullupdown(pingroup, pupd); | ||
872 | if (err < 0) | ||
873 | pr_err("pinmux: can't set pingroup %s pullupdown" | ||
874 | " to %s: %d\n", pingroup_name(pingroup), | ||
875 | pupd_name(pupd), err); | ||
876 | } | ||
877 | } | ||
878 | } | ||
879 | |||
880 | #ifdef CONFIG_DEBUG_FS | ||
881 | |||
882 | #include <linux/debugfs.h> | ||
883 | #include <linux/seq_file.h> | ||
884 | |||
885 | static void dbg_pad_field(struct seq_file *s, int len) | ||
886 | { | ||
887 | seq_putc(s, ','); | ||
888 | |||
889 | while (len-- > -1) | ||
890 | seq_putc(s, ' '); | ||
891 | } | ||
892 | |||
893 | static int dbg_pinmux_show(struct seq_file *s, void *unused) | ||
894 | { | ||
895 | int i; | ||
896 | int len; | ||
897 | |||
898 | for (i = 0; i < TEGRA_MAX_PINGROUP; i++) { | ||
899 | unsigned long tri; | ||
900 | unsigned long mux; | ||
901 | unsigned long pupd; | ||
902 | |||
903 | seq_printf(s, "\t{TEGRA_PINGROUP_%s", pingroups[i].name); | ||
904 | len = strlen(pingroups[i].name); | ||
905 | dbg_pad_field(s, 15 - len); | ||
906 | |||
907 | if (pingroups[i].mux_reg <= 0) { | ||
908 | seq_printf(s, "TEGRA_MUX_NONE"); | ||
909 | len = strlen("NONE"); | ||
910 | } else { | ||
911 | mux = (pg_readl(pingroups[i].mux_reg) >> | ||
912 | pingroups[i].mux_bit) & 0x3; | ||
913 | BUG_ON(pingroups[i].funcs[mux] == 0); | ||
914 | if (pingroups[i].funcs[mux] == TEGRA_MUX_INVALID) { | ||
915 | seq_printf(s, "TEGRA_MUX_INVALID"); | ||
916 | len = 7; | ||
917 | } else if (pingroups[i].funcs[mux] & TEGRA_MUX_RSVD) { | ||
918 | seq_printf(s, "TEGRA_MUX_RSVD%1lu", mux+1); | ||
919 | len = 5; | ||
920 | } else { | ||
921 | BUG_ON(!tegra_mux_names[pingroups[i].funcs[mux]]); | ||
922 | seq_printf(s, "TEGRA_MUX_%s", | ||
923 | tegra_mux_names[pingroups[i].funcs[mux]]); | ||
924 | len = strlen(tegra_mux_names[pingroups[i].funcs[mux]]); | ||
925 | } | ||
926 | } | ||
927 | dbg_pad_field(s, 13-len); | ||
928 | |||
929 | #if defined(TEGRA_PINMUX_HAS_IO_DIRECTION) | ||
930 | { | ||
931 | unsigned long io; | ||
932 | io = (pg_readl(pingroups[i].mux_reg) >> 5) & 0x1; | ||
933 | seq_printf(s, "TEGRA_PIN_%s", io_name(io)); | ||
934 | len = strlen(io_name(io)); | ||
935 | dbg_pad_field(s, 6 - len); | ||
936 | } | ||
937 | #endif | ||
938 | if (pingroups[i].pupd_reg <= 0) { | ||
939 | seq_printf(s, "TEGRA_PUPD_NORMAL"); | ||
940 | len = strlen("NORMAL"); | ||
941 | } else { | ||
942 | pupd = (pg_readl(pingroups[i].pupd_reg) >> | ||
943 | pingroups[i].pupd_bit) & 0x3; | ||
944 | seq_printf(s, "TEGRA_PUPD_%s", pupd_name(pupd)); | ||
945 | len = strlen(pupd_name(pupd)); | ||
946 | } | ||
947 | dbg_pad_field(s, 9 - len); | ||
948 | |||
949 | if (pingroups[i].tri_reg <= 0) { | ||
950 | seq_printf(s, "TEGRA_TRI_NORMAL"); | ||
951 | } else { | ||
952 | tri = (pg_readl(pingroups[i].tri_reg) >> | ||
953 | pingroups[i].tri_bit) & 0x1; | ||
954 | |||
955 | seq_printf(s, "TEGRA_TRI_%s", tri_name(tri)); | ||
956 | } | ||
957 | seq_printf(s, "},\n"); | ||
958 | } | ||
959 | return 0; | ||
960 | } | ||
961 | |||
962 | static int dbg_pinmux_open(struct inode *inode, struct file *file) | ||
963 | { | ||
964 | return single_open(file, dbg_pinmux_show, &inode->i_private); | ||
965 | } | ||
966 | |||
967 | static const struct file_operations debug_fops = { | ||
968 | .open = dbg_pinmux_open, | ||
969 | .read = seq_read, | ||
970 | .llseek = seq_lseek, | ||
971 | .release = single_release, | ||
972 | }; | ||
973 | |||
974 | static int dbg_drive_pinmux_show(struct seq_file *s, void *unused) | ||
975 | { | ||
976 | int i; | ||
977 | int len; | ||
978 | u8 offset; | ||
979 | |||
980 | for (i = 0; i < TEGRA_MAX_DRIVE_PINGROUP; i++) { | ||
981 | u32 reg; | ||
982 | |||
983 | seq_printf(s, "\t{TEGRA_DRIVE_PINGROUP_%s", | ||
984 | drive_pingroups[i].name); | ||
985 | len = strlen(drive_pingroups[i].name); | ||
986 | dbg_pad_field(s, 7 - len); | ||
987 | |||
988 | |||
989 | reg = pg_readl(drive_pingroups[i].reg); | ||
990 | if (HSM_EN(reg)) { | ||
991 | seq_printf(s, "TEGRA_HSM_ENABLE"); | ||
992 | len = 16; | ||
993 | } else { | ||
994 | seq_printf(s, "TEGRA_HSM_DISABLE"); | ||
995 | len = 17; | ||
996 | } | ||
997 | dbg_pad_field(s, 17 - len); | ||
998 | |||
999 | if (SCHMT_EN(reg)) { | ||
1000 | seq_printf(s, "TEGRA_SCHMITT_ENABLE"); | ||
1001 | len = 21; | ||
1002 | } else { | ||
1003 | seq_printf(s, "TEGRA_SCHMITT_DISABLE"); | ||
1004 | len = 22; | ||
1005 | } | ||
1006 | dbg_pad_field(s, 22 - len); | ||
1007 | |||
1008 | seq_printf(s, "TEGRA_DRIVE_%s", drive_name(LPMD(reg))); | ||
1009 | len = strlen(drive_name(LPMD(reg))); | ||
1010 | dbg_pad_field(s, 5 - len); | ||
1011 | |||
1012 | offset = drive_pingroups[i].drvdown_offset; | ||
1013 | seq_printf(s, "TEGRA_PULL_%d", DRVDN(reg, offset)); | ||
1014 | len = DRVDN(reg, offset) < 10 ? 1 : 2; | ||
1015 | dbg_pad_field(s, 2 - len); | ||
1016 | |||
1017 | offset = drive_pingroups[i].drvup_offset; | ||
1018 | seq_printf(s, "TEGRA_PULL_%d", DRVUP(reg, offset)); | ||
1019 | len = DRVUP(reg, offset) < 10 ? 1 : 2; | ||
1020 | dbg_pad_field(s, 2 - len); | ||
1021 | |||
1022 | offset = drive_pingroups[i].slewrise_offset; | ||
1023 | seq_printf(s, "TEGRA_SLEW_%s", slew_name(SLWR(reg, offset))); | ||
1024 | len = strlen(slew_name(SLWR(reg, offset))); | ||
1025 | dbg_pad_field(s, 7 - len); | ||
1026 | |||
1027 | offset= drive_pingroups[i].slewfall_offset; | ||
1028 | seq_printf(s, "TEGRA_SLEW_%s", slew_name(SLWF(reg, offset))); | ||
1029 | |||
1030 | seq_printf(s, "},\n"); | ||
1031 | } | ||
1032 | return 0; | ||
1033 | } | ||
1034 | |||
1035 | static int dbg_drive_pinmux_open(struct inode *inode, struct file *file) | ||
1036 | { | ||
1037 | return single_open(file, dbg_drive_pinmux_show, &inode->i_private); | ||
1038 | } | ||
1039 | |||
1040 | static const struct file_operations debug_drive_fops = { | ||
1041 | .open = dbg_drive_pinmux_open, | ||
1042 | .read = seq_read, | ||
1043 | .llseek = seq_lseek, | ||
1044 | .release = single_release, | ||
1045 | }; | ||
1046 | |||
1047 | static int __init tegra_pinmux_debuginit(void) | ||
1048 | { | ||
1049 | (void) debugfs_create_file("tegra_pinmux", S_IRUGO, | ||
1050 | NULL, NULL, &debug_fops); | ||
1051 | (void) debugfs_create_file("tegra_pinmux_drive", S_IRUGO, | ||
1052 | NULL, NULL, &debug_drive_fops); | ||
1053 | return 0; | ||
1054 | } | ||
1055 | late_initcall(tegra_pinmux_debuginit); | ||
1056 | #endif | ||