最近在学习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驱动的注册原理。。。