OpenWrt – Rev 1

Subversion Repositories:
Rev:
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -656,6 +656,14 @@ config GPIO_WS16C48
          parameter. The interrupt line numbers for the devices may be
          configured via the irq module parameter.
 
+config GPIO_GW_I2C_PLD
+       tristate "Gateworks I2C PLD GPIO Expander"
+       depends on I2C
+       help
+               Say yes here to provide access to the Gateworks I2C PLD GPIO
+               Expander. This is used at least on the GW2358-4.
+
+
 endmenu
 
 menu "I2C GPIO expanders"
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_GPIO_F7188X)     += gpio-f7188x
 obj-$(CONFIG_GPIO_GE_FPGA)     += gpio-ge.o
 obj-$(CONFIG_GPIO_GPIO_MM)     += gpio-gpio-mm.o
 obj-$(CONFIG_GPIO_GRGPIO)      += gpio-grgpio.o
+obj-$(CONFIG_GPIO_GW_I2C_PLD)  += gw_i2c_pld.o
 obj-$(CONFIG_HTC_EGPIO)                += gpio-htc-egpio.o
 obj-$(CONFIG_GPIO_ICH)         += gpio-ich.o
 obj-$(CONFIG_GPIO_IOP)         += gpio-iop.o
--- /dev/null
+++ b/drivers/gpio/gw_i2c_pld.c
@@ -0,0 +1,371 @@
+/*
+ * Gateworks I2C PLD GPIO expander
+ *
+ * Copyright (C) 2009 Gateworks Corporation
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/hardirq.h>
+#include <linux/i2c.h>
+#include <linux/i2c/gw_i2c_pld.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <asm/gpio.h>
+#include <mach/hardware.h>
+
+static const struct i2c_device_id gw_i2c_pld_id[] = {
+       { "gw_i2c_pld", 8 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, gw_i2c_pld_id);
+
+/*
+ * The Gateworks I2C PLD chip only expose one read and one
+ * write register.  Writing a "one" bit (to match the reset state) lets
+ * that pin be used as an input. It is an open-drain model.
+ */
+
+struct gw_i2c_pld {
+       struct gpio_chip        chip;
+       struct i2c_client       *client;
+       unsigned                out;            /* software latch */
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * The Gateworks I2C PLD chip does not properly send the acknowledge bit
+ * thus we cannot use standard i2c_smbus functions. We have recreated
+ * our own here, but we still use the rt_mutex_lock to lock the i2c_bus
+ * as the device still exists on the I2C bus.
+*/
+
+#define PLD_SCL_GPIO 6
+#define PLD_SDA_GPIO 7
+
+#define SCL_LO()  gpio_line_set(PLD_SCL_GPIO, IXP4XX_GPIO_LOW)
+#define SCL_HI()  gpio_line_set(PLD_SCL_GPIO, IXP4XX_GPIO_HIGH)
+#define SCL_EN()  gpio_line_config(PLD_SCL_GPIO, IXP4XX_GPIO_OUT)
+#define SDA_LO()  gpio_line_set(PLD_SDA_GPIO, IXP4XX_GPIO_LOW)
+#define SDA_HI()  gpio_line_set(PLD_SDA_GPIO, IXP4XX_GPIO_HIGH)
+#define SDA_EN()  gpio_line_config(PLD_SDA_GPIO, IXP4XX_GPIO_OUT)
+#define SDA_DIS() gpio_line_config(PLD_SDA_GPIO, IXP4XX_GPIO_IN)
+#define SDA_IN(x) gpio_line_get(PLD_SDA_GPIO, &x);
+
+static int i2c_pld_write_byte(int address, int byte)
+{
+       int i;
+
+       address = (address << 1) & ~0x1;
+
+       SDA_HI();
+       SDA_EN();
+       SCL_EN();
+       SCL_HI();
+       SDA_LO();
+       SCL_LO();
+
+       for (i = 7; i >= 0; i--)
+       {
+               if (address & (1 << i))
+                       SDA_HI();
+               else
+                       SDA_LO();
+
+               SCL_HI();
+               SCL_LO();
+       }
+
+       SDA_DIS();
+       SCL_HI();
+       SDA_IN(i);
+       SCL_LO();
+       SDA_EN();
+
+       for (i = 7; i >= 0; i--)
+       {
+               if (byte & (1 << i))
+      SDA_HI();
+               else
+                       SDA_LO();
+               SCL_HI();
+               SCL_LO();
+       }
+
+       SDA_DIS();
+       SCL_HI();
+       SDA_IN(i);
+       SCL_LO();
+
+       SDA_HI();
+       SDA_EN();
+
+       SDA_LO();
+       SCL_HI();
+       SDA_HI();
+       SCL_LO();
+       SCL_HI();
+
+       return 0;
+}
+
+static unsigned int i2c_pld_read_byte(int address)
+{
+       int i = 0, byte = 0;
+       int bit;
+
+       address = (address << 1) | 0x1;
+
+       SDA_HI();
+       SDA_EN();
+       SCL_EN();
+       SCL_HI();
+       SDA_LO();
+       SCL_LO();
+
+       for (i = 7; i >= 0; i--)
+       {
+               if (address & (1 << i))
+                       SDA_HI();
+               else
+                       SDA_LO();
+
+               SCL_HI();
+               SCL_LO();
+       }
+
+       SDA_DIS();
+       SCL_HI();
+       SDA_IN(i);
+       SCL_LO();
+       SDA_EN();
+
+       SDA_DIS();
+       for (i = 7; i >= 0; i--)
+       {
+               SCL_HI();
+               SDA_IN(bit);
+               byte |= bit << i;
+               SCL_LO();
+       }
+
+       SDA_LO();
+       SCL_HI();
+       SDA_HI();
+       SCL_LO();
+       SCL_HI();
+
+       return byte;
+}
+
+
+static int gw_i2c_pld_input8(struct gpio_chip *chip, unsigned offset)
+{
+       int ret;
+       struct gw_i2c_pld *gpio = container_of(chip, struct gw_i2c_pld, chip);
+       struct i2c_adapter *adap = gpio->client->adapter;
+
+       if (in_atomic() || irqs_disabled()) {
+               ret = rt_mutex_trylock(&adap->bus_lock);
+               if (!ret)
+                       /* I2C activity is ongoing. */
+                       return -EAGAIN;
+       } else {
+               rt_mutex_lock(&adap->bus_lock);
+       }
+
+       gpio->out |= (1 << offset);
+
+       ret = i2c_pld_write_byte(gpio->client->addr, gpio->out);
+
+       rt_mutex_unlock(&adap->bus_lock);
+
+       return ret;
+}
+
+static int gw_i2c_pld_get8(struct gpio_chip *chip, unsigned offset)
+{
+       int ret;
+       s32     value;
+       struct gw_i2c_pld *gpio = container_of(chip, struct gw_i2c_pld, chip);
+       struct i2c_adapter *adap = gpio->client->adapter;
+
+       if (in_atomic() || irqs_disabled()) {
+               ret = rt_mutex_trylock(&adap->bus_lock);
+               if (!ret)
+                       /* I2C activity is ongoing. */
+                       return -EAGAIN;
+       } else {
+               rt_mutex_lock(&adap->bus_lock);
+       }
+
+       value = i2c_pld_read_byte(gpio->client->addr);
+
+       rt_mutex_unlock(&adap->bus_lock);
+
+       return (value < 0) ? 0 : (value & (1 << offset));
+}
+
+static int gw_i2c_pld_output8(struct gpio_chip *chip, unsigned offset, int value)
+{
+       int ret;
+
+       struct gw_i2c_pld *gpio = container_of(chip, struct gw_i2c_pld, chip);
+       struct i2c_adapter *adap = gpio->client->adapter;
+
+       unsigned bit = 1 << offset;
+
+       if (in_atomic() || irqs_disabled()) {
+               ret = rt_mutex_trylock(&adap->bus_lock);
+               if (!ret)
+                       /* I2C activity is ongoing. */
+                       return -EAGAIN;
+       } else {
+               rt_mutex_lock(&adap->bus_lock);
+       }
+
+
+       if (value)
+               gpio->out |= bit;
+       else
+               gpio->out &= ~bit;
+
+       ret = i2c_pld_write_byte(gpio->client->addr, gpio->out);
+
+       rt_mutex_unlock(&adap->bus_lock);
+
+       return ret;
+}
+
+static void gw_i2c_pld_set8(struct gpio_chip *chip, unsigned offset, int value)
+{
+       gw_i2c_pld_output8(chip, offset, value);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int gw_i2c_pld_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       struct gw_i2c_pld_platform_data *pdata;
+       struct gw_i2c_pld *gpio;
+       int status;
+
+       pdata = client->dev.platform_data;
+       if (!pdata)
+               return -ENODEV;
+
+       /* Allocate, initialize, and register this gpio_chip. */
+       gpio = kzalloc(sizeof *gpio, GFP_KERNEL);
+       if (!gpio)
+               return -ENOMEM;
+
+       gpio->chip.base = pdata->gpio_base;
+       gpio->chip.can_sleep = 1;
+       gpio->chip.dev = &client->dev;
+       gpio->chip.owner = THIS_MODULE;
+
+       gpio->chip.ngpio = pdata->nr_gpio;
+       gpio->chip.direction_input = gw_i2c_pld_input8;
+       gpio->chip.get = gw_i2c_pld_get8;
+       gpio->chip.direction_output = gw_i2c_pld_output8;
+       gpio->chip.set = gw_i2c_pld_set8;
+
+       gpio->chip.label = client->name;
+
+       gpio->client = client;
+       i2c_set_clientdata(client, gpio);
+
+       gpio->out = 0xFF;
+
+       status = gpiochip_add(&gpio->chip);
+       if (status < 0)
+               goto fail;
+
+       dev_info(&client->dev, "gpios %d..%d on a %s%s\n",
+                       gpio->chip.base,
+                       gpio->chip.base + gpio->chip.ngpio - 1,
+                       client->name,
+                       client->irq ? " (irq ignored)" : "");
+
+       /* Let platform code set up the GPIOs and their users.
+        * Now is the first time anyone could use them.
+        */
+       if (pdata->setup) {
+               status = pdata->setup(client,
+                               gpio->chip.base, gpio->chip.ngpio,
+                               pdata->context);
+               if (status < 0)
+                       dev_warn(&client->dev, "setup --> %d\n", status);
+       }
+
+       return 0;
+
+fail:
+       dev_dbg(&client->dev, "probe error %d for '%s'\n",
+                       status, client->name);
+       kfree(gpio);
+       return status;
+}
+
+static int gw_i2c_pld_remove(struct i2c_client *client)
+{
+       struct gw_i2c_pld_platform_data *pdata = client->dev.platform_data;
+       struct gw_i2c_pld *gpio = i2c_get_clientdata(client);
+       int                             status = 0;
+
+       if (pdata->teardown) {
+               status = pdata->teardown(client,
+                               gpio->chip.base, gpio->chip.ngpio,
+                               pdata->context);
+               if (status < 0) {
+                       dev_err(&client->dev, "%s --> %d\n",
+                                       "teardown", status);
+                       return status;
+               }
+       }
+
+       gpiochip_remove(&gpio->chip);
+       kfree(gpio);
+       return 0;
+}
+
+static struct i2c_driver gw_i2c_pld_driver = {
+       .driver = {
+               .name   = "gw_i2c_pld",
+               .owner  = THIS_MODULE,
+       },
+       .probe  = gw_i2c_pld_probe,
+       .remove = gw_i2c_pld_remove,
+       .id_table = gw_i2c_pld_id,
+};
+
+static int __init gw_i2c_pld_init(void)
+{
+       return i2c_add_driver(&gw_i2c_pld_driver);
+}
+module_init(gw_i2c_pld_init);
+
+static void __exit gw_i2c_pld_exit(void)
+{
+       i2c_del_driver(&gw_i2c_pld_driver);
+}
+module_exit(gw_i2c_pld_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Chris Lang");
--- /dev/null
+++ b/include/linux/i2c/gw_i2c_pld.h
@@ -0,0 +1,20 @@
+#ifndef __LINUX_GW_I2C_PLD_H
+#define __LINUX_GW_I2C_PLD_H
+
+/**
+ * The Gateworks I2C PLD Implements an additional 8 bits of GPIO through the PLD
+ */
+
+struct gw_i2c_pld_platform_data {
+       unsigned gpio_base;
+       unsigned nr_gpio;
+       int             (*setup)(struct i2c_client *client,
+                                       int gpio, unsigned ngpio,
+                                       void *context);
+       int             (*teardown)(struct i2c_client *client,
+                                       int gpio, unsigned ngpio,
+                                       void *context);
+       void            *context;
+};
+
+#endif /* __LINUX_GW_I2C_PLD_H */

Generated by GNU Enscript 1.6.5.90.