add support for the GPIO expander used on the Cambria based on patches from Chris
authorImre Kaloz <kaloz@openwrt.org>
Wed, 28 Jan 2009 15:26:13 +0000 (15:26 +0000)
committerImre Kaloz <kaloz@openwrt.org>
Wed, 28 Jan 2009 15:26:13 +0000 (15:26 +0000)
SVN-Revision: 14244

target/linux/ixp4xx/patches-2.6.28/020-gateworks_i2c_pld.patch [new file with mode: 0644]
target/linux/ixp4xx/patches-2.6.28/193-cambria_pld_gpio.patch [new file with mode: 0644]

diff --git a/target/linux/ixp4xx/patches-2.6.28/020-gateworks_i2c_pld.patch b/target/linux/ixp4xx/patches-2.6.28/020-gateworks_i2c_pld.patch
new file mode 100644 (file)
index 0000000..6a9f65f
--- /dev/null
@@ -0,0 +1,420 @@
+--- /dev/null
++++ b/drivers/gpio/gw_i2c_pld.c
+@@ -0,0 +1,370 @@
++/*
++ * 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/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 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 = mutex_trylock(&adap->bus_lock);
++              if (!ret)
++                      /* I2C activity is ongoing. */
++                      return -EAGAIN;
++      } else {
++              mutex_lock_nested(&adap->bus_lock, adap->level);
++      }
++
++      gpio->out |= (1 << offset);
++
++      ret = i2c_pld_write_byte(gpio->client->addr, gpio->out);
++
++      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 = mutex_trylock(&adap->bus_lock);
++              if (!ret)
++                      /* I2C activity is ongoing. */
++                      return -EAGAIN;
++      } else {
++              mutex_lock_nested(&adap->bus_lock, adap->level);
++      }
++
++      value = i2c_pld_read_byte(gpio->client->addr);
++
++      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 = mutex_trylock(&adap->bus_lock);
++              if (!ret)
++                      /* I2C activity is ongoing. */
++                      return -EAGAIN;
++      } else {
++              mutex_lock_nested(&adap->bus_lock, adap->level);
++      }
++
++
++      if (value)
++              gpio->out |= bit;
++      else
++              gpio->out &= ~bit;
++
++      ret = i2c_pld_write_byte(gpio->client->addr, gpio->out);
++
++      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
+@@ -160,6 +160,14 @@ config GPIO_BT8XX
+         If unsure, say N.
++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
+@@ -12,3 +12,4 @@ obj-$(CONFIG_GPIO_PCF857X)   += pcf857x.o
+ obj-$(CONFIG_GPIO_TWL4030)    += twl4030-gpio.o
+ obj-$(CONFIG_GPIO_XILINX)     += xilinx_gpio.o
+ obj-$(CONFIG_GPIO_BT8XX)      += bt8xxgpio.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 */
diff --git a/target/linux/ixp4xx/patches-2.6.28/193-cambria_pld_gpio.patch b/target/linux/ixp4xx/patches-2.6.28/193-cambria_pld_gpio.patch
new file mode 100644 (file)
index 0000000..299630f
--- /dev/null
@@ -0,0 +1,107 @@
+--- a/arch/arm/mach-ixp4xx/cambria-setup.c
++++ b/arch/arm/mach-ixp4xx/cambria-setup.c
+@@ -12,11 +12,14 @@
+  */
+ #include <linux/device.h>
++#include <linux/gpio_buttons.h>
+ #include <linux/i2c.h>
+ #include <linux/i2c-gpio.h>
+ #include <linux/i2c/at24.h>
++#include <linux/i2c/gw_i2c_pld.h>
+ #include <linux/if_ether.h>
+ #include <linux/init.h>
++#include <linux/input.h>
+ #include <linux/kernel.h>
+ #include <linux/leds.h>
+ #include <linux/memory.h>
+@@ -323,6 +326,39 @@ static struct platform_device cambria_us
+       },
+ };
++static struct gw_i2c_pld_platform_data gw_i2c_pld_data0 = {
++      .gpio_base      = 16,
++      .nr_gpio        = 8,
++};
++
++static struct gw_i2c_pld_platform_data gw_i2c_pld_data1 = {
++      .gpio_base      = 24,
++      .nr_gpio        = 2,
++};
++
++
++static struct gpio_button cambria_gpio_buttons[] = {
++      {
++              .desc           = "user",
++              .type           = EV_KEY,
++              .code           = BTN_0,
++              .threshold      = 2,
++              .gpio           = 25,
++      }
++};
++
++static struct gpio_buttons_platform_data cambria_gpio_buttons_data = {
++      .poll_interval  = 500,
++      .nbuttons       = 1,
++      .buttons        = cambria_gpio_buttons, 
++};
++
++static struct platform_device cambria_gpio_buttons_device = {
++      .name                   = "gpio-buttons",
++      .id                     = -1,
++      .dev.platform_data      = &cambria_gpio_buttons_data,
++};
++
+ static struct platform_device *cambria_devices[] __initdata = {
+       &cambria_i2c_gpio,
+       &cambria_flash,
+@@ -331,6 +367,11 @@ static struct platform_device *cambria_d
+ static void __init cambria_gw23xx_setup(void)
+ {
++      cambria_gpio_resources[0].start = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) |\
++                                                                                                                                              (1 << 5) | (1 << 8) | (1 << 9) | (1 << 12);
++      cambria_gpio_resources[0].end = cambria_gpio_resources[0].start;
++
++      platform_device_register(&cambria_gpio);
+       platform_device_register(&cambria_npec_device);
+       platform_device_register(&cambria_npea_device);
+ }
+@@ -377,7 +418,8 @@ static void __init cambria_gw2358_setup(
+       cambria_optional_uart_data[1].membase   = (void __iomem *)ioremap(0x53F80000, 0x0fff);
+       cambria_optional_uart_data[1].irq               = IRQ_IXP4XX_GPIO4;
+-      cambria_gpio_resources[0].start = (1 << 14);
++      cambria_gpio_resources[0].start = (1 << 14) | (1 << 16) | (1 << 17) | (1 << 18) |\
++                                                                                                                                              (1 << 19) | (1 << 20) | (1 << 24) | (1 << 25);
+       cambria_gpio_resources[0].end = cambria_gpio_resources[0].start;
+       platform_device_register(&cambria_gpio);
+@@ -391,7 +433,12 @@ static void __init cambria_gw2358_setup(
+       platform_device_register(&cambria_pata);
++      cambria_gpio_leds[0].gpio = 24;
++      platform_device_register(&cambria_gpio_leds_device);
++
+       platform_device_register(&cambria_latch_leds_device);
++
++      platform_device_register(&cambria_gpio_buttons_device);
+ }
+ static struct cambria_board_info cambria_boards[] __initdata = {
+@@ -460,6 +507,14 @@ static struct i2c_board_info __initdata 
+               I2C_BOARD_INFO("24c08", 0x51),
+               .platform_data  = &cambria_eeprom_info
+       },
++      {
++              I2C_BOARD_INFO("gw_i2c_pld", 0x56),
++              .platform_data  = &gw_i2c_pld_data0,
++      },
++      {
++              I2C_BOARD_INFO("gw_i2c_pld", 0x57),
++              .platform_data  = &gw_i2c_pld_data1,
++      },
+ };
+ static void __init cambria_init(void)