Skip to content

Commit 9f0a31f

Browse files
committed
drivers: dma: cc23x0: Add power management
Add PM support to cc23x0 DMA module. We assume that DMA clients (peripheral drivers or applications) should take care of PM lock/unlock (pm_policy_state_lock_get/put). This assumption is made for that SoC because: - If a peripheral channel is used, then the transfer completion is signaled on the peripheral's interrupt (handled in the DMA client driver). This operating mode is specific to this SoC. - If a software channel is used (memory-to-memory transfer), then the transfer completion can be signaled to the application through a callback. Thus, in both cases, the PM can be unlocked at the right time by the DMA client. Signed-off-by: Julien Panis <jpanis@baylibre.com>
1 parent 179045e commit 9f0a31f

File tree

1 file changed

+121
-10
lines changed

1 file changed

+121
-10
lines changed

drivers/dma/dma_ti_cc23x0.c

Lines changed: 121 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ LOG_MODULE_REGISTER(dma_cc23x0, CONFIG_DMA_LOG_LEVEL);
1212
#include <zephyr/device.h>
1313
#include <zephyr/drivers/dma.h>
1414
#include <zephyr/irq.h>
15+
#include <zephyr/pm/device.h>
1516
#include <zephyr/sys/util.h>
1617

1718
#include <driverlib/clkctl.h>
@@ -45,10 +46,19 @@ LOG_MODULE_REGISTER(dma_cc23x0, CONFIG_DMA_LOG_LEVEL);
4546
#define DMA_CC23_IPID_MASK GENMASK(2, 0)
4647
#define DMA_CC23_CHXSEL_REG(ch) HWREG(EVTSVT_BASE + EVTSVT_O_DMACH0SEL + sizeof(uint32_t) * (ch))
4748

49+
#ifdef CONFIG_PM_DEVICE
50+
#define DMA_CC23_ALL_CH_MASK GENMASK(DMA_CC23_SW_CH_MAX, 0)
51+
#endif
52+
4853
struct dma_cc23x0_channel {
4954
uint8_t data_size;
5055
dma_callback_t cb;
5156
void *user_data;
57+
#ifdef CONFIG_PM_DEVICE
58+
bool configured;
59+
struct dma_block_config dma_blk_cfg;
60+
struct dma_config dma_cfg;
61+
#endif
5262
};
5363

5464
struct dma_cc23x0_data {
@@ -117,6 +127,9 @@ static int dma_cc23x0_config(const struct device *dev, uint32_t channel,
117127
uint32_t xfer_size;
118128
uint32_t burst_len;
119129
int ret;
130+
#ifdef CONFIG_PM_DEVICE
131+
enum pm_device_state pm_state;
132+
#endif
120133

121134
if (channel >= UDMA_NUM_CHANNELS) {
122135
LOG_ERR("Invalid channel (%u)", channel);
@@ -233,11 +246,45 @@ static int dma_cc23x0_config(const struct device *dev, uint32_t channel,
233246
(void *)block->dest_address,
234247
xfer_size);
235248

249+
#ifdef CONFIG_PM_DEVICE
250+
pm_device_state_get(dev, &pm_state);
251+
252+
/*
253+
* Save context only if current function is not being called
254+
* from resume operation for restoring channel configuration
255+
*/
256+
if (pm_state == PM_DEVICE_STATE_ACTIVE) {
257+
ch_data->configured = true;
258+
259+
ch_data->dma_blk_cfg.source_address = block->source_address;
260+
ch_data->dma_blk_cfg.dest_address = block->dest_address;
261+
ch_data->dma_blk_cfg.source_addr_adj = block->source_addr_adj;
262+
ch_data->dma_blk_cfg.dest_addr_adj = block->dest_addr_adj;
263+
ch_data->dma_blk_cfg.block_size = block->block_size;
264+
265+
ch_data->dma_cfg.dma_slot = config->dma_slot;
266+
ch_data->dma_cfg.channel_direction = config->channel_direction;
267+
ch_data->dma_cfg.block_count = config->block_count;
268+
ch_data->dma_cfg.head_block = &ch_data->dma_blk_cfg;
269+
ch_data->dma_cfg.source_data_size = config->source_data_size;
270+
ch_data->dma_cfg.dest_data_size = config->dest_data_size;
271+
ch_data->dma_cfg.source_burst_length = config->source_burst_length;
272+
ch_data->dma_cfg.dma_callback = config->dma_callback;
273+
ch_data->dma_cfg.user_data = config->user_data;
274+
275+
LOG_DBG("Configured channel %u for %08x to %08x (%u bytes)",
276+
channel,
277+
block->source_address,
278+
block->dest_address,
279+
block->block_size);
280+
}
281+
#else
236282
LOG_DBG("Configured channel %u for %08x to %08x (%u bytes)",
237283
channel,
238284
block->source_address,
239285
block->dest_address,
240286
block->block_size);
287+
#endif
241288

242289
return 0;
243290
}
@@ -279,6 +326,13 @@ static int dma_cc23x0_reload(const struct device *dev, uint32_t channel,
279326
uDMASetChannelTransfer(&data->desc[channel], DMA_CC23_MODE(channel),
280327
(void *)src, (void *)dst, xfer_size);
281328

329+
#ifdef CONFIG_PM_DEVICE
330+
/* Save context */
331+
ch_data->dma_blk_cfg.source_address = src;
332+
ch_data->dma_blk_cfg.dest_address = dst;
333+
ch_data->dma_blk_cfg.block_size = size;
334+
#endif
335+
282336
LOG_DBG("Reloaded channel %u for %08x to %08x (%u bytes)",
283337
channel, src, dst, size);
284338

@@ -343,29 +397,83 @@ static int dma_cc23x0_get_status(const struct device *dev, uint32_t channel,
343397
return 0;
344398
}
345399

346-
static int dma_cc23x0_init(const struct device *dev)
400+
static int dma_cc23x0_enable(struct dma_cc23x0_data *data)
347401
{
348-
struct dma_cc23x0_data *data = dev->data;
402+
CLKCTLEnable(CLKCTL_BASE, CLKCTL_DMA);
403+
404+
uDMAEnable();
405+
406+
/* Set base address for channel control table (descriptors) */
407+
uDMASetControlBase(data->desc);
408+
409+
return 0;
410+
}
349411

412+
static int dma_cc23x0_init(const struct device *dev)
413+
{
350414
IRQ_CONNECT(DT_INST_IRQN(0),
351415
DT_INST_IRQ(0, priority),
352416
dma_cc23x0_isr,
353417
DEVICE_DT_INST_GET(0),
354418
0);
355419
irq_enable(DT_INST_IRQN(0));
356420

357-
/* Enable clock */
358-
CLKCTLEnable(CLKCTL_BASE, CLKCTL_DMA);
421+
return dma_cc23x0_enable(dev->data);
422+
}
359423

360-
/* Enable DMA */
361-
uDMAEnable();
424+
#ifdef CONFIG_PM_DEVICE
362425

363-
/* Set base address for channel control table (descriptors) */
364-
uDMASetControlBase(data->desc);
426+
static int dma_cc23x0_pm_action(const struct device *dev, enum pm_device_action action)
427+
{
428+
struct dma_cc23x0_data *data = dev->data;
429+
int i = 0;
430+
431+
switch (action) {
432+
case PM_DEVICE_ACTION_SUSPEND:
433+
/*
434+
* We assume that DMA clients (peripheral drivers or applications)
435+
* should take care of PM lock/unlock (pm_policy_state_lock_get/put).
436+
* This assumption is made for that SoC because:
437+
* - If a peripheral channel is used, then the transfer completion is
438+
* signaled on the peripheral's interrupt (handled in the DMA client
439+
* driver). This operating mode is specific to this SoC.
440+
* - If a software channel is used (memory-to-memory transfer), then
441+
* the transfer completion can be signaled to the application through
442+
* a callback.
443+
* Thus, in both cases, the PM can be unlocked at the right time by the
444+
* DMA client. When this point is reached, there should not be ongoing
445+
* transfer.
446+
*
447+
* Despite this assumption, ensure that none transfer is ongoing in case
448+
* PM state lock was not properly handled by DMA clients.
449+
*/
450+
if (uDMAIsChannelEnabled(DMA_CC23_ALL_CH_MASK)) {
451+
return -EBUSY;
452+
}
365453

366-
return 0;
454+
uDMADisable();
455+
CLKCTLDisable(CLKCTL_BASE, CLKCTL_DMA);
456+
457+
return 0;
458+
case PM_DEVICE_ACTION_RESUME:
459+
dma_cc23x0_enable(data);
460+
461+
/* Restore context for the channels that were configured before */
462+
ARRAY_FOR_EACH_PTR(data->channels, ch_data) {
463+
if (ch_data->configured) {
464+
dma_cc23x0_config(dev, i, &ch_data->dma_cfg);
465+
}
466+
i++;
467+
}
468+
469+
return 0;
470+
default:
471+
return -ENOTSUP;
472+
}
367473
}
368474

475+
#endif /* CONFIG_PM_DEVICE */
476+
369477
static struct dma_cc23x0_data cc23x0_data;
370478

371479
static DEVICE_API(dma, dma_cc23x0_api) = {
@@ -376,7 +484,10 @@ static DEVICE_API(dma, dma_cc23x0_api) = {
376484
.get_status = dma_cc23x0_get_status,
377485
};
378486

379-
DEVICE_DT_INST_DEFINE(0, dma_cc23x0_init, NULL,
487+
PM_DEVICE_DT_INST_DEFINE(0, dma_cc23x0_pm_action);
488+
489+
DEVICE_DT_INST_DEFINE(0, dma_cc23x0_init,
490+
PM_DEVICE_DT_INST_GET(0),
380491
&cc23x0_data, NULL,
381492
PRE_KERNEL_1, CONFIG_DMA_INIT_PRIORITY,
382493
&dma_cc23x0_api);

0 commit comments

Comments
 (0)