[Letux-kernel] jz4730-i2c

Paul Boddie paul at boddie.org.uk
Sun Feb 21 16:56:22 CET 2021

On Sunday, 21 February 2021 13:12:08 CET H. Nikolaus Schaller wrote:
> 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.

Good idea!

> 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

OK, TEND is set here.

> 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

This might be the only part of the interaction between the workflow and 
interrupt generation that makes any sense.

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

And maybe this makes sense, too, if BUSY has a very simple meaning that the 
bus is busy and nothing else.

> * after the ADDR is written, TEND is still 1 - maybe because the STA is
> still being processed
> * but DRF is immediately reset

This does make me wonder whether instead of testing for STX, I could test for 
TEND to determine whether DRF is reflecting the validity of incoming data.

> * (N)ACKF comes a while after writing DRF=1 and may even come after queuing
> * 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)

If STX just means that the start or stop conditions are being transmitted, 
which is an interpretation I considered, then the "STA/STO Command is On" 
description is actually accurate, even if "STA/STO FIFO buffer" just adds 
confusion (since STA and STO are just bus conditions and there are no "FIFOs" 

> 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?

I think it says a lot that the manual just ignores interrupts altogether. 
Maybe they were designed into the peripheral in a way that did not really 
consider the workflow, resulting in interrupts in too many situations, or 
maybe the intention was that with an active I2C transaction, there would be 
continual interrupts to force scheduling of handler routines for the 

That latter case sounds absurd, but there is an understandable if simple logic 
that could justify people writing routines that are put in a driver and then 
the hardware just makes sure that the routines keep getting control, even if 
it does impact the performance and normal functioning of the rest of the 
system. This would arguably be better than the legacy driver code that just 
loops, waiting for the appropriate bus conditions: at least a few instructions 
of some other code might get run.

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

I guess that drivers were written to "get it done" with regard to I2C support, 
and no-one really cared whether the interrupt handling made any sense. In the 
JZ4730, the I2C support involves dedicated pins, and the enhancement with 
later SoCs is to allow such support on other pins. That led to people just 
using the GPIO support instead, since that became an option.

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

Certainly, care needs to be taken about initiating various bus conditions.

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

Yes, whether STX is appropriate for that write-to-read transition or, given 
what you have shown above, whether TEND might be usable, are interesting 


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

I think we've both seen plenty of examples of chips that do this kind of thing 
reliably. The first one I saw was probably a Maxim chip doing USB, but I think 
the Microchip devices are similar, where reading and writing are suitably 
"latched" to allow the peripheral to figure out what is going on.

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

I was looking at Verilog at one point (instead of VHDL since the Free Software 
toolchains that could support various hardware devices from synthesis through 
to deployment favoured Verilog), and although I stopped what I was doing 
because I had other things to deal with at the time, I did think that if I 
were to take some of my microcontroller activities further, I would probably 
take this approach instead of having to figure out the workings of things like 
the PIC32 devices.

Certainly, what is odd about this hardware is that there are operational 
workflows but the interrupt generation does not fit in with them in any 
meaningful way. I would have thought that the interrupt generation situations 
would have been obvious to the designers given such workflows. Then again, 
maybe this informed the JZ4780 and similar SoCs, but there are deficiencies 
with the I2C support in the JZ4780 as well.

Anyway, thanks for the hard work looking at this again. Unfortunately, I 
cannot really bring anything other than more tentative observations myself.


More information about the Letux-kernel mailing list