Commit | Line | Data |
---|---|---|
a4417c84 EG |
1 | /* |
2 | * linux/arch/arm/mach-tegra/pinmux.c | |
3 | * | |
4 | * Copyright (C) 2010 Google, Inc. | |
5 | * | |
6 | * This software is licensed under the terms of the GNU General Public | |
7 | * License version 2, as published by the Free Software Foundation, and | |
8 | * may be copied, distributed, and modified under those terms. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | */ | |
16 | ||
c5f04b8d CC |
17 | #include <linux/init.h> |
18 | #include <linux/module.h> | |
a4417c84 EG |
19 | #include <linux/kernel.h> |
20 | #include <linux/errno.h> | |
21 | #include <linux/spinlock.h> | |
22 | #include <linux/io.h> | |
23 | ||
24 | #include <mach/iomap.h> | |
25 | #include <mach/pinmux.h> | |
26 | ||
a4417c84 EG |
27 | #define HSM_EN(reg) (((reg) >> 2) & 0x1) |
28 | #define SCHMT_EN(reg) (((reg) >> 3) & 0x1) | |
29 | #define LPMD(reg) (((reg) >> 4) & 0x3) | |
30 | #define DRVDN(reg) (((reg) >> 12) & 0x1f) | |
31 | #define DRVUP(reg) (((reg) >> 20) & 0x1f) | |
32 | #define SLWR(reg) (((reg) >> 28) & 0x3) | |
33 | #define SLWF(reg) (((reg) >> 30) & 0x3) | |
34 | ||
c5f04b8d CC |
35 | static const struct tegra_pingroup_desc *const pingroups = tegra_soc_pingroups; |
36 | static const struct tegra_drive_pingroup_desc *const drive_pingroups = tegra_soc_drive_pingroups; | |
a4417c84 EG |
37 | |
38 | static char *tegra_mux_names[TEGRA_MAX_MUX] = { | |
39 | [TEGRA_MUX_AHB_CLK] = "AHB_CLK", | |
40 | [TEGRA_MUX_APB_CLK] = "APB_CLK", | |
41 | [TEGRA_MUX_AUDIO_SYNC] = "AUDIO_SYNC", | |
42 | [TEGRA_MUX_CRT] = "CRT", | |
43 | [TEGRA_MUX_DAP1] = "DAP1", | |
44 | [TEGRA_MUX_DAP2] = "DAP2", | |
45 | [TEGRA_MUX_DAP3] = "DAP3", | |
46 | [TEGRA_MUX_DAP4] = "DAP4", | |
47 | [TEGRA_MUX_DAP5] = "DAP5", | |
48 | [TEGRA_MUX_DISPLAYA] = "DISPLAYA", | |
49 | [TEGRA_MUX_DISPLAYB] = "DISPLAYB", | |
50 | [TEGRA_MUX_EMC_TEST0_DLL] = "EMC_TEST0_DLL", | |
51 | [TEGRA_MUX_EMC_TEST1_DLL] = "EMC_TEST1_DLL", | |
52 | [TEGRA_MUX_GMI] = "GMI", | |
53 | [TEGRA_MUX_GMI_INT] = "GMI_INT", | |
54 | [TEGRA_MUX_HDMI] = "HDMI", | |
55 | [TEGRA_MUX_I2C] = "I2C", | |
56 | [TEGRA_MUX_I2C2] = "I2C2", | |
57 | [TEGRA_MUX_I2C3] = "I2C3", | |
58 | [TEGRA_MUX_IDE] = "IDE", | |
59 | [TEGRA_MUX_IRDA] = "IRDA", | |
60 | [TEGRA_MUX_KBC] = "KBC", | |
61 | [TEGRA_MUX_MIO] = "MIO", | |
62 | [TEGRA_MUX_MIPI_HS] = "MIPI_HS", | |
63 | [TEGRA_MUX_NAND] = "NAND", | |
64 | [TEGRA_MUX_OSC] = "OSC", | |
65 | [TEGRA_MUX_OWR] = "OWR", | |
66 | [TEGRA_MUX_PCIE] = "PCIE", | |
67 | [TEGRA_MUX_PLLA_OUT] = "PLLA_OUT", | |
68 | [TEGRA_MUX_PLLC_OUT1] = "PLLC_OUT1", | |
69 | [TEGRA_MUX_PLLM_OUT1] = "PLLM_OUT1", | |
70 | [TEGRA_MUX_PLLP_OUT2] = "PLLP_OUT2", | |
71 | [TEGRA_MUX_PLLP_OUT3] = "PLLP_OUT3", | |
72 | [TEGRA_MUX_PLLP_OUT4] = "PLLP_OUT4", | |
73 | [TEGRA_MUX_PWM] = "PWM", | |
74 | [TEGRA_MUX_PWR_INTR] = "PWR_INTR", | |
75 | [TEGRA_MUX_PWR_ON] = "PWR_ON", | |
76 | [TEGRA_MUX_RTCK] = "RTCK", | |
77 | [TEGRA_MUX_SDIO1] = "SDIO1", | |
78 | [TEGRA_MUX_SDIO2] = "SDIO2", | |
79 | [TEGRA_MUX_SDIO3] = "SDIO3", | |
80 | [TEGRA_MUX_SDIO4] = "SDIO4", | |
81 | [TEGRA_MUX_SFLASH] = "SFLASH", | |
82 | [TEGRA_MUX_SPDIF] = "SPDIF", | |
83 | [TEGRA_MUX_SPI1] = "SPI1", | |
84 | [TEGRA_MUX_SPI2] = "SPI2", | |
85 | [TEGRA_MUX_SPI2_ALT] = "SPI2_ALT", | |
86 | [TEGRA_MUX_SPI3] = "SPI3", | |
87 | [TEGRA_MUX_SPI4] = "SPI4", | |
88 | [TEGRA_MUX_TRACE] = "TRACE", | |
89 | [TEGRA_MUX_TWC] = "TWC", | |
90 | [TEGRA_MUX_UARTA] = "UARTA", | |
91 | [TEGRA_MUX_UARTB] = "UARTB", | |
92 | [TEGRA_MUX_UARTC] = "UARTC", | |
93 | [TEGRA_MUX_UARTD] = "UARTD", | |
94 | [TEGRA_MUX_UARTE] = "UARTE", | |
95 | [TEGRA_MUX_ULPI] = "ULPI", | |
96 | [TEGRA_MUX_VI] = "VI", | |
97 | [TEGRA_MUX_VI_SENSOR_CLK] = "VI_SENSOR_CLK", | |
98 | [TEGRA_MUX_XIO] = "XIO", | |
c5f04b8d | 99 | [TEGRA_MUX_SAFE] = "<safe>", |
a4417c84 EG |
100 | }; |
101 | ||
102 | static const char *tegra_drive_names[TEGRA_MAX_DRIVE] = { | |
103 | [TEGRA_DRIVE_DIV_8] = "DIV_8", | |
104 | [TEGRA_DRIVE_DIV_4] = "DIV_4", | |
105 | [TEGRA_DRIVE_DIV_2] = "DIV_2", | |
106 | [TEGRA_DRIVE_DIV_1] = "DIV_1", | |
107 | }; | |
108 | ||
109 | static const char *tegra_slew_names[TEGRA_MAX_SLEW] = { | |
110 | [TEGRA_SLEW_FASTEST] = "FASTEST", | |
111 | [TEGRA_SLEW_FAST] = "FAST", | |
112 | [TEGRA_SLEW_SLOW] = "SLOW", | |
113 | [TEGRA_SLEW_SLOWEST] = "SLOWEST", | |
114 | }; | |
115 | ||
116 | static DEFINE_SPINLOCK(mux_lock); | |
117 | ||
118 | static const char *pingroup_name(enum tegra_pingroup pg) | |
119 | { | |
120 | if (pg < 0 || pg >= TEGRA_MAX_PINGROUP) | |
121 | return "<UNKNOWN>"; | |
122 | ||
123 | return pingroups[pg].name; | |
124 | } | |
125 | ||
126 | static const char *func_name(enum tegra_mux_func func) | |
127 | { | |
128 | if (func == TEGRA_MUX_RSVD1) | |
129 | return "RSVD1"; | |
130 | ||
131 | if (func == TEGRA_MUX_RSVD2) | |
132 | return "RSVD2"; | |
133 | ||
134 | if (func == TEGRA_MUX_RSVD3) | |
135 | return "RSVD3"; | |
136 | ||
137 | if (func == TEGRA_MUX_RSVD4) | |
138 | return "RSVD4"; | |
139 | ||
140 | if (func == TEGRA_MUX_NONE) | |
141 | return "NONE"; | |
142 | ||
143 | if (func < 0 || func >= TEGRA_MAX_MUX) | |
144 | return "<UNKNOWN>"; | |
145 | ||
146 | return tegra_mux_names[func]; | |
147 | } | |
148 | ||
149 | ||
150 | static const char *tri_name(unsigned long val) | |
151 | { | |
152 | return val ? "TRISTATE" : "NORMAL"; | |
153 | } | |
154 | ||
155 | static const char *pupd_name(unsigned long val) | |
156 | { | |
157 | switch (val) { | |
158 | case 0: | |
159 | return "NORMAL"; | |
160 | ||
161 | case 1: | |
162 | return "PULL_DOWN"; | |
163 | ||
164 | case 2: | |
165 | return "PULL_UP"; | |
166 | ||
167 | default: | |
168 | return "RSVD"; | |
169 | } | |
170 | } | |
171 | ||
172 | ||
173 | static inline unsigned long pg_readl(unsigned long offset) | |
174 | { | |
175 | return readl(IO_TO_VIRT(TEGRA_APB_MISC_BASE + offset)); | |
176 | } | |
177 | ||
178 | static inline void pg_writel(unsigned long value, unsigned long offset) | |
179 | { | |
180 | writel(value, IO_TO_VIRT(TEGRA_APB_MISC_BASE + offset)); | |
181 | } | |
182 | ||
c5f04b8d | 183 | static int tegra_pinmux_set_func(const struct tegra_pingroup_config *config) |
a4417c84 EG |
184 | { |
185 | int mux = -1; | |
186 | int i; | |
187 | unsigned long reg; | |
188 | unsigned long flags; | |
c5f04b8d CC |
189 | enum tegra_pingroup pg = config->pingroup; |
190 | enum tegra_mux_func func = config->func; | |
a4417c84 EG |
191 | |
192 | if (pg < 0 || pg >= TEGRA_MAX_PINGROUP) | |
193 | return -ERANGE; | |
194 | ||
c5f04b8d | 195 | if (pingroups[pg].mux_reg < 0) |
a4417c84 EG |
196 | return -EINVAL; |
197 | ||
198 | if (func < 0) | |
199 | return -ERANGE; | |
200 | ||
c5f04b8d CC |
201 | if (func == TEGRA_MUX_SAFE) |
202 | func = pingroups[pg].func_safe; | |
203 | ||
a4417c84 EG |
204 | if (func & TEGRA_MUX_RSVD) { |
205 | mux = func & 0x3; | |
206 | } else { | |
207 | for (i = 0; i < 4; i++) { | |
208 | if (pingroups[pg].funcs[i] == func) { | |
209 | mux = i; | |
210 | break; | |
211 | } | |
212 | } | |
213 | } | |
214 | ||
215 | if (mux < 0) | |
216 | return -EINVAL; | |
217 | ||
218 | spin_lock_irqsave(&mux_lock, flags); | |
219 | ||
c5f04b8d | 220 | reg = pg_readl(pingroups[pg].mux_reg); |
a4417c84 EG |
221 | reg &= ~(0x3 << pingroups[pg].mux_bit); |
222 | reg |= mux << pingroups[pg].mux_bit; | |
c5f04b8d | 223 | pg_writel(reg, pingroups[pg].mux_reg); |
a4417c84 EG |
224 | |
225 | spin_unlock_irqrestore(&mux_lock, flags); | |
226 | ||
227 | return 0; | |
228 | } | |
229 | ||
230 | int tegra_pinmux_set_tristate(enum tegra_pingroup pg, | |
231 | enum tegra_tristate tristate) | |
232 | { | |
233 | unsigned long reg; | |
234 | unsigned long flags; | |
235 | ||
236 | if (pg < 0 || pg >= TEGRA_MAX_PINGROUP) | |
237 | return -ERANGE; | |
238 | ||
c5f04b8d | 239 | if (pingroups[pg].tri_reg < 0) |
a4417c84 EG |
240 | return -EINVAL; |
241 | ||
242 | spin_lock_irqsave(&mux_lock, flags); | |
243 | ||
c5f04b8d | 244 | reg = pg_readl(pingroups[pg].tri_reg); |
a4417c84 EG |
245 | reg &= ~(0x1 << pingroups[pg].tri_bit); |
246 | if (tristate) | |
247 | reg |= 1 << pingroups[pg].tri_bit; | |
c5f04b8d | 248 | pg_writel(reg, pingroups[pg].tri_reg); |
a4417c84 EG |
249 | |
250 | spin_unlock_irqrestore(&mux_lock, flags); | |
251 | ||
252 | return 0; | |
253 | } | |
254 | ||
255 | int tegra_pinmux_set_pullupdown(enum tegra_pingroup pg, | |
256 | enum tegra_pullupdown pupd) | |
257 | { | |
258 | unsigned long reg; | |
259 | unsigned long flags; | |
260 | ||
261 | if (pg < 0 || pg >= TEGRA_MAX_PINGROUP) | |
262 | return -ERANGE; | |
263 | ||
c5f04b8d | 264 | if (pingroups[pg].pupd_reg < 0) |
a4417c84 EG |
265 | return -EINVAL; |
266 | ||
267 | if (pupd != TEGRA_PUPD_NORMAL && | |
268 | pupd != TEGRA_PUPD_PULL_DOWN && | |
269 | pupd != TEGRA_PUPD_PULL_UP) | |
270 | return -EINVAL; | |
271 | ||
272 | ||
273 | spin_lock_irqsave(&mux_lock, flags); | |
274 | ||
c5f04b8d | 275 | reg = pg_readl(pingroups[pg].pupd_reg); |
a4417c84 EG |
276 | reg &= ~(0x3 << pingroups[pg].pupd_bit); |
277 | reg |= pupd << pingroups[pg].pupd_bit; | |
c5f04b8d | 278 | pg_writel(reg, pingroups[pg].pupd_reg); |
a4417c84 EG |
279 | |
280 | spin_unlock_irqrestore(&mux_lock, flags); | |
281 | ||
282 | return 0; | |
283 | } | |
284 | ||
c5f04b8d | 285 | static void tegra_pinmux_config_pingroup(const struct tegra_pingroup_config *config) |
a4417c84 | 286 | { |
c5f04b8d CC |
287 | enum tegra_pingroup pingroup = config->pingroup; |
288 | enum tegra_mux_func func = config->func; | |
289 | enum tegra_pullupdown pupd = config->pupd; | |
290 | enum tegra_tristate tristate = config->tristate; | |
a4417c84 EG |
291 | int err; |
292 | ||
c5f04b8d CC |
293 | if (pingroups[pingroup].mux_reg >= 0) { |
294 | err = tegra_pinmux_set_func(config); | |
a4417c84 EG |
295 | if (err < 0) |
296 | pr_err("pinmux: can't set pingroup %s func to %s: %d\n", | |
297 | pingroup_name(pingroup), func_name(func), err); | |
298 | } | |
299 | ||
c5f04b8d | 300 | if (pingroups[pingroup].pupd_reg >= 0) { |
a4417c84 EG |
301 | err = tegra_pinmux_set_pullupdown(pingroup, pupd); |
302 | if (err < 0) | |
303 | pr_err("pinmux: can't set pingroup %s pullupdown to %s: %d\n", | |
304 | pingroup_name(pingroup), pupd_name(pupd), err); | |
305 | } | |
306 | ||
c5f04b8d | 307 | if (pingroups[pingroup].tri_reg >= 0) { |
a4417c84 EG |
308 | err = tegra_pinmux_set_tristate(pingroup, tristate); |
309 | if (err < 0) | |
310 | pr_err("pinmux: can't set pingroup %s tristate to %s: %d\n", | |
311 | pingroup_name(pingroup), tri_name(func), err); | |
312 | } | |
313 | } | |
314 | ||
c5f04b8d | 315 | void tegra_pinmux_config_table(const struct tegra_pingroup_config *config, int len) |
a4417c84 EG |
316 | { |
317 | int i; | |
318 | ||
319 | for (i = 0; i < len; i++) | |
c5f04b8d | 320 | tegra_pinmux_config_pingroup(&config[i]); |
a4417c84 EG |
321 | } |
322 | ||
323 | static const char *drive_pinmux_name(enum tegra_drive_pingroup pg) | |
324 | { | |
325 | if (pg < 0 || pg >= TEGRA_MAX_DRIVE_PINGROUP) | |
326 | return "<UNKNOWN>"; | |
327 | ||
328 | return drive_pingroups[pg].name; | |
329 | } | |
330 | ||
331 | static const char *enable_name(unsigned long val) | |
332 | { | |
333 | return val ? "ENABLE" : "DISABLE"; | |
334 | } | |
335 | ||
336 | static const char *drive_name(unsigned long val) | |
337 | { | |
338 | if (val >= TEGRA_MAX_DRIVE) | |
339 | return "<UNKNOWN>"; | |
340 | ||
341 | return tegra_drive_names[val]; | |
342 | } | |
343 | ||
344 | static const char *slew_name(unsigned long val) | |
345 | { | |
346 | if (val >= TEGRA_MAX_SLEW) | |
347 | return "<UNKNOWN>"; | |
348 | ||
349 | return tegra_slew_names[val]; | |
350 | } | |
351 | ||
352 | static int tegra_drive_pinmux_set_hsm(enum tegra_drive_pingroup pg, | |
353 | enum tegra_hsm hsm) | |
354 | { | |
355 | unsigned long flags; | |
356 | u32 reg; | |
357 | if (pg < 0 || pg >= TEGRA_MAX_DRIVE_PINGROUP) | |
358 | return -ERANGE; | |
359 | ||
360 | if (hsm != TEGRA_HSM_ENABLE && hsm != TEGRA_HSM_DISABLE) | |
361 | return -EINVAL; | |
362 | ||
363 | spin_lock_irqsave(&mux_lock, flags); | |
364 | ||
365 | reg = pg_readl(drive_pingroups[pg].reg); | |
366 | if (hsm == TEGRA_HSM_ENABLE) | |
367 | reg |= (1 << 2); | |
368 | else | |
369 | reg &= ~(1 << 2); | |
370 | pg_writel(reg, drive_pingroups[pg].reg); | |
371 | ||
372 | spin_unlock_irqrestore(&mux_lock, flags); | |
373 | ||
374 | return 0; | |
375 | } | |
376 | ||
377 | static int tegra_drive_pinmux_set_schmitt(enum tegra_drive_pingroup pg, | |
378 | enum tegra_schmitt schmitt) | |
379 | { | |
380 | unsigned long flags; | |
381 | u32 reg; | |
382 | if (pg < 0 || pg >= TEGRA_MAX_DRIVE_PINGROUP) | |
383 | return -ERANGE; | |
384 | ||
385 | if (schmitt != TEGRA_SCHMITT_ENABLE && schmitt != TEGRA_SCHMITT_DISABLE) | |
386 | return -EINVAL; | |
387 | ||
388 | spin_lock_irqsave(&mux_lock, flags); | |
389 | ||
390 | reg = pg_readl(drive_pingroups[pg].reg); | |
391 | if (schmitt == TEGRA_SCHMITT_ENABLE) | |
392 | reg |= (1 << 3); | |
393 | else | |
394 | reg &= ~(1 << 3); | |
395 | pg_writel(reg, drive_pingroups[pg].reg); | |
396 | ||
397 | spin_unlock_irqrestore(&mux_lock, flags); | |
398 | ||
399 | return 0; | |
400 | } | |
401 | ||
402 | static int tegra_drive_pinmux_set_drive(enum tegra_drive_pingroup pg, | |
403 | enum tegra_drive drive) | |
404 | { | |
405 | unsigned long flags; | |
406 | u32 reg; | |
407 | if (pg < 0 || pg >= TEGRA_MAX_DRIVE_PINGROUP) | |
408 | return -ERANGE; | |
409 | ||
410 | if (drive < 0 || drive >= TEGRA_MAX_DRIVE) | |
411 | return -EINVAL; | |
412 | ||
413 | spin_lock_irqsave(&mux_lock, flags); | |
414 | ||
415 | reg = pg_readl(drive_pingroups[pg].reg); | |
416 | reg &= ~(0x3 << 4); | |
417 | reg |= drive << 4; | |
418 | pg_writel(reg, drive_pingroups[pg].reg); | |
419 | ||
420 | spin_unlock_irqrestore(&mux_lock, flags); | |
421 | ||
422 | return 0; | |
423 | } | |
424 | ||
425 | static int tegra_drive_pinmux_set_pull_down(enum tegra_drive_pingroup pg, | |
426 | enum tegra_pull_strength pull_down) | |
427 | { | |
428 | unsigned long flags; | |
429 | u32 reg; | |
430 | if (pg < 0 || pg >= TEGRA_MAX_DRIVE_PINGROUP) | |
431 | return -ERANGE; | |
432 | ||
433 | if (pull_down < 0 || pull_down >= TEGRA_MAX_PULL) | |
434 | return -EINVAL; | |
435 | ||
436 | spin_lock_irqsave(&mux_lock, flags); | |
437 | ||
438 | reg = pg_readl(drive_pingroups[pg].reg); | |
439 | reg &= ~(0x1f << 12); | |
440 | reg |= pull_down << 12; | |
441 | pg_writel(reg, drive_pingroups[pg].reg); | |
442 | ||
443 | spin_unlock_irqrestore(&mux_lock, flags); | |
444 | ||
445 | return 0; | |
446 | } | |
447 | ||
448 | static int tegra_drive_pinmux_set_pull_up(enum tegra_drive_pingroup pg, | |
449 | enum tegra_pull_strength pull_up) | |
450 | { | |
451 | unsigned long flags; | |
452 | u32 reg; | |
453 | if (pg < 0 || pg >= TEGRA_MAX_DRIVE_PINGROUP) | |
454 | return -ERANGE; | |
455 | ||
456 | if (pull_up < 0 || pull_up >= TEGRA_MAX_PULL) | |
457 | return -EINVAL; | |
458 | ||
459 | spin_lock_irqsave(&mux_lock, flags); | |
460 | ||
461 | reg = pg_readl(drive_pingroups[pg].reg); | |
462 | reg &= ~(0x1f << 12); | |
463 | reg |= pull_up << 12; | |
464 | pg_writel(reg, drive_pingroups[pg].reg); | |
465 | ||
466 | spin_unlock_irqrestore(&mux_lock, flags); | |
467 | ||
468 | return 0; | |
469 | } | |
470 | ||
471 | static int tegra_drive_pinmux_set_slew_rising(enum tegra_drive_pingroup pg, | |
472 | enum tegra_slew slew_rising) | |
473 | { | |
474 | unsigned long flags; | |
475 | u32 reg; | |
476 | if (pg < 0 || pg >= TEGRA_MAX_DRIVE_PINGROUP) | |
477 | return -ERANGE; | |
478 | ||
479 | if (slew_rising < 0 || slew_rising >= TEGRA_MAX_SLEW) | |
480 | return -EINVAL; | |
481 | ||
482 | spin_lock_irqsave(&mux_lock, flags); | |
483 | ||
484 | reg = pg_readl(drive_pingroups[pg].reg); | |
485 | reg &= ~(0x3 << 28); | |
486 | reg |= slew_rising << 28; | |
487 | pg_writel(reg, drive_pingroups[pg].reg); | |
488 | ||
489 | spin_unlock_irqrestore(&mux_lock, flags); | |
490 | ||
491 | return 0; | |
492 | } | |
493 | ||
494 | static int tegra_drive_pinmux_set_slew_falling(enum tegra_drive_pingroup pg, | |
495 | enum tegra_slew slew_falling) | |
496 | { | |
497 | unsigned long flags; | |
498 | u32 reg; | |
499 | if (pg < 0 || pg >= TEGRA_MAX_DRIVE_PINGROUP) | |
500 | return -ERANGE; | |
501 | ||
502 | if (slew_falling < 0 || slew_falling >= TEGRA_MAX_SLEW) | |
503 | return -EINVAL; | |
504 | ||
505 | spin_lock_irqsave(&mux_lock, flags); | |
506 | ||
507 | reg = pg_readl(drive_pingroups[pg].reg); | |
508 | reg &= ~(0x3 << 30); | |
509 | reg |= slew_falling << 30; | |
510 | pg_writel(reg, drive_pingroups[pg].reg); | |
511 | ||
512 | spin_unlock_irqrestore(&mux_lock, flags); | |
513 | ||
514 | return 0; | |
515 | } | |
516 | ||
517 | static void tegra_drive_pinmux_config_pingroup(enum tegra_drive_pingroup pingroup, | |
518 | enum tegra_hsm hsm, | |
519 | enum tegra_schmitt schmitt, | |
520 | enum tegra_drive drive, | |
521 | enum tegra_pull_strength pull_down, | |
522 | enum tegra_pull_strength pull_up, | |
523 | enum tegra_slew slew_rising, | |
524 | enum tegra_slew slew_falling) | |
525 | { | |
526 | int err; | |
527 | ||
528 | err = tegra_drive_pinmux_set_hsm(pingroup, hsm); | |
529 | if (err < 0) | |
530 | pr_err("pinmux: can't set pingroup %s hsm to %s: %d\n", | |
531 | drive_pinmux_name(pingroup), | |
532 | enable_name(hsm), err); | |
533 | ||
534 | err = tegra_drive_pinmux_set_schmitt(pingroup, schmitt); | |
535 | if (err < 0) | |
536 | pr_err("pinmux: can't set pingroup %s schmitt to %s: %d\n", | |
537 | drive_pinmux_name(pingroup), | |
538 | enable_name(schmitt), err); | |
539 | ||
540 | err = tegra_drive_pinmux_set_drive(pingroup, drive); | |
541 | if (err < 0) | |
542 | pr_err("pinmux: can't set pingroup %s drive to %s: %d\n", | |
543 | drive_pinmux_name(pingroup), | |
544 | drive_name(drive), err); | |
545 | ||
546 | err = tegra_drive_pinmux_set_pull_down(pingroup, pull_down); | |
547 | if (err < 0) | |
548 | pr_err("pinmux: can't set pingroup %s pull down to %d: %d\n", | |
549 | drive_pinmux_name(pingroup), | |
550 | pull_down, err); | |
551 | ||
552 | err = tegra_drive_pinmux_set_pull_up(pingroup, pull_up); | |
553 | if (err < 0) | |
554 | pr_err("pinmux: can't set pingroup %s pull up to %d: %d\n", | |
555 | drive_pinmux_name(pingroup), | |
556 | pull_up, err); | |
557 | ||
558 | err = tegra_drive_pinmux_set_slew_rising(pingroup, slew_rising); | |
559 | if (err < 0) | |
560 | pr_err("pinmux: can't set pingroup %s rising slew to %s: %d\n", | |
561 | drive_pinmux_name(pingroup), | |
562 | slew_name(slew_rising), err); | |
563 | ||
564 | err = tegra_drive_pinmux_set_slew_falling(pingroup, slew_falling); | |
565 | if (err < 0) | |
566 | pr_err("pinmux: can't set pingroup %s falling slew to %s: %d\n", | |
567 | drive_pinmux_name(pingroup), | |
568 | slew_name(slew_falling), err); | |
569 | } | |
570 | ||
571 | void tegra_drive_pinmux_config_table(struct tegra_drive_pingroup_config *config, | |
572 | int len) | |
573 | { | |
574 | int i; | |
575 | ||
576 | for (i = 0; i < len; i++) | |
577 | tegra_drive_pinmux_config_pingroup(config[i].pingroup, | |
578 | config[i].hsm, | |
579 | config[i].schmitt, | |
580 | config[i].drive, | |
581 | config[i].pull_down, | |
582 | config[i].pull_up, | |
583 | config[i].slew_rising, | |
584 | config[i].slew_falling); | |
585 | } | |
586 | ||
c5f04b8d CC |
587 | void tegra_pinmux_set_safe_pinmux_table(const struct tegra_pingroup_config *config, |
588 | int len) | |
589 | { | |
590 | int i; | |
591 | struct tegra_pingroup_config c; | |
592 | ||
593 | for (i = 0; i < len; i++) { | |
594 | int err; | |
595 | c = config[i]; | |
596 | if (c.pingroup < 0 || c.pingroup >= TEGRA_MAX_PINGROUP) { | |
597 | WARN_ON(1); | |
598 | continue; | |
599 | } | |
600 | c.func = pingroups[c.pingroup].func_safe; | |
601 | err = tegra_pinmux_set_func(&c); | |
602 | if (err < 0) | |
603 | pr_err("%s: tegra_pinmux_set_func returned %d setting " | |
604 | "%s to %s\n", __func__, err, | |
605 | pingroup_name(c.pingroup), func_name(c.func)); | |
606 | } | |
607 | } | |
608 | ||
609 | void tegra_pinmux_config_pinmux_table(const struct tegra_pingroup_config *config, | |
610 | int len) | |
611 | { | |
612 | int i; | |
613 | ||
614 | for (i = 0; i < len; i++) { | |
615 | int err; | |
616 | if (config[i].pingroup < 0 || | |
617 | config[i].pingroup >= TEGRA_MAX_PINGROUP) { | |
618 | WARN_ON(1); | |
619 | continue; | |
620 | } | |
621 | err = tegra_pinmux_set_func(&config[i]); | |
622 | if (err < 0) | |
623 | pr_err("%s: tegra_pinmux_set_func returned %d setting " | |
624 | "%s to %s\n", __func__, err, | |
625 | pingroup_name(config[i].pingroup), | |
626 | func_name(config[i].func)); | |
627 | } | |
628 | } | |
629 | ||
630 | void tegra_pinmux_config_tristate_table(const struct tegra_pingroup_config *config, | |
631 | int len, enum tegra_tristate tristate) | |
632 | { | |
633 | int i; | |
634 | int err; | |
635 | enum tegra_pingroup pingroup; | |
636 | ||
637 | for (i = 0; i < len; i++) { | |
638 | pingroup = config[i].pingroup; | |
639 | if (pingroups[pingroup].tri_reg >= 0) { | |
640 | err = tegra_pinmux_set_tristate(pingroup, tristate); | |
641 | if (err < 0) | |
642 | pr_err("pinmux: can't set pingroup %s tristate" | |
643 | " to %s: %d\n", pingroup_name(pingroup), | |
644 | tri_name(tristate), err); | |
645 | } | |
646 | } | |
647 | } | |
648 | ||
649 | void tegra_pinmux_config_pullupdown_table(const struct tegra_pingroup_config *config, | |
650 | int len, enum tegra_pullupdown pupd) | |
651 | { | |
652 | int i; | |
653 | int err; | |
654 | enum tegra_pingroup pingroup; | |
655 | ||
656 | for (i = 0; i < len; i++) { | |
657 | pingroup = config[i].pingroup; | |
658 | if (pingroups[pingroup].pupd_reg >= 0) { | |
659 | err = tegra_pinmux_set_pullupdown(pingroup, pupd); | |
660 | if (err < 0) | |
661 | pr_err("pinmux: can't set pingroup %s pullupdown" | |
662 | " to %s: %d\n", pingroup_name(pingroup), | |
663 | pupd_name(pupd), err); | |
664 | } | |
665 | } | |
666 | } | |
a4417c84 EG |
667 | |
668 | #ifdef CONFIG_DEBUG_FS | |
669 | ||
670 | #include <linux/debugfs.h> | |
671 | #include <linux/seq_file.h> | |
672 | ||
673 | static void dbg_pad_field(struct seq_file *s, int len) | |
674 | { | |
675 | seq_putc(s, ','); | |
676 | ||
677 | while (len-- > -1) | |
678 | seq_putc(s, ' '); | |
679 | } | |
680 | ||
681 | static int dbg_pinmux_show(struct seq_file *s, void *unused) | |
682 | { | |
683 | int i; | |
684 | int len; | |
685 | ||
686 | for (i = 0; i < TEGRA_MAX_PINGROUP; i++) { | |
687 | unsigned long tri; | |
688 | unsigned long mux; | |
689 | unsigned long pupd; | |
690 | ||
691 | seq_printf(s, "\t{TEGRA_PINGROUP_%s", pingroups[i].name); | |
692 | len = strlen(pingroups[i].name); | |
693 | dbg_pad_field(s, 5 - len); | |
694 | ||
c5f04b8d | 695 | if (pingroups[i].mux_reg < 0) { |
a4417c84 EG |
696 | seq_printf(s, "TEGRA_MUX_NONE"); |
697 | len = strlen("NONE"); | |
698 | } else { | |
c5f04b8d | 699 | mux = (pg_readl(pingroups[i].mux_reg) >> |
a4417c84 EG |
700 | pingroups[i].mux_bit) & 0x3; |
701 | if (pingroups[i].funcs[mux] == TEGRA_MUX_RSVD) { | |
702 | seq_printf(s, "TEGRA_MUX_RSVD%1lu", mux+1); | |
703 | len = 5; | |
704 | } else { | |
705 | seq_printf(s, "TEGRA_MUX_%s", | |
706 | tegra_mux_names[pingroups[i].funcs[mux]]); | |
707 | len = strlen(tegra_mux_names[pingroups[i].funcs[mux]]); | |
708 | } | |
709 | } | |
710 | dbg_pad_field(s, 13-len); | |
711 | ||
c5f04b8d | 712 | if (pingroups[i].pupd_reg < 0) { |
a4417c84 EG |
713 | seq_printf(s, "TEGRA_PUPD_NORMAL"); |
714 | len = strlen("NORMAL"); | |
715 | } else { | |
c5f04b8d | 716 | pupd = (pg_readl(pingroups[i].pupd_reg) >> |
a4417c84 EG |
717 | pingroups[i].pupd_bit) & 0x3; |
718 | seq_printf(s, "TEGRA_PUPD_%s", pupd_name(pupd)); | |
719 | len = strlen(pupd_name(pupd)); | |
720 | } | |
721 | dbg_pad_field(s, 9 - len); | |
722 | ||
c5f04b8d | 723 | if (pingroups[i].tri_reg < 0) { |
a4417c84 EG |
724 | seq_printf(s, "TEGRA_TRI_NORMAL"); |
725 | } else { | |
c5f04b8d | 726 | tri = (pg_readl(pingroups[i].tri_reg) >> |
a4417c84 EG |
727 | pingroups[i].tri_bit) & 0x1; |
728 | ||
729 | seq_printf(s, "TEGRA_TRI_%s", tri_name(tri)); | |
730 | } | |
731 | seq_printf(s, "},\n"); | |
732 | } | |
733 | return 0; | |
734 | } | |
735 | ||
736 | static int dbg_pinmux_open(struct inode *inode, struct file *file) | |
737 | { | |
738 | return single_open(file, dbg_pinmux_show, &inode->i_private); | |
739 | } | |
740 | ||
741 | static const struct file_operations debug_fops = { | |
742 | .open = dbg_pinmux_open, | |
743 | .read = seq_read, | |
744 | .llseek = seq_lseek, | |
745 | .release = single_release, | |
746 | }; | |
747 | ||
748 | static int dbg_drive_pinmux_show(struct seq_file *s, void *unused) | |
749 | { | |
750 | int i; | |
751 | int len; | |
752 | ||
753 | for (i = 0; i < TEGRA_MAX_DRIVE_PINGROUP; i++) { | |
754 | u32 reg; | |
755 | ||
756 | seq_printf(s, "\t{TEGRA_DRIVE_PINGROUP_%s", | |
757 | drive_pingroups[i].name); | |
758 | len = strlen(drive_pingroups[i].name); | |
759 | dbg_pad_field(s, 7 - len); | |
760 | ||
761 | ||
762 | reg = pg_readl(drive_pingroups[i].reg); | |
763 | if (HSM_EN(reg)) { | |
764 | seq_printf(s, "TEGRA_HSM_ENABLE"); | |
765 | len = 16; | |
766 | } else { | |
767 | seq_printf(s, "TEGRA_HSM_DISABLE"); | |
768 | len = 17; | |
769 | } | |
770 | dbg_pad_field(s, 17 - len); | |
771 | ||
772 | if (SCHMT_EN(reg)) { | |
773 | seq_printf(s, "TEGRA_SCHMITT_ENABLE"); | |
774 | len = 21; | |
775 | } else { | |
776 | seq_printf(s, "TEGRA_SCHMITT_DISABLE"); | |
777 | len = 22; | |
778 | } | |
779 | dbg_pad_field(s, 22 - len); | |
780 | ||
781 | seq_printf(s, "TEGRA_DRIVE_%s", drive_name(LPMD(reg))); | |
782 | len = strlen(drive_name(LPMD(reg))); | |
783 | dbg_pad_field(s, 5 - len); | |
784 | ||
785 | seq_printf(s, "TEGRA_PULL_%d", DRVDN(reg)); | |
786 | len = DRVDN(reg) < 10 ? 1 : 2; | |
787 | dbg_pad_field(s, 2 - len); | |
788 | ||
789 | seq_printf(s, "TEGRA_PULL_%d", DRVUP(reg)); | |
790 | len = DRVUP(reg) < 10 ? 1 : 2; | |
791 | dbg_pad_field(s, 2 - len); | |
792 | ||
793 | seq_printf(s, "TEGRA_SLEW_%s", slew_name(SLWR(reg))); | |
794 | len = strlen(slew_name(SLWR(reg))); | |
795 | dbg_pad_field(s, 7 - len); | |
796 | ||
797 | seq_printf(s, "TEGRA_SLEW_%s", slew_name(SLWF(reg))); | |
798 | ||
799 | seq_printf(s, "},\n"); | |
800 | } | |
801 | return 0; | |
802 | } | |
803 | ||
804 | static int dbg_drive_pinmux_open(struct inode *inode, struct file *file) | |
805 | { | |
806 | return single_open(file, dbg_drive_pinmux_show, &inode->i_private); | |
807 | } | |
808 | ||
809 | static const struct file_operations debug_drive_fops = { | |
810 | .open = dbg_drive_pinmux_open, | |
811 | .read = seq_read, | |
812 | .llseek = seq_lseek, | |
813 | .release = single_release, | |
814 | }; | |
815 | ||
816 | static int __init tegra_pinmux_debuginit(void) | |
817 | { | |
818 | (void) debugfs_create_file("tegra_pinmux", S_IRUGO, | |
819 | NULL, NULL, &debug_fops); | |
820 | (void) debugfs_create_file("tegra_pinmux_drive", S_IRUGO, | |
821 | NULL, NULL, &debug_drive_fops); | |
822 | return 0; | |
823 | } | |
824 | late_initcall(tegra_pinmux_debuginit); | |
825 | #endif |