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