分析一个驱动,一般是从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 */
阅读(1353) | 评论(0) | 转发(0) |