diff options
Diffstat (limited to 'target/linux/ixp4xx/patches-3.0/020-gateworks_i2c_pld.patch')
-rw-r--r-- | target/linux/ixp4xx/patches-3.0/020-gateworks_i2c_pld.patch | 421 |
1 files changed, 421 insertions, 0 deletions
diff --git a/target/linux/ixp4xx/patches-3.0/020-gateworks_i2c_pld.patch b/target/linux/ixp4xx/patches-3.0/020-gateworks_i2c_pld.patch new file mode 100644 index 0000000000..13713ff387 --- /dev/null +++ b/target/linux/ixp4xx/patches-3.0/020-gateworks_i2c_pld.patch @@ -0,0 +1,421 @@ +--- /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 <asm/gpio.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; ++ } ++ } ++ ++ status = gpiochip_remove(&gpio->chip); ++ if (status == 0) ++ kfree(gpio); ++ else ++ dev_err(&client->dev, "%s --> %d\n", "remove", status); ++ return status; ++} ++ ++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"); +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -388,6 +388,14 @@ config GPIO_RDC321X + Support for the RDC R321x SoC GPIOs over southbridge + PCI configuration space. + ++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. ++ ++ + comment "SPI GPIO expanders:" + + config GPIO_MAX7301 +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -48,3 +48,4 @@ obj-$(CONFIG_GPIO_VX855) += vx855_gpio.o + obj-$(CONFIG_GPIO_ML_IOH) += ml_ioh_gpio.o + obj-$(CONFIG_AB8500_GPIO) += ab8500-gpio.o + obj-$(CONFIG_GPIO_TPS65910) += tps65910-gpio.o ++obj-$(CONFIG_GPIO_GW_I2C_PLD) += gw_i2c_pld.o +--- /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 */ |