Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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
 75struct 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
 83extern u32 pic32_bus_readl(u32 reg);
 84extern void pic32_bus_writel(u32 val, u32 reg);
 85
 86static inline void
 87StartI2C(struct i2c_platform_data *adap)
 88{
 89	pr_debug("StartI2C\n");
 90	pic32_bus_writel(I2CCON_SEN, adap->base + PIC32_I2CxCONSET);
 91}
 92
 93static inline void
 94StopI2C(struct i2c_platform_data *adap)
 95{
 96	pr_debug("StopI2C\n");
 97	pic32_bus_writel(I2CCON_PEN, adap->base + PIC32_I2CxCONSET);
 98}
 99
100static inline void
101AckI2C(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
108static inline void
109NotAckI2C(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
116static inline int
117IdleI2C(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
134static inline u32
135MasterWriteI2C(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
144static inline u32
145MasterReadI2C(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
159static int
160do_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
180static int
181i2c_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
204static int
205i2c_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
230static int
231platform_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
279static u32
280platform_func(struct i2c_adapter *adap)
281{
282	pr_debug("platform_algo\n");
283	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
284}
285
286static const struct i2c_algorithm platform_algo = {
287	.master_xfer	= platform_xfer,
288	.functionality	= platform_func,
289};
290
291static 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
302static void i2c_platform_disable(struct i2c_platform_data *priv)
303{
304	pr_debug("i2c_platform_disable\n");
305}
306
307static 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
358out_mem:
359	kfree(priv);
360out:
361	return ret;
362}
363
364static 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
377static int
378i2c_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
388static int
389i2c_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
403static 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
414static int __init
415i2c_platform_init(void)
416{
417	pr_debug("i2c_platform_init\n");
418	return platform_driver_register(&i2c_platform_driver);
419}
420
421static void __exit
422i2c_platform_exit(void)
423{
424	pr_debug("i2c_platform_exit\n");
425	platform_driver_unregister(&i2c_platform_driver);
426}
427
428MODULE_AUTHOR("Chris Dearman, MIPS Technologies INC.");
429MODULE_DESCRIPTION("PIC32 I2C driver");
430MODULE_LICENSE("GPL");
431
432module_init(i2c_platform_init);
433module_exit(i2c_platform_exit);