| From 6ddc9132bc42a7dfcaca0cce4ff0f94e69bc5114 Mon Sep 17 00:00:00 2001 |
| From: Tyrone Ting <kfting@nuvoton.com> |
| Date: Thu, 19 Dec 2024 17:08:58 +0800 |
| Subject: [PATCH 5/6] i2c: npcm: use i2c frequency table |
| |
| Modify i2c frequency from table parameters for NPCM i2c modules. |
| |
| Supported frequencies are: |
| |
| 1. 100KHz |
| 2. 400KHz |
| 3. 1MHz |
| |
| The original equations were tested on a variety of chips and base clocks. |
| Since we added devices that use higher frequencies of the module we |
| saw that there is a mismatch between the equation and the actual |
| results on the bus itself, measured on scope. |
| |
| Meanwhile, the equations were not accurate to begin with. |
| They are an approximation of the ideal value. The ideal value is |
| calculated per frequency of the core module. |
| |
| So instead of using the equations we did an optimization per module |
| frequency, verified on a device. |
| |
| Most of the work was focused on the rise time of the SCL and SDA, |
| which depends on external load of the bus and PU. |
| |
| Different PCB designs, or specifically to this case: the number |
| and type of targets on the bus, impact the required values for |
| the timing registers. |
| |
| Users can recalculate the numbers for each bus and get an even better |
| optimization, but our users chose not to. |
| |
| We manually picked values per frequency that match the entire valid |
| range of targets (from 1 to max number). Then we check against the |
| AMR described in SMB spec and make sure that none of the values |
| is exceeding. |
| |
| This process was led by the chip architect and included a lot of testing. |
| |
| Upstream-Status: Accepted |
| Signed-off-by: Tyrone Ting <kfting@nuvoton.com> |
| Reviewed-by: Tali Perry <tali.perry1@gmail.com> |
| Link: https://lore.kernel.org/r/20241219090859.18722-4-kfting@nuvoton.com |
| Signed-off-by: Andi Shyti <andi.shyti@kernel.org> |
| --- |
| drivers/i2c/busses/i2c-npcm7xx.c | 374 ++++++++++++++++++++++++------- |
| 1 file changed, 288 insertions(+), 86 deletions(-) |
| |
| diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c |
| index 760608fdd075..1aae1a8a8055 100644 |
| --- a/drivers/i2c/busses/i2c-npcm7xx.c |
| +++ b/drivers/i2c/busses/i2c-npcm7xx.c |
| @@ -263,6 +263,265 @@ static const int npcm_i2caddr[I2C_NUM_OWN_ADDR] = { |
| #define I2C_FREQ_MIN_HZ 10000 |
| #define I2C_FREQ_MAX_HZ I2C_MAX_FAST_MODE_PLUS_FREQ |
| |
| +struct smb_timing_t { |
| + u32 core_clk; |
| + u8 hldt; |
| + u8 dbcnt; |
| + u16 sclfrq; |
| + u8 scllt; |
| + u8 sclht; |
| + bool fast_mode; |
| +}; |
| + |
| +static struct smb_timing_t smb_timing_100khz[] = { |
| + { |
| + .core_clk = 100000000, .hldt = 0x2A, .dbcnt = 0x4, |
| + .sclfrq = 0xFB, .scllt = 0x0, .sclht = 0x0, |
| + .fast_mode = false, |
| + }, |
| + { |
| + .core_clk = 62500000, .hldt = 0x2A, .dbcnt = 0x1, |
| + .sclfrq = 0x9D, .scllt = 0x0, .sclht = 0x0, |
| + .fast_mode = false, |
| + }, |
| + { |
| + .core_clk = 50000000, .hldt = 0x2A, .dbcnt = 0x1, |
| + .sclfrq = 0x7E, .scllt = 0x0, .sclht = 0x0, |
| + .fast_mode = false, |
| + }, |
| + { |
| + .core_clk = 48000000, .hldt = 0x2A, .dbcnt = 0x1, |
| + .sclfrq = 0x79, .scllt = 0x0, .sclht = 0x0, |
| + .fast_mode = false, |
| + }, |
| + { |
| + .core_clk = 40000000, .hldt = 0x2A, .dbcnt = 0x1, |
| + .sclfrq = 0x65, .scllt = 0x0, .sclht = 0x0, |
| + .fast_mode = false, |
| + }, |
| + { |
| + .core_clk = 30000000, .hldt = 0x2A, .dbcnt = 0x1, |
| + .sclfrq = 0x4C, .scllt = 0x0, .sclht = 0x0, |
| + .fast_mode = false, |
| + }, |
| + { |
| + .core_clk = 29000000, .hldt = 0x2A, .dbcnt = 0x1, |
| + .sclfrq = 0x49, .scllt = 0x0, .sclht = 0x0, |
| + .fast_mode = false, |
| + }, |
| + { |
| + .core_clk = 26000000, .hldt = 0x2A, .dbcnt = 0x1, |
| + .sclfrq = 0x42, .scllt = 0x0, .sclht = 0x0, |
| + .fast_mode = false, |
| + }, |
| + { |
| + .core_clk = 25000000, .hldt = 0x2A, .dbcnt = 0x1, |
| + .sclfrq = 0x3F, .scllt = 0x0, .sclht = 0x0, |
| + .fast_mode = false, |
| + }, |
| + { |
| + .core_clk = 24000000, .hldt = 0x2A, .dbcnt = 0x1, |
| + .sclfrq = 0x3D, .scllt = 0x0, .sclht = 0x0, |
| + .fast_mode = false, |
| + }, |
| + { |
| + .core_clk = 20000000, .hldt = 0x2A, .dbcnt = 0x1, |
| + .sclfrq = 0x33, .scllt = 0x0, .sclht = 0x0, |
| + .fast_mode = false, |
| + }, |
| + { |
| + .core_clk = 16180000, .hldt = 0x2A, .dbcnt = 0x1, |
| + .sclfrq = 0x29, .scllt = 0x0, .sclht = 0x0, |
| + .fast_mode = false, |
| + }, |
| + { |
| + .core_clk = 15000000, .hldt = 0x23, .dbcnt = 0x1, |
| + .sclfrq = 0x26, .scllt = 0x0, .sclht = 0x0, |
| + .fast_mode = false, |
| + }, |
| + { |
| + .core_clk = 13000000, .hldt = 0x1D, .dbcnt = 0x1, |
| + .sclfrq = 0x21, .scllt = 0x0, .sclht = 0x0, |
| + .fast_mode = false, |
| + }, |
| + { |
| + .core_clk = 12000000, .hldt = 0x1B, .dbcnt = 0x1, |
| + .sclfrq = 0x1F, .scllt = 0x0, .sclht = 0x0, |
| + .fast_mode = false, |
| + }, |
| + { |
| + .core_clk = 10000000, .hldt = 0x18, .dbcnt = 0x1, |
| + .sclfrq = 0x1A, .scllt = 0x0, .sclht = 0x0, |
| + .fast_mode = false, |
| + }, |
| + { |
| + .core_clk = 9000000, .hldt = 0x16, .dbcnt = 0x1, |
| + .sclfrq = 0x17, .scllt = 0x0, .sclht = 0x0, |
| + .fast_mode = false, |
| + }, |
| + { |
| + .core_clk = 8090000, .hldt = 0x14, .dbcnt = 0x1, |
| + .sclfrq = 0x15, .scllt = 0x0, .sclht = 0x0, |
| + .fast_mode = false, |
| + }, |
| + { |
| + .core_clk = 7500000, .hldt = 0x7, .dbcnt = 0x1, |
| + .sclfrq = 0x13, .scllt = 0x0, .sclht = 0x0, |
| + .fast_mode = false, |
| + }, |
| + { |
| + .core_clk = 6500000, .hldt = 0xE, .dbcnt = 0x1, |
| + .sclfrq = 0x11, .scllt = 0x0, .sclht = 0x0, |
| + .fast_mode = false, |
| + }, |
| + { |
| + .core_clk = 4000000, .hldt = 0x9, .dbcnt = 0x1, |
| + .sclfrq = 0xB, .scllt = 0x0, .sclht = 0x0, |
| + .fast_mode = false, |
| + }, |
| +}; |
| + |
| +static struct smb_timing_t smb_timing_400khz[] = { |
| + { |
| + .core_clk = 100000000, .hldt = 0x2A, .dbcnt = 0x3, |
| + .sclfrq = 0x0, .scllt = 0x47, .sclht = 0x35, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 62500000, .hldt = 0x2A, .dbcnt = 0x2, |
| + .sclfrq = 0x0, .scllt = 0x2C, .sclht = 0x22, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 50000000, .hldt = 0x21, .dbcnt = 0x1, |
| + .sclfrq = 0x0, .scllt = 0x24, .sclht = 0x1B, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 48000000, .hldt = 0x1E, .dbcnt = 0x1, |
| + .sclfrq = 0x0, .scllt = 0x24, .sclht = 0x19, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 40000000, .hldt = 0x1B, .dbcnt = 0x1, |
| + .sclfrq = 0x0, .scllt = 0x1E, .sclht = 0x14, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 33000000, .hldt = 0x15, .dbcnt = 0x1, |
| + .sclfrq = 0x0, .scllt = 0x19, .sclht = 0x11, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 30000000, .hldt = 0x15, .dbcnt = 0x1, |
| + .sclfrq = 0x0, .scllt = 0x19, .sclht = 0xD, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 29000000, .hldt = 0x11, .dbcnt = 0x1, |
| + .sclfrq = 0x0, .scllt = 0x15, .sclht = 0x10, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 26000000, .hldt = 0x10, .dbcnt = 0x1, |
| + .sclfrq = 0x0, .scllt = 0x13, .sclht = 0xE, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 25000000, .hldt = 0xF, .dbcnt = 0x1, |
| + .sclfrq = 0x0, .scllt = 0x13, .sclht = 0xD, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 24000000, .hldt = 0xD, .dbcnt = 0x1, |
| + .sclfrq = 0x0, .scllt = 0x12, .sclht = 0xD, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 20000000, .hldt = 0xB, .dbcnt = 0x1, |
| + .sclfrq = 0x0, .scllt = 0xF, .sclht = 0xA, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 16180000, .hldt = 0xA, .dbcnt = 0x1, |
| + .sclfrq = 0x0, .scllt = 0xC, .sclht = 0x9, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 15000000, .hldt = 0x9, .dbcnt = 0x1, |
| + .sclfrq = 0x0, .scllt = 0xB, .sclht = 0x8, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 13000000, .hldt = 0x7, .dbcnt = 0x1, |
| + .sclfrq = 0x0, .scllt = 0xA, .sclht = 0x7, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 12000000, .hldt = 0x7, .dbcnt = 0x1, |
| + .sclfrq = 0x0, .scllt = 0xA, .sclht = 0x6, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 10000000, .hldt = 0x6, .dbcnt = 0x1, |
| + .sclfrq = 0x0, .scllt = 0x8, .sclht = 0x5, |
| + .fast_mode = true, |
| + }, |
| +}; |
| + |
| +static struct smb_timing_t smb_timing_1000khz[] = { |
| + { |
| + .core_clk = 100000000, .hldt = 0x15, .dbcnt = 0x4, |
| + .sclfrq = 0x0, .scllt = 0x1C, .sclht = 0x15, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 62500000, .hldt = 0xF, .dbcnt = 0x3, |
| + .sclfrq = 0x0, .scllt = 0x11, .sclht = 0xE, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 50000000, .hldt = 0xA, .dbcnt = 0x2, |
| + .sclfrq = 0x0, .scllt = 0xE, .sclht = 0xB, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 48000000, .hldt = 0x9, .dbcnt = 0x2, |
| + .sclfrq = 0x0, .scllt = 0xD, .sclht = 0xB, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 41000000, .hldt = 0x9, .dbcnt = 0x2, |
| + .sclfrq = 0x0, .scllt = 0xC, .sclht = 0x9, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 40000000, .hldt = 0x8, .dbcnt = 0x2, |
| + .sclfrq = 0x0, .scllt = 0xB, .sclht = 0x9, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 33000000, .hldt = 0x7, .dbcnt = 0x1, |
| + .sclfrq = 0x0, .scllt = 0xA, .sclht = 0x7, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 25000000, .hldt = 0x4, .dbcnt = 0x1, |
| + .sclfrq = 0x0, .scllt = 0x7, .sclht = 0x6, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 24000000, .hldt = 0x7, .dbcnt = 0x1, |
| + .sclfrq = 0x0, .scllt = 0x8, .sclht = 0x5, |
| + .fast_mode = true, |
| + }, |
| + { |
| + .core_clk = 20000000, .hldt = 0x4, .dbcnt = 0x1, |
| + .sclfrq = 0x0, .scllt = 0x6, .sclht = 0x4, |
| + .fast_mode = true, |
| + }, |
| +}; |
| + |
| struct npcm_i2c_data { |
| u8 fifo_size; |
| u32 segctl_init_val; |
| @@ -1805,102 +2064,45 @@ static void npcm_i2c_recovery_init(struct i2c_adapter *_adap) |
| */ |
| static int npcm_i2c_init_clk(struct npcm_i2c *bus, u32 bus_freq_hz) |
| { |
| - u32 k1 = 0; |
| - u32 k2 = 0; |
| - u8 dbnct = 0; |
| - u32 sclfrq = 0; |
| - u8 hldt = 7; |
| + struct smb_timing_t *smb_timing; |
| + u8 scl_table_cnt = 0, table_size = 0; |
| u8 fast_mode = 0; |
| - u32 src_clk_khz; |
| - u32 bus_freq_khz; |
| |
| - src_clk_khz = bus->apb_clk / 1000; |
| - bus_freq_khz = bus_freq_hz / 1000; |
| bus->bus_freq = bus_freq_hz; |
| |
| - /* 100KHz and below: */ |
| - if (bus_freq_hz <= I2C_MAX_STANDARD_MODE_FREQ) { |
| - sclfrq = src_clk_khz / (bus_freq_khz * 4); |
| - |
| - if (sclfrq < SCLFRQ_MIN || sclfrq > SCLFRQ_MAX) |
| - return -EDOM; |
| - |
| - if (src_clk_khz >= 40000) |
| - hldt = 17; |
| - else if (src_clk_khz >= 12500) |
| - hldt = 15; |
| - else |
| - hldt = 7; |
| - } |
| - |
| - /* 400KHz: */ |
| - else if (bus_freq_hz <= I2C_MAX_FAST_MODE_FREQ) { |
| - sclfrq = 0; |
| + switch (bus_freq_hz) { |
| + case I2C_MAX_STANDARD_MODE_FREQ: |
| + smb_timing = smb_timing_100khz; |
| + table_size = ARRAY_SIZE(smb_timing_100khz); |
| + break; |
| + case I2C_MAX_FAST_MODE_FREQ: |
| + smb_timing = smb_timing_400khz; |
| + table_size = ARRAY_SIZE(smb_timing_400khz); |
| fast_mode = I2CCTL3_400K_MODE; |
| - |
| - if (src_clk_khz < 7500) |
| - /* 400KHZ cannot be supported for core clock < 7.5MHz */ |
| - return -EDOM; |
| - |
| - else if (src_clk_khz >= 50000) { |
| - k1 = 80; |
| - k2 = 48; |
| - hldt = 12; |
| - dbnct = 7; |
| - } |
| - |
| - /* Master or Slave with frequency > 25MHz */ |
| - else if (src_clk_khz > 25000) { |
| - hldt = clk_coef(src_clk_khz, 300) + 7; |
| - k1 = clk_coef(src_clk_khz, 1600); |
| - k2 = clk_coef(src_clk_khz, 900); |
| - } |
| - } |
| - |
| - /* 1MHz: */ |
| - else if (bus_freq_hz <= I2C_MAX_FAST_MODE_PLUS_FREQ) { |
| - sclfrq = 0; |
| + break; |
| + case I2C_MAX_FAST_MODE_PLUS_FREQ: |
| + smb_timing = smb_timing_1000khz; |
| + table_size = ARRAY_SIZE(smb_timing_1000khz); |
| fast_mode = I2CCTL3_400K_MODE; |
| - |
| - /* 1MHZ cannot be supported for core clock < 24 MHz */ |
| - if (src_clk_khz < 24000) |
| - return -EDOM; |
| - |
| - k1 = clk_coef(src_clk_khz, 620); |
| - k2 = clk_coef(src_clk_khz, 380); |
| - |
| - /* Core clk > 40 MHz */ |
| - if (src_clk_khz > 40000) { |
| - /* |
| - * Set HLDT: |
| - * SDA hold time: (HLDT-7) * T(CLK) >= 120 |
| - * HLDT = 120/T(CLK) + 7 = 120 * FREQ(CLK) + 7 |
| - */ |
| - hldt = clk_coef(src_clk_khz, 120) + 7; |
| - } else { |
| - hldt = 7; |
| - dbnct = 2; |
| - } |
| + break; |
| + default: |
| + return -EINVAL; |
| } |
| |
| - /* Frequency larger than 1 MHz is not supported */ |
| - else |
| - return -EINVAL; |
| + for (scl_table_cnt = 0; scl_table_cnt < table_size; scl_table_cnt++) |
| + if (bus->apb_clk >= smb_timing[scl_table_cnt].core_clk) |
| + break; |
| |
| - if (bus_freq_hz >= I2C_MAX_FAST_MODE_FREQ) { |
| - k1 = round_up(k1, 2); |
| - k2 = round_up(k2 + 1, 2); |
| - if (k1 < SCLFRQ_MIN || k1 > SCLFRQ_MAX || |
| - k2 < SCLFRQ_MIN || k2 > SCLFRQ_MAX) |
| - return -EDOM; |
| - } |
| + if (scl_table_cnt == table_size) |
| + return -EINVAL; |
| |
| /* write sclfrq value. bits [6:0] are in I2CCTL2 reg */ |
| - iowrite8(FIELD_PREP(I2CCTL2_SCLFRQ6_0, sclfrq & 0x7F), |
| + iowrite8(FIELD_PREP(I2CCTL2_SCLFRQ6_0, smb_timing[scl_table_cnt].sclfrq & 0x7F), |
| bus->reg + NPCM_I2CCTL2); |
| |
| /* bits [8:7] are in I2CCTL3 reg */ |
| - iowrite8(fast_mode | FIELD_PREP(I2CCTL3_SCLFRQ8_7, (sclfrq >> 7) & 0x3), |
| + iowrite8(FIELD_PREP(I2CCTL3_SCLFRQ8_7, (smb_timing[scl_table_cnt].sclfrq >> 7) & 0x3) | |
| + fast_mode, |
| bus->reg + NPCM_I2CCTL3); |
| |
| /* Select Bank 0 to access NPCM_I2CCTL4/NPCM_I2CCTL5 */ |
| @@ -1912,13 +2114,13 @@ static int npcm_i2c_init_clk(struct npcm_i2c *bus, u32 bus_freq_hz) |
| * k1 = 2 * SCLLT7-0 -> Low Time = k1 / 2 |
| * k2 = 2 * SCLLT7-0 -> High Time = k2 / 2 |
| */ |
| - iowrite8(k1 / 2, bus->reg + NPCM_I2CSCLLT); |
| - iowrite8(k2 / 2, bus->reg + NPCM_I2CSCLHT); |
| + iowrite8(smb_timing[scl_table_cnt].scllt, bus->reg + NPCM_I2CSCLLT); |
| + iowrite8(smb_timing[scl_table_cnt].sclht, bus->reg + NPCM_I2CSCLHT); |
| |
| - iowrite8(dbnct, bus->reg + NPCM_I2CCTL5); |
| + iowrite8(smb_timing[scl_table_cnt].dbcnt, bus->reg + NPCM_I2CCTL5); |
| } |
| |
| - iowrite8(hldt, bus->reg + NPCM_I2CCTL4); |
| + iowrite8(smb_timing[scl_table_cnt].hldt, bus->reg + NPCM_I2CCTL4); |
| |
| /* Return to Bank 1, and stay there by default: */ |
| npcm_i2c_select_bank(bus, I2C_BANK_1); |
| -- |
| 2.48.1.502.g6dc24dfdaf-goog |
| |