分析一个驱动,一般是从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 */
11-17 01:21
查看更多