分析一个驱动,一般是从module_init()和module_exit()入手,首先来看下module_init()函数。module_init(dw_mci_init);module_exit(dw_mci_exit);static int __init dw_mci_init(void){ return platform_driver_probe(&dw_mci_driver, dw_mci_probe);} 在platform_driver_probe()函数中,涉及到一个结构体和一个探测函数,首先看下dw_mci_driver结构体: static struct platform_driver dw_mci_driver = { .remove = __exit_p(dw_mci_remove), .suspend = dw_mci_suspend, .resume = dw_mci_resume, .driver = { .name = "dw_mmc", //驱动的名字,和设备匹配 }, }; 在这个结构体中,有几个比较传统的函数,包括移除函数dw_mci_remove,电源管理中的挂起函数dw_mci_suspend和恢复函数dw_mci_resume, 在本节最后我们会回过头来分析这三个函数。 驱动的设备定义如下: #ifdef CONFIG_MMC_DW static int sd_init(u32 slot_id, irq_handler_t int_num, void *data) { return 0; } static struct resource dw_sd_resources[] = { { .start = DW_SDIO_BASEADDR & 0x1fffffff, .end = (DW_SDIO_BASEADDR & 0x1fffffff) + 0x104 -1, .flags = IORESOURCE_MEM, }, { .start = EXT_DW_SD_IRQ, .end = EXT_DW_SD_IRQ, .flags = IORESOURCE_IRQ, } }; struct dw_mci_board dw_mci = { .init = sd_init, .num_slots = 1, .bus_hz = 50000000, .quirks = DW_MCI_QUIRK_IDMAC_DTO | DW_MCI_QUIRK_RETRY_DELAY | DW_MCI_QUIRK_HIGHSPEED, }; struct platform_device dw_sd_device = { .name = "dw_mmc", //设备的名字,和驱动名字匹配 .id = 0, .num_resources = ARRAY_SIZE(dw_sd_resources), .resource = dw_sd_resources, .dev = { .platform_data = &dw_mci, } }; #endif 在platform驱动中,当驱动和设备的名字匹配的时候,就会调用探测probe函数: static int dw_mci_probe(struct platform_device *pdev) { struct dw_mci *host; struct resource *regs; struct dw_mci_board *pdata; int irq, ret, i, width; struct clk *mmc_clk; u32 fifo_size; regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); //申请设备资源 if (!regs) return -ENXIO; irq = platform_get_irq(pdev, 0); if (irq return irq; host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL); //创建设备host if (!host) return -ENOMEM; host->pdev = pdev; host->pdata = pdata = pdev->dev.platform_data; //将dw_mci赋值给host的pdata if (!pdata || !pdata->init) { dev_err(&pdev->dev, "Platform data must supply init function\n"); ret = -ENODEV; goto err_freehost; } if (!pdata->select_slot && pdata->num_slots > 1) { dev_err(&pdev->dev, "Platform data must supply select_slot function\n"); ret = -ENODEV; goto err_freehost; } if (!pdata->bus_hz) { dev_err(&pdev->dev, "Platform data must supply bus speed\n"); ret = -ENODEV; goto err_freehost; } host->bus_hz = pdata->bus_hz; //赋值总线时钟 host->quirks = pdata->quirks; spin_lock_init(&host->lock); //初始化自旋锁 INIT_LIST_HEAD(&host->queue); //初始化链表 mmc_clk = clk_get(&pdev->dev, "sdio"); //获取时钟 if (IS_ERR(mmc_clk)) { ret = -ENODEV; goto err_freehost; } clk_enable(mmc_clk); //使能时钟 ret = -ENOMEM; host->regs = ioremap(regs->start, regs->end - regs->start + 1); //dw寄存器的地址映射 if (!host->regs) goto err_freehost; host->dma_ops = pdata->dma_ops; //DMA操作函数集,在第二部分讲述 dw_mci_init_dma(host); //DMA初始化,在第二部分讲述 /* * Get the host data width - this assumes that HCON has been set with * the correct values. */ i = (mci_readl(host, HCON) >> 7) & 0x7; if (!i) { host->push_data = dw_mci_push_data16; //操作数据函数集合,第二部分讲述 host->pull_data = dw_mci_pull_data16; width = 16; host->data_shift = 1; } else if (i == 2) { host->push_data = dw_mci_push_data64; //操作数据函数集合,第二部分讲述 host->pull_data = dw_mci_pull_data64; width = 64; host->data_shift = 3; } else { /* Check for a reserved value, and warn if it is */ WARN((i != 1), "HCON reports a reserved host data width!\n" "Defaulting to 32-bit access.\n"); host->push_data = dw_mci_push_data32; //操作数据函数集合,第二部分讲述 host->pull_data = dw_mci_pull_data32; width = 32; host->data_shift = 2; } /* Reset all blocks */ if (!mci_wait_reset(&pdev->dev, host)) { //SD卡复位,第二部分讲述 ret = -ENODEV; goto err_dmaunmap; } /* Clear the interrupts for the host controller */ mci_writel(host, RINTSTS, 0xFFFFFFFF); mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */ /* Put in max timeout */ mci_writel(host, TMOUT, 0xFFFFFFFF); /* * FIFO threshold settings RxMark = fifo_size / 2 - 1, * Tx Mark = fifo_size / 2 DMA Size = 8 */ fifo_size = mci_readl(host, FIFOTH); fifo_size = ((fifo_size >> 16) & 0x7ff) + 1; host->fifoth_val = ((0x2 ((fifo_size/2) host->fifoth_val = 0x20070008; mci_writel(host, FIFOTH, host->fifoth_val); /* disable clock to CIU */ mci_writel(host, CLKENA, 0); mci_writel(host, CLKSRC, 0); //关于tasklet机制,见本博客其他文章 tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host); //操作函数集合,第三部分讲述 tasklet_init(&host->card_tasklet, dw_mci_tasklet_card, (unsigned long)host); //寻找卡,第三部分讲述 ret = request_irq(irq, dw_mci_interrupt, 0, "dw-mci", host); //申请中断,中断函数接下来讲述。 if (ret) goto err_dmaunmap; platform_set_drvdata(pdev, host); if (host->pdata->num_slots) host->num_slots = host->pdata->num_slots; else host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1; /* We need at least one slot to succeed */ for (i = 0; i num_slots; i++) { ret = dw_mci_init_slot(host, i); //slot初始化,第四部分讲述 if (ret) { ret = -ENODEV; goto err_init_slot; } } /* * Enable interrupts for command done, data over, data empty, card det, * receive ready and error such as transmit, receive timeout, crc error */ mci_writel(host, RINTSTS, 0xFFFFFFFF); mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | SDMMC_INT_TXDR | SDMMC_INT_RXDR | DW_MCI_ERROR_FLAGS | SDMMC_INT_CD); mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */ dev_info(&pdev->dev, "DW MMC controller at irq %d, " "%d bit host data width\n", irq, width); if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) dev_info(&pdev->dev, "Internal DMAC interrupt fix enabled.\n"); return 0; err_init_slot: /* De-init any initialized slots */ while (i > 0) { if (host->slot[i]) dw_mci_cleanup_slot(host->slot[i], i); //清除slot,第四部分讲述 i--; } free_irq(irq, host); err_dmaunmap: if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); dma_free_coherent(&host->pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); iounmap(host->regs); if (host->vmmc) { regulator_disable(host->vmmc); regulator_put(host->vmmc); } err_freehost: kfree(host); return ret; }接下来看下中断函数dw_mci_interrupt():static irqreturn_t dw_mci_interrupt(int irq, void *dev_id){ struct dw_mci *host = dev_id; u32 status, pending; unsigned int pass_count = 0; do { status = mci_readl(host, RINTSTS); //读取原始中断状态寄存器 pending = mci_readl(host, MINTSTS); /* read-only mask reg */ //读取屏蔽中断状态寄存器 /* * DTO fix - version 2.10a and below, and only if internal DMA * is configured. */ if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) { if (!pending && ((mci_readl(host, STATUS) >> 17) & 0x1fff)) pending |= SDMMC_INT_DATA_OVER; } if (!pending) break; if (pending & DW_MCI_CMD_ERROR_FLAGS) { //命令错误,响应错误,响应CRC错误,响应超时错误 mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS); host->cmd_status = status; smp_wmb(); set_bit(EVENT_CMD_COMPLETE, &host->pending_events); tasklet_schedule(&host->tasklet); //调度软中断函数,执行EVENT_CMD_COMPLETE分支 } if (pending & DW_MCI_DATA_ERROR_FLAGS) { //数据错误,数据读超时,CRC错误,主设备超时,起始位错误,最后一位错误 /* if there is an error report DATA_ERROR */ mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS); host->data_status = status; smp_wmb(); set_bit(EVENT_DATA_ERROR, &host->pending_events); tasklet_schedule(&host->tasklet); //调度软中断函数,执行EVENT_DATA_ERROR分支 } if (pending & SDMMC_INT_DATA_OVER) { //数据传输结束中断 mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); if (!host->data_status) host->data_status = status; smp_wmb(); if (host->dir_status == DW_MCI_RECV_STATUS) { if (host->sg != NULL) dw_mci_read_data_pio(host); } set_bit(EVENT_DATA_COMPLETE, &host->pending_events); tasklet_schedule(&host->tasklet); //调度软中断函数,执行EVENT_DATA_COMPLETE分支 } if (pending & SDMMC_INT_RXDR) { //收数中断 mci_writel(host, RINTSTS, SDMMC_INT_RXDR); if (host->sg) dw_mci_read_data_pio(host); //读数据,接下来讲 } if (pending & SDMMC_INT_TXDR) { //发数中断 mci_writel(host, RINTSTS, SDMMC_INT_TXDR); if (host->sg) dw_mci_write_data_pio(host); //写数据,接下来讲 } if (pending & SDMMC_INT_CMD_DONE) { //命令结束中断 mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE); dw_mci_cmd_interrupt(host, status); //命令中断,接下来讲 } if (pending & SDMMC_INT_CD) { //卡发现分支 mci_writel(host, RINTSTS, SDMMC_INT_CD); tasklet_schedule(&host->card_tasklet); //调度软中断函数,卡片处理 } } while (pass_count++#ifdef CONFIG_MMC_DW_IDMAC /* Handle DMA interrupts */ pending = mci_readl(host, IDSTS); //读取内部DMA状态寄存器 if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) { //一个描述符数据发送或者接收完成 mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI); mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI); set_bit(EVENT_DATA_COMPLETE, &host->pending_events); host->dma_ops->complete(host); }#endif return IRQ_HANDLED;}读数据: static void dw_mci_read_data_pio(struct dw_mci *host) { struct scatterlist *sg = host->sg; void *buf = sg_virt(sg); unsigned int offset = host->pio_offset; struct mmc_data *data = host->data; int shift = host->data_shift; u32 status; unsigned int nbytes = 0, len; do { len = SDMMC_GET_FCNT(mci_readl(host, STATUS)) //获取fifo里填充数据个数 if (offset + len length) { host->pull_data(host, (void *)(buf + offset), len); //读取数据 offset += len; nbytes += len; if (offset == sg->length) { flush_dcache_page(sg_page(sg)); host->sg = sg = sg_next(sg); if (!sg) goto done; offset = 0; buf = sg_virt(sg); } } else { unsigned int remaining = sg->length - offset; host->pull_data(host, (void *)(buf + offset), remaining); nbytes += remaining; flush_dcache_page(sg_page(sg)); host->sg = sg = sg_next(sg); if (!sg) goto done; offset = len - remaining; buf = sg_virt(sg); host->pull_data(host, buf, offset); nbytes += offset; } status = mci_readl(host, MINTSTS); mci_writel(host, RINTSTS, SDMMC_INT_RXDR); if (status & DW_MCI_DATA_ERROR_FLAGS) { host->data_status = status; data->bytes_xfered += nbytes; smp_wmb(); set_bit(EVENT_DATA_ERROR, &host->pending_events); tasklet_schedule(&host->tasklet); return; } } while (status & SDMMC_INT_RXDR); /*if the RXDR is ready read again*/ len = SDMMC_GET_FCNT(mci_readl(host, STATUS)); host->pio_offset = offset; data->bytes_xfered += nbytes; return; done: data->bytes_xfered += nbytes; smp_wmb(); set_bit(EVENT_XFER_COMPLETE, &host->pending_events); }写数据: static void dw_mci_write_data_pio(struct dw_mci *host) { struct scatterlist *sg = host->sg; void *buf = sg_virt(sg); unsigned int offset = host->pio_offset; struct mmc_data *data = host->data; int shift = host->data_shift; u32 status; unsigned int nbytes = 0, len; do { len = SDMMC_FIFO_SZ - (SDMMC_GET_FCNT(mci_readl(host, STATUS)) if (offset + len length) { host->push_data(host, (void *)(buf + offset), len); //写数据 offset += len; nbytes += len; if (offset == sg->length) { host->sg = sg = sg_next(sg); if (!sg) goto done; offset = 0; buf = sg_virt(sg); } } else { unsigned int remaining = sg->length - offset; host->push_data(host, (void *)(buf + offset), remaining); nbytes += remaining; host->sg = sg = sg_next(sg); if (!sg) goto done; offset = len - remaining; buf = sg_virt(sg); host->push_data(host, (void *)buf, offset); nbytes += offset; } status = mci_readl(host, MINTSTS); mci_writel(host, RINTSTS, SDMMC_INT_TXDR); if (status & DW_MCI_DATA_ERROR_FLAGS) { host->data_status = status; data->bytes_xfered += nbytes; smp_wmb(); set_bit(EVENT_DATA_ERROR, &host->pending_events); tasklet_schedule(&host->tasklet); return; } } while (status & SDMMC_INT_TXDR); /* if TXDR write again */ host->pio_offset = offset; data->bytes_xfered += nbytes; return; done: data->bytes_xfered += nbytes; smp_wmb(); set_bit(EVENT_XFER_COMPLETE, &host->pending_events); }中断: static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status) { if (!host->cmd_status) host->cmd_status = status; smp_wmb(); set_bit(EVENT_CMD_COMPLETE, &host->pending_events); tasklet_schedule(&host->tasklet); }最后我们看下移除函数,挂起函数和恢复函数:static int __exit dw_mci_remove(struct platform_device *pdev){ struct dw_mci *host = platform_get_drvdata(pdev); int i; mci_writel(host, RINTSTS, 0xFFFFFFFF); mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */ platform_set_drvdata(pdev, NULL); //清除私有数据 for (i = 0; i num_slots; i++) { dev_dbg(&pdev->dev, "remove slot %d\n", i); if (host->slot[i]) dw_mci_cleanup_slot(host->slot[i], i); } /* disable clock to CIU */ mci_writel(host, CLKENA, 0); mci_writel(host, CLKSRC, 0); free_irq(platform_get_irq(pdev, 0), host); dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); if (host->vmmc) { regulator_disable(host->vmmc); regulator_put(host->vmmc); } iounmap(host->regs); kfree(host); return 0;}#ifdef CONFIG_PM/* * TODO: we should probably disable the clock to the card in the suspend path. */static int dw_mci_suspend(struct platform_device *pdev, pm_message_t mesg){ int i, ret; struct dw_mci *host = platform_get_drvdata(pdev); for (i = 0; i num_slots; i++) { struct dw_mci_slot *slot = host->slot[i]; if (!slot) continue; ret = mmc_suspend_host(slot->mmc); if (ret while (--i >= 0) { slot = host->slot[i]; if (slot) mmc_resume_host(host->slot[i]->mmc); } return ret; } } if (host->vmmc) regulator_disable(host->vmmc); return 0;}static int dw_mci_resume(struct platform_device *pdev){ int i, ret; struct dw_mci *host = platform_get_drvdata(pdev); if (host->vmmc) regulator_enable(host->vmmc); if (host->dma_ops->init) host->dma_ops->init(host); if (!mci_wait_reset(&pdev->dev, host)) { ret = -ENODEV; return ret; } /* Restore the old value at FIFOTH register */ mci_writel(host, FIFOTH, host->fifoth_val); mci_writel(host, RINTSTS, 0xFFFFFFFF); mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | SDMMC_INT_TXDR | SDMMC_INT_RXDR | DW_MCI_ERROR_FLAGS | SDMMC_INT_CD); mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); for (i = 0; i num_slots; i++) { struct dw_mci_slot *slot = host->slot[i]; if (!slot) continue; ret = mmc_resume_host(host->slot[i]->mmc); if (ret return ret; } return 0;}#else#define dw_mci_suspend NULL#define dw_mci_resume NULL#endif /* CONFIG_PM */