linux MMC framework(5) - mmc card driver

  • 了解linux mmc card driver.

1.概述

  mmc core/block.c中module_init(mmc_blk_init)调用mmc_register_driver函数创建mmcblk driver,并将之挂载到mmc_bus_type总线的driver list链表上。

Note:
  mmc core提供了mmc_test.c作为mmc driver的测试文件。mmc_test.c中,module_init(mmc_test_init)函数中,调用mmc_register_driver函数创建了mmc_test driver,并且将之挂载在mmc_bus_type总线的driver list链表上。

  mmc模块如果需要使用mmc_test功能,需要把CONFIG_MMC_BLOKC宏关闭,然后把CONFIG_MMC_BLOCK=y。否则,card将会匹配blockdreiver,不会再次匹配mmc_test driver,具体见下图。
在这里插入图片描述

2.相关结构体

2.1.struct mmc_card

struct mmc_card {
    struct mmc_host     *host;      /* the host this device belongs to */    // 该mmc_card所属host
    struct device       dev;        /* the device */    // 对应的device
    unsigned int        rca;        /* relative card address of device */    // 该card的RCA地址
    unsigned int        type;       /* card type */    // card类型,后面说明

    unsigned int        state;      /* (our) card state */    // card的当前状态,后面说明

    unsigned int        quirks;     /* card quirks */    // 该card的一些特点
    unsigned int        erase_size; /* erase size in sectors */   
    unsigned int        erase_shift;    /* if erase unit is power 2 */
    unsigned int        pref_erase; /* in sectors */
    u8          erased_byte;    /* value of erased bytes */

    u32         raw_cid[4]; /* raw card CID */    // 原始的cid寄存器的值
    u32         raw_csd[4]; /* raw card CSD */    // 原始的csd寄存器的值
    u32         raw_scr[2]; /* raw card SCR */    // 原始的scr寄存器的值
    struct mmc_cid      cid;        /* card identification */    // 从cid寄存器的值解析出来的信息
    struct mmc_csd      csd;        /* card specific */    // 从csd寄存器的值解析出来的信息
    struct mmc_ext_csd  ext_csd;    /* mmc v4 extended card specific */    // 从ext_csd寄存器的值解析出来的信息
    struct sd_scr       scr;        /* extra SD information */    // 外部sdcard的信息
    struct sd_ssr       ssr;        /* yet more SD information */    // 更多关于sd card的信息
    struct sd_switch_caps   sw_caps;    /* switch (CMD6) caps */    // sd的切换属性
    unsigned int        sd_bus_speed;   /* Bus Speed Mode set for the card */

    struct dentry       *debugfs_root;    // 对应debug目录的结构体
    struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */    // 物理分区
    unsigned int    nr_parts;    // 分区数量
    unsigned int    part_curr;    // 当前分区

    struct mmc_wr_pack_stats wr_pack_stats; /* packed commands stats*/

    struct mmc_bkops_info   bkops_info;

    struct device_attribute rpm_attrib;    // rpm属性
    unsigned int        idle_timeout; 
    struct notifier_block        reboot_notify;
    bool issue_long_pon;
    u8 *cached_ext_csd;
};

mmc card类型(mmc_card->type)如下:

#define MMC_TYPE_MMC        0       /* MMC card */
#define MMC_TYPE_SD     1       /* SD card */
#define MMC_TYPE_SDIO       2       /* SDIO card */
#define MMC_TYPE_SD_COMBO   3       /* SD combo (IO+mem) card */

mmc card状态(mmc_card->state)如下:

#define MMC_STATE_PRESENT   (1<<0)      /* present in sysfs */
#define MMC_STATE_READONLY  (1<<1)      /* card is read-only */
#define MMC_STATE_HIGHSPEED (1<<2)      /* card is in high speed mode */
#define MMC_STATE_BLOCKADDR (1<<3)      /* card uses block-addressing */
#define MMC_STATE_HIGHSPEED_DDR (1<<4)      /* card is in high speed mode */
#define MMC_STATE_ULTRAHIGHSPEED (1<<5)     /* card is in ultra high speed mode */
#define MMC_CARD_SDXC       (1<<6)      /* card is SDXC */
#define MMC_CARD_REMOVED    (1<<7)      /* card has been removed */
#define MMC_STATE_HIGHSPEED_200 (1<<8)      /* card is in HS200 mode */
#define MMC_STATE_HIGHSPEED_400 (1<<9)      /* card is in HS400 mode */
#define MMC_STATE_DOING_BKOPS   (1<<10)     /* card is doing BKOPS */
#define MMC_STATE_NEED_BKOPS    (1<<11)     /* card needs to do BKOPS */

mmc card的一些特写标识(mmc_card->quirks)如下:

#define MMC_QUIRK_LENIENT_FN0   (1<<0)      /* allow SDIO FN0 writes outside of the VS CCCR range */
#define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1)    /* use func->cur_blksize */
                        /* for byte mode */
#define MMC_QUIRK_NONSTD_SDIO   (1<<2)      /* non-standard SDIO card attached */
                        /* (missing CIA registers) */
#define MMC_QUIRK_BROKEN_CLK_GATING (1<<3)  /* clock gating the sdio bus will make card fail */
#define MMC_QUIRK_NONSTD_FUNC_IF (1<<4)     /* SDIO card has nonstd function interfaces */
#define MMC_QUIRK_DISABLE_CD    (1<<5)      /* disconnect CD/DAT[3] resistor */
#define MMC_QUIRK_INAND_CMD38   (1<<6)      /* iNAND devices have broken CMD38 */
#define MMC_QUIRK_BLK_NO_CMD23  (1<<7)      /* Avoid CMD23 for regular multiblock */
#define MMC_QUIRK_BROKEN_BYTE_MODE_512 (1<<8)   /* Avoid sending 512 bytes in */
#define MMC_QUIRK_LONG_READ_TIME (1<<9)     /* Data read time > CSD says */
#define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10) /* Skip secure for erase/trim */
                        /* byte mode */
#define MMC_QUIRK_INAND_DATA_TIMEOUT  (1<<11)   /* For incorrect data timeout */
/* To avoid eMMC device getting broken permanently due to HPI feature */
#define MMC_QUIRK_BROKEN_HPI (1 << 12)
 /* Skip data-timeout advertised by card */
#define MMC_QUIRK_BROKEN_DATA_TIMEOUT   (1<<13)
#define MMC_QUIRK_CACHE_DISABLE (1 << 14)       /* prevent cache enable */

2.2.struct mmc_part

225 /*   
226  * MMC Physical partitions
227  */
228 struct mmc_part {
229     unsigned int    size;   /* partition size (in bytes) */
230     unsigned int    part_cfg;   /* partition type */
231     char    name[MAX_MMC_PART_NAME_LEN];
232     bool    force_ro;   /* to make boot parts RO by default */
233     unsigned int    area_type;
234 #define MMC_BLK_DATA_AREA_MAIN  (1<<0)                                                                   
235 #define MMC_BLK_DATA_AREA_BOOT  (1<<1)
236 #define MMC_BLK_DATA_AREA_GP    (1<<2)
237 #define MMC_BLK_DATA_AREA_RPMB  (1<<3)
238 };

2.3.struct mmc_bus_ops

  host的mmc总线的操作集,由host插入的card决定。不同类型的card对mmc总线的操作有所不同。

struct mmc_bus_ops {
    int (*awake)(struct mmc_host *);   // 唤醒mmc总线上的card
    int (*sleep)(struct mmc_host *);   // 休眠mmc总线上的card
    void (*remove)(struct mmc_host *);   // 从软件上注销mmc总线上的card
    void (*detect)(struct mmc_host *);   // 检测mmc总线上的card是否被移除
    int (*suspend)(struct mmc_host *);   // 对应mmc总线的suspend操作
    int (*resume)(struct mmc_host *);   // 对应mmc总线的resume操作
    int (*power_save)(struct mmc_host *);   // 存储电源状态
    int (*power_restore)(struct mmc_host *);   // 恢复电源状态
    int (*alive)(struct mmc_host *);   // 检测mmc总线上的card的激活状态
    int (*change_bus_speed)(struct mmc_host *, unsigned long *);   // 修改mmc总线的工作时钟
};

2.4.struct mmc_ios

  struct mmc_ios 由mmc core定义的规范的结构,用来维护mmc总线相关的一些io setting。

struct mmc_ios {
    unsigned int    clock;          /* clock rate */ // 当前工作频率
    unsigned int    old_rate;       /* saved clock rate */    // 上一次的工作频率
    unsigned long   clk_ts;         /* time stamp of last updated clock */    // 上一次更新工作频率的时间戳
    unsigned short  vdd;/* vdd stores the bit number of the selected voltage range from below. */   // 支持的电压表
    unsigned char   bus_mode;       /* command output mode */    // 总线输出模式,包括开漏模式和上拉模式
    unsigned char   chip_select;        /* SPI chip select */    // spi片选
    unsigned char   power_mode;     /* power supply mode */    // 电源状态模式
    unsigned char   bus_width;      /* data bus width */    // 总线宽度
    unsigned char   timing;         /* timing specification used */    // 时序类型
    unsigned char   signal_voltage;     /* signalling voltage (1.8V or 3.3V) */    // 信号的工作电压
    unsigned char   drv_type;       /* driver type (A, B, C, D) */    // 驱动类型
};

操作函数:

  • mmc_set_ios //调用sdhci_set_ios设置ios
  • mmc_set_timing
  • mmc_set_driver_type
  • mmc_set_bus_mode
  • mmc_set_chip_select

3.驱动代码分析

3.1.mmc_bus_type

  • mmc_bus_type代表了mmc虚拟总线。其内容如下:
static struct bus_type mmc_bus_type = {
    .name       = "mmc",                        // 相应会在/sys/bus下生成mmc目录
    .dev_attrs  = mmc_dev_attrs,                  // bus下的device下继承的属性,可以看到/sys/bus/mmc/devices/mmc0:0001/type属性就是这里来的
    .match      = mmc_bus_match,       // 用于mmc bus上device和driver的匹配
    .uevent     = mmc_bus_uevent,
    .probe      = mmc_bus_probe,       // 当match成功的时候,执行的probe操作
    .remove     = mmc_bus_remove,
    .shutdown        = mmc_bus_shutdown,
    .pm     = &mmc_bus_pm_ops,         // 挂在mmc bus上的device的电源管理操作集合
};

3.2.驱动和设备匹配

static int mmc_bus_match(struct device *dev, struct device_driver *drv)
{
    return 1;      // 无条件返回1,说明挂载mmc bus上的device(mmc_card)和driver(mmc_driver)是无条件匹配的。
}

static int mmc_bus_probe(struct device *dev)
{
    struct mmc_driver *drv = to_mmc_driver(dev->driver);
    struct mmc_card *card = mmc_dev_to_card(dev);
    return drv->probe(card);   // 直接调用mmc_driver中的probe操作,对于block.c来说就是mmc_blk_probe
}

3.3.驱动入口函数mmc_blk_init()

  • 该函数向内核申请注册一个块设备,然后进入核心层进行注册。
drivers/mmc/core/block.c:
static int __init mmc_blk_init(void)  
{  
    /*向内核申请注册一个块设备*/  
    res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");  
    /*进入核心层进行注册*/  
    mmc_register_driver(&mmc_driver);      //注册mmc_driver
    return 0;  
} 

int mmc_register_driver(struct mmc_driver *drv)     
{
     drv->drv.bus = &mmc_bus_type;        //mmc设备是挂载在mmc总线上的
     return driver_register(&drv->drv);       //注册mmc驱动
}

3.4.驱动结构体mmc_driver

  • 该结构体定义驱动的名字,驱动探针函数、驱动移除函数、驱动阻塞和驱动重启等函数。
static struct mmc_driver mmc_driver = {  
    .drv            = {  
        .name       = "mmcblk",  
    },  
    .probe          = mmc_blk_probe,  
    .remove         = mmc_blk_remove,  
    .suspend        = mmc_blk_suspend,  
    .resume         = mmc_blk_resume,  
}; 

3.5.块设备操作结构体mmc_bdops

  • 结构体mmc_bdops中定义了块设备操作的接口函数。
static struct block_device_operations mmc_bdops = {  
    .open           = mmc_blk_open,  
    .release        = mmc_blk_release,  
    .getgeo         = mmc_blk_getgeo,  
    .owner          = THIS_MODULE,  
}; 

3.6.块设备探针函数mmc_blk_probe()

  • 该函数主要完成检验卡支持的命令,分配mmc_blk_data结构体空间,设置块的大小,设置card的driver_data 字段,并注册mmc信息到系统。
static int mmc_blk_probe(struct mmc_card *card)  
{  
    struct mmc_blk_data *md;  
    int err;  
    char cap_str[10];  
    
    /*检查卡支持的命令*/  
    if (!(card->csd.cmdclass & CCC_BLOCK_READ))  
        return -ENODEV;  
    /*为card分配mmc_blk_data结构体空间*/  
    md  =  mmc_blk_alloc(card);  
    
    /*设置块的大小*/  
    mmc_blk_set_blksize(md, card);  
    /*将md设置为card的driver_data 字段*/  
    mmc_set_drvdata(card, md);  
    /*把mmc包含的信息向系统进行注册,注册成功后就可以在文
件系统对应目录下找到 mmc_card对应的结点设备*/  
    add_disk(md->disk);  
    return 0;  
} 

3.6.1.mmc_blk_alloc():

 mmc_blk_alloc:
  -> md = mmc_blk_alloc_req(card, &card->dev, size, false, NULL);
    -> md->disk = alloc_disk(perdev_minors);
    -> ret = mmc_init_queue(&md->queue, card, &md->lock, subname); //初始化blk队列,然后建立一个线程mmc_queue_thread().
      -> mq->queue = blk_init_queue(mmc_request, lock);
    -> set_capacity(md->disk, size);
  • mmc_init_queue->mmc_queue_thread->mmc_blk_issue_rq
    其调用关系如下:
mmc_blk_issue_rq
  -> mmc_blk_issue_secdiscard_rq(mq, req);
  -> mmc_blk_issue_discard_rq(mq, req);
  -> mmc_blk_issue_flush(mq, req);
  -> mmc_blk_issue_rw_rq(mq, req);    /* 上面四个函数选一个执行 */
    -> mmc_wait_for_req(card->host, &brq.mrq);
      -> mmc_start_request(host, mrq);
        -> host->ops->request(host, mrq);    /* s3cmci.c中host->requset = s3cmci_request */

请求处理过程:
  mmc_queue_thread----->mmc_blk_issue_rq---->mmc_wait_for_req—>mmc_start_request---->host->ops->request(s3cmci.c里边的s3cmci_request被调用)

3.6.2. mmc_add_disk():

mmc_add_disk(md)
  -> add_disk(md->disk);
  -> device_create_file(disk_to_dev(md->disk), &md->force_ro);

4.APIs

4.1.mmc_alloc_card

  • 用于分配一个struct mmc_card结构体,创建其于mmc host以及mmc bus之间的关联。
struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type)
{
    struct mmc_card *card;

    card = kzalloc(sizeof(struct mmc_card), GFP_KERNEL);    // 分配一个mmc_card
    if (!card)
        return ERR_PTR(-ENOMEM);

    card->host = host;        // 关联mmc_card与mmc_host 

    device_initialize(&card->dev);

    card->dev.parent = mmc_classdev(host);    
        // 设置card的device的parent device为mmc_host的classdev,
        // 注册到设备驱动模型中之后,会在/sys/class/mmc_host/mmc0目录下生成相应card的节点,如mmc0:0001
    card->dev.bus = &mmc_bus_type;
        // 设置card的bus为mmc_bus_type,这样,mmc_card注册到设备驱动模型中之后就会挂在mmc_bus下。
        // 会在/sys/bus/mmc/devices/目录下生成相应card的节点,如mmc0:0001
    card->dev.release = mmc_release_card;
    card->dev.type = type;    // 设置device type

    spin_lock_init(&card->bkops_info.bkops_stats.lock);    // 初始化spin_lock
    spin_lock_init(&card->wr_pack_stats.lock);                   // 初始化spin_lock

    return card;
}

4.2.mmc_add_card

  • 用于注册struct mmc_card到mmc_bus上。
int mmc_add_card(struct mmc_card *card)
{
    int ret;

/* 以下用于打印card的注册信息 */
    const char *type;
    const char *uhs_bus_speed_mode = "";
    
    if (mmc_host_is_spi(card->host)) {
        pr_info("%s: new %s%s%s card on SPI\n",
            mmc_hostname(card->host),
            mmc_card_highspeed(card) ? "high speed " : "",
            mmc_card_ddr_mode(card) ? "DDR " : "",
            type);
    } else {
        pr_info("%s: new %s%s%s%s%s%s card at address %04x\n",
            mmc_hostname(card->host),
            mmc_card_uhs(card) ? "ultra high speed " :
            (mmc_card_highspeed(card) ? "high speed " : ""),
            (mmc_card_hs400(card) ? "HS400 " : ""),
            (mmc_card_hs200(card) ? "HS200 " : ""),
            mmc_card_ddr_mode(card) ? "DDR " : "",
            uhs_bus_speed_mode, type, card->rca);
    }
        // 在这里会打印出card信息的字符串
        // eg:mmc0: new HS200 MMC card at address 0001

/* 设置card的debug节点 */
#ifdef CONFIG_DEBUG_FS
    mmc_add_card_debugfs(card);
        // 创建card对应的debug节点,对应路径例如:/sys/kernel/debug/mmc0/mmc0:0001
#endif
    mmc_init_context_info(card->host);   // 初始化同步的文本信息

/* 以下使能card device的pm runtime的功能 */
    ret = pm_runtime_set_active(&card->dev);   
    if (ret)
        pr_err("%s: %s: failed setting runtime active: ret: %d\n",
               mmc_hostname(card->host), __func__, ret);
    else if (!mmc_card_sdio(card) && mmc_use_core_runtime_pm(card->host))
        pm_runtime_enable(&card->dev);

   /* 添加到设备驱动模型中 */
    ret = device_add(&card->dev);
        // 会创建/sys/bus/mmc/devices/mmc0:0001节点和/sys/class/mmc_host/mmc0/mmc0:0001节点

    /* 使能异步device suspend,初始化runtime_pm_timeout属性 */
    device_enable_async_suspend(&card->dev);
    if (mmc_use_core_runtime_pm(card->host) && !mmc_card_sdio(card)) {
        card->rpm_attrib.show = show_rpm_delay;
        card->rpm_attrib.store = store_rpm_delay;
        sysfs_attr_init(&card->rpm_attrib.attr);
        card->rpm_attrib.attr.name = "runtime_pm_timeout";
        card->rpm_attrib.attr.mode = S_IRUGO | S_IWUSR;

        ret = device_create_file(&card->dev, &card->rpm_attrib);
        if (ret)
            pr_err("%s: %s: creating runtime pm sysfs entry: failed: %d\n",
                   mmc_hostname(card->host), __func__, ret);
        /* Default timeout is 10 seconds */
        card->idle_timeout = RUNTIME_SUSPEND_DELAY_MS;
    }

/* 设置mmc card的state标识 */
    mmc_card_set_present(card);
        // 设置card的MMC_STATE_PRESENT状态
        // #define MMC_STATE_PRESENT    (1<<0)      /* present in sysfs */
        // 表示card已经合入到sysfs中了

    return 0;
}

如上所示:device_add会调用probe函数,即mmc_blk_probe。

发布了161 篇原创文章 · 获赞 15 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_41028621/article/details/103791941