ハードウェア機能をカスタマイズする

本章では、Armadillo-640を例として、ハードウェア機能をカスタマイズするために必要なDeviceTreeの記述方法と、カーネルへのデバイスドライバの有効化方法について述べます。

Linuxカーネルに含まれているデバイスドライバの一覧は付録A Linux カーネルサポートデバイス情報をご覧ください。

次章からは、いくつかのデバイスを例に、具体的なデバイスの追加方法を示します。

使用するソフトウェアのバージョンは以下のとおりです。

  • ブートローダー: U-Boot 2018.03-at4 以降
  • Linuxカーネル: linux-4.14-at9 以降
  • ユーザーランド: Debian GNU/Linux 9(stretch)v20181128 以降

3.1. Device Treeのカスタマイズ

3.1.1. Device Treeとは

Device Treeとは、ハードウェア情報を記述したデータ構造体です。ハードウェアの差分をDevice Treeに記述することによって、1つのLinuxカーネルイメージを複数のハードウェアで利用することができるようになります。

Device Treeに対応しているメリットの1つは、ハードウェアの変更に対するソフトウェアの変更が容易になることです。例えば、CON9(拡張インターフェース)に接続する拡張基板を作成した場合、主にC言語で記述されたLinuxカーネルのソースコードを変更する必要はなく、やりたいことをより直感的に記述できるDTS(Device Tree Source)の変更で対応できます。

ただし、Device Treeは「データ構造体」であるため、ハードウェアの制御方法などの「処理」を記述することができない点に注意してください。Device Treeには、CPUアーキテクチャ、RAMの容量、各種デバイスのベースアドレスや割り込み番号などのハードウェアの構成情報のみが記述されます。

Device Treeのより詳細な情報については、Linuxカーネルのソースコードに含まれているドキュメント(Documentation/devicetree/)、devicetree.orgで公開されている「Device Tree Specification」を参照してください。

Linuxカーネルのソースコードに含まれている初期出荷状態でのDTS、及び関連するファイルを次に示します。

初期出荷状態でのDTS、及び関連するファイル
  • arch/arm/boot/dts/armadillo-640.dts
  • arch/arm/boot/dts/imx6ull.dtsi
  • arch/arm/boot/dts/imx6ul.dtsi
  • armadillo-640-default-console.dtsi
  • armadillo-640-uart5.dtsi
  • armadillo-640-lcd70ext-l00.dtsi

3.1.2. Device Treeの記述

ここではI2Cを使用するための設定を例に説明します。 新規に作成するファイルは".armadillo-640-i2c4.dtsi"とし、次の3つのノードを作成します。

  • iomuxcノード
  • I2Cバスノード
  • I2Cスレーブデバイスノード
  1. iomuxcノードの記述

    I2C4のSCLを例に、iomuxcの記述方法を説明します。

    &iomuxc {
            pinctrl_i2c4: i2c4grp {                   // 他のiomuxcノードと重複しない名前
                    fsl,pins= <
                    //      MX6UL_PAD_AAAAAAAAAAAAA__BBBBBBBB 0xCCCCCCCC  // 説明のため
                            MX6UL_PAD_UART2_TX_DATA__I2C4_SCL 0x40010808  // SCLピンの設定
                            MX6UL_PAD_UART2_RX_DATA__I2C4_SDA 0x40010808  // SDAピンの設定
                    >;
            };
    };

    図3.1 armadillo-640-i2c4.dtsi (I2C使用ピン設定部分)


    AAABBB の部分はピンのマルチプレクサ(接続先を切り替える機能)の設定です。

    1. 「マルチプレクス表」を参照し、I2C4のSCLで使用できるピンを調べます。

    列「I2C4」を見ると、CON14-3とCON11-16が使用できることがわかります。 今回はCON14-3を使用することにします。 CON14-3の行の「ピン名」を見ると UART2_TX_DATA と書かれています。これが AAA の部分に入ります。 _(アンダースコア)を二つ挟み、列「マルチプレクス機能-I2C4」の I2C4_SCLBBB の部分に入ります。 AAABBB の記述により、下図のように入出力のMUXが選択され、太線部分が接続されます。

    MUXの内部構成

    図3.3 MUXの内部構成


    CCC の部分はPAD(各ピンの入出力特性等の設定)の設定値です。 PADの内部は下図のようになっています。

    PADの内部構成

    図3.4 PADの内部構成


    出力バッファーの設定は、信号の周波数等にあわせて調整できます。 入力バッファーのヒステリシス(シュミットトリガー)や、内部プルアップ等の設定も可能です。

    I2Cの場合は下記の点を考慮します。

    • 最高400kHz程度と低速 → ノイズ低減のため、低速な設定を選択
    • ピンは双方向で使用 → 入力バッファーを強制的に有効化
    • オープンドレイン出力 → オープンドレインを選択
    • 遅い信号の立ち上がり → ヒステリシス有りを選択

    32ビットで表される値のフォーマットは図3.5「PAD設定値のフォーマット」のようになっており、 各ビットの動作は表3.1「PAD設定値の詳細」のようになります。

    PAD設定値のフォーマット

    図3.5 PAD設定値のフォーマット


    よって、下表で * が付いている設定にします。

    表3.1 PAD設定値の詳細

    設定動作

    NO_PAD_CTL

    PAD設定の要否

    0 *

    PAD設定を行う

    1

    PAD設定を行わない

    SION

    入力バッファーを強制的に有効化

    • I2Cや1-Wire等の双方向ピンでは要有効化

    0

    出力ピンの場合は入力バッファー無効

    1 *

    強制的に有効

    HYS

    入力バッファーのヒステリシス設定

    • 有りにすると耐ノイズ性向上

    0

    ヒステリシス無し

    1 *

    ヒステリシス有り

    PUS

    プルダウン / プルアップ選択

    00 *

    プルダウン 100KΩ

    01

    プルアップ 47KΩ

    10

    プルアップ 100KΩ

    11

    プルアップ 22KΩ

    PUE

    キーパー / プル選択

    0 *

    キーパー

    1

    プル

    PKE

    キーパー / プル設定

    0 *

    キーパー/プル無効

    1

    キーパー/プル有効

    ODE

    出力バッファーのオープンドレイン選択

    0

    プッシュプル

    1 *

    オープンドレイン

    SPEED

    出力電流設定

    • 高周波設定にすると出力電流を増加
    • 低周波設定にするとスイッチングノイズを低減

    00 *

    最高出力周波数50MHz

    01

    最高出力周波数100MHz

    10

    最高出力周波数100MHz

    11

    最高出力周波数200MHz

    DSE

    ドライブストレングス調整

    • 出力インピーダンスを上げるとオーバーシュート小
    • 出力インピーダンスを下げると立ち上がり・立ち下がりが高速

    000

    出力バッファーがオフ

    001 *

    出力インピーダンス最大

    010

    011

    100

    101

    110

    111

    出力インピーダンス最小

    SRE

    スルーレート設定

    • 低速にするとノイズ低減

    0 *

    低速

    1

    高速


    SDAも同様に UART2_TX_DATAAAA に、 I2C4_SDABBB に入り、CCC は同じ値を設定します。

    詳しくは以下の資料をご覧ください。

    カーネルのソースコードにも関連するドキュメントが付属しています。

    • Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt
    • Documentation/devicetree/bindings/pinctrl/fsl,imx6ul-pinctrl.txt
  2. I2Cバスノードの記述

    次章以降や、 arch/arm/boot/dts/ にある .dtsi .dts ファイルが参考になります。

    関連するドキュメントは Documentation/devicetree/bindings/ 以下にあります。 I2Cの場合は、次に示すファイルとなります。

    • Documentation/devicetree/bindings/i2c/i2c.txt
    • Documentation/devicetree/bindings/i2c/i2c-imx.txt

    100kHzの設定で記述した場合、次のようになります。 このノードの中に、スレーブデバイスのノードも書きます。

    &i2c4 {
            status = "okay";                // okayで上書きする
            clock-frequency = <100000>;    // クロック周波数(100kHz)
            pinctrl-names = "default";
            pinctrl-0 = <&pinctrl_i2c4>;    // 先ほど作成したiomuxcノード
    
            // ここにスレーブデバイスのノードを記述
    };

    図3.6 armadillo-640-i2c4.dtsi (I2Cバスノード部分)


  3. I2Cスレーブデバイスノードの記述

    ここではPCF8591を例に説明します。 記述方法はデバイスによって異なります。

    pcf8591@48 {                            // スレーブのデバイス名@デバイスアドレス
            #address-cells = <1>;
            #size-cells = <0>;
            compatible = "nxp,pcf8591";     // 文字列が一致するデバイスドライバが使用される
            reg = <0x48>;                   // スレーブのデバイスアドレス
    
            input_mode = <0>;              // デバイス固有のプロパティー(あれば)
    };

    図3.7 armadillo-640-i2c4.dtsi (I2Cスレーブデバイスノード部分)


3つのノードを合わせると以下のようになります。 これを"arch/arm/boot/dts/armadillo-640-i2c4.dtsi"として保存します(「サンプルソースコード」のページからダウンロードできます)。

&iomuxc {
        pinctrl_i2c4: i2c4grp {                 // 他のiomuxcノードと重複しない名前
                fsl,pins= <
                        MX6UL_PAD_UART2_TX_DATA__I2C4_SCL 0x40010808  // SCLピンの設定
                        MX6UL_PAD_UART2_RX_DATA__I2C4_SDA 0x40010808  // SDAピンの設定
                >;
        };
};

&i2c4 {
        status = "okay";                // okayで上書きする
        clock-frequency = <100000>;    // クロック周波数(100kHz)
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_i2c4>;    // 先ほど作成したiomuxcノード

        pcf8591@48 {                            // スレーブのデバイス名@デバイスアドレス
                #address-cells = <1>;
                #size-cells = <0>;
                compatible = "nxp,pcf8591";     // 文字列が一致するデバイスドライバが使用される
                reg = <0x48>;                   // スレーブのデバイスアドレス

                input_mode = <0>;              // デバイス固有のプロパティー(あれば)
        };
};

図3.8 armadillo-640-i2c4.dtsi (全体)


さきほどのファイルへのincludeを"arch/arm/boot/dts/armadillo-640.dts"に追加します。

#include "armadillo-640-lcd70ext-l00.dtsi"
#endif
#include "armadillo-640-i2c4.dtsi"              //追加行

/ {
        model = "Atmark Techno Armadillo-640";

図3.9 armadillo-640.dts


このように、新たにデバイス等を追加する場合は、dtsiファイルを追加する形式にすると管理しやすいでしょう。

3.2. カーネルイメージのカスタマイズ

menuconfigを使用してカーネルコンフィギュレーションを変更します。 なお、すでにカスタマイズしたソースコードを元に行う場合、手順5から行います。

  1. Linuxカーネルのソースコードアーカイブを準備 カレントディレクトリにソースコードアーカイブがあることを確認します。

    [ATDE ~]$ ls
    initramfs_a600-[version].cpio.gz linux-v4.14-at[version].tar.gz
  2. Linuxカーネルのソースコードアーカイブを展開

    [ATDE ~]$ tar xf linux-v4.14-at[version].tar.gz
    [ATDE ~]$ ls
    initramfs_a600-[version].cpio.gz linux-v4.14-at[version]  linux-v4.14-at[version].tar.gz
  3. initramfs アーカイブへのシンボリックリンク作成

    [ATDE ~]$ cd linux-v4.14-at[version]
    [ATDE ~/linux-v4.14-at[version]]$ \
    > ln -s ../initramfs_a600-[version].cpio.gz initramfs_a600.cpio.gz
  4. コンフィギュレーションの初期化

    [ATDE ~/linux-v4.14-at[version]]$ make ARCH=arm armadillo-640_defconfig
  5. menuconfigの実行

    [ATDE ~/linux-v4.14-at[version]]$ make ARCH=arm menuconfig
  6. カーネルコンフィギュレーションを変更

    変更後、"Exit"を選択して"Do you wish to save your new configuration? (Press <ESC><ESC> to continue kernel configuration.)"で"Yes"を選択し、カーネルコンフィギュレーションを確定します。

     .config - Linux/arm 4.14-at11 Kernel Configuration
     ------------------------------------------------------------------------------
      ---------------- Linux/arm 4.14-at11 Kernel Configuration -----------------
         Arrow keys navigate the menu.  <Enter> selects submenus ---> (or empty
         submenus ----).  Highlighted letters are hotkeys.  Pressing <Y>
         includes, <N> excludes, <M> modularizes features.  Press <Esc><Esc> to
         exit, <?> for Help, </> for Search.  Legend: [*] built-in  [ ]
        -----------------------------------------------------------------------
                 General setup  --->
             [ ] Enable loadable module support  ----
             [*] Enable the block layer  --->
                 System Type  --->
                 Bus support  --->
                 Kernel Features  --->
                 Boot options  --->
                 CPU Power Management  --->
                 Floating point emulation  --->
                 Userspace binary formats  --->
                 Power management options  --->
             [*] Networking support  --->
                 Device Drivers  --->
                 Firmware Drivers  --->
                 File systems  --->
                 Kernel hacking  --->
                 Security options  --->
             -*- Cryptographic API  --->
                 Library routines  --->
             [ ] Virtualization  ----
        -----------------------------------------------------------------------
      ---------------------------------------------------------------------------
               <Select>    < Exit >    < Help >    < Save >    < Load >
      ---------------------------------------------------------------------------
[ティップ]

Linux Kernel Configurationメニューで"/"キーを押下すると、カーネルコンフィギュレーションの検索を行うことができます。カーネルコンフィギュレーションのシンボル名(の一部)を入力して"Ok"を選択すると、部分一致するシンボル名を持つカーネルコンフィギュレーションの情報が一覧されます。

3.3. DTB・イメージをビルドする

  1. ビルド

    [ATDE ~/linux-v4.14-at[version]]$ \
    > make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- LOADADDR=0x82000000 uImage
    [ATDE ~/linux-v4.14-at[version]]$ \
    > make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
  2. イメージファイルの生成確認

    ビルドが終了すると、arch/arm/boot/ディレクトリと、arch/arm/boot/dts/以下にイメージファイル(LinuxカーネルとDTB)が作成されています。

    [ATDE ~/linux-v4.14-at[version]]$ ls arch/arm/boot/uImage
    arch/arm/boot/uImage
    [ATDE ~/linux-v4.14-at[version]]$ ls arch/arm/boot/dts/armadillo-640.dtb
    arch/arm/boot/dts/armadillo-640.dtb