SD 标准控制器驱动
概述
SDHCI 标准接口驱动在内核中已经有相应比较完善的实现模板,具体实现代码位于“libsylixos/SylixOS/system/device/sdcard/host/”下,其中隐藏了许多操作如 SDIO 中断、Adapter 注册等,驱动仅需提供部分数据即可方便的实现驱动。
SDHCI 标准控制器创建及注册
SDHCI 标准控制器的注册只需要调用 API_SdhciHostCreate 函数即可,其内部封装了如 Adapter 创建、SDM 层 HOST 注册等步骤,减轻驱动开发者负担,其函数原型如下:
#include <SylixOS.h>
PVOID API_SdhciHostCreate(CPCHAR pcAdapterName,
PLW_SDHCI_HOST_ATTR psdhcihostattr);
函数 API_SdhciHostCreate 原型分析:
- 此函数成功返回 SDHCI 主控器指针,失败返回 LW_NULL 。
- 参数 pcAdapterName 为创建的 SD 总线适配器名称,即输入 shell 命令 buss 看到的名称,注意此名称不能重复。
- 参数 psdhcihostattr 为主控器所具有的属性。
为了注册 SDHCI 标准控制器需要提供填充好数据的 PLW_SDHCI_HOST_ATTR 结构体,该结构体中包含了此控制器适应 SDHCI 标准的详细特性,其详细描述如下:
typedef struct lw_sdhci_host_attr {
SDHCI_DRV_FUNCS *SDHCIHOST_pdrvfuncs; /* 标准主控驱动函数结构指针 */
INT SDHCIHOST_iRegAccessType; /* 寄存器访问类型 */
ULONG SDHCIHOST_ulBasePoint; /* 槽基地址指针 */
ULONG SDHCIHOST_ulIntVector; /* 控制器在 CPU 中的中断号 */
UINT32 SDHCIHOST_uiMaxClock; /* 如果控制器没有内部时钟,用户 */
/* 需要提供时钟源 */
SDHCI_QUIRK_OP *SDHCIHOST_pquirkop;
UINT32 SDHCIHOST_uiQuirkFlag;
VOID *SDHCIHOST_pvUsrSpec; /* 用户驱动特殊数据 */
}LW_SDHCI_HOST_ATTR, *PLW_SDHCI_HOST_ATTR;
- SDHCIHOST_pdrvfuncs:主控驱动函数结构指针,大部分情况为 LW_NULL,SDM 会自动根据寄存器访问类型成员取值自行实现,小部分非标准驱动需要提供单独的寄存器读写函数。
- SDHCIHOST_iRegAccessType:寄存器访问类型,其中 x86 平台使用参数为 SDHCI_REGACCESS_TYPE_IO,ARM 等其它平台使用参数为 SDHCI_REGACCESS_TYPE_MEM ,若上一个成员不为空,则此成员值无效。
- SDHCIHOST_ulBasePoint:寄存器(槽)基地址指针; 。
- SDHCIHOST_ulIntVector:此控制器在 CPU 中的中断号。
- SDHCIHOST_uiMaxClock:控制器输入时钟频率。
- SDHCIHOST_pquirkop:若有不符合 SD 标准主控行为的怪异(quirk)行为则需要驱动实现相关函数并填充到此结构体。
- SDHCIHOST_uiQuirkFlag:主要针对不同控制特性的开关详见下表,后期 Quirk 特性可能会随着支持平台的增加而增加。
- SDHCIHOST_pvUsrSpec:用户驱动特殊数据。
QuirkFlag 选项 | 说明 |
---|---|
SDHCI_QUIRK_FLG_DONOT_RESET_ON_EVERY_TRANSACTION | 每一次传输前不需要复位控制器 |
SDHCI_QUIRK_FLG_REENABLE_INTS_ON_EVERY_TRANSACTION | 传输后禁止, 传输前使能中断 |
SDHCI_QUIRK_FLG_DO_RESET_ON_TRANSACTION_ERROR | 传输错误时复位控制器 |
SDHCI_QUIRK_FLG_DONOT_CHECK_BUSY_BEFORE_CMD_SEND | 发送命令前不执行忙检查 |
SDHCI_QUIRK_FLG_DONOT_USE_ACMD12 | 不使用 Auto CMD12 |
SDHCI_QUIRK_FLG_DONOT_SET_POWER | 不操作控制器电源的开/关 |
SDHCI_QUIRK_FLG_DO_RESET_AFTER_SET_POWER_ON | 当打开电源后执行控制器复位 |
SDHCI_QUIRK_FLG_DONOT_SET_VOLTAGE | 不操作控制器的电压 |
SDHCI_QUIRK_FLG_CANNOT_SDIO_INT | 控制器不能发出 SDIO 中断 |
SDHCI_QUIRK_FLG_RECHECK_INTS_AFTER_ISR | 中断服务后再次处理中断状态 |
SDHCI_QUIRK_FLG_CAN_DATA_8BIT | 支持 8 位数据传输 |
SDHCI_QUIRK_FLG_CAN_DATA_4BIT_DDR | 支持 4 位 ddr 数据传输 |
SDHCI_QUIRK_FLG_CAN_DATA_8BIT_DDR | 支持 8 位 ddr 数据传输 |
SDHCI_QUIRK_FLG_MMC_FORCE_1BIT | MMC 卡强制使用 1 位总线 |
主控驱动函数 SDHCI_DRV_FUNCS 为控制器寄存器访问驱动函数,大部分情况下驱动程序不需提供,内部根据 SDHCIHOST_iRegAccessType 使用相应的默认驱动,但为了最大的适应性, 驱动程序也可使用自己的寄存器访问驱动,其详细描述如下:
struct _sdhci_drv_funcs {
UINT32 (*sdhciReadL) (PLW_SDHCI_HOST_ATTR psdhcihostattr, ULONG ulReg);
UINT16 (*sdhciReadW) (PLW_SDHCI_HOST_ATTR psdhcihostattr, ULONG ulReg);
UINT8 (*sdhciReadB) (PLW_SDHCI_HOST_ATTR psdhcihostattr, ULONG ulReg);
VOID (*sdhciWriteL) (PLW_SDHCI_HOST_ATTR psdhcihostattr, ULONG ulReg, UINT32 uiLwor);
VOID (*sdhciWriteW) (PLW_SDHCI_HOST_ATTR psdhcihostattr, ULONG ulReg, UINT16 usWord);
VOID (*sdhciWriteB) (PLW_SDHCI_HOST_ATTR psdhcihostattr,ULONG ulReg, UINT8 ucByte);
};
typedef struct _sdhci_drv_funcs SDHCI_DRV_FUNCS;
- sdhciReadL:读取 8 位长度的数据。
- sdhciReadW:读取 16 位长度的数据。
- sdhciReadB:读取 32 位长度的数据。
- sdhciWriteL:写入 8 位长度的数据。
- sdhciWriteW:写入 16 位长度的数据。
- sdhciWriteB:写入 32 位长度的数据。
怪异(quirk)主控行为结构体 SDHCI_QUIRK_OP ,该结构体的目的是将该控制器中不完全符合 SDHCI 标准的部分操作提取出来单独实现以供 SD Stack 使用,以便达到最大适用性,该结构体详细描述如下:
注:由于 PLW_SDHCI_HOST_ATTR 结构体在注册到SDM层中时发生了拷贝,因此如果 SDHCI_QUIRK_OP 结构体中的实现依赖于更多内部数据,则必须将内部数据保存到 LW_SDHCI_HOST_ATTR 结构体中的 SDHCIHOST_pvUsrSpec 成员里。
struct _sdhci_quirk_op {
INT (*SDHCIQOP_pfuncClockSet) (PLW_SDHCI_HOST_ATTR psdhcihostattr, UINT32 uiClock);
INT (*SDHCIQOP_pfuncClockStop) (PLW_SDHCI_HOST_ATTR psdhcihostattr);
INT (*SDHCIQOP_pfuncBusWidthSet) (PLW_SDHCI_HOST_ATTR psdhcihostattr, UINT32 uiBusWidth);
INT (*SDHCIQOP_pfuncResponseGet) (PLW_SDHCI_HOST_ATTR psdhcihostattr, UINT32 uiRespFlag,
UINT32 *puiRespOut);
VOID (*SDHCIQOP_pfuncIsrEnterHook) (PLW_SDHCI_HOST_ATTR psdhcihostattr);
VOID (*SDHCIQOP_pfuncIsrExitHook) (PLW_SDHCI_HOST_ATTR psdhcihostattr);
BOOL (*SDHCIQOP_pfuncIsCardWp) (PLW_SDHCI_HOST_ATTR psdhcihostattr);
};
typedef struct _sdhci_quirk_op SDHCI_QUIRK_OP;
- SDHCIQOP_pfuncClockSet:时钟设置,若不提供则使用 SDHCI 标准的寄存器进行控制。
- SDHCIQOP_pfuncClockStop:时钟停止。
- SDHCIQOP_pfuncBusWidthSet:主控总线位宽设置。
- SDHCIQOP_pfuncResponseGet:命令正确完成后获取应答及其属性。
- SDHCIQOP_pfuncIsrEnterHook:SDHCI 中断服务程序入口 Hook。
- SDHCIQOP_pfuncIsrExitHook:SDHCI 中断服务程序出口 Hook。
- SDHCIQOP_pfuncIsCardWp:判断对应卡是否写保护,若不提供实现则默认无写保护。
热插拔检测
热插拔检测是通过 SDM 层进行通知的,所以需要获取 SDM 层 HOST 对象用于热插拔事件通知,比非标准接口更方便的是 SDHCI 已经封装好专门的函数来获取 SDM 层 HOST 对象,首先我们要获取用于通知 SDM 层的 SDM 层 Host 对象指针,使用 Host 层 SDHCI 标准驱动封装好的标准函数 API_SdhciSdmHostGet 即可返回该指针,以供热插拔检测使用,函数原型如下:
#include <SylixOS.h>
PVOID API_SdhciSdmHostGet(PVOID pvHost);
函数 API_SdhciSdmHostGet 原型分析:
- 此函数成功返回 SDHCI 控制器对应的 SDM 控制器对象指针,失败返回 LW_NULL 。
- 参数 pvHost 是 SDHCI 主控器指针,即函数 API_SdhciHostCreate 的返回值。
热插拔检测与 SD 非标准接口类似,都需要使用 API_SdmEventNotify 来通知 SDM 层,不同的是在设备移除时需要使用函数 API_SdhciDeviceCheckNotify 来通知 SDHCI 标准主控器设备当前状态,API_SdmEventNotify 函数分析见章节 20.4.420.4.4,API_SdhciDeviceCheckNotify 函数原型如下:
#include <SylixOS.h>
VOID API_SdhciDeviceCheckNotify(PVOID pvHost, INT iDevSta);
函数 API_SdhciDeviceCheckNotify 原型分析:
- pvHost 参数:SDHCI 主控器结构体指针,即 API_SdhciHostCreate 函数的返回值。
- iDevSta 参数:设备状态,取值见下表。
设备状态 | 含义 |
---|---|
SD_DEVSTA_UNEXIST | 设备已经不存在,即将删除 |
SD_DEVSTA_EXIST | 设备存在 |
由于在检测到 SD 设备插入后初始化过程中已默认设置状态为设备存在,故仅需要在检测到设备移除时调用此函数通知上层设备不存在即可。
SD 存储卡做根文件系统
SD 存储卡做根文件系统的情况下需要迅速的创建设备,因此不能等待优先级较低的热插拔线程执行,SylixOS 建议的做法是在 SD 控制器初始化末尾直接通知 SDM 层创建设备。使用到的函数依然是 API_SdmEventNotify,参数为 SDM_EVENT_BOOT_DEV_INSERT 。
SDHCI 标准驱动完整示例
本节给出 SDHCI 标准接口驱动的实现。
所用宏定义及全局变量定义如下:
#define __SYLIXOS_KERNEL
#include <SylixOS.h>
#include <stdio.h>
#include <string.h>
#define __SDHCI_NAME "/bus/sd/0"
#define __SDHCI_CDNAME "sd_cd0"
#define __SDHCI_WPNAME "sd_wp0"
#define __SDHCI_MAXCHANNELNUM 4
#define __SDHCI_VECTOR_CH0 54 /* 中断号 */
#define __SDHCI_VECTOR_CH1 55
#define __SDHCI_VECTOR_CH2 56
#define __SDHCI_VECTOR_CH3 57
#define __SDHCI_PHYADDR_CH0 0x02190000 /* 寄存器物理地址 */
#define __SDHCI_PHYADDR_CH1 0x02194000
#define __SDHCI_PHYADDR_CH2 0x02198000
#define __SDHCI_PHYADDR_CH3 0x0219C000
#define __SDHCI_QUIRK_FLG_CH0 (SDHCI_QUIRK_FLG_CANNOT_SDIO_INT)
/* 每个板子怪异属性不同 */
#define __SDHCI_QUIRK_FLG_CH1 (SDHCI_QUIRK_FLG_CANNOT_SDIO_INT)
#define __SDHCI_QUIRK_FLG_CH2 (SDHCI_QUIRK_FLG_MMC_FORCE_1BIT)
#define __SDHCI_QUIRK_FLG_CH3 (SDHCI_QUIRK_FLG_MMC_FORCE_1BIT)
#define __EMMC_RESERVE_SECTOR 0
#ifndef GPIO_NONE
#define GPIO_NONE LW_CFG_MAX_GPIOS
#endif
#define SD_ERR(fmt, arg...) printk("[SD]"fmt, ##arg);
static __SD_CHANNEL _G_sdChannel[__SDHCI_MAXCHANNELNUM];
控制器内部结构定义如下:
typedef struct {
CHAR SDHCI_cName[10]; /* 名字很重要,不能重复 */
LW_SDHCI_HOST_ATTR SDHCI_attr; /* SDHCI 属性,虽然会发生拷贝 */
/* 但是其中保存了很多有用数据 */
/* 部分控制器可能会使用 */
SDHCI_QUIRK_OP SDHCI_quirkOp; /* 怪异行为操作结构体,也可以 */
/* 使用全局变量多次复用 */
PVOID SDHCI_pvSdhciHost; /* SDHCI 标准主控器结构体指 */
PVOID SDHCI_pvSdmHost; /* 在热插拔中使用以通知上层 */
UINT32 SDHCI_uiSdiCd; /* SD 卡使用的 CD 管脚 */
UINT32 SDHCI_uiSdiWp; /* SD 卡使用的 WP 管脚 */
UINT32 SDHCI_iCardSta; /* 保存 SD 设备当前插拔状态 */
} __SD_CHANNEL, *__PSD_CHANNEL;
查看设备状态函数如下,该函数通过 SD 卡的检测管脚,检测 SD 卡是否存在。函数返回 1 表示 SD 卡插入插槽,返回 0 表示 SD 卡从插槽拔出。
static INT __sdStatGet (__PSD_CHANNEL pChannel)
{
UINT32 uiValue = 0;
if (pChannel->SDHCI_uiSdiCd != GPIO_NONE && pChannel->SDHCI_uiSdiCd != 0) {
uiValue = API_GpioGetValue(pChannel->SDHCI_uiSdiCd);
} else {
uiValue = 0;
}
return (uiValue == 0);
}
查看设备写状态的函数如下,函数返回 0 表示未进行写保护,返回 1 表示已经写保护。
static INT __sdCardWpGet (__PSD_CHANNEL pChannel)
{
UINT32 uiStatus = 0;
if (pChannel->SDHCI_uiSdiWp != GPIO_NONE && pChannel->SDHCI_uiSdiWp != 0) {
uiStatus = API_GpioGetValue(pChannel->SDHCI_uiSdiWp);
} else {
uiStatus = 0;
}
return (uiStatus);
}
热插拔处理函数如下:
static VOID __sdCdHandle(PVOID pvArg)
{
INT iStaCurr;
__PSD_CHANNEL pChannel = (__PSD_CHANNEL)pvArg;
/*
* 自行实现判断卡状态
*/
iStaCurr = __sdStatGet(pChannel);
if (iStaCurr ^ pChannel->SDHCI_iCardSta) { /* 状态变化 */
if (iStaCurr) { /* 插入状态 */
API_SdmEventNotify(pChannel->SDHCI_pvSdmHost, SDM_EVENT_DEV_INSERT);
} else { /* 移除状态 */
API_SdhciDeviceCheckNotify(pChannel->SDHCI_pvSdhciHost, SD_DEVSTA_UNEXIST);
API_SdmEventNotify(pChannel->SDHCI_pvSdmHost, SDM_EVENT_DEV_REMOVE);
}
pChannel->SDHCI_iCardSta = iStaCurr;
}
}
设置时钟频率的函数实现如下:
static INT __sdQuirkClockSet (PLW_SDHCI_HOST_ATTR psdhcihostattr,
UINT32 uiClock)
{
__PSD_CHANNEL pChannel;
pChannel = (__PSD_CHANNEL)psdhcihostattr->SDHCIHOST_pvUsrSpec;
switch (uiClock) {
case SDARG_SETCLK_LOW:
break;
case SDARG_SETCLK_NORMAL:
break;
case SDARG_SETCLK_MAX:
break;
default: /* 根据需要添加 */
return (PX_ERROR);
}
return (ERROR_NONE);
}
判断对应卡是否写保护的函数实现如下,函数返回 LW_TRUE 表示改卡已进行写保护,返回 LW_FALSE 表示该卡未进行写保护。
static BOOL __sdQuirkIsCardWp (PLW_SDHCI_HOST_ATTR psdhcihostattr)
{
__PSD_CHANNEL pChannel;
pChannel = (__PSD_CHANNEL)psdhcihostattr->SDHCIHOST_pvUsrSpec;
return (__sdCardWpGet(pChannel) ? LW_TRUE : LW_FALSE);
}
初始化 SD 控制器软件资源的函数实现如下:
static INT __sdDataInit (INT iChannel, UINT32 uiSdCdPin, UINT32 uiSdWpPin)
{
__PSD_CHANNEL pChannel;
SDHCI_QUIRK_OP *pQuirkOp;
LW_SDHCI_HOST_ATTR *pSdhciAttr;
addr_t addrPhy = 0;
addr_t addrVir = 0;
UINT32 uiVector = 0;
UINT32 uiQuirkFlag = 0;
INT iRet;
CHAR cSdCdName[10] = __SDHCI_CDNAME;
CHAR cSdWpName[10] = __SDHCI_WPNAME;
pChannel = &_G_sdChannel[iChannel];
pQuirkOp = &pChannel->SDHCI_quirkOp;
pSdhciAttr = &pChannel->SDHCI_attr;
switch (iChannel) {
case 0:
addrPhy = __SDHCI_PHYADDR_CH0; /* 初始化内存基地址 */
uiVector = __SDHCI_VECTOR_CH0;
uiQuirkFlag = __SDHCI_QUIRK_FLG_CH0;
break;
case 1:
addrPhy = __SDHCI_PHYADDR_CH1;
uiVector = __SDHCI_VECTOR_CH1;
uiQuirkFlag = __SDHCI_QUIRK_FLG_CH1;
break;
case 2:
addrPhy = __SDHCI_PHYADDR_CH2;
uiVector = __SDHCI_VECTOR_CH2;
uiQuirkFlag = __SDHCI_QUIRK_FLG_CH2;
break;
case 3:
addrPhy = __SDHCI_PHYADDR_CH3;
uiVector = __SDHCI_VECTOR_CH3;
uiQuirkFlag = __SDHCI_QUIRK_FLG_CH3;
break;
}
addrVir = (addr_t)API_VmmIoRemapNocache((PVOID)addrPhy, LW_CFG_VMM_PAGE_SIZE);
/* 映射寄存器地址到虚拟内存 */
if (!addrVir) {
SD_ERR("%s err:No more space!\r\n", __func__);
goto __erra1;
}
cSdCdName[sizeof(__SDHCI_CDNAME) - 2] += iChannel;
cSdWpName[sizeof(__SDHCI_WPNAME) - 2] += iChannel;
if (uiSdCdPin != GPIO_NONE && uiSdCdPin != 0) { /* 申请热插拔检测管脚 */
iRet = API_GpioRequestOne(uiSdCdPin, LW_GPIOF_IN, cSdCdName);
if (iRet != ERROR_NONE) {
SD_ERR("%s err:failed to request gpio %d!\r\n", __func__, uiSdCdPin);
goto __erra2;
}
}
if (uiSdWpPin != GPIO_NONE && uiSdWpPin != 0) { /* 申请写保护检测管脚 */
iRet = API_GpioRequestOne(uiSdWpPin, LW_GPIOF_IN, cSdWpName);
if (iRet != ERROR_NONE) {
SD_ERR("%s err:failed to request gpio %d!\r\n", __func__, uiSdWpPin);
goto __erra3;
}
}
pChannel->SDHCI_uiSdiCd = uiSdCdPin; /* 保存两个管脚号 */
pChannel->SDHCI_uiSdiWp = uiSdWpPin;
pChannel->SDHCI_iCardSta = 0;
snprintf(pChannel->SDHCI_cName, sizeof(__SDHCI_NAME), __SDHCI_NAME);
pChannel->SDHCI_cName[sizeof(__SDHCI_NAME) - 2] += iChannel;
/* 对名称敏感,不能同名 */
pSdhciAttr->SDHCIHOST_pdrvfuncs = LW_NULL;
pSdhciAttr->SDHCIHOST_iRegAccessType = SDHCI_REGACCESS_TYPE_MEM;
pSdhciAttr->SDHCIHOST_ulBasePoint = (ULONG)addrVir;
pSdhciAttr->SDHCIHOST_ulIntVector = uiVector;
pSdhciAttr->SDHCIHOST_uiMaxClock = 200000000; /* 每个板子获取时钟频率方法不同 */
pSdhciAttr->SDHCIHOST_uiQuirkFlag = uiQuirkFlag;
pSdhciAttr->SDHCIHOST_pquirkop = pQuirkOp;
pSdhciAttr->SDHCIHOST_pvUsrSpec = (PVOID)pChannel;
/*
* Quirk 操作并不是每个控制器都需要,也不是全部都需要提供实现,此处仅仅是举例
*/
pQuirkOp->SDHCIQOP_pfuncClockSet = __sdQuirkClockSet;
pQuirkOp->SDHCIQOP_pfuncIsCardWp = __sdQuirkIsCardWp;
return (ERROR_NONE);
__erra3:
API_GpioFree(uiSdCdPin);
__erra2:
API_VmmIoUnmap((PVOID)addrVir);
__erra1:
return (PX_ERROR);
}
回收 SD 控制器软件资源的函数实现如下:
static VOID __sdDataDeinit (INT iChannel)
{
__PSD_CHANNEL pChannel;
pChannel = &_G_sdChannel[iChannel];
API_GpioFree(pChannel->SDHCI_uiSdiWp);
API_GpioFree(pChannel->SDHCI_uiSdiCd);
API_SdhciHostDelete(pChannel->SDHCI_pvSdhciHost);
API_VmmIoUnmap((PVOID)pChannel->SDHCI_attr.SDHCIHOST_ulBasePoint);
memset((PVOID)pChannel, 0, sizeof(__SD_CHANNEL));
}
初始化 SD 控制器额外寄存器资源的函数实现如下:
static INT __sdHwInitEx (INT iChannel)
{
__PSD_CHANNEL pChannel;
addr_t atRegAddr;
pChannel = &_G_sdChannel[iChannel];
atRegAddr = (addr_t)pChannel->SDHCI_attr.SDHCIHOST_ulBasePoint;
/*
* 根据控制器不同实现不同
*/
return (ERROR_NONE);
}
初始化 SD 总线的函数实现如下:
INT sdDrvInstall (INT iChannel,
UINT32 uiSdCdPin,
UINT32 uiSdWpPin,
BOOL bIsBootDev)
{
__PSD_CHANNEL pChannel;
INT iRet;
if (iChannel >= __SDHCI_MAXCHANNELNUM) {
SD_ERR("%s err:This host only have %d channels!\r\n", __func__, __SDHCI_MAXCHANNELNUM);
return (PX_ERROR);
}
/*
* 初始化控制器软件资源
*/
iRet = __sdDataInit(iChannel, uiSdCdPin, uiSdWpPin);
if (iRet != ERROR_NONE) {
return (PX_ERROR);
}
/*
* 初始化 SDHCI 标准之外的其他额外寄存器(部分 CPU 需要)
*/
iRet = __sdHwInitEx(iChannel);
if (iRet != ERROR_NONE) {
goto __errb1;
}
pChannel = &_G_sdChannel[iChannel];
/*
* 调用标准 SDHCI 函数进行初始化
*/
pChannel->SDHCI_pvSdhciHost = API_SdhciHostCreate(pChannel->SDHCI_cName, &pChannel->SDHCI_attr);
if (!pChannel->SDHCI_pvSdhciHost) {
SD_ERR("%s err:Sdhci host create fail!\r\n", __func__);
goto __errb1;
}
/*
* 保存 SDM HOST 以供热插拔使用
*/
pChannel->SDHCI_pvSdmHost = API_SdhciSdmHostGet(pChannel->SDHCI_pvSdhciHost);
if (!pChannel->SDHCI_pvSdmHost) {
SD_ERR("%s err:Sdhci's sdm host get fail!\r\n", __func__);
goto __errb1;
}
/*
* SDM层扩展参数设置,设置触发块大小,提高读写速率,默认64
*/
API_SdmHostExtOptSet(pChannel->SDHCI_pvSdmHost, SDHOST_EXTOPT_MAXBURST_SECTOR_SET, 128);
/*
* BOOT 设备特殊处理: 在当前线程(而非热插拔线程)直接创建设备, 提高启动速度
*/
if (bIsBootDev) {
iRet = API_SdmEventNotify(pChannel->SDHCI_pvSdmHost, SDM_EVENT_BOOT_DEV_INSERT);
pChannel->SDHCI_iCardSta = 1;
if (iRet) {
SD_ERR("%s err:fail to create boot device!\r\n", __func__);
pChannel->SDHCI_iCardSta = 0;
}
/*
* CPU直接从SD卡启动情况可以使用此额外选项避开u-boot段等,其他情况建议使用分区
*/
API_SdmHostExtOptSet(pChannel->SDHCI_pvSdmHost,
SDHOST_EXTOPT_RESERVE_SECTOR_SET,
__EMMC_RESERVE_SECTOR);
}
/*
* 加入热插拔检测线程
*/
hotplugPollAdd((VOIDFUNCPTR)__sdCdHandle, (PVOID)pChannel);
return (ERROR_NONE);
__errb1:
__sdDataDeinit(iChannel);
return (PX_ERROR);
}
SD 系统库初始化函数如下:
VOID sdiLibInit (VOID)
{
/*
* SDM系统层初始化,包含SD存储卡库的初始化
*/
API_SdmLibInit();
/*
* SDIO库初始化
*/
API_SdmSdioLibInit();
API_SdMemDrvInstall(); /* 安装 SD 存储卡驱动 */
API_SdioBaseDrvInstall(); /* 安装 SDIO 接口驱动 */
}