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