diff options
author | David Crocker <dcrocker@eschertech.com> | 2020-06-15 21:28:31 +0300 |
---|---|---|
committer | David Crocker <dcrocker@eschertech.com> | 2020-06-15 21:28:31 +0300 |
commit | cec9ec68435afecbd88acf068bcffc40d4e051bc (patch) | |
tree | a8b13cd300af0071b8de1bb7c01612b3ebad8be1 /src/Libraries | |
parent | 9210008cafead2e5b9c12802861994d0826fa384 (diff) |
Moved sd_mmc library here from CoreNG
Diffstat (limited to 'src/Libraries')
-rw-r--r-- | src/Libraries/Fatfs/diskio.cpp | 4 | ||||
-rw-r--r-- | src/Libraries/sd_mmc/conf_sd_mmc.h | 105 | ||||
-rw-r--r-- | src/Libraries/sd_mmc/ctrl_access.c | 98 | ||||
-rw-r--r-- | src/Libraries/sd_mmc/ctrl_access.h | 171 | ||||
-rw-r--r-- | src/Libraries/sd_mmc/sd_mmc.c | 2131 | ||||
-rw-r--r-- | src/Libraries/sd_mmc/sd_mmc.h | 317 | ||||
-rw-r--r-- | src/Libraries/sd_mmc/sd_mmc_mem.c | 138 | ||||
-rw-r--r-- | src/Libraries/sd_mmc/sd_mmc_mem.h | 127 | ||||
-rw-r--r-- | src/Libraries/sd_mmc/sd_mmc_protocol.h | 1012 | ||||
-rw-r--r-- | src/Libraries/sd_mmc/sd_mmc_spi.c | 629 | ||||
-rw-r--r-- | src/Libraries/sd_mmc/sd_mmc_spi.h | 249 |
11 files changed, 4979 insertions, 2 deletions
diff --git a/src/Libraries/Fatfs/diskio.cpp b/src/Libraries/Fatfs/diskio.cpp index f3d19c40..4ab7860a 100644 --- a/src/Libraries/Fatfs/diskio.cpp +++ b/src/Libraries/Fatfs/diskio.cpp @@ -44,8 +44,8 @@ #include "compiler.h" #include "diskio.h" -#include "ctrl_access.h" -#include "conf_sd_mmc.h" +#include <Libraries/sd_mmc/ctrl_access.h> +#include <Libraries/sd_mmc/conf_sd_mmc.h> #include "RepRapFirmware.h" #include "RepRap.h" diff --git a/src/Libraries/sd_mmc/conf_sd_mmc.h b/src/Libraries/sd_mmc/conf_sd_mmc.h new file mode 100644 index 00000000..86bfd6a8 --- /dev/null +++ b/src/Libraries/sd_mmc/conf_sd_mmc.h @@ -0,0 +1,105 @@ +/** + * \file + * + * \brief SD/MMC stack configuration file. + * + * Copyright (c) 2014-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> + */ + +#ifndef CONF_SD_MMC_H_INCLUDED +#define CONF_SD_MMC_H_INCLUDED + +// Define this to enable the SPI mode instead of Multimedia Card interface mode +//#define SD_MMC_SPI_MODE + +// Define this to enable the SDIO support +//#define SDIO_SUPPORT_ENABLE + +// Define this to enable the debug trace to the current standard output (stdio) +//#define SD_MMC_DEBUG + +/*! \name board MCI SD/MMC slot template definition + * + * The GPIO and MCI/HSMCI connections of the SD/MMC Connector must be added + * in board.h file. + */ + +// SD card configuration for Duet and Duet WiFi +#define SD_MMC_ENABLE + +#if defined(__RADDS__) + +#define SD_MMC_HSMCI_MEM_CNT 0 // Number of HSMCI card slots supported +#define SD_MMC_SPI_MEM_CNT 2 // Number of SPI card slots supported + +#define SD_MMC_SPI_MAX_CLOCK (4000000) // Max 4MHz clock for SPI cards, to allow a reasonable cable length + +#define SD_MMC_WP_DETECT_VALUE false + +#elif defined(__ALLIGATOR__) + +#define SD_MMC_HSMCI_MEM_CNT 0 // Number of HSMCI card slots supported +#define SD_MMC_SPI_MEM_CNT 1 // Number of SPI card slots supported + +#define SD_MMC_SPI_MAX_CLOCK (20000000) // Max 20MHz clock for onboard SPI cards + +#define SD_MMC_WP_DETECT_VALUE false + + +#else + +#define CONF_BOARD_SD_MMC_HSMCI 1 // Enable HSMCI +#define SD_MMC_HSMCI_MEM_CNT 1 // Number of HSMCI card slots supported +#define SD_MMC_HSMCI_SLOT_0_SIZE 4 // HSMCI bus width +#define SD_MMC_SPI_MEM_CNT 1 // Number of SPI card slots supported + +#define SD_MMC_SPI_MAX_CLOCK (4000000) // Max 4MHz clock for SPI cards, to allow a reasonable cable length + +#define SD_MMC_WP_DETECT_VALUE false + +#endif + +#define SD_MMC_MEM_CNT (SD_MMC_HSMCI_MEM_CNT + SD_MMC_SPI_MEM_CNT) + +#define ACCESS_MEM_TO_RAM_ENABLED + +#endif /* CONF_SD_MMC_H_INCLUDED */ + diff --git a/src/Libraries/sd_mmc/ctrl_access.c b/src/Libraries/sd_mmc/ctrl_access.c new file mode 100644 index 00000000..e6940388 --- /dev/null +++ b/src/Libraries/sd_mmc/ctrl_access.c @@ -0,0 +1,98 @@ +/***************************************************************************** + * + * \file + * + * \brief Abstraction layer for memory interfaces. + * + * This module contains the interfaces: + * - MEM <-> USB; + * - MEM <-> RAM; + * - MEM <-> MEM. + * + * This module may be configured and expanded to support the following features: + * - write-protected globals; + * - password-protected data; + * - specific features; + * - etc. + * + * Copyright (c) 2009-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> + */ + + +//_____ I N C L U D E S ____________________________________________________ + +#include "compiler.h" +#include "ctrl_access.h" +#include "sd_mmc_mem.h" + + +Ctrl_status mem_test_unit_ready(uint8_t lun) +{ + return (lun < MAX_LUN) ? sd_mmc_test_unit_ready(lun) : CTRL_FAIL; +} + +Ctrl_status mem_read_capacity(uint8_t lun, uint32_t *u32_nb_sector) +{ + return (lun < MAX_LUN) ? sd_mmc_read_capacity(lun, u32_nb_sector) : CTRL_FAIL; +} + +uint8_t mem_sector_size(uint8_t lun) +{ + return 1; +} + +bool mem_wr_protect(uint8_t lun) +{ + return (lun < MAX_LUN) ? sd_mmc_wr_protect(lun) : true; +} + +Ctrl_status memory_2_ram(uint8_t lun, uint32_t addr, void *ram, uint32_t numBlocks) +{ + return (lun < MAX_LUN) ? sd_mmc_mem_2_ram(lun, addr, ram, numBlocks) : CTRL_FAIL; +} + +Ctrl_status ram_2_memory(uint8_t lun, uint32_t addr, const void *ram, uint32_t numBlocks) +{ + return (lun < MAX_LUN) ? sd_mmc_ram_2_mem(lun, addr, ram, numBlocks) : CTRL_FAIL; +} + + +//! @} diff --git a/src/Libraries/sd_mmc/ctrl_access.h b/src/Libraries/sd_mmc/ctrl_access.h new file mode 100644 index 00000000..ac49bb97 --- /dev/null +++ b/src/Libraries/sd_mmc/ctrl_access.h @@ -0,0 +1,171 @@ +/***************************************************************************** + * + * \file + * + * \brief Abstraction layer for memory interfaces. + * + * This module contains the interfaces: + * - MEM <-> USB; + * - MEM <-> RAM; + * - MEM <-> MEM. + * + * This module may be configured and expanded to support the following features: + * - write-protected globals; + * - password-protected data; + * - specific features; + * - etc. + * + * Copyright (c) 2009-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> + */ + + +#ifndef _CTRL_ACCESS_H_ +#define _CTRL_ACCESS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup group_common_services_storage_ctrl_access Memory Control Access + * + * Common abstraction layer for memory interfaces. It provides interfaces between: + * Memory and USB, Memory and RAM, Memory and Memory. Common API for XMEGA and UC3. + * + * \{ + */ + +#include "compiler.h" + +#ifndef SECTOR_SIZE +# define SECTOR_SIZE 512 +#endif + +//! Status returned by CTRL_ACCESS interfaces. +typedef enum +{ + CTRL_GOOD = 0, //!< Success, memory ready. + CTRL_FAIL = 1, //!< An error occurred. + CTRL_NO_PRESENT = 2, //!< Memory unplugged. + CTRL_BUSY = 3 //!< Memory not initialized or changed. +} Ctrl_status; + +#define MAX_LUN 2 + +/*! \name Control Interface + */ +//! @{ + +/*! \brief Tests the memory state and initializes the memory if required. + * + * The TEST UNIT READY SCSI primary command allows an application client to poll + * a LUN until it is ready without having to allocate memory for returned data. + * + * This command may be used to check the media status of LUNs with removable + * media. + * + * \param lun Logical Unit Number. + * + * \return Status. + */ +extern Ctrl_status mem_test_unit_ready(uint8_t lun) noexcept; + +/*! \brief Returns the address of the last valid sector (512 bytes) in the + * memory. + * + * \param lun Logical Unit Number. + * \param u32_nb_sector Pointer to the address of the last valid sector. + * + * \return Status. + */ +extern Ctrl_status mem_read_capacity(uint8_t lun, uint32_t *u32_nb_sector) noexcept; + +/*! \brief Returns the size of the physical sector. + * + * \param lun Logical Unit Number. + * + * \return Sector size (unit: 512 bytes). + */ +extern uint8_t mem_sector_size(uint8_t lun) noexcept; + +/*! \brief Returns the write-protection state of the memory. + * + * \param lun Logical Unit Number. + * + * \return \c true if the memory is write-protected, else \c false. + * + * \note Only used by removable memories with hardware-specific write + * protection. + */ +extern bool mem_wr_protect(uint8_t lun) noexcept; + +/*! \brief Copies 1 data sector from the memory to RAM. + * + * \param lun Logical Unit Number. + * \param addr Address of first memory sector to read. + * \param ram Pointer to RAM buffer to write. + * + * \return Status. + */ +extern Ctrl_status memory_2_ram(uint8_t lun, uint32_t addr, void *ram, uint32_t numBlocks) noexcept; + +/*! \brief Copies 1 data sector from RAM to the memory. + * + * \param lun Logical Unit Number. + * \param addr Address of first memory sector to write. + * \param ram Pointer to RAM buffer to read. + * + * \return Status. + */ +extern Ctrl_status ram_2_memory(uint8_t lun, uint32_t addr, const void *ram, uint32_t numBlocks) noexcept; + +//! @} + + +/** + * \} + */ + +#ifdef __cplusplus +} +#endif + +#endif // _CTRL_ACCESS_H_ diff --git a/src/Libraries/sd_mmc/sd_mmc.c b/src/Libraries/sd_mmc/sd_mmc.c new file mode 100644 index 00000000..c3db16ef --- /dev/null +++ b/src/Libraries/sd_mmc/sd_mmc.c @@ -0,0 +1,2131 @@ +/** + * \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 "Core.h" +#include <string.h> +#include "sd_mmc_protocol.h" +#include "sd_mmc.h" +#include "conf_sd_mmc.h" + +#include "Core.h" // for digitalRead() and pinMode() + +#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 +extern void debugPrintf(const char* fmt, ...); +# define sd_mmc_debug(...) debugPrintf(__VA_ARGS__) +#else +# define sd_mmc_debug(...) +#endif + +#ifndef SD_MMC_HSMCI_MEM_CNT +# error SD_MMC_HSMCI_MEM_CNT not defined +#endif +#ifndef SD_MMC_SPI_MEM_CNT +# error SD_MMC_SPI_MEM_CNT not defined +#endif + +typedef void (*driverIdleFunc_t)(uint32_t, uint32_t); + +struct DriverInterface +{ + void (*select_device)(uint8_t slot, uint32_t clock, uint8_t bus_width, bool high_speed); + void (*deselect_device)(uint8_t slot); + uint8_t (*get_bus_width)(uint8_t slot); + bool (*is_high_speed_capable)(void); + void (*send_clock)(void); + bool (*send_cmd)(sdmmc_cmd_def_t cmd, uint32_t arg); + uint32_t (*get_response)(void); + void (*get_response_128)(uint8_t* response); + bool (*adtc_start)(sdmmc_cmd_def_t cmd, uint32_t arg, uint16_t block_size, uint16_t nb_block, bool access_block); + bool (*adtc_stop)(sdmmc_cmd_def_t cmd, uint32_t arg); + bool (*read_word)(uint32_t* value); + bool (*write_word)(uint32_t value); + bool (*start_read_blocks)(void *dest, uint16_t nb_block); + bool (*wait_end_of_read_blocks)(void); + bool (*start_write_blocks)(const void *src, uint16_t nb_block); + bool (*wait_end_of_write_blocks)(void); +#if 1 //dc42 + uint32_t (*getInterfaceSpeed)(void); +#endif + driverIdleFunc_t (*set_idle_func)(driverIdleFunc_t); + bool is_spi; // true if the interface is SPI, false if it is HSMCI +}; + +#if (SD_MMC_HSMCI_MEM_CNT != 0) + +# ifdef __SAME54P20A__ + +# include <hal_mci_sync.h> + +static const struct DriverInterface hsmciInterface = { + .select_device = mci_sync_select_device, + .deselect_device = mci_sync_deselect_device, + .get_bus_width = mci_sync_get_bus_width, + .is_high_speed_capable = mci_sync_is_high_speed_capable, + .send_clock = mci_sync_send_clock, + .send_cmd = mci_sync_send_cmd, + .get_response = mci_sync_get_response, + .get_response_128 = mci_sync_get_response_128, + .adtc_start = mci_sync_adtc_start, + .adtc_stop = mci_sync_send_cmd, // adtc_stop aliased to send_cmd as in the ASF original + .read_word = mci_sync_read_word, + .write_word = mci_sync_write_word, + .start_read_blocks = mci_sync_start_read_blocks, + .wait_end_of_read_blocks = mci_sync_wait_end_of_read_blocks, + .start_write_blocks = mci_sync_start_write_blocks, + .wait_end_of_write_blocks = mci_sync_wait_end_of_write_blocks, +#if 1 //dc42 + .getInterfaceSpeed = mci_sync_get_speed, +#endif + .set_idle_func = mci_sync_set_idle_func, + .is_spi = false +}; + +# else + +# include <hsmci/hsmci.h> + +static const struct DriverInterface hsmciInterface = { + .select_device = hsmci_select_device, + .deselect_device = hsmci_deselect_device, + .get_bus_width = hsmci_get_bus_width, + .is_high_speed_capable = hsmci_is_high_speed_capable, + .send_clock = hsmci_send_clock, + .send_cmd = hsmci_send_cmd, + .get_response = hsmci_get_response, + .get_response_128 = hsmci_get_response_128, + .adtc_start = hsmci_adtc_start, + .adtc_stop = hsmci_send_cmd, // adtc_stop aliased to send_cmd as in the ASF original + .read_word = hsmci_read_word, + .write_word = hsmci_write_word, + .start_read_blocks = hsmci_start_read_blocks, + .wait_end_of_read_blocks = hsmci_wait_end_of_read_blocks, + .start_write_blocks = hsmci_start_write_blocks, + .wait_end_of_write_blocks = hsmci_wait_end_of_write_blocks, +#if 1 //dc42 + .getInterfaceSpeed = hsmci_get_speed, +#endif + .set_idle_func = hsmci_set_idle_func, + .is_spi = false +}; + +# endif + +#endif + +#if (SD_MMC_SPI_MEM_CNT != 0) +# include "sd_mmc_spi.h" + +static const struct DriverInterface spiInterface = { + .select_device = sd_mmc_spi_select_device, + .deselect_device = sd_mmc_spi_deselect_device, + .get_bus_width = sd_mmc_spi_get_bus_width, + .is_high_speed_capable = sd_mmc_spi_is_high_speed_capable, + .send_clock = sd_mmc_spi_send_clock, + .send_cmd = sd_mmc_spi_send_cmd, + .get_response = sd_mmc_spi_get_response, + .get_response_128 = sd_mmc_spi_get_response_128, + .adtc_start = sd_mmc_spi_adtc_start, + .adtc_stop = sd_mmc_spi_send_cmd, // adtc_stop aliased to send_cmd as in the ASF original + .read_word = sd_mmc_spi_read_word, + .write_word = sd_mmc_spi_write_word, + .start_read_blocks = sd_mmc_spi_start_read_blocks, + .wait_end_of_read_blocks = sd_mmc_spi_wait_end_of_read_blocks, + .start_write_blocks = sd_mmc_spi_start_write_blocks, + .wait_end_of_write_blocks = sd_mmc_spi_wait_end_of_write_blocks, +#if 1 //dc42 + .getInterfaceSpeed = spi_mmc_get_speed, +#endif + .set_idle_func = sd_mmc_spi_set_idle_func, + .is_spi = true +}; +#endif + +#ifdef SDIO_SUPPORT_ENABLE +# define IS_SDIO() (sd_mmc_card->type & CARD_TYPE_SDIO) +#else +# define IS_SDIO() false +#endif + +//! 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 ongoing + SD_MMC_CARD_STATE_INIT = 2, //!< Initialization ongoing + 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 { + const struct DriverInterface *iface; // Pointer to driver interface functions + uint32_t clock; //!< Card access clock + uint32_t capacity; //!< Card capacity in KBytes +#if 0 // dc42 + Pin cd_gpio; //!< Card detect pin number, or -1 if none present +#endif + Pin wp_gpio; //!< Card write protection pin number, or -1 if none present + 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 slot; // Slot number within the driver + 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 +static struct sd_mmc_card sd_mmc_cards[SD_MMC_MEM_CNT]; + +//! Index of current slot selected +static uint8_t sd_mmc_slot_sel; +//! Pointer on current slot selected +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 + + +/** + * \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 (!sd_mmc_card->iface->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 = sd_mmc_card->iface->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 (!sd_mmc_card->iface->send_cmd(SDMMC_SPI_CMD58_READ_OCR, 0)) { + sd_mmc_debug("%s: CMD58 Fail\n\r", __func__); + return false; + } + // Check OCR value + if ((sd_mmc_card->iface->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 (!sd_mmc_card->iface->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 = sd_mmc_card->iface->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 (!sd_mmc_card->iface->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 (!sd_mmc_card->iface->send_cmd(SD_SPI_ACMD41_SD_SEND_OP_COND, arg)) { + sd_mmc_debug("%s: ACMD41 Fail\n\r", __func__); + return false; + } + resp = sd_mmc_card->iface->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 (!sd_mmc_card->iface->send_cmd(SDMMC_SPI_CMD58_READ_OCR, 0)) { + sd_mmc_debug("%s: CMD58 Fail\n\r", __func__); + return false; + } + if ((sd_mmc_card->iface->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 (!sd_mmc_card->iface->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 (!sd_mmc_card->iface->send_cmd(SD_MCI_ACMD41_SD_SEND_OP_COND, arg)) { + sd_mmc_debug("%s: ACMD41 Fail\n\r", __func__); + return false; + } + resp = sd_mmc_card->iface->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 (!sd_mmc_card->iface->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 = sd_mmc_card->iface->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 (!sd_mmc_card->iface->send_cmd(SDIO_CMD5_SEND_OP_COND, + resp & SD_MMC_VOLTAGE_SUPPORT)) { + sd_mmc_debug("%s: CMD5 Fail\n\r", __func__); + return false; + } + resp = sd_mmc_card->iface->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_new, addr_old; + uint8_t buf[6]; + uint32_t unit; + uint32_t mul; + uint8_t tplfe_max_tran_speed, i; + uint8_t addr_cis[4]; + + /* Read CIS area address in CCCR area */ + addr_old = SDIO_CCCR_CIS_PTR; + for(i = 0; i < 4; i++) { + sdio_cmd52(SDIO_CMD52_READ_FLAG, SDIO_CIA, addr_old, 0, &addr_cis[i]); + addr_old++; + } + addr_old = addr_cis[0] + (addr_cis[1] << 8) + \ + (addr_cis[2] << 16) + (addr_cis[3] << 24); + addr_new = addr_old; + + while (1) { + // Read a sample of CIA area + for(i=0; i<3; i++) { + sdio_cmd52(SDIO_CMD52_READ_FLAG, SDIO_CIA, addr_new, 0, &buf[i]); + addr_new++; + } + if (buf[0] == SDIO_CISTPL_END) { + sd_mmc_debug("%s: CMD52 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: CMD52 Tuple error\n\r", __func__); + return false; // Tuple error + } + + // Next address + addr += (buf[1] + 2); + if (addr > (addr_cis + 256)) { + sd_mmc_debug("%s: CMD52 Outoff CIS area\n\r", __func__); + return false; // Outoff CIS area + } + } + + // Read all Fun0 tuple fields: fn0_blk_siz & max_tran_speed + addr_new -= 3; + for(i = 0; i < 6; i++) { + sdio_cmd52(SDIO_CMD52_READ_FLAG, SDIO_CIA, addr_new, 0, &buf[i]); + addr_new++; + } + + 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 (!sd_mmc_card->iface->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 (!sd_mmc_card->iface->start_read_blocks(switch_status, 1)) { + return false; + } + if (!sd_mmc_card->iface->wait_end_of_read_blocks()) { + return false; + } + + if (sd_mmc_card->iface->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. + sd_mmc_card->iface->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 (!sd_mmc_card->iface->send_cmd(MMC_CMD6_SWITCH, arg)) { + return false; + } + if (sd_mmc_card->iface->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 (!sd_mmc_card->iface->send_cmd(MMC_CMD6_SWITCH, + MMC_CMD6_ACCESS_WRITE_BYTE + | MMC_CMD6_INDEX_HS_TIMING + | MMC_CMD6_VALUE_HS_TIMING_ENABLE)) { + return false; + } + if (sd_mmc_card->iface->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 (!sd_mmc_card->iface->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 = sd_mmc_card->iface->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 (!sd_mmc_card->iface->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 (!sd_mmc_card->iface->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 (!sd_mmc_card->iface->read_word(&sec_count)) { + return false; + } + } + sd_mmc_card->capacity = sec_count / 2; + } + for (; i < EXT_CSD_BSIZE / 4; i++) { + if (!sd_mmc_card->iface->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 (!sd_mmc_card->iface->adtc_start(SDMMC_SPI_CMD9_SEND_CSD, (uint32_t)sd_mmc_card->rca << 16, + CSD_REG_BSIZE, 1, true)) { + return false; + } + if (!sd_mmc_card->iface->start_read_blocks(sd_mmc_card->csd, 1)) { + return false; + } + return sd_mmc_card->iface->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 (!sd_mmc_card->iface->send_cmd(SDMMC_MCI_CMD9_SEND_CSD, (uint32_t)sd_mmc_card->rca << 16)) { + return false; + } + sd_mmc_card->iface->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_card->iface->is_spi) { + if (!sd_mmc_card->iface->send_cmd(SDMMC_SPI_CMD13_SEND_STATUS, 0)) { + return false; + } + // Check busy flag + if (!(sd_mmc_card->iface->get_response() & 0xFF)) { + break; + } + } else { + if (!sd_mmc_card->iface->send_cmd(SDMMC_MCI_CMD13_SEND_STATUS, (uint32_t)sd_mmc_card->rca << 16)) { + return false; + } + // Check busy flag + if (sd_mmc_card->iface->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 (!sd_mmc_card->iface->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 = sd_mmc_card->iface->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 sd_mmc_card->iface->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 (!sd_mmc_card->iface->send_cmd(SDMMC_CMD55_APP_CMD, (uint32_t)sd_mmc_card->rca << 16)) { + return false; + } + // 10b = 4 bits bus + if (!sd_mmc_card->iface->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 (!sd_mmc_card->iface->send_cmd(SDMMC_CMD55_APP_CMD, (uint32_t)sd_mmc_card->rca << 16)) { + return false; + } + if (!sd_mmc_card->iface->adtc_start(SD_ACMD51_SEND_SCR, 0, + SD_SCR_REG_BSIZE, 1, true)) { + return false; + } + if (!sd_mmc_card->iface->start_read_blocks(scr, 1)) { + return false; + } + if (!sd_mmc_card->iface->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 + * \retval SD_MMC_CD_DEBOUNCING Giving the card time to be ready + */ +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 1 // dc42 + // RepRapFirmware now handles the card detect pin and debouncing, so ignore the card detect pin here +#else + if (sd_mmc_cards[slot].cd_gpio != NoPin) { + //! Card Detect pins + if (digitalRead(sd_mmc_cards[slot].cd_gpio) != SD_MMC_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_CD_DEBOUNCING; + } + if (sd_mmc_cards[slot].state == SD_MMC_CARD_STATE_DEBOUNCE) { + if (!SD_MMC_IS_TIMEOUT()) { + // Debounce on going + return SD_MMC_CD_DEBOUNCING; + } + // 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 +#endif + { + // 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; + } + } + + // 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) +{ + sd_mmc_card->iface->select_device(sd_mmc_card->slot, 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) { + sd_mmc_card->iface->deselect_device(sd_mmc_card->slot); + sd_mmc_slot_sel = 0xFF; // No slot selected + } +} + +/** + * \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 automatically 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 + sd_mmc_card->iface->send_clock(); + + // CMD0 - Reset all cards to idle state. + if (!sd_mmc_card->iface->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 (!sd_mmc_card->iface->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 (!sd_mmc_card->iface->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 automatically 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 + sd_mmc_card->iface->send_clock(); + + // CMD0 - Reset all cards to idle state. + if (!sd_mmc_card->iface->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 (!sd_mmc_card->iface->send_cmd(SDMMC_CMD2_ALL_SEND_CID, 0)) { + return false; + } + } + // Ask the card to publish a new relative address (RCA). + if (!sd_mmc_card->iface->send_cmd(SD_CMD3_SEND_RELATIVE_ADDR, 0)) { + return false; + } + sd_mmc_card->rca = (sd_mmc_card->iface->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 (!sd_mmc_card->iface->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 <= sd_mmc_card->iface->get_bus_width(sd_mmc_card->slot))) { + // 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 (sd_mmc_card->iface->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 (!sd_mmc_card->iface->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 (!sd_mmc_card->iface->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 (!sd_mmc_card->iface->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 (!sd_mmc_card->iface->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 (!sd_mmc_card->iface->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 (!sd_mmc_card->iface->send_cmd(SDMMC_CMD2_ALL_SEND_CID, 0)) { + return false; + } + // Assign relative address to the card. + sd_mmc_card->rca = 1; + if (!sd_mmc_card->iface->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 (!sd_mmc_card->iface->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 <= sd_mmc_card->iface->get_bus_width(sd_mmc_card->slot)) { + // Enable more bus width + if (!mmc_cmd6_set_bus_width(sd_mmc_card->iface->get_bus_width(sd_mmc_card->slot))) { + return false; + } + // Re-initialize the slot with the bus width + sd_mmc_configure_slot(); + } + if (sd_mmc_card->iface->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 (sd_mmc_card->iface->send_cmd(SDMMC_CMD16_SET_BLOCKLEN, SD_MMC_BLOCK_SIZE)) { + return true; + } + } + return false; +} + +//------------------------------------------------------------------- +//--------------------- PUBLIC FUNCTIONS ---------------------------- + +void sd_mmc_init(const Pin wpPins[], const Pin spiCsPins[]) +{ + for (size_t slot = 0; slot < SD_MMC_MEM_CNT; slot++) + { + struct sd_mmc_card *card = &sd_mmc_cards[slot]; + card->state = SD_MMC_CARD_STATE_NO_CARD; + card->wp_gpio = wpPins[slot]; + if (card->wp_gpio != NoPin) + { + pinMode(card->wp_gpio, INPUT_PULLUP); + } +#if SD_MMC_HSMCI_MEM_CNT != 0 + if (slot < SD_MMC_HSMCI_MEM_CNT) + { + card->iface = &hsmciInterface; + card->slot = slot; + } + else +#endif + { +#if (SD_MMC_SPI_MEM_CNT != 0) + card->iface = &spiInterface; + card->slot = slot - SD_MMC_HSMCI_MEM_CNT; +#endif + } + } + sd_mmc_slot_sel = 0xFF; // No slot selected + +#if SD_MMC_HSMCI_MEM_CNT != 0 + hsmci_init(); +#endif + +#if SD_MMC_SPI_MEM_CNT != 0 + sd_mmc_spi_init(spiCsPins); +#endif +} + +uint8_t sd_mmc_nb_slot(void) +{ + return SD_MMC_MEM_CNT; +} + +sd_mmc_err_t sd_mmc_check(uint8_t slot) +{ +#if 1 //dc42 + sd_mmc_err_t sd_mmc_err; + do + { + sd_mmc_err = sd_mmc_select_slot(slot); + } while (sd_mmc_err == SD_MMC_CD_DEBOUNCING); +#else + sd_mmc_err_t sd_mmc_err = sd_mmc_select_slot(slot); +#endif + if (sd_mmc_err != SD_MMC_INIT_ONGOING) + { + sd_mmc_deselect_slot(); + return sd_mmc_err; + } + + // Initialization of the card requested + if (sd_mmc_card->iface->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(); +#if 1 + // If we return SD_MMC_INIT_ONGOING here then I can't see how we can ever access the card + return SD_MMC_OK; +#else + // To notify that the card has been just initialized + // It is necessary for USB Device MSC + return SD_MMC_INIT_ONGOING; +#endif + } + 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) +{ + return sd_mmc_cards[slot].wp_gpio != NoPin && digitalRead(sd_mmc_cards[slot].wp_gpio) == SD_MMC_WP_DETECT_VALUE; +} + +#if 1 // dc42 + +// Unmount the card. Must call this to force it to be re-initialised when changing card. +void sd_mmc_unmount(uint8_t slot) +{ + sd_mmc_cards[slot].state = SD_MMC_CARD_STATE_NO_CARD; +} + +// Get the interface speed in bytes/sec +uint32_t sd_mmc_get_interface_speed(uint8_t slot) +{ + return sd_mmc_cards[slot].iface->getInterfaceSpeed(); +} + +#endif + +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 (!sd_mmc_card->iface->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_card->iface->is_spi) { + resp = sd_mmc_card->iface->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 (!sd_mmc_card->iface->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 (!sd_mmc_card->iface->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 (!sd_mmc_card->iface->adtc_stop(SDMMC_CMD12_STOP_TRANSMISSION, 0)) { + sd_mmc_card->iface->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 (!sd_mmc_card->iface->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_card->iface->is_spi) { + resp = sd_mmc_card->iface->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 (!sd_mmc_card->iface->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 (!sd_mmc_card->iface->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_card->iface->is_spi) { + // Note: SPI multi block writes terminate using a special + // token, not a STOP_TRANSMISSION request. + if (!sd_mmc_card->iface->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 (!sd_mmc_card->iface->start_read_blocks(dest, 1)) { + sd_mmc_deselect_slot(); + return SD_MMC_ERR_COMM; + } + if (!sd_mmc_card->iface->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 (!sd_mmc_card->iface->start_write_blocks(src, 1)) { + sd_mmc_deselect_slot(); + return SD_MMC_ERR_COMM; + } + if (!sd_mmc_card->iface->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 + +//! @} diff --git a/src/Libraries/sd_mmc/sd_mmc.h b/src/Libraries/sd_mmc/sd_mmc.h new file mode 100644 index 00000000..33e773bf --- /dev/null +++ b/src/Libraries/sd_mmc/sd_mmc.h @@ -0,0 +1,317 @@ +/** + * \file + * + * \brief Common SD/MMC stack header file + * + * 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> + */ + +#ifndef SD_MMC_H_INCLUDED +#define SD_MMC_H_INCLUDED + +#include "compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup common_memory + * \defgroup sd_mmc_stack_group SD/MMC/SDIO common stack + * + * SD/MMC/SDIO basic APIs used by SD/MMC/SDIO memory + * APIs (\ref sd_mmc_stack_mem_group). + * Also, it can be used by application which use the SDIO card + * or specific application which does not need of File System. + * + * For usual application which use the SD/MMC card in + * memory mode with a file system, please refer to + * \ref sd_mmc_stack_mem_group. + * @{ + */ + +typedef uint8_t sd_mmc_err_t; //!< Type of return error code + +//! \name Return error codes +//! @{ +#define SD_MMC_OK 0 //! No error +#define SD_MMC_INIT_ONGOING 1 //! Card not initialized +#define SD_MMC_ERR_NO_CARD 2 //! No SD/MMC card inserted +#define SD_MMC_ERR_UNUSABLE 3 //! Unusable card +#define SD_MMC_ERR_SLOT 4 //! Slot unknown +#define SD_MMC_ERR_COMM 5 //! General communication error +#define SD_MMC_ERR_PARAM 6 //! Illegal input parameter +#define SD_MMC_ERR_WP 7 //! Card write protected +#define SD_MMC_CD_DEBOUNCING 8 //! Waiting for card to settle after CD +//! @} + +typedef uint8_t card_type_t; //!< Type of card type + +//! \name Card Types +//! @{ +#define CARD_TYPE_UNKNOWN (0) //!< Unknown type card +#define CARD_TYPE_SD (1 << 0) //!< SD card +#define CARD_TYPE_MMC (1 << 1) //!< MMC card +#define CARD_TYPE_SDIO (1 << 2) //!< SDIO card +#define CARD_TYPE_HC (1 << 3) //!< High capacity card +//! SD combo card (io + memory) +#define CARD_TYPE_SD_COMBO (CARD_TYPE_SD | CARD_TYPE_SDIO) +//! @} + +typedef uint8_t card_version_t; //!< Type of card version + +//! \name Card Versions +//! @{ +#define CARD_VER_UNKNOWN (0) //! Unknown card version +#define CARD_VER_SD_1_0 (0x10) //! SD version 1.0 and 1.01 +#define CARD_VER_SD_1_10 (0x1A) //! SD version 1.10 +#define CARD_VER_SD_2_0 (0X20) //! SD version 2.00 +#define CARD_VER_SD_3_0 (0X30) //! SD version 3.0X +#define CARD_VER_MMC_1_2 (0x12) //! MMC version 1.2 +#define CARD_VER_MMC_1_4 (0x14) //! MMC version 1.4 +#define CARD_VER_MMC_2_2 (0x22) //! MMC version 2.2 +#define CARD_VER_MMC_3 (0x30) //! MMC version 3 +#define CARD_VER_MMC_4 (0x40) //! MMC version 4 +//! @} + +//! This SD MMC stack uses the maximum block size authorized (512 bytes) +#define SD_MMC_BLOCK_SIZE 512 + +/** + * \brief Initialize the SD/MMC stack and low level driver required + */ +void sd_mmc_init(const Pin wpPins[], const Pin spiCsPins[]) noexcept; + +/** \brief Return the number of slot available + * + * \return Number of card slot available + */ +uint8_t sd_mmc_nb_slot(void) noexcept; + +/** \brief Performs a card checks + * + * \param slot Card slot to use + * + * \retval SD_MMC_OK Card ready + * \retval SD_MMC_INIT_ONGOING Initialization on going + * \retval SD_MMC_ERR_NO_CARD Card not present in slot + * \retval Other value for error cases, see \ref sd_mmc_err_t + */ +sd_mmc_err_t sd_mmc_check(uint8_t slot) noexcept; + +/** \brief Get the card type + * + * \param slot Card slot + * + * \return Card type (\ref card_type_t) + */ +card_type_t sd_mmc_get_type(uint8_t slot) noexcept; + +/** \brief Get the card version + * + * \param slot Card slot + * + * \return Card version (\ref card_version_t) + */ +card_version_t sd_mmc_get_version(uint8_t slot) noexcept; + +/** \brief Get the memory capacity + * + * \param slot Card slot + * + * \return Capacity (unit KB) + */ +uint32_t sd_mmc_get_capacity(uint8_t slot) noexcept; + +/** \brief Get the card write protection status + * + * \param slot Card slot + * + * \return true, if write protected + */ +bool sd_mmc_is_write_protected(uint8_t slot) noexcept; + +#if 1 // dc42 + +// Unmount the card. Must call this to force it to be re-initialised when changing card. +void sd_mmc_unmount(uint8_t slot) noexcept; + +// Get the interface speed in bytes/sec +uint32_t sd_mmc_get_interface_speed(uint8_t slot) noexcept; + +#endif + +/** + * \brief Initialize the read blocks of data from the card. + * + * \param slot Card slot to use + * \param start Start block number to to read. + * \param nb_block Total number of blocks to be read. + * + * \return return SD_MMC_OK if success, + * otherwise return an error code (\ref sd_mmc_err_t). + */ +sd_mmc_err_t sd_mmc_init_read_blocks(uint8_t slot, uint32_t start, uint16_t nb_block) noexcept; + +/** + * \brief Start the read blocks of data from the card. + * + * \param dest Pointer to read buffer. + * \param nb_block Number of blocks to be read. + * + * \return return SD_MMC_OK if started, + * otherwise return an error code (\ref sd_mmc_err_t). + */ +sd_mmc_err_t sd_mmc_start_read_blocks(void *dest, uint16_t nb_block) noexcept; + +/** + * \brief Wait the end of read blocks of data from the card. + * + * \param abort Abort reading process initialized by + * \ref sd_mmc_init_read_blocks() after the reading issued by + * \ref sd_mmc_start_read_blocks() is done + * + * \return return SD_MMC_OK if success, + * otherwise return an error code (\ref sd_mmc_err_t). + */ +sd_mmc_err_t sd_mmc_wait_end_of_read_blocks(bool abort) noexcept; + +/** + * \brief Initialize the write blocks of data + * + * \param slot Card slot to use + * \param start Start block number to be written. + * \param nb_block Total number of blocks to be written. + * + * \return return SD_MMC_OK if success, + * otherwise return an error code (\ref sd_mmc_err_t). + */ +sd_mmc_err_t sd_mmc_init_write_blocks(uint8_t slot, uint32_t start, uint16_t nb_block) noexcept; + +/** + * \brief Start the write blocks of data + * + * \param src Pointer to write buffer. + * \param nb_block Number of blocks to be written. + * + * \return return SD_MMC_OK if started, + * otherwise return an error code (\ref sd_mmc_err_t). + */ +sd_mmc_err_t sd_mmc_start_write_blocks(const void *src, uint16_t nb_block) noexcept; + +/** + * \brief Wait the end of write blocks of data + * + * \param abort Abort writing process initialized by + * \ref sd_mmc_init_write_blocks() after the writing issued by + * \ref sd_mmc_start_write_blocks() is done + * + * \return return SD_MMC_OK if success, + * otherwise return an error code (\ref sd_mmc_err_t). + */ +sd_mmc_err_t sd_mmc_wait_end_of_write_blocks(bool abort) noexcept; + +#ifdef SDIO_SUPPORT_ENABLE +/** + * \brief Read one byte from SDIO using RW_DIRECT command. + * + * \param slot Card slot to use + * \param func_num Function number. + * \param addr Register address to read from. + * \param dest Pointer to read buffer. + * + * \return return SD_MMC_OK if success, + * otherwise return an error code (\ref sd_mmc_err_t). + */ +sd_mmc_err_t sdio_read_direct(uint8_t slot, uint8_t func_num, uint32_t addr, uint8_t *dest) noexcept; +/** + * \brief Write one byte to SDIO using RW_DIRECT command. + * + * \param slot Card slot to use + * \param func_num Function number. + * \param addr Register address to read from. + * \param data Data to be written. + * + * \return return SD_MMC_OK if success, + * otherwise return an error code (\ref sd_mmc_err_t). + */ +sd_mmc_err_t sdio_write_direct(uint8_t slot, uint8_t func_num, uint32_t addr, uint8_t data) noexcept; + +/** + * \brief Read bytes from SDIO using RW_EXTENDED command. + * + * \param slot Card slot to use + * \param func_num Function number. + * \param addr First register address to read from. + * \param inc_addr 0 - The data address is fixed. + * 1 - The data address increase automatically. + * \param dest Pointer to read buffer. + * \param size Number of bytes to read (1 ~ 512). + * + * \return return SD_MMC_OK if success, + * otherwise return an error code (\ref sd_mmc_err_t). + */ +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) noexcept; + +/** + * \brief Write bytes to SDIO using RW_EXTENDED command. + * + * \param slot Card slot to use + * \param func_num Function number. + * \param addr First register address to write to. + * \param inc_addr 0 - The data address is fixed. + * 1 - The data address increase automatically. + * \param src Pointer to write buffer. + * \param size Number of bytes to read (1 ~ 512). + * + * \return return SD_MMC_OK if success, + * otherwise return an error code (\ref sd_mmc_err_t). + */ +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) noexcept; +#endif // SDIO_SUPPORT_ENABLE + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* SD_MMC_H_INCLUDED */ diff --git a/src/Libraries/sd_mmc/sd_mmc_mem.c b/src/Libraries/sd_mmc/sd_mmc_mem.c new file mode 100644 index 00000000..24912d7c --- /dev/null +++ b/src/Libraries/sd_mmc/sd_mmc_mem.c @@ -0,0 +1,138 @@ +/** + * \file + * + * \brief CTRL_ACCESS interface for 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 "Core.h" +#include "sd_mmc.h" +#include "sd_mmc_mem.h" + +/** + * \ingroup sd_mmc_stack_mem + * \defgroup sd_mmc_stack_mem_internal Implementation of SD/MMC Memory + * @{ + */ + +/** + * \name Control Interface + * @{ + */ + +Ctrl_status sd_mmc_test_unit_ready(uint8_t slot) +{ + switch (sd_mmc_check(slot)) + { + case SD_MMC_OK: + if (sd_mmc_get_type(slot) & (CARD_TYPE_SD | CARD_TYPE_MMC)) { + return CTRL_GOOD; + } + // It is not a memory card + return CTRL_NO_PRESENT; + + case SD_MMC_INIT_ONGOING: + return CTRL_BUSY; + + case SD_MMC_ERR_NO_CARD: + return CTRL_NO_PRESENT; + + default: + return CTRL_FAIL; + } +} + +Ctrl_status sd_mmc_read_capacity(uint8_t slot, uint32_t *nb_sector) +{ + // Return last sector address (-1) + *nb_sector = (sd_mmc_get_capacity(slot) * 2) - 1; + return sd_mmc_test_unit_ready(slot); +} + +bool sd_mmc_wr_protect(uint8_t slot) +{ + return sd_mmc_is_write_protected(slot); +} + +/** + * \name MEM <-> RAM Interface + * @{ + */ +Ctrl_status sd_mmc_mem_2_ram(uint8_t slot, uint32_t addr, void *ram, uint32_t numBlocks) +{ + switch (sd_mmc_init_read_blocks(slot, addr, numBlocks)) { + case SD_MMC_OK: + break; + case SD_MMC_ERR_NO_CARD: + return CTRL_NO_PRESENT; + default: + return CTRL_FAIL; + } + if (SD_MMC_OK != sd_mmc_start_read_blocks(ram, numBlocks)) { + return CTRL_FAIL; + } + if (SD_MMC_OK != sd_mmc_wait_end_of_read_blocks(false)) { + return CTRL_FAIL; + } + return CTRL_GOOD; +} + +Ctrl_status sd_mmc_ram_2_mem(uint8_t slot, uint32_t addr, const void *ram, uint32_t numBlocks) +{ + switch (sd_mmc_init_write_blocks(slot, addr, numBlocks)) { + case SD_MMC_OK: + break; + case SD_MMC_ERR_NO_CARD: + return CTRL_NO_PRESENT; + default: + return CTRL_FAIL; + } + if (SD_MMC_OK != sd_mmc_start_write_blocks(ram, numBlocks)) { + return CTRL_FAIL; + } + if (SD_MMC_OK != sd_mmc_wait_end_of_write_blocks(false)) { + return CTRL_FAIL; + } + return CTRL_GOOD; +} + +// End diff --git a/src/Libraries/sd_mmc/sd_mmc_mem.h b/src/Libraries/sd_mmc/sd_mmc_mem.h new file mode 100644 index 00000000..2554ae5d --- /dev/null +++ b/src/Libraries/sd_mmc/sd_mmc_mem.h @@ -0,0 +1,127 @@ +/** + * \file + * + * \brief CTRL_ACCESS interface for common SD/MMC stack + * + * Copyright (c) 2012 - 2013 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 + * + */ + +#ifndef _SD_MMC_MEM_H_ +#define _SD_MMC_MEM_H_ + +/** + * \ingroup sd_mmc_stack_group + * \defgroup sd_mmc_stack_mem SD/MMC Memory + * + * SD/MMC memory APIs required by CTRL_ACCESS module + * (\ref group_common_services_storage_ctrl_access). + * + * For usual application which use the SD/MMC card in + * memory mode through a file system or a USB device MSC, + * only a call of \ref sd_mmc_init() function is required in the startup. + * + * @{ + */ + +#include "ctrl_access.h" + +/*! \name Control Interface + */ +//! @{ + +/*! \brief Tests the memory state and initializes the memory if required. + * + * The TEST UNIT READY SCSI primary command allows an application client to poll + * a LUN until it is ready without having to allocate memory for returned data. + * + * This command may be used to check the media status of LUNs with removable + * media. + * + * \param slot SD/MMC Slot Card Selected. + * + * \return Status. + */ +extern Ctrl_status sd_mmc_test_unit_ready(uint8_t slot) noexcept; + +/*! \brief Returns the address of the last valid sector in the memory. + * + * \param slot SD/MMC Slot Card Selected. + * \param u32_nb_sector Pointer to the address of the last valid sector. + * + * \return Status. + */ +extern Ctrl_status sd_mmc_read_capacity(uint8_t slot,uint32_t *u32_nb_sector) noexcept; + +/*! \brief Returns the write-protection state of the memory. + * +* \param slot SD/MMC Slot Card Selected. + * \return \c true if the memory is write-protected, else \c false. + * + * \note Only used by removable memories with hardware-specific write + * protection. + */ +extern bool sd_mmc_wr_protect(uint8_t slot) noexcept; + + +/*! \name MEM <-> RAM Interface + */ +//! @{ + +/*! \brief Copies 1 data sector from the memory to RAM. + * + * \param slot SD/MMC Slot Card Selected. + * \param addr Address of first memory sector to read. + * \param ram Pointer to RAM buffer to write. + * + * \return Status. + */ +extern Ctrl_status sd_mmc_mem_2_ram(uint8_t slot, uint32_t addr, void *ram, uint32_t numBlocks) noexcept; + +/*! \brief Copies 1 data sector from RAM to the memory. + * + * \param slot SD/MMC Slot Card Selected. + * \param addr Address of first memory sector to write. + * \param ram Pointer to RAM buffer to read. + * + * \return Status. + */ +extern Ctrl_status sd_mmc_ram_2_mem(uint8_t slot, uint32_t addr, const void *ram, uint32_t numBlocks) noexcept; + +//! @} + +#endif // _SD_MMC_MEM_H_ diff --git a/src/Libraries/sd_mmc/sd_mmc_protocol.h b/src/Libraries/sd_mmc/sd_mmc_protocol.h new file mode 100644 index 00000000..c6f6b7df --- /dev/null +++ b/src/Libraries/sd_mmc/sd_mmc_protocol.h @@ -0,0 +1,1012 @@ +/** + * \file + * + * \brief SD/MMC protocol definitions. + * + * Copyright (c) 2014-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> + */ + +#ifndef SD_MMC_PROTOCOL_H_INCLUDED +#define SD_MMC_PROTOCOL_H_INCLUDED + +#include "compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup sd_mmc_protocol SD/MMC Protocol Definition + * \ingroup sd_mmc_stack_group + * @{ + */ + +// SD/MMC/SDIO default clock frequency for initialization (400KHz) +#define SDMMC_CLOCK_INIT 400000 + + +/** + * \name Macros for command definition + * + * Commands types: + * - broadcast commands (bc), no response + * - broadcast commands with response (bcr) (Note: No open drain on SD card) + * - addressed (point-to-point) commands (ac), no data transfer on DAT lines + * - addressed (point-to-point) data transfer commands (adtc), data transfer + * on DAT lines + * + * Specific MMC norms: + * CMD1, CMD2 & CMD3 are processed in the open-drain mode. + * The CMD line is driven with push-pull drivers. + * + * Specific SD norms: + * There is no open drain mode in SD memory card. + * + *************************************** + * Responses types: + * + * R1, R3, R4 & R5 use a 48 bits response protected by a 7bit CRC checksum + * - R1 receiv data not specified + * - R3 receiv OCR + * - R4, R5 RCA management (MMC only) + * - R6, R7 RCA management (SD only) + * + * R1b assert the BUSY signal and respond with R1. + * If the busy signal is asserted, it is done two clock cycles (Nsr time) + * after the end bit of the command. The DAT0 line is driven low. + * DAT1-DAT7 lines are driven by the card though their values are not relevant. + * + * R2 use a 136 bits response protected by a 7bit CRC checksum + * The content is CID or CSD + * + * Specific MMC norms: + * - R4 (Fast I/O) return RCA + * - R5 (interrupt request) return RCA null + * + * Specific SD norms: + * - R6 (Published RCA) return RCA + * - R7 (Card interface condition) return RCA null + * + * @{ + */ + +/****************************************** WARNING ********************************************** + * There is a copy of this file in project CoreNG because it is #included by the HSMCI driver + * So beware of changing this file, they must be kept in step! + *************************************************************************************************/ + +//! Value to define a SD/MMC/SDIO command +typedef uint32_t sdmmc_cmd_def_t; + +//! \name Flags used to define a SD/MMC/SDIO command +//! @{ +#define SDMMC_CMD_GET_INDEX(cmd) (cmd & 0x3F) +//! Have response (MCI only) +#define SDMMC_RESP_PRESENT (1lu << 8) +//! 8 bit response (SPI only) +#define SDMMC_RESP_8 (1lu << 9) +//! 32 bit response (SPI only) +#define SDMMC_RESP_32 (1lu << 10) +//! 136 bit response (MCI only) +#define SDMMC_RESP_136 (1lu << 11) +//! Expect valid crc (MCI only) +#define SDMMC_RESP_CRC (1lu << 12) +//! Card may send busy +#define SDMMC_RESP_BUSY (1lu << 13) +// Open drain for a braodcast command (bc) +// or to enter in inactive state (MCI only) +#define SDMMC_CMD_OPENDRAIN (1lu << 14) +//! To signal a data write operation +#define SDMMC_CMD_WRITE (1lu << 15) +//! To signal a SDIO tranfer in multi byte mode +#define SDMMC_CMD_SDIO_BYTE (1lu << 16) +//! To signal a SDIO tranfer in block mode +#define SDMMC_CMD_SDIO_BLOCK (1lu << 17) +//! To signal a data transfer in stream mode +#define SDMMC_CMD_STREAM (1lu << 18) +//! To signal a data transfer in single block mode +#define SDMMC_CMD_SINGLE_BLOCK (1lu << 19) +//! To signal a data transfer in multi block mode +#define SDMMC_CMD_MULTI_BLOCK (1lu << 20) +//! @} + +//! \name Set of flags to define a reponse type +//! @{ +#define SDMMC_CMD_NO_RESP (0) +#define SDMMC_CMD_R1 (SDMMC_RESP_PRESENT | SDMMC_RESP_CRC) +#define SDMMC_CMD_R1B (SDMMC_RESP_PRESENT | SDMMC_RESP_CRC | SDMMC_RESP_BUSY) +#define SDMMC_CMD_R2 (SDMMC_RESP_PRESENT | SDMMC_RESP_8 | SDMMC_RESP_136 | SDMMC_RESP_CRC) +#define SDMMC_CMD_R3 (SDMMC_RESP_PRESENT | SDMMC_RESP_32) +#define SDMMC_CMD_R4 (SDMMC_RESP_PRESENT | SDMMC_RESP_32) +#define SDMMC_CMD_R5 (SDMMC_RESP_PRESENT | SDMMC_RESP_8 | SDMMC_RESP_CRC) +#define SDMMC_CMD_R6 (SDMMC_RESP_PRESENT | SDMMC_RESP_CRC) +#define SDMMC_CMD_R7 (SDMMC_RESP_PRESENT | SDMMC_RESP_32 | SDMMC_RESP_CRC) +//! @} + +//! \name SD/MMC/SDIO command definitions +//! SDMMC_CMDx are include in SD and MMC norms +//! MMC_CMDx are include in MMC norms only +//! SD_CMDx are include in SD norms only +//! SDIO_CMDx are include in SDIO norms only +//! @{ + +/* + * --- Basic commands and read-stream command (class 0 and class 1) --- + */ + +/** Cmd0(bc): Reset all cards to idle state */ +#define SDMMC_SPI_CMD0_GO_IDLE_STATE (0 | SDMMC_CMD_R1) +#define SDMMC_MCI_CMD0_GO_IDLE_STATE (0 | SDMMC_CMD_NO_RESP | SDMMC_CMD_OPENDRAIN) +/** MMC Cmd1(bcr, R3): Ask the card to send its Operating Conditions */ +#define MMC_SPI_CMD1_SEND_OP_COND (1 | SDMMC_CMD_R1) +#define MMC_MCI_CMD1_SEND_OP_COND (1 | SDMMC_CMD_R3 | SDMMC_CMD_OPENDRAIN) +/** Cmd2(bcr, R2): Ask the card to send its CID number (stuff but arg 0 used) */ +#define SDMMC_CMD2_ALL_SEND_CID (2 | SDMMC_CMD_R2 | SDMMC_CMD_OPENDRAIN) +/** SD Cmd3(bcr, R6): Ask the card to publish a new relative address (RCA) */ +#define SD_CMD3_SEND_RELATIVE_ADDR (3 | SDMMC_CMD_R6 | SDMMC_CMD_OPENDRAIN) +/** MMC Cmd3(ac, R1): Assigns relative address to the card */ +#define MMC_CMD3_SET_RELATIVE_ADDR (3 | SDMMC_CMD_R1) +/** Cmd4(bc): Program the DSR of all cards (MCI only) */ +#define SDMMC_CMD4_SET_DSR (4 | SDMMC_CMD_NO_RESP) +/** MMC Cmd5(ac, R1b): Toggle the card between Sleep state and Standby state. */ +#define MMC_CMD5_SLEEP_AWAKE (5 | SDMMC_CMD_R1B) +/** Cmd7(ac, R1/R1b): Select/Deselect card + * For SD: R1b only from the selected card. + * For MMC: R1 while selecting from Stand-By State to Transfer State; + * R1b while selecting from Disconnected State to Programming State. + */ +#define SDMMC_CMD7_SELECT_CARD_CMD (7 | SDMMC_CMD_R1B) +#define SDMMC_CMD7_DESELECT_CARD_CMD (7 | SDMMC_CMD_R1) +/** MMC Cmd8(adtc, R1): Send EXT_CSD register as a block of data */ +#define MMC_CMD8_SEND_EXT_CSD (8 | SDMMC_CMD_R1 | SDMMC_CMD_SINGLE_BLOCK) +/** SD Cmd8(bcr, R7) : Send SD Memory Card interface condition */ +#define SD_CMD8_SEND_IF_COND (8 | SDMMC_CMD_R7 | SDMMC_CMD_OPENDRAIN) +/** Cmd9 SPI (R1): Addressed card sends its card-specific data (CSD) */ +#define SDMMC_SPI_CMD9_SEND_CSD (9 | SDMMC_CMD_R1 | SDMMC_CMD_SINGLE_BLOCK) +/** Cmd9 MCI (ac, R2): Addressed card sends its card-specific data (CSD) */ +#define SDMMC_MCI_CMD9_SEND_CSD (9 | SDMMC_CMD_R2) +/** Cmd10(ac, R2): Addressed card sends its card identification (CID) */ +#define SDMMC_CMD10_SEND_CID (10 | SDMMC_CMD_R2) +/** + * MMC Cmd11(adtc, R1): Read data stream from the card, starting at the given + * address, until a STOP_TRANSMISSION follows. + */ +#define MMC_CMD11_READ_DAT_UNTIL_STOP (11 | SDMMC_CMD_R1) +/* SD Cmd11 MCI (ac, R1): Voltage switching */ +#define SD_CMD11_READ_DAT_UNTIL_STOP (11 | SDMMC_CMD_R1) +/** Cmd12(ac, R1b): Force the card to stop transmission */ +#define SDMMC_CMD12_STOP_TRANSMISSION (12 | SDMMC_CMD_R1B) +/** Cmd13(R2): Addressed card sends its status register. */ +#define SDMMC_SPI_CMD13_SEND_STATUS (13 | SDMMC_CMD_R2) +/** Cmd13(ac, R1): Addressed card sends its status register. */ +#define SDMMC_MCI_CMD13_SEND_STATUS (13 | SDMMC_CMD_R1) +/** MMC Cmd14(adtc, R1): Read the reversed bus testing data pattern from a card. */ +#define MMC_CMD14_BUSTEST_R (14 | SDMMC_CMD_R1) +/** Cmd15(ac): Send an addressed card into the Inactive State. */ +// Note: It is a ac cmd, but it must be send like bc cmd to open drain +#define SDMMC_CMD15_GO_INACTIVE_STATE (15 | SDMMC_CMD_NO_RESP | SDMMC_CMD_OPENDRAIN) +/** MMC Cmd19(adtc, R1): Send the bus test data pattern */ +#define MMC_CMD19_BUSTEST_W (19 | SDMMC_CMD_R1) +/** Cmd58(R3): Reads the OCR register of a card */ +#define SDMMC_SPI_CMD58_READ_OCR (58 | SDMMC_CMD_R3) +/** Cmd59(R1): Turns the CRC option on or off */ +#define SDMMC_SPI_CMD59_CRC_ON_OFF (59 | SDMMC_CMD_R1) + +/* + * --- Block-oriented read commands (class 2) --- + */ +/** Cmd16(ac, R1): Set the block length (in bytes) */ +#define SDMMC_CMD16_SET_BLOCKLEN (16 | SDMMC_CMD_R1) +/** Cmd17(adtc, R1): Read single block */ +#define SDMMC_CMD17_READ_SINGLE_BLOCK (17 | SDMMC_CMD_R1 | SDMMC_CMD_SINGLE_BLOCK) +/** Cmd18(adtc, R1): Read multiple block */ +#define SDMMC_CMD18_READ_MULTIPLE_BLOCK (18 | SDMMC_CMD_R1 | SDMMC_CMD_MULTI_BLOCK) + +/* + * --- Sequential write commands (class 3) --- + */ + +/** + * MMC Cmd20(adtc, R1): Write a data stream from the host, starting at the + * given address, until a STOP_TRANSMISSION follows. + */ +#define MMC_CMD20_WRITE_DAT_UNTIL_STOP (20 | SDMMC_CMD_R1) + +/* + * --- Block-oriented write commands (class 4) --- + */ +/** MMC Cmd23(ac, R1): Set block count */ +#define MMC_CMD23_SET_BLOCK_COUNT (23 | SDMMC_CMD_R1) +/** Cmd24(adtc, R1): Write block */ +#define SDMMC_CMD24_WRITE_BLOCK (24 | SDMMC_CMD_R1 | SDMMC_CMD_WRITE | SDMMC_CMD_SINGLE_BLOCK) +/** Cmd25(adtc, R1): Write multiple block */ +#define SDMMC_CMD25_WRITE_MULTIPLE_BLOCK (25 | SDMMC_CMD_R1 | SDMMC_CMD_WRITE | SDMMC_CMD_MULTI_BLOCK) +/** MMC Cmd26(adtc, R1): Programming of the card identification register. */ +#define MMC_CMD26_PROGRAM_CID (26 | SDMMC_CMD_R1) +/** Cmd27(adtc, R1): Programming of the programmable bits of the CSD. */ +#define SDMMC_CMD27_PROGRAM_CSD (27 | SDMMC_CMD_R1) + +/* + * --- Erase commands (class 5) --- + */ +/** SD Cmd32(ac, R1): */ +#define SD_CMD32_ERASE_WR_BLK_START (32 | SDMMC_CMD_R1) +/** SD Cmd33(ac, R1): */ +#define SD_CMD33_ERASE_WR_BLK_END (33 | SDMMC_CMD_R1) +/** MMC Cmd35(ac, R1): */ +#define MMC_CMD35_ERASE_GROUP_START (35 | SDMMC_CMD_R1) +/** MMC Cmd36(ac, R1): */ +#define MMC_CMD36_ERASE_GROUP_END (36 | SDMMC_CMD_R1) +/** Cmd38(ac, R1B): */ +#define SDMMC_CMD38_ERASE (38 | SDMMC_CMD_R1B) + +/* + * --- Block Oriented Write Protection Commands (class 6) --- + */ +/** Cmd28(ac, R1b): Set write protection */ +#define SDMMC_CMD28_SET_WRITE_PROT (28 | SDMMC_CMD_R1B) +/** Cmd29(ac, R1b): Clr write protection */ +#define SDMMC_CMD29_CLR_WRITE_PROT (29 | SDMMC_CMD_R1B) +/** Cmd30(adtc, R1b): Send write protection */ +#define SDMMC_CMD30_SEND_WRITE_PROT (30 | SDMMC_CMD_R1) + +/* + * --- Lock Card (class 7) --- + */ +/** Cmd42(adtc, R1): Used to set/reset the password or lock/unlock the card. */ +#define SDMMC_CMD42_LOCK_UNLOCK (42 | SDMMC_CMD_R1) + +/* + * --- Application-specific commands (class 8) --- + */ +/** + * Cmd55(ac, R1): Indicate to the card that the next command is an application + * specific command rather than a standard command. + */ +#define SDMMC_CMD55_APP_CMD (55 | SDMMC_CMD_R1) +/** + * Cmd 56(adtc, R1): Used either to transfer a data block to the card or to get + * a data block from the card for general purpose/application specific commands. + */ +#define SDMMC_CMD56_GEN_CMD (56 | SDMMC_CMD_R1) + +/** + * MMC Cmd6(ac, R1b) : Switche the mode of operation of the selected card + * or modifies the EXT_CSD registers. + */ +#define MMC_CMD6_SWITCH (6 | SDMMC_CMD_R1B) +/** + * SD Cmd6(adtc, R1) : Check switchable function (mode 0) + * and switch card function (mode 1). + */ +#define SD_CMD6_SWITCH_FUNC (6 | SDMMC_CMD_R1 | SDMMC_CMD_SINGLE_BLOCK) +/** ACMD6(ac, R1): Define the data bus width */ +#define SD_ACMD6_SET_BUS_WIDTH (6 | SDMMC_CMD_R1) +/** ACMD13(adtc, R1): Send the SD Status. */ +#define SD_ACMD13_SD_STATUS (13 | SDMMC_CMD_R1) +/** + * ACMD22(adtc, R1): Send the number of the written (with-out errors) write + * blocks. + */ +#define SD_ACMD22_SEND_NUM_WR_BLOCKS (22 | SDMMC_CMD_R1) +/** + * ACMD23(ac, R1): Set the number of write blocks to be pre-erased before + * writing + */ +#define SD_ACMD23_SET_WR_BLK_ERASE_COUNT (23 | SDMMC_CMD_R1) +/** + * ACMD41(bcr, R3): Send host capacity support information (HCS) and asks the + * accessed card to send its operating condition register (OCR) content + * in the response + */ +#define SD_MCI_ACMD41_SD_SEND_OP_COND (41 | SDMMC_CMD_R3 | SDMMC_CMD_OPENDRAIN) +/** + * ACMD41(R1): Send host capacity support information (HCS) and activates the + * card's initilization process + */ +#define SD_SPI_ACMD41_SD_SEND_OP_COND (41 | SDMMC_CMD_R1) +/** + * ACMD42(ac, R1): Connect[1]/Disconnect[0] the 50 KOhm pull-up resistor on + * CD/DAT3 (pin 1) of the card. + */ +#define SD_ACMD42_SET_CLR_CARD_DETECT (42 | SDMMC_CMD_R1) +/** ACMD51(adtc, R1): Read the SD Configuration Register (SCR). */ +#define SD_ACMD51_SEND_SCR (51 | SDMMC_CMD_R1 | SDMMC_CMD_SINGLE_BLOCK) + +/* + * --- I/O mode commands (class 9) --- + */ +/** MMC Cmd39(ac, R4): Used to write and read 8 bit (register) data fields. */ +#define MMC_CMD39_FAST_IO (39 | SDMMC_CMD_R4) +/** MMC Cmd40(bcr, R5): Set the system into interrupt mode */ +#define MMC_CMD40_GO_IRQ_STATE (40 | SDMMC_CMD_R5 | SDMMC_CMD_OPENDRAIN) +/** SDIO Cmd5(R4): Send operation condition */ +#define SDIO_CMD5_SEND_OP_COND (5 | SDMMC_CMD_R4 | SDMMC_CMD_OPENDRAIN) +/** SDIO CMD52(R5): Direct IO read/write */ +#define SDIO_CMD52_IO_RW_DIRECT (52 | SDMMC_CMD_R5) +/** SDIO CMD53(R5): Extended IO read/write */ +#define SDIO_CMD53_IO_R_BYTE_EXTENDED (53 | SDMMC_CMD_R5 | SDMMC_CMD_SDIO_BYTE) +#define SDIO_CMD53_IO_W_BYTE_EXTENDED (53 | SDMMC_CMD_R5 | SDMMC_CMD_SDIO_BYTE | SDMMC_CMD_WRITE) +#define SDIO_CMD53_IO_R_BLOCK_EXTENDED (53 | SDMMC_CMD_R5 | SDMMC_CMD_SDIO_BLOCK) +#define SDIO_CMD53_IO_W_BLOCK_EXTENDED (53 | SDMMC_CMD_R5 | SDMMC_CMD_SDIO_BLOCK | SDMMC_CMD_WRITE) +//! @} +//! @} + + +//! \name Macros for command argument definition +//! @{ + + //! \name MMC CMD6 argument structure + //! @{ +//! [31:26] Set to 0 +//! [25:24] Access +#define MMC_CMD6_ACCESS_COMMAND_SET (0lu << 24) +#define MMC_CMD6_ACCESS_SET_BITS (1lu << 24) +#define MMC_CMD6_ACCESS_CLEAR_BITS (2lu << 24) +#define MMC_CMD6_ACCESS_WRITE_BYTE (3lu << 24) +//! [23:16] Index for Mode Segment +#define MMC_CMD6_INDEX_CMD_SET (EXT_CSD_CMD_SET_INDEX << 16) +#define MMC_CMD6_INDEX_CMD_SET_REV (EXT_CSD_CMD_SET_REV_INDEX << 16) +#define MMC_CMD6_INDEX_POWER_CLASS (EXT_CSD_POWER_CLASS_INDEX << 16) +#define MMC_CMD6_INDEX_HS_TIMING (EXT_CSD_HS_TIMING_INDEX << 16) +#define MMC_CMD6_INDEX_BUS_WIDTH (EXT_CSD_BUS_WIDTH_INDEX << 16) +#define MMC_CMD6_INDEX_ERASED_MEM_CONT (EXT_CSD_ERASED_MEM_CONT_INDEX << 16) +#define MMC_CMD6_INDEX_BOOT_CONFIG (EXT_CSD_BOOT_CONFIG_INDEX << 16) +#define MMC_CMD6_INDEX_BOOT_BUS_WIDTH (EXT_CSD_BOOT_BUS_WIDTH_INDEX << 16) +#define MMC_CMD6_INDEX_ERASE_GROUP_DEF (EXT_CSD_ERASE_GROUP_DEF_INDEX << 16) +//! [15:8] Value +#define MMC_CMD6_VALUE_BUS_WIDTH_1BIT (0x0lu << 8) +#define MMC_CMD6_VALUE_BUS_WIDTH_4BIT (0x1lu << 8) +#define MMC_CMD6_VALUE_BUS_WIDTH_8BIT (0x2lu << 8) +#define MMC_CMD6_VALUE_HS_TIMING_ENABLE (0x1lu << 8) +#define MMC_CMD6_VALUE_HS_TIMING_DISABLE (0x0lu << 8) +//! [7:3] Set to 0 +//! [2:0] Cmd Set + //! @} + + //! \name SD CMD6 argument structure + //! @{ +//! CMD6 arg[ 3: 0] function group 1, access mode +#define SD_CMD6_GRP1_HIGH_SPEED (0x1lu << 0) +#define SD_CMD6_GRP1_DEFAULT (0x0lu << 0) +//! CMD6 arg[ 7: 4] function group 2, command system +#define SD_CMD6_GRP2_NO_INFLUENCE (0xFlu << 4) +#define SD_CMD6_GRP2_DEFAULT (0x0lu << 4) +//! CMD6 arg[11: 8] function group 3, 0xF or 0x0 +#define SD_CMD6_GRP3_NO_INFLUENCE (0xFlu << 8) +#define SD_CMD6_GRP3_DEFAULT (0x0lu << 8) +//! CMD6 arg[15:12] function group 4, 0xF or 0x0 +#define SD_CMD6_GRP4_NO_INFLUENCE (0xFlu << 12) +#define SD_CMD6_GRP4_DEFAULT (0x0lu << 12) +//! CMD6 arg[19:16] function group 5, 0xF or 0x0 +#define SD_CMD6_GRP5_NO_INFLUENCE (0xFlu << 16) +#define SD_CMD6_GRP5_DEFAULT (0x0lu << 16) +//! CMD6 arg[23:20] function group 6, 0xF or 0x0 +#define SD_CMD6_GRP6_NO_INFLUENCE (0xFlu << 20) +#define SD_CMD6_GRP6_DEFAULT (0x0lu << 20) +//! CMD6 arg[30:24] reserved 0 +//! CMD6 arg[31 ] Mode, 0: Check, 1: Switch +#define SD_CMD6_MODE_CHECK (0lu << 31) +#define SD_CMD6_MODE_SWITCH (1lu << 31) + //! @} + + //! \name SD CMD8 argument structure + //! @{ +#define SD_CMD8_PATTERN 0xAA +#define SD_CMD8_MASK_PATTERN 0xFF +#define SD_CMD8_HIGH_VOLTAGE 0x100 +#define SD_CMD8_MASK_VOLTAGE 0xF00 + //! @} + + //! \name SD ACMD41 arguments + //! @{ +#define SD_ACMD41_HCS (1lu << 30) //!< (SD) Host Capacity Support + //! @} +//! @} + + +//! \name SDIO definitions +//! @{ + + //! \name SDIO state (in R5) + //! @{ +#define SDIO_R5_COM_CRC_ERROR (1lu << 15) /**< CRC check error */ +#define SDIO_R5_ILLEGAL_COMMAND (1lu << 14) /**< Illegal command */ +#define SDIO_R5_STATE (3lu << 12) /**< SDIO R5 state mask */ +#define SDIO_R5_STATE_DIS (0lu << 12) /**< Disabled */ +#define SDIO_R5_STATE_CMD (1lu << 12) /**< DAT lines free */ +#define SDIO_R5_STATE_TRN (2lu << 12) /**< Transfer */ +#define SDIO_R5_STATE_RFU (3lu << 12) /**< Reserved */ +#define SDIO_R5_ERROR (1lu << 11) /**< General error */ +#define SDIO_R5_FUNC_NUM (1lu << 9) /**< Invalid function number */ +#define SDIO_R5_OUT_OF_RANGE (1lu << 8) /**< Argument out of range */ +#define SDIO_R5_STATUS_ERR (SDIO_R5_ERROR | SDIO_R5_FUNC_NUM \ + | SDIO_R5_OUT_OF_RANGE) //!< Errro status bits mask + //! @} + + //! \name SDIO state (in R6) + //! @{ +/** The CRC check of the previous command failed. */ +#define SDIO_R6_COM_CRC_ERROR (1lu << 15) +/** Command not legal for the card state. */ +#define SDIO_R6_ILLEGAL_COMMAND (1lu << 14) +/** A general or an unknown error occurred during the operation. */ +#define SDIO_R6_ERROR (1lu << 13) +/** Status bits mask for SDIO R6 */ +#define SDIO_STATUS_R6 (SDIO_R6_COM_CRC_ERROR \ + | SDIO_R6_ILLEGAL_COMMAND | SDIO_R6_ERROR) + //! @} + + //! \name SDIO CMD52 argument bit offset + //! @{ +//! CMD52 arg[ 7: 0] Write data or stuff bits +#define SDIO_CMD52_WR_DATA 0 +//! CMD52 arg[ 8] Reserved +#define SDIO_CMD52_STUFF0 8 +//! CMD52 arg[25: 9] Register address +#define SDIO_CMD52_REG_ADRR 9 +//! CMD52 arg[ 26] Reserved +#define SDIO_CMD52_STUFF1 26 +//! CMD52 arg[ 27] Read after Write flag +#define SDIO_CMD52_RAW_FLAG 27 +//! CMD52 arg[30:28] Number of the function +#define SDIO_CMD52_FUNCTION_NUM 28 +//! CMD52 arg[ 31] Direction, 1:write, 0:read. +#define SDIO_CMD52_RW_FLAG 31 +# define SDIO_CMD52_READ_FLAG 0 +# define SDIO_CMD52_WRITE_FLAG 1 + //! @} + + //! \name SDIO CMD53 argument structure + //! @{ +/** + * [ 8: 0] Byte mode: number of bytes to transfer, + * 0 cause 512 bytes transfer. + * Block mode: number of blocks to transfer, + * 0 set count to infinite. + */ +#define SDIO_CMD53_COUNT 0 +//! CMD53 arg[25: 9] Start Address I/O register +#define SDIO_CMD53_REG_ADDR 9 +//! CMD53 arg[ 26] 1:Incrementing address, 0: fixed +#define SDIO_CMD53_OP_CODE 26 +//! CMD53 arg[ 27] (Optional) 1:block mode +#define SDIO_CMD53_BLOCK_MODE 27 +//! CMD53 arg[30:28] Number of the function +#define SDIO_CMD53_FUNCTION_NUM 28 +//! CMD53 arg[ 31] Direction, 1:WR, 0:RD +#define SDIO_CMD53_RW_FLAG 31 +# define SDIO_CMD53_READ_FLAG 0 +# define SDIO_CMD53_WRITE_FLAG 1 + //! @} + + //! \name SDIO Functions + //! @{ +#define SDIO_CIA 0 /**< SDIO Function 0 (CIA) */ +#define SDIO_FN0 0 /**< SDIO Function 0 */ +#define SDIO_FN1 1 /**< SDIO Function 1 */ +#define SDIO_FN2 2 /**< SDIO Function 2 */ +#define SDIO_FN3 3 /**< SDIO Function 3 */ +#define SDIO_FN4 4 /**< SDIO Function 4 */ +#define SDIO_FN5 5 /**< SDIO Function 5 */ +#define SDIO_FN6 6 /**< SDIO Function 6 */ +#define SDIO_FN7 7 /**< SDIO Function 7 */ + //! @} + + //! \name SDIO Card Common Control Registers (CCCR) + //! @{ +#define SDIO_CCCR_SDIO_REV 0x00 /**< CCCR/SDIO revision (RO) */ +#define SDIO_CCCR_REV_1_00 (0x0lu << 0) /**< CCCR/FBR Version 1.00 */ +#define SDIO_CCCR_REV_1_10 (0x1lu << 0) /**< CCCR/FBR Version 1.10 */ +#define SDIO_CCCR_REV_2_00 (0x2lu << 0) /**< CCCR/FBR Version 2.00 */ +#define SDIO_CCCR_REV_3_00 (0x3lu << 0) /**< CCCR/FBR Version 3.00 */ +#define SDIO_SDIO_REV_1_00 (0x0lu << 4) /**< SDIO Spec 1.00 */ +#define SDIO_SDIO_REV_1_10 (0x1lu << 4) /**< SDIO Spec 1.10 */ +#define SDIO_SDIO_REV_1_20 (0x2lu << 4) /**< SDIO Spec 1.20(unreleased) */ +#define SDIO_SDIO_REV_2_00 (0x3lu << 4) /**< SDIO Spec Version 2.00 */ +#define SDIO_SDIO_REV_3_00 (0x4lu << 4) /**< SDIO Spec Version 3.00 */ +#define SDIO_CCCR_SD_REV 0x01 /**< SD Spec Revision (RO) */ +#define SDIO_SD_REV_1_01 (0x0lu << 0) /**< SD 1.01 (Mar 2000) */ +#define SDIO_SD_REV_1_10 (0x1lu << 0) /**< SD 1.10 (Oct 2004) */ +#define SDIO_SD_REV_2_00 (0x2lu << 0) /**< SD 2.00 (May 2006) */ +#define SDIO_SD_REV_3_00 (0x3lu << 0) /**< SD 3.00 */ +#define SDIO_CCCR_IOE 0x02 /**< I/O Enable (R/W) */ +#define SDIO_IOE_FN1 (0x1lu << 1) /**< Function 1 Enable/Disable */ +#define SDIO_IOE_FN2 (0x1lu << 2) /**< Function 2 Enable/Disable */ +#define SDIO_IOE_FN3 (0x1lu << 3) /**< Function 3 Enable/Disable */ +#define SDIO_IOE_FN4 (0x1lu << 4) /**< Function 4 Enable/Disable */ +#define SDIO_IOE_FN5 (0x1lu << 5) /**< Function 5 Enable/Disable */ +#define SDIO_IOE_FN6 (0x1lu << 6) /**< Function 6 Enable/Disable */ +#define SDIO_IOE_FN7 (0x1lu << 7) /**< Function 7 Enable/Disable */ +#define SDIO_CCCR_IOR 0x03 /**< I/O Ready (RO) */ +#define SDIO_IOR_FN1 (0x1lu << 1) /**< Function 1 ready */ +#define SDIO_IOR_FN2 (0x1lu << 2) /**< Function 2 ready */ +#define SDIO_IOR_FN3 (0x1lu << 3) /**< Function 3 ready */ +#define SDIO_IOR_FN4 (0x1lu << 4) /**< Function 4 ready */ +#define SDIO_IOR_FN5 (0x1lu << 5) /**< Function 5 ready */ +#define SDIO_IOR_FN6 (0x1lu << 6) /**< Function 6 ready */ +#define SDIO_IOR_FN7 (0x1lu << 7) /**< Function 7 ready */ +#define SDIO_CCCR_IEN 0x04 /**< Int Enable */ +#define SDIO_IENM (0x1lu << 0) /**< Int Enable Master (R/W) */ +#define SDIO_IEN_FN1 (0x1lu << 1) /**< Function 1 Int Enable */ +#define SDIO_IEN_FN2 (0x1lu << 2) /**< Function 2 Int Enable */ +#define SDIO_IEN_FN3 (0x1lu << 3) /**< Function 3 Int Enable */ +#define SDIO_IEN_FN4 (0x1lu << 4) /**< Function 4 Int Enable */ +#define SDIO_IEN_FN5 (0x1lu << 5) /**< Function 5 Int Enable */ +#define SDIO_IEN_FN6 (0x1lu << 6) /**< Function 6 Int Enable */ +#define SDIO_IEN_FN7 (0x1lu << 7) /**< Function 7 Int Enable */ +#define SDIO_CCCR_INT 0x05 /**< Int Pending */ +#define SDIO_INT_FN1 (0x1lu << 1) /**< Function 1 Int pending */ +#define SDIO_INT_FN2 (0x1lu << 2) /**< Function 2 Int pending */ +#define SDIO_INT_FN3 (0x1lu << 3) /**< Function 3 Int pending */ +#define SDIO_INT_FN4 (0x1lu << 4) /**< Function 4 Int pending */ +#define SDIO_INT_FN5 (0x1lu << 5) /**< Function 5 Int pending */ +#define SDIO_INT_FN6 (0x1lu << 6) /**< Function 6 Int pending */ +#define SDIO_INT_FN7 (0x1lu << 7) /**< Function 7 Int pending */ +#define SDIO_CCCR_IOA 0x06 /**< I/O Abort */ +#define SDIO_AS_FN1 (0x1lu << 0) /**< Abort function 1 IO */ +#define SDIO_AS_FN2 (0x2lu << 0) /**< Abort function 2 IO */ +#define SDIO_AS_FN3 (0x3lu << 0) /**< Abort function 3 IO */ +#define SDIO_AS_FN4 (0x4lu << 0) /**< Abort function 4 IO */ +#define SDIO_AS_FN5 (0x5lu << 0) /**< Abort function 5 IO */ +#define SDIO_AS_FN6 (0x6lu << 0) /**< Abort function 6 IO */ +#define SDIO_AS_FN7 (0x7lu << 0) /**< Abort function 7 IO */ +#define SDIO_RES (0x1lu << 3) /**< IO CARD RESET (WO) */ +#define SDIO_CCCR_BUS_CTRL 0x07 /**< Bus Interface Control */ +#define SDIO_BUSWIDTH_1B (0x0lu << 0) /**< 1-bit data bus */ +#define SDIO_BUSWIDTH_4B (0x2lu << 0) /**< 4-bit data bus */ +/** Enable Continuous SPI interrupt (R/W) */ +#define SDIO_BUS_ECSI (0x1lu << 5) +/** Support Continuous SPI interrupt (RO) */ +#define SDIO_BUS_SCSI (0x1lu << 6) +/** Connect(0)/Disconnect(1) pull-up on CD/DAT[3] (R/W) */ +#define SDIO_BUS_CD_DISABLE (0x1lu << 7) +#define SDIO_CCCR_CAP 0x08 /**< Card Capability */ +/** Support Direct Commands during data transfer (RO) */ +#define SDIO_CAP_SDC (0x1lu << 0) +/** Support Multi-Block (RO) */ +#define SDIO_CAP_SMB (0x1lu << 1) +/** Support Read Wait (RO) */ +#define SDIO_CAP_SRW (0x1lu << 2) +/** Support Suspend/Resume (RO) */ +#define SDIO_CAP_SBS (0x1lu << 3) +/** Support interrupt between blocks of data in 4-bit SD mode (RO) */ +#define SDIO_CAP_S4MI (0x1lu << 4) +/** Enable interrupt between blocks of data in 4-bit SD mode (R/W) */ +#define SDIO_CAP_E4MI (0x1lu << 5) +/** Low-Speed Card (RO) */ +#define SDIO_CAP_LSC (0x1lu << 6) +/** 4-bit support for Low-Speed Card (RO) */ +#define SDIO_CAP_4BLS (0x1lu << 7) +/** Pointer to CIS (3B, LSB first) */ +#define SDIO_CCCR_CIS_PTR 0x09 +/** Bus Suspend */ +#define SDIO_CCCR_BUS_SUSPEND 0x0C +/** Bus Status (transfer on DAT[x] lines) (RO) */ +#define SDIO_BS (0x1lu << 0) +/** Bus Release Request/Status (R/W) */ +#define SDIO_BR (0x1lu << 1) +#define SDIO_CCCR_FUN_SEL 0x0D /**< Function select */ +#define SDIO_DF (0x1lu << 7) /**< Resume Data Flag (RO) */ +#define SDIO_FS_CIA (0x0lu << 0) /**< Select CIA (function 0) */ +#define SDIO_FS_FN1 (0x1lu << 0) /**< Select Function 1 */ +#define SDIO_FS_FN2 (0x2lu << 0) /**< Select Function 2 */ +#define SDIO_FS_FN3 (0x3lu << 0) /**< Select Function 3 */ +#define SDIO_FS_FN4 (0x4lu << 0) /**< Select Function 4 */ +#define SDIO_FS_FN5 (0x5lu << 0) /**< Select Function 5 */ +#define SDIO_FS_FN6 (0x6lu << 0) /**< Select Function 6 */ +#define SDIO_FS_FN7 (0x7lu << 0) /**< Select Function 7 */ +#define SDIO_FS_MEM (0x8lu << 0) /**< Select memory in combo card */ +#define SDIO_CCCR_EXEC 0x0E /**< Exec Flags (RO) */ +#define SDIO_EXM (0x1lu << 0) /**< Executing status of memory */ +#define SDIO_EX_FN1 (0x1lu << 1) /**< Executing status of func 1 */ +#define SDIO_EX_FN2 (0x1lu << 2) /**< Executing status of func 2 */ +#define SDIO_EX_FN3 (0x1lu << 3) /**< Executing status of func 3 */ +#define SDIO_EX_FN4 (0x1lu << 4) /**< Executing status of func 4 */ +#define SDIO_EX_FN5 (0x1lu << 5) /**< Executing status of func 5 */ +#define SDIO_EX_FN6 (0x1lu << 6) /**< Executing status of func 6 */ +#define SDIO_EX_FN7 (0x1lu << 7) /**< Executing status of func 7 */ +#define SDIO_CCCR_READY 0x0F /**< Ready Flags (RO) */ +#define SDIO_RFM (0x1lu << 0) /**< Ready Flag for memory */ +#define SDIO_RF_FN1 (0x1lu << 1) /**< Ready Flag for function 1 */ +#define SDIO_RF_FN2 (0x1lu << 2) /**< Ready Flag for function 2 */ +#define SDIO_RF_FN3 (0x1lu << 3) /**< Ready Flag for function 3 */ +#define SDIO_RF_FN4 (0x1lu << 4) /**< Ready Flag for function 4 */ +#define SDIO_RF_FN5 (0x1lu << 5) /**< Ready Flag for function 5 */ +#define SDIO_RF_FN6 (0x1lu << 6) /**< Ready Flag for function 6 */ +#define SDIO_RF_FN7 (0x1lu << 7) /**< Ready Flag for function 7 */ +#define SDIO_CCCR_FN0_BLKSIZ 0x10 /**< FN0 Block Size (2B, LSB first) (R/W) */ +#define SDIO_CCCR_POWER 0x12 /**< Power Control */ +#define SDIO_POWER_SMPC (0x1lu << 0) /**< Support Master Power Control*/ +#define SDIO_POWER_EMPC (0x1lu << 1) /**< Enable Master Power Control */ +#define SDIO_CCCR_HS 0x13 /**< High-Speed */ +#define SDIO_SHS (0x1lu << 0) /**< Support High-Speed (RO) */ +#define SDIO_EHS (0x1lu << 1) /**< Enable High-Speed (R/W) */ + //! @} + + //! \name SDIO Card Metaformat + //! @{ +/** Null tuple (PCMCIA 3.1.9) */ +#define SDIO_CISTPL_NULL 0x00 +/** Device tuple (PCMCIA 3.2.2) */ +#define SDIO_CISTPL_DEVICE 0x01 +/** Checksum control (PCMCIA 3.1.1) */ +#define SDIO_CISTPL_CHECKSUM 0x10 +/** Level 1 version (PCMCIA 3.2.10) */ +#define SDIO_CISTPL_VERS_1 0x15 +/** Alternate Language String (PCMCIA 3.2.1) */ +#define SDIO_CISTPL_ALTSTR 0x16 +/** Manufacturer Identification String (PCMCIA 3.2.9) */ +#define SDIO_CISTPL_MANFID 0x20 +/** Function Identification (PCMCIA 3.2.7) */ +#define SDIO_CISTPL_FUNCID 0x21 +/** Function Extensions (PCMCIA 3.2.6) */ +#define SDIO_CISTPL_FUNCE 0x22 +/** Additional information for SDIO (PCMCIA 6.1.2) */ +#define SDIO_CISTPL_SDIO_STD 0x91 +/** Reserved for future SDIO (PCMCIA 6.1.3) */ +#define SDIO_CISTPL_SDIO_EXT 0x92 +/** The End-of-chain Tuple (PCMCIA 3.1.2) */ +#define SDIO_CISTPL_END 0xFF + //! @} + +//! @} + +//! \name CSD, OCR, SCR, Switch status, extend CSD definitions +//! @{ + +/** + * \brief Macro function to extract a bits field from a large SD MMC register + * Used by : CSD, SCR, Switch status + */ +static inline uint32_t SDMMC_UNSTUFF_BITS(uint8_t *reg, uint16_t reg_size, + uint16_t pos, uint8_t size) +{ + uint32_t value; + value = reg[((reg_size - pos + 7) / 8) - 1] >> (pos % 8); + if (((pos % 8) + size) > 8) { + value |= (uint32_t)reg[((reg_size - pos + 7) / 8) - 2] << (8 - (pos % 8)); + } + if (((pos % 8) + size) > 16) { + value |= (uint32_t)reg[((reg_size - pos + 7) / 8) - 3] << (16 - (pos % 8)); + } + if (((pos % 8) + size) > 16) { + value |= (uint32_t)reg[((reg_size - pos + 7) / 8) - 3] << (16 - (pos % 8)); + } + value &= ((uint32_t)1 << size) - 1; + return value; +} + + //! \name CSD Fields + //! @{ +#define CSD_REG_BIT_SIZE 128 //!< 128 bits +#define CSD_REG_BSIZE (CSD_REG_BIT_SIZE / 8) //!< 16 bytes +#define CSD_STRUCTURE(csd, pos, size) \ + SDMMC_UNSTUFF_BITS(csd, CSD_REG_BIT_SIZE, pos, size) +#define CSD_STRUCTURE_VERSION(csd) CSD_STRUCTURE(csd, 126, 2) +#define SD_CSD_VER_1_0 0 +#define SD_CSD_VER_2_0 1 +#define MMC_CSD_VER_1_0 0 +#define MMC_CSD_VER_1_1 1 +#define MMC_CSD_VER_1_2 2 +#define CSD_TRAN_SPEED(csd) CSD_STRUCTURE(csd, 96, 8) +#define SD_CSD_1_0_C_SIZE(csd) CSD_STRUCTURE(csd, 62, 12) +#define SD_CSD_1_0_C_SIZE_MULT(csd) CSD_STRUCTURE(csd, 47, 3) +#define SD_CSD_1_0_READ_BL_LEN(csd) CSD_STRUCTURE(csd, 80, 4) +#define SD_CSD_2_0_C_SIZE(csd) CSD_STRUCTURE(csd, 48, 22) +#define MMC_CSD_C_SIZE(csd) CSD_STRUCTURE(csd, 62, 12) +#define MMC_CSD_C_SIZE_MULT(csd) CSD_STRUCTURE(csd, 47, 3) +#define MMC_CSD_READ_BL_LEN(csd) CSD_STRUCTURE(csd, 80, 4) +#define MMC_CSD_SPEC_VERS(csd) CSD_STRUCTURE(csd, 122, 4) + //! @} + + //! \name OCR Register Fields + //! @{ +#define OCR_REG_BSIZE (32 / 8) /**< 32 bits, 4 bytes */ +#define OCR_VDD_170_195 (1lu << 7) +#define OCR_VDD_20_21 (1lu << 8) +#define OCR_VDD_21_22 (1lu << 9) +#define OCR_VDD_22_23 (1lu << 10) +#define OCR_VDD_23_24 (1lu << 11) +#define OCR_VDD_24_25 (1lu << 12) +#define OCR_VDD_25_26 (1lu << 13) +#define OCR_VDD_26_27 (1lu << 14) +#define OCR_VDD_27_28 (1lu << 15) +#define OCR_VDD_28_29 (1lu << 16) +#define OCR_VDD_29_30 (1lu << 17) +#define OCR_VDD_30_31 (1lu << 18) +#define OCR_VDD_31_32 (1lu << 19) +#define OCR_VDD_32_33 (1lu << 20) +#define OCR_VDD_33_34 (1lu << 21) +#define OCR_VDD_34_35 (1lu << 22) +#define OCR_VDD_35_36 (1lu << 23) +#define OCR_SDIO_S18R (1lu << 24) /**< Switching to 1.8V Accepted */ +#define OCR_SDIO_MP (1lu << 27) /**< Memory Present */ +#define OCR_SDIO_NF (7lu << 28) /**< Number of I/O Functions */ +#define OCR_ACCESS_MODE_MASK (3lu << 29) /**< (MMC) Access mode mask */ +#define OCR_ACCESS_MODE_BYTE (0lu << 29) /**< (MMC) Byte access mode */ +#define OCR_ACCESS_MODE_SECTOR (2lu << 29) /**< (MMC) Sector access mode */ +#define OCR_CCS (1lu << 30) /**< (SD) Card Capacity Status */ +#define OCR_POWER_UP_BUSY (1lu << 31) /**< Card power up status bit */ + //! @} + + //! \name SD SCR Register Fields + //! @{ +#define SD_SCR_REG_BIT_SIZE 64 //!< 64 bits +#define SD_SCR_REG_BSIZE (SD_SCR_REG_BIT_SIZE / 8) //!< 8 bytes +#define SD_SCR_STRUCTURE(scr, pos, size) \ + SDMMC_UNSTUFF_BITS(scr, SD_SCR_REG_BIT_SIZE, pos, size) +#define SD_SCR_SCR_STRUCTURE(scr) SD_SCR_STRUCTURE(scr, 60, 4) +#define SD_SCR_SCR_STRUCTURE_1_0 0 +#define SD_SCR_SD_SPEC(scr) SD_SCR_STRUCTURE(scr, 56, 4) +#define SD_SCR_SD_SPEC_1_0_01 0 +#define SD_SCR_SD_SPEC_1_10 1 +#define SD_SCR_SD_SPEC_2_00 2 +#define SD_SCR_DATA_STATUS_AFTER_ERASE(scr) SD_SCR_STRUCTURE(scr, 55, 1) +#define SD_SCR_SD_SECURITY(scr) SD_SCR_STRUCTURE(scr, 52, 3) +#define SD_SCR_SD_SECURITY_NO 0 +#define SD_SCR_SD_SECURITY_NOTUSED 1 +#define SD_SCR_SD_SECURITY_1_01 2 +#define SD_SCR_SD_SECURITY_2_00 3 +#define SD_SCR_SD_SECURITY_3_00 4 +#define SD_SCR_SD_BUS_WIDTHS(scr) SD_SCR_STRUCTURE(scr, 48, 4) +#define SD_SCR_SD_BUS_WIDTH_1BITS (1lu << 0) +#define SD_SCR_SD_BUS_WIDTH_4BITS (1lu << 2) +#define SD_SCR_SD_SPEC3(scr) SD_SCR_STRUCTURE(scr, 47, 1) +#define SD_SCR_SD_SPEC_3_00 1 +#define SD_SCR_SD_EX_SECURITY(scr) SD_SCR_STRUCTURE(scr, 43, 4) +#define SD_SCR_SD_CMD_SUPPORT(scr) SD_SCR_STRUCTURE(scr, 32, 2) + //! @} + + //! \name SD Switch Status Fields + //! @{ +#define SD_SW_STATUS_BIT_SIZE 512 //!< 512 bits +#define SD_SW_STATUS_BSIZE (SD_SW_STATUS_BIT_SIZE / 8) //!< 64 bytes +#define SD_SW_STATUS_STRUCTURE(sd_sw_status, pos, size) \ + SDMMC_UNSTUFF_BITS(sd_sw_status, SD_SW_STATUS_BIT_SIZE, pos, size) +#define SD_SW_STATUS_MAX_CURRENT_CONSUMPTION(status) \ + SD_SW_STATUS_STRUCTURE(status, 496, 16) +#define SD_SW_STATUS_FUN_GRP6_INFO(status) \ + SD_SW_STATUS_STRUCTURE(status, 480, 16) +#define SD_SW_STATUS_FUN_GRP5_INFO(status) \ + SD_SW_STATUS_STRUCTURE(status, 464, 16) +#define SD_SW_STATUS_FUN_GRP4_INFO(status) \ + SD_SW_STATUS_STRUCTURE(status, 448, 16) +#define SD_SW_STATUS_FUN_GRP3_INFO(status) \ + SD_SW_STATUS_STRUCTURE(status, 432, 16) +#define SD_SW_STATUS_FUN_GRP2_INFO(status) \ + SD_SW_STATUS_STRUCTURE(status, 416, 16) +#define SD_SW_STATUS_FUN_GRP1_INFO(status) \ + SD_SW_STATUS_STRUCTURE(status, 400, 16) +#define SD_SW_STATUS_FUN_GRP6_RC(status) \ + SD_SW_STATUS_STRUCTURE(status, 396, 4) +#define SD_SW_STATUS_FUN_GRP5_RC(status) \ + SD_SW_STATUS_STRUCTURE(status, 392, 4) +#define SD_SW_STATUS_FUN_GRP4_RC(status) \ + SD_SW_STATUS_STRUCTURE(status, 388, 4) +#define SD_SW_STATUS_FUN_GRP3_RC(status) \ + SD_SW_STATUS_STRUCTURE(status, 384, 4) +#define SD_SW_STATUS_FUN_GRP2_RC(status) \ + SD_SW_STATUS_STRUCTURE(status, 380, 4) +#define SD_SW_STATUS_FUN_GRP1_RC(status) \ + SD_SW_STATUS_STRUCTURE(status, 376, 4) +#define SD_SW_STATUS_FUN_GRP_RC_ERROR 0xFU +#define SD_SW_STATUS_DATA_STRUCT_VER(status) \ + SD_SW_STATUS_STRUCTURE(status, 368, 8) +#define SD_SW_STATUS_FUN_GRP6_BUSY(status) \ + SD_SW_STATUS_STRUCTURE(status, 352, 16) +#define SD_SW_STATUS_FUN_GRP5_BUSY(status) \ + SD_SW_STATUS_STRUCTURE(status, 336, 16) +#define SD_SW_STATUS_FUN_GRP4_BUSY(status) \ + SD_SW_STATUS_STRUCTURE(status, 320, 16) +#define SD_SW_STATUS_FUN_GRP3_BUSY(status) \ + SD_SW_STATUS_STRUCTURE(status, 304, 16) +#define SD_SW_STATUS_FUN_GRP2_BUSY(status) \ + SD_SW_STATUS_STRUCTURE(status, 288, 16) +#define SD_SW_STATUS_FUN_GRP1_BUSY(status) \ + SD_SW_STATUS_STRUCTURE(status, 272, 16) + //! @} + + //! \name Card Status Fields + //! @{ +#define CARD_STATUS_APP_CMD (1lu << 5) +#define CARD_STATUS_SWITCH_ERROR (1lu << 7) +#define CARD_STATUS_READY_FOR_DATA (1lu << 8) +#define CARD_STATUS_STATE_IDLE (0lu << 9) +#define CARD_STATUS_STATE_READY (1lu << 9) +#define CARD_STATUS_STATE_IDENT (2lu << 9) +#define CARD_STATUS_STATE_STBY (3lu << 9) +#define CARD_STATUS_STATE_TRAN (4lu << 9) +#define CARD_STATUS_STATE_DATA (5lu << 9) +#define CARD_STATUS_STATE_RCV (6lu << 9) +#define CARD_STATUS_STATE_PRG (7lu << 9) +#define CARD_STATUS_STATE_DIS (8lu << 9) +#define CARD_STATUS_STATE (0xFlu << 9) +#define CARD_STATUS_ERASE_RESET (1lu << 13) +#define CARD_STATUS_WP_ERASE_SKIP (1lu << 15) +#define CARD_STATUS_CIDCSD_OVERWRITE (1lu << 16) +#define CARD_STATUS_OVERRUN (1lu << 17) +#define CARD_STATUS_UNERRUN (1lu << 18) +#define CARD_STATUS_ERROR (1lu << 19) +#define CARD_STATUS_CC_ERROR (1lu << 20) +#define CARD_STATUS_CARD_ECC_FAILED (1lu << 21) +#define CARD_STATUS_ILLEGAL_COMMAND (1lu << 22) +#define CARD_STATUS_COM_CRC_ERROR (1lu << 23) +#define CARD_STATUS_UNLOCK_FAILED (1lu << 24) +#define CARD_STATUS_CARD_IS_LOCKED (1lu << 25) +#define CARD_STATUS_WP_VIOLATION (1lu << 26) +#define CARD_STATUS_ERASE_PARAM (1lu << 27) +#define CARD_STATUS_ERASE_SEQ_ERROR (1lu << 28) +#define CARD_STATUS_BLOCK_LEN_ERROR (1lu << 29) +#define CARD_STATUS_ADDRESS_MISALIGN (1lu << 30) +#define CARD_STATUS_ADDR_OUT_OF_RANGE (1lu << 31) + +#define CARD_STATUS_ERR_RD_WR (CARD_STATUS_ADDR_OUT_OF_RANGE \ + | CARD_STATUS_ADDRESS_MISALIGN \ + | CARD_STATUS_BLOCK_LEN_ERROR \ + | CARD_STATUS_WP_VIOLATION \ + | CARD_STATUS_ILLEGAL_COMMAND \ + | CARD_STATUS_CC_ERROR \ + | CARD_STATUS_ERROR) + //! @} + + //! \name SD Status Field + //! @{ +#define SD_STATUS_BSIZE (512 / 8) /**< 512 bits, 64bytes */ + //! @} + + //! \name MMC Extended CSD Register Field + //! @{ +#define EXT_CSD_BSIZE 512 /**< 512 bytes. */ +/* Below belongs to Properties Segment */ +#define EXT_CSD_S_CMD_SET_INDEX 504lu +#define EXT_CSD_BOOT_INFO_INDEX 228lu +#define EXT_CSD_BOOT_SIZE_MULTI_INDEX 226lu +#define EXT_CSD_ACC_SIZE_INDEX 225lu +#define EXT_CSD_HC_ERASE_GRP_SIZE_INDEX 224lu +#define EXT_CSD_ERASE_TIMEOUT_MULT_INDEX 223lu +#define EXT_CSD_REL_WR_SEC_C_INDEX 222lu +#define EXT_CSD_HC_WP_GRP_SIZE_INDEX 221lu +#define EXT_CSD_S_C_VCC_INDEX 220lu +#define EXT_CSD_S_C_VCCQ_INDEX 219lu +#define EXT_CSD_S_A_TIMEOUT_INDEX 217lu +#define EXT_CSD_SEC_COUNT_INDEX 212lu +#define EXT_CSD_MIN_PERF_W_8_52_INDEX 210lu +#define EXT_CSD_MIN_PERF_R_8_52_INDEX 209lu +#define EXT_CSD_MIN_PERF_W_8_26_4_52_INDEX 208lu +#define EXT_CSD_MIN_PERF_R_8_26_4_52_INDEX 207lu +#define EXT_CSD_MIN_PERF_W_4_26_INDEX 206lu +#define EXT_CSD_MIN_PERF_R_4_26_INDEX 205lu +#define EXT_CSD_PWR_CL_26_360_INDEX 203lu +#define EXT_CSD_PWR_CL_52_360_INDEX 202lu +#define EXT_CSD_PWR_CL_26_195_INDEX 201lu +#define EXT_CSD_PWR_CL_52_195_INDEX 200lu +#define EXT_CSD_CARD_TYPE_INDEX 196lu +/* MMC card type */ +# define MMC_CTYPE_26MHZ 0x1 +# define MMC_CTYPE_52MHZ 0x2 +#define EXT_CSD_CSD_STRUCTURE_INDEX 194lu +#define EXT_CSD_EXT_CSD_REV_INDEX 192lu + +/* Below belongs to Mode Segment */ +#define EXT_CSD_CMD_SET_INDEX 191lu +#define EXT_CSD_CMD_SET_REV_INDEX 189lu +#define EXT_CSD_POWER_CLASS_INDEX 187lu +#define EXT_CSD_HS_TIMING_INDEX 185lu +#define EXT_CSD_BUS_WIDTH_INDEX 183lu +#define EXT_CSD_ERASED_MEM_CONT_INDEX 181lu +#define EXT_CSD_BOOT_CONFIG_INDEX 179lu +#define EXT_CSD_BOOT_BUS_WIDTH_INDEX 177lu +#define EXT_CSD_ERASE_GROUP_DEF_INDEX 175lu + //! @} +//! @} + + +//! \name Definition for SPI mode only +//! @{ + +//! SPI commands start with a start bit "0" and a transmit bit "1" +#define SPI_CMD_ENCODE(x) (0x40 | (x & 0x3F)) + +//! \name Register R1 definition for SPI mode +//! The R1 register is always send after a command. +//! @{ +#define R1_SPI_IDLE (1lu << 0) +#define R1_SPI_ERASE_RESET (1lu << 1) +#define R1_SPI_ILLEGAL_COMMAND (1lu << 2) +#define R1_SPI_COM_CRC (1lu << 3) +#define R1_SPI_ERASE_SEQ (1lu << 4) +#define R1_SPI_ADDRESS (1lu << 5) +#define R1_SPI_PARAMETER (1lu << 6) +// R1 bit 7 is always zero, reuse this bit for error +#define R1_SPI_ERROR (1lu << 7) +//! @} + +//! \name Register R2 definition for SPI mode +//! The R2 register can be send after R1 register. +//! @{ +#define R2_SPI_CARD_LOCKED (1lu << 0) +#define R2_SPI_WP_ERASE_SKIP (1lu << 1) +#define R2_SPI_LOCK_UNLOCK_FAIL R2_SPI_WP_ERASE_SKIP +#define R2_SPI_ERROR (1lu << 2) +#define R2_SPI_CC_ERROR (1lu << 3) +#define R2_SPI_CARD_ECC_ERROR (1lu << 4) +#define R2_SPI_WP_VIOLATION (1lu << 5) +#define R2_SPI_ERASE_PARAM (1lu << 6) +#define R2_SPI_OUT_OF_RANGE (1lu << 7) +#define R2_SPI_CSD_OVERWRITE R2_SPI_OUT_OF_RANGE +//! @} + +//! \name Control Tokens in SPI Mode +//! @{ + //! \name Tokens used for a read operation + //! @{ +#define SPI_TOKEN_SINGLE_MULTI_READ 0xFE +#define SPI_TOKEN_DATA_ERROR_VALID(token) (((token) & 0xF0) == 0) +#define SPI_TOKEN_DATA_ERROR_ERRORS (0x0F) +#define SPI_TOKEN_DATA_ERROR_ERROR (1lu << 0) +#define SPI_TOKEN_DATA_ERROR_CC_ERROR (1lu << 1) +#define SPI_TOKEN_DATA_ERROR_ECC_ERROR (1lu << 2) +#define SPI_TOKEN_DATA_ERROR_OUT_RANGE (1lu << 3) + //! @} + //! \name Tokens used for a write operation + //! @{ +#define SPI_TOKEN_SINGLE_WRITE 0xFE +#define SPI_TOKEN_MULTI_WRITE 0xFC +#define SPI_TOKEN_STOP_TRAN 0xFD +#define SPI_TOKEN_DATA_RESP_VALID(token) \ + ((((token) & (1 << 4)) == 0) && (((token) & (1 << 0)) == 1)) +#define SPI_TOKEN_DATA_RESP_CODE(token) ((token) & 0x1E) +#define SPI_TOKEN_DATA_RESP_ACCEPTED (2lu << 1) +#define SPI_TOKEN_DATA_RESP_CRC_ERR (5lu << 1) +#define SPI_TOKEN_DATA_RESP_WRITE_ERR (6lu << 1) + //! @} +//! @} +//! @} + + +//! @} end of sd_mmc_protocol + +#ifdef __cplusplus +} +#endif + +#endif /* SD_MMC_PROTOCOL_H_INCLUDED */ diff --git a/src/Libraries/sd_mmc/sd_mmc_spi.c b/src/Libraries/sd_mmc/sd_mmc_spi.c new file mode 100644 index 00000000..adcaf662 --- /dev/null +++ b/src/Libraries/sd_mmc/sd_mmc_spi.c @@ -0,0 +1,629 @@ +/** + * \file + * + * \brief Common SPI interface for 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 <compiler.h> +#include <status_codes.h> +#include <board.h> +#include <string.h> +#include "Core.h" +#include "conf_board.h" +#include "conf_sd_mmc.h" +#include "sd_mmc_protocol.h" +#include "sd_mmc_spi.h" +#include "sd_mmc.h" + +#if SD_MMC_SPI_MEM_CNT != 0 + +/** + * \ingroup sd_mmc_stack_spi + * \defgroup sd_mmc_stack_spi_internal Common SPI interface for SD/MMC stack + * implementation + * + * @{ + */ + +#include <SharedSpi.h> + +// Enable debug information for SD/MMC SPI module +#ifdef SD_MMC_SPI_DEBUG +extern void debugPrintf(const char* fmt, ...); +#define sd_mmc_spi_debug(...) debugPrintf(__VA_ARGS__) +#else +#define sd_mmc_spi_debug(...) +#endif + +//! Internal global error status +static sd_mmc_spi_errno_t sd_mmc_spi_err; + +//! Slot array of SPI structures +static struct sspi_device sd_mmc_spi_devices[SD_MMC_SPI_MEM_CNT]; + +//! 32 bits response of the last command +static uint32_t sd_mmc_spi_response_32; +//! Current position (byte) of the transfer started by mci_adtc_start() +static uint32_t sd_mmc_spi_transfert_pos; +//! Size block requested by last mci_adtc_start() +static uint16_t sd_mmc_spi_block_size; +//! Total number of block requested by last mci_adtc_start() +static uint16_t sd_mmc_spi_nb_block; + +static uint8_t sd_mmc_spi_crc7(uint8_t * buf, uint8_t size); +static bool sd_mmc_spi_wait_busy(void); +static bool sd_mmc_spi_start_read_block(void); +static void sd_mmc_spi_stop_read_block(void); +static void sd_mmc_spi_start_write_block(void); +static bool sd_mmc_spi_stop_write_block(void); +static bool sd_mmc_spi_stop_multiwrite_block(void); + +/** + * \brief Calculates the CRC7 + * + * \param buf Buffer data to compute + * \param size Size of buffer data + * + * \return CRC7 computed + */ +static uint8_t sd_mmc_spi_crc7(uint8_t * buf, uint8_t size) +{ + uint8_t crc, value, i; + + crc = 0; + while (size--) { + value = *buf++; + for (i = 0; i < 8; i++) { + crc <<= 1; + if ((value & 0x80) ^ (crc & 0x80)) { + crc ^= 0x09; + } + value <<= 1; + } + } + crc = (crc << 1) | 1; + return crc; +} + +/** + * \brief Wait the end of busy on DAT0 line + * + * \return true if success, otherwise false + */ +static bool sd_mmc_spi_wait_busy(void) +{ + uint8_t line = 0xFF; + + /* Delay before check busy + * Nbr timing minimum = 8 cylces + */ + sspi_read_packet(&line, 1); + + /* Wait end of busy signal + * Nec timing: 0 to unlimited + * However a timeout is used. + * 200 000 * 8 cycles + */ + uint32_t nec_timeout = 200000; + sspi_read_packet(&line, 1); + do { + sspi_read_packet(&line, 1); + if (!(nec_timeout--)) { + return false; + } + } while (line != 0xFF); + return true; +} + +/** + * \brief Sends the correct TOKEN on the line to start a read block transfer + * + * \return true if success, otherwise false + * with a update of \ref sd_mmc_spi_err. + */ +static bool sd_mmc_spi_start_read_block(void) +{ + uint32_t i; + uint8_t token; + + Assert(!(sd_mmc_spi_transfert_pos % sd_mmc_spi_block_size)); + + /* Wait for start data token: + * The read timeout is the Nac timing. + * Nac must be computed trough CSD values, + * or it is 100ms for SDHC / SDXC + * Compute the maximum timeout: + * Frequency maximum = 25MHz + * 1 byte = 8 cycles + * 100ms = 312500 x sd_mmc_spi_drv_read_packet() maximum + */ + token = 0; + i = 500000; + do { + if (i-- == 0) { + sd_mmc_spi_err = SD_MMC_SPI_ERR_READ_TIMEOUT; + sd_mmc_spi_debug("%s: Read blocks timeout\n\r", __func__); + return false; + } + sspi_read_packet(&token, 1); + if (SPI_TOKEN_DATA_ERROR_VALID(token)) { + Assert(SPI_TOKEN_DATA_ERROR_ERRORS & token); + if (token & (SPI_TOKEN_DATA_ERROR_ERROR + | SPI_TOKEN_DATA_ERROR_ECC_ERROR + | SPI_TOKEN_DATA_ERROR_CC_ERROR)) { + sd_mmc_spi_debug("%s: CRC data error token\n\r", __func__); + sd_mmc_spi_err = SD_MMC_SPI_ERR_READ_CRC; + } else { + sd_mmc_spi_debug("%s: Out of range data error token\n\r", __func__); + sd_mmc_spi_err = SD_MMC_SPI_ERR_OUT_OF_RANGE; + } + return false; + } + } while (token != SPI_TOKEN_SINGLE_MULTI_READ); + + return true; +} + +/** + * \brief Executed the end of a read block transfer + */ +static void sd_mmc_spi_stop_read_block(void) +{ + uint8_t crc[2]; + // Read 16-bit CRC (not cheked) + sspi_read_packet(crc, 2); +} + +/** + * \brief Sends the correct TOKEN on the line to start a write block transfer + */ +static void sd_mmc_spi_start_write_block(void) +{ + uint8_t dummy = 0xFF; + Assert(!(sd_mmc_spi_transfert_pos % sd_mmc_spi_block_size)); + // Delay before start write block: + // Nwr timing minimum = 8 cycles + sspi_write_packet(&dummy, 1); + // Send start token + uint8_t token; + if (1 == sd_mmc_spi_nb_block) { + token = SPI_TOKEN_SINGLE_WRITE; + } else { + token = SPI_TOKEN_MULTI_WRITE; + } + sspi_write_packet(&token, 1); +} + +/** + * \brief Waits the TOKEN which notify the end of write block transfer + * + * \return true if success, otherwise false + * with a update of \ref sd_mmc_spi_err. + */ +static bool sd_mmc_spi_stop_write_block(void) +{ + uint8_t resp; + uint16_t crc; + + // Send CRC + crc = 0xFFFF; /// CRC is disabled in SPI mode + sspi_write_packet((uint8_t *)&crc, 2); + // Receive data response token + sspi_read_packet(&resp, 1); + if (!SPI_TOKEN_DATA_RESP_VALID(resp)) { + sd_mmc_spi_err = SD_MMC_SPI_ERR; + sd_mmc_spi_debug("%s: Invalid Data Response Token 0x%x\n\r", __func__, resp); + return false; + } + // Check data response + switch (SPI_TOKEN_DATA_RESP_CODE(resp)) { + case SPI_TOKEN_DATA_RESP_ACCEPTED: + break; + case SPI_TOKEN_DATA_RESP_CRC_ERR: + sd_mmc_spi_err = SD_MMC_SPI_ERR_WRITE_CRC; + sd_mmc_spi_debug("%s: Write blocks, SD_MMC_SPI_ERR_CRC, resp 0x%x\n\r", __func__, resp); + return false; + case SPI_TOKEN_DATA_RESP_WRITE_ERR: + default: + sd_mmc_spi_err = SD_MMC_SPI_ERR_WRITE; + sd_mmc_spi_debug("%s: Write blocks SD_MMC_SPI_ERR_WR, resp 0x%x\n\r", __func__, resp); + return false; + } + return true; +} + +/** + * \brief Executed the end of a multi blocks write transfer + * + * \return true if success, otherwise false + * with a update of \ref sd_mmc_spi_err. + */ +static bool sd_mmc_spi_stop_multiwrite_block(void) +{ + uint8_t value; + + if (1 == sd_mmc_spi_nb_block) { + return true; // Single block write + } + if (sd_mmc_spi_nb_block > (sd_mmc_spi_transfert_pos / sd_mmc_spi_block_size)) { + return true; // It is not the End of multi write + } + + // Delay before start write block: + // Nwr timing minimum = 8 cylces + value = 0xFF; + sspi_write_packet(&value, 1); + // Send stop token + value = SPI_TOKEN_STOP_TRAN; + sspi_write_packet(&value, 1); + // Wait busy + if (!sd_mmc_spi_wait_busy()) { + sd_mmc_spi_err = SD_MMC_SPI_ERR_WRITE_TIMEOUT; + sd_mmc_spi_debug("%s: Stop write blocks timeout\n\r", __func__); + return false; + } + return true; +} + + +//------------------------------------------------------------------- +//--------------------- PUBLIC FUNCTIONS ---------------------------- + +#if 1 //dc42 + +// Get the speed of the SPI SD card interface for reporting purposes, in bytes/sec +uint32_t spi_mmc_get_speed(void) +{ + return SD_MMC_SPI_MAX_CLOCK/8; +} + +static spiIdleFunc_t spiIdleFunc = NULL; + +// Set the idle function and return the old one +spiIdleFunc_t sd_mmc_spi_set_idle_func(spiIdleFunc_t p) +{ + spiIdleFunc_t ret = spiIdleFunc; + spiIdleFunc = p; + return ret; +} + +#endif + +sd_mmc_spi_errno_t sd_mmc_spi_get_errno(void) +{ + return sd_mmc_spi_err; +} + +void sd_mmc_spi_init(const Pin csPins[SD_MMC_SPI_MEM_CNT]) +{ + sd_mmc_spi_err = SD_MMC_SPI_NO_ERR; + + // Initialize SPI interface and enable it + for (size_t i = 0; i < SD_MMC_SPI_MEM_CNT; ++i) + { + sd_mmc_spi_devices[i].csPin = csPins[i]; + sd_mmc_spi_devices[i].csPolarity = false; + sspi_master_init(&sd_mmc_spi_devices[i], 8); + } +} + +void sd_mmc_spi_select_device(uint8_t slot, uint32_t clock, uint8_t bus_width, bool high_speed) +{ + UNUSED(bus_width); + UNUSED(high_speed); + sd_mmc_spi_err = SD_MMC_SPI_NO_ERR; + +#ifdef SD_MMC_SPI_MAX_CLOCK + if (clock > SD_MMC_SPI_MAX_CLOCK) { + clock = SD_MMC_SPI_MAX_CLOCK; + } +#endif + + struct sspi_device *dev = &sd_mmc_spi_devices[slot]; + dev->spiMode = SPI_MODE_0; + dev->clockFrequency = clock; + sspi_master_setup_device(dev); + sspi_select_device(dev); + delayMicroseconds(1); +} + +void sd_mmc_spi_deselect_device(uint8_t slot) +{ + sd_mmc_spi_err = SD_MMC_SPI_NO_ERR; + sspi_deselect_device(&sd_mmc_spi_devices[slot]); + delayMicroseconds(1); +} + +void sd_mmc_spi_send_clock(void) +{ + uint8_t i; + uint8_t dummy = 0xFF; + + sd_mmc_spi_err = SD_MMC_SPI_NO_ERR; + //! Send 80 cycles + for (i = 0; i < 10; i++) { + sspi_write_packet(&dummy, 1); // 8 cycles + } +} + +bool sd_mmc_spi_send_cmd(sdmmc_cmd_def_t cmd, uint32_t arg) +{ + return sd_mmc_spi_adtc_start(cmd, arg, 0, 0, false); +} + +bool sd_mmc_spi_adtc_start(sdmmc_cmd_def_t cmd, uint32_t arg, + uint16_t block_size, uint16_t nb_block, bool access_block) +{ + uint8_t dummy = 0xFF; + uint8_t cmd_token[6]; + uint8_t ncr_timeout; + uint8_t r1; //! R1 response + + UNUSED(access_block); + Assert(cmd & SDMMC_RESP_PRESENT); // Always a response in SPI mode + sd_mmc_spi_err = SD_MMC_SPI_NO_ERR; + + // Encode SPI command + cmd_token[0] = SPI_CMD_ENCODE(SDMMC_CMD_GET_INDEX(cmd)); + cmd_token[1] = arg >> 24; + cmd_token[2] = arg >> 16; + cmd_token[3] = arg >> 8; + cmd_token[4] = arg; + cmd_token[5] = sd_mmc_spi_crc7(cmd_token, 5); + + // 8 cycles to respect Ncs timing + // Note: This byte does not include start bit "0", + // thus it is ignored by card. + sspi_write_packet(&dummy, 1); + // Send command + sspi_write_packet(cmd_token, sizeof(cmd_token)); + + // Wait for response + // Two retry will be done to manage the Ncr timing between command and reponse + // Ncr: Min. 1x8 clock cycle, Max. 8x8 clock cycles + // WORKAROUND for no compliance card (Atmel Internal ref. SD13): + r1 = 0xFF; + // Ignore first byte because Ncr min. = 8 clock cylces + sspi_read_packet(&r1, 1); + ncr_timeout = 7; + while (1) { + sspi_read_packet(&r1, 1); // 8 cycles + if ((r1 & R1_SPI_ERROR) == 0) { + // Valid R1 response + break; + } + if (--ncr_timeout == 0) { + // Here Valid R1 response received + sd_mmc_spi_debug("%s: cmd %02d, arg 0x%08lX, R1 timeout\n\r", + __func__, (int)SDMMC_CMD_GET_INDEX(cmd), arg); + sd_mmc_spi_err = SD_MMC_SPI_ERR_RESP_TIMEOUT; + return false; + } + } + + // Save R1 (Specific to SPI interface) in 32 bit response + // The R1_SPI_IDLE bit can be checked by high level + sd_mmc_spi_response_32 = r1; + + // Manage error in R1 + if (r1 & R1_SPI_COM_CRC) { + sd_mmc_spi_debug("%s: cmd %02d, arg 0x%08lx, r1 0x%02x, R1_SPI_COM_CRC\n\r", + __func__, (int)SDMMC_CMD_GET_INDEX(cmd), arg, r1); + sd_mmc_spi_err = SD_MMC_SPI_ERR_RESP_CRC; + return false; + } + if (r1 & R1_SPI_ILLEGAL_COMMAND) { + sd_mmc_spi_debug("%s: cmd %02d, arg 0x%08lx, r1 0x%x, R1 ILLEGAL_COMMAND\n\r", + __func__, (int)SDMMC_CMD_GET_INDEX(cmd), arg, r1); + sd_mmc_spi_err = SD_MMC_SPI_ERR_ILLEGAL_COMMAND; + return false; + } + if (r1 & ~R1_SPI_IDLE) { + // Other error + sd_mmc_spi_debug("%s: cmd %02d, arg 0x%08lx, r1 0x%x, R1 error\n\r", + __func__, (int)SDMMC_CMD_GET_INDEX(cmd), arg, r1); + sd_mmc_spi_err = SD_MMC_SPI_ERR; + return false; + } + + // Manage other responses + if (cmd & SDMMC_RESP_BUSY) { + if (!sd_mmc_spi_wait_busy()) { + sd_mmc_spi_err = SD_MMC_SPI_ERR_RESP_BUSY_TIMEOUT; + sd_mmc_spi_debug("%s: cmd %02d, arg 0x%08lx, Busy signal always high\n\r", + __func__, (int)SDMMC_CMD_GET_INDEX(cmd), arg); + return false; + } + } + if (cmd & SDMMC_RESP_8) { + sd_mmc_spi_response_32 = 0; + sspi_read_packet((uint8_t*) & sd_mmc_spi_response_32, 1); + sd_mmc_spi_response_32 = le32_to_cpu(sd_mmc_spi_response_32); + } + if (cmd & SDMMC_RESP_32) { + sspi_read_packet((uint8_t*) & sd_mmc_spi_response_32, 4); + sd_mmc_spi_response_32 = be32_to_cpu(sd_mmc_spi_response_32); + } + + sd_mmc_spi_block_size = block_size; + sd_mmc_spi_nb_block = nb_block; + sd_mmc_spi_transfert_pos = 0; + return true; // Command complete +} + +uint32_t sd_mmc_spi_get_response(void) +{ + return sd_mmc_spi_response_32; +} + +bool sd_mmc_spi_read_word(uint32_t* value) +{ + sd_mmc_spi_err = SD_MMC_SPI_NO_ERR; + Assert(sd_mmc_spi_nb_block > + (sd_mmc_spi_transfert_pos / sd_mmc_spi_block_size)); + + if (!(sd_mmc_spi_transfert_pos % sd_mmc_spi_block_size)) { + // New block + if (!sd_mmc_spi_start_read_block()) { + return false; + } + } + // Read data + sspi_read_packet((uint8_t*)value, 4); + *value = le32_to_cpu(*value); + sd_mmc_spi_transfert_pos += 4; + + if (!(sd_mmc_spi_transfert_pos % sd_mmc_spi_block_size)) { + // End of block + sd_mmc_spi_stop_read_block(); + } + return true; +} + +bool sd_mmc_spi_write_word(uint32_t value) +{ + sd_mmc_spi_err = SD_MMC_SPI_NO_ERR; + Assert(sd_mmc_spi_nb_block > + (sd_mmc_spi_transfert_pos / sd_mmc_spi_block_size)); + + if (!(sd_mmc_spi_transfert_pos % sd_mmc_spi_block_size)) { + // New block + sd_mmc_spi_start_write_block(); + } + + // Write data + value = cpu_to_le32(value); + sspi_write_packet((uint8_t*)&value, 4); + sd_mmc_spi_transfert_pos += 4; + + if (!(sd_mmc_spi_transfert_pos % sd_mmc_spi_block_size)) { + // End of block + if (!sd_mmc_spi_stop_write_block()) { + return false; + } + // Wait busy due to data programmation + if (!sd_mmc_spi_wait_busy()) { + sd_mmc_spi_err = SD_MMC_SPI_ERR_WRITE_TIMEOUT; + sd_mmc_spi_debug("%s: Write blocks timeout\n\r", __func__); + return false; + } + } + return sd_mmc_spi_stop_multiwrite_block(); +} + +bool sd_mmc_spi_start_read_blocks(void *dest, uint16_t nb_block) +{ + uint32_t pos; + + sd_mmc_spi_err = SD_MMC_SPI_NO_ERR; + pos = 0; + while (nb_block--) { + Assert(sd_mmc_spi_nb_block > + (sd_mmc_spi_transfert_pos / sd_mmc_spi_block_size)); + if (!sd_mmc_spi_start_read_block()) { + return false; + } + + // Read block + sspi_read_packet(&((uint8_t*)dest)[pos], sd_mmc_spi_block_size); + pos += sd_mmc_spi_block_size; + sd_mmc_spi_transfert_pos += sd_mmc_spi_block_size; + + sd_mmc_spi_stop_read_block(); + } + return true; +} + +bool sd_mmc_spi_wait_end_of_read_blocks(void) +{ + return true; +} + +bool sd_mmc_spi_start_write_blocks(const void *src, uint16_t nb_block) +{ + uint32_t pos; + + sd_mmc_spi_err = SD_MMC_SPI_NO_ERR; + pos = 0; + while (nb_block--) { + Assert(sd_mmc_spi_nb_block > + (sd_mmc_spi_transfert_pos / sd_mmc_spi_block_size)); + sd_mmc_spi_start_write_block(); + + // Write block + sspi_write_packet(&((uint8_t*)src)[pos], sd_mmc_spi_block_size); + pos += sd_mmc_spi_block_size; + sd_mmc_spi_transfert_pos += sd_mmc_spi_block_size; + + if (!sd_mmc_spi_stop_write_block()) { + return false; + } + // Do not check busy of last block + // but delay it to mci_wait_end_of_write_blocks() + if (nb_block) { + // Wait busy due to data programmation + if (!sd_mmc_spi_wait_busy()) { + sd_mmc_spi_err = SD_MMC_SPI_ERR_WRITE_TIMEOUT; + sd_mmc_spi_debug("%s: Write blocks timeout\n\r", __func__); + return false; + } + } + } + return true; +} + +bool sd_mmc_spi_wait_end_of_write_blocks(void) +{ + // Wait busy due to data programmation of last block writed + if (!sd_mmc_spi_wait_busy()) { + sd_mmc_spi_err = SD_MMC_SPI_ERR_WRITE_TIMEOUT; + sd_mmc_spi_debug("%s: Write blocks timeout\n\r", __func__); + return false; + } + return sd_mmc_spi_stop_multiwrite_block(); +} + +//! @} + +#endif + +// End diff --git a/src/Libraries/sd_mmc/sd_mmc_spi.h b/src/Libraries/sd_mmc/sd_mmc_spi.h new file mode 100644 index 00000000..c9c429a1 --- /dev/null +++ b/src/Libraries/sd_mmc/sd_mmc_spi.h @@ -0,0 +1,249 @@ +/** + * \file + * + * \brief Common SPI interface for 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> + */ + +#ifndef SD_MMC_SPI_H_INCLUDED +#define SD_MMC_SPI_H_INCLUDED + +#include "compiler.h" +#include "sd_mmc_protocol.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup sd_mmc_stack_group + * \defgroup sd_mmc_stack_spi Common SPI interface for SD/MMC stack + * @{ + */ + +//! Type of return error code +typedef uint8_t sd_mmc_spi_errno_t; + +//! \name Return error codes +//! @{ +#define SD_MMC_SPI_NO_ERR 0 //! No error +#define SD_MMC_SPI_ERR 1 //! General or an unknown error +#define SD_MMC_SPI_ERR_RESP_TIMEOUT 2 //! Timeout during command +#define SD_MMC_SPI_ERR_RESP_BUSY_TIMEOUT 3 //! Timeout on busy signal of R1B response +#define SD_MMC_SPI_ERR_READ_TIMEOUT 4 //! Timeout during read operation +#define SD_MMC_SPI_ERR_WRITE_TIMEOUT 5 //! Timeout during write operation +#define SD_MMC_SPI_ERR_RESP_CRC 6 //! Command CRC error +#define SD_MMC_SPI_ERR_READ_CRC 7 //! CRC error during read operation +#define SD_MMC_SPI_ERR_WRITE_CRC 8 //! CRC error during write operation +#define SD_MMC_SPI_ERR_ILLEGAL_COMMAND 9 //! Command not supported +#define SD_MMC_SPI_ERR_WRITE 10 //! Error during write operation +#define SD_MMC_SPI_ERR_OUT_OF_RANGE 11 //! Data access out of range +//! @} + + +/** \brief Return the error code of last function + * + * \return error code + */ +sd_mmc_spi_errno_t sd_mmc_spi_get_errno(void) noexcept; + +/** \brief Initializes the low level driver + * + * This enable the clock required and the hardware interface. + */ +void sd_mmc_spi_init(const Pin csPins[SD_MMC_SPI_MEM_CNT]) noexcept; + +/** \brief Return the maximum bus width of a slot + * + * \param slot Selected slot + * + * \return 1, 4 or 8 lines. + */ +static __inline__ uint8_t sd_mmc_spi_get_bus_width(uint8_t slot) noexcept +{ + UNUSED(slot); + return 1; +} + +/** \brief Return the high speed capability of the driver + * + * \return true, if the high speed is supported + */ +static __inline__ bool sd_mmc_spi_is_high_speed_capable(void) noexcept +{ + return false; +} + +/** + * \brief Select a slot and initialize it + * + * \param slot Selected slot + * \param clock Maximum clock to use (Hz) + * \param bus_width Bus width to use (1, 4 or 8) + * \param high_speed true, to enable high speed mode + */ +void sd_mmc_spi_select_device(uint8_t slot, uint32_t clock, uint8_t bus_width, bool high_speed) noexcept; + +/** + * \brief Deselect a slot + * + * \param slot Selected slot + */ +void sd_mmc_spi_deselect_device(uint8_t slot) noexcept; + +/** \brief Send 74 clock cycles on the line of selected slot + * Note: It is required after card plug and before card install. + */ +void sd_mmc_spi_send_clock(void) noexcept; + +/** \brief Send a command on the selected slot + * + * \param cmd Command definition + * \param arg Argument of the command + * + * \return true if success, otherwise false + */ +bool sd_mmc_spi_send_cmd(sdmmc_cmd_def_t cmd, uint32_t arg) noexcept; + +/** \brief Return the 32 bits response of the last command + * + * \return 32 bits response + */ +uint32_t sd_mmc_spi_get_response(void) noexcept; +static __inline__ void sd_mmc_spi_get_response_128(uint8_t *resp) noexcept +{ + UNUSED(resp); + return; +} + + +/** \brief Send a adtc command on the selected slot + * A adtc command is used for read/write access. + * + * \param cmd Command definition + * \param arg Argument of the command + * \param block_size Block size used for the transfer + * \param nb_block Total number of block for this transfer + * \param access_block if true, the x_read_blocks() and x_write_blocks() + * functions must be used after this function. + * If false, the mci_read_word() and mci_write_word() + * functions must be used after this function. + * + * \return true if success, otherwise false + */ +bool sd_mmc_spi_adtc_start(sdmmc_cmd_def_t cmd, uint32_t arg, uint16_t block_size, uint16_t nb_block, bool access_block) noexcept; + +/** \brief Send a command to stop a adtc command on the selected slot + * + * \param cmd Command definition + * \param arg Argument of the command + * + * \return true if success, otherwise false + */ +bool sd_mmc_spi_adtc_stop(sdmmc_cmd_def_t cmd, uint32_t arg) noexcept; + +/** \brief Read a word on the line + * + * \param value Pointer on a word to fill + * + * \return true if success, otherwise false + */ +bool sd_mmc_spi_read_word(uint32_t* value) noexcept; + +/** \brief Write a word on the line + * + * \param value Word to send + * + * \return true if success, otherwise false + */ +bool sd_mmc_spi_write_word(uint32_t value) noexcept; + +/** \brief Start a read blocks transfer on the line + * Note: The driver will use the DMA available to speed up the transfer. + * + * \param dest Pointer on the buffer to fill + * \param nb_block Number of block to transfer + * + * \return true if started, otherwise false + */ +bool sd_mmc_spi_start_read_blocks(void *dest, uint16_t nb_block) noexcept; + +/** \brief Wait the end of transfer initiated by mci_start_read_blocks() + * + * \return true if success, otherwise false + */ +bool sd_mmc_spi_wait_end_of_read_blocks(void) noexcept; + +/** \brief Start a write blocks transfer on the line + * Note: The driver will use the DMA available to speed up the transfer. + * + * \param src Pointer on the buffer to send + * \param nb_block Number of block to transfer + * + * \return true if started, otherwise false + */ +bool sd_mmc_spi_start_write_blocks(const void *src, uint16_t nb_block) noexcept; + +/** \brief Wait the end of transfer initiated by mci_start_write_blocks() + * + * \return true if success, otherwise false + */ +bool sd_mmc_spi_wait_end_of_write_blocks(void) noexcept; + +#if 1 //dc42 + +// Get the speed of the SPI SD card interface for reporting purposes, in bytes/sec +uint32_t spi_mmc_get_speed(void) noexcept; + +typedef void (*spiIdleFunc_t)(uint32_t, uint32_t) noexcept; + +// Set the idle function and return the old one +spiIdleFunc_t sd_mmc_spi_set_idle_func(spiIdleFunc_t) noexcept; + +#endif +//! @} + +#ifdef __cplusplus +} +#endif + +#endif /* SD_MMC_SPI_H_INCLUDED */ |