設備樹在platform設備驅動中的使用

摘自:http://blog.csdn.net/zqixiao_09/article/details/50889458

關與設備樹的概念,我們在Exynos4412 內核移植(六)—— 設備樹解析 裏面已經學習過,下面看一下設備樹在設備驅動開發中起到的作用

         Device Tree是一種描述硬件的數據結構,設備樹源(Device Tree Source)文件(以.dts結尾)就是用來描述目標板硬件信息的。Device Tree由一系列被命名的結點(node)和屬性(property)組成,而結點本身可包含子結點。所謂屬性,其實就是成對出現的name和value。在Device Tree中,可描述的信息包括(原先這些信息大多被hard code到kernel中)。


一、設備樹基礎概念

1、基本數據格式

      device tree是一個簡單的節點和屬性樹,屬性是鍵值對,節點可以包含屬性和子節點。下面是一個.dts格式的簡單設備樹。

[cpp] view plain copy
  1. / {  
  2.     node1 {  
  3.         a-string-property = "A string";  
  4.         a-string-list-property = "first string""second string";  
  5.         a-byte-data-property = [0x01 0x23 0x34 0x56];  
  6.         child-node1 {  
  7.             first-child-property;  
  8.             second-child-property = <1>;  
  9.             a-string-property = "Hello, world";  
  10.         };  
  11.         child-node2 {  
  12.         };  
  13.     };  
  14.     node2 {  
  15.         an-empty-property;  
  16.         a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */  
  17.         child-node1 {  
  18.         };  
  19.     };  
  20. };  

      該樹並未描述任何東西,也不具備任何實際意義,但它卻揭示了節點和屬性的結構。即:

a -- 一個的根節點:'/',兩個子節點:node1和node2;node1的子節點:child-node1和child-node2,一些屬性分散在樹之間。

b -- 屬性是一些簡單的鍵值對(key-value pairs):value可以爲空也可以包含任意的字節流。而數據類型並沒有編碼成數據結構,有一些基本數據表示可以在device tree源文件中表示。

c -- 文本字符串(null 終止)用雙引號來表示:string-property = "a string"

d -- “Cells”是由尖括號分隔的32位無符號整數:cell-property = <0xbeef 123 0xabcd1234>

e -- 二進制數據是用方括號分隔:binary-property = [0x01 0x23 0x45 0x67];

f -- 不同格式的數據可以用逗號連接在一起:mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;

g -- 逗號也可以用來創建字符串列表:string-list = "red fish", "blue fish";


二、設備在device tree 中的描述

        系統中的每個設備由device tree的一個節點來表示

1、節點命名

     花些時間談談命名習慣是值得的。每個節點都必須有一個<name>[@<unit-address>]格式的名稱。<name>是一個簡單的ascii字符串,最長爲31個字符,總的來說,節點命名是根據它代表什麼設備。比如說,一個代表3com以太網適配器的節點應該命名爲ethernet,而不是3com509。

    如果節點描述的設備有地址的話,就應該加上unit-address,unit-address通常是用來訪問設備的主地址,並在節點的reg屬性中被列出。後面我們將談到reg屬性。


2、設備

      接下來將爲設備樹添加設備節點:

[cpp] view plain copy
  1. / {  
  2.     compatible = "acme,coyotes-revenge";  
  3.   
  4.     cpus {  
  5.         cpu@0 {  
  6.             compatible = "arm,cortex-a9";  
  7.         };  
  8.         cpu@1 {  
  9.                     compatible = "arm,cortex-a9";  
  10.             };  
  11.         };  
  12.   
  13.     serial@101F0000 {  
  14.         compatible = "arm,pl011";  
  15.     };  
  16.   
  17.     serial@101F2000 {  
  18.         compatible = "arm,pl011";  
  19.     };  
  20.   
  21.     gpio@101F3000 {  
  22.         compatible = "arm,pl061";  
  23.     };  
  24.   
  25.     interrupt-controller@10140000 {  
  26.         compatible = "arm,pl190";  
  27.     };  
  28.   
  29.     spi@10115000 {  
  30.         compatible = "arm,pl022";  
  31.     };  
  32.       
  33.     external-bus {  
  34.         ethernet@0,0 {  
  35.             compatible = "smc,smc91c111";  
  36.         };  
  37.       
  38.         i2c@1,0 {  
  39.             compatible = "acme,a1234-i2c-bus";  
  40.             rtc@58 {  
  41.                 compatible = "maxim,ds1338";  
  42.             };  
  43.             };  
  44.   
  45.         flash@2,0 {  
  46.             compatible = "samsung,k8f1315ebm""cfi-flash";  
  47.              };  
  48.      };  
  49. };  

        在上面的設備樹中,系統中的設備節點已經添加進來,樹的層次結構反映了設備如何連到系統中。外部總線上的設備就是外部總線節點的子節點,i2c設備是i2c總線控制節點的子節點。總的來說,層次結構表現的是從CPU視角來看的系統視圖。在這裏這棵樹是依然是無效的。它缺少關於設備之間的連接信息。稍後將添加這些數據。

      設備樹中應當注意:每個設備節點有一個compatible屬性。flash節點的compatible屬性有兩個字符串。請閱讀下一節以瞭解更多內容。 之前提到的,節點命名應當反映設備的類型,而不是特定型號。請參考ePAPR規範2.2.2節的通用節點命名,應優先使用這些命名。


3、compatible 屬性

      樹中的每一個代表了一個設備的節點都要有一個compatible屬性。compatible是OS用來決定綁定到設備的設備驅動的關鍵。

      compatible是字符串的列表。列表中的第一個字符串指定了"<manufacturer>,<model>"格式的節點代表的確切設備,第二個字符串代表了與該設備兼容的其他設備。例如,Freescale MPC8349 SoC有一個串口設備實現了National Semiconductor ns16550寄存器接口。因此MPC8349串口設備的compatible屬性爲:compatible = "fsl,mpc8349-uart", "ns16550"。在這裏,fsl,mpc8349-uart指定了確切的設備,ns16550表明它與National Semiconductor 16550 UART是寄存器級兼容的。

     注:由於歷史原因,ns16550沒有製造商前綴,所有新的compatible值都應使用製造商的前綴這種做法使得現有的設備驅動程序可以綁定到一個新設備上,同時仍能唯一準確的識別硬件


4、編址

      可編址的設備使用下列屬性來將地址信息編碼進設備樹:

reg

#address-cells

#size-cells

       每個可尋址的設備有一個reg屬性,即以下面形式表示的元組列表:

      reg = <address1 length1 [address2 length2] [address3 length3] ... > 

     每個元組,。每個地址值由一個或多個32位整數列表組成,被稱做cells。同樣地,長度值可以是cells列表,也可以爲空。

     既然address和length字段是大小可變的變量,父節點的#address-cells和#size-cells屬性用來說明各個子節點有多少個cells。換句話說,正確解釋一個子節點的reg屬性需要父節點的#address-cells#size-cells值


5、內存映射設備

      與CPU節點中的單一地址值不同,內存映射設備會被分配一個它能響應的地址範圍。#size-cells用來說明每個子節點種reg元組的長度大小。

     在下面的示例中,每個地址值是1 cell (32位) ,並且每個的長度值也爲1 cell,這在32位系統中是非常典型的。64位計算機可以在設備樹中使用2作爲#address-cells和#size-cells的值來實現64位尋址。

[cpp] view plain copy
  1. serial@101f2000 {  
  2.     compatible = "arm,pl011";  
  3.     reg = <0x101f2000 0x1000 >;  
  4. };  
  5.   
  6. gpio@101f3000 {  
  7.         compatible = "arm,pl061";  
  8.         reg = <0x101f3000 0x1000  
  9.                0x101f4000 0x0010>;  
  10. };  
  11.   
  12.   
  13. interrupt-controller@10140000 {  
  14.         compatible = "arm,pl190";  
  15.         reg = <0x10140000 0x1000 >;  
  16. };  

      每個設備都被分配了一個基地址及該區域大小。本例中的GPIO設備地址被分成兩個地址範圍:0x101f3000~0x101f3fff和0x101f4000~0x101f400f。


三、設備樹在platform設備驅動開發中的使用解析

         我們仍以 Linux 設備驅動開發 —— platform設備驅動應用實例解析 文中的例子來解析設備樹在platform設備驅動中如何使用;

1、設備樹對platform中platform_device的替換

         其實我們可以看到,Device Tree 是用來描述設備信息的,每一個設備在設備樹中是以節點的形式表現出來;而在上面的 platform 設備中,我們利用platform_device 來描述一個設備,我們可以看一下二者的對比

fs4412-beep{
         compatible = "fs4412,beep";
         reg = <0x114000a0 0x4 0x139D0000 0x14>;
};

a -- fs4412-beep 爲節點名,符合咱們前面提到的節點命名規範;
      我們通過名字可以知道,該節點描述的設備是beep, 設備名是fs4412-beep;

b -- compatible = "fs4412,beep"; compatible 屬性, 即一個字符串;
      前面提到,所有新的compatible值都應使用製造商的前綴,這裏是
fs4412;

c -- reg = <0x114000a0 0x4 0x139D0000 0x14>;
       reg屬性來將地址信息編碼進設備樹,表示該設備的地址範圍;這裏是我們用到的寄存器及偏移量;
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 = "bigbang",//沒用了
    .id = -1,
    .dev.release = hello_release,
    .num_resources = ARRAY_SIZE(beep_resource ),
    .resource = beep_resource,
};

      可以看到設備樹中的設備節點完全可以替代掉platform_device。


2、有了設備樹,如何實現device 與 driver 的匹配?

      我們在上一篇還有 platform_device 中,是利用 .name 來實現device與driver的匹配的,但現在設備樹替換掉了device,那我們將如何實現二者的匹配呢?有了設備樹後,platform比較的名字存在哪?

     我們先看一下原來是如何匹配的 ,platform_bus_type 下有個match成員,platform_match 定義如下

[cpp] view plain copy
  1. static int platform_match(struct device *dev, struct device_driver *drv)  
  2. {  
  3.     struct platform_device *pdev = to_platform_device(dev);  
  4.     struct platform_driver *pdrv = to_platform_driver(drv);  
  5.   
  6.     /* Attempt an OF style match first */  
  7.     if (of_driver_match_device(dev, drv))  
  8.         return 1;  
  9.   
  10.     /* Then try ACPI style match */  
  11.     if (acpi_driver_match_device(dev, drv))  
  12.         return 1;  
  13.   
  14.     /* Then try to match against the id table */  
  15.     if (pdrv->id_table)  
  16.         return platform_match_id(pdrv->id_table, pdev) != NULL;  
  17.   
  18.     /* fall-back to driver name match */  
  19.     return (strcmp(pdev->name, drv->name) == 0);  
  20. }  
其中又調用了of_driver_match_device(dev, drv) ,其定義如下:
[cpp] view plain copy
  1. static inline int of_driver_match_device(struct device *dev,  
  2.                      const struct device_driver *drv)  
  3. {  
  4.     return of_match_device(drv->of_match_table, dev) != NULL;  
  5. }  
其調用of_match_device(drv->of_match_table, dev) ,繼續追蹤下去,注意這裏的參數drv->of_match_table
[cpp] view plain copy
  1. const struct of_device_id *of_match_device(const struct of_device_id *matches,  
  2.                        const struct device *dev)  
  3. {  
  4.     if ((!matches) || (!dev->of_node))  
  5.         return NULL;  
  6.     return of_match_node(matches, dev->of_node);  
  7. }  
  8. EXPORT_SYMBOL(of_match_device);  
又調用 of_match_node(matches, dev->of_node)  ,其中matches 是struct of_device_id 類型
[cpp] view plain copy
  1. /** 
  2.  * of_match_node - Tell if an device_node has a matching of_match structure 
  3.  *  @matches:   array of of device match structures to search in 
  4.  *  @node:      the of device structure to match against 
  5.  * 
  6.  *  Low level utility function used by device matching. 
  7.  */  
  8. const struct of_device_id *of_match_node(const struct of_device_id *matches,  
  9.                      const struct device_node *node)  
  10. {  
  11.     const struct of_device_id *match;  
  12.     unsigned long flags;  
  13.   
  14.     raw_spin_lock_irqsave(&devtree_lock, flags);  
  15.     match = __of_match_node(matches, node);  
  16.     raw_spin_unlock_irqrestore(&devtree_lock, flags);  
  17.     return match;  
  18. }  
  19. EXPORT_SYMBOL(of_match_node);  
找到 match = __of_match_node(matches, node); 注意着裏的node是struct device_node 類型的
[cpp] view plain copy
  1. const struct of_device_id *__of_match_node(const struct of_device_id *matches,  
  2.                        const struct device_node *node)  
  3. {  
  4.     const struct of_device_id *best_match = NULL;  
  5.     int score, best_score = 0;  
  6.   
  7.     if (!matches)  
  8.         return NULL;  
  9.   
  10.     for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) {  
  11.         score = __of_device_is_compatible(node, matches->compatible,  
  12.                           matches->type, matches->name);  
  13.         if (score > best_score) {  
  14.             best_match = matches;  
  15.             best_score = score;  
  16.         }  
  17.     }  
  18.   
  19.     return best_match;  
  20. }  
繼續追蹤下去
[cpp] view plain copy
  1. static int __of_device_is_compatible(const struct device_node *device,  
  2.                      const char *compat, const char *type, const char *name)  
  3. {  
  4.     struct property *prop;  
  5.     const char *cp;  
  6.     int index = 0, score = 0;  
  7.   
  8.     /* Compatible match has highest priority */  
  9.     if (compat && compat[0]) {  
  10.         prop = __of_find_property(device, "compatible", NULL);  
  11.         for (cp = of_prop_next_string(prop, NULL); cp;  
  12.              cp = of_prop_next_string(prop, cp), index++) {  
  13.             if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {  
  14.                 score = INT_MAX/2 - (index << 2);  
  15.                 break;  
  16.             }  
  17.         }  
  18.         if (!score)  
  19.             return 0;  
  20.     }  
  21.   
  22.     /* Matching type is better than matching name */  
  23.     if (type && type[0]) {  
  24.         if (!device->type || of_node_cmp(type, device->type))  
  25.             return 0;  
  26.         score += 2;  
  27.     }  
  28.   
  29.     /* Matching name is a bit better than not */  
  30.     if (name && name[0]) {  
  31.         if (!device->name || of_node_cmp(name, device->name))  
  32.             return 0;  
  33.         score++;  
  34.     }  
  35.   
  36.     return score;  
  37. }  
看這句 prop = __of_find_property(device, "compatible", NULL);

可以發先追溯到底,是利用"compatible"來匹配的,即設備樹加載之後,內核會自動把設備樹節點轉換成 platform_device這種格式,同時把名字放到of_node這個地方
   

platform_driver 部分

    可以看到原來是利用platform_driver 下的 struct driver 結構體中的 name 成員來匹配的,看一下 struct driver 結構體的定義:
[cpp] view plain copy
  1. struct device_driver {  
  2.     const char      *name;  
  3.     struct bus_type     *bus;  
  4.   
  5.     struct module       *owner;  
  6.     const char      *mod_name;  /* used for built-in modules */  
  7.   
  8.     bool suppress_bind_attrs;   /* disables bind/unbind via sysfs */  
  9.   
  10.     const struct of_device_id   *of_match_table;  
  11.     const struct acpi_device_id *acpi_match_table;  
  12.   
  13.     int (*probe) (struct device *dev);  
  14.     int (*remove) (struct device *dev);  
  15.     void (*shutdown) (struct device *dev);  
  16.     int (*suspend) (struct device *dev, pm_message_t state);  
  17.     int (*resume) (struct device *dev);  
  18.     const struct attribute_group **groups;  
  19.   
  20.     const struct dev_pm_ops *pm;  
  21.   
  22.     struct driver_private *p;  
  23. }  
      成員中有const struct of_device_id*of_match_table; 是struct of_device_id 類型,定義如下:
[cpp] view plain copy
  1. /* 
  2.  * Struct used for matching a device 
  3.  */  
  4. struct of_device_id  
  5. {  
  6.     char    name[32];  
  7.     char    type[32];  
  8.     char    compatible[128];  
  9.     const void *data;  
  10. };  
      可以看到其作用就是爲了匹配一個設備。我們所要做的就是對 char compatible[128] 的填充;設備樹加載之後,內核會自動把設備樹節點轉換成 platform_device這種格式,同時把名字放到of_node這個地方。


3、基於設備樹的driver的結構體的填充

      匹配的方式發生了改變,那我們的platform_driver 也要修改了

基於設備樹的driver的結構體的填充:

[cpp] view plain copy
  1. static struct of_device_id beep_table[] = {  
  2.     {.compatible = "fs4412,beep"},  
  3. };  
  4. static struct platform_driver beep_driver=  
  5. {  
  6.     .probe = beep_probe,  
  7.     .remove = beep_remove,  
  8.     .driver={  
  9.         .name = "bigbang",  
  10.         .of_match_table = beep_table,  
  11.     },  
  12. };  
原來的driver是這樣的,可以對比一下
[cpp] view plain copy
  1. static struct platform_driver beep_driver=  
  2. {  
  3.     .driver.name = "bigbang",  
  4.     .probe = beep_probe,  
  5.     .remove = beep_remove,  
  6. };  

4、設備樹編譯

      我們在 arch/arm/boot/dts/exynos4412-fs4412.dts 中添加

[cpp] view plain copy
  1. fs4412-beep{  
  2.          compatible = "fs4412,beep";  
  3.          reg = <0x114000a0 0x4 0x139D0000 0x14>;  
  4. };  

      就可以編譯設備樹了

make dtbs  在內核根目錄
vim arch/arm/boot/dts/exynos4412-fs4412.dts
sudo cp  arch/arm/boot/dts/exynos4412-fs4412.dtb /tftpboot/

     然後,將設備樹下載到0x42000000處,並加載驅動 insmod driver.ko, 測試下驅動。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章