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 abe28e1bcf533867d38e12f9e75f232fec6a3082
Author: liaoao <[email protected]>
AuthorDate: Mon Jan 6 22:11:06 2025 +0800

    drivers/rpmsg_port_spi/slave: add pm ops support for rpmsg port spi
    
    Add low power support for the rpmsg port spi/slave transport, now
    the rpmsg port spi/slave transport will not allow enter into the
    low power state when there are still tx data to sent or rx data
    to processed.
    
    Signed-off-by: liaoao <[email protected]>
    Signed-off-by: Bowen Wang <[email protected]>
---
 drivers/rpmsg/Kconfig                |   5 ++
 drivers/rpmsg/rpmsg_port_spi.c       | 113 +++++++++++++++++++++++++++++++++++
 drivers/rpmsg/rpmsg_port_spi_slave.c | 113 +++++++++++++++++++++++++++++++++++
 3 files changed, 231 insertions(+)

diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig
index dc2657194c3..5d7e82f2d06 100644
--- a/drivers/rpmsg/Kconfig
+++ b/drivers/rpmsg/Kconfig
@@ -75,6 +75,11 @@ config RPMSG_PORT_SPI_RX_THRESHOLD
        default 50
        range 0 100
 
+config RPMSG_PORT_SPI_PM_TIMEOUT
+       int "Rpmsg SPI Port PM Stay Timeout in Milliseconds"
+       default 1
+       depends on PM
+
 endif # RPMSG_PORT_SPI
 
 config RPMSG_PORT_UART
diff --git a/drivers/rpmsg/rpmsg_port_spi.c b/drivers/rpmsg/rpmsg_port_spi.c
index 4ab47603596..8f355e86560 100644
--- a/drivers/rpmsg/rpmsg_port_spi.c
+++ b/drivers/rpmsg/rpmsg_port_spi.c
@@ -32,6 +32,7 @@
 #include <nuttx/crc16.h>
 #include <nuttx/kmalloc.h>
 #include <nuttx/kthread.h>
+#include <nuttx/power/pm.h>
 #include <nuttx/irq.h>
 #include <nuttx/mutex.h>
 #include <nuttx/spinlock.h>
@@ -103,6 +104,12 @@ struct rpmsg_port_spi_s
   rpmsg_port_rx_cb_t             rxcb;
   volatile uint8_t               state;
   spinlock_t                     lock;
+#ifdef CONFIG_PM
+  struct pm_callback_s           pmcb;
+  spinlock_t                     pmlock;
+  struct wdog_s                  wdog;
+  struct pm_wakelock_s           wakelock;
+#endif
 
   /* Used for flow control */
 
@@ -137,6 +144,102 @@ static const struct rpmsg_port_ops_s g_rpmsg_port_spi_ops 
=
  * Private Functions
  ****************************************************************************/
 
+#ifdef CONFIG_PM
+
+/****************************************************************************
+ * Name: rpmsg_port_spi_pm_callback
+ ****************************************************************************/
+
+static void rpmsg_port_spi_pm_callback(wdparm_t arg)
+{
+  FAR struct rpmsg_port_spi_s *rpspi = (FAR struct rpmsg_port_spi_s *)arg;
+  irqstate_t flags;
+  int count;
+
+  flags = spin_lock_irqsave(&rpspi->pmlock);
+  count = pm_wakelock_staycount(&rpspi->wakelock);
+  if (count > 0 && atomic_load(&rpspi->transferring) == 0 &&
+      rpmsg_port_queue_nused(&rpspi->port.txq) == 0 &&
+      rpmsg_port_queue_nused(&rpspi->port.rxq) == 0)
+    {
+      pm_wakelock_relax(&rpspi->wakelock);
+    }
+  else
+    {
+      wd_start(&rpspi->wdog, MSEC2TICK(CONFIG_RPMSG_PORT_SPI_PM_TIMEOUT),
+               rpmsg_port_spi_pm_callback, (wdparm_t)rpspi);
+    }
+
+  spin_unlock_irqrestore(&rpspi->pmlock, flags);
+}
+
+/****************************************************************************
+ * Name: rpmsg_port_spi_pm_action
+ ****************************************************************************/
+
+static inline void
+rpmsg_port_spi_pm_action(FAR struct rpmsg_port_spi_s *rpspi, bool stay)
+{
+  irqstate_t flags;
+  int count;
+
+  flags = spin_lock_irqsave(&rpspi->pmlock);
+  count = pm_wakelock_staycount(&rpspi->wakelock);
+  if (stay && count == 0)
+    {
+      pm_wakelock_stay(&rpspi->wakelock);
+    }
+  else if (!stay && count > 0 &&
+           rpmsg_port_queue_nused(&rpspi->port.txq) == 0)
+    {
+      wd_start(&rpspi->wdog, MSEC2TICK(CONFIG_RPMSG_PORT_SPI_PM_TIMEOUT),
+               rpmsg_port_spi_pm_callback, (wdparm_t)rpspi);
+    }
+
+  spin_unlock_irqrestore(&rpspi->pmlock, flags);
+}
+
+/****************************************************************************
+ * Name: rpmsg_port_spi_prepare
+ ****************************************************************************/
+
+static int rpmsg_port_spi_prepare(FAR struct pm_callback_s *cb, int domain,
+                                  enum pm_state_e pmstate)
+{
+  FAR struct rpmsg_port_spi_s *rpspi =
+    container_of(cb, struct rpmsg_port_spi_s, pmcb);
+  enum pm_state_e oldstate;
+
+  oldstate = pm_querystate(PM_IDLE_DOMAIN);
+  switch (oldstate)
+    {
+      case PM_NORMAL:
+      case PM_IDLE:
+      case PM_STANDBY:
+        if (pmstate == PM_SLEEP)
+          {
+            if (atomic_load(&rpspi->transferring) ||
+                rpmsg_port_queue_nused(&rpspi->port.rxq) > 0 ||
+                rpmsg_port_queue_nused(&rpspi->port.txq) > 0)
+              {
+                rpmsgerr("rpmsg port spi busy\n");
+                return -EBUSY;
+              }
+          }
+        break;
+
+      case PM_SLEEP:
+      default:
+        break;
+    }
+
+  return 0;
+}
+
+#else
+#  define rpmsg_port_spi_pm_action(rpspi, stay)
+#endif
+
 /****************************************************************************
  * Name: rpmsg_port_spi_notify_tx_ready
  ****************************************************************************/
@@ -237,6 +340,7 @@ static void rpmsg_port_spi_exchange(FAR struct 
rpmsg_port_spi_s *rpspi)
 
   rpmsginfo("irq send cmd:%u avail:%u\n", txhdr->cmd, txhdr->avail);
 
+  rpmsg_port_spi_pm_action(rpspi, true);
   SPI_SELECT(rpspi->spi, rpspi->devid, true);
   SPI_EXCHANGE(rpspi->spi, txhdr, rpspi->rxhdr,
                BYTES2WORDS(rpspi, rpspi->cmdhdr->len));
@@ -345,6 +449,7 @@ static void rpmsg_port_spi_complete_handler(FAR void *arg)
 unlock:
   spin_unlock_irqrestore(&rpspi->lock, flags);
 out:
+  rpmsg_port_spi_pm_action(rpspi, false);
   if (atomic_xchg(&rpspi->transferring, 0) > 1)
     {
       rpmsg_port_spi_exchange(rpspi);
@@ -679,6 +784,14 @@ rpmsg_port_spi_initialize(FAR const struct 
rpmsg_port_config_s *cfg,
       goto out;
     }
 
+#ifdef CONFIG_PM
+  rpspi->pmcb.prepare = rpmsg_port_spi_prepare;
+  pm_register(&rpspi->pmcb);
+  spin_lock_init(&rpspi->pmlock);
+  pm_wakelock_init(&rpspi->wakelock, cfg->remotecpu,
+                   PM_IDLE_DOMAIN, PM_NORMAL);
+#endif
+
   return 0;
 
 out:
diff --git a/drivers/rpmsg/rpmsg_port_spi_slave.c 
b/drivers/rpmsg/rpmsg_port_spi_slave.c
index aeb2a45143b..71495b3254b 100644
--- a/drivers/rpmsg/rpmsg_port_spi_slave.c
+++ b/drivers/rpmsg/rpmsg_port_spi_slave.c
@@ -32,6 +32,7 @@
 #include <nuttx/crc16.h>
 #include <nuttx/kmalloc.h>
 #include <nuttx/kthread.h>
+#include <nuttx/power/pm.h>
 #include <nuttx/irq.h>
 #include <nuttx/mutex.h>
 #include <nuttx/spinlock.h>
@@ -104,6 +105,12 @@ struct rpmsg_port_spi_s
   rpmsg_port_rx_cb_t             rxcb;
   volatile uint8_t               state;
   spinlock_t                     lock;
+#ifdef CONFIG_PM
+  struct pm_callback_s           pmcb;
+  spinlock_t                     pmlock;
+  struct wdog_s                  wdog;
+  struct pm_wakelock_s           wakelock;
+#endif
 
   /* Used for flow control */
 
@@ -158,6 +165,102 @@ static const struct spi_slave_devops_s 
g_rpmsg_port_spi_slave_ops =
  * Private Functions
  ****************************************************************************/
 
+#ifdef CONFIG_PM
+
+/****************************************************************************
+ * Name: rpmsg_port_spi_pm_callback
+ ****************************************************************************/
+
+static void rpmsg_port_spi_pm_callback(wdparm_t arg)
+{
+  FAR struct rpmsg_port_spi_s *rpspi = (FAR struct rpmsg_port_spi_s *)arg;
+  irqstate_t flags;
+  int count;
+
+  flags = spin_lock_irqsave(&rpspi->pmlock);
+  count = pm_wakelock_staycount(&rpspi->wakelock);
+  if (count > 0 && atomic_load(&rpspi->transferring) == 0 &&
+      rpmsg_port_queue_nused(&rpspi->port.txq) == 0 &&
+      rpmsg_port_queue_nused(&rpspi->port.rxq) == 0)
+    {
+      pm_wakelock_relax(&rpspi->wakelock);
+    }
+  else
+    {
+      wd_start(&rpspi->wdog, MSEC2TICK(CONFIG_RPMSG_PORT_SPI_PM_TIMEOUT),
+               rpmsg_port_spi_pm_callback, (wdparm_t)rpspi);
+    }
+
+  spin_unlock_irqrestore(&rpspi->pmlock, flags);
+}
+
+/****************************************************************************
+ * Name: rpmsg_port_spi_pm_action
+ ****************************************************************************/
+
+static inline void
+rpmsg_port_spi_pm_action(FAR struct rpmsg_port_spi_s *rpspi, bool stay)
+{
+  irqstate_t flags;
+  int count;
+
+  flags = spin_lock_irqsave(&rpspi->pmlock);
+  count = pm_wakelock_staycount(&rpspi->wakelock);
+  if (stay && count == 0)
+    {
+      pm_wakelock_stay(&rpspi->wakelock);
+    }
+  else if (!stay && count > 0 &&
+           rpmsg_port_queue_nused(&rpspi->port.txq) == 0)
+    {
+      wd_start(&rpspi->wdog, MSEC2TICK(CONFIG_RPMSG_PORT_SPI_PM_TIMEOUT),
+               rpmsg_port_spi_pm_callback, (wdparm_t)rpspi);
+    }
+
+  spin_unlock_irqrestore(&rpspi->pmlock, flags);
+}
+
+/****************************************************************************
+ * Name: rpmsg_port_spi_prepare
+ ****************************************************************************/
+
+static int rpmsg_port_spi_prepare(FAR struct pm_callback_s *cb, int domain,
+                                  enum pm_state_e pmstate)
+{
+  FAR struct rpmsg_port_spi_s *rpspi =
+    container_of(cb, struct rpmsg_port_spi_s, pmcb);
+  enum pm_state_e oldstate;
+
+  oldstate = pm_querystate(PM_IDLE_DOMAIN);
+  switch (oldstate)
+    {
+      case PM_NORMAL:
+      case PM_IDLE:
+      case PM_STANDBY:
+        if (pmstate == PM_SLEEP)
+          {
+            if (atomic_load(&rpspi->transferring) ||
+                rpmsg_port_queue_nused(&rpspi->port.rxq) > 0 ||
+                rpmsg_port_queue_nused(&rpspi->port.txq) > 0)
+              {
+                rpmsgerr("rpmsg port spi busy\n");
+                return -EBUSY;
+              }
+          }
+        break;
+
+      case PM_SLEEP:
+      default:
+        break;
+    }
+
+  return 0;
+}
+
+#else
+#  define rpmsg_port_spi_pm_action(rpspi, stay)
+#endif
+
 /****************************************************************************
  * Name: rpmsg_port_spi_exchange
  ****************************************************************************/
@@ -199,6 +302,7 @@ static void rpmsg_port_spi_exchange(FAR struct 
rpmsg_port_spi_s *rpspi)
 
   rpmsginfo("send cmd:%u avail:%u\n", txhdr->cmd, txhdr->avail);
 
+  rpmsg_port_spi_pm_action(rpspi, true);
   SPIS_CTRLR_ENQUEUE(rpspi->spictrlr, txhdr,
                      BYTES2WORDS(rpspi, rpspi->cmdhdr->len));
   IOEXP_WRITEPIN(rpspi->ioe, rpspi->sreq, 1);
@@ -406,6 +510,7 @@ static void rpmsg_port_spi_slave_notify(FAR struct 
spi_slave_dev_s *dev,
 unlock:
   spin_unlock_irqrestore(&rpspi->lock, flags);
 out:
+  rpmsg_port_spi_pm_action(rpspi, false);
   if (atomic_xchg(&rpspi->transferring, 0) > 1 ||
       (rpspi->txavail > 0 && rpmsg_port_queue_nused(&rpspi->port.txq) > 0))
     {
@@ -705,6 +810,14 @@ rpmsg_port_spi_slave_initialize(FAR const struct 
rpmsg_port_config_s *cfg,
       goto out;
     }
 
+#ifdef CONFIG_PM
+  rpspi->pmcb.prepare = rpmsg_port_spi_prepare;
+  pm_register(&rpspi->pmcb);
+  spin_lock_init(&rpspi->pmlock);
+  pm_wakelock_init(&rpspi->wakelock, cfg->remotecpu,
+                   PM_IDLE_DOMAIN, PM_NORMAL);
+#endif
+
   return 0;
 
 out:

Reply via email to