OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 1 | --- a/drivers/gpio/Kconfig |
2 | +++ b/drivers/gpio/Kconfig |
||
3 | @@ -656,6 +656,14 @@ config GPIO_WS16C48 |
||
4 | parameter. The interrupt line numbers for the devices may be |
||
5 | configured via the irq module parameter. |
||
6 | |||
7 | +config GPIO_GW_I2C_PLD |
||
8 | + tristate "Gateworks I2C PLD GPIO Expander" |
||
9 | + depends on I2C |
||
10 | + help |
||
11 | + Say yes here to provide access to the Gateworks I2C PLD GPIO |
||
12 | + Expander. This is used at least on the GW2358-4. |
||
13 | + |
||
14 | + |
||
15 | endmenu |
||
16 | |||
17 | menu "I2C GPIO expanders" |
||
18 | --- a/drivers/gpio/Makefile |
||
19 | +++ b/drivers/gpio/Makefile |
||
20 | @@ -48,6 +48,7 @@ obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x |
||
21 | obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o |
||
22 | obj-$(CONFIG_GPIO_GPIO_MM) += gpio-gpio-mm.o |
||
23 | obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o |
||
24 | +obj-$(CONFIG_GPIO_GW_I2C_PLD) += gw_i2c_pld.o |
||
25 | obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o |
||
26 | obj-$(CONFIG_GPIO_ICH) += gpio-ich.o |
||
27 | obj-$(CONFIG_GPIO_IOP) += gpio-iop.o |
||
28 | --- /dev/null |
||
29 | +++ b/drivers/gpio/gw_i2c_pld.c |
||
30 | @@ -0,0 +1,371 @@ |
||
31 | +/* |
||
32 | + * Gateworks I2C PLD GPIO expander |
||
33 | + * |
||
34 | + * Copyright (C) 2009 Gateworks Corporation |
||
35 | + * |
||
36 | + * This program is free software; you can redistribute it and/or modify |
||
37 | + * it under the terms of the GNU General Public License as published by |
||
38 | + * the Free Software Foundation; either version 2 of the License, or |
||
39 | + * (at your option) any later version. |
||
40 | + * |
||
41 | + * This program is distributed in the hope that it will be useful, |
||
42 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
43 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
44 | + * GNU General Public License for more details. |
||
45 | + * |
||
46 | + * You should have received a copy of the GNU General Public License |
||
47 | + * along with this program; if not, write to the Free Software |
||
48 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
||
49 | + */ |
||
50 | + |
||
51 | +#include <linux/kernel.h> |
||
52 | +#include <linux/slab.h> |
||
53 | +#include <linux/hardirq.h> |
||
54 | +#include <linux/i2c.h> |
||
55 | +#include <linux/i2c/gw_i2c_pld.h> |
||
56 | +#include <linux/module.h> |
||
57 | +#include <linux/export.h> |
||
58 | +#include <asm/gpio.h> |
||
59 | +#include <mach/hardware.h> |
||
60 | + |
||
61 | +static const struct i2c_device_id gw_i2c_pld_id[] = { |
||
62 | + { "gw_i2c_pld", 8 }, |
||
63 | + { } |
||
64 | +}; |
||
65 | +MODULE_DEVICE_TABLE(i2c, gw_i2c_pld_id); |
||
66 | + |
||
67 | +/* |
||
68 | + * The Gateworks I2C PLD chip only expose one read and one |
||
69 | + * write register. Writing a "one" bit (to match the reset state) lets |
||
70 | + * that pin be used as an input. It is an open-drain model. |
||
71 | + */ |
||
72 | + |
||
73 | +struct gw_i2c_pld { |
||
74 | + struct gpio_chip chip; |
||
75 | + struct i2c_client *client; |
||
76 | + unsigned out; /* software latch */ |
||
77 | +}; |
||
78 | + |
||
79 | +/*-------------------------------------------------------------------------*/ |
||
80 | + |
||
81 | +/* |
||
82 | + * The Gateworks I2C PLD chip does not properly send the acknowledge bit |
||
83 | + * thus we cannot use standard i2c_smbus functions. We have recreated |
||
84 | + * our own here, but we still use the rt_mutex_lock to lock the i2c_bus |
||
85 | + * as the device still exists on the I2C bus. |
||
86 | +*/ |
||
87 | + |
||
88 | +#define PLD_SCL_GPIO 6 |
||
89 | +#define PLD_SDA_GPIO 7 |
||
90 | + |
||
91 | +#define SCL_LO() gpio_line_set(PLD_SCL_GPIO, IXP4XX_GPIO_LOW) |
||
92 | +#define SCL_HI() gpio_line_set(PLD_SCL_GPIO, IXP4XX_GPIO_HIGH) |
||
93 | +#define SCL_EN() gpio_line_config(PLD_SCL_GPIO, IXP4XX_GPIO_OUT) |
||
94 | +#define SDA_LO() gpio_line_set(PLD_SDA_GPIO, IXP4XX_GPIO_LOW) |
||
95 | +#define SDA_HI() gpio_line_set(PLD_SDA_GPIO, IXP4XX_GPIO_HIGH) |
||
96 | +#define SDA_EN() gpio_line_config(PLD_SDA_GPIO, IXP4XX_GPIO_OUT) |
||
97 | +#define SDA_DIS() gpio_line_config(PLD_SDA_GPIO, IXP4XX_GPIO_IN) |
||
98 | +#define SDA_IN(x) gpio_line_get(PLD_SDA_GPIO, &x); |
||
99 | + |
||
100 | +static int i2c_pld_write_byte(int address, int byte) |
||
101 | +{ |
||
102 | + int i; |
||
103 | + |
||
104 | + address = (address << 1) & ~0x1; |
||
105 | + |
||
106 | + SDA_HI(); |
||
107 | + SDA_EN(); |
||
108 | + SCL_EN(); |
||
109 | + SCL_HI(); |
||
110 | + SDA_LO(); |
||
111 | + SCL_LO(); |
||
112 | + |
||
113 | + for (i = 7; i >= 0; i--) |
||
114 | + { |
||
115 | + if (address & (1 << i)) |
||
116 | + SDA_HI(); |
||
117 | + else |
||
118 | + SDA_LO(); |
||
119 | + |
||
120 | + SCL_HI(); |
||
121 | + SCL_LO(); |
||
122 | + } |
||
123 | + |
||
124 | + SDA_DIS(); |
||
125 | + SCL_HI(); |
||
126 | + SDA_IN(i); |
||
127 | + SCL_LO(); |
||
128 | + SDA_EN(); |
||
129 | + |
||
130 | + for (i = 7; i >= 0; i--) |
||
131 | + { |
||
132 | + if (byte & (1 << i)) |
||
133 | + SDA_HI(); |
||
134 | + else |
||
135 | + SDA_LO(); |
||
136 | + SCL_HI(); |
||
137 | + SCL_LO(); |
||
138 | + } |
||
139 | + |
||
140 | + SDA_DIS(); |
||
141 | + SCL_HI(); |
||
142 | + SDA_IN(i); |
||
143 | + SCL_LO(); |
||
144 | + |
||
145 | + SDA_HI(); |
||
146 | + SDA_EN(); |
||
147 | + |
||
148 | + SDA_LO(); |
||
149 | + SCL_HI(); |
||
150 | + SDA_HI(); |
||
151 | + SCL_LO(); |
||
152 | + SCL_HI(); |
||
153 | + |
||
154 | + return 0; |
||
155 | +} |
||
156 | + |
||
157 | +static unsigned int i2c_pld_read_byte(int address) |
||
158 | +{ |
||
159 | + int i = 0, byte = 0; |
||
160 | + int bit; |
||
161 | + |
||
162 | + address = (address << 1) | 0x1; |
||
163 | + |
||
164 | + SDA_HI(); |
||
165 | + SDA_EN(); |
||
166 | + SCL_EN(); |
||
167 | + SCL_HI(); |
||
168 | + SDA_LO(); |
||
169 | + SCL_LO(); |
||
170 | + |
||
171 | + for (i = 7; i >= 0; i--) |
||
172 | + { |
||
173 | + if (address & (1 << i)) |
||
174 | + SDA_HI(); |
||
175 | + else |
||
176 | + SDA_LO(); |
||
177 | + |
||
178 | + SCL_HI(); |
||
179 | + SCL_LO(); |
||
180 | + } |
||
181 | + |
||
182 | + SDA_DIS(); |
||
183 | + SCL_HI(); |
||
184 | + SDA_IN(i); |
||
185 | + SCL_LO(); |
||
186 | + SDA_EN(); |
||
187 | + |
||
188 | + SDA_DIS(); |
||
189 | + for (i = 7; i >= 0; i--) |
||
190 | + { |
||
191 | + SCL_HI(); |
||
192 | + SDA_IN(bit); |
||
193 | + byte |= bit << i; |
||
194 | + SCL_LO(); |
||
195 | + } |
||
196 | + |
||
197 | + SDA_LO(); |
||
198 | + SCL_HI(); |
||
199 | + SDA_HI(); |
||
200 | + SCL_LO(); |
||
201 | + SCL_HI(); |
||
202 | + |
||
203 | + return byte; |
||
204 | +} |
||
205 | + |
||
206 | + |
||
207 | +static int gw_i2c_pld_input8(struct gpio_chip *chip, unsigned offset) |
||
208 | +{ |
||
209 | + int ret; |
||
210 | + struct gw_i2c_pld *gpio = container_of(chip, struct gw_i2c_pld, chip); |
||
211 | + struct i2c_adapter *adap = gpio->client->adapter; |
||
212 | + |
||
213 | + if (in_atomic() || irqs_disabled()) { |
||
214 | + ret = rt_mutex_trylock(&adap->bus_lock); |
||
215 | + if (!ret) |
||
216 | + /* I2C activity is ongoing. */ |
||
217 | + return -EAGAIN; |
||
218 | + } else { |
||
219 | + rt_mutex_lock(&adap->bus_lock); |
||
220 | + } |
||
221 | + |
||
222 | + gpio->out |= (1 << offset); |
||
223 | + |
||
224 | + ret = i2c_pld_write_byte(gpio->client->addr, gpio->out); |
||
225 | + |
||
226 | + rt_mutex_unlock(&adap->bus_lock); |
||
227 | + |
||
228 | + return ret; |
||
229 | +} |
||
230 | + |
||
231 | +static int gw_i2c_pld_get8(struct gpio_chip *chip, unsigned offset) |
||
232 | +{ |
||
233 | + int ret; |
||
234 | + s32 value; |
||
235 | + struct gw_i2c_pld *gpio = container_of(chip, struct gw_i2c_pld, chip); |
||
236 | + struct i2c_adapter *adap = gpio->client->adapter; |
||
237 | + |
||
238 | + if (in_atomic() || irqs_disabled()) { |
||
239 | + ret = rt_mutex_trylock(&adap->bus_lock); |
||
240 | + if (!ret) |
||
241 | + /* I2C activity is ongoing. */ |
||
242 | + return -EAGAIN; |
||
243 | + } else { |
||
244 | + rt_mutex_lock(&adap->bus_lock); |
||
245 | + } |
||
246 | + |
||
247 | + value = i2c_pld_read_byte(gpio->client->addr); |
||
248 | + |
||
249 | + rt_mutex_unlock(&adap->bus_lock); |
||
250 | + |
||
251 | + return (value < 0) ? 0 : (value & (1 << offset)); |
||
252 | +} |
||
253 | + |
||
254 | +static int gw_i2c_pld_output8(struct gpio_chip *chip, unsigned offset, int value) |
||
255 | +{ |
||
256 | + int ret; |
||
257 | + |
||
258 | + struct gw_i2c_pld *gpio = container_of(chip, struct gw_i2c_pld, chip); |
||
259 | + struct i2c_adapter *adap = gpio->client->adapter; |
||
260 | + |
||
261 | + unsigned bit = 1 << offset; |
||
262 | + |
||
263 | + if (in_atomic() || irqs_disabled()) { |
||
264 | + ret = rt_mutex_trylock(&adap->bus_lock); |
||
265 | + if (!ret) |
||
266 | + /* I2C activity is ongoing. */ |
||
267 | + return -EAGAIN; |
||
268 | + } else { |
||
269 | + rt_mutex_lock(&adap->bus_lock); |
||
270 | + } |
||
271 | + |
||
272 | + |
||
273 | + if (value) |
||
274 | + gpio->out |= bit; |
||
275 | + else |
||
276 | + gpio->out &= ~bit; |
||
277 | + |
||
278 | + ret = i2c_pld_write_byte(gpio->client->addr, gpio->out); |
||
279 | + |
||
280 | + rt_mutex_unlock(&adap->bus_lock); |
||
281 | + |
||
282 | + return ret; |
||
283 | +} |
||
284 | + |
||
285 | +static void gw_i2c_pld_set8(struct gpio_chip *chip, unsigned offset, int value) |
||
286 | +{ |
||
287 | + gw_i2c_pld_output8(chip, offset, value); |
||
288 | +} |
||
289 | + |
||
290 | +/*-------------------------------------------------------------------------*/ |
||
291 | + |
||
292 | +static int gw_i2c_pld_probe(struct i2c_client *client, |
||
293 | + const struct i2c_device_id *id) |
||
294 | +{ |
||
295 | + struct gw_i2c_pld_platform_data *pdata; |
||
296 | + struct gw_i2c_pld *gpio; |
||
297 | + int status; |
||
298 | + |
||
299 | + pdata = client->dev.platform_data; |
||
300 | + if (!pdata) |
||
301 | + return -ENODEV; |
||
302 | + |
||
303 | + /* Allocate, initialize, and register this gpio_chip. */ |
||
304 | + gpio = kzalloc(sizeof *gpio, GFP_KERNEL); |
||
305 | + if (!gpio) |
||
306 | + return -ENOMEM; |
||
307 | + |
||
308 | + gpio->chip.base = pdata->gpio_base; |
||
309 | + gpio->chip.can_sleep = 1; |
||
310 | + gpio->chip.dev = &client->dev; |
||
311 | + gpio->chip.owner = THIS_MODULE; |
||
312 | + |
||
313 | + gpio->chip.ngpio = pdata->nr_gpio; |
||
314 | + gpio->chip.direction_input = gw_i2c_pld_input8; |
||
315 | + gpio->chip.get = gw_i2c_pld_get8; |
||
316 | + gpio->chip.direction_output = gw_i2c_pld_output8; |
||
317 | + gpio->chip.set = gw_i2c_pld_set8; |
||
318 | + |
||
319 | + gpio->chip.label = client->name; |
||
320 | + |
||
321 | + gpio->client = client; |
||
322 | + i2c_set_clientdata(client, gpio); |
||
323 | + |
||
324 | + gpio->out = 0xFF; |
||
325 | + |
||
326 | + status = gpiochip_add(&gpio->chip); |
||
327 | + if (status < 0) |
||
328 | + goto fail; |
||
329 | + |
||
330 | + dev_info(&client->dev, "gpios %d..%d on a %s%s\n", |
||
331 | + gpio->chip.base, |
||
332 | + gpio->chip.base + gpio->chip.ngpio - 1, |
||
333 | + client->name, |
||
334 | + client->irq ? " (irq ignored)" : ""); |
||
335 | + |
||
336 | + /* Let platform code set up the GPIOs and their users. |
||
337 | + * Now is the first time anyone could use them. |
||
338 | + */ |
||
339 | + if (pdata->setup) { |
||
340 | + status = pdata->setup(client, |
||
341 | + gpio->chip.base, gpio->chip.ngpio, |
||
342 | + pdata->context); |
||
343 | + if (status < 0) |
||
344 | + dev_warn(&client->dev, "setup --> %d\n", status); |
||
345 | + } |
||
346 | + |
||
347 | + return 0; |
||
348 | + |
||
349 | +fail: |
||
350 | + dev_dbg(&client->dev, "probe error %d for '%s'\n", |
||
351 | + status, client->name); |
||
352 | + kfree(gpio); |
||
353 | + return status; |
||
354 | +} |
||
355 | + |
||
356 | +static int gw_i2c_pld_remove(struct i2c_client *client) |
||
357 | +{ |
||
358 | + struct gw_i2c_pld_platform_data *pdata = client->dev.platform_data; |
||
359 | + struct gw_i2c_pld *gpio = i2c_get_clientdata(client); |
||
360 | + int status = 0; |
||
361 | + |
||
362 | + if (pdata->teardown) { |
||
363 | + status = pdata->teardown(client, |
||
364 | + gpio->chip.base, gpio->chip.ngpio, |
||
365 | + pdata->context); |
||
366 | + if (status < 0) { |
||
367 | + dev_err(&client->dev, "%s --> %d\n", |
||
368 | + "teardown", status); |
||
369 | + return status; |
||
370 | + } |
||
371 | + } |
||
372 | + |
||
373 | + gpiochip_remove(&gpio->chip); |
||
374 | + kfree(gpio); |
||
375 | + return 0; |
||
376 | +} |
||
377 | + |
||
378 | +static struct i2c_driver gw_i2c_pld_driver = { |
||
379 | + .driver = { |
||
380 | + .name = "gw_i2c_pld", |
||
381 | + .owner = THIS_MODULE, |
||
382 | + }, |
||
383 | + .probe = gw_i2c_pld_probe, |
||
384 | + .remove = gw_i2c_pld_remove, |
||
385 | + .id_table = gw_i2c_pld_id, |
||
386 | +}; |
||
387 | + |
||
388 | +static int __init gw_i2c_pld_init(void) |
||
389 | +{ |
||
390 | + return i2c_add_driver(&gw_i2c_pld_driver); |
||
391 | +} |
||
392 | +module_init(gw_i2c_pld_init); |
||
393 | + |
||
394 | +static void __exit gw_i2c_pld_exit(void) |
||
395 | +{ |
||
396 | + i2c_del_driver(&gw_i2c_pld_driver); |
||
397 | +} |
||
398 | +module_exit(gw_i2c_pld_exit); |
||
399 | + |
||
400 | +MODULE_LICENSE("GPL"); |
||
401 | +MODULE_AUTHOR("Chris Lang"); |
||
402 | --- /dev/null |
||
403 | +++ b/include/linux/i2c/gw_i2c_pld.h |
||
404 | @@ -0,0 +1,20 @@ |
||
405 | +#ifndef __LINUX_GW_I2C_PLD_H |
||
406 | +#define __LINUX_GW_I2C_PLD_H |
||
407 | + |
||
408 | +/** |
||
409 | + * The Gateworks I2C PLD Implements an additional 8 bits of GPIO through the PLD |
||
410 | + */ |
||
411 | + |
||
412 | +struct gw_i2c_pld_platform_data { |
||
413 | + unsigned gpio_base; |
||
414 | + unsigned nr_gpio; |
||
415 | + int (*setup)(struct i2c_client *client, |
||
416 | + int gpio, unsigned ngpio, |
||
417 | + void *context); |
||
418 | + int (*teardown)(struct i2c_client *client, |
||
419 | + int gpio, unsigned ngpio, |
||
420 | + void *context); |
||
421 | + void *context; |
||
422 | +}; |
||
423 | + |
||
424 | +#endif /* __LINUX_GW_I2C_PLD_H */ |