The raspberry pi 3B doesn’t present /sys/class/leds/PWR when you’re running the upstream kernel rather than the raspberry pi foundation’s official fork. You get /sys/class/leds/ACT which controls the green LED correctly, but no PWR entry for the red one. You’ll also see that dtparam=pwr_led_trigger=none and related commands that you are advised to add to config.txt do nothing.

This is because the upstream device-tree simply doesn’t have an entry for it, as it’s implemented weirdly in the hardware. The fix is to define it yourself. Click here to skip to the code, or read on for an explanation.

What is the devicetree?

On an x86 machine, the operating system uses ACPI to query the hardware on boot to find out what devices are present. On arm there’s a simpler system whereby the firmware passes the device definitions to the kernel directly. This data is called the devicetree. On the raspberry pi, the firmware parses dtparam and other commands from your config.txt, allowing you to apply so-called “overlays” to the tree, to do things like turn off the LEDS.

Why doesn’t it work on mainline

Mainline ships bcm2837-rpi-3-b.dtb which only defines the green ACT led. Raspberry Pi’s downstream fork ships bcm2710-rpi-3-b.dts (they use a different name for the chip) which also defines PWR.

It’s like this because the pi3b reuses the power LED pin for the undervoltage warning signal. When expgpio line 7 is requested as an output it provides the warning signal, and the chip fixes the LED to on, until it detects an undervoltage, when it flashes.

When it’s an input, we can use it to control the LED. The downstream raspi kernel configures it in this latter way (with default-trigger), whereas the upstream mainline kernel being more conservative leaves it in the default warning mode, calling it PWR_LOW_N.

The other issue is that on mainline, you’re likely using u-boot instead of the official setup which has the bootloader loading straight into the kernel, so any dtparam commands will have no effect.

How do I fix it?

You need to patch the devicetree yourself. If you’re on NixOS, you just set this:

hardware.deviceTree = {
    enable = true;
    filter = "bcm2837-rpi-3-b.dtb";

    overlays = [
      {
        name = "leds-off";
        dtsText = ''
          /dts-v1/;
          /plugin/;

          / {
            compatible = "raspberrypi,3-model-b";

            fragment@0 {
              target = <&led_act>;
              __overlay__ {
                linux,default-trigger = "none";
                default-state = "off";
              };
            };

            fragment@1 {
              target = <&leds>;
              __overlay__ {
                led_pwr: led-pwr {
                  label = "PWR";
                  gpios = <&expgpio 7 0>;
                  linux,default-trigger = "none";
                  default-state = "off";
                };
              };
            };
          };
        '';
      }
    ];
  };

…and you’re done. Such is life as a NixOS user. This will patch the devicetree definition at your next rebuild.

This overlay actually does two jobs, it defines the power LED on expgpio 7, and it also sets the default-state of both leds to off. The nice thing about this is that the LEDs will never come on in the first place. You could of course define a more minimal overlay, and then turn the LEDs off in userspace using udev rules like so:

ACTION=="add", SUBSYSTEM=="leds", KERNEL=="ACT", ATTR{trigger}="none", ATTR{brightness}="0"
ACTION=="add", SUBSYSTEM=="leds", KERNEL=="PWR", ATTR{trigger}="none", ATTR{brightness}="0"

But this will have them turn on and then off again at boot.

Non-NixOS setup

Users of other distros will want to manually compile the above dtsText definition using the dtc command, place the resulting .dtbo somewhere in /boot according to your distro’s layout, and then configure the FDTOVERLAYS keyword for u-boot in extlinux.conf. This apparently requires a newer u-boot built with the CONFIG_OF_LIBFDT_OVERLAY feature. Or you can patch the binary definitions directly. You’re on your own I’m afraid.

Will this blow up my board?

Well firstly this is just placing it into the pi foundation’s shipped default configuration, so no. If you are using an inadequate power supply you will not be warned with a flashing light, but this only disables the warning, there is no actual undervoltage protection to be disabled. I think you will still get kernel log messages though, regardless you should make sure you’re not undervolting the thing. Have fun!