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,
};
