I'd like to share some experiences I've had with the 9-bit mode of the UARTs inside the iMX6.
For a client we have a custom protocol where several devices listen to a shared serial data line. Message frames are marked using a 9th-bit: the first byte of a frame has the 9th bit set, the other bytes have it cleared. There is a bus master passing around a token. Upon reception of the token, participants on the bus have to react within about 1.5 ms and can transmit what they want to transmit, then need to pass back the token to the bus master. A bus reset is marked as the shared data line being held low for some time.
We're implementing this in Linux, using a Yocto based distribution with a Linux 4.1.x kernel.
At first I thought that receiving frames with a 9-th bit would be the most difficult task. This turned out to be the easiest actually :smileyhappy:. With some minor modifications to the existing imx.c driver (in drivers/tty/serial) I am able to read the 9th bit. Through the Linux TTY layers, this 9th bit is marked as a parity error and passed to user space using some escape sequences. I can also detect a bus reset (as a UART "break" condition). Receiving seems to works fine.
One thing I ran into is that the reaction time from receiving the token from the bus master until we get a message back out the UART is quite long, about 3 milliseconds (this needs to be about two times faster). This turned out to be a problem with the FIFO and the "age timer" interrupt. When receiving a single character, no interrupt is generated because the FIFO logic waits until more characters arrive (up to 16), or until the age time expires. This takes 8 character times, too long for us. The solution was to set the receive FIFO interrupt level (RXTL in the UFCR register) to 1. This effectively disabled the age timer and reduced the latency for us from about 3000 us to 200 us or so.
Now transmitting 9-bit characters appears a lot more challenging! You can specify the 9th bit in TXB8 in register IMX21_UMCR, but it appears the implementation in hardware is quite limited:
* this bit is common for *all* bytes transmitted out of the UART, there is no 9th bit in the TX FIFO
* this bit is unbuffered, if you change it while the 9th bit is being transmitted out of the UART, you see the effect immediately! So you can end up with the 9th bit being low for half of the time and high for the other half of the time, if you happen to change it at the wrong time!
* it is not trivial to set it for just one byte in a frame: my initial plan was to set the bit, then clear it once the first byte was transmitted, in the transmit-complete interrupt. It turns out the "transmitter complete" interrupt is generated already before the stop bit has been transmitted, so basically *just* before the 9th bit is transmitted! So the text in the user manual is a bit misleading about this. This is basically the worst possible time for the interrupt to occur...
Finally, something that helped a lot to speed up the debug cycle with Yocto, is to build the modified driver separately from the Linux kernel. We used the "hello-mod" example for this. I copied the driver from Linux, modified it and made sure the standard imx.c driver is no longer automatically attached to the particular UART, by modifying "compatible" name in the device tree file.
Perhaps the information above helps other people working with the 9-bit UART mode.
Myself, I'm still struggling with transmitting the 9-th bit properly and reliably.
Thanks for writing this post. Especially the part about the TX interrupt being fired too soon is indeed pretty offputting. (as you would need to use timeouts to correct it, seriously affecting performance).
Just curious, did you succeed in getting it reliable and stable after all for your usecase?