diff options
author | Tian Fang <tfang@fb.com> | 2016-04-13 01:49:31 +0100 |
---|---|---|
committer | Javier Jardón <jjardon@gnome.org> | 2016-04-13 01:50:31 +0100 |
commit | 3842b07578ca8c7473e5d96e0bd2000629540426 (patch) | |
tree | 9df2e875b6355b1d7f32a33a00797785a85584d7 /drivers/i2c/busses/i2c-ast.c | |
parent | 1812494d7169dd66ffd0a70132361416f4005963 (diff) | |
download | linux-stable-3842b07578ca8c7473e5d96e0bd2000629540426.tar.gz |
Linux snapshot of OpenBMC f926614
Diffstat (limited to 'drivers/i2c/busses/i2c-ast.c')
-rw-r--r-- | drivers/i2c/busses/i2c-ast.c | 296 |
1 files changed, 217 insertions, 79 deletions
diff --git a/drivers/i2c/busses/i2c-ast.c b/drivers/i2c/busses/i2c-ast.c index 9bb3154e6bd8..48f019278907 100644 --- a/drivers/i2c/busses/i2c-ast.c +++ b/drivers/i2c/busses/i2c-ast.c @@ -49,14 +49,22 @@ /***************************************************************************/ #ifdef CONFIG_AST_I2C_SLAVE_RDWR -#define I2C_S_BUF_SIZE 64 -#define I2C_S_RX_BUF_NUM 4 +#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; @@ -192,12 +200,12 @@ static void ast_i2c_dev_init(struct ast_i2c_dev *i2c_dev) , I2C_FUN_CTRL_REG); /* Set AC Timing */ - ast_i2c_write(i2c_dev, 0x3, I2C_AC_TIMING_REG2); + 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_NO_TIMEOUT_CTRL, I2C_AC_TIMING_REG2); + ast_i2c_write(i2c_dev, AST_I2C_LOW_TIMEOUT, I2C_AC_TIMING_REG2); } #else /* target apeed is xxKhz*/ @@ -252,6 +260,7 @@ static void ast_i2c_slave_buff_init(struct ast_i2c_dev *i2c_dev) 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); @@ -265,15 +274,31 @@ static void ast_i2c_slave_rdwr_xfer(struct ast_i2c_dev *i2c_dev) } } if(i == I2C_S_RX_BUF_NUM) { - printk("RX buffer full ........use tmp msgs buff \n"); - //TODO... + // 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"); + // 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: @@ -379,17 +404,17 @@ ast_i2c_bus_error_recover(struct ast_i2c_dev *i2c_dev) if ((sts & AST_I2CD_SDA_LINE_STS) && (sts & AST_I2CD_SCL_LINE_STS)) { //Means bus is idle. - dev_dbg(i2c_dev->dev, "I2C bus (%d) is idle. I2C slave doesn't exist?!\n", i2c_dev->bus_id); + dev_err(i2c_dev->dev, "I2C bus (%d) is idle. I2C slave doesn't exist?!\n", i2c_dev->bus_id); return -1; } - dev_dbg(i2c_dev->dev, "ERROR!! I2C(%d) bus hanged, try to recovery it!\n", i2c_dev->bus_id); + 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_dbg(i2c_dev->dev, "I2C's master is locking the bus, try to stop it.\n"); + 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; @@ -401,15 +426,15 @@ ast_i2c_bus_error_recover(struct ast_i2c_dev *i2c_dev) if(i2c_dev->cmd_err && i2c_dev->cmd_err != AST_I2CD_INTR_STS_NORMAL_STOP) { - dev_dbg(i2c_dev->dev, "recovery error \n"); + dev_err(i2c_dev->dev, "recovery error \n"); return -1; } if (r == 0) { - dev_dbg(i2c_dev->dev, "recovery timed out\n"); + dev_err(i2c_dev->dev, "recovery timed out\n"); return -1; } else { - dev_dbg(i2c_dev->dev, "Recovery successfully\n"); + dev_err(i2c_dev->dev, "Recovery successfully\n"); return 0; } @@ -417,19 +442,20 @@ ast_i2c_bus_error_recover(struct ast_i2c_dev *i2c_dev) } 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_dbg(i2c_dev->dev, "I2C's slave is dead, try to recover it\n"); + 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_dbg(i2c_dev->dev, "ERROR!! Failed to do recovery command(0x%08x)\n", i2c_dev->cmd_err); + 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 @@ -438,14 +464,14 @@ ast_i2c_bus_error_recover(struct ast_i2c_dev *i2c_dev) break; } if (i == 10) { - dev_dbg(i2c_dev->dev, "ERROR!! recover failed\n"); + dev_err(i2c_dev->dev, "ERROR!! recover failed\n"); return -1; } } else { - dev_dbg(i2c_dev->dev, "Don't know how to handle this case?!\n"); + dev_err(i2c_dev->dev, "Don't know how to handle this case?!\n"); return -1; } - dev_dbg(i2c_dev->dev, "Recovery successfully\n"); + dev_err(i2c_dev->dev, "Recovery successfully\n"); return 0; } @@ -472,7 +498,8 @@ static int ast_i2c_wait_bus_not_busy(struct ast_i2c_dev *i2c_dev) } if (timeout <= 0) { - return -EAGAIN; + //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 @@ -491,6 +518,8 @@ static int ast_i2c_wait_bus_not_busy(struct ast_i2c_dev *i2c_dev) return 0; } + // TODO: hack to reset slave operation flag manually + i2c_dev->slave_operation = 0; return 0; } @@ -499,9 +528,6 @@ static void ast_i2c_do_dma_xfer(struct ast_i2c_dev *i2c_dev) u32 cmd = 0; int i; - i2c_dev->master_xfer_mode = DMA_XFER; - i2c_dev->slave_xfer_mode = DMA_XFER; - if(i2c_dev->slave_operation == 1) { if(i2c_dev->slave_msgs->flags & I2C_M_RD) { //DMA tx mode @@ -683,9 +709,6 @@ static void ast_i2c_do_pool_xfer(struct ast_i2c_dev *i2c_dev) int i; u32 *tx_buf; - i2c_dev->master_xfer_mode = BUFF_XFER; - i2c_dev->slave_xfer_mode = BUFF_XFER; - #if defined(CONFIG_ARCH_AST2400) ast_i2c_write(i2c_dev, (ast_i2c_read(i2c_dev, I2C_FUN_CTRL_REG) & @@ -872,12 +895,6 @@ static void ast_i2c_do_byte_xfer(struct ast_i2c_dev *i2c_dev) u8 *xfer_buf; u32 cmd = 0; - i2c_dev->master_xfer_mode = BYTE_XFER; - i2c_dev->master_xfer_len = 1; - - i2c_dev->slave_xfer_mode = BYTE_XFER; - i2c_dev->slave_xfer_len = 1; - 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) { @@ -973,14 +990,16 @@ 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->master_xfer_mode == BYTE_XFER) { + if(i2c_dev->slave_xfer_mode == BYTE_XFER) { xfer_len = 1; - } else if (i2c_dev->master_xfer_mode == BUFF_XFER) { + } 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); @@ -1004,10 +1023,12 @@ static void ast_i2c_slave_xfer_done(struct ast_i2c_dev *i2c_dev) 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->master_xfer_mode == BUFF_XFER) { + } 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; @@ -1056,7 +1077,7 @@ static void ast_i2c_slave_xfer_done(struct ast_i2c_dev *i2c_dev) i2c_dev->slave_xfer_cnt = 0; } else { if(i2c_dev->slave_xfer_cnt == i2c_dev->slave_msgs->len) { - dev_dbg(i2c_dev->dev,"slave next msgs \n"); + 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 @@ -1068,6 +1089,8 @@ static void ast_i2c_slave_xfer_done(struct ast_i2c_dev *i2c_dev) 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"); @@ -1077,7 +1100,10 @@ static void ast_i2c_slave_xfer_done(struct ast_i2c_dev *i2c_dev) 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; + } } @@ -1212,6 +1238,8 @@ unlock_out: 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; @@ -1219,7 +1247,6 @@ static void ast_i2c_slave_addr_match(struct ast_i2c_dev *i2c_dev) 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 { @@ -1228,23 +1255,33 @@ static void ast_i2c_slave_addr_match(struct ast_i2c_dev *i2c_dev) #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; + 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 - if(i2c_dev->ast_i2c_data->slave_dma == BYTE_MODE) + //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; - else if (i2c_dev->ast_i2c_data->slave_dma == DMA_MODE) + 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; - else { - if(i2c_dev->ast_i2c_data->request_pool_buff_page(&(i2c_dev->req_page)) == 0) + 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; - else + 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); @@ -1254,6 +1291,7 @@ static void ast_i2c_slave_addr_match(struct ast_i2c_dev *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); @@ -1280,17 +1318,15 @@ static irqreturn_t i2c_ast_handler(int this_irq, void *dev_id) } if(AST_I2CD_INTR_STS_ABNORMAL & sts) { - i2c_dev->cmd_err |= AST_I2CD_INTR_STS_ABNORMAL; - // Turn off interrupts for further abnormal - // conditions until we fix this one. - ast_i2c_write(i2c_dev, - ast_i2c_read(i2c_dev,I2C_INTR_CTRL_REG) & - ~AST_I2CD_ABNORMAL_INTR_EN, - I2C_INTR_CTRL_REG); - complete(&i2c_dev->cmd_complete); - sts &= ~AST_I2CD_INTR_STS_ABNORMAL; + // 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) { @@ -1316,15 +1352,47 @@ static irqreturn_t i2c_ast_handler(int this_irq, void *dev_id) ast_i2c_master_xfer_done(i2c_dev); } else { - printk("ast_i2c: TX_ACK | NORMAL_STOP; xfer_last %d\n", i2c_dev->xfer_last); + 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_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; @@ -1332,9 +1400,8 @@ static irqreturn_t i2c_ast_handler(int this_irq, void *dev_id) if(i2c_dev->slave_operation == 1) { i2c_dev->slave_event = I2C_SLAVE_EVENT_NACK; ast_i2c_slave_xfer_done(i2c_dev); - dev_dbg(i2c_dev->dev, "S clear isr: AST_I2CD_INTR_STS_TX_NAK = %x\n",sts); + 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); @@ -1352,7 +1419,10 @@ static irqreturn_t i2c_ast_handler(int this_irq, void *dev_id) 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); @@ -1365,12 +1435,17 @@ static irqreturn_t i2c_ast_handler(int this_irq, void *dev_id) //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) { - i2c_dev->slave_event = I2C_SLAVE_EVENT_NACK; - ast_i2c_slave_xfer_done(i2c_dev); + 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: @@ -1378,7 +1453,17 @@ static irqreturn_t i2c_ast_handler(int this_irq, void *dev_id) 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; @@ -1389,7 +1474,6 @@ static irqreturn_t i2c_ast_handler(int this_irq, void *dev_id) 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; @@ -1407,7 +1491,7 @@ static irqreturn_t i2c_ast_handler(int this_irq, void *dev_id) complete(&i2c_dev->cmd_complete); } break; - case (AST_I2CD_INTR_STS_RX_DOWN | AST_I2CD_INTR_STS_NORMAL_STOP): + 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); @@ -1416,18 +1500,47 @@ static irqreturn_t i2c_ast_handler(int this_irq, void *dev_id) 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_dbg(i2c_dev->dev, "M clear isr: AST_I2CD_INTR_STS_ARBIT_LOSS = %x\n",sts); + 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; @@ -1437,24 +1550,26 @@ static irqreturn_t i2c_ast_handler(int this_irq, void *dev_id) 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_dbg(i2c_dev->dev, "M clear isr: AST_I2CD_INTR_STS_BUS_RECOVER= %x\n",sts); + 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: - //TODO: Clearing this interrupt for now, but needs to cleanup this ISR function - ast_i2c_write(i2c_dev, sts, I2C_INTR_STS_REG); + 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); @@ -1462,22 +1577,35 @@ static irqreturn_t i2c_ast_handler(int this_irq, void *dev_id) // 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); - complete(&i2c_dev->cmd_complete); 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; } @@ -1492,16 +1620,25 @@ static int ast_i2c_do_msgs_xfer(struct ast_i2c_dev *i2c_dev, struct i2c_msg *msg spin_lock_irqsave(&i2c_dev->master_lock, flags); - //request - if(i2c_dev->ast_i2c_data->master_dma == BYTE_MODE) + //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; - else if (i2c_dev->ast_i2c_data->master_dma == DMA_MODE) + 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; - else { - if(i2c_dev->ast_i2c_data->request_pool_buff_page(&(i2c_dev->req_page)) == 0) + 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; - else + 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); @@ -1534,7 +1671,7 @@ static int ast_i2c_do_msgs_xfer(struct ast_i2c_dev *i2c_dev, struct i2c_msg *msg i2c_dev->master_msgs = NULL; if (ret == 0) { - dev_dbg(i2c_dev->dev, "controller timed out\n"); + 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; @@ -1564,9 +1701,10 @@ static int ast_i2c_do_msgs_xfer(struct ast_i2c_dev *i2c_dev, struct i2c_msg *msg } stop: - init_completion(&i2c_dev->cmd_complete); 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); @@ -1605,7 +1743,7 @@ static int ast_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) ret = ast_i2c_do_msgs_xfer(i2c_dev, msgs, num); if (ret != -EAGAIN) goto out; - dev_dbg(&adap->dev, "Retrying transmission [%d]\n",i); + dev_dbg(&i2c_dev->adap.dev, "Retrying transmission [%d]\n",i); udelay(100); } |