diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-ast.c')
-rw-r--r-- | drivers/i2c/busses/i2c-ast.c | 1995 |
1 files changed, 1995 insertions, 0 deletions
diff --git a/drivers/i2c/busses/i2c-ast.c b/drivers/i2c/busses/i2c-ast.c new file mode 100644 index 0000000..22ce167 --- /dev/null +++ b/drivers/i2c/busses/i2c-ast.c @@ -0,0 +1,1995 @@ +/* + * i2c_adap_ast.c + * + * I2C adapter for the ASPEED I2C bus access. + * + * Copyright (C) 2012-2020 ASPEED Technology Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * History: + * 2012.07.26: Initial version [Ryan Chen] + */ +#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/delay.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/completion.h> + +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/clk.h> + +#include <linux/dma-mapping.h> + +#include <asm/irq.h> +#include <asm/io.h> + +#if defined(CONFIG_COLDFIRE) +#include <asm/arch/regs-iic.h> +#include <asm/arch/ast_i2c.h> +#else +#include <plat/regs-iic.h> +#include <plat/ast_i2c.h> +#endif + +//AST2400 buffer mode issue , force I2C slave write use byte mode , read use buffer mode +/* Use platform_data instead of module parameters */ +/* Fast Mode = 400 kHz, Standard = 100 kHz */ +//static int clock = 100; /* Default: 100 kHz */ + + +/***************************************************************************/ + +#ifdef CONFIG_AST_I2C_SLAVE_RDWR +#define I2C_S_BUF_SIZE 256 +#define I2C_S_RX_BUF_NUM 20 +#define BUFF_FULL 0xff00 +#define BUFF_ONGOING 1 +#endif + +#define AST_LOCKUP_DETECTED (0x1 << 15) + +// Enable SCL/SDA pull LOW detection for Yosemite platform +#ifdef CONFIG_YOSEMITE +#define AST_I2C_LOW_TIMEOUT 0x07 +#else +#define AST_I2C_LOW_TIMEOUT 0x00 +#endif //CONFIG_YOSEMITE + + +struct ast_i2c_dev { + struct ast_i2c_driver_data *ast_i2c_data; + struct device *dev; + void __iomem *reg_base; /* virtual */ + int irq; //I2C IRQ number + u32 bus_id; //for i2c dev# IRQ number check + u32 state; //I2C xfer mode state matchine + struct i2c_adapter adap; + struct buf_page *req_page; +//dma or buff mode needed + unsigned char *dma_buf; + dma_addr_t dma_addr; + +//master + int xfer_last; //cur xfer is last msgs for stop msgs + struct i2c_msg *master_msgs; //cur xfer msgs + int master_xfer_len; //cur xfer len + int master_xfer_cnt; //total xfer count + u32 master_xfer_mode; //cur xfer mode ... 0 : no_op , master: 1 byte , 2 : buffer , 3: dma , slave : xxxx + struct completion cmd_complete; + int cmd_err; + u8 blk_r_flag; //for smbus block read + void (*do_master_xfer)(struct ast_i2c_dev *i2c_dev); + spinlock_t master_lock; +//Slave structure + u8 slave_operation; + u8 slave_event; + struct i2c_msg *slave_msgs; //cur slave xfer msgs + int slave_xfer_len; + int slave_xfer_cnt; + u32 slave_xfer_mode; //cur xfer mode ... 0 : no_op , master: 1 byte , 2 : buffer , 3: dma , slave : xxxx + void (*do_slave_xfer)(struct ast_i2c_dev *i2c_dev); +#ifdef CONFIG_AST_I2C_SLAVE_RDWR + struct i2c_msg slave_rx_msg[I2C_S_RX_BUF_NUM + 1]; + struct i2c_msg slave_tx_msg; + spinlock_t slave_rx_lock; +#endif +}; + + + +static inline void +ast_i2c_write(struct ast_i2c_dev *i2c_dev, u32 val, u32 reg) +{ +// dev_dbg(i2c_dev->dev, "ast_i2c_write : val: %x , reg : %x \n",val,reg); + writel(val, i2c_dev->reg_base+ reg); +} + +static inline u32 +ast_i2c_read(struct ast_i2c_dev *i2c_dev, u32 reg) +{ +#if 0 + u32 val = readl(i2c_dev->reg_base + reg); + printk("R : reg %x , val: %x \n",reg, val); + return val; +#else + return readl(i2c_dev->reg_base + reg); +#endif +} + +static u32 select_i2c_clock(struct ast_i2c_dev *i2c_dev) +{ + + unsigned int clk, inc = 0, div, divider_ratio; + u32 SCL_Low, SCL_High, data; + + clk = i2c_dev->ast_i2c_data->get_i2c_clock(); +// printk("pclk = %d \n",clk); + divider_ratio = clk / i2c_dev->ast_i2c_data->bus_clk; + for (div = 0; divider_ratio >= 16; div++) + { + inc |= (divider_ratio & 1); + divider_ratio >>= 1; + } + divider_ratio += inc; + SCL_Low = (divider_ratio >> 1) - 1; + SCL_High = divider_ratio - SCL_Low - 2; + data = 0x77700300 | (SCL_High << 16) | (SCL_Low << 12) | div; +// printk("I2CD04 for %d = %08X\n", target_speed, data); + return data; +} + +#ifdef CONFIG_AST_I2C_SLAVE_MODE +/* AST I2C Slave mode */ +static void ast_slave_issue_alert(struct ast_i2c_dev *i2c_dev, u8 enable) +{ + //only support dev0~3 + if(i2c_dev->bus_id > 3) + return; + else { + if(enable) + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_CMD_REG) | AST_I2CD_S_ALT_EN, I2C_CMD_REG); + else + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_CMD_REG) & ~AST_I2CD_S_ALT_EN, I2C_CMD_REG); + } +} + +static void ast_slave_mode_enable(struct ast_i2c_dev *i2c_dev, struct i2c_msg *msgs) +{ + if(msgs->buf[0] == 1) { + ast_i2c_write(i2c_dev, msgs->addr, I2C_DEV_ADDR_REG); + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_FUN_CTRL_REG) | AST_I2CD_SLAVE_EN, I2C_FUN_CTRL_REG); + } else + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_FUN_CTRL_REG) & ~AST_I2CD_SLAVE_EN, I2C_FUN_CTRL_REG); +} + +#endif + +static void ast_i2c_dev_init(struct ast_i2c_dev *i2c_dev) +{ + //I2CG Reset + ast_i2c_write(i2c_dev, 0, I2C_FUN_CTRL_REG); + +#ifdef CONFIG_AST_I2C_SLAVE_MODE +#ifdef CONFIG_AST_I2C_SLAVE_EEPROM + i2c_dev->ast_i2c_data->slave_init(&(i2c_dev->slave_msgs)); + ast_slave_mode_enable(i2c_dev, i2c_dev->slave_msgs); +#else + i2c_dev->slave_msgs = i2c_dev->slave_rx_msg; +#endif +#endif + + //Enable Master Mode + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev, I2C_FUN_CTRL_REG) | AST_I2CD_MASTER_EN, I2C_FUN_CTRL_REG); + + + /* Set AC Timing */ +#if defined(CONFIG_ARCH_AST2400) + if(i2c_dev->ast_i2c_data->bus_clk/1000 > 400) { + printk("high speed mode enable clk [%dkhz]\n",i2c_dev->ast_i2c_data->bus_clk/1000); + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev, I2C_FUN_CTRL_REG) | + AST_I2CD_M_HIGH_SPEED_EN | + AST_I2CD_M_SDA_DRIVE_1T_EN | + AST_I2CD_SDA_DRIVE_1T_EN + , I2C_FUN_CTRL_REG); + + /* Set AC Timing */ + ast_i2c_write(i2c_dev, AST_I2C_LOW_TIMEOUT, I2C_AC_TIMING_REG2); + ast_i2c_write(i2c_dev, select_i2c_clock(i2c_dev), I2C_AC_TIMING_REG1); + }else { + /* target apeed is xxKhz*/ + ast_i2c_write(i2c_dev, select_i2c_clock(i2c_dev), I2C_AC_TIMING_REG1); + ast_i2c_write(i2c_dev, AST_I2C_LOW_TIMEOUT, I2C_AC_TIMING_REG2); + } +#else + /* target apeed is xxKhz*/ + ast_i2c_write(i2c_dev, select_i2c_clock(i2c_dev), I2C_AC_TIMING_REG1); + ast_i2c_write(i2c_dev, AST_NO_TIMEOUT_CTRL, I2C_AC_TIMING_REG2); +#endif +// ast_i2c_write(i2c_dev, 0x77743335, I2C_AC_TIMING_REG1); +///// + + + //Clear Interrupt + ast_i2c_write(i2c_dev, 0xfffffff, I2C_INTR_STS_REG); + + //TODO +// ast_i2c_write(i2c_dev, 0xAF, I2C_INTR_CTRL_REG); + //Enable Interrupt, STOP Interrupt has bug in AST2000 + + /* Set interrupt generation of I2C controller */ + ast_i2c_write(i2c_dev, + AST_I2CD_SDA_DL_TO_INTR_EN | + AST_I2CD_BUS_RECOVER_INTR_EN | + AST_I2CD_SMBUS_ALT_INTR_EN | +// AST_I2CD_SLAVE_MATCH_INTR_EN | + AST_I2CD_SCL_TO_INTR_EN | + AST_I2CD_ABNORMAL_INTR_EN | + AST_I2CD_NORMAL_STOP_INTR_EN | + AST_I2CD_ARBIT_LOSS_INTR_EN | + AST_I2CD_RX_DOWN_INTR_EN | + AST_I2CD_TX_NAK_INTR_EN | + AST_I2CD_TX_ACK_INTR_EN, + I2C_INTR_CTRL_REG); + +} + +#ifdef CONFIG_AST_I2C_SLAVE_RDWR +//for memory buffer initial +static void ast_i2c_slave_buff_init(struct ast_i2c_dev *i2c_dev) +{ + int i; + //Tx buf 1 + i2c_dev->slave_tx_msg.len = I2C_S_BUF_SIZE; + i2c_dev->slave_tx_msg.buf = kzalloc(I2C_S_BUF_SIZE, GFP_KERNEL); + //Rx buf 4 + for(i=0; i<I2C_S_RX_BUF_NUM+1; i++) { + i2c_dev->slave_rx_msg[i].addr = ~BUFF_ONGOING; + i2c_dev->slave_rx_msg[i].flags = 0; //mean empty buffer + i2c_dev->slave_rx_msg[i].len = I2C_S_BUF_SIZE; + i2c_dev->slave_rx_msg[i].buf = kzalloc(I2C_S_BUF_SIZE, GFP_KERNEL); + } +} + +static void ast_i2c_slave_rdwr_xfer(struct ast_i2c_dev *i2c_dev) +{ + int i; + int count = 0; + unsigned long flags; + + spin_lock_irqsave(&i2c_dev->slave_rx_lock, flags); + + switch(i2c_dev->slave_event) { + case I2C_SLAVE_EVENT_START_WRITE: + for(i=0; i<I2C_S_RX_BUF_NUM; i++) { + if((i2c_dev->slave_rx_msg[i].flags == 0) && (i2c_dev->slave_rx_msg[i].addr != BUFF_ONGOING)) { + i2c_dev->slave_rx_msg[i].addr = BUFF_ONGOING; + break; + } + } + if(i == I2C_S_RX_BUF_NUM) { + // dev_err(i2c_dev->dev, "RX buffer full ........use tmp msgs buff \n"); + for(i=0; i<I2C_S_RX_BUF_NUM; i++) { + if((i2c_dev->slave_rx_msg[i].flags == 0) && (i2c_dev->slave_rx_msg[i].addr == BUFF_ONGOING)) { + count++; + i2c_dev->slave_rx_msg[i].addr = 0; + } + } + + if (count) { + dev_err(i2c_dev->dev, "Cleared slave ongoing buffers of count: %d\n", count); + } + + for(i=0; i<I2C_S_RX_BUF_NUM; i++) { + if((i2c_dev->slave_rx_msg[i].flags == 0) && (i2c_dev->slave_rx_msg[i].addr != BUFF_ONGOING)) { + i2c_dev->slave_rx_msg[i].addr = BUFF_ONGOING; + break; + } + } + } + //printk("I2C_SLAVE_EVENT_START_WRITE ... %d \n", i); + + i2c_dev->slave_msgs = &i2c_dev->slave_rx_msg[i]; + break; + case I2C_SLAVE_EVENT_START_READ: + // printk("I2C_SLAVE_EVENT_START_READ ERROR .. not imple \n"); + i2c_dev->slave_msgs = &i2c_dev->slave_tx_msg; + break; + case I2C_SLAVE_EVENT_WRITE: + //printk("I2C_SLAVE_EVENT_WRITE next write ERROR ...\n"); + i2c_dev->slave_msgs = &i2c_dev->slave_tx_msg; + break; + case I2C_SLAVE_EVENT_READ: + printk("I2C_SLAVE_EVENT_READ ERROR ... \n"); + i2c_dev->slave_msgs = &i2c_dev->slave_tx_msg; + break; + case I2C_SLAVE_EVENT_NACK: + //printk("I2C_SLAVE_EVENT_NACK ERROR ... \n"); + i2c_dev->slave_msgs = &i2c_dev->slave_tx_msg; + break; + case I2C_SLAVE_EVENT_STOP: + //printk("I2C_SLAVE_EVENT_STOP \n"); + for(i=0; i<I2C_S_RX_BUF_NUM; i++) { + if(i2c_dev->slave_rx_msg[i].addr == BUFF_ONGOING) { + i2c_dev->slave_rx_msg[i].flags = BUFF_FULL; + i2c_dev->slave_rx_msg[i].addr = 0; + break; + } + } + + i2c_dev->slave_msgs = &i2c_dev->slave_tx_msg; + break; + } + spin_unlock_irqrestore(&i2c_dev->slave_rx_lock, flags); + +} + +static int ast_i2c_slave_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs) +{ + struct ast_i2c_dev *i2c_dev = adap->algo_data; + int ret=0, i; + unsigned long flags; + + switch(msgs->flags) { + case 0: +// printk("slave read \n"); + //cur_msg = get_free_msg; + spin_lock_irqsave(&i2c_dev->slave_rx_lock, flags); + + for(i=0; i<I2C_S_RX_BUF_NUM; i++) { + if((i2c_dev->slave_rx_msg[i].addr == 0) && (i2c_dev->slave_rx_msg[i].flags == BUFF_FULL)) { + memcpy(msgs->buf, i2c_dev->slave_rx_msg[i].buf, i2c_dev->slave_rx_msg[i].len); + msgs->len = i2c_dev->slave_rx_msg[i].len; + i2c_dev->slave_rx_msg[i].flags = 0; + i2c_dev->slave_rx_msg[i].len = 0; + break; + } + } + spin_unlock_irqrestore(&i2c_dev->slave_rx_lock, flags); + + if(i == I2C_S_RX_BUF_NUM) { + //printk("No buffer ........ \n"); + msgs->len = 0; + ret = -1; + } + break; + case I2C_M_RD: //slave write +// printk("slave write \n"); + memcpy(msgs->buf, i2c_dev->slave_tx_msg.buf, I2C_S_BUF_SIZE); + break; + case I2C_S_EN: + if((msgs->addr < 0x1) || (msgs->addr > 0xff)) { + ret = -1; + printk("addrsss not correct !! \n"); + return ret; + } + if(msgs->len != 1) printk("ERROR \n"); + ast_slave_mode_enable(i2c_dev, msgs); + break; + case I2C_S_ALT: +// printk("slave issue alt\n"); + if(msgs->len != 1) printk("ERROR \n"); + if(msgs->buf[0]==1) + ast_slave_issue_alert(i2c_dev, 1); + else + ast_slave_issue_alert(i2c_dev, 0); + break; + + default: + printk("slave xfer error \n"); + break; + + } + return ret; +} + + +#endif + +static u8 +ast_i2c_bus_error_recover(struct ast_i2c_dev *i2c_dev) +{ + u32 sts; + int r; + u32 i = 0; + + //Check 0x14's SDA and SCL status + sts = ast_i2c_read(i2c_dev,I2C_CMD_REG); + + if ((sts & AST_I2CD_SDA_LINE_STS) && (sts & AST_I2CD_SCL_LINE_STS)) { + //Means bus is idle. + dev_err(i2c_dev->dev, "I2C bus (%d) is idle. I2C slave doesn't exist?!\n", i2c_dev->bus_id); + return -1; + } + + dev_err(i2c_dev->dev, "ERROR!! I2C(%d) bus hanged, try to recovery it!\n", i2c_dev->bus_id); + + + if ((sts & AST_I2CD_SDA_LINE_STS) && !(sts & AST_I2CD_SCL_LINE_STS)) { + //if SDA == 1 and SCL == 0, it means the master is locking the bus. + //Send a stop command to unlock the bus. + dev_err(i2c_dev->dev, "I2C's master is locking the bus, try to stop it.\n"); +// + init_completion(&i2c_dev->cmd_complete); + i2c_dev->cmd_err = 0; + + ast_i2c_write(i2c_dev, AST_I2CD_M_STOP_CMD, I2C_CMD_REG); + + r = wait_for_completion_interruptible_timeout(&i2c_dev->cmd_complete, + i2c_dev->adap.timeout*HZ); + + if(i2c_dev->cmd_err && + i2c_dev->cmd_err != AST_I2CD_INTR_STS_NORMAL_STOP) { + dev_err(i2c_dev->dev, "recovery error \n"); + return -1; + } + + if (r == 0) { + dev_err(i2c_dev->dev, "recovery timed out\n"); + return -1; + } else { + dev_err(i2c_dev->dev, "Recovery successfully\n"); + return 0; + } + + + } else if (!(sts & AST_I2CD_SDA_LINE_STS)) { + //else if SDA == 0, the device is dead. We need to reset the bus + //And do the recovery command. + dev_err(i2c_dev->dev, "I2C's slave is dead, try to recover it\n"); + //Let's retry 10 times + for (i = 0; i < 10; i++) { + ast_i2c_dev_init(i2c_dev); + //Do the recovery command BIT11 + init_completion(&i2c_dev->cmd_complete); + i2c_dev->cmd_err = 0; + ast_i2c_write(i2c_dev, AST_I2CD_BUS_RECOVER_CMD_EN, I2C_CMD_REG); + + r = wait_for_completion_interruptible_timeout(&i2c_dev->cmd_complete, + i2c_dev->adap.timeout*HZ); + if (i2c_dev->cmd_err != 0 && + i2c_dev->cmd_err != AST_I2CD_INTR_STS_NORMAL_STOP) { + dev_err(i2c_dev->dev, "ERROR!! Failed to do recovery command(0x%08x)\n", i2c_dev->cmd_err); + return -1; + } + //Check 0x14's SDA and SCL status + sts = ast_i2c_read(i2c_dev,I2C_CMD_REG); + if (sts & AST_I2CD_SDA_LINE_STS) //Recover OK + break; + } + if (i == 10) { + dev_err(i2c_dev->dev, "ERROR!! recover failed\n"); + return -1; + } + } else { + dev_err(i2c_dev->dev, "Don't know how to handle this case?!\n"); + return -1; + } + dev_err(i2c_dev->dev, "Recovery successfully\n"); + return 0; +} + +static void ast_master_alert_recv(struct ast_i2c_dev *i2c_dev) +{ + printk("ast_master_alert_recv bus id %d, Disable Alt, Please Imple \n",i2c_dev->bus_id); +} + +static int ast_i2c_wait_bus_not_busy(struct ast_i2c_dev *i2c_dev) +{ + int timeout = 10; //TODO number + volatile u8 mode = 0; +// printk("ast_i2c_wait_bus_not_busy \n"); + + // Wait for slave transfer to finish + mode = i2c_dev->slave_operation; + while (mode == 1) { + if (timeout <= 0) { + break; + } + mode = i2c_dev->slave_operation; + timeout--; + msleep(1); + } + + if (timeout <= 0) { + //TODO: sometimes the slave operation flag is not reset properly so go ahead with checking bus busy signal + dev_err(i2c_dev->dev, "slave operation set, check busy status line\n"); + } + + // Wait for Bus to go IDLE + timeout = 10; + while (ast_i2c_read(i2c_dev,I2C_CMD_REG) & AST_I2CD_BUS_BUSY_STS) { + if(timeout<=0) { + break; + } + + timeout--; + msleep(1); + } + + if (timeout <=0) { + ast_i2c_bus_error_recover(i2c_dev); + return 0; + } + + // TODO: hack to reset slave operation flag manually + i2c_dev->slave_operation = 0; + return 0; +} + +static void ast_i2c_do_dma_xfer(struct ast_i2c_dev *i2c_dev) +{ + u32 cmd = 0; + int i; + + if(i2c_dev->slave_operation == 1) { + if(i2c_dev->slave_msgs->flags & I2C_M_RD) { + //DMA tx mode + if(i2c_dev->slave_msgs->len > AST_I2C_DMA_SIZE) + i2c_dev->slave_xfer_len = AST_I2C_DMA_SIZE; + else + i2c_dev->slave_xfer_len = i2c_dev->slave_msgs->len; + + dev_dbg(i2c_dev->dev, "(<--) slave tx DMA \n"); + for(i=0; i<i2c_dev->slave_xfer_len; i++) + i2c_dev->dma_buf[i] = i2c_dev->slave_msgs->buf[i2c_dev->slave_xfer_cnt + i]; + + ast_i2c_write(i2c_dev, i2c_dev->dma_addr, I2C_DMA_BASE_REG); + ast_i2c_write(i2c_dev, (i2c_dev->slave_xfer_len-1), I2C_DMA_LEN_REG); + ast_i2c_write(i2c_dev, AST_I2CD_TX_DMA_ENABLE | AST_I2CD_S_TX_CMD,I2C_CMD_REG); + } else { + //DMA prepare rx + dev_dbg(i2c_dev->dev, "(-->) slave rx DMA \n"); + ast_i2c_write(i2c_dev, i2c_dev->dma_addr, I2C_DMA_BASE_REG); + ast_i2c_write(i2c_dev, (AST_I2C_DMA_SIZE-1), I2C_DMA_LEN_REG); + ast_i2c_write(i2c_dev, AST_I2CD_RX_DMA_ENABLE, I2C_CMD_REG); + } + } else { + dev_dbg(i2c_dev->dev,"M cnt %d, xf len %d \n",i2c_dev->master_xfer_cnt, i2c_dev->master_msgs->len); + if(i2c_dev->master_xfer_cnt == -1) { + //send start + dev_dbg(i2c_dev->dev, " %sing %d byte%s %s 0x%02x\n", + i2c_dev->master_msgs->flags & I2C_M_RD ? "read" : "write", + i2c_dev->master_msgs->len, i2c_dev->master_msgs->len > 1 ? "s" : "", + i2c_dev->master_msgs->flags & I2C_M_RD ? "from" : "to", i2c_dev->master_msgs->addr); + + if(i2c_dev->master_msgs->flags & I2C_M_RD) { + //workaround .. HW can;t send start read addr with buff mode + cmd = AST_I2CD_M_START_CMD | AST_I2CD_M_TX_CMD; + ast_i2c_write(i2c_dev, (i2c_dev->master_msgs->addr <<1) |0x1, I2C_BYTE_BUF_REG); + +// tx_buf[0] = (i2c_dev->master_msgs->addr <<1); //+1 + i2c_dev->master_xfer_len = 1; + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_TX_ACK_INTR_EN, I2C_INTR_CTRL_REG); + } else { + //tx + cmd = AST_I2CD_M_START_CMD | AST_I2CD_M_TX_CMD | AST_I2CD_TX_DMA_ENABLE; + + i2c_dev->dma_buf[0] = (i2c_dev->master_msgs->addr <<1); //+1 + //next data write + if((i2c_dev->master_msgs->len + 1) > AST_I2C_DMA_SIZE) + i2c_dev->master_xfer_len = AST_I2C_DMA_SIZE; + else + i2c_dev->master_xfer_len = i2c_dev->master_msgs->len + 1; + + for(i = 1; i < i2c_dev->master_xfer_len; i++) + i2c_dev->dma_buf[i] = i2c_dev->master_msgs->buf[i2c_dev->master_xfer_cnt+i]; + + if (i2c_dev->xfer_last == 1) { + dev_dbg(i2c_dev->dev, "last stop \n"); + cmd |= AST_I2CD_M_STOP_CMD; + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) & + ~AST_I2CD_TX_ACK_INTR_EN, I2C_INTR_CTRL_REG); + + } else { + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_TX_ACK_INTR_EN, I2C_INTR_CTRL_REG); + } + ast_i2c_write(i2c_dev, i2c_dev->dma_addr, I2C_DMA_BASE_REG); + ast_i2c_write(i2c_dev, (i2c_dev->master_xfer_len-1), I2C_DMA_LEN_REG); + + } + ast_i2c_write(i2c_dev, cmd, I2C_CMD_REG); + dev_dbg(i2c_dev->dev, "txfer size %d , cmd = %x \n",i2c_dev->master_xfer_len, cmd); + + } else if (i2c_dev->master_xfer_cnt < i2c_dev->master_msgs->len){ + //Next send + if(i2c_dev->master_msgs->flags & I2C_M_RD) { + //Rx data + cmd = AST_I2CD_M_RX_CMD | AST_I2CD_RX_DMA_ENABLE; + + if((i2c_dev->master_msgs->len - i2c_dev->master_xfer_cnt) > AST_I2C_DMA_SIZE) { + i2c_dev->master_xfer_len = AST_I2C_DMA_SIZE; + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_RX_DOWN_INTR_EN, I2C_INTR_CTRL_REG); + + } else { + i2c_dev->master_xfer_len = i2c_dev->master_msgs->len - i2c_dev->master_xfer_cnt; + if((i2c_dev->master_msgs->flags & I2C_M_RECV_LEN) && (i2c_dev->blk_r_flag == 0)) { + dev_dbg(i2c_dev->dev, "I2C_M_RECV_LEN \n"); + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_RX_DOWN_INTR_EN, I2C_INTR_CTRL_REG); + } else { +#ifdef CONFIG_AST1010 + //Workaround for ast1010 can't send NACK + if((i2c_dev->master_xfer_len == 1) && (i2c_dev->xfer_last == 1)) { + //change to byte mode + cmd |= AST_I2CD_M_STOP_CMD | AST_I2CD_M_S_RX_CMD_LAST; + cmd &= ~AST_I2CD_RX_DMA_ENABLE; + i2c_dev->master_xfer_mode = BYTE_XFER; + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) & + ~AST_I2CD_RX_DOWN_INTR_EN, I2C_INTR_CTRL_REG); + + } else if (i2c_dev->master_xfer_len > 1) { + i2c_dev->master_xfer_len -=1; + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_RX_DOWN_INTR_EN, I2C_INTR_CTRL_REG); + } else { + printk(" Fix Me !! \n"); + } +#else + if(i2c_dev->xfer_last == 1) { + dev_dbg(i2c_dev->dev, "last stop \n"); + cmd |= AST_I2CD_M_STOP_CMD; + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) & + ~AST_I2CD_RX_DOWN_INTR_EN, I2C_INTR_CTRL_REG); + } else { + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_RX_DOWN_INTR_EN, I2C_INTR_CTRL_REG); + } + //TODO check.... + cmd |= AST_I2CD_M_S_RX_CMD_LAST; +#endif + } + + } + ast_i2c_write(i2c_dev, i2c_dev->dma_addr, I2C_DMA_BASE_REG); + ast_i2c_write(i2c_dev, i2c_dev->master_xfer_len-1, I2C_DMA_LEN_REG); + ast_i2c_write(i2c_dev, cmd, I2C_CMD_REG); + dev_dbg(i2c_dev->dev, "rxfer size %d , cmd = %x \n",i2c_dev->master_xfer_len, cmd); + } else { + //Tx data + //next data write + cmd = AST_I2CD_M_TX_CMD | AST_I2CD_TX_DMA_ENABLE; + if((i2c_dev->master_msgs->len - i2c_dev->master_xfer_cnt) > AST_I2C_DMA_SIZE) { + i2c_dev->master_xfer_len = AST_I2C_DMA_SIZE; + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_TX_ACK_INTR_EN, I2C_INTR_CTRL_REG); + + } else { + i2c_dev->master_xfer_len = i2c_dev->master_msgs->len - i2c_dev->master_xfer_cnt; + if(i2c_dev->xfer_last == 1) { + dev_dbg(i2c_dev->dev, "last stop \n"); + cmd |= AST_I2CD_M_STOP_CMD; + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) & + ~AST_I2CD_TX_ACK_INTR_EN, I2C_INTR_CTRL_REG); + + } else { + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_TX_ACK_INTR_EN, I2C_INTR_CTRL_REG); + } + } + + for(i = 0; i < i2c_dev->master_xfer_len; i++) + i2c_dev->dma_buf[i] = i2c_dev->master_msgs->buf[i2c_dev->master_xfer_cnt + i]; + + ast_i2c_write(i2c_dev, i2c_dev->dma_addr, I2C_DMA_BASE_REG); + ast_i2c_write(i2c_dev, (i2c_dev->master_xfer_len-1), I2C_DMA_LEN_REG); + ast_i2c_write(i2c_dev, cmd , I2C_CMD_REG); + dev_dbg(i2c_dev->dev, "txfer size %d , cmd = %x \n",i2c_dev->master_xfer_len, cmd); + + } + }else { + //should send next msg + if(i2c_dev->master_xfer_cnt != i2c_dev->master_msgs->len) + printk("complete rx ... bus=%d addr=0x%x (%d vs. %d) ERROR\n", + i2c_dev->bus_id, i2c_dev->master_msgs->addr, + i2c_dev->master_xfer_cnt, i2c_dev->master_msgs->len); + + dev_dbg(i2c_dev->dev, "ast_i2c_do_byte_xfer complete \n"); + i2c_dev->cmd_err = 0; + complete(&i2c_dev->cmd_complete); + } + + } + + +} + +static void ast_i2c_do_pool_xfer(struct ast_i2c_dev *i2c_dev) +{ + u32 cmd = 0; + int i; + u32 *tx_buf; + +#if defined(CONFIG_ARCH_AST2400) + ast_i2c_write(i2c_dev, + (ast_i2c_read(i2c_dev, I2C_FUN_CTRL_REG) & + ~AST_I2CD_BUFF_SEL_MASK) | + AST_I2CD_BUFF_SEL(i2c_dev->req_page->page_no), + I2C_FUN_CTRL_REG); +#endif + + tx_buf = (u32 *) i2c_dev->req_page->page_addr; + + + if(i2c_dev->slave_operation == 1) { + if(i2c_dev->slave_msgs->flags & I2C_M_RD) { + dev_dbg(i2c_dev->dev, "(<--) slave tx buf \n"); + + if(i2c_dev->slave_msgs->len > i2c_dev->req_page->page_size) + i2c_dev->slave_xfer_len = i2c_dev->req_page->page_size; + else + i2c_dev->slave_xfer_len = i2c_dev->slave_msgs->len; + + for(i = 0; i< i2c_dev->slave_xfer_len; i++) { + if(i%4 == 0) + tx_buf[i/4] = 0; + tx_buf[i/4] |= (i2c_dev->slave_msgs->buf[i2c_dev->slave_xfer_cnt + i] << ((i%4)*8)) ; + dev_dbg(i2c_dev->dev, "[%x] ",tx_buf[i/4]); + } + dev_dbg(i2c_dev->dev, "\n"); + + ast_i2c_write(i2c_dev, AST_I2CD_TX_DATA_BUF_END_SET((i2c_dev->slave_xfer_len-1)) | + AST_I2CD_BUF_BASE_ADDR_SET((i2c_dev->req_page->page_addr_point)), + I2C_BUF_CTRL_REG); + + ast_i2c_write(i2c_dev, AST_I2CD_TX_BUFF_ENABLE | AST_I2CD_S_TX_CMD, I2C_CMD_REG); + } else { + //prepare for new rx + dev_dbg(i2c_dev->dev, "(-->) slave prepare rx buf \n"); + ast_i2c_write(i2c_dev, + AST_I2CD_RX_BUF_END_ADDR_SET((i2c_dev->req_page->page_size-1)) | + AST_I2CD_BUF_BASE_ADDR_SET((i2c_dev->req_page->page_addr_point)), + I2C_BUF_CTRL_REG); + + ast_i2c_write(i2c_dev, AST_I2CD_RX_BUFF_ENABLE, I2C_CMD_REG); + + } + } else { + dev_dbg(i2c_dev->dev,"M cnt %d, xf len %d \n",i2c_dev->master_xfer_cnt, i2c_dev->master_msgs->len); + if(i2c_dev->master_xfer_cnt == -1) { + //send start + dev_dbg(i2c_dev->dev, " %sing %d byte%s %s 0x%02x\n", + i2c_dev->master_msgs->flags & I2C_M_RD ? "read" : "write", + i2c_dev->master_msgs->len, i2c_dev->master_msgs->len > 1 ? "s" : "", + i2c_dev->master_msgs->flags & I2C_M_RD ? "from" : "to", i2c_dev->master_msgs->addr); + + if(i2c_dev->master_msgs->flags & I2C_M_RD) { +//workaround .. HW can;t send start read addr with buff mode + cmd = AST_I2CD_M_START_CMD | AST_I2CD_M_TX_CMD; + ast_i2c_write(i2c_dev, (i2c_dev->master_msgs->addr <<1) |0x1, I2C_BYTE_BUF_REG); + +// tx_buf[0] = (i2c_dev->master_msgs->addr <<1); //+1 + i2c_dev->master_xfer_len = 1; + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_TX_ACK_INTR_EN, I2C_INTR_CTRL_REG); + } else { + cmd = AST_I2CD_M_START_CMD | AST_I2CD_M_TX_CMD | AST_I2CD_TX_BUFF_ENABLE; + tx_buf[0] = (i2c_dev->master_msgs->addr <<1); //+1 + //next data write + if((i2c_dev->master_msgs->len + 1) > i2c_dev->req_page->page_size) + i2c_dev->master_xfer_len = i2c_dev->req_page->page_size; + else + i2c_dev->master_xfer_len = i2c_dev->master_msgs->len + 1; + + for(i = 1; i < i2c_dev->master_xfer_len; i++) { + if(i%4 == 0) + tx_buf[i/4] = 0; + tx_buf[i/4] |= (i2c_dev->master_msgs->buf[i2c_dev->master_xfer_cnt + i] << ((i%4)*8)) ; + } + + if (i2c_dev->xfer_last == 1) { + dev_dbg(i2c_dev->dev, "last stop \n"); + cmd |= AST_I2CD_M_STOP_CMD; + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) & + ~AST_I2CD_TX_ACK_INTR_EN, I2C_INTR_CTRL_REG); + + } else { + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_TX_ACK_INTR_EN, I2C_INTR_CTRL_REG); + } + ast_i2c_write(i2c_dev, + AST_I2CD_TX_DATA_BUF_END_SET((i2c_dev->master_xfer_len - 1)) | + AST_I2CD_BUF_BASE_ADDR_SET(i2c_dev->req_page->page_addr_point), + I2C_BUF_CTRL_REG); + } + ast_i2c_write(i2c_dev, cmd, I2C_CMD_REG); + dev_dbg(i2c_dev->dev, "txfer size %d , cmd = %x \n",i2c_dev->master_xfer_len, cmd); + + } else if (i2c_dev->master_xfer_cnt < i2c_dev->master_msgs->len){ + //Next send + if(i2c_dev->master_msgs->flags & I2C_M_RD) { + //Rx data + cmd = AST_I2CD_M_RX_CMD | AST_I2CD_RX_BUFF_ENABLE; + + if((i2c_dev->master_msgs->len - i2c_dev->master_xfer_cnt) > i2c_dev->req_page->page_size) { + i2c_dev->master_xfer_len = i2c_dev->req_page->page_size; + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_RX_DOWN_INTR_EN, I2C_INTR_CTRL_REG); + } else { + i2c_dev->master_xfer_len = i2c_dev->master_msgs->len - i2c_dev->master_xfer_cnt; + if((i2c_dev->master_msgs->flags & I2C_M_RECV_LEN) && (i2c_dev->blk_r_flag == 0)) { + dev_dbg(i2c_dev->dev, "I2C_M_RECV_LEN \n"); + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_RX_DOWN_INTR_EN, I2C_INTR_CTRL_REG); + } else { + if(i2c_dev->xfer_last == 1) { + dev_dbg(i2c_dev->dev, "last stop \n"); + cmd |= AST_I2CD_M_STOP_CMD; + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) & + ~AST_I2CD_RX_DOWN_INTR_EN, I2C_INTR_CTRL_REG); + } else { + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_RX_DOWN_INTR_EN, I2C_INTR_CTRL_REG); + } + cmd |= AST_I2CD_M_S_RX_CMD_LAST; + } + } + ast_i2c_write(i2c_dev, + AST_I2CD_RX_BUF_END_ADDR_SET((i2c_dev->master_xfer_len-1))| + AST_I2CD_BUF_BASE_ADDR_SET((i2c_dev->req_page->page_addr_point)), + I2C_BUF_CTRL_REG); + ast_i2c_write(i2c_dev, cmd, I2C_CMD_REG); + dev_dbg(i2c_dev->dev, "rxfer size %d , cmd = %x \n",i2c_dev->master_xfer_len, cmd); + } else { + //Tx data + //next data write + cmd = AST_I2CD_M_TX_CMD | AST_I2CD_TX_BUFF_ENABLE; + if((i2c_dev->master_msgs->len - i2c_dev->master_xfer_cnt) > i2c_dev->req_page->page_size) { + i2c_dev->master_xfer_len = i2c_dev->req_page->page_size; + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_TX_ACK_INTR_EN, I2C_INTR_CTRL_REG); + + } else { + i2c_dev->master_xfer_len = i2c_dev->master_msgs->len - i2c_dev->master_xfer_cnt; + if(i2c_dev->xfer_last == 1) { + dev_dbg(i2c_dev->dev, "last stop \n"); + cmd |= AST_I2CD_M_STOP_CMD; + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) & + ~AST_I2CD_TX_ACK_INTR_EN, I2C_INTR_CTRL_REG); + + } else { + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_TX_ACK_INTR_EN, I2C_INTR_CTRL_REG); + } + } + + for(i = 0; i < i2c_dev->master_xfer_len; i++) { + if(i%4 == 0) + tx_buf[i/4] = 0; + tx_buf[i/4] |= (i2c_dev->master_msgs->buf[i2c_dev->master_xfer_cnt + i] << ((i%4)*8)) ; + } +// printk("count %x \n",ast_i2c_read(i2c_dev,I2C_CMD_REG)); + ast_i2c_write(i2c_dev, + AST_I2CD_TX_DATA_BUF_END_SET((i2c_dev->master_xfer_len - 1)) | + AST_I2CD_BUF_BASE_ADDR_SET(i2c_dev->req_page->page_addr_point), + I2C_BUF_CTRL_REG); + + ast_i2c_write(i2c_dev, cmd , I2C_CMD_REG); + dev_dbg(i2c_dev->dev, "txfer size %d , cmd = %x \n",i2c_dev->master_xfer_len, cmd); + } + } else { + //should send next msg + if(i2c_dev->master_xfer_cnt != i2c_dev->master_msgs->len) + printk("complete rx ... bus=%d addr=0x%x (%d vs. %d) ERROR\n", + i2c_dev->bus_id, i2c_dev->master_msgs->addr, + i2c_dev->master_xfer_cnt, i2c_dev->master_msgs->len); + + dev_dbg(i2c_dev->dev, "ast_i2c_do_byte_xfer complete \n"); + i2c_dev->cmd_err = 0; + complete(&i2c_dev->cmd_complete); + } + + } +} +static void ast_i2c_do_byte_xfer(struct ast_i2c_dev *i2c_dev) +{ + u8 *xfer_buf; + u32 cmd = 0; + + if(i2c_dev->slave_operation == 1) { + dev_dbg(i2c_dev->dev,"S cnt %d, xf len %d \n",i2c_dev->slave_xfer_cnt, i2c_dev->slave_msgs->len); + if(i2c_dev->slave_msgs->flags & I2C_M_RD) { + //READ <-- TX + dev_dbg(i2c_dev->dev, "(<--) slave(tx) buf %d [%x]\n", i2c_dev->slave_xfer_cnt, i2c_dev->slave_msgs->buf[i2c_dev->slave_xfer_cnt]); + ast_i2c_write(i2c_dev, i2c_dev->slave_msgs->buf[i2c_dev->slave_xfer_cnt], I2C_BYTE_BUF_REG); + ast_i2c_write(i2c_dev, AST_I2CD_S_TX_CMD, I2C_CMD_REG); + } else { + // Write -->Rx + //no need to handle in byte mode + dev_dbg(i2c_dev->dev, "(-->) slave(rx) BYTE do nothing\n"); + + } + } else { + dev_dbg(i2c_dev->dev,"M cnt %d, xf len %d \n",i2c_dev->master_xfer_cnt, i2c_dev->master_msgs->len); + if(i2c_dev->master_xfer_cnt == -1) { + //first start + dev_dbg(i2c_dev->dev, " %sing %d byte%s %s 0x%02x\n", + i2c_dev->master_msgs->flags & I2C_M_RD ? "read" : "write", + i2c_dev->master_msgs->len, i2c_dev->master_msgs->len > 1 ? "s" : "", + i2c_dev->master_msgs->flags & I2C_M_RD ? "from" : "to", i2c_dev->master_msgs->addr); + + + if(i2c_dev->master_msgs->flags & I2C_M_RD) + ast_i2c_write(i2c_dev, (i2c_dev->master_msgs->addr <<1) |0x1, I2C_BYTE_BUF_REG); + else + ast_i2c_write(i2c_dev, (i2c_dev->master_msgs->addr <<1), I2C_BYTE_BUF_REG); + + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_TX_ACK_INTR_EN, I2C_INTR_CTRL_REG); + + ast_i2c_write(i2c_dev, AST_I2CD_M_TX_CMD | AST_I2CD_M_START_CMD, I2C_CMD_REG); + + + } else if (i2c_dev->master_xfer_cnt < i2c_dev->master_msgs->len){ + xfer_buf = i2c_dev->master_msgs->buf; + if(i2c_dev->master_msgs->flags & I2C_M_RD) { + //Rx data + cmd = AST_I2CD_M_RX_CMD; + if((i2c_dev->master_msgs->flags & I2C_M_RECV_LEN) && (i2c_dev->master_xfer_cnt == 0)) { + dev_dbg(i2c_dev->dev, "I2C_M_RECV_LEN \n"); + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_RX_DOWN_INTR_EN, I2C_INTR_CTRL_REG); + + } else if((i2c_dev->xfer_last == 1) && (i2c_dev->master_xfer_cnt + 1 == i2c_dev->master_msgs->len)) { + cmd |= AST_I2CD_M_S_RX_CMD_LAST | AST_I2CD_M_STOP_CMD; + // disable rx_dwn isr + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) & + ~AST_I2CD_RX_DOWN_INTR_EN, I2C_INTR_CTRL_REG); + } else { + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_RX_DOWN_INTR_EN, I2C_INTR_CTRL_REG); + } + + dev_dbg(i2c_dev->dev, "(<--) rx byte, cmd = %x \n",cmd); + + ast_i2c_write(i2c_dev, cmd, I2C_CMD_REG); + + + } else { + //Tx data + dev_dbg(i2c_dev->dev, "(-->) xfer byte data index[%02x]:%02x \n",i2c_dev->master_xfer_cnt, *(xfer_buf + i2c_dev->master_xfer_cnt)); + ast_i2c_write(i2c_dev, *(xfer_buf + i2c_dev->master_xfer_cnt), I2C_BYTE_BUF_REG); + if((i2c_dev->xfer_last == 1) && (i2c_dev->master_xfer_cnt + 1 == i2c_dev->master_msgs->len)) { + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) & + ~AST_I2CD_TX_ACK_INTR_EN, I2C_INTR_CTRL_REG); + ast_i2c_write(i2c_dev, AST_I2CD_M_TX_CMD | AST_I2CD_M_STOP_CMD, I2C_CMD_REG); + } else { + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_TX_ACK_INTR_EN, I2C_INTR_CTRL_REG); + ast_i2c_write(i2c_dev, AST_I2CD_M_TX_CMD, I2C_CMD_REG); + } + } + + } else { + //should send next msg + if(i2c_dev->master_xfer_cnt != i2c_dev->master_msgs->len) + printk("CNT ERROR bus=%d addr=0x%x (%d vs. %d)\n", + i2c_dev->bus_id, i2c_dev->master_msgs->addr, + i2c_dev->master_xfer_cnt, i2c_dev->master_msgs->len); + + dev_dbg(i2c_dev->dev, "ast_i2c_do_byte_xfer complete \n"); + i2c_dev->cmd_err = 0; + complete(&i2c_dev->cmd_complete); + + } + } + +} + +static void ast_i2c_slave_xfer_done(struct ast_i2c_dev *i2c_dev) +{ + u32 xfer_len; + int i; + u8 *rx_buf; + unsigned long flags; + + dev_dbg(i2c_dev->dev, "ast_i2c_slave_xfer_done [%d]\n",i2c_dev->slave_xfer_mode); + + + if (i2c_dev->slave_msgs->flags & I2C_M_RD) { + //tx done , only check tx count ... + if(i2c_dev->slave_xfer_mode == BYTE_XFER) { + xfer_len = 1; + } else if (i2c_dev->slave_xfer_mode == BUFF_XFER) { + xfer_len = AST_I2CD_TX_DATA_BUF_GET(ast_i2c_read(i2c_dev, I2C_BUF_CTRL_REG)); + xfer_len++; + dev_dbg(i2c_dev->dev,"S tx buff done len %d \n",xfer_len); + } else { + //DMA mode + xfer_len = ast_i2c_read(i2c_dev, I2C_DMA_LEN_REG); + if(xfer_len == 0) + xfer_len = i2c_dev->slave_xfer_len; + else + xfer_len = i2c_dev->slave_xfer_len - xfer_len - 1; + + dev_dbg(i2c_dev->dev,"S tx rx dma done len %d \n",xfer_len); + } + + } else { + //rx done + if(i2c_dev->slave_xfer_mode == BYTE_XFER) { + //TODO + xfer_len = 1; + if(i2c_dev->slave_event == I2C_SLAVE_EVENT_STOP) { + i2c_dev->slave_msgs->buf[i2c_dev->slave_xfer_cnt] = 0; + i2c_dev->slave_msgs->len = i2c_dev->slave_xfer_cnt; + } else { + if (i2c_dev->slave_xfer_cnt == 0) + dev_err(i2c_dev->dev,"Possible first byte failure issue\n"); + i2c_dev->slave_msgs->buf[i2c_dev->slave_xfer_cnt] = ast_i2c_read(i2c_dev,I2C_BYTE_BUF_REG) >> 8; + } + dev_dbg(i2c_dev->dev,"rx buff %d, [%x] \n",i2c_dev->slave_xfer_cnt ,i2c_dev->slave_msgs->buf[i2c_dev->slave_xfer_cnt]); + } else if (i2c_dev->slave_xfer_mode == BUFF_XFER) { + xfer_len = AST_I2CD_RX_BUF_ADDR_GET(ast_i2c_read(i2c_dev, I2C_BUF_CTRL_REG)); + if(xfer_len == 0) + xfer_len = AST_I2C_PAGE_SIZE; + + dev_dbg(i2c_dev->dev,"rx buff done len %d \n",xfer_len); + + rx_buf = (u8 *)i2c_dev->req_page->page_addr; + + for(i=0;i<xfer_len;i++) { + i2c_dev->slave_msgs->buf[i2c_dev->slave_xfer_cnt+i] = rx_buf[i]; + dev_dbg(i2c_dev->dev,"%d, [%x] \n",i2c_dev->slave_xfer_cnt+i ,i2c_dev->slave_msgs->buf[i2c_dev->slave_xfer_cnt+i]); + } + + } else { + //RX DMA DOWN + xfer_len = ast_i2c_read(i2c_dev, I2C_DMA_LEN_REG); + if(xfer_len == 0) + xfer_len = i2c_dev->slave_xfer_len; + else + xfer_len = i2c_dev->slave_xfer_len - xfer_len - 1; + + dev_dbg(i2c_dev->dev, " rx dma done len %d \n", xfer_len); + + for(i=0;i<xfer_len;i++) { + i2c_dev->slave_msgs->buf[i2c_dev->slave_xfer_cnt+i] = i2c_dev->dma_buf[i]; + dev_dbg(i2c_dev->dev,"%d, [%x] \n",i2c_dev->slave_xfer_cnt+i ,i2c_dev->slave_msgs->buf[i2c_dev->slave_xfer_cnt+i]); + } + } + + } + + if(xfer_len !=i2c_dev->slave_xfer_len) { + //TODO.. + printk(" **slave xfer error ====\n"); + //should goto stop.... + } else + i2c_dev->slave_xfer_cnt += i2c_dev->slave_xfer_len; + + + if((i2c_dev->slave_event == I2C_SLAVE_EVENT_NACK) || (i2c_dev->slave_event == I2C_SLAVE_EVENT_STOP)) { +#ifdef CONFIG_AST_I2C_SLAVE_RDWR + ast_i2c_slave_rdwr_xfer(i2c_dev); +#else + i2c_dev->ast_i2c_data->slave_xfer(i2c_dev->slave_event, &(i2c_dev->slave_msgs)); +#endif + i2c_dev->slave_xfer_cnt = 0; + } else { + if(i2c_dev->slave_xfer_cnt == i2c_dev->slave_msgs->len) { + dev_err(i2c_dev->dev,"slave next msgs with len %d\n", i2c_dev->slave_xfer_cnt); +#ifdef CONFIG_AST_I2C_SLAVE_RDWR + ast_i2c_slave_rdwr_xfer(i2c_dev); +#else + i2c_dev->ast_i2c_data->slave_xfer(i2c_dev->slave_event, &(i2c_dev->slave_msgs)); +#endif + + i2c_dev->slave_xfer_cnt = 0; + } + i2c_dev->do_slave_xfer(i2c_dev); + } + + // Read the current state for clearing up the slave mode + i2c_dev->state = (ast_i2c_read(i2c_dev,I2C_CMD_REG) >> 19) & 0xf; + + if(AST_I2CD_IDLE == i2c_dev->state) { + dev_dbg(i2c_dev->dev,"** Slave go IDLE **\n"); + i2c_dev->slave_operation = 0; + + if(i2c_dev->slave_xfer_mode == BUFF_XFER) { + i2c_dev->ast_i2c_data->free_pool_buff_page(i2c_dev->req_page); + } + + } else if (i2c_dev->slave_event == I2C_SLAVE_EVENT_STOP) { + // TODO: hack to reset slave operation flag in case the stop is received + i2c_dev->slave_operation = 0; + } + +} + +//TX/Rx Done +static void ast_i2c_master_xfer_done(struct ast_i2c_dev *i2c_dev) +{ + u32 xfer_len; + int i; + u8 *pool_buf; + unsigned long flags; + + spin_lock_irqsave(&i2c_dev->master_lock, flags); + + /* + * This function shall be involked during interrupt handling. + * Since the interrupt could be fired at anytime, we will need to make sure + * we have the buffer (i2c_dev->master_msgs) to handle the results. + */ + if (!i2c_dev->master_msgs) { + goto unlock_out; + } + + dev_dbg(i2c_dev->dev, "ast_i2c_master_xfer_done mode[%d]\n",i2c_dev->master_xfer_mode); + + if (i2c_dev->master_msgs->flags & I2C_M_RD) { + if(i2c_dev->master_xfer_cnt == -1) { + xfer_len = 1; + goto next_xfer; + } + if(i2c_dev->master_xfer_mode == BYTE_XFER) { + if ((i2c_dev->master_msgs->flags & I2C_M_RECV_LEN) && (i2c_dev->blk_r_flag == 0)) { + i2c_dev->master_msgs->len += (ast_i2c_read(i2c_dev,I2C_BYTE_BUF_REG) & AST_I2CD_RX_BYTE_BUFFER) >> 8; + i2c_dev->blk_r_flag = 1; + dev_dbg(i2c_dev->dev, "I2C_M_RECV_LEN %d \n", i2c_dev->master_msgs->len -1); + } + xfer_len = 1; + i2c_dev->master_msgs->buf[i2c_dev->master_xfer_cnt] = (ast_i2c_read(i2c_dev,I2C_BYTE_BUF_REG) & AST_I2CD_RX_BYTE_BUFFER) >> 8; + } else if (i2c_dev->master_xfer_mode == BUFF_XFER) { + pool_buf = (u8 *)i2c_dev->req_page->page_addr; + xfer_len = AST_I2CD_RX_BUF_ADDR_GET(ast_i2c_read(i2c_dev, I2C_BUF_CTRL_REG)); + + if(xfer_len == 0) + xfer_len = AST_I2C_PAGE_SIZE; + + for(i = 0; i< xfer_len; i++) { + i2c_dev->master_msgs->buf[i2c_dev->master_xfer_cnt + i] = pool_buf[i]; + dev_dbg(i2c_dev->dev, "rx %d buff[%x]\n",i2c_dev->master_xfer_cnt+i, i2c_dev->master_msgs->buf[i2c_dev->master_xfer_cnt+i]); + } + + if ((i2c_dev->master_msgs->flags & I2C_M_RECV_LEN) && (i2c_dev->blk_r_flag == 0)) { + i2c_dev->master_msgs->len += pool_buf[0]; + i2c_dev->blk_r_flag = 1; + dev_dbg(i2c_dev->dev, "I2C_M_RECV_LEN %d \n", i2c_dev->master_msgs->len -1); + } + } else { + //DMA Mode + xfer_len = ast_i2c_read(i2c_dev, I2C_DMA_LEN_REG); + + if(xfer_len == 0) + xfer_len = i2c_dev->master_xfer_len; + else + xfer_len = i2c_dev->master_xfer_len - xfer_len - 1; + + for(i = 0; i < xfer_len; i++) { + i2c_dev->master_msgs->buf[i2c_dev->master_xfer_cnt + i] = i2c_dev->dma_buf[i]; + dev_dbg(i2c_dev->dev, "buf[%x] \n", i2c_dev->dma_buf[i]); + dev_dbg(i2c_dev->dev, "buf[%x] \n", i2c_dev->dma_buf[i+1]); + } + + if ((i2c_dev->master_msgs->flags & I2C_M_RECV_LEN) && (i2c_dev->blk_r_flag == 0)) { + i2c_dev->master_msgs->len += i2c_dev->dma_buf[0]; + i2c_dev->blk_r_flag = 1; + dev_dbg(i2c_dev->dev, "I2C_M_RECV_LEN %d \n", i2c_dev->master_msgs->len -1); + } + + } + + }else { + if(i2c_dev->master_xfer_mode == BYTE_XFER) { + xfer_len = 1; + } else if(i2c_dev->master_xfer_mode == BUFF_XFER) { + xfer_len = AST_I2CD_TX_DATA_BUF_GET(ast_i2c_read(i2c_dev, I2C_BUF_CTRL_REG)); + xfer_len++; + dev_dbg(i2c_dev->dev,"tx buff done len %d \n",xfer_len); + } else { + //DMA + xfer_len = ast_i2c_read(i2c_dev, I2C_DMA_LEN_REG); + if(xfer_len == 0) + xfer_len = i2c_dev->master_xfer_len; + else + xfer_len = i2c_dev->master_xfer_len - xfer_len - 1; + + dev_dbg(i2c_dev->dev,"tx dma done len %d \n",xfer_len); + } + } + +next_xfer: + + if(xfer_len !=i2c_dev->master_xfer_len) { + //TODO.. + printk(" ** xfer error bus=%d addr=0x%x (%d vs. %d)\n", + i2c_dev->bus_id, i2c_dev->master_msgs->addr, + xfer_len, i2c_dev->master_xfer_len); + //should goto stop.... + i2c_dev->cmd_err = 1; + goto done_out; + } else + i2c_dev->master_xfer_cnt += i2c_dev->master_xfer_len; + + if(i2c_dev->master_xfer_cnt != i2c_dev->master_msgs->len) { + dev_dbg(i2c_dev->dev,"do next cnt \n"); + i2c_dev->do_master_xfer(i2c_dev); + } else { +#if 0 + int i; + printk(" ===== \n"); + for(i=0;i<i2c_dev->master_msgs->len;i++) + printk("rx buf i,[%x]\n",i,i2c_dev->master_msgs->buf[i]); + printk(" ===== \n"); +#endif + i2c_dev->cmd_err = 0; + +done_out: + dev_dbg(i2c_dev->dev,"msgs complete \n"); + complete(&i2c_dev->cmd_complete); + } + +unlock_out: + spin_unlock_irqrestore(&i2c_dev->master_lock, flags); +} + +static void ast_i2c_slave_addr_match(struct ast_i2c_dev *i2c_dev) +{ + u8 match; + unsigned long flags; + + + i2c_dev->slave_operation = 1; + i2c_dev->slave_xfer_cnt = 0; + match = ast_i2c_read(i2c_dev,I2C_BYTE_BUF_REG) >> 8; + i2c_dev->slave_msgs->buf[0] = match; + dev_dbg(i2c_dev->dev, "S Start Addr match [%x] \n",match); + + if(match & 1) { + i2c_dev->slave_event = I2C_SLAVE_EVENT_START_READ; + } else { + i2c_dev->slave_event = I2C_SLAVE_EVENT_START_WRITE; + } + +#ifdef CONFIG_AST_I2C_SLAVE_RDWR + ast_i2c_slave_rdwr_xfer(i2c_dev); + i2c_dev->slave_msgs->buf[0] = match; + i2c_dev->slave_xfer_cnt = 1; + // Reset the length field as we have received new slave address match + i2c_dev->slave_msgs->len = 0x0; +#else + i2c_dev->ast_i2c_data->slave_xfer(i2c_dev->slave_event, &(i2c_dev->slave_msgs)); + i2c_dev->slave_xfer_cnt = 0; +#endif + + //request: set slave_xfer_mode properly based on slave_dma mode + if(i2c_dev->ast_i2c_data->slave_dma == BYTE_MODE) { + i2c_dev->do_slave_xfer = ast_i2c_do_byte_xfer; + i2c_dev->slave_xfer_mode = BYTE_XFER; + i2c_dev->slave_xfer_len = 1; + } else if (i2c_dev->ast_i2c_data->slave_dma == DMA_MODE) { + i2c_dev->do_slave_xfer = ast_i2c_do_dma_xfer; + i2c_dev->slave_xfer_mode = DMA_XFER; + } else { + if(i2c_dev->ast_i2c_data->request_pool_buff_page(&(i2c_dev->req_page)) == 0) { + i2c_dev->do_slave_xfer = ast_i2c_do_pool_xfer; + i2c_dev->slave_xfer_mode = BUFF_XFER; + } else { + i2c_dev->do_slave_xfer = ast_i2c_do_byte_xfer; + dev_err(i2c_dev->dev,"i2cdriver: pool request failed for slave\n"); + i2c_dev->slave_xfer_mode = BYTE_XFER; + i2c_dev->slave_xfer_len = 1; + } + } + + i2c_dev->do_slave_xfer(i2c_dev); + +} + +static irqreturn_t i2c_ast_handler(int this_irq, void *dev_id) +{ + u32 sts; + u32 temp; + + struct ast_i2c_dev *i2c_dev = dev_id; + u32 isr_sts = readl(i2c_dev->ast_i2c_data->reg_gr); + + if(!(isr_sts & (1<< i2c_dev->bus_id))) + return IRQ_NONE; + + i2c_dev->state = (ast_i2c_read(i2c_dev,I2C_CMD_REG) >> 19) & 0xf; + sts = ast_i2c_read(i2c_dev,I2C_INTR_STS_REG); +// printk("ISR : %x , sts [%x]\n",sts , xfer_sts); +// dev_dbg(i2c_dev->dev,"ISR : %x , sts [%x]\n",sts , xfer_sts); + +// dev_dbg(i2c_dev->dev,"sts machine %x, slave_op %d \n", xfer_sts,i2c_dev->slave_operation); + + if(AST_I2CD_INTR_STS_SMBUS_ALT & sts) { + dev_dbg(i2c_dev->dev, "M clear isr: AST_I2CD_INTR_STS_SMBUS_ALT= %x\n",sts); + //Disable ALT INT + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev, I2C_INTR_CTRL_REG) & + ~AST_I2CD_SMBUS_ALT_INTR_EN, + I2C_INTR_CTRL_REG); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_SMBUS_ALT, I2C_INTR_STS_REG); + ast_master_alert_recv(i2c_dev); + sts &= ~AST_I2CD_SMBUS_ALT_INTR_EN; + } + + if(AST_I2CD_INTR_STS_ABNORMAL & sts) { + // TODO: observed abnormal interrupt happening when the bus is stressed with traffic + dev_err(i2c_dev->dev, "abnormal interrupt happens with status: %x, slave mode: %d\n", sts, i2c_dev->slave_operation); + // Need to clear the interrupt + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_ABNORMAL, I2C_INTR_STS_REG); + + i2c_dev->cmd_err |= AST_I2CD_INTR_STS_ABNORMAL; + complete(&i2c_dev->cmd_complete); + + return IRQ_HANDLED; + } + + switch(sts) { + case AST_I2CD_INTR_STS_TX_ACK: + if(i2c_dev->slave_operation == 1) { + i2c_dev->slave_event = I2C_SLAVE_EVENT_READ; + ast_i2c_slave_xfer_done(i2c_dev); + dev_dbg(i2c_dev->dev, "S clear isr: AST_I2CD_INTR_STS_TX_ACK = %x\n",sts); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_TX_ACK, I2C_INTR_STS_REG); + } else { + dev_dbg(i2c_dev->dev, "M clear isr: AST_I2CD_INTR_STS_TX_ACK = %x\n",sts); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_TX_ACK, I2C_INTR_STS_REG); + ast_i2c_master_xfer_done(i2c_dev); + } + break; + case AST_I2CD_INTR_STS_TX_ACK | AST_I2CD_INTR_STS_NORMAL_STOP: + if((i2c_dev->xfer_last == 1) && (i2c_dev->slave_operation == 0)) { + dev_dbg(i2c_dev->dev, "M clear isr: AST_I2CD_INTR_STS_TX_ACK | AST_I2CD_INTR_STS_NORMAL_STOP= %x\n",sts); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_TX_ACK | AST_I2CD_INTR_STS_NORMAL_STOP, I2C_INTR_STS_REG); + //take care + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_TX_ACK_INTR_EN, I2C_INTR_CTRL_REG); + ast_i2c_master_xfer_done(i2c_dev); + + } else { + dev_err(i2c_dev->dev,"ast_i2c: TX_ACK | NORMAL_STOP; xfer_last %d\n", i2c_dev->xfer_last); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_TX_ACK | AST_I2CD_INTR_STS_NORMAL_STOP, I2C_INTR_STS_REG); + uint32_t new_val = ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_NORMAL_STOP_INTR_EN | + AST_I2CD_TX_ACK_INTR_EN; + ast_i2c_write(i2c_dev, new_val, I2C_INTR_CTRL_REG); + //take care + i2c_dev->cmd_err |= AST_LOCKUP_DETECTED; + complete(&i2c_dev->cmd_complete); + } + break; + case AST_I2CD_INTR_STS_TX_ACK | AST_I2CD_INTR_STS_NORMAL_STOP | AST_I2CD_INTR_STS_SLAVE_MATCH : + if((i2c_dev->xfer_last == 1) && (i2c_dev->slave_operation == 0)) { + dev_dbg(i2c_dev->dev, "M clear isr: AST_I2CD_INTR_STS_TX_ACK | AST_I2CD_INTR_STS_NORMAL_STOP | AST_I2CD_INTR_STS_SLAVE_MATCH= %x\n",sts); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_TX_ACK | AST_I2CD_INTR_STS_NORMAL_STOP, I2C_INTR_STS_REG); + //take care + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_TX_ACK_INTR_EN, I2C_INTR_CTRL_REG); + ast_i2c_master_xfer_done(i2c_dev); + + // Handle the new slave match interrupt + ast_i2c_slave_addr_match(i2c_dev); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_SLAVE_MATCH, I2C_INTR_STS_REG); + } else { + dev_err(i2c_dev->dev, "Slave TX_ACK | NORMAL_STOP | AST_I2CD_INTR_STS_SLAVE_MATCH; xfer_last %d\n", i2c_dev->xfer_last); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_TX_ACK | AST_I2CD_INTR_STS_NORMAL_STOP, I2C_INTR_STS_REG); + uint32_t new_val = ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_NORMAL_STOP_INTR_EN | + AST_I2CD_TX_ACK_INTR_EN; + ast_i2c_write(i2c_dev, new_val, I2C_INTR_CTRL_REG); + //take care + i2c_dev->cmd_err |= AST_LOCKUP_DETECTED; + complete(&i2c_dev->cmd_complete); + + // stop previous slave transaction + i2c_dev->slave_event = I2C_SLAVE_EVENT_STOP; + ast_i2c_slave_xfer_done(i2c_dev); + + // Handle the new slave match interrupt + ast_i2c_slave_addr_match(i2c_dev); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_SLAVE_MATCH, I2C_INTR_STS_REG); + } + break; + + case AST_I2CD_INTR_STS_TX_NAK: + if(i2c_dev->slave_operation == 1) { + i2c_dev->slave_event = I2C_SLAVE_EVENT_NACK; + ast_i2c_slave_xfer_done(i2c_dev); + dev_err(i2c_dev->dev, "S clear isr: AST_I2CD_INTR_STS_TX_NAK = %x\n",sts); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_TX_NAK, I2C_INTR_STS_REG); + } else { + dev_dbg(i2c_dev->dev, "M clear isr: AST_I2CD_INTR_STS_TX_NAK = %x\n",sts); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_TX_NAK, I2C_INTR_STS_REG); + if (i2c_dev->master_msgs + && i2c_dev->master_msgs->flags & I2C_M_IGNORE_NAK) { + dev_dbg(i2c_dev->dev, "I2C_M_IGNORE_NAK next send\n"); + } else { + dev_dbg(i2c_dev->dev, "NAK error\n"); + i2c_dev->cmd_err |= AST_I2CD_INTR_STS_TX_NAK; + } + complete(&i2c_dev->cmd_complete); + } + break; + + case AST_I2CD_INTR_STS_TX_NAK | AST_I2CD_INTR_STS_NORMAL_STOP: + if(i2c_dev->slave_operation == 1) { + printk("SLAVE TODO .... \n"); + i2c_dev->slave_operation = 0; + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_TX_NAK | AST_I2CD_INTR_STS_NORMAL_STOP, I2C_INTR_STS_REG); + i2c_dev->cmd_err |= AST_I2CD_INTR_STS_TX_NAK | AST_I2CD_INTR_STS_NORMAL_STOP; + complete(&i2c_dev->cmd_complete); + } else { + dev_dbg(i2c_dev->dev, "M clear isr: AST_I2CD_INTR_STS_TX_NAK| AST_I2CD_INTR_STS_NORMAL_STOP = %x\n",sts); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_TX_NAK | AST_I2CD_INTR_STS_NORMAL_STOP, I2C_INTR_STS_REG); + dev_dbg(i2c_dev->dev, "M TX NAK | NORMAL STOP \n"); + i2c_dev->cmd_err |= AST_I2CD_INTR_STS_TX_NAK | AST_I2CD_INTR_STS_NORMAL_STOP; + complete(&i2c_dev->cmd_complete); + } + break; + + //Issue : Workaround for I2C slave mode + case AST_I2CD_INTR_STS_TX_NAK | AST_I2CD_INTR_STS_SLAVE_MATCH: + if(i2c_dev->slave_operation == 1) { + dev_err(i2c_dev->dev, "i2cdriver: TX_NAK and Slave match sts = %x\n",sts); + ast_i2c_slave_addr_match(i2c_dev); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_TX_NAK | AST_I2CD_INTR_STS_SLAVE_MATCH , I2C_INTR_STS_REG); + } else { + printk("ERROR !!!!\n"); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_TX_NAK, I2C_INTR_STS_REG); + i2c_dev->cmd_err |= AST_I2CD_INTR_STS_TX_NAK; + complete(&i2c_dev->cmd_complete); + + ast_i2c_slave_addr_match(i2c_dev); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_SLAVE_MATCH , I2C_INTR_STS_REG); + } + break; + case AST_I2CD_INTR_STS_RX_DOWN | AST_I2CD_INTR_STS_SLAVE_MATCH: + ast_i2c_slave_addr_match(i2c_dev); + dev_dbg(i2c_dev->dev, "S clear isr: AST_I2CD_INTR_STS_RX_DOWN | AST_I2CD_INTR_STS_SLAVE_MATCH = %x\n",sts); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_RX_DOWN | AST_I2CD_INTR_STS_SLAVE_MATCH, I2C_INTR_STS_REG); + break; + case AST_I2CD_INTR_STS_RX_DOWN | AST_I2CD_INTR_STS_SLAVE_MATCH | AST_I2CD_INTR_STS_ARBIT_LOSS: + dev_err(i2c_dev->dev, "M clear isr: sts = %x\n",sts); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_ARBIT_LOSS, I2C_INTR_STS_REG); + i2c_dev->cmd_err |= AST_I2CD_INTR_STS_ARBIT_LOSS; + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_RX_DOWN_INTR_EN, I2C_INTR_CTRL_REG); + complete(&i2c_dev->cmd_complete); + + ast_i2c_slave_addr_match(i2c_dev); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_RX_DOWN | AST_I2CD_INTR_STS_SLAVE_MATCH, I2C_INTR_STS_REG); + break; + case AST_I2CD_INTR_STS_RX_DOWN: + if(i2c_dev->slave_operation == 1) { + i2c_dev->slave_event = I2C_SLAVE_EVENT_WRITE; + ast_i2c_slave_xfer_done(i2c_dev); + dev_dbg(i2c_dev->dev, "S clear isr: AST_I2CD_INTR_STS_RX_DOWN = %x\n",sts); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_RX_DOWN, I2C_INTR_STS_REG); + } else { + dev_dbg(i2c_dev->dev, "M clear isr: AST_I2CD_INTR_STS_RX_DOWN = %x\n",sts); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_RX_DOWN, I2C_INTR_STS_REG); + ast_i2c_master_xfer_done(i2c_dev); + } + break; + + case AST_I2CD_INTR_STS_NORMAL_STOP: + if(i2c_dev->slave_operation == 1) { + i2c_dev->slave_event = I2C_SLAVE_EVENT_STOP; + ast_i2c_slave_xfer_done(i2c_dev); + dev_dbg(i2c_dev->dev, "S clear isr: AST_I2CD_INTR_STS_NORMAL_STOP = %x\n",sts); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_NORMAL_STOP, I2C_INTR_STS_REG); + dev_dbg(i2c_dev->dev, "state [%x] \n",i2c_dev->state); + } else { + dev_dbg(i2c_dev->dev, "M clear isr: AST_I2CD_INTR_STS_NORMAL_STOP = %x\n",sts); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_NORMAL_STOP, I2C_INTR_STS_REG); + i2c_dev->cmd_err |= AST_I2CD_INTR_STS_NORMAL_STOP; + complete(&i2c_dev->cmd_complete); + } + break; + case AST_I2CD_INTR_STS_RX_DOWN | AST_I2CD_INTR_STS_NORMAL_STOP: + /* Whether or not we're done, the hardware thinks we're done, so bail. */ + if(i2c_dev->slave_operation == 0) { + dev_dbg(i2c_dev->dev, "M clear isr: AST_I2CD_INTR_STS_RX_DOWN | AST_I2CD_INTR_STS_NORMAL_STOP = %x\n",sts); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_RX_DOWN | AST_I2CD_INTR_STS_NORMAL_STOP, I2C_INTR_STS_REG); + //take care + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_RX_DOWN_INTR_EN, I2C_INTR_CTRL_REG); + ast_i2c_master_xfer_done(i2c_dev); + } else { + dev_err(i2c_dev->dev, "S clear isr: AST_I2CD_INTR_STS_RX_DOWN | AST_I2CD_INTR_STS_NORMAL_STOP = %x\n",sts); + i2c_dev->slave_event = I2C_SLAVE_EVENT_STOP; + ast_i2c_slave_xfer_done(i2c_dev); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_RX_DOWN | AST_I2CD_INTR_STS_NORMAL_STOP, I2C_INTR_STS_REG); + ast_i2c_write(i2c_dev, ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) | + AST_I2CD_RX_DOWN_INTR_EN, I2C_INTR_CTRL_REG); + ast_i2c_master_xfer_done(i2c_dev); + } + break; + case AST_I2CD_INTR_STS_ARBIT_LOSS: + dev_err(i2c_dev->dev, "M clear isr: AST_I2CD_INTR_STS_ARBIT_LOSS = %x\n",sts); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_ARBIT_LOSS, I2C_INTR_STS_REG); + i2c_dev->cmd_err |= AST_I2CD_INTR_STS_ARBIT_LOSS; + complete(&i2c_dev->cmd_complete); + break; + case AST_I2CD_INTR_STS_SCL_TO: + dev_err(i2c_dev->dev, "SCL LOW detected with sts = %x, slave mode: %x\n",sts, i2c_dev->slave_operation); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_SCL_TO, I2C_INTR_STS_REG); + i2c_dev->cmd_err |= AST_I2CD_INTR_STS_SCL_TO; + complete(&i2c_dev->cmd_complete); + + // Reset i2c controller + temp = ast_i2c_read(i2c_dev,I2C_FUN_CTRL_REG); + + ast_i2c_write(i2c_dev, temp & ~(AST_I2CD_SLAVE_EN | AST_I2CD_MASTER_EN), I2C_FUN_CTRL_REG); + + ast_i2c_write(i2c_dev, temp, I2C_FUN_CTRL_REG); + break; + case AST_I2CD_INTR_STS_SLAVE_MATCH | AST_I2CD_INTR_STS_SCL_TO: + dev_err(i2c_dev->dev, "SCL LOW detected with sts = %x, slave mode: %x\n",sts, i2c_dev->slave_operation); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_SCL_TO | AST_I2CD_INTR_STS_SLAVE_MATCH, I2C_INTR_STS_REG); + i2c_dev->cmd_err |= AST_I2CD_INTR_STS_SCL_TO; + complete(&i2c_dev->cmd_complete); + + // Reset i2c controller + temp = ast_i2c_read(i2c_dev,I2C_FUN_CTRL_REG); + + ast_i2c_write(i2c_dev, temp & ~(AST_I2CD_SLAVE_EN | AST_I2CD_MASTER_EN), I2C_FUN_CTRL_REG); + + ast_i2c_write(i2c_dev, temp, I2C_FUN_CTRL_REG); + break; + case AST_I2CD_INTR_STS_GCALL_ADDR: + i2c_dev->cmd_err |= AST_I2CD_INTR_STS_GCALL_ADDR; + complete(&i2c_dev->cmd_complete); + + break; + case AST_I2CD_INTR_STS_SMBUS_DEF_ADDR: + break; + case AST_I2CD_INTR_STS_SMBUS_DEV_ALT: + break; + + case AST_I2CD_INTR_STS_SMBUS_ARP_ADDR: + break; + case AST_I2CD_INTR_STS_SDA_DL_TO: + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_SDA_DL_TO, I2C_INTR_STS_REG); + i2c_dev->cmd_err |= AST_I2CD_INTR_STS_SDA_DL_TO; + complete(&i2c_dev->cmd_complete); + break; + case AST_I2CD_INTR_STS_BUS_RECOVER: + dev_err(i2c_dev->dev, "Bus recover with sts= %x, slave mode: %x\n",sts, i2c_dev->slave_operation); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_BUS_RECOVER, I2C_INTR_STS_REG); + complete(&i2c_dev->cmd_complete); + break; + default: + printk("GR %x : Status : %x, bus_id %d\n",i2c_dev->ast_i2c_data->reg_gr, sts, i2c_dev->bus_id); + + // Handle Arbitration Loss + if (sts & AST_I2CD_INTR_STS_ARBIT_LOSS) { + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_ARBIT_LOSS, I2C_INTR_STS_REG); + i2c_dev->cmd_err |= AST_I2CD_INTR_STS_ARBIT_LOSS; + complete(&i2c_dev->cmd_complete); + sts &= (~AST_I2CD_INTR_STS_ARBIT_LOSS); + } + + // Handle the write transaction ACK + if (sts & AST_I2CD_INTR_STS_TX_ACK) { + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_TX_ACK, I2C_INTR_STS_REG); + ast_i2c_master_xfer_done(i2c_dev); + sts &= (~AST_I2CD_INTR_STS_TX_ACK); + } + + // Handle Normal Stop conditon + if (sts & AST_I2CD_INTR_STS_NORMAL_STOP) { + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_TX_ACK, I2C_INTR_STS_REG); + sts &= (~AST_I2CD_INTR_STS_NORMAL_STOP); + i2c_dev->cmd_err |= AST_I2CD_INTR_STS_NORMAL_STOP; + complete(&i2c_dev->cmd_complete); + } + + // Handle the Slave address match + if (sts & AST_I2CD_INTR_STS_SLAVE_MATCH) { + ast_i2c_slave_addr_match(i2c_dev); + sts &= (~AST_I2CD_INTR_STS_SLAVE_MATCH); + ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_SLAVE_MATCH, I2C_INTR_STS_REG); + } + + + // TODO: Debug print for any unhandled condition + if(sts) { + printk("GR %x : Status : %x, bus_id %d\n",i2c_dev->ast_i2c_data->reg_gr, sts, i2c_dev->bus_id); + } + + //TODO: Clearing this interrupt for now, but needs to cleanup this ISR function + ast_i2c_write(i2c_dev, sts, I2C_INTR_STS_REG); + + return IRQ_HANDLED; + } + + return IRQ_HANDLED; +} + +static int ast_i2c_do_msgs_xfer(struct ast_i2c_dev *i2c_dev, struct i2c_msg *msgs, int num) +{ + int i; + int ret = 1; + unsigned long flags; + + spin_lock_irqsave(&i2c_dev->master_lock, flags); + + //request: update master_xfer_mode based on master_dma selection + if(i2c_dev->ast_i2c_data->master_dma == BYTE_MODE) { + i2c_dev->do_master_xfer = ast_i2c_do_byte_xfer; + i2c_dev->master_xfer_mode = BYTE_XFER; + i2c_dev->master_xfer_len = 1; + } else if (i2c_dev->ast_i2c_data->master_dma == DMA_MODE) { + i2c_dev->do_master_xfer = ast_i2c_do_dma_xfer; + i2c_dev->master_xfer_mode = DMA_XFER; + } else { + if(i2c_dev->ast_i2c_data->request_pool_buff_page(&(i2c_dev->req_page)) == 0) { + i2c_dev->do_master_xfer = ast_i2c_do_pool_xfer; + i2c_dev->master_xfer_mode = BUFF_XFER; + } else { + i2c_dev->do_master_xfer = ast_i2c_do_byte_xfer; + dev_err(i2c_dev->dev, "i2cdriver: pool request failed for master\n"); + i2c_dev->master_xfer_mode = BYTE_XFER; + i2c_dev->master_xfer_len = 1; + + } + } + +// printk("start xfer ret = %d \n",ret); + + for (i=0; i < num; i++) { + i2c_dev->blk_r_flag = 0; + i2c_dev->master_msgs = &msgs[i]; + if(num == i+1) + i2c_dev->xfer_last = 1; + else + i2c_dev->xfer_last = 0; + + i2c_dev->blk_r_flag = 0; + init_completion(&i2c_dev->cmd_complete); + i2c_dev->cmd_err = 0; + + if(i2c_dev->master_msgs->flags & I2C_M_NOSTART) + i2c_dev->master_xfer_cnt = 0; + else + i2c_dev->master_xfer_cnt = -1; + + i2c_dev->do_master_xfer(i2c_dev); + + spin_unlock_irqrestore(&i2c_dev->master_lock, flags); + + ret = wait_for_completion_interruptible_timeout(&i2c_dev->cmd_complete, + i2c_dev->adap.timeout*HZ); + + spin_lock_irqsave(&i2c_dev->master_lock, flags); + i2c_dev->master_msgs = NULL; + + if (ret == 0) { + dev_err(i2c_dev->dev, "controller timed out\n"); + i2c_dev->state = (ast_i2c_read(i2c_dev,I2C_CMD_REG) >> 19) & 0xf; +// printk("sts [%x], isr sts [%x] \n",i2c_dev->state, ast_i2c_read(i2c_dev,I2C_INTR_STS_REG)); + ret = -ETIMEDOUT; + spin_unlock_irqrestore(&i2c_dev->master_lock, flags); + goto stop; + } + + if(i2c_dev->cmd_err != 0 && + i2c_dev->cmd_err != AST_I2CD_INTR_STS_NORMAL_STOP) { + if (i2c_dev->cmd_err & AST_LOCKUP_DETECTED) { + printk("ast-i2c: error got unexpected STOP\n"); + // reset the bus + ast_i2c_bus_error_recover(i2c_dev); + } + ret = -EAGAIN; + spin_unlock_irqrestore(&i2c_dev->master_lock, flags); + goto stop; + } + } + + spin_unlock_irqrestore(&i2c_dev->master_lock, flags); + + if(i2c_dev->cmd_err == 0 || + i2c_dev->cmd_err == AST_I2CD_INTR_STS_NORMAL_STOP) { + ret = num; + goto out; + + } +stop: + if(i2c_dev->cmd_err & AST_I2CD_INTR_STS_NORMAL_STOP) + goto out; + init_completion(&i2c_dev->cmd_complete); + i2c_dev->cmd_err = 0; + ast_i2c_write(i2c_dev, AST_I2CD_M_STOP_CMD, I2C_CMD_REG); + wait_for_completion_interruptible_timeout(&i2c_dev->cmd_complete, + i2c_dev->adap.timeout*HZ); + +out: + //Free .. + if(i2c_dev->master_xfer_mode == BUFF_XFER) { + i2c_dev->ast_i2c_data->free_pool_buff_page(i2c_dev->req_page); + + } + dev_dbg(i2c_dev->dev, "end xfer ret = %d, xfer mode[%d]\n",ret, i2c_dev->master_xfer_mode); + return ret; + +} + +static int ast_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct ast_i2c_dev *i2c_dev = adap->algo_data; + int ret, i; + int sts; + + sts = ast_i2c_read(i2c_dev,I2C_CMD_REG); + dev_dbg(i2c_dev->dev, "state[%x],SCL[%d],SDA[%d],BUS[%d]\n", (sts >> 19) & 0xf, (sts >> 18) & 0x1,(sts >> 17) & 0x1,(sts >> 16) & 1); + /* + * Wait for the bus to become free. + */ + + ret = ast_i2c_wait_bus_not_busy(i2c_dev); + if (ret) { + dev_err(&i2c_dev->adap.dev, "i2c_ast: timeout waiting for bus free\n"); + goto out; + } + + for (i = adap->retries; i >= 0; i--) { + + ret = ast_i2c_do_msgs_xfer(i2c_dev, msgs, num); + if (ret != -EAGAIN) + goto out; + dev_dbg(&i2c_dev->adap.dev, "Retrying transmission [%d]\n",i); + udelay(100); + } + + ret = -EREMOTEIO; +out: + + return ret; +} + +static u32 ast_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static const struct i2c_algorithm i2c_ast_algorithm = { + .master_xfer = ast_i2c_xfer, +#ifdef CONFIG_AST_I2C_SLAVE_RDWR + .slave_xfer = ast_i2c_slave_xfer, +#endif + .functionality = ast_i2c_functionality, +}; + +static int ast_i2c_probe(struct platform_device *pdev) +{ + struct ast_i2c_dev *i2c_dev; + struct resource *res; + int ret; + + dev_dbg(&pdev->dev, "ast_i2c_probe \n"); + + i2c_dev = kzalloc(sizeof(struct ast_i2c_dev), GFP_KERNEL); + if (!i2c_dev) { + ret = -ENOMEM; + goto err_no_mem; + } + + i2c_dev->ast_i2c_data = pdev->dev.platform_data; + if(i2c_dev->ast_i2c_data->master_dma == BUFF_MODE) { + dev_dbg(&pdev->dev, "use buffer pool mode 256\n"); + + } else if ((i2c_dev->ast_i2c_data->master_dma == DMA_MODE) || (i2c_dev->ast_i2c_data->slave_dma == DMA_MODE)) { + dev_dbg(&pdev->dev, "use dma mode \n"); + if (!i2c_dev->dma_buf) { + i2c_dev->dma_buf = dma_alloc_coherent(NULL, AST_I2C_DMA_SIZE, &i2c_dev->dma_addr, GFP_KERNEL); + if (!i2c_dev->dma_buf) { + printk("unable to allocate tx Buffer memory\n"); + ret = -ENOMEM; + goto err_no_dma; + } + if(i2c_dev->dma_addr%4 !=0) { + printk("not 4 byte boundary \n"); + ret = -ENOMEM; + goto err_no_dma; + } +// printk("dma_buf = [0x%x] dma_addr = [0x%x], please check 4byte boundary \n",i2c_dev->dma_buf,i2c_dev->dma_addr); + memset (i2c_dev->dma_buf, 0, AST_I2C_DMA_SIZE); + } + + } else { + //master_mode 0: use byte mode + dev_dbg(&pdev->dev, "use default byte mode \n"); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (NULL == res) { + dev_err(&pdev->dev, "cannot get IORESOURCE_MEM\n"); + ret = -ENOENT; + goto err_no_io_res; + } + if (!request_mem_region(res->start, resource_size(res), res->name)) { + dev_err(&pdev->dev, "cannot reserved region\n"); + ret = -ENXIO; + goto err_no_io_res; + } + + i2c_dev->reg_base = ioremap(res->start, resource_size(res)); + if (!i2c_dev->reg_base) { + ret = -EIO; + goto release_mem; + } + + i2c_dev->irq = platform_get_irq(pdev, 0); + if (i2c_dev->irq < 0) { + dev_err(&pdev->dev, "no irq specified\n"); + ret = -ENOENT; + goto ereqirq; + } + + i2c_dev->dev = &pdev->dev; + +#if defined (CONFIG_ARCH_AST1070) + if(i2c_dev->irq == IRQ_C0_I2C) { + i2c_dev->bus_id = pdev->id - NUM_BUS; + dev_dbg(&pdev->dev, "C0 :: pdev->id %d , i2c_dev->bus_id = %d, i2c_dev->irq =%d\n",pdev->id, i2c_dev->bus_id,i2c_dev->irq); +#if (CONFIG_AST1070_NR >= 2) + } else if(i2c_dev->irq == IRQ_C1_I2C) { + i2c_dev->bus_id = pdev->id - (NUM_BUS + 8); + dev_dbg(&pdev->dev, "C1 :: pdev->id %d , i2c_dev->bus_id = %d, i2c_dev->irq =%d\n",pdev->id, i2c_dev->bus_id,i2c_dev->irq); +#endif + } else { + i2c_dev->bus_id = pdev->id; + dev_dbg(&pdev->dev, "AST pdev->id %d , i2c_dev->bus_id = %d, i2c_dev->irq =%d\n",pdev->id, i2c_dev->bus_id,i2c_dev->irq); + } +#else + i2c_dev->bus_id = pdev->id; +#endif + + /* Initialize the I2C adapter */ + i2c_dev->adap.owner = THIS_MODULE; +//TODO + i2c_dev->adap.retries = 0; + +// i2c_dev->adap.retries = 3; + + i2c_dev->adap.timeout = 5; + + i2c_dev->master_xfer_mode = BYTE_XFER; + + /* + * If "pdev->id" is negative we consider it as zero. + * The reason to do so is to avoid sysfs names that only make + * sense when there are multiple adapters. + */ + i2c_dev->adap.nr = pdev->id != -1 ? pdev->id : 0; + snprintf(i2c_dev->adap.name, sizeof(i2c_dev->adap.name), "ast_i2c.%u", + i2c_dev->adap.nr); + + i2c_dev->slave_operation = 0; + i2c_dev->blk_r_flag = 0; + i2c_dev->adap.algo = &i2c_ast_algorithm; + + ast_i2c_dev_init(i2c_dev); + + ret = request_irq(i2c_dev->irq, i2c_ast_handler, IRQF_SHARED, + i2c_dev->adap.name, i2c_dev); + if (ret) { + printk(KERN_INFO "I2C: Failed request irq %d\n", i2c_dev->irq); + goto ereqirq; + } + + spin_lock_init(&i2c_dev->master_lock); + +#ifdef CONFIG_AST_I2C_SLAVE_RDWR + ast_i2c_slave_buff_init(i2c_dev); + spin_lock_init(&i2c_dev->slave_rx_lock); +#endif + + i2c_dev->adap.algo_data = i2c_dev; + i2c_dev->adap.dev.parent = &pdev->dev; + + i2c_dev->adap.id = pdev->id; + + ret = i2c_add_numbered_adapter(&i2c_dev->adap); + if (ret < 0) { + printk(KERN_INFO "I2C: Failed to add bus\n"); + goto eadapt; + } + + platform_set_drvdata(pdev, i2c_dev); + + printk(KERN_INFO "I2C: %s: AST I2C adapter [%d khz]\n", + i2c_dev->adap.dev.bus_id,i2c_dev->ast_i2c_data->bus_clk/1000); + + return 0; + +eadapt: + free_irq(i2c_dev->irq, i2c_dev); +ereqirq: + iounmap(i2c_dev->reg_base); + +release_mem: + release_mem_region(res->start, resource_size(res)); +err_no_io_res: +err_no_dma: + kfree(i2c_dev); + +err_no_mem: + return ret; +} + +static int ast_i2c_remove(struct platform_device *pdev) +{ + struct ast_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + struct resource *res; + + platform_set_drvdata(pdev, NULL); + i2c_del_adapter(&i2c_dev->adap); + + free_irq(i2c_dev->irq, i2c_dev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iounmap(i2c_dev->reg_base); + release_mem_region(res->start, res->end - res->start + 1); + + kfree(i2c_dev); + + return 0; +} + +#ifdef CONFIG_PM +static int ast_i2c_suspend(struct platform_device *pdev, pm_message_t state) +{ + //TODO +// struct ast_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + return 0; +} + +static int ast_i2c_resume(struct platform_device *pdev) +{ + //TODO +// struct ast_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + //Should reset i2c ??? + return 0; +} +#else +#define ast_i2c_suspend NULL +#define ast_i2c_resume NULL +#endif + +static struct platform_driver i2c_ast_driver = { + .probe = ast_i2c_probe, + .remove = __devexit_p(ast_i2c_remove), + .suspend = ast_i2c_suspend, + .resume = ast_i2c_resume, + .driver = { + .name = "ast-i2c", + .owner = THIS_MODULE, + }, +}; + +static int __init ast_i2c_init(void) +{ + return platform_driver_register(&i2c_ast_driver); +} + +static void __exit ast_i2c_exit(void) +{ + platform_driver_unregister(&i2c_ast_driver); +} +//TODO : check module init sequence +module_init(ast_i2c_init); +module_exit(ast_i2c_exit); + +MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>"); +MODULE_DESCRIPTION("ASPEED AST I2C Bus Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ast_i2c"); |