linux-i2c.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] i2c: busses: i2c-bcm2835: limits cdiv to allowed values
@ 2015-05-22  8:36 s. wicki
       [not found] ` <1432283811-22226-1-git-send-email-linux_wi-ADq4ffItWIY@public.gmane.org>
  0 siblings, 1 reply; 2+ messages in thread
From: s. wicki @ 2015-05-22  8:36 UTC (permalink / raw)
  To: swarren-3lzwWm7+Weoh9ZMKESR00Q; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA, s. wicki

fixes:	round down divider instead of incrementing
adds:	make sure bits 16-31 of cdiv register are always 0
adds:	assume minimal divider of 2 if divider resulted in 0
	(bcm2835 sets divider to 32768 if cdiv is set to 0)

Signed-off-by: s. wicki <linux_wi-ADq4ffItWIY@public.gmane.org>
---
 drivers/i2c/busses/i2c-bcm2835.c | 38 +++++++++++++++++++++++++++++++-------
 1 file changed, 31 insertions(+), 7 deletions(-)

diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c
index c9336a3..745181c 100644
--- a/drivers/i2c/busses/i2c-bcm2835.c
+++ b/drivers/i2c/busses/i2c-bcm2835.c
@@ -51,6 +51,9 @@
 #define BCM2835_I2C_S_LEN	BIT(10) /* Fake bit for SW error reporting */
 
 #define BCM2835_I2C_TIMEOUT (msecs_to_jiffies(1000))
+#define BCM2835_I2C_CDIV_MIN	0x0002
+#define BCM2835_I2C_CDIV_MAX	0xFFFE
+#define BCM2835_I2C_CDIV_BITMSK	0xFFFE
 
 struct bcm2835_i2c_dev {
 	struct device *dev;
@@ -251,13 +254,34 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
 	}
 
 	divider = DIV_ROUND_UP(clk_get_rate(i2c_dev->clk), bus_clk_rate);
-	/*
-	 * Per the datasheet, the register is always interpreted as an even
-	 * number, by rounding down. In other words, the LSB is ignored. So,
-	 * if the LSB is set, increment the divider to avoid any issue.
-	 */
-	if (divider & 1)
-		divider++;
+	if (divider == 0) {
+		/*
+		 * divider results in 0 by extremely high bus_clk_rate values
+		 * such as bus_clk_rate >= 4044967297 and core_clock = 250MHz.
+		 * In such a case assume the minimal possible divider since
+		 * bcm2835 chip sets divisor internally to 32768 if cdiv is 0.
+		 */
+		divider = BCM2835_I2C_CDIV_MIN;
+	} else {
+		/* check if divider meets certain bcm2835 specific criterias */
+		if ((divider & BCM2835_I2C_CDIV_BITMSK) != divider) {
+			if (divider > BCM2835_I2C_CDIV_MAX)
+				/*
+				 * Per the datasheet it must be made sure
+				 * that bits 16-31 are set to 0. If that is
+				 * not the case, then set to the maximum
+				 * allowed value.
+				 */
+				divider = BCM2835_I2C_CDIV_MAX;
+			else
+				/*
+				 * Per datasheet the cdiv is always rounded
+				 * down to an even number. Bitmask takes care
+				 * of this and clears the LSB
+				 */
+				divider &= BCM2835_I2C_CDIV_BITMSK;
+		}
+	}
 	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DIV, divider);
 
 	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-- 
2.1.4

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

* Re: [PATCH] i2c: busses: i2c-bcm2835: limits cdiv to allowed values
       [not found] ` <1432283811-22226-1-git-send-email-linux_wi-ADq4ffItWIY@public.gmane.org>
@ 2015-05-28 20:17   ` Stephen Warren
  0 siblings, 0 replies; 2+ messages in thread
From: Stephen Warren @ 2015-05-28 20:17 UTC (permalink / raw)
  To: s. wicki; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA

On 05/22/2015 02:36 AM, s. wicki wrote:
> fixes:	round down divider instead of incrementing
> adds:	make sure bits 16-31 of cdiv register are always 0
> adds:	assume minimal divider of 2 if divider resulted in 0
> 	(bcm2835 sets divider to 32768 if cdiv is set to 0)

Do you have a reference to a specific document and section? That'd be 
good to include in the commit description for future reference.

> Signed-off-by: s. wicki <linux_wi-ADq4ffItWIY@public.gmane.org>

Using your full name rather than an abbreviation is preferred.

You didn't send this pach to the I2C maintainer so I imagine he won't 
see it and hence it won't be applied.

It'd also be a good idea to send this to the Raspberry Pi mailing list, 
linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XZu6nac5fYnt@public.gmane.org


> diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c
> +#define BCM2835_I2C_CDIV_MIN	0x0002
> +#define BCM2835_I2C_CDIV_MAX	0xFFFE
> +#define BCM2835_I2C_CDIV_BITMSK	0xFFFE

>   	divider = DIV_ROUND_UP(clk_get_rate(i2c_dev->clk), bus_clk_rate);
> -	/*
> -	 * Per the datasheet, the register is always interpreted as an even
> -	 * number, by rounding down. In other words, the LSB is ignored. So,
> -	 * if the LSB is set, increment the divider to avoid any issue.
> -	 */
> -	if (divider & 1)
> -		divider++;

The old code used to round the divider up in the case where the LSB was 
set. This ensures that the I2C clock rate is no faster than what was 
requested.

> +	if (divider == 0) {
> +		/*
> +		 * divider results in 0 by extremely high bus_clk_rate values
> +		 * such as bus_clk_rate >= 4044967297 and core_clock = 250MHz.
> +		 * In such a case assume the minimal possible divider since
> +		 * bcm2835 chip sets divisor internally to 32768 if cdiv is 0.
> +		 */
> +		divider = BCM2835_I2C_CDIV_MIN;

What about when divider == 1? Shouldn't that if condition be:

if (divider < BCM2835_I2C_CDIV_MIN)

?

> +	} else {
> +		/* check if divider meets certain bcm2835 specific criterias */
> +		if ((divider & BCM2835_I2C_CDIV_BITMSK) != divider) {
> +			if (divider > BCM2835_I2C_CDIV_MAX)
> +				/*
> +				 * Per the datasheet it must be made sure
> +				 * that bits 16-31 are set to 0. If that is
> +				 * not the case, then set to the maximum
> +				 * allowed value.
> +				 */
> +				divider = BCM2835_I2C_CDIV_MAX;
> +			else
> +				/*
> +				 * Per datasheet the cdiv is always rounded
> +				 * down to an even number. Bitmask takes care
> +				 * of this and clears the LSB
> +				 */

The datasheet describes how the HW interprets the register value (by 
ignoring the LSB). It doesn't say how SW must program the register. In 
other words, SW should round up, to avoid HW rounding down. It's not 
mandatory for SW to round down.

> +				divider &= BCM2835_I2C_CDIV_BITMSK;
> +		}
> +	}
>   	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DIV, divider);
>
>   	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

I think this would be better as:

if (divider < BCM2835_I2C_CDIV_MIN)
     divider = BCM2835_I2C_CDIV_MIN;
if (divider & 1)
     divider++;
if (divider > BCM2835_I2C_CDIV_MAX)
     divider = BCM2835_I2C_CDIV_MAX;

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

end of thread, other threads:[~2015-05-28 20:17 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-05-22  8:36 [PATCH] i2c: busses: i2c-bcm2835: limits cdiv to allowed values s. wicki
     [not found] ` <1432283811-22226-1-git-send-email-linux_wi-ADq4ffItWIY@public.gmane.org>
2015-05-28 20:17   ` Stephen Warren

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).