前言
上一篇《一文搞懂SDIO》简单介绍了SDIO接口及相关的协议。接下来来看一下Linux提供的驱动框架。
MMC子系统介绍
Linux内核中,MMC不仅是一个驱动,而是一个子系统。内核把mmc, sd以及sdio三者的驱动代码整合在一起,俗称MMC子系统。源码位于drivers/mmc下。mmc目录下有core和host两个文件夹(以前的版本可能还有card目录,现在已经和core目录合并了)。
MMC整体框架
- 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 */boolregulator_enabled; /* regulator state */structmmc_supplysupply; structdentry*debugfs_root; //数据传输过程中进行命令传输structmmc_request*ongoing_mrq; structfault_attrfail_mmc_request; unsignedintactual_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 */structblk_keyslot_managerksm; /* 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; conststructbio_crypt_ctx*crypto_ctx; intcrypto_key_slot; };
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控制器的驱动了。