Skip to content

Commit 0b51583

Browse files
committed
drivers: serial: cc23x0: Add power management
Add PM support to cc23x0 UART module. Signed-off-by: Julien Panis <jpanis@baylibre.com>
1 parent 179045e commit 0b51583

File tree

1 file changed

+113
-13
lines changed

1 file changed

+113
-13
lines changed

drivers/serial/uart_cc23x0.c

Lines changed: 113 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
#include <zephyr/drivers/uart.h>
1313
#include <zephyr/drivers/pinctrl.h>
1414
#include <zephyr/irq.h>
15+
#include <zephyr/pm/device.h>
16+
#include <zephyr/pm/policy.h>
17+
#include <zephyr/sys/atomic.h>
1518

1619
#include <errno.h>
1720

@@ -52,6 +55,12 @@ struct uart_cc23x0_config {
5255
#endif
5356
};
5457

58+
enum uart_cc23x0_pm_locks {
59+
UART_CC23X0_PM_LOCK_TX,
60+
UART_CC23X0_PM_LOCK_RX,
61+
UART_CC23X0_PM_LOCK_COUNT,
62+
};
63+
5564
struct uart_cc23x0_data {
5665
struct uart_config uart_config;
5766
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
@@ -74,8 +83,33 @@ struct uart_cc23x0_data {
7483
uint8_t *rx_next_buf;
7584
size_t rx_next_len;
7685
#endif /* CONFIG_UART_CC23X0_DMA_DRIVEN */
86+
#ifdef CONFIG_PM
87+
ATOMIC_DEFINE(pm_lock, UART_CC23X0_PM_LOCK_COUNT);
88+
#endif
7789
};
7890

91+
static inline void uart_cc23x0_pm_policy_state_lock_get(struct uart_cc23x0_data *data,
92+
enum uart_cc23x0_pm_locks pm_lock_type)
93+
{
94+
#ifdef CONFIG_PM_DEVICE
95+
if (!atomic_test_and_set_bit(data->pm_lock, pm_lock_type)) {
96+
pm_policy_state_lock_get(PM_STATE_RUNTIME_IDLE, PM_ALL_SUBSTATES);
97+
pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
98+
}
99+
#endif
100+
}
101+
102+
static inline void uart_cc23x0_pm_policy_state_lock_put(struct uart_cc23x0_data *data,
103+
enum uart_cc23x0_pm_locks pm_lock_type)
104+
{
105+
#ifdef CONFIG_PM_DEVICE
106+
if (atomic_test_and_clear_bit(data->pm_lock, pm_lock_type)) {
107+
pm_policy_state_lock_put(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
108+
pm_policy_state_lock_put(PM_STATE_RUNTIME_IDLE, PM_ALL_SUBSTATES);
109+
}
110+
#endif
111+
}
112+
79113
static int uart_cc23x0_poll_in(const struct device *dev, unsigned char *c)
80114
{
81115
const struct uart_cc23x0_config *config = dev->config;
@@ -94,6 +128,14 @@ static void uart_cc23x0_poll_out(const struct device *dev, unsigned char c)
94128
const struct uart_cc23x0_config *config = dev->config;
95129

96130
UARTPutChar(config->reg, c);
131+
132+
#ifdef CONFIG_PM_DEVICE
133+
/* Wait for character to be transmitted to ensure CPU
134+
* does not enter standby when UART is busy
135+
*/
136+
while (UARTBusy(config->reg)) {
137+
}
138+
#endif
97139
}
98140

99141
static int uart_cc23x0_err_check(const struct device *dev)
@@ -254,6 +296,11 @@ static void uart_cc23x0_irq_tx_enable(const struct device *dev)
254296
{
255297
const struct uart_cc23x0_config *config = dev->config;
256298

299+
/* When TX IRQ is enabled, it is implicit that we are expecting to transmit
300+
* using the UART, hence we should no longer go into standby
301+
*/
302+
uart_cc23x0_pm_policy_state_lock_get(dev->data, UART_CC23X0_PM_LOCK_TX);
303+
257304
UARTEnableInt(config->reg, UART_INT_TX);
258305
}
259306

@@ -262,6 +309,8 @@ static void uart_cc23x0_irq_tx_disable(const struct device *dev)
262309
const struct uart_cc23x0_config *config = dev->config;
263310

264311
UARTDisableInt(config->reg, UART_INT_TX);
312+
313+
uart_cc23x0_pm_policy_state_lock_put(dev->data, UART_CC23X0_PM_LOCK_TX);
265314
}
266315

267316
static int uart_cc23x0_irq_tx_ready(const struct device *dev)
@@ -275,6 +324,11 @@ static void uart_cc23x0_irq_rx_enable(const struct device *dev)
275324
{
276325
const struct uart_cc23x0_config *config = dev->config;
277326

327+
/* When RX IRQ is enabled, it is implicit that we are expecting to receive
328+
* from the UART, hence we can no longer go into standby
329+
*/
330+
uart_cc23x0_pm_policy_state_lock_get(dev->data, UART_CC23X0_PM_LOCK_RX);
331+
278332
/* Trigger the ISR on both RX and Receive Timeout. This is to allow
279333
* the use of the hardware FIFOs for more efficient operation
280334
*/
@@ -286,6 +340,8 @@ static void uart_cc23x0_irq_rx_disable(const struct device *dev)
286340
const struct uart_cc23x0_config *config = dev->config;
287341

288342
UARTDisableInt(config->reg, UART_INT_RX | UART_INT_RT);
343+
344+
uart_cc23x0_pm_policy_state_lock_put(dev->data, UART_CC23X0_PM_LOCK_RX);
289345
}
290346

291347
static int uart_cc23x0_irq_tx_complete(const struct device *dev)
@@ -420,6 +476,9 @@ static int uart_cc23x0_async_tx(const struct device *dev, const uint8_t *buf, si
420476
return ret;
421477
}
422478

479+
/* Lock PM */
480+
uart_cc23x0_pm_policy_state_lock_get(data, UART_CC23X0_PM_LOCK_TX);
481+
423482
/* Enable DMA trigger to start the transfer */
424483
UARTEnableDMA(config->reg, UART_DMA_TX);
425484

@@ -457,6 +516,9 @@ static int uart_cc23x0_tx_halt(struct uart_cc23x0_data *data)
457516
if (data->async_callback) {
458517
data->async_callback(data->dev, &evt, data->async_user_data);
459518
}
519+
520+
/* Unlock PM */
521+
uart_cc23x0_pm_policy_state_lock_put(data, UART_CC23X0_PM_LOCK_TX);
460522
} else {
461523
return -EINVAL;
462524
}
@@ -536,6 +598,9 @@ static int uart_cc23x0_async_rx_enable(const struct device *dev, uint8_t *buf, s
536598
goto unlock;
537599
}
538600

601+
/* Lock PM */
602+
uart_cc23x0_pm_policy_state_lock_get(data, UART_CC23X0_PM_LOCK_RX);
603+
539604
/* Enable DMA trigger to start the transfer */
540605
UARTEnableDMA(config->reg, UART_DMA_RX);
541606

@@ -621,6 +686,9 @@ static int uart_cc23x0_async_rx_disable(const struct device *dev)
621686

622687
dma_stop(config->dma_dev, config->dma_channel_rx);
623688

689+
/* Unlock PM */
690+
uart_cc23x0_pm_policy_state_lock_put(data, UART_CC23X0_PM_LOCK_RX);
691+
624692
if (dma_get_status(config->dma_dev, config->dma_channel_rx, &status) == 0 &&
625693
status.pending_length) {
626694
rx_processed = data->rx_len - status.pending_length;
@@ -704,6 +772,9 @@ static void uart_cc23x0_isr(const struct device *dev)
704772
data->tx_buf = NULL;
705773
data->tx_len = 0;
706774

775+
/* Unlock PM */
776+
uart_cc23x0_pm_policy_state_lock_put(data, UART_CC23X0_PM_LOCK_TX);
777+
707778
irq_unlock(key);
708779

709780
UARTClearInt(config->reg, UART_INT_TXDMADONE);
@@ -731,6 +802,9 @@ static void uart_cc23x0_isr(const struct device *dev)
731802

732803
data->async_callback(dev, &evt, data->async_user_data);
733804
}
805+
806+
/* Unlock PM */
807+
uart_cc23x0_pm_policy_state_lock_put(data, UART_CC23X0_PM_LOCK_RX);
734808
} else {
735809
/* Otherwise, load next buffer and start the transfer */
736810
data->rx_buf = data->rx_next_buf;
@@ -798,8 +872,6 @@ static DEVICE_API(uart, uart_cc23x0_driver_api) = {
798872

799873
#if CONFIG_UART_INTERRUPT_DRIVEN || CONFIG_UART_CC23X0_DMA_DRIVEN
800874
#define UART_CC23X0_IRQ_CFG(n) \
801-
const struct uart_cc23x0_config *config = dev->config; \
802-
\
803875
do { \
804876
UARTClearInt(config->reg, UART_INT_RX); \
805877
UARTClearInt(config->reg, UART_INT_RT); \
@@ -832,17 +904,13 @@ static DEVICE_API(uart, uart_cc23x0_driver_api) = {
832904

833905
static int uart_cc23x0_init_common(const struct device *dev)
834906
{
907+
#ifdef CONFIG_UART_CC23X0_DMA_DRIVEN
835908
const struct uart_cc23x0_config *config = dev->config;
909+
#endif
836910
struct uart_cc23x0_data *data = dev->data;
837-
int ret;
838911

839912
CLKCTLEnable(CLKCTL_BASE, CLKCTL_UART0);
840913

841-
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
842-
if (ret < 0) {
843-
return ret;
844-
}
845-
846914
#ifdef CONFIG_UART_CC23X0_DMA_DRIVEN
847915
if (!device_is_ready(config->dma_dev)) {
848916
return -ENODEV;
@@ -855,34 +923,66 @@ static int uart_cc23x0_init_common(const struct device *dev)
855923
data->dev = dev;
856924
#endif
857925

926+
#ifdef CONFIG_PM_DEVICE
927+
atomic_clear_bit(data->pm_lock, UART_CC23X0_PM_LOCK_RX);
928+
atomic_clear_bit(data->pm_lock, UART_CC23X0_PM_LOCK_TX);
929+
#endif
930+
858931
/* Configure and enable UART */
859932
return uart_cc23x0_configure(dev, &data->uart_config);
860933
}
861934

935+
#ifdef CONFIG_PM_DEVICE
936+
937+
static int uart_cc23x0_pm_action(const struct device *dev, enum pm_device_action action)
938+
{
939+
const struct uart_cc23x0_config *config = dev->config;
940+
941+
switch (action) {
942+
case PM_DEVICE_ACTION_SUSPEND:
943+
UARTDisable(config->reg);
944+
CLKCTLDisable(CLKCTL_BASE, CLKCTL_UART0);
945+
return 0;
946+
case PM_DEVICE_ACTION_RESUME:
947+
return uart_cc23x0_init_common(dev);
948+
default:
949+
return -ENOTSUP;
950+
}
951+
}
952+
953+
#endif /* CONFIG_PM_DEVICE */
954+
862955
#define UART_CC23X0_DEVICE_DEFINE(n) \
863956
\
864-
DEVICE_DT_INST_DEFINE(n, uart_cc23x0_init_##n, NULL, &uart_cc23x0_data_##n, \
865-
&uart_cc23x0_config_##n, PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, \
866-
&uart_cc23x0_driver_api)
957+
DEVICE_DT_INST_DEFINE(n, uart_cc23x0_init_##n, PM_DEVICE_DT_INST_GET(n), \
958+
&uart_cc23x0_data_##n, &uart_cc23x0_config_##n, \
959+
PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, &uart_cc23x0_driver_api)
867960

868961
#define UART_CC23X0_INIT_FUNC(n) \
869962
static int uart_cc23x0_init_##n(const struct device *dev) \
870963
{ \
964+
const struct uart_cc23x0_config *config = dev->config; \
871965
int ret; \
872966
\
967+
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); \
968+
if (ret) { \
969+
return ret; \
970+
} \
971+
\
873972
ret = uart_cc23x0_init_common(dev); \
874-
if (ret < 0) { \
973+
if (ret) { \
875974
return ret; \
876975
} \
877976
\
878977
/* Enable interrupts */ \
879978
UART_CC23X0_IRQ_CFG(n); \
880979
\
881-
return ret; \
980+
return 0; \
882981
}
883982

884983
#define UART_CC23X0_INIT(n) \
885984
PINCTRL_DT_INST_DEFINE(n); \
985+
PM_DEVICE_DT_INST_DEFINE(n, uart_cc23x0_pm_action); \
886986
UART_CC23X0_INIT_FUNC(n); \
887987
\
888988
static struct uart_cc23x0_config uart_cc23x0_config_##n = { \

0 commit comments

Comments
 (0)