在device tree的架構下, i2c device 與 driver 是如何 match
原文: http://janbarry0914.blogspot.tw/2014/08/device-tree-i2c-device-driver-match.html
[文章重點]
了解在 device tree 的架構一下, i2c device 與 i2c driver 是如何 match 進而 call 到 driver 裡的 probe function (本文章是以 Qualcomm MSM8974 liquid board 為例)
[文章目錄]
我們將 Realtek rt5627 這顆 audio DAC ( 其 i2c address 是 0x18 ) 掛在 i2c bus 上 ( 其 physical address 為 0xf9923000 的 i2c bus 上).
[結論]
在使用 device tree 時, 其匹配是利用 device tree 中 的 compatible 屬性, 跟 driver 中的 of_match_tabl e-> compatible 所指的字串做比較, 所以兩個字串要相同, 這樣才會匹配成功.
[補充]
針對 rt5627 這 i2c device 是如何被加進系統中做一個補充, 其實在device tree 章節中就有說明 device 是如何被加到系統中 ( board_dt_populate() -> of_platform_populate( ) -> .... -> device_add( )), 但那只是針對一些父節點而言. 此例中, i2c@f9923000 是父節點, 而 realtek_rt5627@18 是子節點, 那子節點是何時被加到系統中? 在Qualcomm msm8974 平台上, 是在 i2c driver 的 probe function 中去實現, 所以是父節點做完 device <--> driver 的匹配動作之後, 在父節點的 driver 中的 probe function 裡再去找 device tree 中是否還有子節點, 如果有, 就開始一連串 device 註冊的動作.
-->
了解在 device tree 的架構一下, i2c device 與 i2c driver 是如何 match 進而 call 到 driver 裡的 probe function (本文章是以 Qualcomm MSM8974 liquid board 為例)
[文章目錄]
- 在 device tree 架構下的作法
- device 如何被註冊
- driver 如何與 device 匹配
[在 device tree 架構下的作法]
Linux kernel driver 的架構都是由匯流排 ( bus ), 裝置 ( device ) 和 驅動程式 ( driver ) 所構成, 匯流排將裝置和驅動程式綁定. 當 Linux kernel 在註冊一個裝置的時候, 會尋找與之匹配的驅動程式; 相反的, 當 Linux kernel 在註冊一個驅動程式的時候, 也會尋找與之匹配的裝置, 而匹配這動作是由匯流排負責完成. 然而 device tree 在做匹配綁定的概念跟之前的作法仍是一致的, 換句話說, 還是利用字串的比對, 只是由原本的 name 屬性改由 device tree 內的 compatible 屬性去做而已.
首先會說明裝置是如何被建立, 是如何被加到匯流排中, 接下來會說明驅動程式在註冊後, 匯流排如何去做這匹配的動作.
[device 如何被註冊]
那裝置是如何被加到匯流排中? 那就是透過 of_platform_populate( ) 這 function.
從這 function 的註釋中了解到它會走遍整個 device tree 並為 每一個 device node 去創建 device. 接下透過 call flow來了解先後關係.
Linux kernel driver 的架構都是由匯流排 ( bus ), 裝置 ( device ) 和 驅動程式 ( driver ) 所構成, 匯流排將裝置和驅動程式綁定. 當 Linux kernel 在註冊一個裝置的時候, 會尋找與之匹配的驅動程式; 相反的, 當 Linux kernel 在註冊一個驅動程式的時候, 也會尋找與之匹配的裝置, 而匹配這動作是由匯流排負責完成. 然而 device tree 在做匹配綁定的概念跟之前的作法仍是一致的, 換句話說, 還是利用字串的比對, 只是由原本的 name 屬性改由 device tree 內的 compatible 屬性去做而已.
首先會說明裝置是如何被建立, 是如何被加到匯流排中, 接下來會說明驅動程式在註冊後, 匯流排如何去做這匹配的動作.
[device 如何被註冊]
那裝置是如何被加到匯流排中? 那就是透過 of_platform_populate( ) 這 function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | /** * 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 * @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(). * * Returns 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) { struct device_node *child; int rc = 0; root = root ? of_node_get(root) : of_find_node_by_path("/"); if (!root) return -EINVAL; for_each_child_of_node(root, child) { rc = of_platform_bus_create(child, matches, lookup, parent, true); if (rc) break; } of_node_put(root); return rc; } |
從這 function 的註釋中了解到它會走遍整個 device tree 並為 每一個 device node 去創建 device. 接下透過 call flow來了解先後關係.
1 2 3 4 5 6 7 8 9 10 11 12 | DT_MACHINE_START(MSM8974_DT, "Qualcomm MSM 8974 (Flattened Device Tree)")
.map_io = msm8974_map_io,
.init_irq = msm_dt_init_irq,
.init_machine = msm8974_init,
.handle_irq = gic_handle_irq,
.timer = &msm_dt_timer,
.dt_compat = msm8974_dt_match,
.reserve = msm_8974_reserve,
.init_very_early = msm8974_init_very_early,
.restart = msm_restart,
.smp = &msm8974_smp_ops,
MACHINE_END
|
msm8974_init( ) @ board-8974.c
|--> board_dt_populate( ) @ board-dt.c
|--> of_platform_populate( ) @ platform.c
|--> of_plateform_bus_create( ) @ platform.c
|--> of_platform_device_create_pdata( ) @ platform.c
|--> of_device_alloc( ) @ platform.c
/*除了分配struct platform_device的記憶體,
還分配了該platform device需要的resource的記憶體*/
|--> of_device_add( ) @ device.c /*把這個platform device加入系統中*/
|--> device_add( ) /* 它會call device_create_file( )
在 sysfs中建立 attribute file for the device*/
[driver 如何與 device 匹配]
在看完 " device tree 如何新增 device 到系統中" 後, 接下來看 device driver 的註冊過程, 在此, 以 i2c device driver - audio codec rt5627 為例.
從上述的 call flow 得知, device tree 是透過 of_driver_match_device( ) 來做 device 與 driver 匹配的工作. 再從下述的 call flow 得知, 其實最後就是拿 device tree 中的 compatible 屬性跟 driver 中的 of_match_table->compatible 做字串的比較, 如果字串相同, 那就匹配成功囉.
我們再細看一下 of_match_node( ) 在做什麼事
就 device tree 而言(大部分 driver 的 of_match_table 裡只有填寫 compatible 屬性, 例如: 在下述 rt5627_match_table 這例子裡, 就是只有填寫 compatible 屬性), 它會跑到 of_device_is_compatible( ) 去檢查是否匹配, 下面是就是 of_device_is_compatible( ) 完整的 source code
貼一下關於 of_match_table 是如何放在 driver 中 (以 audio codec rt5627為例)
看一下 device tree ( msm8974-liquid.dtsi ) 內, 我們新增一個 i2c device - rt5627
在看完 " device tree 如何新增 device 到系統中" 後, 接下來看 device driver 的註冊過程, 在此, 以 i2c device driver - audio codec rt5627 為例.
i2c_add_driver( ) @ i2c.h
|-> i2c_register_driver( ) @ i2c-core.c /*將 device_driver中的 bus_type 設成 i2c_bus_type */
|--> driver_register( ) @ driver.c
|--> bus_add_driver( ) @ bus.c /* 建立 sysfs file node 與 attr*/
|--> driver_attach( ) @ dd.c
|--> bus_for_each_dev( drv->bus, NULL, drv, __driver_attach) @ bus.c
|--> __driver_attach @ dd.c
|--> driver_match_device(drv, dev) @base.h
|--> i2c_device_match( ) @ i2c-core.c
/***********************************************
如果是device tree, 會透過 of_driver_match_device( ) 去做匹配的動作
如果不是device tree就改用 i2c_match_id( ) 去完成匹配的動作
***************************************************/
|--> driver_probe_device( ) @ dd.c
/*如果匹配成功, 接下來就要準備 call driver 中的 probe function*/
|--> really_probe( ) @ dd.c
|--> i2c_device_probe( ) @ i2c-core.c
|--> rt5627_i2c_probe( ) @ rt5627.c
從上述的 call flow 得知, device tree 是透過 of_driver_match_device( ) 來做 device 與 driver 匹配的工作. 再從下述的 call flow 得知, 其實最後就是拿 device tree 中的 compatible 屬性跟 driver 中的 of_match_table->compatible 做字串的比較, 如果字串相同, 那就匹配成功囉.
of_driver_match_device( ) @ of_device.h
|--> of_match_device(drv-> of_match_table, dev) @ device.c
|--> of_match_node( drv->of_match_table, dev->node) @ base.c
|--> of_device_is_compatible(node, drv->of_match_table->compatible) @ base.c
|--> of_compat_cmp( ) @ prom.h
/*在 of_compat_cmp( ) 會透過 of_get_property()去取得
device tree 中 compatible 對應的值(字串),
再跟 drv->of_match_table->compatible 所指的字串做字串比較*/
|--> strncmp( ) /*字串的比較*/
我們再細看一下 of_match_node( ) 在做什麼事
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | /** * of_match_node - Tell if an 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) { if (!matches) return NULL; while (matches->name[0] || matches->type[0] || matches->compatible[0]) { int match = 1; if (matches->name[0]) match &= node->name && !strcmp(matches->name, node->name); if (matches->type[0]) match &= node->type && !strcmp(matches->type, node->type); if (matches->compatible[0]) match &= of_device_is_compatible(node, matches->compatible); if (match) return matches; matches++; } return NULL; } EXPORT_SYMBOL(of_match_node); |
就 device tree 而言(大部分 driver 的 of_match_table 裡只有填寫 compatible 屬性, 例如: 在下述 rt5627_match_table 這例子裡, 就是只有填寫 compatible 屬性), 它會跑到 of_device_is_compatible( ) 去檢查是否匹配, 下面是就是 of_device_is_compatible( ) 完整的 source code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | /** Checks if the given "compat" string matches one of the strings in * the device's "compatible" property */ int of_device_is_compatible(const struct device_node *device, const char *compat) { const char* cp; int cplen, l; cp = of_get_property(device, "compatible", &cplen); if (cp == NULL) return 0; while (cplen > 0) { if (of_compat_cmp(cp, compat, strlen(compat)) == 0) return 1; l = strlen(cp) + 1; cp += l; cplen -= l; } return 0; } EXPORT_SYMBOL(of_device_is_compatible); |
貼一下關於 of_match_table 是如何放在 driver 中 (以 audio codec rt5627為例)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #ifdef CONFIG_OF static struct of_device_id rt5627_match_table[] = { { .compatible = "realtek,rt5627",}, { }, }; #else #define rt5627_match_table NULL #endif static struct i2c_driver rt5627_i2c_driver = { .driver = { .name = "rt5627", .owner = THIS_MODULE, .of_match_table = of_match_ptr(rt5627_match_table), }, .probe = rt5627_i2c_probe, .remove = rt5627_i2c_remove, .id_table = rt5627_i2c_id, }; |
看一下 device tree ( msm8974-liquid.dtsi ) 內, 我們新增一個 i2c device - rt5627
&soc {
..
i2c@f9923000 {
realtek_rt5627@18 {
compatible = "realtek,rt5627";
reg = <0x18>;
};
};
...
};
0x18>
我們將 Realtek rt5627 這顆 audio DAC ( 其 i2c address 是 0x18 ) 掛在 i2c bus 上 ( 其 physical address 為 0xf9923000 的 i2c bus 上).
[結論]
在使用 device tree 時, 其匹配是利用 device tree 中 的 compatible 屬性, 跟 driver 中的 of_match_tabl e-> compatible 所指的字串做比較, 所以兩個字串要相同, 這樣才會匹配成功.
[補充]
針對 rt5627 這 i2c device 是如何被加進系統中做一個補充, 其實在device tree 章節中就有說明 device 是如何被加到系統中 ( board_dt_populate() -> of_platform_populate( ) -> .... -> device_add( )), 但那只是針對一些父節點而言. 此例中, i2c@f9923000 是父節點, 而 realtek_rt5627@18 是子節點, 那子節點是何時被加到系統中? 在Qualcomm msm8974 平台上, 是在 i2c driver 的 probe function 中去實現, 所以是父節點做完 device <--> driver 的匹配動作之後, 在父節點的 driver 中的 probe function 裡再去找 device tree 中是否還有子節點, 如果有, 就開始一連串 device 註冊的動作.
-->
qup_i2c_probe( ) @ i2c-qup.c
|--> i2c_add_numbered_adapter( ) @ i2c-core.c
/* 宣告 i2c adapter 並給予 bus number*/
|--> of_i2c_register_device( ) @ of_i2c.c
/*走遍 child nodes, 並為 child nodes 註冊 device nodes*/
|--> i2c_new_device( ) @ i2c-core.c
/*為這 i2c device 依bus number 與 address命名, 例如: 4-0018
表示該 device 位於 bus 4, i2c address 是 0018*/
|--> device_register( ) @ core.c
|--> device_add ( ) @ core.c