Commit 244fbbb81c46eefcc5f3a9cc37c4668f9d8ff801
Committed by
Jean Delvare
1 parent
c01b083105
Exists in
master
and in
39 other branches
i2c: Add platform driver on top of the new pca-algorithm
Tested on a blackfin. Signed-off-by: Wolfram Sang <w.sang@pengutronix.de> Signed-off-by: Jean Delvare <khali@linux-fr.org>
Showing 4 changed files with 324 additions and 2 deletions Side-by-side Diff
drivers/i2c/busses/Kconfig
... | ... | @@ -632,8 +632,8 @@ |
632 | 632 | select I2C_ALGOPCA |
633 | 633 | default n |
634 | 634 | help |
635 | - This driver supports ISA boards using the Philips PCA 9564 | |
636 | - Parallel bus to I2C bus controller | |
635 | + This driver supports ISA boards using the Philips PCA9564 | |
636 | + parallel bus to I2C bus controller. | |
637 | 637 | |
638 | 638 | This driver can also be built as a module. If so, the module |
639 | 639 | will be called i2c-pca-isa. |
... | ... | @@ -642,6 +642,17 @@ |
642 | 642 | system which doesn't have this device will result in long |
643 | 643 | delays when I2C/SMBus chip drivers are loaded (e.g. at boot |
644 | 644 | time). If unsure, say N. |
645 | + | |
646 | +config I2C_PCA_PLATFORM | |
647 | + tristate "PCA9564 as platform device" | |
648 | + select I2C_ALGOPCA | |
649 | + default n | |
650 | + help | |
651 | + This driver supports a memory mapped Philips PCA9564 | |
652 | + parallel bus to I2C bus controller. | |
653 | + | |
654 | + This driver can also be built as a module. If so, the module | |
655 | + will be called i2c-pca-platform. | |
645 | 656 | |
646 | 657 | config I2C_MV64XXX |
647 | 658 | tristate "Marvell mv64xxx I2C Controller" |
drivers/i2c/busses/Makefile
... | ... | @@ -30,6 +30,7 @@ |
30 | 30 | obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o |
31 | 31 | obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi.o |
32 | 32 | obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o |
33 | +obj-$(CONFIG_I2C_PCA_PLATFORM) += i2c-pca-platform.o | |
33 | 34 | obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o |
34 | 35 | obj-$(CONFIG_I2C_PMCMSP) += i2c-pmcmsp.o |
35 | 36 | obj-$(CONFIG_I2C_PNX) += i2c-pnx.o |
drivers/i2c/busses/i2c-pca-platform.c
1 | +/* | |
2 | + * i2c_pca_platform.c | |
3 | + * | |
4 | + * Platform driver for the PCA9564 I2C controller. | |
5 | + * | |
6 | + * Copyright (C) 2008 Pengutronix | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify | |
9 | + * it under the terms of the GNU General Public License version 2 as | |
10 | + * published by the Free Software Foundation. | |
11 | + | |
12 | + */ | |
13 | +#include <linux/kernel.h> | |
14 | +#include <linux/module.h> | |
15 | +#include <linux/init.h> | |
16 | +#include <linux/slab.h> | |
17 | +#include <linux/delay.h> | |
18 | +#include <linux/errno.h> | |
19 | +#include <linux/i2c.h> | |
20 | +#include <linux/interrupt.h> | |
21 | +#include <linux/platform_device.h> | |
22 | +#include <linux/i2c-algo-pca.h> | |
23 | +#include <linux/i2c-pca-platform.h> | |
24 | +#include <linux/gpio.h> | |
25 | + | |
26 | +#include <asm/irq.h> | |
27 | +#include <asm/io.h> | |
28 | + | |
29 | +#define res_len(r) ((r)->end - (r)->start + 1) | |
30 | + | |
31 | +struct i2c_pca_pf_data { | |
32 | + void __iomem *reg_base; | |
33 | + int irq; /* if 0, use polling */ | |
34 | + int gpio; | |
35 | + wait_queue_head_t wait; | |
36 | + struct i2c_adapter adap; | |
37 | + struct i2c_algo_pca_data algo_data; | |
38 | + unsigned long io_base; | |
39 | + unsigned long io_size; | |
40 | +}; | |
41 | + | |
42 | +/* Read/Write functions for different register alignments */ | |
43 | + | |
44 | +static int i2c_pca_pf_readbyte8(void *pd, int reg) | |
45 | +{ | |
46 | + struct i2c_pca_pf_data *i2c = pd; | |
47 | + return ioread8(i2c->reg_base + reg); | |
48 | +} | |
49 | + | |
50 | +static int i2c_pca_pf_readbyte16(void *pd, int reg) | |
51 | +{ | |
52 | + struct i2c_pca_pf_data *i2c = pd; | |
53 | + return ioread8(i2c->reg_base + reg * 2); | |
54 | +} | |
55 | + | |
56 | +static int i2c_pca_pf_readbyte32(void *pd, int reg) | |
57 | +{ | |
58 | + struct i2c_pca_pf_data *i2c = pd; | |
59 | + return ioread8(i2c->reg_base + reg * 4); | |
60 | +} | |
61 | + | |
62 | +static void i2c_pca_pf_writebyte8(void *pd, int reg, int val) | |
63 | +{ | |
64 | + struct i2c_pca_pf_data *i2c = pd; | |
65 | + iowrite8(val, i2c->reg_base + reg); | |
66 | +} | |
67 | + | |
68 | +static void i2c_pca_pf_writebyte16(void *pd, int reg, int val) | |
69 | +{ | |
70 | + struct i2c_pca_pf_data *i2c = pd; | |
71 | + iowrite8(val, i2c->reg_base + reg * 2); | |
72 | +} | |
73 | + | |
74 | +static void i2c_pca_pf_writebyte32(void *pd, int reg, int val) | |
75 | +{ | |
76 | + struct i2c_pca_pf_data *i2c = pd; | |
77 | + iowrite8(val, i2c->reg_base + reg * 4); | |
78 | +} | |
79 | + | |
80 | + | |
81 | +static int i2c_pca_pf_waitforcompletion(void *pd) | |
82 | +{ | |
83 | + struct i2c_pca_pf_data *i2c = pd; | |
84 | + int ret = 0; | |
85 | + | |
86 | + if (i2c->irq) { | |
87 | + ret = wait_event_interruptible(i2c->wait, | |
88 | + i2c->algo_data.read_byte(i2c, I2C_PCA_CON) | |
89 | + & I2C_PCA_CON_SI); | |
90 | + } else { | |
91 | + /* | |
92 | + * Do polling... | |
93 | + * XXX: Could get stuck in extreme cases! | |
94 | + * Maybe add timeout, but using irqs is preferred anyhow. | |
95 | + */ | |
96 | + while ((i2c->algo_data.read_byte(i2c, I2C_PCA_CON) | |
97 | + & I2C_PCA_CON_SI) == 0) | |
98 | + udelay(100); | |
99 | + } | |
100 | + | |
101 | + return ret; | |
102 | +} | |
103 | + | |
104 | +static void i2c_pca_pf_dummyreset(void *pd) | |
105 | +{ | |
106 | + struct i2c_pca_pf_data *i2c = pd; | |
107 | + printk(KERN_WARNING "%s: No reset-pin found. Chip may get stuck!\n", | |
108 | + i2c->adap.name); | |
109 | +} | |
110 | + | |
111 | +static void i2c_pca_pf_resetchip(void *pd) | |
112 | +{ | |
113 | + struct i2c_pca_pf_data *i2c = pd; | |
114 | + | |
115 | + gpio_set_value(i2c->gpio, 0); | |
116 | + ndelay(100); | |
117 | + gpio_set_value(i2c->gpio, 1); | |
118 | +} | |
119 | + | |
120 | +static irqreturn_t i2c_pca_pf_handler(int this_irq, void *dev_id) | |
121 | +{ | |
122 | + struct i2c_pca_pf_data *i2c = dev_id; | |
123 | + | |
124 | + if ((i2c->algo_data.read_byte(i2c, I2C_PCA_CON) & I2C_PCA_CON_SI) == 0) | |
125 | + return IRQ_NONE; | |
126 | + | |
127 | + wake_up_interruptible(&i2c->wait); | |
128 | + | |
129 | + return IRQ_HANDLED; | |
130 | +} | |
131 | + | |
132 | + | |
133 | +static int __devinit i2c_pca_pf_probe(struct platform_device *pdev) | |
134 | +{ | |
135 | + struct i2c_pca_pf_data *i2c; | |
136 | + struct resource *res; | |
137 | + struct i2c_pca9564_pf_platform_data *platform_data = | |
138 | + pdev->dev.platform_data; | |
139 | + int ret = 0; | |
140 | + int irq; | |
141 | + | |
142 | + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
143 | + irq = platform_get_irq(pdev, 0); | |
144 | + /* If irq is 0, we do polling. */ | |
145 | + | |
146 | + if (res == NULL) { | |
147 | + ret = -ENODEV; | |
148 | + goto e_print; | |
149 | + } | |
150 | + | |
151 | + if (!request_mem_region(res->start, res_len(res), res->name)) { | |
152 | + ret = -ENOMEM; | |
153 | + goto e_print; | |
154 | + } | |
155 | + | |
156 | + i2c = kzalloc(sizeof(struct i2c_pca_pf_data), GFP_KERNEL); | |
157 | + if (!i2c) { | |
158 | + ret = -ENOMEM; | |
159 | + goto e_alloc; | |
160 | + } | |
161 | + | |
162 | + init_waitqueue_head(&i2c->wait); | |
163 | + | |
164 | + i2c->reg_base = ioremap(res->start, res_len(res)); | |
165 | + if (!i2c->reg_base) { | |
166 | + ret = -EIO; | |
167 | + goto e_remap; | |
168 | + } | |
169 | + i2c->io_base = res->start; | |
170 | + i2c->io_size = res_len(res); | |
171 | + i2c->irq = irq; | |
172 | + | |
173 | + i2c->adap.nr = pdev->id >= 0 ? pdev->id : 0; | |
174 | + i2c->adap.owner = THIS_MODULE; | |
175 | + snprintf(i2c->adap.name, sizeof(i2c->adap.name), "PCA9564 at 0x%08lx", | |
176 | + (unsigned long) res->start); | |
177 | + i2c->adap.algo_data = &i2c->algo_data; | |
178 | + i2c->adap.dev.parent = &pdev->dev; | |
179 | + i2c->adap.timeout = platform_data->timeout; | |
180 | + | |
181 | + i2c->algo_data.i2c_clock = platform_data->i2c_clock_speed; | |
182 | + i2c->algo_data.data = i2c; | |
183 | + | |
184 | + switch (res->flags & IORESOURCE_MEM_TYPE_MASK) { | |
185 | + case IORESOURCE_MEM_32BIT: | |
186 | + i2c->algo_data.write_byte = i2c_pca_pf_writebyte32; | |
187 | + i2c->algo_data.read_byte = i2c_pca_pf_readbyte32; | |
188 | + break; | |
189 | + case IORESOURCE_MEM_16BIT: | |
190 | + i2c->algo_data.write_byte = i2c_pca_pf_writebyte16; | |
191 | + i2c->algo_data.read_byte = i2c_pca_pf_readbyte16; | |
192 | + break; | |
193 | + case IORESOURCE_MEM_8BIT: | |
194 | + default: | |
195 | + i2c->algo_data.write_byte = i2c_pca_pf_writebyte8; | |
196 | + i2c->algo_data.read_byte = i2c_pca_pf_readbyte8; | |
197 | + break; | |
198 | + } | |
199 | + | |
200 | + i2c->algo_data.wait_for_completion = i2c_pca_pf_waitforcompletion; | |
201 | + | |
202 | + i2c->gpio = platform_data->gpio; | |
203 | + i2c->algo_data.reset_chip = i2c_pca_pf_dummyreset; | |
204 | + | |
205 | + /* Use gpio_is_valid() when in mainline */ | |
206 | + if (i2c->gpio > -1) { | |
207 | + ret = gpio_request(i2c->gpio, i2c->adap.name); | |
208 | + if (ret == 0) { | |
209 | + gpio_direction_output(i2c->gpio, 1); | |
210 | + i2c->algo_data.reset_chip = i2c_pca_pf_resetchip; | |
211 | + } else { | |
212 | + printk(KERN_WARNING "%s: Registering gpio failed!\n", | |
213 | + i2c->adap.name); | |
214 | + i2c->gpio = ret; | |
215 | + } | |
216 | + } | |
217 | + | |
218 | + if (irq) { | |
219 | + ret = request_irq(irq, i2c_pca_pf_handler, | |
220 | + IRQF_TRIGGER_FALLING, i2c->adap.name, i2c); | |
221 | + if (ret) | |
222 | + goto e_reqirq; | |
223 | + } | |
224 | + | |
225 | + if (i2c_pca_add_numbered_bus(&i2c->adap) < 0) { | |
226 | + ret = -ENODEV; | |
227 | + goto e_adapt; | |
228 | + } | |
229 | + | |
230 | + platform_set_drvdata(pdev, i2c); | |
231 | + | |
232 | + printk(KERN_INFO "%s registered.\n", i2c->adap.name); | |
233 | + | |
234 | + return 0; | |
235 | + | |
236 | +e_adapt: | |
237 | + if (irq) | |
238 | + free_irq(irq, i2c); | |
239 | +e_reqirq: | |
240 | + if (i2c->gpio > -1) | |
241 | + gpio_free(i2c->gpio); | |
242 | + | |
243 | + iounmap(i2c->reg_base); | |
244 | +e_remap: | |
245 | + kfree(i2c); | |
246 | +e_alloc: | |
247 | + release_mem_region(res->start, res_len(res)); | |
248 | +e_print: | |
249 | + printk(KERN_ERR "Registering PCA9564 FAILED! (%d)\n", ret); | |
250 | + return ret; | |
251 | +} | |
252 | + | |
253 | +static int __devexit i2c_pca_pf_remove(struct platform_device *pdev) | |
254 | +{ | |
255 | + struct i2c_pca_pf_data *i2c = platform_get_drvdata(pdev); | |
256 | + platform_set_drvdata(pdev, NULL); | |
257 | + | |
258 | + i2c_del_adapter(&i2c->adap); | |
259 | + | |
260 | + if (i2c->irq) | |
261 | + free_irq(i2c->irq, i2c); | |
262 | + | |
263 | + if (i2c->gpio > -1) | |
264 | + gpio_free(i2c->gpio); | |
265 | + | |
266 | + iounmap(i2c->reg_base); | |
267 | + release_mem_region(i2c->io_base, i2c->io_size); | |
268 | + kfree(i2c); | |
269 | + | |
270 | + return 0; | |
271 | +} | |
272 | + | |
273 | +static struct platform_driver i2c_pca_pf_driver = { | |
274 | + .probe = i2c_pca_pf_probe, | |
275 | + .remove = __devexit_p(i2c_pca_pf_remove), | |
276 | + .driver = { | |
277 | + .name = "i2c-pca-platform", | |
278 | + .owner = THIS_MODULE, | |
279 | + }, | |
280 | +}; | |
281 | + | |
282 | +static int __init i2c_pca_pf_init(void) | |
283 | +{ | |
284 | + return platform_driver_register(&i2c_pca_pf_driver); | |
285 | +} | |
286 | + | |
287 | +static void __exit i2c_pca_pf_exit(void) | |
288 | +{ | |
289 | + platform_driver_unregister(&i2c_pca_pf_driver); | |
290 | +} | |
291 | + | |
292 | +MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>"); | |
293 | +MODULE_DESCRIPTION("I2C-PCA9564 platform driver"); | |
294 | +MODULE_LICENSE("GPL"); | |
295 | + | |
296 | +module_init(i2c_pca_pf_init); | |
297 | +module_exit(i2c_pca_pf_exit); |
include/linux/i2c-pca-platform.h
1 | +#ifndef I2C_PCA9564_PLATFORM_H | |
2 | +#define I2C_PCA9564_PLATFORM_H | |
3 | + | |
4 | +struct i2c_pca9564_pf_platform_data { | |
5 | + int gpio; /* pin to reset chip. driver will work when | |
6 | + * not supplied (negative value), but it | |
7 | + * cannot exit some error conditions then */ | |
8 | + int i2c_clock_speed; /* values are defined in linux/i2c-algo-pca.h */ | |
9 | + int timeout; /* timeout = this value * 10us */ | |
10 | +}; | |
11 | + | |
12 | +#endif /* I2C_PCA9564_PLATFORM_H */ |