diff options
author | Vladimir Barinov <vbarinov@ru.mvista.com> | 2007-07-10 13:03:43 +0100 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2007-07-12 09:57:09 +0100 |
commit | 3d9edf09d4525dad95f98b31f31aa86b8071fab9 (patch) | |
tree | e923d8ff8c71e83ed4edf97f7f52a95c34da2683 | |
parent | 3e062b07ada88edb9ffdd147e39c7df4b4418f64 (diff) | |
download | kernel_samsung_smdk4412-3d9edf09d4525dad95f98b31f31aa86b8071fab9.zip kernel_samsung_smdk4412-3d9edf09d4525dad95f98b31f31aa86b8071fab9.tar.gz kernel_samsung_smdk4412-3d9edf09d4525dad95f98b31f31aa86b8071fab9.tar.bz2 |
[ARM] 4457/2: davinci: GPIO support
Support GPIO driver for TI DaVinci SoC
Signed-off-by: Vladimir Barinov <vbarino@ru.mvista.com>
Acked-by: David Brownell <david-b@pacbell.net>
Acked-by: Kevin Hilman <khilman@mvista.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r-- | arch/arm/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-davinci/Makefile | 3 | ||||
-rw-r--r-- | arch/arm/mach-davinci/gpio.c | 286 | ||||
-rw-r--r-- | include/asm-arm/arch-davinci/gpio.h | 156 | ||||
-rw-r--r-- | include/asm-arm/arch-davinci/hardware.h | 38 |
5 files changed, 483 insertions, 1 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 50d9f3e..d0aaecb 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -384,6 +384,7 @@ config ARCH_DAVINCI bool "TI DaVinci" select GENERIC_TIME select GENERIC_CLOCKEVENTS + select GENERIC_GPIO help Support for TI's DaVinci platform. diff --git a/arch/arm/mach-davinci/Makefile b/arch/arm/mach-davinci/Makefile index b86c7f0..731c0a6 100644 --- a/arch/arm/mach-davinci/Makefile +++ b/arch/arm/mach-davinci/Makefile @@ -4,7 +4,8 @@ # # Common objects -obj-y := time.o irq.o clock.o serial.o io.o id.o psc.o +obj-y := time.o irq.o clock.o serial.o io.o id.o psc.o \ + gpio.o # Board specific obj-$(CONFIG_MACH_DAVINCI_EVM) += board-evm.o diff --git a/arch/arm/mach-davinci/gpio.c b/arch/arm/mach-davinci/gpio.c new file mode 100644 index 0000000..9c67886 --- /dev/null +++ b/arch/arm/mach-davinci/gpio.c @@ -0,0 +1,286 @@ +/* + * TI DaVinci GPIO Support + * + * Copyright (c) 2006 David Brownell + * Copyright (c) 2007, MontaVista Software, Inc. <source@mvista.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/bitops.h> + +#include <asm/arch/irqs.h> +#include <asm/arch/hardware.h> +#include <asm/arch/gpio.h> + +#include <asm/mach/irq.h> + +static DEFINE_SPINLOCK(gpio_lock); +static DECLARE_BITMAP(gpio_in_use, DAVINCI_N_GPIO); + +int gpio_request(unsigned gpio, const char *tag) +{ + if (gpio >= DAVINCI_N_GPIO) + return -EINVAL; + + if (test_and_set_bit(gpio, gpio_in_use)) + return -EBUSY; + + return 0; +} +EXPORT_SYMBOL(gpio_request); + +void gpio_free(unsigned gpio) +{ + if (gpio >= DAVINCI_N_GPIO) + return; + + clear_bit(gpio, gpio_in_use); +} +EXPORT_SYMBOL(gpio_free); + +/* create a non-inlined version */ +static struct gpio_controller *__iomem gpio2controller(unsigned gpio) +{ + return __gpio_to_controller(gpio); +} + +/* + * Assuming the pin is muxed as a gpio output, set its output value. + */ +void __gpio_set(unsigned gpio, int value) +{ + struct gpio_controller *__iomem g = gpio2controller(gpio); + + __raw_writel(__gpio_mask(gpio), value ? &g->set_data : &g->clr_data); +} +EXPORT_SYMBOL(__gpio_set); + + +/* + * Read the pin's value (works even if it's set up as output); + * returns zero/nonzero. + * + * Note that changes are synched to the GPIO clock, so reading values back + * right after you've set them may give old values. + */ +int __gpio_get(unsigned gpio) +{ + struct gpio_controller *__iomem g = gpio2controller(gpio); + + return !!(__gpio_mask(gpio) & __raw_readl(&g->in_data)); +} +EXPORT_SYMBOL(__gpio_get); + + +/*--------------------------------------------------------------------------*/ + +/* + * board setup code *MUST* set PINMUX0 and PINMUX1 as + * needed, and enable the GPIO clock. + */ + +int gpio_direction_input(unsigned gpio) +{ + struct gpio_controller *__iomem g = gpio2controller(gpio); + u32 temp; + u32 mask; + + if (!g) + return -EINVAL; + + spin_lock(&gpio_lock); + mask = __gpio_mask(gpio); + temp = __raw_readl(&g->dir); + temp |= mask; + __raw_writel(temp, &g->dir); + spin_unlock(&gpio_lock); + return 0; +} +EXPORT_SYMBOL(gpio_direction_input); + +int gpio_direction_output(unsigned gpio, int value) +{ + struct gpio_controller *__iomem g = gpio2controller(gpio); + u32 temp; + u32 mask; + + if (!g) + return -EINVAL; + + spin_lock(&gpio_lock); + mask = __gpio_mask(gpio); + temp = __raw_readl(&g->dir); + temp &= ~mask; + __raw_writel(mask, value ? &g->set_data : &g->clr_data); + __raw_writel(temp, &g->dir); + spin_unlock(&gpio_lock); + return 0; +} +EXPORT_SYMBOL(gpio_direction_output); + +/* + * We expect irqs will normally be set up as input pins, but they can also be + * used as output pins ... which is convenient for testing. + * + * NOTE: GPIO0..GPIO7 also have direct INTC hookups, which work in addition + * to their GPIOBNK0 irq (but with a bit less overhead). But we don't have + * a good way to hook those up ... + * + * All those INTC hookups (GPIO0..GPIO7 plus five IRQ banks) can also + * serve as EDMA event triggers. + */ + +static void gpio_irq_disable(unsigned irq) +{ + struct gpio_controller *__iomem g = get_irq_chip_data(irq); + u32 mask = __gpio_mask(irq_to_gpio(irq)); + + __raw_writel(mask, &g->clr_falling); + __raw_writel(mask, &g->clr_rising); +} + +static void gpio_irq_enable(unsigned irq) +{ + struct gpio_controller *__iomem g = get_irq_chip_data(irq); + u32 mask = __gpio_mask(irq_to_gpio(irq)); + + if (irq_desc[irq].status & IRQ_TYPE_EDGE_FALLING) + __raw_writel(mask, &g->set_falling); + if (irq_desc[irq].status & IRQ_TYPE_EDGE_RISING) + __raw_writel(mask, &g->set_rising); +} + +static int gpio_irq_type(unsigned irq, unsigned trigger) +{ + struct gpio_controller *__iomem g = get_irq_chip_data(irq); + u32 mask = __gpio_mask(irq_to_gpio(irq)); + + if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) + return -EINVAL; + + irq_desc[irq].status &= ~IRQ_TYPE_SENSE_MASK; + irq_desc[irq].status |= trigger; + + __raw_writel(mask, (trigger & IRQ_TYPE_EDGE_FALLING) + ? &g->set_falling : &g->clr_falling); + __raw_writel(mask, (trigger & IRQ_TYPE_EDGE_RISING) + ? &g->set_rising : &g->clr_rising); + return 0; +} + +static struct irq_chip gpio_irqchip = { + .name = "GPIO", + .enable = gpio_irq_enable, + .disable = gpio_irq_disable, + .set_type = gpio_irq_type, +}; + +static void +gpio_irq_handler(unsigned irq, struct irq_desc *desc) +{ + struct gpio_controller *__iomem g = get_irq_chip_data(irq); + u32 mask = 0xffff; + + /* we only care about one bank */ + if (irq & 1) + mask <<= 16; + + /* temporarily mask (level sensitive) parent IRQ */ + desc->chip->ack(irq); + while (1) { + u32 status; + struct irq_desc *gpio; + int n; + int res; + + /* ack any irqs */ + status = __raw_readl(&g->intstat) & mask; + if (!status) + break; + __raw_writel(status, &g->intstat); + if (irq & 1) + status >>= 16; + + /* now demux them to the right lowlevel handler */ + n = (int)get_irq_data(irq); + gpio = &irq_desc[n]; + while (status) { + res = ffs(status); + n += res; + gpio += res; + desc_handle_irq(n - 1, gpio - 1); + status >>= res; + } + } + desc->chip->unmask(irq); + /* now it may re-trigger */ +} + +/* + * NOTE: for suspend/resume, probably best to make a sysdev (and class) + * with its suspend/resume calls hooking into the results of the set_wake() + * calls ... so if no gpios are wakeup events the clock can be disabled, + * with outputs left at previously set levels, and so that VDD3P3V.IOPWDN0 + * can be set appropriately for GPIOV33 pins. + */ + +static int __init davinci_gpio_irq_setup(void) +{ + unsigned gpio, irq, bank; + struct clk *clk; + + clk = clk_get(NULL, "gpio"); + if (IS_ERR(clk)) { + printk(KERN_ERR "Error %ld getting gpio clock?\n", + PTR_ERR(clk)); + return 0; + } + + clk_enable(clk); + + for (gpio = 0, irq = gpio_to_irq(0), bank = IRQ_GPIOBNK0; + gpio < DAVINCI_N_GPIO; bank++) { + struct gpio_controller *__iomem g = gpio2controller(gpio); + unsigned i; + + __raw_writel(~0, &g->clr_falling); + __raw_writel(~0, &g->clr_rising); + + /* set up all irqs in this bank */ + set_irq_chained_handler(bank, gpio_irq_handler); + set_irq_chip_data(bank, g); + set_irq_data(bank, (void *)irq); + + for (i = 0; i < 16 && gpio < DAVINCI_N_GPIO; + i++, irq++, gpio++) { + set_irq_chip(irq, &gpio_irqchip); + set_irq_chip_data(irq, g); + set_irq_handler(irq, handle_simple_irq); + set_irq_flags(irq, IRQF_VALID); + } + } + + /* BINTEN -- per-bank interrupt enable. genirq would also let these + * bits be set/cleared dynamically. + */ + __raw_writel(0x1f, (void *__iomem) + IO_ADDRESS(DAVINCI_GPIO_BASE + 0x08)); + + printk(KERN_INFO "DaVinci: %d gpio irqs\n", irq - gpio_to_irq(0)); + + return 0; +} + +arch_initcall(davinci_gpio_irq_setup); diff --git a/include/asm-arm/arch-davinci/gpio.h b/include/asm-arm/arch-davinci/gpio.h new file mode 100644 index 0000000..ea24a0e --- /dev/null +++ b/include/asm-arm/arch-davinci/gpio.h @@ -0,0 +1,156 @@ +/* + * TI DaVinci GPIO Support + * + * Copyright (c) 2006 David Brownell + * Copyright (c) 2007, MontaVista Software, Inc. <source@mvista.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __DAVINCI_GPIO_H +#define __DAVINCI_GPIO_H + +/* + * basic gpio routines + * + * board-specific init should be done by arch/.../.../board-XXX.c (maybe + * initializing banks together) rather than boot loaders; kexec() won't + * go through boot loaders. + * + * the gpio clock will be turned on when gpios are used, and you may also + * need to pay attention to PINMUX0 and PINMUX1 to be sure those pins are + * used as gpios, not with other peripherals. + * + * GPIOs are numbered 0..(DAVINCI_N_GPIO-1). For documentation, and maybe + * for later updates, code should write GPIO(N) or: + * - GPIOV18(N) for 1.8V pins, N in 0..53; same as GPIO(0)..GPIO(53) + * - GPIOV33(N) for 3.3V pins, N in 0..17; same as GPIO(54)..GPIO(70) + * + * For GPIO IRQs use gpio_to_irq(GPIO(N)) or gpio_to_irq(GPIOV33(N)) etc + * for now, that's != GPIO(N) + */ +#define GPIO(X) (X) /* 0 <= X <= 70 */ +#define GPIOV18(X) (X) /* 1.8V i/o; 0 <= X <= 53 */ +#define GPIOV33(X) ((X)+54) /* 3.3V i/o; 0 <= X <= 17 */ + +struct gpio_controller { + u32 dir; + u32 out_data; + u32 set_data; + u32 clr_data; + u32 in_data; + u32 set_rising; + u32 clr_rising; + u32 set_falling; + u32 clr_falling; + u32 intstat; +}; + +/* The __gpio_to_controller() and __gpio_mask() functions inline to constants + * with constant parameters; or in outlined code they execute at runtime. + * + * You'd access the controller directly when reading or writing more than + * one gpio value at a time, and to support wired logic where the value + * being driven by the cpu need not match the value read back. + * + * These are NOT part of the cross-platform GPIO interface + */ +static inline struct gpio_controller *__iomem +__gpio_to_controller(unsigned gpio) +{ + void *__iomem ptr; + + if (gpio < 32) + ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x10); + else if (gpio < 64) + ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x38); + else if (gpio < DAVINCI_N_GPIO) + ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x60); + else + ptr = NULL; + return ptr; +} + +static inline u32 __gpio_mask(unsigned gpio) +{ + return 1 << (gpio % 32); +} + +/* The get/set/clear functions will inline when called with constant + * parameters, for low-overhead bitbanging. Illegal constant parameters + * cause link-time errors. + * + * Otherwise, calls with variable parameters use outlined functions. + */ +extern int __error_inval_gpio(void); + +extern void __gpio_set(unsigned gpio, int value); +extern int __gpio_get(unsigned gpio); + +static inline void gpio_set_value(unsigned gpio, int value) +{ + if (__builtin_constant_p(value)) { + struct gpio_controller *__iomem g; + u32 mask; + + if (gpio >= DAVINCI_N_GPIO) + __error_inval_gpio(); + + g = __gpio_to_controller(gpio); + mask = __gpio_mask(gpio); + if (value) + __raw_writel(mask, &g->set_data); + else + __raw_writel(mask, &g->clr_data); + return; + } + + __gpio_set(gpio, value); +} + +/* Returns zero or nonzero; works for gpios configured as inputs OR + * as outputs. + * + * NOTE: changes in reported values are synchronized to the GPIO clock. + * This is most easily seen after calling gpio_set_value() and then immediatly + * gpio_get_value(), where the gpio_get_value() would return the old value + * until the GPIO clock ticks and the new value gets latched. + */ + +static inline int gpio_get_value(unsigned gpio) +{ + struct gpio_controller *__iomem g; + + if (!__builtin_constant_p(gpio)) + return __gpio_get(gpio); + + if (gpio >= DAVINCI_N_GPIO) + return __error_inval_gpio(); + + g = __gpio_to_controller(gpio); + return !!(__gpio_mask(gpio) & __raw_readl(&g->in_data)); +} + +/* powerup default direction is IN */ +extern int gpio_direction_input(unsigned gpio); +extern int gpio_direction_output(unsigned gpio, int value); + +#include <asm-generic/gpio.h> /* cansleep wrappers */ + +extern int gpio_request(unsigned gpio, const char *tag); +extern void gpio_free(unsigned gpio); + +static inline int gpio_to_irq(unsigned gpio) +{ + return DAVINCI_N_AINTC_IRQ + gpio; +} + +static inline int irq_to_gpio(unsigned irq) +{ + return irq - DAVINCI_N_AINTC_IRQ; +} + +#endif /* __DAVINCI_GPIO_H */ diff --git a/include/asm-arm/arch-davinci/hardware.h b/include/asm-arm/arch-davinci/hardware.h index 60362d8..a2e8969 100644 --- a/include/asm-arm/arch-davinci/hardware.h +++ b/include/asm-arm/arch-davinci/hardware.h @@ -11,4 +11,42 @@ #ifndef __ASM_ARCH_HARDWARE_H #define __ASM_ARCH_HARDWARE_H +/* + * Base register addresses + */ +#define DAVINCI_DMA_3PCC_BASE (0x01C00000) +#define DAVINCI_DMA_3PTC0_BASE (0x01C10000) +#define DAVINCI_DMA_3PTC1_BASE (0x01C10400) +#define DAVINCI_I2C_BASE (0x01C21000) +#define DAVINCI_PWM0_BASE (0x01C22000) +#define DAVINCI_PWM1_BASE (0x01C22400) +#define DAVINCI_PWM2_BASE (0x01C22800) +#define DAVINCI_SYSTEM_MODULE_BASE (0x01C40000) +#define DAVINCI_PLL_CNTRL0_BASE (0x01C40800) +#define DAVINCI_PLL_CNTRL1_BASE (0x01C40C00) +#define DAVINCI_PWR_SLEEP_CNTRL_BASE (0x01C41000) +#define DAVINCI_SYSTEM_DFT_BASE (0x01C42000) +#define DAVINCI_IEEE1394_BASE (0x01C60000) +#define DAVINCI_USB_OTG_BASE (0x01C64000) +#define DAVINCI_CFC_ATA_BASE (0x01C66000) +#define DAVINCI_SPI_BASE (0x01C66800) +#define DAVINCI_GPIO_BASE (0x01C67000) +#define DAVINCI_UHPI_BASE (0x01C67800) +#define DAVINCI_VPSS_REGS_BASE (0x01C70000) +#define DAVINCI_EMAC_CNTRL_REGS_BASE (0x01C80000) +#define DAVINCI_EMAC_WRAPPER_CNTRL_REGS_BASE (0x01C81000) +#define DAVINCI_EMAC_WRAPPER_RAM_BASE (0x01C82000) +#define DAVINCI_MDIO_CNTRL_REGS_BASE (0x01C84000) +#define DAVINCI_IMCOP_BASE (0x01CC0000) +#define DAVINCI_ASYNC_EMIF_CNTRL_BASE (0x01E00000) +#define DAVINCI_VLYNQ_BASE (0x01E01000) +#define DAVINCI_MCBSP_BASE (0x01E02000) +#define DAVINCI_MMC_SD_BASE (0x01E10000) +#define DAVINCI_MS_BASE (0x01E20000) +#define DAVINCI_ASYNC_EMIF_DATA_CE0_BASE (0x02000000) +#define DAVINCI_ASYNC_EMIF_DATA_CE1_BASE (0x04000000) +#define DAVINCI_ASYNC_EMIF_DATA_CE2_BASE (0x06000000) +#define DAVINCI_ASYNC_EMIF_DATA_CE3_BASE (0x08000000) +#define DAVINCI_VLYNQ_REMOTE_BASE (0x0C000000) + #endif /* __ASM_ARCH_HARDWARE_H */ |