From 7ce87dcff4e87b2d067f911beee23ad221f3b467 Mon Sep 17 00:00:00 2001 From: Patrick Delaunay Date: Mon, 6 Jul 2020 13:31:35 +0200 Subject: [PATCH] i2c: stm32f7: SYSCFG Fast Mode Plus support for I2C STM32F7 Read SYSCFG bindings to set Fast Mode Plus bits if Fast Mode Plus speed is selected. Handle the stm32mp15 specific compatible to handle FastMode+ registers handling which is different on the stm32mp15 compared to the stm32f7 or stm32h7. Indeed, on the stm32mp15, the FastMode+ set and clear registers are separated while on the other platforms (F7 or H7) the control is done in a unique register. Signed-off-by: Patrick Delaunay Reviewed-by: Heiko Schocher --- drivers/i2c/stm32f7_i2c.c | 74 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/stm32f7_i2c.c b/drivers/i2c/stm32f7_i2c.c index 593f713d6b..2f60911549 100644 --- a/drivers/i2c/stm32f7_i2c.c +++ b/drivers/i2c/stm32f7_i2c.c @@ -8,7 +8,9 @@ #include #include #include +#include #include +#include #include #include @@ -154,6 +156,7 @@ struct stm32_i2c_spec { * @fall_time: Fall time (ns) * @dnf: Digital filter coefficient (0-16) * @analog_filter: Analog filter delay (On/Off) + * @fmp_clr_offset: Fast Mode Plus clear register offset from set register */ struct stm32_i2c_setup { u32 speed_freq; @@ -162,6 +165,7 @@ struct stm32_i2c_setup { u32 fall_time; u8 dnf; bool analog_filter; + u32 fmp_clr_offset; }; /** @@ -181,11 +185,26 @@ struct stm32_i2c_timings { u8 scll; }; +/** + * struct stm32_i2c_priv - private data of the controller + * @regs: I2C registers address + * @clk: hw i2c clock + * @setup: I2C timing setup parameters + * @speed: I2C clock frequency of the controller. Standard, Fast or Fast+ + * @regmap: holds SYSCFG phandle for Fast Mode Plus bit + * @regmap_sreg: register address for setting Fast Mode Plus bits + * @regmap_creg: register address for clearing Fast Mode Plus bits + * @regmap_mask: mask for Fast Mode Plus bits + */ struct stm32_i2c_priv { struct stm32_i2c_regs *regs; struct clk clk; struct stm32_i2c_setup *setup; u32 speed; + struct regmap *regmap; + u32 regmap_sreg; + u32 regmap_creg; + u32 regmap_mask; }; static const struct stm32_i2c_spec i2c_specs[] = { @@ -237,6 +256,14 @@ static const struct stm32_i2c_setup stm32f7_setup = { .analog_filter = STM32_I2C_ANALOG_FILTER_ENABLE, }; +static const struct stm32_i2c_setup stm32mp15_setup = { + .rise_time = STM32_I2C_RISE_TIME_DEFAULT, + .fall_time = STM32_I2C_FALL_TIME_DEFAULT, + .dnf = STM32_I2C_DNF_DEFAULT, + .analog_filter = STM32_I2C_ANALOG_FILTER_ENABLE, + .fmp_clr_offset = 0x40, +}; + static int stm32_i2c_check_device_busy(struct stm32_i2c_priv *i2c_priv) { struct stm32_i2c_regs *regs = i2c_priv->regs; @@ -761,6 +788,29 @@ static int stm32_i2c_setup_timing(struct stm32_i2c_priv *i2c_priv, return 0; } +static int stm32_i2c_write_fm_plus_bits(struct stm32_i2c_priv *i2c_priv) +{ + int ret; + bool enable = i2c_priv->speed > I2C_SPEED_FAST_RATE; + + /* Optional */ + if (IS_ERR_OR_NULL(i2c_priv->regmap)) + return 0; + + if (i2c_priv->regmap_sreg == i2c_priv->regmap_creg) + ret = regmap_update_bits(i2c_priv->regmap, + i2c_priv->regmap_sreg, + i2c_priv->regmap_mask, + enable ? i2c_priv->regmap_mask : 0); + else + ret = regmap_write(i2c_priv->regmap, + enable ? i2c_priv->regmap_sreg : + i2c_priv->regmap_creg, + i2c_priv->regmap_mask); + + return ret; +} + static int stm32_i2c_hw_config(struct stm32_i2c_priv *i2c_priv) { struct stm32_i2c_regs *regs = i2c_priv->regs; @@ -775,6 +825,11 @@ static int stm32_i2c_hw_config(struct stm32_i2c_priv *i2c_priv) /* Disable I2C */ clrbits_le32(®s->cr1, STM32_I2C_CR1_PE); + /* Setup Fast mode plus if necessary */ + ret = stm32_i2c_write_fm_plus_bits(i2c_priv); + if (ret) + return ret; + /* Timing settings */ timing |= STM32_I2C_TIMINGR_PRESC(t.presc); timing |= STM32_I2C_TIMINGR_SCLDEL(t.scldel); @@ -850,6 +905,7 @@ static int stm32_ofdata_to_platdata(struct udevice *dev) { struct stm32_i2c_priv *i2c_priv = dev_get_priv(dev); u32 rise_time, fall_time; + int ret; i2c_priv->setup = (struct stm32_i2c_setup *)dev_get_driver_data(dev); if (!i2c_priv->setup) @@ -863,6 +919,22 @@ static int stm32_ofdata_to_platdata(struct udevice *dev) if (fall_time) i2c_priv->setup->fall_time = fall_time; + /* Optional */ + i2c_priv->regmap = syscon_regmap_lookup_by_phandle(dev, + "st,syscfg-fmp"); + if (!IS_ERR(i2c_priv->regmap)) { + u32 fmp[3]; + + ret = dev_read_u32_array(dev, "st,syscfg-fmp", fmp, 3); + if (ret) + return ret; + + i2c_priv->regmap_sreg = fmp[1]; + i2c_priv->regmap_creg = fmp[1] + + i2c_priv->setup->fmp_clr_offset; + i2c_priv->regmap_mask = fmp[2]; + } + return 0; } @@ -873,7 +945,7 @@ static const struct dm_i2c_ops stm32_i2c_ops = { static const struct udevice_id stm32_i2c_of_match[] = { { .compatible = "st,stm32f7-i2c", .data = (ulong)&stm32f7_setup }, - { .compatible = "st,stm32mp15-i2c", .data = (ulong)&stm32f7_setup }, + { .compatible = "st,stm32mp15-i2c", .data = (ulong)&stm32mp15_setup }, {} }; -- 2.39.5