1221 lines
35 KiB
C
1221 lines
35 KiB
C
/****************************************************************************************************//**
|
||
* @file sdio.c
|
||
*
|
||
* @status EXPERIMENTAL
|
||
*
|
||
* @brief LPC18xx_43xx SD/MMC/SDIO controller driver
|
||
*
|
||
* @version V1.0
|
||
* @date 02. November 2011
|
||
*
|
||
* Software that is described herein is for illustrative purposes only
|
||
* which provides customers with programming information regarding the
|
||
* products. This software is supplied "AS IS" without any warranties.
|
||
* NXP Semiconductors assumes no responsibility or liability for the
|
||
* use of the software, conveys no license or title under any patent,
|
||
* copyright, or mask work right to the product. NXP Semiconductors
|
||
* reserves the right to make changes in the software without
|
||
* notification. NXP Semiconductors also make no representation or
|
||
* warranty that such application will be suitable for the specified
|
||
* use without further testing or modification.
|
||
* Permission to use, copy, modify, and distribute this software and its
|
||
* documentation is hereby granted, under NXP Semiconductors’
|
||
* relevant copyright 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 <string.h>
|
||
#include "LPC43xx.h" /* LPC18xx definitions */
|
||
#include "system_LPC43xx.h"
|
||
#include "lpc_sdmmc.h"
|
||
#include "sdio.h"
|
||
|
||
/* Global instance of the current card */
|
||
static MCI_CARD_INFO_T g_card_info;
|
||
|
||
#ifdef USE_DMADESC_DBUFF
|
||
/* Array of DMA descriptors used for double buffered mode. Must be enough to
|
||
transfer up to 64KBytes in a single DMA session. */
|
||
static LPC_SDMMC_DMA_Type mci_dma_dd[1 + (0x10000 / (2 * MCI_DMADES1_MAXTR))];
|
||
#else
|
||
/* Array of DMA descriptors used for chained mode. Must be enough to transfer
|
||
up to 64KBytes in a single DMA session. */
|
||
static LPC_SDMMC_DMA_Type mci_dma_dd[1 + (0x10000 / MCI_DMADES1_MAXTR)];
|
||
#endif
|
||
|
||
/* This must be defined outside the driver and is the rate of the base
|
||
clock going into the SDIO IP */
|
||
uint32_t sdio_clk_rate;
|
||
|
||
extern void timer_wait_us(void *t, int us);
|
||
extern void timer_wait_ms(void *t, int ms);
|
||
|
||
int sdio_execute_command(MCI_CARD_INFO_T* pdev,
|
||
uint32_t cmd,
|
||
uint32_t arg,
|
||
uint32_t wait_status);
|
||
|
||
/***********************************************************************
|
||
* MCI driver private functions
|
||
**********************************************************************/
|
||
|
||
/*****************************************************************************
|
||
** Function name: sdio_enable_clock
|
||
**
|
||
** Descriptions: Enables the SDIO controller clock
|
||
**
|
||
** parameters: None
|
||
**
|
||
** Returned value: None
|
||
**
|
||
*****************************************************************************/
|
||
static void sdio_enable_clock(void)
|
||
{
|
||
/* Enable SD MMC clock */
|
||
LPC_CCU1->CLK_M4_SDIO_CFG = 1;
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: sdio_disable_clock
|
||
**
|
||
** Descriptions: Disables the SDIO controller clock
|
||
**
|
||
** parameters: None
|
||
**
|
||
** Returned value: None
|
||
**
|
||
*****************************************************************************/
|
||
static void sdio_disable_clock(void)
|
||
{
|
||
/* Disable SD MMC clock */
|
||
LPC_CCU1->CLK_M4_SDIO_CFG = 0;
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: sdio_disable_clock
|
||
**
|
||
** Descriptions: Return clock running status
|
||
**
|
||
** parameters: None
|
||
**
|
||
** Returned value: !0 if the clock is enabled, otherwise 0
|
||
**
|
||
*****************************************************************************/
|
||
int sdio_is_clock_enabled(void)
|
||
{
|
||
return (LPC_CCU1->CLK_M4_SDIO_CFG & 0x1);
|
||
}
|
||
|
||
/**********************************************************************
|
||
** Function name: wait_for_program_finish
|
||
**
|
||
** Description: Wait for card program to finish
|
||
**
|
||
** Parameters: pdev : None
|
||
**
|
||
** Returned value: None
|
||
**********************************************************************/
|
||
static void wait_for_program_finish(void)
|
||
{
|
||
while (sdio_get_state() == SDMMC_PRG_ST);
|
||
while (sdio_get_state() != SDMMC_TRAN_ST);
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: sdio_dma_setup
|
||
**
|
||
** Descriptions: Setup DMA descriptors
|
||
**
|
||
** parameters: addr : Address of buffer (source or destination)
|
||
** size: size of buffer in bytes (64K max)
|
||
**
|
||
** Returned value: None
|
||
**
|
||
*****************************************************************************/
|
||
void sdio_dma_setup(uint32_t addr, uint32_t size)
|
||
{
|
||
int i = 0;
|
||
uint32_t ctrl, maxs;
|
||
#ifdef USE_DMADESC_DBUFF
|
||
uint32_t maxs2;
|
||
#endif
|
||
|
||
/* Reset DMA */
|
||
LPC_SDMMC->CTRL |= MCI_CTRL_DMA_RESET | MCI_CTRL_FIFO_RESET;
|
||
while (LPC_SDMMC->CTRL & MCI_CTRL_DMA_RESET);
|
||
|
||
#ifdef USE_DMADESC_DBUFF
|
||
/* Build a descriptor list using the double buffered DMA method */
|
||
while (size > 0) {
|
||
/* Limit size of the transfer to maximum buffer size * 2*/
|
||
maxs = size;
|
||
if (maxs > (MCI_DMADES1_MAXTR * 2))
|
||
maxs = (MCI_DMADES1_MAXTR * 2);
|
||
size -= maxs;
|
||
|
||
/* Setup buffer sizes */
|
||
if (maxs > MCI_DMADES1_MAXTR) {
|
||
maxs2 = maxs - MCI_DMADES1_MAXTR;
|
||
maxs = MCI_DMADES1_MAXTR;
|
||
}
|
||
else
|
||
maxs2 = 0;
|
||
|
||
/* Set buffer sizes */
|
||
mci_dma_dd[i].des1 = MCI_DMADES1_BS1(maxs) | MCI_DMADES1_BS2(maxs2);
|
||
|
||
/* Setup buffer address (double buffered) */
|
||
mci_dma_dd[i].des2 = addr + (i * (MCI_DMADES1_MAXTR * 2));
|
||
mci_dma_dd[i].des3 = mci_dma_dd[i].des2 + maxs;
|
||
|
||
/* Setup basic control */
|
||
ctrl = MCI_DMADES0_OWN; // | MCI_DMADES0_DIC;
|
||
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;
|
||
|
||
mci_dma_dd[i].des0 = ctrl;
|
||
|
||
i++;
|
||
}
|
||
|
||
mci_dma_dd[i].des0 = MCI_DMADES0_OWN | MCI_DMADES0_LD;
|
||
mci_dma_dd[i].des1 = 0;
|
||
mci_dma_dd[i].des2 = 0;
|
||
mci_dma_dd[i].des3 = 0;
|
||
|
||
#else
|
||
/* Build a descriptor list using the chained DMA method */
|
||
i = 0;
|
||
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 */
|
||
mci_dma_dd[i].des1 = MCI_DMADES1_BS1(maxs);
|
||
|
||
/* Setup buffer address (chained) */
|
||
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 */
|
||
mci_dma_dd[i].des3 = (uint32_t) &mci_dma_dd[i + 1];
|
||
mci_dma_dd[i].des0 = ctrl;
|
||
|
||
i++;
|
||
}
|
||
#endif
|
||
|
||
/* Set DMA derscriptor base address */
|
||
LPC_SDMMC->DBADDR = (uint32_t) &mci_dma_dd[0];
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: prv_card_acquired
|
||
**
|
||
** Descriptions: Checks whether card is acquired properly or not
|
||
**
|
||
** parameters: pdev: Pointer to card info structure
|
||
**
|
||
** Returned value: !0 if card has been acquired, otherwise 0
|
||
**
|
||
*****************************************************************************/
|
||
static int prv_card_acquired(MCI_CARD_INFO_T* pdev)
|
||
{
|
||
return (pdev->cid[0] != 0);
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: prv_get_bits
|
||
**
|
||
** Descriptions: Helper function to get a bit field withing multi-word
|
||
** buffer. Used to get fields with-in CSD & EXT-CSD
|
||
** structures.
|
||
**
|
||
** parameters: start: Start position of the bit field
|
||
** end : Start position of the bit field
|
||
** data : Pointer to buffer
|
||
**
|
||
** Returned value: The bit field value of the selected range
|
||
**
|
||
*****************************************************************************/
|
||
static uint32_t prv_get_bits(int start, int end, uint32_t* data)
|
||
{
|
||
uint32_t v;
|
||
uint32_t i = end >> 5;
|
||
uint32_t j = start & 0x1f;
|
||
|
||
if (i == (start >> 5))
|
||
v = (data[i] >> j);
|
||
else
|
||
v = ((data[i] << (32 - j)) | (data[start >> 5] >> j));
|
||
|
||
return (v & ((1 << (end - start + 1)) - 1));
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: prv_clear_all
|
||
**
|
||
** Descriptions: Clears the FIFOs, response and data, and the interrupt
|
||
** status
|
||
**
|
||
** parameters: None
|
||
**
|
||
** Returned value: None
|
||
**
|
||
*****************************************************************************/
|
||
static void prv_clear_all(void)
|
||
{
|
||
/* reset all blocks */
|
||
LPC_SDMMC->CTRL |= MCI_CTRL_FIFO_RESET;
|
||
|
||
/* wait till resets clear */
|
||
while (LPC_SDMMC->CTRL & MCI_CTRL_FIFO_RESET);
|
||
|
||
/* Clear interrupt status */
|
||
LPC_SDMMC->RINTSTS = 0xFFFFFFFF;
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: prv_send_cmd
|
||
**
|
||
** Descriptions: Function to send command to Card interface unit (CIU)
|
||
**
|
||
** parameters: cmd : Command with all flags set.
|
||
** arg : Argument for the command
|
||
**
|
||
** Returned value: TRUE on times-out, otherwise FALSE
|
||
**
|
||
*****************************************************************************/
|
||
static int prv_send_cmd(uint32_t cmd, uint32_t arg)
|
||
{
|
||
volatile int tmo = 50;
|
||
volatile int delay;
|
||
|
||
/* set command arg reg*/
|
||
LPC_SDMMC->CMDARG = arg;
|
||
LPC_SDMMC->CMD = MCI_CMD_START | cmd;
|
||
|
||
/* poll untill command is accepted by the CIU */
|
||
while (--tmo && (LPC_SDMMC->CMD & MCI_CMD_START))
|
||
{
|
||
if (tmo & 1)
|
||
delay = 50;
|
||
else
|
||
delay = 18000;
|
||
|
||
while (--delay > 1);
|
||
}
|
||
|
||
return (tmo < 1) ? 1 : 0;
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: prv_set_clock
|
||
**
|
||
** Descriptions: Function to set speed of the clock going to card
|
||
**
|
||
** parameters: speed : Clock speed
|
||
**
|
||
** Returned value: TRUE on times-out, otherwise FALSE
|
||
**
|
||
*****************************************************************************/
|
||
static void prv_set_clock(uint32_t speed)
|
||
{
|
||
/* compute SD/MMC clock dividers */
|
||
uint32_t div;
|
||
|
||
div = ((sdio_clk_rate / speed) + 2) >> 1;
|
||
|
||
if ((div == LPC_SDMMC->CLKDIV) && LPC_SDMMC->CLKENA)
|
||
return; /* requested speed is already set */
|
||
|
||
/* disable clock */
|
||
LPC_SDMMC->CLKENA = 0;
|
||
|
||
/* User divider 0 */
|
||
LPC_SDMMC->CLKSRC = MCI_CLKSRC_CLKDIV0;
|
||
|
||
/* inform CIU */
|
||
prv_send_cmd(MCI_CMD_UPD_CLK | MCI_CMD_PRV_DAT_WAIT, 0);
|
||
|
||
/* set divider 0 to desired value */
|
||
LPC_SDMMC->CLKDIV = MCI_CLOCK_DIVIDER(0, div);
|
||
/* inform CIU */
|
||
prv_send_cmd(MCI_CMD_UPD_CLK | MCI_CMD_PRV_DAT_WAIT, 0);
|
||
|
||
/* enable clock */
|
||
LPC_SDMMC->CLKENA = MCI_CLKEN_ENABLE(0);
|
||
/* inform CIU */
|
||
prv_send_cmd(MCI_CMD_UPD_CLK | MCI_CMD_PRV_DAT_WAIT, 0);
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: prv_pull_response
|
||
**
|
||
** Descriptions: Function to retrieve command response
|
||
**
|
||
** parameters: pdev: Pointer to card info structure
|
||
** length : the length of the expected response, in bits
|
||
**
|
||
** Returned value: None
|
||
**
|
||
*****************************************************************************/
|
||
static void prv_pull_response(MCI_CARD_INFO_T* pdev, int length)
|
||
{
|
||
/* on this chip response is not a fifo so read all 4 regs */
|
||
pdev->response[0] = LPC_SDMMC->RESP0;
|
||
pdev->response[1] = LPC_SDMMC->RESP1;
|
||
pdev->response[2] = LPC_SDMMC->RESP2;
|
||
pdev->response[3] = LPC_SDMMC->RESP3;
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: prv_wait_for_completion
|
||
**
|
||
** Descriptions: Polls for command completion
|
||
**
|
||
** parameters: pdev : Pointer to card info structure
|
||
** bit : Status bits to poll for command completion.
|
||
**
|
||
** Returned value: 0 on success, or failure condition (-1)
|
||
**
|
||
*****************************************************************************/
|
||
static uint32_t prv_wait_for_completion(MCI_CARD_INFO_T* pdev, uint32_t bits)
|
||
{
|
||
uint32_t status = 0;
|
||
int tmo_count = US_TIMEOUT;
|
||
|
||
/* also check error conditions */
|
||
bits |= MCI_INT_EBE | MCI_INT_SBE | MCI_INT_HLE
|
||
| MCI_INT_RTO | MCI_INT_RCRC | MCI_INT_RESP_ERR;
|
||
|
||
if (bits & MCI_INT_DATA_OVER)
|
||
bits |= MCI_INT_FRUN | MCI_INT_HTO | MCI_INT_DTO
|
||
| MCI_INT_DCRC;
|
||
|
||
if (pdev->wait_func == 0)
|
||
{
|
||
/* do busy polling when wait_func is not set*/
|
||
do
|
||
{
|
||
timer_wait_us(0, 1);
|
||
status = LPC_SDMMC->RINTSTS;
|
||
|
||
if (--tmo_count < 1)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
while ((status & bits) == 0);
|
||
/* set time out flag for driver timeout also */
|
||
status |= ((tmo_count < 1) ? MCI_INT_RTO : 0);
|
||
}
|
||
else
|
||
{
|
||
/* call wait function set by application */
|
||
status = pdev->wait_func(pdev, bits);
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: prv_process_csd
|
||
**
|
||
** Descriptions: Function to process the CSD & EXT-CSD of the card
|
||
**
|
||
** parameters: pdev: Pointer to card info structure
|
||
**
|
||
** Returned value: None
|
||
**
|
||
*****************************************************************************/
|
||
static void prv_process_csd(MCI_CARD_INFO_T* pdev)
|
||
{
|
||
int status = 0;
|
||
int c_size = 0;
|
||
int c_size_mult = 0;
|
||
int mult = 0;
|
||
|
||
/* compute block length based on CSD response */
|
||
pdev->block_len = 1 << prv_get_bits(80, 83, pdev->csd);
|
||
|
||
if ((pdev->card_type & CARD_TYPE_HC) &&
|
||
(pdev->card_type & CARD_TYPE_SD))
|
||
{
|
||
/* See section 5.3.3 CSD Register (CSD Version 2.0) of SD2.0 spec
|
||
an explanation for the calculation of these values
|
||
*/
|
||
c_size = prv_get_bits(48, 63, (uint32_t*)pdev->csd) + 1;
|
||
pdev->blocknr = c_size << 10; /* 512 byte blocks */
|
||
}
|
||
else
|
||
{
|
||
/* See section 5.3 of the 4.1 revision of the MMC specs for
|
||
an explanation for the calculation of these values
|
||
*/
|
||
c_size = prv_get_bits(62, 73, (uint32_t*)pdev->csd);
|
||
c_size_mult = prv_get_bits(47, 49, (uint32_t*)pdev->csd); //csd_c_size_mult ();
|
||
mult = 1 << (c_size_mult + 2);
|
||
pdev->blocknr = (c_size + 1) * mult;
|
||
/* adjust blocknr to 512/block */
|
||
if (pdev->block_len > MMC_SECTOR_SIZE)
|
||
pdev->blocknr = pdev->blocknr * (pdev->block_len >> 9);
|
||
|
||
/* get extended CSD for newer MMC cards CSD spec >= 4.0*/
|
||
if (((pdev->card_type & CARD_TYPE_SD) == 0) &&
|
||
(prv_get_bits(122, 125, (uint32_t*)pdev->csd) >= 4))
|
||
{
|
||
/* put card in trans state */
|
||
status = sdio_execute_command(pdev, CMD_SELECT_CARD, pdev->rca << 16, 0);
|
||
/* set block size and byte count */
|
||
LPC_SDMMC->BLKSIZ = MMC_SECTOR_SIZE;
|
||
LPC_SDMMC->BYTCNT = MMC_SECTOR_SIZE;
|
||
/* send EXT_CSD command */
|
||
sdio_dma_setup((uint32_t) pdev->ext_csd, MMC_SECTOR_SIZE);
|
||
status = sdio_execute_command(pdev, CMD_SEND_EXT_CSD, 0, 0
|
||
| MCI_INT_DATA_OVER);
|
||
if ((status & MCI_INT_ERROR) == 0)
|
||
{
|
||
/* check EXT_CSD_VER is greater than 1.1 */
|
||
if ((pdev->ext_csd[48] & 0xFF) > 1)
|
||
pdev->blocknr = pdev->ext_csd[53]; /* bytes 212:215 represent sec count */
|
||
|
||
/* switch to 52MHz clock if card type is set to 1 or else set to 26MHz */
|
||
if ((pdev->ext_csd[49] & 0xFF) == 1)
|
||
{
|
||
/* for type 1 MMC cards high speed is 52MHz */
|
||
pdev->speed = MMC_HIGH_BUS_MAX_CLOCK;
|
||
}
|
||
else
|
||
{
|
||
/* for type 0 MMC cards high speed is 26MHz */
|
||
pdev->speed = MMC_LOW_BUS_MAX_CLOCK;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
pdev->device_size = pdev->blocknr << 9; /* blocknr * 512 */
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: prv_set_trans_state
|
||
**
|
||
** Descriptions: Puts current selected card in trans state
|
||
**
|
||
** parameters: pdev: Pointer to card info structure
|
||
**
|
||
** Returned value: 0 on success, or error code (-1)
|
||
**
|
||
*****************************************************************************/
|
||
static int prv_set_trans_state(MCI_CARD_INFO_T* pdev)
|
||
{
|
||
uint32_t status;
|
||
|
||
/* get current state of the card */
|
||
status = sdio_execute_command(pdev, CMD_SEND_STATUS, pdev->rca << 16, 0);
|
||
if (status & MCI_INT_RTO)
|
||
{
|
||
/* unable to get the card state. So return immediatly. */
|
||
return -1;
|
||
}
|
||
/* check card state in response */
|
||
status = R1_CURRENT_STATE(pdev->response[0]);
|
||
switch (status)
|
||
{
|
||
case SDMMC_STBY_ST:
|
||
/* put card in 'Trans' state */
|
||
status = sdio_execute_command(pdev, CMD_SELECT_CARD, pdev->rca << 16, 0);
|
||
if (status != 0)
|
||
{
|
||
/* unable to put the card in Trans state. So return immediatly. */
|
||
return -1;
|
||
}
|
||
break;
|
||
case SDMMC_TRAN_ST:
|
||
/*do nothing */
|
||
break;
|
||
default:
|
||
/* card shouldn't be in other states so return */
|
||
return -1;
|
||
}
|
||
|
||
#if SDIO_BUS_WIDTH > 1
|
||
if (pdev->card_type & CARD_TYPE_SD)
|
||
{
|
||
sdio_execute_command(pdev, CMD_SD_SET_WIDTH, 2, 0); /* SD, 4 bit width */
|
||
/* if positive response */
|
||
LPC_SDMMC->CTYPE = MCI_CTYPE_4BIT(0);
|
||
}
|
||
#if SDIO_BUS_WIDTH > 4
|
||
#error 8-bit mode not supported yet!
|
||
#endif
|
||
#endif
|
||
/* set block length */
|
||
LPC_SDMMC->BLKSIZ = MMC_SECTOR_SIZE;
|
||
status = sdio_execute_command(pdev, CMD_SET_BLOCKLEN, MMC_SECTOR_SIZE, 0);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/***********************************************************************
|
||
* MCI driver public functions
|
||
**********************************************************************/
|
||
|
||
/*****************************************************************************
|
||
** Function name: sdio_execute_command
|
||
**
|
||
** Descriptions: Function to execute a command
|
||
**
|
||
** parameters: pdev: Pointer to card info structure
|
||
** cmd : Command with all flags set.
|
||
** arg : Argument for the command
|
||
** wait_status : Status bits to poll for command completion.
|
||
**
|
||
** Returned value: 0 on success, or error code (-1)
|
||
**
|
||
*****************************************************************************/
|
||
int sdio_execute_command(MCI_CARD_INFO_T* pdev,
|
||
uint32_t cmd,
|
||
uint32_t arg,
|
||
uint32_t wait_status)
|
||
{
|
||
/* if APP command there are 2 stages */
|
||
int step = (cmd & CMD_BIT_APP) ? 2 : 1;
|
||
int status = 0;
|
||
uint32_t cmd_reg = 0;
|
||
|
||
if (!wait_status)
|
||
wait_status = (cmd & CMD_MASK_RESP) ? MCI_INT_CMD_DONE : MCI_INT_DATA_OVER;
|
||
|
||
/* Clear the interrupts & FIFOs*/
|
||
if (cmd & CMD_BIT_DATA)
|
||
prv_clear_all();
|
||
|
||
while (step)
|
||
{
|
||
prv_set_clock((cmd & CMD_BIT_LS) ? SD_MMC_ENUM_CLOCK : pdev->speed);
|
||
|
||
/* Clear the interrupts */
|
||
LPC_SDMMC->RINTSTS = 0xFFFFFFFF;
|
||
|
||
switch (step)
|
||
{
|
||
case 1: /* Execute command */
|
||
cmd_reg = ((cmd & CMD_MASK_CMD) >> CMD_SHIFT_CMD)
|
||
| ((cmd & CMD_BIT_INIT) ? MCI_CMD_INIT : 0)
|
||
| ((cmd & CMD_BIT_DATA) ? (MCI_CMD_DAT_EXP | MCI_CMD_PRV_DAT_WAIT) : 0)
|
||
| (((cmd & CMD_MASK_RESP) == CMD_RESP_R2) ? MCI_CMD_RESP_LONG : 0)
|
||
| ((cmd & CMD_MASK_RESP) ? MCI_CMD_RESP_EXP : 0)
|
||
| ((cmd & CMD_BIT_WRITE) ? MCI_CMD_DAT_WR : 0)
|
||
| ((cmd & CMD_BIT_STREAM) ? MCI_CMD_STRM_MODE : 0)
|
||
| ((cmd & CMD_BIT_BUSY) ? MCI_CMD_STOP : 0)
|
||
| ((cmd & CMD_BIT_AUTO_STOP) ? MCI_CMD_SEND_STOP : 0)
|
||
| MCI_CMD_START
|
||
;
|
||
/* wait for previos data finsh for select/deselect commands */
|
||
if (((cmd & CMD_MASK_CMD) >> CMD_SHIFT_CMD) == MMC_SELECT_CARD)
|
||
{
|
||
cmd_reg |= MCI_CMD_PRV_DAT_WAIT;
|
||
}
|
||
|
||
/* wait for command to be accepted by CIU */
|
||
if (prv_send_cmd(cmd_reg, arg) == 0)
|
||
--step;
|
||
break;
|
||
|
||
case 0:
|
||
return 0;
|
||
|
||
case 2: /* APP prefix */
|
||
cmd_reg = MMC_APP_CMD
|
||
| MCI_CMD_RESP_EXP /* Response is status */
|
||
| ((cmd & CMD_BIT_INIT) ? MCI_CMD_INIT : 0)
|
||
| MCI_CMD_START
|
||
;
|
||
if (prv_send_cmd(cmd_reg, pdev->rca << 16) == 0)
|
||
--step;
|
||
break;
|
||
}
|
||
|
||
/* wait for command response*/
|
||
status = prv_wait_for_completion(pdev, wait_status);
|
||
|
||
/* We return an error if there is a timeout, even if we've fetched
|
||
a response */
|
||
if (status & MCI_INT_ERROR)
|
||
return status;
|
||
|
||
if (status & MCI_INT_CMD_DONE)
|
||
{
|
||
switch (cmd & CMD_MASK_RESP)
|
||
{
|
||
case 0:
|
||
break;
|
||
case CMD_RESP_R1:
|
||
case CMD_RESP_R3:
|
||
prv_pull_response(pdev, 48);
|
||
break;
|
||
case CMD_RESP_R2:
|
||
prv_pull_response(pdev, 136);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: sdio_acquire
|
||
**
|
||
** Descriptions: Function to enumerate the SD/MMC/SDHC/MMC+ cards
|
||
**
|
||
** parameters: None
|
||
**
|
||
** Returned value: 1 if a card is acquired, otherwise 0
|
||
**
|
||
*****************************************************************************/
|
||
int sdio_acquire(void)
|
||
{
|
||
MCI_CARD_INFO_T* pdev = &g_card_info;
|
||
int status;
|
||
int tries = 0;
|
||
uint32_t ocr = OCR_VOLTAGE_RANGE_MSK;
|
||
uint32_t r;
|
||
int state = 0;
|
||
uint32_t command = 0;
|
||
/* preserve wait_func and irq callback*/
|
||
MCI_CMD_WAIT_FUNC_T temp = pdev->wait_func;
|
||
MCI_IRQ_CB_FUNC_T tempirq = pdev->irq_callback;
|
||
|
||
/* clear card struct */
|
||
memset(pdev, 0, sizeof(MCI_CARD_INFO_T));
|
||
/* restore wait_func */
|
||
pdev->wait_func = temp;
|
||
pdev->irq_callback = tempirq;
|
||
|
||
/* clear card type */
|
||
LPC_SDMMC->CTYPE = 0;
|
||
|
||
/* set high speed for the card as 20MHz */
|
||
pdev->speed = MMC_MAX_CLOCK;
|
||
|
||
status = sdio_execute_command(pdev, CMD_IDLE, 0, MCI_INT_CMD_DONE);
|
||
|
||
while (state < 100)
|
||
{
|
||
switch (state)
|
||
{
|
||
case 0: /* Setup for SD */
|
||
/* check if it is SDHC card */
|
||
status = sdio_execute_command(pdev, CMD_SD_SEND_IF_COND, SD_SEND_IF_ARG, 0);
|
||
if (!(status & MCI_INT_RTO))
|
||
{
|
||
|
||
/* check response has same echo pattern */
|
||
if ((pdev->response[0] & SD_SEND_IF_ECHO_MSK) == SD_SEND_IF_RESP)
|
||
/* it is SD 2.0 card so indicate we are SDHC capable*/
|
||
ocr |= OCR_HC_CCS;
|
||
}
|
||
|
||
++state;
|
||
command = CMD_SD_OP_COND;
|
||
tries = INIT_OP_RETRIES;
|
||
/* assume SD card */
|
||
pdev->card_type |= CARD_TYPE_SD;
|
||
/* for SD cards high speed is 25MHz */
|
||
pdev->speed = SD_MAX_CLOCK;
|
||
|
||
break;
|
||
|
||
case 10: /* Setup for MMC */
|
||
/* start fresh for MMC crds */
|
||
pdev->card_type &= ~CARD_TYPE_SD;
|
||
status = sdio_execute_command(pdev, CMD_IDLE, 0, MCI_INT_CMD_DONE);
|
||
command = CMD_MMC_OP_COND;
|
||
tries = INIT_OP_RETRIES;
|
||
ocr |= OCR_HC_CCS;
|
||
++state;
|
||
/* for MMC cards high speed is 20MHz */
|
||
pdev->speed = MMC_MAX_CLOCK;
|
||
break;
|
||
|
||
case 1:
|
||
case 11:
|
||
status = sdio_execute_command(pdev, command, 0, 0);
|
||
if (status & MCI_INT_RTO)
|
||
state += 9; /* Mode unavailable */
|
||
else
|
||
++state;
|
||
break;
|
||
|
||
case 2: /* Initial OCR check */
|
||
case 12:
|
||
ocr = pdev->response[0] | (ocr & OCR_HC_CCS);
|
||
if (ocr & OCR_ALL_READY)
|
||
++state;
|
||
else
|
||
state += 2;
|
||
break;
|
||
|
||
case 3: /* Initial wait for OCR clear */
|
||
case 13:
|
||
while ((ocr & OCR_ALL_READY) && --tries > 0)
|
||
{
|
||
timer_wait_ms(0, MS_ACQUIRE_DELAY);
|
||
status = sdio_execute_command(pdev, command, 0, 0);
|
||
ocr = pdev->response[0] | (ocr & OCR_HC_CCS);
|
||
}
|
||
if (ocr & OCR_ALL_READY)
|
||
state += 7;
|
||
else
|
||
++state;
|
||
break;
|
||
|
||
case 14:
|
||
/* for MMC cards set high capacity bit */
|
||
ocr |= OCR_HC_CCS;
|
||
case 4: /* Assign OCR */
|
||
tries = SET_OP_RETRIES;
|
||
ocr &= OCR_VOLTAGE_RANGE_MSK | OCR_HC_CCS; /* Mask for the bits we care about */
|
||
do
|
||
{
|
||
timer_wait_ms(0, MS_ACQUIRE_DELAY);
|
||
status = sdio_execute_command(pdev, command, ocr, 0);
|
||
r = pdev->response[0];
|
||
}
|
||
while (!(r & OCR_ALL_READY) && --tries > 0);
|
||
if (r & OCR_ALL_READY)
|
||
{
|
||
/* is it high capacity card */
|
||
pdev->card_type |= (r & OCR_HC_CCS);
|
||
++state;
|
||
}
|
||
else
|
||
state += 6;
|
||
|
||
break;
|
||
|
||
case 5: /* CID polling */
|
||
case 15:
|
||
status = sdio_execute_command(pdev, CMD_ALL_SEND_CID, 0, 0);
|
||
memcpy(pdev->cid, pdev->response, 16);
|
||
++state;
|
||
break;
|
||
|
||
case 6: /* RCA send, for SD get RCA */
|
||
status = sdio_execute_command(pdev, CMD_SD_SEND_RCA, 0, 0);
|
||
pdev->rca = (pdev->response[0]) >> 16;
|
||
++state;
|
||
break;
|
||
case 16: /* RCA assignment for MMC set to 1 */
|
||
pdev->rca = 1;
|
||
status = sdio_execute_command(pdev, CMD_MMC_SET_RCA, pdev->rca << 16, 0);
|
||
++state;
|
||
break;
|
||
|
||
case 7:
|
||
case 17:
|
||
status = sdio_execute_command(pdev, CMD_SEND_CSD, pdev->rca << 16, 0);
|
||
memcpy(pdev->csd, pdev->response, 16);
|
||
state = 100;
|
||
break;
|
||
|
||
default:
|
||
state += 100; /* break from while loop */
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* Compute card size, block size and no. of blocks
|
||
based on CSD response recived. */
|
||
if (prv_card_acquired(pdev))
|
||
prv_process_csd(pdev);
|
||
|
||
return prv_card_acquired(&g_card_info);
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: sdio_init
|
||
**
|
||
** Descriptions: Initializes the MCI card controller
|
||
**
|
||
** parameters: waitfunc : Pointer to wait function to be used during for poll
|
||
** command status.
|
||
** irqfunc : Pointer to IRQ status callback
|
||
**
|
||
** Returned value: None
|
||
**
|
||
*****************************************************************************/
|
||
void sdio_init(MCI_CMD_WAIT_FUNC_T waitfunc,
|
||
MCI_IRQ_CB_FUNC_T irqfunc)
|
||
{
|
||
volatile uint32_t i;
|
||
|
||
/* enable SD/MMC clock */
|
||
sdio_enable_clock();
|
||
|
||
/* Software reset */
|
||
LPC_SDMMC->BMOD = MCI_BMOD_SWR;
|
||
|
||
/* reset all blocks */
|
||
LPC_SDMMC->CTRL = MCI_CTRL_RESET | MCI_CTRL_FIFO_RESET
|
||
| MCI_CTRL_DMA_RESET;
|
||
while (LPC_SDMMC->CTRL &
|
||
(MCI_CTRL_RESET | MCI_CTRL_FIFO_RESET | MCI_CTRL_DMA_RESET));
|
||
|
||
/* Internal DMA setup for control register */
|
||
#ifdef SDIO_USE_POLLING
|
||
LPC_SDMMC->CTRL = MCI_CTRL_USE_INT_DMAC;
|
||
#else
|
||
LPC_SDMMC->CTRL = MCI_CTRL_USE_INT_DMAC | MCI_CTRL_INT_ENABLE;
|
||
#endif
|
||
/* Clear the interrupts for the host controller */
|
||
LPC_SDMMC->RINTSTS = 0xFFFFFFFF;
|
||
|
||
/* Put in max timeout */
|
||
LPC_SDMMC->TMOUT = 0xFFFFFFFF;
|
||
|
||
/* FIFO threshold settings for DMA, DMA burst of 4,
|
||
FIFO watermark at 16 */
|
||
LPC_SDMMC->FIFOTH = (0x1 << 28) | (0xF << 16) | (0x10 << 0);
|
||
|
||
/* Enable DMA, burst size of 4, fixed burst */
|
||
LPC_SDMMC->BMOD = MCI_BMOD_DE | MCI_BMOD_PBL4 | MCI_BMOD_DSL(4);
|
||
|
||
/* disable clock to CIU (needs latch) */
|
||
LPC_SDMMC->CLKENA = 0;
|
||
LPC_SDMMC->CLKSRC = 0;
|
||
|
||
/* clear mmc structure*/
|
||
memset(&g_card_info, 0, sizeof(MCI_CARD_INFO_T));
|
||
/* set the wait_func if passed */
|
||
g_card_info.wait_func = waitfunc;
|
||
g_card_info.irq_callback = irqfunc;
|
||
|
||
/* If not in polling mode, enable SDIO IRQ */
|
||
#ifndef SDIO_USE_POLLING
|
||
NVIC_EnableIRQ(SDIO_IRQn);
|
||
#endif
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: sdio_card_detect
|
||
**
|
||
** Descriptions: Detect if an SD card is inserted
|
||
**
|
||
** parameters: None
|
||
**
|
||
** Returned value: 1 if a card is detected, otherwise 0
|
||
**
|
||
*****************************************************************************/
|
||
int sdio_card_detect(void)
|
||
{
|
||
/* No card = high state in regsiter */
|
||
if (LPC_SDMMC->CDETECT & 1)
|
||
return 0;
|
||
|
||
return 1;
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: sdio_card_wp_on
|
||
**
|
||
** Descriptions: Detect if write protect is enabled
|
||
**
|
||
** parameters: None
|
||
**
|
||
** Returned value: Returns 1 if card is write protected, otherwise 0
|
||
**
|
||
*****************************************************************************/
|
||
int sdio_card_wp_on(void)
|
||
{
|
||
if (LPC_SDMMC->WRTPRT & 1)
|
||
return 1;
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: sdio_power_on
|
||
**
|
||
** Descriptions: Enable slot power
|
||
**
|
||
** Returned value: None
|
||
**
|
||
*****************************************************************************/
|
||
void sdio_power_on(void)
|
||
{
|
||
sdio_power_onoff(0);
|
||
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: sdio_power_off
|
||
**
|
||
** Descriptions: Enable slot power
|
||
**
|
||
** Returned value: None
|
||
**
|
||
*****************************************************************************/
|
||
void sdio_power_off(void)
|
||
{
|
||
sdio_power_onoff(1);
|
||
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: sdio_power_onoff
|
||
**
|
||
** Descriptions: Enable or disable slot power
|
||
**
|
||
** parameters: enable: !0 to enable, or 0 to disable
|
||
**
|
||
** Returned value: None
|
||
**
|
||
*****************************************************************************/
|
||
void sdio_power_onoff(int enable)
|
||
{
|
||
if (enable)
|
||
LPC_SDMMC->PWREN = 1;
|
||
else
|
||
LPC_SDMMC->PWREN = 0;
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: sdio_get_state
|
||
**
|
||
** Descriptions: Get card's current state (idle, transfer, program, etc.)
|
||
**
|
||
** parameters: None
|
||
**
|
||
** Returned value: Current transfer state (0 -
|
||
**
|
||
*****************************************************************************/
|
||
|
||
/* Get card's current state (idle, transfer, program, etc.) */
|
||
int sdio_get_state(void)
|
||
{
|
||
uint32_t status;
|
||
|
||
/* get current state of the card */
|
||
status = sdio_execute_command(&g_card_info, CMD_SEND_STATUS, g_card_info.rca << 16, 0);
|
||
if (status & MCI_INT_RTO)
|
||
{
|
||
/* unable to get the card state. So return immediatly. */
|
||
return -1;
|
||
}
|
||
/* check card state in response */
|
||
return (int) R1_CURRENT_STATE(g_card_info.response[0]);
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: sdio_deinit
|
||
**
|
||
** Descriptions: Close the MCI
|
||
**
|
||
** parameters: None
|
||
**
|
||
** Returned value: None
|
||
**
|
||
*****************************************************************************/
|
||
void sdio_deinit(void)
|
||
{
|
||
/* Place card in idle state */
|
||
if (prv_card_acquired(&g_card_info) == 0)
|
||
sdio_execute_command(&g_card_info, CMD_IDLE, 0, MCI_INT_CMD_DONE);
|
||
|
||
/* clear mmc structure*/
|
||
memset(&g_card_info, 0, sizeof(MCI_CARD_INFO_T));
|
||
sdio_disable_clock();
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: sdio_read_blocks
|
||
**
|
||
** Descriptions: Performs the read of data from the SD/MMC card
|
||
**
|
||
** parameters: buffer: Pointer to data buffer to copy to
|
||
** start_block: Start block number
|
||
** end_block: End block number
|
||
**
|
||
** Returned value: Bytes read, or 0 on error
|
||
**
|
||
*****************************************************************************/
|
||
int sdio_read_blocks(void* buffer,
|
||
int start_block,
|
||
int end_block)
|
||
{
|
||
MCI_CARD_INFO_T* pdev = &g_card_info;
|
||
int cbRead = (end_block - start_block + 1) << 9; /*(end_block - start_block) * 512 */
|
||
int status = 0;
|
||
int index;
|
||
|
||
/* if card is not acquired return immediately */
|
||
if ((prv_card_acquired(pdev) == 0)
|
||
|| (end_block < start_block) /* check block index in range */
|
||
|| (start_block < 0)
|
||
|| (end_block > pdev->blocknr)
|
||
)
|
||
{
|
||
return 0;
|
||
}
|
||
/* put card in trans state */
|
||
if (prv_set_trans_state(pdev) != 0)
|
||
return 0;
|
||
|
||
/* set number of bytes to read */
|
||
LPC_SDMMC->BYTCNT = cbRead;
|
||
|
||
/* if high capacity card use block indexing */
|
||
if (pdev->card_type & CARD_TYPE_HC)
|
||
index = start_block;
|
||
else
|
||
index = start_block << 9;
|
||
|
||
sdio_dma_setup((uint32_t) buffer, cbRead);
|
||
|
||
/* check how many blocks to read */
|
||
if (end_block == start_block)
|
||
{
|
||
status = sdio_execute_command(pdev, CMD_READ_SINGLE, index,
|
||
0 | MCI_INT_DATA_OVER);
|
||
}
|
||
else
|
||
{
|
||
/* do read multiple */
|
||
status = sdio_execute_command(pdev, CMD_READ_MULTIPLE, index,
|
||
0 | MCI_INT_DATA_OVER);
|
||
}
|
||
|
||
if (status != 0)
|
||
cbRead = 0; /* return error if command fails */
|
||
|
||
wait_for_program_finish();
|
||
|
||
return cbRead;
|
||
}
|
||
|
||
/*****************************************************************************
|
||
** Function name: sdio_write_blocks
|
||
**
|
||
** Descriptions: Performs write of data to the SD/MMC card
|
||
**
|
||
** parameters: buffer: Pointer to data buffer to copy to
|
||
** start_block: Start block number
|
||
** end_block: End block number
|
||
**
|
||
** Returned value: Number of bytes actually written, or 0 on error
|
||
**
|
||
*****************************************************************************/
|
||
int sdio_write_blocks(void* buffer,
|
||
int start_block,
|
||
int end_block)
|
||
{
|
||
MCI_CARD_INFO_T* pdev = &g_card_info;
|
||
/*(end_block - start_block) * 512 */
|
||
int cbWrote = (end_block - start_block + 1) << 9;
|
||
int status;
|
||
int index;
|
||
|
||
/* if card is not acquired return immediately */
|
||
if ((prv_card_acquired(pdev) == 0)
|
||
|| (end_block < start_block) /* check block index in range */
|
||
|| (start_block < 0)
|
||
|| (end_block > pdev->blocknr)
|
||
)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
wait_for_program_finish();
|
||
|
||
/* put card in trans state */
|
||
if (prv_set_trans_state(pdev) != 0)
|
||
return 0;
|
||
|
||
/* set number of bytes to write */
|
||
LPC_SDMMC->BYTCNT = cbWrote;
|
||
|
||
/* if high capacity card use block indexing */
|
||
if (pdev->card_type & CARD_TYPE_HC)
|
||
index = start_block;
|
||
else
|
||
index = start_block << 9;
|
||
|
||
sdio_dma_setup((uint32_t) buffer, cbWrote);
|
||
|
||
wait_for_program_finish();
|
||
|
||
/* check how many blocks to write */
|
||
if (end_block == start_block)
|
||
{
|
||
status = sdio_execute_command(pdev, CMD_WRITE_SINGLE, index,
|
||
0 | MCI_INT_DATA_OVER);
|
||
}
|
||
else
|
||
{
|
||
/* do write multiple */
|
||
status = sdio_execute_command(pdev, CMD_WRITE_MULTIPLE, index,
|
||
0 | MCI_INT_DATA_OVER);
|
||
}
|
||
|
||
if (status != 0)
|
||
cbWrote = 0;
|
||
|
||
wait_for_program_finish();
|
||
|
||
return cbWrote;
|
||
}
|
||
|
||
/**********************************************************************
|
||
** Function name: SDIO_IRQHandler
|
||
**
|
||
** Description: SDIO controller interrupt handler
|
||
**
|
||
** Parameters: None
|
||
**
|
||
** Returned value: None
|
||
**********************************************************************/
|
||
void SDIO_IRQHandler(void)
|
||
{
|
||
if (g_card_info.irq_callback)
|
||
g_card_info.irq_callback(&g_card_info, LPC_SDMMC->RINTSTS);
|
||
}
|
||
|
||
/**********************************************************************
|
||
** Function name: sdio_get_device_size
|
||
**
|
||
** Description: Return the capacity of the SD card in bytes
|
||
**
|
||
** Parameters: None
|
||
**
|
||
** Returned value: Device capacity in bytes
|
||
**********************************************************************/
|
||
int sdio_get_device_size(void)
|
||
{
|
||
return g_card_info.device_size;
|
||
}
|
||
|