API GUIDE / MSC / LL
MSC LL 接口
msc ll api
本文面向 BSP、SoC 驱动、MSC HAL 维护者和高级调试开发者,说明 G32S10 MSC 的 LL(Low Level)寄存器访问接口。ll_msc.h 只提供 static inline 的寄存器读写、位域打包和状态查询 helper,不是可直接承担 SD/eMMC 协议、命令序列、DMA descriptor、cache、超时或并发管理的事务驱动。
API 头文件:
code/include/drivers/g32s10/msc/ll_msc.hcode/include/drivers/g32s10/msc/msc_regs.h(寄存器布局、MSC0/MSC1实例、位域掩码和公开枚举)
ll_msc.h 没有对应 .c 文件,所有接口在包含头文件后内联展开。寄存器地址、字段宽度和控制序列必须与 G32S10 MSC 规格一致;本文不展开 SD/eMMC 协议状态机、卡初始化、命令参数语义、ADMA descriptor 格式、cache 维护、GPIO pinmux、时钟树和板级电气参数。
LL 层不负责参数校验、错误码、软件状态机、互斥、超时、时钟使能、pinmux、卡上下电/复位、DMA 地址转换、cache 操作或中断注册。普通应用应优先使用
hal_msc.h或 MSC 中间件;直接调用 LL 接口时,调用方承担全部正确性责任。
1. 模块概览
ll_msc.h 是 MSC HAL 和中间件的寄存器级基础层,支持 MSC0 和 MSC1 两个控制器实例:
#define MSC0 ((msc_regs_t *)(uintptr_t)MSC0_IOBASE) /* 0x13300000 */
#define MSC1 ((msc_regs_t *)(uintptr_t)MSC1_IOBASE) /* 0x13310000 */
ll_msc_is_instance() 仅接受这两个实例。其它寄存器块指针不被 LL 层支持;控制器是否在板级可用、时钟和 pinmux 是否已配置,需要调用方保证。
| 组件 | 作用 |
|---|---|
ll_msc |
提供 296 个 static inline 寄存器 helper、位域 set/get、状态检查和 PIO 数据端口访问 |
msc_regs |
定义 msc_regs_t、MSC0/MSC1、寄存器偏移、字段 mask/shift 和配置枚举 |
hal_msc |
在 LL 层之上提供控制器发现、IRQ 分发、总线时序、DMA 模式和调谐控制 |
| MSC 中间件 | 在 HAL 之上完成 SD/eMMC/SDIO 协议、卡初始化、DMA/cache、分区和文件系统集成 |
LL 层的使用边界如下:
- 除
ll_msc_is_instance()外,所有接口的第一个参数都是msc_regs_t *mscx,且必须指向MSC0或MSC1。LL 层不检查空指针,也不验证实例。 - 接口不维护软件句柄、初始化状态或请求队列。每次调用都直接访问当前寄存器;硬件是否允许下发新命令要通过
PSTATE_REG和中断状态判断。 - 返回值只有
void、bool、uint8_t、uint16_t、uint32_t、uint64_t和少量枚举,没有error_code_t。写接口成功与否无法通过返回值判断。 - 字段 setter 使用读改写,只修改目标位域;全寄存器 setter 会覆盖整个寄存器,包括保留位。
ll_msc_host_control2_set()会额外屏蔽保留位RSVD_KEEP_0,但仍要求调用方提供完整目标值。 - 读改写不是原子操作。任务与 ISR 共享同一寄存器时,必须由调用方提供临界区、统一所有者或其它串行化措施。
CMD_R是写后触发命令的寄存器。LL 不提供单字段写 helper,必须先用ll_msc_command_make()构造成完整 word,再通过ll_msc_command_set()一次写入。SW_RST_R的位由硬件在复位完成后自动清零;FORCE_*是 W1S,NORMAL_INT_STAT_R/ERROR_INT_STAT_R中的可清事件是 W1C。写 0 通常不会清除或置位对应事件。NORMAL_INT_STAT_R.CARD_INTERRUPT是 DAT1 的只读电平状态,不能用 W1C 清除;停止上报必须关闭对应 signal enable。- LL 层不等待命令完成、数据完成、DMA 边界、调谐完成或软件复位完成,也不提供重试和超时。所有完成与错误处理由调用方完成。
- 控制器正在执行命令或数据传输时,除读取状态、响应和数据端口外,不应修改命令、传输、时钟、总线宽度、DMA、调谐或低功耗配置。
- LL 层不负责 CPM 时钟、GPIO pinmux、卡供电/复位、外部 1.8 V 稳压器、ADMA descriptor 构造、cache clean/invalidate 或 IRQ 注册。
本文覆盖 ll_msc.h 的 296 个公开内联接口,按其访问的寄存器分为 16 个功能组:
| 功能组 | 数量 | 主要寄存器 | 用途 |
|---|---|---|---|
| 实例检查和原始访问 | 7 | — | 判断实例,按字节偏移读写 8/16/32-bit 寄存器 |
| 传输参数和 XFER_MODE | 37 | SDMASA、BLOCKSIZE、BLOCKCOUNT、ARGUMENT、XFER_MODE |
块参数、命令参数、方向、自动命令、DMA 和响应行为 |
| 命令和响应 | 15 | CMD、RESP01/23/45/67 |
编码并触发命令,读取响应寄存器 |
| 数据端口和 PSTATE | 16 | BUF_DATA、PSTATE |
PIO word 访问,命令/DAT/缓冲区状态查询 |
| HOST_CTRL1 | 17 | HOST_CTRL1 |
卡检测、总线宽度、DMA 选择、高速模式 |
| 电源控制 | 12 | PWR_CTRL |
VDD1/VDD2 电压选择和供电使能 |
| Block gap 和唤醒 | 23 | BGAP_CTRL、WUP_CTRL |
block gap、read wait、继续/停止请求和唤醒事件 |
| 时钟、超时和复位 | 20 | CLK_CTRL、TOUT_CTRL、SW_RST |
内部/卡时钟、PLL、低功耗门控、DAT 超时和软件复位 |
| 中断控制 | 15 | NORMAL/ERROR_INT_STAT、status/signal enable |
状态读取、W1C 清除和中断两级使能 |
| HOST_CTRL2 | 28 | HOST_CTRL2 |
异步中断、寻址模式、Host v4、ADMA3、采样时钟、调谐和 UHS 模式 |
| 能力寄存器 | 24 | CAPABILITIES1/2、CURR_CAPABILITIES1/2 |
DMA、速率、总线宽度、调谐和供电能力查询 |
| Force event 和 ADMA | 9 | FORCE_*、ADMA_ERR_STAT、ADMA_SA、ADMA_ID |
强制错误事件、ADMA 错误状态和 64-bit 地址 |
| Preset、Slot 和版本 | 13 | PRESET_*、SLOT_INTR_STATUS、HOST_CNTRL_VERS、MSHC_VER_* |
预设时序、slot 中断和版本识别 |
| MSHC 和 eMMC 控制 | 28 | MSHC_CTRL、EMMC_CTRL |
时钟门控、命令冲突检查、CQE、eMMC reset/CRC/卡类型 |
| 调谐控制 | 23 | HOST_CTRL2、AT_CTRL、AT_STAT |
执行调谐、软件调谐、窗口选择和采样相位 |
| 低功耗控制 | 9 | LP_CTRL |
写等待阈值和空闲卡时钟门控延时 |
2. 典型流程
2.1 前置条件与包含方式
LL 头文件没有独立的 Kconfig 选项和源文件。通过 MSC HAL 使用时,hal_msc.h 已经包含 ll_msc.h;直接使用寄存器接口时,可显式包含:
#include "ll_msc.h"
调用任何会访问硬件的 LL 接口前,调用方必须完成以下准备:
- 确认使用
MSC0还是MSC1,并完成 CLK、CMD、DAT0..DAT3 及可选卡检测信号的 pinmux。 - 使能 MSC 总线时钟和门控时钟,选择正确的时钟父源、频率和 RX/TX 相位。LL 层只操作控制器内部的
CLK_CTRL_R,不配置 CPM。 - 完成 SD/eMMC 供电、复位和可选 1.8 V 切换。
PWR_CTRL_R的供电位只反映控制器请求,不代表外部稳压器已经稳定。 - 确认控制器不在传输中,且 CMD/DAT inhibit 条件允许修改配置。
- 如使用中断,完成平台 IRQ 注册,并在控制器内同时配置 status latch enable 和 signal enable。
2.2 控制器初始化顺序
LL 层没有 init 函数。下面的顺序展示寄存器层的依赖关系;具体上电、时钟、卡检测和电压切换顺序必须结合板级设计和控制器规格:
static bool app_msc_low_level_init(msc_regs_t *mscx)
{
if (!ll_msc_is_instance(mscx)) {
return false;
}
/* 此时应已使能 CPM 时钟、完成 pinmux,并关闭卡时钟。 */
ll_msc_sd_clock_disable(mscx);
ll_msc_internal_clock_disable(mscx);
ll_msc_software_reset(mscx, MSC_SOFTWARE_RESET_ALL);
while (ll_msc_is_software_reset_pending(mscx, MSC_SOFTWARE_RESET_ALL)) {
/* 调用方必须增加超时,避免硬件异常时永久等待。 */
}
ll_msc_interrupt_status_clear(mscx, 0xFFFFU, 0xFFFFU);
ll_msc_timeout_count_set(mscx, 0xEU);
ll_msc_host_control1_set(mscx, 0U);
ll_msc_host_control2_set(mscx, 0U);
ll_msc_internal_clock_enable(mscx);
while (!ll_msc_is_internal_clock_stable(mscx)) {
/* 同上,必须由调用方设置超时。 */
}
/* 电压、总线宽度、UHS 模式、DMA 选择和卡时钟按板级/协议顺序配置。 */
return true;
}
注意:
- 软复位会清除控制器配置。复位完成后需要重新建立时钟、电源、总线宽度、中断和 DMA 配置。
- 内部时钟稳定前不能把
SD_CLK_EN作为有效卡时钟使用。卡时钟的开关时机还应满足 SD/eMMC 初始化要求。 ll_msc_software_reset_get()返回当前仍挂起的复位请求,不是“最后一次复位状态”。某一位清零表示对应域已回到 working 状态。- 初始化代码只展示 LL 调用关系,不包含 CPM、GPIO、稳压器、卡检测消抖或协议层命令。
2.3 命令与 PIO 数据流程
一次非 DMA 命令的基本顺序如下:
- 通过
ll_msc_is_command_inhibited()或ll_msc_is_data_command_inhibited()确认允许下发命令。 - 写入
ARGUMENT_R。参数由协议层按 SD/eMMC 命令定义生成。 - 带数据时先配置
BLOCKSIZE_R、BLOCKCOUNT_R和XFER_MODE_R;不带数据时也要明确清除上一次残留的 DMA、多块和自动命令位。 - 使用
ll_msc_command_make()生成完整的CMD_Rword,再调用ll_msc_command_set()写入。 - 等待
NORMAL_INT_STAT_R.CMD_CMPLT。需要响应时,在命令完成之后读取RESP01/23/45/67。 - 读数据时,在
PSTATE_REG.BUF_RD_EN置位后从BUF_DATA_R读取;写数据时,在BUF_WR_EN置位后写入。必须搬运完整的block_size × block_count数据量。 - 等待
XFER_CMPLT,检查ERROR_INT_STAT_R,最后清除已处理的 W1C 状态。
PIO 端口按 32-bit word 访问,但 word 中字节顺序和 block 内偏移由卡协议和命令定义决定。LL 不判断 FIFO 水位、剩余字节数或传输是否已越界。
2.4 DMA 配置顺序
DMA 数据通路需要同时配置传输模式、控制器 DMA 选择、地址寄存器和 descriptor(若使用 ADMA):
- 确认控制器空闲,并根据协议选择 PIO 或 DMA。
- 使用
ll_msc_dma_select_set()选择 SDMA、32/64-bit ADMA2 或 ADMA2/ADMA3。选择值受HOST_CTRL2_R.HOST_V4_EN影响。 - SDMA:写
SDMASA_R和SDMA_BUF_BDARY,并在边界事件后更新地址。 - ADMA2:在系统内存中构造合法 descriptor,满足对齐要求,把物理地址写入
ADMA_SA_LOW/HIGH,并完成 cache clean。 - ADMA3:把 integrated descriptor 地址写入
ADMA_ID_LOW/HIGH,并按 Host v4/CQE 要求配置。 - 在
XFER_MODE_R中置DMA_EN后写CMD_R触发传输。
LL 层只写寄存器,不构造 descriptor,不转换虚拟地址,不维护 cache,也不在 DMA boundary 自动续写 SDMA 地址。Host v4 下 SDMASA_R 还可表示 32-bit block count,使用前必须确认当前模式,避免把计数写到地址字段。
2.5 中断处理
轮询模式可以只读取状态寄存器;中断模式还需要正确区分 status latch 和 IRQ signal:
NORMAL_INT_STAT_EN_R/ERROR_INT_STAT_EN_R决定哪些事件形成锁存状态。NORMAL_INT_SIGNAL_EN_R/ERROR_INT_SIGNAL_EN_R决定哪些锁存状态路由到控制器 IRQ 输出。- 中断服务程序应先读取
ll_msc_interrupt_status_get(),再处理命令完成、传输完成、DMA boundary、buffer ready 或错误。 - 对可写 1 清除的位,使用
ll_msc_normal_interrupt_status_clear()、ll_msc_error_interrupt_status_clear()或ll_msc_interrupt_status_clear()清除。 CARD_INTERRUPT不能通过 W1C 清除。若要停止 SDIO 卡中断,应关闭对应 signal enable,并在卡侧处理中断源。
2.6 软件复位、调谐和低功耗
软件复位通过 SW_RST_R 请求,可分别复位 DAT、CMD 或整个控制器。请求后调用方必须轮询 ll_msc_is_software_reset_pending() 并设置超时;复位完成后重新配置受影响寄存器。
高频 SD/eMMC 模式需要调谐时,通常先配置 HOST_CTRL2_R、AT_CTRL_R 和调谐数据块,再置 EXEC_TUNING,并由协议层发送调谐命令。LL 只提供启动、清除和状态查询,不发送调谐命令,也不等待完成。硬件自动调谐之外,还可以启用软件调谐并通过 PH_SEL_CODE 选择采样相位。
低功耗配置由 CLK_CTRL_R.CARD_LP_EN、LP_CTRL_R 和 MSHC_CTRL_R.SW_CG_DIS 共同决定。修改门控阈值、关闭时钟或进入低功耗状态前,必须确认没有进行中的命令、PIO 访问、DMA 或调谐。
3. 配置和关键类型
3.1 实例和寄存器布局
msc_regs_t 由 struct msc_regs 定义,两个公开实例为 MSC0 和 MSC1。寄存器块按 SDHCI/厂商扩展布局组织,主要寄存器如下:
| 寄存器 | 偏移 | 访问属性 | 说明 |
|---|---|---|---|
SDMASA_R |
0x000 |
R/W | v3 SDMA 系统地址;Host v4 下可表示 32-bit block count |
BLOCKSIZE_R |
0x004 |
R/W | block size 和 SDMA boundary |
BLOCKCOUNT_R |
0x006 |
R/W | 16-bit block count |
ARGUMENT_R |
0x008 |
R/W | 下一条命令的 32-bit 参数 |
XFER_MODE_R |
0x00C |
R/W | 数据方向、多块、自动命令、DMA 和响应检查 |
CMD_R |
0x00E |
R/W,写触发 | 命令索引、类型、数据/响应选择和检查位 |
RESP01_R~RESP67_R |
0x010~0x01C |
RO | 最多 136-bit 响应 |
BUF_DATA_R |
0x020 |
R/W | 非 DMA PIO 数据端口 |
PSTATE_REG |
0x024 |
RO | 命令、DAT、缓冲区和采样线状态 |
HOST_CTRL1_R |
0x028 |
R/W | 卡检测、位宽、DMA 选择和高速模式 |
PWR_CTRL_R |
0x029 |
R/W | VDD1/VDD2 电压和供电控制 |
BGAP_CTRL_R |
0x02A |
R/W | block gap、read wait、continue 和 stop |
WUP_CTRL_R |
0x02B |
R/W | 卡移除、插入和卡中断唤醒 |
CLK_CTRL_R |
0x02C |
R/W | 内部时钟、SD clock、PLL 和卡时钟低功耗 |
TOUT_CTRL_R |
0x02E |
R/W | DAT timeout 编码 |
SW_RST_R |
0x02F |
W1S/RO | DAT、CMD 或控制器软复位,完成后自动清零 |
NORMAL_INT_STAT_R |
0x030 |
RO/W1C | 正常事件状态,混合只读和 W1C 位 |
ERROR_INT_STAT_R |
0x032 |
W1C | 错误事件状态 |
NORMAL/ERROR_INT_STAT_EN_R |
0x034/0x036 |
R/W | 事件状态锁存使能 |
NORMAL/ERROR_INT_SIGNAL_EN_R |
0x038/0x03A |
R/W | 事件到 IRQ 输出的路由使能 |
AUTO_CMD_STAT_R |
0x03C |
RO | 自动命令错误详情 |
HOST_CTRL2_R |
0x03E |
R/W | 寻址、Host v4、采样时钟、调谐和 UHS 模式 |
CAPABILITIES1/2_R |
0x040/0x044 |
RO | 固定控制器能力 |
CURR_CAPABILITIES1/2_R |
0x048/0x04C |
RO | 当前电压条件下能力 |
FORCE_AUTO_CMD_STAT_R |
0x050 |
WO/W1S | 强制自动命令错误事件 |
FORCE_ERROR_INT_STAT_R |
0x052 |
WO/W1S | 强制错误事件 |
ADMA_ERR_STAT_R |
0x054 |
RO | ADMA 错误状态机快照和长度错误 |
ADMA_SA_LOW/HIGH_R |
0x058/0x05C |
R/W | 64-bit ADMA 系统地址 |
PRESET_*_R |
0x060~0x06E、0x074 |
RO | 各速度模式预设频率、时钟源和驱动强度 |
ADMA_ID_LOW/HIGH_R |
0x078/0x07C |
R/W | 64-bit ADMA3 integrated descriptor 地址 |
SLOT_INTR_STATUS_R |
0x0FC |
RO | slot 中断状态 |
HOST_CNTRL_VERS_R |
0x0FE |
RO | Host 规范版本和厂商版本 |
MSHC_VER_ID/TYPE_R |
0x500/0x504 |
RO | 厂商 MSHC 版本信息 |
MSHC_CTRL_R |
0x508 |
R/W | 软件时钟门控和命令冲突检查 |
EMMC_CTRL_R |
0x52C |
R/W | eMMC 卡选择、复位、CRC 和 CQE 控制 |
AT_CTRL_R |
0x540 |
R/W | 自动/软件调谐、窗口阈值和相位切换延时 |
AT_STAT_R |
0x544 |
R/W | 调谐状态和软件选择的采样相位 |
LP_CTRL_R |
0x550 |
R/W | 低功耗阈值和空闲时钟门控计数 |
ll_msc_reg8_write()、ll_msc_reg16_write() 和 ll_msc_reg32_write() 的 offset 是相对于 mscx 的字节偏移,不是字段编号或 word index。访问宽度和地址对齐必须与目标寄存器匹配。
3.2 传输参数和 XFER_MODE
ll_msc_transfer_mode_make() 负责把常用控制项打包为 XFER_MODE_R。字段如下:
| 位 | 字段 | 作用 |
|---|---|---|
| 0 | DMA_EN |
允许本次带数据命令启动所选 DMA |
| 1 | BLK_CNT_EN |
使用 BLOCKCOUNT_R 限制传输块数 |
| 3:2 | AUTO_CMD_EN |
禁用、CMD12、CMD23 或由硬件自动选择 |
| 4 | DATA_XFER_DIR |
1 为卡到主机读,0 为主机到卡写或无数据 |
| 5 | MULTI_BLK_SEL |
多块选择;单块传输清零 |
| 6 | RESP_TYPE |
响应错误检查选择 R1 或 R5 |
| 7 | RESP_ERR_CHK_EN |
使能 R1/R5 响应错误位检查 |
| 8 | RESP_INT_DIS |
禁止 response/command complete 中断 |
ll_msc_transfer_mode_set() 覆盖整个 16-bit word,不会保留原值。若只想修改一个字段,可使用对应的 enable/disable/select/set 位域 helper;这些 helper 是读改写,仍需调用方串行化。
| 参数/接口 | 约束 |
|---|---|
BLOCKSIZE_R.XFER_BLOCK_SIZE |
0 表示无数据阶段;HAL 当前把数据请求限制在 1~2048 字节,memory card 常用 512 字节 |
BLOCKCOUNT_R.BLOCK_CNT |
16-bit 块数;Host v4 可把 0 与 SDMASA_R 的 32-bit count 组合使用 |
SDMA_BUF_BDARY |
4 KiB~512 KiB,到达边界时硬件置 DMA_INT |
RESP_ERR_CHK_EN |
只对 R1/R5 有意义,不应用于调谐命令 |
AUTO_CMD_EN |
SDIO 传输应使用 MSC_AUTO_COMMAND_DISABLED |
DMA_EN |
选择 SDMA/ADMA 时置位;PIO 模式清零 |
3.3 命令编码和响应
ll_msc_command_make() 打包 CMD_R:
| 位 | 字段 | 作用 |
|---|---|---|
| 13:8 | CMD_INDEX |
命令号,0~63 |
| 7:6 | CMD_TYPE |
normal、suspend、resume 或 abort |
| 5 | DATA_PRES_SEL |
命令包含 DAT 数据阶段 |
| 4 | CMD_IDX_CHK_EN |
检查响应中的命令索引 |
| 3 | CMD_CRC_CHK_EN |
检查响应 CRC |
| 2 | SUB_CMD_FLAG |
标记 sub-command |
| 1:0 | RESP_TYPE_SEL |
无响应、136-bit、48-bit 或 48-bit busy |
ll_msc_command_set() 写入 CMD_R 会产生命令下发副作用。LL 不检查 PSTATE_REG.CMD_INH/CMD_INH_DAT,也不保证前一条命令已经完成。读取 CMD_R 只能得到当前控制字段,不能作为完成状态。
响应寄存器映射如下:
| API | 寄存器 | 正常命令响应位 | 自动命令说明 |
|---|---|---|---|
ll_msc_response01_get() |
RESP01_R |
[39:8] |
— |
ll_msc_response23_get() |
RESP23_R |
[71:40] |
— |
ll_msc_response45_get() |
RESP45_R |
[103:72] |
— |
ll_msc_response67_get() |
RESP67_R |
[135:104] |
自动命令的 32-bit 响应在此寄存器 |
ll_msc_response_get() |
由枚举选择 | 按索引返回对应 word | 越界索引回退到 RESP67_R |
3.4 PSTATE 和中断状态
PSTATE_REG 是实时快照,常用状态位包括:
| 状态 | API | 说明 |
|---|---|---|
CMD_INH |
ll_msc_is_command_inhibited() |
CMD_R 下发被禁止 |
CMD_INH_DAT |
ll_msc_is_data_command_inhibited() |
使用 DAT 线的命令被禁止 |
DAT_LINE_ACT |
ll_msc_is_data_line_active() |
DAT 线正在传输或 busy |
RD_XFER_ACT/WR_XFER_ACT |
ll_msc_is_read_transfer_active() / ll_msc_is_write_transfer_active() |
当前读/写传输活动 |
BUF_RD_EN/BUF_WR_EN |
ll_msc_is_read_buffer_ready() / ll_msc_is_write_buffer_ready() |
PIO 端口可读/可写 |
RE_TUNE_REQ |
ll_msc_is_retune_requested() |
硬件请求重新调谐 |
SUB_CMD_STAT |
ll_msc_is_sub_command_active() |
当前为 sub-command 状态 |
CMD_ISSU_ERR |
ll_msc_is_command_issue_error() |
命令下发错误 |
HOST_REG_VOL |
ll_msc_is_host_regulated_voltage_stable() |
主机稳压电压状态 |
中断状态由 normal 和 error 两个 16-bit 寄存器组成;ll_msc_interrupt_status_get() 组合为 ERROR[31:16] | NORMAL[15:0]。使用中断时必须同时配置 status enable 和 signal enable:只配置 signal 而事件状态没有锁存,或只锁存而没有 signal,都得不到预期 IRQ。
NORMAL_INT_STAT_R 的 CMD_CMPLT、XFER_CMPLT、DMA_INT、BGAP_EVNT、BUF_RD_RDY、BUF_WR_RDY 等是可清除事件;ERR_INT、CARD_INTERRUPT 是状态位。清除接口应只写入需要清除的 W1C 位,避免把只读状态误当作可清事件处理。
3.5 主机控制、电源和时钟
HOST_CTRL1_R 主要字段:
CARD_DETECT_SIG_LVL选择卡检测测试电平或输入引脚。CARD_DETECT_TEST_LVL在测试模式下模拟插入/移除。EXT_DAT_XFER与DAT_XFER_W组合 1/4/8-bit 总线宽度。G32S10 板级通常只提供 DAT0..DAT3;不要仅因为 LL 提供 8-bit helper 就选择 8-bit。DMA_SEL选择 SDMA/ADMA2/ADMA3;值 1 保留。HS_EN选择 high-speed timing。
HOST_CTRL2_R 主要字段:
UHS_MODE_SEL选择 SD 或 eMMC 速度模式;同一编码在 SD/eMMC 下名称不同。SIGNALING_1V8_EN是电压 signaling 位,不等于采样时钟选择。SMP_CLK_SEL选择 tuned 或 fixed sampling clock。EXEC_TUNING启动执行调谐,硬件完成后清零。ADDRESSING选择 32/64-bit 系统寻址,需与 Host v4 和硬件能力一致。HOST_V4_EN改变 SDMA、ADMA 选择、系统寻址和 32-bit block count 语义。CMD23_EN选择 ADMA3 使用自动 CMD23 或自动 CMD12。ADMA2_LEN_MD选择 26-bit 或 16-bit descriptor length。
PWR_CTRL_R 只提供控制器侧的电压选择和供电请求。VDD1 编码随 SD/eMMC 模式不同而不同,VDD2 仅用于 UHS-II 1.8 V。LL 不检查外部电源是否已经稳定,也不保证先关时钟再切换电压。
CLK_CTRL_R 同时包含控制位和状态位:INTERNAL_CLK_EN 请求内部时钟,INTERNAL_CLK_STABLE 是只读稳定状态,SD_CLK_EN 驱动卡时钟,PLL_ENABLE 请求时钟 PLL,CARD_LP_EN 允许空闲卡时钟门控。
3.6 DMA 和地址
msc_dma_select_t 的编码受 Host v4 模式影响:
| 编码 | v3 兼容模式 | Host v4 模式 |
|---|---|---|
| 0 | SDMA | SDMA |
| 2 | 32-bit ADMA2 | ADMA2 |
| 3 | 64-bit ADMA2 | ADMA2 或 ADMA3 |
常用接口和约束:
ll_msc_sdma_address_set()/get()访问SDMASA_R。Host v4 置位后,同一寄存器表示 32-bit block count,不要同时把它当 SDMA 地址。ll_msc_32bit_block_count_set()/get()仅在 Host v4 语义下使用;选择该路径时通常还需要让BLOCKCOUNT_R为 0。ll_msc_sdma_buffer_boundary_set()设置 SDMA 边界;边界事件后需要更新系统地址。ll_msc_adma_address_set()/get()组合ADMA_SA_LOW/HIGH,地址必须是可供控制器访问的物理地址并满足 descriptor 对齐要求。ll_msc_adma_id_address_set()/get()组合ADMA_ID_LOW/HIGH,用于 ADMA3 integrated descriptor。ll_msc_adma_error_status_get()、ll_msc_is_adma_length_error()和ll_msc_adma_error_state_get()用于错误诊断;LL 不自动停止或恢复 DMA。
使用 DMA 前必须完成 descriptor 构造、地址转换、对齐检查和 cache 维护。传输结束后如果 CPU 要读取 DMA 目标缓冲区,还必须按平台 cache 规则执行 invalidate。
3.7 能力、预设和版本
CAPABILITIES1_R 报告固定能力,CAPABILITIES2_R 报告 SDR/DDR、调谐和 ADMA3 能力。CURR_CAPABILITIES1/2_R 可能随当前电压条件变化,不能简单用固定能力代替当前能力。
| 类型/接口 | 含义 |
|---|---|
msc_speed_mode_t |
SD/eMMC 共用 UHS_MODE_SEL 编码 |
msc_preset_index_t |
选择 PRESET_INIT~PRESET_UHS2_HS400 寄存器 |
ll_msc_max_block_length_get() |
返回 0/1/2,对应 512/1024/2048 字节;3 保留 |
ll_msc_max_current_1v8_get()、ll_msc_max_current_3v0_get()、ll_msc_max_current_3v3_get() |
返回 SDHCI current 原始编码,不是毫安值 |
ll_msc_preset_value_get() |
返回 16-bit 预设寄存器原值 |
ll_msc_preset_driver_strength_get() |
0 TypeB、1 TypeA、2 TypeC、3 TypeD |
ll_msc_preset_clock_generator_select_get() |
选择 programmable 或 fixed clock generator |
ll_msc_preset_frequency_select_get() |
返回 10-bit frequency select |
ll_msc_host_vendor_version_get()、ll_msc_host_spec_version_get() |
读取 Host 厂商/规范版本字段 |
ll_msc_mshc_version_id_get()、ll_msc_mshc_version_id_value_get()、ll_msc_mshc_version_type_get()、ll_msc_mshc_version_type_value_get() |
读取厂商 MSHC 版本寄存器 |
预设寄存器是只读值,LL 没有预设寄存器写接口。是否使用 preset 以及如何把 preset 编码应用到 CPM/控制器,由 HAL 或控制器层决定。
3.8 eMMC、CQE、调谐和低功耗
MSHC_CTRL_R:
SW_CG_DIS=1禁止软件时钟门控,因此ll_msc_software_clock_gating_disable()是置位,enable()是清零。CMD_CONFLICT_CHECK控制命令冲突检查。
EMMC_CTRL_R:
CARD_IS_EMMC选择 eMMC 或非 eMMC 行为。EMMC_RST_N_OE控制 reset pad 是否由控制器驱动,EMMC_RST_N为低有效复位。DISABLE_DATA_CRC_CHK=1关闭数据 CRC 检查。CQE_PREFETCH_DISABLE=1关闭 CQE prefetch,CQE_ALGO_SEL选择 CQE task arbitration 算法。修改 CQE 配置应在 CQE 关闭时进行。
AT_CTRL_R/AT_STAT_R:
HOST_CTRL2_R.EXEC_TUNING由ll_msc_execute_tuning_start()置位,硬件完成后自动清零。SW_TUNE_EN选择软件调谐;PH_SEL_CODE在软件调谐下选择 0~7 的采样相位。SWIN_TH_VAL、SWIN_TH_EN控制采样窗口阈值和“首个匹配/最宽窗口”策略。PRE_CHANGE_DLY、POST_CHANGE_DLY是相位切换前后的 cclk 延时,范围 0~3。TUNE_CLK_STOP_EN允许在调谐相位切换时停止卡时钟。RPT_TUNE_ERR控制调谐错误上报。
LP_CTRL_R 与 CLK_CTRL_R.CARD_LP_EN 配合:
LP_TH_VAL与 buffered write 的XFER_BLOCK_SIZE - LP_TH_VAL等待阈值比较,LP_TH_EN=1时生效。LP_CNT是空闲时钟门控延时,CARD_LP_EN=1时生效。- LL 不检查是否还有未完成传输;进入低功耗门控前必须确认控制器空闲。
3.9 公开枚举和常量
msc_regs.h 公开以下配置枚举。字段范围仍由调用方保证,LL 不会阻止把保留值写入寄存器。
| 枚举 | 用途 |
|---|---|
msc_sdma_boundary_t |
SDMA 4 KiB~512 KiB boundary |
msc_response_check_type_t |
R1/R5 响应错误检查选择 |
msc_auto_command_t |
禁用、CMD12、CMD23 或自动选择 |
msc_command_type_t |
normal/suspend/resume/abort |
msc_response_type_t |
none/136-bit/48-bit/48-bit busy |
msc_response_word_t |
选择 RESP01/23/45/67_R |
msc_dma_select_t |
SDMA/ADMA2/ADMA3 编码,受 Host v4 影响 |
msc_bus_width_t |
1/4/8-bit DAT 总线 |
msc_vdd1_voltage_t |
SD/eMMC VDD1 电压编码 |
msc_vdd2_voltage_t |
UHS-II VDD2 电压编码 |
msc_software_reset_t |
DAT/CMD/ALL 软复位掩码,可组合 |
msc_speed_mode_t |
SD/eMMC UHS 速度模式 |
msc_preset_index_t |
preset 寄存器选择 |
寄存器位域 mask/shift、中断位和状态位定义在 msc_regs.h。组合寄存器值时,优先使用这些宏,不要手写裸 bit 位置。
4. API 参考
4.1 API 总览
ll_msc.h
以下列出全部 296 个公开接口,按功能组归类。后续小节给出参数、返回值和寄存器副作用。
实例检查和原始访问
| API | 用途 |
|---|---|
ll_msc_is_instance() |
判断指针是否等于 MSC0 或 MSC1 |
ll_msc_reg8_write() |
按字节偏移写 8-bit 寄存器 |
ll_msc_reg8_read() |
按字节偏移读 8-bit 寄存器 |
ll_msc_reg16_write() |
按半字偏移写 16-bit 寄存器 |
ll_msc_reg16_read() |
按半字偏移读 16-bit 寄存器 |
ll_msc_reg32_write() |
按字偏移写 32-bit 寄存器 |
ll_msc_reg32_read() |
按字偏移读 32-bit 寄存器 |
传输参数和 XFER_MODE
| API | 用途 |
|---|---|
ll_msc_sdma_address_set() |
写 SDMA 系统地址 |
ll_msc_sdma_address_get() |
读 SDMA 系统地址 |
ll_msc_32bit_block_count_set() |
在 Host v4 下写 32-bit block count |
ll_msc_32bit_block_count_get() |
在 Host v4 下读 32-bit block count |
ll_msc_sdma_buffer_boundary_set() |
设置 SDMA buffer boundary |
ll_msc_sdma_buffer_boundary_get() |
读取 SDMA buffer boundary |
ll_msc_block_size_set() |
设置 block size |
ll_msc_block_size_get() |
读取 block size |
ll_msc_block_count_set() |
设置 16-bit block count |
ll_msc_block_count_get() |
读取 16-bit block count |
ll_msc_argument_set() |
设置下一条命令参数 |
ll_msc_argument_get() |
读取命令参数 |
ll_msc_transfer_mode_set() |
覆盖写完整 XFER_MODE_R |
ll_msc_transfer_mode_get() |
读取完整 XFER_MODE_R |
ll_msc_transfer_mode_make() |
打包常用 XFER_MODE_R 位 |
ll_msc_response_interrupt_disable() |
禁止响应完成中断 |
ll_msc_response_interrupt_enable() |
允许响应完成中断 |
ll_msc_is_response_interrupt_disabled() |
查询响应完成中断是否禁止 |
ll_msc_response_error_check_enable() |
开启 R1/R5 响应错误检查 |
ll_msc_response_error_check_disable() |
关闭 R1/R5 响应错误检查 |
ll_msc_is_response_error_check_enabled() |
查询响应错误检查是否开启 |
ll_msc_response_check_type_set() |
选择 R1 或 R5 响应错误检查 |
ll_msc_response_check_type_get() |
读取响应错误检查类型 |
ll_msc_multi_block_select() |
选择多块传输 |
ll_msc_single_block_select() |
选择单块传输 |
ll_msc_is_multi_block_selected() |
查询是否多块传输 |
ll_msc_card_to_host_direction_select() |
选择卡到主机读方向 |
ll_msc_host_to_card_direction_select() |
选择主机到卡写方向 |
ll_msc_is_card_to_host_direction_selected() |
查询是否为卡到主机方向 |
ll_msc_auto_command_set() |
设置自动 CMD12/CMD23 |
ll_msc_auto_command_get() |
读取自动命令选择 |
ll_msc_block_count_enable() |
启用 block count 限制 |
ll_msc_block_count_disable() |
禁用 block count 限制 |
ll_msc_is_block_count_enabled() |
查询 block count 限制 |
ll_msc_dma_enable() |
在传输模式中开启 DMA |
ll_msc_dma_disable() |
在传输模式中关闭 DMA |
ll_msc_is_dma_enabled() |
查询本次传输是否启用 DMA |
命令和响应
| API | 用途 |
|---|---|
ll_msc_command_set() |
写入完整 CMD_R 并触发命令 |
ll_msc_command_get() |
读取 CMD_R 控制字段 |
ll_msc_command_make() |
打包 CMD_R 命令、检查位和响应类型 |
ll_msc_command_index_get() |
读取命令索引 |
ll_msc_command_type_get() |
读取命令类型 |
ll_msc_is_command_data_present() |
查询命令是否包含数据阶段 |
ll_msc_is_command_index_check_enabled() |
查询响应索引检查 |
ll_msc_is_command_crc_check_enabled() |
查询响应 CRC 检查 |
ll_msc_is_sub_command() |
查询 sub-command 标志 |
ll_msc_command_response_type_get() |
读取期望响应类型 |
ll_msc_response01_get() |
读取 RESP01_R |
ll_msc_response23_get() |
读取 RESP23_R |
ll_msc_response45_get() |
读取 RESP45_R |
ll_msc_response67_get() |
读取 RESP67_R |
ll_msc_response_get() |
按枚举读取一个响应 word |
数据端口和 PSTATE
| API | 用途 |
|---|---|
ll_msc_buffer_data_write() |
向 PIO 数据端口写一个 word |
ll_msc_buffer_data_read() |
从 PIO 数据端口读一个 word |
ll_msc_present_state_get() |
读取完整 PSTATE_REG |
ll_msc_is_sub_command_active() |
查询 sub-command 状态 |
ll_msc_is_command_issue_error() |
查询命令下发错误 |
ll_msc_is_host_regulated_voltage_stable() |
查询主机稳压电压状态 |
ll_msc_command_line_level_get() |
读取 CMD 线采样电平 |
ll_msc_data_line_level_get() |
读取 DAT0..DAT7 采样电平 |
ll_msc_is_read_buffer_ready() |
查询 PIO 读缓冲区就绪 |
ll_msc_is_write_buffer_ready() |
查询 PIO 写缓冲区就绪 |
ll_msc_is_read_transfer_active() |
查询读传输是否活动 |
ll_msc_is_write_transfer_active() |
查询写传输是否活动 |
ll_msc_is_retune_requested() |
查询硬件重调谐请求 |
ll_msc_is_data_line_active() |
查询 DAT 线是否活动 |
ll_msc_is_data_command_inhibited() |
查询使用 DAT 的命令是否被禁止 |
ll_msc_is_command_inhibited() |
查询命令下发是否被禁止 |
HOST_CTRL1
| API | 用途 |
|---|---|
ll_msc_host_control1_set() |
覆盖写完整 HOST_CTRL1_R |
ll_msc_host_control1_get() |
读取完整 HOST_CTRL1_R |
ll_msc_card_detect_test_level_select() |
选择卡检测测试电平 |
ll_msc_card_detect_pin_select() |
选择卡检测输入引脚 |
ll_msc_is_card_detect_test_level_selected() |
查询卡检测源 |
ll_msc_card_detect_test_level_inserted_set() |
测试模式报告卡已插入 |
ll_msc_card_detect_test_level_removed_set() |
测试模式报告卡已移除 |
ll_msc_is_card_detect_test_level_inserted() |
读取测试插入电平 |
ll_msc_8bit_bus_select() |
选择 8-bit DAT 总线编码 |
ll_msc_bus_width_get() |
读取当前 DAT 总线宽度 |
ll_msc_dma_select_set() |
选择 SDMA/ADMA2/ADMA3 |
ll_msc_dma_select_get() |
读取 DMA 选择 |
ll_msc_high_speed_select() |
选择 high-speed timing |
ll_msc_normal_speed_select() |
选择 normal-speed timing |
ll_msc_is_high_speed_selected() |
查询是否 high-speed timing |
ll_msc_4bit_bus_select() |
选择 4-bit DAT 总线 |
ll_msc_1bit_bus_select() |
选择 1-bit DAT 总线 |
电源控制
| API | 用途 |
|---|---|
ll_msc_power_control_set() |
覆盖写完整 PWR_CTRL_R |
ll_msc_power_control_get() |
读取完整 PWR_CTRL_R |
ll_msc_vdd2_voltage_set() |
设置 VDD2 电压编码 |
ll_msc_vdd2_voltage_get() |
读取 VDD2 电压编码 |
ll_msc_vdd2_power_enable() |
请求 VDD2 供电 |
ll_msc_vdd2_power_disable() |
关闭 VDD2 供电请求 |
ll_msc_is_vdd2_power_enabled() |
查询 VDD2 供电请求 |
ll_msc_vdd1_voltage_set() |
设置 VDD1 电压编码 |
ll_msc_vdd1_voltage_get() |
读取 VDD1 电压编码 |
ll_msc_vdd1_power_enable() |
请求 VDD1 供电 |
ll_msc_vdd1_power_disable() |
关闭 VDD1 供电请求 |
ll_msc_is_vdd1_power_enabled() |
查询 VDD1 供电请求 |
Block gap 和唤醒
| API | 用途 |
|---|---|
ll_msc_block_gap_control_set() |
覆盖写完整 BGAP_CTRL_R |
ll_msc_block_gap_control_get() |
读取完整 BGAP_CTRL_R |
ll_msc_block_gap_interrupt_enable() |
开启 block-gap 中断 |
ll_msc_block_gap_interrupt_disable() |
关闭 block-gap 中断 |
ll_msc_is_block_gap_interrupt_enabled() |
查询 block-gap 中断 |
ll_msc_read_wait_enable() |
开启 SD read wait |
ll_msc_read_wait_disable() |
关闭 SD read wait |
ll_msc_is_read_wait_enabled() |
查询 SD read wait |
ll_msc_continue_request() |
请求跨越 block gap 继续 |
ll_msc_is_continue_request_pending() |
查询 continue 请求是否挂起 |
ll_msc_stop_at_block_gap_request() |
请求在下一 block gap 停止 |
ll_msc_is_stop_at_block_gap_request_pending() |
查询停止请求是否挂起 |
ll_msc_wakeup_control_set() |
覆盖写完整 WUP_CTRL_R |
ll_msc_wakeup_control_get() |
读取完整 WUP_CTRL_R |
ll_msc_card_removal_wakeup_enable() |
开启卡移除唤醒 |
ll_msc_card_removal_wakeup_disable() |
关闭卡移除唤醒 |
ll_msc_is_card_removal_wakeup_enabled() |
查询卡移除唤醒 |
ll_msc_card_insert_wakeup_enable() |
开启卡插入唤醒 |
ll_msc_card_insert_wakeup_disable() |
关闭卡插入唤醒 |
ll_msc_is_card_insert_wakeup_enabled() |
查询卡插入唤醒 |
ll_msc_card_init_wakeup_enable() |
开启卡中断唤醒 |
ll_msc_card_init_wakeup_disable() |
关闭卡中断唤醒 |
ll_msc_is_card_init_wakeup_enabled() |
查询卡中断唤醒 |
时钟、超时和复位
| API | 用途 |
|---|---|
ll_msc_clock_control_set() |
覆盖写完整 CLK_CTRL_R |
ll_msc_clock_control_get() |
读取完整 CLK_CTRL_R |
ll_msc_card_clock_low_power_enable() |
开启空闲卡时钟门控 |
ll_msc_card_clock_low_power_disable() |
关闭空闲卡时钟门控 |
ll_msc_is_card_clock_low_power_enabled() |
查询空闲卡时钟门控 |
ll_msc_pll_enable() |
请求时钟 PLL |
ll_msc_pll_disable() |
关闭时钟 PLL 请求 |
ll_msc_is_pll_enabled() |
查询时钟 PLL 请求 |
ll_msc_sd_clock_enable() |
驱动 SDCLK/RCLK |
ll_msc_sd_clock_disable() |
停止 SDCLK/RCLK |
ll_msc_is_sd_clock_enabled() |
查询卡时钟使能 |
ll_msc_is_internal_clock_stable() |
查询内部时钟稳定状态 |
ll_msc_internal_clock_enable() |
请求内部时钟 |
ll_msc_internal_clock_disable() |
关闭内部时钟 |
ll_msc_is_internal_clock_enabled() |
查询内部时钟请求 |
ll_msc_timeout_count_set() |
设置 DAT timeout 编码 |
ll_msc_timeout_count_get() |
读取 DAT timeout 编码 |
ll_msc_software_reset_get() |
读取仍挂起的软复位请求 |
ll_msc_software_reset() |
请求 DAT/CMD/ALL 软复位 |
ll_msc_is_software_reset_pending() |
查询指定软复位是否仍未完成 |
中断控制
| API | 用途 |
|---|---|
ll_msc_normal_interrupt_status_get() |
读取 normal 中断状态 |
ll_msc_normal_interrupt_status_clear() |
W1C 清除 normal 事件 |
ll_msc_error_interrupt_status_get() |
读取 error 中断状态 |
ll_msc_error_interrupt_status_clear() |
W1C 清除 error 事件 |
ll_msc_interrupt_status_get() |
组合读取 normal 和 error 状态 |
ll_msc_interrupt_status_clear() |
同时清除 normal 和 error 事件 |
ll_msc_normal_interrupt_status_enable_set() |
覆盖写 normal status enable |
ll_msc_normal_interrupt_status_enable_get() |
读取 normal status enable |
ll_msc_error_interrupt_status_enable_set() |
覆盖写 error status enable |
ll_msc_error_interrupt_status_enable_get() |
读取 error status enable |
ll_msc_normal_interrupt_signal_enable_set() |
覆盖写 normal signal enable |
ll_msc_normal_interrupt_signal_enable_get() |
读取 normal signal enable |
ll_msc_error_interrupt_signal_enable_set() |
覆盖写 error signal enable |
ll_msc_error_interrupt_signal_enable_get() |
读取 error signal enable |
ll_msc_auto_command_status_get() |
读取自动命令错误详情 |
HOST_CTRL2
| API | 用途 |
|---|---|
ll_msc_host_control2_set() |
覆盖写完整 HOST_CTRL2_R |
ll_msc_host_control2_get() |
读取完整 HOST_CTRL2_R |
ll_msc_async_interrupt_enable() |
开启异步中断 |
ll_msc_async_interrupt_disable() |
关闭异步中断 |
ll_msc_is_async_interrupt_enabled() |
查询异步中断 |
ll_msc_64bit_addressing_select() |
选择 64-bit 系统寻址 |
ll_msc_32bit_addressing_select() |
选择 32-bit 系统寻址 |
ll_msc_is_64bit_addressing_selected() |
查询 64-bit 系统寻址 |
ll_msc_host_v4_mode_select() |
选择 Host v4 模式 |
ll_msc_host_v3_compatible_mode_select() |
选择 Host v3 兼容模式 |
ll_msc_is_host_v4_mode_selected() |
查询 Host v4 模式 |
ll_msc_adma3_auto_cmd23_select() |
ADMA3 使用自动 CMD23 |
ll_msc_adma3_auto_cmd12_select() |
ADMA3 使用自动 CMD12 |
ll_msc_is_adma3_auto_cmd23_selected() |
查询 ADMA3 自动命令 |
ll_msc_adma2_length_26bit_select() |
选择 26-bit ADMA2 length |
ll_msc_adma2_length_16bit_select() |
选择 16-bit ADMA2 length |
ll_msc_is_adma2_length_26bit_selected() |
查询 ADMA2 length 模式 |
ll_msc_tuned_sample_clock_select() |
选择 tuned sampling clock |
ll_msc_fixed_sample_clock_select() |
选择 fixed sampling clock |
ll_msc_is_tuned_sample_clock_selected() |
查询采样时钟选择 |
ll_msc_execute_tuning_start() |
置位 EXEC_TUNING 启动调谐 |
ll_msc_execute_tuning_clear() |
清除 EXEC_TUNING |
ll_msc_is_execute_tuning_active() |
查询调谐是否仍在进行 |
ll_msc_1v8_signaling_enable() |
开启 1.8 V signaling |
ll_msc_1v8_signaling_disable() |
关闭 1.8 V signaling |
ll_msc_is_1v8_signaling_enabled() |
查询 1.8 V signaling |
ll_msc_uhs_mode_set() |
设置 SD/eMMC 速度模式 |
ll_msc_uhs_mode_get() |
读取 SD/eMMC 速度模式 |
能力寄存器
| API | 用途 |
|---|---|
ll_msc_capabilities1_get() |
读取固定 CAPABILITIES1_R |
ll_msc_is_async_interrupt_supported() |
查询异步中断能力 |
ll_msc_is_64bit_system_address_v3_supported() |
查询 v3 64-bit 地址能力 |
ll_msc_is_64bit_system_address_v4_supported() |
查询 v4 64-bit 地址能力 |
ll_msc_is_suspend_resume_supported() |
查询 suspend/resume 能力 |
ll_msc_is_sdma_supported() |
查询 SDMA 能力 |
ll_msc_is_high_speed_supported() |
查询 high-speed 能力 |
ll_msc_is_adma2_supported() |
查询 ADMA2 能力 |
ll_msc_is_embedded_8bit_supported() |
查询 embedded 8-bit 能力 |
ll_msc_max_block_length_get() |
读取最大 block length 编码 |
ll_msc_capabilities2_get() |
读取扩展 CAPABILITIES2_R |
ll_msc_is_adma3_supported() |
查询 ADMA3 能力 |
ll_msc_retuning_mode_get() |
读取重调谐模式 |
ll_msc_is_sdr50_tuning_required() |
查询 SDR50 是否要求调谐 |
ll_msc_retune_count_get() |
读取 timer retuning count |
ll_msc_is_ddr50_supported() |
查询 DDR50 能力 |
ll_msc_is_sdr104_supported() |
查询 SDR104 能力 |
ll_msc_is_sdr50_supported() |
查询 SDR50 能力 |
ll_msc_current_capabilities1_get() |
读取当前 VDD1 能力 |
ll_msc_max_current_1v8_get() |
读取 VDD1 1.8 V 最大电流编码 |
ll_msc_max_current_3v0_get() |
读取 VDD1 3.0 V 最大电流编码 |
ll_msc_max_current_3v3_get() |
读取 VDD1 3.3 V 最大电流编码 |
ll_msc_current_capabilities2_get() |
读取当前 VDD2 能力 |
ll_msc_max_current_vdd2_1v8_get() |
读取 VDD2 1.8 V 最大电流编码 |
Force event 和 ADMA
| API | 用途 |
|---|---|
ll_msc_force_auto_command_status() |
W1S 强制自动命令错误状态 |
ll_msc_force_error_interrupt_status() |
W1S 强制错误中断事件 |
ll_msc_adma_error_status_get() |
读取 ADMA_ERR_STAT_R |
ll_msc_is_adma_length_error() |
查询 ADMA length mismatch |
ll_msc_adma_error_state_get() |
读取 ADMA 错误状态机编码 |
ll_msc_adma_address_set() |
写 64-bit ADMA 系统地址 |
ll_msc_adma_address_get() |
读 64-bit ADMA 系统地址 |
ll_msc_adma_id_address_set() |
写 64-bit ADMA3 descriptor 地址 |
ll_msc_adma_id_address_get() |
读 64-bit ADMA3 descriptor 地址 |
Preset、Slot 和版本
| API | 用途 |
|---|---|
ll_msc_preset_value_get() |
按模式读取 preset 寄存器 |
ll_msc_preset_driver_strength_get() |
读取 preset 驱动强度 |
ll_msc_preset_clock_generator_select_get() |
读取 preset 时钟源 |
ll_msc_preset_frequency_select_get() |
读取 preset frequency select |
ll_msc_slot_interrupt_status_get() |
读取 slot 中断状态 |
ll_msc_interrupting_slot_get() |
读取正在中断的 slot bitmap |
ll_msc_host_version_get() |
读取 Host 控制器版本 |
ll_msc_host_vendor_version_get() |
读取 Host 厂商版本字段 |
ll_msc_host_spec_version_get() |
读取 Host 规范版本字段 |
ll_msc_mshc_version_id_get() |
读取 MSHC version ID |
ll_msc_mshc_version_id_value_get() |
读取 MSHC version ID 字段 |
ll_msc_mshc_version_type_get() |
读取 MSHC version type |
ll_msc_mshc_version_type_value_get() |
读取 MSHC version type 字段 |
MSHC 和 eMMC 控制
| API | 用途 |
|---|---|
ll_msc_mshc_control_set() |
覆盖写完整 MSHC_CTRL_R |
ll_msc_mshc_control_get() |
读取完整 MSHC_CTRL_R |
ll_msc_software_clock_gating_disable() |
禁止软件时钟门控 |
ll_msc_software_clock_gating_enable() |
允许软件时钟门控 |
ll_msc_is_software_clock_gating_disabled() |
查询软件时钟门控 |
ll_msc_command_conflict_check_enable() |
开启命令冲突检查 |
ll_msc_command_conflict_check_disable() |
关闭命令冲突检查 |
ll_msc_is_command_conflict_check_enabled() |
查询命令冲突检查 |
ll_msc_emmc_control_set() |
覆盖写完整 EMMC_CTRL_R |
ll_msc_emmc_control_get() |
读取完整 EMMC_CTRL_R |
ll_msc_cqe_prefetch_disable() |
禁止 CQE prefetch |
ll_msc_cqe_prefetch_enable() |
允许 CQE prefetch |
ll_msc_is_cqe_prefetch_disabled() |
查询 CQE prefetch |
ll_msc_cqe_alternate_algorithm_select() |
选择 alternate CQE 算法 |
ll_msc_cqe_default_algorithm_select() |
选择 default CQE 算法 |
ll_msc_is_cqe_alternate_algorithm_selected() |
查询 CQE 算法 |
ll_msc_emmc_reset_output_enable() |
驱动 eMMC reset pad |
ll_msc_emmc_reset_output_disable() |
停止驱动 eMMC reset pad |
ll_msc_is_emmc_reset_output_enabled() |
查询 reset pad 驱动 |
ll_msc_emmc_reset_release() |
释放低有效 eMMC reset |
ll_msc_emmc_reset_assert() |
置位低有效 eMMC reset |
ll_msc_is_emmc_reset_released() |
查询 eMMC reset 状态 |
ll_msc_data_crc_check_disable() |
关闭 eMMC 数据 CRC 检查 |
ll_msc_data_crc_check_enable() |
开启 eMMC 数据 CRC 检查 |
ll_msc_is_data_crc_check_disabled() |
查询数据 CRC 检查 |
ll_msc_emmc_card_select() |
选择 eMMC 卡行为 |
ll_msc_non_emmc_card_select() |
选择非 eMMC 卡行为 |
ll_msc_is_emmc_card_selected() |
查询是否 eMMC 模式 |
调谐控制
| API | 用途 |
|---|---|
ll_msc_auto_tuning_control_set() |
覆盖写完整 AT_CTRL_R |
ll_msc_auto_tuning_control_get() |
读取完整 AT_CTRL_R |
ll_msc_tuning_window_threshold_set() |
设置采样窗口阈值 |
ll_msc_tuning_window_threshold_get() |
读取采样窗口阈值 |
ll_msc_tuning_post_change_delay_set() |
设置相位切换后延时 |
ll_msc_tuning_post_change_delay_get() |
读取相位切换后延时 |
ll_msc_tuning_pre_change_delay_set() |
设置相位切换前延时 |
ll_msc_tuning_pre_change_delay_get() |
读取相位切换前延时 |
ll_msc_tuning_clock_stop_enable() |
调谐相位切换时停止卡时钟 |
ll_msc_tuning_clock_stop_disable() |
调谐相位切换时保持卡时钟 |
ll_msc_is_tuning_clock_stop_enabled() |
查询调谐时钟停止 |
ll_msc_software_tuning_enable() |
开启软件调谐 |
ll_msc_software_tuning_disable() |
关闭软件调谐 |
ll_msc_is_software_tuning_enabled() |
查询软件调谐 |
ll_msc_tuning_error_report_enable() |
开启调谐错误上报 |
ll_msc_tuning_error_report_disable() |
关闭调谐错误上报 |
ll_msc_is_tuning_error_report_enabled() |
查询调谐错误上报 |
ll_msc_first_matching_tuning_window_select() |
选择首个匹配窗口 |
ll_msc_widest_tuning_window_select() |
选择最宽窗口 |
ll_msc_is_first_matching_tuning_window_selected() |
查询窗口选择策略 |
ll_msc_auto_tuning_status_get() |
读取 AT_STAT_R |
ll_msc_phase_select_code_set() |
设置软件调谐采样相位 |
ll_msc_phase_select_code_get() |
读取当前采样相位 |
低功耗控制
| API | 用途 |
|---|---|
ll_msc_low_power_control_set() |
覆盖写完整 LP_CTRL_R |
ll_msc_low_power_control_get() |
读取完整 LP_CTRL_R |
ll_msc_low_power_threshold_set() |
设置 buffered-write 低功耗阈值 |
ll_msc_low_power_threshold_get() |
读取低功耗阈值 |
ll_msc_low_power_threshold_enable() |
开启低功耗阈值 |
ll_msc_low_power_threshold_disable() |
关闭低功耗阈值 |
ll_msc_is_low_power_threshold_enabled() |
查询低功耗阈值 |
ll_msc_low_power_count_set() |
设置空闲时钟门控计数 |
ll_msc_low_power_count_get() |
读取空闲时钟门控计数 |
4.2 通用约定
- 除
ll_msc_is_instance()外,所有接口的第一个参数都是msc_regs_t *mscx,必须指向MSC0或MSC1。LL 层不解引用前检查空指针。 - 字段 setter 只修改目标位域;全寄存器 setter 覆盖整个寄存器。调用方必须决定哪些字段需要先读回、哪些保留位必须保持为 0。
- 参数类型是
uint8_t、uint16_t或uint32_t不代表字段范围已校验。例如 timeout count 的0xF、DMA select 的1、UHS mode 的5~7都是保留值。 - 所有
is_*接口只读取当前状态,不清除状态;get接口同样没有清状态副作用。 - 所有写接口只表示 MMIO 写已经发出,不表示硬件完成。命令完成、传输完成、错误和复位完成必须通过状态/中断判断。
- LL 层没有错误码、等待、重试、超时和互斥。需要这些能力时,应在 HAL 或控制器层实现。
4.3 实例检查和原始访问 API
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_msc_is_instance(mscx) |
const msc_regs_t *mscx |
bool |
仅当指针等于 MSC0 或 MSC1 时返回 true;不访问寄存器 |
ll_msc_reg8_write(mscx, offset, value) |
uint32_t offset,uint8_t value |
void |
在 mscx + offset 处写 8-bit;调用方负责访问属性、保留位和 W1C/W1S 语义 |
ll_msc_reg8_read(mscx, offset) |
uint32_t offset |
uint8_t |
从 mscx + offset 读 8-bit |
ll_msc_reg16_write(mscx, offset, value) |
uint32_t offset,uint16_t value |
void |
写 16-bit;地址应满足半字对齐要求 |
ll_msc_reg16_read(mscx, offset) |
uint32_t offset |
uint16_t |
读 16-bit;地址应满足半字对齐要求 |
ll_msc_reg32_write(mscx, offset, value) |
uint32_t offset,uint32_t value |
void |
写 32-bit;地址应满足字对齐要求 |
ll_msc_reg32_read(mscx, offset) |
uint32_t offset |
uint32_t |
读 32-bit;地址应满足字对齐要求 |
原始访问接口只适合 bring-up、寄存器表和调试。正常驱动应优先使用命名位域 helper,避免直接覆盖保留字段或把只读寄存器当成可写寄存器。
4.4 传输参数和 XFER_MODE API
本节接口访问 SDMASA_R、BLOCKSIZE_R、BLOCKCOUNT_R、ARGUMENT_R 和 XFER_MODE_R。ll_msc_transfer_mode_set() 覆盖整个寄存器,其余 setter 只修改目标字段。
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_msc_sdma_address_set(mscx, address) |
uint32_t address |
void |
写 v3 SDMA 系统地址;Host v4 下该寄存器含义变为 block count |
ll_msc_sdma_address_get(mscx) |
— | uint32_t |
读取 SDMASA_R 当前值 |
ll_msc_32bit_block_count_set(mscx, block_count) |
uint32_t block_count |
void |
Host v4 下写 32-bit block count;通常让 BLOCKCOUNT_R=0 选择该路径 |
ll_msc_32bit_block_count_get(mscx) |
— | uint32_t |
Host v4 下读取 32-bit block count |
ll_msc_sdma_buffer_boundary_set(mscx, boundary) |
msc_sdma_boundary_t boundary |
void |
写 BLOCKSIZE_R.SDMA_BUF_BDARY,范围 4~512 KiB |
ll_msc_sdma_buffer_boundary_get(mscx) |
— | msc_sdma_boundary_t |
读取 SDMA boundary |
ll_msc_block_size_set(mscx, block_size) |
uint16_t block_size |
void |
写 SHIFT 0 的 12-bit block size 字段;0 表示无数据,HAL 数据路径限制 1~2048 字节 |
ll_msc_block_size_get(mscx) |
— | uint16_t |
读取块大小字段 |
ll_msc_block_count_set(mscx, block_count) |
uint16_t block_count |
void |
覆盖写 BLOCKCOUNT_R;0 的含义取决于传输模式和 Host 版本 |
ll_msc_block_count_get(mscx) |
— | uint16_t |
读取 16-bit block count |
ll_msc_argument_set(mscx, argument) |
uint32_t argument |
void |
写下一条命令的 32-bit 参数 |
ll_msc_argument_get(mscx) |
— | uint32_t |
读取当前已写入的命令参数 |
ll_msc_transfer_mode_set(mscx, mode) |
uint16_t mode |
void |
覆盖写 XFER_MODE_R;必须自行保留未修改字段 |
ll_msc_transfer_mode_get(mscx) |
— | uint16_t |
读取完整 XFER_MODE_R |
ll_msc_transfer_mode_make(...) |
8 个布尔/枚举参数,见下 | uint16_t |
不访问硬件,生成完整 XFER_MODE_R 候选值 |
ll_msc_response_interrupt_disable(mscx) |
— | void |
置 RESP_INT_DIS |
ll_msc_response_interrupt_enable(mscx) |
— | void |
清 RESP_INT_DIS |
ll_msc_is_response_interrupt_disabled(mscx) |
— | bool |
查询 RESP_INT_DIS |
ll_msc_response_error_check_enable(mscx) |
— | void |
置 RESP_ERR_CHK_EN,只适用于 R1/R5 |
ll_msc_response_error_check_disable(mscx) |
— | void |
清 RESP_ERR_CHK_EN |
ll_msc_is_response_error_check_enabled(mscx) |
— | bool |
查询响应错误检查 |
ll_msc_response_check_type_set(mscx, type) |
msc_response_check_type_t type |
void |
写 RESP_TYPE:R1 或 R5 |
ll_msc_response_check_type_get(mscx) |
— | msc_response_check_type_t |
读取响应错误检查类型 |
ll_msc_multi_block_select(mscx) |
— | void |
置 MULTI_BLK_SEL |
ll_msc_single_block_select(mscx) |
— | void |
清 MULTI_BLK_SEL |
ll_msc_is_multi_block_selected(mscx) |
— | bool |
查询是否为多块传输 |
ll_msc_card_to_host_direction_select(mscx) |
— | void |
置 DATA_XFER_DIR,选择卡到主机 |
ll_msc_host_to_card_direction_select(mscx) |
— | void |
清 DATA_XFER_DIR,选择主机到卡或无数据 |
ll_msc_is_card_to_host_direction_selected(mscx) |
— | bool |
查询是否为卡到主机方向 |
ll_msc_auto_command_set(mscx, auto_command) |
msc_auto_command_t auto_command |
void |
写 AUTO_CMD_EN;SDIO 传输应禁用自动命令 |
ll_msc_auto_command_get(mscx) |
— | msc_auto_command_t |
读取自动命令选择 |
ll_msc_block_count_enable(mscx) |
— | void |
置 BLK_CNT_EN |
ll_msc_block_count_disable(mscx) |
— | void |
清 BLK_CNT_EN |
ll_msc_is_block_count_enabled(mscx) |
— | bool |
查询是否使用 BLOCKCOUNT_R 停止传输 |
ll_msc_dma_enable(mscx) |
— | void |
置 DMA_EN |
ll_msc_dma_disable(mscx) |
— | void |
清 DMA_EN |
ll_msc_is_dma_enabled(mscx) |
— | bool |
查询本次传输是否启用 DMA |
ll_msc_transfer_mode_make() 的参数顺序为:
uint16_t ll_msc_transfer_mode_make(bool response_interrupt_disable,
bool response_error_check,
msc_response_check_type_t response_check_type,
bool multi_block,
bool read_direction,
msc_auto_command_t auto_command,
bool block_count_enable,
bool dma_enable);
该函数只打包上面列出的字段,不检查 R1/R5 与命令响应类型是否匹配,也不检查 SDIO/direction/auto command 的组合是否合法。
4.5 命令和响应 API
本节接口访问 CMD_R 和 RESP01_R~RESP67_R。写 CMD_R 会触发命令,读取响应前必须确认命令已经完成。
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_msc_command_set(mscx, command) |
uint16_t command |
void |
写入完整 CMD_R 并触发命令;不检查 inhibit 状态 |
ll_msc_command_get(mscx) |
— | uint16_t |
读取当前 CMD_R 控制 word,不代表命令完成 |
ll_msc_command_make(...) |
7 个参数,见下 | uint16_t |
不访问硬件,打包命令索引、类型、数据和响应选择 |
ll_msc_command_index_get(mscx) |
— | uint8_t |
读取 CMD_INDEX[5:0] |
ll_msc_command_type_get(mscx) |
— | msc_command_type_t |
读取 CMD_TYPE |
ll_msc_is_command_data_present(mscx) |
— | bool |
查询 DATA_PRES_SEL |
ll_msc_is_command_index_check_enabled(mscx) |
— | bool |
查询响应索引检查 |
ll_msc_is_command_crc_check_enabled(mscx) |
— | bool |
查询响应 CRC 检查 |
ll_msc_is_sub_command(mscx) |
— | bool |
查询 SUB_CMD_FLAG |
ll_msc_command_response_type_get(mscx) |
— | msc_response_type_t |
读取 RESP_TYPE_SEL |
ll_msc_response01_get(mscx) |
— | uint32_t |
读取响应位 [39:8] |
ll_msc_response23_get(mscx) |
— | uint32_t |
读取响应位 [71:40] |
ll_msc_response45_get(mscx) |
— | uint32_t |
读取响应位 [103:72] |
ll_msc_response67_get(mscx) |
— | uint32_t |
读取响应位 [135:104];自动命令的 32-bit 响应也在此 |
ll_msc_response_get(mscx, index) |
msc_response_word_t index |
uint32_t |
按枚举读取响应 word;越界值回退到 RESP67_R |
ll_msc_command_make() 的签名如下;command_index 的有效命令号范围是 0~63,协议层必须自行保证命令类型、响应类型、索引/CRC 检查位一致:
uint16_t ll_msc_command_make(uint8_t command_index,
msc_command_type_t command_type,
bool data_present,
bool index_check,
bool crc_check,
bool sub_command,
msc_response_type_t response_type);
4.6 数据端口和 PSTATE API
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_msc_buffer_data_write(mscx, data) |
uint32_t data |
void |
向 BUF_DATA_R 写一个 PIO word;不检查 BUF_WR_EN |
ll_msc_buffer_data_read(mscx) |
— | uint32_t |
从 BUF_DATA_R 读一个 word;不检查 BUF_RD_EN |
ll_msc_present_state_get(mscx) |
— | uint32_t |
读取完整 PSTATE_REG 快照 |
ll_msc_is_sub_command_active(mscx) |
— | bool |
查询 SUB_CMD_STAT |
ll_msc_is_command_issue_error(mscx) |
— | bool |
查询 CMD_ISSU_ERR |
ll_msc_is_host_regulated_voltage_stable(mscx) |
— | bool |
查询 HOST_REG_VOL |
ll_msc_command_line_level_get(mscx) |
— | bool |
返回 CMD 线采样电平;UHS-II 场景不适用 |
ll_msc_data_line_level_get(mscx) |
— | uint8_t |
返回 DAT[7:0] 电平,bit0 对应 DAT0 |
ll_msc_is_read_buffer_ready(mscx) |
— | bool |
查询 BUF_RD_EN,使用 PIO 读前必须检查 |
ll_msc_is_write_buffer_ready(mscx) |
— | bool |
查询 BUF_WR_EN,使用 PIO 写前必须检查 |
ll_msc_is_read_transfer_active(mscx) |
— | bool |
查询 RD_XFER_ACT |
ll_msc_is_write_transfer_active(mscx) |
— | bool |
查询 WR_XFER_ACT |
ll_msc_is_retune_requested(mscx) |
— | bool |
查询 RE_TUNE_REQ |
ll_msc_is_data_line_active(mscx) |
— | bool |
查询 DAT_LINE_ACT |
ll_msc_is_data_command_inhibited(mscx) |
— | bool |
查询 CMD_INH_DAT |
ll_msc_is_command_inhibited(mscx) |
— | bool |
查询 CMD_INH |
PIO 接口不做 FIFO 水位管理。应用必须按 block_size × block_count 计算总 word 数,并在读/写就绪时访问数据端口;不能仅凭一次 BUF_RD_EN 判断整个 block 已可读。
4.7 HOST_CTRL1 API
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_msc_host_control1_set(mscx, control) |
uint8_t control |
void |
覆盖写完整 HOST_CTRL1_R |
ll_msc_host_control1_get(mscx) |
— | uint8_t |
读取完整 HOST_CTRL1_R |
ll_msc_card_detect_test_level_select(mscx) |
— | void |
置 CARD_DETECT_SIG_LVL,使用测试电平 |
ll_msc_card_detect_pin_select(mscx) |
— | void |
清 CARD_DETECT_SIG_LVL,使用卡检测引脚 |
ll_msc_is_card_detect_test_level_selected(mscx) |
— | bool |
查询卡检测源 |
ll_msc_card_detect_test_level_inserted_set(mscx) |
— | void |
测试模式报告卡插入 |
ll_msc_card_detect_test_level_removed_set(mscx) |
— | void |
测试模式报告卡移除 |
ll_msc_is_card_detect_test_level_inserted(mscx) |
— | bool |
查询测试插入电平 |
ll_msc_8bit_bus_select(mscx) |
— | void |
置 EXT_DAT_XFER,选择 8-bit 编码 |
ll_msc_bus_width_get(mscx) |
— | msc_bus_width_t |
按 EXT_DAT_XFER 和 DAT_XFER_W 返回 1/4/8-bit |
ll_msc_dma_select_set(mscx, dma_select) |
msc_dma_select_t dma_select |
void |
写 DMA_SEL;值 1 保留 |
ll_msc_dma_select_get(mscx) |
— | msc_dma_select_t |
读 DMA_SEL 编码 |
ll_msc_high_speed_select(mscx) |
— | void |
置 HS_EN |
ll_msc_normal_speed_select(mscx) |
— | void |
清 HS_EN |
ll_msc_is_high_speed_selected(mscx) |
— | bool |
查询高速 timing |
ll_msc_4bit_bus_select(mscx) |
— | void |
清 EXT_DAT_XFER,置 DAT_XFER_W |
ll_msc_1bit_bus_select(mscx) |
— | void |
清 EXT_DAT_XFER 和 DAT_XFER_W |
8-bit helper 只生成寄存器编码。G32S10 板级通常只连接 DAT0..DAT3,HAL 也把 8-bit 配置视为不支持;除非确认 SoC 和板级均具备 DAT[7:0],否则不要调用 ll_msc_8bit_bus_select()。
4.8 电源控制 API
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_msc_power_control_set(mscx, control) |
uint8_t control |
void |
覆盖写完整 PWR_CTRL_R |
ll_msc_power_control_get(mscx) |
— | uint8_t |
读取完整 PWR_CTRL_R |
ll_msc_vdd2_voltage_set(mscx, voltage) |
msc_vdd2_voltage_t voltage |
void |
写 VDD2 电压字段;当前仅定义 1.8 V 编码 |
ll_msc_vdd2_voltage_get(mscx) |
— | msc_vdd2_voltage_t |
读取 VDD2 电压编码 |
ll_msc_vdd2_power_enable(mscx) |
— | void |
置 SD_BUS_PWR_VDD2 |
ll_msc_vdd2_power_disable(mscx) |
— | void |
清 SD_BUS_PWR_VDD2 |
ll_msc_is_vdd2_power_enabled(mscx) |
— | bool |
查询 VDD2 供电请求 |
ll_msc_vdd1_voltage_set(mscx, voltage) |
msc_vdd1_voltage_t voltage |
void |
写 VDD1 电压字段;同一编码在 SD/eMMC 下含义不同 |
ll_msc_vdd1_voltage_get(mscx) |
— | msc_vdd1_voltage_t |
读取 VDD1 电压编码 |
ll_msc_vdd1_power_enable(mscx) |
— | void |
置 SD_BUS_PWR_VDD1 |
ll_msc_vdd1_power_disable(mscx) |
— | void |
清 SD_BUS_PWR_VDD1 |
ll_msc_is_vdd1_power_enabled(mscx) |
— | bool |
查询 VDD1 供电请求 |
这些接口不检查 CAPABILITIES1_R 是否支持目标电压,也不等待外部电源稳定。切换电压时应先按协议停止时钟和传输,并由板级代码控制稳压器。
4.9 Block gap 和唤醒 API
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_msc_block_gap_control_set(mscx, control) |
uint8_t control |
void |
覆盖写完整 BGAP_CTRL_R |
ll_msc_block_gap_control_get(mscx) |
— | uint8_t |
读取 BGAP_CTRL_R,包括请求位 |
ll_msc_block_gap_interrupt_enable(mscx) |
— | void |
置 INT_AT_BGAP |
ll_msc_block_gap_interrupt_disable(mscx) |
— | void |
清 INT_AT_BGAP |
ll_msc_is_block_gap_interrupt_enabled(mscx) |
— | bool |
查询 block-gap 中断 |
ll_msc_read_wait_enable(mscx) |
— | void |
置 RD_WAIT_CTRL,启用 SD read wait |
ll_msc_read_wait_disable(mscx) |
— | void |
清 RD_WAIT_CTRL |
ll_msc_is_read_wait_enabled(mscx) |
— | bool |
查询 read wait |
ll_msc_continue_request(mscx) |
— | void |
置 CONT_REQ,请求跨越 block gap 继续 |
ll_msc_is_continue_request_pending(mscx) |
— | bool |
查询 continue 请求是否仍挂起 |
ll_msc_stop_at_block_gap_request(mscx) |
— | void |
置 STOP_BG_REQ |
ll_msc_is_stop_at_block_gap_request_pending(mscx) |
— | bool |
查询停止请求是否仍挂起 |
ll_msc_wakeup_control_set(mscx, control) |
uint8_t control |
void |
覆盖写完整 WUP_CTRL_R |
ll_msc_wakeup_control_get(mscx) |
— | uint8_t |
读取唤醒使能 bitmap |
ll_msc_card_removal_wakeup_enable(mscx) |
— | void |
开启卡移除唤醒 |
ll_msc_card_removal_wakeup_disable(mscx) |
— | void |
关闭卡移除唤醒 |
ll_msc_is_card_removal_wakeup_enabled(mscx) |
— | bool |
查询卡移除唤醒 |
ll_msc_card_insert_wakeup_enable(mscx) |
— | void |
开启卡插入唤醒 |
ll_msc_card_insert_wakeup_disable(mscx) |
— | void |
关闭卡插入唤醒 |
ll_msc_is_card_insert_wakeup_enabled(mscx) |
— | bool |
查询卡插入唤醒 |
ll_msc_card_init_wakeup_enable(mscx) |
— | void |
置 CARD_INIT,开启卡中断唤醒 |
ll_msc_card_init_wakeup_disable(mscx) |
— | void |
清 CARD_INIT |
ll_msc_is_card_init_wakeup_enabled(mscx) |
— | bool |
查询卡中断唤醒 |
CONT_REQ 和 STOP_BG_REQ 由硬件在请求被接受或完成后更新;LL 只负责置位和轮询。WUP_CTRL_R.CARD_INIT 这个字段名容易误解,它实际控制由 NORMAL_INT_STAT_R.CARD_INTERRUPT 触发唤醒。
4.10 时钟、超时和软件复位 API
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_msc_clock_control_set(mscx, control) |
uint16_t control |
void |
覆盖写完整 CLK_CTRL_R |
ll_msc_clock_control_get(mscx) |
— | uint16_t |
读取控制位和 INTERNAL_CLK_STABLE 状态 |
ll_msc_card_clock_low_power_enable(mscx) |
— | void |
置 CARD_LP_EN,允许空闲卡时钟门控 |
ll_msc_card_clock_low_power_disable(mscx) |
— | void |
清 CARD_LP_EN |
ll_msc_is_card_clock_low_power_enabled(mscx) |
— | bool |
查询空闲卡时钟门控 |
ll_msc_pll_enable(mscx) |
— | void |
置 PLL_ENABLE |
ll_msc_pll_disable(mscx) |
— | void |
清 PLL_ENABLE |
ll_msc_is_pll_enabled(mscx) |
— | bool |
查询 PLL 请求 |
ll_msc_sd_clock_enable(mscx) |
— | void |
置 SD_CLK_EN,驱动卡时钟 |
ll_msc_sd_clock_disable(mscx) |
— | void |
清 SD_CLK_EN,停止卡时钟 |
ll_msc_is_sd_clock_enabled(mscx) |
— | bool |
查询卡时钟输出使能 |
ll_msc_is_internal_clock_stable(mscx) |
— | bool |
查询 INTERNAL_CLK_STABLE |
ll_msc_internal_clock_enable(mscx) |
— | void |
置 INTERNAL_CLK_EN |
ll_msc_internal_clock_disable(mscx) |
— | void |
清 INTERNAL_CLK_EN |
ll_msc_is_internal_clock_enabled(mscx) |
— | bool |
查询内部时钟请求 |
ll_msc_timeout_count_set(mscx, timeout_count) |
uint8_t timeout_count |
void |
写 TOUT_CNT,有效 0~14;0 对应 TMCLK × 2^13,14 对应 TMCLK × 2^27 |
ll_msc_timeout_count_get(mscx) |
— | uint8_t |
返回 timeout 编码 0~14;0xF 保留 |
ll_msc_software_reset_get(mscx) |
— | uint8_t |
返回仍挂起的 DAT/CMD/ALL 复位位 |
ll_msc_software_reset(mscx, reset_mask) |
uint8_t reset_mask |
void |
只写尚未挂起的复位请求位;可 OR msc_software_reset_t |
ll_msc_is_software_reset_pending(mscx, reset_mask) |
uint8_t reset_mask |
bool |
查询任一指定复位域是否仍未完成 |
CLK_CTRL_R 中同时有控制位和只读状态位。使用全寄存器 setter 前应先读取当前值,避免把状态位或其它控制字段误写。ll_msc_software_reset() 不等待完成,也不返回超时。
4.11 中断控制 API
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_msc_normal_interrupt_status_get(mscx) |
— | uint16_t |
读取 NORMAL_INT_STAT_R;包含只读状态和 W1C 事件 |
ll_msc_normal_interrupt_status_clear(mscx, mask) |
uint16_t interrupt_mask |
void |
对 normal W1C 位写 1 清除;只读位不受影响 |
ll_msc_error_interrupt_status_get(mscx) |
— | uint16_t |
读取 error 中断状态 |
ll_msc_error_interrupt_status_clear(mscx, mask) |
uint16_t interrupt_mask |
void |
W1C 清除 error 事件 |
ll_msc_interrupt_status_get(mscx) |
— | uint32_t |
返回 ERROR[31:16] 与 NORMAL[15:0] 的组合 |
ll_msc_interrupt_status_clear(mscx, normal_mask, error_mask) |
两个 uint16_t |
void |
同时清除 normal 和 error 事件 |
ll_msc_normal_interrupt_status_enable_set(mscx, value) |
uint16_t value |
void |
覆盖写 normal status latch enable |
ll_msc_normal_interrupt_status_enable_get(mscx) |
— | uint16_t |
读取 normal status latch enable |
ll_msc_error_interrupt_status_enable_set(mscx, value) |
uint16_t value |
void |
覆盖写 error status latch enable |
ll_msc_error_interrupt_status_enable_get(mscx) |
— | uint16_t |
读取 error status latch enable |
ll_msc_normal_interrupt_signal_enable_set(mscx, value) |
uint16_t value |
void |
覆盖写 normal IRQ signal enable |
ll_msc_normal_interrupt_signal_enable_get(mscx) |
— | uint16_t |
读取 normal IRQ signal enable |
ll_msc_error_interrupt_signal_enable_set(mscx, value) |
uint16_t value |
void |
覆盖写 error IRQ signal enable |
ll_msc_error_interrupt_signal_enable_get(mscx) |
— | uint16_t |
读取 error IRQ signal enable |
ll_msc_auto_command_status_get(mscx) |
— | uint16_t |
读取 AUTO_CMD_STAT_R 的 CMD12/CMD23 错误详情 |
normal/error status enable 和 signal enable 都是完整寄存器写接口,不是按位 OR helper。修改单个事件时,应先读取旧值、修改目标位,再写回。
4.12 HOST_CTRL2 API
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_msc_host_control2_set(mscx, control) |
uint16_t control |
void |
写完整 HOST_CTRL2_R,写入前屏蔽 RSVD_KEEP_0 |
ll_msc_host_control2_get(mscx) |
— | uint16_t |
读取完整 HOST_CTRL2_R |
ll_msc_async_interrupt_enable(mscx) |
— | void |
置 ASYNC_INT_EN |
ll_msc_async_interrupt_disable(mscx) |
— | void |
清 ASYNC_INT_EN |
ll_msc_is_async_interrupt_enabled(mscx) |
— | bool |
查询异步中断 |
ll_msc_64bit_addressing_select(mscx) |
— | void |
置 ADDRESSING,使用 64-bit 系统寻址 |
ll_msc_32bit_addressing_select(mscx) |
— | void |
清 ADDRESSING |
ll_msc_is_64bit_addressing_selected(mscx) |
— | bool |
查询系统寻址宽度 |
ll_msc_host_v4_mode_select(mscx) |
— | void |
置 HOST_V4_EN |
ll_msc_host_v3_compatible_mode_select(mscx) |
— | void |
清 HOST_V4_EN |
ll_msc_is_host_v4_mode_selected(mscx) |
— | bool |
查询 Host v4 |
ll_msc_adma3_auto_cmd23_select(mscx) |
— | void |
置 CMD23_EN |
ll_msc_adma3_auto_cmd12_select(mscx) |
— | void |
清 CMD23_EN |
ll_msc_is_adma3_auto_cmd23_selected(mscx) |
— | bool |
查询 ADMA3 自动命令 |
ll_msc_adma2_length_26bit_select(mscx) |
— | void |
置 ADMA2_LEN_MD |
ll_msc_adma2_length_16bit_select(mscx) |
— | void |
清 ADMA2_LEN_MD |
ll_msc_is_adma2_length_26bit_selected(mscx) |
— | bool |
查询 ADMA2 length 格式 |
ll_msc_tuned_sample_clock_select(mscx) |
— | void |
置 SMP_CLK_SEL |
ll_msc_fixed_sample_clock_select(mscx) |
— | void |
清 SMP_CLK_SEL |
ll_msc_is_tuned_sample_clock_selected(mscx) |
— | bool |
查询采样时钟选择 |
ll_msc_execute_tuning_start(mscx) |
— | void |
置 EXEC_TUNING;不发送调谐命令 |
ll_msc_execute_tuning_clear(mscx) |
— | void |
清 EXEC_TUNING;不等待调谐 |
ll_msc_is_execute_tuning_active(mscx) |
— | bool |
查询调谐是否仍在执行 |
ll_msc_1v8_signaling_enable(mscx) |
— | void |
置 legacy 1.8 V signaling 位 |
ll_msc_1v8_signaling_disable(mscx) |
— | void |
清 1.8 V signaling 位 |
ll_msc_is_1v8_signaling_enabled(mscx) |
— | bool |
查询 1.8 V signaling |
ll_msc_uhs_mode_set(mscx, mode) |
msc_speed_mode_t mode |
void |
写 SD/eMMC 速度模式;5~7 保留 |
ll_msc_uhs_mode_get(mscx) |
— | msc_speed_mode_t |
读取速度模式编码 |
Host v4、64-bit addressing、ADMA3 和 64-bit ADMA2 之间存在组合约束。启用前应同时检查 CAPABILITIES1/2_R、descriptor 格式和所选卡协议;LL 不验证这些组合。
4.13 能力寄存器 API
能力接口只读。固定能力来自 CAPABILITIES1/2_R,当前能力来自 CURR_CAPABILITIES1/2_R;电压切换后应重新读取当前能力。
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_msc_capabilities1_get(mscx) |
— | uint32_t |
读取固定 CAPABILITIES1_R |
ll_msc_is_async_interrupt_supported(mscx) |
— | bool |
查询 SD 模式异步中断能力 |
ll_msc_is_64bit_system_address_v3_supported(mscx) |
— | bool |
查询 v3 64-bit 系统地址能力 |
ll_msc_is_64bit_system_address_v4_supported(mscx) |
— | bool |
查询 v4 64-bit 系统地址能力 |
ll_msc_is_suspend_resume_supported(mscx) |
— | bool |
查询 suspend/resume 命令能力 |
ll_msc_is_sdma_supported(mscx) |
— | bool |
查询 SDMA 能力 |
ll_msc_is_high_speed_supported(mscx) |
— | bool |
查询 high-speed timing 能力 |
ll_msc_is_adma2_supported(mscx) |
— | bool |
查询 ADMA2 能力 |
ll_msc_is_embedded_8bit_supported(mscx) |
— | bool |
查询 embedded 8-bit 能力 |
ll_msc_max_block_length_get(mscx) |
— | uint8_t |
返回编码 0/1/2,对应 512/1024/2048 字节;3 保留 |
ll_msc_capabilities2_get(mscx) |
— | uint32_t |
读取固定 CAPABILITIES2_R |
ll_msc_is_adma3_supported(mscx) |
— | bool |
查询 ADMA3 能力 |
ll_msc_retuning_mode_get(mscx) |
— | uint8_t |
0 timer,1 timer+request,2 传输中自动调谐;3 保留 |
ll_msc_is_sdr50_tuning_required(mscx) |
— | bool |
查询 SDR50 是否要求调谐 |
ll_msc_retune_count_get(mscx) |
— | uint8_t |
读取 timer retuning count 字段 |
ll_msc_is_ddr50_supported(mscx) |
— | bool |
查询 DDR50 能力 |
ll_msc_is_sdr104_supported(mscx) |
— | bool |
查询 SDR104 能力 |
ll_msc_is_sdr50_supported(mscx) |
— | bool |
查询 SDR50 能力 |
ll_msc_current_capabilities1_get(mscx) |
— | uint32_t |
读取当前 VDD1 能力 bitmap |
ll_msc_max_current_1v8_get(mscx) |
— | uint8_t |
读取 VDD1 1.8 V 最大电流原始编码 |
ll_msc_max_current_3v0_get(mscx) |
— | uint8_t |
读取 VDD1 3.0 V 最大电流原始编码 |
ll_msc_max_current_3v3_get(mscx) |
— | uint8_t |
读取 VDD1 3.3 V 最大电流原始编码 |
ll_msc_current_capabilities2_get(mscx) |
— | uint32_t |
读取当前 VDD2 能力 bitmap |
ll_msc_max_current_vdd2_1v8_get(mscx) |
— | uint8_t |
读取 VDD2 1.8 V 最大电流原始编码 |
最大电流字段使用 SDHCI 编码:0 表示其它来源,1 表示 4 mA,2 表示 8 mA,3 表示 13 mA,255 表示 1020 mA。LL 不把编码换算为 mA。
4.14 Force event 和 ADMA API
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_msc_force_auto_command_status(mscx, force_mask) |
uint16_t force_mask |
void |
W1S 强制 AUTO_CMD_STAT_R 事件;写 0 无效果 |
ll_msc_force_error_interrupt_status(mscx, force_mask) |
uint16_t force_mask |
void |
W1S 强制 ERROR_INT_STAT_R 事件;用于错误注入测试 |
ll_msc_adma_error_status_get(mscx) |
— | uint32_t |
读取 ADMA_ERR_STAT_R |
ll_msc_is_adma_length_error(mscx) |
— | bool |
查询 ADMA length mismatch |
ll_msc_adma_error_state_get(mscx) |
— | uint8_t |
返回 0 停止、1 取 descriptor、3 数据搬运;2 未用 |
ll_msc_adma_address_set(mscx, address) |
uint64_t address |
void |
写 ADMA_SA_LOW/HIGH 两个寄存器 |
ll_msc_adma_address_get(mscx) |
— | uint64_t |
组合读取 ADMA_SA_LOW/HIGH |
ll_msc_adma_id_address_set(mscx, address) |
uint64_t address |
void |
写 ADMA_ID_LOW/HIGH 两个寄存器 |
ll_msc_adma_id_address_get(mscx) |
— | uint64_t |
组合读取 ADMA_ID_LOW/HIGH |
Force event 只用于测试和诊断,不应出现在正常产品命令路径。ADMA 错误状态中的地址可能指向出错 descriptor 或下一个 descriptor,具体取决于状态机阶段;恢复时应重新构造 descriptor,而不是盲目复用旧地址。
4.15 Preset、Slot 和版本 API
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_msc_preset_value_get(mscx, preset_index) |
msc_preset_index_t preset_index |
uint16_t |
读取目标 preset 寄存器;越界索引回退到 PRESET_UHS2_R |
ll_msc_preset_driver_strength_get(mscx, preset_index) |
msc_preset_index_t preset_index |
uint8_t |
返回 0 TypeB、1 TypeA、2 TypeC、3 TypeD;1.8 V signaling 下才有意义 |
ll_msc_preset_clock_generator_select_get(mscx, preset_index) |
msc_preset_index_t preset_index |
bool |
true 为 programmable clock generator,false 为 fixed |
ll_msc_preset_frequency_select_get(mscx, preset_index) |
msc_preset_index_t preset_index |
uint16_t |
返回 10-bit frequency select |
ll_msc_slot_interrupt_status_get(mscx) |
— | uint16_t |
读取 SLOT_INTR_STATUS_R |
ll_msc_interrupting_slot_get(mscx) |
— | uint8_t |
返回 slot bitmap,bit0 对应 slot 1,bit7 对应 slot 8 |
ll_msc_host_version_get(mscx) |
— | uint16_t |
读取 Host vendor/spec version bitmap |
ll_msc_host_vendor_version_get(mscx) |
— | uint8_t |
读取 vendor version 字段 |
ll_msc_host_spec_version_get(mscx) |
— | uint8_t |
读取 spec version 字段 |
ll_msc_mshc_version_id_get(mscx) |
— | uint32_t |
读取 MSHC_VER_ID_R |
ll_msc_mshc_version_id_value_get(mscx) |
— | uint32_t |
读取 MSHC version ID 字段 |
ll_msc_mshc_version_type_get(mscx) |
— | uint32_t |
读取 MSHC_VER_TYPE_R |
ll_msc_mshc_version_type_value_get(mscx) |
— | uint32_t |
读取 MSHC version type 字段 |
preset 寄存器是只读的硬件建议值,不等同于当前 CPM 时钟配置。读取 preset 后是否采用,仍需控制器层结合卡模式、时钟能力和板级约束决定。
4.16 MSHC 和 eMMC 控制 API
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_msc_mshc_control_set(mscx, control) |
uint32_t control |
void |
覆盖写完整 MSHC_CTRL_R |
ll_msc_mshc_control_get(mscx) |
— | uint32_t |
读取完整 MSHC_CTRL_R |
ll_msc_software_clock_gating_disable(mscx) |
— | void |
置 SW_CG_DIS,禁止软件时钟门控 |
ll_msc_software_clock_gating_enable(mscx) |
— | void |
清 SW_CG_DIS,允许门控 |
ll_msc_is_software_clock_gating_disabled(mscx) |
— | bool |
查询软件时钟门控是否被禁止 |
ll_msc_command_conflict_check_enable(mscx) |
— | void |
置 CMD_CONFLICT_CHECK |
ll_msc_command_conflict_check_disable(mscx) |
— | void |
清 CMD_CONFLICT_CHECK |
ll_msc_is_command_conflict_check_enabled(mscx) |
— | bool |
查询命令冲突检查 |
ll_msc_emmc_control_set(mscx, control) |
uint32_t control |
void |
覆盖写完整 EMMC_CTRL_R;注意 reset 和 disable 字段的反向语义 |
ll_msc_emmc_control_get(mscx) |
— | uint32_t |
读取完整 EMMC_CTRL_R |
ll_msc_cqe_prefetch_disable(mscx) |
— | void |
置 CQE_PREFETCH_DISABLE,禁止 CQE prefetch |
ll_msc_cqe_prefetch_enable(mscx) |
— | void |
清 CQE_PREFETCH_DISABLE,允许 prefetch |
ll_msc_is_cqe_prefetch_disabled(mscx) |
— | bool |
查询 CQE prefetch 是否被禁止 |
ll_msc_cqe_alternate_algorithm_select(mscx) |
— | void |
置 CQE_ALGO_SEL,选择 alternate 算法 |
ll_msc_cqe_default_algorithm_select(mscx) |
— | void |
清 CQE_ALGO_SEL,选择 default 算法 |
ll_msc_is_cqe_alternate_algorithm_selected(mscx) |
— | bool |
查询 CQE 算法 |
ll_msc_emmc_reset_output_enable(mscx) |
— | void |
置 EMMC_RST_N_OE,由控制器驱动 reset pad |
ll_msc_emmc_reset_output_disable(mscx) |
— | void |
清 EMMC_RST_N_OE,释放 pad 驱动 |
ll_msc_is_emmc_reset_output_enabled(mscx) |
— | bool |
查询 reset pad 驱动使能 |
ll_msc_emmc_reset_release(mscx) |
— | void |
置 EMMC_RST_N,释放低有效复位 |
ll_msc_emmc_reset_assert(mscx) |
— | void |
清 EMMC_RST_N,置位低有效复位 |
ll_msc_is_emmc_reset_released(mscx) |
— | bool |
查询 eMMC reset 是否释放 |
ll_msc_data_crc_check_disable(mscx) |
— | void |
置 DISABLE_DATA_CRC_CHK,关闭数据 CRC 检查 |
ll_msc_data_crc_check_enable(mscx) |
— | void |
清 DISABLE_DATA_CRC_CHK,开启检查 |
ll_msc_is_data_crc_check_disabled(mscx) |
— | bool |
查询数据 CRC 检查是否关闭 |
ll_msc_emmc_card_select(mscx) |
— | void |
置 CARD_IS_EMMC |
ll_msc_non_emmc_card_select(mscx) |
— | void |
清 CARD_IS_EMMC |
ll_msc_is_emmc_card_selected(mscx) |
— | bool |
查询是否 eMMC 模式 |
注意:ll_msc_software_clock_gating_disable() 是置位 SW_CG_DIS,调用后门控被禁止;ll_msc_software_clock_gating_enable() 则是清位,使门控可用。CQE 算法和 prefetch 配置应在 CQE 关闭时修改。eMMC reset 为低有效,assert() 会清零 EMMC_RST_N。
4.17 调谐控制 API
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_msc_auto_tuning_control_set(mscx, control) |
uint32_t control |
void |
覆盖写完整 AT_CTRL_R |
ll_msc_auto_tuning_control_get(mscx) |
— | uint32_t |
读取完整 AT_CTRL_R |
ll_msc_tuning_window_threshold_set(mscx, threshold) |
uint8_t threshold |
void |
写 SWIN_TH_VAL,范围 0~255 |
ll_msc_tuning_window_threshold_get(mscx) |
— | uint8_t |
读采样窗口阈值 |
ll_msc_tuning_post_change_delay_set(mscx, delay) |
uint8_t delay |
void |
写 POST_CHANGE_DLY,范围 0~3,单位 cclk |
ll_msc_tuning_post_change_delay_get(mscx) |
— | uint8_t |
读相位切换后延时 |
ll_msc_tuning_pre_change_delay_set(mscx, delay) |
uint8_t delay |
void |
写 PRE_CHANGE_DLY,范围 0~3,单位 cclk |
ll_msc_tuning_pre_change_delay_get(mscx) |
— | uint8_t |
读相位切换前延时 |
ll_msc_tuning_clock_stop_enable(mscx) |
— | void |
置 TUNE_CLK_STOP_EN |
ll_msc_tuning_clock_stop_disable(mscx) |
— | void |
清 TUNE_CLK_STOP_EN |
ll_msc_is_tuning_clock_stop_enabled(mscx) |
— | bool |
查询调谐相位切换时是否停时钟 |
ll_msc_software_tuning_enable(mscx) |
— | void |
置 SW_TUNE_EN |
ll_msc_software_tuning_disable(mscx) |
— | void |
清 SW_TUNE_EN |
ll_msc_is_software_tuning_enabled(mscx) |
— | bool |
查询软件调谐 |
ll_msc_tuning_error_report_enable(mscx) |
— | void |
置 RPT_TUNE_ERR |
ll_msc_tuning_error_report_disable(mscx) |
— | void |
清 RPT_TUNE_ERR |
ll_msc_is_tuning_error_report_enabled(mscx) |
— | bool |
查询调谐错误上报 |
ll_msc_first_matching_tuning_window_select(mscx) |
— | void |
置 SWIN_TH_EN,选择首个匹配窗口 |
ll_msc_widest_tuning_window_select(mscx) |
— | void |
清 SWIN_TH_EN,选择最宽窗口 |
ll_msc_is_first_matching_tuning_window_selected(mscx) |
— | bool |
查询窗口选择策略 |
ll_msc_auto_tuning_status_get(mscx) |
— | uint32_t |
读取 AT_STAT_R,包含 PH_SEL_CODE |
ll_msc_phase_select_code_set(mscx, phase_code) |
uint8_t phase_code |
void |
写 PH_SEL_CODE,范围 0~7;只在软件调谐时使用 |
ll_msc_phase_select_code_get(mscx) |
— | uint8_t |
读取当前采样相位编码 |
调谐流程通常需要 HAL/控制器层发送调谐命令并检查 ERROR_INT_STAT_R.TUNING_ERR。ll_msc_execute_tuning_start() 和 ll_msc_auto_tuning_control_set() 本身不会触发完整调谐事务。软件调谐时,ll_msc_phase_select_code_set() 修改的是 AT_STAT_R 中的可写控制字段;修改前应确认 SW_TUNE_EN=1,并在控制器空闲时进行。
4.18 低功耗控制 API
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_msc_low_power_control_set(mscx, control) |
uint32_t control |
void |
覆盖写完整 LP_CTRL_R |
ll_msc_low_power_control_get(mscx) |
— | uint32_t |
读取完整 LP_CTRL_R |
ll_msc_low_power_threshold_set(mscx, threshold) |
uint8_t threshold |
void |
写 LP_TH_VAL,范围 0~255 |
ll_msc_low_power_threshold_get(mscx) |
— | uint8_t |
读取 buffered-write 阈值 |
ll_msc_low_power_threshold_enable(mscx) |
— | void |
置 LP_TH_EN |
ll_msc_low_power_threshold_disable(mscx) |
— | void |
清 LP_TH_EN |
ll_msc_is_low_power_threshold_enabled(mscx) |
— | bool |
查询低功耗阈值使能 |
ll_msc_low_power_count_set(mscx, count) |
uint8_t count |
void |
写 LP_CNT,范围 0~127 clocks |
ll_msc_low_power_count_get(mscx) |
— | uint8_t |
读取空闲时钟门控计数 |
LP_TH_VAL 只在 LP_TH_EN=1 且存在 buffered write 时参与 XFER_BLOCK_SIZE - LP_TH_VAL 判断;LP_CNT 只在 CLK_CTRL_R.CARD_LP_EN=1 时影响空闲卡时钟门控。两者都不替代控制器空闲检查。
5. 典型完整示例
5.1 单数据块(CMD17)PIO 读取示例
下面的示例假设 SD/eMMC 卡已经完成协议层初始化,MSC0 的时钟、pinmux、卡供电和总线宽度已经配置,并且当前没有其它命令或数据传输。block_address 的具体单位由协议层和卡类型决定,本例不把它解释为字节地址或 LBA。
#include <stdbool.h>
#include <stdint.h>
#include "ll_msc.h"
#include "im_time.h"
#define APP_MSC_BLOCK_SIZE 512U
#define APP_MSC_BLOCK_WORDS (APP_MSC_BLOCK_SIZE / 4U)
#define APP_MSC_TIMEOUT_MS 200U
static bool app_msc_wait_cmd_ready(msc_regs_t *mscx, uint32_t timeout_ms)
{
uint32_t start_ms = sys_time_get_ms();
while (ll_msc_is_command_inhibited(mscx) ||
ll_msc_is_data_command_inhibited(mscx)) {
if ((uint32_t)(sys_time_get_ms() - start_ms) >= timeout_ms) {
return false;
}
sys_time_delay_ms(1U);
}
return true;
}
static bool app_msc_read_one_block(msc_regs_t *mscx,
uint32_t block_address,
uint32_t words[APP_MSC_BLOCK_WORDS])
{
const uint16_t clear_normal =
MSC_NORMAL_INT_CMD_CMPLT_MASK |
MSC_NORMAL_INT_XFER_CMPLT_MASK |
MSC_NORMAL_INT_BUF_RD_RDY_MASK;
uint32_t start_ms;
uint32_t word_count = 0U;
uint16_t transfer_mode;
uint16_t command;
if (!ll_msc_is_instance(mscx)) {
return false;
}
if (!app_msc_wait_cmd_ready(mscx, APP_MSC_TIMEOUT_MS)) {
return false;
}
ll_msc_interrupt_status_clear(mscx, clear_normal, 0xFFFFU);
ll_msc_argument_set(mscx, block_address);
ll_msc_block_size_set(mscx, (uint16_t)APP_MSC_BLOCK_SIZE);
ll_msc_block_count_set(mscx, 1U);
transfer_mode = ll_msc_transfer_mode_make(
false, /* 允许 response 完成中断 */
true, /* 检查 R1 响应错误位 */
MSC_RESPONSE_CHECK_R1,
false, /* 单块 */
true, /* 卡到主机 */
MSC_AUTO_COMMAND_DISABLED,
false, /* 单块不使用 BLK_CNT_EN */
false); /* PIO,不使用 DMA */
ll_msc_transfer_mode_set(mscx, transfer_mode);
command = ll_msc_command_make(17U,
MSC_COMMAND_TYPE_NORMAL,
true,
true,
true,
false,
MSC_RESPONSE_48_BIT);
ll_msc_command_set(mscx, command);
start_ms = sys_time_get_ms();
for (;;) {
uint16_t normal = ll_msc_normal_interrupt_status_get(mscx);
uint16_t error = ll_msc_error_interrupt_status_get(mscx);
if (error != 0U) {
ll_msc_error_interrupt_status_clear(mscx, error);
ll_msc_normal_interrupt_status_clear(mscx, clear_normal);
return false;
}
if (ll_msc_is_read_buffer_ready(mscx)) {
if (word_count >= APP_MSC_BLOCK_WORDS) {
ll_msc_normal_interrupt_status_clear(mscx, clear_normal);
return false;
}
words[word_count++] = ll_msc_buffer_data_read(mscx);
}
if (((normal & MSC_NORMAL_INT_XFER_CMPLT_MASK) != 0U) &&
!ll_msc_is_read_transfer_active(mscx) &&
!ll_msc_is_read_buffer_ready(mscx)) {
break;
}
if ((uint32_t)(sys_time_get_ms() - start_ms) >= APP_MSC_TIMEOUT_MS) {
/* 超时后的恢复由控制器层决定,可执行 CMD/DAT 软复位。 */
ll_msc_software_reset(mscx,
MSC_SOFTWARE_RESET_CMD |
MSC_SOFTWARE_RESET_DAT);
return false;
}
}
ll_msc_normal_interrupt_status_clear(mscx, clear_normal);
return word_count == APP_MSC_BLOCK_WORDS;
}
说明:
- 示例没有等待
CMD_CMPLT后再开始读;实际控制器层通常同时处理CMD_CMPLT、BUF_RD_RDY和XFER_CMPLT。只要遵守BUF_RD_EN,PIO 数据可以按硬件节奏消费。 - 错误状态可能在传输中到达。这里把任意 error 位都视为失败;产品代码应按具体错误决定重试、CMD/DAT reset 或向上层报告。
- 示例只读取一个 block。多块传输应选择多块模式、正确设置 block count/CMD12/CMD23 策略,并按相同的就绪/完成规则循环。
- 响应值可在
CMD_CMPLT后通过ll_msc_response_get()读取并交给协议层解析;本例没有解析 R1 字段。
5.2 中断和 ADMA2 配置示例
下面的代码展示 ADMA2 descriptor 地址、DMA 选择和中断使能的 LL 调用。descriptor 必须由控制器层构造,并保证物理地址、对齐和 cache 状态正确。
#include <stdbool.h>
#include <stdint.h>
#include "ll_msc.h"
static void app_msc_enable_adma2_irq(msc_regs_t *mscx)
{
const uint16_t normal_events =
MSC_NORMAL_INT_CMD_CMPLT_MASK |
MSC_NORMAL_INT_XFER_CMPLT_MASK |
MSC_NORMAL_INT_DMA_INT_MASK |
MSC_NORMAL_INT_BUF_RD_RDY_MASK |
MSC_NORMAL_INT_BUF_WR_RDY_MASK;
ll_msc_normal_interrupt_status_clear(mscx, normal_events, 0xFFFFU);
ll_msc_normal_interrupt_status_enable_set(mscx, normal_events);
ll_msc_error_interrupt_status_enable_set(mscx, MSC_ERROR_INT_ALL_MASK);
ll_msc_normal_interrupt_signal_enable_set(mscx, normal_events);
ll_msc_error_interrupt_signal_enable_set(mscx, MSC_ERROR_INT_ALL_MASK);
}
static bool app_msc_start_adma2_read(msc_regs_t *mscx,
uint32_t block_address,
uint32_t block_count,
uint64_t descriptor_address)
{
uint16_t transfer_mode;
uint16_t command;
if (!ll_msc_is_instance(mscx) || (block_count == 0U)) {
return false;
}
if (ll_msc_is_command_inhibited(mscx) ||
ll_msc_is_data_command_inhibited(mscx)) {
return false;
}
ll_msc_host_v3_compatible_mode_select(mscx);
ll_msc_32bit_addressing_select(mscx);
ll_msc_adma2_length_16bit_select(mscx);
ll_msc_dma_select_set(mscx, MSC_DMA_V3_ADMA2_32BIT);
ll_msc_adma_address_set(mscx, descriptor_address);
ll_msc_argument_set(mscx, block_address);
ll_msc_block_size_set(mscx, 512U);
ll_msc_block_count_set(mscx, (uint16_t)block_count);
transfer_mode = ll_msc_transfer_mode_make(
false,
true,
MSC_RESPONSE_CHECK_R1,
true,
true,
MSC_AUTO_COMMAND_CMD12,
true,
true);
ll_msc_transfer_mode_set(mscx, transfer_mode);
command = ll_msc_command_make(18U,
MSC_COMMAND_TYPE_NORMAL,
true,
true,
true,
false,
MSC_RESPONSE_48_BIT);
ll_msc_command_set(mscx, command);
return true;
}
static void app_msc_irq_handler(msc_regs_t *mscx)
{
uint16_t normal = ll_msc_normal_interrupt_status_get(mscx);
uint16_t error = ll_msc_error_interrupt_status_get(mscx);
uint16_t normal_ack = normal &
(MSC_NORMAL_INT_CMD_CMPLT_MASK |
MSC_NORMAL_INT_XFER_CMPLT_MASK |
MSC_NORMAL_INT_DMA_INT_MASK |
MSC_NORMAL_INT_BUF_RD_RDY_MASK |
MSC_NORMAL_INT_BUF_WR_RDY_MASK);
if (error != 0U) {
/* 记录错误并交给控制器层决定复位或重试。 */
}
if ((normal & MSC_NORMAL_INT_DMA_INT_MASK) != 0U) {
/* SDMA 模式需要在此更新 SDMA 地址;ADMA2 通常只需记录边界事件。 */
}
if ((normal & MSC_NORMAL_INT_CMD_CMPLT_MASK) != 0U) {
/* 响应已经有效,可读取 RESP01/23/45/67。 */
}
if ((normal & MSC_NORMAL_INT_XFER_CMPLT_MASK) != 0U) {
/* 数据阶段结束;若使用 DMA,需要按平台规则 invalidate cache。 */
}
ll_msc_interrupt_status_clear(mscx, normal_ack, error);
if ((normal & MSC_NORMAL_INT_CARD_INTERRUPT_MASK) != 0U) {
/* CARD_INTERRUPT 是只读电平,调用方必须关闭 signal enable 并处理 SDIO 源。 */
}
}
说明:
- 示例使用 Host v3 兼容模式和
MSC_DMA_V3_ADMA2_32BIT。若硬件和 descriptor 支持 64-bit ADMA2,应同时检查能力位、选择 64-bit addressing 并更新HOST_CTRL1_R.DMA_SEL。 descriptor_address必须是控制器可访问的物理地址。LL 不会对 descriptor 或数据缓冲区执行 cache 操作。app_msc_irq_handler()只是 LL 状态处理骨架,不包含平台 IRQ 注册、中断清除顺序的并发保护或上层请求状态机。- 若 ADMA3 被选用,应使用
ll_msc_adma_id_address_set()和对应 Host v4/CQE 配置,不应复用 ADMA2 descriptor 地址接口。
5.3 软件复位、能力查询和重调谐检查
#include <stdbool.h>
#include <stdint.h>
#include "ll_msc.h"
#include "im_time.h"
#define APP_MSC_RESET_TIMEOUT_MS 50U
static bool app_msc_reset_all(msc_regs_t *mscx)
{
uint32_t start_ms;
if (!ll_msc_is_instance(mscx)) {
return false;
}
ll_msc_sd_clock_disable(mscx);
ll_msc_software_reset(mscx, MSC_SOFTWARE_RESET_ALL);
start_ms = sys_time_get_ms();
while (ll_msc_is_software_reset_pending(mscx, MSC_SOFTWARE_RESET_ALL)) {
if ((uint32_t)(sys_time_get_ms() - start_ms) >=
APP_MSC_RESET_TIMEOUT_MS) {
return false;
}
sys_time_delay_ms(1U);
}
/* 复位后必须按板级和协议要求重新配置时钟、电压、总线和中断。 */
return true;
}
static void app_msc_report_capabilities(msc_regs_t *mscx)
{
uint32_t caps1 = ll_msc_capabilities1_get(mscx);
uint32_t caps2 = ll_msc_capabilities2_get(mscx);
(void)caps1;
(void)caps2;
/* 可用 is_* 接口按功能逐项查询,避免手写能力位。 */
if (ll_msc_is_sdr104_supported(mscx)) {
/* SDR104 需要调谐,具体命令和窗口策略由协议层决定。 */
}
if (ll_msc_is_adma3_supported(mscx)) {
/* 启用 ADMA3 前还要确认 Host v4、descriptor 和 CQE 组合。 */
}
}
static bool app_msc_check_retune(msc_regs_t *mscx)
{
if (!ll_msc_is_instance(mscx)) {
return false;
}
if (!ll_msc_is_retune_requested(mscx)) {
return true;
}
/* 先配置调谐数据阶段,再置 EXEC_TUNING,并由协议层下发 tuning 命令。 */
ll_msc_execute_tuning_start(mscx);
while (ll_msc_is_execute_tuning_active(mscx)) {
/* 增加超时,并检查 ERROR_INT_STAT_R.TUNING_ERR。 */
}
if (ll_msc_is_tuned_sample_clock_selected(mscx)) {
/* 调谐成功后继续使用 tuned sample clock。 */
return true;
}
return false;
}
说明:
- 软复位会清除控制器内部配置;示例在复位后没有自动重建初始化,调用方必须完成这一步。
ll_msc_is_retune_requested()只反映PSTATE_REG.RE_TUNE_REQ,不表示调谐已经开始或必须立即执行。调谐命令、窗口策略、重试次数和超时都由控制器层决定。- 调谐失败时,应先检查
ERROR_INT_STAT_R.TUNING_ERR、AUTO_CMD_STAT_R和当前 UHS/采样时钟设置,再决定重新调谐或降速。
6. 使用约束和检查清单
6.1 关键约束
- LL 层只支持
MSC0和MSC1,不验证板级是否实际引出、供电或完成 pinmux。 - 所有接口直接访问寄存器,没有句柄、参数检查、错误码、超时、重试、锁或请求队列。
CMD_R写入即触发命令;必须先检查PSTATE_REGinhibit 条件,并保证前一条命令/数据传输已经结束。XFER_MODE_R会保留上一次配置。每次命令前必须显式设置方向、单/多块、块数、自动命令和 DMA 位,不能只修改本次关心的字段。BLOCKSIZE_R.XFER_BLOCK_SIZE=0表示无数据阶段;带数据命令必须设置合法非零块大小,并搬运完整数据量。- G32S10 板级通常只有 DAT0..DAT3。LL 的 8-bit helper 只是寄存器编码,不代表系统支持 8-bit 总线。
- 修改总线宽度、UHS 模式、采样时钟、1.8 V signaling、DMA 选择或 Host v4 前,必须停止传输并满足卡协议要求。
- Host v4 会改变
SDMASA_R(SDMA 地址或 32-bit block count)和 DMA select 编码。混用 v3/v4 配置会导致地址或计数错误。 - SDMA 到达 boundary 后需要软件更新地址;ADMA 需要合法 descriptor、物理地址、对齐和 cache 维护。
- 中断必须同时配置 status enable 和 signal enable。
CARD_INTERRUPT是只读电平,不能通过 W1C 清除。 - 软件复位是异步的,且不会自动恢复时钟、供电、总线、DMA 或中断配置。
- 调谐接口不发送命令;
EXEC_TUNING清零只表示执行位结束,仍需检查调谐错误和采样时钟状态。 - LL 层不提供 CPM 时钟、GPIO、稳压器、card detect 消抖、SD/eMMC 协议或文件系统功能。
- 同一控制器的读改写、命令下发和 IRQ 状态清除必须由调用方串行化,避免丢写和竞态。
6.2 首次集成检查
- 已确认使用
MSC0还是MSC1,且msc_regs_t *只指向受支持实例。 - 已使能 MSC 总线时钟和门控时钟,配置父源、频率、RX/TX phase,并完成 CLK/CMD/DAT/卡检测 pinmux。
- 卡供电、复位和可选 1.8 V 稳压器已按板级顺序控制,控制器
PWR_CTRL_R请求与外部硬件一致。 - 控制器空闲,
CMD_INH/CMD_INH_DAT/DAT_LINE_ACT状态符合预期。 - 已配置 timeout count,且没有写入保留值
0xF。 - 内部时钟已请求且
INTERNAL_CLK_STABLE=1后才启用SD_CLK_EN。 - 总线宽度与板级 DAT 线一致,没有误用 8-bit helper。
-
HOST_CTRL2_R的 signaling、UHS mode、sampling clock、Host v4 和寻址模式与卡协议一致。 - 每条命令前都设置了完整的
XFER_MODE_R,没有遗留上一次的 DMA、多块或自动命令位。 - PIO 读写在
BUF_RD_EN/BUF_WR_EN就绪后访问,且搬运字数与block_size × block_count一致。 - PIO 操作有软件超时;超时或 CRC 错误时能执行 CMD/DAT reset 或上报控制器层。
- SDMA/ADMA 地址为有效物理地址,descriptor 已满足对齐和 cache 要求。
- 如果使用 Host v4,已确认
SDMASA_R当前表示 block count 还是 SDMA 地址,并未混用两套语义。 - normal/error status enable 与 signal enable 均已配置,平台 IRQ 已注册并正确分发到对应控制器。
- 中断处理只清除已处理的 W1C 位,
CARD_INTERRUPT通过 signal enable 停止上报。 - 软件复位流程带超时,复位完成后重新初始化控制器并经
ll_msc_is_instance()重新确认实例。 - 高频模式已执行调谐或在固定采样时钟下验证数据完整性,且检查了
TUNING_ERR。 - 低功耗门控、卡时钟关闭和软件复位只发生在控制器空闲时。
- 多任务/IRQ 共享同一控制器时,所有配置、命令、DMA 和状态清除路径都有明确互斥策略。