forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdma_mcux_smartdma.c
162 lines (129 loc) · 4.41 KB
/
dma_mcux_smartdma.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/*
* Copyright 2023 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/device.h>
#include <zephyr/drivers/dma.h>
#include <zephyr/logging/log.h>
#include <zephyr/pm/policy.h>
#include <zephyr/drivers/dma/dma_mcux_smartdma.h>
#include <soc.h>
#include <fsl_smartdma.h>
#include <fsl_inputmux.h>
#include <fsl_power.h>
#define DT_DRV_COMPAT nxp_smartdma
LOG_MODULE_REGISTER(dma_mcux_smartdma, CONFIG_DMA_LOG_LEVEL);
struct dma_mcux_smartdma_config {
SMARTDMA_Type *base;
void (*irq_config_func)(const struct device *dev);
void (**smartdma_progs)(void);
};
struct dma_mcux_smartdma_data {
/* Installed DMA callback and user data */
dma_callback_t callback;
void *user_data;
};
/* Seems to be written to smartDMA control register when it is configured */
#define SMARTDMA_MAGIC 0xC0DE0000U
/* These bits are set when the SMARTDMA boots, cleared to reset it */
#define SMARTDMA_BOOT 0x11
/* Configure a channel */
static int dma_mcux_smartdma_configure(const struct device *dev,
uint32_t channel, struct dma_config *config)
{
const struct dma_mcux_smartdma_config *dev_config = dev->config;
struct dma_mcux_smartdma_data *data = dev->data;
uint32_t prog_idx = config->dma_slot;
/* SMARTDMA does not have channels */
ARG_UNUSED(channel);
data->callback = config->dma_callback;
data->user_data = config->user_data;
/* Reset smartDMA */
SMARTDMA_Reset();
/* Write the head block pointer directly to SMARTDMA */
dev_config->base->ARM2EZH = (uint32_t)config->head_block;
/* Save program */
dev_config->base->BOOTADR = (uint32_t)dev_config->smartdma_progs[prog_idx];
LOG_DBG("Boot address set to 0x%X", dev_config->base->BOOTADR);
return 0;
}
static int dma_mcux_smartdma_start(const struct device *dev, uint32_t channel)
{
const struct dma_mcux_smartdma_config *config = dev->config;
/* Block PM transition until DMA completes */
pm_policy_state_lock_get(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES);
/* Kick off SMARTDMA */
config->base->CTRL = SMARTDMA_MAGIC | SMARTDMA_BOOT;
return 0;
}
static int dma_mcux_smartdma_stop(const struct device *dev, uint32_t channel)
{
ARG_UNUSED(dev);
ARG_UNUSED(channel);
/* Stop DMA */
SMARTDMA_Reset();
/* Release PM lock */
pm_policy_state_lock_put(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES);
return 0;
}
static int dma_mcux_smartdma_init(const struct device *dev)
{
const struct dma_mcux_smartdma_config *config = dev->config;
SMARTDMA_InitWithoutFirmware();
config->irq_config_func(dev);
return 0;
}
static void dma_mcux_smartdma_irq(const struct device *dev)
{
const struct dma_mcux_smartdma_data *data = dev->data;
if (data->callback) {
data->callback(dev, data->user_data, 0, 0);
}
/* Release PM lock */
pm_policy_state_lock_put(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES);
}
/**
* @brief install SMARTDMA firmware
*
* Install a custom firmware for the smartDMA. This function allows the user
* to install a custom firmware into the smartDMA, which implements
* different API functions than the standard MCUX SDK firmware.
* @param dev: smartDMA device
* @param firmware: address of buffer containing smartDMA firmware
* @param len: length of firmware buffer
*/
void dma_smartdma_install_fw(const struct device *dev, uint8_t *firmware,
uint32_t len)
{
const struct dma_mcux_smartdma_config *config = dev->config;
SMARTDMA_InstallFirmware((uint32_t)config->smartdma_progs, firmware, len);
}
static const struct dma_driver_api dma_mcux_smartdma_api = {
.config = dma_mcux_smartdma_configure,
.start = dma_mcux_smartdma_start,
.stop = dma_mcux_smartdma_stop,
};
#define SMARTDMA_INIT(n) \
static void dma_mcux_smartdma_config_func_##n(const struct device *dev) \
{ \
IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \
dma_mcux_smartdma_irq, \
DEVICE_DT_INST_GET(n), 0); \
irq_enable(DT_INST_IRQN(n)); \
} \
\
static const struct dma_mcux_smartdma_config smartdma_##n##_config = { \
.base = (SMARTDMA_Type *)DT_INST_REG_ADDR(n), \
.smartdma_progs = (void (**)(void))DT_INST_PROP(n, program_mem),\
.irq_config_func = dma_mcux_smartdma_config_func_##n, \
}; \
static struct dma_mcux_smartdma_data smartdma_##n##_data; \
\
DEVICE_DT_INST_DEFINE(n, \
&dma_mcux_smartdma_init, \
NULL, \
&smartdma_##n##_data, &smartdma_##n##_config, \
POST_KERNEL, CONFIG_DMA_INIT_PRIORITY, \
&dma_mcux_smartdma_api);
DT_INST_FOREACH_STATUS_OKAY(SMARTDMA_INIT)