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

全部博文(62)

文章存档

2014年(55)

2013年(7)

我的朋友

分类: 嵌入式

2014-05-03 20:44:27

    接下来继续第一部分,来看下DW的SD卡驱动中关于操作DAM部分,在探测函数中,主要是如下两条语句:
    host->dma_ops = pdata->dma_ops;
    dw_mci_init_dma(host);
    由于这里是探测函数,在设备定义中也没有对pdata->dma_ops成员进行初始化,所以接下来看dw_mci_init_dma(host);函数
static void dw_mci_init_dma(struct dw_mci *host)
{
    /* Alloc memory for sg translation */
    host->sg_cpu = dma_alloc_coherent(&host->pdev->dev, PAGE_SIZE,
                      &host->sg_dma, GFP_KERNEL);    //DMA内存申请
    if (!host->sg_cpu) {
        dev_err(&host->pdev->dev, "%s: could not alloc DMA memory\n",
            __func__);
        goto no_dma;
    }
    /* Determine which DMA interface to use */
#ifdef CONFIG_MMC_DW_IDMAC
    host->dma_ops = &dw_mci_idmac_ops;    //DMA操作函数集合
    dev_info(&host->pdev->dev, "Using internal DMA controller.\n");
#endif
    if (!host->dma_ops)
        goto no_dma;
    if (host->dma_ops->init) {
        if (host->dma_ops->init(host)) {
            dev_err(&host->pdev->dev, "%s: Unable to initialize "
                "DMA Controller.\n", __func__);
            goto no_dma;
        }
    } else {
        dev_err(&host->pdev->dev, "DMA initialization not found.\n");
        goto no_dma;
    }
    host->use_dma = 1;    //标识已使用DMA
    return;
no_dma:
    dev_info(&host->pdev->dev, "Using PIO mode.\n");
    host->use_dma = 0;
    return;
}
    DMA操作函数集:
static struct dw_mci_dma_ops dw_mci_idmac_ops = {
    .init = dw_mci_idmac_init,
    .start = dw_mci_idmac_start_dma,
    .stop = dw_mci_idmac_stop_dma,
    .complete = dw_mci_idmac_complete_dma,
    .cleanup = dw_mci_dma_cleanup,
};
     首先看下初始化函数dw_mci_idmac_init:
static int dw_mci_idmac_init(struct dw_mci *host)
{
    struct idmac_desc *p;
    int i;
    /* Number of descriptors in the ring buffer */
    host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
    //描述符初始化
    /* Forward link the descriptor list */
    for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
        p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1));
    /* Set the last descriptor as the end-of-ring descriptor */
    p->des3 = host->sg_dma;
    p->des0 = IDMAC_DES0_ER;
    /* Mask out interrupts - get Tx & Rx complete only */
    mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI |
           SDMMC_IDMAC_INT_TI);
    /* Set the descriptor base address */
    mci_writel(host, DBADDR, host->sg_dma);
    return 0;
}
     开始函数dw_mci_idmac_start_dma:
static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len)
{
    u32 temp;
    dw_mci_translate_sglist(host, host->data, sg_len);    //DMA初始化
    /* Select IDMAC interface */
    temp = mci_readl(host, CTRL);
    temp |= SDMMC_CTRL_USE_IDMAC;
    mci_writel(host, CTRL, temp);
    wmb();
    /* Enable the IDMAC */
    temp = mci_readl(host, BMOD);
    temp |= SDMMC_IDMAC_ENABLE | SDMMC_IDMAC_FB;    //使能内部DMA,固定突发长度
    mci_writel(host, BMOD, temp);
    /* Start it running */
    mci_writel(host, PLDMND, 1);
}
static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data,
                    unsigned int sg_len)
{
    int i;
    struct idmac_desc *desc = host->sg_cpu;
    for (i = 0; i < sg_len; i++, desc++) {
        unsigned int length = sg_dma_len(&data->sg[i]);
        u32 mem_addr = sg_dma_address(&data->sg[i]);
        /* Set the OWN bit and disable interrupts for this descriptor */
        desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH;
        /* Buffer length */
        IDMAC_SET_BUFFER1_SIZE(desc, length);
        /* Physical address to DMA to/from */
        desc->des2 = mem_addr;
    }
    /* Set first descriptor */
    desc = host->sg_cpu;
    desc->des0 |= IDMAC_DES0_FD;
    /* Set last descriptor */
    desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc);
    desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
    desc->des0 |= IDMAC_DES0_LD;
    wmb();
}
     DMA结束函数dw_mci_idmac_stop_dma:
static void dw_mci_idmac_stop_dma(struct dw_mci *host)
{
    u32 temp;
    /* Disable and reset the IDMAC interface */
    temp = mci_readl(host, CTRL);
    temp &= ~SDMMC_CTRL_USE_IDMAC;
    temp |= SDMMC_CTRL_DMA_RESET;
    mci_writel(host, CTRL, temp);
    /* Stop the IDMAC running */
    temp = mci_readl(host, BMOD);
    temp &= ~(SDMMC_IDMAC_ENABLE | SDMMC_IDMAC_FB);
    mci_writel(host, BMOD, temp);
}
     DMA完成函数dw_mci_idmac_complete_dma:
static void dw_mci_idmac_complete_dma(struct dw_mci *host)
{
    struct mmc_data *data = host->data;
    dev_vdbg(&host->pdev->dev, "DMA complete\n");
    host->dma_ops->cleanup(host);
    /*
     * If the card was removed, data will be NULL. No point in trying to
     * send the stop command or waiting for NBUSY in this case.
     */
    if (data) {
        set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
        tasklet_schedule(&host->tasklet);    //调度软中断
    }
}
    DMA清除函数dw_mci_dma_cleanup:
static void dw_mci_dma_cleanup(struct dw_mci *host)
{
    struct mmc_data *data = host->data;
    if (data)
        dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len,
                 ((data->flags & MMC_DATA_WRITE)
                  ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
}

    接下来分析下探测函数中赋值的收发函数,这里根据数据类型,总共有三种,包括16位、32位和64位的收发,程序类似,我们以16位为例讲述。
host->push_data = dw_mci_push_data16;
host->pull_data = dw_mci_pull_data16;
发函数:
static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
{
    u16 *pdata = (u16 *)buf;
    WARN_ON(cnt % 2 != 0);
    cnt = cnt >> 1;    //接收到的长度是以字节为单位的,所以16位的除以2,如果是32位的就右移两位,除以4,以此类推
    while (cnt > 0) {
        mci_writew(host, DATA, *pdata++);
        cnt--;
    }
}
收函数:
static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
{
    u16 *pdata = (u16 *)buf;
    WARN_ON(cnt % 2 != 0);
    cnt = cnt >> 1;    //接收到的长度是以字节为单位的,所以16位的除以2,如果是32位的就右移两位,除以4,以此类推
    while (cnt > 0) {
        *pdata++ = mci_readw(host, DATA);
        cnt--;
    }
}
    最后分析下SD复位函数mci_wait_reset
static bool mci_wait_reset(struct device *dev, struct dw_mci *host)
{
    unsigned long timeout = jiffies + msecs_to_jiffies(500);
    unsigned int ctrl;
    mci_writel(host, CTRL, (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET |
                SDMMC_CTRL_DMA_RESET));    //设置控制寄存器
    /* wait till resets clear */
    do {
        ctrl = mci_readl(host, CTRL);
        if (!(ctrl & (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET |
                  SDMMC_CTRL_DMA_RESET)))
            return true;
    } while (time_before(jiffies, timeout));    //等待超时时间内
    dev_err(dev, "Timeout resetting block (ctrl %#x)\n", ctrl);
    return false;
}
阅读(608) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~