aboutsummaryrefslogtreecommitdiff
path: root/arch/avr32/mach-at32ap
diff options
context:
space:
mode:
Diffstat (limited to 'arch/avr32/mach-at32ap')
-rw-r--r--arch/avr32/mach-at32ap/Makefile2
-rw-r--r--arch/avr32/mach-at32ap/at32ap7000.c236
-rw-r--r--arch/avr32/mach-at32ap/extint.c34
-rw-r--r--arch/avr32/mach-at32ap/intc.c4
-rw-r--r--arch/avr32/mach-at32ap/pio.c322
-rw-r--r--arch/avr32/mach-at32ap/sm.c289
6 files changed, 490 insertions, 397 deletions
diff --git a/arch/avr32/mach-at32ap/Makefile b/arch/avr32/mach-at32ap/Makefile
index f62eb691551..b21bea9af8b 100644
--- a/arch/avr32/mach-at32ap/Makefile
+++ b/arch/avr32/mach-at32ap/Makefile
@@ -1,2 +1,2 @@
-obj-y += at32ap.o clock.o pio.o intc.o extint.o hsmc.o
+obj-y += at32ap.o clock.o intc.o extint.o pio.o hsmc.o
obj-$(CONFIG_CPU_AT32AP7000) += at32ap7000.o
diff --git a/arch/avr32/mach-at32ap/at32ap7000.c b/arch/avr32/mach-at32ap/at32ap7000.c
index 7ff6ad8bab5..c1e477ec757 100644
--- a/arch/avr32/mach-at32ap/at32ap7000.c
+++ b/arch/avr32/mach-at32ap/at32ap7000.c
@@ -11,6 +11,7 @@
#include <asm/io.h>
+#include <asm/arch/at32ap7000.h>
#include <asm/arch/board.h>
#include <asm/arch/portmux.h>
#include <asm/arch/sm.h>
@@ -57,6 +58,9 @@ static struct platform_device _name##_id##_device = { \
.num_resources = ARRAY_SIZE(_name##_id##_resource), \
}
+#define select_peripheral(pin, periph, flags) \
+ at32_select_periph(GPIO_PIN_##pin, GPIO_##periph, flags)
+
#define DEV_CLK(_name, devname, bus, _index) \
static struct clk devname##_##_name = { \
.name = #_name, \
@@ -67,18 +71,6 @@ static struct clk devname##_##_name = { \
.index = _index, \
}
-enum {
- PIOA,
- PIOB,
- PIOC,
- PIOD,
-};
-
-enum {
- FUNC_A,
- FUNC_B,
-};
-
unsigned long at32ap7000_osc_rates[3] = {
[0] = 32768,
/* FIXME: these are ATSTK1002-specific */
@@ -504,9 +496,16 @@ static struct resource pio3_resource[] = {
DEFINE_DEV(pio, 3);
DEV_CLK(mck, pio3, pba, 13);
+static struct resource pio4_resource[] = {
+ PBMEM(0xffe03800),
+ IRQ(17),
+};
+DEFINE_DEV(pio, 4);
+DEV_CLK(mck, pio4, pba, 14);
+
void __init at32_add_system_devices(void)
{
- system_manager.eim_first_irq = NR_INTERNAL_IRQS;
+ system_manager.eim_first_irq = EIM_IRQ_BASE;
platform_device_register(&at32_sm_device);
platform_device_register(&at32_intc0_device);
@@ -517,6 +516,7 @@ void __init at32_add_system_devices(void)
platform_device_register(&pio1_device);
platform_device_register(&pio2_device);
platform_device_register(&pio3_device);
+ platform_device_register(&pio4_device);
}
/* --------------------------------------------------------------------
@@ -529,7 +529,7 @@ static struct atmel_uart_data atmel_usart0_data = {
};
static struct resource atmel_usart0_resource[] = {
PBMEM(0xffe00c00),
- IRQ(7),
+ IRQ(6),
};
DEFINE_DEV_DATA(atmel_usart, 0);
DEV_CLK(usart, atmel_usart0, pba, 4);
@@ -569,29 +569,29 @@ DEV_CLK(usart, atmel_usart3, pba, 6);
static inline void configure_usart0_pins(void)
{
- portmux_set_func(PIOA, 8, FUNC_B); /* RXD */
- portmux_set_func(PIOA, 9, FUNC_B); /* TXD */
+ select_peripheral(PA(8), PERIPH_B, 0); /* RXD */
+ select_peripheral(PA(9), PERIPH_B, 0); /* TXD */
}
static inline void configure_usart1_pins(void)
{
- portmux_set_func(PIOA, 17, FUNC_A); /* RXD */
- portmux_set_func(PIOA, 18, FUNC_A); /* TXD */
+ select_peripheral(PA(17), PERIPH_A, 0); /* RXD */
+ select_peripheral(PA(18), PERIPH_A, 0); /* TXD */
}
static inline void configure_usart2_pins(void)
{
- portmux_set_func(PIOB, 26, FUNC_B); /* RXD */
- portmux_set_func(PIOB, 27, FUNC_B); /* TXD */
+ select_peripheral(PB(26), PERIPH_B, 0); /* RXD */
+ select_peripheral(PB(27), PERIPH_B, 0); /* TXD */
}
static inline void configure_usart3_pins(void)
{
- portmux_set_func(PIOB, 18, FUNC_B); /* RXD */
- portmux_set_func(PIOB, 17, FUNC_B); /* TXD */
+ select_peripheral(PB(18), PERIPH_B, 0); /* RXD */
+ select_peripheral(PB(17), PERIPH_B, 0); /* TXD */
}
-static struct platform_device *at32_usarts[4];
+static struct platform_device *__initdata at32_usarts[4];
void __init at32_map_usart(unsigned int hw_id, unsigned int line)
{
@@ -654,6 +654,15 @@ DEFINE_DEV_DATA(macb, 0);
DEV_CLK(hclk, macb0, hsb, 8);
DEV_CLK(pclk, macb0, pbb, 6);
+static struct eth_platform_data macb1_data;
+static struct resource macb1_resource[] = {
+ PBMEM(0xfff01c00),
+ IRQ(26),
+};
+DEFINE_DEV_DATA(macb, 1);
+DEV_CLK(hclk, macb1, hsb, 9);
+DEV_CLK(pclk, macb1, pbb, 7);
+
struct platform_device *__init
at32_add_device_eth(unsigned int id, struct eth_platform_data *data)
{
@@ -663,27 +672,54 @@ at32_add_device_eth(unsigned int id, struct eth_platform_data *data)
case 0:
pdev = &macb0_device;
- portmux_set_func(PIOC, 3, FUNC_A); /* TXD0 */
- portmux_set_func(PIOC, 4, FUNC_A); /* TXD1 */
- portmux_set_func(PIOC, 7, FUNC_A); /* TXEN */
- portmux_set_func(PIOC, 8, FUNC_A); /* TXCK */
- portmux_set_func(PIOC, 9, FUNC_A); /* RXD0 */
- portmux_set_func(PIOC, 10, FUNC_A); /* RXD1 */
- portmux_set_func(PIOC, 13, FUNC_A); /* RXER */
- portmux_set_func(PIOC, 15, FUNC_A); /* RXDV */
- portmux_set_func(PIOC, 16, FUNC_A); /* MDC */
- portmux_set_func(PIOC, 17, FUNC_A); /* MDIO */
+ select_peripheral(PC(3), PERIPH_A, 0); /* TXD0 */
+ select_peripheral(PC(4), PERIPH_A, 0); /* TXD1 */
+ select_peripheral(PC(7), PERIPH_A, 0); /* TXEN */
+ select_peripheral(PC(8), PERIPH_A, 0); /* TXCK */
+ select_peripheral(PC(9), PERIPH_A, 0); /* RXD0 */
+ select_peripheral(PC(10), PERIPH_A, 0); /* RXD1 */
+ select_peripheral(PC(13), PERIPH_A, 0); /* RXER */
+ select_peripheral(PC(15), PERIPH_A, 0); /* RXDV */
+ select_peripheral(PC(16), PERIPH_A, 0); /* MDC */
+ select_peripheral(PC(17), PERIPH_A, 0); /* MDIO */
if (!data->is_rmii) {
- portmux_set_func(PIOC, 0, FUNC_A); /* COL */
- portmux_set_func(PIOC, 1, FUNC_A); /* CRS */
- portmux_set_func(PIOC, 2, FUNC_A); /* TXER */
- portmux_set_func(PIOC, 5, FUNC_A); /* TXD2 */
- portmux_set_func(PIOC, 6, FUNC_A); /* TXD3 */
- portmux_set_func(PIOC, 11, FUNC_A); /* RXD2 */
- portmux_set_func(PIOC, 12, FUNC_A); /* RXD3 */
- portmux_set_func(PIOC, 14, FUNC_A); /* RXCK */
- portmux_set_func(PIOC, 18, FUNC_A); /* SPD */
+ select_peripheral(PC(0), PERIPH_A, 0); /* COL */
+ select_peripheral(PC(1), PERIPH_A, 0); /* CRS */
+ select_peripheral(PC(2), PERIPH_A, 0); /* TXER */
+ select_peripheral(PC(5), PERIPH_A, 0); /* TXD2 */
+ select_peripheral(PC(6), PERIPH_A, 0); /* TXD3 */
+ select_peripheral(PC(11), PERIPH_A, 0); /* RXD2 */
+ select_peripheral(PC(12), PERIPH_A, 0); /* RXD3 */
+ select_peripheral(PC(14), PERIPH_A, 0); /* RXCK */
+ select_peripheral(PC(18), PERIPH_A, 0); /* SPD */
+ }
+ break;
+
+ case 1:
+ pdev = &macb1_device;
+
+ select_peripheral(PD(13), PERIPH_B, 0); /* TXD0 */
+ select_peripheral(PD(14), PERIPH_B, 0); /* TXD1 */
+ select_peripheral(PD(11), PERIPH_B, 0); /* TXEN */
+ select_peripheral(PD(12), PERIPH_B, 0); /* TXCK */
+ select_peripheral(PD(10), PERIPH_B, 0); /* RXD0 */
+ select_peripheral(PD(6), PERIPH_B, 0); /* RXD1 */
+ select_peripheral(PD(5), PERIPH_B, 0); /* RXER */
+ select_peripheral(PD(4), PERIPH_B, 0); /* RXDV */
+ select_peripheral(PD(3), PERIPH_B, 0); /* MDC */
+ select_peripheral(PD(2), PERIPH_B, 0); /* MDIO */
+
+ if (!data->is_rmii) {
+ select_peripheral(PC(19), PERIPH_B, 0); /* COL */
+ select_peripheral(PC(23), PERIPH_B, 0); /* CRS */
+ select_peripheral(PC(26), PERIPH_B, 0); /* TXER */
+ select_peripheral(PC(27), PERIPH_B, 0); /* TXD2 */
+ select_peripheral(PC(28), PERIPH_B, 0); /* TXD3 */
+ select_peripheral(PC(29), PERIPH_B, 0); /* RXD2 */
+ select_peripheral(PC(30), PERIPH_B, 0); /* RXD3 */
+ select_peripheral(PC(24), PERIPH_B, 0); /* RXCK */
+ select_peripheral(PD(15), PERIPH_B, 0); /* SPD */
}
break;
@@ -700,12 +736,19 @@ at32_add_device_eth(unsigned int id, struct eth_platform_data *data)
/* --------------------------------------------------------------------
* SPI
* -------------------------------------------------------------------- */
-static struct resource spi0_resource[] = {
+static struct resource atmel_spi0_resource[] = {
PBMEM(0xffe00000),
IRQ(3),
};
-DEFINE_DEV(spi, 0);
-DEV_CLK(mck, spi0, pba, 0);
+DEFINE_DEV(atmel_spi, 0);
+DEV_CLK(spi_clk, atmel_spi0, pba, 0);
+
+static struct resource atmel_spi1_resource[] = {
+ PBMEM(0xffe00400),
+ IRQ(4),
+};
+DEFINE_DEV(atmel_spi, 1);
+DEV_CLK(spi_clk, atmel_spi1, pba, 1);
struct platform_device *__init at32_add_device_spi(unsigned int id)
{
@@ -713,13 +756,33 @@ struct platform_device *__init at32_add_device_spi(unsigned int id)
switch (id) {
case 0:
- pdev = &spi0_device;
- portmux_set_func(PIOA, 0, FUNC_A); /* MISO */
- portmux_set_func(PIOA, 1, FUNC_A); /* MOSI */
- portmux_set_func(PIOA, 2, FUNC_A); /* SCK */
- portmux_set_func(PIOA, 3, FUNC_A); /* NPCS0 */
- portmux_set_func(PIOA, 4, FUNC_A); /* NPCS1 */
- portmux_set_func(PIOA, 5, FUNC_A); /* NPCS2 */
+ pdev = &atmel_spi0_device;
+ select_peripheral(PA(0), PERIPH_A, 0); /* MISO */
+ select_peripheral(PA(1), PERIPH_A, 0); /* MOSI */
+ select_peripheral(PA(2), PERIPH_A, 0); /* SCK */
+
+ /* NPCS[2:0] */
+ at32_select_gpio(GPIO_PIN_PA(3),
+ AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH);
+ at32_select_gpio(GPIO_PIN_PA(4),
+ AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH);
+ at32_select_gpio(GPIO_PIN_PA(5),
+ AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH);
+ break;
+
+ case 1:
+ pdev = &atmel_spi1_device;
+ select_peripheral(PB(0), PERIPH_B, 0); /* MISO */
+ select_peripheral(PB(1), PERIPH_B, 0); /* MOSI */
+ select_peripheral(PB(5), PERIPH_B, 0); /* SCK */
+
+ /* NPCS[2:0] */
+ at32_select_gpio(GPIO_PIN_PB(2),
+ AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH);
+ at32_select_gpio(GPIO_PIN_PB(3),
+ AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH);
+ at32_select_gpio(GPIO_PIN_PB(4),
+ AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH);
break;
default:
@@ -762,37 +825,37 @@ at32_add_device_lcdc(unsigned int id, struct lcdc_platform_data *data)
switch (id) {
case 0:
pdev = &lcdc0_device;
- portmux_set_func(PIOC, 19, FUNC_A); /* CC */
- portmux_set_func(PIOC, 20, FUNC_A); /* HSYNC */
- portmux_set_func(PIOC, 21, FUNC_A); /* PCLK */
- portmux_set_func(PIOC, 22, FUNC_A); /* VSYNC */
- portmux_set_func(PIOC, 23, FUNC_A); /* DVAL */
- portmux_set_func(PIOC, 24, FUNC_A); /* MODE */
- portmux_set_func(PIOC, 25, FUNC_A); /* PWR */
- portmux_set_func(PIOC, 26, FUNC_A); /* DATA0 */
- portmux_set_func(PIOC, 27, FUNC_A); /* DATA1 */
- portmux_set_func(PIOC, 28, FUNC_A); /* DATA2 */
- portmux_set_func(PIOC, 29, FUNC_A); /* DATA3 */
- portmux_set_func(PIOC, 30, FUNC_A); /* DATA4 */
- portmux_set_func(PIOC, 31, FUNC_A); /* DATA5 */
- portmux_set_func(PIOD, 0, FUNC_A); /* DATA6 */
- portmux_set_func(PIOD, 1, FUNC_A); /* DATA7 */
- portmux_set_func(PIOD, 2, FUNC_A); /* DATA8 */
- portmux_set_func(PIOD, 3, FUNC_A); /* DATA9 */
- portmux_set_func(PIOD, 4, FUNC_A); /* DATA10 */
- portmux_set_func(PIOD, 5, FUNC_A); /* DATA11 */
- portmux_set_func(PIOD, 6, FUNC_A); /* DATA12 */
- portmux_set_func(PIOD, 7, FUNC_A); /* DATA13 */
- portmux_set_func(PIOD, 8, FUNC_A); /* DATA14 */
- portmux_set_func(PIOD, 9, FUNC_A); /* DATA15 */
- portmux_set_func(PIOD, 10, FUNC_A); /* DATA16 */
- portmux_set_func(PIOD, 11, FUNC_A); /* DATA17 */
- portmux_set_func(PIOD, 12, FUNC_A); /* DATA18 */
- portmux_set_func(PIOD, 13, FUNC_A); /* DATA19 */
- portmux_set_func(PIOD, 14, FUNC_A); /* DATA20 */
- portmux_set_func(PIOD, 15, FUNC_A); /* DATA21 */
- portmux_set_func(PIOD, 16, FUNC_A); /* DATA22 */
- portmux_set_func(PIOD, 17, FUNC_A); /* DATA23 */
+ select_peripheral(PC(19), PERIPH_A, 0); /* CC */
+ select_peripheral(PC(20), PERIPH_A, 0); /* HSYNC */
+ select_peripheral(PC(21), PERIPH_A, 0); /* PCLK */
+ select_peripheral(PC(22), PERIPH_A, 0); /* VSYNC */
+ select_peripheral(PC(23), PERIPH_A, 0); /* DVAL */
+ select_peripheral(PC(24), PERIPH_A, 0); /* MODE */
+ select_peripheral(PC(25), PERIPH_A, 0); /* PWR */
+ select_peripheral(PC(26), PERIPH_A, 0); /* DATA0 */
+ select_peripheral(PC(27), PERIPH_A, 0); /* DATA1 */
+ select_peripheral(PC(28), PERIPH_A, 0); /* DATA2 */
+ select_peripheral(PC(29), PERIPH_A, 0); /* DATA3 */
+ select_peripheral(PC(30), PERIPH_A, 0); /* DATA4 */
+ select_peripheral(PC(31), PERIPH_A, 0); /* DATA5 */
+ select_peripheral(PD(0), PERIPH_A, 0); /* DATA6 */
+ select_peripheral(PD(1), PERIPH_A, 0); /* DATA7 */
+ select_peripheral(PD(2), PERIPH_A, 0); /* DATA8 */
+ select_peripheral(PD(3), PERIPH_A, 0); /* DATA9 */
+ select_peripheral(PD(4), PERIPH_A, 0); /* DATA10 */
+ select_peripheral(PD(5), PERIPH_A, 0); /* DATA11 */
+ select_peripheral(PD(6), PERIPH_A, 0); /* DATA12 */
+ select_peripheral(PD(7), PERIPH_A, 0); /* DATA13 */
+ select_peripheral(PD(8), PERIPH_A, 0); /* DATA14 */
+ select_peripheral(PD(9), PERIPH_A, 0); /* DATA15 */
+ select_peripheral(PD(10), PERIPH_A, 0); /* DATA16 */
+ select_peripheral(PD(11), PERIPH_A, 0); /* DATA17 */
+ select_peripheral(PD(12), PERIPH_A, 0); /* DATA18 */
+ select_peripheral(PD(13), PERIPH_A, 0); /* DATA19 */
+ select_peripheral(PD(14), PERIPH_A, 0); /* DATA20 */
+ select_peripheral(PD(15), PERIPH_A, 0); /* DATA21 */
+ select_peripheral(PD(16), PERIPH_A, 0); /* DATA22 */
+ select_peripheral(PD(17), PERIPH_A, 0); /* DATA23 */
clk_set_parent(&lcdc0_pixclk, &pll0);
clk_set_rate(&lcdc0_pixclk, clk_get_rate(&pll0));
@@ -832,13 +895,17 @@ struct clk *at32_clock_list[] = {
&pio1_mck,
&pio2_mck,
&pio3_mck,
+ &pio4_mck,
&atmel_usart0_usart,
&atmel_usart1_usart,
&atmel_usart2_usart,
&atmel_usart3_usart,
&macb0_hclk,
&macb0_pclk,
- &spi0_mck,
+ &macb1_hclk,
+ &macb1_pclk,
+ &atmel_spi0_spi_clk,
+ &atmel_spi1_spi_clk,
&lcdc0_hclk,
&lcdc0_pixclk,
};
@@ -850,6 +917,7 @@ void __init at32_portmux_init(void)
at32_init_pio(&pio1_device);
at32_init_pio(&pio2_device);
at32_init_pio(&pio3_device);
+ at32_init_pio(&pio4_device);
}
void __init at32_clock_init(void)
diff --git a/arch/avr32/mach-at32ap/extint.c b/arch/avr32/mach-at32ap/extint.c
index 4dff1f98890..4a60eccfebd 100644
--- a/arch/avr32/mach-at32ap/extint.c
+++ b/arch/avr32/mach-at32ap/extint.c
@@ -49,13 +49,17 @@ static void eim_unmask_irq(unsigned int irq)
static int eim_set_irq_type(unsigned int irq, unsigned int flow_type)
{
struct at32_sm *sm = get_irq_chip_data(irq);
+ struct irq_desc *desc;
unsigned int i = irq - sm->eim_first_irq;
u32 mode, edge, level;
unsigned long flags;
int ret = 0;
flow_type &= IRQ_TYPE_SENSE_MASK;
+ if (flow_type == IRQ_TYPE_NONE)
+ flow_type = IRQ_TYPE_LEVEL_LOW;
+ desc = &irq_desc[irq];
spin_lock_irqsave(&sm->lock, flags);
mode = sm_readl(sm, EIM_MODE);
@@ -84,9 +88,16 @@ static int eim_set_irq_type(unsigned int irq, unsigned int flow_type)
break;
}
- sm_writel(sm, EIM_MODE, mode);
- sm_writel(sm, EIM_EDGE, edge);
- sm_writel(sm, EIM_LEVEL, level);
+ if (ret == 0) {
+ sm_writel(sm, EIM_MODE, mode);
+ sm_writel(sm, EIM_EDGE, edge);
+ sm_writel(sm, EIM_LEVEL, level);
+
+ if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
+ flow_type |= IRQ_LEVEL;
+ desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
+ desc->status |= flow_type;
+ }
spin_unlock_irqrestore(&sm->lock, flags);
@@ -109,8 +120,6 @@ static void demux_eim_irq(unsigned int irq, struct irq_desc *desc)
unsigned long status, pending;
unsigned int i, ext_irq;
- spin_lock(&sm->lock);
-
status = sm_readl(sm, EIM_ISR);
pending = status & sm_readl(sm, EIM_IMR);
@@ -120,10 +129,11 @@ static void demux_eim_irq(unsigned int irq, struct irq_desc *desc)
ext_irq = i + sm->eim_first_irq;
ext_desc = irq_desc + ext_irq;
- ext_desc->handle_irq(ext_irq, ext_desc);
+ if (ext_desc->status & IRQ_LEVEL)
+ handle_level_irq(ext_irq, ext_desc);
+ else
+ handle_edge_irq(ext_irq, ext_desc);
}
-
- spin_unlock(&sm->lock);
}
static int __init eim_init(void)
@@ -148,10 +158,16 @@ static int __init eim_init(void)
pattern = sm_readl(sm, EIM_MODE);
nr_irqs = fls(pattern);
+ /* Trigger on falling edge unless overridden by driver */
+ sm_writel(sm, EIM_MODE, 0UL);
+ sm_writel(sm, EIM_EDGE, 0UL);
+
sm->eim_chip = &eim_chip;
for (i = 0; i < nr_irqs; i++) {
- set_irq_chip(sm->eim_first_irq + i, &eim_chip);
+ /* NOTE the handler we set here is ignored by the demux */
+ set_irq_chip_and_handler(sm->eim_first_irq + i, &eim_chip,
+ handle_level_irq);
set_irq_chip_data(sm->eim_first_irq + i, sm);
}
diff --git a/arch/avr32/mach-at32ap/intc.c b/arch/avr32/mach-at32ap/intc.c
index eb87a18ad7b..dd5c009cf22 100644
--- a/arch/avr32/mach-at32ap/intc.c
+++ b/arch/avr32/mach-at32ap/intc.c
@@ -136,3 +136,7 @@ fail:
panic("Interrupt controller initialization failed!\n");
}
+unsigned long intc_get_pending(int group)
+{
+ return intc_readl(&intc0, INTREQ0 + 4 * group);
+}
diff --git a/arch/avr32/mach-at32ap/pio.c b/arch/avr32/mach-at32ap/pio.c
index d3aabfca859..9ba5654cde1 100644
--- a/arch/avr32/mach-at32ap/pio.c
+++ b/arch/avr32/mach-at32ap/pio.c
@@ -12,7 +12,9 @@
#include <linux/debugfs.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/arch/portmux.h>
@@ -25,43 +27,328 @@ struct pio_device {
void __iomem *regs;
const struct platform_device *pdev;
struct clk *clk;
- u32 alloc_mask;
- char name[32];
+ u32 pinmux_mask;
+ u32 gpio_mask;
+ char name[8];
};
static struct pio_device pio_dev[MAX_NR_PIO_DEVICES];
-void portmux_set_func(unsigned int portmux_id, unsigned int pin_id,
- unsigned int function_id)
+static struct pio_device *gpio_to_pio(unsigned int gpio)
{
struct pio_device *pio;
- u32 mask = 1 << pin_id;
+ unsigned int index;
- BUG_ON(portmux_id >= MAX_NR_PIO_DEVICES);
+ index = gpio >> 5;
+ if (index >= MAX_NR_PIO_DEVICES)
+ return NULL;
+ pio = &pio_dev[index];
+ if (!pio->regs)
+ return NULL;
- pio = &pio_dev[portmux_id];
+ return pio;
+}
+
+/* Pin multiplexing API */
+
+void __init at32_select_periph(unsigned int pin, unsigned int periph,
+ unsigned long flags)
+{
+ struct pio_device *pio;
+ unsigned int pin_index = pin & 0x1f;
+ u32 mask = 1 << pin_index;
+
+ pio = gpio_to_pio(pin);
+ if (unlikely(!pio)) {
+ printk("pio: invalid pin %u\n", pin);
+ goto fail;
+ }
- if (function_id)
+ if (unlikely(test_and_set_bit(pin_index, &pio->pinmux_mask))) {
+ printk("%s: pin %u is busy\n", pio->name, pin_index);
+ goto fail;
+ }
+
+ pio_writel(pio, PUER, mask);
+ if (periph)
pio_writel(pio, BSR, mask);
else
pio_writel(pio, ASR, mask);
+
pio_writel(pio, PDR, mask);
+ if (!(flags & AT32_GPIOF_PULLUP))
+ pio_writel(pio, PUDR, mask);
+
+ /* gpio_request NOT allowed */
+ set_bit(pin_index, &pio->gpio_mask);
+
+ return;
+
+fail:
+ dump_stack();
+}
+
+void __init at32_select_gpio(unsigned int pin, unsigned long flags)
+{
+ struct pio_device *pio;
+ unsigned int pin_index = pin & 0x1f;
+ u32 mask = 1 << pin_index;
+
+ pio = gpio_to_pio(pin);
+ if (unlikely(!pio)) {
+ printk("pio: invalid pin %u\n", pin);
+ goto fail;
+ }
+
+ if (unlikely(test_and_set_bit(pin_index, &pio->pinmux_mask))) {
+ printk("%s: pin %u is busy\n", pio->name, pin_index);
+ goto fail;
+ }
+
+ if (flags & AT32_GPIOF_OUTPUT) {
+ if (flags & AT32_GPIOF_HIGH)
+ pio_writel(pio, SODR, mask);
+ else
+ pio_writel(pio, CODR, mask);
+ pio_writel(pio, PUDR, mask);
+ pio_writel(pio, OER, mask);
+ } else {
+ if (flags & AT32_GPIOF_PULLUP)
+ pio_writel(pio, PUER, mask);
+ else
+ pio_writel(pio, PUDR, mask);
+ if (flags & AT32_GPIOF_DEGLITCH)
+ pio_writel(pio, IFER, mask);
+ else
+ pio_writel(pio, IFDR, mask);
+ pio_writel(pio, ODR, mask);
+ }
+
+ pio_writel(pio, PER, mask);
+
+ /* gpio_request now allowed */
+ clear_bit(pin_index, &pio->gpio_mask);
+
+ return;
+
+fail:
+ dump_stack();
+}
+
+/* Reserve a pin, preventing anyone else from changing its configuration. */
+void __init at32_reserve_pin(unsigned int pin)
+{
+ struct pio_device *pio;
+ unsigned int pin_index = pin & 0x1f;
+
+ pio = gpio_to_pio(pin);
+ if (unlikely(!pio)) {
+ printk("pio: invalid pin %u\n", pin);
+ goto fail;
+ }
+
+ if (unlikely(test_and_set_bit(pin_index, &pio->pinmux_mask))) {
+ printk("%s: pin %u is busy\n", pio->name, pin_index);
+ goto fail;
+ }
+
+ return;
+
+fail:
+ dump_stack();
}
+/*--------------------------------------------------------------------------*/
+
+/* GPIO API */
+
+int gpio_request(unsigned int gpio, const char *label)
+{
+ struct pio_device *pio;
+ unsigned int pin;
+
+ pio = gpio_to_pio(gpio);
+ if (!pio)
+ return -ENODEV;
+
+ pin = gpio & 0x1f;
+ if (test_and_set_bit(pin, &pio->gpio_mask))
+ return -EBUSY;
+
+ return 0;
+}
+EXPORT_SYMBOL(gpio_request);
+
+void gpio_free(unsigned int gpio)
+{
+ struct pio_device *pio;
+ unsigned int pin;
+
+ pio = gpio_to_pio(gpio);
+ if (!pio) {
+ printk(KERN_ERR
+ "gpio: attempted to free invalid pin %u\n", gpio);
+ return;
+ }
+
+ pin = gpio & 0x1f;
+ if (!test_and_clear_bit(pin, &pio->gpio_mask))
+ printk(KERN_ERR "gpio: freeing free or non-gpio pin %s-%u\n",
+ pio->name, pin);
+}
+EXPORT_SYMBOL(gpio_free);
+
+int gpio_direction_input(unsigned int gpio)
+{
+ struct pio_device *pio;
+ unsigned int pin;
+
+ pio = gpio_to_pio(gpio);
+ if (!pio)
+ return -ENODEV;
+
+ pin = gpio & 0x1f;
+ pio_writel(pio, ODR, 1 << pin);
+
+ return 0;
+}
+EXPORT_SYMBOL(gpio_direction_input);
+
+int gpio_direction_output(unsigned int gpio)
+{
+ struct pio_device *pio;
+ unsigned int pin;
+
+ pio = gpio_to_pio(gpio);
+ if (!pio)
+ return -ENODEV;
+
+ pin = gpio & 0x1f;
+ pio_writel(pio, OER, 1 << pin);
+
+ return 0;
+}
+EXPORT_SYMBOL(gpio_direction_output);
+
+int gpio_get_value(unsigned int gpio)
+{
+ struct pio_device *pio = &pio_dev[gpio >> 5];
+
+ return (pio_readl(pio, PDSR) >> (gpio & 0x1f)) & 1;
+}
+EXPORT_SYMBOL(gpio_get_value);
+
+void gpio_set_value(unsigned int gpio, int value)
+{
+ struct pio_device *pio = &pio_dev[gpio >> 5];
+ u32 mask;
+
+ mask = 1 << (gpio & 0x1f);
+ if (value)
+ pio_writel(pio, SODR, mask);
+ else
+ pio_writel(pio, CODR, mask);
+}
+EXPORT_SYMBOL(gpio_set_value);
+
+/*--------------------------------------------------------------------------*/
+
+/* GPIO IRQ support */
+
+static void gpio_irq_mask(unsigned irq)
+{
+ unsigned gpio = irq_to_gpio(irq);
+ struct pio_device *pio = &pio_dev[gpio >> 5];
+
+ pio_writel(pio, IDR, 1 << (gpio & 0x1f));
+}
+
+static void gpio_irq_unmask(unsigned irq)
+{
+ unsigned gpio = irq_to_gpio(irq);
+ struct pio_device *pio = &pio_dev[gpio >> 5];
+
+ pio_writel(pio, IER, 1 << (gpio & 0x1f));
+}
+
+static int gpio_irq_type(unsigned irq, unsigned type)
+{
+ if (type != IRQ_TYPE_EDGE_BOTH && type != IRQ_TYPE_NONE)
+ return -EINVAL;
+
+ return 0;
+}
+
+static struct irq_chip gpio_irqchip = {
+ .name = "gpio",
+ .mask = gpio_irq_mask,
+ .unmask = gpio_irq_unmask,
+ .set_type = gpio_irq_type,
+};
+
+static void gpio_irq_handler(unsigned irq, struct irq_desc *desc)
+{
+ struct pio_device *pio = get_irq_chip_data(irq);
+ unsigned gpio_irq;
+
+ gpio_irq = (unsigned) get_irq_data(irq);
+ for (;;) {
+ u32 isr;
+ struct irq_desc *d;
+
+ /* ack pending GPIO interrupts */
+ isr = pio_readl(pio, ISR) & pio_readl(pio, IMR);
+ if (!isr)
+ break;
+ do {
+ int i;
+
+ i = ffs(isr) - 1;
+ isr &= ~(1 << i);
+
+ i += gpio_irq;
+ d = &irq_desc[i];
+
+ d->handle_irq(i, d);
+ } while (isr);
+ }
+}
+
+static void __init
+gpio_irq_setup(struct pio_device *pio, int irq, int gpio_irq)
+{
+ unsigned i;
+
+ set_irq_chip_data(irq, pio);
+ set_irq_data(irq, (void *) gpio_irq);
+
+ for (i = 0; i < 32; i++, gpio_irq++) {
+ set_irq_chip_data(gpio_irq, pio);
+ set_irq_chip_and_handler(gpio_irq, &gpio_irqchip,
+ handle_simple_irq);
+ }
+
+ set_irq_chained_handler(irq, gpio_irq_handler);
+}
+
+/*--------------------------------------------------------------------------*/
+
static int __init pio_probe(struct platform_device *pdev)
{
struct pio_device *pio = NULL;
+ int irq = platform_get_irq(pdev, 0);
+ int gpio_irq_base = GPIO_IRQ_BASE + pdev->id * 32;
BUG_ON(pdev->id >= MAX_NR_PIO_DEVICES);
pio = &pio_dev[pdev->id];
BUG_ON(!pio->regs);
- /* TODO: Interrupts */
+ gpio_irq_setup(pio, irq, gpio_irq_base);
platform_set_drvdata(pdev, pio);
- printk(KERN_INFO "%s: Atmel Port Multiplexer at 0x%p (irq %d)\n",
- pio->name, pio->regs, platform_get_irq(pdev, 0));
+ printk(KERN_DEBUG "%s: base 0x%p, irq %d chains %d..%d\n",
+ pio->name, pio->regs, irq, gpio_irq_base, gpio_irq_base + 31);
return 0;
}
@@ -77,7 +364,7 @@ static int __init pio_init(void)
{
return platform_driver_register(&pio_driver);
}
-subsys_initcall(pio_init);
+postcore_initcall(pio_init);
void __init at32_init_pio(struct platform_device *pdev)
{
@@ -113,6 +400,13 @@ void __init at32_init_pio(struct platform_device *pdev)
pio->pdev = pdev;
pio->regs = ioremap(regs->start, regs->end - regs->start + 1);
- pio_writel(pio, ODR, ~0UL);
- pio_writel(pio, PER, ~0UL);
+ /*
+ * request_gpio() is only valid for pins that have been
+ * explicitly configured as GPIO and not previously requested
+ */
+ pio->gpio_mask = ~0UL;
+
+ /* start with irqs disabled and acked */
+ pio_writel(pio, IDR, ~0UL);
+ (void) pio_readl(pio, ISR);
}
diff --git a/arch/avr32/mach-at32ap/sm.c b/arch/avr32/mach-at32ap/sm.c
deleted file mode 100644
index 03306eb0345..00000000000
--- a/arch/avr32/mach-at32ap/sm.c
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * System Manager driver for AT32AP CPUs
- *
- * Copyright (C) 2006 Atmel Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/random.h>
-#include <linux/spinlock.h>
-
-#include <asm/intc.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#include <asm/arch/sm.h>
-
-#include "sm.h"
-
-#define SM_EIM_IRQ_RESOURCE 1
-#define SM_PM_IRQ_RESOURCE 2
-#define SM_RTC_IRQ_RESOURCE 3
-
-#define to_eim(irqc) container_of(irqc, struct at32_sm, irqc)
-
-struct at32_sm system_manager;
-
-int __init at32_sm_init(void)
-{
- struct resource *regs;
- struct at32_sm *sm = &system_manager;
- int ret = -ENXIO;
-
- regs = platform_get_resource(&at32_sm_device, IORESOURCE_MEM, 0);
- if (!regs)
- goto fail;
-
- spin_lock_init(&sm->lock);
- sm->pdev = &at32_sm_device;
-
- ret = -ENOMEM;
- sm->regs = ioremap(regs->start, regs->end - regs->start + 1);
- if (!sm->regs)
- goto fail;
-
- return 0;
-
-fail:
- printk(KERN_ERR "Failed to initialize System Manager: %d\n", ret);
- return ret;
-}
-
-/*
- * External Interrupt Module (EIM).
- *
- * EIM gets level- or edge-triggered interrupts of either polarity
- * from the outside and converts it to active-high level-triggered
- * interrupts that the internal interrupt controller can handle. EIM
- * also provides masking/unmasking of interrupts, as well as
- * acknowledging of edge-triggered interrupts.
- */
-
-static irqreturn_t spurious_eim_interrupt(int irq, void *dev_id,
- struct pt_regs *regs)
-{
- printk(KERN_WARNING "Spurious EIM interrupt %d\n", irq);
- disable_irq(irq);
- return IRQ_NONE;
-}
-
-static struct irqaction eim_spurious_action = {
- .handler = spurious_eim_interrupt,
-};
-
-static irqreturn_t eim_handle_irq(int irq, void *dev_id, struct pt_regs *regs)
-{
- struct irq_controller * irqc = dev_id;
- struct at32_sm *sm = to_eim(irqc);
- unsigned long pending;
-
- /*
- * No need to disable interrupts globally. The interrupt
- * level relevant to this group must be masked all the time,
- * so we know that this particular EIM instance will not be
- * re-entered.
- */
- spin_lock(&sm->lock);
-
- pending = intc_get_pending(sm->irqc.irq_group);
- if (unlikely(!pending)) {
- printk(KERN_ERR "EIM (group %u): No interrupts pending!\n",
- sm->irqc.irq_group);
- goto unlock;
- }
-
- do {
- struct irqaction *action;
- unsigned int i;
-
- i = fls(pending) - 1;
- pending &= ~(1 << i);
- action = sm->action[i];
-
- /* Acknowledge the interrupt */
- sm_writel(sm, EIM_ICR, 1 << i);
-
- spin_unlock(&sm->lock);
-
- if (action->flags & SA_INTERRUPT)
- local_irq_disable();
- action->handler(sm->irqc.first_irq + i, action->dev_id, regs);
- local_irq_enable();
- spin_lock(&sm->lock);
- if (action->flags & SA_SAMPLE_RANDOM)
- add_interrupt_randomness(sm->irqc.first_irq + i);
- } while (pending);
-
-unlock:
- spin_unlock(&sm->lock);
- return IRQ_HANDLED;
-}
-
-static void eim_mask(struct irq_controller *irqc, unsigned int irq)
-{
- struct at32_sm *sm = to_eim(irqc);
- unsigned int i;
-
- i = irq - sm->irqc.first_irq;
- sm_writel(sm, EIM_IDR, 1 << i);
-}
-
-static void eim_unmask(struct irq_controller *irqc, unsigned int irq)
-{
- struct at32_sm *sm = to_eim(irqc);
- unsigned int i;
-
- i = irq - sm->irqc.first_irq;
- sm_writel(sm, EIM_IER, 1 << i);
-}
-
-static int eim_setup(struct irq_controller *irqc, unsigned int irq,
- struct irqaction *action)
-{
- struct at32_sm *sm = to_eim(irqc);
- sm->action[irq - sm->irqc.first_irq] = action;
- /* Acknowledge earlier interrupts */
- sm_writel(sm, EIM_ICR, (1<<(irq - sm->irqc.first_irq)));
- eim_unmask(irqc, irq);
- return 0;
-}
-
-static void eim_free(struct irq_controller *irqc, unsigned int irq,
- void *dev)
-{
- struct at32_sm *sm = to_eim(irqc);
- eim_mask(irqc, irq);
- sm->action[irq - sm->irqc.first_irq] = &eim_spurious_action;
-}
-
-static int eim_set_type(struct irq_controller *irqc, unsigned int irq,
- unsigned int type)
-{
- struct at32_sm *sm = to_eim(irqc);
- unsigned long flags;
- u32 value, pattern;
-
- spin_lock_irqsave(&sm->lock, flags);
-
- pattern = 1 << (irq - sm->irqc.first_irq);
-
- value = sm_readl(sm, EIM_MODE);
- if (type & IRQ_TYPE_LEVEL)
- value |= pattern;
- else
- value &= ~pattern;
- sm_writel(sm, EIM_MODE, value);
- value = sm_readl(sm, EIM_EDGE);
- if (type & IRQ_EDGE_RISING)
- value |= pattern;
- else
- value &= ~pattern;
- sm_writel(sm, EIM_EDGE, value);
- value = sm_readl(sm, EIM_LEVEL);
- if (type & IRQ_LEVEL_HIGH)
- value |= pattern;
- else
- value &= ~pattern;
- sm_writel(sm, EIM_LEVEL, value);
-
- spin_unlock_irqrestore(&sm->lock, flags);
-
- return 0;
-}
-
-static unsigned int eim_get_type(struct irq_controller *irqc,
- unsigned int irq)
-{
- struct at32_sm *sm = to_eim(irqc);
- unsigned long flags;
- unsigned int type = 0;
- u32 mode, edge, level, pattern;
-
- pattern = 1 << (irq - sm->irqc.first_irq);
-
- spin_lock_irqsave(&sm->lock, flags);
- mode = sm_readl(sm, EIM_MODE);
- edge = sm_readl(sm, EIM_EDGE);
- level = sm_readl(sm, EIM_LEVEL);
- spin_unlock_irqrestore(&sm->lock, flags);
-
- if (mode & pattern)
- type |= IRQ_TYPE_LEVEL;
- if (edge & pattern)
- type |= IRQ_EDGE_RISING;
- if (level & pattern)
- type |= IRQ_LEVEL_HIGH;
-
- return type;
-}
-
-static struct irq_controller_class eim_irq_class = {
- .typename = "EIM",
- .handle = eim_handle_irq,
- .setup = eim_setup,
- .free = eim_free,
- .mask = eim_mask,
- .unmask = eim_unmask,
- .set_type = eim_set_type,
- .get_type = eim_get_type,
-};
-
-static int __init eim_init(void)
-{
- struct at32_sm *sm = &system_manager;
- unsigned int i;
- u32 pattern;
- int ret;
-
- /*
- * The EIM is really the same module as SM, so register
- * mapping, etc. has been taken care of already.
- */
-
- /*
- * Find out how many interrupt lines that are actually
- * implemented in hardware.
- */
- sm_writel(sm, EIM_IDR, ~0UL);
- sm_writel(sm, EIM_MODE, ~0UL);
- pattern = sm_readl(sm, EIM_MODE);
- sm->irqc.nr_irqs = fls(pattern);
-
- ret = -ENOMEM;
- sm->action = kmalloc(sizeof(*sm->action) * sm->irqc.nr_irqs,
- GFP_KERNEL);
- if (!sm->action)
- goto out;
-
- for (i = 0; i < sm->irqc.nr_irqs; i++)
- sm->action[i] = &eim_spurious_action;
-
- spin_lock_init(&sm->lock);
- sm->irqc.irq_group = sm->pdev->resource[SM_EIM_IRQ_RESOURCE].start;
- sm->irqc.class = &eim_irq_class;
-
- ret = intc_register_controller(&sm->irqc);
- if (ret < 0)
- goto out_free_actions;
-
- printk("EIM: External Interrupt Module at 0x%p, IRQ group %u\n",
- sm->regs, sm->irqc.irq_group);
- printk("EIM: Handling %u external IRQs, starting with IRQ%u\n",
- sm->irqc.nr_irqs, sm->irqc.first_irq);
-
- return 0;
-
-out_free_actions:
- kfree(sm->action);
-out:
- return ret;
-}
-arch_initcall(eim_init);