Si5351A Linuxドライバの解析メモ

ドライバの実装仕様

  • Linux KernelのCommon Clk Frameworkに則り実装
  • Si5351シリーズに対応。最大8ch出力できる。
    • 今回はSi5351A 10-MSOP品を制御対象としているの3chでの使い方に絞ってます
  • 8ch出力時のclkout6/7のDivider制約にも対応していそう
  • dtsでクロックツリーを記述できる(各clkoutのクロックソースなど)
    • clocks にリファレンスクロックを指定。clocksは別途fixed-clockで定義しておき参照する。
    • silabs,pll-sourceはPLLのソースを指定
      • 0でXTAL
      • 1でClkin(Variant_Cのみ)
    • silabs,multisynth-sourceはMultiSynthのソースを指定
      • 0でPLLA
      • 1でPLLB or VCXO(Variant_Cのみ)
    • silabs,clock-sourceはclkoutにどのMultiSynthをソースにするか指定。
      • 0で自動的にclkoutのソースをMultiSynthに結び付ける。clockout_Nに対してMultiSynth_Nを割あてる。
      • 1でclkout1-3のソースをMultiSynth_0に結び付け、clkout5-7のソースをMultiSynth_4に結び付ける。clkout0,4には無効
      • 2でclkoutのソースをxtalに結び付ける
      • 3でclkoutのソースをclkinに直結。Variant_Cのみ有効
    • silabs,pll-master 指定したclkoutにPLLの再設定を許可する
      • この指定のあるclkoutに周波数設定を行ったとき、PLL設定周波数を算出・設定する。
    • silabs,drive-strength Drive Strength指定可(2,4,6,8mA)
    • silabs,disable-state Disable State指定可(LOW/HIGH/Hi-Z/Never)
    • clock-frequency clkoutの周波数初期値を設定

dts設定方法

  • dtsの例
/ {
        clocks {
                /* 25MHz reference crystal */
                ref25: oscillator {
                        compatible = "fixed-clock";
                        #clock-cells = <0>;
                        clock-frequency = <25000000>;
                };
        };
};

&i2c0 {
        status = "okay";
        si5351: clock-generator {
                compatible = "silabs,si5351a-msop";
                reg = <0x60>;
                #address-cells = <1>;
                #size-cells = <0>;
                #clock-cells = <1>;

                /* connect xtal input to 25MHz reference */
                clocks = <&ref25>;
                clock-names = "xtal";

                /* connect xtal input as source of pll0 and pll1 */
                silabs,pll-source = <0 0>, <1 0>;

                clkout0 {
                        reg = <0>;
                        silabs,drive-strength = <8>;
                        silabs,multisynth-source = <0>;
                        silabs,clock-source = <0>;
                        silabs,pll-master;
                        silabs,disable-state = <2>;
                        clock-frequency = <22579200>;
                };

                clkout1 {
                        reg = <1>;
                        silabs,drive-strength = <8>;
                        silabs,multisynth-source = <0>;
                        silabs,clock-source = <0>;
                        silabs,disable-state = <2>;
                        clock-frequency = <2822400>;
                };

                clkout2 {
                        reg = <2>;
                        silabs,drive-strength = <8>;
                        silabs,multisynth-source = <0>;
                        silabs,clock-source = <0>;
                        silabs,disable-state = <2>;
                        clock-frequency = <44100>;
                };
        };
};

使い方

  • pll-masterを付加したclkをclk_set_rateすると、PLLまで設定が走る
  • pll-masterが無いclkはclk_set_rateすると、そのときのPLL rateからMultiSynthとR Divで誤差の小さくなるクロックを作る
  • このdtsの場合clkout0 -> clkout1/2の順で設定する必要がある
  • clkout0にmclkを。clkout1にbclk,clkout2にlrclkを割り当てた。
  • clock-cells = <1>なので下記のように参照する。2番目の値がclkoutのreg値を指す
    clocks = <&si5351 0>, <&si5351 1>, <&si5351 2>;

備考

  • 最初のクロック設定で正しく設定しているのにクロックが出ない場合
    • 初回のPLL設定シーケンスに問題がありそう
    • device-treeに何でも良いので初期クロック(clock-frequency)を書いておくことで回避可能です
  • NEO2用Audio Kernelでは下記2点の修正を加えています
    • xtal_cl設定機能
      • device treeより水晶の容量負荷を変更できるようにしています
    • driver init時にクロック出力をPowerDownに設定
      • driver init時にクロック出力状態の場合、不安定になるケースが見られたので修正しています。