Skip to content

Commit cb07e99

Browse files
Martinhoff-makerkartben
authored andcommitted
drivers: serial: silabs: introduce pm for silabs eusart
Make use of pm_device_driver_init to perform driver initialization. Implement PM suspend and resume, which performs the following actions: * Enables/disables the USART * Gates the USART clock * Configures USART pins Also take PM locks to prevent deep sleep during TX and RX operations. Signed-off-by: Martin Hoff <martin.hoff@silabs.com>
1 parent cb9c27e commit cb07e99

File tree

2 files changed

+123
-34
lines changed

2 files changed

+123
-34
lines changed

drivers/serial/Kconfig.silabs_eusart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ config UART_SILABS_EUSART
1414
select DMA if UART_ASYNC_API
1515
select PINCTRL
1616
select CLOCK_CONTROL
17-
select PM_DEVICE if PM
1817
help
1918
Enable the eusart uart driver.
2019

drivers/serial/uart_silabs_eusart.c

Lines changed: 123 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
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

4955
struct 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+
66129
static 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

160228
static 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

194264
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
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)
687761
static 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

739818
static 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
9671038
static 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

9891083
static 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

Comments
 (0)