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