This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit e238804119d9a1b7169ff84926bc8f25b55d0a17
Author: Bowen Wang <[email protected]>
AuthorDate: Wed Jan 21 23:33:35 2026 +0800

    rpmsg/rpmsg_virtio: add rpmsg virtio common pm support
    
    implement the pm feature in rpmsg virtio common part, now the rpmsg
    virtio can use in the low power case.
    
    Signed-off-by: Bowen Wang <[email protected]>
---
 drivers/rpmsg/Kconfig        |  28 +++++
 drivers/rpmsg/rpmsg_virtio.c | 236 ++++++++++++++++++++++++++++++++++++-------
 2 files changed, 229 insertions(+), 35 deletions(-)

diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig
index 77d5968ee2c..dc2657194c3 100644
--- a/drivers/rpmsg/Kconfig
+++ b/drivers/rpmsg/Kconfig
@@ -130,6 +130,34 @@ config RPMSG_VIRTIO_STACKSIZE
        int "rpmsg virtio stack size"
        default DEFAULT_TASK_STACKSIZE
 
+config RPMSG_VIRTIO_PM
+       bool "RPMsg VirtIO power management"
+       depends on PM
+       default n
+       ---help---
+               If TX/RX buffer is supplied and powered by each CPU.
+               And when one CPU in DEEP sleep, then it's buffer will
+               goto RAM-retention mode, can't access from another CPU.
+               So, we provide this method to resolve this.
+
+config RPMSG_VIRTIO_PM_AUTORELAX
+       bool "RPMsg VirtIO pm autorelax"
+       depends on RPMSG_VIRTIO_PM
+       default y
+       ---help---
+               Enable automatic power management wakelock relaxation using 
watchdog timer.
+
+               When enabled, this option uses a watchdog timer to periodically 
check if
+               there are any tx buffers is not returned by remote core. If the 
tx buffers
+               are returned by remote core, the power management wakelock is 
automatically
+               released, allowing the system to enter low-power states. If the 
tx buffers
+               are still not returned by remote core, the wakelock is 
maintained and the
+               timer is restarted.
+
+               Autorelax feature can improve power efficiency in multi-core 
systems because
+               it prevents unnecessary power consumption by reducing the times 
that system
+               enter and leave low-power states.
+
 endif # RPMSG_VIRTIO
 
 config RPMSG_VIRTIO_LITE
diff --git a/drivers/rpmsg/rpmsg_virtio.c b/drivers/rpmsg/rpmsg_virtio.c
index d76df821776..0a2fa559122 100644
--- a/drivers/rpmsg/rpmsg_virtio.c
+++ b/drivers/rpmsg/rpmsg_virtio.c
@@ -30,10 +30,13 @@
 
 #include <nuttx/kmalloc.h>
 #include <nuttx/kthread.h>
+#include <nuttx/power/pm.h>
 #include <nuttx/rpmsg/rpmsg.h>
 #include <nuttx/rpmsg/rpmsg_virtio.h>
 #include <nuttx/semaphore.h>
+#include <nuttx/spinlock.h>
 #include <nuttx/virtio/virtio-config.h>
+#include <nuttx/wdog.h>
 #include <metal/utilities.h>
 #include <openamp/rpmsg_virtio.h>
 
@@ -41,6 +44,7 @@
  * Pre-processor Definitions
  ****************************************************************************/
 
+#define RPMSG_VIRTIO_TIMEOUT_MS      20
 #define RPMSG_VIRTIO_FEATURES        (1 << VIRTIO_RPMSG_F_NS | \
                                       1 << VIRTIO_RPMSG_F_ACK | \
                                       1 << VIRTIO_RPMSG_F_BUFSZ | \
@@ -67,14 +71,24 @@ struct rpmsg_virtio_priv_s
   pid_t                        tid;
   vq_callback                  cbrx;
   vq_callback                  cbtx;
+  vq_notify                    notifytx;
   char                         local_cpuname[VIRTIO_RPMSG_CPUNAME_SIZE];
   char                         cpuname[VIRTIO_RPMSG_CPUNAME_SIZE];
+  uint16_t                     headrx;
+#ifdef CONFIG_RPMSG_VIRTIO_PM
+  spinlock_t                   lock;
+  struct pm_wakelock_s         wakelock;
+  struct wdog_s                wdog;
+#endif
 };
 
 /****************************************************************************
  * Private Function Prototypes
  ****************************************************************************/
 
+static void rpmsg_virtio_wakeup_rx(FAR struct rpmsg_virtio_priv_s *priv);
+static void rpmsg_virtio_wakeup_tx(FAR struct rpmsg_virtio_priv_s *priv);
+
 static int rpmsg_virtio_wait(FAR struct rpmsg_s *rpmsg, FAR sem_t *sem);
 static int rpmsg_virtio_post(FAR struct rpmsg_s *rpmsg, FAR sem_t *sem);
 static void rpmsg_virtio_dump(FAR struct rpmsg_s *rpmsg);
@@ -84,6 +98,7 @@ static FAR const char *rpmsg_virtio_get_cpuname(FAR struct 
rpmsg_s *rpmsg);
 
 static void rpmsg_virtio_rx_callback(FAR struct virtqueue *vq);
 static void rpmsg_virtio_tx_callback(FAR struct virtqueue *vq);
+static void rpmsg_virtio_tx_notify(FAR struct virtqueue *vq);
 
 /****************************************************************************
  * Private Data
@@ -104,6 +119,124 @@ static const struct rpmsg_ops_s g_rpmsg_virtio_ops =
  * Private Functions
  ****************************************************************************/
 
+/****************************************************************************
+ * Name: rpmsg_virtio_buffer_nused
+ ****************************************************************************/
+
+static int rpmsg_virtio_buffer_nused(FAR struct rpmsg_virtio_device *rvdev,
+                                     bool rx)
+{
+  FAR struct virtqueue *vq = rx ? rvdev->rvq : rvdev->svq;
+  bool is_host = rpmsg_virtio_get_role(rvdev) == RPMSG_HOST;
+  uint16_t nused;
+
+  if (is_host)
+    {
+      RPMSG_VIRTIO_INVALIDATE(vq->vq_ring.used->idx);
+    }
+  else
+    {
+      RPMSG_VIRTIO_INVALIDATE(vq->vq_ring.avail->idx);
+    }
+
+  nused = vq->vq_ring.avail->idx - vq->vq_ring.used->idx;
+  if (is_host ^ rx)
+    {
+      return nused;
+    }
+  else
+    {
+      return vq->vq_nentries - nused;
+    }
+}
+
+/****************************************************************************
+ * Name: rpmsg_virtio_pm_callback
+ ****************************************************************************/
+
+#ifdef CONFIG_RPMSG_VIRTIO_PM_AUTORELAX
+static void rpmsg_virtio_pm_callback(wdparm_t arg)
+{
+  FAR struct rpmsg_virtio_priv_s *priv =
+    (FAR struct rpmsg_virtio_priv_s *)arg;
+
+  if (rpmsg_virtio_buffer_nused(&priv->rvdev, false))
+    {
+      wd_start(&priv->wdog, MSEC2TICK(RPMSG_VIRTIO_TIMEOUT_MS),
+               rpmsg_virtio_pm_callback, (wdparm_t)priv);
+    }
+  else
+    {
+      pm_wakelock_relax(&priv->wakelock);
+    }
+}
+#endif
+
+#ifdef CONFIG_RPMSG_VIRTIO_PM
+
+/****************************************************************************
+ * Name: rpmsg_virtio_pm_action
+ ****************************************************************************/
+
+static inline void
+rpmsg_virtio_pm_action(FAR struct rpmsg_virtio_priv_s *priv, bool stay)
+{
+  irqstate_t flags;
+  int count;
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  count = pm_wakelock_staycount(&priv->wakelock);
+  if (stay && count == 0)
+    {
+      pm_wakelock_stay(&priv->wakelock);
+#ifdef CONFIG_RPMSG_VIRTIO_PM_AUTORELAX
+      wd_start(&priv->wdog, MSEC2TICK(RPMSG_VIRTIO_TIMEOUT_MS),
+               rpmsg_virtio_pm_callback, (wdparm_t)priv);
+#endif
+    }
+
+#ifndef CONFIG_RPMSG_VIRTIO_PM_AUTORELAX
+  /* When enabled the CONFIG_RPMSG_VIRTIO_PM_AUTORELAX, use a timer to check
+   * the buffers periodically and relax the pm wakelock and do not use this
+   * logic.
+   */
+
+  if (!stay && count > 0 &&
+      rpmsg_virtio_buffer_nused(&priv->rvdev, false) == 0)
+    {
+      pm_wakelock_relax(&priv->wakelock);
+    }
+#endif
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+/****************************************************************************
+ * Name: rpmsg_virtio_available_rx
+ ****************************************************************************/
+
+static inline bool
+rpmsg_virtio_available_rx(FAR struct rpmsg_virtio_priv_s *priv)
+{
+  FAR struct rpmsg_virtio_device *rvdev = &priv->rvdev;
+  FAR struct virtqueue *rvq = rvdev->rvq;
+
+  if (rpmsg_virtio_get_role(rvdev) == RPMSG_HOST)
+    {
+      return priv->headrx != rvq->vq_used_cons_idx;
+    }
+  else
+    {
+      return priv->headrx != rvq->vq_available_idx;
+    }
+}
+
+#else
+#  define rpmsg_virtio_pm_action(priv, stay)
+#  define rpmsg_virtio_available_rx(priv) true
+#endif
+
 /****************************************************************************
  * Name: rpmsg_virtio_is_recursive
  ****************************************************************************/
@@ -113,6 +246,18 @@ static bool rpmsg_virtio_is_recursive(FAR struct 
rpmsg_virtio_priv_s *priv)
   return nxsched_gettid() == priv->tid;
 }
 
+/****************************************************************************
+ * Name: rpmsg_virtio_rx_worker
+ ****************************************************************************/
+
+static void rpmsg_virtio_rx_worker(FAR struct rpmsg_virtio_priv_s *priv)
+{
+  if (rpmsg_virtio_available_rx(priv))
+    {
+      priv->cbrx(priv->rvdev.rvq);
+    }
+}
+
 /****************************************************************************
  * Name: rpmsg_virtio_wakeup_rx
  ****************************************************************************/
@@ -141,6 +286,13 @@ static void rpmsg_virtio_wakeup_tx(FAR struct 
rpmsg_virtio_priv_s *priv)
     {
       nxsem_post(&priv->semtx);
     }
+
+  /* rpmsg_virtio_wakeup_tx() called normally means the tx buffer has been
+   * returned by peer, so call rpmsg_virtio_pm_action(false) to enter
+   * lowe power mode when there is no pending tx buffer.
+   */
+
+  rpmsg_virtio_pm_action(priv, false);
 }
 
 /****************************************************************************
@@ -167,7 +319,7 @@ static int rpmsg_virtio_wait(FAR struct rpmsg_s *rpmsg, FAR 
sem_t *sem)
         }
 
       nxsem_wait(&priv->semtx);
-      priv->cbrx(priv->rvdev.rvq);
+      rpmsg_virtio_rx_worker(priv);
     }
 
   return ret;
@@ -195,37 +347,6 @@ static int rpmsg_virtio_post(FAR struct rpmsg_s *rpmsg, 
FAR sem_t *sem)
   return ret;
 }
 
-/****************************************************************************
- * Name: rpmsg_virtio_buffer_nused
- ****************************************************************************/
-
-static int rpmsg_virtio_buffer_nused(FAR struct rpmsg_virtio_device *rvdev,
-                                     bool rx)
-{
-  FAR struct virtqueue *vq = rx ? rvdev->rvq : rvdev->svq;
-  bool is_host = rpmsg_virtio_get_role(rvdev) == RPMSG_HOST;
-  uint16_t nused;
-
-  if (is_host)
-    {
-      RPMSG_VIRTIO_INVALIDATE(vq->vq_ring.used->idx);
-    }
-  else
-    {
-      RPMSG_VIRTIO_INVALIDATE(vq->vq_ring.avail->idx);
-    }
-
-  nused = vq->vq_ring.avail->idx - vq->vq_ring.used->idx;
-  if (is_host ^ rx)
-    {
-      return nused;
-    }
-  else
-    {
-      return vq->vq_nentries - nused;
-    }
-}
-
 /****************************************************************************
  * Name: rpmsg_virtio_dump_buffer
  ****************************************************************************/
@@ -294,6 +415,9 @@ static void rpmsg_virtio_dump(FAR struct rpmsg_s *rpmsg)
   FAR struct metal_list *node;
   bool needunlock = false;
 
+  metal_log(METAL_LOG_EMERGENCY, "Local: %s Remote: %s Headrx %u\n",
+            priv->local_cpuname, priv->cpuname, priv->headrx);
+
   if (!rvdev->vdev)
     {
       return;
@@ -368,6 +492,19 @@ static void rpmsg_virtio_rx_callback(FAR struct virtqueue 
*vq)
 {
   FAR struct rpmsg_virtio_priv_s *priv =
     metal_container_of(vq->vq_dev->priv, struct rpmsg_virtio_priv_s, rvdev);
+  FAR struct rpmsg_virtio_device *rvdev = &priv->rvdev;
+  FAR struct virtqueue *rvq = rvdev->rvq;
+
+  if (rpmsg_virtio_get_role(rvdev) == RPMSG_HOST)
+    {
+      RPMSG_VIRTIO_INVALIDATE(rvq->vq_ring.used->idx);
+      priv->headrx = rvq->vq_ring.used->idx;
+    }
+  else
+    {
+      RPMSG_VIRTIO_INVALIDATE(rvq->vq_ring.avail->idx);
+      priv->headrx = rvq->vq_ring.avail->idx;
+    }
 
   rpmsg_virtio_wakeup_rx(priv);
 }
@@ -382,6 +519,25 @@ static void rpmsg_virtio_tx_callback(FAR struct virtqueue 
*vq)
     metal_container_of(vq->vq_dev->priv, struct rpmsg_virtio_priv_s, rvdev);
 
   rpmsg_virtio_wakeup_tx(priv);
+  rpmsg_virtio_pm_action(priv, false);
+}
+
+/****************************************************************************
+ * Name: rpmsg_virtio_tx_notify
+ ****************************************************************************/
+
+static void rpmsg_virtio_tx_notify(FAR struct virtqueue *vq)
+{
+  FAR struct rpmsg_virtio_priv_s *priv =
+    metal_container_of(vq->vq_dev->priv, struct rpmsg_virtio_priv_s, rvdev);
+
+  /* rpmsg_virtio_tx_notify() called normally means send the buffer to peer,
+   * so call rpmsg_virtio_pm_action(true) to hold the pm wakelock to avoid to
+   * enter to low power mode until all the buffers are returned by peer.
+   */
+
+  rpmsg_virtio_pm_action(priv, true);
+  priv->notifytx(vq);
 }
 
 /****************************************************************************
@@ -402,9 +558,10 @@ static int rpmsg_virtio_notify_wait(FAR struct 
rpmsg_device *rdev,
   /* Wait to wakeup */
 
   virtqueue_enable_cb(priv->rvdev.svq);
-  nxsem_tickwait(&priv->semtx, MSEC2TICK(20));
+  nxsem_tickwait(&priv->semtx, MSEC2TICK(RPMSG_VIRTIO_TIMEOUT_MS));
   virtqueue_disable_cb(priv->rvdev.svq);
-  priv->cbrx(priv->rvdev.rvq);
+  rpmsg_virtio_rx_worker(priv);
+
   return 0;
 }
 
@@ -427,8 +584,10 @@ static int rpmsg_virtio_start(FAR struct 
rpmsg_virtio_priv_s *priv)
 
   priv->cbrx = priv->rvdev.rvq->callback;
   priv->cbtx = priv->rvdev.svq->callback;
+  priv->notifytx = priv->rvdev.svq->notify;
   priv->rvdev.rvq->callback = rpmsg_virtio_rx_callback;
   priv->rvdev.svq->callback = rpmsg_virtio_tx_callback;
+  priv->rvdev.svq->notify = rpmsg_virtio_tx_notify;
   priv->rvdev.notify_wait_cb = rpmsg_virtio_notify_wait;
   priv->rvdev.rdev.ns_unbind_cb = rpmsg_ns_unbind;
 
@@ -464,7 +623,7 @@ static int rpmsg_virtio_thread(int argc, FAR char *argv[])
   while (1)
     {
       nxsem_wait_uninterruptible(&priv->semrx);
-      priv->cbrx(priv->rvdev.rvq);
+      rpmsg_virtio_rx_worker(priv);
     }
 
   return 0;
@@ -553,6 +712,13 @@ int rpmsg_virtio_probe(FAR struct virtio_device *vdev)
     }
 
   priv->tid = ret;
+
+#ifdef CONFIG_RPMSG_VIRTIO_PM
+  spin_lock_init(&priv->lock);
+  snprintf(name, sizeof(name), "rpmsg-virtio-%s", priv->cpuname);
+  pm_wakelock_init(&priv->wakelock, name, PM_IDLE_DOMAIN, PM_IDLE);
+#endif
+
   return ret;
 
 err_kthread:

Reply via email to