接下来继续第一部分,来看下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) |