mirror of
https://github.com/torvalds/linux.git
synced 2024-11-16 00:52:01 +00:00
Blackfin: gpio: add peripheral group check
Many Blackfin parts group sets of pins into a single functional unit. This means you cannot use different pins within a group for different peripherals. Our resource conflict checking thus far has been limited to individual pins, so if someone tried to grab a different pin from the same group, it would be allowed while silently changing the other pins in the same group. One common example is the pin set PG12 - PG15 on BF51x parts. They may either be used with SPI0 (1st function), or they may be used with PTP/PWM/AMS3 (3rd function). Ideally, we'd like to use PG12 - PG14 for SPI0 while using PG15 with AMS3, but the hardware does not permit this. In the past, the software would allow the pins to be requested this way, but ultimately things like the Blackfin SPI driver would stop working when the hardware rerouted to a different peripheral. Signed-off-by: steven miao <realmz6@gmail.com> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
This commit is contained in:
parent
a71159b96a
commit
05bbec38db
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* GPIO Abstraction Layer
|
||||
*
|
||||
* Copyright 2006-2009 Analog Devices Inc.
|
||||
* Copyright 2006-2010 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later
|
||||
*/
|
||||
@ -215,82 +215,91 @@ static void port_setup(unsigned gpio, unsigned short usage)
|
||||
}
|
||||
|
||||
#ifdef BF537_FAMILY
|
||||
static struct {
|
||||
unsigned short res;
|
||||
unsigned short offset;
|
||||
} port_mux_lut[] = {
|
||||
{.res = P_PPI0_D13, .offset = 11},
|
||||
{.res = P_PPI0_D14, .offset = 11},
|
||||
{.res = P_PPI0_D15, .offset = 11},
|
||||
{.res = P_SPORT1_TFS, .offset = 11},
|
||||
{.res = P_SPORT1_TSCLK, .offset = 11},
|
||||
{.res = P_SPORT1_DTPRI, .offset = 11},
|
||||
{.res = P_PPI0_D10, .offset = 10},
|
||||
{.res = P_PPI0_D11, .offset = 10},
|
||||
{.res = P_PPI0_D12, .offset = 10},
|
||||
{.res = P_SPORT1_RSCLK, .offset = 10},
|
||||
{.res = P_SPORT1_RFS, .offset = 10},
|
||||
{.res = P_SPORT1_DRPRI, .offset = 10},
|
||||
{.res = P_PPI0_D8, .offset = 9},
|
||||
{.res = P_PPI0_D9, .offset = 9},
|
||||
{.res = P_SPORT1_DRSEC, .offset = 9},
|
||||
{.res = P_SPORT1_DTSEC, .offset = 9},
|
||||
{.res = P_TMR2, .offset = 8},
|
||||
{.res = P_PPI0_FS3, .offset = 8},
|
||||
{.res = P_TMR3, .offset = 7},
|
||||
{.res = P_SPI0_SSEL4, .offset = 7},
|
||||
{.res = P_TMR4, .offset = 6},
|
||||
{.res = P_SPI0_SSEL5, .offset = 6},
|
||||
{.res = P_TMR5, .offset = 5},
|
||||
{.res = P_SPI0_SSEL6, .offset = 5},
|
||||
{.res = P_UART1_RX, .offset = 4},
|
||||
{.res = P_UART1_TX, .offset = 4},
|
||||
{.res = P_TMR6, .offset = 4},
|
||||
{.res = P_TMR7, .offset = 4},
|
||||
{.res = P_UART0_RX, .offset = 3},
|
||||
{.res = P_UART0_TX, .offset = 3},
|
||||
{.res = P_DMAR0, .offset = 3},
|
||||
{.res = P_DMAR1, .offset = 3},
|
||||
{.res = P_SPORT0_DTSEC, .offset = 1},
|
||||
{.res = P_SPORT0_DRSEC, .offset = 1},
|
||||
{.res = P_CAN0_RX, .offset = 1},
|
||||
{.res = P_CAN0_TX, .offset = 1},
|
||||
{.res = P_SPI0_SSEL7, .offset = 1},
|
||||
{.res = P_SPORT0_TFS, .offset = 0},
|
||||
{.res = P_SPORT0_DTPRI, .offset = 0},
|
||||
{.res = P_SPI0_SSEL2, .offset = 0},
|
||||
{.res = P_SPI0_SSEL3, .offset = 0},
|
||||
static const s8 port_mux[] = {
|
||||
[GPIO_PF0] = 3,
|
||||
[GPIO_PF1] = 3,
|
||||
[GPIO_PF2] = 4,
|
||||
[GPIO_PF3] = 4,
|
||||
[GPIO_PF4] = 5,
|
||||
[GPIO_PF5] = 6,
|
||||
[GPIO_PF6] = 7,
|
||||
[GPIO_PF7] = 8,
|
||||
[GPIO_PF8 ... GPIO_PF15] = -1,
|
||||
[GPIO_PG0 ... GPIO_PG7] = -1,
|
||||
[GPIO_PG8] = 9,
|
||||
[GPIO_PG9] = 9,
|
||||
[GPIO_PG10] = 10,
|
||||
[GPIO_PG11] = 10,
|
||||
[GPIO_PG12] = 10,
|
||||
[GPIO_PG13] = 11,
|
||||
[GPIO_PG14] = 11,
|
||||
[GPIO_PG15] = 11,
|
||||
[GPIO_PH0 ... GPIO_PH15] = -1,
|
||||
[PORT_PJ0 ... PORT_PJ3] = -1,
|
||||
[PORT_PJ4] = 1,
|
||||
[PORT_PJ5] = 1,
|
||||
[PORT_PJ6 ... PORT_PJ9] = -1,
|
||||
[PORT_PJ10] = 0,
|
||||
[PORT_PJ11] = 0,
|
||||
};
|
||||
|
||||
static int portmux_group_check(unsigned short per)
|
||||
{
|
||||
u16 ident = P_IDENT(per);
|
||||
u16 function = P_FUNCT2MUX(per);
|
||||
s8 offset = port_mux[ident];
|
||||
u16 m, pmux, pfunc;
|
||||
|
||||
if (offset < 0)
|
||||
return 0;
|
||||
|
||||
pmux = bfin_read_PORT_MUX();
|
||||
for (m = 0; m < ARRAY_SIZE(port_mux); ++m) {
|
||||
if (m == ident)
|
||||
continue;
|
||||
if (port_mux[m] != offset)
|
||||
continue;
|
||||
if (!is_reserved(peri, m, 1))
|
||||
continue;
|
||||
|
||||
if (offset == 1)
|
||||
pfunc = (pmux >> offset) & 3;
|
||||
else
|
||||
pfunc = (pmux >> offset) & 1;
|
||||
if (pfunc != function) {
|
||||
pr_err("pin group conflict! request pin %d func %d conflict with pin %d func %d\n",
|
||||
ident, function, m, pfunc);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void portmux_setup(unsigned short per)
|
||||
{
|
||||
u16 y, offset, muxreg;
|
||||
u16 ident = P_IDENT(per);
|
||||
u16 function = P_FUNCT2MUX(per);
|
||||
s8 offset = port_mux[ident];
|
||||
u16 pmux;
|
||||
|
||||
for (y = 0; y < ARRAY_SIZE(port_mux_lut); y++) {
|
||||
if (port_mux_lut[y].res == per) {
|
||||
if (offset == -1)
|
||||
return;
|
||||
|
||||
/* SET PORTMUX REG */
|
||||
|
||||
offset = port_mux_lut[y].offset;
|
||||
muxreg = bfin_read_PORT_MUX();
|
||||
|
||||
if (offset != 1)
|
||||
muxreg &= ~(1 << offset);
|
||||
else
|
||||
muxreg &= ~(3 << 1);
|
||||
|
||||
muxreg |= (function << offset);
|
||||
bfin_write_PORT_MUX(muxreg);
|
||||
}
|
||||
}
|
||||
pmux = bfin_read_PORT_MUX();
|
||||
if (offset != 1)
|
||||
pmux &= ~(1 << offset);
|
||||
else
|
||||
pmux &= ~(3 << 1);
|
||||
pmux |= (function << offset);
|
||||
bfin_write_PORT_MUX(pmux);
|
||||
}
|
||||
#elif defined(CONFIG_BF54x)
|
||||
inline void portmux_setup(unsigned short per)
|
||||
{
|
||||
u32 pmux;
|
||||
u16 ident = P_IDENT(per);
|
||||
u16 function = P_FUNCT2MUX(per);
|
||||
u32 pmux;
|
||||
|
||||
pmux = gpio_array[gpio_bank(ident)]->port_mux;
|
||||
|
||||
@ -302,20 +311,54 @@ inline void portmux_setup(unsigned short per)
|
||||
|
||||
inline u16 get_portmux(unsigned short per)
|
||||
{
|
||||
u32 pmux;
|
||||
u16 ident = P_IDENT(per);
|
||||
|
||||
pmux = gpio_array[gpio_bank(ident)]->port_mux;
|
||||
|
||||
u32 pmux = gpio_array[gpio_bank(ident)]->port_mux;
|
||||
return (pmux >> (2 * gpio_sub_n(ident)) & 0x3);
|
||||
}
|
||||
static int portmux_group_check(unsigned short per)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#elif defined(CONFIG_BF52x) || defined(CONFIG_BF51x)
|
||||
static int portmux_group_check(unsigned short per)
|
||||
{
|
||||
u16 ident = P_IDENT(per);
|
||||
u16 function = P_FUNCT2MUX(per);
|
||||
u8 offset = pmux_offset[gpio_bank(ident)][gpio_sub_n(ident)];
|
||||
u16 pin, gpiopin, pfunc;
|
||||
|
||||
for (pin = 0; pin < GPIO_BANKSIZE; ++pin) {
|
||||
if (offset != pmux_offset[gpio_bank(ident)][pin])
|
||||
continue;
|
||||
|
||||
gpiopin = gpio_bank(ident) * GPIO_BANKSIZE + pin;
|
||||
if (gpiopin == ident)
|
||||
continue;
|
||||
if (!is_reserved(peri, gpiopin, 1))
|
||||
continue;
|
||||
|
||||
pfunc = *port_mux[gpio_bank(ident)];
|
||||
pfunc = (pfunc >> offset) & 3;
|
||||
if (pfunc != function) {
|
||||
pr_err("pin group conflict! request pin %d func %d conflict with pin %d func %d\n",
|
||||
ident, function, gpiopin, pfunc);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline void portmux_setup(unsigned short per)
|
||||
{
|
||||
u16 pmux, ident = P_IDENT(per), function = P_FUNCT2MUX(per);
|
||||
u16 ident = P_IDENT(per);
|
||||
u16 function = P_FUNCT2MUX(per);
|
||||
u8 offset = pmux_offset[gpio_bank(ident)][gpio_sub_n(ident)];
|
||||
u16 pmux;
|
||||
|
||||
pmux = *port_mux[gpio_bank(ident)];
|
||||
if (((pmux >> offset) & 3) == function)
|
||||
return;
|
||||
pmux &= ~(3 << offset);
|
||||
pmux |= (function & 3) << offset;
|
||||
*port_mux[gpio_bank(ident)] = pmux;
|
||||
@ -323,6 +366,10 @@ inline void portmux_setup(unsigned short per)
|
||||
}
|
||||
#else
|
||||
# define portmux_setup(...) do { } while (0)
|
||||
static int portmux_group_check(unsigned short per)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_BF54x
|
||||
@ -735,6 +782,10 @@ int peripheral_request(unsigned short per, const char *label)
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(portmux_group_check(per))) {
|
||||
hard_local_irq_restore(flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
anyway:
|
||||
reserve(peri, ident);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user