i.mx536(cotex-a8核)的SPI驱动理解一(probe)

//整个probe主要包含以下几步,与其它的ARM芯片很相似
//(1)填充三个结构体struct mxc_spi_master,struct spi_master,struct mxc_spi
//(2)申请IO资源,中断
//(3)SPI寄存器配置
//(4)spi_bitbang_start(即调用spi_register_master)
//(5)spi_new_device
static int mxc_spi_probe(struct platform_device *pdev)
{
	//spi私有数据结构体,imx芯片独有,主要存储板级配置文件中设置的一些参数
	//个人觉得这个结构体有点多余,还没弄清楚原因?
	struct mxc_spi_master *mxc_platform_info;
	//描述spi控制器结构体,spi驱动通用
	struct spi_master *master;
	struct mxc_spi *master_drv_data = NULL;
	//一般的ARM控制器的SPI驱动都会包含以上三个结构体(具体名称不用)
	struct resource *res;
	unsigned int spi_ver, wml;
	int ret = -ENODEV;

	/* Get the platform specific data for this master device */
	//为什么这里可以强制转换struct platform_data为struct mxc_spi_master?
	//因为platform_data是一个void类型的指针
	mxc_platform_info = (struct mxc_spi_master *)pdev->dev.platform_data;
	if (!mxc_platform_info) {
		dev_err(&pdev->dev, "can't get the platform data for CSPI\n");
		return -EINVAL;
	}

	/* Allocate SPI master controller */
	master = spi_alloc_master(&pdev->dev, sizeof(struct mxc_spi));
	if (!master) {
		dev_err(&pdev->dev, "can't alloc for spi_master\n");
		return -ENOMEM;
	}

	/* Set this device's driver data to master */
	//保存master到pdev
	platform_set_drvdata(pdev, master);

	/* Set this master's data from platform_info */
	//主机控制器编号,如果板子上有多个spi总线,靠这个域区分
	master->bus_num = pdev->id + 1;
	//支持spi设备个数
	master->num_chipselect = mxc_platform_info->maxchipselect;
	//模式标志位
	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
#ifdef CONFIG_SPI_MXC_TEST_LOOPBACK
	master->num_chipselect += 1;
#endif
	/* Set the master controller driver data for this master */
	//spi_master_get_devdata调用dev_set_drvdata获取设备私有数据
	//读取保存在master->dev中的私有数据
	//这个私有数据是在上面的spi_alloc_master中配置的。
	master_drv_data = spi_master_get_devdata(master);
	//mxc_bitbang是一个spi_bitbang结构体,struct spi_bitbang 是具体的负责数据传输的结构体
	master_drv_data->mxc_bitbang.master = spi_master_get(master);
	//把存储在mxc_platform_info中的一些参数(板级配置文件中设置)存储到master_drv_data
	if (mxc_platform_info->chipselect_active)
		master_drv_data->chipselect_active =
		    mxc_platform_info->chipselect_active;
	if (mxc_platform_info->chipselect_inactive)
		master_drv_data->chipselect_inactive =
		    mxc_platform_info->chipselect_inactive;

	/* Identify SPI version */
	//根据扳级配置文件中设置的版本
	spi_ver = mxc_platform_info->spi_version;
	if (spi_ver == 7) {
		master_drv_data->spi_ver_def = &spi_ver_0_7;
	} else if (spi_ver == 5) {
		master_drv_data->spi_ver_def = &spi_ver_0_5;
	} else if (spi_ver == 4) {
		master_drv_data->spi_ver_def = &spi_ver_0_4;
	} else if (spi_ver == 0) {
		master_drv_data->spi_ver_def = &spi_ver_0_0;
	} else if (spi_ver == 23) {
		master_drv_data->spi_ver_def = &spi_ver_2_3;
	}

	dev_dbg(&pdev->dev, "SPI_REV 0.%d\n", spi_ver);

	/* Set the master bitbang data */
	//SPI传输的各个函数
	master_drv_data->mxc_bitbang.chipselect = mxc_spi_chipselect;
	master_drv_data->mxc_bitbang.txrx_bufs = mxc_spi_transfer;
	//该函数做一些初始化的工作
	master_drv_data->mxc_bitbang.master->setup = mxc_spi_setup;
	master_drv_data->mxc_bitbang.master->cleanup = mxc_spi_cleanup;
	master_drv_data->mxc_bitbang.setup_transfer = mxc_spi_setup_transfer;

	/* Initialize the completion object */
	//completion是内核中一个轻量级机制,允许一个线程告诉另一个线程工作已完成
	init_completion(&master_drv_data->xfer_done);

	/* Set the master controller register addresses and irqs */
	//获取板级配置文件中设置的资源
	master_drv_data->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!master_drv_data->res) {
		dev_err(&pdev->dev, "can't get platform resource for CSPI%d\n",
			master->bus_num);
		ret = -ENOMEM;
		goto err;
	}
	//检测申请的资源是否可用,并把资源标志为已用
	if (!request_mem_region(master_drv_data->res->start,
				master_drv_data->res->end -
				master_drv_data->res->start + 1, pdev->name)) {
		dev_err(&pdev->dev, "request_mem_region failed for CSPI%d\n",
			master->bus_num);
		ret = -ENOMEM;
		goto err;
	}
	//映射虚拟内存
	master_drv_data->base = ioremap(master_drv_data->res->start,
		master_drv_data->res->end - master_drv_data->res->start + 1);
	if (!master_drv_data->base) {
		dev_err(&pdev->dev, "invalid base address for CSPI%d\n",
			master->bus_num);
		ret = -EINVAL;
		goto err1;
	}
	//获取板级配置文件中设置的中断号
	master_drv_data->irq = platform_get_irq(pdev, 0);
	if (master_drv_data->irq < 0) {
		dev_err(&pdev->dev, "can't get IRQ for CSPI%d\n",
			master->bus_num);
		ret = -EINVAL;
		goto err1;
	}

	/* Register for SPI Interrupt */
	//注册中断函数mxc_spi_isr
	ret = request_irq(master_drv_data->irq, mxc_spi_isr,
			  0, "CSPI_IRQ", master_drv_data);
	if (ret != 0) {
		dev_err(&pdev->dev, "request_irq failed for CSPI%d\n",
			master->bus_num);
		goto err1;
	}

	master_drv_data->dev = &pdev->dev;
	/* Setup the DMA */
	//如果设置了dma,(这个在我的板级配置文件中未配置)
	master_drv_data->usedma = 0;
	res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
	if (res) {
		master_drv_data->dma_tx_id = res->start;
			master_drv_data->dma_tx_id = res->start;
		if (pdev->dev.dma_mask == NULL)
			dev_warn(&pdev->dev, "no dma mask\n");
		else
			master_drv_data->usedma = 1;
	}
	if (master_drv_data->usedma) {
		master_drv_data->dma_tx_ch =
			mxc_dma_request(master_drv_data->dma_tx_id, "mxc_spi");
		if (master_drv_data->dma_tx_ch < 0) {
			dev_info(&pdev->dev, "Can't allocate RX DMA ch\n");
			master_drv_data->usedma = 0;
			ret = -ENXIO;
			goto err_no_txdma;
		}
		mxc_dma_callback_set(master_drv_data->dma_tx_ch,
				mxc_spi_dma_tx_callback,
				(void *)master_drv_data);

		/* Allocate tmp_buf for tx_buf */
		master_drv_data->tmp_buf = kzalloc(SPI_BUFSIZ, GFP_KERNEL);
		if (master_drv_data->tmp_buf == NULL) {
			ret = -ENOMEM;
			goto err_tmp_buf_alloc;
		}
	}

	/* Setup any GPIO active */
	//配置SPI口IO,一般高级ARM芯片有多个SPI口,配置哪个由bus_num决定,bus_num是板级配置文件中设置
	gpio_spi_active(master->bus_num - 1);

	/* Enable the CSPI Clock, CSPI Module, set as a master */

	//i.mx536的SPI控制器的寄存器配置
	master_drv_data->ctrl_addr =
	    master_drv_data->base + master_drv_data->spi_ver_def->ctrl_reg_addr;
	master_drv_data->dma_addr =
	    master_drv_data->base + master_drv_data->spi_ver_def->dma_reg_addr;
	master_drv_data->stat_addr =
	    master_drv_data->base + master_drv_data->spi_ver_def->stat_reg_addr;
	master_drv_data->period_addr =
	    master_drv_data->base +
	    master_drv_data->spi_ver_def->period_reg_addr;
	master_drv_data->test_addr =
	    master_drv_data->base + master_drv_data->spi_ver_def->test_reg_addr;
	master_drv_data->reset_addr =
	    master_drv_data->base +
	    master_drv_data->spi_ver_def->reset_reg_addr;
	//开启SPI时钟
	master_drv_data->clk = clk_get(&pdev->dev, "cspi_clk");
	clk_enable(master_drv_data->clk);
	//获取时钟频率
	master_drv_data->spi_ipg_clk = clk_get_rate(master_drv_data->clk);

	
	__raw_writel(master_drv_data->spi_ver_def->reset_start,
		     master_drv_data->reset_addr);
	udelay(1);
	__raw_writel((master_drv_data->spi_ver_def->spi_enable +
		      master_drv_data->spi_ver_def->master_enable),
		     master_drv_data->base + MXC_CSPICTRL);
	__raw_writel(MXC_CSPIPERIOD_32KHZ, master_drv_data->period_addr);
	__raw_writel(0, MXC_CSPIINT + master_drv_data->ctrl_addr);

	if (master_drv_data->usedma) {
		/* Set water mark level to be the half of fifo_size in DMA */
		wml = master_drv_data->spi_ver_def->fifo_size / 2;
		wml = wml << master_drv_data->spi_ver_def->tx_wml_shift;
		__raw_writel((__raw_readl(master_drv_data->dma_addr)
				& ~master_drv_data->spi_ver_def->tx_wml_mask)
				| wml,
				master_drv_data->dma_addr);
	}
	/* Start the SPI Master Controller driver */
	//启动SPI控制器
	//最终调用spi_register_master来注册spi控制器
	ret = spi_bitbang_start(&master_drv_data->mxc_bitbang);

	if (ret != 0)
		goto err2;

	printk(KERN_INFO "CSPI: %s-%d probed\n", pdev->name, pdev->id);

#ifdef CONFIG_SPI_MXC_TEST_LOOPBACK
	{
		int i;
		struct spi_board_info *bi = &loopback_info[0];
		for (i = 0; i < ARRAY_SIZE(loopback_info); i++, bi++) {
			if (bi->bus_num != master->bus_num)
				continue;

			dev_info(&pdev->dev,
				 "registering loopback device '%s'\n",
				 bi->modalias);

			spi_new_device(master, bi);
		}
	}
#endif
	clk_disable(master_drv_data->clk);
	return ret;

      err2:
	gpio_spi_inactive(master->bus_num - 1);
	clk_disable(master_drv_data->clk);
	clk_put(master_drv_data->clk);
	if (master_drv_data->usedma)
		kfree(master_drv_data->tmp_buf);
err_tmp_buf_alloc:
	if (master_drv_data->usedma)
		mxc_dma_free(master_drv_data->dma_tx_ch);
err_no_txdma:
	free_irq(master_drv_data->irq, master_drv_data);
      err1:
	//最终调用free函数,释放内存
	iounmap(master_drv_data->base);
	release_mem_region(pdev->resource[0].start,
			   pdev->resource[0].end - pdev->resource[0].start + 1);
      err:
	spi_master_put(master);
	kfree(master);
	platform_set_drvdata(pdev, NULL);
	return ret;
}





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值