linux2.6.28块设备mmc_sd卡s3c-sdhci驱动sdhci-s3c.c注册

参考原文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_work把host->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,
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xx-xx-xxx-xxx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值