lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Wed, 18 Jan 2012 18:52:09 -0500
From:	Vivien Didelot <vivien.didelot@...oirfairelinux.com>
To:	x86@...nel.org
Cc:	Jerome Oufella <jerome.oufella@...oirfairelinux.com>,
	"Ingo Molnar" <mingo@...hat.com>,
	"Thomas Gleixner" <tglx@...utronix.de>,
	"H. Peter Anvin" <hpa@...or.com>, linux-kernel@...r.kernel.org,
	"Vivien Didelot" <vivien.didelot@...oirfairelinux.com>
Subject: [PATCH v4 2/4] x86/platform: (TS-5500) add GPIO support

From: Jerome Oufella <jerome.oufella@...oirfairelinux.com>

Signed-off-by: Jerome Oufella <jerome.oufella@...oirfairelinux.com>
Signed-off-by: Vivien Didelot <vivien.didelot@...oirfairelinux.com>
---
 arch/x86/platform/ts5500/Kconfig       |    7 +
 arch/x86/platform/ts5500/Makefile      |    1 +
 arch/x86/platform/ts5500/ts5500_gpio.c |  479 ++++++++++++++++++++++++++++++++
 arch/x86/platform/ts5500/ts5500_gpio.h |   60 ++++
 4 files changed, 547 insertions(+), 0 deletions(-)
 create mode 100644 arch/x86/platform/ts5500/ts5500_gpio.c
 create mode 100644 arch/x86/platform/ts5500/ts5500_gpio.h

diff --git a/arch/x86/platform/ts5500/Kconfig b/arch/x86/platform/ts5500/Kconfig
index 929b617..62095c9 100644
--- a/arch/x86/platform/ts5500/Kconfig
+++ b/arch/x86/platform/ts5500/Kconfig
@@ -6,3 +6,10 @@ config TS5500
 	  Add support for the Technologic Systems TS-5500 platform.
 
 	  If you have a TS-5500, say Y here.
+
+config TS5500_GPIO
+	tristate "TS-5500 GPIO support"
+	depends on TS5500 && GENERIC_GPIO
+	default y
+	help
+	  This enables support for the DIO headers for GPIO usage.
diff --git a/arch/x86/platform/ts5500/Makefile b/arch/x86/platform/ts5500/Makefile
index 0a689a7..71c1398 100644
--- a/arch/x86/platform/ts5500/Makefile
+++ b/arch/x86/platform/ts5500/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_TS5500)			+= ts5500.o
+obj-$(CONFIG_TS5500_GPIO)		+= ts5500_gpio.o
diff --git a/arch/x86/platform/ts5500/ts5500_gpio.c b/arch/x86/platform/ts5500/ts5500_gpio.c
new file mode 100644
index 0000000..46dadc4
--- /dev/null
+++ b/arch/x86/platform/ts5500/ts5500_gpio.c
@@ -0,0 +1,479 @@
+/*
+ * GPIO (DIO) driver for Technologic Systems TS-5500
+ *
+ * Copyright (c) 2010-2012 Savoir-faire Linux Inc.
+ *	Jerome Oufella <jerome.oufella@...oirfairelinux.com>
+ *
+ * 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.
+ *
+ * TS-5500 board has 38 GPIOs referred to as DIOs in the product's literature.
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include "ts5500_gpio.h"
+
+static void port_bit_set(u8 addr, int bit)
+{
+	u8 var;
+	var = inb(addr);
+	var |= (1 << bit);
+	outb(var, addr);
+}
+
+static 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, LCD_RS, LCD_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, LCD_RS, LCD_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, LCD_RS, LCD_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, LCD_RS, LCD_WR */
+};
+
+/* This array is used to track requests for our GPIO lines */
+static int requested_gpios[TS5500_LCD_WR + 1];
+
+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 = 0;
+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 = 0;
+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 = 0;
+module_param(use_lcdio, int, 0644);
+MODULE_PARM_DESC(use_lcdio, "Enable usage of the LCD header for DIO operation"
+		 " (default 0).");
+
+/**
+ * struct ts5500_drvdata - Driver data
+ * @master:		Device.
+ * @gpio_chip:		GPIO chip.
+ * @gpio_lock:		Read/Write Mutex.
+ */
+struct ts5500_drvdata {
+	struct device *master;
+	struct gpio_chip gpio_chip;
+	struct mutex gpio_lock;
+};
+
+static int ts5500_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+	struct ts5500_drvdata *drvdata;
+
+	drvdata = container_of(chip, struct ts5500_drvdata, gpio_chip);
+
+	mutex_lock(&drvdata->gpio_lock);
+	if (requested_gpios[offset]) {
+		mutex_unlock(&drvdata->gpio_lock);
+		return -EBUSY;
+	}
+	requested_gpios[offset] = 1;
+	mutex_unlock(&drvdata->gpio_lock);
+
+	return 0;
+}
+
+static void ts5500_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+	struct ts5500_drvdata *drvdata;
+
+	drvdata = container_of(chip, struct ts5500_drvdata, gpio_chip);
+
+	mutex_lock(&drvdata->gpio_lock);
+	requested_gpios[offset] = 0;
+	mutex_unlock(&drvdata->gpio_lock);
+}
+
+static int ts5500_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	unsigned long ioaddr;
+	u8 byte;
+	int bitno;
+	struct ts5500_drvdata *drvdata;
+
+	drvdata = container_of(chip, struct ts5500_drvdata, gpio_chip);
+
+	/* Some lines are output-only and cannot be read */
+	if (offset == TS5500_LCD_EN || offset > chip->ngpio)
+		return -ENXIO;
+
+	ioaddr = line_to_port_map[offset];
+	bitno = line_to_bit_map[offset];
+	byte = inb(ioaddr);
+
+	return (byte >> bitno) & 0x1;
+}
+
+static void ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+	int bitno;
+	unsigned long ioaddr;
+	struct ts5500_drvdata *drvdata;
+
+	drvdata = container_of(chip, struct ts5500_drvdata, gpio_chip);
+
+	/* Some lines just can't be set */
+	switch (offset) {
+	case TS5500_DIO1_12:
+	case TS5500_DIO1_13:
+	case TS5500_DIO2_13:
+	case TS5500_LCD_RS:
+	case TS5500_LCD_WR:
+		return;
+	default:
+		if (offset > chip->ngpio)
+			return;
+		break;
+	}
+
+	/* Get io port and bit for 'offset' */
+	ioaddr = line_to_port_map[offset];
+	bitno = line_to_bit_map[offset];
+
+	mutex_lock(&drvdata->gpio_lock);
+	if (val == 0)
+		port_bit_clear(ioaddr, bitno);
+	else
+		port_bit_set(ioaddr, bitno);
+	mutex_unlock(&drvdata->gpio_lock);
+}
+
+static int ts5500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	/* Only a few lines are IRQ-Capable */
+	switch (offset) {
+	case TS5500_DIO1_13:
+		return TS5500_DIO1_13_IRQ;
+	case TS5500_DIO2_13:
+		return TS5500_DIO2_13_IRQ;
+	case TS5500_LCD_RS:
+		return TS5500_LCD_RS_IRQ;
+	default:
+		break;
+	}
+
+	/*
+	 * Handle the case where the user bridged the IRQ line 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 int ts5500_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+	unsigned long dir_reg;
+	int dir_bit;
+	struct ts5500_drvdata *drvdata;
+
+	drvdata = container_of(chip, struct ts5500_drvdata, gpio_chip);
+
+	/* Some lines cannot be set as inputs */
+	switch (offset) {
+	case TS5500_LCD_EN:
+		return -ENXIO;
+	default:
+		if (offset > chip->ngpio)
+			return -ENXIO;
+		break;
+	}
+
+	dir_reg = line_to_dir_map[offset];
+	dir_bit = line_to_dir_bit_map[offset];
+
+	mutex_lock(&drvdata->gpio_lock);
+	port_bit_clear(dir_reg, dir_bit);
+	mutex_unlock(&drvdata->gpio_lock);
+
+	return 0;
+}
+
+static int ts5500_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+					int val)
+{
+	unsigned long dir_reg, ioaddr;
+	int dir_bit, bitno;
+	struct ts5500_drvdata *drvdata;
+
+	drvdata = container_of(chip, struct ts5500_drvdata, gpio_chip);
+
+	/* Some lines cannot be set as outputs */
+	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:
+		if (offset > chip->ngpio)
+			return -ENXIO;
+		break;
+	}
+
+	/* Get direction and value registers infos */
+	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];
+
+	mutex_lock(&drvdata->gpio_lock);
+	if (val == 0)
+		port_bit_clear(ioaddr, bitno); /* Set initial line value */
+	else
+		port_bit_set(ioaddr, bitno);
+	port_bit_set(dir_reg, dir_bit); /* Set output direction for line */
+
+	/*
+	 * Confirm initial line output value
+	 * (might have been changed by input)
+	 */
+	if (val == 0)
+		port_bit_clear(ioaddr, bitno);
+	else
+		port_bit_set(ioaddr, bitno);
+	mutex_unlock(&drvdata->gpio_lock);
+
+	return 0;
+}
+
+static int __devinit ts5500_gpio_probe(struct platform_device *pdev)
+{
+	struct ts5500_drvdata *drvdata;
+	struct gpio_chip *chip;
+	int ret;
+
+	if (pdev == NULL) {
+		dev_err(&pdev->dev, "Platform device not available!\n");
+		return -ENODEV;
+	}
+
+	/* Request DIO1 */
+	if (!request_region(0x7A, 3, "ts5500-gpio-DIO1")) {
+		dev_err(&pdev->dev, "Cannot request I/O port 0x7A-7C\n");
+		goto err_req_dio1;
+	}
+
+	/* Request DIO2 */
+	if (!request_region(0x7D, 3, "ts5500-gpio-DIO2")) {
+		dev_err(&pdev->dev, "Cannot request I/O port 0x7D-7F\n");
+		goto err_req_dio2;
+	}
+
+	/* Request LCDIO if wanted */
+	if (use_lcdio && !request_region(0x72, 2, "ts5500-gpio-LCD")) {
+		dev_err(&pdev->dev, "Cannot request I/O port 0x72-73\n");
+		goto err_req_lcdio;
+	}
+
+	/* Setup the gpio_chip structure */
+	drvdata = kzalloc(sizeof(struct ts5500_drvdata), GFP_KERNEL);
+	if (drvdata == NULL)
+		goto err_alloc_dev;
+
+	memset(requested_gpios, 0, sizeof(requested_gpios));
+	mutex_init(&drvdata->gpio_lock);
+
+	drvdata->master = pdev->dev.parent;
+	chip = &drvdata->gpio_chip;
+	chip->request = ts5500_gpio_request;
+	chip->free = ts5500_gpio_free;
+	chip->to_irq = ts5500_gpio_to_irq;
+	chip->direction_input = ts5500_gpio_direction_input;
+	chip->direction_output = ts5500_gpio_direction_output;
+	chip->get = ts5500_gpio_get;
+	chip->set = ts5500_gpio_set;
+	chip->can_sleep = 0;
+	chip->base = TS5500_DIO1_0;
+	chip->label = pdev->name;
+	chip->ngpio = (use_lcdio ? TS5500_LCD_WR + 1 : TS5500_DIO2_13 + 1);
+
+	/* Enable IRQ generation */
+	mutex_lock(&drvdata->gpio_lock);
+	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); /* Enable LCD header usage as DIO */
+		port_bit_set(0x7D, 6);   /* LCD_RS on IRQ1 */
+	}
+	mutex_unlock(&drvdata->gpio_lock);
+
+	/* Register chip */
+	ret = gpiochip_add(&drvdata->gpio_chip);
+	if (ret)
+		goto err_gpiochip_add;
+
+	platform_set_drvdata(pdev, drvdata);
+
+	return 0;
+
+err_gpiochip_add:
+	dev_err(&pdev->dev, "Failed to register the gpio chip.\n");
+	kfree(drvdata);
+
+err_alloc_dev:
+	if (use_lcdio)
+		release_region(0x72, 2);	/* Release LCD's region */
+
+err_req_lcdio:
+	release_region(0x7D, 3);		/* Release DIO2's region */
+
+err_req_dio2:
+	release_region(0x7A, 3);		/* Release DIO1's region */
+
+err_req_dio1:
+	ret = -EBUSY;
+
+	return ret;
+}
+
+/* Callback for releasing resources */
+static void ts5500_gpio_device_release(struct device *dev)
+{
+	/* noop */
+}
+
+static struct platform_device ts5500_gpio_device = {
+	.name = "ts5500_gpio",
+	.id = -1,
+	.dev = {
+		.release = ts5500_gpio_device_release,
+	}
+};
+
+static int __devexit ts5500_gpio_remove(struct platform_device *pdev)
+{
+	struct ts5500_drvdata *drvdata;
+	int ret, i;
+
+	drvdata = platform_get_drvdata(pdev);
+
+	/* Release GPIO lines */
+	for (i = 0; i < ARRAY_SIZE(requested_gpios); i++) {
+		if (requested_gpios[i])
+			gpio_free(i);
+	}
+
+	mutex_lock(&drvdata->gpio_lock);
+	/* Disable IRQs generation */
+	port_bit_clear(0x7A, 7);
+	port_bit_clear(0x7D, 7);
+	if (use_lcdio)
+		port_bit_clear(0x7D, 6);
+
+	/* Release IO regions */
+	release_region(0x7A, 3);
+	release_region(0x7D, 3);
+	if (use_lcdio)
+		release_region(0x72, 2);
+	mutex_unlock(&drvdata->gpio_lock);
+
+	ret = gpiochip_remove(&drvdata->gpio_chip);
+	if (ret)
+		dev_err(&pdev->dev, "Failed to remove the gpio chip\n");
+
+	kfree(drvdata);
+	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)
+		goto error_out;
+
+	ret = platform_device_register(&ts5500_gpio_device);
+	if (ret)
+		goto error_device_register;
+
+	return 0;
+
+error_device_register:
+	platform_driver_unregister(&ts5500_gpio_driver);
+error_out:
+	return ret;
+}
+module_init(ts5500_gpio_init);
+
+static void __exit ts5500_gpio_exit(void)
+{
+	platform_driver_unregister(&ts5500_gpio_driver);
+	platform_device_unregister(&ts5500_gpio_device);
+}
+module_exit(ts5500_gpio_exit);
+
+MODULE_AUTHOR("Jerome Oufella <jerome.oufella@...oirfairelinux.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Technologic Systems TS-5500, GPIO/DIO driver");
diff --git a/arch/x86/platform/ts5500/ts5500_gpio.h b/arch/x86/platform/ts5500/ts5500_gpio.h
new file mode 100644
index 0000000..eecd4fc
--- /dev/null
+++ b/arch/x86/platform/ts5500/ts5500_gpio.h
@@ -0,0 +1,60 @@
+/*
+ * GPIO (DIO) driver for Technologic Systems TS-5500
+ *
+ * Copyright (c) 2010-2012 Savoir-faire Linux Inc.
+ *	Jerome Oufella <jerome.oufella@...oirfairelinux.com>
+ *
+ * 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_GPIO_H
+#define _TS5500_GPIO_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_GPIO_H */
-- 
1.7.6.5

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

Powered by blists - more mailing lists