/* * Copyright (c) 2023 Elektronikutvecklingsbyrån EUB AB * * SPDX-License-Identifier: Apache-2.0 */ #include #include LOG_MODULE_DECLARE(bmi270); #include "bmi270.h" enum { INT_FLAGS_INT1, INT_FLAGS_INT2, }; static void bmi270_raise_int_flag(const struct device *dev, int bit) { struct bmi270_data *data = dev->data; atomic_set_bit(&data->int_flags, bit); #if defined(CONFIG_BMI270_TRIGGER_OWN_THREAD) k_sem_give(&data->trig_sem); #elif defined(CONFIG_BMI270_TRIGGER_GLOBAL_THREAD) k_work_submit(&data->trig_work); #endif } static void bmi270_int1_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { struct bmi270_data *data = CONTAINER_OF(cb, struct bmi270_data, int1_cb); bmi270_raise_int_flag(data->dev, INT_FLAGS_INT1); } static void bmi270_int2_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { struct bmi270_data *data = CONTAINER_OF(cb, struct bmi270_data, int2_cb); bmi270_raise_int_flag(data->dev, INT_FLAGS_INT2); } static void bmi270_thread_cb(const struct device *dev) { struct bmi270_data *data = dev->data; int ret; /* INT1 is used for feature interrupts */ if (atomic_test_and_clear_bit(&data->int_flags, INT_FLAGS_INT1)) { uint16_t int_status; ret = bmi270_reg_read(dev, BMI270_REG_INT_STATUS_0, (uint8_t *)&int_status, sizeof(int_status)); if (ret < 0) { LOG_ERR("read interrupt status returned %d", ret); return; } k_mutex_lock(&data->trigger_mutex, K_FOREVER); if (data->motion_handler != NULL) { if (int_status & BMI270_INT_STATUS_ANY_MOTION) { data->motion_handler(dev, data->motion_trigger); } } k_mutex_unlock(&data->trigger_mutex); } /* INT2 is used for data ready interrupts */ if (atomic_test_and_clear_bit(&data->int_flags, INT_FLAGS_INT2)) { k_mutex_lock(&data->trigger_mutex, K_FOREVER); if (data->drdy_handler != NULL) { data->drdy_handler(dev, data->drdy_trigger); } k_mutex_unlock(&data->trigger_mutex); } } #ifdef CONFIG_BMI270_TRIGGER_OWN_THREAD static void bmi270_thread(void *p1, void *p2, void *p3) { ARG_UNUSED(p2); ARG_UNUSED(p3); struct bmi270_data *data = p1; while (1) { k_sem_take(&data->trig_sem, K_FOREVER); bmi270_thread_cb(data->dev); } } #endif #ifdef CONFIG_BMI270_TRIGGER_GLOBAL_THREAD static void bmi270_trig_work_cb(struct k_work *work) { struct bmi270_data *data = CONTAINER_OF(work, struct bmi270_data, trig_work); bmi270_thread_cb(data->dev); } #endif static int bmi270_feature_reg_write(const struct device *dev, const struct bmi270_feature_reg *reg, uint16_t value) { int ret; uint8_t feat_page = reg->page; ret = bmi270_reg_write(dev, BMI270_REG_FEAT_PAGE, &feat_page, 1); if (ret < 0) { LOG_ERR("bmi270_reg_write (0x%02x) failed: %d", BMI270_REG_FEAT_PAGE, ret); return ret; } LOG_DBG("feature reg[0x%02x]@%d = 0x%04x", reg->addr, reg->page, value); ret = bmi270_reg_write(dev, reg->addr, (uint8_t *)&value, 2); if (ret < 0) { LOG_ERR("bmi270_reg_write (0x%02x) failed: %d", reg->addr, ret); return ret; } return 0; } static int bmi270_init_int_pin(const struct gpio_dt_spec *pin, struct gpio_callback *pin_cb, gpio_callback_handler_t handler) { int ret; if (!pin->port) { return 0; } if (!device_is_ready(pin->port)) { LOG_DBG("%s not ready", pin->port->name); return -ENODEV; } gpio_init_callback(pin_cb, handler, BIT(pin->pin)); ret = gpio_pin_configure_dt(pin, GPIO_INPUT); if (ret) { return ret; } ret = gpio_pin_interrupt_configure_dt(pin, GPIO_INT_EDGE_TO_ACTIVE); if (ret) { return ret; } ret = gpio_add_callback(pin->port, pin_cb); if (ret) { return ret; } return 0; } int bmi270_init_interrupts(const struct device *dev) { const struct bmi270_config *cfg = dev->config; struct bmi270_data *data = dev->data; int ret; #if CONFIG_BMI270_TRIGGER_OWN_THREAD k_sem_init(&data->trig_sem, 0, 1); k_thread_create(&data->thread, data->thread_stack, CONFIG_BMI270_THREAD_STACK_SIZE, bmi270_thread, data, NULL, NULL, K_PRIO_COOP(CONFIG_BMI270_THREAD_PRIORITY), 0, K_NO_WAIT); #elif CONFIG_BMI270_TRIGGER_GLOBAL_THREAD k_work_init(&data->trig_work, bmi270_trig_work_cb); #endif ret = bmi270_init_int_pin(&cfg->int1, &data->int1_cb, bmi270_int1_callback); if (ret) { LOG_ERR("Failed to initialize INT1"); return -EINVAL; } ret = bmi270_init_int_pin(&cfg->int2, &data->int2_cb, bmi270_int2_callback); if (ret) { LOG_ERR("Failed to initialize INT2"); return -EINVAL; } if (cfg->int1.port) { uint8_t int1_io_ctrl = BMI270_INT_IO_CTRL_OUTPUT_EN; ret = bmi270_reg_write(dev, BMI270_REG_INT1_IO_CTRL, &int1_io_ctrl, 1); if (ret < 0) { LOG_ERR("failed configuring INT1_IO_CTRL (%d)", ret); return ret; } } if (cfg->int2.port) { uint8_t int2_io_ctrl = BMI270_INT_IO_CTRL_OUTPUT_EN; ret = bmi270_reg_write(dev, BMI270_REG_INT2_IO_CTRL, &int2_io_ctrl, 1); if (ret < 0) { LOG_ERR("failed configuring INT2_IO_CTRL (%d)", ret); return ret; } } return 0; } static int bmi270_anymo_config(const struct device *dev, bool enable) { const struct bmi270_config *cfg = dev->config; struct bmi270_data *data = dev->data; uint16_t anymo_2; int ret; if (enable) { ret = bmi270_feature_reg_write(dev, cfg->feature->anymo_1, data->anymo_1); if (ret < 0) { return ret; } } anymo_2 = data->anymo_2; if (enable) { anymo_2 |= BMI270_ANYMO_2_ENABLE; } ret = bmi270_feature_reg_write(dev, cfg->feature->anymo_2, anymo_2); if (ret < 0) { return ret; } uint8_t int1_map_feat = 0; if (enable) { int1_map_feat |= BMI270_INT_MAP_ANY_MOTION; } ret = bmi270_reg_write(dev, BMI270_REG_INT1_MAP_FEAT, &int1_map_feat, 1); if (ret < 0) { LOG_ERR("failed configuring INT1_MAP_FEAT (%d)", ret); return ret; } return 0; } static int bmi270_drdy_config(const struct device *dev, bool enable) { int ret; uint8_t int_map_data = 0; if (enable) { int_map_data |= BMI270_INT_MAP_DATA_DRDY_INT2; } ret = bmi270_reg_write(dev, BMI270_REG_INT_MAP_DATA, &int_map_data, 1); if (ret < 0) { LOG_ERR("failed configuring INT_MAP_DATA (%d)", ret); return ret; } return 0; } int bmi270_trigger_set(const struct device *dev, const struct sensor_trigger *trig, sensor_trigger_handler_t handler) { struct bmi270_data *data = dev->data; const struct bmi270_config *cfg = dev->config; switch (trig->type) { case SENSOR_TRIG_MOTION: if (!cfg->int1.port) { return -ENOTSUP; } k_mutex_lock(&data->trigger_mutex, K_FOREVER); data->motion_handler = handler; data->motion_trigger = trig; k_mutex_unlock(&data->trigger_mutex); return bmi270_anymo_config(dev, handler != NULL); case SENSOR_TRIG_DATA_READY: if (!cfg->int2.port) { return -ENOTSUP; } k_mutex_lock(&data->trigger_mutex, K_FOREVER); data->drdy_handler = handler; data->drdy_trigger = trig; k_mutex_unlock(&data->trigger_mutex); return bmi270_drdy_config(dev, handler != NULL); default: return -ENOTSUP; } return 0; }