diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-ast.c')
-rw-r--r-- | drivers/i2c/busses/i2c-ast.c | 115 |
1 files changed, 85 insertions, 30 deletions
diff --git a/drivers/i2c/busses/i2c-ast.c b/drivers/i2c/busses/i2c-ast.c index bccf5a33bc1e..7a083de714df 100644 --- a/drivers/i2c/busses/i2c-ast.c +++ b/drivers/i2c/busses/i2c-ast.c @@ -88,8 +88,10 @@ struct ast_i2c_dev { struct i2c_msg slave_rx_msg[I2C_S_RX_BUF_NUM + 1]; struct i2c_msg slave_tx_msg; +static spinlock_t slave_rx_lock = SPIN_LOCK_UNLOCKED; #endif +static spinlock_t g_master_lock = SPIN_LOCK_UNLOCKED; static inline void ast_i2c_write(struct ast_i2c_dev *i2c_dev, u32 val, u32 reg) @@ -243,8 +245,9 @@ 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; - spinlock_t lock; - spin_lock(&lock); + unsigned long flags; + + spin_lock_irqsave(&slave_rx_lock, flags); switch(i2c_dev->slave_event) { case I2C_SLAVE_EVENT_START_WRITE: @@ -291,7 +294,7 @@ static void ast_i2c_slave_rdwr_xfer(struct ast_i2c_dev *i2c_dev) i2c_dev->slave_msgs = &slave_tx_msg; break; } - spin_unlock(&lock); + spin_unlock_irqrestore(&slave_rx_lock, flags); } @@ -299,11 +302,14 @@ 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(&slave_rx_lock, flags); + for(i=0; i<I2C_S_RX_BUF_NUM; i++) { if((slave_rx_msg[i].addr == 0) && (slave_rx_msg[i].flags == BUFF_FULL)) { memcpy(msgs->buf, slave_rx_msg[i].buf, slave_rx_msg[i].len); @@ -313,6 +319,7 @@ static int ast_i2c_slave_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs) break; } } + spin_unlock_irqrestore(&slave_rx_lock, flags); if(i == I2C_S_RX_BUF_NUM) { printk("No buffer ........ \n"); @@ -378,13 +385,15 @@ ast_i2c_bus_error_recover(struct ast_i2c_dev *i2c_dev) dev_dbg(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) { + if(i2c_dev->cmd_err && + i2c_dev->cmd_err != AST_I2CD_INTR_STS_NORMAL_STOP) { dev_dbg(i2c_dev->dev, "recovery error \n"); return -1; } @@ -411,7 +420,8 @@ ast_i2c_bus_error_recover(struct ast_i2c_dev *i2c_dev) r = wait_for_completion_interruptible_timeout(&i2c_dev->cmd_complete, i2c_dev->adap.timeout*HZ); - if (i2c_dev->cmd_err != 0) { + 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); return -1; } @@ -621,7 +631,9 @@ static void ast_i2c_do_dma_xfer(struct ast_i2c_dev *i2c_dev) }else { //should send next msg if(i2c_dev->master_xfer_cnt != i2c_dev->master_msgs->len) - printk("complete rx ... ERROR \n"); + 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; @@ -812,7 +824,9 @@ static void ast_i2c_do_pool_xfer(struct ast_i2c_dev *i2c_dev) } else { //should send next msg if(i2c_dev->master_xfer_cnt != i2c_dev->master_msgs->len) - printk("complete rx ... ERROR \n"); + 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; @@ -909,7 +923,9 @@ static void ast_i2c_do_byte_xfer(struct ast_i2c_dev *i2c_dev) } else { //should send next msg if(i2c_dev->master_xfer_cnt != i2c_dev->master_msgs->len) - printk("CNT ERROR \n"); + 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; @@ -1039,6 +1055,18 @@ 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(&g_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); @@ -1118,7 +1146,9 @@ next_xfer: if(xfer_len !=i2c_dev->master_xfer_len) { //TODO.. - printk(" ** xfer error \n"); + 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; @@ -1142,6 +1172,9 @@ done_out: dev_dbg(i2c_dev->dev,"msgs complete \n"); complete(&i2c_dev->cmd_complete); } + +unlock_out: + spin_unlock_irqrestore(&g_master_lock, flags); } static void ast_i2c_slave_addr_match(struct ast_i2c_dev *i2c_dev) @@ -1214,6 +1247,18 @@ static irqreturn_t i2c_ast_handler(int this_irq, void *dev_id) sts &= ~AST_I2CD_SMBUS_ALT_INTR_EN; } + 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; + } + switch(sts) { case AST_I2CD_INTR_STS_TX_ACK: if(i2c_dev->slave_operation == 1) { @@ -1251,12 +1296,12 @@ static irqreturn_t i2c_ast_handler(int this_irq, void *dev_id) } 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->flags == I2C_M_IGNORE_NAK) { + 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"); - i2c_dev->cmd_err = 0; } else { dev_dbg(i2c_dev->dev, "NAK error\n"); - i2c_dev->cmd_err = AST_I2CD_INTR_STS_TX_NAK; + i2c_dev->cmd_err |= AST_I2CD_INTR_STS_TX_NAK; } complete(&i2c_dev->cmd_complete); } @@ -1270,7 +1315,7 @@ static irqreturn_t i2c_ast_handler(int this_irq, void *dev_id) 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; + i2c_dev->cmd_err |= AST_I2CD_INTR_STS_TX_NAK | AST_I2CD_INTR_STS_NORMAL_STOP; complete(&i2c_dev->cmd_complete); } break; @@ -1316,39 +1361,34 @@ static irqreturn_t i2c_ast_handler(int this_irq, void *dev_id) } 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 = 0; + 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): - if((i2c_dev->xfer_last == 1) && (i2c_dev->slave_operation == 0)) { + /* 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 { - printk("TODO .. .. ..\n"); } 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); 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_ABNORMAL: - i2c_dev->cmd_err = AST_I2CD_INTR_STS_ABNORMAL; + i2c_dev->cmd_err |= AST_I2CD_INTR_STS_ARBIT_LOSS; complete(&i2c_dev->cmd_complete); break; case AST_I2CD_INTR_STS_SCL_TO: - i2c_dev->cmd_err = AST_I2CD_INTR_STS_SCL_TO; + i2c_dev->cmd_err |= AST_I2CD_INTR_STS_SCL_TO; complete(&i2c_dev->cmd_complete); break; case AST_I2CD_INTR_STS_GCALL_ADDR: - i2c_dev->cmd_err = AST_I2CD_INTR_STS_GCALL_ADDR; + i2c_dev->cmd_err |= AST_I2CD_INTR_STS_GCALL_ADDR; complete(&i2c_dev->cmd_complete); break; @@ -1365,7 +1405,6 @@ static irqreturn_t i2c_ast_handler(int this_irq, void *dev_id) case AST_I2CD_INTR_STS_BUS_RECOVER: dev_dbg(i2c_dev->dev, "M clear isr: AST_I2CD_INTR_STS_BUS_RECOVER= %x\n",sts); ast_i2c_write(i2c_dev, AST_I2CD_INTR_STS_BUS_RECOVER, I2C_INTR_STS_REG); - i2c_dev->cmd_err = 0; complete(&i2c_dev->cmd_complete); break; default: @@ -1382,6 +1421,9 @@ static int ast_i2c_do_msgs_xfer(struct ast_i2c_dev *i2c_dev, struct i2c_msg *msg { int i; int ret = 1; + unsigned long flags; + + spin_lock_irqsave(&g_master_lock, flags); //request if(i2c_dev->ast_i2c_data->master_dma == BYTE_MODE) @@ -1407,6 +1449,7 @@ static int ast_i2c_do_msgs_xfer(struct ast_i2c_dev *i2c_dev, struct i2c_msg *msg 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; @@ -1415,31 +1458,43 @@ static int ast_i2c_do_msgs_xfer(struct ast_i2c_dev *i2c_dev, struct i2c_msg *msg i2c_dev->do_master_xfer(i2c_dev); + spin_unlock_irqrestore(&g_master_lock, flags); + ret = wait_for_completion_interruptible_timeout(&i2c_dev->cmd_complete, i2c_dev->adap.timeout*HZ); + spin_lock_irqsave(&g_master_lock, flags); + i2c_dev->master_msgs = NULL; + if (ret == 0) { dev_dbg(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(&g_master_lock, flags); goto stop; } - if(i2c_dev->cmd_err != 0) { + if(i2c_dev->cmd_err != 0 && + i2c_dev->cmd_err != AST_I2CD_INTR_STS_NORMAL_STOP) { ret = -EAGAIN; + spin_unlock_irqrestore(&g_master_lock, flags); goto stop; } - } - if(i2c_dev->cmd_err == 0) { + spin_unlock_irqrestore(&g_master_lock, flags); + + if(i2c_dev->cmd_err == 0 || + i2c_dev->cmd_err == AST_I2CD_INTR_STS_NORMAL_STOP) { ret = num; goto out; } stop: init_completion(&i2c_dev->cmd_complete); + if(i2c_dev->cmd_err & AST_I2CD_INTR_STS_NORMAL_STOP) + goto out; 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); @@ -1610,6 +1665,8 @@ static int ast_i2c_probe(struct platform_device *pdev) 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) { @@ -1617,8 +1674,6 @@ static int ast_i2c_probe(struct platform_device *pdev) goto ereqirq; } - ast_i2c_dev_init(i2c_dev); - #ifdef CONFIG_AST_I2C_SLAVE_RDWR ast_i2c_slave_buff_init(i2c_dev); #endif |