Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1147421
  • 博文数量: 241
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 2279
  • 用 户 组: 普通用户
  • 注册时间: 2012-11-27 19:53
个人简介

JustForFun

文章分类

全部博文(241)

文章存档

2023年(8)

2022年(2)

2021年(3)

2020年(30)

2019年(11)

2018年(27)

2017年(54)

2016年(83)

2015年(23)

我的朋友

分类: LINUX

2016-11-07 15:07:48


参考原文http://blog.csdn.net/zoe6553/article/details/6588306
图片

///
第一条线    mmc子系统核心初始化
         drivers/mmc/core/core.c
//子系统初始化 subsys_initcall(mmc_init);
第二条线 mmc控制器平台驱动注册

   
      
module_init(sdhci_drv_init);
第三条线  磁盘设备驱动注册
drivers/mmc/card/block.c
module_init(mmc_blk_init);

图片

图片


///////////////////////////////////////////////////////////////////////////////////////////////////

static struct platform_driver sdhci_s3c_driver = {
    .probe        = sdhci_s3c_probe,
    .remove        = __devexit_p(sdhci_s3c_remove),
        .suspend        = sdhci_s3c_suspend,
        .resume         = sdhci_s3c_resume,
    .driver        = {
        .owner    = THIS_MODULE,
//platform_device 定义分布定义在
//    s3c_device_hsmmc1  .name        = "s3c-sdhci",
        .name    = "s3c-sdhci",     
    },
};

static int __init sdhci_s3c_init(void)
{
    return platform_driver_register(&sdhci_s3c_driver);
}
module_init(sdhci_s3c_init);
///////////////////////////////////////////////////////////////////


struct sdhci_s3c {

//新创建的host控制器
 
    struct sdhci_host    *host;
    struct platform_device    *pdev;
    struct resource        *ioarea;
    struct s3c_sdhci_platdata *pdata;
    unsigned int        cur_clk;

    struct clk        *clk_io;    /* clock for io bus */
    struct clk        *clk_bus[MAX_BUS_CLK];
};
///////////////////////////////////////////////


static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
{
    struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data;
    struct device *dev = &pdev->dev;
    struct sdhci_host *host;
    struct sdhci_s3c *sc;
    struct resource *res;
    int ret, irq, ptr, clks;

    if (!pdata) {
        dev_err(dev, "no device data specified\n");
        return -ENOENT;
    }


//platform_get_irq()调用platform_get_resource, 会返回一个start, 即可用的中断号。
    irq = platform_get_irq(pdev, 0);
    if (irq < 0) {
        dev_err(dev, "no irq specified\n");
        return irq;
    }
//
获取IO内存资源
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res) {
        dev_err(dev, "no memory specified\n");
        return -ENOENT;
    }
//
host就是通过sdhci_alloc_host函数来构造出来的 
//也分配了sdhci_s3c结构体内存空间 
//
sdhci_alloc_host分配了sdhci_host和mmc_host两个结构体,并将sdhci_host的mmc成员指向mmc_host  
host = sdhci_alloc_host(dev, sizeof(struct sdhci_s3c));
    if (IS_ERR(host)) {
        dev_err(dev, "sdhci_alloc_host() failed\n");
        return PTR_ERR(host);
    }

//sc = host->private;
//
host->private已经被sdhci_alloc_host函数alloc了额外的sizeof(struct sdhci_s3c)给private成员    sc = sdhci_priv(host);

        platform_set_drvdata(pdev, host);

    sc->host = host;
    sc->pdev = pdev;
    sc->pdata = pdata;

    sc->clk_io = clk_get(dev, "hsmmc");
    if (IS_ERR(sc->clk_io)) {
        dev_err(dev, "failed to get io clock\n");
        ret = PTR_ERR(sc->clk_io);
        goto err_io_clk;
    }

    /* enable the local io clock and keep it running for the moment. */
    clk_enable(sc->clk_io);

    for (clks = 0, ptr = 0; ptr < MAX_BUS_CLK; ptr++) {
        struct clk *clk;
        char *name = pdata->clocks[ptr];

        if (name == NULL)
            continue;

        clk = clk_get(dev, name);
        if (IS_ERR(clk)) {
            dev_err(dev, "failed to get clock %s\n", name);
            continue;
        }

        clks++;
        sc->clk_bus[ptr] = clk;
        clk_enable(clk);

        dev_info(dev, "clock source %d: %s (%ld Hz)\n",
             ptr, name, clk_get_rate(clk));
    }

    if (clks == 0) {
        dev_err(dev, "failed to find any bus clocks\n");
        ret = -ENOENT;
        goto err_no_busclks;
    }
//
申请I/O内存的函数
 
    sc->ioarea = request_mem_region(res->start, resource_size(res),
                    mmc_hostname(host->mmc));
    if (!sc->ioarea) {
        dev_err(dev, "failed to reserve register area\n");
        ret = -ENXIO;
        goto err_req_regs;
    }
//
把内存映射到CPU空间
 
//
这个版本的ioremap确保这些内存在CPU是不可缓冲的
 
    host->ioaddr = ioremap_nocache(res->start, resource_size(res));
    if (!host->ioaddr) {
        dev_err(dev, "failed to map registers\n");
        ret = -ENXIO;
        goto err_req_regs;
    }

    /* Ensure we have minimal gpio selected CMD/CLK/Detect */
    if (pdata->cfg_gpio)
        pdata->cfg_gpio(pdev, 0);

    sdhci_s3c_check_sclk(host);

    host->hw_name = "samsung-hsmmc";
    host->ops = &sdhci_s3c_ops;
    host->quirks = 0;
    host->irq = irq;

    /* Setup quirks for the controller */

    host->flags = SDHCI_USE_DMA;

    /* It seems we do not get an DATA transfer complete on non-busy
     * transfers, not sure if this is a problem with this specific
     * SDHCI block, or a missing configuration that needs to be set. */
    host->quirks |= SDHCI_QUIRK_NO_TCIRQ_ON_NOT_BUSY;

    host->quirks |= (SDHCI_QUIRK_32BIT_DMA_ADDR |
             SDHCI_QUIRK_32BIT_DMA_SIZE);

    if (pdata->host_caps)
        host->mmc->caps = pdata->host_caps;
    else
        host->mmc->caps = 0;

    /* to add external irq as a card detect signal */
    printk("[SDHCI]to add external irq as a card detect signal......\n");
    if (pdata->cfg_ext_cd) {
    printk("[SDHCI]if (pdata->cfg_ext_cd)......\n");
        pdata->cfg_ext_cd();
        if (pdata->detect_ext_cd())
            host->flags |= SDHCI_DEVICE_ALIVE;
    }
//
通过sdhci_add_host函数来注册sdhci_host
//
sdhci_add_host调用了mmc_add_host将这个控制器设备添加到内核中
//
将host添加入mmc管理,其本质即是添加host的class_dev
 
 
    ret = sdhci_add_host(host);
    if (ret) {
        dev_err(dev, "sdhci_add_host() failed\n");
        goto err_add_host;
    }

    /* register external irq here (after all init is done) */
    if (pdata->cfg_ext_cd) {
    printk("[SDHCI]request_irq......\n");
        ret = request_irq(pdata->ext_cd, sdhci_irq_cd,
                IRQF_SHARED, mmc_hostname(host->mmc), sc);
    }

    return 0;

 err_add_host:
    release_resource(sc->ioarea);
    kfree(sc->ioarea);

 err_req_regs:
    for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) {
        clk_disable(sc->clk_bus[ptr]);
        clk_put(sc->clk_bus[ptr]);
    }

 err_no_busclks:
    clk_disable(sc->clk_io);
    clk_put(sc->clk_io);

 err_io_clk:
    sdhci_free_host(host);

    return ret;
}
///////////////////////////////////////////////////////////////////

int sdhci_add_host(struct sdhci_host *host)
{
    struct mmc_host *mmc;
    unsigned int caps;
    int ret;

    WARN_ON(host == NULL);
    if (host == NULL)
        return -EINVAL;

    mmc = host->mmc;

    if (debug_quirks)
        host->quirks = debug_quirks;

    sdhci_reset(host, SDHCI_RESET_ALL);

    host->version = readw(host->ioaddr + SDHCI_HOST_VERSION);
    host->version = (host->version & SDHCI_SPEC_VER_MASK)
                >> SDHCI_SPEC_VER_SHIFT;
    if (host->version > SDHCI_SPEC_200) {
        printk(KERN_ERR "%s: Unknown controller version (%d). "
            "You may experience problems.\n", mmc_hostname(mmc),
            host->version);
    }
图片
//#define SDHCI_CAPABILITIES    0x40
    caps = readl(host->ioaddr + SDHCI_CAPABILITIES);

    if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
        host->flags |= SDHCI_USE_DMA;
    else if (!(caps & SDHCI_CAN_DO_DMA))
        DBG("Controller doesn't have DMA capability\n");
    else
        host->flags |= SDHCI_USE_DMA;

    if ((host->quirks & SDHCI_QUIRK_BROKEN_DMA) &&
        (host->flags & SDHCI_USE_DMA)) {
        DBG("Disabling DMA as it is marked broken\n");
        host->flags &= ~SDHCI_USE_DMA;
    }

    if (host->flags & SDHCI_USE_DMA) {
        if ((host->version >= SDHCI_SPEC_200) &&
                (caps & SDHCI_CAN_DO_ADMA2))
            host->flags |= SDHCI_USE_ADMA;
    }

    if ((host->quirks & SDHCI_QUIRK_BROKEN_ADMA) &&
        (host->flags & SDHCI_USE_ADMA)) {
        DBG("Disabling ADMA as it is marked broken\n");
        host->flags &= ~SDHCI_USE_ADMA;
    }

    if (host->flags & SDHCI_USE_DMA) {
        if (host->ops->enable_dma) {
            if (host->ops->enable_dma(host)) {
                printk(KERN_WARNING "%s: No suitable DMA "
                    "available. Falling back to PIO.\n",
                    mmc_hostname(mmc));
                host->flags &= ~(SDHCI_USE_DMA | SDHCI_USE_ADMA);
            }
        }
    }

    if (host->flags & SDHCI_USE_ADMA) {
        /*
         * We need to allocate descriptors for all sg entries
         * (128) and potentially one alignment transfer for
         * each of those entries.
         */
        host->adma_desc = kmalloc((128 * 2 + 1) * 4, GFP_KERNEL);
        host->align_buffer = kmalloc(128 * 4, GFP_KERNEL);
        if (!host->adma_desc || !host->align_buffer) {
            kfree(host->adma_desc);
            kfree(host->align_buffer);
            printk(KERN_WARNING "%s: Unable to allocate ADMA "
                "buffers. Falling back to standard DMA.\n",
                mmc_hostname(mmc));
            host->flags &= ~SDHCI_USE_ADMA;
        }
    }

    /*
     * If we use DMA, then it's up to the caller to set the DMA
     * mask, but PIO does not need the hw shim so we set a new
     * mask here in that case.
     */
    if (!(host->flags & SDHCI_USE_DMA)) {
        host->dma_mask = DMA_BIT_MASK(64);
        mmc_dev(host->mmc)->dma_mask = &host->dma_mask;
    }

    if (host->ops->get_max_clock)
        host->max_clk = host->ops->get_max_clock(host);
    else {
        host->max_clk =    (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
        host->max_clk *= 1000000;
    }
    if (host->max_clk == 0) {
        printk(KERN_ERR "%s: Hardware doesn't specify base clock "
            "frequency.\n", mmc_hostname(mmc));
        return -ENODEV;
    }

    if (host->ops->get_timeout_clock)
        host->timeout_clk = host->ops->get_timeout_clock(host);
    else
        host->timeout_clk =
            (caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
    if (host->timeout_clk == 0) {
        printk(KERN_ERR "%s: Hardware doesn't specify timeout clock "
            "frequency.\n", mmc_hostname(mmc));
        return -ENODEV;
    }
    if (caps & SDHCI_TIMEOUT_CLK_UNIT)
        host->timeout_clk *= 1000;

    /*
     * Set host parameters.
     */
    mmc->ops = &sdhci_ops;
    mmc->f_min = host->max_clk / 256;
    mmc->f_max = host->max_clk;
#if defined(CONFIG_MMC_SDHCI_S3C) || defined(CONFIG_MMC_SDHCI_MODULE)
    mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
#else
    mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
#endif
    if ((caps & SDHCI_CAN_DO_HISPD) ||
        (host->quirks & SDHCI_QUIRK_FORCE_HIGHSPEED))
        mmc->caps |= (MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED);

    mmc->ocr_avail = 0;
    if (caps & SDHCI_CAN_VDD_330)
        mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34;
    if (caps & SDHCI_CAN_VDD_300)
        mmc->ocr_avail |= MMC_VDD_29_30|MMC_VDD_30_31;
    if (caps & SDHCI_CAN_VDD_180)
        mmc->ocr_avail |= MMC_VDD_165_195;

    if (mmc->ocr_avail == 0) {
        printk(KERN_ERR "%s: Hardware doesn't report any "
            "support voltages.\n", mmc_hostname(mmc));
        return -ENODEV;
    }

    spin_lock_init(&host->lock);

    /*
     * Maximum number of segments. Depends on if the hardware
     * can do scatter/gather or not.
     */
    if (host->flags & SDHCI_USE_ADMA)
        mmc->max_hw_segs = 128;
    else if (host->flags & SDHCI_USE_DMA)
        mmc->max_hw_segs = 1;
    else /* PIO */
        mmc->max_hw_segs = 128;
    mmc->max_phys_segs = 128;

    /*
     * Maximum number of sectors in one transfer. Limited by DMA boundary
     * size (512KiB).
     */
    mmc->max_req_size = 524288;

    /*
     * Maximum segment size. Could be one segment with the maximum number
     * of bytes. When doing hardware scatter/gather, each entry cannot
     * be larger than 64 KiB though.
     */
    if (host->flags & SDHCI_USE_ADMA)
        mmc->max_seg_size = 65536;
    else
        mmc->max_seg_size = mmc->max_req_size;

    /*
     * Maximum block size. This varies from controller to controller and
     * is specified in the capabilities register.
     */
    mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT;
    if (mmc->max_blk_size >= 3) {
        printk(KERN_WARNING "%s: Invalid maximum block size, "
            "assuming 512 bytes\n", mmc_hostname(mmc));
        mmc->max_blk_size = 512;
    } else
        mmc->max_blk_size = 512 << mmc->max_blk_size;

    /*
     * Maximum block count.
     */
    mmc->max_blk_count = 65535;

    /*
     * Init tasklets.
     */
//
card_tasklet用于处理MMC插槽上的状态变化
    tasklet_init(&host->card_tasklet,
        sdhci_tasklet_card, (unsigned long)host);
//
finish_tasklet用于命令传输完成后的处理
    tasklet_init(&host->finish_tasklet,
        sdhci_tasklet_finish, (unsigned long)host);
//
Timer用于等待硬件中断
//命令发送超时的相关处理
    setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);

//中断注册函request_irq的第一个参数中断号就取自于s3c_device_hsmmc3.resource里面的irq参数,
//sdhci_irq就是中断服务程序
   
该中断函数一般在插卡、拔卡或者从设备反馈给host信息时会被调用
//命令数据收发中断的处理
 
//见

ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
        mmc_hostname(mmc), host);
    if (ret)
        goto untasklet;
//使能SD 中断寄存器 和中断信号寄存器
    sdhci_init(host);

#ifdef CONFIG_MMC_DEBUG
    sdhci_dumpregs(host);
#endif

#ifdef CONFIG_LEDS_CLASS
    host->led.name = mmc_hostname(mmc);
    host->led.brightness = LED_OFF;
    host->led.default_trigger = mmc_hostname(mmc);
    host->led.brightness_set = sdhci_led_control;

    ret = led_classdev_register(mmc_dev(mmc), &host->led);
    if (ret)
        goto reset;
#endif

    mmiowb();

    mmc_add_host(mmc);

    printk(KERN_INFO "%s: SDHCI controller on %s [%s] using %s%s\n",
        mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
        (host->flags & SDHCI_USE_ADMA)?"A":"",
        (host->flags & SDHCI_USE_DMA)?"DMA":"PIO");

    return 0;

#ifdef CONFIG_LEDS_CLASS
reset:
    sdhci_reset(host, SDHCI_RESET_ALL);
    free_irq(host->irq, host);
#endif
untasklet:
    tasklet_kill(&host->card_tasklet);
    tasklet_kill(&host->finish_tasklet);

    return ret;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


int sdhci_add_host(struct sdhci_host *host)

    mmc->ops = &sdhci_ops;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


 
static const struct mmc_host_ops sdhci_ops = {
//
request函数指针指向的函数用来处理host向从设备发送命令的请求,
 
    .request    = sdhci_request,
//
set_ios用来设置电源、时钟等等之类(需要重点关注),   
    .set_ios    = sdhci_set_ios,
//
get_ro用来判断是否写保护 
    .get_ro        = sdhci_get_ro,
//使能SD/MMC IRQ中断
    .enable_sdio_irq = sdhci_enable_sdio_irq,
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//中断注册函request_irq的第一个参数中断号就取自于s3c_device_hsmmc3.resource里面的irq参数,
//sdhci_irq就是中断服务程序
   
该中断函数一般在插卡、拔卡或者从设备反馈给host信息时会被调用
 
//见

static irqreturn_t sdhci_irq(int irq, void *dev_id)
{
    irqreturn_t result;
    struct sdhci_host* host = dev_id;
    u32 intmask;
    int cardint = 0;

    spin_lock(&host->lock);

//
//#define SDHCI_INT_STATUS    0x30
图片
    intmask = readl(host->ioaddr + SDHCI_INT_STATUS);

    if (!intmask || intmask == 0xffffffff) {
        result = IRQ_NONE;
        goto out;
    }

    DBG("*** %s got interrupt: 0x%08x\n",
        mmc_hostname(host->mmc), intmask);
//判断  insert或 remove标志位
    if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
//写1清0 清 中断
        writel(intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE),
            host->ioaddr + SDHCI_INT_STATUS);
//调度tasklet运行
        tasklet_schedule(&host->card_tasklet);
    }

    intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
//???????
    if (intmask & SDHCI_INT_CMD_MASK) {
#if defined(CONFIG_MMC_SDHCI_S3C) || defined(CONFIG_MMC_SDHCI_MODULE)
        /* read until all status bit is up. by scsuh */
        int i;
        for (i=0; i<0x1000000; i++) {
            intmask = readl(host->ioaddr + SDHCI_INT_STATUS);
            if (intmask & SDHCI_INT_RESPONSE)
                break;
        }
        if (0x1000000 == i) {
            printk("FAIL: waiting for status update.\n");
        }
#endif
        writel(intmask & SDHCI_INT_CMD_MASK,
            host->ioaddr + SDHCI_INT_STATUS);
        sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);
    }
////命令处理中断和数据处理中断是不同的,一条既有命令又有数据的mmc cmd,
//////会至少激活2次中断,1次给命令,1次给数据。
    if (intmask & SDHCI_INT_DATA_MASK) {
        writel(intmask & SDHCI_INT_DATA_MASK,
            host->ioaddr + SDHCI_INT_STATUS);
        sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
    }

    intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK);

    intmask &= ~SDHCI_INT_ERROR;

    if (intmask & SDHCI_INT_BUS_POWER) {
        printk(KERN_ERR "%s: Card is consuming too much power!\n",
            mmc_hostname(host->mmc));
        writel(SDHCI_INT_BUS_POWER, host->ioaddr + SDHCI_INT_STATUS);
    }

    intmask &= ~SDHCI_INT_BUS_POWER;

    if (intmask & SDHCI_INT_CARD_INT)
        cardint = 1;

    intmask &= ~SDHCI_INT_CARD_INT;

    if (intmask) {
        printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n",
            mmc_hostname(host->mmc), intmask);
        sdhci_dumpregs(host);

        writel(intmask, host->ioaddr + SDHCI_INT_STATUS);
    }

    result = IRQ_HANDLED;

    mmiowb();
out:
    spin_unlock(&host->lock);

    /*
     * We have to delay this as it calls back into the driver.
     */
    if (cardint)
        mmc_signal_sdio_irq(host->mmc);

    return result;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 *    mmc_add_host - initialise host hardware
 *    @host: mmc host
 *
 *    Register the host with the driver model. The host must be
 *    prepared to start servicing requests before this function
 *    completes.
 */
//将host添加入mmc管理,其本质即是添加host的class_dev
int mmc_add_host(struct mmc_host *host)
{
    int err;
//host->caps    host capabilities    主机能力 高速,低速
    WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&
        !host->ops->enable_sdio_irq);

    led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
//??
//??struct platform_device s3c_device_hsmmc1 -> .dev??     
//这里添加的device_add是这个吗? 

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

#ifdef CONFIG_DEBUG_FS
    mmc_add_host_debugfs(host);
#endif
//启动host
    mmc_start_host(host);

    return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//
启动host
void mmc_start_host(struct mmc_host *host)
{
//mmc_power_off 会调用mmc_set_ios(host); --->调用host->ops->set_ios(host, ios);
//
//static const struct mmc_host_ops   sdhci_ops = {  里赋值了set_ios成员
//set_ios用来设置电源、时钟等等之类(需要重点关注)
//
关闭MMC
    mmc_power_off(host);
//include/linux/mmc/host.h:
//#define MMC_CAP_BOOT_ONTHEFLY    (1 << 8)    /* Can detect device at boot time */

//在mmc_alloc_host时,存在下面一行code

//INIT_DELAYED_WORK(&host->detect, mmc_rescan);

//到此就会发现mmc_start_host一定会执行mmc_rescan
//mmc_detect_change只不过一个是延时后执行,要么直接调用mmc_rescan一个是没有延时执行。


    if (host->caps & MMC_CAP_BOOT_ONTHEFLY)
        mmc_rescan(&host->detect.work);//功能就是扫描所插入的卡
    else
        mmc_detect_change(host, 0);//处理状态改变
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 *    mmc_detect_change - process change of state on a MMC socket
 *    @host: host which changed state.
 *    @delay: optional delay to wait before detection (jiffies)
 *
 *    MMC drivers should call this when they detect a card has been
 *    inserted or removed. The MMC layer will confirm that any
 *    present card is still functional, and initialize any newly
 *    inserted.
 */
void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{
//queue_delayed_workhost->detect提交到workqueue工作对列中。
//所以,当此delayed_work执行的时候,mmc_rescan将会被调用
// Schedule delayed work in the MMC work queue.
//类似于INIT_WORK和schedule_work的关系(工作队列)

    mmc_schedule_delayed_work(&host->detect, delay);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/*
 * Internal function. Schedule delayed work in the MMC work queue.
 */
static int mmc_schedule_delayed_work(struct delayed_work *work,
                     unsigned long delay)
{
//////drivers/mmc/core/core.c
//static int __init mmc_init(void)函数里有
////用于创建workqueue,只创建一个内核线程 的工作队列
 ////workqueue = create_singlethread_workqueue("kmmcd");
///drivers/mmc/core/host.c:mmc_alloc_host函数中调用
////    INIT_DELAYED_WORK(&host->detect, mmc_rescan);    初始化delayedwork并将函数与delay工作队列绑定
    return queue_delayed_work(workqueue, work, delay);

}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void mmc_rescan(struct work_struct *work)
{
    struct mmc_host *host =
        container_of(work, struct mmc_host, detect.work);
    u32 ocr;
    int err;

    mmc_bus_get(host);
//如果为第一次扫描总线上设备
    if (host->bus_ops == NULL) {
        /*
         * Only we can add a new handler, so it's safe to
         * release the lock here.
         */
        mmc_bus_put(host);

        if (host->ops->get_cd && host->ops->get_cd(host) == 0)
            goto out;
/* 驱动中使用mmc_claim_host(host);来得知,当前mmc控制器是否被占用,当前mmc控制器如果被占用,那么 host->claimed = 1;否则为0,如果为1,那么会在for(;;)循环中调用schedule切换出自己,当占用mmc控制器的操作完成之后,执行 mmc_release_host()的时候,会激活登记到等待队列&host->wq中的其他程序获得mmc主控制器的物理使用权*/
//这个函数和mmc_release_host(host);配对使用,相当于一把锁,就是在同一个时间只有一个sd卡可以保持和主控制器通讯;
        mmc_claim_host(host);
//
//最开始,卡是power_off
//
开启host电源
        mmc_power_up(host);
设置host为idle模式
        mmc_go_idle(host);
///是用于验证SD卡接口操作状态的有效性命令(CMD8)。
//如果 SD_SEND_IF_COND指示为符合SD2.0标准的卡,
//则设置操作状态寄存器ocrbit30指示能 够处理块地址SDHC卡
//在SD子系统中,所有的数据传输均是由host发起。
//Host发送完一命令则会等待中断的产生,在这里采用完成量来实现进程的阻塞。
        mmc_send_if_cond(host, host->ocr_avail);

        /*
         * First we search for SDIO...
         */
//判断是否是SDIO协议卡
//通过发送命令CMD5
//CMD5协议与SD接口中的ACMD41类似,用于检查是否支持SDIO的电压。
//CMD5的回应中,MP为0,表示是SDIO卡;如果MP为1,表示不但是SDIO卡,并且是SD卡。

        err = mmc_send_io_op_cond(host, 0, &ocr);
        if (!err) {
            if (mmc_attach_sdio(host, ocr))
                mmc_power_off(host);
            goto out;
        }

        /*
         * ...then normal SD...
         */
//判断是否是SD协议卡
//ocr 是指 card 內部的 Operation Condition Register (OCR) 讀出來的值
//發送 CMD41 CMD55 讀取 OCR 的值
//#define SD_APP_OP_COND           41   /* bcr  [31:0] OCR         R3  */
        err = mmc_send_app_op_cond(host, 0, &ocr);//
        if (!err) {
//装载 绑定 SD卡设备
            if (mmc_attach_sd(host, ocr))
                mmc_power_off(host);
            goto out;
        }

        /*
         * ...and finally MMC.
         */
//判断是否是MMC协议卡
        err = mmc_send_op_cond(host, 0, &ocr);
        if (!err) {
            if (mmc_attach_mmc(host, ocr))
                mmc_power_off(host);
            goto out;
        }
////释放卡对主机的持有权
        mmc_release_host(host);
        mmc_power_off(host);
    } else {
        if (host->bus_ops->detect && !host->bus_dead)
            host->bus_ops->detect(host);

        mmc_bus_put(host);
    }
out:
    if (host->caps & MMC_CAP_NEEDS_POLL)
        mmc_schedule_delayed_work(&host->detect, HZ);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



static struct sdhci_ops sdhci_s3c_ops = {
    .get_max_clock        = sdhci_s3c_get_max_clk,
    .get_timeout_clock    = sdhci_s3c_get_timeout_clk,
    .change_clock        = sdhci_s3c_change_clock,
//用来设置电源、时钟等等之类
    .set_ios        = sdhci_s3c_set_ios,
};















阅读(3344) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~