diff options
author | Dino Li <Dino.Li@ite.com.tw> | 2020-08-11 14:13:19 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-09-02 09:56:48 +0000 |
commit | 589c496232eb3cd878e3a94f4db5fa188e79efdf (patch) | |
tree | a267582cb2534a10a3052849317f75f4e15c0161 | |
parent | dc72e1d946422ea3359c35c15f30cd68e1ce9ca4 (diff) | |
download | chrome-ec-589c496232eb3cd878e3a94f4db5fa188e79efdf.tar.gz |
it83xx/i2c: enable 400kHz timing registers for port A/B/C
With this change, port A/B/C will use timing registers to
config clock frequency of 400kHz.
BUG=b:163384683
BRANCH=none
TEST=tlow meet timing when port speed configuration is 400kHz
Signed-off-by: Dino Li <Dino.Li@ite.com.tw>
Change-Id: Id4fafafa021966acc33711dd454c109b3bf93766
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2359623
Reviewed-by: Diana Z <dzigterman@chromium.org>
-rw-r--r-- | chip/it83xx/i2c.c | 120 |
1 files changed, 78 insertions, 42 deletions
diff --git a/chip/it83xx/i2c.c b/chip/it83xx/i2c.c index 362462cdb7..91863eb308 100644 --- a/chip/it83xx/i2c.c +++ b/chip/it83xx/i2c.c @@ -773,59 +773,95 @@ void i2c_interrupt(int port) } } -static void i2c_freq_changed(void) +/* + * Set i2c standard port (A, B, or C) runs at 400kHz by using timing registers + * (offset 0h ~ 7h). + */ +static void i2c_standard_port_timing_regs_400khz(int port) { - int i, f, clk_div, psr, freq; - int p_ch; + /* Port clock frequency depends on setting of timing registers. */ + IT83XX_SMB_SCLKTS(port) = 0; + /* Suggested setting of timing registers of 400kHz. */ + IT83XX_SMB_4P7USL = 0x5; + IT83XX_SMB_4P0USL = 0x1; + IT83XX_SMB_300NS = 0x1; + IT83XX_SMB_250NS = 0x2; + IT83XX_SMB_45P3USL = 0x6a; + IT83XX_SMB_45P3USH = 0x1; + IT83XX_SMB_4P7A4P0H = 0; +} +/* Set clock frequency for i2c port A, B , or C */ +static void i2c_standard_port_set_frequency(int port, int freq_khz) +{ /* - * Standard I2C Channels + * If port's clock frequency is 400kHz, we use timing registers + * for setting. So we can adjust tlow to meet timing. + * The others use basic 50/100/1000 KHz setting. */ - for (i = 0; i < i2c_ports_used; i++) { - freq = i2c_ports[i].kbps; - if (i2c_ports[i].port < I2C_STANDARD_PORT_COUNT) { - for (f = ARRAY_SIZE(i2c_freq_select) - 1; f >= 0; f--) { - if (freq >= i2c_freq_select[f].kbps) { - IT83XX_SMB_SCLKTS(i2c_ports[i].port) = + if (freq_khz == 400) { + i2c_standard_port_timing_regs_400khz(port); + } else { + for (int f = ARRAY_SIZE(i2c_freq_select) - 1; f >= 0; f--) { + if (freq_khz >= i2c_freq_select[f].kbps) { + IT83XX_SMB_SCLKTS(port) = i2c_freq_select[f].freq_set; - break; - } - } - } else { - p_ch = i2c_ch_reg_shift(i2c_ports[i].port); - /* - * Let psr(Prescale) = IT83XX_I2C_PSR(p_ch) - * Then, 1 SCL cycle = 2 x (psr + 2) x SMBus clock cycle - * SMBus clock = PLL_CLOCK / clk_div - * SMBus clock cycle = 1 / SMBus clock - * 1 SCL cycle = 1 / (1000 x freq) - * 1 / (1000 x freq) = - * 2 x (psr + 2) x (1 / (PLL_CLOCK / clk_div)) - * psr = ((PLL_CLOCK / clk_div) x - * (1 / (1000 x freq)) x (1 / 2)) - 2 - */ - if (freq) { - /* Get SMBus clock divide value */ - clk_div = (IT83XX_ECPM_SCDCR2 & 0x0F) + 1; - /* Calculate PSR value */ - psr = (PLL_CLOCK / - (clk_div * (2 * 1000 * freq))) - 2; - /* Set psr value under 0xFD */ - if (psr > 0xFD) - psr = 0xFD; - - /* Set I2C Speed */ - IT83XX_I2C_PSR(p_ch) = (psr & 0xFF); - IT83XX_I2C_HSPR(p_ch) = (psr & 0xFF); - - /* Backup */ - pdata[i2c_ports[i].port].freq = (psr & 0xFF); + break; } } } + /* This field defines the SMCLK0/1/2 clock/data low timeout. */ IT83XX_SMB_25MS = I2C_CLK_LOW_TIMEOUT; } + +/* Set clock frequency for i2c port D, E , or F */ +static void i2c_enhanced_port_set_frequency(int port, int freq_khz) +{ + int port_reg_shift, clk_div, psr; + + /* Get base address of i2c enhanced port's registers. */ + port_reg_shift = i2c_ch_reg_shift(port); + /* + * Let psr(Prescale) = IT83XX_I2C_PSR(port_reg_shift) + * Then, 1 SCL cycle = 2 x (psr + 2) x SMBus clock cycle + * SMBus clock = PLL_CLOCK / clk_div + * SMBus clock cycle = 1 / SMBus clock + * 1 SCL cycle = 1 / (1000 x freq) + * 1 / (1000 x freq) = 2 x (psr + 2) x (1 / (PLL_CLOCK / clk_div)) + * psr = ((PLL_CLOCK / clk_div) x (1 / (1000 x freq)) x (1 / 2)) - 2 + */ + if (freq_khz) { + /* Get SMBus clock divide value */ + clk_div = (IT83XX_ECPM_SCDCR2 & 0x0F) + 1; + /* Calculate PSR value */ + psr = (PLL_CLOCK / (clk_div * (2 * 1000 * freq_khz))) - 2; + /* Set psr value under 0xFD */ + if (psr > 0xFD) + psr = 0xFD; + + /* Set I2C Speed */ + IT83XX_I2C_PSR(port_reg_shift) = (psr & 0xFF); + IT83XX_I2C_HSPR(port_reg_shift) = (psr & 0xFF); + /* Backup */ + pdata[port].freq = (psr & 0xFF); + } +} + +static void i2c_freq_changed(void) +{ + int i, freq, port; + + /* Set clock frequency for I2C ports */ + for (i = 0; i < i2c_ports_used; i++) { + freq = i2c_ports[i].kbps; + port = i2c_ports[i].port; + if (port < I2C_STANDARD_PORT_COUNT) + i2c_standard_port_set_frequency(port, freq); + else + i2c_enhanced_port_set_frequency(port, freq); + } +} DECLARE_HOOK(HOOK_FREQ_CHANGE, i2c_freq_changed, HOOK_PRIO_DEFAULT); void i2c_init(void) |