diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-tegra.c')
-rw-r--r-- | drivers/i2c/busses/i2c-tegra.c | 79 |
1 files changed, 67 insertions, 12 deletions
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 0a6b21f8f077..d50c5e5db862 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -19,6 +19,7 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/err.h> #include <linux/i2c.h> #include <linux/io.h> #include <linux/interrupt.h> @@ -44,8 +45,8 @@ #define I2C_SL_CNFG 0x020 #define I2C_SL_CNFG_NACK (1<<1) #define I2C_SL_CNFG_NEWSL (1<<2) -#define I2C_SL_ADDR1 0x02c -#define I2C_SL_ADDR2 0x030 +#define I2C_SL_ADDR1 0x02c +#define I2C_SL_ADDR2 0x030 #define I2C_TX_FIFO 0x050 #define I2C_RX_FIFO 0x054 #define I2C_PACKET_TRANSFER_STATUS 0x058 @@ -136,6 +137,7 @@ struct tegra_i2c_dev { int msg_read; int msg_transfer_complete; struct i2c_msg *msgs; + int msg_add; int msgs_num; bool is_suspended; int bus_count; @@ -156,6 +158,20 @@ static u32 dvc_readl(struct tegra_i2c_dev *i2c_dev, unsigned long reg) return readl(i2c_dev->base + reg); } +static void dvc_i2c_mask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask) +{ + u32 int_mask = dvc_readl(i2c_dev, DVC_CTRL_REG3); + int_mask &= ~mask; + dvc_writel(i2c_dev, int_mask, DVC_CTRL_REG3); +} + +static void dvc_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask) +{ + u32 int_mask = dvc_readl(i2c_dev, DVC_CTRL_REG3); + int_mask |= mask; + dvc_writel(i2c_dev, int_mask, DVC_CTRL_REG3); +} + /* i2c_writel and i2c_readl will offset the register if necessary to talk * to the I2C block inside the DVC block */ @@ -272,10 +288,15 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev) for (word = 0; word < words_to_transfer; word++) { val = get_unaligned_le32(buf); - i2c_writel(i2c_dev, val, I2C_TX_FIFO); + + /* Update the field before writing into Tx Fifo */ buf += BYTES_PER_FIFO_WORD; buf_remaining -= BYTES_PER_FIFO_WORD; tx_fifo_avail--; + i2c_dev->msg_buf_remaining = buf_remaining; + i2c_dev->msg_buf = buf; + + i2c_writel(i2c_dev, val, I2C_TX_FIFO); } if (tx_fifo_avail > 0 && buf_remaining > 0) { @@ -285,9 +306,14 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev) val = 0; for (byte = 0; byte < bytes_to_transfer; byte++) val |= (*buf++) << (byte * 8); - i2c_writel(i2c_dev, val, I2C_TX_FIFO); + + /* Update the field before writing into Tx Fifo */ buf_remaining -= bytes_to_transfer; tx_fifo_avail--; + i2c_dev->msg_buf_remaining = buf_remaining; + i2c_dev->msg_buf = buf; + + i2c_writel(i2c_dev, val, I2C_TX_FIFO); } BUG_ON(tx_fifo_avail > 0 && buf_remaining > 0); i2c_dev->msg_buf_remaining = buf_remaining; @@ -306,7 +332,6 @@ static void tegra_dvc_init(struct tegra_i2c_dev *i2c_dev) u32 val = 0; val = dvc_readl(i2c_dev, DVC_CTRL_REG3); val |= DVC_CTRL_REG3_SW_PROG; - val |= DVC_CTRL_REG3_I2C_DONE_INTR_EN; dvc_writel(i2c_dev, val, DVC_CTRL_REG3); val = dvc_readl(i2c_dev, DVC_CTRL_REG1); @@ -335,6 +360,10 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) clk_enable(i2c_dev->clk); + /* Interrupt generated before sending stop signal so + * wait for some time so that stop signal can be send proerly */ + mdelay(1); + tegra_periph_reset_assert(i2c_dev->clk); udelay(2); tegra_periph_reset_deassert(i2c_dev->clk); @@ -370,13 +399,14 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) { u32 status; - const u32 status_err = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST; + const u32 status_err = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST | I2C_INT_TX_FIFO_OVERFLOW; struct tegra_i2c_dev *i2c_dev = dev_id; status = i2c_readl(i2c_dev, I2C_INT_STATUS); if (status == 0) { - dev_warn(i2c_dev->dev, "unknown interrupt\n"); + dev_err(i2c_dev->dev, "unknown interrupt Add 0x%02x\n", + i2c_dev->msg_add); i2c_dev->msg_err |= I2C_ERR_UNKNOWN_INTERRUPT; if (!i2c_dev->irq_disabled) { @@ -389,16 +419,30 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) } if (unlikely(status & status_err)) { + dev_err(i2c_dev->dev, "I2c error status 0x%08x\n", status); if (status & I2C_INT_NO_ACK) { i2c_dev->msg_err |= I2C_ERR_NO_ACK; - dev_warn(i2c_dev->dev, "no acknowledge\n"); + dev_err(i2c_dev->dev, "no acknowledge from address" + " 0x%x\n", i2c_dev->msg_add); + dev_err(i2c_dev->dev, "Packet status 0x%08x\n", + i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS)); } if (status & I2C_INT_ARBITRATION_LOST) { i2c_dev->msg_err |= I2C_ERR_ARBITRATION_LOST; - dev_warn(i2c_dev->dev, "arbitration lost\n"); + dev_err(i2c_dev->dev, "arbitration lost during " + " communicate to add 0x%x\n", i2c_dev->msg_add); + dev_err(i2c_dev->dev, "Packet status 0x%08x\n", + i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS)); } + if (status & I2C_INT_TX_FIFO_OVERFLOW) { + i2c_dev->msg_err |= I2C_INT_TX_FIFO_OVERFLOW; + dev_warn(i2c_dev->dev, "Tx fifo overflow during " + " communicate to add 0x%x\n", i2c_dev->msg_add); + dev_warn(i2c_dev->dev, "Packet status 0x%08x\n", + i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS)); + } complete(&i2c_dev->msg_complete); goto err; } @@ -407,7 +451,7 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) && (status == I2C_INT_TX_FIFO_DATA_REQ) && i2c_dev->msg_read && i2c_dev->msg_buf_remaining)) { - dev_warn(i2c_dev->dev, "unexpected status\n"); + dev_err(i2c_dev->dev, "unexpected status\n"); i2c_dev->msg_err |= I2C_ERR_UNEXPECTED_STATUS; if (!i2c_dev->irq_disabled) { @@ -470,10 +514,14 @@ err: /* An error occured, mask all interrupts */ tegra_i2c_mask_irq(i2c_dev, I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST | I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ | - I2C_INT_RX_FIFO_DATA_REQ); + I2C_INT_RX_FIFO_DATA_REQ | I2C_INT_TX_FIFO_OVERFLOW); i2c_writel(i2c_dev, status, I2C_INT_STATUS); + /* An error occured, mask dvc interrupt */ + if (i2c_dev->is_dvc) + dvc_i2c_mask_irq(i2c_dev, DVC_CTRL_REG3_I2C_DONE_INTR_EN); + if (i2c_dev->is_dvc) dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); @@ -499,6 +547,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_bus *i2c_bus, i2c_dev->msg_transfer_complete = 0; i2c_dev->msg_read = (msg->flags & I2C_M_RD); INIT_COMPLETION(i2c_dev->msg_complete); + i2c_dev->msg_add = msg->addr; i2c_dev->packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) | PACKET_HEADER0_PROTOCOL_I2C | @@ -524,7 +573,10 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_bus *i2c_bus, if (!(msg->flags & I2C_M_RD)) tegra_i2c_fill_tx_fifo(i2c_dev); - int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST; + if (i2c_dev->is_dvc) + dvc_i2c_unmask_irq(i2c_dev, DVC_CTRL_REG3_I2C_DONE_INTR_EN); + + int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST | I2C_INT_TX_FIFO_OVERFLOW; if (msg->flags & I2C_M_RD) int_mask |= I2C_INT_RX_FIFO_DATA_REQ; else if (i2c_dev->msg_buf_remaining) @@ -536,6 +588,9 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_bus *i2c_bus, TEGRA_I2C_TIMEOUT); tegra_i2c_mask_irq(i2c_dev, int_mask); + if (i2c_dev->is_dvc) + dvc_i2c_mask_irq(i2c_dev, DVC_CTRL_REG3_I2C_DONE_INTR_EN); + if (WARN_ON(ret == 0)) { dev_err(i2c_dev->dev, "i2c transfer timed out, addr 0x%04x, data 0x%02x\n", |