iic_transfer: "Send no start" flag
Dave Higton (1515) 3526 posts |
I’m worried by this flag. The documentation for iic_transfer says:
As far as I know, when a stop bit has been sent, a new transfer must address a device. What would respond otherwise? And a stop bit has been sent, because the iic_transfer specification says so. Not only that, but, if you use an IIC controller rather than bit-banging, there seems to be no way not to send a stop bit. I’m currently looking at two IIC controllers: the BCM2835 (Raspberry pi) and the LPC1114 (a little ARM single chip micro). The controllers are entirely different, but neither is AFAIAA capable of not sending a stop bit at the end of a transfer. It may be worth mentioning that there are some data sheets for IIC devices that send start, the device address, the address within the device, another start (note: no stop before it!), then data, and finally a stop. I can’t see any way to get either of the controllers I know to do that. Can anyone tell me if there is any RISC OS software out there that currently makes use of this flag bit? |
Jeffrey Lee (213) 6048 posts |
In that case, the specification is wrong. The docs aren’t entirely clear on the matter (as usual), but a stop bit should only be sent at the end of the last iic_transfer block in the list. And there’s the implicit assumption that if the nostart flag is set, the device address and R/W flag must be identical to that used in the previous iic_transfer block (since the address and R/W flag will only get sent over the bus if the start bit is being sent). If the IIC controller in the Pi needs to be told how much data is being sent in any particular transfer (e.g. as is the case with the OMAP3/OMAP4 controller) then you’ll need to scan ahead in the iic_transfer list and count up the length field of all the transfers until you reach the next transfer with the nostart flag not set (or until you reach the end of the list). Then make your FIFO fill/empty routine go from one iic_transfer to the next until all the data has been sent/received.
The kernel does, for one – see WriteSubBlock in Kernel.s.PMF.i2cutils. |
Jeffrey Lee (213) 6048 posts |
The OMAP & IOP IIC controllers allows you to control whether a stop bit is sent at the end of each transfer. So it’s not an unreasonable part of the API. I don’t think it’s a serious issue if the controller in the Pi forces stop bits to be sent at the end of each transfer, as the way the transfers are queued by the kernel, and the fact that the CPU is the only bus master, means that there won’t be any problems with multi-part transfers (e.g. EEPROM reads) getting messed up by other transfers happening inbetween. |
Dave Higton (1515) 3526 posts |
@Jeffrey: I see you updated the iic_transfer page yesterday. Thanks for that. Next question: I see that HAL_IICTransfer only returns a return code, and I see that SWI IIC_Op says that, on exit, the transfer list is updated, if applicable. But there’s no way to indicate on which transfer it failed, nor is there any specification of how the transfer list might be updated. This might be academic in the case of a list where the nostart bit is set, because AFAICS all that does is transfer more data. I find it difficult to imagine why one would want to break a transfer into several sub-transfers. Maybe some controllers can only handle a small number of bytes, but provide a mechanism to chain transfers without stop or start bits, so RISC OS has to cope with the least capable hardware? If that’s true, perhaps it would be useful to have a call to interrogate the controller for maximum transfer length that it supports – which would have to be returned per bus, although I also read that RISC OS only supports one bus, which simplifies it. I’ve just read again the BCM2835 data sheet section on the serial controller (claimed to be IIC compatible), and there is no published way to avoid sending a start at the beginning of a transfer or a stop at the end. OTOH it supports transfers up to 65535 bytes, which is not bad, so transfers with nostart set can be simply aggregated up to a maximum of 65535. Anything longer than that in the Raspberry Pi without stop and start bits will require bit-banging. |
Jeffrey Lee (213) 6048 posts |
For updating the transfer list, I believe the only action is to update the checksum value(s) if the ‘checksumonly’ flag is set. For reporting that a transfer has failed, yes, you’re quite right, there’s no way of indicating where it failed.
Being able to break things into sub-transfers allows you to treat the transfer list like a DMA scatter list. E.g. if you’re writing N bytes to an EEPROM, you’d have one sub-transfer to write the address within the device, and then another sub-transfer (with the nostart flag set) to write the data. That way if there’s any abstraction around the EEPROM reads/writes, the code doesn’t have to deal with shuffling the data around into one contiguous buffer in order to perform the IIC operation.
In that situation, I would have assumed the driver would take care of splitting up the transfers as appropriate.
The OMAP has the same 65535 byte limit, and that hasn’t caused any problems (yet!) |
Dave Higton (1515) 3526 posts |
Interestingly, and unexpectedly, I have discovered that it is possible for the RPi serial (IIC) controller to send a start in the middle without a stop: (My apologies if that looks huge – YMMV. The JPEG is set to 600 dpi, which should have the image dimensions in the ballpark of 100mm per side. I don’t AFAIK have any way to convert to PNG.) Anyway: note that the picture shows an IIC start, the device address with write, another IIC start, the device address with read, and the non-zoomed version goes on to show lots of bytes being read from the device. The snag is that I don’t know how I’ve achieved this. It wasn’t what I expected. Work continues! |
Rick Murray (539) 13840 posts |
My memory is hazy for I’ve not bit-bashed IIC for teletext for 6-7 years, however… [of course, given my memory, I reserve the right to be very wrong |
Dave Higton (1515) 3526 posts |
You’re not very wrong; you’re close. For reads: Start, dev+write, reg (which generalises to address within device; note that the length of this field depends on device size), start, dev+read, read-read-read, stop. For writes: Start, dev+write, address within device, write-write-write, stop. The first thing after a start always has to be the device address and the read/write flag. You have to write the address within the device. It isn’t possible to read it, nor to set it by means of a read operation. If the address withn the device isn’t sent, it normally continues from the last-written or last-used-plus-one address, and auto increments, normally through the entire address space of the device in the case of reads, then wraps to zero. The write address, by contrast, auto increments and wraps within the page for an EEPROM device, the page being that which is written in one operation. |
Dave Higton (1515) 3526 posts |
The key appears to be to initiate another transfer after all data bytes have been transferred, but before Transfer Active ceases to be true. It’s not documented in the chip’s data sheet, but it appears to work. The other interesting thing I found is that there can be a surprisingly long delay between initiating the first transfer, and the Transfer Active flag going true. I’ve seen loop counts over 50. The trace pictured above was before I knew to loop round waiting for TA, which is why there are no data bytes after the first address. |
Dave Higton (1515) 3526 posts |
Sorry to have to ask this, but I can’t remember how to do it, and it’s important… How do I (on the Raspberry Pi, if it makes any difference) store the interrupt enable state, disable interrupts, and restore the interrupt state? The IIC code cannot be re-entrant, so I have to use a flag to prevent a second call. Clearly I have to have interrupts disabled around manipulation of that flag. |
Ben Avison (25) 445 posts |
You’re thinking of the MRS and MSR instructions. However, you don’t have to worry about it in this case – the kernel takes care of re-entrancy checks for you. |
Dave Higton (1515) 3526 posts |
Thanks, Ben, I didn’t expect it was going to be that easy! |