diff options
Diffstat (limited to 'arch/cris/arch-v32/mach-a3/pinmux.c')
-rw-r--r-- | arch/cris/arch-v32/mach-a3/pinmux.c | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/arch/cris/arch-v32/mach-a3/pinmux.c b/arch/cris/arch-v32/mach-a3/pinmux.c new file mode 100644 index 000000000000..0a28c9bedfb7 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/pinmux.c | |||
@@ -0,0 +1,386 @@ | |||
1 | /* | ||
2 | * Allocator for I/O pins. All pins are allocated to GPIO at bootup. | ||
3 | * Unassigned pins and GPIO pins can be allocated to a fixed interface | ||
4 | * or the I/O processor instead. | ||
5 | * | ||
6 | * Copyright (c) 2005-2007 Axis Communications AB. | ||
7 | */ | ||
8 | |||
9 | #include <linux/init.h> | ||
10 | #include <linux/errno.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/string.h> | ||
13 | #include <linux/spinlock.h> | ||
14 | #include <hwregs/reg_map.h> | ||
15 | #include <hwregs/reg_rdwr.h> | ||
16 | #include <pinmux.h> | ||
17 | #include <hwregs/pinmux_defs.h> | ||
18 | #include <hwregs/clkgen_defs.h> | ||
19 | |||
20 | #undef DEBUG | ||
21 | |||
22 | #define PINS 80 | ||
23 | #define PORT_PINS 32 | ||
24 | #define PORTS 3 | ||
25 | |||
26 | static char pins[PINS]; | ||
27 | static DEFINE_SPINLOCK(pinmux_lock); | ||
28 | |||
29 | static void crisv32_pinmux_set(int port); | ||
30 | |||
31 | int | ||
32 | crisv32_pinmux_init(void) | ||
33 | { | ||
34 | static int initialized; | ||
35 | |||
36 | if (!initialized) { | ||
37 | initialized = 1; | ||
38 | REG_WR_INT(pinmux, regi_pinmux, rw_hwprot, 0); | ||
39 | crisv32_pinmux_alloc(PORT_A, 0, 31, pinmux_gpio); | ||
40 | crisv32_pinmux_alloc(PORT_B, 0, 31, pinmux_gpio); | ||
41 | crisv32_pinmux_alloc(PORT_C, 0, 15, pinmux_gpio); | ||
42 | } | ||
43 | |||
44 | return 0; | ||
45 | } | ||
46 | |||
47 | int | ||
48 | crisv32_pinmux_alloc(int port, int first_pin, int last_pin, enum pin_mode mode) | ||
49 | { | ||
50 | int i; | ||
51 | unsigned long flags; | ||
52 | |||
53 | crisv32_pinmux_init(); | ||
54 | |||
55 | if (port >= PORTS) | ||
56 | return -EINVAL; | ||
57 | |||
58 | spin_lock_irqsave(&pinmux_lock, flags); | ||
59 | |||
60 | for (i = first_pin; i <= last_pin; i++) { | ||
61 | if ((pins[port * PORT_PINS + i] != pinmux_none) && | ||
62 | (pins[port * PORT_PINS + i] != pinmux_gpio) && | ||
63 | (pins[port * PORT_PINS + i] != mode)) { | ||
64 | spin_unlock_irqrestore(&pinmux_lock, flags); | ||
65 | #ifdef DEBUG | ||
66 | panic("Pinmux alloc failed!\n"); | ||
67 | #endif | ||
68 | return -EPERM; | ||
69 | } | ||
70 | } | ||
71 | |||
72 | for (i = first_pin; i <= last_pin; i++) | ||
73 | pins[port * PORT_PINS + i] = mode; | ||
74 | |||
75 | crisv32_pinmux_set(port); | ||
76 | |||
77 | spin_unlock_irqrestore(&pinmux_lock, flags); | ||
78 | |||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | int | ||
83 | crisv32_pinmux_alloc_fixed(enum fixed_function function) | ||
84 | { | ||
85 | int ret = -EINVAL; | ||
86 | char saved[sizeof pins]; | ||
87 | unsigned long flags; | ||
88 | |||
89 | spin_lock_irqsave(&pinmux_lock, flags); | ||
90 | |||
91 | /* Save internal data for recovery */ | ||
92 | memcpy(saved, pins, sizeof pins); | ||
93 | |||
94 | crisv32_pinmux_init(); /* must be done before we read rw_hwprot */ | ||
95 | |||
96 | reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); | ||
97 | reg_clkgen_rw_clk_ctrl clk_ctrl = REG_RD(clkgen, regi_clkgen, | ||
98 | rw_clk_ctrl); | ||
99 | |||
100 | switch (function) { | ||
101 | case pinmux_eth: | ||
102 | clk_ctrl.eth = regk_clkgen_yes; | ||
103 | clk_ctrl.dma0_1_eth = regk_clkgen_yes; | ||
104 | ret = crisv32_pinmux_alloc(PORT_B, 8, 23, pinmux_fixed); | ||
105 | ret |= crisv32_pinmux_alloc(PORT_B, 24, 25, pinmux_fixed); | ||
106 | hwprot.eth = hwprot.eth_mdio = regk_pinmux_yes; | ||
107 | break; | ||
108 | case pinmux_geth: | ||
109 | ret = crisv32_pinmux_alloc(PORT_B, 0, 7, pinmux_fixed); | ||
110 | hwprot.geth = regk_pinmux_yes; | ||
111 | break; | ||
112 | case pinmux_tg_cmos: | ||
113 | clk_ctrl.ccd_tg_100 = clk_ctrl.ccd_tg_200 = regk_clkgen_yes; | ||
114 | ret = crisv32_pinmux_alloc(PORT_B, 27, 29, pinmux_fixed); | ||
115 | hwprot.tg_clk = regk_pinmux_yes; | ||
116 | break; | ||
117 | case pinmux_tg_ccd: | ||
118 | clk_ctrl.ccd_tg_100 = clk_ctrl.ccd_tg_200 = regk_clkgen_yes; | ||
119 | ret = crisv32_pinmux_alloc(PORT_B, 27, 31, pinmux_fixed); | ||
120 | ret |= crisv32_pinmux_alloc(PORT_C, 0, 15, pinmux_fixed); | ||
121 | hwprot.tg = hwprot.tg_clk = regk_pinmux_yes; | ||
122 | break; | ||
123 | case pinmux_vout: | ||
124 | clk_ctrl.strdma0_2_video = regk_clkgen_yes; | ||
125 | ret = crisv32_pinmux_alloc(PORT_A, 8, 18, pinmux_fixed); | ||
126 | hwprot.vout = hwprot.vout_sync = regk_pinmux_yes; | ||
127 | break; | ||
128 | case pinmux_ser1: | ||
129 | clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; | ||
130 | ret = crisv32_pinmux_alloc(PORT_A, 24, 25, pinmux_fixed); | ||
131 | hwprot.ser1 = regk_pinmux_yes; | ||
132 | break; | ||
133 | case pinmux_ser2: | ||
134 | clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; | ||
135 | ret = crisv32_pinmux_alloc(PORT_A, 26, 27, pinmux_fixed); | ||
136 | hwprot.ser2 = regk_pinmux_yes; | ||
137 | break; | ||
138 | case pinmux_ser3: | ||
139 | clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; | ||
140 | ret = crisv32_pinmux_alloc(PORT_A, 28, 29, pinmux_fixed); | ||
141 | hwprot.ser3 = regk_pinmux_yes; | ||
142 | break; | ||
143 | case pinmux_ser4: | ||
144 | clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; | ||
145 | ret = crisv32_pinmux_alloc(PORT_A, 30, 31, pinmux_fixed); | ||
146 | hwprot.ser4 = regk_pinmux_yes; | ||
147 | break; | ||
148 | case pinmux_sser: | ||
149 | clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; | ||
150 | ret = crisv32_pinmux_alloc(PORT_A, 19, 23, pinmux_fixed); | ||
151 | hwprot.sser = regk_pinmux_yes; | ||
152 | break; | ||
153 | case pinmux_pio: | ||
154 | hwprot.pio = regk_pinmux_yes; | ||
155 | ret = 0; | ||
156 | break; | ||
157 | case pinmux_pwm0: | ||
158 | ret = crisv32_pinmux_alloc(PORT_A, 30, 30, pinmux_fixed); | ||
159 | hwprot.pwm0 = regk_pinmux_yes; | ||
160 | break; | ||
161 | case pinmux_pwm1: | ||
162 | ret = crisv32_pinmux_alloc(PORT_A, 31, 31, pinmux_fixed); | ||
163 | hwprot.pwm1 = regk_pinmux_yes; | ||
164 | break; | ||
165 | case pinmux_pwm2: | ||
166 | ret = crisv32_pinmux_alloc(PORT_B, 26, 26, pinmux_fixed); | ||
167 | hwprot.pwm2 = regk_pinmux_yes; | ||
168 | break; | ||
169 | case pinmux_i2c0: | ||
170 | ret = crisv32_pinmux_alloc(PORT_A, 0, 1, pinmux_fixed); | ||
171 | hwprot.i2c0 = regk_pinmux_yes; | ||
172 | break; | ||
173 | case pinmux_i2c1: | ||
174 | ret = crisv32_pinmux_alloc(PORT_A, 2, 3, pinmux_fixed); | ||
175 | hwprot.i2c1 = regk_pinmux_yes; | ||
176 | break; | ||
177 | case pinmux_i2c1_3wire: | ||
178 | ret = crisv32_pinmux_alloc(PORT_A, 2, 3, pinmux_fixed); | ||
179 | ret |= crisv32_pinmux_alloc(PORT_A, 7, 7, pinmux_fixed); | ||
180 | hwprot.i2c1 = hwprot.i2c1_sen = regk_pinmux_yes; | ||
181 | break; | ||
182 | case pinmux_i2c1_sda1: | ||
183 | ret = crisv32_pinmux_alloc(PORT_A, 2, 4, pinmux_fixed); | ||
184 | hwprot.i2c1 = hwprot.i2c1_sda1 = regk_pinmux_yes; | ||
185 | break; | ||
186 | case pinmux_i2c1_sda2: | ||
187 | ret = crisv32_pinmux_alloc(PORT_A, 2, 3, pinmux_fixed); | ||
188 | ret |= crisv32_pinmux_alloc(PORT_A, 5, 5, pinmux_fixed); | ||
189 | hwprot.i2c1 = hwprot.i2c1_sda2 = regk_pinmux_yes; | ||
190 | break; | ||
191 | case pinmux_i2c1_sda3: | ||
192 | ret = crisv32_pinmux_alloc(PORT_A, 2, 3, pinmux_fixed); | ||
193 | ret |= crisv32_pinmux_alloc(PORT_A, 6, 6, pinmux_fixed); | ||
194 | hwprot.i2c1 = hwprot.i2c1_sda3 = regk_pinmux_yes; | ||
195 | break; | ||
196 | default: | ||
197 | ret = -EINVAL; | ||
198 | break; | ||
199 | } | ||
200 | |||
201 | if (!ret) { | ||
202 | REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); | ||
203 | REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, clk_ctrl); | ||
204 | } else | ||
205 | memcpy(pins, saved, sizeof pins); | ||
206 | |||
207 | spin_unlock_irqrestore(&pinmux_lock, flags); | ||
208 | |||
209 | return ret; | ||
210 | } | ||
211 | |||
212 | void | ||
213 | crisv32_pinmux_set(int port) | ||
214 | { | ||
215 | int i; | ||
216 | int gpio_val = 0; | ||
217 | int iop_val = 0; | ||
218 | int pin = port * PORT_PINS; | ||
219 | |||
220 | for (i = 0; (i < PORT_PINS) && (pin < PINS); i++, pin++) { | ||
221 | if (pins[pin] == pinmux_gpio) | ||
222 | gpio_val |= (1 << i); | ||
223 | else if (pins[pin] == pinmux_iop) | ||
224 | iop_val |= (1 << i); | ||
225 | } | ||
226 | |||
227 | REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_gio_pa + 4 * port, | ||
228 | gpio_val); | ||
229 | REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_iop_pa + 4 * port, | ||
230 | iop_val); | ||
231 | |||
232 | #ifdef DEBUG | ||
233 | crisv32_pinmux_dump(); | ||
234 | #endif | ||
235 | } | ||
236 | |||
237 | int | ||
238 | crisv32_pinmux_dealloc(int port, int first_pin, int last_pin) | ||
239 | { | ||
240 | int i; | ||
241 | unsigned long flags; | ||
242 | |||
243 | crisv32_pinmux_init(); | ||
244 | |||
245 | if (port > PORTS) | ||
246 | return -EINVAL; | ||
247 | |||
248 | spin_lock_irqsave(&pinmux_lock, flags); | ||
249 | |||
250 | for (i = first_pin; i <= last_pin; i++) | ||
251 | pins[port * PORT_PINS + i] = pinmux_none; | ||
252 | |||
253 | crisv32_pinmux_set(port); | ||
254 | spin_unlock_irqrestore(&pinmux_lock, flags); | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | int | ||
260 | crisv32_pinmux_dealloc_fixed(enum fixed_function function) | ||
261 | { | ||
262 | int ret = -EINVAL; | ||
263 | char saved[sizeof pins]; | ||
264 | unsigned long flags; | ||
265 | |||
266 | spin_lock_irqsave(&pinmux_lock, flags); | ||
267 | |||
268 | /* Save internal data for recovery */ | ||
269 | memcpy(saved, pins, sizeof pins); | ||
270 | |||
271 | crisv32_pinmux_init(); /* must be done before we read rw_hwprot */ | ||
272 | |||
273 | reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); | ||
274 | |||
275 | switch (function) { | ||
276 | case pinmux_eth: | ||
277 | ret = crisv32_pinmux_dealloc(PORT_B, 8, 23); | ||
278 | ret |= crisv32_pinmux_dealloc(PORT_B, 24, 25); | ||
279 | ret |= crisv32_pinmux_dealloc(PORT_B, 0, 7); | ||
280 | hwprot.eth = hwprot.eth_mdio = hwprot.geth = regk_pinmux_no; | ||
281 | break; | ||
282 | case pinmux_tg_cmos: | ||
283 | ret = crisv32_pinmux_dealloc(PORT_B, 27, 29); | ||
284 | hwprot.tg_clk = regk_pinmux_no; | ||
285 | break; | ||
286 | case pinmux_tg_ccd: | ||
287 | ret = crisv32_pinmux_dealloc(PORT_B, 27, 31); | ||
288 | ret |= crisv32_pinmux_dealloc(PORT_C, 0, 15); | ||
289 | hwprot.tg = hwprot.tg_clk = regk_pinmux_no; | ||
290 | break; | ||
291 | case pinmux_vout: | ||
292 | ret = crisv32_pinmux_dealloc(PORT_A, 8, 18); | ||
293 | hwprot.vout = hwprot.vout_sync = regk_pinmux_no; | ||
294 | break; | ||
295 | case pinmux_ser1: | ||
296 | ret = crisv32_pinmux_dealloc(PORT_A, 24, 25); | ||
297 | hwprot.ser1 = regk_pinmux_no; | ||
298 | break; | ||
299 | case pinmux_ser2: | ||
300 | ret = crisv32_pinmux_dealloc(PORT_A, 26, 27); | ||
301 | hwprot.ser2 = regk_pinmux_no; | ||
302 | break; | ||
303 | case pinmux_ser3: | ||
304 | ret = crisv32_pinmux_dealloc(PORT_A, 28, 29); | ||
305 | hwprot.ser3 = regk_pinmux_no; | ||
306 | break; | ||
307 | case pinmux_ser4: | ||
308 | ret = crisv32_pinmux_dealloc(PORT_A, 30, 31); | ||
309 | hwprot.ser4 = regk_pinmux_no; | ||
310 | break; | ||
311 | case pinmux_sser: | ||
312 | ret = crisv32_pinmux_dealloc(PORT_A, 19, 23); | ||
313 | hwprot.sser = regk_pinmux_no; | ||
314 | break; | ||
315 | case pinmux_pwm0: | ||
316 | ret = crisv32_pinmux_dealloc(PORT_A, 30, 30); | ||
317 | hwprot.pwm0 = regk_pinmux_no; | ||
318 | break; | ||
319 | case pinmux_pwm1: | ||
320 | ret = crisv32_pinmux_dealloc(PORT_A, 31, 31); | ||
321 | hwprot.pwm1 = regk_pinmux_no; | ||
322 | break; | ||
323 | case pinmux_pwm2: | ||
324 | ret = crisv32_pinmux_dealloc(PORT_B, 26, 26); | ||
325 | hwprot.pwm2 = regk_pinmux_no; | ||
326 | break; | ||
327 | case pinmux_i2c0: | ||
328 | ret = crisv32_pinmux_dealloc(PORT_A, 0, 1); | ||
329 | hwprot.i2c0 = regk_pinmux_no; | ||
330 | break; | ||
331 | case pinmux_i2c1: | ||
332 | ret = crisv32_pinmux_dealloc(PORT_A, 2, 3); | ||
333 | hwprot.i2c1 = regk_pinmux_no; | ||
334 | break; | ||
335 | case pinmux_i2c1_3wire: | ||
336 | ret = crisv32_pinmux_dealloc(PORT_A, 2, 3); | ||
337 | ret |= crisv32_pinmux_dealloc(PORT_A, 7, 7); | ||
338 | hwprot.i2c1 = hwprot.i2c1_sen = regk_pinmux_no; | ||
339 | break; | ||
340 | case pinmux_i2c1_sda1: | ||
341 | ret = crisv32_pinmux_dealloc(PORT_A, 2, 4); | ||
342 | hwprot.i2c1_sda1 = regk_pinmux_no; | ||
343 | break; | ||
344 | case pinmux_i2c1_sda2: | ||
345 | ret = crisv32_pinmux_dealloc(PORT_A, 2, 3); | ||
346 | ret |= crisv32_pinmux_dealloc(PORT_A, 5, 5); | ||
347 | hwprot.i2c1_sda2 = regk_pinmux_no; | ||
348 | break; | ||
349 | case pinmux_i2c1_sda3: | ||
350 | ret = crisv32_pinmux_dealloc(PORT_A, 2, 3); | ||
351 | ret |= crisv32_pinmux_dealloc(PORT_A, 6, 6); | ||
352 | hwprot.i2c1_sda3 = regk_pinmux_no; | ||
353 | break; | ||
354 | default: | ||
355 | ret = -EINVAL; | ||
356 | break; | ||
357 | } | ||
358 | |||
359 | if (!ret) | ||
360 | REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); | ||
361 | else | ||
362 | memcpy(pins, saved, sizeof pins); | ||
363 | |||
364 | spin_unlock_irqrestore(&pinmux_lock, flags); | ||
365 | |||
366 | return ret; | ||
367 | } | ||
368 | |||
369 | void | ||
370 | crisv32_pinmux_dump(void) | ||
371 | { | ||
372 | int i, j; | ||
373 | int pin = 0; | ||
374 | |||
375 | crisv32_pinmux_init(); | ||
376 | |||
377 | for (i = 0; i < PORTS; i++) { | ||
378 | pin++; | ||
379 | printk(KERN_DEBUG "Port %c\n", 'A'+i); | ||
380 | for (j = 0; (j < PORT_PINS) && (pin < PINS); j++, pin++) | ||
381 | printk(KERN_DEBUG | ||
382 | " Pin %d = %d\n", j, pins[i * PORT_PINS + j]); | ||
383 | } | ||
384 | } | ||
385 | |||
386 | __initcall(crisv32_pinmux_init); | ||