linux spi驱动

https://blog.csdn.net/chenliang0224/article/details/51057109

最近在学习Linux spi驱动,中途出现了诸多疑问,天苍苍野茫茫,坚持总是可以看到牛羊的,本文以新唐NUC972这颗芯片为例进行逐步分析
 
参考很有价值的两篇文章:
	http://www.th7.cn/system/lin/201507/122488.shtml
	http://blog.chinaunix.net/uid-25445243-id-4026974.html
	
1、SPI 总线注册
	源码路径:drivers/spi/spi.c
	有兴趣的可以看下这位大神的文章,很详细http://blog.chinaunix.net/uid-25445243-id-4032371.html
	
2、spidev.c
	源码路径:drivers/spi/spidev.c
	如果不想写具体的芯片驱动,就可以用kernel自带的驱动spidev.c(这里是困扰我的地方,之前一直没搞明白这里有了
	spi驱动程序,为啥还要编写,因为某个设备对应的驱动必须是唯一的,而驱动却可以对应多个设备),在板级信息spi_board_info
	结构体中将.modalias = "spidev"即可,因为spidev.c中已经驱动取名为“spidev”
 
3、SPI platform_device设备注册 
	源码路径:arch/arm/mach-nuc970/dev.c
	
	static struct nuc970_spi_info nuc970_spi0_platform_data = {
        .num_cs		= 1,
        .lsb		= 0,
        .txneg		= 1,
        .rxneg		= 0,
        .divider	= 4,
        .sleep		= 0,
        .txnum		= 0,
        .txbitlen	= 8,
        .bus_num	= 0,	//注意这个参数很重要!表示SPI控制器的编号是0,将来在spi从设备的
							//板级信息中有体现,意思是将来这个spi从设备挂在编号为0的spi总线下面;
	};
	
	static struct resource nuc970_spi0_resource[] = {
        [0] = {
                .start = NUC970_PA_SPI0,
                .end   = NUC970_PA_SPI0 + NUC970_SZ_SPI0 - 1,
                .flags = IORESOURCE_MEM,
        },
        [1] = {
                .start = IRQ_SPI0,
                .end   = IRQ_SPI0,
                .flags = IORESOURCE_IRQ,
        }
	};
	
	struct platform_device nuc970_device_spi0 = {
        .name		  = "nuc970-spi0",	//设备名
        .id		  = -1,
        .num_resources	  = ARRAY_SIZE(nuc970_spi0_resource),
        .resource	  = nuc970_spi0_resource,
		#if defined(CONFIG_MTD_M25P80) || defined(CONFIG_SPI_SPIDEV)
        .dev		= {
                .platform_data = &nuc970_spi0_platform_data,
		}
		#endif
	};
	
	static struct platform_device *nuc970_public_dev[] __initdata = {
        &nuc970_device_spi0,
	}
 
	//注册spi设备
	void __init nuc970_platform_init(struct platform_device **device, int size)
	{
		platform_add_devices(device, size);
		platform_add_devices(nuc970_public_dev, ARRAY_SIZE(nuc970_public_dev));
	}
 
4、SPI platform_driver驱动注册
	源代码路径:drivers/spi/spi-nuc970-p0.c
	
	static struct platform_driver nuc970_spi0_driver = {
		.probe		= nuc970_spi0_probe,	//由于platform_device先于platform_driver注册,所以设
											//备.name=设备驱动.name时将回调nuc970_spi0_probe
		.remove		= nuc970_spi0_remove,
		.driver		= {
			.name	= "nuc970-spi0",
			.owner	= THIS_MODULE,
		},
	};
	
	//注册spi驱动
	module_platform_driver(nuc970_spi0_driver);
 
	
5、SPI从设备板级信息 以M25P80 norflash芯片为例
	源码路径:arch/arm/mach-nuc970/dev.c
	
	#if defined(CONFIG_SPI_FLASH_W25Q)
	 static struct gsc3280_spi_info w25q_spi1_dev_platdata = {
		 .cs_type            = 1,
		.pin_cs            = 87,
		.num_cs            = 1,
		.cs_value            = 0,
		.lsb_flg            = 0,
		.bits_per_word    = 8,
	};
	#endif
	static struct spi_board_info gsc3280_spi_devices[] = {
	#if defined(CONFIG_SPI_FLASH_W25Q)
		{
			.modalias        = "spi-w25q",
			.bus_num        = 0,//注意这个参数很重要(参考第3点解释)!这个从设备将挂在编号为0的总线上,
								//这样从设备“spi-w25q”就与“nuc970-spi0”平台总线(控制器驱动)连接起来了
			.chip_select        = 2,
			.mode            = SPI_MODE_3,
			.max_speed_hz    = 5 * 1000 * 1000,
			.controller_data    = &w25q_spi1_dev_platdata,
		},
	#endif
	};
	
	//注册“spi-w25q”板级信息
	static int __init gsc3280_spi_devices_init(void)
	{
		spi_register_board_info(gsc3280_spi_devices, ARRAY_SIZE(gsc3280_spi_devices));
		return 0;
	}
	device_initcall(gsc3280_spi_devices_init);
	
6、SPI 从设备驱动程序(以字符设备,att7022e就是通过这里进行操作的)
	static struct spi_driver w25q_driver = {
		.driver    = {
			.name    = "spi-w25q",
			.owner    = THIS_MODULE,
		},
		//.id_table    = w25q_ids,
		.probe    = w25q_probe,
		.remove    = __devexit_p(w25q_remove),
	};
 
 
	static int __init w25q_init(void)
	{
		return spi_register_driver(&w25q_driver);
	}
	static void __exit w25q_exit(void)
	{
		spi_unregister_driver(&w25q_driver);
	}
	module_init(w25q_init);
	module_exit(w25q_exit);
	
	......
	
	花了一个三天的时间才搞明白SPI驱动的注册原理。。。

 

你可能感兴趣的:(NUC977,Linux)