summaryrefslogtreecommitdiff
path: root/src/soc/mediatek/common
diff options
context:
space:
mode:
authorDaolong Zhu <jg_daolongzhu@mediatek.corp-partner.google.com>2021-09-15 13:01:55 +0800
committerFelix Held <felix-coreboot@felixheld.de>2021-10-02 11:48:34 +0000
commitf4b71734b266b5cf276e7bd3b0de45553a0728bd (patch)
tree85e45203fb6a32dc86f527c74ef9c9f4ae9d369e /src/soc/mediatek/common
parent38abbdab71e6bf275c9c49748f1830576ddb2f22 (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.c65
-rw-r--r--src/soc/mediatek/common/include/soc/i2c_common.h43
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(&regs->slave_addr),
- read32(&regs->intr_mask),
- read32(&regs->intr_stat),
- read32(&regs->control),
- read32(&regs->transfer_len),
- read32(&regs->transac_len),
- read32(&regs->delay_len),
- read32(&regs->timing),
- read32(&regs->start),
- read32(&regs->fifo_stat),
- read32(&regs->io_config),
- read32(&regs->hs),
- read32(&regs->debug_stat),
- read32(&regs->ext_conf));
+ read32(&regs->slave_addr),
+ read32(&regs->intr_mask),
+ read32(&regs->intr_stat),
+ read32(&regs->control),
+ read32(&regs->transfer_len),
+ read32(&regs->transac_len),
+ read32(&regs->delay_len),
+ read32(&regs->timing),
+ read32(&regs->start),
+ read32(&regs->fifo_stat),
+ read32(&regs->io_config),
+ read32(&regs->hs),
+ read32(&regs->debug_stat),
+ read32(&regs->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