123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870 |
- From de6f2293ab087f405dbcf7b8df45d1f9b03fc091 Mon Sep 17 00:00:00 2001
- From: Weijie Gao <[email protected]>
- Date: Wed, 27 Jul 2022 17:16:38 +0800
- Subject: [PATCH 17/31] i2c: add support for MediaTek I2C interface
- This patch adds support for MediaTek I2C interface
- Reviewed-by: Heiko Schocher <[email protected]>
- Reviewed-by: Simon Glass <[email protected]>
- Signed-off-by: Weijie Gao <[email protected]>
- ---
- drivers/i2c/Kconfig | 9 +
- drivers/i2c/Makefile | 1 +
- drivers/i2c/mtk_i2c.c | 822 ++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 832 insertions(+)
- create mode 100644 drivers/i2c/mtk_i2c.c
- --- a/drivers/i2c/Kconfig
- +++ b/drivers/i2c/Kconfig
- @@ -261,6 +261,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"
- + 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_MICROCHIP
- bool "Microchip I2C driver"
- help
- --- a/drivers/i2c/Makefile
- +++ b/drivers/i2c/Makefile
- @@ -32,6 +32,7 @@ obj-$(CONFIG_SYS_I2C_MICROCHIP) += i2c-m
- obj-$(CONFIG_SYS_I2C_MV) += mv_i2c.o
- obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o
- obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o
- +obj-$(CONFIG_SYS_I2C_MTK) += mtk_i2c.o
- obj-$(CONFIG_SYS_I2C_NEXELL) += nx_i2c.o
- obj-$(CONFIG_SYS_I2C_OCORES) += ocores_i2c.o
- obj-$(CONFIG_SYS_I2C_OCTEON) += octeon_i2c.o
- --- /dev/null
- +++ b/drivers/i2c/mtk_i2c.c
- @@ -0,0 +1,822 @@
- +// SPDX-License-Identifier: GPL-2.0+
- +/*
- + * Copyright (C) 2022 MediaTek Inc. All Rights Reserved.
- + *
- + * Author: Mingming Lee <[email protected]>
- + *
- + * MediaTek I2C Interface driver
- + */
- +
- +#include <clk.h>
- +#include <cpu_func.h>
- +#include <dm.h>
- +#include <i2c.h>
- +#include <log.h>
- +#include <asm/cache.h>
- +#include <asm/io.h>
- +#include <linux/delay.h>
- +#include <linux/errno.h>
- +
- +#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_TX_RX 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)
- +
- +#define I2C_MASTER_WR BIT(0)
- +#define I2C_MASTER_RD BIT(1)
- +#define I2C_MASTER_WRRD (I2C_MASTER_WR | I2C_MASTER_RD)
- +
- +enum I2C_REGS_OFFSET {
- + REG_PORT,
- + REG_SLAVE_ADDR,
- + REG_INTR_MASK,
- + REG_INTR_STAT,
- + REG_CONTROL,
- + REG_TRANSFER_LEN,
- + REG_TRANSAC_LEN,
- + REG_DELAY_LEN,
- + REG_TIMING,
- + REG_START,
- + REG_EXT_CONF,
- + REG_FIFO_STAT1,
- + REG_LTIMING,
- + REG_FIFO_STAT,
- + REG_FIFO_THRESH,
- + REG_FIFO_ADDR_CLR,
- + REG_IO_CONFIG,
- + REG_RSV_DEBUG,
- + REG_HS,
- + REG_SOFTRESET,
- + REG_DCM_EN,
- + REG_PATH_DIR,
- + REG_DEBUGSTAT,
- + REG_DEBUGCTRL,
- + REG_TRANSFER_LEN_AUX,
- + REG_CLOCK_DIV,
- + REG_SCL_HL_RATIO,
- + REG_SCL_HS_HL_RATIO,
- + REG_SCL_MIS_COMP_POINT,
- + REG_STA_STOP_AC_TIME,
- + REG_HS_STA_STOP_AC_TIME,
- + REG_DATA_TIME,
- +};
- +
- +enum DMA_REGS_OFFSET {
- + REG_INT_FLAG = 0x0,
- + REG_INT_EN = 0x04,
- + REG_EN = 0x08,
- + REG_RST = 0x0c,
- + REG_CON = 0x18,
- + REG_TX_MEM_ADDR = 0x1c,
- + REG_RX_MEM_ADDR = 0x20,
- + REG_TX_LEN = 0x24,
- + REG_RX_LEN = 0x28,
- +};
- +
- +static const uint mt_i2c_regs_v1[] = {
- + [REG_PORT] = 0x0,
- + [REG_SLAVE_ADDR] = 0x4,
- + [REG_INTR_MASK] = 0x8,
- + [REG_INTR_STAT] = 0xc,
- + [REG_CONTROL] = 0x10,
- + [REG_TRANSFER_LEN] = 0x14,
- + [REG_TRANSAC_LEN] = 0x18,
- + [REG_DELAY_LEN] = 0x1c,
- + [REG_TIMING] = 0x20,
- + [REG_START] = 0x24,
- + [REG_EXT_CONF] = 0x28,
- + [REG_FIFO_STAT1] = 0x2c,
- + [REG_FIFO_STAT] = 0x30,
- + [REG_FIFO_THRESH] = 0x34,
- + [REG_FIFO_ADDR_CLR] = 0x38,
- + [REG_IO_CONFIG] = 0x40,
- + [REG_RSV_DEBUG] = 0x44,
- + [REG_HS] = 0x48,
- + [REG_SOFTRESET] = 0x50,
- + [REG_SOFTRESET] = 0x50,
- + [REG_DCM_EN] = 0x54,
- + [REG_DEBUGSTAT] = 0x64,
- + [REG_DEBUGCTRL] = 0x68,
- + [REG_TRANSFER_LEN_AUX] = 0x6c,
- + [REG_CLOCK_DIV] = 0x70,
- + [REG_SCL_HL_RATIO] = 0x74,
- + [REG_SCL_HS_HL_RATIO] = 0x78,
- + [REG_SCL_MIS_COMP_POINT] = 0x7c,
- + [REG_STA_STOP_AC_TIME] = 0x80,
- + [REG_HS_STA_STOP_AC_TIME] = 0x84,
- + [REG_DATA_TIME] = 0x88,
- +};
- +
- +static const uint mt_i2c_regs_v2[] = {
- + [REG_PORT] = 0x0,
- + [REG_SLAVE_ADDR] = 0x4,
- + [REG_INTR_MASK] = 0x8,
- + [REG_INTR_STAT] = 0xc,
- + [REG_CONTROL] = 0x10,
- + [REG_TRANSFER_LEN] = 0x14,
- + [REG_TRANSAC_LEN] = 0x18,
- + [REG_DELAY_LEN] = 0x1c,
- + [REG_TIMING] = 0x20,
- + [REG_START] = 0x24,
- + [REG_EXT_CONF] = 0x28,
- + [REG_LTIMING] = 0x2c,
- + [REG_HS] = 0x30,
- + [REG_IO_CONFIG] = 0x34,
- + [REG_FIFO_ADDR_CLR] = 0x38,
- + [REG_TRANSFER_LEN_AUX] = 0x44,
- + [REG_CLOCK_DIV] = 0x48,
- + [REG_SOFTRESET] = 0x50,
- + [REG_DEBUGSTAT] = 0xe0,
- + [REG_DEBUGCTRL] = 0xe8,
- + [REG_FIFO_STAT] = 0xf4,
- + [REG_FIFO_THRESH] = 0xf8,
- + [REG_DCM_EN] = 0xf88,
- +};
- +
- +struct mtk_i2c_soc_data {
- + const uint *regs;
- + uint 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; /* Compatible data for different IC */
- + int op; /* operation mode */
- + bool zero_len; /* Only transfer slave address, no data */
- + bool pushpull; /* push pull mode or open drain mode */
- + bool filter_msg; /* filter msg error log */
- + bool auto_restart; /* restart mode */
- + bool ignore_restart_irq; /* ignore restart IRQ */
- + uint speed; /* i2c speed, unit: hz */
- +};
- +
- +static inline void i2c_writel(struct mtk_i2c_priv *priv, uint reg, uint value)
- +{
- + u32 offset = priv->soc_data->regs[reg];
- +
- + writel(value, priv->base + offset);
- +}
- +
- +static inline uint i2c_readl(struct mtk_i2c_priv *priv, uint offset)
- +{
- + return readl(priv->base + priv->soc_data->regs[offset]);
- +}
- +
- +static int mtk_i2c_clk_enable(struct mtk_i2c_priv *priv)
- +{
- + int ret;
- +
- + ret = clk_enable(&priv->clk_main);
- + if (ret)
- + return log_msg_ret("enable clk_main", ret);
- +
- + ret = clk_enable(&priv->clk_dma);
- + if (ret)
- + return log_msg_ret("enable clk_dma", ret);
- +
- + return 0;
- +}
- +
- +static int mtk_i2c_clk_disable(struct mtk_i2c_priv *priv)
- +{
- + int ret;
- +
- + ret = clk_disable(&priv->clk_dma);
- + if (ret)
- + return log_msg_ret("disable clk_dma", ret);
- +
- + ret = clk_disable(&priv->clk_main);
- + if (ret)
- + return log_msg_ret("disable clk_main", ret);
- +
- + return 0;
- +}
- +
- +static void mtk_i2c_init_hw(struct mtk_i2c_priv *priv)
- +{
- + uint control_reg;
- +
- + writel(I2C_DMA_HARD_RST, priv->pdmabase + REG_RST);
- + writel(I2C_DMA_CLR_FLAG, priv->pdmabase + REG_RST);
- + i2c_writel(priv, REG_SOFTRESET, I2C_SOFT_RST);
- + /* set ioconfig */
- + if (priv->pushpull)
- + i2c_writel(priv, REG_IO_CONFIG, I2C_IO_CONFIG_PUSH_PULL);
- + else
- + i2c_writel(priv, REG_IO_CONFIG, I2C_IO_CONFIG_OPEN_DRAIN);
- +
- + i2c_writel(priv, REG_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, REG_CONTROL, control_reg);
- + i2c_writel(priv, REG_DELAY_LEN, I2C_DELAY_LEN);
- +}
- +
- +/*
- + * Calculate i2c port speed
- + *
- + * Hardware design:
- + * i2c_bus_freq = parent_clk / (clock_div * 2 * sample_cnt * step_cnt)
- + * clock_div: fixed in hardware, but may be various in different SoCs
- + *
- + * The calculation want to pick the highest bus frequency that is still
- + * less than or equal to target_speed. The calculation try to get
- + * sample_cnt and step_cn
- + * @param[in]
- + * clk_src: i2c clock source
- + * @param[out]
- + * timing_step_cnt: step cnt calculate result
- + * @param[out]
- + * timing_sample_cnt: sample cnt calculate result
- + * @return
- + * 0, set speed successfully.
- + * -EINVAL, Unsupported speed.
- + */
- +static int mtk_i2c_calculate_speed(uint clk_src,
- + uint target_speed,
- + uint *timing_step_cnt,
- + uint *timing_sample_cnt)
- +{
- + uint base_sample_cnt = MAX_SAMPLE_CNT_DIV;
- + uint base_step_cnt;
- + uint max_step_cnt;
- + uint sample_cnt;
- + uint step_cnt;
- + uint opt_div;
- + uint best_mul;
- + uint 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 log_msg_ret("calculate speed", -EINVAL);
- + }
- +
- + *timing_step_cnt = step_cnt - 1;
- + *timing_sample_cnt = sample_cnt - 1;
- +
- + return 0;
- +}
- +
- +/*
- + * mtk_i2c_set_speed
- + *
- + * @par Description
- + * Calculate i2c speed and write sample_cnt, step_cnt to TIMING register.
- + * @param[in]
- + * dev: udevice pointer, struct udevice contains i2c source clock,
- + * clock divide and speed.
- + * @return
- + * 0, set speed successfully.\n
- + * error code from mtk_i2c_calculate_speed().
- + */
- +static int mtk_i2c_set_speed(struct udevice *dev, uint speed)
- +{
- + struct mtk_i2c_priv *priv = dev_get_priv(dev);
- + uint high_speed_reg;
- + uint sample_cnt;
- + uint timing_reg;
- + uint step_cnt;
- + uint clk_src;
- + int ret = 0;
- +
- + priv->speed = speed;
- + if (mtk_i2c_clk_enable(priv))
- + return log_msg_ret("set_speed enable clk", -1);
- +
- + clk_src = clk_get_rate(&priv->clk_main) / I2C_DEFAULT_CLK_DIV;
- + i2c_writel(priv, REG_CLOCK_DIV, (I2C_DEFAULT_CLK_DIV - 1));
- + if (priv->speed > MAX_FS_MODE_SPEED) {
- + /* Set master code speed register */
- + ret = mtk_i2c_calculate_speed(clk_src, MAX_FS_MODE_SPEED,
- + &step_cnt, &sample_cnt);
- + if (ret < 0)
- + goto exit;
- +
- + timing_reg = (sample_cnt << TIMING_SAMPLE_OFFSET) | step_cnt;
- + i2c_writel(priv, REG_TIMING, timing_reg);
- + /* Set the high speed mode register */
- + ret = mtk_i2c_calculate_speed(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);
- + i2c_writel(priv, REG_HS, high_speed_reg);
- + } else {
- + ret = mtk_i2c_calculate_speed(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;
- + i2c_writel(priv, REG_TIMING, timing_reg);
- + i2c_writel(priv, REG_HS, high_speed_reg);
- + }
- +exit:
- + if (mtk_i2c_clk_disable(priv))
- + return log_msg_ret("set_speed disable clk", -1);
- +
- + return ret;
- +}
- +
- +/*
- + * mtk_i2c_do_transfer
- + *
- + * @par Description
- + * Configure i2c register and trigger transfer.
- + * @param[in]
- + * priv: mtk_i2cmtk_i2c_priv pointer, struct mtk_i2c_priv contains register base\n
- + * address, operation mode, interrupt status and i2c driver data.
- + * @param[in]
- + * msgs: i2c_msg pointer, struct i2c_msg contains slave\n
- + * address, operation mode, msg length and data buffer.
- + * @param[in]
- + * num: i2c_msg number.
- + * @param[in]
- + * left_num: left i2c_msg number.
- + * @return
- + * 0, i2c transfer successfully.\n
- + * -ETIMEDOUT, i2c transfer timeout.\n
- + * -EREMOTEIO, i2c transfer ack error.
- + */
- +static int mtk_i2c_do_transfer(struct mtk_i2c_priv *priv,
- + struct i2c_msg *msgs,
- + int num, int left_num)
- +{
- + struct i2c_msg *msg_rx = NULL;
- + uint restart_flag = 0;
- + uint trans_error = 0;
- + uint irq_stat = 0;
- + uint tmo_poll = 0;
- + uint control_reg;
- + bool tmo = false;
- + uint start_reg;
- + uint addr_reg;
- + int ret = 0;
- +
- + if (priv->auto_restart)
- + restart_flag = I2C_RS_TRANSFER;
- +
- + control_reg = i2c_readl(priv, REG_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, REG_CONTROL, control_reg);
- +
- + /* set start condition */
- + if (priv->speed <= MAX_ST_MODE_SPEED)
- + i2c_writel(priv, REG_EXT_CONF, I2C_ST_START_CON);
- + else
- + i2c_writel(priv, REG_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, REG_SLAVE_ADDR, addr_reg | TRANS_ADDR_ONLY);
- + else
- + i2c_writel(priv, REG_SLAVE_ADDR, addr_reg);
- +
- + /* clear interrupt status */
- + i2c_writel(priv, REG_INTR_STAT, restart_flag | I2C_HS_NACKERR |
- + I2C_ACKERR | I2C_TRANSAC_COMP);
- + i2c_writel(priv, REG_FIFO_ADDR_CLR, I2C_FIFO_ADDR_CLR);
- +
- + /* enable interrupt */
- + i2c_writel(priv, REG_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, REG_TRANSFER_LEN, msgs->len);
- + i2c_writel(priv, REG_TRANSFER_LEN_AUX, (msgs + 1)->len);
- + i2c_writel(priv, REG_TRANSAC_LEN, I2C_WRRD_TRANAC_VALUE);
- + } else {
- + i2c_writel(priv, REG_TRANSFER_LEN, msgs->len);
- + i2c_writel(priv, REG_TRANSAC_LEN, num);
- + }
- +
- + /* Clear DMA interrupt flag */
- + writel(I2C_DMA_INT_FLAG_NONE, priv->pdmabase + REG_INT_FLAG);
- +
- + /* Flush cache for first msg */
- + flush_cache((ulong)msgs->buf, msgs->len);
- +
- + /*
- + * prepare buffer data to start transfer
- + * three cases here: read, write, write then read
- + */
- + if (priv->op & I2C_MASTER_WR) {
- + /* Set DMA direction TX (w/ or w/o RX) */
- + writel(I2C_DMA_CON_TX, priv->pdmabase + REG_CON);
- +
- + /* Write the tx buffer address to dma register */
- + writel((ulong)msgs->buf, priv->pdmabase + REG_TX_MEM_ADDR);
- + /* Write the tx length to dma register */
- + writel(msgs->len, priv->pdmabase + REG_TX_LEN);
- +
- + if (priv->op & I2C_MASTER_RD) {
- + /* write then read */
- + msg_rx = msgs + 1;
- +
- + /* Flush cache for second msg */
- + flush_cache((ulong)msg_rx->buf, msg_rx->len);
- + }
- + }
- +
- + if (priv->op & I2C_MASTER_RD) {
- + if (!msg_rx) {
- + /* Set DMA direction RX */
- + writel(I2C_DMA_CON_RX, priv->pdmabase + REG_CON);
- +
- + msg_rx = msgs;
- + }
- +
- + /* Write the rx buffer address to dma register */
- + writel((ulong)msg_rx->buf, priv->pdmabase + REG_RX_MEM_ADDR);
- + /* Write the rx length to dma register */
- + writel(msg_rx->len, priv->pdmabase + REG_RX_LEN);
- + }
- +
- + writel(I2C_DMA_START_EN, priv->pdmabase + REG_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, REG_START, start_reg);
- +
- + for (;;) {
- + irq_stat = i2c_readl(priv, REG_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;
- + i2c_writel(priv, REG_START, I2C_RS_MUL_CNFG |
- + I2C_RS_MUL_TRIG | I2C_TRANSAC_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, REG_INTR_MASK, ~(restart_flag | I2C_HS_NACKERR |
- + I2C_ACKERR | I2C_TRANSAC_COMP));
- +
- + if (!tmo && trans_error != 0) {
- + if (tmo) {
- + ret = -ETIMEDOUT;
- + if (!priv->filter_msg)
- + debug("I2C timeout! addr: 0x%x,\n", msgs->addr);
- + } else {
- + ret = -EREMOTEIO;
- + if (!priv->filter_msg)
- + debug("I2C ACKERR! addr: 0x%x,IRQ:0x%x\n",
- + msgs->addr, irq_stat);
- + }
- + mtk_i2c_init_hw(priv);
- + }
- +
- + return ret;
- +}
- +
- +/*
- + * mtk_i2c_transfer
- + *
- + * @par Description
- + * Common i2c transfer API. Set i2c transfer mode according to i2c_msg\n
- + * information, then call mtk_i2c_do_transfer() to configure i2c register\n
- + * and trigger transfer.
- + * @param[in]
- + * dev: udevice pointer, struct udevice contains struct mtk_i2c_priv, \n
- + * struct mtk_i2c_priv contains register base\n
- + * address, operation mode, interrupt status and i2c driver data.
- + * @param[in]
- + * msgs: i2c_msg pointer, struct i2c_msg contains slave\n
- + * address, operation mode, msg length and data buffer.
- + * @param[in]
- + * num: i2c_msg number.
- + * @return
- + * i2c_msg number, i2c transfer successfully.\n
- + * -EINVAL, msg length is more than 16\n
- + * use DMA MODE or slave address more than 0x7f.\n
- + * error code from mtk_i2c_init_base().\n
- + * error code from mtk_i2c_set_speed().\n
- + * error code from mtk_i2c_do_transfer().
- + */
- +static int mtk_i2c_transfer(struct udevice *dev, struct i2c_msg *msg,
- + int nmsgs)
- +{
- + struct mtk_i2c_priv *priv = dev_get_priv(dev);
- + int left_num;
- + uint num_cnt;
- + int ret;
- +
- + priv->auto_restart = true;
- + left_num = nmsgs;
- + if (mtk_i2c_clk_enable(priv))
- + return log_msg_ret("transfer enable clk", -1);
- +
- + for (num_cnt = 0; num_cnt < nmsgs; num_cnt++) {
- + if (((msg + num_cnt)->addr) > MAX_I2C_ADDR) {
- + ret = -EINVAL;
- + goto err_exit;
- + }
- + if ((msg + num_cnt)->len > MAX_I2C_LEN) {
- + ret = -EINVAL;
- + goto err_exit;
- + }
- + }
- +
- + /* check 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--) {
- + /* 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 = 0;
- +
- +err_exit:
- + if (mtk_i2c_clk_disable(priv))
- + return log_msg_ret("transfer disable clk", -1);
- +
- + return ret;
- +}
- +
- +static int mtk_i2c_of_to_plat(struct udevice *dev)
- +{
- + struct mtk_i2c_priv *priv = dev_get_priv(dev);
- + int ret;
- +
- + 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 log_msg_ret("clk_get_by_index 0", 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);
- +
- + if (mtk_i2c_clk_enable(priv))
- + return log_msg_ret("probe enable clk", -1);
- +
- + mtk_i2c_init_hw(priv);
- +
- + if (mtk_i2c_clk_disable(priv))
- + return log_msg_ret("probe disable clk", -1);
- +
- + return 0;
- +}
- +
- +static int mtk_i2c_deblock(struct udevice *dev)
- +{
- + struct mtk_i2c_priv *priv = dev_get_priv(dev);
- +
- + if (mtk_i2c_clk_enable(priv))
- + return log_msg_ret("deblock enable clk", -1);
- +
- + mtk_i2c_init_hw(priv);
- +
- + if (mtk_i2c_clk_disable(priv))
- + return log_msg_ret("deblock disable clk", -1);
- +
- + return 0;
- +}
- +
- +static const struct mtk_i2c_soc_data mt76xx_soc_data = {
- + .regs = mt_i2c_regs_v1,
- + .dma_sync = 0,
- +};
- +
- +static const struct mtk_i2c_soc_data mt7981_soc_data = {
- + .regs = mt_i2c_regs_v1,
- + .dma_sync = 1,
- +};
- +
- +static const struct mtk_i2c_soc_data mt7986_soc_data = {
- + .regs = mt_i2c_regs_v1,
- + .dma_sync = 1,
- +};
- +
- +static const struct mtk_i2c_soc_data mt8183_soc_data = {
- + .regs = mt_i2c_regs_v2,
- + .dma_sync = 1,
- +};
- +
- +static const struct mtk_i2c_soc_data mt8518_soc_data = {
- + .regs = mt_i2c_regs_v1,
- + .dma_sync = 0,
- +};
- +
- +static const struct mtk_i2c_soc_data mt8512_soc_data = {
- + .regs = mt_i2c_regs_v1,
- + .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,mt7622-i2c",
- + .data = (ulong)&mt76xx_soc_data,
- + }, {
- + .compatible = "mediatek,mt7623-i2c",
- + .data = (ulong)&mt76xx_soc_data,
- + }, {
- + .compatible = "mediatek,mt7629-i2c",
- + .data = (ulong)&mt76xx_soc_data,
- + }, {
- + .compatible = "mediatek,mt7981-i2c",
- + .data = (ulong)&mt7981_soc_data,
- + }, {
- + .compatible = "mediatek,mt7986-i2c",
- + .data = (ulong)&mt7986_soc_data,
- + }, {
- + .compatible = "mediatek,mt8183-i2c",
- + .data = (ulong)&mt8183_soc_data,
- + }, {
- + .compatible = "mediatek,mt8512-i2c",
- + .data = (ulong)&mt8512_soc_data,
- + }, {
- + .compatible = "mediatek,mt8518-i2c",
- + .data = (ulong)&mt8518_soc_data,
- + }
- +};
- +
- +U_BOOT_DRIVER(mtk_i2c) = {
- + .name = "mtk_i2c",
- + .id = UCLASS_I2C,
- + .of_match = mtk_i2c_ids,
- + .of_to_plat = mtk_i2c_of_to_plat,
- + .probe = mtk_i2c_probe,
- + .priv_auto = sizeof(struct mtk_i2c_priv),
- + .ops = &mtk_i2c_ops,
- +};
|