Linux MMC 框架源码分析

linux mmc 框架源码分析

文章基于

内核版本
linux-3.2.0x
CPU
芯唐 的 NUC972
源码路径 linux-3.10.x\drivers\mmc

文章仅分析 sd卡这类设备在 Linux mmc 框架中的源码
本文基于个人的理解与思考, 如有错误的地方欢迎指正.

linux mmc 框架代码目录结构

源码路径 kernel-3.2\drivers\mmc

mmc/
|-- card
|   |-- block.c
|   |-- block.o
|   |-- queue.c
|   |-- queue.h
|   `-- sdio_uart.c
|-- core
|   |-- bus.c
|   |-- bus.h
|   |-- core.c
|   |-- core.h
|   |-- host.c
|   |-- host.h
|   |-- quirks.c
|   |-- sd.c
|   |-- sd.h
|   |-- sd_ops.c
|   `-- sd_ops.h
`-- host
    |-- nuc970_emmc.c
    `-- nuc970_sd.c
card
mmc card 层代码, 实现块设备的各种功能接口
core
mmc 核心层代码, 实现检测外接存储(如sd卡)和生成对应设备的一整个流程
host
mmc host 层代码,对应不同的cpu有不同的 host 驱动,通过对 cpu mmc 相关寄存器控制实现与外接sd卡的命令/数据收发
目录下的 nuc970_sd.c 就是 NUC972 芯片的针对 sd 类的设备的适配器驱动
而 nuc970_emmc.c 就是 NUC972 芯片的针对 emmc 类的设备的适配器驱动

linux mmc 框架

在这里插入图片描述

linux mmc 分为三层,分别是:

  • mmc card 层
  • mmc 核心层
  • mmc host 层

mmc 核心层

mmc 核心层提供一下功能:

  • 提供 mmc host 注册接口: int mmc_add_host(struct mmc_host *host)
  • 提供 mmc card 驱动注册接口: int mmc_register_driver(struct mmc_driver *drv)
  • 负责外接sd卡的检测和检测到sd卡接入后生成对应总线设备的一整个过程

mmc host 层

  • 调用核心层接口 mmc_add_host() 注册一个 mmc host
  • 提供最外接sd卡的一系列操作函数, 即 struct mmc_host_ops 结构体的一系列函数, 如 .get_cd 函数实现检测sd卡是否在位
  • 实现检测sd卡插入/拔出的机制(如中断)

struct mmc_host_ops 结构体包含了一系列对外接sd卡的操作函数, 结构体中比较常用的回调有以下几个:

struct mmc_host_ops {
    ...
	void	(*request)(struct mmc_host *host, struct mmc_request *req);
	void	(*set_ios)(struct mmc_host *host, struct mmc_ios *ios);
	int	(*get_ro)(struct mmc_host *host);
	int	(*get_cd)(struct mmc_host *host);
	void	(*enable_sdio_irq)(struct mmc_host *host, int enable);
	...
};

其中(与sd卡的数据交互包括两种, 一种是命令 CMD, 一种是数据 DATA)
request 函数
: 进行sd卡数据读写时的回调, 需要控制 mmc 寄存器实现数据发送接收

set_ios 函数
进行sd卡命令收发时的回调, 需要控制 mmc 寄存器实现命令发送接收
get_ro 函数
检测sd卡是否为只读模式的回调
get_cd 函数
检测sd卡接入/拔出状态的的回调
enable_sdio_irq 函数
使能/关闭 cpu mmc 中断功能的回调

mmc card 层

  • 调用核心层接口 mmc_register_driver() 注册一个 mmc crad 层的平台驱动, 用来与平台设备匹配之后生成sd卡对应的块设备
  • 实现块设备的分区管理和读写管理

mmc host层

host层平台设备

源码: linux-3.10.x\arch\arm\mach-nuc970\dev.c

添加平台设备

void __init nuc970_platform_init(struct platform_device **device, int size)
	platform_add_devices(nuc970_public_dev, ARRAY_SIZE(nuc970_public_dev)); /*添加平台设备*/
static struct platform_device *nuc970_public_dev[] __initdata = {
   
    ...
    &nuc970_device_sdh,
    ...
}
struct platform_device nuc970_device_sdh = {
   
        .name		  = "nuc970-sdh",
        .id		  = -1,
        .num_resources	  = ARRAY_SIZE(nuc970_sdh_resource),
        .resource	  = nuc970_sdh_resource,
	    .dev              = {
   
		.dma_mask = &nuc970_device_sdh_dmamask,
		.coherent_dma_mask = 0xffffffffUL
	}
};
static struct resource nuc970_sdh_resource[] = {
   
        [0] = {
   
                .start = NUC970_PA_SDH,
                .end   = NUC970_PA_SDH + NUC970_SZ_SDH - 1,
                .flags = IORESOURCE_MEM,
        },
        [1] = {
   
                .start = IRQ_SDH,
                .end   = IRQ_SDH,
                .flags = IORESOURCE_IRQ,
        }
};

host层平台驱动

源码: linux-3.10.x\drivers\mmc\host\nuc970_sd.c

注册平台驱动

static struct platform_driver nuc970_sd_driver = {
   
        .probe      = nuc970_sd_probe,
        ...
        .driver     = {
   
                .name   = "nuc970-sdh",
                .owner  = THIS_MODULE,
        },
};
module_platform_driver(nuc970_sd_driver);

注册平台驱动和设备后
平台总线根据 name 为 “nuc970-sdh” 进行平台驱动/设备的匹配
匹配后回调进入 .probe = nuc970_sd_probe

static int nuc970_sd_probe(struct platform_device *pdev)
    /*获取平台资源*/
    platform_get_resource(pdev, IORESOURCE_MEM, 0);
    request_mem_region(res->start, res->end - res->start + 1, DRIVER_NAME)

    sema_init(&sdh_fmi_sem, 1); /*信号量初始化*/
    devm_pinctrl_get_select(&pdev->dev, "sd0"); /*引脚初始化*/

    /*时钟初始化*/
    clk_prepare(clk_get(NULL, "sdh_hclk"));
    ...

    /*
    * 申请 mmc_host 结构体,并申请一段私有数据段存放 struct nuc970_sd_host 结构体
    * mmc_host 结构体描述了 mmc 的工作方式
    * nuc970_sd_host 结构体是根据 芯唐nuc970平台 host 层驱动逻辑需求多定义的一个结构体
    */
    mmc = mmc_alloc_host(sizeof(struct nuc970_sd_host), &pdev->dev);
        ->进入 mmc 核心层,进入 struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
        host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); /*申请一段内存存放 struct mmc_host*/
        host->rescan_disable = 1; /*这个变量在 mmc 扫描是否外接sd卡时用到, 由于mmc host 还没初始化完成,暂时停止扫描*/
        ...
        dev_set_name(&host->class_dev, "mmc%d", host->index); /*设置设备名*/
        ...
        host->class_dev.class = &mmc_host_class; /*设置设备 class 为 mmc_host_class*/
        device_initialize(&host->class_dev); /*初始化设备*/

        /*设置 host 的时钟门控
        * 通过动态调整 mmc 工作时钟,节省功耗
        * 例如sd卡空闲时,关闭 mmc 的时钟
        * 这部分功能逻辑这里不作分析
        */
        mmc_host_clk_init(host);

        /*一些锁的初始化*/
        ...

        init_waitqueue_head(&host->wq); /*初始化一个等待队列 host->wq*/
        INIT_DELAYED_WORK(&host->detect, mmc_rescan); /*初始化一个延时任务 mmc_rescan, mmc_rescan 用于检测是否有外接设备, 如sd卡*/

        /*初始化主机控制器 host 结构体, 填充好默认参数*/
        host->max_segs = 1;
        host->max_seg_size = PAGE_CACHE_SIZE;
        host->max_req_size = PAGE_CACHE_SIZE;
        host->max_blk_size = 512;
        host->max_blk_count = PAGE_CACHE_SIZE / 512;

    /*根据cpu的sd硬件接口,填充 mmc_host 结构体
    * struct mmc_host 结构体是 mmc 核心层处理相关逻辑时使用到的
    */
    mmc->ops = &nuc970_sd_ops; /*注册 mmc_host 的功能接口,由核心层回调*/
        ->nuc970_sd_ops 结构体定义
        static const struct mmc_host_ops nuc970_sd_ops = {
   
            .request    = nuc970_sd_request,              /*处理 mmc 请求, 进行外接 mmc 设备的读写, 如sd卡的读写*/
            .set_ios    = nuc970_sd_set_ios,              /*配置 mmc 硬件接口, 根据传进的 ios 信息, 配置 mmc 的开关/mmc 宽度等信息*/
            .get_ro     = nuc970_sd_get_ro,               /*检测外接 mmc 设备是否处于写保护模式*/
            .get_cd     = nuc970_sd_card_detect,          /*通过sd卡检测脚判断是否有sd卡接入*/
            .enable_sdio_irq = nuc900_sd_enable_sdio_irq, /*使能/关闭 mmc 的中断寄存器*/
        };
    mmc->f_min = 300000;   /*mmc 控制器支持的最小频率*/
    mmc->f_max = 50000000; /*mmc 控制器支持的最大频率*/
    mmc->ocr_avail = MMC_VDD_27_28|MMC_VDD_28_29|MMC_VDD_29_30|MMC_VDD_30_31|MMC_VDD_31_32|MMC_VDD_32_33 | MMC_VDD_33_34; /*mmc 控制器支持的有效电压范围*/
    mmc->caps |= (MMC_CAP_4_BIT_DATA|MMC_CAP_SDIO_IRQ|MMC_CAP_SD_HIGHSPEED|MMC_CAP_MMC_HIGHSPEED); /*mmc 控制器支持的功能,如4位数据收发等*/
    mmc->max_blk_size  = MCI_MAXBLKSIZE;
    mmc->max_blk_count = MCI_BLKATONCE
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值