zte's code,first commit

Change-Id: I9a04da59e459a9bc0d67f101f700d9d7dc8d681b
diff --git a/ap/os/linux/linux-3.4.x/drivers/i2c/busses/i2c-zx29.c b/ap/os/linux/linux-3.4.x/drivers/i2c/busses/i2c-zx29.c
new file mode 100644
index 0000000..73e12ac
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/i2c/busses/i2c-zx29.c
@@ -0,0 +1,1051 @@
+/* linux/drivers/i2c/busses/i2c-zx29.c
+ *
+ * Copyright (C) 2015 Sanechips-TSP
+ *
+ * ----------NOTICE-------------
+ * ZX29 serials I2C Controller driver used by zx297520, this driver
+ * do not support slave mode. bus clock rate should between 400KHz--100KHz.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/hrtimer.h>
+#include <linux/ktime.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 <linux/io.h>
+//#include <linux/of_i2c.h>
+#include <linux/of_gpio.h>
+#include <linux/clk/zx29-clk.h>
+
+
+#include <asm/irq.h>
+#include <mach/i2c_private.h>
+#include <mach/spinlock.h>
+#include <linux/soc/zte/pm/drv_idle.h>
+
+/*#define DEBUG_I2C_ADAPTER*/
+
+#ifdef DEBUG_I2C_ADAPTER
+#define drv_printk(fmt, arg...)  printk(KERN_DEBUG fmt, ##arg)
+#pragma GCC optimize("O0")
+#else
+#define drv_printk(fmt, arg...)
+#endif
+
+#define I2C_PSM_CONTROL         (1)
+#define I2C_LONG_TRANSFER		(1)
+
+#define I2C_FIFO_DEPTH   32
+#define I2C_TIMEOUT 	(msecs_to_jiffies(1000))
+#define MAX_BUS_CLK   	400000
+#define MIN_BUS_CLK   	100000
+
+#define I2C_WCLK_FREQ	(26*1000*1000)
+
+#if I2C_PSM_CONTROL
+static volatile unsigned int i2c_active_count = 0;
+#endif
+
+#if I2C_LONG_TRANSFER
+/* functions for hrtimer */
+
+static enum hrtimer_restart zx29_i2c_timer_callback(struct hrtimer *timer)
+{
+    struct zx29_i2c *i2c;
+    enum hrtimer_restart ret = HRTIMER_NORESTART;
+
+    i2c = container_of(timer, struct zx29_i2c, hr_timer);
+
+    if ((i2c->state == STATE_READ) && (i2c->buf_remaining > I2C_FIFO_DEPTH)) {
+    	unsigned int len_rx = 0;
+        unsigned int len_diff = 0;
+    	u8 *buf = i2c->msg->buf;
+    	u8 val = 0;
+
+    	len_rx = i2c_get_rx_fifo_length(i2c);
+        len_diff = i2c->buf_remaining - I2C_FIFO_DEPTH;
+        if (len_rx < len_diff) {
+            ret = HRTIMER_RESTART;
+			hrtimer_forward_now(timer, i2c->ktime);
+        } else {
+            len_rx = len_diff;
+        }
+
+    	i2c->buf_remaining -= len_rx;
+
+    	/*read data from fifo*/
+    	while (len_rx--) {
+    		val = (u8)i2c_read_data(i2c);
+    		*(buf + i2c->msg_ptr) = val;
+    		i2c->msg_ptr++;
+    	}
+    } else if ((i2c->state == STATE_WRITE) && (i2c->buf_remaining > 0)) {
+    	unsigned int len_tx = 0;
+        unsigned int len_diff = 0;
+    	u8 *buf = i2c->msg->buf;
+    	u8 val = 0;
+
+    	len_tx = i2c_get_tx_fifo_length(i2c);
+        len_tx = I2C_FIFO_DEPTH - len_tx;
+        len_diff = i2c->buf_remaining;
+        if (len_tx < len_diff) {
+            ret = HRTIMER_RESTART;
+			hrtimer_forward_now(timer, i2c->ktime);
+        } else {
+            len_tx = len_diff;
+        }
+
+    	i2c->buf_remaining -= len_tx;
+
+    	/* write data to fifo */
+    	while (len_tx--) {
+    		val = *(buf + i2c->msg_ptr);
+    		i2c_write_data(i2c, val);
+    		i2c->msg_ptr++;
+    	}
+    }
+
+    return ret;
+}
+
+static void zx29_i2c_start_timer( struct zx29_i2c *i2c )
+{
+    unsigned long delay_in_us = (9*I2C_FIFO_DEPTH/2)*1000*1000 / i2c->clkrate;
+
+    i2c->ktime = ktime_set(0, delay_in_us * 1000);
+
+    hrtimer_init( &i2c->hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL );
+
+    i2c->hr_timer.function = zx29_i2c_timer_callback;
+
+    hrtimer_start( &i2c->hr_timer, i2c->ktime, HRTIMER_MODE_REL );
+}
+
+static void zx29_i2c_stop_timer( struct zx29_i2c *i2c )
+{
+    int ret;
+
+    ret = hrtimer_cancel(&i2c->hr_timer);
+    if(ret){
+		pr_info("%s return %d\n", __FUNCTION__, ret);
+    }
+
+    return;
+}
+#endif
+
+/* functions for hrtimer end */
+
+#if I2C_PSM_CONTROL
+static void zx29_i2c_set_active(struct wake_lock *lock)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+    if(i2c_active_count == 0)
+    {
+        zx_cpuidle_set_busy(IDLE_FLAG_I2C);
+    }
+    i2c_active_count++;
+
+	local_irq_restore(flags);
+
+    wake_lock(lock);
+}
+
+static void zx29_i2c_set_idle(struct wake_lock *lock)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+    i2c_active_count--;
+    if(i2c_active_count == 0)
+    {
+        zx_cpuidle_set_free(IDLE_FLAG_I2C);
+    }
+
+	local_irq_restore(flags);
+
+    wake_unlock(lock);
+}
+#endif
+
+#ifdef CONFIG_SYSFS
+static ssize_t zx29_i2c_sysfs_show(struct device *dev,
+		struct device_attribute *attr, char *buf);
+static ssize_t zx29_i2c_sysfs_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count);
+
+
+DEVICE_ATTR(clk_rate, S_IWUSR | S_IRUGO, zx29_i2c_sysfs_show, zx29_i2c_sysfs_store);
+
+static struct attribute * zx29_i2c_sysfs_attrs[] = {
+	&dev_attr_clk_rate.attr,
+	NULL
+};
+
+static const struct attribute_group zx29_i2c_sysfs_attr_group = {
+	.attrs = zx29_i2c_sysfs_attrs,
+};
+
+static int zx29_i2c_sysfs_create_group(struct zx29_i2c *i2c)
+{
+	return sysfs_create_group(&i2c->dev->kobj, &zx29_i2c_sysfs_attr_group);
+}
+
+static void zx29_i2c_sysfs_remove_group(struct zx29_i2c *i2c)
+{
+	sysfs_remove_group(&i2c->dev->kobj, &zx29_i2c_sysfs_attr_group);
+}
+
+static ssize_t zx29_i2c_sysfs_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct zx29_i2c *i2c = dev_get_drvdata(dev);
+	
+	if (!i2c)
+		return -EINVAL;
+
+	return scnprintf(buf, PAGE_SIZE, "%ld\n", i2c->clkrate);
+}
+
+static ssize_t zx29_i2c_sysfs_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct zx29_i2c *i2c = dev_get_drvdata(dev);
+	unsigned long clock_rate = 0;
+
+	if(strict_strtoul(buf, 0, &clock_rate))
+		return -EINVAL;
+	if((clock_rate < MIN_BUS_CLK) || (clock_rate > MAX_BUS_CLK) || (NULL == i2c))
+		return -EINVAL;
+
+	i2c_lock_adapter(&i2c->adap);
+	i2c->clkrate = clock_rate;
+	i2c_unlock_adapter(&i2c->adap);
+
+	pr_info("new i2c%d scl rate: %ld\n", i2c->id, i2c->clkrate);
+
+	return count;
+}
+#else
+static int zx29_i2c_sysfs_create_group(struct zx29_i2c *i2c)
+{
+	return 0;
+}
+
+static void zx29_i2c_sysfs_remove_group(struct zx29_i2c *i2c)
+{
+	return;
+}
+#endif
+
+static emsf_lock_id i2c_get_sflock(unsigned int i2c_id)
+{
+ 	emsf_lock_id	softLock = I2C2_SFLOCK;
+
+	switch(i2c_id)
+	{
+		case 0:			//pmic i2c
+			softLock = I2C2_SFLOCK;
+			break;
+		case 1:
+			softLock = I2C1_SFLOCK;
+			break;
+		default:
+			BUG();
+	}
+
+	return softLock;
+}
+
+/*
+  * zx29_i2c_init
+  *
+  * initialise the controller, set frequency
+  */
+static int zx29_i2c_init(struct zx29_i2c *i2c)
+{
+	unsigned int bus_clk;
+	unsigned int pdiv;
+ 	emsf_lock_id	softLock = i2c_get_sflock(i2c->id);
+	unsigned long actual_clk = 0;
+
+	soft_spin_lock(softLock);
+
+	/*reset*/
+	zx29_i2c_reset_fifo(i2c);
+	udelay(1);
+
+	/*calculate bus clock division, fix input clock is 26M*/
+	bus_clk=i2c->clkrate/1000;  /*in KHz*/
+	pdiv=((I2C_WCLK_FREQ/1000)/4)/bus_clk - 1;
+    if(((I2C_WCLK_FREQ/1000)/4) % bus_clk != 0){
+        pdiv++;
+    }
+
+    actual_clk = (I2C_WCLK_FREQ/4)/(pdiv+1);   /*actul bus clock rate*/
+	if ((actual_clk > MAX_BUS_CLK) || (actual_clk < MIN_BUS_CLK)){
+		pr_warn("bus clock rate error %ld!\n", actual_clk);
+		BUG();
+	}
+	i2c_set_clk_div(i2c, pdiv);
+
+	/*master mode, disable interrupt, clear interrupt*/
+	i2c_set_cmd(i2c, 0x1);
+
+    soft_spin_unlock(softLock);
+
+    i2c->state = STATE_IDLE;
+
+	return 0;
+}
+
+/*
+ *zx29_i2c_start
+ * put the start of a message onto the bus
+ */
+static void zx29_i2c_start(struct zx29_i2c *i2c, uint8_t fmt)
+{
+	unsigned int  addr = i2c->msg->addr;
+	unsigned long ctrl = 0;
+	unsigned int  addr_mode = 0;
+
+	/*set slave device address*/
+	addr_mode = i2c->msg->flags & I2C_M_TEN;
+
+	i2c_set_addr(i2c, addr, addr_mode);
+
+	i2c_clr_irq_ack(i2c);
+
+	/*set cmd register for rx or tx*/
+	ctrl = i2c_get_cmd(i2c);
+	ctrl &= ~CMD_I2C_FMT_MSK;
+	ctrl |= fmt;
+
+	if (addr_mode){
+		/*10 bit address mode :clear interrupt conditions, set master mode,set 10bit address mode  and start*/
+		ctrl |= CMD_I2C_START|CMD_I2C_MODE|CMD_TENBIT_MODE;
+	}
+	else{
+    	/*7 bit address mode:clear interrupt conditions, set master mode  and start*/
+		ctrl &= ~CMD_TENBIT_MODE;
+		ctrl |= CMD_I2C_START|CMD_I2C_MODE;
+	}
+
+	i2c_set_cmd(i2c, ctrl);
+}
+
+
+/*
+ * zx29_i2c_stop
+ */
+
+static inline void zx29_i2c_stop(struct zx29_i2c *i2c)
+{
+	//unsigned long ctrl = ioread32(i2c->regs + I2C_CMD);
+
+	/* stop the transfer ,clear interrupt conditions */
+	//ctrl |= CMD_I2C_STOP|CMD_IRQ_ACK;
+	//iowrite32(ctrl, i2c->regs + I2C_CMD);
+
+	i2c->state = STATE_STOP;
+}
+
+/*
+ * check i2c busy or not
+ */
+static inline int zx29_i2c_is_busy(struct zx29_i2c *i2c)
+{
+	unsigned int time_out=0xff;
+
+	while(time_out--){
+		if(i2c_get_bus_status(i2c)==0)
+			return 0;
+		else
+			udelay(1);
+	}
+	return -ETIMEDOUT;
+}
+
+/*
+ * i2c fifo write
+ */
+static void zx29_i2c_prepare_write(struct zx29_i2c *i2c)
+{
+	u8 val=0;
+	u8 *buf=i2c->msg->buf;
+	u16 len =i2c->buf_remaining;
+	u16 len_tx = 0;
+	unsigned int ret=0;
+
+    /*this step must be done. because FIFO empty interrupt does not mean bus is idle*/
+	ret=zx29_i2c_is_busy(i2c);
+	if(ret)
+		BUG();
+
+    zx29_i2c_reset_tx_fifo(i2c);
+
+	if(len > I2C_FIFO_DEPTH)
+		len_tx = I2C_FIFO_DEPTH;
+	else
+		len_tx = len;
+	i2c->buf_remaining -= len_tx;
+
+	i2c_set_tx_fifo_depth(i2c, len_tx-1);
+
+	/*write data to fifo*/
+	while(len_tx--){
+		val = *(buf + i2c->msg_ptr);
+		i2c_write_data(i2c, val);
+		i2c->msg_ptr++;
+	}
+	barrier();
+
+//    zx29_i2c_start(i2c);
+}
+
+/*
+ * i2c start read
+ */
+static void zx29_i2c_prepare_read(struct zx29_i2c *i2c)
+{
+	u16 len =i2c->buf_remaining;
+	u16 len_rx = 0;
+	unsigned int ret=0;
+
+    /*i2c  can only be operated when bus is idle*/
+	ret=zx29_i2c_is_busy(i2c);
+	if(ret)
+		BUG();
+
+    zx29_i2c_reset_rx_fifo(i2c);
+
+	if(len > I2C_FIFO_DEPTH)
+		len_rx = I2C_FIFO_DEPTH;
+	else
+		len_rx = len;
+
+	i2c_set_rx_fifo_depth(i2c, len_rx-1);
+
+//    zx29_i2c_start(i2c);
+}
+
+/*
+ * i2c fifo read
+ */
+static void zx29_i2c_read_fifo(struct zx29_i2c *i2c)
+{
+	unsigned int len_rx=0;
+	u8 *buf=i2c->msg->buf;
+	u8 val=0;
+
+	len_rx = i2c_get_rx_fifo_length(i2c);
+
+	i2c->buf_remaining -= len_rx;
+
+	/*read data from fifo*/
+	while(len_rx--){
+		val =(u8) i2c_read_data(i2c);
+		*(buf + i2c->msg_ptr) = val;
+		i2c->msg_ptr++;
+	}
+ }
+
+/* zx29_i2c_irq
+ *
+ * top level IRQ servicing routine
+ */
+static irqreturn_t zx29_i2c_irq(int irqno, void *dev_id)
+{
+	struct zx29_i2c *i2c = dev_id;
+	unsigned long status;
+
+	status = i2c_get_irq_status(i2c);
+	i2c->reg_status = (i2c->reg_status << 8) | status;
+
+	/*
+	 * if state is idle or stop, maybe other core on 7510 platform  trigger interrupt
+	 * it will be looked as spurious interrupt, nothing need to be done
+	 */
+	if ((i2c->state == STATE_IDLE) || (i2c->state == STATE_STOP))
+		return IRQ_HANDLED;
+
+    if (status & (IRQ_ERR_DEVICE | IRQ_ERR_DATA | IRQ_TRANS_DONE)) {
+		i2c_disable_irq(i2c);
+		disable_irq_nosync(i2c->irq);
+
+	 	up(&i2c->msg_complete);
+
+		return IRQ_HANDLED;
+    }
+
+    if (status & IRQ_TIME_OUT) {
+		i2c_clr_irq_ack(i2c);
+		return IRQ_HANDLED;
+    }
+
+	return IRQ_HANDLED;
+}
+
+
+/* zx29_i2c_get_bus
+ *
+ * get the i2c bus for a master transaction
+*/
+static int zx29_i2c_get_bus(struct zx29_i2c *i2c)
+{
+	int timeout = 400;
+
+	while (timeout-- > 0) {
+
+		if (!i2c_get_bus_status(i2c))
+			return 0;
+
+		msleep(1);
+	}
+
+	return -ETIMEDOUT;
+}
+
+/* zx29_i2c_doxfer
+ *
+ * this starts an i2c transfer
+ */
+static int zx29_i2c_doxfer(struct zx29_i2c *i2c, struct i2c_msg *msgs, bool cmb_rw)
+{
+	int ret;
+
+	if (i2c->suspended)
+		return -EIO;
+
+	i2c->msg     = msgs;
+	i2c->msg_ptr = 0;
+	i2c->buf_remaining = msgs->len;
+//	i2c->msg_idx = 0;
+
+//	init_completion(&(i2c->msg_complete));
+	if (unlikely(i2c->msg_complete.count)) {
+		pr_err("i2c sem %d\n", i2c->msg_complete.count);
+	}
+	i2c->reg_status = 0;
+    i2c_clr_irq_ack(i2c);
+	i2c_enable_irq(i2c);
+    enable_irq(i2c->irq);
+
+	if (cmb_rw) {
+		zx29_i2c_prepare_write(i2c);
+
+		i2c->msg     = &msgs[1];
+		i2c->msg_ptr = 0;
+		i2c->buf_remaining = msgs[1].len;
+
+		i2c->state = STATE_READ;
+		memset(msgs[1].buf, 0, msgs[1].len);
+        zx29_i2c_prepare_read(i2c);
+
+		zx29_i2c_start(i2c, CMD_I2C_FMT_CMB);
+	} else if (i2c->msg->flags & I2C_M_RD) {
+		i2c->state = STATE_READ;
+		memset(msgs->buf, 0, msgs->len);
+        zx29_i2c_prepare_read(i2c);
+		zx29_i2c_start(i2c, CMD_I2C_FMT_RD);
+	} else {
+		i2c->state = STATE_WRITE;
+		zx29_i2c_prepare_write(i2c);
+		zx29_i2c_start(i2c, CMD_I2C_FMT_WR);
+	}
+
+#if I2C_LONG_TRANSFER
+    if (i2c->msg->len > I2C_FIFO_DEPTH) {
+        zx29_i2c_start_timer(i2c);
+    }
+#endif
+
+    /* waiting for tranfer finished */
+	ret = down_timeout(&i2c->msg_complete, I2C_TIMEOUT);
+
+#if I2C_LONG_TRANSFER
+    if (i2c->msg->len > I2C_FIFO_DEPTH) {
+        zx29_i2c_stop_timer(i2c);
+    }
+#endif
+
+	if (ret < 0) {
+		i2c_disable_irq(i2c);
+	    disable_irq_nosync(i2c->irq);
+
+		pr_err("i2c transfer timeout\n");
+
+		zx29_i2c_stop(i2c);
+		zx29_i2c_init(i2c);
+
+		return -ETIMEDOUT;
+	}
+
+	if (i2c->state == STATE_READ) {
+		zx29_i2c_read_fifo(i2c);
+	}
+
+	/* slave no ack on address phase or data phase */
+    if (i2c_get_irq_status(i2c) & (IRQ_ERR_DEVICE | IRQ_ERR_DATA)) {
+		pr_err("NACK by slave 0x%X, status %X\n", i2c->msg->addr, i2c_get_irq_status(i2c));
+
+		while (~ i2c_get_irq_status(i2c) & IRQ_TRANS_DONE);
+		i2c_clr_irq_ack(i2c);
+
+		zx29_i2c_init(i2c);
+
+		return -ECOMM;
+    }
+
+	i2c_clr_irq_ack(i2c);
+	i2c->state = STATE_IDLE;
+
+#if I2C_LONG_TRANSFER
+	if(i2c->buf_remaining) {
+		pr_err("%s: %d/%d isn't transferred\n", __FUNCTION__, i2c->buf_remaining, i2c->msg->len);
+		return -EAGAIN;
+	}
+#endif
+
+	return 0;
+}
+
+/* zx29_i2c_xfer
+ *
+ * first port of call from the i2c bus code when an message needs
+ * transferring across the i2c bus.
+*/
+static int zx29_i2c_xfer(struct i2c_adapter *adap,
+			struct i2c_msg *msgs, int num)
+{
+	struct zx29_i2c *i2c = (struct zx29_i2c *)adap->algo_data;
+	int ret;
+	int i = 0;
+	bool combie_rw_applicable;
+	unsigned int  pdiv;
+	emsf_lock_id	softLock = i2c_get_sflock(i2c->id);
+
+	if (i2c->suspended)
+		return -EIO;
+
+	if (msgs->len == 0)
+		return -EINVAL;
+
+#if I2C_PSM_CONTROL
+    zx29_i2c_set_active(&i2c->psm_lock);
+#endif
+
+    soft_spin_lock(softLock);
+
+#ifdef CONFIG_ARCH_ZX297502
+	clk_enable(i2c->clk);
+#endif
+
+	ret = zx29_i2c_get_bus(i2c);
+	if (ret != 0) {
+		dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);
+
+        //soft_spin_unlock(I2C_SFLOCK);   /* zhouqi */
+
+		ret = -EAGAIN;
+		goto exit;
+	}
+
+	/* calculate bus clock division, fixed input clock is 26M */
+	pdiv = (I2C_WCLK_FREQ / 4 / i2c->clkrate) - 1;
+    if((I2C_WCLK_FREQ / 4) % i2c->clkrate != 0)
+        pdiv++;
+	i2c_set_clk_div(i2c, pdiv);
+
+//	i2c->msg_num = num;
+	/* check whether combined rw is applicable */
+	if ((num == 2) && (msgs[0].addr == msgs[1].addr)) {
+		combie_rw_applicable = ((msgs[0].flags & I2C_M_TEN) == (msgs[1].flags & I2C_M_TEN)) \
+								&& (~msgs[0].flags & I2C_M_RD) \
+								&& (msgs[1].flags & I2C_M_RD) \
+								&& (msgs[0].len <= I2C_FIFO_DEPTH);
+	} else
+		combie_rw_applicable = 0;
+
+	if (combie_rw_applicable) {
+		ret = zx29_i2c_doxfer(i2c, msgs, 1);
+	} else for (i = 0; i < num; i++) {
+		ret = zx29_i2c_doxfer(i2c, &msgs[i], 0);
+//			i2c->msg_idx=i;
+		if (ret) {
+			//printk(KERN_INFO "%s err code=%d\n", __FUNCTION__, ret);
+			break;
+		}
+    }
+
+exit:
+#ifdef CONFIG_ARCH_ZX297502
+	clk_disable(i2c->clk);
+#endif
+
+    soft_spin_unlock(softLock);
+
+#if I2C_PSM_CONTROL
+    zx29_i2c_set_idle(&i2c->psm_lock);
+#endif
+
+	return ret ? ret : num;
+}
+
+/* declare our i2c functionality */
+static u32 zx29_i2c_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK)| I2C_FUNC_10BIT_ADDR;
+}
+
+/* i2c bus registration info */
+static const struct i2c_algorithm zx29_i2c_algorithm = {
+	.master_xfer		= zx29_i2c_xfer,
+	.functionality		= zx29_i2c_func,
+};
+
+#ifdef CONFIG_OF
+/* zx29_i2c_parse_dt
+ *
+ * Parse the device tree node and retreive the platform data.
+*/
+static void
+zx29_i2c_parse_dt(struct device_node *np, struct zx29_i2c *i2c)
+{
+	struct zx29_i2c_platform_data *pdata = i2c->pdata;
+
+	if (!np)
+		return;
+	of_property_read_u32(np, "Sanechips-tsp,i2c-slave-addr", &pdata->slave_addr);
+	of_property_read_u32(np, "Sanechips-tsp,i2c-max-bus-freq",(u32 *)&pdata->max_bus_clk);
+}
+#else
+static void
+zx29_i2c_parse_dt(struct device_node *np, struct zx29_i2c *i2c)
+{
+	return;
+}
+#endif
+
+static int i2c_init_clks(struct platform_device *pdev, struct zx29_i2c *i2c)
+{
+	/* find the clock and enable it */
+	i2c->clk = clk_get(&pdev->dev, "work_clk");
+	if (IS_ERR(i2c->clk)) {
+		dev_err(&pdev->dev, "cannot get work clock\n");
+		return -ENOENT;
+	}
+
+	/* set i2c work clock at 26MHz/1 */
+	clk_set_rate(i2c->clk, I2C_WCLK_FREQ);
+	clk_enable(i2c->clk);
+
+	i2c->pclk = clk_get(&pdev->dev, "apb_clk");
+	if (IS_ERR(i2c->pclk)) {
+		dev_err(&pdev->dev, "cannot get apb clock\n");
+		return -ENOENT;
+	}
+	clk_enable(i2c->pclk);
+
+	clk_set_auto_gate(i2c->clk, true);
+	clk_set_auto_gate(i2c->pclk, true);
+
+	return 0;
+}
+
+/* zx29_i2c_probe
+ *
+ * called by the bus driver when a suitable device is found
+*/
+static int zx29_i2c_probe(struct platform_device *pdev)
+{
+	struct zx29_i2c *i2c=NULL;
+	struct zx29_i2c_platform_data *pdata = NULL;
+	struct resource *res=NULL;
+	void __iomem *base=NULL;
+	const unsigned int *prop=NULL;
+	int ret=0;
+    emsf_lock_id softLock;
+
+	/* irq&register */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no memory resource\n");
+		ret = -EINVAL;
+		goto err_iomap;
+	}
+	base = (void __iomem *)(res->start);
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no irq resource\n");
+		ret = -EINVAL;
+		goto err_iomap;
+	}
+
+    i2c = devm_kzalloc(&pdev->dev, sizeof(struct zx29_i2c), GFP_KERNEL);
+	if (!i2c) {
+		dev_err(&pdev->dev, "no memory for state\n");
+        ret = -ENOMEM;
+		goto err_iomap;
+	}
+
+	if (!pdev->dev.of_node) {
+	  	pdata = pdev->dev.platform_data;
+	  	if (!pdata) {
+	  		dev_err(&pdev->dev, "no platform data\n");
+	  		return -EINVAL;
+		}
+    }
+
+	snprintf(i2c->name, sizeof(i2c->name), "zx29-i2c%d", pdev->id);
+	i2c->pdata	= pdata;
+    i2c->id 	= pdev->id;
+	i2c->regs	= base;
+	i2c->irq	= res->start;
+	i2c->dev 	= &pdev->dev;
+	i2c->state 	= STATE_IDLE;
+
+    i2c->clkrate = MIN_BUS_CLK; /* default clock rate */
+	if (pdata) {
+		i2c->clkrate = pdata->bus_clk_rate;
+	}
+	else if (i2c->dev->of_node) {    /* if there is a device tree node ... */
+		prop = of_get_property(i2c->dev->of_node,
+				"clock-frequency", NULL);
+		if (prop)
+			i2c->clkrate = be32_to_cpup(prop);
+	}
+
+	spin_lock_init(&i2c->lock);
+
+	sema_init(&i2c->msg_complete, 0);
+
+    softLock = i2c_get_sflock(i2c->id);
+    soft_spin_lock(softLock);
+
+	i2c_config_pins(i2c);
+
+	ret = i2c_init_clks(pdev, i2c);
+	if(ret < 0)
+	{
+        soft_spin_unlock(softLock);
+		goto err_free;
+	}
+
+	/* initialise the i2c controller */
+	ret = zx29_i2c_init(i2c);
+	if (ret != 0){
+        soft_spin_unlock(softLock);
+        goto err_free;
+	}
+
+    i2c_disable_irq(i2c);
+	/* find the IRQ for this unit (note, this relies on the init call to
+	 * ensure no current IRQs pending
+	 */
+	ret = devm_request_irq(&pdev->dev, i2c->irq, zx29_i2c_irq,
+							IRQF_TRIGGER_HIGH | IRQF_NO_THREAD | IRQF_ONESHOT,
+							dev_name(&pdev->dev), i2c);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
+        soft_spin_unlock(softLock);
+        goto err_clk;
+	}
+    disable_irq_nosync(i2c->irq);
+
+    soft_spin_unlock(softLock);
+
+	/* Note, previous versions of the driver used i2c_add_adapter()
+	 * to add the bus at any number. We now pass the bus number via
+	 * the platform data, so if unset it will now default to always
+	 * being bus 0.
+	 */
+	zx29_i2c_parse_dt(pdev->dev.of_node, i2c);
+	strlcpy(i2c->adap.name, "zx29-i2c", sizeof(i2c->adap.name));
+	i2c->adap.owner   = THIS_MODULE;
+	i2c->adap.algo    = &zx29_i2c_algorithm;
+	i2c->adap.retries = 3;
+	i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+	i2c->adap.algo_data = i2c;
+	i2c->adap.dev.parent = &pdev->dev;
+	i2c->adap.nr = pdev->id;
+	i2c->adap.dev.of_node = pdev->dev.of_node;
+
+#if I2C_PSM_CONTROL
+    wake_lock_init(&i2c->psm_lock, WAKE_LOCK_SUSPEND, i2c->name);
+#endif
+
+	ret = i2c_add_numbered_adapter(&i2c->adap);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to add bus to i2c core\n");
+		goto err_irq;
+	}
+
+//	of_i2c_register_devices(&i2c->adap);
+	platform_set_drvdata(pdev, i2c);
+
+	zx29_i2c_sysfs_create_group(i2c);
+
+	dev_info(&pdev->dev, "%s: ZX29 I2C adapter\n", dev_name(&i2c->adap.dev));
+
+#ifdef CONFIG_ARCH_ZX297502
+	clk_disable(i2c->clk);
+#endif
+	return 0;
+
+ /*err_cpufreq:
+	zx29_i2c_deregister_cpufreq(i2c);*/
+
+err_irq:
+#if I2C_PSM_CONTROL
+    wake_lock_destroy(&i2c->psm_lock);
+#endif
+	devm_free_irq(&pdev->dev, i2c->irq, i2c);
+
+err_clk:
+	clk_disable(i2c->clk);
+	clk_put(i2c->clk);
+	clk_disable(i2c->pclk);
+	clk_put(i2c->pclk);
+
+
+err_free:
+    kfree(i2c);
+
+err_iomap:
+//	iounmap(base);
+
+	return ret;
+}
+
+/* zx29_i2c_remove
+ *
+ * called when device is removed from the bus
+*/
+static int zx29_i2c_remove(struct platform_device *pdev)
+{
+	struct zx29_i2c *i2c = platform_get_drvdata(pdev);
+
+	zx29_i2c_sysfs_remove_group(i2c);
+
+	i2c_del_adapter(&i2c->adap);
+
+	clk_disable(i2c->clk);
+	clk_put(i2c->clk);
+	clk_disable(i2c->pclk);
+	clk_put(i2c->pclk);
+
+#if I2C_PSM_CONTROL
+    wake_lock_destroy(&i2c->psm_lock);
+#endif
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int zx29_i2c_suspend(struct platform_device *pdev, pm_message_t state)
+{
+
+#ifdef CONFIG_ARCH_ZX297502
+	struct zx29_i2c *i2c = platform_get_drvdata(pdev);
+
+    i2c_lock_adapter(&i2c->adap);
+	i2c->suspended = 1;
+	i2c_unlock_adapter(&i2c->adap);
+#endif
+
+	return 0;
+}
+
+static int zx29_i2c_resume(struct platform_device *pdev)
+{
+
+#ifdef CONFIG_ARCH_ZX297502
+	struct zx29_i2c *i2c = platform_get_drvdata(pdev);
+
+    i2c_lock_adapter(&i2c->adap);
+	clk_enable(i2c->clk);
+	zx29_i2c_init(i2c);
+	clk_disable(i2c->clk);
+	i2c->suspended = 0;
+    i2c_unlock_adapter(&i2c->adap);
+#endif
+
+	return 0;
+}
+#endif
+
+
+#ifdef CONFIG_OF
+static const struct of_device_id zx29_i2c_match[] = {
+	{ .compatible = "Sanechips-TSP, zx29_i2c0" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, zx29_i2c_match);
+#else
+#define zx29_i2c_match NULL
+#endif
+
+static struct platform_driver zx29_i2c_driver = {
+	.probe		= zx29_i2c_probe,
+	.remove		= zx29_i2c_remove,
+#ifdef CONFIG_PM
+	.suspend    = zx29_i2c_suspend,
+	.resume     = zx29_i2c_resume,
+#endif
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "zx29_i2c",
+		.of_match_table = zx29_i2c_match,
+	},
+};
+
+static int __init i2c_adap_zx29_init(void)
+{
+	int ret;
+
+	ret=platform_driver_register(&zx29_i2c_driver);
+	if (ret<0) {
+		pr_err("zx29 i2c driver register fail\n");
+	}
+	return ret;
+}
+subsys_initcall(i2c_adap_zx29_init);
+
+static void __exit i2c_adap_zx29_exit(void)
+{
+	platform_driver_unregister(&zx29_i2c_driver);
+}
+module_exit(i2c_adap_zx29_exit);
+