1 /* HopeRF Electronic HP206C precision barometer and altimeter driver
2  *
3  * Copyright (c) 2016 Intel Corporation
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  *
7  * Datasheet:
8  *   http://www.hoperf.com/upload/sensor/HP206C_DataSheet_EN_V2.0.pdf
9  */
10 
11 #define DT_DRV_COMPAT hoperf_hp206c
12 
13 #include <zephyr/init.h>
14 #include <zephyr/drivers/sensor.h>
15 #include <zephyr/drivers/i2c.h>
16 #include <zephyr/sys/byteorder.h>
17 #include <zephyr/kernel.h>
18 #include <zephyr/logging/log.h>
19 
20 #include "hp206c.h"
21 
22 LOG_MODULE_REGISTER(HP206C, CONFIG_SENSOR_LOG_LEVEL);
23 
hp206c_bus_config(const struct device * dev)24 static inline int hp206c_bus_config(const struct device *dev)
25 {
26 	const struct hp206c_device_config *cfg = dev->config;
27 	uint32_t i2c_cfg;
28 
29 	i2c_cfg = I2C_MODE_CONTROLLER | I2C_SPEED_SET(I2C_SPEED_STANDARD);
30 
31 	return i2c_configure(cfg->i2c.bus, i2c_cfg);
32 }
33 
hp206c_read(const struct device * dev,uint8_t cmd,uint8_t * data,uint8_t len)34 static int hp206c_read(const struct device *dev, uint8_t cmd, uint8_t *data,
35 		       uint8_t len)
36 {
37 	const struct hp206c_device_config *cfg = dev->config;
38 
39 	hp206c_bus_config(dev);
40 
41 	if (i2c_burst_read_dt(&cfg->i2c, cmd, data, len) < 0) {
42 		return -EIO;
43 	}
44 
45 	return 0;
46 }
47 
hp206c_read_reg(const struct device * dev,uint8_t reg_addr,uint8_t * reg_val)48 static int hp206c_read_reg(const struct device *dev, uint8_t reg_addr,
49 			   uint8_t *reg_val)
50 {
51 	uint8_t cmd = HP206C_CMD_READ_REG | (reg_addr & HP206C_REG_ADDR_MASK);
52 
53 	return hp206c_read(dev, cmd, reg_val, 1);
54 }
55 
hp206c_write(const struct device * dev,uint8_t cmd,uint8_t * data,uint8_t len)56 static int hp206c_write(const struct device *dev, uint8_t cmd, uint8_t *data,
57 			uint8_t len)
58 {
59 	const struct hp206c_device_config *cfg = dev->config;
60 
61 	hp206c_bus_config(dev);
62 
63 	if (i2c_burst_write_dt(&cfg->i2c, cmd, data, len) < 0) {
64 		return -EIO;
65 	}
66 
67 	return 0;
68 }
69 
hp206c_write_reg(const struct device * dev,uint8_t reg_addr,uint8_t reg_val)70 static int hp206c_write_reg(const struct device *dev, uint8_t reg_addr,
71 			    uint8_t reg_val)
72 {
73 	uint8_t cmd = HP206C_CMD_WRITE_REG | (reg_addr & HP206C_REG_ADDR_MASK);
74 
75 	return hp206c_write(dev, cmd, &reg_val, 1);
76 }
77 
hp206c_cmd_send(const struct device * dev,uint8_t cmd)78 static int hp206c_cmd_send(const struct device *dev, uint8_t cmd)
79 {
80 	const struct hp206c_device_config *cfg = dev->config;
81 
82 	hp206c_bus_config(dev);
83 
84 	return i2c_write_dt(&cfg->i2c, &cmd, 1);
85 }
86 
87 /*
88  * The conversion times in this map were rounded up. The reason for doing that
89  * is merely to spare 24 bytes that, otherwise, would've been taken by having
90  * the times converted to microseconds. The trade-off is 900us added to the
91  * conversion wait time which looks like a good compromise provided the highest
92  * precision computation takes 131.1ms.
93  */
94 static uint8_t hp206c_adc_time_ms[] = {
95 /*	conversion time(ms),   OSR  */
96 	132,		    /* 4096 */
97 	66,		    /* 2048 */
98 	34,		    /* 1024 */
99 	17,		    /* 512  */
100 	9,		    /* 256  */
101 	5,		    /* 128  */
102 };
103 
hp206c_osr_set(const struct device * dev,uint16_t osr)104 static int hp206c_osr_set(const struct device *dev, uint16_t osr)
105 {
106 	struct hp206c_device_data *hp206c = dev->data;
107 	uint8_t i;
108 
109 	/* the following code translates OSR values to an index */
110 	for (i = 0U; i < 6 && BIT(12 - i) != osr; i++) {
111 	}
112 
113 	if (i == 6U) {
114 		return -ENOTSUP;
115 	}
116 
117 	hp206c->osr = i;
118 
119 	return 0;
120 }
121 
hp206c_altitude_offs_set(const struct device * dev,int16_t offs)122 static int hp206c_altitude_offs_set(const struct device *dev, int16_t offs)
123 {
124 	uint8_t reg_val;
125 
126 	reg_val = offs & 0xff;
127 
128 	if (hp206c_write_reg(dev, HP206C_REG_ALT_OFF_LSB, reg_val) < 0) {
129 		return -EIO;
130 	}
131 
132 	reg_val = (offs & 0xff00) >> 8;
133 
134 	if (hp206c_write_reg(dev, HP206C_REG_ALT_OFF_MSB, reg_val) < 0) {
135 		return -EIO;
136 	}
137 
138 	return hp206c_write_reg(dev, HP206C_REG_PARA, HP206C_COMPENSATION_EN);
139 }
140 
hp206c_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)141 static int hp206c_attr_set(const struct device *dev, enum sensor_channel chan,
142 			   enum sensor_attribute attr,
143 			   const struct sensor_value *val)
144 {
145 #ifdef CONFIG_HP206C_OSR_RUNTIME
146 	if (attr == SENSOR_ATTR_OVERSAMPLING) {
147 		return hp206c_osr_set(dev, val->val1);
148 	}
149 #endif
150 #ifdef CONFIG_HP206C_ALT_OFFSET_RUNTIME
151 	if (attr == SENSOR_ATTR_OFFSET) {
152 		if (chan != SENSOR_CHAN_ALTITUDE) {
153 			return -ENOTSUP;
154 		}
155 
156 		return hp206c_altitude_offs_set(dev, val->val1);
157 	}
158 #endif
159 
160 	return -ENOTSUP;
161 }
162 
hp206c_wait_dev_ready(const struct device * dev,uint32_t timeout_ms)163 static int hp206c_wait_dev_ready(const struct device *dev,
164 				 uint32_t timeout_ms)
165 {
166 	struct hp206c_device_data *hp206c = dev->data;
167 	uint8_t int_src;
168 
169 	k_timer_start(&hp206c->tmr, K_MSEC(timeout_ms), K_NO_WAIT);
170 	k_timer_status_sync(&hp206c->tmr);
171 
172 	if (hp206c_read_reg(dev, HP206C_REG_INT_SRC, &int_src) < 0) {
173 		return -EIO;
174 	}
175 
176 	if (int_src & HP206C_DEV_RDY) {
177 		return 0;
178 	}
179 
180 	return -EBUSY;
181 }
182 
hp206c_adc_acquire(const struct device * dev,enum sensor_channel chan)183 static int hp206c_adc_acquire(const struct device *dev,
184 			      enum sensor_channel chan)
185 {
186 	struct hp206c_device_data *hp206c = dev->data;
187 
188 	if (hp206c_cmd_send(dev, HP206C_CMD_ADC_CVT | (hp206c->osr << 2)) < 0) {
189 		return -EIO;
190 	}
191 
192 	return hp206c_wait_dev_ready(dev, hp206c_adc_time_ms[hp206c->osr]);
193 }
194 
hp206c_buf_convert(uint8_t * buf,bool signed_val)195 static int32_t hp206c_buf_convert(uint8_t *buf, bool signed_val)
196 {
197 	int32_t tmp = 0;
198 
199 	if (signed_val && (buf[0] & 0x08)) {
200 		tmp |= (0xff << 24) | (0xf0 << 16);
201 	}
202 
203 	tmp |= ((buf[0] & 0x0f) << 16) | (buf[1] << 8) | buf[2];
204 
205 	return tmp;
206 }
207 
hp206c_val_get(const struct device * dev,uint8_t cmd,struct sensor_value * val)208 static int hp206c_val_get(const struct device *dev,
209 			  uint8_t cmd, struct sensor_value *val)
210 {
211 	uint8_t buf[3];
212 	int32_t temp = 0;
213 
214 	if (hp206c_read(dev, cmd, buf, 3) < 0) {
215 		return -EIO;
216 	}
217 
218 	/*
219 	 * According to documentation, pressure and altitude are 20 bit unsigned
220 	 * values whereas temperature is a signed.
221 	 */
222 	if (cmd == HP206C_CMD_READ_T) {
223 		temp = hp206c_buf_convert(buf, true);
224 	} else {
225 		temp = hp206c_buf_convert(buf, false);
226 	}
227 
228 	if (cmd == HP206C_CMD_READ_P) {
229 		val->val1 = temp / 1000;
230 		val->val2 = temp % 1000 * 1000;
231 	} else {
232 		val->val1 = temp / 100;
233 		val->val2 = temp % 100 * 10000;
234 	}
235 
236 	return 0;
237 }
238 
hp206c_pressure_get(const struct device * dev,struct sensor_value * val)239 static inline int hp206c_pressure_get(const struct device *dev,
240 				      struct sensor_value *val)
241 {
242 	return hp206c_val_get(dev, HP206C_CMD_READ_P, val);
243 }
244 
hp206c_altitude_get(const struct device * dev,struct sensor_value * val)245 static inline int hp206c_altitude_get(const struct device *dev,
246 				      struct sensor_value *val)
247 {
248 	return hp206c_val_get(dev, HP206C_CMD_READ_A, val);
249 }
250 
hp206c_temperature_get(const struct device * dev,struct sensor_value * val)251 static inline int hp206c_temperature_get(const struct device *dev,
252 					 struct sensor_value *val)
253 {
254 	return hp206c_val_get(dev, HP206C_CMD_READ_T, val);
255 }
256 
hp206c_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)257 static int hp206c_channel_get(const struct device *dev,
258 			      enum sensor_channel chan,
259 			      struct sensor_value *val)
260 {
261 	switch (chan) {
262 	case SENSOR_CHAN_AMBIENT_TEMP:
263 		return hp206c_temperature_get(dev, val);
264 
265 	case SENSOR_CHAN_PRESS:
266 		return hp206c_pressure_get(dev, val);
267 
268 	case SENSOR_CHAN_ALTITUDE:
269 		return hp206c_altitude_get(dev, val);
270 
271 	default:
272 		return -ENOTSUP;
273 	}
274 
275 	return 0;
276 }
277 
278 static const struct sensor_driver_api hp206c_api = {
279 	.attr_set = hp206c_attr_set,
280 	.sample_fetch = hp206c_adc_acquire,
281 	.channel_get = hp206c_channel_get,
282 };
283 
hp206c_init(const struct device * dev)284 static int hp206c_init(const struct device *dev)
285 {
286 	struct hp206c_device_data *hp206c = dev->data;
287 	const struct hp206c_device_config *cfg = dev->config;
288 
289 	if (!device_is_ready(cfg->i2c.bus)) {
290 		LOG_ERR("Bus device is not ready");
291 		return -EINVAL;
292 	}
293 
294 	/* reset the chip */
295 	if (hp206c_cmd_send(dev, HP206C_CMD_SOFT_RST) < 0) {
296 		LOG_ERR("Cannot reset chip.");
297 		return -EIO;
298 	}
299 
300 	k_timer_init(&hp206c->tmr, NULL, NULL);
301 
302 	k_busy_wait(500);
303 
304 	if (hp206c_osr_set(dev, HP206C_DEFAULT_OSR) < 0) {
305 		LOG_ERR("OSR value is not supported.");
306 		return -ENOTSUP;
307 	}
308 
309 	if (hp206c_altitude_offs_set(dev, HP206C_DEFAULT_ALT_OFFSET) < 0) {
310 		return -EIO;
311 	}
312 
313 	return 0;
314 }
315 
316 #define HP206C_DEFINE(inst)								\
317 	static struct hp206c_device_data hp206c_data_##inst;				\
318 											\
319 	static const struct hp206c_device_config hp206c_config_##inst = {		\
320 		.i2c = I2C_DT_SPEC_INST_GET(inst),					\
321 	};										\
322 											\
323 	SENSOR_DEVICE_DT_INST_DEFINE(inst, hp206c_init, NULL,				\
324 			      &hp206c_data_##inst, &hp206c_config_##inst, POST_KERNEL,	\
325 			      CONFIG_SENSOR_INIT_PRIORITY, &hp206c_api);		\
326 
327 DT_INST_FOREACH_STATUS_OKAY(HP206C_DEFINE)
328