Linux SD卡/SDIO驱动开发-dw_mci_probe

以瑞芯微(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;
    }

你可能感兴趣的:(Linux-MMC子系统)