2 +++ b/drivers/gpio/gw_i2c_pld.c
5 + * Gateworks I2C PLD GPIO expander
7 + * Copyright (C) 2009 Gateworks Corporation
9 + * This program is free software; you can redistribute it and/or modify
10 + * it under the terms of the GNU General Public License as published by
11 + * the Free Software Foundation; either version 2 of the License, or
12 + * (at your option) any later version.
14 + * This program is distributed in the hope that it will be useful,
15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 + * GNU General Public License for more details.
19 + * You should have received a copy of the GNU General Public License
20 + * along with this program; if not, write to the Free Software
21 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 +#include <linux/kernel.h>
25 +#include <linux/slab.h>
26 +#include <linux/hardirq.h>
27 +#include <linux/i2c.h>
28 +#include <linux/i2c/gw_i2c_pld.h>
29 +#include <asm/gpio.h>
31 +static const struct i2c_device_id gw_i2c_pld_id[] = {
32 + { "gw_i2c_pld", 8 },
35 +MODULE_DEVICE_TABLE(i2c, gw_i2c_pld_id);
38 + * The Gateworks I2C PLD chip only expose one read and one
39 + * write register. Writing a "one" bit (to match the reset state) lets
40 + * that pin be used as an input. It is an open-drain model.
44 + struct gpio_chip chip;
45 + struct i2c_client *client;
46 + unsigned out; /* software latch */
49 +/*-------------------------------------------------------------------------*/
52 + * The Gateworks I2C PLD chip does not properly send the acknowledge bit
53 + * thus we cannot use standard i2c_smbus functions. We have recreated
54 + * our own here, but we still use the mutex_lock to lock the i2c_bus
55 + * as the device still exists on the I2C bus.
58 +#define PLD_SCL_GPIO 6
59 +#define PLD_SDA_GPIO 7
61 +#define SCL_LO() gpio_line_set(PLD_SCL_GPIO, IXP4XX_GPIO_LOW)
62 +#define SCL_HI() gpio_line_set(PLD_SCL_GPIO, IXP4XX_GPIO_HIGH)
63 +#define SCL_EN() gpio_line_config(PLD_SCL_GPIO, IXP4XX_GPIO_OUT)
64 +#define SDA_LO() gpio_line_set(PLD_SDA_GPIO, IXP4XX_GPIO_LOW)
65 +#define SDA_HI() gpio_line_set(PLD_SDA_GPIO, IXP4XX_GPIO_HIGH)
66 +#define SDA_EN() gpio_line_config(PLD_SDA_GPIO, IXP4XX_GPIO_OUT)
67 +#define SDA_DIS() gpio_line_config(PLD_SDA_GPIO, IXP4XX_GPIO_IN)
68 +#define SDA_IN(x) gpio_line_get(PLD_SDA_GPIO, &x);
70 +static int i2c_pld_write_byte(int address, int byte)
74 + address = (address << 1) & ~0x1;
83 + for (i = 7; i >= 0; i--)
85 + if (address & (1 << i))
100 + for (i = 7; i >= 0; i--)
102 + if (byte & (1 << i))
127 +static unsigned int i2c_pld_read_byte(int address)
129 + int i = 0, byte = 0;
132 + address = (address << 1) | 0x1;
141 + for (i = 7; i >= 0; i--)
143 + if (address & (1 << i))
159 + for (i = 7; i >= 0; i--)
177 +static int gw_i2c_pld_input8(struct gpio_chip *chip, unsigned offset)
180 + struct gw_i2c_pld *gpio = container_of(chip, struct gw_i2c_pld, chip);
181 + struct i2c_adapter *adap = gpio->client->adapter;
183 + if (in_atomic() || irqs_disabled()) {
184 + ret = mutex_trylock(&adap->bus_lock);
186 + /* I2C activity is ongoing. */
189 + mutex_lock_nested(&adap->bus_lock, adap->level);
192 + gpio->out |= (1 << offset);
194 + ret = i2c_pld_write_byte(gpio->client->addr, gpio->out);
196 + mutex_unlock(&adap->bus_lock);
201 +static int gw_i2c_pld_get8(struct gpio_chip *chip, unsigned offset)
205 + struct gw_i2c_pld *gpio = container_of(chip, struct gw_i2c_pld, chip);
206 + struct i2c_adapter *adap = gpio->client->adapter;
208 + if (in_atomic() || irqs_disabled()) {
209 + ret = mutex_trylock(&adap->bus_lock);
211 + /* I2C activity is ongoing. */
214 + mutex_lock_nested(&adap->bus_lock, adap->level);
217 + value = i2c_pld_read_byte(gpio->client->addr);
219 + mutex_unlock(&adap->bus_lock);
221 + return (value < 0) ? 0 : (value & (1 << offset));
224 +static int gw_i2c_pld_output8(struct gpio_chip *chip, unsigned offset, int value)
228 + struct gw_i2c_pld *gpio = container_of(chip, struct gw_i2c_pld, chip);
229 + struct i2c_adapter *adap = gpio->client->adapter;
231 + unsigned bit = 1 << offset;
233 + if (in_atomic() || irqs_disabled()) {
234 + ret = mutex_trylock(&adap->bus_lock);
236 + /* I2C activity is ongoing. */
239 + mutex_lock_nested(&adap->bus_lock, adap->level);
248 + ret = i2c_pld_write_byte(gpio->client->addr, gpio->out);
250 + mutex_unlock(&adap->bus_lock);
255 +static void gw_i2c_pld_set8(struct gpio_chip *chip, unsigned offset, int value)
257 + gw_i2c_pld_output8(chip, offset, value);
260 +/*-------------------------------------------------------------------------*/
262 +static int gw_i2c_pld_probe(struct i2c_client *client,
263 + const struct i2c_device_id *id)
265 + struct gw_i2c_pld_platform_data *pdata;
266 + struct gw_i2c_pld *gpio;
269 + pdata = client->dev.platform_data;
273 + /* Allocate, initialize, and register this gpio_chip. */
274 + gpio = kzalloc(sizeof *gpio, GFP_KERNEL);
278 + gpio->chip.base = pdata->gpio_base;
279 + gpio->chip.can_sleep = 1;
280 + gpio->chip.dev = &client->dev;
281 + gpio->chip.owner = THIS_MODULE;
283 + gpio->chip.ngpio = pdata->nr_gpio;
284 + gpio->chip.direction_input = gw_i2c_pld_input8;
285 + gpio->chip.get = gw_i2c_pld_get8;
286 + gpio->chip.direction_output = gw_i2c_pld_output8;
287 + gpio->chip.set = gw_i2c_pld_set8;
289 + gpio->chip.label = client->name;
291 + gpio->client = client;
292 + i2c_set_clientdata(client, gpio);
296 + status = gpiochip_add(&gpio->chip);
300 + dev_info(&client->dev, "gpios %d..%d on a %s%s\n",
302 + gpio->chip.base + gpio->chip.ngpio - 1,
304 + client->irq ? " (irq ignored)" : "");
306 + /* Let platform code set up the GPIOs and their users.
307 + * Now is the first time anyone could use them.
309 + if (pdata->setup) {
310 + status = pdata->setup(client,
311 + gpio->chip.base, gpio->chip.ngpio,
314 + dev_warn(&client->dev, "setup --> %d\n", status);
320 + dev_dbg(&client->dev, "probe error %d for '%s'\n",
321 + status, client->name);
326 +static int gw_i2c_pld_remove(struct i2c_client *client)
328 + struct gw_i2c_pld_platform_data *pdata = client->dev.platform_data;
329 + struct gw_i2c_pld *gpio = i2c_get_clientdata(client);
332 + if (pdata->teardown) {
333 + status = pdata->teardown(client,
334 + gpio->chip.base, gpio->chip.ngpio,
337 + dev_err(&client->dev, "%s --> %d\n",
338 + "teardown", status);
343 + status = gpiochip_remove(&gpio->chip);
347 + dev_err(&client->dev, "%s --> %d\n", "remove", status);
351 +static struct i2c_driver gw_i2c_pld_driver = {
353 + .name = "gw_i2c_pld",
354 + .owner = THIS_MODULE,
356 + .probe = gw_i2c_pld_probe,
357 + .remove = gw_i2c_pld_remove,
358 + .id_table = gw_i2c_pld_id,
361 +static int __init gw_i2c_pld_init(void)
363 + return i2c_add_driver(&gw_i2c_pld_driver);
365 +module_init(gw_i2c_pld_init);
367 +static void __exit gw_i2c_pld_exit(void)
369 + i2c_del_driver(&gw_i2c_pld_driver);
371 +module_exit(gw_i2c_pld_exit);
373 +MODULE_LICENSE("GPL");
374 +MODULE_AUTHOR("Chris Lang");
375 --- a/drivers/gpio/Kconfig
376 +++ b/drivers/gpio/Kconfig
377 @@ -298,6 +298,14 @@ config GPIO_RDC321X
378 Support for the RDC R321x SoC GPIOs over southbridge
379 PCI configuration space.
381 +config GPIO_GW_I2C_PLD
382 + tristate "Gateworks I2C PLD GPIO Expander"
385 + Say yes here to provide access to the Gateworks I2C PLD GPIO
386 + Expander. This is used at least on the GW2358-4.
389 comment "SPI GPIO expanders:"
392 --- a/drivers/gpio/Makefile
393 +++ b/drivers/gpio/Makefile
394 @@ -35,3 +35,4 @@ obj-$(CONFIG_GPIO_WM8994) += wm8994-gpio
395 obj-$(CONFIG_GPIO_SCH) += sch_gpio.o
396 obj-$(CONFIG_GPIO_RDC321X) += rdc321x-gpio.o
397 obj-$(CONFIG_GPIO_JANZ_TTL) += janz-ttl.o
398 +obj-$(CONFIG_GPIO_GW_I2C_PLD) += gw_i2c_pld.o
400 +++ b/include/linux/i2c/gw_i2c_pld.h
402 +#ifndef __LINUX_GW_I2C_PLD_H
403 +#define __LINUX_GW_I2C_PLD_H
406 + * The Gateworks I2C PLD Implements an additional 8 bits of GPIO through the PLD
409 +struct gw_i2c_pld_platform_data {
410 + unsigned gpio_base;
412 + int (*setup)(struct i2c_client *client,
413 + int gpio, unsigned ngpio,
415 + int (*teardown)(struct i2c_client *client,
416 + int gpio, unsigned ngpio,
421 +#endif /* __LINUX_GW_I2C_PLD_H */