[PATCH] AMD Geode CS553X GPIO driver
From: Alessandro Zummo
Date: Wed Dec 24 2008 - 17:53:25 EST
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@xxxxxxxxxxxx>
---
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@xxxxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxxxx>");
+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
--
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/