soc/mediatek/mt8192: add i2c driver support

Add I2C controller for MT8192, and revise the common I2C driver
to support I2C controller running in APDMA async mode. In that
case we have to initiate a different handshake protocol and reset
I2C differently.

BUG=b:155715435
TEST=Asurada boots up to shell

Signed-off-by: Qii Wang <qii.wang@mediatek.com>
Change-Id: I13835e00eb674a93aa5496a9870d1e601e263368
Reviewed-on: https://review.coreboot.org/c/coreboot/+/47800
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Hung-Te Lin <hungte@chromium.org>
diff --git a/src/soc/mediatek/common/i2c.c b/src/soc/mediatek/common/i2c.c
index 87925ba..3c54b17 100644
--- a/src/soc/mediatek/common/i2c.c
+++ b/src/soc/mediatek/common/i2c.c
@@ -10,14 +10,34 @@
 #include <soc/i2c.h>
 #include <device/i2c_simple.h>
 
-static inline void i2c_dma_reset(struct mt_i2c_dma_regs *dma_regs)
+static inline void i2c_hw_reset(uint8_t bus)
 {
-	write32(&dma_regs->dma_rst, 0x1);
-	udelay(50);
-	write32(&dma_regs->dma_rst, 0x2);
-	udelay(50);
-	write32(&dma_regs->dma_rst, 0x0);
-	udelay(50);
+	struct mt_i2c_regs *regs;
+	struct mt_i2c_dma_regs *dma_regs;
+
+	regs = mtk_i2c_bus_controller[bus].i2c_regs;
+	dma_regs = mtk_i2c_bus_controller[bus].i2c_dma_regs;
+
+	if (mtk_i2c_bus_controller[bus].mt_i2c_flag == I2C_APDMA_ASYNC) {
+		write32(&dma_regs->dma_rst, I2C_DMA_WARM_RST);
+		udelay(10);
+		write32(&dma_regs->dma_rst, I2C_DMA_CLR_FLAG);
+		udelay(10);
+		write32(&dma_regs->dma_rst,
+			I2C_DMA_HARD_RST | I2C_DMA_HANDSHAKE_RST);
+		write32(&regs->softreset, I2C_SOFT_RST | I2C_HANDSHAKE_RST);
+		udelay(10);
+		write32(&dma_regs->dma_rst, I2C_DMA_CLR_FLAG);
+		write32(&regs->softreset, I2C_CLR_FLAG);
+	} else {
+		write32(&regs->softreset, I2C_SOFT_RST);
+		write32(&dma_regs->dma_rst, I2C_DMA_WARM_RST);
+		udelay(50);
+		write32(&dma_regs->dma_rst, I2C_DMA_HARD_RST);
+		udelay(50);
+		write32(&dma_regs->dma_rst, I2C_DMA_CLR_FLAG);
+		udelay(50);
+	}
 }
 
 static inline void mtk_i2c_dump_info(struct mt_i2c_regs *regs)
@@ -47,6 +67,7 @@
 {
 	uint32_t ret_code = I2C_OK;
 	uint16_t status;
+	uint16_t dma_sync = 0;
 	uint32_t time_out_val = 0;
 	uint8_t  addr;
 	uint32_t write_len = 0;
@@ -62,6 +83,12 @@
 
 	addr = seg[0].slave;
 
+	if (mtk_i2c_bus_controller[bus].mt_i2c_flag == I2C_APDMA_ASYNC) {
+		dma_sync = I2C_DMA_SKIP_CONFIG | I2C_DMA_ASYNC_MODE;
+		if (mode == I2C_WRITE_READ_MODE)
+			dma_sync |= I2C_DMA_DIR_CHANGE;
+	}
+
 	switch (mode) {
 	case I2C_WRITE_MODE:
 		assert(seg[0].len > 0 && seg[0].len <= 255);
@@ -113,7 +140,7 @@
 		write32(&regs->slave_addr, addr << 1);
 
 		/* Prepare buffer data to start transfer */
-		write32(&dma_regs->dma_con, I2C_DMA_CON_TX);
+		write32(&dma_regs->dma_con, I2C_DMA_CON_TX | dma_sync);
 		write32(&dma_regs->dma_tx_mem_addr, (uintptr_t)_dma_coherent);
 		write32(&dma_regs->dma_tx_len, write_len);
 		break;
@@ -132,7 +159,7 @@
 		write32(&regs->slave_addr, (addr << 1 | 0x1));
 
 		/* Prepare buffer data to start transfer */
-		write32(&dma_regs->dma_con, I2C_DMA_CON_RX);
+		write32(&dma_regs->dma_con, I2C_DMA_CON_RX | dma_sync);
 		write32(&dma_regs->dma_rx_mem_addr, (uintptr_t)_dma_coherent);
 		write32(&dma_regs->dma_rx_len, read_len);
 		break;
@@ -154,7 +181,7 @@
 		write32(&regs->slave_addr, addr << 1);
 
 		/* Prepare buffer data to start transfer */
-		write32(&dma_regs->dma_con, I2C_DMA_CLR_FLAG);
+		write32(&dma_regs->dma_con, I2C_DMA_CLR_FLAG | dma_sync);
 		write32(&dma_regs->dma_tx_mem_addr, (uintptr_t)_dma_coherent);
 		write32(&dma_regs->dma_tx_len, write_len);
 		write32(&dma_regs->dma_rx_mem_addr, (uintptr_t)_dma_coherent);
@@ -206,9 +233,7 @@
 		I2C_TRANSAC_COMP);
 
 	/* reset the i2c controller for next i2c transfer. */
-	write32(&regs->softreset, 0x1);
-
-	i2c_dma_reset(dma_regs);
+	i2c_hw_reset(bus);
 
 	return ret_code;
 }
diff --git a/src/soc/mediatek/common/include/soc/i2c_common.h b/src/soc/mediatek/common/include/soc/i2c_common.h
index 0100e26..d2da27ef 100644
--- a/src/soc/mediatek/common/include/soc/i2c_common.h
+++ b/src/soc/mediatek/common/include/soc/i2c_common.h
@@ -38,6 +38,12 @@
 	I2C_DMA_INT_FLAG_NONE   = 0x0,
 	I2C_DMA_CLR_FLAG        = 0x0,
 	I2C_DMA_FLUSH_FLAG      = 0x1,
+	I2C_DMA_ASYNC_MODE      = 0x0004,
+	I2C_DMA_SKIP_CONFIG     = 0x0010,
+	I2C_DMA_DIR_CHANGE      = 0x0200,
+	I2C_DMA_WARM_RST        = 0x1,
+	I2C_DMA_HARD_RST        = 0x2,
+	I2C_DMA_HANDSHAKE_RST   = 0x4,
 };
 
 enum {
@@ -46,6 +52,11 @@
 	I2C_CONTROL_MASK = (0x3f << 1)
 };
 
+enum {
+	I2C_APDMA_NOASYNC       = 0,
+	I2C_APDMA_ASYNC         = 1,
+};
+
 /* Register mask */
 enum {
 	I2C_HS_NACKERR = (1 << 2),
@@ -53,6 +64,13 @@
 	I2C_TRANSAC_COMP = (1 << 0),
 };
 
+/* reset bits */
+enum {
+	I2C_CLR_FLAG            = 0x0,
+	I2C_SOFT_RST            = 0x1,
+	I2C_HANDSHAKE_RST       = 0x20,
+};
+
 /* i2c control bits */
 enum {
 	ASYNC_MODE = (1 << 9),
@@ -80,6 +98,7 @@
 struct mtk_i2c {
 	struct mt_i2c_regs *i2c_regs;
 	struct mt_i2c_dma_regs *i2c_dma_regs;
+	uint32_t mt_i2c_flag;
 };
 
 extern struct mtk_i2c mtk_i2c_bus_controller[];
diff --git a/src/soc/mediatek/mt8192/Makefile.inc b/src/soc/mediatek/mt8192/Makefile.inc
index bd6fe27..8394c12 100644
--- a/src/soc/mediatek/mt8192/Makefile.inc
+++ b/src/soc/mediatek/mt8192/Makefile.inc
@@ -4,6 +4,7 @@
 bootblock-y += bootblock.c
 bootblock-y += flash_controller.c
 bootblock-y += ../common/gpio.c gpio.c
+bootblock-y += ../common/i2c.c i2c.c
 bootblock-y += ../common/mmu_operations.c
 bootblock-y += ../common/pll.c pll.c
 bootblock-$(CONFIG_SPI_FLASH) += ../common/spi.c spi.c
@@ -17,6 +18,7 @@
 verstage-y += ../common/auxadc.c
 verstage-y += flash_controller.c
 verstage-y += ../common/gpio.c gpio.c
+verstage-y += ../common/i2c.c i2c.c
 verstage-$(CONFIG_SPI_FLASH) += ../common/spi.c spi.c
 verstage-y += ../common/timer.c
 verstage-y += ../common/uart.c
@@ -26,6 +28,7 @@
 romstage-y += emi.c
 romstage-y += flash_controller.c
 romstage-y += ../common/gpio.c gpio.c
+romstage-y += ../common/i2c.c i2c.c
 romstage-y += ../common/mmu_operations.c mmu_operations.c
 romstage-y += memory.c dramc_param.c ../common/memory_test.c
 romstage-$(CONFIG_SPI_FLASH) += ../common/spi.c spi.c
@@ -38,6 +41,7 @@
 ramstage-y += dpm.c
 ramstage-y += flash_controller.c
 ramstage-y += ../common/gpio.c gpio.c
+ramstage-y += ../common/i2c.c i2c.c
 ramstage-y += emi.c
 ramstage-$(CONFIG_SPI_FLASH) += ../common/spi.c spi.c
 ramstage-y += ../common/mcu.c
diff --git a/src/soc/mediatek/mt8192/i2c.c b/src/soc/mediatek/mt8192/i2c.c
new file mode 100644
index 0000000..e38cbb6
--- /dev/null
+++ b/src/soc/mediatek/mt8192/i2c.c
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <assert.h>
+#include <device/mmio.h>
+#include <soc/pll.h>
+#include <soc/i2c.h>
+#include <soc/gpio.h>
+
+#define I2C_CLK_HZ (UNIVPLL_HZ / 20)
+struct mtk_i2c mtk_i2c_bus_controller[] = {
+	[0] = {
+		.i2c_regs = (void *)(I2C_BASE + 0x250000),
+		.i2c_dma_regs = (void *)(I2C_DMA_BASE),
+		.mt_i2c_flag = I2C_APDMA_ASYNC,
+	},
+	[1] = {
+		.i2c_regs = (void *)(I2C_BASE + 0x70000),
+		.i2c_dma_regs = (void *)(I2C_DMA_BASE + 0x80),
+		.mt_i2c_flag = I2C_APDMA_ASYNC,
+	},
+	[2] = {
+		.i2c_regs = (void *)(I2C_BASE + 0x71000),
+		.i2c_dma_regs = (void *)(I2C_DMA_BASE + 0x100),
+		.mt_i2c_flag = I2C_APDMA_ASYNC,
+	},
+	[3] = {
+		.i2c_regs = (void *)(I2C_BASE),
+		.i2c_dma_regs = (void *)(I2C_DMA_BASE + 0x280),
+		.mt_i2c_flag = I2C_APDMA_ASYNC,
+	},
+	[4] = {
+		.i2c_regs = (void *)(I2C_BASE + 0x72000),
+		.i2c_dma_regs = (void *)(I2C_DMA_BASE + 0x300),
+		.mt_i2c_flag = I2C_APDMA_ASYNC,
+	},
+	[5] = {
+		.i2c_regs = (void *)(I2C_BASE + 0x150000),
+		.i2c_dma_regs = (void *)(I2C_DMA_BASE + 0x480),
+		.mt_i2c_flag = I2C_APDMA_ASYNC,
+	},
+	[6] = {
+		.i2c_regs = (void *)(I2C_BASE + 0x251000),
+		.i2c_dma_regs = (void *)(I2C_DMA_BASE + 0x500),
+		.mt_i2c_flag = I2C_APDMA_ASYNC,
+	},
+	[7] = {
+		.i2c_regs = (void *)(I2C_BASE + 0x50000),
+		.i2c_dma_regs = (void *)(I2C_DMA_BASE + 0x580),
+		.mt_i2c_flag = I2C_APDMA_ASYNC,
+	},
+	[8] = {
+		.i2c_regs = (void *)(I2C_BASE + 0x51000),
+		.i2c_dma_regs = (void *)(I2C_DMA_BASE + 0x700),
+		.mt_i2c_flag = I2C_APDMA_ASYNC,
+	},
+	[9] = {
+		.i2c_regs = (void *)(I2C_BASE + 0x52000),
+		.i2c_dma_regs = (void *)(I2C_DMA_BASE + 0x880),
+		.mt_i2c_flag = I2C_APDMA_ASYNC,
+	},
+};
+
+#define I2C_BUS_NUMBER ARRAY_SIZE(mtk_i2c_bus_controller)
+
+struct pad_func {
+	gpio_t gpio;
+	u8 func;
+};
+
+#define PAD_FUNC(name, func) {GPIO(name), PAD_##name##_FUNC_##func}
+
+static const struct pad_func i2c_funcs[I2C_BUS_NUMBER][2] = {
+	[0] = {
+		PAD_FUNC(SDA0, SDA0),
+		PAD_FUNC(SCL0, SCL0),
+	},
+	[1] = {
+		PAD_FUNC(SDA1, SDA1),
+		PAD_FUNC(SCL1, SCL1),
+	},
+	[2] = {
+		PAD_FUNC(SDA2, SDA2),
+		PAD_FUNC(SCL2, SCL2),
+	},
+	[3] = {
+		PAD_FUNC(SDA3, SDA3),
+		PAD_FUNC(SCL3, SCL3),
+	},
+	[4] = {
+		PAD_FUNC(SDA4, SDA4),
+		PAD_FUNC(SCL4, SCL4),
+	},
+	[5] = {
+		PAD_FUNC(SDA5, SDA5),
+		PAD_FUNC(SCL5, SCL5),
+	},
+	[6] = {
+		PAD_FUNC(SDA6, SDA6),
+		PAD_FUNC(SCL6, SCL6),
+	},
+	[7] = {
+		PAD_FUNC(SDA7, SDA7),
+		PAD_FUNC(SCL7, SCL7),
+	},
+	[8] = {
+		PAD_FUNC(SDA8, SDA8),
+		PAD_FUNC(SCL8, SCL8),
+	},
+	[9] = {
+		PAD_FUNC(SDA9, SDA9),
+		PAD_FUNC(SCL9, SCL9),
+	},
+};
+
+static void mtk_i2c_set_gpio_pinmux(uint8_t bus)
+{
+	assert(bus < I2C_BUS_NUMBER);
+
+	const struct pad_func *ptr = i2c_funcs[bus];
+	for (size_t i = 0; i < 2; i++) {
+		gpio_set_mode(ptr[i].gpio, ptr[i].func);
+		gpio_set_pull(ptr[i].gpio, GPIO_PULL_ENABLE, GPIO_PULL_UP);
+	}
+}
+
+static void mtk_i2c_speed_init(uint8_t bus)
+{
+	uint8_t step_div;
+	const uint8_t clock_div = 5;
+	const uint8_t sample_div = 1;
+	uint32_t i2c_freq;
+
+	assert(bus < I2C_BUS_NUMBER);
+
+	/* Calculate i2c frequency */
+	step_div = DIV_ROUND_UP(I2C_CLK_HZ,
+				(400 * KHz * sample_div * 2) * clock_div);
+	i2c_freq = I2C_CLK_HZ / (step_div * sample_div * 2 * clock_div);
+	assert(sample_div < 8 && step_div < 64 && i2c_freq <= 400 * KHz &&
+	       i2c_freq >= 380 * KHz);
+
+	/* Init i2c bus timing register */
+	write32(&mtk_i2c_bus_controller[bus].i2c_regs->timing,
+		(sample_div - 1) << 8 | (step_div - 1));
+	write32(&mtk_i2c_bus_controller[bus].i2c_regs->ltiming,
+		(sample_div - 1) << 6 | (step_div - 1));
+
+	/* Init i2c bus clock_div register */
+	write32(&mtk_i2c_bus_controller[bus].i2c_regs->clock_div,
+		clock_div - 1);
+}
+
+void mtk_i2c_bus_init(uint8_t bus)
+{
+	mtk_i2c_speed_init(bus);
+	mtk_i2c_set_gpio_pinmux(bus);
+}
diff --git a/src/soc/mediatek/mt8192/include/soc/addressmap.h b/src/soc/mediatek/mt8192/include/soc/addressmap.h
index 8297129..0d756b8 100644
--- a/src/soc/mediatek/mt8192/include/soc/addressmap.h
+++ b/src/soc/mediatek/mt8192/include/soc/addressmap.h
@@ -27,6 +27,7 @@
 	PMIF_SPMI_BASE		= IO_PHYS + 0x00027000,
 	PMICSPI_MST_BASE	= IO_PHYS + 0x00028000,
 	SPMI_MST_BASE		= IO_PHYS + 0x00029000,
+	I2C_DMA_BASE		= IO_PHYS + 0x00217080,
 	SSPM_SRAM_BASE		= IO_PHYS + 0x00400000,
 	SSPM_CFG_BASE		= IO_PHYS + 0x00440000,
 	DPM_PM_SRAM_BASE	= IO_PHYS + 0x00900000,
@@ -46,6 +47,7 @@
 	SFLASH_REG_BASE		= IO_PHYS + 0x01234000,
 	EFUSEC_BASE		= IO_PHYS + 0x01C10000,
 	IOCFG_RM_BASE		= IO_PHYS + 0x01C20000,
+	I2C_BASE		= IO_PHYS + 0x01CB0000,
 	IOCFG_BM_BASE		= IO_PHYS + 0x01D10000,
 	IOCFG_BL_BASE		= IO_PHYS + 0x01D30000,
 	IOCFG_BR_BASE		= IO_PHYS + 0x01D40000,
diff --git a/src/soc/mediatek/mt8192/include/soc/i2c.h b/src/soc/mediatek/mt8192/include/soc/i2c.h
new file mode 100644
index 0000000..72a9af1
--- /dev/null
+++ b/src/soc/mediatek/mt8192/include/soc/i2c.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef SOC_MEDIATEK_MT8192_I2C_H
+#define SOC_MEDIATEK_MT8192_I2C_H
+
+#include <soc/i2c_common.h>
+
+/* I2C Register */
+struct mt_i2c_regs {
+	uint32_t data_port;
+	uint32_t slave_addr;
+	uint32_t intr_mask;
+	uint32_t intr_stat;
+	uint32_t control;
+	uint32_t transfer_len;
+	uint32_t transac_len;
+	uint32_t delay_len;
+	uint32_t timing;
+	uint32_t start;
+	uint32_t ext_conf;
+	uint32_t ltiming;
+	uint32_t hs;
+	uint32_t io_config;
+	uint32_t fifo_addr_clr;
+	uint32_t reserved0[2];
+	uint32_t transfer_aux_len;
+	uint32_t clock_div;
+	uint32_t time_out;
+	uint32_t softreset;
+	uint32_t reserved1[36];
+	uint32_t debug_stat;
+	uint32_t debug_ctrl;
+	uint32_t reserved2[2];
+	uint32_t fifo_stat;
+	uint32_t fifo_thresh;
+	uint32_t reserved3[932];
+	uint32_t multi_dma;
+	uint32_t reserved4[2];
+	uint32_t rollback;
+};
+
+check_member(mt_i2c_regs, multi_dma, 0xf8c);
+
+void mtk_i2c_bus_init(uint8_t bus);
+
+#endif /* SOC_MEDIATEK_MT8192_I2C_H */