diff options
author | Tom Rini <trini@konsulko.com> | 2023-11-02 10:12:33 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2023-11-02 10:12:33 -0400 |
commit | 07fe79c93c5caba181f37844ca95fbda4db3f613 (patch) | |
tree | 9766b3de49837bae1d4ea6c6b69fc33710333fd2 /drivers/i2c | |
parent | b0c391ce0c01064a96711965e22f5d745e73edc3 (diff) | |
parent | 5b6ee512ceb8d990e010646c4fe7b8a3633fad68 (diff) |
Merge tag 'i2cfixes-for-v2024-01-rc2' of https://source.denx.de/u-boot/custodians/u-boot-i2c
i2c updates for v2024.01-rc2
- nuvoton: support standard/fast/fast plus mode
- bootcount: remove legacy i2c driver and implement
DM based version
Bugfixes:
- designware_i2c: adjust timing calculation
SPL probing failed on the StarFive VisionFive 2 board
Heinrich fixed this, by syncing timing calculation with
linux implementation.
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/designware_i2c.c | 19 | ||||
-rw-r--r-- | drivers/i2c/i2c-uclass.c | 75 | ||||
-rw-r--r-- | drivers/i2c/npcm_i2c.c | 5 |
3 files changed, 90 insertions, 9 deletions
diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index e54de42abc..215ce010cb 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -24,6 +24,17 @@ */ #define DW_I2C_COMP_TYPE 0x44570140 +/* + * This constant is used to calculate when during the clock high phase the data + * bit shall be read. The value was copied from the Linux v6.5 function + * i2c_dw_scl_hcnt() which provides the following explanation: + * + * "This is just an experimental rule: the tHD;STA period turned out to be + * proportinal to (_HCNT + 3). With this setting, we could meet both tHIGH and + * tHD;STA timing specs." + */ +#define T_HD_STA_OFFSET 3 + static int dw_i2c_enable(struct i2c_regs *i2c_base, bool enable) { u32 ena = enable ? IC_ENABLE_0B : 0; @@ -155,10 +166,10 @@ static int dw_i2c_calc_timing(struct dw_i2c *priv, enum i2c_speed_mode mode, /* * Back-solve for hcnt and lcnt according to the following equations: - * SCL_High_time = [(HCNT + IC_*_SPKLEN + 7) * ic_clk] + SCL_Fall_time + * SCL_High_time = [(HCNT + IC_*_SPKLEN + T_HD_STA_OFFSET) * ic_clk] + SCL_Fall_time * SCL_Low_time = [(LCNT + 1) * ic_clk] - SCL_Fall_time + SCL_Rise_time */ - hcnt = min_thigh_cnt - fall_cnt - 7 - spk_cnt; + hcnt = min_thigh_cnt - fall_cnt - T_HD_STA_OFFSET - spk_cnt; lcnt = min_tlow_cnt - rise_cnt + fall_cnt - 1; if (hcnt < 0 || lcnt < 0) { @@ -170,13 +181,13 @@ static int dw_i2c_calc_timing(struct dw_i2c *priv, enum i2c_speed_mode mode, * Now add things back up to ensure the period is hit. If it is off, * split the difference and bias to lcnt for remainder */ - tot = hcnt + lcnt + 7 + spk_cnt + rise_cnt + 1; + tot = hcnt + lcnt + T_HD_STA_OFFSET + spk_cnt + rise_cnt + 1; if (tot < period_cnt) { diff = (period_cnt - tot) / 2; hcnt += diff; lcnt += diff; - tot = hcnt + lcnt + 7 + spk_cnt + rise_cnt + 1; + tot = hcnt + lcnt + T_HD_STA_OFFSET + spk_cnt + rise_cnt + 1; lcnt += period_cnt - tot; } diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 8867a560bd..5405067861 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -388,6 +388,81 @@ int i2c_get_chip_for_busnum(int busnum, int chip_addr, uint offset_len, return 0; } +/* Find and probe I2C bus based on a chip attached to it */ +static int i2c_get_parent_bus(ofnode chip, struct udevice **devp) +{ + ofnode node; + struct udevice *dev; + int ret; + + node = ofnode_get_parent(chip); + if (!ofnode_valid(node)) + return -ENODEV; + + ret = uclass_get_device_by_ofnode(UCLASS_I2C, node, &dev); + if (ret) { + *devp = NULL; + return ret; + } + + *devp = dev; + return 0; +} + +int i2c_get_chip_by_phandle(const struct udevice *parent, const char *prop_name, + struct udevice **devp) +{ + ofnode node; + uint phandle; + struct udevice *bus, *chip; + char *dev_name; + int ret; + + debug("%s: Searching I2C chip for phandle \"%s\"\n", + __func__, prop_name); + + dev_name = strdup(prop_name); + if (!dev_name) { + ret = -ENOMEM; + goto err_exit; + } + + ret = dev_read_u32(parent, "i2cbcdev", &phandle); + if (ret) + goto err_exit; + + node = ofnode_get_by_phandle(phandle); + if (!ofnode_valid(node)) { + ret = -ENODEV; + goto err_exit; + } + + ret = i2c_get_parent_bus(node, &bus); + if (ret) + goto err_exit; + + ret = device_bind_driver_to_node(bus, "i2c_generic_chip_drv", + dev_name, node, &chip); + if (ret) + goto err_exit; + + ret = device_probe(chip); + if (ret) { + device_unbind(chip); + goto err_exit; + } + + debug("%s succeeded\n", __func__); + *devp = chip; + return 0; + +err_exit: + free(dev_name); + debug("%s failed, ret = %d\n", __func__, ret); + *devp = NULL; + return ret; +} + int dm_i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags, struct udevice **devp) { diff --git a/drivers/i2c/npcm_i2c.c b/drivers/i2c/npcm_i2c.c index ea4ef53256..b867b6c8e9 100644 --- a/drivers/i2c/npcm_i2c.c +++ b/drivers/i2c/npcm_i2c.c @@ -517,11 +517,6 @@ static int npcm_i2c_init_clk(struct npcm_i2c_bus *bus, u32 bus_freq) u32 sclfrq; u8 hldt, val; - if (bus_freq > I2C_FREQ_100K) { - printf("Support standard mode only\n"); - return -EINVAL; - } - /* SCLFRQ = T(SCL)/4/T(CLK) = FREQ(CLK)/4/FREQ(SCL) */ sclfrq = freq / (bus_freq * 4); if (sclfrq < SCLFRQ_MIN || sclfrq > SCLFRQ_MAX) |