summaryrefslogtreecommitdiff
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.c172
-rw-r--r--arch/avr32/mach-at32ap/clock.c8
-rw-r--r--arch/avr32/mach-at32ap/clock.h2
-rw-r--r--arch/avr32/mach-at32ap/extint.c36
-rw-r--r--arch/avr32/mach-at32ap/pio.c255
6 files changed, 408 insertions, 67 deletions
diff --git a/arch/avr32/mach-at32ap/Makefile b/arch/avr32/mach-at32ap/Makefile
index f62eb6915510..b21bea9af8b1 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 48f4ef38c70e..bc235507c5c7 100644
--- a/arch/avr32/mach-at32ap/at32ap7000.c
+++ b/arch/avr32/mach-at32ap/at32ap7000.c
@@ -8,6 +8,7 @@
#include <linux/clk.h>
#include <linux/init.h>
#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
#include <asm/io.h>
@@ -310,8 +311,6 @@ static void genclk_mode(struct clk *clk, int enabled)
{
u32 control;
- BUG_ON(clk->index > 7);
-
control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index);
if (enabled)
control |= SM_BIT(CEN);
@@ -325,11 +324,6 @@ static unsigned long genclk_get_rate(struct clk *clk)
u32 control;
unsigned long div = 1;
- BUG_ON(clk->index > 7);
-
- if (!clk->parent)
- return 0;
-
control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index);
if (control & SM_BIT(DIVEN))
div = 2 * (SM_BFEXT(DIV, control) + 1);
@@ -342,11 +336,6 @@ static long genclk_set_rate(struct clk *clk, unsigned long rate, int apply)
u32 control;
unsigned long parent_rate, actual_rate, div;
- BUG_ON(clk->index > 7);
-
- if (!clk->parent)
- return 0;
-
parent_rate = clk->parent->get_rate(clk->parent);
control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index);
@@ -373,11 +362,8 @@ int genclk_set_parent(struct clk *clk, struct clk *parent)
{
u32 control;
- BUG_ON(clk->index > 7);
-
printk("clk %s: new parent %s (was %s)\n",
- clk->name, parent->name,
- clk->parent ? clk->parent->name : "(null)");
+ clk->name, parent->name, clk->parent->name);
control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index);
@@ -399,6 +385,22 @@ int genclk_set_parent(struct clk *clk, struct clk *parent)
return 0;
}
+static void __init genclk_init_parent(struct clk *clk)
+{
+ u32 control;
+ struct clk *parent;
+
+ BUG_ON(clk->index > 7);
+
+ control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index);
+ if (control & SM_BIT(OSCSEL))
+ parent = (control & SM_BIT(PLLSEL)) ? &pll1 : &osc1;
+ else
+ parent = (control & SM_BIT(PLLSEL)) ? &pll0 : &osc0;
+
+ clk->parent = parent;
+}
+
/* --------------------------------------------------------------------
* System peripherals
* -------------------------------------------------------------------- */
@@ -496,9 +498,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 +518,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 +531,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 +593,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,32 +738,79 @@ 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)
+static void
+at32_spi_setup_slaves(unsigned int bus_num, struct spi_board_info *b,
+ unsigned int n, const u8 *pins)
{
+ unsigned int pin, mode;
+
+ for (; n; n--, b++) {
+ b->bus_num = bus_num;
+ if (b->chip_select >= 4)
+ continue;
+ pin = (unsigned)b->controller_data;
+ if (!pin) {
+ pin = pins[b->chip_select];
+ b->controller_data = (void *)pin;
+ }
+ mode = AT32_GPIOF_OUTPUT;
+ if (!(b->mode & SPI_CS_HIGH))
+ mode |= AT32_GPIOF_HIGH;
+ at32_select_gpio(pin, mode);
+ }
+}
+
+struct platform_device *__init
+at32_add_device_spi(unsigned int id, struct spi_board_info *b, unsigned int n)
+{
+ /*
+ * Manage the chipselects as GPIOs, normally using the same pins
+ * the SPI controller expects; but boards can use other pins.
+ */
+ static u8 __initdata spi0_pins[] =
+ { GPIO_PIN_PA(3), GPIO_PIN_PA(4),
+ GPIO_PIN_PA(5), GPIO_PIN_PA(20), };
+ static u8 __initdata spi1_pins[] =
+ { GPIO_PIN_PB(2), GPIO_PIN_PB(3),
+ GPIO_PIN_PB(4), GPIO_PIN_PA(27), };
struct platform_device *pdev;
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 */
+ at32_spi_setup_slaves(0, b, n, spi0_pins);
+ 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 */
+ at32_spi_setup_slaves(1, b, n, spi1_pins);
break;
default:
return NULL;
}
+ spi_register_board_info(b, n);
platform_device_register(pdev);
return pdev;
}
@@ -837,6 +894,50 @@ at32_add_device_lcdc(unsigned int id, struct lcdc_platform_data *data)
return pdev;
}
+/* --------------------------------------------------------------------
+ * GCLK
+ * -------------------------------------------------------------------- */
+static struct clk gclk0 = {
+ .name = "gclk0",
+ .mode = genclk_mode,
+ .get_rate = genclk_get_rate,
+ .set_rate = genclk_set_rate,
+ .set_parent = genclk_set_parent,
+ .index = 0,
+};
+static struct clk gclk1 = {
+ .name = "gclk1",
+ .mode = genclk_mode,
+ .get_rate = genclk_get_rate,
+ .set_rate = genclk_set_rate,
+ .set_parent = genclk_set_parent,
+ .index = 1,
+};
+static struct clk gclk2 = {
+ .name = "gclk2",
+ .mode = genclk_mode,
+ .get_rate = genclk_get_rate,
+ .set_rate = genclk_set_rate,
+ .set_parent = genclk_set_parent,
+ .index = 2,
+};
+static struct clk gclk3 = {
+ .name = "gclk3",
+ .mode = genclk_mode,
+ .get_rate = genclk_get_rate,
+ .set_rate = genclk_set_rate,
+ .set_parent = genclk_set_parent,
+ .index = 3,
+};
+static struct clk gclk4 = {
+ .name = "gclk4",
+ .mode = genclk_mode,
+ .get_rate = genclk_get_rate,
+ .set_rate = genclk_set_rate,
+ .set_parent = genclk_set_parent,
+ .index = 4,
+};
+
struct clk *at32_clock_list[] = {
&osc32k,
&osc0,
@@ -860,6 +961,7 @@ struct clk *at32_clock_list[] = {
&pio1_mck,
&pio2_mck,
&pio3_mck,
+ &pio4_mck,
&atmel_usart0_usart,
&atmel_usart1_usart,
&atmel_usart2_usart,
@@ -868,9 +970,15 @@ 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,
+ &gclk0,
+ &gclk1,
+ &gclk2,
+ &gclk3,
+ &gclk4,
};
unsigned int at32_nr_clocks = ARRAY_SIZE(at32_clock_list);
@@ -880,6 +988,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)
@@ -898,6 +1007,13 @@ void __init at32_clock_init(void)
if (sm_readl(sm, PM_PLL1) & SM_BIT(PLLOSC))
pll1.parent = &osc1;
+ genclk_init_parent(&gclk0);
+ genclk_init_parent(&gclk1);
+ genclk_init_parent(&gclk2);
+ genclk_init_parent(&gclk3);
+ genclk_init_parent(&gclk4);
+ genclk_init_parent(&lcdc0_pixclk);
+
/*
* Turn on all clocks that have at least one user already, and
* turn off everything else. We only do this for module
diff --git a/arch/avr32/mach-at32ap/clock.c b/arch/avr32/mach-at32ap/clock.c
index 3d0d1097389f..00c435452d7e 100644
--- a/arch/avr32/mach-at32ap/clock.c
+++ b/arch/avr32/mach-at32ap/clock.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2006 Atmel Corporation
*
- * Based on arch/arm/mach-at91rm9200/clock.c
+ * Based on arch/arm/mach-at91/clock.c
* Copyright (C) 2005 David Brownell
* Copyright (C) 2005 Ivan Kokshaysky
*
@@ -63,7 +63,11 @@ EXPORT_SYMBOL(clk_enable);
static void __clk_disable(struct clk *clk)
{
- BUG_ON(clk->users == 0);
+ if (clk->users == 0) {
+ printk(KERN_ERR "%s: mismatched disable\n", clk->name);
+ WARN_ON(1);
+ return;
+ }
if (--clk->users == 0 && clk->mode)
clk->mode(clk, 0);
diff --git a/arch/avr32/mach-at32ap/clock.h b/arch/avr32/mach-at32ap/clock.h
index f953f044ba4d..bb8e1f295835 100644
--- a/arch/avr32/mach-at32ap/clock.h
+++ b/arch/avr32/mach-at32ap/clock.h
@@ -3,7 +3,7 @@
*
* Copyright (C) 2006 Atmel Corporation
*
- * Based on arch/arm/mach-at91rm9200/clock.c
+ * Based on arch/arm/mach-at91/clock.c
* Copyright (C) 2005 David Brownell
* Copyright (C) 2005 Ivan Kokshaysky
*
diff --git a/arch/avr32/mach-at32ap/extint.c b/arch/avr32/mach-at32ap/extint.c
index b59272e81b9a..4a60eccfebd2 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 f1280ed8ed6d..9ba5654cde11 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);
}