Skip to content

Commit

Permalink
i2c: mxs: fix broken timing calculation
Browse files Browse the repository at this point in the history
The timing calculation is rather bogus and gives extremely wrong
results for higher frequencies (on an i.MX28). E.g. instead of 400 kHz
I measured 770 kHz.

Implement a calculation that adheres to the I2C spec and gives exact
results for I2C frequencies from 12.56 kHz to 960 kHz.

Also the bus_free and leadin parameters are programmed according to
the I2C spec for standard and fast mode.

This was tested on a Ka-Ro TX28 module with a DS1339, TSC2007, PCA9554
and SGTL5000 client.

Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
Acked-by: Marek Vasut <marex@denx.de>
[wsa: patch fixes whitespace issue, too]
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
  • Loading branch information
Lothar Waßmann authored and Wolfram Sang committed Aug 16, 2013
1 parent b7d12a8 commit 869c6a3
Showing 1 changed file with 63 additions and 24 deletions.
87 changes: 63 additions & 24 deletions drivers/i2c/busses/i2c-mxs.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,10 @@ struct mxs_i2c_dev {

uint32_t timing0;
uint32_t timing1;
uint32_t timing2;

/* DMA support components */
struct dma_chan *dmach;
struct dma_chan *dmach;
uint32_t pio_data[2];
uint32_t addr_data;
struct scatterlist sg_io[2];
Expand All @@ -138,7 +139,7 @@ static int mxs_i2c_reset(struct mxs_i2c_dev *i2c)
*/
writel(i2c->timing0, i2c->regs + MXS_I2C_TIMING0);
writel(i2c->timing1, i2c->regs + MXS_I2C_TIMING1);
writel(0x00300030, i2c->regs + MXS_I2C_TIMING2);
writel(i2c->timing2, i2c->regs + MXS_I2C_TIMING2);

writel(MXS_I2C_IRQ_MASK << 8, i2c->regs + MXS_I2C_CTRL1_SET);

Expand Down Expand Up @@ -587,41 +588,79 @@ static const struct i2c_algorithm mxs_i2c_algo = {
.functionality = mxs_i2c_func,
};

static void mxs_i2c_derive_timing(struct mxs_i2c_dev *i2c, int speed)
static void mxs_i2c_derive_timing(struct mxs_i2c_dev *i2c, uint32_t speed)
{
/* The I2C block clock run at 24MHz */
/* The I2C block clock runs at 24MHz */
const uint32_t clk = 24000000;
uint32_t base;
uint32_t divider;
uint16_t high_count, low_count, rcv_count, xmit_count;
uint32_t bus_free, leadin;
struct device *dev = i2c->dev;

if (speed > 540000) {
dev_warn(dev, "Speed too high (%d Hz), using 540 kHz\n", speed);
speed = 540000;
} else if (speed < 12000) {
dev_warn(dev, "Speed too low (%d Hz), using 12 kHz\n", speed);
speed = 12000;
divider = DIV_ROUND_UP(clk, speed);

if (divider < 25) {
/*
* limit the divider, so that min(low_count, high_count)
* is >= 1
*/
divider = 25;
dev_warn(dev,
"Speed too high (%u.%03u kHz), using %u.%03u kHz\n",
speed / 1000, speed % 1000,
clk / divider / 1000, clk / divider % 1000);
} else if (divider > 1897) {
/*
* limit the divider, so that max(low_count, high_count)
* cannot exceed 1023
*/
divider = 1897;
dev_warn(dev,
"Speed too low (%u.%03u kHz), using %u.%03u kHz\n",
speed / 1000, speed % 1000,
clk / divider / 1000, clk / divider % 1000);
}

/*
* The timing derivation algorithm. There is no documentation for this
* algorithm available, it was derived by using the scope and fiddling
* with constants until the result observed on the scope was good enough
* for 20kHz, 50kHz, 100kHz, 200kHz, 300kHz and 400kHz. It should be
* possible to assume the algorithm works for other frequencies as well.
* The I2C spec specifies the following timing data:
* standard mode fast mode Bitfield name
* tLOW (SCL LOW period) 4700 ns 1300 ns
* tHIGH (SCL HIGH period) 4000 ns 600 ns
* tSU;DAT (data setup time) 250 ns 100 ns
* tHD;STA (START hold time) 4000 ns 600 ns
* tBUF (bus free time) 4700 ns 1300 ns
*
* Note it was necessary to cap the frequency on both ends as it's not
* possible to configure completely arbitrary frequency for the I2C bus
* clock.
* The hardware (of the i.MX28 at least) seems to add 2 additional
* clock cycles to the low_count and 7 cycles to the high_count.
* This is compensated for by subtracting the respective constants
* from the values written to the timing registers.
*/
base = ((clk / speed) - 38) / 2;
high_count = base + 3;
low_count = base - 3;
rcv_count = (high_count * 3) / 4;
xmit_count = low_count / 4;
if (speed > 100000) {
/* fast mode */
low_count = DIV_ROUND_CLOSEST(divider * 13, (13 + 6));
high_count = DIV_ROUND_CLOSEST(divider * 6, (13 + 6));
leadin = DIV_ROUND_UP(600 * (clk / 1000000), 1000);
bus_free = DIV_ROUND_UP(1300 * (clk / 1000000), 1000);
} else {
/* normal mode */
low_count = DIV_ROUND_CLOSEST(divider * 47, (47 + 40));
high_count = DIV_ROUND_CLOSEST(divider * 40, (47 + 40));
leadin = DIV_ROUND_UP(4700 * (clk / 1000000), 1000);
bus_free = DIV_ROUND_UP(4700 * (clk / 1000000), 1000);
}
rcv_count = high_count * 3 / 8;
xmit_count = low_count * 3 / 8;

dev_dbg(dev,
"speed=%u(actual %u) divider=%u low=%u high=%u xmit=%u rcv=%u leadin=%u bus_free=%u\n",
speed, clk / divider, divider, low_count, high_count,
xmit_count, rcv_count, leadin, bus_free);

low_count -= 2;
high_count -= 7;
i2c->timing0 = (high_count << 16) | rcv_count;
i2c->timing1 = (low_count << 16) | xmit_count;
i2c->timing2 = (bus_free << 16 | leadin);
}

static int mxs_i2c_get_ofdata(struct mxs_i2c_dev *i2c)
Expand Down

0 comments on commit 869c6a3

Please sign in to comment.