[Letux-kernel] jz4730-i2c

H. Nikolaus Schaller hns at goldelico.com
Sun Feb 21 13:12:08 CET 2021


Hi Paul,
thanks for the explanations.

It gave me another idea. I have simply increased the clock divider to 65535
so that i2c clock is very slow.

And, I have added a timestamp to my logging mechansism (based on reading the OST1
register and taking care of overflows - much like sched_clock() but inlined in my
code).

With this I can now see the SR register behave as follows (if writing 0 bytes to
a non-responding address)

action				SR	IRQ after this action
IDLE				0x04	IRQ inactive
setting STA			0x1c	IRQ active
interrupt after a moment	0x0c	IRQ active
writing DR and DRF (addr)	0x0e	IRQ active
interrupt immediately		0x08	IRQ active
doing nothing			0x08	IRQ remains active
interrupt immediately		0x08	IRQ active
writing STO			0x18	IRQ active
sequence of IRQs		0x18	IRQ active
after a while			0x19	IRQ active
after some IRQs			0x1d	IRQ active
finally				0x05	IRQ becomes inactive

On a responding address there is no 0x19 and 0x1d -> 0x1c and 0x05 -> 0x04.
So only the ACKF differs - as expected.

So far I conclude:
* setting STA triggers the first IRQ - but with some delay (may be induced by my test setup or kernel IRQ latency)
* after STA is sent, there is the second IRQ
* BUSY is set immediately by STA and reset by STO (but only after done - seems to be queued)
* BUSY is not included in IRQ generation (or the read IRQs would not work)
* after the ADDR is written, TEND is still 1 - maybe because the STA is still being processed
* but DRF is immediately reset
* (N)ACKF comes a while after writing DRF=1 and may even come after queuing STO
* ACKF is not included in IRQ generation
* after a while TEND becomes 1 (likely because I have not written DRF=1 and after shifting in/out the ACK bit)
* so TEND is sort of a "FIFO underrun" indicator
* then, the STA/STO buffer becomes empty, TEND=1 and interrupts stop
* from all this I conclude: IRQ = STX || !TEND (at least for writing)

What I still have no idea is how to handle this flood of IRQs.

Strange black box...

If my formula is correct, the IRQs go only away after the FIFO did
drain empty and the controller is ready to send STO. But this is
not how I would design it.

Could it be a hardware bug so that the I2C can never work with IRQs?

But what I wonder is why the jz4740 has exactly the same. They would
have had plenty of time to fix it.

Well, my driver may get confused by the IRQs and ripples through the
driver states too fast. Therefore it may send the STO too early.
And that may as well confuse the hardware state engine.

So it is still open how to reset the IRQ after providing a new byte
while transmitting and how to properly switch from writing to reading,
i.e. tell the controller to wait until data has arrived. Instead of
requesting data to be sent.

BR,
Nikolaus

PS: unfortunately I have never learned VHDL. If I had, it would probably
be easier to discuss a reengineered version of the I2C controller...

I have a rough idea of a 9 bit shift register feeded at one end with
received SDA and sending out SDA on the other end (while in write mode).

This register can be filled by writing something to the DR and setting AC
bit. And a shift out is triggered by actively setting DRF=1.
If it is not filled through DRF=1 it becomes filled by reading SDA.

And some counter is counting 9 bits each and either starting to send
the next byte or set DRF=1 in receive mode. Or send STO after all has
been sent and STX is active.


> Am 17.02.2021 um 00:49 schrieb Paul Boddie <paul at boddie.org.uk>:
> 
> On Sunday, 14 February 2021 17:29:04 CET H. Nikolaus Schaller wrote:
>> 
>> Questions:
>> * why do you wait for interrupt at all?
> 
> I'm not so much as waiting as yielding to the scheduler, with any I2C 
> interrupt causing my program to be scheduled again, resuming at the point at 
> which it yielded or "waited". There's no polling going on here, or at least 
> not as the intended means of performing the operations involved.
> 
> In fact, I just rewrote my code to use a state machine instead with a 
> dispatcher for when interrupt requests arrive. It seems to work mostly like 
> the previous form of the code, so I think there isn't anything special about 
> what I am doing.
> 
>> * polling for STX doesn't seem necessary from the Flow Chart
> 
> So, the manual says that STX is...
> 
> STA/STO Command is On.
> 0 – STA/STO FIFO buffer is empty.
> 1 – STA/STO FIFO buffer is not empty.
> 
> This is a bit incoherent, but I could interpret it as meaning that given the 
> issuing of a start or stop condition, if STX is set then the bus is not ready 
> for actual data transfer.
> 
> What I have now just done is to record various event occurrences to see what 
> might be happening when interrupts occur. If I measure occurrences at the 
> start of reading, after the address has been sent, when I test for the 
> following condition...
> 
> STX || (!DRF && !NACK)
> 
> ...which is, of course...
> 
> STX || (!DRF && ACKF)
> 
> ...then, on the latest cycle of the test, I see...
> 
> 393 read operations
> 1835 cases of the entire condition above applying, of which...
> 423 cases where STX is set
> 1412 cases where STX is not set
> 
> On average, then, STX appears to be set once per read operation, but there 
> will be between 4 and 5 evaluations with the entire condition being satisfied, 
> with between 3 and 4 evaluations involving STX not being set and thus the more 
> interesting condition (no data available to read) being satisfied.
> 
> Since I have made my interrupt dispatch occur with no retries upon any 
> timeout, and with a 0.1s timeout to prevent any premature evaluation of the 
> condition, this suggests that the interrupt condition may indeed get "stuck" 
> at this point in the workflow, causing my code to repeatedly evaluate the 
> above condition in the program. This might answer an earlier question.
> 
> My previous experience was that I have to test for STX. What I think happens, 
> and maybe can be tested tomorrow (or soon), is that when the address is sent 
> the following sequence of conditions are observed...
> 
> 1) !STX,  DRF, !NACK  (address issued to peripheral by program)
> 2)  STX,       !NACK  (address presented on bus by peripheral, DRF uncertain)
> 3) !STX, !DRF, !NACK  (device busy - no data available)
> 4) !STX,  DRF, !NACK  (data available)
> ) !STX, !DRF,  NACK  (alternative: NACK)
> 
> The challenge is distinguishing between the first and fourth steps in the 
> above sequence, at least as far as I see it. If the status is tested too soon 
> (thanks to that uncontrollable interrupt), then STX helps to prevent the 
> interpretation of the first step as the fourth.
> 
> I hope this helps to provide some more context and useful observations.
> 
> Paul
> 
> 



More information about the Letux-kernel mailing list