diff options
Diffstat (limited to 'arch/mips/mti-sead3/sead3-i2c-drv.c')
-rw-r--r-- | arch/mips/mti-sead3/sead3-i2c-drv.c | 404 |
1 files changed, 0 insertions, 404 deletions
diff --git a/arch/mips/mti-sead3/sead3-i2c-drv.c b/arch/mips/mti-sead3/sead3-i2c-drv.c deleted file mode 100644 index 2bebf0974e39..000000000000 --- a/arch/mips/mti-sead3/sead3-i2c-drv.c +++ /dev/null | |||
@@ -1,404 +0,0 @@ | |||
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/init.h> | ||
9 | #include <linux/module.h> | ||
10 | #include <linux/slab.h> | ||
11 | #include <linux/delay.h> | ||
12 | #include <linux/i2c.h> | ||
13 | #include <linux/platform_device.h> | ||
14 | |||
15 | #define PIC32_I2CxCON 0x0000 | ||
16 | #define PIC32_I2CCON_ON (1<<15) | ||
17 | #define PIC32_I2CCON_ACKDT (1<<5) | ||
18 | #define PIC32_I2CCON_ACKEN (1<<4) | ||
19 | #define PIC32_I2CCON_RCEN (1<<3) | ||
20 | #define PIC32_I2CCON_PEN (1<<2) | ||
21 | #define PIC32_I2CCON_RSEN (1<<1) | ||
22 | #define PIC32_I2CCON_SEN (1<<0) | ||
23 | #define PIC32_I2CxCONCLR 0x0004 | ||
24 | #define PIC32_I2CxCONSET 0x0008 | ||
25 | #define PIC32_I2CxSTAT 0x0010 | ||
26 | #define PIC32_I2CxSTATCLR 0x0014 | ||
27 | #define PIC32_I2CSTAT_ACKSTAT (1<<15) | ||
28 | #define PIC32_I2CSTAT_TRSTAT (1<<14) | ||
29 | #define PIC32_I2CSTAT_BCL (1<<10) | ||
30 | #define PIC32_I2CSTAT_IWCOL (1<<7) | ||
31 | #define PIC32_I2CSTAT_I2COV (1<<6) | ||
32 | #define PIC32_I2CxBRG 0x0040 | ||
33 | #define PIC32_I2CxTRN 0x0050 | ||
34 | #define PIC32_I2CxRCV 0x0060 | ||
35 | |||
36 | static DEFINE_SPINLOCK(pic32_bus_lock); | ||
37 | |||
38 | static void __iomem *bus_xfer = (void __iomem *)0xbf000600; | ||
39 | static void __iomem *bus_status = (void __iomem *)0xbf000060; | ||
40 | |||
41 | #define DELAY() udelay(100) | ||
42 | |||
43 | static inline unsigned int ioready(void) | ||
44 | { | ||
45 | return readl(bus_status) & 1; | ||
46 | } | ||
47 | |||
48 | static inline void wait_ioready(void) | ||
49 | { | ||
50 | do { } while (!ioready()); | ||
51 | } | ||
52 | |||
53 | static inline void wait_ioclear(void) | ||
54 | { | ||
55 | do { } while (ioready()); | ||
56 | } | ||
57 | |||
58 | static inline void check_ioclear(void) | ||
59 | { | ||
60 | if (ioready()) { | ||
61 | do { | ||
62 | (void) readl(bus_xfer); | ||
63 | DELAY(); | ||
64 | } while (ioready()); | ||
65 | } | ||
66 | } | ||
67 | |||
68 | static u32 pic32_bus_readl(u32 reg) | ||
69 | { | ||
70 | unsigned long flags; | ||
71 | u32 status, val; | ||
72 | |||
73 | spin_lock_irqsave(&pic32_bus_lock, flags); | ||
74 | |||
75 | check_ioclear(); | ||
76 | writel((0x01 << 24) | (reg & 0x00ffffff), bus_xfer); | ||
77 | DELAY(); | ||
78 | wait_ioready(); | ||
79 | status = readl(bus_xfer); | ||
80 | DELAY(); | ||
81 | val = readl(bus_xfer); | ||
82 | wait_ioclear(); | ||
83 | |||
84 | spin_unlock_irqrestore(&pic32_bus_lock, flags); | ||
85 | |||
86 | return val; | ||
87 | } | ||
88 | |||
89 | static void pic32_bus_writel(u32 val, u32 reg) | ||
90 | { | ||
91 | unsigned long flags; | ||
92 | u32 status; | ||
93 | |||
94 | spin_lock_irqsave(&pic32_bus_lock, flags); | ||
95 | |||
96 | check_ioclear(); | ||
97 | writel((0x10 << 24) | (reg & 0x00ffffff), bus_xfer); | ||
98 | DELAY(); | ||
99 | writel(val, bus_xfer); | ||
100 | DELAY(); | ||
101 | wait_ioready(); | ||
102 | status = readl(bus_xfer); | ||
103 | wait_ioclear(); | ||
104 | |||
105 | spin_unlock_irqrestore(&pic32_bus_lock, flags); | ||
106 | } | ||
107 | |||
108 | struct pic32_i2c_platform_data { | ||
109 | u32 base; | ||
110 | struct i2c_adapter adap; | ||
111 | u32 xfer_timeout; | ||
112 | u32 ack_timeout; | ||
113 | u32 ctl_timeout; | ||
114 | }; | ||
115 | |||
116 | static inline void pic32_i2c_start(struct pic32_i2c_platform_data *adap) | ||
117 | { | ||
118 | pic32_bus_writel(PIC32_I2CCON_SEN, adap->base + PIC32_I2CxCONSET); | ||
119 | } | ||
120 | |||
121 | static inline void pic32_i2c_stop(struct pic32_i2c_platform_data *adap) | ||
122 | { | ||
123 | pic32_bus_writel(PIC32_I2CCON_PEN, adap->base + PIC32_I2CxCONSET); | ||
124 | } | ||
125 | |||
126 | static inline void pic32_i2c_ack(struct pic32_i2c_platform_data *adap) | ||
127 | { | ||
128 | pic32_bus_writel(PIC32_I2CCON_ACKDT, adap->base + PIC32_I2CxCONCLR); | ||
129 | pic32_bus_writel(PIC32_I2CCON_ACKEN, adap->base + PIC32_I2CxCONSET); | ||
130 | } | ||
131 | |||
132 | static inline void pic32_i2c_nack(struct pic32_i2c_platform_data *adap) | ||
133 | { | ||
134 | pic32_bus_writel(PIC32_I2CCON_ACKDT, adap->base + PIC32_I2CxCONSET); | ||
135 | pic32_bus_writel(PIC32_I2CCON_ACKEN, adap->base + PIC32_I2CxCONSET); | ||
136 | } | ||
137 | |||
138 | static inline int pic32_i2c_idle(struct pic32_i2c_platform_data *adap) | ||
139 | { | ||
140 | int i; | ||
141 | |||
142 | for (i = 0; i < adap->ctl_timeout; i++) { | ||
143 | if (((pic32_bus_readl(adap->base + PIC32_I2CxCON) & | ||
144 | (PIC32_I2CCON_ACKEN | PIC32_I2CCON_RCEN | | ||
145 | PIC32_I2CCON_PEN | PIC32_I2CCON_RSEN | | ||
146 | PIC32_I2CCON_SEN)) == 0) && | ||
147 | ((pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & | ||
148 | (PIC32_I2CSTAT_TRSTAT)) == 0)) | ||
149 | return 0; | ||
150 | udelay(1); | ||
151 | } | ||
152 | return -ETIMEDOUT; | ||
153 | } | ||
154 | |||
155 | static inline u32 pic32_i2c_master_write(struct pic32_i2c_platform_data *adap, | ||
156 | u32 byte) | ||
157 | { | ||
158 | pic32_bus_writel(byte, adap->base + PIC32_I2CxTRN); | ||
159 | return pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & | ||
160 | PIC32_I2CSTAT_IWCOL; | ||
161 | } | ||
162 | |||
163 | static inline u32 pic32_i2c_master_read(struct pic32_i2c_platform_data *adap) | ||
164 | { | ||
165 | pic32_bus_writel(PIC32_I2CCON_RCEN, adap->base + PIC32_I2CxCONSET); | ||
166 | while (pic32_bus_readl(adap->base + PIC32_I2CxCON) & PIC32_I2CCON_RCEN) | ||
167 | ; | ||
168 | pic32_bus_writel(PIC32_I2CSTAT_I2COV, adap->base + PIC32_I2CxSTATCLR); | ||
169 | return pic32_bus_readl(adap->base + PIC32_I2CxRCV); | ||
170 | } | ||
171 | |||
172 | static int pic32_i2c_address(struct pic32_i2c_platform_data *adap, | ||
173 | unsigned int addr, int rd) | ||
174 | { | ||
175 | pic32_i2c_idle(adap); | ||
176 | pic32_i2c_start(adap); | ||
177 | pic32_i2c_idle(adap); | ||
178 | |||
179 | addr <<= 1; | ||
180 | if (rd) | ||
181 | addr |= 1; | ||
182 | |||
183 | if (pic32_i2c_master_write(adap, addr)) | ||
184 | return -EIO; | ||
185 | pic32_i2c_idle(adap); | ||
186 | if (pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & | ||
187 | PIC32_I2CSTAT_ACKSTAT) | ||
188 | return -EIO; | ||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | static int sead3_i2c_read(struct pic32_i2c_platform_data *adap, | ||
193 | unsigned char *buf, unsigned int len) | ||
194 | { | ||
195 | u32 data; | ||
196 | int i; | ||
197 | |||
198 | i = 0; | ||
199 | while (i < len) { | ||
200 | data = pic32_i2c_master_read(adap); | ||
201 | buf[i++] = data; | ||
202 | if (i < len) | ||
203 | pic32_i2c_ack(adap); | ||
204 | else | ||
205 | pic32_i2c_nack(adap); | ||
206 | } | ||
207 | |||
208 | pic32_i2c_stop(adap); | ||
209 | pic32_i2c_idle(adap); | ||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | static int sead3_i2c_write(struct pic32_i2c_platform_data *adap, | ||
214 | unsigned char *buf, unsigned int len) | ||
215 | { | ||
216 | int i; | ||
217 | u32 data; | ||
218 | |||
219 | i = 0; | ||
220 | while (i < len) { | ||
221 | data = buf[i]; | ||
222 | if (pic32_i2c_master_write(adap, data)) | ||
223 | return -EIO; | ||
224 | pic32_i2c_idle(adap); | ||
225 | if (pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & | ||
226 | PIC32_I2CSTAT_ACKSTAT) | ||
227 | return -EIO; | ||
228 | i++; | ||
229 | } | ||
230 | |||
231 | pic32_i2c_stop(adap); | ||
232 | pic32_i2c_idle(adap); | ||
233 | return 0; | ||
234 | } | ||
235 | |||
236 | static int sead3_pic32_platform_xfer(struct i2c_adapter *i2c_adap, | ||
237 | struct i2c_msg *msgs, int num) | ||
238 | { | ||
239 | struct pic32_i2c_platform_data *adap = i2c_adap->algo_data; | ||
240 | struct i2c_msg *p; | ||
241 | int i, err = 0; | ||
242 | |||
243 | for (i = 0; i < num; i++) { | ||
244 | #define __BUFSIZE 80 | ||
245 | int ii; | ||
246 | static char buf[__BUFSIZE]; | ||
247 | char *b = buf; | ||
248 | |||
249 | p = &msgs[i]; | ||
250 | b += sprintf(buf, " [%d bytes]", p->len); | ||
251 | if ((p->flags & I2C_M_RD) == 0) { | ||
252 | for (ii = 0; ii < p->len; ii++) { | ||
253 | if (b < &buf[__BUFSIZE-4]) { | ||
254 | b += sprintf(b, " %02x", p->buf[ii]); | ||
255 | } else { | ||
256 | strcat(b, "..."); | ||
257 | break; | ||
258 | } | ||
259 | } | ||
260 | } | ||
261 | } | ||
262 | |||
263 | for (i = 0; !err && i < num; i++) { | ||
264 | p = &msgs[i]; | ||
265 | err = pic32_i2c_address(adap, p->addr, p->flags & I2C_M_RD); | ||
266 | if (err || !p->len) | ||
267 | continue; | ||
268 | if (p->flags & I2C_M_RD) | ||
269 | err = sead3_i2c_read(adap, p->buf, p->len); | ||
270 | else | ||
271 | err = sead3_i2c_write(adap, p->buf, p->len); | ||
272 | } | ||
273 | |||
274 | /* Return the number of messages processed, or the error code. */ | ||
275 | if (err == 0) | ||
276 | err = num; | ||
277 | |||
278 | return err; | ||
279 | } | ||
280 | |||
281 | static u32 sead3_pic32_platform_func(struct i2c_adapter *adap) | ||
282 | { | ||
283 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; | ||
284 | } | ||
285 | |||
286 | static const struct i2c_algorithm sead3_platform_algo = { | ||
287 | .master_xfer = sead3_pic32_platform_xfer, | ||
288 | .functionality = sead3_pic32_platform_func, | ||
289 | }; | ||
290 | |||
291 | static void sead3_i2c_platform_setup(struct pic32_i2c_platform_data *priv) | ||
292 | { | ||
293 | pic32_bus_writel(500, priv->base + PIC32_I2CxBRG); | ||
294 | pic32_bus_writel(PIC32_I2CCON_ON, priv->base + PIC32_I2CxCONCLR); | ||
295 | pic32_bus_writel(PIC32_I2CCON_ON, priv->base + PIC32_I2CxCONSET); | ||
296 | pic32_bus_writel(PIC32_I2CSTAT_BCL | PIC32_I2CSTAT_IWCOL, | ||
297 | priv->base + PIC32_I2CxSTATCLR); | ||
298 | } | ||
299 | |||
300 | static int sead3_i2c_platform_probe(struct platform_device *pdev) | ||
301 | { | ||
302 | struct pic32_i2c_platform_data *priv; | ||
303 | struct resource *r; | ||
304 | int ret; | ||
305 | |||
306 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
307 | if (!r) { | ||
308 | ret = -ENODEV; | ||
309 | goto out; | ||
310 | } | ||
311 | |||
312 | priv = kzalloc(sizeof(struct pic32_i2c_platform_data), GFP_KERNEL); | ||
313 | if (!priv) { | ||
314 | ret = -ENOMEM; | ||
315 | goto out; | ||
316 | } | ||
317 | |||
318 | priv->base = r->start; | ||
319 | if (!priv->base) { | ||
320 | ret = -EBUSY; | ||
321 | goto out_mem; | ||
322 | } | ||
323 | |||
324 | priv->xfer_timeout = 200; | ||
325 | priv->ack_timeout = 200; | ||
326 | priv->ctl_timeout = 200; | ||
327 | |||
328 | priv->adap.nr = pdev->id; | ||
329 | priv->adap.algo = &sead3_platform_algo; | ||
330 | priv->adap.algo_data = priv; | ||
331 | priv->adap.dev.parent = &pdev->dev; | ||
332 | strlcpy(priv->adap.name, "SEAD3 PIC32", sizeof(priv->adap.name)); | ||
333 | |||
334 | sead3_i2c_platform_setup(priv); | ||
335 | |||
336 | ret = i2c_add_numbered_adapter(&priv->adap); | ||
337 | if (ret == 0) { | ||
338 | platform_set_drvdata(pdev, priv); | ||
339 | return 0; | ||
340 | } | ||
341 | |||
342 | out_mem: | ||
343 | kfree(priv); | ||
344 | out: | ||
345 | return ret; | ||
346 | } | ||
347 | |||
348 | static int sead3_i2c_platform_remove(struct platform_device *pdev) | ||
349 | { | ||
350 | struct pic32_i2c_platform_data *priv = platform_get_drvdata(pdev); | ||
351 | |||
352 | platform_set_drvdata(pdev, NULL); | ||
353 | i2c_del_adapter(&priv->adap); | ||
354 | kfree(priv); | ||
355 | return 0; | ||
356 | } | ||
357 | |||
358 | #ifdef CONFIG_PM | ||
359 | static int sead3_i2c_platform_suspend(struct platform_device *pdev, | ||
360 | pm_message_t state) | ||
361 | { | ||
362 | dev_dbg(&pdev->dev, "i2c_platform_disable\n"); | ||
363 | return 0; | ||
364 | } | ||
365 | |||
366 | static int sead3_i2c_platform_resume(struct platform_device *pdev) | ||
367 | { | ||
368 | struct pic32_i2c_platform_data *priv = platform_get_drvdata(pdev); | ||
369 | |||
370 | dev_dbg(&pdev->dev, "sead3_i2c_platform_setup\n"); | ||
371 | sead3_i2c_platform_setup(priv); | ||
372 | |||
373 | return 0; | ||
374 | } | ||
375 | #else | ||
376 | #define sead3_i2c_platform_suspend NULL | ||
377 | #define sead3_i2c_platform_resume NULL | ||
378 | #endif | ||
379 | |||
380 | static struct platform_driver sead3_i2c_platform_driver = { | ||
381 | .driver = { | ||
382 | .name = "sead3-i2c", | ||
383 | }, | ||
384 | .probe = sead3_i2c_platform_probe, | ||
385 | .remove = sead3_i2c_platform_remove, | ||
386 | .suspend = sead3_i2c_platform_suspend, | ||
387 | .resume = sead3_i2c_platform_resume, | ||
388 | }; | ||
389 | |||
390 | static int __init sead3_i2c_platform_init(void) | ||
391 | { | ||
392 | return platform_driver_register(&sead3_i2c_platform_driver); | ||
393 | } | ||
394 | module_init(sead3_i2c_platform_init); | ||
395 | |||
396 | static void __exit sead3_i2c_platform_exit(void) | ||
397 | { | ||
398 | platform_driver_unregister(&sead3_i2c_platform_driver); | ||
399 | } | ||
400 | module_exit(sead3_i2c_platform_exit); | ||
401 | |||
402 | MODULE_AUTHOR("Chris Dearman, MIPS Technologies INC."); | ||
403 | MODULE_DESCRIPTION("SEAD3 PIC32 I2C driver"); | ||
404 | MODULE_LICENSE("GPL"); | ||