[PATCH] AMD Geode CS553X GPIO driver

Alessandro Zummo alessandro.zummo at towertech.it
Wed Dec 24 17:43:21 EST 2008



 A GPIO driver for the AMD Geode CS5535/5536 Companion Devices.

 It uses the gpio framework and the gpio api as defined in
 arch/x86/kernel/geode_32.c

 Patch is against 2.6.28-rc9.

 Signed-off-by: Alessandro Zummo <a.zummo at towertech.it>


---
 arch/x86/include/asm/geode.h |   18 ++++
 drivers/gpio/Kconfig         |   14 +++
 drivers/gpio/Makefile        |    1 
 drivers/gpio/cs553x-gpio.c   |  172 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 205 insertions(+)

--- linux-tuxrouter-2.6.28-rc9.orig/drivers/gpio/Kconfig	2008-12-24 23:34:02.000000000 +0100
+++ linux-tuxrouter-2.6.28-rc9/drivers/gpio/Kconfig	2008-12-24 23:34:44.000000000 +0100
@@ -175,4 +175,18 @@ config GPIO_MCP23S08
 	  SPI driver for Microchip MCP23S08 I/O expander.  This provides
 	  a GPIO interface supporting inputs and outputs.
 
+comment "Other GPIO expanders:"
+
+config GPIO_CS553X
+	tristate "AMD CS5535/CS5536 Geode Companion Devices"
+	depends on MGEODE_LX && !CS5535_GPIO
+	help
+	  Say yes here to provide support for the 28 GPIO pins on
+	  the AMD CS5535 and CS5536 Geode Companion Devices.
+
+	  Your board setup code will have to declare an appropriate
+	  platform device for this driver to work.
+
+	  If unsure, say N.
+
 endif
--- linux-tuxrouter-2.6.28-rc9.orig/drivers/gpio/Makefile	2008-12-24 23:34:02.000000000 +0100
+++ linux-tuxrouter-2.6.28-rc9/drivers/gpio/Makefile	2008-12-24 23:34:44.000000000 +0100
@@ -4,6 +4,7 @@ ccflags-$(CONFIG_DEBUG_GPIO)	+= -DDEBUG
 
 obj-$(CONFIG_GPIOLIB)		+= gpiolib.o
 
+obj-$(CONFIG_GPIO_CS553X)	+= cs553x-gpio.o
 obj-$(CONFIG_GPIO_MAX7301)	+= max7301.o
 obj-$(CONFIG_GPIO_MAX732X)	+= max732x.o
 obj-$(CONFIG_GPIO_MCP23S08)	+= mcp23s08.o
--- linux-tuxrouter-2.6.28-rc9.orig/arch/x86/include/asm/geode.h	2008-12-24 23:34:02.000000000 +0100
+++ linux-tuxrouter-2.6.28-rc9/arch/x86/include/asm/geode.h	2008-12-24 23:34:44.000000000 +0100
@@ -12,6 +12,7 @@
 
 #include <asm/processor.h>
 #include <linux/io.h>
+#include <linux/platform_device.h>
 
 /* Generic southbridge functions */
 
@@ -165,6 +166,23 @@ static inline void geode_gpio_event_pme(
 	geode_gpio_setup_event(gpio, pair, 1);
 }
 
+struct cs553x_gpio_platform_data {
+
+	unsigned	gpio_base; /* number of the first GPIO */
+
+	resource_size_t	io_base;
+
+	void		*context; /* param to setup/teardown */
+
+	int		(*setup)(struct platform_device *pdev,
+				unsigned base, unsigned ngpio,
+				void *context);
+
+	int		(*teardown)(struct platform_device *pdev,
+				unsigned base, unsigned ngpio,
+				void *context);
+};
+
 /* Specific geode tests */
 
 static inline int is_geode_gx(void)
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-tuxrouter-2.6.28-rc9/drivers/gpio/cs553x-gpio.c	2008-12-24 23:35:41.000000000 +0100
@@ -0,0 +1,172 @@
+/*
+ * AMD CS5535/CS5536 Geode Companion Devices GPIO driver
+ *
+ * Copyright (C) 2008 Tower Technologies
+ * Written by Alessandro Zummo <a.zummo at towertech.it>
+ *
+ * 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/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+
+#include <asm/geode.h>
+
+#define DRIVER_NAME "cs553x-gpio"
+#define DRIVER_VERSION "1.0"
+
+static void cs553x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	if (value)
+		geode_gpio_set(geode_gpio(offset), GPIO_OUTPUT_VAL);
+	else
+		geode_gpio_clear(geode_gpio(offset), GPIO_OUTPUT_VAL);
+}
+
+static int cs553x_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	return geode_gpio_isset(geode_gpio(offset), GPIO_READ_BACK);
+}
+
+static int cs553x_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+	geode_gpio_clear(geode_gpio(offset), GPIO_OUTPUT_ENABLE);
+	geode_gpio_set(geode_gpio(offset), GPIO_INPUT_ENABLE);
+
+	return 0;
+}
+
+static int cs553x_direction_output(struct gpio_chip *chip, unsigned offset,
+					int value)
+{
+	cs553x_gpio_set(chip, offset, value);
+
+	geode_gpio_set(geode_gpio(offset), GPIO_OUTPUT_ENABLE);
+	geode_gpio_clear(geode_gpio(offset), GPIO_INPUT_ENABLE);
+
+	return 0;
+}
+
+static struct gpio_chip cs553x = {
+	.owner	= THIS_MODULE,
+	.label	= DRIVER_NAME,
+	.ngpio	= 28,
+
+	.get	= cs553x_gpio_get,
+	.set	= cs553x_gpio_set,
+
+	.direction_input = cs553x_direction_input,
+	.direction_output = cs553x_direction_output,
+};
+
+
+static int __init cs553x_gpio_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct cs553x_gpio_platform_data *pdata = pdev->dev.platform_data;
+
+	if (pdata == NULL) {
+		dev_err(&pdev->dev, "missing CS553X platform data\n");
+		return -ENODEV;
+	}
+
+	if (!pdata->io_base) {
+		dev_err(&pdev->dev, "platform data has no GPIO base region\n");
+		return -ENODEV;
+	}
+
+	if (!request_region(pdata->io_base, LBAR_GPIO_SIZE, DRIVER_NAME)) {
+		dev_err(&pdev->dev, "cannot allocate I/O region at %x\n",
+				pdata->io_base);
+		return -ENODEV;
+	}
+
+	cs553x.dev = &pdev->dev;
+	cs553x.base = pdata->gpio_base;
+
+	rc = gpiochip_add(&cs553x);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "cannot add GPIO\n");
+		goto fail_release;
+	}
+
+	dev_info(&pdev->dev, "registered at 0x%x, GPIO base %d\n",
+			pdata->io_base, cs553x.base);
+
+	if (pdata->setup) {
+		rc = pdata->setup(pdev, cs553x.base,
+				cs553x.ngpio, pdata->context);
+
+		if (rc < 0) {
+			dev_err(&pdev->dev, "setup failed, %d\n", rc);
+			goto fail_remove;
+		}
+	}
+
+	return 0;
+
+fail_remove:
+	gpiochip_remove(&cs553x);
+
+fail_release:
+	release_region(pdata->io_base, LBAR_GPIO_SIZE);
+
+	return rc;
+}
+
+static int __exit cs553x_gpio_remove(struct platform_device *pdev)
+{
+	struct cs553x_gpio_platform_data *pdata = pdev->dev.platform_data;
+
+	if (pdata->teardown) {
+		int rc = pdata->teardown(pdev, cs553x.base,
+				cs553x.ngpio, pdata->context);
+		if (rc < 0) {
+			dev_err(&pdev->dev, "teardown failed, %d\n", rc);
+			return rc;
+		}
+	}
+
+	gpiochip_remove(&cs553x);
+	release_region(pdata->io_base, LBAR_GPIO_SIZE);
+
+	return 0;
+}
+
+static struct platform_driver cs553x_gpio_driver = {
+	.driver	 = {
+		.name   = DRIVER_NAME,
+		.owner  = THIS_MODULE,
+	},
+	.remove	 = __exit_p(cs553x_gpio_remove),
+};
+
+static int __init cs553x_gpio_init(void)
+{
+	if (!is_geode())
+		return -ENODEV;
+
+	return platform_driver_probe(&cs553x_gpio_driver, cs553x_gpio_probe);
+}
+
+static void __exit cs553x_gpio_exit(void)
+{
+	platform_driver_unregister(&cs553x_gpio_driver);
+}
+
+module_init(cs553x_gpio_init);
+module_exit(cs553x_gpio_exit);
+
+
+MODULE_AUTHOR("Alessandro Zummo <a.zummo at towertech.it>");
+MODULE_DESCRIPTION("AMD CS5535/CS5536 GPIO driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_ALIAS("platform:cs553x-gpio");

-- 

 Best regards,

 Alessandro Zummo,
  Tower Technologies - Torino, Italy

  http://www.towertech.it




More information about the Linux-geode mailing list