123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421 |
- --- a/drivers/gpio/Kconfig
- +++ b/drivers/gpio/Kconfig
- @@ -795,6 +795,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
- @@ -106,3 +106,4 @@ obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx
- obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o
- obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o
- obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o
- +obj-$(CONFIG_GPIO_GW_I2C_PLD) += gw_i2c_pld.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 */
|