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®ister */
+ 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);
+