EMMC - can't get READ_RDY

I'm writing a driver for the SD/MMC host interface, working from information in the BCM2835 datasheet and the official SD card "simplified specifications". I'm able to set up the card, fetch the CID and CSD registers, and select it into transfer state. I issue commands SET_BLOCKLEN(512) and READ_SINGLE_BLOCK(0), which terminate with no errors. I then loop waiting for the READ_RDY bit to appear in the Interrupt register to show that data is available in the fifo, but it never sets (nor does any error bit - the Interrupt register just stays at zero). And yes, I have enabled all the bits in the Irpt_mask register.

I've probably missed out some small but essential step. Any suggestions?

/* *The Arasan has a bugette whereby it may lose the content of * successive writes to registers that are within two SD-card clock * cycles of each other (a clock domain crossing problem). * It seems, however, that the data register does not have this problem. * (Which is just as well - otherwise we'd have to nobble the DMA engine * too) */ while (get_timer(last_write) < twoticks_delay) {} writel(val, host->ioaddr + reg); last_write = get_timer(0);

Thanks for the pointers to source code. I prefer not to read linux source unless I'm really really desperate. And it turns out there is more to the RiscOS SD driver than just that bit of assembly - in cddl/RiscOS/Sources/HWSupport/SD/SDIODriver there's another 5681 lines of C source and headers.

I don't think it's a delay problem - however much delay I put between register writes, the READ_RDY bit never appears. But looking at RiscOS, they are not using that bit, but instead polling bit 11 in the Status register to see when data is available. This contradicts the Broadcom datasheet which (a) says that bit is "reserved", and (b) says the status register is "not recommended for polling". However it is consistent with the SD HCI spec for a "Buffer Read Enable" bit at that position.

Using Status bit 11, my driver can now read blocks successfully from the card. Not the best solution, though, because that bit can't generate an interrupt.

Cycl0ne, are you managing to get a READ_RDY interrupt somehow, or are you using DMA and letting the DMA interrupt signal the availability of data?

9pi wrote:Thanks for the pointers to source code. I prefer not to read linux source unless I'm really really desperate. And it turns out there is more to the RiscOS SD driver than just that bit of assembly - in cddl/RiscOS/Sources/HWSupport/SD/SDIODriver there's another 5681 lines of C source and headers.

I don't think it's a delay problem - however much delay I put between register writes, the READ_RDY bit never appears. But looking at RiscOS, they are not using that bit, but instead polling bit 11 in the Status register to see when data is available. This contradicts the Broadcom datasheet which (a) says that bit is "reserved", and (b) says the status register is "not recommended for polling". However it is consistent with the SD HCI spec for a "Buffer Read Enable" bit at that position.

Using Status bit 11, my driver can now read blocks successfully from the card. Not the best solution, though, because that bit can't generate an interrupt.

Cycl0ne, are you managing to get a READ_RDY interrupt somehow, or are you using DMA and letting the DMA interrupt signal the availability of data?

Thanks, I've read the errata but there's nothing about EMMC there. Maybe I should add something?

Strictly speaking, the armv6 doesn't have a memory read barrier; I think the Data Memory Barrier is the closest equivalent. I haven't yet put in DMBs between accesses to different peripherals. Not yet convinced that they are necessary, as I'm doing all I/O access through MMU addresses marked as non-cached & non-buffered, and I haven't seen any suggestion that this arm core does out-of-order execution of instructions. Also it's not clear to me what is a "peripheral" - for example are the system and ARM timers on the AXI bus?

The BCM doc may be wrong (there are many errors, check the errata on the wiki page). I assume you are polling at the moment, what you can do is enable all interrupts and poll the interrupt status register to see what if anything is changing. I sorted out a uart doc bug this way. Some of those interrupts are used by the GPU and appear to be quite active, so when you see those just ignore them (dont enable them in your mask).

In particular if the BCM docs have "its just like this chip X except we dont support Y", I have found at least two instances where you actually have to set the Y bits to make the thing work even though they say you dont. So if this module is derived from a well known module, get the docs for the well known module and program it as if it were the real module, maybe you will find a missing bit needed to make it work.

gin wrote:Is there anything I'm missing in my initialization sequence or does anyone know of any pitfalls they ran into trying to get this setup?

Initialisation - what I do is:- write Reset Host Controller to Control1, wait for bit to clear- after writing clock divisor to Control1, wait for Clock Stable bit to set (and then wait another millisecond for good measure)

I start with a slower clock initially until the card is selected, but with SD cards this may not be strictly necessary.

If CMD3 is really the first thing you're sending the card, it may be a problem. The complete command sequence I use to set up the card initially is:

DexOS wrote:Once you get your code fully working, a writeup of basic steps would be help full, no need for code, just basic steps.Thanks.

It seems the problem was underestimating the subtlety of the READ_RDY interrupt. It doesn't mean "data is available in the read fifo", it means "there has been a transition from data not available to data available". So before waiting for the READ_RDY interrupt, it's prudent to check whether data is already available, using the undocumented (by Broadcom) status bit 11.

An example of working code to read len words to address buf (word-aligned) after issuing a READ_SINGLE_BLOCK or READ_MULTIPLE_BLOCK command is:

You can leave out the BLOCKLEN and BUS_WIDTH if you keep the default values.

Thanks you so much! I got it working! I realized I was looking at the SPI documentation and was looking at eMMC startup sequences. Your steps worked perfectly.!

One thing I noticed for other people trying to get this working is to start out with decent sleeps (I did 10ms between command and when I tried to look at the response). I think it can be dialed down (I don't know if checking CRC and CMD add any response time) but just waiting on the INTERRUPT or STATUS flags wasn't always working for me (again I may be doing something dumb I might catch when I run through this again to clean it up).

Again - thanks a ton for the guide on how to get it set up! I'm excited to be able to right a file system for this thing!

Most common problem here seems to be that the ACMD41 typically takes a significant time - 100's of milliseconds. From what you list above, you are getting the response for the ACMD41 after the next command - the 00ff8000 response is the card agreeing on the voltage levels which you sent in the ACMD41 command.

The thing that threw me originally is that the CMD_DONE interrupt does not necessarily mean that the response has been received from the card, just that the command has been sent, so you cannot just assume the resp value is valid at that point, especially for ACMD41. The spec says that you need to leave up to 2 seconds for the response from ACMD41. I think my code waits 500ms after sending the command before checking interrupt for command complete and the response value, although I don't have access to it right now to check.

hldswrth wrote:Most common problem here seems to be that the ACMD41 typically takes a significant time - 100's of milliseconds. From what you list above, you are getting the response for the ACMD41 after the next command - the 00ff8000 response is the card agreeing on the voltage levels which you sent in the ACMD41 command.

The thing that threw me originally is that the CMD_DONE interrupt does not necessarily mean that the response has been received from the card, just that the command has been sent, so you cannot just assume the resp value is valid at that point, especially for ACMD41. The spec says that you need to leave up to 2 seconds for the response from ACMD41. I think my code waits 500ms after sending the command before checking interrupt for command complete and the response value, although I don't have access to it right now to check.

Thanks much for the reply.Adding the delay did the job. Now I shall get it reading and writing data.Thanks everyone for the help.

hldswrth wrote:Most common problem here seems to be that the ACMD41 typically takes a significant time - 100's of milliseconds. From what you list above, you are getting the response for the ACMD41 after the next command - the 00ff8000 response is the card agreeing on the voltage levels which you sent in the ACMD41 command.

The thing that threw me originally is that the CMD_DONE interrupt does not necessarily mean that the response has been received from the card, just that the command has been sent, so you cannot just assume the resp value is valid at that point, especially for ACMD41. The spec says that you need to leave up to 2 seconds for the response from ACMD41. I think my code waits 500ms after sending the command before checking interrupt for command complete and the response value, although I don't have access to it right now to check.

Thanks much for the reply.Adding the delay did the job. Now I shall get it reading and writing data.Thanks everyone for the help.

- Brijen

Hi, I am stuck in the same place, getting the 0x00ff8000 response after ACMD41, but i am not able to get it working after that. If I try to execute CMD2,CMD3 or CMD11 I get status 0x01ff0001, and if I understand it correctly, that means there is error executing last command. That is also signaled by interrupt status which is then 0x00018043 and after ACMD41 was 0x00000043. I wait for 500ms after every command just to be sure.

Brijen, can you please send your code after you get it working - successful switch data-transfer mode. Or if anyone can tell me what I am missing.