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

来源:互联网 发布:php pre match 编辑:程序博客网 时间:2024/06/05 09:31
//整个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_devicestatic 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到pdevplatform_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_LOOPBACKmaster->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_dataif (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_isrret = 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");elsemaster_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);}}#endifclk_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;}


	
				
		
原创粉丝点击