@Vengineerの戯言 : Twitter
SystemVerilogの世界へようこそ、すべては、SystemC v0.9公開から始まった
Apple M1 Mac mini の PCIe 関連
dts ファイル では、次の部分が PCIe 部分になります。
デバイスドライバは、apple,pcie-m1 で、ここ にあります。
pcie: pcie@690000000 { compatible = "apple,pcie-m1"; reg = <0x6 0x90000000 0x0 0x1000000 /* config */ 0x6 0x80000000 0x0 0x100000 0x6 0x8c000000 0x0 0x100000 /* core and AXI bridge */ 0x6 0x81000000 0x0 0x20000 0x6 0x82000000 0x0 0x20000 0x6 0x83000000 0x0 0x20000>; /* ports */ interrupt-parent = <&aic>; interrupts = <0 695 4 0 698 4 0 701 4 /* state */ 0 704 1 0 705 1 0 706 1 0 707 1 0 708 1 0 709 1 0 710 1 0 711 1 /* MSI */ 0 712 1 0 713 1 0 714 1 0 715 1 0 716 1 0 717 1 0 718 1 0 719 1 0 720 1 0 721 1 0 722 1 0 723 1 0 724 1 0 725 1 0 726 1 0 727 1 0 728 1 0 729 1 0 730 1 0 731 1 0 732 1 0 733 1 0 734 1 0 735 1>; clocks = <&pcie_gp_clk &pcie_clk &pcie_refclk>; clock-names = "core", "aux", "ref"; pinctrl-0 = <&pcie_clkreq_pins>; pinctrl-names = "default"; perst-gpios = <&gpio 152 0 &gpio 153 0 &gpio 33 0>; clkreq-gpios = <&gpio 150 0 &gpio 151 0 &gpio 32 0>; devpwr-gpios = <&smc 13 0>; devpwr-on-0 = <0 1>; devpwr-on-1 = <>; devpwr-on-2 = <>; #address-cells = <3>; #size-cells = <2>; #interrupt-cells = <1>; device_type = "pci"; msi-controller; msi-parent = <&pcie>; msi-doorbell = <0x0 0xfffff000>; ranges = <0x43000000 0x6 0xa0000000 0x6 0xa0000000 0x0 0x20000000 0x02000000 0x0 0xc0000000 0x6 0xc0000000 0x0 0x40000000>; bus-range = <0x00 0x0f>; iommu-map = <0x0000 &pcie_dart0 0x8000 0x0100>, /* fake, and should never be used as RC bridges don't DMA */ <0x0100 &pcie_dart0 0x0000 0x0100>, <0x0200 &pcie_dart1 0x0000 0x0100>, <0x0300 &pcie_dart2 0x0000 0x0100>; refclk-always-on-2; max-speed-2 = <1>; /* 2.5 GT/s */ };
アドレス空間は、この部分で、全部で6個です。最初の 0x6_9000_0000 からの 16MB は PCI Configuration 空間です。2番目の 0x6_8000_0000 からの 1MB と 0x6_8c00_0000 からの1MBが PCIe RootComplexのコアと AXI bridge 回路用です(もしこれが本当なら 内部バスは、AXI なんですね)。最後の3つはポートで、0x6_8100_0000, 0x6_8200_0000, 0x6_8300_0000 でそれぞれ128KBの空間です。
reg = <0x6 0x90000000 0x0 0x1000000 /* config */ 0x6 0x80000000 0x0 0x100000 0x6 0x8c000000 0x0 0x100000 /* core and AXI bridge */ 0x6 0x81000000 0x0 0x20000 0x6 0x82000000 0x0 0x20000 0x6 0x83000000 0x0 0x20000>; /* ports */
次の2つは、割り込み関連です。interrupt-parent は、PCIe から出る割り込み線がどこに繋がっているかを示しています。aic に繋がっています。
割り込み線は、全部で35本出ています。最初の3個(0 695 4 0 698 4 0 701 4)は、この回路の割り込み用です。残りの32個は、MSI用の割り込み線です。
interrupt-parent = <&aic>; interrupts = <0 695 4 0 698 4 0 701 4 /* state */ 0 704 1 0 705 1 0 706 1 0 707 1 0 708 1 0 709 1 0 710 1 0 711 1 /* MSI */ 0 712 1 0 713 1 0 714 1 0 715 1 0 716 1 0 717 1 0 718 1 0 719 1 0 720 1 0 721 1 0 722 1 0 723 1 0 724 1 0 725 1 0 726 1 0 727 1 0 728 1 0 729 1 0 730 1 0 731 1 0 732 1 0 733 1 0 734 1 0 735 1>;
aic は、apple,aic で アドレス空間 (0x2_3b10_0000 から32KB)で、fast-ipi オプションが設定されています。
aic: interrupt-controller@23b100000 { compatible = "apple,aic"; #interrupt-cells = <3>; interrupt-controller; reg = <0x2 0x3b100000 0x0 0x8000>; fast-ipi; };
MSIは、下記の部分です。msi-doorbel は、ドアベル用の64ビットアドレスでここでは、0x_ffff_f000 を設定しています。
msi-controller; msi-parent = <&pcie>; msi-doorbell = <0x0 0xfffff000>;
下記の ranges の部分は、アドレス変換です。
- 1番目のエントリは、non-prefetchable 32-bit アドレス(0x4300_0000)、64-bitアドレス(0x6_0xa000_0000)は、0x6_a000_0000 から512MBに
- 2番目のエントリは、non-prefetchable 32-bit アドレス(0x0200_0000)、64-bitアドレス(0x0_0xc000_0000)は、0x6_c000_0000 から1GBに
マッピングされます。
ranges = <0x43000000 0x6 0xa0000000 0x6 0xa0000000 0x0 0x20000000 0x02000000 0x0 0xc0000000 0x6 0xc0000000 0x0 0x40000000>;
次は、クロック部分です。3つのクロックが接続していて、core, aux, ref という名前が付いています。
clocks = <&pcie_gp_clk &pcie_clk &pcie_refclk>; clock-names = "core", "aux", "ref";
3つのクロック、pcie_gp_clk, pcie_clk, pcie_refclk は、次のような接続になっています。refclk100mhz (100MHzのクロックから)、pcie_refclk と imx_clk に接続しています。この pcie_reflk と imx_clk は、apple,pmgr-clk-gate が示すように、Gated Clock です。ここでクロックを止めることができます。pcie_refclk は、pcie に接続されています。imx_clk は、pcie_clk に繋がっています。ここでも apple,pmgr-clk-gate なので Gated Clock になります。この pcie_clk も pcie に接続しています。そして、pcie_gp_clk を経由して、pcie と pcie_dart0, 1, 2 に接続しています。pcie_gp_clk も Gated Clockになります。Gated Clockにすることで使っていない場合は、この部分を Off にすればいいだけです。Apple M1 Mac mini は PCIe を使っていますが、Apple M1 Macbook Air と Apple M1 Macbook Pro では PCIe を使っていないのでこの大本となる pcie_refclk と pcie_clk を Off にしていると思います。
- refclk100mhz
次の perst-gpios は、PCIe の PERST の制御に使うものだと思います。3つありますので、ポートはやっぱり、3ポートなんでしょうね。
perst-gpios = <&gpio 152 0 &gpio 153 0 &gpio 33 0>;
次の clkreq-gpios は、クロックに関係する何かなんではないでしょうか?
clkreq-gpios = <&gpio 150 0 &gpio 151 0 &gpio 32 0>;
gpio の 150, 151, 32 は、gpio:pinctrl にも、pcie_clkreq_pins: pcie_clkreq_pins とあります。
gpio: pinctrl@23c100000 { compatible = "apple,gpio-v0"; reg = <0x2 0x3c100000 0x0 0x100000>; pin-count = <212>; interrupts = <0 190 4 0 191 4 0 192 4 0 193 4 0 194 4 0 195 4 0 196 4>; clocks = <&gpio_clk>; gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupt-cells = <2>; i2c0_pins: i2c0_pins { pins = "gpio192", "gpio188"; function = "periph"; }; pcie_clkreq_pins: pcie_clkreq_pins { pins = "gpio150", "gpio151", "gpio32"; function = "periph"; }; };
次の devpwr-gpios と devpwr-on-X は電源関連だと思います。
devpwr-gpios = <&smc 13 0>; devpwr-on-0 = <0 1>; devpwr-on-1 = <>; devpwr-on-2 = <>;
下記のコードにあるように、ポート番号(0, 1, 2) の内、0のみ、<0 1> を設定しています。ポート番号0のみ電源をONにするのでしょうか?
sprintf(name, "devpwr-on-%d", i); ret = of_property_count_elems_of_size(node, name, 8); pcie->devpwron[i].num = ret; if(ret <= 0) continue; pcie->devpwron[i].seq = devm_kzalloc(dev, ret * 8, GFP_KERNEL); if(!pcie->devpwron[i].seq) return -ENOMEM; ret = of_property_read_variable_u32_array(node, name, pcie->devpwron[i].seq, ret * 2, ret * 2); if(ret < 0) return ret;
bus-range は、PCIe の バス番号は 0x00から0x0f、iommu-map は、pcie_dart0,1,2 に繋がっています。
bus-range = <0x00 0x0f>; iommu-map = <0x0000 &pcie_dart0 0x8000 0x0100>, /* fake, and should never be used as RC bridges don't DMA */ <0x0100 &pcie_dart0 0x0000 0x0100>, <0x0200 &pcie_dart1 0x0000 0x0100>, <0x0300 &pcie_dart2 0x0000 0x0100>; refclk-always-on-2; max-speed-2 = <1>; /* 2.5 GT/s */
refclk-always-on-2 と max-speed-2 は、PCIe のデバイスドライバ のこの部分でパースしています。refclk-always-on-2 は、2の部分をパースしています。
sprintf(name, "refclk-always-on-%d", i); pcie->refclk_always_on[i] = of_property_read_bool(node, name);
パースした値がポート番号なので、2番目のポートの REFCLK を設定しています。
if(!pcie->refclk_always_on[idx]) rmwl(PORT_REFCLK_CGDIS, 0, pcie->base_port[idx] + PORT_REFCLK); rmwl(PORT_APPCLK_CGDIS, 0, pcie->base_port[idx] + PORT_APPCLK);
また、max-speed-2 も 2 の部分をパースしています。この部分はポート番号はポート2に 1 (2.5GT/s、Gen1)を設定しています。設定していない場合はデフォルト値の 2 (5.0GT/s、Gen2)のようです。
sprintf(name, "max-speed-%d", i); if(of_property_read_u32(node, name, &pcie->max_speed[i]) < 0) pcie->max_speed[i] = 2;
結果的に、ポート0には電源を供給し、ポート0はGen2、ポート1はGen2、ポート2はGen1に設定しているようです。ポート2をGen1にしているのは接続しているデバイスがGen1対応のみだからでしょうか?