1 /*
2  * Copyright (c) 2023 Grinn
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #define DT_DRV_COMPAT adi_ad559x_adc
7 
8 #include <zephyr/drivers/adc.h>
9 #include <zephyr/kernel.h>
10 #include <zephyr/sys/byteorder.h>
11 
12 #include <zephyr/drivers/mfd/ad559x.h>
13 
14 #define ADC_CONTEXT_USES_KERNEL_TIMER
15 #include "adc_context.h"
16 
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_REGISTER(adc_ad559x, CONFIG_ADC_LOG_LEVEL);
19 
20 #define AD559X_ADC_RD_POINTER_SIZE 1
21 #define AD559X_ADC_RD_POINTER      0x40
22 
23 #define AD559X_ADC_RESOLUTION 12U
24 #define AD559X_ADC_VREF_MV 2500U
25 
26 #define AD559X_ADC_RES_IND_BIT BIT(15)
27 #define AD559X_ADC_RES_CHAN_MASK GENMASK(14, 12)
28 #define AD559X_ADC_RES_VAL_MASK GENMASK(11, 0)
29 
30 struct adc_ad559x_config {
31 	const struct device *mfd_dev;
32 	bool double_input_range;
33 };
34 
35 struct adc_ad559x_data {
36 	struct adc_context ctx;
37 	const struct device *dev;
38 	uint8_t adc_conf;
39 	uint16_t *buffer;
40 	uint16_t *repeat_buffer;
41 	uint8_t channels;
42 	struct k_thread thread;
43 	struct k_sem sem;
44 
45 	K_KERNEL_STACK_MEMBER(stack, CONFIG_ADC_AD559X_ACQUISITION_THREAD_STACK_SIZE);
46 };
47 
adc_ad559x_channel_setup(const struct device * dev,const struct adc_channel_cfg * channel_cfg)48 static int adc_ad559x_channel_setup(const struct device *dev,
49 				    const struct adc_channel_cfg *channel_cfg)
50 {
51 	const struct adc_ad559x_config *config = dev->config;
52 	struct adc_ad559x_data *data = dev->data;
53 
54 	if (channel_cfg->channel_id >= AD559X_PIN_MAX) {
55 		LOG_ERR("invalid channel id %d", channel_cfg->channel_id);
56 		return -EINVAL;
57 	}
58 
59 	data->adc_conf |= BIT(channel_cfg->channel_id);
60 
61 	return mfd_ad559x_write_reg(config->mfd_dev, AD559X_REG_ADC_CONFIG, data->adc_conf);
62 }
63 
adc_ad559x_validate_buffer_size(const struct device * dev,const struct adc_sequence * sequence)64 static int adc_ad559x_validate_buffer_size(const struct device *dev,
65 					   const struct adc_sequence *sequence)
66 {
67 	uint8_t channels;
68 	size_t needed;
69 
70 	channels = POPCOUNT(sequence->channels);
71 	needed = channels * sizeof(uint16_t);
72 
73 	if (sequence->buffer_size < needed) {
74 		return -ENOMEM;
75 	}
76 
77 	return 0;
78 }
79 
adc_ad559x_start_read(const struct device * dev,const struct adc_sequence * sequence)80 static int adc_ad559x_start_read(const struct device *dev, const struct adc_sequence *sequence)
81 {
82 	struct adc_ad559x_data *data = dev->data;
83 	int ret;
84 
85 	if (sequence->resolution != AD559X_ADC_RESOLUTION) {
86 		LOG_ERR("invalid resolution %d", sequence->resolution);
87 		return -EINVAL;
88 	}
89 
90 	if (find_msb_set(sequence->channels) > AD559X_PIN_MAX) {
91 		LOG_ERR("invalid channels in mask: 0x%08x", sequence->channels);
92 		return -EINVAL;
93 	}
94 
95 	ret = adc_ad559x_validate_buffer_size(dev, sequence);
96 	if (ret < 0) {
97 		LOG_ERR("insufficient buffer size");
98 		return ret;
99 	}
100 
101 	data->buffer = sequence->buffer;
102 	adc_context_start_read(&data->ctx, sequence);
103 
104 	return adc_context_wait_for_completion(&data->ctx);
105 }
106 
adc_ad559x_read_channel(const struct device * dev,uint8_t channel,uint16_t * result)107 static int adc_ad559x_read_channel(const struct device *dev, uint8_t channel, uint16_t *result)
108 {
109 	const struct adc_ad559x_config *config = dev->config;
110 	uint16_t val;
111 	uint8_t conv_channel;
112 	int ret;
113 
114 	/* Select channel */
115 	ret = mfd_ad559x_write_reg(config->mfd_dev, AD559X_REG_SEQ_ADC, BIT(channel));
116 	if (ret < 0) {
117 		return ret;
118 	}
119 
120 	if (mfd_ad559x_has_pointer_byte_map(config->mfd_dev)) {
121 		/* Start readback */
122 		val = AD559X_ADC_RD_POINTER;
123 		ret = mfd_ad559x_write_raw(config->mfd_dev, (uint8_t *)&val,
124 					   AD559X_ADC_RD_POINTER_SIZE);
125 		if (ret < 0) {
126 			return ret;
127 		}
128 
129 		/* Read channel */
130 		ret = mfd_ad559x_read_raw(config->mfd_dev, (uint8_t *)&val, sizeof(val));
131 		if (ret < 0) {
132 			return ret;
133 		}
134 	} else {
135 		/*
136 		 * Invalid data:
137 		 * See Figure 46. Single-Channel ADC Conversion Sequence.
138 		 * The first conversion result always returns invalid data.
139 		 */
140 		(void)mfd_ad559x_read_raw(config->mfd_dev, (uint8_t *)&val, sizeof(val));
141 
142 		ret = mfd_ad559x_read_raw(config->mfd_dev, (uint8_t *)&val, sizeof(val));
143 		if (ret < 0) {
144 			return ret;
145 		}
146 	}
147 
148 	val = sys_be16_to_cpu(val);
149 
150 	/*
151 	 * Invalid data:
152 	 * See AD5592 "ADC section" in "Theory of operation" chapter.
153 	 * Valid ADC result has MSB bit set to 0.
154 	 */
155 	if ((val & AD559X_ADC_RES_IND_BIT) != 0) {
156 		return -EAGAIN;
157 	}
158 
159 	/*
160 	 * Invalid channel converted:
161 	 * See AD5592 "ADC section" in "Theory of operation" chapter.
162 	 * Conversion result contains channel number which should match requested channel.
163 	 */
164 	conv_channel = FIELD_GET(AD559X_ADC_RES_CHAN_MASK, val);
165 	if (conv_channel != channel) {
166 		return -EIO;
167 	}
168 
169 	*result = val & AD559X_ADC_RES_VAL_MASK;
170 
171 	return 0;
172 }
173 
adc_context_start_sampling(struct adc_context * ctx)174 static void adc_context_start_sampling(struct adc_context *ctx)
175 {
176 	struct adc_ad559x_data *data = CONTAINER_OF(ctx, struct adc_ad559x_data, ctx);
177 
178 	data->channels = ctx->sequence.channels;
179 	data->repeat_buffer = data->buffer;
180 
181 	k_sem_give(&data->sem);
182 }
183 
adc_context_update_buffer_pointer(struct adc_context * ctx,bool repeat_sampling)184 static void adc_context_update_buffer_pointer(struct adc_context *ctx, bool repeat_sampling)
185 {
186 	struct adc_ad559x_data *data = CONTAINER_OF(ctx, struct adc_ad559x_data, ctx);
187 
188 	if (repeat_sampling) {
189 		data->buffer = data->repeat_buffer;
190 	}
191 }
192 
adc_ad559x_acquisition_thread(struct adc_ad559x_data * data)193 static void adc_ad559x_acquisition_thread(struct adc_ad559x_data *data)
194 {
195 	uint16_t result;
196 	uint8_t channel;
197 	int ret;
198 
199 	while (true) {
200 		k_sem_take(&data->sem, K_FOREVER);
201 
202 		while (data->channels != 0) {
203 			channel = find_lsb_set(data->channels) - 1;
204 
205 			ret = adc_ad559x_read_channel(data->dev, channel, &result);
206 			if (ret < 0) {
207 				LOG_ERR("failed to read channel %d (ret %d)", channel, ret);
208 				adc_context_complete(&data->ctx, ret);
209 				break;
210 			}
211 
212 			*data->buffer++ = result;
213 			WRITE_BIT(data->channels, channel, 0);
214 		}
215 
216 		adc_context_on_sampling_done(&data->ctx, data->dev);
217 	}
218 }
219 
adc_ad559x_read_async(const struct device * dev,const struct adc_sequence * sequence,struct k_poll_signal * async)220 static int adc_ad559x_read_async(const struct device *dev, const struct adc_sequence *sequence,
221 				 struct k_poll_signal *async)
222 {
223 	struct adc_ad559x_data *data = dev->data;
224 	int ret;
225 
226 	adc_context_lock(&data->ctx, async ? true : false, async);
227 	ret = adc_ad559x_start_read(dev, sequence);
228 	adc_context_release(&data->ctx, ret);
229 
230 	return ret;
231 }
232 
adc_ad559x_read(const struct device * dev,const struct adc_sequence * sequence)233 static int adc_ad559x_read(const struct device *dev, const struct adc_sequence *sequence)
234 {
235 	return adc_ad559x_read_async(dev, sequence, NULL);
236 }
237 
adc_ad559x_init(const struct device * dev)238 static int adc_ad559x_init(const struct device *dev)
239 {
240 	const struct adc_ad559x_config *config = dev->config;
241 	struct adc_ad559x_data *data = dev->data;
242 	k_tid_t tid;
243 	int ret;
244 	uint16_t reg_val;
245 
246 	if (!device_is_ready(config->mfd_dev)) {
247 		return -ENODEV;
248 	}
249 
250 	ret = mfd_ad559x_read_reg(config->mfd_dev, AD559X_REG_GEN_CTRL, 0, &reg_val);
251 	if (ret < 0) {
252 		return ret;
253 	}
254 
255 	if (config->double_input_range) {
256 		reg_val |= AD559X_ADC_RANGE;
257 	} else {
258 		reg_val &= ~AD559X_ADC_RANGE;
259 	}
260 
261 	ret = mfd_ad559x_write_reg(config->mfd_dev, AD559X_REG_GEN_CTRL, reg_val);
262 	if (ret < 0) {
263 		return ret;
264 	}
265 
266 	ret = mfd_ad559x_write_reg(config->mfd_dev, AD559X_REG_PD_REF_CTRL, AD559X_EN_REF);
267 	if (ret < 0) {
268 		return ret;
269 	}
270 
271 	data->dev = dev;
272 
273 	k_sem_init(&data->sem, 0, 1);
274 	adc_context_init(&data->ctx);
275 
276 	tid = k_thread_create(&data->thread, data->stack,
277 			K_KERNEL_STACK_SIZEOF(data->stack),
278 			(k_thread_entry_t)adc_ad559x_acquisition_thread, data, NULL, NULL,
279 			CONFIG_ADC_AD559X_ACQUISITION_THREAD_PRIO, 0, K_NO_WAIT);
280 
281 	if (IS_ENABLED(CONFIG_THREAD_NAME)) {
282 		ret = k_thread_name_set(tid, "adc_ad559x");
283 		if (ret < 0) {
284 			return ret;
285 		}
286 	}
287 
288 	adc_context_unlock_unconditionally(&data->ctx);
289 
290 	return 0;
291 }
292 
293 #ifdef CONFIG_ADC_ASYNC
294 #define ADC_AD559X_ASYNC() .read_async = adc_ad559x_read_async,
295 #else
296 #define ADC_AD559X_ASYNC()
297 #endif
298 
299 #define ADC_AD559X_DRIVER_API(inst)                                                                \
300 	static DEVICE_API(adc, adc_ad559x_api##inst) = {                                           \
301 		.channel_setup = adc_ad559x_channel_setup,                                         \
302 		.read = adc_ad559x_read,                                                           \
303 		.ref_internal = AD559X_ADC_VREF_MV * (1 + DT_INST_PROP(inst, double_input_range)), \
304 		ADC_AD559X_ASYNC()}
305 
306 #define ADC_AD559X_DEFINE(inst)                                                                    \
307 	ADC_AD559X_DRIVER_API(inst);                                                               \
308                                                                                                    \
309 	static const struct adc_ad559x_config adc_ad559x_config##inst = {                          \
310 		.mfd_dev = DEVICE_DT_GET(DT_INST_PARENT(inst)),                                    \
311 		.double_input_range = DT_INST_PROP(inst, double_input_range),                      \
312 	};                                                                                         \
313                                                                                                    \
314 	static struct adc_ad559x_data adc_ad559x_data##inst;                                       \
315                                                                                                    \
316 	DEVICE_DT_INST_DEFINE(inst, adc_ad559x_init, NULL, &adc_ad559x_data##inst,                 \
317 			      &adc_ad559x_config##inst, POST_KERNEL, CONFIG_MFD_INIT_PRIORITY,     \
318 			      &adc_ad559x_api##inst);
319 
320 DT_INST_FOREACH_STATUS_OKAY(ADC_AD559X_DEFINE)
321