Root/
Source at commit 1c406da created 12 years 7 months ago. By Xiangfu Liu, ben nanonote: forward patches to linux-3.0 | |
---|---|
1 | From 5e219770079a61b8c8e59abe5510678361c94696 Mon Sep 17 00:00:00 2001 |
2 | From: Lars-Peter Clausen <lars@metafoo.de> |
3 | Date: Sun, 5 Sep 2010 03:19:10 +0200 |
4 | Subject: [PATCH 08/29] i2c: Add i2c driver for JZ47XX SoCs |
5 | |
6 | This patch adds a driver for the i2c controller found in Ingenic JZ47XX based |
7 | SoCs. |
8 | |
9 | Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> |
10 | --- |
11 | drivers/i2c/busses/Kconfig | 10 + |
12 | drivers/i2c/busses/Makefile | 1 + |
13 | drivers/i2c/busses/i2c-jz47xx.c | 424 +++++++++++++++++++++++++++++++++++++++ |
14 | 3 files changed, 435 insertions(+), 0 deletions(-) |
15 | create mode 100644 drivers/i2c/busses/i2c-jz47xx.c |
16 | |
17 | diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig |
18 | index 646068e..5b76fcf 100644 |
19 | --- a/drivers/i2c/busses/Kconfig |
20 | +++ b/drivers/i2c/busses/Kconfig |
21 | @@ -434,6 +434,16 @@ config I2C_IXP2000 |
22 | This driver is deprecated and will be dropped soon. Use i2c-gpio |
23 | instead. |
24 | |
25 | +config I2C_JZ47XX |
26 | + tristate "JZ4740 I2C Interface" |
27 | + depends on ARCH_JZ4740 |
28 | + help |
29 | + Say Y here if you want support for the I2C controller found on Ingenic |
30 | + JZ47XX based SoCs. |
31 | + |
32 | + This driver can also be built as a module. If so, the module will be |
33 | + called i2c-jz47xx. |
34 | + |
35 | config I2C_MPC |
36 | tristate "MPC107/824x/85xx/512x/52xx/83xx/86xx" |
37 | depends on PPC |
38 | diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile |
39 | index e6cf294..07be071 100644 |
40 | --- a/drivers/i2c/busses/Makefile |
41 | +++ b/drivers/i2c/busses/Makefile |
42 | @@ -41,6 +41,7 @@ obj-$(CONFIG_I2C_IMX) += i2c-imx.o |
43 | obj-$(CONFIG_I2C_INTEL_MID) += i2c-intel-mid.o |
44 | obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o |
45 | obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o |
46 | +obj-$(CONFIG_I2C_JZ47XX) += i2c-jz47xx.o |
47 | obj-$(CONFIG_I2C_MPC) += i2c-mpc.o |
48 | obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o |
49 | obj-$(CONFIG_I2C_MXS) += i2c-mxs.o |
50 | diff --git a/drivers/i2c/busses/i2c-jz47xx.c b/drivers/i2c/busses/i2c-jz47xx.c |
51 | new file mode 100644 |
52 | index 0000000..492d350 |
53 | --- /dev/null |
54 | +++ b/drivers/i2c/busses/i2c-jz47xx.c |
55 | @@ -0,0 +1,424 @@ |
56 | + |
57 | +#include <linux/init.h> |
58 | +#include <linux/kernel.h> |
59 | +#include <linux/module.h> |
60 | +#include <linux/err.h> |
61 | +#include <linux/clk.h> |
62 | +#include <linux/platform_device.h> |
63 | +#include <linux/i2c.h> |
64 | +#include <linux/slab.h> |
65 | +#include <linux/interrupt.h> |
66 | + |
67 | +#include <linux/gpio.h> |
68 | +#include <linux/delay.h> |
69 | + |
70 | +#define JZ47XX_REG_I2C_DATA 0x00 |
71 | +#define JZ47XX_REG_I2C_CTRL 0x04 |
72 | +#define JZ47XX_REG_I2C_STATUS 0x08 |
73 | +#define JZ47XX_REG_I2C_CLOCK 0x0C |
74 | + |
75 | +#define JZ47XX_I2C_STATUS_FIFO_FULL BIT(4) |
76 | +#define JZ47XX_I2C_STATUS_BUSY BIT(3) |
77 | +#define JZ47XX_I2C_STATUS_TEND BIT(2) |
78 | +#define JZ47XX_I2C_STATUS_DATA_VALID BIT(1) |
79 | +#define JZ47XX_I2C_STATUS_NACK BIT(0) |
80 | + |
81 | +#define JZ47XX_I2C_CTRL_IRQ_ENABLE BIT(4) |
82 | +#define JZ47XX_I2C_CTRL_START BIT(3) |
83 | +#define JZ47XX_I2C_CTRL_STOP BIT(2) |
84 | +#define JZ47XX_I2C_CTRL_NACK BIT(1) |
85 | +#define JZ47XX_I2C_CTRL_ENABLE BIT(0) |
86 | + |
87 | +struct jz47xx_i2c { |
88 | + struct resource *mem; |
89 | + void __iomem *base; |
90 | + int irq; |
91 | + struct clk *clk; |
92 | + |
93 | + struct i2c_adapter adapter; |
94 | + |
95 | + wait_queue_head_t wait_queue; |
96 | +}; |
97 | + |
98 | +static inline struct jz47xx_i2c *adapter_to_jz47xx_i2c(struct i2c_adapter *adap) |
99 | +{ |
100 | + return container_of(adap, struct jz47xx_i2c, adapter); |
101 | +} |
102 | + |
103 | +static inline void jz47xx_i2c_set_ctrl(struct jz47xx_i2c *jz47xx_i2c, |
104 | + uint8_t mask, uint8_t value) |
105 | +{ |
106 | + uint8_t ctrl; |
107 | + ctrl = readb(jz47xx_i2c->base + JZ47XX_REG_I2C_CTRL); |
108 | + ctrl &= ~mask; |
109 | + ctrl |= value; |
110 | + printk("ctrl: %x\n", ctrl); |
111 | + writeb(ctrl, jz47xx_i2c->base + JZ47XX_REG_I2C_CTRL); |
112 | +} |
113 | + |
114 | +static irqreturn_t jz47xx_i2c_irq_handler(int irq, void *devid) |
115 | +{ |
116 | + struct jz47xx_i2c *jz47xx_i2c = devid; |
117 | + |
118 | + printk("IRQ\n"); |
119 | + |
120 | + wake_up(&jz47xx_i2c->wait_queue); |
121 | + |
122 | + jz47xx_i2c_set_ctrl(jz47xx_i2c, JZ47XX_I2C_CTRL_IRQ_ENABLE, 0); |
123 | + |
124 | + return IRQ_HANDLED; |
125 | +} |
126 | + |
127 | +static inline void jz47xx_i2c_set_data_valid(struct jz47xx_i2c *jz47xx_i2c, |
128 | + bool valid) |
129 | +{ |
130 | + uint8_t val; |
131 | + val = readb(jz47xx_i2c->base + JZ47XX_REG_I2C_STATUS); |
132 | + if (valid) |
133 | + val |= JZ47XX_I2C_STATUS_DATA_VALID; |
134 | + else |
135 | + val &= ~JZ47XX_I2C_STATUS_DATA_VALID; |
136 | + writeb(val, jz47xx_i2c->base + JZ47XX_REG_I2C_STATUS); |
137 | +} |
138 | + |
139 | +static int jz47xx_i2c_test_event(struct jz47xx_i2c *jz47xx_i2c, uint8_t mask, uint8_t value) |
140 | +{ |
141 | + uint8_t status; |
142 | + |
143 | + mask |= JZ47XX_I2C_STATUS_NACK; |
144 | + value |= JZ47XX_I2C_STATUS_NACK; |
145 | + |
146 | + status = readb(jz47xx_i2c->base + JZ47XX_REG_I2C_STATUS); |
147 | + printk("status: %x %x %x %x\n", status, mask, value, (status & mask) ^ |
148 | + value); |
149 | + if (((status & mask) ^ value) == mask) { |
150 | + jz47xx_i2c_set_ctrl(jz47xx_i2c, JZ47XX_I2C_CTRL_IRQ_ENABLE, |
151 | + JZ47XX_I2C_CTRL_IRQ_ENABLE); |
152 | + return 0; |
153 | + } |
154 | + return 1; |
155 | +} |
156 | + |
157 | +static int jz47xx_i2c_wait_event_or_nack(struct jz47xx_i2c *jz47xx_i2c, uint8_t |
158 | +mask, uint8_t value) |
159 | +{ |
160 | + int ret; |
161 | + |
162 | + ret = wait_event_interruptible_timeout(jz47xx_i2c->wait_queue, |
163 | + jz47xx_i2c_test_event(jz47xx_i2c, mask, value), 30 * HZ); |
164 | + |
165 | +/* while (!jz47xx_i2c_test_event(jz47xx_i2c, mask, value)); |
166 | + |
167 | + ret = 1;*/ |
168 | + |
169 | + printk("wait event or nack: %d %x\n", ret, readb(jz47xx_i2c->base + |
170 | + JZ47XX_REG_I2C_STATUS)); |
171 | + |
172 | + if (ret == 0) |
173 | + ret = -ETIMEDOUT; |
174 | + else if(ret > 0) { |
175 | + if (readb(jz47xx_i2c->base + JZ47XX_REG_I2C_STATUS) & JZ47XX_I2C_STATUS_NACK) |
176 | + ret = -EIO; |
177 | + else |
178 | + ret = 0; |
179 | + } |
180 | + |
181 | + return ret; |
182 | +} |
183 | + |
184 | +static int jz47xx_i2c_wait_event(struct jz47xx_i2c *jz47xx_i2c, uint8_t event) |
185 | +{ |
186 | + int ret; |
187 | + |
188 | + ret = wait_event_interruptible_timeout(jz47xx_i2c->wait_queue, |
189 | + jz47xx_i2c_test_event(jz47xx_i2c, event, event), 30 * HZ); |
190 | + |
191 | + if (ret == 0) |
192 | + ret = -ETIMEDOUT; |
193 | + else if(ret > 0) |
194 | + ret = 0; |
195 | + |
196 | + return ret; |
197 | +} |
198 | + |
199 | + |
200 | +static int jz47xx_i2c_write_msg(struct jz47xx_i2c *jz47xx_i2c, |
201 | + struct i2c_msg *msg) |
202 | +{ |
203 | + int ret; |
204 | + int i; |
205 | + |
206 | + printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__); |
207 | + for (i = 0; i < msg->len; ++i) { |
208 | + writeb(msg->buf[i], jz47xx_i2c->base + JZ47XX_REG_I2C_DATA); |
209 | + jz47xx_i2c_set_data_valid(jz47xx_i2c, true); |
210 | + ret = jz47xx_i2c_wait_event_or_nack(jz47xx_i2c, |
211 | + JZ47XX_I2C_STATUS_DATA_VALID, 0); |
212 | + if (ret) |
213 | + break; |
214 | + } |
215 | + jz47xx_i2c_set_ctrl(jz47xx_i2c, JZ47XX_I2C_CTRL_STOP, |
216 | + JZ47XX_I2C_CTRL_STOP); |
217 | + |
218 | + if (!ret) |
219 | + ret = jz47xx_i2c_wait_event_or_nack(jz47xx_i2c, JZ47XX_I2C_STATUS_TEND, |
220 | + JZ47XX_I2C_STATUS_TEND); |
221 | + |
222 | + return ret; |
223 | +} |
224 | + |
225 | +static int jz47xx_i2c_read_msg(struct jz47xx_i2c *jz47xx_i2c, |
226 | + struct i2c_msg *msg) |
227 | +{ |
228 | + int i; |
229 | + int ret; |
230 | + printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__); |
231 | + |
232 | + jz47xx_i2c_set_ctrl(jz47xx_i2c, JZ47XX_I2C_CTRL_NACK, |
233 | + msg->len == 1 ? JZ47XX_I2C_CTRL_NACK : 0); |
234 | + |
235 | + for (i = 0; i < msg->len; ++i) { |
236 | + ret = jz47xx_i2c_wait_event(jz47xx_i2c, JZ47XX_I2C_STATUS_DATA_VALID); |
237 | + if (ret) { |
238 | + jz47xx_i2c_set_ctrl(jz47xx_i2c, JZ47XX_I2C_CTRL_NACK, |
239 | + JZ47XX_I2C_CTRL_NACK); |
240 | + break; |
241 | + } |
242 | + |
243 | + if (i == msg->len - 2) { |
244 | + jz47xx_i2c_set_ctrl(jz47xx_i2c, JZ47XX_I2C_CTRL_NACK, |
245 | + JZ47XX_I2C_CTRL_NACK); |
246 | + } |
247 | + |
248 | + msg->buf[i] = readb(jz47xx_i2c->base + JZ47XX_REG_I2C_DATA); |
249 | + printk("read: %x\n", msg->buf[i]); |
250 | + jz47xx_i2c_set_data_valid(jz47xx_i2c, false); |
251 | + } |
252 | + |
253 | + jz47xx_i2c_set_ctrl(jz47xx_i2c, JZ47XX_I2C_CTRL_STOP, |
254 | + JZ47XX_I2C_CTRL_STOP); |
255 | + |
256 | + return ret; |
257 | +} |
258 | + |
259 | +static int jz47xx_i2c_xfer_msg(struct jz47xx_i2c *jz47xx_i2c, |
260 | + struct i2c_msg *msg) |
261 | +{ |
262 | + uint8_t addr; |
263 | + int ret; |
264 | + |
265 | + addr = msg->addr << 1; |
266 | + if (msg->flags & I2C_M_RD) |
267 | + addr |= 1; |
268 | + |
269 | + jz47xx_i2c_set_ctrl(jz47xx_i2c, JZ47XX_I2C_CTRL_START, |
270 | + JZ47XX_I2C_CTRL_START); |
271 | + writeb(addr, jz47xx_i2c->base + JZ47XX_REG_I2C_DATA); |
272 | + jz47xx_i2c_set_data_valid(jz47xx_i2c, true); |
273 | + |
274 | + if (msg->flags & I2C_M_RD) { |
275 | + printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__); |
276 | + ret = jz47xx_i2c_wait_event_or_nack(jz47xx_i2c, |
277 | + JZ47XX_I2C_STATUS_TEND, JZ47XX_I2C_STATUS_TEND); |
278 | + if (!ret) |
279 | + ret = jz47xx_i2c_read_msg(jz47xx_i2c, msg); |
280 | + } else { |
281 | + printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__); |
282 | + ret = jz47xx_i2c_wait_event_or_nack(jz47xx_i2c, |
283 | + JZ47XX_I2C_STATUS_DATA_VALID, 0); |
284 | + if (!ret) |
285 | + ret = jz47xx_i2c_write_msg(jz47xx_i2c, msg); |
286 | + } |
287 | + |
288 | + return ret; |
289 | +} |
290 | + |
291 | +static int jz47xx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int |
292 | +num) |
293 | +{ |
294 | + struct jz47xx_i2c *jz47xx_i2c = adapter_to_jz47xx_i2c(adap); |
295 | + int ret = 0; |
296 | + int i; |
297 | + int mask = JZ47XX_I2C_CTRL_ENABLE; |
298 | + |
299 | + printk("xfer: %d %x\n", num, readb(jz47xx_i2c->base + |
300 | + JZ47XX_REG_I2C_STATUS)); |
301 | + |
302 | + clk_enable(jz47xx_i2c->clk); |
303 | + jz47xx_i2c_set_ctrl(jz47xx_i2c, mask, mask); |
304 | + |
305 | + for (i = 0; i < num; ++i) { |
306 | + ret = jz47xx_i2c_xfer_msg(jz47xx_i2c, &msgs[i]); |
307 | + if (ret) |
308 | + break; |
309 | + } |
310 | + |
311 | + jz47xx_i2c_set_ctrl(jz47xx_i2c, mask, 0); |
312 | + clk_disable(jz47xx_i2c->clk); |
313 | + |
314 | + printk("xfer ret: %d\n", ret); |
315 | + |
316 | + return ret; |
317 | +} |
318 | + |
319 | +static u32 jz47xx_i2c_functionality(struct i2c_adapter *adap) |
320 | +{ |
321 | + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; |
322 | +} |
323 | + |
324 | +static const struct i2c_algorithm jz47xx_i2c_algorithm = { |
325 | + .master_xfer = jz47xx_i2c_xfer, |
326 | + .functionality = jz47xx_i2c_functionality, |
327 | +}; |
328 | + |
329 | +const static struct jz_gpio_bulk_request jz47xx_i2c_pins[] = { |
330 | + JZ_GPIO_BULK_PIN(I2C_SDA), |
331 | + JZ_GPIO_BULK_PIN(I2C_SCK), |
332 | +}; |
333 | + |
334 | +static int __devinit jz47xx_i2c_probe(struct platform_device *pdev) |
335 | +{ |
336 | + struct jz47xx_i2c *jz47xx_i2c; |
337 | + struct resource *mem; |
338 | + void __iomem *base; |
339 | + struct clk *clk; |
340 | + int irq; |
341 | + int ret; |
342 | + |
343 | + irq = platform_get_irq(pdev, 0); |
344 | + if (!irq) { |
345 | + dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq); |
346 | + return irq; |
347 | + } |
348 | + |
349 | + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
350 | + if (!mem) { |
351 | + dev_err(&pdev->dev, "Failed to get iomem region\n"); |
352 | + return -ENXIO; |
353 | + } |
354 | + |
355 | + mem = request_mem_region(mem->start, resource_size(mem), pdev->name); |
356 | + if (!mem) { |
357 | + dev_err(&pdev->dev, "Failed to request iomem region\n"); |
358 | + return -EBUSY; |
359 | + } |
360 | + |
361 | + base = ioremap(mem->start, resource_size(mem)); |
362 | + if (!base) { |
363 | + dev_err(&pdev->dev, "Failed to ioremap iomem\n"); |
364 | + ret = -EBUSY; |
365 | + goto err_release_mem_region; |
366 | + } |
367 | + |
368 | + clk = clk_get(&pdev->dev, "i2c"); |
369 | + if (IS_ERR(clk)) { |
370 | + ret = PTR_ERR(clk); |
371 | + goto err_iounmap; |
372 | + } |
373 | + |
374 | + jz47xx_i2c = kzalloc(sizeof(*jz47xx_i2c), GFP_KERNEL); |
375 | + if (!jz47xx_i2c) { |
376 | + ret = -ENOMEM; |
377 | + goto err_clk_put; |
378 | + } |
379 | + |
380 | + jz47xx_i2c->adapter.owner = THIS_MODULE; |
381 | + jz47xx_i2c->adapter.algo = &jz47xx_i2c_algorithm; |
382 | + jz47xx_i2c->adapter.dev.parent = &pdev->dev; |
383 | + jz47xx_i2c->adapter.nr = pdev->id < 0 ?: 0; |
384 | + strcpy(jz47xx_i2c->adapter.name, pdev->name); |
385 | + |
386 | + jz47xx_i2c->mem = mem; |
387 | + jz47xx_i2c->base = base; |
388 | + jz47xx_i2c->clk = clk; |
389 | + jz47xx_i2c->irq = irq; |
390 | + |
391 | + init_waitqueue_head(&jz47xx_i2c->wait_queue); |
392 | + |
393 | + ret = request_irq(irq, jz47xx_i2c_irq_handler, 0, pdev->name, jz47xx_i2c); |
394 | + if (ret) { |
395 | + dev_err(&pdev->dev, "Failed to request irq: %d\n", ret); |
396 | + goto err_free; |
397 | + } |
398 | + |
399 | + ret = jz_gpio_bulk_request(jz47xx_i2c_pins, ARRAY_SIZE(jz47xx_i2c_pins)); |
400 | + if (ret) { |
401 | + dev_err(&pdev->dev, "Failed to request i2c pins: %d\n", ret); |
402 | + goto err_free_irq; |
403 | + } |
404 | + |
405 | + writew(0x10, jz47xx_i2c->base + JZ47XX_REG_I2C_CLOCK); |
406 | + |
407 | + ret = i2c_add_numbered_adapter(&jz47xx_i2c->adapter); |
408 | + if (ret) { |
409 | + dev_err(&pdev->dev, "Failed to add i2c adapter: %d\n", ret); |
410 | + goto err_free_gpios; |
411 | + } |
412 | + |
413 | + platform_set_drvdata(pdev, jz47xx_i2c); |
414 | + |
415 | + printk("JZ4740 I2C\n"); |
416 | + |
417 | + return 0; |
418 | + |
419 | +err_free_gpios: |
420 | + jz_gpio_bulk_free(jz47xx_i2c_pins, ARRAY_SIZE(jz47xx_i2c_pins)); |
421 | +err_free_irq: |
422 | + free_irq(irq, jz47xx_i2c); |
423 | +err_free: |
424 | + kfree(jz47xx_i2c); |
425 | +err_clk_put: |
426 | + clk_put(clk); |
427 | +err_iounmap: |
428 | + iounmap(base); |
429 | +err_release_mem_region: |
430 | + release_mem_region(mem->start, resource_size(mem)); |
431 | + return ret; |
432 | +} |
433 | + |
434 | +static int __devexit jz47xx_i2c_remove(struct platform_device *pdev) |
435 | +{ |
436 | + struct jz47xx_i2c *jz47xx_i2c = platform_get_drvdata(pdev); |
437 | + |
438 | + platform_set_drvdata(pdev, NULL); |
439 | + i2c_del_adapter(&jz47xx_i2c->adapter); |
440 | + |
441 | + jz_gpio_bulk_free(jz47xx_i2c_pins, ARRAY_SIZE(jz47xx_i2c_pins)); |
442 | + |
443 | + free_irq(jz47xx_i2c->irq, jz47xx_i2c); |
444 | + clk_put(jz47xx_i2c->clk); |
445 | + |
446 | + iounmap(jz47xx_i2c->base); |
447 | + release_mem_region(jz47xx_i2c->mem->start, resource_size(jz47xx_i2c->mem)); |
448 | + |
449 | + kfree(jz47xx_i2c); |
450 | + |
451 | + return 0; |
452 | +} |
453 | + |
454 | +static struct platform_driver jz47xx_i2c_driver = { |
455 | + .probe = jz47xx_i2c_probe, |
456 | + .remove = jz47xx_i2c_remove, |
457 | + .driver = { |
458 | + .name = "jz47xx-i2c", |
459 | + .owner = THIS_MODULE, |
460 | + }, |
461 | +}; |
462 | + |
463 | +static int __init jz47xx_i2c_init(void) |
464 | +{ |
465 | + return platform_driver_register(&jz47xx_i2c_driver); |
466 | +} |
467 | +module_init(jz47xx_i2c_init); |
468 | + |
469 | +static void jz47xx_i2c_exit(void) |
470 | +{ |
471 | + platform_driver_unregister(&jz47xx_i2c_driver); |
472 | +} |
473 | +module_exit(jz47xx_i2c_exit); |
474 | + |
475 | +MODULE_LICENSE("GPL"); |
476 | +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); |
477 | +MODULE_DESCRIPTION("I2C adapter driver for JZ47XX SoCs"); |
478 | +MODULE_ALIAS("platform:jz47xx-i2c"); |
479 | + |
480 | -- |
481 | 1.7.4.1 |
482 | |
483 |