[PATCH 2/3] arch: arm: mach-cns3xxx: Add gpiolib support

Tommy Lin tommy.lin.1101 at gmail.com
Thu Jul 28 13:16:41 EDT 2011


This patch makes CNS3XXX platform support gpiolib. Users can
configure GPIO via gpiolib APIs easily on CNS3XXX platform.

Signed-off-by: Tommy Lin <tommy.lin.1101 at gmail.com>
---
 arch/arm/Kconfig                             |    1 +
 arch/arm/configs/cns3420vb_defconfig         |    1 +
 arch/arm/mach-cns3xxx/Makefile               |    1 +
 arch/arm/mach-cns3xxx/cns3420vb.c            |   48 ++
 arch/arm/mach-cns3xxx/gpio.c                 |  692 ++++++++++++++++++++++++++
 arch/arm/mach-cns3xxx/include/mach/cns3xxx.h |   10 +-
 arch/arm/mach-cns3xxx/include/mach/gpio.h    |   63 +++
 7 files changed, 814 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm/mach-cns3xxx/gpio.c
 create mode 100644 arch/arm/mach-cns3xxx/include/mach/gpio.h

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 09ebf0b..ea48dd0 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -333,6 +333,7 @@ config ARCH_CNS3XXX
 	select ARM_GIC
 	select MIGHT_HAVE_PCI
 	select PCI_DOMAINS if PCI
+	select ARCH_REQUIRE_GPIOLIB
 	help
 	  Support for Cavium Networks CNS3XXX platform.
 
diff --git a/arch/arm/configs/cns3420vb_defconfig b/arch/arm/configs/cns3420vb_defconfig
index 313627a..c83d14c 100644
--- a/arch/arm/configs/cns3420vb_defconfig
+++ b/arch/arm/configs/cns3420vb_defconfig
@@ -52,6 +52,7 @@ CONFIG_SERIAL_8250_CONSOLE=y
 CONFIG_LEGACY_PTY_COUNT=16
 # CONFIG_HW_RANDOM is not set
 # CONFIG_HWMON is not set
+CONFIG_GPIO_SYSFS=y
 # CONFIG_VGA_CONSOLE is not set
 # CONFIG_HID_SUPPORT is not set
 # CONFIG_USB_SUPPORT is not set
diff --git a/arch/arm/mach-cns3xxx/Makefile b/arch/arm/mach-cns3xxx/Makefile
index 11033f1..7bb9c88 100644
--- a/arch/arm/mach-cns3xxx/Makefile
+++ b/arch/arm/mach-cns3xxx/Makefile
@@ -1,3 +1,4 @@
+obj-y					+= gpio.o
 obj-$(CONFIG_ARCH_CNS3XXX)		+= core.o pm.o devices.o
 obj-$(CONFIG_PCI)			+= pcie.o
 obj-$(CONFIG_MACH_CNS3420VB)		+= cns3420vb.o
diff --git a/arch/arm/mach-cns3xxx/cns3420vb.c b/arch/arm/mach-cns3xxx/cns3420vb.c
index 3e7d149..34c4a87 100644
--- a/arch/arm/mach-cns3xxx/cns3420vb.c
+++ b/arch/arm/mach-cns3xxx/cns3420vb.c
@@ -24,6 +24,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/physmap.h>
 #include <linux/mtd/partitions.h>
+#include <linux/proc_fs.h>
 #include <asm/setup.h>
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -158,10 +159,55 @@ static struct platform_device cns3xxx_usb_ohci_device = {
 	},
 };
 
+/* GPIO */
+static struct resource cns3xxx_gpio_resources[] = {
+	[0] = {
+		.start = IRQ_CNS3XXX_GPIOA,
+		.end   = IRQ_CNS3XXX_GPIOA,
+		.flags = IORESOURCE_IRQ,
+		.name  = "GPIOA"
+	},
+	[1] = {
+		.start = CNS3XXX_GPIOA_BASE_VIRT,
+		.end   = CNS3XXX_GPIOA_BASE_VIRT + 0x44,
+		.flags = IORESOURCE_MEM,
+		.name  = "GPIOA",
+	},
+	[2] = {
+		.start = IRQ_CNS3XXX_GPIOB,
+		.end   = IRQ_CNS3XXX_GPIOB,
+		.flags = IORESOURCE_IRQ,
+		.name  = "GPIOB"
+	},
+	[3] = {
+		.start = CNS3XXX_GPIOB_BASE_VIRT,
+		.end   = CNS3XXX_GPIOB_BASE_VIRT + 0x44,
+		.flags = IORESOURCE_MEM,
+		.name  = "GPIOB",
+	},
+	[4] = {
+		.start = CNS3XXX_MISC_BASE_VIRT,
+		.end   = CNS3XXX_MISC_BASE_VIRT + 0x800,
+		.flags = IORESOURCE_MEM,
+		.name  = "MISC"
+	},
+};
+
+static struct platform_device cns3xxx_gpio_device = {
+	.name		= "cns3xxx-gpio",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(cns3xxx_gpio_resources),
+	.resource	= cns3xxx_gpio_resources,
+};
+
+
+struct proc_dir_entry *cns3xxx_proc_dir;
+
 /*
  * Initialization
  */
 static struct platform_device *cns3420_pdevs[] __initdata = {
+	&cns3xxx_gpio_device,
 	&cns3420_nor_pdev,
 	&cns3xxx_usb_ehci_device,
 	&cns3xxx_usb_ohci_device,
@@ -176,6 +222,8 @@ static void __init cns3420_init(void)
 	cns3xxx_ahci_init();
 	cns3xxx_sdhci_init();
 
+	cns3xxx_proc_dir = proc_mkdir("cns3xxx", NULL);
+
 	pm_power_off = cns3xxx_power_off;
 }
 
diff --git a/arch/arm/mach-cns3xxx/gpio.c b/arch/arm/mach-cns3xxx/gpio.c
new file mode 100644
index 0000000..cafb65f
--- /dev/null
+++ b/arch/arm/mach-cns3xxx/gpio.c
@@ -0,0 +1,692 @@
+/*******************************************************************************
+ *
+ *  arch/arm/mach-cns3xxx/gpio.c
+ *
+ *  GPIO driver for the CNS3XXX SOCs
+ *
+ *  Copyright (c) 2011 Cavium
+ *
+ *  This file 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.
+ *
+ *  This file is distributed in the hope that it will be useful,
+ *  but AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ *  NONINFRINGEMENT.  See the GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this file; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA or
+ *  visit http://www.gnu.org/licenses/.
+ *
+ *  This file may also be available under a different license from Cavium.
+ *  Contact Cavium for more information
+ *
+ ******************************************************************************/
+
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <linux/io.h>
+
+#include <mach/gpio.h>
+#include <mach/cns3xxx.h>
+
+#include <asm/mach/irq.h>
+
+#define CONFIG_GPIO_CNS3XXX_DEBUG
+
+#ifdef CONFIG_GPIO_CNS3XXX_DEBUG
+#define __pr_debug(fmt, args...)	printk(KERN_ERR fmt, ##args)
+#else
+#define __pr_debug(fmt, args...)
+#endif
+
+
+#ifdef CONFIG_DEBUG_FS
+static void cns3xxx_dbg_show(struct seq_file *s, struct gpio_chip *chip);
+#else
+#define cns3xxx_dbg_show	NULL
+#endif
+
+#define GPIO_PROC_NAME		"gpio"
+static struct proc_dir_entry	*proc_cns3xxx_gpio;
+static struct irq_chip		cns3xxx_gpio_irq_chip;
+
+struct chog_gpio_chip {
+	struct gpio_chip	chip;
+	int			irq;
+	void __iomem		*reg_base;
+	void __iomem		*reg_sharepin_en;
+};
+
+#define INIT_CHOG_GPIO_CHIP(name, base_no, nr_gpio)			\
+	{								\
+		.chip = {						\
+			.label			= name,			\
+			.owner			= THIS_MODULE,		\
+			.request		= cns3xxx_request,	\
+			.direction_input	= cns3xxx_direction_in,	\
+			.direction_output	= cns3xxx_direction_out,\
+			.get			= cns3xxx_get,		\
+			.set			= cns3xxx_set,		\
+			.to_irq			= cns3xxx_to_irq,	\
+			.dbg_show		= cns3xxx_dbg_show,	\
+			.base			= base_no,		\
+			.ngpio			= nr_gpio,		\
+			.can_sleep		= 0,			\
+		},							\
+	}
+
+
+#define to_chog_gpio_chip(c)	container_of(c, struct chog_gpio_chip, chip)
+#define nr_banks	(sizeof(cns3xxx_gchip)/sizeof(struct chog_gpio_chip))
+
+/* The CNS3XXX GPIO pins are shard with special functions which is described in
+ * the following table. "none" in this table represent the corresponding pins
+ * are dedicate GPIO.
+ */
+const char *sharepin_desc[] = {
+	/* GPIOA group */
+/*  0 */ "none",	"none",		"SD_PWR_ON",	"OTG_DRV_VBUS",
+/*  4 */ "Don't use",	"none",		"none",		"none",
+/*  8 */ "CIM_nOE",	"LCD_Power",	"SMI_nCS3",	"SMI_nCS2",
+/* 12 */ "SMI_Clk",	"SMI_nADV",	"SMI_CRE",	"SMI_Addr[26]",
+/* 16 */ "SD_nCD",	"SD_nWP",	"SD_CLK",	"SD_CMD",
+/* 20 */ "SD_DT[7]",	"SD_DT[6]",	"SD_DT[5]",	"SD_DT[4]",
+/* 24 */ "SD_DT[3]",	"SD_DT[2]",	"SD_DT[1]",	"SD_DT[0]",
+/* 28 */ "SD_LED",	"UR_RXD1",	"UR_TXD1",	"UR_RTS2",
+	/* GPIOB group */
+/*  0 */ "UR_CTS2",	"UR_RXD2",	"UR_TXD2",	"PCMCLK",
+/*  4 */ "PCMFS",	"PCMDT",	"PCMDR",	"SPInCS1",
+/*  8 */ "SPInCS2",	"SPICLK",	"SPIDT",	"SPIDR",
+/* 12 */ "SCL",		"SDA",		"GMII2_CRS",	"GMII2_COL",
+/* 16 */ "RGMII1_CRS",	"RGMII1_COL",	"RGMII0_CRS",	"RGMII0_COL",
+/* 20 */ "MDC",		"MDIO",		"I2SCLK",	"I2SFS",
+/* 24 */ "I2SDT",	"I2SDR",	"ClkOut",	"Ext_Intr2",
+/* 28 */ "Ext_Intr1",	"Ext_Intr0",	"SATA_LED1",	"SATA_LED0",
+};
+
+struct cns3xxx_regs {
+	char *name;
+	void __iomem *addr;
+	u32 offset;
+};
+
+struct cns3xxx_regs gpio_regs[] =  {
+	{"Data Out",			0, GPIO_OUTPUT_OFFSET},
+	{"Data In",			0, GPIO_INPUT_OFFSET},
+	{"Direction",			0, GPIO_DIR_OFFSET},
+	{"Interrupt Enable",		0, GPIO_INTR_ENABLE_OFFSET},
+	{"Interrupt Raw Status",	0, GPIO_INTR_RAW_STATUS_OFFSET},
+	{"Interrupt Masked Status",	0, GPIO_INTR_MASKED_STATUS_OFFSET},
+	{"Interrupt Level Trigger",	0, GPIO_INTR_TRIGGER_METHOD_OFFSET},
+	{"Interrupt Both Edge",		0, GPIO_INTR_TRIGGER_BOTH_EDGES_OFFSET},
+	{"Interrupt Falling Edge",	0, GPIO_INTR_TRIGGER_TYPE_OFFSET},
+	{"Interrupt MASKED",		0, GPIO_INTR_MASK_OFFSET},
+	{"GPIO Bounce Enable",		0, GPIO_BOUNCE_ENABLE_OFFSET},
+	{"GPIO Bounce Prescale",	0, GPIO_BOUNCE_PRESCALE_OFFSET},
+};
+
+struct cns3xxx_regs misc_regs[] =  {
+	{"Drive Strength Register A",	MISC_IO_PAD_DRIVE_STRENGTH_CTRL_A},
+	{"Drive Strength Register B",	MISC_IO_PAD_DRIVE_STRENGTH_CTRL_B},
+	{"Pull Up/Down Ctrl A[15:0]",	MISC_GPIOA_15_0_PULL_CTRL_REG},
+	{"Pull Up/Down Ctrl A[31:16]",	MISC_GPIOA_16_31_PULL_CTRL_REG},
+	{"Pull Up/Down Ctrl B[15:0]",	MISC_GPIOB_15_0_PULL_CTRL_REG},
+	{"Pull Up/Down Ctrl B[31:16]",	MISC_GPIOB_16_31_PULL_CTRL_REG},
+};
+
+
+static int cns3xxx_request(struct gpio_chip *chip, unsigned offset)
+{
+	/* GPIOA4 is reserved for chip bonding configuration. Please don't use
+	 * and configure GPIOA4.
+	 */
+	if ((strcmp(chip->label, "GPIOA") == 0) && (offset == 4))
+		return -EINVAL;
+	return 0;
+}
+
+/*
+ * Configure the GPIO line as an input.
+ */
+static int cns3xxx_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+	struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip);
+	u32 reg;
+
+	/* Clear corresponding register bit to set as input pin. */
+	reg = readl(cns3xxx_gpio->reg_base + GPIO_DIR_OFFSET);
+	reg &= ~(1 << offset);
+	writel(reg, cns3xxx_gpio->reg_base + GPIO_DIR_OFFSET);
+
+	return 0;
+}
+
+/*
+ * Set the state of an output GPIO line.
+ */
+static void cns3xxx_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip);
+
+	if (value)
+		/* Write 1 to set corresponding bit output "HIGH"
+		 * Multi-bit write is allowed. Write 0 makes no change.
+		 */
+		writel(1 << offset,
+				cns3xxx_gpio->reg_base + GPIO_BIT_SET_OFFSET);
+	else
+		/* Write 1 to set corresponding bit output "LOW"
+		 * Multi-bit write is allowed. Write 0 makes no change.
+		 */
+		writel(1 << offset,
+				cns3xxx_gpio->reg_base + GPIO_BIT_CLEAR_OFFSET);
+}
+
+/*
+ * Configure the GPIO line as an output, with default state.
+ */
+static int cns3xxx_direction_out(struct gpio_chip *chip,
+		unsigned offset, int value)
+{
+	struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip);
+	u32 reg;
+
+	/* Set corresponding register bit to set as output pin. */
+	reg = readl(cns3xxx_gpio->reg_base + GPIO_DIR_OFFSET);
+	reg |= 1 << offset;
+	writel(reg, cns3xxx_gpio->reg_base + GPIO_DIR_OFFSET);
+
+	cns3xxx_set(chip, offset, value);
+
+	return 0;
+}
+
+/*
+ * Read the state of a GPIO line.
+ */
+static int cns3xxx_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip);
+	u32 reg;
+	int ret;
+
+	reg = readl(cns3xxx_gpio->reg_base + GPIO_INPUT_OFFSET);
+	ret = reg & (1 << offset);
+
+	return ret;
+}
+
+/*
+ * GPIO interrtups are remapped to unused irq number.
+ * The remapped GPIO IRQ number start from NR_IRQS_CNS3XXX (96). Here is the
+ * table of GPIO to irq mapping table.
+ *
+ *	GPIOA	GPIOB	|	GPIOA	GPIOB
+ * No.	IRQ	IRQ	|  No.	IRQ	IRQ
+ *  0	 96	128	|  16	112	144
+ *  1	 97	129	|  17	113	145
+ *  2	 98	130	|  18	114	146
+ *  3	 99	131	|  19	115	147
+ *  4	100	132	|  20	116	148
+ *  5	101	133	|  21	117	149
+ *  6	102	134	|  22	118	150
+ *  7	103	135	|  23	119	151
+ *  8	104	136	|  24	120	152
+ *  9	105	137	|  25	121	153
+ * 10	106	138	|  26	122	154
+ * 11	107	139	|  27	123	155
+ * 12	108	140	|  28	124	156
+ * 13	109	141	|  29	125	157
+ * 14	110	142	|  30	126	158
+ * 15	111	143	|  31	127	159
+ */
+static int cns3xxx_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip);
+
+	return offset + NR_IRQS_CNS3XXX + cns3xxx_gpio->chip.base;
+}
+
+static unsigned cns3xxx_irq_to_gpio_offset(struct gpio_chip *chip, int irq)
+{
+	struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip);
+
+	return irq - NR_IRQS_CNS3XXX - cns3xxx_gpio->chip.base;
+}
+
+int cns3xxx_gpio_set_irq_type(struct irq_data *data, unsigned int type)
+{
+	struct gpio_chip *chip = data->chip_data;
+	void __iomem *base = to_chog_gpio_chip(chip)->reg_base;
+	unsigned offset;
+	u32 reg_level, reg_both, reg_low, index;
+
+	offset = cns3xxx_irq_to_gpio_offset(chip, data->irq);
+	index = 1 << offset;
+
+	reg_level = readl(base + GPIO_INTR_TRIGGER_METHOD_OFFSET);
+	reg_both = readl(base + GPIO_INTR_TRIGGER_BOTH_EDGES_OFFSET);
+	reg_low = readl(base + GPIO_INTR_TRIGGER_TYPE_OFFSET);
+
+	switch (type) {
+	case IRQ_TYPE_EDGE_RISING:
+		reg_level &= ~index;
+		reg_both &= ~index;
+		reg_low &= ~index;
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		reg_level &= ~index;
+		reg_both &= ~index;
+		reg_low |= index;
+		break;
+	case IRQ_TYPE_EDGE_BOTH:
+		reg_level &= ~index;
+		reg_both |= index;
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		reg_level |= index;
+		reg_low |= index;
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		reg_level |= index;
+		reg_low &= ~index;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	cns3xxx_direction_in(chip, offset);
+	writel(reg_level, base + GPIO_INTR_TRIGGER_METHOD_OFFSET);
+	writel(reg_both, base + GPIO_INTR_TRIGGER_BOTH_EDGES_OFFSET);
+	writel(reg_low, base + GPIO_INTR_TRIGGER_TYPE_OFFSET);
+
+	return 0;
+}
+
+static void cns3xxx_irq_enable(struct irq_data *data)
+{
+	struct gpio_chip *chip = data->chip_data;
+	void __iomem *base = to_chog_gpio_chip(chip)->reg_base;
+	unsigned offset = cns3xxx_irq_to_gpio_offset(chip, data->irq);
+	u32 reg;
+
+	reg = readl(base + GPIO_INTR_ENABLE_OFFSET);
+	reg |= (1 << offset);
+	writel(reg, base + GPIO_INTR_ENABLE_OFFSET);
+}
+
+static void cns3xxx_irq_disable(struct irq_data *data)
+{
+	struct gpio_chip *chip = data->chip_data;
+	void __iomem *base = to_chog_gpio_chip(chip)->reg_base;
+	unsigned offset = cns3xxx_irq_to_gpio_offset(chip, data->irq);
+	u32 reg;
+
+	reg = readl(base + GPIO_INTR_ENABLE_OFFSET);
+	reg &= ~(1 << offset);
+	writel(reg, base + GPIO_INTR_ENABLE_OFFSET);
+}
+
+static void cns3xxx_gpio_mask(struct irq_data *data)
+{
+	struct gpio_chip *chip = data->chip_data;
+	void __iomem *base = to_chog_gpio_chip(chip)->reg_base;
+	unsigned offset = cns3xxx_irq_to_gpio_offset(chip, data->irq);
+	u32 reg;
+
+	reg = readl(base + GPIO_INTR_MASK_OFFSET);
+	reg |= (1 << offset);
+	writel(reg, base + GPIO_INTR_MASK_OFFSET);
+}
+
+static void cns3xxx_gpio_unmask(struct irq_data *data)
+{
+	struct gpio_chip *chip = data->chip_data;
+	void __iomem *base = to_chog_gpio_chip(chip)->reg_base;
+	unsigned offset = cns3xxx_irq_to_gpio_offset(chip, data->irq);
+	u32 reg;
+
+	reg = readl(base + GPIO_INTR_MASK_OFFSET);
+	reg &= ~(1 << offset);
+	writel(reg, base + GPIO_INTR_MASK_OFFSET);
+}
+
+static struct irq_chip cns3xxx_gpio_irq_chip = {
+	.name = "GPIO",
+	.irq_enable = cns3xxx_irq_enable,
+	.irq_disable = cns3xxx_irq_disable,
+	.irq_mask = cns3xxx_gpio_mask,
+	.irq_unmask = cns3xxx_gpio_unmask,
+	.irq_set_type = cns3xxx_gpio_set_irq_type,
+};
+
+static struct chog_gpio_chip cns3xxx_gchip[] = {
+			  /* label,  base,	   ngpio */
+	INIT_CHOG_GPIO_CHIP("GPIOA", 0x00,	   MAX_GPIOA_NO),
+	INIT_CHOG_GPIO_CHIP("GPIOB", MAX_GPIOA_NO, MAX_GPIOB_NO),
+};
+
+
+static int cns3xxx_gpio_read_proc(char *page, char **start,  off_t off,
+		int count, int *eof, void *data)
+{
+	int num = 0, i, nr_regs;
+
+	nr_regs = sizeof(gpio_regs)/sizeof(struct cns3xxx_regs);
+	num += sprintf(page + num,
+			"Register Description        GPIOA     GPIOB\n"
+			"====================        =====     =====\n");
+	num += sprintf(page + num, "%-26.26s: %08x  %08x\n", "GPIO Disable",
+		readl(cns3xxx_gchip[0].reg_sharepin_en),
+		readl(cns3xxx_gchip[1].reg_sharepin_en));
+	for (i = 0; i < nr_regs; i++) {
+		num += sprintf(page + num, "%-26.26s: %08x  %08x\n",
+			gpio_regs[i].name,
+			readl(cns3xxx_gchip[0].reg_base + gpio_regs[i].offset),
+			readl(cns3xxx_gchip[1].reg_base + gpio_regs[i].offset));
+	}
+
+	num += sprintf(page + num, "\n"
+			"Register Description        Value\n"
+			"====================        =====\n");
+	nr_regs = sizeof(misc_regs)/sizeof(struct cns3xxx_regs);
+	for (i = 0; i < nr_regs; i++) {
+		num += sprintf(page + num, "%-26.26s: %08x\n",
+			misc_regs[i].name,
+			readl(misc_regs[i].addr));
+	}
+
+	return num;
+}
+
+/*
+ * Turn on corresponding shared pin function.
+ * Turn on shared pin function will also disable GPIO function. Related GPIO
+ * control registers are still accessable but not reflect to pin.
+ */
+int cns3xxx_sharepin_request(unsigned gpio, const char *label)
+{
+	struct gpio_chip *chip;
+	int i, reg, ret, offset = gpio;
+
+	ret = gpio_request(gpio, label);
+	if (ret) {
+		__pr_debug("gpio%d(%s) already in use! Err=%d\n",
+				gpio, label, ret);
+		return ret;
+	}
+
+	for (i = 0; i < nr_banks; i++) {
+		chip = &cns3xxx_gchip[i].chip;
+		if (offset > chip->ngpio) {
+			offset -= chip->ngpio;
+			continue;
+		}
+		reg = readl(cns3xxx_gchip[i].reg_sharepin_en);
+		reg |= (1 << offset);
+		writel(reg, cns3xxx_gchip[i].reg_sharepin_en);
+		__pr_debug("%s[%d] is occupied by %s function!\n",
+				chip->label, offset, sharepin_desc[gpio]);
+		break;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(cns3xxx_sharepin_request);
+
+/*
+ * Turn off corresponding share pin function.
+ */
+void cns3xxx_sharepin_free(unsigned gpio)
+{
+	struct gpio_chip *chip;
+	int i, reg, offset = gpio;
+
+	gpio_free(gpio);
+
+	for (i = 0; i < nr_banks; i++) {
+		chip = &cns3xxx_gchip[i].chip;
+		if (offset > chip->ngpio) {
+			offset -= chip->ngpio;
+			continue;
+		}
+		reg = readl(cns3xxx_gchip[i].reg_sharepin_en);
+		reg &= ~(1 << offset);
+		writel(reg, cns3xxx_gchip[i].reg_sharepin_en);
+		printk(KERN_INFO "%s[%d] share pin function (%s) disabled!\n",
+				chip->label, offset, sharepin_desc[gpio]);
+		break;
+	}
+}
+EXPORT_SYMBOL(cns3xxx_sharepin_free);
+
+
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+static void cns3xxx_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+	struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip);
+	int i, is_out;
+	unsigned gpio;
+	const char *gpio_label;
+
+	for (i = 0; i < chip->ngpio; i++) {
+		gpio = chip->base + i;
+		gpio_label = gpiochip_is_requested(chip, i);
+		if (!gpio_label)
+			continue;
+		is_out = test_bit(i, cns3xxx_gpio->reg_base + GPIO_DIR_OFFSET);
+		seq_printf(s, " gpio-%-3d [%-20.20s] %s %s (%s%-2d)\n",
+			gpio, gpio_label,
+			is_out ? "out" : "in ",
+			chip->get(chip, i) ? "hi" : "lo",
+			chip->label, i);
+	}
+}
+
+static int cns3xxx_dbg_gpio_show_all(struct seq_file *s, void *unused)
+{
+	int i, j, is_out, disabled;
+	unsigned gpio;
+	const char *gpio_label;
+	struct gpio_chip *chip;
+
+	for (j = 0; j < nr_banks; j++) {
+		chip = &cns3xxx_gchip[j].chip;
+		seq_printf(s, "           Label                 Mode      Dir Value Note\n");
+		seq_printf(s, " ============================================================\n");
+		for (i = 0; i < chip->ngpio; i++) {
+			gpio = chip->base + i;
+			disabled = test_bit(i, cns3xxx_gchip[j].reg_sharepin_en);
+			gpio_label = gpiochip_is_requested(chip, i);
+			if (!gpio_label) {
+				if (disabled)
+					gpio_label = sharepin_desc[gpio];
+				else
+					gpio_label = "Not requested";
+			}
+			is_out = test_bit(i, cns3xxx_gchip[j].reg_base + GPIO_DIR_OFFSET);
+			seq_printf(s, " gpio-%-3d [%-20.20s] %-10.10s%s %s   (%s%-2d)\n",
+				gpio, gpio_label,
+				disabled ? "Function" : "GPIO",
+				is_out ? "out" : "in ",
+				chip->get(chip, i) ? "hi" : "lo",
+				chip->label, i);
+		}
+		seq_printf(s, "\n");
+	}
+
+	return 0;
+}
+
+static int dbg_gpio_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, cns3xxx_dbg_gpio_show_all, &inode->i_private);
+}
+
+static const struct file_operations debug_fops = {
+	.open		= dbg_gpio_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int __init cns3xxx_gpio_debuginit(void)
+{
+	(void) debugfs_create_file("gpio-cns3xxx", S_IRUGO,
+			NULL, NULL, &debug_fops);
+	return 0;
+}
+late_initcall(cns3xxx_gpio_debuginit);
+
+#endif /* CONFIG_DEBUG_FS */
+
+static void chained_gpio_irq_handler(unsigned irq, struct irq_desc *desc)
+{
+	struct chog_gpio_chip *chip = irq_get_handler_data(irq);
+	struct irq_chip *ichip = irq_desc_get_chip(desc);
+	unsigned i;
+	int target_irq;
+	u32 status;
+
+	chained_irq_enter(ichip, desc);
+
+	status = readl(chip->reg_base + GPIO_INTR_MASKED_STATUS_OFFSET);
+
+	/* Clean System irq status */
+	writel(status, chip->reg_base + GPIO_INTR_CLEAR_OFFSET);
+
+	for (i = 0; i < chip->chip.ngpio; i++) {
+		if (status & (1 << i)) {
+			target_irq = cns3xxx_to_irq(&chip->chip, i);
+			printk(KERN_INFO "Invoke cascaded irq %d from irq %d\n",
+					target_irq, chip->irq);
+			generic_handle_irq(target_irq);
+		}
+	}
+
+	chained_irq_exit(ichip, desc);
+}
+
+static int __devinit gpio_probe(struct platform_device *pdev)
+{
+	int i, j, nr_gpios = 0, irq = 0;
+	struct resource *res;
+	void __iomem *misc_reg;
+
+	cns3xxx_pwr_clk_en(0x1 << PM_CLK_GATE_REG_OFFSET_GPIO);
+	cns3xxx_pwr_soft_rst(0x1 << PM_CLK_GATE_REG_OFFSET_GPIO);
+
+	if (cns3xxx_proc_dir) {
+		proc_cns3xxx_gpio = create_proc_entry(GPIO_PROC_NAME,
+				S_IFREG | S_IRUGO, cns3xxx_proc_dir) ;
+		if (proc_cns3xxx_gpio)
+			proc_cns3xxx_gpio->read_proc = cns3xxx_gpio_read_proc;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "MISC");
+	if (res)
+		misc_reg = (void __iomem *)res->start;
+	else
+		return -ENODEV;
+
+	/* Scan and match GPIO resources */
+	for (i = 0; i < nr_banks; i++) {
+		/* Fetech GPIO base address */
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+				cns3xxx_gchip[i].chip.label);
+		if (!res)
+			continue;
+		cns3xxx_gchip[i].reg_base = (void __iomem *)res->start;
+
+		/* Fetech GPIO interrupt number */
+		res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+				cns3xxx_gchip[i].chip.label);
+		if (!res)
+			continue;
+		irq = res->start;
+		cns3xxx_gchip[i].irq = irq;
+		__pr_debug("%s base=0x%08x, irq=%d",
+				cns3xxx_gchip[i].chip.label,
+				(u32)cns3xxx_gchip[i].reg_base, irq);
+
+		cns3xxx_gchip[i].chip.dev = &pdev->dev;
+		cns3xxx_gchip[i].reg_sharepin_en = misc_reg + 0x14 + i * 4;
+
+		gpiochip_add(&cns3xxx_gchip[i].chip);
+
+		/* Clear All Interrupt Status */
+		writel(0xFFFFFFFF, cns3xxx_gchip[i].reg_base +
+				GPIO_INTR_CLEAR_OFFSET);
+
+		/* Initial irq_chip to handle virtual GPIO irqs. */
+		for (j = 0; j < cns3xxx_gchip[i].chip.ngpio; j++) {
+			irq = cns3xxx_to_irq(&cns3xxx_gchip[i].chip, j);
+			irq_set_chip_and_handler(irq, &cns3xxx_gpio_irq_chip,
+				handle_simple_irq);
+			set_irq_flags(irq, IRQF_VALID);
+			irq_set_chip_data(irq, &cns3xxx_gchip[i]);
+		}
+		irq_set_chained_handler(cns3xxx_gchip[i].irq,
+				chained_gpio_irq_handler);
+		irq_set_handler_data(cns3xxx_gchip[i].irq,
+				&cns3xxx_gchip[i]);
+
+		nr_gpios += cns3xxx_gchip[i].chip.ngpio;
+		if (nr_gpios >= MAX_GPIO_NO)
+			break;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int gpio_cns3xxx_suspend(struct platform_device *dev, pm_message_t state)
+{
+	__pr_debug("%s,%s,%d\n", __FILE__, __func__, __LINE__);
+
+	return 0;
+}
+
+static int gpio_cns3xxx_resume(struct platform_device *dev)
+{
+	__pr_debug("%s,%s,%d\n", __FILE__, __func__, __LINE__);
+
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct platform_driver gpio_driver = {
+	.probe		= gpio_probe,
+#ifdef CONFIG_PM
+	.suspend	= gpio_cns3xxx_suspend,
+	.resume		= gpio_cns3xxx_resume,
+#endif /* CONFIG_PM */
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "cns3xxx-gpio",
+	},
+};
+
+int __init cns3xxx_gpio_init(void)
+{
+	return platform_driver_probe(&gpio_driver, gpio_probe);
+}
+
+void __exit cns3xxx_gpio_exit(void)
+{
+	if (proc_cns3xxx_gpio)
+		remove_proc_entry(GPIO_PROC_NAME, cns3xxx_proc_dir);
+}
+
+module_init(cns3xxx_gpio_init);
+module_exit(cns3xxx_gpio_exit);
+
+MODULE_LICENSE("GPL");
+
diff --git a/arch/arm/mach-cns3xxx/include/mach/cns3xxx.h b/arch/arm/mach-cns3xxx/include/mach/cns3xxx.h
index 191c8e5..217bb4d 100644
--- a/arch/arm/mach-cns3xxx/include/mach/cns3xxx.h
+++ b/arch/arm/mach-cns3xxx/include/mach/cns3xxx.h
@@ -624,9 +624,15 @@ int cns3xxx_cpu_clock(void);
 
 #define NR_IRQS_CNS3XXX			(IRQ_TC11MP_GIC_START + 64)
 
-#if !defined(NR_IRQS) || (NR_IRQS < NR_IRQS_CNS3XXX)
+#define MAX_GPIOA_NO			32
+#define MAX_GPIOB_NO			32
+#define MAX_GPIO_NO			(MAX_GPIOA_NO + MAX_GPIOB_NO)
+
+#if !defined(NR_IRQS) || (NR_IRQS < (NR_IRQS_CNS3XXX + MAX_GPIO_NO))
 #undef NR_IRQS
-#define NR_IRQS				NR_IRQS_CNS3XXX
+#define NR_IRQS				(NR_IRQS_CNS3XXX + MAX_GPIO_NO)
 #endif
 
+extern struct proc_dir_entry *cns3xxx_proc_dir;
+
 #endif	/* __MACH_BOARD_CNS3XXX_H */
diff --git a/arch/arm/mach-cns3xxx/include/mach/gpio.h b/arch/arm/mach-cns3xxx/include/mach/gpio.h
new file mode 100644
index 0000000..22efca8
--- /dev/null
+++ b/arch/arm/mach-cns3xxx/include/mach/gpio.h
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ *
+ *  Copyright (c) 2011 Cavium
+ *
+ *  This file 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.
+ *
+ *  This file is distributed in the hope that it will be useful,
+ *  but AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ *  NONINFRINGEMENT.  See the GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this file; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA or
+ *  visit http://www.gnu.org/licenses/.
+ *
+ *  This file may also be available under a different license from Cavium.
+ *  Contact Cavium for more information
+ *
+ ******************************************************************************/
+
+#ifndef	_CNS3XXX_GPIO_H_
+#define	_CNS3XXX_GPIO_H_
+
+#include <mach/cns3xxx.h>
+
+#define ARCH_NR_GPIOS				MAX_GPIO_NO
+
+#include <asm-generic/gpio.h>
+
+#define GPIO_OUTPUT_OFFSET			0x00
+#define GPIO_INPUT_OFFSET			0x04
+#define GPIO_DIR_OFFSET				0x08
+#define GPIO_BIT_SET_OFFSET			0x10
+#define GPIO_BIT_CLEAR_OFFSET			0x14
+#define GPIO_INTR_ENABLE_OFFSET			0x20
+#define GPIO_INTR_RAW_STATUS_OFFSET		0x24
+#define GPIO_INTR_MASKED_STATUS_OFFSET		0x28
+#define GPIO_INTR_MASK_OFFSET			0x2C
+#define GPIO_INTR_CLEAR_OFFSET			0x30
+#define GPIO_INTR_TRIGGER_METHOD_OFFSET		0x34
+#define GPIO_INTR_TRIGGER_BOTH_EDGES_OFFSET	0x38
+#define GPIO_INTR_TRIGGER_TYPE_OFFSET		0x3C
+#define GPIO_BOUNCE_ENABLE_OFFSET		0x40
+#define GPIO_BOUNCE_PRESCALE_OFFSET		0x44
+
+
+#define gpio_get_value				__gpio_get_value
+#define gpio_set_value				__gpio_set_value
+#define gpio_cansleep				__gpio_cansleep
+#define gpio_to_irq				__gpio_to_irq
+
+#define GPIOA(n)				n
+#define GPIOB(n)				(MAX_GPIOA_NO + n)
+
+/* Function prototype */
+int cns3xxx_sharepin_request(unsigned gpio, const char *label);
+void cns3xxx_sharepin_free(unsigned gpio);
+
+#endif
+
-- 
1.7.6




More information about the linux-arm-kernel mailing list