RTS / CTS of UART in i.MX8

After removing the voltage divider before WLAN_EN and BT_EN described in Wi-Fi / Bluetooth bringup failure due to voltage divider, Wi-Fi worked properly but Bluetooth didn’t. It turned out to be an issue in hardware flow control (RTS / CTS) of UART module used for Bluetooth.

The problem

After updating the device tree and pinmux for Bluetooth, it can read local version and construct file path of firmware correctly, but timeout in downloading firmware:

[   10.225930] (hci_tty): inside hci_tty_init
[   10.230294] (hci_tty): allocated 239, 0
[   11.283024] Bluetooth: Core ver 2.22
[   11.291184] Bluetooth: HCI device and connection manager initialized
[   11.297581] Bluetooth: HCI socket layer initialized
[   11.302483] Bluetooth: L2CAP socket layer initialized
[   11.307565] Bluetooth: SCO socket layer initialized
[   11.320108] (stc):  chnl_id list empty :4
[   11.320121] (stk) : st_kim_start
[   11.429318] (stk) :ldisc_install = 1
[   11.433544] (stc): st_tty_open
[   11.437217] (stk) :line discipline installed
[   11.443662] (stk) :ti-connectivity/TIInit_11.8.32.bts
[   14.889097] (stk) :response timeout/signaled during fw download
[   14.889144] (stk) :download firmware failed
[   14.895301] (stk) :ldisc_install = 0
[   14.899913] (stc): st_tty_close
[   15.010623] (stk) :ldisc_install = 1
[   15.014808] (stc): st_tty_open
[   15.018473] (stk) :line discipline installed
[   15.024931] (stk) :ti-connectivity/TIInit_11.8.32.bts
[   18.441153] (stk) :response timeout/signaled during fw download
[   18.441237] (stk) :download firmware failed
[   18.447384] (stk) :ldisc_install = 0
[   18.452352] (stc): st_tty_close
[   18.563189] (stk) :ldisc_install = 1
[   18.567503] (stc): st_tty_open
[   18.571304] (stk) :line discipline installed
[   18.578099] (stk) :ti-connectivity/TIInit_11.8.32.bts
[   21.993065] (stk) :response timeout/signaled during fw download
[   21.993115] (stk) :download firmware failed
[   21.999229] (stk) :ldisc_install = 0
[   22.003712] (stc): st_tty_close
[   22.114387] (stk) :ldisc_install = 1
[   22.118485] (stc): st_tty_open
[   22.122126] (stk) :line discipline installed
[   22.128549] (stk) :ti-connectivity/TIInit_11.8.32.bts
[   25.545340] (stk) :response timeout/signaled during fw download
[   25.550477] (stk) :download firmware failed
[   25.556642] (stk) :ldisc_install = 0
[   25.561862] (stc): st_tty_close
[   25.672592] (stk) :ldisc_install = 1
[   25.676935] (stc): st_tty_open
[   25.680607] (stk) :line discipline installed
[   25.687076] (stk) :ti-connectivity/TIInit_11.8.32.bts
[   29.097130] (stk) :response timeout/signaled during fw download
[   29.102354] (stk) :download firmware failed
[   29.108441] (stk) :ldisc_install = 0
[   29.113030] (stc): st_tty_close
[   29.223777] (stk) :ldisc_install = 1
[   29.227927] (stc): st_tty_open
[   29.231613] (stk) :line discipline installed
[   29.238036] (stk) :ti-connectivity/TIInit_11.8.32.bts
[   32.649118] (stk) :response timeout/signaled during fw download
[   32.654232] (stk) :download firmware failed
[   32.660286] (stk) :ldisc_install = 0
[   32.664766] (stc): st_tty_close
[   32.670438] Bluetooth: st_register failed -22

Interesting enough, by enabling the following debug print in download_firmware(), the firmware could be downloaded successfully:

diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c
index 048f2fd17275..11e675f9fa9f 100644
--- a/drivers/misc/ti-st/st_kim.c
+++ b/drivers/misc/ti-st/st_kim.c
@@ -321,7 +321,7 @@ static long download_firmware(struct kim_data_s *kim_gdata)
        len -= sizeof(struct bts_header);
 
        while (len > 0 && ptr) {
-               pr_debug(" action size %d, type %d ",
+               printk(" action size %d, type %d ",
                           ((struct bts_action *)ptr)->size,
                           ((struct bts_action *)ptr)->type);
 

Apparently the debug print changes the timing of firmware download and it seems like a flow control issue. So I probed RTS and CTS pins and found there were no signal on these two pins during the download.

Direction is the key

Currently we are using the pinmux and presuming the direction between i.MX8MM and Bluetooth module as below:

This matches the pin attributes on Bluetooth UART side from WL1835MOD datasheet:

However, on i.MX 8M Mini reference manual, RTS_B is described as INPUT signal:

whereas CTS_B is described as OUTPUT signal:

Both of them have clear statement that the operation is the same regardless of whether the UART is in DTE or DCE mode. See the screenshot below on how DCE and DTE mode affects the routing of external signals to the UART model:

What makes it even confusing is, on i.MX 8QuadMax reference manual, these two pins have opposite directions - RTS_B is O (output) and CTS_B is I (input):

In our use case, Bluetooth UART is definitely DTE, so UART module on i.MX 8M Mini side should be in DCE mode. Hence, RTS_B should be an input signal and CTS_B should be a output. The correct connection should be:

The fix

Tried to swap RTS_B and CTS_B pins in pinmux but it didn’t work. Confirmed with FAE that these two pins cannot be swapped by pinmux. So we ended up by changing the layout of hardware to make them have correct directions.

Similar to i.MX6

UART on i.MX 8M Mini is much more similar to UART on i.MX6, so the same confusing happens there as well. See the following posts as reference:

Have fun!

comments powered by Disqus