blob: e70e91a646c21c443f42025990a7638b96656bcc [file] [log] [blame]
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/of_gpio.h>
#include <linux/clk/zx29-clk.h>
#include <asm/irq.h>
#include <mach/i2c_private.h>
#include <mach/spinlock.h>
#define I2C_FIFO_DEPTH 32
#define MAX_BUS_CLK 400000
#define MIN_BUS_CLK 100000
#define I2C_WCLK_FREQ (26*1000*1000)
#define i2c_if_tx_fifo_empty(d) ((zx_read_reg(d->regs+I2C_TXF_STATUS) & WFIFO_EMPTY)>>1)
#define i2c_if_rx_fifo_full(d) (zx_read_reg(d->regs+I2C_RXF_STATUS) & RFIFO_FULL)
static int zx29_i2c_init(struct zx29_i2c *i2c)
{
unsigned int bus_clk;
unsigned int pdiv;
/*reset*/
zx29_i2c_reset_fifo(i2c);
udelay(1);
/*calculate bus clock division, fix input clock is 26M*/
bus_clk = MIN_BUS_CLK/1000; /*in KHz*/
pdiv = ((I2C_WCLK_FREQ/1000)/4)/bus_clk - 1;
#if 0
i2c->clkrate = (I2C_WCLK_FREQ/4)/(pdiv+1); /*actul bus clock rate*/
if ((i2c->clkrate >MAX_BUS_CLK)||(i2c->clkrate < MIN_BUS_CLK)){
printk(KERN_ERR"bus clock rate error!\n");
BUG();
}
#endif
i2c_set_clk_div(i2c, pdiv);
/*master mode, disable interrupt, clear interrupt*/
i2c_set_cmd(i2c, 0x1);
return 0;
}
static void zx29_i2c_start(struct zx29_i2c *i2c, struct i2c_msg *msg)
{
unsigned int addr = msg->addr;
unsigned long ctrl = 0;
unsigned int addr_mode = 0;
/*set slave device address*/
addr_mode = msg->flags & I2C_M_TEN;
i2c_set_addr(i2c, addr, addr_mode);
/*set cmd register for rx or tx*/
ctrl = i2c_get_cmd(i2c);
if (msg->flags & I2C_M_RD) {
ctrl |= CMD_I2C_RW;
}
else{
ctrl &= ~CMD_I2C_RW;
}
if (addr_mode){
/*10 bit address mode :clear interrupt conditions, set master mode,set 10bit address mode and start*/
ctrl |= CMD_I2C_START|CMD_I2C_MODE|CMD_TENBIT_MODE;
}
else{
/*7 bit address mode:clear interrupt conditions, set master mode and start*/
ctrl &= ~CMD_TENBIT_MODE;
ctrl |= CMD_I2C_START|CMD_I2C_MODE;
}
i2c_set_cmd(i2c, ctrl);
}
static int zx29_i2c_read(struct zx29_i2c *i2c, struct i2c_msg *msg)
{
u8 *buf = msg->buf;
u16 len_rx = 0;
unsigned int ret=0;
u8 val, msg_ptr = 0;
if ((msg->len > 0) && (msg->len <= I2C_FIFO_DEPTH))
len_rx = msg->len;
else
return -EINVAL;
while((ret=i2c_get_bus_status(i2c)));
zx29_i2c_init(i2c); //zx29_i2c_reset_fifo(i2c);
i2c_set_rx_fifo_depth(i2c, len_rx-1);
zx29_i2c_start(i2c, msg);
while(!i2c_if_rx_fifo_full(i2c));
while (len_rx--) {
val = (u8)i2c_read_data(i2c);
*(buf + msg_ptr) = val;
msg_ptr++;
}
return 0;
}
static int zx29_i2c_write(struct zx29_i2c *i2c, struct i2c_msg *msg)
{
u8 val, msg_ptr = 0;
u8 *buf = msg->buf;
u16 len_tx = 0;
unsigned int ret=0;
if ((msg->len > 0) && (msg->len <= I2C_FIFO_DEPTH))
len_tx = msg->len;
else
return -EINVAL;
while((ret=i2c_get_bus_status(i2c)));
zx29_i2c_init(i2c); //zx29_i2c_reset_fifo(i2c);
i2c_set_tx_fifo_depth(i2c, len_tx-1);
/*write data to fifo*/
while(len_tx--){
val = *(buf + msg_ptr);
i2c_write_data(i2c, val);
msg_ptr++;
}
barrier();
zx29_i2c_start(i2c, msg);
while(!i2c_if_tx_fifo_empty(i2c));
return 0;
}
static int zx29_i2c_doxfer(struct zx29_i2c *i2c, struct i2c_msg *msg)
{
int ret;
if (msg->flags & I2C_M_RD) {
ret = zx29_i2c_read(i2c, msg);
} else {
ret = zx29_i2c_write(i2c, msg);
}
return ret;
}
int zx29_i2c_xfer_PSM(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
struct zx29_i2c *i2c = (struct zx29_i2c *)adap->algo_data;
int ret = 0;
int i;
if (msgs->len == 0)
return -EINVAL;
// i2c->msg_num = num;
for (i = 0; i < num; i++) {
ret = zx29_i2c_doxfer(i2c, &msgs[i]);
// i2c->msg_idx=i;
if (ret)
break;
}
return ret ?: i;
}