[PATCH] i2c: stm32f7_i2c: allows for any bus frequency

Patrick Delaunay patrick.delaunay at st.com
Fri Mar 6 11:09:14 CET 2020


From: Alain Volmat <alain.volmat at st.com>

Do not limit to 3 (100KHz, 400KHz, 1MHz) bus frequencies, but
instead allow for any frequency. Depending on the requested
frequency (via the clock-frequency DT entry), use the spec
data from either Standard, Fast or Fast Plus mode.

In order to do so, the driver do not use anymore spec identifier
by directly handle the requested frequency and from it retrieve
the corresponding spec data to be used for the computation
of the timing register.

Signed-off-by: Alain Volmat <alain.volmat at st.com>
Reviewed-by: Patrick DELAUNAY <patrick.delaunay at st.com>
Signed-off-by: Patrick Delaunay <patrick.delaunay at st.com>
---

 drivers/i2c/stm32f7_i2c.c | 105 +++++++++++++++++++++-----------------
 1 file changed, 59 insertions(+), 46 deletions(-)

diff --git a/drivers/i2c/stm32f7_i2c.c b/drivers/i2c/stm32f7_i2c.c
index 7d046c1a1e..fc5c1221e1 100644
--- a/drivers/i2c/stm32f7_i2c.c
+++ b/drivers/i2c/stm32f7_i2c.c
@@ -7,10 +7,10 @@
 #include <clk.h>
 #include <dm.h>
 #include <i2c.h>
-#include <malloc.h>
 #include <reset.h>
 
 #include <dm/device.h>
+#include <linux/err.h>
 #include <linux/io.h>
 
 /* STM32 I2C registers */
@@ -145,7 +145,6 @@ struct stm32_i2c_spec {
 
 /**
  * struct stm32_i2c_setup - private I2C timing setup parameters
- * @speed: I2C speed mode (standard, Fast Plus)
  * @speed_freq: I2C speed frequency  (Hz)
  * @clock_src: I2C clock source frequency (Hz)
  * @rise_time: Rise time (ns)
@@ -154,7 +153,6 @@ struct stm32_i2c_spec {
  * @analog_filter: Analog filter delay (On/Off)
  */
 struct stm32_i2c_setup {
-	enum i2c_speed_mode speed;
 	u32 speed_freq;
 	u32 clock_src;
 	u32 rise_time;
@@ -184,10 +182,11 @@ struct stm32_i2c_priv {
 	struct stm32_i2c_regs *regs;
 	struct clk clk;
 	struct stm32_i2c_setup *setup;
-	int speed;
+	u32 speed;
 };
 
 static const struct stm32_i2c_spec i2c_specs[] = {
+	/* Standard speed - 100 KHz */
 	[IC_SPEED_MODE_STANDARD] = {
 		.rate = I2C_SPEED_STANDARD_RATE,
 		.rate_min = 8000,
@@ -200,6 +199,7 @@ static const struct stm32_i2c_spec i2c_specs[] = {
 		.l_min = 4700,
 		.h_min = 4000,
 	},
+	/* Fast speed - 400 KHz */
 	[IC_SPEED_MODE_FAST] = {
 		.rate = I2C_SPEED_FAST_RATE,
 		.rate_min = 320000,
@@ -212,6 +212,7 @@ static const struct stm32_i2c_spec i2c_specs[] = {
 		.l_min = 1300,
 		.h_min = 600,
 	},
+	/* Fast Plus Speed - 1 MHz */
 	[IC_SPEED_MODE_FAST_PLUS] = {
 		.rate = I2C_SPEED_FAST_PLUS_RATE,
 		.rate_min = 800000,
@@ -474,6 +475,7 @@ static int stm32_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
 }
 
 static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup,
+				       const struct stm32_i2c_spec *specs,
 				       struct list_head *solutions)
 {
 	struct stm32_i2c_timings *v;
@@ -490,13 +492,13 @@ static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup,
 	af_delay_max = setup->analog_filter ?
 		       STM32_I2C_ANALOG_FILTER_DELAY_MAX : 0;
 
-	sdadel_min = i2c_specs[setup->speed].hddat_min + setup->fall_time -
+	sdadel_min = specs->hddat_min + setup->fall_time -
 		     af_delay_min - (setup->dnf + 3) * i2cclk;
 
-	sdadel_max = i2c_specs[setup->speed].vddat_max - setup->rise_time -
+	sdadel_max = specs->vddat_max - setup->rise_time -
 		     af_delay_max - (setup->dnf + 4) * i2cclk;
 
-	scldel_min = setup->rise_time + i2c_specs[setup->speed].sudat_min;
+	scldel_min = setup->rise_time + specs->sudat_min;
 
 	if (sdadel_min < 0)
 		sdadel_min = 0;
@@ -548,6 +550,7 @@ static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup,
 }
 
 static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup,
+				     const struct stm32_i2c_spec *specs,
 				     struct list_head *solutions,
 				     struct stm32_i2c_timings *s)
 {
@@ -570,8 +573,8 @@ static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup,
 	dnf_delay = setup->dnf * i2cclk;
 
 	tsync = af_delay_min + dnf_delay + (2 * i2cclk);
-	clk_max = STM32_NSEC_PER_SEC / i2c_specs[setup->speed].rate_min;
-	clk_min = STM32_NSEC_PER_SEC / i2c_specs[setup->speed].rate_max;
+	clk_max = STM32_NSEC_PER_SEC / specs->rate_min;
+	clk_min = STM32_NSEC_PER_SEC / specs->rate_max;
 
 	/*
 	 * Among Prescaler possibilities discovered above figures out SCL Low
@@ -589,7 +592,7 @@ static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup,
 		for (l = 0; l < STM32_SCLL_MAX; l++) {
 			u32 tscl_l = (l + 1) * prescaler + tsync;
 
-			if ((tscl_l < i2c_specs[setup->speed].l_min) ||
+			if (tscl_l < specs->l_min ||
 			    (i2cclk >=
 			     ((tscl_l - af_delay_min - dnf_delay) / 4))) {
 				continue;
@@ -601,7 +604,7 @@ static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup,
 					   setup->rise_time + setup->fall_time;
 
 				if ((tscl >= clk_min) && (tscl <= clk_max) &&
-				    (tscl_h >= i2c_specs[setup->speed].h_min) &&
+				    (tscl_h >= specs->h_min) &&
 				    (i2cclk < tscl_h)) {
 					u32 clk_error;
 
@@ -630,26 +633,40 @@ static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup,
 	return ret;
 }
 
+static const struct stm32_i2c_spec *get_specs(u32 rate)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(i2c_specs); i++)
+		if (rate <= i2c_specs[i].rate)
+			return &i2c_specs[i];
+
+	/* NOT REACHED */
+	return ERR_PTR(-EINVAL);
+}
+
 static int stm32_i2c_compute_timing(struct stm32_i2c_priv *i2c_priv,
 				    struct stm32_i2c_setup *setup,
 				    struct stm32_i2c_timings *output)
 {
+	const struct stm32_i2c_spec *specs;
 	struct stm32_i2c_timings *v, *_v;
 	struct list_head solutions;
 	int ret;
 
-	if (setup->speed >= ARRAY_SIZE(i2c_specs)) {
-		pr_err("%s: speed out of bound {%d/%d}\n", __func__,
-		       setup->speed, ARRAY_SIZE(i2c_specs) - 1);
+	specs = get_specs(setup->speed_freq);
+	if (specs == ERR_PTR(-EINVAL)) {
+		pr_err("%s: speed out of bound {%d}\n", __func__,
+		       setup->speed_freq);
 		return -EINVAL;
 	}
 
-	if ((setup->rise_time > i2c_specs[setup->speed].rise_max) ||
-	    (setup->fall_time > i2c_specs[setup->speed].fall_max)) {
+	if (setup->rise_time > specs->rise_max ||
+	    setup->fall_time > specs->fall_max) {
 		pr_err("%s :timings out of bound Rise{%d>%d}/Fall{%d>%d}\n",
 		       __func__,
-		       setup->rise_time, i2c_specs[setup->speed].rise_max,
-		       setup->fall_time, i2c_specs[setup->speed].fall_max);
+		       setup->rise_time, specs->rise_max,
+		       setup->fall_time, specs->fall_max);
 		return -EINVAL;
 	}
 
@@ -659,18 +676,12 @@ static int stm32_i2c_compute_timing(struct stm32_i2c_priv *i2c_priv,
 		return -EINVAL;
 	}
 
-	if (setup->speed_freq > i2c_specs[setup->speed].rate) {
-		pr_err("%s: Freq {%d/%d}\n", __func__,
-		       setup->speed_freq, i2c_specs[setup->speed].rate);
-		return -EINVAL;
-	}
-
 	INIT_LIST_HEAD(&solutions);
-	ret = stm32_i2c_compute_solutions(setup, &solutions);
+	ret = stm32_i2c_compute_solutions(setup, specs, &solutions);
 	if (ret)
 		goto exit;
 
-	ret = stm32_i2c_choose_solution(setup, &solutions, output);
+	ret = stm32_i2c_choose_solution(setup, specs, &solutions, output);
 	if (ret)
 		goto exit;
 
@@ -689,14 +700,24 @@ exit:
 	return ret;
 }
 
+static u32 get_lower_rate(u32 rate)
+{
+	int i;
+
+	for (i = ARRAY_SIZE(i2c_specs) - 1; i >= 0; i--)
+		if (rate > i2c_specs[i].rate)
+			return i2c_specs[i].rate;
+
+	return i2c_specs[0].rate;
+}
+
 static int stm32_i2c_setup_timing(struct stm32_i2c_priv *i2c_priv,
 				  struct stm32_i2c_timings *timing)
 {
 	struct stm32_i2c_setup *setup = i2c_priv->setup;
 	int ret = 0;
 
-	setup->speed = i2c_priv->speed;
-	setup->speed_freq = i2c_specs[setup->speed].rate;
+	setup->speed_freq = i2c_priv->speed;
 	setup->clock_src = clk_get_rate(&i2c_priv->clk);
 
 	if (!setup->clock_src) {
@@ -709,13 +730,11 @@ static int stm32_i2c_setup_timing(struct stm32_i2c_priv *i2c_priv,
 		if (ret) {
 			debug("%s: failed to compute I2C timings.\n",
 			      __func__);
-			if (i2c_priv->speed > IC_SPEED_MODE_STANDARD) {
-				i2c_priv->speed--;
-				setup->speed = i2c_priv->speed;
+			if (setup->speed_freq > I2C_SPEED_STANDARD_RATE) {
 				setup->speed_freq =
-					i2c_specs[setup->speed].rate;
+					get_lower_rate(setup->speed_freq);
 				debug("%s: downgrade I2C Speed Freq to (%i)\n",
-				      __func__, i2c_specs[setup->speed].rate);
+				      __func__, setup->speed_freq);
 			} else {
 				break;
 			}
@@ -727,13 +746,15 @@ static int stm32_i2c_setup_timing(struct stm32_i2c_priv *i2c_priv,
 		return ret;
 	}
 
-	debug("%s: I2C Speed(%i), Freq(%i), Clk Source(%i)\n", __func__,
-	      setup->speed, setup->speed_freq, setup->clock_src);
+	debug("%s: I2C Freq(%i), Clk Source(%i)\n", __func__,
+	      setup->speed_freq, setup->clock_src);
 	debug("%s: I2C Rise(%i) and Fall(%i) Time\n", __func__,
 	      setup->rise_time, setup->fall_time);
 	debug("%s: I2C Analog Filter(%s), DNF(%i)\n", __func__,
 	      setup->analog_filter ? "On" : "Off", setup->dnf);
 
+	i2c_priv->speed = setup->speed_freq;
+
 	return 0;
 }
 
@@ -773,21 +794,13 @@ static int stm32_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
 {
 	struct stm32_i2c_priv *i2c_priv = dev_get_priv(bus);
 
-	switch (speed) {
-	case I2C_SPEED_STANDARD_RATE:
-		i2c_priv->speed = IC_SPEED_MODE_STANDARD;
-		break;
-	case I2C_SPEED_FAST_RATE:
-		i2c_priv->speed = IC_SPEED_MODE_FAST;
-		break;
-	case I2C_SPEED_FAST_PLUS_RATE:
-		i2c_priv->speed = IC_SPEED_MODE_FAST_PLUS;
-		break;
-	default:
+	if (speed > I2C_SPEED_FAST_PLUS_RATE) {
 		debug("%s: Speed %d not supported\n", __func__, speed);
 		return -EINVAL;
 	}
 
+	i2c_priv->speed = speed;
+
 	return stm32_i2c_hw_config(i2c_priv);
 }
 
-- 
2.17.1



More information about the U-Boot mailing list