上面学了根节点,这里来学习设备节点:
内容来自:《Linux设备驱动》
在.dts文件的每个设备节点中,都有一个兼容属性,兼容属性用于驱动和设备的绑定。兼**容属性是一个字符串的列表,列表中的第一个字符串表征了节点代表的确切设备,**形式为",",其后的字符串表征可兼容的其他设备。可以说前面的是特指,后面的则涵盖更广的范围。如在vexpress-v2m.dtsi中的Flash节点如下:
(根节点是绑定更高层级的,比如芯片板子,设备节点就是具体的设备)
flash@0,00000000 { compatible = "arm,vexpress-flash", "cfi-flash"; reg = <0 0x00000000 0x04000000>, <1 0x00000000 0x04000000>; bank-width = <4>; };
兼容属性的第2个字符串"cfi-flash"明显比第1个字符串"arm,vexpress-flash"涵盖的范围更广。
再如,Freescale MPC8349SoC含一个串口设备,它实现了国家半导体(National Sem-iconductor)的NS16550寄存器接口。则MPC8349串口设备的兼容属性为compatible=“fsl,mpc8349-uart”,“ns16550”。其中,fsl,mpc8349-uart指代了确切的设备,ns16550代表该设备与NS16550UART保持了寄存器兼容。因此,设备节点的兼容性和根节点的兼容性是类似的,都是“从具体到抽象”。
使用设备树后,**驱动需要与.dts中描述的设备节点进行匹配,从而使驱动的probe()函数执行。**对于platform_driver而言,需要添加一个OF匹配表,如前文的.dts文件的"acme,a1234-i2c-bus"兼容I2C控制器节点的OF匹配表,具体代码清单18.6所示。
1 static const struct of_device_id a1234_i2c_of_match[] = { 2 { .compatible = "acme,a1234-i2c-bus", }, 3 {}, 4 }; 5 MODULE_DEVICE_TABLE(of, a1234_i2c_of_match); 6 7 static struct platform_driver i2c_a1234_driver = { 8 .driver = { 9 .name = "a1234-i2c-bus", 10 .owner = THIS_MODULE, 11 .of_match_table = a1234_i2c_of_match, 12 }, 13 .probe = i2c_a1234_probe, 14 .remove = i2c_a1234_remove, 15 }; 16 module_platform_driver(i2c_a1234_driver);
对于I2C和SPI从设备而言,同样也可以通过of_match_table添加匹配的.dts中的相关节点的兼容属性,如sound/soc/codecs/wm8753.c中的针对WolfsonWM8753的of_match_table,具体如代码清单18.7所示。
1 static const struct of_device_id wm8753_of_match[] = { 2 { .compatible = "wlf,wm8753", }, 3 { } 4 }; 5 MODULE_DEVICE_TABLE(of, wm8753_of_match); 6 static struct spi_driver wm8753_spi_driver = { 7 .driver = { 8 .name = "wm8753", 9 .owner = THIS_MODULE, 10 .of_match_table = wm8753_of_match, 11 }, 12 .probe = wm8753_spi_probe, 13 .remove = wm8753_spi_remove, 14 }; 15 static struct i2c_driver wm8753_i2c_driver = { 16 .driver = { 17 .name = "wm8753", 18 .owner = THIS_MODULE, 19 .of_match_table = wm8753_of_match, 20 }, 21 .probe = wm8753_i2c_probe, 22 .remove = wm8753_i2c_remove, 23 .id_table = wm8753_i2c_id, 24 };
(其实看了这个部分,对于前面根节点应该也有所感悟,应该到这里既知道了怎么去看dts,应该知道了怎么将属性和driver的probe绑定起来。)
上述代码中的第2行显示WM8753的供应商是“wlf”,它其实是对应于Wolfson Microe-lectronics的前缀。详细的前缀可见于内核文档:Documentation/devicetree/bindings/vendor-prefixes.txt
1 static int spi_match_device(struct device *dev, struct device_driver *drv) 2 { 3 const struct spi_device *spi = to_spi_device(dev); 4 const struct spi_driver *sdrv = to_spi_driver(drv); 5 6 /* Attempt an OF style match */ 7 if (of_driver_match_device(dev, drv)) 8 return 1; 9 10 /* Then try ACPI */ 11 if (acpi_driver_match_device(dev, drv)) 12 return 1; 13 14 if (sdrv->id_table) 15 return !!spi_match_id(sdrv->id_table, spi); 16 17 return strcmp(spi->modalias, drv->name) == 0; 18 } 19 static const struct spi_device_id *spi_match_id(const struct spi_device_id *id, 20 const struct spi_device *sdev) 21 { 22 while (id->name[0]) { 23 if (!strcmp(sdev->modalias, id->name)) 24 return id; 25 id++; 26 } 27 return NULL; 28 }
通过这个别名匹配,实际上,SPI和I2C的外设驱动即使没有of_match_table,还是可以和设备树中的节点匹配上的。
一个驱动可以在of_match_table中兼容多个设备,在Linux内核中常常使用如下API来判断具体的设备是什么:
int of_device_is_compatible(const struct device_node *device,const char *compat);
此函数用于判断设备节点的兼容属性是否包含compat指定的字符串。这个API多用于一个驱动支持两个以上设备的时候。
当一个驱动支持两个或多个设备的时候,这些不同.dts文件中设备的兼容属性都会写入驱动OF匹配表。
因此驱动可以通过Bootloader **传递给内核设备树中的真正节点的兼容属性以确定究竟是哪一种设备,**从而根据不同的设备类型进行不同的处理。
如arch/powerpc/platforms/83xx/usb.c中的mpc831x_usb_cfg()就进行了类似处理:
if (immr_node && (of_device_is_compatible(immr_node, "fsl,mpc8315-immr") || of_device_is_compatible(immr_node, "fsl,mpc8308-immr"))) clrsetbits_be32(immap + MPC83XX_SCCR_OFFS, MPC8315_SCCR_USB_MASK, MPC8315_SCCR_USB_DRCM_01); else clrsetbits_be32(immap + MPC83XX_SCCR_OFFS, MPC83XX_SCCR_USB_MASK, MPC83XX_SCCR_USB_DRCM_11); /* Configure pin mux for ULPI. There is no pin mux for UTMI */ if (prop && !strcmp(prop, "ulpi")) { if (of_device_is_compatible(immr_node, "fsl,mpc8308-immr")) { clrsetbits_be32(immap + MPC83XX_SICRH_OFFS, MPC8308_SICRH_USB_MASK, MPC8308_SICRH_USB_ULPI); } else if (of_device_is_compatible(immr_node, "fsl,mpc8315-immr")) { clrsetbits_be32(immap + MPC83XX_SICRL_OFFS, MPC8315_SICRL_USB_MASK, MPC8315_SICRL_USB_ULPI); clrsetbits_be32(immap + MPC83XX_SICRH_OFFS, MPC8315_SICRH_USB_MASK, MPC8315_SICRH_USB_ULPI); } else { clrsetbits_be32(immap + MPC83XX_SICRL_OFFS, MPC831X_SICRL_USB_MASK, MPC831X_SICRL_USB_ULPI); clrsetbits_be32(immap + MPC83XX_SICRH_OFFS, MPC831X_SICRH_USB_MASK, MPC831X_SICRH_USB_ULPI); } }
它根据具体的设备是fsl,mpc8315-immr和fsl,mpc8308-immr、中的哪一种来进行不同的处理。
当一个驱动可以兼容多种设备的时候,除了of_device_is_compatible()这种判断方法以外,还可以**采用在驱动的of_device_id表中填充.data成员的形式。**譬如,arch/arm/mm/cache-l2x0.c支持“arm,l210-cache”“arm,pl310-cache”“arm,l220-cache”等多种设备,其of_device_id表如代码清单18.9所示。
1 #define L2C_ID(name, fns) { .compatible = name, .data = (void *)&fns } 2 static const struct of_device_id l2x0_ids[] __initconst = { 3 L2C_ID("arm,l210-cache", of_l2c210_data), 4 L2C_ID("arm,l220-cache", of_l2c220_data), 5 L2C_ID("arm,pl310-cache", of_l2c310_data), 6 L2C_ID("brcm,bcm11351-a2-pl310-cache", of_bcm_l2x0_data), 7 L2C_ID("marvell,aurora-outer-cache", of_aurora_with_outer_data), 8 L2C_ID("marvell,aurora-system-cache", of_aurora_no_outer_data), 9 L2C_ID("marvell,tauros3-cache", of_tauros3_data), 10 /* Deprecated IDs */ 11 L2C_ID("bcm,bcm11351-a2-pl310-cache", of_bcm_l2x0_data), 12 {} 13 };
在驱动中,通过如代码清单18.10的方法拿到了对应于L2缓存类型的.data成员,其中主要用到了of_match_node()这个API。
1 int __init l2x0_of_init(u32 aux_val, u32 aux_mask) 2 { 3 const struct l2c_init_data *data; 4 struct device_node *np; 5 6 np = of_find_matching_node(NULL, l2x0_ids); 7 if (!np) 8 return -ENODEV; 9 … 10 data = of_match_node(l2x0_ids, np)->data; 11 }
如果电路板的.dts文件中L2缓存是arm,pl310-cache,那么上述代码第10行找到的data就是of_l2c310_data,它是l2c_init_data结构体的一个实例。
l2c_init_data是一个由L2缓存驱动自定义的数据结构,在其定义中既可以保护数据成员,又可以包含函数指针,如代码清单18.11所示。
1 struct l2c_init_data { 2 const char *type; 3 unsigned way_size_0; 4 unsigned num_lock; 5 void (*of_parse)(const struct device_node *, u32 *, u32 *); 6 void (*enable)(void __iomem *, u32, unsigned); 7 void (*fixup)(void __iomem *, u32, struct outer_cache_fns *); 8 void (*save)(void __iomem *); 9 struct outer_cache_fns outer_cache; 10 };
通过这种方法,驱动可以把与某个设备兼容的私有数据寻找出来,如此体现了一种面向对象的设计思想,避免了大量的if,else或者switch,case语句。
(如果现在不能理解,那么我要和你握个手,我也是。但是当有天真的涉及到这个场景的时候,知道回来看看,那么就不错。现在你的知识还没足够和它产生联接,所以看过没啥感觉也是一样的。)