summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrank Li <Frank.Li@freescale.com>2010-09-01 14:08:05 +0800
committerFrank Li <Frank.Li@freescale.com>2010-09-01 14:40:54 +0800
commit2656f9a25b97f25f8154cc2a1db4511eb0f92bf1 (patch)
treedee8011579c08520357a6d3ddf1a642435857fef
parent23b45bea4da35a5648b08ffc134ef78a09340712 (diff)
ENGR00126961 I2C: Fix i2c will be fail after miss ack
I2C transfer will be always fail if miss ack happen. i2cdump will be fail after i2cdetect. Reset i2c module when MISS ACK irq issue. Signed-off-by: Frank Li <Frank.Li@freescale.com>
-rw-r--r--drivers/i2c/busses/i2c-mxs.c45
-rw-r--r--drivers/i2c/busses/i2c-mxs.h1
2 files changed, 30 insertions, 16 deletions
diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c
index e3235ff8e551..8bb795e7d7b2 100644
--- a/drivers/i2c/busses/i2c-mxs.c
+++ b/drivers/i2c/busses/i2c-mxs.c
@@ -68,10 +68,19 @@ static u8 *i2c_buf_virt;
static void hw_i2c_dmachan_reset(struct mxs_i2c_dev *dev)
{
+ mxs_dma_disable(dev->dma_chan);
mxs_dma_reset(dev->dma_chan);
mxs_dma_ack_irq(dev->dma_chan);
}
+static mxs_i2c_reset(struct mxs_i2c_dev *mxs_i2c)
+{
+ hw_i2c_dmachan_reset(mxs_i2c);
+ mxs_dma_enable_irq(mxs_i2c->dma_chan, 1);
+ mxs_reset_block((void __iomem *)mxs_i2c->regbase, 0);
+ __raw_writel(0x0000FF00, mxs_i2c->regbase + HW_I2C_CTRL1_SET);
+}
+
static int hw_i2c_dma_init(struct platform_device *pdev)
{
struct mxs_i2c_dev *mxs_i2c = platform_get_drvdata(pdev);
@@ -159,7 +168,7 @@ static void hw_i2c_dma_setup_read(u8 addr, void *buff, int len, int flags)
desc[0]->cmd.cmd.bits.pio_words = 1;
desc[0]->cmd.cmd.bits.wait4end = 1;
desc[0]->cmd.cmd.bits.dec_sem = 1;
- desc[0]->cmd.cmd.bits.irq = 1;
+ desc[0]->cmd.cmd.bits.irq = 0;
desc[0]->cmd.cmd.bits.chain = 1;
desc[0]->cmd.cmd.bits.command = DMA_READ;
desc[0]->cmd.address = i2c_buf_phys;
@@ -319,6 +328,7 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap,
msecs_to_jiffies(1000)
);
if (err <= 0) {
+ mxs_i2c_reset(dev);
dev_dbg(dev->dev, "controller is timed out\n");
return -ETIMEDOUT;
}
@@ -365,9 +375,19 @@ static irqreturn_t mxs_i2c_dma_isr(int this_irq, void *dev_id)
mxs_dma_ack_irq(mxs_i2c->dma_chan);
mxs_dma_cooked(mxs_i2c->dma_chan, &list);
+ complete(&mxs_i2c->cmd_complete);
+
return IRQ_HANDLED;
}
+static void mxs_i2c_task(struct work_struct *work)
+{
+ struct mxs_i2c_dev *mxs_i2c = container_of(work,
+ struct mxs_i2c_dev, work);
+ mxs_i2c_reset(mxs_i2c);
+ complete(&mxs_i2c->cmd_complete);
+}
+
#define I2C_IRQ_MASK 0x000000FF
static irqreturn_t mxs_i2c_isr(int this_irq, void *dev_id)
{
@@ -382,20 +402,8 @@ static irqreturn_t mxs_i2c_isr(int this_irq, void *dev_id)
if (stat & BM_I2C_CTRL1_NO_SLAVE_ACK_IRQ) {
mxs_i2c->cmd_err = -EREMOTEIO;
-
- /*
- * Stop DMA
- * Clear NAK
- */
- __raw_writel(BM_I2C_CTRL1_CLR_GOT_A_NAK,
- mxs_i2c->regbase + HW_I2C_CTRL1_SET);
- hw_i2c_dmachan_reset(mxs_i2c);
- mxs_reset_block((void __iomem *)mxs_i2c->regbase, 1);
- /* Will catch all error (IRQ mask) */
- __raw_writel(0x0000FF00, mxs_i2c->regbase + HW_I2C_CTRL1_SET);
-
- complete(&mxs_i2c->cmd_complete);
-
+ /* it takes long time to reset i2c */
+ schedule_work(&mxs_i2c->work);
goto done;
}
@@ -407,7 +415,10 @@ static irqreturn_t mxs_i2c_isr(int this_irq, void *dev_id)
complete(&mxs_i2c->cmd_complete);
goto done;
}
- if ((stat & done_mask) == done_mask)
+
+ if ((stat & done_mask) == done_mask &&
+ (mxs_i2c->flags & MXS_I2C_PIOQUEUE_MODE))
+
complete(&mxs_i2c->cmd_complete);
done:
@@ -524,6 +535,8 @@ static int mxs_i2c_probe(struct platform_device *pdev)
}
+ INIT_WORK(&mxs_i2c->work, mxs_i2c_task);
+
return 0;
no_i2c_adapter:
diff --git a/drivers/i2c/busses/i2c-mxs.h b/drivers/i2c/busses/i2c-mxs.h
index 4ddca007624a..1a35385b793b 100644
--- a/drivers/i2c/busses/i2c-mxs.h
+++ b/drivers/i2c/busses/i2c-mxs.h
@@ -37,5 +37,6 @@ struct mxs_i2c_dev {
struct i2c_adapter adapter;
spinlock_t lock;
wait_queue_head_t queue;
+ struct work_struct work;
};
#endif