Linux驱动分析之MMC子系统框架

简介: Linux内核中,MMC不仅是一个驱动,而是一个子系统。内核把mmc, sd以及sdio三者的驱动代码整合在一起,俗称MMC子系统。源码位于drivers/mmc下。mmc目录下有core和host两个文件夹(以前的版本可能还有card目录,现在已经和core目录合并了)。

前言

上一篇《一文搞懂SDIO》简单介绍了SDIO接口及相关的协议。接下来来看一下Linux提供的驱动框架。


MMC子系统介绍

Linux内核中,MMC不仅是一个驱动,而是一个子系统。内核把mmc, sd以及sdio三者的驱动代码整合在一起,俗称MMC子系统。源码位于drivers/mmc下。mmc目录下有core和host两个文件夹(以前的版本可能还有card目录,现在已经和core目录合并了)。


MMC整体框架

6b44f299adf13a183b3442282b3117d2.png

  • Host:针对不同主机端的SDHC、MMC控制器的驱动,这部分需要由驱动工程师来完成。
  • Core:整个MMC的核心层,这部分实现了不同协议和规范,为host层和设备驱动层提供接口函数。合并后还存放了块设备的相关驱动。


重要结构体

mmc_host

//描述控制器structmmc_host {
structdevice*parent;
structdeviceclass_dev;
intindex;
conststructmmc_host_ops*ops; //host操作函数structmmc_pwrseq*pwrseq;
unsignedintf_min;
unsignedintf_max;
unsignedintf_init;
u32ocr_avail;
u32ocr_avail_sdio;  //SDIO OCR寄存器u32ocr_avail_sd;  //SD OCR寄存器u32ocr_avail_mmc;  //MMC OCR寄存器structwakeup_source*ws;  /* Enable consume of uevents */u32max_current_330;
u32max_current_300;
u32max_current_180;
u32caps;    //host特性u32caps2;    //更多host特性intfixed_drv_type;  //固定驱动类型, 不可移除的设备使用mmc_pm_flag_tpm_caps;  //PM特性//host块数据unsignedintmax_seg_size;  /* see blk_queue_max_segment_size */unsignedshortmax_segs;  /* see blk_queue_max_segments */unsignedshortunused;
unsignedintmax_req_size;  //单个请求的最大字节数unsignedintmax_blk_size;  //单个mmc块最大大小unsignedintmax_blk_count;  //单个请求中最大块数unsignedintmax_busy_timeout; //最大繁忙超时时间 ms//私有数据spinlock_tlock;    //总线操作时的锁structmmc_iosios;    //当前IO总线设置unsignedintuse_spi_crc:1;
unsignedintclaimed:1;  //host声明unsignedintdoing_init_tune:1; //表示初始化调节中,调节指调节采样点unsignedintcan_retune:1;  //表示是否可重新调节unsignedintdoing_retune:1;  //表示重新调节中unsignedintretune_now:1;  //表示下一个请求中重新调节unsignedintretune_paused:1; //表示重新调节暂时关闭unsignedintretune_crc_disable:1; /* don't trigger retune upon crc */unsignedintcan_dma_map_merge:1; /* merging can be used */intrescan_disable;  //关闭卡检测intrescan_entered;  //用于不可移除的设备intneed_retune;  //表示是否需要重新调节inthold_retune;  //表示推迟重新调整unsignedintretune_period;  //重新调整的周期structtimer_listretune_timer;  //重新调整的Timerbooltrigger_card_event; /* card_event necessary */structmmc_card*card;    //附着在host上的设备wait_queue_head_twq;             //等待队列structmmc_ctx*claimer;  //host上下文intclaim_cnt;  /* "claim" nesting count */structmmc_ctxdefault_ctx;  //默认上下文structdelayed_workdetect;
intdetect_change;  //卡检测标志structmmc_slotslot;
conststructmmc_bus_ops*bus_ops;  //总线操作函数集unsignedintsdio_irqs;      //sdio中断structtask_struct*sdio_irq_thread; //中断处理线程structdelayed_worksdio_irq_work;
boolsdio_irq_pending;
atomic_tsdio_irq_thread_abort;
mmc_pm_flag_tpm_flags;  /* requested pm features */structled_trigger*led;    /* activity led */#ifdef CONFIG_REGULATORboolregulator_enabled; /* regulator state */#endifstructmmc_supplysupply;
structdentry*debugfs_root;
//数据传输过程中进行命令传输structmmc_request*ongoing_mrq; 
#ifdef CONFIG_FAIL_MMC_REQUESTstructfault_attrfail_mmc_request;
#endifunsignedintactual_clock;  /* Actual HC clock rate */unsignedintslotno;  /* used for sdio acpi binding */intdsr_req;  /* DSR value is valid */u32dsr;  /* optional driver stage (DSR) value *///命令队列引擎conststructmmc_cqe_ops*cqe_ops;
void*cqe_private;
intcqe_qdepth;
boolcqe_enabled;
boolcqe_on;
/* Inline encryption support */#ifdef CONFIG_MMC_CRYPTOstructblk_keyslot_managerksm;
#endif/* Host Software Queue support */boolhsq_enabled;
unsignedlongprivate[] ____cacheline_aligned;
};


mmc_card

//描述卡structmmc_card {
structmmc_host*host;    //设备所属的hoststructdevicedev;    /* the device */u32ocr;    //当前OCR设置unsignedintrca;    //设备的卡地址unsignedinttype;    //卡类型unsignedintstate;    //卡状态unsignedintquirks;   /* card quirks */unsignedintquirk_max_rate;  /* max rate set by quirks */boolreenable_cmdq;  //重新使能命令队列unsignedinterase_size;  //扇区擦除的大小unsignedinterase_shift;  /* if erase unit is power 2 */unsignedintpref_erase;  /* in sectors */unsignedinteg_boundary;  /* don't cross erase-group boundaries */unsignedinterase_arg;  /* erase / trim / discard */u8erased_byte;  //已经擦除的字节数u32raw_cid[4];  //原始卡CIDu32raw_csd[4];  //原始卡CSDu32raw_scr[2];  //原始卡SCRu32raw_ssr[16];  //原始卡SSRstructmmc_cidcid;    //原始卡IDstructmmc_csdcsd;    /* card specific */structmmc_ext_csdext_csd;  /* mmc v4 extended card specific */structsd_scrscr;    //附加SD信息structsd_ssrssr;    /* yet more SD information */structsd_switch_capssw_caps;  /* switch (CMD6) caps */structsd_ext_regext_power;  /* SD extension reg for PM */structsd_ext_regext_perf;  /* SD extension reg for PERF */unsignedintsdio_funcs;  //SDIO function数量atomic_tsdio_funcs_probed; /* number of probed SDIO funcs */structsdio_cccrcccr;    //通用卡信息structsdio_ciscis;    //通用tuple信息, tuple指存储结构structsdio_func*sdio_func[SDIO_MAX_FUNCS]; /* SDIO functions (devices) */structsdio_func*sdio_single_irq; /* SDIO function when only one IRQ active */u8major_rev;  //主版本号u8minor_rev;  //次版本号unsignednum_info;  //信息字符串数量constchar**info;    //信息字符串structsdio_func_tuple*tuples;  //未知通用tuplesunsignedintsd_bus_speed;  //总线速率模式设置unsignedintmmc_avail_type;  //支持的设备类型:host或cardunsignedintdrive_strength;  /* for UHS-I, HS200 or HS400 */structdentry*debugfs_root;
structmmc_partpart[MMC_NUM_PHY_PARTITION]; //物理分区unsignedintnr_parts;
structworkqueue_struct*complete_wq;  //私有工作队列};


mmc_host_ops

structmmc_host_ops {
//pre_req和post_req是为双buffervoid  (*post_req)(structmmc_host*host, structmmc_request*req,
interr);
void  (*pre_req)(structmmc_host*host, structmmc_request*req);
void  (*request)(structmmc_host*host, structmmc_request*req);
//提交请求(有原子上下文)int  (*request_atomic)(structmmc_host*host,
structmmc_request*req);
//避免经常调用下面三个函数//IO总线设置void  (*set_ios)(structmmc_host*host, structmmc_ios*ios);
//获取卡是否是只读(0:读写 1:只读)int  (*get_ro)(structmmc_host*host);
//获取卡是否存在int  (*get_cd)(structmmc_host*host);
//使能中断void  (*enable_sdio_irq)(structmmc_host*host, intenable);
/* Mandatory callback when using MMC_CAP2_SDIO_IRQ_NOTHREAD. */void  (*ack_sdio_irq)(structmmc_host*host);
/* optional callback for HC quirks */void  (*init_card)(structmmc_host*host, structmmc_card*card);
int  (*start_signal_voltage_switch)(structmmc_host*host, structmmc_ios*ios);
//检查卡的data[0]是否处于拉低中         int  (*card_busy)(structmmc_host*host);
//SD和eMMC卡的调节命令(tuning command)opcode是不同的int  (*execute_tuning)(structmmc_host*host, u32opcode);
/* Prepare HS400 target operating frequency depending host driver */int  (*prepare_hs400_tuning)(structmmc_host*host, structmmc_ios*ios);
/* Prepare switch to DDR during the HS400 init sequence */int  (*hs400_prepare_ddr)(structmmc_host*host);
/* Prepare for switching from HS400 to HS200 */void  (*hs400_downgrade)(structmmc_host*host);
/* Complete selection of HS400 */void  (*hs400_complete)(structmmc_host*host);
/* Prepare enhanced strobe depending host driver */void  (*hs400_enhanced_strobe)(structmmc_host*host,
structmmc_ios*ios);
int  (*select_drive_strength)(structmmc_card*card,
unsignedintmax_dtr, inthost_drv,
intcard_drv, int*drv_type);
//通过RST_n重置eMMCvoid  (*hw_reset)(structmmc_host*host);
void  (*card_event)(structmmc_host*host);
//多I/O控制器硬件错误回调int  (*multi_io_quirk)(structmmc_card*card,
unsignedintdirection, intblk_size);
//初始化SD express卡int  (*init_sd_express)(structmmc_host*host, structmmc_ios*ios);
};


sdio_func

//描述SDIO功能structsdio_func {
structmmc_card*card;    //所属的卡structdevicedev;    /* the device */sdio_irq_handler_t*irq_handler;  //中断回调unsignedintnum;    //function号 unsignedcharclass;    //标准接口类(class)unsignedshortvendor;    //VID, 厂家IDunsignedshortdevice;    //DID, 设备IDunsignedmax_blksize;  //最大块大小unsignedcur_blksize;  //当前块大小unsignedenable_timeout;  //最大使能超时时间unsignedintstate;    //function状态u8*tmpbuf;  //DMA:临时bufferu8major_rev;  //主版本号u8minor_rev;  //次版本号unsignednum_info;  //信息字符串数量constchar**info;    //信息字符串structsdio_func_tuple*tuples;         //CIS tuple};


mmc_driver

//mmc卡驱动,和platform_driver类似structmmc_driver {
structdevice_driverdrv;
int (*probe)(structmmc_card*card);
void (*remove)(structmmc_card*card);
void (*shutdown)(structmmc_card*card);
};


mmc_request

//表示一个传输请求structmmc_request {
structmmc_command*sbc;     //多块传输时设置块数,即CMD23命令structmmc_command*cmd;     //请求的命令structmmc_data*data;    //本次请求所携带的数据structmmc_command*stop;    //停止命令//完成量,用于同步命令完成structcompletioncompletion;
structcompletioncmd_completion;
void            (*done)(structmmc_request*); //命令完成回调void            (*recovery_notifier)(structmmc_request*);
structmmc_host*host; //host对象boolcap_cmd_during_tfr; //数据传输过程或忙等待时是否允许其他命令inttag;
#ifdef CONFIG_MMC_CRYPTOconststructbio_crypt_ctx*crypto_ctx;
intcrypto_key_slot;
#endif};


mmc_command

//表示commandstructmmc_command {
u32opcode; //命令码,比如CMD5, opcode=5u32arg;    //参数u32resp[4]; //应答unsignedintflags;      /* expected response type */unsignedintretries;    //最大重试次数interror;      //错误码unsignedintbusy_timeout;   //繁忙超时时间structmmc_data*data;      //命令关联的数据structmmc_request*mrq;       //关联的mmc_request};


mmc_data

//表示数据structmmc_data {
unsignedinttimeout_ns; //数据超时时间,最大80msunsignedinttimeout_clks;  //数据超时(时钟)unsignedintblksz;     //数据块大小unsignedintblocks;    //块数量unsignedintblk_addr;  //块地址interror;      /* data error */unsignedintflags;
unsignedintbytes_xfered;
structmmc_command*stop;      //停止命令structmmc_request*mrq;       //关联的mmc_requestunsignedintsg_len;     /* size of scatter list */intsg_count;   /* mapped sg entries */structscatterlist*sg;        /* I/O scatter list */s32host_cookie;    //host私有数据};


API函数

//注册/注销mmc驱动intmmc_register_driver(structmmc_driver*drv)
voidmmc_unregister_driver(structmmc_driver*drv)
/*******************************************************///分配host结构体structmmc_host*mmc_alloc_host(intextra, structdevice*dev)
//注册host到驱动模型intmmc_add_host(structmmc_host*host)
//从驱动模型中移除hostvoidmmc_remove_host(structmmc_host*host)
//释放host结构体voidmmc_free_host(structmmc_host*host)
/*******************************************************///分配sdio_func结构体structsdio_func*sdio_alloc_func(structmmc_card*card)
//注册function到驱动模型intsdio_add_func(structsdio_func*func)
/*******************************************************///分配mmc_card结构体structmmc_card*mmc_alloc_card(structmmc_host*host, structdevice_type*type)
//注册卡到驱动模型intmmc_add_card(structmmc_card*card)
//发送传输请求,mmc_start_request函数最终调用host->ops->requestintmmc_start_request(structmmc_host*host, structmmc_request*mrq)


总结

MMC子系统框架其实很简单,MMC核心层起着承上启下的作用,提供了接口和结构体。host目录下已经实现了大量MMC控制器的驱动了。

相关文章
|
26天前
|
Linux 网络安全 虚拟化
适用于Linux的Windows子系统(WSL1)的安装与使用记录
并放到启动文件夹,就可以开机自动启动了。
33 0
|
3月前
|
Ubuntu Linux 虚拟化
安装Windows Linux 子系统的方法:适用于windows 11 版本
本文提供了在Windows 11系统上安装Linux子系统(WSL)的详细步骤,包括启用子系统和虚拟化功能、从Microsoft Store安装Linux发行版、设置WSL默认版本、安装WSL2补丁,以及完成Ubuntu的首次安装设置。
925 2
|
3月前
|
Linux C语言
深度探索Linux操作系统 —— 编译过程分析
深度探索Linux操作系统 —— 编译过程分析
28 2
|
3月前
|
存储 Unix Linux
Linux 内核源代码情景分析(四)(下)
Linux 内核源代码情景分析(四)
25 2
|
2月前
|
存储 传感器 Linux
STM32微控制器为何不适合运行Linux系统的分析
总的来说,虽然技术上可能存在某些特殊情况下将Linux移植到高端STM32微控制器上的可能性,但从资源、性能、成本和应用场景等多个方面考虑,STM32微控制器不适合运行Linux系统。对于需要运行Linux的应用,更适合选择ARM Cortex-A系列处理器的开发平台。
237 0
|
2月前
|
Linux API SoC
Linux电压和电流调节器框架 【ChatGPT】
Linux电压和电流调节器框架 【ChatGPT】
|
2月前
|
Linux API
Linux里的高精度时间计时器(HPET)驱动 【ChatGPT】
Linux里的高精度时间计时器(HPET)驱动 【ChatGPT】
|
3月前
|
存储 算法 Unix
Linux 内核源代码情景分析(四)(中)
Linux 内核源代码情景分析(四)
46 0
|
存储 Unix Linux
浅入分析Linux
Linux 操作系统必须完成的两个主要目的 与硬件部分交互, 为包含在硬件平台上的所有底层可编程部件提供服务 为运行在计算机系统上的应用程序(即所谓的用户空间)提供执行环境 一些操作系统运行所有的用户程序都直接与硬件部分进行交互, 比如典型的MS-DOS。
1004 0
|
7天前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
29 3