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:
