SD 标准控制器驱动

更新时间:
2024-03-14
下载文档

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_1BITMMC 卡强制使用 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 接口驱动          */
}
文档内容是否对您有所帮助?
有帮助
没帮助