/* arch/arm/mach-msm/board-sapphire-gpio.c * Copyright (C) 2007-2009 HTC Corporation. * Author: Thomas Tsai * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include "gpio_chip.h" #include "board-sapphire.h" #ifdef DEBUG_SAPPHIRE_GPIO #define DBG(fmt, arg...) printk(KERN_INFO "%s: " fmt "\n", __func__, ## arg) #else #define DBG(fmt, arg...) do {} while (0) #endif #define SAPPHIRE_CPLD_INT_STATUS (SAPPHIRE_CPLD_BASE + 0x0E) #define SAPPHIRE_CPLD_INT_LEVEL (SAPPHIRE_CPLD_BASE + 0x08) #define SAPPHIRE_CPLD_INT_MASK (SAPPHIRE_CPLD_BASE + 0x0C) /*CPLD misc reg offset*/ static const int _g_CPLD_MISCn_Offset[] = { 0x0A, /*misc1 reg*/ 0x00, /*misc2 reg*/ 0x02, /*misc3 reg*/ 0x04, /*misc4 reg*/ 0x06}; /*misc5 reg*/ /*CPLD INT Bank*/ /*BANK0: int1 status, int2 level, int3 mask*/ static const int _g_INT_BANK_Offset[][3] = {{0x0E, 0x08, 0x0C} }; static uint8_t sapphire_cpld_initdata[4] = { [0] = 0x80, /* for serial debug UART3, low current misc2*/ [1] = 0x34, /* jog & tp enable, I2C pull misc3*/ [3] = 0x04, /* mmdi 32k en misc5*/ }; /*save current working int mask, so the value can be restored after resume. Sapphire has only bank0.*/ static uint8_t sapphire_int_mask[] = { [0] = 0xfb, /* enable all interrupts, bit 2 is not used */ }; /*Sleep have to prepare the wake up source in advance. default to disable all wakeup sources when suspend.*/ static uint8_t sapphire_sleep_int_mask[] = { [0] = 0x00, /* bit2 is not used */ }; static int sapphire_suspended; static int sapphire_gpio_read(struct gpio_chip *chip, unsigned n) { if (n < SAPPHIRE_GPIO_INT_B0_BASE) /*MISCn*/ return !!(readb(CPLD_GPIO_REG(n)) & CPLD_GPIO_BIT_POS_MASK(n)); else if (n <= SAPPHIRE_GPIO_END) /*gpio n is INT pin*/ return !!(readb(CPLD_INT_LEVEL_REG_G(n)) & CPLD_GPIO_BIT_POS_MASK(n)); return 0; } /*CPLD Write only register :MISC2, MISC3, MISC4, MISC5 => reg=0,2,4,6 Reading from write-only registers is undefined, so the writing value should be kept in shadow for later usage.*/ int sapphire_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on) { unsigned long flags; uint8_t reg_val; if (n > SAPPHIRE_GPIO_END) return -1; local_irq_save(flags); reg_val = readb(CPLD_GPIO_REG(n)); if (on) reg_val |= CPLD_GPIO_BIT_POS_MASK(n); else reg_val &= ~CPLD_GPIO_BIT_POS_MASK(n); writeb(reg_val, CPLD_GPIO_REG(n)); DBG("gpio=%d, l=0x%x\r\n", n, readb(SAPPHIRE_CPLD_INT_LEVEL)); local_irq_restore(flags); return 0; } static int sapphire_gpio_configure(struct gpio_chip *chip, unsigned int gpio, unsigned long flags) { if (flags & (GPIOF_OUTPUT_LOW | GPIOF_OUTPUT_HIGH)) sapphire_gpio_write(chip, gpio, flags & GPIOF_OUTPUT_HIGH); DBG("gpio=%d, l=0x%x\r\n", gpio, readb(SAPPHIRE_CPLD_INT_LEVEL)); return 0; } static int sapphire_gpio_get_irq_num(struct gpio_chip *chip, unsigned int gpio, unsigned int *irqp, unsigned long *irqnumflagsp) { DBG("gpio=%d, l=0x%x\r\n", gpio, readb(SAPPHIRE_CPLD_INT_LEVEL)); DBG("SAPPHIRE_GPIO_INT_B0_BASE=%d, SAPPHIRE_GPIO_LAST_INT=%d\r\n", SAPPHIRE_GPIO_INT_B0_BASE, SAPPHIRE_GPIO_LAST_INT); if ((gpio < SAPPHIRE_GPIO_INT_B0_BASE) || (gpio > SAPPHIRE_GPIO_LAST_INT)) return -ENOENT; *irqp = SAPPHIRE_GPIO_TO_INT(gpio); DBG("*irqp=%d\r\n", *irqp); if (irqnumflagsp) *irqnumflagsp = 0; return 0; } /*write 1 to clear INT status bit.*/ static void sapphire_gpio_irq_ack(unsigned int irq) { /*write 1 to clear*/ writeb(SAPPHIRE_INT_BIT_MASK(irq), CPLD_INT_STATUS_REG(irq)); } /*unmask/enable the INT static void sapphire_gpio_irq_unmask(unsigned int irq)*/ static void sapphire_gpio_irq_enable(unsigned int irq) { unsigned long flags; uint8_t reg_val; local_irq_save(flags); /*disabling all interrupts*/ reg_val = readb(CPLD_INT_MASK_REG(irq)) | SAPPHIRE_INT_BIT_MASK(irq); DBG("(irq=%d,0x%x, 0x%x)\r\n", irq, CPLD_INT_MASK_REG(irq), SAPPHIRE_INT_BIT_MASK(irq)); DBG("sapphire_suspended=%d\r\n", sapphire_suspended); /*printk(KERN_INFO "sapphire_gpio_irq_mask irq %d => %d:%02x\n", irq, bank, reg_val);*/ if (!sapphire_suspended) writeb(reg_val, CPLD_INT_MASK_REG(irq)); reg_val = readb(CPLD_INT_MASK_REG(irq)); DBG("reg_val= 0x%x\r\n", reg_val); DBG("l=0x%x\r\n", readb(SAPPHIRE_CPLD_INT_LEVEL)); local_irq_restore(flags); /*restore the interrupts*/ } /*mask/disable INT static void sapphire_gpio_irq_mask(unsigned int irq)*/ static void sapphire_gpio_irq_disable(unsigned int irq) { unsigned long flags; uint8_t reg_val; local_irq_save(flags); reg_val = readb(CPLD_INT_MASK_REG(irq)) & ~SAPPHIRE_INT_BIT_MASK(irq); /*CPLD INT MASK is r/w now.*/ /*printk(KERN_INFO "sapphire_gpio_irq_unmask irq %d => %d:%02x\n", irq, bank, reg_val);*/ DBG("(%d,0x%x, 0x%x, 0x%x)\r\n", irq, reg_val, CPLD_INT_MASK_REG(irq), SAPPHIRE_INT_BIT_MASK(irq)); DBG("sapphire_suspended=%d\r\n", sapphire_suspended); if (!sapphire_suspended) writeb(reg_val, CPLD_INT_MASK_REG(irq)); reg_val = readb(CPLD_INT_MASK_REG(irq)); DBG("reg_val= 0x%x\r\n", reg_val); DBG("l=0x%x\r\n", readb(SAPPHIRE_CPLD_INT_LEVEL)); local_irq_restore(flags); } /*preparing enable/disable wake source before sleep*/ int sapphire_gpio_irq_set_wake(unsigned int irq, unsigned int on) { unsigned long flags; uint8_t mask = SAPPHIRE_INT_BIT_MASK(irq); local_irq_save(flags); if (on) /*wake on -> mask the bit*/ sapphire_sleep_int_mask[CPLD_INT_TO_BANK(irq)] |= mask; else /*no wake -> unmask the bit*/ sapphire_sleep_int_mask[CPLD_INT_TO_BANK(irq)] &= ~mask; local_irq_restore(flags); return 0; } /*Sapphire has only one INT Bank.*/ static void sapphire_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) { int j; unsigned v; int int_base = SAPPHIRE_INT_START; v = readb(SAPPHIRE_CPLD_INT_STATUS); /*INT1 status reg, BANK0*/ for (j = 0; j < 8 ; j++) { /*8 bit per bank*/ if (v & (1U << j)) { /*got the INT Bit*/ DBG("generic_handle_irq j=0x%x\r\n", j); generic_handle_irq(int_base + j); } } desc->chip->ack(irq); /*clear CPLD INT in SOC side.*/ DBG("irq=%d, l=0x%x\r\n", irq, readb(SAPPHIRE_CPLD_INT_LEVEL)); } /*Save current working sources before sleep, so we can restore it after * resume.*/ static int sapphire_sysdev_suspend(struct sys_device *dev, pm_message_t state) { sapphire_suspended = 1; /*save current masking*/ sapphire_int_mask[0] = readb(SAPPHIRE_CPLD_BASE + SAPPHIRE_GPIO_INT_B0_MASK_REG); /*set waking source before sleep.*/ writeb(sapphire_sleep_int_mask[0], SAPPHIRE_CPLD_BASE + SAPPHIRE_GPIO_INT_B0_MASK_REG); return 0; } /*All the registers will be kept till a power loss...*/ int sapphire_sysdev_resume(struct sys_device *dev) { /*restore the working mask saved before sleep*/ writeb(sapphire_int_mask[0], SAPPHIRE_CPLD_BASE + SAPPHIRE_GPIO_INT_B0_MASK_REG); sapphire_suspended = 0; return 0; } /** * linux/irq.h :: struct irq_chip * @enable: enable the interrupt (defaults to chip->unmask if NULL) * @disable: disable the interrupt (defaults to chip->mask if NULL) * @ack: start of a new interrupt * @mask: mask an interrupt source * @mask_ack: ack and mask an interrupt source * @unmask: unmask an interrupt source */ static struct irq_chip sapphire_gpio_irq_chip = { .name = "sapphiregpio", .ack = sapphire_gpio_irq_ack, .mask = sapphire_gpio_irq_disable, /*sapphire_gpio_irq_mask,*/ .unmask = sapphire_gpio_irq_enable, /*sapphire_gpio_irq_unmask,*/ .set_wake = sapphire_gpio_irq_set_wake, /*.set_type = sapphire_gpio_irq_set_type,*/ }; /*Thomas:For CPLD*/ static struct gpio_chip sapphire_gpio_chip = { .start = SAPPHIRE_GPIO_START, .end = SAPPHIRE_GPIO_END, .configure = sapphire_gpio_configure, .get_irq_num = sapphire_gpio_get_irq_num, .read = sapphire_gpio_read, .write = sapphire_gpio_write, /* .read_detect_status = sapphire_gpio_read_detect_status, .clear_detect_status = sapphire_gpio_clear_detect_status */ }; struct sysdev_class sapphire_sysdev_class = { .name = "sapphiregpio_irq", .suspend = sapphire_sysdev_suspend, .resume = sapphire_sysdev_resume, }; static struct sys_device sapphire_irq_device = { .cls = &sapphire_sysdev_class, }; int sapphire_init_gpio(void) { int i; if (!machine_is_sapphire()) return 0; DBG("%d,%d\r\n", SAPPHIRE_INT_START, SAPPHIRE_INT_END); DBG("NR_MSM_IRQS=%d, NR_GPIO_IRQS=%d\r\n", NR_MSM_IRQS, NR_GPIO_IRQS); for (i = SAPPHIRE_INT_START; i <= SAPPHIRE_INT_END; i++) { set_irq_chip(i, &sapphire_gpio_irq_chip); set_irq_handler(i, handle_edge_irq); set_irq_flags(i, IRQF_VALID); } register_gpio_chip(&sapphire_gpio_chip); /*setup CPLD INT connecting to SOC's gpio 17 */ set_irq_type(MSM_GPIO_TO_INT(17), IRQF_TRIGGER_HIGH); set_irq_chained_handler(MSM_GPIO_TO_INT(17), sapphire_gpio_irq_handler); set_irq_wake(MSM_GPIO_TO_INT(17), 1); if (sysdev_class_register(&sapphire_sysdev_class) == 0) sysdev_register(&sapphire_irq_device); return 0; } int sapphire_init_cpld(unsigned int sys_rev) { int i; for (i = 0; i < ARRAY_SIZE(sapphire_cpld_initdata); i++) writeb(sapphire_cpld_initdata[i], SAPPHIRE_CPLD_BASE + i * 2); return 0; } postcore_initcall(sapphire_init_gpio);