aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-u300/padmux.c
diff options
context:
space:
mode:
authorLinus Walleij <linus.walleij@stericsson.com>2009-08-10 07:52:40 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2009-08-15 10:36:28 -0400
commitdf1e0520f9434b5b771c854a13dd928727d8673a (patch)
tree5d48442a1c8e43122511fd0c73596ac3917f8483 /arch/arm/mach-u300/padmux.c
parent5ad73d07173e7b76c16bcb8b6cf64d8386019689 (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.c395
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. */ 27static DEFINE_MUTEX(pmx_mutex);
20void pmx_set_mission_mode_mmc(void) 28
21{ 29const 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 = {
33void 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 48static 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; 64static 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 */
83static struct pmx *pmx_settings[] = {
84 &mmc_setting,
85 &spi_setting,
86};
87
88static 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
109struct 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}
139EXPORT_SYMBOL(pmx_get);
140
141int 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}
170EXPORT_SYMBOL(pmx_put);
171
172int 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
207exit:
208 mutex_unlock(&pmx_mutex);
209 return ret;
210}
211EXPORT_SYMBOL(pmx_activate);
212
213int 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}
251EXPORT_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 */
258static 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))
287static 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
324static int pmx_open(struct inode *inode, struct file *file)
325{
326 return single_open(file, pmx_show, NULL);
327}
328
329static 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
337static 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 */
351module_init(init_pmx_read_debugfs);
352#endif
353
354static 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 */
367core_initcall(pmx_init);