SDMMC2 driver.

git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@13271 27425a3e-05d8-49a3-a47f-9c15f0e5edd8
This commit is contained in:
Giovanni Di Sirio 2020-01-14 09:33:05 +00:00
parent 9bb67b290f
commit d6639df4af
6 changed files with 1303 additions and 6 deletions

View File

@ -0,0 +1,9 @@
ifeq ($(USE_SMART_BUILD),yes)
ifneq ($(findstring HAL_USE_SDC TRUE,$(HALCONF)),)
PLATFORMSRC += $(CHIBIOS)/os/hal/ports/STM32/LLD/SDMMCv2/hal_sdc_lld.c
endif
else
PLATFORMSRC += $(CHIBIOS)/os/hal/ports/STM32/LLD/SDMMCv2/hal_sdc_lld.c
endif
PLATFORMINC += $(CHIBIOS)/os/hal/ports/STM32/LLD/SDMMCv2

View File

@ -0,0 +1,936 @@
/*
ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file SDMMCv2/hal_sdc_lld.c
* @brief STM32 SDC subsystem low level driver source.
*
* @addtogroup SDC
* @{
*/
#include <string.h>
#include "hal.h"
#if HAL_USE_SDC || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
#define SDMMC_ICR_ALL_FLAGS 0xFFFFFFFFU
#define SDMMC_STA_ERROR_MASK \
(SDMMC_STA_CCRCFAIL | SDMMC_STA_DCRCFAIL | \
SDMMC_STA_CTIMEOUT | SDMMC_STA_DTIMEOUT | \
SDMMC_STA_TXUNDERR | SDMMC_STA_RXOVERR)
#define SDMMC_CLKDIV_HS (2 - 2)
#define SDMMC_CLKDIV_LS (120 - 2)
#define SDMMC1_WRITE_TIMEOUT \
(((STM32_SDMMC1CLK / (SDMMC_CLKDIV_HS + 2)) / 1000) * \
STM32_SDC_SDMMC_WRITE_TIMEOUT)
#define SDMMC1_READ_TIMEOUT \
(((STM32_SDMMC1CLK / (SDMMC_CLKDIV_HS + 2)) / 1000) * \
STM32_SDC_SDMMC_READ_TIMEOUT)
#define SDMMC2_WRITE_TIMEOUT \
(((STM32_SDMMC2CLK / (SDMMC_CLKDIV_HS + 2)) / 1000) * \
STM32_SDC_SDMMC_WRITE_TIMEOUT)
#define SDMMC2_READ_TIMEOUT \
(((STM32_SDMMC2CLK / (SDMMC_CLKDIV_HS + 2)) / 1000) * \
STM32_SDC_SDMMC_READ_TIMEOUT)
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/** @brief SDCD1 driver identifier.*/
#if STM32_SDC_USE_SDMMC1 || defined(__DOXYGEN__)
SDCDriver SDCD1;
#endif
/** @brief SDCD2 driver identifier.*/
#if STM32_SDC_USE_SDMMC2 || defined(__DOXYGEN__)
SDCDriver SDCD2;
#endif
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
#if STM32_SDC_SDMMC_UNALIGNED_SUPPORT
/**
* @brief Buffer for temporary storage during unaligned transfers.
*/
static union {
uint32_t alignment;
uint8_t buf[MMCSD_BLOCK_SIZE];
} u;
#endif /* STM32_SDC_SDMMC_UNALIGNED_SUPPORT */
/**
* @brief SDIO default configuration.
*/
static const SDCConfig sdc_default_cfg = {
NULL,
SDC_MODE_4BIT
};
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/**
* @brief Prepares to handle read transaction.
* @details Designed for read special registers from card.
*
* @param[in] sdcp pointer to the @p SDCDriver object
* @param[out] buf pointer to the read buffer
* @param[in] bytes number of bytes to read
*
* @return The operation status.
* @retval HAL_SUCCESS operation succeeded.
* @retval HAL_FAILED operation failed.
*
* @notapi
*/
static bool sdc_lld_prepare_read_bytes(SDCDriver *sdcp,
uint8_t *buf, uint32_t bytes) {
osalDbgCheck(bytes < 0x1000000);
sdcp->sdmmc->DTIMER = sdcp->rtmo;
/* Checks for errors and waits for the card to be ready for reading.*/
if (_sdc_wait_for_transfer_state(sdcp))
return HAL_FAILED;
/* Setting up data transfer.*/
sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS;
sdcp->sdmmc->MASK = SDMMC_MASK_DCRCFAILIE |
SDMMC_MASK_DTIMEOUTIE |
SDMMC_MASK_RXOVERRIE |
SDMMC_MASK_DATAENDIE;
sdcp->sdmmc->DLEN = bytes;
/* Transfer modes.*/
sdcp->sdmmc->DCTRL = SDMMC_DCTRL_DTDIR |
SDMMC_DCTRL_DTMODE; /* Multibyte data transfer.*/
/* Prepares IDMA.*/
sdcp->sdmmc->IDMABASE0 = (uint32_t)buf;
sdcp->sdmmc->IDMACTRL = SDMMC_IDMA_IDMAEN;
/* Transaction starts just after DTEN bit setting.*/
sdcp->sdmmc->DCTRL |= SDMMC_DCTRL_DTEN;
return HAL_SUCCESS;
}
/**
* @brief Prepares card to handle read transaction.
*
* @param[in] sdcp pointer to the @p SDCDriver object
* @param[in] startblk first block to read
* @param[in] n number of blocks to read
* @param[in] resp pointer to the response buffer
*
* @return The operation status.
* @retval HAL_SUCCESS operation succeeded.
* @retval HAL_FAILED operation failed.
*
* @notapi
*/
static bool sdc_lld_prepare_read(SDCDriver *sdcp, uint32_t startblk,
uint32_t n, uint32_t *resp) {
/* Driver handles data in 512 bytes blocks (just like HC cards). But if we
have not HC card than we must convert address from blocks to bytes.*/
if (!(sdcp->cardmode & SDC_MODE_HIGH_CAPACITY))
startblk *= MMCSD_BLOCK_SIZE;
if (n > 1) {
/* Send read multiple blocks command to card.*/
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_READ_MULTIPLE_BLOCK,
startblk, resp) || MMCSD_R1_ERROR(resp[0]))
return HAL_FAILED;
}
else {
/* Send read single block command.*/
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_READ_SINGLE_BLOCK,
startblk, resp) || MMCSD_R1_ERROR(resp[0]))
return HAL_FAILED;
}
return HAL_SUCCESS;
}
/**
* @brief Prepares card to handle write transaction.
*
* @param[in] sdcp pointer to the @p SDCDriver object
* @param[in] startblk first block to read
* @param[in] n number of blocks to write
* @param[in] resp pointer to the response buffer
*
* @return The operation status.
* @retval HAL_SUCCESS operation succeeded.
* @retval HAL_FAILED operation failed.
*
* @notapi
*/
static bool sdc_lld_prepare_write(SDCDriver *sdcp, uint32_t startblk,
uint32_t n, uint32_t *resp) {
/* Driver handles data in 512 bytes blocks (just like HC cards). But if we
have not HC card than we must convert address from blocks to bytes.*/
if (!(sdcp->cardmode & SDC_MODE_HIGH_CAPACITY))
startblk *= MMCSD_BLOCK_SIZE;
if (n > 1) {
/* Write multiple blocks command.*/
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_WRITE_MULTIPLE_BLOCK,
startblk, resp) || MMCSD_R1_ERROR(resp[0]))
return HAL_FAILED;
}
else {
/* Write single block command.*/
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_WRITE_BLOCK,
startblk, resp) || MMCSD_R1_ERROR(resp[0]))
return HAL_FAILED;
}
return HAL_SUCCESS;
}
/**
* @brief Wait end of data transaction and performs finalizations.
*
* @param[in] sdcp pointer to the @p SDCDriver object
* @param[in] n number of blocks in transaction
* @param[in] resp pointer to the response buffer
*
* @return The operation status.
* @retval HAL_SUCCESS operation succeeded.
* @retval HAL_FAILED operation failed.
*/
static bool sdc_lld_wait_transaction_end(SDCDriver *sdcp, uint32_t n,
uint32_t *resp) {
/* Note the mask is checked before going to sleep because the interrupt
may have occurred before reaching the critical zone.*/
osalSysLock();
if (sdcp->sdmmc->MASK != 0)
osalThreadSuspendS(&sdcp->thread);
/* Stopping operations.*/
sdcp->sdmmc->IDMACTRL = 0;
sdcp->sdmmc->MASK = 0;
sdcp->sdmmc->DCTRL = 0;
if ((sdcp->sdmmc->STA & SDMMC_STA_DATAEND) == 0) {
osalSysUnlock();
return HAL_FAILED;
}
/* Clearing status.*/
sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS;
osalSysUnlock();
/* Finalize transaction.*/
if (n > 1)
return sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp);
return HAL_SUCCESS;
}
/**
* @brief Gets SDC errors.
*
* @param[in] sdcp pointer to the @p SDCDriver object
* @param[in] sta value of the STA register
*
* @notapi
*/
static void sdc_lld_collect_errors(SDCDriver *sdcp, uint32_t sta) {
uint32_t errors = SDC_NO_ERROR;
if (sta & SDMMC_STA_CCRCFAIL)
errors |= SDC_CMD_CRC_ERROR;
if (sta & SDMMC_STA_DCRCFAIL)
errors |= SDC_DATA_CRC_ERROR;
if (sta & SDMMC_STA_CTIMEOUT)
errors |= SDC_COMMAND_TIMEOUT;
if (sta & SDMMC_STA_DTIMEOUT)
errors |= SDC_DATA_TIMEOUT;
if (sta & SDMMC_STA_TXUNDERR)
errors |= SDC_TX_UNDERRUN;
if (sta & SDMMC_STA_RXOVERR)
errors |= SDC_RX_OVERRUN;
/* if (sta & SDMMC_STA_STBITERR)
errors |= SDC_STARTBIT_ERROR;*/
sdcp->errors |= errors;
}
/**
* @brief Performs clean transaction stopping in case of errors.
*
* @param[in] sdcp pointer to the @p SDCDriver object
* @param[in] n number of blocks in transaction
* @param[in] resp pointer to the response buffer
*
* @notapi
*/
static void sdc_lld_error_cleanup(SDCDriver *sdcp,
uint32_t n,
uint32_t *resp) {
uint32_t sta = sdcp->sdmmc->STA;
/* Clearing status.*/
sta = sdcp->sdmmc->STA;
sdcp->sdmmc->ICR = sta;
sdc_lld_collect_errors(sdcp, sta);
if (n > 1)
sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp);
}
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
/**
* @brief SDMMC1 IRQ handler.
* @details It just wakes transaction thread, errors handling is performed in
* there.
*
* @isr
*/
#if STM32_SDC_USE_SDMMC1 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(STM32_SDMMC1_HANDLER) {
OSAL_IRQ_PROLOGUE();
osalSysLockFromISR();
/* Disables the source but the status flags are not reset because the
read/write functions needs to check them.*/
SDMMC1->MASK = 0;
osalThreadResumeI(&SDCD1.thread, MSG_OK);
osalSysUnlockFromISR();
OSAL_IRQ_EPILOGUE();
}
#endif
/**
* @brief SDMMC2 IRQ handler.
* @details It just wakes transaction thread, errors handling is performed in
* there.
*
* @isr
*/
#if STM32_SDC_USE_SDMMC2 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(STM32_SDMMC2_HANDLER) {
OSAL_IRQ_PROLOGUE();
osalSysLockFromISR();
/* Disables the source but the status flags are not reset because the
read/write functions needs to check them.*/
SDMMC2->MASK = 0;
osalThreadResumeI(&SDCD2.thread, MSG_OK);
osalSysUnlockFromISR();
OSAL_IRQ_EPILOGUE();
}
#endif
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level SDC driver initialization.
*
* @notapi
*/
void sdc_lld_init(void) {
#if STM32_SDC_USE_SDMMC1
sdcObjectInit(&SDCD1);
SDCD1.thread = NULL;
SDCD1.rtmo = SDMMC1_READ_TIMEOUT;
SDCD1.wtmo = SDMMC1_WRITE_TIMEOUT;
SDCD1.sdmmc = SDMMC1;
nvicEnableVector(STM32_SDMMC1_NUMBER, STM32_SDC_SDMMC1_IRQ_PRIORITY);
#endif
#if STM32_SDC_USE_SDMMC2
sdcObjectInit(&SDCD2);
SDCD2.thread = NULL;
SDCD2.rtmo = SDMMC2_READ_TIMEOUT;
SDCD2.wtmo = SDMMC2_WRITE_TIMEOUT;
SDCD2.sdmmc = SDMMC2;
nvicEnableVector(STM32_SDMMC2_NUMBER, STM32_SDC_SDMMC2_IRQ_PRIORITY);
#endif
}
/**
* @brief Configures and activates the SDC peripheral.
*
* @param[in] sdcp pointer to the @p SDCDriver object
*
* @notapi
*/
void sdc_lld_start(SDCDriver *sdcp) {
/* Checking configuration, using a default if NULL has been passed.*/
if (sdcp->config == NULL) {
sdcp->config = &sdc_default_cfg;
}
/* If in stopped state then clocks are enabled and DMA initialized.*/
if (sdcp->state == BLK_STOP) {
#if STM32_SDC_USE_SDMMC1
if (&SDCD1 == sdcp) {
rccEnableSDMMC1(true);
}
#endif /* STM32_SDC_USE_SDMMC1 */
#if STM32_SDC_USE_SDMMC2
if (&SDCD2 == sdcp) {
rccEnableSDMMC2(true);
}
#endif /* STM32_SDC_USE_SDMMC2 */
}
/* Configuration, card clock is initially stopped.*/
sdcp->sdmmc->IDMACTRL = 0;
sdcp->sdmmc->DCTRL = 0;
sdcp->sdmmc->POWER = 0;
sdcp->sdmmc->CLKCR = 0;
sdcp->sdmmc->DTIMER = 0;
sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS;
}
/**
* @brief Deactivates the SDC peripheral.
*
* @param[in] sdcp pointer to the @p SDCDriver object
*
* @notapi
*/
void sdc_lld_stop(SDCDriver *sdcp) {
if (sdcp->state != BLK_STOP) {
/* SDIO deactivation.*/
sdcp->sdmmc->IDMACTRL = 0;
sdcp->sdmmc->DCTRL = 0;
sdcp->sdmmc->POWER = 0;
sdcp->sdmmc->CLKCR = 0;
sdcp->sdmmc->DTIMER = 0;
sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS;
/* Clock deactivation.*/
#if STM32_SDC_USE_SDMMC1
if (&SDCD1 == sdcp) {
rccDisableSDMMC1();
}
#endif
#if STM32_SDC_USE_SDMMC2
if (&SDCD2 == sdcp) {
rccDisableSDMMC2();
}
#endif
}
}
/**
* @brief Starts the SDIO clock and sets it to init mode (400kHz or less).
*
* @param[in] sdcp pointer to the @p SDCDriver object
*
* @notapi
*/
void sdc_lld_start_clk(SDCDriver *sdcp) {
/* Initial clock setting: 400kHz, 1bit mode.*/
sdcp->sdmmc->CLKCR = SDMMC_CLKDIV_LS;
sdcp->sdmmc->POWER |= SDMMC_POWER_PWRCTRL_0 | SDMMC_POWER_PWRCTRL_1;
/* TODO sdcp->sdmmc->CLKCR |= SDMMC_CLKCR_CLKEN;*/
/* Clock activation delay.*/
osalThreadSleep(OSAL_MS2I(STM32_SDC_SDMMC_CLOCK_DELAY));
}
/**
* @brief Sets the SDIO clock to data mode (25/50 MHz or less).
*
* @param[in] sdcp pointer to the @p SDCDriver object
* @param[in] clk the clock mode
*
* @notapi
*/
void sdc_lld_set_data_clk(SDCDriver *sdcp, sdcbusclk_t clk) {
#if STM32_SDC_SDMMC_50MHZ
if (SDC_CLK_50MHz == clk) {
sdcp->sdmmc->CLKCR = (sdcp->sdmmc->CLKCR & 0xFFFFFF00U) |
#if STM32_SDC_SDMMC_PWRSAV
SDMMC_CLKDIV_HS | SDMMC_CLKCR_BYPASS |
SDMMC_CLKCR_PWRSAV;
#else
SDMMC_CLKDIV_HS | SDMMC_CLKCR_BYPASS;
#endif
}
else {
#if STM32_SDC_SDMMC_PWRSAV
sdcp->sdmmc->CLKCR = (sdcp->sdmmc->CLKCR & 0xFFFFFF00U) | SDMMC_CLKDIV_HS |
SDMMC_CLKCR_PWRSAV;
#else
sdcp->sdmmc->CLKCR = (sdcp->sdmmc->CLKCR & 0xFFFFFF00U) | SDMMC_CLKDIV_HS;
#endif
}
#else
(void)clk;
#if STM32_SDC_SDMMC_PWRSAV
sdcp->sdmmc->CLKCR = (sdcp->sdmmc->CLKCR & 0xFFFFFF00U) | SDMMC_CLKDIV_HS |
SDMMC_CLKCR_PWRSAV;
#else
sdcp->sdmmc->CLKCR = (sdcp->sdmmc->CLKCR & 0xFFFFFF00U) | SDMMC_CLKDIV_HS;
#endif
#endif
}
/**
* @brief Stops the SDIO clock.
*
* @param[in] sdcp pointer to the @p SDCDriver object
*
* @notapi
*/
void sdc_lld_stop_clk(SDCDriver *sdcp) {
sdcp->sdmmc->CLKCR = 0;
sdcp->sdmmc->POWER = 0;
}
/**
* @brief Switches the bus to 1, 4 or 8 bits mode.
*
* @param[in] sdcp pointer to the @p SDCDriver object
* @param[in] mode bus mode
*
* @notapi
*/
void sdc_lld_set_bus_mode(SDCDriver *sdcp, sdcbusmode_t mode) {
uint32_t clk = sdcp->sdmmc->CLKCR & ~SDMMC_CLKCR_WIDBUS;
switch (mode) {
case SDC_MODE_1BIT:
sdcp->sdmmc->CLKCR = clk;
break;
case SDC_MODE_4BIT:
sdcp->sdmmc->CLKCR = clk | SDMMC_CLKCR_WIDBUS_0;
break;
case SDC_MODE_8BIT:
sdcp->sdmmc->CLKCR = clk | SDMMC_CLKCR_WIDBUS_1;
break;
}
}
/**
* @brief Sends an SDIO command with no response expected.
*
* @param[in] sdcp pointer to the @p SDCDriver object
* @param[in] cmd card command
* @param[in] arg command argument
*
* @notapi
*/
void sdc_lld_send_cmd_none(SDCDriver *sdcp, uint8_t cmd, uint32_t arg) {
sdcp->sdmmc->ARG = arg;
sdcp->sdmmc->CMD = (uint32_t)cmd | SDMMC_CMD_CPSMEN;
while ((sdcp->sdmmc->STA & SDMMC_STA_CMDSENT) == 0)
;
sdcp->sdmmc->ICR = SDMMC_ICR_CMDSENTC;
}
/**
* @brief Sends an SDIO command with a short response expected.
* @note The CRC is not verified.
*
* @param[in] sdcp pointer to the @p SDCDriver object
* @param[in] cmd card command
* @param[in] arg command argument
* @param[out] resp pointer to the response buffer (one word)
*
* @return The operation status.
* @retval HAL_SUCCESS operation succeeded.
* @retval HAL_FAILED operation failed.
*
* @notapi
*/
bool sdc_lld_send_cmd_short(SDCDriver *sdcp, uint8_t cmd, uint32_t arg,
uint32_t *resp) {
uint32_t sta;
sdcp->sdmmc->ARG = arg;
sdcp->sdmmc->CMD = (uint32_t)cmd | SDMMC_CMD_WAITRESP_0 | SDMMC_CMD_CPSMEN;
while (((sta = sdcp->sdmmc->STA) & (SDMMC_STA_CMDREND | SDMMC_STA_CTIMEOUT |
SDMMC_STA_CCRCFAIL)) == 0)
;
sdcp->sdmmc->ICR = sta & (SDMMC_STA_CMDREND | SDMMC_STA_CTIMEOUT |
SDMMC_STA_CCRCFAIL);
if ((sta & (SDMMC_STA_CTIMEOUT)) != 0) {
sdc_lld_collect_errors(sdcp, sta);
return HAL_FAILED;
}
*resp = sdcp->sdmmc->RESP1;
return HAL_SUCCESS;
}
/**
* @brief Sends an SDIO command with a short response expected and CRC.
*
* @param[in] sdcp pointer to the @p SDCDriver object
* @param[in] cmd card command
* @param[in] arg command argument
* @param[out] resp pointer to the response buffer (one word)
*
* @return The operation status.
* @retval HAL_SUCCESS operation succeeded.
* @retval HAL_FAILED operation failed.
*
* @notapi
*/
bool sdc_lld_send_cmd_short_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg,
uint32_t *resp) {
uint32_t sta;
sdcp->sdmmc->ARG = arg;
sdcp->sdmmc->CMD = (uint32_t)cmd | SDMMC_CMD_WAITRESP_0 | SDMMC_CMD_CPSMEN;
while (((sta = sdcp->sdmmc->STA) & (SDMMC_STA_CMDREND | SDMMC_STA_CTIMEOUT |
SDMMC_STA_CCRCFAIL)) == 0)
;
sdcp->sdmmc->ICR = sta & (SDMMC_STA_CMDREND | SDMMC_STA_CTIMEOUT | SDMMC_STA_CCRCFAIL);
if ((sta & (SDMMC_STA_CTIMEOUT | SDMMC_STA_CCRCFAIL)) != 0) {
sdc_lld_collect_errors(sdcp, sta);
return HAL_FAILED;
}
*resp = sdcp->sdmmc->RESP1;
return HAL_SUCCESS;
}
/**
* @brief Sends an SDIO command with a long response expected and CRC.
*
* @param[in] sdcp pointer to the @p SDCDriver object
* @param[in] cmd card command
* @param[in] arg command argument
* @param[out] resp pointer to the response buffer (four words)
*
* @return The operation status.
* @retval HAL_SUCCESS operation succeeded.
* @retval HAL_FAILED operation failed.
*
* @notapi
*/
bool sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg,
uint32_t *resp) {
uint32_t sta;
(void)sdcp;
sdcp->sdmmc->ARG = arg;
sdcp->sdmmc->CMD = (uint32_t)cmd | SDMMC_CMD_WAITRESP_0 | SDMMC_CMD_WAITRESP_1 |
SDMMC_CMD_CPSMEN;
while (((sta = sdcp->sdmmc->STA) & (SDMMC_STA_CMDREND | SDMMC_STA_CTIMEOUT |
SDMMC_STA_CCRCFAIL)) == 0)
;
sdcp->sdmmc->ICR = sta & (SDMMC_STA_CMDREND | SDMMC_STA_CTIMEOUT |
SDMMC_STA_CCRCFAIL);
if ((sta & (SDMMC_STA_ERROR_MASK)) != 0) {
sdc_lld_collect_errors(sdcp, sta);
return HAL_FAILED;
}
/* Save bytes in reverse order because MSB in response comes first.*/
*resp++ = sdcp->sdmmc->RESP4;
*resp++ = sdcp->sdmmc->RESP3;
*resp++ = sdcp->sdmmc->RESP2;
*resp = sdcp->sdmmc->RESP1;
return HAL_SUCCESS;
}
/**
* @brief Reads special registers using data bus.
* @details Needs only during card detection procedure.
*
* @param[in] sdcp pointer to the @p SDCDriver object
* @param[out] buf pointer to the read buffer
* @param[in] bytes number of bytes to read
* @param[in] cmd card command
* @param[in] arg argument for command
*
* @return The operation status.
* @retval HAL_SUCCESS operation succeeded.
* @retval HAL_FAILED operation failed.
*
* @notapi
*/
bool sdc_lld_read_special(SDCDriver *sdcp, uint8_t *buf, size_t bytes,
uint8_t cmd, uint32_t arg) {
uint32_t resp[1];
if (sdc_lld_prepare_read_bytes(sdcp, buf, bytes))
goto error;
if (sdc_lld_send_cmd_short_crc(sdcp, cmd, arg, resp)
|| MMCSD_R1_ERROR(resp[0]))
goto error;
if (sdc_lld_wait_transaction_end(sdcp, 1, resp))
goto error;
return HAL_SUCCESS;
error:
sdc_lld_error_cleanup(sdcp, 1, resp);
return HAL_FAILED;
}
/**
* @brief Reads one or more blocks.
*
* @param[in] sdcp pointer to the @p SDCDriver object
* @param[in] startblk first block to read
* @param[out] buf pointer to the read buffer
* @param[in] blocks number of blocks to read
*
* @return The operation status.
* @retval HAL_SUCCESS operation succeeded.
* @retval HAL_FAILED operation failed.
*
* @notapi
*/
bool sdc_lld_read_aligned(SDCDriver *sdcp, uint32_t startblk,
uint8_t *buf, uint32_t blocks) {
uint32_t resp[1];
osalDbgCheck(blocks < 0x1000000 / MMCSD_BLOCK_SIZE);
sdcp->sdmmc->DTIMER = sdcp->rtmo;
/* Checks for errors and waits for the card to be ready for reading.*/
if (_sdc_wait_for_transfer_state(sdcp))
return HAL_FAILED;
/* Setting up data transfer.*/
sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS;
sdcp->sdmmc->MASK = SDMMC_MASK_DCRCFAILIE |
SDMMC_MASK_DTIMEOUTIE |
SDMMC_MASK_RXOVERRIE |
SDMMC_MASK_DATAENDIE;
sdcp->sdmmc->DLEN = blocks * MMCSD_BLOCK_SIZE;
/* Transfer modes.*/
sdcp->sdmmc->DCTRL = SDMMC_DCTRL_DTDIR |
SDMMC_DCTRL_DBLOCKSIZE_3 |
SDMMC_DCTRL_DBLOCKSIZE_0;
/* Prepares IDMA.*/
sdcp->sdmmc->IDMABASE0 = (uint32_t)buf;
sdcp->sdmmc->IDMACTRL = SDMMC_IDMA_IDMAEN;
/* Transaction starts just after DTEN bit setting.*/
sdcp->sdmmc->DCTRL |= SDMMC_DCTRL_DTEN;
if (sdc_lld_prepare_read(sdcp, startblk, blocks, resp) == true)
goto error;
if (sdc_lld_wait_transaction_end(sdcp, blocks, resp) == true)
goto error;
return HAL_SUCCESS;
error:
sdc_lld_error_cleanup(sdcp, blocks, resp);
return HAL_FAILED;
}
/**
* @brief Writes one or more blocks.
*
* @param[in] sdcp pointer to the @p SDCDriver object
* @param[in] startblk first block to write
* @param[out] buf pointer to the write buffer
* @param[in] n number of blocks to write
*
* @return The operation status.
* @retval HAL_SUCCESS operation succeeded.
* @retval HAL_FAILED operation failed.
*
* @notapi
*/
bool sdc_lld_write_aligned(SDCDriver *sdcp, uint32_t startblk,
const uint8_t *buf, uint32_t blocks) {
uint32_t resp[1];
osalDbgCheck(blocks < 0x1000000 / MMCSD_BLOCK_SIZE);
sdcp->sdmmc->DTIMER = sdcp->wtmo;
/* Checks for errors and waits for the card to be ready for writing.*/
if (_sdc_wait_for_transfer_state(sdcp))
return HAL_FAILED;
/* Setting up data transfer.*/
sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS;
sdcp->sdmmc->MASK = SDMMC_MASK_DCRCFAILIE |
SDMMC_MASK_DTIMEOUTIE |
SDMMC_MASK_TXUNDERRIE |
SDMMC_MASK_DATAENDIE;
sdcp->sdmmc->DLEN = blocks * MMCSD_BLOCK_SIZE;
/* Talk to card what we want from it.*/
if (sdc_lld_prepare_write(sdcp, startblk, blocks, resp) == true)
goto error;
/* Transaction starts just after DTEN bit setting.*/
sdcp->sdmmc->DCTRL = SDMMC_DCTRL_DBLOCKSIZE_3 |
SDMMC_DCTRL_DBLOCKSIZE_0;
/* Prepares IDMA.*/
sdcp->sdmmc->IDMABASE0 = (uint32_t)buf;
sdcp->sdmmc->IDMACTRL = SDMMC_IDMA_IDMAEN;
/* Transaction starts just after DTEN bit setting.*/
sdcp->sdmmc->DCTRL |= SDMMC_DCTRL_DTEN;
if (sdc_lld_wait_transaction_end(sdcp, blocks, resp) == true)
goto error;
return HAL_SUCCESS;
error:
sdc_lld_error_cleanup(sdcp, blocks, resp);
return HAL_FAILED;
}
/**
* @brief Reads one or more blocks.
*
* @param[in] sdcp pointer to the @p SDCDriver object
* @param[in] startblk first block to read
* @param[out] buf pointer to the read buffer
* @param[in] blocks number of blocks to read
*
* @return The operation status.
* @retval HAL_SUCCESS operation succeeded.
* @retval HAL_FAILED operation failed.
*
* @notapi
*/
bool sdc_lld_read(SDCDriver *sdcp, uint32_t startblk,
uint8_t *buf, uint32_t blocks) {
#if STM32_SDC_SDMMC_UNALIGNED_SUPPORT
if (((unsigned)buf & 3) != 0) {
uint32_t i;
for (i = 0; i < blocks; i++) {
if (sdc_lld_read_aligned(sdcp, startblk, u.buf, 1))
return HAL_FAILED;
memcpy(buf, u.buf, MMCSD_BLOCK_SIZE);
buf += MMCSD_BLOCK_SIZE;
startblk++;
}
return HAL_SUCCESS;
}
#else /* !STM32_SDC_SDIO_UNALIGNED_SUPPORT */
osalDbgAssert((((unsigned)buf & 3) == 0), "unaligned buffer");
#endif /* !STM32_SDC_SDIO_UNALIGNED_SUPPORT */
return sdc_lld_read_aligned(sdcp, startblk, buf, blocks);
}
/**
* @brief Writes one or more blocks.
*
* @param[in] sdcp pointer to the @p SDCDriver object
* @param[in] startblk first block to write
* @param[out] buf pointer to the write buffer
* @param[in] blocks number of blocks to write
*
* @return The operation status.
* @retval HAL_SUCCESS operation succeeded.
* @retval HAL_FAILED operation failed.
*
* @notapi
*/
bool sdc_lld_write(SDCDriver *sdcp, uint32_t startblk,
const uint8_t *buf, uint32_t blocks) {
#if STM32_SDC_SDMMC_UNALIGNED_SUPPORT
if (((unsigned)buf & 3) != 0) {
uint32_t i;
for (i = 0; i < blocks; i++) {
memcpy(u.buf, buf, MMCSD_BLOCK_SIZE);
buf += MMCSD_BLOCK_SIZE;
if (sdc_lld_write_aligned(sdcp, startblk, u.buf, 1))
return HAL_FAILED;
startblk++;
}
return HAL_SUCCESS;
}
#else /* !STM32_SDC_SDIO_UNALIGNED_SUPPORT */
osalDbgAssert((((unsigned)buf & 3) == 0), "unaligned buffer");
#endif /* !STM32_SDC_SDIO_UNALIGNED_SUPPORT */
return sdc_lld_write_aligned(sdcp, startblk, buf, blocks);
}
/**
* @brief Waits for card idle condition.
*
* @param[in] sdcp pointer to the @p SDCDriver object
*
* @return The operation status.
* @retval HAL_SUCCESS the operation succeeded.
* @retval HAL_FAILED the operation failed.
*
* @api
*/
bool sdc_lld_sync(SDCDriver *sdcp) {
/* CHTODO: Implement.*/
(void)sdcp;
return HAL_SUCCESS;
}
#endif /* HAL_USE_SDC */
/** @} */

View File

@ -0,0 +1,340 @@
/*
ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file SDMMCv2/hal_sdc_lld.h
* @brief STM32 SDC subsystem low level driver header.
*
* @addtogroup SDC
* @{
*/
#ifndef HAL_SDC_LLD_H
#define HAL_SDC_LLD_H
#if HAL_USE_SDC || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/**
* @name Configuration options
* @{
*/
/**
* @brief SDMMC1 driver enable switch.
* @details If set to @p TRUE the support for SDMMC1 is included.
* @note The default is @p FALSE.
*/
#if !defined(STM32_SDC_USE_SDMMC1) || defined(__DOXYGEN__)
#define STM32_SDC_USE_SDMMC1 FALSE
#endif
/**
* @brief SDMMC2 driver enable switch.
* @details If set to @p TRUE the support for SDMMC2 is included.
* @note The default is @p FALSE.
*/
#if !defined(STM32_SDC_USE_SDMMC2) || defined(__DOXYGEN__)
#define STM32_SDC_USE_SDMMC2 FALSE
#endif
/**
* @brief Support for unaligned transfers.
* @note Unaligned transfers are much slower.
*/
#if !defined(STM32_SDC_SDMMC_UNALIGNED_SUPPORT) || defined(__DOXYGEN__)
#define STM32_SDC_SDMMC_UNALIGNED_SUPPORT TRUE
#endif
/**
* @brief Enable clock bypass.
* @note Allow clock speed up to 50 Mhz.
*/
#if !defined(STM32_SDC_SDMMC_50MHZ) || defined(__DOXYGEN__)
#define STM32_SDC_SDMMC_50MHZ FALSE
#endif
/**
* @brief Write timeout in milliseconds.
*/
#if !defined(STM32_SDC_SDMMC_WRITE_TIMEOUT) || defined(__DOXYGEN__)
#define STM32_SDC_SDMMC_WRITE_TIMEOUT 1000
#endif
/**
* @brief Read timeout in milliseconds.
*/
#if !defined(STM32_SDC_SDMMC_READ_TIMEOUT) || defined(__DOXYGEN__)
#define STM32_SDC_SDMMC_READ_TIMEOUT 1000
#endif
/**
* @brief Card clock activation delay in milliseconds.
*/
#if !defined(STM32_SDC_SDMMC_CLOCK_DELAY) || defined(__DOXYGEN__)
#define STM32_SDC_SDMMC_CLOCK_DELAY 10
#endif
/**
* @brief Card clock power saving enable.
*/
#if !defined(STM32_SDC_SDMMC_PWRSAV) || defined(__DOXYGEN__)
#define STM32_SDC_SDMMC_PWRSAV TRUE
#endif
/**
* @brief SDMMC1 interrupt priority level setting.
*/
#if !defined(STM32_SDC_SDMMC1_IRQ_PRIORITY) || defined(__DOXYGEN__)
#define STM32_SDC_SDMMC1_IRQ_PRIORITY 9
#endif
/**
* @brief SDMMC2 interrupt priority level setting.
*/
#if !defined(STM32_SDC_SDMMC2_IRQ_PRIORITY) || defined(__DOXYGEN__)
#define STM32_SDC_SDMMC2_IRQ_PRIORITY 9
#endif
/** @} */
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
/* Registry checks.*/
#if (STM32_SDC_USE_SDMMC1 && !defined(STM32_SDMMC1_HANDLER)) || \
(STM32_SDC_USE_SDMMC2 && !defined(STM32_SDMMC2_HANDLER))
#error "STM32_SDMMCx_HANDLER not defined in registry"
#endif
#if (STM32_SDC_USE_SDMMC1 && !defined(STM32_SDMMC1_NUMBER)) || \
(STM32_SDC_USE_SDMMC2 && !defined(STM32_SDMMC2_NUMBER))
#error "STM32_SDMMCx_NUMBER not defined in registry"
#endif
/* Units checks.*/
#if STM32_SDC_USE_SDMMC1 && !STM32_HAS_SDMMC1
#error "SDMMC1 not present in the selected device"
#endif
#if STM32_SDC_USE_SDMMC2 && !STM32_HAS_SDMMC2
#error "SDMMC2 not present in the selected device"
#endif
#if !STM32_SDC_USE_SDMMC1 && !STM32_SDC_USE_SDMMC2
#error "SDC driver activated but no SDMMC peripheral assigned"
#endif
/* Clock related tests.*/
#if STM32_HAS_SDMMC1 && !defined(STM32_SDMMC1CLK)
#error "STM32_SDMMC1CLK not defined"
#endif
/* Clock related tests.*/
#if STM32_HAS_SDMMC2 && !defined(STM32_SDMMC2CLK)
#error "STM32_SDMMC2CLK not defined"
#endif
#if !defined(STM32_HCLK)
#error "STM32_HCLK not defined"
#endif
#if STM32_HAS_SDMMC1 && (STM32_SDMMC1CLK * 10 > STM32_HCLK * 7)
#error "STM32_SDMMC1CLK must not exceed STM32_HCLK * 0.7"
#endif
#if STM32_HAS_SDMMC2 && (STM32_SDMMC2CLK * 10 > STM32_HCLK * 7)
#error "STM32_SDMMC2CLK must not exceed STM32_HCLK * 0.7"
#endif
#if !defined(STM32_SDMMC_MAXCLK)
#define STM32_SDMMC_MAXCLK 48000000
#endif
#if STM32_HAS_SDMMC1 && (STM32_SDMMC1CLK > STM32_SDMMC_MAXCLK)
#error "STM32_SDMMC1CLK must not exceed STM32_SDMMC_MAXCLK"
#endif
#if STM32_HAS_SDMMC2 && (STM32_SDMMC2CLK > STM32_SDMMC_MAXCLK)
#error "STM32_SDMMC2CLK must not exceed STM32_SDMMC_MAXCLK"
#endif
/* SDMMC IRQ priority tests.*/
#if !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SDC_SDMMC1_IRQ_PRIORITY)
#error "Invalid IRQ priority assigned to SDMMC1"
#endif
#if !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SDC_SDMMC2_IRQ_PRIORITY)
#error "Invalid IRQ priority assigned to SDMMC2"
#endif
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief Type of card flags.
*/
typedef uint32_t sdcmode_t;
/**
* @brief SDC Driver condition flags type.
*/
typedef uint32_t sdcflags_t;
/**
* @brief Type of a structure representing an SDC driver.
*/
typedef struct SDCDriver SDCDriver;
/**
* @brief Driver configuration structure.
* @note It could be empty on some architectures.
*/
typedef struct {
/**
* @brief Working area for memory consuming operations.
* @note Buffer must be word aligned and big enough to store 512 bytes.
* @note It is mandatory for detecting MMC cards bigger than 2GB else it
* can be @p NULL. SD cards do NOT need it.
* @note Memory pointed by this buffer is only used by @p sdcConnect(),
* afterward it can be reused for other purposes.
*/
uint8_t *scratchpad;
/**
* @brief Bus width.
*/
sdcbusmode_t bus_width;
/* End of the mandatory fields.*/
} SDCConfig;
/**
* @brief @p SDCDriver specific methods.
*/
#define _sdc_driver_methods \
_mmcsd_block_device_methods
/**
* @extends MMCSDBlockDeviceVMT
*
* @brief @p SDCDriver virtual methods table.
*/
struct SDCDriverVMT {
_sdc_driver_methods
};
/**
* @brief Structure representing an SDC driver.
*/
struct SDCDriver {
/**
* @brief Virtual Methods Table.
*/
const struct SDCDriverVMT *vmt;
_mmcsd_block_device_data
/**
* @brief Current configuration data.
*/
const SDCConfig *config;
/**
* @brief Various flags regarding the mounted card.
*/
sdcmode_t cardmode;
/**
* @brief Errors flags.
*/
sdcflags_t errors;
/**
* @brief Card RCA.
*/
uint32_t rca;
/* End of the mandatory fields.*/
/**
* @brief Thread waiting for I/O completion IRQ.
*/
thread_reference_t thread;
/**
* @brief DTIMER register value for read operations.
*/
uint32_t rtmo;
/**
* @brief DTIMER register value for write operations.
*/
uint32_t wtmo;
/**
* @brief Pointer to the SDMMC registers block.
* @note Needed for debugging aid.
*/
SDMMC_TypeDef *sdmmc;
};
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#if STM32_SDC_USE_SDMMC1 && !defined(__DOXYGEN__)
extern SDCDriver SDCD1;
#endif
#if STM32_SDC_USE_SDMMC2 && !defined(__DOXYGEN__)
extern SDCDriver SDCD2;
#endif
#ifdef __cplusplus
extern "C" {
#endif
void sdc_lld_init(void);
void sdc_lld_start(SDCDriver *sdcp);
void sdc_lld_stop(SDCDriver *sdcp);
void sdc_lld_start_clk(SDCDriver *sdcp);
void sdc_lld_set_data_clk(SDCDriver *sdcp, sdcbusclk_t clk);
void sdc_lld_stop_clk(SDCDriver *sdcp);
void sdc_lld_set_bus_mode(SDCDriver *sdcp, sdcbusmode_t mode);
void sdc_lld_send_cmd_none(SDCDriver *sdcp, uint8_t cmd, uint32_t arg);
bool sdc_lld_send_cmd_short(SDCDriver *sdcp, uint8_t cmd, uint32_t arg,
uint32_t *resp);
bool sdc_lld_send_cmd_short_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg,
uint32_t *resp);
bool sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg,
uint32_t *resp);
bool sdc_lld_read_special(SDCDriver *sdcp, uint8_t *buf, size_t bytes,
uint8_t cmd, uint32_t argument);
bool sdc_lld_read(SDCDriver *sdcp, uint32_t startblk,
uint8_t *buf, uint32_t blocks);
bool sdc_lld_write(SDCDriver *sdcp, uint32_t startblk,
const uint8_t *buf, uint32_t blocks);
bool sdc_lld_sync(SDCDriver *sdcp);
bool sdc_lld_is_card_inserted(SDCDriver *sdcp);
bool sdc_lld_is_write_protected(SDCDriver *sdcp);
#ifdef __cplusplus
}
#endif
#endif /* HAL_USE_SDC */
#endif /* HAL_SDC_LLD_H */
/** @} */

View File

@ -2742,14 +2742,20 @@
#if (STM32_SDMMCSEL == STM32_SDMMCSEL_PLL1_Q_CK) || defined(__DOXYGEN__)
/**
* @brief SDMMC frequency.
* @brief SDMMC1 frequency.
*/
#define STM32_SDMMCCLK STM32_PLL1_Q_CK
#define STM32_SDMMC1CLK STM32_PLL1_Q_CK
/**
* @brief SDMMC2 frequency.
*/
#define STM32_SDMMC2CLK STM32_PLL1_Q_CK
#elif STM32_SDMMCSEL == STM32_SDMMCSEL_PLL2_R_CK
#define STM32_SDMMCCLK STM32_PLL2_R_CK
#define STM32_SDMMC1CLK STM32_PLL2_R_CK
#define STM32_SDMMC2CLK STM32_PLL2_R_CK
#else
#error "invalid source selected for STM32_SDMMCSEL clock"
#error "invalid source selected for STM32_SDMMCxSEL clock"
#endif
#if (STM32_QSPISEL == STM32_QSPISEL_HCLK) || defined(__DOXYGEN__)

View File

@ -34,6 +34,7 @@ include $(CHIBIOS)/os/hal/ports/STM32/LLD/EXTIv1/driver.mk
include $(CHIBIOS)/os/hal/ports/STM32/LLD/GPIOv2/driver.mk
include $(CHIBIOS)/os/hal/ports/STM32/LLD/I2Cv3/driver.mk
include $(CHIBIOS)/os/hal/ports/STM32/LLD/OTGv1/driver.mk
include $(CHIBIOS)/os/hal/ports/STM32/LLD/SDMMCv2/driver.mk
include $(CHIBIOS)/os/hal/ports/STM32/LLD/SPIv3/driver.mk
include $(CHIBIOS)/os/hal/ports/STM32/LLD/RNGv1/driver.mk
include $(CHIBIOS)/os/hal/ports/STM32/LLD/RTCv2/driver.mk

View File

@ -201,8 +201,13 @@
#define STM32_RTC_HAS_INTERRUPTS FALSE
/* SDMMC attributes.*/
#define STM32_HAS_SDMMC1 FALSE
#define STM32_HAS_SDMMC2 FALSE
#define STM32_HAS_SDMMC1 TRUE
#define STM32_SDMMC1_HANDLER Vector104
#define STM32_SDMMC1_NUMBER 49
#define STM32_HAS_SDMMC2 TRUE
#define STM32_SDMMC2_HANDLER Vector230
#define STM32_SDMMC2_NUMBER 131
/* SPI attributes.*/
#define STM32_HAS_SPI1 TRUE