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