Chinaunix首页 | 论坛 | 博客
  • 博客访问: 81743
  • 博文数量: 62
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 14
  • 用 户 组: 普通用户
  • 注册时间: 2013-12-23 21:49
文章分类

全部博文(62)

文章存档

2014年(55)

2013年(7)

我的朋友

分类: 嵌入式

2014-05-03 20:44:12

       分析一个驱动,一般是从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 < 0)
        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 << 28) | ((fifo_size/2 - 1) << 16) |
            ((fifo_size/2) << 0));
    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 < host->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++ < 5);

#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)) << shift;    //获取fifo里填充数据个数
        if (offset + len <= sg->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)) << shift);
        if (offset + len <= sg->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 < host->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 < host->num_slots; i++) {
        struct dw_mci_slot *slot = host->slot[i];
        if (!slot)
            continue;
        ret = mmc_suspend_host(slot->mmc);
        if (ret < 0) {
            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 < host->num_slots; i++) {
        struct dw_mci_slot *slot = host->slot[i];
        if (!slot)
            continue;
        ret = mmc_resume_host(host->slot[i]->mmc);
        if (ret < 0)
            return ret;
    }
    return 0;
}
#else
#define dw_mci_suspend    NULL
#define dw_mci_resume    NULL
#endif /* CONFIG_PM */
阅读(1058) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~