Skip to content

Conversation

SeppoTakalo
Copy link
Contributor

@SeppoTakalo SeppoTakalo commented Oct 10, 2025

I'm putting up this PR as a RFC for now until I have been able to split the smaller commits to separate PR and to allow reviewers to see what the end goal is. It is easier to argue why I change something if I show the intent here.

I have so far submitted changes from this work on following PRs:

Description of the work: CMUX Power Saving

The 3GPP TS 27.010 specifies a power saving mechanism for CMUX which can be used in Zephyr when the modem supports it.

The power saving mechanism is covered in the following sections on the specification:

  • 5.2.5 Inter-frame Fill
  • 5.4.6.3.2 Power Saving Control (PSC) message
  • 5.4.7 Power Control and Wake-up Mechanisms
image

States are explained in the generated documen in OS Services -> Modem Modules.

The power saving mechanism allows runtime power management for the UART device used by the CMUX module. When there is no data to be sent or received on any DLCI channel, the CMUX module will enter the idle state after a configurable timeout. In the idle state, the CMUX module will send a Power Saving Control message to the modem, requesting it to enter a low power state. The CMUX module may then close the pipe device, allowing the UART device to be powered down if runtime power management is enabled.

PIPE backend is in response of actually power controlling the UART as it is the last endpoint in PIPE interface
image

When data is to be sent or received on any DLCI channel, the CMUX module will exit the idle state and wakes the modem up by sending flag characters until it receives a flag character from the modem.

Some modems allow UART to be powered down only when the DTR (Data Terminal Ready) signal is de-asserted. In this case, a UART device with DTR support can be used with the CMUX module to control the DTR signal based on the power state of the UART.

Waking up on incoming data when UART is powered down requires a modem that supports RING signal to wake up the host. The RING signal is handled by the modem driver and it opens the pipe device when the RING signal is detected, allowing the CMUX module to wake up the modem and process incoming data.

CMUX options are controlled using Device Tree settings, because CMUX is a modem dependant.

cmux-enable-runtime-power-save:
  type: boolean
  description: Enable runtime power saving using CMUX PSC commands.
               This requires modem to support CMUX and PSC commands while keeping the data
               connection active.
cmux-close-pipe-on-power-save:
  type: boolean
  description: Close the modem pipe when entering power save mode.
              When runtime power management is enabled, this closes the UART.
              This requires modem to support waking up the UART using RING signal.
cmux-idle-timeout-ms:
  type: int
  description: Time in milliseconds after which CMUX will enter power save mode.
  default: 10000

Example setup where CMUX power control is enabled and UART is allowed to shut down and control the DTR line:

&uart1 {
  status = "okay";
  zephyr,pm-device-runtime-auto;

  uart_dtr: uart-dtr {
    compatible = "zephyr,uart-dtr";
    dtr-gpios = <&interface_to_nrf9160 4 GPIO_ACTIVE_LOW>;
    status = "okay";
    zephyr,pm-device-runtime-auto;

    modem: modem {
      compatible = "nordic,nrf91-slm";
      status = "okay";
      mdm-ring-gpios = <&interface_to_nrf9160 5 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
      zephyr,pm-device-runtime-auto;
      cmux-enable-runtime-power-save;
      cmux-close-pipe-on-power-save;
      cmux-idle-timeout-ms = <5000>;
    };
  };
};

This PR implements CMUX Power Saving in a way that it allows Zephyr host to keep PPP connection up while the underlying UART is powered down and the module may enter eDRX or PSM sleep modes. Waking up is completely automatic when PPP is sending data. Incoming data wakes up the modem modules using RING signal.

UART power management requires DTR and RING as the CMUX itself can only initiate wake up by sending flag characters.

The usage of DTR pin is typical in such modems that allow disconnecting the UART:
image

However, even if UART cannot be shut down, modem MAY benefit from the knowledge that CMUX is in the sleep mode.
Without DTR/power off, this PR has been tested against Quectel BG96 modem. With DTR and power control, I have tested this against ongoing work of nRF9160 Serial Modem application.

@zephyrbot zephyrbot added area: Devicetree Bindings area: Tests Issues related to a particular existing or missing test area: Boards/SoCs area: UART Universal Asynchronous Receiver-Transmitter area: Modem area: Devicetree area: Modem Drivers labels Oct 10, 2025
@SeppoTakalo SeppoTakalo force-pushed the cmux_power_control_upstream branch 3 times, most recently from f4f4666 to 1368e5f Compare October 14, 2025 11:19
Implement Modem Status Command(MSC) and a per DLC flow
control by using it.

Send flow control signals when our input buffer don't fit
full frame anymore.
Stop TX if we have received from controls on MSC.

Signed-off-by: Seppo Takalo <[email protected]>
Refactor internal event bits to use state enum values
and define set_state() and wait_state() so we don't need
two set of variables to maintain.

Signed-off-by: Seppo Takalo <[email protected]>
Instead of dropping all responses, handle the CLD.

Signed-off-by: Seppo Takalo <[email protected]>
If we immediately send disconnected event when CLD is received,
we might close the UART pipe before the response is actually send.

Also, shutdown_handler should not retry indefinitely.

Signed-off-by: Seppo Takalo <[email protected]>
Properly close down the CMUX channel before shutting down
the modem.

The CMUX Close-Down command should indicate the remote end
to clean up, even if we don't have shutdown script or power-key GPIO.

Signed-off-by: Seppo Takalo <[email protected]>
Signal powersaving mode for the remote end using PSC command.
Wakes up the remote end from powersaving mode by sending flag characters.

This method is defined in 3GPP TS 27.010.
Sections 5.4.6.3.2 Power Saving Control (PSC) and
5.4.7 Power Control and Wake-up Mechanisms.

Essentially it is one PSC command to indicate a sleep state, and
then repeated flag characters to wake up the remote end or indicate
that we are woken up.

Signed-off-by: Seppo Takalo <[email protected]>
CMUX driver can enable the support for idle-timer in
devicetree and can be requested to shut down the pipe
during sleep.

Then UART backend put the actual device into sleep when
pipe is closed.

Waking up is requested by sending data to DLC pipe
or by manually opening the uart_pipe.
Modem may request similar wake-up by a RING interrupt which
would open the same pipe.

When UART is powered and pipe is not closed, CMUX wake-up
procedure is automatic. Either end may initiate the wake-up.

Signed-off-by: Seppo Takalo <[email protected]>
Ringbuffer is not safe in ISR but k_pipe without waiting is.
So use pipe for events, so that possible GPIO callbacks from
ISR content can post events.

Signed-off-by: Seppo Takalo <[email protected]>
Use ring indicator to wake up the CMUX device
from sleep.
Only used for runtime power management, but same event
could be used for initiating idle -> connected as well.

Signed-off-by: Seppo Takalo <[email protected]>
This driver wraps an existing UART device and adds
DTR (Data Terminal Ready) GPIO output for a runtime
power management.
When the UART is powered off, DTR is deasserted.
When the UART is powered on, DTR is asserted.

This allows remote end to shut down the UART when DTR is deasserted.

Signed-off-by: Seppo Takalo <[email protected]>
Add documentation and state machine diagrams for CMUX power saving
feature and how to use it with Zephyr.

Signed-off-by: Seppo Takalo <[email protected]>
Instead of copying all fields from cmux_config into run-time
struct cmux, just have the configuration structure as a member.

Signed-off-by: Seppo Takalo <[email protected]>
When working on CMUX power saving, it is typical
that we end up closing the pipe before the last
RX_READY event is handled from workqueue, so we end up
receiving -EPERM which is not really a fatal error.

Pipes recover when they are re-opened. So drop this error
and return zero instead, like modem_pipe_open() and close() does.

Signed-off-by: Seppo Takalo <[email protected]>
@SeppoTakalo SeppoTakalo force-pushed the cmux_power_control_upstream branch from 1368e5f to de7938f Compare October 14, 2025 21:15
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: Boards/SoCs area: Devicetree Bindings area: Devicetree area: Modem Drivers area: Modem area: Tests Issues related to a particular existing or missing test area: UART Universal Asynchronous Receiver-Transmitter

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants