[BPI-R4] Failed attempt: add NVMe support on uboot

Context

Hi guys, I just got my BPI-R4 several days ago. For that, I built a relative large OpenWRT image (~150MB) which should not install into the 128MB NAND flash. (I know I can install it into EMMC by utitlize /tmp and dd command, but using an optane ssd would be better.)

Add nvme support (Failed)

1. Enable NVM Express device / NVM Express PCI device support on menuconfig

  1. go into the uboot source ./build_dir/target-aarch64_cortex-a53_musl/u-boot-mt7988_bananapi_bpi-r4-snand/u-boot-2024.01
  2. make menuconfig

2. A dive into OpenWRT’s DTS

We can find the OpenWRT’s DTS here:

./target/linux/mediatek/files-6.1/arch/arm64/boot/dts/mediatek

2.1 PCIe pinctrl in mt7988a.dtsi

pcie3_pins: pcie3-pins {
        mux {
                function = "pcie";
                groups = "pcie_1l_1_pereset", "pcie_clk_req_n3",
                         "pcie_wake_n3_0";
        };
};

2.2 PCIe device in mt7988a.dtsi

pcie3: pcie@11290000 {
  compatible = "mediatek,mt7988-pcie",
               "mediatek,mt7986-pcie",
               "mediatek,mt8192-pcie";
  reg = <0 0x11290000 0 0x2000>;
  reg-names = "pcie-mac";
  ranges = <0x81000000 0x00 0x28000000 0x00
            0x28000000 0x00 0x00200000>,
           <0x82000000 0x00 0x28200000 0x00
            0x28200000 0x00 0x07e00000>;
  device_type = "pci";
  linux,pci-domain = <2>;
  interrupts = <GIC_SPI 171 IRQ_TYPE_LEVEL_HIGH>;
  bus-range = <0x00 0xff>;
  clocks = <&infracfg CLK_INFRA_PCIE_PIPE_P3>,
           <&infracfg CLK_INFRA_PCIE_GFMUX_TL_P3>,
           <&infracfg CLK_INFRA_PCIE_PERI_26M_CK_P3>,
           <&infracfg CLK_INFRA_133M_PCIE_CK_P3>;
  clock-names = "pl_250m", "tl_26m", "peri_26m",
                "top_133m";
  pinctrl-names = "default";
  pinctrl-0 = <&pcie3_pins>;
  #interrupt-cells = <1>;
  interrupt-map-mask = <0 0 0 0x7>;
  interrupt-map = <0 0 0 1 &pcie_intc3 0>,
                  <0 0 0 2 &pcie_intc3 1>,
                  <0 0 0 3 &pcie_intc3 2>,
                  <0 0 0 4 &pcie_intc3 3>;
  #address-cells = <3>;
  #size-cells = <2>;
  status = "disabled";

  pcie_intc3: interrupt-controller {
          #address-cells = <0>;
          #interrupt-cells = <1>;
          interrupt-controller;
  };
};

2.3 PCIe enable entries in mt7988a-bananapi-bpi-r4.dts

/* M.2 key-M SSD */
&pcie3 {
        pinctrl-names = "default";
        pinctrl-0 = <&pcie3_pins>;
        status = "okay";
};

3. Add PCIe3 into uboot’s DTS

We only care about &pcie3 because that is the one for SSD.

3.1 Find the DTS files

Under path

./build_dir/target-aarch64_cortex-a53_musl/u-boot-mt7988_bananapi_bpi-r4-snand/u-boot-2024.01/arch/arm/dts

we can find the following dts file:

and we can find the following build targets from .config

CONFIG_DEFAULT_FDT_FILE="mediatek/mt7988a-bpi-r4-emmc.dtb"
CONFIG_OF_LIST="mt7988a-bananapi-bpi-r4-emmc"

3.2 The including relationship between DTS files

mt7988a-bananapi-bpi-r4-emmc.dts
→ mt7988a-bananapi-bpi-r4.dtsi
→ → mt7988.dtsi

3.3 Add PCIe3 Device support in mt7988.dtsi

pcie3: pcie@11290000 {
        compatible = "mediatek,mt7988-pcie",
                                 "mediatek,mt7986-pcie",
                                 "mediatek,mt8192-pcie";
        reg = <0 0x11290000 0 0x2000>;
        reg-names = "pcie-mac";
        ranges = <0x81000000 0x00 0x28000000 0x00
                          0x28000000 0x00 0x00200000>,
                         <0x82000000 0x00 0x28200000 0x00
                          0x28200000 0x00 0x07e00000>;
        device_type = "pci";
        linux,pci-domain = <2>;
        interrupts = <GIC_SPI 171 IRQ_TYPE_LEVEL_HIGH>;
        bus-range = <0x00 0xff>;
        clocks = <&infracfg_ao CK_INFRA_PCIE_PIPE_P3>,
                         <&infracfg_ao CK_INFRA_PCIE_GFMUX_TL_P3>,
                         <&infracfg_ao CK_INFRA_PCIE_PERI_26M_CK_P3>,
                         <&infracfg_ao CK_INFRA_133M_PCIE_CK_P3>;
        clock-names = "pl_250m", "tl_26m", "peri_26m",
                                  "top_133m";
        #interrupt-cells = <1>;
        interrupt-map-mask = <0 0 0 0x7>;
        interrupt-map = <0 0 0 1 &pcie_intc3 0>,
                                        <0 0 0 2 &pcie_intc3 1>,
                                        <0 0 0 3 &pcie_intc3 2>,
                                        <0 0 0 4 &pcie_intc3 3>;
        #address-cells = <3>;
        #size-cells = <2>;
        status = "disabled";

        pcie_intc3: interrupt-controller {
                        #address-cells = <0>;
                        #interrupt-cells = <1>;
                        interrupt-controller;
        };
};

There are things to notice when migrate from OpenWRT’s DTS:

  1. Change &infracfg to &infracfg_ao: because the label for pinctrl is different.
  2. Change the “CLK_” prefix to CK_: because the drive for uboot is different than the one in Linux/Openwrt kernal
  3. I removed the pinctrl-0 = <&pcie3_pins> because I want to defined it later.

3.4 Add PCIe3 pinctrl define in mt7988a-bananapi-bpi-r4.dtsi

Under the pinctrl section, add:

pcie3_pins: pcie3-pins {
        mux {
                function = "pcie";
                groups = "pcie_1l_1_pereset", "pcie_clk_req_n3",
                                 "pcie_wake_n3_0";
        };
};

3.5 Add PCIe3 enable in mt7988a-bananapi-bpi-r4-emmc.dts

/* M.2 key-M SSD */
&pcie3 {
        pinctrl-names = "default";
        pinctrl-0 = <&pcie3_pins>;
        status = "okay";
};

4. Compile the fip file

4.1 Compile Uboot first

Simply run the following command and be sure to replace the placeholder:

make clean; make CROSS_COMPILE=<your_openwrt_path>/staging_dir/toolchain-<suffix_is_variated>/bin/<suffix_is_variated>-linux- -j<THREAD> STAGING_DIR=<your_openwrt_path>/staging_dir

And you would see u-boot.bin file.

4.2 Compile the arm-trusted-firmware

Under path:

./build_dir/target-aarch64_cortex-a53_musl/arm-trusted-firmware-mediatek-mt7988-sdmmc-comb/arm-trusted-firmware-mediatek-<suffix_is_variated>

Run:

make -f Makefile PLAT=mt7988 BOOT_DEVICE=snand BL33=<PATH_TO_UBOOT_BIN> all fip CROSS_COMPILE=<your_openwrt_path>/staging_dir/toolchain-<suffix_is_variated>/bin/<suffix_is_variated>-linux- STAGIN_DIR=<your_openwrt_path>/staging_dir

The artifact is under:

build/mt7988/release/fip.bin

5. Install your custom uboot

5.1 Pre-request

You should flash openwrt(official one or your custom build? doesn’t matter) into NAND.

5.2 Flash it!

  1. Download you fip.bin
  2. Choose #7 in the Uboot menu
  3. Rename it as Uboot wants (in my case openwrt-mediatek-filogic-bananapi_bpi-r4-snand-bl31-uboot.fip)
  4. Run you TFTP server

You will see this when success:

  1. Press the Reset button to load the new uboot

However, this method failed

No nvme device detacted

image

The FDT did work, however

  1. Load FDT

  2. List it

  3. We can see that we did assign the device, status, and pinctrl

    fdt print /pinctrl@1001f000

Conclusion

I failed to add nvme support to uBoot and the reason is unknow.

May be

  • we can discuss the possible reason for this
  • we should find a way to list all device (pci command give me empty output)
  • we should find a way to make Uboot print detail start log

Any help will be appreciated!

I don’t see that you have added pcie driver to your uboot…i already did some changes but it is not working on r4 yet,maybe my codebase is a better start as nvme already works on r3(mini).

See also [BPI-R4] Boot from M.2 NVMe SSD

I noticed couple things that you did:

  1. you add a pcie-phy@11c00000 in the dts, is that a mt7986 things?
  2. there is a new file drivers/pci/pcie_mediatek_gen3.c is that the “pcie driver” you mentioned? If it is, whats the difference between the NVM Express device / NVM Express PCI device support in Uboot’s config

Thanks for the reference, my guess for this would be change pcie: pcie@11280000 to pcie: pcie@11290000 (and remove the phy?)

BTW, I did find pcie-mediatek.c in the uboot drive, is this not sufficient?

after adding drivers/pci/pcie_mediatek_gen3.c from your repo, here is what I got

I guess error no card detected is solved but the drive can’t init correctly?

You need the gen3 driver for filogic (mt798x) socs and nvme+nvme_pcie and yes,it is the pcie driver i meant.

I have it not got working,but m.2 of r4 should be 0x11290000, 0x11280000 is pcie2 which does not work in linux too (for 5g/lte). Not sure about phy,but on mt7986(r3) phy is needed. And in kernel it is linked to first pcie (0x11280000).

I wonder why you get pinctrl message…i have not added pinctrl as it was not added by john for mt7986 too

I add the pinctrl purely because the DTS from Openwrt says so. I just remove the pinctrl section from my DTS and here is what I got:

pci enum will no longer produce error, but still the nvme does not work.
image

Here is the FDT


Notice that I did not change the clock pin

You removed my debugs? Are you sure 0x11290000 is probed till the end?

Yes Linux uses pinctrl,uboot with johns driver (mt7986) does not,so i have skipped it because it seems there is only 1 possible pinctrl setting for mt798x

What does pci list says on your board? In the other thread it only tries the 0x11280000 controller

Have you the nvme detected in linux? Can you confirm it is 11290000? I have no free nvme currently. Have you removed the resistors because of i2c?

Your status is disabled :stuck_out_tongue: but only last picture…are both from uboot? Not sure if ftd show lists the devicetree uboot uses

Debug related

You mean the printf statement inside pcie_mediatek_gen3.c? No I didn’t.

BTW I add 2 extra print statments here (L263 inside mtk_pcie_startup_port)

But they did not print anything using pci enum

Detect related

The nvme is detected in OpenWRT. I use 0x11290000 because in wrt’s dts there are comments refering to which pcie[0-3] is to which.

image

PCIe address

I added all 4 pci ports.

Other

For the resistors, no I didn’t. (FYI my board version is v1.1)

The status has been overwrite by mt7988a-bananapi-bpi-r4-emmc.dts

&pcie3 {
        status = "okay";
};

I added a print statment that indicate which fuction start exec, and here is what I got image

Let we dive in XD

Edit: I guess the one with return code -19 is the m.2 ssd because the next three is -ENOENT

I would suggest to merge this thread with my thread, because we are trying to do exactly the same.

1 Like

Sure, but I’m kind of new to this forum system, what should I do.

image

I guess I should change the name? But I don’t think mt7988 need a pci-phy, so I comment that part and get:

Why not using my debugs? It shows the controler address too,no need for guessing

I also made the phy optional in driver like it is done in linux driver

I was assuming that there is no config for mt7988, however I’m wrong. I will migrate to your uboot now

See defconfig: add pci options · frank-w/u-boot@a31b305 · GitHub for config :slight_smile:

And debugs are added in multiple commits also for optional phy node

If phy is optional uboot will crash on 3rd pci controller because of a clock (do not know why),so i had disabled last 2

Maybe you need to add pinctrl back as i had leave them because mt7986 not needed it,but maybe mt7988 need them

Imho we cannot merge 2 threads into one,but i referrenced your above so they are linked…we should stay now here,else it gets confusing when continue in the other thread

1 Like

humm, that is interesting. Let me tweak the dts file

BTW, I did cover the i2c pins

Edit: I enable pinctrl but still

Because I’m reaching the post limit, I have to respond here :see_no_evil:

I don’t think the driver use pinctrl either.
May be we could dive into openwrt’s pinctrl/pcie driver to see how they handle this?

Is pinctrl used? Afair pcie driver does not look at it,maybe it needs code to enable the pinctrl…don’t think pinctrl driver does it

I wrote some extra debug info, here is the result. IMO, the dts does not “link” to the physical layer.

I looked up on the driver in Linux and noticed that there is a reset step. So I migrate that part into uboot. https://codebrowser.dev/linux/linux/drivers/pci/controller/pcie-mediatek-gen3.c.html#mtk_pcie_startup_port

Another thing that I notice is that, in official driver, they power up the phy at mtk_pcie_power_up https://codebrowser.dev/linux/linux/drivers/pci/controller/pcie-mediatek-gen3.c.html#mtk_pcie_power_up So I was measuring the voltage out from M.2. But the result is not clear to me and the SSD controller is hot which does not seem like a power issue