diff options
author | Kevin | 2014-11-15 10:00:36 +0800 |
---|---|---|
committer | Kevin | 2014-11-15 10:00:36 +0800 |
commit | 9d40ac5867b9aefe0722bc1f110b965ff294d30d (patch) | |
tree | de942df665fac4bac0d9cb7ae86910fe937b0c1a /ANDROID_3.4.5/drivers/i2c | |
parent | 392e8802486cb573b916e746010e141a75f507e6 (diff) | |
download | FOSSEE-netbook-kernel-source-9d40ac5867b9aefe0722bc1f110b965ff294d30d.tar.gz FOSSEE-netbook-kernel-source-9d40ac5867b9aefe0722bc1f110b965ff294d30d.tar.bz2 FOSSEE-netbook-kernel-source-9d40ac5867b9aefe0722bc1f110b965ff294d30d.zip |
add via modify part source code for wm8880 4.4 kitkat
Diffstat (limited to 'ANDROID_3.4.5/drivers/i2c')
-rw-r--r-- | ANDROID_3.4.5/drivers/i2c/Kconfig | 8 | ||||
-rw-r--r-- | ANDROID_3.4.5/drivers/i2c/Makefile | 1 | ||||
-rw-r--r-- | ANDROID_3.4.5/drivers/i2c/algos/Kconfig | 8 | ||||
-rw-r--r-- | ANDROID_3.4.5/drivers/i2c/algos/Makefile | 1 | ||||
-rwxr-xr-x | ANDROID_3.4.5/drivers/i2c/algos/wmt-i2c-algo.c | 308 | ||||
-rw-r--r-- | ANDROID_3.4.5/drivers/i2c/busses/Kconfig | 21 | ||||
-rw-r--r-- | ANDROID_3.4.5/drivers/i2c/busses/Makefile | 2 | ||||
-rwxr-xr-x | ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-bus-1.c | 1294 | ||||
-rwxr-xr-x | ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-bus-2.c | 1253 | ||||
-rwxr-xr-x | ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-bus-3.c | 1256 | ||||
-rwxr-xr-x | ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-bus-4.c | 1274 | ||||
-rwxr-xr-x | ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-bus.c | 1254 | ||||
-rwxr-xr-x | ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-slave-bus-1.c | 661 | ||||
-rwxr-xr-x | ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-slave-bus.c | 662 | ||||
-rwxr-xr-x | ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-slave-bus.h | 24 | ||||
-rwxr-xr-x | ANDROID_3.4.5/drivers/i2c/i2c-api.c | 216 |
16 files changed, 8243 insertions, 0 deletions
diff --git a/ANDROID_3.4.5/drivers/i2c/Kconfig b/ANDROID_3.4.5/drivers/i2c/Kconfig index 5f13c62e..dd2ba673 100644 --- a/ANDROID_3.4.5/drivers/i2c/Kconfig +++ b/ANDROID_3.4.5/drivers/i2c/Kconfig @@ -46,6 +46,14 @@ config I2C_CHARDEV This support is also available as a module. If so, the module will be called i2c-dev. + +config I2C_API + tristate "I2C API support" + help + Say Y here if you want to use i2c interface simply in other modules. + + This support is also available as a module. If so, the module + will be called i2c-api. config I2C_MUX tristate "I2C bus multiplexing support" diff --git a/ANDROID_3.4.5/drivers/i2c/Makefile b/ANDROID_3.4.5/drivers/i2c/Makefile index beee6b2d..38b383c4 100644 --- a/ANDROID_3.4.5/drivers/i2c/Makefile +++ b/ANDROID_3.4.5/drivers/i2c/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_I2C) += i2c-core.o obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o obj-$(CONFIG_I2C_MUX) += i2c-mux.o +obj-$(CONFIG_I2C_API) += i2c-api.o obj-y += algos/ busses/ muxes/ ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG diff --git a/ANDROID_3.4.5/drivers/i2c/algos/Kconfig b/ANDROID_3.4.5/drivers/i2c/algos/Kconfig index f1cfe7e5..39b0ade7 100644 --- a/ANDROID_3.4.5/drivers/i2c/algos/Kconfig +++ b/ANDROID_3.4.5/drivers/i2c/algos/Kconfig @@ -14,4 +14,12 @@ config I2C_ALGOPCF config I2C_ALGOPCA tristate "I2C PCA 9564 interfaces" +config I2C_ALGOWMT + tristate "WMT I2C Algorithm" + depends on ARCH_WMT && I2C + help + This supports the use of the WMT I2C interface found on WMT + processors. Say Y if you have one of these. You should also say Y + for the WMT I2C peripheral driver support below. + endmenu diff --git a/ANDROID_3.4.5/drivers/i2c/algos/Makefile b/ANDROID_3.4.5/drivers/i2c/algos/Makefile index 215303f6..d26856d2 100644 --- a/ANDROID_3.4.5/drivers/i2c/algos/Makefile +++ b/ANDROID_3.4.5/drivers/i2c/algos/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o obj-$(CONFIG_I2C_ALGOPCA) += i2c-algo-pca.o ccflags-$(CONFIG_I2C_DEBUG_ALGO) := -DDEBUG +obj-$(CONFIG_I2C_WMT) += wmt-i2c-algo.o diff --git a/ANDROID_3.4.5/drivers/i2c/algos/wmt-i2c-algo.c b/ANDROID_3.4.5/drivers/i2c/algos/wmt-i2c-algo.c new file mode 100755 index 00000000..3be850d2 --- /dev/null +++ b/ANDROID_3.4.5/drivers/i2c/algos/wmt-i2c-algo.c @@ -0,0 +1,308 @@ +/*++ + drivers/i2c/algos/wmt_i2c_algo.c + + Copyright (c) 2008 WonderMedia Technologies, Inc. + + 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, see <http://www.gnu.org/licenses/>. + + WonderMedia Technologies, Inc. + 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. +--*/ + +#define WMT_I2C_ALGO_C + +#include <linux/kernel.h> +#include <linux/module.h> + +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/i2c.h> +/* +#include <linux/i2c-id.h> +*/ + +#include <mach/hardware.h> +#include <mach/wmt-i2c-bus.h> + +#ifdef __KERNEL__ + +#ifdef DEBUG + #define DPRINTK printk +#else + #define DPRINTK(x...) +#endif + +#else +#define DPRINTK printf + +#endif + +static struct i2c_adapter *wmt_i2c_adap[5]; + +/*!************************************************************************* +* wmt_i2c_valid_messages() +* +* Private Function by Paul Kwong, 2007/1/12 +*/ +/*! +* \brief verify the input message +* +* \retval 1 if success +*/ +static int wmt_i2c_valid_messages( + struct i2c_msg msgs[], /*!<; //[IN] transfer data */ + int num /*!<; //[IN] transfer data length */ +) +{ + int i; + if (num < 1 || num > MAX_MESSAGES) { + DPRINTK(KERN_INFO "Invalid number of messages (max=%d, num=%d)\n", MAX_MESSAGES, num); + return -EINVAL; + } + + /* check consistency of our messages */ + for (i = 0; i < num; i++) { + if (&msgs[i] == NULL) { + DPRINTK(KERN_INFO "Msgs is NULL\n"); + return -EINVAL; + } else { + if (msgs[i].buf == NULL) { + DPRINTK(KERN_INFO "Length is less than zero\n"); + return -EINVAL; + } + } + } + + return 1; +} +/*!************************************************************************* +* wmt_i2c_do_xfer() +* +* Private Function by Paul Kwong, 2007/1/12 +*/ +/*! +* \brief +* +* \retval 0 if success +*/ +static int wmt_i2c_do_xfer( + struct i2c_adapter *i2c_adap, /*!<; //[IN] a pointer point to struct inode */ + struct i2c_msg msgs[], /*!<; //[IN] transfer data */ + int num /*!<; //[IN] transfer data length */ +) +{ + int i; + struct i2c_algo_wmt_data *adap; + int ret = 0 ; + + adap = i2c_adap->algo_data; + + /*ret = adap->wait_bus_not_busy();*/ + for (i = 0 ; i < 10; ++i) + ; + if (ret < 0) + return ret ; + + ret = adap->send_request(msgs, num, 0, 0, 0); + + return ret; + +} + +/*!************************************************************************* +* wmt_i2c_xfer() +* +* Private Function by Paul Kwong, 2007/1/12 +*/ +/*! +* \brief Transfer (read/write) data to i2c bus, wmt_i2c_do_xfer will be called to transfer +* +* \retval 0 if success +*/ +static int wmt_i2c_xfer( + struct i2c_adapter *i2c_adap, /*!<; //[IN] a pointer point to struct inode */ + struct i2c_msg msgs[], /*!<; //[IN] transfer data */ + int num /*!<; //[IN] transfer data length */ +) +{ + int ret ; + int i ; + + ret = wmt_i2c_valid_messages(msgs, num); + if (ret < 0) + return ret ; + + for (i = i2c_adap->retries ; i >= 0; i--) { + + ret = wmt_i2c_do_xfer(i2c_adap, msgs, num); + if (ret > 0) + return ret ; + DPRINTK(KERN_INFO"Retrying transmission \n"); + udelay(100); + } + + DPRINTK(KERN_INFO"Retried %i times\n", i2c_adap->retries); + return ret; + +} +/*!************************************************************************* +* wmt_i2c_functionality() +* +* Private Function by Paul Kwong, 2007/1/12 +*/ +/*! +* \brief +* +* \retval smbus functionality +*/ +static u32 wmt_i2c_functionality( + struct i2c_adapter *adapter /*!<; //[IN] a pointer point to struct inode */ +) +{ + /* Emulate the SMBUS functions*/ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +/* + * send i2c_msg into fifo of i2c bus + * msg: transfer data content + * msg_num:number of transferring msg + * bus_id :used to indicate which bus do device want to access + */ +int wmt_i2c_transfer(struct i2c_msg* msgs, int msg_num, int bus_id, void (*callback)(void *data), void *data) +{ + struct i2c_algo_wmt_data *adap; + int ret = 0 ; + + adap = wmt_i2c_adap[bus_id]->algo_data; + ret = adap->send_request(msgs, msg_num, 1, callback, data); + return ret; +} + +#if 0 +/*!************************************************************************* +* wmt_i2c_control() +* +* Private Function by Paul Kwong, 2007/1/12 +*/ +/*! +* \brief To set i2c transfer mode +* +* \retval 0 if success +*/ +static int wmt_i2c_control( + struct i2c_adapter *i2c_adap, /*!<; //[IN] a pointer point to struct inode */ + unsigned int cmd, /*!<; //[IN] standard or fast mode */ + unsigned long arg /*!<; //Not in used, but can't delete */ +) +{ + int ret ; + struct i2c_algo_wmt_data *adap = i2c_adap->algo_data; + + ret = 0 ; + DPRINTK("wmt_i2c_control: cmd = 0x%8.8x \n", cmd); + + switch (cmd) { + case I2C_SET_STANDARD_MODE: + adap->set_mode(I2C_STANDARD_MODE); + break ; + case I2C_SET_FAST_MODE: + adap->set_mode(I2C_FAST_MODE); + break ; + default: + ret = -EINVAL; + break; + } + + return ret; +} +#endif + + +struct i2c_algorithm wmt_i2c_algorithm = { + .master_xfer = wmt_i2c_xfer, + .functionality = wmt_i2c_functionality, +}; +/*!************************************************************************* +* wmt_i2c_add_bus() +* +* Private Function by Paul Kwong, 2007/1/12 +*/ +/*! +* \brief +* +* \retval NULL +*/ +int wmt_i2c_add_bus(struct i2c_adapter *i2c_adap) +{ + printk(KERN_INFO"i2c: adding %s.\n", i2c_adap->name); + + i2c_adap->algo = &wmt_i2c_algorithm; + wmt_i2c_adap[i2c_adap->nr] = i2c_adap; + + /* register new adapter to i2c module... */ + /* + i2c_add_adapter(i2c_adap); + */ + i2c_add_numbered_adapter(i2c_adap); + + /* adap->reset();*/ + + return 0; +} +/*!************************************************************************* +* wmt_i2c_del_bus() +* +* Private Function by Paul Kwong, 2007/1/12 +*/ +/*! +* \brief +* +* \retval NULL +*/ +int wmt_i2c_del_bus(struct i2c_adapter *i2c_adap) +{ + int res; + res = i2c_del_adapter(i2c_adap); + if (res < 0) + return res; + + printk(KERN_INFO "i2c: removing %s.\n", i2c_adap->name); + + return 0; +} +/*!************************************************************************* +* wmt_i2c_algo_init() +* +* Private Function by Paul Kwong, 2007/1/12 +*/ +/*! +* \brief +* +* \retval NULL +*/ +static int __init wmt_i2c_algo_init(void) +{ + printk(KERN_INFO "i2c: wmt algorithm module loaded.\n"); + return 0; +} + +EXPORT_SYMBOL(wmt_i2c_add_bus); +EXPORT_SYMBOL(wmt_i2c_del_bus); + +MODULE_AUTHOR("WonderMedia Technologies, Inc."); +MODULE_DESCRIPTION("WMT I2C ALGO Driver"); +MODULE_LICENSE("GPL"); + +module_init(wmt_i2c_algo_init); + +#undef WMT_I2C_ALGO_C diff --git a/ANDROID_3.4.5/drivers/i2c/busses/Kconfig b/ANDROID_3.4.5/drivers/i2c/busses/Kconfig index d2c5095d..1bfed463 100644 --- a/ANDROID_3.4.5/drivers/i2c/busses/Kconfig +++ b/ANDROID_3.4.5/drivers/i2c/busses/Kconfig @@ -907,4 +907,25 @@ config SCx200_ACB This support is also available as a module. If so, the module will be called scx200_acb. +config I2C_WMT + tristate "WonderMedia I2C Interface" + depends on ARCH_WMT && I2C && I2C_ALGOWMT + help + This supports the use of the WMT I2C interface found on the WMT + Say Y if you have one of these. You should also say Y for the WMT + I2C peripheral driver support below. + + To compile this driver as a module, say M here: the + modules will be called i2c-wmt and i2c-algo-wmt. +config I2C1_WMT + bool + depends on ARCH_WMT && I2C && I2C_ALGOWMT + default y +config I2C_SLAVE_WMT + bool "WonderMedia I2C-SLAVE Interface" + depends on ARCH_WMT && I2C_WMT + help + This supports the use of the WMT I2C SLAVE interface found on the WMT + Say Y if you have one of these. You should also say Y for the WMT + I2C peripheral driver support below. endmenu diff --git a/ANDROID_3.4.5/drivers/i2c/busses/Makefile b/ANDROID_3.4.5/drivers/i2c/busses/Makefile index 569567b0..20082f63 100644 --- a/ANDROID_3.4.5/drivers/i2c/busses/Makefile +++ b/ANDROID_3.4.5/drivers/i2c/busses/Makefile @@ -88,4 +88,6 @@ obj-$(CONFIG_I2C_STUB) += i2c-stub.o obj-$(CONFIG_SCx200_ACB) += scx200_acb.o obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o +obj-$(CONFIG_I2C_WMT) += wmt-i2c-bus.o wmt-i2c-bus-1.o wmt-i2c-bus-2.o wmt-i2c-bus-3.o wmt-i2c-bus-4.o +obj-$(CONFIG_I2C_SLAVE_WMT) += wmt-i2c-slave-bus.o wmt-i2c-slave-bus-1.o ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git a/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-bus-1.c b/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-bus-1.c new file mode 100755 index 00000000..3fd0f5a3 --- /dev/null +++ b/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-bus-1.c @@ -0,0 +1,1294 @@ +/*++ + drivers/i2c/busses/wmt_i2c_bus-1.c + + Copyright (c) 2013 WonderMedia Technologies, Inc. + + 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, see <http://www.gnu.org/licenses/>. + + WonderMedia Technologies, Inc. + 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. +--*/ +/* Include your headers here*/ +#include <linux/kernel.h> +#include <linux/module.h> + +#include <linux/i2c.h> +/* +#include <linux/i2c-id.h> +*/ +#include <linux/init.h> +#include <linux/time.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/interrupt.h> + +#include <mach/hardware.h> +#include <asm/irq.h> +#include <mach/irqs.h> +#include <mach/wmt-i2c-bus.h> +#include <linux/slab.h> +#include <linux/pm.h> +#include <linux/syscore_ops.h> + +#ifdef __KERNEL__ + +#ifdef DEBUG + #define DPRINTK printk +#else + #define DPRINTK(x...) +#endif + +#else + #define DPRINTK printf + +#endif + + +#define MAX_BUS_READY_CNT 50 /* jiffy*/ +#define MAX_TX_TIMEOUT 500 /* ms*/ +#define MAX_RX_TIMEOUT 500 /* ms*/ +#define CTRL_GPIO GPIO_CTRL_GP17_I2C_BYTE_ADDR +#define PU_EN_GPIO PULL_EN_GP17_I2C_BYTE_ADDR +#define PU_CTRL_GPIO PULL_CTRL_GP17_I2C_BYTE_ADDR + +#define USE_UBOOT_PARA + +struct wmt_i2c_s { + struct i2c_regs_s *regs; + int irq_no ; + enum i2c_mode_e i2c_mode ; + int volatile isr_nack ; + int volatile isr_byte_end ; + int volatile isr_timeout ; + int volatile isr_int_pending ; +}; + +static int i2c_wmt_wait_bus_not_busy(void); +static int i2c_wmt_wait_bus_release(void); +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +static unsigned int speed_mode = 0; +unsigned int wmt_i2c1_speed_mode = 0; +static unsigned int is_master = 1;/*master:1, slave:0*/ +unsigned int wmt_i2c1_is_master = 1; +static unsigned int wmt_i2c1_power_state = 0;/*0:power on, 1:suspend, 2:shutdown*/ +EXPORT_SYMBOL(wmt_i2c1_is_master); + +/**/ +/* variable*/ +/*-------------------------------------------------*/ +static volatile struct wmt_i2c_s i2c ; + +DECLARE_WAIT_QUEUE_HEAD(i2c_wait); +/* +spinlock_t i2c_wmt_irqlock = SPIN_LOCK_UNLOCKED; +*/ +static DEFINE_SPINLOCK(i2c_wmt_irqlock); +static struct list_head wmt_i2c_fifohead; +/* +static spinlock_t i2c_fifolock = SPIN_LOCK_UNLOCKED; +*/ +static DEFINE_SPINLOCK(i2c_fifolock); +static int i2c_wmt_read_buf( + unsigned int slave_addr, + char *buf, + unsigned int length, + int restart, + int last +); +static int i2c_wmt_write_buf( + unsigned int slave_addr, + char *buf, + unsigned int length, + int restart, + int last +); + +static void i2c_wmt_set_mode(enum i2c_mode_e mode /*!<; //[IN] mode */) +{ + if (is_master == 0) + return; + i2c.i2c_mode = mode ; + + if (i2c.i2c_mode == I2C_STANDARD_MODE) { + DPRINTK("I2C: set standard mode \n"); + i2c.regs->tr_reg = I2C_TR_STD_VALUE ; /* 0x8041*/ + } else if (i2c.i2c_mode == I2C_FAST_MODE) { + DPRINTK("I2C: set fast mode \n"); + i2c.regs->tr_reg = I2C_TR_FAST_VALUE ; /* 0x8011*/ + } +} + + +static int i2c_send_request( + struct i2c_msg *msg, + int msg_num, + int non_block, + void (*callback)(void *data), + void *data +) +{ + struct wmt_i2cbusfifo *i2c_fifo_head; + struct i2c_msg *pmsg = NULL; + int ret = 0; + int restart = 0; + int last = 0; + unsigned long flags; + int slave_addr = msg[0].addr; + + if (slave_addr == WMT_I2C_API_I2C_ADDR) + return ret ; + + if (wmt_i2c1_power_state == 2) { + printk("I2C1 has been shutdown\n"); + return -EIO; + } + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + if (non_block == 0) + i2c.isr_int_pending = 0; + + i2c_fifo_head = kzalloc(sizeof(struct wmt_i2cbusfifo), GFP_ATOMIC); + if (!i2c_fifo_head) { + pr_err("%s: kzalloc fail\n", __func__); + return -ENOMEM; + } + INIT_LIST_HEAD(&i2c_fifo_head->busfifohead); + + pmsg = &msg[0]; + i2c_fifo_head->msg = pmsg; + i2c_fifo_head->msg_num = msg_num; + + if (list_empty(&wmt_i2c_fifohead)) { + if (i2c_wmt_wait_bus_release()) { + kfree(i2c_fifo_head); + printk("i2c1 bus has been pull down by slave\n"); + return -EIO; + } + } + + spin_lock_irqsave(&i2c_fifolock, flags); + if (list_empty(&wmt_i2c_fifohead)) { + i2c_wmt_wait_bus_not_busy(); + pmsg = &msg[0]; + i2c_fifo_head->xfer_length = 1; + i2c_fifo_head->xfer_msgnum = 0; + i2c_fifo_head->restart = 0; + i2c_fifo_head->non_block = non_block; + + if (non_block == 1) { + i2c_fifo_head->callback = callback; + i2c_fifo_head->data = data; + } else { + i2c_fifo_head->callback = 0; + i2c_fifo_head->data = 0; + } + + list_add_tail(&i2c_fifo_head->busfifohead, &wmt_i2c_fifohead); + if (pmsg->flags & I2C_M_RD) { + i2c_fifo_head->xfer_length = 1; + ret = i2c_wmt_read_buf(pmsg->addr, pmsg->buf, pmsg->len, restart, last); + } else { + i2c_fifo_head->xfer_length = 1; + if (pmsg->flags & I2C_M_NOSTART) + i2c_fifo_head->restart = 1; + else + i2c_fifo_head->restart = 0; + ret = i2c_wmt_write_buf(pmsg->addr, pmsg->buf, pmsg->len, restart, last); + } + + } else { + i2c_fifo_head->xfer_length = 0; + i2c_fifo_head->xfer_msgnum = 0; + i2c_fifo_head->restart = 0; + i2c_fifo_head->non_block = non_block; + if (non_block == 1) { + i2c_fifo_head->callback = callback; + i2c_fifo_head->data = data; + } else { + i2c_fifo_head->callback = 0; + i2c_fifo_head->data = 0; + } + list_add_tail(&i2c_fifo_head->busfifohead, &wmt_i2c_fifohead); + } + spin_unlock_irqrestore(&i2c_fifolock, flags); + if (non_block == 0) { + wait_event(i2c_wait, i2c.isr_int_pending); +#if 0 + if(wait_event_timeout(i2c_wait, i2c.isr_int_pending, HZ) == 0) { + fifo_head = list_first_entry(&wmt_i2c_fifohead, struct wmt_i2cbusfifo, busfifohead); + if( fifo_head ){ + list_del(&fifo_head->busfifohead);/*del request*/ + kfree(fifo_head); + fifo_head = NULL; + } + i2c.regs->cr_reg = 0 ; + i2c.regs->cr_reg |= I2C_CR_ENABLE ; + return -ETIMEDOUT; + } +#endif + ret = msg_num; + if (i2c.isr_nack == 1) { + DPRINTK("i2c_err : write NACK error (rx) \n\r") ; + ret = -EIO ; + } + if (i2c.isr_timeout == 1) { + DPRINTK("i2c_err : write SCL timeout error (rx)\n\r") ; + ret = -ETIMEDOUT ; + } + + } + + return ret; + + +} +static int i2c_wmt_read_buf( + unsigned int slave_addr, + char *buf, + unsigned int length, + int restart, + int last +) +{ + unsigned short tcr_value; + int ret = 0; + + DPRINTK("[%s]:length = %d , slave_addr = %x\n", __func__, length , slave_addr); + + if (length <=0) + return -1; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ + if (length <=0) + return -1; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + tcr_value = 0 ; + + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + if (length == 1) + i2c.regs->cr_reg |= I2C_CR_TX_NEXT_NO_ACK; /*only 8-bit to read*/ + + i2c.regs->tcr_reg = tcr_value ; + return ret; +} + +static int i2c_wmt_write_buf( + unsigned int slave_addr, + char *buf, + unsigned int length, + int restart, + int last +) +{ + unsigned short tcr_value ; + unsigned int xfer_length ; + int ret = 0 ; + + DPRINTK("[%s]length = %d , slave_addr = %x\n", __func__, length , slave_addr); + if (slave_addr == WMT_I2C_API_I2C_ADDR) + return ret ; + + if (is_master == 0) + return -ENXIO; + + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length < 0) + return -1 ; + xfer_length = 0 ; /* for array index and also for checking counting*/ + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ + + if (length == 0) + i2c.regs->cdr_reg = 0 ; + else + i2c.regs->cdr_reg = (unsigned short)(buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + i2c.regs->tcr_reg = tcr_value ; + + ret = 0 ; + return ret; + +} +static int i2c_wmt_read_msg( + unsigned int slave_addr, /*!<; //[IN] Salve address */ + char *buf, /*!<; //[OUT] Pointer to data */ + unsigned int length, /*!<; //Data length */ + int restart, /*!<; //Need to restart after a complete read */ + int last /*!<; //Last read */ +) +{ + unsigned short tcr_value ; + unsigned int xfer_length ; + int is_timeout ; + int ret = 0 ; + int wait_event_result = 0 ; + + if (is_master == 0) + return -ENXIO; + if (length <= 0) + return -1 ; + xfer_length = 0 ; + + if (restart == 0) + ret = i2c_wmt_wait_bus_not_busy() ; + if (ret < 0) + return ret ; + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ + if (restart == 0) + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) { + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + } else if (i2c.i2c_mode == I2C_FAST_MODE) { + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + } + if (length == 1) + i2c.regs->cr_reg |= I2C_CR_TX_NEXT_NO_ACK; /*only 8-bit to read*/ + + i2c.regs->tcr_reg = tcr_value ; + + /*repeat start case*/ + if (restart == 1) + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + + ret = 0 ; + for (; ;) { + is_timeout = 0 ; + wait_event_result = wait_event_interruptible_timeout(i2c_wait, i2c.isr_int_pending , + (MAX_RX_TIMEOUT * HZ / 1000)) ; + if (likely(wait_event_result > 0)) { + DPRINTK("I2C: wait interrupted (rx) \n"); + ret = 0 ; + } else if (likely(i2c.isr_int_pending == 0)) { + DPRINTK("I2C: wait timeout (rx) \n"); + is_timeout = 1 ; + ret = -ETIMEDOUT ; + } + + /**/ + /* fail case*/ + /**/ + if (i2c.isr_nack == 1) { + DPRINTK("i2c_err : write NACK error (rx) \n\r") ; + ret = -EIO ; + break ; + } + if (i2c.isr_timeout == 1) { + DPRINTK("i2c_err : write SCL timeout error (rx)\n\r") ; + msleep(10); + ret = -ETIMEDOUT ; + break ; + } + if (is_timeout == 1) { + DPRINTK("i2c_err: write software timeout error (rx) \n\r") ; + ret = -ETIMEDOUT ; + break ; + } + + + /**/ + /* pass case*/ + /**/ + if (i2c.isr_byte_end == 1) { + buf[xfer_length] = (i2c.regs->cdr_reg >> 8) ; + ++xfer_length ; + DPRINTK("i2c_test: received BYTE_END\n\r"); + } + i2c.isr_int_pending = 0; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + + if (length > xfer_length) { + if ((length - 1) == xfer_length) { /* next read is the last one*/ + i2c.regs->cr_reg |= (I2C_CR_TX_NEXT_NO_ACK | I2C_CR_CPU_RDY); + DPRINTK("i2c_test: set CPU_RDY & TX_ACK. next data is last.\r\n"); + } else { + i2c.regs->cr_reg |= I2C_CR_CPU_RDY ; + DPRINTK("i2c_test: more data to read. only set CPU_RDY. \r\n"); + } + } else if (length == xfer_length) { /* end rx xfer*/ + if (last == 1) { /* stop case*/ + DPRINTK("i2c_test: read completed \r\n"); + break ; + } else { /* restart case*/ + /* ??? how to handle the restart after read ?*/ + DPRINTK("i2c_test: RX ReStart Case \r\n") ; + break ; + } + } else { + DPRINTK("i2c_err : read known error\n\r") ; + ret = -EIO ; + break ; + } + } + + DPRINTK("i2c_test: read sequence completed\n\r"); + return ret ; +} + +static int i2c_wmt_write_msg( + unsigned int slave_addr, /*!<; //[IN] Salve address */ + char *buf, /*!<; //[OUT] Pointer to data */ + unsigned int length, /*!<; //Data length */ + int restart, /*!<; //Need to restart after a complete write */ + int last /*!<; //Last read */ +) +{ + unsigned short tcr_value ; + unsigned int xfer_length ; + int is_timeout ; + int ret = 0 ; + int wait_event_result ; + + DPRINTK("length = %d , slave_addr = %x\n", length , slave_addr); + if (slave_addr == WMT_I2C_API_I2C_ADDR) + return ret ; + + if (is_master == 0) + return -ENXIO; + + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length < 0) + return -1 ; + xfer_length = 0 ; /* for array index and also for checking counting*/ + if (restart == 0) + ret = i2c_wmt_wait_bus_not_busy() ; + if (ret < 0) + return ret ; + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + + /**/ + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length == 0) + i2c.regs->cdr_reg = 0 ; + else + i2c.regs->cdr_reg = (unsigned short)(buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + + if (restart == 0) { + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + } + + /**/ + /* I2C: Set transfer mode [standard/fast]*/ + /**/ + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + i2c.regs->tcr_reg = tcr_value ; + + if (restart == 1) + i2c.regs->cr_reg |= I2C_CR_CPU_RDY ; + + ret = 0 ; + for (; ;) { + + is_timeout = 0 ; + /**/ + /* I2C: Wait for interrupt. if ( i2c.isr_int_pending == 1 ) ==> an interrupt exsits.*/ + /**/ + wait_event_result = wait_event_interruptible_timeout(i2c_wait, i2c.isr_int_pending , (MAX_TX_TIMEOUT * HZ / 1000)) ; + + if (likely(wait_event_result > 0)) { + DPRINTK("I2C: wait interrupted (tx)\n"); + ret = 0 ; + } else if (likely(i2c.isr_int_pending == 0)) { + DPRINTK("I2C: wait timeout (tx) \n"); + is_timeout = 1 ; + ret = -ETIMEDOUT ; + } + + /**/ + /* fail case*/ + /**/ + if (i2c.isr_nack == 1) { + DPRINTK("i2c_err : write NACK error (tx) \n\r") ; + ret = -EIO ; + break ; + } + if (i2c.isr_timeout == 1) { + DPRINTK("i2c_err : write SCL timeout error (tx)\n\r") ; + msleep(10); + ret = -ETIMEDOUT ; + break ; + } + if (is_timeout == 1) { + DPRINTK("i2c_err : write software timeout error (tx)\n\r") ; + ret = -ETIMEDOUT ; + break ; + } + + /**/ + /* pass case*/ + /**/ + if (i2c.isr_byte_end == 1) { + DPRINTK("i2c: isr end byte (tx)\n\r") ; + ++xfer_length ; + } + i2c.isr_int_pending = 0 ; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + + + if ((i2c.regs->csr_reg & I2C_CSR_RCV_ACK_MASK) == I2C_CSR_RCV_NOT_ACK) { + DPRINTK("i2c_err : write RCV NACK error\n\r") ; + ret = -EIO ; + break ; + } + + /**/ + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length == 0) { + i2c.regs->cr_reg = (I2C_CR_TX_END|I2C_CR_CPU_RDY|I2C_CR_ENABLE) ; + break ; + } + if (length > xfer_length) { + i2c.regs->cdr_reg = (unsigned short) (buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + i2c.regs->cr_reg = (I2C_CR_CPU_RDY | I2C_CR_ENABLE) ; + DPRINTK("i2c_test: write register data \n\r") ; + } else if (length == xfer_length) { /* end tx xfer*/ + if (last == 1) { /* stop case*/ + i2c.regs->cr_reg = (I2C_CR_TX_END|I2C_CR_CPU_RDY|I2C_CR_ENABLE) ; + DPRINTK("i2c_test: finish write \n\r") ; + break ; + } else { /* restart case*/ + /* handle the restart for first write then the next is read*/ + i2c.regs->cr_reg = (I2C_CR_ENABLE) ; + DPRINTK("i2c_test: tx restart Case \n\r") ; + break ; + } + } else { + DPRINTK("i2c_err : write unknown error\n\r") ; + ret = -EIO ; + break ; + } + } ; + + DPRINTK("i2c_test: write sequence completed\n\r"); + + return ret ; +} + +static int i2c_wmt_wait_bus_not_busy(void) +{ + int ret ; + int cnt ; + + ret = 0 ; + cnt = 0 ; + while (1) { + if ((REG16_VAL(I2C1_CSR_ADDR) & I2C_STATUS_MASK) == I2C_READY) { + ret = 0; + break ; + } + cnt++ ; + + if (cnt > MAX_BUS_READY_CNT) { + ret = (-EBUSY) ; + printk("i2c_err : wait but not ready time-out\n\r") ; + cnt = 0; + break; + } + } + return ret ; +} + +static int i2c_wmt_wait_bus_release(void) +{ + int val, cnt = 0; + while(1) { + val = REG8_VAL(__GPIO_BASE + 0x0011); + if (0x0c == (val & 0x0c)) + return 0; + udelay(1); + cnt ++; + if (cnt > 5000) { + return -ETIMEDOUT; + } + } + } + +static void i2c_wmt_reset(void) +{ + unsigned short tmp ; + if (is_master == 0) + return; + + /**/ + /* software initial*/ + /**/ + i2c.regs = (struct i2c_regs_s *)I2C1_BASE_ADDR ; + i2c.irq_no = IRQ_I2C1 ; + if (speed_mode == 0) + i2c.i2c_mode = I2C_STANDARD_MODE ; + else + i2c.i2c_mode = I2C_FAST_MODE ; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + + /**/ + /* hardware initial*/ + /**/ + + i2c.regs->cr_reg = 0 ; + i2c.regs->div_reg = APB_96M_I2C_DIV ; + i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ + i2c.regs->imr_reg = I2C_IMR_ALL_ENABLE ; /* 0x0007*/ + + i2c.regs->cr_reg = I2C_CR_ENABLE ; + tmp = i2c.regs->csr_reg ; /* read clear*/ + i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ + + if (i2c.i2c_mode == I2C_STANDARD_MODE) + i2c.regs->tr_reg = I2C_TR_STD_VALUE ; /* 0x8041*/ + else if (i2c.i2c_mode == I2C_FAST_MODE) + i2c.regs->tr_reg = I2C_TR_FAST_VALUE ; /* 0x8011*/ + + DPRINTK("Resetting I2C Controller Unit\n"); + + return ; +} +static int wmt_i2c_transfer_msg(struct wmt_i2cbusfifo *fifo_head) +{ + int xfer_length = fifo_head->xfer_length; + int xfer_msgnum = fifo_head->xfer_msgnum; + struct i2c_msg *pmsg = &fifo_head->msg[xfer_msgnum]; + int restart = fifo_head->restart; + unsigned short tcr_value; + unsigned short slave_addr = pmsg->addr; + int length = pmsg->len; + int ret = 0; + + if (pmsg->flags & I2C_M_RD) { + if (restart == 0) + i2c_wmt_wait_bus_not_busy(); + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ + if (restart == 0) + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) { + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + } else if (i2c.i2c_mode == I2C_FAST_MODE) { + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + } + if (length == 1) + i2c.regs->cr_reg |= I2C_CR_TX_NEXT_NO_ACK; /*only 8-bit to read*/ + + i2c.regs->tcr_reg = tcr_value ; + + /*repeat start case*/ + if (restart == 1) + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + } else { + if (restart == 0) + i2c_wmt_wait_bus_not_busy(); + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + /**/ + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length == 0) + i2c.regs->cdr_reg = 0 ; + else + i2c.regs->cdr_reg = (unsigned short)(pmsg->buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + + if (restart == 0) { + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + } + + /**/ + /* I2C: Set transfer mode [standard/fast]*/ + /**/ + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + i2c.regs->tcr_reg = tcr_value ; + + if (restart == 1) + i2c.regs->cr_reg |= I2C_CR_CPU_RDY ; + } + return ret; +} + +static irqreturn_t i2c_wmt_handler( + int this_irq, /*!<; //[IN] IRQ number */ + void *dev_id /*!<; //[IN] Pointer to device ID */ +) +{ + int wakeup ; + unsigned short isr_status ; + unsigned short tmp ; + unsigned long flags; + struct wmt_i2cbusfifo *fifo_head; + int xfer_length = 0; + int xfer_msgnum = 0; + struct i2c_msg *pmsg; + volatile unsigned short csr_reg; + + spin_lock_irqsave(&i2c_wmt_irqlock, flags); + isr_status = i2c.regs->isr_reg ; + csr_reg = i2c.regs->csr_reg; + wakeup = 0 ; + fifo_head = list_first_entry(&wmt_i2c_fifohead, struct wmt_i2cbusfifo, busfifohead); + + if (isr_status & I2C_ISR_NACK_ADDR) { + DPRINTK("[%s]:i2c NACK\n", __func__); + /*spin_lock(&i2c_fifolock);*/ + list_del(&fifo_head->busfifohead);/*del request*/ + kfree(fifo_head); + /*spin_unlock(&i2c_fifolock);*/ + xfer_length = 0; + i2c.regs->isr_reg = I2C_ISR_NACK_ADDR_WRITE_CLEAR ; + tmp = i2c.regs->csr_reg ; /* read clear*/ + i2c.isr_nack = 1 ; + wakeup = 1 ; + } + + if ((isr_status & I2C_ISR_BYTE_END && ((csr_reg & I2C_CSR_RCV_ACK_MASK) == I2C_CSR_RCV_NOT_ACK))) { + /* + printk("data rcv nack\n"); + */ + list_del(&fifo_head->busfifohead);/*del request*/ + kfree(fifo_head); + xfer_length = 0; + i2c.regs->isr_reg = I2C_ISR_BYTE_END_WRITE_CLEAR ; + i2c.isr_nack = 1 ; + wakeup = 1 ; + } else if (isr_status & I2C_ISR_BYTE_END) { + i2c.regs->isr_reg = I2C_ISR_BYTE_END_WRITE_CLEAR ; + i2c.isr_byte_end = 1 ; + xfer_length = fifo_head->xfer_length; + xfer_msgnum = fifo_head->xfer_msgnum; + pmsg = &fifo_head->msg[xfer_msgnum]; + + /*read case*/ + if (pmsg->flags & I2C_M_RD) { + pmsg->buf[xfer_length - 1] = (i2c.regs->cdr_reg >> 8) ; + /*the last data in current msg?*/ + if (xfer_length == pmsg->len - 1) { + /*last msg of the current request?*/ + /*spin_lock(&i2c_fifolock);*/ + if (pmsg->flags & I2C_M_NOSTART) { + ++fifo_head->xfer_length; + fifo_head->restart = 1; + /* + ++fifo_head->xfer_msgnum; + */ + i2c.regs->cr_reg |= I2C_CR_CPU_RDY; + } else { + ++fifo_head->xfer_length; + fifo_head->restart = 0; + /* + ++fifo_head->xfer_msgnum; + */ + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY | I2C_CR_TX_NEXT_NO_ACK); + } + /*spin_unlock(&i2c_fifolock);*/ + } else if (xfer_length == pmsg->len) {/*next msg*/ + if (xfer_msgnum < fifo_head->msg_num - 1) { + /*spin_lock(&i2c_fifolock);*/ + fifo_head->xfer_length = 0; + ++fifo_head->xfer_msgnum; + + wmt_i2c_transfer_msg(fifo_head); + ++fifo_head->xfer_length; + /*spin_unlock(&i2c_fifolock);*/ + } else { /*data of this msg has been transfered*/ + /*spin_lock(&i2c_fifolock);*/ + list_del(&fifo_head->busfifohead);/*del request*/ + /*next request exist?*/ + if (list_empty(&wmt_i2c_fifohead)) {/*no more reqeust*/ + if (fifo_head->non_block == 0) { + wakeup = 1; + } else { + fifo_head->callback(fifo_head->data); + } + kfree(fifo_head); + } else { /*more request*/ + if (fifo_head->non_block == 0) { + wakeup = 1; + } else { + fifo_head->callback(fifo_head->data); + } + kfree(fifo_head); + fifo_head = list_first_entry(&wmt_i2c_fifohead, + struct wmt_i2cbusfifo, busfifohead); + /* + if (fifo_head->non_block == 0) + wakeup = 1; + */ + + fifo_head->xfer_length = 0; + wmt_i2c_transfer_msg(fifo_head); + ++fifo_head->xfer_length; + + /* + if (fifo_head->non_block == 0) { + printk("2 : non callback\n"); + wakeup = 1; + } else { + printk("2 :callback\n"); + fifo_head->callback(fifo_head->data); + } + */ + } + /*spin_unlock(&i2c_fifolock);*/ + } + } else {/*next data*/ + /*spin_lock(&i2c_fifolock);*/ + ++fifo_head->xfer_length; + /*spin_unlock(&i2c_fifolock);*/ + i2c.regs->cr_reg |= I2C_CR_CPU_RDY; + } + + } else { /*write case*/ + /*the last data in current msg?*/ + if (xfer_length == pmsg->len) { + /*last msg of the current request?*/ + if (xfer_msgnum < fifo_head->msg_num - 1) { + /*spin_lock(&i2c_fifolock);*/ + if (pmsg->flags & I2C_M_NOSTART) { + ++fifo_head->xfer_length; + fifo_head->restart = 1; + } else { + ++fifo_head->xfer_length; + fifo_head->restart = 0; + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); + udelay(2); + i2c.regs->cr_reg |= (I2C_CR_TX_END); + } + /*access next msg*/ + fifo_head->xfer_length = 0; + ++fifo_head->xfer_msgnum; + + wmt_i2c_transfer_msg(fifo_head); + ++fifo_head->xfer_length; + /*spin_unlock(&i2c_fifolock);*/ + } else {/*this request finish*/ + /*spin_lock(&i2c_fifolock);*/ + /*next request exist?*/ + list_del(&fifo_head->busfifohead);/*del request*/ + if (list_empty(&wmt_i2c_fifohead)) { + /*kfree(fifo_head);*/ + /* + if (fifo_head->non_block == 0) + wakeup = 1; + */ + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); + udelay(2); + i2c.regs->cr_reg |= (I2C_CR_TX_END); + if (fifo_head->non_block == 0) { + wakeup = 1; + } else { + fifo_head->callback(fifo_head->data); + } + kfree(fifo_head); + + } else { + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); + udelay(2); + i2c.regs->cr_reg |= (I2C_CR_TX_END); + if (fifo_head->non_block == 0) { + wakeup = 1; + } else { + fifo_head->callback(fifo_head->data); + } + kfree(fifo_head); + fifo_head = list_first_entry(&wmt_i2c_fifohead, + struct wmt_i2cbusfifo, busfifohead); + /* + if (fifo_head->non_block == 0) + wakeup = 1; + */ + + /*next msg*/ + fifo_head->xfer_length = 0; + ++fifo_head->xfer_msgnum; + wmt_i2c_transfer_msg(fifo_head); + ++fifo_head->xfer_length; + /* + if (fifo_head->non_block == 0) { + printk("4:non callback\n"); + wakeup = 1; + } else { + printk("4:callback\n"); + fifo_head->callback(fifo_head->data); + } + */ + } + /*spin_unlock(&i2c_fifolock);*/ + } + } else {/*next data*/ + i2c.regs->cdr_reg = (unsigned short) (pmsg->buf[fifo_head->xfer_length] & I2C_CDR_DATA_WRITE_MASK); + /*spin_lock(&i2c_fifolock);*/ + ++fifo_head->xfer_length; + /*spin_unlock(&i2c_fifolock);*/ + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY | I2C_CR_ENABLE); + } + } + } + + if (isr_status & I2C_ISR_SCL_TIME_OUT) { + printk("[%s]I2C1 SCL timeout\n", __func__); +#if 0 + i2c.regs->cr_reg |= BIT7;/*reset status*/ + /*spin_lock(&i2c_fifolock);*/ + list_del(&fifo_head->busfifohead);/*del request*/ + /*spin_unlock(&i2c_fifolock);*/ + xfer_length = 0; + i2c.regs->isr_reg = I2C_ISR_SCL_TIME_OUT_WRITE_CLEAR | I2C_ISR_BYTE_END_WRITE_CLEAR; + i2c.isr_timeout = 1 ; + wakeup = 1; +#endif + i2c.regs->isr_reg = I2C_ISR_SCL_TIME_OUT_WRITE_CLEAR ; + } + + + if (wakeup) { + /*spin_lock_irqsave(&i2c_wmt_irqlock, flags);*/ + i2c.isr_int_pending = 1; + /*spin_unlock_irqrestore(&i2c_wmt_irqlock, flags);*/ + wake_up(&i2c_wait); + } else + DPRINTK("i2c_err : unknown I2C ISR Handle 0x%4.4X" , isr_status) ; + spin_unlock_irqrestore(&i2c_wmt_irqlock, flags); + return IRQ_HANDLED; +} + +static int i2c_wmt_resource_init(void) +{ + if (is_master == 0) + return 0; + if (request_irq(i2c.irq_no , &i2c_wmt_handler, IRQF_DISABLED, "i2c", 0) < 0) { + DPRINTK(KERN_INFO "I2C: Failed to register I2C irq %i\n", i2c.irq_no); + return -ENODEV; + } + return 0; +} + +static void i2c_wmt_resource_release(void) +{ + if (is_master == 0) + return; + free_irq(i2c.irq_no, 0); +} + +static struct i2c_algo_wmt_data i2c_wmt_data = { + write_msg: i2c_wmt_write_msg, + read_msg: i2c_wmt_read_msg, + send_request: i2c_send_request, + wait_bus_not_busy: i2c_wmt_wait_bus_not_busy, + reset: i2c_wmt_reset, + set_mode: i2c_wmt_set_mode, + udelay: I2C_ALGO_UDELAY, + timeout: I2C_ALGO_TIMEOUT, +}; + +static struct i2c_adapter i2c_wmt_ops = { + .owner = THIS_MODULE, + /* + .id = I2C_ALGO_WMT, + */ + .algo_data = &i2c_wmt_data, + .name = "wmt_i2c_adapter1", + .retries = I2C_ADAPTER_RETRIES, + .nr = 1, +}; + +#ifdef CONFIG_PM +static struct i2c_regs_s wmt_i2c_reg ; +static void i2c_shutdown(void) +{ + printk("i2c1 shutdown\n"); + wmt_i2c1_power_state = 2; + while (!list_empty(&wmt_i2c_fifohead)) + msleep(1); + while (1) {/*wait busy clear*/ + if ((REG16_VAL(I2C1_CSR_ADDR) & I2C_STATUS_MASK) == I2C_READY) + break ; + msleep(1); + } + return; +} +static int i2c_suspend(void) +{ + printk("i2c1 suspend\n"); + wmt_i2c_reg.imr_reg = i2c.regs->imr_reg; + wmt_i2c_reg.tr_reg = i2c.regs->tr_reg; + wmt_i2c_reg.div_reg = i2c.regs->div_reg; + return 0; +} +static void i2c_resume(void) +{ + printk("i2c1 resume\n"); + GPIO_CTRL_GP17_I2C_BYTE_VAL &= ~(BIT2 | BIT3); + PULL_EN_GP17_I2C_BYTE_VAL |= (BIT2 | BIT3); + PULL_CTRL_GP17_I2C_BYTE_VAL |= (BIT2 | BIT3); + auto_pll_divisor(DEV_I2C1, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_I2C1, SET_DIV, 2, 20);/*20M Hz*/ + + i2c.regs->cr_reg = 0 ; + i2c.regs->div_reg = wmt_i2c_reg.div_reg; + i2c.regs->imr_reg = wmt_i2c_reg.imr_reg; + i2c.regs->tr_reg = wmt_i2c_reg.tr_reg ; + i2c.regs->cr_reg = 0x001 ; +} +#else +#define i2c_suspend NULL +#define i2c_resume NULL +#define i2c_shutdown NULL +#endif +extern int wmt_i2c_add_bus(struct i2c_adapter *); +extern int wmt_i2c_del_bus(struct i2c_adapter *); + +#ifdef CONFIG_PM +static struct syscore_ops wmt_i2c_syscore_ops = +{ + .suspend = i2c_suspend, + .resume = i2c_resume, + .shutdown = i2c_shutdown, +}; +#endif + +static int __init i2c_adap_wmt_init(void) +{ + unsigned short tmp ; + char varname[] = "wmt.i2c.param"; +#ifdef CONFIG_I2C_SLAVE_WMT + char varname1[] = "wmt.bus.i2c.slave_port"; +#endif + unsigned char buf[80]; + int ret; + unsigned int port_num; + int idx = 0; + int varlen = 80; + unsigned int pllb_freq = 0; + unsigned int tr_val = 0; + +#ifdef CONFIG_I2C_SLAVE_WMT +#ifdef USE_UBOOT_PARA + ret = wmt_getsyspara(varname1, buf, &varlen); +#else + ret = 1; +#endif + is_master = 1; + if (ret == 0) { + ret = sscanf(buf, "%x", &port_num); + while (ret) { + if (port_num != 1) + is_master = 1; + else { + is_master = 0; + break; + } + idx += ret; + ret = sscanf(buf + idx, ",%x", &port_num); + } + } else + is_master = 1; +#endif + wmt_i2c1_is_master = is_master; + if (is_master == 1) { +#ifdef USE_UBOOT_PARA + ret = wmt_getsyspara(varname, buf, &varlen); +#else + ret = 1; +#endif + + if (ret == 0) { + ret = sscanf(buf, "%x:%x", &port_num, &speed_mode); + idx += 3; + while (ret) { + if (ret < 2) + speed_mode = 0; + else { + if (port_num != 1) + speed_mode = 0; + else + break; + } + ret = sscanf(buf + idx, ",%x:%x", &port_num, &speed_mode); + idx += 4; + } + } + if (speed_mode > 1) + speed_mode = 0; + wmt_i2c1_speed_mode = speed_mode; + + /**/ + /* software initial*/ + /**/ + i2c.regs = (struct i2c_regs_s *)I2C1_BASE_ADDR ; + i2c.irq_no = IRQ_I2C1 ; + printk("PORT 1 speed_mode = %d\n", speed_mode); + if (speed_mode == 0) + i2c.i2c_mode = I2C_STANDARD_MODE ; + else if (speed_mode == 1) + i2c.i2c_mode = I2C_FAST_MODE ; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + /**/ + /* hardware initial*/ + /**/ + auto_pll_divisor(DEV_I2C1, CLK_ENABLE, 0, 0); + pllb_freq = auto_pll_divisor(DEV_I2C1, SET_DIV, 2, 20);/*20M Hz*/ + if ((pllb_freq%(1000*2*100)) != 0) + tr_val = pllb_freq/(1000*2*100) + 1; + else + tr_val = pllb_freq/(1000*2*100); + *(volatile unsigned char *)CTRL_GPIO &= ~(BIT2 | BIT3); + *(volatile unsigned char *)PU_EN_GPIO |= (BIT2 | BIT3); + *(volatile unsigned char *)PU_CTRL_GPIO |= (BIT2 | BIT3); + i2c.regs->cr_reg = 0 ; + i2c.regs->div_reg = APB_96M_I2C_DIV ; + i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ + i2c.regs->imr_reg = I2C_IMR_ALL_ENABLE ; /* 0x0007*/ + + i2c.regs->cr_reg = I2C_CR_ENABLE ; + tmp = i2c.regs->csr_reg ; /* read clear*/ + i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ + + if (i2c.i2c_mode == I2C_STANDARD_MODE) + i2c.regs->tr_reg = 0xff00|tr_val; + else if (i2c.i2c_mode == I2C_FAST_MODE) { + tr_val /= 4; + i2c.regs->tr_reg = 0xff00|tr_val ; + } + } + + + if (i2c_wmt_resource_init() == 0) { + if (wmt_i2c_add_bus(&i2c_wmt_ops) < 0) { + i2c_wmt_resource_release(); + printk(KERN_INFO "i2c: Failed to add bus\n"); + return -ENODEV; + } + } else + return -ENODEV; + + INIT_LIST_HEAD(&wmt_i2c_fifohead); + +#ifdef CONFIG_PM + register_syscore_ops(&wmt_i2c_syscore_ops); +#endif + + wmt_i2c1_power_state = 0; + printk(KERN_INFO "i2c: successfully added bus\n"); + +#ifdef I2C_REG_TEST + printk("i2c.regs->cr_reg= 0x%08x\n\r", i2c.regs->cr_reg); + printk("i2c.regs->tcr_reg= 0x%08x\n\r", i2c.regs->tcr_reg); + printk("i2c.regs->csr_reg= 0x%08x\n\r", i2c.regs->csr_reg); + printk("i2c.regs->isr_reg= 0x%08x\n\r", i2c.regs->isr_reg); + printk("i2c.regs->imr_reg= 0x%08x\n\r", i2c.regs->imr_reg); + printk("i2c.regs->cdr_reg= 0x%08x\n\r", i2c.regs->cdr_reg); + printk("i2c.regs->tr_reg= 0x%08x\n\r", i2c.regs->tr_reg); + printk("i2c.regs->div_reg= 0x%08x\n\r", i2c.regs->div_reg); +#endif + + return 0; +} +subsys_initcall(i2c_adap_wmt_init); + +static void i2c_adap_wmt_exit(void) +{ + wmt_i2c_del_bus(&i2c_wmt_ops); + i2c_wmt_resource_release(); + + printk(KERN_INFO "i2c: successfully removed bus\n"); +} + + +MODULE_AUTHOR("WonderMedia Technologies, Inc."); +MODULE_DESCRIPTION("WMT I2C Adapter Driver"); +MODULE_LICENSE("GPL"); + +module_exit(i2c_adap_wmt_exit); + diff --git a/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-bus-2.c b/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-bus-2.c new file mode 100755 index 00000000..64f5646c --- /dev/null +++ b/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-bus-2.c @@ -0,0 +1,1253 @@ +/*++ + drivers/i2c/busses/wmt-i2c-bus-2.c + + Copyright (c) 2013 WonderMedia Technologies, Inc. + + 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, see <http://www.gnu.org/licenses/>. + + WonderMedia Technologies, Inc. + 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. +--*/ +/* Include your headers here*/ +#include <linux/kernel.h> +#include <linux/module.h> + +#include <linux/i2c.h> +/* +#include <linux/i2c-id.h> +*/ +#include <linux/init.h> +#include <linux/time.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/interrupt.h> + +#include <mach/hardware.h> +#include <asm/irq.h> +#include <mach/irqs.h> +#include <mach/wmt-i2c-bus.h> +#include <linux/slab.h> +#include <linux/pm.h> +#include <linux/syscore_ops.h> + +#ifdef __KERNEL__ + +#ifdef DEBUG + #define DPRINTK printk +#else + #define DPRINTK(x...) +#endif + +#else + #define DPRINTK printf + +#endif + + +#define MAX_BUS_READY_CNT 50 /* jiffy*/ +#define MAX_TX_TIMEOUT 500 /* ms*/ +#define MAX_RX_TIMEOUT 500 /* ms*/ +#define CTRL_GPIO GPIO_CTRL_GP17_I2C_BYTE_ADDR +#define PU_EN_GPIO PULL_EN_GP17_I2C_BYTE_ADDR +#define PU_CTRL_GPIO PULL_CTRL_GP17_I2C_BYTE_ADDR + +#define USE_UBOOT_PARA + +struct wmt_i2c_s { + struct i2c_regs_s *regs; + int irq_no ; + enum i2c_mode_e i2c_mode ; + int volatile isr_nack ; + int volatile isr_byte_end ; + int volatile isr_timeout ; + int volatile isr_int_pending ; +}; + +static int i2c_wmt_wait_bus_not_busy(void); +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +static unsigned int speed_mode = 1; +static unsigned int is_master = 1;/*master:1, slave:0*/ +unsigned int wmt_i2c2_is_master = 1; +unsigned int wmt_i2c2_speed_mode = 0; +static unsigned int wmt_i2c2_power_state = 0;/*0:power on, 1:suspend, 2:shutdown*/ +EXPORT_SYMBOL(wmt_i2c2_is_master); + +/**/ +/* variable*/ +/*-------------------------------------------------*/ +static volatile struct wmt_i2c_s i2c ; + +DECLARE_WAIT_QUEUE_HEAD(i2c2_wait); +/* +spinlock_t i2c2_wmt_irqlock = SPIN_LOCK_UNLOCKED; +*/ +static DEFINE_SPINLOCK(i2c2_wmt_irqlock); +static struct list_head wmt_i2c_fifohead; +/* +static spinlock_t i2c_fifolock = SPIN_LOCK_UNLOCKED; +*/ +static DEFINE_SPINLOCK(i2c_fifolock); +static int i2c_wmt_read_buf( + unsigned int slave_addr, + char *buf, + unsigned int length, + int restart, + int last +); +static int i2c_wmt_write_buf( + unsigned int slave_addr, + char *buf, + unsigned int length, + int restart, + int last +); + +static void i2c_wmt_set_mode(enum i2c_mode_e mode /*!<; //[IN] mode */) +{ + if (is_master == 0) + return; + i2c.i2c_mode = mode ; + + if (i2c.i2c_mode == I2C_STANDARD_MODE) { + DPRINTK("I2C: set standard mode \n"); + i2c.regs->tr_reg = I2C_TR_STD_VALUE ; /* 0x8041*/ + } else if (i2c.i2c_mode == I2C_FAST_MODE) { + DPRINTK("I2C: set fast mode \n"); + i2c.regs->tr_reg = I2C_TR_FAST_VALUE ; /* 0x8011*/ + } +} + + +static int i2c_send_request( + struct i2c_msg *msg, + int msg_num, + int non_block, + void (*callback)(void *data), + void *data +) +{ + struct wmt_i2cbusfifo *i2c_fifo_head; + struct i2c_msg *pmsg = NULL; + int ret = 0; + int restart = 0; + int last = 0; + unsigned long flags; + int slave_addr = msg[0].addr; + + if (slave_addr == WMT_I2C_API_I2C_ADDR) + return ret ; + if (wmt_i2c2_power_state == 2) { + printk("I2C2 has been shutdown\n"); + return -EIO; + } + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + + i2c_fifo_head = kzalloc(sizeof(struct wmt_i2cbusfifo), GFP_ATOMIC); + INIT_LIST_HEAD(&i2c_fifo_head->busfifohead); + + pmsg = &msg[0]; + i2c_fifo_head->msg = pmsg; + i2c_fifo_head->msg_num = msg_num; + + spin_lock_irqsave(&i2c_fifolock, flags); + if (list_empty(&wmt_i2c_fifohead)) { + i2c_wmt_wait_bus_not_busy(); + pmsg = &msg[0]; + i2c_fifo_head->xfer_length = 1; + i2c_fifo_head->xfer_msgnum = 0; + i2c_fifo_head->restart = 0; + i2c_fifo_head->non_block = non_block; + + if (non_block == 1) { + i2c_fifo_head->callback = callback; + i2c_fifo_head->data = data; + } else { + i2c_fifo_head->callback = 0; + i2c_fifo_head->data = 0; + } + + list_add_tail(&i2c_fifo_head->busfifohead, &wmt_i2c_fifohead); + if (pmsg->flags & I2C_M_RD) { + i2c_fifo_head->xfer_length = 1; + ret = i2c_wmt_read_buf(pmsg->addr, pmsg->buf, pmsg->len, restart, last); + } else { + i2c_fifo_head->xfer_length = 1; + if (pmsg->flags & I2C_M_NOSTART) + i2c_fifo_head->restart = 1; + else + i2c_fifo_head->restart = 0; + ret = i2c_wmt_write_buf(pmsg->addr, pmsg->buf, pmsg->len, restart, last); + } + + } else { + i2c_fifo_head->xfer_length = 0; + i2c_fifo_head->xfer_msgnum = 0; + i2c_fifo_head->restart = 0; + i2c_fifo_head->non_block = non_block; + if (non_block == 1) { + i2c_fifo_head->callback = callback; + i2c_fifo_head->data = data; + } else { + i2c_fifo_head->callback = 0; + i2c_fifo_head->data = 0; + } + list_add_tail(&i2c_fifo_head->busfifohead, &wmt_i2c_fifohead); + } + spin_unlock_irqrestore(&i2c_fifolock, flags); + if (non_block == 0) { + wait_event(i2c2_wait, i2c.isr_int_pending); + ret = msg_num; + if (i2c.isr_nack == 1) { + DPRINTK("i2c_err : write NACK error (rx) \n\r") ; + ret = -EIO ; + } + if (i2c.isr_timeout == 1) { + DPRINTK("i2c_err : write SCL timeout error (rx)\n\r") ; + ret = -ETIMEDOUT ; + } + + } + + return ret; + + +} +static int i2c_wmt_read_buf( + unsigned int slave_addr, + char *buf, + unsigned int length, + int restart, + int last +) +{ + unsigned short tcr_value; + int ret = 0; + + DPRINTK("[%s]:length = %d , slave_addr = %x\n", __func__, length , slave_addr); + + if (length <=0) + return -1; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ + if (length <=0) + return -1; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + tcr_value = 0 ; + + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + if (length == 1) + i2c.regs->cr_reg |= I2C_CR_TX_NEXT_NO_ACK; /*only 8-bit to read*/ + + i2c.regs->tcr_reg = tcr_value ; + return ret; +} + +static int i2c_wmt_write_buf( + unsigned int slave_addr, + char *buf, + unsigned int length, + int restart, + int last +) +{ + unsigned short tcr_value ; + unsigned int xfer_length ; + int ret = 0 ; + + DPRINTK("[%s]length = %d , slave_addr = %x\n", __func__, length , slave_addr); + if (slave_addr == WMT_I2C_API_I2C_ADDR) + return ret ; + + if (is_master == 0) + return -ENXIO; + + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length < 0) + return -1 ; + xfer_length = 0 ; /* for array index and also for checking counting*/ + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ + + if (length == 0) + i2c.regs->cdr_reg = 0 ; + else + i2c.regs->cdr_reg = (unsigned short)(buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + i2c.regs->tcr_reg = tcr_value ; + + ret = 0 ; + return ret; + +} +static int i2c_wmt_read_msg( + unsigned int slave_addr, /*!<; //[IN] Salve address */ + char *buf, /*!<; //[OUT] Pointer to data */ + unsigned int length, /*!<; //Data length */ + int restart, /*!<; //Need to restart after a complete read */ + int last /*!<; //Last read */ +) +{ + unsigned short tcr_value ; + unsigned int xfer_length ; + int is_timeout ; + int ret = 0 ; + int wait_event_result = 0 ; + + if (is_master == 0) + return -ENXIO; + if (length <= 0) + return -1 ; + xfer_length = 0 ; + + if (restart == 0) + ret = i2c_wmt_wait_bus_not_busy() ; + if (ret < 0) + return ret ; + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ + if (restart == 0) + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) { + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + } else if (i2c.i2c_mode == I2C_FAST_MODE) { + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + } + if (length == 1) + i2c.regs->cr_reg |= I2C_CR_TX_NEXT_NO_ACK; /*only 8-bit to read*/ + + i2c.regs->tcr_reg = tcr_value ; + + /*repeat start case*/ + if (restart == 1) + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + + ret = 0 ; + for (; ;) { + is_timeout = 0 ; + wait_event_result = wait_event_interruptible_timeout(i2c2_wait, i2c.isr_int_pending , + (MAX_RX_TIMEOUT * HZ / 1000)) ; + if (likely(wait_event_result > 0)) { + DPRINTK("I2C: wait interrupted (rx) \n"); + ret = 0 ; + } else if (likely(i2c.isr_int_pending == 0)) { + DPRINTK("I2C: wait timeout (rx) \n"); + is_timeout = 1 ; + ret = -ETIMEDOUT ; + } + + /**/ + /* fail case*/ + /**/ + if (i2c.isr_nack == 1) { + DPRINTK("i2c_err : write NACK error (rx) \n\r") ; + ret = -EIO ; + break ; + } + if (i2c.isr_timeout == 1) { + DPRINTK("i2c_err : write SCL timeout error (rx)\n\r") ; + msleep(10); + ret = -ETIMEDOUT ; + break ; + } + if (is_timeout == 1) { + DPRINTK("i2c_err: write software timeout error (rx) \n\r") ; + ret = -ETIMEDOUT ; + break ; + } + + + /**/ + /* pass case*/ + /**/ + if (i2c.isr_byte_end == 1) { + buf[xfer_length] = (i2c.regs->cdr_reg >> 8) ; + ++xfer_length ; + DPRINTK("i2c_test: received BYTE_END\n\r"); + } + i2c.isr_int_pending = 0; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + + if (length > xfer_length) { + if ((length - 1) == xfer_length) { /* next read is the last one*/ + i2c.regs->cr_reg |= (I2C_CR_TX_NEXT_NO_ACK | I2C_CR_CPU_RDY); + DPRINTK("i2c_test: set CPU_RDY & TX_ACK. next data is last.\r\n"); + } else { + i2c.regs->cr_reg |= I2C_CR_CPU_RDY ; + DPRINTK("i2c_test: more data to read. only set CPU_RDY. \r\n"); + } + } else if (length == xfer_length) { /* end rx xfer*/ + if (last == 1) { /* stop case*/ + DPRINTK("i2c_test: read completed \r\n"); + break ; + } else { /* restart case*/ + /* ??? how to handle the restart after read ?*/ + DPRINTK("i2c_test: RX ReStart Case \r\n") ; + break ; + } + } else { + DPRINTK("i2c_err : read known error\n\r") ; + ret = -EIO ; + break ; + } + } + + DPRINTK("i2c_test: read sequence completed\n\r"); + return ret ; +} + +static int i2c_wmt_write_msg( + unsigned int slave_addr, /*!<; //[IN] Salve address */ + char *buf, /*!<; //[OUT] Pointer to data */ + unsigned int length, /*!<; //Data length */ + int restart, /*!<; //Need to restart after a complete write */ + int last /*!<; //Last read */ +) +{ + unsigned short tcr_value ; + unsigned int xfer_length ; + int is_timeout ; + int ret = 0 ; + int wait_event_result ; + + DPRINTK("length = %d , slave_addr = %x\n", length , slave_addr); + if (slave_addr == WMT_I2C_API_I2C_ADDR) + return ret ; + + if (is_master == 0) + return -ENXIO; + + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length < 0) + return -1 ; + xfer_length = 0 ; /* for array index and also for checking counting*/ + if (restart == 0) + ret = i2c_wmt_wait_bus_not_busy() ; + if (ret < 0) + return ret ; + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + + /**/ + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length == 0) + i2c.regs->cdr_reg = 0 ; + else + i2c.regs->cdr_reg = (unsigned short)(buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + + if (restart == 0) { + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + } + + /**/ + /* I2C: Set transfer mode [standard/fast]*/ + /**/ + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + i2c.regs->tcr_reg = tcr_value ; + + if (restart == 1) + i2c.regs->cr_reg |= I2C_CR_CPU_RDY ; + + ret = 0 ; + for (; ;) { + + is_timeout = 0 ; + /**/ + /* I2C: Wait for interrupt. if ( i2c.isr_int_pending == 1 ) ==> an interrupt exsits.*/ + /**/ + wait_event_result = wait_event_interruptible_timeout(i2c2_wait, i2c.isr_int_pending , (MAX_TX_TIMEOUT * HZ / 1000)) ; + + if (likely(wait_event_result > 0)) { + DPRINTK("I2C: wait interrupted (tx)\n"); + ret = 0 ; + } else if (likely(i2c.isr_int_pending == 0)) { + DPRINTK("I2C: wait timeout (tx) \n"); + is_timeout = 1 ; + ret = -ETIMEDOUT ; + } + + /**/ + /* fail case*/ + /**/ + if (i2c.isr_nack == 1) { + DPRINTK("i2c_err : write NACK error (tx) \n\r") ; + ret = -EIO ; + break ; + } + if (i2c.isr_timeout == 1) { + DPRINTK("i2c_err : write SCL timeout error (tx)\n\r") ; + msleep(10); + ret = -ETIMEDOUT ; + break ; + } + if (is_timeout == 1) { + DPRINTK("i2c_err : write software timeout error (tx)\n\r") ; + ret = -ETIMEDOUT ; + break ; + } + + /**/ + /* pass case*/ + /**/ + if (i2c.isr_byte_end == 1) { + DPRINTK("i2c: isr end byte (tx)\n\r") ; + ++xfer_length ; + } + i2c.isr_int_pending = 0 ; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + + + if ((i2c.regs->csr_reg & I2C_CSR_RCV_ACK_MASK) == I2C_CSR_RCV_NOT_ACK) { + DPRINTK("i2c_err : write RCV NACK error\n\r") ; + ret = -EIO ; + break ; + } + + /**/ + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length == 0) { + i2c.regs->cr_reg = (I2C_CR_TX_END|I2C_CR_CPU_RDY|I2C_CR_ENABLE) ; + break ; + } + if (length > xfer_length) { + i2c.regs->cdr_reg = (unsigned short) (buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + i2c.regs->cr_reg = (I2C_CR_CPU_RDY | I2C_CR_ENABLE) ; + DPRINTK("i2c_test: write register data \n\r") ; + } else if (length == xfer_length) { /* end tx xfer*/ + if (last == 1) { /* stop case*/ + i2c.regs->cr_reg = (I2C_CR_TX_END|I2C_CR_CPU_RDY|I2C_CR_ENABLE) ; + DPRINTK("i2c_test: finish write \n\r") ; + break ; + } else { /* restart case*/ + /* handle the restart for first write then the next is read*/ + i2c.regs->cr_reg = (I2C_CR_ENABLE) ; + DPRINTK("i2c_test: tx restart Case \n\r") ; + break ; + } + } else { + DPRINTK("i2c_err : write unknown error\n\r") ; + ret = -EIO ; + break ; + } + } ; + + DPRINTK("i2c_test: write sequence completed\n\r"); + + return ret ; +} + +static int i2c_wmt_wait_bus_not_busy(void) +{ + int ret ; + int cnt ; + + ret = 0 ; + cnt = 0 ; + while (1) { + if ((REG16_VAL(I2C2_CSR_ADDR) & I2C_STATUS_MASK) == I2C_READY) { + ret = 0; + break ; + } + cnt++ ; + + if (cnt > MAX_BUS_READY_CNT) { + ret = (-EBUSY) ; + printk("i2c_err 2: wait but not ready time-out\n\r") ; + cnt = 0; + break; + } + } + return ret ; +} + +static void i2c_wmt_reset(void) +{ + unsigned short tmp ; + if (is_master == 0) + return; + + /**/ + /* software initial*/ + /**/ + i2c.regs = (struct i2c_regs_s *)I2C2_BASE_ADDR ; + i2c.irq_no = IRQ_I2C2 ; + if (speed_mode == 0) + i2c.i2c_mode = I2C_STANDARD_MODE ; + else + i2c.i2c_mode = I2C_FAST_MODE ; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + + /**/ + /* hardware initial*/ + /**/ + + i2c.regs->cr_reg = 0 ; + i2c.regs->div_reg = APB_96M_I2C_DIV ; + i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ + i2c.regs->imr_reg = I2C_IMR_ALL_ENABLE ; /* 0x0007*/ + + i2c.regs->cr_reg = I2C_CR_ENABLE ; + tmp = i2c.regs->csr_reg ; /* read clear*/ + i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ + + if (i2c.i2c_mode == I2C_STANDARD_MODE) + i2c.regs->tr_reg = I2C_TR_STD_VALUE ; /* 0x8041*/ + else if (i2c.i2c_mode == I2C_FAST_MODE) + i2c.regs->tr_reg = I2C_TR_FAST_VALUE ; /* 0x8011*/ + + DPRINTK("Resetting I2C Controller Unit\n"); + + return ; +} +static int wmt_i2c_transfer_msg(struct wmt_i2cbusfifo *fifo_head) +{ + int xfer_length = fifo_head->xfer_length; + int xfer_msgnum = fifo_head->xfer_msgnum; + struct i2c_msg *pmsg = &fifo_head->msg[xfer_msgnum]; + int restart = fifo_head->restart; + unsigned short tcr_value; + unsigned short slave_addr = pmsg->addr; + int length = pmsg->len; + int ret = 0; + + if (pmsg->flags & I2C_M_RD) { + if (restart == 0) + i2c_wmt_wait_bus_not_busy(); + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ + if (restart == 0) + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) { + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + } else if (i2c.i2c_mode == I2C_FAST_MODE) { + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + } + if (length == 1) + i2c.regs->cr_reg |= I2C_CR_TX_NEXT_NO_ACK; /*only 8-bit to read*/ + + i2c.regs->tcr_reg = tcr_value ; + + /*repeat start case*/ + if (restart == 1) + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + } else { + if (restart == 0) + i2c_wmt_wait_bus_not_busy(); + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + /**/ + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length == 0) + i2c.regs->cdr_reg = 0 ; + else + i2c.regs->cdr_reg = (unsigned short)(pmsg->buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + + if (restart == 0) { + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + } + + /**/ + /* I2C: Set transfer mode [standard/fast]*/ + /**/ + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + i2c.regs->tcr_reg = tcr_value ; + + if (restart == 1) + i2c.regs->cr_reg |= I2C_CR_CPU_RDY ; + } + return ret; +} + +static irqreturn_t i2c_wmt_handler( + int this_irq, /*!<; //[IN] IRQ number */ + void *dev_id /*!<; //[IN] Pointer to device ID */ +) +{ + int wakeup ; + unsigned short isr_status ; + unsigned short tmp ; + unsigned long flags; + struct wmt_i2cbusfifo *fifo_head; + int xfer_length = 0; + int xfer_msgnum = 0; + struct i2c_msg *pmsg; + volatile unsigned short csr_reg; + + spin_lock_irqsave(&i2c2_wmt_irqlock, flags); + isr_status = i2c.regs->isr_reg ; + csr_reg = i2c.regs->csr_reg; + wakeup = 0 ; + fifo_head = list_first_entry(&wmt_i2c_fifohead, struct wmt_i2cbusfifo, busfifohead); + + if (isr_status & I2C_ISR_NACK_ADDR) { + DPRINTK("[%s]:i2c NACK\n", __func__); + /*spin_lock(&i2c_fifolock);*/ + list_del(&fifo_head->busfifohead);/*del request*/ + kfree(fifo_head); + /*spin_unlock(&i2c_fifolock);*/ + xfer_length = 0; + i2c.regs->isr_reg = I2C_ISR_NACK_ADDR_WRITE_CLEAR ; + tmp = i2c.regs->csr_reg ; /* read clear*/ + i2c.isr_nack = 1 ; + wakeup = 1 ; + } + + if ((isr_status & I2C_ISR_BYTE_END && ((csr_reg & I2C_CSR_RCV_ACK_MASK) == I2C_CSR_RCV_NOT_ACK))) { + /* + printk("data rcv nack\n"); + */ + list_del(&fifo_head->busfifohead);/*del request*/ + kfree(fifo_head); + xfer_length = 0; + i2c.regs->isr_reg = I2C_ISR_BYTE_END_WRITE_CLEAR ; + i2c.isr_nack = 1 ; + wakeup = 1 ; + } else if (isr_status & I2C_ISR_BYTE_END) { + i2c.regs->isr_reg = I2C_ISR_BYTE_END_WRITE_CLEAR ; + i2c.isr_byte_end = 1 ; + xfer_length = fifo_head->xfer_length; + xfer_msgnum = fifo_head->xfer_msgnum; + pmsg = &fifo_head->msg[xfer_msgnum]; + + /*read case*/ + if (pmsg->flags & I2C_M_RD) { + pmsg->buf[xfer_length - 1] = (i2c.regs->cdr_reg >> 8) ; + /*the last data in current msg?*/ + if (xfer_length == pmsg->len - 1) { + /*last msg of the current request?*/ + /*spin_lock(&i2c_fifolock);*/ + if (pmsg->flags & I2C_M_NOSTART) { + ++fifo_head->xfer_length; + fifo_head->restart = 1; + /* + ++fifo_head->xfer_msgnum; + */ + i2c.regs->cr_reg |= I2C_CR_CPU_RDY; + } else { + ++fifo_head->xfer_length; + fifo_head->restart = 0; + /* + ++fifo_head->xfer_msgnum; + */ + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY | I2C_CR_TX_NEXT_NO_ACK); + } + /*spin_unlock(&i2c_fifolock);*/ + } else if (xfer_length == pmsg->len) {/*next msg*/ + if (xfer_msgnum < fifo_head->msg_num - 1) { + /*spin_lock(&i2c_fifolock);*/ + fifo_head->xfer_length = 0; + ++fifo_head->xfer_msgnum; + + wmt_i2c_transfer_msg(fifo_head); + ++fifo_head->xfer_length; + /*spin_unlock(&i2c_fifolock);*/ + } else { /*data of this msg has been transfered*/ + /*spin_lock(&i2c_fifolock);*/ + list_del(&fifo_head->busfifohead);/*del request*/ + /*next request exist?*/ + if (list_empty(&wmt_i2c_fifohead)) {/*no more reqeust*/ + /*kfree(fifo_head);*/ + if (fifo_head->non_block == 0) { + wakeup = 1; + } else { + fifo_head->callback(fifo_head->data); + } + kfree(fifo_head); + } else { /*more request*/ + if (fifo_head->non_block == 0) { + wakeup = 1; + } else { + fifo_head->callback(fifo_head->data); + } + kfree(fifo_head); + fifo_head = list_first_entry(&wmt_i2c_fifohead, + struct wmt_i2cbusfifo, busfifohead); + /* + if (fifo_head->non_block == 0) + wakeup = 1; + */ + + fifo_head->xfer_length = 0; + wmt_i2c_transfer_msg(fifo_head); + ++fifo_head->xfer_length; + + /* + if (fifo_head->non_block == 0) { + printk("2 : non callback\n"); + wakeup = 1; + } else { + printk("2 :callback\n"); + fifo_head->callback(fifo_head->data); + } + */ + } + /*spin_unlock(&i2c_fifolock);*/ + } + } else {/*next data*/ + /*spin_lock(&i2c_fifolock);*/ + ++fifo_head->xfer_length; + /*spin_unlock(&i2c_fifolock);*/ + i2c.regs->cr_reg |= I2C_CR_CPU_RDY; + } + + } else { /*write case*/ + /*the last data in current msg?*/ + if (xfer_length == pmsg->len) { + /*last msg of the current request?*/ + if (xfer_msgnum < fifo_head->msg_num - 1) { + /*spin_lock(&i2c_fifolock);*/ + if (pmsg->flags & I2C_M_NOSTART) { + ++fifo_head->xfer_length; + fifo_head->restart = 1; + } else { + ++fifo_head->xfer_length; + fifo_head->restart = 0; + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); + udelay(2); + i2c.regs->cr_reg |= (I2C_CR_TX_END); + } + /*access next msg*/ + fifo_head->xfer_length = 0; + ++fifo_head->xfer_msgnum; + + wmt_i2c_transfer_msg(fifo_head); + ++fifo_head->xfer_length; + /*spin_unlock(&i2c_fifolock);*/ + } else {/*this request finish*/ + /*spin_lock(&i2c_fifolock);*/ + /*next request exist?*/ + list_del(&fifo_head->busfifohead);/*del request*/ + if (list_empty(&wmt_i2c_fifohead)) { + /*kfree(fifo_head);*/ + /* + if (fifo_head->non_block == 0) + wakeup = 1; + */ + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); + udelay(2); + i2c.regs->cr_reg |= (I2C_CR_TX_END); + if (fifo_head->non_block == 0) { + wakeup = 1; + } else { + fifo_head->callback(fifo_head->data); + } + kfree(fifo_head); + + } else { + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); + udelay(2); + i2c.regs->cr_reg |= (I2C_CR_TX_END); + if (fifo_head->non_block == 0) { + wakeup = 1; + } else { + fifo_head->callback(fifo_head->data); + } + kfree(fifo_head); + fifo_head = list_first_entry(&wmt_i2c_fifohead, + struct wmt_i2cbusfifo, busfifohead); + /* + if (fifo_head->non_block == 0) + wakeup = 1; + */ + + /*next msg*/ + fifo_head->xfer_length = 0; + ++fifo_head->xfer_msgnum; + wmt_i2c_transfer_msg(fifo_head); + ++fifo_head->xfer_length; + /* + if (fifo_head->non_block == 0) { + printk("4:non callback\n"); + wakeup = 1; + } else { + printk("4:callback\n"); + fifo_head->callback(fifo_head->data); + } + */ + } + /*spin_unlock(&i2c_fifolock);*/ + } + } else {/*next data*/ + i2c.regs->cdr_reg = (unsigned short) (pmsg->buf[fifo_head->xfer_length] & I2C_CDR_DATA_WRITE_MASK); + /*spin_lock(&i2c_fifolock);*/ + ++fifo_head->xfer_length; + /*spin_unlock(&i2c_fifolock);*/ + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY | I2C_CR_ENABLE); + } + } + } + + if (isr_status & I2C_ISR_SCL_TIME_OUT) { + DPRINTK("[%s]SCL timeout\n", __func__); +#if 0 + i2c.regs->cr_reg |= BIT7;/*reset status*/ + /*spin_lock(&i2c_fifolock);*/ + list_del(&fifo_head->busfifohead);/*del request*/ + /*spin_unlock(&i2c_fifolock);*/ + xfer_length = 0; + i2c.regs->isr_reg = I2C_ISR_SCL_TIME_OUT_WRITE_CLEAR | I2C_ISR_BYTE_END_WRITE_CLEAR; + i2c.isr_timeout = 1 ; + wakeup = 1; +#endif + i2c.regs->isr_reg = I2C_ISR_SCL_TIME_OUT_WRITE_CLEAR ; + } + + + if (wakeup) { + /*spin_lock_irqsave(&i2c_wmt_irqlock, flags);*/ + i2c.isr_int_pending = 1; + /*spin_unlock_irqrestore(&i2c_wmt_irqlock, flags);*/ + wake_up(&i2c2_wait); + } else + DPRINTK("i2c_err : unknown I2C ISR Handle 0x%4.4X" , isr_status) ; + spin_unlock_irqrestore(&i2c2_wmt_irqlock, flags); + return IRQ_HANDLED; +} + +static int i2c_wmt_resource_init(void) +{ + if (is_master == 0) + return 0; + if (request_irq(i2c.irq_no , &i2c_wmt_handler, IRQF_DISABLED, "i2c", 0) < 0) { + DPRINTK(KERN_INFO "I2C: Failed to register I2C irq %i\n", i2c.irq_no); + return -ENODEV; + } + return 0; +} + +static void i2c_wmt_resource_release(void) +{ + if (is_master == 0) + return; + free_irq(i2c.irq_no, 0); +} + +static struct i2c_algo_wmt_data i2c_wmt_data = { + write_msg: i2c_wmt_write_msg, + read_msg: i2c_wmt_read_msg, + send_request: i2c_send_request, + wait_bus_not_busy: i2c_wmt_wait_bus_not_busy, + reset: i2c_wmt_reset, + set_mode: i2c_wmt_set_mode, + udelay: I2C_ALGO_UDELAY, + timeout: I2C_ALGO_TIMEOUT, +}; + +static struct i2c_adapter i2c_wmt_ops = { + .owner = THIS_MODULE, + /* + .id = I2C_ALGO_WMT, + */ + .algo_data = &i2c_wmt_data, + .name = "wmt_i2c2_adapter", + .retries = I2C_ADAPTER_RETRIES, + .nr = 2, +}; + +#ifdef CONFIG_PM +static struct i2c_regs_s wmt_i2c_reg ; +static void i2c_shutdown(void) +{ + printk("i2c2 shutdown\n"); + wmt_i2c2_power_state = 2; + while (!list_empty(&wmt_i2c_fifohead)) + msleep(1); + while (1) {/*wait busy clear*/ + if ((REG16_VAL(I2C2_CSR_ADDR) & I2C_STATUS_MASK) == I2C_READY) + break ; + msleep(1); + } + return; +} +static int i2c_suspend(void) +{ + printk("i2c2 suspend\n"); + wmt_i2c_reg.imr_reg = i2c.regs->imr_reg; + wmt_i2c_reg.tr_reg = i2c.regs->tr_reg; + wmt_i2c_reg.div_reg = i2c.regs->div_reg; + return 0; +} +static void i2c_resume(void) +{ + printk("i2c2 resume\n"); + GPIO_CTRL_GP17_I2C_BYTE_VAL &= ~(BIT4 | BIT5); + PULL_EN_GP17_I2C_BYTE_VAL |= (BIT4 | BIT5); + PULL_CTRL_GP17_I2C_BYTE_VAL |= (BIT4 | BIT5); + auto_pll_divisor(DEV_I2C2, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_I2C2, SET_DIV, 2, 20);/*20M Hz*/ + + i2c.regs->cr_reg = 0 ; + i2c.regs->div_reg = wmt_i2c_reg.div_reg; + i2c.regs->imr_reg = wmt_i2c_reg.imr_reg; + i2c.regs->tr_reg = wmt_i2c_reg.tr_reg ; + i2c.regs->cr_reg = 0x001 ; +} +#else +#define i2c_suspend NULL +#define i2c_resume NULL +#define i2c_shutdown NULL +#endif +extern int wmt_i2c_add_bus(struct i2c_adapter *); +extern int wmt_i2c_del_bus(struct i2c_adapter *); + +#ifdef CONFIG_PM +static struct syscore_ops wmt_i2c_syscore_ops = { + .suspend = i2c_suspend, + .resume = i2c_resume, + .shutdown = i2c_shutdown, +}; +#endif + +static int __init i2c_adap_wmt_init(void) +{ + unsigned short tmp ; + char varname[] = "wmt.i2c.param"; +#ifdef CONFIG_I2C_SLAVE_WMT + char varname1[] = "wmt.bus.i2c.slave_port"; +#endif + unsigned char buf[80]; + int ret; + unsigned int port_num; + int idx = 0; + int varlen = 80; + unsigned int pllb_freq = 0; + unsigned int tr_val = 0; + +#ifdef CONFIG_I2C_SLAVE_WMT +#ifdef USE_UBOOT_PARA + ret = wmt_getsyspara(varname1, buf, &varlen); +#else + ret = 1; +#endif + is_master = 1; + if (ret == 0) { + ret = sscanf(buf, "%x", &port_num); + while (ret) { + if (port_num != 0) + is_master = 1; + else { + is_master = 0; + break; + } + idx += ret; + ret = sscanf(buf + idx, ",%x", &port_num); + } + } else + is_master = 1; +#endif + wmt_i2c2_is_master = is_master; + if (is_master == 1) { +#ifdef USE_UBOOT_PARA + ret = wmt_getsyspara(varname, buf, &varlen); +#else + ret = 1; +#endif + + if (ret == 0) { + ret = sscanf(buf, "%x:%x", &port_num, &speed_mode); + idx += 3; + while (ret) { + if (ret < 2) + speed_mode = 0; + else { + if (port_num != 2) + speed_mode = 0; + else + break; + } + ret = sscanf(buf + idx, ",%x:%x", &port_num, &speed_mode); + idx += 4; + } + } + if (speed_mode > 1) + speed_mode = 0; + wmt_i2c2_speed_mode = speed_mode; + + /**/ + /* software initial*/ + /**/ + i2c.regs = (struct i2c_regs_s *)I2C2_BASE_ADDR ; + i2c.irq_no = IRQ_I2C2 ; + + printk("PORT 2 speed_mode = %d\n", speed_mode); + if (speed_mode == 0) + i2c.i2c_mode = I2C_STANDARD_MODE ; + else if (speed_mode == 1) + i2c.i2c_mode = I2C_FAST_MODE ; + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + /**/ + /* hardware initial*/ + /**/ + auto_pll_divisor(DEV_I2C2, CLK_ENABLE, 0, 0); + pllb_freq = auto_pll_divisor(DEV_I2C2, SET_DIV, 2, 20);/*20M Hz*/ + if ((pllb_freq%(1000*2*100)) != 0) + tr_val = pllb_freq/(1000*2*100) + 1; + else + tr_val = pllb_freq/(1000*2*100); + *(volatile unsigned char *)CTRL_GPIO &= ~(BIT4 | BIT5); + *(volatile unsigned char *)PU_EN_GPIO |= (BIT4 | BIT5); + *(volatile unsigned char *)PU_CTRL_GPIO |= (BIT4 | BIT5); + i2c.regs->cr_reg = 0 ; + i2c.regs->div_reg = APB_96M_I2C_DIV ; + i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ + i2c.regs->imr_reg = I2C_IMR_ALL_ENABLE ; /* 0x0007*/ + + i2c.regs->cr_reg = I2C_CR_ENABLE ; + tmp = i2c.regs->csr_reg ; /* read clear*/ + i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ + + if (i2c.i2c_mode == I2C_STANDARD_MODE) + i2c.regs->tr_reg = 0xff00|tr_val; + else if (i2c.i2c_mode == I2C_FAST_MODE) { + tr_val /= 4; + i2c.regs->tr_reg = 0xff00|tr_val ; + } + } + + + if (i2c_wmt_resource_init() == 0) { + if (wmt_i2c_add_bus(&i2c_wmt_ops) < 0) { + i2c_wmt_resource_release(); + printk(KERN_INFO "i2c: Failed to add bus\n"); + return -ENODEV; + } + } else + return -ENODEV; + + INIT_LIST_HEAD(&wmt_i2c_fifohead); + +#ifdef CONFIG_PM + register_syscore_ops(&wmt_i2c_syscore_ops); +#endif + + printk(KERN_INFO "i2c: successfully added bus\n"); + +#ifdef I2C_REG_TEST + printk("i2c.regs->cr_reg= 0x%08x\n\r", i2c.regs->cr_reg); + printk("i2c.regs->tcr_reg= 0x%08x\n\r", i2c.regs->tcr_reg); + printk("i2c.regs->csr_reg= 0x%08x\n\r", i2c.regs->csr_reg); + printk("i2c.regs->isr_reg= 0x%08x\n\r", i2c.regs->isr_reg); + printk("i2c.regs->imr_reg= 0x%08x\n\r", i2c.regs->imr_reg); + printk("i2c.regs->cdr_reg= 0x%08x\n\r", i2c.regs->cdr_reg); + printk("i2c.regs->tr_reg= 0x%08x\n\r", i2c.regs->tr_reg); + printk("i2c.regs->div_reg= 0x%08x\n\r", i2c.regs->div_reg); +#endif + + return 0; +} +subsys_initcall(i2c_adap_wmt_init); + +static void i2c_adap_wmt_exit(void) +{ + wmt_i2c_del_bus(&i2c_wmt_ops); + i2c_wmt_resource_release(); + + printk(KERN_INFO "i2c: successfully removed bus\n"); +} + + +MODULE_AUTHOR("WonderMedia Technologies, Inc."); +MODULE_DESCRIPTION("WMT I2C Adapter Driver"); +MODULE_LICENSE("GPL"); + +module_exit(i2c_adap_wmt_exit); + diff --git a/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-bus-3.c b/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-bus-3.c new file mode 100755 index 00000000..b362a156 --- /dev/null +++ b/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-bus-3.c @@ -0,0 +1,1256 @@ +/*++ + drivers/i2c/busses/wmt-i2c-bus-3.c + + Copyright (c) 2013 WonderMedia Technologies, Inc. + + 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, see <http://www.gnu.org/licenses/>. + + WonderMedia Technologies, Inc. + 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. +--*/ +/* Include your headers here*/ +#include <linux/kernel.h> +#include <linux/module.h> + +#include <linux/i2c.h> +/* +#include <linux/i2c-id.h> +*/ +#include <linux/init.h> +#include <linux/time.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/interrupt.h> + +#include <mach/hardware.h> +#include <asm/irq.h> +#include <mach/irqs.h> +#include <mach/wmt-i2c-bus.h> +#include <linux/slab.h> +#include <linux/pm.h> +#include <linux/syscore_ops.h> + +#ifdef __KERNEL__ + +#ifdef DEBUG + #define DPRINTK printk +#else + #define DPRINTK(x...) +#endif + +#else + #define DPRINTK printf + +#endif + + +#define MAX_BUS_READY_CNT 50 /* jiffy*/ +#define MAX_TX_TIMEOUT 500 /* ms*/ +#define MAX_RX_TIMEOUT 500 /* ms*/ +#define CTRL_GPIO GPIO_CTRL_GP23_I2C3_BYTE_ADDR +#define PU_EN_GPIO PULL_EN_GP23_I2C3_BYTE_ADDR +#define PU_CTRL_GPIO PULL_CTRL_GP23_I2C3_BYTE_ADDR + +#define USE_UBOOT_PARA + +struct wmt_i2c_s { + struct i2c_regs_s *regs; + int irq_no ; + enum i2c_mode_e i2c_mode ; + int volatile isr_nack ; + int volatile isr_byte_end ; + int volatile isr_timeout ; + int volatile isr_int_pending ; +}; + +static int i2c_wmt_wait_bus_not_busy(void); +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +static unsigned int speed_mode = 1; +static unsigned int is_master = 1;/*master:1, slave:0*/ +unsigned int wmt_i2c3_is_master = 1; +unsigned int wmt_i2c3_speed_mode = 0; +static unsigned int wmt_i2c3_power_state = 0;/*0:power on, 1:suspend, 2:shutdown*/ +EXPORT_SYMBOL(wmt_i2c3_is_master); + +/**/ +/* variable*/ +/*-------------------------------------------------*/ +static volatile struct wmt_i2c_s i2c ; + +DECLARE_WAIT_QUEUE_HEAD(i2c3_wait); +/* +spinlock_t i2c3_wmt_irqlock = SPIN_LOCK_UNLOCKED; +*/ +static DEFINE_SPINLOCK(i2c3_wmt_irqlock); +static struct list_head wmt_i2c_fifohead; +/* +static spinlock_t i2c_fifolock = SPIN_LOCK_UNLOCKED; +*/ +static DEFINE_SPINLOCK(i2c_fifolock); +static int i2c_wmt_read_buf( + unsigned int slave_addr, + char *buf, + unsigned int length, + int restart, + int last +); +static int i2c_wmt_write_buf( + unsigned int slave_addr, + char *buf, + unsigned int length, + int restart, + int last +); + +static void i2c_wmt_set_mode(enum i2c_mode_e mode /*!<; //[IN] mode */) +{ + if (is_master == 0) + return; + i2c.i2c_mode = mode ; + + if (i2c.i2c_mode == I2C_STANDARD_MODE) { + DPRINTK("I2C: set standard mode \n"); + i2c.regs->tr_reg = I2C_TR_STD_VALUE ; /* 0x8041*/ + } else if (i2c.i2c_mode == I2C_FAST_MODE) { + DPRINTK("I2C: set fast mode \n"); + i2c.regs->tr_reg = I2C_TR_FAST_VALUE ; /* 0x8011*/ + } +} + + +static int i2c_send_request( + struct i2c_msg *msg, + int msg_num, + int non_block, + void (*callback)(void *data), + void *data +) +{ + struct wmt_i2cbusfifo *i2c_fifo_head; + struct i2c_msg *pmsg = NULL; + int ret = 0; + int restart = 0; + int last = 0; + unsigned long flags; + int slave_addr = msg[0].addr; + + if (slave_addr == WMT_I2C_API_I2C_ADDR) + return ret ; + if (wmt_i2c3_power_state == 2) { + printk("I2C3 has been shutdown\n"); + return -EIO; + } + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + + i2c_fifo_head = kzalloc(sizeof(struct wmt_i2cbusfifo), GFP_ATOMIC); + INIT_LIST_HEAD(&i2c_fifo_head->busfifohead); + + pmsg = &msg[0]; + i2c_fifo_head->msg = pmsg; + i2c_fifo_head->msg_num = msg_num; + + spin_lock_irqsave(&i2c_fifolock, flags); + if (list_empty(&wmt_i2c_fifohead)) { + i2c_wmt_wait_bus_not_busy(); + pmsg = &msg[0]; + i2c_fifo_head->xfer_length = 1; + i2c_fifo_head->xfer_msgnum = 0; + i2c_fifo_head->restart = 0; + i2c_fifo_head->non_block = non_block; + + if (non_block == 1) { + i2c_fifo_head->callback = callback; + i2c_fifo_head->data = data; + } else { + i2c_fifo_head->callback = 0; + i2c_fifo_head->data = 0; + } + + list_add_tail(&i2c_fifo_head->busfifohead, &wmt_i2c_fifohead); + if (pmsg->flags & I2C_M_RD) { + i2c_fifo_head->xfer_length = 1; + ret = i2c_wmt_read_buf(pmsg->addr, pmsg->buf, pmsg->len, restart, last); + } else { + i2c_fifo_head->xfer_length = 1; + if (pmsg->flags & I2C_M_NOSTART) + i2c_fifo_head->restart = 1; + else + i2c_fifo_head->restart = 0; + ret = i2c_wmt_write_buf(pmsg->addr, pmsg->buf, pmsg->len, restart, last); + } + + } else { + i2c_fifo_head->xfer_length = 0; + i2c_fifo_head->xfer_msgnum = 0; + i2c_fifo_head->restart = 0; + i2c_fifo_head->non_block = non_block; + if (non_block == 1) { + i2c_fifo_head->callback = callback; + i2c_fifo_head->data = data; + } else { + i2c_fifo_head->callback = 0; + i2c_fifo_head->data = 0; + } + list_add_tail(&i2c_fifo_head->busfifohead, &wmt_i2c_fifohead); + } + spin_unlock_irqrestore(&i2c_fifolock, flags); + if (non_block == 0) { + wait_event(i2c3_wait, i2c.isr_int_pending); + ret = msg_num; + if (i2c.isr_nack == 1) { + DPRINTK("i2c_err : write NACK error (rx) \n\r") ; + ret = -EIO ; + } + if (i2c.isr_timeout == 1) { + DPRINTK("i2c_err : write SCL timeout error (rx)\n\r") ; + ret = -ETIMEDOUT ; + } + + } + + return ret; + + +} +static int i2c_wmt_read_buf( + unsigned int slave_addr, + char *buf, + unsigned int length, + int restart, + int last +) +{ + unsigned short tcr_value; + int ret = 0; + + DPRINTK("[%s]:length = %d , slave_addr = %x\n", __func__, length , slave_addr); + + if (length <=0) + return -1; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ + if (length <=0) + return -1; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + tcr_value = 0 ; + + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + if (length == 1) + i2c.regs->cr_reg |= I2C_CR_TX_NEXT_NO_ACK; /*only 8-bit to read*/ + + i2c.regs->tcr_reg = tcr_value ; + return ret; +} + +static int i2c_wmt_write_buf( + unsigned int slave_addr, + char *buf, + unsigned int length, + int restart, + int last +) +{ + unsigned short tcr_value ; + unsigned int xfer_length ; + int ret = 0 ; + + DPRINTK("[%s]length = %d , slave_addr = %x\n", __func__, length , slave_addr); + if (slave_addr == WMT_I2C_API_I2C_ADDR) + return ret ; + + if (is_master == 0) + return -ENXIO; + + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length < 0) + return -1 ; + xfer_length = 0 ; /* for array index and also for checking counting*/ + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ + + if (length == 0) + i2c.regs->cdr_reg = 0 ; + else + i2c.regs->cdr_reg = (unsigned short)(buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + i2c.regs->tcr_reg = tcr_value ; + + ret = 0 ; + return ret; + +} +static int i2c_wmt_read_msg( + unsigned int slave_addr, /*!<; //[IN] Salve address */ + char *buf, /*!<; //[OUT] Pointer to data */ + unsigned int length, /*!<; //Data length */ + int restart, /*!<; //Need to restart after a complete read */ + int last /*!<; //Last read */ +) +{ + unsigned short tcr_value ; + unsigned int xfer_length ; + int is_timeout ; + int ret = 0 ; + int wait_event_result = 0 ; + + if (is_master == 0) + return -ENXIO; + if (length <= 0) + return -1 ; + xfer_length = 0 ; + + if (restart == 0) + ret = i2c_wmt_wait_bus_not_busy() ; + if (ret < 0) + return ret ; + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ + if (restart == 0) + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) { + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + } else if (i2c.i2c_mode == I2C_FAST_MODE) { + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + } + if (length == 1) + i2c.regs->cr_reg |= I2C_CR_TX_NEXT_NO_ACK; /*only 8-bit to read*/ + + i2c.regs->tcr_reg = tcr_value ; + + /*repeat start case*/ + if (restart == 1) + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + + ret = 0 ; + for (; ;) { + is_timeout = 0 ; + wait_event_result = wait_event_interruptible_timeout(i2c3_wait, i2c.isr_int_pending , + (MAX_RX_TIMEOUT * HZ / 1000)) ; + if (likely(wait_event_result > 0)) { + DPRINTK("I2C: wait interrupted (rx) \n"); + ret = 0 ; + } else if (likely(i2c.isr_int_pending == 0)) { + DPRINTK("I2C: wait timeout (rx) \n"); + is_timeout = 1 ; + ret = -ETIMEDOUT ; + } + + /**/ + /* fail case*/ + /**/ + if (i2c.isr_nack == 1) { + DPRINTK("i2c_err : write NACK error (rx) \n\r") ; + ret = -EIO ; + break ; + } + if (i2c.isr_timeout == 1) { + DPRINTK("i2c_err : write SCL timeout error (rx)\n\r") ; + msleep(10); + ret = -ETIMEDOUT ; + break ; + } + if (is_timeout == 1) { + DPRINTK("i2c_err: write software timeout error (rx) \n\r") ; + ret = -ETIMEDOUT ; + break ; + } + + + /**/ + /* pass case*/ + /**/ + if (i2c.isr_byte_end == 1) { + buf[xfer_length] = (i2c.regs->cdr_reg >> 8) ; + ++xfer_length ; + DPRINTK("i2c_test: received BYTE_END\n\r"); + } + i2c.isr_int_pending = 0; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + + if (length > xfer_length) { + if ((length - 1) == xfer_length) { /* next read is the last one*/ + i2c.regs->cr_reg |= (I2C_CR_TX_NEXT_NO_ACK | I2C_CR_CPU_RDY); + DPRINTK("i2c_test: set CPU_RDY & TX_ACK. next data is last.\r\n"); + } else { + i2c.regs->cr_reg |= I2C_CR_CPU_RDY ; + DPRINTK("i2c_test: more data to read. only set CPU_RDY. \r\n"); + } + } else if (length == xfer_length) { /* end rx xfer*/ + if (last == 1) { /* stop case*/ + DPRINTK("i2c_test: read completed \r\n"); + break ; + } else { /* restart case*/ + /* ??? how to handle the restart after read ?*/ + DPRINTK("i2c_test: RX ReStart Case \r\n") ; + break ; + } + } else { + DPRINTK("i2c_err : read known error\n\r") ; + ret = -EIO ; + break ; + } + } + + DPRINTK("i2c_test: read sequence completed\n\r"); + return ret ; +} + +static int i2c_wmt_write_msg( + unsigned int slave_addr, /*!<; //[IN] Salve address */ + char *buf, /*!<; //[OUT] Pointer to data */ + unsigned int length, /*!<; //Data length */ + int restart, /*!<; //Need to restart after a complete write */ + int last /*!<; //Last read */ +) +{ + unsigned short tcr_value ; + unsigned int xfer_length ; + int is_timeout ; + int ret = 0 ; + int wait_event_result ; + + DPRINTK("length = %d , slave_addr = %x\n", length , slave_addr); + if (slave_addr == WMT_I2C_API_I2C_ADDR) + return ret ; + + if (is_master == 0) + return -ENXIO; + + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length < 0) + return -1 ; + xfer_length = 0 ; /* for array index and also for checking counting*/ + if (restart == 0) + ret = i2c_wmt_wait_bus_not_busy() ; + if (ret < 0) + return ret ; + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + + /**/ + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length == 0) + i2c.regs->cdr_reg = 0 ; + else + i2c.regs->cdr_reg = (unsigned short)(buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + + if (restart == 0) { + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + } + + /**/ + /* I2C: Set transfer mode [standard/fast]*/ + /**/ + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + i2c.regs->tcr_reg = tcr_value ; + + if (restart == 1) + i2c.regs->cr_reg |= I2C_CR_CPU_RDY ; + + ret = 0 ; + for (; ;) { + + is_timeout = 0 ; + /**/ + /* I2C: Wait for interrupt. if ( i2c.isr_int_pending == 1 ) ==> an interrupt exsits.*/ + /**/ + wait_event_result = wait_event_interruptible_timeout(i2c3_wait, i2c.isr_int_pending , (MAX_TX_TIMEOUT * HZ / 1000)) ; + + if (likely(wait_event_result > 0)) { + DPRINTK("I2C: wait interrupted (tx)\n"); + ret = 0 ; + } else if (likely(i2c.isr_int_pending == 0)) { + DPRINTK("I2C: wait timeout (tx) \n"); + is_timeout = 1 ; + ret = -ETIMEDOUT ; + } + + /**/ + /* fail case*/ + /**/ + if (i2c.isr_nack == 1) { + DPRINTK("i2c_err : write NACK error (tx) \n\r") ; + ret = -EIO ; + break ; + } + if (i2c.isr_timeout == 1) { + DPRINTK("i2c_err : write SCL timeout error (tx)\n\r") ; + msleep(10); + ret = -ETIMEDOUT ; + break ; + } + if (is_timeout == 1) { + DPRINTK("i2c_err : write software timeout error (tx)\n\r") ; + ret = -ETIMEDOUT ; + break ; + } + + /**/ + /* pass case*/ + /**/ + if (i2c.isr_byte_end == 1) { + DPRINTK("i2c: isr end byte (tx)\n\r") ; + ++xfer_length ; + } + i2c.isr_int_pending = 0 ; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + + + if ((i2c.regs->csr_reg & I2C_CSR_RCV_ACK_MASK) == I2C_CSR_RCV_NOT_ACK) { + DPRINTK("i2c_err : write RCV NACK error\n\r") ; + ret = -EIO ; + break ; + } + + /**/ + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length == 0) { + i2c.regs->cr_reg = (I2C_CR_TX_END|I2C_CR_CPU_RDY|I2C_CR_ENABLE) ; + break ; + } + if (length > xfer_length) { + i2c.regs->cdr_reg = (unsigned short) (buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + i2c.regs->cr_reg = (I2C_CR_CPU_RDY | I2C_CR_ENABLE) ; + DPRINTK("i2c_test: write register data \n\r") ; + } else if (length == xfer_length) { /* end tx xfer*/ + if (last == 1) { /* stop case*/ + i2c.regs->cr_reg = (I2C_CR_TX_END|I2C_CR_CPU_RDY|I2C_CR_ENABLE) ; + DPRINTK("i2c_test: finish write \n\r") ; + break ; + } else { /* restart case*/ + /* handle the restart for first write then the next is read*/ + i2c.regs->cr_reg = (I2C_CR_ENABLE) ; + DPRINTK("i2c_test: tx restart Case \n\r") ; + break ; + } + } else { + DPRINTK("i2c_err : write unknown error\n\r") ; + ret = -EIO ; + break ; + } + } ; + + DPRINTK("i2c_test: write sequence completed\n\r"); + + return ret ; +} + +static int i2c_wmt_wait_bus_not_busy(void) +{ + int ret ; + int cnt ; + + ret = 0 ; + cnt = 0 ; + while (1) { + if ((REG16_VAL(I2C3_CSR_ADDR) & I2C_STATUS_MASK) == I2C_READY) { + ret = 0; + break ; + } + cnt++ ; + + if (cnt > MAX_BUS_READY_CNT) { + ret = (-EBUSY) ; + printk("i2c_err 3: wait but not ready time-out\n\r") ; + cnt = 0; + break; + } + } + return ret ; +} + +static void i2c_wmt_reset(void) +{ + unsigned short tmp ; + if (is_master == 0) + return; + + /**/ + /* software initial*/ + /**/ + i2c.regs = (struct i2c_regs_s *)I2C3_BASE_ADDR ; + i2c.irq_no = IRQ_I2C3 ; + if (speed_mode == 0) + i2c.i2c_mode = I2C_STANDARD_MODE ; + else + i2c.i2c_mode = I2C_FAST_MODE ; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + + /**/ + /* hardware initial*/ + /**/ + + i2c.regs->cr_reg = 0 ; + i2c.regs->div_reg = APB_96M_I2C_DIV ; + i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ + i2c.regs->imr_reg = I2C_IMR_ALL_ENABLE ; /* 0x0007*/ + + i2c.regs->cr_reg = I2C_CR_ENABLE ; + tmp = i2c.regs->csr_reg ; /* read clear*/ + i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ + + if (i2c.i2c_mode == I2C_STANDARD_MODE) + i2c.regs->tr_reg = I2C_TR_STD_VALUE ; /* 0x8041*/ + else if (i2c.i2c_mode == I2C_FAST_MODE) + i2c.regs->tr_reg = I2C_TR_FAST_VALUE ; /* 0x8011*/ + + DPRINTK("Resetting I2C Controller Unit\n"); + + return ; +} +static int wmt_i2c_transfer_msg(struct wmt_i2cbusfifo *fifo_head) +{ + int xfer_length = fifo_head->xfer_length; + int xfer_msgnum = fifo_head->xfer_msgnum; + struct i2c_msg *pmsg = &fifo_head->msg[xfer_msgnum]; + int restart = fifo_head->restart; + unsigned short tcr_value; + unsigned short slave_addr = pmsg->addr; + int length = pmsg->len; + int ret = 0; + + if (pmsg->flags & I2C_M_RD) { + if (restart == 0) + i2c_wmt_wait_bus_not_busy(); + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ + if (restart == 0) + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) { + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + } else if (i2c.i2c_mode == I2C_FAST_MODE) { + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + } + if (length == 1) + i2c.regs->cr_reg |= I2C_CR_TX_NEXT_NO_ACK; /*only 8-bit to read*/ + + i2c.regs->tcr_reg = tcr_value ; + + /*repeat start case*/ + if (restart == 1) + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + } else { + if (restart == 0) + i2c_wmt_wait_bus_not_busy(); + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + /**/ + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length == 0) + i2c.regs->cdr_reg = 0 ; + else + i2c.regs->cdr_reg = (unsigned short)(pmsg->buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + + if (restart == 0) { + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + } + + /**/ + /* I2C: Set transfer mode [standard/fast]*/ + /**/ + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + i2c.regs->tcr_reg = tcr_value ; + + if (restart == 1) + i2c.regs->cr_reg |= I2C_CR_CPU_RDY ; + } + return ret; +} + +static irqreturn_t i2c_wmt_handler( + int this_irq, /*!<; //[IN] IRQ number */ + void *dev_id /*!<; //[IN] Pointer to device ID */ +) +{ + int wakeup ; + unsigned short isr_status ; + unsigned short tmp ; + unsigned long flags; + struct wmt_i2cbusfifo *fifo_head; + int xfer_length = 0; + int xfer_msgnum = 0; + struct i2c_msg *pmsg; + volatile unsigned short csr_reg; + + spin_lock_irqsave(&i2c3_wmt_irqlock, flags); + isr_status = i2c.regs->isr_reg ; + csr_reg = i2c.regs->csr_reg; + wakeup = 0 ; + fifo_head = list_first_entry(&wmt_i2c_fifohead, struct wmt_i2cbusfifo, busfifohead); + + if (isr_status & I2C_ISR_NACK_ADDR) { + DPRINTK("[%s]:i2c NACK\n", __func__); + /*spin_lock(&i2c_fifolock);*/ + list_del(&fifo_head->busfifohead);/*del request*/ + kfree(fifo_head); + /*spin_unlock(&i2c_fifolock);*/ + xfer_length = 0; + i2c.regs->isr_reg = I2C_ISR_NACK_ADDR_WRITE_CLEAR ; + tmp = i2c.regs->csr_reg ; /* read clear*/ + i2c.isr_nack = 1 ; + wakeup = 1 ; + } + + if ((isr_status & I2C_ISR_BYTE_END && ((csr_reg & I2C_CSR_RCV_ACK_MASK) == I2C_CSR_RCV_NOT_ACK))) { + /* + printk("data rcv nack\n"); + */ + list_del(&fifo_head->busfifohead);/*del request*/ + kfree(fifo_head); + xfer_length = 0; + i2c.regs->isr_reg = I2C_ISR_BYTE_END_WRITE_CLEAR ; + i2c.isr_nack = 1 ; + wakeup = 1 ; + } else if (isr_status & I2C_ISR_BYTE_END) { + i2c.regs->isr_reg = I2C_ISR_BYTE_END_WRITE_CLEAR ; + i2c.isr_byte_end = 1 ; + xfer_length = fifo_head->xfer_length; + xfer_msgnum = fifo_head->xfer_msgnum; + pmsg = &fifo_head->msg[xfer_msgnum]; + + /*read case*/ + if (pmsg->flags & I2C_M_RD) { + pmsg->buf[xfer_length - 1] = (i2c.regs->cdr_reg >> 8) ; + /*the last data in current msg?*/ + if (xfer_length == pmsg->len - 1) { + /*last msg of the current request?*/ + /*spin_lock(&i2c_fifolock);*/ + if (pmsg->flags & I2C_M_NOSTART) { + ++fifo_head->xfer_length; + fifo_head->restart = 1; + /* + ++fifo_head->xfer_msgnum; + */ + i2c.regs->cr_reg |= I2C_CR_CPU_RDY; + } else { + ++fifo_head->xfer_length; + fifo_head->restart = 0; + /* + ++fifo_head->xfer_msgnum; + */ + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY | I2C_CR_TX_NEXT_NO_ACK); + } + /*spin_unlock(&i2c_fifolock);*/ + } else if (xfer_length == pmsg->len) {/*next msg*/ + if (xfer_msgnum < fifo_head->msg_num - 1) { + /*spin_lock(&i2c_fifolock);*/ + fifo_head->xfer_length = 0; + ++fifo_head->xfer_msgnum; + + wmt_i2c_transfer_msg(fifo_head); + ++fifo_head->xfer_length; + /*spin_unlock(&i2c_fifolock);*/ + } else { /*data of this msg has been transfered*/ + /*spin_lock(&i2c_fifolock);*/ + list_del(&fifo_head->busfifohead);/*del request*/ + /*next request exist?*/ + if (list_empty(&wmt_i2c_fifohead)) {/*no more reqeust*/ + /*kfree(fifo_head);*/ + if (fifo_head->non_block == 0) { + wakeup = 1; + } else { + fifo_head->callback(fifo_head->data); + } + kfree(fifo_head); + } else { /*more request*/ + if (fifo_head->non_block == 0) { + wakeup = 1; + } else { + fifo_head->callback(fifo_head->data); + } + kfree(fifo_head); + fifo_head = list_first_entry(&wmt_i2c_fifohead, + struct wmt_i2cbusfifo, busfifohead); + /* + if (fifo_head->non_block == 0) + wakeup = 1; + */ + + fifo_head->xfer_length = 0; + wmt_i2c_transfer_msg(fifo_head); + ++fifo_head->xfer_length; + + /* + if (fifo_head->non_block == 0) { + printk("2 : non callback\n"); + wakeup = 1; + } else { + printk("2 :callback\n"); + fifo_head->callback(fifo_head->data); + } + */ + } + /*spin_unlock(&i2c_fifolock);*/ + } + } else {/*next data*/ + /*spin_lock(&i2c_fifolock);*/ + ++fifo_head->xfer_length; + /*spin_unlock(&i2c_fifolock);*/ + i2c.regs->cr_reg |= I2C_CR_CPU_RDY; + } + + } else { /*write case*/ + /*the last data in current msg?*/ + if (xfer_length == pmsg->len) { + /*last msg of the current request?*/ + if (xfer_msgnum < fifo_head->msg_num - 1) { + /*spin_lock(&i2c_fifolock);*/ + if (pmsg->flags & I2C_M_NOSTART) { + ++fifo_head->xfer_length; + fifo_head->restart = 1; + } else { + ++fifo_head->xfer_length; + fifo_head->restart = 0; + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); + udelay(2); + i2c.regs->cr_reg |= (I2C_CR_TX_END); + } + /*access next msg*/ + fifo_head->xfer_length = 0; + ++fifo_head->xfer_msgnum; + + wmt_i2c_transfer_msg(fifo_head); + ++fifo_head->xfer_length; + /*spin_unlock(&i2c_fifolock);*/ + } else {/*this request finish*/ + /*spin_lock(&i2c_fifolock);*/ + /*next request exist?*/ + list_del(&fifo_head->busfifohead);/*del request*/ + if (list_empty(&wmt_i2c_fifohead)) { + /*kfree(fifo_head);*/ + /* + if (fifo_head->non_block == 0) + wakeup = 1; + */ + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); + udelay(2); + i2c.regs->cr_reg |= (I2C_CR_TX_END); + if (fifo_head->non_block == 0) { + wakeup = 1; + } else { + fifo_head->callback(fifo_head->data); + } + kfree(fifo_head); + + } else { + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); + udelay(2); + i2c.regs->cr_reg |= (I2C_CR_TX_END); + if (fifo_head->non_block == 0) { + wakeup = 1; + } else { + fifo_head->callback(fifo_head->data); + } + kfree(fifo_head); + fifo_head = list_first_entry(&wmt_i2c_fifohead, + struct wmt_i2cbusfifo, busfifohead); + /* + if (fifo_head->non_block == 0) + wakeup = 1; + */ + + /*next msg*/ + fifo_head->xfer_length = 0; + ++fifo_head->xfer_msgnum; + wmt_i2c_transfer_msg(fifo_head); + ++fifo_head->xfer_length; + /* + if (fifo_head->non_block == 0) { + printk("4:non callback\n"); + wakeup = 1; + } else { + printk("4:callback\n"); + fifo_head->callback(fifo_head->data); + } + */ + } + /*spin_unlock(&i2c_fifolock);*/ + } + } else {/*next data*/ + i2c.regs->cdr_reg = (unsigned short) (pmsg->buf[fifo_head->xfer_length] & I2C_CDR_DATA_WRITE_MASK); + /*spin_lock(&i2c_fifolock);*/ + ++fifo_head->xfer_length; + /*spin_unlock(&i2c_fifolock);*/ + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY | I2C_CR_ENABLE); + } + } + } + + if (isr_status & I2C_ISR_SCL_TIME_OUT) { + DPRINTK("[%s]SCL timeout\n", __func__); +#if 0 + i2c.regs->cr_reg |= BIT7;/*reset status*/ + /*spin_lock(&i2c_fifolock);*/ + list_del(&fifo_head->busfifohead);/*del request*/ + /*spin_unlock(&i2c_fifolock);*/ + xfer_length = 0; + i2c.regs->isr_reg = I2C_ISR_SCL_TIME_OUT_WRITE_CLEAR | I2C_ISR_BYTE_END_WRITE_CLEAR; + i2c.isr_timeout = 1 ; + wakeup = 1; +#endif + i2c.regs->isr_reg = I2C_ISR_SCL_TIME_OUT_WRITE_CLEAR ; + } + + + if (wakeup) { + /*spin_lock_irqsave(&i2c_wmt_irqlock, flags);*/ + i2c.isr_int_pending = 1; + /*spin_unlock_irqrestore(&i2c_wmt_irqlock, flags);*/ + wake_up(&i2c3_wait); + } else + DPRINTK("i2c_err : unknown I2C ISR Handle 0x%4.4X" , isr_status) ; + spin_unlock_irqrestore(&i2c3_wmt_irqlock, flags); + return IRQ_HANDLED; +} + +static int i2c_wmt_resource_init(void) +{ + if (is_master == 0) + return 0; + if (request_irq(i2c.irq_no , &i2c_wmt_handler, IRQF_DISABLED, "i2c", 0) < 0) { + DPRINTK(KERN_INFO "I2C: Failed to register I2C irq %i\n", i2c.irq_no); + return -ENODEV; + } + return 0; +} + +static void i2c_wmt_resource_release(void) +{ + if (is_master == 0) + return; + free_irq(i2c.irq_no, 0); +} + +static struct i2c_algo_wmt_data i2c_wmt_data = { + write_msg: i2c_wmt_write_msg, + read_msg: i2c_wmt_read_msg, + send_request: i2c_send_request, + wait_bus_not_busy: i2c_wmt_wait_bus_not_busy, + reset: i2c_wmt_reset, + set_mode: i2c_wmt_set_mode, + udelay: I2C_ALGO_UDELAY, + timeout: I2C_ALGO_TIMEOUT, +}; + +static struct i2c_adapter i2c_wmt_ops = { + .owner = THIS_MODULE, + /* + .id = I2C_ALGO_WMT, + */ + .algo_data = &i2c_wmt_data, + .name = "wmt_i2c3_adapter", + .retries = I2C_ADAPTER_RETRIES, + .nr = 3, +}; + +#ifdef CONFIG_PM +static struct i2c_regs_s wmt_i2c_reg ; +static void i2c_shutdown(void) +{ + printk("i2c3 shutdown\n"); + wmt_i2c3_power_state = 2; + while (!list_empty(&wmt_i2c_fifohead)) + msleep(1); + while (1) {/*wait busy clear*/ + if ((REG16_VAL(I2C3_CSR_ADDR) & I2C_STATUS_MASK) == I2C_READY) + break ; + msleep(1); + } + return; +} +static int i2c_suspend(void) +{ + printk("i2c3 suspend\n"); + wmt_i2c_reg.imr_reg = i2c.regs->imr_reg; + wmt_i2c_reg.tr_reg = i2c.regs->tr_reg; + wmt_i2c_reg.div_reg = i2c.regs->div_reg; + return 0; +} +static void i2c_resume(void) +{ + printk("i2c3 resume\n"); + GPIO_CTRL_GP23_I2C3_BYTE_VAL &= ~(BIT0 | BIT1); + PULL_EN_GP23_I2C3_BYTE_VAL |= (BIT0 | BIT1); + PULL_CTRL_GP23_I2C3_BYTE_VAL |= (BIT0 | BIT1); + PIN_SHARING_SEL_4BYTE_VAL &= ~BIT28; + auto_pll_divisor(DEV_I2C3, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_I2C3, SET_DIV, 2, 20);/*20M Hz*/ + + i2c.regs->cr_reg = 0 ; + i2c.regs->div_reg = wmt_i2c_reg.div_reg; + i2c.regs->imr_reg = wmt_i2c_reg.imr_reg; + i2c.regs->tr_reg = wmt_i2c_reg.tr_reg ; + i2c.regs->cr_reg = 0x001 ; +} +#else +#define i2c_suspend NULL +#define i2c_resume NULL +#define i2c_shutdown NULL +#endif +extern int wmt_i2c_add_bus(struct i2c_adapter *); +extern int wmt_i2c_del_bus(struct i2c_adapter *); + +#ifdef CONFIG_PM +static struct syscore_ops wmt_i2c_syscore_ops = { + .suspend = i2c_suspend, + .resume = i2c_resume, + .shutdown = i2c_shutdown, +}; +#endif + +static int __init i2c_adap_wmt_init(void) +{ + unsigned short tmp ; + char varname[] = "wmt.i2c.param"; +#ifdef CONFIG_I2C_SLAVE_WMT + char varname1[] = "wmt.bus.i2c.slave_port"; +#endif + unsigned char buf[80]; + int ret; + unsigned int port_num; + int idx = 0; + int varlen = 80; + unsigned int pllb_freq = 0; + unsigned int tr_val = 0; + +#ifdef CONFIG_I2C_SLAVE_WMT +#ifdef USE_UBOOT_PARA + ret = wmt_getsyspara(varname1, buf, &varlen); +#else + ret = 1; +#endif + is_master = 1; + if (ret == 0) { + ret = sscanf(buf, "%x", &port_num); + while (ret) { + if (port_num != 0) + is_master = 1; + else { + is_master = 0; + break; + } + idx += ret; + ret = sscanf(buf + idx, ",%x", &port_num); + } + } else + is_master = 1; +#endif + wmt_i2c3_is_master = is_master; + if (is_master == 1) { +#ifdef USE_UBOOT_PARA + ret = wmt_getsyspara(varname, buf, &varlen); +#else + ret = 1; +#endif + + if (ret == 0) { + ret = sscanf(buf, "%x:%x", &port_num, &speed_mode); + idx += 3; + while (ret) { + if (ret < 2) + speed_mode = 0; + else { + if (port_num != 3) + speed_mode = 0; + else + break; + } + ret = sscanf(buf + idx, ",%x:%x", &port_num, &speed_mode); + idx += 4; + } + } + if (speed_mode > 1) + speed_mode = 0; + wmt_i2c3_speed_mode = speed_mode; + + /**/ + /* software initial*/ + /**/ + i2c.regs = (struct i2c_regs_s *)I2C3_BASE_ADDR ; + i2c.irq_no = IRQ_I2C3 ; + + printk("PORT 3 speed_mode = %d\n", speed_mode); + if (speed_mode == 0) + i2c.i2c_mode = I2C_STANDARD_MODE ; + else if (speed_mode == 1) + i2c.i2c_mode = I2C_FAST_MODE ; + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + /**/ + /* hardware initial*/ + /**/ + auto_pll_divisor(DEV_I2C3, CLK_ENABLE, 0, 0); + pllb_freq = auto_pll_divisor(DEV_I2C3, SET_DIV, 2, 20);/*20M Hz*/ + printk("pllb_freq = %d\n", pllb_freq); + if ((pllb_freq%(1000*2*100)) != 0) + tr_val = pllb_freq/(1000*2*100) + 1; + else + tr_val = pllb_freq/(1000*2*100); + *(volatile unsigned char *)CTRL_GPIO &= ~(BIT0 | BIT1); + *(volatile unsigned char *)PU_EN_GPIO |= (BIT0 | BIT1); + *(volatile unsigned char *)PU_CTRL_GPIO |= (BIT0 | BIT1); + PIN_SHARING_SEL_4BYTE_VAL &= ~BIT28; + i2c.regs->cr_reg = 0 ; + i2c.regs->div_reg = APB_96M_I2C_DIV ; + i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ + i2c.regs->imr_reg = I2C_IMR_ALL_ENABLE ; /* 0x0007*/ + + i2c.regs->cr_reg = I2C_CR_ENABLE ; + tmp = i2c.regs->csr_reg ; /* read clear*/ + i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ + + if (i2c.i2c_mode == I2C_STANDARD_MODE) + i2c.regs->tr_reg = 0xff00|tr_val; + else if (i2c.i2c_mode == I2C_FAST_MODE) { + tr_val /= 4; + i2c.regs->tr_reg = 0xff00|tr_val ; + } + } + + + if (i2c_wmt_resource_init() == 0) { + if (wmt_i2c_add_bus(&i2c_wmt_ops) < 0) { + i2c_wmt_resource_release(); + printk(KERN_INFO "i2c: Failed to add bus\n"); + return -ENODEV; + } + } else + return -ENODEV; + + INIT_LIST_HEAD(&wmt_i2c_fifohead); + +#ifdef CONFIG_PM + register_syscore_ops(&wmt_i2c_syscore_ops); +#endif + + printk(KERN_INFO "i2c: successfully added bus\n"); + +#ifdef I2C_REG_TEST + printk("i2c.regs->cr_reg= 0x%08x\n\r", i2c.regs->cr_reg); + printk("i2c.regs->tcr_reg= 0x%08x\n\r", i2c.regs->tcr_reg); + printk("i2c.regs->csr_reg= 0x%08x\n\r", i2c.regs->csr_reg); + printk("i2c.regs->isr_reg= 0x%08x\n\r", i2c.regs->isr_reg); + printk("i2c.regs->imr_reg= 0x%08x\n\r", i2c.regs->imr_reg); + printk("i2c.regs->cdr_reg= 0x%08x\n\r", i2c.regs->cdr_reg); + printk("i2c.regs->tr_reg= 0x%08x\n\r", i2c.regs->tr_reg); + printk("i2c.regs->div_reg= 0x%08x\n\r", i2c.regs->div_reg); +#endif + + return 0; +} +subsys_initcall(i2c_adap_wmt_init); + +static void i2c_adap_wmt_exit(void) +{ + wmt_i2c_del_bus(&i2c_wmt_ops); + i2c_wmt_resource_release(); + + printk(KERN_INFO "i2c: successfully removed bus\n"); +} + + +MODULE_AUTHOR("WonderMedia Technologies, Inc."); +MODULE_DESCRIPTION("WMT I2C Adapter Driver"); +MODULE_LICENSE("GPL"); + +module_exit(i2c_adap_wmt_exit); + diff --git a/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-bus-4.c b/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-bus-4.c new file mode 100755 index 00000000..72e6531d --- /dev/null +++ b/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-bus-4.c @@ -0,0 +1,1274 @@ +/*++ + drivers/i2c/busses/wmt_i2c_bus-4.c + + Copyright (c) 2013 WonderMedia Technologies, Inc. + + 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, see <http://www.gnu.org/licenses/>. + + WonderMedia Technologies, Inc. + 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. +--*/ +/* Include your headers here*/ +#include <linux/kernel.h> +#include <linux/module.h> + +#include <linux/i2c.h> +/* +#include <linux/i2c-id.h> +*/ +#include <linux/init.h> +#include <linux/time.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/interrupt.h> + +#include <mach/hardware.h> +#include <asm/irq.h> +#include <mach/irqs.h> +#include <mach/wmt-i2c-bus.h> +#include <linux/slab.h> +#include <linux/pm.h> +#include <linux/syscore_ops.h> + +#ifdef __KERNEL__ + +#ifdef DEBUG + #define DPRINTK printk +#else + #define DPRINTK(x...) +#endif + +#else + #define DPRINTK printf + +#endif + + +#define MAX_BUS_READY_CNT 50 /* jiffy*/ +#define MAX_TX_TIMEOUT 500 /* ms*/ +#define MAX_RX_TIMEOUT 500 /* ms*/ + +#define USE_UBOOT_PARA + +struct wmt_i2c_s { + struct i2c_regs_s *regs; + int irq_no ; + enum i2c_mode_e i2c_mode ; + int volatile isr_nack ; + int volatile isr_byte_end ; + int volatile isr_timeout ; + int volatile isr_int_pending ; +}; + +static int i2c_wmt_wait_bus_not_busy(void); +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +static unsigned int speed_mode = 0; +unsigned int wmt_i2c4_speed_mode = 0; +static unsigned int is_master = 1;/*master:1, slave:0*/ +unsigned int wmt_i2c4_is_master = 1; +static unsigned int wmt_i2c4_power_state = 0;/*0:power on, 1:suspend, 2:shutdown*/ +EXPORT_SYMBOL(wmt_i2c4_is_master); + +/**/ +/* variable*/ +/*-------------------------------------------------*/ +static volatile struct wmt_i2c_s i2c ; + +DECLARE_WAIT_QUEUE_HEAD(i2c4_wait); +/* +spinlock_t i2c_wmt_irqlock = SPIN_LOCK_UNLOCKED; +*/ +static DEFINE_SPINLOCK(i2c_wmt_irqlock); +static struct list_head wmt_i2c_fifohead; +/* +static spinlock_t i2c_fifolock = SPIN_LOCK_UNLOCKED; +*/ +static DEFINE_SPINLOCK(i2c_fifolock); +static int i2c_wmt_read_buf( + unsigned int slave_addr, + char *buf, + unsigned int length, + int restart, + int last +); +static int i2c_wmt_write_buf( + unsigned int slave_addr, + char *buf, + unsigned int length, + int restart, + int last +); + +static void i2c_gpio_init(void) +{ + GPIO_CTRL_GP10_I2S_BYTE_VAL &= ~BIT4; + GPIO_CTRL_GP11_I2S_BYTE_VAL &= ~BIT2; + + PULL_EN_GP10_I2S_BYTE_VAL |= BIT4; + PULL_EN_GP11_I2S_BYTE_VAL |= BIT2; + + PULL_CTRL_GP10_I2S_BYTE_VAL |= BIT4; + PULL_CTRL_GP11_I2S_BYTE_VAL |= BIT2; + + PIN_SHARING_SEL_4BYTE_VAL |= BIT21 | BIT22 | BIT13 | BIT14; +} + +static void i2c_wmt_set_mode(enum i2c_mode_e mode /*!<; //[IN] mode */) +{ + if (is_master == 0) + return; + i2c.i2c_mode = mode ; + + if (i2c.i2c_mode == I2C_STANDARD_MODE) { + DPRINTK("I2C: set standard mode \n"); + i2c.regs->tr_reg = I2C_TR_STD_VALUE ; /* 0x8041*/ + } else if (i2c.i2c_mode == I2C_FAST_MODE) { + DPRINTK("I2C: set fast mode \n"); + i2c.regs->tr_reg = I2C_TR_FAST_VALUE ; /* 0x8011*/ + } +} + + +static int i2c_send_request( + struct i2c_msg *msg, + int msg_num, + int non_block, + void (*callback)(void *data), + void *data +) +{ + struct wmt_i2cbusfifo *i2c_fifo_head; + struct i2c_msg *pmsg = NULL; + int ret = 0; + int restart = 0; + int last = 0; + unsigned long flags; + int slave_addr = msg[0].addr; + + if (slave_addr == WMT_I2C_API_I2C_ADDR) + return ret ; + + if (wmt_i2c4_power_state == 2) { + printk("I2C4 has been shutdown\n"); + return -EIO; + } + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + if (non_block == 0) + i2c.isr_int_pending = 0; + + i2c_fifo_head = kzalloc(sizeof(struct wmt_i2cbusfifo), GFP_ATOMIC); + INIT_LIST_HEAD(&i2c_fifo_head->busfifohead); + + pmsg = &msg[0]; + i2c_fifo_head->msg = pmsg; + i2c_fifo_head->msg_num = msg_num; + + spin_lock_irqsave(&i2c_fifolock, flags); + if (list_empty(&wmt_i2c_fifohead)) { + i2c_wmt_wait_bus_not_busy(); + pmsg = &msg[0]; + i2c_fifo_head->xfer_length = 1; + i2c_fifo_head->xfer_msgnum = 0; + i2c_fifo_head->restart = 0; + i2c_fifo_head->non_block = non_block; + + if (non_block == 1) { + i2c_fifo_head->callback = callback; + i2c_fifo_head->data = data; + } else { + i2c_fifo_head->callback = 0; + i2c_fifo_head->data = 0; + } + + list_add_tail(&i2c_fifo_head->busfifohead, &wmt_i2c_fifohead); + if (pmsg->flags & I2C_M_RD) { + i2c_fifo_head->xfer_length = 1; + ret = i2c_wmt_read_buf(pmsg->addr, pmsg->buf, pmsg->len, restart, last); + } else { + i2c_fifo_head->xfer_length = 1; + if (pmsg->flags & I2C_M_NOSTART) + i2c_fifo_head->restart = 1; + else + i2c_fifo_head->restart = 0; + ret = i2c_wmt_write_buf(pmsg->addr, pmsg->buf, pmsg->len, restart, last); + } + + } else { + i2c_fifo_head->xfer_length = 0; + i2c_fifo_head->xfer_msgnum = 0; + i2c_fifo_head->restart = 0; + i2c_fifo_head->non_block = non_block; + if (non_block == 1) { + i2c_fifo_head->callback = callback; + i2c_fifo_head->data = data; + } else { + i2c_fifo_head->callback = 0; + i2c_fifo_head->data = 0; + } + list_add_tail(&i2c_fifo_head->busfifohead, &wmt_i2c_fifohead); + } + spin_unlock_irqrestore(&i2c_fifolock, flags); + if (non_block == 0) { + wait_event(i2c4_wait, i2c.isr_int_pending); + ret = msg_num; + if (i2c.isr_nack == 1) { + DPRINTK("i2c_err : write NACK error (rx) \n\r") ; + ret = -EIO ; + } + if (i2c.isr_timeout == 1) { + DPRINTK("i2c_err : write SCL timeout error (rx)\n\r") ; + ret = -ETIMEDOUT ; + } + + } + + return ret; + + +} +static int i2c_wmt_read_buf( + unsigned int slave_addr, + char *buf, + unsigned int length, + int restart, + int last +) +{ + unsigned short tcr_value; + int ret = 0; + + DPRINTK("[%s]:length = %d , slave_addr = %x\n", __func__, length , slave_addr); + + if (length <=0) + return -1; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ + if (length <=0) + return -1; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + tcr_value = 0 ; + + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + if (length == 1) + i2c.regs->cr_reg |= I2C_CR_TX_NEXT_NO_ACK; /*only 8-bit to read*/ + + i2c.regs->tcr_reg = tcr_value ; + return ret; +} + +static int i2c_wmt_write_buf( + unsigned int slave_addr, + char *buf, + unsigned int length, + int restart, + int last +) +{ + unsigned short tcr_value ; + unsigned int xfer_length ; + int ret = 0 ; + + DPRINTK("[%s]length = %d , slave_addr = %x\n", __func__, length , slave_addr); + if (slave_addr == WMT_I2C_API_I2C_ADDR) + return ret ; + + if (is_master == 0) + return -ENXIO; + + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length < 0) + return -1 ; + xfer_length = 0 ; /* for array index and also for checking counting*/ + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ + + if (length == 0) + i2c.regs->cdr_reg = 0 ; + else + i2c.regs->cdr_reg = (unsigned short)(buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + i2c.regs->tcr_reg = tcr_value ; + + ret = 0 ; + return ret; + +} +static int i2c_wmt_read_msg( + unsigned int slave_addr, /*!<; //[IN] Salve address */ + char *buf, /*!<; //[OUT] Pointer to data */ + unsigned int length, /*!<; //Data length */ + int restart, /*!<; //Need to restart after a complete read */ + int last /*!<; //Last read */ +) +{ + unsigned short tcr_value ; + unsigned int xfer_length ; + int is_timeout ; + int ret = 0 ; + int wait_event_result = 0 ; + + if (is_master == 0) + return -ENXIO; + if (length <= 0) + return -1 ; + xfer_length = 0 ; + + if (restart == 0) + ret = i2c_wmt_wait_bus_not_busy() ; + if (ret < 0) + return ret ; + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ + if (restart == 0) + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) { + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + } else if (i2c.i2c_mode == I2C_FAST_MODE) { + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + } + if (length == 1) + i2c.regs->cr_reg |= I2C_CR_TX_NEXT_NO_ACK; /*only 8-bit to read*/ + + i2c.regs->tcr_reg = tcr_value ; + + /*repeat start case*/ + if (restart == 1) + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + + ret = 0 ; + for (; ;) { + is_timeout = 0 ; + wait_event_result = wait_event_interruptible_timeout(i2c4_wait, i2c.isr_int_pending , + (MAX_RX_TIMEOUT * HZ / 1000)) ; + if (likely(wait_event_result > 0)) { + DPRINTK("I2C: wait interrupted (rx) \n"); + ret = 0 ; + } else if (likely(i2c.isr_int_pending == 0)) { + DPRINTK("I2C: wait timeout (rx) \n"); + is_timeout = 1 ; + ret = -ETIMEDOUT ; + } + + /**/ + /* fail case*/ + /**/ + if (i2c.isr_nack == 1) { + DPRINTK("i2c_err : write NACK error (rx) \n\r") ; + ret = -EIO ; + break ; + } + if (i2c.isr_timeout == 1) { + DPRINTK("i2c_err : write SCL timeout error (rx)\n\r") ; + msleep(10); + ret = -ETIMEDOUT ; + break ; + } + if (is_timeout == 1) { + DPRINTK("i2c_err: write software timeout error (rx) \n\r") ; + ret = -ETIMEDOUT ; + break ; + } + + + /**/ + /* pass case*/ + /**/ + if (i2c.isr_byte_end == 1) { + buf[xfer_length] = (i2c.regs->cdr_reg >> 8) ; + ++xfer_length ; + DPRINTK("i2c_test: received BYTE_END\n\r"); + } + i2c.isr_int_pending = 0; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + + if (length > xfer_length) { + if ((length - 1) == xfer_length) { /* next read is the last one*/ + i2c.regs->cr_reg |= (I2C_CR_TX_NEXT_NO_ACK | I2C_CR_CPU_RDY); + DPRINTK("i2c_test: set CPU_RDY & TX_ACK. next data is last.\r\n"); + } else { + i2c.regs->cr_reg |= I2C_CR_CPU_RDY ; + DPRINTK("i2c_test: more data to read. only set CPU_RDY. \r\n"); + } + } else if (length == xfer_length) { /* end rx xfer*/ + if (last == 1) { /* stop case*/ + DPRINTK("i2c_test: read completed \r\n"); + break ; + } else { /* restart case*/ + /* ??? how to handle the restart after read ?*/ + DPRINTK("i2c_test: RX ReStart Case \r\n") ; + break ; + } + } else { + DPRINTK("i2c_err : read known error\n\r") ; + ret = -EIO ; + break ; + } + } + + DPRINTK("i2c_test: read sequence completed\n\r"); + return ret ; +} + +static int i2c_wmt_write_msg( + unsigned int slave_addr, /*!<; //[IN] Salve address */ + char *buf, /*!<; //[OUT] Pointer to data */ + unsigned int length, /*!<; //Data length */ + int restart, /*!<; //Need to restart after a complete write */ + int last /*!<; //Last read */ +) +{ + unsigned short tcr_value ; + unsigned int xfer_length ; + int is_timeout ; + int ret = 0 ; + int wait_event_result ; + + DPRINTK("length = %d , slave_addr = %x\n", length , slave_addr); + if (slave_addr == WMT_I2C_API_I2C_ADDR) + return ret ; + + if (is_master == 0) + return -ENXIO; + + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length < 0) + return -1 ; + xfer_length = 0 ; /* for array index and also for checking counting*/ + if (restart == 0) + ret = i2c_wmt_wait_bus_not_busy() ; + if (ret < 0) + return ret ; + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + + /**/ + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length == 0) + i2c.regs->cdr_reg = 0 ; + else + i2c.regs->cdr_reg = (unsigned short)(buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + + if (restart == 0) { + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + } + + /**/ + /* I2C: Set transfer mode [standard/fast]*/ + /**/ + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + i2c.regs->tcr_reg = tcr_value ; + + if (restart == 1) + i2c.regs->cr_reg |= I2C_CR_CPU_RDY ; + + ret = 0 ; + for (; ;) { + + is_timeout = 0 ; + /**/ + /* I2C: Wait for interrupt. if ( i2c.isr_int_pending == 1 ) ==> an interrupt exsits.*/ + /**/ + wait_event_result = wait_event_interruptible_timeout(i2c4_wait, i2c.isr_int_pending , (MAX_TX_TIMEOUT * HZ / 1000)) ; + + if (likely(wait_event_result > 0)) { + DPRINTK("I2C: wait interrupted (tx)\n"); + ret = 0 ; + } else if (likely(i2c.isr_int_pending == 0)) { + DPRINTK("I2C: wait timeout (tx) \n"); + is_timeout = 1 ; + ret = -ETIMEDOUT ; + } + + /**/ + /* fail case*/ + /**/ + if (i2c.isr_nack == 1) { + DPRINTK("i2c_err : write NACK error (tx) \n\r") ; + ret = -EIO ; + break ; + } + if (i2c.isr_timeout == 1) { + DPRINTK("i2c_err : write SCL timeout error (tx)\n\r") ; + msleep(10); + ret = -ETIMEDOUT ; + break ; + } + if (is_timeout == 1) { + DPRINTK("i2c_err : write software timeout error (tx)\n\r") ; + ret = -ETIMEDOUT ; + break ; + } + + /**/ + /* pass case*/ + /**/ + if (i2c.isr_byte_end == 1) { + DPRINTK("i2c: isr end byte (tx)\n\r") ; + ++xfer_length ; + } + i2c.isr_int_pending = 0 ; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + + + if ((i2c.regs->csr_reg & I2C_CSR_RCV_ACK_MASK) == I2C_CSR_RCV_NOT_ACK) { + DPRINTK("i2c_err : write RCV NACK error\n\r") ; + ret = -EIO ; + break ; + } + + /**/ + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length == 0) { + i2c.regs->cr_reg = (I2C_CR_TX_END|I2C_CR_CPU_RDY|I2C_CR_ENABLE) ; + break ; + } + if (length > xfer_length) { + i2c.regs->cdr_reg = (unsigned short) (buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + i2c.regs->cr_reg = (I2C_CR_CPU_RDY | I2C_CR_ENABLE) ; + DPRINTK("i2c_test: write register data \n\r") ; + } else if (length == xfer_length) { /* end tx xfer*/ + if (last == 1) { /* stop case*/ + i2c.regs->cr_reg = (I2C_CR_TX_END|I2C_CR_CPU_RDY|I2C_CR_ENABLE) ; + DPRINTK("i2c_test: finish write \n\r") ; + break ; + } else { /* restart case*/ + /* handle the restart for first write then the next is read*/ + i2c.regs->cr_reg = (I2C_CR_ENABLE) ; + DPRINTK("i2c_test: tx restart Case \n\r") ; + break ; + } + } else { + DPRINTK("i2c_err : write unknown error\n\r") ; + ret = -EIO ; + break ; + } + } ; + + DPRINTK("i2c_test: write sequence completed\n\r"); + + return ret ; +} + +static int i2c_wmt_wait_bus_not_busy(void) +{ + int ret ; + int cnt ; + + ret = 0 ; + cnt = 0 ; + while (1) { + if ((REG16_VAL(I2C4_CSR_ADDR) & I2C_STATUS_MASK) == I2C_READY) { + ret = 0; + break ; + } + cnt++ ; + + if (cnt > MAX_BUS_READY_CNT) { + ret = (-EBUSY) ; + printk("i2c_err : wait but not ready time-out\n\r") ; + cnt = 0; + break; + } + } + return ret ; +} + +static void i2c_wmt_reset(void) +{ + unsigned short tmp ; + if (is_master == 0) + return; + + /**/ + /* software initial*/ + /**/ + i2c.regs = (struct i2c_regs_s *)I2C4_BASE_ADDR ; + i2c.irq_no = IRQ_I2C4 ; + if (speed_mode == 0) + i2c.i2c_mode = I2C_STANDARD_MODE ; + else + i2c.i2c_mode = I2C_FAST_MODE ; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + + /**/ + /* hardware initial*/ + /**/ + + i2c.regs->cr_reg = 0 ; + i2c.regs->div_reg = APB_96M_I2C_DIV ; + i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ + i2c.regs->imr_reg = I2C_IMR_ALL_ENABLE ; /* 0x0007*/ + + i2c.regs->cr_reg = I2C_CR_ENABLE ; + tmp = i2c.regs->csr_reg ; /* read clear*/ + i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ + + if (i2c.i2c_mode == I2C_STANDARD_MODE) + i2c.regs->tr_reg = I2C_TR_STD_VALUE ; /* 0x8041*/ + else if (i2c.i2c_mode == I2C_FAST_MODE) + i2c.regs->tr_reg = I2C_TR_FAST_VALUE ; /* 0x8011*/ + + DPRINTK("Resetting I2C Controller Unit\n"); + + return ; +} +static int wmt_i2c_transfer_msg(struct wmt_i2cbusfifo *fifo_head) +{ + int xfer_length = fifo_head->xfer_length; + int xfer_msgnum = fifo_head->xfer_msgnum; + struct i2c_msg *pmsg = &fifo_head->msg[xfer_msgnum]; + int restart = fifo_head->restart; + unsigned short tcr_value; + unsigned short slave_addr = pmsg->addr; + int length = pmsg->len; + int ret = 0; + + if (pmsg->flags & I2C_M_RD) { + if (restart == 0) + i2c_wmt_wait_bus_not_busy(); + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ + if (restart == 0) + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) { + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + } else if (i2c.i2c_mode == I2C_FAST_MODE) { + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + } + if (length == 1) + i2c.regs->cr_reg |= I2C_CR_TX_NEXT_NO_ACK; /*only 8-bit to read*/ + + i2c.regs->tcr_reg = tcr_value ; + + /*repeat start case*/ + if (restart == 1) + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + } else { + if (restart == 0) + i2c_wmt_wait_bus_not_busy(); + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + /**/ + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length == 0) + i2c.regs->cdr_reg = 0 ; + else + i2c.regs->cdr_reg = (unsigned short)(pmsg->buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + + if (restart == 0) { + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + } + + /**/ + /* I2C: Set transfer mode [standard/fast]*/ + /**/ + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + i2c.regs->tcr_reg = tcr_value ; + + if (restart == 1) + i2c.regs->cr_reg |= I2C_CR_CPU_RDY ; + } + return ret; +} + +static irqreturn_t i2c_wmt_handler( + int this_irq, /*!<; //[IN] IRQ number */ + void *dev_id /*!<; //[IN] Pointer to device ID */ +) +{ + int wakeup ; + unsigned short isr_status ; + unsigned short tmp ; + unsigned long flags; + struct wmt_i2cbusfifo *fifo_head; + int xfer_length = 0; + int xfer_msgnum = 0; + struct i2c_msg *pmsg; + volatile unsigned short csr_reg; + + spin_lock_irqsave(&i2c_wmt_irqlock, flags); + isr_status = i2c.regs->isr_reg ; + csr_reg = i2c.regs->csr_reg; + wakeup = 0 ; + fifo_head = list_first_entry(&wmt_i2c_fifohead, struct wmt_i2cbusfifo, busfifohead); + + if (isr_status & I2C_ISR_NACK_ADDR) { + DPRINTK("[%s]:i2c NACK\n", __func__); + /*spin_lock(&i2c_fifolock);*/ + list_del(&fifo_head->busfifohead);/*del request*/ + kfree(fifo_head); + /*spin_unlock(&i2c_fifolock);*/ + xfer_length = 0; + i2c.regs->isr_reg = I2C_ISR_NACK_ADDR_WRITE_CLEAR ; + tmp = i2c.regs->csr_reg ; /* read clear*/ + i2c.isr_nack = 1 ; + wakeup = 1 ; + } + + if ((isr_status & I2C_ISR_BYTE_END && ((csr_reg & I2C_CSR_RCV_ACK_MASK) == I2C_CSR_RCV_NOT_ACK))) { + /* + printk("data rcv nack\n"); + */ + list_del(&fifo_head->busfifohead);/*del request*/ + kfree(fifo_head); + xfer_length = 0; + i2c.regs->isr_reg = I2C_ISR_BYTE_END_WRITE_CLEAR ; + i2c.isr_nack = 1 ; + wakeup = 1 ; + } else if (isr_status & I2C_ISR_BYTE_END) { + i2c.regs->isr_reg = I2C_ISR_BYTE_END_WRITE_CLEAR ; + i2c.isr_byte_end = 1 ; + xfer_length = fifo_head->xfer_length; + xfer_msgnum = fifo_head->xfer_msgnum; + pmsg = &fifo_head->msg[xfer_msgnum]; + + /*read case*/ + if (pmsg->flags & I2C_M_RD) { + pmsg->buf[xfer_length - 1] = (i2c.regs->cdr_reg >> 8) ; + /*the last data in current msg?*/ + if (xfer_length == pmsg->len - 1) { + /*last msg of the current request?*/ + /*spin_lock(&i2c_fifolock);*/ + if (pmsg->flags & I2C_M_NOSTART) { + ++fifo_head->xfer_length; + fifo_head->restart = 1; + /* + ++fifo_head->xfer_msgnum; + */ + i2c.regs->cr_reg |= I2C_CR_CPU_RDY; + } else { + ++fifo_head->xfer_length; + fifo_head->restart = 0; + /* + ++fifo_head->xfer_msgnum; + */ + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY | I2C_CR_TX_NEXT_NO_ACK); + } + /*spin_unlock(&i2c_fifolock);*/ + } else if (xfer_length == pmsg->len) {/*next msg*/ + if (xfer_msgnum < fifo_head->msg_num - 1) { + /*spin_lock(&i2c_fifolock);*/ + fifo_head->xfer_length = 0; + ++fifo_head->xfer_msgnum; + + wmt_i2c_transfer_msg(fifo_head); + ++fifo_head->xfer_length; + /*spin_unlock(&i2c_fifolock);*/ + } else { /*data of this msg has been transfered*/ + /*spin_lock(&i2c_fifolock);*/ + list_del(&fifo_head->busfifohead);/*del request*/ + /*next request exist?*/ + if (list_empty(&wmt_i2c_fifohead)) {/*no more reqeust*/ + if (fifo_head->non_block == 0) { + wakeup = 1; + } else { + fifo_head->callback(fifo_head->data); + } + kfree(fifo_head); + } else { /*more request*/ + if (fifo_head->non_block == 0) { + wakeup = 1; + } else { + fifo_head->callback(fifo_head->data); + } + kfree(fifo_head); + fifo_head = list_first_entry(&wmt_i2c_fifohead, + struct wmt_i2cbusfifo, busfifohead); + /* + if (fifo_head->non_block == 0) + wakeup = 1; + */ + + fifo_head->xfer_length = 0; + wmt_i2c_transfer_msg(fifo_head); + ++fifo_head->xfer_length; + + /* + if (fifo_head->non_block == 0) { + printk("2 : non callback\n"); + wakeup = 1; + } else { + printk("2 :callback\n"); + fifo_head->callback(fifo_head->data); + } + */ + } + /*spin_unlock(&i2c_fifolock);*/ + } + } else {/*next data*/ + /*spin_lock(&i2c_fifolock);*/ + ++fifo_head->xfer_length; + /*spin_unlock(&i2c_fifolock);*/ + i2c.regs->cr_reg |= I2C_CR_CPU_RDY; + } + + } else { /*write case*/ + /*the last data in current msg?*/ + if (xfer_length == pmsg->len) { + /*last msg of the current request?*/ + if (xfer_msgnum < fifo_head->msg_num - 1) { + /*spin_lock(&i2c_fifolock);*/ + if (pmsg->flags & I2C_M_NOSTART) { + ++fifo_head->xfer_length; + fifo_head->restart = 1; + } else { + ++fifo_head->xfer_length; + fifo_head->restart = 0; + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); + udelay(2); + i2c.regs->cr_reg |= (I2C_CR_TX_END); + } + /*access next msg*/ + fifo_head->xfer_length = 0; + ++fifo_head->xfer_msgnum; + + wmt_i2c_transfer_msg(fifo_head); + ++fifo_head->xfer_length; + /*spin_unlock(&i2c_fifolock);*/ + } else {/*this request finish*/ + /*spin_lock(&i2c_fifolock);*/ + /*next request exist?*/ + list_del(&fifo_head->busfifohead);/*del request*/ + if (list_empty(&wmt_i2c_fifohead)) { + /*kfree(fifo_head);*/ + /* + if (fifo_head->non_block == 0) + wakeup = 1; + */ + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); + udelay(2); + i2c.regs->cr_reg |= (I2C_CR_TX_END); + if (fifo_head->non_block == 0) { + wakeup = 1; + } else { + fifo_head->callback(fifo_head->data); + } + kfree(fifo_head); + + } else { + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); + udelay(2); + i2c.regs->cr_reg |= (I2C_CR_TX_END); + if (fifo_head->non_block == 0) { + wakeup = 1; + } else { + fifo_head->callback(fifo_head->data); + } + kfree(fifo_head); + fifo_head = list_first_entry(&wmt_i2c_fifohead, + struct wmt_i2cbusfifo, busfifohead); + /* + if (fifo_head->non_block == 0) + wakeup = 1; + */ + + /*next msg*/ + fifo_head->xfer_length = 0; + ++fifo_head->xfer_msgnum; + wmt_i2c_transfer_msg(fifo_head); + ++fifo_head->xfer_length; + /* + if (fifo_head->non_block == 0) { + printk("4:non callback\n"); + wakeup = 1; + } else { + printk("4:callback\n"); + fifo_head->callback(fifo_head->data); + } + */ + } + /*spin_unlock(&i2c_fifolock);*/ + } + } else {/*next data*/ + i2c.regs->cdr_reg = (unsigned short) (pmsg->buf[fifo_head->xfer_length] & I2C_CDR_DATA_WRITE_MASK); + /*spin_lock(&i2c_fifolock);*/ + ++fifo_head->xfer_length; + /*spin_unlock(&i2c_fifolock);*/ + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY | I2C_CR_ENABLE); + } + } + } + + if (isr_status & I2C_ISR_SCL_TIME_OUT) { + DPRINTK("[%s]SCL timeout\n", __func__); +#if 0 + i2c.regs->cr_reg |= BIT7;/*reset status*/ + /*spin_lock(&i2c_fifolock);*/ + list_del(&fifo_head->busfifohead);/*del request*/ + /*spin_unlock(&i2c_fifolock);*/ + xfer_length = 0; + i2c.regs->isr_reg = I2C_ISR_SCL_TIME_OUT_WRITE_CLEAR | I2C_ISR_BYTE_END_WRITE_CLEAR; + i2c.isr_timeout = 1 ; + wakeup = 1; +#endif + i2c.regs->isr_reg = I2C_ISR_SCL_TIME_OUT_WRITE_CLEAR ; + } + + + if (wakeup) { + /*spin_lock_irqsave(&i2c_wmt_irqlock, flags);*/ + i2c.isr_int_pending = 1; + /*spin_unlock_irqrestore(&i2c_wmt_irqlock, flags);*/ + wake_up(&i2c4_wait); + } else + DPRINTK("i2c_err : unknown I2C ISR Handle 0x%4.4X" , isr_status) ; + spin_unlock_irqrestore(&i2c_wmt_irqlock, flags); + return IRQ_HANDLED; +} + +static int i2c_wmt_resource_init(void) +{ + if (is_master == 0) + return 0; + if (request_irq(i2c.irq_no , &i2c_wmt_handler, IRQF_DISABLED, "i2c", 0) < 0) { + DPRINTK(KERN_INFO "I2C: Failed to register I2C irq %i\n", i2c.irq_no); + return -ENODEV; + } + return 0; +} + +static void i2c_wmt_resource_release(void) +{ + if (is_master == 0) + return; + free_irq(i2c.irq_no, 0); +} + +static struct i2c_algo_wmt_data i2c_wmt_data = { + write_msg: i2c_wmt_write_msg, + read_msg: i2c_wmt_read_msg, + send_request: i2c_send_request, + wait_bus_not_busy: i2c_wmt_wait_bus_not_busy, + reset: i2c_wmt_reset, + set_mode: i2c_wmt_set_mode, + udelay: I2C_ALGO_UDELAY, + timeout: I2C_ALGO_TIMEOUT, +}; + +static struct i2c_adapter i2c_wmt_ops = { + .owner = THIS_MODULE, + /* + .id = I2C_ALGO_WMT, + */ + .algo_data = &i2c_wmt_data, + .name = "wmt_i2c4_adapter", + .retries = I2C_ADAPTER_RETRIES, + .nr = 4, +}; + +#ifdef CONFIG_PM +static struct i2c_regs_s wmt_i2c_reg ; +static void i2c_shutdown(void) +{ + printk("i2c4 shutdown\n"); + wmt_i2c4_power_state = 2; + while (!list_empty(&wmt_i2c_fifohead)) + msleep(1); + while (1) {/*wait busy clear*/ + if ((REG16_VAL(I2C4_CSR_ADDR) & I2C_STATUS_MASK) == I2C_READY) + break ; + msleep(1); + } + return; +} +static int i2c_suspend(void) +{ + printk("i2c4 suspend\n"); + wmt_i2c_reg.imr_reg = i2c.regs->imr_reg; + wmt_i2c_reg.tr_reg = i2c.regs->tr_reg; + wmt_i2c_reg.div_reg = i2c.regs->div_reg; + return 0; +} +static void i2c_resume(void) +{ + printk("i2c4 resume\n"); + /* + GPIO_CTRL_GP17_I2C_BYTE_VAL &= ~(BIT2 | BIT3); + PULL_EN_GP17_I2C_BYTE_VAL |= (BIT2 | BIT3); + PULL_CTRL_GP17_I2C_BYTE_VAL |= (BIT2 | BIT3); + */ + i2c_gpio_init(); + auto_pll_divisor(DEV_I2C4, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_I2C4, SET_DIV, 2, 20);/*20M Hz*/ + + i2c.regs->cr_reg = 0 ; + i2c.regs->div_reg = wmt_i2c_reg.div_reg; + i2c.regs->imr_reg = wmt_i2c_reg.imr_reg; + i2c.regs->tr_reg = wmt_i2c_reg.tr_reg ; + i2c.regs->cr_reg = 0x001 ; +} +#else +#define i2c_suspend NULL +#define i2c_resume NULL +#define i2c_shutdown NULL +#endif +extern int wmt_i2c_add_bus(struct i2c_adapter *); +extern int wmt_i2c_del_bus(struct i2c_adapter *); + +#ifdef CONFIG_PM +static struct syscore_ops wmt_i2c_syscore_ops = +{ + .suspend = i2c_suspend, + .resume = i2c_resume, + .shutdown = i2c_shutdown, +}; +#endif + +static int __init i2c_adap_wmt_init(void) +{ + unsigned short tmp ; + char varname[] = "wmt.i2c.param"; +#ifdef CONFIG_I2C_SLAVE_WMT + char varname1[] = "wmt.bus.i2c.slave_port"; +#endif + unsigned char buf[80]; + int ret; + unsigned int port_num; + int idx = 0; + int varlen = 80; + /*since i2c4 is share pin, it turn on while uboot parameter define i2c4 mode*/ + int i2c_turn_on = 0; + unsigned int pllb_freq = 0; + unsigned int tr_val = 0; + +#ifdef CONFIG_I2C_SLAVE_WMT +#ifdef USE_UBOOT_PARA + ret = wmt_getsyspara(varname1, buf, &varlen); +#else + ret = 1; +#endif + is_master = 1; + if (ret == 0) { + ret = sscanf(buf, "%x", &port_num); + while (ret) { + if (port_num != 4) + is_master = 1; + else { + is_master = 0; + break; + } + idx += ret; + ret = sscanf(buf + idx, ",%x", &port_num); + } + } else + is_master = 1; +#endif + wmt_i2c4_is_master = is_master; + if (is_master == 1) { +#ifdef USE_UBOOT_PARA + ret = wmt_getsyspara(varname, buf, &varlen); +#else + ret = 1; +#endif + + if (ret == 0) { + ret = sscanf(buf, "%x:%x", &port_num, &speed_mode); + idx += 3; + while (ret) { + if (ret < 2) + speed_mode = 0; + else { + if (port_num != 4) + speed_mode = 0; + else { + i2c_turn_on = 1; + break; + } + } + ret = sscanf(buf + idx, ",%x:%x", &port_num, &speed_mode); + idx += 4; + } + } + if (i2c_turn_on == 0) { + printk("I2C4 turn off\n"); + return -EIO; + } + if (speed_mode > 1) + speed_mode = 0; + wmt_i2c4_speed_mode = speed_mode; + + /**/ + /* software initial*/ + /**/ + i2c.regs = (struct i2c_regs_s *)I2C4_BASE_ADDR ; + i2c.irq_no = IRQ_I2C4 ; + printk("PORT 4 speed_mode = %d\n", speed_mode); + if (speed_mode == 0) + i2c.i2c_mode = I2C_STANDARD_MODE ; + else if (speed_mode == 1) + i2c.i2c_mode = I2C_FAST_MODE ; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + /**/ + /* hardware initial*/ + /**/ + auto_pll_divisor(DEV_I2C4, CLK_ENABLE, 0, 0); + pllb_freq = auto_pll_divisor(DEV_I2C4, SET_DIV, 2, 20);/*20M Hz*/ + i2c_gpio_init(); + printk("pllb_freq = %d\n", pllb_freq); + if ((pllb_freq%(1000*2*100)) != 0) + tr_val = pllb_freq/(1000*2*100) + 1; + else + tr_val = pllb_freq/(1000*2*100); + i2c.regs->cr_reg = 0 ; + i2c.regs->div_reg = APB_96M_I2C_DIV ; + i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ + i2c.regs->imr_reg = I2C_IMR_ALL_ENABLE ; /* 0x0007*/ + + i2c.regs->cr_reg = I2C_CR_ENABLE ; + tmp = i2c.regs->csr_reg ; /* read clear*/ + i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ + + if (i2c.i2c_mode == I2C_STANDARD_MODE) + i2c.regs->tr_reg = 0xff00|tr_val; + else if (i2c.i2c_mode == I2C_FAST_MODE) { + tr_val /= 4; + i2c.regs->tr_reg = 0xff00|tr_val ; + } + } + + + if (i2c_wmt_resource_init() == 0) { + if (wmt_i2c_add_bus(&i2c_wmt_ops) < 0) { + i2c_wmt_resource_release(); + printk(KERN_INFO "i2c: Failed to add bus\n"); + return -ENODEV; + } + } else + return -ENODEV; + + INIT_LIST_HEAD(&wmt_i2c_fifohead); + +#ifdef CONFIG_PM + register_syscore_ops(&wmt_i2c_syscore_ops); +#endif + + wmt_i2c4_power_state = 0; + printk(KERN_INFO "i2c: successfully added bus\n"); + +#ifdef I2C_REG_TEST + printk("i2c.regs->cr_reg= 0x%08x\n\r", i2c.regs->cr_reg); + printk("i2c.regs->tcr_reg= 0x%08x\n\r", i2c.regs->tcr_reg); + printk("i2c.regs->csr_reg= 0x%08x\n\r", i2c.regs->csr_reg); + printk("i2c.regs->isr_reg= 0x%08x\n\r", i2c.regs->isr_reg); + printk("i2c.regs->imr_reg= 0x%08x\n\r", i2c.regs->imr_reg); + printk("i2c.regs->cdr_reg= 0x%08x\n\r", i2c.regs->cdr_reg); + printk("i2c.regs->tr_reg= 0x%08x\n\r", i2c.regs->tr_reg); + printk("i2c.regs->div_reg= 0x%08x\n\r", i2c.regs->div_reg); +#endif + + return 0; +} +subsys_initcall(i2c_adap_wmt_init); + +static void i2c_adap_wmt_exit(void) +{ + wmt_i2c_del_bus(&i2c_wmt_ops); + i2c_wmt_resource_release(); + + printk(KERN_INFO "i2c: successfully removed bus\n"); +} + + +MODULE_AUTHOR("WonderMedia Technologies, Inc."); +MODULE_DESCRIPTION("WMT I2C Adapter Driver"); +MODULE_LICENSE("GPL"); + +module_exit(i2c_adap_wmt_exit); + diff --git a/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-bus.c b/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-bus.c new file mode 100755 index 00000000..ecfda642 --- /dev/null +++ b/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-bus.c @@ -0,0 +1,1254 @@ +/*++ + drivers/i2c/busses/wmt-i2c-bus.c + + Copyright (c) 2013 WonderMedia Technologies, Inc. + + 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, see <http://www.gnu.org/licenses/>. + + WonderMedia Technologies, Inc. + 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. +--*/ +/* Include your headers here*/ +#include <linux/kernel.h> +#include <linux/module.h> + +#include <linux/i2c.h> +/* +#include <linux/i2c-id.h> +*/ +#include <linux/init.h> +#include <linux/time.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/interrupt.h> + +#include <mach/hardware.h> +#include <asm/irq.h> +#include <mach/irqs.h> +#include <mach/wmt-i2c-bus.h> +#include <linux/slab.h> +#include <linux/pm.h> +#include <linux/syscore_ops.h> + +#ifdef __KERNEL__ + +#ifdef DEBUG + #define DPRINTK printk +#else + #define DPRINTK(x...) +#endif + +#else + #define DPRINTK printf + +#endif + + +#define MAX_BUS_READY_CNT 50 /* jiffy*/ +#define MAX_TX_TIMEOUT 500 /* ms*/ +#define MAX_RX_TIMEOUT 500 /* ms*/ +#define CTRL_GPIO GPIO_CTRL_GP17_I2C_BYTE_ADDR +#define PU_EN_GPIO PULL_EN_GP17_I2C_BYTE_ADDR +#define PU_CTRL_GPIO PULL_CTRL_GP17_I2C_BYTE_ADDR + +#define USE_UBOOT_PARA + +struct wmt_i2c_s { + struct i2c_regs_s *regs; + int irq_no ; + enum i2c_mode_e i2c_mode ; + int volatile isr_nack ; + int volatile isr_byte_end ; + int volatile isr_timeout ; + int volatile isr_int_pending ; +}; + +static int i2c_wmt_wait_bus_not_busy(void); +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +static unsigned int speed_mode = 0; +static unsigned int is_master = 1;/*master:1, slave:0*/ +unsigned int wmt_i2c0_is_master = 1; +unsigned int wmt_i2c0_speed_mode = 0; +static unsigned int wmt_i2c0_power_state = 0;/*0:power on, 1:suspend, 2:shutdown*/ +EXPORT_SYMBOL(wmt_i2c0_is_master); + +/**/ +/* variable*/ +/*-------------------------------------------------*/ +static volatile struct wmt_i2c_s i2c ; + +DECLARE_WAIT_QUEUE_HEAD(i2c1_wait); +/* +spinlock_t i2c1_wmt_irqlock = SPIN_LOCK_UNLOCKED; +*/ +static DEFINE_SPINLOCK(i2c1_wmt_irqlock); +static struct list_head wmt_i2c_fifohead; +/* +static spinlock_t i2c_fifolock = SPIN_LOCK_UNLOCKED; +*/ +static DEFINE_SPINLOCK(i2c_fifolock); +static int i2c_wmt_read_buf( + unsigned int slave_addr, + char *buf, + unsigned int length, + int restart, + int last +); +static int i2c_wmt_write_buf( + unsigned int slave_addr, + char *buf, + unsigned int length, + int restart, + int last +); + +static void i2c_wmt_set_mode(enum i2c_mode_e mode /*!<; //[IN] mode */) +{ + if (is_master == 0) + return; + i2c.i2c_mode = mode ; + + if (i2c.i2c_mode == I2C_STANDARD_MODE) { + DPRINTK("I2C: set standard mode \n"); + i2c.regs->tr_reg = I2C_TR_STD_VALUE ; /* 0x8041*/ + } else if (i2c.i2c_mode == I2C_FAST_MODE) { + DPRINTK("I2C: set fast mode \n"); + i2c.regs->tr_reg = I2C_TR_FAST_VALUE ; /* 0x8011*/ + } +} + + +static int i2c_send_request( + struct i2c_msg *msg, + int msg_num, + int non_block, + void (*callback)(void *data), + void *data +) +{ + struct wmt_i2cbusfifo *i2c_fifo_head; + struct i2c_msg *pmsg = NULL; + int ret = 0; + int restart = 0; + int last = 0; + unsigned long flags; + int slave_addr = msg[0].addr; + + if (slave_addr == WMT_I2C_API_I2C_ADDR) + return ret ; + if (wmt_i2c0_power_state == 2) { + printk("I2C0 has been shutdown\n"); + return -EIO; + } + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + + i2c_fifo_head = kzalloc(sizeof(struct wmt_i2cbusfifo), GFP_ATOMIC); + INIT_LIST_HEAD(&i2c_fifo_head->busfifohead); + + pmsg = &msg[0]; + i2c_fifo_head->msg = pmsg; + i2c_fifo_head->msg_num = msg_num; + + spin_lock_irqsave(&i2c_fifolock, flags); + if (list_empty(&wmt_i2c_fifohead)) { + i2c_wmt_wait_bus_not_busy(); + pmsg = &msg[0]; + i2c_fifo_head->xfer_length = 1; + i2c_fifo_head->xfer_msgnum = 0; + i2c_fifo_head->restart = 0; + i2c_fifo_head->non_block = non_block; + + if (non_block == 1) { + i2c_fifo_head->callback = callback; + i2c_fifo_head->data = data; + } else { + i2c_fifo_head->callback = 0; + i2c_fifo_head->data = 0; + } + + list_add_tail(&i2c_fifo_head->busfifohead, &wmt_i2c_fifohead); + if (pmsg->flags & I2C_M_RD) { + i2c_fifo_head->xfer_length = 1; + ret = i2c_wmt_read_buf(pmsg->addr, pmsg->buf, pmsg->len, restart, last); + } else { + i2c_fifo_head->xfer_length = 1; + if (pmsg->flags & I2C_M_NOSTART) + i2c_fifo_head->restart = 1; + else + i2c_fifo_head->restart = 0; + ret = i2c_wmt_write_buf(pmsg->addr, pmsg->buf, pmsg->len, restart, last); + } + + } else { + i2c_fifo_head->xfer_length = 0; + i2c_fifo_head->xfer_msgnum = 0; + i2c_fifo_head->restart = 0; + i2c_fifo_head->non_block = non_block; + if (non_block == 1) { + i2c_fifo_head->callback = callback; + i2c_fifo_head->data = data; + } else { + i2c_fifo_head->callback = 0; + i2c_fifo_head->data = 0; + } + list_add_tail(&i2c_fifo_head->busfifohead, &wmt_i2c_fifohead); + } + spin_unlock_irqrestore(&i2c_fifolock, flags); + if (non_block == 0) { + wait_event(i2c1_wait, i2c.isr_int_pending); + ret = msg_num; + if (i2c.isr_nack == 1) { + DPRINTK("i2c_err : write NACK error (rx) \n\r") ; + ret = -EIO ; + } + if (i2c.isr_timeout == 1) { + DPRINTK("i2c_err : write SCL timeout error (rx)\n\r") ; + ret = -ETIMEDOUT ; + } + + } + + return ret; + + +} +static int i2c_wmt_read_buf( + unsigned int slave_addr, + char *buf, + unsigned int length, + int restart, + int last +) +{ + unsigned short tcr_value; + int ret = 0; + + DPRINTK("[%s]:length = %d , slave_addr = %x\n", __func__, length , slave_addr); + + if (length <=0) + return -1; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ + if (length <=0) + return -1; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + tcr_value = 0 ; + + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + if (length == 1) + i2c.regs->cr_reg |= I2C_CR_TX_NEXT_NO_ACK; /*only 8-bit to read*/ + + i2c.regs->tcr_reg = tcr_value ; + return ret; +} + +static int i2c_wmt_write_buf( + unsigned int slave_addr, + char *buf, + unsigned int length, + int restart, + int last +) +{ + unsigned short tcr_value ; + unsigned int xfer_length ; + int ret = 0 ; + + DPRINTK("[%s]length = %d , slave_addr = %x\n", __func__, length , slave_addr); + if (slave_addr == WMT_I2C_API_I2C_ADDR) + return ret ; + + if (is_master == 0) + return -ENXIO; + + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length < 0) + return -1 ; + xfer_length = 0 ; /* for array index and also for checking counting*/ + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ + + if (length == 0) + i2c.regs->cdr_reg = 0 ; + else + i2c.regs->cdr_reg = (unsigned short)(buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + i2c.regs->tcr_reg = tcr_value ; + + ret = 0 ; + return ret; + +} +static int i2c_wmt_read_msg( + unsigned int slave_addr, /*!<; //[IN] Salve address */ + char *buf, /*!<; //[OUT] Pointer to data */ + unsigned int length, /*!<; //Data length */ + int restart, /*!<; //Need to restart after a complete read */ + int last /*!<; //Last read */ +) +{ + unsigned short tcr_value ; + unsigned int xfer_length ; + int is_timeout ; + int ret = 0 ; + int wait_event_result = 0 ; + + if (is_master == 0) + return -ENXIO; + if (length <= 0) + return -1 ; + xfer_length = 0 ; + + if (restart == 0) + ret = i2c_wmt_wait_bus_not_busy() ; + if (ret < 0) + return ret ; + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ + if (restart == 0) + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) { + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + } else if (i2c.i2c_mode == I2C_FAST_MODE) { + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + } + if (length == 1) + i2c.regs->cr_reg |= I2C_CR_TX_NEXT_NO_ACK; /*only 8-bit to read*/ + + i2c.regs->tcr_reg = tcr_value ; + + /*repeat start case*/ + if (restart == 1) + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + + ret = 0 ; + for (; ;) { + is_timeout = 0 ; + wait_event_result = wait_event_interruptible_timeout(i2c1_wait, i2c.isr_int_pending , + (MAX_RX_TIMEOUT * HZ / 1000)) ; + if (likely(wait_event_result > 0)) { + DPRINTK("I2C: wait interrupted (rx) \n"); + ret = 0 ; + } else if (likely(i2c.isr_int_pending == 0)) { + DPRINTK("I2C: wait timeout (rx) \n"); + is_timeout = 1 ; + ret = -ETIMEDOUT ; + } + + /**/ + /* fail case*/ + /**/ + if (i2c.isr_nack == 1) { + DPRINTK("i2c_err : write NACK error (rx) \n\r") ; + ret = -EIO ; + break ; + } + if (i2c.isr_timeout == 1) { + DPRINTK("i2c_err : write SCL timeout error (rx)\n\r") ; + msleep(10); + ret = -ETIMEDOUT ; + break ; + } + if (is_timeout == 1) { + DPRINTK("i2c_err: write software timeout error (rx) \n\r") ; + ret = -ETIMEDOUT ; + break ; + } + + + /**/ + /* pass case*/ + /**/ + if (i2c.isr_byte_end == 1) { + buf[xfer_length] = (i2c.regs->cdr_reg >> 8) ; + ++xfer_length ; + DPRINTK("i2c_test: received BYTE_END\n\r"); + } + i2c.isr_int_pending = 0; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + + if (length > xfer_length) { + if ((length - 1) == xfer_length) { /* next read is the last one*/ + i2c.regs->cr_reg |= (I2C_CR_TX_NEXT_NO_ACK | I2C_CR_CPU_RDY); + DPRINTK("i2c_test: set CPU_RDY & TX_ACK. next data is last.\r\n"); + } else { + i2c.regs->cr_reg |= I2C_CR_CPU_RDY ; + DPRINTK("i2c_test: more data to read. only set CPU_RDY. \r\n"); + } + } else if (length == xfer_length) { /* end rx xfer*/ + if (last == 1) { /* stop case*/ + DPRINTK("i2c_test: read completed \r\n"); + break ; + } else { /* restart case*/ + /* ??? how to handle the restart after read ?*/ + DPRINTK("i2c_test: RX ReStart Case \r\n") ; + break ; + } + } else { + DPRINTK("i2c_err : read known error\n\r") ; + ret = -EIO ; + break ; + } + } + + DPRINTK("i2c_test: read sequence completed\n\r"); + return ret ; +} + +static int i2c_wmt_write_msg( + unsigned int slave_addr, /*!<; //[IN] Salve address */ + char *buf, /*!<; //[OUT] Pointer to data */ + unsigned int length, /*!<; //Data length */ + int restart, /*!<; //Need to restart after a complete write */ + int last /*!<; //Last read */ +) +{ + unsigned short tcr_value ; + unsigned int xfer_length ; + int is_timeout ; + int ret = 0 ; + int wait_event_result ; + + DPRINTK("length = %d , slave_addr = %x\n", length , slave_addr); + if (slave_addr == WMT_I2C_API_I2C_ADDR) + return ret ; + + if (is_master == 0) + return -ENXIO; + + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length < 0) + return -1 ; + xfer_length = 0 ; /* for array index and also for checking counting*/ + if (restart == 0) + ret = i2c_wmt_wait_bus_not_busy() ; + if (ret < 0) + return ret ; + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + + /**/ + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length == 0) + i2c.regs->cdr_reg = 0 ; + else + i2c.regs->cdr_reg = (unsigned short)(buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + + if (restart == 0) { + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + } + + /**/ + /* I2C: Set transfer mode [standard/fast]*/ + /**/ + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + i2c.regs->tcr_reg = tcr_value ; + + if (restart == 1) + i2c.regs->cr_reg |= I2C_CR_CPU_RDY ; + + ret = 0 ; + for (; ;) { + + is_timeout = 0 ; + /**/ + /* I2C: Wait for interrupt. if ( i2c.isr_int_pending == 1 ) ==> an interrupt exsits.*/ + /**/ + wait_event_result = wait_event_interruptible_timeout(i2c1_wait, i2c.isr_int_pending , (MAX_TX_TIMEOUT * HZ / 1000)) ; + + if (likely(wait_event_result > 0)) { + DPRINTK("I2C: wait interrupted (tx)\n"); + ret = 0 ; + } else if (likely(i2c.isr_int_pending == 0)) { + DPRINTK("I2C: wait timeout (tx) \n"); + is_timeout = 1 ; + ret = -ETIMEDOUT ; + } + + /**/ + /* fail case*/ + /**/ + if (i2c.isr_nack == 1) { + DPRINTK("i2c_err : write NACK error (tx) \n\r") ; + ret = -EIO ; + break ; + } + if (i2c.isr_timeout == 1) { + DPRINTK("i2c_err : write SCL timeout error (tx)\n\r") ; + msleep(10); + ret = -ETIMEDOUT ; + break ; + } + if (is_timeout == 1) { + DPRINTK("i2c_err : write software timeout error (tx)\n\r") ; + ret = -ETIMEDOUT ; + break ; + } + + /**/ + /* pass case*/ + /**/ + if (i2c.isr_byte_end == 1) { + DPRINTK("i2c: isr end byte (tx)\n\r") ; + ++xfer_length ; + } + i2c.isr_int_pending = 0 ; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + + + if ((i2c.regs->csr_reg & I2C_CSR_RCV_ACK_MASK) == I2C_CSR_RCV_NOT_ACK) { + DPRINTK("i2c_err : write RCV NACK error\n\r") ; + ret = -EIO ; + break ; + } + + /**/ + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length == 0) { + i2c.regs->cr_reg = (I2C_CR_TX_END|I2C_CR_CPU_RDY|I2C_CR_ENABLE) ; + break ; + } + if (length > xfer_length) { + i2c.regs->cdr_reg = (unsigned short) (buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + i2c.regs->cr_reg = (I2C_CR_CPU_RDY | I2C_CR_ENABLE) ; + DPRINTK("i2c_test: write register data \n\r") ; + } else if (length == xfer_length) { /* end tx xfer*/ + if (last == 1) { /* stop case*/ + i2c.regs->cr_reg = (I2C_CR_TX_END|I2C_CR_CPU_RDY|I2C_CR_ENABLE) ; + DPRINTK("i2c_test: finish write \n\r") ; + break ; + } else { /* restart case*/ + /* handle the restart for first write then the next is read*/ + i2c.regs->cr_reg = (I2C_CR_ENABLE) ; + DPRINTK("i2c_test: tx restart Case \n\r") ; + break ; + } + } else { + DPRINTK("i2c_err : write unknown error\n\r") ; + ret = -EIO ; + break ; + } + } ; + + DPRINTK("i2c_test: write sequence completed\n\r"); + + return ret ; +} + +static int i2c_wmt_wait_bus_not_busy(void) +{ + int ret ; + int cnt ; + + ret = 0 ; + cnt = 0 ; + while (1) { + if ((REG16_VAL(I2C_CSR_ADDR) & I2C_STATUS_MASK) == I2C_READY) { + ret = 0; + break ; + } + cnt++ ; + + if (cnt > MAX_BUS_READY_CNT) { + ret = (-EBUSY) ; + printk("i2c_err 0: wait but not ready time-out\n\r") ; + cnt = 0; + break; //add by rambo d10 always has it 2013-4-14 + } + } + return ret ; +} + +static void i2c_wmt_reset(void) +{ + unsigned short tmp ; + if (is_master == 0) + return; + + /**/ + /* software initial*/ + /**/ + i2c.regs = (struct i2c_regs_s *)I2C0_BASE_ADDR ; + i2c.irq_no = IRQ_I2C0 ; + if (speed_mode == 0) + i2c.i2c_mode = I2C_STANDARD_MODE ; + else + i2c.i2c_mode = I2C_FAST_MODE ; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + + /**/ + /* hardware initial*/ + /**/ + + i2c.regs->cr_reg = 0 ; + i2c.regs->div_reg = APB_96M_I2C_DIV ; + i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ + i2c.regs->imr_reg = I2C_IMR_ALL_ENABLE ; /* 0x0007*/ + + i2c.regs->cr_reg = I2C_CR_ENABLE ; + tmp = i2c.regs->csr_reg ; /* read clear*/ + i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ + + if (i2c.i2c_mode == I2C_STANDARD_MODE) + i2c.regs->tr_reg = I2C_TR_STD_VALUE ; /* 0x8041*/ + else if (i2c.i2c_mode == I2C_FAST_MODE) + i2c.regs->tr_reg = I2C_TR_FAST_VALUE ; /* 0x8011*/ + + DPRINTK("Resetting I2C Controller Unit\n"); + + return ; +} +static int wmt_i2c_transfer_msg(struct wmt_i2cbusfifo *fifo_head) +{ + int xfer_length = fifo_head->xfer_length; + int xfer_msgnum = fifo_head->xfer_msgnum; + struct i2c_msg *pmsg = &fifo_head->msg[xfer_msgnum]; + int restart = fifo_head->restart; + unsigned short tcr_value; + unsigned short slave_addr = pmsg->addr; + int length = pmsg->len; + int ret = 0; + + if (pmsg->flags & I2C_M_RD) { + if (restart == 0) + i2c_wmt_wait_bus_not_busy(); + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ + if (restart == 0) + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) { + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + } else if (i2c.i2c_mode == I2C_FAST_MODE) { + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + } + if (length == 1) + i2c.regs->cr_reg |= I2C_CR_TX_NEXT_NO_ACK; /*only 8-bit to read*/ + + i2c.regs->tcr_reg = tcr_value ; + + /*repeat start case*/ + if (restart == 1) + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + } else { + if (restart == 0) + i2c_wmt_wait_bus_not_busy(); + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /*i2c.isr_int_pending = 0;*/ + + /**/ + /* special case allow length:0, for i2c_smbus_xfer*/ + /**/ + if (length == 0) + i2c.regs->cdr_reg = 0 ; + else + i2c.regs->cdr_reg = (unsigned short)(pmsg->buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + + if (restart == 0) { + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ + } + + /**/ + /* I2C: Set transfer mode [standard/fast]*/ + /**/ + tcr_value = 0 ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + i2c.regs->tcr_reg = tcr_value ; + + if (restart == 1) + i2c.regs->cr_reg |= I2C_CR_CPU_RDY ; + } + return ret; +} + +static irqreturn_t i2c_wmt_handler( + int this_irq, /*!<; //[IN] IRQ number */ + void *dev_id /*!<; //[IN] Pointer to device ID */ +) +{ + int wakeup ; + unsigned short isr_status ; + unsigned short tmp ; + unsigned long flags; + struct wmt_i2cbusfifo *fifo_head; + int xfer_length = 0; + int xfer_msgnum = 0; + struct i2c_msg *pmsg; + volatile unsigned short csr_reg; + + spin_lock_irqsave(&i2c1_wmt_irqlock, flags); + isr_status = i2c.regs->isr_reg ; + csr_reg = i2c.regs->csr_reg; + wakeup = 0 ; + fifo_head = list_first_entry(&wmt_i2c_fifohead, struct wmt_i2cbusfifo, busfifohead); + + if (isr_status & I2C_ISR_NACK_ADDR) { + DPRINTK("[%s]:i2c NACK\n", __func__); + /*spin_lock(&i2c_fifolock);*/ + list_del(&fifo_head->busfifohead);/*del request*/ + kfree(fifo_head); + /*spin_unlock(&i2c_fifolock);*/ + xfer_length = 0; + i2c.regs->isr_reg = I2C_ISR_NACK_ADDR_WRITE_CLEAR ; + tmp = i2c.regs->csr_reg ; /* read clear*/ + i2c.isr_nack = 1 ; + wakeup = 1 ; + } + + if ((isr_status & I2C_ISR_BYTE_END && ((csr_reg & I2C_CSR_RCV_ACK_MASK) == I2C_CSR_RCV_NOT_ACK))) { + /* + printk("data rcv nack\n"); + */ + list_del(&fifo_head->busfifohead);/*del request*/ + kfree(fifo_head); + xfer_length = 0; + i2c.regs->isr_reg = I2C_ISR_BYTE_END_WRITE_CLEAR ; + i2c.isr_nack = 1 ; + wakeup = 1 ; + } else if (isr_status & I2C_ISR_BYTE_END) { + i2c.regs->isr_reg = I2C_ISR_BYTE_END_WRITE_CLEAR ; + i2c.isr_byte_end = 1 ; + xfer_length = fifo_head->xfer_length; + xfer_msgnum = fifo_head->xfer_msgnum; + pmsg = &fifo_head->msg[xfer_msgnum]; + + /*read case*/ + if (pmsg->flags & I2C_M_RD) { + pmsg->buf[xfer_length - 1] = (i2c.regs->cdr_reg >> 8) ; + /*the last data in current msg?*/ + if (xfer_length == pmsg->len - 1) { + /*last msg of the current request?*/ + /*spin_lock(&i2c_fifolock);*/ + if (pmsg->flags & I2C_M_NOSTART) { + ++fifo_head->xfer_length; + fifo_head->restart = 1; + /* + ++fifo_head->xfer_msgnum; + */ + i2c.regs->cr_reg |= I2C_CR_CPU_RDY; + } else { + ++fifo_head->xfer_length; + fifo_head->restart = 0; + /* + ++fifo_head->xfer_msgnum; + */ + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY | I2C_CR_TX_NEXT_NO_ACK); + } + /*spin_unlock(&i2c_fifolock);*/ + } else if (xfer_length == pmsg->len) {/*next msg*/ + if (xfer_msgnum < fifo_head->msg_num - 1) { + /*spin_lock(&i2c_fifolock);*/ + fifo_head->xfer_length = 0; + ++fifo_head->xfer_msgnum; + + wmt_i2c_transfer_msg(fifo_head); + ++fifo_head->xfer_length; + /*spin_unlock(&i2c_fifolock);*/ + } else { /*data of this msg has been transfered*/ + /*spin_lock(&i2c_fifolock);*/ + list_del(&fifo_head->busfifohead);/*del request*/ + /*next request exist?*/ + if (list_empty(&wmt_i2c_fifohead)) {/*no more reqeust*/ + /*kfree(fifo_head);*/ + if (fifo_head->non_block == 0) { + wakeup = 1; + } else { + fifo_head->callback(fifo_head->data); + } + kfree(fifo_head); + } else { /*more request*/ + if (fifo_head->non_block == 0) { + wakeup = 1; + } else { + fifo_head->callback(fifo_head->data); + } + kfree(fifo_head); + fifo_head = list_first_entry(&wmt_i2c_fifohead, + struct wmt_i2cbusfifo, busfifohead); + /* + if (fifo_head->non_block == 0) + wakeup = 1; + */ + + fifo_head->xfer_length = 0; + wmt_i2c_transfer_msg(fifo_head); + ++fifo_head->xfer_length; + + /* + if (fifo_head->non_block == 0) { + printk("2 : non callback\n"); + wakeup = 1; + } else { + printk("2 :callback\n"); + fifo_head->callback(fifo_head->data); + } + */ + } + /*spin_unlock(&i2c_fifolock);*/ + } + } else {/*next data*/ + /*spin_lock(&i2c_fifolock);*/ + ++fifo_head->xfer_length; + /*spin_unlock(&i2c_fifolock);*/ + i2c.regs->cr_reg |= I2C_CR_CPU_RDY; + } + + } else { /*write case*/ + /*the last data in current msg?*/ + if (xfer_length == pmsg->len) { + /*last msg of the current request?*/ + if (xfer_msgnum < fifo_head->msg_num - 1) { + /*spin_lock(&i2c_fifolock);*/ + if (pmsg->flags & I2C_M_NOSTART) { + ++fifo_head->xfer_length; + fifo_head->restart = 1; + } else { + ++fifo_head->xfer_length; + fifo_head->restart = 0; + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); + udelay(2); + i2c.regs->cr_reg |= (I2C_CR_TX_END); + } + /*access next msg*/ + fifo_head->xfer_length = 0; + ++fifo_head->xfer_msgnum; + + wmt_i2c_transfer_msg(fifo_head); + ++fifo_head->xfer_length; + /*spin_unlock(&i2c_fifolock);*/ + } else {/*this request finish*/ + /*spin_lock(&i2c_fifolock);*/ + /*next request exist?*/ + list_del(&fifo_head->busfifohead);/*del request*/ + if (list_empty(&wmt_i2c_fifohead)) { + /*kfree(fifo_head);*/ + /* + if (fifo_head->non_block == 0) + wakeup = 1; + */ + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); + udelay(2); + i2c.regs->cr_reg |= (I2C_CR_TX_END); + if (fifo_head->non_block == 0) { + wakeup = 1; + } else { + fifo_head->callback(fifo_head->data); + } + kfree(fifo_head); + + } else { + i2c.regs->cr_reg &= ~(I2C_CR_TX_END); + udelay(2); + i2c.regs->cr_reg |= (I2C_CR_TX_END); + if (fifo_head->non_block == 0) { + wakeup = 1; + } else { + fifo_head->callback(fifo_head->data); + } + kfree(fifo_head); + fifo_head = list_first_entry(&wmt_i2c_fifohead, + struct wmt_i2cbusfifo, busfifohead); + /* + if (fifo_head->non_block == 0) + wakeup = 1; + */ + + /*next msg*/ + fifo_head->xfer_length = 0; + ++fifo_head->xfer_msgnum; + wmt_i2c_transfer_msg(fifo_head); + ++fifo_head->xfer_length; + /* + if (fifo_head->non_block == 0) { + printk("4:non callback\n"); + wakeup = 1; + } else { + printk("4:callback\n"); + fifo_head->callback(fifo_head->data); + } + */ + } + /*spin_unlock(&i2c_fifolock);*/ + } + } else {/*next data*/ + i2c.regs->cdr_reg = (unsigned short) (pmsg->buf[fifo_head->xfer_length] & I2C_CDR_DATA_WRITE_MASK); + /*spin_lock(&i2c_fifolock);*/ + ++fifo_head->xfer_length; + /*spin_unlock(&i2c_fifolock);*/ + i2c.regs->cr_reg |= (I2C_CR_CPU_RDY | I2C_CR_ENABLE); + } + } + } + + if (isr_status & I2C_ISR_SCL_TIME_OUT) { + DPRINTK("[%s]SCL timeout\n", __func__); +#if 0 + i2c.regs->cr_reg |= BIT7;/*reset status*/ + /*spin_lock(&i2c_fifolock);*/ + list_del(&fifo_head->busfifohead);/*del request*/ + /*spin_unlock(&i2c_fifolock);*/ + xfer_length = 0; + i2c.regs->isr_reg = I2C_ISR_SCL_TIME_OUT_WRITE_CLEAR | I2C_ISR_BYTE_END_WRITE_CLEAR; + i2c.isr_timeout = 1 ; + wakeup = 1; +#endif + i2c.regs->isr_reg = I2C_ISR_SCL_TIME_OUT_WRITE_CLEAR ; + } + + + if (wakeup) { + /*spin_lock_irqsave(&i2c_wmt_irqlock, flags);*/ + i2c.isr_int_pending = 1; + /*spin_unlock_irqrestore(&i2c_wmt_irqlock, flags);*/ + wake_up(&i2c1_wait); + } else + DPRINTK("i2c_err : unknown I2C ISR Handle 0x%4.4X" , isr_status) ; + spin_unlock_irqrestore(&i2c1_wmt_irqlock, flags); + return IRQ_HANDLED; +} + +static int i2c_wmt_resource_init(void) +{ + if (is_master == 0) + return 0; + if (request_irq(i2c.irq_no , &i2c_wmt_handler, IRQF_DISABLED, "i2c", 0) < 0) { + DPRINTK(KERN_INFO "I2C: Failed to register I2C irq %i\n", i2c.irq_no); + return -ENODEV; + } + return 0; +} + +static void i2c_wmt_resource_release(void) +{ + if (is_master == 0) + return; + free_irq(i2c.irq_no, 0); +} + +static struct i2c_algo_wmt_data i2c_wmt_data = { + write_msg: i2c_wmt_write_msg, + read_msg: i2c_wmt_read_msg, + send_request: i2c_send_request, + wait_bus_not_busy: i2c_wmt_wait_bus_not_busy, + reset: i2c_wmt_reset, + set_mode: i2c_wmt_set_mode, + udelay: I2C_ALGO_UDELAY, + timeout: I2C_ALGO_TIMEOUT, +}; + +static struct i2c_adapter i2c_wmt_ops = { + .owner = THIS_MODULE, + /* + .id = I2C_ALGO_WMT, + */ + .algo_data = &i2c_wmt_data, + .name = "wmt_i2c_adapter", + .retries = I2C_ADAPTER_RETRIES, + .nr = 0, +}; + +#ifdef CONFIG_PM +static struct i2c_regs_s wmt_i2c_reg ; +static void i2c_shutdown(void) +{ + printk("i2c0 shutdown\n"); + wmt_i2c0_power_state = 2; + while (!list_empty(&wmt_i2c_fifohead)) + msleep(1); + while (1) {/*wait busy clear*/ + if ((REG16_VAL(I2C_CSR_ADDR) & I2C_STATUS_MASK) == I2C_READY) + break ; + msleep(1); + } + return; +} +static int i2c_suspend(void) +{ + printk("i2c0 suspend\n"); + wmt_i2c_reg.imr_reg = i2c.regs->imr_reg; + wmt_i2c_reg.tr_reg = i2c.regs->tr_reg; + wmt_i2c_reg.div_reg = i2c.regs->div_reg; + return 0; +} +static void i2c_resume(void) +{ + printk("i2c0 resume\n"); + GPIO_CTRL_GP17_I2C_BYTE_VAL &= ~(BIT0 | BIT1); + PULL_EN_GP17_I2C_BYTE_VAL |= (BIT0 | BIT1); + PULL_CTRL_GP17_I2C_BYTE_VAL |= (BIT0 | BIT1); + auto_pll_divisor(DEV_I2C0, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_I2C0, SET_DIV, 2, 20);/*20M Hz*/ + + i2c.regs->cr_reg = 0 ; + i2c.regs->div_reg = wmt_i2c_reg.div_reg; + i2c.regs->imr_reg = wmt_i2c_reg.imr_reg; + i2c.regs->tr_reg = wmt_i2c_reg.tr_reg ; + i2c.regs->cr_reg = 0x001 ; +} +#else +#define i2c_suspend NULL +#define i2c_resume NULL +#define i2c_shutdown NULL +#endif +extern int wmt_i2c_add_bus(struct i2c_adapter *); +extern int wmt_i2c_del_bus(struct i2c_adapter *); + +#ifdef CONFIG_PM +static struct syscore_ops wmt_i2c_syscore_ops = +{ + .suspend = i2c_suspend, + .resume = i2c_resume, + .shutdown = i2c_shutdown, +}; +#endif + +static int __init i2c_adap_wmt_init(void) +{ + unsigned short tmp ; + char varname[] = "wmt.i2c.param"; +#ifdef CONFIG_I2C_SLAVE_WMT + char varname1[] = "wmt.bus.i2c.slave_port"; +#endif + unsigned char buf[80]; + int ret; + unsigned int port_num; + int idx = 0; + int varlen = 80; + unsigned int pllb_freq = 0; + unsigned int tr_val = 0; + +#ifdef CONFIG_I2C_SLAVE_WMT +#ifdef USE_UBOOT_PARA + ret = wmt_getsyspara(varname1, buf, &varlen); +#else + ret = 1; +#endif + is_master = 1; + if (ret == 0) { + ret = sscanf(buf, "%x", &port_num); + while (ret) { + if (port_num != 0) + is_master = 1; + else { + is_master = 0; + break; + } + idx += ret; + ret = sscanf(buf + idx, ",%x", &port_num); + } + } else + is_master = 1; +#endif + wmt_i2c0_is_master = is_master; + if (is_master == 1) { +#ifdef USE_UBOOT_PARA + ret = wmt_getsyspara(varname, buf, &varlen); +#else + ret = 1; +#endif + + if (ret == 0) { + ret = sscanf(buf, "%x:%x", &port_num, &speed_mode); + idx += 3; + while (ret) { + if (ret < 2) + speed_mode = 0; + else { + if (port_num != 0) + speed_mode = 0; + else + break; + } + ret = sscanf(buf + idx, ",%x:%x", &port_num, &speed_mode); + idx += 4; + } + } + if (speed_mode > 1) + speed_mode = 0; + wmt_i2c0_speed_mode = speed_mode; + + /**/ + /* software initial*/ + /**/ + i2c.regs = (struct i2c_regs_s *)I2C0_BASE_ADDR ; + i2c.irq_no = IRQ_I2C0 ; + + printk("PORT 0 speed_mode = %d\n", speed_mode); + if (speed_mode == 0) + i2c.i2c_mode = I2C_STANDARD_MODE ; + else if (speed_mode == 1) + i2c.i2c_mode = I2C_FAST_MODE ; + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + i2c.isr_int_pending = 0; + /**/ + /* hardware initial*/ + /**/ + auto_pll_divisor(DEV_I2C0, CLK_ENABLE, 0, 0); + pllb_freq = auto_pll_divisor(DEV_I2C0, SET_DIV, 2, 20);/*20M Hz*/ + if ((pllb_freq%(1000*2*100)) != 0) + tr_val = pllb_freq/(1000*2*100) + 1; + else + tr_val = pllb_freq/(1000*2*100); + *(volatile unsigned char *)CTRL_GPIO &= ~(BIT0 | BIT1); + *(volatile unsigned char *)PU_EN_GPIO |= (BIT0 | BIT1); + *(volatile unsigned char *)PU_CTRL_GPIO |= (BIT0 | BIT1); + i2c.regs->cr_reg = 0 ; + i2c.regs->div_reg = APB_96M_I2C_DIV ; + i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ + i2c.regs->imr_reg = I2C_IMR_ALL_ENABLE ; /* 0x0007*/ + + i2c.regs->cr_reg = I2C_CR_ENABLE ; + tmp = i2c.regs->csr_reg ; /* read clear*/ + i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ + + if (i2c.i2c_mode == I2C_STANDARD_MODE) + i2c.regs->tr_reg = 0xff00|tr_val; + else if (i2c.i2c_mode == I2C_FAST_MODE) { + tr_val /= 4; + i2c.regs->tr_reg = 0xff00|tr_val ; + } + } + + + if (i2c_wmt_resource_init() == 0) { + if (wmt_i2c_add_bus(&i2c_wmt_ops) < 0) { + i2c_wmt_resource_release(); + printk(KERN_INFO "i2c: Failed to add bus\n"); + return -ENODEV; + } + } else + return -ENODEV; + + INIT_LIST_HEAD(&wmt_i2c_fifohead); + +#ifdef CONFIG_PM + printk("register i2c0 syscore ops\n"); + register_syscore_ops(&wmt_i2c_syscore_ops); +#endif + wmt_i2c0_power_state = 0; + printk(KERN_INFO "i2c: successfully added bus\n"); + +#ifdef I2C_REG_TEST + printk("i2c.regs->cr_reg= 0x%08x\n\r", i2c.regs->cr_reg); + printk("i2c.regs->tcr_reg= 0x%08x\n\r", i2c.regs->tcr_reg); + printk("i2c.regs->csr_reg= 0x%08x\n\r", i2c.regs->csr_reg); + printk("i2c.regs->isr_reg= 0x%08x\n\r", i2c.regs->isr_reg); + printk("i2c.regs->imr_reg= 0x%08x\n\r", i2c.regs->imr_reg); + printk("i2c.regs->cdr_reg= 0x%08x\n\r", i2c.regs->cdr_reg); + printk("i2c.regs->tr_reg= 0x%08x\n\r", i2c.regs->tr_reg); + printk("i2c.regs->div_reg= 0x%08x\n\r", i2c.regs->div_reg); +#endif + + return 0; +} +subsys_initcall(i2c_adap_wmt_init); + +static void i2c_adap_wmt_exit(void) +{ + wmt_i2c_del_bus(&i2c_wmt_ops); + i2c_wmt_resource_release(); + + printk(KERN_INFO "i2c: successfully removed bus\n"); +} + + +MODULE_AUTHOR("WonderMedia Technologies, Inc."); +MODULE_DESCRIPTION("WMT I2C Adapter Driver"); +MODULE_LICENSE("GPL"); + +module_exit(i2c_adap_wmt_exit); diff --git a/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-slave-bus-1.c b/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-slave-bus-1.c new file mode 100755 index 00000000..90b49ea8 --- /dev/null +++ b/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-slave-bus-1.c @@ -0,0 +1,661 @@ +/*++ + drivers/i2c/busses/wmt-i2c-slave-bus-1.c + + Copyright (c) 2008 WonderMedia Technologies, Inc. + + 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, see <http://www.gnu.org/licenses/>. + + WonderMedia Technologies, Inc. + 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. + + History: + 2010/03/15 First Version +--*/ + +#include <linux/config.h> +#define WMT_I2C1_SLAVE_C + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/errno.h> +#include <asm/uaccess.h> +#include <linux/kdev_t.h> +#include <linux/cdev.h> +#include <asm/semaphore.h> +#include <linux/proc_fs.h> + +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <mach/hardware.h> +#include "./wmt-i2c-slave-bus.h" +/*#define DEBUG*/ +#ifdef DEBUG +#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args) +#else +#define DPRINTK(fmt, args...) +#endif + + +#define DEVICE_NAME "WMT_I2C_SLAVE-1" +#define PMC_ClOCK_ENABLE_LOWER 0xd8130250 +#define CTRL_GPIO21 0xD8110055 +#define PU_EN_GPIO21 0xD8110495 +#define PU_CTRL_GPIO21 0xD81104D5 + +#define DISABLE_I2C_SLAVE BIT15 + +struct i2c_slave_msg { + __u16 addr; /* slave address */ + __u16 flags; +#define I2C_M_RD 0x01 + __u16 len; /* data length */ + __u8 *buf; /* pointer to data */ +}; + +struct slave_i2c_dev_s { + /* module parameters */ + char *buf; + + /* char dev struct */ + struct cdev cdev; + struct class *class_slave_i2c; +}; + + +struct wmt_slave_i2c_s { + struct i2c_regs_s *regs; + int irq_no ; + enum i2c_mode_e i2c_mode ; + int isr_nack ; + int isr_byte_end ; + int isr_timeout ; + int isr_int_pending ; + struct compat_semaphore tx_sem; + struct compat_semaphore rx_sem; +}; + +static struct i2c_regs_s regs_backup; +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +static unsigned int is_master = 1;/*master:1, slave:0*/ + +static DEFINE_SPINLOCK(slave_i2c_lock); + +static int slave_i2c_dev_major = SLAVE_I2C_MAJOR; +static int slave_i2c_dev_minor = 1; +static int slave_i2c_dev_nr = 1; +static struct slave_i2c_dev_s slave_i2c_dev; + +static struct wmt_slave_i2c_s slave_i2c_port; + +static unsigned char slave_i2c_addr = 0x31; + +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *slave_i2c_proc; + +static int slave_i2c_reg_read(char *buf, char **start, off_t offset, int len) +{ + char *p = buf; + p += sprintf(p, "reg : value\n"); + p += sprintf(p, "cr : 0x%.4x\n", slave_i2c_port.regs->cr_reg); + p += sprintf(p, "tcr : 0x%.4x\n", slave_i2c_port.regs->tcr_reg); + p += sprintf(p, "csr : 0x%.4x\n", slave_i2c_port.regs->csr_reg); + p += sprintf(p, "isr : 0x%.4x\n", slave_i2c_port.regs->isr_reg); + p += sprintf(p, "imr : 0x%.4x\n", slave_i2c_port.regs->imr_reg); + p += sprintf(p, "cdr : 0x%.4x\n", slave_i2c_port.regs->cdr_reg); + p += sprintf(p, "tr : 0x%.4x\n", slave_i2c_port.regs->tr_reg); + p += sprintf(p, "scr : 0x%.4x\n", slave_i2c_port.regs->scr_reg); + p += sprintf(p, "cssr : 0x%.4x\n", slave_i2c_port.regs->cssr_reg); + p += sprintf(p, "simr : 0x%.4x\n", slave_i2c_port.regs->simr_reg); + p += sprintf(p, "sisr : 0x%.4x\n", slave_i2c_port.regs->sisr_reg); + p += sprintf(p, "csdr : 0x%.4x\n", slave_i2c_port.regs->csdr_reg); + p += sprintf(p, "str : 0x%.4x\n", slave_i2c_port.regs->str_reg); + return p - buf; +} + +static int slave_i2c_addr_read(char *buf, char **start, off_t offset, int len) +{ + char *p = buf; + p += sprintf(p, "i2c-slave address : 0x%.2x\n", slave_i2c_addr); + return p - buf; +} + +#endif + +static irqreturn_t slave_i2c_isr( + int irq, /*!<; // IRQ number */ + void *dev, /*!<; // Private paramater(i.e. pointer of spi port) */ + struct pt_regs *regs /*!<; // interrupt register */ +) +{ + unsigned short isr_status = slave_i2c_port.regs->sisr_reg; + unsigned short ssr_status = slave_i2c_port.regs->cssr_reg; + DPRINTK("slave_i2c isr sisr = %x\n", isr_status); + DPRINTK("slave_i2c isr cssr = %x\n", ssr_status); + if (isr_status & I2C_SISR_DAT_REQ) { + slave_i2c_port.regs->sisr_reg |= I2C_SISR_DAT_REQ_WRITE_CLEAR; + slave_i2c_port.isr_nack = ssr_status & I2C_SRCV_NACK_MASK; + compat_up(&slave_i2c_port.tx_sem); + } else if (isr_status & I2C_SISR_BYTE_END) { + slave_i2c_port.regs->sisr_reg |= I2C_SISR_BYTE_END_WRITE_CLEAR; + slave_i2c_port.isr_byte_end = 1; + slave_i2c_port.isr_nack = ssr_status & I2C_SRCV_NACK_MASK; + compat_up(&slave_i2c_port.rx_sem); + } else if (isr_status & I2C_SISR_SCL_TIME_OUT) { + slave_i2c_port.regs->sisr_reg |= I2C_SISR_SCL_TIME_OUT_WRITE_CLEAR; + slave_i2c_port.isr_timeout = 1 ; + } + return IRQ_HANDLED; +} + +static int wmt_slave_i2c_read_data(unsigned short addr , unsigned char *buf, int size, int flags) +{ + int ret = 0; + int xfer_len = 0; + int sleep_count = 1; + DPRINTK("ssr = %x, isr = %x\n", slave_i2c_port.regs->cssr_reg, slave_i2c_port.regs->sisr_reg); + while (xfer_len < size) { + compat_down_interruptible(&slave_i2c_port.rx_sem); + buf[xfer_len] = ((slave_i2c_port.regs->csdr_reg & I2C_SLAVE_READ_DATA_MASK) >> I2C_SLAVE_READ_DATA_SHIFT); + DPRINTK("data = %x\n", buf[xfer_len]); + xfer_len++; + } + ret = xfer_len; + while (slave_i2c_port.regs->cssr_reg & I2C_SLAVE_ACTIVE) { + if (compat_sema_count(&slave_i2c_port.rx_sem) > 0) {/*receive data was longer than request*/ + ret = -1; + break; + } + msleep(sleep_count); + if (sleep_count < 16) + sleep_count *= 2; + } + DPRINTK("ssr = %x, isr = %x\n", slave_i2c_port.regs->cssr_reg, slave_i2c_port.regs->sisr_reg); + + return ret; +} + +static int wmt_slave_i2c_write_data(unsigned short addr, unsigned char *buf, int size, int flags) +{ + int ret = 0; + int xfer_len = 0; + int sleep_count = 1; + DPRINTK("tx ssr = %x, isr = %x\n", slave_i2c_port.regs->cssr_reg, slave_i2c_port.regs->sisr_reg); + while (xfer_len < size) { + compat_down_interruptible(&slave_i2c_port.tx_sem); + slave_i2c_port.regs->csdr_reg = buf[xfer_len]; + DPRINTK("data = %x\n", buf[xfer_len]); + ++xfer_len; + } + ret = xfer_len; + while (slave_i2c_port.regs->cssr_reg & I2C_SLAVE_ACTIVE) { + msleep(sleep_count); + if (sleep_count < 16) + sleep_count *= 2; + } + DPRINTK("2.tx ssr = %x, isr = %x\n", slave_i2c_port.regs->cssr_reg, slave_i2c_port.regs->sisr_reg); + return ret; +} + +static int slave_i2c_do_xfer(unsigned short addr, unsigned char *buf, int size, int flags) +{ + int ret; + if (flags & I2C_M_RD) + ret = wmt_slave_i2c_read_data(addr , buf, size, flags); + else + ret = wmt_slave_i2c_write_data(addr , buf, size, flags); + return ret; +} + +int wmt_i2cslave_transfer1(struct i2c_slave_msg slave_msg) +{ + int flags = slave_msg.flags; + unsigned char *buf = slave_msg.buf; + unsigned short addr = slave_msg.addr; + int size = slave_msg.len; + int xfer_len; + if (is_master == 1) + return 0; + spin_lock(&slave_i2c_lock); + xfer_len = slave_i2c_do_xfer(addr, buf, size, flags); + spin_unlock(&slave_i2c_lock); + return xfer_len; +} +EXPORT_SYMBOL(wmt_i2cslave_transfer1); + +void wmt_i2cslave_setaddr1(struct i2c_slave_msg msg) +{ + if (is_master == 1) + return; + spin_lock(&slave_i2c_lock); + + slave_i2c_addr = msg.addr; + + if (msg.addr & DISABLE_I2C_SLAVE) + slave_i2c_port.regs->cr_reg &= ~I2C_CR_ENABLE; + else { + slave_i2c_port.regs->cr_reg = 0; + slave_i2c_port.regs->scr_reg = 0; + slave_i2c_port.regs->cr_reg = (I2C_SLAV_MODE_SEL|I2C_CR_ENABLE); + slave_i2c_port.regs->sisr_reg = I2C_SISR_ALL_WRITE_CLEAR; + + if (slave_i2c_port.i2c_mode == I2C_STANDARD_MODE) + slave_i2c_port.regs->scr_reg = slave_i2c_addr; + else if (slave_i2c_port.i2c_mode == I2C_FAST_MODE) + slave_i2c_port.regs->scr_reg = slave_i2c_addr; + else + slave_i2c_port.regs->scr_reg = (slave_i2c_addr|I2C_SLAVE_HS_MODE); + + slave_i2c_port.regs->simr_reg = I2C_SIMR_ALL_ENABLE; + } + spin_unlock(&slave_i2c_lock); +} +EXPORT_SYMBOL(wmt_i2cslave_setaddr1); + +static ssize_t slave_i2c_read( + struct file *filp, + char __user *buf, + size_t count, + loff_t *f_pos +) +{ + int ret = 0; + struct i2c_slave_msg slave_msg; + if (is_master == 1) + return ret; + slave_msg.buf = (char *)kmalloc(count * sizeof(unsigned char), GFP_KERNEL); + slave_msg.flags = 0; + slave_msg.flags |= I2C_M_RD; + slave_msg.len = count; + slave_msg.addr = slave_i2c_addr; + ret = wmt_i2cslave_transfer1(slave_msg); + if (copy_to_user(buf, slave_msg.buf, count)) { + kfree(slave_msg.buf); + return -EFAULT; + } + return ret; +} + +static ssize_t slave_i2c_write( + struct file *filp, + const char __user *buf, + size_t count, + loff_t *f_pos +) +{ + int ret = 0; + struct i2c_slave_msg slave_msg; + if (is_master == 1) + return ret; + slave_msg.buf = (char *)kmalloc(count * sizeof(unsigned char), GFP_KERNEL); + slave_msg.flags = 0; + slave_msg.flags &= ~I2C_M_RD; + slave_msg.len = count; + slave_msg.addr = slave_i2c_addr; + if (copy_from_user(slave_msg.buf, buf, count)) { + kfree(slave_msg.buf); + return -EFAULT; + } + ret = wmt_i2cslave_transfer1(slave_msg); + return ret; /* return Write out data size*/ +} + +static int slave_i2c_open( + struct inode *inode, + struct file *filp +) +{ + struct slave_i2c_dev_s *dev; + char name[40]; + int minor_no; + + dev = container_of(inode->i_cdev, struct slave_i2c_dev_s, cdev); + filp->private_data = dev; + minor_no = iminor(inode); /* get */ + + /* Create user name*/ + memset(name, 0x0, 8); + sprintf(name, "slave-i2c%d", minor_no); + return 0; +} + +static int slave_i2c_release( + struct inode *inode, + struct file *filp +) +{ + struct slave_i2c_dev_s *dev; + int minor_no; + + dev = container_of(inode->i_cdev, struct slave_i2c_dev_s, cdev); + minor_no = iminor(inode); + + return 0; +} + +static int slave_i2c_ioctl( + struct inode *inode, + struct file *filp, + unsigned int cmd, + unsigned long arg +) +{ + int ret = 0; + struct i2c_slave_msg slave_msg[1]; + unsigned char *data_ptr; + unsigned char __user *usr_ptr; + switch (cmd) { + case IOCTL_DO_TRANSFER: + if (copy_from_user(slave_msg, (struct i2c_slave_msg *)arg, + sizeof(struct i2c_slave_msg))) + return -EFAULT; + + data_ptr = (unsigned char *)kmalloc(slave_msg->len*sizeof(unsigned char), GFP_KERNEL); + usr_ptr = (unsigned char __user *)slave_msg->buf; + + if (copy_from_user(data_ptr, (unsigned char *)slave_msg->buf, + slave_msg->len*sizeof(unsigned char))) { + kfree(data_ptr); + return -EFAULT; + } + slave_msg->buf = data_ptr; + + ret = wmt_i2cslave_transfer1((struct i2c_slave_msg)*slave_msg); + + if (slave_msg->flags & I2C_M_RD) { + if (copy_to_user( + usr_ptr, + data_ptr, + slave_msg->len)) + ret = -EFAULT; + } + + kfree(data_ptr); + break; + case IOCTL_SET_ADDR: + if (copy_from_user(slave_msg, (struct i2c_slave_msg *)arg, + sizeof(struct i2c_slave_msg))) + return -EFAULT; + wmt_i2cslave_setaddr1((struct i2c_slave_msg) *slave_msg); + break; + case IOCTL_QUERY_DATA: + break; + case IOCTL_SET_SPEED_MODE: + break; + default: + break; + } + return ret; +} + +/*!************************************************************************* + driver file operations struct define +****************************************************************************/ +static struct file_operations i2c_slave_fops = { + .owner = THIS_MODULE, + .open = slave_i2c_open, + .read = slave_i2c_read, + .write = slave_i2c_write, + .ioctl = slave_i2c_ioctl, + .release = slave_i2c_release, +}; + +static int slave_i2c_probe( + struct device *dev +) +{ + int ret = 0; + dev_t dev_no; + struct cdev *cdev; + char name[40]; + char buf[80]; + unsigned int port_num; + int idx = 0; + char varname1[] = "wmt.bus.i2c.slave_port"; + int ret_val = 0; + int varlen = 80; + memset(name, 0, 40); + + dev_no = MKDEV(slave_i2c_dev_major, slave_i2c_dev_minor); + + sprintf(name, "wmt_i2cslave%d",slave_i2c_dev_minor); + cdev = &slave_i2c_dev.cdev; + cdev_init(cdev, &i2c_slave_fops); + ret = cdev_add(cdev, dev_no, 8); + slave_i2c_dev.class_slave_i2c = class_create(THIS_MODULE, "wmt_i2cslave1"); + device_create(slave_i2c_dev.class_slave_i2c, NULL , + MKDEV(slave_i2c_dev_major,slave_i2c_dev_minor), + NULL, name); + if (ret) { + printk(KERN_ALERT "*E* register char dev \n"); + return ret; + } + +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *res; + + slave_i2c_proc = proc_mkdir("driver/wmt_i2cslave1", NULL); + /* + slave_i2c_proc->owner = THIS_MODULE; + */ + res = create_proc_entry("registers", 0, slave_i2c_proc); + if (res) { + res->read_proc = slave_i2c_reg_read; + } + + res = create_proc_entry("address", 0, slave_i2c_proc); + if (res) { + res->read_proc = slave_i2c_addr_read; + } +#endif + slave_i2c_port.regs = (struct i2c_regs_s *)I2C1_BASE_ADDR; + slave_i2c_port.irq_no = IRQ_I2C1; + slave_i2c_port.i2c_mode = I2C_STANDARD_MODE; + slave_i2c_port.isr_nack = 0; + slave_i2c_port.isr_byte_end = 0; + slave_i2c_port.isr_timeout = 0; + slave_i2c_port.isr_int_pending = 0; + compat_sema_init(&slave_i2c_port.tx_sem, 0); + compat_sema_init(&slave_i2c_port.rx_sem, 0); + ret_val = wmt_getsyspara(varname1, buf, &varlen); + is_master = 1; + if (ret_val == 0) { + ret_val = sscanf(buf, "%x", &port_num); + while (ret_val) { + if (port_num != 1) + is_master = 1; + else { + is_master = 0; + break; + } + idx += ret_val; + ret_val = sscanf(buf + idx, ",%x", &port_num); + } + } else + is_master = 1; + /**/ + /* hardware initial*/ + /**/ + if (is_master == 0) { + *(volatile unsigned int *)PMC_ClOCK_ENABLE_LOWER |= (BIT0); + *(volatile unsigned int *)CTRL_GPIO21 &= ~(BIT2 | BIT3); + *(volatile unsigned int *)PU_EN_GPIO21 |= (BIT2 | BIT3); + *(volatile unsigned int *)PU_CTRL_GPIO21 |= (BIT2 | BIT3); + + /*set i2c slave register*/ + slave_i2c_port.regs->cr_reg = 0; + slave_i2c_port.regs->scr_reg = 0; + slave_i2c_port.regs->cr_reg = (I2C_SLAV_MODE_SEL|I2C_CR_ENABLE); + slave_i2c_port.regs->sisr_reg = I2C_SISR_ALL_WRITE_CLEAR; + + if (slave_i2c_port.i2c_mode == I2C_STANDARD_MODE) + slave_i2c_port.regs->scr_reg = slave_i2c_addr; + else if (slave_i2c_port.i2c_mode == I2C_FAST_MODE) + slave_i2c_port.regs->scr_reg = slave_i2c_addr; + else + slave_i2c_port.regs->scr_reg = (slave_i2c_addr|I2C_SLAVE_HS_MODE); + + slave_i2c_port.regs->simr_reg = I2C_SIMR_ALL_ENABLE; + + slave_i2c_port.regs->cr_reg &= ~I2C_CR_ENABLE; + + if (request_irq(slave_i2c_port.irq_no , &slave_i2c_isr, IRQF_DISABLED, "i2c-slave", 0) < 0) { + DPRINTK(KERN_INFO "I2C-SLAVE: Failed to register I2C-SLAVE irq %i\n", slave_i2c_port.irq_no); + return -ENODEV; + } + } + + return ret; +} + +static int slave_i2c_remove( + struct device *dev /*!<; // please add parameters description her*/ +) +{ + struct cdev *cdev; + + cdev = &slave_i2c_dev.cdev; + cdev_del(cdev); + + return 0; +} /* End of spi_remove() */ + +static void slave_i2c_backup(void) +{ + regs_backup.cr_reg = slave_i2c_port.regs->cr_reg; + regs_backup.tcr_reg = slave_i2c_port.regs->tcr_reg; + regs_backup.scr_reg = slave_i2c_port.regs->scr_reg; + regs_backup.simr_reg = slave_i2c_port.regs->simr_reg; + regs_backup.str_reg = slave_i2c_port.regs->str_reg; + +} + +static void slave_i2c_restore(void) +{ + slave_i2c_port.regs->cr_reg = 0; + slave_i2c_port.regs->cr_reg = regs_backup.cr_reg; + slave_i2c_port.regs->tcr_reg = regs_backup.tcr_reg; + slave_i2c_port.regs->scr_reg = regs_backup.scr_reg; + slave_i2c_port.regs->simr_reg = regs_backup.simr_reg; + slave_i2c_port.regs->str_reg = regs_backup.str_reg; +} + +static int slave_i2c_suspend( + struct device *dev, /*!<; // please add parameters description her*/ + pm_message_t state /*!<; // please add parameters description her*/ +) +{ + if (is_master == 1) + return 0; + slave_i2c_backup(); + compat_sema_init(&slave_i2c_port.tx_sem, 0); + compat_sema_init(&slave_i2c_port.rx_sem, 0); + return 0; +} + + +static int slave_i2c_resume( + struct device *dev +) +{ + if (is_master == 1) + return 0; + *(volatile unsigned int *)PMC_ClOCK_ENABLE_LOWER |= (BIT0); + *(volatile unsigned int *)CTRL_GPIO21 &= ~(BIT2 | BIT3); + *(volatile unsigned int *)PU_EN_GPIO21 |= (BIT2 | BIT3); + *(volatile unsigned int *)PU_CTRL_GPIO21 |= (BIT2 | BIT3); + slave_i2c_restore(); + return 0; +} + +/*!************************************************************************* + device driver struct define +****************************************************************************/ +static struct device_driver slave_i2c_driver = { + .name = "wmt_i2c_slave_1", /* This name should equal to platform device name.*/ + .bus = &platform_bus_type, + .probe = slave_i2c_probe, + .remove = slave_i2c_remove, + .suspend = slave_i2c_suspend, + .resume = slave_i2c_resume +}; + +static void slave_i2c_platform_release( + struct device *device +) +{ +} + +/*!************************************************************************* + platform device struct define +****************************************************************************/ + +static struct platform_device slave_i2c_device = { + .name = "wmt_i2c_slave_1", + .id = 1, + .dev = { .release = slave_i2c_platform_release, + }, + .num_resources = 0, + .resource = NULL, +}; + +static int slave_i2c_init(void) +{ + int ret; + dev_t dev_no; + char dev_name[40]; + memset(dev_name, 0, 40); + sprintf(dev_name, "wmt_i2c_slave%d", slave_i2c_device.id); + + if (slave_i2c_dev_major) { + dev_no = MKDEV(slave_i2c_dev_major, slave_i2c_dev_minor); + ret = register_chrdev_region(dev_no, slave_i2c_dev_nr, dev_name); + } else { + ret = alloc_chrdev_region(&dev_no, slave_i2c_dev_minor, slave_i2c_dev_nr, dev_name); + slave_i2c_dev_major = MAJOR(dev_no); + } + + if (ret < 0) { + printk(KERN_ALERT "*E* can't get major %d\n", slave_i2c_dev_major); + return ret; + } + + platform_device_register(&slave_i2c_device); + ret = driver_register(&slave_i2c_driver); + + return ret; +} + +module_init(slave_i2c_init); + +static void slave_i2c_exit(void) +{ + dev_t dev_no; + + driver_unregister(&slave_i2c_driver); + platform_device_unregister(&slave_i2c_device); + dev_no = MKDEV(slave_i2c_dev_major, slave_i2c_dev_minor); + unregister_chrdev_region(dev_no, slave_i2c_dev_nr); + + return; +} + +module_exit(slave_i2c_exit); + +MODULE_AUTHOR("WMT SW Team"); +MODULE_DESCRIPTION("WMT_slave_i2c device driver"); +MODULE_LICENSE("GPL"); +#undef WMT_I2C_SLAVE_C diff --git a/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-slave-bus.c b/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-slave-bus.c new file mode 100755 index 00000000..547e5ad0 --- /dev/null +++ b/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-slave-bus.c @@ -0,0 +1,662 @@ +/*++ + drivers/i2c/busses/wmt-i2c-slave-bus.c + + Copyright (c) 2008 WonderMedia Technologies, Inc. + + 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, see <http://www.gnu.org/licenses/>. + + WonderMedia Technologies, Inc. + 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. + + History: + 2010/03/11 First Version +--*/ + +#include <linux/config.h> +#define WMT_I2C_SLAVE_C + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/errno.h> +#include <asm/uaccess.h> +#include <linux/kdev_t.h> +#include <linux/cdev.h> +#include <asm/semaphore.h> +#include <linux/proc_fs.h> + +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <mach/hardware.h> +#include "./wmt-i2c-slave-bus.h" +/*#define DEBUG*/ +#ifdef DEBUG + +#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args) +#else +#define DPRINTK(fmt, args...) +#endif + + +#define DEVICE_NAME "WMT_I2C_SLAVE-0" +#define PMC_ClOCK_ENABLE_LOWER 0xd8130250 +#define CTRL_GPIO21 0xD8110055 +#define PU_EN_GPIO21 0xD8110495 +#define PU_CTRL_GPIO21 0xD81104D5 + +#define DISABLE_I2C_SLAVE BIT15 + +struct i2c_slave_msg { + __u16 addr; /* slave address */ + __u16 flags; +#define I2C_M_RD 0x01 + __u16 len; /* data length */ + __u8 *buf; /* pointer to data */ +}; + +struct slave_i2c_dev_s { + /* module parameters */ + char *buf; + + /* char dev struct */ + struct cdev cdev; + struct class *class_slave_i2c; +}; + + +struct wmt_slave_i2c_s { + struct i2c_regs_s *regs; + int irq_no ; + enum i2c_mode_e i2c_mode ; + int isr_nack ; + int isr_byte_end ; + int isr_timeout ; + int isr_int_pending ; + struct compat_semaphore tx_sem; + struct compat_semaphore rx_sem; +}; + +static struct i2c_regs_s regs_backup; +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +static unsigned int is_master = 1;/*master:1, slave:0*/ + +static DEFINE_SPINLOCK(slave_i2c_lock); + +static int slave_i2c_dev_major = SLAVE_I2C_MAJOR; +static int slave_i2c_dev_minor; +static int slave_i2c_dev_nr = 1; +static struct slave_i2c_dev_s slave_i2c_dev; + +static struct wmt_slave_i2c_s slave_i2c_port; + +static unsigned char slave_i2c_addr = 0x31; + +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *slave_i2c_proc; + +static int slave_i2c_reg_read(char *buf, char **start, off_t offset, int len) +{ + char *p = buf; + p += sprintf(p, "reg : value\n"); + p += sprintf(p, "cr : 0x%.4x\n", slave_i2c_port.regs->cr_reg); + p += sprintf(p, "tcr : 0x%.4x\n", slave_i2c_port.regs->tcr_reg); + p += sprintf(p, "csr : 0x%.4x\n", slave_i2c_port.regs->csr_reg); + p += sprintf(p, "isr : 0x%.4x\n", slave_i2c_port.regs->isr_reg); + p += sprintf(p, "imr : 0x%.4x\n", slave_i2c_port.regs->imr_reg); + p += sprintf(p, "cdr : 0x%.4x\n", slave_i2c_port.regs->cdr_reg); + p += sprintf(p, "tr : 0x%.4x\n", slave_i2c_port.regs->tr_reg); + p += sprintf(p, "scr : 0x%.4x\n", slave_i2c_port.regs->scr_reg); + p += sprintf(p, "cssr : 0x%.4x\n", slave_i2c_port.regs->cssr_reg); + p += sprintf(p, "simr : 0x%.4x\n", slave_i2c_port.regs->simr_reg); + p += sprintf(p, "sisr : 0x%.4x\n", slave_i2c_port.regs->sisr_reg); + p += sprintf(p, "csdr : 0x%.4x\n", slave_i2c_port.regs->csdr_reg); + p += sprintf(p, "str : 0x%.4x\n", slave_i2c_port.regs->str_reg); + return p - buf; +} + +static int slave_i2c_addr_read(char *buf, char **start, off_t offset, int len) +{ + char *p = buf; + p += sprintf(p, "i2c-slave address : 0x%.2x\n", slave_i2c_addr); + return p - buf; +} + +#endif + +static irqreturn_t slave_i2c_isr( + int irq, /*!<; // IRQ number */ + void *dev, /*!<; // Private paramater(i.e. pointer of spi port) */ + struct pt_regs *regs /*!<; // interrupt register */ +) +{ + unsigned short isr_status = slave_i2c_port.regs->sisr_reg; + unsigned short ssr_status = slave_i2c_port.regs->cssr_reg; + DPRINTK("slave_i2c isr sisr = %x\n", isr_status); + DPRINTK("slave_i2c isr cssr = %x\n", ssr_status); + if (isr_status & I2C_SISR_DAT_REQ) { + slave_i2c_port.regs->sisr_reg |= I2C_SISR_DAT_REQ_WRITE_CLEAR; + slave_i2c_port.isr_nack = ssr_status & I2C_SRCV_NACK_MASK; + compat_up(&slave_i2c_port.tx_sem); + } else if (isr_status & I2C_SISR_BYTE_END) { + slave_i2c_port.regs->sisr_reg |= I2C_SISR_BYTE_END_WRITE_CLEAR; + slave_i2c_port.isr_byte_end = 1; + slave_i2c_port.isr_nack = ssr_status & I2C_SRCV_NACK_MASK; + compat_up(&slave_i2c_port.rx_sem); + } else if (isr_status & I2C_SISR_SCL_TIME_OUT) { + slave_i2c_port.regs->sisr_reg |= I2C_SISR_SCL_TIME_OUT_WRITE_CLEAR; + slave_i2c_port.isr_timeout = 1 ; + } + return IRQ_HANDLED; +} + +static int wmt_slave_i2c_read_data(unsigned short addr , unsigned char *buf, int size, int flags) +{ + int ret = 0; + int xfer_len = 0; + int sleep_count = 1; + DPRINTK("ssr = %x, isr = %x\n", slave_i2c_port.regs->cssr_reg, slave_i2c_port.regs->sisr_reg); + while (xfer_len < size) { + compat_down_interruptible(&slave_i2c_port.rx_sem); + buf[xfer_len] = ((slave_i2c_port.regs->csdr_reg & I2C_SLAVE_READ_DATA_MASK) >> I2C_SLAVE_READ_DATA_SHIFT); + DPRINTK("data = %x\n", buf[xfer_len]); + xfer_len++; + } + ret = xfer_len; + while (slave_i2c_port.regs->cssr_reg & I2C_SLAVE_ACTIVE) { + if (compat_sema_count(&slave_i2c_port.rx_sem) > 0) {/*receive data was longer than request*/ + ret = -1; + break; + } + msleep(sleep_count); + if (sleep_count < 16) + sleep_count *= 2; + } + DPRINTK("ssr = %x, isr = %x\n", slave_i2c_port.regs->cssr_reg, slave_i2c_port.regs->sisr_reg); + + return ret; +} + +static int wmt_slave_i2c_write_data(unsigned short addr, unsigned char *buf, int size, int flags) +{ + int ret = 0; + int xfer_len = 0; + int sleep_count = 1; + DPRINTK("tx ssr = %x, isr = %x\n", slave_i2c_port.regs->cssr_reg, slave_i2c_port.regs->sisr_reg); + while (xfer_len < size) { + compat_down_interruptible(&slave_i2c_port.tx_sem); + slave_i2c_port.regs->csdr_reg = buf[xfer_len]; + DPRINTK("data = %x\n", buf[xfer_len]); + ++xfer_len; + } + ret = xfer_len; + while (slave_i2c_port.regs->cssr_reg & I2C_SLAVE_ACTIVE) { + msleep(sleep_count); + if (sleep_count < 16) + sleep_count *= 2; + } + DPRINTK("2.tx ssr = %x, isr = %x\n", slave_i2c_port.regs->cssr_reg, slave_i2c_port.regs->sisr_reg); + return ret; +} + +static int slave_i2c_do_xfer(unsigned short addr, unsigned char *buf, int size, int flags) +{ + int ret; + if (flags & I2C_M_RD) + ret = wmt_slave_i2c_read_data(addr , buf, size, flags); + else + ret = wmt_slave_i2c_write_data(addr , buf, size, flags); + return ret; +} + +int wmt_i2cslave_transfer0(struct i2c_slave_msg slave_msg) +{ + int flags = slave_msg.flags; + unsigned char *buf = slave_msg.buf; + unsigned short addr = slave_msg.addr; + int size = slave_msg.len; + int xfer_len; + if (is_master == 1) + return 0; + spin_lock(&slave_i2c_lock); + xfer_len = slave_i2c_do_xfer(addr, buf, size, flags); + spin_unlock(&slave_i2c_lock); + return xfer_len; +} +EXPORT_SYMBOL(wmt_i2cslave_transfer0); + +void wmt_i2cslave_setaddr0(struct i2c_slave_msg msg) +{ + if (is_master == 1) + return; + spin_lock(&slave_i2c_lock); + + slave_i2c_addr = msg.addr; + + if (msg.addr & DISABLE_I2C_SLAVE) + slave_i2c_port.regs->cr_reg &= ~I2C_CR_ENABLE; + else { + slave_i2c_port.regs->cr_reg = 0; + slave_i2c_port.regs->scr_reg = 0; + slave_i2c_port.regs->cr_reg = (I2C_SLAV_MODE_SEL|I2C_CR_ENABLE); + slave_i2c_port.regs->sisr_reg = I2C_SISR_ALL_WRITE_CLEAR; + + if (slave_i2c_port.i2c_mode == I2C_STANDARD_MODE) + slave_i2c_port.regs->scr_reg = slave_i2c_addr; + else if (slave_i2c_port.i2c_mode == I2C_FAST_MODE) + slave_i2c_port.regs->scr_reg = slave_i2c_addr; + else + slave_i2c_port.regs->scr_reg = (slave_i2c_addr|I2C_SLAVE_HS_MODE); + + slave_i2c_port.regs->simr_reg = I2C_SIMR_ALL_ENABLE; + } + spin_unlock(&slave_i2c_lock); +} +EXPORT_SYMBOL(wmt_i2cslave_setaddr0); + +static ssize_t slave_i2c_read( + struct file *filp, + char __user *buf, + size_t count, + loff_t *f_pos +) +{ + int ret = 0; + struct i2c_slave_msg slave_msg; + if (is_master == 1) + return ret; + slave_msg.buf = (char *)kmalloc(count * sizeof(unsigned char), GFP_KERNEL); + slave_msg.flags = 0; + slave_msg.flags |= I2C_M_RD; + slave_msg.len = count; + slave_msg.addr = slave_i2c_addr; + ret = wmt_i2cslave_transfer0(slave_msg); + if (copy_to_user(buf, slave_msg.buf, count)) { + kfree(slave_msg.buf); + return -EFAULT; + } + return ret; +} + +static ssize_t slave_i2c_write( + struct file *filp, + const char __user *buf, + size_t count, + loff_t *f_pos +) +{ + int ret = 0; + struct i2c_slave_msg slave_msg; + if (is_master == 1) + return ret; + slave_msg.buf = (char *)kmalloc(count * sizeof(unsigned char), GFP_KERNEL); + slave_msg.flags = 0; + slave_msg.flags &= ~I2C_M_RD; + slave_msg.len = count; + slave_msg.addr = slave_i2c_addr; + if (copy_from_user(slave_msg.buf, buf, count)) { + kfree(slave_msg.buf); + return -EFAULT; + } + ret = wmt_i2cslave_transfer0(slave_msg); + return ret; /* return Write out data size*/ +} + +static int slave_i2c_open( + struct inode *inode, + struct file *filp +) +{ + struct slave_i2c_dev_s *dev; + char name[40]; + int minor_no; + + dev = container_of(inode->i_cdev, struct slave_i2c_dev_s, cdev); + filp->private_data = dev; + minor_no = iminor(inode); /* get */ + + /* Create user name*/ + memset(name, 0x0, 8); + sprintf(name, "slave-i2c%d", minor_no); + return 0; +} + +static int slave_i2c_release( + struct inode *inode, + struct file *filp +) +{ + struct slave_i2c_dev_s *dev; + int minor_no; + + dev = container_of(inode->i_cdev, struct slave_i2c_dev_s, cdev); + minor_no = iminor(inode); + + return 0; +} + +static int slave_i2c_ioctl( + struct inode *inode, + struct file *filp, + unsigned int cmd, + unsigned long arg +) +{ + int ret = 0; + struct i2c_slave_msg slave_msg[1]; + unsigned char *data_ptr; + unsigned char __user *usr_ptr; + switch (cmd) { + case IOCTL_DO_TRANSFER: + if (copy_from_user(slave_msg, (struct i2c_slave_msg *)arg, + sizeof(struct i2c_slave_msg))) + return -EFAULT; + + data_ptr = (unsigned char *)kmalloc(slave_msg->len*sizeof(unsigned char), GFP_KERNEL); + usr_ptr = (unsigned char __user *)slave_msg->buf; + + if (copy_from_user(data_ptr, (unsigned char *)slave_msg->buf, + slave_msg->len*sizeof(unsigned char))) { + kfree(data_ptr); + return -EFAULT; + } + slave_msg->buf = data_ptr; + + ret = wmt_i2cslave_transfer0((struct i2c_slave_msg)*slave_msg); + + if (slave_msg->flags & I2C_M_RD) { + if (copy_to_user( + usr_ptr, + data_ptr, + slave_msg->len)) + ret = -EFAULT; + } + + kfree(data_ptr); + break; + case IOCTL_SET_ADDR: + if (copy_from_user(slave_msg, (struct i2c_slave_msg *)arg, + sizeof(struct i2c_slave_msg))) + return -EFAULT; + wmt_i2cslave_setaddr0((struct i2c_slave_msg) *slave_msg); + break; + case IOCTL_QUERY_DATA: + break; + case IOCTL_SET_SPEED_MODE: + break; + default: + break; + } + return ret; +} + +/*!************************************************************************* + driver file operations struct define +****************************************************************************/ +static struct file_operations i2c_slave_fops = { + .owner = THIS_MODULE, + .open = slave_i2c_open, + .read = slave_i2c_read, + .write = slave_i2c_write, + .ioctl = slave_i2c_ioctl, + .release = slave_i2c_release, +}; + +static int slave_i2c_probe( + struct device *dev +) +{ + int ret = 0; + dev_t dev_no; + struct cdev *cdev; + char name[40]; + char buf[80]; + unsigned int port_num; + int idx = 0; + char varname1[] = "wmt.bus.i2c.slave_port"; + int ret_val = 0; + int varlen = 80; + memset(name, 0, 40); + + dev_no = MKDEV(slave_i2c_dev_major, slave_i2c_dev_minor); + + sprintf(name, "wmt_i2cslave%d",slave_i2c_dev_minor); + cdev = &slave_i2c_dev.cdev; + cdev_init(cdev, &i2c_slave_fops); + ret = cdev_add(cdev, dev_no, 8); + slave_i2c_dev.class_slave_i2c = class_create(THIS_MODULE, "wmt_i2cslave"); + device_create(slave_i2c_dev.class_slave_i2c, NULL , + MKDEV(slave_i2c_dev_major,slave_i2c_dev_minor), + NULL, name); + if (ret) { + printk(KERN_ALERT "*E* register char dev \n"); + return ret; + } + +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *res; + + slave_i2c_proc = proc_mkdir("driver/wmt_i2cslave0", NULL); + /* + slave_i2c_proc->owner = THIS_MODULE; + */ + res = create_proc_entry("registers", 0, slave_i2c_proc); + if (res) { + res->read_proc = slave_i2c_reg_read; + } + + res = create_proc_entry("address", 0, slave_i2c_proc); + if (res) { + res->read_proc = slave_i2c_addr_read; + } +#endif + slave_i2c_port.regs = (struct i2c_regs_s *)I2C0_BASE_ADDR; + slave_i2c_port.irq_no = IRQ_I2C0; + slave_i2c_port.i2c_mode = I2C_STANDARD_MODE; + slave_i2c_port.isr_nack = 0; + slave_i2c_port.isr_byte_end = 0; + slave_i2c_port.isr_timeout = 0; + slave_i2c_port.isr_int_pending = 0; + compat_sema_init(&slave_i2c_port.tx_sem, 0); + compat_sema_init(&slave_i2c_port.rx_sem, 0); + ret_val = wmt_getsyspara(varname1, buf, &varlen); + is_master = 1; + if (ret_val == 0) { + ret_val = sscanf(buf, "%x", &port_num); + while (ret_val) { + if (port_num != 0) + is_master = 1; + else { + is_master = 0; + break; + } + idx += ret_val; + ret_val = sscanf(buf + idx, ",%x", &port_num); + } + } else + is_master = 1; + /**/ + /* hardware initial*/ + /**/ + if (is_master == 0) { + *(volatile unsigned int *)PMC_ClOCK_ENABLE_LOWER |= (BIT5); + *(volatile unsigned int *)CTRL_GPIO21 &= ~(BIT0 | BIT1); + *(volatile unsigned int *)PU_EN_GPIO21 |= (BIT0 | BIT1); + *(volatile unsigned int *)PU_CTRL_GPIO21 |= (BIT0 | BIT1); + + /*set i2c slave register*/ + slave_i2c_port.regs->cr_reg = 0; + slave_i2c_port.regs->scr_reg = 0; + slave_i2c_port.regs->cr_reg = (I2C_SLAV_MODE_SEL|I2C_CR_ENABLE); + slave_i2c_port.regs->sisr_reg = I2C_SISR_ALL_WRITE_CLEAR; + + if (slave_i2c_port.i2c_mode == I2C_STANDARD_MODE) + slave_i2c_port.regs->scr_reg = slave_i2c_addr; + else if (slave_i2c_port.i2c_mode == I2C_FAST_MODE) + slave_i2c_port.regs->scr_reg = slave_i2c_addr; + else + slave_i2c_port.regs->scr_reg = (slave_i2c_addr|I2C_SLAVE_HS_MODE); + + slave_i2c_port.regs->simr_reg = I2C_SIMR_ALL_ENABLE; + + slave_i2c_port.regs->cr_reg &= ~I2C_CR_ENABLE; + + if (request_irq(slave_i2c_port.irq_no , &slave_i2c_isr, IRQF_DISABLED, "i2c-slave", 0) < 0) { + DPRINTK(KERN_INFO "I2C-SLAVE: Failed to register I2C-SLAVE irq %i\n", slave_i2c_port.irq_no); + return -ENODEV; + } + } + + return ret; +} + +static int slave_i2c_remove( + struct device *dev /*!<; // please add parameters description her*/ +) +{ + struct cdev *cdev; + + cdev = &slave_i2c_dev.cdev; + cdev_del(cdev); + + return 0; +} /* End of spi_remove() */ + +static void slave_i2c_backup(void) +{ + regs_backup.cr_reg = slave_i2c_port.regs->cr_reg; + regs_backup.tcr_reg = slave_i2c_port.regs->tcr_reg; + regs_backup.scr_reg = slave_i2c_port.regs->scr_reg; + regs_backup.simr_reg = slave_i2c_port.regs->simr_reg; + regs_backup.str_reg = slave_i2c_port.regs->str_reg; + +} + +static void slave_i2c_restore(void) +{ + slave_i2c_port.regs->cr_reg = 0; + slave_i2c_port.regs->cr_reg = regs_backup.cr_reg; + slave_i2c_port.regs->tcr_reg = regs_backup.tcr_reg; + slave_i2c_port.regs->scr_reg = regs_backup.scr_reg; + slave_i2c_port.regs->simr_reg = regs_backup.simr_reg; + slave_i2c_port.regs->str_reg = regs_backup.str_reg; +} + +static int slave_i2c_suspend( + struct device *dev, /*!<; // please add parameters description her*/ + pm_message_t state /*!<; // please add parameters description her*/ +) +{ + if (is_master == 1) + return 0; + slave_i2c_backup(); + compat_sema_init(&slave_i2c_port.tx_sem, 0); + compat_sema_init(&slave_i2c_port.rx_sem, 0); + return 0; +} + + +static int slave_i2c_resume( + struct device *dev +) +{ + if (is_master == 1) + return 0; + *(volatile unsigned int *)PMC_ClOCK_ENABLE_LOWER |= (BIT5); + *(volatile unsigned int *)CTRL_GPIO21 &= ~(BIT0 | BIT1); + *(volatile unsigned int *)PU_EN_GPIO21 |= (BIT0 | BIT1); + *(volatile unsigned int *)PU_CTRL_GPIO21 |= (BIT0 | BIT1); + slave_i2c_restore(); + return 0; +} + +/*!************************************************************************* + device driver struct define +****************************************************************************/ +static struct device_driver slave_i2c_driver = { + .name = "wmt_i2c_slave_0", /* This name should equal to platform device name.*/ + .bus = &platform_bus_type, + .probe = slave_i2c_probe, + .remove = slave_i2c_remove, + .suspend = slave_i2c_suspend, + .resume = slave_i2c_resume +}; + +static void slave_i2c_platform_release( + struct device *device +) +{ +} + +/*!************************************************************************* + platform device struct define +****************************************************************************/ + +static struct platform_device slave_i2c_device = { + .name = "wmt_i2c_slave_0", + .id = 0, + .dev = { .release = slave_i2c_platform_release, + }, + .num_resources = 0, + .resource = NULL, +}; + +static int slave_i2c_init(void) +{ + int ret; + dev_t dev_no; + char dev_name[40]; + memset(dev_name, 0, 40); + sprintf(dev_name, "wmt_i2c_slave%d", slave_i2c_device.id); + + if (slave_i2c_dev_major) { + dev_no = MKDEV(slave_i2c_dev_major, slave_i2c_dev_minor); + ret = register_chrdev_region(dev_no, slave_i2c_dev_nr, dev_name); + } else { + ret = alloc_chrdev_region(&dev_no, slave_i2c_dev_minor, slave_i2c_dev_nr, dev_name); + slave_i2c_dev_major = MAJOR(dev_no); + } + + if (ret < 0) { + printk(KERN_ALERT "*E* can't get major %d\n", slave_i2c_dev_major); + return ret; + } + + platform_device_register(&slave_i2c_device); + ret = driver_register(&slave_i2c_driver); + + return ret; +} + +module_init(slave_i2c_init); + +static void slave_i2c_exit(void) +{ + dev_t dev_no; + + driver_unregister(&slave_i2c_driver); + platform_device_unregister(&slave_i2c_device); + dev_no = MKDEV(slave_i2c_dev_major, slave_i2c_dev_minor); + unregister_chrdev_region(dev_no, slave_i2c_dev_nr); + + return; +} + +module_exit(slave_i2c_exit); + +MODULE_AUTHOR("WMT SW Team"); +MODULE_DESCRIPTION("WMT_slave_i2c device driver"); +MODULE_LICENSE("GPL"); +#undef WMT_I2C_SLAVE_C diff --git a/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-slave-bus.h b/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-slave-bus.h new file mode 100755 index 00000000..485823db --- /dev/null +++ b/ANDROID_3.4.5/drivers/i2c/busses/wmt-i2c-slave-bus.h @@ -0,0 +1,24 @@ +/*++ +Copyright (c) 2010 WonderMedia Technologies, Inc. + +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, see http://www.gnu.org/licenses/>. + +WonderMedia Technologies, Inc. +10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. +--*/ + +#define SLAVE_I2C_MAJOR 191 + +#define IOCTL_DO_TRANSFER 0xBF01 +#define IOCTL_SET_ADDR 0xBF02 +#define IOCTL_QUERY_DATA 0xBF03 +#define IOCTL_SET_SPEED_MODE 0xBF04 diff --git a/ANDROID_3.4.5/drivers/i2c/i2c-api.c b/ANDROID_3.4.5/drivers/i2c/i2c-api.c new file mode 100755 index 00000000..d01335bb --- /dev/null +++ b/ANDROID_3.4.5/drivers/i2c/i2c-api.c @@ -0,0 +1,216 @@ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/i2c.h> +#include <linux/slab.h> + +#define I2C_API_FAKE_ADDR 0x7f +#define I2C_MINORS 256 + +struct i2c_api { + struct list_head list; + struct i2c_client *client; +}; + +static LIST_HEAD(i2c_api_list); +static DEFINE_SPINLOCK(i2c_api_list_lock); + +static struct i2c_api *get_i2c_api(int bus_id) +{ + struct i2c_api *i2c_api; + + spin_lock(&i2c_api_list_lock); + list_for_each_entry(i2c_api, &i2c_api_list, list) { + if (i2c_api->client->adapter->nr == bus_id) + goto found; + } + i2c_api = NULL; + +found: + spin_unlock(&i2c_api_list_lock); + return i2c_api; +} + +static struct i2c_api *add_i2c_api(struct i2c_client *client) +{ + struct i2c_api *i2c_api; + + if (client->adapter->nr >= I2C_MINORS) { + printk(KERN_ERR "i2c_api: Out of device minors (%d)\n", + client->adapter->nr); + return NULL; + } + + i2c_api = kzalloc(sizeof(*i2c_api), GFP_KERNEL); + if (!i2c_api) + return NULL; + i2c_api->client = client; + + spin_lock(&i2c_api_list_lock); + list_add_tail(&i2c_api->list, &i2c_api_list); + spin_unlock(&i2c_api_list_lock); + return i2c_api; +} + +static void del_i2c_api(struct i2c_api *i2c_api) +{ + spin_lock(&i2c_api_list_lock); + list_del(&i2c_api->list); + spin_unlock(&i2c_api_list_lock); + kfree(i2c_api); +} + +static int i2c_api_do_xfer(int bus_id, char chip_addr, char sub_addr, int mode, + char *buf, unsigned int size) +{ +/** you could define more transfer mode here, implement it following. */ +#define I2C_API_XFER_MODE_SEND 0x0 /* standard send */ +#define I2C_API_XFER_MODE_RECV 0x1 /* standard receive */ +#define I2C_API_XFER_MODE_SEND_NO_SUBADDR 0x2 /* send without sub-address */ +#define I2C_API_XFER_MODE_RECV_NO_SUBADDR 0x3 /* receive without sub-address */ + + int ret = 0; + char *tmp; + struct i2c_api *i2c_api = get_i2c_api(bus_id); + + if (!i2c_api) + return -ENODEV; + + i2c_api->client->addr = chip_addr; + switch (mode) { + case I2C_API_XFER_MODE_SEND: + tmp = kmalloc(size + 1,GFP_KERNEL); + if (tmp == NULL) + return -ENOMEM; + tmp[0] = sub_addr; + memcpy(&tmp[1], buf, size); + ret = i2c_master_send(i2c_api->client, tmp, size + 1); + ret = (ret == size + 1) ? size : ret; + kfree(tmp); + break; + + case I2C_API_XFER_MODE_RECV: + ret = i2c_master_send(i2c_api->client, &sub_addr, 1); + if (ret < 0) + return ret; + ret = i2c_master_recv(i2c_api->client, buf, size); + break; + + case I2C_API_XFER_MODE_SEND_NO_SUBADDR: + ret = i2c_master_send(i2c_api->client, buf, size); + break; + + case I2C_API_XFER_MODE_RECV_NO_SUBADDR: + ret = i2c_master_recv(i2c_api->client, buf, size); + break; + + default: + return -EINVAL; + } + return ret; +} + +int i2c_api_do_send(int bus_id, char chip_addr, char sub_addr, char *buf, unsigned int size) +{ + return i2c_api_do_xfer(bus_id, chip_addr, sub_addr, I2C_API_XFER_MODE_SEND, buf, size); +} + +int i2c_api_do_recv(int bus_id, char chip_addr, char sub_addr, char *buf, unsigned int size) +{ + return i2c_api_do_xfer(bus_id, chip_addr, sub_addr, I2C_API_XFER_MODE_RECV, buf, size); +} + +int i2c_api_do_send_without_subaddr(int bus_id, char chip_addr, char *buf, unsigned int size) +{ + return i2c_api_do_xfer(bus_id, chip_addr, 0, I2C_API_XFER_MODE_SEND_NO_SUBADDR, buf, size); +} + +int i2c_api_do_recv_without_subaddr(int bus_id, char chip_addr, char *buf, unsigned int size) +{ + return i2c_api_do_xfer(bus_id, chip_addr, 0, I2C_API_XFER_MODE_RECV_NO_SUBADDR, buf, size); +} + +int i2c_api_attach(struct i2c_adapter *adap) +{ + struct i2c_board_info info; + struct i2c_client *client; + + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "i2c_api", I2C_NAME_SIZE); + info.addr = I2C_API_FAKE_ADDR; + client = i2c_new_device(adap, &info); + if (client) + add_i2c_api(client); + printk(KERN_INFO "i2c_api_attach adap[%d]\n", adap->nr); + return 0; +} + +int i2c_api_detach(struct i2c_adapter *adap) +{ + struct i2c_api *i2c_api; + + i2c_api = get_i2c_api(adap->nr); + if (i2c_api) + del_i2c_api(i2c_api); + return 0; +} + +static const unsigned short normal_addr[] = { I2C_API_FAKE_ADDR, I2C_CLIENT_END }; +static const unsigned short ignore[] = { I2C_CLIENT_END }; +/*static struct i2c_client_address_data addr_data = +{ + .normal_i2c = normal_addr, + .probe = ignore, + .ignore = ignore, + .forces = NULL, +};*/ + +static const struct i2c_device_id id[] = { + {"I2C-API", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, id); + +static struct i2c_driver i2c_api_driver = { + .id_table = id, + .attach_adapter = i2c_api_attach, + .detach_adapter = i2c_api_detach, + .command = NULL, + .driver = { + .name = "I2C-API", + .owner = THIS_MODULE, + }, + //.address_data = &addr_data, +}; + +static int __init i2c_api_init(void) +{ + int ret = i2c_add_driver(&i2c_api_driver); + + if (ret) { + printk(KERN_ERR "[%s] Driver registration failed, module not inserted.\n", __func__); + return ret; + } + + return 0 ; +} + +static void __exit i2c_api_exit(void) +{ + i2c_del_driver(&i2c_api_driver); +} + +MODULE_AUTHOR("Loon, <Loonzhong@wondermedia.com.cn>"); +MODULE_DESCRIPTION("I2C i2c_api Driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_api_init); +module_exit(i2c_api_exit); + +EXPORT_SYMBOL_GPL(i2c_api_do_send); +EXPORT_SYMBOL_GPL(i2c_api_do_recv); +EXPORT_SYMBOL_GPL(i2c_api_do_send_without_subaddr); +EXPORT_SYMBOL_GPL(i2c_api_do_recv_without_subaddr); |