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:
parent
9bb67b290f
commit
d6639df4af
|
@ -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
|
|
@ -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 */
|
||||
|
||||
/** @} */
|
|
@ -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 */
|
||||
|
||||
/** @} */
|
|
@ -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__)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue