diff options
-rw-r--r-- | arch/blackfin/kernel/bfin_gpio.c | 185 |
1 files changed, 118 insertions, 67 deletions
diff --git a/arch/blackfin/kernel/bfin_gpio.c b/arch/blackfin/kernel/bfin_gpio.c index ca1c1f9debd6..170cf90735ba 100644 --- a/arch/blackfin/kernel/bfin_gpio.c +++ b/arch/blackfin/kernel/bfin_gpio.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * GPIO Abstraction Layer | 2 | * GPIO Abstraction Layer |
3 | * | 3 | * |
4 | * Copyright 2006-2009 Analog Devices Inc. | 4 | * Copyright 2006-2010 Analog Devices Inc. |
5 | * | 5 | * |
6 | * Licensed under the GPL-2 or later | 6 | * Licensed under the GPL-2 or later |
7 | */ | 7 | */ |
@@ -215,82 +215,91 @@ static void port_setup(unsigned gpio, unsigned short usage) | |||
215 | } | 215 | } |
216 | 216 | ||
217 | #ifdef BF537_FAMILY | 217 | #ifdef BF537_FAMILY |
218 | static struct { | 218 | static const s8 port_mux[] = { |
219 | unsigned short res; | 219 | [GPIO_PF0] = 3, |
220 | unsigned short offset; | 220 | [GPIO_PF1] = 3, |
221 | } port_mux_lut[] = { | 221 | [GPIO_PF2] = 4, |
222 | {.res = P_PPI0_D13, .offset = 11}, | 222 | [GPIO_PF3] = 4, |
223 | {.res = P_PPI0_D14, .offset = 11}, | 223 | [GPIO_PF4] = 5, |
224 | {.res = P_PPI0_D15, .offset = 11}, | 224 | [GPIO_PF5] = 6, |
225 | {.res = P_SPORT1_TFS, .offset = 11}, | 225 | [GPIO_PF6] = 7, |
226 | {.res = P_SPORT1_TSCLK, .offset = 11}, | 226 | [GPIO_PF7] = 8, |
227 | {.res = P_SPORT1_DTPRI, .offset = 11}, | 227 | [GPIO_PF8 ... GPIO_PF15] = -1, |
228 | {.res = P_PPI0_D10, .offset = 10}, | 228 | [GPIO_PG0 ... GPIO_PG7] = -1, |
229 | {.res = P_PPI0_D11, .offset = 10}, | 229 | [GPIO_PG8] = 9, |
230 | {.res = P_PPI0_D12, .offset = 10}, | 230 | [GPIO_PG9] = 9, |
231 | {.res = P_SPORT1_RSCLK, .offset = 10}, | 231 | [GPIO_PG10] = 10, |
232 | {.res = P_SPORT1_RFS, .offset = 10}, | 232 | [GPIO_PG11] = 10, |
233 | {.res = P_SPORT1_DRPRI, .offset = 10}, | 233 | [GPIO_PG12] = 10, |
234 | {.res = P_PPI0_D8, .offset = 9}, | 234 | [GPIO_PG13] = 11, |
235 | {.res = P_PPI0_D9, .offset = 9}, | 235 | [GPIO_PG14] = 11, |
236 | {.res = P_SPORT1_DRSEC, .offset = 9}, | 236 | [GPIO_PG15] = 11, |
237 | {.res = P_SPORT1_DTSEC, .offset = 9}, | 237 | [GPIO_PH0 ... GPIO_PH15] = -1, |
238 | {.res = P_TMR2, .offset = 8}, | 238 | [PORT_PJ0 ... PORT_PJ3] = -1, |
239 | {.res = P_PPI0_FS3, .offset = 8}, | 239 | [PORT_PJ4] = 1, |
240 | {.res = P_TMR3, .offset = 7}, | 240 | [PORT_PJ5] = 1, |
241 | {.res = P_SPI0_SSEL4, .offset = 7}, | 241 | [PORT_PJ6 ... PORT_PJ9] = -1, |
242 | {.res = P_TMR4, .offset = 6}, | 242 | [PORT_PJ10] = 0, |
243 | {.res = P_SPI0_SSEL5, .offset = 6}, | 243 | [PORT_PJ11] = 0, |
244 | {.res = P_TMR5, .offset = 5}, | ||
245 | {.res = P_SPI0_SSEL6, .offset = 5}, | ||
246 | {.res = P_UART1_RX, .offset = 4}, | ||
247 | {.res = P_UART1_TX, .offset = 4}, | ||
248 | {.res = P_TMR6, .offset = 4}, | ||
249 | {.res = P_TMR7, .offset = 4}, | ||
250 | {.res = P_UART0_RX, .offset = 3}, | ||
251 | {.res = P_UART0_TX, .offset = 3}, | ||
252 | {.res = P_DMAR0, .offset = 3}, | ||
253 | {.res = P_DMAR1, .offset = 3}, | ||
254 | {.res = P_SPORT0_DTSEC, .offset = 1}, | ||
255 | {.res = P_SPORT0_DRSEC, .offset = 1}, | ||
256 | {.res = P_CAN0_RX, .offset = 1}, | ||
257 | {.res = P_CAN0_TX, .offset = 1}, | ||
258 | {.res = P_SPI0_SSEL7, .offset = 1}, | ||
259 | {.res = P_SPORT0_TFS, .offset = 0}, | ||
260 | {.res = P_SPORT0_DTPRI, .offset = 0}, | ||
261 | {.res = P_SPI0_SSEL2, .offset = 0}, | ||
262 | {.res = P_SPI0_SSEL3, .offset = 0}, | ||
263 | }; | 244 | }; |
264 | 245 | ||
265 | static void portmux_setup(unsigned short per) | 246 | static int portmux_group_check(unsigned short per) |
266 | { | 247 | { |
267 | u16 y, offset, muxreg; | 248 | u16 ident = P_IDENT(per); |
268 | u16 function = P_FUNCT2MUX(per); | 249 | u16 function = P_FUNCT2MUX(per); |
250 | s8 offset = port_mux[ident]; | ||
251 | u16 m, pmux, pfunc; | ||
269 | 252 | ||
270 | for (y = 0; y < ARRAY_SIZE(port_mux_lut); y++) { | 253 | if (offset < 0) |
271 | if (port_mux_lut[y].res == per) { | 254 | return 0; |
272 | |||
273 | /* SET PORTMUX REG */ | ||
274 | |||
275 | offset = port_mux_lut[y].offset; | ||
276 | muxreg = bfin_read_PORT_MUX(); | ||
277 | 255 | ||
278 | if (offset != 1) | 256 | pmux = bfin_read_PORT_MUX(); |
279 | muxreg &= ~(1 << offset); | 257 | for (m = 0; m < ARRAY_SIZE(port_mux); ++m) { |
280 | else | 258 | if (m == ident) |
281 | muxreg &= ~(3 << 1); | 259 | continue; |
260 | if (port_mux[m] != offset) | ||
261 | continue; | ||
262 | if (!is_reserved(peri, m, 1)) | ||
263 | continue; | ||
282 | 264 | ||
283 | muxreg |= (function << offset); | 265 | if (offset == 1) |
284 | bfin_write_PORT_MUX(muxreg); | 266 | pfunc = (pmux >> offset) & 3; |
267 | else | ||
268 | pfunc = (pmux >> offset) & 1; | ||
269 | if (pfunc != function) { | ||
270 | pr_err("pin group conflict! request pin %d func %d conflict with pin %d func %d\n", | ||
271 | ident, function, m, pfunc); | ||
272 | return -EINVAL; | ||
285 | } | 273 | } |
286 | } | 274 | } |
275 | |||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | static void portmux_setup(unsigned short per) | ||
280 | { | ||
281 | u16 ident = P_IDENT(per); | ||
282 | u16 function = P_FUNCT2MUX(per); | ||
283 | s8 offset = port_mux[ident]; | ||
284 | u16 pmux; | ||
285 | |||
286 | if (offset == -1) | ||
287 | return; | ||
288 | |||
289 | pmux = bfin_read_PORT_MUX(); | ||
290 | if (offset != 1) | ||
291 | pmux &= ~(1 << offset); | ||
292 | else | ||
293 | pmux &= ~(3 << 1); | ||
294 | pmux |= (function << offset); | ||
295 | bfin_write_PORT_MUX(pmux); | ||
287 | } | 296 | } |
288 | #elif defined(CONFIG_BF54x) | 297 | #elif defined(CONFIG_BF54x) |
289 | inline void portmux_setup(unsigned short per) | 298 | inline void portmux_setup(unsigned short per) |
290 | { | 299 | { |
291 | u32 pmux; | ||
292 | u16 ident = P_IDENT(per); | 300 | u16 ident = P_IDENT(per); |
293 | u16 function = P_FUNCT2MUX(per); | 301 | u16 function = P_FUNCT2MUX(per); |
302 | u32 pmux; | ||
294 | 303 | ||
295 | pmux = gpio_array[gpio_bank(ident)]->port_mux; | 304 | pmux = gpio_array[gpio_bank(ident)]->port_mux; |
296 | 305 | ||
@@ -302,20 +311,54 @@ inline void portmux_setup(unsigned short per) | |||
302 | 311 | ||
303 | inline u16 get_portmux(unsigned short per) | 312 | inline u16 get_portmux(unsigned short per) |
304 | { | 313 | { |
305 | u32 pmux; | ||
306 | u16 ident = P_IDENT(per); | 314 | u16 ident = P_IDENT(per); |
307 | 315 | u32 pmux = gpio_array[gpio_bank(ident)]->port_mux; | |
308 | pmux = gpio_array[gpio_bank(ident)]->port_mux; | ||
309 | |||
310 | return (pmux >> (2 * gpio_sub_n(ident)) & 0x3); | 316 | return (pmux >> (2 * gpio_sub_n(ident)) & 0x3); |
311 | } | 317 | } |
318 | static int portmux_group_check(unsigned short per) | ||
319 | { | ||
320 | return 0; | ||
321 | } | ||
312 | #elif defined(CONFIG_BF52x) || defined(CONFIG_BF51x) | 322 | #elif defined(CONFIG_BF52x) || defined(CONFIG_BF51x) |
323 | static int portmux_group_check(unsigned short per) | ||
324 | { | ||
325 | u16 ident = P_IDENT(per); | ||
326 | u16 function = P_FUNCT2MUX(per); | ||
327 | u8 offset = pmux_offset[gpio_bank(ident)][gpio_sub_n(ident)]; | ||
328 | u16 pin, gpiopin, pfunc; | ||
329 | |||
330 | for (pin = 0; pin < GPIO_BANKSIZE; ++pin) { | ||
331 | if (offset != pmux_offset[gpio_bank(ident)][pin]) | ||
332 | continue; | ||
333 | |||
334 | gpiopin = gpio_bank(ident) * GPIO_BANKSIZE + pin; | ||
335 | if (gpiopin == ident) | ||
336 | continue; | ||
337 | if (!is_reserved(peri, gpiopin, 1)) | ||
338 | continue; | ||
339 | |||
340 | pfunc = *port_mux[gpio_bank(ident)]; | ||
341 | pfunc = (pfunc >> offset) & 3; | ||
342 | if (pfunc != function) { | ||
343 | pr_err("pin group conflict! request pin %d func %d conflict with pin %d func %d\n", | ||
344 | ident, function, gpiopin, pfunc); | ||
345 | return -EINVAL; | ||
346 | } | ||
347 | } | ||
348 | |||
349 | return 0; | ||
350 | } | ||
351 | |||
313 | inline void portmux_setup(unsigned short per) | 352 | inline void portmux_setup(unsigned short per) |
314 | { | 353 | { |
315 | u16 pmux, ident = P_IDENT(per), function = P_FUNCT2MUX(per); | 354 | u16 ident = P_IDENT(per); |
355 | u16 function = P_FUNCT2MUX(per); | ||
316 | u8 offset = pmux_offset[gpio_bank(ident)][gpio_sub_n(ident)]; | 356 | u8 offset = pmux_offset[gpio_bank(ident)][gpio_sub_n(ident)]; |
357 | u16 pmux; | ||
317 | 358 | ||
318 | pmux = *port_mux[gpio_bank(ident)]; | 359 | pmux = *port_mux[gpio_bank(ident)]; |
360 | if (((pmux >> offset) & 3) == function) | ||
361 | return; | ||
319 | pmux &= ~(3 << offset); | 362 | pmux &= ~(3 << offset); |
320 | pmux |= (function & 3) << offset; | 363 | pmux |= (function & 3) << offset; |
321 | *port_mux[gpio_bank(ident)] = pmux; | 364 | *port_mux[gpio_bank(ident)] = pmux; |
@@ -323,6 +366,10 @@ inline void portmux_setup(unsigned short per) | |||
323 | } | 366 | } |
324 | #else | 367 | #else |
325 | # define portmux_setup(...) do { } while (0) | 368 | # define portmux_setup(...) do { } while (0) |
369 | static int portmux_group_check(unsigned short per) | ||
370 | { | ||
371 | return 0; | ||
372 | } | ||
326 | #endif | 373 | #endif |
327 | 374 | ||
328 | #ifndef CONFIG_BF54x | 375 | #ifndef CONFIG_BF54x |
@@ -735,6 +782,10 @@ int peripheral_request(unsigned short per, const char *label) | |||
735 | } | 782 | } |
736 | } | 783 | } |
737 | 784 | ||
785 | if (unlikely(portmux_group_check(per))) { | ||
786 | hard_local_irq_restore(flags); | ||
787 | return -EBUSY; | ||
788 | } | ||
738 | anyway: | 789 | anyway: |
739 | reserve(peri, ident); | 790 | reserve(peri, ident); |
740 | 791 | ||