]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
035e111f JN |
2 | /* |
3 | * Allocator for I/O pins. All pins are allocated to GPIO at bootup. | |
4 | * Unassigned pins and GPIO pins can be allocated to a fixed interface | |
5 | * or the I/O processor instead. | |
6 | * | |
7 | * Copyright (c) 2004-2007 Axis Communications AB. | |
8 | */ | |
9 | ||
10 | #include <linux/init.h> | |
11 | #include <linux/errno.h> | |
12 | #include <linux/kernel.h> | |
13 | #include <linux/string.h> | |
14 | #include <linux/spinlock.h> | |
15 | #include <hwregs/reg_map.h> | |
16 | #include <hwregs/reg_rdwr.h> | |
17 | #include <pinmux.h> | |
18 | #include <hwregs/pinmux_defs.h> | |
19 | ||
20 | #undef DEBUG | |
21 | ||
22 | #define PORT_PINS 18 | |
23 | #define PORTS 4 | |
24 | ||
25 | static char pins[PORTS][PORT_PINS]; | |
26 | static DEFINE_SPINLOCK(pinmux_lock); | |
27 | ||
28 | static void crisv32_pinmux_set(int port); | |
29 | ||
a3199ad9 JN |
30 | static int __crisv32_pinmux_alloc(int port, int first_pin, int last_pin, |
31 | enum pin_mode mode) | |
32 | { | |
33 | int i; | |
34 | ||
35 | for (i = first_pin; i <= last_pin; i++) { | |
36 | if ((pins[port][i] != pinmux_none) | |
37 | && (pins[port][i] != pinmux_gpio) | |
38 | && (pins[port][i] != mode)) { | |
39 | #ifdef DEBUG | |
40 | panic("Pinmux alloc failed!\n"); | |
41 | #endif | |
42 | return -EPERM; | |
43 | } | |
44 | } | |
45 | ||
46 | for (i = first_pin; i <= last_pin; i++) | |
47 | pins[port][i] = mode; | |
48 | ||
49 | crisv32_pinmux_set(port); | |
94230178 JN |
50 | |
51 | return 0; | |
a3199ad9 JN |
52 | } |
53 | ||
54 | static int crisv32_pinmux_init(void) | |
035e111f JN |
55 | { |
56 | static int initialized; | |
57 | ||
58 | if (!initialized) { | |
59 | reg_pinmux_rw_pa pa = REG_RD(pinmux, regi_pinmux, rw_pa); | |
60 | initialized = 1; | |
61 | REG_WR_INT(pinmux, regi_pinmux, rw_hwprot, 0); | |
62 | pa.pa0 = pa.pa1 = pa.pa2 = pa.pa3 = | |
63 | pa.pa4 = pa.pa5 = pa.pa6 = pa.pa7 = regk_pinmux_yes; | |
64 | REG_WR(pinmux, regi_pinmux, rw_pa, pa); | |
a3199ad9 JN |
65 | __crisv32_pinmux_alloc(PORT_B, 0, PORT_PINS - 1, pinmux_gpio); |
66 | __crisv32_pinmux_alloc(PORT_C, 0, PORT_PINS - 1, pinmux_gpio); | |
67 | __crisv32_pinmux_alloc(PORT_D, 0, PORT_PINS - 1, pinmux_gpio); | |
68 | __crisv32_pinmux_alloc(PORT_E, 0, PORT_PINS - 1, pinmux_gpio); | |
035e111f JN |
69 | } |
70 | ||
71 | return 0; | |
72 | } | |
73 | ||
a3199ad9 JN |
74 | int crisv32_pinmux_alloc(int port, int first_pin, int last_pin, |
75 | enum pin_mode mode) | |
035e111f | 76 | { |
035e111f | 77 | unsigned long flags; |
a3199ad9 | 78 | int ret; |
035e111f JN |
79 | |
80 | crisv32_pinmux_init(); | |
81 | ||
f25234f1 | 82 | if (port > PORTS || port < 0) |
035e111f JN |
83 | return -EINVAL; |
84 | ||
85 | spin_lock_irqsave(&pinmux_lock, flags); | |
86 | ||
a3199ad9 | 87 | ret = __crisv32_pinmux_alloc(port, first_pin, last_pin, mode); |
035e111f JN |
88 | |
89 | spin_unlock_irqrestore(&pinmux_lock, flags); | |
90 | ||
a3199ad9 | 91 | return ret; |
035e111f JN |
92 | } |
93 | ||
94 | int crisv32_pinmux_alloc_fixed(enum fixed_function function) | |
95 | { | |
96 | int ret = -EINVAL; | |
97 | char saved[sizeof pins]; | |
98 | unsigned long flags; | |
94230178 | 99 | reg_pinmux_rw_hwprot hwprot; |
035e111f JN |
100 | |
101 | spin_lock_irqsave(&pinmux_lock, flags); | |
102 | ||
103 | /* Save internal data for recovery */ | |
104 | memcpy(saved, pins, sizeof pins); | |
105 | ||
106 | crisv32_pinmux_init(); /* Must be done before we read rw_hwprot */ | |
107 | ||
94230178 | 108 | hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); |
035e111f JN |
109 | |
110 | switch (function) { | |
111 | case pinmux_ser1: | |
a3199ad9 | 112 | ret = __crisv32_pinmux_alloc(PORT_C, 4, 7, pinmux_fixed); |
035e111f JN |
113 | hwprot.ser1 = regk_pinmux_yes; |
114 | break; | |
115 | case pinmux_ser2: | |
a3199ad9 | 116 | ret = __crisv32_pinmux_alloc(PORT_C, 8, 11, pinmux_fixed); |
035e111f JN |
117 | hwprot.ser2 = regk_pinmux_yes; |
118 | break; | |
119 | case pinmux_ser3: | |
a3199ad9 | 120 | ret = __crisv32_pinmux_alloc(PORT_C, 12, 15, pinmux_fixed); |
035e111f JN |
121 | hwprot.ser3 = regk_pinmux_yes; |
122 | break; | |
123 | case pinmux_sser0: | |
a3199ad9 JN |
124 | ret = __crisv32_pinmux_alloc(PORT_C, 0, 3, pinmux_fixed); |
125 | ret |= __crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed); | |
035e111f JN |
126 | hwprot.sser0 = regk_pinmux_yes; |
127 | break; | |
128 | case pinmux_sser1: | |
a3199ad9 | 129 | ret = __crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed); |
035e111f JN |
130 | hwprot.sser1 = regk_pinmux_yes; |
131 | break; | |
132 | case pinmux_ata0: | |
a3199ad9 JN |
133 | ret = __crisv32_pinmux_alloc(PORT_D, 5, 7, pinmux_fixed); |
134 | ret |= __crisv32_pinmux_alloc(PORT_D, 15, 17, pinmux_fixed); | |
035e111f JN |
135 | hwprot.ata0 = regk_pinmux_yes; |
136 | break; | |
137 | case pinmux_ata1: | |
a3199ad9 JN |
138 | ret = __crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed); |
139 | ret |= __crisv32_pinmux_alloc(PORT_E, 17, 17, pinmux_fixed); | |
035e111f JN |
140 | hwprot.ata1 = regk_pinmux_yes; |
141 | break; | |
142 | case pinmux_ata2: | |
a3199ad9 JN |
143 | ret = __crisv32_pinmux_alloc(PORT_C, 11, 15, pinmux_fixed); |
144 | ret |= __crisv32_pinmux_alloc(PORT_E, 3, 3, pinmux_fixed); | |
035e111f JN |
145 | hwprot.ata2 = regk_pinmux_yes; |
146 | break; | |
147 | case pinmux_ata3: | |
a3199ad9 JN |
148 | ret = __crisv32_pinmux_alloc(PORT_C, 8, 10, pinmux_fixed); |
149 | ret |= __crisv32_pinmux_alloc(PORT_C, 0, 2, pinmux_fixed); | |
035e111f JN |
150 | hwprot.ata2 = regk_pinmux_yes; |
151 | break; | |
152 | case pinmux_ata: | |
a3199ad9 JN |
153 | ret = __crisv32_pinmux_alloc(PORT_B, 0, 15, pinmux_fixed); |
154 | ret |= __crisv32_pinmux_alloc(PORT_D, 8, 15, pinmux_fixed); | |
035e111f JN |
155 | hwprot.ata = regk_pinmux_yes; |
156 | break; | |
157 | case pinmux_eth1: | |
a3199ad9 | 158 | ret = __crisv32_pinmux_alloc(PORT_E, 0, 17, pinmux_fixed); |
035e111f JN |
159 | hwprot.eth1 = regk_pinmux_yes; |
160 | hwprot.eth1_mgm = regk_pinmux_yes; | |
161 | break; | |
162 | case pinmux_timer: | |
a3199ad9 | 163 | ret = __crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed); |
035e111f JN |
164 | hwprot.timer = regk_pinmux_yes; |
165 | spin_unlock_irqrestore(&pinmux_lock, flags); | |
166 | return ret; | |
167 | } | |
168 | ||
169 | if (!ret) | |
170 | REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); | |
171 | else | |
172 | memcpy(pins, saved, sizeof pins); | |
173 | ||
174 | spin_unlock_irqrestore(&pinmux_lock, flags); | |
175 | ||
176 | return ret; | |
177 | } | |
178 | ||
179 | void crisv32_pinmux_set(int port) | |
180 | { | |
181 | int i; | |
182 | int gpio_val = 0; | |
183 | int iop_val = 0; | |
184 | ||
185 | for (i = 0; i < PORT_PINS; i++) { | |
186 | if (pins[port][i] == pinmux_gpio) | |
187 | gpio_val |= (1 << i); | |
188 | else if (pins[port][i] == pinmux_iop) | |
189 | iop_val |= (1 << i); | |
190 | } | |
191 | ||
192 | REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_pb_gio + 8 * port, | |
193 | gpio_val); | |
194 | REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_pb_iop + 8 * port, | |
195 | iop_val); | |
196 | ||
197 | #ifdef DEBUG | |
198 | crisv32_pinmux_dump(); | |
199 | #endif | |
200 | } | |
201 | ||
a3199ad9 | 202 | static int __crisv32_pinmux_dealloc(int port, int first_pin, int last_pin) |
035e111f JN |
203 | { |
204 | int i; | |
a3199ad9 JN |
205 | |
206 | for (i = first_pin; i <= last_pin; i++) | |
207 | pins[port][i] = pinmux_none; | |
208 | ||
209 | crisv32_pinmux_set(port); | |
210 | return 0; | |
211 | } | |
212 | ||
213 | int crisv32_pinmux_dealloc(int port, int first_pin, int last_pin) | |
214 | { | |
035e111f JN |
215 | unsigned long flags; |
216 | ||
217 | crisv32_pinmux_init(); | |
218 | ||
f25234f1 | 219 | if (port > PORTS || port < 0) |
035e111f JN |
220 | return -EINVAL; |
221 | ||
222 | spin_lock_irqsave(&pinmux_lock, flags); | |
a3199ad9 | 223 | __crisv32_pinmux_dealloc(port, first_pin, last_pin); |
035e111f JN |
224 | spin_unlock_irqrestore(&pinmux_lock, flags); |
225 | ||
226 | return 0; | |
227 | } | |
228 | ||
229 | int crisv32_pinmux_dealloc_fixed(enum fixed_function function) | |
230 | { | |
231 | int ret = -EINVAL; | |
232 | char saved[sizeof pins]; | |
233 | unsigned long flags; | |
94230178 | 234 | reg_pinmux_rw_hwprot hwprot; |
035e111f JN |
235 | |
236 | spin_lock_irqsave(&pinmux_lock, flags); | |
237 | ||
238 | /* Save internal data for recovery */ | |
239 | memcpy(saved, pins, sizeof pins); | |
240 | ||
241 | crisv32_pinmux_init(); /* Must be done before we read rw_hwprot */ | |
242 | ||
94230178 | 243 | hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); |
035e111f JN |
244 | |
245 | switch (function) { | |
246 | case pinmux_ser1: | |
a3199ad9 | 247 | ret = __crisv32_pinmux_dealloc(PORT_C, 4, 7); |
035e111f JN |
248 | hwprot.ser1 = regk_pinmux_no; |
249 | break; | |
250 | case pinmux_ser2: | |
a3199ad9 | 251 | ret = __crisv32_pinmux_dealloc(PORT_C, 8, 11); |
035e111f JN |
252 | hwprot.ser2 = regk_pinmux_no; |
253 | break; | |
254 | case pinmux_ser3: | |
a3199ad9 | 255 | ret = __crisv32_pinmux_dealloc(PORT_C, 12, 15); |
035e111f JN |
256 | hwprot.ser3 = regk_pinmux_no; |
257 | break; | |
258 | case pinmux_sser0: | |
a3199ad9 JN |
259 | ret = __crisv32_pinmux_dealloc(PORT_C, 0, 3); |
260 | ret |= __crisv32_pinmux_dealloc(PORT_C, 16, 16); | |
035e111f JN |
261 | hwprot.sser0 = regk_pinmux_no; |
262 | break; | |
263 | case pinmux_sser1: | |
a3199ad9 | 264 | ret = __crisv32_pinmux_dealloc(PORT_D, 0, 4); |
035e111f JN |
265 | hwprot.sser1 = regk_pinmux_no; |
266 | break; | |
267 | case pinmux_ata0: | |
a3199ad9 JN |
268 | ret = __crisv32_pinmux_dealloc(PORT_D, 5, 7); |
269 | ret |= __crisv32_pinmux_dealloc(PORT_D, 15, 17); | |
035e111f JN |
270 | hwprot.ata0 = regk_pinmux_no; |
271 | break; | |
272 | case pinmux_ata1: | |
a3199ad9 JN |
273 | ret = __crisv32_pinmux_dealloc(PORT_D, 0, 4); |
274 | ret |= __crisv32_pinmux_dealloc(PORT_E, 17, 17); | |
035e111f JN |
275 | hwprot.ata1 = regk_pinmux_no; |
276 | break; | |
277 | case pinmux_ata2: | |
a3199ad9 JN |
278 | ret = __crisv32_pinmux_dealloc(PORT_C, 11, 15); |
279 | ret |= __crisv32_pinmux_dealloc(PORT_E, 3, 3); | |
035e111f JN |
280 | hwprot.ata2 = regk_pinmux_no; |
281 | break; | |
282 | case pinmux_ata3: | |
a3199ad9 JN |
283 | ret = __crisv32_pinmux_dealloc(PORT_C, 8, 10); |
284 | ret |= __crisv32_pinmux_dealloc(PORT_C, 0, 2); | |
035e111f JN |
285 | hwprot.ata2 = regk_pinmux_no; |
286 | break; | |
287 | case pinmux_ata: | |
a3199ad9 JN |
288 | ret = __crisv32_pinmux_dealloc(PORT_B, 0, 15); |
289 | ret |= __crisv32_pinmux_dealloc(PORT_D, 8, 15); | |
035e111f JN |
290 | hwprot.ata = regk_pinmux_no; |
291 | break; | |
292 | case pinmux_eth1: | |
a3199ad9 | 293 | ret = __crisv32_pinmux_dealloc(PORT_E, 0, 17); |
035e111f JN |
294 | hwprot.eth1 = regk_pinmux_no; |
295 | hwprot.eth1_mgm = regk_pinmux_no; | |
296 | break; | |
297 | case pinmux_timer: | |
a3199ad9 | 298 | ret = __crisv32_pinmux_dealloc(PORT_C, 16, 16); |
035e111f JN |
299 | hwprot.timer = regk_pinmux_no; |
300 | spin_unlock_irqrestore(&pinmux_lock, flags); | |
301 | return ret; | |
302 | } | |
303 | ||
304 | if (!ret) | |
305 | REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); | |
306 | else | |
307 | memcpy(pins, saved, sizeof pins); | |
308 | ||
309 | spin_unlock_irqrestore(&pinmux_lock, flags); | |
310 | ||
311 | return ret; | |
312 | } | |
313 | ||
a3199ad9 JN |
314 | #ifdef DEBUG |
315 | static void crisv32_pinmux_dump(void) | |
035e111f JN |
316 | { |
317 | int i, j; | |
318 | ||
319 | crisv32_pinmux_init(); | |
320 | ||
321 | for (i = 0; i < PORTS; i++) { | |
322 | printk(KERN_DEBUG "Port %c\n", 'B' + i); | |
323 | for (j = 0; j < PORT_PINS; j++) | |
324 | printk(KERN_DEBUG " Pin %d = %d\n", j, pins[i][j]); | |
325 | } | |
326 | } | |
a3199ad9 | 327 | #endif |
035e111f | 328 | __initcall(crisv32_pinmux_init); |