Skip to content

drivers: realtek: support PM device on all Bee SoCs - #316

Closed
YuzhuoLiuRTK wants to merge 18 commits into
rtkconnectivity:realtek-main-v4.4from
YuzhuoLiuRTK:downstream/v4.4/drivers/add-pm-device
Closed

YuzhuoLiuRTK wants to merge 18 commits into
rtkconnectivity:realtek-main-v4.4from
YuzhuoLiuRTK:downstream/v4.4/drivers/add-pm-device

Conversation

@YuzhuoLiuRTK

@YuzhuoLiuRTK YuzhuoLiuRTK commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Support the Zephyr PM device actions on every Realtek Bee driver, instead of only on the SoC series whose HAL drives an Arm PCK-600 power control kit.

  • With a PCK-600 (RTL87X2J) a peripheral keeps its own power and clock domain across a low power state, retains its registers and switches its pads in hardware, so a device only drops its always-on power request.
  • Without one (RTL87X2G, RTL8752H) the platform power manager takes the whole SoC down, so a driver saves and restores its registers, gates its clock and switches its pads itself.

A new hidden Kconfig symbol, REALTEK_BEE_HAS_PCK600, lets each driver key on that capability instead of on the SoC series.

Drivers covered: counter, pwm, gpio, adc, i2c, ir, spi, qdec, can, i2s, codec, keyscan, uart. Where the HAL provides a save buffer the registers are restored from it, otherwise resume replays the configuration the driver last applied.
uart, i2s, codec and keyscan also register a platform PM check callback: a device runtime reference alone cannot hold off the platform low power state, because the platform takes the SoC down without asking the devices.

Dependency

This series does not build on its own. It has to go in together with the companion hal_realtek pull request: rtkconnectivity/hal_realtek#22

@YuzhuoLiuRTK
YuzhuoLiuRTK force-pushed the downstream/v4.4/drivers/add-pm-device branch from 87d075e to b4c8edf Compare October 9, 2026 01:53
@YuzhuoLiuRTK
YuzhuoLiuRTK marked this pull request as ready for review October 9, 2026 01:54
@YuzhuoLiuRTK
YuzhuoLiuRTK force-pushed the downstream/v4.4/drivers/add-pm-device branch 2 times, most recently from 2015176 to 7ae6462 Compare October 14, 2026 02:41
YuzhuoLiuRTK and others added 18 commits October 14, 2026 10:49
Where the HAL drives the Arm PCK-600 power control kit, a peripheral keeps
its own power and clock domain across a low power state and switches its
pads in hardware. Where it does not, the platform power manager takes the
whole SoC down and a driver has to save, restore and switch pads itself.

Add REALTEK_BEE_HAS_PCK600 so that a driver can key on that capability
instead of on the SoC series.

Co-Authored-By: Claude Opus 5 <[email protected]>
Signed-off-by: Yuzhuo Liu <[email protected]>
The sleep hardware state and the wakeup polarity of a pin were only
described for RTL87X2J, the only series whose drivers configured a pad for
a low power state. Every series needs them now.

Co-Authored-By: Claude Opus 5 <[email protected]>
Signed-off-by: Yuzhuo Liu <[email protected]>
Without the PCK-600 power control a timer loses its registers in a low
power state, so the shared timer layer gains a store and a restore
operation per instance and a pair for the registers the instances share.
The shared registers are only stored once per suspend. The HAL does not
declare its DLPS entry points in a header, so declare them there.

The counter and the PWM driver then suspend by saving the registers and
resume by putting them back, the PWM along with its clock and pinctrl
state. A suspended PWM still has to hold its output, so drive the pad by
hand at the level the last duty cycle left it at when no sleep pinctrl
state describes one.

Co-Authored-By: Claude Opus 5 <[email protected]>
Signed-off-by: Yuzhuo Liu <[email protected]>
GPIO_GET_TRIGGER_MODE() and GPIO_GET_TRIGGER_POLARITY() cast the port after
selecting the register instead of before, so on RTL8752H they dereference
the port as the register and read a register that is not theirs.

A both edge interrupt is emulated with a level interrupt whose polarity is
flipped from the handler. The level stays asserted while the handler runs
and the debounce filter keeps the old verdict, so the flip can raise the
very interrupt it just handled. Take the interrupt away around the flip and
clear what the flip raised. An edge that arrives before the debounce filter
validated the level the interrupt was armed on leaves the pin on the other
level, and nothing has been detected in that case, so drop the pin from the
callback set and leave the polarity alone. The hardware both edge trigger
mode is never selected either way, so BEE_GPIO_TRIGGER_BOTH_EDGE goes.

Every interrupt enabled on a pin also armed the pad as a system wakeup
source, which woke the SoC on pins the application never marked as a wakeup
and made a plain input interrupt cost standby current. Honour
GPIO_INT_TRIG_WAKE instead: remember the flag per pin, strip it before
decoding the trigger, and arm the pad only for the pins that carry it.

Co-Authored-By: Claude Opus 5 <[email protected]>
Signed-off-by: Yuzhuo Liu <[email protected]>
Without the PCK-600 power control the GPIO port loses its registers in a
low power state, so add a PM device that saves and restores them.

While the port is down each connected pin holds its own state: an output
keeps its level from the pad, an input with an interrupt enabled arms the
pad as the system wakeup source at the polarity it triggers on, and the pad
goes to software mode. On resume an edge that arrived while the port was
gone is replayed from the level the pin was left at, because it left no
pending bit behind. A debounced pin is left out, its debounce logic raises
the interrupt once the pin settles.

Co-Authored-By: Claude Opus 5 <[email protected]>
Signed-off-by: Yuzhuo Liu <[email protected]>
Pick the low power flow from REALTEK_BEE_HAS_PCK600 instead of the SoC
series. With the PCK-600 power control the controller keeps its context and
only drops its always on power request, which the driver already did.

Without it the registers are gone over a low power state, so add a PM
device that gates the clock and the pads on suspend and restores the
registers on resume. The HAL save buffer is filled at init and the few
registers written at runtime are mirrored into it, which avoids reading the
whole controller back on every suspend.

Co-Authored-By: Claude Opus 5 <[email protected]>
Signed-off-by: Yuzhuo Liu <[email protected]>
Pick the low power flow from REALTEK_BEE_HAS_PCK600 instead of the SoC
series, and drop the lookup that guarded the sleep state, applying it
already reports -ENOENT when it is missing.

Without the PCK-600 power control the registers are gone over a low power
state, so add a PM device that gates the clock and the pads on suspend and
restores the registers on resume. The HAL save buffer is filled when the
bus is configured and the interrupt mask and the enable register are
mirrored into it as they are written.

Co-Authored-By: Claude Opus 5 <[email protected]>
Signed-off-by: Yuzhuo Liu <[email protected]>
Pick the low power flow from REALTEK_BEE_HAS_PCK600 instead of the SoC
series: the sleep pinctrl state that drops the power request is applied at
init where the PCK-600 power control is there.

Without it the registers are gone over a low power state, so add a PM
device that gates the clock and the pads on suspend and restores the
registers on resume. The HAL save buffer is filled whenever the controller
is set up, and resume configures the pin for the direction last selected.

Drop the two comments on which HAL header a SoC series resolves <rtl_ir.h>
to while touching them, the aliases right below already show it.

Co-Authored-By: Claude Opus 5 <[email protected]>
Signed-off-by: Yuzhuo Liu <[email protected]>
Pick the low power flow from REALTEK_BEE_HAS_PCK600 instead of the SoC
series, and drop the lookup that guarded the sleep state, applying it
already reports -ENOENT when it is missing.

Without the PCK-600 power control the registers are gone over a low power
state, so add a PM device that gates the clock and the pads on suspend. The
controller has no HAL save buffer, so resume replays the configuration the
bus was last configured with.

Co-Authored-By: Claude Opus 5 <[email protected]>
Signed-off-by: Yuzhuo Liu <[email protected]>
Without the PCK-600 power control the registers of the non always on
decoder are gone over a low power state, so add a PM device that gates the
clock and the pads on suspend and restores the registers on resume. The HAL
save buffer is filled at init and the interrupt mask is mirrored into it
whenever a trigger is set.

The counter restarts from zero, so suspend folds the count it reached into
a per axis accumulator that a sample adds back, which keeps the reported
rotation continuous.

Co-Authored-By: Claude Opus 5 <[email protected]>
Signed-off-by: Yuzhuo Liu <[email protected]>
Pick the low power flow from REALTEK_BEE_HAS_PCK600 instead of the SoC
series, and drop the lookup that guarded the sleep state, applying it
already reports -ENOENT when it is missing.

Without the PCK-600 power control the registers are gone over a low power
state, so add a PM device that gates the clock and the pads on suspend. The
controller has no HAL save buffer, so resume replays the retained
init_struct and re-enables the interrupts, and a work item waits for the
bus to come back on before restoring the receive message buffers, because
that wait cannot run in the resume path.

can_bee_start() re-initializes the controller on every SoC now rather than
only where deep sleep power gates it. A stopped controller is left behind
by can_bee_stop() everywhere, and re-initializing from the retained
init_struct is what makes a restart pick the configuration back up instead
of report a bus off.

Co-Authored-By: Claude Opus 5 <[email protected]>
Signed-off-by: Yuzhuo Liu <[email protected]>
Without the PCK-600 power control the registers are gone over a low power
state, so add a PM device that gates the clock and the pads on suspend. The
controller has no HAL save buffer, so resume replays the stored i2s_config
of every stream that reached I2S_STATE_READY.

A running stream cannot survive the platform taking the SoC down, and a
device runtime reference does not hold off DLPS on its own, so register a
platform PM check callback that fails while any instance has a stream
running. Streams are refcounted because transmit and receive can run at the
same time.

Co-Authored-By: Claude Opus 5 <[email protected]>
Signed-off-by: Yuzhuo Liu <[email protected]>
The driver reached for the RCC HAL directly, which spells the codec clock
differently on every SoC series and bypasses the clock controller the rest
of the Bee drivers go through.

Take the clock from the devicetree and enable it with the clock control
API. The clocks property becomes required, so describe it on the codec node
of the three boards the i2s_bee sample supports.

Co-Authored-By: Claude Opus 5 <[email protected]>
Signed-off-by: Yuzhuo Liu <[email protected]>
Without the PCK-600 power control the codec is gone over a low power state,
so add a PM device that gates the clock and applies the sleep pads on
suspend and, when output was running, sets the codec up again on resume.
Stopping the output no longer applies the sleep pads itself.

Output cannot survive the platform taking the SoC down, and a device
runtime reference does not hold off DLPS on its own, so register a platform
PM check callback that fails while any instance is running output.

Co-Authored-By: Claude Opus 5 <[email protected]>
Signed-off-by: Yuzhuo Liu <[email protected]>
Pick the low power flow from REALTEK_BEE_HAS_PCK600 instead of the SoC
series, which also lets both flows share the wakeup pad bookkeeping.

Without the PCK-600 power control the registers are gone over a low power
state, so add a PM device that gates the clock and the pads on suspend and
restores the registers on resume. The HAL save buffer is filled when the
controller is set up and the enable and the interrupt mask are mirrored
into it as they are written.

Suspend arms the sleep state row pads as wakeup sources and resume reports
which pad woke the system, so a key pressed while the scanner was down
starts a manual scan. A row already holding a key down cannot wake on
another press, so BEE_INPUT_KEYSCAN_PM_RELEASE_WAKEUP chooses between
waking on the release, which leaks through the pull resistor while the key
is held, and waiting for the next timer wakeup.

A scan in progress cannot survive the platform taking the SoC down, so
register a platform PM check callback that fails while a scan is pending.

The input_dump sample moves its rows on the rtl87x2j evb while at it, P2_4
and P2_5 are not brought out as keys on that board.

Co-Authored-By: Claude Opus 5 <[email protected]>
Signed-off-by: Yuzhuo Liu <[email protected]>
UART_INTConfig() read-modify-writes the interrupt enable register, and so
does the interrupt service routine when it re-arms the RX idle interrupt,
so one can lose the bit the other has just written. Wrap the call in a
helper that holds the interrupt lock and route every call site through it.

dma_start() only rejects a channel the preceding dma_config() has already
accepted, and returning early from there left the UART DMA requests enabled
and the timeout timer running, which is a worse state than letting the
transfer report its own failure.

The async path goes through the DMA driver API and names no DMA HAL
identifier of its own, so drop the per SoC series DMA HAL headers.

Co-Authored-By: Claude Opus 5 <[email protected]>
Signed-off-by: Yuzhuo Liu <[email protected]>
The driver kept the UART clock forced on for as long as a transfer was in
flight, which only works where the PCK-600 power control is there and keeps
the device out of the Zephyr power management.

Implement the PM device actions instead. Without a PCK-600 suspend saves
the registers through the HAL low power store hook and resume restores
them. A device runtime reference is taken for as long as the transmitter or
the receiver is enabled, so a UART nobody uses can be suspended while the
rest of the system runs.

A UART marked as a wakeup source arms its RX pad before suspending and,
once it has woken the system up, holds a reference for
UART_BEE_KEEP_ACTIVE_TIMEOUT_MSEC so that the incoming frame is received.
Holding off the platform low power state needs a check callback on top of
the device reference, because the platform takes the SoC down without
asking the devices.

Drop the always-clock-force-on devicetree property, which has no user left
now that the device runtime power management decides when the UART may be
powered down.

Co-Authored-By: Claude Opus 5 <[email protected]>
Signed-off-by: Yuzhuo Liu <[email protected]>
The test picked its power management code by SoC series, which no longer
says anything now that every Bee SoC supports the PM device actions. What
differs is the PCK-600: with one the kernel idle enters the low power state
on its own, without one the platform power manager takes the whole SoC down
once every check callback agrees. Key the platform callbacks on that, and
wrap the wakeup counter, which only a PCK-600 keeps, in a macro.

Ask for a wakeup capable interrupt through the GPIO_INT_WAKEUP flag rather
than a Bee specific flag, and turn on the device runtime power management
on the boards that have a PCK-600.

Add a shell command that hands back the device runtime reference the serial
shell backend holds on its UART, which is what keeps the whole system out
of a low power state. The driver takes its own reference around every
transfer and arms a pad wakeup in between, so the first incoming frame
resumes the UART and the shell stays usable.

Drop the RTC RAP GPIO toggle test, which drives the RTC and the RAP
(Realtek Action Peripheral) through the HAL behind the back of the RTC
driver, so it only runs when the counter node is disabled and has to
undefine GPIO_INT_MASK to include the HAL headers at all.

Co-Authored-By: Claude Opus 5 <[email protected]>
Signed-off-by: Yuzhuo Liu <[email protected]>
@YuzhuoLiuRTK
YuzhuoLiuRTK force-pushed the downstream/v4.4/drivers/add-pm-device branch from 7ae6462 to b695811 Compare October 14, 2026 06:04
@YuzhuoLiuRTK
YuzhuoLiuRTK marked this pull request as draft October 16, 2026 03:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant