1. 程式人生 > >《Linux4.0裝置驅動開發詳解》筆記--第十八章:ARM Linux裝置樹

《Linux4.0裝置驅動開發詳解》筆記--第十八章:ARM Linux裝置樹

18.1 ARM裝置樹簡介

  • 裝置舒適一種描述印鑑的資料結構,它起源於OpenFirmware(OF)
    • 採用裝置樹前後對比:
      • 採用裝置樹之前:ARM架構的板極硬體細節過多的被硬編碼在arch/arm/plat-xxx和arch/arm/mach-xxx中
      • 採用裝置樹之後:許多硬體細節可以直接通過它傳遞給Linux,而不再需要在訥河中進行大量的冗餘編碼
    • 裝置樹的組成:由一系列被命名的節點(Node)和屬性(Property)組成
      • 節點本身有包含子節點
      • 屬性:成對出現的名稱和值
    • 裝置樹中可描述的資訊包括:
      • CPU的數量和類別
      • 記憶體基地址和大小
      • 匯流排和橋
      • 外設連結
      • 中斷控制器和中斷使用情況
      • GPIO控制器和GPIO使用情況
      • 時鐘控制器核實中使用情況
    • 通俗描述:
      • 它基本上就是畫一顆電路板上CPU、匯流排、裝置組成的樹
      • bootloader會將這棵樹傳遞給核心
      • 核心可以識別這棵樹,並根據他站開出Linux核心中的platform_device、i2c_client、spi_device等裝置
      • 這些裝置用到的記憶體、IRQ等資源,也被傳遞給了核心,核心會將這些資源繫結給展開的相應的裝置

18.2 裝置樹的組成和結構

整個裝置數牽涉買你比較廣,既增加了新的用於描述裝置硬體資訊的檔案格式,又增加了編譯這個文字的工具,同時bootloader也需要支援將編譯後的裝置數傳遞給Linux核心

18.2.1 DTS、DTC和DTB等

18.2.1.1 DTS

  • DTS:檔案.dts是一種ASCII文字格式的裝置樹描述,人性化適合閱讀
    • .dts的基本元素為節點和屬性
    • ARM Linux中,一個.dts檔案對應一個ARM的裝置,一般放置在核心的arch/arm/boot/dts目錄中
    • DTS不是ARM獨有的:在arch/powerpc/boot/dts、arch/openrisc/boot/dts等目錄中,也有大量的.dts檔案
    • SOC公用的部分或多個裝置共同的部分一般提煉為.dtsi,類似C的標頭檔案,其他裝置對應的.dts就包含.dtsi
      • 和C語言的標頭檔案類似,.dtsi也可以包括其他的.dtsi,譬如幾乎所有的ARM SOC的.dtsi都引用了skeleton.dtsi
    • 如下:裝置數的模板
      • 他基本表徵了一個裝置樹原始檔的結構
        • 1個root節點”/”;
        • root節點下面含一系列子節點,如程式碼中的child-node1和child-node2
        • 節點node1和下又含有一系列子節點,如本例中的child-node1和child-node2
        • 各個節點都有一系列屬性
          • 這些屬性可能為空,如an-empty-property
          • 可能為字串,如a-string-property
          • 可能為字串樹組,如a-string-list-property
          • 可能為Cells(由u32整陣列成),如second-child-property
          • 可能為二進位制數,如a-byte-data-property
/ {
    nodel {
        a-string-property = "A string";
        a-string-list-property = "first string", "second string"
        a-byte-data-property = {0x01 0x23 0x34 0x56};
        child-node {
            first-child-property;
            second-child-property = <1>
            a-string-property = "Hello, world"
        };
        child-node2 {
        };
    };
   node2 {
        an-enpty-property;
        a-cell-property = <1 2 3 4>; /* each munber (cell) is a uint32 */
        child-node1 {
        };
    };
};
  • 簡單的裝置數檔案例項
    • 假設此machine的配置如下:
      • 1個雙核ARM Cortex-A9 32位處理器;
      • ARM的local bus上的記憶體對映區域分佈了
        • 2個串列埠(分別位於0x101F1000 和 0x101F2000)
        • GPIO控制器(位於0x101F3000)
        • SPI控制器(位於0x10170000)
        • 中斷控制器(位於0x10140000)
        • 一個external bus橋,該橋上又連線了
          • SMC SMC91111 Ethernet(位於0x10100000)
          • 64MB NOR Flash(位於0x30000000)
          • I2C控制器(位於0x10160000),該I2C控制器所對應的I2C總線上又連線了
            • Maxim DS1338實時鐘(I2C地址為0x58)
    • 其對應的.dts檔案如下:
      • 從程式碼可知,external-bus是根節點的子節點,I2C又是external-bus的子節點,RTC有進一步是I2C的子節點
/ {
    compatible = "acme,coyotes-revenge";
    #address-cells = <1>;
    #size-cells = <1>;
    interrupt-parent = <&intc>;

    cpus {
        #address-cells = <1>;
        #size-cells = <0>;
        [email protected]0 {
            compatible = "arm,cortex-a9";
            reg = <0>;
        };
        [email protected]1 {
            compatible = "arm,cortex-a9";
            reg = <1>;
        };
    };

    [email protected]101f0000 {
        compatible = "arm,pl011";
        reg = <0x101f0000 0x1000 >;
        interrupts = < 1 0 >;
    };

    [email protected]101f2000 {
        compatible = "arm,pl011";
        reg = <0x101f2000 0x1000 >;
        interrupts = < 2 0 >;
    };

    [email protected]101f3000 {
        compatible = "arm,pl061";
        reg = <0x101f3000 0x1000
               0x101f4000 0x0010>;
        interrupts = < 3 0 >;
    };

    intc: interrupt-controller@10140000 {
        compatible = "arm,pl190";
        reg = <0x10140000 0x1000 >;
        interrupt-controller;
        #interrupt-cells = <2>;
    };

    [email protected]10115000 {
        compatible = "arm,pl022";
        reg = <0x10115000 0x1000 >;
        interrupts = < 4 0 >;
    };

    external-bus {
        #address-cells = <2>
        #size-cells = <1>;
        ranges = <0 0  0x10100000   0x10000     // Chipselect 1, Ethernet
                  1 0  0x10160000   0x10000     // Chipselect 2, i2c controller
                  2 0  0x30000000   0x1000000>; // Chipselect 3, NOR Flash

        [email protected]0,0 {
            compatible = "smc,smc91c111";
            reg = <0 0 0x1000>;
            interrupts = < 5 2 >;
        };

        [email protected]1,0 {
            compatible = "acme,a1234-i2c-bus";
            #address-cells = <1>;
            #size-cells = <0>;
            reg = <1 0 0x1000>;
            interrupts = < 6 2 >;
            [email protected]58 {
                compatible = "maxim,ds1338";
                reg = <58>;
                interrupts = < 7 3 >;
            };
        };

        [email protected]2,0 {
            compatible = "samsung,k8f1315ebm", "cfi-flash";
            reg = <2 0 0x4000000>;
        };
    };
};

18.2.1.2 DTC(Device Tree Compiler)

  • DTC是將.dts編譯為.dtb的工具
    • DTC的原始碼位於核心的scripts/dtc目錄中
    • 在Linux核心使能了Device Tree的情況下,編譯核心的時候主機工具dtc會被編譯出來,對應scripts/dtc/Makefile中的“hostprogs-y := dtc”這一hostprogs編譯target
    • Ubuntu中單獨安裝DTC:sudo apt-get install device-tree-compiler
    • 在Linux核心的arch/arm/boot/dts/Makefile中,描述了當某種SOC被選中後,哪些.dtb檔案會被編譯出來
      • 如與VEXPRESS對應的.dtb如下:
        • 在Linux下可以單獨編譯裝置樹檔案,當在Linux核心下執行make dtbs時,若之前選擇了ARCH_VEXPRESS,上述.dtb都會有對應的.dts編譯出來,因為arch/arm/Makefile中包含有一個.dtbs編譯目標專案
dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2p-ca5s.dtb \
        vexpress-v2p-ca9.dtb \
        vexpress-v2p-ca15-tc1.dtb \
        vexpress-v2p-ca15_a7.dtb \
        xenvm-4.2.dtb

18.2.1.3 DTB(Device Tree Blob)

  • 檔案.dtb是.dts被DTC編譯後的二進位制格式的裝置樹描述,可由Linux核心解析,UBOOT也可以識別
    • 使用.dtb兩種方式:
      • 在製作NAND、SD啟動映像時會留有一小片區域給.dtb檔案單獨存放,bootloader在引導核心時會先讀取該.dtb到記憶體
      • 將.dtb直接和zImage繫結在一起做成一個映像檔案,此時核心編譯時需要使能CONFIG_ARM_APPENDED_DTB選項

18.2.1.4 繫結(Bingding)

  • 裝置樹繫結文件(.txt):描述對應節點的相容性、必要的屬性和可選的屬性
    • 文件位置:核心的Documentation/devicetree/bindings目錄
      • 其下又分為很多子目錄,譬如,Documentation/devicetree/bindings/i2c/i2c-xiic.txt描述了Xilinx的I2C控制器
    • 文件主要內容:
      • 關於該模組的最基本描述
      • 必需屬性(Required Properties)的描述
      • 可選屬性(Optional properties)的描述
      • 一個例項

18.2.1.5 Bootloader

  • 使能裝置樹的bootloader配置
    • 編譯UBOOT的時候在config檔案中加入:#define CONFIG_OF_LIBFDT
    • 在UBOOT中,可以從NAND、SD或者TFTP等任意介質中將.dtb讀入記憶體
      • 例如:假設.dtb放入的記憶體地址為0x71000000,之後可在UBOOT中執行fdt addr命令設定.dtb的地址
        • fdt的其他命令就變得可以使用,如fdt resize、fdt print等
        • 對於ARM來講,可以通過bootz kernel_addr initrd_address dtb_address的命令來啟動核心
          • dtb_address作為bootz或者bootm的最後一次引數,第一個引數為核心映像的地址,第二個引數為initrc的地址
UBoot > fdt addr 0x71000000

18.2.2 根節點相容性

  • Linux核心通過根節點“/”的相容性即可判斷它啟動的是什麼裝置
    • 組織形式:,
    • 相容性的包含成員
      • 首個相容性的字串是板子級別的名字
      • 後面一個相容性是晶片級別的名字
    • 兩個字串相容性例項如例1:
      • 程式碼中各個電路板的共性是兼容於arm,vexpress,爾特性分別兼容於:arm,vexpress,v2p-ca9/ca5s/ca15_a7
    • 多個字串相容性例項如例2:
      • 第一個字串是板子名字(很特定),第二個是晶片名字(比較特定),第三個是晶片系列的名字(比較通用)
//例1
//板子arch/arm/boot/dts/vexpress-v2p-ca9.dts兼容於"arm,vexpress,v2p-ca9"和“arm,vexpress”
compatible = "arm,vexpress,v2p-ca9", "arm, vexpress"
//板子arch/arm/boot/dts/vexpress-v2p-ca5s.dts相容性如下:
compatible = "arm,vexpress,v2p-ca5s", "arm, vexpress"
//板子arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts相容性如下:
compatible = "arm,vexpress,v2p-ca15_a7", "arm, vexpress"

//例2
//arch/arm/boot/dts/exymos4210-origen.dts的相容性:
compatible = "insignal,origen", "samsung,exynos4210", "samsung,exynos4"
//arch/arm/boot/dts/exymos4210-universal_c210.dts的相容性:
compatible = "insignal,universal_c210", "samsung,exynos4210", "samsung,exynos4"
  • ARM Linux針對不同的電路板會建立由MACHINE_START和MACHINE_END包圍起來的針對這個machine的一系列callback
    • ARM Linux3.x引入Device Tree之後,MACHINE_START變更為DT_MACHINE_START
      • DT_MACHINE_START中含有一個.dt_compat成員,用於表明相關的machine與.dts中root結點的compatible屬性相容關係
      • 如果Bootloader傳遞給核心的Device Tree中root結點的compatible屬性出現在某machine的.dt_compat表中,相關的machine就與對應的Device Tree匹配,從而引發這一machine的一系列初始化函式被執行。
 static const char * const v2m_dt_match[] __initconst = {
         "arm,vexpress",
         "xen,xenvm",
         NULL,
 };
 DT_MACHINE_START(VEXPRESS_DT, "ARM-Versatile Express")
         .dt_compat      = v2m_dt_match,
         .smp            = smp_ops(vexpress_smp_ops),
         .map_io         = v2m_dt_map_io,
         .init_early     = v2m_dt_init_early,
         .init_irq       = v2m_dt_init_irq,
         .timer          = &v2m_dt_timer,
         .init_machine   = v2m_dt_init,
         .handle_irq     = gic_handle_irq,
         .restart        = vexpress_restart,
 MACHINE_END
  • Linux倡導針對多個SoC、多個電路板的通用DT machine,即一個DT machine的.dt_compat表含多個電路板.dts檔案的root結點compatible屬性字串
    • 如果這麼多電路板的初始化序列不一樣,可以透過以下API判斷具體的電路板是什麼
      • 此API判斷目前執行的板子或者SOC的相容性,它匹配的是裝置樹根節點下的相容性
    • 如果一個相容包含多個字串,譬如對於前面介紹的根節點相容compatible = “samsung, universal_c210”, “samsung,exynos4210”,”samsung,exynos4”的情況,如下表達式都成立
int of_machine_is_compatible(const char *compat) 

of_machine_is_compatible("samsung,universal_c210")
of_machine_is_compatible("samsung,exynos4210")
of_machine_is_compatible("samsung,exynos4")

18.2.3 裝置節點相容性

  • 在.dts檔案的每個裝置節點中,都有一個相容性屬性,用於驅動和裝置的繫結
    • 相容屬性是一個字串的列表,形式為”,”
      • 第一個字串表徵節點代表的確切裝置,是個特指
      • 第二個字串表徵可相容的其他裝置,涵蓋更廣的範圍
    • 例如:在vexpress-v2m.dtsi中Flash節點如下
      • 相容屬性的第2個字串“cfi-flash”明顯比第1個字串“arm,vexpress-flash”涵蓋範圍更廣
flash@0,00000000 {
compatible = "arm,vexpress-flash","cfi-flash";
reg = <0 0x00000000 0x04000000>
<1 0x00000000 0x04000000>;
bank-width = <4>
};
  • 使用裝置樹後,驅動需要與.dts中描述的裝置節點進行匹配,從而使驅動的probe()函式執行
    • 對於platform_driver而言,需要新增一個OF匹配表,如前文的.dts檔案的“acme,a1234-i2c-bus”相容I2C控制節點的OF匹配表,程式碼如下:
    • 對於i2c和spi從裝置而言,同樣也可以通過of_match_table新增匹配的.dts中的相關節點的相容屬性
    • i2c和spi外裝置驅動和裝置樹中裝置節點的相容屬性還有一種弱勢匹配方法——“別名”匹配
      • 別名就是去掉相容屬性中manufacturer字首後的部分
//platform裝置驅動中的of_match_table
 static const struct of_device_id a1234_i2c_of_match[] = {
         { .compatible = "acme,a1234-i2c-bus ", },
         {},
 };
 MODULE_DEVICE_TABLE(of, a1234_i2c_of_match);

 static struct platform_driver i2c_a1234_driver = {
         .driver = {
                 .name = "a1234-i2c-bus ",
                 .owner = THIS_MODULE,
                 .of_match_table = a1234_i2c_of_match,
         },
         .probe = i2c_a1234_probe,
         .remove = i2c_a1234_remove,
 };
 module_platform_driver(i2c_a1234_driver);

//i2c和spi裝置驅動中的of_match_table
 static const struct of_device_id wm8753_of_match[] = {
         { .compatible = "wlf,wm8753", },
         { }
 };
 MODULE_DEVICE_TABLE(of, wm8753_of_match);
 static struct spi_driver wm8753_spi_driver = {
         .driver = {
                 .name   = "wm8753",
                .owner  = THIS_MODULE,
                 .of_match_table = wm8753_of_match,
        },
         .probe          = wm8753_spi_probe,
         .remove         = wm8753_spi_remove,
 };
 static struct i2c_driver wm8753_i2c_driver = {
         .driver = {
                 .name = "wm8753",
                 .owner = THIS_MODULE,
                 .of_match_table = wm8753_of_match,
         },
         .probe =    wm8753_i2c_probe,
         .remove =   wm8753_i2c_remove,
         .id_table = wm8753_i2c_id,
 };

//spi的別名匹配
   static int spi_match_device(struct device *dev, struct device_driver *drv) 
   { 
           const struct spi_device *spi = to_spi_device(dev); 
          const struct spi_driver *sdrv = to_spi_driver(drv); 

           /* Attempt an OF style match */ 
           if (of_driver_match_device(dev, drv)) 
                   return 1; 

          /* Then try ACPI */ 
          if (acpi_driver_match_device(dev, drv)) 
                  return 1; 

          if (sdrv->id_table) 
                  return !!spi_match_id(sdrv->id_table, spi); 

          return strcmp(spi->modalias, drv->name) == 0; 
  } 
  static const struct spi_device_id *spi_match_id(const struct spi_device_id *id,
 const struct spi_device *sdev) 
  { 
          while (id->name[0]) { 
                  if (!strcmp(sdev->modalias, id->name)) 
                          return id; 
                  id++; 
          }
          return NULL; 
  }  
  • 當一個驅動支援兩個或多個裝置的時候,這些不同的.dts檔案中裝置的相容屬性都會寫入驅動OF匹配表
    • 驅動可以通過bootloader傳遞給核心裝置樹中的真正節點的相容屬性以確定究竟是哪一種裝置,從而不同裝置不同處理
    • Linux核心通過如下of_device_is_compatible的API來判斷具體的裝置是什麼
      • 除了上面方法,還可以採用在驅動的of_device_id表中填充.data成員的形式
        • 例如,arch/arm/mm/cache-12x0.c支援“arm,1210-cache” “arm,p1310-cache” “arm,1220-cache”等多種裝置
int of_device_is_compatible(const struct device_node *device, const char *compat);

18.2.4 裝置節點及label的命名

  • root結點”/”的cpus子節點下面又包含2個cpu子結點,描述了此machine上的2個CPU,且它們的compatible 屬性為”arm,cortex-a9”。

    • cpus和cpus的2個cpu子結點的命名所遵循的組織形式為:[@]
      • <>中的內容是必選項,[]中的則為可選項
      • name:是一個ASCII字串,用於描述結點對應的裝置型別,如3com Ethernet介面卡對應的結點name宜為ethernet,而不是3com509
      • @unit-address:如果一個結點描述的裝置有地址,則應該給出
    • ePAPR標準給出了節點命名的規範,具體可參考
  • 可以給一個裝置節點新增label,之後可以通過&label的形式訪問這個label,這種引用是通過phandle(pointer handle)進行的

18.2.5 地址編碼

  • 可定址的裝置使用如下資訊在裝置樹中編碼地址資訊
    • reg的組織形式為reg =
//程式碼1
reg
    #address-cells
    #size-cells
//程式碼2
ranges = <0 0  0x10100000   0x10000     // Chipselect 1, Ethernet
              1 0  0x10160000   0x10000     // Chipselect 2, i2c controller
              2 0  0x30000000   0x1000000>; // Chipselect 3, NOR Flash

18.2.6 中斷連線

  • 對於中斷控制器而言,它提供如下屬性:
    • interrupt-controller – 這個屬性為空,中斷控制器應該加上此屬性表明自己的身份
    • #interrupt-cells – 與#address-cells 和 #size-cells相似,它表明連線此中斷控制器的裝置的interrupts屬性的cell大小
  • 在整個Device Tree中,與中斷相關的屬性還包括:
    • interrupt-parent – 裝置結點透過它來指定它所依附的中斷控制器的phandle,當節點沒有指定interrupt-parent 時,則從父級節點繼承
      • 對於本例而言,root節點指定了interrupt-parent = <&intc>;其對應於intc:[email protected],而root節點的子節點並未指定interrupt-parent,因此它們都繼承了intc,即位於0x10140000的中斷控制器。
    • interrupts – 用到了中斷的裝置節點透過它指定中斷號、觸發方法等,具體這個屬性含有多少個cell,由它依附的中斷控制器節點的#interrupt-cells屬性決定。而具體每個cell又是什麼含義,一般由驅動的實現決定,而且也會在Device Tree的binding文件中說明
      • 譬如,對於ARM GIC中斷控制器而言,#interrupt-cells為3,它3個cell的具體含義Documentation/devicetree/bindings/arm/gic.txt就有如下文字說明:
    The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI
    interrupts.

    The 2nd cell contains the interrupt number for the interrupt type.
    SPI interrupts are in the range [0-987].  PPI interrupts are in the
    range [0-15].

    The 3rd cell is the flags, encoded as follows:
          bits[3:0] trigger type and level flags.
                  1 = low-to-high edge triggered
                  2 = high-to-low edge triggered
                  4 = active high level-sensitive
                  8 = active low level-sensitive
          bits[15:8] PPI interrupt cpu mask.  Each bit corresponds to each of
          the 8 possible cpus attached to the GIC.  A bit set to '1' indicated
          the interrupt is wired to that CPU.  Only valid for PPI interrupts.
  • 一個裝置還可能用到多箇中斷號
    • 對於ARM GIC而言,若某裝置使用了SPI的168、169號2箇中斷,而且都是高電平觸發,則該裝置結點的interrupts屬性可定義為:interrupts = <0 168 4>, <0 169 4>;

18.2.7 GPIO、時鐘、pinmux連線

  • 除了中斷以外,在ARM Linux中clock、GPIO、pinmux都可以透過.dts中的結點和屬性進行描述

18.3 Device Tree引發的BSP和驅動變更

有了Device Tree後,大量的板級資訊都不再需要,譬如過去經常在arch/arm/plat-xxx和arch/arm/mach-xxx實施的如下事情:

18.3.1. 註冊platform_device,繫結resource,即記憶體、IRQ等板級資訊。

  • 通過裝置樹後,形如程式碼1中的platform_device程式碼都不再需要,其中platform_device會由kernel自動展開
    • 這些resource實際來源於.dts中裝置結點的reg、interrupts屬性
    • 典型地,大多數匯流排都與“simple_bus”相容,而在SoC對應的machine的.init_machine成員函式中,呼叫of_platform_bus_probe(NULL, xxx_of_bus_ids, NULL);即可自動展開所有的platform_device
      • 譬如,假設我們有個XXX SoC,則可在arch/arm/mach-xxx/的板檔案中透過如下方式展開.dts中的裝置結點對應的platform_device:如程式碼2
//程式碼1
90 static struct resource xxx_resources[] = {
91         [0] = {
92                 .start  = …,
93                 .end    = …,
94                 .flags  = IORESOURCE_MEM,
95         },
96         [1] = {
97                 .start  = …,
98                 .end    = …,
99                 .flags  = IORESOURCE_IRQ,
100         },
101 };
102
103 static struct platform_device xxx_device = {
104         .name           = "xxx",
105         .id             = -1,
106         .dev            = {
107                                 .platform_data          = &xxx_data,
108         },
109         .resource       = xxx_resources,
110         .num_resources  = ARRAY_SIZE(xxx_resources),
111 };
//程式碼2
18 static struct of_device_id xxx_of_bus_ids[] __initdata = {
19         { .compatible = "simple-bus", },
20         {},
21 };
22
23 void __init xxx_mach_init(void)
24 {
25         of_platform_bus_probe(NULL, xxx_of_bus_ids, NULL);
26 }
32
33 #ifdef CONFIG_ARCH_XXX
38
39 DT_MACHINE_START(XXX_DT, "Generic XXX (Flattened Device Tree)")
4145         .init_machine   = xxx_mach_init,
4649 MACHINE_END
50 #endif

18.3.2 註冊i2c_board_info,指定IRQ等板級資訊。

  • 形如程式碼1之類的i2c_board_info程式碼,目前不再需要出現
//程式碼1
145 static struct i2c_board_info __initdata afeb9260_i2c_devices[] = {
146         {
147                 I2C_BOARD_INFO("tlv320aic23", 0x1a),
148         }, {
149                 I2C_BOARD_INFO("fm3130", 0x68),
150         }, {
151                 I2C_BOARD_INFO("24c64", 0x50),
152         },
153 };
  • 現在只需要把tlv320aic23、fm3130、24c64這些裝置結點填充作為相應的I2C controller結點的子結點即可,類似於前面的程式碼2
    • Device Tree中的I2C client會透過I2C host驅動的probe()函式中呼叫of_i2c_register_devices(&i2c_dev->adapter);被自動展開。
      i2c@1,0 {
            compatible = "acme,a1234-i2c-bus";
            …
            rtc@58 {
                compatible = "maxim,ds1338";
                reg = <58>;
                interrupts = < 7 3 >;
            };
        };

18.3.3 註冊spi_board_info,指定IRQ等板級資訊。

  • 如下的spi_board_info程式碼,目前不再需要出現
79 static struct spi_board_info afeb9260_spi_devices[] = {
80         {       /* DataFlash chip */
81                 .modalias       = "mtd_dataflash",
82                 .chip_select    = 1,
83                 .max_speed_hz   = 15 * 1000 * 1000,
84                 .bus_num        = 0,
85         },
86 };
  • 與I2C類似,現在只需要把mtd_dataflash之類的結點,作為SPI控制器的子結點即可,SPI host驅動的probe函式透過spi_register_master()註冊master的時候,會自動展開依附於它的slave。

18.3.4 多個針對不同電路板的machine,以及相關的callback。

  • 過去,ARM Linux針對不同的電路板會建立由MACHINE_START和MACHINE_END包圍起來的針對這個machine的一系列callback,譬如:
    [cpp] view plain copy
    373 MACHINE_START(VEXPRESS, “ARM-Versatile Express”)
    374 .atag_offset = 0x100,
    375 .smp = smp_ops(vexpress_smp_ops),
    376 .map_io = v2m_map_io,
    377 .init_early = v2m_init_early,
    378 .init_irq = v2m_init_irq,
    379 .timer = &v2m_timer,
    380 .handle_irq = gic_handle_irq,
    381 .init_machine = v2m_init,
    382 .restart = vexpress_restart,
    383 MACHINE_END
  • Device Tree之後,MACHINE_START變更為DT_MACHINE_START

18.3.5 裝置與驅動的匹配方式

  • 在上面介紹過

18.3.6 裝置平臺數據屬性化

18.4 常用的OF API

在Linux的BSP和驅動程式碼中,還經常會使用到Linux中一組Device Tree的API,這些API通常被冠以of_字首,它們的實現程式碼位於核心的drivers/of目錄。

18.4.1 尋找節點

  • 根據compatible屬性,獲得裝置結點
    • 遍歷Device Tree中所有的裝置結點,看看哪個結點的型別、compatible屬性與本函式的輸入引數匹配
    • 大多數情況下,from、type為NULL。
struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compatible);

18.4.2 讀取屬性

int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz);
int of_property_read_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz);
int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz);
int of_property_read_u64(const struct device_node *np, const char *propname, u64 *out_value);
  • 讀取裝置結點np的屬性名為propname,型別為8、16、32、64位整型陣列的屬性
    • 對於32位處理器來講,最常用的是of_property_read_u32_array()
      • 如在arch/arm/mm/cache-l2x0.c中,透過如下語句讀取L2 cache的”arm,data-latency”屬性:
534         of_property_read_u32_array(np, "arm,data-latency",
535                                    data, ARRAY_SIZE(data));
  • 在arch/arm/boot/dts/vexpress-v2p-ca9.dts中,含有”arm,data-latency”屬性的L2 cache結點如下:
137         L2: cache-controller@1e00a000 {
138                 compatible = "arm,pl310-cache";
139                 reg = <0x1e00a000 0x1000>;
140                 interrupts = <0 43 4>;
141                 cache-level = <2>;
142                 arm,data-latency = <1 1 1>;
143                 arm,tag-latency = <1 1 1>;
144         }

有些情況下,整形屬性的長度可能為1,於是核心為了方便呼叫者,又在上述API的基礎上封裝出了更加簡單的讀單一整形屬性的API,它們為int of_property_read_u8()、of_property_read_u16()等,實現於include/linux/of.h:

513 static inline int of_property_read_u8(const struct device_node *np,
514                                        const char *propname,
515                                        u8 *out_value)
516 {
517         return of_property_read_u8_array(np, propname, out_value, 1);
518 }
519
520 static inline int of_property_read_u16(const struct device_node *np,
521                                        const char *propname,
522                                        u16 *out_value)
523 {
524         return of_property_read_u16_array(np, propname, out_value, 1);
525 }
526
527 static inline int of_property_read_u32(const struct device_node *np,
528                                        const char *propname,
529                                        u32 *out_value)
530 {
531         return of_property_read_u32_array(np, propname, out_value, 1);
532 }

處整形屬性外,字串屬性也比較常用,其對應的API包括:

int of_property_read_string(struct device_node *np, const char
*propname, const char **out_string);
int of_property_read_string_index(struct device_node *np, const char
    *propname, int index, const char **output);

前者讀取字串屬性,後者讀取字串陣列屬性中的第index個字串。如drivers/clk/clk.c中的of_clk_get_parent_name()透過of_property_read_string_index()遍歷clkspec結點的所有”clock-output-names”字串陣列屬性。

1759 const char *of_clk_get_parent_name(struct device_node *np, int index)
1760 {
1761         struct of_phandle_args clkspec;
1762         const char *clk_name;
1763         int rc;
1764
1765         if (index < 0)
1766                 return NULL;
1767
1768         rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index,
1769                                         &clkspec);
1770         if (rc)
1771                 return NULL;
1772
1773         if (of_property_read_string_index(clkspec.np, "clock-output-names",
1774                                   clkspec.args_count ? clkspec.args[0] : 0,
1775                                           &clk_name) < 0)
1776                 clk_name = clkspec.np->name;
1777
1778         of_node_put(clkspec.np);
1779         return clk_name;
1780 }
1781 EXPORT_SYMBOL_GPL(of_clk_get_parent_name);

處整形外、字串以外的最常用屬性類就是布林型別,其對應的API如下

static inline bool of_property_read_bool(const struct device_node *np, const char *propname);

如果裝置結點np含有propname屬性,則返回true,否則返回false。一般用於檢查空屬性是否存在。

18.4.3 記憶體對映

void __iomem *of_iomap(struct device_node *node, int index);

通過裝置結點直接進行裝置記憶體區間的 ioremap(),index是記憶體段的索引。若裝置結點的reg屬性有多段,可通過index標示要ioremap的是哪一段,只有1段的情況,index為0。採用Device Tree後,大量的裝置驅動通過of_iomap()進行對映,而不再通過傳統的ioremap。

unsigned int irq_of_parse_and_map(struct device_node *dev, int index);

透過Device Tree或者裝置的中斷號,實際上是從.dts中的interrupts屬性解析出中斷號。若裝置使用了多箇中斷,index指定中斷的索引號。
還有一些OF API,這裡不一一列舉,具體可參考include/linux/of.h標頭檔案。

相關推薦

Linux4.0裝置驅動開發筆記--ARM Linux裝置

18.1 ARM裝置樹簡介 裝置舒適一種描述印鑑的資料結構,它起源於OpenFirmware(OF) 採用裝置樹前後對比: 採用裝置樹之前:ARM架構的板極硬體細節過多的被硬編碼在arch/arm/plat-xxx和arch/arm/mach-xxx中

分享《Linux裝置驅動開發2版高清電子版

新浪微博:@宋寶華Barry 在@微盤 分享了《linux裝置驅動開發詳解》第2版1080P電子版,擬升級為第3版,3.16核心,Cortex-A9 SMP,Device tree, DVFS, suspend/hibernation, big.LITTLE, CMA,分層/

Linux4.0裝置驅動開發筆記--第二Linux核心及核心程式設計

2.1 Linux核心發展及演變 1991年10月5日 Linus Torvalds建立 五個支柱:Unix系統、Minix系統、GNU計劃、POSIX標準和Internet 每2-3個月更新一次大的版本號 2.2 Linux2.6後的核心特點

Linux裝置驅動開發 3版 (即 Linux裝置驅動開發 基於最新的Linux 4 0核心 )進展同步更

                本博實時更新《Linux裝置驅動開發詳解(第3版)》的最新進展。 目前已經完成稿件。 2015

linux裝置驅動開發筆記——15 linux i2c驅動

  結合實際程式碼和書中描述,可能跟書上有一定出入。本文後續晶片相關程式碼參考ZYNQ。 15.1 總體結構   如下圖,i2c驅動分為如下幾個重要模組 核心層core,完成i2c匯流排、裝置、驅動模型,對使用者提供sys檔案系統訪問支援;為i2c內部adpter等提供註冊介面。  adpter,介面卡,實

關於召回《Linux裝置驅動開發-基於最新的Linux 4.0核心》的通知

問題描述關於《Linux裝置驅動開發詳解:基於最新的Linux 4.0核心》一書1.華章分社在沒

LinuxLinux裝置驅動開發基於最新的Linux 4.0核心

1 Linux裝置驅動概述及開發環境構建 1.1 裝置驅動的作用 驅使硬體裝置行動 1.2 無作業系統時的裝置驅動 典型架構:一個無限迴圈中夾雜著對裝置中斷的檢測或者對裝置的輪詢 1.3 有作業系統時的裝置驅動 併發 、記

Linux裝置驅動開發基於最新的Linux 4.0核心》一刷勘誤

這是第一次印刷的勘誤,大部分應該買的都是5刷了,這些錯誤基本已經絕跡。還是有部分童鞋買的書老書,

Linux裝置驅動開發》-- 互斥體(mutex)

儘管訊號量已經可以實現互斥的功能,而且包含 DECLARE_MUTEX() 、init_MUTEX ()等定義訊號量的巨集或函式, 從名字上看就體現出了互斥體的概念, 但是mutex 在 Linux 核心中還是真實地存

宋寶華《Linux裝置驅動開發》——sysfs檔案系統與linux裝置模型(5.4.2)

以下讀書筆記內容,摘自宋寶華《Linux裝置驅動開發詳解》一書。 1、sysfs檔案系統的簡介 (1)linux2.6以後的核心引進syfs檔案系統,是虛擬檔案系統; (2)產生一個包括所有系統硬體

宋寶華《linux裝置驅動開發》——platform裝置驅動(12.2)

以下讀書筆記,整理於宋寶華《linux裝置驅動開發詳解》一書。 1、piatform匯流排出現的原因 在SOC整合的獨立外設控制器、掛接在soc記憶體空間的外設不依附與此類匯流排(PCI、USB、I

《TCP IP 卷1協議》閱讀筆記 -

序列號 child 劃分 定時 eap targe not 檢驗 審計 閱讀須知:筆記為閱讀《TCP IP 詳解卷1:協議》後摘抄的一些知識點,其間也有加入一些根據英文原版的自己翻譯和結合網上知識後的理解,所以有些段落之間並不能夠串聯上或者知識點與書上略有差別(基本差別不大

Android群英傳讀書筆記——Android 5.X新特性

第十二章目錄 12.1 Android5.X UI設計初步  12.1.1 材料的形態模擬 12.1.2 更加真實的動畫 12.1.3 大色塊的使用 12.2 Material Design主題 12.3 Palette 12.4 檢視與陰影 12

中山大學演算法課程題目週)

Given an array of non-negative integers, you are initially positioned at the first index of the array. Each element in the array represents your maximum

“全棧2019”Java異常Exception

分享圖片 http jdk 公眾 alt 原創 ava 進步 頭條 難度 初級 學習時間 10分鐘 適合人群 零基礎 開發語言 Java 開發環境 JDK v11 IntelliJ IDEA v2018.3 文章原文鏈接 “全棧2019”Java異常第十八章:Excep

演算法導論 二叉查詢 筆記(二叉查詢、查詢二叉查詢、插入和刪除、隨機構造的二叉查詢

二叉查詢樹是一種樹資料結構,它與普通的二叉樹最大的不同就是二叉查詢樹滿足一個性質:對於樹中的任意一個節點,均有其左子樹中的所有節點的關鍵字值都不大於該節點的關鍵字值,其右子樹中的任意一個節點的關鍵字值都不小於該節點的關鍵字值。 在二叉查詢樹上可以進行搜尋、取最小值、取最大值、取指定節點的前驅

卜若的程式碼筆記系列-Web系列-SpringBoot-jdbc向sqlserver寫資料-3218

1.你通過十七章的學習學會了怎麼連線資料庫,現在教你怎麼向資料庫裡面寫資料 首先,你需要例項化一個JdbcTemplate物件 怎麼例項化呢,直接給該欄位加一個@Autowired的批註,就能夠例項化該物件,具體的我會放在第十九章講解 @Autowired     pr

SpringBoot | web應用開發之WebJars使用

前言 前面一章節我們主要講解了關於檔案上傳的兩種方式。本章節繼續web開發的相關知識點。通常對於web開發而言,像js、css、images等靜態資源版本管理是比較混亂的,比如Jquery、Bootstrap、Vue.js可能各個前端框架所依賴的自個元件的版本都不盡相同,一

鳥哥的Linux私房菜(伺服器)- 、網路驅動器裝置 iSCSI 伺服器

由於企業的資料量越來越大,而且重要性與保密性越來越高,尤其類似資料庫的內容,常常容量單位是以 TB (1TB = 1024GB) 在進行計算的。哇!真可怕!所以囉,磁碟陣列的應用就很重要了。那麼磁碟陣列通常是在哪裡呢? 磁碟陣列通常是 (1)主機內部有磁碟陣列控制卡,可以自行管理磁碟陣列。不過想要提供磁碟陣

機器學習學習筆記 SVM調參並觀察

支援向量機(SVM) SVM調參 %matplotlib inline import numpy as np import matplotlib.pyplot as plt from scipy imp