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:
