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;
}





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章