1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/sam0_common/sam0_sdhc/sdhc.c
Dylan Laduranty ccc155e351 cpu/sam0: remove bitfield usage in sdhc driver
Signed-off-by: Dylan Laduranty <dylan.laduranty@mesotic.com>
2024-06-25 16:13:17 +02:00

1187 lines
34 KiB
C

/*
* Copyright (C) 2018 Alkgrove
*
* C file for SD card host controller driver
* This is a complete rewrite of the SD Card code from ASF4
* Copyright © Alkgrove 04/29/2018
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided
* with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific prior written
* permission.
*
* @par THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @ingroup cpu_sam0_common_sdhc
* @{
*
* @file
* @brief Driver for the SD Host Controller
*
* @author alkgrove <bob@alkgrove.com>
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
*
* @}
*/
#include <errno.h>
#include <limits.h>
#include <stdalign.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "busy_wait.h"
#include "periph_cpu.h"
#include "periph/pm.h"
#include "macros/math.h"
#include "vendor/sd_mmc_protocol.h"
#include "sdhc.h"
#include "time_units.h"
#include "ztimer.h"
#define ENABLE_DEBUG 0
#include "debug.h"
#ifndef SDHC_CLOCK
#define SDHC_CLOCK SAM0_GCLK_MAIN
#endif
#ifndef SDHC_CLOCK_SLOW
#define SDHC_CLOCK_SLOW SAM0_GCLK_TIMER
#endif
#ifndef SDHC_ENABLE_HS
#define SDHC_ENABLE_HS 1
#endif
/**
* @brief The board can overwrite this if only a single SDHC instance is used
* to save 80 Bytes of ROM.
*/
#ifndef SDHC_DEV
#ifdef SDHC1
#define SDHC_DEV state->dev
#else
#define SDHC_DEV SDHC0
#endif
#endif
/**
* @brief Monitor card insertion and removal
*/
#define NISTR_CARD_DETECT (SDHC_NISTR_CREM | SDHC_NISTR_CINS)
static sdhc_state_t *isr_ctx_0;
static sdhc_state_t *isr_ctx_1;
static void _set_speed(sdhc_state_t *state, uint32_t fsdhc);
static void _set_hc(sdhc_state_t *state);
static bool _test_voltage(sdhc_state_t *state, bool f8);
static bool _test_capacity(sdhc_state_t *state);
static bool _test_version(sdhc_state_t *state);
static bool _wait_not_busy(sdhc_state_t *state);
static bool _test_bus_width(sdhc_state_t *state);
static bool _test_high_speed(sdhc_state_t *state);
static bool _init_transfer(sdhc_state_t *state, uint32_t cmd, uint32_t arg, uint16_t block_size,
uint16_t num_blocks);
static bool sdio_test_type(sdhc_state_t *state);
static bool _card_detect(sdhc_state_t *state)
{
return state->dev->PSR.reg & SDHC_PSR_CARDINS;
}
static inline void _clock_sdcard(sdhc_state_t *state, bool on)
{
(void)state;
if (on) {
SDHC_DEV->CCR.reg |= SDHC_CCR_SDCLKEN;
}
else {
SDHC_DEV->CCR.reg &= ~SDHC_CCR_SDCLKEN;
}
}
static bool _check_mask(uint32_t val, uint32_t mask)
{
return (val & mask) == mask;
}
static void _delay(unsigned us)
{
if (IS_USED(MODULE_ZTIMER_USEC)) {
ztimer_sleep(ZTIMER_USEC, us);
} else if (IS_USED(MODULE_ZTIMER_MSEC)) {
ztimer_sleep(ZTIMER_MSEC, 1);
} else {
busy_wait_us(us);
}
}
/**
* @brief Reset the entire SDHC peripheral or a part of it
*
* @param state SDHC device context
* @param type Reset type
* [SDHC_SRR_SWRSTALL | SDHC_SRR_SWRSTCMD | SDHC_SRR_SWRSTDAT]
*/
static void _reset_sdhc(sdhc_state_t *state, uint8_t type)
{
SDHC_DEV->SRR.reg = type;
while (SDHC_DEV->SRR.reg & type) {}
if (type == SDHC_SRR_SWRSTALL) {
/* trigger card_init */
state->need_init = true;
state->error = 0;
}
}
/**
* @brief Wait for a given event while checking for errors
*
* @param state SDHC device context
* @param event Event to wait for [SDHC_NISTR_*]
* @param error_mask Mask of errors to be checked [SDHC_EISTR_*]
* @param reset Reset type in case of errors
* [SDHC_SRR_SWRSTALL | SDHC_SRR_SWRSTCMD | SDHC_SRR_SWRSTDAT]
*
* @return true if event occurred or false on error
*/
static bool _wait_for_event(sdhc_state_t *state,
uint16_t event, uint16_t error_mask,
uint8_t reset)
{
/* wait for the event given by event mask */
do {
/* check for any error in error mask */
if (SDHC_DEV->EISTR.reg & error_mask) {
state->error = SDHC_DEV->EISTR.reg;
SDHC_DEV->EISTR.reg = SDHC_EISTR_MASK;
if (IS_USED(ENABLE_DEBUG)) {
DEBUG("sdhc error: %x, ", state->error);
switch (reset) {
case SDHC_SRR_SWRSTCMD:
DEBUG("reset CMD\n");
break;
case SDHC_SRR_SWRSTDAT:
DEBUG("reset DAT\n");
break;
case SDHC_SRR_SWRSTALL:
DEBUG("reset ALL\n");
break;
default:
assert(false);
break;
}
}
_reset_sdhc(state, reset);
return false;
}
} while (!(SDHC_DEV->NISTR.reg & event));
/* clear the event */
SDHC_DEV->NISTR.reg = event;
return true;
}
static void _init_clocks(sdhc_state_t *state)
{
assert((SDHC_DEV == SDHC0)
#ifdef SDHC1
|| (SDHC_DEV == SDHC1)
#endif
);
sam0_gclk_enable(SDHC_CLOCK);
sam0_gclk_enable(SDHC_CLOCK_SLOW);
gpio_init_mux(state->cd, SAM0_SDHC_MUX);
if (gpio_is_valid(state->wp)) {
gpio_init_mux(state->wp, SAM0_SDHC_MUX);
}
if (SDHC_DEV == SDHC0) {
/* data pins are fixed */
gpio_init_mux(SAM0_SDHC0_PIN_SDDAT0, SAM0_SDHC_MUX);
gpio_init_mux(SAM0_SDHC0_PIN_SDDAT1, SAM0_SDHC_MUX);
gpio_init_mux(SAM0_SDHC0_PIN_SDDAT2, SAM0_SDHC_MUX);
gpio_init_mux(SAM0_SDHC0_PIN_SDDAT3, SAM0_SDHC_MUX);
gpio_init_mux(SAM0_SDHC0_PIN_SDCMD, SAM0_SDHC_MUX);
gpio_init_mux(SAM0_SDHC0_PIN_SDCK, SAM0_SDHC_MUX);
GCLK->PCHCTRL[SDHC0_GCLK_ID].reg = GCLK_PCHCTRL_CHEN
| GCLK_PCHCTRL_GEN(SDHC_CLOCK);
GCLK->PCHCTRL[SDHC0_GCLK_ID_SLOW].reg = GCLK_PCHCTRL_CHEN
| GCLK_PCHCTRL_GEN(SDHC_CLOCK_SLOW);
MCLK->AHBMASK.reg |= MCLK_AHBMASK_SDHC0;
isr_ctx_0 = state;
NVIC_EnableIRQ(SDHC0_IRQn);
}
#ifdef SDHC1
if (SDHC_DEV == SDHC1) {
/* data pins are fixed */
gpio_init_mux(SAM0_SDHC1_PIN_SDDAT0, SAM0_SDHC_MUX);
gpio_init_mux(SAM0_SDHC1_PIN_SDDAT1, SAM0_SDHC_MUX);
gpio_init_mux(SAM0_SDHC1_PIN_SDDAT2, SAM0_SDHC_MUX);
gpio_init_mux(SAM0_SDHC1_PIN_SDDAT3, SAM0_SDHC_MUX);
gpio_init_mux(SAM0_SDHC1_PIN_SDCMD, SAM0_SDHC_MUX);
gpio_init_mux(SAM0_SDHC1_PIN_SDCK, SAM0_SDHC_MUX);
GCLK->PCHCTRL[SDHC1_GCLK_ID].reg = GCLK_PCHCTRL_CHEN
| GCLK_PCHCTRL_GEN(SDHC_CLOCK);
GCLK->PCHCTRL[SDHC1_GCLK_ID_SLOW].reg = GCLK_PCHCTRL_CHEN
| GCLK_PCHCTRL_GEN(SDHC_CLOCK_SLOW);
MCLK->AHBMASK.reg |= MCLK_AHBMASK_SDHC1;
isr_ctx_1 = state;
NVIC_EnableIRQ(SDHC1_IRQn);
}
#endif
}
int sdhc_init(sdhc_state_t *state)
{
bool f8;
uint32_t response;
int res = 0;
/* set the initial clock slow, single bit and normal speed */
state->type = CARD_TYPE_SD;
state->version = CARD_VER_UNKNOWN;
state->rca = 0;
state->bus_width = 1;
state->high_speed = false;
state->clock = SDHC_SLOW_CLOCK_HZ;
mutex_init(&state->lock);
const mutex_t _init_locked = MUTEX_INIT_LOCKED;
state->sync = _init_locked;
_init_clocks(state);
_reset_sdhc(state, SDHC_SRR_SWRSTALL);
SDHC_DEV->NISIER.reg = NISTR_CARD_DETECT;
SDHC_DEV->TCR.reg = 14; /* max timeout is 14 or about 1sec */
SDHC_DEV->PCR.reg = SDHC_PCR_SDBPWR | SDHC_PCR_SDBVSEL_3V3;
SDHC_DEV->NISTER.reg = SDHC_NISTER_MASK; /* clear all normal interrupt bits */
SDHC_DEV->EISTER.reg = SDHC_EISTER_MASK; /* clear all error interrupt bits */
_set_hc(state);
/* 74 startup clocks (190us) */
_delay(190);
/* reset the SD card to idle state CMD0 */
f8 = false;
for (int i = 0; i < 2; i++) { /* we do this step twice before failing */
if (!sdhc_send_cmd(state, SDMMC_MCI_CMD0_GO_IDLE_STATE, 0)) {
if (i == 1) {
res = -EIO;
goto out;
}
}
/* Test for SD version 2 */
if (!sdhc_send_cmd(state, SD_CMD8_SEND_IF_COND, SD_CMD8_PATTERN | SD_CMD8_HIGH_VOLTAGE)) {
if (i == 1) {
/* bad card */
res = -EIO;
goto out;
}
}
else {
response = SDHC_DEV->RR[0].reg;
/* good response but no compliance R7 Value, legacy card */
if (response == 0xFFFFFFFF) {
f8 = false;
break;
}
if (_check_mask(response, SD_CMD8_PATTERN | SD_CMD8_HIGH_VOLTAGE)) {
f8 = true;
break;
}
}
}
if (!sdio_test_type(state)) {
res = -EIO;
goto out;
}
if (state->type & CARD_TYPE_SDIO) {
res = -ENOTSUP;
goto out;
}
/* Try to get the SD card's operating condition */
if (!_test_voltage(state, f8)) {
state->type = CARD_TYPE_UNKNOWN;
res = -EIO;
goto out;
}
/* SD MEMORY, Put the Card in Identify Mode
* Note: The CID is not used in this stack */
if (!sdhc_send_cmd(state, SDMMC_CMD2_ALL_SEND_CID, 0)) {
res = -EIO;
goto out;
}
/* Ask the card to publish a new relative address (RCA).*/
if (!sdhc_send_cmd(state, SD_CMD3_SEND_RELATIVE_ADDR, 0)) {
res = -EIO;
goto out;
}
state->rca = (uint16_t)(SDHC_DEV->RR[0].reg >> 16);
/* SD MEMORY, Get the Card-Specific Data */
if (!_test_capacity(state)) {
res = -EIO;
goto out;
}
/* Put it into Transfer Mode */
if (!sdhc_send_cmd(state, SDMMC_CMD7_SELECT_CARD_CMD, (uint32_t)state->rca << 16)) {
res = -EIO;
goto out;
}
/* SD MEMORY, Read the SCR to get card version */
if (!_test_version(state)) {
res = -EIO;
goto out;
}
if (!_test_bus_width(state)) {
res = -EIO;
goto out;
}
/* all SD Cards should support this clock at that point */
state->clock = SDHC_FAST_CLOCK_HZ;
/* update the host controller to the detected changes in bus_width and clock */
_set_hc(state);
/* if it is high speed capable, (well it is) */
if (IS_USED(SDHC_ENABLE_HS) && (SDHC_DEV->CA0R.reg & SDHC_CA0R_HSSUP)) {
if (!_test_high_speed(state)) {
res = -EIO;
goto out;
}
}
/* update host controller */
_set_hc(state);
if (!sdhc_send_cmd(state, SDMMC_CMD16_SET_BLOCKLEN, SD_MMC_BLOCK_SIZE)) {
res = -EIO;
goto out;
}
state->need_init = false;
out:
_clock_sdcard(state, 0);
return res;
}
bool sdhc_send_cmd(sdhc_state_t *state, uint32_t cmd, uint32_t arg)
{
uint32_t timeout = 0xFFFFFFFF;
uint32_t command;
uint32_t eis;
/* wait if card is busy */
while (SDHC_DEV->PSR.reg & (SDHC_PSR_CMDINHC | SDHC_PSR_CMDINHD)) {}
SDHC_DEV->TMR.reg = 0; /* only single transfer */
SDHC_DEV->BCR.reg = 0; /* block count is zero */
command = SDHC_CR_CMDIDX(cmd) | SDHC_CR_CMDTYP_NORMAL;
if (cmd & MCI_RESP_PRESENT) {
if (cmd & MCI_RESP_136) {
command |= SDHC_CR_RESPTYP_136_BIT;
}
else if (cmd & MCI_RESP_BUSY) {
command |= SDHC_CR_RESPTYP_48_BIT_BUSY;
}
else {
command |= SDHC_CR_RESPTYP_48_BIT;
}
}
if (cmd & MCI_CMD_OPENDRAIN) {
SDHC_DEV->MC1R.reg |= SDHC_MC1R_OPD;
}
else {
SDHC_DEV->MC1R.reg &= ~SDHC_MC1R_OPD;
}
eis = (cmd & MCI_RESP_CRC)
? SDHC_EISTR_CMDTEO | SDHC_EISTR_CMDEND | SDHC_EISTR_CMDIDX | SDHC_EISTR_DATTEO |
SDHC_EISTR_DATEND | SDHC_EISTR_ADMA
: SDHC_EISTR_CMDTEO | SDHC_EISTR_CMDEND | SDHC_EISTR_CMDIDX | SDHC_EISTR_DATTEO |
SDHC_EISTR_DATEND | SDHC_EISTR_ADMA | SDHC_EISTR_CMDCRC | SDHC_EISTR_DATCRC;
SDHC_DEV->ARG1R.reg = arg; /* setup the argument register */
SDHC_DEV->CR.reg = command; /* send command */
if (!_wait_for_event(state, SDHC_NISTR_CMDC, eis, SDHC_SRR_SWRSTCMD)) {
return false;
}
if (cmd & MCI_RESP_BUSY) {
do {
if (--timeout == 0) {
SDHC_DEV->SRR.reg = SDHC_SRR_SWRSTCMD; /* reset command */
while (SDHC_DEV->SRR.reg & SDHC_SRR_SWRSTCMD) {}
return false;
}
} while (!(SDHC_DEV->PSR.reg & SDHC_PSR_DATLL(1))); /* DAT[0] is busy bit */
}
return true;
}
static void _set_speed(sdhc_state_t *state, uint32_t fsdhc)
{
(void)state;
if (SDHC_DEV->CCR.reg & SDHC_CCR_SDCLKEN) {
/* wait for command/data to go inactive */
while (SDHC_DEV->PSR.reg & (SDHC_PSR_CMDINHC | SDHC_PSR_CMDINHD)) {}
/* disable the clock */
SDHC_DEV->CCR.reg = 0;
}
uint32_t div = DIV_ROUND_UP(sam0_gclk_freq(SDHC_CLOCK), fsdhc) - 1;
DEBUG("sdhc: switch to %lu Hz (div %lu) -> %lu Hz\n",
fsdhc, div, sam0_gclk_freq(SDHC_CLOCK) / (div + 1));
/* write the 10 bit clock divider */
SDHC_DEV->CCR.reg = SDHC_CCR_SDCLKFSEL(div) | SDHC_CCR_USDCLKFSEL(div >> 8)
| SDHC_CCR_CLKGSEL | SDHC_CCR_INTCLKEN;
while (!(SDHC_DEV->CCR.reg & SDHC_CCR_INTCLKS)) {} /* wait for clock to be stable */
SDHC_DEV->CCR.reg |= SDHC_CCR_SDCLKEN; /* enable clock to card */
}
/**
* _set_hc selects either one or four bit mode, low/high speed and clock
*
* bitwidth is SDHC_HC1R_DW_1BIT_Val or SDHC_HC1R_DW_4BIT_Val
* speed is SDHC_HC1R_HSEN_NORMAL_Val or SDHC_HC1R_HSEN_HIGH_Val
*/
static void _set_hc(sdhc_state_t *state)
{
if (state->high_speed) {
SDHC_DEV->HC1R.reg |= SDHC_HC1R_HSEN;
}
else {
SDHC_DEV->HC1R.reg &= ~SDHC_HC1R_HSEN;
}
if (!(SDHC_DEV->HC2R.reg & SDHC_HC2R_PVALEN)) { /* PVALEN is probably always low */
_set_speed(state, state->clock);
}
if (state->bus_width == 4) {
/* set four bit mode */
SDHC_DEV->HC1R.reg |= SDHC_HC1R_DW;
}
else {
/* set one bit mode */
SDHC_DEV->HC1R.reg &= ~SDHC_HC1R_DW;
}
}
/**
* @brief Ask to all cards to send their operations conditions (MCI only).
* - ACMD41 sends operation condition command.
* - ACMD41 reads OCR
*
* @param state pointer to sdhc
*
* @return true if success, otherwise false
*/
static bool _test_voltage(sdhc_state_t *state, bool f8)
{
uint32_t arg;
uint32_t retry = 2100;
uint32_t response;
/*
* Timeout 1s = 400KHz / ((6+6+6+6)*8) cycles = 2100 retry
* 6 = cmd byte size
* 6 = response byte size
* 6 = cmd byte size
* 6 = response byte size
*/
retry = 2100;
do {
/* CMD55 - Indicate to the card that the next command is an
* application specific command rather than a standard command.*/
if (!sdhc_send_cmd(state, SDMMC_CMD55_APP_CMD, 0)) {
return false;
}
/* (ACMD41) Sends host OCR register */
arg = OCR_VDD_27_28 | OCR_VDD_28_29 | OCR_VDD_29_30 | OCR_VDD_30_31 |
OCR_VDD_31_32 | OCR_VDD_32_33;
if (f8) { /* if not legacy card */
arg |= SD_ACMD41_HCS;
}
/* Check response */
if (!sdhc_send_cmd(state, SD_MCI_ACMD41_SD_SEND_OP_COND, arg)) {
return false;
}
response = SDHC_DEV->RR[0].reg;
if (response & OCR_POWER_UP_BUSY) {
/* Card is ready */
if ((response & OCR_CCS) != 0) {
state->type |= CARD_TYPE_HC;
}
break;
}
} while (retry--);
return retry;
}
/**
* \brief CMD9: Addressed card sends its card-specific
* data (CSD) on the CMD line mci.
*
* \return true if success, otherwise false
*/
static bool _test_capacity(sdhc_state_t *state)
{
alignas(uint32_t)
uint8_t csd[CSD_REG_BSIZE];
if (!sdhc_send_cmd(state, SDMMC_MCI_CMD9_SEND_CSD, (uint32_t)state->rca << 16)) {
return false;
}
for (int i = 0; i < 4; i++) {
uint32_t *csd32 = (void *)csd;
csd32[i] = __builtin_bswap32(SDHC_DEV->RR[3 - i].reg);
}
/*
* Card Capacity.
* ----------------------------------------------------
* For normal SD/MMC card:
* sector size is ((device size + 1) * (1 << (device size multiplier + 2)) * (1 << max_read_data_block_length))/512
* we can rearrange this like this
* sector size is ((device size + 1) * (1 << (device size multiplier + max_read_data_block_length - 7))
* device_size = SD_CSD_1_0_C_SIZE(&csd[1])
* device_size_multiplier = SD_CSD_1_0_C_SIZE_MULT(&csd[1])
* max_read_data_block_length = SD_CSD_1_0_READ_BL_LEN(&csd[1])
* Number of sectors is bytes/512
* ----------------------------------------------------
* For high capacity SD card:
* memory capacity = (C_SIZE+1) * 512K byte
* So number of sectors is ((C_SIZE+1) * 512 * 1024) / 512
* or 1024 * (C_SIZE+1)
*/
if (CSD_STRUCTURE_VERSION(&csd[1]) >= SD_CSD_VER_2_0) {
state->sectors = (SD_CSD_2_0_C_SIZE(&csd[1]) + 1) * 1024;
}
else {
state->sectors = (SD_CSD_1_0_C_SIZE(&csd[1]) + 1)
* (1 << (SD_CSD_1_0_C_SIZE_MULT(&csd[1]) + SD_CSD_1_0_READ_BL_LEN(&csd[1]) - 7));
}
return true;
}
/**
* @brief ACMD51 - Read the SD Configuration Register.
*
* @note
* SD Card Configuration Register (SCR) provides information on the SD Memory
* Card's special features that were configured into the given card. The size
* of SCR register is 64 bits.
*
*
* @return true if success, otherwise false
*/
static bool _test_version(sdhc_state_t *state)
{
uint8_t scr[SD_SCR_REG_BSIZE];
uint32_t *p = (void *)scr;
/* CMD55 - Indicate to the card that the next command is an
* application specific command rather than a standard command.*/
if (!sdhc_send_cmd(state, SDMMC_CMD55_APP_CMD, (uint32_t)state->rca << 16)) {
return false;
}
if (!_init_transfer(state, SD_ACMD51_SEND_SCR, 0, SD_SCR_REG_BSIZE, 1)) {
return false;
}
/* wait until buffer read ready */
if (!_wait_for_event(state, SDHC_NISTR_BRDRDY,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
return false;
}
for (int words = 0; words < (SD_SCR_REG_BSIZE / 4); words++) {
*p++ = SDHC_DEV->BDPR.reg;
}
/* wait until transfer is complete */
if (!_wait_for_event(state, SDHC_NISTR_TRFC,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
return false;
}
/* Get SD Memory Card - Spec. Version */
switch (SD_SCR_SD_SPEC(scr)) {
case SD_SCR_SD_SPEC_1_0_01:
state->version = CARD_VER_SD_1_0;
break;
case SD_SCR_SD_SPEC_1_10:
state->version = CARD_VER_SD_1_10;
break;
case SD_SCR_SD_SPEC_2_00:
if (SD_SCR_SD_SPEC3(scr) == SD_SCR_SD_SPEC_3_00) {
state->version = CARD_VER_SD_3_0;
}
else {
state->version = CARD_VER_SD_2_0;
}
break;
default:
state->version = CARD_VER_SD_1_0;
break;
}
return true;
}
static bool _init_transfer(sdhc_state_t *state, uint32_t cmd, uint32_t arg, uint16_t block_size,
uint16_t num_blocks)
{
uint32_t tmr;
uint32_t command;
uint32_t eis;
uint32_t timeout = 0xFFFFFFFF;
/* wait if card is busy */
while (SDHC_DEV->PSR.reg & (SDHC_PSR_CMDINHC | SDHC_PSR_CMDINHD)) {}
if (cmd & MCI_CMD_WRITE) {
tmr = SDHC_TMR_DTDSEL_WRITE;
}
else {
tmr = SDHC_TMR_DTDSEL_READ;
}
if (cmd & MCI_CMD_SDIO_BYTE) {
tmr |= SDHC_TMR_MSBSEL_SINGLE;
}
else if (cmd & MCI_CMD_SDIO_BLOCK) {
tmr |= SDHC_TMR_BCEN | SDHC_TMR_MSBSEL_MULTIPLE;
}
else if (cmd & MCI_CMD_SINGLE_BLOCK) {
tmr |= SDHC_TMR_MSBSEL_SINGLE;
}
else if (cmd & MCI_CMD_MULTI_BLOCK) {
tmr |= SDHC_TMR_BCEN | SDHC_TMR_MSBSEL_MULTIPLE;
if (tmr & SDHC_TMR_DTDSEL_WRITE) {
tmr |= SDHC_TMR_ACMDEN_CMD23;
} else {
tmr |= SDHC_TMR_ACMDEN_CMD12;
}
}
else {
return false;
}
SDHC_DEV->TMR.reg = tmr;
SDHC_DEV->BSR.reg = SDHC_BSR_BLOCKSIZE(block_size) | SDHC_BSR_BOUNDARY_4K;
SDHC_DEV->BCR.reg = SDHC_BCR_BCNT(num_blocks);
command = SDHC_CR_DPSEL_DATA;
command |= SDHC_CR_CMDIDX(cmd) | SDHC_CR_CMDTYP_NORMAL;
if (cmd & MCI_RESP_PRESENT) {
if (cmd & MCI_RESP_136) {
command |= SDHC_CR_RESPTYP_136_BIT;
}
else if (cmd & MCI_RESP_BUSY) {
command |= SDHC_CR_RESPTYP_48_BIT_BUSY;
}
else {
command |= SDHC_CR_RESPTYP_48_BIT;
}
}
if (cmd & MCI_CMD_OPENDRAIN) {
SDHC_DEV->MC1R.reg |= SDHC_MC1R_OPD;
}
else {
SDHC_DEV->MC1R.reg &= ~SDHC_MC1R_OPD;
}
eis = SDHC_EISTR_CMDTEO | SDHC_EISTR_CMDEND | SDHC_EISTR_CMDIDX | SDHC_EISTR_DATTEO
| SDHC_EISTR_DATEND | SDHC_EISTR_ADMA | SDHC_EISTR_ACMD;
if ((cmd & MCI_RESP_CRC) == 0) {
eis |= (SDHC_EISTR_CMDCRC | SDHC_EISTR_DATCRC);
}
DEBUG("sdhc: send cmd %lx\n", command);
SDHC_DEV->SSAR.reg = num_blocks; /* Setup block size for Auto CMD23 */
SDHC_DEV->ARG1R.reg = arg; /* setup the argument register */
SDHC_DEV->CR.reg = command; /* send command */
if (!_wait_for_event(state, SDHC_NISTR_CMDC, eis, SDHC_SRR_SWRSTCMD)) {
return false;
}
if (cmd & MCI_RESP_BUSY) {
do {
if (--timeout == 0) {
SDHC_DEV->SRR.reg = SDHC_SRR_SWRSTCMD; /* reset command */
while (SDHC_DEV->SRR.reg & SDHC_SRR_SWRSTCMD) {}
return false;
}
} while (!(SDHC_DEV->PSR.reg & SDHC_PSR_DATLL(1))); /* DAT[0] is busy bit */
}
return true;
}
/**
* @brief Start a read blocks transfer on the line
*
* dst must be on 4 byte boundary
*/
int sdhc_read_blocks(sdhc_state_t *state, uint32_t address, void *dst, uint16_t num_blocks)
{
uint32_t cmd;
uint32_t arg;
uint32_t *p = dst;
int res = 0;
/* card detect should be done differently
* card detect with interrupt and if removed and reinstalled
* set need_init, sdhc_init clears need_init
*/
if (!_card_detect(state)) {
return -ENODEV;
}
mutex_lock(&state->lock);
_clock_sdcard(state, 1);
if (state->need_init) {
res = sdhc_init(state);
if (res != 0) {
goto out;
}
}
if (state->type & CARD_TYPE_HC) {
arg = address;
}
else {
arg = address * SD_MMC_BLOCK_SIZE;
}
cmd = (1 == num_blocks)
? SDMMC_CMD17_READ_SINGLE_BLOCK
: SDMMC_CMD18_READ_MULTIPLE_BLOCK;
if (!_wait_not_busy(state)) {
res = -EBUSY;
goto out;
}
if (!_init_transfer(state, cmd, arg, SD_MMC_BLOCK_SIZE, num_blocks)) {
res = -EIO;
goto out;
}
if (SDHC_DEV->RR[0].reg & CARD_STATUS_ERR_RD_WR) {
res = -EIO;
goto out;
}
/* wait until buffer read ready */
if (!_wait_for_event(state, SDHC_NISTR_BRDRDY,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
res = -EIO;
goto out;
}
int num_words = (num_blocks * SD_MMC_BLOCK_SIZE) / 4;
for (int words = 0; words < num_words; words++) {
while (!(SDHC_DEV->PSR.reg & SDHC_PSR_BUFRDEN)) {}
*p++ = SDHC_DEV->BDPR.reg;
}
/* wait until transfer is complete */
if (!_wait_for_event(state, SDHC_NISTR_TRFC,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
res = -EIO;
goto out;
}
out:
_clock_sdcard(state, 0);
mutex_unlock(&state->lock);
return res;
}
/**
* @brief Start a write blocks transfer on the line
* @note The driver will use the DMA available to speed up the transfer.
* (There is no evidence of that.)
* @pre @p src must be on 4 byte boundary
*/
int sdhc_write_blocks(sdhc_state_t *state, uint32_t address, const void *src,
uint16_t num_blocks)
{
assert(((uintptr_t)src & 3) == 0);
uint32_t cmd;
uint32_t arg;
const uint32_t *p = src;
int res = 0;
if (!_card_detect(state)) {
return -ENODEV;
}
mutex_lock(&state->lock);
_clock_sdcard(state, 1);
if (state->need_init) {
res = sdhc_init(state);
if (res != 0) {
goto out;
}
}
/*
* SDSC Card (CCS=0) uses byte unit address,
* SDHC and SDXC Cards (CCS=1) use block unit address (512 Bytes unit).
*/
if (state->type & CARD_TYPE_HC) {
arg = address;
}
else {
arg = address * SD_MMC_BLOCK_SIZE;
}
cmd = (1 == num_blocks) ? SDMMC_CMD24_WRITE_BLOCK : SDMMC_CMD25_WRITE_MULTIPLE_BLOCK;
if (!_wait_not_busy(state)) {
res = -EBUSY;
goto out;
}
if (!_init_transfer(state, cmd, arg, SD_MMC_BLOCK_SIZE, num_blocks)) {
res = -EIO;
goto out;
}
if (SDHC_DEV->RR[0].reg & CARD_STATUS_ERR_RD_WR) {
res = -EIO;
goto out;
}
/* wait until buffer write ready */
if (!_wait_for_event(state, SDHC_NISTR_BWRRDY,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
res = -EIO;
goto out;
}
/* Write data */
int num_words = (num_blocks * SD_MMC_BLOCK_SIZE) / 4;
for (int words = 0; words < num_words; words++) {
while (!(SDHC_DEV->PSR.reg & SDHC_PSR_BUFWREN)) {}
SDHC_DEV->BDPR.reg = *p++;
}
/* wait until transfer is complete */
if (!_wait_for_event(state, SDHC_NISTR_TRFC,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
res = -EIO;
goto out;
}
out:
_wait_not_busy(state);
_clock_sdcard(state, 0);
mutex_unlock(&state->lock);
return res;
}
int sdhc_erase_blocks(sdhc_state_t *state, uint32_t start, uint16_t num_blocks)
{
uint32_t end = start + num_blocks - 1;
int res = 0;
if (!_card_detect(state)) {
return -ENODEV;
}
mutex_lock(&state->lock);
_clock_sdcard(state, 1);
if (state->need_init) {
res = sdhc_init(state);
if (res != 0) {
goto out;
}
}
/*
* SDSC Card (CCS=0) uses byte unit address,
* SDHC and SDXC Cards (CCS=1) use block unit address (512 Bytes unit).
*/
if (!(state->type & CARD_TYPE_HC)) {
start *= SD_MMC_BLOCK_SIZE;
end *= SD_MMC_BLOCK_SIZE;
}
if (!_wait_not_busy(state)) {
res = -EBUSY;
goto out;
}
sdhc_send_cmd(state, SD_CMD32_ERASE_WR_BLK_START, start);
sdhc_send_cmd(state, SD_CMD33_ERASE_WR_BLK_END, end);
if (!sdhc_send_cmd(state, SDMMC_CMD38_ERASE, 0)) {
res = -EIO;
}
out:
_wait_not_busy(state);
_clock_sdcard(state, 0);
mutex_unlock(&state->lock);
return res;
}
/**
* @brief CMD13 - Addressed card sends its status register.
* This function waits the clear of the busy flag
*
* @return true if success, otherwise false
*/
static bool _wait_not_busy(sdhc_state_t *state)
{
uint32_t timeout;
/* Wait for data ready status.
* Nec timing: 0 to unlimited
* However a timeout is used.
* 200 000 * 8 cycles
*/
timeout = 200000;
do {
if (!sdhc_send_cmd(state, SDMMC_MCI_CMD13_SEND_STATUS, (uint32_t)state->rca << 16)) {
return false;
}
/* Check busy flag */
if (SDHC_DEV->RR[0].reg & CARD_STATUS_READY_FOR_DATA) {
break;
}
if (timeout-- == 0) {
return false;
}
} while (1);
return true;
}
/**
* @brief Try to get the SDIO card's operating condition
* - CMD5 to read OCR NF field
* - CMD5 to wait OCR power up busy
* - CMD5 to read OCR MP field
* sd_mmc_card->type is updated
*
* @return true if success, otherwise false
*/
static bool sdio_test_type(sdhc_state_t *state)
{
uint32_t response;
uint32_t cmd5_retry = 5000;
/* CMD5 - SDIO send operation condition (OCR) command. */
if (!sdhc_send_cmd(state, SDIO_CMD5_SEND_OP_COND, 0)) {
return true; /* No error but card type not updated */
}
response = SDHC_DEV->RR[0].reg;
if ((response & OCR_SDIO_NF) == 0) {
return true; /* No error but card type not updated */
}
/*
* Wait card ready
* Timeout 1s = 400KHz / ((6+4)*8) cycles = 5000 retry
* 6 = cmd byte size
* 4(SPI) 6(MCI) = response byte size
*/
while (1) {
response &= OCR_VDD_27_28 | OCR_VDD_28_29 | OCR_VDD_29_30
| OCR_VDD_30_31 | OCR_VDD_31_32 | OCR_VDD_32_33;
/* CMD5 - SDIO send operation condition (OCR) command.*/
if (!sdhc_send_cmd(state, SDIO_CMD5_SEND_OP_COND, response)) {
return false;
}
response = SDHC_DEV->RR[0].reg;
if ((response & OCR_POWER_UP_BUSY) == OCR_POWER_UP_BUSY) {
break;
}
if (cmd5_retry-- == 0) {
return false;
}
}
/* Update card type at the end of busy */
if ((response & OCR_SDIO_MP) > 0) {
state->type = CARD_TYPE_SD_COMBO;
}
else {
state->type = CARD_TYPE_SDIO;
}
return true;
}
static bool _test_high_speed(sdhc_state_t *state)
{
alignas(uint32_t)
uint8_t switch_status[SD_SW_STATUS_BSIZE] = { 0 };
uint32_t *p = (void *)switch_status;
if ((state->type & CARD_TYPE_SD) && (state->version > CARD_VER_SD_1_0)) {
if (!_init_transfer(state, SD_CMD6_SWITCH_FUNC,
SD_CMD6_MODE_SWITCH | SD_CMD6_GRP6_NO_INFLUENCE |
SD_CMD6_GRP5_NO_INFLUENCE | SD_CMD6_GRP4_NO_INFLUENCE |
SD_CMD6_GRP3_NO_INFLUENCE | SD_CMD6_GRP2_DEFAULT |
SD_CMD6_GRP1_HIGH_SPEED, SD_SW_STATUS_BSIZE, 1)) {
return false;
}
/* wait until buffer read ready */
if (!_wait_for_event(state, SDHC_NISTR_BRDRDY,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
return false;
}
for (int words = 0; words < (SD_SW_STATUS_BSIZE / 4); words++) {
*p++ = SDHC_DEV->BDPR.reg;
}
/* wait until transfer is complete */
if (!_wait_for_event(state, SDHC_NISTR_TRFC,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
return false;
}
if (SDHC_DEV->RR[0].reg & CARD_STATUS_SWITCH_ERROR) {
return false;
}
if (SD_SW_STATUS_FUN_GRP1_RC(switch_status) == SD_SW_STATUS_FUN_GRP_RC_ERROR) {
/* No supported, it is not a protocol error */
return true;
}
if (SD_SW_STATUS_FUN_GRP1_BUSY(switch_status)) {
return false;
}
/* CMD6 function switching period is within 8 clocks
* after the end bit of status data.*/
_delay(100);
}
state->high_speed = true;
state->clock *= 2; /* turbo clock */
return true;
}
static bool _test_bus_width(sdhc_state_t *state)
{
/**
* A SD memory card always supports bus 4bit
* A SD COMBO card always supports bus 4bit
* A SDIO Full-Speed alone always supports 4bit
* A SDIO Low-Speed alone can supports 4bit (Optional)
*/
if (state->type & CARD_TYPE_SD) {
/* CMD55 - Indicate to the card that the next command is an
* application specific command rather than a standard command.*/
if (!sdhc_send_cmd(state, SDMMC_CMD55_APP_CMD, (uint32_t)state->rca << 16)) {
return false;
}
if (!sdhc_send_cmd(state, SD_ACMD6_SET_BUS_WIDTH, SD_ACMD6_4B)) {
return false;
}
}
state->bus_width = 4;
return true;
}
static void _isr(sdhc_state_t *state)
{
/* if card got inserted we need to re-init */
if (SDHC_DEV->NISTR.reg & NISTR_CARD_DETECT) {
SDHC_DEV->NISTR.reg = NISTR_CARD_DETECT;
DEBUG_PUTS("card presence changed");
state->need_init = true;
}
cortexm_isr_end();
}
#ifdef SDHC_DEV_ISR
void SDHC_DEV_ISR(void)
{
if (SDHC_DEV == SDHC0) {
_isr(isr_ctx_0);
}
if (SDHC_DEV == SDHC1) {
_isr(isr_ctx_1);
}
}
#else
void isr_sdhc0(void)
{
_isr(isr_ctx_0);
}
void isr_sdhc1(void)
{
_isr(isr_ctx_1);
}
#endif