From 1808a11f2a43a665aa88a662eef96028db5da6e5 Mon Sep 17 00:00:00 2001 From: Caveh Jalali Date: Sat, 18 Sep 2021 00:19:11 -0700 Subject: npcx/i2c: Refactor i2c_freq_changed This splits the implementation of i2c_freq_changed() into a per-port worker function and a top-level iterator function. This change makes i2c_port_set_freq() available to set a port's I2C speed. BRANCH=none BUG=b:201039003 TEST=with follow-on patches, switched I2C bus speed between 400 kHz and 1 MHz. Change-Id: Ifb5613ac3a59cbd64f01f3a396f978db5e77d9f1 Signed-off-by: Caveh Jalali Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3170291 Reviewed-by: Daisuke Nojiri --- chip/npcx/i2c.c | 160 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 86 insertions(+), 74 deletions(-) diff --git a/chip/npcx/i2c.c b/chip/npcx/i2c.c index 0528a7cca8..ccc3be554d 100644 --- a/chip/npcx/i2c.c +++ b/chip/npcx/i2c.c @@ -1017,90 +1017,103 @@ int i2c_raw_get_sda(int port) } /*****************************************************************************/ -/* Hooks */ -static void i2c_freq_changed(void) -{ - int freq, i, j; - for (i = 0; i < i2c_ports_used; i++) { - int bus_freq = i2c_ports[i].kbps; - int ctrl = i2c_port_to_controller(i2c_ports[i].port); - int scl_freq; +static void i2c_port_set_freq(const int ctrl, const int bus_freq) +{ + int freq, j; + int scl_freq; + const struct i2c_timing *pTiming; + int i2c_timing_used; #if NPCX_FAMILY_VERSION >= NPCX_FAMILY_NPCX7 - /* - * SMB0/1/4/5/6/7 use APB3 clock - * SMB2/3 use APB2 clock - */ - freq = (ctrl < 2 || ctrl > 3) ? - clock_get_apb3_freq() : clock_get_apb2_freq(); + /* + * SMB0/1/4/5/6/7 use APB3 clock + * SMB2/3 use APB2 clock + */ + freq = (ctrl < 2 || ctrl > 3) ? + clock_get_apb3_freq() : clock_get_apb2_freq(); #else /* CHIP_FAMILY_NPCX5 */ - /* - * SMB0/1 use core clock - * SMB2/3 use APB2 clock - */ - freq = (ctrl < 2) ? clock_get_freq() : clock_get_apb2_freq(); + /* + * SMB0/1 use core clock + * SMB2/3 use APB2 clock + */ + freq = (ctrl < 2) ? clock_get_freq() : clock_get_apb2_freq(); #endif - /* - * Set SCL frequency by formula: - * tSCL = 4 * SCLFRQ * tCLK - * fSCL = fCLK / (4*SCLFRQ) - * SCLFRQ = ceil(fCLK/(4*fSCL)) - */ - scl_freq = DIV_ROUND_UP(freq, bus_freq*4000); /* Unit in bps */ - - /* Normal mode if i2c freq is under 100kHz */ - if (bus_freq <= 100) { - /* Set divider value of SCL */ - SET_FIELD(NPCX_SMBCTL2(ctrl), NPCX_SMBCTL2_SCLFRQ7_FIELD - , (scl_freq & 0x7F)); - SET_FIELD(NPCX_SMBCTL3(ctrl), NPCX_SMBCTL3_SCLFRQ2_FIELD - , (scl_freq >> 7)); - } else { - const struct i2c_timing *pTiming; - int i2c_timing_used; + /* + * Set SCL frequency by formula: + * tSCL = 4 * SCLFRQ * tCLK + * fSCL = fCLK / (4*SCLFRQ) + * SCLFRQ = ceil(fCLK/(4*fSCL)) + */ + scl_freq = DIV_ROUND_UP(freq, bus_freq*4000); /* Unit in bps */ + + /* Normal mode if I2C freq is under 100kHz */ + if (bus_freq <= 100) { + /* Set divider value of SCL */ + SET_FIELD(NPCX_SMBCTL2(ctrl), NPCX_SMBCTL2_SCLFRQ7_FIELD, + (scl_freq & 0x7F)); + SET_FIELD(NPCX_SMBCTL3(ctrl), NPCX_SMBCTL3_SCLFRQ2_FIELD, + (scl_freq >> 7)); + return; + } - /* use Fast Mode */ - SET_BIT(NPCX_SMBCTL3(ctrl), NPCX_SMBCTL3_400K); - /* - * Set SCLH(L)T and hold-time directly for best i2c - * timing condition for all source clocks. Please refer - * Section 7.5.9 "SMBus Timing - Fast Mode" for detail. - */ - if (bus_freq == 400) { - pTiming = i2c_400k_timings; - i2c_timing_used = i2c_400k_timing_used; - } else if (bus_freq == 1000) { - pTiming = i2c_1m_timings; - i2c_timing_used = i2c_1m_timing_used; - } else { - /* Set value from formula */ - NPCX_SMBSCLLT(ctrl) = scl_freq; - NPCX_SMBSCLHT(ctrl) = scl_freq; - cprints(CC_I2C, - "Warning: Use 400K or 1MHz for better timing of I2c %d", - ctrl); - continue; - } + /* use Fast Mode */ + SET_BIT(NPCX_SMBCTL3(ctrl), NPCX_SMBCTL3_400K); + /* + * Set SCLH(L)T and hold-time directly for best I2C + * timing condition for all source clocks. Please refer + * Section 7.5.9 "SMBus Timing - Fast Mode" for detail. + */ + if (bus_freq == 400) { + pTiming = i2c_400k_timings; + i2c_timing_used = i2c_400k_timing_used; + } else if (bus_freq == 1000) { + pTiming = i2c_1m_timings; + i2c_timing_used = i2c_1m_timing_used; + } else { + /* Set value from formula */ + NPCX_SMBSCLLT(ctrl) = scl_freq; + NPCX_SMBSCLHT(ctrl) = scl_freq; + cprints(CC_I2C, + "Warning: I2C %d: Use 400kHz or 1MHz for better timing", + ctrl); + return; + } - for (j = 0; j < i2c_timing_used; j++, pTiming++) { - if (pTiming->clock == (freq/SECOND)) { - /* Set SCLH(L)T and hold-time */ - NPCX_SMBSCLLT(ctrl) = pTiming->k1/2; - NPCX_SMBSCLHT(ctrl) = pTiming->k2/2; - SET_FIELD(NPCX_SMBCTL4(ctrl), - NPCX_SMBCTL4_HLDT_FIELD, pTiming->HLDT); - break; - } - } - if (j == i2c_timing_used) - cprints(CC_I2C, - "Error: Please make sure src clock of i2c %d is supported", - ctrl); + for (j = 0; j < i2c_timing_used; j++, pTiming++) { + if (pTiming->clock == (freq/SECOND)) { + /* Set SCLH(L)T and hold-time */ + NPCX_SMBSCLLT(ctrl) = pTiming->k1/2; + NPCX_SMBSCLHT(ctrl) = pTiming->k2/2; + SET_FIELD(NPCX_SMBCTL4(ctrl), + NPCX_SMBCTL4_HLDT_FIELD, pTiming->HLDT); + break; } } + if (j == i2c_timing_used) + cprints(CC_I2C, "Error: I2C %d: src clk %d not supported", + ctrl, freq / SECOND); } + +/* Hooks */ + +static void i2c_freq_changed(void) +{ + int i; + + for (i = 0; i < i2c_ports_used; i++) { + const struct i2c_port_t *p; + int ctrl; + + p = &i2c_ports[i]; + ctrl = i2c_port_to_controller(p->port); + if (ctrl < 0) + continue; + i2c_port_set_freq(ctrl, p->kbps); + } +} + DECLARE_HOOK(HOOK_FREQ_CHANGE, i2c_freq_changed, HOOK_PRIO_DEFAULT); void i2c_init(void) @@ -1147,4 +1160,3 @@ void i2c_init(void) i2c_init_bus(ctrl); } } - -- cgit v1.2.1