diff options
author | Daolong Zhu <jg_daolongzhu@mediatek.corp-partner.google.com> | 2021-09-15 13:01:55 +0800 |
---|---|---|
committer | Felix Held <felix-coreboot@felixheld.de> | 2021-10-02 11:48:34 +0000 |
commit | f4b71734b266b5cf276e7bd3b0de45553a0728bd (patch) | |
tree | 85e45203fb6a32dc86f527c74ef9c9f4ae9d369e /src/soc/mediatek/common | |
parent | 38abbdab71e6bf275c9c49748f1830576ddb2f22 (diff) |
soc/mediatek: Fix I2C failures by adjusting AC timing and bus speed
1. The original algorithm for I2C speed cannot always make the
timing meet I2C specification so a new algorithm is introduced
to calculate the timing parameters more correctly.
2. Some I2C buses should be initialized in a different speed while
the original implementation was fixed at fast mode (400Khz).
So the mtk_i2c_bus_init is now also taking an extra speed
parameter.
There is an equivalent change in kernel side:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/drivers/i2c/busses/i2c-mt65xx.c?h=v5.15-rc3&id=be5ce0e97cc7a5c0d2da45d617b7bc567c3d3fa1
BUG=b:189899864
TEST=Test on Tomato, boot pass and timing pass
at 100/300/400/500/800/1000Khz.
Signed-off-by: Daolong Zhu <jg_daolongzhu@mediatek.corp-partner.google.com>
Change-Id: Id25b7bb3a76908a7943b940eb5bee799e80626a0
Reviewed-on: https://review.coreboot.org/c/coreboot/+/58053
Reviewed-by: Rex-BC Chen <rex-bc.chen@mediatek.corp-partner.google.com>
Reviewed-by: Yu-Ping Wu <yupingso@google.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Diffstat (limited to 'src/soc/mediatek/common')
-rw-r--r-- | src/soc/mediatek/common/i2c.c | 65 | ||||
-rw-r--r-- | src/soc/mediatek/common/include/soc/i2c_common.h | 43 |
2 files changed, 92 insertions, 16 deletions
diff --git a/src/soc/mediatek/common/i2c.c b/src/soc/mediatek/common/i2c.c index 3c54b17738..1e713dbea6 100644 --- a/src/soc/mediatek/common/i2c.c +++ b/src/soc/mediatek/common/i2c.c @@ -10,6 +10,39 @@ #include <soc/i2c.h> #include <device/i2c_simple.h> +const struct i2c_spec_values standard_mode_spec = { + .min_low_ns = 4700 + I2C_STANDARD_MODE_BUFFER, + .min_su_sta_ns = 4700 + I2C_STANDARD_MODE_BUFFER, + .max_hd_dat_ns = 3450 - I2C_STANDARD_MODE_BUFFER, + .min_su_dat_ns = 250 + I2C_STANDARD_MODE_BUFFER, +}; + +const struct i2c_spec_values fast_mode_spec = { + .min_low_ns = 1300 + I2C_FAST_MODE_BUFFER, + .min_su_sta_ns = 600 + I2C_FAST_MODE_BUFFER, + .max_hd_dat_ns = 900 - I2C_FAST_MODE_BUFFER, + .min_su_dat_ns = 100 + I2C_FAST_MODE_BUFFER, +}; + +const struct i2c_spec_values fast_mode_plus_spec = { + .min_low_ns = 500 + I2C_FAST_MODE_PLUS_BUFFER, + .min_su_sta_ns = 260 + I2C_FAST_MODE_PLUS_BUFFER, + .max_hd_dat_ns = 400 - I2C_FAST_MODE_PLUS_BUFFER, + .min_su_dat_ns = 50 + I2C_FAST_MODE_PLUS_BUFFER, +}; + +__weak void mtk_i2c_dump_more_info(struct mt_i2c_regs *regs) { /* do nothing */ } + +const struct i2c_spec_values *mtk_i2c_get_spec(uint32_t speed) +{ + if (speed <= I2C_SPEED_STANDARD) + return &standard_mode_spec; + else if (speed <= I2C_SPEED_FAST) + return &fast_mode_spec; + else + return &fast_mode_plus_spec; +} + static inline void i2c_hw_reset(uint8_t bus) { struct mt_i2c_regs *regs; @@ -42,24 +75,26 @@ static inline void i2c_hw_reset(uint8_t bus) static inline void mtk_i2c_dump_info(struct mt_i2c_regs *regs) { - printk(BIOS_ERR, "I2C register:\nSLAVE_ADDR %x\nINTR_MASK %x\n" + printk(BIOS_DEBUG, "I2C register:\nSLAVE_ADDR %x\nINTR_MASK %x\n" "INTR_STAT %x\nCONTROL %x\nTRANSFER_LEN %x\nTRANSAC_LEN %x\n" "DELAY_LEN %x\nTIMING %x\nSTART %x\nFIFO_STAT %x\nIO_CONFIG %x\n" "HS %x\nDEBUGSTAT %x\nEXT_CONF %x\n", - read32(®s->slave_addr), - read32(®s->intr_mask), - read32(®s->intr_stat), - read32(®s->control), - read32(®s->transfer_len), - read32(®s->transac_len), - read32(®s->delay_len), - read32(®s->timing), - read32(®s->start), - read32(®s->fifo_stat), - read32(®s->io_config), - read32(®s->hs), - read32(®s->debug_stat), - read32(®s->ext_conf)); + read32(®s->slave_addr), + read32(®s->intr_mask), + read32(®s->intr_stat), + read32(®s->control), + read32(®s->transfer_len), + read32(®s->transac_len), + read32(®s->delay_len), + read32(®s->timing), + read32(®s->start), + read32(®s->fifo_stat), + read32(®s->io_config), + read32(®s->hs), + read32(®s->debug_stat), + read32(®s->ext_conf)); + + mtk_i2c_dump_more_info(regs); } static uint32_t mtk_i2c_transfer(uint8_t bus, struct i2c_msg *seg, diff --git a/src/soc/mediatek/common/include/soc/i2c_common.h b/src/soc/mediatek/common/include/soc/i2c_common.h index d2da27ef4c..72ec46a093 100644 --- a/src/soc/mediatek/common/include/soc/i2c_common.h +++ b/src/soc/mediatek/common/include/soc/i2c_common.h @@ -3,6 +3,8 @@ #ifndef MTK_COMMON_I2C_H #define MTK_COMMON_I2C_H +#include <device/i2c.h> + /* I2C DMA Registers */ struct mt_i2c_dma_regs { uint32_t dma_int_flag; @@ -84,7 +86,6 @@ enum { }; /* I2C Status Code */ - enum { I2C_OK = 0x0000, I2C_SET_SPEED_FAIL_OVER_SPEED = 0xA001, @@ -95,11 +96,51 @@ enum { I2C_TRANSFER_INVALID_ARGUMENT = 0xA006 }; +struct mtk_i2c_ac_timing { + u16 htiming; + u16 ltiming; + u16 hs; + u16 ext; + u16 inter_clk_div; + u16 scl_hl_ratio; + u16 hs_scl_hl_ratio; + u16 sta_stop; + u16 hs_sta_stop; + u16 sda_timing; +}; + struct mtk_i2c { struct mt_i2c_regs *i2c_regs; struct mt_i2c_dma_regs *i2c_dma_regs; + struct mtk_i2c_ac_timing ac_timing; uint32_t mt_i2c_flag; }; +#define I2C_TIME_CLR_VALUE 0x0000 +#define MAX_SAMPLE_CNT_DIV 8 +#define MAX_STEP_CNT_DIV 64 +#define MAX_HS_STEP_CNT_DIV 8 +#define I2C_TIME_DEFAULT_VALUE 0x0083 +#define I2C_STANDARD_MODE_BUFFER (1000 / 3) +#define I2C_FAST_MODE_BUFFER (300 / 3) +#define I2C_FAST_MODE_PLUS_BUFFER (20 / 3) + +/* + * struct i2c_spec_values: + * @min_low_ns: min LOW period of the SCL clock + * @min_su_sta_ns: min set-up time for a repeated START condition + * @max_hd_dat_ns: max data hold time + * @min_su_dat_ns: min data set-up time + */ +struct i2c_spec_values { + uint32_t min_low_ns; + uint32_t min_su_sta_ns; + uint32_t max_hd_dat_ns; + uint32_t min_su_dat_ns; +}; + extern struct mtk_i2c mtk_i2c_bus_controller[]; +const struct i2c_spec_values *mtk_i2c_get_spec(uint32_t speed); +void mtk_i2c_dump_more_info(struct mt_i2c_regs *regs); + #endif |