#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; | |
} |