Arduino_STM32/STM32F4/cores/maple/libmaple/sdio.c

195 lines
5.3 KiB
C

/******************************************************************************
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*****************************************************************************/
/**
* @file libmaple/sdio.c
* @author stevstrong
* @brief Secure digital input/output interface.
*/
#include "sdio.h"
#include "gpio.h"
#include "wirish_time.h"
#if defined(BOARD_generic_f407v)
sdio_dev * SDIO = SDIO_BASE;
#define DELAY_LONG 10
#define DELAY_SHORT 1
uint8_t dly = DELAY_LONG; // microseconds delay after accessing registers
/*
* SDIO convenience routines
*/
static void sdio_gpios_init(void)
{
gpio_set_mode(BOARD_SDIO_D0, GPIO_AF_OUTPUT_PP_PU);
gpio_set_mode(BOARD_SDIO_D1, GPIO_AF_OUTPUT_PP_PU);
gpio_set_mode(BOARD_SDIO_D2, GPIO_AF_OUTPUT_PP_PU);
gpio_set_mode(BOARD_SDIO_D3, GPIO_AF_OUTPUT_PP_PU);
gpio_set_mode(BOARD_SDIO_CLK, GPIO_AF_OUTPUT_PP);
gpio_set_mode(BOARD_SDIO_CMD, GPIO_AF_OUTPUT_PP_PU);
//
gpio_set_af_mode(BOARD_SDIO_D0, 12);
gpio_set_af_mode(BOARD_SDIO_D1, 12);
gpio_set_af_mode(BOARD_SDIO_D2, 12);
gpio_set_af_mode(BOARD_SDIO_D3, 12);
gpio_set_af_mode(BOARD_SDIO_CLK, 12);
gpio_set_af_mode(BOARD_SDIO_CMD, 12);
}
static void sdio_gpios_deinit(void)
{
gpio_set_mode(BOARD_SDIO_D0, GPIO_INPUT_FLOATING);
gpio_set_mode(BOARD_SDIO_D1, GPIO_INPUT_FLOATING);
gpio_set_mode(BOARD_SDIO_D2, GPIO_INPUT_FLOATING);
gpio_set_mode(BOARD_SDIO_D3, GPIO_INPUT_FLOATING);
gpio_set_mode(BOARD_SDIO_CLK, GPIO_INPUT_FLOATING);
gpio_set_mode(BOARD_SDIO_CMD, GPIO_INPUT_FLOATING);
//
gpio_set_af_mode(BOARD_SDIO_D0, 0);
gpio_set_af_mode(BOARD_SDIO_D1, 0);
gpio_set_af_mode(BOARD_SDIO_D2, 0);
gpio_set_af_mode(BOARD_SDIO_D3, 0);
gpio_set_af_mode(BOARD_SDIO_CLK, 0);
gpio_set_af_mode(BOARD_SDIO_CMD, 0);
}
/**
* @brief Initialize and reset the SDIO device.
*/
void sdio_init(void)
{
rcc_clk_enable(RCC_SDIO);
rcc_reset_dev(RCC_SDIO);
}
void sdio_power_on(void)
{
SDIO->POWER = SDIO_POWER_PWRCTRL_ON;
// After a data write, data cannot be written to this register for three SDIOCLK clock periods
// plus two PCLK2 clock periods.
delayMicroseconds(DELAY_LONG);
}
void sdio_power_off(void)
{
SDIO->POWER = SDIO_POWER_PWRCTRL_OFF;
// After a data write, data cannot be written to this register for three SDIOCLK clock periods
// plus two PCLK2 clock periods.
delayMicroseconds(DELAY_LONG);
}
/**
* @brief Enable the SDIO peripheral
void sdio_peripheral_enable(void) {
bb_peri_set_bit(SDIO->CR1, SPI_CR1_SPE_BIT, 1);
}
*/
/**
* @brief Disable a SPI peripheral
void sdio_peripheral_disable(spi_dev *dev) {
bb_peri_set_bit(&dev->regs->CR1, SPI_CR1_SPE_BIT, 0);
}
*/
void sdio_set_clock(uint32_t clk)
{
if (clk>24000000UL) clk = 24000000UL; // limit the SDIO master clock to 24MHz
if (clk<1000000) dly = DELAY_LONG;
else dly = DELAY_SHORT;
sdio_disable();
SDIO->CLKCR = (SDIO->CLKCR & (~(SDIO_CLKCR_CLKDIV|SDIO_CLKCR_BYPASS))) | SDIO_CLKCR_CLKEN | (((SDIOCLK/clk)-2)&SDIO_CLKCR_CLKDIV);
delayMicroseconds(dly);
}
void sdio_set_dbus_width(uint16_t bus_w)
{
SDIO->CLKCR = (SDIO->CLKCR & (~SDIO_CLKCR_WIDBUS)) | bus_w;
delayMicroseconds(dly);
}
void sdio_set_dblock_size(uint8_t dbsize)
{
SDIO->DCTRL = (SDIO->DCTRL&(~SDIO_DCTRL_DBLOCKSIZE)) | ((dbsize&0xF)<<4);
delayMicroseconds(dly);
}
void sdio_enable(void)
{
SDIO->CLKCR |= SDIO_CLKCR_CLKEN;
delayMicroseconds(dly);
}
void sdio_disable(void)
{
SDIO->CLKCR ^= SDIO_CLKCR_CLKEN;
delayMicroseconds(dly);
}
/**
* @brief Configure and enable the SDIO device.
*/
void sdio_begin(void)
{
sdio_gpios_init();
sdio_init();
sdio_power_on();
// Set initial SCK rate.
sdio_set_clock(400000);
delayMicroseconds(200); // generate 80 pulses at 400kHz
}
/**
* @brief Disables the SDIO device.
*/
void sdio_end(void)
{
sdio_disable();
while ( sdio_cmd_xfer_ongoing() );
sdio_power_off();
rcc_clk_disable(RCC_SDIO);
sdio_gpios_deinit();
}
/**
* @brief Send command by the SDIO device.
*/
uint8_t sdio_cmd_send(uint16_t cmd_index_resp_type, uint32_t arg)
{
uint8_t retries = 10; // in case of errors
do { // retry command if errors detected
// clear interrupt flags - IMPORTANT!!!
SDIO->ICR = SDIO_ICR_CMD_FLAGS;
// write command
SDIO->ARG = arg;
SDIO->CMD = (uint32_t)(SDIO_CMD_CPSMEN | cmd_index_resp_type );
while ( (SDIO->STA&SDIO_STA_CMDACT) ) ; // wait for actual command transfer to finish
// wait for response, if the case
if ( cmd_index_resp_type&(SDIO_CMD_WAIT_SHORT_RESP|SDIO_CMD_WAIT_LONG_RESP) ) {
while ( !(SDIO->STA&(SDIO_STA_CMDREND|SDIO_STA_CMD_ERROR_FLAGS)) ) ;
} else break; // no response required
if ( SDIO->STA&(SDIO_STA_CMDREND|SDIO_STA_CTIMEOUT) )
break; // response received or timeout
// ignore CRC error for CMD5 and ACMD41
if ( ((cmd_index_resp_type&SDIO_CMD_CMDINDEX)==5) || ((cmd_index_resp_type&SDIO_CMD_CMDINDEX)==41) )
break;
} while ( (--retries) );
return (uint8_t)retries;
}
#endif // BOARD_generic_f407v