diff options
Diffstat (limited to 'src/soc/mediatek/mt8195/i2c.c')
-rw-r--r-- | src/soc/mediatek/mt8195/i2c.c | 168 |
1 files changed, 168 insertions, 0 deletions
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 <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) +#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); +} |