diff options
Diffstat (limited to 'arch/avr32/mach-at32ap')
-rw-r--r-- | arch/avr32/mach-at32ap/Makefile | 2 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/at32ap7000.c | 236 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/extint.c | 34 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/intc.c | 4 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/pio.c | 322 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/sm.c | 289 |
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); |