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