2013-01-08 22:40:58 +08:00
|
|
|
/*
|
|
|
|
* @brief SD/SDIO (MCI) registers and control functions
|
|
|
|
*
|
|
|
|
* @note
|
|
|
|
* Copyright(C) NXP Semiconductors, 2012
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* @par
|
|
|
|
* Software that is described herein is for illustrative purposes only
|
|
|
|
* which provides customers with programming information regarding the
|
|
|
|
* LPC products. This software is supplied "AS IS" without any warranties of
|
|
|
|
* any kind, and NXP Semiconductors and its licensor disclaim any and
|
|
|
|
* all warranties, express or implied, including all implied warranties of
|
|
|
|
* merchantability, fitness for a particular purpose and non-infringement of
|
|
|
|
* intellectual property rights. NXP Semiconductors assumes no responsibility
|
|
|
|
* or liability for the use of the software, conveys no license or rights under any
|
|
|
|
* patent, copyright, mask work right, or any other intellectual property rights in
|
|
|
|
* or to any products. NXP Semiconductors reserves the right to make changes
|
|
|
|
* in the software without notification. NXP Semiconductors also makes no
|
|
|
|
* representation or warranty that such application will be suitable for the
|
|
|
|
* specified use without further testing or modification.
|
|
|
|
*
|
|
|
|
* @par
|
|
|
|
* Permission to use, copy, modify, and distribute this software and its
|
|
|
|
* documentation is hereby granted, under NXP Semiconductors' and its
|
|
|
|
* licensor's relevant copyrights in the software, without fee, provided that it
|
|
|
|
* is used in conjunction with NXP Semiconductors microcontrollers. This
|
|
|
|
* copyright, permission, and disclaimer notice must appear in all copies of
|
|
|
|
* this code.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "sdmmc_001.h"
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* Private types/enumerations/variables
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* Public types/enumerations/variables
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* Private functions
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* Public functions
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/* Initializes the MCI card controller */
|
|
|
|
void IP_SDMMC_Init(IP_SDMMC_001_Type *pSDMMC)
|
|
|
|
{
|
|
|
|
/* Software reset */
|
|
|
|
pSDMMC->BMOD = MCI_BMOD_SWR;
|
|
|
|
|
|
|
|
/* reset all blocks */
|
|
|
|
pSDMMC->CTRL = MCI_CTRL_RESET | MCI_CTRL_FIFO_RESET | MCI_CTRL_DMA_RESET;
|
|
|
|
while (pSDMMC->CTRL & (MCI_CTRL_RESET | MCI_CTRL_FIFO_RESET | MCI_CTRL_DMA_RESET)) {}
|
|
|
|
|
|
|
|
/* Internal DMA setup for control register */
|
|
|
|
pSDMMC->CTRL = MCI_CTRL_USE_INT_DMAC | MCI_CTRL_INT_ENABLE;
|
|
|
|
pSDMMC->INTMASK = 0;
|
|
|
|
|
|
|
|
/* Clear the interrupts for the host controller */
|
|
|
|
pSDMMC->RINTSTS = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
/* Put in max timeout */
|
|
|
|
pSDMMC->TMOUT = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
/* FIFO threshold settings for DMA, DMA burst of 4, FIFO watermark at 16 */
|
|
|
|
pSDMMC->FIFOTH = MCI_FIFOTH_DMA_MTS_4 | MCI_FIFOTH_RX_WM((SD_FIFO_SZ / 2) - 1) | MCI_FIFOTH_TX_WM(SD_FIFO_SZ / 2);
|
|
|
|
|
|
|
|
/* Enable internal DMA, burst size of 4, fixed burst */
|
|
|
|
pSDMMC->BMOD = MCI_BMOD_DE | MCI_BMOD_PBL4 | MCI_BMOD_DSL(4);
|
|
|
|
|
|
|
|
/* disable clock to CIU (needs latch) */
|
|
|
|
pSDMMC->CLKENA = 0;
|
|
|
|
pSDMMC->CLKSRC = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Close the MCI */
|
|
|
|
void IP_SDMMC_DeInit(IP_SDMMC_001_Type *pSDMMC)
|
|
|
|
{}
|
|
|
|
|
|
|
|
/* Set block size for transfer */
|
|
|
|
void IP_SDMMC_SetBlkSize(IP_SDMMC_001_Type *pSDMMC, uint32_t bytes)
|
|
|
|
{
|
|
|
|
pSDMMC->BLKSIZ = bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reset card in slot */
|
|
|
|
void IP_SDMMC_Reset(IP_SDMMC_001_Type *pSDMMC, int32_t reset)
|
|
|
|
{
|
|
|
|
if (reset) {
|
|
|
|
pSDMMC->RST_N = 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
pSDMMC->RST_N = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Enable or disable slot power */
|
|
|
|
void IP_SDMMC_PowerOnOff(IP_SDMMC_001_Type *pSDMMC, int32_t enable)
|
|
|
|
{
|
|
|
|
if (enable) {
|
|
|
|
pSDMMC->PWREN = 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
pSDMMC->PWREN = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Detect if write protect is enabled */
|
|
|
|
int32_t IP_SDMMC_CardWpOn(IP_SDMMC_001_Type *pSDMMC)
|
|
|
|
{
|
|
|
|
if (pSDMMC->WRTPRT & 1) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Detect if an SD card is inserted */
|
|
|
|
int32_t IP_SDMMC_CardNDetect(IP_SDMMC_001_Type *pSDMMC)
|
|
|
|
{
|
|
|
|
/* No card = high state in regsiter */
|
|
|
|
if (pSDMMC->CDETECT & 1) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Function to send command to Card interface unit (CIU) */
|
|
|
|
int32_t IP_SDMMC_SendCmd(IP_SDMMC_001_Type *pSDMMC, uint32_t cmd, uint32_t arg)
|
|
|
|
{
|
|
|
|
volatile int32_t tmo = 50;
|
|
|
|
volatile int delay;
|
|
|
|
|
|
|
|
/* set command arg reg*/
|
|
|
|
pSDMMC->CMDARG = arg;
|
|
|
|
pSDMMC->CMD = MCI_CMD_START | cmd;
|
|
|
|
|
|
|
|
/* poll untill command is accepted by the CIU */
|
|
|
|
while (--tmo && (pSDMMC->CMD & MCI_CMD_START)) {
|
|
|
|
if (tmo & 1) {
|
|
|
|
delay = 50;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
delay = 18000;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (--delay > 1) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (tmo < 1) ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read the response from the last command */
|
|
|
|
void IP_SDMMC_GetResponse(IP_SDMMC_001_Type *pSDMMC, uint32_t *resp)
|
|
|
|
{
|
|
|
|
/* on this chip response is not a fifo so read all 4 regs */
|
|
|
|
resp[0] = pSDMMC->RESP0;
|
|
|
|
resp[1] = pSDMMC->RESP1;
|
|
|
|
resp[2] = pSDMMC->RESP2;
|
|
|
|
resp[3] = pSDMMC->RESP3;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Sets the SD bus clock speed */
|
|
|
|
void IP_SDMMC_SetClock(IP_SDMMC_001_Type *pSDMMC, uint32_t clk_rate, uint32_t speed)
|
|
|
|
{
|
|
|
|
/* compute SD/MMC clock dividers */
|
|
|
|
uint32_t div;
|
|
|
|
|
|
|
|
div = ((clk_rate / speed) + 2) >> 1;
|
|
|
|
|
|
|
|
if ((div == pSDMMC->CLKDIV) && pSDMMC->CLKENA) {
|
|
|
|
return; /* Closest speed is already set */
|
|
|
|
|
|
|
|
}
|
|
|
|
/* disable clock */
|
|
|
|
pSDMMC->CLKENA = 0;
|
|
|
|
|
|
|
|
/* User divider 0 */
|
|
|
|
pSDMMC->CLKSRC = MCI_CLKSRC_CLKDIV0;
|
|
|
|
|
|
|
|
/* inform CIU */
|
|
|
|
IP_SDMMC_SendCmd(pSDMMC, MCI_CMD_UPD_CLK | MCI_CMD_PRV_DAT_WAIT, 0);
|
|
|
|
|
|
|
|
/* set divider 0 to desired value */
|
|
|
|
pSDMMC->CLKDIV = MCI_CLOCK_DIVIDER(0, div);
|
|
|
|
|
|
|
|
/* inform CIU */
|
|
|
|
IP_SDMMC_SendCmd(pSDMMC, MCI_CMD_UPD_CLK | MCI_CMD_PRV_DAT_WAIT, 0);
|
|
|
|
|
|
|
|
/* enable clock */
|
|
|
|
pSDMMC->CLKENA = MCI_CLKEN_ENABLE;
|
|
|
|
|
|
|
|
/* inform CIU */
|
|
|
|
IP_SDMMC_SendCmd(pSDMMC, MCI_CMD_UPD_CLK | MCI_CMD_PRV_DAT_WAIT, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Function to set card type */
|
|
|
|
void IP_SDMMC_SetCardType(IP_SDMMC_001_Type *pSDMMC, uint32_t ctype)
|
|
|
|
{
|
|
|
|
pSDMMC->CTYPE = ctype;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Function to clear interrupt & FIFOs */
|
|
|
|
void IP_SDMMC_SetClearIntFifo(IP_SDMMC_001_Type *pSDMMC)
|
|
|
|
{
|
|
|
|
/* reset all blocks */
|
|
|
|
pSDMMC->CTRL |= MCI_CTRL_FIFO_RESET;
|
|
|
|
|
|
|
|
/* wait till resets clear */
|
|
|
|
while (pSDMMC->CTRL & MCI_CTRL_FIFO_RESET) {}
|
|
|
|
|
|
|
|
/* Clear interrupt status */
|
|
|
|
pSDMMC->RINTSTS = 0xFFFFFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns the raw SD interface interrupt status */
|
|
|
|
uint32_t IP_SDMMC_GetRawIntStatus(IP_SDMMC_001_Type *pSDMMC)
|
|
|
|
{
|
|
|
|
return pSDMMC->RINTSTS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Sets the raw SD interface interrupt status */
|
|
|
|
void IP_SDMMC_SetRawIntStatus(IP_SDMMC_001_Type *pSDMMC, uint32_t iVal)
|
|
|
|
{
|
|
|
|
pSDMMC->RINTSTS = iVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Sets the SD interface interrupt mask */
|
|
|
|
void IP_SDMMC_SetIntMask(IP_SDMMC_001_Type *pSDMMC, uint32_t iVal)
|
|
|
|
{
|
|
|
|
pSDMMC->INTMASK = iVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Setup DMA descriptors */
|
|
|
|
void IP_SDMMC_DmaSetup(IP_SDMMC_001_Type *pSDMMC, sdif_device *psdif_dev, uint32_t addr, uint32_t size)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
uint32_t ctrl, maxs;
|
|
|
|
|
|
|
|
/* Reset DMA */
|
|
|
|
pSDMMC->CTRL |= MCI_CTRL_DMA_RESET | MCI_CTRL_FIFO_RESET;
|
|
|
|
while (pSDMMC->CTRL & MCI_CTRL_DMA_RESET) {}
|
|
|
|
|
|
|
|
/* Build a descriptor list using the chained DMA method */
|
|
|
|
while (size > 0) {
|
|
|
|
/* Limit size of the transfer to maximum buffer size */
|
|
|
|
maxs = size;
|
|
|
|
if (maxs > MCI_DMADES1_MAXTR) {
|
|
|
|
maxs = MCI_DMADES1_MAXTR;
|
|
|
|
}
|
|
|
|
size -= maxs;
|
|
|
|
|
|
|
|
/* Set buffer size */
|
|
|
|
psdif_dev->mci_dma_dd[i].des1 = MCI_DMADES1_BS1(maxs);
|
|
|
|
|
|
|
|
/* Setup buffer address (chained) */
|
|
|
|
psdif_dev->mci_dma_dd[i].des2 = addr + (i * MCI_DMADES1_MAXTR);
|
|
|
|
|
|
|
|
/* Setup basic control */
|
|
|
|
ctrl = MCI_DMADES0_OWN | MCI_DMADES0_CH;
|
|
|
|
if (i == 0) {
|
|
|
|
ctrl |= MCI_DMADES0_FS; /* First DMA buffer */
|
|
|
|
|
|
|
|
}
|
|
|
|
/* No more data? Then this is the last descriptor */
|
|
|
|
if (!size) {
|
|
|
|
ctrl |= MCI_DMADES0_LD;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ctrl |= MCI_DMADES0_DIC;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Another descriptor is needed */
|
|
|
|
psdif_dev->mci_dma_dd[i].des3 = (uint32_t) &psdif_dev->mci_dma_dd[i + 1];
|
|
|
|
psdif_dev->mci_dma_dd[i].des0 = ctrl;
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set DMA derscriptor base address */
|
|
|
|
pSDMMC->DBADDR = (uint32_t) &psdif_dev->mci_dma_dd[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Sets the transfer block size
|
|
|
|
* @param pSDMMC : Pointer to IP_SDMMC_001_Type structure
|
|
|
|
* @param blk_size : Block Size value
|
|
|
|
* @return None
|
|
|
|
*/
|
|
|
|
void IP_SDMMC_SetBlockSize(IP_SDMMC_001_Type *pSDMMC, uint32_t blk_size)
|
|
|
|
{
|
|
|
|
/* set block size and byte count */
|
|
|
|
pSDMMC->BLKSIZ = blk_size;
|
|
|
|
pSDMMC->BYTCNT = blk_size;
|
|
|
|
}
|