diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-mt65xx.c')
-rw-r--r-- | drivers/i2c/busses/i2c-mt65xx.c | 82 |
1 files changed, 81 insertions, 1 deletions
diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index 7d4b3eb7077a..9ea427f53083 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -15,6 +15,7 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/mm.h> #include <linux/module.h> @@ -49,6 +50,8 @@ #define I2C_RD_TRANAC_VALUE 0x0001 #define I2C_SCL_MIS_COMP_VALUE 0x0000 #define I2C_CHN_CLR_FLAG 0x0000 +#define I2C_RELIABILITY 0x0010 +#define I2C_DMAACK_ENABLE 0x0008 #define I2C_DMA_CON_TX 0x0000 #define I2C_DMA_CON_RX 0x0001 @@ -127,6 +130,7 @@ enum I2C_REGS_OFFSET { OFFSET_HS, OFFSET_SOFTRESET, OFFSET_DCM_EN, + OFFSET_MULTI_DMA, OFFSET_PATH_DIR, OFFSET_DEBUGSTAT, OFFSET_DEBUGCTRL, @@ -194,8 +198,9 @@ static const u16 mt_i2c_regs_v2[] = { [OFFSET_TRANSFER_LEN_AUX] = 0x44, [OFFSET_CLOCK_DIV] = 0x48, [OFFSET_SOFTRESET] = 0x50, + [OFFSET_MULTI_DMA] = 0x8c, [OFFSET_SCL_MIS_COMP_POINT] = 0x90, - [OFFSET_DEBUGSTAT] = 0xe0, + [OFFSET_DEBUGSTAT] = 0xe4, [OFFSET_DEBUGCTRL] = 0xe8, [OFFSET_FIFO_STAT] = 0xf4, [OFFSET_FIFO_THRESH] = 0xf8, @@ -842,6 +847,57 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk) return 0; } +static void i2c_dump_register(struct mtk_i2c *i2c) +{ + dev_dbg(i2c->dev, "SLAVE_ADDR: 0x%x, INTR_MASK: 0x%x\n", + mtk_i2c_readw(i2c, OFFSET_SLAVE_ADDR), + mtk_i2c_readw(i2c, OFFSET_INTR_MASK)); + dev_dbg(i2c->dev, "INTR_STAT: 0x%x, CONTROL: 0x%x\n", + mtk_i2c_readw(i2c, OFFSET_INTR_STAT), + mtk_i2c_readw(i2c, OFFSET_CONTROL)); + dev_dbg(i2c->dev, "TRANSFER_LEN: 0x%x, TRANSAC_LEN: 0x%x\n", + mtk_i2c_readw(i2c, OFFSET_TRANSFER_LEN), + mtk_i2c_readw(i2c, OFFSET_TRANSAC_LEN)); + dev_dbg(i2c->dev, "DELAY_LEN: 0x%x, HTIMING: 0x%x\n", + mtk_i2c_readw(i2c, OFFSET_DELAY_LEN), + mtk_i2c_readw(i2c, OFFSET_TIMING)); + dev_dbg(i2c->dev, "START: 0x%x, EXT_CONF: 0x%x\n", + mtk_i2c_readw(i2c, OFFSET_START), + mtk_i2c_readw(i2c, OFFSET_EXT_CONF)); + dev_dbg(i2c->dev, "HS: 0x%x, IO_CONFIG: 0x%x\n", + mtk_i2c_readw(i2c, OFFSET_HS), + mtk_i2c_readw(i2c, OFFSET_IO_CONFIG)); + dev_dbg(i2c->dev, "DCM_EN: 0x%x, TRANSFER_LEN_AUX: 0x%x\n", + mtk_i2c_readw(i2c, OFFSET_DCM_EN), + mtk_i2c_readw(i2c, OFFSET_TRANSFER_LEN_AUX)); + dev_dbg(i2c->dev, "CLOCK_DIV: 0x%x, FIFO_STAT: 0x%x\n", + mtk_i2c_readw(i2c, OFFSET_CLOCK_DIV), + mtk_i2c_readw(i2c, OFFSET_FIFO_STAT)); + dev_dbg(i2c->dev, "DEBUGCTRL : 0x%x, DEBUGSTAT: 0x%x\n", + mtk_i2c_readw(i2c, OFFSET_DEBUGCTRL), + mtk_i2c_readw(i2c, OFFSET_DEBUGSTAT)); + if (i2c->dev_comp->regs == mt_i2c_regs_v2) { + dev_dbg(i2c->dev, "LTIMING: 0x%x, MULTI_DMA: 0x%x\n", + mtk_i2c_readw(i2c, OFFSET_LTIMING), + mtk_i2c_readw(i2c, OFFSET_MULTI_DMA)); + } + dev_dbg(i2c->dev, "\nDMA_INT_FLAG: 0x%x, DMA_INT_EN: 0x%x\n", + readl(i2c->pdmabase + OFFSET_INT_FLAG), + readl(i2c->pdmabase + OFFSET_INT_EN)); + dev_dbg(i2c->dev, "DMA_EN: 0x%x, DMA_CON: 0x%x\n", + readl(i2c->pdmabase + OFFSET_EN), + readl(i2c->pdmabase + OFFSET_CON)); + dev_dbg(i2c->dev, "DMA_TX_MEM_ADDR: 0x%x, DMA_RX_MEM_ADDR: 0x%x\n", + readl(i2c->pdmabase + OFFSET_TX_MEM_ADDR), + readl(i2c->pdmabase + OFFSET_RX_MEM_ADDR)); + dev_dbg(i2c->dev, "DMA_TX_LEN: 0x%x, DMA_RX_LEN: 0x%x\n", + readl(i2c->pdmabase + OFFSET_TX_LEN), + readl(i2c->pdmabase + OFFSET_RX_LEN)); + dev_dbg(i2c->dev, "DMA_TX_4G_MODE: 0x%x, DMA_RX_4G_MODE: 0x%x", + readl(i2c->pdmabase + OFFSET_TX_4G_MODE), + readl(i2c->pdmabase + OFFSET_RX_4G_MODE)); +} + static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, int num, int left_num) { @@ -851,6 +907,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, u16 restart_flag = 0; u16 dma_sync = 0; u32 reg_4g_mode; + u32 reg_dma_reset; u8 *dma_rd_buf = NULL; u8 *dma_wr_buf = NULL; dma_addr_t rpaddr = 0; @@ -864,6 +921,28 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, reinit_completion(&i2c->msg_complete); + if (i2c->dev_comp->apdma_sync && + i2c->op != I2C_MASTER_WRRD && num > 1) { + mtk_i2c_writew(i2c, 0x00, OFFSET_DEBUGCTRL); + writel(I2C_DMA_HANDSHAKE_RST | I2C_DMA_WARM_RST, + i2c->pdmabase + OFFSET_RST); + + ret = readw_poll_timeout(i2c->pdmabase + OFFSET_RST, + reg_dma_reset, + !(reg_dma_reset & I2C_DMA_WARM_RST), + 0, 100); + if (ret) { + dev_err(i2c->dev, "DMA warm reset timeout\n"); + return -ETIMEDOUT; + } + + writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_RST); + mtk_i2c_writew(i2c, I2C_HANDSHAKE_RST, OFFSET_SOFTRESET); + mtk_i2c_writew(i2c, I2C_CHN_CLR_FLAG, OFFSET_SOFTRESET); + mtk_i2c_writew(i2c, I2C_RELIABILITY | I2C_DMAACK_ENABLE, + OFFSET_DEBUGCTRL); + } + control_reg = mtk_i2c_readw(i2c, OFFSET_CONTROL) & ~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS); if ((i2c->speed_hz > I2C_MAX_FAST_MODE_PLUS_FREQ) || (left_num >= 1)) @@ -1049,6 +1128,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, if (ret == 0) { dev_dbg(i2c->dev, "addr: %x, transfer timeout\n", msgs->addr); + i2c_dump_register(i2c); mtk_i2c_init_hw(i2c); return -ETIMEDOUT; } |