pinebuds/platform/hal/hal_sdmmc.c

2790 lines
79 KiB
C

/***************************************************************************
*
* Copyright 2015-2019 BES.
* All rights reserved. All unpublished rights reserved.
*
* No part of this work may be used or reproduced in any form or by any
* means, or stored in a database or retrieval system, without prior written
* permission of BES.
*
* Use of this work is governed by a license granted by BES.
* This work contains confidential and proprietary information of
* BES. which is protected by copyright, trade secret,
* trademark and other intellectual property rights.
*
****************************************************************************/
#ifdef CHIP_HAS_SDMMC
#include "hal_sdmmc.h"
#include "cmsis_nvic.h"
#include "errno.h"
#include "hal_cmu.h"
#include "hal_dma.h"
#include "hal_timer.h"
#include "hal_trace.h"
#include "hal_uart.h"
#include "plat_addr_map.h"
#include "reg_sdmmcip.h"
#include "stdarg.h"
#include "stdio.h"
#include "string.h"
#ifndef ETIMEDOUT
#define ETIMEDOUT 110 /* Connection timed out */
#endif
#define HAL_SDMMC_USE_DMA 1
// #define __BUS_WIDTH_SUPPORT_4BIT__ 1
#define HAL_SDMMC_TRACE(...)
// #define HAL_SDMMC_TRACE TRACE
#define HAL_SDMMC_ASSERT(...)
// #define HAL_SDMMC_ASSERT ASSERT
#define _SDMMC_CLOCK 52000000
#define _SDMMC_DMA_MINALIGN 32
#define _sdmmc_be32_to_cpu(x) \
((uint32_t)((((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \
(((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \
(((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \
(((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24)))
#define _SDMMC_DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d))
#define _SDMMC_ROUND(a, b) (((a) + (b)-1) & ~((b)-1))
#define _SDMMC_PAD_COUNT(s, pad) (((s)-1) / (pad) + 1)
#define _SDMMC_PAD_SIZE(s, pad) (_SDMMC_PAD_COUNT(s, pad) * pad)
#define _SDMMC_ALLOC_ALIGN_BUFFER_PAD(type, name, size, align, pad) \
char __##name[_SDMMC_ROUND(_SDMMC_PAD_SIZE((size) * sizeof(type), pad), \
align) + \
(align - 1)]; \
type *name = (type *)ALIGN((uint32_t)__##name, align)
#define _SDMMC_ALLOC_ALIGN_BUFFER(type, name, size, align) \
_SDMMC_ALLOC_ALIGN_BUFFER_PAD(type, name, size, align, 1)
#define _SDMMC_ALLOC_CACHE_ALIGN_BUFFER_PAD(type, name, size, pad) \
_SDMMC_ALLOC_ALIGN_BUFFER_PAD(type, name, size, _SDMMC_DMA_MINALIGN, pad)
#define _SDMMC_ALLOC_CACHE_ALIGN_BUFFER(type, name, size) \
_SDMMC_ALLOC_ALIGN_BUFFER(type, name, size, _SDMMC_DMA_MINALIGN)
typedef struct block_dev_desc {
int if_type; /* type of the interface */
int dev; /* device number */
unsigned char part_type; /* partition type */
unsigned char target; /* target SCSI ID */
unsigned char lun; /* target LUN */
unsigned char type; /* device type */
unsigned char removable; /* removable device */
#ifdef CONFIG_LBA48
unsigned char lba48; /* device can use 48bit addr (ATA/ATAPI v7) */
#endif
uint32_t lba; /* number of blocks */
unsigned long blksz; /* block size */
int log2blksz; /* for convenience: log2(blksz) */
char vendor[40 + 1]; /* IDE model, SCSI Vendor */
char product[20 + 1]; /* IDE Serial no, SCSI product */
char revision[8 + 1]; /* firmware revision */
void *priv; /* driver private struct pointer */
} block_dev_desc_t;
/* SD/MMC version bits; 8 flags, 8 major, 8 minor, 8 change */
#define SD_VERSION_SD (1U << 31)
#define MMC_VERSION_MMC (1U << 30)
#define MAKE_SDMMC_VERSION(a, b, c) \
((((uint32_t)(a)) << 16) | ((uint32_t)(b) << 8) | (uint32_t)(c))
#define MAKE_SD_VERSION(a, b, c) (SD_VERSION_SD | MAKE_SDMMC_VERSION(a, b, c))
#define MAKE_MMC_VERSION(a, b, c) \
(MMC_VERSION_MMC | MAKE_SDMMC_VERSION(a, b, c))
#define EXTRACT_SDMMC_MAJOR_VERSION(x) (((uint32_t)(x) >> 16) & 0xff)
#define EXTRACT_SDMMC_MINOR_VERSION(x) (((uint32_t)(x) >> 8) & 0xff)
#define EXTRACT_SDMMC_CHANGE_VERSION(x) ((uint32_t)(x)&0xff)
#define SD_VERSION_3 MAKE_SD_VERSION(3, 0, 0)
#define SD_VERSION_2 MAKE_SD_VERSION(2, 0, 0)
#define SD_VERSION_1_0 MAKE_SD_VERSION(1, 0, 0)
#define SD_VERSION_1_10 MAKE_SD_VERSION(1, 10, 0)
#define MMC_VERSION_UNKNOWN MAKE_MMC_VERSION(0, 0, 0)
#define MMC_VERSION_1_2 MAKE_MMC_VERSION(1, 2, 0)
#define MMC_VERSION_1_4 MAKE_MMC_VERSION(1, 4, 0)
#define MMC_VERSION_2_2 MAKE_MMC_VERSION(2, 2, 0)
#define MMC_VERSION_3 MAKE_MMC_VERSION(3, 0, 0)
#define MMC_VERSION_4 MAKE_MMC_VERSION(4, 0, 0)
#define MMC_VERSION_4_1 MAKE_MMC_VERSION(4, 1, 0)
#define MMC_VERSION_4_2 MAKE_MMC_VERSION(4, 2, 0)
#define MMC_VERSION_4_3 MAKE_MMC_VERSION(4, 3, 0)
#define MMC_VERSION_4_41 MAKE_MMC_VERSION(4, 4, 1)
#define MMC_VERSION_4_5 MAKE_MMC_VERSION(4, 5, 0)
#define MMC_VERSION_5_0 MAKE_MMC_VERSION(5, 0, 0)
#define MMC_MODE_HS (1 << 0)
#define MMC_MODE_HS_52MHz (1 << 1)
#define MMC_MODE_4BIT (1 << 2)
#define MMC_MODE_8BIT (1 << 3)
#define MMC_MODE_SPI (1 << 4)
#define MMC_MODE_DDR_52MHz (1 << 5)
#define SD_DATA_4BIT 0x00040000
#define IS_SD(x) ((x)->version & SD_VERSION_SD)
#define IS_MMC(x) ((x)->version & MMC_VERSION_MMC)
#define MMC_DATA_READ 1
#define MMC_DATA_WRITE 2
#define NO_CARD_ERR -16 /* No SD/MMC card inserted */
#define UNUSABLE_ERR -17 /* Unusable Card */
#define COMM_ERR -18 /* Communications Error */
#define TIMEOUT -19
#define SWITCH_ERR -20 /* Card reports failure to switch mode */
#define MMC_CMD_GO_IDLE_STATE 0
#define MMC_CMD_SEND_OP_COND 1
#define MMC_CMD_ALL_SEND_CID 2
#define MMC_CMD_SET_RELATIVE_ADDR 3
#define MMC_CMD_SET_DSR 4
#define MMC_CMD_SWITCH 6
#define MMC_CMD_SELECT_CARD 7
#define MMC_CMD_SEND_EXT_CSD 8
#define MMC_CMD_SEND_CSD 9
#define MMC_CMD_SEND_CID 10
#define MMC_CMD_STOP_TRANSMISSION 12
#define MMC_CMD_SEND_STATUS 13
#define MMC_CMD_SET_BLOCKLEN 16
#define MMC_CMD_READ_SINGLE_BLOCK 17
#define MMC_CMD_READ_MULTIPLE_BLOCK 18
#define MMC_CMD_SET_BLOCK_COUNT 23
#define MMC_CMD_WRITE_SINGLE_BLOCK 24
#define MMC_CMD_WRITE_MULTIPLE_BLOCK 25
#define MMC_CMD_ERASE_GROUP_START 35
#define MMC_CMD_ERASE_GROUP_END 36
#define MMC_CMD_ERASE 38
#define MMC_CMD_APP_CMD 55
#define MMC_CMD_SPI_READ_OCR 58
#define MMC_CMD_SPI_CRC_ON_OFF 59
#define MMC_CMD_RES_MAN 62
#define MMC_CMD62_ARG1 0xefac62ec
#define MMC_CMD62_ARG2 0xcbaea7
#define SD_CMD_SEND_RELATIVE_ADDR 3
#define SD_CMD_SWITCH_FUNC 6
#define SD_CMD_SEND_IF_COND 8
#define SD_CMD_SWITCH_UHS18V 11
#define SD_CMD_APP_SET_BUS_WIDTH 6
#define SD_CMD_ERASE_WR_BLK_START 32
#define SD_CMD_ERASE_WR_BLK_END 33
#define SD_CMD_APP_SEND_OP_COND 41
#define SD_CMD_APP_SEND_SCR 51
/* SCR definitions in different words */
#define SD_HIGHSPEED_BUSY 0x00020000
#define SD_HIGHSPEED_SUPPORTED 0x00020000
#define OCR_BUSY 0x80000000
#define OCR_HCS 0x40000000
#define OCR_VOLTAGE_MASK 0x007FFF80
#define OCR_ACCESS_MODE 0x60000000
#define SECURE_ERASE 0x80000000
#define MMC_STATUS_MASK (~0x0206BF7F)
#define MMC_STATUS_SWITCH_ERROR (1 << 7)
#define MMC_STATUS_RDY_FOR_DATA (1 << 8)
#define MMC_STATUS_CURR_STATE (0xf << 9)
#define MMC_STATUS_ERROR (1 << 19)
#define MMC_STATE_PRG (7 << 9)
#define MMC_VDD_165_195 0x00000080 /* VDD voltage 1.65 - 1.95 */
#define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */
#define MMC_VDD_21_22 0x00000200 /* VDD voltage 2.1 ~ 2.2 */
#define MMC_VDD_22_23 0x00000400 /* VDD voltage 2.2 ~ 2.3 */
#define MMC_VDD_23_24 0x00000800 /* VDD voltage 2.3 ~ 2.4 */
#define MMC_VDD_24_25 0x00001000 /* VDD voltage 2.4 ~ 2.5 */
#define MMC_VDD_25_26 0x00002000 /* VDD voltage 2.5 ~ 2.6 */
#define MMC_VDD_26_27 0x00004000 /* VDD voltage 2.6 ~ 2.7 */
#define MMC_VDD_27_28 0x00008000 /* VDD voltage 2.7 ~ 2.8 */
#define MMC_VDD_28_29 0x00010000 /* VDD voltage 2.8 ~ 2.9 */
#define MMC_VDD_29_30 0x00020000 /* VDD voltage 2.9 ~ 3.0 */
#define MMC_VDD_30_31 0x00040000 /* VDD voltage 3.0 ~ 3.1 */
#define MMC_VDD_31_32 0x00080000 /* VDD voltage 3.1 ~ 3.2 */
#define MMC_VDD_32_33 0x00100000 /* VDD voltage 3.2 ~ 3.3 */
#define MMC_VDD_33_34 0x00200000 /* VDD voltage 3.3 ~ 3.4 */
#define MMC_VDD_34_35 0x00400000 /* VDD voltage 3.4 ~ 3.5 */
#define MMC_VDD_35_36 0x00800000 /* VDD voltage 3.5 ~ 3.6 */
#define MMC_SWITCH_MODE_CMD_SET 0x00 /* Change the command set */
#define MMC_SWITCH_MODE_SET_BITS \
0x01 /* Set bits in EXT_CSD byte \
addressed by index which are \
1 in value field */
#define MMC_SWITCH_MODE_CLEAR_BITS \
0x02 /* Clear bits in EXT_CSD byte \
addressed by index, which are \
1 in value field */
#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target byte to value */
#define SD_SWITCH_CHECK 0
#define SD_SWITCH_SWITCH 1
/*
* EXT_CSD fields
*/
#define EXT_CSD_ENH_START_ADDR 136 /* R/W */
#define EXT_CSD_ENH_SIZE_MULT 140 /* R/W */
#define EXT_CSD_GP_SIZE_MULT 143 /* R/W */
#define EXT_CSD_PARTITION_SETTING 155 /* R/W */
#define EXT_CSD_PARTITIONS_ATTRIBUTE 156 /* R/W */
#define EXT_CSD_MAX_ENH_SIZE_MULT 157 /* R */
#define EXT_CSD_PARTITIONING_SUPPORT 160 /* RO */
#define EXT_CSD_RST_N_FUNCTION 162 /* R/W */
#define EXT_CSD_WR_REL_PARAM 166 /* R */
#define EXT_CSD_WR_REL_SET 167 /* R/W */
#define EXT_CSD_RPMB_MULT 168 /* RO */
#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */
#define EXT_CSD_BOOT_BUS_WIDTH 177
#define EXT_CSD_PART_CONF 179 /* R/W */
#define EXT_CSD_BUS_WIDTH 183 /* R/W */
#define EXT_CSD_HS_TIMING 185 /* R/W */
#define EXT_CSD_REV 192 /* RO */
#define EXT_CSD_CARD_TYPE 196 /* RO */
#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */
#define EXT_CSD_HC_WP_GRP_SIZE 221 /* RO */
#define EXT_CSD_HC_ERASE_GRP_SIZE 224 /* RO */
#define EXT_CSD_BOOT_MULT 226 /* RO */
/*
* EXT_CSD field definitions
*/
#define EXT_CSD_CMD_SET_NORMAL (1 << 0)
#define EXT_CSD_CMD_SET_SECURE (1 << 1)
#define EXT_CSD_CMD_SET_CPSECURE (1 << 2)
#define EXT_CSD_CARD_TYPE_26 (1 << 0) /* Card can run at 26MHz */
#define EXT_CSD_CARD_TYPE_52 (1 << 1) /* Card can run at 52MHz */
#define EXT_CSD_CARD_TYPE_DDR_1_8V (1 << 2)
#define EXT_CSD_CARD_TYPE_DDR_1_2V (1 << 3)
#define EXT_CSD_CARD_TYPE_DDR_52 \
(EXT_CSD_CARD_TYPE_DDR_1_8V | EXT_CSD_CARD_TYPE_DDR_1_2V)
#define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */
#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */
#define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */
#define EXT_CSD_DDR_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */
#define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */
#define EXT_CSD_BOOT_ACK_ENABLE (1 << 6)
#define EXT_CSD_BOOT_PARTITION_ENABLE (1 << 3)
#define EXT_CSD_PARTITION_ACCESS_ENABLE (1 << 0)
#define EXT_CSD_PARTITION_ACCESS_DISABLE (0 << 0)
#define EXT_CSD_BOOT_ACK(x) (x << 6)
#define EXT_CSD_BOOT_PART_NUM(x) (x << 3)
#define EXT_CSD_PARTITION_ACCESS(x) (x << 0)
#define EXT_CSD_BOOT_BUS_WIDTH_MODE(x) (x << 3)
#define EXT_CSD_BOOT_BUS_WIDTH_RESET(x) (x << 2)
#define EXT_CSD_BOOT_BUS_WIDTH_WIDTH(x) (x)
#define EXT_CSD_PARTITION_SETTING_COMPLETED (1 << 0)
#define EXT_CSD_ENH_USR (1 << 0) /* user data area is enhanced */
#define EXT_CSD_ENH_GP(x) (1 << ((x) + 1)) /* GP part (x+1) is enhanced */
#define EXT_CSD_HS_CTRL_REL (1 << 0) /* host controlled WR_REL_SET */
#define EXT_CSD_WR_DATA_REL_USR (1 << 0) /* user data area WR_REL */
#define EXT_CSD_WR_DATA_REL_GP(x) (1 << ((x) + 1)) /* GP part (x+1) WR_REL */
#define R1_ILLEGAL_COMMAND (1 << 22)
#define R1_APP_CMD (1 << 5)
#define MMC_RSP_PRESENT (1 << 0)
#define MMC_RSP_136 (1 << 1) /* 136 bit response */
#define MMC_RSP_CRC (1 << 2) /* expect valid crc */
#define MMC_RSP_BUSY (1 << 3) /* card may send busy */
#define MMC_RSP_OPCODE (1 << 4) /* response contains opcode */
#define MMC_RSP_NONE (0)
#define MMC_RSP_R1 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
#define MMC_RSP_R1b \
(MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE | MMC_RSP_BUSY)
#define MMC_RSP_R2 (MMC_RSP_PRESENT | MMC_RSP_136 | MMC_RSP_CRC)
#define MMC_RSP_R3 (MMC_RSP_PRESENT)
#define MMC_RSP_R4 (MMC_RSP_PRESENT)
#define MMC_RSP_R5 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
#define MMC_RSP_R6 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
#define MMC_RSP_R7 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
#define MMCPART_NOAVAILABLE (0xff)
#define PART_ACCESS_MASK (0x7)
#define PART_SUPPORT (0x1)
#define ENHNCD_SUPPORT (0x2)
#define PART_ENH_ATTRIB (0x1f)
/* Maximum block size for MMC */
#define MMC_MAX_BLOCK_LEN 512
/* The number of MMC physical partitions. These consist of:
* boot partitions (2), general purpose partitions (4) in MMC v4.4.
*/
#define MMC_NUM_BOOT_PARTITION 2
#define MMC_PART_RPMB 3 /* RPMB partition number */
struct mmc_cid {
unsigned long psn;
unsigned short oid;
uint8_t mid;
uint8_t prv;
uint8_t mdt;
char pnm[7];
};
struct mmc_cmd {
u16 cmdidx;
uint32_t resp_type;
uint32_t cmdarg;
uint32_t response[4];
};
struct mmc_data {
union {
char *dest;
const char *src; /* src buffers don't get written to */
};
uint32_t flags;
uint32_t blocks;
uint32_t blocksize;
};
/* forward decl. */
struct mmc;
struct mmc_ops {
int (*send_cmd)(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data);
void (*set_ios)(struct mmc *mmc);
int (*init)(struct mmc *mmc);
int (*getcd)(struct mmc *mmc);
int (*getwp)(struct mmc *mmc);
};
struct mmc_config {
const struct mmc_ops *ops;
uint32_t host_caps;
uint32_t voltages;
uint32_t f_min;
uint32_t f_max;
uint32_t b_max;
uint8_t part_type;
};
/* TODO struct mmc should be in mmc_private but it's hard to fix right now */
struct mmc {
const struct mmc_config *cfg; /* provided configuration */
uint32_t version;
void *priv;
uint32_t has_init;
int high_capacity;
uint32_t bus_width;
uint32_t clock;
uint32_t card_caps;
uint32_t ocr;
uint32_t dsr;
uint32_t dsr_imp;
uint32_t scr[2];
uint32_t csd[4];
uint32_t cid[4];
u16 rca;
u8 part_support;
u8 part_attr;
u8 wr_rel_set;
char part_config;
char part_num;
uint32_t tran_speed;
uint32_t read_bl_len;
uint32_t write_bl_len;
uint32_t erase_grp_size; /* in 512-byte sectors */
uint32_t hc_wp_grp_size; /* in 512-byte sectors */
uint64_t capacity;
uint64_t capacity_user;
uint64_t capacity_boot;
uint64_t capacity_rpmb;
uint64_t capacity_gp[4];
uint64_t enh_user_start;
uint64_t enh_user_size;
block_dev_desc_t block_dev;
char op_cond_pending; /* 1 if we are waiting on an op_cond command */
char init_in_progress; /* 1 if we have done mmc_start_init() */
char preinit; /* start init as early as possible */
int ddr_mode;
};
struct mmc_hwpart_conf {
struct {
uint32_t enh_start; /* in 512-byte sectors */
uint32_t enh_size; /* in 512-byte sectors, if 0 no enh area */
unsigned wr_rel_change : 1;
unsigned wr_rel_set : 1;
} user;
struct {
uint32_t size; /* in 512-byte sectors */
unsigned enhanced : 1;
unsigned wr_rel_change : 1;
unsigned wr_rel_set : 1;
} gp_part[4];
};
enum mmc_hwpart_conf_mode {
MMC_HWPART_CONF_CHECK,
MMC_HWPART_CONF_SET,
MMC_HWPART_CONF_COMPLETE,
};
struct sdmmcip_host {
void *ioaddr;
unsigned int quirks;
unsigned int caps;
unsigned int version;
unsigned int clock;
unsigned int bus_hz;
unsigned int div;
int dev_index;
int dev_id;
int buswidth;
uint32_t fifoth_val;
bool detect_enb;
struct mmc *mmc;
void *priv;
void (*clksel)(struct sdmmcip_host *host);
void (*board_init)(struct sdmmcip_host *host);
unsigned int (*get_mmc_clk)(struct sdmmcip_host *host);
struct mmc_config cfg;
#ifdef HAL_SDMMC_USE_DMA
/* TODO : os and non-os condition */
uint8_t dma_ch;
volatile uint32_t sdmmc_dma_lock;
HAL_DMA_IRQ_HANDLER_T tx_dma_handler;
HAL_DMA_IRQ_HANDLER_T rx_dma_handler;
#endif
};
struct sdmmcip_idmac {
uint32_t flags;
uint32_t cnt;
uint32_t addr;
uint32_t next_addr;
};
static void hal_sdmmc_delay(uint32_t ms);
static HAL_SDMMC_DELAY_FUNC sdmmc_delay = NULL;
static inline void sdmmcip_writel(struct sdmmcip_host *host, int reg,
uint32_t val) {
*((volatile uint32_t *)(host->ioaddr + reg)) = val;
}
static inline uint32_t sdmmcip_readl(struct sdmmcip_host *host, int reg) {
return *((volatile uint32_t *)(host->ioaddr + reg));
}
#ifdef CONFIG_MMC_SPI
#define sdmmc_host_is_spi(mmc) ((mmc)->cfg->host_caps & MMC_MODE_SPI)
#else
#define sdmmc_host_is_spi(mmc) 0
#endif
#ifndef CONFIG_SYS_MMC_MAX_BLK_COUNT
#define CONFIG_SYS_MMC_MAX_BLK_COUNT 65535
#endif
static void (*sdmmc_detected_callback)(uint8_t) = NULL;
static uint32_t sdmmc_ip_base[HAL_SDMMC_ID_NUM] = {
SDMMC_BASE,
};
static struct sdmmcip_host sdmmc_host[HAL_SDMMC_ID_NUM];
static struct mmc sdmmc_devices[HAL_SDMMC_ID_NUM];
// static const char * const invalid_id = "Invalid SDMMC ID: %d\n";
#ifdef HAL_SDMMC_USE_DMA
static void sdmmcip0_ext_dma_tx_handler(uint8_t chan, uint32_t remain_tsize,
uint32_t error,
struct HAL_DMA_DESC_T *lli);
static void sdmmcip0_ext_dma_rx_handler(uint8_t chan, uint32_t remain_tsize,
uint32_t error,
struct HAL_DMA_DESC_T *lli);
static HAL_DMA_IRQ_HANDLER_T
sdmmcip_ext_dma_irq_handlers[HAL_SDMMC_ID_NUM * 2] = {
sdmmcip0_ext_dma_tx_handler,
sdmmcip0_ext_dma_rx_handler,
};
#endif
uint32_t _sdmmc_div64_32(uint64_t *n, uint32_t base) {
uint64_t rem = *n;
uint64_t b = base;
uint64_t res, d = 1;
uint32_t high = rem >> 32;
/* Reduce the thing a bit first */
res = 0;
if (high >= base) {
high /= base;
res = (uint64_t)high << 32;
rem -= (uint64_t)(high * base) << 32;
}
while ((int64_t)b > 0 && b < rem) {
b = b + b;
d = d + d;
}
do {
if (rem >= b) {
rem -= b;
res += d;
}
b >>= 1;
d >>= 1;
} while (d);
*n = res;
return rem;
}
/* The unnecessary pointer compare is there
* * to check for type safety (n must be 64bit)
* */
#define _sdmmc_do_div(n, base) \
({ \
uint32_t __base = (base); \
uint32_t __rem; \
(void)(((typeof((n)) *)0) == ((uint64_t *)0)); \
if (((n) >> 32) == 0) { \
__rem = (uint32_t)(n) % __base; \
(n) = (uint32_t)(n) / __base; \
} else \
__rem = _sdmmc_div64_32(&(n), __base); \
__rem; \
})
/* Wrapper for _sdmmc_do_div(). Doesn't modify dividend and returns
* * the result, not reminder.
* */
static inline uint64_t _sdmmc_lldiv(uint64_t dividend, uint32_t divisor) {
uint64_t __res = dividend;
_sdmmc_do_div(__res, divisor);
return (__res);
}
static void mmc_udelay(int cnt) {
volatile uint32_t i = 0, c = 0;
for (i = 0; i < cnt; ++i) {
c++;
__asm("nop");
}
}
static int sdmmcip_wait_reset(struct sdmmcip_host *host, uint32_t value) {
uint32_t ctrl;
unsigned long timeout = 1000;
sdmmcip_writel(host, SDMMCIP_REG_CTRL, value);
while (timeout--) {
ctrl = sdmmcip_readl(host, SDMMCIP_REG_CTRL);
if (!(ctrl & SDMMCIP_REG_RESET_ALL))
return 1;
}
return 0;
}
#ifdef HAL_SDMMC_USE_DMA
static void sdmmcip0_ext_dma_tx_handler(uint8_t chan, uint32_t remain_tsize,
uint32_t error,
struct HAL_DMA_DESC_T *lli) {
uint32_t ip_raw_int_status = 0;
struct sdmmcip_host *host = &sdmmc_host[HAL_SDMMC_ID_0];
HAL_SDMMC_TRACE(2, "%s:%d\n", __func__, __LINE__);
ip_raw_int_status = sdmmcip_readl(host, SDMMCIP_REG_RINTSTS);
HAL_SDMMC_TRACE(3, "%s:%d, tx ip_raw_int_status 0x%x\n", __func__, __LINE__,
ip_raw_int_status);
if (ip_raw_int_status & (SDMMCIP_REG_DATA_ERR | SDMMCIP_REG_DATA_TOUT)) {
HAL_SDMMC_TRACE(3, "%s:%d, sdmmcip0 tx dma error 0x%x\n", __func__,
__LINE__, ip_raw_int_status);
}
/* TODO : os and non-os condition */
host->sdmmc_dma_lock = 0;
}
static void sdmmcip0_ext_dma_rx_handler(uint8_t chan, uint32_t remain_tsize,
uint32_t error,
struct HAL_DMA_DESC_T *lli) {
uint32_t ip_raw_int_status = 0;
struct sdmmcip_host *host = &sdmmc_host[HAL_SDMMC_ID_0];
HAL_SDMMC_TRACE(2, "%s:%d\n", __func__, __LINE__);
ip_raw_int_status = sdmmcip_readl(host, SDMMCIP_REG_RINTSTS);
HAL_SDMMC_TRACE(3, "%s:%d, ip_raw_int_status 0x%x\n", __func__, __LINE__,
ip_raw_int_status);
if (ip_raw_int_status & (SDMMCIP_REG_DATA_ERR | SDMMCIP_REG_DATA_TOUT)) {
HAL_SDMMC_TRACE(3, "%s:%d, sdmmcip0 rx dma error 0x%x\n", __func__,
__LINE__, ip_raw_int_status);
}
/* TODO : os and non-os condition */
host->sdmmc_dma_lock = 0;
}
#endif
static void sdmmcip_prepare_data(struct sdmmcip_host *host,
struct mmc_data *data,
struct sdmmcip_idmac *cur_idmac,
void *bounce_buffer) {
#ifdef HAL_SDMMC_USE_DMA
uint32_t ctrl = 0;
struct HAL_DMA_CH_CFG_T dma_cfg;
#endif
sdmmcip_writel(host, SDMMCIP_REG_BLKSIZ, data->blocksize);
sdmmcip_writel(host, SDMMCIP_REG_BYTCNT, data->blocksize * data->blocks);
/* use dma */
#ifdef HAL_SDMMC_USE_DMA
HAL_SDMMC_TRACE(0, "sdmmc use dma\n");
/* enable sdmmcip e-dma */
ctrl = sdmmcip_readl(host, SDMMCIP_REG_CTRL);
ctrl |= (SDMMCIP_REG_DMA_EN);
sdmmcip_writel(host, SDMMCIP_REG_CTRL, ctrl);
host->sdmmc_dma_lock = 1;
memset(&dma_cfg, 0, sizeof(dma_cfg));
if (data->flags & MMC_DATA_READ) {
HAL_SDMMC_TRACE(0, "sdmmc use dma read\n");
dma_cfg.dst = (uint32_t)data->dest;
// HAL_SDMMC_TRACE(2,"sddma :len %d, buf 0x%x\n",
// data->blocksize*data->blocks, dma_cfg.dst);
if ((dma_cfg.dst % 4) != 0) {
dma_cfg.dst_width = HAL_DMA_WIDTH_BYTE;
} else {
dma_cfg.dst_width = HAL_DMA_WIDTH_WORD;
}
dma_cfg.dst_bsize = HAL_DMA_BSIZE_1;
dma_cfg.dst_periph = 0; // useless
// dma_cfg.dst_width = HAL_DMA_WIDTH_WORD;
dma_cfg.handler = host->rx_dma_handler;
dma_cfg.src_bsize = HAL_DMA_BSIZE_1;
dma_cfg.src_periph = HAL_GPDMA_SDMMC;
dma_cfg.src_tsize = data->blocks * data->blocksize / 4;
HAL_SDMMC_TRACE(1, "sdmmc use dma tsize %d\n", dma_cfg.src_tsize);
dma_cfg.src_width = HAL_DMA_WIDTH_WORD;
dma_cfg.try_burst = 1;
dma_cfg.type = HAL_DMA_FLOW_P2M_DMA;
dma_cfg.src = (uint32_t)0; // useless
dma_cfg.ch = hal_gpdma_get_chan(dma_cfg.src_periph, HAL_DMA_HIGH_PRIO);
HAL_SDMMC_TRACE(1, "sdmmc use dma get ch %d\n", dma_cfg.ch);
} else {
HAL_SDMMC_TRACE(0, "sdmmc use dma write\n");
dma_cfg.dst = 0; // useless
dma_cfg.dst_bsize = HAL_DMA_BSIZE_1;
dma_cfg.dst_periph = HAL_GPDMA_SDMMC;
dma_cfg.dst_width = HAL_DMA_WIDTH_WORD;
dma_cfg.handler = host->tx_dma_handler;
dma_cfg.src_bsize = HAL_DMA_BSIZE_1;
dma_cfg.src_periph = 0; // useless
// dma_cfg.src_tsize = data->blocks*data->blocksize/4;
HAL_SDMMC_TRACE(1, "sdmmc use dma tsize %d\n", dma_cfg.src_tsize);
// dma_cfg.src_width = HAL_DMA_WIDTH_WORD;
dma_cfg.try_burst = 1;
dma_cfg.type = HAL_DMA_FLOW_M2P_DMA;
dma_cfg.src = (uint32_t)data->src;
// uart_printf("sddma :len %d, buf 0x%x\n", data->blocksize*data->blocks,
// dma_cfg.src);
if ((dma_cfg.src % 4) != 0) {
dma_cfg.src_width = HAL_DMA_WIDTH_BYTE;
dma_cfg.src_tsize = data->blocks * data->blocksize;
} else {
dma_cfg.src_width = HAL_DMA_WIDTH_WORD;
dma_cfg.src_tsize = data->blocks * data->blocksize / 4;
}
dma_cfg.ch = hal_gpdma_get_chan(dma_cfg.dst_periph, HAL_DMA_HIGH_PRIO);
HAL_SDMMC_TRACE(1, "sdmmc use dma get ch %d\n", dma_cfg.ch);
// uart_printf("%s:%d src 0x%x\n", __func__, __LINE__, dma_cfg.src);
}
host->dma_ch = dma_cfg.ch;
hal_gpdma_start(&dma_cfg);
#endif
}
static int sdmmcip_set_transfer_mode(struct sdmmcip_host *host,
struct mmc_data *data) {
unsigned long mode;
mode = SDMMCIP_REG_CMD_DATA_EXP;
if (data->flags & MMC_DATA_WRITE)
mode |= SDMMCIP_REG_CMD_RW;
return mode;
}
static int sdmmcip_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data) {
int ret = 0;
int flags = 0, i;
uint32_t retry = 1000000;
uint32_t mask, ctrl;
int busy_tmo = MS_TO_TICKS(1000), busy_t = 0;
#ifndef HAL_SDMMC_USE_DMA
uint32_t status = 0, fifo_data = 0;
#endif
struct sdmmcip_host *host = mmc->priv;
busy_t = hal_sys_timer_get();
while ((sdmmcip_readl(host, SDMMCIP_REG_STATUS) & SDMMCIP_REG_BUSY) &&
hal_sys_timer_get() < (busy_t + busy_tmo)) {
HAL_SDMMC_TRACE(0, "[sdmmc]busy\n");
}
sdmmcip_writel(host, SDMMCIP_REG_RINTSTS, SDMMCIP_REG_INTMSK_CD);
if (data) {
sdmmcip_prepare_data(host, data, 0, 0);
}
sdmmcip_writel(host, SDMMCIP_REG_CMDARG, cmd->cmdarg);
if (data)
flags = sdmmcip_set_transfer_mode(host, data);
if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY))
return -1;
if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
flags |= SDMMCIP_REG_CMD_ABORT_STOP;
else
flags |= SDMMCIP_REG_CMD_PRV_DAT_WAIT;
if (cmd->resp_type & MMC_RSP_PRESENT) {
flags |= SDMMCIP_REG_CMD_RESP_EXP;
if (cmd->resp_type & MMC_RSP_136)
flags |= SDMMCIP_REG_CMD_RESP_LENGTH;
}
if (cmd->resp_type & MMC_RSP_CRC)
flags |= SDMMCIP_REG_CMD_CHECK_CRC;
flags |= (cmd->cmdidx | SDMMCIP_REG_CMD_START | SDMMCIP_REG_CMD_USE_HOLD_REG);
sdmmcip_writel(host, SDMMCIP_REG_CMD, flags);
for (i = 0; i < retry; i++) {
mask = sdmmcip_readl(host, SDMMCIP_REG_RINTSTS);
if (mask & SDMMCIP_REG_INTMSK_CDONE) {
if (!data)
sdmmcip_writel(host, SDMMCIP_REG_RINTSTS, SDMMCIP_REG_INTMSK_CDONE);
break;
}
}
if (i == retry) {
HAL_SDMMC_TRACE(1, "%s: Timeout.\n", __func__);
return TIMEOUT;
}
if (mask & SDMMCIP_REG_INTMSK_RTO) {
/*
* Timeout here is not necessarily fatal. (e)MMC cards
* will splat here when they receive CMD55 as they do
* not support this command and that is exactly the way
* to tell them apart from SD cards. Thus, this output
* below shall be TRACE(). eMMC cards also do not favor
* CMD8, please keep that in mind.
*/
HAL_SDMMC_TRACE(1, "%s: Response Timeout.\n", __func__);
return TIMEOUT;
} else if (mask & SDMMCIP_REG_INTMSK_RE) {
HAL_SDMMC_TRACE(1, "%s: Response Error.\n", __func__);
return -1;
}
if (cmd->resp_type & MMC_RSP_PRESENT) {
if (cmd->resp_type & MMC_RSP_136) {
cmd->response[0] = sdmmcip_readl(host, SDMMCIP_REG_RESP3);
cmd->response[1] = sdmmcip_readl(host, SDMMCIP_REG_RESP2);
cmd->response[2] = sdmmcip_readl(host, SDMMCIP_REG_RESP1);
cmd->response[3] = sdmmcip_readl(host, SDMMCIP_REG_RESP0);
} else {
cmd->response[0] = sdmmcip_readl(host, SDMMCIP_REG_RESP0);
}
}
#ifndef HAL_SDMMC_USE_DMA
if (data) {
i = 0;
while (1) {
mask = sdmmcip_readl(host, SDMMCIP_REG_RINTSTS);
if (mask & (SDMMCIP_REG_DATA_ERR | SDMMCIP_REG_DATA_TOUT)) {
HAL_SDMMC_TRACE(1, "%s: READ DATA ERROR!\n", __func__);
ret = -1;
goto out;
}
status = sdmmcip_readl(host, SDMMCIP_REG_STATUS);
if (data->flags == MMC_DATA_READ) {
if (status & SDMMCIP_REG_FIFO_COUNT_MASK) {
fifo_data = sdmmcip_readl(host, SDMMCIP_REG_FIFO_OFFSET);
// HAL_SDMMC_TRACE(3,"%s: count %d, read -> 0x%x\n", __func__, i,
// fifo_data);
/* FIXME: now we just deal with 32bit width fifo one time */
if (i < data->blocks * data->blocksize) {
memcpy(data->dest + i, &fifo_data, sizeof(fifo_data));
i += sizeof(fifo_data);
} else {
HAL_SDMMC_TRACE(1, "%s: fifo data too much\n", __func__);
ret = -1;
goto out;
}
}
/* nothing to read from fifo and DTO is set */
else if (mask & SDMMCIP_REG_INTMSK_DTO) {
if (i != data->blocks * data->blocksize) {
HAL_SDMMC_TRACE(3, "%s: need to read %d, actually read %d\n",
__func__, data->blocks * data->blocksize, i);
}
ret = 0;
goto out;
}
} else {
/* nothing to write to fifo and DTO is set */
if (mask & SDMMCIP_REG_INTMSK_DTO) {
/* check if number is right */
if (i != data->blocks * data->blocksize) {
HAL_SDMMC_TRACE(3, "%s: need to write %d, actually written %d\n",
__func__, data->blocks * data->blocksize, i);
}
ret = 0;
goto out;
} else if (!(status & SDMMCIP_REG_FIFO_COUNT_MASK)) {
/* FIXME: now we just deal with 32bit width fifo one time */
if (i < data->blocks * data->blocksize) {
memcpy(&fifo_data, data->src + i, sizeof(fifo_data));
// HAL_SDMMC_TRACE(4,"%s: fifo %d, count %d, write -> 0x%x\n",
// __func__, ((status &
// SDMMCIP_REG_FIFO_COUNT_MASK)>>SDMMCIP_REG_FIFO_COUNT_SHIFT), i,
// fifo_data);
i += sizeof(fifo_data);
sdmmcip_writel(host, SDMMCIP_REG_FIFO_OFFSET, fifo_data);
} else {
HAL_SDMMC_TRACE(1, "%s: no data to write to fifo, do nothing\n",
__func__);
}
}
}
}
}
#endif
#ifdef HAL_SDMMC_USE_DMA
while (host->sdmmc_dma_lock)
;
ctrl = sdmmcip_readl(host, SDMMCIP_REG_CTRL);
ctrl |= SDMMCIP_REG_CTRL_DMA_RESET;
sdmmcip_wait_reset(host, ctrl);
if (data) {
hal_gpdma_free_chan(host->dma_ch);
#if 0
if (data->flags & MMC_DATA_READ) {
for (int ccc = 0; ccc < 400; ++ccc) {
HAL_SDMMC_TRACE(2,"%d-0x%x ", ccc, data->dest[ccc]);
}
HAL_SDMMC_TRACE(0,"\n");
}
#endif
}
#endif
#ifndef HAL_SDMMC_USE_DMA
out:
#endif
mask = sdmmcip_readl(host, SDMMCIP_REG_RINTSTS);
sdmmcip_writel(host, SDMMCIP_REG_RINTSTS, mask & (~SDMMCIP_REG_INTMSK_CD));
ctrl = sdmmcip_readl(host, SDMMCIP_REG_CTRL);
ctrl &= ~(SDMMCIP_REG_DMA_EN);
sdmmcip_writel(host, SDMMCIP_REG_CTRL, ctrl);
mmc_udelay(100);
return ret;
}
static int sdmmcip_setup_bus(struct sdmmcip_host *host, uint32_t freq) {
uint32_t div, status;
int timeout = 10000;
unsigned long sclk;
if ((freq == host->clock) || (freq == 0))
return 0;
/*
* If host->get_mmc_clk isn't defined,
* then assume that host->bus_hz is source clock value.
* host->bus_hz should be set by user.
*/
if (host->get_mmc_clk)
sclk = host->get_mmc_clk(host);
else if (host->bus_hz)
sclk = host->bus_hz;
else {
HAL_SDMMC_TRACE(1, "%s: Didn't get source clock value.\n", __func__);
return -EINVAL;
}
if (sclk == freq)
div = 0; /* bypass mode */
else
div = _SDMMC_DIV_ROUND_UP(sclk, 2 * freq);
HAL_SDMMC_TRACE(4, "%s : freq %d, sclk %ld, div %d\n", __func__, freq, sclk,
div);
sdmmcip_writel(host, SDMMCIP_REG_CLKENA, 0);
// sdmmcip_writel(host, SDMMCIP_REG_CLKSRC, 0);
// sdmmcip_writel(host, SDMMCIP_REG_CLKSRC, 0x03000000);
// sdmmcip_writel(host, SDMMCIP_REG_CLKSRC, 0x11000000);
sdmmcip_writel(host, SDMMCIP_REG_CLKDIV, div);
sdmmcip_writel(host, SDMMCIP_REG_CMD,
SDMMCIP_REG_CMD_PRV_DAT_WAIT | SDMMCIP_REG_CMD_UPD_CLK |
SDMMCIP_REG_CMD_START);
do {
status = sdmmcip_readl(host, SDMMCIP_REG_CMD);
if (timeout-- < 0) {
HAL_SDMMC_TRACE(1, "%s: Timeout!\n", __func__);
return -ETIMEDOUT;
}
} while (status & SDMMCIP_REG_CMD_START);
sdmmcip_writel(host, SDMMCIP_REG_CLKENA,
SDMMCIP_REG_CLKEN_ENABLE | SDMMCIP_REG_CLKEN_LOW_PWR);
// sdmmcip_writel(host, SDMMCIP_REG_CLKENA, SDMMCIP_REG_CLKEN_ENABLE);
sdmmcip_writel(host, SDMMCIP_REG_CMD,
SDMMCIP_REG_CMD_PRV_DAT_WAIT | SDMMCIP_REG_CMD_UPD_CLK |
SDMMCIP_REG_CMD_START);
timeout = 10000;
do {
status = sdmmcip_readl(host, SDMMCIP_REG_CMD);
if (timeout-- < 0) {
HAL_SDMMC_TRACE(1, "%s: Timeout!\n", __func__);
return -ETIMEDOUT;
}
} while (status & SDMMCIP_REG_CMD_START);
host->clock = freq;
return 0;
}
static void sdmmcip_set_ios(struct mmc *mmc) {
struct sdmmcip_host *host = (struct sdmmcip_host *)mmc->priv;
uint32_t ctype, regs;
HAL_SDMMC_TRACE(2, "Buswidth = %d, clock: %d\n", mmc->bus_width, mmc->clock);
sdmmcip_setup_bus(host, mmc->clock);
switch (mmc->bus_width) {
case 8:
ctype = SDMMCIP_REG_CTYPE_8BIT;
break;
case 4:
ctype = SDMMCIP_REG_CTYPE_4BIT;
break;
default:
ctype = SDMMCIP_REG_CTYPE_1BIT;
break;
}
sdmmcip_writel(host, SDMMCIP_REG_CTYPE, ctype);
regs = sdmmcip_readl(host, SDMMCIP_REG_UHS_REG);
if (mmc->ddr_mode)
regs |= SDMMCIP_REG_DDR_MODE;
else
regs &= ~SDMMCIP_REG_DDR_MODE;
sdmmcip_writel(host, SDMMCIP_REG_UHS_REG, regs);
if (host->clksel)
host->clksel(host);
}
static int sdmmcip_init(struct mmc *mmc) {
struct sdmmcip_host *host = mmc->priv;
if (host->board_init)
host->board_init(host);
HAL_SDMMC_TRACE(2, "host->ioaddr %x, SDMMCIP_REG_PWREN %x\n", host->ioaddr,
SDMMCIP_REG_PWREN);
sdmmcip_writel(host, SDMMCIP_REG_PWREN, 1);
if (!sdmmcip_wait_reset(host, SDMMCIP_REG_RESET_ALL)) {
HAL_SDMMC_TRACE(2, "%s[%d] Fail-reset!!\n", __func__, __LINE__);
return -1;
}
/* Enumerate at 400KHz */
sdmmcip_setup_bus(host, mmc->cfg->f_min);
if (host->detect_enb) {
sdmmcip_writel(host, SDMMCIP_REG_RINTSTS,
SDMMCIP_REG_INTMSK_ALL & (~SDMMCIP_REG_INTMSK_CD));
sdmmcip_writel(host, SDMMCIP_REG_INTMASK, SDMMCIP_REG_INTMSK_CD);
sdmmcip_writel(host, SDMMCIP_REG_CTRL, SDMMCIP_REG_INT_EN);
} else {
sdmmcip_writel(host, SDMMCIP_REG_RINTSTS, SDMMCIP_REG_INTMSK_ALL);
sdmmcip_writel(host, SDMMCIP_REG_INTMASK, ~SDMMCIP_REG_INTMSK_ALL);
}
sdmmcip_writel(host, SDMMCIP_REG_TMOUT, 0xFFFFFFFF);
sdmmcip_writel(host, SDMMCIP_REG_IDINTEN, 0);
sdmmcip_writel(host, SDMMCIP_REG_BMOD, 1);
sdmmcip_writel(host, SDMMCIP_REG_FIFOTH, host->fifoth_val);
sdmmcip_writel(host, SDMMCIP_REG_CLKENA, 0);
// sdmmcip_writel(host, SDMMCIP_REG_CLKSRC, 0);
// sdmmcip_writel(host, SDMMCIP_REG_CLKSRC, 0x03000000);
// sdmmcip_writel(host, SDMMCIP_REG_CLKSRC, 0x11000000);
sdmmcip_writel(host, SDMMCIP_REG_RESET_CARD, 0);
mmc_udelay(1000);
sdmmcip_writel(host, SDMMCIP_REG_RESET_CARD, 1);
mmc_udelay(1000);
mmc_udelay(1000);
sdmmcip_writel(host, SDMMCIP_REG_RESET_CARD, 0);
// sdmmcip_writel(host, SDMMCIP_REG_RESET_CARD, 1);
return 0;
}
static const struct mmc_ops sdmmcip_ops = {
.send_cmd = sdmmcip_send_cmd,
.set_ios = sdmmcip_set_ios,
.init = sdmmcip_init,
};
int board_mmc_getcd(struct mmc *mmc) { return -1; }
int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) {
int ret;
int i;
uint8_t *ptr;
HAL_SDMMC_TRACE(1, "CMD_SEND:%d\n", cmd->cmdidx);
HAL_SDMMC_TRACE(1, "\t\tARG\t\t\t 0x%08X\n", cmd->cmdarg);
ret = mmc->cfg->ops->send_cmd(mmc, cmd, data);
HAL_SDMMC_TRACE(0, "send cmd end...");
switch (cmd->resp_type) {
case MMC_RSP_NONE:
HAL_SDMMC_TRACE(0, "\t\tMMC_RSP_NONE\n");
break;
case MMC_RSP_R1:
HAL_SDMMC_TRACE(1, "\t\tMMC_RSP_R1,5,6,7 \t 0x%08X \n", cmd->response[0]);
break;
case MMC_RSP_R1b:
HAL_SDMMC_TRACE(1, "\t\tMMC_RSP_R1b\t\t 0x%08X \n", cmd->response[0]);
break;
case MMC_RSP_R2:
HAL_SDMMC_TRACE(1, "\t\tMMC_RSP_R2\t\t 0x%08X \n", cmd->response[0]);
HAL_SDMMC_TRACE(1, "\t\t \t\t 0x%08X \n", cmd->response[1]);
HAL_SDMMC_TRACE(1, "\t\t \t\t 0x%08X \n", cmd->response[2]);
HAL_SDMMC_TRACE(1, "\t\t \t\t 0x%08X \n", cmd->response[3]);
HAL_SDMMC_TRACE(0, "\n");
HAL_SDMMC_TRACE(0, "\t\t\t\t\tDUMPING DATA\n");
for (i = 0; i < 4; i++) {
int j;
HAL_SDMMC_TRACE(1, "\t\t\t\t\t%03d - ", i * 4);
ptr = (uint8_t *)&cmd->response[i];
ptr += 3;
for (j = 0; j < 4; j++)
HAL_SDMMC_TRACE(1, "%02X ", *ptr--);
HAL_SDMMC_TRACE(0, "\n");
}
break;
case MMC_RSP_R3:
HAL_SDMMC_TRACE(1, "\t\tMMC_RSP_R3,4\t\t 0x%08X \n", cmd->response[0]);
break;
default:
HAL_SDMMC_TRACE(0, "\t\tERROR MMC rsp not supported\n");
break;
}
return ret;
}
int mmc_send_status(struct mmc *mmc, int timeout) {
struct mmc_cmd cmd;
int err, retries = 5;
int POSSIBLY_UNUSED status;
cmd.cmdidx = MMC_CMD_SEND_STATUS;
cmd.resp_type = MMC_RSP_R1;
if (!sdmmc_host_is_spi(mmc))
cmd.cmdarg = mmc->rca << 16;
while (1) {
err = mmc_send_cmd(mmc, &cmd, NULL);
if (!err) {
if ((cmd.response[0] & MMC_STATUS_RDY_FOR_DATA) &&
(cmd.response[0] & MMC_STATUS_CURR_STATE) != MMC_STATE_PRG)
break;
else if (cmd.response[0] & MMC_STATUS_MASK) {
return COMM_ERR;
}
} else if (--retries < 0)
return err;
if (timeout-- <= 0)
break;
mmc_udelay(1000);
}
status = (cmd.response[0] & MMC_STATUS_CURR_STATE) >> 9;
HAL_SDMMC_TRACE(1, "CURR STATE:%d\n", status);
if (timeout <= 0) {
return TIMEOUT;
}
if (cmd.response[0] & MMC_STATUS_SWITCH_ERROR)
return SWITCH_ERR;
return 0;
}
int mmc_set_blocklen(struct mmc *mmc, int len) {
struct mmc_cmd cmd;
if (mmc->ddr_mode)
return 0;
cmd.cmdidx = MMC_CMD_SET_BLOCKLEN;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = len;
return mmc_send_cmd(mmc, &cmd, NULL);
}
struct mmc *find_mmc_device(int dev_num) { return &sdmmc_devices[dev_num]; }
static int mmc_read_blocks(struct mmc *mmc, void *dst, uint32_t start,
uint32_t blkcnt) {
struct mmc_cmd cmd;
struct mmc_data data;
if (blkcnt > 1)
cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
else
cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;
if (mmc->high_capacity)
cmd.cmdarg = start;
else
cmd.cmdarg = start * mmc->read_bl_len;
cmd.resp_type = MMC_RSP_R1;
data.dest = dst;
data.blocks = blkcnt;
data.blocksize = mmc->read_bl_len;
data.flags = MMC_DATA_READ;
if (mmc_send_cmd(mmc, &cmd, &data))
return 0;
if (blkcnt > 1) {
cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
cmd.cmdarg = 0;
cmd.resp_type = MMC_RSP_R1b;
if (mmc_send_cmd(mmc, &cmd, NULL)) {
return 0;
}
}
return blkcnt;
}
static unsigned long mmc_bread(int dev_num, uint32_t start, uint32_t blkcnt,
void *dst) {
uint32_t cur, blocks_todo = blkcnt;
if (blkcnt == 0)
return 0;
struct mmc *mmc = find_mmc_device(dev_num);
if (!mmc)
return 0;
if ((start + blkcnt) > mmc->block_dev.lba) {
return 0;
}
if (mmc_set_blocklen(mmc, mmc->read_bl_len))
return 0;
do {
cur = (blocks_todo > mmc->cfg->b_max) ? mmc->cfg->b_max : blocks_todo;
if (mmc_read_blocks(mmc, dst, start, cur) != cur)
return 0;
blocks_todo -= cur;
start += cur;
dst += cur * mmc->read_bl_len;
} while (blocks_todo > 0);
return blkcnt;
}
static unsigned long mmc_erase_t(struct mmc *mmc, unsigned long start,
uint32_t blkcnt) {
struct mmc_cmd cmd;
unsigned long end;
int err, start_cmd, end_cmd;
if (mmc->high_capacity) {
end = start + blkcnt - 1;
} else {
end = (start + blkcnt - 1) * mmc->write_bl_len;
start *= mmc->write_bl_len;
}
if (IS_SD(mmc)) {
start_cmd = SD_CMD_ERASE_WR_BLK_START;
end_cmd = SD_CMD_ERASE_WR_BLK_END;
} else {
start_cmd = MMC_CMD_ERASE_GROUP_START;
end_cmd = MMC_CMD_ERASE_GROUP_END;
}
cmd.cmdidx = start_cmd;
cmd.cmdarg = start;
cmd.resp_type = MMC_RSP_R1;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
goto err_out;
cmd.cmdidx = end_cmd;
cmd.cmdarg = end;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
goto err_out;
cmd.cmdidx = MMC_CMD_ERASE;
cmd.cmdarg = SECURE_ERASE;
cmd.resp_type = MMC_RSP_R1b;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
goto err_out;
return 0;
err_out:
// puts("mmc erase failed\n");
return err;
}
unsigned long mmc_berase(int dev_num, uint32_t start, uint32_t blkcnt) {
int err = 0;
struct mmc *mmc = find_mmc_device(dev_num);
uint32_t blk = 0, blk_r = 0;
int timeout = 1000;
if (!mmc)
return -1;
if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size))
// printf("\n\nCaution! Your devices Erase group is 0x%x\n"
// "The erase range would be change to "
// "0x" LBAF "~0x" LBAF "\n\n",
// mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1),
// ((start + blkcnt + mmc->erase_grp_size)
// & ~(mmc->erase_grp_size - 1)) - 1);
while (blk < blkcnt) {
blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ? mmc->erase_grp_size
: (blkcnt - blk);
err = mmc_erase_t(mmc, start + blk, blk_r);
if (err)
break;
blk += blk_r;
/* Waiting for the ready status */
if (mmc_send_status(mmc, timeout))
return 0;
}
return blk;
}
static unsigned long mmc_write_blocks(struct mmc *mmc, uint32_t start,
uint32_t blkcnt, const void *src) {
struct mmc_cmd cmd;
struct mmc_data data;
int timeout = 1000;
if ((start + blkcnt) > mmc->block_dev.lba) {
return 0;
}
if (blkcnt == 0)
return 0;
else if (blkcnt == 1)
cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK;
else
cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
if (mmc->high_capacity)
cmd.cmdarg = start;
else
cmd.cmdarg = start * mmc->write_bl_len;
cmd.resp_type = MMC_RSP_R1;
data.src = src;
data.blocks = blkcnt;
data.blocksize = mmc->write_bl_len;
data.flags = MMC_DATA_WRITE;
if (mmc_send_cmd(mmc, &cmd, &data)) {
HAL_SDMMC_TRACE(0, "mmc write failed\n");
return 0;
}
/* SPI multiblock writes terminate using a special
* token, not a STOP_TRANSMISSION request.
*/
if (!sdmmc_host_is_spi(mmc) && blkcnt > 1) {
cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
cmd.cmdarg = 0;
cmd.resp_type = MMC_RSP_R1b;
if (mmc_send_cmd(mmc, &cmd, NULL)) {
HAL_SDMMC_TRACE(0, "mmc fail to send stop cmd\n");
return 0;
}
}
/* Waiting for the ready status */
if (mmc_send_status(mmc, timeout))
return 0;
return blkcnt;
}
unsigned long mmc_bwrite(int dev_num, uint32_t start, uint32_t blkcnt,
const void *src) {
uint32_t cur, blocks_todo = blkcnt;
struct mmc *mmc = find_mmc_device(dev_num);
if (!mmc)
return 0;
if (mmc_set_blocklen(mmc, mmc->write_bl_len))
return 0;
do {
cur = (blocks_todo > mmc->cfg->b_max) ? mmc->cfg->b_max : blocks_todo;
if (mmc_write_blocks(mmc, start, cur, src) != cur)
return 0;
blocks_todo -= cur;
start += cur;
src += cur * mmc->write_bl_len;
} while (blocks_todo > 0);
return blkcnt;
}
static int mmc_go_idle(struct mmc *mmc) {
struct mmc_cmd cmd;
int err;
mmc_udelay(1000);
cmd.cmdidx = MMC_CMD_GO_IDLE_STATE;
cmd.cmdarg = 0;
cmd.resp_type = MMC_RSP_NONE;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
mmc_udelay(2000);
return 0;
}
static int sd_send_op_cond(struct mmc *mmc) {
int timeout = 1000;
int err;
struct mmc_cmd cmd;
while (1) {
cmd.cmdidx = MMC_CMD_APP_CMD;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
cmd.cmdidx = SD_CMD_APP_SEND_OP_COND;
cmd.resp_type = MMC_RSP_R3;
/*
* Most cards do not answer if some reserved bits
* in the ocr are set. However, Some controller
* can set bit 7 (reserved for low voltages), but
* how to manage low voltages SD card is not yet
* specified.
*/
cmd.cmdarg = sdmmc_host_is_spi(mmc) ? 0 : (mmc->cfg->voltages & 0xff8000);
if (mmc->version == SD_VERSION_2)
cmd.cmdarg |= OCR_HCS;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
if (cmd.response[0] & OCR_BUSY)
break;
if (timeout-- <= 0)
return UNUSABLE_ERR;
mmc_udelay(1000);
}
if (mmc->version != SD_VERSION_2)
mmc->version = SD_VERSION_1_0;
if (sdmmc_host_is_spi(mmc)) { /* read OCR for spi */
cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
cmd.resp_type = MMC_RSP_R3;
cmd.cmdarg = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
}
mmc->ocr = cmd.response[0];
mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
mmc->rca = 0;
return 0;
}
static int mmc_send_op_cond_iter(struct mmc *mmc, int use_arg) {
struct mmc_cmd cmd;
int err;
cmd.cmdidx = MMC_CMD_SEND_OP_COND;
cmd.resp_type = MMC_RSP_R3;
cmd.cmdarg = 0;
if (use_arg && !sdmmc_host_is_spi(mmc))
cmd.cmdarg = OCR_HCS |
(mmc->cfg->voltages & (mmc->ocr & OCR_VOLTAGE_MASK)) |
(mmc->ocr & OCR_ACCESS_MODE);
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
mmc->ocr = cmd.response[0];
return 0;
}
static int mmc_send_op_cond(struct mmc *mmc) {
int err, i;
/* Some cards seem to need this */
// mmc_go_idle(mmc);
/* Asking to the card its capabilities */
for (i = 0; i < 2; i++) {
err = mmc_send_op_cond_iter(mmc, i != 0);
if (err)
return err;
/* exit if not busy (flag seems to be inverted) */
if (mmc->ocr & OCR_BUSY)
break;
}
mmc->op_cond_pending = 1;
return 0;
}
static int mmc_complete_op_cond(struct mmc *mmc) {
struct mmc_cmd cmd;
int err;
mmc->op_cond_pending = 0;
if (!(mmc->ocr & OCR_BUSY)) {
while (1) {
err = mmc_send_op_cond_iter(mmc, 1);
if (err)
return err;
if (mmc->ocr & OCR_BUSY)
break;
mmc_udelay(100);
}
}
if (sdmmc_host_is_spi(mmc)) { /* read OCR for spi */
cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
cmd.resp_type = MMC_RSP_R3;
cmd.cmdarg = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
mmc->ocr = cmd.response[0];
}
mmc->version = MMC_VERSION_UNKNOWN;
mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
mmc->rca = 1;
return 0;
}
static int mmc_send_ext_csd(struct mmc *mmc, uint8_t *ext_csd) {
struct mmc_cmd cmd;
struct mmc_data data;
int err;
/* Get the Card Status Register */
cmd.cmdidx = MMC_CMD_SEND_EXT_CSD;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = 0;
data.dest = (char *)ext_csd;
data.blocks = 1;
data.blocksize = MMC_MAX_BLOCK_LEN;
data.flags = MMC_DATA_READ;
HAL_SDMMC_TRACE(1, "[%s] start...", __func__);
err = mmc_send_cmd(mmc, &cmd, &data);
HAL_SDMMC_TRACE(2, "[%s] err = %d", __func__, err);
return err;
}
static int mmc_switch(struct mmc *mmc, uint8_t set, uint8_t index,
uint8_t value) {
struct mmc_cmd cmd;
int timeout = 1000;
int ret;
cmd.cmdidx = MMC_CMD_SWITCH;
cmd.resp_type = MMC_RSP_R1b;
cmd.cmdarg =
(MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8);
ret = mmc_send_cmd(mmc, &cmd, NULL);
/* Waiting for the ready status */
if (!ret)
ret = mmc_send_status(mmc, timeout);
return ret;
}
static int mmc_change_freq(struct mmc *mmc) {
_SDMMC_ALLOC_CACHE_ALIGN_BUFFER(uint8_t, ext_csd, MMC_MAX_BLOCK_LEN);
char cardtype;
int err;
mmc->card_caps = 0;
if (sdmmc_host_is_spi(mmc))
return 0;
/* Only version 4 supports high-speed */
if (mmc->version < MMC_VERSION_4)
return 0;
mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT;
err = mmc_send_ext_csd(mmc, ext_csd);
if (err)
return err;
cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0xf;
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1);
if (err)
return err == SWITCH_ERR ? 0 : err;
/* Now check to see that it worked */
err = mmc_send_ext_csd(mmc, ext_csd);
if (err)
return err;
/* No high-speed support */
if (!ext_csd[EXT_CSD_HS_TIMING])
return 0;
/* High Speed is set, there are two types: 52MHz and 26MHz */
if (cardtype & EXT_CSD_CARD_TYPE_52) {
if (cardtype & EXT_CSD_CARD_TYPE_DDR_1_8V)
mmc->card_caps |= MMC_MODE_DDR_52MHz;
mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
} else {
mmc->card_caps |= MMC_MODE_HS;
}
return 0;
}
static int mmc_set_capacity(struct mmc *mmc, int part_num) {
switch (part_num) {
case 0:
mmc->capacity = mmc->capacity_user;
break;
case 1:
case 2:
mmc->capacity = mmc->capacity_boot;
break;
case 3:
mmc->capacity = mmc->capacity_rpmb;
break;
case 4:
case 5:
case 6:
case 7:
mmc->capacity = mmc->capacity_gp[part_num - 4];
break;
default:
return -1;
}
mmc->block_dev.lba = _sdmmc_lldiv(mmc->capacity, mmc->read_bl_len);
return 0;
}
int mmc_getcd(struct mmc *mmc) {
int cd;
cd = board_mmc_getcd(mmc);
if (cd < 0) {
if (mmc->cfg->ops->getcd)
cd = mmc->cfg->ops->getcd(mmc);
else
cd = 1;
}
return cd;
}
static int sd_switch(struct mmc *mmc, int mode, int group, uint8_t value,
uint8_t *resp) {
struct mmc_cmd cmd;
struct mmc_data data;
/* Switch the frequency */
cmd.cmdidx = SD_CMD_SWITCH_FUNC;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = (mode << 31) | 0xffffff;
cmd.cmdarg &= ~(0xf << (group * 4));
cmd.cmdarg |= value << (group * 4);
data.dest = (char *)resp;
data.blocksize = 64;
data.blocks = 1;
data.flags = MMC_DATA_READ;
hal_sdmmc_delay(1);
return mmc_send_cmd(mmc, &cmd, &data);
}
static int sd_change_freq(struct mmc *mmc) {
int err;
struct mmc_cmd cmd;
_SDMMC_ALLOC_CACHE_ALIGN_BUFFER(uint32_t, scr, 2);
_SDMMC_ALLOC_CACHE_ALIGN_BUFFER(uint32_t, switch_status, 16);
struct mmc_data data;
int timeout;
mmc->card_caps = 0;
if (sdmmc_host_is_spi(mmc))
return 0;
/* Read the SCR to find out if this card supports higher speeds */
cmd.cmdidx = MMC_CMD_APP_CMD;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = mmc->rca << 16;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
cmd.cmdidx = SD_CMD_APP_SEND_SCR;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = 0;
timeout = 3;
retry_scr:
data.dest = (char *)scr;
data.blocksize = 8;
data.blocks = 1;
data.flags = MMC_DATA_READ;
err = mmc_send_cmd(mmc, &cmd, &data);
if (err) {
if (timeout--)
goto retry_scr;
return err;
}
mmc->scr[0] = _sdmmc_be32_to_cpu(scr[0]);
mmc->scr[1] = _sdmmc_be32_to_cpu(scr[1]);
switch ((mmc->scr[0] >> 24) & 0xf) {
case 0:
mmc->version = SD_VERSION_1_0;
break;
case 1:
mmc->version = SD_VERSION_1_10;
break;
case 2:
mmc->version = SD_VERSION_2;
if ((mmc->scr[0] >> 15) & 0x1)
mmc->version = SD_VERSION_3;
break;
default:
mmc->version = SD_VERSION_1_0;
break;
}
if (mmc->scr[0] & SD_DATA_4BIT)
mmc->card_caps |= MMC_MODE_4BIT;
/* Version 1.0 doesn't support switching */
if (mmc->version == SD_VERSION_1_0)
return 0;
timeout = 4;
while (timeout--) {
err = sd_switch(mmc, SD_SWITCH_CHECK, 0, 1, (uint8_t *)switch_status);
if (err)
return err;
/* The high-speed function is busy. Try again */
if (!(_sdmmc_be32_to_cpu(switch_status[7]) & SD_HIGHSPEED_BUSY))
break;
}
/* If high-speed isn't supported, we return */
if (!(_sdmmc_be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED))
return 0;
/*
* If the host doesn't support SD_HIGHSPEED, do not switch card to
* HIGHSPEED mode even if the card support SD_HIGHSPPED.
* This can avoid furthur problem when the card runs in different
* mode between the host.
*/
if (!((mmc->cfg->host_caps & MMC_MODE_HS_52MHz) &&
(mmc->cfg->host_caps & MMC_MODE_HS)))
return 0;
err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (uint8_t *)switch_status);
if (err)
return err;
if ((_sdmmc_be32_to_cpu(switch_status[4]) & 0x0f000000) == 0x01000000)
mmc->card_caps |= MMC_MODE_HS;
return 0;
}
/* frequency bases */
/* divided by 10 to be nice to platforms without floating point */
static const int fbase[] = {
10000,
100000,
1000000,
10000000,
};
/* Multiplier values for TRAN_SPEED. Multiplied by 10 to be nice
* to platforms without floating point.
*/
static const int multipliers[] = {
0, /* reserved */
10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80,
};
static void mmc_set_ios(struct mmc *mmc) {
if (mmc->cfg->ops->set_ios)
mmc->cfg->ops->set_ios(mmc);
}
void mmc_set_clock(struct mmc *mmc, uint32_t clock) {
if (clock > mmc->cfg->f_max)
clock = mmc->cfg->f_max;
if (clock < mmc->cfg->f_min)
clock = mmc->cfg->f_min;
mmc->clock = clock;
mmc_set_ios(mmc);
}
static void mmc_set_bus_width(struct mmc *mmc, uint32_t width) {
mmc->bus_width = width;
mmc_set_ios(mmc);
}
static int mmc_startup(struct mmc *mmc) {
int err, i;
uint32_t mult, freq;
uint32_t cmult, csize, capacity;
struct mmc_cmd cmd;
_SDMMC_ALLOC_CACHE_ALIGN_BUFFER(uint8_t, ext_csd, MMC_MAX_BLOCK_LEN);
_SDMMC_ALLOC_CACHE_ALIGN_BUFFER(uint8_t, test_csd, MMC_MAX_BLOCK_LEN);
int timeout = 1000;
bool has_parts = false;
bool part_completed;
#ifdef CONFIG_MMC_SPI_CRC_ON
if (sdmmc_host_is_spi(mmc)) { /* enable CRC check for spi */
cmd.cmdidx = MMC_CMD_SPI_CRC_ON_OFF;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = 1;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
}
#endif
/* Put the Card in Identify Mode */
cmd.cmdidx = sdmmc_host_is_spi(mmc)
? MMC_CMD_SEND_CID
: MMC_CMD_ALL_SEND_CID; /* cmd not supported in spi */
cmd.resp_type = MMC_RSP_R2;
cmd.cmdarg = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
memcpy(mmc->cid, cmd.response, 16);
/*
* For MMC cards, set the Relative Address.
* For SD cards, get the Relatvie Address.
* This also puts the cards into Standby State
*/
if (!sdmmc_host_is_spi(mmc)) { /* cmd not supported in spi */
cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR;
cmd.cmdarg = mmc->rca << 16;
cmd.resp_type = MMC_RSP_R6;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
if (IS_SD(mmc))
mmc->rca = (cmd.response[0] >> 16) & 0xffff;
}
/* Get the Card-Specific Data */
cmd.cmdidx = MMC_CMD_SEND_CSD;
cmd.resp_type = MMC_RSP_R2;
cmd.cmdarg = mmc->rca << 16;
err = mmc_send_cmd(mmc, &cmd, NULL);
/* Waiting for the ready status */
mmc_send_status(mmc, timeout);
if (err)
return err;
mmc->csd[0] = cmd.response[0];
mmc->csd[1] = cmd.response[1];
mmc->csd[2] = cmd.response[2];
mmc->csd[3] = cmd.response[3];
if (mmc->version == MMC_VERSION_UNKNOWN) {
int version = (cmd.response[0] >> 26) & 0xf;
switch (version) {
case 0:
mmc->version = MMC_VERSION_1_2;
break;
case 1:
mmc->version = MMC_VERSION_1_4;
break;
case 2:
mmc->version = MMC_VERSION_2_2;
break;
case 3:
mmc->version = MMC_VERSION_3;
break;
case 4:
mmc->version = MMC_VERSION_4;
break;
default:
mmc->version = MMC_VERSION_1_2;
break;
}
}
/* divide frequency by 10, since the mults are 10x bigger */
freq = fbase[(cmd.response[0] & 0x7)];
mult = multipliers[((cmd.response[0] >> 3) & 0xf)];
mmc->tran_speed = freq * mult;
mmc->dsr_imp = ((cmd.response[1] >> 12) & 0x1);
mmc->read_bl_len = 1 << ((cmd.response[1] >> 16) & 0xf);
if (IS_SD(mmc))
mmc->write_bl_len = mmc->read_bl_len;
else
mmc->write_bl_len = 1 << ((cmd.response[3] >> 22) & 0xf);
if (mmc->high_capacity) {
csize = (mmc->csd[1] & 0x3f) << 16 | (mmc->csd[2] & 0xffff0000) >> 16;
cmult = 8;
} else {
csize = (mmc->csd[1] & 0x3ff) << 2 | (mmc->csd[2] & 0xc0000000) >> 30;
cmult = (mmc->csd[2] & 0x00038000) >> 15;
}
mmc->capacity_user = (csize + 1) << (cmult + 2);
mmc->capacity_user *= mmc->read_bl_len;
mmc->capacity_boot = 0;
mmc->capacity_rpmb = 0;
for (i = 0; i < 4; i++)
mmc->capacity_gp[i] = 0;
if (mmc->read_bl_len > MMC_MAX_BLOCK_LEN)
mmc->read_bl_len = MMC_MAX_BLOCK_LEN;
if (mmc->write_bl_len > MMC_MAX_BLOCK_LEN)
mmc->write_bl_len = MMC_MAX_BLOCK_LEN;
if ((mmc->dsr_imp) && (0xffffffff != mmc->dsr)) {
cmd.cmdidx = MMC_CMD_SET_DSR;
cmd.cmdarg = (mmc->dsr & 0xffff) << 16;
cmd.resp_type = MMC_RSP_NONE;
if (mmc_send_cmd(mmc, &cmd, NULL)) {
HAL_SDMMC_TRACE(0, "MMC: SET_DSR failed\n");
}
}
/* Select the card, and put it into Transfer Mode */
if (!sdmmc_host_is_spi(mmc)) { /* cmd not supported in spi */
cmd.cmdidx = MMC_CMD_SELECT_CARD;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = mmc->rca << 16;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
}
/*
* For SD, its erase group is always one sector
*/
mmc->erase_grp_size = 1;
mmc->part_config = MMCPART_NOAVAILABLE;
if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) {
/* check ext_csd version and capacity */
err = mmc_send_ext_csd(mmc, ext_csd);
if (err)
return err;
if (ext_csd[EXT_CSD_REV] >= 2) {
/*
* According to the JEDEC Standard, the value of
* ext_csd's capacity is valid if the value is more
* than 2GB
*/
capacity = ext_csd[EXT_CSD_SEC_CNT] << 0 |
ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
ext_csd[EXT_CSD_SEC_CNT + 2] << 16 |
ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
capacity *= MMC_MAX_BLOCK_LEN;
if ((capacity >> 20) > 2 * 1024)
mmc->capacity_user = capacity;
}
switch (ext_csd[EXT_CSD_REV]) {
case 1:
mmc->version = MMC_VERSION_4_1;
break;
case 2:
mmc->version = MMC_VERSION_4_2;
break;
case 3:
mmc->version = MMC_VERSION_4_3;
break;
case 5:
mmc->version = MMC_VERSION_4_41;
break;
case 6:
mmc->version = MMC_VERSION_4_5;
break;
case 7:
mmc->version = MMC_VERSION_5_0;
break;
}
/* The partition data may be non-zero but it is only
* effective if PARTITION_SETTING_COMPLETED is set in
* EXT_CSD, so ignore any data if this bit is not set,
* except for enabling the high-capacity group size
* definition (see below). */
part_completed = !!(ext_csd[EXT_CSD_PARTITION_SETTING] &
EXT_CSD_PARTITION_SETTING_COMPLETED);
/* store the partition info of emmc */
mmc->part_support = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) ||
ext_csd[EXT_CSD_BOOT_MULT])
mmc->part_config = ext_csd[EXT_CSD_PART_CONF];
if (part_completed &&
(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & ENHNCD_SUPPORT))
mmc->part_attr = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE];
mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17;
mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17;
for (i = 0; i < 4; i++) {
int idx = EXT_CSD_GP_SIZE_MULT + i * 3;
uint32_t mult =
(ext_csd[idx + 2] << 16) + (ext_csd[idx + 1] << 8) + ext_csd[idx];
if (mult)
has_parts = true;
if (!part_completed)
continue;
mmc->capacity_gp[i] = mult;
mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
mmc->capacity_gp[i] <<= 19;
}
if (part_completed) {
mmc->enh_user_size = (ext_csd[EXT_CSD_ENH_SIZE_MULT + 2] << 16) +
(ext_csd[EXT_CSD_ENH_SIZE_MULT + 1] << 8) +
ext_csd[EXT_CSD_ENH_SIZE_MULT];
mmc->enh_user_size *= ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
mmc->enh_user_size *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
mmc->enh_user_size <<= 19;
mmc->enh_user_start = (ext_csd[EXT_CSD_ENH_START_ADDR + 3] << 24) +
(ext_csd[EXT_CSD_ENH_START_ADDR + 2] << 16) +
(ext_csd[EXT_CSD_ENH_START_ADDR + 1] << 8) +
ext_csd[EXT_CSD_ENH_START_ADDR];
if (mmc->high_capacity)
mmc->enh_user_start <<= 9;
}
/*
* Host needs to enable ERASE_GRP_DEF bit if device is
* partitioned. This bit will be lost every time after a reset
* or power off. This will affect erase size.
*/
if (part_completed)
has_parts = true;
if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) &&
(ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB))
has_parts = true;
if (has_parts) {
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_ERASE_GROUP_DEF, 1);
if (err)
return err;
else
ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1;
}
if (ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01) {
/* Read out group size from ext_csd */
mmc->erase_grp_size = ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024;
/*
* if high capacity and partition setting completed
* SEC_COUNT is valid even if it is smaller than 2 GiB
* JEDEC Standard JESD84-B45, 6.2.4
*/
if (mmc->high_capacity && part_completed) {
capacity = (ext_csd[EXT_CSD_SEC_CNT]) |
(ext_csd[EXT_CSD_SEC_CNT + 1] << 8) |
(ext_csd[EXT_CSD_SEC_CNT + 2] << 16) |
(ext_csd[EXT_CSD_SEC_CNT + 3] << 24);
capacity *= MMC_MAX_BLOCK_LEN;
mmc->capacity_user = capacity;
}
} else {
/* Calculate the group size from the csd value. */
int erase_gsz, erase_gmul;
erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10;
erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5;
mmc->erase_grp_size = (erase_gsz + 1) * (erase_gmul + 1);
}
mmc->hc_wp_grp_size = 1024 * ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] *
ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET];
}
err = mmc_set_capacity(mmc, mmc->part_num);
if (err)
return err;
if (IS_SD(mmc))
err = sd_change_freq(mmc);
else
err = mmc_change_freq(mmc);
if (err)
return err;
/* Restrict card's capabilities by what the host can do */
mmc->card_caps &= mmc->cfg->host_caps;
if (IS_SD(mmc)) {
#ifdef __BUS_WIDTH_SUPPORT_4BIT__
if (mmc->card_caps & MMC_MODE_4BIT) {
cmd.cmdidx = MMC_CMD_APP_CMD;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = mmc->rca << 16;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = 2;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
mmc_set_bus_width(mmc, 4);
}
#endif
if (mmc->card_caps & MMC_MODE_HS)
mmc->tran_speed = 50000000;
else
mmc->tran_speed = 25000000;
} else if (mmc->version >= MMC_VERSION_4) {
/* Only version 4 of MMC supports wider bus widths */
int idx;
/* An array of possible bus widths in order of preference */
static unsigned ext_csd_bits[] = {
EXT_CSD_DDR_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_4, EXT_CSD_BUS_WIDTH_8,
EXT_CSD_BUS_WIDTH_4, EXT_CSD_BUS_WIDTH_1,
};
/* An array to map CSD bus widths to host cap bits */
static unsigned ext_to_hostcaps[] = {
[EXT_CSD_DDR_BUS_WIDTH_4] = MMC_MODE_DDR_52MHz | MMC_MODE_4BIT,
[EXT_CSD_DDR_BUS_WIDTH_8] = MMC_MODE_DDR_52MHz | MMC_MODE_8BIT,
[EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT,
[EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT,
};
/* An array to map chosen bus width to an integer */
static unsigned widths[] = {
8, 4, 8, 4, 1,
};
for (idx = 0; idx < ARRAY_SIZE(ext_csd_bits); idx++) {
unsigned int extw = ext_csd_bits[idx];
unsigned int caps = ext_to_hostcaps[extw];
/*
* If the bus width is still not changed,
* don't try to set the default again.
* Otherwise, recover from switch attempts
* by switching to 1-bit bus width.
*/
if (extw == EXT_CSD_BUS_WIDTH_1 && mmc->bus_width == 1) {
err = 0;
break;
}
/*
* Check to make sure the card and controller support
* these capabilities
*/
if ((mmc->card_caps & caps) != caps)
continue;
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, extw);
if (err)
continue;
mmc->ddr_mode = (caps & MMC_MODE_DDR_52MHz) ? 1 : 0;
mmc_set_bus_width(mmc, widths[idx]);
err = mmc_send_ext_csd(mmc, test_csd);
if (err)
continue;
/* Only compare read only fields */
if (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] ==
test_csd[EXT_CSD_PARTITIONING_SUPPORT] &&
ext_csd[EXT_CSD_HC_WP_GRP_SIZE] == test_csd[EXT_CSD_HC_WP_GRP_SIZE] &&
ext_csd[EXT_CSD_REV] == test_csd[EXT_CSD_REV] &&
ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] ==
test_csd[EXT_CSD_HC_ERASE_GRP_SIZE] &&
memcmp(&ext_csd[EXT_CSD_SEC_CNT], &test_csd[EXT_CSD_SEC_CNT], 4) == 0)
break;
else
err = SWITCH_ERR;
}
if (err)
return err;
if (mmc->card_caps & MMC_MODE_HS) {
if (mmc->card_caps & MMC_MODE_HS_52MHz)
mmc->tran_speed = 52000000;
else
mmc->tran_speed = 26000000;
}
}
mmc_set_clock(mmc, mmc->tran_speed);
/* Fix the block length for DDR mode */
if (mmc->ddr_mode) {
mmc->read_bl_len = MMC_MAX_BLOCK_LEN;
mmc->write_bl_len = MMC_MAX_BLOCK_LEN;
}
/* fill in device description */
mmc->block_dev.lun = 0;
mmc->block_dev.type = 0;
mmc->block_dev.blksz = mmc->read_bl_len;
// mmc->block_dev.log2blksz = LOG2(mmc->block_dev.blksz);
mmc->block_dev.lba = _sdmmc_lldiv(mmc->capacity, mmc->read_bl_len);
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
sprintf(mmc->block_dev.vendor, "Man %06x Snr %04x%04x",
(unsigned)(mmc->cid[0] >> 24), (unsigned)(mmc->cid[2] & 0xffff),
(unsigned)((mmc->cid[3] >> 16) & 0xffff));
sprintf(mmc->block_dev.product, "%c%c%c%c%c%c", (char)mmc->cid[0],
(char)(mmc->cid[1] >> 24), (char)(mmc->cid[1] >> 16),
(char)(mmc->cid[1] >> 8), (char)mmc->cid[1],
(char)(mmc->cid[2] >> 24));
sprintf(mmc->block_dev.revision, "%d.%d",
(unsigned)((mmc->cid[2] >> 20) & 0xf),
(unsigned)((mmc->cid[2] >> 16) & 0xf));
#else
mmc->block_dev.vendor[0] = 0;
mmc->block_dev.product[0] = 0;
mmc->block_dev.revision[0] = 0;
#endif
HAL_SDMMC_TRACE(1, "%s done\n", __func__);
return 0;
}
static int mmc_send_if_cond(struct mmc *mmc) {
struct mmc_cmd cmd;
int err;
cmd.cmdidx = SD_CMD_SEND_IF_COND;
/* We set the bit if the host supports voltages between 2.7 and 3.6 V */
cmd.cmdarg = ((mmc->cfg->voltages & 0xff8000) != 0) << 8 | 0xaa;
cmd.resp_type = MMC_RSP_R7;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
if ((cmd.response[0] & 0xff) != 0xaa)
return UNUSABLE_ERR;
else
mmc->version = SD_VERSION_2;
return 0;
}
struct mmc *mmc_create(int id, const struct mmc_config *cfg, void *priv) {
struct mmc *mmc;
/* quick validation */
if (cfg == NULL || cfg->ops == NULL || cfg->ops->send_cmd == NULL ||
cfg->f_min == 0 || cfg->f_max == 0 || cfg->b_max == 0)
return NULL;
mmc = &sdmmc_devices[id];
if (mmc == NULL)
return NULL;
mmc->cfg = cfg;
mmc->priv = priv;
/* the following chunk was mmc_register() */
/* Setup dsr related values */
mmc->dsr_imp = 0;
mmc->dsr = 0xffffffff;
/* Setup the universal parts of the block interface just once */
mmc->block_dev.removable = 1;
/* setup initial part type */
mmc->block_dev.part_type = mmc->cfg->part_type;
return mmc;
}
int mmc_start_init(struct mmc *mmc) {
int err;
/* we pretend there's no card when init is NULL */
if (mmc_getcd(mmc) == 0 || mmc->cfg->ops->init == NULL) {
mmc->has_init = 0;
HAL_SDMMC_TRACE(1, "%s : no card present\n", __func__);
return NO_CARD_ERR;
}
if (mmc->has_init)
return 0;
/* made sure it's not NULL earlier */
err = mmc->cfg->ops->init(mmc);
HAL_SDMMC_TRACE(1, "%s : init done\n", __func__);
if (err)
return err;
mmc->ddr_mode = 0;
mmc_set_bus_width(mmc, 1);
mmc_set_clock(mmc, 1);
/* Reset the Card */
err = mmc_go_idle(mmc);
HAL_SDMMC_TRACE(1, "%s : idle done\n", __func__);
if (err)
return err;
/* The internal partition reset to user partition(0) at every CMD0*/
mmc->part_num = 0;
/* Test for SD version 2 */
err = mmc_send_if_cond(mmc);
/* Now try to get the SD card's operating condition */
err = sd_send_op_cond(mmc);
/* If the command timed out, we check for an MMC card */
if (err == TIMEOUT) {
err = mmc_send_op_cond(mmc);
if (err) {
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
HAL_SDMMC_TRACE(1, "%s : Card did not respond to voltage select!\n",
__func__);
#endif
return UNUSABLE_ERR;
}
}
if (!err)
mmc->init_in_progress = 1;
HAL_SDMMC_TRACE(1, "%s : done\n", __func__);
return err;
}
static int mmc_complete_init(struct mmc *mmc) {
int err = 0;
mmc->init_in_progress = 0;
if (mmc->op_cond_pending)
err = mmc_complete_op_cond(mmc);
if (!err)
err = mmc_startup(mmc);
if (err)
mmc->has_init = 0;
else
mmc->has_init = 1;
return err;
}
void hal_sdmmc_irq_handler(void) {
uint32_t cdetect, mask;
struct sdmmcip_host *host = &sdmmc_host[HAL_SDMMC_ID_0];
mask = sdmmcip_readl(host, SDMMCIP_REG_RINTSTS);
if (mask & SDMMCIP_REG_INTMSK_CD) {
sdmmcip_writel(host, SDMMCIP_REG_RINTSTS, SDMMCIP_REG_INTMSK_CD);
cdetect = sdmmcip_readl(host, SDMMCIP_REG_CDETECT);
HAL_SDMMC_TRACE(3, "%s cdetect:%d mask:%x\n", __func__, cdetect, mask);
if (sdmmc_detected_callback)
sdmmc_detected_callback(cdetect & 0x01 ? 0 : 1);
}
}
int hal_sdmmc_enable_detecter(enum HAL_SDMMC_ID_T id, void (*cb)(uint8_t)) {
uint32_t ctrl;
struct sdmmcip_host *host = NULL;
HAL_SDMMC_TRACE(1, "%s\n", __func__);
sdmmc_detected_callback = cb;
host = &sdmmc_host[id];
host->ioaddr = (void *)sdmmc_ip_base[id];
if (!sdmmcip_wait_reset(host, SDMMCIP_REG_RESET_ALL)) {
HAL_SDMMC_TRACE(2, "%s[%d] Fail-reset!!\n", __func__, __LINE__);
return -1;
}
host->detect_enb = true;
sdmmcip_writel(host, SDMMCIP_REG_RINTSTS, 0xFFFFFFFF);
sdmmcip_writel(host, SDMMCIP_REG_INTMASK, SDMMCIP_REG_INTMSK_CD);
ctrl = SDMMCIP_REG_INT_EN;
sdmmcip_writel(host, SDMMCIP_REG_CTRL, ctrl);
NVIC_SetVector(SDMMC_IRQn, (uint32_t)hal_sdmmc_irq_handler);
NVIC_SetPriority(SDMMC_IRQn, IRQ_PRIORITY_NORMAL);
NVIC_ClearPendingIRQ(SDMMC_IRQn);
NVIC_EnableIRQ(SDMMC_IRQn);
return 0;
}
void hal_sdmmc_disable_detecter(enum HAL_SDMMC_ID_T id) {
uint32_t ctrl = 0;
struct sdmmcip_host *host = NULL;
host = &sdmmc_host[id];
NVIC_DisableIRQ(SDMMC_IRQn);
NVIC_SetVector(SDMMC_IRQn, (uint32_t)NULL);
sdmmcip_writel(host, SDMMCIP_REG_RINTSTS, 0xFFFFFFFF);
sdmmcip_writel(host, SDMMCIP_REG_INTMASK, ~SDMMCIP_REG_INTMSK_ALL);
ctrl = sdmmcip_readl(host, SDMMCIP_REG_CTRL);
ctrl |= (SDMMCIP_REG_DMA_EN);
sdmmcip_writel(host, SDMMCIP_REG_CTRL, ctrl);
host->detect_enb = false;
sdmmc_detected_callback = NULL;
}
int mmc_init(struct mmc *mmc) {
int err = 0;
if (mmc->has_init)
return 0;
if (!mmc->init_in_progress)
err = mmc_start_init(mmc);
if (!err)
err = mmc_complete_init(mmc);
return err;
}
/* hal api */
static void hal_sdmmc_delay(uint32_t ms) {
if (sdmmc_delay) {
sdmmc_delay(ms);
} else {
hal_sys_timer_delay(MS_TO_TICKS(ms));
}
}
HAL_SDMMC_DELAY_FUNC hal_sdmmc_set_delay_func(HAL_SDMMC_DELAY_FUNC new_func) {
HAL_SDMMC_DELAY_FUNC old_func = sdmmc_delay;
sdmmc_delay = new_func;
return old_func;
}
int32_t hal_sdmmc_open(enum HAL_SDMMC_ID_T id) {
int32_t ret = 0;
struct sdmmcip_host *host = NULL;
HAL_SDMMC_ASSERT(id < HAL_SDMMC_ID_NUM, invalid_id, id);
hal_cmu_sdmmc_set_freq(HAL_CMU_PERIPH_FREQ_52M);
hal_cmu_clock_enable(HAL_CMU_MOD_O_SDMMC);
hal_cmu_clock_enable(HAL_CMU_MOD_H_SDMMC);
hal_cmu_reset_clear(HAL_CMU_MOD_O_SDMMC);
hal_cmu_reset_clear(HAL_CMU_MOD_H_SDMMC);
host = &sdmmc_host[id];
host->ioaddr = (void *)sdmmc_ip_base[id];
// host->buswidth = 4;
host->buswidth = 1;
host->clksel = 0;
host->bus_hz = _SDMMC_CLOCK;
host->dev_index = id;
/* fifo threshold : MSIZE 0 - 1 transter, rx water mark 0 - greater than 0
* word triiger dma-req, tx water mark 1 - less than or equal 1 word triiger
* dma-req */
host->fifoth_val = MSIZE(0) | RX_WMARK(0) | TX_WMARK(1);
host->cfg.ops = &sdmmcip_ops;
host->cfg.f_min = 400000;
// host->cfg.f_min = 1000000;
// host->cfg.f_min = 25000000;
// host->cfg.f_min = 52000000;
// host->cfg.f_max = 1000000;
host->cfg.f_max = 26000000;
// host->cfg.f_max = 52000000;
host->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
host->cfg.host_caps = 0;
#ifdef HAL_SDMMC_USE_DMA
host->tx_dma_handler = sdmmcip_ext_dma_irq_handlers[id * 2];
host->rx_dma_handler = sdmmcip_ext_dma_irq_handlers[id * 2 + 1];
#endif
if (host->buswidth == 8) {
host->cfg.host_caps |= MMC_MODE_8BIT;
host->cfg.host_caps &= ~MMC_MODE_4BIT;
} else if (host->buswidth == 4) {
host->cfg.host_caps |= MMC_MODE_4BIT;
host->cfg.host_caps &= ~MMC_MODE_8BIT;
} else {
// host->cfg.host_caps &= ~MMC_MODE_4BIT;
host->cfg.host_caps |= MMC_MODE_4BIT;
host->cfg.host_caps &= ~MMC_MODE_8BIT;
}
host->cfg.host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz;
host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
host->mmc = mmc_create(host->dev_index, &host->cfg, host);
ret = mmc_init(host->mmc);
hal_sdmmc_dump(HAL_SDMMC_ID_0);
return ret;
}
uint32_t hal_sdmmc_read_blocks(enum HAL_SDMMC_ID_T id, uint32_t start_block,
uint32_t block_count, uint8_t *dest) {
HAL_SDMMC_ASSERT(id < HAL_SDMMC_ID_NUM, invalid_id, id);
return mmc_bread(id, start_block, block_count, dest);
}
uint32_t hal_sdmmc_write_blocks(enum HAL_SDMMC_ID_T id, uint32_t start_block,
uint32_t block_count, uint8_t *src) {
HAL_SDMMC_ASSERT(id < HAL_SDMMC_ID_NUM, invalid_id, id);
return mmc_bwrite(id, start_block, block_count, src);
}
void hal_sdmmc_close(enum HAL_SDMMC_ID_T id) {
struct sdmmcip_host *host = NULL;
HAL_SDMMC_ASSERT(id < HAL_SDMMC_ID_NUM, invalid_id, id);
HAL_SDMMC_TRACE(1, "%s\n", __func__);
host = &sdmmc_host[id];
while (host->mmc->init_in_progress)
hal_sdmmc_delay(1);
host->mmc->has_init = 0;
hal_cmu_reset_set(HAL_CMU_MOD_H_SDMMC);
hal_cmu_reset_set(HAL_CMU_MOD_O_SDMMC);
hal_cmu_clock_disable(HAL_CMU_MOD_H_SDMMC);
hal_cmu_clock_disable(HAL_CMU_MOD_O_SDMMC);
}
void hal_sdmmc_dump(enum HAL_SDMMC_ID_T id) {
struct mmc *mmc = NULL;
struct sdmmcip_host *host = NULL;
HAL_SDMMC_ASSERT(id < HAL_SDMMC_ID_NUM, invalid_id, id);
host = &sdmmc_host[id];
mmc = host->mmc;
HAL_SDMMC_TRACE(0, "-----------hal_sdmmc_dump-------------\n");
HAL_SDMMC_TRACE(1, " [sdmmc id] : %d\n", id);
HAL_SDMMC_TRACE(1, " [io register address] : 0x%x\n",
sdmmc_ip_base[id]);
HAL_SDMMC_TRACE(1, " [ddr mode] : %d\n",
mmc->ddr_mode);
switch (mmc->version) {
case SD_VERSION_1_0:
HAL_SDMMC_TRACE(0,
" [version] : SD_VERSION_1_0\n");
break;
case SD_VERSION_1_10:
HAL_SDMMC_TRACE(0,
" [version] : SD_VERSION_1_10\n");
break;
case SD_VERSION_2:
HAL_SDMMC_TRACE(0, " [version] : SD_VERSION_2\n");
break;
case SD_VERSION_3:
HAL_SDMMC_TRACE(0, " [version] : SD_VERSION_3\n");
break;
case MMC_VERSION_5_0:
HAL_SDMMC_TRACE(0,
" [version] : MMC_VERSION_5_0\n");
break;
case MMC_VERSION_4_5:
HAL_SDMMC_TRACE(0,
" [version] : MMC_VERSION_4_5\n");
break;
case MMC_VERSION_4_41:
HAL_SDMMC_TRACE(
0, " [version] : MMC_VERSION_4_41\n");
break;
case MMC_VERSION_4_3:
HAL_SDMMC_TRACE(0,
" [version] : MMC_VERSION_4_3\n");
break;
case MMC_VERSION_4_2:
HAL_SDMMC_TRACE(0,
" [version] : MMC_VERSION_4_2\n");
break;
case MMC_VERSION_4_1:
HAL_SDMMC_TRACE(0,
" [version] : MMC_VERSION_4_1\n");
break;
case MMC_VERSION_4:
HAL_SDMMC_TRACE(0,
" [version] : MMC_VERSION_4\n");
break;
case MMC_VERSION_3:
HAL_SDMMC_TRACE(0,
" [version] : MMC_VERSION_3\n");
break;
case MMC_VERSION_2_2:
HAL_SDMMC_TRACE(0,
" [version] : MMC_VERSION_2_2\n");
break;
case MMC_VERSION_1_4:
HAL_SDMMC_TRACE(0,
" [version] : MMC_VERSION_1_4\n");
break;
case MMC_VERSION_1_2:
HAL_SDMMC_TRACE(0,
" [version] : MMC_VERSION_1_2\n");
break;
default:
HAL_SDMMC_TRACE(0,
" [version] : unkown version\n");
break;
}
HAL_SDMMC_TRACE(1, " [is SD card] : 0x%x\n",
IS_SD(mmc));
HAL_SDMMC_TRACE(1, " [high_capacity] : 0x%x\n",
mmc->high_capacity);
HAL_SDMMC_TRACE(1, " [bus_width] : 0x%x\n",
mmc->bus_width);
HAL_SDMMC_TRACE(1, " [clock] : %d\n", mmc->clock);
HAL_SDMMC_TRACE(1, " [card_caps] : 0x%x\n",
mmc->card_caps);
HAL_SDMMC_TRACE(1, " [ocr] : 0x%x\n", mmc->ocr);
HAL_SDMMC_TRACE(1, " [dsr] : 0x%x\n", mmc->dsr);
HAL_SDMMC_TRACE(1, " [capacity_user/1024] : %d(K)\n",
(uint32_t)(mmc->capacity_user / 1024));
HAL_SDMMC_TRACE(1, " [capacity_user/1024/1024] : %d(M)\n",
(uint32_t)(mmc->capacity_user / 1024 / 1024));
HAL_SDMMC_TRACE(1, " [capacity_user/1024/1024/1024] : %d(G)\n",
(uint32_t)(mmc->capacity_user / 1024 / 1024 / 1024));
HAL_SDMMC_TRACE(1, " [read_bl_len] : %d\n",
mmc->read_bl_len);
HAL_SDMMC_TRACE(1, " [write_bl_len] : %d\n",
mmc->write_bl_len);
HAL_SDMMC_TRACE(0, "--------------------------------------\n");
}
void hal_sdmmc_info(enum HAL_SDMMC_ID_T id, uint32_t *sector_count,
uint32_t *sector_size) {
struct mmc *mmc = NULL;
struct sdmmcip_host *host = NULL;
HAL_SDMMC_ASSERT(id < HAL_SDMMC_ID_NUM, invalid_id, id);
host = &sdmmc_host[id];
mmc = host->mmc;
if (sector_size) {
*sector_size = mmc->read_bl_len;
HAL_SDMMC_TRACE(1, "[sdmmc] sector_size %d\n", *sector_size);
}
if (sector_count) {
if (mmc->read_bl_len != 0)
*sector_count = mmc->capacity_user / mmc->read_bl_len;
else
*sector_count = 0;
HAL_SDMMC_TRACE(1, "[sdmmc] sector_count %d\n", *sector_count);
}
}
#endif // CHIP_HAS_SDMMC