From 978fa765ca93d02eb9f1f1aacb65a1783fa86fb0 Mon Sep 17 00:00:00 2001 From: kewei xu Date: Mon, 1 Mar 2021 11:04:20 +0800 Subject: soc/mediatek/mt8195: Add i2c driver support TEST=write/read EEPROM on MT8195 EVB successfully Change-Id: Ia26e55512501e9758d7f5543d176730cf30ce03d Signed-off-by: kewei xu Reviewed-on: https://review.coreboot.org/c/coreboot/+/53894 Reviewed-by: Yu-Ping Wu Tested-by: build bot (Jenkins) --- src/soc/mediatek/mt8195/Makefile.inc | 4 + src/soc/mediatek/mt8195/i2c.c | 168 ++++++++++++++++++++++++++++++ src/soc/mediatek/mt8195/include/soc/i2c.h | 58 +++++++++++ 3 files changed, 230 insertions(+) create mode 100644 src/soc/mediatek/mt8195/i2c.c create mode 100644 src/soc/mediatek/mt8195/include/soc/i2c.h (limited to 'src') diff --git a/src/soc/mediatek/mt8195/Makefile.inc b/src/soc/mediatek/mt8195/Makefile.inc index f848f7bf33..aca3905f24 100644 --- a/src/soc/mediatek/mt8195/Makefile.inc +++ b/src/soc/mediatek/mt8195/Makefile.inc @@ -3,6 +3,7 @@ ifeq ($(CONFIG_SOC_MEDIATEK_MT8195),y) bootblock-y += bootblock.c bootblock-y += ../common/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 @@ -12,6 +13,7 @@ bootblock-y += ../common/wdt.c verstage-y += ../common/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 timer.c verstage-y += ../common/uart.c @@ -21,6 +23,7 @@ romstage-y += ../common/cbmem.c romstage-y += emi.c romstage-y += ../common/flash_controller.c romstage-y += ../common/gpio.c gpio.c +romstage-y += ../common/i2c.c i2c.c romstage-y += ../common/pll.c pll.c romstage-$(CONFIG_SPI_FLASH) += ../common/spi.c spi.c romstage-y += ../common/timer.c timer.c @@ -35,6 +38,7 @@ romstage-y += ../common/mt6359p.c mt6359p.c ramstage-y += emi.c ramstage-y += ../common/flash_controller.c ramstage-y += ../common/gpio.c gpio.c +ramstage-y += ../common/i2c.c i2c.c ramstage-y += ../common/mmu_operations.c mmu_operations.c ramstage-y += ../common/mtcmos.c mtcmos.c ramstage-$(CONFIG_SPI_FLASH) += ../common/spi.c spi.c diff --git a/src/soc/mediatek/mt8195/i2c.c b/src/soc/mediatek/mt8195/i2c.c new file mode 100644 index 0000000000..648083e7b6 --- /dev/null +++ b/src/soc/mediatek/mt8195/i2c.c @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include + +#define I2C_CLK_HZ (UNIVPLL_HZ / 20) +#define I2C_FULL_DUTY 100 +#define I2C_HALF_DUTY 50 +#define I2C_ADJUSTED_DUTY 50 +#define I2C_FS_START_CON 0x0 + +struct mtk_i2c mtk_i2c_bus_controller[] = { + [0] = { + .i2c_regs = (void *)(I2C_BASE), + .i2c_dma_regs = (void *)(I2C_DMA_BASE), + .mt_i2c_flag = I2C_APDMA_ASYNC, + }, + [1] = { + .i2c_regs = (void *)(I2C_BASE + 0x1000), + .i2c_dma_regs = (void *)(I2C_DMA_BASE + 0x180), + .mt_i2c_flag = I2C_APDMA_ASYNC, + }, + [2] = { + .i2c_regs = (void *)(I2C_BASE + 0x2000), + .i2c_dma_regs = (void *)(I2C_DMA_BASE + 0x300), + .mt_i2c_flag = I2C_APDMA_ASYNC, + }, + [3] = { + .i2c_regs = (void *)(I2C_BASE + 0x3000), + .i2c_dma_regs = (void *)(I2C_DMA_BASE + 0x400), + .mt_i2c_flag = I2C_APDMA_ASYNC, + }, + [4] = { + .i2c_regs = (void *)(I2C_BASE + 0x4000), + .i2c_dma_regs = (void *)(I2C_DMA_BASE + 0x480), + .mt_i2c_flag = I2C_APDMA_ASYNC, + }, + [5] = { + .i2c_regs = (void *)(I2C_BASE - 0x100000), + .i2c_dma_regs = (void *)(I2C_DMA_BASE + 0x500), + .mt_i2c_flag = I2C_APDMA_ASYNC, + }, + [6] = { + .i2c_regs = (void *)(I2C_BASE - 0xFF000), + .i2c_dma_regs = (void *)(I2C_DMA_BASE + 0x580), + .mt_i2c_flag = I2C_APDMA_ASYNC, + }, + [7] = { + .i2c_regs = (void *)(I2C_BASE - 0xFE000), + .i2c_dma_regs = (void *)(I2C_DMA_BASE + 0x600), + .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(HDMIRX_SCL, SCL5), + PAD_FUNC(HDMIRX_SDA, SDA5), + }, + [6] = { + PAD_FUNC(HDMITX_SCL, SCL6), + PAD_FUNC(HDMITX_SDA, SDA6), + }, + [7] = { + PAD_FUNC(HDMIRX_HTPLG, SCL7), + PAD_FUNC(HDMIRX_PWR5V, SDA7), + }, + +}; + +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); + if (bus <= I2C4) + 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; + uint32_t tar_speed = 400; + uint32_t tar_speed_high; + uint32_t tar_speed_low; + + assert(bus < I2C_BUS_NUMBER); + + /* Adjust ratio of high/low level */ + tar_speed_high = tar_speed * I2C_HALF_DUTY / I2C_ADJUSTED_DUTY; + + /* Calculate i2c frequency */ + step_div = DIV_ROUND_UP(I2C_CLK_HZ, + (tar_speed_high * 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 <= tar_speed_high * KHz && + i2c_freq >= (tar_speed_high - 20) * KHz); + + /* Init i2c bus timing register */ + write32(&mtk_i2c_bus_controller[bus].i2c_regs->timing, + (sample_div - 1) << 8 | (step_div - 1)); + + /* Adjust ratio of high/low level */ + tar_speed_low = tar_speed * I2C_HALF_DUTY / + (I2C_FULL_DUTY - I2C_ADJUSTED_DUTY); + + /* Calculate i2c frequency */ + step_div = DIV_ROUND_UP(I2C_CLK_HZ, + (tar_speed_low * 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 <= tar_speed_low * KHz && + i2c_freq >= (tar_speed_low - 20) * KHz); + 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); + + /* Adjust tSU,STA/tHD,STA/tSU,STO */ + write32(&mtk_i2c_bus_controller[bus].i2c_regs->ext_conf, I2C_FS_START_CON); +} + +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/mt8195/include/soc/i2c.h b/src/soc/mediatek/mt8195/include/soc/i2c.h new file mode 100644 index 0000000000..977106e482 --- /dev/null +++ b/src/soc/mediatek/mt8195/include/soc/i2c.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef SOC_MEDIATEK_MT8195_I2C_H +#define SOC_MEDIATEK_MT8195_I2C_H + +#include + +/* 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; +}; + +/* I2C ID Number*/ +enum { + I2C0, + I2C1, + I2C2, + I2C3, + I2C4, + I2C5, + I2C6, + I2C7, +}; + +check_member(mt_i2c_regs, multi_dma, 0xf8c); + +void mtk_i2c_bus_init(uint8_t bus); + +#endif /* SOC_MEDIATEK_MT8195_I2C_H */ -- cgit v1.2.3