1 /*
2  * Copyright (c) 2022 ITE Technology Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT ite_it8xxx2_vcmp
8 
9 #include <zephyr/device.h>
10 #include <zephyr/devicetree/io-channels.h>
11 #include <zephyr/drivers/adc.h>
12 #include <zephyr/drivers/sensor.h>
13 #include <zephyr/drivers/sensor/it8xxx2_vcmp.h>
14 #include <zephyr/dt-bindings/dt-util.h>
15 #include <zephyr/dt-bindings/sensor/it8xxx2_vcmp.h>
16 #include <errno.h>
17 #include <soc.h>
18 #include <soc_dt.h>
19 
20 #include <zephyr/logging/log.h>
21 LOG_MODULE_REGISTER(vcmp_ite_it8xxx2, CONFIG_SENSOR_LOG_LEVEL);
22 
23 #define VCMP_REG_MASK		0x7
24 #define VCMP_RESOLUTION		BIT(10)
25 #ifdef CONFIG_ADC_IT8XXX2_VOL_FULL_SCALE
26 #define VCMP_MAX_MVOLT		3300
27 #else
28 #define VCMP_MAX_MVOLT		3000
29 #endif
30 
31 /* Device config */
32 struct vcmp_it8xxx2_config {
33 	/* Voltage comparator x control register */
34 	volatile uint8_t *reg_vcmpxctl;
35 	/* Voltage comparator x channel select MSB register */
36 	volatile uint8_t *reg_vcmpxcselm;
37 	/* Voltage comparator scan period register */
38 	volatile uint8_t *reg_vcmpscp;
39 	/* Voltage comparator x threshold data buffer MSB register */
40 	volatile uint8_t *reg_vcmpxthrdatm;
41 	/* Voltage comparator x threshold data buffer LSB register */
42 	volatile uint8_t *reg_vcmpxthrdatl;
43 	/* Voltage comparator status register */
44 	volatile uint8_t *reg_vcmpsts;
45 	/* Voltage comparator status 2 register */
46 	volatile uint8_t *reg_vcmpsts2;
47 	/* Voltage comparator module irq */
48 	int irq;
49 	/* Voltage comparator channel */
50 	int vcmp_ch;
51 	/* Scan period for "all voltage comparator channel" */
52 	int scan_period;
53 	/*
54 	 * Determines the condition between ADC data and threshold_mv
55 	 * that will trigger voltage comparator interrupt.
56 	 */
57 	int comparison;
58 	/* Threshold assert value in mv */
59 	int threshold_mv;
60 	/* Pointer of ADC device that will be performing measurement */
61 	const struct device *adc;
62 };
63 
64 /* Driver data */
65 struct vcmp_it8xxx2_data {
66 	/* ADC channel config */
67 	struct adc_channel_cfg adc_ch_cfg;
68 	/* Work queue to be notified when threshold assertion happens */
69 	struct k_work work;
70 	/* Sensor trigger hanlder to notify user of assetion */
71 	sensor_trigger_handler_t handler;
72 	const struct sensor_trigger *trig;
73 	/* Pointer of voltage comparator device */
74 	const struct device *vcmp;
75 };
76 
77 /* Voltage comparator work queue address */
78 static uint32_t vcmp_work_addr[VCMP_CHANNEL_CNT];
79 #ifdef CONFIG_VCMP_IT8XXX2_WORKQUEUE
80 /*
81  * Pointer of work queue thread to be notified when threshold assertion
82  * occurs.
83  */
84 struct k_work_q *work_q;
85 #endif
86 
clear_vcmp_status(const struct device * dev,int vcmp_ch)87 static void clear_vcmp_status(const struct device *dev, int vcmp_ch)
88 {
89 	const struct vcmp_it8xxx2_config *const config = dev->config;
90 	volatile uint8_t *reg_vcmpsts = config->reg_vcmpsts;
91 	volatile uint8_t *reg_vcmpsts2 = config->reg_vcmpsts2;
92 
93 	/* W/C voltage comparator specific channel interrupt status */
94 	if (vcmp_ch <= VCMP_CHANNEL_2) {
95 		*reg_vcmpsts = BIT(vcmp_ch);
96 	} else {
97 		*reg_vcmpsts2 = BIT(vcmp_ch - VCMP_CHANNEL_3);
98 	}
99 }
100 
vcmp_enable(const struct device * dev,int enable)101 static void vcmp_enable(const struct device *dev, int enable)
102 {
103 	const struct vcmp_it8xxx2_config *const config = dev->config;
104 	volatile uint8_t *reg_vcmpxctl = config->reg_vcmpxctl;
105 
106 	if (enable) {
107 		/* Enable voltage comparator specific channel interrupt */
108 		*reg_vcmpxctl |= IT8XXX2_VCMP_CMPINTEN;
109 		/* Start voltage comparator specific channel */
110 		*reg_vcmpxctl |= IT8XXX2_VCMP_CMPEN;
111 	} else {
112 		/* Stop voltage comparator specific channel */
113 		*reg_vcmpxctl &= ~IT8XXX2_VCMP_CMPEN;
114 		/* Disable voltage comparator specific channel interrupt */
115 		*reg_vcmpxctl &= ~IT8XXX2_VCMP_CMPINTEN;
116 	}
117 }
118 
vcmp_set_threshold(const struct device * dev,enum sensor_attribute attr,int32_t reg_val)119 static int vcmp_set_threshold(const struct device *dev,
120 			      enum sensor_attribute attr,
121 			      int32_t reg_val)
122 {
123 	const struct vcmp_it8xxx2_config *const config = dev->config;
124 	volatile uint8_t *reg_vcmpxthrdatm = config->reg_vcmpxthrdatm;
125 	volatile uint8_t *reg_vcmpxthrdatl = config->reg_vcmpxthrdatl;
126 	volatile uint8_t *reg_vcmpxctl = config->reg_vcmpxctl;
127 
128 	if (reg_val >= VCMP_RESOLUTION) {
129 		LOG_ERR("Vcmp%d threshold only support 10-bits", config->vcmp_ch);
130 		return -ENOTSUP;
131 	}
132 
133 	/* Set threshold raw value */
134 	*reg_vcmpxthrdatl = (uint8_t)(reg_val & 0xff);
135 	*reg_vcmpxthrdatm = (uint8_t)((reg_val >> 8) & 0xff);
136 
137 	/* Set lower or higher threshold */
138 	if ((attr == SENSOR_ATTR_UPPER_THRESH) ||
139 	    (attr == (uint16_t) SENSOR_ATTR_UPPER_VOLTAGE_THRESH)) {
140 		*reg_vcmpxctl |= IT8XXX2_VCMP_GREATER_THRESHOLD;
141 	} else {
142 		*reg_vcmpxctl &= ~IT8XXX2_VCMP_GREATER_THRESHOLD;
143 	}
144 
145 	return 0;
146 }
147 
it8xxx2_vcmp_trigger_work_handler(struct k_work * item)148 static void it8xxx2_vcmp_trigger_work_handler(struct k_work *item)
149 {
150 	struct vcmp_it8xxx2_data *data =
151 			CONTAINER_OF(item, struct vcmp_it8xxx2_data, work);
152 
153 	if (data->handler) {
154 		data->handler(data->vcmp, data->trig);
155 	}
156 }
157 
vcmp_ite_it8xxx2_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)158 static int vcmp_ite_it8xxx2_attr_set(const struct device *dev,
159 				     enum sensor_channel chan,
160 				     enum sensor_attribute attr,
161 				     const struct sensor_value *val)
162 {
163 	const struct vcmp_it8xxx2_config *const config = dev->config;
164 	int32_t reg_val, ret = 0;
165 
166 	if (chan != SENSOR_CHAN_VOLTAGE) {
167 		return -ENOTSUP;
168 	}
169 
170 	switch ((uint16_t)attr) {
171 	case SENSOR_ATTR_LOWER_THRESH:
172 	case SENSOR_ATTR_UPPER_THRESH:
173 		ret = vcmp_set_threshold(dev, attr, val->val1);
174 		break;
175 	case SENSOR_ATTR_LOWER_VOLTAGE_THRESH:
176 	case SENSOR_ATTR_UPPER_VOLTAGE_THRESH:
177 		/*
178 		 * Tranfrom threshold from mv to raw
179 		 * NOTE: CMPXTHRDAT[9:0] = threshold(mv) * 1024 / 3000(mv)
180 		 */
181 		reg_val = (val->val1 * VCMP_RESOLUTION / VCMP_MAX_MVOLT);
182 		ret = vcmp_set_threshold(dev, attr, reg_val);
183 		break;
184 	case SENSOR_ATTR_ALERT:
185 		if (!!val->val1) {
186 			clear_vcmp_status(dev, config->vcmp_ch);
187 			vcmp_enable(dev, 1);
188 		} else {
189 			vcmp_enable(dev, 0);
190 			clear_vcmp_status(dev, config->vcmp_ch);
191 		}
192 
193 		break;
194 	default:
195 		ret = -ENOTSUP;
196 	}
197 
198 	return ret;
199 }
200 
vcmp_ite_it8xxx2_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)201 static int vcmp_ite_it8xxx2_trigger_set(const struct device *dev,
202 					const struct sensor_trigger *trig,
203 					sensor_trigger_handler_t handler)
204 {
205 	const struct vcmp_it8xxx2_config *const config = dev->config;
206 	struct vcmp_it8xxx2_data *const data = dev->data;
207 
208 	if (trig->type != SENSOR_TRIG_THRESHOLD ||
209 	    trig->chan != SENSOR_CHAN_VOLTAGE) {
210 		return -ENOTSUP;
211 	}
212 
213 	data->handler = handler;
214 	data->trig = trig;
215 
216 	vcmp_work_addr[config->vcmp_ch] = (uint32_t) &data->work;
217 
218 	return 0;
219 }
220 
vcmp_it8xxx2_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)221 static int vcmp_it8xxx2_channel_get(const struct device *dev,
222 				    enum sensor_channel chan,
223 				    struct sensor_value *val)
224 {
225 	const struct vcmp_it8xxx2_config *const config = dev->config;
226 
227 	if (chan != SENSOR_CHAN_VOLTAGE) {
228 		return -ENOTSUP;
229 	}
230 
231 	/*
232 	 * It8xxx2 adc and comparator module read automatically, according to
233 	 * {ADCCTS1, ADCCTS2} and VCMPSCP register setting.
234 	 */
235 	val->val1 = config->vcmp_ch;
236 
237 	return 0;
238 }
239 
240 /*
241  * All voltage comparator channels share one irq interrupt, so we
242  * need to handle all channels, when the interrupt fired.
243  */
vcmp_it8xxx2_isr(const struct device * dev)244 static void vcmp_it8xxx2_isr(const struct device *dev)
245 {
246 	const struct vcmp_it8xxx2_config *const config = dev->config;
247 	volatile uint8_t *reg_vcmpsts = config->reg_vcmpsts;
248 	volatile uint8_t *reg_vcmpsts2 = config->reg_vcmpsts2;
249 	int idx, status;
250 
251 	/* Find out which voltage comparator triggered */
252 	status = *reg_vcmpsts & VCMP_REG_MASK;
253 	status |= (*reg_vcmpsts2 & VCMP_REG_MASK) << 3;
254 
255 	for (idx = VCMP_CHANNEL_0; idx < VCMP_CHANNEL_CNT; idx++) {
256 		if (status & BIT(idx)) {
257 			/* Call triggered channel callback function in work queue */
258 			if (vcmp_work_addr[idx]) {
259 #ifdef CONFIG_VCMP_IT8XXX2_WORKQUEUE
260 				k_work_submit_to_queue(work_q,
261 						       (struct k_work *) vcmp_work_addr[idx]);
262 #else
263 				k_work_submit((struct k_work *) vcmp_work_addr[idx]);
264 #endif
265 			}
266 			/* W/C voltage comparator specific channel interrupt status */
267 			clear_vcmp_status(dev, idx);
268 		}
269 	}
270 
271 	/* W/C voltage comparator irq interrupt status */
272 	ite_intc_isr_clear(config->irq);
273 }
274 
vcmp_it8xxx2_init(const struct device * dev)275 static int vcmp_it8xxx2_init(const struct device *dev)
276 {
277 	const struct vcmp_it8xxx2_config *const config = dev->config;
278 	struct vcmp_it8xxx2_data *const data = dev->data;
279 	volatile uint8_t *reg_vcmpxctl = config->reg_vcmpxctl;
280 	volatile uint8_t *reg_vcmpxcselm = config->reg_vcmpxcselm;
281 	volatile uint8_t *reg_vcmpscp = config->reg_vcmpscp;
282 
283 	/* Disable voltage comparator specific channel before init */
284 	vcmp_enable(dev, 0);
285 
286 	/*
287 	 * ADC channel signal output to voltage comparator,
288 	 * so we need to set ADC channel to alternate mode first.
289 	 */
290 	if (!device_is_ready(config->adc)) {
291 		LOG_ERR("ADC device not ready");
292 		return -ENODEV;
293 	}
294 	adc_channel_setup(config->adc, &data->adc_ch_cfg);
295 
296 	/* Select which ADC channel output voltage into comparator */
297 	if (data->adc_ch_cfg.channel_id <= 7) {
298 		/* ADC channel 0~7 map to value 0x0~0x7 */
299 		*reg_vcmpxctl |= data->adc_ch_cfg.channel_id & VCMP_REG_MASK;
300 		*reg_vcmpxcselm &= ~IT8XXX2_VCMP_VCMPXCSELM;
301 	} else {
302 		/* ADC channel 13~16 map to value 0x8~0xb */
303 		*reg_vcmpxctl |= (data->adc_ch_cfg.channel_id - 5) & VCMP_REG_MASK;
304 		*reg_vcmpxcselm |= IT8XXX2_VCMP_VCMPXCSELM;
305 	}
306 
307 	/* Set minimum scan period for "all voltage comparator channel" */
308 	if (*reg_vcmpscp > config->scan_period) {
309 		*reg_vcmpscp = config->scan_period;
310 	}
311 
312 	/* Data must keep device reference for worker handler */
313 	data->vcmp = dev;
314 
315 	/* Init and set work item to enable notifications */
316 	k_work_init(&data->work, it8xxx2_vcmp_trigger_work_handler);
317 	vcmp_work_addr[config->vcmp_ch] = (uint32_t) &data->work;
318 
319 	/* Set threshold and comparison if set on device tree */
320 	if ((config->threshold_mv != IT8XXX2_VCMP_UNDEFINED) &&
321 	     (config->comparison != IT8XXX2_VCMP_UNDEFINED)) {
322 		enum sensor_attribute attr;
323 		struct sensor_value val;
324 
325 		if (config->comparison == IT8XXX2_VCMP_LESS_OR_EQUAL) {
326 			attr = SENSOR_ATTR_LOWER_VOLTAGE_THRESH;
327 		} else {
328 			attr = SENSOR_ATTR_UPPER_VOLTAGE_THRESH;
329 		}
330 
331 		val.val1 = config->threshold_mv;
332 		val.val2 = 0;
333 
334 		vcmp_ite_it8xxx2_attr_set(dev, SENSOR_CHAN_VOLTAGE, attr, &val);
335 	}
336 
337 	/*
338 	 * All voltage comparator channels share one irq interrupt,
339 	 * so if the irq is enabled before, we needn't to enable again.
340 	 * And we will figure out the triggered channel in vcmp_it8xxx2_isr().
341 	 */
342 	if (!irq_is_enabled(config->irq)) {
343 		ite_intc_isr_clear(config->irq);
344 
345 		irq_connect_dynamic(config->irq, 0,
346 				    (void (*)(const void *))vcmp_it8xxx2_isr,
347 				    (const void *)dev, 0);
348 
349 		irq_enable(config->irq);
350 	}
351 
352 	return 0;
353 }
354 
355 static DEVICE_API(sensor, vcmp_ite_it8xxx2_api) = {
356 	.attr_set = vcmp_ite_it8xxx2_attr_set,
357 	.trigger_set = vcmp_ite_it8xxx2_trigger_set,
358 	.channel_get = vcmp_it8xxx2_channel_get,
359 };
360 
361 #ifdef CONFIG_VCMP_IT8XXX2_WORKQUEUE
362 struct k_work_q vcmp_it8xxx2_work_q;
363 
364 static K_KERNEL_STACK_DEFINE(vcmp_it8xxx2_work_q_stack,
365 			     CONFIG_VCMP_IT8XXX2_WORKQUEUE_STACK_SIZE);
366 
vcmp_it8xxx2_init_work_q(void)367 static int vcmp_it8xxx2_init_work_q(void)
368 {
369 	struct k_work_queue_config cfg = {
370 		.name = "vcmp_work",
371 		.no_yield = false,
372 	};
373 
374 	k_work_queue_start(&vcmp_it8xxx2_work_q,
375 			   vcmp_it8xxx2_work_q_stack,
376 			   K_KERNEL_STACK_SIZEOF(vcmp_it8xxx2_work_q_stack),
377 			   CONFIG_VCMP_IT8XXX2_WORKQUEUE_PRIORITY, &cfg);
378 
379 	work_q = &vcmp_it8xxx2_work_q;
380 	return 0;
381 }
382 
383 SYS_INIT(vcmp_it8xxx2_init_work_q, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY);
384 #endif
385 
386 #define VCMP_IT8XXX2_INIT(inst)								\
387 	static const struct vcmp_it8xxx2_config vcmp_it8xxx2_cfg_##inst = {		\
388 		.reg_vcmpxctl = (uint8_t *)DT_INST_REG_ADDR_BY_IDX(inst, 0),		\
389 		.reg_vcmpxcselm = (uint8_t *)DT_INST_REG_ADDR_BY_IDX(inst, 1),		\
390 		.reg_vcmpscp = (uint8_t *)DT_INST_REG_ADDR_BY_IDX(inst, 2),		\
391 		.reg_vcmpxthrdatm = (uint8_t *)DT_INST_REG_ADDR_BY_IDX(inst, 3),	\
392 		.reg_vcmpxthrdatl = (uint8_t *)DT_INST_REG_ADDR_BY_IDX(inst, 4),	\
393 		.reg_vcmpsts = (uint8_t *)DT_INST_REG_ADDR_BY_IDX(inst, 5),		\
394 		.reg_vcmpsts2 = (uint8_t *)DT_INST_REG_ADDR_BY_IDX(inst, 6),		\
395 		.irq = DT_INST_IRQN(inst),						\
396 		.vcmp_ch = DT_INST_PROP(inst, vcmp_ch),					\
397 		.scan_period = DT_INST_PROP(inst, scan_period),				\
398 		.comparison = DT_INST_PROP(inst, comparison),				\
399 		.threshold_mv = DT_INST_PROP(inst, threshold_mv),			\
400 		.adc = DEVICE_DT_GET(DT_INST_IO_CHANNELS_CTLR(inst)),			\
401 	};										\
402 											\
403 	static struct vcmp_it8xxx2_data vcmp_it8xxx2_data_##inst = {			\
404 		.adc_ch_cfg.gain = ADC_GAIN_1,						\
405 		.adc_ch_cfg.reference = ADC_REF_INTERNAL,				\
406 		.adc_ch_cfg.acquisition_time = ADC_ACQ_TIME_DEFAULT,			\
407 		.adc_ch_cfg.channel_id = (uint8_t)DT_INST_IO_CHANNELS_INPUT(inst),	\
408 	};										\
409 											\
410 	SENSOR_DEVICE_DT_INST_DEFINE(inst,						\
411 			      vcmp_it8xxx2_init,					\
412 			      NULL,							\
413 			      &vcmp_it8xxx2_data_##inst,				\
414 			      &vcmp_it8xxx2_cfg_##inst,					\
415 			      POST_KERNEL,						\
416 			      CONFIG_VCMP_IT8XXX2_INIT_PRIORITY,			\
417 			      &vcmp_ite_it8xxx2_api);
418 
419 DT_INST_FOREACH_STATUS_OKAY(VCMP_IT8XXX2_INIT)
420 #ifdef CONFIG_VCMP_IT8XXX2_WORKQUEUE
421 BUILD_ASSERT(CONFIG_SENSOR_INIT_PRIORITY < CONFIG_VCMP_IT8XXX2_INIT_PRIORITY,
422 	"CONFIG_SENSOR_INIT_PRIORITY must be less than CONFIG_VCMP_IT8XXX2_INIT_PRIORITY");
423 #endif
424