1 /*
2  * Copyright (c) 2019 Thomas Schmid <tom@lfence.de>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT meas_ms5607
8 
9 #include <zephyr/init.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/sys/byteorder.h>
12 #include <zephyr/drivers/sensor.h>
13 #include <zephyr/sys/__assert.h>
14 
15 #include "ms5607.h"
16 
17 #define LOG_LEVEL CONFIG_SENSOR_LOG_LEVEL
18 #include <zephyr/logging/log.h>
19 LOG_MODULE_REGISTER(ms5607);
20 
ms5607_compensate(struct ms5607_data * data,const int32_t adc_temperature,const int32_t adc_pressure)21 static void ms5607_compensate(struct ms5607_data *data,
22 			      const int32_t adc_temperature,
23 			      const int32_t adc_pressure)
24 {
25 	int64_t dT;
26 	int64_t OFF;
27 	int64_t SENS;
28 	int64_t temp_sq;
29 	int64_t Ti;
30 	int64_t OFFi;
31 	int64_t SENSi;
32 
33 	/* first order compensation as per datasheet
34 	 * (https://www.te.com/usa-en/product-CAT-BLPS0035.html) section
35 	 * PRESSURE AND TEMPERATURE CALCULATION
36 	 */
37 
38 	dT = adc_temperature - ((int32_t)(data->t_ref) << 8);
39 	data->temperature = 2000 + (dT * data->tempsens) / (1ll << 23);
40 	OFF = ((int64_t)(data->off_t1) << 17) + (dT * data->tco) / (1ll << 6);
41 	SENS = ((int64_t)(data->sens_t1) << 16) + (dT * data->tcs) / (1ll << 7);
42 
43 	/* Second order compensation as per datasheet
44 	 * (https://www.te.com/usa-en/product-CAT-BLPS0035.html) section
45 	 * SECOND ORDER TEMPERATURE COMPENSATION
46 	 */
47 
48 	temp_sq = (int64_t)(data->temperature - 2000) *
49 		  (int64_t)(data->temperature - 2000);
50 	if (data->temperature < 2000) {
51 		Ti = (dT * dT) / (1ll << 31);
52 		OFFi = (61ll * temp_sq) / (1ll << 4);
53 		SENSi = 2ll * temp_sq;
54 		if (data->temperature < -1500) {
55 			temp_sq = (int64_t)(data->temperature + 1500) *
56 				  (int64_t)(data->temperature + 1500);
57 			OFFi += 15ll * temp_sq;
58 			SENSi += 8ll * temp_sq;
59 		}
60 	} else {
61 		SENSi = 0;
62 		OFFi = 0;
63 		Ti = 0;
64 	}
65 
66 	OFF -= OFFi;
67 	SENS -= SENSi;
68 
69 	data->temperature -= Ti;
70 	data->pressure = (SENS * (int64_t)adc_pressure / (1ll << 21) - OFF) /
71 			 (1ll << 15);
72 }
73 
ms5607_read_prom(const struct ms5607_config * config,uint8_t cmd,uint16_t * val)74 static int ms5607_read_prom(const struct ms5607_config *config, uint8_t cmd,
75 			    uint16_t *val)
76 {
77 	int err;
78 
79 	err = config->tf->read_prom(config, cmd, val);
80 	if (err < 0) {
81 		LOG_ERR("Error reading prom");
82 		return err;
83 	}
84 
85 	return 0;
86 }
87 
ms5607_get_measurement(const struct ms5607_config * config,uint32_t * val,uint8_t cmd,uint8_t delay)88 static int ms5607_get_measurement(const struct ms5607_config *config,
89 				  uint32_t *val,
90 				  uint8_t cmd,
91 				  uint8_t delay)
92 {
93 	int err;
94 
95 	*val = 0U;
96 
97 	err = config->tf->start_conversion(config, cmd);
98 	if (err < 0) {
99 		return err;
100 	}
101 
102 	k_msleep(delay);
103 
104 	err = config->tf->read_adc(config, val);
105 	if (err < 0) {
106 		return err;
107 	}
108 
109 	return 0;
110 }
111 
ms5607_sample_fetch(const struct device * dev,enum sensor_channel channel)112 static int ms5607_sample_fetch(const struct device *dev,
113 			       enum sensor_channel channel)
114 {
115 	const struct ms5607_config *config = dev->config;
116 	struct ms5607_data *data = dev->data;
117 	int err;
118 	uint32_t adc_pressure, adc_temperature;
119 
120 	__ASSERT_NO_MSG(channel == SENSOR_CHAN_ALL);
121 
122 	err = ms5607_get_measurement(config,
123 				     &adc_pressure,
124 				     data->pressure_conv_cmd,
125 				     data->pressure_conv_delay);
126 	if (err < 0) {
127 		return err;
128 	}
129 
130 	err = ms5607_get_measurement(config,
131 				     &adc_temperature,
132 				     data->temperature_conv_cmd,
133 				     data->temperature_conv_delay);
134 	if (err < 0) {
135 		return err;
136 	}
137 
138 	ms5607_compensate(data, adc_temperature, adc_pressure);
139 	return 0;
140 }
141 
ms5607_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)142 static int ms5607_channel_get(const struct device *dev,
143 			      enum sensor_channel chan,
144 			      struct sensor_value *val)
145 {
146 	const struct ms5607_data *data = dev->data;
147 
148 	switch (chan) {
149 	case SENSOR_CHAN_AMBIENT_TEMP:
150 		val->val1 = data->temperature / 100;
151 		val->val2 = data->temperature % 100 * 10000;
152 		break;
153 	case SENSOR_CHAN_PRESS:
154 		val->val1 = data->pressure / 100;
155 		val->val2 = data->pressure % 100 * 10000;
156 		break;
157 	default:
158 		return -ENOTSUP;
159 	}
160 
161 	return 0;
162 }
163 
ms5607_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)164 static int ms5607_attr_set(const struct device *dev, enum sensor_channel chan,
165 			   enum sensor_attribute attr,
166 			   const struct sensor_value *val)
167 {
168 	struct ms5607_data *data = dev->data;
169 	uint8_t p_conv_cmd, t_conv_cmd, conv_delay;
170 
171 	if (attr != SENSOR_ATTR_OVERSAMPLING) {
172 		return -ENOTSUP;
173 	}
174 
175 	switch (val->val1) {
176 	case 4096:
177 		p_conv_cmd = MS5607_CMD_CONV_P_4096;
178 		t_conv_cmd = MS5607_CMD_CONV_T_4096;
179 		conv_delay = 9U;
180 		break;
181 	case 2048:
182 		p_conv_cmd = MS5607_CMD_CONV_P_2048;
183 		t_conv_cmd = MS5607_CMD_CONV_T_2048;
184 		conv_delay = 5U;
185 		break;
186 	case 1024:
187 		p_conv_cmd = MS5607_CMD_CONV_P_1024;
188 		t_conv_cmd = MS5607_CMD_CONV_T_1024;
189 		conv_delay = 3U;
190 		break;
191 	case 512:
192 		p_conv_cmd = MS5607_CMD_CONV_P_512;
193 		t_conv_cmd = MS5607_CMD_CONV_T_512;
194 		conv_delay = 2U;
195 		break;
196 	case 256:
197 		p_conv_cmd = MS5607_CMD_CONV_P_256;
198 		t_conv_cmd = MS5607_CMD_CONV_T_256;
199 		conv_delay = 1U;
200 		break;
201 	default:
202 		LOG_ERR("invalid oversampling rate %d", val->val1);
203 		return -EINVAL;
204 	}
205 
206 	switch (chan) {
207 	case SENSOR_CHAN_ALL:
208 		data->pressure_conv_cmd = p_conv_cmd;
209 		data->temperature_conv_cmd = t_conv_cmd;
210 		data->temperature_conv_delay = conv_delay;
211 		data->pressure_conv_delay = conv_delay;
212 		break;
213 	case SENSOR_CHAN_PRESS:
214 		data->pressure_conv_cmd = p_conv_cmd;
215 		data->pressure_conv_delay = conv_delay;
216 		break;
217 	case SENSOR_CHAN_AMBIENT_TEMP:
218 		data->temperature_conv_cmd = t_conv_cmd;
219 		data->temperature_conv_delay = conv_delay;
220 		break;
221 	default:
222 		return -ENOTSUP;
223 	}
224 
225 	return 0;
226 }
227 
ms5607_init(const struct device * dev)228 static int ms5607_init(const struct device *dev)
229 {
230 	const struct ms5607_config *const config = dev->config;
231 	struct ms5607_data *data = dev->data;
232 	struct sensor_value val;
233 	int err;
234 
235 	err = config->tf->bus_check(config);
236 	if (err < 0) {
237 		return err;
238 	}
239 
240 	data->pressure = 0;
241 	data->temperature = 0;
242 
243 
244 	val.val1 = MS5607_PRES_OVER_DEFAULT;
245 	err = ms5607_attr_set(dev, SENSOR_CHAN_PRESS, SENSOR_ATTR_OVERSAMPLING,
246 			      &val);
247 	if (err < 0) {
248 		return err;
249 	}
250 
251 	val.val1 = MS5607_TEMP_OVER_DEFAULT;
252 	err = ms5607_attr_set(dev, SENSOR_CHAN_AMBIENT_TEMP,
253 			      SENSOR_ATTR_OVERSAMPLING, &val);
254 	if (err < 0) {
255 		return err;
256 	}
257 
258 	err = config->tf->reset(config);
259 	if (err < 0) {
260 		return err;
261 	}
262 
263 	k_sleep(K_MSEC(2));
264 
265 	err = ms5607_read_prom(config, MS5607_CMD_CONV_READ_OFF_T1,
266 			       &data->off_t1);
267 	if (err < 0) {
268 		return err;
269 	}
270 
271 	LOG_DBG("OFF_T1: %d", data->off_t1);
272 
273 	err = ms5607_read_prom(config, MS5607_CMD_CONV_READ_SENSE_T1,
274 			       &data->sens_t1);
275 	if (err < 0) {
276 		return err;
277 	}
278 
279 	LOG_DBG("SENSE_T1: %d", data->sens_t1);
280 
281 	err = ms5607_read_prom(config, MS5607_CMD_CONV_READ_T_REF, &data->t_ref);
282 	if (err < 0) {
283 		return err;
284 	}
285 
286 	LOG_DBG("T_REF: %d", data->t_ref);
287 
288 	err = ms5607_read_prom(config, MS5607_CMD_CONV_READ_TCO, &data->tco);
289 	if (err < 0) {
290 		return err;
291 	}
292 
293 	LOG_DBG("TCO: %d", data->tco);
294 
295 	err = ms5607_read_prom(config, MS5607_CMD_CONV_READ_TCS, &data->tcs);
296 	if (err < 0) {
297 		return err;
298 	}
299 
300 	LOG_DBG("TCS: %d", data->tcs);
301 
302 	err = ms5607_read_prom(config, MS5607_CMD_CONV_READ_TEMPSENS,
303 			       &data->tempsens);
304 	if (err < 0) {
305 		return err;
306 	}
307 
308 	LOG_DBG("TEMPSENS: %d", data->tempsens);
309 
310 	return 0;
311 }
312 
313 static DEVICE_API(sensor, ms5607_api_funcs) = {
314 	.attr_set = ms5607_attr_set,
315 	.sample_fetch = ms5607_sample_fetch,
316 	.channel_get = ms5607_channel_get,
317 };
318 
319 #define MS5607_SPI_OPERATION (SPI_OP_MODE_MASTER | SPI_WORD_SET(8) |	\
320 			      SPI_MODE_CPOL | SPI_MODE_CPHA | SPI_TRANSFER_MSB)
321 
322 /* Initializes a struct ms5607_config for an instance on a SPI bus. */
323 #define MS5607_CONFIG_SPI(inst)						\
324 	{								\
325 		.tf = &ms5607_spi_transfer_function,			\
326 		.bus_cfg.spi = SPI_DT_SPEC_INST_GET(inst,		\
327 						MS5607_SPI_OPERATION,	\
328 						0),			\
329 	}
330 
331 /* Initializes a struct ms5607_config for an instance on a I2C bus. */
332 #define MS5607_CONFIG_I2C(inst)						\
333 	{								\
334 		.tf = &ms5607_i2c_transfer_function,			\
335 		.bus_cfg.i2c = I2C_DT_SPEC_INST_GET(inst),		\
336 	}
337 
338 /*
339  * Main instantiation macro, which selects the correct bus-specific
340  * instantiation macros for the instance.
341  */
342 #define MS5607_DEFINE(inst)						\
343 	static struct ms5607_data ms5607_data_##inst;			\
344 	static const struct ms5607_config ms5607_config_##inst =	\
345 		COND_CODE_1(DT_INST_ON_BUS(inst, spi),			\
346 			    (MS5607_CONFIG_SPI(inst)),			\
347 			    (MS5607_CONFIG_I2C(inst)));			\
348 	SENSOR_DEVICE_DT_INST_DEFINE(inst,				\
349 			ms5607_init,					\
350 			NULL,						\
351 			&ms5607_data_##inst,				\
352 			&ms5607_config_##inst,				\
353 			POST_KERNEL,					\
354 			CONFIG_SENSOR_INIT_PRIORITY,			\
355 			&ms5607_api_funcs);
356 
357 /* Create the struct device for every status "okay" node in the devicetree. */
358 DT_INST_FOREACH_STATUS_OKAY(MS5607_DEFINE)
359