All of lore.kernel.org
 help / color / mirror / Atom feed
From: mingming lee <mingming.lee@mediatek.com>
To: u-boot@lists.denx.de
Subject: [PATCH 1/3] i2c: mediatek: add basic driver support
Date: Wed, 9 Sep 2020 14:07:26 +0800	[thread overview]
Message-ID: <20200909060728.5616-2-mingming.lee@mediatek.com> (raw)
In-Reply-To: <20200909060728.5616-1-mingming.lee@mediatek.com>

From: Mingming Lee <Mingming.Lee@mediatek.com>

Add MediaTek I2C basic driver

Signed-off-by: Mingming Lee <Mingming.Lee@mediatek.com>
---
 drivers/i2c/Kconfig  |   9 +
 drivers/i2c/Makefile |   1 +
 drivers/i2c/mt_i2c.c | 619 +++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 629 insertions(+)
 create mode 100644 drivers/i2c/mt_i2c.c

diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index dec6dc9..103688e 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -159,6 +159,15 @@ config SYS_I2C_MESON
 	  internal buffer holding up to 8 bytes for transfers and supports
 	  both 7-bit and 10-bit addresses.
 
+config SYS_I2C_MTK
+	bool "MediaTek I2C driver"
+	default n
+	help
+	  This selects the MediaTek Integrated Inter Circuit bus driver.
+	  The I2C bus adapter is the base for some other I2C client, eg: touch, sensors.
+	  If you want to use MediaTek I2C interface, say Y here.
+	  If unsure, say N.
+
 config SYS_I2C_MXC
 	bool "NXP MXC I2C driver"
 	help
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index e851ec4..7227742 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o
 obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o
 obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o
 obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o
+obj-$(CONFIG_SYS_I2C_MTK) += mt_i2c.o
 obj-$(CONFIG_SYS_I2C_NEXELL) += nx_i2c.o
 obj-$(CONFIG_SYS_I2C_OCTEON) += octeon_i2c.o
 obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o
diff --git a/drivers/i2c/mt_i2c.c b/drivers/i2c/mt_i2c.c
new file mode 100644
index 0000000..5badde9
--- /dev/null
+++ b/drivers/i2c/mt_i2c.c
@@ -0,0 +1,619 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek I2C Interface driver
+ *
+ * Copyright (C) 2020 MediaTek Inc.
+ * Author: Mingming Lee <Mingming.Lee@mediatek.com>
+ */
+
+#include <asm/cache.h>
+#include <asm/io.h>
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <i2c.h>
+#include <linux/delay.h>
+
+#define I2C_OK			0
+#define EINVAL_I2C		22  /* invalid argument */
+#define ETIMEDOUT_I2C		110 /* connection timed out */
+#define EACK_I2C		6   /* remote I/O error */
+
+#define I2C_RS_TRANSFER		BIT(4)
+#define I2C_HS_NACKERR		BIT(2)
+#define I2C_ACKERR		BIT(1)
+#define I2C_TRANSAC_COMP	BIT(0)
+#define I2C_TRANSAC_START	BIT(0)
+#define I2C_RS_MUL_CNFG		BIT(15)
+#define I2C_RS_MUL_TRIG		BIT(14)
+#define I2C_DCM_DISABLE			0x0000
+#define I2C_IO_CONFIG_OPEN_DRAIN	0x0003
+#define I2C_IO_CONFIG_PUSH_PULL		0x0000
+#define I2C_SOFT_RST			0x0001
+#define I2C_FIFO_ADDR_CLR		0x0001
+#define I2C_DELAY_LEN			0x0002
+#define I2C_ST_START_CON		0x8001
+#define I2C_FS_START_CON		0x1800
+#define I2C_TIME_CLR_VALUE		0x0000
+#define I2C_TIME_DEFAULT_VALUE		0x0003
+#define I2C_WRRD_TRANAC_VALUE		0x0002
+#define I2C_RD_TRANAC_VALUE		0x0001
+
+#define I2C_DMA_CON_TX			0x0000
+#define I2C_DMA_CON_RX			0x0001
+#define I2C_DMA_START_EN		0x0001
+#define I2C_DMA_INT_FLAG_NONE		0x0000
+#define I2C_DMA_CLR_FLAG		0x0000
+#define I2C_DMA_HARD_RST		0x0002
+
+#define MAX_ST_MODE_SPEED		100000
+#define MAX_FS_MODE_SPEED		400000
+#define MAX_HS_MODE_SPEED		3400000
+#define MAX_SAMPLE_CNT_DIV		8
+#define MAX_STEP_CNT_DIV		64
+#define MAX_HS_STEP_CNT_DIV		8
+#define I2C_DEFAULT_CLK_DIV		4
+
+#define MAX_I2C_ADDR		0x7F
+#define MAX_I2C_LEN		0xFF
+#define TRANS_ADDR_ONLY		BIT(8)
+#define TRANSFER_TIMEOUT		50000  /* us */
+#define I2C_FIFO_STAT1_MASK		0x001F
+#define TIMING_SAMPLE_OFFSET	8
+#define HS_SAMPLE_OFFSET	12
+#define HS_STEP_OFFSET		8
+
+#define I2C_CONTROL_WRAPPER		BIT(0)
+#define I2C_CONTROL_RS			BIT(1)
+#define I2C_CONTROL_DMA_EN		BIT(2)
+#define I2C_CONTROL_CLK_EXT_EN		BIT(3)
+#define I2C_CONTROL_DIR_CHANGE		BIT(4)
+#define I2C_CONTROL_ACKERR_DET_EN	BIT(5)
+#define I2C_CONTROL_TRANSFER_LEN_CHANGE BIT(6)
+#define I2C_CONTROL_DMAACK		BIT(8)
+#define I2C_CONTROL_ASYNC		BIT(9)
+
+enum I2C_REGS_OFFSET {
+	OFFSET_PORT = 0x0,
+	OFFSET_SLAVE_ADDR = 0x04,
+	OFFSET_INTR_MASK = 0x08,
+	OFFSET_INTR_STAT = 0x0C,
+	OFFSET_CONTROL = 0x10,
+	OFFSET_TRANSFER_LEN = 0x14,
+	OFFSET_TRANSAC_LEN  = 0x18,
+	OFFSET_DELAY_LEN = 0x1C,
+	OFFSET_TIMING = 0x20,
+	OFFSET_START = 0x24,
+	OFFSET_EXT_CONF = 0x28,
+	OFFSET_FIFO_STAT1 = 0x2C,
+	OFFSET_FIFO_STAT = 0x30,
+	OFFSET_FIFO_THRESH = 0x34,
+	OFFSET_FIFO_ADDR_CLR = 0x38,
+	OFFSET_IO_CONFIG = 0x40,
+	OFFSET_RSV_DEBUG = 0x44,
+	OFFSET_HS = 0x48,
+	OFFSET_SOFTRESET = 0x50,
+	OFFSET_DCM_EN = 0x54,
+	OFFSET_DEBUGSTAT = 0x64,
+	OFFSET_DEBUGCTRL = 0x68,
+	OFFSET_TRANSFER_LEN_AUX = 0x6C,
+	OFFSET_CLOCK_DIV = 0x70,
+	OFFSET_SCL_HL_RATIO = 0x74,
+	OFFSET_SCL_HS_HL_RATIO = 0x78,
+	OFFSET_SCL_MIS_COMP_POINT = 0x7C,
+	OFFSET_STA_STOP_AC_TIME = 0x80,
+	OFFSET_HS_STA_STOP_AC_TIME = 0x84,
+	OFFSET_DATA_TIME = 0x88,
+};
+
+enum DMA_REGS_OFFSET {
+	OFFSET_INT_FLAG = 0x0,
+	OFFSET_INT_EN = 0x04,
+	OFFSET_EN = 0x08,
+	OFFSET_RST = 0x0C,
+	OFFSET_CON = 0x18,
+	OFFSET_TX_MEM_ADDR = 0x1C,
+	OFFSET_RX_MEM_ADDR = 0x20,
+	OFFSET_TX_LEN = 0x24,
+	OFFSET_RX_LEN = 0x28,
+};
+
+enum mtk_trans_op {
+	I2C_MASTER_WR = 1,
+	I2C_MASTER_RD,
+	I2C_MASTER_WRRD,
+};
+
+struct mtk_i2c_soc_data {
+	u8 dma_sync: 1;
+};
+
+struct mtk_i2c_priv {
+	/* set in i2c probe */
+	void __iomem *base;			/* i2c base addr */
+	void __iomem *pdmabase;		/* dma base address*/
+	struct clk clk_main;		/* main clock for i2c bus */
+	struct clk clk_dma;		    /* DMA clock for i2c via DMA */
+	const struct mtk_i2c_soc_data *soc_data;
+	enum mtk_trans_op op;
+	bool   zero_len;
+	bool   pushpull;            /* open drain */
+	bool   filter_msg;          /* filter msg error log */
+	bool   auto_restart;
+	bool ignore_restart_irq;
+	u32 speed;                 /* hz */
+	u8 *tx_buff;
+	u8 *rx_buff;
+};
+
+static inline void i2c_writel(struct mtk_i2c_priv *priv, u8 offset, u32 value)
+{
+	writel(value, (priv->base + offset));
+}
+
+static inline u32 i2c_readl(struct mtk_i2c_priv *priv, u8 offset)
+{
+	return readl(priv->base + offset);
+}
+
+static void mtk_i2c_clk_enable(struct mtk_i2c_priv *priv)
+{
+	clk_enable(&priv->clk_main);
+	clk_enable(&priv->clk_dma);
+}
+
+static void mtk_i2c_clk_disable(struct mtk_i2c_priv *priv)
+{
+	clk_disable(&priv->clk_dma);
+	clk_disable(&priv->clk_main);
+}
+
+static void mtk_i2c_init_hw(struct mtk_i2c_priv *priv)
+{
+	u16 control_reg;
+
+	writel(I2C_DMA_HARD_RST, priv->pdmabase + OFFSET_RST);
+	writel(I2C_DMA_CLR_FLAG, priv->pdmabase + OFFSET_RST);
+	i2c_writel(priv, OFFSET_SOFTRESET, I2C_SOFT_RST);
+	/* set ioconfig */
+	if (priv->pushpull)
+		i2c_writel(priv, OFFSET_IO_CONFIG, I2C_IO_CONFIG_PUSH_PULL);
+	else
+		i2c_writel(priv, OFFSET_IO_CONFIG, I2C_IO_CONFIG_OPEN_DRAIN);
+
+	i2c_writel(priv, OFFSET_DCM_EN, I2C_DCM_DISABLE);
+	control_reg = I2C_CONTROL_ACKERR_DET_EN | I2C_CONTROL_CLK_EXT_EN;
+	if (priv->soc_data->dma_sync)
+		control_reg |= I2C_CONTROL_DMAACK | I2C_CONTROL_ASYNC;
+	i2c_writel(priv, OFFSET_CONTROL, control_reg);
+	i2c_writel(priv, OFFSET_DELAY_LEN, I2C_DELAY_LEN);
+}
+
+static int mtk_i2c_calculate_speed(struct mtk_i2c_priv *priv,
+				   u32 clk_src,
+				   u32 target_speed,
+				   u32 *timing_step_cnt,
+				   u32 *timing_sample_cnt)
+{
+	u32 step_cnt;
+	u32 sample_cnt;
+	u32 max_step_cnt;
+	u32 base_sample_cnt = MAX_SAMPLE_CNT_DIV;
+	u32 base_step_cnt;
+	u32 opt_div;
+	u32 best_mul;
+	u32 cnt_mul;
+
+	if (target_speed > MAX_HS_MODE_SPEED)
+		target_speed = MAX_HS_MODE_SPEED;
+
+	if (target_speed > MAX_FS_MODE_SPEED)
+		max_step_cnt = MAX_HS_STEP_CNT_DIV;
+	else
+		max_step_cnt = MAX_STEP_CNT_DIV;
+
+	base_step_cnt = max_step_cnt;
+	/* Find the best combination */
+	opt_div = DIV_ROUND_UP(clk_src >> 1, target_speed);
+	best_mul = MAX_SAMPLE_CNT_DIV * max_step_cnt;
+
+	/* Search for the best pair (sample_cnt, step_cnt) with
+	 * 0 < sample_cnt < MAX_SAMPLE_CNT_DIV
+	 * 0 < step_cnt < max_step_cnt
+	 * sample_cnt * step_cnt >= opt_div
+	 * optimizing for sample_cnt * step_cnt being minimal
+	 */
+	for (sample_cnt = 1; sample_cnt <= MAX_SAMPLE_CNT_DIV; sample_cnt++) {
+		step_cnt = DIV_ROUND_UP(opt_div, sample_cnt);
+		cnt_mul = step_cnt * sample_cnt;
+		if (step_cnt > max_step_cnt)
+			continue;
+
+		if (cnt_mul < best_mul) {
+			best_mul = cnt_mul;
+			base_sample_cnt = sample_cnt;
+			base_step_cnt = step_cnt;
+			if (best_mul == opt_div)
+				break;
+		}
+	}
+
+	sample_cnt = base_sample_cnt;
+	step_cnt = base_step_cnt;
+
+	if ((clk_src / (2 * sample_cnt * step_cnt)) > target_speed) {
+		/* In this case, hardware can't support such
+		 * low i2c_bus_freq
+		 */
+		debug("Unsupported speed(%uhz)\n", target_speed);
+		return -EINVAL;
+	}
+
+	*timing_step_cnt = step_cnt - 1;
+	*timing_sample_cnt = sample_cnt - 1;
+	return 0;
+}
+
+static int mtk_i2c_set_speed(struct udevice *dev, uint speed)
+{
+	u32 clk_src;
+	u32 step_cnt;
+	u32 sample_cnt;
+	u16 timing_reg;
+	u16 high_speed_reg;
+	int ret = 0;
+	struct mtk_i2c_priv *priv = dev_get_priv(dev);
+
+	priv->speed = speed;
+	mtk_i2c_clk_enable(priv);
+	clk_src = clk_get_rate(&priv->clk_main) /
+			I2C_DEFAULT_CLK_DIV;
+	i2c_writel(priv, OFFSET_CLOCK_DIV, (I2C_DEFAULT_CLK_DIV - 1));
+	if (priv->speed > MAX_FS_MODE_SPEED) {
+		/* Set master code speed register */
+		ret = mtk_i2c_calculate_speed(priv, clk_src, MAX_FS_MODE_SPEED,
+					      &step_cnt, &sample_cnt);
+		if (ret < 0)
+			goto exit;
+
+		timing_reg = (sample_cnt << TIMING_SAMPLE_OFFSET) | step_cnt;
+		writel(timing_reg, priv->base + OFFSET_TIMING);
+		/* Set the high speed mode register */
+		ret = mtk_i2c_calculate_speed(priv, clk_src, priv->speed,
+					      &step_cnt, &sample_cnt);
+		if (ret < 0)
+			goto exit;
+
+		high_speed_reg = I2C_TIME_DEFAULT_VALUE |
+				(sample_cnt << HS_SAMPLE_OFFSET) |
+				(step_cnt << HS_STEP_OFFSET);
+		writel(high_speed_reg, priv->base + OFFSET_HS);
+	} else {
+		ret = mtk_i2c_calculate_speed(priv, clk_src, priv->speed,
+					      &step_cnt, &sample_cnt);
+		if (ret < 0)
+			goto exit;
+
+		timing_reg = (sample_cnt << TIMING_SAMPLE_OFFSET) | step_cnt;
+		/* Disable the high speed transaction */
+		high_speed_reg = I2C_TIME_CLR_VALUE;
+		writel(timing_reg, priv->base + OFFSET_TIMING);
+		writel(high_speed_reg, priv->base + OFFSET_HS);
+	}
+exit:
+	mtk_i2c_clk_disable(priv);
+	return ret;
+}
+
+static int mtk_i2c_do_transfer(struct mtk_i2c_priv *priv,
+			       struct i2c_msg *msgs,
+			       int num, int left_num)
+{
+	u16 addr_reg;
+	u16 start_reg;
+	u16 control_reg;
+	u16 restart_flag = 0;
+	u16	irq_stat = 0;
+	u8 trans_error = 0;
+	bool tmo = false;
+	u8 *ptr = msgs->buf;
+	u16 data_size = msgs->len;
+	u32 tmo_poll = 0;
+	int ret;
+
+	if (priv->auto_restart)
+		restart_flag = I2C_RS_TRANSFER;
+
+	control_reg = i2c_readl(priv, OFFSET_CONTROL) &
+		~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS);
+
+	if (priv->speed > MAX_FS_MODE_SPEED || num > 1)
+		control_reg |= I2C_CONTROL_RS;
+
+	if (priv->op == I2C_MASTER_WRRD)
+		control_reg |= I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS;
+	control_reg |= I2C_CONTROL_DMA_EN;
+	i2c_writel(priv, OFFSET_CONTROL, control_reg);
+
+	/* set start condition */
+	if (priv->speed <= MAX_ST_MODE_SPEED)
+		i2c_writel(priv, OFFSET_EXT_CONF, I2C_ST_START_CON);
+	else
+		i2c_writel(priv, OFFSET_EXT_CONF, I2C_FS_START_CON);
+
+	addr_reg = msgs->addr << 1;
+	if (priv->op == I2C_MASTER_RD)
+		addr_reg |= I2C_M_RD;
+	if (priv->zero_len)
+		i2c_writel(priv, OFFSET_SLAVE_ADDR, addr_reg | TRANS_ADDR_ONLY);
+	else
+		i2c_writel(priv, OFFSET_SLAVE_ADDR, addr_reg);
+
+	/* clear interrupt status */
+	i2c_writel(priv, OFFSET_INTR_STAT, restart_flag | I2C_HS_NACKERR |
+						I2C_ACKERR | I2C_TRANSAC_COMP);
+		i2c_writel(priv, OFFSET_FIFO_ADDR_CLR, I2C_FIFO_ADDR_CLR);
+
+	/* enable interrupt */
+	i2c_writel(priv, OFFSET_INTR_MASK, restart_flag | I2C_HS_NACKERR |
+						I2C_ACKERR | I2C_TRANSAC_COMP);
+
+	/* set transfer and transaction len */
+	if (priv->op == I2C_MASTER_WRRD) {
+		i2c_writel(priv, OFFSET_TRANSFER_LEN, msgs->len);
+		i2c_writel(priv, OFFSET_TRANSFER_LEN_AUX,
+			   (msgs + 1)->len);
+		i2c_writel(priv, OFFSET_TRANSAC_LEN, I2C_WRRD_TRANAC_VALUE);
+	} else {
+		i2c_writel(priv, OFFSET_TRANSFER_LEN, msgs->len);
+		i2c_writel(priv, OFFSET_TRANSAC_LEN, num);
+	}
+
+	/* prepare buffer data to start transfer */
+	if (priv->op == I2C_MASTER_RD) {
+		writel(I2C_DMA_INT_FLAG_NONE, priv->pdmabase +
+		OFFSET_INT_FLAG);
+		writel(I2C_DMA_CON_RX, priv->pdmabase + OFFSET_CON);
+		priv->rx_buff = (u8 *)noncached_alloc(msgs->len,
+				ARCH_DMA_MINALIGN);
+		writel((u32)priv->rx_buff, priv->pdmabase +
+		OFFSET_RX_MEM_ADDR);
+		writel(msgs->len, priv->pdmabase + OFFSET_RX_LEN);
+	} else if (priv->op == I2C_MASTER_WR) {
+		writel(I2C_DMA_INT_FLAG_NONE, priv->pdmabase +
+			  OFFSET_INT_FLAG);
+		writel(I2C_DMA_CON_TX, priv->pdmabase + OFFSET_CON);
+		priv->tx_buff = (u8 *)noncached_alloc(msgs->len,
+				ARCH_DMA_MINALIGN);
+		memcpy(priv->tx_buff, msgs->buf, msgs->len);
+		writel((u32)priv->tx_buff, priv->pdmabase +
+			  OFFSET_TX_MEM_ADDR);
+		writel(msgs->len, priv->pdmabase + OFFSET_TX_LEN);
+	} else {
+		writel(I2C_DMA_CLR_FLAG, priv->pdmabase +
+			OFFSET_INT_FLAG);
+		writel(I2C_DMA_CLR_FLAG, priv->pdmabase + OFFSET_CON);
+		priv->tx_buff = (u8 *)noncached_alloc(msgs->len,
+				ARCH_DMA_MINALIGN);
+		priv->rx_buff = (u8 *)noncached_alloc((msgs + 1)->len,
+				ARCH_DMA_MINALIGN);
+		memcpy(priv->tx_buff, msgs->buf, msgs->len);
+		writel((u32)priv->tx_buff, priv->pdmabase +
+			  OFFSET_TX_MEM_ADDR);
+		writel((u32)priv->rx_buff, priv->pdmabase +
+			  OFFSET_RX_MEM_ADDR);
+		writel(msgs->len, priv->pdmabase + OFFSET_TX_LEN);
+		writel((msgs + 1)->len, priv->pdmabase +
+			  OFFSET_RX_LEN);
+	}
+	writel(I2C_DMA_START_EN, priv->pdmabase + OFFSET_EN);
+
+	if (!priv->auto_restart) {
+		start_reg = I2C_TRANSAC_START;
+	} else {
+		start_reg = I2C_TRANSAC_START | I2C_RS_MUL_TRIG;
+		if (left_num >= 1)
+			start_reg |= I2C_RS_MUL_CNFG;
+	}
+	i2c_writel(priv, OFFSET_START, start_reg);
+
+	for (;;) {
+		irq_stat = i2c_readl(priv, OFFSET_INTR_STAT);
+
+		/* ignore the first restart irq after the master code */
+		if (priv->ignore_restart_irq && (irq_stat | restart_flag)) {
+			priv->ignore_restart_irq = false;
+			irq_stat = 0;
+			writel(I2C_RS_MUL_CNFG | I2C_RS_MUL_TRIG |
+			       I2C_TRANSAC_START, priv->base + OFFSET_START);
+		}
+
+		if (irq_stat & (I2C_TRANSAC_COMP | restart_flag)) {
+			tmo = false;
+			if (irq_stat & (I2C_HS_NACKERR | I2C_ACKERR))
+				trans_error = 1;
+
+			break;
+		}
+		udelay(1);
+		if (tmo_poll++ >= TRANSFER_TIMEOUT) {
+			tmo = true;
+			break;
+		}
+	}
+	/* clear interrupt mask */
+	i2c_writel(priv, OFFSET_INTR_MASK, ~(restart_flag | I2C_HS_NACKERR |
+		  I2C_ACKERR | I2C_TRANSAC_COMP));
+
+	if (!tmo && trans_error == 0) {
+		/* transfer success ,we need to get data from fifo */
+		if (priv->op == I2C_MASTER_RD || priv->op == I2C_MASTER_WRRD) {
+			ptr = priv->op == I2C_MASTER_RD ?
+					msgs->buf : (msgs + 1)->buf;
+			data_size = priv->op == I2C_MASTER_RD ?
+					msgs->len : (msgs + 1)->len;
+			memcpy(ptr, priv->rx_buff, data_size);
+		}
+	} else {
+		/* timeout or ACKERR */
+		if (tmo) {
+			ret = -ETIMEDOUT_I2C;
+			if (!priv->filter_msg)
+				debug("transfer timeout! addr: 0x%x,\n",
+				      msgs->addr);
+		} else {
+			ret = -EACK_I2C;
+			if (!priv->filter_msg)
+				debug("ACKERR! addr: 0x%x,IRQ:0x%x\n",
+				      msgs->addr, irq_stat);
+		}
+		mtk_i2c_init_hw(priv);
+		return ret;
+	}
+	return 0;
+}
+
+static int mtk_i2c_transfer(struct udevice *dev, struct i2c_msg *msg,
+			    int nmsgs)
+{
+	int ret;
+	int left_num;
+	u8 num_cnt;
+	struct mtk_i2c_priv *priv = dev_get_priv(dev);
+
+	priv->auto_restart = true;
+	left_num = nmsgs;
+	mtk_i2c_clk_enable(priv);
+	for (num_cnt = 0; num_cnt < nmsgs; num_cnt++) {
+		if (((msg + num_cnt)->addr) > MAX_I2C_ADDR) {
+			ret = -EINVAL_I2C;
+			goto err_exit;
+		}
+		if ((msg + num_cnt)->len > MAX_I2C_LEN) {
+			ret = -EINVAL_I2C;
+			goto err_exit;
+		}
+	}
+
+	/* checking if we can skip restart and optimize using WRRD mode */
+	if (priv->auto_restart && nmsgs == 2) {
+		if (!(msg[0].flags & I2C_M_RD) && (msg[1].flags & I2C_M_RD) &&
+		    msg[0].addr == msg[1].addr) {
+			priv->auto_restart = false;
+		}
+	}
+
+	if (priv->auto_restart && nmsgs >= 2 && priv->speed > MAX_FS_MODE_SPEED)
+		/* ignore the first restart irq after the master code,
+		 * otherwise the first transfer will be discarded.
+		 */
+		priv->ignore_restart_irq = true;
+	else
+		priv->ignore_restart_irq = false;
+
+	while (left_num--) {
+		/*priv->zero_len = true
+		 *transfer slave address only to support devices detect
+		 */
+		if (!msg->buf)
+			priv->zero_len = true;
+		else
+			priv->zero_len = false;
+
+		if (msg->flags & I2C_M_RD)
+			priv->op = I2C_MASTER_RD;
+		else
+			priv->op = I2C_MASTER_WR;
+
+		if (!priv->auto_restart) {
+			if (nmsgs > 1) {
+				/* combined two messages into one transaction */
+				priv->op = I2C_MASTER_WRRD;
+				left_num--;
+			}
+		}
+		ret = mtk_i2c_do_transfer(priv, msg, nmsgs, left_num);
+		if (ret < 0)
+			goto err_exit;
+		msg++;
+	}
+	ret = I2C_OK;
+
+err_exit:
+	mtk_i2c_clk_disable(priv);
+	return ret;
+}
+
+static int mtk_i2c_ofdata_to_platdata(struct udevice *dev)
+{
+	int ret;
+	struct mtk_i2c_priv *priv = dev_get_priv(dev);
+
+	priv->base = dev_remap_addr_index(dev, 0);
+	priv->pdmabase = dev_remap_addr_index(dev, 1);
+	ret = clk_get_by_index(dev, 0, &priv->clk_main);
+	if (ret)
+		return ret;
+
+	ret = clk_get_by_index(dev, 1, &priv->clk_dma);
+
+	return ret;
+}
+
+static int mtk_i2c_probe(struct udevice *dev)
+{
+	struct mtk_i2c_priv *priv = dev_get_priv(dev);
+
+	priv->soc_data = (struct mtk_i2c_soc_data *)dev_get_driver_data(dev);
+	mtk_i2c_clk_enable(priv);
+	mtk_i2c_init_hw(priv);
+	mtk_i2c_clk_disable(priv);
+	return 0;
+}
+
+static int mtk_i2c_deblock(struct udevice *dev)
+{
+	struct mtk_i2c_priv *priv = dev_get_priv(dev);
+
+	mtk_i2c_clk_enable(priv);
+	mtk_i2c_init_hw(priv);
+	mtk_i2c_clk_disable(priv);
+	return 0;
+}
+
+static const struct mtk_i2c_soc_data mt8518_soc_data = {
+	.dma_sync = 0,
+};
+
+static const struct mtk_i2c_soc_data mt8512_soc_data = {
+	.dma_sync = 1,
+};
+
+static const struct dm_i2c_ops mtk_i2c_ops = {
+	.xfer		= mtk_i2c_transfer,
+	.set_bus_speed	= mtk_i2c_set_speed,
+	.deblock	= mtk_i2c_deblock,
+};
+
+static const struct udevice_id mtk_i2c_ids[] = {
+	{
+		.compatible = "mediatek,mt8518-i2c",
+		.data = (ulong)&mt8518_soc_data,
+	},
+	{
+		.compatible = "mediatek,mt8512-i2c",
+		.data = (ulong)&mt8512_soc_data,
+	},
+	{
+	}
+};
+
+U_BOOT_DRIVER(mtk_i2c) = {
+	.name		= "mtk_i2c",
+	.id		= UCLASS_I2C,
+	.of_match	= mtk_i2c_ids,
+	.ofdata_to_platdata = mtk_i2c_ofdata_to_platdata,
+	.probe		= mtk_i2c_probe,
+	.priv_auto_alloc_size = sizeof(struct mtk_i2c_priv),
+	.ops		= &mtk_i2c_ops,
+};
-- 
1.9.1

  reply	other threads:[~2020-09-09  6:07 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-09-09  6:07 [PATCH 0/3] Add i2c support for MediaTek mt8512 mingming lee
2020-09-09  6:07 ` mingming lee [this message]
2020-09-30  5:38   ` [PATCH 1/3] i2c: mediatek: add basic driver support Heiko Schocher
2020-09-30  6:15     ` Mingming Lee
2020-09-09  6:07 ` [PATCH 2/3] ARM: dts: Mediatek: add i2c node support for mt8512 mingming lee
2020-09-30  5:40   ` Heiko Schocher
2020-09-09  6:07 ` [PATCH 3/3] configs: mt8512: Enable I2C related configs mingming lee
2020-09-30  5:41   ` Heiko Schocher

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200909060728.5616-2-mingming.lee@mediatek.com \
    --to=mingming.lee@mediatek.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.