[RFC PATCH 0/3] Fix poweroff restarting the board on Firefly-RK3399

Yaozhong Li posted 3 patches 2 weeks, 2 days ago
There is a newer version of this series
.../bindings/mfd/rockchip,rk808.yaml          | 20 +++++++++++++
.../boot/dts/rockchip/rk3399-firefly.dts      |  4 ++-
drivers/mfd/rk8xx-core.c                      | 29 +++++++++++++++++++
include/linux/mfd/rk808.h                     |  4 +++
4 files changed, 56 insertions(+), 1 deletion(-)
[RFC PATCH 0/3] Fix poweroff restarting the board on Firefly-RK3399
Posted by Yaozhong Li 2 weeks, 2 days ago
Sent as an RFC: the open questions below are about where this belongs and
what the property should be called, and the patched kernel itself has not
been booted on hardware yet (see Testing).

On the Firefly-RK3399, "poweroff" drops the rails and immediately brings
them back up, so the board reboots instead of staying off. U-Boot reports
the result as a power-on reset.

Firefly's BSP drives two SoC pins low during shutdown, before writing the
RK808's shutdown bit: GPIO1_D0 and GPIO1_B5. Mainline does not describe
GPIO1_D0 at all, and describes GPIO1_B5 as the backlight enable GPIO,
although the vendor's own backlight node has no enable GPIO. Nothing
therefore releases either line at power-off.

What was established on the board is the sequence, not what happens inside
the PMIC: the shutdown sticks only when GPIO1_D0 goes from high to low
during power-off prepare, with GPIO1_B5 low, and the shutdown bit written
after a settle delay. Leaving GPIO1_B5 asserted makes the board come back
up even when GPIO1_D0 is released correctly, which is why the backlight
cannot keep that pin. Why the board powers back up in the failing cases is
not explained here.

Why not gpio-poweroff
---------------------

gpio-poweroff drives the line active, back to inactive, then active again,
waits timeout-ms and then WARN()s on the assumption that it is itself
performing the power off, and it registers at SYS_OFF_MODE_POWER_OFF. Here
the RK808 performs the power off from its own POWER_OFF_PREPARE handler,
the lines only have to be released and left released, and there are two of
them.

rk3188-bqedison2qc.dts does drive a pwr_hold pin from gpio-poweroff, which
works there because pulling that line low is by itself enough to cut the
power: the board is gone during the first active-delay-ms and the rest of
the sequence never runs. That is not the case here - driving a line low at
runtime, with no PMIC access at all, left this board running for the 10 s
it was observed.

Open questions
--------------

1. Does this belong in the PMIC driver? Releasing the lines inside
   rk808_power_off() keeps the ordering against the I2C write explicit and
   needs no second handler, but it does put board level wiring into the
   PMIC MFD driver. A separate driver registering at POWER_OFF_PREPARE
   with a higher priority would work too.

2. Property naming. power-hold-gpios follows the <function>-gpios
   convention, like the existing dvs-gpios in this binding. If the binding
   should describe Rockchip specific board wiring rather than an RK8xx
   feature, rockchip,power-hold-gpios would be more appropriate. The
   vendor DT calls these pins pmic,stby-gpio and pmic,hold-gpio.

3. Should the DT also carry a pinctrl group for the pins, as the vendor DT
   does? It works here without one, so nothing untested was added.

4. Dropping the backlight enable-gpios is required for the sequence to
   work, but it also means the backlight loses an enable GPIO it may
   genuinely have wanted. The vendor's backlight node has none and is
   disabled entirely, which suggests the mainline property was a mistake,
   but nobody here has the schematic to confirm it.

Testing
-------

Tested on a Firefly-RK3399 (4 GB, RK808) with the shutdown captured on the
debug UART at 1500000 8N1. A run counts as "stayed off" only if the
console produced nothing for 150 s and there was no ICMP reply and no USB
gadget afterwards; a restart is unambiguous because "DDR Version" and
"Reset cause: POR" appear on the console within seconds.

The patched kernel has NOT been booted: CONFIG_MFD_RK8XX is built in on
the test system and a full kernel build did not fit on it. The series
compiles (aarch64, W=1, no warnings). dt_binding_check passes; dtbs_check
reports only the pre-existing usb2phy diagnostics for this board,
reproduced unchanged on the unpatched tree. The functional evidence comes
from an out-of-tree module using the same gpiod array consumer name, the
same GPIOD_OUT_HIGH, the same per-descriptor gpiod_set_value_cansleep()
loop and the same msleep() as this series, against a device tree carrying
exactly these properties. It registers at POWER_OFF_PREPARE with
SYS_OFF_PRIO_HIGH + 1, immediately ahead of the RK808 handler. It does not
reproduce the RK808 acquiring the array at probe time.

Every run started from a cold boot with both pins at their reset state
(inputs, low), verified by reading the GPIO registers beforehand:

  both lines, as in this series:            3 of 3 stayed off
  GPIO1_D0 only, GPIO1_B5 left low:        3 of 3 stayed off
  GPIO1_B5 only, GPIO1_D0 left low:        0 of 2 stayed off
  GPIO1_D0 released, GPIO1_B5 left high:   0 of 2 stayed off
  nothing driven (mainline today):         restarts after about 2 s

Note the second and third rows: on this particular board pwm-backlight
does not bind, so GPIO1_B5 stays an input and describing GPIO1_D0 alone
was enough here. That is not true in general, which is why both lines are
described and the backlight property is dropped.

The 200 ms is the value the vendor uses; the threshold was not
characterised. The gpiod calls and msleep() run in POWER_OFF_PREPARE,
which is allowed to sleep - the msleep() was measured at 200 to 201 ms on
the console timestamps.

One early run appeared to show GPIO1_B5 alone was sufficient. It had
reached that state through a warm reboot rather than a cold boot, so the
pin states were not what they were assumed to be; it is excluded, and it
is not fully explained by the description above either.


This series was prepared with AI assistance (Claude); the analysis and
the measurements on the board were reviewed by the author.

Yaozhong Li (3):
  dt-bindings: mfd: rk808: add board level power hold GPIOs
  mfd: rk8xx: release the power hold GPIOs before powering off
  arm64: dts: rockchip: fix power-off on Firefly-RK3399

 .../bindings/mfd/rockchip,rk808.yaml          | 20 +++++++++++++
 .../boot/dts/rockchip/rk3399-firefly.dts      |  4 ++-
 drivers/mfd/rk8xx-core.c                      | 29 +++++++++++++++++++
 include/linux/mfd/rk808.h                     |  4 +++
 4 files changed, 56 insertions(+), 1 deletion(-)

-- 
2.55.0.windows.3
[RFC PATCH v2 0/3] Fix poweroff restarting the board on Firefly-RK3399
Posted by Yaozhong Li 6 days, 6 hours ago
Still an RFC: the patched kernel has not been booted on hardware (see
Testing), and the open questions from v1 about placement and naming are
unanswered.

v1: https://lore.kernel.org/all/20260909092728.1859-1-yaozhonguwl@gmail.com/

Changes since v1, addressing the Sashiko review Lee Jones asked me to act on
--------------------------------------------------------------------------

* [Medium] "Acquiring 'power-hold' GPIOs after registering MFD child devices
  can cause probe deferral thrashing."

  Fixed, and the finding was correct. v1 claimed the array after
  devm_mfd_add_devices(), so a -EPROBE_DEFER from the GPIO provider would
  have unwound the regulators, RTC and clocks on every retry. v2 claims it
  before devm_regmap_add_irq_chip(), i.e. before anything is registered on
  the device: the property test is hoisted into a bool and reused for the
  sys-off registration further down, which is otherwise unchanged. A comment
  records why the acquisition has to stay there.

  Worth noting that this is exactly the part v1 disclosed as untested - the
  out-of-tree proxy used for the functional testing claims the GPIOs at
  module load, so it never exercises the probe path at all.

* [Low] "The commit message description violates the MFD subsystem formatting
  rules by using a lowercase letter."

  Fixed: "mfd: rk8xx: Release ...". The dt-bindings and arm64 dts subjects
  keep their own subsystems' lowercase convention.

* The binding now states that power-hold-gpios is only meaningful together
  with system-power-controller, which is what the driver implements. This is
  not expressed as a "dependencies" entry because the driver also accepts the
  deprecated rockchip,system-power-controller, and a dependency on one
  spelling would reject device trees using the other.

* checkpatch --strict alignment fix in the GPIO acquisition; no functional
  change.

The problem
-----------

On the Firefly-RK3399, "poweroff" drops the rails and immediately brings
them back up, so the board reboots instead of staying off. U-Boot reports
the result as a power-on reset.

Firefly's BSP drives two SoC pins low during shutdown, before writing the
RK808's shutdown bit: GPIO1_D0 and GPIO1_B5. Mainline does not describe
GPIO1_D0 at all, and describes GPIO1_B5 as the backlight enable GPIO,
although the vendor's own backlight node has no enable GPIO. Nothing
therefore releases either line at power-off.

What was established on the board is the sequence, not what happens inside
the PMIC: the shutdown sticks only when GPIO1_D0 goes from high to low
during power-off prepare, with GPIO1_B5 low, and the shutdown bit written
after a settle delay. Leaving GPIO1_B5 asserted makes the board come back
up even when GPIO1_D0 is released correctly, which is why the backlight
cannot keep that pin.

Why not gpio-poweroff
---------------------

gpio-poweroff drives the line active, back to inactive, then active again,
waits timeout-ms and then WARN()s on the assumption that it is itself
performing the power off, and it registers at SYS_OFF_MODE_POWER_OFF. Here
the RK808 performs the power off from its own POWER_OFF_PREPARE handler,
the lines only have to be released and left released, and there are two of
them.

rk3188-bqedison2qc.dts does drive a pwr_hold pin from gpio-poweroff, which
works there because pulling that line low is by itself enough to cut the
power. That is not the case here - driving a line low at runtime, with no
PMIC access at all, left this board running for the 10 s it was observed.

Open questions (unchanged from v1)
----------------------------------

1. Does this belong in the PMIC driver at all, rather than a small separate
   driver registering at POWER_OFF_PREPARE with a higher priority?
2. Should the property be rockchip,power-hold-gpios? The vendor DT calls
   these pins pmic,stby-gpio and pmic,hold-gpio.
3. Should the DT also carry a pinctrl group for the pins, as the vendor does?
4. Dropping the backlight enable-gpios is required for the sequence to work,
   but nobody here has the schematic to confirm that property was wrong to
   begin with.

Testing
-------

Tested on a Firefly-RK3399 (4 GB, RK808), shutdown captured on the debug
UART at 1500000 8N1. A run counts as "stayed off" only if the console
produced nothing for 150 s and there was no ICMP reply and no USB gadget
afterwards.

The patched kernel has NOT been booted: CONFIG_MFD_RK8XX is built in on the
test system and a full kernel build does not fit on it. The series compiles
(aarch64, W=1, no warnings), checkpatch --strict is clean, dt_binding_check
passes, and dtbs_check reports only the pre-existing usb2phy diagnostics for
this board, reproduced unchanged on the unpatched tree.

The functional evidence comes from an out-of-tree module using the same
gpiod array consumer name, the same GPIOD_OUT_HIGH, the same per-descriptor
gpiod_set_value_cansleep() loop and the same msleep() as this series,
against a device tree carrying exactly these properties, registered at
POWER_OFF_PREPARE with SYS_OFF_PRIO_HIGH + 1. It does not exercise the probe
path, so the v2 reordering above is not covered by it.

Every run started from a cold boot with both pins at their reset state
(inputs, low), verified by reading the GPIO registers beforehand:

  both lines, as in this series:            3 of 3 stayed off
  GPIO1_D0 only, GPIO1_B5 left low:        3 of 3 stayed off
  GPIO1_B5 only, GPIO1_D0 left low:        0 of 2 stayed off
  GPIO1_D0 released, GPIO1_B5 left high:   0 of 2 stayed off
  nothing driven (mainline today):         restarts after about 2 s

On this particular board pwm-backlight does not bind, so GPIO1_B5 stays an
input and describing GPIO1_D0 alone was enough here. That is not true in
general, which is why both lines are described and the backlight property is
dropped.

The 200 ms is the value the vendor uses; the threshold was not characterised.
The msleep() was measured at 200 to 201 ms on the console timestamps.


This series was prepared with AI assistance (Claude); the analysis and
the measurements on the board were reviewed by the author.

Yaozhong Li (3):
  dt-bindings: mfd: rk808: add board level power hold GPIOs
  mfd: rk8xx: Release the power hold GPIOs before powering off
  arm64: dts: rockchip: fix power-off on Firefly-RK3399

 .../bindings/mfd/rockchip,rk808.yaml          | 21 +++++++++
 .../boot/dts/rockchip/rk3399-firefly.dts      |  4 +-
 drivers/mfd/rk8xx-core.c                      | 47 ++++++++++++++++++-
 include/linux/mfd/rk808.h                     |  4 ++
 4 files changed, 73 insertions(+), 3 deletions(-)

-- 
2.55.0.windows.3