1 /*
2 * Copyright (c) 2023 Nuvoton Technology Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT nuvoton_numaker_adc
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/drivers/reset.h>
11 #include <zephyr/drivers/pinctrl.h>
12 #include <zephyr/drivers/adc.h>
13 #include <zephyr/drivers/clock_control.h>
14 #include <zephyr/drivers/clock_control/clock_control_numaker.h>
15 #include <zephyr/logging/log.h>
16 #include <soc.h>
17 #include <NuMicro.h>
18
19 #define ADC_CONTEXT_USES_KERNEL_TIMER
20 #define ADC_CONTEXT_ENABLE_ON_COMPLETE
21 #include "adc_context.h"
22
23 LOG_MODULE_REGISTER(adc_numaker, CONFIG_ADC_LOG_LEVEL);
24
25 /* Device config */
26 struct adc_numaker_config {
27 /* eadc base address */
28 EADC_T *eadc_base;
29 uint8_t channel_cnt;
30 const struct reset_dt_spec reset;
31 /* clock configuration */
32 uint32_t clk_modidx;
33 uint32_t clk_src;
34 uint32_t clk_div;
35 const struct device *clk_dev;
36 const struct pinctrl_dev_config *pincfg;
37 void (*irq_config_func)(const struct device *dev);
38 };
39
40 /* Driver context/data */
41 struct adc_numaker_data {
42 struct adc_context ctx;
43 const struct device *dev;
44 uint16_t *buffer;
45 uint16_t *buf_end;
46 uint16_t *repeat_buffer;
47 bool is_differential;
48 uint32_t channels;
49 uint32_t acq_time;
50 };
51
adc_numaker_channel_setup(const struct device * dev,const struct adc_channel_cfg * chan_cfg)52 static int adc_numaker_channel_setup(const struct device *dev,
53 const struct adc_channel_cfg *chan_cfg)
54 {
55 const struct adc_numaker_config *cfg = dev->config;
56 struct adc_numaker_data *data = dev->data;
57
58 if (chan_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) {
59 if ((ADC_ACQ_TIME_UNIT(chan_cfg->acquisition_time) != ADC_ACQ_TIME_TICKS) ||
60 (ADC_ACQ_TIME_VALUE(chan_cfg->acquisition_time) > 255)) {
61 LOG_ERR("Selected ADC acquisition time is not in 0~255 ticks");
62 return -EINVAL;
63 }
64 }
65 data->acq_time = ADC_ACQ_TIME_VALUE(chan_cfg->acquisition_time);
66
67 if (chan_cfg->gain != ADC_GAIN_1) {
68 LOG_ERR("Not support channel gain");
69 return -ENOTSUP;
70 }
71
72 if (chan_cfg->reference != ADC_REF_INTERNAL) {
73 LOG_ERR("Not support channel reference");
74 return -ENOTSUP;
75 }
76
77 if (chan_cfg->channel_id >= cfg->channel_cnt) {
78 LOG_ERR("Invalid channel (%u)", chan_cfg->channel_id);
79 return -EINVAL;
80 }
81
82 data->is_differential = (chan_cfg->differential) ? true : false;
83
84 return 0;
85 }
86
m_adc_numaker_validate_buffer_size(const struct device * dev,const struct adc_sequence * sequence)87 static int m_adc_numaker_validate_buffer_size(const struct device *dev,
88 const struct adc_sequence *sequence)
89 {
90 const struct adc_numaker_config *cfg = dev->config;
91 uint8_t channel_cnt = 0;
92 uint32_t mask;
93 size_t needed_size;
94
95 for (mask = BIT(cfg->channel_cnt - 1); mask != 0; mask >>= 1) {
96 if (mask & sequence->channels) {
97 channel_cnt++;
98 }
99 }
100
101 needed_size = channel_cnt * sizeof(uint16_t);
102 if (sequence->options) {
103 needed_size *= (1 + sequence->options->extra_samplings);
104 }
105
106 if (sequence->buffer_size < needed_size) {
107 return -ENOBUFS;
108 }
109
110 return 0;
111 }
112
adc_numaker_isr(const struct device * dev)113 static void adc_numaker_isr(const struct device *dev)
114 {
115 const struct adc_numaker_config *cfg = dev->config;
116 EADC_T *eadc = cfg->eadc_base;
117 struct adc_numaker_data *const data = dev->data;
118 uint32_t channel_mask = data->channels;
119 uint32_t module_mask = channel_mask;
120 uint32_t module_id;
121 uint16_t conv_data;
122 uint32_t pend_flag;
123
124 LOG_DBG("ADC ISR pend flag: 0x%X\n", pend_flag);
125 LOG_DBG("ADC ISR STATUS2[0x%x] STATUS3[0x%x]", eadc->STATUS2, eadc->STATUS3);
126 /* Complete the conversion of channels.
127 * Check EAC idle by EADC_STATUS2_BUSY_Msk
128 * Check trigger source coming by EADC_STATUS2_ADOVIF_Msk
129 * Confirm all sample modules are idle by EADC_STATUS2_ADOVIF_Msk
130 */
131 if (!(eadc->STATUS2 & EADC_STATUS2_BUSY_Msk) &&
132 ((eadc->STATUS3 & EADC_STATUS3_CURSPL_Msk) == EADC_STATUS3_CURSPL_Msk)) {
133 /* Stop the conversion for sample module */
134 EADC_STOP_CONV(eadc, module_mask);
135
136 /* Disable sample module A/D ADINT0 interrupt. */
137 EADC_DISABLE_INT(eadc, BIT0);
138
139 /* Disable the sample module ADINT0 interrupt source */
140 EADC_DISABLE_SAMPLE_MODULE_INT(eadc, 0, module_mask);
141
142 /* Get conversion data of each sample module for selected channel */
143 while (module_mask) {
144 module_id = find_lsb_set(module_mask) - 1;
145
146 conv_data = EADC_GET_CONV_DATA(eadc, module_id);
147 if (data->buffer < data->buf_end) {
148 *data->buffer++ = conv_data;
149 LOG_DBG("ADC ISR id=%d, data=0x%x", module_id, conv_data);
150 }
151 module_mask &= ~BIT(module_id);
152
153 /* Disable all channels on each sample module */
154 eadc->SCTL[module_id] = 0;
155 }
156
157 /* Inform sampling is done */
158 adc_context_on_sampling_done(&data->ctx, data->dev);
159 }
160
161 /* Clear the A/D ADINT0 interrupt flag */
162 EADC_CLR_INT_FLAG(eadc, EADC_STATUS2_ADIF0_Msk);
163 }
164
m_adc_numaker_start_scan(const struct device * dev)165 static void m_adc_numaker_start_scan(const struct device *dev)
166 {
167 const struct adc_numaker_config *cfg = dev->config;
168 EADC_T *eadc = cfg->eadc_base;
169 struct adc_numaker_data *const data = dev->data;
170 uint32_t channel_mask = data->channels;
171 uint32_t module_mask = channel_mask;
172 uint32_t channel_id;
173 uint32_t module_id;
174
175 /* Configure the sample module, analog input channel and software trigger source */
176 while (channel_mask) {
177 channel_id = find_lsb_set(channel_mask) - 1;
178 module_id = channel_id;
179 channel_mask &= ~BIT(channel_id);
180 EADC_ConfigSampleModule(eadc, module_id,
181 EADC_SOFTWARE_TRIGGER, channel_id);
182 /* Set sample module external sampling time to 0 */
183 EADC_SetExtendSampleTime(eadc, module_id, data->acq_time);
184 }
185
186 /* Clear the A/D ADINT0 interrupt flag for safe */
187 EADC_CLR_INT_FLAG(eadc, EADC_STATUS2_ADIF0_Msk);
188
189 /* Enable sample module A/D ADINT0 interrupt. */
190 EADC_ENABLE_INT(eadc, BIT0);
191
192 /* Enable sample module interrupt ADINT0. */
193 EADC_ENABLE_SAMPLE_MODULE_INT(eadc, 0, module_mask);
194
195 /* Start conversion */
196 EADC_START_CONV(eadc, module_mask);
197 }
198
199 /* Implement ADC API functions of adc_context.h
200 * - adc_context_start_sampling()
201 * - adc_context_update_buffer_pointer()
202 */
adc_context_start_sampling(struct adc_context * ctx)203 static void adc_context_start_sampling(struct adc_context *ctx)
204 {
205 struct adc_numaker_data *const data =
206 CONTAINER_OF(ctx, struct adc_numaker_data, ctx);
207
208 data->repeat_buffer = data->buffer;
209 data->channels = ctx->sequence.channels;
210
211 /* Start ADC conversion for sample modules/channels */
212 m_adc_numaker_start_scan(data->dev);
213 }
214
adc_context_update_buffer_pointer(struct adc_context * ctx,bool repeat_sampling)215 static void adc_context_update_buffer_pointer(struct adc_context *ctx,
216 bool repeat_sampling)
217 {
218 struct adc_numaker_data *data =
219 CONTAINER_OF(ctx, struct adc_numaker_data, ctx);
220
221 if (repeat_sampling) {
222 data->buffer = data->repeat_buffer;
223 }
224 }
225
adc_context_on_complete(struct adc_context * ctx,int status)226 static void adc_context_on_complete(struct adc_context *ctx, int status)
227 {
228 struct adc_numaker_data *data =
229 CONTAINER_OF(ctx, struct adc_numaker_data, ctx);
230 const struct adc_numaker_config *cfg = data->dev->config;
231 EADC_T *eadc = cfg->eadc_base;
232
233 ARG_UNUSED(status);
234
235 /* Disable ADC */
236 EADC_Close(eadc);
237 }
238
m_adc_numaker_start_read(const struct device * dev,const struct adc_sequence * sequence)239 static int m_adc_numaker_start_read(const struct device *dev,
240 const struct adc_sequence *sequence)
241 {
242 const struct adc_numaker_config *cfg = dev->config;
243 struct adc_numaker_data *data = dev->data;
244 EADC_T *eadc = cfg->eadc_base;
245 int err;
246
247 err = m_adc_numaker_validate_buffer_size(dev, sequence);
248 if (err) {
249 LOG_ERR("ADC provided buffer is too small");
250 return err;
251 }
252
253 if (!sequence->resolution) {
254 LOG_ERR("ADC resolution is not valid");
255 return -EINVAL;
256 }
257 LOG_DBG("Configure resolution=%d", sequence->resolution);
258
259 /* Enable the A/D converter */
260 if (data->is_differential) {
261 EADC_Open(eadc, EADC_CTL_DIFFEN_DIFFERENTIAL);
262 } else {
263 EADC_Open(eadc, EADC_CTL_DIFFEN_SINGLE_END);
264 }
265
266 data->buffer = sequence->buffer;
267 data->buf_end = data->buffer + sequence->buffer_size / sizeof(uint16_t);
268
269 /* Start ADC conversion */
270 adc_context_start_read(&data->ctx, sequence);
271
272 return adc_context_wait_for_completion(&data->ctx);
273 }
274
adc_numaker_read(const struct device * dev,const struct adc_sequence * sequence)275 static int adc_numaker_read(const struct device *dev,
276 const struct adc_sequence *sequence)
277 {
278 struct adc_numaker_data *data = dev->data;
279 int err;
280
281 adc_context_lock(&data->ctx, false, NULL);
282 err = m_adc_numaker_start_read(dev, sequence);
283 adc_context_release(&data->ctx, err);
284
285 return err;
286 }
287
288 #ifdef CONFIG_ADC_ASYNC
adc_numaker_read_async(const struct device * dev,const struct adc_sequence * sequence,struct k_poll_signal * async)289 static int adc_numaker_read_async(const struct device *dev,
290 const struct adc_sequence *sequence,
291 struct k_poll_signal *async)
292 {
293 struct adc_numaker_data *data = dev->data;
294 int err;
295
296 adc_context_lock(&data->ctx, true, async);
297 err = m_adc_numaker_start_read(dev, sequence);
298 adc_context_release(&data->ctx, err);
299
300 return err;
301 }
302 #endif
303
304 static const struct adc_driver_api adc_numaker_driver_api = {
305 .channel_setup = adc_numaker_channel_setup,
306 .read = adc_numaker_read,
307 #ifdef CONFIG_ADC_ASYNC
308 .read_async = adc_numaker_read_async,
309 #endif
310 };
311
adc_numaker_init(const struct device * dev)312 static int adc_numaker_init(const struct device *dev)
313 {
314 const struct adc_numaker_config *cfg = dev->config;
315 struct adc_numaker_data *data = dev->data;
316 int err;
317 struct numaker_scc_subsys scc_subsys;
318
319 /* Validate this module's reset object */
320 if (!device_is_ready(cfg->reset.dev)) {
321 LOG_ERR("reset controller not ready");
322 return -ENODEV;
323 }
324
325 data->dev = dev;
326
327 SYS_UnlockReg();
328
329 /* CLK controller */
330 memset(&scc_subsys, 0x00, sizeof(scc_subsys));
331 scc_subsys.subsys_id = NUMAKER_SCC_SUBSYS_ID_PCC;
332 scc_subsys.pcc.clk_modidx = cfg->clk_modidx;
333 scc_subsys.pcc.clk_src = cfg->clk_src;
334 scc_subsys.pcc.clk_div = cfg->clk_div;
335
336 /* Equivalent to CLK_EnableModuleClock() */
337 err = clock_control_on(cfg->clk_dev, (clock_control_subsys_t)&scc_subsys);
338 if (err != 0) {
339 goto done;
340 }
341 /* Equivalent to CLK_SetModuleClock() */
342 err = clock_control_configure(cfg->clk_dev, (clock_control_subsys_t)&scc_subsys, NULL);
343 if (err != 0) {
344 goto done;
345 }
346
347 err = pinctrl_apply_state(cfg->pincfg, PINCTRL_STATE_DEFAULT);
348 if (err) {
349 LOG_ERR("Failed to apply pinctrl state");
350 goto done;
351 }
352
353 /* Reset EADC to default state, same as BSP's SYS_ResetModule(id_rst) */
354 reset_line_toggle_dt(&cfg->reset);
355
356 /* Enable NVIC */
357 cfg->irq_config_func(dev);
358
359 /* Init mutex of adc_context */
360 adc_context_unlock_unconditionally(&data->ctx);
361
362 done:
363 SYS_LockReg();
364 return err;
365 }
366
367 #define ADC_NUMAKER_IRQ_CONFIG_FUNC(n) \
368 static void adc_numaker_irq_config_func_##n(const struct device *dev) \
369 { \
370 IRQ_CONNECT(DT_INST_IRQN(n), \
371 DT_INST_IRQ(n, priority), \
372 adc_numaker_isr, \
373 DEVICE_DT_INST_GET(n), 0); \
374 \
375 irq_enable(DT_INST_IRQN(n)); \
376 }
377
378 #define ADC_NUMAKER_INIT(inst) \
379 PINCTRL_DT_INST_DEFINE(inst); \
380 ADC_NUMAKER_IRQ_CONFIG_FUNC(inst) \
381 \
382 static const struct adc_numaker_config adc_numaker_cfg_##inst = { \
383 .eadc_base = (EADC_T *)DT_INST_REG_ADDR(inst), \
384 .channel_cnt = DT_INST_PROP(inst, channels), \
385 .reset = RESET_DT_SPEC_INST_GET(inst), \
386 .clk_modidx = DT_INST_CLOCKS_CELL(inst, clock_module_index), \
387 .clk_src = DT_INST_CLOCKS_CELL(inst, clock_source), \
388 .clk_div = DT_INST_CLOCKS_CELL(inst, clock_divider), \
389 .clk_dev = DEVICE_DT_GET(DT_PARENT(DT_INST_CLOCKS_CTLR(inst))), \
390 .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
391 .irq_config_func = adc_numaker_irq_config_func_##inst, \
392 }; \
393 \
394 static struct adc_numaker_data adc_numaker_data_##inst = { \
395 ADC_CONTEXT_INIT_TIMER(adc_numaker_data_##inst, ctx), \
396 ADC_CONTEXT_INIT_LOCK(adc_numaker_data_##inst, ctx), \
397 ADC_CONTEXT_INIT_SYNC(adc_numaker_data_##inst, ctx), \
398 }; \
399 DEVICE_DT_INST_DEFINE(inst, \
400 &adc_numaker_init, NULL, \
401 &adc_numaker_data_##inst, &adc_numaker_cfg_##inst, \
402 POST_KERNEL, CONFIG_ADC_INIT_PRIORITY, \
403 &adc_numaker_driver_api);
404
405 DT_INST_FOREACH_STATUS_OKAY(ADC_NUMAKER_INIT)
406