[PATCH 5/9] lpc2k: gpiolib

Ithamar R. Adema ithamar.adema at team-embedded.nl
Thu Mar 17 11:54:20 EDT 2011


gpiolib support, including gpio interrupts.

Signed-off-by: Ithamar R. Adema <ithamar.adema at team-embedded.nl>
---
 arch/arm/Kconfig                            |    1 +
 arch/arm/mach-lpc2k/Makefile                |    2 +-
 arch/arm/mach-lpc2k/gpio.c                  |  265 +++++++++++++++++++++++++++
 arch/arm/mach-lpc2k/include/mach/gpio.h     |   22 +++
 arch/arm/mach-lpc2k/include/mach/hardware.h |    2 +
 arch/arm/mach-lpc2k/include/mach/irqs.h     |   69 +++++++-
 6 files changed, 359 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm/mach-lpc2k/gpio.c
 create mode 100644 arch/arm/mach-lpc2k/include/mach/gpio.h

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 35297f9..fce855d 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -499,6 +499,7 @@ config ARCH_LPC2K
 	select CLKDEV_LOOKUP
 	select GENERIC_TIME
 	select GENERIC_CLOCKEVENTS
+	select ARCH_REQUIRE_GPIOLIB
 	help
 	  Support for NXP LPC2K family of SoCs. These SoCs are based on
 	  an ARM7TDMI-S core, and have optional on-chip flash and SRAM,
diff --git a/arch/arm/mach-lpc2k/Makefile b/arch/arm/mach-lpc2k/Makefile
index 267432c..89596a7 100644
--- a/arch/arm/mach-lpc2k/Makefile
+++ b/arch/arm/mach-lpc2k/Makefile
@@ -1 +1 @@
-obj-y	:= clock.o irq.o time.o
+obj-y	:= clock.o irq.o gpio.o time.o
diff --git a/arch/arm/mach-lpc2k/gpio.c b/arch/arm/mach-lpc2k/gpio.c
new file mode 100644
index 0000000..44f0eb9
--- /dev/null
+++ b/arch/arm/mach-lpc2k/gpio.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2011 Team Embeded VOF
+ *     Ithamar R. Adema <ihamar.adema at team-embedded.nl>
+ *
+ * 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 <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+
+#define LPC2K_GPIO_CHIP(name, base_gpio, nr_gpio, irqbase)		\
+	{								\
+		.chip = {						\
+			.label		  = name,			\
+			.direction_input  = lpc2k_dir_input,		\
+			.direction_output = lpc2k_dir_output,		\
+			.to_irq           = lpc2k_to_irq,		\
+			.get		  = lpc2k_gpio_get,		\
+			.set		  = lpc2k_gpio_set,		\
+			.base		  = base_gpio,			\
+			.ngpio		  = nr_gpio,			\
+		},							\
+		.irq_base = irqbase,					\
+	}
+
+#define to_lpc2k_gpio_chip(c) container_of(c, struct lpc2k_gpio_chip, chip)
+
+#define FIODIR	0x00
+#define FIOMASK	0x10
+#define FIOPIN	0x14
+#define FIOSET	0x18
+#define FIOCLR	0x1c
+
+struct lpc2k_gpio_chip {
+	struct gpio_chip chip;
+	void __iomem *regbase;
+	int irq_base;
+	spinlock_t gpio_lock;
+};
+
+static int lpc2k_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	struct lpc2k_gpio_chip *lpc2k_gpio = to_lpc2k_gpio_chip(chip);
+	if (lpc2k_gpio->irq_base < 0)
+		return -EINVAL;
+
+	return lpc2k_gpio->irq_base + offset;
+}
+
+static int lpc2k_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct lpc2k_gpio_chip *lpc2k_gpio = to_lpc2k_gpio_chip(chip);
+
+	return !!(readl(lpc2k_gpio->regbase + FIOPIN) & (1 << offset));
+}
+
+static void lpc2k_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+	struct lpc2k_gpio_chip *lpc2k_gpio = to_lpc2k_gpio_chip(chip);
+
+	writel(1 << offset, lpc2k_gpio->regbase + (val ? FIOSET : FIOCLR));
+}
+
+static int lpc2k_dir_input(struct gpio_chip *chip, unsigned offset)
+{
+	struct lpc2k_gpio_chip *lpc2k_gpio = to_lpc2k_gpio_chip(chip);
+	void __iomem *pio = lpc2k_gpio->regbase + FIODIR;
+	unsigned int regval;
+	unsigned long flags;
+
+	spin_lock_irqsave(&lpc2k_gpio->gpio_lock, flags);
+
+	regval = readl(pio);
+	regval &= ~(1 << offset);
+	writel(regval, pio);
+
+	spin_unlock_irqrestore(&lpc2k_gpio->gpio_lock, flags);
+
+	return 0;
+}
+
+static int lpc2k_dir_output(struct gpio_chip *chip, unsigned offset, int val)
+{
+	struct lpc2k_gpio_chip *lpc2k_gpio = to_lpc2k_gpio_chip(chip);
+	unsigned int regval;
+	unsigned long flags;
+
+	spin_lock_irqsave(&lpc2k_gpio->gpio_lock, flags);
+
+	regval = readl(lpc2k_gpio->regbase + FIODIR);
+	regval |= (1 << offset);
+	writel(regval, lpc2k_gpio->regbase + FIODIR);
+
+	writel(1 << offset, lpc2k_gpio->regbase + (val ? FIOSET : FIOCLR));
+
+	spin_unlock_irqrestore(&lpc2k_gpio->gpio_lock, flags);
+
+	return 0;
+}
+
+static struct lpc2k_gpio_chip lpc2k_gpio[] = {
+	LPC2K_GPIO_CHIP("P0", 0, 32, IRQ_LPC2K_GPIO0),
+	LPC2K_GPIO_CHIP("P1", 32, 32, -1),
+	LPC2K_GPIO_CHIP("P2", 64, 32, IRQ_LPC2K_GPIO64),
+	LPC2K_GPIO_CHIP("P3", 96, 32, -1),
+	LPC2K_GPIO_CHIP("P4", 128, 32, -1),
+};
+
+#define IOINTSTAT	0x0080
+#define IO0INT		0x0084
+#define IO2INT		0x00a4
+
+#define IOINTSTATR	0x0000
+#define IOINTSTATF	0x0004
+#define IOINTCLR	0x0008
+#define IOINTENR	0x000c
+#define IOINTENF	0x0010
+
+static void lpc2k_gpio_enable_irq(struct irq_data *d)
+{
+	void __iomem *base = irq_data_get_irq_chip_data(d);
+	unsigned irq = d->irq & 31;
+	unsigned status = irq_to_desc(d->irq)->status;
+
+	status &= IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING;
+	if (!status)
+		status = IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING;
+
+	if (status & IRQ_TYPE_EDGE_RISING)
+		writel(readl(base + IOINTENR) | (1 << irq), base + IOINTENR);
+	else
+		writel(readl(base + IOINTENR) & ~(1 << irq), base + IOINTENR);
+
+	if (status & IRQ_TYPE_EDGE_FALLING)
+		writel(readl(base + IOINTENF) | (1 << irq), base + IOINTENF);
+	else
+		writel(readl(base + IOINTENF) & ~(1 << irq), base + IOINTENF);
+}
+
+static void lpc2k_gpio_disable_irq(struct irq_data *d)
+{
+	void __iomem *base = irq_data_get_irq_chip_data(d);
+	unsigned irq = d->irq & 31;
+
+	writel(readl(base + IOINTENR) & ~(1 << irq), base + IOINTENR);
+	writel(readl(base + IOINTENF) & ~(1 << irq), base + IOINTENF);
+}
+
+static int lpc2k_gpio_set_type(struct irq_data *d, unsigned trigger)
+{
+	void __iomem *base = irq_data_get_irq_chip_data(d);
+	struct irq_desc *desc = irq_to_desc(d->irq);
+	unsigned irq = d->irq & 31;
+
+	if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+		return -EINVAL;
+
+	desc->status &= ~IRQ_TYPE_SENSE_MASK;
+	desc->status |= trigger;
+
+	/* don't enable the IRQ if it's currently disabled */
+	if (desc->depth == 0) {
+		if (trigger & IRQ_TYPE_EDGE_RISING)
+			writel(readl(base + IOINTENR) | (1 << irq),
+			       base + IOINTENR);
+		else
+			writel(readl(base + IOINTENR) & ~(1 << irq),
+			       base + IOINTENR);
+
+		if (trigger & IRQ_TYPE_EDGE_FALLING)
+			writel(readl(base + IOINTENF) | (1 << irq),
+			       base + IOINTENF);
+		else
+			writel(readl(base + IOINTENF) & ~(1 << irq),
+			       base + IOINTENF);
+	}
+
+	return 0;
+}
+
+struct irq_chip gpio_irq_chip_p0 = {
+	.name = "GPIO-P0",
+	.irq_enable = lpc2k_gpio_enable_irq,
+	.irq_disable = lpc2k_gpio_disable_irq,
+	.irq_set_type = lpc2k_gpio_set_type,
+};
+
+struct irq_chip gpio_irq_chip_p2 = {
+	.name = "GPIO-P2",
+	.irq_enable = lpc2k_gpio_enable_irq,
+	.irq_disable = lpc2k_gpio_disable_irq,
+	.irq_set_type = lpc2k_gpio_set_type,
+};
+
+static void lpc2k_demux_gpio_irq(unsigned int irq, struct irq_desc *desc)
+{
+	u32 status = readl(APB_GPIO_BASE + IOINTSTAT);
+	if (status & 1) {
+		int i, stat = readl(APB_GPIO_BASE + IO0INT + IOINTSTATR) |
+		    readl(APB_GPIO_BASE + IO0INT + IOINTSTATF);
+
+		writel(stat, APB_GPIO_BASE + IO0INT + IOINTCLR);
+
+		for (i = 0; i < 32; i++)
+			if (stat & (1 << i))
+				generic_handle_irq(IRQ_LPC2K_GPIO0 + i);
+	}
+
+	if (status & 4) {
+		int i, stat = readl(APB_GPIO_BASE + IO2INT + IOINTSTATR) |
+		    readl(APB_GPIO_BASE + IO2INT + IOINTSTATF);
+
+		writel(stat, APB_GPIO_BASE + IO2INT + IOINTCLR);
+
+		for (i = 0; i < 32; i++)
+			if (stat & (1 << i))
+				generic_handle_irq(IRQ_LPC2K_GPIO64 + i);
+	}
+}
+
+static int __init lpc2k_init_gpio(void)
+{
+	struct lpc2k_gpio_chip *gpio_chip;
+	void __iomem *base;
+	unsigned i;
+
+	for (i = 0; i < ARRAY_SIZE(lpc2k_gpio); i++) {
+		gpio_chip = &lpc2k_gpio[i];
+		spin_lock_init(&gpio_chip->gpio_lock);
+		gpio_chip->regbase =
+		    (void __iomem *)(FAST_GPIO_BASE + i * 0x20);
+		gpiochip_add(&gpio_chip->chip);
+
+		writel(0, gpio_chip->regbase + FIOMASK);
+	}
+
+	base = (void __iomem *)(APB_GPIO_BASE + IO0INT);
+	for (i = IRQ_LPC2K_GPIO0; i <= IRQ_LPC2K_GPIO31; i++) {
+		set_irq_chip(i, &gpio_irq_chip_p0);
+		set_irq_chip_data(i, base);
+		set_irq_handler(i, handle_simple_irq);
+		set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
+	}
+
+	base = (void __iomem *)(APB_GPIO_BASE + IO2INT);
+	for (i = IRQ_LPC2K_GPIO64; i <= IRQ_LPC2K_GPIO95; i++) {
+		set_irq_chip(i, &gpio_irq_chip_p2);
+		set_irq_chip_data(i, base);
+		set_irq_handler(i, handle_simple_irq);
+		set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
+	}
+
+	set_irq_chained_handler(IRQ_LPC2K_EINT3, lpc2k_demux_gpio_irq);
+
+	return 0;
+}
+
+postcore_initcall(lpc2k_init_gpio);
diff --git a/arch/arm/mach-lpc2k/include/mach/gpio.h b/arch/arm/mach-lpc2k/include/mach/gpio.h
new file mode 100644
index 0000000..efa970a
--- /dev/null
+++ b/arch/arm/mach-lpc2k/include/mach/gpio.h
@@ -0,0 +1,22 @@
+/*
+ *  Copyright (C) 2011 Team Embedded VOF
+ *     Ithamar R. Adema <ihamar.adema at team-embedded.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#ifndef MACH_LPC2K_GPIO_H
+#define MACH_LPC2K_GPIO_H
+
+#include <asm-generic/gpio.h>
+
+#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
+
+#endif /* MACH_LPC2K_GPIO_H */
diff --git a/arch/arm/mach-lpc2k/include/mach/hardware.h b/arch/arm/mach-lpc2k/include/mach/hardware.h
index ce19ff7..0d7c10c 100644
--- a/arch/arm/mach-lpc2k/include/mach/hardware.h
+++ b/arch/arm/mach-lpc2k/include/mach/hardware.h
@@ -15,9 +15,11 @@
 /* Default memory size if no ATAGS found */
 #define MEM_SIZE	(SZ_32M)
 
+#define FAST_GPIO_BASE		0x3fffc000
 #define APB_TIMER0_BASE		0xe0004000
 #define APB_TIMER1_BASE		0xe0008000
 #define APB_UART0_BASE		0xe000c000
+#define APB_GPIO_BASE		0xe0028000
 #define APB_SCB_BASE		0xe01fc000
 #define APH_VIC_BASE		0xfffff000
 
diff --git a/arch/arm/mach-lpc2k/include/mach/irqs.h b/arch/arm/mach-lpc2k/include/mach/irqs.h
index 87b80dd..b7b2410 100644
--- a/arch/arm/mach-lpc2k/include/mach/irqs.h
+++ b/arch/arm/mach-lpc2k/include/mach/irqs.h
@@ -44,6 +44,73 @@
 #define IRQ_LPC2K_I2C2		30
 #define IRQ_LPC2K_I2S		31
 
-#define NR_IRQS			32
+#define IRQ_LPC2K_GPIO0		32
+#define IRQ_LPC2K_GPIO1		33
+#define IRQ_LPC2K_GPIO2		34
+#define IRQ_LPC2K_GPIO3		35
+#define IRQ_LPC2K_GPIO4		36
+#define IRQ_LPC2K_GPIO5		37
+#define IRQ_LPC2K_GPIO6		38
+#define IRQ_LPC2K_GPIO7		39
+#define IRQ_LPC2K_GPIO8		40
+#define IRQ_LPC2K_GPIO9		41
+#define IRQ_LPC2K_GPIO10	42
+#define IRQ_LPC2K_GPIO11	43
+#define IRQ_LPC2K_GPIO12	44
+#define IRQ_LPC2K_GPIO13	45
+#define IRQ_LPC2K_GPIO14	46
+#define IRQ_LPC2K_GPIO15	47
+#define IRQ_LPC2K_GPIO16	48
+#define IRQ_LPC2K_GPIO17	49
+#define IRQ_LPC2K_GPIO18	50
+#define IRQ_LPC2K_GPIO19	51
+#define IRQ_LPC2K_GPIO20	52
+#define IRQ_LPC2K_GPIO21	53
+#define IRQ_LPC2K_GPIO22	54
+#define IRQ_LPC2K_GPIO23	55
+#define IRQ_LPC2K_GPIO24	56
+#define IRQ_LPC2K_GPIO25	57
+#define IRQ_LPC2K_GPIO26	58
+#define IRQ_LPC2K_GPIO27	59
+#define IRQ_LPC2K_GPIO28	60
+#define IRQ_LPC2K_GPIO29	61
+#define IRQ_LPC2K_GPIO30	62
+#define IRQ_LPC2K_GPIO31	63
+
+/* GPIO P2 */
+#define IRQ_LPC2K_GPIO64	64
+#define IRQ_LPC2K_GPIO65	65
+#define IRQ_LPC2K_GPIO66	66
+#define IRQ_LPC2K_GPIO67	67
+#define IRQ_LPC2K_GPIO68	68
+#define IRQ_LPC2K_GPIO69	69
+#define IRQ_LPC2K_GPIO70	70
+#define IRQ_LPC2K_GPIO71	71
+#define IRQ_LPC2K_GPIO72	72
+#define IRQ_LPC2K_GPIO73	73
+#define IRQ_LPC2K_GPIO74	74
+#define IRQ_LPC2K_GPIO75	75
+#define IRQ_LPC2K_GPIO76	76
+#define IRQ_LPC2K_GPIO77	77
+#define IRQ_LPC2K_GPIO78	78
+#define IRQ_LPC2K_GPIO79	79
+#define IRQ_LPC2K_GPIO80	80
+#define IRQ_LPC2K_GPIO81	81
+#define IRQ_LPC2K_GPIO82	82
+#define IRQ_LPC2K_GPIO83	83
+#define IRQ_LPC2K_GPIO84	84
+#define IRQ_LPC2K_GPIO85	85
+#define IRQ_LPC2K_GPIO86	86
+#define IRQ_LPC2K_GPIO87	87
+#define IRQ_LPC2K_GPIO88	88
+#define IRQ_LPC2K_GPIO89	89
+#define IRQ_LPC2K_GPIO90	90
+#define IRQ_LPC2K_GPIO91	91
+#define IRQ_LPC2K_GPIO92	92
+#define IRQ_LPC2K_GPIO93	93
+#define IRQ_LPC2K_GPIO94	94
+#define IRQ_LPC2K_GPIO95	95
+
+#define NR_IRQS			96
 
 #endif /* MACH_LPC2K_IRQS_H */
-- 
1.7.1




More information about the linux-arm-kernel mailing list