1d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao/*
2d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao * I2C driver for PKUnity-v3 SoC
3d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao * Code specific to PKUnity SoC and UniCore ISA
4d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao *
5d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao *	Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
6d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao *	Copyright (C) 2001-2010 Guan Xuetao
7d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao *
8d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao * This program is free software; you can redistribute it and/or modify
9d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao * it under the terms of the GNU General Public License version 2 as
10d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao * published by the Free Software Foundation.
11d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao */
12d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
13d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao#include <linux/module.h>
14d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao#include <linux/kernel.h>
15d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao#include <linux/err.h>
16d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao#include <linux/slab.h>
17d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao#include <linux/types.h>
18d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao#include <linux/delay.h>
19d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao#include <linux/i2c.h>
20d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao#include <linux/init.h>
21d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao#include <linux/clk.h>
22d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao#include <linux/platform_device.h>
23d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao#include <linux/io.h>
24d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao#include <mach/hardware.h>
25d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
26d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao/*
27d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao * Poll the i2c status register until the specified bit is set.
28d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao * Returns 0 if timed out (100 msec).
29d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao */
30d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetaostatic short poll_status(unsigned long bit)
31d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao{
32d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	int loop_cntr = 1000;
33d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
34d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	if (bit & I2C_STATUS_TFNF) {
35d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		do {
36d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao			udelay(10);
37d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		} while (!(readl(I2C_STATUS) & bit) && (--loop_cntr > 0));
38d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	} else {
39d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		/* RXRDY handler */
40d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		do {
41d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao			if (readl(I2C_TAR) == I2C_TAR_EEPROM)
42d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao				msleep(20);
43d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao			else
44d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao				udelay(10);
45d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		} while (!(readl(I2C_RXFLR) & 0xf) && (--loop_cntr > 0));
46d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	}
47d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
48d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	return (loop_cntr > 0);
49d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao}
50d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
51d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetaostatic int xfer_read(struct i2c_adapter *adap, unsigned char *buf, int length)
52d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao{
53d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	int i2c_reg = *buf;
54d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
55d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	/* Read data */
56d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	while (length--) {
57d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		if (!poll_status(I2C_STATUS_TFNF)) {
58d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao			dev_dbg(&adap->dev, "Tx FIFO Not Full timeout\n");
59d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao			return -ETIMEDOUT;
60d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		}
61d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
62d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		/* send addr */
63d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		writel(i2c_reg | I2C_DATACMD_WRITE, I2C_DATACMD);
64d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
65d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		/* get ready to next write */
66d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		i2c_reg++;
67d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
68d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		/* send read CMD */
69d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		writel(I2C_DATACMD_READ, I2C_DATACMD);
70d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
71d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		/* wait until the Rx FIFO have available */
72d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		if (!poll_status(I2C_STATUS_RFNE)) {
73d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao			dev_dbg(&adap->dev, "RXRDY timeout\n");
74d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao			return -ETIMEDOUT;
75d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		}
76d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
77d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		/* read the data to buf */
78d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		*buf = (readl(I2C_DATACMD) & I2C_DATACMD_DAT_MASK);
79d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		buf++;
80d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	}
81d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
82d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	return 0;
83d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao}
84d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
85d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetaostatic int xfer_write(struct i2c_adapter *adap, unsigned char *buf, int length)
86d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao{
87d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	int i2c_reg = *buf;
88d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
89d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	/* Do nothing but storing the reg_num to a static variable */
90d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	if (i2c_reg == -1) {
91d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		printk(KERN_WARNING "Error i2c reg\n");
92d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		return -ETIMEDOUT;
93d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	}
94d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
95d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	if (length == 1)
96d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		return 0;
97d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
98d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	buf++;
99d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	length--;
100d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	while (length--) {
101d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		/* send addr */
102d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		writel(i2c_reg | I2C_DATACMD_WRITE, I2C_DATACMD);
103d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
104d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		/* send write CMD */
105d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		writel(*buf | I2C_DATACMD_WRITE, I2C_DATACMD);
106d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
107d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		/* wait until the Rx FIFO have available */
108d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		msleep(20);
109d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
110d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		/* read the data to buf */
111d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		i2c_reg++;
112d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		buf++;
113d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	}
114d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
115d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	return 0;
116d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao}
117d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
118d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao/*
119d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao * Generic i2c master transfer entrypoint.
120d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao *
121d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao */
122d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetaostatic int puv3_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *pmsg,
123d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		int num)
124d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao{
125d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	int i, ret;
126d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	unsigned char swap;
127d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
128d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	/* Disable i2c */
129d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	writel(I2C_ENABLE_DISABLE, I2C_ENABLE);
130d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
131d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	/* Set the work mode and speed*/
132d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	writel(I2C_CON_MASTER | I2C_CON_SPEED_STD | I2C_CON_SLAVEDISABLE, I2C_CON);
133d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
134d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	writel(pmsg->addr, I2C_TAR);
135d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
136d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	/* Enable i2c */
137d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	writel(I2C_ENABLE_ENABLE, I2C_ENABLE);
138d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
139d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	dev_dbg(&adap->dev, "puv3_i2c_xfer: processing %d messages:\n", num);
140d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
141d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	for (i = 0; i < num; i++) {
142d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		dev_dbg(&adap->dev, " #%d: %sing %d byte%s %s 0x%02x\n", i,
143d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao			pmsg->flags & I2C_M_RD ? "read" : "writ",
144d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao			pmsg->len, pmsg->len > 1 ? "s" : "",
145d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao			pmsg->flags & I2C_M_RD ? "from" : "to",	pmsg->addr);
146d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
147d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		if (pmsg->len && pmsg->buf) {	/* sanity check */
148d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao			if (pmsg->flags & I2C_M_RD)
149d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao				ret = xfer_read(adap, pmsg->buf, pmsg->len);
150d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao			else
151d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao				ret = xfer_write(adap, pmsg->buf, pmsg->len);
152d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
153d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao			if (ret)
154d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao				return ret;
155d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
156d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		}
157d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		dev_dbg(&adap->dev, "transfer complete\n");
158d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		pmsg++;		/* next message */
159d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	}
160d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
161d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	/* XXX: fixup be16_to_cpu in bq27x00_battery.c */
162d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	if (pmsg->addr == I2C_TAR_PWIC) {
163d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		swap = pmsg->buf[0];
164d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		pmsg->buf[0] = pmsg->buf[1];
165d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		pmsg->buf[1] = swap;
166d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	}
167d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
168d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	return i;
169d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao}
170d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
171d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao/*
172d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao * Return list of supported functionality.
173d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao */
174d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetaostatic u32 puv3_i2c_func(struct i2c_adapter *adapter)
175d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao{
176d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
177d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao}
178d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
179d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetaostatic struct i2c_algorithm puv3_i2c_algorithm = {
180d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	.master_xfer	= puv3_i2c_xfer,
181d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	.functionality	= puv3_i2c_func,
182d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao};
183d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
184d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao/*
185d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao * Main initialization routine.
186d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao */
187d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetaostatic int __devinit puv3_i2c_probe(struct platform_device *pdev)
188d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao{
189d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	struct i2c_adapter *adapter;
190d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	struct resource *mem;
191d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	int rc;
192d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
193d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
194d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	if (!mem)
195d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		return -ENODEV;
196d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
197d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	if (!request_mem_region(mem->start, resource_size(mem), "puv3_i2c"))
198d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		return -EBUSY;
199d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
200d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
201d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	if (adapter == NULL) {
202d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		dev_err(&pdev->dev, "can't allocate inteface!\n");
203d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		rc = -ENOMEM;
204d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		goto fail_nomem;
205d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	}
206d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	snprintf(adapter->name, sizeof(adapter->name), "PUV3-I2C at 0x%08x",
207d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao			mem->start);
208d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	adapter->algo = &puv3_i2c_algorithm;
209d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	adapter->class = I2C_CLASS_HWMON;
210d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	adapter->dev.parent = &pdev->dev;
211d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
212d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	platform_set_drvdata(pdev, adapter);
213d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
214d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	adapter->nr = pdev->id;
215d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	rc = i2c_add_numbered_adapter(adapter);
216d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	if (rc) {
217d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		dev_err(&pdev->dev, "Adapter '%s' registration failed\n",
218d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao				adapter->name);
219d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		goto fail_add_adapter;
220d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	}
221d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
222d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	dev_info(&pdev->dev, "PKUnity v3 i2c bus adapter.\n");
223d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	return 0;
224d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
225d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetaofail_add_adapter:
226d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	platform_set_drvdata(pdev, NULL);
227d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	kfree(adapter);
228d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetaofail_nomem:
229d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	release_mem_region(mem->start, resource_size(mem));
230d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
231d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	return rc;
232d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao}
233d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
234d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetaostatic int __devexit puv3_i2c_remove(struct platform_device *pdev)
235d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao{
236d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	struct i2c_adapter *adapter = platform_get_drvdata(pdev);
237d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	struct resource *mem;
238d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	int rc;
239d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
240d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	rc = i2c_del_adapter(adapter);
241d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	if (rc) {
242d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		dev_err(&pdev->dev, "Adapter '%s' delete fail\n",
243d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao				adapter->name);
244d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		return rc;
245d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	}
246d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
247d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	put_device(&pdev->dev);
248d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	platform_set_drvdata(pdev, NULL);
249d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
250d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
251d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	release_mem_region(mem->start, resource_size(mem));
252d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
253d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	return rc;
254d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao}
255d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
256d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao#ifdef CONFIG_PM
257d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetaostatic int puv3_i2c_suspend(struct platform_device *dev, pm_message_t state)
258d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao{
259d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	int poll_count;
260d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	/* Disable the IIC */
261d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	writel(I2C_ENABLE_DISABLE, I2C_ENABLE);
262d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	for (poll_count = 0; poll_count < 50; poll_count++) {
263d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		if (readl(I2C_ENSTATUS) & I2C_ENSTATUS_ENABLE)
264d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao			udelay(25);
265d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	}
266d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
267d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	return 0;
268d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao}
269d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
270d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetaostatic int puv3_i2c_resume(struct platform_device *dev)
271d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao{
272d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	return 0 ;
273d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao}
274d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao#else
275d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao#define puv3_i2c_suspend NULL
276d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao#define puv3_i2c_resume NULL
277d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao#endif
278d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
279d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetaostatic struct platform_driver puv3_i2c_driver = {
280d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	.probe		= puv3_i2c_probe,
281d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	.remove		= __devexit_p(puv3_i2c_remove),
282d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	.suspend	= puv3_i2c_suspend,
283d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	.resume		= puv3_i2c_resume,
284d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	.driver		= {
285d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		.name	= "PKUnity-v3-I2C",
286d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao		.owner	= THIS_MODULE,
287d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao	}
288d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao};
289d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
290858af58f67d4aba8afb02438e74292b9273cdb10Guan Xuetaomodule_platform_driver(puv3_i2c_driver);
291d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetao
292d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetaoMODULE_DESCRIPTION("PKUnity v3 I2C driver");
293d10e4a660d11212a41ac5d2c116a655e25e2d38aGuanXuetaoMODULE_LICENSE("GPL v2");
294858af58f67d4aba8afb02438e74292b9273cdb10Guan XuetaoMODULE_ALIAS("platform:puv3_i2c");
295