Root/
Source at commit b917bcd created 13 years 7 months ago. By juhosg, ixp4xx: refresh patches | |
---|---|
1 | --- /dev/null |
2 | +++ b/drivers/gpio/gw_i2c_pld.c |
3 | @@ -0,0 +1,371 @@ |
4 | +/* |
5 | + * Gateworks I2C PLD GPIO expander |
6 | + * |
7 | + * Copyright (C) 2009 Gateworks Corporation |
8 | + * |
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. |
13 | + * |
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. |
18 | + * |
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. |
22 | + */ |
23 | + |
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> |
30 | + |
31 | +static const struct i2c_device_id gw_i2c_pld_id[] = { |
32 | + { "gw_i2c_pld", 8 }, |
33 | + { } |
34 | +}; |
35 | +MODULE_DEVICE_TABLE(i2c, gw_i2c_pld_id); |
36 | + |
37 | +/* |
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. |
41 | + */ |
42 | + |
43 | +struct gw_i2c_pld { |
44 | + struct gpio_chip chip; |
45 | + struct i2c_client *client; |
46 | + unsigned out; /* software latch */ |
47 | +}; |
48 | + |
49 | +/*-------------------------------------------------------------------------*/ |
50 | + |
51 | +/* |
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. |
56 | +*/ |
57 | + |
58 | +#define PLD_SCL_GPIO 6 |
59 | +#define PLD_SDA_GPIO 7 |
60 | + |
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); |
69 | + |
70 | +static int i2c_pld_write_byte(int address, int byte) |
71 | +{ |
72 | + int i; |
73 | + |
74 | + address = (address << 1) & ~0x1; |
75 | + |
76 | + SDA_HI(); |
77 | + SDA_EN(); |
78 | + SCL_EN(); |
79 | + SCL_HI(); |
80 | + SDA_LO(); |
81 | + SCL_LO(); |
82 | + |
83 | + for (i = 7; i >= 0; i--) |
84 | + { |
85 | + if (address & (1 << i)) |
86 | + SDA_HI(); |
87 | + else |
88 | + SDA_LO(); |
89 | + |
90 | + SCL_HI(); |
91 | + SCL_LO(); |
92 | + } |
93 | + |
94 | + SDA_DIS(); |
95 | + SCL_HI(); |
96 | + SDA_IN(i); |
97 | + SCL_LO(); |
98 | + SDA_EN(); |
99 | + |
100 | + for (i = 7; i >= 0; i--) |
101 | + { |
102 | + if (byte & (1 << i)) |
103 | + SDA_HI(); |
104 | + else |
105 | + SDA_LO(); |
106 | + SCL_HI(); |
107 | + SCL_LO(); |
108 | + } |
109 | + |
110 | + SDA_DIS(); |
111 | + SCL_HI(); |
112 | + SDA_IN(i); |
113 | + SCL_LO(); |
114 | + |
115 | + SDA_HI(); |
116 | + SDA_EN(); |
117 | + |
118 | + SDA_LO(); |
119 | + SCL_HI(); |
120 | + SDA_HI(); |
121 | + SCL_LO(); |
122 | + SCL_HI(); |
123 | + |
124 | + return 0; |
125 | +} |
126 | + |
127 | +static unsigned int i2c_pld_read_byte(int address) |
128 | +{ |
129 | + int i = 0, byte = 0; |
130 | + int bit; |
131 | + |
132 | + address = (address << 1) | 0x1; |
133 | + |
134 | + SDA_HI(); |
135 | + SDA_EN(); |
136 | + SCL_EN(); |
137 | + SCL_HI(); |
138 | + SDA_LO(); |
139 | + SCL_LO(); |
140 | + |
141 | + for (i = 7; i >= 0; i--) |
142 | + { |
143 | + if (address & (1 << i)) |
144 | + SDA_HI(); |
145 | + else |
146 | + SDA_LO(); |
147 | + |
148 | + SCL_HI(); |
149 | + SCL_LO(); |
150 | + } |
151 | + |
152 | + SDA_DIS(); |
153 | + SCL_HI(); |
154 | + SDA_IN(i); |
155 | + SCL_LO(); |
156 | + SDA_EN(); |
157 | + |
158 | + SDA_DIS(); |
159 | + for (i = 7; i >= 0; i--) |
160 | + { |
161 | + SCL_HI(); |
162 | + SDA_IN(bit); |
163 | + byte |= bit << i; |
164 | + SCL_LO(); |
165 | + } |
166 | + |
167 | + SDA_LO(); |
168 | + SCL_HI(); |
169 | + SDA_HI(); |
170 | + SCL_LO(); |
171 | + SCL_HI(); |
172 | + |
173 | + return byte; |
174 | +} |
175 | + |
176 | + |
177 | +static int gw_i2c_pld_input8(struct gpio_chip *chip, unsigned offset) |
178 | +{ |
179 | + int ret; |
180 | + struct gw_i2c_pld *gpio = container_of(chip, struct gw_i2c_pld, chip); |
181 | + struct i2c_adapter *adap = gpio->client->adapter; |
182 | + |
183 | + if (in_atomic() || irqs_disabled()) { |
184 | + ret = mutex_trylock(&adap->bus_lock); |
185 | + if (!ret) |
186 | + /* I2C activity is ongoing. */ |
187 | + return -EAGAIN; |
188 | + } else { |
189 | + mutex_lock_nested(&adap->bus_lock, adap->level); |
190 | + } |
191 | + |
192 | + gpio->out |= (1 << offset); |
193 | + |
194 | + ret = i2c_pld_write_byte(gpio->client->addr, gpio->out); |
195 | + |
196 | + mutex_unlock(&adap->bus_lock); |
197 | + |
198 | + return ret; |
199 | +} |
200 | + |
201 | +static int gw_i2c_pld_get8(struct gpio_chip *chip, unsigned offset) |
202 | +{ |
203 | + int ret; |
204 | + s32 value; |
205 | + struct gw_i2c_pld *gpio = container_of(chip, struct gw_i2c_pld, chip); |
206 | + struct i2c_adapter *adap = gpio->client->adapter; |
207 | + |
208 | + if (in_atomic() || irqs_disabled()) { |
209 | + ret = mutex_trylock(&adap->bus_lock); |
210 | + if (!ret) |
211 | + /* I2C activity is ongoing. */ |
212 | + return -EAGAIN; |
213 | + } else { |
214 | + mutex_lock_nested(&adap->bus_lock, adap->level); |
215 | + } |
216 | + |
217 | + value = i2c_pld_read_byte(gpio->client->addr); |
218 | + |
219 | + mutex_unlock(&adap->bus_lock); |
220 | + |
221 | + return (value < 0) ? 0 : (value & (1 << offset)); |
222 | +} |
223 | + |
224 | +static int gw_i2c_pld_output8(struct gpio_chip *chip, unsigned offset, int value) |
225 | +{ |
226 | + int ret; |
227 | + |
228 | + struct gw_i2c_pld *gpio = container_of(chip, struct gw_i2c_pld, chip); |
229 | + struct i2c_adapter *adap = gpio->client->adapter; |
230 | + |
231 | + unsigned bit = 1 << offset; |
232 | + |
233 | + if (in_atomic() || irqs_disabled()) { |
234 | + ret = mutex_trylock(&adap->bus_lock); |
235 | + if (!ret) |
236 | + /* I2C activity is ongoing. */ |
237 | + return -EAGAIN; |
238 | + } else { |
239 | + mutex_lock_nested(&adap->bus_lock, adap->level); |
240 | + } |
241 | + |
242 | + |
243 | + if (value) |
244 | + gpio->out |= bit; |
245 | + else |
246 | + gpio->out &= ~bit; |
247 | + |
248 | + ret = i2c_pld_write_byte(gpio->client->addr, gpio->out); |
249 | + |
250 | + mutex_unlock(&adap->bus_lock); |
251 | + |
252 | + return ret; |
253 | +} |
254 | + |
255 | +static void gw_i2c_pld_set8(struct gpio_chip *chip, unsigned offset, int value) |
256 | +{ |
257 | + gw_i2c_pld_output8(chip, offset, value); |
258 | +} |
259 | + |
260 | +/*-------------------------------------------------------------------------*/ |
261 | + |
262 | +static int gw_i2c_pld_probe(struct i2c_client *client, |
263 | + const struct i2c_device_id *id) |
264 | +{ |
265 | + struct gw_i2c_pld_platform_data *pdata; |
266 | + struct gw_i2c_pld *gpio; |
267 | + int status; |
268 | + |
269 | + pdata = client->dev.platform_data; |
270 | + if (!pdata) |
271 | + return -ENODEV; |
272 | + |
273 | + /* Allocate, initialize, and register this gpio_chip. */ |
274 | + gpio = kzalloc(sizeof *gpio, GFP_KERNEL); |
275 | + if (!gpio) |
276 | + return -ENOMEM; |
277 | + |
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; |
282 | + |
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; |
288 | + |
289 | + gpio->chip.label = client->name; |
290 | + |
291 | + gpio->client = client; |
292 | + i2c_set_clientdata(client, gpio); |
293 | + |
294 | + gpio->out = 0xFF; |
295 | + |
296 | + status = gpiochip_add(&gpio->chip); |
297 | + if (status < 0) |
298 | + goto fail; |
299 | + |
300 | + dev_info(&client->dev, "gpios %d..%d on a %s%s\n", |
301 | + gpio->chip.base, |
302 | + gpio->chip.base + gpio->chip.ngpio - 1, |
303 | + client->name, |
304 | + client->irq ? " (irq ignored)" : ""); |
305 | + |
306 | + /* Let platform code set up the GPIOs and their users. |
307 | + * Now is the first time anyone could use them. |
308 | + */ |
309 | + if (pdata->setup) { |
310 | + status = pdata->setup(client, |
311 | + gpio->chip.base, gpio->chip.ngpio, |
312 | + pdata->context); |
313 | + if (status < 0) |
314 | + dev_warn(&client->dev, "setup --> %d\n", status); |
315 | + } |
316 | + |
317 | + return 0; |
318 | + |
319 | +fail: |
320 | + dev_dbg(&client->dev, "probe error %d for '%s'\n", |
321 | + status, client->name); |
322 | + kfree(gpio); |
323 | + return status; |
324 | +} |
325 | + |
326 | +static int gw_i2c_pld_remove(struct i2c_client *client) |
327 | +{ |
328 | + struct gw_i2c_pld_platform_data *pdata = client->dev.platform_data; |
329 | + struct gw_i2c_pld *gpio = i2c_get_clientdata(client); |
330 | + int status = 0; |
331 | + |
332 | + if (pdata->teardown) { |
333 | + status = pdata->teardown(client, |
334 | + gpio->chip.base, gpio->chip.ngpio, |
335 | + pdata->context); |
336 | + if (status < 0) { |
337 | + dev_err(&client->dev, "%s --> %d\n", |
338 | + "teardown", status); |
339 | + return status; |
340 | + } |
341 | + } |
342 | + |
343 | + status = gpiochip_remove(&gpio->chip); |
344 | + if (status == 0) |
345 | + kfree(gpio); |
346 | + else |
347 | + dev_err(&client->dev, "%s --> %d\n", "remove", status); |
348 | + return status; |
349 | +} |
350 | + |
351 | +static struct i2c_driver gw_i2c_pld_driver = { |
352 | + .driver = { |
353 | + .name = "gw_i2c_pld", |
354 | + .owner = THIS_MODULE, |
355 | + }, |
356 | + .probe = gw_i2c_pld_probe, |
357 | + .remove = gw_i2c_pld_remove, |
358 | + .id_table = gw_i2c_pld_id, |
359 | +}; |
360 | + |
361 | +static int __init gw_i2c_pld_init(void) |
362 | +{ |
363 | + return i2c_add_driver(&gw_i2c_pld_driver); |
364 | +} |
365 | +module_init(gw_i2c_pld_init); |
366 | + |
367 | +static void __exit gw_i2c_pld_exit(void) |
368 | +{ |
369 | + i2c_del_driver(&gw_i2c_pld_driver); |
370 | +} |
371 | +module_exit(gw_i2c_pld_exit); |
372 | + |
373 | +MODULE_LICENSE("GPL"); |
374 | +MODULE_AUTHOR("Chris Lang"); |
375 | --- a/drivers/gpio/Kconfig |
376 | +++ b/drivers/gpio/Kconfig |
377 | @@ -275,6 +275,14 @@ config GPIO_TIMBERDALE |
378 | ---help--- |
379 | Add support for the GPIO IP in the timberdale FPGA. |
380 | |
381 | +config GPIO_GW_I2C_PLD |
382 | + tristate "Gateworks I2C PLD GPIO Expander" |
383 | + depends on I2C |
384 | + help |
385 | + Say yes here to provide access to the Gateworks I2C PLD GPIO |
386 | + Expander. This is used at least on the GW2358-4. |
387 | + |
388 | + |
389 | comment "SPI GPIO expanders:" |
390 | |
391 | config GPIO_MAX7301 |
392 | --- a/drivers/gpio/Makefile |
393 | +++ b/drivers/gpio/Makefile |
394 | @@ -27,4 +27,5 @@ obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu. |
395 | obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o |
396 | obj-$(CONFIG_GPIO_WM8350) += wm8350-gpiolib.o |
397 | obj-$(CONFIG_GPIO_WM8994) += wm8994-gpio.o |
398 | -obj-$(CONFIG_GPIO_SCH) += sch_gpio.o |
399 | \ No newline at end of file |
400 | +obj-$(CONFIG_GPIO_SCH) += sch_gpio.o |
401 | +obj-$(CONFIG_GPIO_GW_I2C_PLD) += gw_i2c_pld.o |
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 */ |
425 |