• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
StartI2C(struct i2c_platform_data * adap)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
StopI2C(struct i2c_platform_data * adap)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
AckI2C(struct i2c_platform_data * adap)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
NotAckI2C(struct i2c_platform_data * adap)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
IdleI2C(struct i2c_platform_data * adap)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
MasterWriteI2C(struct i2c_platform_data * adap,u32 byte)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
MasterReadI2C(struct i2c_platform_data * adap)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
do_address(struct i2c_platform_data * adap,unsigned int addr,int rd)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
i2c_read(struct i2c_platform_data * adap,unsigned char * buf,unsigned int len)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
i2c_write(struct i2c_platform_data * adap,unsigned char * buf,unsigned int len)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
platform_xfer(struct i2c_adapter * i2c_adap,struct i2c_msg * msgs,int num)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
platform_func(struct i2c_adapter * adap)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 
i2c_platform_setup(struct i2c_platform_data * priv)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 
i2c_platform_disable(struct i2c_platform_data * priv)302 static void i2c_platform_disable(struct i2c_platform_data *priv)
303 {
304 	pr_debug("i2c_platform_disable\n");
305 }
306 
i2c_platform_probe(struct platform_device * pdev)307 static int i2c_platform_probe(struct platform_device *pdev)
308 {
309 	struct i2c_platform_data *priv;
310 	struct resource *r;
311 	int ret;
312 
313 	pr_debug("i2c_platform_probe\n");
314 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
315 	if (!r) {
316 		ret = -ENODEV;
317 		goto out;
318 	}
319 
320 	priv = kzalloc(sizeof(struct i2c_platform_data), GFP_KERNEL);
321 	if (!priv) {
322 		ret = -ENOMEM;
323 		goto out;
324 	}
325 
326 	/* FIXME: need to allocate resource in PIC32 space */
327 #if 0
328 	priv->base = bus_request_region(r->start, resource_size(r),
329 					  pdev->name);
330 #else
331 	priv->base = r->start;
332 #endif
333 	if (!priv->base) {
334 		ret = -EBUSY;
335 		goto out_mem;
336 	}
337 
338 	priv->xfer_timeout = 200;
339 	priv->ack_timeout = 200;
340 	priv->ctl_timeout = 200;
341 
342 	priv->adap.nr = pdev->id;
343 	priv->adap.algo = &platform_algo;
344 	priv->adap.algo_data = priv;
345 	priv->adap.dev.parent = &pdev->dev;
346 	strlcpy(priv->adap.name, "PIC32 I2C", sizeof(priv->adap.name));
347 
348 	i2c_platform_setup(priv);
349 
350 	ret = i2c_add_numbered_adapter(&priv->adap);
351 	if (ret == 0) {
352 		platform_set_drvdata(pdev, priv);
353 		return 0;
354 	}
355 
356 	i2c_platform_disable(priv);
357 
358 out_mem:
359 	kfree(priv);
360 out:
361 	return ret;
362 }
363 
i2c_platform_remove(struct platform_device * pdev)364 static int i2c_platform_remove(struct platform_device *pdev)
365 {
366 	struct i2c_platform_data *priv = platform_get_drvdata(pdev);
367 
368 	pr_debug("i2c_platform_remove\n");
369 	platform_set_drvdata(pdev, NULL);
370 	i2c_del_adapter(&priv->adap);
371 	i2c_platform_disable(priv);
372 	kfree(priv);
373 	return 0;
374 }
375 
376 #ifdef CONFIG_PM
377 static int
i2c_platform_suspend(struct platform_device * pdev,pm_message_t state)378 i2c_platform_suspend(struct platform_device *pdev, pm_message_t state)
379 {
380 	struct i2c_platform_data *priv = platform_get_drvdata(pdev);
381 
382 	dev_dbg(&pdev->dev, "i2c_platform_disable\n");
383 	i2c_platform_disable(priv);
384 
385 	return 0;
386 }
387 
388 static int
i2c_platform_resume(struct platform_device * pdev)389 i2c_platform_resume(struct platform_device *pdev)
390 {
391 	struct i2c_platform_data *priv = platform_get_drvdata(pdev);
392 
393 	dev_dbg(&pdev->dev, "i2c_platform_setup\n");
394 	i2c_platform_setup(priv);
395 
396 	return 0;
397 }
398 #else
399 #define i2c_platform_suspend	NULL
400 #define i2c_platform_resume	NULL
401 #endif
402 
403 static struct platform_driver i2c_platform_driver = {
404 	.driver = {
405 		.name	= "i2c_pic32",
406 		.owner	= THIS_MODULE,
407 	},
408 	.probe		= i2c_platform_probe,
409 	.remove		= i2c_platform_remove,
410 	.suspend	= i2c_platform_suspend,
411 	.resume		= i2c_platform_resume,
412 };
413 
414 static int __init
i2c_platform_init(void)415 i2c_platform_init(void)
416 {
417 	pr_debug("i2c_platform_init\n");
418 	return platform_driver_register(&i2c_platform_driver);
419 }
420 
421 static void __exit
i2c_platform_exit(void)422 i2c_platform_exit(void)
423 {
424 	pr_debug("i2c_platform_exit\n");
425 	platform_driver_unregister(&i2c_platform_driver);
426 }
427 
428 MODULE_AUTHOR("Chris Dearman, MIPS Technologies INC.");
429 MODULE_DESCRIPTION("PIC32 I2C driver");
430 MODULE_LICENSE("GPL");
431 
432 module_init(i2c_platform_init);
433 module_exit(i2c_platform_exit);
434