[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
STX, BUSY, TEND
> interrupt after a moment 0x0c IRQ active
BUSY, TEND
> writing DR and DRF (addr) 0x0e IRQ active
BUSY, TEND, DRF
> interrupt immediately 0x08 IRQ active
BUSY
> doing nothing 0x08 IRQ remains active
> interrupt immediately 0x08 IRQ active
> writing STO 0x18 IRQ active
STX, BUSY
> sequence of IRQs 0x18 IRQ active
> after a while 0x19 IRQ active
STX, BUSY, ACKF
> after some IRQs 0x1d IRQ active
STX, BUSY, TEND, ACKF
> finally 0x05 IRQ becomes inactive
TEND, ACKF
> 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
> 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)
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"
involved).
> 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
transaction.
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
questions.
[...]
> 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.
Paul
More information about the Letux-kernel
mailing list