linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2] i2c: aspeed: fix invalid clock parameters for very large divisors
@ 2018-09-21 22:10 Brendan Higgins
  2018-09-21 22:26 ` Jae Hyun Yoo
  0 siblings, 1 reply; 4+ messages in thread
From: Brendan Higgins @ 2018-09-21 22:10 UTC (permalink / raw)
  To: jae.hyun.yoo, benh, joel, andrew
  Cc: linux-i2c, openbmc, linux-aspeed, linux-kernel, Brendan Higgins

The function that computes clock parameters from divisors did not
respect the maximum size of the bitfields that the parameters were
written to. This fixes the bug.

This bug can be reproduced with (and this fix verified with) the test
at: https://kunit-review.googlesource.com/c/linux/+/1035/

Discovered-by-KUnit: https://kunit-review.googlesource.com/c/linux/+/1035/
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Reviewed-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
 - v2 updates the title of the patch, renames a local variable, and
   prints an error when the clock divider is clamped.
---
 drivers/i2c/busses/i2c-aspeed.c | 62 +++++++++++++++++++++++----------
 1 file changed, 43 insertions(+), 19 deletions(-)

diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
index c258c4d9a4c0..4e946cbb7270 100644
--- a/drivers/i2c/busses/i2c-aspeed.c
+++ b/drivers/i2c/busses/i2c-aspeed.c
@@ -142,7 +142,8 @@ struct aspeed_i2c_bus {
 	/* Synchronizes I/O mem access to base. */
 	spinlock_t			lock;
 	struct completion		cmd_complete;
-	u32				(*get_clk_reg_val)(u32 divisor);
+	u32				(*get_clk_reg_val)(struct device *dev,
+							   u32 divisor);
 	unsigned long			parent_clk_frequency;
 	u32				bus_frequency;
 	/* Transaction state. */
@@ -705,16 +706,27 @@ static const struct i2c_algorithm aspeed_i2c_algo = {
 #endif /* CONFIG_I2C_SLAVE */
 };
 
-static u32 aspeed_i2c_get_clk_reg_val(u32 clk_high_low_max, u32 divisor)
+static u32 aspeed_i2c_get_clk_reg_val(struct device *dev,
+				      u32 clk_high_low_mask,
+				      u32 divisor)
 {
-	u32 base_clk, clk_high, clk_low, tmp;
+	u32 base_clk_divisor, clk_high_low_max, clk_high, clk_low, tmp;
+
+	/*
+	 * SCL_high and SCL_low represent a value 1 greater than what is stored
+	 * since a zero divider is meaningless. Thus, the max value each can
+	 * store is every bit set + 1. Since SCL_high and SCL_low are added
+	 * together (see below), the max value of both is the max value of one
+	 * them times two.
+	 */
+	clk_high_low_max = (clk_high_low_mask + 1) * 2;
 
 	/*
 	 * The actual clock frequency of SCL is:
 	 *	SCL_freq = APB_freq / (base_freq * (SCL_high + SCL_low))
 	 *		 = APB_freq / divisor
 	 * where base_freq is a programmable clock divider; its value is
-	 *	base_freq = 1 << base_clk
+	 *	base_freq = 1 << base_clk_divisor
 	 * SCL_high is the number of base_freq clock cycles that SCL stays high
 	 * and SCL_low is the number of base_freq clock cycles that SCL stays
 	 * low for a period of SCL.
@@ -724,47 +736,59 @@ static u32 aspeed_i2c_get_clk_reg_val(u32 clk_high_low_max, u32 divisor)
 	 *	SCL_low	 = clk_low + 1
 	 * Thus,
 	 *	SCL_freq = APB_freq /
-	 *		((1 << base_clk) * (clk_high + 1 + clk_low + 1))
+	 *		((1 << base_clk_divisor) * (clk_high + 1 + clk_low + 1))
 	 * The documentation recommends clk_high >= clk_high_max / 2 and
 	 * clk_low >= clk_low_max / 2 - 1 when possible; this last constraint
 	 * gives us the following solution:
 	 */
-	base_clk = divisor > clk_high_low_max ?
+	base_clk_divisor = divisor > clk_high_low_max ?
 			ilog2((divisor - 1) / clk_high_low_max) + 1 : 0;
-	tmp = (divisor + (1 << base_clk) - 1) >> base_clk;
-	clk_low = tmp / 2;
-	clk_high = tmp - clk_low;
 
-	if (clk_high)
-		clk_high--;
+	if (base_clk_divisor > ASPEED_I2CD_TIME_BASE_DIVISOR_MASK) {
+		base_clk_divisor = ASPEED_I2CD_TIME_BASE_DIVISOR_MASK;
+		clk_low = clk_high_low_mask;
+		clk_high = clk_high_low_mask;
+		dev_err(dev,
+			"clamping clock divider: divider requested, %u, is greater than largest possible divider, %u.",
+			divisor, (1 << base_clk_divisor) * clk_high_low_max);
+	} else {
+		tmp = (divisor + (1 << base_clk_divisor) - 1)
+				>> base_clk_divisor;
+		clk_low = tmp / 2;
+		clk_high = tmp - clk_low;
+
+		if (clk_high)
+			clk_high--;
 
-	if (clk_low)
-		clk_low--;
+		if (clk_low)
+			clk_low--;
+	}
 
 
 	return ((clk_high << ASPEED_I2CD_TIME_SCL_HIGH_SHIFT)
 		& ASPEED_I2CD_TIME_SCL_HIGH_MASK)
 			| ((clk_low << ASPEED_I2CD_TIME_SCL_LOW_SHIFT)
 			   & ASPEED_I2CD_TIME_SCL_LOW_MASK)
-			| (base_clk & ASPEED_I2CD_TIME_BASE_DIVISOR_MASK);
+			| (base_clk_divisor
+			   & ASPEED_I2CD_TIME_BASE_DIVISOR_MASK);
 }
 
-static u32 aspeed_i2c_24xx_get_clk_reg_val(u32 divisor)
+static u32 aspeed_i2c_24xx_get_clk_reg_val(struct device *dev, u32 divisor)
 {
 	/*
 	 * clk_high and clk_low are each 3 bits wide, so each can hold a max
 	 * value of 8 giving a clk_high_low_max of 16.
 	 */
-	return aspeed_i2c_get_clk_reg_val(16, divisor);
+	return aspeed_i2c_get_clk_reg_val(dev, GENMASK(2, 0), divisor);
 }
 
-static u32 aspeed_i2c_25xx_get_clk_reg_val(u32 divisor)
+static u32 aspeed_i2c_25xx_get_clk_reg_val(struct device *dev, u32 divisor)
 {
 	/*
 	 * clk_high and clk_low are each 4 bits wide, so each can hold a max
 	 * value of 16 giving a clk_high_low_max of 32.
 	 */
-	return aspeed_i2c_get_clk_reg_val(32, divisor);
+	return aspeed_i2c_get_clk_reg_val(dev, GENMASK(3, 0), divisor);
 }
 
 /* precondition: bus.lock has been acquired. */
@@ -777,7 +801,7 @@ static int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus)
 	clk_reg_val &= (ASPEED_I2CD_TIME_TBUF_MASK |
 			ASPEED_I2CD_TIME_THDSTA_MASK |
 			ASPEED_I2CD_TIME_TACST_MASK);
-	clk_reg_val |= bus->get_clk_reg_val(divisor);
+	clk_reg_val |= bus->get_clk_reg_val(bus->dev, divisor);
 	writel(clk_reg_val, bus->base + ASPEED_I2C_AC_TIMING_REG1);
 	writel(ASPEED_NO_TIMEOUT_CTRL, bus->base + ASPEED_I2C_AC_TIMING_REG2);
 
-- 
2.19.0.444.g18242da7ef-goog


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v2] i2c: aspeed: fix invalid clock parameters for very large divisors
  2018-09-21 22:10 [PATCH v2] i2c: aspeed: fix invalid clock parameters for very large divisors Brendan Higgins
@ 2018-09-21 22:26 ` Jae Hyun Yoo
  2018-09-21 22:30   ` Brendan Higgins
  0 siblings, 1 reply; 4+ messages in thread
From: Jae Hyun Yoo @ 2018-09-21 22:26 UTC (permalink / raw)
  To: Brendan Higgins, benh, joel, andrew
  Cc: openbmc, linux-i2c, linux-aspeed, linux-kernel

Hi,

On 9/21/2018 3:10 PM, Brendan Higgins wrote:
> The function that computes clock parameters from divisors did not
> respect the maximum size of the bitfields that the parameters were
> written to. This fixes the bug.
> 
> This bug can be reproduced with (and this fix verified with) the test
> at: https://kunit-review.googlesource.com/c/linux/+/1035/
> 
> Discovered-by-KUnit: https://kunit-review.googlesource.com/c/linux/+/1035/
> Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
> Reviewed-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> ---
>   - v2 updates the title of the patch, renames a local variable, and
>     prints an error when the clock divider is clamped.
> ---

[....]

> +	if (base_clk_divisor > ASPEED_I2CD_TIME_BASE_DIVISOR_MASK) {
> +		base_clk_divisor = ASPEED_I2CD_TIME_BASE_DIVISOR_MASK;
> +		clk_low = clk_high_low_mask;
> +		clk_high = clk_high_low_mask;
> +		dev_err(dev,
> +			"clamping clock divider: divider requested, %u, is greater than largest possible divider, %u.",

Please put a newline character at the end of the string.

Thanks,
Jae

[....]

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v2] i2c: aspeed: fix invalid clock parameters for very large divisors
  2018-09-21 22:26 ` Jae Hyun Yoo
@ 2018-09-21 22:30   ` Brendan Higgins
  2018-09-22 14:16     ` Joe Perches
  0 siblings, 1 reply; 4+ messages in thread
From: Brendan Higgins @ 2018-09-21 22:30 UTC (permalink / raw)
  To: Jae Hyun Yoo
  Cc: Benjamin Herrenschmidt, Joel Stanley, Andrew Jeffery,
	OpenBMC Maillist, linux-i2c, linux-aspeed,
	Linux Kernel Mailing List

On Fri, Sep 21, 2018 at 3:26 PM Jae Hyun Yoo
<jae.hyun.yoo@linux.intel.com> wrote:
>
<snip>
>
> > +     if (base_clk_divisor > ASPEED_I2CD_TIME_BASE_DIVISOR_MASK) {
> > +             base_clk_divisor = ASPEED_I2CD_TIME_BASE_DIVISOR_MASK;
> > +             clk_low = clk_high_low_mask;
> > +             clk_high = clk_high_low_mask;
> > +             dev_err(dev,
> > +                     "clamping clock divider: divider requested, %u, is greater than largest possible divider, %u.",
>
> Please put a newline character at the end of the string.

I always forget to do that. I wonder if anyone has considered adding a
warning for this to checkpatch?

>
> Thanks,
> Jae
>
> [....]

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v2] i2c: aspeed: fix invalid clock parameters for very large divisors
  2018-09-21 22:30   ` Brendan Higgins
@ 2018-09-22 14:16     ` Joe Perches
  0 siblings, 0 replies; 4+ messages in thread
From: Joe Perches @ 2018-09-22 14:16 UTC (permalink / raw)
  To: Brendan Higgins, Jae Hyun Yoo
  Cc: Benjamin Herrenschmidt, Joel Stanley, Andrew Jeffery,
	OpenBMC Maillist, linux-i2c, linux-aspeed,
	Linux Kernel Mailing List

On Fri, 2018-09-21 at 15:30 -0700, Brendan Higgins wrote:
> On Fri, Sep 21, 2018 at 3:26 PM Jae Hyun Yoo
[]
> > > +             dev_err(dev,
> > > +                     "clamping clock divider: divider requested, %u, is greater than largest possible divider, %u.",
> > 
> > Please put a newline character at the end of the string.
> 
> I always forget to do that. I wonder if anyone has considered adding a
> warning for this to checkpatch?

Yes, but it's not possible to know when there
a pr_cont that follows so any checkpatch test
would be a false positive generator too.


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2018-09-22 14:17 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-09-21 22:10 [PATCH v2] i2c: aspeed: fix invalid clock parameters for very large divisors Brendan Higgins
2018-09-21 22:26 ` Jae Hyun Yoo
2018-09-21 22:30   ` Brendan Higgins
2018-09-22 14:16     ` Joe Perches

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).