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 ¶m);
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 ¶m);
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 ¶m.val);
97 if (ret) {
98 goto init_error;
99 }
100
101 ret = adc_npcx_threshold_ctrl_set_param(config->adc,
102 config->th_sel, ¶m);
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, ¶m);
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, ¶m.val);
139 if (ret) {
140 return ret;
141 }
142 }
143
144 ret = adc_npcx_threshold_ctrl_set_param(config->adc,
145 config->th_sel, ¶m);
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, ¶m);
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 ¶m);
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