Linux MMC子系统分析(三)——Card的检测方式

Linux MMC子系统分析(三)——Card检测方式分析


前言

前面对host驱动内容进行简单的学习分析,现在开始对SD卡的添加检测进行简单的学习和分析。

Linux内核中SD卡的检测方式

1、轮询方式 ,通过在设备树中指定 broken-cd ,便可使用轮询方式来检测SD卡的插拔检测 适用于没有中断引脚的情况下。这种方式不做过多说明。 2、中断检测方式,通过在设备树中指定cd-gpios,即可使用中断方式来检测。
&sdhci_1 {
        pinctrl-names = "default";
        pinctrl-0 = <&sd0_clk>, <&sd0_cmd>,
                        <&sd0_bus1>, <&sd0_bus4>;
        bus-width = <4>;
        cd-gpios = <&gpf 1 0>;	//当在设备树中指定了该引脚,相应的内核就会自行的注册想相应的中断处理函数
        cd-inverted;
        status = "okay";
};

中断处理函数的位置位于core/slot-gpio.c,默认提供的中断函数:

static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)       
{                                                                
        /* Schedule a card detection after a debounce timeout */ 
        struct mmc_host *host = dev_id;                          
        
        host->trigger_card_event = true;
        mmc_detect_change(host, msecs_to_jiffies(200));  //最终会调用到工 mmc_rescan,

        return IRQ_HANDLED;                                      
}   

当然,如果你需要在中断函数中引入其他功能,你也可以手动指定中断函数,该文件中同样提供了设置中断函数的接口:

void mmc_gpio_set_cd_isr(struct mmc_host *host, 
						irqreturn_t (*isr)(int irq, void *dev_id))   

那么该中断函数是在什么位置被注册的?
之前的分析,在mmc_start_host函数中调用了 mmc_gpiod_request_cd_irq,可以看一下该函数中的内容

void mmc_gpiod_request_cd_irq(struct mmc_host *host)                                            
{                                                                                               
	..........
        irq = gpiod_to_irq(ctx->cd_gpio);          
		.................
        if (irq >= 0) {                                                                         
                if (!ctx->cd_gpio_isr)             //如果没有设置自定义的中断处理函数,就使用默认的                                             
                        ctx->cd_gpio_isr = mmc_gpio_cd_irqt;                                    
                ret = devm_request_threaded_irq(host->parent, irq,                              
                        NULL, ctx->cd_gpio_isr,                                                 
                        IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,              
                        ctx->cd_label, host);   //注册中断函数                                                  
                if (ret < 0)                                                                    
                        irq = ret;                                                              
        }                                                                                       
                                                                                                
        host->slot.cd_irq = irq;                                                                
                                                                                                
        if (irq < 0)                                                                            
                host->caps |= MMC_CAP_NEEDS_POLL;                                               
}    

关于中断检测SD卡的方式大概就是这么多,更加细节的内容还需要细读内核源码。

下面来分析 mmc_rescan函数 该函数位于 core/core.c中
该函数是探测SD卡的重要函数

void mmc_rescan(struct work_struct *work)
{
	..........
	mmc_claim_host(host);                                                  
	for (i = 0; i < ARRAY_SIZE(freqs); i++) {                              
			if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))    //依次使用 400K 300 200 100KHz的时钟对SD卡或eMMC进行初始化操作
					break;                                                 
			if (freqs[i] <= host->f_min)                                   
					break;                                                 
	}                                                                      
	mmc_release_host(host);                                                
	...........
}

mmc_rescan_try_freq函数

static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) 
{
	mc_power_up(host, host->ocr_avail);	//打开供电 这里会涉及到电平切换的问题 SD卡在初始化时,需要在3.3V的条件下进行,初始化完成后高速卡是在1.8v条件下工作
	mmc_hw_reset_for_init(host);		//硬件复位,需要在设备树中使能,并在驱动中实现相应函数
	sdio_reset(host);
	mmc_go_idle(host);		// cmd0

	mmc_send_if_cond(host, host->ocr_avail);

	/* Order's important: probe SDIO, then SD, then MMC */
	if (!mmc_attach_sdio(host))
			return 0;
	if (!mmc_attach_sd(host))
			return 0;
	if (!mmc_attach_mmc(host))
			return 0;

	mmc_power_off(host);
	return -EIO;
}

void mmc_power_up(struct mmc_host *host, u32 ocr)                                        
{                                                                                        
        if (host->ios.power_mode == MMC_POWER_ON)                                        
                return;                                                                                                                                               
        mmc_pwrseq_pre_power_on(host);                                                                                                                                  
        host->ios.vdd = fls(ocr) - 1;                                                    
        host->ios.power_mode = MMC_POWER_UP;                                             
        /* Set initial state and call mmc_set_ios */                                     
        mmc_set_initial_state(host);                                                     
                                                                                         
        /* Try to set signal voltage to 3.3V but fall back to 1.8v or 1.2v */      //尝试将电平设置为3.3V,因为根据协议说明,在初始化时需要在3.3v条件下进行       
        if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330) == 0)      //会进行如下调用host->ops->start_signal_voltage_switch(host, &host->ios);
                dev_dbg(mmc_dev(host), "Initial signal voltage of 3.3v\n");              
        else if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180) == 0)            
                dev_dbg(mmc_dev(host), "Initial signal voltage of 1.8v\n");              
        else if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120) == 0)            
                dev_dbg(mmc_dev(host), "Initial signal voltage of 1.2v\n");              
                                                                                         
        /*                                                                               
         * This delay should be sufficient to allow the power supply                     
         * to reach the minimum voltage.                                                 
         */                                                                              
        mmc_delay(10);                                                                                                                                                  
        mmc_pwrseq_post_power_on(host);                                                                                                                                   
        host->ios.clock = host->f_init;                                                                                                                                           
        host->ios.power_mode = MMC_POWER_ON;                                             
        mmc_set_ios(host);                                                                                                                                                    
        /*                                                                               
         * This delay must be at least 74 clock sizes, or 1 ms, or the                   
         * time required to reach a stable voltage.                                      
         */                                                                              
        mmc_delay(10);            //上电完成后,需要等待74个clk                                                       int mmc_attach_sd(struct mmc_host *host) 
{
	......
	/*                                          
	* Detect and init the card.                
	*/                                         
	err = mmc_sd_init_card(host, rocr, NULL);    //根据SD协议来初始化设备

	err = mmc_add_card(host->card);		//将初始化完成的设备,添加到内核中

}

mmc_add_card(host->card);执行后,会mmc_bus_probe函数  函数位于core/bus.c中
在该函数中会直接调用mmc_driver中的probe函数,将SD卡实现为块设备。

总结

本文简单记录的SD卡的检测方式,这也是mmc子系统的检测流程,那些emmc sdio等的检测方式也差不多,只是命令和顺序上可能有一些不同。 SD卡检测的大概流程:SD卡接入--->触发中断--->调用mmc_rescan--->上电初始化--->添加设备--->实现相应的设备功能
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值