4.x的内核都是已经支持设备树的,所以platform bus也是做了一些调整。
主要是在匹配函数里面的支持设备树。
struct bus_type platform_bus_type = { .name = "platform", .dev_groups = platform_dev_groups, .match = platform_match, .uevent = platform_uevent, .dma_configure = platform_dma_configure, .pm = &platform_dev_pm_ops, };
/** * platform_match - bind platform device to platform driver. * @dev: device. * @drv: driver. * * Platform device IDs are assumed to be encoded like this: * "<name><instance>", where <name> is a short description of the type of * device, like "pci" or "floppy", and <instance> is the enumerated * instance of the device, like '0' or '42'. Driver IDs are simply * "<name>". So, extract the <name> from the platform_device structure, * and compare it against the name of the driver. Return whether they match * or not. */ static int platform_match(struct device *dev, struct device_driver *drv) { struct platform_device *pdev = to_platform_device(dev); struct platform_driver *pdrv = to_platform_driver(drv); /* When driver_override is set, only bind to the matching driver */ /* 针对特殊情况,dev中的driver_override被设置,则只匹配和driver_override名字相同的驱动程序 */ if (pdev->driver_override) return !strcmp(pdev->driver_override, drv->name); /* Attempt an OF style match first,设备树方式匹配 */ if (of_driver_match_device(dev, drv)) return 1; /* Then try ACPI style match */ /* 高级配置和电源管理之类的匹配,这里不是我们这次的重点 */ if (acpi_driver_match_device(dev, drv)) return 1; /* Then try to match against the id table */ /* 有驱动中有id_table,则dev中的名字和任何一个id_table里面的值匹配就认为匹配 */ if (pdrv->id_table) return platform_match_id(pdrv->id_table, pdev) != NULL; /* fall-back to driver name match */ /* 驱动和设备的名字匹配 */ return (strcmp(pdev->name, drv->name) == 0); }
这里可以看到匹配的优先级如下:
- 设备里的driver_override被设置,优先级最高。
- 驱动里的of_match_table,和平台设备的compatible比较。
- 高级配置和电源管理之类的匹配。
- platform_driver里的id_table里的所有名字和设备名字匹配。
- 最后再是设备名字和驱动名字比较。
当然除了第一个之外,其它的只要没匹配到,后面的几个匹配还会继续执行的。
设备树匹配方式
/** * of_driver_match_device - Tell if a driver's of_match_table matches a device. * @drv: the device_driver structure to test * @dev: the device structure to match against */ static inline int of_driver_match_device(struct device *dev, const struct device_driver *drv) { return of_match_device(drv->of_match_table, dev) != NULL; } /** * of_match_device - Tell if a struct device matches an of_device_id list * @ids: array of of device match structures to search in * @dev: the of device structure to match against * * Used by a driver to check whether an platform_device present in the * system is in its list of supported devices. */ const struct of_device_id *of_match_device(const struct of_device_id *matches, const struct device *dev) { if ((!matches) || (!dev->of_node)) return NULL; return of_match_node(matches, dev->of_node); } /** * of_match_node - Tell if a device_node has a matching of_match structure * @matches: array of of device match structures to search in * @node: the of device structure to match against * * Low level utility function used by device matching. */ const struct of_device_id *of_match_node(const struct of_device_id *matches, const struct device_node *node) { const struct of_device_id *match; unsigned long flags; raw_spin_lock_irqsave(&devtree_lock, flags); match = __of_match_node(matches, node); raw_spin_unlock_irqrestore(&devtree_lock, flags); return match; } static const struct of_device_id *__of_match_node(const struct of_device_id *matches, const struct device_node *node) { const struct of_device_id *best_match = NULL; int score, best_score = 0; if (!matches) return NULL; //根据匹配的分数,选择最高分的是最佳匹配 for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) { score = __of_device_is_compatible(node, matches->compatible, matches->type, matches->name); if (score > best_score) { best_match = matches; best_score = score; } } return best_match; } /** * __of_device_is_compatible() - Check if the node matches given constraints * @device: pointer to node * @compat: required compatible string, NULL or "" for any match * @type: required device_type value, NULL or "" for any match * @name: required node name, NULL or "" for any match * * Checks if the given @compat, @type and @name strings match the * properties of the given @device. A constraints can be skipped by * passing NULL or an empty string as the constraint. * * Returns 0 for no match, and a positive integer on match. The return * value is a relative score with larger values indicating better * matches. The score is weighted for the most specific compatible value * to get the highest score. Matching type is next, followed by matching * name. Practically speaking, this results in the following priority * order for matches: * * 1. specific compatible && type && name * 2. specific compatible && type * 3. specific compatible && name * 4. specific compatible * 5. general compatible && type && name * 6. general compatible && type * 7. general compatible && name * 8. general compatible * 9. type && name * 10. type * 11. name */ static int __of_device_is_compatible(const struct device_node *device, const char *compat, const char *type, const char *name) { struct property *prop; const char *cp; int index = 0, score = 0; /* Compatible match has highest priority */ /* compatible 匹配有限即最高,匹配到了,则给的分数相对较高 */ if (compat && compat[0]) { prop = __of_find_property(device, "compatible", NULL); for (cp = of_prop_next_string(prop, NULL); cp; cp = of_prop_next_string(prop, cp), index++) { if (of_compat_cmp(cp, compat, strlen(compat)) == 0) { score = INT_MAX/2 - (index << 2); break; } } if (!score) return 0; } /* Matching type is better than matching name,类型匹配会加2分 */ if (type && type[0]) { if (!device->type || of_node_cmp(type, device->type)) return 0; score += 2; } /* Matching name is a bit better than not,最后在确认名字匹配加1分 */ if (name && name[0]) { if (!device->name || of_node_cmp(name, device->name)) return 0; score++; } return score; }
看这句 prop = __of_find_property(device, "compatible", NULL);
可以发先追溯到底,是利用"compatible"来匹配的,即设备树加载之后,内核会自动把设备树节点转换成 platform_device这种格式,同时把名字放到of_node这个地方。
id_tabel是根据id_table表中的每一个和设备名字进行匹配,这样一个驱动可以支持多个名称的设备。
static const struct platform_device_id *platform_match_id( const struct platform_device_id *id, struct platform_device *pdev) { while (id->name[0]) { if (strcmp(pdev->name, id->name) == 0) { pdev->id_entry = id; return id; } id++; } return NULL; }
举例:
1.ti的omap8250驱动可以支持好多个型号的芯片,其它芯片只要这个的驱动基础上做很小的改动就可通用。
其中的改动点,使用of_device_id 的date表示的。
/* * Struct used for matching a device */ struct of_device_id { char name[32]; char type[32]; char compatible[128]; const void *data; }; static const u8 omap4_habit = UART_ERRATA_CLOCK_DISABLE; static const u8 am3352_habit = OMAP_DMA_TX_KICK | UART_ERRATA_CLOCK_DISABLE; static const u8 dra742_habit = UART_ERRATA_CLOCK_DISABLE; static const struct of_device_id omap8250_dt_ids[] = { { .compatible = "ti,am654-uart" }, { .compatible = "ti,omap2-uart" }, { .compatible = "ti,omap3-uart" }, { .compatible = "ti,omap4-uart", .data = &omap4_habit, }, { .compatible = "ti,am3352-uart", .data = &am3352_habit, }, { .compatible = "ti,am4372-uart", .data = &am3352_habit, }, { .compatible = "ti,dra742-uart", .data = &dra742_habit, }, {}, }; static struct platform_driver omap8250_platform_driver = { .driver = { .name = "omap8250", .pm = &omap8250_dev_pm_ops, .of_match_table = omap8250_dt_ids, }, .probe = omap8250_probe, .remove = omap8250_remove, };
2.ad5380有好多中类型,芯片使用完全兼容。可能就是版本差异。驱动可以完全兼容。
static const struct spi_device_id ad5380_spi_ids[] = { { "ad5380-3", ID_AD5380_3 }, { "ad5380-5", ID_AD5380_5 }, { "ad5381-3", ID_AD5381_3 }, { "ad5381-5", ID_AD5381_5 }, { "ad5382-3", ID_AD5382_3 }, { "ad5382-5", ID_AD5382_5 }, { "ad5383-3", ID_AD5383_3 }, { "ad5383-5", ID_AD5383_5 }, { "ad5384-3", ID_AD5380_3 }, { "ad5384-5", ID_AD5380_5 }, { "ad5390-3", ID_AD5390_3 }, { "ad5390-5", ID_AD5390_5 }, { "ad5391-3", ID_AD5391_3 }, { "ad5391-5", ID_AD5391_5 }, { "ad5392-3", ID_AD5392_3 }, { "ad5392-5", ID_AD5392_5 }, { } }; MODULE_DEVICE_TABLE(spi, ad5380_spi_ids); static struct spi_driver ad5380_spi_driver = { .driver = { .name = "ad5380", }, .probe = ad5380_spi_probe, .remove = ad5380_spi_remove, .id_table = ad5380_spi_ids, };
最后总结一下有了设备树前后,设备驱动怎么写
有了设备树这样在dts中编写
samsung-beep{ compatible = "samsung,beep"; reg = <0x114000a0 0x4 0x139D0000 0x14>; };
a -- samsung-beep 为节点名,符合咱们前面提到的节点命名规范;
我们通过名字可以知道,该节点描述的设备是beep, 设备名是samsung-beep;
b -- compatible = "samsung,beep"; compatible 属性, 即一个字符串;
前面提到,所有新的compatible值都应使用制造商的前缀,这里是samsung
c -- reg = <0x114000a0 0x4 0x139D0000 0x14>;
reg属性来将地址信息编码进设备树,表示该设备的地址范围;这里是我们用到的寄存器及偏移量;
之前在mach-xxx.c中编写
static struct resource beep_resource[] = { [0] = { .start = 0x114000a0, .end = 0x114000a0+0x4, .flags = IORESOURCE_MEM, }, [1] = { .start = 0x139D0000, .end = 0x139D0000+0x14, .flags = IORESOURCE_MEM, }, }; static struct platform_device hello_device= { .name = "beep", .id = -1, .dev.release = hello_release, .num_resources = ARRAY_SIZE(beep_resource ), .resource = beep_resource, };