Skip to content

Commit 5cb535a

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 5cb535a

File tree

1 file changed

+151
-13
lines changed

1 file changed

+151
-13
lines changed

drivers/serial/uart_cc23x0.c

Lines changed: 151 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
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/device_runtime.h>
17+
#include <zephyr/pm/policy.h>
18+
#include <zephyr/sys/atomic.h>
1519

1620
#include <errno.h>
1721

@@ -52,6 +56,12 @@ struct uart_cc23x0_config {
5256
#endif
5357
};
5458

59+
enum uart_cc23x0_pm_locks {
60+
UART_CC23X0_PM_LOCK_TX,
61+
UART_CC23X0_PM_LOCK_RX,
62+
UART_CC23X0_PM_LOCK_COUNT,
63+
};
64+
5565
struct uart_cc23x0_data {
5666
struct uart_config uart_config;
5767
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
@@ -74,8 +84,33 @@ struct uart_cc23x0_data {
7484
uint8_t *rx_next_buf;
7585
size_t rx_next_len;
7686
#endif /* CONFIG_UART_CC23X0_DMA_DRIVEN */
87+
#ifdef CONFIG_PM
88+
ATOMIC_DEFINE(pm_lock, UART_CC23X0_PM_LOCK_COUNT);
89+
#endif
7790
};
7891

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

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

99142
static int uart_cc23x0_err_check(const struct device *dev)
@@ -254,6 +297,11 @@ static void uart_cc23x0_irq_tx_enable(const struct device *dev)
254297
{
255298
const struct uart_cc23x0_config *config = dev->config;
256299

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

@@ -262,6 +310,8 @@ static void uart_cc23x0_irq_tx_disable(const struct device *dev)
262310
const struct uart_cc23x0_config *config = dev->config;
263311

264312
UARTDisableInt(config->reg, UART_INT_TX);
313+
314+
uart_cc23x0_pm_policy_state_lock_put(dev->data, UART_CC23X0_PM_LOCK_TX);
265315
}
266316

267317
static int uart_cc23x0_irq_tx_ready(const struct device *dev)
@@ -275,6 +325,11 @@ static void uart_cc23x0_irq_rx_enable(const struct device *dev)
275325
{
276326
const struct uart_cc23x0_config *config = dev->config;
277327

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

288343
UARTDisableInt(config->reg, UART_INT_RX | UART_INT_RT);
344+
345+
uart_cc23x0_pm_policy_state_lock_put(dev->data, UART_CC23X0_PM_LOCK_RX);
289346
}
290347

291348
static int uart_cc23x0_irq_tx_complete(const struct device *dev)
@@ -401,6 +458,12 @@ static int uart_cc23x0_async_tx(const struct device *dev, const uint8_t *buf, si
401458

402459
irq_unlock(key);
403460

461+
/* Resume DMA (TX) */
462+
ret = pm_device_runtime_get(config->dma_dev);
463+
if (ret) {
464+
return ret;
465+
}
466+
404467
ret = dma_config(config->dma_dev, config->dma_channel_tx, &dma_cfg_tx);
405468
if (ret) {
406469
return ret;
@@ -420,6 +483,9 @@ static int uart_cc23x0_async_tx(const struct device *dev, const uint8_t *buf, si
420483
return ret;
421484
}
422485

486+
/* Lock PM */
487+
uart_cc23x0_pm_policy_state_lock_get(data, UART_CC23X0_PM_LOCK_TX);
488+
423489
/* Enable DMA trigger to start the transfer */
424490
UARTEnableDMA(config->reg, UART_DMA_TX);
425491

@@ -433,6 +499,7 @@ static int uart_cc23x0_tx_halt(struct uart_cc23x0_data *data)
433499
struct uart_event evt;
434500
size_t total_len;
435501
unsigned int key;
502+
int ret;
436503

437504
key = irq_lock();
438505

@@ -457,6 +524,15 @@ static int uart_cc23x0_tx_halt(struct uart_cc23x0_data *data)
457524
if (data->async_callback) {
458525
data->async_callback(data->dev, &evt, data->async_user_data);
459526
}
527+
528+
/* Unlock PM */
529+
uart_cc23x0_pm_policy_state_lock_put(data, UART_CC23X0_PM_LOCK_TX);
530+
531+
/* Suspend DMA (TX) */
532+
ret = pm_device_runtime_put(config->dma_dev);
533+
if (ret) {
534+
return ret;
535+
}
460536
} else {
461537
return -EINVAL;
462538
}
@@ -522,6 +598,12 @@ static int uart_cc23x0_async_rx_enable(const struct device *dev, uint8_t *buf, s
522598
goto unlock;
523599
}
524600

601+
/* Resume DMA (RX) */
602+
ret = pm_device_runtime_get(config->dma_dev);
603+
if (ret) {
604+
goto unlock;
605+
}
606+
525607
ret = dma_config(config->dma_dev, config->dma_channel_rx, &dma_cfg_rx);
526608
if (ret) {
527609
goto unlock;
@@ -536,6 +618,9 @@ static int uart_cc23x0_async_rx_enable(const struct device *dev, uint8_t *buf, s
536618
goto unlock;
537619
}
538620

621+
/* Lock PM */
622+
uart_cc23x0_pm_policy_state_lock_get(data, UART_CC23X0_PM_LOCK_RX);
623+
539624
/* Enable DMA trigger to start the transfer */
540625
UARTEnableDMA(config->reg, UART_DMA_RX);
541626

@@ -621,13 +706,22 @@ static int uart_cc23x0_async_rx_disable(const struct device *dev)
621706

622707
dma_stop(config->dma_dev, config->dma_channel_rx);
623708

709+
/* Unlock PM */
710+
uart_cc23x0_pm_policy_state_lock_put(data, UART_CC23X0_PM_LOCK_RX);
711+
624712
if (dma_get_status(config->dma_dev, config->dma_channel_rx, &status) == 0 &&
625713
status.pending_length) {
626714
rx_processed = data->rx_len - status.pending_length;
627715

628716
uart_cc23x0_notify_rx_processed(data, rx_processed);
629717
}
630718

719+
/* Suspend DMA (RX) */
720+
ret = pm_device_runtime_put(config->dma_dev);
721+
if (ret) {
722+
goto unlock;
723+
}
724+
631725
if (data->async_callback) {
632726
evt.type = UART_RX_BUF_RELEASED;
633727
evt.data.rx_buf.buf = data->rx_buf;
@@ -704,6 +798,12 @@ static void uart_cc23x0_isr(const struct device *dev)
704798
data->tx_buf = NULL;
705799
data->tx_len = 0;
706800

801+
/* Unlock PM */
802+
uart_cc23x0_pm_policy_state_lock_put(data, UART_CC23X0_PM_LOCK_TX);
803+
804+
/* Suspend DMA (TX) */
805+
pm_device_runtime_put(config->dma_dev);
806+
707807
irq_unlock(key);
708808

709809
UARTClearInt(config->reg, UART_INT_TXDMADONE);
@@ -731,6 +831,12 @@ static void uart_cc23x0_isr(const struct device *dev)
731831

732832
data->async_callback(dev, &evt, data->async_user_data);
733833
}
834+
835+
/* Unlock PM */
836+
uart_cc23x0_pm_policy_state_lock_put(data, UART_CC23X0_PM_LOCK_RX);
837+
838+
/* Suspend DMA (RX) */
839+
pm_device_runtime_put(config->dma_dev);
734840
} else {
735841
/* Otherwise, load next buffer and start the transfer */
736842
data->rx_buf = data->rx_next_buf;
@@ -798,8 +904,6 @@ static DEVICE_API(uart, uart_cc23x0_driver_api) = {
798904

799905
#if CONFIG_UART_INTERRUPT_DRIVEN || CONFIG_UART_CC23X0_DMA_DRIVEN
800906
#define UART_CC23X0_IRQ_CFG(n) \
801-
const struct uart_cc23x0_config *config = dev->config; \
802-
\
803907
do { \
804908
UARTClearInt(config->reg, UART_INT_RX); \
805909
UARTClearInt(config->reg, UART_INT_RT); \
@@ -832,57 +936,91 @@ static DEVICE_API(uart, uart_cc23x0_driver_api) = {
832936

833937
static int uart_cc23x0_init_common(const struct device *dev)
834938
{
939+
#ifdef CONFIG_UART_CC23X0_DMA_DRIVEN
835940
const struct uart_cc23x0_config *config = dev->config;
836-
struct uart_cc23x0_data *data = dev->data;
837941
int ret;
942+
#endif
943+
struct uart_cc23x0_data *data = dev->data;
838944

839945
CLKCTLEnable(CLKCTL_BASE, CLKCTL_UART0);
840946

841-
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
842-
if (ret < 0) {
843-
return ret;
844-
}
845-
846947
#ifdef CONFIG_UART_CC23X0_DMA_DRIVEN
847948
if (!device_is_ready(config->dma_dev)) {
848949
return -ENODEV;
849950
}
850951

952+
ret = pm_device_runtime_enable(config->dma_dev);
953+
if (ret) {
954+
return ret;
955+
}
956+
851957
UARTEnableInt(config->reg, UART_INT_TXDMADONE | UART_INT_RXDMADONE);
852958

853959
k_work_init_delayable(&data->tx_timeout_work, uart_cc23x0_async_tx_timeout);
854960

855961
data->dev = dev;
856962
#endif
857963

964+
#ifdef CONFIG_PM_DEVICE
965+
atomic_clear_bit(data->pm_lock, UART_CC23X0_PM_LOCK_RX);
966+
atomic_clear_bit(data->pm_lock, UART_CC23X0_PM_LOCK_TX);
967+
#endif
968+
858969
/* Configure and enable UART */
859970
return uart_cc23x0_configure(dev, &data->uart_config);
860971
}
861972

973+
#ifdef CONFIG_PM_DEVICE
974+
975+
static int uart_cc23x0_pm_action(const struct device *dev, enum pm_device_action action)
976+
{
977+
const struct uart_cc23x0_config *config = dev->config;
978+
979+
switch (action) {
980+
case PM_DEVICE_ACTION_SUSPEND:
981+
UARTDisable(config->reg);
982+
CLKCTLDisable(CLKCTL_BASE, CLKCTL_UART0);
983+
return 0;
984+
case PM_DEVICE_ACTION_RESUME:
985+
return uart_cc23x0_init_common(dev);
986+
default:
987+
return -ENOTSUP;
988+
}
989+
}
990+
991+
#endif /* CONFIG_PM_DEVICE */
992+
862993
#define UART_CC23X0_DEVICE_DEFINE(n) \
863994
\
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)
995+
DEVICE_DT_INST_DEFINE(n, uart_cc23x0_init_##n, PM_DEVICE_DT_INST_GET(n), \
996+
&uart_cc23x0_data_##n, &uart_cc23x0_config_##n, \
997+
PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, &uart_cc23x0_driver_api)
867998

868999
#define UART_CC23X0_INIT_FUNC(n) \
8691000
static int uart_cc23x0_init_##n(const struct device *dev) \
8701001
{ \
1002+
const struct uart_cc23x0_config *config = dev->config; \
8711003
int ret; \
8721004
\
1005+
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); \
1006+
if (ret) { \
1007+
return ret; \
1008+
} \
1009+
\
8731010
ret = uart_cc23x0_init_common(dev); \
874-
if (ret < 0) { \
1011+
if (ret) { \
8751012
return ret; \
8761013
} \
8771014
\
8781015
/* Enable interrupts */ \
8791016
UART_CC23X0_IRQ_CFG(n); \
8801017
\
881-
return ret; \
1018+
return 0; \
8821019
}
8831020

8841021
#define UART_CC23X0_INIT(n) \
8851022
PINCTRL_DT_INST_DEFINE(n); \
1023+
PM_DEVICE_DT_INST_DEFINE(n, uart_cc23x0_pm_action); \
8861024
UART_CC23X0_INIT_FUNC(n); \
8871025
\
8881026
static struct uart_cc23x0_config uart_cc23x0_config_##n = { \

0 commit comments

Comments
 (0)