[Letux-kernel] jz4730-i2c

H. Nikolaus Schaller hns at goldelico.com
Sun Feb 21 17:42:38 CET 2021

> Am 21.02.2021 um 16:56 schrieb Paul Boddie <paul at boddie.org.uk>:
> 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
>> 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).

Well, there is some buffering. In my log the 0x1d state "survives" quite a while
and generates IRQs until after ca. 20 it falls back to 0x05.

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

Well, it doesn't completely. The flowcharts describe to set up IRQs...

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

Yes, they may have made a mistake interfacing that VHDL block to the accessible
registers. Maybe they forgot to put one bit.

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

Especially if there are setups using DMA.

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

Maybe we suddenly have the right idea how it could work. I even tried reading
the data register instead of writing and setting DRF=1 to end "write mode"
and the IRQ, but failed as well.

The most mysterious part for me is still how the controller switches from
write to read mode.

At the moment it looks as if it is very simple: if 9 bits have been sent
and there was no new DRF=1 it sets TEND=1 and goes to read mode. Read mode
sets DRF=1 automatically every 9 bits. Each such event generates an IRQ.

So something like this:

for(every scl)
if (BUSY)
	sda-out = (sr & 0x100) != 0;	// send MSB
	sr <<= 1;
	sr |= sda-in;	// 9 bit shift register

	if (count == 9)
		if (!TEND)
			if (DRF)
				sr = (dr << 1) | AC;	// prepare 9 bit word to be sent (MSB first)
				DRF = 0;	// notify driver
				TEND = 1;	// switch to receive mode
			DR = sr >> 1;	// MSB first
			ACKF = (sr&1);	// last bit was ACKF
			DRF = 1;	// notify driver
			count = 0;	// start over
if (STO || STX)
	STX = 1;
	if(count == 0)
		{ // after sending/receiving full 9 bits
		send STO condition
		BUSY = 0;
		STX = 0;
if (STA)
	BUSY = 1;
	STX = 1;
	send STA condition;
	STX = 0;
	TEND = 0;	// too early according to my analysis
	count = 9;	// start with first byte

IRQ = STX || !TEND || (TEND & DRF);	// this makes IRQ go away when resetting DRF=0 after reading a byte
// IRQ = (!TEND && !DRF) || (TEND & DRF);	// this would IMHO make more sense...

This almost explains the behaviour as I think I have deduced from my logs.

Well, the single bug in jz4730 and jz4740 may be how IRQ is generated.
Or how resetting the DRF works when writing.

AFAIR (haven't tested with the lowest speed mode), reading data did work strangely
with my IRQ driven driver, even when sending on byte address first.

The main issue is if we want to send 0 the address only and no data...

So maybe time gives us another idea.

Or we have to go back to a non-IRQ driver model to become able to fix the
remaining issues and upstream everything soon. Then we get a chance for
inclusion into v5.13.

So let's shift priorities and come back to this IRQ based driver later.


More information about the Letux-kernel mailing list