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@xxxxxxxx> --- 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 -- To unsubscribe from this list: send the line "unsubscribe linux-i2c" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html