1515#include <zephyr/irq.h>
1616#include <zephyr/logging/log.h>
1717#include <zephyr/pm/device.h>
18+ #include <zephyr/pm/policy.h>
1819#include <em_eusart.h>
1920#include <zephyr/drivers/dma.h>
2021#include <zephyr/drivers/dma/dma_silabs_ldma.h>
@@ -41,9 +42,14 @@ struct eusart_config {
4142 const struct pinctrl_dev_config * pcfg ;
4243 const struct device * clock_dev ;
4344 const struct silabs_clock_control_cmu_config clock_cfg ;
44- #if defined(CONFIG_UART_INTERRUPT_DRIVEN ) || defined(CONFIG_UART_SILABS_EUSART_ASYNC )
4545 void (* irq_config_func )(const struct device * dev );
46- #endif
46+ };
47+
48+ enum eusart_pm_lock {
49+ EUSART_PM_LOCK_TX ,
50+ EUSART_PM_LOCK_TX_POLL ,
51+ EUSART_PM_LOCK_RX ,
52+ EUSART_PM_LOCK_COUNT ,
4753};
4854
4955struct eusart_data {
@@ -61,8 +67,65 @@ struct eusart_data {
6167 uint8_t * rx_next_buffer ;
6268 size_t rx_next_buffer_len ;
6369#endif
70+ #ifdef CONFIG_PM
71+ ATOMIC_DEFINE (pm_lock , EUSART_PM_LOCK_COUNT );
72+ #endif
6473};
6574
75+ static int eusart_pm_action (const struct device * dev , enum pm_device_action action );
76+
77+ /**
78+ * @brief Get PM lock on low power states
79+ *
80+ * @param dev UART device struct
81+ * @param lock UART PM lock type
82+ *
83+ * @return true if lock was taken, false otherwise
84+ */
85+ static bool eusart_pm_lock_get (const struct device * dev , enum eusart_pm_lock lock )
86+ {
87+ #ifdef CONFIG_PM
88+ struct eusart_data * data = dev -> data ;
89+ bool was_locked = atomic_test_and_set_bit (data -> pm_lock , lock );
90+
91+ if (!was_locked ) {
92+ /* Lock out low-power states that would interfere with UART traffic */
93+ pm_policy_state_lock_get (PM_STATE_SUSPEND_TO_IDLE , PM_ALL_SUBSTATES );
94+ pm_policy_state_lock_get (PM_STATE_STANDBY , PM_ALL_SUBSTATES );
95+ }
96+
97+ return !was_locked ;
98+ #else
99+ return false;
100+ #endif
101+ }
102+
103+ /**
104+ * @brief Release PM lock on low power states
105+ *
106+ * @param dev UART device struct
107+ * @param lock UART PM lock type
108+ *
109+ * @return true if lock was released, false otherwise
110+ */
111+ static bool eusart_pm_lock_put (const struct device * dev , enum eusart_pm_lock lock )
112+ {
113+ #ifdef CONFIG_PM
114+ struct eusart_data * data = dev -> data ;
115+ bool was_locked = atomic_test_and_clear_bit (data -> pm_lock , lock );
116+
117+ if (was_locked ) {
118+ /* Unlock low-power states that would interfere with UART traffic */
119+ pm_policy_state_lock_put (PM_STATE_SUSPEND_TO_IDLE , PM_ALL_SUBSTATES );
120+ pm_policy_state_lock_put (PM_STATE_STANDBY , PM_ALL_SUBSTATES );
121+ }
122+
123+ return was_locked ;
124+ #else
125+ return false;
126+ #endif
127+ }
128+
66129static int eusart_poll_in (const struct device * dev , unsigned char * c )
67130{
68131 const struct eusart_config * config = dev -> config ;
@@ -79,6 +142,9 @@ static void eusart_poll_out(const struct device *dev, unsigned char c)
79142{
80143 const struct eusart_config * config = dev -> config ;
81144
145+ if (eusart_pm_lock_get (dev , EUSART_PM_LOCK_TX_POLL )) {
146+ EUSART_IntEnable (config -> eusart , EUSART_IF_TXC );
147+ }
82148 /* EUSART_Tx function already waits for the transmit buffer being empty
83149 * and waits for the bus to be free to transmit.
84150 */
@@ -145,6 +211,7 @@ static void eusart_irq_tx_enable(const struct device *dev)
145211{
146212 const struct eusart_config * config = dev -> config ;
147213
214+ eusart_pm_lock_get (dev , EUSART_PM_LOCK_TX );
148215 EUSART_IntClear (config -> eusart , EUSART_IEN_TXFL | EUSART_IEN_TXC );
149216 EUSART_IntEnable (config -> eusart , EUSART_IEN_TXFL | EUSART_IEN_TXC );
150217}
@@ -155,6 +222,7 @@ static void eusart_irq_tx_disable(const struct device *dev)
155222
156223 EUSART_IntDisable (config -> eusart , EUSART_IEN_TXFL | EUSART_IEN_TXC );
157224 EUSART_IntClear (config -> eusart , EUSART_IEN_TXFL | EUSART_IEN_TXC );
225+ eusart_pm_lock_put (dev , EUSART_PM_LOCK_TX );
158226}
159227
160228static int eusart_irq_tx_complete (const struct device * dev )
@@ -179,6 +247,7 @@ static void eusart_irq_rx_enable(const struct device *dev)
179247{
180248 const struct eusart_config * config = dev -> config ;
181249
250+ eusart_pm_lock_get (dev , EUSART_PM_LOCK_RX );
182251 EUSART_IntClear (config -> eusart , EUSART_IEN_RXFL );
183252 EUSART_IntEnable (config -> eusart , EUSART_IEN_RXFL );
184253}
@@ -189,6 +258,7 @@ static void eusart_irq_rx_disable(const struct device *dev)
189258
190259 EUSART_IntDisable (config -> eusart , EUSART_IEN_RXFL );
191260 EUSART_IntClear (config -> eusart , EUSART_IEN_RXFL );
261+ eusart_pm_lock_put (dev , EUSART_PM_LOCK_RX );
192262}
193263
194264static int eusart_irq_rx_ready (const struct device * dev )
@@ -428,6 +498,8 @@ static int eusart_async_tx(const struct device *dev, const uint8_t *tx_data, siz
428498 data -> dma_tx .blk_cfg .source_address = (uint32_t )data -> dma_tx .buffer ;
429499 data -> dma_tx .blk_cfg .block_size = data -> dma_tx .buffer_length ;
430500
501+ eusart_pm_lock_get (dev , EUSART_PM_LOCK_TX );
502+
431503 EUSART_IntClear (config -> eusart , EUSART_IF_TXC );
432504 EUSART_IntEnable (config -> eusart , EUSART_IF_TXC );
433505
@@ -470,6 +542,7 @@ static int eusart_async_tx_abort(const struct device *dev)
470542
471543 EUSART_IntDisable (config -> eusart , EUSART_IF_TXC );
472544 EUSART_IntClear (config -> eusart , EUSART_IF_TXC );
545+ eusart_pm_lock_put (dev , EUSART_PM_LOCK_TX );
473546
474547 k_work_cancel_delayable (& data -> dma_tx .timeout_work );
475548
@@ -520,6 +593,7 @@ static int eusart_async_rx_enable(const struct device *dev, uint8_t *rx_buf, siz
520593 return - EFAULT ;
521594 }
522595
596+ eusart_pm_lock_get (dev , EUSART_PM_LOCK_RX );
523597 EUSART_IntClear (config -> eusart , EUSART_IF_RXOF | EUSART_IF_RXTO );
524598 EUSART_IntEnable (config -> eusart , EUSART_IF_RXOF );
525599 EUSART_IntEnable (config -> eusart , EUSART_IF_RXTO );
@@ -549,6 +623,7 @@ static int eusart_async_rx_disable(const struct device *dev)
549623 EUSART_IntDisable (eusart , EUSART_IF_RXOF );
550624 EUSART_IntDisable (eusart , EUSART_IF_RXTO );
551625 EUSART_IntClear (eusart , EUSART_IF_RXOF | EUSART_IF_RXTO );
626+ eusart_pm_lock_put (dev , EUSART_PM_LOCK_RX );
552627
553628 k_work_cancel_delayable (& data -> dma_rx .timeout_work );
554629
@@ -683,16 +758,20 @@ static int eusart_async_init(const struct device *dev)
683758}
684759#endif /* CONFIG_UART_SILABS_EUSART_ASYNC */
685760
686- #if defined(CONFIG_UART_INTERRUPT_DRIVEN ) || defined(CONFIG_UART_SILABS_EUSART_ASYNC )
687761static void eusart_isr (const struct device * dev )
688762{
689- struct eusart_data * data = dev -> data ;
690- #ifdef CONFIG_UART_SILABS_EUSART_ASYNC
763+ __maybe_unused struct eusart_data * data = dev -> data ;
691764 const struct eusart_config * config = dev -> config ;
692765 EUSART_TypeDef * eusart = config -> eusart ;
693766 uint32_t flags = EUSART_IntGet (eusart );
694- struct dma_status stat ;
695- #endif
767+ __maybe_unused struct dma_status stat ;
768+
769+ if (flags & EUSART_IF_TXC ) {
770+ if (eusart_pm_lock_put (dev , EUSART_PM_LOCK_TX_POLL )) {
771+ EUSART_IntDisable (eusart , EUSART_IEN_TXC );
772+ EUSART_IntClear (eusart , EUSART_IF_TXC );
773+ }
774+ }
696775#ifdef CONFIG_UART_INTERRUPT_DRIVEN
697776 if (data -> callback ) {
698777 data -> callback (dev , data -> cb_data );
@@ -728,13 +807,13 @@ static void eusart_isr(const struct device *dev)
728807 if (data -> dma_tx .counter == data -> dma_tx .buffer_length ) {
729808 EUSART_IntDisable (eusart , EUSART_IF_TXC );
730809 EUSART_IntClear (eusart , EUSART_IF_TXC );
810+ eusart_pm_lock_put (dev , EUSART_PM_LOCK_TX );
731811 }
732812
733813 eusart_async_evt_tx_done (data );
734814 }
735815#endif /* CONFIG_UART_SILABS_EUSART_ASYNC */
736816}
737- #endif
738817
739818static EUSART_Parity_TypeDef eusart_cfg2ll_parity (enum uart_config_parity parity )
740819{
@@ -942,16 +1021,9 @@ static int eusart_init(const struct device *dev)
9421021 return err ;
9431022 }
9441023
945- err = pinctrl_apply_state (config -> pcfg , PINCTRL_STATE_DEFAULT );
946- if (err < 0 ) {
947- return err ;
948- }
949-
950- eusart_configure_peripheral (dev , true);
1024+ eusart_configure_peripheral (dev , false);
9511025
952- #if defined(CONFIG_UART_INTERRUPT_DRIVEN ) || defined(CONFIG_UART_SILABS_EUSART_ASYNC )
9531026 config -> irq_config_func (dev );
954- #endif
9551027
9561028#ifdef CONFIG_UART_SILABS_EUSART_ASYNC
9571029 err = eusart_async_init (dev );
@@ -960,31 +1032,53 @@ static int eusart_init(const struct device *dev)
9601032 }
9611033#endif
9621034
963- return 0 ;
1035+ return pm_device_driver_init ( dev , eusart_pm_action ) ;
9641036}
9651037
966- #ifdef CONFIG_PM_DEVICE
9671038static int eusart_pm_action (const struct device * dev , enum pm_device_action action )
9681039{
969- __maybe_unused const struct eusart_config * config = dev -> config ;
1040+ __maybe_unused struct eusart_data * data = dev -> data ;
1041+ const struct eusart_config * config = dev -> config ;
1042+ int err ;
9701043
971- switch (action ) {
972- case PM_DEVICE_ACTION_SUSPEND :
973- /* Wait for TX FIFO to flush before suspending */
974- while (!(EUSART_StatusGet (config -> eusart ) & EUSART_STATUS_TXIDLE )) {
1044+ if (action == PM_DEVICE_ACTION_RESUME ) {
1045+ err = clock_control_on (config -> clock_dev ,
1046+ (clock_control_subsys_t )& config -> clock_cfg );
1047+ if (err < 0 && err != - EALREADY ) {
1048+ return err ;
9751049 }
976- break ;
9771050
978- case PM_DEVICE_ACTION_RESUME :
979- break ;
1051+ err = pinctrl_apply_state (config -> pcfg , PINCTRL_STATE_DEFAULT );
1052+ if (err < 0 ) {
1053+ return err ;
1054+ }
9801055
981- default :
1056+ EUSART_Enable (config -> eusart , eusartEnable );
1057+ } else if (IS_ENABLED (CONFIG_PM_DEVICE ) && (action == PM_DEVICE_ACTION_SUSPEND )) {
1058+ #ifdef CONFIG_UART_SILABS_EUSART_ASYNC
1059+ /* Entering suspend requires there to be no active asynchronous calls. */
1060+ __ASSERT_NO_MSG (!data -> dma_rx .enabled );
1061+ __ASSERT_NO_MSG (!data -> dma_tx .enabled );
1062+ #endif
1063+ EUSART_Enable (config -> eusart , eusartDisable );
1064+
1065+ err = clock_control_off (config -> clock_dev ,
1066+ (clock_control_subsys_t )& config -> clock_cfg );
1067+ if (err < 0 ) {
1068+ return err ;
1069+ }
1070+
1071+ err = pinctrl_apply_state (config -> pcfg , PINCTRL_STATE_SLEEP );
1072+ if (err < 0 && err != - ENOENT ) {
1073+ return err ;
1074+ }
1075+
1076+ } else {
9821077 return - ENOTSUP ;
9831078 }
9841079
9851080 return 0 ;
9861081}
987- #endif
9881082
9891083static DEVICE_API (uart , eusart_driver_api ) = {
9901084 .poll_in = eusart_poll_in ,
@@ -1042,7 +1136,7 @@ static DEVICE_API(uart, eusart_driver_api) = {
10421136#define EUSART_DMA_CHANNEL (index , dir )
10431137#endif
10441138
1045- #if defined( CONFIG_UART_INTERRUPT_DRIVEN ) || defined( CONFIG_UART_SILABS_EUSART_ASYNC )
1139+
10461140#define SILABS_EUSART_IRQ_HANDLER_FUNC (idx ) .irq_config_func = eusart_config_func_##idx,
10471141#define SILABS_EUSART_IRQ_HANDLER (idx ) \
10481142 static void eusart_config_func_##idx(const struct device *dev) \
@@ -1057,10 +1151,6 @@ static DEVICE_API(uart, eusart_driver_api) = {
10571151 irq_enable(DT_INST_IRQ_BY_NAME(idx, rx, irq)); \
10581152 irq_enable(DT_INST_IRQ_BY_NAME(idx, tx, irq)); \
10591153 }
1060- #else
1061- #define SILABS_EUSART_IRQ_HANDLER_FUNC (idx )
1062- #define SILABS_EUSART_IRQ_HANDLER (idx )
1063- #endif
10641154
10651155#define SILABS_EUSART_INIT (idx ) \
10661156 SILABS_EUSART_IRQ_HANDLER(idx); \
0 commit comments