This patch adds support for handling OMAP1 specific GPIO operations including gpio_init Signed-off-by: Charulatha V <charu@xxxxxx> --- arch/arm/mach-omap1/gpio.c | 404 ++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 404 insertions(+), 0 deletions(-) create mode 100644 arch/arm/mach-omap1/gpio.c diff --git a/arch/arm/mach-omap1/gpio.c b/arch/arm/mach-omap1/gpio.c new file mode 100644 index 0000000..e54ce07 --- /dev/null +++ b/arch/arm/mach-omap1/gpio.c @@ -0,0 +1,404 @@ +/* + * gpio.c - OMAP1 architecture specific common gpio code + * + * Copyright (C) 2010 Texas Instruments, Inc. + * + * Author: + * Charulatha V <charu@xxxxxx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <plat/gpio.h> + +static struct gpio_bank omap1610_gpio_bank[5] = { + { OMAP1_MPUIO_VBASE, NULL, INT_MPUIO, IH_MPUIO_BASE, + METHOD_MPUIO }, + { OMAP1610_GPIO1_BASE, NULL, INT_GPIO_BANK1, IH_GPIO_BASE, + METHOD_GPIO_1610 }, + { OMAP1610_GPIO2_BASE, NULL, INT_1610_GPIO_BANK2, IH_GPIO_BASE + 16, + METHOD_GPIO_1610 }, + { OMAP1610_GPIO3_BASE, NULL, INT_1610_GPIO_BANK3, IH_GPIO_BASE + 32, + METHOD_GPIO_1610 }, + { OMAP1610_GPIO4_BASE, NULL, INT_1610_GPIO_BANK4, IH_GPIO_BASE + 48, + METHOD_GPIO_1610 }, +}; + +static struct gpio_bank omap1510_gpio_bank[2] = { + { OMAP1_MPUIO_VBASE, NULL, INT_MPUIO, IH_MPUIO_BASE, + METHOD_MPUIO }, + { OMAP1510_GPIO_BASE, NULL, INT_GPIO_BANK1, IH_GPIO_BASE, + METHOD_GPIO_1510 } +}; + +static struct gpio_bank omap7xx_gpio_bank[7] = { + { OMAP1_MPUIO_VBASE, NULL, INT_7XX_MPUIO, IH_MPUIO_BASE, + METHOD_MPUIO }, + { OMAP7XX_GPIO1_BASE, NULL, INT_7XX_GPIO_BANK1, IH_GPIO_BASE, + METHOD_GPIO_7XX }, + { OMAP7XX_GPIO2_BASE, NULL, INT_7XX_GPIO_BANK2, IH_GPIO_BASE + 32, + METHOD_GPIO_7XX }, + { OMAP7XX_GPIO3_BASE, NULL, INT_7XX_GPIO_BANK3, IH_GPIO_BASE + 64, + METHOD_GPIO_7XX }, + { OMAP7XX_GPIO4_BASE, NULL, INT_7XX_GPIO_BANK4, IH_GPIO_BASE + 96, + METHOD_GPIO_7XX }, + { OMAP7XX_GPIO5_BASE, NULL, INT_7XX_GPIO_BANK5, IH_GPIO_BASE + 128, + METHOD_GPIO_7XX }, + { OMAP7XX_GPIO6_BASE, NULL, INT_7XX_GPIO_BANK6, IH_GPIO_BASE + 160, + METHOD_GPIO_7XX }, +}; + +static struct gpio_reg_offset omap1610_gpio_reg = { + .data_in = OMAP1610_GPIO_DATAIN, + .data_out = OMAP1610_GPIO_DATAOUT, + .data_out_set = OMAP1610_GPIO_SET_DATAOUT, + .data_out_clear = OMAP1610_GPIO_CLEAR_DATAOUT, + .dir_ctrl = OMAP1610_GPIO_DIRECTION, + .irq_status0 = OMAP1610_GPIO_IRQSTATUS1, + .irq_mask = OMAP1610_GPIO_IRQENABLE1, + .irq_set = OMAP1610_GPIO_SET_IRQENABLE1, + .irq_clear = OMAP1610_GPIO_CLEAR_IRQENABLE1, + .irq_mask_bits = 0xffff, + .irq_inv = 0, + .wkup_enable = OMAP1610_GPIO_WAKEUPENABLE, + .wkup_clear = OMAP1610_GPIO_CLEAR_WAKEUPENA, + .wkup_set = OMAP1610_GPIO_SET_WAKEUPENA, + .syscfg = OMAP1610_GPIO_SYSCONFIG, +}; + +static struct gpio_reg_offset omap1510_gpio_reg = { + .data_in = OMAP1510_GPIO_DATA_INPUT, + .data_out = OMAP1510_GPIO_DATA_OUTPUT, + .dir_ctrl = OMAP1510_GPIO_DIR_CONTROL, + .irq_status0 = OMAP1510_GPIO_INT_STATUS, + .irq_mask = OMAP1510_GPIO_INT_MASK, + .irq_mask_bits = 0xffff, + .irq_inv = 1, + .ctrl = OMAP1510_GPIO_PIN_CONTROL, +}; + +static struct gpio_reg_offset omap7xx_gpio_reg = { + .data_in = OMAP7XX_GPIO_DATA_INPUT, + .data_out = OMAP7XX_GPIO_DATA_OUTPUT, + .dir_ctrl = OMAP7XX_GPIO_DIR_CONTROL, + .irq_status0 = OMAP7XX_GPIO_INT_STATUS, + .irq_mask = OMAP7XX_GPIO_INT_MASK, + .irq_mask_bits = 0xffffffff, + .irq_inv = 1, +}; + +static struct mpu_gpio_reg_off omap1_gpio_mpureg = { + .mpu_io_ctrl = OMAP_MPUIO_IO_CNTL, + .mpu_data_out = OMAP_MPUIO_OUTPUT, + .mpu_data_in = OMAP_MPUIO_INPUT_LATCH, + .mpu_isr = OMAP_MPUIO_GPIO_INT, + .mpu_irq_mask = OMAP_MPUIO_GPIO_MASKIT, + .mpu_irq_mask_bits = 0xffff, + .mpu_irq_inv = 1, +}; + +static struct gpio_bank *omap1_get_gpio_bank(int gpio, + struct gpio_bank *gpio_bank) +{ + if (cpu_is_omap15xx()) { + if (OMAP_GPIO_IS_MPUIO(gpio)) + return &gpio_bank[0]; + return &gpio_bank[1]; + } else if (cpu_is_omap16xx()) { + if (OMAP_GPIO_IS_MPUIO(gpio)) + return &gpio_bank[0]; + return &gpio_bank[1 + (gpio >> 4)]; + } else if (cpu_is_omap7xx()) { + if (OMAP_GPIO_IS_MPUIO(gpio)) + return &gpio_bank[0]; + return &gpio_bank[1 + (gpio >> 5)]; + } + BUG(); + return NULL; +} + +/* + * This only applies to chips that can't do both rising and falling edge + * detection at once. For all other chips, this function is a noop. + */ +static void omap1_toggle_edge_triggering(struct gpio_bank *bank, int gpio) +{ + void __iomem *reg = bank->base; + u32 l = 0; + + switch (bank->method) { + case METHOD_MPUIO: + reg += OMAP_MPUIO_GPIO_INT_EDGE; + break; + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_INT_CONTROL; + break; + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_INT_CONTROL; + break; + default: + return; + } + + l = __raw_readl(reg); + if ((l >> gpio) & 1) + l &= ~(1 << gpio); + else + l |= 1 << gpio; + + __raw_writel(l, reg); +} + +static int set_omap1_gpio_triggering(struct gpio_bank *bank, int gpio, + int trigger) +{ + void __iomem *reg = bank->base; + u32 l = 0; + + switch (bank->method) { + case METHOD_MPUIO: + reg += OMAP_MPUIO_GPIO_INT_EDGE; + l = __raw_readl(reg); + if (trigger & IRQ_TYPE_EDGE_BOTH) + bank->toggle_mask |= 1 << gpio; + if (trigger & IRQ_TYPE_EDGE_RISING) + l |= 1 << gpio; + else if (trigger & IRQ_TYPE_EDGE_FALLING) + l &= ~(1 << gpio); + else + goto bad; + break; + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_INT_CONTROL; + l = __raw_readl(reg); + if (trigger & IRQ_TYPE_EDGE_BOTH) + bank->toggle_mask |= 1 << gpio; + if (trigger & IRQ_TYPE_EDGE_RISING) + l |= 1 << gpio; + else if (trigger & IRQ_TYPE_EDGE_FALLING) + l &= ~(1 << gpio); + else + goto bad; + break; + case METHOD_GPIO_1610: + if (gpio & 0x08) + reg += OMAP1610_GPIO_EDGE_CTRL2; + else + reg += OMAP1610_GPIO_EDGE_CTRL1; + gpio &= 0x07; + l = __raw_readl(reg); + l &= ~(3 << (gpio << 1)); + if (trigger & IRQ_TYPE_EDGE_RISING) + l |= 2 << (gpio << 1); + if (trigger & IRQ_TYPE_EDGE_FALLING) + l |= 1 << (gpio << 1); + if (trigger) + /* Enable wake-up during idle for dynamic tick */ + __raw_writel(1 << gpio, bank->base + + OMAP1610_GPIO_SET_WAKEUPENA); + else + __raw_writel(1 << gpio, bank->base + + OMAP1610_GPIO_CLEAR_WAKEUPENA); + break; + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_INT_CONTROL; + l = __raw_readl(reg); + if (trigger & IRQ_TYPE_EDGE_BOTH) + bank->toggle_mask |= 1 << gpio; + if (trigger & IRQ_TYPE_EDGE_RISING) + l |= 1 << gpio; + else if (trigger & IRQ_TYPE_EDGE_FALLING) + l &= ~(1 << gpio); + else + goto bad; + break; + default: + goto bad; + } + __raw_writel(l, reg); + return 0; +bad: + return -EINVAL; +} + +#ifdef CONFIG_ARCH_OMAP16XX +static int omap_mpuio_suspend_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gpio_bank *bank = platform_get_drvdata(pdev); + void __iomem *mask_reg = bank->base + OMAP_MPUIO_GPIO_MASKIT; + unsigned long flags; + + spin_lock_irqsave(&bank->lock, flags); + bank->saved_wakeup = __raw_readl(mask_reg); + __raw_writel(0xffff & ~bank->suspend_wakeup, mask_reg); + spin_unlock_irqrestore(&bank->lock, flags); + + return 0; +} + +static int omap_mpuio_resume_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gpio_bank *bank = platform_get_drvdata(pdev); + void __iomem *mask_reg = bank->base + OMAP_MPUIO_GPIO_MASKIT; + unsigned long flags; + + spin_lock_irqsave(&bank->lock, flags); + __raw_writel(bank->saved_wakeup, mask_reg); + spin_unlock_irqrestore(&bank->lock, flags); + + return 0; +} + +static const struct dev_pm_ops omap_mpuio_dev_pm_ops = { + .suspend_noirq = omap_mpuio_suspend_noirq, + .resume_noirq = omap_mpuio_resume_noirq, +}; + +/* use platform_driver for this, now that there's no longer any + * point to sys_device (other than not disturbing old code). + */ +static struct platform_driver omap_mpuio_driver = { + .driver = { + .name = "mpuio", + .pm = &omap_mpuio_dev_pm_ops, + }, +}; + +static struct platform_device omap_mpuio_device = { + .name = "mpuio", + .id = -1, + .dev = { + .driver = &omap_mpuio_driver.driver, + } + /* could list the /proc/iomem resources */ +}; + +void mpuio_init(void) +{ + platform_set_drvdata(&omap_mpuio_device, &omap1610_gpio_bank[0]); + + if (platform_driver_register(&omap_mpuio_driver) == 0) + (void) platform_device_register(&omap_mpuio_device); +} +#else +static struct platform_device omap_mpuio_device; +void mpuio_init(void) {} +#endif + +/* + * OMAP1 GPIO function pointers, reg offset pointer and other info + */ +static struct omap_gpio_info omap1_gpio_data = { + .get_gpio_bank = omap1_get_gpio_bank, + .set_gpio_triggering = set_omap1_gpio_triggering, + .toggle_edge_triggering = omap1_toggle_edge_triggering, + .mpu_reg = &omap1_gpio_mpureg, +}; + +static void __init omap1_init_gpio(struct gpio_bank *gpio_bank) +{ + int i; + struct gpio_bank *bank; + + if (cpu_is_omap15xx()) { + struct clk *gpio_ick; + gpio_ick = clk_get(NULL, "arm_gpio_ck"); + if (IS_ERR(gpio_ick)) + printk(KERN_ERR "Could not get arm_gpio_ck\n"); + else + clk_enable(gpio_ick); + } + + for (i = 0; i < omap1_gpio_data.bank_count; i++) { + bank = &gpio_bank[i]; + spin_lock_init(&bank->lock); + + /* Static mapping, never released */ + bank->base = ioremap(bank->pbase, SZ_2K); + if (!bank->base) { + printk(KERN_ERR "Could not ioremap gpio bank%i\n", i); + continue; + } + } + + if (bank_is_mpuio(bank)) + __raw_writew(0xffff, bank->base + OMAP_MPUIO_GPIO_MASKIT); + else if (cpu_is_omap15xx() && bank->method == METHOD_GPIO_1510) { + __raw_writew(0xffff, bank->base + OMAP1510_GPIO_INT_MASK); + __raw_writew(0x0000, bank->base + OMAP1510_GPIO_INT_STATUS); + } else if (cpu_is_omap16xx() && bank->method == METHOD_GPIO_1610) { + __raw_writew(0x0000, bank->base + OMAP1610_GPIO_IRQENABLE1); + __raw_writew(0xffff, bank->base + OMAP1610_GPIO_IRQSTATUS1); + __raw_writew(0x0014, bank->base + OMAP1610_GPIO_SYSCONFIG); + } else if (cpu_is_omap7xx() && bank->method == METHOD_GPIO_7XX) { + __raw_writel(0xffffffff, bank->base + OMAP7XX_GPIO_INT_MASK); + __raw_writel(0x00000000, bank->base + OMAP7XX_GPIO_INT_STATUS); + } + + if (cpu_is_omap16xx()) { + u32 rev; + /* Enable system clock for GPIO module. + * The CAM_CLK_CTRL *is* really the right place. + */ + bank->chip.dev = &omap_mpuio_device.dev; + if (bank_is_mpuio(bank)) + omap_writel(omap_readl(ULPD_CAM_CLK_CTRL) | + 0x04, ULPD_CAM_CLK_CTRL); + rev = __raw_readw(gpio_bank[1].base + OMAP1610_GPIO_REVISION); + printk(KERN_INFO "OMAP GPIO hardware version %d.%d\n", + (rev >> 4) & 0x0f, rev & 0x0f); + } +} + +static void __init omap1_gpio_init(void) +{ + if (cpu_is_omap16xx()) { + omap1_gpio_data.reg_off = &omap1610_gpio_reg; + omap1_gpio_data.index_mask = 0x0f; + omap1_gpio_data.no_of_gpio = 64; + omap1_gpio_data.bank_count = 5; + omap1_gpio_data.bank_bits = 16; + omap1_gpio_data.gpio_bank = omap1610_gpio_bank; + } else if (cpu_is_omap15xx()) { + omap1_gpio_data.reg_off = &omap1510_gpio_reg; + omap1_gpio_data.index_mask = 0x0f; + omap1_gpio_data.no_of_gpio = 16; + omap1_gpio_data.bank_count = 2; + omap1_gpio_data.bank_bits = 16; + omap1_gpio_data.gpio_bank = omap1510_gpio_bank; + } else if (cpu_is_omap7xx()) { + omap1_gpio_data.reg_off = &omap7xx_gpio_reg; + omap1_gpio_data.index_mask = 0x1f; + omap1_gpio_data.no_of_gpio = 192; + omap1_gpio_data.bank_count = 7; + omap1_gpio_data.bank_bits = 32; + omap1_gpio_data.gpio_bank = omap7xx_gpio_bank; + } + omap1_init_gpio(omap1_gpio_data.gpio_bank); + gpio_init(&omap1_gpio_data); +} + +/* + * This may get called early from board specific init + * for boards that have interrupts routed via FPGA. + */ +int __init omap_gpio_init(void) +{ + int i; + static int initialized; + + if ((!initialized) && cpu_class_is_omap1()) { + omap1_gpio_init(); + for (i = 0; i < omap1_gpio_data.bank_count; i++) + init_gpio_chip_irq(omap1_gpio_data.gpio_bank); + + mpuio_init(); + } + initialized = 1; + + return 0; +} -- 1.6.3.3 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html