当前位置:   article > 正文

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

linux sd卡检测

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";
};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

中断处理函数的位置位于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;                                      
}   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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

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

那么该中断函数是在什么位置被注册的?
之前的分析,在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;                                               
}    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

关于中断检测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);                                                
	...........
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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);		//将初始化完成的设备,添加到内核中

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

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

总结

本文简单记录的SD卡的检测方式,这也是mmc子系统的检测流程,那些emmc sdio等的检测方式也差不多,只是命令和顺序上可能有一些不同。 SD卡检测的大概流程:SD卡接入--->触发中断--->调用mmc_rescan--->上电初始化--->添加设备--->实现相应的设备功能
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/寸_铁/article/detail/896174
推荐阅读
相关标签
  

闽ICP备14008679号