From 8ca210a4af9fd039e290cfcc309adde543999c1f Mon Sep 17 00:00:00 2001 From: gdisirio Date: Fri, 9 Aug 2013 08:24:22 +0000 Subject: git-svn-id: svn://svn.code.sf.net/p/chibios/svn/branches/kernel_3_dev@6108 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/platforms/STM32/sdc_lld.c | 788 --------------------------------------- 1 file changed, 788 deletions(-) delete mode 100644 os/hal/platforms/STM32/sdc_lld.c (limited to 'os/hal/platforms/STM32/sdc_lld.c') diff --git a/os/hal/platforms/STM32/sdc_lld.c b/os/hal/platforms/STM32/sdc_lld.c deleted file mode 100644 index 69873d5f0..000000000 --- a/os/hal/platforms/STM32/sdc_lld.c +++ /dev/null @@ -1,788 +0,0 @@ -/* - ChibiOS/RT - Copyright (C) 2006-2013 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 STM32/sdc_lld.c - * @brief STM32 SDC subsystem low level driver source. - * - * @addtogroup SDC - * @{ - */ - -/* - TODO: Try preerase blocks before writing (ACMD23). - */ - -#include - -#include "ch.h" -#include "hal.h" - -#if HAL_USE_SDC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SDC_SDIO_DMA_STREAM, \ - STM32_SDC_SDIO_DMA_CHN) - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief SDCD1 driver identifier.*/ -SDCDriver SDCD1; - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -#if STM32_SDC_SDIO_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_SDIO_UNALIGNED_SUPPORT */ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @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 CH_SUCCESS operation succeeded. - * @retval CH_FAILED operation failed. - * - * @notapi - */ -static bool_t 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 CH_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 CH_FAILED; - } - - return CH_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 CH_SUCCESS operation succeeded. - * @retval CH_FAILED operation failed. - * - * @notapi - */ -static bool_t 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 CH_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 CH_FAILED; - } - - return CH_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 CH_SUCCESS operation succeeded. - * @retval CH_FAILED operation failed. - */ -static bool_t 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.*/ - chSysLock(); - if (SDIO->MASK != 0) { - chDbgAssert(sdcp->thread == NULL, - "sdc_lld_start_data_transaction(), #1", "not NULL"); - sdcp->thread = chThdSelf(); - chSchGoSleepS(THD_STATE_SUSPENDED); - chDbgAssert(sdcp->thread == NULL, - "sdc_lld_start_data_transaction(), #2", "not NULL"); - } - if ((SDIO->STA & SDIO_STA_DATAEND) == 0) { - chSysUnlock(); - return CH_FAILED; - } - -#if (defined(STM32F4XX) || defined(STM32F2XX)) - /* Wait until DMA channel enabled to be sure that all data transferred.*/ - while (sdcp->dma->stream->CR & STM32_DMA_CR_EN) - ; - - /* DMA event flags must be manually cleared.*/ - dmaStreamClearInterrupt(sdcp->dma); - - SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS; - SDIO->DCTRL = 0; - chSysUnlock(); - - /* Wait until interrupt flags to be cleared.*/ - /*while (((DMA2->LISR) >> (sdcp->dma->ishift)) & STM32_DMA_ISR_TCIF) - dmaStreamClearInterrupt(sdcp->dma);*/ -#else - /* Waits for transfer completion at DMA level, the the stream is - disabled and cleared.*/ - dmaWaitCompletion(sdcp->dma); - - SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS; - SDIO->DCTRL = 0; - chSysUnlock(); -#endif - - /* Finalize transaction.*/ - if (n > 1) - return sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp); - - return CH_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 & SDIO_STA_CCRCFAIL) - errors |= SDC_CMD_CRC_ERROR; - if (sta & SDIO_STA_DCRCFAIL) - errors |= SDC_DATA_CRC_ERROR; - if (sta & SDIO_STA_CTIMEOUT) - errors |= SDC_COMMAND_TIMEOUT; - if (sta & SDIO_STA_DTIMEOUT) - errors |= SDC_DATA_TIMEOUT; - if (sta & SDIO_STA_TXUNDERR) - errors |= SDC_TX_UNDERRUN; - if (sta & SDIO_STA_RXOVERR) - errors |= SDC_RX_OVERRUN; - if (sta & SDIO_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 = SDIO->STA; - - dmaStreamClearInterrupt(sdcp->dma); - dmaStreamDisable(sdcp->dma); - SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS; - SDIO->MASK = 0; - SDIO->DCTRL = 0; - 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. */ -/*===========================================================================*/ - -#if !defined(STM32_SDIO_HANDLER) -#error "STM32_SDIO_HANDLER not defined" -#endif -/** - * @brief SDIO IRQ handler. - * @details It just wakes transaction thread. All error handling performs in - * that thread. - * - * @isr - */ -CH_IRQ_HANDLER(STM32_SDIO_HANDLER) { - - CH_IRQ_PROLOGUE(); - - chSysLockFromIsr() - - /* Disables the source but the status flags are not reset because the - read/write functions needs to check them.*/ - SDIO->MASK = 0; - - if (SDCD1.thread != NULL) { - chSchReadyI(SDCD1.thread); - SDCD1.thread = NULL; - } - - chSysUnlockFromIsr(); - - CH_IRQ_EPILOGUE(); -} - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level SDC driver initialization. - * - * @notapi - */ -void sdc_lld_init(void) { - - sdcObjectInit(&SDCD1); - SDCD1.thread = NULL; - SDCD1.dma = STM32_DMA_STREAM(STM32_SDC_SDIO_DMA_STREAM); -#if CH_DBG_ENABLE_ASSERTS - SDCD1.sdio = SDIO; -#endif -} - -/** - * @brief Configures and activates the SDC peripheral. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @notapi - */ -void sdc_lld_start(SDCDriver *sdcp) { - - sdcp->dmamode = STM32_DMA_CR_CHSEL(DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SDC_SDIO_DMA_PRIORITY) | - STM32_DMA_CR_PSIZE_WORD | - STM32_DMA_CR_MSIZE_WORD | - STM32_DMA_CR_MINC; - -#if (defined(STM32F4XX) || defined(STM32F2XX)) - sdcp->dmamode |= STM32_DMA_CR_PFCTRL | - STM32_DMA_CR_PBURST_INCR4 | - STM32_DMA_CR_MBURST_INCR4; -#endif - - if (sdcp->state == BLK_STOP) { - /* Note, the DMA must be enabled before the IRQs.*/ - bool_t b; - b = dmaStreamAllocate(sdcp->dma, STM32_SDC_SDIO_IRQ_PRIORITY, NULL, NULL); - chDbgAssert(!b, "sdc_lld_start(), #1", "stream already allocated"); - dmaStreamSetPeripheral(sdcp->dma, &SDIO->FIFO); -#if (defined(STM32F4XX) || defined(STM32F2XX)) - dmaStreamSetFIFO(sdcp->dma, STM32_DMA_FCR_DMDIS | STM32_DMA_FCR_FTH_FULL); -#endif - nvicEnableVector(STM32_SDIO_NUMBER, - CORTEX_PRIORITY_MASK(STM32_SDC_SDIO_IRQ_PRIORITY)); - rccEnableSDIO(FALSE); - } - - /* Configuration, card clock is initially stopped.*/ - SDIO->POWER = 0; - SDIO->CLKCR = 0; - SDIO->DCTRL = 0; - SDIO->DTIMER = 0; -} - -/** - * @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.*/ - SDIO->POWER = 0; - SDIO->CLKCR = 0; - SDIO->DCTRL = 0; - SDIO->DTIMER = 0; - - /* Clock deactivation.*/ - nvicDisableVector(STM32_SDIO_NUMBER); - dmaStreamRelease(sdcp->dma); - rccDisableSDIO(FALSE); - } -} - -/** - * @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) { - - (void)sdcp; - - /* Initial clock setting: 400kHz, 1bit mode.*/ - SDIO->CLKCR = STM32_SDIO_DIV_LS; - SDIO->POWER |= SDIO_POWER_PWRCTRL_0 | SDIO_POWER_PWRCTRL_1; - SDIO->CLKCR |= SDIO_CLKCR_CLKEN; -} - -/** - * @brief Sets the SDIO clock to data mode (25MHz or less). - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @notapi - */ -void sdc_lld_set_data_clk(SDCDriver *sdcp) { - - (void)sdcp; - - SDIO->CLKCR = (SDIO->CLKCR & 0xFFFFFF00) | STM32_SDIO_DIV_HS; -} - -/** - * @brief Stops the SDIO clock. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @notapi - */ -void sdc_lld_stop_clk(SDCDriver *sdcp) { - - (void)sdcp; - - SDIO->CLKCR = 0; - SDIO->POWER = 0; -} - -/** - * @brief Switches the bus to 4 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 = SDIO->CLKCR & ~SDIO_CLKCR_WIDBUS; - - (void)sdcp; - - switch (mode) { - case SDC_MODE_1BIT: - SDIO->CLKCR = clk; - break; - case SDC_MODE_4BIT: - SDIO->CLKCR = clk | SDIO_CLKCR_WIDBUS_0; - break; - case SDC_MODE_8BIT: - SDIO->CLKCR = clk | SDIO_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) { - - (void)sdcp; - - SDIO->ARG = arg; - SDIO->CMD = (uint32_t)cmd | SDIO_CMD_CPSMEN; - while ((SDIO->STA & SDIO_STA_CMDSENT) == 0) - ; - SDIO->ICR = SDIO_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 CH_SUCCESS operation succeeded. - * @retval CH_FAILED operation failed. - * - * @notapi - */ -bool_t sdc_lld_send_cmd_short(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp) { - uint32_t sta; - - (void)sdcp; - - SDIO->ARG = arg; - SDIO->CMD = (uint32_t)cmd | SDIO_CMD_WAITRESP_0 | SDIO_CMD_CPSMEN; - while (((sta = SDIO->STA) & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | - SDIO_STA_CCRCFAIL)) == 0) - ; - SDIO->ICR = sta; - if ((sta & (SDIO_STA_CTIMEOUT)) != 0) { - sdc_lld_collect_errors(sdcp, sta); - return CH_FAILED; - } - *resp = SDIO->RESP1; - return CH_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 CH_SUCCESS operation succeeded. - * @retval CH_FAILED operation failed. - * - * @notapi - */ -bool_t sdc_lld_send_cmd_short_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp) { - uint32_t sta; - - (void)sdcp; - - SDIO->ARG = arg; - SDIO->CMD = (uint32_t)cmd | SDIO_CMD_WAITRESP_0 | SDIO_CMD_CPSMEN; - while (((sta = SDIO->STA) & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | - SDIO_STA_CCRCFAIL)) == 0) - ; - SDIO->ICR = sta; - if ((sta & (SDIO_STA_CTIMEOUT | SDIO_STA_CCRCFAIL)) != 0) { - sdc_lld_collect_errors(sdcp, sta); - return CH_FAILED; - } - *resp = SDIO->RESP1; - return CH_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 CH_SUCCESS operation succeeded. - * @retval CH_FAILED operation failed. - * - * @notapi - */ -bool_t sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp) { - uint32_t sta; - - (void)sdcp; - - SDIO->ARG = arg; - SDIO->CMD = (uint32_t)cmd | SDIO_CMD_WAITRESP_0 | SDIO_CMD_WAITRESP_1 | - SDIO_CMD_CPSMEN; - while (((sta = SDIO->STA) & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | - SDIO_STA_CCRCFAIL)) == 0) - ; - SDIO->ICR = sta; - if ((sta & (STM32_SDIO_STA_ERROR_MASK)) != 0) { - sdc_lld_collect_errors(sdcp, sta); - return CH_FAILED; - } - /* Save bytes in reverse order because MSB in response comes first.*/ - *resp++ = SDIO->RESP4; - *resp++ = SDIO->RESP3; - *resp++ = SDIO->RESP2; - *resp = SDIO->RESP1; - return CH_SUCCESS; -} - -/** - * @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] n number of blocks to read - * - * @return The operation status. - * @retval CH_SUCCESS operation succeeded. - * @retval CH_FAILED operation failed. - * - * @notapi - */ -bool_t sdc_lld_read_aligned(SDCDriver *sdcp, uint32_t startblk, - uint8_t *buf, uint32_t n) { - uint32_t resp[1]; - - chDbgCheck((n < (0x1000000 / MMCSD_BLOCK_SIZE)), "max transaction size"); - - SDIO->DTIMER = STM32_SDC_READ_TIMEOUT; - - /* Checks for errors and waits for the card to be ready for reading.*/ - if (_sdc_wait_for_transfer_state(sdcp)) - return CH_FAILED; - - /* Prepares the DMA channel for writing.*/ - dmaStreamSetMemory0(sdcp->dma, buf); - dmaStreamSetTransactionSize(sdcp->dma, - (n * MMCSD_BLOCK_SIZE) / sizeof (uint32_t)); - dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_P2M); - dmaStreamEnable(sdcp->dma); - - /* Setting up data transfer.*/ - SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS; - SDIO->MASK = SDIO_MASK_DCRCFAILIE | - SDIO_MASK_DTIMEOUTIE | - SDIO_MASK_STBITERRIE | - SDIO_MASK_RXOVERRIE | - SDIO_MASK_DATAENDIE; - SDIO->DLEN = n * MMCSD_BLOCK_SIZE; - - /* Talk to card what we want from it.*/ - if (sdc_lld_prepare_read(sdcp, startblk, n, resp) == TRUE) - goto error; - - /* Transaction starts just after DTEN bit setting.*/ - SDIO->DCTRL = SDIO_DCTRL_DTDIR | - SDIO_DCTRL_DBLOCKSIZE_3 | - SDIO_DCTRL_DBLOCKSIZE_0 | - SDIO_DCTRL_DMAEN | - SDIO_DCTRL_DTEN; - if (sdc_lld_wait_transaction_end(sdcp, n, resp) == TRUE) - goto error; - - return CH_SUCCESS; - -error: - sdc_lld_error_cleanup(sdcp, n, resp); - return CH_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 CH_SUCCESS operation succeeded. - * @retval CH_FAILED operation failed. - * - * @notapi - */ -bool_t sdc_lld_write_aligned(SDCDriver *sdcp, uint32_t startblk, - const uint8_t *buf, uint32_t n) { - uint32_t resp[1]; - - chDbgCheck((n < (0x1000000 / MMCSD_BLOCK_SIZE)), "max transaction size"); - - SDIO->DTIMER = STM32_SDC_WRITE_TIMEOUT; - - /* Checks for errors and waits for the card to be ready for writing.*/ - if (_sdc_wait_for_transfer_state(sdcp)) - return CH_FAILED; - - /* Prepares the DMA channel for writing.*/ - dmaStreamSetMemory0(sdcp->dma, buf); - dmaStreamSetTransactionSize(sdcp->dma, - (n * MMCSD_BLOCK_SIZE) / sizeof (uint32_t)); - dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_M2P); - dmaStreamEnable(sdcp->dma); - - /* Setting up data transfer.*/ - SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS; - SDIO->MASK = SDIO_MASK_DCRCFAILIE | - SDIO_MASK_DTIMEOUTIE | - SDIO_MASK_STBITERRIE | - SDIO_MASK_TXUNDERRIE | - SDIO_MASK_DATAENDIE; - SDIO->DLEN = n * MMCSD_BLOCK_SIZE; - - /* Talk to card what we want from it.*/ - if (sdc_lld_prepare_write(sdcp, startblk, n, resp) == TRUE) - goto error; - - /* Transaction starts just after DTEN bit setting.*/ - SDIO->DCTRL = SDIO_DCTRL_DBLOCKSIZE_3 | - SDIO_DCTRL_DBLOCKSIZE_0 | - SDIO_DCTRL_DMAEN | - SDIO_DCTRL_DTEN; - if (sdc_lld_wait_transaction_end(sdcp, n, resp) == TRUE) - goto error; - - return CH_SUCCESS; - -error: - sdc_lld_error_cleanup(sdcp, n, resp); - return CH_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] n number of blocks to read - * - * @return The operation status. - * @retval CH_SUCCESS operation succeeded. - * @retval CH_FAILED operation failed. - * - * @notapi - */ -bool_t sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, - uint8_t *buf, uint32_t n) { - -#if STM32_SDC_SDIO_UNALIGNED_SUPPORT - if (((unsigned)buf & 3) != 0) { - uint32_t i; - for (i = 0; i < n; i++) { - if (sdc_lld_read_aligned(sdcp, startblk, u.buf, 1)) - return CH_FAILED; - memcpy(buf, u.buf, MMCSD_BLOCK_SIZE); - buf += MMCSD_BLOCK_SIZE; - startblk++; - } - return CH_SUCCESS; - } -#endif /* STM32_SDC_SDIO_UNALIGNED_SUPPORT */ - return sdc_lld_read_aligned(sdcp, startblk, buf, n); -} - -/** - * @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 CH_SUCCESS operation succeeded. - * @retval CH_FAILED operation failed. - * - * @notapi - */ -bool_t sdc_lld_write(SDCDriver *sdcp, uint32_t startblk, - const uint8_t *buf, uint32_t n) { - -#if STM32_SDC_SDIO_UNALIGNED_SUPPORT - if (((unsigned)buf & 3) != 0) { - uint32_t i; - for (i = 0; i < n; i++) { - memcpy(u.buf, buf, MMCSD_BLOCK_SIZE); - buf += MMCSD_BLOCK_SIZE; - if (sdc_lld_write_aligned(sdcp, startblk, u.buf, 1)) - return CH_FAILED; - startblk++; - } - return CH_SUCCESS; - } -#endif /* STM32_SDC_SDIO_UNALIGNED_SUPPORT */ - return sdc_lld_write_aligned(sdcp, startblk, buf, n); -} - -/** - * @brief Waits for card idle condition. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @return The operation status. - * @retval CH_SUCCESS the operation succeeded. - * @retval CH_FAILED the operation failed. - * - * @api - */ -bool_t sdc_lld_sync(SDCDriver *sdcp) { - - /* TODO: Implement.*/ - (void)sdcp; - return CH_SUCCESS; -} - -#endif /* HAL_USE_SDC */ - -/** @} */ -- cgit v1.2.3 From 0e34cd051ed686263001af3a721f6858db94d072 Mon Sep 17 00:00:00 2001 From: gdisirio Date: Fri, 9 Aug 2013 08:25:09 +0000 Subject: git-svn-id: svn://svn.code.sf.net/p/chibios/svn/branches/kernel_3_dev@6109 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/platforms/STM32/sdc_lld.c | 788 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 788 insertions(+) create mode 100644 os/hal/platforms/STM32/sdc_lld.c (limited to 'os/hal/platforms/STM32/sdc_lld.c') diff --git a/os/hal/platforms/STM32/sdc_lld.c b/os/hal/platforms/STM32/sdc_lld.c new file mode 100644 index 000000000..69873d5f0 --- /dev/null +++ b/os/hal/platforms/STM32/sdc_lld.c @@ -0,0 +1,788 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2013 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 STM32/sdc_lld.c + * @brief STM32 SDC subsystem low level driver source. + * + * @addtogroup SDC + * @{ + */ + +/* + TODO: Try preerase blocks before writing (ACMD23). + */ + +#include + +#include "ch.h" +#include "hal.h" + +#if HAL_USE_SDC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SDC_SDIO_DMA_STREAM, \ + STM32_SDC_SDIO_DMA_CHN) + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief SDCD1 driver identifier.*/ +SDCDriver SDCD1; + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +#if STM32_SDC_SDIO_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_SDIO_UNALIGNED_SUPPORT */ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @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 CH_SUCCESS operation succeeded. + * @retval CH_FAILED operation failed. + * + * @notapi + */ +static bool_t 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 CH_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 CH_FAILED; + } + + return CH_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 CH_SUCCESS operation succeeded. + * @retval CH_FAILED operation failed. + * + * @notapi + */ +static bool_t 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 CH_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 CH_FAILED; + } + + return CH_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 CH_SUCCESS operation succeeded. + * @retval CH_FAILED operation failed. + */ +static bool_t 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.*/ + chSysLock(); + if (SDIO->MASK != 0) { + chDbgAssert(sdcp->thread == NULL, + "sdc_lld_start_data_transaction(), #1", "not NULL"); + sdcp->thread = chThdSelf(); + chSchGoSleepS(THD_STATE_SUSPENDED); + chDbgAssert(sdcp->thread == NULL, + "sdc_lld_start_data_transaction(), #2", "not NULL"); + } + if ((SDIO->STA & SDIO_STA_DATAEND) == 0) { + chSysUnlock(); + return CH_FAILED; + } + +#if (defined(STM32F4XX) || defined(STM32F2XX)) + /* Wait until DMA channel enabled to be sure that all data transferred.*/ + while (sdcp->dma->stream->CR & STM32_DMA_CR_EN) + ; + + /* DMA event flags must be manually cleared.*/ + dmaStreamClearInterrupt(sdcp->dma); + + SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS; + SDIO->DCTRL = 0; + chSysUnlock(); + + /* Wait until interrupt flags to be cleared.*/ + /*while (((DMA2->LISR) >> (sdcp->dma->ishift)) & STM32_DMA_ISR_TCIF) + dmaStreamClearInterrupt(sdcp->dma);*/ +#else + /* Waits for transfer completion at DMA level, the the stream is + disabled and cleared.*/ + dmaWaitCompletion(sdcp->dma); + + SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS; + SDIO->DCTRL = 0; + chSysUnlock(); +#endif + + /* Finalize transaction.*/ + if (n > 1) + return sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp); + + return CH_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 & SDIO_STA_CCRCFAIL) + errors |= SDC_CMD_CRC_ERROR; + if (sta & SDIO_STA_DCRCFAIL) + errors |= SDC_DATA_CRC_ERROR; + if (sta & SDIO_STA_CTIMEOUT) + errors |= SDC_COMMAND_TIMEOUT; + if (sta & SDIO_STA_DTIMEOUT) + errors |= SDC_DATA_TIMEOUT; + if (sta & SDIO_STA_TXUNDERR) + errors |= SDC_TX_UNDERRUN; + if (sta & SDIO_STA_RXOVERR) + errors |= SDC_RX_OVERRUN; + if (sta & SDIO_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 = SDIO->STA; + + dmaStreamClearInterrupt(sdcp->dma); + dmaStreamDisable(sdcp->dma); + SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS; + SDIO->MASK = 0; + SDIO->DCTRL = 0; + 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. */ +/*===========================================================================*/ + +#if !defined(STM32_SDIO_HANDLER) +#error "STM32_SDIO_HANDLER not defined" +#endif +/** + * @brief SDIO IRQ handler. + * @details It just wakes transaction thread. All error handling performs in + * that thread. + * + * @isr + */ +CH_IRQ_HANDLER(STM32_SDIO_HANDLER) { + + CH_IRQ_PROLOGUE(); + + chSysLockFromIsr() + + /* Disables the source but the status flags are not reset because the + read/write functions needs to check them.*/ + SDIO->MASK = 0; + + if (SDCD1.thread != NULL) { + chSchReadyI(SDCD1.thread); + SDCD1.thread = NULL; + } + + chSysUnlockFromIsr(); + + CH_IRQ_EPILOGUE(); +} + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level SDC driver initialization. + * + * @notapi + */ +void sdc_lld_init(void) { + + sdcObjectInit(&SDCD1); + SDCD1.thread = NULL; + SDCD1.dma = STM32_DMA_STREAM(STM32_SDC_SDIO_DMA_STREAM); +#if CH_DBG_ENABLE_ASSERTS + SDCD1.sdio = SDIO; +#endif +} + +/** + * @brief Configures and activates the SDC peripheral. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @notapi + */ +void sdc_lld_start(SDCDriver *sdcp) { + + sdcp->dmamode = STM32_DMA_CR_CHSEL(DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SDC_SDIO_DMA_PRIORITY) | + STM32_DMA_CR_PSIZE_WORD | + STM32_DMA_CR_MSIZE_WORD | + STM32_DMA_CR_MINC; + +#if (defined(STM32F4XX) || defined(STM32F2XX)) + sdcp->dmamode |= STM32_DMA_CR_PFCTRL | + STM32_DMA_CR_PBURST_INCR4 | + STM32_DMA_CR_MBURST_INCR4; +#endif + + if (sdcp->state == BLK_STOP) { + /* Note, the DMA must be enabled before the IRQs.*/ + bool_t b; + b = dmaStreamAllocate(sdcp->dma, STM32_SDC_SDIO_IRQ_PRIORITY, NULL, NULL); + chDbgAssert(!b, "sdc_lld_start(), #1", "stream already allocated"); + dmaStreamSetPeripheral(sdcp->dma, &SDIO->FIFO); +#if (defined(STM32F4XX) || defined(STM32F2XX)) + dmaStreamSetFIFO(sdcp->dma, STM32_DMA_FCR_DMDIS | STM32_DMA_FCR_FTH_FULL); +#endif + nvicEnableVector(STM32_SDIO_NUMBER, + CORTEX_PRIORITY_MASK(STM32_SDC_SDIO_IRQ_PRIORITY)); + rccEnableSDIO(FALSE); + } + + /* Configuration, card clock is initially stopped.*/ + SDIO->POWER = 0; + SDIO->CLKCR = 0; + SDIO->DCTRL = 0; + SDIO->DTIMER = 0; +} + +/** + * @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.*/ + SDIO->POWER = 0; + SDIO->CLKCR = 0; + SDIO->DCTRL = 0; + SDIO->DTIMER = 0; + + /* Clock deactivation.*/ + nvicDisableVector(STM32_SDIO_NUMBER); + dmaStreamRelease(sdcp->dma); + rccDisableSDIO(FALSE); + } +} + +/** + * @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) { + + (void)sdcp; + + /* Initial clock setting: 400kHz, 1bit mode.*/ + SDIO->CLKCR = STM32_SDIO_DIV_LS; + SDIO->POWER |= SDIO_POWER_PWRCTRL_0 | SDIO_POWER_PWRCTRL_1; + SDIO->CLKCR |= SDIO_CLKCR_CLKEN; +} + +/** + * @brief Sets the SDIO clock to data mode (25MHz or less). + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @notapi + */ +void sdc_lld_set_data_clk(SDCDriver *sdcp) { + + (void)sdcp; + + SDIO->CLKCR = (SDIO->CLKCR & 0xFFFFFF00) | STM32_SDIO_DIV_HS; +} + +/** + * @brief Stops the SDIO clock. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @notapi + */ +void sdc_lld_stop_clk(SDCDriver *sdcp) { + + (void)sdcp; + + SDIO->CLKCR = 0; + SDIO->POWER = 0; +} + +/** + * @brief Switches the bus to 4 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 = SDIO->CLKCR & ~SDIO_CLKCR_WIDBUS; + + (void)sdcp; + + switch (mode) { + case SDC_MODE_1BIT: + SDIO->CLKCR = clk; + break; + case SDC_MODE_4BIT: + SDIO->CLKCR = clk | SDIO_CLKCR_WIDBUS_0; + break; + case SDC_MODE_8BIT: + SDIO->CLKCR = clk | SDIO_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) { + + (void)sdcp; + + SDIO->ARG = arg; + SDIO->CMD = (uint32_t)cmd | SDIO_CMD_CPSMEN; + while ((SDIO->STA & SDIO_STA_CMDSENT) == 0) + ; + SDIO->ICR = SDIO_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 CH_SUCCESS operation succeeded. + * @retval CH_FAILED operation failed. + * + * @notapi + */ +bool_t sdc_lld_send_cmd_short(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, + uint32_t *resp) { + uint32_t sta; + + (void)sdcp; + + SDIO->ARG = arg; + SDIO->CMD = (uint32_t)cmd | SDIO_CMD_WAITRESP_0 | SDIO_CMD_CPSMEN; + while (((sta = SDIO->STA) & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | + SDIO_STA_CCRCFAIL)) == 0) + ; + SDIO->ICR = sta; + if ((sta & (SDIO_STA_CTIMEOUT)) != 0) { + sdc_lld_collect_errors(sdcp, sta); + return CH_FAILED; + } + *resp = SDIO->RESP1; + return CH_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 CH_SUCCESS operation succeeded. + * @retval CH_FAILED operation failed. + * + * @notapi + */ +bool_t sdc_lld_send_cmd_short_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, + uint32_t *resp) { + uint32_t sta; + + (void)sdcp; + + SDIO->ARG = arg; + SDIO->CMD = (uint32_t)cmd | SDIO_CMD_WAITRESP_0 | SDIO_CMD_CPSMEN; + while (((sta = SDIO->STA) & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | + SDIO_STA_CCRCFAIL)) == 0) + ; + SDIO->ICR = sta; + if ((sta & (SDIO_STA_CTIMEOUT | SDIO_STA_CCRCFAIL)) != 0) { + sdc_lld_collect_errors(sdcp, sta); + return CH_FAILED; + } + *resp = SDIO->RESP1; + return CH_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 CH_SUCCESS operation succeeded. + * @retval CH_FAILED operation failed. + * + * @notapi + */ +bool_t sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, + uint32_t *resp) { + uint32_t sta; + + (void)sdcp; + + SDIO->ARG = arg; + SDIO->CMD = (uint32_t)cmd | SDIO_CMD_WAITRESP_0 | SDIO_CMD_WAITRESP_1 | + SDIO_CMD_CPSMEN; + while (((sta = SDIO->STA) & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | + SDIO_STA_CCRCFAIL)) == 0) + ; + SDIO->ICR = sta; + if ((sta & (STM32_SDIO_STA_ERROR_MASK)) != 0) { + sdc_lld_collect_errors(sdcp, sta); + return CH_FAILED; + } + /* Save bytes in reverse order because MSB in response comes first.*/ + *resp++ = SDIO->RESP4; + *resp++ = SDIO->RESP3; + *resp++ = SDIO->RESP2; + *resp = SDIO->RESP1; + return CH_SUCCESS; +} + +/** + * @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] n number of blocks to read + * + * @return The operation status. + * @retval CH_SUCCESS operation succeeded. + * @retval CH_FAILED operation failed. + * + * @notapi + */ +bool_t sdc_lld_read_aligned(SDCDriver *sdcp, uint32_t startblk, + uint8_t *buf, uint32_t n) { + uint32_t resp[1]; + + chDbgCheck((n < (0x1000000 / MMCSD_BLOCK_SIZE)), "max transaction size"); + + SDIO->DTIMER = STM32_SDC_READ_TIMEOUT; + + /* Checks for errors and waits for the card to be ready for reading.*/ + if (_sdc_wait_for_transfer_state(sdcp)) + return CH_FAILED; + + /* Prepares the DMA channel for writing.*/ + dmaStreamSetMemory0(sdcp->dma, buf); + dmaStreamSetTransactionSize(sdcp->dma, + (n * MMCSD_BLOCK_SIZE) / sizeof (uint32_t)); + dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_P2M); + dmaStreamEnable(sdcp->dma); + + /* Setting up data transfer.*/ + SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS; + SDIO->MASK = SDIO_MASK_DCRCFAILIE | + SDIO_MASK_DTIMEOUTIE | + SDIO_MASK_STBITERRIE | + SDIO_MASK_RXOVERRIE | + SDIO_MASK_DATAENDIE; + SDIO->DLEN = n * MMCSD_BLOCK_SIZE; + + /* Talk to card what we want from it.*/ + if (sdc_lld_prepare_read(sdcp, startblk, n, resp) == TRUE) + goto error; + + /* Transaction starts just after DTEN bit setting.*/ + SDIO->DCTRL = SDIO_DCTRL_DTDIR | + SDIO_DCTRL_DBLOCKSIZE_3 | + SDIO_DCTRL_DBLOCKSIZE_0 | + SDIO_DCTRL_DMAEN | + SDIO_DCTRL_DTEN; + if (sdc_lld_wait_transaction_end(sdcp, n, resp) == TRUE) + goto error; + + return CH_SUCCESS; + +error: + sdc_lld_error_cleanup(sdcp, n, resp); + return CH_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 CH_SUCCESS operation succeeded. + * @retval CH_FAILED operation failed. + * + * @notapi + */ +bool_t sdc_lld_write_aligned(SDCDriver *sdcp, uint32_t startblk, + const uint8_t *buf, uint32_t n) { + uint32_t resp[1]; + + chDbgCheck((n < (0x1000000 / MMCSD_BLOCK_SIZE)), "max transaction size"); + + SDIO->DTIMER = STM32_SDC_WRITE_TIMEOUT; + + /* Checks for errors and waits for the card to be ready for writing.*/ + if (_sdc_wait_for_transfer_state(sdcp)) + return CH_FAILED; + + /* Prepares the DMA channel for writing.*/ + dmaStreamSetMemory0(sdcp->dma, buf); + dmaStreamSetTransactionSize(sdcp->dma, + (n * MMCSD_BLOCK_SIZE) / sizeof (uint32_t)); + dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_M2P); + dmaStreamEnable(sdcp->dma); + + /* Setting up data transfer.*/ + SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS; + SDIO->MASK = SDIO_MASK_DCRCFAILIE | + SDIO_MASK_DTIMEOUTIE | + SDIO_MASK_STBITERRIE | + SDIO_MASK_TXUNDERRIE | + SDIO_MASK_DATAENDIE; + SDIO->DLEN = n * MMCSD_BLOCK_SIZE; + + /* Talk to card what we want from it.*/ + if (sdc_lld_prepare_write(sdcp, startblk, n, resp) == TRUE) + goto error; + + /* Transaction starts just after DTEN bit setting.*/ + SDIO->DCTRL = SDIO_DCTRL_DBLOCKSIZE_3 | + SDIO_DCTRL_DBLOCKSIZE_0 | + SDIO_DCTRL_DMAEN | + SDIO_DCTRL_DTEN; + if (sdc_lld_wait_transaction_end(sdcp, n, resp) == TRUE) + goto error; + + return CH_SUCCESS; + +error: + sdc_lld_error_cleanup(sdcp, n, resp); + return CH_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] n number of blocks to read + * + * @return The operation status. + * @retval CH_SUCCESS operation succeeded. + * @retval CH_FAILED operation failed. + * + * @notapi + */ +bool_t sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, + uint8_t *buf, uint32_t n) { + +#if STM32_SDC_SDIO_UNALIGNED_SUPPORT + if (((unsigned)buf & 3) != 0) { + uint32_t i; + for (i = 0; i < n; i++) { + if (sdc_lld_read_aligned(sdcp, startblk, u.buf, 1)) + return CH_FAILED; + memcpy(buf, u.buf, MMCSD_BLOCK_SIZE); + buf += MMCSD_BLOCK_SIZE; + startblk++; + } + return CH_SUCCESS; + } +#endif /* STM32_SDC_SDIO_UNALIGNED_SUPPORT */ + return sdc_lld_read_aligned(sdcp, startblk, buf, n); +} + +/** + * @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 CH_SUCCESS operation succeeded. + * @retval CH_FAILED operation failed. + * + * @notapi + */ +bool_t sdc_lld_write(SDCDriver *sdcp, uint32_t startblk, + const uint8_t *buf, uint32_t n) { + +#if STM32_SDC_SDIO_UNALIGNED_SUPPORT + if (((unsigned)buf & 3) != 0) { + uint32_t i; + for (i = 0; i < n; i++) { + memcpy(u.buf, buf, MMCSD_BLOCK_SIZE); + buf += MMCSD_BLOCK_SIZE; + if (sdc_lld_write_aligned(sdcp, startblk, u.buf, 1)) + return CH_FAILED; + startblk++; + } + return CH_SUCCESS; + } +#endif /* STM32_SDC_SDIO_UNALIGNED_SUPPORT */ + return sdc_lld_write_aligned(sdcp, startblk, buf, n); +} + +/** + * @brief Waits for card idle condition. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @return The operation status. + * @retval CH_SUCCESS the operation succeeded. + * @retval CH_FAILED the operation failed. + * + * @api + */ +bool_t sdc_lld_sync(SDCDriver *sdcp) { + + /* TODO: Implement.*/ + (void)sdcp; + return CH_SUCCESS; +} + +#endif /* HAL_USE_SDC */ + +/** @} */ -- cgit v1.2.3 From 74b860fee65ca8feb2a796144976ff6c44e64bcc Mon Sep 17 00:00:00 2001 From: gdisirio Date: Mon, 2 Sep 2013 09:48:38 +0000 Subject: git-svn-id: svn://svn.code.sf.net/p/chibios/svn/branches/kernel_3_dev@6242 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/platforms/STM32/sdc_lld.c | 788 --------------------------------------- 1 file changed, 788 deletions(-) delete mode 100644 os/hal/platforms/STM32/sdc_lld.c (limited to 'os/hal/platforms/STM32/sdc_lld.c') diff --git a/os/hal/platforms/STM32/sdc_lld.c b/os/hal/platforms/STM32/sdc_lld.c deleted file mode 100644 index 69873d5f0..000000000 --- a/os/hal/platforms/STM32/sdc_lld.c +++ /dev/null @@ -1,788 +0,0 @@ -/* - ChibiOS/RT - Copyright (C) 2006-2013 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 STM32/sdc_lld.c - * @brief STM32 SDC subsystem low level driver source. - * - * @addtogroup SDC - * @{ - */ - -/* - TODO: Try preerase blocks before writing (ACMD23). - */ - -#include - -#include "ch.h" -#include "hal.h" - -#if HAL_USE_SDC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SDC_SDIO_DMA_STREAM, \ - STM32_SDC_SDIO_DMA_CHN) - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief SDCD1 driver identifier.*/ -SDCDriver SDCD1; - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -#if STM32_SDC_SDIO_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_SDIO_UNALIGNED_SUPPORT */ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @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 CH_SUCCESS operation succeeded. - * @retval CH_FAILED operation failed. - * - * @notapi - */ -static bool_t 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 CH_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 CH_FAILED; - } - - return CH_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 CH_SUCCESS operation succeeded. - * @retval CH_FAILED operation failed. - * - * @notapi - */ -static bool_t 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 CH_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 CH_FAILED; - } - - return CH_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 CH_SUCCESS operation succeeded. - * @retval CH_FAILED operation failed. - */ -static bool_t 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.*/ - chSysLock(); - if (SDIO->MASK != 0) { - chDbgAssert(sdcp->thread == NULL, - "sdc_lld_start_data_transaction(), #1", "not NULL"); - sdcp->thread = chThdSelf(); - chSchGoSleepS(THD_STATE_SUSPENDED); - chDbgAssert(sdcp->thread == NULL, - "sdc_lld_start_data_transaction(), #2", "not NULL"); - } - if ((SDIO->STA & SDIO_STA_DATAEND) == 0) { - chSysUnlock(); - return CH_FAILED; - } - -#if (defined(STM32F4XX) || defined(STM32F2XX)) - /* Wait until DMA channel enabled to be sure that all data transferred.*/ - while (sdcp->dma->stream->CR & STM32_DMA_CR_EN) - ; - - /* DMA event flags must be manually cleared.*/ - dmaStreamClearInterrupt(sdcp->dma); - - SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS; - SDIO->DCTRL = 0; - chSysUnlock(); - - /* Wait until interrupt flags to be cleared.*/ - /*while (((DMA2->LISR) >> (sdcp->dma->ishift)) & STM32_DMA_ISR_TCIF) - dmaStreamClearInterrupt(sdcp->dma);*/ -#else - /* Waits for transfer completion at DMA level, the the stream is - disabled and cleared.*/ - dmaWaitCompletion(sdcp->dma); - - SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS; - SDIO->DCTRL = 0; - chSysUnlock(); -#endif - - /* Finalize transaction.*/ - if (n > 1) - return sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp); - - return CH_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 & SDIO_STA_CCRCFAIL) - errors |= SDC_CMD_CRC_ERROR; - if (sta & SDIO_STA_DCRCFAIL) - errors |= SDC_DATA_CRC_ERROR; - if (sta & SDIO_STA_CTIMEOUT) - errors |= SDC_COMMAND_TIMEOUT; - if (sta & SDIO_STA_DTIMEOUT) - errors |= SDC_DATA_TIMEOUT; - if (sta & SDIO_STA_TXUNDERR) - errors |= SDC_TX_UNDERRUN; - if (sta & SDIO_STA_RXOVERR) - errors |= SDC_RX_OVERRUN; - if (sta & SDIO_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 = SDIO->STA; - - dmaStreamClearInterrupt(sdcp->dma); - dmaStreamDisable(sdcp->dma); - SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS; - SDIO->MASK = 0; - SDIO->DCTRL = 0; - 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. */ -/*===========================================================================*/ - -#if !defined(STM32_SDIO_HANDLER) -#error "STM32_SDIO_HANDLER not defined" -#endif -/** - * @brief SDIO IRQ handler. - * @details It just wakes transaction thread. All error handling performs in - * that thread. - * - * @isr - */ -CH_IRQ_HANDLER(STM32_SDIO_HANDLER) { - - CH_IRQ_PROLOGUE(); - - chSysLockFromIsr() - - /* Disables the source but the status flags are not reset because the - read/write functions needs to check them.*/ - SDIO->MASK = 0; - - if (SDCD1.thread != NULL) { - chSchReadyI(SDCD1.thread); - SDCD1.thread = NULL; - } - - chSysUnlockFromIsr(); - - CH_IRQ_EPILOGUE(); -} - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level SDC driver initialization. - * - * @notapi - */ -void sdc_lld_init(void) { - - sdcObjectInit(&SDCD1); - SDCD1.thread = NULL; - SDCD1.dma = STM32_DMA_STREAM(STM32_SDC_SDIO_DMA_STREAM); -#if CH_DBG_ENABLE_ASSERTS - SDCD1.sdio = SDIO; -#endif -} - -/** - * @brief Configures and activates the SDC peripheral. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @notapi - */ -void sdc_lld_start(SDCDriver *sdcp) { - - sdcp->dmamode = STM32_DMA_CR_CHSEL(DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SDC_SDIO_DMA_PRIORITY) | - STM32_DMA_CR_PSIZE_WORD | - STM32_DMA_CR_MSIZE_WORD | - STM32_DMA_CR_MINC; - -#if (defined(STM32F4XX) || defined(STM32F2XX)) - sdcp->dmamode |= STM32_DMA_CR_PFCTRL | - STM32_DMA_CR_PBURST_INCR4 | - STM32_DMA_CR_MBURST_INCR4; -#endif - - if (sdcp->state == BLK_STOP) { - /* Note, the DMA must be enabled before the IRQs.*/ - bool_t b; - b = dmaStreamAllocate(sdcp->dma, STM32_SDC_SDIO_IRQ_PRIORITY, NULL, NULL); - chDbgAssert(!b, "sdc_lld_start(), #1", "stream already allocated"); - dmaStreamSetPeripheral(sdcp->dma, &SDIO->FIFO); -#if (defined(STM32F4XX) || defined(STM32F2XX)) - dmaStreamSetFIFO(sdcp->dma, STM32_DMA_FCR_DMDIS | STM32_DMA_FCR_FTH_FULL); -#endif - nvicEnableVector(STM32_SDIO_NUMBER, - CORTEX_PRIORITY_MASK(STM32_SDC_SDIO_IRQ_PRIORITY)); - rccEnableSDIO(FALSE); - } - - /* Configuration, card clock is initially stopped.*/ - SDIO->POWER = 0; - SDIO->CLKCR = 0; - SDIO->DCTRL = 0; - SDIO->DTIMER = 0; -} - -/** - * @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.*/ - SDIO->POWER = 0; - SDIO->CLKCR = 0; - SDIO->DCTRL = 0; - SDIO->DTIMER = 0; - - /* Clock deactivation.*/ - nvicDisableVector(STM32_SDIO_NUMBER); - dmaStreamRelease(sdcp->dma); - rccDisableSDIO(FALSE); - } -} - -/** - * @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) { - - (void)sdcp; - - /* Initial clock setting: 400kHz, 1bit mode.*/ - SDIO->CLKCR = STM32_SDIO_DIV_LS; - SDIO->POWER |= SDIO_POWER_PWRCTRL_0 | SDIO_POWER_PWRCTRL_1; - SDIO->CLKCR |= SDIO_CLKCR_CLKEN; -} - -/** - * @brief Sets the SDIO clock to data mode (25MHz or less). - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @notapi - */ -void sdc_lld_set_data_clk(SDCDriver *sdcp) { - - (void)sdcp; - - SDIO->CLKCR = (SDIO->CLKCR & 0xFFFFFF00) | STM32_SDIO_DIV_HS; -} - -/** - * @brief Stops the SDIO clock. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @notapi - */ -void sdc_lld_stop_clk(SDCDriver *sdcp) { - - (void)sdcp; - - SDIO->CLKCR = 0; - SDIO->POWER = 0; -} - -/** - * @brief Switches the bus to 4 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 = SDIO->CLKCR & ~SDIO_CLKCR_WIDBUS; - - (void)sdcp; - - switch (mode) { - case SDC_MODE_1BIT: - SDIO->CLKCR = clk; - break; - case SDC_MODE_4BIT: - SDIO->CLKCR = clk | SDIO_CLKCR_WIDBUS_0; - break; - case SDC_MODE_8BIT: - SDIO->CLKCR = clk | SDIO_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) { - - (void)sdcp; - - SDIO->ARG = arg; - SDIO->CMD = (uint32_t)cmd | SDIO_CMD_CPSMEN; - while ((SDIO->STA & SDIO_STA_CMDSENT) == 0) - ; - SDIO->ICR = SDIO_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 CH_SUCCESS operation succeeded. - * @retval CH_FAILED operation failed. - * - * @notapi - */ -bool_t sdc_lld_send_cmd_short(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp) { - uint32_t sta; - - (void)sdcp; - - SDIO->ARG = arg; - SDIO->CMD = (uint32_t)cmd | SDIO_CMD_WAITRESP_0 | SDIO_CMD_CPSMEN; - while (((sta = SDIO->STA) & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | - SDIO_STA_CCRCFAIL)) == 0) - ; - SDIO->ICR = sta; - if ((sta & (SDIO_STA_CTIMEOUT)) != 0) { - sdc_lld_collect_errors(sdcp, sta); - return CH_FAILED; - } - *resp = SDIO->RESP1; - return CH_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 CH_SUCCESS operation succeeded. - * @retval CH_FAILED operation failed. - * - * @notapi - */ -bool_t sdc_lld_send_cmd_short_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp) { - uint32_t sta; - - (void)sdcp; - - SDIO->ARG = arg; - SDIO->CMD = (uint32_t)cmd | SDIO_CMD_WAITRESP_0 | SDIO_CMD_CPSMEN; - while (((sta = SDIO->STA) & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | - SDIO_STA_CCRCFAIL)) == 0) - ; - SDIO->ICR = sta; - if ((sta & (SDIO_STA_CTIMEOUT | SDIO_STA_CCRCFAIL)) != 0) { - sdc_lld_collect_errors(sdcp, sta); - return CH_FAILED; - } - *resp = SDIO->RESP1; - return CH_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 CH_SUCCESS operation succeeded. - * @retval CH_FAILED operation failed. - * - * @notapi - */ -bool_t sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp) { - uint32_t sta; - - (void)sdcp; - - SDIO->ARG = arg; - SDIO->CMD = (uint32_t)cmd | SDIO_CMD_WAITRESP_0 | SDIO_CMD_WAITRESP_1 | - SDIO_CMD_CPSMEN; - while (((sta = SDIO->STA) & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | - SDIO_STA_CCRCFAIL)) == 0) - ; - SDIO->ICR = sta; - if ((sta & (STM32_SDIO_STA_ERROR_MASK)) != 0) { - sdc_lld_collect_errors(sdcp, sta); - return CH_FAILED; - } - /* Save bytes in reverse order because MSB in response comes first.*/ - *resp++ = SDIO->RESP4; - *resp++ = SDIO->RESP3; - *resp++ = SDIO->RESP2; - *resp = SDIO->RESP1; - return CH_SUCCESS; -} - -/** - * @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] n number of blocks to read - * - * @return The operation status. - * @retval CH_SUCCESS operation succeeded. - * @retval CH_FAILED operation failed. - * - * @notapi - */ -bool_t sdc_lld_read_aligned(SDCDriver *sdcp, uint32_t startblk, - uint8_t *buf, uint32_t n) { - uint32_t resp[1]; - - chDbgCheck((n < (0x1000000 / MMCSD_BLOCK_SIZE)), "max transaction size"); - - SDIO->DTIMER = STM32_SDC_READ_TIMEOUT; - - /* Checks for errors and waits for the card to be ready for reading.*/ - if (_sdc_wait_for_transfer_state(sdcp)) - return CH_FAILED; - - /* Prepares the DMA channel for writing.*/ - dmaStreamSetMemory0(sdcp->dma, buf); - dmaStreamSetTransactionSize(sdcp->dma, - (n * MMCSD_BLOCK_SIZE) / sizeof (uint32_t)); - dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_P2M); - dmaStreamEnable(sdcp->dma); - - /* Setting up data transfer.*/ - SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS; - SDIO->MASK = SDIO_MASK_DCRCFAILIE | - SDIO_MASK_DTIMEOUTIE | - SDIO_MASK_STBITERRIE | - SDIO_MASK_RXOVERRIE | - SDIO_MASK_DATAENDIE; - SDIO->DLEN = n * MMCSD_BLOCK_SIZE; - - /* Talk to card what we want from it.*/ - if (sdc_lld_prepare_read(sdcp, startblk, n, resp) == TRUE) - goto error; - - /* Transaction starts just after DTEN bit setting.*/ - SDIO->DCTRL = SDIO_DCTRL_DTDIR | - SDIO_DCTRL_DBLOCKSIZE_3 | - SDIO_DCTRL_DBLOCKSIZE_0 | - SDIO_DCTRL_DMAEN | - SDIO_DCTRL_DTEN; - if (sdc_lld_wait_transaction_end(sdcp, n, resp) == TRUE) - goto error; - - return CH_SUCCESS; - -error: - sdc_lld_error_cleanup(sdcp, n, resp); - return CH_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 CH_SUCCESS operation succeeded. - * @retval CH_FAILED operation failed. - * - * @notapi - */ -bool_t sdc_lld_write_aligned(SDCDriver *sdcp, uint32_t startblk, - const uint8_t *buf, uint32_t n) { - uint32_t resp[1]; - - chDbgCheck((n < (0x1000000 / MMCSD_BLOCK_SIZE)), "max transaction size"); - - SDIO->DTIMER = STM32_SDC_WRITE_TIMEOUT; - - /* Checks for errors and waits for the card to be ready for writing.*/ - if (_sdc_wait_for_transfer_state(sdcp)) - return CH_FAILED; - - /* Prepares the DMA channel for writing.*/ - dmaStreamSetMemory0(sdcp->dma, buf); - dmaStreamSetTransactionSize(sdcp->dma, - (n * MMCSD_BLOCK_SIZE) / sizeof (uint32_t)); - dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_M2P); - dmaStreamEnable(sdcp->dma); - - /* Setting up data transfer.*/ - SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS; - SDIO->MASK = SDIO_MASK_DCRCFAILIE | - SDIO_MASK_DTIMEOUTIE | - SDIO_MASK_STBITERRIE | - SDIO_MASK_TXUNDERRIE | - SDIO_MASK_DATAENDIE; - SDIO->DLEN = n * MMCSD_BLOCK_SIZE; - - /* Talk to card what we want from it.*/ - if (sdc_lld_prepare_write(sdcp, startblk, n, resp) == TRUE) - goto error; - - /* Transaction starts just after DTEN bit setting.*/ - SDIO->DCTRL = SDIO_DCTRL_DBLOCKSIZE_3 | - SDIO_DCTRL_DBLOCKSIZE_0 | - SDIO_DCTRL_DMAEN | - SDIO_DCTRL_DTEN; - if (sdc_lld_wait_transaction_end(sdcp, n, resp) == TRUE) - goto error; - - return CH_SUCCESS; - -error: - sdc_lld_error_cleanup(sdcp, n, resp); - return CH_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] n number of blocks to read - * - * @return The operation status. - * @retval CH_SUCCESS operation succeeded. - * @retval CH_FAILED operation failed. - * - * @notapi - */ -bool_t sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, - uint8_t *buf, uint32_t n) { - -#if STM32_SDC_SDIO_UNALIGNED_SUPPORT - if (((unsigned)buf & 3) != 0) { - uint32_t i; - for (i = 0; i < n; i++) { - if (sdc_lld_read_aligned(sdcp, startblk, u.buf, 1)) - return CH_FAILED; - memcpy(buf, u.buf, MMCSD_BLOCK_SIZE); - buf += MMCSD_BLOCK_SIZE; - startblk++; - } - return CH_SUCCESS; - } -#endif /* STM32_SDC_SDIO_UNALIGNED_SUPPORT */ - return sdc_lld_read_aligned(sdcp, startblk, buf, n); -} - -/** - * @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 CH_SUCCESS operation succeeded. - * @retval CH_FAILED operation failed. - * - * @notapi - */ -bool_t sdc_lld_write(SDCDriver *sdcp, uint32_t startblk, - const uint8_t *buf, uint32_t n) { - -#if STM32_SDC_SDIO_UNALIGNED_SUPPORT - if (((unsigned)buf & 3) != 0) { - uint32_t i; - for (i = 0; i < n; i++) { - memcpy(u.buf, buf, MMCSD_BLOCK_SIZE); - buf += MMCSD_BLOCK_SIZE; - if (sdc_lld_write_aligned(sdcp, startblk, u.buf, 1)) - return CH_FAILED; - startblk++; - } - return CH_SUCCESS; - } -#endif /* STM32_SDC_SDIO_UNALIGNED_SUPPORT */ - return sdc_lld_write_aligned(sdcp, startblk, buf, n); -} - -/** - * @brief Waits for card idle condition. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @return The operation status. - * @retval CH_SUCCESS the operation succeeded. - * @retval CH_FAILED the operation failed. - * - * @api - */ -bool_t sdc_lld_sync(SDCDriver *sdcp) { - - /* TODO: Implement.*/ - (void)sdcp; - return CH_SUCCESS; -} - -#endif /* HAL_USE_SDC */ - -/** @} */ -- cgit v1.2.3