1 /*
2  * Copyright (c) 2024 Ambiq Micro Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT ambiq_adc
8 
9 #include <zephyr/drivers/adc.h>
10 #include <zephyr/drivers/pinctrl.h>
11 #include <zephyr/pm/device.h>
12 #include <zephyr/pm/device_runtime.h>
13 #include <zephyr/kernel.h>
14 
15 #define ADC_CONTEXT_USES_KERNEL_TIMER
16 #include "adc_context.h"
17 
18 /* ambiq-sdk includes */
19 #include <am_mcu_apollo.h>
20 
21 #include <zephyr/logging/log.h>
22 LOG_MODULE_REGISTER(adc_ambiq, CONFIG_ADC_LOG_LEVEL);
23 
24 typedef int (*ambiq_adc_pwr_func_t)(void);
25 #define PWRCTRL_MAX_WAIT_US   5
26 /* Number of slots available. */
27 #define AMBIQ_ADC_SLOT_NUMBER AM_HAL_ADC_MAX_SLOTS
28 
29 struct adc_ambiq_config {
30 	uint32_t base;
31 	int size;
32 	uint8_t num_channels;
33 	void (*irq_config_func)(void);
34 	const struct pinctrl_dev_config *pin_cfg;
35 	ambiq_adc_pwr_func_t pwr_func;
36 };
37 
38 struct adc_ambiq_data {
39 	struct adc_context ctx;
40 	void *adcHandle;
41 	uint16_t *buffer;
42 	uint16_t *repeat_buffer;
43 	uint8_t active_channels;
44 };
45 
adc_ambiq_set_resolution(am_hal_adc_slot_prec_e * prec,uint8_t adc_resolution)46 static int adc_ambiq_set_resolution(am_hal_adc_slot_prec_e *prec, uint8_t adc_resolution)
47 {
48 	switch (adc_resolution) {
49 	case 8:
50 		*prec = AM_HAL_ADC_SLOT_8BIT;
51 		break;
52 	case 10:
53 		*prec = AM_HAL_ADC_SLOT_10BIT;
54 		break;
55 	case 12:
56 		*prec = AM_HAL_ADC_SLOT_12BIT;
57 		break;
58 #if !defined(CONFIG_SOC_SERIES_APOLLO4X)
59 	case 14:
60 		*prec = AM_HAL_ADC_SLOT_14BIT;
61 		break;
62 #endif
63 	default:
64 		return -ENOTSUP;
65 	}
66 
67 	return 0;
68 }
69 
adc_ambiq_slot_config(const struct device * dev,const struct adc_sequence * sequence,am_hal_adc_slot_chan_e channel,uint32_t ui32SlotNumber)70 static int adc_ambiq_slot_config(const struct device *dev, const struct adc_sequence *sequence,
71 				 am_hal_adc_slot_chan_e channel, uint32_t ui32SlotNumber)
72 {
73 	struct adc_ambiq_data *data = dev->data;
74 	am_hal_adc_slot_config_t ADCSlotConfig;
75 
76 	if (adc_ambiq_set_resolution(&ADCSlotConfig.ePrecisionMode, sequence->resolution) != 0) {
77 		LOG_ERR("unsupported resolution %d", sequence->resolution);
78 		return -ENOTSUP;
79 	}
80 
81 	/* Set up an ADC slot */
82 	ADCSlotConfig.eMeasToAvg = AM_HAL_ADC_SLOT_AVG_1;
83 	ADCSlotConfig.eChannel = channel;
84 	ADCSlotConfig.bWindowCompare = false;
85 	ADCSlotConfig.bEnabled = true;
86 #if defined(CONFIG_SOC_SERIES_APOLLO4X)
87 	ADCSlotConfig.ui32TrkCyc = AM_HAL_ADC_MIN_TRKCYC;
88 #endif
89 	if (AM_HAL_STATUS_SUCCESS !=
90 	    am_hal_adc_configure_slot(data->adcHandle, ui32SlotNumber, &ADCSlotConfig)) {
91 		LOG_ERR("configuring ADC Slot 0 failed.\n");
92 		return -ENODEV;
93 	}
94 
95 	return 0;
96 }
97 
adc_ambiq_isr(const struct device * dev)98 static void adc_ambiq_isr(const struct device *dev)
99 {
100 	struct adc_ambiq_data *data = dev->data;
101 	uint32_t ui32IntMask;
102 	uint32_t ui32NumSamples;
103 	am_hal_adc_sample_t Sample;
104 
105 	/* Read the interrupt status. */
106 	am_hal_adc_interrupt_status(data->adcHandle, &ui32IntMask, true);
107 	/* Clear the ADC interrupt.*/
108 	am_hal_adc_interrupt_clear(data->adcHandle, ui32IntMask);
109 
110 	/*
111 	 * If we got a conversion completion interrupt (which should be our only
112 	 * ADC interrupt), go ahead and read the data.
113 	 */
114 	if (ui32IntMask & AM_HAL_ADC_INT_CNVCMP) {
115 		for (uint32_t i = 0; i < data->active_channels; i++) {
116 			/* Read the value from the FIFO. */
117 			ui32NumSamples = 1;
118 			am_hal_adc_samples_read(data->adcHandle, false, NULL, &ui32NumSamples,
119 						&Sample);
120 			*data->buffer++ = Sample.ui32Sample;
121 		}
122 		am_hal_adc_disable(data->adcHandle);
123 		adc_context_on_sampling_done(&data->ctx, dev);
124 	}
125 }
126 
adc_ambiq_check_buffer_size(const struct adc_sequence * sequence,uint8_t active_channels)127 static int adc_ambiq_check_buffer_size(const struct adc_sequence *sequence, uint8_t active_channels)
128 {
129 	size_t needed_buffer_size;
130 
131 	needed_buffer_size = active_channels * sizeof(uint16_t);
132 
133 	if (sequence->options) {
134 		needed_buffer_size *= (1 + sequence->options->extra_samplings);
135 	}
136 
137 	if (sequence->buffer_size < needed_buffer_size) {
138 		LOG_DBG("Provided buffer is too small (%u/%u)", sequence->buffer_size,
139 			needed_buffer_size);
140 		return -ENOMEM;
141 	}
142 
143 	return 0;
144 }
145 
adc_ambiq_start_read(const struct device * dev,const struct adc_sequence * sequence)146 static int adc_ambiq_start_read(const struct device *dev, const struct adc_sequence *sequence)
147 {
148 	struct adc_ambiq_data *data = dev->data;
149 	const struct adc_ambiq_config *cfg = dev->config;
150 	uint8_t channel_id = 0;
151 	uint32_t channels = 0;
152 	uint8_t active_channels = 0;
153 	uint8_t slot_index;
154 
155 	int error = 0;
156 
157 	if (sequence->channels & ~BIT_MASK(cfg->num_channels)) {
158 		LOG_ERR("Incorrect channels, bitmask 0x%x", sequence->channels);
159 		return -EINVAL;
160 	}
161 
162 	if (sequence->channels == 0UL) {
163 		LOG_ERR("No channel selected");
164 		return -EINVAL;
165 	}
166 
167 	error = adc_ambiq_check_buffer_size(sequence, active_channels);
168 	if (error < 0) {
169 		return error;
170 	}
171 
172 	active_channels = POPCOUNT(sequence->channels);
173 	if (active_channels > AMBIQ_ADC_SLOT_NUMBER) {
174 		LOG_ERR("Too many channels for sequencer. Max: %d", AMBIQ_ADC_SLOT_NUMBER);
175 		return -ENOTSUP;
176 	}
177 
178 	channels = sequence->channels;
179 	for (slot_index = 0; slot_index < active_channels; slot_index++) {
180 		channel_id = find_lsb_set(channels) - 1;
181 		error = adc_ambiq_slot_config(dev, sequence, channel_id, slot_index);
182 		if (error < 0) {
183 			return error;
184 		}
185 		channels &= ~BIT(channel_id);
186 	}
187 	__ASSERT_NO_MSG(channels == 0);
188 
189 	data->active_channels = active_channels;
190 	data->buffer = sequence->buffer;
191 	/* Start ADC conversion */
192 	adc_context_start_read(&data->ctx, sequence);
193 	error = adc_context_wait_for_completion(&data->ctx);
194 
195 	return error;
196 }
197 
adc_ambiq_read(const struct device * dev,const struct adc_sequence * sequence)198 static int adc_ambiq_read(const struct device *dev, const struct adc_sequence *sequence)
199 {
200 	struct adc_ambiq_data *data = dev->data;
201 	int error = 0;
202 
203 	error = pm_device_runtime_get(dev);
204 	if (error < 0) {
205 		LOG_ERR("pm_device_runtime_get failed: %d", error);
206 	}
207 
208 	adc_context_lock(&data->ctx, false, NULL);
209 	error = adc_ambiq_start_read(dev, sequence);
210 	adc_context_release(&data->ctx, error);
211 
212 	int ret = error;
213 
214 	error = pm_device_runtime_put(dev);
215 	if (error < 0) {
216 		LOG_ERR("pm_device_runtime_put failed: %d", error);
217 	}
218 
219 	error = ret;
220 
221 	return error;
222 }
223 
adc_ambiq_channel_setup(const struct device * dev,const struct adc_channel_cfg * chan_cfg)224 static int adc_ambiq_channel_setup(const struct device *dev, const struct adc_channel_cfg *chan_cfg)
225 {
226 	const struct adc_ambiq_config *cfg = dev->config;
227 
228 	if (chan_cfg->channel_id >= cfg->num_channels) {
229 		LOG_ERR("unsupported channel id '%d'", chan_cfg->channel_id);
230 		return -ENOTSUP;
231 	}
232 
233 	if (chan_cfg->gain != ADC_GAIN_1) {
234 		LOG_ERR("Gain is not valid");
235 		return -ENOTSUP;
236 	}
237 
238 	if (chan_cfg->reference != ADC_REF_INTERNAL) {
239 		LOG_ERR("Reference is not valid");
240 		return -ENOTSUP;
241 	}
242 
243 	if (chan_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) {
244 		LOG_ERR("unsupported acquisition_time '%d'", chan_cfg->acquisition_time);
245 		return -ENOTSUP;
246 	}
247 
248 	if (chan_cfg->differential) {
249 		LOG_ERR("Differential sampling not supported");
250 		return -ENOTSUP;
251 	}
252 
253 	return 0;
254 }
255 
adc_context_update_buffer_pointer(struct adc_context * ctx,bool repeat_sampling)256 static void adc_context_update_buffer_pointer(struct adc_context *ctx, bool repeat_sampling)
257 {
258 	struct adc_ambiq_data *data = CONTAINER_OF(ctx, struct adc_ambiq_data, ctx);
259 
260 	if (repeat_sampling) {
261 		data->buffer = data->repeat_buffer;
262 	}
263 }
264 
adc_context_start_sampling(struct adc_context * ctx)265 static void adc_context_start_sampling(struct adc_context *ctx)
266 {
267 	struct adc_ambiq_data *data = CONTAINER_OF(ctx, struct adc_ambiq_data, ctx);
268 
269 	data->repeat_buffer = data->buffer;
270 	/* Enable the ADC. */
271 	am_hal_adc_enable(data->adcHandle);
272 	/*Trigger the ADC*/
273 	am_hal_adc_sw_trigger(data->adcHandle);
274 }
275 
adc_ambiq_init(const struct device * dev)276 static int adc_ambiq_init(const struct device *dev)
277 {
278 	struct adc_ambiq_data *data = dev->data;
279 	const struct adc_ambiq_config *cfg = dev->config;
280 	am_hal_adc_config_t ADCConfig;
281 
282 	int ret;
283 
284 	/* Initialize the ADC and get the handle*/
285 	if (AM_HAL_STATUS_SUCCESS !=
286 	    am_hal_adc_initialize((cfg->base - ADC_BASE) / (cfg->size * 4), &data->adcHandle)) {
287 		ret = -ENODEV;
288 		LOG_ERR("Faile to initialize ADC, code:%d", ret);
289 		return ret;
290 	}
291 
292 	/* power on ADC*/
293 	ret = cfg->pwr_func();
294 
295 	/* Set up the ADC configuration parameters. These settings are reasonable
296 	 *  for accurate measurements at a low sample rate.
297 	 */
298 #if !defined(CONFIG_SOC_SERIES_APOLLO4X)
299 	ADCConfig.eClock = AM_HAL_ADC_CLKSEL_HFRC;
300 	ADCConfig.eReference = AM_HAL_ADC_REFSEL_INT_1P5;
301 #else
302 	ADCConfig.eClock = AM_HAL_ADC_CLKSEL_HFRC_24MHZ;
303 	ADCConfig.eRepeatTrigger = AM_HAL_ADC_RPTTRIGSEL_TMR,
304 #endif
305 	ADCConfig.ePolarity = AM_HAL_ADC_TRIGPOL_RISING;
306 	ADCConfig.eTrigger = AM_HAL_ADC_TRIGSEL_SOFTWARE;
307 	ADCConfig.eClockMode = AM_HAL_ADC_CLKMODE_LOW_POWER;
308 	ADCConfig.ePowerMode = AM_HAL_ADC_LPMODE0;
309 	ADCConfig.eRepeat = AM_HAL_ADC_SINGLE_SCAN;
310 	if (AM_HAL_STATUS_SUCCESS != am_hal_adc_configure(data->adcHandle, &ADCConfig)) {
311 		ret = -ENODEV;
312 		LOG_ERR("Configuring ADC failed, code:%d", ret);
313 		return ret;
314 	}
315 
316 	ret = pinctrl_apply_state(cfg->pin_cfg, PINCTRL_STATE_DEFAULT);
317 	if (ret < 0) {
318 		return ret;
319 	}
320 
321 	/* Enable the ADC interrupts in the ADC. */
322 	cfg->irq_config_func();
323 	am_hal_adc_interrupt_enable(data->adcHandle, AM_HAL_ADC_INT_CNVCMP);
324 
325 	adc_context_unlock_unconditionally(&data->ctx);
326 
327 	return 0;
328 }
329 
330 #ifdef CONFIG_ADC_ASYNC
adc_ambiq_read_async(const struct device * dev,const struct adc_sequence * sequence,struct k_poll_signal * async)331 static int adc_ambiq_read_async(const struct device *dev, const struct adc_sequence *sequence,
332 				struct k_poll_signal *async)
333 {
334 	struct adc_ambiq_data *data = dev->data;
335 	int error = 0;
336 
337 	error = pm_device_runtime_get(dev);
338 	if (error < 0) {
339 		LOG_ERR("pm_device_runtime_get failed: %d", error);
340 	}
341 
342 	adc_context_lock(&data->ctx, true, async);
343 	error = adc_ambiq_start_read(dev, sequence);
344 	adc_context_release(&data->ctx, error);
345 
346 	int ret = error;
347 
348 	error = pm_device_runtime_put(dev);
349 	if (error < 0) {
350 		LOG_ERR("pm_device_runtime_put failed: %d", error);
351 	}
352 
353 	error = ret;
354 
355 	return error;
356 }
357 #endif
358 
359 #ifdef CONFIG_PM_DEVICE
adc_ambiq_pm_action(const struct device * dev,enum pm_device_action action)360 static int adc_ambiq_pm_action(const struct device *dev, enum pm_device_action action)
361 {
362 	struct adc_ambiq_data *data = dev->data;
363 	uint32_t ret = 0;
364 	am_hal_sysctrl_power_state_e status;
365 
366 	switch (action) {
367 	case PM_DEVICE_ACTION_RESUME:
368 		status = AM_HAL_SYSCTRL_WAKE;
369 		break;
370 	case PM_DEVICE_ACTION_SUSPEND:
371 		status = AM_HAL_SYSCTRL_DEEPSLEEP;
372 		break;
373 	default:
374 		return -ENOTSUP;
375 	}
376 
377 	ret = am_hal_adc_power_control(data->adcHandle, status, true);
378 
379 	if (ret != AM_HAL_STATUS_SUCCESS) {
380 		return -EPERM;
381 	} else {
382 		return 0;
383 	}
384 }
385 #endif /* CONFIG_PM_DEVICE */
386 
387 #ifdef CONFIG_ADC_ASYNC
388 #define ADC_AMBIQ_DRIVER_API(n)                                                                    \
389 	static DEVICE_API(adc, adc_ambiq_driver_api_##n) = {                                       \
390 		.channel_setup = adc_ambiq_channel_setup,                                          \
391 		.read = adc_ambiq_read,                                                            \
392 		.read_async = adc_ambiq_read_async,                                                \
393 		.ref_internal = DT_INST_PROP(n, internal_vref_mv),                                 \
394 	};
395 #else
396 #define ADC_AMBIQ_DRIVER_API(n)                                                                    \
397 	static DEVICE_API(adc, adc_ambiq_driver_api_##n) = {                                       \
398 		.channel_setup = adc_ambiq_channel_setup,                                          \
399 		.read = adc_ambiq_read,                                                            \
400 		.ref_internal = DT_INST_PROP(n, internal_vref_mv),                                 \
401 	};
402 #endif
403 
404 #define ADC_AMBIQ_INIT(n)                                                                          \
405 	PINCTRL_DT_INST_DEFINE(n);                                                                 \
406 	ADC_AMBIQ_DRIVER_API(n);                                                                   \
407 	static int pwr_on_ambiq_adc_##n(void)                                                      \
408 	{                                                                                          \
409 		uint32_t addr = DT_REG_ADDR(DT_INST_PHANDLE(n, ambiq_pwrcfg)) +                    \
410 				DT_INST_PHA(n, ambiq_pwrcfg, offset);                              \
411 		sys_write32((sys_read32(addr) | DT_INST_PHA(n, ambiq_pwrcfg, mask)), addr);        \
412 		k_busy_wait(PWRCTRL_MAX_WAIT_US);                                                  \
413 		return 0;                                                                          \
414 	}                                                                                          \
415 	static void adc_irq_config_func_##n(void)                                                  \
416 	{                                                                                          \
417 		IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), adc_ambiq_isr,              \
418 			    DEVICE_DT_INST_GET(n), 0);                                             \
419 		irq_enable(DT_INST_IRQN(n));                                                       \
420 	};                                                                                         \
421 	static struct adc_ambiq_data adc_ambiq_data_##n = {                                        \
422 		ADC_CONTEXT_INIT_TIMER(adc_ambiq_data_##n, ctx),                                   \
423 		ADC_CONTEXT_INIT_LOCK(adc_ambiq_data_##n, ctx),                                    \
424 		ADC_CONTEXT_INIT_SYNC(adc_ambiq_data_##n, ctx),                                    \
425 	};                                                                                         \
426 	const static struct adc_ambiq_config adc_ambiq_config_##n = {                              \
427 		.base = DT_INST_REG_ADDR(n),                                                       \
428 		.size = DT_INST_REG_SIZE(n),                                                       \
429 		.num_channels = DT_PROP(DT_DRV_INST(n), channel_count),                            \
430 		.irq_config_func = adc_irq_config_func_##n,                                        \
431 		.pin_cfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n),                                      \
432 		.pwr_func = pwr_on_ambiq_adc_##n,                                                  \
433 	};                                                                                         \
434 	PM_DEVICE_DT_INST_DEFINE(n, adc_ambiq_pm_action);                                          \
435 	DEVICE_DT_INST_DEFINE(n, &adc_ambiq_init, PM_DEVICE_DT_INST_GET(n), &adc_ambiq_data_##n,   \
436 			      &adc_ambiq_config_##n, POST_KERNEL, CONFIG_ADC_INIT_PRIORITY,        \
437 			      &adc_ambiq_driver_api_##n);
438 
439 DT_INST_FOREACH_STATUS_OKAY(ADC_AMBIQ_INIT)
440