以瑞芯微(rk)平台的代码解析,其他平台也类似,供其他同学参考学习.
参考:http://blog.chinaunix.net/uid-25445243-id-3885164.html
int dw_mci_probe(struct dw_mci *host)
{
const struct dw_mci_drv_data *drv_data = host->drv_data;
int width, i, ret = 0;
u32 fifo_size;
int init_slots = 0;
if (!host->pdata) {
host->pdata = dw_mci_parse_dt(host); //解析dts中卡槽,队列深度,检测卡之前的延时(毫秒)
if (IS_ERR(host->pdata)) {
dev_err(host->dev, "platform data not available\n");
return -EINVAL;
}
}
if (host->pdata->num_slots < 1) {
dev_err(host->dev,
"Platform data must supply num_slots.\n");
return -ENODEV;
}
host->biu_clk = devm_clk_get(host->dev, "biu"); //获取总线接口单元时钟 biu=bus interface unit clock
if (IS_ERR(host->biu_clk)) {
dev_dbg(host->dev, "biu clock not available\n");
} else {
ret = clk_prepare_enable(host->biu_clk); //clk使能
if (ret) {
dev_err(host->dev, "failed to enable biu clock\n");
return ret;
}
}
host->ciu_clk = devm_clk_get(host->dev, "ciu"); //获取sd卡接口时钟 ciu=card interface unit clock
if (IS_ERR(host->ciu_clk)) {
dev_dbg(host->dev, "ciu clock not available\n");
host->bus_hz = host->pdata->bus_hz;
} else {
ret = clk_prepare_enable(host->ciu_clk);
if (ret) {
dev_err(host->dev, "failed to enable ciu clock\n");
goto err_clk_biu;
}
if (host->pdata->bus_hz) {
ret = clk_set_rate(host->ciu_clk, host->pdata->bus_hz);
if (ret)
dev_warn(host->dev,
"Unable to set bus rate to %uHz\n",
host->pdata->bus_hz);
}
host->bus_hz = clk_get_rate(host->ciu_clk); //获得卡的设备时钟频率赋值给bus_hz
}
if (!host->bus_hz) {
dev_err(host->dev,
"Platform data must supply bus speed\n");
ret = -ENODEV;
goto err_clk_ciu;
}
if (drv_data && drv_data->init) { //调用host/dw_mmc-rockchip.c中的回调函数.init = dw_mci_rockchip_init,
ret = drv_data->init(host);
if (ret) {
dev_err(host->dev,
"implementation specific init failed\n");
goto err_clk_ciu;
}
}
if (drv_data && drv_data->setup_clock) { //同上也是回调函数
ret = drv_data->setup_clock(host);
if (ret) {
dev_err(host->dev,
"implementation specific clock setup failed\n");
goto err_clk_ciu;
}
}
setup_timer(&host->cmd11_timer,
dw_mci_cmd11_timer, (unsigned long)host); //内核定时机制,主要设置host->cmd11_timer的回调, \ host->cmd11_timer->function = dw_mci_cmd11_timer; host->cmd11_timer->data =host
host->quirks = host->pdata->quirks;
setup_timer(&host->cto_timer,
dw_mci_cto_timer, (unsigned long)host);
if (host->quirks & DW_MCI_QUIRK_BROKEN_DTO)
setup_timer(&host->dto_timer,
dw_mci_dto_timer, (unsigned long)host);
if (host->quirks & DW_MCI_QUIRK_BROKEN_XFER)
setup_timer(&host->xfer_timer,
dw_mci_xfer_timer, (unsigned long)host);
spin_lock_init(&host->lock); //初始化自旋锁
spin_lock_init(&host->irq_lock); //初始化自旋锁
INIT_LIST_HEAD(&host->queue); //初始化链表
/*
* Get the host data width - this assumes that HCON has been set with
* the correct values.
*/
i = SDMMC_GET_HDATA_WIDTH(mci_readl(host, HCON));
if (!i) {
host->push_data = dw_mci_push_data16; //操作数据函数集合,测函数中赋值的收发函数,这里根据数据类型,总共有三种,包括16位、32位和64位的收发 接收到的长度是以字节为单位的,所以16位的除以2,如果是32位的就右移两位,除以4,以此类推
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 (!dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS)) //sd卡复位,设置控制寄存器,等待超时时间内
return -ENODEV;
host->dma_ops = host->pdata->dma_ops;
dw_mci_init_dma(host); // dma初始化
/* 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
*/
if (!host->pdata->fifo_depth) {
/*
* Power-on value of RX_WMark is FIFO_DEPTH-1, but this may
* have been overwritten by the bootloader, just like we're
* about to do, so if you know the value for your hardware, you
* should put it in the platform data.
*/
fifo_size = mci_readl(host, FIFOTH);
fifo_size = 1 + ((fifo_size >> 16) & 0xfff);
} else {
fifo_size = host->pdata->fifo_depth;
}
host->fifo_depth = fifo_size;
host->fifoth_val =
SDMMC_SET_FIFOTH(0x2, fifo_size / 2 - 1, fifo_size / 2);
mci_writel(host, FIFOTH, host->fifoth_val);
/* disable clock to CIU */
mci_writel(host, CLKENA, 0);
mci_writel(host, CLKSRC, 0);
/*
* In 2.40a spec, Data offset is changed.
* Need to check the version-id and set data-offset for DATA register.
*/
host->verid = SDMMC_GET_VERID(mci_readl(host, VERID));
dev_info(host->dev, "Version ID is %04x\n", host->verid);
printk("hdj sdmmc Version ID is %04x\n", host->verid);
if (host->verid < DW_MMC_240A)
host->fifo_reg = host->regs + DATA_OFFSET;
else
host->fifo_reg = host->regs + DATA_240A_OFFSET;
tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host); //操作函数集合,回调函数赋值给host->tasklet变量
ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt, //往下看
host->irq_flags, "dw-mci", host);
if (ret)
goto err_dmaunmap;
if (host->pdata->num_slots)
host->num_slots = host->pdata->num_slots;
else
host->num_slots = SDMMC_GET_SLOT_NUM(mci_readl(host, HCON));
/*
* Enable interrupts for command done, data over, data empty,
* 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);
/* Enable mci interrupt */
mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE);
dev_info(host->dev,
"DW MMC controller at irq %d,%d bit host data width,%u deep fifo\n",
host->irq, width, fifo_size);
/* We need at least one slot to succeed */
for (i = 0; i < host->num_slots; i++) {
ret = dw_mci_init_slot(host, i); //slot初始化,申请host,识别sd卡,读取sd卡cid,会调到core层
if (ret)
dev_dbg(host->dev, "slot %d init failed\n", i);
else
init_slots++;
}
if (init_slots) {
dev_info(host->dev, "%d slots initialized\n", init_slots);
} else {
dev_dbg(host->dev,
"attempted to initialize %d slots, but failed on all\n",
host->num_slots);
goto err_dmaunmap;
}
/* Now that slots are all setup, we can enable card detect */
dw_mci_enable_cd(host);
if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
dev_info(host->dev, "Internal DMAC interrupt fix enabled.\n");
return 0;
}
static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
{
struct dw_mci *host = dev_id;
u32 pending;
int i;
unsigned long irqflags;
pending = mci_readl(host, MINTSTS); /* read-only mask reg 读取屏蔽中断状态寄存器 DMMC_MINTSTS */
/*
* 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) {
/* Check volt switch first, since it can look like an error */
if ((host->state == STATE_SENDING_CMD11) && //CMD11电压切换
(pending & SDMMC_INT_VOLT_SWITCH)) {
mci_writel(host, RINTSTS, SDMMC_INT_VOLT_SWITCH);
pending &= ~SDMMC_INT_VOLT_SWITCH;
/*
* Hold the lock; we know cmd11_timer can't be kicked
* off after the lock is released, so safe to delete.
*/
spin_lock_irqsave(&host->irq_lock, irqflags);
dw_mci_cmd_interrupt(host, pending); //调用软中断taskler_schedule
spin_unlock_irqrestore(&host->irq_lock, irqflags);
del_timer(&host->cmd11_timer);
}
if (pending & DW_MCI_CMD_ERROR_FLAGS) { ////命令错误,响应错误,响应CRC错误,响应超时错误
spin_lock_irqsave(&host->irq_lock, irqflags);
del_timer(&host->cto_timer);
mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
host->cmd_status = pending;
if ((host->quirks & DW_MCI_QUIRK_BROKEN_XFER) &&
host->dir_status == DW_MCI_RECV_STATUS)
del_timer(&host->xfer_timer);
smp_wmb(); /* drain writebuffer */
set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
spin_unlock_irqrestore(&host->irq_lock, irqflags);
}
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 = pending;
smp_wmb(); /* drain writebuffer */
set_bit(EVENT_DATA_ERROR, &host->pending_events);
tasklet_schedule(&host->tasklet); //调度软中断函数,执行EVENT_DATA_ERROR分支
}
if (pending & SDMMC_INT_DATA_OVER) { //数据传输结束中断
spin_lock_irqsave(&host->irq_lock, irqflags);
if (host->quirks & DW_MCI_QUIRK_BROKEN_DTO)
del_timer(&host->dto_timer);
mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
if (!host->data_status)
host->data_status = pending;
smp_wmb(); /* drain writebuffer */
if (host->dir_status == DW_MCI_RECV_STATUS) {
if (host->sg != NULL)
dw_mci_read_data_pio(host, true); //读数据
}
set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
tasklet_schedule(&host->tasklet);
spin_unlock_irqrestore(&host->irq_lock, irqflags);
}
if (pending & SDMMC_INT_RXDR) { //收数据中断
mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
if (host->dir_status == DW_MCI_RECV_STATUS && host->sg)
dw_mci_read_data_pio(host, false);
}
if (pending & SDMMC_INT_TXDR) { //发数据中断
mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
if (host->dir_status == DW_MCI_SEND_STATUS && host->sg)
dw_mci_write_data_pio(host); 写数据
}
if (pending & SDMMC_INT_CMD_DONE) { // //命令结束中断
spin_lock_irqsave(&host->irq_lock, irqflags);
mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE);
dw_mci_cmd_interrupt(host, pending);
spin_unlock_irqrestore(&host->irq_lock, irqflags);
}
if (pending & SDMMC_INT_CD) { //发现sd卡后
mci_writel(host, RINTSTS, SDMMC_INT_CD);
dw_mci_handle_cd(host); //卡插入或拔出的处理函数
}
/* Handle SDIO Interrupts */
for (i = 0; i < host->num_slots; i++) {
struct dw_mci_slot *slot = host->slot[i];
if (!slot)
continue;
if (pending & SDMMC_INT_SDIO(slot->sdio_id)) { //判断sdio设备
mci_writel(host, RINTSTS,
SDMMC_INT_SDIO(slot->sdio_id));
mmc_signal_sdio_irq(slot->mmc); //唤醒中断线程
}
}
}
if (host->use_dma != TRANS_MODE_IDMAC)
return IRQ_HANDLED;
/* Handle IDMA interrupts */
if (host->dma_64bit_address == 1) {
pending = mci_readl(host, IDSTS64); //读取内部DMA状态寄存器
if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_TI |
SDMMC_IDMAC_INT_RI);
mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_NI);
if (!test_bit(EVENT_DATA_ERROR, &host->pending_events))
host->dma_ops->complete((void *)host);
}
} else {
pending = mci_readl(host, IDSTS);
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);
if (!test_bit(EVENT_DATA_ERROR, &host->pending_events))
host->dma_ops->complete((void *)host);
}
}
return IRQ_HANDLED;
}