Freescale iMX53 and iMX6 ECSPI silicon bug

Update: We have repeated our tests on iMX6 and unfortunately the same silicon bug is also present on the iMX6, so everything you read here can be applied to the iMX6 as well...

We recently did a full rewrite of the Freescale ECSPI driver for our Opal i.MX53 BSP (now fully supporting all ECSPI features; master/slave mode, DMA, etc) and during testing we discovered a rather big problem with the ECSPI module inside the i.MX53...

For future reference and to prevent others from having to go through the same time-consuming research and analysis we decided to publish our findings through this blog post.

Problem Analysis

The ECSPI module shows unexpected behaviour when the bitcount is set to [(32 * n) + 1], where [n > 0].

As an example, let's walk through sending 1 x 33 bits.

In this case, we have to load the TXIFO with 2 UINT32s. The first UINT32 value contains 1 LSB and the 2nd UINT32 contains the remaining 32 bits.

For this transaction you'd have to configure the ECSPI registers as follows (33 bits, master mode, SS1, DRCTL don't care, loopback enable, no DMA, enable TC interrupt):

CONREG BURSTLENGTH      = 32
CONREG CHANNELSELECT    = 1
CONREG DRCTL            = 0
CONREG PREDIVIDER       = 0
CONREG POSTDIVIDER      = 0
CONREG CHANNELMODE      = 2
CONREG SMC              = 0
CONREG XCH              = 0
CONREG HT               = 0
CONREG EN               = 1

CONFIGREG HTLENGTH      = 0
CONFIGREG SCLKCTL       = 0
CONFIGREG DATACTL       = 0
CONFIGREG SSPOL         = 2
CONFIGREG SSCTL         = 2
CONFIGREG SCLKPOL       = 2
CONFIGREG SCLKPHA       = 0

INTREG TCEN             = 1
INTREG ROEN             = 0
INTREG RFEN             = 0
INTREG RDREN            = 0
INTREG RREN             = 0
INTREG TFEN             = 0
INTREG TDREN            = 0
INTREG TEEN             = 0

DMAREG RXTDEN           = 0
DMAREG RXDMALENGTH      = 0
DMAREG RXDEN            = 0
DMAREG RXTHRESHOLD      = 0
DMAREG TEDEN            = 0
DMAREG TXTHRESHOLD      = 0

STATREG TC              = 0
STATREG RO              = 0
STATREG RF              = 0
STATREG RDR             = 0
STATREG RR              = 0
STATREG TF              = 0
STATREG TDR             = 1
STATREG TE              = 1

PERIODREG CSDCTL        = 0
PERIODREG CSRC          = 0
PERIODREG SAMPLEPERIOD  = 0

TESTREG LBC             = 1
TESTREG RXCNT           = 0
TESTREG TXCNT           = 0

Load the TXFIFO with 2 UINTs as follows:

[0] 0x00000001
[1] 0xAAAAAAAA

The STAT & TEST registers reflect this:

STATREG TC              = 0
STATREG RO              = 0
STATREG RF              = 0
STATREG RDR             = 0
STATREG RR              = 0
STATREG TF              = 0
STATREG TDR             = 0
STATREG TE              = 0

TESTREG LBC             = 1
TESTREG RXCNT           = 0
TESTREG TXCNT           = 2

At this moment start the transfer by setting the XCH bit in the CONREG. This is when strange things start to happen:

CONREG BURSTLENGTH      = 32
CONREG CHANNELSELECT    = 1
CONREG DRCTL            = 0
CONREG PREDIVIDER       = 0
CONREG POSTDIVIDER      = 0
CONREG CHANNELMODE      = 2
CONREG SMC              = 0
CONREG XCH              = 0
CONREG HT               = 0
CONREG EN               = 1

CONFIGREG HTLENGTH      = 0
CONFIGREG SCLKCTL       = 0
CONFIGREG DATACTL       = 0
CONFIGREG SSPOL         = 2
CONFIGREG SSCTL         = 2
CONFIGREG SCLKPOL       = 2
CONFIGREG SCLKPHA       = 0

INTREG TCEN             = 1
INTREG ROEN             = 0
INTREG RFEN             = 0
INTREG RDREN            = 0
INTREG RREN             = 0
INTREG TFEN             = 0
INTREG TDREN            = 0
INTREG TEEN             = 0

DMAREG RXTDEN           = 0
DMAREG RXDMALENGTH      = 0
DMAREG RXDEN            = 0
DMAREG RXTHRESHOLD      = 0
DMAREG TEDEN            = 0
DMAREG TXTHRESHOLD      = 0

STATREG TC              = 1
STATREG RO              = 0
STATREG RF              = 0
STATREG RDR             = 1
STATREG RR              = 1
STATREG TF              = 0
STATREG TDR             = 1
STATREG TE              = 1

PERIODREG CSDCTL        = 0
PERIODREG CSRC          = 0
PERIODREG SAMPLEPERIOD  = 0

TESTREG LBC             = 1
TESTREG RXCNT           = 4
TESTREG TXCNT           = 0

As you can see from the above, the XCH bit is clear (good!), the TC interrupt fired (good!), but the RXFIFO contains not 2, but 4 UINT32s!

At this moment RXFIFO contains:

[0] 0x00000001
[1] 0x00000001
[2] 0x00000001
[3] 0xAAAAAAAA

As you can see, the first UINT32 from the TXFIFO is duplicated twice at the beginning of the RXFIFO.

The situation is slightly different with the 65 bit case. In that case the TC interrupt never fires:

We load the TXFIFO with 65 bits as follows:

[0] 0x00000001
[1] 0xAAAAAAAA
[2] 0x55555555
CONREG BURSTLENGTH      = 64

TESTREG LBC             = 1
TESTREG RXCNT           = 0
TESTREG TXCNT           = 3

All other registers are the same as per above.

Now we start the transfer by setting the XCH bit in the CONREG. This is when more strange things start to happen:

CONREG BURSTLENGTH      = 64
CONREG CHANNELSELECT    = 1
CONREG DRCTL            = 0
CONREG PREDIVIDER       = 0
CONREG POSTDIVIDER      = 0
CONREG CHANNELMODE      = 2
CONREG SMC              = 0
CONREG XCH              = 1
CONREG HT               = 0
CONREG EN               = 1

CONFIGREG HTLENGTH      = 0
CONFIGREG SCLKCTL       = 0
CONFIGREG DATACTL       = 0
CONFIGREG SSPOL         = 2
CONFIGREG SSCTL         = 2
CONFIGREG SCLKPOL       = 2
CONFIGREG SCLKPHA       = 0

INTREG TCEN             = 1
INTREG ROEN             = 0
INTREG RFEN             = 0
INTREG RDREN            = 0
INTREG RREN             = 0
INTREG TFEN             = 0
INTREG TDREN            = 0
INTREG TEEN             = 0

DMAREG RXTDEN           = 0
DMAREG RXDMALENGTH      = 0
DMAREG RXDEN            = 0
DMAREG RXTHRESHOLD      = 0
DMAREG TEDEN            = 0
DMAREG TXTHRESHOLD      = 0

STATREG TC              = 0
STATREG RO              = 0
STATREG RF              = 0
STATREG RDR             = 1
STATREG RR              = 1
STATREG TF              = 0
STATREG TDR             = 1
STATREG TE              = 1

PERIODREG CSDCTL        = 0
PERIODREG CSRC          = 0
PERIODREG SAMPLEPERIOD  = 0

TESTREG LBC             = 1
TESTREG RXCNT           = 5
TESTREG TXCNT           = 0

As you can see from the above, XCH stays set, the TC interrupt never fires and the TESTREG shows there are 5 UINT32s received in the RXFIFO (note that loopback is enabled!).

The contents of the RXFIFO are:

[0] 0x00000001
[1] 0x00000001
[2] 0xAAAAAAAA
[3] 0x00000001
[4] 0x55555555

As you can see, the 1st UINT32 from the TXFIFO is duplicated in the RXFIFO at position 0, 1 and 3.

The behavior is slightly different when doing the same but with SSCTL set to 0, but it's still not doing the right thing.

When doing a transfer of 33 bits with SSCTL set to 0, only 32 bits are sent:

Load TXFIFO with:

[0] 0x00000001
[1] 0xAAAAAAAA

Set XCH, wait for TC, result:

TXFIFO still contains:

[0] 0xAAAAAAAA

RXFIFO contains:

[0] 0x00000001
[1] 0x00000001

Note that SSCTL=0 works correct for bitcounts that are not [(32*n)+1] where [n>0].

Here are the scope captures of 32, 33, 34 and 35 bits, showing the i.MX53's bad behavior when transferring 33 bits:


yellow = chip select
blue = clock
purple = data

Freescale's response

We of course asked Freescale to verify this bug and they have. This is their response:

We are able to reproduce it. Unfortunately, there's no workaround for this error.

Other i.MX CPUs affected by this silicon bug?

We have verified the i.MX25 is NOT affected by this bug (but the i.MX25 contains an older version of the SPI block).
We have not been able to try our tests on the i.MX6 yet, but since the i.MX6 contains the exact same ECSPI block as the i.MX53, we suspect this issue will also be present on the i.MX6 series CPUs.
The issue is also present on the iMX6.

We have asked Freescale to update the errata sheets for the affected processor(s), but so far the erratas are silent about this issue.

More ECSPI silicon bugs?

Unfortunately; yes. Also the RX DMA TAIL mechanism (allowing non 32-word aligned DMA transfers to complete) does not work correctly. Here is the official response from Freescale:

We have confirmed this with the SDMA script owner. The script does not support the 32bytes unaligned DMA transfer.
This is a documentation bug. This will be corrected on documentation on next release.

Interesting way to solve silicon bugs... ;)

Conclusion

The ECSPI module inside the iMX53 CAN NOT BE USED for SPI transfers on 32 bit edges + 1.

When BURSTLENGTH is set to [(32 * n) + 1], the following happens:

For n = 0 (bitcount 1): All works fine!
For n = 1 (bitcount 33): TC interrupt fires, XCH bit clears, but more data than expected in RXFIFO (first UINT32 from TXFIFO duplicated).
For n > 1 (bitcount 65, 97, 129, etc): TC interrupt DOES NOT fire, XCH bit stays set, and also more data than expected in RXFIFO (first UINT32 from TXFIFO duplicated).

Comments

Does this applies to imx6 as spi slave too ?

Yes, the same bug is present on the iMX6 unfortunately.

Have they updated yet?

No, the bug hasn't even made it to the errata yet, so surely they didn't update the silicon... :-(