From afb88651a689aa0aed67c4cf439f1dc0d8803d5a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 23 Jan 2020 11:48:06 -0700 Subject: i2c: designware_i2c: Include clk.h in the header file We use struct clk here so really should include this header file to avoid build errors. Also switch the order of clk.h in the C file to match the required code style. Signed-off-by: Simon Glass Reviewed-by: Ley Foon Tan Reviewed-by: Jun Chen Reviewed-by: Heiko Schocher --- drivers/i2c/designware_i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c/designware_i2c.c') diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index b8cdd1c6613..138fc725612 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -4,8 +4,8 @@ * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com. */ -#include #include +#include #include #include #include -- cgit v1.2.3 From 6db7943b92f7a4b5858b6a68f137462ed0293e09 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 23 Jan 2020 11:48:07 -0700 Subject: i2c: designware_i2c: Rename 'max' speed to 'high' speed Some SoCs support a higher speed than what is currently called 'max' in this driver. Rename it to 'high' speed, which is the official name of the 3.4MHz speed. Signed-off-by: Simon Glass Reviewed-by: Jun Chen Reviewed-by: Heiko Schocher --- drivers/i2c/designware_i2c.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/i2c/designware_i2c.c') diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index 138fc725612..dd1cc0b8238 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -62,10 +62,10 @@ static unsigned int __dw_i2c_set_bus_speed(struct i2c_regs *i2c_base, unsigned int ena; int i2c_spd; - /* Allow max speed if there is no config, or the config allows it */ - if (speed >= I2C_MAX_SPEED && - (!scl_sda_cfg || scl_sda_cfg->has_max_speed)) - i2c_spd = IC_SPEED_MODE_MAX; + /* Allow high speed if there is no config, or the config allows it */ + if (speed >= I2C_HIGH_SPEED && + (!scl_sda_cfg || scl_sda_cfg->has_high_speed)) + i2c_spd = IC_SPEED_MODE_HIGH; else if (speed >= I2C_FAST_SPEED) i2c_spd = IC_SPEED_MODE_FAST; else @@ -80,7 +80,7 @@ static unsigned int __dw_i2c_set_bus_speed(struct i2c_regs *i2c_base, cntl = (readl(&i2c_base->ic_con) & (~IC_CON_SPD_MSK)); switch (i2c_spd) { - case IC_SPEED_MODE_MAX: + case IC_SPEED_MODE_HIGH: cntl |= IC_CON_SPD_SS; if (scl_sda_cfg) { hcnt = scl_sda_cfg->fs_hcnt; -- cgit v1.2.3 From 65190d15efffc0c3700e59654ead4ef149981ad4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 23 Jan 2020 11:48:08 -0700 Subject: i2c: designware_i2c: Use an enum for selected speed mode Group these #defines into an enum to make it easier to understand the relationship between them. Signed-off-by: Simon Glass Reviewed-by: Jun Chen Reviewed-by: Heiko Schocher --- drivers/i2c/designware_i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c/designware_i2c.c') diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index dd1cc0b8238..2416ef32f9f 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -57,10 +57,10 @@ static unsigned int __dw_i2c_set_bus_speed(struct i2c_regs *i2c_base, unsigned int speed, unsigned int bus_mhz) { + enum i2c_speed_mode i2c_spd; unsigned int cntl; unsigned int hcnt, lcnt; unsigned int ena; - int i2c_spd; /* Allow high speed if there is no config, or the config allows it */ if (speed >= I2C_HIGH_SPEED && -- cgit v1.2.3 From dd3c1602cf25b0be89d41d99ff33ef6d7d51f9f4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 23 Jan 2020 11:48:09 -0700 Subject: i2c: designware_i2c: Use an accurate bus clock instead of MHz At present the driver uses an approximation for the bus clock, e.g. 166MHz instead of 166 2/3 MHz. This can result in small errors in the resulting I2C speed, perhaps 0.5% or so. Adjust the existing code to start from the accurate figure, even if later rounding reduces this accuracy. Update the bus speed code to work in KHz instead of MHz, which removes most of the error. Signed-off-by: Simon Glass Reviewed-by: Heiko Schocher --- drivers/i2c/designware_i2c.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers/i2c/designware_i2c.c') diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index 2416ef32f9f..0a1df8015fe 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -55,8 +55,9 @@ static int dw_i2c_enable(struct i2c_regs *i2c_base, bool enable) static unsigned int __dw_i2c_set_bus_speed(struct i2c_regs *i2c_base, struct dw_scl_sda_cfg *scl_sda_cfg, unsigned int speed, - unsigned int bus_mhz) + unsigned int bus_clk) { + ulong bus_khz = bus_clk / 1000; enum i2c_speed_mode i2c_spd; unsigned int cntl; unsigned int hcnt, lcnt; @@ -86,8 +87,8 @@ static unsigned int __dw_i2c_set_bus_speed(struct i2c_regs *i2c_base, hcnt = scl_sda_cfg->fs_hcnt; lcnt = scl_sda_cfg->fs_lcnt; } else { - hcnt = (bus_mhz * MIN_HS_SCL_HIGHTIME) / NANO_TO_MICRO; - lcnt = (bus_mhz * MIN_HS_SCL_LOWTIME) / NANO_TO_MICRO; + hcnt = (bus_khz * MIN_HS_SCL_HIGHTIME) / NANO_TO_KILO; + lcnt = (bus_khz * MIN_HS_SCL_LOWTIME) / NANO_TO_KILO; } writel(hcnt, &i2c_base->ic_hs_scl_hcnt); writel(lcnt, &i2c_base->ic_hs_scl_lcnt); @@ -99,8 +100,8 @@ static unsigned int __dw_i2c_set_bus_speed(struct i2c_regs *i2c_base, hcnt = scl_sda_cfg->ss_hcnt; lcnt = scl_sda_cfg->ss_lcnt; } else { - hcnt = (bus_mhz * MIN_SS_SCL_HIGHTIME) / NANO_TO_MICRO; - lcnt = (bus_mhz * MIN_SS_SCL_LOWTIME) / NANO_TO_MICRO; + hcnt = (bus_khz * MIN_SS_SCL_HIGHTIME) / NANO_TO_KILO; + lcnt = (bus_khz * MIN_SS_SCL_LOWTIME) / NANO_TO_KILO; } writel(hcnt, &i2c_base->ic_ss_scl_hcnt); writel(lcnt, &i2c_base->ic_ss_scl_lcnt); @@ -113,8 +114,8 @@ static unsigned int __dw_i2c_set_bus_speed(struct i2c_regs *i2c_base, hcnt = scl_sda_cfg->fs_hcnt; lcnt = scl_sda_cfg->fs_lcnt; } else { - hcnt = (bus_mhz * MIN_FS_SCL_HIGHTIME) / NANO_TO_MICRO; - lcnt = (bus_mhz * MIN_FS_SCL_LOWTIME) / NANO_TO_MICRO; + hcnt = (bus_khz * MIN_FS_SCL_HIGHTIME) / NANO_TO_KILO; + lcnt = (bus_khz * MIN_FS_SCL_LOWTIME) / NANO_TO_KILO; } writel(hcnt, &i2c_base->ic_fs_scl_hcnt); writel(lcnt, &i2c_base->ic_fs_scl_lcnt); @@ -511,9 +512,6 @@ static int designware_i2c_set_bus_speed(struct udevice *bus, unsigned int speed) rate = clk_get_rate(&i2c->clk); if (IS_ERR_VALUE(rate)) return -EINVAL; - - /* Convert to MHz */ - rate /= 1000000; #else rate = IC_CLK; #endif -- cgit v1.2.3 From 80a03db4f3bfac8220d0445c33aee24c5a3c13b1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 23 Jan 2020 11:48:11 -0700 Subject: i2c: designware_i2c: Read device-tree properties The i2c controller defines a few timing properties. Read these in and store them for use by the driver. Signed-off-by: Simon Glass Reviewed-by: Heiko Schocher --- drivers/i2c/designware_i2c.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/i2c/designware_i2c.c') diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index 0a1df8015fe..34b6816545f 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -535,11 +535,15 @@ static int designware_i2c_probe_chip(struct udevice *bus, uint chip_addr, return ret; } -static int designware_i2c_ofdata_to_platdata(struct udevice *bus) +int designware_i2c_ofdata_to_platdata(struct udevice *bus) { struct dw_i2c *priv = dev_get_priv(bus); - priv->regs = (struct i2c_regs *)devfdt_get_addr_ptr(bus); + if (!priv->regs) + priv->regs = (struct i2c_regs *)devfdt_get_addr_ptr(bus); + dev_read_u32(bus, "i2c-scl-rising-time-ns", &priv->scl_rise_time_ns); + dev_read_u32(bus, "i2c-scl-falling-time-ns", &priv->scl_fall_time_ns); + dev_read_u32(bus, "i2c-sda-hold-time-ns", &priv->sda_hold_time_ns); return 0; } -- cgit v1.2.3 From d22409e2dc7adfaf99c1a5a02902a425f98fd15a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 23 Jan 2020 11:48:12 -0700 Subject: i2c: designware_i2c: Drop scl_sda_cfg parameter Instead of passing this parameter into __dw_i2c_set_bus_speed(), pass in the driver's private data, from which the function can obtain that information. This allows the function to have access to the full state of the driver. Signed-off-by: Sicomp_param1mon Glass Reviewed-by: Heiko Schocher Signed-off-by: Simon Glass --- drivers/i2c/designware_i2c.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/i2c/designware_i2c.c') diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index 34b6816545f..6e5545cd0cd 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -52,17 +52,20 @@ static int dw_i2c_enable(struct i2c_regs *i2c_base, bool enable) * * Set the i2c speed. */ -static unsigned int __dw_i2c_set_bus_speed(struct i2c_regs *i2c_base, - struct dw_scl_sda_cfg *scl_sda_cfg, +static unsigned int __dw_i2c_set_bus_speed(struct dw_i2c *priv, + struct i2c_regs *i2c_base, unsigned int speed, unsigned int bus_clk) { + const struct dw_scl_sda_cfg *scl_sda_cfg = NULL; ulong bus_khz = bus_clk / 1000; enum i2c_speed_mode i2c_spd; unsigned int cntl; unsigned int hcnt, lcnt; unsigned int ena; + if (priv) + scl_sda_cfg = priv->scl_sda_cfg; /* Allow high speed if there is no config, or the config allows it */ if (speed >= I2C_HIGH_SPEED && (!scl_sda_cfg || scl_sda_cfg->has_high_speed)) @@ -371,7 +374,7 @@ static int __dw_i2c_init(struct i2c_regs *i2c_base, int speed, int slaveaddr) writel(IC_TX_TL, &i2c_base->ic_tx_tl); writel(IC_STOP_DET, &i2c_base->ic_intr_mask); #ifndef CONFIG_DM_I2C - __dw_i2c_set_bus_speed(i2c_base, NULL, speed, IC_CLK); + __dw_i2c_set_bus_speed(NULL, i2c_base, speed, IC_CLK); writel(slaveaddr, &i2c_base->ic_sar); #endif @@ -416,7 +419,7 @@ static unsigned int dw_i2c_set_bus_speed(struct i2c_adapter *adap, unsigned int speed) { adap->speed = speed; - return __dw_i2c_set_bus_speed(i2c_get_base(adap), NULL, speed, IC_CLK); + return __dw_i2c_set_bus_speed(NULL, i2c_get_base(adap), speed, IC_CLK); } static void dw_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) @@ -515,8 +518,7 @@ static int designware_i2c_set_bus_speed(struct udevice *bus, unsigned int speed) #else rate = IC_CLK; #endif - return __dw_i2c_set_bus_speed(i2c->regs, i2c->scl_sda_cfg, speed, - rate); + return __dw_i2c_set_bus_speed(i2c, i2c->regs, speed, rate); } static int designware_i2c_probe_chip(struct udevice *bus, uint chip_addr, -- cgit v1.2.3 From 31adb873e7a8680f8aac8ba259717b3f2265872e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 23 Jan 2020 11:48:13 -0700 Subject: i2c: designware_i2c: Put hold config in a struct Create a struct to hold the three timing parameters. This will make it easier to move these calculations into a separate function in a later patch. Signed-off-by: Simon Glass Reviewed-by: Heiko Schocher --- drivers/i2c/designware_i2c.c | 82 +++++++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 27 deletions(-) (limited to 'drivers/i2c/designware_i2c.c') diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index 6e5545cd0cd..e50987a7175 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -13,6 +13,23 @@ #include #include "designware_i2c.h" +/** + * struct dw_i2c_speed_config - timings to use for a particular speed + * + * This holds calculated values to be written to the I2C controller. Each value + * is represented as a number of IC clock cycles. + * + * @scl_lcnt: Low count value for SCL + * @scl_hcnt: High count value for SCL + * @sda_hold: Data hold count + */ +struct dw_i2c_speed_config { + /* SCL high and low period count */ + uint scl_lcnt; + uint scl_hcnt; + uint sda_hold; +}; + #ifdef CONFIG_SYS_I2C_DW_ENABLE_STATUS_UNSUPPORTED static int dw_i2c_enable(struct i2c_regs *i2c_base, bool enable) { @@ -58,10 +75,10 @@ static unsigned int __dw_i2c_set_bus_speed(struct dw_i2c *priv, unsigned int bus_clk) { const struct dw_scl_sda_cfg *scl_sda_cfg = NULL; + struct dw_i2c_speed_config config; ulong bus_khz = bus_clk / 1000; enum i2c_speed_mode i2c_spd; unsigned int cntl; - unsigned int hcnt, lcnt; unsigned int ena; if (priv) @@ -83,53 +100,64 @@ static unsigned int __dw_i2c_set_bus_speed(struct dw_i2c *priv, cntl = (readl(&i2c_base->ic_con) & (~IC_CON_SPD_MSK)); + config.scl_hcnt = 0; + config.scl_lcnt = 0; + config.sda_hold = 0; + if (scl_sda_cfg) { + config.sda_hold = scl_sda_cfg->sda_hold; + if (i2c_spd == IC_SPEED_MODE_STANDARD) { + config.scl_hcnt = scl_sda_cfg->ss_hcnt; + config.scl_lcnt = scl_sda_cfg->ss_lcnt; + } else { + config.scl_hcnt = scl_sda_cfg->fs_hcnt; + config.scl_lcnt = scl_sda_cfg->fs_lcnt; + } + } + switch (i2c_spd) { case IC_SPEED_MODE_HIGH: cntl |= IC_CON_SPD_SS; - if (scl_sda_cfg) { - hcnt = scl_sda_cfg->fs_hcnt; - lcnt = scl_sda_cfg->fs_lcnt; - } else { - hcnt = (bus_khz * MIN_HS_SCL_HIGHTIME) / NANO_TO_KILO; - lcnt = (bus_khz * MIN_HS_SCL_LOWTIME) / NANO_TO_KILO; + if (!scl_sda_cfg) { + config.scl_hcnt = (bus_khz * MIN_HS_SCL_HIGHTIME) / + NANO_TO_KILO; + config.scl_lcnt = (bus_khz * MIN_HS_SCL_LOWTIME) / + NANO_TO_KILO; } - writel(hcnt, &i2c_base->ic_hs_scl_hcnt); - writel(lcnt, &i2c_base->ic_hs_scl_lcnt); + writel(config.scl_hcnt, &i2c_base->ic_hs_scl_hcnt); + writel(config.scl_lcnt, &i2c_base->ic_hs_scl_lcnt); break; case IC_SPEED_MODE_STANDARD: cntl |= IC_CON_SPD_SS; - if (scl_sda_cfg) { - hcnt = scl_sda_cfg->ss_hcnt; - lcnt = scl_sda_cfg->ss_lcnt; - } else { - hcnt = (bus_khz * MIN_SS_SCL_HIGHTIME) / NANO_TO_KILO; - lcnt = (bus_khz * MIN_SS_SCL_LOWTIME) / NANO_TO_KILO; + if (!scl_sda_cfg) { + config.scl_hcnt = (bus_khz * MIN_SS_SCL_HIGHTIME) / + NANO_TO_KILO; + config.scl_lcnt = (bus_khz * MIN_SS_SCL_LOWTIME) / + NANO_TO_KILO; } - writel(hcnt, &i2c_base->ic_ss_scl_hcnt); - writel(lcnt, &i2c_base->ic_ss_scl_lcnt); + writel(config.scl_hcnt, &i2c_base->ic_ss_scl_hcnt); + writel(config.scl_lcnt, &i2c_base->ic_ss_scl_lcnt); break; case IC_SPEED_MODE_FAST: default: cntl |= IC_CON_SPD_FS; - if (scl_sda_cfg) { - hcnt = scl_sda_cfg->fs_hcnt; - lcnt = scl_sda_cfg->fs_lcnt; - } else { - hcnt = (bus_khz * MIN_FS_SCL_HIGHTIME) / NANO_TO_KILO; - lcnt = (bus_khz * MIN_FS_SCL_LOWTIME) / NANO_TO_KILO; + if (!scl_sda_cfg) { + config.scl_hcnt = (bus_khz * MIN_FS_SCL_HIGHTIME) / + NANO_TO_KILO; + config.scl_lcnt = (bus_khz * MIN_FS_SCL_LOWTIME) / + NANO_TO_KILO; } - writel(hcnt, &i2c_base->ic_fs_scl_hcnt); - writel(lcnt, &i2c_base->ic_fs_scl_lcnt); + writel(config.scl_hcnt, &i2c_base->ic_fs_scl_hcnt); + writel(config.scl_lcnt, &i2c_base->ic_fs_scl_lcnt); break; } writel(cntl, &i2c_base->ic_con); /* Configure SDA Hold Time if required */ - if (scl_sda_cfg) - writel(scl_sda_cfg->sda_hold, &i2c_base->ic_sda_hold); + if (config.sda_hold) + writel(config.sda_hold, &i2c_base->ic_sda_hold); /* Restore back i2c now speed set */ if (ena == IC_ENABLE_0B) -- cgit v1.2.3 From e71b6f6622d6a3380d866943799f36e473a3cd9b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 23 Jan 2020 11:48:14 -0700 Subject: i2c: designware_i2c: Rewrite timing calculation At present the driver can end up with timing parameters which are slightly faster than those expected. It is possible to optimise the parameters to get the best possible result. Create a new function to handle the timing calculation. This uses a table of defaults for each speed mode rather than writing it in code. The function works by calculating the 'period' of each bit on the bus in terms of the input clock to the controller (IC_CLK). It makes sure that the constraints are met and that the different components of that period add up correctly. This code was taken from coreboot which has ended up with this same driver, but now in a much-different form. Signed-off-by: Simon Glass Reviewed-by: Heiko Schocher --- drivers/i2c/designware_i2c.c | 169 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 147 insertions(+), 22 deletions(-) (limited to 'drivers/i2c/designware_i2c.c') diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index e50987a7175..00696021032 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -63,6 +63,147 @@ static int dw_i2c_enable(struct i2c_regs *i2c_base, bool enable) } #endif +/* High and low times in different speed modes (in ns) */ +enum { + /* SDA Hold Time */ + DEFAULT_SDA_HOLD_TIME = 300, +}; + +/** + * calc_counts() - Convert a period to a number of IC clk cycles + * + * @ic_clk: Input clock in Hz + * @period_ns: Period to represent, in ns + * @return calculated count + */ +static uint calc_counts(uint ic_clk, uint period_ns) +{ + return DIV_ROUND_UP(ic_clk / 1000 * period_ns, NANO_TO_KILO); +} + +/** + * struct i2c_mode_info - Information about an I2C speed mode + * + * Each speed mode has its own characteristics. This struct holds these to aid + * calculations in dw_i2c_calc_timing(). + * + * @speed: Speed in Hz + * @min_scl_lowtime_ns: Minimum value for SCL low period in ns + * @min_scl_hightime_ns: Minimum value for SCL high period in ns + * @def_rise_time_ns: Default rise time in ns + * @def_fall_time_ns: Default fall time in ns + */ +struct i2c_mode_info { + int speed; + int min_scl_hightime_ns; + int min_scl_lowtime_ns; + int def_rise_time_ns; + int def_fall_time_ns; +}; + +static const struct i2c_mode_info info_for_mode[] = { + [IC_SPEED_MODE_STANDARD] = { + I2C_STANDARD_SPEED, + MIN_SS_SCL_HIGHTIME, + MIN_SS_SCL_LOWTIME, + 1000, + 300, + }, + [IC_SPEED_MODE_FAST] = { + I2C_FAST_SPEED, + MIN_FS_SCL_HIGHTIME, + MIN_FS_SCL_LOWTIME, + 300, + 300, + }, + [IC_SPEED_MODE_HIGH] = { + I2C_HIGH_SPEED, + MIN_HS_SCL_HIGHTIME, + MIN_HS_SCL_LOWTIME, + 120, + 120, + }, +}; + +/** + * dw_i2c_calc_timing() - Calculate the timings to use for a bus + * + * @priv: Bus private information (NULL if not using driver model) + * @mode: Speed mode to use + * @ic_clk: IC clock speed in Hz + * @spk_cnt: Spike-suppression count + * @config: Returns value to use + * @return 0 if OK, -EINVAL if the calculation failed due to invalid data + */ +static int dw_i2c_calc_timing(struct dw_i2c *priv, enum i2c_speed_mode mode, + int ic_clk, int spk_cnt, + struct dw_i2c_speed_config *config) +{ + int fall_cnt, rise_cnt, min_tlow_cnt, min_thigh_cnt; + int hcnt, lcnt, period_cnt, diff, tot; + int sda_hold_time_ns, scl_rise_time_ns, scl_fall_time_ns; + const struct i2c_mode_info *info; + + /* + * Find the period, rise, fall, min tlow, and min thigh in terms of + * counts of the IC clock + */ + info = &info_for_mode[mode]; + period_cnt = ic_clk / info->speed; + scl_rise_time_ns = priv && priv->scl_rise_time_ns ? + priv->scl_rise_time_ns : info->def_rise_time_ns; + scl_fall_time_ns = priv && priv->scl_fall_time_ns ? + priv->scl_fall_time_ns : info->def_fall_time_ns; + rise_cnt = calc_counts(ic_clk, scl_rise_time_ns); + fall_cnt = calc_counts(ic_clk, scl_fall_time_ns); + min_tlow_cnt = calc_counts(ic_clk, info->min_scl_lowtime_ns); + min_thigh_cnt = calc_counts(ic_clk, info->min_scl_hightime_ns); + + debug("dw_i2c: period %d rise %d fall %d tlow %d thigh %d spk %d\n", + period_cnt, rise_cnt, fall_cnt, min_tlow_cnt, min_thigh_cnt, + spk_cnt); + + /* + * Back-solve for hcnt and lcnt according to the following equations: + * SCL_High_time = [(HCNT + IC_*_SPKLEN + 7) * 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; + lcnt = min_tlow_cnt - rise_cnt + fall_cnt - 1; + + if (hcnt < 0 || lcnt < 0) { + debug("dw_i2c: bad counts. hcnt = %d lcnt = %d\n", hcnt, lcnt); + return -EINVAL; + } + + /* + * 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; + + if (tot < period_cnt) { + diff = (period_cnt - tot) / 2; + hcnt += diff; + lcnt += diff; + tot = hcnt + lcnt + 7 + spk_cnt + rise_cnt + 1; + lcnt += period_cnt - tot; + } + + config->scl_lcnt = lcnt; + config->scl_hcnt = hcnt; + + /* Use internal default unless other value is specified */ + sda_hold_time_ns = priv && priv->sda_hold_time_ns ? + priv->sda_hold_time_ns : DEFAULT_SDA_HOLD_TIME; + config->sda_hold = calc_counts(ic_clk, sda_hold_time_ns); + + debug("dw_i2c: hcnt = %d lcnt = %d sda hold = %d\n", hcnt, lcnt, + config->sda_hold); + + return 0; +} + /* * i2c_set_bus_speed - Set the i2c speed * @speed: required i2c speed @@ -76,10 +217,10 @@ static unsigned int __dw_i2c_set_bus_speed(struct dw_i2c *priv, { const struct dw_scl_sda_cfg *scl_sda_cfg = NULL; struct dw_i2c_speed_config config; - ulong bus_khz = bus_clk / 1000; enum i2c_speed_mode i2c_spd; unsigned int cntl; unsigned int ena; + int ret; if (priv) scl_sda_cfg = priv->scl_sda_cfg; @@ -100,9 +241,6 @@ static unsigned int __dw_i2c_set_bus_speed(struct dw_i2c *priv, cntl = (readl(&i2c_base->ic_con) & (~IC_CON_SPD_MSK)); - config.scl_hcnt = 0; - config.scl_lcnt = 0; - config.sda_hold = 0; if (scl_sda_cfg) { config.sda_hold = scl_sda_cfg->sda_hold; if (i2c_spd == IC_SPEED_MODE_STANDARD) { @@ -112,29 +250,22 @@ static unsigned int __dw_i2c_set_bus_speed(struct dw_i2c *priv, config.scl_hcnt = scl_sda_cfg->fs_hcnt; config.scl_lcnt = scl_sda_cfg->fs_lcnt; } + } else { + ret = dw_i2c_calc_timing(priv, i2c_spd, bus_clk, 0, + &config); + if (ret) + return log_msg_ret("gen_confg", ret); } switch (i2c_spd) { case IC_SPEED_MODE_HIGH: cntl |= IC_CON_SPD_SS; - if (!scl_sda_cfg) { - config.scl_hcnt = (bus_khz * MIN_HS_SCL_HIGHTIME) / - NANO_TO_KILO; - config.scl_lcnt = (bus_khz * MIN_HS_SCL_LOWTIME) / - NANO_TO_KILO; - } writel(config.scl_hcnt, &i2c_base->ic_hs_scl_hcnt); writel(config.scl_lcnt, &i2c_base->ic_hs_scl_lcnt); break; case IC_SPEED_MODE_STANDARD: cntl |= IC_CON_SPD_SS; - if (!scl_sda_cfg) { - config.scl_hcnt = (bus_khz * MIN_SS_SCL_HIGHTIME) / - NANO_TO_KILO; - config.scl_lcnt = (bus_khz * MIN_SS_SCL_LOWTIME) / - NANO_TO_KILO; - } writel(config.scl_hcnt, &i2c_base->ic_ss_scl_hcnt); writel(config.scl_lcnt, &i2c_base->ic_ss_scl_lcnt); break; @@ -142,12 +273,6 @@ static unsigned int __dw_i2c_set_bus_speed(struct dw_i2c *priv, case IC_SPEED_MODE_FAST: default: cntl |= IC_CON_SPD_FS; - if (!scl_sda_cfg) { - config.scl_hcnt = (bus_khz * MIN_FS_SCL_HIGHTIME) / - NANO_TO_KILO; - config.scl_lcnt = (bus_khz * MIN_FS_SCL_LOWTIME) / - NANO_TO_KILO; - } writel(config.scl_hcnt, &i2c_base->ic_fs_scl_hcnt); writel(config.scl_lcnt, &i2c_base->ic_fs_scl_lcnt); break; -- cgit v1.2.3 From 96fe11c3dace9038e2df0e7c4625d1c3e330425f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 23 Jan 2020 11:48:15 -0700 Subject: i2c: designware_i2c: Add spike supression Some versions of this peripheral include a spike-suppression phase of the bus. Add support for this. Signed-off-by: Simon Glass Reviewed-by: Heiko Schocher --- drivers/i2c/designware_i2c.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/i2c/designware_i2c.c') diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index 00696021032..4aee25c5438 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -220,6 +220,7 @@ static unsigned int __dw_i2c_set_bus_speed(struct dw_i2c *priv, enum i2c_speed_mode i2c_spd; unsigned int cntl; unsigned int ena; + int spk_cnt; int ret; if (priv) @@ -241,6 +242,13 @@ static unsigned int __dw_i2c_set_bus_speed(struct dw_i2c *priv, cntl = (readl(&i2c_base->ic_con) & (~IC_CON_SPD_MSK)); + /* Get the proper spike-suppression count based on target speed */ + if (!priv || !priv->has_spk_cnt) + spk_cnt = 0; + else if (i2c_spd >= IC_SPEED_MODE_HIGH) + spk_cnt = readl(&i2c_base->hs_spklen); + else + spk_cnt = readl(&i2c_base->fs_spklen); if (scl_sda_cfg) { config.sda_hold = scl_sda_cfg->sda_hold; if (i2c_spd == IC_SPEED_MODE_STANDARD) { @@ -251,7 +259,7 @@ static unsigned int __dw_i2c_set_bus_speed(struct dw_i2c *priv, config.scl_lcnt = scl_sda_cfg->fs_lcnt; } } else { - ret = dw_i2c_calc_timing(priv, i2c_spd, bus_clk, 0, + ret = dw_i2c_calc_timing(priv, i2c_spd, bus_clk, spk_cnt, &config); if (ret) return log_msg_ret("gen_confg", ret); -- cgit v1.2.3 From 54290c666e12f9cad12f368980071f285e1ada24 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 23 Jan 2020 11:48:18 -0700 Subject: i2c: designware_i2c: Update to use standard enums for speed Update this driver to use the new standard enums for speed. Signed-off-by: Simon Glass Reviewed-by: Heiko Schocher --- drivers/i2c/designware_i2c.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/i2c/designware_i2c.c') diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index 4aee25c5438..1f41e3eae06 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -103,21 +103,21 @@ struct i2c_mode_info { static const struct i2c_mode_info info_for_mode[] = { [IC_SPEED_MODE_STANDARD] = { - I2C_STANDARD_SPEED, + I2C_SPEED_STANDARD_RATE, MIN_SS_SCL_HIGHTIME, MIN_SS_SCL_LOWTIME, 1000, 300, }, [IC_SPEED_MODE_FAST] = { - I2C_FAST_SPEED, + I2C_SPEED_FAST_RATE, MIN_FS_SCL_HIGHTIME, MIN_FS_SCL_LOWTIME, 300, 300, }, [IC_SPEED_MODE_HIGH] = { - I2C_HIGH_SPEED, + I2C_SPEED_HIGH_RATE, MIN_HS_SCL_HIGHTIME, MIN_HS_SCL_LOWTIME, 120, @@ -226,10 +226,10 @@ static unsigned int __dw_i2c_set_bus_speed(struct dw_i2c *priv, if (priv) scl_sda_cfg = priv->scl_sda_cfg; /* Allow high speed if there is no config, or the config allows it */ - if (speed >= I2C_HIGH_SPEED && + if (speed >= I2C_SPEED_HIGH_RATE && (!scl_sda_cfg || scl_sda_cfg->has_high_speed)) i2c_spd = IC_SPEED_MODE_HIGH; - else if (speed >= I2C_FAST_SPEED) + else if (speed >= I2C_SPEED_FAST_RATE) i2c_spd = IC_SPEED_MODE_FAST; else i2c_spd = IC_SPEED_MODE_STANDARD; -- cgit v1.2.3 From d96440d1e36e080ca1229dab65bc417640b58df5 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 23 Jan 2020 11:48:23 -0700 Subject: i2c: designware_i2c: Add support for fast-plus speed Fast-plus runs at 1MHz and is used by some devices. Add support for this. Signed-off-by: Simon Glass --- drivers/i2c/designware_i2c.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/i2c/designware_i2c.c') diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index 1f41e3eae06..d9e0d81ff05 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -116,6 +116,13 @@ static const struct i2c_mode_info info_for_mode[] = { 300, 300, }, + [IC_SPEED_MODE_FAST_PLUS] = { + I2C_SPEED_FAST_PLUS_RATE, + MIN_FP_SCL_HIGHTIME, + MIN_FP_SCL_LOWTIME, + 260, + 500, + }, [IC_SPEED_MODE_HIGH] = { I2C_SPEED_HIGH_RATE, MIN_HS_SCL_HIGHTIME, @@ -230,6 +237,8 @@ static unsigned int __dw_i2c_set_bus_speed(struct dw_i2c *priv, (!scl_sda_cfg || scl_sda_cfg->has_high_speed)) i2c_spd = IC_SPEED_MODE_HIGH; else if (speed >= I2C_SPEED_FAST_RATE) + i2c_spd = IC_SPEED_MODE_FAST_PLUS; + else if (speed >= I2C_SPEED_FAST_PLUS_RATE) i2c_spd = IC_SPEED_MODE_FAST; else i2c_spd = IC_SPEED_MODE_STANDARD; @@ -271,13 +280,12 @@ static unsigned int __dw_i2c_set_bus_speed(struct dw_i2c *priv, writel(config.scl_hcnt, &i2c_base->ic_hs_scl_hcnt); writel(config.scl_lcnt, &i2c_base->ic_hs_scl_lcnt); break; - case IC_SPEED_MODE_STANDARD: cntl |= IC_CON_SPD_SS; writel(config.scl_hcnt, &i2c_base->ic_ss_scl_hcnt); writel(config.scl_lcnt, &i2c_base->ic_ss_scl_lcnt); break; - + case IC_SPEED_MODE_FAST_PLUS: case IC_SPEED_MODE_FAST: default: cntl |= IC_CON_SPD_FS; -- cgit v1.2.3 From a8d2b515f98298dc1e30d7e3ae148eeca49a97b2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 23 Jan 2020 11:48:24 -0700 Subject: i2c: designware_i2c: Move dw_i2c_speed_config to header This is used to store the speed information for a bus. We want to provide this to ACPI so that it can tell the kernel. Move this struct to the header file so it can be accessed by the ACPI i2c implementation being added later. Signed-off-by: Simon Glass --- drivers/i2c/designware_i2c.c | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'drivers/i2c/designware_i2c.c') diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index d9e0d81ff05..6be98ee43b4 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -13,23 +13,6 @@ #include #include "designware_i2c.h" -/** - * struct dw_i2c_speed_config - timings to use for a particular speed - * - * This holds calculated values to be written to the I2C controller. Each value - * is represented as a number of IC clock cycles. - * - * @scl_lcnt: Low count value for SCL - * @scl_hcnt: High count value for SCL - * @sda_hold: Data hold count - */ -struct dw_i2c_speed_config { - /* SCL high and low period count */ - uint scl_lcnt; - uint scl_hcnt; - uint sda_hold; -}; - #ifdef CONFIG_SYS_I2C_DW_ENABLE_STATUS_UNSUPPORTED static int dw_i2c_enable(struct i2c_regs *i2c_base, bool enable) { -- cgit v1.2.3 From 23ad52ebb1e5b322e22c5c6b9d1a06b36fdc97c0 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 23 Jan 2020 11:48:25 -0700 Subject: i2c: designware_i2c: Separate out the speed calculation We want to be able to calculate the speed separately from actually setting the speed, so we can generate the required ACPI tables. Split out the calculation into its own function. Drop the double underscore on __dw_i2c_set_bus_speed while we are here. That is reserved for compiler internals. Signed-off-by: Simon Glass --- drivers/i2c/designware_i2c.c | 78 +++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 33 deletions(-) (limited to 'drivers/i2c/designware_i2c.c') diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index 6be98ee43b4..39af25af9a5 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -194,22 +194,12 @@ static int dw_i2c_calc_timing(struct dw_i2c *priv, enum i2c_speed_mode mode, return 0; } -/* - * i2c_set_bus_speed - Set the i2c speed - * @speed: required i2c speed - * - * Set the i2c speed. - */ -static unsigned int __dw_i2c_set_bus_speed(struct dw_i2c *priv, - struct i2c_regs *i2c_base, - unsigned int speed, - unsigned int bus_clk) +static int calc_bus_speed(struct dw_i2c *priv, int speed, ulong bus_clk, + struct dw_i2c_speed_config *config) { const struct dw_scl_sda_cfg *scl_sda_cfg = NULL; - struct dw_i2c_speed_config config; + struct i2c_regs *regs = priv->regs; enum i2c_speed_mode i2c_spd; - unsigned int cntl; - unsigned int ena; int spk_cnt; int ret; @@ -226,38 +216,60 @@ static unsigned int __dw_i2c_set_bus_speed(struct dw_i2c *priv, else i2c_spd = IC_SPEED_MODE_STANDARD; - /* Get enable setting for restore later */ - ena = readl(&i2c_base->ic_enable) & IC_ENABLE_0B; - - /* to set speed cltr must be disabled */ - dw_i2c_enable(i2c_base, false); - - cntl = (readl(&i2c_base->ic_con) & (~IC_CON_SPD_MSK)); - /* Get the proper spike-suppression count based on target speed */ if (!priv || !priv->has_spk_cnt) spk_cnt = 0; else if (i2c_spd >= IC_SPEED_MODE_HIGH) - spk_cnt = readl(&i2c_base->hs_spklen); + spk_cnt = readl(®s->hs_spklen); else - spk_cnt = readl(&i2c_base->fs_spklen); + spk_cnt = readl(®s->fs_spklen); if (scl_sda_cfg) { - config.sda_hold = scl_sda_cfg->sda_hold; + config->sda_hold = scl_sda_cfg->sda_hold; if (i2c_spd == IC_SPEED_MODE_STANDARD) { - config.scl_hcnt = scl_sda_cfg->ss_hcnt; - config.scl_lcnt = scl_sda_cfg->ss_lcnt; + config->scl_hcnt = scl_sda_cfg->ss_hcnt; + config->scl_lcnt = scl_sda_cfg->ss_lcnt; } else { - config.scl_hcnt = scl_sda_cfg->fs_hcnt; - config.scl_lcnt = scl_sda_cfg->fs_lcnt; + config->scl_hcnt = scl_sda_cfg->fs_hcnt; + config->scl_lcnt = scl_sda_cfg->fs_lcnt; } } else { ret = dw_i2c_calc_timing(priv, i2c_spd, bus_clk, spk_cnt, - &config); + config); if (ret) return log_msg_ret("gen_confg", ret); } + config->speed_mode = i2c_spd; + + return 0; +} + +/* + * _dw_i2c_set_bus_speed - Set the i2c speed + * @speed: required i2c speed + * + * Set the i2c speed. + */ +static int _dw_i2c_set_bus_speed(struct dw_i2c *priv, struct i2c_regs *i2c_base, + unsigned int speed, unsigned int bus_clk) +{ + struct dw_i2c_speed_config config; + unsigned int cntl; + unsigned int ena; + int ret; + + ret = calc_bus_speed(priv, speed, bus_clk, &config); + if (ret) + return ret; + + /* Get enable setting for restore later */ + ena = readl(&i2c_base->ic_enable) & IC_ENABLE_0B; + + /* to set speed cltr must be disabled */ + dw_i2c_enable(i2c_base, false); + + cntl = (readl(&i2c_base->ic_con) & (~IC_CON_SPD_MSK)); - switch (i2c_spd) { + switch (config.speed_mode) { case IC_SPEED_MODE_HIGH: cntl |= IC_CON_SPD_SS; writel(config.scl_hcnt, &i2c_base->ic_hs_scl_hcnt); @@ -526,7 +538,7 @@ static int __dw_i2c_init(struct i2c_regs *i2c_base, int speed, int slaveaddr) writel(IC_TX_TL, &i2c_base->ic_tx_tl); writel(IC_STOP_DET, &i2c_base->ic_intr_mask); #ifndef CONFIG_DM_I2C - __dw_i2c_set_bus_speed(NULL, i2c_base, speed, IC_CLK); + _dw_i2c_set_bus_speed(NULL, i2c_base, speed, IC_CLK); writel(slaveaddr, &i2c_base->ic_sar); #endif @@ -571,7 +583,7 @@ static unsigned int dw_i2c_set_bus_speed(struct i2c_adapter *adap, unsigned int speed) { adap->speed = speed; - return __dw_i2c_set_bus_speed(NULL, i2c_get_base(adap), speed, IC_CLK); + return _dw_i2c_set_bus_speed(NULL, i2c_get_base(adap), speed, IC_CLK); } static void dw_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) @@ -670,7 +682,7 @@ static int designware_i2c_set_bus_speed(struct udevice *bus, unsigned int speed) #else rate = IC_CLK; #endif - return __dw_i2c_set_bus_speed(i2c, i2c->regs, speed, rate); + return _dw_i2c_set_bus_speed(i2c, i2c->regs, speed, rate); } static int designware_i2c_probe_chip(struct udevice *bus, uint chip_addr, -- cgit v1.2.3 From 2034f6c27fc91407fe8bbd36670714b77d9ef1dc Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 23 Jan 2020 11:48:26 -0700 Subject: i2c: designware_i2c: Do more in the probe() method Move some of the code currently in the ofdata_to_platdata() method to probe() so that it is not executed when generating ACPI tables. Signed-off-by: Simon Glass --- drivers/i2c/designware_i2c.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/i2c/designware_i2c.c') diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index 39af25af9a5..c8c5d2c3310 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -704,6 +704,7 @@ static int designware_i2c_probe_chip(struct udevice *bus, uint chip_addr, int designware_i2c_ofdata_to_platdata(struct udevice *bus) { struct dw_i2c *priv = dev_get_priv(bus); + int ret; if (!priv->regs) priv->regs = (struct i2c_regs *)devfdt_get_addr_ptr(bus); @@ -711,14 +712,6 @@ int designware_i2c_ofdata_to_platdata(struct udevice *bus) dev_read_u32(bus, "i2c-scl-falling-time-ns", &priv->scl_fall_time_ns); dev_read_u32(bus, "i2c-sda-hold-time-ns", &priv->sda_hold_time_ns); - return 0; -} - -int designware_i2c_probe(struct udevice *bus) -{ - struct dw_i2c *priv = dev_get_priv(bus); - int ret; - ret = reset_get_bulk(bus, &priv->resets); if (ret) dev_warn(bus, "Can't get reset: %d\n", ret); @@ -738,6 +731,13 @@ int designware_i2c_probe(struct udevice *bus) } #endif + return 0; +} + +int designware_i2c_probe(struct udevice *bus) +{ + struct dw_i2c *priv = dev_get_priv(bus); + return __dw_i2c_init(priv->regs, 0, 0); } -- cgit v1.2.3