[PATCH v6 3/3] gpio: TS-5500 GPIO support

From: Vivien Didelot
Date: Thu Apr 12 2012 - 20:52:09 EST


From: Jerome Oufella <jerome.oufella@xxxxxxxxxxxxxxxxxxxx>

Signed-off-by: Jerome Oufella <jerome.oufella@xxxxxxxxxxxxxxxxxxxx>
Signed-off-by: Vivien Didelot <vivien.didelot@xxxxxxxxxxxxxxxxxxxx>
---
MAINTAINERS | 2 +
arch/x86/include/asm/ts5500.h | 62 ++++++++
drivers/gpio/Kconfig | 7 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-ts5500.c | 347 +++++++++++++++++++++++++++++++++++++++++
5 files changed, 419 insertions(+)
create mode 100644 arch/x86/include/asm/ts5500.h
create mode 100644 drivers/gpio/gpio-ts5500.c

diff --git a/MAINTAINERS b/MAINTAINERS
index b126129..b7b5d95 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6624,6 +6624,8 @@ TECHNOLOGIC SYSTEMS TS-5500 MACHINE SUPPORT
M: Savoir-faire Linux Inc. <kernel@xxxxxxxxxxxxxxxxxxxx>
S: Maintained
F: arch/x86/platform/ts5500/
+F: arch/x86/include/asm/ts5500.h
+F: drivers/gpio/gpio-ts5500.c

TEGRA SUPPORT
M: Colin Cross <ccross@xxxxxxxxxxx>
diff --git a/arch/x86/include/asm/ts5500.h b/arch/x86/include/asm/ts5500.h
new file mode 100644
index 0000000..baf136c
--- /dev/null
+++ b/arch/x86/include/asm/ts5500.h
@@ -0,0 +1,62 @@
+/*
+ * Technologic Systems TS-5500 GPIO (DIO) definitions
+ *
+ * Copyright (c) 2010-2012 Savoir-faire Linux Inc.
+ * Jerome Oufella <jerome.oufella@xxxxxxxxxxxxxxxxxxxx>
+ *
+ * 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.
+ */
+
+#ifndef _TS5500_H
+#define _TS5500_H
+
+#define TS5500_DIO1_0 0
+#define TS5500_DIO1_1 1
+#define TS5500_DIO1_2 2
+#define TS5500_DIO1_3 3
+#define TS5500_DIO1_4 4
+#define TS5500_DIO1_5 5
+#define TS5500_DIO1_6 6
+#define TS5500_DIO1_7 7
+#define TS5500_DIO1_8 8
+#define TS5500_DIO1_9 9
+#define TS5500_DIO1_10 10
+#define TS5500_DIO1_11 11
+#define TS5500_DIO1_12 12
+#define TS5500_DIO1_13 13
+
+#define TS5500_DIO2_0 14
+#define TS5500_DIO2_1 15
+#define TS5500_DIO2_2 16
+#define TS5500_DIO2_3 17
+#define TS5500_DIO2_4 18
+#define TS5500_DIO2_5 19
+#define TS5500_DIO2_6 20
+#define TS5500_DIO2_7 21
+#define TS5500_DIO2_8 22
+#define TS5500_DIO2_9 23
+#define TS5500_DIO2_10 24
+#define TS5500_DIO2_11 25
+/* #define TS5500_DIO2_12 - Keep commented out as it simply doesn't exist. */
+#define TS5500_DIO2_13 26
+
+#define TS5500_LCD_0 27
+#define TS5500_LCD_1 28
+#define TS5500_LCD_2 29
+#define TS5500_LCD_3 30
+#define TS5500_LCD_4 31
+#define TS5500_LCD_5 32
+#define TS5500_LCD_6 33
+#define TS5500_LCD_7 34
+#define TS5500_LCD_EN 35
+#define TS5500_LCD_RS 36
+#define TS5500_LCD_WR 37
+
+/* Lines that can trigger IRQs */
+#define TS5500_DIO1_13_IRQ 7
+#define TS5500_DIO2_13_IRQ 6
+#define TS5500_LCD_RS_IRQ 1
+
+#endif /* _TS5500_H */
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index edadbda..a19b0f3 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -319,6 +319,13 @@ config GPIO_TPS65912
help
This driver supports TPS65912 gpio chip

+config GPIO_TS5500
+ tristate "TS-5500 GPIOs"
+ depends on TS5500
+ help
+ This driver supports the DIO headers for GPIO usage on the Technologic
+ Systems TS-5500 platform.
+
config GPIO_TWL4030
tristate "TWL4030, TWL5030, and TPS659x0 GPIOs"
depends on TWL4030_CORE
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 007f54b..30cbd03 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -56,6 +56,7 @@ obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o
obj-$(CONFIG_ARCH_DAVINCI_TNETV107X) += gpio-tnetv107x.o
obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o
obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o
+obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o
obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o
obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o
obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o
diff --git a/drivers/gpio/gpio-ts5500.c b/drivers/gpio/gpio-ts5500.c
new file mode 100644
index 0000000..01f34d8
--- /dev/null
+++ b/drivers/gpio/gpio-ts5500.c
@@ -0,0 +1,347 @@
+/*
+ * GPIO (DIO) driver for Technologic Systems TS-5500
+ *
+ * TS-5500 board has 38 GPIOs referred to as DIOs in the product's literature.
+ *
+ * Copyright (c) 2010-2012 Savoir-faire Linux Inc.
+ * Jerome Oufella <jerome.oufella@xxxxxxxxxxxxxxxxxxxx>
+ * Vivien Didelot <vivien.didelot@xxxxxxxxxxxxxxxxxxxx>
+ *
+ * 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/slab.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <asm/ts5500.h>
+
+static int dio1_irq = 1;
+module_param(dio1_irq, int, 0644);
+MODULE_PARM_DESC(dio1_irq, "Enable usage of IRQ7 for any DIO1 line"
+ " (default 1)");
+
+static int dio2_irq;
+module_param(dio2_irq, int, 0644);
+MODULE_PARM_DESC(dio2_irq, "Enable usage of IRQ6 for any DIO2 line"
+ " (default 0)");
+
+static int lcd_irq;
+module_param(lcd_irq, int, 0644);
+MODULE_PARM_DESC(lcd_irq, "Enable usage of IRQ1 for any LCD line"
+ " (default 0)");
+
+static int use_lcdio;
+module_param(use_lcdio, int, 0644);
+MODULE_PARM_DESC(use_lcdio, "Enable usage of the LCD header for DIO operation"
+ " (default 0)");
+
+static DEFINE_SPINLOCK(gpio_lock);
+
+static inline void port_bit_set(u8 addr, int bit)
+{
+ u8 var;
+
+ var = inb(addr);
+ var |= (1 << bit);
+ outb(var, addr);
+}
+
+static inline void port_bit_clear(u8 addr, int bit)
+{
+ u8 var;
+
+ var = inb(addr);
+ var &= ~(1 << bit);
+ outb(var, addr);
+}
+
+/* "DIO" line to IO port mapping table for line's value */
+static const unsigned long line_to_port_map[] = {
+ 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, /* DIO1_[0-7] */
+ 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, /* DIO1_[8-13] */
+ 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, /* DIO2_[0-7] */
+ 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, /* DIO2_[8-13] */
+ 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, /* LCD_[0-7] */
+ 0x73, 0x73, 0x73 /* LCD_{EN,RS,WR} */
+};
+
+/* "DIO" line to IO port's bit map for line's value */
+static const int line_to_bit_map[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, /* DIO1_[0-7] */
+ 0, 1, 2, 3, 4, 5, /* DIO1_[8-13] */
+ 0, 1, 2, 3, 4, 5, 6, 7, /* DIO2_[0-7] */
+ 0, 1, 2, 3, 4, 5, /* DIO2_[8-13] */
+ 0, 1, 2, 3, 4, 5, 6, 7, /* LCD_[0-7] */
+ 0, 7, 6 /* LCD_{EN,RS,WR} */
+};
+
+/* "DIO" line's direction control mapping table */
+static const unsigned long line_to_dir_map[] = {
+ 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, /* DIO1_[0-7] */
+ 0x7A, 0x7A, 0x7A, 0x7A, 0, 0, /* DIO1_[8-13] */
+ 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, /* DIO2_[0-7] */
+ 0x7D, 0x7D, 0x7D, 0x7D, 0, 0, /* DIO2_[8-13] */
+ 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, /* LCD_[0-7] */
+ 0, 0, 0 /* LCD_{EN,RS,WR} */
+};
+
+/* "DIO" line's direction control bit-mapping table */
+static const int line_to_dir_bit_map[] = {
+ 0, 0, 0, 0, 1, 1, 1, 1, /* DIO1_[0-7] */
+ 5, 5, 5, 5, -1, -1, /* DIO1_[8-13] */
+ 0, 0, 0, 0, 1, 1, 1, 1, /* DIO2_[0-7] */
+ 5, 5, 5, 5, -1, -1, /* DIO2_[8-13] */
+ 2, 2, 2, 2, 3, 3, 3, 3, /* LCD_[0-7] */
+ -1, -1, -1 /* LCD_{EN,RS,WR} */
+};
+
+static int ts5500_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ unsigned long dir_reg, flags;
+ int dir_bit;
+
+ /* Some lines cannot be configured as input */
+ if (offset == TS5500_LCD_EN)
+ return -ENXIO;
+
+ dir_reg = line_to_dir_map[offset];
+ dir_bit = line_to_dir_bit_map[offset];
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ port_bit_clear(dir_reg, dir_bit);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return 0;
+}
+
+static int ts5500_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ unsigned long ioaddr;
+ int bitno;
+
+ ioaddr = line_to_port_map[offset];
+ bitno = line_to_bit_map[offset];
+
+ return (inb(ioaddr) >> bitno) & 0x1;
+}
+
+static int ts5500_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int val)
+{
+ unsigned long dir_reg, ioaddr, flags;
+ int dir_bit, bitno;
+
+ /* Some lines cannot be configured as output */
+ switch (offset) {
+ case TS5500_DIO1_12:
+ case TS5500_DIO1_13:
+ case TS5500_DIO2_13:
+ case TS5500_LCD_RS:
+ case TS5500_LCD_WR:
+ return -ENXIO;
+ default:
+ break;
+ }
+
+ dir_reg = line_to_dir_map[offset];
+ dir_bit = line_to_dir_bit_map[offset];
+ ioaddr = line_to_port_map[offset];
+ bitno = line_to_bit_map[offset];
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ port_bit_set(dir_reg, dir_bit);
+ if (val)
+ port_bit_set(ioaddr, bitno);
+ else
+ port_bit_clear(ioaddr, bitno);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return 0;
+}
+
+static void ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+ int bitno;
+ unsigned long ioaddr, flags;
+
+ ioaddr = line_to_port_map[offset];
+ bitno = line_to_bit_map[offset];
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ if (val)
+ port_bit_set(ioaddr, bitno);
+ else
+ port_bit_clear(ioaddr, bitno);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+}
+
+static int ts5500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ /* Only a few lines are IRQ-capable */
+ if (offset == TS5500_DIO1_13)
+ return TS5500_DIO1_13_IRQ;
+ if (offset == TS5500_DIO2_13)
+ return TS5500_DIO2_13_IRQ;
+ if (offset == TS5500_LCD_RS)
+ return TS5500_LCD_RS_IRQ;
+
+ /* IRQ line may be bridged with another DIO line from the same header */
+ if (dio1_irq && offset >= TS5500_DIO1_0 && offset < TS5500_DIO1_13)
+ return TS5500_DIO1_13_IRQ;
+ if (dio2_irq && offset >= TS5500_DIO2_0 && offset < TS5500_DIO2_13)
+ return TS5500_DIO2_13_IRQ;
+ if (lcd_irq && offset >= TS5500_LCD_0 && offset <= TS5500_LCD_WR)
+ return TS5500_LCD_RS_IRQ;
+
+ return -ENXIO;
+}
+
+static struct gpio_chip ts5500_gpio_chip = {
+ .label = "ts5500-gpio",
+ .owner = THIS_MODULE,
+ .direction_input = ts5500_gpio_direction_input,
+ .get = ts5500_gpio_get,
+ .direction_output = ts5500_gpio_direction_output,
+ .set = ts5500_gpio_set,
+ .to_irq = ts5500_gpio_to_irq,
+ .base = TS5500_DIO1_0,
+};
+
+static void ts5500_gpio_release(struct device *dev)
+{
+ /* noop */
+}
+
+static struct platform_device ts5500_gpio_pdev = {
+ .name = "ts5500-gpio",
+ .id = -1,
+ .dev = {
+ .release = ts5500_gpio_release,
+ },
+};
+
+static int __devinit ts5500_gpio_probe(struct platform_device *pdev)
+{
+ int ret;
+ unsigned long flags;
+
+ if (pdev == NULL)
+ return -ENODEV;
+
+ if (!request_region(0x7A, 3, "ts5500-gpio-DIO1")) {
+ dev_err(&pdev->dev, "failed to request I/O port 0x7A-7C\n");
+ return -EBUSY;
+ }
+
+ if (!request_region(0x7D, 3, "ts5500-gpio-DIO2")) {
+ dev_err(&pdev->dev, "failed to request I/O port 0x7D-7F\n");
+ ret = -EBUSY;
+ goto release_dio1;
+ }
+
+ if (use_lcdio && !request_region(0x72, 2, "ts5500-gpio-LCD")) {
+ dev_err(&pdev->dev, "failed to request I/O port 0x72-73\n");
+ ret = -EBUSY;
+ goto release_dio2;
+ }
+
+ if (use_lcdio)
+ ts5500_gpio_chip.ngpio = TS5500_LCD_WR + 1;
+ else
+ ts5500_gpio_chip.ngpio = TS5500_DIO2_13 + 1;
+
+ ret = gpiochip_add(&ts5500_gpio_chip);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register the gpio chip\n");
+ goto release_lcd;
+ }
+
+ /* Enable IRQ generation */
+ spin_lock_irqsave(&gpio_lock, flags);
+ port_bit_set(0x7A, 7); /* DIO1_13 on IRQ7 */
+ port_bit_set(0x7D, 7); /* DIO2_13 on IRQ6 */
+ if (use_lcdio) {
+ port_bit_clear(0x7D, 4); /* LCD header usage as DIO */
+ port_bit_set(0x7D, 6); /* LCD_RS on IRQ1 */
+ }
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return 0;
+
+release_lcd:
+ if (use_lcdio)
+ release_region(0x72, 2);
+release_dio2:
+ release_region(0x7D, 3);
+release_dio1:
+ release_region(0x7A, 3);
+
+ return ret;
+}
+
+static int __devexit ts5500_gpio_remove(struct platform_device *pdev)
+{
+ int ret;
+ unsigned long flags;
+
+ /* Disable IRQ generation */
+ spin_lock_irqsave(&gpio_lock, flags);
+ port_bit_clear(0x7A, 7);
+ port_bit_clear(0x7D, 7);
+ if (use_lcdio)
+ port_bit_clear(0x7D, 6);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ release_region(0x7A, 3);
+ release_region(0x7D, 3);
+ if (use_lcdio)
+ release_region(0x72, 2);
+
+ ret = gpiochip_remove(&ts5500_gpio_chip);
+ if (ret)
+ dev_err(&pdev->dev, "failed to remove the gpio chip\n");
+
+ return ret;
+}
+
+static struct platform_driver ts5500_gpio_driver = {
+ .driver = {
+ .name = "ts5500-gpio",
+ .owner = THIS_MODULE,
+ },
+ .probe = ts5500_gpio_probe,
+ .remove = __devexit_p(ts5500_gpio_remove),
+};
+
+static int __init ts5500_gpio_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&ts5500_gpio_driver);
+ if (ret)
+ return ret;
+
+ ret = platform_device_register(&ts5500_gpio_pdev);
+ if (ret) {
+ platform_driver_unregister(&ts5500_gpio_driver);
+ return ret;
+ }
+
+ return 0;
+}
+module_init(ts5500_gpio_init);
+
+static void __exit ts5500_gpio_exit(void)
+{
+ platform_driver_unregister(&ts5500_gpio_driver);
+ platform_device_unregister(&ts5500_gpio_pdev);
+}
+module_exit(ts5500_gpio_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@xxxxxxxxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("Technologic Systems TS-5500, GPIO/DIO driver");
--
1.7.9.2

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/