stm32: Add sdio support
Adds sdio support for the stm32f4 to allow for SD card flash updates without power cycling some boards, e.g. BTT Octopus Pro. Signed-off-by: H. Gregor Molter <gregor.molter@secretlab.de> Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
committed by
Kevin O'Connor
parent
848a78d1a5
commit
d7bd7f1f4b
@@ -9,6 +9,7 @@ config STM32_SELECT
|
||||
select HAVE_GPIO_ADC
|
||||
select HAVE_GPIO_I2C if !(MACH_STM32F031 || MACH_STM32H7)
|
||||
select HAVE_GPIO_SPI if !MACH_STM32F031
|
||||
select HAVE_GPIO_SDIO if MACH_STM32F4
|
||||
select HAVE_GPIO_HARD_PWM if MACH_STM32F1 || MACH_STM32F4 || MACH_STM32G0 || MACH_STM32H7
|
||||
select HAVE_GPIO_BITBANGING if !MACH_STM32F031
|
||||
select HAVE_STRICT_TIMING
|
||||
|
||||
@@ -64,6 +64,8 @@ src-$(CONFIG_MACH_STM32L4) += stm32/stm32h7_adc.c stm32/stm32f0_i2c.c
|
||||
spi-src-y := stm32/spi.c
|
||||
spi-src-$(CONFIG_MACH_STM32H7) := stm32/stm32h7_spi.c
|
||||
src-$(CONFIG_HAVE_GPIO_SPI) += $(spi-src-y)
|
||||
sdio-src-y := stm32/sdio.c
|
||||
src-$(CONFIG_HAVE_GPIO_SDIO) += $(sdio-src-y)
|
||||
usb-src-$(CONFIG_HAVE_STM32_USBFS) := stm32/usbfs.c
|
||||
usb-src-$(CONFIG_HAVE_STM32_USBOTG) := stm32/usbotg.c
|
||||
src-$(CONFIG_USBSERIAL) += $(usb-src-y) stm32/chipid.c generic/usb_cdc.c
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "internal.h" // gpio_peripheral
|
||||
|
||||
// Set the mode and extended function of a pin
|
||||
// Set the mode, extended function and speed of a pin
|
||||
void
|
||||
gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup)
|
||||
{
|
||||
@@ -16,7 +16,8 @@ gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup)
|
||||
gpio_clock_enable(regs);
|
||||
|
||||
// Configure GPIO
|
||||
uint32_t mode_bits = mode & 0xf, func = (mode >> 4) & 0xf, od = mode >> 8;
|
||||
uint32_t mode_bits = mode & 0xf, func = (mode >> 4) & 0xf;
|
||||
uint32_t od = (mode >> 8) & 0x1, hs = (mode >> 9) & 0x1;
|
||||
uint32_t pup = pullup ? (pullup > 0 ? 1 : 2) : 0;
|
||||
uint32_t pos = gpio % 16, af_reg = pos / 8;
|
||||
uint32_t af_shift = (pos % 8) * 4, af_msk = 0x0f << af_shift;
|
||||
@@ -33,6 +34,6 @@ gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup)
|
||||
// stm32f4 is ~50Mhz at 40pF
|
||||
// stm32g0 is ~30Mhz at 50pF
|
||||
// stm32h7 is ~85Mhz at 50pF
|
||||
uint32_t ospeed = CONFIG_MACH_STM32F0 ? 0x01 : 0x02;
|
||||
uint32_t ospeed = hs ? 0x03 : (CONFIG_MACH_STM32F0 ? 0x01 : 0x02);
|
||||
regs->OSPEEDR = (regs->OSPEEDR & ~m_msk) | (ospeed << m_shift);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ extern GPIO_TypeDef * const digital_regs[];
|
||||
#define GPIO_INPUT 0
|
||||
#define GPIO_OUTPUT 1
|
||||
#define GPIO_OPEN_DRAIN 0x100
|
||||
#define GPIO_HIGH_SPEED 0x200
|
||||
#define GPIO_FUNCTION(fn) (2 | ((fn) << 4))
|
||||
#define GPIO_ANALOG 3
|
||||
void gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup);
|
||||
|
||||
414
src/stm32/sdio.c
Normal file
414
src/stm32/sdio.c
Normal file
@@ -0,0 +1,414 @@
|
||||
// SDIO functions on STM32
|
||||
//
|
||||
// Copyright (C) 2022 H. Gregor Molter <gregor.molter@secretlab.de>
|
||||
//
|
||||
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
|
||||
#include "board/io.h" // readb, writeb
|
||||
#include "command.h" // shutdown
|
||||
#include "sdio.h" // sdio_setup
|
||||
#include "internal.h" // gpio_peripheral
|
||||
#include "sched.h" // sched_shutdown
|
||||
#include "generic/armcm_timer.h" // udelay
|
||||
|
||||
struct sdio_info {
|
||||
SDIO_TypeDef *sdio;
|
||||
uint8_t clk_pin, cmd_pin, dat0_pin, dat1_pin, dat2_pin, dat3_pin, function;
|
||||
};
|
||||
|
||||
enum {
|
||||
SDIO_OK = 0,
|
||||
SDIO_ERROR = 1,
|
||||
SDIO_TIMEOUT = 2,
|
||||
SDIO_CMD_RESPONSE_TIMEOUT = 3,
|
||||
SDIO_CRC_FAIL = 4,
|
||||
SDIO_WRONG_CMD_RESPONSE = 5,
|
||||
SDIO_WRONG_BLOCKSIZE = 6,
|
||||
SDIO_DATA_TIMEOUT = 7,
|
||||
SDIO_READ_OVERRUN = 8,
|
||||
SDIO_WRITE_UNDERRUN = 9,
|
||||
SDIO_NO_DATA_MEMORY = 10,
|
||||
};
|
||||
|
||||
enum {
|
||||
SDIO_CARDVER_UNKNOWN = 0,
|
||||
SDIO_CARDVER_1X = 1,
|
||||
SDIO_CARDVER_2X = 2
|
||||
};
|
||||
|
||||
enum {
|
||||
SDIO_CARDTYPE_UNKNOWN = 0,
|
||||
SDIO_CARDTYPE_SDHC_SDXC = 1,
|
||||
SDIO_CARDTYPE_SDSC = 2
|
||||
};
|
||||
|
||||
enum {
|
||||
SDIO_WAIT_NO_RESPONSE = 0,
|
||||
SDIO_WAIT_SHORT_RESPONSE = 1,
|
||||
SDIO_WAIT_LONG_RESPONSE = 2
|
||||
};
|
||||
|
||||
// PINS: CLK -> PC12 , CMD -> PD2,
|
||||
// DAT0 -> PC8, DAT1 -> PC9, DAT2 -> PC10, DAT3 -> PC11
|
||||
DECL_ENUMERATION("sdio_bus", "sdio", 0);
|
||||
DECL_CONSTANT_STR("BUS_PINS_sdio", "PC12,PD2,PC8,PC9,PC10,PC11");
|
||||
|
||||
#define SDIO_FUNCTION GPIO_FUNCTION(12)
|
||||
|
||||
static const struct sdio_info sdio_bus[] = {
|
||||
{ SDIO, GPIO('C', 12), GPIO('D', 2), GPIO('C', 8),
|
||||
GPIO('C', 9), GPIO('C', 10), GPIO('C', 11), SDIO_FUNCTION },
|
||||
};
|
||||
|
||||
#define SDIO_CLK_FREQ 48000000
|
||||
#define SDIO_INIT_CLK 400000
|
||||
#define SDIO_MAX_TIMEOUT 500 // Wait for at least 500ms before a timeout occurs
|
||||
#define CLKCR_CLEAR_MASK (SDIO_CLKCR_CLKDIV | SDIO_CLKCR_PWRSAV | \
|
||||
SDIO_CLKCR_BYPASS | SDIO_CLKCR_WIDBUS | SDIO_CLKCR_NEGEDGE | \
|
||||
SDIO_CLKCR_HWFC_EN)
|
||||
#define DCTRL_CLEAR_MASK (SDIO_DCTRL_DTEN | SDIO_DCTRL_DTDIR | \
|
||||
SDIO_DCTRL_DTMODE | SDIO_DCTRL_DBLOCKSIZE)
|
||||
#define CMD_CLEAR_MASK (SDIO_CMD_CMDINDEX | SDIO_CMD_WAITRESP | \
|
||||
SDIO_CMD_WAITINT | SDIO_CMD_WAITPEND | SDIO_CMD_CPSMEN | \
|
||||
SDIO_CMD_SDIOSUSPEND)
|
||||
#define SDIO_CLOCK_BYPASS_DISABLE 0
|
||||
#define SDIO_CLOCK_EDGE_RISING 0
|
||||
#define SDIO_CLOCK_POWER_SAVE_DISABLE 0
|
||||
#define SDIO_BUS_WIDE_1B 0
|
||||
#define SDIO_BUS_WIDE_4B SDIO_CLKCR_WIDBUS_0
|
||||
#define SDIO_HARDWARE_FLOW_CONTROL_DISABLE 0
|
||||
#define SDIO_HARDWARE_FLOW_CONTROL_ENABLE SDIO_CLKCR_HWFC_EN
|
||||
#define SDIO_CMD_FLAGS (SDIO_STA_CCRCFAIL | SDIO_STA_CTIMEOUT | \
|
||||
SDIO_STA_CMDREND | SDIO_STA_CMDSENT)
|
||||
#define SDIO_STATIC_FLAGS (SDIO_STA_CCRCFAIL | SDIO_STA_DCRCFAIL | \
|
||||
SDIO_STA_CTIMEOUT | SDIO_STA_DTIMEOUT | SDIO_STA_TXUNDERR | \
|
||||
SDIO_STA_RXOVERR | SDIO_STA_CMDREND | SDIO_STA_CMDSENT | \
|
||||
SDIO_STA_DATAEND | SDIO_STA_DBCKEND | SDIO_STA_SDIOIT)
|
||||
|
||||
struct sdio_config
|
||||
sdio_setup(uint32_t bus)
|
||||
{
|
||||
if (bus >= ARRAY_SIZE(sdio_bus))
|
||||
shutdown("Invalid sdio bus");
|
||||
|
||||
// Enable SDIO
|
||||
SDIO_TypeDef *sdio = sdio_bus[bus].sdio;
|
||||
if (!is_enabled_pclock((uint32_t)sdio)) {
|
||||
// Enable clock
|
||||
enable_pclock((uint32_t)sdio);
|
||||
// Initialize pins
|
||||
gpio_peripheral(
|
||||
sdio_bus[bus].dat0_pin, sdio_bus[bus].function|GPIO_HIGH_SPEED, 1);
|
||||
gpio_peripheral(
|
||||
sdio_bus[bus].dat1_pin, sdio_bus[bus].function|GPIO_HIGH_SPEED, 1);
|
||||
gpio_peripheral(
|
||||
sdio_bus[bus].dat2_pin, sdio_bus[bus].function|GPIO_HIGH_SPEED, 1);
|
||||
gpio_peripheral(
|
||||
sdio_bus[bus].dat3_pin, sdio_bus[bus].function|GPIO_HIGH_SPEED, 1);
|
||||
gpio_peripheral(
|
||||
sdio_bus[bus].cmd_pin, sdio_bus[bus].function|GPIO_HIGH_SPEED, 1);
|
||||
gpio_peripheral(
|
||||
sdio_bus[bus].clk_pin, sdio_bus[bus].function|GPIO_HIGH_SPEED, 1);
|
||||
}
|
||||
|
||||
struct sdio_config sdio_config = { .sdio = sdio };
|
||||
|
||||
// Setup SDIO with 1 bit width first and slow clock ~400 kHz
|
||||
sdio_set_speed(sdio_config, SDIO_INIT_CLK);
|
||||
|
||||
// Disable clk
|
||||
CLEAR_BIT(sdio->CLKCR, SDIO_CLKCR_CLKEN);
|
||||
// Set power state to _on_
|
||||
sdio->POWER = SDIO_POWER_PWRCTRL;
|
||||
// Wait for 2ms (standard: at least 1ms) to settle
|
||||
udelay(2000);
|
||||
// Enable Clk
|
||||
SET_BIT(sdio->CLKCR, SDIO_CLKCR_CLKEN);
|
||||
|
||||
return sdio_config;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
sdio_get_cmd_error(struct sdio_config sdio, uint32_t flags)
|
||||
{
|
||||
SDIO_TypeDef *regs = sdio.sdio;
|
||||
|
||||
// wait for a timeout (max. SDIO_MAX_TIMEOUT) in msec.
|
||||
// 8 cycles is the instruction cycles for the loop below.
|
||||
uint32_t sta;
|
||||
uint32_t count = SDIO_MAX_TIMEOUT * (SystemCoreClock / 8U / 1000U);
|
||||
do {
|
||||
if (count-- == 0) {
|
||||
return SDIO_TIMEOUT;
|
||||
}
|
||||
sta = regs->STA;
|
||||
} while ((sta & flags) == 0 || (sta & SDIO_STA_CMDACT) != 0);
|
||||
|
||||
regs->ICR = SDIO_CMD_FLAGS;
|
||||
|
||||
if (sta & SDIO_STA_CTIMEOUT) {
|
||||
return SDIO_CMD_RESPONSE_TIMEOUT;
|
||||
}
|
||||
if (sta & SDIO_STA_CCRCFAIL) {
|
||||
return SDIO_CRC_FAIL;
|
||||
}
|
||||
|
||||
return SDIO_OK;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
sdio_send_command(struct sdio_config sdio_config, uint8_t cmd,
|
||||
uint32_t argument, uint8_t wait, uint8_t *response_data,
|
||||
uint8_t *response_data_len)
|
||||
{
|
||||
SDIO_TypeDef *sdio = sdio_config.sdio;
|
||||
uint32_t wait_flags = 0; // valid for SDIO_WAIT_NO_RESPONSE
|
||||
uint32_t sta_flags = (wait == SDIO_WAIT_NO_RESPONSE) ?
|
||||
SDIO_STA_CMDSENT : SDIO_STA_CCRCFAIL | SDIO_STA_CMDREND | \
|
||||
SDIO_STA_CTIMEOUT;
|
||||
|
||||
if (wait == SDIO_WAIT_SHORT_RESPONSE) {
|
||||
wait_flags = SDIO_CMD_WAITRESP_0;
|
||||
} else if (wait == SDIO_WAIT_LONG_RESPONSE) {
|
||||
wait_flags = SDIO_CMD_WAITRESP;
|
||||
}
|
||||
|
||||
// Step 1: Send command and argument
|
||||
// CMD and State Machine enabled.
|
||||
// Wait for response like specified by wait_flags.
|
||||
uint32_t cmdreg = (cmd & 0x3F) | wait_flags | SDIO_CMD_CPSMEN;
|
||||
sdio->ARG = argument;
|
||||
MODIFY_REG(sdio->CMD, CMD_CLEAR_MASK, cmdreg);
|
||||
|
||||
// Step 2: Wait until response
|
||||
// wait for a timeout (max. SDIO_MAX_TIMEOUT) in msec.
|
||||
// 8 cycles is the instruction cycles for the loop below.
|
||||
uint32_t sta;
|
||||
uint32_t count = SDIO_MAX_TIMEOUT * (SystemCoreClock / 8U / 1000U);
|
||||
do {
|
||||
if (count-- == 0) {
|
||||
return SDIO_TIMEOUT;
|
||||
}
|
||||
sta = sdio->STA;
|
||||
} while ((sta & sta_flags) == 0 || (sta & SDIO_STA_CMDACT) != 0);
|
||||
|
||||
sdio->ICR = SDIO_CMD_FLAGS;
|
||||
|
||||
// Step 3: Store response_data and check for short and long responses
|
||||
// timeout and crc.
|
||||
if (response_data != NULL) {
|
||||
if (wait == SDIO_WAIT_SHORT_RESPONSE) {
|
||||
response_data[0] = (uint8_t) ((sdio->RESP1 >> 24) & 0xFF);
|
||||
response_data[1] = (uint8_t) ((sdio->RESP1 >> 16) & 0xFF);
|
||||
response_data[2] = (uint8_t) ((sdio->RESP1 >> 8) & 0xFF);
|
||||
response_data[3] = (uint8_t) ((sdio->RESP1) & 0xFF);
|
||||
*response_data_len = 4;
|
||||
} else if (wait == SDIO_WAIT_LONG_RESPONSE) {
|
||||
// TODO Inverse?
|
||||
response_data[0] = (uint8_t) ((sdio->RESP1 >> 24) & 0xFF);
|
||||
response_data[1] = (uint8_t) ((sdio->RESP1 >> 16) & 0xFF);
|
||||
response_data[2] = (uint8_t) ((sdio->RESP1 >> 8) & 0xFF);
|
||||
response_data[3] = (uint8_t) ((sdio->RESP1) & 0xFF);
|
||||
response_data[4] = (uint8_t) ((sdio->RESP2 >> 24) & 0xFF);
|
||||
response_data[5] = (uint8_t) ((sdio->RESP2 >> 16) & 0xFF);
|
||||
response_data[6] = (uint8_t) ((sdio->RESP2 >> 8) & 0xFF);
|
||||
response_data[7] = (uint8_t) ((sdio->RESP2) & 0xFF);
|
||||
response_data[8] = (uint8_t) ((sdio->RESP3 >> 24) & 0xFF);
|
||||
response_data[9] = (uint8_t) ((sdio->RESP3 >> 16) & 0xFF);
|
||||
response_data[10] = (uint8_t) ((sdio->RESP3 >> 8) & 0xFF);
|
||||
response_data[11] = (uint8_t) ((sdio->RESP3) & 0xFF);
|
||||
response_data[12] = (uint8_t) ((sdio->RESP4 >> 24) & 0xFF);
|
||||
response_data[13] = (uint8_t) ((sdio->RESP4 >> 16) & 0xFF);
|
||||
response_data[14] = (uint8_t) ((sdio->RESP4 >> 8) & 0xFF);
|
||||
response_data[15] = (uint8_t) ((sdio->RESP4) & 0xFF);
|
||||
*response_data_len = 16;
|
||||
}
|
||||
}
|
||||
|
||||
if (wait != SDIO_WAIT_NO_RESPONSE) {
|
||||
// CTIMEOUT and CCRCFAIL check only for short or long responses.
|
||||
if (sta & SDIO_STA_CTIMEOUT) {
|
||||
return SDIO_CMD_RESPONSE_TIMEOUT;
|
||||
}
|
||||
if (sta & SDIO_STA_CCRCFAIL) {
|
||||
return SDIO_CRC_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: For a short response check the response cmd field, too.
|
||||
if (wait == SDIO_WAIT_SHORT_RESPONSE) {
|
||||
if (sdio->RESPCMD != cmd) {
|
||||
return SDIO_WRONG_CMD_RESPONSE;
|
||||
}
|
||||
}
|
||||
|
||||
return SDIO_OK;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
sdio_get_dctrl_blocksize(uint32_t value)
|
||||
{
|
||||
switch(value) {
|
||||
case 1: return 0U;
|
||||
case 2: return SDIO_DCTRL_DBLOCKSIZE_0;
|
||||
case 4: return SDIO_DCTRL_DBLOCKSIZE_1;
|
||||
case 8: return SDIO_DCTRL_DBLOCKSIZE_1|SDIO_DCTRL_DBLOCKSIZE_0;
|
||||
case 16: return SDIO_DCTRL_DBLOCKSIZE_2;
|
||||
case 32: return SDIO_DCTRL_DBLOCKSIZE_2|SDIO_DCTRL_DBLOCKSIZE_0;
|
||||
case 64: return SDIO_DCTRL_DBLOCKSIZE_2|SDIO_DCTRL_DBLOCKSIZE_1;
|
||||
case 128: return SDIO_DCTRL_DBLOCKSIZE_2|SDIO_DCTRL_DBLOCKSIZE_1| \
|
||||
SDIO_DCTRL_DBLOCKSIZE_0;
|
||||
case 256: return SDIO_DCTRL_DBLOCKSIZE_3;
|
||||
case 512: return SDIO_DCTRL_DBLOCKSIZE_3|SDIO_DCTRL_DBLOCKSIZE_0;
|
||||
case 1024: return SDIO_DCTRL_DBLOCKSIZE_3|SDIO_DCTRL_DBLOCKSIZE_1;
|
||||
case 2048: return SDIO_DCTRL_DBLOCKSIZE_3|SDIO_DCTRL_DBLOCKSIZE_1| \
|
||||
SDIO_DCTRL_DBLOCKSIZE_0;
|
||||
case 4096: return SDIO_DCTRL_DBLOCKSIZE_3|SDIO_DCTRL_DBLOCKSIZE_2;
|
||||
case 8192: return SDIO_DCTRL_DBLOCKSIZE_3|SDIO_DCTRL_DBLOCKSIZE_2| \
|
||||
SDIO_DCTRL_DBLOCKSIZE_0;
|
||||
case 16384: return SDIO_DCTRL_DBLOCKSIZE_3|SDIO_DCTRL_DBLOCKSIZE_2| \
|
||||
SDIO_DCTRL_DBLOCKSIZE_1;
|
||||
}
|
||||
return SDIO_DCTRL_DBLOCKSIZE_Msk;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
sdio_prepare_data_transfer(struct sdio_config sdio_config, uint8_t read,
|
||||
uint32_t numblocks, uint32_t blocksize, uint32_t timeout)
|
||||
{
|
||||
SDIO_TypeDef *sdio = sdio_config.sdio;
|
||||
uint32_t dctrl_blocksize = sdio_get_dctrl_blocksize(blocksize);
|
||||
uint32_t reg = dctrl_blocksize | ((read > 0) ?
|
||||
SDIO_DCTRL_DTDIR : 0U) | SDIO_DCTRL_DTEN;
|
||||
|
||||
if (dctrl_blocksize == SDIO_DCTRL_DBLOCKSIZE_Msk)
|
||||
return SDIO_WRONG_BLOCKSIZE;
|
||||
|
||||
sdio->DCTRL = 0;
|
||||
sdio->DTIMER = timeout;
|
||||
sdio->DLEN = numblocks*blocksize;
|
||||
MODIFY_REG(sdio->DCTRL, DCTRL_CLEAR_MASK, reg);
|
||||
return SDIO_OK;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
sdio_read_data(struct sdio_config sdio_config, uint8_t *data,
|
||||
uint32_t numblocks, uint32_t blocksize)
|
||||
{
|
||||
// Read data by polling
|
||||
SDIO_TypeDef *sdio = sdio_config.sdio;
|
||||
uint32_t data_remaining = numblocks*blocksize;
|
||||
uint8_t *buf = data;
|
||||
|
||||
if (data == NULL) {
|
||||
return SDIO_NO_DATA_MEMORY;
|
||||
}
|
||||
|
||||
while ((sdio->STA & (SDIO_STA_RXOVERR | SDIO_STA_DCRCFAIL |
|
||||
SDIO_STA_DTIMEOUT | SDIO_STA_DATAEND)) == 0) {
|
||||
if ((sdio->STA & SDIO_STA_RXDAVL) != 0) {
|
||||
uint32_t tmp = sdio->FIFO;
|
||||
for (uint8_t i=0; (i<4) && (data_remaining>0); i++) {
|
||||
*buf = (uint8_t)(tmp & 0xFF);
|
||||
buf++;
|
||||
data_remaining--;
|
||||
tmp >>= 8U;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t sta = sdio->STA;
|
||||
sdio->ICR = SDIO_STATIC_FLAGS;
|
||||
|
||||
if (sta & SDIO_STA_DTIMEOUT) {
|
||||
return SDIO_DATA_TIMEOUT;
|
||||
}
|
||||
if (sta & SDIO_STA_DCRCFAIL) {
|
||||
return SDIO_CRC_FAIL;
|
||||
}
|
||||
if (sta & SDIO_STA_RXOVERR) {
|
||||
return SDIO_READ_OVERRUN;
|
||||
}
|
||||
|
||||
// Empty FIFO and clear flags again
|
||||
while (((sdio->STA & SDIO_STA_RXDAVL) != 0) && (data_remaining > 0)) {
|
||||
uint32_t tmp = sdio->FIFO;
|
||||
for (uint8_t i=0; (i<4) && (data_remaining>0); i++) {
|
||||
*buf = (uint8_t)(tmp & 0xFF);
|
||||
buf++;
|
||||
data_remaining--;
|
||||
tmp >>= 8U;
|
||||
}
|
||||
}
|
||||
|
||||
sdio->ICR = SDIO_STATIC_FLAGS;
|
||||
return SDIO_OK;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
sdio_write_data(struct sdio_config sdio_config, uint8_t *data,
|
||||
uint32_t numblocks, uint32_t blocksize)
|
||||
{
|
||||
// Write data by polling
|
||||
SDIO_TypeDef *sdio = sdio_config.sdio;
|
||||
uint32_t data_remaining = numblocks*blocksize;
|
||||
uint8_t *buf = data;
|
||||
|
||||
if (data == NULL) {
|
||||
return SDIO_NO_DATA_MEMORY;
|
||||
}
|
||||
|
||||
while ((sdio->STA & (SDIO_STA_TXUNDERR | SDIO_STA_DCRCFAIL |
|
||||
SDIO_STA_DTIMEOUT | SDIO_STA_DATAEND)) == 0) {
|
||||
if ((sdio->STA & SDIO_STA_TXFIFOF) == 0) {
|
||||
uint32_t tmp = 0;
|
||||
for (uint8_t i=0; (i<4) && (data_remaining>0); i++) {
|
||||
tmp |= ((uint32_t)(*buf) << (i<<3));
|
||||
buf++;
|
||||
data_remaining--;
|
||||
}
|
||||
sdio->FIFO = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t sta = sdio->STA;
|
||||
sdio->ICR = SDIO_STATIC_FLAGS;
|
||||
|
||||
if (sta & SDIO_STA_DTIMEOUT) {
|
||||
return SDIO_DATA_TIMEOUT;
|
||||
}
|
||||
if (sta & SDIO_STA_DCRCFAIL) {
|
||||
return SDIO_CRC_FAIL;
|
||||
}
|
||||
if (sta & SDIO_STA_TXUNDERR) {
|
||||
return SDIO_WRITE_UNDERRUN;
|
||||
}
|
||||
|
||||
return SDIO_OK;
|
||||
}
|
||||
|
||||
void
|
||||
sdio_send_cmd(struct sdio_config sdio_config, uint8_t cmd, uint32_t argument,
|
||||
uint8_t wait)
|
||||
{
|
||||
SDIO_TypeDef *sdio = sdio_config.sdio;
|
||||
|
||||
sdio->ARG = argument;
|
||||
//CMD and State Machine enabled. Wait for response like specified.
|
||||
uint32_t cmdreg = (cmd & 0x3F) | ((wait & 0xC0)) | SDIO_CMD_CPSMEN;
|
||||
|
||||
MODIFY_REG(sdio->CMD, CMD_CLEAR_MASK, cmdreg);
|
||||
}
|
||||
|
||||
void
|
||||
sdio_set_speed(struct sdio_config sdio_config, uint32_t speed)
|
||||
{
|
||||
SDIO_TypeDef *sdio = sdio_config.sdio;
|
||||
|
||||
uint8_t clkdiv = (SDIO_CLK_FREQ/speed)-2;
|
||||
|
||||
uint32_t sdio_confreg = SDIO_CLOCK_EDGE_RISING | \
|
||||
SDIO_CLOCK_BYPASS_DISABLE | SDIO_CLOCK_POWER_SAVE_DISABLE | \
|
||||
SDIO_BUS_WIDE_1B | SDIO_HARDWARE_FLOW_CONTROL_DISABLE | (clkdiv & 0xFF);
|
||||
MODIFY_REG(sdio->CLKCR, CLKCR_CLEAR_MASK, sdio_confreg);
|
||||
}
|
||||
25
src/stm32/sdio.h
Normal file
25
src/stm32/sdio.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef __STM32_SDIO_H
|
||||
#define __STM32_SDIO_H
|
||||
|
||||
#include <stdint.h> // uint32_t
|
||||
|
||||
struct sdio_config {
|
||||
void *sdio;
|
||||
};
|
||||
struct sdio_config sdio_setup(uint32_t bus);
|
||||
void sdio_send_cmd(struct sdio_config sdio, uint8_t cmd, uint32_t argument
|
||||
, uint8_t wait);
|
||||
uint8_t sdio_send_command(struct sdio_config sdio_config, uint8_t cmd
|
||||
, uint32_t argument, uint8_t wait
|
||||
, uint8_t *response_data
|
||||
, uint8_t *response_data_len);
|
||||
uint8_t sdio_prepare_data_transfer(struct sdio_config sdio_config, uint8_t read
|
||||
, uint32_t numblocks, uint32_t blocksize
|
||||
, uint32_t timeout);
|
||||
uint8_t sdio_read_data(struct sdio_config sdio_config, uint8_t *data
|
||||
, uint32_t numblocks, uint32_t blocksize);
|
||||
uint8_t sdio_write_data(struct sdio_config sdio_config, uint8_t *data
|
||||
, uint32_t numblocks, uint32_t blocksize);
|
||||
void sdio_set_speed(struct sdio_config sdio_config, uint32_t speed);
|
||||
|
||||
#endif // sdio.h
|
||||
@@ -138,8 +138,8 @@ enable_clock_stm32f446(void)
|
||||
while (!(PWR->CSR & PWR_CSR_ODSWRDY))
|
||||
;
|
||||
|
||||
// Enable 48Mhz USB clock
|
||||
if (CONFIG_USB) {
|
||||
// Enable 48Mhz USB clock for USB or for SDIO
|
||||
if (CONFIG_USB || CONFIG_HAVE_GPIO_SDIO) {
|
||||
uint32_t ref = (CONFIG_STM32_CLOCK_REF_INTERNAL
|
||||
? 16000000 : CONFIG_CLOCK_REF_FREQ);
|
||||
uint32_t plls_base = 2000000, plls_freq = FREQ_USB * 4;
|
||||
@@ -153,6 +153,14 @@ enable_clock_stm32f446(void)
|
||||
;
|
||||
|
||||
RCC->DCKCFGR2 = RCC_DCKCFGR2_CK48MSEL;
|
||||
} else {
|
||||
// Reset value just in case the booloader modified the default value
|
||||
RCC->DCKCFGR2 = 0;
|
||||
}
|
||||
|
||||
// Set SDIO clk to PLL48CLK
|
||||
if (CONFIG_HAVE_GPIO_SDIO) {
|
||||
MODIFY_REG(RCC->DCKCFGR2, RCC_DCKCFGR2_SDIOSEL, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user