15
15
#include <zephyr/irq.h>
16
16
#include <zephyr/logging/log.h>
17
17
#include <zephyr/pm/device.h>
18
+ #include <zephyr/pm/policy.h>
18
19
#include <em_eusart.h>
19
20
#include <zephyr/drivers/dma.h>
20
21
#include <zephyr/drivers/dma/dma_silabs_ldma.h>
@@ -41,9 +42,14 @@ struct eusart_config {
41
42
const struct pinctrl_dev_config * pcfg ;
42
43
const struct device * clock_dev ;
43
44
const struct silabs_clock_control_cmu_config clock_cfg ;
44
- #if defined(CONFIG_UART_INTERRUPT_DRIVEN ) || defined(CONFIG_UART_SILABS_EUSART_ASYNC )
45
45
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 ,
47
53
};
48
54
49
55
struct eusart_data {
@@ -61,8 +67,65 @@ struct eusart_data {
61
67
uint8_t * rx_next_buffer ;
62
68
size_t rx_next_buffer_len ;
63
69
#endif
70
+ #ifdef CONFIG_PM
71
+ ATOMIC_DEFINE (pm_lock , EUSART_PM_LOCK_COUNT );
72
+ #endif
64
73
};
65
74
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
+
66
129
static int eusart_poll_in (const struct device * dev , unsigned char * c )
67
130
{
68
131
const struct eusart_config * config = dev -> config ;
@@ -79,6 +142,9 @@ static void eusart_poll_out(const struct device *dev, unsigned char c)
79
142
{
80
143
const struct eusart_config * config = dev -> config ;
81
144
145
+ if (eusart_pm_lock_get (dev , EUSART_PM_LOCK_TX_POLL )) {
146
+ EUSART_IntEnable (config -> eusart , EUSART_IF_TXC );
147
+ }
82
148
/* EUSART_Tx function already waits for the transmit buffer being empty
83
149
* and waits for the bus to be free to transmit.
84
150
*/
@@ -145,6 +211,7 @@ static void eusart_irq_tx_enable(const struct device *dev)
145
211
{
146
212
const struct eusart_config * config = dev -> config ;
147
213
214
+ eusart_pm_lock_get (dev , EUSART_PM_LOCK_TX );
148
215
EUSART_IntClear (config -> eusart , EUSART_IEN_TXFL | EUSART_IEN_TXC );
149
216
EUSART_IntEnable (config -> eusart , EUSART_IEN_TXFL | EUSART_IEN_TXC );
150
217
}
@@ -155,6 +222,7 @@ static void eusart_irq_tx_disable(const struct device *dev)
155
222
156
223
EUSART_IntDisable (config -> eusart , EUSART_IEN_TXFL | EUSART_IEN_TXC );
157
224
EUSART_IntClear (config -> eusart , EUSART_IEN_TXFL | EUSART_IEN_TXC );
225
+ eusart_pm_lock_put (dev , EUSART_PM_LOCK_TX );
158
226
}
159
227
160
228
static int eusart_irq_tx_complete (const struct device * dev )
@@ -179,6 +247,7 @@ static void eusart_irq_rx_enable(const struct device *dev)
179
247
{
180
248
const struct eusart_config * config = dev -> config ;
181
249
250
+ eusart_pm_lock_get (dev , EUSART_PM_LOCK_RX );
182
251
EUSART_IntClear (config -> eusart , EUSART_IEN_RXFL );
183
252
EUSART_IntEnable (config -> eusart , EUSART_IEN_RXFL );
184
253
}
@@ -189,6 +258,7 @@ static void eusart_irq_rx_disable(const struct device *dev)
189
258
190
259
EUSART_IntDisable (config -> eusart , EUSART_IEN_RXFL );
191
260
EUSART_IntClear (config -> eusart , EUSART_IEN_RXFL );
261
+ eusart_pm_lock_put (dev , EUSART_PM_LOCK_RX );
192
262
}
193
263
194
264
static 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
428
498
data -> dma_tx .blk_cfg .source_address = (uint32_t )data -> dma_tx .buffer ;
429
499
data -> dma_tx .blk_cfg .block_size = data -> dma_tx .buffer_length ;
430
500
501
+ eusart_pm_lock_get (dev , EUSART_PM_LOCK_TX );
502
+
431
503
EUSART_IntClear (config -> eusart , EUSART_IF_TXC );
432
504
EUSART_IntEnable (config -> eusart , EUSART_IF_TXC );
433
505
@@ -470,6 +542,7 @@ static int eusart_async_tx_abort(const struct device *dev)
470
542
471
543
EUSART_IntDisable (config -> eusart , EUSART_IF_TXC );
472
544
EUSART_IntClear (config -> eusart , EUSART_IF_TXC );
545
+ eusart_pm_lock_put (dev , EUSART_PM_LOCK_TX );
473
546
474
547
k_work_cancel_delayable (& data -> dma_tx .timeout_work );
475
548
@@ -520,6 +593,7 @@ static int eusart_async_rx_enable(const struct device *dev, uint8_t *rx_buf, siz
520
593
return - EFAULT ;
521
594
}
522
595
596
+ eusart_pm_lock_get (dev , EUSART_PM_LOCK_RX );
523
597
EUSART_IntClear (config -> eusart , EUSART_IF_RXOF | EUSART_IF_RXTO );
524
598
EUSART_IntEnable (config -> eusart , EUSART_IF_RXOF );
525
599
EUSART_IntEnable (config -> eusart , EUSART_IF_RXTO );
@@ -549,6 +623,7 @@ static int eusart_async_rx_disable(const struct device *dev)
549
623
EUSART_IntDisable (eusart , EUSART_IF_RXOF );
550
624
EUSART_IntDisable (eusart , EUSART_IF_RXTO );
551
625
EUSART_IntClear (eusart , EUSART_IF_RXOF | EUSART_IF_RXTO );
626
+ eusart_pm_lock_put (dev , EUSART_PM_LOCK_RX );
552
627
553
628
k_work_cancel_delayable (& data -> dma_rx .timeout_work );
554
629
@@ -683,16 +758,20 @@ static int eusart_async_init(const struct device *dev)
683
758
}
684
759
#endif /* CONFIG_UART_SILABS_EUSART_ASYNC */
685
760
686
- #if defined(CONFIG_UART_INTERRUPT_DRIVEN ) || defined(CONFIG_UART_SILABS_EUSART_ASYNC )
687
761
static void eusart_isr (const struct device * dev )
688
762
{
689
- struct eusart_data * data = dev -> data ;
690
- #ifdef CONFIG_UART_SILABS_EUSART_ASYNC
763
+ __maybe_unused struct eusart_data * data = dev -> data ;
691
764
const struct eusart_config * config = dev -> config ;
692
765
EUSART_TypeDef * eusart = config -> eusart ;
693
766
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
+ }
696
775
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
697
776
if (data -> callback ) {
698
777
data -> callback (dev , data -> cb_data );
@@ -728,13 +807,13 @@ static void eusart_isr(const struct device *dev)
728
807
if (data -> dma_tx .counter == data -> dma_tx .buffer_length ) {
729
808
EUSART_IntDisable (eusart , EUSART_IF_TXC );
730
809
EUSART_IntClear (eusart , EUSART_IF_TXC );
810
+ eusart_pm_lock_put (dev , EUSART_PM_LOCK_TX );
731
811
}
732
812
733
813
eusart_async_evt_tx_done (data );
734
814
}
735
815
#endif /* CONFIG_UART_SILABS_EUSART_ASYNC */
736
816
}
737
- #endif
738
817
739
818
static EUSART_Parity_TypeDef eusart_cfg2ll_parity (enum uart_config_parity parity )
740
819
{
@@ -942,16 +1021,9 @@ static int eusart_init(const struct device *dev)
942
1021
return err ;
943
1022
}
944
1023
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);
951
1025
952
- #if defined(CONFIG_UART_INTERRUPT_DRIVEN ) || defined(CONFIG_UART_SILABS_EUSART_ASYNC )
953
1026
config -> irq_config_func (dev );
954
- #endif
955
1027
956
1028
#ifdef CONFIG_UART_SILABS_EUSART_ASYNC
957
1029
err = eusart_async_init (dev );
@@ -960,31 +1032,53 @@ static int eusart_init(const struct device *dev)
960
1032
}
961
1033
#endif
962
1034
963
- return 0 ;
1035
+ return pm_device_driver_init ( dev , eusart_pm_action ) ;
964
1036
}
965
1037
966
- #ifdef CONFIG_PM_DEVICE
967
1038
static int eusart_pm_action (const struct device * dev , enum pm_device_action action )
968
1039
{
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 ;
970
1043
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 ;
975
1049
}
976
- break ;
977
1050
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
+ }
980
1055
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 {
982
1077
return - ENOTSUP ;
983
1078
}
984
1079
985
1080
return 0 ;
986
1081
}
987
- #endif
988
1082
989
1083
static DEVICE_API (uart , eusart_driver_api ) = {
990
1084
.poll_in = eusart_poll_in ,
@@ -1042,7 +1136,7 @@ static DEVICE_API(uart, eusart_driver_api) = {
1042
1136
#define EUSART_DMA_CHANNEL (index , dir )
1043
1137
#endif
1044
1138
1045
- #if defined( CONFIG_UART_INTERRUPT_DRIVEN ) || defined( CONFIG_UART_SILABS_EUSART_ASYNC )
1139
+
1046
1140
#define SILABS_EUSART_IRQ_HANDLER_FUNC (idx ) .irq_config_func = eusart_config_func_##idx,
1047
1141
#define SILABS_EUSART_IRQ_HANDLER (idx ) \
1048
1142
static void eusart_config_func_##idx(const struct device *dev) \
@@ -1057,10 +1151,6 @@ static DEVICE_API(uart, eusart_driver_api) = {
1057
1151
irq_enable(DT_INST_IRQ_BY_NAME(idx, rx, irq)); \
1058
1152
irq_enable(DT_INST_IRQ_BY_NAME(idx, tx, irq)); \
1059
1153
}
1060
- #else
1061
- #define SILABS_EUSART_IRQ_HANDLER_FUNC (idx )
1062
- #define SILABS_EUSART_IRQ_HANDLER (idx )
1063
- #endif
1064
1154
1065
1155
#define SILABS_EUSART_INIT (idx ) \
1066
1156
SILABS_EUSART_IRQ_HANDLER(idx); \
0 commit comments