Linux MMC子系统分析(二)——Host分析

Linux MMC子系统分析(二)——Host分析


前言

通过前面对mmc子系统的模型分析,我们能够知道host是对应于硬件控制器的具体操作。本文将以sdhci-s3c.c为例对host进行简单的分析。

sdhci-s3c.c驱动代码的构成

实际上很多厂商的控制器代码都是基于sdhci.c这个驱动代码来实现的,不同的厂商调用其提供的函数实现自己驱动的个性化功能。下面将结合sdhci.c和sdhci-s3c.c来分析一个host的具体实现。

Host驱动的分析

通常分析一个驱动可以从它的init函数为入口进行分析,可是在sdhci-s3c.c文件中并没有相应的init函数,不过最驱动的最末端可以看见一个宏的使用

module_platform_driver(sdhci_s3c_driver)

这里通过使用替代了驱动中常见的init和exit函数,进一步查看这个宏:

define module_platform_driver(__platform_driver) \                    
       module_driver(__platform_driver, platform_driver_register, \   
                       platform_driver_unregister)                    

可以看见driver的register和unregister函数,使用这个宏可以和在init函数调用register以及在exit中调用unregister达到相同的效果。通过分析可以知道,这里是注册了一个platform driver,看一下sdhci_s3c_driver结构体中有哪些内容:

static struct platform_driver sdhci_s3c_driver = {                   
        .probe          = sdhci_s3c_probe,                           
        .remove         = sdhci_s3c_remove,                          
        .id_table       = sdhci_s3c_driver_ids,                      
        .driver         = {                                          
                .name   = "s3c-sdhci", //驱动名称                        
                .of_match_table = of_match_ptr(sdhci_s3c_dt_match),  //设备树匹配
                .pm     = SDHCI_S3C_PMOPS,     //电源管理相关                      
        },                                                           
};                                                                   

dt_match中的内容:

static const struct of_device_id sdhci_s3c_dt_match[] = {    
        { .compatible = "samsung,s3c6410-sdhci", },          
        { .compatible = "samsung,exynos4210-sdhci",          
                .data = (void *)EXYNOS4_SDHCI_DRV_DATA },    
        {},                                                  
};                                                           

当我们的设备树中有能够与dt_match的成员相匹配时,就会调用probe函数。这里以列举一下s3c64xx.dtsi中关于sdhci节点的内容:

sdhci0: sdhci@7c200000 {
        compatible = "samsung,s3c6410-sdhci";
        reg = <0x7c200000 0x100>;
        interrupt-parent = <&vic1>;
        interrupts = <24>;
        clock-names = "hsmmc", "mmc_busclk.0", "mmc_busclk.2";
        clocks = <&clocks HCLK_HSMMC0>, <&clocks HCLK_HSMMC0>,
                        <&clocks SCLK_MMC0>;
        status = "disabled";
};

可以看见设备树中指定了该控制器的寄存器地址、中断以及时钟相关的内容,并没有指定其引脚等内容,因为这些内容需要根据不同板级来进行配置。设备树的节点,会被转换为相应的device,这里的sdhci0在会被转换为一个platform_device,当加载相应的驱动后便会进行匹配从而调用驱动中的probe函数。

sdhci_s3c_probe函数分析

probe函数中完成的主要工作就是申请并添加host、中断注册、以及设备树内容的解析工作;当然这其中还有一些标志位的设置等。这里去掉了很多关于标志位设置,只有一些个人认为和流程相关的函数内容,并对其中的函数进行相应的分析。

static int sdhci_s3c_probe(struct platform_device *pdev)  
{
	host = sdhci_alloc_host(dev, sizeof(struct sdhci_s3c)); 
	......
	host->ops = &sdhci_s3c_ops;   //这里的ops是提供给sdhci.c调用的,需要结合源码进行阅读
	......
	ret = mmc_of_parse(host->mmc);                         
    ret = sdhci_add_host(host);                                                                          
}

1、很多芯片厂商使用的驱动都是结合sdhci.c来实现的,不同芯片厂商的功能设置操作可可能有所差异,因此sdhci.c就提供了一个统一的接来进行管理。这个ops中提供的函数在sdhci.c中的一些位置会进行调用。

host->ops = &sdhci_s3c_ops;
/*----------------------------------------------------------------------------*/
static struct sdhci_ops sdhci_s3c_ops = {                          
        .get_max_clock          = sdhci_s3c_get_max_clk,     //获取能够提供的最大的时钟频率      
        .set_clock              = sdhci_s3c_set_clock,        //设置sd卡时钟     
        .get_min_clock          = sdhci_s3c_get_min_clock,         
        .set_bus_width          = sdhci_s3c_set_bus_width,     //设置bus位宽 4 or 8bit
        .reset                  = sdhci_reset,                  
        .set_uhs_signaling      = sdhci_set_uhs_signaling,      //uhs 信号电平切换   
};  
/*-----------------------------------------------------------------------------*/

2、sdhci_alloc_host函数分析,此函数位于sdhci.c中

sdhci_alloc_host(dev, sizeof(struct sdhci_s3c))
	mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev);
	host->mmc_host_ops = sdhci_ops;	//硬件操作方法接口
	mmc->ops = &host->mmc_host_ops;
/*-------------------------------------------------------------------*/
//向core层提供操作方法  按照core提供的统一接口编写的驱动
static const struct mmc_host_ops sdhci_ops = {
        .request        = sdhci_request,
        .post_req       = sdhci_post_req,
        .pre_req        = sdhci_pre_req,
        .set_ios        = sdhci_set_ios,
        .get_cd         = sdhci_get_cd,
        .get_ro         = sdhci_get_ro,
        .hw_reset       = sdhci_hw_reset,
        .enable_sdio_irq = sdhci_enable_sdio_irq,
        .start_signal_voltage_switch    = sdhci_start_signal_voltage_switch,
        .prepare_hs400_tuning           = sdhci_prepare_hs400_tuning,
        .execute_tuning                 = sdhci_execute_tuning,
        .select_drive_strength          = sdhci_select_drive_strength,
        .card_event                     = sdhci_card_event,
        .card_busy      = sdhci_card_busy,
};
/*-------------------------------------------------------------------*/

mmc_alloc_host函数

struct mmc_host *mmc_alloc_host(int extra, struct device *dev)  
{
	.........
	mmc_gpio_alloc(host);            //与SD卡检测  cd引脚相关的数据接口建立                                                                    
	spin_lock_init(&host->lock);                       
	init_waitqueue_head(&host->wq);                    
	INIT_DELAYED_WORK(&host->detect, mmc_rescan);       //延迟工作队列   用于后续初始化完成后的SD卡的扫描检测
	..........
}

3、mmc_of_parse函数,core层对设备树内容的解析

int mmc_of_parse(struct mmc_host *host)  //对设备树中的内容进行解析  列举一些关键内容
{
	/* "bus-width" is translated to MMC_CAP_*_BIT_DATA flags */      
	if (of_property_read_u32(np, "bus-width", &bus_width) < 0) {     
			dev_dbg(host->parent,                                    
					"\"bus-width\" property is missing, assuming 1 bi
			bus_width = 1;                                           
	}                                                                
	switch (bus_width) {                     //bus_width设置                        
	case 8:                                                          
			host->caps |= MMC_CAP_8_BIT_DATA;                        
			/* Hosts capable of 8-bit transfers can also do 4 bits */
	case 4:                                                          
			host->caps |= MMC_CAP_4_BIT_DATA;                        
			break;                                                   
	case 1:                                                          
			break;                                                   
	default:                                                         
			dev_err(host->parent,                                    
					"Invalid \"bus-width\" value %u!\n", bus_width); 
			return -EINVAL;                                          
	}                                                                
	of_property_read_u32(np, "max-frequency", &host->f_max);  
	
	cd_cap_invert = of_property_read_bool(np, "cd-inverted");   //  cd引脚翻转 通常无卡为低电平,如果硬件设计为无卡高电平 则可以在设备树中进行指定
                                                         
	if (of_property_read_bool(np, "broken-cd"))              	//  在没有cd检测引脚的情况下,可以使用轮询方式进行检测,需要在设备树中指定
			host->caps |= MMC_CAP_NEEDS_POLL;                
                                                         
	ret = mmc_gpiod_request_cd(host, "cd", 0, true,          //获取cd检测引脚的gpio相关信息
							   0, &cd_gpio_invert);          
	if (of_property_read_bool(np, "cap-sd-highspeed"))            
			host->caps |= MMC_CAP_SD_HIGHSPEED;                   
	if (of_property_read_bool(np, "cap-mmc-highspeed"))           
			host->caps |= MMC_CAP_MMC_HIGHSPEED;                  
	if (of_property_read_bool(np, "sd-uhs-sdr12"))         	//指定支持哪些速度等级的卡       
			host->caps |= MMC_CAP_UHS_SDR12;                      
	if (of_property_read_bool(np, "sd-uhs-sdr25"))              //可结合源码进行详细了解  
			host->caps |= MMC_CAP_UHS_SDR25;                      
	if (of_property_read_bool(np, "sd-uhs-sdr50"))                
			host->caps |= MMC_CAP_UHS_SDR50;                      
	if (of_property_read_bool(np, "sd-uhs-sdr104"))               
			host->caps |= MMC_CAP_UHS_SDR104;                     
	if (of_property_read_bool(np, "sd-uhs-ddr50"))                
			host->caps |= MMC_CAP_UHS_DDR50;                                     
	if (of_property_read_bool(np, "cap-mmc-hw-reset"))        //指定是否支持硬件复位    
}
```c
data = pd.read_csv(
    'https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv')
print(data.head())

4、sdhci_add_host

int sdhci_add_host(struct sdhci_host *host)
{
	...........
	 sdhci_do_reset(host, SDHCI_RESET_ALL);
	 tasklet_init(&host->finish_tasklet,
     sdhci_tasklet_finish, (unsigned long)host);
	 setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);  //设置每次操作的超时定时器
	 init_waitqueue_head(&host->buf_ready_int);
	 sdhci_init(host, 0);  //初始化控制器
	..........
	mmc_add_host(mmc);		//

}

int mmc_add_host(struct mmc_host *host)
{
	........
	device_add(&host->class_dev);
	mmc_start_host(host);
	.......
}
void mmc_start_host(struct mmc_host *host)
{      
        host->f_init = max(freqs[0], host->f_min);
        host->rescan_disable = 0;
        host->ios.power_mode = MMC_POWER_UNDEFINED;

        mmc_claim_host(host);   //独占host
        if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)
                mmc_power_off(host);
        else
                mmc_power_up(host, host->ocr_avail);     
        mmc_release_host(host);

        mmc_gpiod_request_cd_irq(host);   //注册 cd 检测引脚的中断 并设置中断函数 关于SD卡检测处理  后续会有单独说明
        _mmc_detect_change(host, 0, false);  //  待前面的工作都完成后,就进行一次mmc的探测
}

总结

本文只针对mmc host驱动内容进行了简单的分析,在实际的开发过程一般进行控制控制器驱动开发的工作内容比较少,中除非是芯片设计原厂的驱动开发人员;但是作为芯片用户开发者,了解一下控制器驱动的整个流程会有助于我们对整个系统的理解和错误的调试。
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值