参考原文
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,
};
阅读(3440) | 评论(0) | 转发(0) |