summaryrefslogtreecommitdiff
path: root/drivers/i2c/busses/i2c-sprd-v1.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/busses/i2c-sprd-v1.c')
-rw-r--r--drivers/i2c/busses/i2c-sprd-v1.c938
1 files changed, 938 insertions, 0 deletions
diff --git a/drivers/i2c/busses/i2c-sprd-v1.c b/drivers/i2c/busses/i2c-sprd-v1.c
new file mode 100644
index 00000000..6ca6b97c
--- /dev/null
+++ b/drivers/i2c/busses/i2c-sprd-v1.c
@@ -0,0 +1,938 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+
+#include <asm/irq.h>
+#include <asm/io.h>
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/io.h>
+#include <mach/globalregs.h>
+
+
+#define SPRD_I2C_CTL_ID (10)
+
+/*offset*/
+#define I2C_CTL 0x0000
+#define I2C_CMD 0x0004
+#define I2C_CLKD0 0x0008
+#define I2C_CLKD1 0x000C
+#define I2C_RST 0x0010
+#define I2C_CMD_BUF 0x0014
+#define I2C_CMD_BUF_CTL 0x0018
+
+/*The corresponding bit of I2C_CTL register*/
+#define I2C_CTL_INT (1 << 0) /* I2c interrupt */
+#define I2C_CTL_ACK (1 << 1) /* I2c received ack value */
+#define I2C_CTL_BUSY (1 << 2) /* I2c data line value */
+#define I2C_CTL_IE (1 << 3) /* I2c interrupt enable */
+#define I2C_CTL_EN (1 << 4) /* I2c module enable */
+#define I2C_CTL_SCL_LINE (1 << 5) /*scl line signal */
+#define I2C_CTL_SDA_LINE (1 << 6) /* sda line signal */
+#define I2C_CTL_NOACK_INT_EN (1 << 7) /* no ack int enable */
+#define I2C_CTL_NOACK_INT_STS (1 << 8) /* no ack int status */
+#define I2C_CTL_NOACK_INT_CLR (1 << 9) /* no ack int clear */
+
+/*The corresponding bit of I2C_CMD register*/
+#define I2C_CMD_INT_ACK (1 << 0) /* I2c interrupt clear bit */
+#define I2C_CMD_TX_ACK (1 << 1) /* I2c transmit ack that need to be send */
+#define I2C_CMD_WRITE (1 << 2) /* I2c write command */
+#define I2C_CMD_READ (1 << 3) /* I2c read command */
+#define I2C_CMD_STOP (1 << 4) /* I2c stop command */
+#define I2C_CMD_START (1 << 5) /* I2c start command */
+#define I2C_CMD_ACK (1 << 6) /* I2c received ack value */
+#define I2C_CMD_BUSY (1 << 7) /* I2c busy in exec commands */
+#define I2C_CMD_DATA 0xFF00 /* I2c data received or data need to be transmitted */
+
+/*The corresponding bit of I2C_RST register*/
+#define I2C_RST_RST (1 << 0) /* I2c reset bit */
+
+/*The corresponding bit of I2C_CMD_BUF_CTL register*/
+#define I2C_CTL_CMDBUF_EN (1 << 0) /* Enable the cmd buffer mode */
+#define I2C_CTL_CMDBUF_EXEC (1 << 1) /* Start to exec the cmd in the cmd buffer */
+
+#ifdef CONFIG_I2C_RESUME_EARLY
+#include <linux/syscore_ops.h>
+static struct platform_device *pdev_chip_i2c[SPRD_I2C_CTL_ID];
+#endif
+
+
+/* i2c controller state */
+enum sprd_i2c_state {
+ STATE_IDLE,
+ STATE_START,
+ STATE_READ,
+ STATE_WRITE,
+ STATE_STOP,
+ STATE_ADDR_BYTE2
+};
+struct sprd_i2c {
+ spinlock_t lock;
+ wait_queue_head_t wait;
+
+ struct i2c_msg *msg;
+ unsigned int msg_num;
+ unsigned int msg_idx;
+ unsigned int msg_ptr;
+
+ struct i2c_adapter adap;
+
+ enum sprd_i2c_state state;
+ struct clk *clk;
+ int irq;
+ bool is_suspended;
+ void __iomem *membase;
+ unsigned int *memphys;
+ unsigned int *memsize;
+};
+struct sprd_platform_i2c {
+ unsigned int normal_freq; /* normal bus frequency */
+ unsigned int fast_freq; /* fast frequency for the bus */
+ unsigned int min_freq; /* min frequency for the bus */
+};
+static struct sprd_platform_i2c sprd_i2c_default_platform = {
+ .normal_freq = 100*1000,
+ .fast_freq = 400*1000,
+ .min_freq = 10*1000,
+};
+static struct sprd_i2c *sprd_i2c_ctl[SPRD_I2C_CTL_ID];//set to 10
+
+
+static inline void dump_i2c_reg(struct sprd_i2c *pi2c)
+{
+ printk(KERN_ERR ": ======dump i2c-%d reg=======\n", pi2c->adap.nr);
+ printk(KERN_ERR ": I2C_CTRL:0x%x\n",__raw_readl(pi2c->membase + I2C_CTL));
+ printk(KERN_ERR ": I2C_CMD:0x%x\n",__raw_readl(pi2c->membase + I2C_CMD));
+ printk(KERN_ERR ": I2C_DVD0:0x%x\n",__raw_readl(pi2c->membase + I2C_CLKD0));
+ printk(KERN_ERR ": I2C_DVD1:0x%x\n",__raw_readl(pi2c->membase + I2C_CLKD1));
+ printk(KERN_ERR ": I2C_RST:0x%x\n",__raw_readl(pi2c->membase + I2C_RST));
+ printk(KERN_ERR ": I2C_CMD_BUF:0x%x\n",__raw_readl(pi2c->membase + I2C_CMD_BUF));
+ printk(KERN_ERR ": I2C_CMD_BUF_CTL:0x%x\n",__raw_readl(pi2c->membase + I2C_CMD_BUF_CTL));
+}
+
+/* sprd_i2c_wait_exec
+ *
+ * wait i2c cmd executable
+*/
+static inline int sprd_i2c_wait_exec(struct sprd_i2c *i2c)
+{
+ unsigned int cmd;
+ int timeout = 2000;
+
+ while (timeout-- > 0) {
+ cmd = __raw_readl(i2c->membase + I2C_CMD);
+ if (!(cmd & I2C_CMD_BUSY))
+ return 0;
+ }
+ printk( "I2C:timeout,busy in exec commands !\n");
+ dump_i2c_reg(i2c);
+ return -ETIMEDOUT;
+}
+
+/*irq enable function*/
+static inline void sprd_i2c_enable_irq(struct sprd_i2c *i2c)
+{
+ unsigned int ctl;
+
+ ctl=__raw_readl(i2c->membase+I2C_CTL);
+ __raw_writel(ctl | I2C_CTL_IE,i2c->membase+I2C_CTL);
+
+}
+
+/*irq disable function*/
+static inline void sprd_i2c_disable_irq(struct sprd_i2c *i2c)
+{
+ unsigned int ctl;
+
+ ctl=__raw_readl(i2c->membase+I2C_CTL);
+ __raw_writel(ctl &~I2C_CTL_IE,i2c->membase+I2C_CTL);
+}
+
+static inline void sprd_clr_irq(struct sprd_i2c *i2c)
+{
+ unsigned int cmd;
+
+ cmd=__raw_readl(i2c->membase+I2C_CMD) & 0xff00;
+ __raw_writel(cmd | I2C_CMD_INT_ACK, i2c->membase+I2C_CMD);
+}
+
+static inline struct sprd_platform_i2c *sprd_i2c_get_platformdata(struct device *dev)
+{
+ if (dev!=NULL && dev->platform_data != NULL)
+ return (struct sprd_platform_i2c *)dev->platform_data;
+
+ return &sprd_i2c_default_platform;
+}
+
+static void set_i2c_clk(struct sprd_i2c *i2c,unsigned int freq)
+{
+ unsigned int apb_clk;
+ unsigned int i2c_div;
+
+ apb_clk=26000000;
+ i2c_div=apb_clk/(4*freq)-3;
+
+ __raw_writel(i2c_div&0xffff,i2c->membase+I2C_CLKD0);
+ __raw_writel(i2c_div>>16,i2c->membase+I2C_CLKD1);
+
+}
+
+
+/**
+ * sprd_i2c_message_start - send START to the bus.
+ * @i2c: The struct sprd_i2c.
+ * @msg: Pointer to the messages to be processed.
+ *
+ * Returns 0 on success, otherwise a negative errno.
+ */
+static void sprd_i2c_message_start(struct sprd_i2c *i2c, struct i2c_msg *msg)
+{
+ unsigned int cmd;
+ if (msg->flags & I2C_M_TEN) {
+ cmd = 0xf0 | (((msg->addr >> 8) & 0x03)<<1);
+ if (msg->flags & I2C_M_RD){
+ i2c->state = STATE_START;
+ }
+ else{
+ i2c->state = STATE_ADDR_BYTE2;
+ }
+ }
+ else
+ {
+ cmd = (msg->addr & 0x7f) << 1;
+ }
+
+ if (msg->flags & I2C_M_RD)
+ cmd |= 0x1;
+
+ cmd=(cmd<<8) | I2C_CMD_START | I2C_CMD_WRITE;
+ __raw_writel(cmd, i2c->membase + I2C_CMD);
+}
+
+
+
+void sprd_i2c_ctl_chg_clk(unsigned int id_nr, unsigned int freq)
+{
+ unsigned int tmp;
+ clk_prepare_enable(sprd_i2c_ctl[id_nr]->clk);
+
+ tmp = __raw_readl(sprd_i2c_ctl[id_nr]->membase + I2C_CTL);
+ __raw_writel(tmp & (~I2C_CTL_EN),
+ sprd_i2c_ctl[id_nr]->membase + I2C_CTL);
+ tmp = __raw_readl(sprd_i2c_ctl[id_nr]->membase + I2C_CTL);
+
+ set_i2c_clk(sprd_i2c_ctl[id_nr], freq);
+
+ tmp = __raw_readl(sprd_i2c_ctl[id_nr]->membase + I2C_CTL);
+ __raw_writel(tmp | I2C_CTL_EN,
+ sprd_i2c_ctl[id_nr]->membase + I2C_CTL);
+ tmp = __raw_readl(sprd_i2c_ctl[id_nr]->membase + I2C_CTL);
+ clk_disable_unprepare(sprd_i2c_ctl[id_nr]->clk);
+
+}
+
+EXPORT_SYMBOL_GPL(sprd_i2c_ctl_chg_clk);
+
+static int sprd_i2c_clk_init(struct sprd_i2c *pi2c)
+{
+ char buf[256] = { 0 };
+ if (unlikely(pi2c->adap.nr >= 5)) /* dolphin less than 5 and shark's i2c device that large than 5 is named clk_i2c' */
+ strcpy(buf, "clk_i2c");
+ else
+ sprintf(buf, "clk_i2c%d", pi2c->adap.nr);
+ dev_info(&pi2c->adap.dev, "%s buf=%s", __func__, buf);
+
+ pi2c->clk = clk_get(&pi2c->adap.dev, buf);
+ if (IS_ERR(pi2c->clk)) {
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static void sprd_i2c_enable(struct sprd_i2c *i2c)
+{
+ unsigned int tmp;
+ struct sprd_platform_i2c *pdata;
+
+ __raw_writel(0x1,i2c->membase+I2C_RST);
+ __raw_writel(0,i2c->membase+I2C_RST);
+
+ tmp = __raw_readl(i2c->membase + I2C_CTL);
+ __raw_writel(tmp & ~I2C_CTL_EN, i2c->membase + I2C_CTL);
+ tmp = __raw_readl(i2c->membase + I2C_CTL);
+ __raw_writel(tmp & ~I2C_CTL_IE, i2c->membase + I2C_CTL);
+ tmp = __raw_readl(i2c->membase + I2C_CMD_BUF_CTL);
+ __raw_writel(tmp & ~I2C_CTL_CMDBUF_EN, i2c->membase + I2C_CMD_BUF_CTL);
+
+ pdata = sprd_i2c_get_platformdata(i2c->adap.dev.parent);
+ dev_dbg(&i2c->adap.dev, "%s() freq=%d\n", __func__,
+ pdata->normal_freq);
+ set_i2c_clk(i2c,pdata->normal_freq);
+
+ tmp = __raw_readl(i2c->membase + I2C_CTL);
+ __raw_writel(tmp | I2C_CTL_EN | I2C_CTL_IE, i2c->membase + I2C_CTL);
+
+ //__raw_writel(I2C_CMD_INT_ACK, i2c->membase + I2C_CMD);
+ sprd_clr_irq(i2c);
+
+}
+
+
+/**
+ * sprd_i2c_doxfer - The driver's doxfer function.
+ * @i2c: Pointer to the sprd_i2c structure.
+ * @msgs: Pointer to the messages to be processed.
+ * @num: Length of the MSGS array.
+ *
+ * Returns the number of messages processed, or a negative errno on
+ * failure.
+ */
+static int sprd_i2c_doxfer(struct sprd_i2c *i2c, struct i2c_msg *msgs, int num)
+{
+ unsigned int timeout;
+ unsigned long flags;
+ int ret;
+ clk_prepare_enable(i2c->clk);
+
+ ret = sprd_i2c_wait_exec(i2c);
+ if (ret != 0) {
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ spin_lock_irqsave(&i2c->lock,flags);
+
+ i2c->msg = msgs;
+ i2c->msg_num = num;
+ i2c->msg_ptr = 0;
+ i2c->msg_idx = 0;
+ i2c->state = STATE_START;
+
+ sprd_clr_irq(i2c);
+ sprd_i2c_enable_irq(i2c);
+ sprd_i2c_message_start(i2c, msgs);
+
+ spin_unlock_irqrestore(&i2c->lock,flags);
+
+ timeout=wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ);
+
+ ret = i2c->msg_idx;
+ /* having these next two as dev_err() makes life very
+ * noisy when doing an i2cdetect */
+ if (timeout == 0){
+ printk("i2c timeout i2c_trl =0x%x, i2c_cmd=0x%x\n",
+ __raw_readl(i2c->membase+I2C_CTL), __raw_readl(i2c->membase+I2C_CMD));
+
+ sprd_i2c_disable_irq(i2c);
+ sprd_clr_irq(i2c);
+ sprd_i2c_enable(i2c);
+ ret = -ENXIO;
+ }else if (ret != num){
+ printk("incomplete xfer (%d)\n", ret);
+ ret = -EAGAIN;
+ }
+
+ out:
+ clk_disable_unprepare(i2c->clk);
+ return ret;
+}
+
+/**
+ * sprd_i2c_xfer - The driver's master_xfer function.
+ * @adap: Pointer to the i2c_adapter structure.
+ * @msgs: Pointer to the messages to be processed.
+ * @num: Length of the MSGS array.
+ *
+ * Returns the number of messages processed, or a negative errno on
+ * failure.
+*/
+static int sprd_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)
+{
+ struct sprd_i2c *i2c = (struct sprd_i2c *)adap->algo_data;
+ int retry;
+ int ret;
+ if (i2c->is_suspended)
+ return -EBUSY;
+ for (retry = 0; retry < adap->retries; retry++) {
+
+ ret = sprd_i2c_doxfer(i2c, msgs, num);
+
+ if (ret != -EAGAIN)
+ return ret;
+
+ printk("I2C:Retrying transmission (%d)\n", retry);
+
+ udelay(100);
+ }
+ printk("I2C:transmission failed!\n");
+ return -EREMOTEIO;
+}
+
+/* declare our i2c functionality */
+static unsigned int sprd_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL ;
+}
+
+/* i2c bus registration info */
+static const struct i2c_algorithm sprd_i2c_algorithm = {
+ .master_xfer = sprd_i2c_xfer,
+ .functionality = sprd_i2c_func,
+};
+
+/* sprd_i2c_complete
+ *
+ * complete the message and wake up the caller, using the given return code,
+ * or zero to mean ok.
+*/
+static inline void sprd_i2c_complete(struct sprd_i2c *i2c, int ret)
+{
+ i2c->msg_ptr = 0;
+ i2c->msg = NULL;
+ i2c->msg_idx ++;
+ i2c->msg_num = 0;
+ if (ret)
+ i2c->msg_idx = ret;
+
+ wake_up(&i2c->wait);
+}
+
+static inline void sprd_i2c_stop_status(struct sprd_i2c *i2c, int ret)
+{
+ i2c->state = STATE_STOP;
+
+ sprd_i2c_complete(i2c, ret);
+ sprd_i2c_disable_irq(i2c);
+}
+
+static inline void sprd_i2c_stop(struct sprd_i2c *i2c, int ret)
+{
+ unsigned int cmd;
+
+ cmd= I2C_CMD_STOP | I2C_CMD_WRITE;
+ __raw_writel(cmd,i2c->membase+I2C_CMD);
+
+ i2c->state = STATE_STOP;
+
+ sprd_i2c_complete(i2c, ret);
+ sprd_i2c_disable_irq(i2c);
+}
+
+/* is_lastmsg()
+ *
+ * returns TRUE if the current message is the last in the set
+*/
+static inline int is_lastmsg(struct sprd_i2c *i2c)
+{
+ return i2c->msg_idx >= (i2c->msg_num - 1);
+}
+
+/* is_msglast
+ *
+ * returns TRUE if we this is the last byte in the current message
+*/
+static inline int is_msglast(struct sprd_i2c *i2c)
+{
+ return i2c->msg_ptr == i2c->msg->len-1;
+}
+
+/* is_msgend
+ *
+ * returns TRUE if we reached the end of the current message
+*/
+static inline int is_msgend(struct sprd_i2c *i2c)
+{
+ return i2c->msg_ptr >= i2c->msg->len;
+}
+
+static inline void sprd_i2c_read_tx_ack(struct sprd_i2c *i2c)
+{
+ unsigned int cmd;
+
+ cmd =I2C_CMD_READ | I2C_CMD_STOP | I2C_CMD_TX_ACK;
+ __raw_writel(cmd,i2c->membase+I2C_CMD);
+}
+
+static inline void sprd_i2c_read(struct sprd_i2c *i2c)
+{
+ unsigned int cmd;
+
+ cmd =I2C_CMD_READ;
+ __raw_writel(cmd,i2c->membase+I2C_CMD);
+}
+
+static void sprd_read_handle(struct sprd_i2c *i2c)
+{
+ if (is_msglast(i2c)) {
+ if (is_lastmsg(i2c)){
+ sprd_i2c_read_tx_ack(i2c);
+ }
+ } else if (is_msgend(i2c)) {
+ if (is_lastmsg(i2c)) {
+ i2c->state = STATE_STOP;
+ sprd_i2c_complete(i2c,0);
+ sprd_i2c_disable_irq(i2c);
+
+ } else {
+ i2c->msg_ptr = 0;
+ i2c->msg_idx++;
+ i2c->msg++;
+ sprd_i2c_read(i2c);
+ }
+ }
+ else{
+ sprd_i2c_read(i2c);
+ }
+}
+
+static int sprd_i2c_irq_nextbyte(struct sprd_i2c *i2c,unsigned int cmd_reg)
+{
+ unsigned char byte;
+ unsigned int cmd;
+ int ret = 0;
+
+ switch (i2c->state) {
+
+ case STATE_IDLE:
+ printk("sprd_i2c_irq_nextbyte error: called in STATE_IDLE\n");
+ goto out;
+ break;
+
+ case STATE_STOP:
+ printk( "sprd_i2c_irq_nextbyte error: called in STATE_STOP\n");
+ sprd_i2c_disable_irq(i2c);
+ goto out;
+
+ case STATE_ADDR_BYTE2:
+ cmd =((i2c->msg->addr & 0xff)<<8) | I2C_CMD_WRITE;
+ __raw_writel(cmd,i2c->membase+I2C_CMD);
+ i2c->state = STATE_START;
+ break;
+
+ case STATE_START:
+ if (cmd_reg & I2C_CMD_ACK && !(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
+ printk("I2C error:ack was not received\n");
+ dump_i2c_reg(i2c);
+ sprd_i2c_stop(i2c,-EREMOTEIO);
+ goto out;
+ }
+
+ if (i2c->msg->flags & I2C_M_RD)
+ i2c->state = STATE_READ;
+ else
+ i2c->state = STATE_WRITE;
+
+ if (is_lastmsg(i2c) && i2c->msg->len == 0) {
+ printk("detect address finish\n");
+ sprd_i2c_stop(i2c,0);
+ goto out;
+ }
+
+ if (i2c->state == STATE_READ)
+ {
+ sprd_read_handle(i2c);
+ break;
+ }
+
+ case STATE_WRITE:
+ if (!is_msgend(i2c)) {
+ if(is_msglast(i2c) && is_lastmsg(i2c)){
+ byte = i2c->msg->buf[i2c->msg_ptr++];
+ cmd =(byte<<8) | I2C_CMD_WRITE | I2C_CMD_STOP;
+ __raw_writel(cmd,i2c->membase+I2C_CMD);
+
+ }
+ else
+ {
+ byte = i2c->msg->buf[i2c->msg_ptr++];
+ cmd =(byte<<8) | I2C_CMD_WRITE;
+
+ __raw_writel(cmd,i2c->membase+I2C_CMD);
+ }
+ } else if (!is_lastmsg(i2c)) {
+
+ i2c->msg_ptr = 0;
+ i2c->msg_idx ++;
+ i2c->msg++;
+
+ sprd_i2c_message_start(i2c, i2c->msg);
+ i2c->state = STATE_START;
+
+ } else {
+ sprd_i2c_stop_status(i2c,0);
+ }
+ break;
+
+ case STATE_READ:
+ if (!(i2c->msg->flags & I2C_M_IGNORE_NAK) &&
+ !(is_msglast(i2c) && is_lastmsg(i2c))) {
+
+ if (cmd_reg & I2C_CMD_ACK) {
+ printk("I2C READ error: No Ack\n");
+ dump_i2c_reg(i2c);
+ sprd_i2c_stop(i2c,-ECONNREFUSED);
+ goto out;
+ }
+ }
+
+ cmd = __raw_readl(i2c->membase+I2C_CMD);
+ byte = (unsigned char)(cmd>>8);
+ i2c->msg->buf[i2c->msg_ptr++] = byte;
+ sprd_read_handle(i2c);
+
+ break;
+ }
+ out:
+ return ret;
+}
+
+/**
+ * sprd_i2c_irq - the interrupt service routine.
+ * @int: The irq, unused.
+ * @dev_id: Our struct sprd_i2c.
+ */
+static irqreturn_t sprd_i2c_irq(int irq, void *dev_id)
+{
+ struct sprd_i2c *i2c;
+ unsigned int cmd;
+ unsigned int ctl;
+ int ret;
+
+ i2c = (struct sprd_i2c *)dev_id;
+
+ ctl = __raw_readl(i2c->membase+I2C_CTL);
+
+ if (!(ctl & I2C_CTL_INT)) {
+#if 0//only for debug
+ unsigned int debug_irq_status = 0;
+ debug_irq_status = __raw_readl(SPRD_I2C0_BASE+I2C_CTL);
+ debug_irq_status |= __raw_readl(SPRD_I2C1_BASE+I2C_CTL);
+ debug_irq_status |= __raw_readl(SPRD_I2C2_BASE+I2C_CTL);
+ debug_irq_status |= __raw_readl(SPRD_I2C3_BASE+I2C_CTL);
+ if (!(debug_irq_status & I2C_CTL_INT)) {
+ printk("I2C:sprd_i2c_irq, NOT FOUND Or had handled!!!\n");
+ }
+#endif
+ return IRQ_NONE;
+ }
+ sprd_clr_irq(i2c);
+
+ if (i2c->state == STATE_IDLE) {
+ printk( "I2c irq: error i2c->state == IDLE\n");
+ goto out;
+ }
+
+ ret = sprd_i2c_wait_exec(i2c);
+ if (ret != 0) {
+ printk("I2C busy on exec command!\n");
+ goto out;
+ }
+
+
+ cmd =__raw_readl(i2c->membase+I2C_CMD);
+ ret=sprd_i2c_irq_nextbyte(i2c,cmd);
+
+
+ if(ret!=0)
+ printk("I2C irq:error\n");
+ out:
+ return IRQ_HANDLED;
+}
+
+
+static ssize_t i2c_reset(struct device* cd, struct device_attribute *attr,
+ const char* buf, size_t len)
+{
+ printk("%s\n",__func__);
+ sprd_i2c_enable(sprd_i2c_ctl[1]);
+ return len;
+}
+
+static DEVICE_ATTR(reset, S_IRUGO | S_IWUSR, NULL, i2c_reset);
+
+static int i2c_create_sysfs(struct platform_device *pdev)
+{
+ int err;
+ struct device *dev = &(pdev->dev);
+
+ err = device_create_file(dev, &dev_attr_reset);
+
+ return err;
+}
+
+
+static int sprd_i2c_probe(struct platform_device *pdev)
+{
+ struct sprd_i2c *i2c;
+ struct resource *res;
+ int irq;
+ int ret;
+
+ i2c = kzalloc(sizeof(struct sprd_i2c), GFP_KERNEL);
+ if (!i2c){
+ printk("I2C:kzalloc failed!\n");
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ printk("I2C: res->start=%x\n", res->start);
+ irq = platform_get_irq(pdev, 0);
+ printk("I2C: irq=%d\n", irq);
+ if (res == NULL || irq < 0){
+ printk("I2C:platform_get_resource&irq failed!\n");
+ ret=-ENODEV;
+ goto err_irq;
+ }
+#if 0
+ if (!request_mem_region(res->start, res_len(res), res->name)){
+ printk("I2C:request_mem_region failed!\n");
+ ret=-ENOMEM;
+ goto err_irq;
+ }
+#endif
+
+ i2c->membase= (void*)(res->start);
+ if (!i2c->membase) {
+ printk("I2C:ioremap failed!\n");
+ ret=-EIO;
+ goto err_irq;
+ }
+
+ i2c->irq = irq;
+
+ spin_lock_init(&i2c->lock);
+ init_waitqueue_head(&i2c->wait);
+
+ snprintf(i2c->adap.name,sizeof(i2c->adap.name),"%s","sprd-i2c");
+ i2c->adap.owner = THIS_MODULE;
+ i2c->adap.retries = 4;
+ i2c->adap.algo = &sprd_i2c_algorithm;
+ i2c->adap.algo_data = i2c;
+ i2c->adap.dev.parent = &pdev->dev;
+
+ i2c->adap.nr = pdev->id;
+ /* initialize the i2c controller */
+
+ ret = sprd_i2c_clk_init(i2c);
+ if (ret) {
+ dev_err(&pdev->dev, "get src clk failed\n");
+ goto err_irq;
+ }
+
+ clk_prepare_enable(i2c->clk);
+ sprd_i2c_enable(i2c);
+
+ clk_disable_unprepare(i2c->clk);
+
+
+ //sprd_i2c_special_init(pdev->id);
+
+ ret = request_irq(i2c->irq, sprd_i2c_irq, IRQF_SHARED,pdev->name,i2c);
+ if (ret) {
+ printk("I2C:request_irq failed!\n");
+ goto err_irq;
+ }
+
+ ret = i2c_add_numbered_adapter(&i2c->adap);
+ if (ret < 0){
+ printk("I2C:add_adapter failed!\n");
+ goto err_adap;
+ }
+
+ sprd_i2c_ctl[pdev->id] = i2c;
+ printk("I2C:sprd_i2c_ctl[%d]=%x",pdev->id, (unsigned int)(sprd_i2c_ctl[pdev->id]));
+ platform_set_drvdata(pdev, i2c);
+#ifdef CONFIG_I2C_RESUME_EARLY
+ pdev_chip_i2c[pdev->id] = pdev;
+#endif
+
+ i2c_create_sysfs(pdev);
+
+ return 0;
+
+err_adap:
+ free_irq(i2c->irq,i2c);
+err_irq:
+ kfree(i2c);
+
+ return ret;
+}
+
+static int sprd_i2c_remove(struct platform_device *pdev)
+{
+ struct sprd_i2c *i2c = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+#ifdef CONFIG_I2C_RESUME_EARLY
+ pdev_chip_i2c[pdev->id] = NULL;
+#endif
+
+ i2c_del_adapter(&i2c->adap);
+
+ free_irq(i2c->irq, i2c);
+
+ kfree(i2c);
+ printk("[I2C:sprd_i2c_remove]");
+
+ return 0;
+}
+
+#if defined (CONFIG_PM) && defined(CONFIG_ARCH_SCX35)
+struct i2c_regs {
+ unsigned long ctl;/*0x0*/
+ unsigned long cmd;/*0x4*/
+ unsigned long div0;/*0x8*/
+ unsigned long div1;/*0xc*/
+ unsigned long rst;/*0x10*/
+ unsigned long cmd_buf;/*0x14*/
+ unsigned long cmd_buf_ctl;/*0x18*/
+
+ /*global i2c_regs*/
+};
+
+static struct i2c_regs l2c_saved_regs[SPRD_I2C_CTL_ID];
+static int i2c_controller_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct sprd_i2c *pi2c = platform_get_drvdata(pdev);
+
+ if (pi2c && (pi2c->adap.nr < ARRAY_SIZE(l2c_saved_regs))) {
+ clk_prepare_enable(pi2c->clk);
+ l2c_saved_regs[pi2c->adap.nr].ctl = __raw_readl(pi2c->membase + I2C_CTL);
+ l2c_saved_regs[pi2c->adap.nr].cmd = __raw_readl(pi2c->membase + I2C_CMD);
+ l2c_saved_regs[pi2c->adap.nr].div0 = __raw_readl(pi2c->membase + I2C_CLKD0);
+ l2c_saved_regs[pi2c->adap.nr].div1 = __raw_readl(pi2c->membase + I2C_CLKD1);
+ l2c_saved_regs[pi2c->adap.nr].rst = __raw_readl(pi2c->membase + I2C_RST);
+ l2c_saved_regs[pi2c->adap.nr].cmd_buf = __raw_readl(pi2c->membase + I2C_CMD_BUF);
+ l2c_saved_regs[pi2c->adap.nr].cmd_buf_ctl = __raw_readl(pi2c->membase + I2C_CMD_BUF_CTL);
+ pi2c->is_suspended = true;
+ }
+ if (pi2c && !IS_ERR(pi2c->clk))
+ clk_disable_unprepare(pi2c->clk);
+ return 0;
+}
+
+static int i2c_controller_resume(struct platform_device *pdev)
+{
+ unsigned int tmp;
+ struct sprd_i2c *pi2c = platform_get_drvdata(pdev);
+
+ if (pi2c && !IS_ERR(pi2c->clk))
+ clk_prepare_enable(pi2c->clk);
+ if (pi2c) {
+ tmp = __raw_readl( pi2c->membase + I2C_CTL);
+ __raw_writel(tmp & (~I2C_CTL_EN), pi2c->membase + I2C_CTL);
+ __raw_writel(l2c_saved_regs[pi2c->adap.nr].div0, pi2c->membase + I2C_CLKD0);
+ __raw_writel(l2c_saved_regs[pi2c->adap.nr].div1, pi2c->membase + I2C_CLKD1);
+ __raw_writel(l2c_saved_regs[pi2c->adap.nr].ctl, pi2c->membase + I2C_CTL);
+ __raw_writel(l2c_saved_regs[pi2c->adap.nr].cmd, pi2c->membase + I2C_CMD);
+ __raw_writel(l2c_saved_regs[pi2c->adap.nr].rst, pi2c->membase + I2C_RST);
+ __raw_writel(l2c_saved_regs[pi2c->adap.nr].cmd_buf, pi2c->membase + I2C_CMD_BUF);
+ __raw_writel(l2c_saved_regs[pi2c->adap.nr].cmd_buf_ctl, pi2c->membase + I2C_CMD_BUF_CTL);
+ pi2c->is_suspended = false;
+ clk_disable_unprepare(pi2c->clk);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_I2C_RESUME_EARLY
+static int i2c_controller_suspend_late(void)
+{
+ int i;
+ pm_message_t state = {
+ .event = 0
+ };
+
+ for (i = 0; i < ARRAY_SIZE(sprd_i2c_ctl); i++) {
+ if (pdev_chip_i2c[i] == NULL)
+ continue;
+ i2c_controller_suspend(pdev_chip_i2c[i], state);
+ }
+
+ return 0;
+}
+
+static void i2c_controller_resume_early(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sprd_i2c_ctl); i++) {
+ if (pdev_chip_i2c[i] == NULL)
+ continue;
+ i2c_controller_resume(pdev_chip_i2c[i]);
+ }
+}
+
+static struct syscore_ops sprd_i2c_syscore_ops = {
+ .suspend = i2c_controller_suspend_late,
+ .resume = i2c_controller_resume_early,
+};
+
+static int __init sprd_i2c_syscore_init(void)
+{
+ register_syscore_ops(&sprd_i2c_syscore_ops);
+ return 0;
+}
+subsys_initcall(sprd_i2c_syscore_init);
+#endif /* CONFIG_I2C_RESUME_EARLY */
+#else
+#define i2c_controller_suspend NULL
+#define i2c_controller_resume NULL
+#endif
+
+
+static struct platform_driver sprd_i2c_driver = {
+ .probe = sprd_i2c_probe,
+ .remove = sprd_i2c_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "sprd-i2c",
+ },
+#ifndef CONFIG_I2C_RESUME_EARLY
+ .suspend = i2c_controller_suspend,
+ .resume = i2c_controller_resume,
+#endif
+
+};
+
+static int __init i2c_adap_sprd_init(void)
+{
+ printk(KERN_INFO"I2c:sprd driver $Revision:1.0 $\n");
+
+ return platform_driver_register(&sprd_i2c_driver);
+}
+subsys_initcall(i2c_adap_sprd_init);
+
+static void __exit i2c_adap_sprd_exit(void)
+{
+ platform_driver_unregister(&sprd_i2c_driver);
+}
+module_exit(i2c_adap_sprd_exit);
+
+MODULE_DESCRIPTION("sprd I2C Bus driver");
+MODULE_AUTHOR("hao liu, <hao.liu@spreadtrum.com>");
+MODULE_LICENSE("GPL");
+