diff options
author | Russell King <rmk@dyn-67.arm.linux.org.uk> | 2008-12-20 18:40:03 +0100 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2008-12-20 18:40:03 +0100 |
commit | a663a71e429d7d06a7773edc4092995c5fe1171f (patch) | |
tree | 3a9e4b3a00754c8d4207fd6e4c310e53879d9772 /arch/arm | |
parent | Merge branch 'for-rmk' of git://git.pengutronix.de/git/ukl/linux-2.6 into devel (diff) | |
parent | [ARM] mv78xx0: implement GPIO and GPIO interrupt support (diff) | |
download | linux-a663a71e429d7d06a7773edc4092995c5fe1171f.tar.xz linux-a663a71e429d7d06a7773edc4092995c5fe1171f.zip |
Merge git://git.marvell.com/orion into devel
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/Kconfig | 2 | ||||
-rw-r--r-- | arch/arm/mach-kirkwood/include/mach/gpio.h | 38 | ||||
-rw-r--r-- | arch/arm/mach-kirkwood/include/mach/irqs.h | 4 | ||||
-rw-r--r-- | arch/arm/mach-kirkwood/include/mach/kirkwood.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-kirkwood/irq.c | 35 | ||||
-rw-r--r-- | arch/arm/mach-mv78xx0/include/mach/gpio.h | 40 | ||||
-rw-r--r-- | arch/arm/mach-mv78xx0/include/mach/irqs.h | 4 | ||||
-rw-r--r-- | arch/arm/mach-mv78xx0/include/mach/mv78xx0.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-mv78xx0/irq.c | 29 | ||||
-rw-r--r-- | arch/arm/mach-orion5x/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-orion5x/common.h | 7 | ||||
-rw-r--r-- | arch/arm/mach-orion5x/gpio.c | 231 | ||||
-rw-r--r-- | arch/arm/mach-orion5x/include/mach/gpio.h | 29 | ||||
-rw-r--r-- | arch/arm/mach-orion5x/include/mach/irqs.h | 4 | ||||
-rw-r--r-- | arch/arm/mach-orion5x/include/mach/orion5x.h | 9 | ||||
-rw-r--r-- | arch/arm/mach-orion5x/irq.c | 183 | ||||
-rw-r--r-- | arch/arm/mach-orion5x/mpp.c | 6 | ||||
-rw-r--r-- | arch/arm/plat-orion/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/plat-orion/gpio.c | 415 | ||||
-rw-r--r-- | arch/arm/plat-orion/include/plat/gpio.h | 39 |
20 files changed, 642 insertions, 443 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 4f870207f496..d6ebe39934b5 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -387,6 +387,7 @@ config ARCH_KIRKWOOD bool "Marvell Kirkwood" select CPU_FEROCEON select PCI + select GENERIC_GPIO select GENERIC_TIME select GENERIC_CLOCKEVENTS select PLAT_ORION @@ -429,6 +430,7 @@ config ARCH_MV78XX0 bool "Marvell MV78xx0" select CPU_FEROCEON select PCI + select GENERIC_GPIO select GENERIC_TIME select GENERIC_CLOCKEVENTS select PLAT_ORION diff --git a/arch/arm/mach-kirkwood/include/mach/gpio.h b/arch/arm/mach-kirkwood/include/mach/gpio.h new file mode 100644 index 000000000000..81b335eb62ec --- /dev/null +++ b/arch/arm/mach-kirkwood/include/mach/gpio.h @@ -0,0 +1,38 @@ +/* + * arch/asm-arm/mach-kirkwood/include/mach/gpio.h + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __ASM_ARCH_GPIO_H +#define __ASM_ARCH_GPIO_H + +#include <mach/irqs.h> +#include <plat/gpio.h> +#include <asm-generic/gpio.h> /* cansleep wrappers */ + +#define GPIO_MAX 50 +#define GPIO_OFF(pin) (((pin) >> 5) ? 0x0140 : 0x0100) +#define GPIO_OUT(pin) (DEV_BUS_VIRT_BASE + GPIO_OFF(pin) + 0x00) +#define GPIO_IO_CONF(pin) (DEV_BUS_VIRT_BASE + GPIO_OFF(pin) + 0x04) +#define GPIO_BLINK_EN(pin) (DEV_BUS_VIRT_BASE + GPIO_OFF(pin) + 0x08) +#define GPIO_IN_POL(pin) (DEV_BUS_VIRT_BASE + GPIO_OFF(pin) + 0x0c) +#define GPIO_DATA_IN(pin) (DEV_BUS_VIRT_BASE + GPIO_OFF(pin) + 0x10) +#define GPIO_EDGE_CAUSE(pin) (DEV_BUS_VIRT_BASE + GPIO_OFF(pin) + 0x14) +#define GPIO_EDGE_MASK(pin) (DEV_BUS_VIRT_BASE + GPIO_OFF(pin) + 0x18) +#define GPIO_LEVEL_MASK(pin) (DEV_BUS_VIRT_BASE + GPIO_OFF(pin) + 0x1c) + +static inline int gpio_to_irq(int pin) +{ + return pin + IRQ_KIRKWOOD_GPIO_START; +} + +static inline int irq_to_gpio(int irq) +{ + return irq - IRQ_KIRKWOOD_GPIO_START; +} + + +#endif diff --git a/arch/arm/mach-kirkwood/include/mach/irqs.h b/arch/arm/mach-kirkwood/include/mach/irqs.h index 3a964bb14973..f00a0a45a67e 100644 --- a/arch/arm/mach-kirkwood/include/mach/irqs.h +++ b/arch/arm/mach-kirkwood/include/mach/irqs.h @@ -11,8 +11,6 @@ #ifndef __ASM_ARCH_IRQS_H #define __ASM_ARCH_IRQS_H -#include "kirkwood.h" /* need GPIO_MAX */ - /* * Low Interrupt Controller */ @@ -57,7 +55,7 @@ * KIRKWOOD General Purpose Pins */ #define IRQ_KIRKWOOD_GPIO_START 64 -#define NR_GPIO_IRQS GPIO_MAX +#define NR_GPIO_IRQS 50 #define NR_IRQS (IRQ_KIRKWOOD_GPIO_START + NR_GPIO_IRQS) diff --git a/arch/arm/mach-kirkwood/include/mach/kirkwood.h b/arch/arm/mach-kirkwood/include/mach/kirkwood.h index eae42406fd86..ada480c0e197 100644 --- a/arch/arm/mach-kirkwood/include/mach/kirkwood.h +++ b/arch/arm/mach-kirkwood/include/mach/kirkwood.h @@ -117,7 +117,4 @@ #define SATA_PHYS_BASE (KIRKWOOD_REGS_PHYS_BASE | 0x80000) -#define GPIO_MAX 50 - - #endif diff --git a/arch/arm/mach-kirkwood/irq.c b/arch/arm/mach-kirkwood/irq.c index 5790643ffe07..efb86b700276 100644 --- a/arch/arm/mach-kirkwood/irq.c +++ b/arch/arm/mach-kirkwood/irq.c @@ -13,10 +13,45 @@ #include <linux/irq.h> #include <linux/io.h> #include <plat/irq.h> +#include <asm/gpio.h> #include "common.h" +static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + BUG_ON(irq < IRQ_KIRKWOOD_GPIO_LOW_0_7); + BUG_ON(irq > IRQ_KIRKWOOD_GPIO_HIGH_16_23); + + orion_gpio_irq_handler((irq - IRQ_KIRKWOOD_GPIO_LOW_0_7) << 3); +} + void __init kirkwood_init_irq(void) { + int i; + orion_irq_init(0, (void __iomem *)(IRQ_VIRT_BASE + IRQ_MASK_LOW_OFF)); orion_irq_init(32, (void __iomem *)(IRQ_VIRT_BASE + IRQ_MASK_HIGH_OFF)); + + /* + * Mask and clear GPIO IRQ interrupts. + */ + writel(0, GPIO_LEVEL_MASK(0)); + writel(0, GPIO_EDGE_MASK(0)); + writel(0, GPIO_EDGE_CAUSE(0)); + writel(0, GPIO_LEVEL_MASK(32)); + writel(0, GPIO_EDGE_MASK(32)); + writel(0, GPIO_EDGE_CAUSE(32)); + + for (i = IRQ_KIRKWOOD_GPIO_START; i < NR_IRQS; i++) { + set_irq_chip(i, &orion_gpio_irq_level_chip); + set_irq_handler(i, handle_level_irq); + irq_desc[i].status |= IRQ_LEVEL; + set_irq_flags(i, IRQF_VALID); + } + set_irq_chained_handler(IRQ_KIRKWOOD_GPIO_LOW_0_7, gpio_irq_handler); + set_irq_chained_handler(IRQ_KIRKWOOD_GPIO_LOW_8_15, gpio_irq_handler); + set_irq_chained_handler(IRQ_KIRKWOOD_GPIO_LOW_16_23, gpio_irq_handler); + set_irq_chained_handler(IRQ_KIRKWOOD_GPIO_LOW_24_31, gpio_irq_handler); + set_irq_chained_handler(IRQ_KIRKWOOD_GPIO_HIGH_0_7, gpio_irq_handler); + set_irq_chained_handler(IRQ_KIRKWOOD_GPIO_HIGH_8_15, gpio_irq_handler); + set_irq_chained_handler(IRQ_KIRKWOOD_GPIO_HIGH_16_23, gpio_irq_handler); } diff --git a/arch/arm/mach-mv78xx0/include/mach/gpio.h b/arch/arm/mach-mv78xx0/include/mach/gpio.h new file mode 100644 index 000000000000..d9d1535ea100 --- /dev/null +++ b/arch/arm/mach-mv78xx0/include/mach/gpio.h @@ -0,0 +1,40 @@ +/* + * arch/asm-arm/mach-mv78xx0/include/mach/gpio.h + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __ASM_ARCH_GPIO_H +#define __ASM_ARCH_GPIO_H + +#include <mach/irqs.h> +#include <plat/gpio.h> +#include <asm-generic/gpio.h> /* cansleep wrappers */ + +extern int mv78xx0_core_index(void); + +#define GPIO_MAX 32 +#define GPIO_OUT(pin) (DEV_BUS_VIRT_BASE + 0x0100) +#define GPIO_IO_CONF(pin) (DEV_BUS_VIRT_BASE + 0x0104) +#define GPIO_BLINK_EN(pin) (DEV_BUS_VIRT_BASE + 0x0108) +#define GPIO_IN_POL(pin) (DEV_BUS_VIRT_BASE + 0x010c) +#define GPIO_DATA_IN(pin) (DEV_BUS_VIRT_BASE + 0x0110) +#define GPIO_EDGE_CAUSE(pin) (DEV_BUS_VIRT_BASE + 0x0114) +#define GPIO_MASK_OFF (mv78xx0_core_index() ? 0x18 : 0) +#define GPIO_EDGE_MASK(pin) (DEV_BUS_VIRT_BASE + 0x0118 + GPIO_MASK_OFF) +#define GPIO_LEVEL_MASK(pin) (DEV_BUS_VIRT_BASE + 0x011c + GPIO_MASK_OFF) + +static inline int gpio_to_irq(int pin) +{ + return pin + IRQ_MV78XX0_GPIO_START; +} + +static inline int irq_to_gpio(int irq) +{ + return irq - IRQ_MV78XX0_GPIO_START; +} + + +#endif diff --git a/arch/arm/mach-mv78xx0/include/mach/irqs.h b/arch/arm/mach-mv78xx0/include/mach/irqs.h index bebc330281ec..fa1d422196c2 100644 --- a/arch/arm/mach-mv78xx0/include/mach/irqs.h +++ b/arch/arm/mach-mv78xx0/include/mach/irqs.h @@ -11,8 +11,6 @@ #ifndef __ASM_ARCH_IRQS_H #define __ASM_ARCH_IRQS_H -#include "mv78xx0.h" /* need GPIO_MAX */ - /* * MV78xx0 Low Interrupt Controller */ @@ -88,7 +86,7 @@ * MV78XX0 General Purpose Pins */ #define IRQ_MV78XX0_GPIO_START 96 -#define NR_GPIO_IRQS GPIO_MAX +#define NR_GPIO_IRQS 32 #define NR_IRQS (IRQ_MV78XX0_GPIO_START + NR_GPIO_IRQS) diff --git a/arch/arm/mach-mv78xx0/include/mach/mv78xx0.h b/arch/arm/mach-mv78xx0/include/mach/mv78xx0.h index ee9c5593ee92..e930ea5330a2 100644 --- a/arch/arm/mach-mv78xx0/include/mach/mv78xx0.h +++ b/arch/arm/mach-mv78xx0/include/mach/mv78xx0.h @@ -122,7 +122,4 @@ #define SATA_PHYS_BASE (MV78XX0_REGS_PHYS_BASE | 0xa0000) -#define GPIO_MAX 32 - - #endif diff --git a/arch/arm/mach-mv78xx0/irq.c b/arch/arm/mach-mv78xx0/irq.c index 503e5d195ae5..e273418797b4 100644 --- a/arch/arm/mach-mv78xx0/irq.c +++ b/arch/arm/mach-mv78xx0/irq.c @@ -11,13 +11,42 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/pci.h> +#include <linux/irq.h> +#include <asm/gpio.h> #include <mach/mv78xx0.h> #include <plat/irq.h> #include "common.h" +static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + BUG_ON(irq < IRQ_MV78XX0_GPIO_0_7 || irq > IRQ_MV78XX0_GPIO_24_31); + + orion_gpio_irq_handler((irq - IRQ_MV78XX0_GPIO_0_7) << 3); +} + void __init mv78xx0_init_irq(void) { + int i; + orion_irq_init(0, (void __iomem *)(IRQ_VIRT_BASE + IRQ_MASK_LOW_OFF)); orion_irq_init(32, (void __iomem *)(IRQ_VIRT_BASE + IRQ_MASK_HIGH_OFF)); orion_irq_init(64, (void __iomem *)(IRQ_VIRT_BASE + IRQ_MASK_ERR_OFF)); + + /* + * Mask and clear GPIO IRQ interrupts. + */ + writel(0, GPIO_LEVEL_MASK(0)); + writel(0, GPIO_EDGE_MASK(0)); + writel(0, GPIO_EDGE_CAUSE(0)); + + for (i = IRQ_MV78XX0_GPIO_START; i < NR_IRQS; i++) { + set_irq_chip(i, &orion_gpio_irq_level_chip); + set_irq_handler(i, handle_level_irq); + irq_desc[i].status |= IRQ_LEVEL; + set_irq_flags(i, IRQF_VALID); + } + set_irq_chained_handler(IRQ_MV78XX0_GPIO_0_7, gpio_irq_handler); + set_irq_chained_handler(IRQ_MV78XX0_GPIO_8_15, gpio_irq_handler); + set_irq_chained_handler(IRQ_MV78XX0_GPIO_16_23, gpio_irq_handler); + set_irq_chained_handler(IRQ_MV78XX0_GPIO_24_31, gpio_irq_handler); } diff --git a/arch/arm/mach-orion5x/Makefile b/arch/arm/mach-orion5x/Makefile index 3d4a1bc12355..edc38e2c856f 100644 --- a/arch/arm/mach-orion5x/Makefile +++ b/arch/arm/mach-orion5x/Makefile @@ -1,4 +1,4 @@ -obj-y += common.o addr-map.o pci.o gpio.o irq.o mpp.o +obj-y += common.o addr-map.o pci.o irq.o mpp.o obj-$(CONFIG_MACH_DB88F5281) += db88f5281-setup.o obj-$(CONFIG_MACH_RD88F5182) += rd88f5182-setup.o obj-$(CONFIG_MACH_KUROBOX_PRO) += kurobox_pro-setup.o diff --git a/arch/arm/mach-orion5x/common.h b/arch/arm/mach-orion5x/common.h index a000c7c6ee96..798b9a5e3da9 100644 --- a/arch/arm/mach-orion5x/common.h +++ b/arch/arm/mach-orion5x/common.h @@ -51,13 +51,6 @@ int orion5x_pci_sys_setup(int nr, struct pci_sys_data *sys); struct pci_bus *orion5x_pci_sys_scan_bus(int nr, struct pci_sys_data *sys); int orion5x_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin); -/* - * Valid GPIO pins according to MPP setup, used by machine-setup. - * (/mach-orion/gpio.c). - */ -void orion5x_gpio_set_valid(unsigned pin, int valid); -void gpio_display(void); /* debug */ - struct machine_desc; struct meminfo; struct tag; diff --git a/arch/arm/mach-orion5x/gpio.c b/arch/arm/mach-orion5x/gpio.c deleted file mode 100644 index f99d08811e5a..000000000000 --- a/arch/arm/mach-orion5x/gpio.c +++ /dev/null @@ -1,231 +0,0 @@ -/* - * arch/arm/mach-orion5x/gpio.c - * - * GPIO functions for Marvell Orion System On Chip - * - * Maintainer: Tzachi Perelstein <tzachi@marvell.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/spinlock.h> -#include <linux/bitops.h> -#include <linux/io.h> -#include <asm/gpio.h> -#include <mach/orion5x.h> -#include "common.h" - -static DEFINE_SPINLOCK(gpio_lock); -static unsigned long gpio_valid[BITS_TO_LONGS(GPIO_MAX)]; -static const char *gpio_label[GPIO_MAX]; /* non null for allocated GPIOs */ - -void __init orion5x_gpio_set_valid(unsigned pin, int valid) -{ - if (valid) - __set_bit(pin, gpio_valid); - else - __clear_bit(pin, gpio_valid); -} - -/* - * GENERIC_GPIO primitives - */ -int gpio_direction_input(unsigned pin) -{ - unsigned long flags; - - if (pin >= GPIO_MAX || !test_bit(pin, gpio_valid)) { - pr_debug("%s: invalid GPIO %d\n", __func__, pin); - return -EINVAL; - } - - spin_lock_irqsave(&gpio_lock, flags); - - /* - * Some callers might have not used the gpio_request(), - * so flag this pin as requested now. - */ - if (!gpio_label[pin]) - gpio_label[pin] = "?"; - - orion5x_setbits(GPIO_IO_CONF, 1 << pin); - - spin_unlock_irqrestore(&gpio_lock, flags); - return 0; -} -EXPORT_SYMBOL(gpio_direction_input); - -int gpio_direction_output(unsigned pin, int value) -{ - unsigned long flags; - int mask; - - if (pin >= GPIO_MAX || !test_bit(pin, gpio_valid)) { - pr_debug("%s: invalid GPIO %d\n", __func__, pin); - return -EINVAL; - } - - spin_lock_irqsave(&gpio_lock, flags); - - /* - * Some callers might have not used the gpio_request(), - * so flag this pin as requested now. - */ - if (!gpio_label[pin]) - gpio_label[pin] = "?"; - - mask = 1 << pin; - orion5x_clrbits(GPIO_BLINK_EN, mask); - if (value) - orion5x_setbits(GPIO_OUT, mask); - else - orion5x_clrbits(GPIO_OUT, mask); - orion5x_clrbits(GPIO_IO_CONF, mask); - - spin_unlock_irqrestore(&gpio_lock, flags); - return 0; -} -EXPORT_SYMBOL(gpio_direction_output); - -int gpio_get_value(unsigned pin) -{ - int val, mask = 1 << pin; - - if (readl(GPIO_IO_CONF) & mask) - val = readl(GPIO_DATA_IN) ^ readl(GPIO_IN_POL); - else - val = readl(GPIO_OUT); - - return val & mask; -} -EXPORT_SYMBOL(gpio_get_value); - -void gpio_set_value(unsigned pin, int value) -{ - unsigned long flags; - int mask = 1 << pin; - - spin_lock_irqsave(&gpio_lock, flags); - - orion5x_clrbits(GPIO_BLINK_EN, mask); - if (value) - orion5x_setbits(GPIO_OUT, mask); - else - orion5x_clrbits(GPIO_OUT, mask); - - spin_unlock_irqrestore(&gpio_lock, flags); -} -EXPORT_SYMBOL(gpio_set_value); - -void orion5x_gpio_set_blink(unsigned pin, int blink) -{ - unsigned long flags; - int mask = 1 << pin; - - spin_lock_irqsave(&gpio_lock, flags); - - orion5x_clrbits(GPIO_OUT, mask); - if (blink) - orion5x_setbits(GPIO_BLINK_EN, mask); - else - orion5x_clrbits(GPIO_BLINK_EN, mask); - - spin_unlock_irqrestore(&gpio_lock, flags); -} -EXPORT_SYMBOL(orion5x_gpio_set_blink); - -int gpio_request(unsigned pin, const char *label) -{ - int ret = 0; - unsigned long flags; - - if (pin >= GPIO_MAX || !test_bit(pin, gpio_valid)) { - pr_debug("%s: invalid GPIO %d\n", __func__, pin); - return -EINVAL; - } - - spin_lock_irqsave(&gpio_lock, flags); - - if (gpio_label[pin]) { - pr_debug("%s: GPIO %d already used as %s\n", - __func__, pin, gpio_label[pin]); - ret = -EBUSY; - } else - gpio_label[pin] = label ? label : "?"; - - spin_unlock_irqrestore(&gpio_lock, flags); - return ret; -} -EXPORT_SYMBOL(gpio_request); - -void gpio_free(unsigned pin) -{ - might_sleep(); - - if (pin >= GPIO_MAX || !test_bit(pin, gpio_valid)) { - pr_debug("%s: invalid GPIO %d\n", __func__, pin); - return; - } - - if (!gpio_label[pin]) - pr_warning("%s: GPIO %d already freed\n", __func__, pin); - else - gpio_label[pin] = NULL; -} -EXPORT_SYMBOL(gpio_free); - -/* Debug helper */ -void gpio_display(void) -{ - int i; - - for (i = 0; i < GPIO_MAX; i++) { - printk(KERN_DEBUG "Pin-%d: ", i); - - if (!test_bit(i, gpio_valid)) { - printk("non-GPIO\n"); - } else if (!gpio_label[i]) { - printk("GPIO, free\n"); - } else { - printk("GPIO, used by %s, ", gpio_label[i]); - if (readl(GPIO_IO_CONF) & (1 << i)) { - printk("input, active %s, level %s, edge %s\n", - ((readl(GPIO_IN_POL) >> i) & 1) ? "low" : "high", - ((readl(GPIO_LEVEL_MASK) >> i) & 1) ? "enabled" : "masked", - ((readl(GPIO_EDGE_MASK) >> i) & 1) ? "enabled" : "masked"); - } else { - printk("output, val=%d\n", (readl(GPIO_OUT) >> i) & 1); - } - } - } - - printk(KERN_DEBUG "MPP_0_7_CTRL (0x%08x) = 0x%08x\n", - MPP_0_7_CTRL, readl(MPP_0_7_CTRL)); - printk(KERN_DEBUG "MPP_8_15_CTRL (0x%08x) = 0x%08x\n", - MPP_8_15_CTRL, readl(MPP_8_15_CTRL)); - printk(KERN_DEBUG "MPP_16_19_CTRL (0x%08x) = 0x%08x\n", - MPP_16_19_CTRL, readl(MPP_16_19_CTRL)); - printk(KERN_DEBUG "MPP_DEV_CTRL (0x%08x) = 0x%08x\n", - MPP_DEV_CTRL, readl(MPP_DEV_CTRL)); - printk(KERN_DEBUG "GPIO_OUT (0x%08x) = 0x%08x\n", - GPIO_OUT, readl(GPIO_OUT)); - printk(KERN_DEBUG "GPIO_IO_CONF (0x%08x) = 0x%08x\n", - GPIO_IO_CONF, readl(GPIO_IO_CONF)); - printk(KERN_DEBUG "GPIO_BLINK_EN (0x%08x) = 0x%08x\n", - GPIO_BLINK_EN, readl(GPIO_BLINK_EN)); - printk(KERN_DEBUG "GPIO_IN_POL (0x%08x) = 0x%08x\n", - GPIO_IN_POL, readl(GPIO_IN_POL)); - printk(KERN_DEBUG "GPIO_DATA_IN (0x%08x) = 0x%08x\n", - GPIO_DATA_IN, readl(GPIO_DATA_IN)); - printk(KERN_DEBUG "GPIO_LEVEL_MASK (0x%08x) = 0x%08x\n", - GPIO_LEVEL_MASK, readl(GPIO_LEVEL_MASK)); - printk(KERN_DEBUG "GPIO_EDGE_CAUSE (0x%08x) = 0x%08x\n", - GPIO_EDGE_CAUSE, readl(GPIO_EDGE_CAUSE)); - printk(KERN_DEBUG "GPIO_EDGE_MASK (0x%08x) = 0x%08x\n", - GPIO_EDGE_MASK, readl(GPIO_EDGE_MASK)); -} diff --git a/arch/arm/mach-orion5x/include/mach/gpio.h b/arch/arm/mach-orion5x/include/mach/gpio.h index 65dc136a86f7..d8182e87ac16 100644 --- a/arch/arm/mach-orion5x/include/mach/gpio.h +++ b/arch/arm/mach-orion5x/include/mach/gpio.h @@ -2,18 +2,26 @@ * arch/arm/mach-orion5x/include/mach/gpio.h * * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any + * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ -extern int gpio_request(unsigned pin, const char *label); -extern void gpio_free(unsigned pin); -extern int gpio_direction_input(unsigned pin); -extern int gpio_direction_output(unsigned pin, int value); -extern int gpio_get_value(unsigned pin); -extern void gpio_set_value(unsigned pin, int value); -extern void orion5x_gpio_set_blink(unsigned pin, int blink); -extern void gpio_display(void); /* debug */ +#ifndef __ASM_ARCH_GPIO_H +#define __ASM_ARCH_GPIO_H + +#include <mach/irqs.h> +#include <plat/gpio.h> +#include <asm-generic/gpio.h> /* cansleep wrappers */ + +#define GPIO_MAX 32 +#define GPIO_OUT(pin) ORION5X_DEV_BUS_REG(0x100) +#define GPIO_IO_CONF(pin) ORION5X_DEV_BUS_REG(0x104) +#define GPIO_BLINK_EN(pin) ORION5X_DEV_BUS_REG(0x108) +#define GPIO_IN_POL(pin) ORION5X_DEV_BUS_REG(0x10c) +#define GPIO_DATA_IN(pin) ORION5X_DEV_BUS_REG(0x110) +#define GPIO_EDGE_CAUSE(pin) ORION5X_DEV_BUS_REG(0x114) +#define GPIO_EDGE_MASK(pin) ORION5X_DEV_BUS_REG(0x118) +#define GPIO_LEVEL_MASK(pin) ORION5X_DEV_BUS_REG(0x11c) static inline int gpio_to_irq(int pin) { @@ -25,4 +33,5 @@ static inline int irq_to_gpio(int irq) return irq - IRQ_ORION5X_GPIO_START; } -#include <asm-generic/gpio.h> /* cansleep wrappers */ + +#endif diff --git a/arch/arm/mach-orion5x/include/mach/irqs.h b/arch/arm/mach-orion5x/include/mach/irqs.h index d5b0fbf6b965..a6fa9d8f12d8 100644 --- a/arch/arm/mach-orion5x/include/mach/irqs.h +++ b/arch/arm/mach-orion5x/include/mach/irqs.h @@ -13,8 +13,6 @@ #ifndef __ASM_ARCH_IRQS_H #define __ASM_ARCH_IRQS_H -#include "orion5x.h" /* need GPIO_MAX */ - /* * Orion Main Interrupt Controller */ @@ -54,7 +52,7 @@ * Orion General Purpose Pins */ #define IRQ_ORION5X_GPIO_START 32 -#define NR_GPIO_IRQS GPIO_MAX +#define NR_GPIO_IRQS 32 #define NR_IRQS (IRQ_ORION5X_GPIO_START + NR_GPIO_IRQS) diff --git a/arch/arm/mach-orion5x/include/mach/orion5x.h b/arch/arm/mach-orion5x/include/mach/orion5x.h index 9f5ce1ce5840..67bda31406dd 100644 --- a/arch/arm/mach-orion5x/include/mach/orion5x.h +++ b/arch/arm/mach-orion5x/include/mach/orion5x.h @@ -134,14 +134,6 @@ #define MPP_16_19_CTRL ORION5X_DEV_BUS_REG(0x050) #define MPP_DEV_CTRL ORION5X_DEV_BUS_REG(0x008) #define MPP_RESET_SAMPLE ORION5X_DEV_BUS_REG(0x010) -#define GPIO_OUT ORION5X_DEV_BUS_REG(0x100) -#define GPIO_IO_CONF ORION5X_DEV_BUS_REG(0x104) -#define GPIO_BLINK_EN ORION5X_DEV_BUS_REG(0x108) -#define GPIO_IN_POL ORION5X_DEV_BUS_REG(0x10c) -#define GPIO_DATA_IN ORION5X_DEV_BUS_REG(0x110) -#define GPIO_EDGE_CAUSE ORION5X_DEV_BUS_REG(0x114) -#define GPIO_EDGE_MASK ORION5X_DEV_BUS_REG(0x118) -#define GPIO_LEVEL_MASK ORION5X_DEV_BUS_REG(0x11c) #define DEV_BANK_0_PARAM ORION5X_DEV_BUS_REG(0x45c) #define DEV_BANK_1_PARAM ORION5X_DEV_BUS_REG(0x460) #define DEV_BANK_2_PARAM ORION5X_DEV_BUS_REG(0x464) @@ -149,7 +141,6 @@ #define DEV_BUS_CTRL ORION5X_DEV_BUS_REG(0x4c0) #define DEV_BUS_INT_CAUSE ORION5X_DEV_BUS_REG(0x4d0) #define DEV_BUS_INT_MASK ORION5X_DEV_BUS_REG(0x4d4) -#define GPIO_MAX 32 /*************************************************************************** * Orion CPU Bridge Registers diff --git a/arch/arm/mach-orion5x/irq.c b/arch/arm/mach-orion5x/irq.c index 632a36f5cf14..0caae43301e5 100644 --- a/arch/arm/mach-orion5x/irq.c +++ b/arch/arm/mach-orion5x/irq.c @@ -19,193 +19,38 @@ #include <plat/irq.h> #include "common.h" -/***************************************************************************** - * Orion GPIO IRQ - * - * GPIO_IN_POL register controlls whether GPIO_DATA_IN will hold the same - * value of the line or the opposite value. - * - * Level IRQ handlers: DATA_IN is used directly as cause register. - * Interrupt are masked by LEVEL_MASK registers. - * Edge IRQ handlers: Change in DATA_IN are latched in EDGE_CAUSE. - * Interrupt are masked by EDGE_MASK registers. - * Both-edge handlers: Similar to regular Edge handlers, but also swaps - * the polarity to catch the next line transaction. - * This is a race condition that might not perfectly - * work on some use cases. - * - * Every eight GPIO lines are grouped (OR'ed) before going up to main - * cause register. - * - * EDGE cause mask - * data-in /--------| |-----| |----\ - * -----| |----- ---- to main cause reg - * X \----------------| |----/ - * polarity LEVEL mask - * - ****************************************************************************/ -static void orion5x_gpio_irq_ack(u32 irq) -{ - int pin = irq_to_gpio(irq); - if (irq_desc[irq].status & IRQ_LEVEL) - /* - * Mask bit for level interrupt - */ - orion5x_clrbits(GPIO_LEVEL_MASK, 1 << pin); - else - /* - * Clear casue bit for egde interrupt - */ - orion5x_clrbits(GPIO_EDGE_CAUSE, 1 << pin); -} - -static void orion5x_gpio_irq_mask(u32 irq) -{ - int pin = irq_to_gpio(irq); - if (irq_desc[irq].status & IRQ_LEVEL) - orion5x_clrbits(GPIO_LEVEL_MASK, 1 << pin); - else - orion5x_clrbits(GPIO_EDGE_MASK, 1 << pin); -} - -static void orion5x_gpio_irq_unmask(u32 irq) +static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) { - int pin = irq_to_gpio(irq); - if (irq_desc[irq].status & IRQ_LEVEL) - orion5x_setbits(GPIO_LEVEL_MASK, 1 << pin); - else - orion5x_setbits(GPIO_EDGE_MASK, 1 << pin); -} - -static int orion5x_gpio_set_irq_type(u32 irq, u32 type) -{ - int pin = irq_to_gpio(irq); - struct irq_desc *desc; - - if ((readl(GPIO_IO_CONF) & (1 << pin)) == 0) { - printk(KERN_ERR "orion5x_gpio_set_irq_type failed " - "(irq %d, pin %d).\n", irq, pin); - return -EINVAL; - } - - desc = irq_desc + irq; - - switch (type) { - case IRQ_TYPE_LEVEL_HIGH: - desc->handle_irq = handle_level_irq; - desc->status |= IRQ_LEVEL; - orion5x_clrbits(GPIO_IN_POL, (1 << pin)); - break; - case IRQ_TYPE_LEVEL_LOW: - desc->handle_irq = handle_level_irq; - desc->status |= IRQ_LEVEL; - orion5x_setbits(GPIO_IN_POL, (1 << pin)); - break; - case IRQ_TYPE_EDGE_RISING: - desc->handle_irq = handle_edge_irq; - desc->status &= ~IRQ_LEVEL; - orion5x_clrbits(GPIO_IN_POL, (1 << pin)); - break; - case IRQ_TYPE_EDGE_FALLING: - desc->handle_irq = handle_edge_irq; - desc->status &= ~IRQ_LEVEL; - orion5x_setbits(GPIO_IN_POL, (1 << pin)); - break; - case IRQ_TYPE_EDGE_BOTH: - desc->handle_irq = handle_edge_irq; - desc->status &= ~IRQ_LEVEL; - /* - * set initial polarity based on current input level - */ - if ((readl(GPIO_IN_POL) ^ readl(GPIO_DATA_IN)) - & (1 << pin)) - orion5x_setbits(GPIO_IN_POL, (1 << pin)); /* falling */ - else - orion5x_clrbits(GPIO_IN_POL, (1 << pin)); /* rising */ - - break; - default: - printk(KERN_ERR "failed to set irq=%d (type=%d)\n", irq, type); - return -EINVAL; - } - - desc->status &= ~IRQ_TYPE_SENSE_MASK; - desc->status |= type & IRQ_TYPE_SENSE_MASK; - - return 0; -} - -static struct irq_chip orion5x_gpio_irq_chip = { - .name = "Orion-IRQ-GPIO", - .ack = orion5x_gpio_irq_ack, - .mask = orion5x_gpio_irq_mask, - .unmask = orion5x_gpio_irq_unmask, - .set_type = orion5x_gpio_set_irq_type, -}; - -static void orion5x_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) -{ - u32 cause, offs, pin; - BUG_ON(irq < IRQ_ORION5X_GPIO_0_7 || irq > IRQ_ORION5X_GPIO_24_31); - offs = (irq - IRQ_ORION5X_GPIO_0_7) * 8; - cause = (readl(GPIO_DATA_IN) & readl(GPIO_LEVEL_MASK)) | - (readl(GPIO_EDGE_CAUSE) & readl(GPIO_EDGE_MASK)); - for (pin = offs; pin < offs + 8; pin++) { - if (cause & (1 << pin)) { - irq = gpio_to_irq(pin); - desc = irq_desc + irq; - if ((desc->status & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) { - /* Swap polarity (race with GPIO line) */ - u32 polarity = readl(GPIO_IN_POL); - polarity ^= 1 << pin; - writel(polarity, GPIO_IN_POL); - } - generic_handle_irq(irq); - } - } + orion_gpio_irq_handler((irq - IRQ_ORION5X_GPIO_0_7) << 3); } -static void __init orion5x_init_gpio_irq(void) +void __init orion5x_init_irq(void) { int i; - struct irq_desc *desc; + + orion_irq_init(0, (void __iomem *)MAIN_IRQ_MASK); /* * Mask and clear GPIO IRQ interrupts */ - writel(0x0, GPIO_LEVEL_MASK); - writel(0x0, GPIO_EDGE_MASK); - writel(0x0, GPIO_EDGE_CAUSE); + writel(0x0, GPIO_LEVEL_MASK(0)); + writel(0x0, GPIO_EDGE_MASK(0)); + writel(0x0, GPIO_EDGE_CAUSE(0)); /* * Register chained level handlers for GPIO IRQs by default. * User can use set_type() if he wants to use edge types handlers. */ for (i = IRQ_ORION5X_GPIO_START; i < NR_IRQS; i++) { - set_irq_chip(i, &orion5x_gpio_irq_chip); + set_irq_chip(i, &orion_gpio_irq_level_chip); set_irq_handler(i, handle_level_irq); - desc = irq_desc + i; - desc->status |= IRQ_LEVEL; + irq_desc[i].status |= IRQ_LEVEL; set_irq_flags(i, IRQF_VALID); } - set_irq_chained_handler(IRQ_ORION5X_GPIO_0_7, orion5x_gpio_irq_handler); - set_irq_chained_handler(IRQ_ORION5X_GPIO_8_15, orion5x_gpio_irq_handler); - set_irq_chained_handler(IRQ_ORION5X_GPIO_16_23, orion5x_gpio_irq_handler); - set_irq_chained_handler(IRQ_ORION5X_GPIO_24_31, orion5x_gpio_irq_handler); -} - -/***************************************************************************** - * Orion Main IRQ - ****************************************************************************/ -static void __init orion5x_init_main_irq(void) -{ - orion_irq_init(0, (void __iomem *)MAIN_IRQ_MASK); -} - -void __init orion5x_init_irq(void) -{ - orion5x_init_main_irq(); - orion5x_init_gpio_irq(); + set_irq_chained_handler(IRQ_ORION5X_GPIO_0_7, gpio_irq_handler); + set_irq_chained_handler(IRQ_ORION5X_GPIO_8_15, gpio_irq_handler); + set_irq_chained_handler(IRQ_ORION5X_GPIO_16_23, gpio_irq_handler); + set_irq_chained_handler(IRQ_ORION5X_GPIO_24_31, gpio_irq_handler); } diff --git a/arch/arm/mach-orion5x/mpp.c b/arch/arm/mach-orion5x/mpp.c index 640ea2a3fc6c..e23a3f91d6c6 100644 --- a/arch/arm/mach-orion5x/mpp.c +++ b/arch/arm/mach-orion5x/mpp.c @@ -12,6 +12,7 @@ #include <linux/init.h> #include <linux/mbus.h> #include <linux/io.h> +#include <asm/gpio.h> #include <mach/hardware.h> #include "common.h" #include "mpp.h" @@ -152,7 +153,10 @@ void __init orion5x_mpp_conf(struct orion5x_mpp_mode *mode) *reg &= ~(0xf << shift); *reg |= (num_type & 0xf) << shift; - orion5x_gpio_set_valid(mode->mpp, !!(mode->type == MPP_GPIO)); + if (mode->type == MPP_UNUSED && (mode->mpp < 16 || is_5182())) + orion_gpio_set_unused(mode->mpp); + + orion_gpio_set_valid(mode->mpp, !!(mode->type == MPP_GPIO)); mode++; } diff --git a/arch/arm/plat-orion/Makefile b/arch/arm/plat-orion/Makefile index 198f3dde2be3..56021a72e10c 100644 --- a/arch/arm/plat-orion/Makefile +++ b/arch/arm/plat-orion/Makefile @@ -6,3 +6,5 @@ obj-y := irq.o pcie.o time.o obj-m := obj-n := obj- := + +obj-$(CONFIG_GENERIC_GPIO) += gpio.o diff --git a/arch/arm/plat-orion/gpio.c b/arch/arm/plat-orion/gpio.c new file mode 100644 index 000000000000..967186425ca1 --- /dev/null +++ b/arch/arm/plat-orion/gpio.c @@ -0,0 +1,415 @@ +/* + * arch/arm/plat-orion/gpio.c + * + * Marvell Orion SoC GPIO handling. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/bitops.h> +#include <linux/io.h> +#include <asm/gpio.h> + +static DEFINE_SPINLOCK(gpio_lock); +static const char *gpio_label[GPIO_MAX]; /* non null for allocated GPIOs */ +static unsigned long gpio_valid[BITS_TO_LONGS(GPIO_MAX)]; + +static inline void __set_direction(unsigned pin, int input) +{ + u32 u; + + u = readl(GPIO_IO_CONF(pin)); + if (input) + u |= 1 << (pin & 31); + else + u &= ~(1 << (pin & 31)); + writel(u, GPIO_IO_CONF(pin)); +} + +static void __set_level(unsigned pin, int high) +{ + u32 u; + + u = readl(GPIO_OUT(pin)); + if (high) + u |= 1 << (pin & 31); + else + u &= ~(1 << (pin & 31)); + writel(u, GPIO_OUT(pin)); +} + + +/* + * GENERIC_GPIO primitives. + */ +int gpio_direction_input(unsigned pin) +{ + unsigned long flags; + + if (pin >= GPIO_MAX || !test_bit(pin, gpio_valid)) { + pr_debug("%s: invalid GPIO %d\n", __func__, pin); + return -EINVAL; + } + + spin_lock_irqsave(&gpio_lock, flags); + + /* + * Some callers might not have used gpio_request(), + * so flag this pin as requested now. + */ + if (gpio_label[pin] == NULL) + gpio_label[pin] = "?"; + + /* + * Configure GPIO direction. + */ + __set_direction(pin, 1); + + spin_unlock_irqrestore(&gpio_lock, flags); + + return 0; +} +EXPORT_SYMBOL(gpio_direction_input); + +int gpio_direction_output(unsigned pin, int value) +{ + unsigned long flags; + u32 u; + + if (pin >= GPIO_MAX || !test_bit(pin, gpio_valid)) { + pr_debug("%s: invalid GPIO %d\n", __func__, pin); + return -EINVAL; + } + + spin_lock_irqsave(&gpio_lock, flags); + + /* + * Some callers might not have used gpio_request(), + * so flag this pin as requested now. + */ + if (gpio_label[pin] == NULL) + gpio_label[pin] = "?"; + + /* + * Disable blinking. + */ + u = readl(GPIO_BLINK_EN(pin)); + u &= ~(1 << (pin & 31)); + writel(u, GPIO_BLINK_EN(pin)); + + /* + * Configure GPIO output value. + */ + __set_level(pin, value); + + /* + * Configure GPIO direction. + */ + __set_direction(pin, 0); + + spin_unlock_irqrestore(&gpio_lock, flags); + + return 0; +} +EXPORT_SYMBOL(gpio_direction_output); + +int gpio_get_value(unsigned pin) +{ + int val; + + if (readl(GPIO_IO_CONF(pin)) & (1 << (pin & 31))) + val = readl(GPIO_DATA_IN(pin)) ^ readl(GPIO_IN_POL(pin)); + else + val = readl(GPIO_OUT(pin)); + + return (val >> (pin & 31)) & 1; +} +EXPORT_SYMBOL(gpio_get_value); + +void gpio_set_value(unsigned pin, int value) +{ + unsigned long flags; + u32 u; + + spin_lock_irqsave(&gpio_lock, flags); + + /* + * Disable blinking. + */ + u = readl(GPIO_BLINK_EN(pin)); + u &= ~(1 << (pin & 31)); + writel(u, GPIO_BLINK_EN(pin)); + + /* + * Configure GPIO output value. + */ + __set_level(pin, value); + + spin_unlock_irqrestore(&gpio_lock, flags); +} +EXPORT_SYMBOL(gpio_set_value); + +int gpio_request(unsigned pin, const char *label) +{ + unsigned long flags; + int ret; + + if (pin >= GPIO_MAX || !test_bit(pin, gpio_valid)) { + pr_debug("%s: invalid GPIO %d\n", __func__, pin); + return -EINVAL; + } + + spin_lock_irqsave(&gpio_lock, flags); + if (gpio_label[pin] == NULL) { + gpio_label[pin] = label ? label : "?"; + ret = 0; + } else { + pr_debug("%s: GPIO %d already used as %s\n", + __func__, pin, gpio_label[pin]); + ret = -EBUSY; + } + spin_unlock_irqrestore(&gpio_lock, flags); + + return ret; +} +EXPORT_SYMBOL(gpio_request); + +void gpio_free(unsigned pin) +{ + if (pin >= GPIO_MAX || !test_bit(pin, gpio_valid)) { + pr_debug("%s: invalid GPIO %d\n", __func__, pin); + return; + } + + if (gpio_label[pin] == NULL) + pr_warning("%s: GPIO %d already freed\n", __func__, pin); + else + gpio_label[pin] = NULL; +} +EXPORT_SYMBOL(gpio_free); + + +/* + * Orion-specific GPIO API extensions. + */ +void __init orion_gpio_set_unused(unsigned pin) +{ + /* + * Configure as output, drive low. + */ + __set_level(pin, 0); + __set_direction(pin, 0); +} + +void __init orion_gpio_set_valid(unsigned pin, int valid) +{ + if (valid) + __set_bit(pin, gpio_valid); + else + __clear_bit(pin, gpio_valid); +} + +void orion_gpio_set_blink(unsigned pin, int blink) +{ + unsigned long flags; + u32 u; + + spin_lock_irqsave(&gpio_lock, flags); + + /* + * Set output value to zero. + */ + __set_level(pin, 0); + + u = readl(GPIO_BLINK_EN(pin)); + if (blink) + u |= 1 << (pin & 31); + else + u &= ~(1 << (pin & 31)); + writel(u, GPIO_BLINK_EN(pin)); + + spin_unlock_irqrestore(&gpio_lock, flags); +} +EXPORT_SYMBOL(orion_gpio_set_blink); + + +/***************************************************************************** + * Orion GPIO IRQ + * + * GPIO_IN_POL register controls whether GPIO_DATA_IN will hold the same + * value of the line or the opposite value. + * + * Level IRQ handlers: DATA_IN is used directly as cause register. + * Interrupt are masked by LEVEL_MASK registers. + * Edge IRQ handlers: Change in DATA_IN are latched in EDGE_CAUSE. + * Interrupt are masked by EDGE_MASK registers. + * Both-edge handlers: Similar to regular Edge handlers, but also swaps + * the polarity to catch the next line transaction. + * This is a race condition that might not perfectly + * work on some use cases. + * + * Every eight GPIO lines are grouped (OR'ed) before going up to main + * cause register. + * + * EDGE cause mask + * data-in /--------| |-----| |----\ + * -----| |----- ---- to main cause reg + * X \----------------| |----/ + * polarity LEVEL mask + * + ****************************************************************************/ +static void gpio_irq_edge_ack(u32 irq) +{ + int pin = irq_to_gpio(irq); + + writel(~(1 << (pin & 31)), GPIO_EDGE_CAUSE(pin)); +} + +static void gpio_irq_edge_mask(u32 irq) +{ + int pin = irq_to_gpio(irq); + u32 u; + + u = readl(GPIO_EDGE_MASK(pin)); + u &= ~(1 << (pin & 31)); + writel(u, GPIO_EDGE_MASK(pin)); +} + +static void gpio_irq_edge_unmask(u32 irq) +{ + int pin = irq_to_gpio(irq); + u32 u; + + u = readl(GPIO_EDGE_MASK(pin)); + u |= 1 << (pin & 31); + writel(u, GPIO_EDGE_MASK(pin)); +} + +static void gpio_irq_level_mask(u32 irq) +{ + int pin = irq_to_gpio(irq); + u32 u; + + u = readl(GPIO_LEVEL_MASK(pin)); + u &= ~(1 << (pin & 31)); + writel(u, GPIO_LEVEL_MASK(pin)); +} + +static void gpio_irq_level_unmask(u32 irq) +{ + int pin = irq_to_gpio(irq); + u32 u; + + u = readl(GPIO_LEVEL_MASK(pin)); + u |= 1 << (pin & 31); + writel(u, GPIO_LEVEL_MASK(pin)); +} + +static int gpio_irq_set_type(u32 irq, u32 type) +{ + int pin = irq_to_gpio(irq); + struct irq_desc *desc; + u32 u; + + u = readl(GPIO_IO_CONF(pin)) & (1 << (pin & 31)); + if (!u) { + printk(KERN_ERR "orion gpio_irq_set_type failed " + "(irq %d, pin %d).\n", irq, pin); + return -EINVAL; + } + + desc = irq_desc + irq; + + /* + * Set edge/level type. + */ + if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) { + desc->chip = &orion_gpio_irq_edge_chip; + } else if (type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { + desc->chip = &orion_gpio_irq_level_chip; + } else { + printk(KERN_ERR "failed to set irq=%d (type=%d)\n", irq, type); + return -EINVAL; + } + + /* + * Configure interrupt polarity. + */ + if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH) { + u = readl(GPIO_IN_POL(pin)); + u &= ~(1 << (pin & 31)); + writel(u, GPIO_IN_POL(pin)); + } else if (type == IRQ_TYPE_EDGE_FALLING || type == IRQ_TYPE_LEVEL_LOW) { + u = readl(GPIO_IN_POL(pin)); + u |= 1 << (pin & 31); + writel(u, GPIO_IN_POL(pin)); + } else if (type == IRQ_TYPE_EDGE_BOTH) { + u32 v; + + v = readl(GPIO_IN_POL(pin)) ^ readl(GPIO_DATA_IN(pin)); + + /* + * set initial polarity based on current input level + */ + u = readl(GPIO_IN_POL(pin)); + if (v & (1 << (pin & 31))) + u |= 1 << (pin & 31); /* falling */ + else + u &= ~(1 << (pin & 31)); /* rising */ + writel(u, GPIO_IN_POL(pin)); + } + + desc->status = (desc->status & ~IRQ_TYPE_SENSE_MASK) | type; + + return 0; +} + +struct irq_chip orion_gpio_irq_edge_chip = { + .name = "orion_gpio_irq_edge", + .ack = gpio_irq_edge_ack, + .mask = gpio_irq_edge_mask, + .unmask = gpio_irq_edge_unmask, + .set_type = gpio_irq_set_type, +}; + +struct irq_chip orion_gpio_irq_level_chip = { + .name = "orion_gpio_irq_level", + .mask = gpio_irq_level_mask, + .mask_ack = gpio_irq_level_mask, + .unmask = gpio_irq_level_unmask, + .set_type = gpio_irq_set_type, +}; + +void orion_gpio_irq_handler(int pinoff) +{ + u32 cause; + int pin; + + cause = readl(GPIO_DATA_IN(pinoff)) & readl(GPIO_LEVEL_MASK(pinoff)); + cause |= readl(GPIO_EDGE_CAUSE(pinoff)) & readl(GPIO_EDGE_MASK(pinoff)); + + for (pin = pinoff; pin < pinoff + 8; pin++) { + int irq = gpio_to_irq(pin); + struct irq_desc *desc = irq_desc + irq; + + if (!(cause & (1 << (pin & 31)))) + continue; + + if ((desc->status & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) { + /* Swap polarity (race with GPIO line) */ + u32 polarity; + + polarity = readl(GPIO_IN_POL(pin)); + polarity ^= 1 << (pin & 31); + writel(polarity, GPIO_IN_POL(pin)); + } + desc_handle_irq(irq, desc); + } +} diff --git a/arch/arm/plat-orion/include/plat/gpio.h b/arch/arm/plat-orion/include/plat/gpio.h new file mode 100644 index 000000000000..54deaf274b52 --- /dev/null +++ b/arch/arm/plat-orion/include/plat/gpio.h @@ -0,0 +1,39 @@ +/* + * arch/arm/plat-orion/include/plat/gpio.h + * + * Marvell Orion SoC GPIO handling. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __PLAT_GPIO_H +#define __PLAT_GPIO_H + +/* + * GENERIC_GPIO primitives. + */ +int gpio_request(unsigned pin, const char *label); +void gpio_free(unsigned pin); +int gpio_direction_input(unsigned pin); +int gpio_direction_output(unsigned pin, int value); +int gpio_get_value(unsigned pin); +void gpio_set_value(unsigned pin, int value); + +/* + * Orion-specific GPIO API extensions. + */ +void orion_gpio_set_unused(unsigned pin); +void orion_gpio_set_valid(unsigned pin, int valid); +void orion_gpio_set_blink(unsigned pin, int blink); + +/* + * GPIO interrupt handling. + */ +extern struct irq_chip orion_gpio_irq_edge_chip; +extern struct irq_chip orion_gpio_irq_level_chip; +void orion_gpio_irq_handler(int irqoff); + + +#endif |