summaryrefslogtreecommitdiff
path: root/drivers/i2c/busses/i2c-ast.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/busses/i2c-ast.c')
-rw-r--r--drivers/i2c/busses/i2c-ast.c115
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