/* * (c) danielinux 2019 * * GPLv.2 * Based on work by Chuck M. (see https://github.com/ChuckM/stm32f4-sdio-driver/) * * Permission to release under the terms of GPLv2 are granted by the * copyright holders. * * See LICENSE for details */ /* * SDIO Bus Driver layer. This code sends commands and drives * the SDIO peripheral on the STM32F4xx, there is a layer above * this, the SD Card driver, which uses this driver to talk to * SD Cards. The SPI driver can also talk to SD Cards, hence the * split at this layer. * * Note that the simple implementation for the SDIO driver runs * in a 'polled' mode. This is easier to explain and debug and * sufficient for the first few projects. A more sophisticated * version with DMA and interrupts will follow. * * * */ #include #include "system.h" #include "sdcard.h" #define SDCARD_BLOCK_SIZE 512 #ifndef NULL #define NULL (void *)(0x00000000) #endif /* * Helper defines to pull out various bit fields of the CSD for * the size calculation. */ #define SDIO_CSD_VERSION(x) stm32_sdio_bit_slice(x->csd, 128, 127, 126) #define SDIO_CSD1_CSIZE_MULT(x) stm32_sdio_bit_slice(x->csd, 128, 49, 47) #define SDIO_CSD1_RBLKLEN(x) stm32_sdio_bit_slice(x->csd, 128, 83, 80) #define SDIO_CSD1_CSIZE(x) stm32_sdio_bit_slice(x->csd, 128, 73, 62) #define SDIO_CSD2_CSIZE(x) stm32_sdio_bit_slice(x->csd, 128, 69, 48) /* * Conveniently swaps the bytes in a long around * used by the SCR code. */ #define byte_swap(val) \ asm("rev %[swap], %[swap]" : [swap] "=r" (val) : "0" (val)); static const uint32_t sdio_pins[5] = { 8, 9, 10, 11, 12}; /* GPIOC */ static const uint32_t sdio_cmd_pin = 2; /* GPIOD2 */ static const uint32_t sdio_detect_pin = 8; /* GPIOA8 */ static const uint32_t sdio_af = 12; static struct dev_sd SD; /* * sdio_bus * * Set the bus width and the clock speed for the * SDIO bus. * * Returns 0 on success * -1 illegal bit specification * -2 illegal clock specification */ static int stm32_sdio_bus(struct dev_sd *sd, int bits, enum SDIO_CLOCK_DIV freq) { int clkreg = 0; switch (bits) { case 1: clkreg |= SDIO_CLKCR_WIDBUS_1; break; case 4: clkreg |= SDIO_CLKCR_WIDBUS_4; break; default: return -1; } switch (freq) { case SDIO_24MHZ: break; case SDIO_16MHZ: clkreg |= 1; break; case SDIO_12MHZ: clkreg |= 2; break; case SDIO_8MHZ: clkreg |= 8; break; case SDIO_4MHZ: clkreg |= 10; break; case SDIO_1MHZ: clkreg |= 46; break; case SDIO_400KHZ: clkreg |= 118; break; default: return -2; } clkreg |= SDIO_CLKCR_CLKEN; SDIO_CLKCR = clkreg; return 0; } /* * Reset the state of the SDIO bus and peripheral. This code tries * to reset the bus *AND* the card if one is plugged in. The bus * can be reset by software but the card is reset by powering it down. * * The SDIO_POWER_STATE tells the code which state to leave the bus in, * powered up or powered down. * * If the state is POWER_ON, then the bus is reset to 400Khz, 1 bit wide * which is what he spec requires. Once the type and capabilities of the * card have been determined, it can be upgraded. */ void stm32_sdio_reset(struct dev_sd *sd, enum SDIO_POWER_STATE state) { /* Step 1 power off the interface */ SDIO_POWER = SDIO_POWER_PWRCTRL_PWROFF; /* reset the SDIO peripheral interface */ APB2_CLOCK_RST |= SDIO_APB2_CLOCK_ER_VAL; APB2_CLOCK_RST &= ~SDIO_APB2_CLOCK_ER_VAL; if (state == SDIO_POWER_ON) { SDIO_POWER = SDIO_POWER_PWRCTRL_PWRON; stm32_sdio_bus(sd, 1, SDIO_400KHZ); // required by the spec } } /* * The error message catalog. */ static const char *__sdio_error_msgs[] = { "Success", "Command Timeout", // -1 "Command CRC Failure", // -2 "Soft Timeout (No Response)", // -3 "Data CRC Failure", // -4 "RX FIFO Overrun", // -5 "TX FIFO Underrun", // -6 "Unsupported Card" // -7 }; #define SDIO_ESUCCESS 0 #define SDIO_ECTIMEOUT -1 #define SDIO_ECCRCFAIL -2 #define SDIO_ENORESP -3 #define SDIO_EDCRCFAIL -4 #define SDIO_ERXOVERR -5 #define SDIO_ETXUNDER -6 #define SDIO_EBADCARD -7 #define SDIO_EUNKNOWN -8 /* * Return a text string description of the error code. */ const char * stm32_sdio_errmsg(int err) { return (err <= SDIO_EUNKNOWN) ? (const char *) "Unknown Error" : __sdio_error_msgs[0-err]; } /* * stm32_sdio_bit_slice - helper function * * A number of the things the SDIO returns are in bit * fields. This code is designed to slice out a range * of bits and return them as a value (up to 32 bits * worth). */ uint32_t stm32_sdio_bit_slice(uint32_t a[], int bits, int msb, int lsb) { uint32_t t; int i; if (((msb >= bits) || (msb < 0)) || (lsb > msb) || ((lsb < 0) || (lsb >= bits))) { return 0; } t = 0; for (i = msb; i > lsb; i--) { t |= (a[((bits-1) - i)/32] >> (i % 32)) & 0x1; t <<= 1; } t |= (a[((bits-1) - lsb)/32] >> (lsb % 32)) & 0x1; return t; } /* * A convienence define. These are the flags we care about when * sending a command. During command processing SDIO_STA_CMDACT * will be set. */ #define COMMAND_FLAGS (SDIO_STA_CMDSENT |\ SDIO_STA_CCRCFAIL |\ SDIO_STA_CMDREND |\ SDIO_STA_CTIMEOUT) /* * Send a command over the SDIO bus. * Passed a command (8 bit value) and an argument (32 bit value) * This command figures out if the command will return a short (32 bit) * or long (64 bit) response. It is up to the calling program to pull * data from the long response commands. * Passed: * cmd - Command to execute * int sdcard_sense(void *dev); * * arg - Argument to pass to the command * buf - pointer to a long aligned buffer if data * len - expected length of buffer (in bytes) */ int stm32_sdio_command(struct dev_sd *sd, uint32_t cmd, uint32_t arg) { uint32_t tmp_val; int error = 0; tmp_val = SDIO_CMD & ~0x7ff; // Read pre-existing state tmp_val |= (cmd & SDIO_CMD_CMDINDEX_MSK); // Put the Command in tmp_val |= SDIO_CMD_CPSMEN; // We'll be running CPSM switch(cmd) { case 0: tmp_val |= SDIO_CMD_WAITRESP_NO_0; break; case 2: case 9: tmp_val |= SDIO_CMD_WAITRESP_LONG; break; default: tmp_val |= SDIO_CMD_WAITRESP_SHORT; // the common case break; } /* If a data transaction is in progress, wait for it to finish */ while ((cmd != 12) & (SDIO_STA & (SDIO_STA_RXACT | SDIO_STA_TXACT)));; /* * EXECUTE: * o Reset all status bits * o Put ARG into SDIO ARG * o reset the error indicator * o Enable all interrupts. * o Do the command */ SDIO_ICR = 0x7ff; // Reset everything that isn't bolted down. SDIO_ARG = arg; SDIO_CMD = tmp_val; /* * In a polled mode we should be able to just read the status bits * directly. */ tmp_val = 0; do { tmp_val |= (SDIO_STA & 0x7ff); } while ((SDIO_STA & SDIO_STA_CMDACT) || (! tmp_val));; SDIO_ICR = tmp_val; /* * Compute the error here. Which can be one of * -- Success (either CMDSENT or CMDREND depending on response) * -- Timeout (based on CTIMEOUT) * -- No Response (based on no response in the time alloted) * -- CRC Error (based on CCRCFAIL) */ if (! tmp_val) { error = SDIO_ENORESP; } else if (tmp_val & SDIO_STA_CCRCFAIL) { error = SDIO_ECCRCFAIL; } else if (tmp_val & (SDIO_STA_CMDREND | SDIO_STA_CMDSENT)) { error = SDIO_ESUCCESS; } else if (tmp_val & SDIO_STA_CTIMEOUT) { error = SDIO_ECTIMEOUT; } else { error = SDIO_EUNKNOWN; } return error; } /* our static data buffer we use for data movement commands */ /* * Helper function - sdio_select * * This function "selects" a card using CMD7, note that if * you select card 0 that deselects the card (RCA is not allowed * to be 0) */ static int sdio_select(struct dev_sd *sd, int rca) { int err; err = stm32_sdio_command(sd, 7, rca << 16); if ((rca == 0) && (err == SDIO_ECTIMEOUT)) { return 0; // "cheat" a timeout selecting 0 is a successful deselect } return err; } /* * Helper function - sdio_scr * * Unlike the CID and CSD functions this function transfers data * so it needs to use the DPSM. * * Note that data over the wire is byte swapped so we swap it back * to "fix" it. * * Note when this return 0 the first two longs in the data_buf are * the SCR register. */ static uint32_t data_buf[129]; static int sdio_scr(struct dev_sd *sd, SDIO_CARD c) { int err; uint32_t tmp_reg; int ndx; int data_len; /* Select the card */ err = sdio_select(sd, c->rca); if (! err) { /* Set the Block Size */ err = stm32_sdio_command(sd, 16, 8); if (! err) { /* APPCMD (our RCA) */ err = stm32_sdio_command(sd, 55, c->rca << 16); if (! err) { SDIO_ICR = 0xFFFFFFFF; /* Clear all status flags */ SDIO_DTIMER = 0xffffffff; SDIO_DLEN = 8; SDIO_DCTRL = SDIO_DCTRL_DBLOCKSIZE_3 | SDIO_DCTRL_DTDIR | SDIO_DCTRL_DTEN; /* ACMD51 - Send SCR */ err = stm32_sdio_command(sd, 51, 0); if (! err) { data_len = 0; do { tmp_reg = SDIO_STA; if (tmp_reg & SDIO_STA_RXDAVL) { data_buf[data_len++] = SDIO_FIFO; } } while (tmp_reg & SDIO_STA_RXACT); if ((tmp_reg & SDIO_STA_DBCKEND) == 0) { if (tmp_reg & SDIO_STA_DCRCFAIL) { err = SDIO_EDCRCFAIL; } else if (tmp_reg & SDIO_STA_RXOVERR) { err = SDIO_ERXOVERR; } else { err = SDIO_EUNKNOWN; // unknown error } } if (! err) { for (ndx = 0; ndx < 2; ndx++) { byte_swap(data_buf[ndx]); c->scr[ndx] = data_buf[ndx]; } } } } } } (void) sdio_select(sd, 0); return err; } /* * Read a Block from our Card * * NB: There is a possibly useless test in this code, during the read * phase it allows that the SDIO card might try to send more than SDCARD_BLOCK_SIZE * bytes (128 32 bit longs) and allows it to do so, constantly over * writing the last long in the just-in-case-over-long-by-1 data buffer. * To compromise the system you would need a borked or custom crafted * sdio card which did that. */ int sdcard_read(void *dev, void *_buf, uint32_t lba) { int err; uint32_t tmp_reg; uint32_t addr = lba; uint8_t *t; int ndx, bdx = 0; struct dev_sd *sd = &SD; uint32_t *buf = _buf; uint32_t buf_len = 0; if (! SDIO_CARD_CCS(sd->card)) { addr = lba * SDCARD_BLOCK_SIZE; // non HC cards use byte address } err = sdio_select(sd, sd->card->rca); if (! err) { err = stm32_sdio_command(sd, 16, SDCARD_BLOCK_SIZE); if (!err) { SDIO_DTIMER = 0xffffffff; SDIO_DLEN = SDCARD_BLOCK_SIZE; SDIO_DCTRL = SDIO_DCTRL_DBLOCKSIZE_9 | SDIO_DCTRL_DTDIR | SDIO_DCTRL_DTEN; err = stm32_sdio_command(sd, 17, addr); if (! err) { buf_len = 0; do { tmp_reg = SDIO_STA; if (tmp_reg & SDIO_STA_RXDAVL) { buf[buf_len] = SDIO_FIFO; if (buf_len < 128) { ++buf_len; } } } while (tmp_reg & SDIO_STA_RXACT); if ((tmp_reg & SDIO_STA_DBCKEND) == 0) { if (tmp_reg & SDIO_STA_DCRCFAIL) { err = SDIO_EDCRCFAIL; } else if (tmp_reg & SDIO_STA_RXOVERR) { err = SDIO_ERXOVERR; } else { err = SDIO_EUNKNOWN; // Unknown Error! } } else { err = SDCARD_BLOCK_SIZE; } } } } // deselect the card (void) sdio_select(sd, 0); return err; } uint32_t sdcard_getsize(void *dev) { if (SD.card) return SD.card->size; return 0; } /* * Write a Block from our Card */ int sdcard_write(void *dev, void *_buf, uint32_t lba) { int err; uint32_t tmp_reg; uint32_t addr = lba; uint8_t *t; int ndx; struct dev_sd *sd = &SD; SDIO_CARD c; uint32_t *buf = _buf; uint32_t buf_len = 0; c = SD.card; if (! SDIO_CARD_CCS(c)) { addr = lba * SDCARD_BLOCK_SIZE; // non HC cards use byte address } err = sdio_select(sd, c->rca); if (! err) { /* Set Block Size to SDCARD_BLOCK_SIZE */ err = stm32_sdio_command(sd, 16, SDCARD_BLOCK_SIZE); if (!err) { SDIO_DTIMER = 0xffffffff; SDIO_DLEN = SDCARD_BLOCK_SIZE; SDIO_DCTRL = SDIO_DCTRL_DBLOCKSIZE_9 | SDIO_DCTRL_DTEN; err = stm32_sdio_command(sd, 24, addr); if (! err) { do { tmp_reg = SDIO_STA; if (tmp_reg & SDIO_STA_TXFIFOHE) { SDIO_FIFO = buf[buf_len]; if (buf_len < 128) { ++buf_len; } } } while (tmp_reg & SDIO_STA_TXACT); if ((tmp_reg & SDIO_STA_DBCKEND) == 0) { if (tmp_reg & SDIO_STA_DCRCFAIL) { err = SDIO_EDCRCFAIL; } else if (tmp_reg & SDIO_STA_TXUNDERR) { err = SDIO_ETXUNDER; } else { err = SDIO_EUNKNOWN; // Unknown Error! } } } } } // deselect the card (void) sdio_select(sd, 0); if (err == 0) return SDCARD_BLOCK_SIZE; return err; } /* * sdio-status - Get Card Status page * * This function fetches the SD Card Status page and * copies it into the CARD structure. */ /* int sdio_status(SDIO_CARD c) { uint32_t tmp_reg; int ndx; int err; err = sdio_select(c->rca); if (! err) { err = stm32_sdio_command(16, 64); if (! err) { err = stm32_sdio_command(55, c->rca << 16); if (! err) { SDIO_DTIMER = 0xffffffff; SDIO_DLEN = 64; SDIO_DCTRL = SDIO_DCTRL_DBLOCKSIZE_6 | SDIO_DCTRL_DTDIR | SDIO_DCTRL_DTEN; */ /* ACMD13 - Send Status Reg */ /* err = stm32_sdio_command(13, 0); if (! err) { data_len = 0; do { tmp_reg = SDIO_STA; if (tmp_reg & SDIO_STA_RXDAVL) { data_buf[data_len] = SDIO_FIFO; if (data_len < 128) { ++data_len; } } } while (tmp_reg & SDIO_STA_RXACT); if ((tmp_reg & SDIO_STA_DBCKEND) == 0) { if (tmp_reg & SDIO_STA_DCRCFAIL) { err = SDIO_EDCRCFAIL; } else if (tmp_reg & SDIO_STA_RXOVERR) { err = SDIO_ERXOVERR; } else { err = SDIO_EUNKNOWN; // Unknown Error! } } else { for (ndx = 0; ndx < 16; ndx++) { byte_swap(data_buf[ndx]); c->status[ndx] = data_buf[ndx]; } } (void) sdio_select(0); } } } } return err; } */ static struct SDIO_CARD_DATA sdio_card_data; #define MAX_RETRIES 5 /* * stm32_sdio_open - Prepare to use SDIO card * * This function resets the SDIO bus and identifies the * card (if any) that is plugged in. If there is no card * present, or an error in figuring out what the card is * (for example its an old MMC card) the function returns * NULL. If it fails and you have logging enabled you can * look at the last few commands sent. */ SDIO_CARD stm32_sdio_open(struct dev_sd *sd) { int err; int i; uint8_t *t; uint32_t tmp_reg; SDIO_CARD res = &sdio_card_data; // basically bset(0, sdio_card_data) t = (uint8_t *) &sdio_card_data; for (i = 0; i < (int) sizeof(sdio_card_data); i++) { *t++ = 0; } stm32_sdio_reset(sd, SDIO_POWER_ON); err = stm32_sdio_command(sd, 0, 0); if (!err) { err = stm32_sdio_command(sd, 8, 0x1aa); if (!err) { // Woot! We support CMD8 so we're a v2 card at least */ tmp_reg = SDIO_RESP1; sdio_card_data.props = 1; i = 0; err = stm32_sdio_command(sd, 5, 0); if (! err) { // It is an SDIO card which is unsupported! err = SDIO_EBADCARD; return NULL; } do { err = stm32_sdio_command(sd, 55, 0); // broadcast ACMD if (err) { break; // try again } // Testing Card Busy, Voltage match, and capacity err = stm32_sdio_command(sd, 41, 0xc0100000); if (err != -2) { // Expect CCRCFAIL here break; // try again } tmp_reg = SDIO_RESP1; // what did the card send? if ((tmp_reg & 0x80000000) == 0) { continue; // still powering up } res->ocr = tmp_reg; // Ok OCR is valid break; } while (++i < MAX_RETRIES); if (res->ocr) { err = stm32_sdio_command(sd, 2, 0); if (! err) { res->cid[0] = SDIO_RESP1; res->cid[1] = SDIO_RESP2; res->cid[2] = SDIO_RESP3; res->cid[3] = SDIO_RESP4; err = stm32_sdio_command(sd, 3, 0); // get the RCA if (! err) { tmp_reg = SDIO_RESP1; res->rca = (tmp_reg >> 16) & 0xffff; if (! res->rca) { /* * If the card says '0' tell it to pick * we assume this will work because the * previous send RCA worked and the card * should be in the ident state if it is * functioning correctly. */ (void) stm32_sdio_command(sd, 3, 0); // try again tmp_reg = SDIO_RESP1; res->rca = (tmp_reg >> 16) & 0xffff; } err = stm32_sdio_command(sd, 9, res->rca << 16); if (! err) { res->csd[0] = SDIO_RESP1; res->csd[1] = SDIO_RESP2; res->csd[2] = SDIO_RESP3; res->csd[3] = SDIO_RESP4; err = sdio_scr(sd, res); // Capture the SCR if (! err) { /* All SD Cards support 4 bit bus and 24Mhz */ err = sdio_select(sd, res->rca); if (! err) { err = stm32_sdio_command(sd, 55, res->rca << 16); if (! err) { err = stm32_sdio_command(sd, 6, 2); if (! err) { //XXX stm32_sdio_bus(4, SDIO_24MHZ); //Seems we have speed issues for now... stm32_sdio_bus(sd, 4, SDIO_12MHZ); (void) sdio_select(sd, 0); } } } } } } } } } } /* Compute the size of the card based on fields in the CSD * block. There are two kinds, V1 or V2. * In the V1 Case : * Size = 1<size = 0; switch (SDIO_CSD_VERSION(res)) { case 0: tmp_reg = ((1 << (SDIO_CSD1_CSIZE_MULT(res) + 2)) * ( 1 << SDIO_CSD1_RBLKLEN(res))) >> 9; res->size = tmp_reg * (SDIO_CSD1_CSIZE(res) + 1); break; case 1: res->size = (SDIO_CSD2_CSIZE(res)+1) << 10; break; default: res->size = 0; // Bug if its not CSD V1 or V2 } } return (err == 0) ? res : NULL; } static int stm32_sdio_card_detect(void) { SDIO_CARD sdcard; if (SD.card != NULL) return 0; sdcard = stm32_sdio_open(&SD); if (sdcard != NULL) { SD.card = sdcard; return 0; } return -1; } int sdcard_detect(void) { int i; for (i = 0; i < 20; i++) { if (stm32_sdio_card_detect() == 0) return 0; } return -1; } /* * Set up the GPIO pins and peripheral clocks for the SDIO * system. The code should probably take an option card detect * pin, at the moment it uses the one used by the Embest board. */ void sdcard_hw_init(void) { int i; uint32_t reg; /* Enable clocks for SDIO and DMA2 */ APB2_CLOCK_ER |= SDIO_APB2_CLOCK_ER_VAL; /* Enable clocks for GPIOA, GPIOC, GPIOD */ AHB1_CLOCK_ER |= GPIOA_AHB1_CLOCK_ER | GPIOC_AHB1_CLOCK_ER | GPIOD_AHB1_CLOCK_ER; for (i = 0 ; i < 5; i++) { /* mode = AF */ reg = GPIOC_MODE & ~ (0x03 << (sdio_pins[i] * 2)); GPIOC_MODE = reg | (2 << (sdio_pins[i] * 2)); /* Alternate function: use high pins (8..12) */ reg = GPIOC_AFH & ~(0xf << ((sdio_pins[i] - 8) * 4)); GPIOC_AFH = reg | (sdio_af << ((sdio_pins[i] - 8) * 4)); } /* cmd pin */ reg = GPIOD_MODE & ~ (0x03 << (sdio_cmd_pin * 2)); GPIOD_MODE = reg | (2 << (sdio_cmd_pin * 2)); /* Alternate function: use low pin */ reg = GPIOD_AFL & ~(0xf << ((sdio_cmd_pin) * 4)); GPIOD_AFL = reg | (sdio_af << ((sdio_cmd_pin) * 4)); /* card detect pin configured as input */ GPIOA_MODE &= ~ (0x03 << (sdio_detect_pin * 2)); reg = GPIOA_PUPD & (~(0x03 << (sdio_detect_pin & 2))); GPIOA_PUPD = reg | (1 << (sdio_detect_pin * 2)); } void *sdcard_open(void) { int ret; do { ret = stm32_sdio_card_detect(); } while (ret < 0); return &SD; } int sdcard_sense(void) { return !!(GPIOA_IDR & (1 << sdio_detect_pin)); }