kernel SPI 框架

SPI驱动从整体上遵循spi bus - drv -dev模型,核心层为spi.c,这与平台总线设备驱动模型及其相似,都遵循内核的bus_type框架,只是在构造spi device时会稍微有些复杂。

根据上面描述的spi总线模型,spi驱动核心的结构体有spi_devicespi_driver,其中,spi_device结构体中会含有spi控制器的信息,也就是spi_master结构体。

一 从spi master驱动到spi device的构建

在处理挂载在spi总线上的设备驱动之前,需要完成对spi总线的驱动,即spi Master驱动。spi Master驱动符合platform bus-dev-drv模型,其中,dev部分为设备树中spi设备节点,drv部分注册为一个platform_driver,内核在扫描设备树时,会将spi节点自动识别为一个平台设备,构造device_node结构体,最终构造platform_device结构体,并将该结构体注册进platform bus 中,如果platform bus中有相同的compatible属性的platform driver,则调用驱动程序的probe函数,相关代码如下:

// platform driver中相关结构体 (spi master driver)
static const struct of_device_id rockchip_spi_dt_match[] = {
	{ .compatible = "rockchip,rk3399-spi",   },
	... ... 
	{ },
};
static struct platform_driver rockchip_spi_driver = {
	.driver = {
		.name	= DRIVER_NAME,
		.pm = &rockchip_spi_pm,
		.of_match_table = of_match_ptr(rockchip_spi_dt_match),
	},
	.probe = rockchip_spi_probe,
	.remove = rockchip_spi_remove,
};

module_platform_driver(rockchip_spi_driver);

// dts中的spi节点
	spi0: spi@ff1c0000 {
		compatible = "rockchip,rk3399-spi", "rockchip,rk3066-spi";
		reg = <0x0 0xff1c0000 0x0 0x1000>;
		clocks = <&cru SCLK_SPI0>, <&cru PCLK_SPI0>;
		clock-names = "spiclk", "apb_pclk";
		interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH 0>;
		pinctrl-names = "default";
		pinctrl-0 = <&spi0_clk &spi0_tx &spi0_rx &spi0_cs0>;
		#address-cells = <1>;
		#size-cells = <0>;
		status = "disabled";
	};
// 匹配过程
platform_match
	of_driver_match_device
		of_match_device
			of_match_node
				__of_device_is_compatible
					of_compat_cmp

spi master 驱动需要完成两个任务,一是完成spi master结构体中定义的所有函数,即spi 总线驱动的相关设置和收发的底层函数,二是完成对spi子设备节点的解析,构造spi_device结构体,并向spi bus注册这个结构体。

挂载在spi或iic等平台总线下的设备,会被识别成哪种设备在spi/iic controller驱动的probe函数中决定。

static int rockchip_spi_probe(struct platform_device *pdev)
{
	/* 分配spi_master */
	master = spi_alloc_master(&pdev->dev, sizeof(struct rockchip_spi));
	... .../* 设置 spi_master */ 
	/* 注册spi_master */
	ret = devm_spi_register_master(&pdev->dev, master);
}

根据设备树信息构造spi_device是在devm_spi_register_master里面实现的

在spi_device构建完成之后,会寻找spi bus中的driver,同样还是根据compatible属性,如果找到同名的compatible属性,则调用spi driver中的probe函数。

rockchip_spi_probe
		devm_spi_register_master
			spi_register_master
				of_register_spi_devices
					of_register_spi_device
						spi_alloc_device
                        spi_add_device
                            spi_setup
                            device_add
                                bus_add_device   /* add device */
                                bus_probe_device   /* probe device */
                                    device_initial_probe
                                        __device_attach
                                            bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver);
										
__device_attach_driver	
	driver_probe_device
		really_probe
			if (dev->bus->probe) ret = dev->bus->probe(dev);

二 spi_driver驱动注册到字符设备注册

spi_driver:

static const struct of_device_id rockchip_spi_test_dt_match[] = {
	{ .compatible = "rockchip,spi_test_bus0_cs0", },
	... ...
	{},
};
static struct spi_driver spi_rockchip_test_driver = {
	.driver = {
		.name	= "spi_test",
		.owner = THIS_MODULE,
		.of_match_table = of_match_ptr(rockchip_spi_test_dt_match),
	},
	.probe = rockchip_spi_test_probe,
	.remove = rockchip_spi_test_remove,
};

static int __init spi_rockchip_test_init(void)
{
	int ret = 0;

	misc_register(&spi_test_misc);
	ret = spi_register_driver(&spi_rockchip_test_driver);
	return ret;
}

在spi driver的probe函数中,一般会注册一个spi字符设备,并实现spi设备的read/write/ioctrl等函数,以便于User调用,操作SPI设备,在spi driver中不能直接调用spi master里面的某些函数来进行spi设备的操作,而是使用spi.h中提供的接口来操作spi,如spi_setup,spi_read,spi_write

需要注意的是,

从dts中创建的platform/spi device 并不含有任何的硬件信息,只含有:
1 platform device的dev.device_node,含有device_node信息
2 platform device的resource数组,对应占用的相关资源信息(reg,interrupts属性),有中断、内存、IO资源,驱动程序可以通过pdev->dev.of_node来获取其他信息,如硬件信息

三 SPI设备的枚举过程

内核首先会对dts文件进行识别,构造不同的device_node结构体,内核会自动将spi controller节点识别为平台设备,构造platform_device结构体,向平台总线注册这个结构体。

在注册spi master驱动时,会搜索平台总线中匹配的设备,如果有匹配的设备,则执行驱动的probe函数,在驱动程序中,会实现spi的收发,设置等底层函数,构造,设置spi_master结构体,并识别该spi设备节点下的子节点,构造spi_device结构体,并将spi_master赋值到spi_device中,并向spi核心层注册spi_device,同时搜索匹配的spi_driver,如果匹配,则调用spi_driver的probe函数,在该函数中,会注册字符设备驱动,实现该spi设备的枚举,并提供字符设备的open/read/write/ioctrl等函数。