summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/soc/mediatek/common/i2c.c51
-rw-r--r--src/soc/mediatek/common/include/soc/i2c_common.h19
-rw-r--r--src/soc/mediatek/mt8192/Makefile.inc4
-rw-r--r--src/soc/mediatek/mt8192/i2c.c157
-rw-r--r--src/soc/mediatek/mt8192/include/soc/addressmap.h2
-rw-r--r--src/soc/mediatek/mt8192/include/soc/i2c.h46
6 files changed, 266 insertions, 13 deletions
diff --git a/src/soc/mediatek/common/i2c.c b/src/soc/mediatek/common/i2c.c
index 87925ba169..3c54b17738 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 @@ static uint32_t mtk_i2c_transfer(uint8_t bus, struct i2c_msg *seg,
{
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 @@ static uint32_t mtk_i2c_transfer(uint8_t bus, struct i2c_msg *seg,
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 @@ static uint32_t mtk_i2c_transfer(uint8_t bus, struct i2c_msg *seg,
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 @@ static uint32_t mtk_i2c_transfer(uint8_t bus, struct i2c_msg *seg,
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 @@ static uint32_t mtk_i2c_transfer(uint8_t bus, struct i2c_msg *seg,
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 @@ static uint32_t mtk_i2c_transfer(uint8_t bus, struct i2c_msg *seg,
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 0100e261cd..d2da27ef4c 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 @@ enum {
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 @@ enum {
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 @@ enum {
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 @@ enum {
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 bd6fe27377..8394c12fba 100644
--- a/src/soc/mediatek/mt8192/Makefile.inc
+++ b/src/soc/mediatek/mt8192/Makefile.inc
@@ -4,6 +4,7 @@ bootblock-y += ../common/auxadc.c
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 @@ bootblock-y += mt6359p.c
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 += ../common/cbmem.c
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 += ../common/auxadc.c
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 0000000000..e38cbb617d
--- /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 829712926d..0d756b8b7a 100644
--- a/src/soc/mediatek/mt8192/include/soc/addressmap.h
+++ b/src/soc/mediatek/mt8192/include/soc/addressmap.h
@@ -27,6 +27,7 @@ enum {
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 @@ enum {
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 0000000000..72a9af10e2
--- /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 */