mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-16 23:05:38 +00:00
sdmmc: add peripheral driver and protocol layer
This commit is contained in:

committed by
Ivan Grokhotkov

parent
0efaa4f4b8
commit
edd924f273
303
components/driver/include/driver/sdmmc_defs.h
Normal file
303
components/driver/include/driver/sdmmc_defs.h
Normal file
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
* Adaptations to ESP-IDF Copyright (c) 2016 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _SDMMC_DEFS_H_
|
||||
#define _SDMMC_DEFS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
|
||||
/* MMC commands */ /* response type */
|
||||
#define MMC_GO_IDLE_STATE 0 /* R0 */
|
||||
#define MMC_SEND_OP_COND 1 /* R3 */
|
||||
#define MMC_ALL_SEND_CID 2 /* R2 */
|
||||
#define MMC_SET_RELATIVE_ADDR 3 /* R1 */
|
||||
#define MMC_SWITCH 6 /* R1B */
|
||||
#define MMC_SELECT_CARD 7 /* R1 */
|
||||
#define MMC_SEND_EXT_CSD 8 /* R1 */
|
||||
#define MMC_SEND_CSD 9 /* R2 */
|
||||
#define MMC_STOP_TRANSMISSION 12 /* R1B */
|
||||
#define MMC_SEND_STATUS 13 /* R1 */
|
||||
#define MMC_SET_BLOCKLEN 16 /* R1 */
|
||||
#define MMC_READ_BLOCK_SINGLE 17 /* R1 */
|
||||
#define MMC_READ_BLOCK_MULTIPLE 18 /* R1 */
|
||||
#define MMC_SET_BLOCK_COUNT 23 /* R1 */
|
||||
#define MMC_WRITE_BLOCK_SINGLE 24 /* R1 */
|
||||
#define MMC_WRITE_BLOCK_MULTIPLE 25 /* R1 */
|
||||
#define MMC_APP_CMD 55 /* R1 */
|
||||
|
||||
/* SD commands */ /* response type */
|
||||
#define SD_SEND_RELATIVE_ADDR 3 /* R6 */
|
||||
#define SD_SEND_SWITCH_FUNC 6 /* R1 */
|
||||
#define SD_SEND_IF_COND 8 /* R7 */
|
||||
|
||||
/* SD application commands */ /* response type */
|
||||
#define SD_APP_SET_BUS_WIDTH 6 /* R1 */
|
||||
#define SD_APP_OP_COND 41 /* R3 */
|
||||
#define SD_APP_SEND_SCR 51 /* R1 */
|
||||
|
||||
/* OCR bits */
|
||||
#define MMC_OCR_MEM_READY (1<<31) /* memory power-up status bit */
|
||||
#define MMC_OCR_ACCESS_MODE_MASK 0x60000000 /* bits 30:29 */
|
||||
#define MMC_OCR_SECTOR_MODE (1<<30)
|
||||
#define MMC_OCR_BYTE_MODE (1<<29)
|
||||
#define MMC_OCR_3_5V_3_6V (1<<23)
|
||||
#define MMC_OCR_3_4V_3_5V (1<<22)
|
||||
#define MMC_OCR_3_3V_3_4V (1<<21)
|
||||
#define MMC_OCR_3_2V_3_3V (1<<20)
|
||||
#define MMC_OCR_3_1V_3_2V (1<<19)
|
||||
#define MMC_OCR_3_0V_3_1V (1<<18)
|
||||
#define MMC_OCR_2_9V_3_0V (1<<17)
|
||||
#define MMC_OCR_2_8V_2_9V (1<<16)
|
||||
#define MMC_OCR_2_7V_2_8V (1<<15)
|
||||
#define MMC_OCR_2_6V_2_7V (1<<14)
|
||||
#define MMC_OCR_2_5V_2_6V (1<<13)
|
||||
#define MMC_OCR_2_4V_2_5V (1<<12)
|
||||
#define MMC_OCR_2_3V_2_4V (1<<11)
|
||||
#define MMC_OCR_2_2V_2_3V (1<<10)
|
||||
#define MMC_OCR_2_1V_2_2V (1<<9)
|
||||
#define MMC_OCR_2_0V_2_1V (1<<8)
|
||||
#define MMC_OCR_1_65V_1_95V (1<<7)
|
||||
|
||||
#define SD_OCR_SDHC_CAP (1<<30)
|
||||
#define SD_OCR_VOL_MASK 0xFF8000 /* bits 23:15 */
|
||||
|
||||
/* R1 response type bits */
|
||||
#define MMC_R1_READY_FOR_DATA (1<<8) /* ready for next transfer */
|
||||
#define MMC_R1_APP_CMD (1<<5) /* app. commands supported */
|
||||
|
||||
/* 48-bit response decoding (32 bits w/o CRC) */
|
||||
#define MMC_R1(resp) ((resp)[0])
|
||||
#define MMC_R3(resp) ((resp)[0])
|
||||
#define SD_R6(resp) ((resp)[0])
|
||||
#define MMC_R1_CURRENT_STATE(resp) (((resp)[0] >> 9) & 0xf)
|
||||
|
||||
/* RCA argument and response */
|
||||
#define MMC_ARG_RCA(rca) ((rca) << 16)
|
||||
#define SD_R6_RCA(resp) (SD_R6((resp)) >> 16)
|
||||
|
||||
/* bus width argument */
|
||||
#define SD_ARG_BUS_WIDTH_1 0
|
||||
#define SD_ARG_BUS_WIDTH_4 2
|
||||
|
||||
/* EXT_CSD fields */
|
||||
#define EXT_CSD_BUS_WIDTH 183 /* WO */
|
||||
#define EXT_CSD_HS_TIMING 185 /* R/W */
|
||||
#define EXT_CSD_REV 192 /* RO */
|
||||
#define EXT_CSD_STRUCTURE 194 /* RO */
|
||||
#define EXT_CSD_CARD_TYPE 196 /* RO */
|
||||
#define EXT_CSD_SEC_COUNT 212 /* RO */
|
||||
|
||||
/* EXT_CSD field definitions */
|
||||
#define EXT_CSD_CMD_SET_NORMAL (1U << 0)
|
||||
#define EXT_CSD_CMD_SET_SECURE (1U << 1)
|
||||
#define EXT_CSD_CMD_SET_CPSECURE (1U << 2)
|
||||
|
||||
/* EXT_CSD_HS_TIMING */
|
||||
#define EXT_CSD_HS_TIMING_BC 0
|
||||
#define EXT_CSD_HS_TIMING_HS 1
|
||||
#define EXT_CSD_HS_TIMING_HS200 2
|
||||
#define EXT_CSD_HS_TIMING_HS400 3
|
||||
|
||||
/* EXT_CSD_BUS_WIDTH */
|
||||
#define EXT_CSD_BUS_WIDTH_1 0
|
||||
#define EXT_CSD_BUS_WIDTH_4 1
|
||||
#define EXT_CSD_BUS_WIDTH_8 2
|
||||
#define EXT_CSD_BUS_WIDTH_4_DDR 5
|
||||
#define EXT_CSD_BUS_WIDTH_8_DDR 6
|
||||
|
||||
/* EXT_CSD_CARD_TYPE */
|
||||
/* The only currently valid values for this field are 0x01, 0x03, 0x07,
|
||||
* 0x0B and 0x0F. */
|
||||
#define EXT_CSD_CARD_TYPE_F_26M (1 << 0)
|
||||
#define EXT_CSD_CARD_TYPE_F_52M (1 << 1)
|
||||
#define EXT_CSD_CARD_TYPE_F_52M_1_8V (1 << 2)
|
||||
#define EXT_CSD_CARD_TYPE_F_52M_1_2V (1 << 3)
|
||||
#define EXT_CSD_CARD_TYPE_26M 0x01
|
||||
#define EXT_CSD_CARD_TYPE_52M 0x03
|
||||
#define EXT_CSD_CARD_TYPE_52M_V18 0x07
|
||||
#define EXT_CSD_CARD_TYPE_52M_V12 0x0b
|
||||
#define EXT_CSD_CARD_TYPE_52M_V12_18 0x0f
|
||||
|
||||
/* MMC_SWITCH access mode */
|
||||
#define MMC_SWITCH_MODE_CMD_SET 0x00 /* Change the command set */
|
||||
#define MMC_SWITCH_MODE_SET_BITS 0x01 /* Set bits in value */
|
||||
#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits in value */
|
||||
#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */
|
||||
|
||||
/* MMC R2 response (CSD) */
|
||||
#define MMC_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2)
|
||||
#define MMC_CSD_CSDVER_1_0 1
|
||||
#define MMC_CSD_CSDVER_2_0 2
|
||||
#define MMC_CSD_CSDVER_EXT_CSD 3
|
||||
#define MMC_CSD_MMCVER(resp) MMC_RSP_BITS((resp), 122, 4)
|
||||
#define MMC_CSD_MMCVER_1_0 0 /* MMC 1.0 - 1.2 */
|
||||
#define MMC_CSD_MMCVER_1_4 1 /* MMC 1.4 */
|
||||
#define MMC_CSD_MMCVER_2_0 2 /* MMC 2.0 - 2.2 */
|
||||
#define MMC_CSD_MMCVER_3_1 3 /* MMC 3.1 - 3.3 */
|
||||
#define MMC_CSD_MMCVER_4_0 4 /* MMC 4 */
|
||||
#define MMC_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4)
|
||||
#define MMC_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12)
|
||||
#define MMC_CSD_CAPACITY(resp) ((MMC_CSD_C_SIZE((resp))+1) << \
|
||||
(MMC_CSD_C_SIZE_MULT((resp))+2))
|
||||
#define MMC_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3)
|
||||
|
||||
/* MMC v1 R2 response (CID) */
|
||||
#define MMC_CID_MID_V1(resp) MMC_RSP_BITS((resp), 104, 24)
|
||||
#define MMC_CID_PNM_V1_CPY(resp, pnm) \
|
||||
do { \
|
||||
(pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \
|
||||
(pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \
|
||||
(pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \
|
||||
(pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \
|
||||
(pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \
|
||||
(pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \
|
||||
(pnm)[6] = MMC_RSP_BITS((resp), 48, 8); \
|
||||
(pnm)[7] = '\0'; \
|
||||
} while (0)
|
||||
#define MMC_CID_REV_V1(resp) MMC_RSP_BITS((resp), 40, 8)
|
||||
#define MMC_CID_PSN_V1(resp) MMC_RSP_BITS((resp), 16, 24)
|
||||
#define MMC_CID_MDT_V1(resp) MMC_RSP_BITS((resp), 8, 8)
|
||||
|
||||
/* MMC v2 R2 response (CID) */
|
||||
#define MMC_CID_MID_V2(resp) MMC_RSP_BITS((resp), 120, 8)
|
||||
#define MMC_CID_OID_V2(resp) MMC_RSP_BITS((resp), 104, 16)
|
||||
#define MMC_CID_PNM_V2_CPY(resp, pnm) \
|
||||
do { \
|
||||
(pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \
|
||||
(pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \
|
||||
(pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \
|
||||
(pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \
|
||||
(pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \
|
||||
(pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \
|
||||
(pnm)[6] = '\0'; \
|
||||
} while (0)
|
||||
#define MMC_CID_PSN_V2(resp) MMC_RSP_BITS((resp), 16, 32)
|
||||
|
||||
/* SD R2 response (CSD) */
|
||||
#define SD_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2)
|
||||
#define SD_CSD_CSDVER_1_0 0
|
||||
#define SD_CSD_CSDVER_2_0 1
|
||||
#define SD_CSD_TAAC(resp) MMC_RSP_BITS((resp), 112, 8)
|
||||
#define SD_CSD_TAAC_1_5_MSEC 0x26
|
||||
#define SD_CSD_NSAC(resp) MMC_RSP_BITS((resp), 104, 8)
|
||||
#define SD_CSD_SPEED(resp) MMC_RSP_BITS((resp), 96, 8)
|
||||
#define SD_CSD_SPEED_25_MHZ 0x32
|
||||
#define SD_CSD_SPEED_50_MHZ 0x5a
|
||||
#define SD_CSD_CCC(resp) MMC_RSP_BITS((resp), 84, 12)
|
||||
#define SD_CSD_CCC_BASIC (1 << 0) /* basic */
|
||||
#define SD_CSD_CCC_BR (1 << 2) /* block read */
|
||||
#define SD_CSD_CCC_BW (1 << 4) /* block write */
|
||||
#define SD_CSD_CCC_ERASE (1 << 5) /* erase */
|
||||
#define SD_CSD_CCC_WP (1 << 6) /* write protection */
|
||||
#define SD_CSD_CCC_LC (1 << 7) /* lock card */
|
||||
#define SD_CSD_CCC_AS (1 << 8) /*application specific*/
|
||||
#define SD_CSD_CCC_IOM (1 << 9) /* I/O mode */
|
||||
#define SD_CSD_CCC_SWITCH (1 << 10) /* switch */
|
||||
#define SD_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4)
|
||||
#define SD_CSD_READ_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 79, 1)
|
||||
#define SD_CSD_WRITE_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 78, 1)
|
||||
#define SD_CSD_READ_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 77, 1)
|
||||
#define SD_CSD_DSR_IMP(resp) MMC_RSP_BITS((resp), 76, 1)
|
||||
#define SD_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12)
|
||||
#define SD_CSD_CAPACITY(resp) ((SD_CSD_C_SIZE((resp))+1) << \
|
||||
(SD_CSD_C_SIZE_MULT((resp))+2))
|
||||
#define SD_CSD_V2_C_SIZE(resp) MMC_RSP_BITS((resp), 48, 22)
|
||||
#define SD_CSD_V2_CAPACITY(resp) ((SD_CSD_V2_C_SIZE((resp))+1) << 10)
|
||||
#define SD_CSD_V2_BL_LEN 0x9 /* 512 */
|
||||
#define SD_CSD_VDD_R_CURR_MIN(resp) MMC_RSP_BITS((resp), 59, 3)
|
||||
#define SD_CSD_VDD_R_CURR_MAX(resp) MMC_RSP_BITS((resp), 56, 3)
|
||||
#define SD_CSD_VDD_W_CURR_MIN(resp) MMC_RSP_BITS((resp), 53, 3)
|
||||
#define SD_CSD_VDD_W_CURR_MAX(resp) MMC_RSP_BITS((resp), 50, 3)
|
||||
#define SD_CSD_VDD_RW_CURR_100mA 0x7
|
||||
#define SD_CSD_VDD_RW_CURR_80mA 0x6
|
||||
#define SD_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3)
|
||||
#define SD_CSD_ERASE_BLK_EN(resp) MMC_RSP_BITS((resp), 46, 1)
|
||||
#define SD_CSD_SECTOR_SIZE(resp) MMC_RSP_BITS((resp), 39, 7) /* +1 */
|
||||
#define SD_CSD_WP_GRP_SIZE(resp) MMC_RSP_BITS((resp), 32, 7) /* +1 */
|
||||
#define SD_CSD_WP_GRP_ENABLE(resp) MMC_RSP_BITS((resp), 31, 1)
|
||||
#define SD_CSD_R2W_FACTOR(resp) MMC_RSP_BITS((resp), 26, 3)
|
||||
#define SD_CSD_WRITE_BL_LEN(resp) MMC_RSP_BITS((resp), 22, 4)
|
||||
#define SD_CSD_RW_BL_LEN_2G 0xa
|
||||
#define SD_CSD_RW_BL_LEN_1G 0x9
|
||||
#define SD_CSD_WRITE_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 21, 1)
|
||||
#define SD_CSD_FILE_FORMAT_GRP(resp) MMC_RSP_BITS((resp), 15, 1)
|
||||
#define SD_CSD_COPY(resp) MMC_RSP_BITS((resp), 14, 1)
|
||||
#define SD_CSD_PERM_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 13, 1)
|
||||
#define SD_CSD_TMP_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 12, 1)
|
||||
#define SD_CSD_FILE_FORMAT(resp) MMC_RSP_BITS((resp), 10, 2)
|
||||
|
||||
/* SD R2 response (CID) */
|
||||
#define SD_CID_MID(resp) MMC_RSP_BITS((resp), 120, 8)
|
||||
#define SD_CID_OID(resp) MMC_RSP_BITS((resp), 104, 16)
|
||||
#define SD_CID_PNM_CPY(resp, pnm) \
|
||||
do { \
|
||||
(pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \
|
||||
(pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \
|
||||
(pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \
|
||||
(pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \
|
||||
(pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \
|
||||
(pnm)[5] = '\0'; \
|
||||
} while (0)
|
||||
#define SD_CID_REV(resp) MMC_RSP_BITS((resp), 56, 8)
|
||||
#define SD_CID_PSN(resp) MMC_RSP_BITS((resp), 24, 32)
|
||||
#define SD_CID_MDT(resp) MMC_RSP_BITS((resp), 8, 12)
|
||||
|
||||
/* SCR (SD Configuration Register) */
|
||||
#define SCR_STRUCTURE(scr) MMC_RSP_BITS((scr), 60, 4)
|
||||
#define SCR_STRUCTURE_VER_1_0 0 /* Version 1.0 */
|
||||
#define SCR_SD_SPEC(scr) MMC_RSP_BITS((scr), 56, 4)
|
||||
#define SCR_SD_SPEC_VER_1_0 0 /* Version 1.0 and 1.01 */
|
||||
#define SCR_SD_SPEC_VER_1_10 1 /* Version 1.10 */
|
||||
#define SCR_SD_SPEC_VER_2 2 /* Version 2.00 or Version 3.0X */
|
||||
#define SCR_DATA_STAT_AFTER_ERASE(scr) MMC_RSP_BITS((scr), 55, 1)
|
||||
#define SCR_SD_SECURITY(scr) MMC_RSP_BITS((scr), 52, 3)
|
||||
#define SCR_SD_SECURITY_NONE 0 /* no security */
|
||||
#define SCR_SD_SECURITY_1_0 1 /* security protocol 1.0 */
|
||||
#define SCR_SD_SECURITY_1_0_2 2 /* security protocol 1.0 */
|
||||
#define SCR_SD_BUS_WIDTHS(scr) MMC_RSP_BITS((scr), 48, 4)
|
||||
#define SCR_SD_BUS_WIDTHS_1BIT (1 << 0) /* 1bit (DAT0) */
|
||||
#define SCR_SD_BUS_WIDTHS_4BIT (1 << 2) /* 4bit (DAT0-3) */
|
||||
#define SCR_SD_SPEC3(scr) MMC_RSP_BITS((scr), 47, 1)
|
||||
#define SCR_EX_SECURITY(scr) MMC_RSP_BITS((scr), 43, 4)
|
||||
#define SCR_SD_SPEC4(scr) MMC_RSP_BITS((scr), 42, 1)
|
||||
#define SCR_RESERVED(scr) MMC_RSP_BITS((scr), 34, 8)
|
||||
#define SCR_CMD_SUPPORT_CMD23(scr) MMC_RSP_BITS((scr), 33, 1)
|
||||
#define SCR_CMD_SUPPORT_CMD20(scr) MMC_RSP_BITS((scr), 32, 1)
|
||||
#define SCR_RESERVED2(scr) MMC_RSP_BITS((scr), 0, 32)
|
||||
|
||||
/* Status of Switch Function */
|
||||
#define SFUNC_STATUS_GROUP(status, group) \
|
||||
(__bitfield((uint32_t *)(status), 400 + (group - 1) * 16, 16))
|
||||
|
||||
#define SD_ACCESS_MODE_SDR12 0
|
||||
#define SD_ACCESS_MODE_SDR25 1
|
||||
#define SD_ACCESS_MODE_SDR50 2
|
||||
#define SD_ACCESS_MODE_SDR104 3
|
||||
#define SD_ACCESS_MODE_DDR50 4
|
||||
|
||||
static inline uint32_t MMC_RSP_BITS(uint32_t *src, int start, int len)
|
||||
{
|
||||
uint32_t mask = (len % 32 == 0) ? UINT_MAX : UINT_MAX >> (32 - (len % 32));
|
||||
size_t word = 3 - start / 32;
|
||||
size_t shift = start % 32;
|
||||
uint32_t right = src[word] >> shift;
|
||||
uint32_t left = (len + shift <= 32) ? 0 : src[word - 1] << ((32 - shift) % 32);
|
||||
return (left | right) & mask;
|
||||
}
|
||||
|
||||
#endif //_SDMMC_DEFS_H_
|
165
components/driver/include/driver/sdmmc_host.h
Normal file
165
components/driver/include/driver/sdmmc_host.h
Normal file
@@ -0,0 +1,165 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "esp_err.h"
|
||||
#include "sdmmc_types.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SDMMC_HOST_SLOT_0 0 ///< SDMMC slot 0
|
||||
#define SDMMC_HOST_SLOT_1 1 ///< SDMMC slot 1
|
||||
|
||||
/**
|
||||
* @brief Default sdmmc_host_t structure initializer for SDMMC peripheral
|
||||
*
|
||||
* Uses SDMMC peripheral, with 4-bit mode enabled, and max frequency set to 20MHz
|
||||
*/
|
||||
#define SDMMC_HOST_DEFAULT() {\
|
||||
.flags = SDMMC_HOST_FLAG_4BIT, \
|
||||
.slot = SDMMC_HOST_SLOT_1, \
|
||||
.max_freq_khz = SDMMC_FREQ_DEFAULT, \
|
||||
.io_voltage = 3.3f, \
|
||||
.init = &sdmmc_host_init, \
|
||||
.set_bus_width = &sdmmc_host_set_bus_width, \
|
||||
.set_card_clk = &sdmmc_host_set_card_clk, \
|
||||
.do_transaction = &sdmmc_host_do_transaction, \
|
||||
.deinit = &sdmmc_host_deinit, \
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra configuration for SDMMC peripheral slot
|
||||
*/
|
||||
typedef struct {
|
||||
gpio_num_t gpio_cd; ///< GPIO number of card detect signal
|
||||
gpio_num_t gpio_wp; ///< GPIO number of write protect signal
|
||||
} sdmmc_slot_config_t;
|
||||
|
||||
#define SDMMC_SLOT_NO_CD ((gpio_num_t) -1) ///< indicates that card detect line is not used
|
||||
#define SDMMC_SLOT_NO_WP ((gpio_num_t) -1) ///< indicates that write protect line is not used
|
||||
|
||||
/**
|
||||
* Macro defining default configuration of SDMMC host slot
|
||||
*/
|
||||
#define SDMMC_SLOT_CONFIG_DEFAULT() {\
|
||||
.gpio_cd = SDMMC_SLOT_NO_CD, \
|
||||
.gpio_wp = SDMMC_SLOT_NO_WP, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize SDMMC host peripheral
|
||||
*
|
||||
* @note This function is not thread safe
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if sdmmc_host_init was already called
|
||||
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||
*/
|
||||
esp_err_t sdmmc_host_init();
|
||||
|
||||
/**
|
||||
* @brief Initialize given slot of SDMMC peripheral
|
||||
*
|
||||
* On the ESP32, SDMMC peripheral has two slots:
|
||||
* - Slot 0: 8-bit wide, maps to HS1_* signals in PIN MUX
|
||||
* - Slot 1: 4-bit wide, maps to HS2_* signals in PIN MUX
|
||||
*
|
||||
* Card detect and write protect signals can be routed to
|
||||
* arbitrary GPIOs using GPIO matrix.
|
||||
*
|
||||
* @note This function is not thread safe
|
||||
*
|
||||
* @param slot slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)
|
||||
* @param slot_config additional configuration for the slot
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if host has not been initialized using sdmmc_host_init
|
||||
*/
|
||||
esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t* slot_config);
|
||||
|
||||
/**
|
||||
* @brief Select bus width to be used for data transfer
|
||||
*
|
||||
* SD/MMC card must be initialized prior to this command, and a command to set
|
||||
* bus width has to be sent to the card (e.g. SD_APP_SET_BUS_WIDTH)
|
||||
*
|
||||
* @note This function is not thread safe
|
||||
*
|
||||
* @param slot slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)
|
||||
* @param width bus width (1, 4, or 8 for slot 0; 1 or 4 for slot 1)
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG if slot number or width is not valid
|
||||
*/
|
||||
esp_err_t sdmmc_host_set_bus_width(int slot, size_t width);
|
||||
|
||||
/**
|
||||
* @brief Set card clock frequency
|
||||
*
|
||||
* Currently only integer fractions of 40MHz clock can be used.
|
||||
* For High Speed cards, 40MHz can be used.
|
||||
* For Default Speed cards, 20MHz can be used.
|
||||
*
|
||||
* @note This function is not thread safe
|
||||
*
|
||||
* @param slot slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)
|
||||
* @param freq_khz card clock frequency, in kHz
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - other error codes may be returned in the future
|
||||
*/
|
||||
esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz);
|
||||
|
||||
/**
|
||||
* @brief Send command to the card and get response
|
||||
*
|
||||
* This function returns when command is sent and response is received,
|
||||
* or data is transferred, or timeout occurs.
|
||||
*
|
||||
* @note This function is not thread safe w.r.t. init/deinit functions,
|
||||
* and bus width/clock speed configuration functions. Multiple tasks
|
||||
* can call sdmmc_host_do_transaction as long as other sdmmc_host_*
|
||||
* functions are not called.
|
||||
*
|
||||
* @param slot slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)
|
||||
* @param cmdinfo pointer to structure describing command and data to transfer
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_TIMEOUT if response or data transfer has timed out
|
||||
* - ESP_ERR_INVALID_CRC if response or data transfer CRC check has failed
|
||||
* - ESP_ERR_INVALID_RESPONSE if the card has sent an invalid response
|
||||
*/
|
||||
esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo);
|
||||
|
||||
/**
|
||||
* @brief Disable SDMMC host and release allocated resources
|
||||
*
|
||||
* @note This function is not thread safe
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if sdmmc_host_init function has not been called
|
||||
*/
|
||||
esp_err_t sdmmc_host_deinit();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
140
components/driver/include/driver/sdmmc_types.h
Normal file
140
components/driver/include/driver/sdmmc_types.h
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
* Adaptations to ESP-IDF Copyright (c) 2016 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _SDMMC_TYPES_H_
|
||||
#define _SDMMC_TYPES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
/**
|
||||
* Decoded values from SD card Card Specific Data register
|
||||
*/
|
||||
typedef struct {
|
||||
int csd_ver; /*!< CSD structure format */
|
||||
int mmc_ver; /*!< MMC version (for CID format) */
|
||||
int capacity; /*!< total number of sectors */
|
||||
int sector_size; /*!< sector size in bytes */
|
||||
int read_block_len; /*!< block length for reads */
|
||||
int card_command_class; /*!< Card Command Class for SD */
|
||||
int tr_speed; /*!< Max transfer speed */
|
||||
} sdmmc_csd_t;
|
||||
|
||||
/**
|
||||
* Decoded values from SD card Card IDentification register
|
||||
*/
|
||||
typedef struct {
|
||||
int mfg_id; /*!< manufacturer identification number */
|
||||
int oem_id; /*!< OEM/product identification number */
|
||||
char name[8]; /*!< product name (MMC v1 has the longest) */
|
||||
int revision; /*!< product revision */
|
||||
int serial; /*!< product serial number */
|
||||
int date; /*!< manufacturing date */
|
||||
} sdmmc_cid_t;
|
||||
|
||||
/**
|
||||
* Decoded values from SD Configuration Register
|
||||
*/
|
||||
typedef struct {
|
||||
int sd_spec; /*!< SD Physical layer specification version, reported by card */
|
||||
int bus_width; /*!< bus widths supported by card: BIT(0) — 1-bit bus, BIT(2) — 4-bit bus */
|
||||
} sdmmc_scr_t;
|
||||
|
||||
/**
|
||||
* SD/MMC command response buffer
|
||||
*/
|
||||
typedef uint32_t sdmmc_response_t[4];
|
||||
|
||||
/**
|
||||
* SD/MMC command information
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t opcode; /*!< SD or MMC command index */
|
||||
uint32_t arg; /*!< SD/MMC command argument */
|
||||
sdmmc_response_t response; /*!< response buffer */
|
||||
void* data; /*!< buffer to send or read into */
|
||||
size_t datalen; /*!< length of data buffer */
|
||||
size_t blklen; /*!< block length */
|
||||
int flags; /*!< see below */
|
||||
#define SCF_ITSDONE 0x0001 /*!< command is complete */
|
||||
#define SCF_CMD(flags) ((flags) & 0x00f0)
|
||||
#define SCF_CMD_AC 0x0000
|
||||
#define SCF_CMD_ADTC 0x0010
|
||||
#define SCF_CMD_BC 0x0020
|
||||
#define SCF_CMD_BCR 0x0030
|
||||
#define SCF_CMD_READ 0x0040 /*!< read command (data expected) */
|
||||
#define SCF_RSP_BSY 0x0100
|
||||
#define SCF_RSP_136 0x0200
|
||||
#define SCF_RSP_CRC 0x0400
|
||||
#define SCF_RSP_IDX 0x0800
|
||||
#define SCF_RSP_PRESENT 0x1000
|
||||
/* response types */
|
||||
#define SCF_RSP_R0 0 /*!< none */
|
||||
#define SCF_RSP_R1 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
|
||||
#define SCF_RSP_R1B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY)
|
||||
#define SCF_RSP_R2 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_136)
|
||||
#define SCF_RSP_R3 (SCF_RSP_PRESENT)
|
||||
#define SCF_RSP_R4 (SCF_RSP_PRESENT)
|
||||
#define SCF_RSP_R5 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
|
||||
#define SCF_RSP_R5B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY)
|
||||
#define SCF_RSP_R6 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
|
||||
#define SCF_RSP_R7 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
|
||||
esp_err_t error; /*!< error returned from transfer */
|
||||
} sdmmc_command_t;
|
||||
|
||||
/**
|
||||
* SD/MMC Host description
|
||||
*
|
||||
* This structure defines properties of SD/MMC host and functions
|
||||
* of SD/MMC host which can be used by upper layers.
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t flags; /*!< flags defining host properties */
|
||||
#define SDMMC_HOST_FLAG_1BIT BIT(0) /*!< host supports 1-line SD and MMC protocol */
|
||||
#define SDMMC_HOST_FLAG_4BIT BIT(1) /*!< host supports 4-line SD and MMC protocol */
|
||||
#define SDMMC_HOST_FLAG_8BIT BIT(2) /*!< host supports 8-line MMC protocol */
|
||||
#define SDMMC_HOST_FLAG_SPI BIT(3) /*!< host supports SPI protocol */
|
||||
int slot; /*!< slot number, to be passed to host functions */
|
||||
int max_freq_khz; /*!< max frequency supported by the host */
|
||||
#define SDMMC_FREQ_DEFAULT 20000 /*!< SD/MMC Default speed (limited by clock divider) */
|
||||
#define SDMMC_FREQ_HIGHSPEED 40000 /*!< SD High speed (limited by clock divider) */
|
||||
#define SDMMC_FREQ_PROBING 4000 /*!< SD/MMC probing speed */
|
||||
float io_voltage; /*!< I/O voltage used by the controller (voltage switching is not supported) */
|
||||
esp_err_t (*init)(void); /*!< Host function to initialize the driver */
|
||||
esp_err_t (*set_bus_width)(int slot, size_t width); /*!< host function to set bus width */
|
||||
esp_err_t (*set_card_clk)(int slot, uint32_t freq_khz); /*!< host function to set card clock frequency */
|
||||
esp_err_t (*do_transaction)(int slot, sdmmc_command_t* cmdinfo); /*!< host function to do a transaction */
|
||||
esp_err_t (*deinit)(void); /*!< host function to deinitialize the driver */
|
||||
} sdmmc_host_t;
|
||||
|
||||
/**
|
||||
* SD/MMC card information structure
|
||||
*/
|
||||
typedef struct {
|
||||
sdmmc_host_t host; /*!< Host with which the card is associated */
|
||||
uint32_t ocr; /*!< OCR (Operation Conditions Register) value */
|
||||
sdmmc_cid_t cid; /*!< decoded CID (Card IDentification) register value */
|
||||
sdmmc_csd_t csd; /*!< decoded CSD (Card-Specific Data) register value */
|
||||
sdmmc_scr_t scr; /*!< decoded SCR (SD card Configuration Register) value */
|
||||
uint16_t rca; /*!< RCA (Relative Card Address) */
|
||||
} sdmmc_card_t;
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // _SDMMC_TYPES_H_
|
463
components/driver/sdmmc_host.c
Normal file
463
components/driver/sdmmc_host.c
Normal file
@@ -0,0 +1,463 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/param.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "soc/sdmmc_struct.h"
|
||||
#include "soc/sdmmc_reg.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "rom/gpio.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/sdmmc_host.h"
|
||||
#include "sdmmc_private.h"
|
||||
|
||||
#define SDMMC_EVENT_QUEUE_LENGTH 32
|
||||
|
||||
typedef struct {
|
||||
uint32_t clk;
|
||||
uint32_t cmd;
|
||||
uint32_t d0;
|
||||
uint32_t d1;
|
||||
uint32_t d2;
|
||||
uint32_t d3;
|
||||
uint32_t d4;
|
||||
uint32_t d5;
|
||||
uint32_t d6;
|
||||
uint32_t d7;
|
||||
uint8_t card_detect;
|
||||
uint8_t write_protect;
|
||||
uint8_t width;
|
||||
} sdmmc_slot_info_t;
|
||||
|
||||
|
||||
static void sdmmc_isr(void* arg);
|
||||
static void sdmmc_host_dma_init();
|
||||
|
||||
static const sdmmc_slot_info_t s_slot_info[2] = {
|
||||
{
|
||||
.clk = PERIPHS_IO_MUX_SD_CLK_U,
|
||||
.cmd = PERIPHS_IO_MUX_SD_CMD_U,
|
||||
.d0 = PERIPHS_IO_MUX_SD_DATA0_U,
|
||||
.d1 = PERIPHS_IO_MUX_SD_DATA1_U,
|
||||
.d2 = PERIPHS_IO_MUX_SD_DATA2_U,
|
||||
.d3 = PERIPHS_IO_MUX_SD_DATA3_U,
|
||||
.d4 = PERIPHS_IO_MUX_GPIO16_U,
|
||||
.d5 = PERIPHS_IO_MUX_GPIO17_U,
|
||||
.d6 = PERIPHS_IO_MUX_GPIO5_U,
|
||||
.d7 = PERIPHS_IO_MUX_GPIO18_U,
|
||||
.card_detect = HOST_CARD_DETECT_N_1_IDX,
|
||||
.write_protect = HOST_CARD_WRITE_PRT_1_IDX,
|
||||
.width = 8
|
||||
},
|
||||
{
|
||||
.clk = PERIPHS_IO_MUX_MTMS_U,
|
||||
.cmd = PERIPHS_IO_MUX_MTDO_U,
|
||||
.d0 = PERIPHS_IO_MUX_GPIO2_U,
|
||||
.d1 = PERIPHS_IO_MUX_GPIO4_U,
|
||||
.d2 = PERIPHS_IO_MUX_MTDI_U,
|
||||
.d3 = PERIPHS_IO_MUX_MTCK_U,
|
||||
.card_detect = HOST_CARD_DETECT_N_2_IDX,
|
||||
.write_protect = HOST_CARD_WRITE_PRT_2_IDX,
|
||||
.width = 4
|
||||
}
|
||||
};
|
||||
|
||||
static const char* TAG = "sdmmc_periph";
|
||||
static intr_handle_t s_intr_handle;
|
||||
static QueueHandle_t s_event_queue;
|
||||
|
||||
|
||||
void sdmmc_host_reset()
|
||||
{
|
||||
// Set reset bits
|
||||
SDMMC.ctrl.controller_reset = 1;
|
||||
SDMMC.ctrl.dma_reset = 1;
|
||||
SDMMC.ctrl.fifo_reset = 1;
|
||||
// Wait for the reset bits to be cleared by hardware
|
||||
while (SDMMC.ctrl.controller_reset || SDMMC.ctrl.fifo_reset || SDMMC.ctrl.dma_reset) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/* We have two clock divider stages:
|
||||
* - one is the clock generator which drives SDMMC peripheral,
|
||||
* it can be configured using SDMMC.clock register. It can generate
|
||||
* frequencies 160MHz/(N + 1), where 0 < N < 16, I.e. from 10 to 80 MHz.
|
||||
* - 4 clock dividers inside SDMMC peripheral, which can divide clock
|
||||
* from the first stage by 2 * M, where 0 < M < 255
|
||||
* (they can also be bypassed).
|
||||
*
|
||||
* For cards which aren't UHS-1 or UHS-2 cards, which we don't support,
|
||||
* maximum bus frequency in high speed (HS) mode is 50 MHz.
|
||||
* Note: for non-UHS-1 cards, HS mode is optional.
|
||||
* Default speed (DS) mode is mandatory, it works up to 25 MHz.
|
||||
* Whether the card supports HS or not can be determined using TRAN_SPEED
|
||||
* field of card's CSD register.
|
||||
*
|
||||
* 50 MHz can not be obtained exactly, closest we can get is 53 MHz.
|
||||
* For now set the first stage divider to generate 40MHz, and then configure
|
||||
* the second stage dividers to generate the frequency requested.
|
||||
*
|
||||
* Of the second stage dividers, div0 is used for card 0, and div1 is used
|
||||
* for card 1.
|
||||
*/
|
||||
|
||||
static void sdmmc_host_input_clk_enable()
|
||||
{
|
||||
// Set frequency to 160MHz / (p + 1) = 40MHz, duty cycle (h + 1)/(p + 1) = 1/2
|
||||
SDMMC.clock.div_factor_p = 3;
|
||||
SDMMC.clock.div_factor_h = 1;
|
||||
SDMMC.clock.div_factor_m = 3;
|
||||
// Set phases for in/out clocks
|
||||
SDMMC.clock.phase_dout = 4;
|
||||
SDMMC.clock.phase_din = 4;
|
||||
SDMMC.clock.phase_core = 0;
|
||||
// Wait for the clock to propagate
|
||||
ets_delay_us(10);
|
||||
}
|
||||
|
||||
static void sdmmc_host_input_clk_disable()
|
||||
{
|
||||
SDMMC.clock.val = 0;
|
||||
}
|
||||
|
||||
static void sdmmc_host_clock_update_command(int slot)
|
||||
{
|
||||
// Clock update command (not a real command; just updates CIU registers)
|
||||
sdmmc_hw_cmd_t cmd_val = {
|
||||
.card_num = slot,
|
||||
.update_clk_reg = 1,
|
||||
.wait_complete = 1
|
||||
};
|
||||
bool repeat = true;
|
||||
while(repeat) {
|
||||
sdmmc_host_start_command(slot, cmd_val, 0);
|
||||
while (true) {
|
||||
// Sending clock update command to the CIU can generate HLE error.
|
||||
// According to the manual, this is okay and we must retry the command.
|
||||
if (SDMMC.rintsts.hle) {
|
||||
SDMMC.rintsts.hle = 1;
|
||||
repeat = true;
|
||||
break;
|
||||
}
|
||||
// When the command is accepted by CIU, start_command bit will be
|
||||
// cleared in SDMMC.cmd register.
|
||||
if (SDMMC.cmd.start_command == 0) {
|
||||
repeat = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz)
|
||||
{
|
||||
if (!(slot == 0 || slot == 1)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
const int clk40m = 40000;
|
||||
|
||||
// Disable clock first
|
||||
SDMMC.clkena.cclk_enable &= ~BIT(slot);
|
||||
sdmmc_host_clock_update_command(slot);
|
||||
|
||||
// Calculate new dividers
|
||||
int div = 0;
|
||||
if (freq_khz < clk40m) {
|
||||
// round up; extra *2 is because clock divider divides by 2*n
|
||||
div = (clk40m + freq_khz * 2 - 1) / (freq_khz * 2);
|
||||
}
|
||||
ESP_LOGD(TAG, "slot=%d div=%d freq=%dkHz", slot, div,
|
||||
(div == 0) ? clk40m : clk40m / (2 * div));
|
||||
|
||||
// Program CLKDIV and CLKSRC, send them to the CIU
|
||||
switch(slot) {
|
||||
case 0:
|
||||
SDMMC.clksrc.card0 = 0;
|
||||
SDMMC.clkdiv.div0 = div;
|
||||
break;
|
||||
case 1:
|
||||
SDMMC.clksrc.card1 = 1;
|
||||
SDMMC.clkdiv.div1 = div;
|
||||
break;
|
||||
}
|
||||
sdmmc_host_clock_update_command(slot);
|
||||
|
||||
// Re-enable clocks
|
||||
SDMMC.clkena.cclk_enable |= BIT(slot);
|
||||
SDMMC.clkena.cclk_low_power |= BIT(slot);
|
||||
sdmmc_host_clock_update_command(slot);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_host_start_command(int slot, sdmmc_hw_cmd_t cmd, uint32_t arg) {
|
||||
if (!(slot == 0 || slot == 1)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
while (SDMMC.cmd.start_command == 1) {
|
||||
;
|
||||
}
|
||||
SDMMC.cmdarg = arg;
|
||||
cmd.card_num = slot;
|
||||
cmd.start_command = 1;
|
||||
SDMMC.cmd = cmd;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_host_init()
|
||||
{
|
||||
if (s_intr_handle) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
// Enable clock to peripheral
|
||||
sdmmc_host_input_clk_enable();
|
||||
|
||||
// Reset
|
||||
sdmmc_host_reset();
|
||||
ESP_LOGD(TAG, "peripheral version %x, hardware config %08x", SDMMC.verid, SDMMC.hcon);
|
||||
|
||||
// Clear interrupt status and set interrupt mask to known state
|
||||
SDMMC.rintsts.val = 0xffffffff;
|
||||
SDMMC.intmask.val = 0;
|
||||
SDMMC.ctrl.int_enable = 0;
|
||||
|
||||
// Allocate event queue
|
||||
s_event_queue = xQueueCreate(SDMMC_EVENT_QUEUE_LENGTH, sizeof(sdmmc_event_t));
|
||||
if (!s_event_queue) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
// Attach interrupt handler
|
||||
esp_err_t ret = esp_intr_alloc(ETS_SDIO_HOST_INTR_SOURCE, 0, &sdmmc_isr, s_event_queue, &s_intr_handle);
|
||||
if (ret != ESP_OK) {
|
||||
vQueueDelete(s_event_queue);
|
||||
s_event_queue = NULL;
|
||||
return ret;
|
||||
}
|
||||
// Enable interrupts
|
||||
SDMMC.intmask.val =
|
||||
SDMMC_INTMASK_CD |
|
||||
SDMMC_INTMASK_CMD_DONE |
|
||||
SDMMC_INTMASK_DATA_OVER |
|
||||
SDMMC_INTMASK_RCRC | SDMMC_INTMASK_DCRC |
|
||||
SDMMC_INTMASK_RTO | SDMMC_INTMASK_DTO | SDMMC_INTMASK_HTO |
|
||||
SDMMC_INTMASK_SBE | SDMMC_INTMASK_EBE |
|
||||
SDMMC_INTMASK_RESP_ERR | SDMMC_INTMASK_HLE;
|
||||
SDMMC.ctrl.int_enable = 1;
|
||||
|
||||
// Enable DMA
|
||||
sdmmc_host_dma_init();
|
||||
|
||||
// Initialize transaction handler
|
||||
ret = sdmmc_host_transaction_handler_init();
|
||||
if (ret != ESP_OK) {
|
||||
vQueueDelete(s_event_queue);
|
||||
s_event_queue = NULL;
|
||||
esp_intr_free(s_intr_handle);
|
||||
s_intr_handle = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
static inline void configure_pin(uint32_t io_mux_reg)
|
||||
{
|
||||
const int sdmmc_func = 3;
|
||||
const int drive_strength = 3;
|
||||
PIN_INPUT_ENABLE(io_mux_reg);
|
||||
PIN_FUNC_SELECT(io_mux_reg, sdmmc_func);
|
||||
PIN_SET_DRV(io_mux_reg, drive_strength);
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t* slot_config)
|
||||
{
|
||||
if (!s_intr_handle) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (!(slot == 0 || slot == 1)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (slot_config == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
int gpio_cd = slot_config->gpio_cd;
|
||||
int gpio_wp = slot_config->gpio_wp;
|
||||
|
||||
// Configure pins
|
||||
const sdmmc_slot_info_t* pslot = &s_slot_info[slot];
|
||||
configure_pin(pslot->clk);
|
||||
configure_pin(pslot->cmd);
|
||||
configure_pin(pslot->d0);
|
||||
configure_pin(pslot->d1);
|
||||
configure_pin(pslot->d2);
|
||||
configure_pin(pslot->d3);
|
||||
if (pslot->width == 8) {
|
||||
configure_pin(pslot->d4);
|
||||
configure_pin(pslot->d5);
|
||||
configure_pin(pslot->d6);
|
||||
configure_pin(pslot->d7);
|
||||
}
|
||||
if (gpio_cd != -1) {
|
||||
gpio_set_direction(gpio_cd, GPIO_MODE_INPUT);
|
||||
gpio_matrix_in(gpio_cd, pslot->card_detect, 0);
|
||||
}
|
||||
if (gpio_wp != -1) {
|
||||
gpio_set_direction(gpio_wp, GPIO_MODE_INPUT);
|
||||
gpio_matrix_in(gpio_wp, pslot->write_protect, 0);
|
||||
}
|
||||
// By default, set probing frequency (400kHz) and 1-bit bus
|
||||
esp_err_t ret = sdmmc_host_set_card_clk(slot, 400);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
ret = sdmmc_host_set_bus_width(slot, 1);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_host_deinit()
|
||||
{
|
||||
if (!s_intr_handle) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
esp_intr_free(s_intr_handle);
|
||||
s_intr_handle = NULL;
|
||||
vQueueDelete(s_event_queue);
|
||||
s_event_queue = NULL;
|
||||
sdmmc_host_input_clk_disable();
|
||||
sdmmc_host_transaction_handler_deinit();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_host_wait_for_event(int tick_count, sdmmc_event_t* out_event)
|
||||
{
|
||||
if (!out_event) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (!s_event_queue) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
int ret = xQueueReceive(s_event_queue, out_event, tick_count);
|
||||
if (ret == pdFALSE) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_host_set_bus_width(int slot, size_t width)
|
||||
{
|
||||
if (!(slot == 0 || slot == 1)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (s_slot_info[slot].width < width) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
const uint16_t mask = BIT(slot);
|
||||
if (width == 1) {
|
||||
SDMMC.ctype.card_width_8 &= ~mask;
|
||||
SDMMC.ctype.card_width &= ~mask;
|
||||
} else if (width == 4) {
|
||||
SDMMC.ctype.card_width_8 &= ~mask;
|
||||
SDMMC.ctype.card_width |= mask;
|
||||
} else if (width == 8){
|
||||
SDMMC.ctype.card_width_8 |= mask;
|
||||
} else {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
ESP_LOGD(TAG, "slot=%d width=%d", slot, width);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void sdmmc_host_dma_init()
|
||||
{
|
||||
SDMMC.ctrl.dma_enable = 1;
|
||||
SDMMC.bmod.val = 0;
|
||||
SDMMC.bmod.sw_reset = 1;
|
||||
SDMMC.idinten.ni = 1;
|
||||
SDMMC.idinten.ri = 1;
|
||||
SDMMC.idinten.ti = 1;
|
||||
}
|
||||
|
||||
|
||||
void sdmmc_host_dma_stop()
|
||||
{
|
||||
SDMMC.ctrl.use_internal_dma = 0;
|
||||
SDMMC.ctrl.dma_reset = 1;
|
||||
SDMMC.bmod.fb = 0;
|
||||
SDMMC.bmod.enable = 0;
|
||||
}
|
||||
|
||||
void sdmmc_host_dma_prepare(sdmmc_desc_t* desc, size_t block_size, size_t data_size)
|
||||
{
|
||||
// TODO: set timeout depending on data size
|
||||
SDMMC.tmout.val = 0xffffffff;
|
||||
|
||||
// Set size of data and DMA descriptor pointer
|
||||
SDMMC.bytcnt = data_size;
|
||||
SDMMC.blksiz = block_size;
|
||||
SDMMC.dbaddr = desc;
|
||||
|
||||
// Enable everything needed to use DMA
|
||||
SDMMC.ctrl.dma_enable = 1;
|
||||
SDMMC.ctrl.use_internal_dma = 1;
|
||||
SDMMC.bmod.enable = 1;
|
||||
SDMMC.bmod.fb = 1;
|
||||
sdmmc_host_dma_resume();
|
||||
}
|
||||
|
||||
void sdmmc_host_dma_resume()
|
||||
{
|
||||
SDMMC.pldmnd = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SDMMC interrupt handler
|
||||
*
|
||||
* Ignoring SDIO and streaming read/writes for now (and considering just SD memory cards),
|
||||
* all communication is driven by the master, and the hardware handles things like stop
|
||||
* commands automatically. So the interrupt handler doesn't need to do much, we just push
|
||||
* interrupt status into a queue, clear interrupt flags, and let the task currently doing
|
||||
* communication figure out what to do next.
|
||||
*
|
||||
* Card detect interrupts pose a small issue though, because if a card is plugged in and
|
||||
* out a few times, while there is no task to process the events, event queue can become
|
||||
* full and some card detect events may be dropped. We ignore this problem for now, since
|
||||
* the there are no other interesting events which can get lost due to this.
|
||||
*/
|
||||
static void sdmmc_isr(void* arg) {
|
||||
QueueHandle_t queue = (QueueHandle_t) arg;
|
||||
sdmmc_event_t event;
|
||||
uint32_t pending = SDMMC.mintsts.val;
|
||||
SDMMC.rintsts.val = pending;
|
||||
event.sdmmc_status = pending;
|
||||
|
||||
uint32_t dma_pending = SDMMC.idsts.val;
|
||||
SDMMC.idsts.val = dma_pending;
|
||||
event.dma_status = dma_pending & 0x1f;
|
||||
|
||||
int higher_priority_task_awoken = pdFALSE;
|
||||
xQueueSendFromISR(queue, &event, &higher_priority_task_awoken);
|
||||
if (higher_priority_task_awoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
44
components/driver/sdmmc_private.h
Normal file
44
components/driver/sdmmc_private.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "esp_err.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "soc/sdmmc_struct.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t sdmmc_status; ///< masked SDMMC interrupt status
|
||||
uint32_t dma_status; ///< masked DMA interrupt status
|
||||
} sdmmc_event_t;
|
||||
|
||||
void sdmmc_host_reset();
|
||||
|
||||
esp_err_t sdmmc_host_start_command(int slot, sdmmc_hw_cmd_t cmd, uint32_t arg);
|
||||
|
||||
esp_err_t sdmmc_host_wait_for_event(int tick_count, sdmmc_event_t* out_event);
|
||||
|
||||
void sdmmc_host_dma_prepare(sdmmc_desc_t* desc, size_t block_size, size_t data_size);
|
||||
|
||||
void sdmmc_host_dma_stop();
|
||||
|
||||
void sdmmc_host_dma_resume();
|
||||
|
||||
esp_err_t sdmmc_host_transaction_handler_init();
|
||||
|
||||
void sdmmc_host_transaction_handler_deinit();
|
||||
|
372
components/driver/sdmmc_transaction.c
Normal file
372
components/driver/sdmmc_transaction.c
Normal file
@@ -0,0 +1,372 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "soc/sdmmc_reg.h"
|
||||
#include "soc/sdmmc_struct.h"
|
||||
#include "driver/sdmmc_types.h"
|
||||
#include "driver/sdmmc_defs.h"
|
||||
#include "driver/sdmmc_host.h"
|
||||
#include "sdmmc_private.h"
|
||||
|
||||
|
||||
/* Number of DMA descriptors used for transfer.
|
||||
* Increasing this value above 4 doesn't improve performance for the usual case
|
||||
* of SD memory cards (most data transfers are multiples of 512 bytes).
|
||||
*/
|
||||
#define SDMMC_DMA_DESC_CNT 4
|
||||
|
||||
static const char* TAG = "sdmmc_req";
|
||||
|
||||
typedef enum {
|
||||
SDMMC_IDLE,
|
||||
SDMMC_SENDING_CMD,
|
||||
SDMMC_SENDING_DATA,
|
||||
SDMMC_BUSY,
|
||||
} sdmmc_req_state_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t* ptr;
|
||||
size_t size_remaining;
|
||||
size_t next_desc;
|
||||
size_t desc_remaining;
|
||||
} sdmmc_transfer_state_t;
|
||||
|
||||
const uint32_t SDMMC_DATA_ERR_MASK =
|
||||
SDMMC_INTMASK_DTO | SDMMC_INTMASK_DCRC |
|
||||
SDMMC_INTMASK_HTO | SDMMC_INTMASK_SBE |
|
||||
SDMMC_INTMASK_EBE;
|
||||
|
||||
const uint32_t SDMMC_DMA_DONE_MASK =
|
||||
SDMMC_IDMAC_INTMASK_RI | SDMMC_IDMAC_INTMASK_TI |
|
||||
SDMMC_IDMAC_INTMASK_NI;
|
||||
|
||||
const uint32_t SDMMC_CMD_ERR_MASK =
|
||||
SDMMC_INTMASK_RTO |
|
||||
SDMMC_INTMASK_RCRC |
|
||||
SDMMC_INTMASK_RESP_ERR;
|
||||
|
||||
static sdmmc_desc_t s_dma_desc[SDMMC_DMA_DESC_CNT];
|
||||
static sdmmc_transfer_state_t s_cur_transfer = { 0 };
|
||||
static QueueHandle_t s_request_mutex;
|
||||
|
||||
static esp_err_t handle_idle_state_events();
|
||||
static sdmmc_hw_cmd_t make_hw_cmd(sdmmc_command_t* cmd);
|
||||
static esp_err_t handle_event(sdmmc_command_t* cmd, sdmmc_req_state_t* pstate);
|
||||
static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd, sdmmc_req_state_t* pstate);
|
||||
static void process_command_response(uint32_t status, sdmmc_command_t* cmd);
|
||||
static void fill_dma_descriptors(size_t num_desc);
|
||||
|
||||
esp_err_t sdmmc_host_transaction_handler_init()
|
||||
{
|
||||
assert(s_request_mutex == NULL);
|
||||
s_request_mutex = xSemaphoreCreateMutex();
|
||||
if (!s_request_mutex) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void sdmmc_host_transaction_handler_deinit()
|
||||
{
|
||||
assert(s_request_mutex);
|
||||
vSemaphoreDelete(s_request_mutex);
|
||||
s_request_mutex = NULL;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo)
|
||||
{
|
||||
xSemaphoreTake(s_request_mutex, portMAX_DELAY);
|
||||
// dispose of any events which happened asynchronously
|
||||
handle_idle_state_events();
|
||||
// convert cmdinfo to hardware register value
|
||||
sdmmc_hw_cmd_t hw_cmd = make_hw_cmd(cmdinfo);
|
||||
if (cmdinfo->data) {
|
||||
// these constraints should be handled by upper layer
|
||||
assert(cmdinfo->datalen >= 4);
|
||||
assert(cmdinfo->blklen % 4 == 0);
|
||||
// this clears "owned by IDMAC" bits
|
||||
memset(s_dma_desc, 0, sizeof(s_dma_desc));
|
||||
// initialize first descriptor
|
||||
s_dma_desc[0].first_descriptor = 1;
|
||||
// save transfer info
|
||||
s_cur_transfer.ptr = (uint8_t*) cmdinfo->data;
|
||||
s_cur_transfer.size_remaining = cmdinfo->datalen;
|
||||
s_cur_transfer.next_desc = 0;
|
||||
s_cur_transfer.desc_remaining = (cmdinfo->datalen + SDMMC_DMA_MAX_BUF_LEN - 1) / SDMMC_DMA_MAX_BUF_LEN;
|
||||
// prepare descriptors
|
||||
fill_dma_descriptors(SDMMC_DMA_DESC_CNT);
|
||||
// write transfer info into hardware
|
||||
sdmmc_host_dma_prepare(&s_dma_desc[0], cmdinfo->blklen, cmdinfo->datalen);
|
||||
}
|
||||
// write command into hardware, this also sends the command to the card
|
||||
esp_err_t ret = sdmmc_host_start_command(slot, hw_cmd, cmdinfo->arg);
|
||||
if (ret != ESP_OK) {
|
||||
xSemaphoreGive(s_request_mutex);
|
||||
return ret;
|
||||
}
|
||||
// process events until transfer is complete
|
||||
cmdinfo->error = ESP_OK;
|
||||
sdmmc_req_state_t state = SDMMC_SENDING_CMD;
|
||||
while (state != SDMMC_IDLE) {
|
||||
ret = handle_event(cmdinfo, &state);
|
||||
if (ret != ESP_OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(s_request_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void fill_dma_descriptors(size_t num_desc)
|
||||
{
|
||||
for (size_t i = 0; i < num_desc; ++i) {
|
||||
if (s_cur_transfer.size_remaining == 0) {
|
||||
return;
|
||||
}
|
||||
const size_t next = s_cur_transfer.next_desc;
|
||||
sdmmc_desc_t* desc = &s_dma_desc[next];
|
||||
assert(!desc->owned_by_idmac);
|
||||
size_t size_to_fill =
|
||||
(s_cur_transfer.size_remaining < SDMMC_DMA_MAX_BUF_LEN) ?
|
||||
s_cur_transfer.size_remaining : SDMMC_DMA_MAX_BUF_LEN;
|
||||
bool last = size_to_fill == s_cur_transfer.size_remaining;
|
||||
desc->last_descriptor = last;
|
||||
desc->second_address_chained = 1;
|
||||
desc->owned_by_idmac = 1;
|
||||
desc->buffer1_ptr = s_cur_transfer.ptr;
|
||||
desc->next_desc_ptr = (last) ? NULL : &s_dma_desc[(next + 1) % SDMMC_DMA_DESC_CNT];
|
||||
desc->buffer1_size = size_to_fill;
|
||||
|
||||
s_cur_transfer.size_remaining -= size_to_fill;
|
||||
s_cur_transfer.ptr += size_to_fill;
|
||||
s_cur_transfer.next_desc = (s_cur_transfer.next_desc + 1) % SDMMC_DMA_DESC_CNT;
|
||||
ESP_LOGV(TAG, "fill %d desc=%d rem=%d next=%d last=%d sz=%d",
|
||||
num_desc, next, s_cur_transfer.size_remaining,
|
||||
s_cur_transfer.next_desc, desc->last_descriptor, desc->buffer1_size);
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t handle_idle_state_events()
|
||||
{
|
||||
/* Handle any events which have happened in between transfers.
|
||||
* Under current assumptions (no SDIO support) only card detect events
|
||||
* can happen in the idle state.
|
||||
*/
|
||||
sdmmc_event_t evt;
|
||||
while (sdmmc_host_wait_for_event(0, &evt) == ESP_OK) {
|
||||
if (evt.sdmmc_status & SDMMC_INTMASK_CD) {
|
||||
ESP_LOGV(TAG, "card detect event");
|
||||
evt.sdmmc_status &= ~SDMMC_INTMASK_CD;
|
||||
}
|
||||
if (evt.sdmmc_status != 0 || evt.dma_status != 0) {
|
||||
ESP_LOGE(TAG, "handle_idle_state_events unhandled: %08x %08x",
|
||||
evt.sdmmc_status, evt.dma_status);
|
||||
}
|
||||
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
static esp_err_t handle_event(sdmmc_command_t* cmd, sdmmc_req_state_t* state)
|
||||
{
|
||||
sdmmc_event_t evt;
|
||||
esp_err_t err = sdmmc_host_wait_for_event(portMAX_DELAY, &evt);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "sdmmc_host_wait_for_event returned %d", err);
|
||||
return err;
|
||||
}
|
||||
ESP_LOGV(TAG, "sdmmc_handle_event: evt %08x %08x", evt.sdmmc_status, evt.dma_status);
|
||||
process_events(evt, cmd, state);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static sdmmc_hw_cmd_t make_hw_cmd(sdmmc_command_t* cmd)
|
||||
{
|
||||
sdmmc_hw_cmd_t res = { 0 };
|
||||
|
||||
res.cmd_index = cmd->opcode;
|
||||
if (cmd->opcode == MMC_STOP_TRANSMISSION) {
|
||||
res.stop_abort_cmd = 1;
|
||||
} else {
|
||||
res.wait_complete = 1;
|
||||
}
|
||||
if (cmd->opcode == SD_APP_SET_BUS_WIDTH) {
|
||||
res.send_auto_stop = 1;
|
||||
res.data_expected = 1;
|
||||
}
|
||||
if (cmd->flags & SCF_RSP_PRESENT) {
|
||||
res.response_expect = 1;
|
||||
if (cmd->flags & SCF_RSP_136) {
|
||||
res.response_long = 1;
|
||||
}
|
||||
}
|
||||
if (cmd->flags & SCF_RSP_CRC) {
|
||||
res.check_response_crc = 1;
|
||||
}
|
||||
res.use_hold_reg = 1;
|
||||
if (cmd->data) {
|
||||
res.data_expected = 1;
|
||||
if ((cmd->flags & SCF_CMD_READ) == 0) {
|
||||
res.rw = 1;
|
||||
}
|
||||
assert(cmd->datalen % cmd->blklen == 0);
|
||||
if ((cmd->datalen / cmd->blklen) > 1) {
|
||||
res.send_auto_stop = 1;
|
||||
}
|
||||
}
|
||||
ESP_LOGV(TAG, "%s: opcode=%d, rexp=%d, crc=%d", __func__,
|
||||
res.cmd_index, res.response_expect, res.check_response_crc);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void process_command_response(uint32_t status, sdmmc_command_t* cmd)
|
||||
{
|
||||
if (cmd->flags & SCF_RSP_PRESENT) {
|
||||
if (cmd->flags & SCF_RSP_136) {
|
||||
cmd->response[3] = SDMMC.resp[0];
|
||||
cmd->response[2] = SDMMC.resp[1];
|
||||
cmd->response[1] = SDMMC.resp[2];
|
||||
cmd->response[0] = SDMMC.resp[3];
|
||||
|
||||
} else {
|
||||
cmd->response[0] = SDMMC.resp[0];
|
||||
cmd->response[1] = 0;
|
||||
cmd->response[2] = 0;
|
||||
cmd->response[3] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ((status & SDMMC_INTMASK_RTO) &&
|
||||
cmd->opcode != MMC_ALL_SEND_CID &&
|
||||
cmd->opcode != MMC_SELECT_CARD &&
|
||||
cmd->opcode != MMC_STOP_TRANSMISSION) {
|
||||
cmd->error = ESP_ERR_TIMEOUT;
|
||||
} else if ((cmd->flags & SCF_RSP_CRC) && (status & SDMMC_INTMASK_RCRC)) {
|
||||
cmd->error = ESP_ERR_INVALID_CRC;
|
||||
} else if (status & SDMMC_INTMASK_RESP_ERR) {
|
||||
cmd->error = ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
if (cmd->error != 0) {
|
||||
if (cmd->data) {
|
||||
sdmmc_host_dma_stop();
|
||||
}
|
||||
ESP_LOGD(TAG, "%s: error %d", __func__, cmd->error);
|
||||
}
|
||||
}
|
||||
|
||||
static void process_data_status(uint32_t status, sdmmc_command_t* cmd)
|
||||
{
|
||||
if (status & SDMMC_DATA_ERR_MASK) {
|
||||
if (status & SDMMC_INTMASK_DTO) {
|
||||
cmd->error = ESP_ERR_TIMEOUT;
|
||||
} else if (status & SDMMC_INTMASK_DCRC) {
|
||||
cmd->error = ESP_ERR_INVALID_CRC;
|
||||
} else if ((status & SDMMC_INTMASK_EBE) &&
|
||||
(cmd->flags & SCF_CMD_READ) == 0) {
|
||||
cmd->error = ESP_ERR_TIMEOUT;
|
||||
} else {
|
||||
cmd->error = ESP_FAIL;
|
||||
}
|
||||
SDMMC.ctrl.fifo_reset = 1;
|
||||
}
|
||||
if (cmd->error != 0) {
|
||||
if (cmd->data) {
|
||||
sdmmc_host_dma_stop();
|
||||
}
|
||||
ESP_LOGD(TAG, "%s: error %d", __func__, cmd->error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static inline bool mask_check_and_clear(uint32_t* state, uint32_t mask) {
|
||||
bool ret = ((*state) & mask) != 0;
|
||||
*state &= ~mask;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd, sdmmc_req_state_t* pstate)
|
||||
{
|
||||
const char* const s_state_names[] __attribute__((unused)) = {
|
||||
"IDLE",
|
||||
"SENDING_CMD",
|
||||
"SENDIND_DATA",
|
||||
"BUSY"
|
||||
};
|
||||
sdmmc_event_t orig_evt = evt;
|
||||
ESP_LOGV(TAG, "%s: state=%s", __func__, s_state_names[*pstate]);
|
||||
sdmmc_req_state_t next_state = *pstate;
|
||||
sdmmc_req_state_t state = (sdmmc_req_state_t) -1;
|
||||
while (next_state != state) {
|
||||
state = next_state;
|
||||
switch (state) {
|
||||
case SDMMC_IDLE:
|
||||
break;
|
||||
|
||||
case SDMMC_SENDING_CMD:
|
||||
if (mask_check_and_clear(&evt.sdmmc_status, SDMMC_CMD_ERR_MASK)) {
|
||||
process_command_response(orig_evt.sdmmc_status, cmd);
|
||||
break;
|
||||
}
|
||||
if (!mask_check_and_clear(&evt.sdmmc_status, SDMMC_INTMASK_CMD_DONE)) {
|
||||
break;
|
||||
}
|
||||
process_command_response(orig_evt.sdmmc_status, cmd);
|
||||
if (cmd->error != ESP_OK || cmd->data == NULL) {
|
||||
next_state = SDMMC_IDLE;
|
||||
break;
|
||||
}
|
||||
next_state = SDMMC_SENDING_DATA;
|
||||
break;
|
||||
|
||||
|
||||
case SDMMC_SENDING_DATA:
|
||||
if (mask_check_and_clear(&evt.sdmmc_status, SDMMC_DATA_ERR_MASK)) {
|
||||
process_data_status(orig_evt.sdmmc_status, cmd);
|
||||
sdmmc_host_dma_stop();
|
||||
}
|
||||
if (mask_check_and_clear(&evt.dma_status, SDMMC_DMA_DONE_MASK)) {
|
||||
s_cur_transfer.desc_remaining--;
|
||||
if (s_cur_transfer.size_remaining) {
|
||||
fill_dma_descriptors(1);
|
||||
sdmmc_host_dma_resume();
|
||||
}
|
||||
if (s_cur_transfer.desc_remaining == 0) {
|
||||
next_state = SDMMC_BUSY;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SDMMC_BUSY:
|
||||
if (!mask_check_and_clear(&evt.sdmmc_status, SDMMC_INTMASK_DATA_OVER)) {
|
||||
break;
|
||||
}
|
||||
process_data_status(orig_evt.sdmmc_status, cmd);
|
||||
next_state = SDMMC_IDLE;
|
||||
break;
|
||||
}
|
||||
ESP_LOGV(TAG, "%s state=%s next_state=%s", __func__, s_state_names[state], s_state_names[next_state]);
|
||||
}
|
||||
*pstate = state;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -35,7 +35,8 @@ typedef int32_t esp_err_t;
|
||||
#define ESP_ERR_NOT_FOUND 0x105
|
||||
#define ESP_ERR_NOT_SUPPORTED 0x106
|
||||
#define ESP_ERR_TIMEOUT 0x107
|
||||
|
||||
#define ESP_ERR_INVALID_RESPONSE 0x108
|
||||
#define ESP_ERR_INVALID_CRC 0x109
|
||||
|
||||
#define ESP_ERR_WIFI_BASE 0x3000 /*!< Starting number of WiFi error codes */
|
||||
|
||||
|
94
components/esp32/include/soc/sdmmc_reg.h
Normal file
94
components/esp32/include/soc/sdmmc_reg.h
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef _SOC_SDMMC_REG_H_
|
||||
#define _SOC_SDMMC_REG_H_
|
||||
#include "soc.h"
|
||||
|
||||
#define SDMMC_CTRL_REG (DR_REG_SDMMC_BASE + 0x00)
|
||||
#define SDMMC_PWREN_REG (DR_REG_SDMMC_BASE + 0x04)
|
||||
#define SDMMC_CLKDIV_REG (DR_REG_SDMMC_BASE + 0x08)
|
||||
#define SDMMC_CLKSRC_REG (DR_REG_SDMMC_BASE + 0x0c)
|
||||
#define SDMMC_CLKENA_REG (DR_REG_SDMMC_BASE + 0x10)
|
||||
#define SDMMC_TMOUT_REG (DR_REG_SDMMC_BASE + 0x14)
|
||||
#define SDMMC_CTYPE_REG (DR_REG_SDMMC_BASE + 0x18)
|
||||
#define SDMMC_BLKSIZ_REG (DR_REG_SDMMC_BASE + 0x1c)
|
||||
#define SDMMC_BYTCNT_REG (DR_REG_SDMMC_BASE + 0x20)
|
||||
#define SDMMC_INTMASK_REG (DR_REG_SDMMC_BASE + 0x24)
|
||||
#define SDMMC_CMDARG_REG (DR_REG_SDMMC_BASE + 0x28)
|
||||
#define SDMMC_CMD_REG (DR_REG_SDMMC_BASE + 0x2c)
|
||||
#define SDMMC_RESP0_REG (DR_REG_SDMMC_BASE + 0x30)
|
||||
#define SDMMC_RESP1_REG (DR_REG_SDMMC_BASE + 0x34)
|
||||
#define SDMMC_RESP2_REG (DR_REG_SDMMC_BASE + 0x38)
|
||||
#define SDMMC_RESP3_REG (DR_REG_SDMMC_BASE + 0x3c)
|
||||
|
||||
#define SDMMC_MINTSTS_REG (DR_REG_SDMMC_BASE + 0x40)
|
||||
#define SDMMC_RINTSTS_REG (DR_REG_SDMMC_BASE + 0x44)
|
||||
#define SDMMC_STATUS_REG (DR_REG_SDMMC_BASE + 0x48)
|
||||
#define SDMMC_FIFOTH_REG (DR_REG_SDMMC_BASE + 0x4c)
|
||||
#define SDMMC_CDETECT_REG (DR_REG_SDMMC_BASE + 0x50)
|
||||
#define SDMMC_WRTPRT_REG (DR_REG_SDMMC_BASE + 0x54)
|
||||
#define SDMMC_GPIO_REG (DR_REG_SDMMC_BASE + 0x58)
|
||||
#define SDMMC_TCBCNT_REG (DR_REG_SDMMC_BASE + 0x5c)
|
||||
#define SDMMC_TBBCNT_REG (DR_REG_SDMMC_BASE + 0x60)
|
||||
#define SDMMC_DEBNCE_REG (DR_REG_SDMMC_BASE + 0x64)
|
||||
#define SDMMC_USRID_REG (DR_REG_SDMMC_BASE + 0x68)
|
||||
#define SDMMC_VERID_REG (DR_REG_SDMMC_BASE + 0x6c)
|
||||
#define SDMMC_HCON_REG (DR_REG_SDMMC_BASE + 0x70)
|
||||
#define SDMMC_UHS_REG_REG (DR_REG_SDMMC_BASE + 0x74)
|
||||
#define SDMMC_RST_N_REG (DR_REG_SDMMC_BASE + 0x78)
|
||||
#define SDMMC_BMOD_REG (DR_REG_SDMMC_BASE + 0x80)
|
||||
#define SDMMC_PLDMND_REG (DR_REG_SDMMC_BASE + 0x84)
|
||||
#define SDMMC_DBADDR_REG (DR_REG_SDMMC_BASE + 0x88)
|
||||
#define SDMMC_DBADDRU_REG (DR_REG_SDMMC_BASE + 0x8c)
|
||||
#define SDMMC_IDSTS_REG (DR_REG_SDMMC_BASE + 0x8c)
|
||||
#define SDMMC_IDINTEN_REG (DR_REG_SDMMC_BASE + 0x90)
|
||||
#define SDMMC_DSCADDR_REG (DR_REG_SDMMC_BASE + 0x94)
|
||||
#define SDMMC_DSCADDRL_REG (DR_REG_SDMMC_BASE + 0x98)
|
||||
#define SDMMC_DSCADDRU_REG (DR_REG_SDMMC_BASE + 0x9c)
|
||||
#define SDMMC_BUFADDRL_REG (DR_REG_SDMMC_BASE + 0xa0)
|
||||
#define SDMMC_BUFADDRU_REG (DR_REG_SDMMC_BASE + 0xa4)
|
||||
#define SDMMC_CARDTHRCTL_REG (DR_REG_SDMMC_BASE + 0x100)
|
||||
#define SDMMC_BACK_END_POWER_REG (DR_REG_SDMMC_BASE + 0x104)
|
||||
#define SDMMC_UHS_REG_EXT_REG (DR_REG_SDMMC_BASE + 0x108)
|
||||
#define SDMMC_EMMC_DDR_REG_REG (DR_REG_SDMMC_BASE + 0x10c)
|
||||
#define SDMMC_ENABLE_SHIFT_REG (DR_REG_SDMMC_BASE + 0x110)
|
||||
|
||||
#define SDMMC_CLOCK_REG (DR_REG_SDMMC_BASE + 0x800)
|
||||
|
||||
#define SDMMC_INTMASK_EBE BIT(15)
|
||||
#define SDMMC_INTMASK_ACD BIT(14)
|
||||
#define SDMMC_INTMASK_SBE BIT(13)
|
||||
#define SDMMC_INTMASK_HLE BIT(12)
|
||||
#define SDMMC_INTMASK_FRUN BIT(11)
|
||||
#define SDMMC_INTMASK_HTO BIT(10)
|
||||
#define SDMMC_INTMASK_DTO BIT(9)
|
||||
#define SDMMC_INTMASK_RTO BIT(8)
|
||||
#define SDMMC_INTMASK_DCRC BIT(7)
|
||||
#define SDMMC_INTMASK_RCRC BIT(6)
|
||||
#define SDMMC_INTMASK_RXDR BIT(5)
|
||||
#define SDMMC_INTMASK_TXDR BIT(4)
|
||||
#define SDMMC_INTMASK_DATA_OVER BIT(3)
|
||||
#define SDMMC_INTMASK_CMD_DONE BIT(2)
|
||||
#define SDMMC_INTMASK_RESP_ERR BIT(1)
|
||||
#define SDMMC_INTMASK_CD BIT(0)
|
||||
|
||||
#define SDMMC_IDMAC_INTMASK_AI BIT(9)
|
||||
#define SDMMC_IDMAC_INTMASK_NI BIT(8)
|
||||
#define SDMMC_IDMAC_INTMASK_CES BIT(5)
|
||||
#define SDMMC_IDMAC_INTMASK_DU BIT(4)
|
||||
#define SDMMC_IDMAC_INTMASK_FBE BIT(2)
|
||||
#define SDMMC_IDMAC_INTMASK_RI BIT(1)
|
||||
#define SDMMC_IDMAC_INTMASK_TI BIT(0)
|
||||
|
||||
#endif /* _SOC_SDMMC_REG_H_ */
|
371
components/esp32/include/soc/sdmmc_struct.h
Normal file
371
components/esp32/include/soc/sdmmc_struct.h
Normal file
@@ -0,0 +1,371 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef _SOC_SDMMC_STRUCT_H_
|
||||
#define _SOC_SDMMC_STRUCT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
uint32_t reserved1: 1;
|
||||
uint32_t disable_int_on_completion: 1;
|
||||
uint32_t last_descriptor: 1;
|
||||
uint32_t first_descriptor: 1;
|
||||
uint32_t second_address_chained: 1;
|
||||
uint32_t end_of_ring: 1;
|
||||
uint32_t reserved2: 24;
|
||||
uint32_t card_error_summary: 1;
|
||||
uint32_t owned_by_idmac: 1;
|
||||
uint32_t buffer1_size: 13;
|
||||
uint32_t buffer2_size: 13;
|
||||
uint32_t reserved3: 6;
|
||||
void* buffer1_ptr;
|
||||
union {
|
||||
void* buffer2_ptr;
|
||||
void* next_desc_ptr;
|
||||
};
|
||||
} sdmmc_desc_t;
|
||||
|
||||
#define SDMMC_DMA_MAX_BUF_LEN 4096
|
||||
|
||||
_Static_assert(sizeof(sdmmc_desc_t) == 16, "invalid size of sdmmc_desc_t structure");
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint32_t cmd_index: 6; ///< Command index
|
||||
uint32_t response_expect: 1; ///< set if response is expected
|
||||
uint32_t response_long: 1; ///< 0: short response expected, 1: long response expected
|
||||
uint32_t check_response_crc: 1; ///< set if controller should check response CRC
|
||||
uint32_t data_expected: 1; ///< 0: no data expected, 1: data expected
|
||||
uint32_t rw: 1; ///< 0: read from card, 1: write to card (don't care if no data expected)
|
||||
uint32_t stream_mode: 1; ///< 0: block transfer, 1: stream transfer (don't care if no data expected)
|
||||
uint32_t send_auto_stop: 1; ///< set to send stop at the end of the transfer
|
||||
uint32_t wait_complete: 1; ///< 0: send command at once, 1: wait for previous command to complete
|
||||
uint32_t stop_abort_cmd: 1; ///< set if this is a stop or abort command intended to stop current transfer
|
||||
uint32_t send_init: 1; ///< set to send init sequence (80 clocks of 1)
|
||||
uint32_t card_num: 5; ///< card number
|
||||
uint32_t update_clk_reg: 1; ///< 0: normal command, 1: don't send command, just update clock registers
|
||||
uint32_t read_ceata: 1; ///< set if performing read from CE-ATA device
|
||||
uint32_t ccs_expected: 1; ///< set if CCS is expected from CE-ATA device
|
||||
uint32_t enable_boot: 1; ///< set for mandatory boot mode
|
||||
uint32_t expect_boot_ack: 1; ///< when set along with enable_boot, controller expects boot ack pattern
|
||||
uint32_t disable_boot: 1; ///< set to terminate boot operation (don't set along with enable_boot)
|
||||
uint32_t boot_mode: 1; ///< 0: mandatory boot operation, 1: alternate boot operation
|
||||
uint32_t volt_switch: 1; ///< set to enable voltage switching (for CMD11 only)
|
||||
uint32_t use_hold_reg: 1; ///< clear to bypass HOLD register
|
||||
uint32_t reserved: 1;
|
||||
uint32_t start_command: 1; ///< Start command; once command is sent to the card, bit is cleared.
|
||||
} sdmmc_hw_cmd_t; ///< command format used in cmd register; this structure is defined to make it easier to build command values
|
||||
|
||||
_Static_assert(sizeof(sdmmc_hw_cmd_t) == 4, "invalid size of sdmmc_cmd_t structure");
|
||||
|
||||
|
||||
typedef volatile struct {
|
||||
union {
|
||||
struct {
|
||||
uint32_t controller_reset: 1;
|
||||
uint32_t fifo_reset: 1;
|
||||
uint32_t dma_reset: 1;
|
||||
uint32_t reserved1: 1;
|
||||
uint32_t int_enable: 1;
|
||||
uint32_t dma_enable: 1;
|
||||
uint32_t read_wait: 1;
|
||||
uint32_t send_irq_response: 1;
|
||||
uint32_t abort_read_data: 1;
|
||||
uint32_t send_ccsd: 1;
|
||||
uint32_t send_auto_stop_ccsd: 1;
|
||||
uint32_t ceata_device_interrupt_status: 1;
|
||||
uint32_t reserved2: 4;
|
||||
uint32_t card_voltage_a: 4;
|
||||
uint32_t card_voltage_b: 4;
|
||||
uint32_t enable_od_pullup: 1;
|
||||
uint32_t use_internal_dma: 1;
|
||||
uint32_t reserved3: 6;
|
||||
};
|
||||
uint32_t val;
|
||||
} ctrl;
|
||||
|
||||
uint32_t pwren; ///< 1: enable power to card, 0: disable power to card
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t div0: 8; ///< 0: bypass, 1-255: divide clock by (2*div0).
|
||||
uint32_t div1: 8; ///< 0: bypass, 1-255: divide clock by (2*div0).
|
||||
uint32_t div2: 8; ///< 0: bypass, 1-255: divide clock by (2*div0).
|
||||
uint32_t div3: 8; ///< 0: bypass, 1-255: divide clock by (2*div0).
|
||||
};
|
||||
uint32_t val;
|
||||
} clkdiv;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t card0: 2; ///< 0-3: select clock divider for card 0 among div0-div3
|
||||
uint32_t card1: 2; ///< 0-3: select clock divider for card 1 among div0-div3
|
||||
uint32_t reserved: 28;
|
||||
};
|
||||
uint32_t val;
|
||||
} clksrc;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t cclk_enable: 16; ///< 1: enable clock to card, 0: disable clock
|
||||
uint32_t cclk_low_power: 16; ///< 1: enable clock gating when card is idle, 0: disable clock gating
|
||||
};
|
||||
uint32_t val;
|
||||
} clkena;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t response: 8; ///< response timeout, in card output clock cycles
|
||||
uint32_t data: 24; ///< data read timeout, in card output clock cycles
|
||||
};
|
||||
uint32_t val;
|
||||
} tmout;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t card_width: 16; ///< one bit for each card: 0: 1-bit mode, 1: 4-bit mode
|
||||
uint32_t card_width_8: 16; ///< one bit for each card: 0: not 8-bit mode (corresponding card_width bit is used), 1: 8-bit mode (card_width bit is ignored)
|
||||
};
|
||||
uint32_t val;
|
||||
} ctype;
|
||||
|
||||
uint32_t blksiz: 16; ///< block size, default 0x200
|
||||
uint32_t : 16;
|
||||
|
||||
uint32_t bytcnt; ///< number of bytes to be transferred
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t cd: 1; ///< Card detect interrupt enable
|
||||
uint32_t re: 1; ///< Response error interrupt enable
|
||||
uint32_t cmd_done: 1; ///< Command done interrupt enable
|
||||
uint32_t dto: 1; ///< Data transfer over interrupt enable
|
||||
uint32_t txdr: 1; ///< Transmit FIFO data request interrupt enable
|
||||
uint32_t rxdr: 1; ///< Receive FIFO data request interrupt enable
|
||||
uint32_t rcrc: 1; ///< Response CRC error interrupt enable
|
||||
uint32_t dcrc: 1; ///< Data CRC error interrupt enable
|
||||
uint32_t rto: 1; ///< Response timeout interrupt enable
|
||||
uint32_t drto: 1; ///< Data read timeout interrupt enable
|
||||
uint32_t hto: 1; ///< Data starvation-by-host timeout interrupt enable
|
||||
uint32_t frun: 1; ///< FIFO underrun/overrun error interrupt enable
|
||||
uint32_t hle: 1; ///< Hardware locked write error interrupt enable
|
||||
uint32_t sbi_bci: 1; ///< Start bit error / busy clear interrupt enable
|
||||
uint32_t acd: 1; ///< Auto command done interrupt enable
|
||||
uint32_t ebe: 1; ///< End bit error / write no CRC interrupt enable
|
||||
uint32_t sdio: 16; ///< SDIO interrupt enable
|
||||
};
|
||||
uint32_t val;
|
||||
} intmask;
|
||||
|
||||
uint32_t cmdarg; ///< Command argument to be passed to card
|
||||
|
||||
sdmmc_hw_cmd_t cmd;
|
||||
|
||||
uint32_t resp[4]; ///< Response from card
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t cd: 1; ///< Card detect interrupt masked status
|
||||
uint32_t re: 1; ///< Response error interrupt masked status
|
||||
uint32_t cmd_done: 1; ///< Command done interrupt masked status
|
||||
uint32_t dto: 1; ///< Data transfer over interrupt masked status
|
||||
uint32_t txdr: 1; ///< Transmit FIFO data request interrupt masked status
|
||||
uint32_t rxdr: 1; ///< Receive FIFO data request interrupt masked status
|
||||
uint32_t rcrc: 1; ///< Response CRC error interrupt masked status
|
||||
uint32_t dcrc: 1; ///< Data CRC error interrupt masked status
|
||||
uint32_t rto: 1; ///< Response timeout interrupt masked status
|
||||
uint32_t drto: 1; ///< Data read timeout interrupt masked status
|
||||
uint32_t hto: 1; ///< Data starvation-by-host timeout interrupt masked status
|
||||
uint32_t frun: 1; ///< FIFO underrun/overrun error interrupt masked status
|
||||
uint32_t hle: 1; ///< Hardware locked write error interrupt masked status
|
||||
uint32_t sbi_bci: 1; ///< Start bit error / busy clear interrupt masked status
|
||||
uint32_t acd: 1; ///< Auto command done interrupt masked status
|
||||
uint32_t ebe: 1; ///< End bit error / write no CRC interrupt masked status
|
||||
uint32_t sdio: 16; ///< SDIO interrupt masked status
|
||||
};
|
||||
uint32_t val;
|
||||
} mintsts;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t cd: 1; ///< Card detect raw interrupt status
|
||||
uint32_t re: 1; ///< Response error raw interrupt status
|
||||
uint32_t cmd_done: 1; ///< Command done raw interrupt status
|
||||
uint32_t dto: 1; ///< Data transfer over raw interrupt status
|
||||
uint32_t txdr: 1; ///< Transmit FIFO data request raw interrupt status
|
||||
uint32_t rxdr: 1; ///< Receive FIFO data request raw interrupt status
|
||||
uint32_t rcrc: 1; ///< Response CRC error raw interrupt status
|
||||
uint32_t dcrc: 1; ///< Data CRC error raw interrupt status
|
||||
uint32_t rto: 1; ///< Response timeout raw interrupt status
|
||||
uint32_t drto: 1; ///< Data read timeout raw interrupt status
|
||||
uint32_t hto: 1; ///< Data starvation-by-host timeout raw interrupt status
|
||||
uint32_t frun: 1; ///< FIFO underrun/overrun error raw interrupt status
|
||||
uint32_t hle: 1; ///< Hardware locked write error raw interrupt status
|
||||
uint32_t sbi_bci: 1; ///< Start bit error / busy clear raw interrupt status
|
||||
uint32_t acd: 1; ///< Auto command done raw interrupt status
|
||||
uint32_t ebe: 1; ///< End bit error / write no CRC raw interrupt status
|
||||
uint32_t sdio: 16; ///< SDIO raw interrupt status
|
||||
};
|
||||
uint32_t val;
|
||||
} rintsts; ///< interrupts can be cleared by writing this register
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t fifo_rx_watermark: 1; ///< FIFO reached receive watermark level
|
||||
uint32_t fifo_tx_watermark: 1; ///< FIFO reached transmit watermark level
|
||||
uint32_t fifo_empty: 1; ///< FIFO is empty
|
||||
uint32_t fifo_full: 1; ///< FIFO is full
|
||||
uint32_t cmd_fsm_state: 4; ///< command FSM state
|
||||
uint32_t data3_status: 1; ///< this bit reads 1 if card is present
|
||||
uint32_t data_busy: 1; ///< this bit reads 1 if card is busy
|
||||
uint32_t data_fsm_busy: 1; ///< this bit reads 1 if transmit/receive FSM is busy
|
||||
uint32_t response_index: 6; ///< index of the previous response
|
||||
uint32_t fifo_count: 13; ///< number of filled locations in the FIFO
|
||||
uint32_t dma_ack: 1; ///< DMA acknowledge signal
|
||||
uint32_t dma_req: 1; ///< DMA request signal
|
||||
};
|
||||
uint32_t val;
|
||||
} status;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t tx_watermark: 12; ///< FIFO TX watermark level
|
||||
uint32_t reserved1: 4;
|
||||
uint32_t rx_watermark: 12; ///< FIFO RX watermark level
|
||||
uint32_t dw_dma_mts: 3;
|
||||
uint32_t reserved2: 1;
|
||||
};
|
||||
uint32_t val;
|
||||
} fifoth;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t cards: 2; ///< bit N reads 1 if card N is present
|
||||
uint32_t reserved: 30;
|
||||
};
|
||||
uint32_t val;
|
||||
} cdetect;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t card0: 2; ///< bit N reads 1 if card N is write protected
|
||||
uint32_t reserved: 30;
|
||||
};
|
||||
uint32_t val;
|
||||
} wrtprt;
|
||||
|
||||
uint32_t gpio; ///< unused
|
||||
uint32_t tcbcnt; ///< transferred (to card) byte count
|
||||
uint32_t tbbcnt; ///< transferred from host to FIFO byte count
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t debounce_count: 24; ///< number of host cycles used by debounce filter, typical time should be 5-25ms
|
||||
uint32_t reserved: 8;
|
||||
};
|
||||
} debnce;
|
||||
|
||||
uint32_t usrid; ///< user ID
|
||||
uint32_t verid; ///< IP block version
|
||||
uint32_t hcon; ///< compile-time IP configuration
|
||||
uint32_t uhs; ///< TBD
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t cards: 2; ///< bit N resets card N, active low
|
||||
uint32_t reserved: 30;
|
||||
};
|
||||
} rst_n;
|
||||
|
||||
uint32_t reserved_7c;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t sw_reset: 1; ///< set to reset DMA controller
|
||||
uint32_t fb: 1; ///< set if AHB master performs fixed burst transfers
|
||||
uint32_t dsl: 5; ///< descriptor skip length: number of words to skip between two unchained descriptors
|
||||
uint32_t enable: 1; ///< set to enable IDMAC
|
||||
uint32_t pbl: 3; ///< programmable burst length
|
||||
uint32_t reserved: 21;
|
||||
};
|
||||
uint32_t val;
|
||||
} bmod;
|
||||
|
||||
uint32_t pldmnd; ///< set any bit to resume IDMAC FSM from suspended state
|
||||
sdmmc_desc_t* dbaddr; ///< descriptor list base
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t ti: 1; ///< transmit interrupt status
|
||||
uint32_t ri: 1; ///< receive interrupt status
|
||||
uint32_t fbe: 1; ///< fatal bus error
|
||||
uint32_t reserved1: 1;
|
||||
uint32_t du: 1; ///< descriptor unavailable
|
||||
uint32_t ces: 1; ///< card error summary
|
||||
uint32_t reserved2: 2;
|
||||
uint32_t nis: 1; ///< normal interrupt summary
|
||||
uint32_t fbe_code: 3; ///< code of fatal bus error
|
||||
uint32_t fsm: 4; ///< DMAC FSM state
|
||||
uint32_t reserved3: 15;
|
||||
};
|
||||
uint32_t val;
|
||||
} idsts;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t ti: 1; ///< transmit interrupt enable
|
||||
uint32_t ri: 1; ///< receive interrupt enable
|
||||
uint32_t fbe: 1; ///< fatal bus error interrupt enable
|
||||
uint32_t reserved1: 1;
|
||||
uint32_t du: 1; ///< descriptor unavailable interrupt enable
|
||||
uint32_t ces: 1; ///< card error interrupt enable
|
||||
uint32_t reserved2: 2;
|
||||
uint32_t ni: 1; ///< normal interrupt interrupt enable
|
||||
uint32_t ai: 1; ///< abnormal interrupt enable
|
||||
uint32_t reserved3: 22;
|
||||
};
|
||||
uint32_t val;
|
||||
} idinten;
|
||||
|
||||
uint32_t dscaddr; ///< current host descriptor address
|
||||
uint32_t dscaddrl; ///< unused
|
||||
uint32_t dscaddru; ///< unused
|
||||
uint32_t bufaddrl; ///< unused
|
||||
uint32_t bufaddru; ///< unused
|
||||
uint32_t reserved_a8[22];
|
||||
uint32_t cardthrctl;
|
||||
uint32_t back_end_power;
|
||||
uint32_t uhs_reg_ext;
|
||||
uint32_t emmc_ddr_reg;
|
||||
uint32_t enable_shift;
|
||||
uint32_t reserved_114[443];
|
||||
union {
|
||||
struct {
|
||||
uint32_t phase_dout: 3; ///< phase of data output clock (0x0: 0, 0x1: 90, 0x4: 180, 0x6: 270)
|
||||
uint32_t phase_din: 3; ///< phase of data input clock
|
||||
uint32_t phase_core: 3; ///< phase of the clock to SDMMC peripheral
|
||||
uint32_t div_factor_p: 4; ///< controls clock period; it will be (div_factor_p + 1) / 160MHz
|
||||
uint32_t div_factor_h: 4; ///< controls length of high pulse; it will be (div_factor_h + 1) / 160MHz
|
||||
uint32_t div_factor_m: 4; ///< should be equal to div_factor_p
|
||||
};
|
||||
uint32_t val;
|
||||
} clock;
|
||||
} sdmmc_dev_t;
|
||||
extern sdmmc_dev_t SDMMC;
|
||||
|
||||
_Static_assert(sizeof(sdmmc_dev_t) == 0x804, "invalid size of sdmmc_dev_t structure");
|
||||
|
||||
|
||||
|
||||
#endif //_SOC_SDMMC_STRUCT_H_
|
@@ -153,7 +153,7 @@
|
||||
#define DR_REG_FRC_TIMER_BASE 0x3ff47000
|
||||
#define DR_REG_RTCCNTL_BASE 0x3ff48000
|
||||
#define DR_REG_RTCIO_BASE 0x3ff48400
|
||||
#define DR_REG_SENS_BASE 0x3ff48800
|
||||
#define DR_REG_SENS_BASE 0x3ff48800
|
||||
#define DR_REG_IO_MUX_BASE 0x3ff49000
|
||||
#define DR_REG_RTCMEM0_BASE 0x3ff61000
|
||||
#define DR_REG_RTCMEM1_BASE 0x3ff62000
|
||||
|
@@ -19,3 +19,4 @@ PROVIDE ( SPI3 = 0x3ff65000 );
|
||||
PROVIDE ( I2C1 = 0x3ff67000 );
|
||||
PROVIDE ( I2S1 = 0x3ff6D000 );
|
||||
PROVIDE ( UART2 = 0x3ff6E000 );
|
||||
PROVIDE ( SDMMC = 0x3ff68000 );
|
||||
|
0
components/sdmmc/component.mk
Executable file
0
components/sdmmc/component.mk
Executable file
77
components/sdmmc/include/sdmmc_cmd.h
Normal file
77
components/sdmmc/include/sdmmc_cmd.h
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/sdmmc_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Probe and initialize SD/MMC card using given host
|
||||
*
|
||||
* @note Only SD cards (SDSC and SDHC/SDXC) are supported now.
|
||||
* Support for MMC/eMMC cards will be added later.
|
||||
*
|
||||
* @param host pointer to structure defining host controller
|
||||
* @param out_card pointer to structure which will receive information about the card when the function completes
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_card_init(const sdmmc_host_t* host,
|
||||
sdmmc_card_t* out_card);
|
||||
|
||||
/**
|
||||
* @brief Print information about the card to a stream
|
||||
* @param stream stream obtained using fopen or fdopen
|
||||
* @param card card information structure initialized using sdmmc_card_init
|
||||
*/
|
||||
void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card);
|
||||
|
||||
/**
|
||||
* Write given number of sectors to SD/MMC card
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized using sdmmc_card_init
|
||||
* @param src pointer to data buffer to read data from; data size must be equal to sector_count * card->csd.sector_size
|
||||
* @param start_sector sector where to start writing
|
||||
* @param sector_count number of sectors to write
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src,
|
||||
size_t start_sector, size_t sector_count);
|
||||
|
||||
/**
|
||||
* Write given number of sectors to SD/MMC card
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized using sdmmc_card_init
|
||||
* @param dst pointer to data buffer to write into; buffer size must be at least sector_count * card->csd.sector_size
|
||||
* @param start_sector sector where to start reading
|
||||
* @param sector_count number of sectors to read
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_read_sectors(sdmmc_card_t* card, void* dst,
|
||||
size_t start_sector, size_t sector_count);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
571
components/sdmmc/sdmmc_cmd.c
Normal file
571
components/sdmmc/sdmmc_cmd.c
Normal file
@@ -0,0 +1,571 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
* Adaptations to ESP-IDF Copyright (c) 2016 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_heap_alloc_caps.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/sdmmc_defs.h"
|
||||
#include "driver/sdmmc_types.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
|
||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||
|
||||
static const char* TAG = "sdmmc_cmd";
|
||||
|
||||
static esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd);
|
||||
static esp_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd);
|
||||
static esp_err_t sdmmc_send_cmd_go_idle_state(sdmmc_card_t* card);
|
||||
static esp_err_t sdmmc_send_cmd_send_if_cond(sdmmc_card_t* card, uint32_t ocr);
|
||||
static esp_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp);
|
||||
static esp_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid);
|
||||
static esp_err_t sddmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_cid_t* out_cid);
|
||||
static esp_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* out_rca);
|
||||
static esp_err_t sdmmc_send_cmd_set_blocklen(sdmmc_card_t* card, sdmmc_csd_t* csd);
|
||||
static esp_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd);
|
||||
static esp_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd);
|
||||
static esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card);
|
||||
static esp_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr);
|
||||
static esp_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_scr);
|
||||
static esp_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width);
|
||||
static esp_err_t sdmmc_send_cmd_stop_transmission(sdmmc_card_t* card, uint32_t* status);
|
||||
static esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status);
|
||||
static uint32_t get_host_ocr(float voltage);
|
||||
|
||||
|
||||
esp_err_t sdmmc_card_init(const sdmmc_host_t* config,
|
||||
sdmmc_card_t* card)
|
||||
{
|
||||
ESP_LOGD(TAG, "%s", __func__);
|
||||
memset(card, 0, sizeof(*card));
|
||||
memcpy(&card->host, config, sizeof(*config));
|
||||
esp_err_t err = sdmmc_send_cmd_go_idle_state(card);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: go_idle_state (1) returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
ets_delay_us(10000);
|
||||
uint32_t host_ocr = get_host_ocr(config->io_voltage);
|
||||
err = sdmmc_send_cmd_send_if_cond(card, host_ocr);
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGD(TAG, "SDHC/SDXC card");
|
||||
host_ocr |= SD_OCR_SDHC_CAP;
|
||||
} else if (err == ESP_ERR_TIMEOUT) {
|
||||
ESP_LOGD(TAG, "CMD8 timeout; not an SDHC/SDXC card");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "%s: send_if_cond (1) returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
err = sdmmc_send_cmd_send_op_cond(card, host_ocr, &card->ocr);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: send_op_cond (1) returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
host_ocr &= card->ocr;
|
||||
ESP_LOGD(TAG, "sdmmc_card_init: host_ocr=%08x, card_ocr=%08x", host_ocr, card->ocr);
|
||||
err = sddmc_send_cmd_all_send_cid(card, &card->cid);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: all_send_cid returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
err = sdmmc_send_cmd_set_relative_addr(card, &card->rca);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: set_relative_addr returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
err = sdmmc_send_cmd_send_csd(card, &card->csd);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: send_csd returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
const size_t max_sdsc_capacity = UINT32_MAX / card->csd.sector_size + 1;
|
||||
if (!(card->ocr & SD_OCR_SDHC_CAP) &&
|
||||
card->csd.capacity > max_sdsc_capacity) {
|
||||
ESP_LOGW(TAG, "%s: SDSC card reports capacity=%u. Limiting to %u.",
|
||||
__func__, card->csd.capacity, max_sdsc_capacity);
|
||||
card->csd.capacity = max_sdsc_capacity;
|
||||
}
|
||||
err = sdmmc_send_cmd_select_card(card);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: select_card returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
if ((card->ocr & SD_OCR_SDHC_CAP) == 0) {
|
||||
err = sdmmc_send_cmd_set_blocklen(card, &card->csd);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: set_blocklen returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
err = sdmmc_send_cmd_send_scr(card, &card->scr);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: send_scr returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
if ((config->flags & SDMMC_HOST_FLAG_4BIT) &&
|
||||
(card->scr.bus_width & SCR_SD_BUS_WIDTHS_4BIT)) {
|
||||
ESP_LOGD(TAG, "switching to 4-bit bus mode");
|
||||
err = sdmmc_send_cmd_set_bus_width(card, 4);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "set_bus_width failed");
|
||||
return err;
|
||||
}
|
||||
err = (*config->set_bus_width)(config->slot, 4);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "slot->set_bus_width failed");
|
||||
return err;
|
||||
}
|
||||
uint32_t status;
|
||||
err = sdmmc_send_cmd_stop_transmission(card, &status);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "stop_transmission failed (0x%x)", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
uint32_t status = 0;
|
||||
while (!(status & MMC_R1_READY_FOR_DATA)) {
|
||||
// TODO: add some timeout here
|
||||
uint32_t count = 0;
|
||||
err = sdmmc_send_cmd_send_status(card, &status);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
if (++count % 10 == 0) {
|
||||
ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
|
||||
}
|
||||
}
|
||||
if (config->max_freq_khz >= SDMMC_FREQ_HIGHSPEED &&
|
||||
card->csd.tr_speed / 1000 >= SDMMC_FREQ_HIGHSPEED) {
|
||||
ESP_LOGD(TAG, "switching to HS bus mode");
|
||||
err = (*config->set_card_clk)(config->slot, SDMMC_FREQ_HIGHSPEED);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "failed to switch peripheral to HS bus mode");
|
||||
return err;
|
||||
}
|
||||
} else if (config->max_freq_khz >= SDMMC_FREQ_DEFAULT &&
|
||||
card->csd.tr_speed / 1000 >= SDMMC_FREQ_DEFAULT) {
|
||||
ESP_LOGD(TAG, "switching to DS bus mode");
|
||||
err = (*config->set_card_clk)(config->slot, SDMMC_FREQ_DEFAULT);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "failed to switch peripheral to HS bus mode");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
sdmmc_scr_t scr_tmp;
|
||||
err = sdmmc_send_cmd_send_scr(card, &scr_tmp);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: send_scr returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
if (memcmp(&card->scr, &scr_tmp, sizeof(scr_tmp)) != 0) {
|
||||
ESP_LOGE(TAG, "data check fail!");
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card)
|
||||
{
|
||||
fprintf(stream, "Name: %s\n", card->cid.name);
|
||||
fprintf(stream, "Type: %s\n", (card->ocr & SD_OCR_SDHC_CAP)?"SDHC/SDXC":"SDSC");
|
||||
fprintf(stream, "Speed: %s\n", (card->csd.tr_speed > 25000000)?"high speed":"default speed");
|
||||
fprintf(stream, "Size: %lluMB\n", ((uint64_t) card->csd.capacity) * card->csd.sector_size / (1024 * 1024));
|
||||
fprintf(stream, "CSD: ver=%d, sector_size=%d, capacity=%d read_bl_len=%d\n",
|
||||
card->csd.csd_ver,
|
||||
card->csd.sector_size, card->csd.capacity, card->csd.read_block_len);
|
||||
fprintf(stream, "SCR: sd_spec=%d, bus_width=%d\n", card->scr.sd_spec, card->scr.bus_width);
|
||||
}
|
||||
|
||||
static esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd)
|
||||
{
|
||||
int slot = card->host.slot;
|
||||
ESP_LOGV(TAG, "sending cmd slot=%d op=%d arg=%x flags=%x data=%p blklen=%d datalen=%d",
|
||||
slot, cmd->opcode, cmd->arg, cmd->flags, cmd->data, cmd->blklen, cmd->datalen);
|
||||
esp_err_t err = (*card->host.do_transaction)(slot, cmd);
|
||||
if (err != 0) {
|
||||
ESP_LOGD(TAG, "sdmmc_req_run returned 0x%x", err);
|
||||
return err;
|
||||
}
|
||||
int state = MMC_R1_CURRENT_STATE(cmd->response);
|
||||
ESP_LOGV(TAG, "cmd response %08x %08x %08x %08x err=0x%x state=%d",
|
||||
cmd->response[0],
|
||||
cmd->response[1],
|
||||
cmd->response[2],
|
||||
cmd->response[3],
|
||||
cmd->error,
|
||||
state);
|
||||
return cmd->error;
|
||||
}
|
||||
|
||||
static esp_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd)
|
||||
{
|
||||
sdmmc_command_t app_cmd = {
|
||||
.opcode = MMC_APP_CMD,
|
||||
.flags = SCF_CMD_AC | SCF_RSP_R1,
|
||||
.arg = MMC_ARG_RCA(card->rca),
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &app_cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
if (!(MMC_R1(app_cmd.response) & MMC_R1_APP_CMD)) {
|
||||
ESP_LOGW(TAG, "card doesn't support APP_CMD");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
return sdmmc_send_cmd(card, cmd);
|
||||
}
|
||||
|
||||
|
||||
static esp_err_t sdmmc_send_cmd_go_idle_state(sdmmc_card_t* card)
|
||||
{
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_GO_IDLE_STATE,
|
||||
.flags = SCF_CMD_BC | SCF_RSP_R0,
|
||||
};
|
||||
return sdmmc_send_cmd(card, &cmd);
|
||||
}
|
||||
|
||||
|
||||
static esp_err_t sdmmc_send_cmd_send_if_cond(sdmmc_card_t* card, uint32_t ocr)
|
||||
{
|
||||
const uint8_t pattern = 0xaa; /* any pattern will do here */
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = SD_SEND_IF_COND,
|
||||
.arg = (((ocr & SD_OCR_VOL_MASK) != 0) << 8) | pattern,
|
||||
.flags = SCF_CMD_BCR | SCF_RSP_R7,
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
uint8_t response = cmd.response[0] & 0xff;
|
||||
if (response != pattern) {
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp)
|
||||
{
|
||||
sdmmc_command_t cmd = {
|
||||
.arg = ocr,
|
||||
.flags = SCF_CMD_BCR | SCF_RSP_R3,
|
||||
.opcode = SD_APP_OP_COND
|
||||
};
|
||||
int nretries = 100; // arbitrary, BSD driver uses this value
|
||||
for (; nretries != 0; --nretries) {
|
||||
esp_err_t err = sdmmc_send_app_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
if ((MMC_R3(cmd.response) & MMC_OCR_MEM_READY) ||
|
||||
ocr == 0) {
|
||||
break;
|
||||
}
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
if (nretries == 0) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
if (ocrp) {
|
||||
*ocrp = MMC_R3(cmd.response);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid)
|
||||
{
|
||||
out_cid->mfg_id = SD_CID_MID(resp);
|
||||
out_cid->oem_id = SD_CID_OID(resp);
|
||||
SD_CID_PNM_CPY(resp, out_cid->name);
|
||||
out_cid->revision = SD_CID_REV(resp);
|
||||
out_cid->serial = SD_CID_PSN(resp);
|
||||
out_cid->date = SD_CID_MDT(resp);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t sddmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_cid_t* out_cid)
|
||||
{
|
||||
assert(out_cid);
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_ALL_SEND_CID,
|
||||
.flags = SCF_CMD_BCR | SCF_RSP_R2
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
return sdmmc_decode_cid(cmd.response, out_cid);
|
||||
}
|
||||
|
||||
|
||||
static esp_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* out_rca)
|
||||
{
|
||||
assert(out_rca);
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = SD_SEND_RELATIVE_ADDR,
|
||||
.flags = SCF_CMD_BCR | SCF_RSP_R6
|
||||
};
|
||||
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
*out_rca = SD_R6_RCA(cmd.response);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
static esp_err_t sdmmc_send_cmd_set_blocklen(sdmmc_card_t* card, sdmmc_csd_t* csd)
|
||||
{
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_SET_BLOCKLEN,
|
||||
.arg = csd->sector_size,
|
||||
.flags = SCF_CMD_AC | SCF_RSP_R1
|
||||
};
|
||||
return sdmmc_send_cmd(card, &cmd);
|
||||
}
|
||||
|
||||
static esp_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd)
|
||||
{
|
||||
out_csd->csd_ver = SD_CSD_CSDVER(response);
|
||||
switch (out_csd->csd_ver) {
|
||||
case SD_CSD_CSDVER_2_0:
|
||||
out_csd->capacity = SD_CSD_V2_CAPACITY(response);
|
||||
out_csd->read_block_len = SD_CSD_V2_BL_LEN;
|
||||
break;
|
||||
case SD_CSD_CSDVER_1_0:
|
||||
out_csd->capacity = SD_CSD_CAPACITY(response);
|
||||
out_csd->read_block_len = SD_CSD_READ_BL_LEN(response);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "unknown SD CSD structure version 0x%x", out_csd->csd_ver);
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
out_csd->card_command_class = SD_CSD_CCC(response);
|
||||
int read_bl_size = 1 << out_csd->read_block_len;
|
||||
out_csd->sector_size = MIN(read_bl_size, 512);
|
||||
if (out_csd->sector_size < read_bl_size) {
|
||||
out_csd->capacity *= read_bl_size / out_csd->sector_size;
|
||||
}
|
||||
int speed = SD_CSD_SPEED(response);
|
||||
if (speed == SD_CSD_SPEED_50_MHZ) {
|
||||
out_csd->tr_speed = 50000000;
|
||||
} else {
|
||||
out_csd->tr_speed = 25000000;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd)
|
||||
{
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_SEND_CSD,
|
||||
.arg = MMC_ARG_RCA(card->rca),
|
||||
.flags = SCF_CMD_AC | SCF_RSP_R2
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
return sdmmc_decode_csd(cmd.response, out_csd);
|
||||
}
|
||||
|
||||
static esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card)
|
||||
{
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_SELECT_CARD,
|
||||
.arg = MMC_ARG_RCA(card->rca),
|
||||
.flags = SCF_CMD_AC | SCF_RSP_R1
|
||||
};
|
||||
return sdmmc_send_cmd(card, &cmd);
|
||||
}
|
||||
|
||||
static esp_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr)
|
||||
{
|
||||
sdmmc_response_t resp = {0xabababab, 0xabababab, 0x12345678, 0x09abcdef};
|
||||
resp[2] = __builtin_bswap32(raw_scr[0]);
|
||||
resp[3] = __builtin_bswap32(raw_scr[1]);
|
||||
int ver = SCR_STRUCTURE(resp);
|
||||
if (ver != 0) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
out_scr->sd_spec = SCR_SD_SPEC(resp);
|
||||
out_scr->bus_width = SCR_SD_BUS_WIDTHS(resp);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_scr)
|
||||
{
|
||||
size_t datalen = 8;
|
||||
uint32_t* buf = (uint32_t*) pvPortMallocCaps(datalen, MALLOC_CAP_DMA);
|
||||
if (buf == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
sdmmc_command_t cmd = {
|
||||
.data = buf,
|
||||
.datalen = datalen,
|
||||
.blklen = datalen,
|
||||
.flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1,
|
||||
.opcode = SD_APP_SEND_SCR
|
||||
};
|
||||
esp_err_t err = sdmmc_send_app_cmd(card, &cmd);
|
||||
if (err == ESP_OK) {
|
||||
buf[0] = (buf[0]);
|
||||
buf[1] = (buf[1]);
|
||||
err = sdmmc_decode_scr(buf, out_scr);
|
||||
}
|
||||
free(buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width)
|
||||
{
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = SD_APP_SET_BUS_WIDTH,
|
||||
.flags = SCF_RSP_R1 | SCF_CMD_AC,
|
||||
.arg = (width == 4) ? SD_ARG_BUS_WIDTH_4 : SD_ARG_BUS_WIDTH_1
|
||||
};
|
||||
|
||||
return sdmmc_send_app_cmd(card, &cmd);
|
||||
}
|
||||
|
||||
static esp_err_t sdmmc_send_cmd_stop_transmission(sdmmc_card_t* card, uint32_t* status)
|
||||
{
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_STOP_TRANSMISSION,
|
||||
.arg = 0,
|
||||
.flags = SCF_RSP_R1B | SCF_CMD_AC
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err == 0) {
|
||||
*status = MMC_R1(cmd.response);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static uint32_t get_host_ocr(float voltage)
|
||||
{
|
||||
// TODO: report exact voltage to the card
|
||||
// For now tell that the host has 2.8-3.6V voltage range
|
||||
(void) voltage;
|
||||
return SD_OCR_VOL_MASK;
|
||||
}
|
||||
|
||||
static esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status)
|
||||
{
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_SEND_STATUS,
|
||||
.arg = MMC_ARG_RCA(card->rca),
|
||||
.flags = SCF_CMD_AC | SCF_RSP_R1
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
if (out_status) {
|
||||
*out_status = MMC_R1(cmd.response);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src,
|
||||
size_t start_block, size_t block_count)
|
||||
{
|
||||
if (start_block + block_count > card->csd.capacity) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
size_t block_size = card->csd.sector_size;
|
||||
sdmmc_command_t cmd = {
|
||||
.flags = SCF_CMD_ADTC | SCF_RSP_R1,
|
||||
.blklen = block_size,
|
||||
.data = (void*) src,
|
||||
.datalen = block_count * block_size
|
||||
};
|
||||
if (block_count == 1) {
|
||||
cmd.opcode = MMC_WRITE_BLOCK_SINGLE;
|
||||
} else {
|
||||
cmd.opcode = MMC_WRITE_BLOCK_MULTIPLE;
|
||||
}
|
||||
if (card->ocr & SD_OCR_SDHC_CAP) {
|
||||
cmd.arg = start_block;
|
||||
} else {
|
||||
cmd.arg = start_block * block_size;
|
||||
}
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
uint32_t status = 0;
|
||||
size_t count = 0;
|
||||
while (!(status & MMC_R1_READY_FOR_DATA)) {
|
||||
// TODO: add some timeout here
|
||||
err = sdmmc_send_cmd_send_status(card, &status);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
if (++count % 10 == 0) {
|
||||
ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_read_sectors(sdmmc_card_t* card, void* dst,
|
||||
size_t start_block, size_t block_count)
|
||||
{
|
||||
if (start_block + block_count > card->csd.capacity) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
size_t block_size = card->csd.sector_size;
|
||||
sdmmc_command_t cmd = {
|
||||
.flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1,
|
||||
.blklen = block_size,
|
||||
.data = (void*) dst,
|
||||
.datalen = block_count * block_size
|
||||
};
|
||||
if (block_count == 1) {
|
||||
cmd.opcode = MMC_READ_BLOCK_SINGLE;
|
||||
} else {
|
||||
cmd.opcode = MMC_READ_BLOCK_MULTIPLE;
|
||||
}
|
||||
if (card->ocr & SD_OCR_SDHC_CAP) {
|
||||
cmd.arg = start_block;
|
||||
} else {
|
||||
cmd.arg = start_block * block_size;
|
||||
}
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
uint32_t status = 0;
|
||||
size_t count = 0;
|
||||
while (!(status & MMC_R1_READY_FOR_DATA)) {
|
||||
// TODO: add some timeout here
|
||||
err = sdmmc_send_cmd_send_status(card, &status);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
if (++count % 10 == 0) {
|
||||
ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
1
components/sdmmc/test/component.mk
Normal file
1
components/sdmmc/test/component.mk
Normal file
@@ -0,0 +1 @@
|
||||
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
|
95
components/sdmmc/test/test_sd.c
Normal file
95
components/sdmmc/test/test_sd.c
Normal file
@@ -0,0 +1,95 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "unity.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/sdmmc_host.h"
|
||||
#include "driver/sdmmc_defs.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_heap_alloc_caps.h"
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
|
||||
TEST_CASE("can probe SD", "[sd]")
|
||||
{
|
||||
sdmmc_host_t config = SDMMC_HOST_DEFAULT();
|
||||
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||
sdmmc_host_init();
|
||||
sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config);
|
||||
sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
|
||||
TEST_ASSERT_NOT_NULL(card);
|
||||
TEST_ESP_OK(sdmmc_card_init(&config, card));
|
||||
sdmmc_card_print_info(stdout, card);
|
||||
sdmmc_host_deinit();
|
||||
free(card);
|
||||
}
|
||||
|
||||
|
||||
static void do_single_write_read_test(sdmmc_card_t* card,
|
||||
size_t start_block, size_t block_count)
|
||||
{
|
||||
size_t block_size = card->csd.sector_size;
|
||||
size_t total_size = block_size * block_count;
|
||||
printf(" %8d | %3d | %4.1f ", start_block, block_count, total_size / 1024.0f);
|
||||
uint32_t* buffer = pvPortMallocCaps(total_size, MALLOC_CAP_DMA);
|
||||
srand(start_block);
|
||||
for (size_t i = 0; i < total_size / sizeof(buffer[0]); ++i) {
|
||||
buffer[i] = rand();
|
||||
}
|
||||
struct timeval t_start_wr;
|
||||
gettimeofday(&t_start_wr, NULL);
|
||||
TEST_ESP_OK(sdmmc_write_sectors(card, buffer, start_block, block_count));
|
||||
struct timeval t_stop_wr;
|
||||
gettimeofday(&t_stop_wr, NULL);
|
||||
float time_wr = 1e3f * (t_stop_wr.tv_sec - t_start_wr.tv_sec) + 1e-3f * (t_stop_wr.tv_usec - t_start_wr.tv_usec);
|
||||
memset(buffer, 0xbb, total_size);
|
||||
struct timeval t_start_rd;
|
||||
gettimeofday(&t_start_rd, NULL);
|
||||
TEST_ESP_OK(sdmmc_read_sectors(card, buffer, start_block, block_count));
|
||||
struct timeval t_stop_rd;
|
||||
gettimeofday(&t_stop_rd, NULL);
|
||||
float time_rd = 1e3f * (t_stop_rd.tv_sec - t_start_rd.tv_sec) + 1e-3f * (t_stop_rd.tv_usec - t_start_rd.tv_usec);
|
||||
|
||||
printf(" | %6.2f | %.2f | %.2fs | %.2f\n",
|
||||
time_wr, total_size / (time_wr / 1000) / (1024 * 1024),
|
||||
time_rd, total_size / (time_rd / 1000) / (1024 * 1024));
|
||||
srand(start_block);
|
||||
for (size_t i = 0; i < total_size / sizeof(buffer[0]); ++i) {
|
||||
TEST_ASSERT_EQUAL_HEX32(rand(), buffer[i]);
|
||||
}
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
TEST_CASE("can write and read back blocks", "[sd]")
|
||||
{
|
||||
sdmmc_host_t config = SDMMC_HOST_DEFAULT();
|
||||
config.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
|
||||
sdmmc_host_init();
|
||||
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||
sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config);
|
||||
sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
|
||||
TEST_ASSERT_NOT_NULL(card);
|
||||
TEST_ESP_OK(sdmmc_card_init(&config, card));
|
||||
sdmmc_card_print_info(stdout, card);
|
||||
printf(" sector | count | size(kB) | wr_time(ms) | wr_speed(MB/s) | rd_time(ms) | rd_speed(MB/s)\n");
|
||||
do_single_write_read_test(card, 0, 1);
|
||||
do_single_write_read_test(card, 0, 4);
|
||||
do_single_write_read_test(card, 1, 16);
|
||||
do_single_write_read_test(card, 16, 32);
|
||||
do_single_write_read_test(card, 48, 64);
|
||||
do_single_write_read_test(card, 128, 128);
|
||||
do_single_write_read_test(card, card->csd.capacity - 64, 32);
|
||||
do_single_write_read_test(card, card->csd.capacity - 64, 64);
|
||||
do_single_write_read_test(card, card->csd.capacity - 8, 1);
|
||||
do_single_write_read_test(card, card->csd.capacity/2, 1);
|
||||
do_single_write_read_test(card, card->csd.capacity/2, 4);
|
||||
do_single_write_read_test(card, card->csd.capacity/2, 8);
|
||||
do_single_write_read_test(card, card->csd.capacity/2, 16);
|
||||
do_single_write_read_test(card, card->csd.capacity/2, 32);
|
||||
do_single_write_read_test(card, card->csd.capacity/2, 64);
|
||||
do_single_write_read_test(card, card->csd.capacity/2, 128);
|
||||
free(card);
|
||||
sdmmc_host_deinit();
|
||||
}
|
Reference in New Issue
Block a user