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, ®, 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 = ®, .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