BPI-M5 with Armbian and UARTs/I2C/SPI overlays

Hello there,

I wonder if I’m the only one with this problem, or perhaps others who care about the same requirements, are just more competent on embedded linux than I am (which is not very hard, I admit).

I’m trying to get my M5 running with a maintained linux that can :

  • keep itself updated with its package manager, and ideally follow major upgrades
  • run docker
  • UARTs support (I’ll be needing two among UART_EE_A on header gpio pins 8 & 10, UART_EE_B on pins 33 & 15, UART_AO_B on pins 12 & 37)
  • I2C support (have a RTC and a Matrix orbital graphic display there)
  • SPI support (for a RFM69 radio with mysensors, but later)

The images linked on the BPi wiki are ok for short term, I tested ubuntu server 20.04 and debian buster, however I noticed that both have the kernel frozen in 4.9. Although dist upgrade to 22 for ubuntu, and bullseye for debian, did work, this broke docker as the immediately noticeable result. And it is probably only the tip of the iceberg.

Using recent binaries and libs on top of an outdated kernel looks to me like asking for trouble.

And, even with docker, containers run on the same kernel so… I’ll run into trouble with the containerized software as well.

I also tried the armbian image from BPi, but first apt upgrade replaces the kernel and kicks all overlays out. So I’d have to freeze the kernel, so back with the maintenability issue, even if I’d have more time with a 6.x kernel. That, and only one UART works.

So unless I missed something, so far, official Armbian fits the bill for the maintainable part, but it seems to lack the I/O support part.

With

# cat /boot/armbianEnv.txt
verbosity=1
console=both
overlay_prefix=meson
rootdev=UUID=b0ae2d42-5b5d-41cc-bf0c-3dad452a18d4
rootfstype=ext4
overlays=i2cA i2cB uartA uartC
user_overlays=
usbstoragequirks=0x2537:0x1066:u,0x2537:0x1068:u

Here are the provider overlay’s result at the debug console :

Applying kernel provided DT overlay meson-i2cA.dtbo
failed on fdt_overlay_apply(): FDT_ERR_NOTFOUND
343 bytes read in 2 ms (167 KiB/s)
Applying kernel provided DT overlay meson-i2cB.dtbo
failed on fdt_overlay_apply(): FDT_ERR_BADMAGIC
base fdt does did not have a /__symbols__ node
make sure you've compiled with -@
238 bytes read in 2 ms (116.2 KiB/s)
Applying kernel provided DT overlay meson-uartA.dtbo
failed on fdt_overlay_apply(): FDT_ERR_BADMAGIC
base fdt does did not have a /__symbols__ node
make sure you've compiled with -@
238 bytes read in 2 ms (116.2 KiB/s)
Applying kernel provided DT overlay meson-uartC.dtbo
failed on fdt_overlay_apply(): FDT_ERR_BADMAGIC
base fdt does did not have a /__symbols__ node
make sure you've compiled with -@
Error applying DT overlays, restoring original DT

This issue is mentioned in a post on the armbian forum, but so far no news.

So my question is : how difficult is it to create those overlays, and what is needed ? I keep looking at documentation about the subject, but so far it is totally esoteric to me and I don’t even know if it is possible on the stock armbian distribution, or if building a system from scratch is needed. Or perhaps someone already has dts working for armbian ?

Having to recompile them when the kernel gets updated would be a relatively little constraint. The uses of the UARTs will be monitored so if I break something with a kernel upgrade, I will know it quickly.

Thanks for reading.

I’ll keep the post updated if I find something, of course.

It seems armbian shipped overlays have incorrect addresses in target paths for bpi-m5 (probably meant for another amlogic board). You can decompile bpi-m5 dtb and needed overlay dtbos with dtc -Idtb -Odts -o file.dts file.dtb, find correct addresses in decompiled bpi-m5 dts file and fix them in decompiled overlay dts files. Then compile overlays with dtc -@ -Idts -Odtb -o file.dtbo file.dtso. You can save them under different filenames to prevent overwriting on upgrade and change armbianEnv.txt accordingly. And file a bug on armbian about the issue.

Thanks for answering. As I said I’m completely ignorant on the topic and hours (days?) of reading docs on the subject away from understanting what I’m doing. Last time I even compiled a kernel was… can’t remember. And that was on x86, already had ACPI and sundries. Only ARM boards I had before were RPi with their OS, but then this is hw and sw bundled together by the same org.

If I get it correctly, I should, for example for uartA :

  • On the official armbian, decompile the shipped dtbo :

dtc -Idtb -Odts -o ~/meson-uartA.dts /boot/dtb-6.1.11-meson64/amlogic/overlay/meson-uartA.dtbo

  • On any bpi distro (armbian or would debian do ? the bpi armbian had only the first uart working if I remember) : same with its matching uart1.dtbo

  • Pick address(es) from the second dts file and replace in the first, ~/meson-uartA.dts

  • Recompile the modified dts :

dtc -@ -Idts -Odtb -o /boot/overlay-user/uart1.dtbo ~/meson-uartA.dts

However there’s something disturbing :

BPi Debian’s decompiled uart1.dtbo has no address

/ {

	fragment@0 {
		target = < 0xffffffff >;

		__overlay__ {
			status = "okay";
		};
	};

	__fixups__ {
		uart_A = "/fragment@0:target:0";
	};
};

but uart_A matches meson64_bananapi_m5.dtbo which contains

/ {
	...
	__symbols__ {
		...
		uart_A = "/serial@ffd24000";
		...
	};
};

BPi’s Armbian from meson-uartA.dtbo :

/ {
	compatible = "amlogic,meson-gxbb";

	fragment@0 {
		target-path = "/soc/bus@c1100000/serial@84c0";

		__overlay__ {
			status = "okay";
		};
	};
};

The addresses are completely different. This is getting me real confused.

As for filing a bug to armbian, I’ll first try to understand things a little more, because right now apart of telling them “huh ! doesn’t work !” my contribution will be next to useless :slight_smile:

I grabbed addresses from the BPi debian image’s decompiled meson-sm1-bananapi-m5.dts, which gave me :

            target-path = "/soc/bus@ffd00000/serial@24000";
            target-path = "/soc/bus@ffd00000/serial@23000";
            target-path = "/soc/bus@ffd00000/serial@22000";
            target-path = "/soc/aobus@ff800000/serial@4000";

So I tried :

dtc -Idtb -Odts -o ~/meson-uartA.dts /boot/dtb-6.1.11-meson64/amlogic/overlay/meson-uartA.dtbo
cp ~/meson-uartA.dts uart1.dts
sed -i -e 's!bus@c1100000!bus@ffd00000!' -e 's!serial@84c0!serial@24000!' uart1.dts

cat uart1.dts
/dts-v1/;

/ {
    compatible = "amlogic,meson-gxbb";

    fragment@0 {
            target-path = "/soc/bus@ffd00000/serial@24000";

            __overlay__ {
                    status = "okay";
            };
    };
};

dtc -@ -Idts -Odtb -o /boot/overlay-user/uart1.dtbo ~/uart1.dts

And so on with uart2, uart3, uart4.

Compiled overlays uart[123] load fine

Applying user provided DT overlay uart1.dtbo
238 bytes read in 2 ms (116.2 KiB/s)
Applying user provided DT overlay uart2.dtbo
238 bytes read in 2 ms (116.2 KiB/s)
Applying user provided DT overlay uart3.dtbo
232 bytes read in 3 ms (75.2 KiB/s)
Applying kernel provided DT fixup script (meson-fixup.scr)

Not uart4 though (would be UART_AO_B on pins 12&37) :

Applying user provided DT overlay uart4.dtbo
failed on fdt_overlay_apply(): FDT_ERR_NOTFOUND
Error applying DT overlays, restoring original DT

I’m dropping this one for now, I’ll figure it out later.

So, with

user_overlays=uart1 uart2 uart3

in my /boot/armbianEnv.txt I get

pi@gtlpi:~$ ls -l /dev/ttyAML*
crw--w---- 1 root tty     243, 0 21 avril 16:50 /dev/ttyAML0
crw-rw---- 1 root dialout 243, 6 21 avril 16:50 /dev/ttyAML6
crw-rw---- 1 root dialout 243, 7 21 avril 16:50 /dev/ttyAML7
crw-rw---- 1 root dialout 243, 8 21 avril 16:50 /dev/ttyAML8

I can set their baudrate/data format, open them in r/w in python or screen, all without error.

But they’re all deaf and mute : a usb/serial adapter wired on the expected pins, nothing either in or out.

Isn’t there something with missing setting up mux so the signals from the UARTs get routed to the actual SOC pins ?

I throw that guess because the SOC pins on the schematic have multiple labels that imply pins are multi-purpose, but I have no idea how this selection is done.

Got it working for the first two UARTs

uart1 on GPIO pins 8(tx),10(rx)

/dts-v1/;

/ {

    compatible = "amlogic,meson-gxbb";

    fragment@0 {
        target-path = "/aliases";

        __overlay__ {
            serial1 = "/soc/bus@ffd00000/serial@24000";
        };
    };

    fragment@1 {
        target-path = "/soc/bus@ffd00000/serial@24000";

        __overlay__ {
            pinctrl-0 = <0xdc 0xdd>;
            pinctrl-names = "default";
            status = "okay";
        };
    };
};

uart2 on GPIO pins 33(tx), 15(rx)

/dts-v1/;

/ {
    compatible = "amlogic,meson-gxbb";

    fragment@0 {
        target-path = "/aliases";

        __overlay__ {
            serial2 = "/soc/bus@ffd00000/serial@23000";
        };

    };

    fragment@1 {
        target-path = "/soc/bus@ffd00000/serial@23000";

        __overlay__ {
            pinctrl-0 = <0xde>;
            pinctrl-names = "default";
            status = "okay";
        };
    };
};

Some things that helped when I understood them :

  • that pinctrl-0 refer to phandles of nodes already defined in /boot/dtb-6.1.11-meson64/amlogic/meson-sm1-bananapi-m5.dtb that ships with the official Armbian
  • target-path = “/aliases” fragment maps the peripheral to the correct /dev/ttyAMLx number

And uart4 on GPIO pins 12(tx), 37 (rx)

/dts- v1/;

/ {
    compatible = "amlogic,meson-gxbb";

    fragment@0 {
        target-path = "/aliases";

        __overlay__ {
            serial4 = "/soc/bus@ff800000/serial@4000";
        };
    };

    fragment@1 {
        target-path = "/soc/bus@ff800000/serial@4000";

        __overlay__ {
            pinctrl-0 = <0x103>;
            pinctrl-names = "default";
            status = "okay";
        };
    };
};

I2C on pins 5(scl), 3(sda)

/dts-v 1/;

/ {
    compatible = "amlogic,meson-gxbb";

    fragment@0 {
        target-path = "/aliases";

        __overlay__ {
            i2c0 = "/soc/bus@ffd00000/i2c@1d000";
        };
    };

    fragment@1 {
        target-path = "/soc/bus@ffd00000/i2c@1d000";

        __overlay__ {
            pinctrl-0 = <0x6f 0x70>;
            pinctrl-names = "default";
            status = "okay";
        };
    };
}

I don’t know how this will survive a kernel upgrade though, if the pinctrl phandles change. But this will be easy to fix then.