summaryrefslogtreecommitdiff
path: root/arch/arm/mach-msm/board-sapphire-gpio.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-msm/board-sapphire-gpio.c')
-rw-r--r--arch/arm/mach-msm/board-sapphire-gpio.c326
1 files changed, 326 insertions, 0 deletions
diff --git a/arch/arm/mach-msm/board-sapphire-gpio.c b/arch/arm/mach-msm/board-sapphire-gpio.c
new file mode 100644
index 000000000000..2f2df97956cf
--- /dev/null
+++ b/arch/arm/mach-msm/board-sapphire-gpio.c
@@ -0,0 +1,326 @@
+/* arch/arm/mach-msm/board-sapphire-gpio.c
+ * Copyright (C) 2007-2009 HTC Corporation.
+ * Author: Thomas Tsai <thomas_tsai@htc.com>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/irq.h>
+#include <linux/pm.h>
+#include <linux/sysdev.h>
+
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <asm/mach-types.h>
+
+#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;
+}
+
+arch_initcall(sapphire_init_gpio);