i2c-imx: writing to eeprom via dma fails on ls1021a

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Commit cc07fd3c45c1 (ARM: dts: ls1021a: Enable I2C DMA support) enabled
the use of DMA on ls1021a. However, writing to a M24C32-R eeprom in
chunks larger than 16 bytes now consistently fails, e.g.

# dd if=/dev/urandom
of=/sys/devices/platform/soc/2180000.i2c/i2c-0/0-0050/eeprom bs=32
seek=12 count=2
dd: writing '/sys/devices/platform/soc/2180000.i2c/i2c-0/0-0050/eeprom':
Connection timed out
2+0 records in
1+0 records out

Doing just a single write of something > 32 bytes (i.e. the page size)
causes the same symptom since the at24 driver splits up the write.
Reading back the eeprom shows that the first page does indeed make it to
the chip, but subsequent ones do not.

The problem is that while in the non-dma case (say 'bs=8 seek=50
count=4'), the i2c-imx driver checks that the slave responds to the
slave address

        /* write slave address */
        imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx,
IMX_I2C_I2DR);
        result = i2c_imx_trx_complete(i2c_imx);
        if (result)
                return result;
        result = i2c_imx_acked(i2c_imx);
        if (result)
                return result;

and this works as intended back in at24.c

  at24 0-0050: write 8@400 --> 0 (190428)
  at24 0-0050: write 8@408 --> -6 (190428)
  at24 0-0050: write 8@408 --> -6 (190428)
  at24 0-0050: write 8@408 --> 0 (190428)
  at24 0-0050: write 8@416 --> -6 (190428)
  at24 0-0050: write 8@416 --> -6 (190429)
  at24 0-0050: write 8@416 --> 0 (190429)
  at24 0-0050: write 8@424 --> -6 (190429)
  at24 0-0050: write 8@424 --> -6 (190429)
  at24 0-0050: write 8@424 --> 0 (190429)

which retries a couple of times until the eeprom finishes its write
cycle. But in the DMA case, we cannot (or at least do not) check that
the slave responds since setting the DMAEN bit means we don't get a
transfer complete interrupt. So the dma_write case just does

	/*
	 * Write slave address.
	 * The first byte must be transmitted by the CPU.
	 */
	imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
	time_left = wait_for_completion_timeout(
				&i2c_imx->dma->cmd_complete,
				msecs_to_jiffies(DMA_TIMEOUT));
	if (time_left == 0) {
		dmaengine_terminate_all(dma->chan_using);
		return -ETIMEDOUT;
	}

and DMA_TIMEOUT is a whole second. So when the second dma transfer fails
a whole second has passed, meaning we're way past our time budget back
in at24.c.

  at24 0-0050: write 32@384 --> 0 (258590)
  at24 0-0050: write 32@416 --> -110 (258696)
  at24 0-0050: write 32@416 --> -6 (258696)

(I have HZ=100). Now, since 9a9e295e7c5c (eeprom: at24: fix unexpected
timeout under high load), the at24 driver will always do at least two
attempts, which is what we see above. On the second attempt, the eeprom
is of course long done with its write cycle, so we actually get all the
way through i2c_imx_dma_write() before finally returning an error from
i2c_imx_acked().

Now, _why_ that fails I don't really understand (perhaps there's some
state that doesn't get correctly unwound in the first error case?), but
even if it worked, using dma would limit us to 32 bytes/second because
the first attempt at writing the second page only fails after
DMA_TIMEOUT, which makes using dma rather pointless.

Options:

(1) revert cc07fd3c45c1. But then we'd lose use of DMA for read which
works just fine.

(2) make DMA_THRESHOLD in i2c-imx configurable (module param/CONFIG_*
knob) and separate for r/w, so one could disable dma for write by
setting the threshold to "infinity".

(3) figure out if i2c-imx can report early on that the slave doesn't
respond and hence make the logic in at24.c work as intended

(4) ideas welcome

I don't think this is specific to LS1021A, the same thing should be seen
on other SOCs using the same i2c driver with DMA (e.g. ls1043a and
ls1046a from their .dtsi files). I'm wondering why nobody else seems to
have run into this.

Rasmus




[Index of Archives]     [Linux GPIO]     [Linux SPI]     [Linux Hardward Monitoring]     [LM Sensors]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux