| 
									
										
										
										
											2012-05-30 21:02:49 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * This file is subject to the terms and conditions of the GNU General Public | 
					
						
							|  |  |  |  * License.  See the file "COPYING" in the main directory of this archive | 
					
						
							|  |  |  |  * for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2012 MIPS Technologies, Inc.  All rights reserved. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/slab.h>
 | 
					
						
							|  |  |  | #include <linux/delay.h>
 | 
					
						
							|  |  |  | #include <linux/i2c.h>
 | 
					
						
							|  |  |  | #include <linux/platform_device.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define PIC32_I2CxCON		0x0000
 | 
					
						
							| 
									
										
										
										
											2013-01-22 12:59:30 +01:00
										 |  |  | #define	 PIC32_I2CCON_ON	(1<<15)
 | 
					
						
							|  |  |  | #define	 PIC32_I2CCON_ACKDT	(1<<5)
 | 
					
						
							|  |  |  | #define	 PIC32_I2CCON_ACKEN	(1<<4)
 | 
					
						
							|  |  |  | #define	 PIC32_I2CCON_RCEN	(1<<3)
 | 
					
						
							|  |  |  | #define	 PIC32_I2CCON_PEN	(1<<2)
 | 
					
						
							|  |  |  | #define	 PIC32_I2CCON_RSEN	(1<<1)
 | 
					
						
							|  |  |  | #define	 PIC32_I2CCON_SEN	(1<<0)
 | 
					
						
							| 
									
										
										
										
											2012-05-30 21:02:49 +00:00
										 |  |  | #define PIC32_I2CxCONCLR	0x0004
 | 
					
						
							|  |  |  | #define PIC32_I2CxCONSET	0x0008
 | 
					
						
							|  |  |  | #define PIC32_I2CxSTAT		0x0010
 | 
					
						
							|  |  |  | #define PIC32_I2CxSTATCLR	0x0014
 | 
					
						
							| 
									
										
										
										
											2013-01-22 12:59:30 +01:00
										 |  |  | #define	 PIC32_I2CSTAT_ACKSTAT	(1<<15)
 | 
					
						
							|  |  |  | #define	 PIC32_I2CSTAT_TRSTAT	(1<<14)
 | 
					
						
							|  |  |  | #define	 PIC32_I2CSTAT_BCL	(1<<10)
 | 
					
						
							|  |  |  | #define	 PIC32_I2CSTAT_IWCOL	(1<<7)
 | 
					
						
							|  |  |  | #define	 PIC32_I2CSTAT_I2COV	(1<<6)
 | 
					
						
							| 
									
										
										
										
											2012-05-30 21:02:49 +00:00
										 |  |  | #define PIC32_I2CxBRG		0x0040
 | 
					
						
							|  |  |  | #define PIC32_I2CxTRN		0x0050
 | 
					
						
							|  |  |  | #define PIC32_I2CxRCV		0x0060
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static DEFINE_SPINLOCK(pic32_bus_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-22 12:59:30 +01:00
										 |  |  | static void __iomem *bus_xfer	= (void __iomem *)0xbf000600; | 
					
						
							| 
									
										
										
										
											2012-05-30 21:02:49 +00:00
										 |  |  | static void __iomem *bus_status = (void __iomem *)0xbf000060; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-22 12:59:30 +01:00
										 |  |  | #define DELAY() udelay(100)
 | 
					
						
							| 
									
										
										
										
											2012-05-30 21:02:49 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | static inline unsigned int ioready(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return readl(bus_status) & 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void wait_ioready(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	do { } while (!ioready()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void wait_ioclear(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	do { } while (ioready()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void check_ioclear(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (ioready()) { | 
					
						
							|  |  |  | 		do { | 
					
						
							|  |  |  | 			(void) readl(bus_xfer); | 
					
						
							|  |  |  | 			DELAY(); | 
					
						
							|  |  |  | 		} while (ioready()); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static u32 pic32_bus_readl(u32 reg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 	u32 status, val; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_irqsave(&pic32_bus_lock, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	check_ioclear(); | 
					
						
							|  |  |  | 	writel((0x01 << 24) | (reg & 0x00ffffff), bus_xfer); | 
					
						
							|  |  |  | 	DELAY(); | 
					
						
							|  |  |  | 	wait_ioready(); | 
					
						
							|  |  |  | 	status = readl(bus_xfer); | 
					
						
							|  |  |  | 	DELAY(); | 
					
						
							|  |  |  | 	val = readl(bus_xfer); | 
					
						
							|  |  |  | 	wait_ioclear(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&pic32_bus_lock, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return val; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void pic32_bus_writel(u32 val, u32 reg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 	u32 status; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_irqsave(&pic32_bus_lock, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	check_ioclear(); | 
					
						
							|  |  |  | 	writel((0x10 << 24) | (reg & 0x00ffffff), bus_xfer); | 
					
						
							|  |  |  | 	DELAY(); | 
					
						
							|  |  |  | 	writel(val, bus_xfer); | 
					
						
							|  |  |  | 	DELAY(); | 
					
						
							|  |  |  | 	wait_ioready(); | 
					
						
							|  |  |  | 	status = readl(bus_xfer); | 
					
						
							|  |  |  | 	wait_ioclear(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&pic32_bus_lock, flags); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct pic32_i2c_platform_data { | 
					
						
							|  |  |  | 	u32	base; | 
					
						
							|  |  |  | 	struct i2c_adapter adap; | 
					
						
							|  |  |  | 	u32	xfer_timeout; | 
					
						
							|  |  |  | 	u32	ack_timeout; | 
					
						
							|  |  |  | 	u32	ctl_timeout; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void pic32_i2c_start(struct pic32_i2c_platform_data *adap) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	pic32_bus_writel(PIC32_I2CCON_SEN, adap->base + PIC32_I2CxCONSET); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void pic32_i2c_stop(struct pic32_i2c_platform_data *adap) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	pic32_bus_writel(PIC32_I2CCON_PEN, adap->base + PIC32_I2CxCONSET); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void pic32_i2c_ack(struct pic32_i2c_platform_data *adap) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	pic32_bus_writel(PIC32_I2CCON_ACKDT, adap->base + PIC32_I2CxCONCLR); | 
					
						
							|  |  |  | 	pic32_bus_writel(PIC32_I2CCON_ACKEN, adap->base + PIC32_I2CxCONSET); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void pic32_i2c_nack(struct pic32_i2c_platform_data *adap) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	pic32_bus_writel(PIC32_I2CCON_ACKDT, adap->base + PIC32_I2CxCONSET); | 
					
						
							|  |  |  | 	pic32_bus_writel(PIC32_I2CCON_ACKEN, adap->base + PIC32_I2CxCONSET); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline int pic32_i2c_idle(struct pic32_i2c_platform_data *adap) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < adap->ctl_timeout; i++) { | 
					
						
							|  |  |  | 		if (((pic32_bus_readl(adap->base + PIC32_I2CxCON) & | 
					
						
							|  |  |  | 		      (PIC32_I2CCON_ACKEN | PIC32_I2CCON_RCEN | | 
					
						
							|  |  |  | 		       PIC32_I2CCON_PEN | PIC32_I2CCON_RSEN | | 
					
						
							|  |  |  | 		       PIC32_I2CCON_SEN)) == 0) && | 
					
						
							|  |  |  | 		    ((pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & | 
					
						
							|  |  |  | 		      (PIC32_I2CSTAT_TRSTAT)) == 0)) | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		udelay(1); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return -ETIMEDOUT; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline u32 pic32_i2c_master_write(struct pic32_i2c_platform_data *adap, | 
					
						
							|  |  |  | 		u32 byte) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	pic32_bus_writel(byte, adap->base + PIC32_I2CxTRN); | 
					
						
							|  |  |  | 	return pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & | 
					
						
							|  |  |  | 			PIC32_I2CSTAT_IWCOL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline u32 pic32_i2c_master_read(struct pic32_i2c_platform_data *adap) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	pic32_bus_writel(PIC32_I2CCON_RCEN, adap->base + PIC32_I2CxCONSET); | 
					
						
							|  |  |  | 	while (pic32_bus_readl(adap->base + PIC32_I2CxCON) & PIC32_I2CCON_RCEN) | 
					
						
							|  |  |  | 		; | 
					
						
							|  |  |  | 	pic32_bus_writel(PIC32_I2CSTAT_I2COV, adap->base + PIC32_I2CxSTATCLR); | 
					
						
							|  |  |  | 	return pic32_bus_readl(adap->base + PIC32_I2CxRCV); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int pic32_i2c_address(struct pic32_i2c_platform_data *adap, | 
					
						
							|  |  |  | 		unsigned int addr, int rd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	pic32_i2c_idle(adap); | 
					
						
							|  |  |  | 	pic32_i2c_start(adap); | 
					
						
							|  |  |  | 	pic32_i2c_idle(adap); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	addr <<= 1; | 
					
						
							|  |  |  | 	if (rd) | 
					
						
							|  |  |  | 		addr |= 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (pic32_i2c_master_write(adap, addr)) | 
					
						
							|  |  |  | 		return -EIO; | 
					
						
							|  |  |  | 	pic32_i2c_idle(adap); | 
					
						
							|  |  |  | 	if (pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & | 
					
						
							|  |  |  | 			PIC32_I2CSTAT_ACKSTAT) | 
					
						
							|  |  |  | 		return -EIO; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int sead3_i2c_read(struct pic32_i2c_platform_data *adap, | 
					
						
							|  |  |  | 		unsigned char *buf, unsigned int len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 data; | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	i = 0; | 
					
						
							|  |  |  | 	while (i < len) { | 
					
						
							|  |  |  | 		data = pic32_i2c_master_read(adap); | 
					
						
							|  |  |  | 		buf[i++] = data; | 
					
						
							|  |  |  | 		if (i < len) | 
					
						
							|  |  |  | 			pic32_i2c_ack(adap); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			pic32_i2c_nack(adap); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pic32_i2c_stop(adap); | 
					
						
							|  |  |  | 	pic32_i2c_idle(adap); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int sead3_i2c_write(struct pic32_i2c_platform_data *adap, | 
					
						
							|  |  |  | 		unsigned char *buf, unsigned int len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 	u32 data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	i = 0; | 
					
						
							|  |  |  | 	while (i < len) { | 
					
						
							|  |  |  | 		data = buf[i]; | 
					
						
							|  |  |  | 		if (pic32_i2c_master_write(adap, data)) | 
					
						
							|  |  |  | 			return -EIO; | 
					
						
							|  |  |  | 		pic32_i2c_idle(adap); | 
					
						
							|  |  |  | 		if (pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & | 
					
						
							|  |  |  | 					PIC32_I2CSTAT_ACKSTAT) | 
					
						
							|  |  |  | 			return -EIO; | 
					
						
							|  |  |  | 		i++; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pic32_i2c_stop(adap); | 
					
						
							|  |  |  | 	pic32_i2c_idle(adap); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int sead3_pic32_platform_xfer(struct i2c_adapter *i2c_adap, | 
					
						
							|  |  |  | 		struct i2c_msg *msgs, int num) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct pic32_i2c_platform_data *adap = i2c_adap->algo_data; | 
					
						
							|  |  |  | 	struct i2c_msg *p; | 
					
						
							|  |  |  | 	int i, err = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < num; i++) { | 
					
						
							|  |  |  | #define __BUFSIZE 80
 | 
					
						
							|  |  |  | 		int ii; | 
					
						
							|  |  |  | 		static char buf[__BUFSIZE]; | 
					
						
							|  |  |  | 		char *b = buf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		p = &msgs[i]; | 
					
						
							|  |  |  | 		b += sprintf(buf, " [%d bytes]", p->len); | 
					
						
							|  |  |  | 		if ((p->flags & I2C_M_RD) == 0) { | 
					
						
							|  |  |  | 			for (ii = 0; ii < p->len; ii++) { | 
					
						
							|  |  |  | 				if (b < &buf[__BUFSIZE-4]) { | 
					
						
							|  |  |  | 					b += sprintf(b, " %02x", p->buf[ii]); | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					strcat(b, "..."); | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; !err && i < num; i++) { | 
					
						
							|  |  |  | 		p = &msgs[i]; | 
					
						
							|  |  |  | 		err = pic32_i2c_address(adap, p->addr, p->flags & I2C_M_RD); | 
					
						
							|  |  |  | 		if (err || !p->len) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		if (p->flags & I2C_M_RD) | 
					
						
							|  |  |  | 			err = sead3_i2c_read(adap, p->buf, p->len); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			err = sead3_i2c_write(adap, p->buf, p->len); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Return the number of messages processed, or the error code. */ | 
					
						
							|  |  |  | 	if (err == 0) | 
					
						
							|  |  |  | 		err = num; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static u32 sead3_pic32_platform_func(struct i2c_adapter *adap) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct i2c_algorithm sead3_platform_algo = { | 
					
						
							|  |  |  | 	.master_xfer	= sead3_pic32_platform_xfer, | 
					
						
							|  |  |  | 	.functionality	= sead3_pic32_platform_func, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void sead3_i2c_platform_setup(struct pic32_i2c_platform_data *priv) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	pic32_bus_writel(500, priv->base + PIC32_I2CxBRG); | 
					
						
							|  |  |  | 	pic32_bus_writel(PIC32_I2CCON_ON, priv->base + PIC32_I2CxCONCLR); | 
					
						
							|  |  |  | 	pic32_bus_writel(PIC32_I2CCON_ON, priv->base + PIC32_I2CxCONSET); | 
					
						
							|  |  |  | 	pic32_bus_writel(PIC32_I2CSTAT_BCL | PIC32_I2CSTAT_IWCOL, | 
					
						
							|  |  |  | 		priv->base + PIC32_I2CxSTATCLR); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-21 14:04:39 -08:00
										 |  |  | static int sead3_i2c_platform_probe(struct platform_device *pdev) | 
					
						
							| 
									
										
										
										
											2012-05-30 21:02:49 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct pic32_i2c_platform_data *priv; | 
					
						
							|  |  |  | 	struct resource *r; | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
					
						
							|  |  |  | 	if (!r) { | 
					
						
							|  |  |  | 		ret = -ENODEV; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	priv = kzalloc(sizeof(struct pic32_i2c_platform_data), GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!priv) { | 
					
						
							|  |  |  | 		ret = -ENOMEM; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	priv->base = r->start; | 
					
						
							|  |  |  | 	if (!priv->base) { | 
					
						
							|  |  |  | 		ret = -EBUSY; | 
					
						
							|  |  |  | 		goto out_mem; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	priv->xfer_timeout = 200; | 
					
						
							|  |  |  | 	priv->ack_timeout = 200; | 
					
						
							|  |  |  | 	priv->ctl_timeout = 200; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	priv->adap.nr = pdev->id; | 
					
						
							|  |  |  | 	priv->adap.algo = &sead3_platform_algo; | 
					
						
							|  |  |  | 	priv->adap.algo_data = priv; | 
					
						
							|  |  |  | 	priv->adap.dev.parent = &pdev->dev; | 
					
						
							|  |  |  | 	strlcpy(priv->adap.name, "SEAD3 PIC32", sizeof(priv->adap.name)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sead3_i2c_platform_setup(priv); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = i2c_add_numbered_adapter(&priv->adap); | 
					
						
							|  |  |  | 	if (ret == 0) { | 
					
						
							|  |  |  | 		platform_set_drvdata(pdev, priv); | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | out_mem: | 
					
						
							|  |  |  | 	kfree(priv); | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-21 14:04:39 -08:00
										 |  |  | static int sead3_i2c_platform_remove(struct platform_device *pdev) | 
					
						
							| 
									
										
										
										
											2012-05-30 21:02:49 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct pic32_i2c_platform_data *priv = platform_get_drvdata(pdev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	platform_set_drvdata(pdev, NULL); | 
					
						
							|  |  |  | 	i2c_del_adapter(&priv->adap); | 
					
						
							|  |  |  | 	kfree(priv); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_PM
 | 
					
						
							|  |  |  | static int sead3_i2c_platform_suspend(struct platform_device *pdev, | 
					
						
							|  |  |  | 		pm_message_t state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	dev_dbg(&pdev->dev, "i2c_platform_disable\n"); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int sead3_i2c_platform_resume(struct platform_device *pdev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct pic32_i2c_platform_data *priv = platform_get_drvdata(pdev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dev_dbg(&pdev->dev, "sead3_i2c_platform_setup\n"); | 
					
						
							|  |  |  | 	sead3_i2c_platform_setup(priv); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | #define sead3_i2c_platform_suspend	NULL
 | 
					
						
							|  |  |  | #define sead3_i2c_platform_resume	NULL
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct platform_driver sead3_i2c_platform_driver = { | 
					
						
							|  |  |  | 	.driver = { | 
					
						
							|  |  |  | 		.name	= "sead3-i2c", | 
					
						
							|  |  |  | 		.owner	= THIS_MODULE, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	.probe		= sead3_i2c_platform_probe, | 
					
						
							| 
									
										
										
										
											2012-12-21 14:04:39 -08:00
										 |  |  | 	.remove		= sead3_i2c_platform_remove, | 
					
						
							| 
									
										
										
										
											2012-05-30 21:02:49 +00:00
										 |  |  | 	.suspend	= sead3_i2c_platform_suspend, | 
					
						
							|  |  |  | 	.resume		= sead3_i2c_platform_resume, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init sead3_i2c_platform_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return platform_driver_register(&sead3_i2c_platform_driver); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | module_init(sead3_i2c_platform_init); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __exit sead3_i2c_platform_exit(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	platform_driver_unregister(&sead3_i2c_platform_driver); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | module_exit(sead3_i2c_platform_exit); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_AUTHOR("Chris Dearman, MIPS Technologies INC."); | 
					
						
							|  |  |  | MODULE_DESCRIPTION("SEAD3 PIC32 I2C driver"); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); |