diff options
author | Arnd Bergmann <arnd@arndb.de> | 2012-03-02 13:22:28 +0000 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2012-03-02 13:22:28 +0000 |
commit | 48b3b08e00012382cb52099e509b529305ae0a00 (patch) | |
tree | 0b5ff03c195707e3e545731121bc4537a3b42ac4 /arch/arm/mach-at91 | |
parent | 6e1d521b9d1201214ec4a67a7e4360232be5f963 (diff) | |
parent | f75622f4679479d352d2fa83e0d84c6c13cfcb5f (diff) |
Merge branch 'at91-3.4-cleanup2+DT' of git://github.com/at91linux/linux-at91 into next/dt
* 'at91-3.4-cleanup2+DT' of git://github.com/at91linux/linux-at91: (22 commits)
ARM: at91: at91sam9x5cm/dt: add leds support
ARM: at91: usb_a9g20/dt: add gpio-keys support
ARM: at91: at91sam9m10g45ek/dt: add gpio-keys support
ARM: at91: at91sam9m10g45ek/dt: add leds support
ARM: at91: usb_a9g20/dt: add leds support
ARM: at91/pio: add new PIO3 features
ARM: at91: add sam9_smc.o to at91sam9x5 build
ARM: at91/tc/clocksource: Add 32 bit variant to Timer Counter
ARM: at91/tc: add device tree support to atmel_tclib
ARM: at91/tclib: take iomem size from resource
ARM: at91/pit: add traces in case of error
ARM: at91: pit add DT support
ARM: at91: AIC and GPIO IRQ device tree initialization
ARM: at91/board-dt: remove AIC irq domain from board file
ARM: at91/gpio: remove the static specification of gpio_chip.base
ARM: at91/gpio: add .to_irq gpio_chip handler
ARM: at91/gpio: non-DT builds do not have gpio_chip.of_node field
ARM: at91/gpio: add irqdomain and DT support
ARM: at91/gpio: change comments and one variable name
ARM/USB: at91/ohci-at91: remove the use of irq_to_gpio
...
Diffstat (limited to 'arch/arm/mach-at91')
-rw-r--r-- | arch/arm/mach-at91/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-at91/at91sam9260.c | 7 | ||||
-rw-r--r-- | arch/arm/mach-at91/at91sam9260_devices.c | 21 | ||||
-rw-r--r-- | arch/arm/mach-at91/at91sam926x_time.c | 68 | ||||
-rw-r--r-- | arch/arm/mach-at91/at91sam9g45.c | 3 | ||||
-rw-r--r-- | arch/arm/mach-at91/at91sam9g45_devices.c | 21 | ||||
-rw-r--r-- | arch/arm/mach-at91/at91sam9x5.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-at91/board-dt.c | 14 | ||||
-rw-r--r-- | arch/arm/mach-at91/board-snapper9260.c | 10 | ||||
-rw-r--r-- | arch/arm/mach-at91/generic.h | 6 | ||||
-rw-r--r-- | arch/arm/mach-at91/gpio.c | 625 | ||||
-rw-r--r-- | arch/arm/mach-at91/include/mach/at91_pio.h | 25 | ||||
-rw-r--r-- | arch/arm/mach-at91/include/mach/gpio.h | 17 | ||||
-rw-r--r-- | arch/arm/mach-at91/irq.c | 132 |
14 files changed, 810 insertions, 143 deletions
diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile index 1b6518518d9..8512e53bed9 100644 --- a/arch/arm/mach-at91/Makefile +++ b/arch/arm/mach-at91/Makefile @@ -20,7 +20,7 @@ obj-$(CONFIG_ARCH_AT91SAM9263) += at91sam9263.o at91sam926x_time.o at91sam9263_d obj-$(CONFIG_ARCH_AT91SAM9RL) += at91sam9rl.o at91sam926x_time.o at91sam9rl_devices.o sam9_smc.o obj-$(CONFIG_ARCH_AT91SAM9G20) += at91sam9260.o at91sam926x_time.o at91sam9260_devices.o sam9_smc.o obj-$(CONFIG_ARCH_AT91SAM9G45) += at91sam9g45.o at91sam926x_time.o at91sam9g45_devices.o sam9_smc.o -obj-$(CONFIG_ARCH_AT91SAM9X5) += at91sam9x5.o at91sam926x_time.o +obj-$(CONFIG_ARCH_AT91SAM9X5) += at91sam9x5.o at91sam926x_time.o sam9_smc.o obj-$(CONFIG_ARCH_AT91X40) += at91x40.o at91x40_time.o # AT91RM9200 board-specific support diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c index 4ade265be80..14b5a9c9a51 100644 --- a/arch/arm/mach-at91/at91sam9260.c +++ b/arch/arm/mach-at91/at91sam9260.c @@ -209,6 +209,13 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("usart", "fffd0000.serial", &usart3_clk), CLKDEV_CON_DEV_ID("usart", "fffd4000.serial", &usart4_clk), CLKDEV_CON_DEV_ID("usart", "fffd8000.serial", &usart5_clk), + /* more tc lookup table for DT entries */ + CLKDEV_CON_DEV_ID("t0_clk", "fffa0000.timer", &tc0_clk), + CLKDEV_CON_DEV_ID("t1_clk", "fffa0000.timer", &tc1_clk), + CLKDEV_CON_DEV_ID("t2_clk", "fffa0000.timer", &tc2_clk), + CLKDEV_CON_DEV_ID("t0_clk", "fffdc000.timer", &tc3_clk), + CLKDEV_CON_DEV_ID("t1_clk", "fffdc000.timer", &tc4_clk), + CLKDEV_CON_DEV_ID("t2_clk", "fffdc000.timer", &tc5_clk), /* fake hclk clock */ CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk), CLKDEV_CON_ID("pioA", &pioA_clk), diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c index 40f6aeaea04..7e5651ee9f8 100644 --- a/arch/arm/mach-at91/at91sam9260_devices.c +++ b/arch/arm/mach-at91/at91sam9260_devices.c @@ -642,7 +642,7 @@ void __init at91_add_device_spi(struct spi_board_info *devices, int nr_devices) static struct resource tcb0_resources[] = { [0] = { .start = AT91SAM9260_BASE_TCB0, - .end = AT91SAM9260_BASE_TCB0 + SZ_16K - 1, + .end = AT91SAM9260_BASE_TCB0 + SZ_256 - 1, .flags = IORESOURCE_MEM, }, [1] = { @@ -672,7 +672,7 @@ static struct platform_device at91sam9260_tcb0_device = { static struct resource tcb1_resources[] = { [0] = { .start = AT91SAM9260_BASE_TCB1, - .end = AT91SAM9260_BASE_TCB1 + SZ_16K - 1, + .end = AT91SAM9260_BASE_TCB1 + SZ_256 - 1, .flags = IORESOURCE_MEM, }, [1] = { @@ -699,8 +699,25 @@ static struct platform_device at91sam9260_tcb1_device = { .num_resources = ARRAY_SIZE(tcb1_resources), }; +#if defined(CONFIG_OF) +static struct of_device_id tcb_ids[] = { + { .compatible = "atmel,at91rm9200-tcb" }, + { /*sentinel*/ } +}; +#endif + static void __init at91_add_device_tc(void) { +#if defined(CONFIG_OF) + struct device_node *np; + + np = of_find_matching_node(NULL, tcb_ids); + if (np) { + of_node_put(np); + return; + } +#endif + platform_device_register(&at91sam9260_tcb0_device); platform_device_register(&at91sam9260_tcb1_device); } diff --git a/arch/arm/mach-at91/at91sam926x_time.c b/arch/arm/mach-at91/at91sam926x_time.c index d89ead740a9..a94758b4273 100644 --- a/arch/arm/mach-at91/at91sam926x_time.c +++ b/arch/arm/mach-at91/at91sam926x_time.c @@ -14,6 +14,9 @@ #include <linux/kernel.h> #include <linux/clk.h> #include <linux/clockchips.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <asm/mach/time.h> @@ -133,7 +136,8 @@ static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id) static struct irqaction at91sam926x_pit_irq = { .name = "at91_tick", .flags = IRQF_SHARED | IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, - .handler = at91sam926x_pit_interrupt + .handler = at91sam926x_pit_interrupt, + .irq = AT91_ID_SYS, }; static void at91sam926x_pit_reset(void) @@ -149,6 +153,51 @@ static void at91sam926x_pit_reset(void) pit_write(AT91_PIT_MR, (pit_cycle - 1) | AT91_PIT_PITEN); } +#ifdef CONFIG_OF +static struct of_device_id pit_timer_ids[] = { + { .compatible = "atmel,at91sam9260-pit" }, + { /* sentinel */ } +}; + +static int __init of_at91sam926x_pit_init(void) +{ + struct device_node *np; + int ret; + + np = of_find_matching_node(NULL, pit_timer_ids); + if (!np) + goto err; + + pit_base_addr = of_iomap(np, 0); + if (!pit_base_addr) + goto node_err; + + /* Get the interrupts property */ + ret = irq_of_parse_and_map(np, 0); + if (!ret) { + pr_crit("AT91: PIT: Unable to get IRQ from DT\n"); + goto ioremap_err; + } + at91sam926x_pit_irq.irq = ret; + + of_node_put(np); + + return 0; + +ioremap_err: + iounmap(pit_base_addr); +node_err: + of_node_put(np); +err: + return -EINVAL; +} +#else +static int __init of_at91sam926x_pit_init(void) +{ + return -EINVAL; +} +#endif + /* * Set up both clocksource and clockevent support. */ @@ -156,6 +205,10 @@ static void __init at91sam926x_pit_init(void) { unsigned long pit_rate; unsigned bits; + int ret; + + /* For device tree enabled device: initialize here */ + of_at91sam926x_pit_init(); /* * Use our actual MCK to figure out how many MCK/16 ticks per @@ -177,7 +230,9 @@ static void __init at91sam926x_pit_init(void) clocksource_register_hz(&pit_clk, pit_rate); /* Set up irq handler */ - setup_irq(AT91_ID_SYS, &at91sam926x_pit_irq); + ret = setup_irq(at91sam926x_pit_irq.irq, &at91sam926x_pit_irq); + if (ret) + pr_crit("AT91: PIT: Unable to setup IRQ\n"); /* Set up and register clockevents */ pit_clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, pit_clkevt.shift); @@ -193,6 +248,15 @@ static void at91sam926x_pit_suspend(void) void __init at91sam926x_ioremap_pit(u32 addr) { +#if defined(CONFIG_OF) + struct device_node *np = + of_find_matching_node(NULL, pit_timer_ids); + + if (np) { + of_node_put(np); + return; + } +#endif pit_base_addr = ioremap(addr, 16); if (!pit_base_addr) diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c index a41622ea61b..0014573dfe1 100644 --- a/arch/arm/mach-at91/at91sam9g45.c +++ b/arch/arm/mach-at91/at91sam9g45.c @@ -229,6 +229,9 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("usart", "fff90000.serial", &usart1_clk), CLKDEV_CON_DEV_ID("usart", "fff94000.serial", &usart2_clk), CLKDEV_CON_DEV_ID("usart", "fff98000.serial", &usart3_clk), + /* more tc lookup table for DT entries */ + CLKDEV_CON_DEV_ID("t0_clk", "fff7c000.timer", &tcb0_clk), + CLKDEV_CON_DEV_ID("t0_clk", "fffd4000.timer", &tcb0_clk), /* fake hclk clock */ CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &uhphs_clk), CLKDEV_CON_ID("pioA", &pioA_clk), diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c index 410829532aa..4320b209678 100644 --- a/arch/arm/mach-at91/at91sam9g45_devices.c +++ b/arch/arm/mach-at91/at91sam9g45_devices.c @@ -1052,7 +1052,7 @@ void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data) {} static struct resource tcb0_resources[] = { [0] = { .start = AT91SAM9G45_BASE_TCB0, - .end = AT91SAM9G45_BASE_TCB0 + SZ_16K - 1, + .end = AT91SAM9G45_BASE_TCB0 + SZ_256 - 1, .flags = IORESOURCE_MEM, }, [1] = { @@ -1073,7 +1073,7 @@ static struct platform_device at91sam9g45_tcb0_device = { static struct resource tcb1_resources[] = { [0] = { .start = AT91SAM9G45_BASE_TCB1, - .end = AT91SAM9G45_BASE_TCB1 + SZ_16K - 1, + .end = AT91SAM9G45_BASE_TCB1 + SZ_256 - 1, .flags = IORESOURCE_MEM, }, [1] = { @@ -1090,8 +1090,25 @@ static struct platform_device at91sam9g45_tcb1_device = { .num_resources = ARRAY_SIZE(tcb1_resources), }; +#if defined(CONFIG_OF) +static struct of_device_id tcb_ids[] = { + { .compatible = "atmel,at91rm9200-tcb" }, + { /*sentinel*/ } +}; +#endif + static void __init at91_add_device_tc(void) { +#if defined(CONFIG_OF) + struct device_node *np; + + np = of_find_matching_node(NULL, tcb_ids); + if (np) { + of_node_put(np); + return; + } +#endif + platform_device_register(&at91sam9g45_tcb0_device); platform_device_register(&at91sam9g45_tcb1_device); } diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c index d17d4262665..a34d96afa74 100644 --- a/arch/arm/mach-at91/at91sam9x5.c +++ b/arch/arm/mach-at91/at91sam9x5.c @@ -301,8 +301,6 @@ static void __init at91sam9x5_map_io(void) static void __init at91sam9x5_ioremap_registers(void) { - if (of_at91sam926x_pit_init() < 0) - panic("Impossible to find PIT\n"); at91_ioremap_ramc(0, AT91SAM9X5_BASE_DDRSDRC0, 512); } diff --git a/arch/arm/mach-at91/board-dt.c b/arch/arm/mach-at91/board-dt.c index 08c8ad8609b..583b72472ad 100644 --- a/arch/arm/mach-at91/board-dt.c +++ b/arch/arm/mach-at91/board-dt.c @@ -15,7 +15,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/gpio.h> -#include <linux/irqdomain.h> +#include <linux/of.h> #include <linux/of_irq.h> #include <linux/of_platform.h> @@ -82,15 +82,17 @@ static void __init ek_add_device_nand(void) at91_add_device_nand(&ek_nand_data); } -static const struct of_device_id aic_of_match[] __initconst = { - { .compatible = "atmel,at91rm9200-aic", }, - {}, +static const struct of_device_id irq_of_match[] __initconst = { + + { .compatible = "atmel,at91rm9200-aic", .data = at91_aic_of_init }, + { .compatible = "atmel,at91rm9200-gpio", .data = at91_gpio_of_irq_setup }, + { .compatible = "atmel,at91sam9x5-gpio", .data = at91_gpio_of_irq_setup }, + { /*sentinel*/ } }; static void __init at91_dt_init_irq(void) { - irq_domain_generate_simple(aic_of_match, 0xfffff000, 0); - at91_init_irq_default(); + of_irq_init(irq_of_match); } static void __init at91_dt_device_init(void) diff --git a/arch/arm/mach-at91/board-snapper9260.c b/arch/arm/mach-at91/board-snapper9260.c index 4770db08e5a..3c2e3fcc310 100644 --- a/arch/arm/mach-at91/board-snapper9260.c +++ b/arch/arm/mach-at91/board-snapper9260.c @@ -145,11 +145,11 @@ static struct i2c_board_info __initdata snapper9260_i2c_devices[] = { /* Audio codec */ I2C_BOARD_INFO("tlv320aic23", 0x1a), }, - { +}; + +static struct i2c_board_info __initdata snapper9260_i2c_isl1208 = { /* RTC */ I2C_BOARD_INFO("isl1208", 0x6f), - .irq = gpio_to_irq(AT91_PIN_PA31), - }, }; static void __init snapper9260_add_device_nand(void) @@ -163,6 +163,10 @@ static void __init snapper9260_board_init(void) { at91_add_device_i2c(snapper9260_i2c_devices, ARRAY_SIZE(snapper9260_i2c_devices)); + + snapper9260_i2c_isl1208.irq = gpio_to_irq(AT91_PIN_PA31); + i2c_register_board_info(0, &snapper9260_i2c_isl1208, 1); + at91_add_device_serial(); at91_add_device_usbh(&snapper9260_usbh_data); at91_add_device_udc(&snapper9260_udc_data); diff --git a/arch/arm/mach-at91/generic.h b/arch/arm/mach-at91/generic.h index 4cad85e5747..459f01a4a54 100644 --- a/arch/arm/mach-at91/generic.h +++ b/arch/arm/mach-at91/generic.h @@ -9,6 +9,7 @@ */ #include <linux/clkdev.h> +#include <linux/of.h> /* Map io */ extern void __init at91_map_io(void); @@ -25,6 +26,9 @@ extern void __init at91_init_irq_default(void); extern void __init at91_init_interrupts(unsigned int priority[]); extern void __init at91x40_init_interrupts(unsigned int priority[]); extern void __init at91_aic_init(unsigned int priority[]); +extern int __init at91_aic_of_init(struct device_node *node, + struct device_node *parent); + /* Timer */ struct sys_timer; @@ -84,5 +88,7 @@ struct at91_gpio_bank { }; extern void __init at91_gpio_init(struct at91_gpio_bank *, int nr_banks); extern void __init at91_gpio_irq_setup(void); +extern int __init at91_gpio_of_irq_setup(struct device_node *node, + struct device_node *parent); extern int at91_extern_irq; diff --git a/arch/arm/mach-at91/gpio.c b/arch/arm/mach-at91/gpio.c index 74d6783eeab..325837a264c 100644 --- a/arch/arm/mach-at91/gpio.c +++ b/arch/arm/mach-at91/gpio.c @@ -11,6 +11,7 @@ #include <linux/clk.h> #include <linux/errno.h> +#include <linux/device.h> #include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/irq.h> @@ -20,6 +21,10 @@ #include <linux/list.h> #include <linux/module.h> #include <linux/io.h> +#include <linux/irqdomain.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_gpio.h> #include <mach/hardware.h> #include <mach/at91_pio.h> @@ -29,9 +34,12 @@ struct at91_gpio_chip { struct gpio_chip chip; struct at91_gpio_chip *next; /* Bank sharing same clock */ - int id; /* ID of register bank */ - void __iomem *regbase; /* Base of register bank */ + int pioc_hwirq; /* PIO bank interrupt identifier on AIC */ + int pioc_virq; /* PIO bank Linux virtual interrupt */ + int pioc_idx; /* PIO bank index */ + void __iomem *regbase; /* PIO bank virtual address */ struct clk *clock; /* associated clock */ + struct irq_domain *domain; /* associated irq domain */ }; #define to_at91_gpio_chip(c) container_of(c, struct at91_gpio_chip, chip) @@ -43,8 +51,9 @@ static int at91_gpiolib_direction_output(struct gpio_chip *chip, unsigned offset, int val); static int at91_gpiolib_direction_input(struct gpio_chip *chip, unsigned offset); +static int at91_gpiolib_to_irq(struct gpio_chip *chip, unsigned offset); -#define AT91_GPIO_CHIP(name, base_gpio, nr_gpio) \ +#define AT91_GPIO_CHIP(name, nr_gpio) \ { \ .chip = { \ .label = name, \ @@ -53,20 +62,28 @@ static int at91_gpiolib_direction_input(struct gpio_chip *chip, .get = at91_gpiolib_get, \ .set = at91_gpiolib_set, \ .dbg_show = at91_gpiolib_dbg_show, \ - .base = base_gpio, \ + .to_irq = at91_gpiolib_to_irq, \ .ngpio = nr_gpio, \ }, \ } static struct at91_gpio_chip gpio_chip[] = { - AT91_GPIO_CHIP("pioA", 0x00, 32), - AT91_GPIO_CHIP("pioB", 0x20, 32), - AT91_GPIO_CHIP("pioC", 0x40, 32), - AT91_GPIO_CHIP("pioD", 0x60, 32), - AT91_GPIO_CHIP("pioE", 0x80, 32), + AT91_GPIO_CHIP("pioA", 32), + AT91_GPIO_CHIP("pioB", 32), + AT91_GPIO_CHIP("pioC", 32), + AT91_GPIO_CHIP("pioD", 32), + AT91_GPIO_CHIP("pioE", 32), }; static int gpio_banks; +static unsigned long at91_gpio_caps; + +/* All PIO controllers support PIO3 features */ +#define AT91_GPIO_CAP_PIO3 (1 << 0) + +#define has_pio3() (at91_gpio_caps & AT91_GPIO_CAP_PIO3) + +/*--------------------------------------------------------------------------*/ static inline void __iomem *pin_to_controller(unsigned pin) { @@ -83,6 +100,25 @@ static inline unsigned pin_to_mask(unsigned pin) } +static char peripheral_function(void __iomem *pio, unsigned mask) +{ + char ret = 'X'; + u8 select; + + if (pio) { + if (has_pio3()) { + select = !!(__raw_readl(pio + PIO_ABCDSR1) & mask); + select |= (!!(__raw_readl(pio + PIO_ABCDSR2) & mask) << 1); + ret = 'A' + select; + } else { + ret = __raw_readl(pio + PIO_ABSR) & mask ? + 'B' : 'A'; + } + } + + return ret; +} + /*--------------------------------------------------------------------------*/ /* Not all hardware capabilities are exposed through these calls; they @@ -130,7 +166,14 @@ int __init_or_module at91_set_A_periph(unsigned pin, int use_pullup) __raw_writel(mask, pio + PIO_IDR); __raw_writel(mask, pio + (use_pullup ? PIO_PUER : PIO_PUDR)); - __raw_writel(mask, pio + PIO_ASR); + if (has_pio3()) { + __raw_writel(__raw_readl(pio + PIO_ABCDSR1) & ~mask, + pio + PIO_ABCDSR1); + __raw_writel(__raw_readl(pio + PIO_ABCDSR2) & ~mask, + pio + PIO_ABCDSR2); + } else { + __raw_writel(mask, pio + PIO_ASR); + } __raw_writel(mask, pio + PIO_PDR); return 0; } @@ -150,7 +193,14 @@ int __init_or_module at91_set_B_periph(unsigned pin, int use_pullup) __raw_writel(mask, pio + PIO_IDR); __raw_writel(mask, pio + (use_pullup ? PIO_PUER : PIO_PUDR)); - __raw_writel(mask, pio + PIO_BSR); + if (has_pio3()) { + __raw_writel(__raw_readl(pio + PIO_ABCDSR1) | mask, + pio + PIO_ABCDSR1); + __raw_writel(__raw_readl(pio + PIO_ABCDSR2) & ~mask, + pio + PIO_ABCDSR2); + } else { + __raw_writel(mask, pio + PIO_BSR); + } __raw_writel(mask, pio + PIO_PDR); return 0; } @@ -158,8 +208,50 @@ EXPORT_SYMBOL(at91_set_B_periph); /* - * mux the pin to the gpio controller (instead of "A" or "B" peripheral), and - * configure it for an input. + * mux the pin to the "C" internal peripheral role. + */ +int __init_or_module at91_set_C_periph(unsigned pin, int use_pullup) +{ + void __iomem *pio = pin_to_controller(pin); + unsigned mask = pin_to_mask(pin); + + if (!pio || !has_pio3()) + return -EINVAL; + + __raw_writel(mask, pio + PIO_IDR); + __raw_writel(mask, pio + (use_pullup ? PIO_PUER : PIO_PUDR)); + __raw_writel(__raw_readl(pio + PIO_ABCDSR1) & ~mask, pio + PIO_ABCDSR1); + __raw_writel(__raw_readl(pio + PIO_ABCDSR2) | mask, pio + PIO_ABCDSR2); + __raw_writel(mask, pio + PIO_PDR); + return 0; +} +EXPORT_SYMBOL(at91_set_C_periph); + + +/* + * mux the pin to the "D" internal peripheral role. + */ +int __init_or_module at91_set_D_periph(unsigned pin, int use_pullup) +{ + void __iomem *pio = pin_to_controller(pin); + unsigned mask = pin_to_mask(pin); + + if (!pio || !has_pio3()) + return -EINVAL; + + __raw_writel(mask, pio + PIO_IDR); + __raw_writel(mask, pio + (use_pullup ? PIO_PUER : PIO_PUDR)); + __raw_writel(__raw_readl(pio + PIO_ABCDSR1) | mask, pio + PIO_ABCDSR1); + __raw_writel(__raw_readl(pio + PIO_ABCDSR2) | mask, pio + PIO_ABCDSR2); + __raw_writel(mask, pio + PIO_PDR); + return 0; +} +EXPORT_SYMBOL(at91_set_D_periph); + + +/* + * mux the pin to the gpio controller (instead of "A", "B", "C" + * or "D" peripheral), and configure it for an input. */ int __init_or_module at91_set_gpio_input(unsigned pin, int use_pullup) { @@ -179,8 +271,8 @@ EXPORT_SYMBOL(at91_set_gpio_input); /* - * mux the pin to the gpio controller (instead of "A" or "B" peripheral), - * and configure it for an output. + * mux the pin to the gpio controller (instead of "A", "B", "C" + * or "D" peripheral), and configure it for an output. */ int __init_or_module at91_set_gpio_output(unsigned pin, int value) { @@ -210,12 +302,37 @@ int __init_or_module at91_set_deglitch(unsigned pin, int is_on) if (!pio) return -EINVAL; + + if (has_pio3() && is_on) + __raw_writel(mask, pio + PIO_IFSCDR); __raw_writel(mask, pio + (is_on ? PIO_IFER : PIO_IFDR)); return 0; } EXPORT_SYMBOL(at91_set_deglitch); /* + * enable/disable the debounce filter; + */ +int __init_or_module at91_set_debounce(unsigned pin, int is_on, int div) +{ + void __iomem *pio = pin_to_controller(pin); + unsigned mask = pin_to_mask(pin); + + if (!pio || !has_pio3()) + return -EINVAL; + + if (is_on) { + __raw_writel(mask, pio + PIO_IFSCER); + __raw_writel(div & PIO_SCDR_DIV, pio + PIO_SCDR); + __raw_writel(mask, pio + PIO_IFER); + } else { + __raw_writel(mask, pio + PIO_IFDR); + } + return 0; +} +EXPORT_SYMBOL(at91_set_debounce); + +/* * enable/disable the multi-driver; This is only valid for output and * allows the output pin to run as an open collector output. */ @@ -233,6 +350,41 @@ int __init_or_module at91_set_multi_drive(unsigned pin, int is_on) EXPORT_SYMBOL(at91_set_multi_drive); /* + * enable/disable the pull-down. + * If pull-up already enabled while calling the function, we disable it. + */ +int __init_or_module at91_set_pulldown(unsigned pin, int is_on) +{ + void __iomem *pio = pin_to_controller(pin); + unsigned mask = pin_to_mask(pin); + + if (!pio || !has_pio3()) + return -EINVAL; + + /* Disable pull-up anyway */ + __raw_writel(mask, pio + PIO_PUDR); + __raw_writel(mask, pio + (is_on ? PIO_PPDER : PIO_PPDDR)); + return 0; +} +EXPORT_SYMBOL(at91_set_pulldown); + +/* + * disable Schmitt trigger + */ +int __init_or_module at91_disable_schmitt_trig(unsigned pin) +{ + void __iomem *pio = pin_to_controller(pin); + unsigned mask = pin_to_mask(pin); + + if (!pio || !has_pio3()) + return -EINVAL; + + __raw_writel(__raw_readl(pio + PIO_SCHMITT) | mask, pio + PIO_SCHMITT); + return 0; +} +EXPORT_SYMBOL(at91_disable_schmitt_trig); + +/* * assuming the pin is muxed as a gpio output, set its value. */ int at91_set_gpio_value(unsigned pin, int value) @@ -273,9 +425,9 @@ static u32 backups[MAX_GPIO_BANKS]; static int gpio_irq_set_wake(struct irq_data *d, unsigned state) { - unsigned pin = irq_to_gpio(d->irq); - unsigned mask = pin_to_mask(pin); - unsigned bank = pin / 32; + struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); + unsigned mask = 1 << d->hwirq; + unsigned bank = at91_gpio->pioc_idx; if (unlikely(bank >= MAX_GPIO_BANKS)) return -EINVAL; @@ -285,7 +437,7 @@ static int gpio_irq_set_wake(struct irq_data *d, unsigned state) else wakeups[bank] &= ~mask; - irq_set_irq_wake(gpio_chip[bank].id, state); + irq_set_irq_wake(at91_gpio->pioc_virq, state); return 0; } @@ -301,9 +453,10 @@ void at91_gpio_suspend(void) __raw_writel(backups[i], pio + PIO_IDR); __raw_writel(wakeups[i], pio + PIO_IER); - if (!wakeups[i]) + if (!wakeups[i]) { + clk_unprepare(gpio_chip[i].clock); clk_disable(gpio_chip[i].clock); - else { + } else { #ifdef CONFIG_PM_DEBUG printk(KERN_DEBUG "GPIO-%c may wake for %08x\n", 'A'+i, wakeups[i]); #endif @@ -318,8 +471,10 @@ void at91_gpio_resume(void) for (i = 0; i < gpio_banks; i++) { void __iomem *pio = gpio_chip[i].regbase; - if (!wakeups[i]) - clk_enable(gpio_chip[i].clock); + if (!wakeups[i]) { + if (clk_prepare(gpio_chip[i].clock) == 0) + clk_enable(gpio_chip[i].clock); + } __raw_writel(wakeups[i], pio + PIO_IDR); __raw_writel(backups[i], pio + PIO_IER); @@ -335,7 +490,10 @@ void at91_gpio_resume(void) * To use any AT91_PIN_* as an externally triggered IRQ, first call * at91_set_gpio_input() then maybe enable its glitch filter. * Then just request_irq() with the pin ID; it works like any ARM IRQ - * handler, though it always triggers on rising and falling edges. + * handler. + * First implementation always triggers on rising and falling edges + * whereas the newer PIO3 can be additionally configured to trigger on + * level, edge with any polarity. * * Alternatively, certain pins may be used directly as IRQ0..IRQ6 after * configuring them with at91_set_a_periph() or at91_set_b_periph(). @@ -344,9 +502,9 @@ void at91_gpio_resume(void) static void gpio_irq_mask(struct irq_data *d) { - unsigned pin = irq_to_gpio(d->irq); - void __iomem *pio = pin_to_controller(pin); - unsigned mask = pin_to_mask(pin); + struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << d->hwirq; if (pio) __raw_writel(mask, pio + PIO_IDR); @@ -354,9 +512,9 @@ static void gpio_irq_mask(struct irq_data *d) static void gpio_irq_unmask(struct irq_data *d) { - unsigned pin = irq_to_gpio(d->irq); - void __iomem *pio = pin_to_controller(pin); - unsigned mask = pin_to_mask(pin); + struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << d->hwirq; if (pio) __raw_writel(mask, pio + PIO_IER); @@ -373,23 +531,66 @@ static int gpio_irq_type(struct irq_data *d, unsigned type) } } +/* Alternate irq type for PIO3 support */ +static int alt_gpio_irq_type(struct irq_data *d, unsigned type) +{ + struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << d->hwirq; + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + __raw_writel(mask, pio + PIO_ESR); + __raw_writel(mask, pio + PIO_REHLSR); + break; + case IRQ_TYPE_EDGE_FALLING: + __raw_writel(mask, pio + PIO_ESR); + __raw_writel(mask, pio + PIO_FELLSR); + break; + case IRQ_TYPE_LEVEL_LOW: + __raw_writel(mask, pio + PIO_LSR); + __raw_writel(mask, pio + PIO_FELLSR); + break; + case IRQ_TYPE_LEVEL_HIGH: + __raw_writel(mask, pio + PIO_LSR); + __raw_writel(mask, pio + PIO_REHLSR); + break; + case IRQ_TYPE_EDGE_BOTH: + /* + * disable additional interrupt modes: + * fall back to default behavior + */ + __raw_writel(mask, pio + PIO_AIMDR); + return 0; + case IRQ_TYPE_NONE: + default: + pr_warn("AT91: No type for irq %d\n", gpio_to_irq(d->irq)); + return -EINVAL; + } + + /* enable additional interrupt modes */ + __raw_writel(mask, pio + PIO_AIMER); + + return 0; +} + static struct irq_chip gpio_irqchip = { .name = "GPIO", .irq_disable = gpio_irq_mask, .irq_mask = gpio_irq_mask, .irq_unmask = gpio_irq_unmask, - .irq_set_type = gpio_irq_type, + /* .irq_set_type is set dynamically */ .irq_set_wake = gpio_irq_set_wake, }; static void gpio_irq_handler(unsigned irq, struct irq_desc *desc) { - unsigned irq_pin; struct irq_data *idata = irq_desc_get_irq_data(desc); struct irq_chip *chip = irq_data_get_irq_chip(idata); struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(idata); void __iomem *pio = at91_gpio->regbase; - u32 isr; + unsigned long isr; + int n; /* temporarily mask (level sensitive) parent IRQ */ chip->irq_ack(idata); @@ -407,13 +608,10 @@ static void gpio_irq_handler(unsigned irq, struct irq_desc *desc) continue; } - irq_pin = gpio_to_irq(at91_gpio->chip.base); - - while (isr) { - if (isr & 1) - generic_handle_irq(irq_pin); - irq_pin++; - isr >>= 1; + n = find_first_bit(&isr, BITS_PER_LONG); + while (n < BITS_PER_LONG) { + generic_handle_irq(irq_find_mapping(at91_gpio->domain, n)); + n = find_next_bit(&isr, BITS_PER_LONG, n + 1); } } chip->irq_unmask(idata); @@ -424,6 +622,33 @@ static void gpio_irq_handler(unsigned irq, struct irq_desc *desc) #ifdef CONFIG_DEBUG_FS +static void gpio_printf(struct seq_file *s, void __iomem *pio, unsigned mask) +{ + char *trigger = NULL; + char *polarity = NULL; + + if (__raw_readl(pio + PIO_IMR) & mask) { + if (!has_pio3() || !(__raw_readl(pio + PIO_AIMMR) & mask )) { + trigger = "edge"; + polarity = "both"; + } else { + if (__raw_readl(pio + PIO_ELSR) & mask) { + trigger = "level"; + polarity = __raw_readl(pio + PIO_FRLHSR) & mask ? + "high" : "low"; + } else { + trigger = "edge"; + polarity = __raw_readl(pio + PIO_FRLHSR) & mask ? + "rising" : "falling"; + } + } + seq_printf(s, "IRQ:%s-%s\t", trigger, polarity); + } else { + seq_printf(s, "GPIO:%s\t\t", + __raw_readl(pio + PIO_PDSR) & mask ? "1" : "0"); + } +} + static int at91_gpio_show(struct seq_file *s, void *unused) { int bank, j; @@ -431,7 +656,7 @@ static int at91_gpio_show(struct seq_file *s, void *unused) /* print heading */ seq_printf(s, "Pin\t"); for (bank = 0; bank < gpio_banks; bank++) { - seq_printf(s, "PIO%c\t", 'A' + bank); + seq_printf(s, "PIO%c\t\t", 'A' + bank); }; seq_printf(s, "\n\n"); @@ -445,11 +670,10 @@ static int at91_gpio_show(struct seq_file *s, void *unused) unsigned mask = pin_to_mask(pin); if (__raw_readl(pio + PIO_PSR) & mask) - seq_printf(s, "GPIO:%s", __raw_readl(pio + PIO_PDSR) & mask ? "1" : "0"); + gpio_printf(s, pio, mask); else - seq_printf(s, "%s", __raw_readl(pio + PIO_ABSR) & mask ? "B" : "A"); - - seq_printf(s, "\t"); + seq_printf(s, "%c\t\t", + peripheral_function(pio, mask)); } seq_printf(s, "\n"); @@ -488,46 +712,152 @@ postcore_initcall(at91_gpio_debugfs_init); */ static struct lock_class_key gpio_lock_class; +#if defined(CONFIG_OF) +static int at91_gpio_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct at91_gpio_chip *at91_gpio = h->host_data; + + irq_set_lockdep_class(virq, &gpio_lock_class); + + /* + * Can use the "simple" and not "edge" handler since it's + * shorter, and the AIC handles interrupts sanely. + */ + irq_set_chip_and_handler(virq, &gpio_irqchip, + handle_simple_irq); + set_irq_flags(virq, IRQF_VALID); + irq_set_chip_data(virq, at91_gpio); + + return 0; +} + +static struct irq_domain_ops at91_gpio_ops = { + .map = at91_gpio_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +int __init at91_gpio_of_irq_setup(struct device_node *node, + struct device_node *parent) +{ + struct at91_gpio_chip *prev = NULL; + int alias_idx = of_alias_get_id(node, "gpio"); + struct at91_gpio_chip *at91_gpio = &gpio_chip[alias_idx]; + + /* Setup proper .irq_set_type function */ + if (has_pio3()) + gpio_irqchip.irq_set_type = alt_gpio_irq_type; + else + gpio_irqchip.irq_set_type = gpio_irq_type; + + /* Disable irqs of this PIO controller */ |