From bcf08503f571553074a4e9563977225446c183fa Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 22 Apr 2020 10:13:53 -0600 Subject: [PATCH 1/4] i2c: designware_i2c: Tidy up use of NULL priv At present we still have pre-driver-model code in this driver and it makes things a bit confusing. In particular calc_bus_speed() is called with priv as NULL if not using driver model. This results in spk_cnt and comp_param1 being read from an invalid address if not using driver model. For comp_param1 this may not cause problems if reading from addresses close to 0 happens to be allowed, as high speed is only supported by DM code. But spk_cnt is subsequently used to calculate the bus periods and so this may cause problems (e.g. on spear600 board which has not been migrated yet). Add a new parameter regs parameter to calc_bus_speed() and add more comments to this function and to _dw_i2c_set_bus_speed(), which calls it. Signed-off-by: Simon Glass Reported-by: Heinrich Schuchardt --- drivers/i2c/designware_i2c.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index 42ee7d523e..c6a20721f6 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -199,18 +199,24 @@ static int dw_i2c_calc_timing(struct dw_i2c *priv, enum i2c_speed_mode mode, return 0; } -static int calc_bus_speed(struct dw_i2c *priv, int speed, ulong bus_clk, - struct dw_i2c_speed_config *config) +/** + * calc_bus_speed() - Calculate the config to use for a particular i2c speed + * + * @priv: Private information for the driver (NULL if not using driver model) + * @i2c_base: Registers for the I2C controller + * @speed: Required i2c speed in Hz + * @bus_clk: Input clock to the I2C controller in Hz (e.g. IC_CLK) + * @config: Returns the config to use for this speed + * @return 0 if OK, -ve on error + */ +static int calc_bus_speed(struct dw_i2c *priv, struct i2c_regs *regs, int speed, + ulong bus_clk, struct dw_i2c_speed_config *config) { const struct dw_scl_sda_cfg *scl_sda_cfg = NULL; - struct i2c_regs *regs = priv->regs; enum i2c_speed_mode i2c_spd; - u32 comp_param1; int spk_cnt; int ret; - comp_param1 = readl(®s->comp_param1); - if (priv) scl_sda_cfg = priv->scl_sda_cfg; /* Allow high speed if there is no config, or the config allows it */ @@ -225,6 +231,9 @@ static int calc_bus_speed(struct dw_i2c *priv, int speed, ulong bus_clk, /* Check is high speed possible and fall back to fast mode if not */ if (i2c_spd == IC_SPEED_MODE_HIGH) { + u32 comp_param1; + + comp_param1 = readl(®s->comp_param1); if ((comp_param1 & DW_IC_COMP_PARAM_1_SPEED_MODE_MASK) != DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH) i2c_spd = IC_SPEED_MODE_FAST; @@ -260,11 +269,14 @@ static int calc_bus_speed(struct dw_i2c *priv, int speed, ulong bus_clk, return 0; } -/* - * _dw_i2c_set_bus_speed - Set the i2c speed - * @speed: required i2c speed +/** + * _dw_i2c_set_bus_speed() - Set the i2c speed * - * Set the i2c speed. + * @priv: Private information for the driver (NULL if not using driver model) + * @i2c_base: Registers for the I2C controller + * @speed: Required i2c speed in Hz + * @bus_clk: Input clock to the I2C controller in Hz (e.g. IC_CLK) + * @return 0 if OK, -ve on error */ static int _dw_i2c_set_bus_speed(struct dw_i2c *priv, struct i2c_regs *i2c_base, unsigned int speed, unsigned int bus_clk) @@ -274,7 +286,7 @@ static int _dw_i2c_set_bus_speed(struct dw_i2c *priv, struct i2c_regs *i2c_base, unsigned int ena; int ret; - ret = calc_bus_speed(priv, speed, bus_clk, &config); + ret = calc_bus_speed(priv, i2c_base, speed, bus_clk, &config); if (ret) return ret; From f6f9a016899e62cb65016421a09fd3fe06ce660f Mon Sep 17 00:00:00 2001 From: Raul E Rangel Date: Wed, 22 Apr 2020 10:13:54 -0600 Subject: [PATCH 2/4] i2c: designware_i2c: Check if the device is powered If the device doesn't return a version that means the device is non-functional. The dw_i2c_regs had invalid offsets for the version field. I got the correct value from the DesignWare databook. It also matches what the Picasso PPR says. Signed-off-by: Raul E Rangel Reviewed-by: Simon Glass Reviewed-by: Furquan Shaikh Tested on chromebook_coral: Signed-off-by: Simon Glass --- drivers/i2c/designware_i2c.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index c6a20721f6..3616e2105f 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -18,6 +18,12 @@ #include #include +/* + * This assigned unique hex value is constant and is derived from the two ASCII + * letters 'DW' followed by a 16-bit unsigned number + */ +#define DW_I2C_COMP_TYPE 0x44570140 + #ifdef CONFIG_SYS_I2C_DW_ENABLE_STATUS_UNSUPPORTED static int dw_i2c_enable(struct i2c_regs *i2c_base, bool enable) { @@ -766,6 +772,17 @@ int designware_i2c_ofdata_to_platdata(struct udevice *bus) int designware_i2c_probe(struct udevice *bus) { struct dw_i2c *priv = dev_get_priv(bus); + uint comp_type; + + comp_type = readl(&priv->regs->comp_type); + if (comp_type != DW_I2C_COMP_TYPE) { + log_err("I2C bus %s has unknown type %#x\n", bus->name, + comp_type); + return -ENXIO; + } + + log_info("I2C bus %s version %#x\n", bus->name, + readl(&priv->regs->comp_version)); return __dw_i2c_init(priv->regs, 0, 0); } From da585c3c680add381662417b11cfc7d0ac310ba5 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 9 May 2020 18:20:18 +0200 Subject: [PATCH 3/4] i2c: observe scl_count in i2c_deblock_gpio_loop() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When compiling with -Wtype-limits we see this error: drivers/i2c/i2c-uclass.c: In function ‘i2c_deblock_gpio_loop’: drivers/i2c/i2c-uclass.c:517:21: error: comparison of unsigned expression >= 0 is always true [-Werror=type-limits] 517 | while (scl_count-- >= 0) { | Don't loop forever. Fixes: 1f746a2c82b1 ("i2c: Make deblock delay and SCL clock configurable") Signed-off-by: Heinrich Schuchardt --- drivers/i2c/i2c-uclass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 8166df7ba6..8bc69e870f 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -516,7 +516,7 @@ int i2c_deblock_gpio_loop(struct gpio_desc *sda_pin, udelay(delay); /* Toggle SCL until slave release SDA */ - while (scl_count-- >= 0) { + for (; scl_count; --scl_count) { i2c_gpio_set_pin(scl_pin, 1); udelay(delay); i2c_gpio_set_pin(scl_pin, 0); From b24dc83f156604f253ef6521776444188c5bff9b Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Thu, 7 May 2020 11:53:18 +0300 Subject: [PATCH 4/4] misc: i2c_eeprom: implement different probe test eeprom offset Because of this commit : 5ae84860b0 ("misc: i2c_eeprom: verify that the chip is functional at probe()") at probe time, each eeprom is tested for read at offset 0. The Atmel AT24MAC402 eeprom has different mapping. One i2c slave address is used for the lower 0x80 bytes and another i2c slave address is used for the upper 0x80 bytes. Because of this basically the i2c master sees 2 different slaves. We need the upper bytes because we read the unique MAC address from this EEPROM area. However this implies that our slave address will return error on reads from address 0x0 to 0x80. To solve this, implemented an offset field inside platform data that is by default 0 (as it is used now), but can be changed in the compatible table. The probe function will now read at this offset and use it, instead of blindly checking offset 0. This will fix the regression noticed on these EEPROMs since the commit abovementioned that introduces the probe failed issue. Signed-off-by: Eugen Hristev Reviewed-by: Heiko Schocher --- drivers/misc/i2c_eeprom.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/misc/i2c_eeprom.c b/drivers/misc/i2c_eeprom.c index ed23a62384..45c34d388c 100644 --- a/drivers/misc/i2c_eeprom.c +++ b/drivers/misc/i2c_eeprom.c @@ -18,6 +18,7 @@ struct i2c_eeprom_drv_data { u32 pagesize; /* page size in bytes */ u32 addr_offset_mask; /* bits in addr used for offset overflow */ u32 offset_len; /* size in bytes of offset */ + u32 start_offset; /* valid start offset inside memory, by default 0 */ }; int i2c_eeprom_read(struct udevice *dev, int offset, uint8_t *buf, int size) @@ -148,7 +149,11 @@ static int i2c_eeprom_std_probe(struct udevice *dev) i2c_set_chip_addr_offset_mask(dev, data->addr_offset_mask); /* Verify that the chip is functional */ - ret = i2c_eeprom_read(dev, 0, &test_byte, 1); + /* + * Not all eeproms start from offset 0. Valid offset is available + * in the platform data struct. + */ + ret = i2c_eeprom_read(dev, data->start_offset, &test_byte, 1); if (ret) return -ENODEV; @@ -216,6 +221,7 @@ static const struct i2c_eeprom_drv_data atmel24mac402_data = { .pagesize = 16, .addr_offset_mask = 0, .offset_len = 1, + .start_offset = 0x80, }; static const struct i2c_eeprom_drv_data atmel24c32_data = {