三,Linux SDIO驱动框架之扫卡流程

 本文以kernel-4.4,以MTK平台分析host端初始化的扫卡流程
 mtk平台host驱动代码在drivers/mmc/host/mtk-sdio.c,sd card,mmc和sdio card的相关核心代码都存在drivers/mmc/目录中,本文只关心host是如何扫描到card的,下面分析host扫卡流程。

1,host驱动初始化

 平台驱动代码中注册platform_driver,匹配到device调用到probe函数int msdc_drv_probe(struct platform_device *pdev),下面代码省略了很多host的初始化的细节,只截取了我们关心的部分:

static int msdc_drv_probe(struct platform_device *pdev) 
{
    ...
    在mmc core中分配一个mmc_host结构体
    mmc = mmc_alloc_host(sizeof(struct msdc_host), &pdev->dev);
    ...
    ret = mmc_add_host(mmc);
}

如上所示代码中主要调用两个接口来向mmc core注册host驱动

  • 调用 mmc_alloc_host用于分配一个struct mmc_host 结构体,在该函数中初始化了一个延时工作队列INIT_DELAYED_WORK(&host->detect, mmc_rescan);后续的扫卡工作主要是靠mmc_rescan来完成,这点在本文之后会分析。
  • mmc_add_host,主要的工作是初始化host,并对sdio总线上进行一次detect用于扫描总线的card
    下面对mmc_add_host进行分析
int mmc_add_host(struct mmc_host *host)
{
    int err;

    err = device_add(&host->class_dev);
    if (err)
        return err;

    mmc_start_host(host);

    return 0;                                                           
}


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);                         
    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);
    _mmc_detect_change(host, 0, false);  detect SDIO总线上是否存在card
}

void _mmc_detect_change(struct mmc_host *host, unsigned long delay,
                bool cd_irq)
{
    
    host->detect_change = 1;
    mmc_schedule_delayed_work(&host->detect, delay);
}
从中可以看出最终调用到的是在mmc_alloc_host中初始化的延迟工作队列host->detect,
所以mmc_rescan函数会被调用

从中我们总结出HOST在初始化阶段的detect 流程:
platform_driver probe接口msdc_drv_probe
 >mmc_alloc_host
  >> 初始化延迟工作队列host->detect,设置工作队列work func为mmc_rescan

 >mmc_start_host
  >>mmc_schedule_delayed_work(&host->detect, delay); 最终会调用到host->detect延迟工作队列work func mmc_rescan

2,mmc_rescan接口分析

 下面是具体的扫卡过程

void mmc_rescan(struct work_struct *work)
{

    mmc_claim_host(host);
    for (i = 0; i < ARRAY_SIZE(freqs); i++) {
        //unsigned freqs[] = { 400000, 300000, 200000, 100000 };扫卡频率
        if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
            break;
        if (freqs[i] <= host->f_min)
            break;
    }
}
scan card设置的频率是在400k到host支持的最低频率,逐个频率之间进行扫描
比如说400k频率扫卡失败了,接着进行300k的频率进行扫描,直到host支持的最低频率,或者100k为止

int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
    host->f_init = freq;

	这个power_up接口主要做两件事:
    1)根据DTS中mmc-pwrseq中的配置对card进行上电操作
    2)对sdio host controller进行上电,设置3.3或者1.8v信号电压
    mmc_power_up(host, host->ocr_avail);

    /*
     * Some eMMCs (with VCCQ always on) may not be reset after power up, so
     * do a hardware reset if possible.
     */
    mmc_hw_reset_for_init(host);
	
	在power up上电之后通过command对card进行复位设置初始状态
    发送CMD52 REG6 ,对card进行reset
    sdio_reset(host);
    发送CMD0 设置card为idle状态
    mmc_go_idle(host);
    发送CMD8 询问card是否支持host的电压域,如果不是memory card那么不回复
    mmc_send_if_cond(host, host->ocr_avail);

    //依次进行匹配是否是SDIO,SD card,MMC设备
    /* 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;
}

 我们只分析SDIO card,所有接下来分析mmc_attach_sdio(host)

int mmc_attach_sdio(struct mmc_host *host)
{
    int err, i, funcs;
    u32 ocr, rocr;
    struct mmc_card *card;

    获取sdio card的电压域
    err = mmc_send_io_op_cond(host, 0, &ocr);
    if (err)
        return err;

    mmc_attach_bus(host, &mmc_sdio_ops);
    if (host->ocr_avail_sdio)
        host->ocr_avail = host->ocr_avail_sdio;

    选择host和card都支持的电压rocr
    rocr = mmc_select_voltage(host, ocr);

   检测并初始化card
    err = mmc_sdio_init_card(host, rocr, NULL, 0);
    if (err)
        goto err;

    card = host->card;

    /*
     * The number of functions on the card is encoded inside
     * the ocr.
     */
    funcs = (ocr & 0x70000000) >> 28; 从OCR中获取card中的function number
    card->sdio_funcs = 0;

    /*
     * Initialize (but don't add) all present functions.
     */
	for (i = 0; i < funcs; i++, card->sdio_funcs++) {
     err = sdio_init_func(host->card, i + 1);
     if (err)
		goto remove;
	}

    //dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca);
    //调用device_add(&card->dev)
    err = mmc_add_card(host->card);
    if (err)                                               
        goto remove_added;

    /*
     * ...then the SDIO functions.
     */
    for (i = 0;i < funcs;i++) {
        //调用dev_set_name(&func->dev, "%s:%d", mmc_card_id(func->card), func->num);//设置name
        //调用device_add(&func->dev);
        err = sdio_add_func(host->card->sdio_func[i]);
        if (err)
            goto remove_added;
    }

    return err;
}

 这个函数比较长,拆分为几部分分析:

  1. mmc_send_io_op_cond通过CMD5,获取card支持的电压域,根据host所支持的电压域,得到host和card都支持的电压域rocr,并将rocr传入mmc_sdio_init_card。有关CMD的说明可以参考:二,sdio总线之cmd
  2. mmc_sdio_init_card,这个函数用于检测,并初始化card,从CCCR寄存器获取card支持的speed模式,bus width。从CIS Area中获取CIS 信息,比如vendor id,device id等。这个函数接下来会分析。
  3. 根据在CMD5 response中获取到的function number初始化每个function,,获取每个function的CIS信息,并为每个function分配一个device,设置bus type为sdio_bus_type,拥有匹配sdio_driver。
  4. mmc_add_card调用device_add(&card->dev),将card->dev添加到device框架中,如果成功扫到了card,会有这么一句打印:

mmc1: new ultra high speed SDR104 SDIO card at address 0001(从log中可以看出card支持UHS mode,支持速率为SDR104,RCA为1)

  1. sdio_add_func()将所有的function device调用device_add(&func->dev);添加到device架构中。

接着分析mmc_sdio_init_card函数

int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
                  struct mmc_card *oldcard, int powered_resume)
{
    如果host支持uhs模式,则switch信号电压为1.8v
    if (mmc_host_uhs(host))
        ocr |= R4_18V_PRESENT;
    
    设置card支持的电压为ocr,这个电压是mmc_sdio_init_card传入的rocr
    if (!powered_resume) {
    err = mmc_send_io_op_cond(host, ocr, &rocr);
     
    分配一个mmc_card结构体
    card = mmc_alloc_card(host, NULL);
    if (IS_ERR(card)) {
        err = PTR_ERR(card);
        goto err;
    }
    
    如果card存在memory部分,那么获取card的cid,并设置card类型是SD_COMBO card。
    if ((rocr & R4_MEMORY_PRESENT) &&
            mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) {
        card->type = MMC_TYPE_SD_COMBO;

        if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
            memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) {
            mmc_remove_card(card);
            return -ENOENT;
        }
    } else {
        如果没有memory部分,那么card类型为MMC_TYPE_SDIO card
        card->type = MMC_TYPE_SDIO;

        if (oldcard && oldcard->type != MMC_TYPE_SDIO) {
            mmc_remove_card(card);
            return -ENOENT;
        }
    }

   如果card支持UHS-I(rocr 中S18A=1,并且和host也支持UHS-I,那么进行信号电压电平切换为1.8v
    if (!powered_resume && (rocr & ocr & R4_18V_PRESENT)) {
        发送CMD11,然后进行电压切换操作
        err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180,
                    ocr_card);
        if (err == -EAGAIN) {
            sdio_reset(host);
            mmc_go_idle(host);
            mmc_send_if_cond(host, host->ocr_avail);
            mmc_remove_card(card);
            retries--;
            goto try_again;
        } else if (err) {
            ocr &= ~R4_18V_PRESENT;
        }
        err = 0;
    } else {
        ocr &= ~R4_18V_PRESENT;
    }

    /*
     * For native busses:  set card RCA and quit open drain mode.
     */
    if (!powered_resume && !mmc_host_is_spi(host)) {
        发送CMD3,获取RCA
        err = mmc_send_relative_addr(host, &card->rca);
        if (err)
            goto remove;
    }
    
    /*
    * Read CSD, before selecting the card
    */
    if (!oldcard && card->type == MMC_TYPE_SD_COMBO) {
        如果包含memory部分,则获取CSD
        err = mmc_sd_get_csd(host, card);
        if (err)
            return err;

        mmc_decode_cid(card);                         
    }

    /*
     * Select card, as all following commands rely on that.
     */
    if (!powered_resume && !mmc_host_is_spi(host)) {
        发送CMD7 select card
        err = mmc_select_card(card);                       
        if (err)
            goto remove;
    }  
	
	获取CCCR信息,下面会分析
    err = sdio_read_cccr(card,  ocr);
    if (err)
        goto remove;
	
	获取Common CIS信息,下面会分析
    err = sdio_read_common_cis(card);
    if (err)
        goto remove;

    card->ocr = ocr_card;        
    mmc_fixup_device(card, NULL);
    /*
     * If needed, disconnect card detection pull-up resistor.
     */
    err = sdio_disable_cd(card);
    if (err)
        goto remove;

    /* Initialization sequence for UHS-I cards */
    /* Only if card supports 1.8v and UHS signaling */
    设置card speed,wide
    if ((ocr & R4_18V_PRESENT) && card->sw_caps.sd3_bus_mode) 
        err = mmc_sdio_init_uhs_card(card);

}

CCCR(Card Common Control Registers)寄存器读取
int sdio_read_cccr(struct mmc_card *card, u32 ocr)
{
    int ret;
    int cccr_vsn;
    int uhs = ocr & R4_18V_PRESENT;
    unsigned char data;
    unsigned char speed;

    读取CCCR寄存器0x00,获取CCCR version(0-3bit)和sdio version(4-7bit)
    ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CCCR, 0, &data);
    if (ret)
        goto out;                                                      

    CCCR version (1.0 ,1.1, 2.0, 3.0)
    cccr_vsn = data & 0x0f;

    if (cccr_vsn > SDIO_CCCR_REV_3_00) {
        pr_err("%s: unrecognised CCCR structure version %d\n",
            mmc_hostname(card->host), cccr_vsn);
        return -EINVAL;
    }

    SDIO version(1.0 ,1.1, 1.2, 2.0, 3.0)
    card->cccr.sdio_vsn = (data & 0xf0) >> 4;

    获取card capability(地址0x08)
    ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CAPS, 0, &data);
    if (ret)
        goto out;

    是否支持多块传输,该标志表示card是否支持CMD53进行多块传输,如果该位为1则支持
    if (data & SDIO_CCCR_CAP_SMB)
        card->cccr.multi_block = 1;
    如果该位为1,则代表该卡为低速卡(Low-Speed Card),如果为0,则是全速卡(High Speed card)
    if (data & SDIO_CCCR_CAP_LSC)
        card->cccr.low_speed = 1;
    该标志代表Low -Speed Card是否支持4 bit模式,当LSC=1,并且支持4bit模式,该标志则为1
    if (data & SDIO_CCCR_CAP_4BLS) 
        card->cccr.wide_bus = 1;
        
    当CCCR version大于1.1
    if (cccr_vsn >= SDIO_CCCR_REV_1_10) {
        寄存器0x12
        ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_POWER, 0, &data);
        if (ret)
            goto out;
        //bit 0,These bits tell the host if the card supports Master Power Control.
        //SMPC=0: The total card power is up to 720mW (3.6Vx200mA), 
        //even if all functions are active (IOEx=1). EMPC, SPS and EPS shall be zero.
        //SMPC=1: The total card power may exceed 720mW (3.6Vx200mA). 
        //Controls of EMPC, SPS and EPS are available.
        if (data & SDIO_POWER_SMPC)
            card->cccr.high_power = 1;
    }

    如果cccr_version 大于等于2.0,读取speed寄存器0x13
    if (cccr_vsn >= SDIO_CCCR_REV_1_20) {
        //读取Bus Speed Select寄存器获取速度
        ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed);
        if (ret)
            goto out;

        card->scr.sda_spec3 = 0;
        card->sw_caps.sd3_bus_mode = 0;
        card->sw_caps.sd3_drv_type = 0;
        if (cccr_vsn >= SDIO_CCCR_REV_3_00 && uhs) {
            card->scr.sda_spec3 = 1;
            读取UHS-I Support寄存器(0x14)
            ret = mmc_io_rw_direct(card, 0, 0,
                SDIO_CCCR_UHS, 0, &data);

            host 是否支持uhs模式
            if (mmc_host_uhs(card->host)) {
                if (data & SDIO_UHS_DDR50)
                    card->sw_caps.sd3_bus_mode
                        |= SD_MODE_UHS_DDR50;//card支持 UHS_DDR50

                if (data & SDIO_UHS_SDR50)
                    card->sw_caps.sd3_bus_mode
                        |= SD_MODE_UHS_SDR50;//card支持 UHS_SDR50

                if (data & SDIO_UHS_SDR104)
                    card->sw_caps.sd3_bus_mode
                        |= SD_MODE_UHS_SDR104;//card支持UHS_SDR104
            }

            ret = mmc_io_rw_direct(card, 0, 0,
                SDIO_CCCR_DRIVE_STRENGTH, 0, &data);
            if (ret)
                goto out;

            if (data & SDIO_DRIVE_SDTA)
                card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_A;
            if (data & SDIO_DRIVE_SDTC)
                card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_C;
            if (data & SDIO_DRIVE_SDTD)
                card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_D;

            ret = mmc_io_rw_direct(card, 0, 0,
                 SDIO_CCCR_INTERRUPT_EXT, 0, &data);
            if (ret) {
                goto out;
            } else {
                //card是否支持同步中断
                if (data & SDIO_INTERRUPT_EXT_SAI) {
                    card->cccr.sai = 1;
                    data |= SDIO_INTERRUPT_EXT_EAI;//使能同步中断
                    ret = mmc_io_rw_direct(card, 1, 0,
                         SDIO_CCCR_INTERRUPT_EXT,
                         data, NULL);
           
                    ret = mmc_io_rw_direct(card, 0, 0,
                         SDIO_CCCR_INTERRUPT_EXT,
                         0, &data);
                    if (ret)
                        goto out;
                    else if (data & SDIO_INTERRUPT_EXT_EAI)
                        card->cccr.eai = 1;
                }
            }
        }

        /* if no uhs mode ensure we check for high speed */
        if (!card->sw_caps.sd3_bus_mode) {
            如果card 不支持uhs模式,则根据speed(寄存器0x13)判断是否支持high speed
            if (speed & SDIO_SPEED_SHS) {
                card->cccr.high_speed = 1;
                card->sw_caps.hs_max_dtr = 50000000; //host最大时钟频率为50Mhz
            } else {
                card->cccr.high_speed = 0;
                card->sw_caps.hs_max_dtr = 25000000;//host最大时钟频率为25Mhz
            }
        }
    }

    return ret;
}

Card Speed分类:
 是否支持UHS,OCR中S18A=1,则card支持,否则不支持,UHShost模式信号电压需要切换到1.8v,非UHS模式的信号电压为3.3V
11.8V信号电压UHS模式:
    信号电压1.8V时,UHS模式的选择是通过Bus Speed Select寄存器(0x13),BSS[2:0]进行选择,如果SHS=0,那么选择无效
BSS[2:0] Bus Speed  1.8V    Max Clock Frequency 
000b:                   SDR12              25MHz
001b:                   SDR25              50MHz
010b:                   SDR50              100MHz
011b:                   SDR104             208MHz
100b:                   DDR50              50MHz
101b-111b:        Reserved
2)3.3V信号电压模式
	只有当SHS=1时才能选择High Speed
BSS[2:0] Bus Speed 3.3V    Max. Clock Frequency
xx0b:             Default Speed         25MHz
xx1b:             High Speed             50MHz

//获取common cis信息
CCCR的地址区间为0x00 - 0xff
FBR地址区间为0xn00 - 0xnff (n从1到7)
int sdio_read_common_cis(struct mmc_card *card)
{
//获取common cis信息,获取的是common CIS 所以func设置为NULL
return sdio_read_cis(card, NULL);
}

static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
{                                                                      
    int ret;                                                           
    struct sdio_func_tuple *this, **prev;                              
    unsigned i, ptr = 0;                                               
                                                                       
    /*                                                                 
     * Note that this works for the common CIS (function number 0) as  
     * well as a function's CIS * since SDIO_CCCR_CIS and SDIO_FBR_CIS
     * have the same offset.                                           
     */                                                                
    for (i = 0; i < 3; i++) {                                          
        unsigned char x, fn;                                           
                                                                       
        if (func)                                                      
            fn = func->num;                                            
        else                                                           
            fn = 0;                                                    
        这个for循环主要做这么几个事情:
        1)在SDIO_FBR_CIS 寄存器中获取CIS信息所在的地址,如果是Common CIS则SDIO_FBR_BASE(fn)=0
        ret = mmc_io_rw_direct(card, 0, 0,                             
            SDIO_FBR_BASE(fn) + SDIO_FBR_CIS + i, 0, &x);
        if (ret)                                                       
            return ret;
        2)将得到的地址拼成一位3字节的地址,这个地址实际上是CIS Area中的地址
        ptr |= x << (i * 8);                                           
    }
   
                                                                   
    if (func)                                                          
        prev = &func->tuples;                                          
    else                                                               
        prev = &card->tuples;                                          
                                                                       
    BUG_ON(*prev);                                                     
    
    接下里的do循环中做这几件事情来解析CIS中的tuple数据:
    1)获取tuple的code码
    2)获取tuple的size大小tpl_link
    3)根据tpl_link大小读取这么多个字节的data
    4)根据tuple的code码,解析data
    do {                                                               
        unsigned char tpl_code, tpl_link;                              
        
        读取ptr地址中的数据,tuple数据的第一个字节代表tuple的code码                                                               
        ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_code);       
        if (ret)                                                       
            break;                                                     
                                                                       
        /* 0xff means we're done */    如果code码为0xff则退出                                
        if (tpl_code == 0xff)                                          
            break;                                                     
                                                                       
        /* null entries have no link field or data */                  
        if (tpl_code == 0x00)                                          
            continue;                                                  
        
        code码接下来是tuple的数据的大小
        ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link);       
        if (ret)                                                       
            break;                                                     
                                                                       
        /* a size of 0xff also means we're done */                     
        if (tpl_link == 0xff)                                          
            break;
        
        分配一个tuple结构体内存,其中tpl_link大小用来存储data                                                       
        this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL);  
        if (!this)                                             
            return -ENOMEM;                                    
        
        读取tpl_link个字节大小到data[i]
        for (i = 0; i < tpl_link; i++) {                       
            ret = mmc_io_rw_direct(card, 0, 0,                 
                           ptr + i, 0, &this->data[i]);        
            if (ret)                                           
                break;                                         
        }                                                      
        if (ret) {                                             
            kfree(this);                                       
            break;                                             
        }                                                      
        
        根据tuple code,对data数据进行解析
        /* Try to parse the CIS tuple */
        ret = cis_tpl_parse(card, func, "CIS",
                    cis_tpl_list, ARRAY_SIZE(cis_tpl_list),
                    tpl_code, this->data, tpl_link);           
        if (ret == -EILSEQ || ret == -ENOENT) {                
            /*                                                 
             * The tuple is unknown or known but not parsed.   
             * Queue the tuple for the function driver.        
             */                                                
            this->next = NULL;                                 
            this->code = tpl_code;                             
            this->size = tpl_link;                             
            *prev = this;                                      
            prev = &this->next;                                
                                                               
            if (ret == -ENOENT) {                              
                /* warn about unknown tuples */                
                pr_warn_ratelimited("%s: queuing unknown"      
                       " CIS tuple 0x%02x (%u bytes)\n",       
                       mmc_hostname(card->host),               
                       tpl_code, tpl_link);                    
            }                                                  
                                                               
            /* keep on analyzing tuples */                     
            ret = 0;                                           
        } else {                                               
            /*                                                 
             * We don't need the tuple anymore if it was       
             * successfully parsed by the SDIO core or if it is
             * not going to be queued for a driver.            
             */                                                
            kfree(this);                                       
        }                                                      
                                                               
        ptr += tpl_link;                                       
    } while (!ret);                                            
                                                               
    /* 
     * Link in all unknown tuples found in the common CIS so that
     * drivers don't have to go digging in two places.
     */
    if (func)
        *prev = card->tuples;
                                                                 
    return ret;
}

分析一下这个函数的实现cis_tpl_parse
static int cis_tpl_parse(struct mmc_card *card, struct sdio_func *func,
             const char *tpl_descr,
             const struct cis_tpl *tpl, int tpl_count,
             unsigned char code,
             const unsigned char *buf, unsigned size)
{
    int i, ret;

    /* look for a matching code in the table */
    for (i = 0; i < tpl_count; i++, tpl++) {
        if (tpl->code == code) /根据code知道对应的tuple解析函数
            break;
    }
    if (i < tpl_count) {
        if (size >= tpl->min_size) {
            if (tpl->parse)                                            
                ret = tpl->parse(card, func, buf, size);调用tuple解析函数
            else
                ret = -EILSEQ;  /* known tuple, not parsed */
        } else {
            /* invalid tuple */
            ret = -EINVAL;
        }
        if (ret && ret != -EILSEQ && ret != -ENOENT) {
            pr_err("%s: bad %s tuple 0x%02x (%u bytes)\n",
                   mmc_hostname(card->host), tpl_descr, code, size);
        }
    } else {
        /* unknown tuple */
        ret = -ENOENT;
    }

    return ret;
}

        我们在cis_tpl_parse中传进去的tpl参数实际上是cis_tpl_list,
       	结构体第一个成员是tuple code,第二个成员是tuple数据data的长度,第三个成员是解析
        static const struct cis_tpl cis_tpl_list[] = {
             {   0x15,   3,  cistpl_vers_1       },    
             {   0x20,   4,  cistpl_manfid       },    
             {   0x21,   2,  /* cistpl_funcid */ },    
             {   0x22,   0,  cistpl_funce        },    
        };
		tuple code 0x20对应的tuple信息实际上是vendor id,devie id,解析函数如下
        int cistpl_manfid(struct mmc_card *card, struct sdio_func *func,
             const unsigned char *buf, unsigned size)                  
         {                                                                      
             unsigned int vendor, device;                                       
                                                                       
            /* TPLMID_MANF */                                                  
            vendor = buf[0] | (buf[1] << 8);                                   
                                                                       
            /* TPLMID_CARD */                                                  
            device = buf[2] | (buf[3] << 8);                                   
                                                                       
            if (func) {                                                        
                func->vendor = vendor;                                         
                func->device = device;                                         
            } else {                                                           
                card->cis.vendor = vendor;                                     
                card->cis.device = device;                                     
            }                                                                  
                                                                       
            return 0;                                                          
         }

4,sdio_driver匹配原则

为了弄清sdio bus 的match原则,我们先分析在什么地方设置dev的bus type
在mmc_sdio_init_card函数中,sdio_alloc_func为function分配一个sdio_func结构体,还初始化了function的device

struct sdio_func *sdio_alloc_func(struct mmc_card *card)                
{                                                                       
    struct sdio_func *func;                                             
                                                                        
    func = kzalloc(sizeof(struct sdio_func), GFP_KERNEL);               
    if (!func)                                                          
        return ERR_PTR(-ENOMEM);                                        
                                                                        
    /*                                                                  
     * allocate buffer separately to make sure it's properly aligned for
     * DMA usage (incl. 64 bit DMA)                                     
     */                                                                 
    func->tmpbuf = kmalloc(4, GFP_KERNEL);                              
    if (!func->tmpbuf) {                                                
        kfree(func);                                                    
        return ERR_PTR(-ENOMEM);                                        
    }                                                                   
                                                                        
    func->card = card;                                                  
    //初始化func的dev                                                                    
    device_initialize(&func->dev);                                      
                                                                        
    func->dev.parent = &card->dev;                                      
    func->dev.bus = &sdio_bus_type;	设置func->dev的bus type
    func->dev.release = sdio_release_func;                              
                                                                        
    return func;                                                        
}

值得注意的是sdio_bus_type的match函数
static struct bus_type sdio_bus_type = {
    .name       = "sdio",                
    .dev_groups = sdio_dev_groups,       
    .match      = sdio_bus_match,        
    .uevent     = sdio_bus_uevent,       
    .probe      = sdio_bus_probe,        
    .remove     = sdio_bus_remove,       
    .pm     = &sdio_bus_pm_ops,          
};

static int sdio_bus_match(struct device *dev, struct device_driver *drv)
{                                                                       
    struct sdio_func *func = dev_to_sdio_func(dev);                     
    struct sdio_driver *sdrv = to_sdio_driver(drv);                     
                                                                        
    if (sdio_match_device(func, sdrv))                                  
        return 1;                                                       
                                                                        
    return 0;                                                           
}

static const struct sdio_device_id *sdio_match_device(struct sdio_func *func,
    struct sdio_driver *sdrv)                                                
{                                                                            
    const struct sdio_device_id *ids;                                        
                                                                             
    ids = sdrv->id_table;                                                    
                                                                             
    if (ids) {                                                               
        while (ids->class || ids->vendor || ids->device) {                   
            if (sdio_match_one(func, ids))                                   
                return ids;                                                  
            ids++;                                                           
        }                                                                    
    }                                                                        
                                                                             
    return NULL;                                                             
}
static const struct sdio_device_id *sdio_match_one(struct sdio_func *func,
    const struct sdio_device_id *id)                                      
{                                                                         
    if (id->class != (__u8)SDIO_ANY_ID && id->class != func->class)       
        return NULL;                                                      
    if (id->vendor != (__u16)SDIO_ANY_ID && id->vendor != func->vendor)   
        return NULL;                                                      
    if (id->device != (__u16)SDIO_ANY_ID && id->device != func->device)   
        return NULL;                                                      
    return id;                                                            
}    

可以看出匹配规则为通过driver id和func比较vendor和device来确定是否匹配device和driver
从上面可以看出sdio_driver通过driver id匹配的device实际上是function的device

4,sdio device之间的关系图

sdio device关系
card device的bus type和func device的bus type是有区别的
1)card device的bus类型为mmc_bus_type
static struct bus_type mmc_bus_type = {
.name = “mmc”,
.dev_groups = mmc_dev_groups,
.match = mmc_bus_match,
.uevent = mmc_bus_uevent,
.probe = mmc_bus_probe,
.remove = mmc_bus_remove,
.shutdown = mmc_bus_shutdown,
.pm = &mmc_bus_pm_ops,
};
2)func device的bus类型为sdio_bus_type

5,sdio 扫卡小结

1)scan card设置的频率是在400k到host支持的最低频率,逐个频率之间进行扫描,直到扫描到card为止
2)获取card支持的电压ocr域,是否支持UHS,以及card支持的function num
3)card支持uhs,则对电压电平进行切换
4)读取card的CCCR和CIS寄存器信息,其中CCCR信息包括了sdio version,bus wide以及card支持的速率,CIS信息包括vendor,device id等
5)调用sdio_alloc_func 为每一func分配一个sdio_func结构体,并通过device_initialize初始化func->dev
6)调用sdio_add_func 通过device_add添加每个fun的dev,用于匹配sdio_driver
  • 7
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值