1334 lines
38 KiB
C
1334 lines
38 KiB
C
/***************************************************************************//**
|
|
* @file drv_sdcard.c
|
|
* @brief Memory card driver (SPI mode) of RT-Thread RTOS for using EFM32
|
|
* USART module
|
|
* COPYRIGHT (C) 2011, RT-Thread Development Team
|
|
* @author onelife
|
|
* @version 0.4 beta
|
|
*******************************************************************************
|
|
* @section License
|
|
* The license and distribution terms for this file may be found in the file
|
|
* LICENSE in this distribution or at http://www.rt-thread.org/license/LICENSE
|
|
*******************************************************************************
|
|
* @section Change Logs
|
|
* Date Author Notes
|
|
* 2011-05-13 onelife Initial creation for using EFM32 USART module
|
|
* 2011-07-07 onelife Modify initialization function to return error code
|
|
* 2011-12-08 onelife Add giant gecko development kit support
|
|
* 2011-12-15 onelife Move MicroSD enabling routine to driver
|
|
* initialization function (board.c)
|
|
* 2011-12-21 onelife Modify code due to SPI write format changed
|
|
******************************************************************************/
|
|
|
|
/***************************************************************************//**
|
|
* @addtogroup efm32_dk
|
|
* @{
|
|
******************************************************************************/
|
|
|
|
/* Includes ------------------------------------------------------------------*/
|
|
#include "board.h"
|
|
#include "drv_usart.h"
|
|
#include "drv_sdcard.h"
|
|
|
|
#if defined(EFM32_USING_SPISD)
|
|
#include <dfs_fs.h>
|
|
|
|
/* Private typedef -----------------------------------------------------------*/
|
|
/* Private define ------------------------------------------------------------*/
|
|
/* Private macro -------------------------------------------------------------*/
|
|
#ifdef EFM32_SDCARD_DEBUG
|
|
#define sdcard_debug(format,args...) rt_kprintf(format, ##args)
|
|
#else
|
|
#define sdcard_debug(format,args...)
|
|
#endif
|
|
|
|
/* Private constants ---------------------------------------------------------*/
|
|
/* Private variables ---------------------------------------------------------*/
|
|
static struct rt_device sd_device;
|
|
static struct dfs_partition sdPart;
|
|
static rt_device_t spi = RT_NULL;
|
|
static rt_uint16_t sdType;
|
|
static rt_bool_t sdAutoCs = true;
|
|
static rt_timer_t sdTimer = RT_NULL;
|
|
static volatile rt_bool_t sdInTime = true;
|
|
|
|
/* Private function prototypes -----------------------------------------------*/
|
|
/* Private functions ---------------------------------------------------------*/
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Memory device timeout interrupt handler
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] parameter
|
|
* Parameter
|
|
******************************************************************************/
|
|
static void efm_spiSd_timer(void* parameter)
|
|
{
|
|
sdInTime = false;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Set/Clear chip select
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] enable
|
|
* Chip select pin setting
|
|
******************************************************************************/
|
|
static void efm_spiSd_cs(rt_uint8_t enable)
|
|
{
|
|
if (!sdAutoCs)
|
|
{
|
|
if (enable)
|
|
{
|
|
GPIO_PinOutClear(SD_CS_PORT, SD_CS_PIN);
|
|
}
|
|
else
|
|
{
|
|
GPIO_PinOutSet(SD_CS_PORT, SD_CS_PIN);
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Set operation speed level
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] level
|
|
* Set SD speed level
|
|
******************************************************************************/
|
|
static void efm_spiSd_speed(rt_uint8_t level)
|
|
{
|
|
RT_ASSERT(spi != RT_NULL);
|
|
|
|
struct efm32_usart_device_t *usart;
|
|
rt_uint32_t baudrate;
|
|
|
|
usart = (struct efm32_usart_device_t *)(spi->user_data);
|
|
if (level == SD_SPEED_HIGH)
|
|
{
|
|
baudrate = EFM32_SDCLK_HIGH;
|
|
}
|
|
else
|
|
{
|
|
baudrate = EFM32_SDCLK_LOW;
|
|
}
|
|
USART_BaudrateSyncSet(usart->usart_device, 0, baudrate);
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Read raw data from memory device
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] buffer
|
|
* Poniter to the buffer
|
|
*
|
|
* @param[in] size
|
|
* Buffer size in byte
|
|
*
|
|
* @return
|
|
* Number of read bytes
|
|
******************************************************************************/
|
|
static rt_size_t efm_spiSd_read(void *buffer, rt_size_t size)
|
|
{
|
|
RT_ASSERT(spi != RT_NULL);
|
|
|
|
rt_uint8_t buf_read[5], ret;
|
|
|
|
/* Build instruction buffer */
|
|
buf_read[0] = 0x00;
|
|
*(rt_uint8_t **)(&buf_read[1]) = buffer;
|
|
/* Read data */
|
|
efm_spiSd_cs(1);
|
|
if ((ret = spi->read(spi, EFM32_NO_DATA, buf_read, size)) == 0)
|
|
{
|
|
sdcard_debug("SPISD: Read failed!\n");
|
|
}
|
|
efm_spiSd_cs(0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Send command to memory device
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] cmd
|
|
* Command index
|
|
*
|
|
* @param[in] arg
|
|
* Argument
|
|
*
|
|
* @param[in] trail
|
|
* Pointer to the buffer to store trailing data
|
|
*
|
|
* @return
|
|
* Command response
|
|
******************************************************************************/
|
|
static rt_uint16_t efm_spiSd_cmd(
|
|
rt_uint8_t cmd,
|
|
rt_uint32_t arg,
|
|
rt_uint8_t *trail)
|
|
{
|
|
RT_ASSERT(spi != RT_NULL);
|
|
|
|
rt_uint8_t buf_ins[11];
|
|
rt_uint8_t buf_res[32]; /* Expect (x+1+4) bytes for CRC, (x+1+19) for CSD/CID */
|
|
rt_uint8_t len_trl, i, j;
|
|
rt_uint16_t ret;
|
|
rt_bool_t skip;
|
|
|
|
ret = 0xffff;
|
|
rt_memset(buf_res, 0xff, sizeof(buf_res));
|
|
|
|
sdcard_debug("SPISD: Send command %d(%x)\n", cmd, arg);
|
|
do
|
|
{
|
|
/* Build instruction buffer */
|
|
buf_ins[0] = 6; /* Instruction length */
|
|
buf_ins[1] = 0x40 | cmd; /* Command index */
|
|
buf_ins[2] = (arg >> 24) & 0x000000ff; /* Argument: MSB first */
|
|
buf_ins[3] = (arg >> 16) & 0x000000ff;
|
|
buf_ins[4] = (arg >> 8) & 0x000000ff;
|
|
buf_ins[5] = arg & 0x000000ff;
|
|
if (cmd == CMD0)
|
|
{
|
|
buf_ins[6] = 0x95; /* Valid CRC for CMD0(0) */
|
|
}
|
|
else if (cmd == CMD8)
|
|
{
|
|
buf_ins[6] = 0x87; /* Valid CRC for CMD8(0x1AA) */
|
|
}
|
|
else if (cmd == CMD58)
|
|
{
|
|
buf_ins[6] = 0x01; /* Dummy CRC + Stop */
|
|
}
|
|
else
|
|
{
|
|
buf_ins[6] = 0x01; /* Dummy CRC + Stop */
|
|
}
|
|
*(rt_uint8_t **)(&buf_ins[7]) = buf_res; /* Pointer to RX buffer */
|
|
|
|
/* Set trail length */
|
|
if (cmd == CMD8)
|
|
{
|
|
len_trl = 4; /* R7 response */
|
|
}
|
|
else if (cmd == CMD9)
|
|
{
|
|
len_trl = SD_BLOCK_SIZE_CSD;
|
|
}
|
|
else if (cmd == CMD10)
|
|
{
|
|
len_trl = SD_BLOCK_SIZE_CID;
|
|
}
|
|
else if (cmd == CMD58)
|
|
{
|
|
len_trl = SD_BLOCK_SIZE_OCR; /* R3 response */
|
|
}
|
|
else
|
|
{
|
|
len_trl = 0;
|
|
}
|
|
|
|
/* Send command and get response */
|
|
efm_spiSd_cs(1);
|
|
if (spi->read(spi, EFM32_NO_DATA, buf_ins, sizeof(buf_res)) == 0)
|
|
{
|
|
sdcard_debug("SPISD: Send command failed!\n");
|
|
break;
|
|
}
|
|
efm_spiSd_cs(0);
|
|
|
|
/* Skip a stuff byte when stop reading */
|
|
if (cmd == CMD12)
|
|
{
|
|
skip = true;
|
|
}
|
|
else
|
|
{
|
|
skip = false;
|
|
}
|
|
/* Find valid response: The response is sent back within command response time
|
|
(NCR), 0 to 8 bytes for SDC, 1 to 8 bytes for MMC */
|
|
for (i = 0; i < sizeof(buf_res); i++)
|
|
{
|
|
if (buf_res[i] != 0xff)
|
|
{
|
|
if (skip)
|
|
{
|
|
skip = false;
|
|
sdcard_debug("SPISD: Skip %x (at %d)\n", buf_res[i], i);
|
|
continue;
|
|
}
|
|
|
|
if (cmd == ACMD13 & 0x7f)
|
|
{
|
|
ret = (rt_uint16_t)buf_res[i]; /* R2 response */
|
|
}
|
|
else
|
|
{
|
|
ret = (rt_uint8_t)buf_res[i];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
sdcard_debug("SPISD: Response %x (at %d)\n", ret, i);
|
|
i++;
|
|
/* Copy the trailing data */
|
|
if ((ret != 0xffff) && len_trl && trail)
|
|
{
|
|
if (cmd == CMD9 || cmd == CMD10)
|
|
{
|
|
/* Wait for data block */
|
|
for (; i < sizeof(buf_res); i++)
|
|
{
|
|
if (buf_res[i] == 0xfe)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
/* Check if valid */
|
|
if (i >= sizeof(buf_res))
|
|
{
|
|
sdcard_debug("SPISD: Token is not found!\n");
|
|
ret = 0xffff;
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
/* Copy the data */
|
|
for (j = 0; j < len_trl; j++)
|
|
{
|
|
trail[j] = buf_res[i + j];
|
|
}
|
|
}
|
|
} while(0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Read a block of data from memory device. This function is used to handle
|
|
* the responses of specified commands (e.g. ACMD13, CMD17 and CMD18)
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] buffer
|
|
* Poniter to the buffer
|
|
*
|
|
* @param[in] size
|
|
* Buffer size in byte
|
|
*
|
|
* @return
|
|
* Error code
|
|
******************************************************************************/
|
|
static rt_err_t efm_spiSd_readBlock(void *buffer, rt_size_t size)
|
|
{
|
|
RT_ASSERT(spi != RT_NULL);
|
|
|
|
rt_uint8_t buf_ins[5];
|
|
rt_uint8_t buf_res[8]; /* Expect 2 bytes for CRC */
|
|
rt_uint8_t i, len_copy;
|
|
rt_bool_t start;
|
|
|
|
start = false;
|
|
do
|
|
{
|
|
/* Build instruction buffer */
|
|
buf_ins[0] = 0; /* Instruction length */
|
|
*(rt_uint8_t **)(&buf_ins[1]) = buf_res; /* Pointer to RX buffer */
|
|
|
|
while(1)
|
|
{
|
|
/* Send read command */
|
|
efm_spiSd_cs(1);
|
|
if (spi->read(spi, EFM32_NO_DATA, buf_ins, \
|
|
sizeof(buf_res)) == 0)
|
|
{
|
|
sdcard_debug("SPISD: Get read command response failed!\n");
|
|
break;
|
|
}
|
|
efm_spiSd_cs(0);
|
|
/* Wait for data */
|
|
for (i = 0; i < sizeof(buf_res); i++)
|
|
{
|
|
if (buf_res[i] != 0xff)
|
|
{
|
|
start = true;
|
|
break;
|
|
}
|
|
}
|
|
if (start)
|
|
{
|
|
break;
|
|
}
|
|
};
|
|
|
|
/* Ckeck if valid */
|
|
if (!start || (buf_res[i] != 0xfe))
|
|
{
|
|
sdcard_debug("SPISD: Token is invalid! (%x)\n", buf_res[i]);
|
|
break;
|
|
}
|
|
/* Copy data to buffer and read the rest */
|
|
len_copy = sizeof(buf_res) - i - 1;
|
|
rt_memcpy(buffer, &buf_res[i + 1], len_copy);
|
|
sdcard_debug("SPISD: Read block start at %d, copy %d bytes\n", i, \
|
|
len_copy);
|
|
|
|
/* Build instruction buffer */
|
|
buf_ins[0] = 0; /* Instruction length */
|
|
*(rt_uint8_t **)(&buf_ins[1]) = (rt_uint8_t *)buffer + len_copy; /* Pointer to RX buffer */
|
|
|
|
/* Send read command */
|
|
efm_spiSd_cs(1);
|
|
if (spi->read(spi, EFM32_NO_DATA, buf_ins, size - len_copy) == 0)
|
|
{
|
|
sdcard_debug("SPISD: Read data block failed!\n");
|
|
break;
|
|
}
|
|
*(rt_uint8_t **)(&buf_ins[1]) = buf_res; /* Pointer to RX buffer */
|
|
if (spi->read(spi, EFM32_NO_DATA, buf_ins, sizeof(buf_res)) == 0)
|
|
{
|
|
sdcard_debug("SPISD: Read CRC failed!\n");
|
|
break;
|
|
}
|
|
sdcard_debug("SPISD: Read CRC %x %x\n", buf_res[0], buf_res[1]);
|
|
efm_spiSd_cs(0);
|
|
|
|
return RT_EOK;
|
|
} while(0);
|
|
|
|
sdcard_debug("SPISD: Read block failed!\n");
|
|
return -RT_ERROR;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Write a block of data to memory device. This function is used to send data
|
|
* and control tokens for block write commands (e.g. CMD24 and CMD25)
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] buffer
|
|
* Poniter to the buffer
|
|
*
|
|
* @param[in] token
|
|
* Control token
|
|
*
|
|
* @return
|
|
* Error code
|
|
******************************************************************************/
|
|
static rt_err_t efm_spiSd_writeBlock(void *buffer, rt_uint8_t token)
|
|
{
|
|
RT_ASSERT(spi != RT_NULL);
|
|
|
|
rt_err_t ret;
|
|
rt_uint8_t buf_ins[11];
|
|
rt_uint8_t buf_res[8]; /* Expect a byte for data response */
|
|
rt_uint8_t i;
|
|
|
|
ret = RT_ERROR;
|
|
sdcard_debug("SPISD: Write block\n");
|
|
do
|
|
{
|
|
/* Initialize timer */
|
|
sdInTime = true;
|
|
rt_timer_start(sdTimer);
|
|
/* Wait for card ready */
|
|
do
|
|
{
|
|
efm_spiSd_read(buf_res, sizeof(buf_res));
|
|
} while (sdInTime && (buf_res[sizeof(buf_res) - 1] != 0xff));
|
|
if (buf_res[sizeof(buf_res) - 1] != 0xff)
|
|
{
|
|
sdcard_debug("SPISD: Card is busy before writing! (%x)\n", \
|
|
buf_res[sizeof(buf_res) - 1]);
|
|
ret = -RT_EBUSY;
|
|
break;
|
|
}
|
|
rt_timer_stop(sdTimer);
|
|
|
|
/* Send data */
|
|
sdcard_debug("SPISD: Send data, token %x\n", token);
|
|
if (token != 0xfd)
|
|
{
|
|
/* Send token and data */
|
|
buf_ins[0] = 1; /* Instruction length */
|
|
buf_ins[1] = token;
|
|
*(rt_uint8_t **)(&buf_ins[2]) = (rt_uint8_t *)buffer; /* Pointer to TX buffer */
|
|
efm_spiSd_cs(1);
|
|
if (spi->write(spi, EFM32_NO_DATA, buf_ins, SD_SECTOR_SIZE) == 0)
|
|
{
|
|
sdcard_debug("SPISD: Write data failed!\n");
|
|
break;
|
|
}
|
|
|
|
/* Build instruction buffer */
|
|
buf_ins[0] = 2; /* Instruction length */
|
|
buf_ins[1] = 0xff; /* CRC (Dummy) */
|
|
buf_ins[2] = 0xff;
|
|
*(rt_uint8_t **)(&buf_ins[3]) = buf_res; /* Pointer to RX buffer */
|
|
/* Send CRC and read a byte */
|
|
if (spi->read(spi, EFM32_NO_DATA, buf_ins, sizeof(buf_res)) == 0)
|
|
{
|
|
sdcard_debug("SPISD: Write CRC failed!\n");
|
|
break;
|
|
}
|
|
efm_spiSd_cs(0);
|
|
|
|
/* Check if accepted */
|
|
for (i = 0; i < sizeof(buf_res); i++)
|
|
{
|
|
if (buf_res[i] != 0xff)
|
|
{
|
|
buf_res[i] &= 0x1f;
|
|
break;
|
|
}
|
|
}
|
|
if (buf_res[i] != 0x05)
|
|
{
|
|
sdcard_debug("SPISD: Writing is not accepted! (%x at %d)\n", \
|
|
buf_res[i], i);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Send token */
|
|
buf_ins[0] = 1; /* Instruction length */
|
|
buf_ins[1] = token;
|
|
*(rt_uint8_t **)(&buf_ins[2]) = RT_NULL; /* Pointer to TX buffer */
|
|
efm_spiSd_cs(1);
|
|
if (spi->write(spi, EFM32_NO_DATA, buf_ins, 0) != 0)
|
|
{
|
|
sdcard_debug("SPISD: Write token failed!\n");
|
|
break;
|
|
}
|
|
|
|
/* Initialize timer */
|
|
sdInTime = true;
|
|
rt_timer_start(sdTimer);
|
|
/* Wait for card ready */
|
|
do
|
|
{
|
|
efm_spiSd_read(buf_res, sizeof(buf_res));
|
|
} while (sdInTime && (buf_res[sizeof(buf_res) - 1] != 0xff));
|
|
if (buf_res[sizeof(buf_res) - 1] != 0xff)
|
|
{
|
|
sdcard_debug("SPISD: Card is busy after writing! (%x)\n", \
|
|
buf_res[sizeof(buf_res) - 1] );
|
|
ret = -RT_EBUSY;
|
|
break;
|
|
}
|
|
rt_timer_stop(sdTimer);
|
|
}
|
|
|
|
return RT_EOK;
|
|
} while(0);
|
|
|
|
sdcard_debug("SPISD: Write block failed!\n");
|
|
return ret;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Wrapper function of send command to memory device
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] cmd
|
|
* Command index
|
|
*
|
|
* @param[in] arg
|
|
* Argument
|
|
*
|
|
* @param[in] trail
|
|
* Pointer to the buffer to store trailing data
|
|
*
|
|
* @return
|
|
* Command response
|
|
******************************************************************************/
|
|
rt_uint16_t efm_spiSd_sendCmd(
|
|
rt_uint8_t cmd,
|
|
rt_uint32_t arg,
|
|
rt_uint8_t *trail)
|
|
{
|
|
rt_uint16_t ret;
|
|
|
|
/* ACMD<n> is the command sequense of CMD55-CMD<n> */
|
|
if (cmd & 0x80)
|
|
{
|
|
cmd &= 0x7f;
|
|
ret = efm_spiSd_cmd(CMD55, 0x00000000, EFM32_NO_POINTER);
|
|
if (ret > 0x01)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return efm_spiSd_cmd(cmd, arg, trail);
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Initialize memory card device
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] dev
|
|
* Pointer to device descriptor
|
|
*
|
|
* @return
|
|
* Error code
|
|
******************************************************************************/
|
|
static rt_err_t rt_spiSd_init(rt_device_t dev)
|
|
{
|
|
RT_ASSERT(spi != RT_NULL);
|
|
|
|
rt_uint8_t type, cmd, tril[4];
|
|
rt_uint8_t *buf_res;
|
|
|
|
type = 0;
|
|
buf_res = RT_NULL;
|
|
|
|
do
|
|
{
|
|
/* Create and setup timer */
|
|
if ((sdTimer = rt_timer_create(
|
|
"sd_tmr",
|
|
efm_spiSd_timer,
|
|
RT_NULL,
|
|
SD_WAIT_PERIOD,
|
|
RT_TIMER_FLAG_ONE_SHOT)) == RT_NULL)
|
|
{
|
|
sdcard_debug("SPISD: Create timer failed!\n");
|
|
break;
|
|
}
|
|
|
|
/* Open SPI device */
|
|
if (spi->open(spi, RT_DEVICE_OFLAG_RDWR) != RT_EOK)
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* Switch to low speed */
|
|
efm_spiSd_speed(SD_SPEED_LOW);
|
|
|
|
/* 80 dummy clocks */
|
|
efm_spiSd_read(RT_NULL, 80);
|
|
/* Enter Idle state */
|
|
if (efm_spiSd_sendCmd(CMD0, 0x00000000, EFM32_NO_POINTER) != 0x01)
|
|
{
|
|
break;
|
|
}
|
|
/* Check if SDv2 */
|
|
if (efm_spiSd_sendCmd(CMD8, 0x000001AA, tril) == 0x01)
|
|
{
|
|
/* SDv2, Vdd: 2.7-3.6V */
|
|
if (tril[2] == 0x01 && tril[3] == 0xAA)
|
|
{
|
|
/* Initialize timer */
|
|
sdInTime = true;
|
|
rt_timer_start(sdTimer);
|
|
/* Wait for leaving idle state (ACMD41 with HCS bit) */
|
|
while (efm_spiSd_sendCmd(ACMD41, 0x40000000, EFM32_NO_POINTER) \
|
|
&& sdInTime);
|
|
/* Check CCS bit (bit 30) in the OCR */
|
|
if (sdInTime && efm_spiSd_sendCmd(CMD58, 0x00000000, tril) \
|
|
== 0x00)
|
|
{
|
|
type = (tril[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (efm_spiSd_sendCmd(ACMD41, 0x00000000, EFM32_NO_POINTER) <= 0x01)
|
|
{
|
|
/* SDv1 */
|
|
type = CT_SD1;
|
|
cmd = ACMD41;
|
|
}
|
|
else
|
|
{
|
|
/* MMCv3 */
|
|
type = CT_MMC;
|
|
cmd = CMD1;
|
|
}
|
|
/* Initialize timer */
|
|
sdInTime = true;
|
|
rt_timer_start(sdTimer);
|
|
/* Wait for leaving idle state */
|
|
while (efm_spiSd_sendCmd(cmd, 0x00000000, EFM32_NO_POINTER) && \
|
|
sdInTime);
|
|
/* Set read/write block length to SD_BLOCK_SIZE */
|
|
if (!sdInTime || \
|
|
(efm_spiSd_sendCmd(CMD16, SD_SECTOR_SIZE, EFM32_NO_POINTER) \
|
|
!= 0x00))
|
|
{
|
|
type = 0;
|
|
break;
|
|
}
|
|
}
|
|
rt_timer_stop(sdTimer);
|
|
|
|
/* Check type */
|
|
sdType = type;
|
|
if (sdType)
|
|
{
|
|
/* Initialization succeded */
|
|
efm_spiSd_speed(SD_SPEED_HIGH);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* Allocate buffer */
|
|
if ((buf_res = rt_malloc(SD_SECTOR_SIZE)) == RT_NULL)
|
|
{
|
|
sdcard_debug("SPISD: No memory for sector buffer\n");
|
|
break;
|
|
}
|
|
/* Read the first sector for partition table */
|
|
if (dev->read(dev, 0, buf_res, 1) != 1)
|
|
{
|
|
sdcard_debug("SPISD: Read first sector failed!\n");
|
|
break;
|
|
}
|
|
/* Fetch the partition table */
|
|
if (dfs_filesystem_get_partition(&sdPart, buf_res, 0) != RT_EOK)
|
|
{
|
|
sdPart.offset = 0;
|
|
sdPart.size = 0;
|
|
sdcard_debug("SPISD: No partition table\n");
|
|
}
|
|
/* Release buffer */
|
|
rt_free(buf_res);
|
|
sdcard_debug("SPISD: Init OK, card type %x\n", sdType);
|
|
return RT_EOK;
|
|
} while (0);
|
|
|
|
/* Release buffer */
|
|
if (buf_res)
|
|
{
|
|
rt_free(buf_res);
|
|
}
|
|
efm_spiSd_deinit();
|
|
rt_kprintf("SPISD: Init failed!\n");
|
|
return -RT_ERROR;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Open memory card device
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] dev
|
|
* Pointer to device descriptor
|
|
*
|
|
* @param[in] oflag
|
|
* Device open flag
|
|
*
|
|
* @return
|
|
* Error code
|
|
******************************************************************************/
|
|
static rt_err_t rt_spiSd_open(rt_device_t dev, rt_uint16_t oflag)
|
|
{
|
|
sdcard_debug("SPISD: Open, flag %x\n", sd_device.flag);
|
|
return RT_EOK;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Close memory card device
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] dev
|
|
* Pointer to device descriptor
|
|
*
|
|
* @return
|
|
* Error code
|
|
******************************************************************************/
|
|
static rt_err_t rt_spiSd_close(rt_device_t dev)
|
|
{
|
|
sdcard_debug("SPISD: Close, flag %x\n", sd_device.flag);
|
|
return RT_EOK;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Read from memory card device
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] dev
|
|
* Pointer to device descriptor
|
|
*
|
|
* @param[in] sector
|
|
* Start sector number (LBA)
|
|
*
|
|
* @param[in] buffer
|
|
* Pointer to the buffer
|
|
*
|
|
* @param[in] count
|
|
* Sector count (1..255)
|
|
*
|
|
* @return
|
|
* Number of read sectors
|
|
******************************************************************************/
|
|
static rt_size_t rt_spiSd_read(
|
|
rt_device_t dev,
|
|
rt_off_t sector,
|
|
void *buffer,
|
|
rt_size_t count)
|
|
{
|
|
rt_uint8_t buf_ins[11], buf_res[12];
|
|
rt_uint8_t *ptr;
|
|
rt_uint8_t cmd, i;
|
|
rt_size_t cnt;
|
|
|
|
ptr = (rt_uint8_t *)buffer;
|
|
cnt = count;
|
|
|
|
sdcard_debug("SPISD: ****** Read Data ******\n");
|
|
if (!(sdType & CT_BLOCK))
|
|
{
|
|
/* Convert to byte address if needed */
|
|
sector *= SD_SECTOR_SIZE;
|
|
}
|
|
|
|
do
|
|
{
|
|
if (cnt == 1)
|
|
{
|
|
/* Single block read */
|
|
cmd = CMD17;
|
|
sdcard_debug("SPISD: Read single block\n");
|
|
}
|
|
else
|
|
{
|
|
/* Multiple block read */
|
|
cmd = CMD18;
|
|
sdcard_debug("SPISD: Read multiple blocks\n");
|
|
}
|
|
|
|
if (efm_spiSd_sendCmd(cmd, sector, EFM32_NO_POINTER))
|
|
{
|
|
sdcard_debug("SPISD: Read command error!\n");
|
|
break;
|
|
}
|
|
|
|
/* Read data */
|
|
do
|
|
{
|
|
if (efm_spiSd_readBlock(ptr, SD_SECTOR_SIZE))
|
|
{
|
|
break;
|
|
}
|
|
ptr += SD_SECTOR_SIZE;
|
|
} while(--cnt);
|
|
|
|
/* Stop transmission */
|
|
if (cmd == CMD18)
|
|
{
|
|
if (efm_spiSd_sendCmd(CMD12, 0x00000000, EFM32_NO_POINTER))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (count);
|
|
} while(0);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Write to memory card device
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] dev
|
|
* Pointer to device descriptor
|
|
*
|
|
* @param[in] sector
|
|
* Start sector number (LBA)
|
|
*
|
|
* @param[in] buffer
|
|
* Pointer to the buffer
|
|
*
|
|
* @param[in] count
|
|
* Sector count (1..255)
|
|
*
|
|
* @return
|
|
* Number of written sectors
|
|
******************************************************************************/
|
|
static rt_size_t rt_spiSd_write (
|
|
rt_device_t dev,
|
|
rt_off_t sector,
|
|
const void *buffer,
|
|
rt_size_t count)
|
|
{
|
|
rt_uint8_t buf_ins[11], buf_res[12];
|
|
rt_uint8_t *ptr;
|
|
rt_uint8_t cmd, token, i;
|
|
rt_size_t cnt;
|
|
|
|
ptr = (rt_uint8_t *)buffer;
|
|
cnt = count;
|
|
|
|
sdcard_debug("SPISD: ****** Write Data ******\n");
|
|
if (!(sdType & CT_BLOCK))
|
|
{
|
|
/* Convert to byte address if needed */
|
|
sector *= SD_SECTOR_SIZE;
|
|
}
|
|
|
|
do
|
|
{
|
|
if (cnt == 1)
|
|
{
|
|
/* Single block write */
|
|
cmd = CMD24;
|
|
token = 0xfe;
|
|
sdcard_debug("SPISD: Write single block\n");
|
|
}
|
|
else
|
|
{
|
|
/* Multiple block write */
|
|
cmd = CMD25;
|
|
token = 0xfc;
|
|
sdcard_debug("SPISD: Write multiple blocks\n");
|
|
if (sdType & CT_SDC)
|
|
{
|
|
if (efm_spiSd_sendCmd(ACMD23, count, EFM32_NO_POINTER))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (efm_spiSd_sendCmd(cmd, sector, EFM32_NO_POINTER))
|
|
{
|
|
sdcard_debug("SPISD: Write command error!\n");
|
|
break;
|
|
}
|
|
|
|
/* Write data */
|
|
do
|
|
{
|
|
if (efm_spiSd_writeBlock(ptr, token))
|
|
{
|
|
break;
|
|
}
|
|
ptr += SD_SECTOR_SIZE;
|
|
} while(--cnt);
|
|
|
|
/* Stop transmission token */
|
|
if (efm_spiSd_writeBlock(EFM32_NO_POINTER, 0xfd))
|
|
{
|
|
break;
|
|
}
|
|
|
|
return (count);
|
|
} while(0);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Configure memory card device
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] dev
|
|
* Pointer to device descriptor
|
|
*
|
|
* @param[in] ctrl
|
|
* Memory card control command
|
|
*
|
|
* @param[in] buffer
|
|
* Pointer to the buffer of in/out data
|
|
*
|
|
* @return
|
|
* Error code
|
|
******************************************************************************/
|
|
static rt_err_t rt_spiSd_control (
|
|
rt_device_t dev,
|
|
rt_uint8_t ctrl,
|
|
void *buffer)
|
|
{
|
|
rt_err_t ret;
|
|
rt_uint32_t c_size;
|
|
rt_uint8_t n;
|
|
rt_uint8_t *buf_res;
|
|
|
|
ret = -RT_ERROR;
|
|
buf_res = RT_NULL;
|
|
switch (ctrl)
|
|
{
|
|
case RT_DEVICE_CTRL_SD_SYNC:
|
|
/* Flush dirty buffer if present */
|
|
efm_spiSd_cs(1);
|
|
efm_spiSd_cs(0);
|
|
ret = RT_EOK;
|
|
break;
|
|
|
|
case RT_DEVICE_CTRL_SD_GET_SCOUNT:
|
|
{
|
|
/* Allocate buffer */
|
|
if ((buf_res = rt_malloc(SD_BLOCK_SIZE_CSD)) == RT_NULL)
|
|
{
|
|
sdcard_debug("SPISD: No memory for RX buffer\n");
|
|
break;
|
|
}
|
|
/* Get number of sectors on the disk (32 bits) */
|
|
if (efm_spiSd_sendCmd(CMD9, 0x00000000, buf_res))
|
|
{
|
|
sdcard_debug("SPISD: Get CSD failed!\n");
|
|
break;
|
|
}
|
|
|
|
if ((buf_res[0] >> 6) == 0x01)
|
|
{
|
|
/* SDv2 */
|
|
/* C_SIZE: Bit 48~69 */
|
|
c_size = ((rt_uint32_t)(buf_res[7] & 0x3f) << 16) + \
|
|
((rt_uint32_t)buf_res[8] << 8) + buf_res[9] + 1;
|
|
/* Result = Capacity / Sector Size */
|
|
*(rt_uint32_t *)buffer = (rt_uint32_t)c_size << \
|
|
(19 - SD_SECTOR_SIZE_SHIFT);
|
|
}
|
|
else
|
|
{
|
|
/* SDv1 or MMC */
|
|
/* C_SIZE: Bit 62~73 */
|
|
c_size = ((rt_uint32_t)(buf_res[6] & 0x03) << 10) + \
|
|
((rt_uint16_t)buf_res[7] << 2) + (buf_res[8] >> 6) + 1;
|
|
/* READ_BL_LEN: Bit 80~83, C_SIZE_MULT: Bit 47~49 */
|
|
n = ((buf_res[9] & 0x03) << 1) + ((buf_res[10] & 0x80) >> 7) + \
|
|
2 + (buf_res[5] & 0x0f);
|
|
/* Result = Capacity / Sector Size */
|
|
*(rt_uint32_t *)buffer = (rt_uint32_t)c_size << \
|
|
(n - SD_SECTOR_SIZE_SHIFT);
|
|
}
|
|
ret = RT_EOK;
|
|
break;
|
|
}
|
|
|
|
case RT_DEVICE_CTRL_SD_GET_SSIZE:
|
|
/* Get sectors on the disk (16 bits) */
|
|
*(rt_uint16_t *)buffer = SD_SECTOR_SIZE;
|
|
ret = RT_EOK;
|
|
break;
|
|
|
|
case RT_DEVICE_CTRL_SD_GET_BSIZE:
|
|
/* Get erase block size in unit of sectors (32 bits) */
|
|
if (sdType & CT_SD2)
|
|
{
|
|
/* Allocate buffer */
|
|
if ((buf_res = rt_malloc(SD_BLOCK_SIZE_SDSTAT)) == RT_NULL)
|
|
{
|
|
sdcard_debug("SPISD: No memory for RX buffer\n");
|
|
break;
|
|
}
|
|
/* SDv2 */
|
|
if (efm_spiSd_sendCmd(ACMD13, 0x00000000, EFM32_NO_POINTER))
|
|
{
|
|
sdcard_debug("SPISD: Get SD status failed!\n");
|
|
break;
|
|
}
|
|
if (efm_spiSd_readBlock(buf_res, SD_BLOCK_SIZE_SDSTAT))
|
|
{
|
|
sdcard_debug("SPISD: Read SD status failed!\n");
|
|
break;
|
|
}
|
|
/* AU_SIZE: Bit 428~431 */
|
|
*(rt_uint32_t *)buffer = 16UL << ((buf_res[10] >> 4) + 9 - \
|
|
SD_SECTOR_SIZE_SHIFT);
|
|
}
|
|
else
|
|
{
|
|
/* Allocate buffer */
|
|
if ((buf_res = rt_malloc(SD_BLOCK_SIZE_CSD)) == RT_NULL)
|
|
{
|
|
sdcard_debug("SPISD: No memory for RX buffer\n");
|
|
break;
|
|
}
|
|
/* SDv1 or MMC */
|
|
if (efm_spiSd_sendCmd(CMD9, 0x00000000, buf_res))
|
|
{
|
|
sdcard_debug("SPISD: Get CSD failed!\n");
|
|
break;
|
|
}
|
|
|
|
if (sdType & CT_SD1)
|
|
{
|
|
/* SECTOR_SIZE: Bit 39~45, WRITE_BL_LEN: Bit 22~25 (9, 10 or 11) */
|
|
*(rt_uint32_t *)buffer = (((buf_res[10] & 0x3f) << 1) + \
|
|
((rt_uint32_t)(buf_res[11] & 0x80) >> 7) + 1) << \
|
|
(8 + (buf_res[13] >> 6) - SD_SECTOR_SIZE_SHIFT);
|
|
}
|
|
else
|
|
{
|
|
/* ERASE_GRP_SIZE: Bit 42~46, ERASE_GRP_MULT: Bit 37~41 */
|
|
*(rt_uint32_t *)buffer = \
|
|
((rt_uint16_t)((buf_res[10] & 0x7c) >> 2) + 1) * \
|
|
(((buf_res[10] & 0x03) << 3) + \
|
|
((buf_res[11] & 0xe0) >> 5) + 1);
|
|
}
|
|
}
|
|
ret = RT_EOK;
|
|
break;
|
|
|
|
case RT_DEVICE_CTRL_SD_GET_TYPE:
|
|
/* Get card type flags (1 byte) */
|
|
*(rt_uint8_t *)buffer = sdType;
|
|
ret = RT_EOK;
|
|
break;
|
|
|
|
case RT_DEVICE_CTRL_SD_GET_CSD:
|
|
/* Receive CSD as a data block (16 bytes) */
|
|
if (efm_spiSd_sendCmd(CMD9, 0x00000000, buffer))
|
|
{
|
|
sdcard_debug("SPISD: Get CSD failed!\n");
|
|
break;
|
|
}
|
|
ret = RT_EOK;
|
|
break;
|
|
|
|
case RT_DEVICE_CTRL_SD_GET_CID:
|
|
/* Receive CID as a data block (16 bytes) */
|
|
if (efm_spiSd_sendCmd(CMD10, 0x00000000, buffer))
|
|
{
|
|
sdcard_debug("SPISD: Get CID failed!\n");
|
|
break;
|
|
}
|
|
ret = RT_EOK;
|
|
break;
|
|
|
|
case RT_DEVICE_CTRL_SD_GET_OCR:
|
|
/* Receive OCR as an R3 resp (4 bytes) */
|
|
if (efm_spiSd_sendCmd(CMD58, 0x00000000, buffer))
|
|
{
|
|
sdcard_debug("SPISD: Get OCR failed!\n");
|
|
break;
|
|
}
|
|
ret = RT_EOK;
|
|
break;
|
|
|
|
case RT_DEVICE_CTRL_SD_GET_SDSTAT:
|
|
/* Receive SD statsu as a data block (64 bytes) */
|
|
if (efm_spiSd_sendCmd(ACMD13, 0x00000000, buffer))
|
|
{
|
|
sdcard_debug("SPISD: Get SD status failed!\n");
|
|
break;
|
|
}
|
|
if (efm_spiSd_readBlock(buffer, SD_BLOCK_SIZE_SDSTAT))
|
|
{
|
|
sdcard_debug("SPISD: Read SD status failed!\n");
|
|
break;
|
|
}
|
|
ret = RT_EOK;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (buf_res)
|
|
{
|
|
rt_free(buf_res);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Initialize all memory card related hardware and register the device to
|
|
* kernel
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @return
|
|
* Error code
|
|
******************************************************************************/
|
|
rt_err_t efm_spiSd_init(void)
|
|
{
|
|
struct efm32_usart_device_t *usart;
|
|
|
|
do
|
|
{
|
|
/* Find SPI device */
|
|
spi = rt_device_find(SPISD_USING_DEVICE_NAME);
|
|
if (spi == RT_NULL)
|
|
{
|
|
sdcard_debug("SPISD: Can't find device %s!\n",
|
|
SPISD_USING_DEVICE_NAME);
|
|
break;
|
|
}
|
|
sdcard_debug("SPISD: Find device %s\n", SPISD_USING_DEVICE_NAME);
|
|
|
|
/* Config chip slect pin */
|
|
usart = (struct efm32_usart_device_t *)(spi->user_data);
|
|
if (!(usart->state & USART_STATE_AUTOCS))
|
|
{
|
|
GPIO_PinModeSet(SD_CS_PORT, SD_CS_PIN, gpioModePushPull, 1);
|
|
sdAutoCs = false;
|
|
}
|
|
|
|
/* Register SPI SD device */
|
|
sd_device.type = RT_Device_Class_MTD;
|
|
sd_device.init = rt_spiSd_init;
|
|
sd_device.open = rt_spiSd_open;
|
|
sd_device.close = rt_spiSd_close;
|
|
sd_device.read = rt_spiSd_read;
|
|
sd_device.write = rt_spiSd_write;
|
|
sd_device.control = rt_spiSd_control;
|
|
sd_device.user_data = RT_NULL;
|
|
rt_device_register(
|
|
&sd_device,
|
|
SPISD_DEVICE_NAME,
|
|
RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE | RT_DEVICE_FLAG_STANDALONE);
|
|
|
|
sdcard_debug("SPISD: HW init OK, card type %x\n", sdType);
|
|
return RT_EOK;
|
|
} while (0);
|
|
|
|
/* Release buffer */
|
|
rt_kprintf("SPISD: HW init failed!\n");
|
|
return -RT_ERROR;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* De-initialize memory card device
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
******************************************************************************/
|
|
void efm_spiSd_deinit(void)
|
|
{
|
|
/* Close SPI device */
|
|
if (spi != RT_NULL)
|
|
{
|
|
spi->close(spi);
|
|
spi = RT_NULL;
|
|
sdcard_debug("SPISD: Close device %s\n", SPISD_USING_DEVICE_NAME);
|
|
}
|
|
/* Delete timer */
|
|
if (sdTimer != RT_NULL)
|
|
{
|
|
rt_timer_delete(sdTimer);
|
|
sdTimer = RT_NULL;
|
|
sdcard_debug("SPISD: Delete timer\n");
|
|
}
|
|
|
|
sdcard_debug("SPISD: Deinit OK\n");
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Export to FINSH
|
|
******************************************************************************/
|
|
#ifdef RT_USING_FINSH
|
|
#include <finsh.h>
|
|
|
|
void list_sd(void)
|
|
{
|
|
rt_uint8_t buf_res[16];
|
|
rt_uint32_t capacity, temp32;
|
|
rt_uint16_t temp16;
|
|
|
|
rt_kprintf(" SD Card on %s\n", SPISD_USING_DEVICE_NAME);
|
|
rt_kprintf(" ------------------------------\n");
|
|
sd_device.control(&sd_device, RT_DEVICE_CTRL_SD_GET_CID, buf_res);
|
|
rt_kprintf(" Manufacturer ID:\t%x\n", buf_res[0]);
|
|
rt_kprintf(" OEM/Application ID:\t%x%x\n", buf_res[1], buf_res[2]);
|
|
rt_kprintf(" Product revision:\t%x\n", buf_res[8]);
|
|
buf_res[8] = 0;
|
|
rt_kprintf(" Product name:\t\t%s\n", &buf_res[3]);
|
|
rt_kprintf(" Serial number:\t\t%x%x%x%x\n", \
|
|
buf_res[9], buf_res[10], buf_res[11], buf_res[12]);
|
|
rt_kprintf(" Manufacturing date:\t%d.%d\n", \
|
|
2000 + ((buf_res[13] & 0x0F) << 4) + ((buf_res[14] & 0xF0) >> 4), \
|
|
buf_res[14] & 0x0F);
|
|
rt_kprintf(" Card type:\t\t");
|
|
sd_device.control(&sd_device, RT_DEVICE_CTRL_SD_GET_TYPE, buf_res);
|
|
if (buf_res[0] == CT_MMC)
|
|
{
|
|
rt_kprintf("%s\n", "MMC");
|
|
}
|
|
else if (buf_res[0] == CT_SDC)
|
|
{
|
|
rt_kprintf("%s\n", "SDXC");
|
|
}
|
|
else if (buf_res[0] == CT_SD1)
|
|
{
|
|
rt_kprintf("%s\n", "SDSC");
|
|
}
|
|
else if (buf_res[0] == CT_SD2)
|
|
{
|
|
rt_kprintf("%s\n", "SDHC");
|
|
}
|
|
sd_device.control(&sd_device, RT_DEVICE_CTRL_SD_GET_SSIZE, &temp16);
|
|
sd_device.control(&sd_device, RT_DEVICE_CTRL_SD_GET_SCOUNT, &temp32);
|
|
capacity = ((temp32 & 0x0000FFFF) * temp16) >> 16;
|
|
capacity += ((temp32 >> 16) * temp16);
|
|
capacity >>= 4;
|
|
rt_kprintf(" Card capacity:\t\t%dMB\n", capacity);
|
|
}
|
|
FINSH_FUNCTION_EXPORT(list_sd, list the SD card.)
|
|
#endif
|
|
|
|
#endif /* defined(EFM32_USING_SPISD) */
|
|
/***************************************************************************//**
|
|
* @}
|
|
******************************************************************************/
|