gadget-securevault/sdcard.c

796 lines
24 KiB
C
Raw Permalink Normal View History

2019-10-27 07:30:03 +01:00
/*
* (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 <stdint.h>
#include "system.h"
#include "sdcard.h"
2019-11-02 14:44:45 +01:00
#define SDCARD_BLOCK_SIZE 512
2019-10-27 07:30:03 +01:00
#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
2019-11-02 14:44:45 +01:00
* phase it allows that the SDIO card might try to send more than SDCARD_BLOCK_SIZE
2019-10-27 07:30:03 +01:00
* 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)) {
2019-11-02 14:44:45 +01:00
addr = lba * SDCARD_BLOCK_SIZE; // non HC cards use byte address
2019-10-27 07:30:03 +01:00
}
err = sdio_select(sd, sd->card->rca);
if (! err) {
2019-11-02 14:44:45 +01:00
err = stm32_sdio_command(sd, 16, SDCARD_BLOCK_SIZE);
2019-10-27 07:30:03 +01:00
if (!err) {
SDIO_DTIMER = 0xffffffff;
2019-11-02 14:44:45 +01:00
SDIO_DLEN = SDCARD_BLOCK_SIZE;
2019-10-27 07:30:03 +01:00
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 {
2019-11-02 14:44:45 +01:00
err = SDCARD_BLOCK_SIZE;
2019-10-27 07:30:03 +01:00
}
}
}
}
// deselect the card
(void) sdio_select(sd, 0);
return err;
}
2019-11-04 15:38:49 +01:00
uint32_t sdcard_getsize(void *dev)
{
if (SD.card)
return SD.card->size;
return 0;
}
2019-10-27 07:30:03 +01:00
/*
* 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)) {
2019-11-02 14:44:45 +01:00
addr = lba * SDCARD_BLOCK_SIZE; // non HC cards use byte address
2019-10-27 07:30:03 +01:00
}
err = sdio_select(sd, c->rca);
if (! err) {
2019-11-02 14:44:45 +01:00
/* Set Block Size to SDCARD_BLOCK_SIZE */
err = stm32_sdio_command(sd, 16, SDCARD_BLOCK_SIZE);
2019-10-27 07:30:03 +01:00
if (!err) {
SDIO_DTIMER = 0xffffffff;
2019-11-02 14:44:45 +01:00
SDIO_DLEN = SDCARD_BLOCK_SIZE;
2019-10-27 07:30:03 +01:00
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)
2019-11-02 14:44:45 +01:00
return SDCARD_BLOCK_SIZE;
2019-10-27 07:30:03 +01:00
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<<BLOCK_LEN * 1<<(MULT+2) * (C_SIZE+1) bytes.
* In the V2 Case :
2019-11-02 14:44:45 +01:00
* Size = (C_SIZE + 1) * SDCARD_BLOCK_SIZEK bytes.
* But for our structure we want the size in SDCARD_BLOCK_SIZE byte "blocks"
2019-10-27 07:30:03 +01:00
* since that is the addressing unit we're going to export so
2019-11-02 14:44:45 +01:00
* we compute the size / SDCARD_BLOCK_SIZE as the "size" for the structure.
2019-10-27 07:30:03 +01:00
*/
if (! err) {
res->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));
}