[PATCH 2/2] netX: add generic GPIO suppport
Juergen Beisert
jbe at pengutronix.de
Fri Apr 27 07:19:06 EDT 2012
This family of processors has three kinds of GPIO/PIO/HIFPIO pins with
different features each. This patch adds generic (G)PIO support to hide
the details for the users.
Signed-off-by: Juergen Beisert <jbe at pengutronix.de>
---
arch/arm/mach-netx/Kconfig | 4 +
arch/arm/mach-netx/generic.c | 42 +++
arch/arm/mach-netx/include/mach/gpio.h | 27 ++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-netX.c | 464 ++++++++++++++++++++++++++++++++
5 files changed, 538 insertions(+)
create mode 100644 arch/arm/mach-netx/include/mach/gpio.h
create mode 100644 drivers/gpio/gpio-netX.c
diff --git a/arch/arm/mach-netx/Kconfig b/arch/arm/mach-netx/Kconfig
index 3d90ef1..6625598 100644
--- a/arch/arm/mach-netx/Kconfig
+++ b/arch/arm/mach-netx/Kconfig
@@ -1,6 +1,10 @@
menu "NetX Implementations"
depends on ARCH_NETX
+config ARCH_NETX_GPIOLIB
+ bool
+ select ARCH_REQUIRE_GPIOLIB
+
config MACH_NXDKN
bool "Enable Hilscher nxdkn Eval Board support"
depends on ARCH_NETX
diff --git a/arch/arm/mach-netx/generic.c b/arch/arm/mach-netx/generic.c
index 5a7b575..13eb4bf 100644
--- a/arch/arm/mach-netx/generic.c
+++ b/arch/arm/mach-netx/generic.c
@@ -58,7 +58,49 @@ static struct platform_device netx_rtc_device = {
.resource = netx_rtc_resources,
};
+static struct resource netx_gpio_res = {
+ .name = "gpio",
+ .start = NETX_PA_GPIO,
+ .end = NETX_PA_GPIO + 0xdf,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device netx_gpio_device = {
+ .name = "netx-gpio",
+ .num_resources = 1,
+ .resource = &netx_gpio_res,
+};
+
+static struct resource netx_pio_res = {
+ .name = "pio",
+ .start = NETX_PA_PIO,
+ .end = NETX_PA_PIO + 0x0f,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device netx_pio_device = {
+ .name = "netx-pio",
+ .num_resources = 1,
+ .resource = &netx_pio_res,
+};
+
+static struct resource netx_hifpio_res = {
+ .name = "hifpio",
+ .start = NETX_PA_EXTBUS + 0x20,
+ .end = NETX_PA_EXTBUS + 0x20 + 0x1b,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device netx_hifpio_device = {
+ .name = "netx-hifpio",
+ .num_resources = 1,
+ .resource = &netx_hifpio_res,
+};
+
static struct platform_device *devices[] __initdata = {
+ &netx_gpio_device,
+ &netx_pio_device,
+ &netx_hifpio_device,
&netx_rtc_device,
};
diff --git a/arch/arm/mach-netx/include/mach/gpio.h b/arch/arm/mach-netx/include/mach/gpio.h
new file mode 100644
index 0000000..640148b
--- /dev/null
+++ b/arch/arm/mach-netx/include/mach/gpio.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2012 Juergen Beisert <kernel at pengutronix.de>, Pengutronix
+ * On behalf of Hilscher GmbH, http://www.hilscher.com/
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#ifndef _MACH_NETX_GPIO_H_
+# define _MACH_NETX_GPIO_H_
+
+#define ARCH_NR_GPIOS 128
+
+#define NETX_GPIO_BASE_NO 0
+#define NETX_PIO_BASE_NO 16
+#define NETX_HIFPIO_BASE_NO 48
+
+#include <asm-generic/gpio.h>
+
+#endif /* _MACH_NETX_GPIO_H_ */
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 007f54b..10b7275 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o
obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o
obj-$(CONFIG_PLAT_NOMADIK) += gpio-nomadik.o
obj-$(CONFIG_ARCH_OMAP) += gpio-omap.o
+obj-$(CONFIG_ARCH_NETX) += gpio-netX.o
obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o
obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o
obj-$(CONFIG_GPIO_PCH) += gpio-pch.o
diff --git a/drivers/gpio/gpio-netX.c b/drivers/gpio/gpio-netX.c
new file mode 100644
index 0000000..9ba9d6c
--- /dev/null
+++ b/drivers/gpio/gpio-netX.c
@@ -0,0 +1,464 @@
+/*
+ * Copyright (c) 2012 Juergen Beisert <kernel at pengutronix.de>, Pengutronix
+ * On behalf of Hilscher GmbH, http://www.hilscher.com/
+ *
+ * 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.
+ *
+ * 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/init.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#define NETX_GPIO_DRIVER_NAME "netx-gpio"
+#define NETX_GPIO_CFG(x) ((x) << 2)
+# define NETX_GPIO_CFG_MASK 0x03
+# define NETX_GPIO_CFG_IN 0x0
+# define NETX_GPIO_CFG_OUT 0x1
+# define NETX_GPIO_CFG_MODE_IN (0x0 << 3)
+# define NETX_GPIO_CFG_MODE_OUT (0x2 << 3)
+#define NETX_GPIO_OUT 0xc8
+#define NETX_GPIO_IN 0xcc
+
+#define NETX_PIO_DRIVER_NAME "netx-pio"
+#define NETX_PIO_IN 0x0
+#define NETX_PIO_OUT 0x4
+#define NETX_PIO_OUT_EN 0x8
+
+#define NETX_HIFPIO_DRIVER_NAME "netx-hifpio"
+#define NETX_HIFPIO_MODE 0x00
+# define NETX_HIFPIO_MODE_SAMPLE_EN (0x1 << 30)
+#define NETX_HIFPIO_DRV_EN 0x04
+#define NETX_HIFPIO_DATA 0x08
+
+struct netx_io_port {
+ void __iomem *base;
+ spinlock_t lock;
+ struct gpio_chip chip;
+};
+
+#define to_netx_io_port(x) container_of((x), struct netx_io_port, chip)
+
+/* ---- GPIOs share pins with the peripherals, interrupts and counter ----- */
+
+static int netx_gpio_get(struct gpio_chip *chip, unsigned pin)
+{
+ struct netx_io_port *port = to_netx_io_port(chip);
+ u32 mask = 1 << pin;
+
+ return !!(readl(port->base + NETX_GPIO_IN) & mask);
+}
+
+static int netx_gpio_input(struct gpio_chip *chip, unsigned pin)
+{
+ struct netx_io_port *port = to_netx_io_port(chip);
+
+ /* swtch off every different function than GPIO */
+ writel(NETX_GPIO_CFG_IN | NETX_GPIO_CFG_MODE_IN,
+ port->base + NETX_GPIO_CFG(pin));
+
+ return 0;
+}
+
+static void netx_gpio_set(struct gpio_chip *chip, unsigned pin, int value)
+{
+ struct netx_io_port *port = to_netx_io_port(chip);
+ u32 reg, mask = 1 << pin;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ reg = readl(port->base + NETX_GPIO_OUT) & ~mask;
+ writel(reg | (!!value) << pin, port->base + NETX_GPIO_OUT);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int netx_gpio_output(struct gpio_chip *chip, unsigned pin, int value)
+{
+ struct netx_io_port *port = to_netx_io_port(chip);
+
+ netx_gpio_set(chip, pin, value);
+
+ /* swtch off every different function than GPIO */
+ writel(NETX_GPIO_CFG_OUT | NETX_GPIO_CFG_MODE_OUT,
+ port->base + NETX_GPIO_CFG(pin));
+
+ return 0;
+}
+
+static int __devinit netx_gpio_probe(struct platform_device *pd)
+{
+ struct netx_io_port *port;
+ struct resource *res;
+ int err;
+
+ port = kzalloc(sizeof(struct netx_io_port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ res = platform_get_resource(pd, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pd->dev, "No resource defined\n");
+ err = -ENODEV;
+ goto out_kfree;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), pd->name)) {
+ dev_err(&pd->dev, "Resource already in use\n");
+ err = -EBUSY;
+ goto out_kfree;
+ }
+
+ port->base = ioremap(res->start, resource_size(res));
+ if (!port->base) {
+ dev_err(&pd->dev, "Failed to map the registers\n");
+ err = -ENOMEM;
+ goto out_release_mem;
+ }
+
+ port->chip.base = NETX_GPIO_BASE_NO;
+ port->chip.ngpio = 16;
+ port->chip.label = "netX-GPIO";
+ port->chip.dev = &pd->dev;
+ port->chip.owner = THIS_MODULE;
+ port->chip.can_sleep = false;
+
+ port->chip.direction_input = netx_gpio_input;
+ port->chip.get = netx_gpio_get;
+ port->chip.direction_output = netx_gpio_output;
+ port->chip.set = netx_gpio_set;
+
+ spin_lock_init(&port->lock);
+
+ err = gpiochip_add(&port->chip);
+ if (err)
+ goto out_iounmap;
+
+ return 0;
+
+out_iounmap:
+ iounmap(port->base);
+out_release_mem:
+ release_mem_region(res->start, resource_size(res));
+out_kfree:
+ kfree(port);
+ dev_err(&pd->dev, "%s failed with errno %d\n", __func__, err);
+ return err;
+}
+
+static struct platform_driver netx_gpio_driver = {
+ .driver = {
+ .name = NETX_GPIO_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = netx_gpio_probe,
+};
+
+/* ----------- PIOs share pins with peripherals ------------------- */
+
+static int netx_pio_get(struct gpio_chip *chip, unsigned pin)
+{
+ struct netx_io_port *port = to_netx_io_port(chip);
+ u32 mask = 1 << pin;
+
+ return !!(readl(port->base + NETX_PIO_IN) & mask);
+}
+
+static int netx_pio_input(struct gpio_chip *chip, unsigned pin)
+{
+ struct netx_io_port *port = to_netx_io_port(chip);
+ u32 mask = 1 << pin, reg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ reg = readl(port->base + NETX_PIO_OUT_EN) & ~mask;
+ writel(reg, port->base + NETX_PIO_OUT_EN);
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return 0;
+}
+
+static void netx_pio_set(struct gpio_chip *chip, unsigned pin, int value)
+{
+ struct netx_io_port *port = to_netx_io_port(chip);
+ u32 reg, mask = 1 << pin;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ reg = readl(port->base + NETX_PIO_OUT) & ~mask;
+ writel(reg | (!!value) << pin, port->base + NETX_PIO_OUT);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int netx_pio_output(struct gpio_chip *chip, unsigned pin, int value)
+{
+ struct netx_io_port *port = to_netx_io_port(chip);
+ u32 mask = 1 << pin, reg;
+ unsigned long flags;
+
+ netx_gpio_set(chip, pin, value);
+
+ spin_lock_irqsave(&port->lock, flags);
+ reg = readl(port->base + NETX_PIO_OUT_EN) | mask;
+ writel(reg, port->base + NETX_PIO_OUT_EN);
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return 0;
+}
+
+static int __devinit netx_pio_probe(struct platform_device *pd)
+{
+ struct netx_io_port *port;
+ struct resource *res;
+ int err;
+
+ port = kzalloc(sizeof(struct netx_io_port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ res = platform_get_resource(pd, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pd->dev, "No resource defined\n");
+ err = -ENODEV;
+ goto out_kfree;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), pd->name)) {
+ dev_err(&pd->dev, "Resource already in use\n");
+ err = -EBUSY;
+ goto out_kfree;
+ }
+
+ port->base = ioremap(res->start, resource_size(res));
+ if (!port->base) {
+ dev_err(&pd->dev, "Failed to map the registers\n");
+ err = -ENOMEM;
+ goto out_release_mem;
+ }
+
+ port->chip.base = NETX_PIO_BASE_NO;
+ port->chip.ngpio = 31; /* note: really! 31 */
+ port->chip.label = "netX-PIO";
+ port->chip.dev = &pd->dev;
+ port->chip.owner = THIS_MODULE;
+ port->chip.can_sleep = false;
+
+ port->chip.direction_input = netx_pio_input;
+ port->chip.get = netx_pio_get;
+ port->chip.direction_output = netx_pio_output;
+ port->chip.set = netx_pio_set;
+
+ spin_lock_init(&port->lock);
+
+ err = gpiochip_add(&port->chip);
+ if (err)
+ goto out_iounmap;
+
+ return 0;
+
+out_iounmap:
+ iounmap(port->base);
+out_release_mem:
+ release_mem_region(res->start, resource_size(res));
+out_kfree:
+ kfree(port);
+ dev_err(&pd->dev, "%s failed with errno %d\n", __func__, err);
+ return err;
+}
+
+static struct platform_driver netx_pio_driver = {
+ .driver = {
+ .name = NETX_PIO_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = netx_pio_probe,
+};
+
+/* ---------- HIFPIOs share pins with the host interface ----------------- */
+
+static void netx_hifpio_enable_sampling_ondemand(struct gpio_chip *chip)
+{
+ struct netx_io_port *port = to_netx_io_port(chip);
+ u32 mode0, mode1, en0, en1, check0, check1;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ mode0 = readl(port->base + NETX_HIFPIO_MODE);
+ mode1 = readl(port->base + NETX_HIFPIO_MODE + 0x10) &
+ ~NETX_HIFPIO_MODE_SAMPLE_EN;
+ en0 = readl(port->base + NETX_HIFPIO_DRV_EN);
+ en1 = readl(port->base + NETX_HIFPIO_DRV_EN + 0x10);
+ check0 = ~mode0 & ~en0;
+ check1 = ~(mode1 | 0xffe00000) & ~(en1 | 0xffe00000);
+ if (check0 || check1) {
+ /* at least one PIO is enabled and configured for input */
+ writel(mode1 | NETX_HIFPIO_MODE_SAMPLE_EN,
+ port->base + NETX_HIFPIO_MODE + 0x10);
+ } else
+ writel(mode1, port->base + NETX_HIFPIO_MODE + 0x10);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int netx_hifpio_get(struct gpio_chip *chip, unsigned pin)
+{
+ struct netx_io_port *port = to_netx_io_port(chip);
+ unsigned offset = pin > 31 ? 0x10 : 0x00;
+ unsigned shift = pin > 31 ? pin - 32 : pin;
+ u32 mask = 1 << shift;
+
+ return !!(readl(port->base + NETX_HIFPIO_DATA + offset) & mask);
+}
+
+static int netx_hifpio_input(struct gpio_chip *chip, unsigned pin)
+{
+ struct netx_io_port *port = to_netx_io_port(chip);
+ unsigned offset = pin > 31 ? 0x10 : 0x00;
+ unsigned shift = pin > 31 ? pin - 32 : pin;
+ u32 mask = 1 << shift, mode, drv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ /* swtch off every different function than PIO */
+ mode = readl(port->base + NETX_HIFPIO_MODE + offset) & ~mask;
+ drv = readl(port->base + NETX_HIFPIO_DRV_EN + offset) & ~mask;
+
+ writel(drv, port->base + NETX_HIFPIO_DRV_EN + offset);
+ writel(mode, port->base + NETX_HIFPIO_MODE + offset);
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ netx_hifpio_enable_sampling_ondemand(chip);
+
+ return 0;
+}
+
+static void netx_hifpio_set(struct gpio_chip *chip, unsigned pin, int value)
+{
+ struct netx_io_port *port = to_netx_io_port(chip);
+ unsigned offset = pin > 31 ? 0x10 : 0x00;
+ unsigned shift = pin > 31 ? pin - 32 : pin;
+ u32 mask = 1 << shift, reg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ reg = readl(port->base + NETX_HIFPIO_DATA + offset) & ~mask;
+ if (value)
+ reg |= mask;
+ writel(reg, port->base + NETX_HIFPIO_DATA + offset);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int netx_hifpio_output(struct gpio_chip *chip, unsigned pin, int value)
+{
+ struct netx_io_port *port = to_netx_io_port(chip);
+ unsigned offset = pin > 31 ? 0x10 : 0x00;
+ unsigned shift = pin > 31 ? pin - 32 : pin;
+ u32 mask = 1 << shift, mode, drv;
+ unsigned long flags;
+
+ netx_gpio_set(chip, pin, value);
+
+ spin_lock_irqsave(&port->lock, flags);
+ /* swtch off every different function than PIO */
+ mode = readl(port->base + NETX_HIFPIO_MODE + offset) & ~mask;
+ /* enable the output driver */
+ drv = readl(port->base + NETX_HIFPIO_DRV_EN + offset) | mask;
+
+ writel(drv, port->base + NETX_HIFPIO_DRV_EN + offset);
+ writel(mode, port->base + NETX_HIFPIO_MODE + offset);
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ netx_hifpio_enable_sampling_ondemand(chip);
+
+ return 0;
+}
+
+static int __devinit netx_hifpio_probe(struct platform_device *pd)
+{
+ struct netx_io_port *port;
+ struct resource *res;
+ int err;
+
+ port = kzalloc(sizeof(struct netx_io_port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ res = platform_get_resource(pd, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pd->dev, "No resource defined\n");
+ err = -ENODEV;
+ goto out_kfree;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), pd->name)) {
+ dev_err(&pd->dev, "Resource already in use\n");
+ err = -EBUSY;
+ goto out_kfree;
+ }
+
+ port->base = ioremap(res->start, resource_size(res));
+ if (!port->base) {
+ dev_err(&pd->dev, "Failed to map the registers\n");
+ err = -ENOMEM;
+ goto out_release_mem;
+ }
+
+ port->chip.base = NETX_HIFPIO_BASE_NO;
+ port->chip.ngpio = 53;
+ port->chip.label = "netX-HIFPIO";
+ port->chip.dev = &pd->dev;
+ port->chip.owner = THIS_MODULE;
+ port->chip.can_sleep = false;
+
+ port->chip.direction_input = netx_hifpio_input;
+ port->chip.get = netx_hifpio_get;
+ port->chip.direction_output = netx_hifpio_output;
+ port->chip.set = netx_hifpio_set;
+
+ spin_lock_init(&port->lock);
+
+ err = gpiochip_add(&port->chip);
+ if (err)
+ goto out_iounmap;
+
+ return 0;
+
+out_iounmap:
+ iounmap(port->base);
+out_release_mem:
+ release_mem_region(res->start, resource_size(res));
+out_kfree:
+ kfree(port);
+ dev_err(&pd->dev, "%s failed with errno %d\n", __func__, err);
+ return err;
+}
+
+static struct platform_driver netx_hifpio_driver = {
+ .driver = {
+ .name = NETX_HIFPIO_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = netx_hifpio_probe,
+};
+
+static int __init netx_gpio_init(void)
+{
+ platform_driver_register(&netx_gpio_driver);
+ platform_driver_register(&netx_pio_driver);
+ platform_driver_register(&netx_hifpio_driver);
+ return 0;
+}
+postcore_initcall(netx_gpio_init);
+
+MODULE_AUTHOR("Juergen Beisert <kernel at pengutronix.de>");
+MODULE_DESCRIPTION("Hilscher netX GPIO");
+MODULE_LICENSE("GPL");
--
1.7.10
More information about the linux-arm-kernel
mailing list