1 /*
2  * Copyright (c) 2024, Vitrolife A/S
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Datasheet:
7  * https://www.festo.com/media/pim/620/D15000100140620.PDF
8  *
9  */
10 
11 #define DT_DRV_COMPAT festo_veaa_x_3
12 
13 #include <stdio.h>
14 
15 #include <zephyr/device.h>
16 #include <zephyr/drivers/adc.h>
17 #include <zephyr/drivers/dac.h>
18 #include <zephyr/drivers/sensor.h>
19 #include <zephyr/drivers/sensor/veaa_x_3.h>
20 #include <zephyr/logging/log.h>
21 #include <zephyr/sys/math_extras.h>
22 #include <zephyr/sys/util_macro.h>
23 
24 LOG_MODULE_REGISTER(veaa_x_3_sensor, CONFIG_SENSOR_LOG_LEVEL);
25 
26 struct veaa_x_3_data {
27 	uint16_t adc_buf;
28 };
29 
30 struct veaa_x_3_cfg {
31 	const struct adc_dt_spec adc;
32 	const struct device *dac;
33 	const uint8_t dac_channel;
34 	const uint8_t dac_resolution;
35 	const uint16_t kpa_max;
36 	const uint8_t kpa_min;
37 };
38 
veaa_x_3_kpa_range(const struct veaa_x_3_cfg * cfg)39 static uint16_t veaa_x_3_kpa_range(const struct veaa_x_3_cfg *cfg)
40 {
41 	return cfg->kpa_max - cfg->kpa_min;
42 }
43 
veaa_x_3_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)44 static int veaa_x_3_attr_set(const struct device *dev, enum sensor_channel chan,
45 			     enum sensor_attribute attr, const struct sensor_value *val)
46 {
47 	const struct veaa_x_3_cfg *cfg = dev->config;
48 	uint32_t tmp;
49 
50 	if (chan != SENSOR_CHAN_PRESS) {
51 		return -ENOTSUP;
52 	}
53 
54 	switch ((enum sensor_attribute_veaa_x_3)attr) {
55 	case SENSOR_ATTR_VEAA_X_3_SETPOINT:
56 		if (val->val1 > cfg->kpa_max || val->val1 < cfg->kpa_min) {
57 			LOG_ERR("%d kPa outside range", val->val1);
58 			return -EINVAL;
59 		}
60 
61 		/* Convert from kPa to DAC value */
62 		tmp = val->val1 - cfg->kpa_min;
63 		if (u32_mul_overflow(tmp, BIT(cfg->dac_resolution) - 1, &tmp)) {
64 			LOG_ERR("kPa to DAC overflow");
65 			return -ERANGE;
66 		}
67 		tmp /= veaa_x_3_kpa_range(cfg);
68 
69 		return dac_write_value(cfg->dac, cfg->dac_channel, tmp);
70 	default:
71 		return -ENOTSUP;
72 	}
73 }
74 
veaa_x_3_attr_get(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,struct sensor_value * val)75 static int veaa_x_3_attr_get(const struct device *dev, enum sensor_channel chan,
76 			     enum sensor_attribute attr, struct sensor_value *val)
77 {
78 	const struct veaa_x_3_cfg *cfg = dev->config;
79 
80 	if (chan != SENSOR_CHAN_PRESS) {
81 		return -ENOTSUP;
82 	}
83 
84 	switch ((enum sensor_attribute_veaa_x_3)attr) {
85 	case SENSOR_ATTR_VEAA_X_3_RANGE:
86 		val->val1 = cfg->kpa_min;
87 		val->val2 = cfg->kpa_max;
88 		return 0;
89 	default:
90 		return -ENOTSUP;
91 	}
92 }
93 
veaa_x_3_sample_fetch(const struct device * dev,enum sensor_channel chan)94 static int veaa_x_3_sample_fetch(const struct device *dev, enum sensor_channel chan)
95 {
96 	int rc;
97 	const struct veaa_x_3_cfg *cfg = dev->config;
98 	struct veaa_x_3_data *data = dev->data;
99 	struct adc_sequence sequence = {
100 		.buffer = &data->adc_buf,
101 		.buffer_size = sizeof(data->adc_buf),
102 	};
103 
104 	if (chan != SENSOR_CHAN_PRESS && chan != SENSOR_CHAN_ALL) {
105 		return -ENOTSUP;
106 	}
107 
108 	rc = adc_sequence_init_dt(&cfg->adc, &sequence);
109 	if (rc != 0) {
110 		return rc;
111 	}
112 	sequence.options = NULL;
113 	sequence.buffer = &data->adc_buf;
114 	sequence.buffer_size = sizeof(data->adc_buf);
115 	sequence.calibrate = false;
116 
117 	rc = adc_read_dt(&cfg->adc, &sequence);
118 	if (rc != 0) {
119 		return rc;
120 	}
121 
122 	return 0;
123 }
124 
veaa_x_3_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)125 static int veaa_x_3_channel_get(const struct device *dev, enum sensor_channel chan,
126 				struct sensor_value *val)
127 {
128 	const struct veaa_x_3_cfg *cfg = dev->config;
129 	struct veaa_x_3_data *data = dev->data;
130 	const uint32_t max_adc_val = BIT(cfg->adc.resolution) - 1;
131 
132 	if (chan != SENSOR_CHAN_PRESS) {
133 		return -ENOTSUP;
134 	}
135 
136 	/* Convert from ADC value to kPa */
137 	if (u32_mul_overflow(data->adc_buf, veaa_x_3_kpa_range(cfg), &val->val1)) {
138 		LOG_ERR("ADC to kPa overflow");
139 		return -ERANGE;
140 	}
141 	val->val2 = (val->val1 % max_adc_val) * 1000000 / max_adc_val;
142 	val->val1 = (val->val1 / max_adc_val) + cfg->kpa_min;
143 
144 	return 0;
145 }
146 
147 static DEVICE_API(sensor, veaa_x_3_api_funcs) = {
148 	.attr_set = veaa_x_3_attr_set,
149 	.attr_get = veaa_x_3_attr_get,
150 	.sample_fetch = veaa_x_3_sample_fetch,
151 	.channel_get = veaa_x_3_channel_get,
152 };
153 
veaa_x_3_init(const struct device * dev)154 static int veaa_x_3_init(const struct device *dev)
155 {
156 	int rc;
157 	const struct veaa_x_3_cfg *cfg = dev->config;
158 	const struct dac_channel_cfg dac_cfg = {
159 		.channel_id = cfg->dac_channel,
160 		.resolution = cfg->dac_resolution,
161 		.buffered = false,
162 	};
163 
164 	LOG_DBG("Initializing %s with range %u-%u kPa", dev->name, cfg->kpa_min, cfg->kpa_max);
165 
166 	if (!adc_is_ready_dt(&cfg->adc)) {
167 		LOG_ERR("ADC not ready");
168 		return -ENODEV;
169 	}
170 
171 	rc = adc_channel_setup_dt(&cfg->adc);
172 	if (rc != 0) {
173 		LOG_ERR("%s setup failed: %d", cfg->adc.dev->name, rc);
174 		return -ENODEV;
175 	}
176 
177 	if (!device_is_ready(cfg->dac)) {
178 		LOG_ERR("DAC not ready");
179 		return -ENODEV;
180 	}
181 
182 	rc = dac_channel_setup(cfg->dac, &dac_cfg);
183 	if (rc != 0) {
184 		LOG_ERR("%s setup failed: %d", cfg->dac->name, rc);
185 		return -ENODEV;
186 	}
187 
188 	return 0;
189 }
190 
191 #define VEAA_X_3_RANGE_KPA_INIT(n)                                                                 \
192 	COND_CODE_1(DT_INST_ENUM_HAS_VALUE(n, pressure_range_type, d11), ({.max = 1000, min = 5}), \
193 		    (COND_CODE_1(DT_INST_ENUM_HAS_VALUE(n, pressure_range_type, d9),               \
194 				 ({.max = 600, min = 3}), ({.max = 200, .min = 1}))))
195 
196 #define VEAA_X_3_TYPE_INIT(n)                                                                      \
197 	COND_CODE_1(DT_INST_ENUM_HAS_VALUE(n, pressure_range_type, d11),                           \
198 		    (.kpa_max = 1000, .kpa_min = 5),                                               \
199 		    (COND_CODE_1(DT_INST_ENUM_HAS_VALUE(n, pressure_range_type, d9),               \
200 				 (.kpa_max = 600, kpa_min = 3), (.kpa_max = 200, .kpa_min = 1))))
201 
202 #define VEAA_X_3_INIT(n)                                                                           \
203                                                                                                    \
204 	static struct veaa_x_3_data veaa_x_3_data_##n;                                             \
205                                                                                                    \
206 	static const struct veaa_x_3_cfg veaa_x_3_cfg_##n = {                                      \
207 		.adc = ADC_DT_SPEC_INST_GET(n),                                                    \
208 		.dac = DEVICE_DT_GET(DT_INST_PHANDLE(n, dac)),                                     \
209 		.dac_channel = DT_INST_PROP(n, dac_channel_id),                                    \
210 		.dac_resolution = DT_INST_PROP(n, dac_resolution),                                 \
211 		VEAA_X_3_TYPE_INIT(n)};                                                            \
212                                                                                                    \
213 	SENSOR_DEVICE_DT_INST_DEFINE(n, veaa_x_3_init, NULL, &veaa_x_3_data_##n,                   \
214 				     &veaa_x_3_cfg_##n, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,  \
215 				     &veaa_x_3_api_funcs);
216 
217 DT_INST_FOREACH_STATUS_OKAY(VEAA_X_3_INIT)
218