966 lines
28 KiB
C
966 lines
28 KiB
C
/* -----------------------------------------------------------------------------
|
|
* Copyright (c) 2013-2015 ARM Ltd.
|
|
*
|
|
* This software is provided 'as-is', without any express or implied warranty.
|
|
* In no event will the authors be held liable for any damages arising from
|
|
* the use of this software. Permission is granted to anyone to use this
|
|
* software for any purpose, including commercial applications, and to alter
|
|
* it and redistribute it freely, subject to the following restrictions:
|
|
*
|
|
* 1. The origin of this software must not be misrepresented; you must not
|
|
* claim that you wrote the original software. If you use this software in
|
|
* a product, an acknowledgment in the product documentation would be
|
|
* appreciated but is not required.
|
|
*
|
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
* misrepresented as being the original software.
|
|
*
|
|
* 3. This notice may not be removed or altered from any source distribution.
|
|
*
|
|
*
|
|
* $Date: 22. September 2015
|
|
* $Revision: V2.0
|
|
*
|
|
* Driver: Driver_MCI0
|
|
* Configured: via RTE_Device.h configuration file
|
|
* Project: MCI Driver for STMicroelectronics STM32F10x
|
|
* --------------------------------------------------------------------------
|
|
* Use the following configuration settings in the middleware component
|
|
* to connect to this driver.
|
|
*
|
|
* Configuration Setting Value
|
|
* --------------------- -----
|
|
* Connect to hardware via Driver_MCI# = 0
|
|
* -------------------------------------------------------------------------- */
|
|
|
|
/* History:
|
|
* Version 2.0
|
|
* Updated to CMSIS Driver API V2.02
|
|
* Version 1.2
|
|
* ST StdPeriph Drivers used for GPIO and DMA
|
|
* Version 1.1
|
|
* Based on API V1.10 (namespace prefix ARM_ added)
|
|
* Version 1.0
|
|
* Initial release
|
|
*/
|
|
|
|
#include "MCI_STM32F10x.h"
|
|
|
|
#define ARM_MCI_DRV_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(2,0) /* driver version */
|
|
|
|
/* Enable High Speed bus mode */
|
|
#if defined(MemoryCard_Bus_Mode_HS_Enable)
|
|
#define MCI_BUS_MODE_HS 1U
|
|
#else
|
|
#define MCI_BUS_MODE_HS 0U
|
|
#endif
|
|
|
|
/* Define Card Detect pin active state */
|
|
#if !defined(MemoryCard_CD_Pin_Active)
|
|
#define MemoryCard_CD_Pin_Active GPIO_PIN_RESET
|
|
#endif
|
|
|
|
/* Define Write Protect pin active state */
|
|
#if !defined(MemoryCard_WP_Pin_Active)
|
|
#define MemoryCard_WP_Pin_Active GPIO_PIN_SET
|
|
#endif
|
|
|
|
static MCI_INFO MCI;
|
|
|
|
/* IRQ Handler prototype */
|
|
void SDIO_IRQHandler (void);
|
|
|
|
|
|
/* Driver Version */
|
|
static const ARM_DRIVER_VERSION DriverVersion = {
|
|
ARM_MCI_API_VERSION,
|
|
ARM_MCI_DRV_VERSION
|
|
};
|
|
|
|
/* Driver Capabilities */
|
|
static const ARM_MCI_CAPABILITIES DriverCapabilities = {
|
|
MCI_CD_PIN, /* cd_state */
|
|
0U, /* cd_event */
|
|
MCI_WP_PIN, /* wp_state */
|
|
0U, /* vdd */
|
|
0U, /* vdd_1v8 */
|
|
0U, /* vccq */
|
|
0U, /* vccq_1v8 */
|
|
0U, /* vccq_1v2 */
|
|
MCI_BUS_WIDTH_4, /* data_width_4 */
|
|
MCI_BUS_WIDTH_8, /* data_width_8 */
|
|
0U, /* data_width_4_ddr */
|
|
0U, /* data_width_8_ddr */
|
|
MCI_BUS_MODE_HS, /* high_speed */
|
|
0U, /* uhs_signaling */
|
|
0U, /* uhs_tuning */
|
|
0U, /* uhs_sdr50 */
|
|
0U, /* uhs_sdr104 */
|
|
0U, /* uhs_ddr50 */
|
|
0U, /* uhs_driver_type_a */
|
|
0U, /* uhs_driver_type_c */
|
|
0U, /* uhs_driver_type_d */
|
|
1U, /* sdio_interrupt */
|
|
1U, /* read_wait */
|
|
0U, /* suspend_resume */
|
|
0U, /* mmc_interrupt */
|
|
0U, /* mmc_boot */
|
|
0U, /* rst_n */
|
|
0U, /* ccs */
|
|
0U /* ccs_timeout */
|
|
};
|
|
|
|
|
|
/**
|
|
\fn ARM_DRV_VERSION GetVersion (void)
|
|
\brief Get driver version.
|
|
\return \ref ARM_DRV_VERSION
|
|
*/
|
|
static ARM_DRIVER_VERSION GetVersion (void) {
|
|
return DriverVersion;
|
|
}
|
|
|
|
|
|
/**
|
|
\fn ARM_MCI_CAPABILITIES MCI_GetCapabilities (void)
|
|
\brief Get driver capabilities.
|
|
\return \ref ARM_MCI_CAPABILITIES
|
|
*/
|
|
static ARM_MCI_CAPABILITIES GetCapabilities (void) {
|
|
return DriverCapabilities;
|
|
}
|
|
|
|
|
|
/**
|
|
\fn int32_t Initialize (ARM_MCI_SignalEvent_t cb_event)
|
|
\brief Initialize the Memory Card Interface
|
|
\param[in] cb_event Pointer to \ref ARM_MCI_SignalEvent
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t Initialize (ARM_MCI_SignalEvent_t cb_event) {
|
|
|
|
if (MCI.flags & MCI_INIT) { return ARM_DRIVER_OK; }
|
|
|
|
SystemCoreClockUpdate();
|
|
|
|
/* GPIO Ports Clock Enable */
|
|
GPIO_PortClock (GPIOC, true);
|
|
GPIO_PortClock (GPIOD, true);
|
|
|
|
/* Configure CMD, CK and D0 pins */
|
|
GPIO_PinConfigure(RTE_SDIO_CMD_PORT, RTE_SDIO_CMD_PIN, GPIO_AF_PUSHPULL,
|
|
GPIO_MODE_OUT50MHZ);
|
|
|
|
GPIO_PinConfigure(RTE_SDIO_CK_PORT, RTE_SDIO_CK_PIN, GPIO_AF_PUSHPULL,
|
|
GPIO_MODE_OUT50MHZ);
|
|
|
|
GPIO_PinConfigure(RTE_SDIO_D0_PORT, RTE_SDIO_D0_PIN, GPIO_AF_PUSHPULL,
|
|
GPIO_MODE_OUT50MHZ);
|
|
|
|
#if (MCI_BUS_WIDTH_4)
|
|
/* D[1:3] */
|
|
GPIO_PinConfigure(RTE_SDIO_D1_PORT, RTE_SDIO_D1_PIN, GPIO_AF_PUSHPULL,
|
|
GPIO_MODE_OUT50MHZ);
|
|
GPIO_PinConfigure(RTE_SDIO_D2_PORT, RTE_SDIO_D2_PIN, GPIO_AF_PUSHPULL,
|
|
GPIO_MODE_OUT50MHZ);
|
|
GPIO_PinConfigure(RTE_SDIO_D3_PORT, RTE_SDIO_D3_PIN, GPIO_AF_PUSHPULL,
|
|
GPIO_MODE_OUT50MHZ);
|
|
#endif
|
|
|
|
#if (MCI_BUS_WIDTH_8)
|
|
/* D[4:7] */
|
|
GPIO_PortClock (GPIOB, true);
|
|
|
|
GPIO_PinConfigure(RTE_SDIO_D4_PORT, RTE_SDIO_D4_PIN, GPIO_AF_PUSHPULL,
|
|
GPIO_MODE_OUT50MHZ);
|
|
GPIO_PinConfigure(RTE_SDIO_D5_PORT, RTE_SDIO_D5_PIN, GPIO_AF_PUSHPULL,
|
|
GPIO_MODE_OUT50MHZ);
|
|
GPIO_PinConfigure(RTE_SDIO_D6_PORT, RTE_SDIO_D6_PIN, GPIO_AF_PUSHPULL,
|
|
GPIO_MODE_OUT50MHZ);
|
|
GPIO_PinConfigure(RTE_SDIO_D7_PORT, RTE_SDIO_D7_PIN, GPIO_AF_PUSHPULL,
|
|
GPIO_MODE_OUT50MHZ);
|
|
#endif
|
|
|
|
/* Configure CD (Card Detect) Pin */
|
|
#if defined (MemoryCard_CD_Pin)
|
|
GPIO_PortClock (MemoryCard_CD_GPIOx, true);
|
|
|
|
GPIO_PinConfigure (MemoryCard_CD_GPIOx, MemoryCard_CD_GPIO_Pin, MemoryCard_CD_GPIO_PuPd,
|
|
GPIO_MODE_INPUT);
|
|
#endif
|
|
|
|
/* Configure WP (Write Protect) Pin */
|
|
#if defined (MemoryCard_WP_Pin)
|
|
GPIO_PortClock (MemoryCard_WP_GPIOx, true);
|
|
|
|
GPIO_PinConfigure (MemoryCard_WP_GPIOx, MemoryCard_WP_GPIO_Pin, MemoryCard_WP_GPIO_PuPd,
|
|
GPIO_MODE_INPUT);
|
|
#endif
|
|
|
|
/* Clear control structure */
|
|
memset (&MCI, 0, sizeof (MCI_INFO));
|
|
|
|
MCI.cb_event = cb_event;
|
|
MCI.flags = MCI_INIT;
|
|
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
|
|
/**
|
|
\fn int32_t Uninitialize (void)
|
|
\brief De-initialize Memory Card Interface.
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t Uninitialize (void) {
|
|
|
|
MCI.flags = 0U;
|
|
|
|
/* Unconfigure CMD, CK and D0 pins */
|
|
GPIO_PinConfigure(RTE_SDIO_CMD_PORT, RTE_SDIO_CMD_PIN, GPIO_IN_ANALOG, GPIO_MODE_INPUT);
|
|
GPIO_PinConfigure(RTE_SDIO_CK_PORT, RTE_SDIO_CK_PIN, GPIO_IN_ANALOG, GPIO_MODE_INPUT);
|
|
GPIO_PinConfigure(RTE_SDIO_D0_PORT, RTE_SDIO_D0_PIN, GPIO_IN_ANALOG, GPIO_MODE_INPUT);
|
|
|
|
#if (MCI_BUS_WIDTH_4)
|
|
/* Unconfigure D[1:3] */
|
|
GPIO_PinConfigure(RTE_SDIO_D1_PORT, RTE_SDIO_D1_PIN, GPIO_IN_ANALOG, GPIO_MODE_INPUT);
|
|
GPIO_PinConfigure(RTE_SDIO_D2_PORT, RTE_SDIO_D2_PIN, GPIO_IN_ANALOG, GPIO_MODE_INPUT);
|
|
GPIO_PinConfigure(RTE_SDIO_D3_PORT, RTE_SDIO_D3_PIN, GPIO_IN_ANALOG, GPIO_MODE_INPUT);
|
|
#endif
|
|
|
|
#if (MCI_BUS_WIDTH_8)
|
|
/* Unconfigure D[4:7] */
|
|
GPIO_PinConfigure(RTE_SDIO_D4_PORT, RTE_SDIO_D4_PIN, GPIO_IN_ANALOG, GPIO_MODE_INPUT);
|
|
GPIO_PinConfigure(RTE_SDIO_D5_PORT, RTE_SDIO_D5_PIN, GPIO_IN_ANALOG, GPIO_MODE_INPUT);
|
|
GPIO_PinConfigure(RTE_SDIO_D6_PORT, RTE_SDIO_D6_PIN, GPIO_IN_ANALOG, GPIO_MODE_INPUT);
|
|
GPIO_PinConfigure(RTE_SDIO_D7_PORT, RTE_SDIO_D7_PIN, GPIO_IN_ANALOG, GPIO_MODE_INPUT);
|
|
#endif
|
|
|
|
/* Unconfigure CD (Card Detect) Pin */
|
|
#if defined (MemoryCard_CD_Pin)
|
|
GPIO_PinConfigure (MemoryCard_CD_GPIOx, MemoryCard_CD_GPIO_Pin, GPIO_IN_ANALOG,
|
|
GPIO_MODE_INPUT);
|
|
#endif
|
|
/* Unconfigure WP (Write Protect) Pin */
|
|
#if defined (MemoryCard_WP_Pin)
|
|
GPIO_PinConfigure (MemoryCard_WP_GPIOx, MemoryCard_WP_GPIO_Pin, GPIO_IN_ANALOG,
|
|
GPIO_MODE_INPUT);
|
|
#endif
|
|
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
|
|
/**
|
|
\fn int32_t PowerControl (ARM_POWER_STATE state)
|
|
\brief Control Memory Card Interface Power.
|
|
\param[in] state Power state \ref ARM_POWER_STATE
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t PowerControl (ARM_POWER_STATE state) {
|
|
int32_t status;
|
|
|
|
status = ARM_DRIVER_OK;
|
|
|
|
switch (state) {
|
|
case ARM_POWER_OFF:
|
|
/* Disable SDIO interrupts in NVIC */
|
|
NVIC_DisableIRQ (SDIO_IRQn);
|
|
|
|
/* Disable DMA channel */
|
|
DMA_ChannelUninitialize(SDIO_DMA_Number, SDIO_DMA_Channel);
|
|
|
|
/* SDIO peripheral clock disable */
|
|
RCC->AHBENR &= ~RCC_AHBENR_SDIOEN;
|
|
|
|
/* Clear status */
|
|
MCI.status.command_active = 0U;
|
|
MCI.status.command_timeout = 0U;
|
|
MCI.status.command_error = 0U;
|
|
MCI.status.transfer_active = 0U;
|
|
MCI.status.transfer_timeout = 0U;
|
|
MCI.status.transfer_error = 0U;
|
|
MCI.status.sdio_interrupt = 0U;
|
|
MCI.status.ccs = 0U;
|
|
|
|
MCI.flags &= ~MCI_POWER;
|
|
break;
|
|
|
|
case ARM_POWER_FULL:
|
|
if ((MCI.flags & MCI_INIT) == 0U) {
|
|
return ARM_DRIVER_ERROR;
|
|
}
|
|
if ((MCI.flags & MCI_POWER) != 0U) {
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
/* Enable SDIO peripheral clock */
|
|
RCC->AHBENR |= RCC_AHBENR_SDIOEN;
|
|
|
|
/* Clear response and transfer variables */
|
|
MCI.response = NULL;
|
|
MCI.xfer.cnt = 0U;
|
|
|
|
/* Clear pending interrupts */
|
|
SDIO->ICR = SDIO_ICR_BIT_Msk;
|
|
|
|
/* Enable SDIO peripheral interrupts */
|
|
SDIO->MASK = SDIO_MASK_DATAENDIE |
|
|
SDIO_MASK_STBITERRIE |
|
|
SDIO_MASK_CMDSENTIE |
|
|
SDIO_MASK_CMDRENDIE |
|
|
SDIO_MASK_DTIMEOUTIE |
|
|
SDIO_MASK_CTIMEOUTIE |
|
|
SDIO_MASK_DCRCFAILIE |
|
|
SDIO_MASK_CCRCFAILIE ;
|
|
|
|
/* Set max data timeout */
|
|
SDIO->DTIMER = 0xFFFFFFFF;
|
|
|
|
/* Enable clock to the card (SDIO_CK) */
|
|
SDIO->POWER = SDIO_POWER_PWRCTRL_1 | SDIO_POWER_PWRCTRL_0;
|
|
|
|
/* Enable DMA channel */
|
|
DMA_ChannelInitialize (SDIO_DMA_Number, SDIO_DMA_Channel);
|
|
|
|
/* Enable SDIO interrupts in NVIC */
|
|
NVIC_ClearPendingIRQ(SDIO_IRQn);
|
|
NVIC_EnableIRQ(SDIO_IRQn);
|
|
|
|
MCI.flags |= MCI_POWER;
|
|
break;
|
|
|
|
case ARM_POWER_LOW:
|
|
default:
|
|
return ARM_DRIVER_ERROR_UNSUPPORTED;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
/**
|
|
\fn int32_t CardPower (uint32_t voltage)
|
|
\brief Set Memory Card supply voltage.
|
|
\param[in] voltage Memory Card supply voltage
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t CardPower (uint32_t voltage) {
|
|
|
|
if ((MCI.flags & MCI_POWER) == 0U) { return ARM_DRIVER_ERROR; }
|
|
return ARM_DRIVER_ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
|
|
/**
|
|
\fn int32_t ReadCD (void)
|
|
\brief Read Card Detect (CD) state.
|
|
\return 1:card detected, 0:card not detected, or error
|
|
*/
|
|
static int32_t ReadCD (void) {
|
|
|
|
if ((MCI.flags & MCI_POWER) == 0U) { return ARM_DRIVER_ERROR; }
|
|
|
|
/* Read CD (Card Detect) Pin */
|
|
#if defined (MemoryCard_CD_Pin)
|
|
if (GPIO_PinRead(MemoryCard_CD_GPIOx, MemoryCard_CD_GPIO_Pin) == MemoryCard_CD_Pin_Active) {
|
|
/* Card Detect switch is active */
|
|
return (1);
|
|
}
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
|
|
/**
|
|
\fn int32_t ReadWP (void)
|
|
\brief Read Write Protect (WP) state.
|
|
\return 1:write protected, 0:not write protected, or error
|
|
*/
|
|
static int32_t ReadWP (void) {
|
|
|
|
if ((MCI.flags & MCI_POWER) == 0U) { return ARM_DRIVER_ERROR; }
|
|
|
|
/* Read WP (Write Protect) Pin */
|
|
#if defined (MemoryCard_WP_Pin)
|
|
if (GPIO_PinRead(MemoryCard_WP_GPIOx, MemoryCard_WP_GPIO_Pin) == MemoryCard_WP_Pin_Active) {
|
|
/* Write protect switch is active */
|
|
return (1);
|
|
}
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
|
|
/**
|
|
\fn int32_t SendCommand (uint32_t cmd,
|
|
uint32_t arg,
|
|
uint32_t flags,
|
|
uint32_t *response)
|
|
\brief Send Command to card and get the response.
|
|
\param[in] cmd Memory Card command
|
|
\param[in] arg Command argument
|
|
\param[in] flags Command flags
|
|
\param[out] response Pointer to buffer for response
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t SendCommand (uint32_t cmd, uint32_t arg, uint32_t flags, uint32_t *response) {
|
|
uint32_t i, clkcr;
|
|
|
|
if (((flags & MCI_RESPONSE_EXPECTED_Msk) != 0U) && (response == NULL)) {
|
|
return ARM_DRIVER_ERROR_PARAMETER;
|
|
}
|
|
if ((MCI.flags & MCI_SETUP) == 0U) {
|
|
return ARM_DRIVER_ERROR;
|
|
}
|
|
if (MCI.status.command_active) {
|
|
return ARM_DRIVER_ERROR_BUSY;
|
|
}
|
|
MCI.status.command_active = 1U;
|
|
MCI.status.command_timeout = 0U;
|
|
MCI.status.command_error = 0U;
|
|
MCI.status.transfer_timeout = 0U;
|
|
MCI.status.transfer_error = 0U;
|
|
MCI.status.ccs = 0U;
|
|
|
|
if (flags & ARM_MCI_CARD_INITIALIZE) {
|
|
clkcr = SDIO->CLKCR;
|
|
|
|
if (((clkcr & SDIO_CLKCR_CLKEN) == 0) || ((clkcr & SDIO_CLKCR_PWRSAV) != 0)) {
|
|
SDIO->CLKCR = (SDIO->CLKCR & ~SDIO_CLKCR_PWRSAV) | SDIO_CLKCR_CLKEN;
|
|
|
|
i = SystemCoreClock;
|
|
for (i = (i/5000000U)*1000U; i; i--) {
|
|
; /* Wait for approximate 1000us */
|
|
}
|
|
SDIO->CLKCR = clkcr;
|
|
}
|
|
}
|
|
|
|
/* Set command register value */
|
|
cmd = SDIO_CMD_CPSMEN | (cmd & 0xFFU);
|
|
|
|
MCI.response = response;
|
|
MCI.flags &= ~(MCI_RESP_CRC | MCI_RESP_LONG);
|
|
|
|
switch (flags & ARM_MCI_RESPONSE_Msk) {
|
|
case ARM_MCI_RESPONSE_NONE:
|
|
/* No response expected (wait CMDSENT) */
|
|
break;
|
|
|
|
case ARM_MCI_RESPONSE_SHORT:
|
|
case ARM_MCI_RESPONSE_SHORT_BUSY:
|
|
/* Short response expected (wait CMDREND or CCRCFAIL) */
|
|
cmd |= SDIO_CMD_WAITRESP_0;
|
|
break;
|
|
|
|
case ARM_MCI_RESPONSE_LONG:
|
|
MCI.flags |= MCI_RESP_LONG;
|
|
/* Long response expected (wait CMDREND or CCRCFAIL) */
|
|
cmd |= SDIO_CMD_WAITRESP_1 | SDIO_CMD_WAITRESP_0;
|
|
break;
|
|
|
|
default:
|
|
return ARM_DRIVER_ERROR;
|
|
}
|
|
if (flags & ARM_MCI_RESPONSE_CRC) {
|
|
MCI.flags |= MCI_RESP_CRC;
|
|
}
|
|
if (flags & ARM_MCI_TRANSFER_DATA) {
|
|
MCI.flags |= MCI_DATA_XFER;
|
|
}
|
|
|
|
/* Clear all interrupt flags */
|
|
SDIO->ICR = SDIO_ICR_BIT_Msk;
|
|
|
|
/* Send the command */
|
|
SDIO->ARG = arg;
|
|
SDIO->CMD = cmd;
|
|
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
|
|
/**
|
|
\fn int32_t SetupTransfer (uint8_t *data,
|
|
uint32_t block_count,
|
|
uint32_t block_size,
|
|
uint32_t mode)
|
|
\brief Setup read or write transfer operation.
|
|
\param[in,out] data Pointer to data block(s) to be written or read
|
|
\param[in] block_count Number of blocks
|
|
\param[in] block_size Size of a block in bytes
|
|
\param[in] mode Transfer mode
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t SetupTransfer (uint8_t *data, uint32_t block_count, uint32_t block_size, uint32_t mode) {
|
|
uint32_t sz, cnt, cfg, dctrl;
|
|
|
|
if ((data == NULL) || (block_count == 0U) || (block_size == 0U)) { return ARM_DRIVER_ERROR_PARAMETER; }
|
|
|
|
if ((MCI.flags & MCI_SETUP) == 0U) {
|
|
return ARM_DRIVER_ERROR;
|
|
}
|
|
if (MCI.status.transfer_active) {
|
|
return ARM_DRIVER_ERROR_BUSY;
|
|
}
|
|
|
|
MCI.xfer.buf = data;
|
|
MCI.xfer.cnt = block_count * block_size;
|
|
|
|
cnt = MCI.xfer.cnt;
|
|
if (cnt > 0xFFFFU) {
|
|
cnt = 0xFFFFU;
|
|
}
|
|
|
|
MCI.xfer.cnt -= cnt;
|
|
MCI.xfer.buf += cnt;
|
|
|
|
dctrl = 0U;
|
|
|
|
if ((mode & ARM_MCI_TRANSFER_WRITE) == 0) {
|
|
/* Direction: From card to controller */
|
|
MCI.flags |= MCI_DATA_READ;
|
|
dctrl |= SDIO_DCTRL_DTDIR;
|
|
}
|
|
else {
|
|
MCI.flags &= ~MCI_DATA_READ;
|
|
}
|
|
|
|
if (mode & ARM_MCI_TRANSFER_STREAM) {
|
|
/* Stream or SDIO multibyte data transfer enable */
|
|
dctrl |= SDIO_DCTRL_DTMODE;
|
|
}
|
|
|
|
/* Set data block size */
|
|
if (block_size == 512U) {
|
|
sz = 9U;
|
|
}
|
|
else {
|
|
if (block_size > 16384U) {
|
|
return ARM_DRIVER_ERROR_UNSUPPORTED;
|
|
}
|
|
for (sz = 0U; sz < 14U; sz++) {
|
|
if (block_size & (1UL << sz)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Configure and enable DMA channel */
|
|
cfg = DMA_PERIPHERAL_TO_MEMORY |
|
|
(SDIO_DMA_Priority << DMA_PRIORITY_POS) |
|
|
DMA_MEMORY_DATA_32BIT |
|
|
DMA_PERIPHERAL_DATA_32BIT |
|
|
DMA_MEMORY_INCREMENT |
|
|
DMA_TRANSFER_ERROR_INTERRUPT |
|
|
DMA_TRANSFER_COMPLETE_INTERRUPT ;
|
|
|
|
if (mode & ARM_MCI_TRANSFER_WRITE) {
|
|
cfg |= DMA_READ_MEMORY;
|
|
}
|
|
|
|
DMA_ChannelConfigure(SDIO_DMA_Instance, cfg, (uint32_t)&(SDIO->FIFO), (uint32_t)data, cnt/4);
|
|
DMA_ChannelEnable (SDIO_DMA_Instance);
|
|
|
|
MCI.dlen = cnt;
|
|
MCI.dctrl = dctrl | (sz << 4) | SDIO_DCTRL_DMAEN;
|
|
|
|
return (ARM_DRIVER_OK);
|
|
}
|
|
|
|
|
|
/**
|
|
\fn int32_t AbortTransfer (void)
|
|
\brief Abort current read/write data transfer.
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t AbortTransfer (void) {
|
|
int32_t status;
|
|
uint32_t mask;
|
|
|
|
if ((MCI.flags & MCI_SETUP) == 0U) { return ARM_DRIVER_ERROR; }
|
|
|
|
status = ARM_DRIVER_OK;
|
|
|
|
/* Disable SDIO interrupts */
|
|
mask = SDIO->MASK;
|
|
SDIO->MASK = 0U;
|
|
|
|
/* Disable DMA and clear data transfer bit */
|
|
SDIO->DCTRL &= ~(SDIO_DCTRL_DMAEN | SDIO_DCTRL_DTEN);
|
|
|
|
DMA_ChannelDisable (SDIO_DMA_Instance);
|
|
|
|
/* Clear SDIO FIFO */
|
|
while (SDIO->FIFOCNT) {
|
|
SDIO->FIFO;
|
|
}
|
|
|
|
MCI.status.command_active = 0U;
|
|
MCI.status.transfer_active = 0U;
|
|
MCI.status.sdio_interrupt = 0U;
|
|
MCI.status.ccs = 0U;
|
|
|
|
/* Clear pending SDIO interrupts */
|
|
SDIO->ICR = SDIO_ICR_BIT_Msk;
|
|
|
|
/* Enable SDIO interrupts */
|
|
SDIO->MASK = mask;
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/**
|
|
\fn int32_t Control (uint32_t control, uint32_t arg)
|
|
\brief Control MCI Interface.
|
|
\param[in] control Operation
|
|
\param[in] arg Argument of operation (optional)
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t Control (uint32_t control, uint32_t arg) {
|
|
uint32_t val, clkdiv, bps;
|
|
|
|
if ((MCI.flags & MCI_POWER) == 0U) { return ARM_DRIVER_ERROR; }
|
|
|
|
switch (control) {
|
|
case ARM_MCI_BUS_SPEED:
|
|
/* Determine clock divider and set bus speed */
|
|
bps = arg;
|
|
|
|
if ((bps < SDIOCLK) || (MCI_BUS_MODE_HS == 0U)) {
|
|
/* bps = SDIOCLK / (clkdiv + 2) */
|
|
clkdiv = (SDIOCLK + bps - 1U) / bps;
|
|
|
|
if (clkdiv < 2) { clkdiv = 0U; }
|
|
else { clkdiv -= 2U; }
|
|
|
|
if (clkdiv > SDIO_CLKCR_CLKDIV) {
|
|
clkdiv = SDIO_CLKCR_CLKDIV;
|
|
}
|
|
|
|
SDIO->CLKCR = (SDIO->CLKCR & ~(SDIO_CLKCR_CLKDIV | SDIO_CLKCR_BYPASS)) |
|
|
SDIO_CLKCR_CLKEN | clkdiv;
|
|
bps = SDIOCLK / (clkdiv + 2U);
|
|
}
|
|
else {
|
|
/* Max output clock is SDIOCLK */
|
|
SDIO->CLKCR |= SDIO_CLKCR_BYPASS | SDIO_CLKCR_CLKEN;
|
|
|
|
bps = SDIOCLK;
|
|
}
|
|
|
|
for (val = (SDIOCLK/5000000U)*20U; val; val--) {
|
|
; /* Wait a bit to get stable clock */
|
|
}
|
|
|
|
/* Bus speed configured */
|
|
MCI.flags |= MCI_SETUP;
|
|
return ((int32_t)bps);
|
|
|
|
case ARM_MCI_BUS_SPEED_MODE:
|
|
switch (arg) {
|
|
case ARM_MCI_BUS_DEFAULT_SPEED:
|
|
/* Speed mode up to 25MHz */
|
|
SDIO->CLKCR &= ~SDIO_CLKCR_NEGEDGE;
|
|
break;
|
|
case ARM_MCI_BUS_HIGH_SPEED:
|
|
/* Speed mode up to 50MHz */
|
|
/* Errata: configuration with the NEGEDGE bit set should not be used. */
|
|
break;
|
|
default: return ARM_DRIVER_ERROR_UNSUPPORTED;
|
|
}
|
|
break;
|
|
|
|
case ARM_MCI_BUS_CMD_MODE:
|
|
switch (arg) {
|
|
case ARM_MCI_BUS_CMD_OPEN_DRAIN:
|
|
/* Configure command line in open-drain mode */
|
|
GPIO_PinConfigure(RTE_SDIO_CMD_PORT, RTE_SDIO_CMD_PIN, GPIO_AF_OPENDRAIN,
|
|
GPIO_MODE_OUT50MHZ);
|
|
break;
|
|
case ARM_MCI_BUS_CMD_PUSH_PULL:
|
|
/* Configure command line in push-pull mode */
|
|
GPIO_PinConfigure(RTE_SDIO_CMD_PORT, RTE_SDIO_CMD_PIN, GPIO_AF_PUSHPULL,
|
|
GPIO_MODE_OUT50MHZ);
|
|
break;
|
|
default:
|
|
return ARM_DRIVER_ERROR_UNSUPPORTED;
|
|
}
|
|
break;
|
|
|
|
case ARM_MCI_BUS_DATA_WIDTH:
|
|
switch (arg) {
|
|
case ARM_MCI_BUS_DATA_WIDTH_1:
|
|
SDIO->CLKCR &= ~SDIO_CLKCR_WIDBUS;
|
|
break;
|
|
case ARM_MCI_BUS_DATA_WIDTH_4:
|
|
SDIO->CLKCR = (SDIO->CLKCR & ~SDIO_CLKCR_WIDBUS) | SDIO_CLKCR_WIDBUS_0;
|
|
break;
|
|
case ARM_MCI_BUS_DATA_WIDTH_8:
|
|
SDIO->CLKCR = (SDIO->CLKCR & ~SDIO_CLKCR_WIDBUS) | SDIO_CLKCR_WIDBUS_1;
|
|
break;
|
|
default:
|
|
return ARM_DRIVER_ERROR_UNSUPPORTED;
|
|
}
|
|
break;
|
|
|
|
case ARM_MCI_CONTROL_CLOCK_IDLE:
|
|
if (arg) {
|
|
/* Clock generation enabled when idle */
|
|
SDIO->CLKCR &= ~SDIO_CLKCR_PWRSAV;
|
|
}
|
|
else {
|
|
/* Clock generation disabled when idle */
|
|
SDIO->CLKCR |= SDIO_CLKCR_PWRSAV;
|
|
}
|
|
break;
|
|
|
|
case ARM_MCI_DATA_TIMEOUT:
|
|
SDIO->DTIMER = arg;
|
|
break;
|
|
|
|
case ARM_MCI_MONITOR_SDIO_INTERRUPT:
|
|
MCI.status.sdio_interrupt = 0U;
|
|
SDIO->MASK |= SDIO_MASK_SDIOITIE;
|
|
break;
|
|
|
|
case ARM_MCI_CONTROL_READ_WAIT:
|
|
if (arg) {
|
|
/* Assert read wait */
|
|
MCI.flags |= MCI_READ_WAIT;
|
|
}
|
|
else {
|
|
/* Clear read wait */
|
|
MCI.flags &= ~MCI_READ_WAIT;
|
|
SDIO->DCTRL &= ~SDIO_DCTRL_RWSTOP;
|
|
}
|
|
break;
|
|
|
|
default: return ARM_DRIVER_ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
|
|
/**
|
|
\fn ARM_MCI_STATUS GetStatus (void)
|
|
\brief Get MCI status.
|
|
\return MCI status \ref ARM_MCI_STATUS
|
|
*/
|
|
static ARM_MCI_STATUS GetStatus (void) {
|
|
return MCI.status;
|
|
}
|
|
|
|
|
|
/* SDIO IRQ Handler */
|
|
void SDIO_IRQHandler (void) {
|
|
uint32_t sta, icr, event, mask;
|
|
|
|
event = 0U;
|
|
icr = 0U;
|
|
|
|
/* Read SDIO interrupt status */
|
|
sta = SDIO->STA;
|
|
|
|
if (sta & SDIO_STA_ERR_BIT_Msk) {
|
|
/* Check error interrupts */
|
|
if (sta & SDIO_STA_CCRCFAIL) {
|
|
icr |= SDIO_ICR_CCRCFAILC;
|
|
/* Command response CRC check failed */
|
|
if (MCI.flags & MCI_RESP_CRC) {
|
|
MCI.status.command_error = 1U;
|
|
|
|
event |= ARM_MCI_EVENT_COMMAND_ERROR;
|
|
}
|
|
else {
|
|
/* Ignore CRC error and read the response */
|
|
sta |= SDIO_STA_CMDREND;
|
|
}
|
|
}
|
|
if (sta & SDIO_STA_DCRCFAIL) {
|
|
icr |= SDIO_ICR_DCRCFAILC;
|
|
/* Data block CRC check failed */
|
|
MCI.status.transfer_error = 1U;
|
|
|
|
event |= ARM_MCI_EVENT_TRANSFER_ERROR;
|
|
}
|
|
if (sta & SDIO_STA_CTIMEOUT) {
|
|
icr |= SDIO_ICR_CTIMEOUTC;
|
|
/* Command response timeout */
|
|
MCI.status.command_timeout = 1U;
|
|
|
|
event |= ARM_MCI_EVENT_COMMAND_TIMEOUT;
|
|
}
|
|
if (sta & SDIO_STA_DTIMEOUT) {
|
|
icr |= SDIO_ICR_DTIMEOUTC;
|
|
/* Data timeout */
|
|
MCI.status.transfer_timeout = 1U;
|
|
|
|
event |= ARM_MCI_EVENT_TRANSFER_TIMEOUT;
|
|
}
|
|
if (sta & SDIO_STA_STBITERR) {
|
|
icr |= SDIO_ICR_STBITERRC;
|
|
/* Start bit not detected on all data signals */
|
|
event |= ARM_MCI_EVENT_TRANSFER_ERROR;
|
|
}
|
|
}
|
|
|
|
if (sta & SDIO_STA_CMDREND) {
|
|
icr |= SDIO_ICR_CMDRENDC;
|
|
/* Command response received */
|
|
event |= ARM_MCI_EVENT_COMMAND_COMPLETE;
|
|
|
|
if (MCI.response) {
|
|
/* Read response registers */
|
|
if (MCI.flags & MCI_RESP_LONG) {
|
|
MCI.response[0] = SDIO->RESP4;
|
|
MCI.response[1] = SDIO->RESP3;
|
|
MCI.response[2] = SDIO->RESP2;
|
|
MCI.response[3] = SDIO->RESP1;
|
|
}
|
|
else {
|
|
MCI.response[0] = SDIO->RESP1;
|
|
}
|
|
}
|
|
if (MCI.flags & MCI_DATA_XFER) {
|
|
MCI.flags &= ~MCI_DATA_XFER;
|
|
|
|
if (MCI.flags & MCI_READ_WAIT) {
|
|
MCI.dctrl |= SDIO_DCTRL_RWSTART;
|
|
}
|
|
|
|
/* Start data transfer */
|
|
SDIO->DLEN = MCI.dlen;
|
|
SDIO->DCTRL = MCI.dctrl | SDIO_DCTRL_DTEN;
|
|
|
|
MCI.status.transfer_active = 1U;
|
|
}
|
|
}
|
|
if (sta & SDIO_STA_CMDSENT) {
|
|
icr |= SDIO_ICR_CMDSENTC;
|
|
/* Command sent (no response required) */
|
|
event |= ARM_MCI_EVENT_COMMAND_COMPLETE;
|
|
}
|
|
if (sta & SDIO_STA_DATAEND) {
|
|
icr |= SDIO_ICR_DATAENDC;
|
|
/* Data end (DCOUNT is zero) */
|
|
if ((MCI.flags & MCI_DATA_READ) == 0) {
|
|
/* Write transfer */
|
|
SDIO->MASK |= SDIO_MASK_DBCKENDIE;
|
|
}
|
|
}
|
|
if (sta & SDIO_STA_DBCKEND) {
|
|
icr |= SDIO_ICR_DBCKENDC;
|
|
/* Data block sent/received (CRC check passed) */
|
|
if ((MCI.flags & MCI_DATA_READ) == 0) {
|
|
/* Write transfer */
|
|
if (MCI.xfer.cnt == 0) {
|
|
event |= ARM_MCI_EVENT_TRANSFER_COMPLETE;
|
|
}
|
|
}
|
|
SDIO->MASK &= ~SDIO_MASK_DBCKENDIE;
|
|
}
|
|
if (sta & SDIO_STA_SDIOIT) {
|
|
icr |= SDIO_ICR_SDIOITC;
|
|
/* Disable interrupt (must be re-enabled using Control) */
|
|
SDIO->MASK &= SDIO_MASK_SDIOITIE;
|
|
|
|
event |= ARM_MCI_EVENT_SDIO_INTERRUPT;
|
|
}
|
|
|
|
/* Clear processed interrupts */
|
|
SDIO->ICR = icr;
|
|
|
|
if (event) {
|
|
/* Check for transfer events */
|
|
mask = ARM_MCI_EVENT_TRANSFER_ERROR |
|
|
ARM_MCI_EVENT_TRANSFER_TIMEOUT |
|
|
ARM_MCI_EVENT_TRANSFER_COMPLETE;
|
|
if (event & mask) {
|
|
MCI.status.transfer_active = 0U;
|
|
|
|
if (MCI.cb_event) {
|
|
if (event & ARM_MCI_EVENT_TRANSFER_ERROR) {
|
|
(MCI.cb_event)(ARM_MCI_EVENT_TRANSFER_ERROR);
|
|
}
|
|
else if (event & ARM_MCI_EVENT_TRANSFER_TIMEOUT) {
|
|
(MCI.cb_event)(ARM_MCI_EVENT_TRANSFER_TIMEOUT);
|
|
}
|
|
else {
|
|
(MCI.cb_event)(ARM_MCI_EVENT_TRANSFER_COMPLETE);
|
|
}
|
|
}
|
|
}
|
|
/* Check for command events */
|
|
mask = ARM_MCI_EVENT_COMMAND_ERROR |
|
|
ARM_MCI_EVENT_COMMAND_TIMEOUT |
|
|
ARM_MCI_EVENT_COMMAND_COMPLETE;
|
|
if (event & mask) {
|
|
MCI.status.command_active = 0U;
|
|
|
|
if (MCI.cb_event) {
|
|
if (event & ARM_MCI_EVENT_COMMAND_ERROR) {
|
|
(MCI.cb_event)(ARM_MCI_EVENT_COMMAND_ERROR);
|
|
}
|
|
else if (event & ARM_MCI_EVENT_COMMAND_TIMEOUT) {
|
|
(MCI.cb_event)(ARM_MCI_EVENT_COMMAND_TIMEOUT);
|
|
}
|
|
else {
|
|
(MCI.cb_event)(ARM_MCI_EVENT_COMMAND_COMPLETE);
|
|
}
|
|
}
|
|
}
|
|
/* Check for SDIO INT event */
|
|
if (event & ARM_MCI_EVENT_SDIO_INTERRUPT) {
|
|
MCI.status.sdio_interrupt = 1U;
|
|
|
|
if (MCI.cb_event) {
|
|
(MCI.cb_event)(ARM_MCI_EVENT_SDIO_INTERRUPT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* DMA event handler */
|
|
void SDIO_DMA_Handler (uint32_t event) {
|
|
uint32_t evt = 0;
|
|
|
|
/* Disable DMA channel */
|
|
DMA_ChannelDisable(SDIO_DMA_Instance);
|
|
|
|
if (event & DMA_TRANSFER_COMPLETE_INTERRUPT) {
|
|
if (MCI.flags & MCI_DATA_READ) {
|
|
evt = ARM_MCI_EVENT_TRANSFER_COMPLETE;
|
|
|
|
MCI.status.transfer_active = 0U;
|
|
}
|
|
}
|
|
|
|
if (event & DMA_CHANNEL_TRANSFER_ERROR) {
|
|
evt = ARM_MCI_EVENT_TRANSFER_COMPLETE;
|
|
}
|
|
|
|
if (evt && MCI.cb_event) {
|
|
(MCI.cb_event)(evt);
|
|
}
|
|
}
|
|
|
|
|
|
/* MCI Driver Control Block */
|
|
ARM_DRIVER_MCI Driver_MCI0 = {
|
|
GetVersion,
|
|
GetCapabilities,
|
|
Initialize,
|
|
Uninitialize,
|
|
PowerControl,
|
|
CardPower,
|
|
ReadCD,
|
|
ReadWP,
|
|
SendCommand,
|
|
SetupTransfer,
|
|
AbortTransfer,
|
|
Control,
|
|
GetStatus
|
|
};
|