1 /*
2  * Copyright (c) 2023 Cypress Semiconductor Corporation (an Infineon company) or
3  * an affiliate of Cypress Semiconductor Corporation
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /**
9  * @brief ADC driver for Infineon CAT1 MCU family.
10  */
11 
12 #define DT_DRV_COMPAT infineon_cat1_adc
13 
14 #include <zephyr/drivers/adc.h>
15 #include <cyhal_adc.h>
16 #include <cyhal_utils_impl.h>
17 
18 #define ADC_CONTEXT_USES_KERNEL_TIMER
19 #include "adc_context.h"
20 
21 #include <zephyr/logging/log.h>
22 LOG_MODULE_REGISTER(ifx_cat1_adc, CONFIG_ADC_LOG_LEVEL);
23 
24 #if defined(PASS_SARMUX_PADS0_PORT)
25 #define _ADC_PORT PASS_SARMUX_PADS0_PORT
26 #elif defined(ADCMIC_GPIO_ADC_IN0_PORT)
27 #define _ADC_PORT ADCMIC_GPIO_ADC_IN0_PORT
28 #else
29 #error The selected device does not supported ADC
30 #endif
31 
32 #define ADC_CAT1_EVENTS_MASK (CYHAL_ADC_EOS | CYHAL_ADC_ASYNC_READ_COMPLETE)
33 
34 #define ADC_CAT1_DEFAULT_ACQUISITION_NS (1000u)
35 #define ADC_CAT1_RESOLUTION             (12u)
36 #define ADC_CAT1_REF_INTERNAL_MV        (1200u)
37 
38 #if defined(CONFIG_SOC_FAMILY_INFINEON_CAT1B)
39 #define IFX_ADC_NUM_CHANNELS                                                                       \
40 	ARRAY_SIZE(cyhal_pin_map_adcmic_gpio_adc_in)
41 #else
42 #define IFX_ADC_NUM_CHANNELS CY_SAR_SEQ_NUM_CHANNELS
43 #endif
44 
45 struct ifx_cat1_adc_data {
46 	struct adc_context ctx;
47 	const struct device *dev;
48 	cyhal_adc_t adc_obj;
49 	cyhal_adc_channel_t adc_chan_obj[IFX_ADC_NUM_CHANNELS];
50 	uint16_t *buffer;
51 	uint16_t *repeat_buffer;
52 	uint32_t channels;
53 	uint32_t channels_mask;
54 #ifdef CONFIG_SOC_FAMILY_INFINEON_CAT1B
55 	struct k_work adc_worker_thread;
56 #endif
57 };
58 
59 struct ifx_cat1_adc_config {
60 	uint8_t irq_priority;
61 };
62 
_cyhal_adc_event_callback(void * callback_arg,cyhal_adc_event_t event)63 static void _cyhal_adc_event_callback(void *callback_arg, cyhal_adc_event_t event)
64 {
65 	const struct device *dev = (const struct device *) callback_arg;
66 	struct ifx_cat1_adc_data *data = dev->data;
67 	uint32_t channels = data->channels;
68 	int32_t result;
69 	uint32_t channel_id;
70 
71 	while (channels != 0) {
72 		channel_id = find_lsb_set(channels) - 1;
73 		channels &= ~BIT(channel_id);
74 
75 		result = Cy_SAR_GetResult32(data->adc_chan_obj[channel_id].adc->base,
76 					    data->adc_chan_obj[channel_id].channel_idx);
77 		/* Legacy API for BWC. Convert from signed to unsigned by adding 0x800 to
78 		 * convert the lowest signed 12-bit number to 0x0.
79 		 */
80 		*data->buffer = (uint16_t)(result + 0x800);
81 		data->buffer++;
82 	}
83 
84 	adc_context_on_sampling_done(&data->ctx, dev);
85 
86 	LOG_DBG("%s ISR triggered.", dev->name);
87 }
88 
89 #ifdef CONFIG_SOC_FAMILY_INFINEON_CAT1B
ifx_cat1_adc_worker(struct k_work * adc_worker_thread)90 static void ifx_cat1_adc_worker(struct k_work *adc_worker_thread)
91 {
92 	struct ifx_cat1_adc_data *data =
93 		CONTAINER_OF(adc_worker_thread, struct ifx_cat1_adc_data, adc_worker_thread);
94 
95 	uint32_t channels = data->channels;
96 	int32_t result;
97 	uint32_t channel_id;
98 
99 	while (channels != 0) {
100 		channel_id = find_lsb_set(channels) - 1;
101 		channels &= ~BIT(channel_id);
102 
103 		result = cyhal_adc_read(&data->adc_chan_obj[channel_id]);
104 		/* Legacy API for BWC. Convert from signed to unsigned by adding 0x800 to
105 		 * convert the lowest signed 12-bit number to 0x0.
106 		 */
107 		*data->buffer = (uint16_t)(result + 0x800);
108 		data->buffer++;
109 	}
110 	adc_context_on_sampling_done(&data->ctx, data->dev);
111 }
112 #endif
113 
adc_context_start_sampling(struct adc_context * ctx)114 static void adc_context_start_sampling(struct adc_context *ctx)
115 {
116 	struct ifx_cat1_adc_data *data = CONTAINER_OF(ctx, struct ifx_cat1_adc_data, ctx);
117 
118 	data->repeat_buffer = data->buffer;
119 
120 #if defined(CONFIG_SOC_FAMILY_INFINEON_CAT1B)
121 	k_work_submit(&data->adc_worker_thread);
122 #else
123 	Cy_SAR_StartConvert(data->adc_obj.base, CY_SAR_START_CONVERT_SINGLE_SHOT);
124 #endif
125 }
126 
adc_context_update_buffer_pointer(struct adc_context * ctx,bool repeat_sampling)127 static void adc_context_update_buffer_pointer(struct adc_context *ctx,
128 					      bool repeat_sampling)
129 {
130 	struct ifx_cat1_adc_data *data = CONTAINER_OF(ctx, struct ifx_cat1_adc_data, ctx);
131 
132 	if (repeat_sampling) {
133 		data->buffer = data->repeat_buffer;
134 	}
135 }
136 
ifx_cat1_adc_channel_setup(const struct device * dev,const struct adc_channel_cfg * channel_cfg)137 static int ifx_cat1_adc_channel_setup(const struct device *dev,
138 				      const struct adc_channel_cfg *channel_cfg)
139 {
140 	struct ifx_cat1_adc_data *data = dev->data;
141 	cy_rslt_t result;
142 
143 	cyhal_gpio_t vplus = CYHAL_GET_GPIO(_ADC_PORT, channel_cfg->input_positive);
144 	cyhal_gpio_t vminus = channel_cfg->differential
145 				      ? CYHAL_GET_GPIO(_ADC_PORT, channel_cfg->input_negative)
146 				      : CYHAL_ADC_VNEG;
147 	uint32_t acquisition_ns = ADC_CAT1_DEFAULT_ACQUISITION_NS;
148 
149 	if (channel_cfg->reference != ADC_REF_INTERNAL) {
150 		LOG_ERR("Selected ADC reference is not valid");
151 		return -EINVAL;
152 	}
153 
154 	if (channel_cfg->gain != ADC_GAIN_1) {
155 		LOG_ERR("Selected ADC gain is not valid");
156 		return -EINVAL;
157 	}
158 
159 	if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) {
160 		switch (ADC_ACQ_TIME_UNIT(channel_cfg->acquisition_time)) {
161 		case ADC_ACQ_TIME_MICROSECONDS:
162 			acquisition_ns = ADC_ACQ_TIME_VALUE(channel_cfg->acquisition_time) * 1000;
163 			break;
164 		case ADC_ACQ_TIME_NANOSECONDS:
165 			acquisition_ns = ADC_ACQ_TIME_VALUE(channel_cfg->acquisition_time);
166 			break;
167 		default:
168 			LOG_ERR("Selected ADC acquisition time units is not valid");
169 			return -EINVAL;
170 		}
171 	}
172 
173 	/* ADC channel configuration */
174 	const cyhal_adc_channel_config_t channel_config = {
175 		/* Disable averaging for channel */
176 		.enable_averaging = false,
177 		/* Minimum acquisition time set to 1us */
178 		.min_acquisition_ns = acquisition_ns,
179 		/* Sample channel when ADC performs a scan */
180 		.enabled = true
181 	};
182 
183 	/* Initialize a channel and configure it to scan the input pin(s). */
184 	cyhal_adc_channel_free(&data->adc_chan_obj[channel_cfg->channel_id]);
185 	result = cyhal_adc_channel_init_diff(&data->adc_chan_obj[channel_cfg->channel_id],
186 					     &data->adc_obj, vplus, vminus, &channel_config);
187 	if (result != CY_RSLT_SUCCESS) {
188 		LOG_ERR("ADC channel initialization failed. Error: 0x%08X\n", (unsigned int)result);
189 		return -EIO;
190 	}
191 
192 	data->channels_mask |= BIT(channel_cfg->channel_id);
193 
194 	return 0;
195 }
196 
validate_buffer_size(const struct adc_sequence * sequence)197 static int validate_buffer_size(const struct adc_sequence *sequence)
198 {
199 	int active_channels = 0;
200 	int total_buffer_size;
201 
202 	for (int i = 0; i < IFX_ADC_NUM_CHANNELS; i++) {
203 		if (sequence->channels & BIT(i)) {
204 			active_channels++;
205 		}
206 	}
207 
208 	total_buffer_size = active_channels * sizeof(uint16_t);
209 
210 	if (sequence->options) {
211 		total_buffer_size *= (1 + sequence->options->extra_samplings);
212 	}
213 
214 	if (sequence->buffer_size < total_buffer_size) {
215 		return -ENOMEM;
216 	}
217 
218 	return 0;
219 }
220 
start_read(const struct device * dev,const struct adc_sequence * sequence)221 static int start_read(const struct device *dev,
222 		      const struct adc_sequence *sequence)
223 {
224 	struct ifx_cat1_adc_data *data = dev->data;
225 	uint32_t channels = sequence->channels;
226 	uint32_t unconfigured_channels = channels & ~data->channels_mask;
227 
228 	if (sequence->resolution != ADC_CAT1_RESOLUTION) {
229 		LOG_ERR("Invalid ADC resolution (%d)", sequence->resolution);
230 		return -EINVAL;
231 	}
232 
233 	if (unconfigured_channels != 0) {
234 		LOG_ERR("ADC channel(s) not configured: 0x%08X\n", unconfigured_channels);
235 		return -EINVAL;
236 	}
237 
238 	if (sequence->oversampling) {
239 		LOG_ERR("Oversampling not supported");
240 		return -ENOTSUP;
241 	}
242 
243 	int return_val = validate_buffer_size(sequence);
244 
245 	if (return_val < 0) {
246 		LOG_ERR("Invalid sequence buffer size");
247 		return return_val;
248 	}
249 
250 	data->channels = channels;
251 	data->buffer = sequence->buffer;
252 	adc_context_start_read(&data->ctx, sequence);
253 
254 	return adc_context_wait_for_completion(&data->ctx);
255 }
256 
ifx_cat1_adc_read(const struct device * dev,const struct adc_sequence * sequence)257 static int ifx_cat1_adc_read(const struct device *dev,
258 			     const struct adc_sequence *sequence)
259 {
260 	int ret;
261 	struct ifx_cat1_adc_data *data = dev->data;
262 
263 	adc_context_lock(&data->ctx, false, NULL);
264 	ret = start_read(dev, sequence);
265 	adc_context_release(&data->ctx, ret);
266 	return ret;
267 }
268 
269 #ifdef CONFIG_ADC_ASYNC
ifx_cat1_adc_read_async(const struct device * dev,const struct adc_sequence * sequence,struct k_poll_signal * async)270 static int ifx_cat1_adc_read_async(const struct device *dev,
271 				   const struct adc_sequence *sequence,
272 				   struct k_poll_signal *async)
273 {
274 	int ret;
275 	struct ifx_cat1_adc_data *data = dev->data;
276 
277 	adc_context_lock(&data->ctx, true, async);
278 	ret = start_read(dev, sequence);
279 	adc_context_release(&data->ctx, ret);
280 
281 	return ret;
282 }
283 #endif
284 
ifx_cat1_adc_init(const struct device * dev)285 static int ifx_cat1_adc_init(const struct device *dev)
286 {
287 	struct ifx_cat1_adc_data *data = dev->data;
288 	const struct ifx_cat1_adc_config *config = dev->config;
289 	cy_rslt_t result;
290 
291 	data->dev = dev;
292 
293 	/* Initialize ADC. The ADC block which can connect to the input pin is selected */
294 	result = cyhal_adc_init(&data->adc_obj, CYHAL_GET_GPIO(_ADC_PORT, 0), NULL);
295 	if (result != CY_RSLT_SUCCESS) {
296 		LOG_ERR("ADC initialization failed. Error: 0x%08X\n", (unsigned int)result);
297 		return -EIO;
298 	}
299 
300 	/* Enable ADC Interrupt */
301 	cyhal_adc_enable_event(&data->adc_obj, (cyhal_adc_event_t)ADC_CAT1_EVENTS_MASK,
302 			       config->irq_priority, true);
303 	cyhal_adc_register_callback(&data->adc_obj, _cyhal_adc_event_callback, (void *) dev);
304 
305 	adc_context_unlock_unconditionally(&data->ctx);
306 
307 	return 0;
308 }
309 
310 static DEVICE_API(adc, adc_cat1_driver_api) = {
311 	.channel_setup = ifx_cat1_adc_channel_setup,
312 	.read = ifx_cat1_adc_read,
313 	#ifdef CONFIG_ADC_ASYNC
314 	.read_async = ifx_cat1_adc_read_async,
315 	#endif
316 	.ref_internal = ADC_CAT1_REF_INTERNAL_MV
317 };
318 #ifdef CONFIG_SOC_FAMILY_INFINEON_CAT1B
319 #define ADC_WORKER_THREAD_INIT() .adc_worker_thread = Z_WORK_INITIALIZER(ifx_cat1_adc_worker),
320 #else
321 #define ADC_WORKER_THREAD_INIT()
322 #endif
323 
324 /* Macros for ADC instance declaration */
325 #define INFINEON_CAT1_ADC_INIT(n)                                                                  \
326 	static struct ifx_cat1_adc_data ifx_cat1_adc_data##n = {                                   \
327 		ADC_CONTEXT_INIT_TIMER(ifx_cat1_adc_data##n, ctx),                                 \
328 		ADC_CONTEXT_INIT_LOCK(ifx_cat1_adc_data##n, ctx),                                  \
329 		ADC_CONTEXT_INIT_SYNC(ifx_cat1_adc_data##n, ctx), ADC_WORKER_THREAD_INIT()};       \
330                                                                                                    \
331 	static const struct ifx_cat1_adc_config adc_cat1_cfg_##n = {                               \
332 		.irq_priority = DT_INST_IRQ(n, priority),                                          \
333 	};                                                                                         \
334                                                                                                    \
335 	DEVICE_DT_INST_DEFINE(n, ifx_cat1_adc_init, NULL, &ifx_cat1_adc_data##n,                   \
336 			      &adc_cat1_cfg_##n, POST_KERNEL, CONFIG_ADC_INIT_PRIORITY,            \
337 			      &adc_cat1_driver_api);
338 
339 DT_INST_FOREACH_STATUS_OKAY(INFINEON_CAT1_ADC_INIT)
340