1 /*
2  * Copyright (c) 2022 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/drivers/sensor.h>
8 #include <zephyr/drivers/adc/adc_npcx_threshold.h>
9 #include <zephyr/drivers/sensor/adc_cmp_npcx.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/logging/log.h>
12 
13 LOG_MODULE_REGISTER(adc_cmp_npcx, CONFIG_SENSOR_LOG_LEVEL);
14 
15 struct adc_cmp_npcx_data {
16 	/* Work queue to be notified when threshold assertion happens */
17 	struct k_work work;
18 	/* Sensor trigger hanlder to notify user of assetion */
19 	sensor_trigger_handler_t handler;
20 	/* ADC NPCX driver reference */
21 	const struct device *dev;
22 	/* Driver user sensor trigger reference */
23 	const struct sensor_trigger *trigger;
24 };
25 
26 struct adc_cmp_npcx_config {
27 	/*
28 	 * Pointer of ADC device that will be performing measurement, this
29 	 * must be provied by device tree.
30 	 */
31 	const struct device *adc;
32 	/*
33 	 * ADC channel that will be used to measure signal, this must be
34 	 * provided by device tree.
35 	 */
36 	uint8_t chnsel;
37 	/* Threshold selection number assigned during initialization */
38 	uint8_t th_sel;
39 	/* Threshold assert value in millivolts */
40 	uint32_t thr_mv;
41 	/*
42 	 * Condition to be met between measured signal and threshold assert
43 	 * value that will trigger event
44 	 */
45 	enum adc_cmp_npcx_comparison comparison;
46 };
47 
48 #define DT_DRV_COMPAT nuvoton_adc_cmp
49 
50 #define ADC_CMP_NPCX_UNDEFINED		(-1)
adc_cmp_npcx_trigger_work_handler(struct k_work * item)51 static void adc_cmp_npcx_trigger_work_handler(struct k_work *item)
52 {
53 	struct adc_cmp_npcx_data *data =
54 			CONTAINER_OF(item, struct adc_cmp_npcx_data, work);
55 
56 	if (data->handler) {
57 		data->handler(data->dev, data->trigger);
58 	}
59 }
60 
adc_cmp_npcx_init(const struct device * dev)61 static int adc_cmp_npcx_init(const struct device *dev)
62 {
63 	const struct adc_cmp_npcx_config *const config = dev->config;
64 	struct adc_cmp_npcx_data *data = dev->data;
65 	struct adc_npcx_threshold_param param;
66 	int ret;
67 
68 	LOG_DBG("Initialize ADC CMP threshold selection (%d)", config->th_sel);
69 	/* Data must keep device reference for worker handler*/
70 	data->dev = dev;
71 
72 	/* Set ADC channel selection */
73 	param.type = ADC_NPCX_THRESHOLD_PARAM_CHNSEL;
74 	param.val = (uint32_t)config->chnsel;
75 	ret = adc_npcx_threshold_ctrl_set_param(config->adc, config->th_sel,
76 						&param);
77 	if (ret) {
78 		goto init_error;
79 	}
80 
81 	/* Init and set Worker queue to enable notifications */
82 	k_work_init(&data->work, adc_cmp_npcx_trigger_work_handler);
83 	param.type = ADC_NPCX_THRESHOLD_PARAM_WORK;
84 	param.val = (uint32_t)&data->work;
85 	ret = adc_npcx_threshold_ctrl_set_param(config->adc, config->th_sel,
86 						&param);
87 	if (ret) {
88 		goto init_error;
89 	}
90 
91 	/* Set threshold value if set on device tree */
92 	if (config->thr_mv != ADC_CMP_NPCX_UNDEFINED) {
93 		param.type = ADC_NPCX_THRESHOLD_PARAM_THVAL;
94 		/* Convert from millivolts to ADC raw register value */
95 		ret = adc_npcx_threshold_mv_to_thrval(config->adc, config->thr_mv,
96 						&param.val);
97 		if (ret) {
98 			goto init_error;
99 		}
100 
101 		ret = adc_npcx_threshold_ctrl_set_param(config->adc,
102 				config->th_sel,	&param);
103 		if (ret) {
104 			goto init_error;
105 		}
106 	}
107 
108 	/* Set threshold comparison if set on device tree */
109 	if (config->comparison == ADC_CMP_NPCX_GREATER ||
110 	    config->comparison == ADC_CMP_NPCX_LESS_OR_EQUAL) {
111 		param.type = ADC_NPCX_THRESHOLD_PARAM_L_H;
112 		param.val =
113 			config->comparison == ADC_CMP_NPCX_GREATER ?
114 			ADC_NPCX_THRESHOLD_PARAM_L_H_HIGHER :
115 			ADC_NPCX_THRESHOLD_PARAM_L_H_LOWER;
116 		ret = adc_npcx_threshold_ctrl_set_param(config->adc,
117 				config->th_sel,	&param);
118 	}
119 
120 init_error:
121 	if (ret) {
122 		LOG_ERR("Error setting parameter %d - value %d",
123 			(uint32_t)param.type, param.val);
124 	}
125 
126 	return ret;
127 }
128 
adc_cmp_npcx_set_threshold(const struct device * dev,bool is_upper,bool is_mv,uint32_t value)129 static int adc_cmp_npcx_set_threshold(const struct device *dev, bool is_upper,
130 				      bool is_mv, uint32_t value)
131 {
132 	const struct adc_cmp_npcx_config *const config = dev->config;
133 	struct adc_npcx_threshold_param param;
134 	int ret;
135 
136 	param.type = ADC_NPCX_THRESHOLD_PARAM_THVAL;
137 	if (is_mv) {
138 		ret = adc_npcx_threshold_mv_to_thrval(config->adc, value, &param.val);
139 		if (ret) {
140 			return ret;
141 		}
142 	}
143 
144 	ret = adc_npcx_threshold_ctrl_set_param(config->adc,
145 						config->th_sel, &param);
146 	if (ret) {
147 		return ret;
148 	}
149 
150 	param.type = ADC_NPCX_THRESHOLD_PARAM_L_H;
151 	param.val = is_upper ? ADC_NPCX_THRESHOLD_PARAM_L_H_HIGHER :
152 		ADC_NPCX_THRESHOLD_PARAM_L_H_LOWER;
153 
154 	ret = adc_npcx_threshold_ctrl_set_param(config->adc,
155 						config->th_sel, &param);
156 
157 	return ret;
158 }
159 
adc_cmp_npcx_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)160 static int adc_cmp_npcx_attr_set(const struct device *dev,
161 			  enum sensor_channel chan,
162 			  enum sensor_attribute attr,
163 			  const struct sensor_value *val)
164 {
165 	const struct adc_cmp_npcx_config *const config = dev->config;
166 	int ret;
167 
168 	if (chan != SENSOR_CHAN_VOLTAGE) {
169 		return -ENOTSUP;
170 	}
171 
172 	switch ((uint16_t)attr) {
173 	case SENSOR_ATTR_LOWER_THRESH:
174 		__fallthrough;
175 	case SENSOR_ATTR_UPPER_THRESH:
176 		__fallthrough;
177 	case SENSOR_ATTR_LOWER_VOLTAGE_THRESH:
178 		__fallthrough;
179 	case SENSOR_ATTR_UPPER_VOLTAGE_THRESH:
180 		ret = adc_cmp_npcx_set_threshold(dev,
181 			/* Is upper? */
182 			attr == SENSOR_ATTR_UPPER_THRESH ||
183 			(uint16_t)attr == SENSOR_ATTR_UPPER_VOLTAGE_THRESH,
184 			/* Is mV? */
185 			(uint16_t)attr == SENSOR_ATTR_LOWER_VOLTAGE_THRESH ||
186 			(uint16_t)attr == SENSOR_ATTR_UPPER_VOLTAGE_THRESH,
187 			val->val1);
188 		break;
189 	case SENSOR_ATTR_ALERT:
190 		ret = adc_npcx_threshold_ctrl_enable(config->adc,
191 						config->th_sel, !!val->val1);
192 		break;
193 	default:
194 		ret = -ENOTSUP;
195 	}
196 	return ret;
197 }
198 
adc_cmp_npcx_attr_get(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,struct sensor_value * val)199 static int adc_cmp_npcx_attr_get(const struct device *dev,
200 			  enum sensor_channel chan,
201 			  enum sensor_attribute attr,
202 			  struct sensor_value *val)
203 {
204 	return -ENOTSUP;
205 }
206 
adc_cmp_npcx_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)207 static int adc_cmp_npcx_trigger_set(const struct device *dev,
208 			     const struct sensor_trigger *trig,
209 			     sensor_trigger_handler_t handler)
210 {
211 	const struct adc_cmp_npcx_config *const config = dev->config;
212 	struct adc_cmp_npcx_data *data = dev->data;
213 	struct adc_npcx_threshold_param param;
214 
215 	if (trig == NULL || handler == NULL) {
216 		return -EINVAL;
217 	}
218 
219 	if (trig->type != SENSOR_TRIG_THRESHOLD ||
220 	    trig->chan != SENSOR_CHAN_VOLTAGE) {
221 		return -ENOTSUP;
222 	}
223 
224 	data->handler = handler;
225 	data->trigger = trig;
226 
227 	param.type = ADC_NPCX_THRESHOLD_PARAM_WORK;
228 	param.val = (uint32_t)&data->work;
229 	return adc_npcx_threshold_ctrl_set_param(config->adc, config->th_sel,
230 						&param);
231 }
232 
adc_cmp_npcx_sample_fetch(const struct device * dev,enum sensor_channel chan)233 static int adc_cmp_npcx_sample_fetch(const struct device *dev,
234 			      enum sensor_channel chan)
235 {
236 	return -ENOTSUP;
237 }
238 
adc_cmp_npcx_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)239 static int adc_cmp_npcx_channel_get(const struct device *dev,
240 			     enum sensor_channel chan,
241 			     struct sensor_value *val)
242 {
243 	return -ENOTSUP;
244 }
245 
246 static const struct sensor_driver_api adc_cmp_npcx_api = {
247 	.attr_set = adc_cmp_npcx_attr_set,
248 	.attr_get = adc_cmp_npcx_attr_get,
249 	.trigger_set = adc_cmp_npcx_trigger_set,
250 	.sample_fetch = adc_cmp_npcx_sample_fetch,
251 	.channel_get = adc_cmp_npcx_channel_get,
252 };
253 
254 #define NPCX_ADC_CMP_INIT(inst)							\
255 	static struct adc_cmp_npcx_data adc_cmp_npcx_data_##inst;		\
256 	static const struct adc_cmp_npcx_config adc_cmp_npcx_config_##inst = {	\
257 		.adc = DEVICE_DT_GET(DT_INST_IO_CHANNELS_CTLR(inst)),		\
258 		.chnsel = DT_INST_IO_CHANNELS_INPUT(inst),			\
259 		.th_sel = DT_INST_STRING_TOKEN_OR(inst, thr_sel, inst),		\
260 		.thr_mv = DT_INST_PROP_OR(inst, threshold_mv,			\
261 			ADC_CMP_NPCX_UNDEFINED),				\
262 		.comparison = DT_INST_STRING_TOKEN_OR(inst,			\
263 			comparison, ADC_CMP_NPCX_UNDEFINED)			\
264 	};									\
265 	SENSOR_DEVICE_DT_INST_DEFINE(inst, adc_cmp_npcx_init, NULL,		\
266 			      &adc_cmp_npcx_data_##inst,			\
267 			      &adc_cmp_npcx_config_##inst, POST_KERNEL,		\
268 			      CONFIG_SENSOR_INIT_PRIORITY,			\
269 			      &adc_cmp_npcx_api);				\
270 	BUILD_ASSERT(DT_INST_STRING_TOKEN_OR(inst, thr_sel, inst) <		\
271 		     DT_PROP(DT_INST_IO_CHANNELS_CTLR(inst), threshold_count),	\
272 		     "Exceed the number of threshold detectors adc supports");
273 DT_INST_FOREACH_STATUS_OKAY(NPCX_ADC_CMP_INIT)
274