1 /* Bosch BMI08X inertial measurement unit driver
2  *
3  * Copyright (c) 2022 Meta Platforms, Inc. and its affiliates
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <zephyr/drivers/sensor.h>
9 #include <zephyr/pm/device.h>
10 #include <zephyr/init.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/logging/log.h>
13 #include <zephyr/sys/__assert.h>
14 #include <zephyr/sys/byteorder.h>
15 
16 #define DT_DRV_COMPAT bosch_bmi08x_gyro
17 #include "bmi08x.h"
18 
19 LOG_MODULE_REGISTER(BMI08X_GYRO, CONFIG_SENSOR_LOG_LEVEL);
20 
21 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
22 
bmi08x_gyro_transceive_i2c(const struct device * dev,uint8_t reg,bool write,void * data,size_t length)23 static int bmi08x_gyro_transceive_i2c(const struct device *dev, uint8_t reg, bool write, void *data,
24 				      size_t length)
25 {
26 	const struct bmi08x_gyro_config *bmi08x = dev->config;
27 
28 	if (!write) {
29 		return i2c_write_read_dt(&bmi08x->bus.i2c, &reg, 1, data, length);
30 	}
31 	if (length > CONFIG_BMI08X_I2C_WRITE_BURST_SIZE) {
32 		return -EINVAL;
33 	}
34 	uint8_t buf[1 + CONFIG_BMI08X_I2C_WRITE_BURST_SIZE];
35 
36 	buf[0] = reg;
37 	memcpy(&buf[1], data, length);
38 	return i2c_write_dt(&bmi08x->bus.i2c, buf, 1 + length);
39 }
40 
bmi08x_bus_check_i2c(const union bmi08x_bus * bus)41 static int bmi08x_bus_check_i2c(const union bmi08x_bus *bus)
42 {
43 	return i2c_is_ready_dt(&bus->i2c) ? 0 : -ENODEV;
44 }
45 
46 static const struct bmi08x_gyro_bus_io bmi08x_i2c_api = {
47 	.check = bmi08x_bus_check_i2c,
48 	.transceive = bmi08x_gyro_transceive_i2c,
49 };
50 
51 #endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */
52 
53 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
54 
bmi08x_gyro_transceive_spi(const struct device * dev,uint8_t reg,bool write,void * data,size_t length)55 static int bmi08x_gyro_transceive_spi(const struct device *dev, uint8_t reg, bool write, void *data,
56 				      size_t length)
57 {
58 	const struct bmi08x_gyro_config *bmi08x = dev->config;
59 	const struct spi_buf tx_buf[2] = {{.buf = &reg, .len = 1}, {.buf = data, .len = length}};
60 	const struct spi_buf_set tx = {.buffers = tx_buf, .count = write ? 2 : 1};
61 
62 	if (!write) {
63 		uint16_t dummy;
64 		const struct spi_buf rx_buf[2] = {{.buf = &dummy, .len = 1},
65 						  {.buf = data, .len = length}};
66 		const struct spi_buf_set rx = {.buffers = rx_buf, .count = 2};
67 
68 		return spi_transceive_dt(&bmi08x->bus.spi, &tx, &rx);
69 	}
70 
71 	return spi_write_dt(&bmi08x->bus.spi, &tx);
72 }
73 
bmi08x_bus_check_spi(const union bmi08x_bus * bus)74 static int bmi08x_bus_check_spi(const union bmi08x_bus *bus)
75 {
76 	return spi_is_ready_dt(&bus->spi) ? 0 : -ENODEV;
77 }
78 
79 static const struct bmi08x_gyro_bus_io bmi08x_spi_api = {
80 	.check = bmi08x_bus_check_spi,
81 	.transceive = bmi08x_gyro_transceive_spi,
82 };
83 
84 #endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) */
85 
bmi08x_bus_check(const struct device * dev)86 static inline int bmi08x_bus_check(const struct device *dev)
87 {
88 	const struct bmi08x_gyro_config *config = dev->config;
89 
90 	return config->api->check(&config->bus);
91 }
92 
bmi08x_gyro_transceive(const struct device * dev,uint8_t reg,bool write,void * data,size_t length)93 static int bmi08x_gyro_transceive(const struct device *dev, uint8_t reg, bool write, void *data,
94 				  size_t length)
95 {
96 	const struct bmi08x_gyro_config *cfg = dev->config;
97 
98 	return cfg->api->transceive(dev, reg, write, data, length);
99 }
100 
bmi08x_gyro_read(const struct device * dev,uint8_t reg_addr,uint8_t * data,uint8_t len)101 int bmi08x_gyro_read(const struct device *dev, uint8_t reg_addr, uint8_t *data, uint8_t len)
102 {
103 	return bmi08x_gyro_transceive(dev, reg_addr | BIT(7), false, data, len);
104 }
105 
bmi08x_gyro_byte_read(const struct device * dev,uint8_t reg_addr,uint8_t * byte)106 int bmi08x_gyro_byte_read(const struct device *dev, uint8_t reg_addr, uint8_t *byte)
107 {
108 	return bmi08x_gyro_transceive(dev, reg_addr | BIT(7), false, byte, 1);
109 }
110 
bmi08x_gyro_byte_write(const struct device * dev,uint8_t reg_addr,uint8_t byte)111 int bmi08x_gyro_byte_write(const struct device *dev, uint8_t reg_addr, uint8_t byte)
112 {
113 	return bmi08x_gyro_transceive(dev, reg_addr & 0x7F, true, &byte, 1);
114 }
115 
bmi08x_gyro_word_write(const struct device * dev,uint8_t reg_addr,uint16_t word)116 int bmi08x_gyro_word_write(const struct device *dev, uint8_t reg_addr, uint16_t word)
117 {
118 	uint8_t tx_word[2] = {(uint8_t)(word & 0xff), (uint8_t)(word >> 8)};
119 
120 	return bmi08x_gyro_transceive(dev, reg_addr & 0x7F, true, tx_word, 2);
121 }
122 
bmi08x_gyro_reg_field_update(const struct device * dev,uint8_t reg_addr,uint8_t pos,uint8_t mask,uint8_t val)123 int bmi08x_gyro_reg_field_update(const struct device *dev, uint8_t reg_addr, uint8_t pos,
124 				 uint8_t mask, uint8_t val)
125 {
126 	uint8_t old_val;
127 	int ret;
128 
129 	ret = bmi08x_gyro_byte_read(dev, reg_addr, &old_val);
130 	if (ret < 0) {
131 		return ret;
132 	}
133 
134 	return bmi08x_gyro_byte_write(dev, reg_addr, (old_val & ~mask) | ((val << pos) & mask));
135 }
136 
137 static const struct bmi08x_range bmi08x_gyr_range_map[] = {
138 	{125, BMI08X_GYR_RANGE_125DPS},   {250, BMI08X_GYR_RANGE_250DPS},
139 	{500, BMI08X_GYR_RANGE_500DPS},   {1000, BMI08X_GYR_RANGE_1000DPS},
140 	{2000, BMI08X_GYR_RANGE_2000DPS},
141 };
142 #define BMI08X_GYR_RANGE_MAP_SIZE ARRAY_SIZE(bmi08x_gyr_range_map)
143 
bmi08x_gyr_reg_val_to_range(uint8_t reg_val)144 int32_t bmi08x_gyr_reg_val_to_range(uint8_t reg_val)
145 {
146 	return bmi08x_reg_val_to_range(reg_val, bmi08x_gyr_range_map, BMI08X_GYR_RANGE_MAP_SIZE);
147 }
148 
bmi08x_gyr_odr_set(const struct device * dev,uint16_t freq_int,uint16_t freq_milli)149 static int bmi08x_gyr_odr_set(const struct device *dev, uint16_t freq_int, uint16_t freq_milli)
150 {
151 	int odr = bmi08x_freq_to_odr_val(freq_int, freq_milli);
152 
153 	if (odr < 0) {
154 		return odr;
155 	}
156 
157 	if (odr < BMI08X_GYRO_BW_532_ODR_2000_HZ || odr > BMI08X_GYRO_BW_32_ODR_100_HZ) {
158 		return -ENOTSUP;
159 	}
160 
161 	return bmi08x_gyro_byte_write(dev, BMI08X_REG_GYRO_BANDWIDTH, (uint8_t)odr);
162 }
163 
bmi08x_gyr_range_set(const struct device * dev,uint16_t range)164 static int bmi08x_gyr_range_set(const struct device *dev, uint16_t range)
165 {
166 	struct bmi08x_gyro_data *bmi08x = dev->data;
167 	int32_t reg_val =
168 		bmi08x_range_to_reg_val(range, bmi08x_gyr_range_map, BMI08X_GYR_RANGE_MAP_SIZE);
169 	int ret;
170 
171 	if (reg_val < 0) {
172 		return reg_val;
173 	}
174 
175 	ret = bmi08x_gyro_byte_write(dev, BMI08X_REG_GYRO_RANGE, reg_val);
176 	if (ret < 0) {
177 		return ret;
178 	}
179 
180 	bmi08x->scale = BMI08X_GYR_SCALE(range);
181 
182 	return ret;
183 }
184 
bmi08x_gyr_config(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)185 static int bmi08x_gyr_config(const struct device *dev, enum sensor_channel chan,
186 			     enum sensor_attribute attr, const struct sensor_value *val)
187 {
188 	switch (attr) {
189 	case SENSOR_ATTR_FULL_SCALE:
190 		return bmi08x_gyr_range_set(dev, sensor_rad_to_degrees(val));
191 	case SENSOR_ATTR_SAMPLING_FREQUENCY:
192 		return bmi08x_gyr_odr_set(dev, val->val1, val->val2 / 1000);
193 	default:
194 		LOG_DBG("Gyro attribute not supported.");
195 		return -ENOTSUP;
196 	}
197 }
198 
bmi08x_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)199 static int bmi08x_attr_set(const struct device *dev, enum sensor_channel chan,
200 			   enum sensor_attribute attr, const struct sensor_value *val)
201 {
202 #ifdef CONFIG_PM_DEVICE
203 	enum pm_device_state state;
204 
205 	(void)pm_device_state_get(dev, &state);
206 	if (state != PM_DEVICE_STATE_ACTIVE) {
207 		return -EBUSY;
208 	}
209 #endif
210 
211 	switch (chan) {
212 	case SENSOR_CHAN_GYRO_X:
213 	case SENSOR_CHAN_GYRO_Y:
214 	case SENSOR_CHAN_GYRO_Z:
215 	case SENSOR_CHAN_GYRO_XYZ:
216 		return bmi08x_gyr_config(dev, chan, attr, val);
217 	default:
218 		LOG_DBG("attr_set() not supported on this channel.");
219 		return -ENOTSUP;
220 	}
221 }
222 
bmi08x_sample_fetch(const struct device * dev,enum sensor_channel chan)223 static int bmi08x_sample_fetch(const struct device *dev, enum sensor_channel chan)
224 {
225 	struct bmi08x_gyro_data *bmi08x = dev->data;
226 	size_t i;
227 	int ret;
228 
229 	if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_GYRO_XYZ) {
230 		LOG_DBG("Unsupported sensor channel");
231 		return -ENOTSUP;
232 	}
233 
234 	ret = bmi08x_gyro_read(dev, BMI08X_REG_GYRO_X_LSB, (uint8_t *)bmi08x->gyr_sample,
235 			       sizeof(bmi08x->gyr_sample));
236 	if (ret < 0) {
237 		return ret;
238 	}
239 
240 	/* convert samples to cpu endianness */
241 	for (i = 0; i < ARRAY_SIZE(bmi08x->gyr_sample); i++) {
242 		bmi08x->gyr_sample[i] = sys_le16_to_cpu(bmi08x->gyr_sample[i]);
243 	}
244 
245 	return ret;
246 }
247 
bmi08x_to_fixed_point(int16_t raw_val,uint16_t scale,struct sensor_value * val)248 static void bmi08x_to_fixed_point(int16_t raw_val, uint16_t scale, struct sensor_value *val)
249 {
250 	int32_t converted_val;
251 
252 	/*
253 	 * maximum converted value we can get is: max(raw_val) * max(scale)
254 	 *	max(raw_val) = +/- 2^15
255 	 *	max(scale) = 4785
256 	 *	max(converted_val) = 156794880 which is less than 2^31
257 	 */
258 	converted_val = raw_val * scale;
259 	val->val1 = converted_val / 1000000;
260 	val->val2 = converted_val % 1000000;
261 }
262 
bmi08x_channel_convert(enum sensor_channel chan,uint16_t scale,uint16_t * raw_xyz,struct sensor_value * val)263 static void bmi08x_channel_convert(enum sensor_channel chan, uint16_t scale, uint16_t *raw_xyz,
264 				   struct sensor_value *val)
265 {
266 	int i;
267 	uint8_t ofs_start, ofs_stop;
268 
269 	switch (chan) {
270 	case SENSOR_CHAN_GYRO_X:
271 		ofs_start = ofs_stop = 0U;
272 		break;
273 	case SENSOR_CHAN_GYRO_Y:
274 		ofs_start = ofs_stop = 1U;
275 		break;
276 	case SENSOR_CHAN_GYRO_Z:
277 		ofs_start = ofs_stop = 2U;
278 		break;
279 	default:
280 		ofs_start = 0U;
281 		ofs_stop = 2U;
282 		break;
283 	}
284 
285 	for (i = ofs_start; i <= ofs_stop; i++, val++) {
286 		bmi08x_to_fixed_point(raw_xyz[i], scale, val);
287 	}
288 }
289 
bmi08x_gyr_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)290 static inline void bmi08x_gyr_channel_get(const struct device *dev, enum sensor_channel chan,
291 					  struct sensor_value *val)
292 {
293 	struct bmi08x_gyro_data *bmi08x = dev->data;
294 
295 	bmi08x_channel_convert(chan, bmi08x->scale, bmi08x->gyr_sample, val);
296 }
297 
bmi08x_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)298 static int bmi08x_channel_get(const struct device *dev, enum sensor_channel chan,
299 			      struct sensor_value *val)
300 {
301 #ifdef CONFIG_PM_DEVICE
302 	enum pm_device_state state;
303 
304 	(void)pm_device_state_get(dev, &state);
305 	if (state != PM_DEVICE_STATE_ACTIVE) {
306 		return -EBUSY;
307 	}
308 #endif
309 
310 	switch ((int16_t)chan) {
311 	case SENSOR_CHAN_GYRO_X:
312 	case SENSOR_CHAN_GYRO_Y:
313 	case SENSOR_CHAN_GYRO_Z:
314 	case SENSOR_CHAN_GYRO_XYZ:
315 		bmi08x_gyr_channel_get(dev, chan, val);
316 		return 0;
317 	default:
318 		LOG_DBG("Channel not supported.");
319 		return -ENOTSUP;
320 	}
321 }
322 
323 #ifdef CONFIG_PM_DEVICE
bmi08x_gyro_pm_action(const struct device * dev,enum pm_device_action action)324 static int bmi08x_gyro_pm_action(const struct device *dev, enum pm_device_action action)
325 {
326 	uint8_t reg_val;
327 	int ret;
328 
329 	switch (action) {
330 	case PM_DEVICE_ACTION_RESUME:
331 		reg_val = BMI08X_GYRO_PM_NORMAL;
332 		break;
333 	case PM_DEVICE_ACTION_SUSPEND:
334 		reg_val = BMI08X_GYRO_PM_SUSPEND;
335 		break;
336 	default:
337 		return -ENOTSUP;
338 	}
339 
340 	ret = bmi08x_gyro_byte_write(dev, BMI08X_REG_GYRO_LPM1, reg_val);
341 	if (ret < 0) {
342 		LOG_ERR("Failed to set power mode");
343 		return ret;
344 	}
345 	k_msleep(BMI08X_GYRO_POWER_MODE_CONFIG_DELAY);
346 
347 	return ret;
348 }
349 #endif /* CONFIG_PM_DEVICE */
350 
351 static DEVICE_API(sensor, bmi08x_api) = {
352 	.attr_set = bmi08x_attr_set,
353 #ifdef CONFIG_BMI08X_GYRO_TRIGGER
354 	.trigger_set = bmi08x_trigger_set_gyr,
355 #endif
356 	.sample_fetch = bmi08x_sample_fetch,
357 	.channel_get = bmi08x_channel_get,
358 };
359 
bmi08x_gyro_init(const struct device * dev)360 int bmi08x_gyro_init(const struct device *dev)
361 {
362 	const struct bmi08x_gyro_config *config = dev->config;
363 	uint8_t val = 0U;
364 	int ret;
365 
366 	ret = bmi08x_bus_check(dev);
367 	if (ret < 0) {
368 		LOG_ERR("Bus not ready for '%s'", dev->name);
369 		return ret;
370 	}
371 
372 	/* reboot the chip */
373 	ret = bmi08x_gyro_byte_write(dev, BMI08X_REG_GYRO_SOFTRESET, BMI08X_SOFT_RESET_CMD);
374 	if (ret < 0) {
375 		LOG_ERR("Cannot reboot chip.");
376 		return ret;
377 	}
378 
379 	k_msleep(BMI08X_GYRO_SOFTRESET_DELAY);
380 
381 	ret = bmi08x_gyro_byte_read(dev, BMI08X_REG_GYRO_CHIP_ID, &val);
382 	if (ret < 0) {
383 		LOG_ERR("Failed to read chip id.");
384 		return ret;
385 	}
386 
387 	if (val != BMI08X_GYRO_CHIP_ID) {
388 		LOG_ERR("Unsupported chip detected (0x%02x)!", val);
389 		return -ENODEV;
390 	}
391 
392 	/* set gyro default range */
393 	ret = bmi08x_gyr_range_set(dev, config->gyro_fs);
394 	if (ret < 0) {
395 		LOG_ERR("Cannot set default range for gyroscope.");
396 		return ret;
397 	}
398 
399 	/* set gyro default bandwidth */
400 	ret = bmi08x_gyro_byte_write(dev, BMI08X_REG_GYRO_BANDWIDTH, config->gyro_hz);
401 	if (ret < 0) {
402 		LOG_ERR("Failed to set gyro's default ODR.");
403 		return ret;
404 	}
405 
406 #ifdef CONFIG_BMI08X_GYRO_TRIGGER
407 	ret = bmi08x_gyr_trigger_mode_init(dev);
408 	if (ret < 0) {
409 		LOG_ERR("Cannot set up trigger mode.");
410 		return ret;
411 	}
412 #endif
413 /* with BMI08X_DATA_SYNC set, it is expected that the INT3 or INT4 is wired to either INT1
414  * or INT2
415  */
416 #if defined(CONFIG_BMI08X_GYRO_TRIGGER) || BMI08X_GYRO_ANY_INST_HAS_DATA_SYNC
417 	/* set gyro ints */
418 	/* set ints */
419 	ret = bmi08x_gyro_byte_write(dev, BMI08X_REG_GYRO_INT_CTRL, 0x80);
420 	if (ret < 0) {
421 		LOG_ERR("Failed to map interrupts.");
422 		return ret;
423 	}
424 	ret = bmi08x_gyro_byte_write(dev, BMI08X_REG_GYRO_INT3_INT4_IO_CONF,
425 				     config->int3_4_conf_io);
426 	if (ret < 0) {
427 		LOG_ERR("Failed to map interrupts.");
428 		return ret;
429 	}
430 	ret = bmi08x_gyro_byte_write(dev, BMI08X_REG_GYRO_INT3_INT4_IO_MAP, config->int3_4_map);
431 	if (ret < 0) {
432 		LOG_ERR("Failed to map interrupts.");
433 		return ret;
434 	}
435 #endif
436 
437 	return ret;
438 }
439 
440 #define BMI08X_CONFIG_SPI(inst)                                                                    \
441 	.bus.spi = SPI_DT_SPEC_INST_GET(                                                           \
442 		inst, SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8), 2),
443 
444 #define BMI08X_CONFIG_I2C(inst) .bus.i2c = I2C_DT_SPEC_INST_GET(inst),
445 
446 #define BMI08X_GYRO_TRIG(inst)                                                                     \
447 	.int3_4_map = DT_INST_PROP(inst, int3_4_map_io),                                           \
448 	.int3_4_conf_io = DT_INST_PROP(inst, int3_4_conf_io),
449 
450 #if BMI08X_GYRO_ANY_INST_HAS_DATA_SYNC
451 /* the bmi08x-gyro should not have trigger mode with data-sync enabled */
452 BUILD_ASSERT(CONFIG_BMI08X_GYRO_TRIGGER_NONE,
453 	     "Only none trigger type allowed for bmi08x-gyro with data-sync enabled");
454 /* with data-sync, one of the int pins should be wired directory to the accel's int pins, their
455  * config should be defined
456  */
457 #define BMI08X_GYRO_TRIGGER_PINS(inst) BMI08X_GYRO_TRIG(inst)
458 #else
459 #define BMI08X_GYRO_TRIGGER_PINS(inst)                                                             \
460 	IF_ENABLED(CONFIG_BMI08X_GYRO_TRIGGER, (BMI08X_GYRO_TRIG(inst)))
461 #endif
462 
463 #define BMI08X_CREATE_INST(inst)                                                                   \
464                                                                                                    \
465 	static struct bmi08x_gyro_data bmi08x_drv_##inst;                                          \
466                                                                                                    \
467 	static const struct bmi08x_gyro_config bmi08x_config_##inst = {                            \
468 		COND_CODE_1(DT_INST_ON_BUS(inst, spi), (BMI08X_CONFIG_SPI(inst)),                  \
469 			    (BMI08X_CONFIG_I2C(inst)))                                             \
470 			.api = COND_CODE_1(DT_INST_ON_BUS(inst, spi), (&bmi08x_spi_api),           \
471 					   (&bmi08x_i2c_api)),                                     \
472 		IF_ENABLED(CONFIG_BMI08X_GYRO_TRIGGER,                                             \
473 			   (.int_gpio = GPIO_DT_SPEC_INST_GET(inst, int_gpios),))                  \
474 			.gyro_hz = DT_INST_ENUM_IDX(inst, gyro_hz),                                \
475 		BMI08X_GYRO_TRIGGER_PINS(inst).gyro_fs = DT_INST_PROP(inst, gyro_fs),              \
476 	};                                                                                         \
477                                                                                                    \
478 	PM_DEVICE_DT_INST_DEFINE(inst, bmi08x_gyro_pm_action);                                     \
479 	SENSOR_DEVICE_DT_INST_DEFINE(inst, bmi08x_gyro_init, PM_DEVICE_DT_INST_GET(inst),          \
480 				     &bmi08x_drv_##inst, &bmi08x_config_##inst, POST_KERNEL,       \
481 				     CONFIG_SENSOR_INIT_PRIORITY, &bmi08x_api);
482 
483 /* Create the struct device for every status "okay" node in the devicetree. */
484 DT_INST_FOREACH_STATUS_OKAY(BMI08X_CREATE_INST)
485