linux 5.15 armv8-a64
本文主要讲述spi 驱动的master和slave的初始化过程,linux中抽象spi master为spi_controler,spi slave 为spi_device,其中spi_controler实质是platform device,理解spi需要先从platform开始。
platform bus的由来
linux最基本的驱动的模型是设备device,驱动driver,bus总线。device是对物理设备的抽象,总线会枚举设备,并连接设备与驱动。CPU一般通过特定的总线访问设备,比如usb总线,这些总线会发现并通知system 该bus所连接的设备,但有一类设备不是特殊的总线连接到system也没有办法让system知道自己的存在,例如spi controler cpu可以直接访问它的寄存器,但是他不会枚举自己,这种设备就满足不了linux标准驱动模型。为了用标准驱动模型来抽象这类设备,linux引入了platform bus(virtual bus)利用这个软件虚拟bus实现这类设备的枚举和驱动的连接,linux Document解释如下。
Platform devices are devices that typically appear as autonomous entities
in the system. This includes legacy port-based devices and host bridges
to peripheral buses, and most controllers integrated into system-on-chip
platforms. What they usually have in common is direct addressing from
a CPU bus. Rarely, a platform_device will be connected through a segment
of some other kind of bus; but its registers will still be directly addressable.
spi controler的初始化过程
platform bus 初始化在start_kernel的最后kernel_init线程中
start_kernel --> rest_init --> kernel_init --> kernel_init_freeable --> do_basic_setup --> driver_init --> platform_bus_init
int __init platform_bus_init(void)
{
int error;
early_platform_cleanup();
error = device_register(&platform_bus);
if (error) {
put_device(&platform_bus);
return error;
}
error = bus_register(&platform_bus_type);
if (error)
device_unregister(&platform_bus);
of_platform_register_reconfig_notifier();
return error;
}
"platform.c" 1524 lines --99%--
do_basic_setup --> do_initcalls(driver_init之后)
arch_initcall_sync(of_platform_default_populate_init);
/**
* of_platform_populate() - Populate platform_devices from device tree data
* @root: parent of the first level to probe or NULL for the root of the tree
* @matches: match table, NULL to use the default
* @lookup: auxdata table for matching id and platform_data with device nodes
* @parent: parent to hook devices from, NULL for toplevel
*
* Similar to of_platform_bus_probe(), this function walks the device tree
* and creates devices from nodes. It differs in that it follows the modern
* convention of requiring all device nodes to have a 'compatible' property,
* and it is suitable for creating devices which are children of the root
* node (of_platform_bus_probe will only create children of the root which
* are selected by the @matches argument).
*
* New board support should be using this function instead of
* of_platform_bus_probe().
*
* Return: 0 on success, < 0 on failure.
*/
int of_platform_populate(struct device_node *root,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent)
matches参数of_default_bus_match_table
of_platform_populate(root, of_default_bus_match_table, lookup, parent);
const struct of_device_id of_default_bus_match_table[] = {
{ .compatible = "simple-bus", },
{ .compatible = "simple-mfd", },
{ .compatible = "isa", },
#ifdef CONFIG_ARM_AMBA
{ .compatible = "arm,amba-bus", },
#endif /* CONFIG_ARM_AMBA */
{} /* Empty terminated list */
};
/**
* of_platform_bus_create() - Create a device for a node and its children.
* @bus: device node of the bus to instantiate
* @matches: match table for bus nodes
* @lookup: auxdata table for matching id and platform_data with device nodes
* @parent: parent for new device, or NULL for top level.
* @strict: require compatible property
*
* Creates a platform_device for the provided device_node, and optionally
* recursively create devices for all the child nodes. */
static int of_platform_bus_create(structdevice_node*bus,
conststructof_device_id*matches,
conststructof_dev_auxdata*lookup,
structdevice*parent,boolstrict);
/**
* of_platform_device_create_pdata - Alloc, initialize and register an of_device
* @np: pointer to node to create device for
* @bus_id: name to assign device
* @platform_data: pointer to populate platform_data pointer with
* @parent: Linux device model parent device.
*
* Return: Pointer to created platform device, or NULL if a device was not
* registered. Unavailable devices will not get registered.
*/
static struct platform_device *of_platform_device_create_pdata(
struct device_node *np,
const char *bus_id,
void *platform_data,
struct device *parent)
{
struct platform_device *dev;
if (!of_device_is_available(np) ||
of_node_test_and_set_flag(np, OF_POPULATED))
return NULL;
dev = of_device_alloc(np, bus_id, parent);
if (!dev)
goto err_clear_flag;
dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
if (!dev->dev.dma_mask)
dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
dev->dev.bus = &platform_bus_type;
dev->dev.platform_data = platform_data;
of_msi_configure(&dev->dev, dev->dev.of_node);
if (of_device_add(dev) != 0) {
platform_device_put(dev);
goto err_clear_flag;
}
return dev;
err_clear_flag:
of_node_clear_flag(np, OF_POPULATED);
return NULL;
}
of_platform_bus_create --> of_platform_device_create_pdata 创建paltform_device
例如qcom 的sm8250.dtsi 中soc 节点的compatible属性simple-bus,of_platform_bus_create函数递归的将compatible属性为simple-bus的节点以及其一级子节点都创建为platform device,spi controler spi14同理。
soc: soc@0 {
#address-cells = <2>;
#size-cells = <2>;
ranges = <0 0 0 0 0x10 0>;
dma-ranges = <0 0 0 0 0x10 0>;
compatible = "simple-bus";
spi14: spi@880000 {
compatible = "qcom,geni-spi";
reg = <0 0x00880000 0 0x4000>;
status = "disabled";
};
i2c15: i2c@884000 {
compatible = "qcom,geni-i2c";
reg = <0 0x00884000 0 0x4000>;
};
};
platform bus he platform device ready后,只缺platform driver
module_platform_driver(spi_geni_driver);
do_initcalls --> module_init --> platform_driver_register --> driver_register --> bus_add_driver
--> driver_attatch --> __driver_attatch --> driver_probe_device
spi controler的probe方法在注册platform driver的时候被调用
当然在driver_probe_device之前有一个driver与device match的过程,使用的driver->bus的match 方法
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
spi slave的初始化过程
postcore_initcall(spi_init);
do_basic_setup --> do_initcalls --> postcore_initcall
spi_init过程会register spi bus
struct bus_type spi_bus_type = {
.name = "spi",
.dev_groups = spi_dev_groups,
.match = spi_match_device,
.uevent = spi_uevent,
.probe = spi_probe,
.remove = spi_remove,
.shutdown = spi_shutdown,
};
EXPORT_SYMBOL_GPL(spi_bus_type);
spi slave在dts中需要以spi controler节点的子节点存在如下
&main_spi0 {
pinctrl-names = "default";
pinctrl-0 = <&main_spi0_pins_default>;
ti,pindir-d0-out-d1-in;
eeprom@0 {
compatible = "microchip,93lc46b";
reg = <0>;
spi-max-frequency = <1000000>;
spi-cs-high;
data-size = <16>;
};
};
spi controler的子节点是怎样被创建成device的呢
spi controler的probe方法中又调用spi_register_master --> of_register_spi_devices --> spi_add_device,of_register_spi_devices会遍历spi controler的子节点然后创建为spi_device
/**
* of_register_spi_devices() - Register child devices onto the SPI bus
* @ctlr: Pointer to spi_controller device
*
* Registers an spi_device for each child node of controller node which
* represents a valid SPI slave.
*/
static void of_register_spi_devices(struct spi_controller *ctlr)
{
struct spi_device *spi;
struct device_node *nc;
if (!ctlr->dev.of_node)
return;
for_each_available_child_of_node(ctlr->dev.of_node, nc) {
if (of_node_test_and_set_flag(nc, OF_POPULATED))
continue;
spi = of_register_spi_device(ctlr, nc);
if (IS_ERR(spi)) {
dev_warn(&ctlr->dev,
"Failed to create SPI device for %pOF\n", nc);
of_node_clear_flag(nc, OF_POPULATED);
}
}
}
spi slave的driver代码如下 module_spi_driver --> module_init --> spi_driver_register --> driver_register
spi_driver_register 设置sdrv->bus = spi_bus_type,后面的driver_register到spi slave probe方法被调用的过程与platform driver的过程类似
static struct spi_driver eeprom_93xx46_driver = {
.driver = {
.name = "93xx46",
.of_match_table = of_match_ptr(eeprom_93xx46_of_table),
},
.probe = eeprom_93xx46_probe,
.remove = eeprom_93xx46_remove,
.id_table = eeprom_93xx46_spi_ids,
};
module_spi_driver(eeprom_93xx46_driver);
至此controler slave 与spi bus 都已经准备完成