diff --git a/src/usecfs_dev_sdio.c b/src/usecfs_dev_sdio.c new file mode 100644 index 0000000..16f6b7e --- /dev/null +++ b/src/usecfs_dev_sdio.c @@ -0,0 +1,803 @@ + +/* + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the software. If not, see . + * + * Original Author: Chuck M. (see https://github.com/ChuckM/stm32f4-sdio-driver/) + * Re-adapted by: Daniele Lacamera + * + * Permission to release under the terms of GPLv2 are granted by the + * copyright holders. + * + */ + +/* + * 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 "usecfs_dev_sdio.h" +#include "system.h" + +#if (!defined(BLOCK_SIZE)) || (BLOCK_SIZE != 512) +# error BLOCK_SIZE undefined or invalid +#endif + +#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; /* GPIOD */ +static const uint32_t sdio_detect_pin = 8; /* GPIOA */ +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 + */ +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 + * 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 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 block_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 * BLOCK_SIZE; // non HC cards use byte address + } + err = sdio_select(sd, sd->card->rca); + if (! err) { + err = stm32_sdio_command(sd, 16, BLOCK_SIZE); + if (!err) { + SDIO_DTIMER = 0xffffffff; + SDIO_DLEN = 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 = BLOCK_SIZE; + } + } + } + } + // deselect the card + (void) sdio_select(sd, 0); + return err; +} + +/* + * Write a Block from our Card + */ +int block_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 * BLOCK_SIZE; // non HC cards use byte address + } + + err = sdio_select(sd, c->rca); + if (! err) { + /* Set Block Size to BLOCK_SIZE */ + err = stm32_sdio_command(sd, 16, BLOCK_SIZE); + if (!err) { + SDIO_DTIMER = 0xffffffff; + SDIO_DLEN = 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 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) +{ + uint8_t block[BLOCK_SIZE]; + SDIO_CARD sdcard; + if (SD.card != NULL) + return 0; + sdcard = stm32_sdio_open(&SD); + if (sdcard != NULL) { + SD.card = sdcard; + 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. + */ +static void sdio_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)); + +#if 0 + /* Set 100MHz ospeed + pullup */ + GPIOC_OSPEED |= (0x03 << (sdio_pins[i] * 2)); + GPIOC_OTYPE &= ~(0x01 << (sdio_pins[i])); + reg = GPIOC_PUPD & (~(0x03 << (sdio_pins[i] & 2))); + GPIOC_PUPD = reg | (1 << (sdio_pins[i] * 2)); +#endif + + /* 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)); +#if 0 + /* Set 100MHz ospeed + pullup */ + GPIOD_OSPEED |= (0x03 << (sdio_cmd_pin * 2)); + GPIOD_OTYPE &= ~(0x01 << (sdio_cmd_pin)); + reg = GPIOD_PUPD & (~(0x03 << (sdio_cmd_pin & 2))); + GPIOD_PUPD = reg | (1 << (sdio_cmd_pin * 2)); +#endif + /* 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 *block_open(void) +{ + int ret; + /* Initialize sdio RCC and pins */ + sdio_hw_init(); + do { + ret = stm32_sdio_card_detect(); + } while (ret < 0); + return &SD; +} + + diff --git a/src/usecfs_dev_sdio.h b/src/usecfs_dev_sdio.h new file mode 100644 index 0000000..bf7b8b8 --- /dev/null +++ b/src/usecfs_dev_sdio.h @@ -0,0 +1,54 @@ +/* this define lets the init code know that you are using a GPIO as a card + * detect pin */ +#define SDIO_HAS_CARD_DETECT + +enum SDIO_CLOCK_DIV { + SDIO_24MHZ = 0, + SDIO_16MHZ, + SDIO_12MHZ, + SDIO_8MHZ, + SDIO_4MHZ, + SDIO_1MHZ, + SDIO_400KHZ +}; + +enum SDIO_POWER_STATE { + SDIO_POWER_ON, + SDIO_POWER_OFF +}; + +#define SDIO_CARD_CCS(c) (((c)->ocr & 0x40000000) != 0) +#define SDIO_CARD_UHS2(c) (((c)->ocr & 0x40000000) != 0) +#define SDIO_CARD_LVOK(c) (((c)->ocr & 0x01000000) != 0) + +typedef struct SDIO_CARD_DATA { + uint32_t props; + uint32_t ocr; + uint32_t cid[4]; + uint32_t csd[4]; + uint32_t scr[2]; + uint32_t status[16]; + uint32_t size; + uint16_t rca; +} * SDIO_CARD; + +struct dev_sd { + struct device *dev; + uint32_t base; + uint32_t *rcc_rst_reg; + uint32_t rcc_rst; + SDIO_CARD card; +}; + +/* API for sd_bus clock setting */ +enum SD_CLOCK_DIV { + CLOCK_24MHZ = 0, + CLOCK_16MHZ, + CLOCK_12MHZ, + CLOCK_8MHZ, + CLOCK_4MHZ, + CLOCK_1MHZ, + CLOCK_400KHZ +}; + +//int stm32_sd_bus(int bits, enum SD_CLOCK_DIV freq);