SPI驱动之主控制器驱动程序



嵌入式微处理器访问SPI设备有两种方式:使用GPIO模拟SPI接口的工作时序或者使用SPI控制器。使用GPIO模拟SPI接口的工作时序是非常容易实现的,但是会导致大量的时间耗费在模拟SPI接口的时序上,访问效率比较低,容易成为系统瓶颈。这里主要分析使用SPI控制器的情况。

在内核的drivers/spi/目录下有两个spi主控制器驱动程序:spi_s3c24xx.c和spi_s3c24xx_gpio.c其中spi_s3c24xx.c是基于s3c24xx下相应的spi接口的驱动程序,spi_s3c24xx_gpio.c运行用户指定3个gpio口分别充当spi_clk、spi_mosi和spi_miso接口,模拟标准的spi总线。UT4412BV01开发板预留了两路的spi接口(spi0和spi1),对于UT4412BV01开发板而言,使用的是spi_s3c64xx.c,也就是硬件SPI,不是软件SPI。注:下面是基于硬件SPI的spi1分析。

1. 定义platform device

kernel3.0.15/arch/arm/mach-exynos/dev-spi.c

  1. static struct resource exynos_spi1_resource[] = {  
  2.     [0] = {  
  3.         .start = EXYNOS_PA_SPI1,  
  4.         .end   = EXYNOS_PA_SPI1 + 0x100 - 1,  
  5.         .flags = IORESOURCE_MEM,  
  6.     },  
  7.     [1] = {  
  8.         .start = DMACH_SPI1_TX,  
  9.         .end   = DMACH_SPI1_TX,  
  10.         .flags = IORESOURCE_DMA,  
  11.     },  
  12.     [2] = {  
  13.         .start = DMACH_SPI1_RX,  
  14.         .end   = DMACH_SPI1_RX,  
  15.         .flags = IORESOURCE_DMA,  
  16.     },  
  17.     [3] = {  
  18.         .start = IRQ_SPI1,  
  19.         .end   = IRQ_SPI1,  
  20.         .flags = IORESOURCE_IRQ,  
  21.     },  
  22. };  
  23.   
  24. static struct s3c64xx_spi_info exynos_spi1_pdata = {  
  25.     .cfg_gpio = exynos_spi_cfg_gpio,  
  26.     .fifo_lvl_mask = 0x7f,  
  27.     .rx_lvl_offset = 15,  
  28.     .high_speed = 1,  
  29.     .clk_from_cmu = true,  
  30.     .tx_st_done = 25,  
  31. };  
  32.   
  33. struct platform_device exynos_device_spi1 = {  
  34.     .name         = "s3c64xx-spi",  
  35.     .id       = 1,  
  36.     .num_resources    = ARRAY_SIZE(exynos_spi1_resource),  
  37.     .resource     = exynos_spi1_resource,  
  38.     .dev = {  
  39.         .dma_mask       = &spi_dmamask,  
  40.         .coherent_dma_mask  = DMA_BIT_MASK(32),  
  41.         .platform_data = &exynos_spi1_pdata,  
  42.     },  
  43. };  
static struct resource exynos_spi1_resource[] = {
	[0] = {
		.start = EXYNOS_PA_SPI1,
		.end   = EXYNOS_PA_SPI1 + 0x100 - 1,
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = DMACH_SPI1_TX,
		.end   = DMACH_SPI1_TX,
		.flags = IORESOURCE_DMA,
	},
	[2] = {
		.start = DMACH_SPI1_RX,
		.end   = DMACH_SPI1_RX,
		.flags = IORESOURCE_DMA,
	},
	[3] = {
		.start = IRQ_SPI1,
		.end   = IRQ_SPI1,
		.flags = IORESOURCE_IRQ,
	},
};

static struct s3c64xx_spi_info exynos_spi1_pdata = {
	.cfg_gpio = exynos_spi_cfg_gpio,
	.fifo_lvl_mask = 0x7f,
	.rx_lvl_offset = 15,
	.high_speed = 1,
	.clk_from_cmu = true,
	.tx_st_done = 25,
};

struct platform_device exynos_device_spi1 = {
	.name		  = "s3c64xx-spi",
	.id		  = 1,
	.num_resources	  = ARRAY_SIZE(exynos_spi1_resource),
	.resource	  = exynos_spi1_resource,
	.dev = {
		.dma_mask		= &spi_dmamask,
		.coherent_dma_mask	= DMA_BIT_MASK(32),
		.platform_data = &exynos_spi1_pdata,
	},
};
exynos4412总共定义了三个spi控制器平台设备,实际上UT4412BV01开发板只预留了两个spi控制器(spi0和spi1)。platform设备给出了spi1接口的寄存器地址资源及IRQ资源。 注意其设备名为s3c64xx-spi

2. 定义platform driver

kernel3.0.15/drivers/spi/spi_s3c64xx.c

  1. static struct platform_driver s3c64xx_spi_driver = {  
  2.     .driver = {  
  3.         .name   = "s3c64xx-spi",  
  4.         .owner = THIS_MODULE,  
  5.     },  
  6.     .remove = s3c64xx_spi_remove,  
  7.     .suspend = s3c64xx_spi_suspend,  
  8.     .resume = s3c64xx_spi_resume,  
  9. };  
  10. MODULE_ALIAS("platform:s3c64xx-spi");  
  11.   
  12. static int __init s3c64xx_spi_init(void)  
  13. {  
  14. <span style="white-space: pre;">    </span>//设备不可热插拔,所以使用该函数,而不是platform_driver_register  
  15.     return platform_driver_probe(&s3c64xx_spi_driver, s3c64xx_spi_probe);  
  16. }  
  17. subsys_initcall(s3c64xx_spi_init);  
  18.   
  19. static void __exit s3c64xx_spi_exit(void)  
  20. {  
  21.     platform_driver_unregister(&s3c64xx_spi_driver);  
  22. }  
  23. module_exit(s3c64xx_spi_exit);  
  24.   
  25. MODULE_AUTHOR("Jaswinder Singh <jassi.brar@samsung.com>");  
  26. MODULE_DESCRIPTION("S3C64XX SPI Controller Driver");  
  27. MODULE_LICENSE("GPL");  
static struct platform_driver s3c64xx_spi_driver = {
	.driver = {
		.name	= "s3c64xx-spi",
		.owner = THIS_MODULE,
	},
	.remove = s3c64xx_spi_remove,
	.suspend = s3c64xx_spi_suspend,
	.resume = s3c64xx_spi_resume,
};
MODULE_ALIAS("platform:s3c64xx-spi");

static int __init s3c64xx_spi_init(void)
{
	//设备不可热插拔,所以使用该函数,而不是platform_driver_register
	return platform_driver_probe(&s3c64xx_spi_driver, s3c64xx_spi_probe);
}
subsys_initcall(s3c64xx_spi_init);

static void __exit s3c64xx_spi_exit(void)
{
	platform_driver_unregister(&s3c64xx_spi_driver);
}
module_exit(s3c64xx_spi_exit);

MODULE_AUTHOR("Jaswinder Singh <jassi.brar@samsung.com>");
MODULE_DESCRIPTION("S3C64XX SPI Controller Driver");
MODULE_LICENSE("GPL");
调用了platform_driver_probe注册platform驱动,注册完成以后将会调用platform的s3c64xx_spi_probe函数。 注意:platform驱动的name和platform device的name是相同的

3. s3c64xx_spi_probe函数

kernel3.0.15/drivers/spi/spi_s3c64xx.c

当exynos_device_spi1中的name与s3c64xx_spi_driver中的name相同时,也就是是设备名字跟驱动名字可以匹配,s3c64xx_spi_probe驱动探测函数被调用,该函数代码如下所示:

  1. static int __init s3c64xx_spi_probe(struct platform_device *pdev)  
  2. {  
  3.     struct resource *mem_res, *dmatx_res, *dmarx_res;  
  4.     struct s3c64xx_spi_driver_data *sdd;  
  5.     struct s3c64xx_spi_info *sci;  
  6.     struct spi_master *master;  
  7.     int ret;  
  8.   
  9.     if (pdev->id < 0) { //pdev->id = 1  
  10.         dev_err(&pdev->dev,  
  11.                 "Invalid platform device id-%d\n", pdev->id);  
  12.         return -ENODEV;  
  13.     }  
  14.   
  15.     if (pdev->dev.platform_data == NULL) { //pdev->dev.platform_data = &exynos_spi1_pdata  
  16.         dev_err(&pdev->dev, "platform_data missing!\n");  
  17.         return -ENODEV;  
  18.     }  
  19.   
  20.     sci = pdev->dev.platform_data;  
  21.     if (!sci->src_clk_name) { //在板级文件中通过调用s3c64xx_spi_set_info()来初始化  
  22.         dev_err(&pdev->dev,  
  23.             "Board init must call s3c64xx_spi_set_info()\n");  
  24.         return -EINVAL;  
  25.     }  
  26.   
  27.     /* Check for availability of necessary resource */  
  28.   
  29.     //获取DMA0资源  
  30.     dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);  
  31.     if (dmatx_res == NULL) {  
  32.         dev_err(&pdev->dev, "Unable to get SPI-Tx dma resource\n");  
  33.         return -ENXIO;  
  34.     }  
  35.   
  36.     //获取DMA1资源  
  37.     dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);  
  38.     if (dmarx_res == NULL) {  
  39.         dev_err(&pdev->dev, "Unable to get SPI-Rx dma resource\n");  
  40.         return -ENXIO;  
  41.     }  
  42.   
  43.     //获取IO内存资源  
  44.     mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  45.     if (mem_res == NULL) {  
  46.         dev_err(&pdev->dev, "Unable to get SPI MEM resource\n");  
  47.         return -ENXIO;  
  48.     }  
  49.   
  50.     /**  
  51.      * 通过跟踪spi_alloc_master相关源码可知, 
  52.      * 此处分配struct spi_master + struct s3c64xx_spi_driver_data大小的数据, 
  53.      * 把s3c64xx_spi_driver_data设为spi_master的私有数据  
  54.      */  
  55.     master = spi_alloc_master(&pdev->dev,  
  56.                 sizeof(struct s3c64xx_spi_driver_data));  
  57.     if (master == NULL) {  
  58.         dev_err(&pdev->dev, "Unable to allocate SPI Master\n");  
  59.         return -ENOMEM;  
  60.     }  
  61.   
  62.     /** 
  63.      * platform_set_drvdata 和 platform_get_drvdata 
  64.      * probe函数中定义的局部变量,如果我想在其他地方使用它怎么办呢? 
  65.      * 这就需要把它保存起来。内核提供了这个方法, 
  66.      * 使用函数platform_set_drvdata()可以将master保存成平台总线设备的私有数据。 
  67.      * 以后再要使用它时只需调用platform_get_drvdata()就可以了。 
  68.      */  
  69.     platform_set_drvdata(pdev, master);  
  70.   
  71.     //从master中获得s3c64xx_spi_driver_data,并初始化相关成员  
  72.     sdd = spi_master_get_devdata(master);  
  73.     sdd->master = master;  
  74.     sdd->cntrlr_info = sci;  
  75.     sdd->pdev = pdev;  
  76.     sdd->sfr_start = mem_res->start;  
  77.     sdd->tx_dmach = dmatx_res->start;  
  78.     sdd->rx_dmach = dmarx_res->start;  
  79.   
  80.     sdd->cur_bpw = 8;  
  81.   
  82.     //master相关成员的初始化  
  83.     master->bus_num = pdev->id; //总线号  
  84.     master->setup = s3c64xx_spi_setup;  
  85.     master->transfer = s3c64xx_spi_transfer;  
  86.     master->num_chipselect = sci->num_cs; //该总线上的设备数  
  87.     master->dma_alignment = 8;  
  88.     /* the spi->mode bits understood by this driver: */  
  89.     master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; //mode_3  
  90.   
  91.     //申请IO内存  
  92.     if (request_mem_region(mem_res->start,  
  93.             resource_size(mem_res), pdev->name) == NULL) {  
  94.         dev_err(&pdev->dev, "Req mem region failed\n");  
  95.         ret = -ENXIO;  
  96.         goto err0;  
  97.     }  
  98.   
  99.     //建立映射  
  100.     sdd->regs = ioremap(mem_res->start, resource_size(mem_res));  
  101.     if (sdd->regs == NULL) {  
  102.         dev_err(&pdev->dev, "Unable to remap IO\n");  
  103.         ret = -ENXIO;  
  104.         goto err1;  
  105.     }  
  106.   
  107.     //SPI的IO管脚配置,将相应的IO管脚设置为SPI功能  
  108.     if (sci->cfg_gpio == NULL || sci->cfg_gpio(pdev)) {  
  109.         dev_err(&pdev->dev, "Unable to config gpio\n");  
  110.         ret = -EBUSY;  
  111.         goto err2;  
  112.     }  
  113.   
  114.     //使能时钟  
  115.     sdd->clk = clk_get(&pdev->dev, "spi");  
  116.     if (IS_ERR(sdd->clk)) {  
  117.         dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n");  
  118.         ret = PTR_ERR(sdd->clk);  
  119.         goto err3;  
  120.     }  
  121.   
  122.     if (clk_enable(sdd->clk)) {  
  123.         dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n");  
  124.         ret = -EBUSY;  
  125.         goto err4;  
  126.     }  
  127.   
  128.     sdd->src_clk = clk_get(&pdev->dev, sci->src_clk_name);  
  129.     if (IS_ERR(sdd->src_clk)) {  
  130.         dev_err(&pdev->dev,  
  131.             "Unable to acquire clock '%s'\n", sci->src_clk_name);  
  132.         ret = PTR_ERR(sdd->src_clk);  
  133.         goto err5;  
  134.     }  
  135.   
  136.     if (clk_enable(sdd->src_clk)) {  
  137.         dev_err(&pdev->dev, "Couldn't enable clock '%s'\n",  
  138.                             sci->src_clk_name);  
  139.         ret = -EBUSY;  
  140.         goto err6;  
  141.     }  
  142.   
  143.     //创建单个线程的工作队列,用于数据收发操作  
  144.     sdd->workqueue = create_singlethread_workqueue(  
  145.                         dev_name(master->dev.parent));  
  146.     if (sdd->workqueue == NULL) {  
  147.         dev_err(&pdev->dev, "Unable to create workqueue\n");  
  148.         ret = -ENOMEM;  
  149.         goto err7;  
  150.     }  
  151.   
  152.     //硬件初始化,初始化设置寄存器,包括对SPIMOSI、SPIMISO、SPICLK引脚的设置  
  153.     s3c64xx_spi_hwinit(sdd, pdev->id);  
  154.   
  155.     //锁、工作队列等初始化  
  156.     spin_lock_init(&sdd->lock);  
  157.     init_completion(&sdd->xfer_completion);  
  158.     INIT_WORK(&sdd->work, s3c64xx_spi_work);  
  159.     INIT_LIST_HEAD(&sdd->queue);  
  160.   
  161.     if (spi_register_master(master)) {  
  162.         dev_err(&pdev->dev, "cannot register SPI master\n");  
  163.         ret = -EBUSY;  
  164.         goto err8;  
  165.     }  
  166.   
  167.     dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d "  
  168.                     "with %d Slaves attached\n",  
  169.                     pdev->id, master->num_chipselect);  
  170.     dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n",  
  171.                     mem_res->end, mem_res->start,  
  172.                     sdd->rx_dmach, sdd->tx_dmach);  
  173.   
  174.     return 0;  
  175.   
  176. err8:  
  177.     destroy_workqueue(sdd->workqueue);  
  178. err7:  
  179.     clk_disable(sdd->src_clk);  
  180. err6:  
  181.     clk_put(sdd->src_clk);  
  182. err5:  
  183.     clk_disable(sdd->clk);  
  184. err4:  
  185.     clk_put(sdd->clk);  
  186. err3:  
  187. err2:  
  188.     iounmap((void *) sdd->regs);  
  189. err1:  
  190.     release_mem_region(mem_res->start, resource_size(mem_res));  
  191. err0:  
  192.     platform_set_drvdata(pdev, NULL);  
  193.     spi_master_put(master);  
  194.   
  195.     return ret;  
  196. }  
static int __init s3c64xx_spi_probe(struct platform_device *pdev)
{
	struct resource	*mem_res, *dmatx_res, *dmarx_res;
	struct s3c64xx_spi_driver_data *sdd;
	struct s3c64xx_spi_info *sci;
	struct spi_master *master;
	int ret;

	if (pdev->id < 0) { //pdev->id = 1
		dev_err(&pdev->dev,
				"Invalid platform device id-%d\n", pdev->id);
		return -ENODEV;
	}

	if (pdev->dev.platform_data == NULL) { //pdev->dev.platform_data = &exynos_spi1_pdata
		dev_err(&pdev->dev, "platform_data missing!\n");
		return -ENODEV;
	}

	sci = pdev->dev.platform_data;
	if (!sci->src_clk_name) { //在板级文件中通过调用s3c64xx_spi_set_info()来初始化
		dev_err(&pdev->dev,
			"Board init must call s3c64xx_spi_set_info()\n");
		return -EINVAL;
	}

	/* Check for availability of necessary resource */

	//获取DMA0资源
	dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
	if (dmatx_res == NULL) {
		dev_err(&pdev->dev, "Unable to get SPI-Tx dma resource\n");
		return -ENXIO;
	}

	//获取DMA1资源
	dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
	if (dmarx_res == NULL) {
		dev_err(&pdev->dev, "Unable to get SPI-Rx dma resource\n");
		return -ENXIO;
	}

	//获取IO内存资源
	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (mem_res == NULL) {
		dev_err(&pdev->dev, "Unable to get SPI MEM resource\n");
		return -ENXIO;
	}

	/** 
	 * 通过跟踪spi_alloc_master相关源码可知,
	 * 此处分配struct spi_master + struct s3c64xx_spi_driver_data大小的数据,
	 * 把s3c64xx_spi_driver_data设为spi_master的私有数据 
	 */
	master = spi_alloc_master(&pdev->dev,
				sizeof(struct s3c64xx_spi_driver_data));
	if (master == NULL) {
		dev_err(&pdev->dev, "Unable to allocate SPI Master\n");
		return -ENOMEM;
	}

	/**
	 * platform_set_drvdata 和 platform_get_drvdata
	 * probe函数中定义的局部变量,如果我想在其他地方使用它怎么办呢?
	 * 这就需要把它保存起来。内核提供了这个方法,
	 * 使用函数platform_set_drvdata()可以将master保存成平台总线设备的私有数据。
	 * 以后再要使用它时只需调用platform_get_drvdata()就可以了。
	 */
	platform_set_drvdata(pdev, master);

	//从master中获得s3c64xx_spi_driver_data,并初始化相关成员
	sdd = spi_master_get_devdata(master);
	sdd->master = master;
	sdd->cntrlr_info = sci;
	sdd->pdev = pdev;
	sdd->sfr_start = mem_res->start;
	sdd->tx_dmach = dmatx_res->start;
	sdd->rx_dmach = dmarx_res->start;

	sdd->cur_bpw = 8;

	//master相关成员的初始化
	master->bus_num = pdev->id; //总线号
	master->setup = s3c64xx_spi_setup;
	master->transfer = s3c64xx_spi_transfer;
	master->num_chipselect = sci->num_cs; //该总线上的设备数
	master->dma_alignment = 8;
	/* the spi->mode bits understood by this driver: */
	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; //mode_3

	//申请IO内存
	if (request_mem_region(mem_res->start,
			resource_size(mem_res), pdev->name) == NULL) {
		dev_err(&pdev->dev, "Req mem region failed\n");
		ret = -ENXIO;
		goto err0;
	}

	//建立映射
	sdd->regs = ioremap(mem_res->start, resource_size(mem_res));
	if (sdd->regs == NULL) {
		dev_err(&pdev->dev, "Unable to remap IO\n");
		ret = -ENXIO;
		goto err1;
	}

	//SPI的IO管脚配置,将相应的IO管脚设置为SPI功能
	if (sci->cfg_gpio == NULL || sci->cfg_gpio(pdev)) {
		dev_err(&pdev->dev, "Unable to config gpio\n");
		ret = -EBUSY;
		goto err2;
	}

	//使能时钟
	sdd->clk = clk_get(&pdev->dev, "spi");
	if (IS_ERR(sdd->clk)) {
		dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n");
		ret = PTR_ERR(sdd->clk);
		goto err3;
	}

	if (clk_enable(sdd->clk)) {
		dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n");
		ret = -EBUSY;
		goto err4;
	}

	sdd->src_clk = clk_get(&pdev->dev, sci->src_clk_name);
	if (IS_ERR(sdd->src_clk)) {
		dev_err(&pdev->dev,
			"Unable to acquire clock '%s'\n", sci->src_clk_name);
		ret = PTR_ERR(sdd->src_clk);
		goto err5;
	}

	if (clk_enable(sdd->src_clk)) {
		dev_err(&pdev->dev, "Couldn't enable clock '%s'\n",
							sci->src_clk_name);
		ret = -EBUSY;
		goto err6;
	}

	//创建单个线程的工作队列,用于数据收发操作
	sdd->workqueue = create_singlethread_workqueue(
						dev_name(master->dev.parent));
	if (sdd->workqueue == NULL) {
		dev_err(&pdev->dev, "Unable to create workqueue\n");
		ret = -ENOMEM;
		goto err7;
	}

	//硬件初始化,初始化设置寄存器,包括对SPIMOSI、SPIMISO、SPICLK引脚的设置
	s3c64xx_spi_hwinit(sdd, pdev->id);

	//锁、工作队列等初始化
	spin_lock_init(&sdd->lock);
	init_completion(&sdd->xfer_completion);
	INIT_WORK(&sdd->work, s3c64xx_spi_work);
	INIT_LIST_HEAD(&sdd->queue);

	if (spi_register_master(master)) {
		dev_err(&pdev->dev, "cannot register SPI master\n");
		ret = -EBUSY;
		goto err8;
	}

	dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d "
					"with %d Slaves attached\n",
					pdev->id, master->num_chipselect);
	dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n",
					mem_res->end, mem_res->start,
					sdd->rx_dmach, sdd->tx_dmach);

	return 0;

err8:
	destroy_workqueue(sdd->workqueue);
err7:
	clk_disable(sdd->src_clk);
err6:
	clk_put(sdd->src_clk);
err5:
	clk_disable(sdd->clk);
err4:
	clk_put(sdd->clk);
err3:
err2:
	iounmap((void *) sdd->regs);
err1:
	release_mem_region(mem_res->start, resource_size(mem_res));
err0:
	platform_set_drvdata(pdev, NULL);
	spi_master_put(master);

	return ret;
}
s3c64xx_spi_probe函数很长,但做的事情却很简单,从上面代码的注释可以基本理清整个探测流程。其中用到几个比较重要的函数,下面来一一解释。

spi_alloc_master(kernel3.0.15/drivers/spi/spi.c)

spi_alloc_master函数用于请求分配一个spi_master。

  1. struct spi_master *spi_alloc_master(struct device *dev, unsigned size)  
  2. {  
  3.     struct spi_master   *master;  
  4.   
  5.     if (!dev)  
  6.         return NULL;  
  7.   
  8.     /* 分配内存,分配的内存大小是*master + size,包含了两部分内存 */  
  9.     master = kzalloc(size + sizeof *master, GFP_KERNEL);  
  10.     if (!master)  
  11.         return NULL;  
  12.   
  13.     device_initialize(&master->dev); //设备模型中的初始设备函数  
  14.     master->dev.class = &spi_master_class; //spi_master_class在SPI子系统初始化的时候就已经注册好了  
  15.     master->dev.parent = get_device(dev); //设备当前设备的父设备,这与设备模型相关  
  16.     spi_master_set_devdata(master, &master[1]); //&master[1]就是master之后的另一部分内存的起始地址  
  17.   
  18.     return master;  
  19. }  
struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
{
	struct spi_master	*master;

	if (!dev)
		return NULL;

	/* 分配内存,分配的内存大小是*master + size,包含了两部分内存 */
	master = kzalloc(size + sizeof *master, GFP_KERNEL);
	if (!master)
		return NULL;

	device_initialize(&master->dev); //设备模型中的初始设备函数
	master->dev.class = &spi_master_class; //spi_master_class在SPI子系统初始化的时候就已经注册好了
	master->dev.parent = get_device(dev); //设备当前设备的父设备,这与设备模型相关
	spi_master_set_devdata(master, &master[1]); //&master[1]就是master之后的另一部分内存的起始地址

	return master;
}

spi_master_register(kernel3.0.15/drivers/spi/spi.c)

spi_master_register函数用于向内核注册一个spi_master。

  1. int spi_register_master(struct spi_master *master)  
  2. {  
  3.     static atomic_t     dyn_bus_id = ATOMIC_INIT((1<<15) - 1);  
  4.     struct device       *dev = master->dev.parent;  
  5.     struct boardinfo    *bi;  
  6.     int         status = -ENODEV;  
  7.     int         dynamic = 0;  
  8.   
  9.     if (!dev)  
  10.         return -ENODEV;  
  11.   
  12.     /* even if it's just one always-selected device, there must 
  13.      * be at least one chipselect 
  14.      */  
  15.     if (master->num_chipselect == 0) //一个SPI控制器至少有一个片选,因此片选数为0则出错  
  16.         return -EINVAL;  
  17.   
  18.     /* convention:  dynamically assigned bus IDs count down from the max */  
  19.     if (master->bus_num < 0) { //如果总线号小于0则动态分配一个总线号  
  20.         /* FIXME switch to an IDR based scheme, something like 
  21.          * I2C now uses, so we can't run out of "dynamic" IDs 
  22.          */  
  23.         master->bus_num = atomic_dec_return(&dyn_bus_id);  
  24.         dynamic = 1;  
  25.     }  
  26.   
  27.     spin_lock_init(&master->bus_lock_spinlock);  
  28.     mutex_init(&master->bus_lock_mutex);  
  29.     master->bus_lock_flag = 0;  
  30.   
  31.     /* register the device, then userspace will see it. 
  32.      * registration fails if the bus ID is in use. 
  33.      */  
  34.     dev_set_name(&master->dev, "spi%u", master->bus_num); //把master加入到设备模型中  
  35.     status = device_add(&master->dev);  
  36.     if (status < 0)  
  37.         goto done;  
  38.     dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),  
  39.             dynamic ? " (dynamic)" : "");  
  40.   
  41.     mutex_lock(&board_lock);  
  42.     list_add_tail(&master->list, &spi_master_list);  
  43.     list_for_each_entry(bi, &board_list, list) //遍历board_list这个链表  
  44.         spi_match_master_to_boardinfo(master, &bi->board_info);  
  45.     mutex_unlock(&board_lock);  
  46.   
  47.     status = 0;  
  48.   
  49.     /* Register devices from the device tree */  
  50.     of_register_spi_devices(master);  
  51. done:  
  52.     return status;  
  53. }  
int spi_register_master(struct spi_master *master)
{
	static atomic_t		dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
	struct device		*dev = master->dev.parent;
	struct boardinfo	*bi;
	int			status = -ENODEV;
	int			dynamic = 0;

	if (!dev)
		return -ENODEV;

	/* even if it's just one always-selected device, there must
	 * be at least one chipselect
	 */
	if (master->num_chipselect == 0) //一个SPI控制器至少有一个片选,因此片选数为0则出错
		return -EINVAL;

	/* convention:  dynamically assigned bus IDs count down from the max */
	if (master->bus_num < 0) { //如果总线号小于0则动态分配一个总线号
		/* FIXME switch to an IDR based scheme, something like
		 * I2C now uses, so we can't run out of "dynamic" IDs
		 */
		master->bus_num = atomic_dec_return(&dyn_bus_id);
		dynamic = 1;
	}

	spin_lock_init(&master->bus_lock_spinlock);
	mutex_init(&master->bus_lock_mutex);
	master->bus_lock_flag = 0;

	/* register the device, then userspace will see it.
	 * registration fails if the bus ID is in use.
	 */
	dev_set_name(&master->dev, "spi%u", master->bus_num); //把master加入到设备模型中
	status = device_add(&master->dev);
	if (status < 0)
		goto done;
	dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),
			dynamic ? " (dynamic)" : "");

	mutex_lock(&board_lock);
	list_add_tail(&master->list, &spi_master_list);
	list_for_each_entry(bi, &board_list, list) //遍历board_list这个链表
		spi_match_master_to_boardinfo(master, &bi->board_info);
	mutex_unlock(&board_lock);

	status = 0;

	/* Register devices from the device tree */
	of_register_spi_devices(master);
done:
	return status;
}

spi_match_master_to_boardinfo(kernel3.0.15/drivers/spi/spi.c)

  1. static void spi_match_master_to_boardinfo(struct spi_master *master,  
  2.                 struct spi_board_info *bi)  
  3. {  
  4.     struct spi_device *dev;  
  5.   
  6.     if (master->bus_num != bi->bus_num) //每找到一个成员就将它的总线号与master的总线号进行比较,如果相等则调用spi_new_device函数创建一个spi设备  
  7.         return;  
  8.   
  9.     dev = spi_new_device(master, bi);  
  10.     if (!dev)  
  11.         dev_err(master->dev.parent, "can't create new device for %s\n",  
  12.             bi->modalias);  
  13. }  
static void spi_match_master_to_boardinfo(struct spi_master *master,
				struct spi_board_info *bi)
{
	struct spi_device *dev;

	if (master->bus_num != bi->bus_num) //每找到一个成员就将它的总线号与master的总线号进行比较,如果相等则调用spi_new_device函数创建一个spi设备
		return;

	dev = spi_new_device(master, bi);
	if (!dev)
		dev_err(master->dev.parent, "can't create new device for %s\n",
			bi->modalias);
}

spi_new_device(kernel3.0.15/drivers/spi/spi.c)

  1. struct spi_device *spi_new_device(struct spi_master *master,  
  2.                   struct spi_board_info *chip)  
  3. {  
  4.     struct spi_device   *proxy;  
  5.     int         status;  
  6.   
  7.     /* NOTE:  caller did any chip->bus_num checks necessary. 
  8.      * 
  9.      * Also, unless we change the return value convention to use 
  10.      * error-or-pointer (not NULL-or-pointer), troubleshootability 
  11.      * suggests syslogged diagnostics are best here (ugh). 
  12.      */  
  13.   
  14.     proxy = spi_alloc_device(master);  
  15.     if (!proxy)  
  16.         return NULL;  
  17.   
  18.     WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));  
  19.   
  20.     proxy->chip_select = chip->chip_select;  
  21.     proxy->max_speed_hz = chip->max_speed_hz;  
  22.     proxy->mode = chip->mode;  
  23.     proxy->irq = chip->irq;  
  24.     strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias)); //此处比较关键,设备名字拷贝  
  25.     proxy->dev.platform_data = (void *) chip->platform_data;  
  26.     proxy->controller_data = chip->controller_data;  
  27.     proxy->controller_state = NULL;  
  28.   
  29.     status = spi_add_device(proxy);  
  30.     if (status < 0) {  
  31.         spi_dev_put(proxy);  
  32.         return NULL;  
  33.     }  
  34.   
  35.     return proxy;  
  36. }  
struct spi_device *spi_new_device(struct spi_master *master,
				  struct spi_board_info *chip)
{
	struct spi_device	*proxy;
	int			status;

	/* NOTE:  caller did any chip->bus_num checks necessary.
	 *
	 * Also, unless we change the return value convention to use
	 * error-or-pointer (not NULL-or-pointer), troubleshootability
	 * suggests syslogged diagnostics are best here (ugh).
	 */

	proxy = spi_alloc_device(master);
	if (!proxy)
		return NULL;

	WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));

	proxy->chip_select = chip->chip_select;
	proxy->max_speed_hz = chip->max_speed_hz;
	proxy->mode = chip->mode;
	proxy->irq = chip->irq;
	strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias)); //此处比较关键,设备名字拷贝
	proxy->dev.platform_data = (void *) chip->platform_data;
	proxy->controller_data = chip->controller_data;
	proxy->controller_state = NULL;

	status = spi_add_device(proxy);
	if (status < 0) {
		spi_dev_put(proxy);
		return NULL;
	}

	return proxy;
}
spi_alloc_device(kernel3.0.15/drivers/spi/spi.c)
  1. struct spi_device *spi_alloc_device(struct spi_master *master)  
  2. {  
  3.     struct spi_device   *spi;  
  4.     struct device       *dev = master->dev.parent;  
  5.   
  6.     if (!spi_master_get(master)) //错误检测  
  7.         return NULL;  
  8.   
  9.     spi = kzalloc(sizeof *spi, GFP_KERNEL); //分配内存  
  10.     if (!spi) {  
  11.         dev_err(dev, "cannot alloc spi_device\n");  
  12.         spi_master_put(master);  
  13.         return NULL;  
  14.     }  
  15.   
  16.     spi->master = master;  
  17.     spi->dev.parent = dev;  
  18.     spi->dev.bus = &spi_bus_type; //该spi设备属于SPI子系统初始化时注册的叫“spi”的总线  
  19.     spi->dev.release = spidev_release;  
  20.     device_initialize(&spi->dev); //设备模型方面的初始化  
  21.     return spi;  
  22. }  
struct spi_device *spi_alloc_device(struct spi_master *master)
{
	struct spi_device	*spi;
	struct device		*dev = master->dev.parent;

	if (!spi_master_get(master)) //错误检测
		return NULL;

	spi = kzalloc(sizeof *spi, GFP_KERNEL); //分配内存
	if (!spi) {
		dev_err(dev, "cannot alloc spi_device\n");
		spi_master_put(master);
		return NULL;
	}

	spi->master = master;
	spi->dev.parent = dev;
	spi->dev.bus = &spi_bus_type; //该spi设备属于SPI子系统初始化时注册的叫“spi”的总线
	spi->dev.release = spidev_release;
	device_initialize(&spi->dev); //设备模型方面的初始化
	return spi;
}
spi_add_device(kernel3.0.15/drivers/spi/spi.c)
  1. int spi_add_device(struct spi_device *spi)  
  2. {  
  3.     static DEFINE_MUTEX(spi_add_lock);  
  4.     struct device *dev = spi->master->dev.parent;  
  5.     struct device *d;  
  6.     int status;  
  7.   
  8.     /* Chipselects are numbered 0..max; validate. */  
  9.     if (spi->chip_select >= spi->master->num_chipselect) { //片选号是从0开始的,如果大于或者等于片选数的话则返回出错  
  10.         dev_err(dev, "cs%d >= max %d\n",  
  11.             spi->chip_select,  
  12.             spi->master->num_chipselect);  
  13.         return -EINVAL;  
  14.     }  
  15.   
  16.     /* Set the bus ID string */  
  17.     dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),   
  18.             spi->chip_select);  
  19.   
  20.   
  21.     /* We need to make sure there's no other device with this 
  22.      * chipselect **BEFORE** we call setup(), else we'll trash 
  23.      * its configuration.  Lock against concurrent add() calls. 
  24.      */  
  25.     mutex_lock(&spi_add_lock);  
  26.   
  27.     d = bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev)); //遍历spi总线,看是否已经注册过该设备  
  28.     if (d != NULL) {  
  29.         dev_err(dev, "chipselect %d already in use\n",  
  30.                 spi->chip_select);  
  31.         put_device(d);  
  32.         status = -EBUSY;  
  33.         goto done;  
  34.     }  
  35.   
  36.     /* Drivers may modify this initial i/o setup, but will 
  37.      * normally rely on the device being setup.  Devices 
  38.      * using SPI_CS_HIGH can't coexist well otherwise... 
  39.      */  
  40.     status = spi_setup(spi);  
  41.     if (status < 0) {  
  42.         dev_err(dev, "can't setup %s, status %d\n",  
  43.                 dev_name(&spi->dev), status);  
  44.         goto done;  
  45.     }  
  46.   
  47.     /* Device may be bound to an active driver when this returns */  
  48.     status = device_add(&spi->dev);  
  49.     if (status < 0)  
  50.         dev_err(dev, "can't add %s, status %d\n",  
  51.                 dev_name(&spi->dev), status);  
  52.     else  
  53.         dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev));  
  54.   
  55. done:  
  56.     mutex_unlock(&spi_add_lock);  
  57.     return status;  
  58. }  
int spi_add_device(struct spi_device *spi)
{
	static DEFINE_MUTEX(spi_add_lock);
	struct device *dev = spi->master->dev.parent;
	struct device *d;
	int status;

	/* Chipselects are numbered 0..max; validate. */
	if (spi->chip_select >= spi->master->num_chipselect) { //片选号是从0开始的,如果大于或者等于片选数的话则返回出错
		dev_err(dev, "cs%d >= max %d\n",
			spi->chip_select,
			spi->master->num_chipselect);
		return -EINVAL;
	}

	/* Set the bus ID string */
	dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev), 
			spi->chip_select);


	/* We need to make sure there's no other device with this
	 * chipselect **BEFORE** we call setup(), else we'll trash
	 * its configuration.  Lock against concurrent add() calls.
	 */
	mutex_lock(&spi_add_lock);

	d = bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev)); //遍历spi总线,看是否已经注册过该设备
	if (d != NULL) {
		dev_err(dev, "chipselect %d already in use\n",
				spi->chip_select);
		put_device(d);
		status = -EBUSY;
		goto done;
	}

	/* Drivers may modify this initial i/o setup, but will
	 * normally rely on the device being setup.  Devices
	 * using SPI_CS_HIGH can't coexist well otherwise...
	 */
	status = spi_setup(spi);
	if (status < 0) {
		dev_err(dev, "can't setup %s, status %d\n",
				dev_name(&spi->dev), status);
		goto done;
	}

	/* Device may be bound to an active driver when this returns */
	status = device_add(&spi->dev);
	if (status < 0)
		dev_err(dev, "can't add %s, status %d\n",
				dev_name(&spi->dev), status);
	else
		dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev));

done:
	mutex_unlock(&spi_add_lock);
	return status;
}
spi_setup(kernel3.0.15/drivers/spi/spi.c)
  1. int spi_setup(struct spi_device *spi)  
  2. {  
  3.     unsigned    bad_bits;  
  4.     int     status;  
  5.   
  6.     /* help drivers fail *cleanly* when they need options 
  7.      * that aren't supported with their current master 
  8.      */  
  9.     bad_bits = spi->mode & ~spi->master->mode_bits; //如果驱动不支持该设备的工作模式则返回出错  
  10.     if (bad_bits) {  
  11.         dev_err(&spi->dev, "setup: unsupported mode bits %x\n",  
  12.             bad_bits);  
  13.         return -EINVAL;  
  14.     }  
  15.   
  16.     if (!spi->bits_per_word)  
  17.         spi->bits_per_word = 8;  
  18.   
  19.     status = spi->master->setup(spi); //调用控制器驱动里的s3c64xx_spi_setup函数  
  20.   
  21.     dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s"  
  22.                 "%u bits/w, %u Hz max --> %d\n",  
  23.             (int) (spi->mode & (SPI_CPOL | SPI_CPHA)),  
  24.             (spi->mode & SPI_CS_HIGH) ? "cs_high, " : "",  
  25.             (spi->mode & SPI_LSB_FIRST) ? "lsb, " : "",  
  26.             (spi->mode & SPI_3WIRE) ? "3wire, " : "",  
  27.             (spi->mode & SPI_LOOP) ? "loopback, " : "",  
  28.             spi->bits_per_word, spi->max_speed_hz,  
  29.             status);  
  30.   
  31.     return status;  
  32. }  
int spi_setup(struct spi_device *spi)
{
	unsigned	bad_bits;
	int		status;

	/* help drivers fail *cleanly* when they need options
	 * that aren't supported with their current master
	 */
	bad_bits = spi->mode & ~spi->master->mode_bits; //如果驱动不支持该设备的工作模式则返回出错
	if (bad_bits) {
		dev_err(&spi->dev, "setup: unsupported mode bits %x\n",
			bad_bits);
		return -EINVAL;
	}

	if (!spi->bits_per_word)
		spi->bits_per_word = 8;

	status = spi->master->setup(spi); //调用控制器驱动里的s3c64xx_spi_setup函数

	dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s"
				"%u bits/w, %u Hz max --> %d\n",
			(int) (spi->mode & (SPI_CPOL | SPI_CPHA)),
			(spi->mode & SPI_CS_HIGH) ? "cs_high, " : "",
			(spi->mode & SPI_LSB_FIRST) ? "lsb, " : "",
			(spi->mode & SPI_3WIRE) ? "3wire, " : "",
			(spi->mode & SPI_LOOP) ? "loopback, " : "",
			spi->bits_per_word, spi->max_speed_hz,
			status);

	return status;
}

s3c64xx_spi_setup(kernel3.0.15/drivers/spi/spi_s3c64xx.c)

  1. static int s3c64xx_spi_setup(struct spi_device *spi)  
  2. {  
  3.     //由此可知在实例化struct spi_board_info时,其controller_data成员就应该指向struct s3c64xx_spi_csinfo的对象  
  4.     struct s3c64xx_spi_csinfo *cs = spi->controller_data;   
  5.     struct s3c64xx_spi_driver_data *sdd;  
  6.     struct s3c64xx_spi_info *sci;  
  7.     struct spi_message *msg;  
  8.     unsigned long flags;  
  9.     int err = 0;  
  10.   
  11.     if (cs == NULL || cs->set_level == NULL) {  
  12.         dev_err(&spi->dev, "No CS for SPI(%d)\n", spi->chip_select);  
  13.         return -ENODEV;  
  14.     }  
  15.   
  16.     sdd = spi_master_get_devdata(spi->master);  
  17.     sci = sdd->cntrlr_info;  
  18.   
  19.     spin_lock_irqsave(&sdd->lock, flags);  
  20.   
  21.     list_for_each_entry(msg, &sdd->queue, queue) {  
  22.         /* Is some mssg is already queued for this device */  
  23.         if (msg->spi == spi) {  
  24.             dev_err(&spi->dev,  
  25.                 "setup: attempt while mssg in queue!\n");  
  26.             spin_unlock_irqrestore(&sdd->lock, flags);  
  27.             return -EBUSY;  
  28.         }  
  29.     }  
  30.   
  31.     if (sdd->state & SUSPND) {  
  32.         spin_unlock_irqrestore(&sdd->lock, flags);  
  33.         dev_err(&spi->dev,  
  34.             "setup: SPI-%d not active!\n", spi->master->bus_num);  
  35.         return -ESHUTDOWN;  
  36.     }  
  37.   
  38.     spin_unlock_irqrestore(&sdd->lock, flags);  
  39.   
  40.     if (spi->bits_per_word != 8  
  41.             && spi->bits_per_word != 16  
  42.             && spi->bits_per_word != 32) {  
  43.         dev_err(&spi->dev, "setup: %dbits/wrd not supported!\n",  
  44.                             spi->bits_per_word);  
  45.         err = -EINVAL;  
  46.         goto setup_exit;  
  47.     }  
  48.   
  49.     /* Check if we can provide the requested rate */  
  50.     if (!sci->clk_from_cmu) {  
  51.         u32 psr, speed;  
  52.   
  53.         /* Max possible */  
  54.         speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1);  
  55.   
  56.         if (spi->max_speed_hz > speed)  
  57.             spi->max_speed_hz = speed;  
  58.   
  59.         psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1;  
  60.         psr &= S3C64XX_SPI_PSR_MASK;  
  61.         if (psr == S3C64XX_SPI_PSR_MASK)  
  62.             psr--;  
  63.   
  64.         speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);  
  65.         if (spi->max_speed_hz < speed) {  
  66.             if (psr+1 < S3C64XX_SPI_PSR_MASK) {  
  67.                 psr++;  
  68.             } else {  
  69.                 err = -EINVAL;  
  70.                 goto setup_exit;  
  71.             }  
  72.         }  
  73.   
  74.         speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);  
  75.         if (spi->max_speed_hz >= speed)  
  76.             spi->max_speed_hz = speed;  
  77.         else  
  78.             err = -EINVAL;  
  79.     }  
  80.   
  81. setup_exit:  
  82.   
  83.     /* setup() returns with device de-selected */  
  84.     disable_cs(sdd, spi);  
  85.   
  86.     return err;  
  87. }  

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TMC5160是一款先进的步进电机驱动芯片,具有高度集成、高性能和可编程性等特点。而SPI(串行外设接口)是一种通信协议,用于在微控制器和外部设备之间进行数据传输。 TMC5160的SPI驱动程序主要用于控制TMC5160芯片的配置和功能控制。在编写SPI驱动程序时,需要包含相关的SPI库文件,并按照芯片手册的指导,设置合适的数据传输速率和模式。 首先,需要初始化SPI接口和TMC5160芯片。通过初始化函数或函数库来配置SPI的通信参数,如时钟频率、数据传输模式和位序等。然后,设置TMC5160芯片的初始配置,如电流限制、微步分辨率、速度和加速度等参数。 接下来,可以使用SPI驱动程序来控制TMC5160芯片的各种功能。例如,开始和停止电机运动、改变电机的运动速度和方向、读取电机当前位置等。通过向芯片发送指令和数据,以及接收芯片返回的数据,可以实现对TMC5160的配置和控制。 在编写SPI驱动程序过程中,需要注意以下几点。首先,要确保SPI接口的引脚连接正确,以及芯片的供电和地线接触良好。其次,要正确设置SPI的时钟和数据传输模式,以保证与TMC5160芯片的通信正常。最后,要根据具体应用需求,合理选择SPI驱动程序的功能和参数配置,以实现所需的电机运动控制效果。 总结来说,TMC5160的SPI驱动程序是用于与TMC5160芯片进行通信和控制的程序,通过SPI接口实现与该驱动芯片之间的数据传输和功能配置。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值