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.c60
-rw-r--r--arch/avr32/mach-at32ap/extint.c36
-rw-r--r--arch/avr32/mach-at32ap/pio.c255
4 files changed, 306 insertions, 47 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 48f4ef38c70..c1e477ec757 100644
--- a/arch/avr32/mach-at32ap/at32ap7000.c
+++ b/arch/avr32/mach-at32ap/at32ap7000.c
@@ -496,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);
@@ -509,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);
}
/* --------------------------------------------------------------------
@@ -521,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);
@@ -583,7 +591,7 @@ static inline void configure_usart3_pins(void)
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)
{
@@ -728,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)
{
@@ -741,13 +756,33 @@ struct platform_device *__init at32_add_device_spi(unsigned int id)
switch (id) {
case 0:
- pdev = &spi0_device;
+ 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 */
- select_peripheral(PA(3), PERIPH_A, 0); /* NPCS0 */
- select_peripheral(PA(4), PERIPH_A, 0); /* NPCS1 */
- select_peripheral(PA(5), PERIPH_A, 0); /* NPCS2 */
+
+ /* 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:
@@ -860,6 +895,7 @@ struct clk *at32_clock_list[] = {
&pio1_mck,
&pio2_mck,
&pio3_mck,
+ &pio4_mck,
&atmel_usart0_usart,
&atmel_usart1_usart,
&atmel_usart2_usart,
@@ -868,7 +904,8 @@ struct clk *at32_clock_list[] = {
&macb0_pclk,
&macb1_hclk,
&macb1_pclk,
- &spi0_mck,
+ &atmel_spi0_spi_clk,
+ &atmel_spi1_spi_clk,
&lcdc0_hclk,
&lcdc0_pixclk,
};
@@ -880,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 b59272e81b9..4a60eccfebd 100644
--- a/arch/avr32/mach-at32ap/extint.c
+++ b/arch/avr32/mach-at32ap/extint.c
@@ -55,20 +55,11 @@ static int eim_set_irq_type(unsigned int irq, unsigned int flow_type)
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];
- desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
- desc->status |= flow_type & IRQ_TYPE_SENSE_MASK;
-
- if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) {
- desc->status |= IRQ_LEVEL;
- set_irq_handler(irq, handle_level_irq);
- } else {
- set_irq_handler(irq, handle_edge_irq);
- }
-
spin_lock_irqsave(&sm->lock, flags);
mode = sm_readl(sm, EIM_MODE);
@@ -97,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);
@@ -122,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);
@@ -133,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)
@@ -168,8 +165,9 @@ static int __init eim_init(void)
sm->eim_chip = &eim_chip;
for (i = 0; i < nr_irqs; i++) {
+ /* NOTE the handler we set here is ignored by the demux */
set_irq_chip_and_handler(sm->eim_first_irq + i, &eim_chip,
- handle_edge_irq);
+ handle_level_irq);
set_irq_chip_data(sm->eim_first_irq + i, sm);
}
diff --git a/arch/avr32/mach-at32ap/pio.c b/arch/avr32/mach-at32ap/pio.c
index f1280ed8ed6..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>
@@ -26,7 +28,8 @@ struct pio_device {
const struct platform_device *pdev;
struct clk *clk;
u32 pinmux_mask;
- char name[32];
+ u32 gpio_mask;
+ char name[8];
};
static struct pio_device pio_dev[MAX_NR_PIO_DEVICES];
@@ -76,6 +79,9 @@ void __init at32_select_periph(unsigned int pin, unsigned int periph,
if (!(flags & AT32_GPIOF_PULLUP))
pio_writel(pio, PUDR, mask);
+ /* gpio_request NOT allowed */
+ set_bit(pin_index, &pio->gpio_mask);
+
return;
fail:
@@ -99,19 +105,52 @@ void __init at32_select_gpio(unsigned int pin, unsigned long flags)
goto fail;
}
- pio_writel(pio, PUER, mask);
- if (flags & AT32_GPIOF_HIGH)
- pio_writel(pio, SODR, mask);
- else
- pio_writel(pio, CODR, mask);
- if (flags & AT32_GPIOF_OUTPUT)
+ 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
+ } 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);
- if (!(flags & AT32_GPIOF_PULLUP))
- pio_writel(pio, PUDR, 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;
@@ -119,20 +158,197 @@ 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;
}
@@ -148,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)
{
@@ -184,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);
}