Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
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 0000000..375440c
--- /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;
+}
+
+postcore_initcall(sapphire_init_gpio);