Commit | Line | Data |
---|---|---|
bd41b99d LW |
1 | /* |
2 | * | |
3 | * arch/arm/mach-u300/padmux.c | |
4 | * | |
5 | * | |
6 | * Copyright (C) 2009 ST-Ericsson AB | |
7 | * License terms: GNU General Public License (GPL) version 2 | |
8 | * U300 PADMUX functions | |
df1e0520 | 9 | * Author: Martin Persson <martin.persson@stericsson.com> |
bd41b99d | 10 | */ |
df1e0520 LW |
11 | |
12 | #include <linux/module.h> | |
13 | #include <linux/kernel.h> | |
14 | #include <linux/device.h> | |
bd41b99d | 15 | #include <linux/err.h> |
df1e0520 LW |
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> | |
bd41b99d LW |
23 | #include <mach/u300-regs.h> |
24 | #include <mach/syscon.h> | |
bd41b99d LW |
25 | #include "padmux.h" |
26 | ||
df1e0520 LW |
27 | static DEFINE_MUTEX(pmx_mutex); |
28 | ||
29 | const u32 pmx_registers[] = { | |
30 | (U300_SYSCON_VBASE + U300_SYSCON_PMC1LR), | |
31 | (U300_SYSCON_VBASE + U300_SYSCON_PMC1HR), | |
32 | (U300_SYSCON_VBASE + U300_SYSCON_PMC2R), | |
33 | (U300_SYSCON_VBASE + U300_SYSCON_PMC3R), | |
34 | (U300_SYSCON_VBASE + U300_SYSCON_PMC4R) | |
35 | }; | |
36 | ||
37 | /* High level functionality */ | |
38 | ||
39 | /* Lazy dog: | |
40 | * onmask = { | |
41 | * {"PMC1LR" mask, "PMC1LR" value}, | |
42 | * {"PMC1HR" mask, "PMC1HR" value}, | |
43 | * {"PMC2R" mask, "PMC2R" value}, | |
44 | * {"PMC3R" mask, "PMC3R" value}, | |
45 | * {"PMC4R" mask, "PMC4R" value} | |
46 | * } | |
47 | */ | |
48 | static struct pmx mmc_setting = { | |
49 | .setting = U300_APP_PMX_MMC_SETTING, | |
50 | .default_on = false, | |
51 | .activated = false, | |
52 | .name = "MMC", | |
53 | .onmask = { | |
54 | {U300_SYSCON_PMC1LR_MMCSD_MASK, | |
55 | U300_SYSCON_PMC1LR_MMCSD_MMCSD}, | |
56 | {0, 0}, | |
57 | {0, 0}, | |
58 | {0, 0}, | |
59 | {U300_SYSCON_PMC4R_APP_MISC_12_MASK, | |
60 | U300_SYSCON_PMC4R_APP_MISC_12_APP_GPIO} | |
61 | }, | |
62 | }; | |
63 | ||
64 | static struct pmx spi_setting = { | |
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; | |
bd41b99d | 284 | } |
df1e0520 LW |
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); |