diff options
Diffstat (limited to 'arch/mips/mti-sead3/sead3-pic32-i2c-drv.c')
-rw-r--r-- | arch/mips/mti-sead3/sead3-pic32-i2c-drv.c | 435 |
1 files changed, 435 insertions, 0 deletions
diff --git a/arch/mips/mti-sead3/sead3-pic32-i2c-drv.c b/arch/mips/mti-sead3/sead3-pic32-i2c-drv.c new file mode 100644 index 000000000000..46509b0a620d --- /dev/null +++ b/arch/mips/mti-sead3/sead3-pic32-i2c-drv.c | |||
@@ -0,0 +1,435 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. | ||
7 | */ | ||
8 | #include <linux/delay.h> | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/spinlock.h> | ||
12 | #include <linux/platform_device.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/i2c.h> | ||
16 | #include <linux/slab.h> | ||
17 | |||
18 | #define PIC32_I2CxCON 0x0000 | ||
19 | #define PIC32_I2CxCONCLR 0x0004 | ||
20 | #define PIC32_I2CxCONSET 0x0008 | ||
21 | #define PIC32_I2CxCONINV 0x000C | ||
22 | #define I2CCON_ON (1<<15) | ||
23 | #define I2CCON_FRZ (1<<14) | ||
24 | #define I2CCON_SIDL (1<<13) | ||
25 | #define I2CCON_SCLREL (1<<12) | ||
26 | #define I2CCON_STRICT (1<<11) | ||
27 | #define I2CCON_A10M (1<<10) | ||
28 | #define I2CCON_DISSLW (1<<9) | ||
29 | #define I2CCON_SMEN (1<<8) | ||
30 | #define I2CCON_GCEN (1<<7) | ||
31 | #define I2CCON_STREN (1<<6) | ||
32 | #define I2CCON_ACKDT (1<<5) | ||
33 | #define I2CCON_ACKEN (1<<4) | ||
34 | #define I2CCON_RCEN (1<<3) | ||
35 | #define I2CCON_PEN (1<<2) | ||
36 | #define I2CCON_RSEN (1<<1) | ||
37 | #define I2CCON_SEN (1<<0) | ||
38 | |||
39 | #define PIC32_I2CxSTAT 0x0010 | ||
40 | #define PIC32_I2CxSTATCLR 0x0014 | ||
41 | #define PIC32_I2CxSTATSET 0x0018 | ||
42 | #define PIC32_I2CxSTATINV 0x001C | ||
43 | #define I2CSTAT_ACKSTAT (1<<15) | ||
44 | #define I2CSTAT_TRSTAT (1<<14) | ||
45 | #define I2CSTAT_BCL (1<<10) | ||
46 | #define I2CSTAT_GCSTAT (1<<9) | ||
47 | #define I2CSTAT_ADD10 (1<<8) | ||
48 | #define I2CSTAT_IWCOL (1<<7) | ||
49 | #define I2CSTAT_I2COV (1<<6) | ||
50 | #define I2CSTAT_DA (1<<5) | ||
51 | #define I2CSTAT_P (1<<4) | ||
52 | #define I2CSTAT_S (1<<3) | ||
53 | #define I2CSTAT_RW (1<<2) | ||
54 | #define I2CSTAT_RBF (1<<1) | ||
55 | #define I2CSTAT_TBF (1<<0) | ||
56 | |||
57 | #define PIC32_I2CxADD 0x0020 | ||
58 | #define PIC32_I2CxADDCLR 0x0024 | ||
59 | #define PIC32_I2CxADDSET 0x0028 | ||
60 | #define PIC32_I2CxADDINV 0x002C | ||
61 | #define PIC32_I2CxMSK 0x0030 | ||
62 | #define PIC32_I2CxMSKCLR 0x0034 | ||
63 | #define PIC32_I2CxMSKSET 0x0038 | ||
64 | #define PIC32_I2CxMSKINV 0x003C | ||
65 | #define PIC32_I2CxBRG 0x0040 | ||
66 | #define PIC32_I2CxBRGCLR 0x0044 | ||
67 | #define PIC32_I2CxBRGSET 0x0048 | ||
68 | #define PIC32_I2CxBRGINV 0x004C | ||
69 | #define PIC32_I2CxTRN 0x0050 | ||
70 | #define PIC32_I2CxTRNCLR 0x0054 | ||
71 | #define PIC32_I2CxTRNSET 0x0058 | ||
72 | #define PIC32_I2CxTRNINV 0x005C | ||
73 | #define PIC32_I2CxRCV 0x0060 | ||
74 | |||
75 | struct i2c_platform_data { | ||
76 | u32 base; | ||
77 | struct i2c_adapter adap; | ||
78 | u32 xfer_timeout; | ||
79 | u32 ack_timeout; | ||
80 | u32 ctl_timeout; | ||
81 | }; | ||
82 | |||
83 | extern u32 pic32_bus_readl(u32 reg); | ||
84 | extern void pic32_bus_writel(u32 val, u32 reg); | ||
85 | |||
86 | static inline void | ||
87 | StartI2C(struct i2c_platform_data *adap) | ||
88 | { | ||
89 | pr_debug("StartI2C\n"); | ||
90 | pic32_bus_writel(I2CCON_SEN, adap->base + PIC32_I2CxCONSET); | ||
91 | } | ||
92 | |||
93 | static inline void | ||
94 | StopI2C(struct i2c_platform_data *adap) | ||
95 | { | ||
96 | pr_debug("StopI2C\n"); | ||
97 | pic32_bus_writel(I2CCON_PEN, adap->base + PIC32_I2CxCONSET); | ||
98 | } | ||
99 | |||
100 | static inline void | ||
101 | AckI2C(struct i2c_platform_data *adap) | ||
102 | { | ||
103 | pr_debug("AckI2C\n"); | ||
104 | pic32_bus_writel(I2CCON_ACKDT, adap->base + PIC32_I2CxCONCLR); | ||
105 | pic32_bus_writel(I2CCON_ACKEN, adap->base + PIC32_I2CxCONSET); | ||
106 | } | ||
107 | |||
108 | static inline void | ||
109 | NotAckI2C(struct i2c_platform_data *adap) | ||
110 | { | ||
111 | pr_debug("NakI2C\n"); | ||
112 | pic32_bus_writel(I2CCON_ACKDT, adap->base + PIC32_I2CxCONSET); | ||
113 | pic32_bus_writel(I2CCON_ACKEN, adap->base + PIC32_I2CxCONSET); | ||
114 | } | ||
115 | |||
116 | static inline int | ||
117 | IdleI2C(struct i2c_platform_data *adap) | ||
118 | { | ||
119 | int i; | ||
120 | |||
121 | pr_debug("IdleI2C\n"); | ||
122 | for (i = 0; i < adap->ctl_timeout; i++) { | ||
123 | if (((pic32_bus_readl(adap->base + PIC32_I2CxCON) & | ||
124 | (I2CCON_ACKEN | I2CCON_RCEN | I2CCON_PEN | I2CCON_RSEN | | ||
125 | I2CCON_SEN)) == 0) && | ||
126 | ((pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & | ||
127 | (I2CSTAT_TRSTAT)) == 0)) | ||
128 | return 0; | ||
129 | udelay(1); | ||
130 | } | ||
131 | return -ETIMEDOUT; | ||
132 | } | ||
133 | |||
134 | static inline u32 | ||
135 | MasterWriteI2C(struct i2c_platform_data *adap, u32 byte) | ||
136 | { | ||
137 | pr_debug("MasterWriteI2C\n"); | ||
138 | |||
139 | pic32_bus_writel(byte, adap->base + PIC32_I2CxTRN); | ||
140 | |||
141 | return pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & I2CSTAT_IWCOL; | ||
142 | } | ||
143 | |||
144 | static inline u32 | ||
145 | MasterReadI2C(struct i2c_platform_data *adap) | ||
146 | { | ||
147 | pr_debug("MasterReadI2C\n"); | ||
148 | |||
149 | pic32_bus_writel(I2CCON_RCEN, adap->base + PIC32_I2CxCONSET); | ||
150 | |||
151 | while (pic32_bus_readl(adap->base + PIC32_I2CxCON) & I2CCON_RCEN) | ||
152 | ; | ||
153 | |||
154 | pic32_bus_writel(I2CSTAT_I2COV, adap->base + PIC32_I2CxSTATCLR); | ||
155 | |||
156 | return pic32_bus_readl(adap->base + PIC32_I2CxRCV); | ||
157 | } | ||
158 | |||
159 | static int | ||
160 | do_address(struct i2c_platform_data *adap, unsigned int addr, int rd) | ||
161 | { | ||
162 | pr_debug("doaddress\n"); | ||
163 | |||
164 | IdleI2C(adap); | ||
165 | StartI2C(adap); | ||
166 | IdleI2C(adap); | ||
167 | |||
168 | addr <<= 1; | ||
169 | if (rd) | ||
170 | addr |= 1; | ||
171 | |||
172 | if (MasterWriteI2C(adap, addr)) | ||
173 | return -EIO; | ||
174 | IdleI2C(adap); | ||
175 | if (pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & I2CSTAT_ACKSTAT) | ||
176 | return -EIO; | ||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | static int | ||
181 | i2c_read(struct i2c_platform_data *adap, unsigned char *buf, | ||
182 | unsigned int len) | ||
183 | { | ||
184 | int i; | ||
185 | u32 data; | ||
186 | |||
187 | pr_debug("i2c_read\n"); | ||
188 | |||
189 | i = 0; | ||
190 | while (i < len) { | ||
191 | data = MasterReadI2C(adap); | ||
192 | buf[i++] = data; | ||
193 | if (i < len) | ||
194 | AckI2C(adap); | ||
195 | else | ||
196 | NotAckI2C(adap); | ||
197 | } | ||
198 | |||
199 | StopI2C(adap); | ||
200 | IdleI2C(adap); | ||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static int | ||
205 | i2c_write(struct i2c_platform_data *adap, unsigned char *buf, | ||
206 | unsigned int len) | ||
207 | { | ||
208 | int i; | ||
209 | u32 data; | ||
210 | |||
211 | pr_debug("i2c_write\n"); | ||
212 | |||
213 | i = 0; | ||
214 | while (i < len) { | ||
215 | data = buf[i]; | ||
216 | if (MasterWriteI2C(adap, data)) | ||
217 | return -EIO; | ||
218 | IdleI2C(adap); | ||
219 | if (pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & | ||
220 | I2CSTAT_ACKSTAT) | ||
221 | return -EIO; | ||
222 | i++; | ||
223 | } | ||
224 | |||
225 | StopI2C(adap); | ||
226 | IdleI2C(adap); | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | static int | ||
231 | platform_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) | ||
232 | { | ||
233 | struct i2c_platform_data *adap = i2c_adap->algo_data; | ||
234 | struct i2c_msg *p; | ||
235 | int i, err = 0; | ||
236 | |||
237 | pr_debug("platform_xfer\n"); | ||
238 | for (i = 0; i < num; i++) { | ||
239 | #define __BUFSIZE 80 | ||
240 | int ii; | ||
241 | static char buf[__BUFSIZE]; | ||
242 | char *b = buf; | ||
243 | |||
244 | p = &msgs[i]; | ||
245 | b += sprintf(buf, " [%d bytes]", p->len); | ||
246 | if ((p->flags & I2C_M_RD) == 0) { | ||
247 | for (ii = 0; ii < p->len; ii++) { | ||
248 | if (b < &buf[__BUFSIZE-4]) { | ||
249 | b += sprintf(b, " %02x", p->buf[ii]); | ||
250 | } else { | ||
251 | strcat(b, "..."); | ||
252 | break; | ||
253 | } | ||
254 | } | ||
255 | } | ||
256 | pr_debug("xfer%d: DevAddr: %04x Op:%s Data:%s\n", i, p->addr, | ||
257 | (p->flags & I2C_M_RD) ? "Rd" : "Wr", buf); | ||
258 | } | ||
259 | |||
260 | |||
261 | for (i = 0; !err && i < num; i++) { | ||
262 | p = &msgs[i]; | ||
263 | err = do_address(adap, p->addr, p->flags & I2C_M_RD); | ||
264 | if (err || !p->len) | ||
265 | continue; | ||
266 | if (p->flags & I2C_M_RD) | ||
267 | err = i2c_read(adap, p->buf, p->len); | ||
268 | else | ||
269 | err = i2c_write(adap, p->buf, p->len); | ||
270 | } | ||
271 | |||
272 | /* Return the number of messages processed, or the error code. */ | ||
273 | if (err == 0) | ||
274 | err = num; | ||
275 | |||
276 | return err; | ||
277 | } | ||
278 | |||
279 | static u32 | ||
280 | platform_func(struct i2c_adapter *adap) | ||
281 | { | ||
282 | pr_debug("platform_algo\n"); | ||
283 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; | ||
284 | } | ||
285 | |||
286 | static const struct i2c_algorithm platform_algo = { | ||
287 | .master_xfer = platform_xfer, | ||
288 | .functionality = platform_func, | ||
289 | }; | ||
290 | |||
291 | static void i2c_platform_setup(struct i2c_platform_data *priv) | ||
292 | { | ||
293 | pr_debug("i2c_platform_setup\n"); | ||
294 | |||
295 | pic32_bus_writel(500, priv->base + PIC32_I2CxBRG); | ||
296 | pic32_bus_writel(I2CCON_ON, priv->base + PIC32_I2CxCONCLR); | ||
297 | pic32_bus_writel(I2CCON_ON, priv->base + PIC32_I2CxCONSET); | ||
298 | pic32_bus_writel((I2CSTAT_BCL | I2CSTAT_IWCOL), | ||
299 | (priv->base + PIC32_I2CxSTATCLR)); | ||
300 | } | ||
301 | |||
302 | static void i2c_platform_disable(struct i2c_platform_data *priv) | ||
303 | { | ||
304 | pr_debug("i2c_platform_disable\n"); | ||
305 | } | ||
306 | |||
307 | static int __devinit | ||
308 | i2c_platform_probe(struct platform_device *pdev) | ||
309 | { | ||
310 | struct i2c_platform_data *priv; | ||
311 | struct resource *r; | ||
312 | int ret; | ||
313 | |||
314 | pr_debug("i2c_platform_probe\n"); | ||
315 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
316 | if (!r) { | ||
317 | ret = -ENODEV; | ||
318 | goto out; | ||
319 | } | ||
320 | |||
321 | priv = kzalloc(sizeof(struct i2c_platform_data), GFP_KERNEL); | ||
322 | if (!priv) { | ||
323 | ret = -ENOMEM; | ||
324 | goto out; | ||
325 | } | ||
326 | |||
327 | /* FIXME: need to allocate resource in PIC32 space */ | ||
328 | #if 0 | ||
329 | priv->base = bus_request_region(r->start, resource_size(r), | ||
330 | pdev->name); | ||
331 | #else | ||
332 | priv->base = r->start; | ||
333 | #endif | ||
334 | if (!priv->base) { | ||
335 | ret = -EBUSY; | ||
336 | goto out_mem; | ||
337 | } | ||
338 | |||
339 | priv->xfer_timeout = 200; | ||
340 | priv->ack_timeout = 200; | ||
341 | priv->ctl_timeout = 200; | ||
342 | |||
343 | priv->adap.nr = pdev->id; | ||
344 | priv->adap.algo = &platform_algo; | ||
345 | priv->adap.algo_data = priv; | ||
346 | priv->adap.dev.parent = &pdev->dev; | ||
347 | strlcpy(priv->adap.name, "PIC32 I2C", sizeof(priv->adap.name)); | ||
348 | |||
349 | i2c_platform_setup(priv); | ||
350 | |||
351 | ret = i2c_add_numbered_adapter(&priv->adap); | ||
352 | if (ret == 0) { | ||
353 | platform_set_drvdata(pdev, priv); | ||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | i2c_platform_disable(priv); | ||
358 | |||
359 | out_mem: | ||
360 | kfree(priv); | ||
361 | out: | ||
362 | return ret; | ||
363 | } | ||
364 | |||
365 | static int __devexit | ||
366 | i2c_platform_remove(struct platform_device *pdev) | ||
367 | { | ||
368 | struct i2c_platform_data *priv = platform_get_drvdata(pdev); | ||
369 | |||
370 | pr_debug("i2c_platform_remove\n"); | ||
371 | platform_set_drvdata(pdev, NULL); | ||
372 | i2c_del_adapter(&priv->adap); | ||
373 | i2c_platform_disable(priv); | ||
374 | kfree(priv); | ||
375 | return 0; | ||
376 | } | ||
377 | |||
378 | #ifdef CONFIG_PM | ||
379 | static int | ||
380 | i2c_platform_suspend(struct platform_device *pdev, pm_message_t state) | ||
381 | { | ||
382 | struct i2c_platform_data *priv = platform_get_drvdata(pdev); | ||
383 | |||
384 | dev_dbg(&pdev->dev, "i2c_platform_disable\n"); | ||
385 | i2c_platform_disable(priv); | ||
386 | |||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | static int | ||
391 | i2c_platform_resume(struct platform_device *pdev) | ||
392 | { | ||
393 | struct i2c_platform_data *priv = platform_get_drvdata(pdev); | ||
394 | |||
395 | dev_dbg(&pdev->dev, "i2c_platform_setup\n"); | ||
396 | i2c_platform_setup(priv); | ||
397 | |||
398 | return 0; | ||
399 | } | ||
400 | #else | ||
401 | #define i2c_platform_suspend NULL | ||
402 | #define i2c_platform_resume NULL | ||
403 | #endif | ||
404 | |||
405 | static struct platform_driver i2c_platform_driver = { | ||
406 | .driver = { | ||
407 | .name = "i2c_pic32", | ||
408 | .owner = THIS_MODULE, | ||
409 | }, | ||
410 | .probe = i2c_platform_probe, | ||
411 | .remove = __devexit_p(i2c_platform_remove), | ||
412 | .suspend = i2c_platform_suspend, | ||
413 | .resume = i2c_platform_resume, | ||
414 | }; | ||
415 | |||
416 | static int __init | ||
417 | i2c_platform_init(void) | ||
418 | { | ||
419 | pr_debug("i2c_platform_init\n"); | ||
420 | return platform_driver_register(&i2c_platform_driver); | ||
421 | } | ||
422 | |||
423 | static void __exit | ||
424 | i2c_platform_exit(void) | ||
425 | { | ||
426 | pr_debug("i2c_platform_exit\n"); | ||
427 | platform_driver_unregister(&i2c_platform_driver); | ||
428 | } | ||
429 | |||
430 | MODULE_AUTHOR("Chris Dearman, MIPS Technologies INC."); | ||
431 | MODULE_DESCRIPTION("PIC32 I2C driver"); | ||
432 | MODULE_LICENSE("GPL"); | ||
433 | |||
434 | module_init(i2c_platform_init); | ||
435 | module_exit(i2c_platform_exit); | ||