
Implemented F, H and R parameters to M106 command. The second fan output on a Duet 0.8.5 now defaults to being a thermostatic fan at power up. Improved speed of file upload to SD card G32 is now allowed if the printer has not been homed, if there is a bed.g file G30 commands are no longer allowed on a delta that has not been homed M572 parameter P (drive number) replaced by parameter D (extruder number) File info requests are now processed in stages to reduce impact on printing (thanks chrishamm) Use latest network stack and webserver modules from chrishamm (thanks chrishamm) Added Roland mill support (thanks RRP/chrishamm) Added S parameter (idle timeout) to M18 ans M84 commands (thanks chrishamm) Moved I/O pin assignments to separate Pins.h file to more easily support alternative hardware (thanks dnewman) Bug fix: filament usage and % print complete figures were incorrect when absolute extruder coordinates were used Bug fix: file-based print estimate was occasionally returned as 'inf' which caused the web interface to disconnect Bug fix: M666 now flags all towers as not homed Bug fixes to extruder pressure compensation (M572 command).
2130 lines
55 KiB
C
2130 lines
55 KiB
C
/**
|
|
* \file
|
|
*
|
|
* \brief Common SD/MMC stack
|
|
*
|
|
* Copyright (c) 2012-2015 Atmel Corporation. All rights reserved.
|
|
*
|
|
* \asf_license_start
|
|
*
|
|
* \page License
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* 3. The name of Atmel may not be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* 4. This software may only be redistributed and used in connection with an
|
|
* Atmel microcontroller product.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
|
|
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
|
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* \asf_license_stop
|
|
*
|
|
*/
|
|
/*
|
|
* Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include "../SD_HSMCI.h"
|
|
#include "sd_mmc_protocol.h"
|
|
#include "sd_mmc.h"
|
|
|
|
#ifdef FREERTOS_USED
|
|
#include "FreeRTOS.h"
|
|
#include "task.h"
|
|
#include "portmacro.h"
|
|
#include "projdefs.h"
|
|
#endif
|
|
|
|
/**
|
|
* \ingroup sd_mmc_stack
|
|
* \defgroup sd_mmc_stack_internal Implementation of SD/MMC/SDIO Stack
|
|
* @{
|
|
*/
|
|
|
|
// Enable debug information for SD/MMC module
|
|
#ifdef SD_MMC_DEBUG
|
|
# define sd_mmc_debug(...) debugPrintf(__VA_ARGS__)
|
|
#else
|
|
# define sd_mmc_debug(...)
|
|
#endif
|
|
|
|
#ifndef SD_MMC_SPI_MEM_CNT
|
|
# define SD_MMC_SPI_MEM_CNT 0
|
|
#endif
|
|
#ifndef SD_MMC_MCI_MEM_CNT
|
|
# define SD_MMC_MCI_MEM_CNT 0
|
|
#endif
|
|
#ifndef SD_MMC_HSMCI_MEM_CNT
|
|
# define SD_MMC_HSMCI_MEM_CNT 0
|
|
#endif
|
|
|
|
// Macros to switch SD MMC stack to the correct driver (MCI or SPI)
|
|
#ifdef SD_MMC_SPI_MODE
|
|
# if (SD_MMC_SPI_MEM_CNT != 0)
|
|
# include "sd_mmc_spi.h"
|
|
# define driver sd_mmc_spi
|
|
# define SD_MMC_MEM_CNT SD_MMC_SPI_MEM_CNT
|
|
# define sd_mmc_is_spi() true
|
|
# else
|
|
# error No SPI interface is defined for SD MMC stack. \
|
|
SD_MMC_SPI_MEM_CNT must be added in board.h file.
|
|
# endif
|
|
#else
|
|
# if (SD_MMC_HSMCI_MEM_CNT != 0)
|
|
# include "hsmci.h"
|
|
# define driver hsmci
|
|
# define SD_MMC_MEM_CNT SD_MMC_HSMCI_MEM_CNT
|
|
# define sd_mmc_is_spi() false
|
|
# elif (SD_MMC_MCI_MEM_CNT != 0)
|
|
# include "mci.h"
|
|
# define driver mci
|
|
# define SD_MMC_MEM_CNT SD_MMC_MCI_MEM_CNT
|
|
# define sd_mmc_is_spi() false
|
|
# else
|
|
# error No MCI or HSMCI interfaces are defined for SD MMC stack. \
|
|
SD_MMC_MCI_MEM_CNT or SD_MMC_HSMCI_MEM_CNT must be added in board.h file.
|
|
# endif
|
|
#endif
|
|
|
|
|
|
#define driver_init ATPASTE2(driver, _init)
|
|
#define driver_select_device ATPASTE2(driver, _select_device)
|
|
#define driver_deselect_device ATPASTE2(driver, _deselect_device)
|
|
#define driver_get_bus_width ATPASTE2(driver, _get_bus_width)
|
|
#define driver_is_high_speed_capable ATPASTE2(driver, _is_high_speed_capable)
|
|
#define driver_send_clock ATPASTE2(driver, _send_clock)
|
|
#define driver_send_cmd ATPASTE2(driver, _send_cmd)
|
|
#define driver_get_response ATPASTE2(driver, _get_response)
|
|
#define driver_get_response_128 ATPASTE2(driver, _get_response_128)
|
|
#define driver_adtc_start ATPASTE2(driver, _adtc_start)
|
|
#define driver_adtc_stop ATPASTE2(driver, _send_cmd)
|
|
#define driver_read_word ATPASTE2(driver, _read_word)
|
|
#define driver_write_word ATPASTE2(driver, _write_word)
|
|
#define driver_start_read_blocks ATPASTE2(driver, _start_read_blocks)
|
|
#define driver_wait_end_of_read_blocks ATPASTE2(driver, _wait_end_of_read_blocks)
|
|
#define driver_start_write_blocks ATPASTE2(driver, _start_write_blocks)
|
|
#define driver_wait_end_of_write_blocks ATPASTE2(driver, _wait_end_of_write_blocks)
|
|
|
|
|
|
#if (!defined SD_MMC_0_CD_GPIO) || (!defined SD_MMC_0_CD_DETECT_VALUE)
|
|
# warning No pin for card detection has been defined in board.h. \
|
|
The define SD_MMC_0_CD_GPIO, SD_MMC_0_CD_DETECT_VALUE must be added in board.h file.
|
|
#endif
|
|
|
|
#ifdef SDIO_SUPPORT_ENABLE
|
|
# define IS_SDIO() (sd_mmc_card->type & CARD_TYPE_SDIO)
|
|
#else
|
|
# define IS_SDIO() false
|
|
#endif
|
|
|
|
#define sd_mmc_is_mci() (!sd_mmc_is_spi())
|
|
|
|
//! This SD MMC stack supports only the high voltage
|
|
#define SD_MMC_VOLTAGE_SUPPORT \
|
|
(OCR_VDD_27_28 | OCR_VDD_28_29 | \
|
|
OCR_VDD_29_30 | OCR_VDD_30_31 | \
|
|
OCR_VDD_31_32 | OCR_VDD_32_33)
|
|
|
|
//! SD/MMC card states
|
|
enum card_state {
|
|
SD_MMC_CARD_STATE_READY = 0, //!< Ready to use
|
|
SD_MMC_CARD_STATE_DEBOUNCE = 1, //!< Debounce on going
|
|
SD_MMC_CARD_STATE_INIT = 2, //!< Initialization on going
|
|
SD_MMC_CARD_STATE_UNUSABLE = 3, //!< Unusable card
|
|
SD_MMC_CARD_STATE_NO_CARD = 4, //!< No SD/MMC card inserted
|
|
};
|
|
|
|
//! SD/MMC card information structure
|
|
struct sd_mmc_card {
|
|
uint32_t clock; //!< Card access clock
|
|
uint32_t capacity; //!< Card capacity in KBytes
|
|
#if (defined SD_MMC_0_CD_GPIO)
|
|
uint32_t cd_gpio; //!< Card detect GPIO
|
|
# if (defined SD_MMC_0_WP_GPIO)
|
|
uint32_t wp_gpio; //!< Card write protection GPIO
|
|
# endif
|
|
#endif
|
|
uint16_t rca; //!< Relative card address
|
|
enum card_state state; //!< Card state
|
|
card_type_t type; //!< Card type
|
|
card_version_t version; //!< Card version
|
|
uint8_t bus_width; //!< Number of DATA lines on bus (MCI only)
|
|
uint8_t csd[CSD_REG_BSIZE];//!< CSD register
|
|
uint8_t high_speed; //!< High speed card (1)
|
|
};
|
|
|
|
//! SD/MMC card list
|
|
//! Note: Initialize card detect pin fields if present
|
|
static struct sd_mmc_card sd_mmc_cards[SD_MMC_MEM_CNT]
|
|
#if (defined SD_MMC_0_CD_GPIO) && (defined SD_MMC_0_WP_GPIO)
|
|
= {
|
|
# define SD_MMC_CD_WP(slot, unused) \
|
|
{.cd_gpio = SD_MMC_##slot##_CD_GPIO, \
|
|
.wp_gpio = SD_MMC_##slot##_WP_GPIO},
|
|
MREPEAT(SD_MMC_MEM_CNT, SD_MMC_CD_WP, ~)
|
|
# undef SD_MMC_CD_WP
|
|
}
|
|
#elif (defined SD_MMC_0_CD_GPIO)
|
|
= {
|
|
# define SD_MMC_CD(slot, unused) \
|
|
{.cd_gpio = SD_MMC_##slot##_CD_GPIO},
|
|
MREPEAT(SD_MMC_MEM_CNT, SD_MMC_CD, ~)
|
|
# undef SD_MMC_CD
|
|
}
|
|
#endif
|
|
;
|
|
|
|
//! Index of current slot configurated
|
|
static uint8_t sd_mmc_slot_sel;
|
|
//! Pointer on current slot configurated
|
|
static struct sd_mmc_card *sd_mmc_card;
|
|
//! Number of block to read or write on the current transfer
|
|
static uint16_t sd_mmc_nb_block_to_tranfer = 0;
|
|
//! Number of block remaining to read or write on the current transfer
|
|
static uint16_t sd_mmc_nb_block_remaining = 0;
|
|
|
|
//! SD/MMC transfer rate unit codes (10K) list
|
|
const uint32_t sd_mmc_trans_units[7] = {
|
|
10, 100, 1000, 10000, 0, 0, 0
|
|
};
|
|
//! SD transfer multiplier factor codes (1/10) list
|
|
const uint32_t sd_trans_multipliers[16] = {
|
|
0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80
|
|
};
|
|
//! MMC transfer multiplier factor codes (1/10) list
|
|
const uint32_t mmc_trans_multipliers[16] = {
|
|
0, 10, 12, 13, 15, 20, 26, 30, 35, 40, 45, 52, 55, 60, 70, 80
|
|
};
|
|
|
|
//! \name MMC, SD and SDIO commands process
|
|
//! @{
|
|
static bool mmc_spi_op_cond(void);
|
|
static bool mmc_mci_op_cond(void);
|
|
static bool sd_spi_op_cond(uint8_t v2);
|
|
static bool sd_mci_op_cond(uint8_t v2);
|
|
static bool sdio_op_cond(void);
|
|
static bool sdio_get_max_speed(void);
|
|
static bool sdio_cmd52_set_bus_width(void);
|
|
static bool sdio_cmd52_set_high_speed(void);
|
|
static bool sd_cm6_set_high_speed(void);
|
|
static bool mmc_cmd6_set_bus_width(uint8_t bus_width);
|
|
static bool mmc_cmd6_set_high_speed(void);
|
|
static bool sd_cmd8(uint8_t * v2);
|
|
static bool mmc_cmd8(uint8_t *b_authorize_high_speed);
|
|
static bool sd_mmc_cmd9_spi(void);
|
|
static bool sd_mmc_cmd9_mci(void);
|
|
static void mmc_decode_csd(void);
|
|
static void sd_decode_csd(void);
|
|
static bool sd_mmc_cmd13(void);
|
|
#ifdef SDIO_SUPPORT_ENABLE
|
|
static bool sdio_cmd52(uint8_t rw_flag, uint8_t func_nb,
|
|
uint32_t reg_addr, uint8_t rd_after_wr, uint8_t *io_data);
|
|
static bool sdio_cmd53(uint8_t rw_flag, uint8_t func_nb, uint32_t reg_addr,
|
|
uint8_t inc_addr, uint32_t size, bool access_block);
|
|
#endif // SDIO_SUPPORT_ENABLE
|
|
static bool sd_acmd6(void);
|
|
static bool sd_acmd51(void);
|
|
//! @}
|
|
|
|
//! \name Internal function to process the initialization and install
|
|
//! @{
|
|
static sd_mmc_err_t sd_mmc_select_slot(uint8_t slot);
|
|
static void sd_mmc_configure_slot(void);
|
|
static void sd_mmc_deselect_slot(void);
|
|
static bool sd_mmc_spi_card_init(void);
|
|
static bool sd_mmc_mci_card_init(void);
|
|
static bool sd_mmc_spi_install_mmc(void);
|
|
static bool sd_mmc_mci_install_mmc(void);
|
|
//! @}
|
|
|
|
|
|
//! \name Internal functions to manage a large timeout after a card insertion
|
|
//! @{
|
|
#define SD_MMC_DEBOUNCE_TIMEOUT 1000 // Unit ms
|
|
|
|
#if XMEGA || defined(REPRAPFIRMWARE)
|
|
# define SD_MMC_START_TIMEOUT() delay_ms(SD_MMC_DEBOUNCE_TIMEOUT)
|
|
# define SD_MMC_IS_TIMEOUT() true
|
|
# define SD_MMC_STOP_TIMEOUT()
|
|
#endif
|
|
|
|
#if UC3
|
|
static t_cpu_time timer;
|
|
# define SD_MMC_START_TIMEOUT() \
|
|
cpu_set_timeout(cpu_ms_2_cy(SD_MMC_DEBOUNCE_TIMEOUT, sysclk_get_cpu_hz()), &timer)
|
|
# define SD_MMC_IS_TIMEOUT() \
|
|
cpu_is_timeout(&timer)
|
|
# define SD_MMC_STOP_TIMEOUT()
|
|
#endif
|
|
|
|
#if SAM && !defined(REPRAPFIRMWARE)
|
|
static bool sd_mmc_sam_systick_used;
|
|
# ifdef FREERTOS_USED
|
|
static xTimeOutType xTimeOut;
|
|
# endif
|
|
|
|
static inline void SD_MMC_START_TIMEOUT(void)
|
|
{
|
|
if (!SysTick->CTRL) {
|
|
sd_mmc_sam_systick_used = true;
|
|
SysTick->LOAD = (sysclk_get_cpu_hz() / (8 * 1000))
|
|
* SD_MMC_DEBOUNCE_TIMEOUT;
|
|
SysTick->CTRL = SysTick_CTRL_ENABLE_Msk;
|
|
} else {
|
|
sd_mmc_sam_systick_used = false;
|
|
#ifdef FREERTOS_USED
|
|
// Note: the define INCLUDE_vTaskDelay must be set to one
|
|
// in FreeRTOSConfig.h file.
|
|
vTaskSetTimeOutState(&xTimeOut);
|
|
#else
|
|
delay_ms(SD_MMC_DEBOUNCE_TIMEOUT);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static inline bool SD_MMC_IS_TIMEOUT(void)
|
|
{
|
|
if (!sd_mmc_sam_systick_used) {
|
|
#ifdef FREERTOS_USED
|
|
portTickType xTicksToWait =
|
|
SD_MMC_DEBOUNCE_TIMEOUT / portTICK_RATE_MS;
|
|
return (xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdTRUE);
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
if (SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) {
|
|
SysTick->CTRL = 0;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static inline void SD_MMC_STOP_TIMEOUT(void)
|
|
{
|
|
if (sd_mmc_sam_systick_used) {
|
|
SysTick->CTRL = 0;
|
|
}
|
|
}
|
|
#endif //SAM
|
|
//! @}
|
|
|
|
/**
|
|
* \brief Sends operation condition command and read OCR (SPI only)
|
|
* - CMD1 sends operation condition command
|
|
* - CMD58 reads OCR
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool mmc_spi_op_cond(void)
|
|
{
|
|
uint32_t retry, resp;
|
|
|
|
/*
|
|
* Timeout 1s = 400KHz / ((6+1)*8) cylces = 7150 retry
|
|
* 6 = cmd byte size
|
|
* 1 = response byte size
|
|
*/
|
|
retry = 7150;
|
|
do {
|
|
if (!driver_send_cmd(MMC_SPI_CMD1_SEND_OP_COND, 0)) {
|
|
sd_mmc_debug("%s: CMD1 SPI Fail - Busy retry %d\n\r",
|
|
__func__, (int)(7150 - retry));
|
|
return false;
|
|
}
|
|
// Check busy flag
|
|
resp = driver_get_response();
|
|
if (!(resp & R1_SPI_IDLE)) {
|
|
break;
|
|
}
|
|
if (retry-- == 0) {
|
|
sd_mmc_debug("%s: CMD1 Timeout on busy\n\r", __func__);
|
|
return false;
|
|
}
|
|
} while (1);
|
|
|
|
// Read OCR for SPI mode
|
|
if (!driver_send_cmd(SDMMC_SPI_CMD58_READ_OCR, 0)) {
|
|
sd_mmc_debug("%s: CMD58 Fail\n\r", __func__);
|
|
return false;
|
|
}
|
|
// Check OCR value
|
|
if ((driver_get_response() & OCR_ACCESS_MODE_MASK)
|
|
== OCR_ACCESS_MODE_SECTOR) {
|
|
sd_mmc_card->type |= CARD_TYPE_HC;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Sends operation condition command and read OCR (MCI only)
|
|
* - CMD1 sends operation condition command
|
|
* - CMD1 reads OCR
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool mmc_mci_op_cond(void)
|
|
{
|
|
uint32_t retry, resp;
|
|
|
|
/*
|
|
* Timeout 1s = 400KHz / ((6+6)*8) cylces = 4200 retry
|
|
* 6 = cmd byte size
|
|
* 6 = response byte size
|
|
*/
|
|
retry = 4200;
|
|
do {
|
|
if (!driver_send_cmd(MMC_MCI_CMD1_SEND_OP_COND,
|
|
SD_MMC_VOLTAGE_SUPPORT | OCR_ACCESS_MODE_SECTOR)) {
|
|
sd_mmc_debug("%s: CMD1 MCI Fail - Busy retry %d\n\r",
|
|
__func__, (int)(4200 - retry));
|
|
return false;
|
|
}
|
|
// Check busy flag
|
|
resp = driver_get_response();
|
|
if (resp & OCR_POWER_UP_BUSY) {
|
|
// Check OCR value
|
|
if ((resp & OCR_ACCESS_MODE_MASK)
|
|
== OCR_ACCESS_MODE_SECTOR) {
|
|
sd_mmc_card->type |= CARD_TYPE_HC;
|
|
}
|
|
break;
|
|
}
|
|
if (retry-- == 0) {
|
|
sd_mmc_debug("%s: CMD1 Timeout on busy\n\r", __func__);
|
|
return false;
|
|
}
|
|
} while (1);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Ask to all cards to send their operations conditions (SPI only).
|
|
* - ACMD41 sends operation condition command.
|
|
* - CMD58 reads OCR
|
|
*
|
|
* \param v2 Shall be 1 if it is a SD card V2
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool sd_spi_op_cond(uint8_t v2)
|
|
{
|
|
uint32_t arg, retry, resp;
|
|
|
|
/*
|
|
* Timeout 1s = 400KHz / ((6+1)*8) cylces = 7150 retry
|
|
* 6 = cmd byte size
|
|
* 1 = response byte size
|
|
*/
|
|
retry = 7150;
|
|
do {
|
|
// CMD55 - Indicate to the card that the next command is an
|
|
// application specific command rather than a standard command.
|
|
if (!driver_send_cmd(SDMMC_CMD55_APP_CMD, 0)) {
|
|
sd_mmc_debug("%s: CMD55 Fail\n\r", __func__);
|
|
return false;
|
|
}
|
|
|
|
// (ACMD41) Sends host OCR register
|
|
arg = 0;
|
|
if (v2) {
|
|
arg |= SD_ACMD41_HCS;
|
|
}
|
|
// Check response
|
|
if (!driver_send_cmd(SD_SPI_ACMD41_SD_SEND_OP_COND, arg)) {
|
|
sd_mmc_debug("%s: ACMD41 Fail\n\r", __func__);
|
|
return false;
|
|
}
|
|
resp = driver_get_response();
|
|
if (!(resp & R1_SPI_IDLE)) {
|
|
// Card is ready
|
|
break;
|
|
}
|
|
if (retry-- == 0) {
|
|
sd_mmc_debug("%s: ACMD41 Timeout on busy, resp32 0x%08x \n\r",
|
|
__func__, resp);
|
|
return false;
|
|
}
|
|
} while (1);
|
|
|
|
// Read OCR for SPI mode
|
|
if (!driver_send_cmd(SDMMC_SPI_CMD58_READ_OCR, 0)) {
|
|
sd_mmc_debug("%s: CMD58 Fail\n\r", __func__);
|
|
return false;
|
|
}
|
|
if ((driver_get_response() & OCR_CCS) != 0) {
|
|
sd_mmc_card->type |= CARD_TYPE_HC;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Ask to all cards to send their operations conditions (MCI only).
|
|
* - ACMD41 sends operation condition command.
|
|
* - ACMD41 reads OCR
|
|
*
|
|
* \param v2 Shall be 1 if it is a SD card V2
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool sd_mci_op_cond(uint8_t v2)
|
|
{
|
|
uint32_t arg, retry, resp;
|
|
|
|
/*
|
|
* Timeout 1s = 400KHz / ((6+6+6+6)*8) cylces = 2100 retry
|
|
* 6 = cmd byte size
|
|
* 6 = response byte size
|
|
* 6 = cmd byte size
|
|
* 6 = response byte size
|
|
*/
|
|
retry = 2100;
|
|
do {
|
|
// CMD55 - Indicate to the card that the next command is an
|
|
// application specific command rather than a standard command.
|
|
if (!driver_send_cmd(SDMMC_CMD55_APP_CMD, 0)) {
|
|
sd_mmc_debug("%s: CMD55 Fail\n\r", __func__);
|
|
return false;
|
|
}
|
|
|
|
// (ACMD41) Sends host OCR register
|
|
arg = SD_MMC_VOLTAGE_SUPPORT;
|
|
if (v2) {
|
|
arg |= SD_ACMD41_HCS;
|
|
}
|
|
// Check response
|
|
if (!driver_send_cmd(SD_MCI_ACMD41_SD_SEND_OP_COND, arg)) {
|
|
sd_mmc_debug("%s: ACMD41 Fail\n\r", __func__);
|
|
return false;
|
|
}
|
|
resp = driver_get_response();
|
|
if (resp & OCR_POWER_UP_BUSY) {
|
|
// Card is ready
|
|
if ((resp & OCR_CCS) != 0) {
|
|
sd_mmc_card->type |= CARD_TYPE_HC;
|
|
}
|
|
break;
|
|
}
|
|
if (retry-- == 0) {
|
|
sd_mmc_debug("%s: ACMD41 Timeout on busy, resp32 0x%08x \n\r",
|
|
__func__, resp);
|
|
return false;
|
|
}
|
|
} while (1);
|
|
return true;
|
|
}
|
|
|
|
#ifdef SDIO_SUPPORT_ENABLE
|
|
/**
|
|
* \brief Try to get the SDIO card's operating condition
|
|
* - CMD5 to read OCR NF field
|
|
* - CMD5 to wait OCR power up busy
|
|
* - CMD5 to read OCR MP field
|
|
* sd_mmc_card->type is updated
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool sdio_op_cond(void)
|
|
{
|
|
uint32_t resp;
|
|
|
|
// CMD5 - SDIO send operation condition (OCR) command.
|
|
if (!driver_send_cmd(SDIO_CMD5_SEND_OP_COND, 0)) {
|
|
sd_mmc_debug("%s: CMD5 Fail\n\r", __func__);
|
|
return true; // No error but card type not updated
|
|
}
|
|
resp = driver_get_response();
|
|
if ((resp & OCR_SDIO_NF) == 0) {
|
|
return true; // No error but card type not updated
|
|
}
|
|
|
|
/*
|
|
* Wait card ready
|
|
* Timeout 1s = 400KHz / ((6+4)*8) cylces = 5000 retry
|
|
* 6 = cmd byte size
|
|
* 4(SPI) 6(MCI) = response byte size
|
|
*/
|
|
uint32_t cmd5_retry = 5000;
|
|
while (1) {
|
|
// CMD5 - SDIO send operation condition (OCR) command.
|
|
if (!driver_send_cmd(SDIO_CMD5_SEND_OP_COND,
|
|
resp & SD_MMC_VOLTAGE_SUPPORT)) {
|
|
sd_mmc_debug("%s: CMD5 Fail\n\r", __func__);
|
|
return false;
|
|
}
|
|
resp = driver_get_response();
|
|
if ((resp & OCR_POWER_UP_BUSY) == OCR_POWER_UP_BUSY) {
|
|
break;
|
|
}
|
|
if (cmd5_retry-- == 0) {
|
|
sd_mmc_debug("%s: CMD5 Timeout on busy\n\r", __func__);
|
|
return false;
|
|
}
|
|
}
|
|
// Update card type at the end of busy
|
|
if ((resp & OCR_SDIO_MP) > 0) {
|
|
sd_mmc_card->type = CARD_TYPE_SD_COMBO;
|
|
} else {
|
|
sd_mmc_card->type = CARD_TYPE_SDIO;
|
|
}
|
|
return true; // No error and card type updated with SDIO type
|
|
}
|
|
|
|
/**
|
|
* \brief Get SDIO max transfer speed in Hz.
|
|
* - CMD53 reads CIS area address in CCCR area.
|
|
* - Nx CMD53 search Fun0 tuple in CIS area
|
|
* - CMD53 reads TPLFE_MAX_TRAN_SPEED in Fun0 tuple
|
|
* - Compute maximum speed of SDIO
|
|
* and update sd_mmc_card->clock
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool sdio_get_max_speed(void)
|
|
{
|
|
uint32_t addr, addr_cis;
|
|
uint8_t buf[6];
|
|
uint32_t unit;
|
|
uint32_t mul;
|
|
uint8_t tplfe_max_tran_speed;
|
|
|
|
// Read CIS area address in CCCR area
|
|
addr_cis = 0; // Init all bytes, because the next function fill 3 bytes only
|
|
if (!sdio_cmd53(SDIO_CMD53_READ_FLAG, SDIO_CIA, SDIO_CCCR_CIS_PTR,
|
|
1, 3, true)) {
|
|
sd_mmc_debug("%s: CMD53 Read CIS Fail\n\r", __func__);
|
|
return false;
|
|
}
|
|
if (!driver_start_read_blocks((uint8_t *)&addr_cis, 1)) {
|
|
return false;
|
|
}
|
|
if (!driver_wait_end_of_read_blocks()) {
|
|
return false;
|
|
}
|
|
addr_cis = le32_to_cpu(addr_cis);
|
|
|
|
// Search Fun0 tuple in the CIA area
|
|
addr = addr_cis;
|
|
while (1) {
|
|
// Read a sample of CIA area
|
|
if (!sdio_cmd53(SDIO_CMD53_READ_FLAG, SDIO_CIA, addr, 1, 3, true)) {
|
|
sd_mmc_debug("%s: CMD53 Read CIA Fail\n\r", __func__);
|
|
return false;
|
|
}
|
|
if (!driver_start_read_blocks(buf, 1)) {
|
|
return false;
|
|
}
|
|
if (!driver_wait_end_of_read_blocks()) {
|
|
return false;
|
|
}
|
|
if (buf[0] == SDIO_CISTPL_END) {
|
|
sd_mmc_debug("%s: CMD53 Tuple error\n\r", __func__);
|
|
return false; // Tuple error
|
|
}
|
|
if (buf[0] == SDIO_CISTPL_FUNCE && buf[2] == 0x00) {
|
|
break; // Fun0 tuple found
|
|
}
|
|
if (buf[1] == 0) {
|
|
sd_mmc_debug("%s: CMD53 Tuple error\n\r", __func__);
|
|
return false; // Tuple error
|
|
}
|
|
|
|
// Next address
|
|
addr += (buf[1] + 2);
|
|
if (addr > (addr_cis + 256)) {
|
|
sd_mmc_debug("%s: CMD53 Outoff CIS area\n\r", __func__);
|
|
return false; // Outoff CIS area
|
|
}
|
|
}
|
|
|
|
// Read all Fun0 tuple fields: fn0_blk_siz & max_tran_speed
|
|
if (!sdio_cmd53(SDIO_CMD53_READ_FLAG, SDIO_CIA, addr, 1, 6, true)) {
|
|
sd_mmc_debug("%s: CMD53 Read all Fun0 Fail\n\r", __func__);
|
|
return false;
|
|
}
|
|
if (!driver_start_read_blocks(buf, 1)) {
|
|
return false;
|
|
}
|
|
if (!driver_wait_end_of_read_blocks()) {
|
|
return false;
|
|
}
|
|
tplfe_max_tran_speed = buf[5];
|
|
if (tplfe_max_tran_speed > 0x32) {
|
|
/* Error on SDIO register, the high speed is not activated
|
|
* and the clock can not be more than 25MHz.
|
|
* This error is present on specific SDIO card
|
|
* (H&D wireless card - HDG104 WiFi SIP).
|
|
*/
|
|
tplfe_max_tran_speed = 0x32; // 25Mhz
|
|
}
|
|
|
|
// Decode transfer speed in Hz.
|
|
unit = sd_mmc_trans_units[tplfe_max_tran_speed & 0x7];
|
|
mul = sd_trans_multipliers[(tplfe_max_tran_speed >> 3) & 0xF];
|
|
sd_mmc_card->clock = unit * mul * 1000;
|
|
/**
|
|
* Note: A combo card shall be a Full-Speed SDIO card
|
|
* which supports upto 25MHz.
|
|
* A SDIO card alone can be:
|
|
* - a Low-Speed SDIO card which supports 400Khz minimum
|
|
* - a Full-Speed SDIO card which supports upto 25MHz
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief CMD52 for SDIO - Switches the bus width mode to 4
|
|
*
|
|
* \note sd_mmc_card->bus_width is updated.
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool sdio_cmd52_set_bus_width(void)
|
|
{
|
|
/**
|
|
* A SD memory card always supports bus 4bit
|
|
* A SD COMBO card always supports bus 4bit
|
|
* A SDIO Full-Speed alone always supports 4bit
|
|
* A SDIO Low-Speed alone can supports 4bit (Optional)
|
|
*/
|
|
uint8_t u8_value;
|
|
|
|
// Check 4bit support in 4BLS of "Card Capability" register
|
|
if (!sdio_cmd52(SDIO_CMD52_READ_FLAG, SDIO_CIA, SDIO_CCCR_CAP,
|
|
0, &u8_value)) {
|
|
return false;
|
|
}
|
|
if ((u8_value & SDIO_CAP_4BLS) != SDIO_CAP_4BLS) {
|
|
// No supported, it is not a protocol error
|
|
return true;
|
|
}
|
|
// HS mode possible, then enable
|
|
u8_value = SDIO_BUSWIDTH_4B;
|
|
if (!sdio_cmd52(SDIO_CMD52_WRITE_FLAG, SDIO_CIA, SDIO_CCCR_BUS_CTRL,
|
|
1, &u8_value)) {
|
|
return false;
|
|
}
|
|
sd_mmc_card->bus_width = 4;
|
|
sd_mmc_debug("%d-bit bus width enabled.\n\r", (int)sd_mmc_card->bus_width);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief CMD52 for SDIO - Enable the high speed mode
|
|
*
|
|
* \note sd_mmc_card->high_speed is updated.
|
|
* \note sd_mmc_card->clock is updated.
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool sdio_cmd52_set_high_speed(void)
|
|
{
|
|
uint8_t u8_value;
|
|
|
|
// Check CIA.HS
|
|
if (!sdio_cmd52(SDIO_CMD52_READ_FLAG, SDIO_CIA, SDIO_CCCR_HS, 0, &u8_value)) {
|
|
return false;
|
|
}
|
|
if ((u8_value & SDIO_SHS) != SDIO_SHS) {
|
|
// No supported, it is not a protocol error
|
|
return true;
|
|
}
|
|
// HS mode possible, then enable
|
|
u8_value = SDIO_EHS;
|
|
if (!sdio_cmd52(SDIO_CMD52_WRITE_FLAG, SDIO_CIA, SDIO_CCCR_HS,
|
|
1, &u8_value)) {
|
|
return false;
|
|
}
|
|
sd_mmc_card->high_speed = 1;
|
|
sd_mmc_card->clock *= 2;
|
|
return true;
|
|
}
|
|
|
|
#else
|
|
static bool sdio_op_cond(void)
|
|
{
|
|
return true; // No error but card type not updated
|
|
}
|
|
static bool sdio_get_max_speed(void)
|
|
{
|
|
return false;
|
|
}
|
|
static bool sdio_cmd52_set_bus_width(void)
|
|
{
|
|
return false;
|
|
}
|
|
static bool sdio_cmd52_set_high_speed(void)
|
|
{
|
|
return false;
|
|
}
|
|
#endif // SDIO_SUPPORT_ENABLE
|
|
|
|
/**
|
|
* \brief CMD6 for SD - Switch card in high speed mode
|
|
*
|
|
* \note CMD6 for SD is valid under the "trans" state.
|
|
* \note sd_mmc_card->high_speed is updated.
|
|
* \note sd_mmc_card->clock is updated.
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool sd_cm6_set_high_speed(void)
|
|
{
|
|
uint8_t switch_status[SD_SW_STATUS_BSIZE];
|
|
|
|
if (!driver_adtc_start(SD_CMD6_SWITCH_FUNC,
|
|
SD_CMD6_MODE_SWITCH
|
|
| SD_CMD6_GRP6_NO_INFLUENCE
|
|
| SD_CMD6_GRP5_NO_INFLUENCE
|
|
| SD_CMD6_GRP4_NO_INFLUENCE
|
|
| SD_CMD6_GRP3_NO_INFLUENCE
|
|
| SD_CMD6_GRP2_DEFAULT
|
|
| SD_CMD6_GRP1_HIGH_SPEED,
|
|
SD_SW_STATUS_BSIZE, 1, true)) {
|
|
return false;
|
|
}
|
|
if (!driver_start_read_blocks(switch_status, 1)) {
|
|
return false;
|
|
}
|
|
if (!driver_wait_end_of_read_blocks()) {
|
|
return false;
|
|
}
|
|
|
|
if (driver_get_response() & CARD_STATUS_SWITCH_ERROR) {
|
|
sd_mmc_debug("%s: CMD6 CARD_STATUS_SWITCH_ERROR\n\r", __func__);
|
|
return false;
|
|
}
|
|
if (SD_SW_STATUS_FUN_GRP1_RC(switch_status)
|
|
== SD_SW_STATUS_FUN_GRP_RC_ERROR) {
|
|
// No supported, it is not a protocol error
|
|
return true;
|
|
}
|
|
if (SD_SW_STATUS_FUN_GRP1_BUSY(switch_status)) {
|
|
sd_mmc_debug("%s: CMD6 SD_SW_STATUS_FUN_GRP1_BUSY\n\r", __func__);
|
|
return false;
|
|
}
|
|
// CMD6 function switching period is within 8 clocks
|
|
// after the end bit of status data.
|
|
driver_send_clock();
|
|
sd_mmc_card->high_speed = 1;
|
|
sd_mmc_card->clock *= 2;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief CMD6 for MMC - Switches the bus width mode
|
|
*
|
|
* \note CMD6 is valid under the "trans" state.
|
|
* \note sd_mmc_card->bus_width is updated.
|
|
*
|
|
* \param bus_width Bus width to set
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool mmc_cmd6_set_bus_width(uint8_t bus_width)
|
|
{
|
|
uint32_t arg;
|
|
|
|
switch (bus_width) {
|
|
case 8:
|
|
arg = MMC_CMD6_ACCESS_SET_BITS
|
|
| MMC_CMD6_INDEX_BUS_WIDTH
|
|
| MMC_CMD6_VALUE_BUS_WIDTH_8BIT;
|
|
break;
|
|
case 4:
|
|
arg = MMC_CMD6_ACCESS_SET_BITS
|
|
| MMC_CMD6_INDEX_BUS_WIDTH
|
|
| MMC_CMD6_VALUE_BUS_WIDTH_4BIT;
|
|
break;
|
|
default:
|
|
arg = MMC_CMD6_ACCESS_SET_BITS
|
|
| MMC_CMD6_INDEX_BUS_WIDTH
|
|
| MMC_CMD6_VALUE_BUS_WIDTH_1BIT;
|
|
break;
|
|
}
|
|
if (!driver_send_cmd(MMC_CMD6_SWITCH, arg)) {
|
|
return false;
|
|
}
|
|
if (driver_get_response() & CARD_STATUS_SWITCH_ERROR) {
|
|
// No supported, it is not a protocol error
|
|
sd_mmc_debug("%s: CMD6 CARD_STATUS_SWITCH_ERROR\n\r", __func__);
|
|
return false;
|
|
}
|
|
sd_mmc_card->bus_width = bus_width;
|
|
sd_mmc_debug("%d-bit bus width enabled.\n\r", (int)sd_mmc_card->bus_width);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief CMD6 for MMC - Switches in high speed mode
|
|
*
|
|
* \note CMD6 is valid under the "trans" state.
|
|
* \note sd_mmc_card->high_speed is updated.
|
|
* \note sd_mmc_card->clock is updated.
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool mmc_cmd6_set_high_speed(void)
|
|
{
|
|
if (!driver_send_cmd(MMC_CMD6_SWITCH,
|
|
MMC_CMD6_ACCESS_WRITE_BYTE
|
|
| MMC_CMD6_INDEX_HS_TIMING
|
|
| MMC_CMD6_VALUE_HS_TIMING_ENABLE)) {
|
|
return false;
|
|
}
|
|
if (driver_get_response() & CARD_STATUS_SWITCH_ERROR) {
|
|
// No supported, it is not a protocol error
|
|
sd_mmc_debug("%s: CMD6 CARD_STATUS_SWITCH_ERROR\n\r", __func__);
|
|
return false;
|
|
}
|
|
sd_mmc_card->high_speed = 1;
|
|
sd_mmc_card->clock = 52000000lu;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief CMD8 for SD card - Send Interface Condition Command.
|
|
*
|
|
* \note
|
|
* Send SD Memory Card interface condition, which includes host supply
|
|
* voltage information and asks the card whether card supports voltage.
|
|
* Should be performed at initialization time to detect the card type.
|
|
*
|
|
* \param v2 Pointer to v2 flag to update
|
|
*
|
|
* \return true if success, otherwise false
|
|
* with a update of \ref sd_mmc_err.
|
|
*/
|
|
static bool sd_cmd8(uint8_t * v2)
|
|
{
|
|
uint32_t resp;
|
|
|
|
*v2 = 0;
|
|
// Test for SD version 2
|
|
if (!driver_send_cmd(SD_CMD8_SEND_IF_COND,
|
|
SD_CMD8_PATTERN | SD_CMD8_HIGH_VOLTAGE)) {
|
|
return true; // It is not a V2
|
|
}
|
|
// Check R7 response
|
|
resp = driver_get_response();
|
|
if (resp == 0xFFFFFFFF) {
|
|
// No compliance R7 value
|
|
return true; // It is not a V2
|
|
}
|
|
if ((resp & (SD_CMD8_MASK_PATTERN | SD_CMD8_MASK_VOLTAGE))
|
|
!= (SD_CMD8_PATTERN | SD_CMD8_HIGH_VOLTAGE)) {
|
|
sd_mmc_debug("%s: CMD8 resp32 0x%08x UNUSABLE CARD\n\r",
|
|
__func__, resp);
|
|
return false;
|
|
}
|
|
sd_mmc_debug("SD card V2\n\r");
|
|
*v2 = 1;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief CMD8 - The card sends its EXT_CSD register as a block of data.
|
|
*
|
|
* \param b_authorize_high_speed Pointer to update with the high speed
|
|
* support information
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool mmc_cmd8(uint8_t *b_authorize_high_speed)
|
|
{
|
|
uint16_t i;
|
|
uint32_t ext_csd;
|
|
uint32_t sec_count;
|
|
|
|
if (!driver_adtc_start(MMC_CMD8_SEND_EXT_CSD, 0,
|
|
EXT_CSD_BSIZE, 1, false)) {
|
|
return false;
|
|
}
|
|
//** Read and decode Extended Extended CSD
|
|
// Note: The read access is done in byte to avoid a buffer
|
|
// of EXT_CSD_BSIZE Byte in stack.
|
|
|
|
// Read card type
|
|
for (i = 0; i < (EXT_CSD_CARD_TYPE_INDEX + 4) / 4; i++) {
|
|
if (!driver_read_word(&ext_csd)) {
|
|
return false;
|
|
}
|
|
}
|
|
*b_authorize_high_speed = (ext_csd >> ((EXT_CSD_CARD_TYPE_INDEX % 4) * 8))
|
|
& MMC_CTYPE_52MHZ;
|
|
|
|
if (MMC_CSD_C_SIZE(sd_mmc_card->csd) == 0xFFF) {
|
|
// For high capacity SD/MMC card,
|
|
// memory capacity = SEC_COUNT * 512 byte
|
|
for (; i <(EXT_CSD_SEC_COUNT_INDEX + 4) / 4; i++) {
|
|
if (!driver_read_word(&sec_count)) {
|
|
return false;
|
|
}
|
|
}
|
|
sd_mmc_card->capacity = sec_count / 2;
|
|
}
|
|
for (; i < EXT_CSD_BSIZE / 4; i++) {
|
|
if (!driver_read_word(&sec_count)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief CMD9: Addressed card sends its card-specific
|
|
* data (CSD) on the CMD line spi.
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool sd_mmc_cmd9_spi(void)
|
|
{
|
|
if (!driver_adtc_start(SDMMC_SPI_CMD9_SEND_CSD, (uint32_t)sd_mmc_card->rca << 16,
|
|
CSD_REG_BSIZE, 1, true)) {
|
|
return false;
|
|
}
|
|
if (!driver_start_read_blocks(sd_mmc_card->csd, 1)) {
|
|
return false;
|
|
}
|
|
return driver_wait_end_of_read_blocks();
|
|
}
|
|
|
|
/**
|
|
* \brief CMD9: Addressed card sends its card-specific
|
|
* data (CSD) on the CMD line mci.
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool sd_mmc_cmd9_mci(void)
|
|
{
|
|
if (!driver_send_cmd(SDMMC_MCI_CMD9_SEND_CSD, (uint32_t)sd_mmc_card->rca << 16)) {
|
|
return false;
|
|
}
|
|
driver_get_response_128(sd_mmc_card->csd);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Decodes MMC CSD register
|
|
*/
|
|
static void mmc_decode_csd(void)
|
|
{
|
|
uint32_t unit;
|
|
uint32_t mul;
|
|
uint32_t tran_speed;
|
|
|
|
// Get MMC System Specification version supported by the card
|
|
switch (MMC_CSD_SPEC_VERS(sd_mmc_card->csd)) {
|
|
default:
|
|
case 0:
|
|
sd_mmc_card->version = CARD_VER_MMC_1_2;
|
|
break;
|
|
|
|
case 1:
|
|
sd_mmc_card->version = CARD_VER_MMC_1_4;
|
|
break;
|
|
|
|
case 2:
|
|
sd_mmc_card->version = CARD_VER_MMC_2_2;
|
|
break;
|
|
|
|
case 3:
|
|
sd_mmc_card->version = CARD_VER_MMC_3;
|
|
break;
|
|
|
|
case 4:
|
|
sd_mmc_card->version = CARD_VER_MMC_4;
|
|
break;
|
|
}
|
|
|
|
// Get MMC memory max transfer speed in Hz.
|
|
tran_speed = CSD_TRAN_SPEED(sd_mmc_card->csd);
|
|
unit = sd_mmc_trans_units[tran_speed & 0x7];
|
|
mul = mmc_trans_multipliers[(tran_speed >> 3) & 0xF];
|
|
sd_mmc_card->clock = unit * mul * 1000;
|
|
|
|
/*
|
|
* Get card capacity.
|
|
* ----------------------------------------------------
|
|
* For normal SD/MMC card:
|
|
* memory capacity = BLOCKNR * BLOCK_LEN
|
|
* Where
|
|
* BLOCKNR = (C_SIZE+1) * MULT
|
|
* MULT = 2 ^ (C_SIZE_MULT+2) (C_SIZE_MULT < 8)
|
|
* BLOCK_LEN = 2 ^ READ_BL_LEN (READ_BL_LEN < 12)
|
|
* ----------------------------------------------------
|
|
* For high capacity SD/MMC card:
|
|
* memory capacity = SEC_COUNT * 512 byte
|
|
*/
|
|
if (MMC_CSD_C_SIZE(sd_mmc_card->csd) != 0xFFF) {
|
|
uint32_t blocknr = ((MMC_CSD_C_SIZE(sd_mmc_card->csd) + 1) *
|
|
(1 << (MMC_CSD_C_SIZE_MULT(sd_mmc_card->csd) + 2)));
|
|
sd_mmc_card->capacity = blocknr *
|
|
(1 << MMC_CSD_READ_BL_LEN(sd_mmc_card->csd)) / 1024;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Decodes SD CSD register
|
|
*/
|
|
static void sd_decode_csd(void)
|
|
{
|
|
uint32_t unit;
|
|
uint32_t mul;
|
|
uint32_t tran_speed;
|
|
|
|
// Get SD memory maximum transfer speed in Hz.
|
|
tran_speed = CSD_TRAN_SPEED(sd_mmc_card->csd);
|
|
unit = sd_mmc_trans_units[tran_speed & 0x7];
|
|
mul = sd_trans_multipliers[(tran_speed >> 3) & 0xF];
|
|
sd_mmc_card->clock = unit * mul * 1000;
|
|
|
|
/*
|
|
* Get card capacity.
|
|
* ----------------------------------------------------
|
|
* For normal SD/MMC card:
|
|
* memory capacity = BLOCKNR * BLOCK_LEN
|
|
* Where
|
|
* BLOCKNR = (C_SIZE+1) * MULT
|
|
* MULT = 2 ^ (C_SIZE_MULT+2) (C_SIZE_MULT < 8)
|
|
* BLOCK_LEN = 2 ^ READ_BL_LEN (READ_BL_LEN < 12)
|
|
* ----------------------------------------------------
|
|
* For high capacity SD card:
|
|
* memory capacity = (C_SIZE+1) * 512K byte
|
|
*/
|
|
if (CSD_STRUCTURE_VERSION(sd_mmc_card->csd) >= SD_CSD_VER_2_0) {
|
|
sd_mmc_card->capacity =
|
|
(SD_CSD_2_0_C_SIZE(sd_mmc_card->csd) + 1)
|
|
* 512;
|
|
} else {
|
|
uint32_t blocknr = ((SD_CSD_1_0_C_SIZE(sd_mmc_card->csd) + 1) *
|
|
(1 << (SD_CSD_1_0_C_SIZE_MULT(sd_mmc_card->csd) + 2)));
|
|
sd_mmc_card->capacity = blocknr *
|
|
(1 << SD_CSD_1_0_READ_BL_LEN(sd_mmc_card->csd))
|
|
/ 1024;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief CMD13 - Addressed card sends its status register.
|
|
* This function waits the clear of the busy flag
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool sd_mmc_cmd13(void)
|
|
{
|
|
uint32_t nec_timeout;
|
|
|
|
/* Wait for data ready status.
|
|
* Nec timing: 0 to unlimited
|
|
* However a timeout is used.
|
|
* 200 000 * 8 cycles
|
|
*/
|
|
nec_timeout = 200000;
|
|
do {
|
|
if (sd_mmc_is_spi()) {
|
|
if (!driver_send_cmd(SDMMC_SPI_CMD13_SEND_STATUS, 0)) {
|
|
return false;
|
|
}
|
|
// Check busy flag
|
|
if (!(driver_get_response() & 0xFF)) {
|
|
break;
|
|
}
|
|
} else {
|
|
if (!driver_send_cmd(SDMMC_MCI_CMD13_SEND_STATUS,
|
|
(uint32_t)sd_mmc_card->rca << 16)) {
|
|
return false;
|
|
}
|
|
// Check busy flag
|
|
if (driver_get_response() & CARD_STATUS_READY_FOR_DATA) {
|
|
break;
|
|
}
|
|
}
|
|
if (nec_timeout-- == 0) {
|
|
sd_mmc_debug("%s: CMD13 Busy timeout\n\r", __func__);
|
|
return false;
|
|
}
|
|
} while (1);
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifdef SDIO_SUPPORT_ENABLE
|
|
/**
|
|
* \brief CMD52 - SDIO IO_RW_DIRECT command
|
|
*
|
|
* \param rw_flag Direction, 1:write, 0:read.
|
|
* \param func_nb Number of the function.
|
|
* \param rd_after_wr Read after Write flag.
|
|
* \param reg_addr register address.
|
|
* \param io_data Pointer to input argument and response buffer.
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool sdio_cmd52(uint8_t rw_flag, uint8_t func_nb,
|
|
uint32_t reg_addr, uint8_t rd_after_wr, uint8_t *io_data)
|
|
{
|
|
Assert(io_data != NULL);
|
|
if (!driver_send_cmd(SDIO_CMD52_IO_RW_DIRECT,
|
|
((uint32_t)*io_data << SDIO_CMD52_WR_DATA)
|
|
| ((uint32_t)rw_flag << SDIO_CMD52_RW_FLAG)
|
|
| ((uint32_t)func_nb << SDIO_CMD52_FUNCTION_NUM)
|
|
| ((uint32_t)rd_after_wr << SDIO_CMD52_RAW_FLAG)
|
|
| ((uint32_t)reg_addr << SDIO_CMD52_REG_ADRR))) {
|
|
return false;
|
|
}
|
|
*io_data = driver_get_response() & 0xFF;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief CMD53 - SDIO IO_RW_EXTENDED command
|
|
* This implementation support only the SDIO multi-byte transfer mode which is
|
|
* similar to the single block transfer on memory.
|
|
* Note: The SDIO block transfer mode is optional for SDIO card.
|
|
*
|
|
* \param rw_flag Direction, 1:write, 0:read.
|
|
* \param func_nb Number of the function.
|
|
* \param reg_addr Register address.
|
|
* \param inc_addr 1:Incrementing address, 0: fixed.
|
|
* \param size Transfer data size.
|
|
* \param access_block true, if the block access (DMA) is used
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool sdio_cmd53(uint8_t rw_flag, uint8_t func_nb, uint32_t reg_addr,
|
|
uint8_t inc_addr, uint32_t size, bool access_block)
|
|
{
|
|
Assert(size != 0);
|
|
Assert(size <= 512);
|
|
|
|
return driver_adtc_start((rw_flag == SDIO_CMD53_READ_FLAG)?
|
|
SDIO_CMD53_IO_R_BYTE_EXTENDED :
|
|
SDIO_CMD53_IO_W_BYTE_EXTENDED,
|
|
((size % 512) << SDIO_CMD53_COUNT)
|
|
| ((uint32_t)reg_addr << SDIO_CMD53_REG_ADDR)
|
|
| ((uint32_t)inc_addr << SDIO_CMD53_OP_CODE)
|
|
| ((uint32_t)0 << SDIO_CMD53_BLOCK_MODE)
|
|
| ((uint32_t)func_nb << SDIO_CMD53_FUNCTION_NUM)
|
|
| ((uint32_t)rw_flag << SDIO_CMD53_RW_FLAG),
|
|
size, 1, access_block);
|
|
}
|
|
#endif // SDIO_SUPPORT_ENABLE
|
|
|
|
/**
|
|
* \brief ACMD6 - Define the data bus width to 4 bits bus
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool sd_acmd6(void)
|
|
{
|
|
// CMD55 - Indicate to the card that the next command is an
|
|
// application specific command rather than a standard command.
|
|
if (!driver_send_cmd(SDMMC_CMD55_APP_CMD, (uint32_t)sd_mmc_card->rca << 16)) {
|
|
return false;
|
|
}
|
|
// 10b = 4 bits bus
|
|
if (!driver_send_cmd(SD_ACMD6_SET_BUS_WIDTH, 0x2)) {
|
|
return false;
|
|
}
|
|
sd_mmc_card->bus_width = 4;
|
|
sd_mmc_debug("%d-bit bus width enabled.\n\r", (int)sd_mmc_card->bus_width);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief ACMD51 - Read the SD Configuration Register.
|
|
*
|
|
* \note
|
|
* SD Card Configuration Register (SCR) provides information on the SD Memory
|
|
* Card's special features that were configured into the given card. The size
|
|
* of SCR register is 64 bits.
|
|
*
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool sd_acmd51(void)
|
|
{
|
|
uint8_t scr[SD_SCR_REG_BSIZE];
|
|
|
|
// CMD55 - Indicate to the card that the next command is an
|
|
// application specific command rather than a standard command.
|
|
if (!driver_send_cmd(SDMMC_CMD55_APP_CMD, (uint32_t)sd_mmc_card->rca << 16)) {
|
|
return false;
|
|
}
|
|
if (!driver_adtc_start(SD_ACMD51_SEND_SCR, 0,
|
|
SD_SCR_REG_BSIZE, 1, true)) {
|
|
return false;
|
|
}
|
|
if (!driver_start_read_blocks(scr, 1)) {
|
|
return false;
|
|
}
|
|
if (!driver_wait_end_of_read_blocks()) {
|
|
return false;
|
|
}
|
|
|
|
// Get SD Memory Card - Spec. Version
|
|
switch (SD_SCR_SD_SPEC(scr)) {
|
|
case SD_SCR_SD_SPEC_1_0_01:
|
|
sd_mmc_card->version = CARD_VER_SD_1_0;
|
|
break;
|
|
|
|
case SD_SCR_SD_SPEC_1_10:
|
|
sd_mmc_card->version = CARD_VER_SD_1_10;
|
|
break;
|
|
|
|
case SD_SCR_SD_SPEC_2_00:
|
|
if (SD_SCR_SD_SPEC3(scr) == SD_SCR_SD_SPEC_3_00) {
|
|
sd_mmc_card->version = CARD_VER_SD_3_0;
|
|
} else {
|
|
sd_mmc_card->version = CARD_VER_SD_2_0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
sd_mmc_card->version = CARD_VER_SD_1_0;
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Select a card slot and initialize the associated driver
|
|
*
|
|
* \param slot Card slot number
|
|
*
|
|
* \retval SD_MMC_ERR_SLOT Wrong slot number
|
|
* \retval SD_MMC_ERR_NO_CARD No card present on slot
|
|
* \retval SD_MMC_ERR_UNUSABLE Unusable card
|
|
* \retval SD_MMC_INIT_ONGOING Card initialization requested
|
|
* \retval SD_MMC_OK Card present
|
|
*/
|
|
static sd_mmc_err_t sd_mmc_select_slot(uint8_t slot)
|
|
{
|
|
if (slot >= SD_MMC_MEM_CNT) {
|
|
return SD_MMC_ERR_SLOT;
|
|
}
|
|
Assert(sd_mmc_nb_block_remaining == 0);
|
|
|
|
#if (defined SD_MMC_0_CD_GPIO)
|
|
//! Card Detect pins
|
|
if (digitalRead(SD_MMC_0_CD_GPIO) != SD_MMC_0_CD_DETECT_VALUE) {
|
|
if (sd_mmc_cards[slot].state == SD_MMC_CARD_STATE_DEBOUNCE) {
|
|
SD_MMC_STOP_TIMEOUT();
|
|
}
|
|
sd_mmc_cards[slot].state = SD_MMC_CARD_STATE_NO_CARD;
|
|
return SD_MMC_ERR_NO_CARD;
|
|
}
|
|
if (sd_mmc_cards[slot].state == SD_MMC_CARD_STATE_NO_CARD) {
|
|
// A card plug on going, but this is not initialized
|
|
sd_mmc_cards[slot].state = SD_MMC_CARD_STATE_DEBOUNCE;
|
|
// Debounce + Power On Setup
|
|
SD_MMC_START_TIMEOUT();
|
|
return SD_MMC_ERR_NO_CARD;
|
|
}
|
|
if (sd_mmc_cards[slot].state == SD_MMC_CARD_STATE_DEBOUNCE) {
|
|
if (!SD_MMC_IS_TIMEOUT()) {
|
|
// Debounce on going
|
|
return SD_MMC_ERR_NO_CARD;
|
|
}
|
|
// Card is not initialized
|
|
sd_mmc_cards[slot].state = SD_MMC_CARD_STATE_INIT;
|
|
// Set 1-bit bus width and low clock for initialization
|
|
sd_mmc_cards[slot].clock = SDMMC_CLOCK_INIT;
|
|
sd_mmc_cards[slot].bus_width = 1;
|
|
sd_mmc_cards[slot].high_speed = 0;
|
|
}
|
|
if (sd_mmc_cards[slot].state == SD_MMC_CARD_STATE_UNUSABLE) {
|
|
return SD_MMC_ERR_UNUSABLE;
|
|
}
|
|
#else
|
|
// No pin card detection, then always try to install it
|
|
if ((sd_mmc_cards[slot].state == SD_MMC_CARD_STATE_NO_CARD)
|
|
|| (sd_mmc_cards[slot].state == SD_MMC_CARD_STATE_UNUSABLE)) {
|
|
// Card is not initialized
|
|
sd_mmc_cards[slot].state = SD_MMC_CARD_STATE_INIT;
|
|
// Set 1-bit bus width and low clock for initialization
|
|
sd_mmc_cards[slot].clock = SDMMC_CLOCK_INIT;
|
|
sd_mmc_cards[slot].bus_width = 1;
|
|
sd_mmc_cards[slot].high_speed = 0;
|
|
}
|
|
#endif
|
|
|
|
// Initialize interface
|
|
sd_mmc_slot_sel = slot;
|
|
sd_mmc_card = &sd_mmc_cards[slot];
|
|
sd_mmc_configure_slot();
|
|
return (sd_mmc_cards[slot].state == SD_MMC_CARD_STATE_INIT) ?
|
|
SD_MMC_INIT_ONGOING : SD_MMC_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Configures the driver with the selected card configuration
|
|
*/
|
|
static void sd_mmc_configure_slot(void)
|
|
{
|
|
driver_select_device(sd_mmc_slot_sel, sd_mmc_card->clock,
|
|
sd_mmc_card->bus_width, sd_mmc_card->high_speed);
|
|
}
|
|
|
|
/**
|
|
* \brief Deselect the current card slot
|
|
*/
|
|
static void sd_mmc_deselect_slot(void)
|
|
{
|
|
if (sd_mmc_slot_sel < SD_MMC_MEM_CNT) {
|
|
driver_deselect_device(sd_mmc_slot_sel);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Initialize the SD card in SPI mode.
|
|
*
|
|
* \note
|
|
* This function runs the initialization procedure and the identification
|
|
* process, then it sets the SD/MMC card in transfer state.
|
|
* At last, it will automaticaly enable maximum bus width and transfer speed.
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool sd_mmc_spi_card_init(void)
|
|
{
|
|
uint8_t v2 = 0;
|
|
|
|
// In first, try to install SD/SDIO card
|
|
sd_mmc_card->type = CARD_TYPE_SD;
|
|
sd_mmc_card->version = CARD_VER_UNKNOWN;
|
|
sd_mmc_card->rca = 0;
|
|
sd_mmc_debug("Start SD card install\n\r");
|
|
|
|
// Card need of 74 cycles clock minimum to start
|
|
driver_send_clock();
|
|
|
|
// CMD0 - Reset all cards to idle state.
|
|
if (!driver_send_cmd(SDMMC_SPI_CMD0_GO_IDLE_STATE, 0)) {
|
|
return false;
|
|
}
|
|
if (!sd_cmd8(&v2)) {
|
|
return false;
|
|
}
|
|
// Try to get the SDIO card's operating condition
|
|
if (!sdio_op_cond()) {
|
|
return false;
|
|
}
|
|
|
|
if (sd_mmc_card->type & CARD_TYPE_SD) {
|
|
// Try to get the SD card's operating condition
|
|
if (!sd_spi_op_cond(v2)) {
|
|
// It is not a SD card
|
|
sd_mmc_debug("Start MMC Install\n\r");
|
|
sd_mmc_card->type = CARD_TYPE_MMC;
|
|
return sd_mmc_spi_install_mmc();
|
|
}
|
|
|
|
/* The CRC on card is disabled by default.
|
|
* However, to be sure, the CRC OFF command is send.
|
|
* Unfortunately, specific SDIO card does not support it
|
|
* (H&D wireless card - HDG104 WiFi SIP)
|
|
* and the command is send only on SD card.
|
|
*/
|
|
if (!driver_send_cmd(SDMMC_SPI_CMD59_CRC_ON_OFF, 0)) {
|
|
return false;
|
|
}
|
|
}
|
|
// SD MEMORY
|
|
if (sd_mmc_card->type & CARD_TYPE_SD) {
|
|
// Get the Card-Specific Data
|
|
if (!sd_mmc_cmd9_spi()) {
|
|
return false;
|
|
}
|
|
sd_decode_csd();
|
|
// Read the SCR to get card version
|
|
if (!sd_acmd51()) {
|
|
return false;
|
|
}
|
|
}
|
|
if (IS_SDIO()) {
|
|
if (!sdio_get_max_speed()) {
|
|
return false;
|
|
}
|
|
}
|
|
// SD MEMORY not HC, Set default block size
|
|
if ((sd_mmc_card->type & CARD_TYPE_SD) &&
|
|
(0 == (sd_mmc_card->type & CARD_TYPE_HC))) {
|
|
if (!driver_send_cmd(SDMMC_CMD16_SET_BLOCKLEN, SD_MMC_BLOCK_SIZE)) {
|
|
return false;
|
|
}
|
|
}
|
|
// Check communication
|
|
if (sd_mmc_card->type & CARD_TYPE_SD) {
|
|
if (!sd_mmc_cmd13()) {
|
|
return false;
|
|
}
|
|
}
|
|
// Re-initialize the slot with the new speed
|
|
sd_mmc_configure_slot();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Initialize the SD card in MCI mode.
|
|
*
|
|
* \note
|
|
* This function runs the initialization procedure and the identification
|
|
* process, then it sets the SD/MMC card in transfer state.
|
|
* At last, it will automaticly enable maximum bus width and transfer speed.
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool sd_mmc_mci_card_init(void)
|
|
{
|
|
uint8_t v2 = 0;
|
|
|
|
// In first, try to install SD/SDIO card
|
|
sd_mmc_card->type = CARD_TYPE_SD;
|
|
sd_mmc_card->version = CARD_VER_UNKNOWN;
|
|
sd_mmc_card->rca = 0;
|
|
sd_mmc_debug("Start SD card install\n\r");
|
|
|
|
// Card need of 74 cycles clock minimum to start
|
|
driver_send_clock();
|
|
|
|
// CMD0 - Reset all cards to idle state.
|
|
if (!driver_send_cmd(SDMMC_MCI_CMD0_GO_IDLE_STATE, 0)) {
|
|
return false;
|
|
}
|
|
if (!sd_cmd8(&v2)) {
|
|
return false;
|
|
}
|
|
// Try to get the SDIO card's operating condition
|
|
if (!sdio_op_cond()) {
|
|
return false;
|
|
}
|
|
|
|
if (sd_mmc_card->type & CARD_TYPE_SD) {
|
|
// Try to get the SD card's operating condition
|
|
if (!sd_mci_op_cond(v2)) {
|
|
// It is not a SD card
|
|
sd_mmc_debug("Start MMC Install\n\r");
|
|
sd_mmc_card->type = CARD_TYPE_MMC;
|
|
return sd_mmc_mci_install_mmc();
|
|
}
|
|
}
|
|
|
|
if (sd_mmc_card->type & CARD_TYPE_SD) {
|
|
// SD MEMORY, Put the Card in Identify Mode
|
|
// Note: The CID is not used in this stack
|
|
if (!driver_send_cmd(SDMMC_CMD2_ALL_SEND_CID, 0)) {
|
|
return false;
|
|
}
|
|
}
|
|
// Ask the card to publish a new relative address (RCA).
|
|
if (!driver_send_cmd(SD_CMD3_SEND_RELATIVE_ADDR, 0)) {
|
|
return false;
|
|
}
|
|
sd_mmc_card->rca = (driver_get_response() >> 16) & 0xFFFF;
|
|
|
|
// SD MEMORY, Get the Card-Specific Data
|
|
if (sd_mmc_card->type & CARD_TYPE_SD) {
|
|
if (!sd_mmc_cmd9_mci()) {
|
|
return false;
|
|
}
|
|
sd_decode_csd();
|
|
}
|
|
// Select the and put it into Transfer Mode
|
|
if (!driver_send_cmd(SDMMC_CMD7_SELECT_CARD_CMD,
|
|
(uint32_t)sd_mmc_card->rca << 16)) {
|
|
return false;
|
|
}
|
|
// SD MEMORY, Read the SCR to get card version
|
|
if (sd_mmc_card->type & CARD_TYPE_SD) {
|
|
if (!sd_acmd51()) {
|
|
return false;
|
|
}
|
|
}
|
|
if (IS_SDIO()) {
|
|
if (!sdio_get_max_speed()) {
|
|
return false;
|
|
}
|
|
}
|
|
if ((4 <= driver_get_bus_width(sd_mmc_slot_sel))) {
|
|
// TRY to enable 4-bit mode
|
|
if (IS_SDIO()) {
|
|
if (!sdio_cmd52_set_bus_width()) {
|
|
return false;
|
|
}
|
|
}
|
|
if (sd_mmc_card->type & CARD_TYPE_SD) {
|
|
if (!sd_acmd6()) {
|
|
return false;
|
|
}
|
|
}
|
|
// Switch to selected bus mode
|
|
sd_mmc_configure_slot();
|
|
}
|
|
if (driver_is_high_speed_capable()) {
|
|
// TRY to enable High-Speed Mode
|
|
if (IS_SDIO()) {
|
|
if (!sdio_cmd52_set_high_speed()) {
|
|
return false;
|
|
}
|
|
}
|
|
if (sd_mmc_card->type & CARD_TYPE_SD) {
|
|
if (sd_mmc_card->version > CARD_VER_SD_1_0) {
|
|
if (!sd_cm6_set_high_speed()) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
// Valid new configuration
|
|
sd_mmc_configure_slot();
|
|
}
|
|
// SD MEMORY, Set default block size
|
|
if (sd_mmc_card->type & CARD_TYPE_SD) {
|
|
if (!driver_send_cmd(SDMMC_CMD16_SET_BLOCKLEN, SD_MMC_BLOCK_SIZE)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Initialize the MMC card in SPI mode.
|
|
*
|
|
* \note
|
|
* This function runs the initialization procedure and the identification
|
|
* process, then it sets the SD/MMC card in transfer state.
|
|
* At last, it will automatically enable maximum bus width and transfer speed.
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool sd_mmc_spi_install_mmc(void)
|
|
{
|
|
uint8_t b_authorize_high_speed;
|
|
|
|
// CMD0 - Reset all cards to idle state.
|
|
if (!driver_send_cmd(SDMMC_SPI_CMD0_GO_IDLE_STATE, 0)) {
|
|
return false;
|
|
}
|
|
|
|
if (!mmc_spi_op_cond()) {
|
|
return false;
|
|
}
|
|
|
|
// Disable CRC check for SPI mode
|
|
if (!driver_send_cmd(SDMMC_SPI_CMD59_CRC_ON_OFF, 0)) {
|
|
return false;
|
|
}
|
|
// Get the Card-Specific Data
|
|
if (!sd_mmc_cmd9_spi()) {
|
|
return false;
|
|
}
|
|
mmc_decode_csd();
|
|
// For MMC 4.0 Higher version
|
|
if (sd_mmc_card->version >= CARD_VER_MMC_4) {
|
|
// Get EXT_CSD
|
|
if (!mmc_cmd8(&b_authorize_high_speed)) {
|
|
return false;
|
|
}
|
|
}
|
|
// Set default block size
|
|
if (!driver_send_cmd(SDMMC_CMD16_SET_BLOCKLEN, SD_MMC_BLOCK_SIZE)) {
|
|
return false;
|
|
}
|
|
// Check communication
|
|
if (!sd_mmc_cmd13()) {
|
|
return false;
|
|
}
|
|
// Re-initialize the slot with the new speed
|
|
sd_mmc_configure_slot();
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* \brief Initialize the MMC card in MCI mode.
|
|
*
|
|
* \note
|
|
* This function runs the initialization procedure and the identification
|
|
* process, then it sets the SD/MMC card in transfer state.
|
|
* At last, it will automatically enable maximum bus width and transfer speed.
|
|
*
|
|
* \return true if success, otherwise false
|
|
*/
|
|
static bool sd_mmc_mci_install_mmc(void)
|
|
{
|
|
uint8_t b_authorize_high_speed;
|
|
|
|
// CMD0 - Reset all cards to idle state.
|
|
if (!driver_send_cmd(SDMMC_MCI_CMD0_GO_IDLE_STATE, 0)) {
|
|
return false;
|
|
}
|
|
|
|
if (!mmc_mci_op_cond()) {
|
|
return false;
|
|
}
|
|
|
|
// Put the Card in Identify Mode
|
|
// Note: The CID is not used in this stack
|
|
if (!driver_send_cmd(SDMMC_CMD2_ALL_SEND_CID, 0)) {
|
|
return false;
|
|
}
|
|
// Assign relative address to the card.
|
|
sd_mmc_card->rca = 1;
|
|
if (!driver_send_cmd(MMC_CMD3_SET_RELATIVE_ADDR,
|
|
(uint32_t)sd_mmc_card->rca << 16)) {
|
|
return false;
|
|
}
|
|
// Get the Card-Specific Data
|
|
if (!sd_mmc_cmd9_mci()) {
|
|
return false;
|
|
}
|
|
mmc_decode_csd();
|
|
// Select the and put it into Transfer Mode
|
|
if (!driver_send_cmd(SDMMC_CMD7_SELECT_CARD_CMD,
|
|
(uint32_t)sd_mmc_card->rca << 16)) {
|
|
return false;
|
|
}
|
|
if (sd_mmc_card->version >= CARD_VER_MMC_4) {
|
|
// For MMC 4.0 Higher version
|
|
// Get EXT_CSD
|
|
if (!mmc_cmd8(&b_authorize_high_speed)) {
|
|
return false;
|
|
}
|
|
if (4 <= driver_get_bus_width(sd_mmc_slot_sel)) {
|
|
// Enable more bus width
|
|
if (!mmc_cmd6_set_bus_width(driver_get_bus_width(sd_mmc_slot_sel))) {
|
|
return false;
|
|
}
|
|
// Re-initialize the slot with the bus width
|
|
sd_mmc_configure_slot();
|
|
}
|
|
if (driver_is_high_speed_capable() && b_authorize_high_speed) {
|
|
// Enable HS
|
|
if (!mmc_cmd6_set_high_speed()) {
|
|
return false;
|
|
}
|
|
// Re-initialize the slot with the new speed
|
|
sd_mmc_configure_slot();
|
|
}
|
|
} else {
|
|
// Re-initialize the slot with the new speed
|
|
sd_mmc_configure_slot();
|
|
}
|
|
|
|
uint8_t retry = 10;
|
|
while (retry--) {
|
|
// Retry is a WORKAROUND for no compliance card (Atmel Internal ref. MMC19):
|
|
// These cards seem not ready immediatly
|
|
// after the end of busy of mmc_cmd6_set_high_speed()
|
|
|
|
// Set default block size
|
|
if (driver_send_cmd(SDMMC_CMD16_SET_BLOCKLEN, SD_MMC_BLOCK_SIZE)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
//--------------------- PUBLIC FUNCTIONS ----------------------------
|
|
|
|
void sd_mmc_init(void)
|
|
{
|
|
//! Enable the card detect pins
|
|
#if (defined SD_MMC_0_CD_GPIO) && (!defined SAM4L)
|
|
pinMode(SD_MMC_0_CD_GPIO, INPUT_PULLUP);
|
|
#endif
|
|
//! Enable the PMC clock for the card write protection pins
|
|
#if (defined SD_MMC_0_WP_GPIO) && (!defined SAM4L)
|
|
# include "pmc.h"
|
|
# define SD_MMC_ENABLE_WP_PIN(slot, unused) \
|
|
pmc_enable_periph_clk(SD_MMC_##slot##_WP_PIO_ID);
|
|
MREPEAT(SD_MMC_MEM_CNT, SD_MMC_ENABLE_WP_PIN, ~)
|
|
# undef SD_MMC_ENABLE_WP_PIN
|
|
#endif
|
|
for (uint8_t slot = 0; slot < SD_MMC_MEM_CNT; slot++) {
|
|
sd_mmc_cards[slot].state = SD_MMC_CARD_STATE_NO_CARD;
|
|
}
|
|
sd_mmc_slot_sel = 0xFF; // No slot configured
|
|
driver_init();
|
|
}
|
|
|
|
uint8_t sd_mmc_nb_slot(void)
|
|
{
|
|
return SD_MMC_MEM_CNT;
|
|
}
|
|
|
|
sd_mmc_err_t sd_mmc_check(uint8_t slot)
|
|
{
|
|
sd_mmc_err_t sd_mmc_err;
|
|
|
|
sd_mmc_err = sd_mmc_select_slot(slot);
|
|
if (sd_mmc_err != SD_MMC_INIT_ONGOING) {
|
|
sd_mmc_deselect_slot();
|
|
return sd_mmc_err;
|
|
}
|
|
|
|
// Initialization of the card requested
|
|
if (sd_mmc_is_spi()? sd_mmc_spi_card_init()
|
|
: sd_mmc_mci_card_init()) {
|
|
sd_mmc_debug("SD/MMC card ready\n\r");
|
|
sd_mmc_card->state = SD_MMC_CARD_STATE_READY;
|
|
sd_mmc_deselect_slot();
|
|
// To notify that the card has been just initialized
|
|
// It is necessary for USB Device MSC
|
|
return SD_MMC_INIT_ONGOING;
|
|
}
|
|
sd_mmc_debug("SD/MMC card initialization failed\n\r");
|
|
sd_mmc_card->state = SD_MMC_CARD_STATE_UNUSABLE;
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_ERR_UNUSABLE;
|
|
}
|
|
|
|
card_type_t sd_mmc_get_type(uint8_t slot)
|
|
{
|
|
if (SD_MMC_OK != sd_mmc_select_slot(slot)) {
|
|
return CARD_TYPE_UNKNOWN;
|
|
}
|
|
sd_mmc_deselect_slot();
|
|
return sd_mmc_card->type;
|
|
}
|
|
|
|
card_version_t sd_mmc_get_version(uint8_t slot)
|
|
{
|
|
if (SD_MMC_OK != sd_mmc_select_slot(slot)) {
|
|
return CARD_VER_UNKNOWN;
|
|
}
|
|
sd_mmc_deselect_slot();
|
|
return sd_mmc_card->version;
|
|
}
|
|
|
|
uint32_t sd_mmc_get_capacity(uint8_t slot)
|
|
{
|
|
if (SD_MMC_OK != sd_mmc_select_slot(slot)) {
|
|
return 0;
|
|
}
|
|
sd_mmc_deselect_slot();
|
|
return sd_mmc_card->capacity;
|
|
}
|
|
|
|
bool sd_mmc_is_write_protected(uint8_t slot)
|
|
{
|
|
UNUSED(slot);
|
|
#if (defined SD_MMC_0_WP_GPIO)
|
|
//! Card Detect pins
|
|
if (ioport_get_pin_level(sd_mmc_cards[slot].wp_gpio)
|
|
== SD_MMC_0_WP_DETECT_VALUE) {
|
|
return true;
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
sd_mmc_err_t sd_mmc_init_read_blocks(uint8_t slot, uint32_t start,
|
|
uint16_t nb_block)
|
|
{
|
|
sd_mmc_err_t sd_mmc_err;
|
|
uint32_t cmd, arg, resp;
|
|
|
|
sd_mmc_err = sd_mmc_select_slot(slot);
|
|
if (sd_mmc_err != SD_MMC_OK) {
|
|
return sd_mmc_err;
|
|
}
|
|
|
|
// Wait for data ready status
|
|
if (!sd_mmc_cmd13()) {
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_ERR_COMM;
|
|
}
|
|
|
|
if (nb_block > 1) {
|
|
cmd = SDMMC_CMD18_READ_MULTIPLE_BLOCK;
|
|
} else {
|
|
cmd = SDMMC_CMD17_READ_SINGLE_BLOCK;
|
|
}
|
|
/*
|
|
* SDSC Card (CCS=0) uses byte unit address,
|
|
* SDHC and SDXC Cards (CCS=1) use block unit address (512 Bytes unit).
|
|
*/
|
|
if (sd_mmc_card->type & CARD_TYPE_HC) {
|
|
arg = start;
|
|
} else {
|
|
arg = (start * SD_MMC_BLOCK_SIZE);
|
|
}
|
|
|
|
if (!driver_adtc_start(cmd, arg, SD_MMC_BLOCK_SIZE, nb_block, true)) {
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_ERR_COMM;
|
|
}
|
|
// Check response
|
|
if (sd_mmc_is_mci()) {
|
|
resp = driver_get_response();
|
|
if (resp & CARD_STATUS_ERR_RD_WR) {
|
|
sd_mmc_debug("%s: Read blocks %02d resp32 0x%08x CARD_STATUS_ERR_RD_WR\n\r",
|
|
__func__, (int)SDMMC_CMD_GET_INDEX(cmd), resp);
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_ERR_COMM;
|
|
}
|
|
}
|
|
sd_mmc_nb_block_remaining = nb_block;
|
|
sd_mmc_nb_block_to_tranfer = nb_block;
|
|
return SD_MMC_OK;
|
|
}
|
|
|
|
sd_mmc_err_t sd_mmc_start_read_blocks(void *dest, uint16_t nb_block)
|
|
{
|
|
Assert(sd_mmc_nb_block_remaining >= nb_block);
|
|
|
|
if (!driver_start_read_blocks(dest, nb_block)) {
|
|
sd_mmc_nb_block_remaining = 0;
|
|
return SD_MMC_ERR_COMM;
|
|
}
|
|
sd_mmc_nb_block_remaining -= nb_block;
|
|
return SD_MMC_OK;
|
|
}
|
|
|
|
sd_mmc_err_t sd_mmc_wait_end_of_read_blocks(bool abort)
|
|
{
|
|
if (!driver_wait_end_of_read_blocks()) {
|
|
return SD_MMC_ERR_COMM;
|
|
}
|
|
if (abort) {
|
|
sd_mmc_nb_block_remaining = 0;
|
|
} else if (sd_mmc_nb_block_remaining) {
|
|
return SD_MMC_OK;
|
|
}
|
|
|
|
// All blocks are transfered then stop read operation
|
|
if (sd_mmc_nb_block_to_tranfer == 1) {
|
|
// Single block transfer, then nothing to do
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_OK;
|
|
}
|
|
// WORKAROUND for no compliance card (Atmel Internal ref. !MMC7 !SD19):
|
|
// The errors on this command must be ignored
|
|
// and one retry can be necessary in SPI mode for no compliance card.
|
|
if (!driver_adtc_stop(SDMMC_CMD12_STOP_TRANSMISSION, 0)) {
|
|
driver_adtc_stop(SDMMC_CMD12_STOP_TRANSMISSION, 0);
|
|
}
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_OK;
|
|
}
|
|
|
|
sd_mmc_err_t sd_mmc_init_write_blocks(uint8_t slot, uint32_t start, uint16_t nb_block)
|
|
{
|
|
sd_mmc_err_t sd_mmc_err;
|
|
uint32_t cmd, arg, resp;
|
|
|
|
sd_mmc_err = sd_mmc_select_slot(slot);
|
|
if (sd_mmc_err != SD_MMC_OK) {
|
|
return sd_mmc_err;
|
|
}
|
|
if (sd_mmc_is_write_protected(slot)) {
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_ERR_WP;
|
|
}
|
|
|
|
if (nb_block > 1) {
|
|
cmd = SDMMC_CMD25_WRITE_MULTIPLE_BLOCK;
|
|
} else {
|
|
cmd = SDMMC_CMD24_WRITE_BLOCK;
|
|
}
|
|
/*
|
|
* SDSC Card (CCS=0) uses byte unit address,
|
|
* SDHC and SDXC Cards (CCS=1) use block unit address (512 Bytes unit).
|
|
*/
|
|
if (sd_mmc_card->type & CARD_TYPE_HC) {
|
|
arg = start;
|
|
} else {
|
|
arg = (start * SD_MMC_BLOCK_SIZE);
|
|
}
|
|
if (!driver_adtc_start(cmd, arg, SD_MMC_BLOCK_SIZE, nb_block, true)) {
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_ERR_COMM;
|
|
}
|
|
// Check response
|
|
if (sd_mmc_is_mci()) {
|
|
resp = driver_get_response();
|
|
if (resp & CARD_STATUS_ERR_RD_WR) {
|
|
sd_mmc_debug("%s: Write blocks %02d r1 0x%08x CARD_STATUS_ERR_RD_WR\n\r",
|
|
__func__, (int)SDMMC_CMD_GET_INDEX(cmd), resp);
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_ERR_COMM;
|
|
}
|
|
}
|
|
sd_mmc_nb_block_remaining = nb_block;
|
|
sd_mmc_nb_block_to_tranfer = nb_block;
|
|
return SD_MMC_OK;
|
|
}
|
|
|
|
sd_mmc_err_t sd_mmc_start_write_blocks(const void *src, uint16_t nb_block)
|
|
{
|
|
Assert(sd_mmc_nb_block_remaining >= nb_block);
|
|
if (!driver_start_write_blocks(src, nb_block)) {
|
|
sd_mmc_nb_block_remaining = 0;
|
|
return SD_MMC_ERR_COMM;
|
|
}
|
|
sd_mmc_nb_block_remaining -= nb_block;
|
|
return SD_MMC_OK;
|
|
}
|
|
|
|
sd_mmc_err_t sd_mmc_wait_end_of_write_blocks(bool abort)
|
|
{
|
|
if (!driver_wait_end_of_write_blocks()) {
|
|
return SD_MMC_ERR_COMM;
|
|
}
|
|
if (abort) {
|
|
sd_mmc_nb_block_remaining = 0;
|
|
} else if (sd_mmc_nb_block_remaining) {
|
|
return SD_MMC_OK;
|
|
}
|
|
|
|
// All blocks are transfered then stop write operation
|
|
if (sd_mmc_nb_block_to_tranfer == 1) {
|
|
// Single block transfer, then nothing to do
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_OK;
|
|
}
|
|
|
|
if (sd_mmc_is_mci()) {
|
|
// Note: SPI multi block writes terminate using a special
|
|
// token, not a STOP_TRANSMISSION request.
|
|
if (!driver_adtc_stop(SDMMC_CMD12_STOP_TRANSMISSION, 0)) {
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_ERR_COMM;
|
|
}
|
|
}
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_OK;
|
|
}
|
|
|
|
#ifdef SDIO_SUPPORT_ENABLE
|
|
sd_mmc_err_t sdio_read_direct(uint8_t slot, uint8_t func_num, uint32_t addr,
|
|
uint8_t *dest)
|
|
{
|
|
sd_mmc_err_t sd_mmc_err;
|
|
|
|
if (dest == NULL) {
|
|
return SD_MMC_ERR_PARAM;
|
|
}
|
|
|
|
sd_mmc_err = sd_mmc_select_slot(slot);
|
|
if (sd_mmc_err != SD_MMC_OK) {
|
|
return sd_mmc_err;
|
|
}
|
|
|
|
if (!sdio_cmd52(SDIO_CMD52_READ_FLAG, func_num, addr, 0, dest)) {
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_ERR_COMM;
|
|
}
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_OK;
|
|
}
|
|
|
|
sd_mmc_err_t sdio_write_direct(uint8_t slot, uint8_t func_num, uint32_t addr,
|
|
uint8_t data)
|
|
{
|
|
sd_mmc_err_t sd_mmc_err;
|
|
|
|
sd_mmc_err = sd_mmc_select_slot(slot);
|
|
if (sd_mmc_err != SD_MMC_OK) {
|
|
return sd_mmc_err;
|
|
}
|
|
|
|
if (!sdio_cmd52(SDIO_CMD52_WRITE_FLAG, func_num, addr, 0, &data)) {
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_ERR_COMM;
|
|
}
|
|
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_OK;
|
|
}
|
|
|
|
sd_mmc_err_t sdio_read_extended(uint8_t slot, uint8_t func_num, uint32_t addr,
|
|
uint8_t inc_addr, uint8_t *dest, uint16_t size)
|
|
{
|
|
sd_mmc_err_t sd_mmc_err;
|
|
|
|
if ((size == 0) || (size > 512)) {
|
|
return SD_MMC_ERR_PARAM;
|
|
}
|
|
|
|
sd_mmc_err = sd_mmc_select_slot(slot);
|
|
if (sd_mmc_err != SD_MMC_OK) {
|
|
return sd_mmc_err;
|
|
}
|
|
|
|
if (!sdio_cmd53(SDIO_CMD53_READ_FLAG, func_num, addr, inc_addr,
|
|
size, true)) {
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_ERR_COMM;
|
|
}
|
|
if (!driver_start_read_blocks(dest, 1)) {
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_ERR_COMM;
|
|
}
|
|
if (!driver_wait_end_of_read_blocks()) {
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_ERR_COMM;
|
|
}
|
|
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_OK;
|
|
}
|
|
|
|
sd_mmc_err_t sdio_write_extended(uint8_t slot, uint8_t func_num, uint32_t addr,
|
|
uint8_t inc_addr, uint8_t *src, uint16_t size)
|
|
{
|
|
sd_mmc_err_t sd_mmc_err;
|
|
|
|
if ((size == 0) || (size > 512)) {
|
|
return SD_MMC_ERR_PARAM;
|
|
}
|
|
|
|
sd_mmc_err = sd_mmc_select_slot(slot);
|
|
if (sd_mmc_err != SD_MMC_OK) {
|
|
return sd_mmc_err;
|
|
}
|
|
|
|
if (!sdio_cmd53(SDIO_CMD53_WRITE_FLAG, func_num, addr, inc_addr,
|
|
size, true)) {
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_ERR_COMM;
|
|
}
|
|
if (!driver_start_write_blocks(src, 1)) {
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_ERR_COMM;
|
|
}
|
|
if (!driver_wait_end_of_write_blocks()) {
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_ERR_COMM;
|
|
}
|
|
|
|
sd_mmc_deselect_slot();
|
|
return SD_MMC_OK;
|
|
}
|
|
#endif // SDIO_SUPPORT_ENABLE
|
|
|
|
//! @}
|