USB Type-C の Linux での扱い
前回の記事で PCIe に刺すタイプの USB Type-C 拡張ボード(玄人志向 USB3.2C-P2-PCIE3)が Linux でいまいちうまく動かないということを書いた。 具体的にはデータ転送はうまくいっているようだが電源周りが怪しかったので、そのあたりを解決すべくいろいろ調べた結果をメモしておく。 なお残念ながら完璧に動かして 3 A を流すには至っていない。
前回の記事の冒頭で USB PD (Power Delivery) なしであっても Type-C の方が Type-A より流せる電流が大きいらしいと書いたが、これは USB Type-C Connector System Software Interface (UCSI) というもので定められている。 Intel の仕様によると、UCSI に SET_POWER_LEVEL というコマンドがあり USB Type-C Current というフィールドを 1 に設定すると 3 A、2 に設定すると 1.5 A となるようだ(引用: 56 ページの Table 4-48)。
Linux では UCSI 関連のドライバは drivers/usb/typec/ucsi
にあり、ucsi.h
にある以下の定数定義が 3 A のモード等を表すと思われる(前述の仕様にある値と違いかつ仕様より値の種類が多いのが気持ち悪いが...)。
#define UCSI_CONSTAT_PWR_OPMODE(_f_) ((_f_) & GENMASK(2, 0)) #define UCSI_CONSTAT_PWR_OPMODE_NONE 0 #define UCSI_CONSTAT_PWR_OPMODE_DEFAULT 1 #define UCSI_CONSTAT_PWR_OPMODE_BC 2 #define UCSI_CONSTAT_PWR_OPMODE_PD 3 #define UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5 4 #define UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0 5
つまり Type-C で 3 A や 1.5 A を流すためにはここにある typec_ucsi のようなドライバを使わなければならないが、現状ではこのボードに対して xhci_pci しか使われている様子がない(なおカーネルの設定で CONFIG_TYPEC は y、 CONFIG_TYPEC_UCSI は m にしている)。 前回の記事で動いているケースでも電流が少ないと書いているが、その原因はたぶんここにある気がする。
ここからはかなり推測だが、おそらく PCIe に刺さったボード自体には xhci_pci ドライバを使い、さらにそこに生えている Type-C ポートを独立のデバイスとして認識し各ポートに typec_ucsi ドライバを使うのが正しい動作ではないだろうか。 カーネルの Type-C に関するドキュメントには "Every port will be presented as its own device under /sys/class/typec/"、"port drivers will describe every Type-C port they control ..." とあるのでポートごとにデバイスドライバが必要という認識で正しそうだ。
本ボードの動作に話を戻すと、なんとなく動いてはいるが /sys/class/typec/
には何も存在しない、つまり Type-C のポートが独立したデバイスとして認識されていない。
実は ASM3142 チップは Type-C と Type-A のどちらも扱えるようになっていて、例えば同じ玄人志向からは ASM3142 搭載で Type-A ポートを増設するボードも売られている。
このあたりが Type-C ポートが独立したデバイスとして認識されず typec_ucsi ドライバも使われていないことの原因になっている気はする。
さらに言うと、Type-C がいろいろな機能を持ちすぎていてポートやケーブル自体をデバイスドライバで制御しないといけないことが遠因と言えそうだ。 元々 USB ポート・ケーブルは電気的に決まった動作をするただの結線であり、ソフトウェアからは意識する必要がなかったはずである。 例えば Type-A ポートを制御する PCI ボードがあればソフトウェアはそのボードさえ制御していればよく、ボードについている Type-A ポートはボードに載ったコントローラチップがよろしくやっておいてくれればよい。 しかし Type-C で流す電流・電圧を変えるとかどちらがソース・シンクか動的に変わるとかを実現せねばならないため、意識しなくてよい単なる物理層であったポートをソフトウェアから制御する必要が出てきた(つまりある抽象化レイヤにあったものが高機能化のために一つ上のレイヤに移動してしまった)、 その結果今回のような不一致が出てくる原因になった、と考えている。