hi,
I am developing an interrupt based I2C multimaster driver on AM335x under QNX6.6. This is intended as an interrupt based alternative to the polling driver as in AM335x I2C multi-master/slave mode . I am in the same team those guys, and we agreed I would post separately so as not to muddy the other thread which at present only concerns a polling-based driver. I am familiar with Steve’s polling driver and some of the issues being following up with TI there.
The first stage was to develop an interrupt based driver for the 3 basic modes we require, standalone:-
- Master TX : basically working. Here, I am using the same approach as in the AM335x QNX6.6 BSP I2C driver.
- Slave RX: basically working. I am using RXTRSH=0 hence reading 1 byte at a time, looking for RRDY interrupt.
- Slave TX: not working. As is reqd for slave TX, I am using TXTRSH=0, so sending 1 byte at a time. I continue sending data, and expect the Master to send a NACK. However, I can get at most 1 data byte, then the bus is held low. After the 1st byte is sent (signalled by XRDY), I write another byte to the FIFO, and I clear XRDY. Unfortunately, XRDY bit is not asserted again. I am of course checking that AERR,NACK etc are not being set.
I am focussing on debugging slave TX in isolation now, as the other modes are more or less ok. For this purpose I have disabled the master side of the code.
My state machine handles transition into Slave TX or RX based on the interrupt flags.
In QNX I am using InterruptAttachEvent() rather than InterruptAttach() so there is no ISR as such. Handling is done in user space so can be debugged more easily. For the slave thread, I am waiting for a message on the channel attached to the interrupt, upon which I read the IRQSTATUS_RAW register immediately once to get the status and perform handling for master or slave – based on AAS. Clearing is done by writing to IRQSTATUS based on the flags and the states entered, and then the driver loops back to wait for another interrupt.
Pseudocode for slave (master not shown) is as below.
I attach a screenshot showing the timings for the failed Slave TX and a log.
FYI I have also tried variations on this such as:-
- not clearing XRDY
- using XUDF as indication of ongoing transmit (in the absence of XRDY.)
Assumptions:
-I am not going to clear AAS until ARDY appears.
-I am not clearing XUDF, since writing to the FIFO should do that.
Please can you give any advice as to why XRDY may stop appearing.
Init:
dev->iid = InterruptAttachEvent(dev->intr,…)
create/call SlaveIsThread()
reset i2c
Slave Thread
while(1)
{
If timed out: reset I2C;
If (slave state==idle)
{
do once per entry into slave state:
{
set own address;
out16( pdev->regbase + AM335x_I2C_IE_SET, ( AM335x_I2C_XUDF | AM335x_I2C_AAS |
AM335x_I2C_AERR | AM335x_I2C_XRDY|
AM335x_I2C_RRDY | AM335x_I2C_ARDY | AM335x_I2C_NACK ) )
set RX/TXTHRES to 1 byte; reset the RX/TX FIFOs;
}
wait until bus not busy: IRQSTATUS_RAW & BB, if timed out, try again
}
enable I2C in I2C_CON slave mode;
InterruptUnmask(pdev->intr,pdev->iid);
WAIT FOR INTERRUPT()
If (SlaveRxComplete) – copy data to host buffer etc;
If (SlaveTxComplete) – just start again;
}
Wait for interrupt
if(MsgReceivePulse(dev->chid, &pulse,….)
if timeout, start waiting again;
else:
InterruptStatusReg = in16(dev->regbase + AM335x_I2C_STAT_RAW);
if(InterruptStatusReg & AAS) : handleSlave(InterruptStatusReg );
else if(InterruptStatusReg & (BB | XRDY | XDR) ): handleMaster(InterruptStatusReg ); // NB! Master RX not reqd..
handleSlave
if((InterruptStatusReg & (XRDY | XUDF))==0) // slave RX
{
if(InterruptStatusReg & RRDY): read byte, clear RRDY
if(InterruptStatusReg & AM335x_I2C_ARDY) : SlaveRxComplete = 1;
}
else if(InterruptStatusReg & (AM335x_I2C_XRDY)) // Slave Transmit
{
if we are within our slave buffer bounds: write next byte 1 byte to FIFO; clear XRDY;
else write 0;
}
else
{
if(InterruptStatusReg & ARDY): clearValue |= (ARDY | AAS | BF)
}
Screenshot:
rmi2c:[1642] Reg_GetBusActive mode=SLAVE rmi2c:[194]:i2c_wait_bus_not_busy: entry (CON 0x8000 STATRAW 0x0) rmi2c:[227] i2c_wait_bus_not_busy: (CTRREG 0x8000 STSREG 0x0) rmi2c:[250] i2c_wait_bus_not_busy: return 0 (CTRREG 0x8000 STSREG 0x0) rmi2c:Reg_GetBusActive: <SLAVE> bus is free rmi2c:[926] SlaveIsThread: Bus is free, enabling i2c module. own addr=0x45 rmi2c:[596] omap_wait_status: mode=<SLAVE> entry: CON=0x8000 IE set= 0x29e STAT_RAW= 0x0 rmi2c:[632] omap_wait_status: <SLAVE> AM335x_I2C_AAS slave state=10 STAT=0x1600 RAW=0x1600 rmi2c:[180] handleSlave:entry: STATRAW=0x1600 rmi2c:[307] handleSlave: state = I2C_SLAVE_IDLE: TX started (BB|XUDF) but XRDY not on yet. setting mode I2C_SLAVE_TX rmi2c:[1626] Reg_InterruptsClear. AM335x_I2C_IRQSTAT_RAW= 0x1610 rmi2c:[1627] Reg_InterruptsClear. Writing to AM335x_I2C_STAT 0x0 rmi2c:[638] omap_wait_status: <SLAVE> done handleSlave rmi2c:[671] omap_wait_status: <SLAVE> done rmi2c: [958] SlaveIsThread: gotIntr[I2C_INDEX_SLAVE]==1 - released from omap_wait_status() rmi2c:[963] I2C Slave MaxIntr:1 rmi2c: [1002] SlaveIsThread: end while: Slave TX STAT=0x1610 RAW=0x1610 rmi2c:[596] omap_wait_status: mode=<SLAVE> entry: CON=0x8000 IE set= 0x29e STAT_RAW= 0x1610 rmi2c:[632] omap_wait_status: <SLAVE> AM335x_I2C_AAS slave state=12 STAT=0x1610 RAW=0x1610 rmi2c:[180] handleSlave:entry: STATRAW=0x1610 rmi2c:[388] handleSlave: I2C_SLAVE_TX rmi2c:[410] handleSlave: I2C_SLAVE_TX slave TX : XRDY/XUDF rmi2c:[433] handleSlave: state = I2C_SLAVE_TX: SlaveTxSentBytes=1 data=0x36(6) <<<<<<<<<<<<<<<<<<<<<<<<< {PIL} 1st byte ok rmi2c:[1626] Reg_InterruptsClear. AM335x_I2C_IRQSTAT_RAW= 0x1610 rmi2c:[1627] Reg_InterruptsClear. Writing to AM335x_I2C_STAT 0x10 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< clear XRDY rmi2c:[638] omap_wait_status: <SLAVE> done handleSlave rmi2c:[671] omap_wait_status: <SLAVE> done rmi2c: [958] SlaveIsThread: gotIntr[I2C_INDEX_SLAVE]==1 - released from omap_wait_status() rmi2c:[963] I2C Slave MaxIntr:1 rmi2c: [1002] SlaveIsThread: end while: Slave TX STAT=0x1600 RAW=0x1600 rmi2c:[596] omap_wait_status: mode=<SLAVE> entry: CON=0x8000 IE set= 0x29e STAT_RAW= 0x1600 rmi2c:[632] omap_wait_status: <SLAVE> AM335x_I2C_AAS slave state=12 STAT=0x1600 RAW=0x1600 rmi2c:[180] handleSlave:entry: STATRAW=0x1600 rmi2c:[388] handleSlave: I2C_SLAVE_TX rmi2c:[410] handleSlave: I2C_SLAVE_TX slave TX : XRDY/XUDF rmi2c:[433] handleSlave: state = I2C_SLAVE_TX: SlaveTxSentBytes=2 data=0x37(7) <<<<<<<<<<<<<<<<<<<<<<<<<<<<< {PIL} 2nd byte ok rmi2c:[1626] Reg_InterruptsClear. AM335x_I2C_IRQSTAT_RAW= 0x1200 rmi2c:[1627] Reg_InterruptsClear. Writing to AM335x_I2C_STAT 0x0 <<<<<<<<<<< not clearing anything rmi2c:[638] omap_wait_status: <SLAVE> done handleSlave rmi2c:[671] omap_wait_status: <SLAVE> done rmi2c: [958] SlaveIsThread: gotIntr[I2C_INDEX_SLAVE]==1 - released from omap_wait_status() rmi2c: [1002] SlaveIsThread: end while: Slave TX STAT=0x1200 RAW=0x1200 rmi2c:[596] omap_wait_status: mode=<SLAVE> entry: CON=0x8000 IE set= 0x29e STAT_RAW= 0x1200 <<<<<<<<<<<<< but now, only BB and AAS set rmi2c:[632] omap_wait_status: <SLAVE> AM335x_I2C_AAS slave state=12 STAT=0x1200 RAW=0x1200 rmi2c:[180] handleSlave:entry: STATRAW=0x1200 rmi2c:[388] handleSlave: I2C_SLAVE_TX rmi2c:[447] handleSlave: SLAVE TX ??? que? statreg=0x1200 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< nothing to handle since XDRY / XUDF both 0 rmi2c:[1626] Reg_InterruptsClear. AM335x_I2C_IRQSTAT_RAW= 0x1200 rmi2c:[1627] Reg_InterruptsClear. Writing to AM335x_I2C_STAT 0x0 rmi2c:[638] omap_wait_status: <SLAVE> done handleSlave rmi2c:[671] omap_wait_status: <SLAVE> done rmi2c: [958] SlaveIsThread: gotIntr[I2C_INDEX_SLAVE]==1 - released from omap_wait_status() rmi2c: [1002] SlaveIsThread: end while: Slave TX STAT=0x1200 RAW=0x1200