diff options
author | Linus Walleij <linus.walleij@stericsson.com> | 2009-08-10 07:52:40 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-08-15 10:36:28 -0400 |
commit | df1e0520f9434b5b771c854a13dd928727d8673a (patch) | |
tree | 5d48442a1c8e43122511fd0c73596ac3917f8483 /arch/arm/mach-u300/padmux.c | |
parent | 5ad73d07173e7b76c16bcb8b6cf64d8386019689 (diff) |
ARM: 5666/1: Revamped U300 padmux API
This abstracts the hackish padmux API on the U300 platform into
something more manageable. It provides a way for drivers to
activate/deactivate a certain padmux setting. It will also switch
the users of the old API over to using the new style, pushing
muxing into the apropriate setup files.
Signed-off-by: Linus Walleij <linus.walleij@stericsson.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mach-u300/padmux.c')
-rw-r--r-- | arch/arm/mach-u300/padmux.c | 395 |
1 files changed, 352 insertions, 43 deletions
diff --git a/arch/arm/mach-u300/padmux.c b/arch/arm/mach-u300/padmux.c index f3664564f086..4c93c6cefd37 100644 --- a/arch/arm/mach-u300/padmux.c +++ b/arch/arm/mach-u300/padmux.c | |||
@@ -6,53 +6,362 @@ | |||
6 | * Copyright (C) 2009 ST-Ericsson AB | 6 | * Copyright (C) 2009 ST-Ericsson AB |
7 | * License terms: GNU General Public License (GPL) version 2 | 7 | * License terms: GNU General Public License (GPL) version 2 |
8 | * U300 PADMUX functions | 8 | * U300 PADMUX functions |
9 | * Author: Linus Walleij <linus.walleij@stericsson.com> | 9 | * Author: Martin Persson <martin.persson@stericsson.com> |
10 | * | ||
11 | */ | 10 | */ |
12 | #include <linux/io.h> | 11 | |
12 | #include <linux/module.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/device.h> | ||
13 | #include <linux/err.h> | 15 | #include <linux/err.h> |
16 | #include <linux/errno.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/mutex.h> | ||
19 | #include <linux/string.h> | ||
20 | #include <linux/bug.h> | ||
21 | #include <linux/debugfs.h> | ||
22 | #include <linux/seq_file.h> | ||
14 | #include <mach/u300-regs.h> | 23 | #include <mach/u300-regs.h> |
15 | #include <mach/syscon.h> | 24 | #include <mach/syscon.h> |
16 | |||
17 | #include "padmux.h" | 25 | #include "padmux.h" |
18 | 26 | ||
19 | /* Set the PAD MUX to route the MMC reader correctly to GPIO0. */ | 27 | static DEFINE_MUTEX(pmx_mutex); |
20 | void pmx_set_mission_mode_mmc(void) | 28 | |
21 | { | 29 | const u32 pmx_registers[] = { |
22 | u16 val; | 30 | (U300_SYSCON_VBASE + U300_SYSCON_PMC1LR), |
23 | 31 | (U300_SYSCON_VBASE + U300_SYSCON_PMC1HR), | |
24 | val = readw(U300_SYSCON_VBASE + U300_SYSCON_PMC1LR); | 32 | (U300_SYSCON_VBASE + U300_SYSCON_PMC2R), |
25 | val &= ~U300_SYSCON_PMC1LR_MMCSD_MASK; | 33 | (U300_SYSCON_VBASE + U300_SYSCON_PMC3R), |
26 | writew(val, U300_SYSCON_VBASE + U300_SYSCON_PMC1LR); | 34 | (U300_SYSCON_VBASE + U300_SYSCON_PMC4R) |
27 | val = readw(U300_SYSCON_VBASE + U300_SYSCON_PMC1HR); | 35 | }; |
28 | val &= ~U300_SYSCON_PMC1HR_APP_GPIO_1_MASK; | 36 | |
29 | val |= U300_SYSCON_PMC1HR_APP_GPIO_1_MMC; | 37 | /* High level functionality */ |
30 | writew(val, U300_SYSCON_VBASE + U300_SYSCON_PMC1HR); | 38 | |
31 | } | 39 | /* Lazy dog: |
32 | 40 | * onmask = { | |
33 | void pmx_set_mission_mode_spi(void) | 41 | * {"PMC1LR" mask, "PMC1LR" value}, |
34 | { | 42 | * {"PMC1HR" mask, "PMC1HR" value}, |
35 | u16 val; | 43 | * {"PMC2R" mask, "PMC2R" value}, |
36 | 44 | * {"PMC3R" mask, "PMC3R" value}, | |
37 | /* Set up padmuxing so the SPI port and its chipselects are active */ | 45 | * {"PMC4R" mask, "PMC4R" value} |
38 | val = readw(U300_SYSCON_VBASE + U300_SYSCON_PMC1HR); | 46 | * } |
39 | /* | 47 | */ |
40 | * Activate the SPI port (disable the use of these pins for generic | 48 | static struct pmx mmc_setting = { |
41 | * GPIO, DSP, AAIF | 49 | .setting = U300_APP_PMX_MMC_SETTING, |
42 | */ | 50 | .default_on = false, |
43 | val &= ~U300_SYSCON_PMC1HR_APP_SPI_2_MASK; | 51 | .activated = false, |
44 | val |= U300_SYSCON_PMC1HR_APP_SPI_2_SPI; | 52 | .name = "MMC", |
45 | /* | 53 | .onmask = { |
46 | * Use GPIO pin SPI CS1 for CS1 actually (it can be used for other | 54 | {U300_SYSCON_PMC1LR_MMCSD_MASK, |
47 | * things also) | 55 | U300_SYSCON_PMC1LR_MMCSD_MMCSD}, |
48 | */ | 56 | {0, 0}, |
49 | val &= ~U300_SYSCON_PMC1HR_APP_SPI_CS_1_MASK; | 57 | {0, 0}, |
50 | val |= U300_SYSCON_PMC1HR_APP_SPI_CS_1_SPI; | 58 | {0, 0}, |
51 | /* | 59 | {U300_SYSCON_PMC4R_APP_MISC_12_MASK, |
52 | * Use GPIO pin SPI CS2 for CS2 actually (it can be used for other | 60 | U300_SYSCON_PMC4R_APP_MISC_12_APP_GPIO} |
53 | * things also) | 61 | }, |
54 | */ | 62 | }; |
55 | val &= ~U300_SYSCON_PMC1HR_APP_SPI_CS_2_MASK; | 63 | |
56 | val |= U300_SYSCON_PMC1HR_APP_SPI_CS_2_SPI; | 64 | static struct pmx spi_setting = { |
57 | writew(val, U300_SYSCON_VBASE + U300_SYSCON_PMC1HR); | 65 | .setting = U300_APP_PMX_SPI_SETTING, |
66 | .default_on = false, | ||
67 | .activated = false, | ||
68 | .name = "SPI", | ||
69 | .onmask = {{0, 0}, | ||
70 | {U300_SYSCON_PMC1HR_APP_SPI_2_MASK | | ||
71 | U300_SYSCON_PMC1HR_APP_SPI_CS_1_MASK | | ||
72 | U300_SYSCON_PMC1HR_APP_SPI_CS_2_MASK, | ||
73 | U300_SYSCON_PMC1HR_APP_SPI_2_SPI | | ||
74 | U300_SYSCON_PMC1HR_APP_SPI_CS_1_SPI | | ||
75 | U300_SYSCON_PMC1HR_APP_SPI_CS_2_SPI}, | ||
76 | {0, 0}, | ||
77 | {0, 0}, | ||
78 | {0, 0} | ||
79 | }, | ||
80 | }; | ||
81 | |||
82 | /* Available padmux settings */ | ||
83 | static struct pmx *pmx_settings[] = { | ||
84 | &mmc_setting, | ||
85 | &spi_setting, | ||
86 | }; | ||
87 | |||
88 | static void update_registers(struct pmx *pmx, bool activate) | ||
89 | { | ||
90 | u16 regval, val, mask; | ||
91 | int i; | ||
92 | |||
93 | for (i = 0; i < ARRAY_SIZE(pmx_registers); i++) { | ||
94 | if (activate) | ||
95 | val = pmx->onmask[i].val; | ||
96 | else | ||
97 | val = 0; | ||
98 | |||
99 | mask = pmx->onmask[i].mask; | ||
100 | if (mask != 0) { | ||
101 | regval = readw(pmx_registers[i]); | ||
102 | regval &= ~mask; | ||
103 | regval |= val; | ||
104 | writew(regval, pmx_registers[i]); | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 | struct pmx *pmx_get(struct device *dev, enum pmx_settings setting) | ||
110 | { | ||
111 | int i; | ||
112 | struct pmx *pmx = ERR_PTR(-ENOENT); | ||
113 | |||
114 | if (dev == NULL) | ||
115 | return ERR_PTR(-EINVAL); | ||
116 | |||
117 | mutex_lock(&pmx_mutex); | ||
118 | for (i = 0; i < ARRAY_SIZE(pmx_settings); i++) { | ||
119 | |||
120 | if (setting == pmx_settings[i]->setting) { | ||
121 | |||
122 | if (pmx_settings[i]->dev != NULL) { | ||
123 | WARN(1, "padmux: required setting " | ||
124 | "in use by another consumer\n"); | ||
125 | } else { | ||
126 | pmx = pmx_settings[i]; | ||
127 | pmx->dev = dev; | ||
128 | dev_dbg(dev, "padmux: setting nr %d is now " | ||
129 | "bound to %s and ready to use\n", | ||
130 | setting, dev_name(dev)); | ||
131 | break; | ||
132 | } | ||
133 | } | ||
134 | } | ||
135 | mutex_unlock(&pmx_mutex); | ||
136 | |||
137 | return pmx; | ||
138 | } | ||
139 | EXPORT_SYMBOL(pmx_get); | ||
140 | |||
141 | int pmx_put(struct device *dev, struct pmx *pmx) | ||
142 | { | ||
143 | int i; | ||
144 | int ret = -ENOENT; | ||
145 | |||
146 | if (pmx == NULL || dev == NULL) | ||
147 | return -EINVAL; | ||
148 | |||
149 | mutex_lock(&pmx_mutex); | ||
150 | for (i = 0; i < ARRAY_SIZE(pmx_settings); i++) { | ||
151 | |||
152 | if (pmx->setting == pmx_settings[i]->setting) { | ||
153 | |||
154 | if (dev != pmx->dev) { | ||
155 | WARN(1, "padmux: cannot release handle as " | ||
156 | "it is bound to another consumer\n"); | ||
157 | ret = -EINVAL; | ||
158 | break; | ||
159 | } else { | ||
160 | pmx_settings[i]->dev = NULL; | ||
161 | ret = 0; | ||
162 | break; | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | mutex_unlock(&pmx_mutex); | ||
167 | |||
168 | return ret; | ||
169 | } | ||
170 | EXPORT_SYMBOL(pmx_put); | ||
171 | |||
172 | int pmx_activate(struct device *dev, struct pmx *pmx) | ||
173 | { | ||
174 | int i, j, ret; | ||
175 | ret = 0; | ||
176 | |||
177 | if (pmx == NULL || dev == NULL) | ||
178 | return -EINVAL; | ||
179 | |||
180 | mutex_lock(&pmx_mutex); | ||
181 | |||
182 | /* Make sure the required bits are not used */ | ||
183 | for (i = 0; i < ARRAY_SIZE(pmx_settings); i++) { | ||
184 | |||
185 | if (pmx_settings[i]->dev == NULL || pmx_settings[i] == pmx) | ||
186 | continue; | ||
187 | |||
188 | for (j = 0; j < ARRAY_SIZE(pmx_registers); j++) { | ||
189 | |||
190 | if (pmx_settings[i]->onmask[j].mask & pmx-> | ||
191 | onmask[j].mask) { | ||
192 | /* More than one entry on the same bits */ | ||
193 | WARN(1, "padmux: cannot activate " | ||
194 | "setting. Bit conflict with " | ||
195 | "an active setting\n"); | ||
196 | |||
197 | ret = -EUSERS; | ||
198 | goto exit; | ||
199 | } | ||
200 | } | ||
201 | } | ||
202 | update_registers(pmx, true); | ||
203 | pmx->activated = true; | ||
204 | dev_dbg(dev, "padmux: setting nr %d is activated\n", | ||
205 | pmx->setting); | ||
206 | |||
207 | exit: | ||
208 | mutex_unlock(&pmx_mutex); | ||
209 | return ret; | ||
210 | } | ||
211 | EXPORT_SYMBOL(pmx_activate); | ||
212 | |||
213 | int pmx_deactivate(struct device *dev, struct pmx *pmx) | ||
214 | { | ||
215 | int i; | ||
216 | int ret = -ENOENT; | ||
217 | |||
218 | if (pmx == NULL || dev == NULL) | ||
219 | return -EINVAL; | ||
220 | |||
221 | mutex_lock(&pmx_mutex); | ||
222 | for (i = 0; i < ARRAY_SIZE(pmx_settings); i++) { | ||
223 | |||
224 | if (pmx_settings[i]->dev == NULL) | ||
225 | continue; | ||
226 | |||
227 | if (pmx->setting == pmx_settings[i]->setting) { | ||
228 | |||
229 | if (dev != pmx->dev) { | ||
230 | WARN(1, "padmux: cannot deactivate " | ||
231 | "pmx setting as it was activated " | ||
232 | "by another consumer\n"); | ||
233 | |||
234 | ret = -EBUSY; | ||
235 | continue; | ||
236 | } else { | ||
237 | update_registers(pmx, false); | ||
238 | pmx_settings[i]->dev = NULL; | ||
239 | pmx->activated = false; | ||
240 | ret = 0; | ||
241 | dev_dbg(dev, "padmux: setting nr %d is deactivated", | ||
242 | pmx->setting); | ||
243 | break; | ||
244 | } | ||
245 | } | ||
246 | } | ||
247 | mutex_unlock(&pmx_mutex); | ||
248 | |||
249 | return ret; | ||
250 | } | ||
251 | EXPORT_SYMBOL(pmx_deactivate); | ||
252 | |||
253 | /* | ||
254 | * For internal use only. If it is to be exported, | ||
255 | * it should be reentrant. Notice that pmx_activate | ||
256 | * (i.e. runtime settings) always override default settings. | ||
257 | */ | ||
258 | static int pmx_set_default(void) | ||
259 | { | ||
260 | /* Used to identify several entries on the same bits */ | ||
261 | u16 modbits[ARRAY_SIZE(pmx_registers)]; | ||
262 | |||
263 | int i, j; | ||
264 | |||
265 | memset(modbits, 0, ARRAY_SIZE(pmx_registers) * sizeof(u16)); | ||
266 | |||
267 | for (i = 0; i < ARRAY_SIZE(pmx_settings); i++) { | ||
268 | |||
269 | if (!pmx_settings[i]->default_on) | ||
270 | continue; | ||
271 | |||
272 | for (j = 0; j < ARRAY_SIZE(pmx_registers); j++) { | ||
273 | |||
274 | /* Make sure there is only one entry on the same bits */ | ||
275 | if (modbits[j] & pmx_settings[i]->onmask[j].mask) { | ||
276 | BUG(); | ||
277 | return -EUSERS; | ||
278 | } | ||
279 | modbits[j] |= pmx_settings[i]->onmask[j].mask; | ||
280 | } | ||
281 | update_registers(pmx_settings[i], true); | ||
282 | } | ||
283 | return 0; | ||
58 | } | 284 | } |
285 | |||
286 | #if (defined(CONFIG_DEBUG_FS) && defined(CONFIG_U300_DEBUG)) | ||
287 | static int pmx_show(struct seq_file *s, void *data) | ||
288 | { | ||
289 | int i; | ||
290 | seq_printf(s, "-------------------------------------------------\n"); | ||
291 | seq_printf(s, "SETTING BOUND TO DEVICE STATE\n"); | ||
292 | seq_printf(s, "-------------------------------------------------\n"); | ||
293 | mutex_lock(&pmx_mutex); | ||
294 | for (i = 0; i < ARRAY_SIZE(pmx_settings); i++) { | ||
295 | /* Format pmx and device name nicely */ | ||
296 | char cdp[33]; | ||
297 | int chars; | ||
298 | |||
299 | chars = snprintf(&cdp[0], 17, "%s", pmx_settings[i]->name); | ||
300 | while (chars < 16) { | ||
301 | cdp[chars] = ' '; | ||
302 | chars++; | ||
303 | } | ||
304 | chars = snprintf(&cdp[16], 17, "%s", pmx_settings[i]->dev ? | ||
305 | dev_name(pmx_settings[i]->dev) : "N/A"); | ||
306 | while (chars < 16) { | ||
307 | cdp[chars+16] = ' '; | ||
308 | chars++; | ||
309 | } | ||
310 | cdp[32] = '\0'; | ||
311 | |||
312 | seq_printf(s, | ||
313 | "%s\t%s\n", | ||
314 | &cdp[0], | ||
315 | pmx_settings[i]->activated ? | ||
316 | "ACTIVATED" : "DEACTIVATED" | ||
317 | ); | ||
318 | |||
319 | } | ||
320 | mutex_unlock(&pmx_mutex); | ||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | static int pmx_open(struct inode *inode, struct file *file) | ||
325 | { | ||
326 | return single_open(file, pmx_show, NULL); | ||
327 | } | ||
328 | |||
329 | static const struct file_operations pmx_operations = { | ||
330 | .owner = THIS_MODULE, | ||
331 | .open = pmx_open, | ||
332 | .read = seq_read, | ||
333 | .llseek = seq_lseek, | ||
334 | .release = single_release, | ||
335 | }; | ||
336 | |||
337 | static int __init init_pmx_read_debugfs(void) | ||
338 | { | ||
339 | /* Expose a simple debugfs interface to view pmx settings */ | ||
340 | (void) debugfs_create_file("padmux", S_IFREG | S_IRUGO, | ||
341 | NULL, NULL, | ||
342 | &pmx_operations); | ||
343 | return 0; | ||
344 | } | ||
345 | |||
346 | /* | ||
347 | * This needs to come in after the core_initcall(), | ||
348 | * because debugfs is not available until | ||
349 | * the subsystems come up. | ||
350 | */ | ||
351 | module_init(init_pmx_read_debugfs); | ||
352 | #endif | ||
353 | |||
354 | static int __init pmx_init(void) | ||
355 | { | ||
356 | int ret; | ||
357 | |||
358 | ret = pmx_set_default(); | ||
359 | |||
360 | if (IS_ERR_VALUE(ret)) | ||
361 | pr_crit("padmux: default settings could not be set\n"); | ||
362 | |||
363 | return 0; | ||
364 | } | ||
365 | |||
366 | /* Should be initialized before consumers */ | ||
367 | core_initcall(pmx_init); | ||