diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index ca65a412800..679d9c3c6e4 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -62,6 +62,7 @@ add_subdirectory_ifdef(CONFIG_TH02 th02) add_subdirectory_ifdef(CONFIG_TSIC_XX6 tsic_xx6) add_subdirectory_ifdef(CONFIG_VEAA_X_3 veaa_x_3) add_subdirectory_ifdef(CONFIG_VOLTAGE_DIVIDER voltage_divider) +add_subdirectory_ifdef(CONFIG_XBR818 xbr818) add_subdirectory_ifdef(CONFIG_TACH_ENE_KB1200 ene_tach_kb1200) zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/sensor.h) diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index 452d8182eb7..4c6bfc3c413 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -146,6 +146,7 @@ source "drivers/sensor/th02/Kconfig" source "drivers/sensor/tsic_xx6/Kconfig" source "drivers/sensor/veaa_x_3/Kconfig" source "drivers/sensor/voltage_divider/Kconfig" +source "drivers/sensor/xbr818/Kconfig" source "drivers/sensor/ene_tach_kb1200/Kconfig" endif # SENSOR diff --git a/drivers/sensor/bosch/CMakeLists.txt b/drivers/sensor/bosch/CMakeLists.txt index 4125ab1e7ed..8c1207bfa20 100644 --- a/drivers/sensor/bosch/CMakeLists.txt +++ b/drivers/sensor/bosch/CMakeLists.txt @@ -13,6 +13,7 @@ add_subdirectory_ifdef(CONFIG_BMI160 bmi160) add_subdirectory_ifdef(CONFIG_BMI270 bmi270) add_subdirectory_ifdef(CONFIG_BMI323 bmi323) add_subdirectory_ifdef(CONFIG_BMM150 bmm150) +add_subdirectory_ifdef(CONFIG_BMM350 bmm350) add_subdirectory_ifdef(CONFIG_BMP180 bmp180) add_subdirectory_ifdef(CONFIG_BMP388 bmp388) add_subdirectory_ifdef(CONFIG_BMP581 bmp581) diff --git a/drivers/sensor/bosch/Kconfig b/drivers/sensor/bosch/Kconfig index 795284d3a2d..b0232edfce0 100644 --- a/drivers/sensor/bosch/Kconfig +++ b/drivers/sensor/bosch/Kconfig @@ -13,6 +13,7 @@ source "drivers/sensor/bosch/bmi160/Kconfig" source "drivers/sensor/bosch/bmi270/Kconfig" source "drivers/sensor/bosch/bmi323/Kconfig" source "drivers/sensor/bosch/bmm150/Kconfig" +source "drivers/sensor/bosch/bmm350/Kconfig" source "drivers/sensor/bosch/bmp180/Kconfig" source "drivers/sensor/bosch/bmp388/Kconfig" source "drivers/sensor/bosch/bmp581/Kconfig" diff --git a/drivers/sensor/bosch/bmm350/CMakeLists.txt b/drivers/sensor/bosch/bmm350/CMakeLists.txt new file mode 100644 index 00000000000..3ccf040f8b8 --- /dev/null +++ b/drivers/sensor/bosch/bmm350/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (c) 2024 Bosch Sensortec GmbH + +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_sources(bmm350.c bmm350_i2c.c) +zephyr_library_sources_ifdef(CONFIG_BMM350_TRIGGER bmm350_trigger.c) diff --git a/drivers/sensor/bosch/bmm350/Kconfig b/drivers/sensor/bosch/bmm350/Kconfig new file mode 100644 index 00000000000..1280f009a70 --- /dev/null +++ b/drivers/sensor/bosch/bmm350/Kconfig @@ -0,0 +1,22 @@ +# BMM350 Geomagnetic sensor configuration options + +# Copyright (c) 2024 Bosch Sensortec GmbH + +# SPDX-License-Identifier: Apache-2.0 + +menuconfig BMM350 + bool "BMM350 I2C Geomagnetic Chip" + default y + depends on DT_HAS_BOSCH_BMM350_ENABLED + select I2C + help + Enable driver for BMM350 I2C-based Geomagnetic sensor. + +if BMM350 + +module = BMM350 +thread_priority = 10 +thread_stack_size = 1024 +source "drivers/sensor/Kconfig.trigger_template" + +endif # BMM350 diff --git a/drivers/sensor/bosch/bmm350/bmm350.c b/drivers/sensor/bosch/bmm350/bmm350.c new file mode 100644 index 00000000000..901d3ffa734 --- /dev/null +++ b/drivers/sensor/bosch/bmm350/bmm350.c @@ -0,0 +1,988 @@ +/* + * Copyright (c) 2024 Bosch Sensortec GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Bus-specific functionality for BMM350s accessed via I2C. + * version 1.0.0 + */ + +#include +#include "bmm350.h" + +LOG_MODULE_REGISTER(BMM350, CONFIG_SENSOR_LOG_LEVEL); + +static inline int bmm350_bus_check(const struct device *dev) +{ + const struct bmm350_config *cfg = dev->config; + + return cfg->bus_io->check(&cfg->bus); +} + +static inline int bmm350_reg_read(const struct device *dev, uint8_t start, uint8_t *buf, int size) +{ + const struct bmm350_config *cfg = dev->config; + + return cfg->bus_io->read(&cfg->bus, start, buf, size); +} + +int bmm350_reg_write(const struct device *dev, uint8_t reg, uint8_t val) +{ + const struct bmm350_config *cfg = dev->config; + + return cfg->bus_io->write(&cfg->bus, reg, val); +} + +static int8_t bmm350_read_otp_word(const struct device *dev, uint8_t addr, uint16_t *lsb_msb) +{ + int8_t ret = 0; + uint8_t tx_buf = 0; + uint8_t rx_buf[3] = {0x00}; + uint8_t otp_status = 0; + uint8_t otp_err = BMM350_OTP_STATUS_NO_ERROR; + uint8_t lsb = 0, msb = 0; + + if (lsb_msb) { + /* Set OTP command at specified address */ + tx_buf = BMM350_OTP_CMD_DIR_READ | (addr & BMM350_OTP_WORD_ADDR_MSK); + ret = bmm350_reg_write(dev, BMM350_REG_OTP_CMD_REG, tx_buf); + if (ret) { + LOG_ERR("i2c xfer failed! read addr = 0x%02x, ret = %d\n", tx_buf, ret); + return ret; + } + + do { + /* Get OTP status */ + ret += bmm350_reg_read(dev, BMM350_REG_OTP_STATUS_REG, &rx_buf[0], 3); + otp_status = rx_buf[2]; + otp_err = BMM350_OTP_STATUS_ERROR(otp_status); + if (otp_err != BMM350_OTP_STATUS_NO_ERROR) { + break; + } + } while ((!(otp_status & BMM350_OTP_STATUS_CMD_DONE)) && (ret == BMM350_OK)); + + if (otp_err != BMM350_OTP_STATUS_NO_ERROR) { + LOG_ERR("OTP error code: 0x%02x\n", otp_err); + return -EIO; + } + + /* Get OTP L/MSB data */ + ret += bmm350_reg_read(dev, BMM350_REG_OTP_DATA_MSB_REG, &rx_buf[0], 3); + msb = rx_buf[2]; + ret += bmm350_reg_read(dev, BMM350_REG_OTP_DATA_LSB_REG, &rx_buf[0], 3); + lsb = rx_buf[2]; + *lsb_msb = ((uint16_t)(msb << 8) | lsb) & 0xFFFF; + } + + return ret; +} + +static int32_t fix_sign(uint32_t inval, int8_t number_of_bits) +{ + int32_t ret = 0; + int32_t power = 0; + + switch ((enum bmm350_signed_bit)number_of_bits) { + case BMM350_SIGNED_8_BIT: + power = 128; /* 2^7 */ + break; + case BMM350_SIGNED_12_BIT: + power = 2048; /* 2^11 */ + break; + case BMM350_SIGNED_16_BIT: + power = 32768; /* 2^15 */ + break; + case BMM350_SIGNED_21_BIT: + power = 1048576; /* 2^20 */ + break; + case BMM350_SIGNED_24_BIT: + power = 8388608; /* 2^23 */ + break; + default: + power = 0; + break; + } + + ret = (int32_t)inval; + if (ret >= power) { + ret = ret - (power * 2); + } + + return ret; +} + +static void bmm350_update_mag_off_sens(struct bmm350_data *data) +{ + uint16_t off_x_lsb_msb, off_y_lsb_msb, off_z_lsb_msb, t_off = 0; + uint8_t sens_x, sens_y, sens_z, t_sens = 0; + uint8_t tco_x, tco_y, tco_z = 0; + uint8_t tcs_x, tcs_y, tcs_z = 0; + uint8_t cross_x_y, cross_y_x, cross_z_x, cross_z_y = 0; + + off_x_lsb_msb = data->otp_data[BMM350_MAG_OFFSET_X] & 0x0FFF; + off_y_lsb_msb = ((data->otp_data[BMM350_MAG_OFFSET_X] & 0xF000) >> 4) + + (data->otp_data[BMM350_MAG_OFFSET_Y] & BMM350_LSB_MASK); + off_z_lsb_msb = (data->otp_data[BMM350_MAG_OFFSET_Y] & 0x0F00) + + (data->otp_data[BMM350_MAG_OFFSET_Z] & BMM350_LSB_MASK); + t_off = data->otp_data[BMM350_TEMP_OFF_SENS] & BMM350_LSB_MASK; + + data->mag_comp.dut_offset_coef.offset_x = fix_sign(off_x_lsb_msb, BMM350_SIGNED_12_BIT); + data->mag_comp.dut_offset_coef.offset_y = fix_sign(off_y_lsb_msb, BMM350_SIGNED_12_BIT); + data->mag_comp.dut_offset_coef.offset_z = fix_sign(off_z_lsb_msb, BMM350_SIGNED_12_BIT); + data->mag_comp.dut_offset_coef.t_offs = + fix_sign(t_off, BMM350_SIGNED_8_BIT) * BMM350_MAG_COMP_COEFF_SCALING; + data->mag_comp.dut_offset_coef.t_offs = data->mag_comp.dut_offset_coef.t_offs / 5; + + sens_x = (data->otp_data[BMM350_MAG_SENS_X] & BMM350_MSB_MASK) >> 8; + sens_y = (data->otp_data[BMM350_MAG_SENS_Y] & BMM350_LSB_MASK); + sens_z = (data->otp_data[BMM350_MAG_SENS_Z] & BMM350_MSB_MASK) >> 8; + t_sens = (data->otp_data[BMM350_TEMP_OFF_SENS] & BMM350_MSB_MASK) >> 8; + + data->mag_comp.dut_sensit_coef.sens_x = + fix_sign(sens_x, BMM350_SIGNED_8_BIT) * BMM350_MAG_COMP_COEFF_SCALING; + data->mag_comp.dut_sensit_coef.sens_y = + fix_sign(sens_y, BMM350_SIGNED_8_BIT) * BMM350_MAG_COMP_COEFF_SCALING; + data->mag_comp.dut_sensit_coef.sens_z = + fix_sign(sens_z, BMM350_SIGNED_8_BIT) * BMM350_MAG_COMP_COEFF_SCALING; + data->mag_comp.dut_sensit_coef.t_sens = + fix_sign(t_sens, BMM350_SIGNED_8_BIT) * BMM350_MAG_COMP_COEFF_SCALING; + + data->mag_comp.dut_sensit_coef.sens_x = (data->mag_comp.dut_sensit_coef.sens_x / 256); + data->mag_comp.dut_sensit_coef.sens_y = (data->mag_comp.dut_sensit_coef.sens_y / 256); + data->mag_comp.dut_sensit_coef.sens_z = (data->mag_comp.dut_sensit_coef.sens_z / 256); + data->mag_comp.dut_sensit_coef.t_sens = (data->mag_comp.dut_sensit_coef.t_sens / 512); + + tco_x = (data->otp_data[BMM350_MAG_TCO_X] & BMM350_LSB_MASK); + tco_y = (data->otp_data[BMM350_MAG_TCO_Y] & BMM350_LSB_MASK); + tco_z = (data->otp_data[BMM350_MAG_TCO_Z] & BMM350_LSB_MASK); + + data->mag_comp.dut_tco.tco_x = + fix_sign(tco_x, BMM350_SIGNED_8_BIT) * BMM350_MAG_COMP_COEFF_SCALING; + data->mag_comp.dut_tco.tco_y = + fix_sign(tco_y, BMM350_SIGNED_8_BIT) * BMM350_MAG_COMP_COEFF_SCALING; + data->mag_comp.dut_tco.tco_z = + fix_sign(tco_z, BMM350_SIGNED_8_BIT) * BMM350_MAG_COMP_COEFF_SCALING; + + data->mag_comp.dut_tco.tco_x = (data->mag_comp.dut_tco.tco_x / 32); + data->mag_comp.dut_tco.tco_y = (data->mag_comp.dut_tco.tco_y / 32); + data->mag_comp.dut_tco.tco_z = (data->mag_comp.dut_tco.tco_z / 32); + + tcs_x = (data->otp_data[BMM350_MAG_TCS_X] & BMM350_MSB_MASK) >> 8; + tcs_y = (data->otp_data[BMM350_MAG_TCS_Y] & BMM350_MSB_MASK) >> 8; + tcs_z = (data->otp_data[BMM350_MAG_TCS_Z] & BMM350_MSB_MASK) >> 8; + + data->mag_comp.dut_tcs.tcs_x = + fix_sign(tcs_x, BMM350_SIGNED_8_BIT) * BMM350_MAG_COMP_COEFF_SCALING; + data->mag_comp.dut_tcs.tcs_y = + fix_sign(tcs_y, BMM350_SIGNED_8_BIT) * BMM350_MAG_COMP_COEFF_SCALING; + data->mag_comp.dut_tcs.tcs_z = + fix_sign(tcs_z, BMM350_SIGNED_8_BIT) * BMM350_MAG_COMP_COEFF_SCALING; + + data->mag_comp.dut_tcs.tcs_x = (data->mag_comp.dut_tcs.tcs_x / 16384); + data->mag_comp.dut_tcs.tcs_y = (data->mag_comp.dut_tcs.tcs_y / 16384); + data->mag_comp.dut_tcs.tcs_z = (data->mag_comp.dut_tcs.tcs_z / 16384); + + data->mag_comp.dut_t0 = + (fix_sign(data->otp_data[BMM350_MAG_DUT_T_0], BMM350_SIGNED_16_BIT) / 512) + 23; + + cross_x_y = (data->otp_data[BMM350_CROSS_X_Y] & BMM350_LSB_MASK); + cross_y_x = (data->otp_data[BMM350_CROSS_Y_X] & BMM350_MSB_MASK) >> 8; + cross_z_x = (data->otp_data[BMM350_CROSS_Z_X] & BMM350_LSB_MASK); + cross_z_y = (data->otp_data[BMM350_CROSS_Z_Y] & BMM350_MSB_MASK) >> 8; + + data->mag_comp.cross_axis.cross_x_y = + fix_sign(cross_x_y, BMM350_SIGNED_8_BIT) * BMM350_MAG_COMP_COEFF_SCALING; + data->mag_comp.cross_axis.cross_y_x = + fix_sign(cross_y_x, BMM350_SIGNED_8_BIT) * BMM350_MAG_COMP_COEFF_SCALING; + data->mag_comp.cross_axis.cross_z_x = + fix_sign(cross_z_x, BMM350_SIGNED_8_BIT) * BMM350_MAG_COMP_COEFF_SCALING; + data->mag_comp.cross_axis.cross_z_y = + fix_sign(cross_z_y, BMM350_SIGNED_8_BIT) * BMM350_MAG_COMP_COEFF_SCALING; + + data->mag_comp.cross_axis.cross_x_y = (data->mag_comp.cross_axis.cross_x_y / 800); + data->mag_comp.cross_axis.cross_y_x = (data->mag_comp.cross_axis.cross_y_x / 800); + data->mag_comp.cross_axis.cross_z_x = (data->mag_comp.cross_axis.cross_z_x / 800); + data->mag_comp.cross_axis.cross_z_y = (data->mag_comp.cross_axis.cross_z_y / 800); +} + +static int bmm350_otp_dump_after_boot(const struct device *dev) +{ + struct bmm350_data *data = dev->data; + int ret = 0; + uint16_t otp_word = 0; + + for (uint8_t idx = 0; idx < BMM350_OTP_DATA_LENGTH; idx++) { + ret = bmm350_read_otp_word(dev, idx, &otp_word); + data->otp_data[idx] = otp_word; + } + + data->var_id = (data->otp_data[30] & 0x7f00) >> 9; + /* Set the default auto bit reset configuration */ + data->enable_auto_br = ((data->var_id > BMM350_CURRENT_SHUTTLE_VARIANT_ID) ? BMM350_DISABLE + : BMM350_ENABLE); + + LOG_DBG("bmm350 Find the var id %d\n", data->var_id); + /* Update magnetometer offset and sensitivity data. */ + bmm350_update_mag_off_sens(data); + + if (ret) { + LOG_ERR("i2c xfer failed, ret = %d\n", ret); + } + + return ret; +} + +/*! + * @brief This API gets the PMU command status 0 value + */ +static int8_t bmm350_get_pmu_cmd_status_0(const struct device *dev, + struct bmm350_pmu_cmd_status_0 *pmu_cmd_stat_0) +{ + /* Variable to store the function result */ + int8_t ret; + uint8_t rx_buf[3] = {0x00}; + + if (pmu_cmd_stat_0 != NULL) { + /* Get PMU command status 0 data */ + ret = bmm350_reg_read(dev, BMM350_REG_PMU_CMD_STATUS_0, &rx_buf[0], 3); + LOG_DBG("pmu cmd status 0:0x%x\n", rx_buf[2]); + if (ret == BMM350_OK) { + pmu_cmd_stat_0->pmu_cmd_busy = + BMM350_GET_BITS_POS_0(rx_buf[2], BMM350_PMU_CMD_BUSY); + pmu_cmd_stat_0->odr_ovwr = BMM350_GET_BITS(rx_buf[2], BMM350_ODR_OVWR); + pmu_cmd_stat_0->avr_ovwr = BMM350_GET_BITS(rx_buf[2], BMM350_AVG_OVWR); + pmu_cmd_stat_0->pwr_mode_is_normal = + BMM350_GET_BITS(rx_buf[2], BMM350_PWR_MODE_IS_NORMAL); + pmu_cmd_stat_0->cmd_is_illegal = + BMM350_GET_BITS(rx_buf[2], BMM350_CMD_IS_ILLEGAL); + pmu_cmd_stat_0->pmu_cmd_value = + BMM350_GET_BITS(rx_buf[2], BMM350_PMU_CMD_VALUE); + } + } else { + ret = -EINVAL; + } + return ret; +} + +/*! + * @brief This internal API is used to switch from suspend mode to normal mode or forced mode. + */ +static int8_t set_powermode(const struct device *dev, enum bmm350_power_modes powermode) +{ + /* Variable to store the function result */ + int8_t ret = 0; + uint8_t rx_buf[3] = {0x00}; + uint8_t reg_data = powermode; + + /* Array to store suspend to forced mode delay */ + uint32_t sus_to_forced_mode[4] = { + BMM350_SUS_TO_FORCEDMODE_NO_AVG_DELAY, BMM350_SUS_TO_FORCEDMODE_AVG_2_DELAY, + BMM350_SUS_TO_FORCEDMODE_AVG_4_DELAY, BMM350_SUS_TO_FORCEDMODE_AVG_8_DELAY}; + + /* Array to store suspend to forced mode fast delay */ + uint32_t sus_to_forced_mode_fast[4] = {BMM350_SUS_TO_FORCEDMODE_FAST_NO_AVG_DELAY, + BMM350_SUS_TO_FORCEDMODE_FAST_AVG_2_DELAY, + BMM350_SUS_TO_FORCEDMODE_FAST_AVG_4_DELAY, + BMM350_SUS_TO_FORCEDMODE_FAST_AVG_8_DELAY}; + + uint8_t avg = 0; + uint32_t delay_us = 0; + + if (ret == BMM350_OK) { + /* Get average configuration */ + ret = bmm350_reg_read(dev, BMM350_REG_PMU_CMD_AGGR_SET, &rx_buf[0], 3); + /* Mask the average value */ + avg = ((rx_buf[2] & BMM350_AVG_MSK) >> BMM350_AVG_POS); + if (ret == BMM350_OK) { + /* Get average configuration */ + if (powermode == BMM350_NORMAL_MODE) { + delay_us = BMM350_SUSPEND_TO_NORMAL_DELAY; + } + + /* Check if desired power mode is forced mode */ + if (powermode == BMM350_FORCED_MODE) { + /* Store delay based on averaging mode */ + delay_us = sus_to_forced_mode[avg]; + } + + /* Check if desired power mode is forced mode fast */ + if (powermode == BMM350_FORCED_MODE_FAST) { + /* Store delay based on averaging mode */ + delay_us = sus_to_forced_mode_fast[avg]; + } + + /* Set PMU command configuration to desired power mode */ + ret = bmm350_reg_write(dev, BMM350_REG_PMU_CMD, reg_data); + k_usleep(delay_us); + } + } + + LOG_DBG("pmu cmd agget set powermode %d\n", powermode); + + return ret; +} + +/*! + * @brief This API is used to set the power mode of the sensor + */ +static int8_t bmm350_set_powermode(const struct device *dev, enum bmm350_power_modes powermode) +{ + /* Variable to store the function result */ + int8_t ret = 0; + uint8_t rx_buf[3] = {0x00}; + + if (ret == BMM350_OK) { + ret = bmm350_reg_read(dev, BMM350_REG_PMU_CMD, &rx_buf[0], 3); + if (ret == BMM350_OK) { + if (rx_buf[2] > BMM350_PMU_CMD_NM_TC) { + ret = -EINVAL; + } + + if ((ret == BMM350_OK) && ((rx_buf[2] == BMM350_PMU_CMD_NM) || + (rx_buf[2] == BMM350_PMU_CMD_UPD_OAE))) { + /* Set PMU command configuration */ + ret = bmm350_reg_write(dev, BMM350_REG_PMU_CMD, BMM350_PMU_CMD_SUS); + } + + if (ret == BMM350_OK) { + ret = set_powermode(dev, powermode); + } + } + } + + return ret; +} + +/*! + * used to perform the magnetic reset of the sensor + * which is necessary after a field shock ( 400mT field applied to sensor ) + */ +static int8_t bmm350_magnetic_reset_and_wait(const struct device *dev) +{ + /* Variable to store the function result */ + int8_t ret = 0; + struct bmm350_pmu_cmd_status_0 pmu_cmd_stat_0 = {0}; + uint8_t restore_normal = BMM350_DISABLE; + + /* Read PMU CMD status */ + ret = bmm350_get_pmu_cmd_status_0(dev, &pmu_cmd_stat_0); + LOG_DBG("get status result 0:%d\n", ret); + + /* Check the powermode is normal before performing magnetic reset */ + if ((ret == BMM350_OK) && (pmu_cmd_stat_0.pwr_mode_is_normal == BMM350_ENABLE)) { + restore_normal = BMM350_ENABLE; + /* Reset can only be triggered in suspend */ + ret = bmm350_set_powermode(dev, BMM350_SUSPEND_MODE); + LOG_DBG("set power mode 0:%d\n", ret); + } + if (ret == BMM350_OK) { + /* Set BR to PMU_CMD register */ + ret = bmm350_reg_write(dev, BMM350_REG_PMU_CMD, BMM350_PMU_CMD_BR); + k_usleep(BMM350_BR_DELAY); + } + + if (ret == BMM350_OK) { + /* Verify if PMU_CMD_STATUS_0 register has BR set */ + ret = bmm350_get_pmu_cmd_status_0(dev, &pmu_cmd_stat_0); + LOG_DBG("get status result 1:%d\n", ret); + if ((ret == BMM350_OK) && + (pmu_cmd_stat_0.pmu_cmd_value != BMM350_PMU_CMD_STATUS_0_BR)) { + ret = -EIO; + } + } + + if (ret == BMM350_OK) { + /* Set FGR to PMU_CMD register */ + ret = bmm350_reg_write(dev, BMM350_REG_PMU_CMD, BMM350_PMU_CMD_FGR); + k_usleep(BMM350_FGR_DELAY); + } + if (ret == BMM350_OK) { + /* Verify if PMU_CMD_STATUS_0 register has FGR set */ + ret = bmm350_get_pmu_cmd_status_0(dev, &pmu_cmd_stat_0); + LOG_DBG("get status result 2:%d\n", ret); + + if ((ret == BMM350_OK) && + (pmu_cmd_stat_0.pmu_cmd_value != BMM350_PMU_CMD_STATUS_0_FGR)) { + ret = -EIO; + } + } + + if ((ret == BMM350_OK) && (restore_normal == BMM350_ENABLE)) { + ret = bmm350_set_powermode(dev, BMM350_NORMAL_MODE); + LOG_DBG("set power mode 1:%d\n", ret); + } else { + if (ret == BMM350_OK) { + /* Reset PMU_CMD register */ + ret = bmm350_reg_write(dev, BMM350_REG_PMU_CMD, 0x00); + } + } + return ret; +} +/*! + * @brief This API is used to read uncompensated mag and temperature data. + */ +int8_t bmm350_read_uncomp_mag_temp_data(const struct device *dev, + struct bmm350_raw_mag_data *raw_data) +{ + struct bmm350_data *data = dev->data; + int8_t rslt = BMM350_OK; + uint8_t mag_data[14] = {0}; + uint32_t raw_mag_x, raw_mag_y, raw_mag_z, raw_temp; + + if (raw_data != NULL) { + /* Get uncompensated mag data */ + rslt = bmm350_reg_read(dev, BMM350_REG_MAG_X_XLSB, mag_data, 14); + + if (rslt == BMM350_OK) { + raw_mag_x = (uint32_t)mag_data[2] + ((uint32_t)mag_data[3] << 8) + + ((uint32_t)mag_data[4] << 16); + raw_mag_y = (uint32_t)mag_data[5] + ((uint32_t)mag_data[6] << 8) + + ((uint32_t)mag_data[7] << 16); + raw_mag_z = (uint32_t)mag_data[8] + ((uint32_t)mag_data[9] << 8) + + ((uint32_t)mag_data[10] << 16); + raw_temp = (uint32_t)mag_data[11] + ((uint32_t)mag_data[12] << 8) + + ((uint32_t)mag_data[13] << 16); + + if ((data->axis_en & BMM350_EN_X_MSK) == BMM350_DISABLE) { + raw_data->raw_xdata = BMM350_DISABLE; + } else { + raw_data->raw_xdata = fix_sign(raw_mag_x, BMM350_SIGNED_24_BIT); + } + + if ((data->axis_en & BMM350_EN_Y_MSK) == BMM350_DISABLE) { + raw_data->raw_ydata = BMM350_DISABLE; + } else { + raw_data->raw_ydata = fix_sign(raw_mag_y, BMM350_SIGNED_24_BIT); + } + + if ((data->axis_en & BMM350_EN_Z_MSK) == BMM350_DISABLE) { + raw_data->raw_zdata = BMM350_DISABLE; + } else { + raw_data->raw_zdata = fix_sign(raw_mag_z, BMM350_SIGNED_24_BIT); + } + + raw_data->raw_data_temp = fix_sign(raw_temp, BMM350_SIGNED_24_BIT); + } + } else { + rslt = -EINVAL; + } + + return rslt; +} +static int8_t read_out_raw_data(const struct device *dev, int32_t *out_data) +{ + int8_t rslt = BMM350_OK; + int32_t temp = 0; + struct bmm350_raw_mag_data raw_data = {0}; + + if (out_data != NULL) { + rslt = bmm350_read_uncomp_mag_temp_data(dev, &raw_data); + + if (rslt == BMM350_OK) { + /* Convert mag lsb to uT and temp lsb to degC */ + out_data[0] = ((raw_data.raw_xdata * BMM350_LSB_TO_UT_XY_COEFF) / + BMM350_LSB_TO_UT_COEFF_DIV); + out_data[1] = ((raw_data.raw_ydata * BMM350_LSB_TO_UT_XY_COEFF) / + BMM350_LSB_TO_UT_COEFF_DIV); + out_data[2] = ((raw_data.raw_zdata * BMM350_LSB_TO_UT_Z_COEFF) / + BMM350_LSB_TO_UT_COEFF_DIV); + out_data[3] = ((raw_data.raw_data_temp * BMM350_LSB_TO_UT_TEMP_COEFF) / + BMM350_LSB_TO_UT_COEFF_DIV); + + if (out_data[3] > 0) { + temp = (out_data[3] - (2549 / 100)); + } else if (out_data[3] < 0) { + temp = (out_data[3] + (2549 / 100)); + } else { + temp = out_data[3]; + } + + out_data[3] = temp; + } + } else { + rslt = -EINVAL; + } + + return rslt; +} + +int8_t bmm350_get_compensated_mag_xyz_temp_data_fixed(const struct device *dev, + struct bmm350_mag_temp_data *mag_temp_data) +{ + struct bmm350_data *data = dev->data; + int8_t rslt = BMM350_OK; + uint8_t indx; + int32_t out_data[4] = {0}; + int32_t dut_offset_coef[3], dut_sensit_coef[3], dut_tco[3], dut_tcs[3]; + int32_t cr_ax_comp_x, cr_ax_comp_y, cr_ax_comp_z; + + if (mag_temp_data != NULL) { + /* Reads raw magnetic x,y and z axis along with temperature */ + rslt = read_out_raw_data(dev, out_data); + + if (rslt == BMM350_OK) { + /* Apply compensation to temperature reading */ + out_data[3] = (((BMM350_MAG_COMP_COEFF_SCALING + + data->mag_comp.dut_sensit_coef.t_sens) * + out_data[3]) + + data->mag_comp.dut_offset_coef.t_offs) / + BMM350_MAG_COMP_COEFF_SCALING; + + /* Store magnetic compensation structure to an array */ + dut_offset_coef[0] = data->mag_comp.dut_offset_coef.offset_x; + dut_offset_coef[1] = data->mag_comp.dut_offset_coef.offset_y; + dut_offset_coef[2] = data->mag_comp.dut_offset_coef.offset_z; + + dut_sensit_coef[0] = data->mag_comp.dut_sensit_coef.sens_x; + dut_sensit_coef[1] = data->mag_comp.dut_sensit_coef.sens_y; + dut_sensit_coef[2] = data->mag_comp.dut_sensit_coef.sens_z; + + dut_tco[0] = data->mag_comp.dut_tco.tco_x; + dut_tco[1] = data->mag_comp.dut_tco.tco_y; + dut_tco[2] = data->mag_comp.dut_tco.tco_z; + + dut_tcs[0] = data->mag_comp.dut_tcs.tcs_x; + dut_tcs[1] = data->mag_comp.dut_tcs.tcs_y; + dut_tcs[2] = data->mag_comp.dut_tcs.tcs_z; + + /* Compensate raw magnetic data */ + for (indx = 0; indx < 3; indx++) { + out_data[indx] = (out_data[indx] * (BMM350_MAG_COMP_COEFF_SCALING + + dut_sensit_coef[indx])) / + BMM350_MAG_COMP_COEFF_SCALING; + out_data[indx] = (out_data[indx] + dut_offset_coef[indx]); + out_data[indx] = + ((out_data[indx] * BMM350_MAG_COMP_COEFF_SCALING) + + (dut_tco[indx] * (out_data[3] - data->mag_comp.dut_t0))) / + BMM350_MAG_COMP_COEFF_SCALING; + out_data[indx] = + (out_data[indx] * BMM350_MAG_COMP_COEFF_SCALING) / + (BMM350_MAG_COMP_COEFF_SCALING + + (dut_tcs[indx] * (out_data[3] - data->mag_comp.dut_t0))); + } + + cr_ax_comp_x = + ((((out_data[0] * BMM350_MAG_COMP_COEFF_SCALING) - + (data->mag_comp.cross_axis.cross_x_y * out_data[1])) * + BMM350_MAG_COMP_COEFF_SCALING) / + ((BMM350_MAG_COMP_COEFF_SCALING * BMM350_MAG_COMP_COEFF_SCALING) - + (data->mag_comp.cross_axis.cross_y_x * + data->mag_comp.cross_axis.cross_x_y))); + + cr_ax_comp_y = + ((((out_data[1] * BMM350_MAG_COMP_COEFF_SCALING) - + (data->mag_comp.cross_axis.cross_y_x * out_data[0])) * + BMM350_MAG_COMP_COEFF_SCALING) / + ((BMM350_MAG_COMP_COEFF_SCALING * BMM350_MAG_COMP_COEFF_SCALING) - + (data->mag_comp.cross_axis.cross_y_x * + data->mag_comp.cross_axis.cross_x_y))); + + cr_ax_comp_z = + (out_data[2] + + (((out_data[0] * ((data->mag_comp.cross_axis.cross_y_x * + data->mag_comp.cross_axis.cross_z_y) - + (data->mag_comp.cross_axis.cross_z_x * + BMM350_MAG_COMP_COEFF_SCALING))) - + (out_data[1] * ((data->mag_comp.cross_axis.cross_z_y * + BMM350_MAG_COMP_COEFF_SCALING) - + (data->mag_comp.cross_axis.cross_x_y * + data->mag_comp.cross_axis.cross_z_x))))) / + (((BMM350_MAG_COMP_COEFF_SCALING * + BMM350_MAG_COMP_COEFF_SCALING) - + data->mag_comp.cross_axis.cross_y_x * + data->mag_comp.cross_axis.cross_x_y))); + + out_data[0] = (int32_t)cr_ax_comp_x; + out_data[1] = (int32_t)cr_ax_comp_y; + out_data[2] = (int32_t)cr_ax_comp_z; + } + LOG_DBG("mag data %d %d %d\n", out_data[0], out_data[1], out_data[2]); + + if (rslt == BMM350_OK) { + if ((data->axis_en & BMM350_EN_X_MSK) == BMM350_DISABLE) { + mag_temp_data->x = BMM350_DISABLE; + } else { + mag_temp_data->x = out_data[0]; + } + + if ((data->axis_en & BMM350_EN_Y_MSK) == BMM350_DISABLE) { + mag_temp_data->y = BMM350_DISABLE; + } else { + mag_temp_data->y = out_data[1]; + } + + if ((data->axis_en & BMM350_EN_Z_MSK) == BMM350_DISABLE) { + mag_temp_data->z = BMM350_DISABLE; + } else { + mag_temp_data->z = out_data[2]; + } + mag_temp_data->temperature = out_data[3]; + } + } else { + rslt = -EINVAL; + } + return rslt; +} + +static int bmm350_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + struct bmm350_data *drv_data = dev->data; + struct bmm350_mag_temp_data mag_temp_data; + + if (bmm350_get_compensated_mag_xyz_temp_data_fixed(dev, &mag_temp_data) < 0) { + LOG_ERR("failed to read sample"); + return -EIO; + } + drv_data->mag_temp_data.x = mag_temp_data.x; + drv_data->mag_temp_data.y = mag_temp_data.y; + drv_data->mag_temp_data.z = mag_temp_data.z; + return 0; +} + +/* + * ut change to Gauss + */ +static void bmm350_convert(struct sensor_value *val, int raw_val) +{ + val->val1 = raw_val / 100; + val->val2 = (raw_val % 100) * 10000; +} + +static int bmm350_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + struct bmm350_data *drv_data = dev->data; + + switch (chan) { + case SENSOR_CHAN_MAGN_X: + bmm350_convert(val, drv_data->mag_temp_data.x); + break; + case SENSOR_CHAN_MAGN_Y: + bmm350_convert(val, drv_data->mag_temp_data.y); + break; + case SENSOR_CHAN_MAGN_Z: + bmm350_convert(val, drv_data->mag_temp_data.z); + break; + case SENSOR_CHAN_MAGN_XYZ: + bmm350_convert(val, drv_data->mag_temp_data.x); + bmm350_convert(val + 1, drv_data->mag_temp_data.y); + bmm350_convert(val + 2, drv_data->mag_temp_data.z); + break; + default: + return -ENOTSUP; + } + + return 0; +} +static uint8_t acc_odr_to_reg(const struct sensor_value *val) +{ + double odr = sensor_value_to_double((struct sensor_value *)val); + + uint8_t reg = BMM350_DATA_RATE_100HZ; + + if ((odr >= 0.78125) && (odr <= 1.5625)) { + reg = BMM350_DATA_RATE_1_5625HZ; + } else if ((odr > 1.5625) && (odr <= 3.125)) { + reg = BMM350_DATA_RATE_3_125HZ; + } else if ((odr > 3.125) && (odr <= 6.25)) { + reg = BMM350_DATA_RATE_6_25HZ; + } else if ((odr > 6.25) && (odr <= 12.5)) { + reg = BMM350_DATA_RATE_12_5HZ; + } else if ((odr > 12.5) && (odr <= 25.0)) { + reg = BMM350_DATA_RATE_25HZ; + } else if ((odr > 25.0) && (odr <= 50.0)) { + reg = BMM350_DATA_RATE_50HZ; + } else if ((odr > 50.0) && (odr <= 100.0)) { + reg = BMM350_DATA_RATE_100HZ; + } else if ((odr > 100.0) && (odr <= 200.0)) { + reg = BMM350_DATA_RATE_200HZ; + } else if ((odr > 200.0) && (odr <= 400.0)) { + reg = BMM350_DATA_RATE_400HZ; + } else if (odr > 400.0) { + reg = BMM350_DATA_RATE_400HZ; + } + return reg; +} + +static uint8_t acc_osr_to_reg(const struct sensor_value *val) +{ + switch (val->val1) { + case 0: + return BMM350_NO_AVERAGING; + case 2: + return BMM350_AVERAGING_2; + case 4: + return BMM350_AVERAGING_4; + case 8: + return BMM350_AVERAGING_8; + default: + return 0xFF; + } +} + +/*! + * @brief This API sets the ODR and averaging factor. + */ +int8_t bmm350_set_odr_performance(enum bmm350_data_rates odr, + enum bmm350_performance_parameters performance, + const struct device *dev) +{ + /* Variable to store the function result */ + int8_t rslt = 0x00; + /* Variable to get PMU command */ + uint8_t reg_data = 0; + enum bmm350_performance_parameters performance_fix = performance; + + /* Reduce the performance setting when too high for the chosen ODR */ + if ((odr == BMM350_DATA_RATE_400HZ) && (performance >= BMM350_AVERAGING_2)) { + performance_fix = BMM350_NO_AVERAGING; + } else if ((odr == BMM350_DATA_RATE_200HZ) && (performance >= BMM350_AVERAGING_4)) { + performance_fix = BMM350_AVERAGING_2; + } else if ((odr == BMM350_DATA_RATE_100HZ) && (performance >= BMM350_AVERAGING_8)) { + performance_fix = BMM350_AVERAGING_4; + } + if (performance_fix != performance) { + LOG_WRN("performance adjusted to %d", performance_fix); + } + + /* ODR is an enum taking the generated constants from the register map */ + reg_data = ((uint8_t)odr & BMM350_ODR_MSK); + /* AVG / performance is an enum taking the generated constants from the register map */ + reg_data = BMM350_SET_BITS(reg_data, BMM350_AVG, (uint8_t)performance_fix); + /* Set PMU command configurations for ODR and performance */ + rslt = bmm350_reg_write(dev, BMM350_REG_PMU_CMD_AGGR_SET, reg_data); + LOG_DBG("odr index %d odr_reg_data 0x%x", odr, reg_data); + + if (rslt == BMM350_OK) { + /* Set PMU command configurations to update odr and average */ + reg_data = BMM350_PMU_CMD_UPD_OAE; + /* Set PMU command configuration */ + rslt = bmm350_reg_write(dev, BMM350_REG_PMU_CMD, reg_data); + if (rslt == BMM350_OK) { + k_usleep(BMM350_UPD_OAE_DELAY); + } + } + + return rslt; +} +static int set_mag_odr_osr(const struct device *dev, const struct sensor_value *odr, + const struct sensor_value *osr) +{ + int ret; + uint8_t rx_buf[3] = {0x00}; + uint8_t osr_bits; + uint8_t odr_bits; + + /* read current state */ + ret = bmm350_reg_read(dev, BMM350_REG_PMU_CMD_AGGR_SET, &rx_buf[0], 3); + if (ret < 0) { + LOG_ERR("failed to read PMU_CMD_AGGR_SET"); + return -EIO; + } + osr_bits = ((rx_buf[2] & BMM350_AVG_MSK) >> BMM350_AVG_POS); + odr_bits = ((rx_buf[2] & BMM350_ODR_MSK) >> BMM350_ODR_POS); + + /* to change sampling rate, device needs to suspend first */ + ret = bmm350_set_powermode(dev, BMM350_SUSPEND_MODE); + if (ret < 0) { + LOG_ERR("failed to set suspend mode"); + return -EIO; + } + + if (odr) { + odr_bits = acc_odr_to_reg(odr); + } + if (osr) { + osr_bits = acc_osr_to_reg(osr); + if (osr_bits == 0xFF) { + LOG_ERR("unsupported oversampling rate"); + return -EINVAL; + } + } + if (bmm350_set_odr_performance((enum bmm350_data_rates)odr_bits, osr_bits, dev) < 0) { + LOG_ERR("bmm350_set_odr_performance failed"); + return -EIO; + } + + /* go to normal mode now, measurements are requested. */ + ret = bmm350_set_powermode(dev, BMM350_NORMAL_MODE); + if (ret < 0) { + LOG_ERR("failed to set suspend mode"); + return -EIO; + } + + return 0; +} + +static int bmm350_attr_set(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, const struct sensor_value *val) +{ + switch (attr) { + case SENSOR_ATTR_SAMPLING_FREQUENCY: + if (set_mag_odr_osr(dev, val, NULL) < 0) { + return -EIO; + } + break; + case SENSOR_ATTR_OVERSAMPLING: + if (set_mag_odr_osr(dev, NULL, val) < 0) { + return -EIO; + } + break; + default: + return -EINVAL; + } + + return 0; +} +static DEVICE_API(sensor, bmm350_api_funcs) = { + .attr_set = bmm350_attr_set, + .sample_fetch = bmm350_sample_fetch, + .channel_get = bmm350_channel_get, +#ifdef CONFIG_BMM350_TRIGGER + .trigger_set = bmm350_trigger_set, +#endif +}; + +static int bmm350_init_chip(const struct device *dev) +{ + struct bmm350_pmu_cmd_status_0 pmu_cmd_stat_0 = {0}; + /* Variable to store soft-reset command */ + uint8_t soft_reset; + uint8_t rx_buf[3] = {0x00}; + uint8_t chip_id[3] = {0x00}; + int ret = 0; + /* Read chip ID (can only be read in sleep mode)*/ + if (bmm350_reg_read(dev, BMM350_REG_CHIP_ID, &chip_id[0], 3) < 0) { + LOG_ERR("failed reading chip id"); + goto err_poweroff; + } + if (chip_id[2] != BMM350_CHIP_ID) { + LOG_ERR("invalid chip id 0x%x", chip_id[2]); + goto err_poweroff; + } + /* Soft-reset */ + soft_reset = BMM350_CMD_SOFTRESET; + + /* Set the command in the command register */ + ret = bmm350_reg_write(dev, BMM350_REG_CMD, soft_reset); + k_usleep(BMM350_SOFT_RESET_DELAY); + /* Read chip ID (can only be read in sleep mode)*/ + if (bmm350_reg_read(dev, BMM350_REG_CHIP_ID, &chip_id[0], 3) < 0) { + LOG_ERR("failed reading chip id"); + goto err_poweroff; + } + if (chip_id[2] != BMM350_CHIP_ID) { + LOG_ERR("invalid chip id 0x%x", chip_id[2]); + goto err_poweroff; + } + ret = bmm350_otp_dump_after_boot(dev); + LOG_DBG("bmm350 chip_id 0x%x otp dump after boot %d\n", chip_id[2], ret); + + if (bmm350_reg_write(dev, BMM350_REG_OTP_CMD_REG, BMM350_OTP_CMD_PWR_OFF_OTP) < 0) { + LOG_ERR("failed to set REP"); + goto err_poweroff; + } + + ret += bmm350_magnetic_reset_and_wait(dev); + + LOG_DBG("bmm350 setup result %d\n", ret); + + ret += bmm350_get_pmu_cmd_status_0(dev, &pmu_cmd_stat_0); + ret += bmm350_reg_read(dev, BMM350_REG_ERR_REG, &rx_buf[0], 3); + if (ret != 0) { + LOG_ERR("%s %d", __func__, ret); + } + + return 0; + +err_poweroff: + ret = bmm350_set_powermode(dev, BMM350_SUSPEND_MODE); + if (ret != 0) { + return -EIO; + } + return -EIO; +} + +#ifdef CONFIG_PM_DEVICE +static int pm_action(const struct device *dev, enum pm_device_action action) +{ + int ret; + + switch (action) { + case PM_DEVICE_ACTION_RESUME: + ret = bmm350_set_powermode(dev, BMM350_NORMAL_MODE); + if (ret != 0) { + LOG_ERR("failed to enter normal mode: %d", ret); + } + break; + case PM_DEVICE_ACTION_SUSPEND: + ret = bmm350_set_powermode(dev, BMM350_SUSPEND_MODE); + if (ret != 0) { + LOG_ERR("failed to enter suspend mode: %d", ret); + } + break; + default: + return -ENOTSUP; + } + + return ret; +} +#endif + +static int bmm350_init(const struct device *dev) +{ + int err = 0; + struct bmm350_data *data = dev->data; + const struct sensor_value odr = {100, 0}; + const struct sensor_value osr = {2, 0}; + + err = bmm350_bus_check(dev); + if (err < 0) { + LOG_ERR("bus check failed: %d", err); + return err; + } + + if (bmm350_init_chip(dev) < 0) { + LOG_ERR("failed to initialize chip"); + return -EIO; + } + +#ifdef CONFIG_BMM350_TRIGGER + if (bmm350_trigger_mode_init(dev) < 0) { + LOG_ERR("Cannot set up trigger mode."); + return -EINVAL; + } +#endif + + /* Assign axis_en with all axis enabled (BMM350_EN_XYZ_MSK) */ + data->axis_en = BMM350_EN_XYZ_MSK; + + /* Initialize to 100Hz, averaging between 2 samples by default */ + if (set_mag_odr_osr(dev, &odr, &osr) < 0) { + return -EIO; + } + + return 0; +} + +/* Initializes a struct bmm350_config for an instance on an I2C bus. */ +#define BMM350_CONFIG_I2C(inst) .bus.i2c = I2C_DT_SPEC_INST_GET(inst), .bus_io = &bmm350_bus_io_i2c, +#define BMM350_INT_CFG(inst) .drdy_int = GPIO_DT_SPEC_INST_GET_OR(inst, drdy_gpios, {0}), + +#define BMM350_DEFINE(inst) \ + static struct bmm350_data bmm350_data_##inst; \ + static const struct bmm350_config bmm350_config_##inst = { \ + .bus.i2c = I2C_DT_SPEC_INST_GET(inst), \ + .bus_io = &bmm350_bus_io_i2c, \ + BMM350_INT_CFG(inst)}; \ + \ + PM_DEVICE_DT_INST_DEFINE(inst, pm_action); \ + \ + SENSOR_DEVICE_DT_INST_DEFINE(inst, bmm350_init, PM_DEVICE_DT_INST_GET(inst), \ + &bmm350_data_##inst, &bmm350_config_##inst, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &bmm350_api_funcs); + +/* Create the struct device for every status "okay" node in the devicetree. */ +DT_INST_FOREACH_STATUS_OKAY(BMM350_DEFINE) diff --git a/drivers/sensor/bosch/bmm350/bmm350.h b/drivers/sensor/bosch/bmm350/bmm350.h new file mode 100644 index 00000000000..adf23a068e8 --- /dev/null +++ b/drivers/sensor/bosch/bmm350/bmm350.h @@ -0,0 +1,497 @@ +/* + * Copyright (c) 2024 Bosch Sensortec GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Bus-specific functionality for BMM350s accessed via I2C. + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_BMM350_BMM350_H_ +#define ZEPHYR_DRIVERS_SENSOR_BMM350_BMM350_H_ + +#include +#include +#include +#include + +#define DT_DRV_COMPAT bosch_bmm350 + +#define BMM350_BUS_I2C DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) + +struct bmm350_bus { + struct i2c_dt_spec i2c; +}; + +typedef int (*bmm350_bus_check_fn)(const struct bmm350_bus *bus); +typedef int (*bmm350_reg_read_fn)(const struct bmm350_bus *bus, uint8_t start, uint8_t *buf, + int size); +typedef int (*bmm350_reg_write_fn)(const struct bmm350_bus *bus, uint8_t reg, uint8_t val); + +struct bmm350_bus_io { + bmm350_bus_check_fn check; + bmm350_reg_read_fn read; + bmm350_reg_write_fn write; +}; + +extern const struct bmm350_bus_io bmm350_bus_io_i2c; + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define DT_DRV_COMPAT bosch_bmm350 +#define BMM350_OK (0) +#define BMM350_DISABLE UINT8_C(0x0) +#define BMM350_ENABLE UINT8_C(0x1) + +#define BMM350_REG_CHIP_ID UINT8_C(0x00) +#define BMM350_REG_REV_ID UINT8_C(0x01) +#define BMM350_REG_ERR_REG UINT8_C(0x02) +#define BMM350_REG_PAD_CTRL UINT8_C(0x03) +#define BMM350_REG_PMU_CMD_AGGR_SET UINT8_C(0x04) +#define BMM350_REG_PMU_CMD_AXIS_EN UINT8_C(0x05) +#define BMM350_REG_PMU_CMD UINT8_C(0x06) +#define BMM350_REG_PMU_CMD_STATUS_0 UINT8_C(0x07) +#define BMM350_REG_PMU_CMD_STATUS_1 UINT8_C(0x08) +#define BMM350_REG_I3C_ERR UINT8_C(0x09) +#define BMM350_REG_I2C_WDT_SET UINT8_C(0x0A) +#define BMM350_REG_TRSDCR_REV_ID UINT8_C(0x0D) +#define BMM350_REG_TC_SYNC_TU UINT8_C(0x21) +#define BMM350_REG_TC_SYNC_ODR UINT8_C(0x22) +#define BMM350_REG_TC_SYNC_TPH_1 UINT8_C(0x23) +#define BMM350_REG_TC_SYNC_TPH_2 UINT8_C(0x24) +#define BMM350_REG_TC_SYNC_DT UINT8_C(0x25) +#define BMM350_REG_TC_SYNC_ST_0 UINT8_C(0x26) +#define BMM350_REG_TC_SYNC_ST_1 UINT8_C(0x27) +#define BMM350_REG_TC_SYNC_ST_2 UINT8_C(0x28) +#define BMM350_REG_TC_SYNC_STATUS UINT8_C(0x29) +#define BMM350_REG_INT_CTRL UINT8_C(0x2E) +#define BMM350_REG_INT_CTRL_IBI UINT8_C(0x2F) +#define BMM350_REG_INT_STATUS UINT8_C(0x30) +#define BMM350_REG_MAG_X_XLSB UINT8_C(0x31) +#define BMM350_REG_MAG_X_LSB UINT8_C(0x32) +#define BMM350_REG_MAG_X_MSB UINT8_C(0x33) +#define BMM350_REG_MAG_Y_XLSB UINT8_C(0x34) +#define BMM350_REG_MAG_Y_LSB UINT8_C(0x35) +#define BMM350_REG_MAG_Y_MSB UINT8_C(0x36) +#define BMM350_REG_MAG_Z_XLSB UINT8_C(0x37) +#define BMM350_REG_MAG_Z_LSB UINT8_C(0x38) +#define BMM350_REG_MAG_Z_MSB UINT8_C(0x39) +#define BMM350_REG_TEMP_XLSB UINT8_C(0x3A) +#define BMM350_REG_TEMP_LSB UINT8_C(0x3B) +#define BMM350_REG_TEMP_MSB UINT8_C(0x3C) +#define BMM350_REG_SENSORTIME_XLSB UINT8_C(0x3D) +#define BMM350_REG_SENSORTIME_LSB UINT8_C(0x3E) +#define BMM350_REG_SENSORTIME_MSB UINT8_C(0x3F) +#define BMM350_REG_OTP_CMD_REG UINT8_C(0x50) +#define BMM350_REG_OTP_DATA_MSB_REG UINT8_C(0x52) +#define BMM350_REG_OTP_DATA_LSB_REG UINT8_C(0x53) +#define BMM350_REG_OTP_STATUS_REG UINT8_C(0x55) +#define BMM350_REG_TMR_SELFTEST_USER UINT8_C(0x60) +#define BMM350_REG_CTRL_USER UINT8_C(0x61) +#define BMM350_REG_CMD UINT8_C(0x7E) + +/************************* Sensor Shuttle Variant **************************/ +#define BMM350_LEGACY_SHUTTLE_VARIANT_ID UINT8_C(0x10) +#define BMM350_CURRENT_SHUTTLE_VARIANT_ID UINT8_C(0x11) + +/********************* Sensor interface success code **********************/ +#define BMM350_INTF_RET_SUCCESS INT8_C(0) + +/* default value */ +#define BMM350_CHIP_ID UINT8_C(0x33) +#define BMM350_REV_ID UINT8_C(0x00) +#define BMM350_OTP_CMD_DIR_READ UINT8_C(0x20) +#define BMM350_OTP_WORD_ADDR_MSK UINT8_C(0x1F) +#define BMM350_OTP_STATUS_ERROR_MSK UINT8_C(0xE0) +#define BMM350_OTP_STATUS_ERROR(val) (val & BMM350_OTP_STATUS_ERROR_MSK) +#define BMM350_OTP_STATUS_NO_ERROR UINT8_C(0x00) +#define BMM350_OTP_STATUS_BOOT_ERR (0x20) +#define BMM350_OTP_STATUS_PAGE_RD_ERR (0x40) +#define BMM350_OTP_STATUS_PAGE_PRG_ERR (0x60) +#define BMM350_OTP_STATUS_SIGN_ERR (0x80) +#define BMM350_OTP_STATUS_INV_CMD_ERR (0xA0) +#define BMM350_OTP_STATUS_CMD_DONE UINT8_C(0x01) +#define BMM350_CMD_SOFTRESET UINT8_C(0xB6) + +/****************************** OTP indices ***************************/ +#define BMM350_TEMP_OFF_SENS UINT8_C(0x0D) +#define BMM350_MAG_OFFSET_X UINT8_C(0x0E) +#define BMM350_MAG_OFFSET_Y UINT8_C(0x0F) +#define BMM350_MAG_OFFSET_Z UINT8_C(0x10) + +#define BMM350_MAG_SENS_X UINT8_C(0x10) +#define BMM350_MAG_SENS_Y UINT8_C(0x11) +#define BMM350_MAG_SENS_Z UINT8_C(0x11) + +#define BMM350_MAG_TCO_X UINT8_C(0x12) +#define BMM350_MAG_TCO_Y UINT8_C(0x13) +#define BMM350_MAG_TCO_Z UINT8_C(0x14) + +#define BMM350_MAG_TCS_X UINT8_C(0x12) +#define BMM350_MAG_TCS_Y UINT8_C(0x13) +#define BMM350_MAG_TCS_Z UINT8_C(0x14) + +#define BMM350_MAG_DUT_T_0 UINT8_C(0x18) + +#define BMM350_CROSS_X_Y UINT8_C(0x15) +#define BMM350_CROSS_Y_X UINT8_C(0x15) +#define BMM350_CROSS_Z_X UINT8_C(0x16) +#define BMM350_CROSS_Z_Y UINT8_C(0x16) + +/**************************** Signed bit macros **********************/ +enum bmm350_signed_bit { + BMM350_SIGNED_8_BIT = 8, + BMM350_SIGNED_12_BIT = 12, + BMM350_SIGNED_16_BIT = 16, + BMM350_SIGNED_21_BIT = 21, + BMM350_SIGNED_24_BIT = 24 +}; + +/********************* Power modes *************************/ +#define BMM350_PMU_CMD_SUS 0x00 +#define BMM350_PMU_CMD_NM 0x01 +#define BMM350_PMU_CMD_UPD_OAE UINT8_C(0x02) +#define BMM350_PMU_CMD_FM 0x03 +#define BMM350_PMU_CMD_FM_FAST 0x04 +#define BMM350_PMU_CMD_FGR UINT8_C(0x05) +#define BMM350_PMU_CMD_FGR_FAST UINT8_C(0x06) +#define BMM350_PMU_CMD_BR UINT8_C(0x07) +#define BMM350_PMU_CMD_BR_FAST UINT8_C(0x08) +#define BMM350_PMU_CMD_ENABLE_XYZ UINT8_C(0x70) +#define BMM350_PMU_STATUS_0 UINT8_C(0x00) + +/**************************** PMU command status 0 macros **********************/ +#define BMM350_PMU_CMD_STATUS_0_SUS UINT8_C(0x00) +#define BMM350_PMU_CMD_STATUS_0_NM UINT8_C(0x01) +#define BMM350_PMU_CMD_STATUS_0_UPD_OAE UINT8_C(0x02) +#define BMM350_PMU_CMD_STATUS_0_FM UINT8_C(0x03) +#define BMM350_PMU_CMD_STATUS_0_FM_FAST UINT8_C(0x04) +#define BMM350_PMU_CMD_STATUS_0_FGR UINT8_C(0x05) +#define BMM350_PMU_CMD_STATUS_0_FGR_FAST UINT8_C(0x06) +#define BMM350_PMU_CMD_STATUS_0_BR UINT8_C(0x07) +#define BMM350_PMU_CMD_STATUS_0_BR_FAST UINT8_C(0x07) + +/*********************** Macros for bit masking ***************************/ +#define BMM350_AVG_MSK (0x30) +#define BMM350_AVG_POS UINT8_C(0x04) +#define BMM350_PMU_CMD_BUSY_MSK UINT8_C(0x01) +#define BMM350_PMU_CMD_BUSY_POS UINT8_C(0x00) +#define BMM350_ODR_OVWR_MSK UINT8_C(0x02) +#define BMM350_ODR_OVWR_POS UINT8_C(0x01) +#define BMM350_AVG_OVWR_MSK UINT8_C(0x04) +#define BMM350_AVG_OVWR_POS UINT8_C(0x02) +#define BMM350_PWR_MODE_IS_NORMAL_MSK UINT8_C(0x08) +#define BMM350_PWR_MODE_IS_NORMAL_POS UINT8_C(0x03) +#define BMM350_CMD_IS_ILLEGAL_MSK UINT8_C(0x10) +#define BMM350_CMD_IS_ILLEGAL_POS UINT8_C(0x04) +#define BMM350_PMU_CMD_VALUE_MSK UINT8_C(0xE0) +#define BMM350_PMU_CMD_VALUE_POS UINT8_C(0x05) + +/**************************** Self-test macros **********************/ +#define BMM350_SELF_TEST_DISABLE UINT8_C(0x00) +#define BMM350_SELF_TEST_POS_X UINT8_C(0x0D) +#define BMM350_SELF_TEST_NEG_X UINT8_C(0x0B) +#define BMM350_SELF_TEST_POS_Y UINT8_C(0x15) +#define BMM350_SELF_TEST_NEG_Y UINT8_C(0x13) + +/************************* Sensor delay time settings in microseconds **************************/ +#define BMM350_SOFT_RESET_DELAY UINT32_C(24000) +#define BMM350_MAGNETIC_RESET_DELAY UINT32_C(40000) +#define BMM350_START_UP_TIME_FROM_POR UINT32_C(3000) + +#define BMM350_GOTO_SUSPEND_DELAY UINT32_C(6000) +#define BMM350_SUSPEND_TO_NORMAL_DELAY UINT32_C(38000) + +#define BMM350_SUS_TO_FORCEDMODE_NO_AVG_DELAY (15000) +#define BMM350_SUS_TO_FORCEDMODE_AVG_2_DELAY (17000) +#define BMM350_SUS_TO_FORCEDMODE_AVG_4_DELAY (20000) +#define BMM350_SUS_TO_FORCEDMODE_AVG_8_DELAY (28000) + +#define BMM350_SUS_TO_FORCEDMODE_FAST_NO_AVG_DELAY (4000) +#define BMM350_SUS_TO_FORCEDMODE_FAST_AVG_2_DELAY (5000) +#define BMM350_SUS_TO_FORCEDMODE_FAST_AVG_4_DELAY (9000) +#define BMM350_SUS_TO_FORCEDMODE_FAST_AVG_8_DELAY (16000) + +#define BMM350_PMU_CMD_NM_TC UINT8_C(0x09) +#define BMM350_OTP_DATA_LENGTH UINT8_C(32) +#define BMM350_READ_BUFFER_LENGTH UINT8_C(127) +#define BMM350_MAG_TEMP_DATA_LEN UINT8_C(12) +#define BMM350_OTP_CMD_PWR_OFF_OTP UINT8_C(0x80) +#define BMM350_UPD_OAE_DELAY UINT16_C(1000) + +#define BMM350_BR_DELAY UINT16_C(14000) +#define BMM350_FGR_DELAY UINT16_C(18000) +#define BMM350_SOFT_RESET_DELAY UINT32_C(24000) + +#define BMM350_LSB_MASK UINT16_C(0x00FF) +#define BMM350_MSB_MASK UINT16_C(0xFF00) + +#define BMM350_LSB_TO_UT_XY_COEFF 71 +#define BMM350_LSB_TO_UT_Z_COEFF 72 +#define BMM350_LSB_TO_UT_TEMP_COEFF 10 +#define BMM350_LSB_TO_UT_COEFF_DIV 10000 +#define BMM350_MAG_COMP_COEFF_SCALING 1000 + +#define BMM350_SENS_CORR_Y 1 +#define BMM350_TCS_CORR_Z 1 + +#define BMM350_EN_X_MSK UINT8_C(0x01) +#define BMM350_EN_X_POS UINT8_C(0x0) +#define BMM350_EN_Y_MSK UINT8_C(0x02) +#define BMM350_EN_Y_POS UINT8_C(0x1) +#define BMM350_EN_Z_MSK UINT8_C(0x04) +#define BMM350_EN_Z_POS UINT8_C(0x2) +#define BMM350_EN_XYZ_MSK UINT8_C(0x7) +#define BMM350_EN_XYZ_POS UINT8_C(0x0) +/************************ Averaging macros **********************/ +#define BMM350_AVG_NO_AVG 0x0 +#define BMM350_AVG_2 0x1 +#define BMM350_AVG_4 0x2 +#define BMM350_AVG_8 0x3 +/******************************* ODR **************************/ +#define BMM350_ODR_400HZ UINT8_C(0x2) +#define BMM350_ODR_200HZ UINT8_C(0x3) +#define BMM350_ODR_100HZ UINT8_C(0x4) +#define BMM350_ODR_50HZ UINT8_C(0x5) +#define BMM350_ODR_25HZ UINT8_C(0x6) +#define BMM350_ODR_12_5HZ UINT8_C(0x7) +#define BMM350_ODR_6_25HZ UINT8_C(0x8) +#define BMM350_ODR_3_125HZ UINT8_C(0x9) +#define BMM350_ODR_1_5625HZ UINT8_C(0xA) +#define BMM350_ODR_MSK UINT8_C(0xf) +#define BMM350_ODR_POS UINT8_C(0x0) +#define BMM350_DATA_READY_INT_CTRL UINT8_C(0x8e) + +/* Macro to SET and GET BITS of a register*/ +#define BMM350_SET_BITS(reg_data, bitname, data) \ + ((reg_data & ~(bitname##_MSK)) | ((data << bitname##_POS) & bitname##_MSK)) + +#define BMM350_GET_BITS(reg_data, bitname) ((reg_data & (bitname##_MSK)) >> (bitname##_POS)) + +#define BMM350_GET_BITS_POS_0(reg_data, bitname) (reg_data & (bitname##_MSK)) + +#define BMM350_SET_BITS_POS_0(reg_data, bitname, data) \ + ((reg_data & ~(bitname##_MSK)) | (data & bitname##_MSK)) + +enum bmm350_power_modes { + BMM350_SUSPEND_MODE = BMM350_PMU_CMD_SUS, + BMM350_NORMAL_MODE = BMM350_PMU_CMD_NM, + BMM350_FORCED_MODE = BMM350_PMU_CMD_FM, + BMM350_FORCED_MODE_FAST = BMM350_PMU_CMD_FM_FAST +}; + +enum bmm350_data_rates { + BMM350_DATA_RATE_400HZ = 2, /* BMM350_ODR_400HZ */ + BMM350_DATA_RATE_200HZ = 3, /* BMM350_ODR_200HZ */ + BMM350_DATA_RATE_100HZ = 4, /* BMM350_ODR_100HZ */ + BMM350_DATA_RATE_50HZ = 5, /* BMM350_ODR_50HZ */ + BMM350_DATA_RATE_25HZ = 6, /* BMM350_ODR_25HZ */ + BMM350_DATA_RATE_12_5HZ = 7, /* BMM350_ODR_12_5HZ */ + BMM350_DATA_RATE_6_25HZ = 8, /* BMM350_ODR_6_25HZ */ + BMM350_DATA_RATE_3_125HZ = 9, /* BMM350_ODR_3_125HZ */ + BMM350_DATA_RATE_1_5625HZ = 10 /* BMM350_ODR_1_5625HZ */ +}; +enum bmm350_performance_parameters { + BMM350_NO_AVERAGING = BMM350_AVG_NO_AVG, + BMM350_AVERAGING_2 = BMM350_AVG_2, + BMM350_AVERAGING_4 = BMM350_AVG_4, + BMM350_AVERAGING_8 = BMM350_AVG_8, + BMM350_ULTRALOWNOISE = BMM350_AVG_8, + BMM350_LOWNOISE = BMM350_AVG_4, + BMM350_REGULARPOWER = BMM350_AVG_2, + BMM350_LOWPOWER = BMM350_AVG_NO_AVG +}; + +/*! + * @brief bmm350 compensated magnetometer data and temperature data + */ +struct bmm350_mag_temp_data { + /*! Compensated mag X data */ + int32_t x; + + /*! Compensated mag Y data */ + int32_t y; + + /*! Compensated mag Z data */ + int32_t z; + + /*! Temperature */ + int32_t temperature; +}; + +/*! + * @brief bmm350 magnetometer dut offset coefficient structure + */ +struct bmm350_dut_offset_coef { + /*! Temperature offset */ + int32_t t_offs; + + /*! Offset x-axis */ + int32_t offset_x; + + /*! Offset y-axis */ + int32_t offset_y; + + /*! Offset z-axis */ + int32_t offset_z; +}; + +/*! + * @brief bmm350 magnetometer dut sensitivity coefficient structure + */ +struct bmm350_dut_sensit_coef { + /*! Temperature sensitivity */ + int32_t t_sens; + + /*! Sensitivity x-axis */ + int32_t sens_x; + + /*! Sensitivity y-axis */ + int32_t sens_y; + + /*! Sensitivity z-axis */ + int32_t sens_z; +}; + +/*! + * @brief bmm350 magnetometer dut tco structure + */ +struct bmm350_dut_tco { + int32_t tco_x; + int32_t tco_y; + int32_t tco_z; +}; + +/*! + * @brief bmm350 magnetometer dut tcs structure + */ +struct bmm350_dut_tcs { + int32_t tcs_x; + int32_t tcs_y; + int32_t tcs_z; +}; + +/*! + * @brief bmm350 magnetometer cross axis compensation structure + */ +struct bmm350_cross_axis { + int32_t cross_x_y; + int32_t cross_y_x; + int32_t cross_z_x; + int32_t cross_z_y; +}; +struct mag_compensate { + /*! Structure to store dut offset coefficient */ + struct bmm350_dut_offset_coef dut_offset_coef; + + /*! Structure to store dut sensitivity coefficient */ + struct bmm350_dut_sensit_coef dut_sensit_coef; + + /*! Structure to store dut tco */ + struct bmm350_dut_tco dut_tco; + + /*! Structure to store dut tcs */ + struct bmm350_dut_tcs dut_tcs; + + /*! Initialize T0_reading parameter */ + int32_t dut_t0; + + /*! Structure to define cross axis compensation */ + struct bmm350_cross_axis cross_axis; +}; + +struct bmm350_pmu_cmd_status_0 { + /*! The previous PMU CMD is still in processing */ + uint8_t pmu_cmd_busy; + + /*! The previous PMU_CMD_AGGR_SET.odr has been overwritten */ + uint8_t odr_ovwr; + + /*! The previous PMU_CMD_AGGR_SET.avg has been overwritten */ + uint8_t avr_ovwr; + + /*! The chip is in normal power mode */ + uint8_t pwr_mode_is_normal; + + /*! CMD value is not allowed */ + uint8_t cmd_is_illegal; + + /*! Stores the latest PMU_CMD code processed */ + uint8_t pmu_cmd_value; +}; + +/*! + * @brief bmm350 un-compensated (raw) magnetometer data, signed integer + */ +struct bmm350_raw_mag_data { + /*! Raw mag X data */ + int32_t raw_xdata; + + /*! Raw mag Y data */ + int32_t raw_ydata; + + /*! Raw mag Z data */ + int32_t raw_zdata; + + /*! Raw mag temperature value */ + int32_t raw_data_temp; +}; + +struct bmm350_config { + struct bmm350_bus bus; + const struct bmm350_bus_io *bus_io; + struct gpio_dt_spec drdy_int; +}; + +struct bmm350_data { + + /*! Variable to store status of axes enabled */ + uint8_t axis_en; + struct mag_compensate mag_comp; + /*! Array to store OTP data */ + uint16_t otp_data[BMM350_OTP_DATA_LENGTH]; + /*! Variant ID */ + uint8_t var_id; + /*! Variable to enable/disable xy bit reset */ + uint8_t enable_auto_br; + struct bmm350_mag_temp_data mag_temp_data; + +#ifdef CONFIG_BMM350_TRIGGER + struct gpio_callback gpio_cb; +#endif + +#ifdef CONFIG_BMM350_TRIGGER_OWN_THREAD + struct k_sem sem; +#endif + +#ifdef CONFIG_BMM350_TRIGGER_GLOBAL_THREAD + struct k_work work; +#endif + +#if defined(CONFIG_BMM350_TRIGGER_GLOBAL_THREAD) + const struct device *dev; +#endif + +#ifdef CONFIG_BMM350_TRIGGER + const struct sensor_trigger *drdy_trigger; + sensor_trigger_handler_t drdy_handler; +#endif /* CONFIG_BMM350_TRIGGER */ +}; + +int bmm350_trigger_mode_init(const struct device *dev); + +int bmm350_trigger_set(const struct device *dev, const struct sensor_trigger *trig, + sensor_trigger_handler_t handler); +int bmm350_reg_write(const struct device *dev, uint8_t reg, uint8_t val); +#endif /* __SENSOR_BMM350_H__ */ diff --git a/drivers/sensor/bosch/bmm350/bmm350_i2c.c b/drivers/sensor/bosch/bmm350/bmm350_i2c.c new file mode 100644 index 00000000000..5c230d0e809 --- /dev/null +++ b/drivers/sensor/bosch/bmm350/bmm350_i2c.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024 Bosch Sensortec GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "bmm350.h" + +static int bmm350_bus_check_i2c(const struct bmm350_bus *bus) +{ + return i2c_is_ready_dt(&bus->i2c) ? 0 : -ENODEV; +} + +static int bmm350_reg_read_i2c(const struct bmm350_bus *bus, uint8_t start, uint8_t *buf, int size) +{ + return i2c_burst_read_dt(&bus->i2c, start, buf, size); +} + +static int bmm350_reg_write_i2c(const struct bmm350_bus *bus, uint8_t reg, uint8_t val) +{ + return i2c_reg_write_byte_dt(&bus->i2c, reg, val); +} + +const struct bmm350_bus_io bmm350_bus_io_i2c = { + .check = bmm350_bus_check_i2c, + .read = bmm350_reg_read_i2c, + .write = bmm350_reg_write_i2c, +}; diff --git a/drivers/sensor/bosch/bmm350/bmm350_trigger.c b/drivers/sensor/bosch/bmm350/bmm350_trigger.c new file mode 100644 index 00000000000..f5e0cb04a4f --- /dev/null +++ b/drivers/sensor/bosch/bmm350/bmm350_trigger.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2024 Bosch Sensortec GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Bus-specific functionality for BMM350s accessed via I2C. + */ +#include +#include +#include + +#include "bmm350.h" + +LOG_MODULE_DECLARE(BMM350, CONFIG_SENSOR_LOG_LEVEL); + +static void bmm350_handle_interrupts(const void *arg) +{ + const struct device *dev = (const struct device *)arg; + struct bmm350_data *data = dev->data; + + if (data->drdy_handler) { + data->drdy_handler(dev, data->drdy_trigger); + } +} + +#ifdef CONFIG_BMM350_TRIGGER_OWN_THREAD +static K_THREAD_STACK_DEFINE(bmm350_thread_stack, CONFIG_BMM350_THREAD_STACK_SIZE); +static struct k_thread bmm350_thread; + +static void bmm350_thread_main(void *arg1, void *unused1, void *unused2) +{ + ARG_UNUSED(unused1); + ARG_UNUSED(unused2); + const struct device *dev = (const struct device *)arg1; + struct bmm350_data *data = dev->data; + + while (1) { + k_sem_take(&data->sem, K_FOREVER); + bmm350_handle_interrupts(dev); + } +} +#endif + +#ifdef CONFIG_BMM350_TRIGGER_GLOBAL_THREAD +static void bmm350_work_handler(struct k_work *work) +{ + struct bmm350_data *data = CONTAINER_OF(work, struct bmm350_data, work); + + bmm350_handle_interrupts(data->dev); +} +#endif + +static void bmm350_gpio_callback(const struct device *port, struct gpio_callback *cb, uint32_t pin) +{ + struct bmm350_data *data = CONTAINER_OF(cb, struct bmm350_data, gpio_cb); + + ARG_UNUSED(port); + ARG_UNUSED(pin); + +#if defined(CONFIG_BMM350_TRIGGER_OWN_THREAD) + k_sem_give(&data->sem); +#elif defined(CONFIG_BMM350_TRIGGER_GLOBAL_THREAD) + k_work_submit(&data->work); +#endif +} + +int bmm350_trigger_set(const struct device *dev, const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + struct bmm350_data *data = dev->data; + int ret = 0; + +#ifdef CONFIG_PM_DEVICE + enum pm_device_state state; + + (void)pm_device_state_get(dev, &state); + if (state != PM_DEVICE_STATE_ACTIVE) { + return -EBUSY; + } +#endif + + if (trig->type != SENSOR_TRIG_DATA_READY) { + return -ENOTSUP; + } + + data->drdy_trigger = trig; + data->drdy_handler = handler; + + /* Set PMU command configuration */ + ret = bmm350_reg_write(dev, BMM350_REG_INT_CTRL, BMM350_DATA_READY_INT_CTRL); + if (ret < 0) { + return ret; + } + return 0; +} + +int bmm350_trigger_mode_init(const struct device *dev) +{ + struct bmm350_data *data = dev->data; + const struct bmm350_config *cfg = dev->config; + int ret; + + if (!device_is_ready(cfg->drdy_int.port)) { + LOG_ERR("INT device is not ready"); + return -ENODEV; + } + +#if defined(CONFIG_BMM350_TRIGGER_OWN_THREAD) + k_sem_init(&data->sem, 0, 1); + k_thread_create(&bmm350_thread, bmm350_thread_stack, CONFIG_BMM350_THREAD_STACK_SIZE, + bmm350_thread_main, (void *)dev, NULL, NULL, + K_PRIO_COOP(CONFIG_BMM350_THREAD_PRIORITY), 0, K_NO_WAIT); +#elif defined(CONFIG_BMM350_TRIGGER_GLOBAL_THREAD) + k_work_init(&data->work, bmm350_work_handler); +#endif + +#if defined(CONFIG_BMM350_TRIGGER_GLOBAL_THREAD) + data->dev = dev; +#endif + + ret = gpio_pin_configure_dt(&cfg->drdy_int, GPIO_INPUT); + if (ret < 0) { + return ret; + } + + gpio_init_callback(&data->gpio_cb, bmm350_gpio_callback, BIT(cfg->drdy_int.pin)); + + ret = gpio_add_callback(cfg->drdy_int.port, &data->gpio_cb); + if (ret < 0) { + return ret; + } + + ret = gpio_pin_interrupt_configure_dt(&cfg->drdy_int, GPIO_INT_EDGE_TO_ACTIVE); + if (ret < 0) { + return ret; + } + + return 0; +} diff --git a/drivers/sensor/ti/CMakeLists.txt b/drivers/sensor/ti/CMakeLists.txt index 452d0e8e37a..8a497df2948 100644 --- a/drivers/sensor/ti/CMakeLists.txt +++ b/drivers/sensor/ti/CMakeLists.txt @@ -20,4 +20,5 @@ add_subdirectory_ifdef(CONFIG_TMP108 tmp108) add_subdirectory_ifdef(CONFIG_TMP112 tmp112) add_subdirectory_ifdef(CONFIG_TMP114 tmp114) add_subdirectory_ifdef(CONFIG_TMP116 tmp116) +add_subdirectory_ifdef(CONFIG_TMP435 tmp435) # zephyr-keep-sorted-stop diff --git a/drivers/sensor/ti/Kconfig b/drivers/sensor/ti/Kconfig index f0655981a4c..7e9f0e95f29 100644 --- a/drivers/sensor/ti/Kconfig +++ b/drivers/sensor/ti/Kconfig @@ -20,4 +20,5 @@ source "drivers/sensor/ti/tmp108/Kconfig" source "drivers/sensor/ti/tmp112/Kconfig" source "drivers/sensor/ti/tmp114/Kconfig" source "drivers/sensor/ti/tmp116/Kconfig" +source "drivers/sensor/ti/tmp435/Kconfig" # zephyr-keep-sorted-stop diff --git a/drivers/sensor/ti/tmp435/CMakeLists.txt b/drivers/sensor/ti/tmp435/CMakeLists.txt new file mode 100644 index 00000000000..c84b5c95445 --- /dev/null +++ b/drivers/sensor/ti/tmp435/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (c) 2024 Bittium Corporation +# SPDX-License-Identifier: Apache-2.0 +# + +zephyr_library() + +zephyr_library_sources(tmp435.c) diff --git a/drivers/sensor/ti/tmp435/Kconfig b/drivers/sensor/ti/tmp435/Kconfig new file mode 100644 index 00000000000..dfbd455fcc5 --- /dev/null +++ b/drivers/sensor/ti/tmp435/Kconfig @@ -0,0 +1,11 @@ +# Copyright (c) 2024 Bittium Corporation +# SPDX-License-Identifier: Apache-2.0 +# + +config TMP435 + bool "TMP435 temperature sensor" + default y + depends on DT_HAS_TI_TMP435_ENABLED + select I2C + help + Enable the driver for the TMP435 temperature sensor diff --git a/drivers/sensor/ti/tmp435/tmp435.c b/drivers/sensor/ti/tmp435/tmp435.c new file mode 100644 index 00000000000..54cda9f402f --- /dev/null +++ b/drivers/sensor/ti/tmp435/tmp435.c @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2024 Bittium Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ti_tmp435 + +#include +#include + +#include +#include +#include +#include +#include +#include "tmp435.h" + +LOG_MODULE_REGISTER(TMP435, CONFIG_SENSOR_LOG_LEVEL); + +static inline int tmp435_reg_read(const struct tmp435_config *cfg, uint8_t reg, uint8_t *buf, + uint32_t size) +{ + return i2c_burst_read_dt(&cfg->i2c, reg, buf, size); +} + +static inline int tmp435_reg_write(const struct tmp435_config *cfg, uint8_t reg, uint8_t *buf, + uint32_t size) +{ + return i2c_burst_write_dt(&cfg->i2c, reg, buf, size); +} + +static inline int tmp435_get_status(const struct tmp435_config *cfg, uint8_t *status) +{ + return tmp435_reg_read(cfg, TMP435_STATUS_REG, status, 1); +} + +static int tmp435_one_shot(const struct device *dev) +{ + uint8_t data = 0; + uint8_t status = 0; + int ret = 0; + const struct tmp435_config *cfg = dev->config; + + data = 1; /* write anything to start */ + ret = tmp435_reg_write(cfg, TMP435_ONE_SHOT_START_REG, &data, 1); + for (uint16_t i = 0; i < TMP435_CONV_LOOP_LIMIT; i++) { + ret = tmp435_get_status(cfg, &status); + if (ret < 0) { + LOG_DBG("Failed to read TMP435_STATUS_REG, ret:%d", ret); + } else { + if (status & TMP435_STATUS_REG_BUSY) { + /* conversion not ready */ + k_msleep(10); + } else { + LOG_DBG("conv over, loops:%d status:%x", i, status); + break; + } + } + } + return ret; +} + +static int tmp435_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + int ret = 0; + uint8_t value = 0; + int32_t temp = 0; + const struct tmp435_config *cfg = dev->config; + struct tmp435_data *data = dev->data; + + if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_DIE_TEMP && + chan != SENSOR_CHAN_AMBIENT_TEMP) { + return -ENOTSUP; + } + tmp435_one_shot(dev); /* start conversion */ + if ((chan == SENSOR_CHAN_ALL) || (chan == SENSOR_CHAN_DIE_TEMP)) { + ret = tmp435_reg_read(cfg, TMP435_LOCAL_TEMP_H_REG, &value, sizeof(value)); + if (ret < 0) { + LOG_ERR("Failed to read TMP435_LOCAL_TEMP_H_REG, ret:%d", ret); + return ret; + } + temp = value; + ret = tmp435_reg_read(cfg, TMP435_LOCAL_TEMP_L_REG, &value, sizeof(value)); + if (ret < 0) { + LOG_ERR("Failed to read TMP435_LOCAL_TEMP_L_REG, ret:%d", ret); + return ret; + } + if (value > TMP435_FRACTION_INC) { + temp++; + } + data->temp_die = temp + tmp435_temp_offset; + } + + if ((chan == SENSOR_CHAN_ALL) || (chan == SENSOR_CHAN_AMBIENT_TEMP)) { + if (!(cfg->external_channel)) { + return 0; /* not enabled, just return */ + } + ret = tmp435_reg_read(cfg, TMP435_REMOTE_TEMP_H_REG, &value, sizeof(value)); + if (ret < 0) { + LOG_ERR("Failed to read TMP435_REMOTE_TEMP_H_REG ret:%d", ret); + return ret; + } + temp = value; + ret = tmp435_reg_read(cfg, TMP435_REMOTE_TEMP_L_REG, &value, sizeof(value)); + if (ret < 0) { + LOG_ERR("Failed to read TMP435_REMOTE_TEMP_L_REG, ret:%d", ret); + return ret; + } + if (value > TMP435_FRACTION_INC) { + temp++; + } + data->temp_ambient = temp + tmp435_temp_offset; + } + return 0; +} + +static int tmp435_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + int ret = 0; + struct tmp435_data *data = dev->data; + const struct tmp435_config *cfg = dev->config; + + switch (chan) { + + case SENSOR_CHAN_DIE_TEMP: + val->val1 = data->temp_die; + val->val2 = 0; + break; + case SENSOR_CHAN_AMBIENT_TEMP: + if (cfg->external_channel) { + val->val1 = data->temp_ambient; + val->val2 = 0; + } else { + ret = -ENOTSUP; + } + break; + default: + ret = -ENOTSUP; + break; + } + return ret; +} + +static const struct sensor_driver_api tmp435_driver_api = { + .sample_fetch = tmp435_sample_fetch, + .channel_get = tmp435_channel_get, +}; + +static int tmp435_init(const struct device *dev) +{ + uint8_t data = 0; + int ret = 0; + const struct tmp435_config *cfg = dev->config; + + if (!(i2c_is_ready_dt(&cfg->i2c))) { + LOG_ERR("I2C dev not ready"); + return -ENODEV; + } + + data = 1; /* write anything to reset */ + ret = tmp435_reg_write(cfg, TMP435_SOFTWARE_RESET_REG, &data, 1); + if (ret < 0) { + LOG_ERR("Failed to write TMP435_SOFTWARE_RESET_REG ret:%d", ret); + return ret; + } + + data = TMP435_CONF_REG_1_DATA; + ret = tmp435_reg_write(cfg, TMP435_CONF_REG_1, &data, 1); + if (ret < 0) { + LOG_ERR("Failed to write TMP435_CONF_REG_1 ret:%d", ret); + return ret; + } + + data = TMP435_CONF_REG_2_DATA; + if (cfg->external_channel) { + data = data + TMP435_CONF_REG_2_REN; + } + if (cfg->resistance_correction) { + data = data + TMP435_CONF_REG_2_RC; + } + ret = tmp435_reg_write(cfg, TMP435_CONF_REG_2, &data, 1); + if (ret < 0) { + LOG_ERR("Failed to write TMP435_CONF_REG_2 ret:%d", ret); + return ret; + } + + data = cfg->beta_compensation; + ret = tmp435_reg_write(cfg, TMP435_BETA_RANGE_REG, &data, 1); + if (ret < 0) { + LOG_ERR("Failed to write TMP435_BETA_RANGE_REG ret:%d", ret); + return ret; + } + return 0; +} + +/* + * Device creation macros + */ + +#define TMP435_INST(inst) \ + static struct tmp435_data tmp435_data_##inst; \ + static const struct tmp435_config tmp435_config_##inst = { \ + .i2c = I2C_DT_SPEC_INST_GET(inst), \ + .external_channel = DT_INST_PROP(inst, external_channel), \ + .resistance_correction = DT_INST_PROP(inst, resistance_correction), \ + .beta_compensation = DT_INST_PROP(inst, beta_compensation), \ + }; \ + SENSOR_DEVICE_DT_INST_DEFINE(inst, tmp435_init, NULL, &tmp435_data_##inst, \ + &tmp435_config_##inst, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &tmp435_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(TMP435_INST) diff --git a/drivers/sensor/ti/tmp435/tmp435.h b/drivers/sensor/ti/tmp435/tmp435.h new file mode 100644 index 00000000000..f138e15f37b --- /dev/null +++ b/drivers/sensor/ti/tmp435/tmp435.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024 Bittium Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_TMP435_H_ +#define ZEPHYR_DRIVERS_SENSOR_TMP435_H_ + +#define TMP435_CONF_REG_1 0x03 +#define TMP435_CONF_REG_1_DATA 0xc4 +/* [7]=1 ALERT Masked, [6]=1 Shut Down (one shot mode), [2]=1 −55 C to +150 C */ +#define TMP435_CONF_REG_2 0x1a +#define TMP435_CONF_REG_2_REN 0x10 /* [4]=1 External channel 1 enabled */ +#define TMP435_CONF_REG_2_RC 0x04 /* [2]=1 Resistance correction enabled */ +#define TMP435_CONF_REG_2_DATA 0x08 /* [3]=1 Local channel enabled */ +#define TMP435_BETA_RANGE_REG 0x25 +#define TMP435_STATUS_REG 0x02 +#define TMP435_STATUS_REG_BUSY 0x80 /* conv not ready */ +#define TMP435_SOFTWARE_RESET_REG 0xfc +#define TMP435_ONE_SHOT_START_REG 0x0f +#define TMP435_LOCAL_TEMP_H_REG 0x00 +#define TMP435_LOCAL_TEMP_L_REG 0x15 +#define TMP435_REMOTE_TEMP_H_REG 0x01 +#define TMP435_REMOTE_TEMP_L_REG 0x10 + +#define TMP435_CONV_LOOP_LIMIT 50 /* max 50*10 ms */ +#define TMP435_FRACTION_INC 0x80 /* 0.5000 */ + +static const int32_t tmp435_temp_offset = -64; + +struct tmp435_data { + int32_t temp_die; /* Celsius degrees */ + int32_t temp_ambient; /* Celsius degrees */ +}; + +struct tmp435_config { + struct i2c_dt_spec i2c; + bool external_channel; + bool resistance_correction; + uint8_t beta_compensation; +}; + +#endif /* ZEPHYR_DRIVERS_SENSOR_TMP435_H_ */ diff --git a/drivers/sensor/vishay/CMakeLists.txt b/drivers/sensor/vishay/CMakeLists.txt index 51f0b77fde7..c0462dba862 100644 --- a/drivers/sensor/vishay/CMakeLists.txt +++ b/drivers/sensor/vishay/CMakeLists.txt @@ -4,5 +4,6 @@ # zephyr-keep-sorted-start add_subdirectory_ifdef(CONFIG_VCNL36825T vcnl36825t) add_subdirectory_ifdef(CONFIG_VCNL4040 vcnl4040) +add_subdirectory_ifdef(CONFIG_VEML6031 veml6031) add_subdirectory_ifdef(CONFIG_VEML7700 veml7700) # zephyr-keep-sorted-stop diff --git a/drivers/sensor/vishay/Kconfig b/drivers/sensor/vishay/Kconfig index e11eb201193..564a9740c9c 100644 --- a/drivers/sensor/vishay/Kconfig +++ b/drivers/sensor/vishay/Kconfig @@ -4,5 +4,6 @@ # zephyr-keep-sorted-start source "drivers/sensor/vishay/vcnl36825t/Kconfig" source "drivers/sensor/vishay/vcnl4040/Kconfig" +source "drivers/sensor/vishay/veml6031/Kconfig" source "drivers/sensor/vishay/veml7700/Kconfig" # zephyr-keep-sorted-stop diff --git a/drivers/sensor/vishay/veml6031/CMakeLists.txt b/drivers/sensor/vishay/veml6031/CMakeLists.txt new file mode 100644 index 00000000000..c5439bd757b --- /dev/null +++ b/drivers/sensor/vishay/veml6031/CMakeLists.txt @@ -0,0 +1,5 @@ +# Copyright (c) 2025 Andreas Klinger +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_sources(veml6031.c) diff --git a/drivers/sensor/vishay/veml6031/Kconfig b/drivers/sensor/vishay/veml6031/Kconfig new file mode 100644 index 00000000000..48914702933 --- /dev/null +++ b/drivers/sensor/vishay/veml6031/Kconfig @@ -0,0 +1,12 @@ +# Copyright (c) 2025 Andreas Klinger +# SPDX-License-Identifier: Apache-2.0 + +# Vishay VEML6031 ambient light sensor driver options. + +config VEML6031 + bool "Vishay VEML6031 ambient light sensor" + default y + depends on DT_HAS_VISHAY_VEML6031_ENABLED + select I2C + help + Enable Vishay VEML6031 ambient light sensor driver. diff --git a/drivers/sensor/vishay/veml6031/veml6031.c b/drivers/sensor/vishay/veml6031/veml6031.c new file mode 100644 index 00000000000..897c43e7eef --- /dev/null +++ b/drivers/sensor/vishay/veml6031/veml6031.c @@ -0,0 +1,617 @@ +/* + * Copyright (c) 2025 Andreas Klinger + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT vishay_veml6031 + +#include +#include +#include +#include +#include +#include +#include + +#include + +LOG_MODULE_REGISTER(VEML6031, CONFIG_SENSOR_LOG_LEVEL); + +/* + * ID code of device + */ +#define VEML6031_DEFAULT_ID 0x01 + +/* + * Bit mask to check for data ready in single measurement. + */ +#define VEML6031_ALS_AF_DATA_READY BIT(3) + +/* + * Maximum value of ALS data which also means that the sensor is in saturation + * and that the measured value might be wrong. + * In such a case the user program should reduce one or more of the following + * attributes to get a relyable value: + * gain + * integration time + * effective photodiode size + * + */ +#define VEML6031_ALS_DATA_OVERFLOW 0xFFFF + +/* + * 16-bit command register addresses + */ +#define VEML6031_CMDCODE_ALS_CONF_0 0x00 +#define VEML6031_CMDCODE_ALS_CONF_1 0x01 +#define VEML6031_CMDCODE_ALS_WH_L 0x04 +#define VEML6031_CMDCODE_ALS_WH_H 0x05 +#define VEML6031_CMDCODE_ALS_WL_L 0x06 +#define VEML6031_CMDCODE_ALS_WL_H 0x07 +#define VEML6031_CMDCODE_ALS_DATA_L 0x10 +#define VEML6031_CMDCODE_ALS_DATA_H 0x11 +#define VEML6031_CMDCODE_IR_DATA_L 0x12 +#define VEML6031_CMDCODE_IR_DATA_H 0x13 +#define VEML6031_CMDCODE_ID_L 0x14 +#define VEML6031_CMDCODE_ID_H 0x15 +#define VEML6031_CMDCODE_ALS_INT 0x17 + +/* + * ALS integration time struct. + */ +struct veml6031_it_data { + enum veml6031_it num; + uint8_t val; + int us; +}; + +/* + * ALS integration time setting values. + * + * The enumerators of enum veml6031_it provide + * indices into this array to get the related value for the + * ALS_IT configuration bits. + */ +static const struct veml6031_it_data veml6031_it_values[] = { + {VEML6031_IT_3_125, 0x00, 3125}, /* 3.125 - 0b0000 */ + {VEML6031_IT_6_25, 0x01, 6250}, /* 6.25 - 0b0001 */ + {VEML6031_IT_12_5, 0x02, 12500}, /* 12.5 - 0b0010 */ + {VEML6031_IT_25, 0x03, 25000}, /* 25 - 0b0011 */ + {VEML6031_IT_50, 0x04, 50000}, /* 50 - 0b0100 */ + {VEML6031_IT_100, 0x05, 100000}, /* 100 - 0b0101 */ + {VEML6031_IT_200, 0x06, 200000}, /* 200 - 0b0110 */ + {VEML6031_IT_400, 0x07, 400000}, /* 400 - 0b0111 */ +}; + +/* + * Resolution matrix for values to convert between data provided + * by the sensor ("counts") and lux. + * + * These values depend on the current size, gain and integration time settings. + * The enumerators of enum veml6031_div4, enum veml6031_gain + * and enum veml6031_als_it are used for indices into this matrix. + */ +static const float + veml6031_resolution[VEML6031_DIV4_COUNT][VEML6031_GAIN_COUNT][VEML6031_IT_COUNT] = { + /*3.125ms 6.25ms 12.5ms 25ms 50ms 100ms 200ms 400ms IT */ + /* size 4/4 */ + { + {0.8704f, 0.4352f, 0.2176f, 0.1088f, 0.0544f, 0.0272f, 0.0136f, + 0.0068f}, /* Gain 1 */ + {0.4352f, 0.2176f, 0.1088f, 0.0544f, 0.0272f, 0.0136f, 0.0068f, + 0.0034f}, /* Gain 2 */ + {1.3188f, 0.6504f, 0.3297f, 0.1648f, 0.0824f, 0.0412f, 0.0206f, + 0.0103f}, /* Gain 0.66 */ + {1.7408f, 0.8704f, 0.4352f, 0.2176f, 0.1088f, 0.0544f, 0.0272f, + 0.0136f}, /* Gain 0.5 */ + }, + { + /* size 1/4 */ + {3.4816f, 1.7408f, 0.8704f, 0.4352f, 0.2176f, 0.1088f, 0.0544f, + 0.0272f}, /* Gain 1 */ + {1.7408f, 0.8704f, 0.4352f, 0.2176f, 0.1088f, 0.0544f, 0.0272f, + 0.0136f}, /* Gain 2 */ + {5.2752f, 2.6376f, 1.3188f, 0.6594f, 0.3297f, 0.1648f, 0.0824f, + 0.0412f}, /* Gain 0.66 */ + {6.9632f, 3.4816f, 1.7408f, 0.8704f, 0.4352f, 0.2176f, 0.1088f, + 0.0544f}, /* Gain 0.5 */ + }, +}; + +struct veml6031_config { + struct i2c_dt_spec bus; +}; + +struct veml6031_data { + uint8_t sd; /* Band gap and LDO shutdown */ + uint8_t int_en; /* ALS interrupt enable */ + uint8_t trig; /* ALS active force trigger */ + uint8_t af; /* Active force mode */ + uint8_t ir_sd; /* ALS and IR channel shutdown */ + uint8_t cal; /* Power on ready */ + enum veml6031_div4 div4; /* effective photodiode size */ + enum veml6031_gain gain; /* gain selection */ + enum veml6031_it itim; /* ALS integration time */ + enum veml6031_pers pers; /* ALS persistens protect */ + uint16_t thresh_high; + uint16_t thresh_low; + uint16_t als_data; + uint32_t als_lux; + uint16_t ir_data; + uint32_t int_flags; +}; + +static void veml6031_sleep_by_integration_time(const struct veml6031_data *data) +{ + k_sleep(K_USEC(veml6031_it_values[data->itim].us)); +} + +static int veml6031_check_gain(const struct sensor_value *val) +{ + return val->val1 >= VEML6031_GAIN_1 && val->val1 <= VEML6031_GAIN_0_5; +} + +static int veml6031_check_it(const struct sensor_value *val) +{ + return val->val1 >= VEML6031_IT_3_125 && val->val1 <= VEML6031_IT_400; +} + +static int veml6031_check_div4(const struct sensor_value *val) +{ + return val->val1 >= VEML6031_SIZE_4_4 && val->val1 <= VEML6031_SIZE_1_4; +} + +static int veml6031_check_pers(const struct sensor_value *val) +{ + return val->val1 >= VEML6031_PERS_1 && val->val1 <= VEML6031_PERS_8; +} + +static int veml6031_read(const struct device *dev, uint8_t cmd, uint8_t *data) +{ + const struct veml6031_config *conf = dev->config; + + uint8_t recv_buf; + int ret; + + ret = i2c_reg_read_byte_dt(&conf->bus, cmd, &recv_buf); + if (ret < 0) { + return ret; + } + + *data = recv_buf; + + return 0; +} + +static int veml6031_read16(const struct device *dev, uint8_t cmd, uint8_t *data) +{ + const struct veml6031_config *conf = dev->config; + int ret; + + ret = i2c_burst_read_dt(&conf->bus, cmd, data, 2); + if (ret < 0) { + return ret; + } + + return 0; +} + +static int veml6031_write16(const struct device *dev, uint8_t cmd, uint8_t *data) +{ + const struct veml6031_config *conf = dev->config; + + return i2c_burst_write_dt(&conf->bus, cmd, data, 2); +} + +static int veml6031_write_conf(const struct device *dev) +{ + int ret; + struct veml6031_data *data = dev->data; + uint8_t conf[2] = {0, 0}; + + /* Bits 7 -> ALS and IR channel shutdown */ + conf[1] |= data->ir_sd << 7; + /* Bits 6 -> Effective photodiode size */ + conf[1] |= data->div4 << 6; + /* Bit 5 -> reserved */ + /* Bits 4:3 -> Gain selection */ + conf[1] |= data->gain << 3; + /* Bits 2:1 -> ALS persistence protect number */ + conf[1] |= data->pers << 1; + /* Bit 0 -> Power on ready */ + if (data->cal) { + conf[1] |= BIT(0); + } + /* Bit 7 -> reserved, have to be 0 */ + /* Bits 6:4 -> integration time (ALS_IT) */ + conf[0] |= data->itim << 4; + /* Bit 3 -> Active force mode enable */ + if (data->af) { + conf[0] |= BIT(3); + } + /* Bit 2 -> ALS active force trigger */ + if (data->trig) { + conf[0] |= BIT(2); + } + /* Bit 1 -> ALS interrupt enable */ + if (data->int_en) { + conf[0] |= BIT(1); + } + /* Bit 0 -> shut down setting (SD) */ + if (data->sd) { + conf[0] |= BIT(0); + } + + ret = veml6031_write16(dev, VEML6031_CMDCODE_ALS_CONF_0, conf); + if (ret) { + LOG_ERR("Error while writing conf[0] ret: %d", ret); + return ret; + } + + return 0; +} + +static int veml6031_write_thresh_high(const struct device *dev) +{ + int ret; + const struct veml6031_data *data = dev->data; + uint8_t val[2]; + + val[0] = data->thresh_high & 0xFF; + val[1] = data->thresh_high >> 8; + + LOG_DBG("Writing high threshold counts: %d", data->thresh_high); + ret = veml6031_write16(dev, VEML6031_CMDCODE_ALS_WH_L, val); + if (ret) { + return ret; + } + + return 0; +} + +static int veml6031_write_thresh_low(const struct device *dev) +{ + int ret; + const struct veml6031_data *data = dev->data; + uint8_t val[2]; + + val[0] = data->thresh_low & 0xFF; + val[1] = data->thresh_low >> 8; + + LOG_DBG("Writing low threshold counts: %d", data->thresh_low); + ret = veml6031_write16(dev, VEML6031_CMDCODE_ALS_WL_L, val); + if (ret) { + return ret; + } + + return 0; +} + +static int veml6031_fetch(const struct device *dev) +{ + struct veml6031_data *data = dev->data; + int ret; + + ret = veml6031_read16(dev, VEML6031_CMDCODE_ALS_DATA_L, (uint8_t *)&data->als_data); + if (ret < 0) { + return ret; + } + data->als_data = sys_le16_to_cpu(data->als_data); + + ret = veml6031_read16(dev, VEML6031_CMDCODE_IR_DATA_L, (uint8_t *)&data->ir_data); + if (ret < 0) { + return ret; + } + data->ir_data = sys_le16_to_cpu(data->ir_data); + + data->als_lux = data->als_data * veml6031_resolution[data->div4][data->gain][data->itim]; + + LOG_DBG("Read ALS measurement: counts=%d, lux=%d ir=%d", data->als_data, data->als_lux, + data->ir_data); + + if (data->als_data == VEML6031_ALS_DATA_OVERFLOW) { + return -E2BIG; + } + + return 0; +} + +static int veml6031_attr_set(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, const struct sensor_value *val) +{ + struct veml6031_data *data = dev->data; + + if (chan != SENSOR_CHAN_LIGHT) { + return -ENOTSUP; + } + + /* SENSOR_ATTR_.*_THRESH are not in enum sensor_attribute_veml6031 */ + switch ((int)attr) { + case SENSOR_ATTR_VEML6031_IT: + if (veml6031_check_it(val)) { + data->itim = (enum veml6031_it)val->val1; + } else { + return -EINVAL; + } + break; + case SENSOR_ATTR_VEML6031_DIV4: + if (veml6031_check_div4(val)) { + data->div4 = (enum veml6031_div4)val->val1; + } else { + return -EINVAL; + } + break; + case SENSOR_ATTR_VEML6031_GAIN: + if (veml6031_check_gain(val)) { + data->gain = (enum veml6031_gain)val->val1; + } else { + return -EINVAL; + } + break; + case SENSOR_ATTR_VEML6031_PERS: + if (veml6031_check_pers(val)) { + data->pers = (enum veml6031_pers)val->val1; + } else { + return -EINVAL; + } + break; + case SENSOR_ATTR_LOWER_THRESH: + data->thresh_low = + val->val1 / veml6031_resolution[data->div4][data->gain][data->itim]; + return veml6031_write_thresh_low(dev); + case SENSOR_ATTR_UPPER_THRESH: + data->thresh_high = + val->val1 / veml6031_resolution[data->div4][data->gain][data->itim]; + return veml6031_write_thresh_high(dev); + default: + return -ENOTSUP; + } + + return 0; +} + +static int veml6031_attr_get(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, struct sensor_value *val) +{ + struct veml6031_data *data = dev->data; + + if (chan != SENSOR_CHAN_LIGHT) { + return -ENOTSUP; + } + + /* SENSOR_ATTR_.*_THRESH are not in enum sensor_attribute_veml6031 */ + switch ((int)attr) { + case SENSOR_ATTR_VEML6031_IT: + val->val1 = data->itim; + break; + case SENSOR_ATTR_VEML6031_DIV4: + val->val1 = data->div4; + break; + case SENSOR_ATTR_VEML6031_GAIN: + val->val1 = data->gain; + break; + case SENSOR_ATTR_VEML6031_PERS: + val->val1 = data->pers; + break; + case SENSOR_ATTR_LOWER_THRESH: + val->val1 = data->thresh_low; + break; + case SENSOR_ATTR_UPPER_THRESH: + val->val1 = data->thresh_high; + break; + default: + return -ENOTSUP; + } + + val->val2 = 0; + + return 0; +} + +static int veml6031_perform_single_measurement(const struct device *dev) +{ + struct veml6031_data *data = dev->data; + int ret; + uint8_t val; + int cnt = 0; + + data->ir_sd = 0; + data->cal = 1; + data->af = 1; + data->trig = 1; + data->int_en = 0; + data->sd = 0; + + ret = veml6031_write_conf(dev); + if (ret) { + return ret; + } + + ret = veml6031_read(dev, VEML6031_CMDCODE_ALS_INT, &val); + + veml6031_sleep_by_integration_time(data); + + while (1) { + ret = veml6031_read(dev, VEML6031_CMDCODE_ALS_INT, &val); + if (ret) { + return ret; + } + + if (val & VEML6031_ALS_AF_DATA_READY) { + break; + } + + k_sleep(K_MSEC(1)); + + cnt++; + } + + LOG_DBG("read VEML6031_CMDCODE_ALS_INT: %02X (%d)", val, cnt); + + return 0; +} + +static int veml6031_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + int ret; + + /* Start sensor for new measurement */ + if (chan == SENSOR_CHAN_LIGHT || chan == SENSOR_CHAN_ALL) { + ret = veml6031_perform_single_measurement(dev); + if (ret < 0) { + return ret; + } + + return veml6031_fetch(dev); + } else { + return -ENOTSUP; + } + + return 0; +} + +static int veml6031_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + struct veml6031_data *data = dev->data; + + switch ((int)chan) { + case SENSOR_CHAN_LIGHT: + val->val1 = data->als_lux; + break; + case SENSOR_CHAN_VEML6031_ALS_RAW_COUNTS: + val->val1 = data->als_data; + break; + case SENSOR_CHAN_VEML6031_IR_RAW_COUNTS: + val->val1 = data->ir_data; + break; + default: + return -ENOTSUP; + } + + val->val2 = 0; + + return 0; +} + +#ifdef CONFIG_PM_DEVICE + +static int veml6031_set_shutdown_flag(const struct device *dev, uint8_t new_val) +{ + struct veml6031_data *data = dev->data; + uint8_t prev_sd; + uint8_t prev_ir_sd; + int ret; + + prev_sd = data->sd; + prev_ir_sd = data->ir_sd; + data->sd = new_val; + data->ir_sd = new_val; + + ret = veml6031_write_conf(dev); + if (ret < 0) { + data->ir_sd = prev_ir_sd; + data->sd = prev_sd; + } + return ret; +} + +static int veml6031_pm_action(const struct device *dev, enum pm_device_action action) +{ + struct veml6031_data *data = dev->data; + + if (!data->sd) { + switch (action) { + case PM_DEVICE_ACTION_SUSPEND: + return veml6031_set_shutdown_flag(dev, 1); + + case PM_DEVICE_ACTION_RESUME: + return veml6031_set_shutdown_flag(dev, 0); + + default: + return -ENOTSUP; + } + } + + return 0; +} + +#endif /* CONFIG_PM_DEVICE */ + +static int veml6031_init(const struct device *dev) +{ + const struct veml6031_config *conf = dev->config; + int ret; + uint8_t val8; + + if (!i2c_is_ready_dt(&conf->bus)) { + LOG_ERR("VEML device not ready"); + return -ENODEV; + } + + ret = veml6031_read(dev, VEML6031_CMDCODE_ID_L, &val8); + if (ret < 0) { + LOG_ERR("Error while reading ID low. ret: %d", ret); + return ret; + } + if (val8 != VEML6031_DEFAULT_ID) { + LOG_ERR("Device ID wrong: %d", val8); + return -EIO; + } + ret = veml6031_read(dev, VEML6031_CMDCODE_ID_H, &val8); + if (ret < 0) { + LOG_ERR("Error while reading ID high. ret: %d", ret); + return ret; + } + LOG_DBG("veml6031 found package: %02d address: %02X version: %3s", val8 >> 6, + val8 >> 4 & 0x03 ? 0x10 : 0x29, val8 & 0x0F ? "XXX" : "A01"); + + /* Initialize sensor configuration */ + ret = veml6031_write_thresh_low(dev); + if (ret < 0) { + LOG_ERR("Error while writing thresh low. ret: %d", ret); + return ret; + } + + ret = veml6031_write_thresh_high(dev); + if (ret < 0) { + LOG_ERR("Error while writing thresh high. ret: %d", ret); + return ret; + } + + ret = veml6031_write_conf(dev); + if (ret < 0) { + LOG_ERR("Error while writing conf. ret: %d", ret); + return ret; + } + + return 0; +} + +static DEVICE_API(sensor, veml6031_api) = { + .sample_fetch = veml6031_sample_fetch, + .channel_get = veml6031_channel_get, + .attr_set = veml6031_attr_set, + .attr_get = veml6031_attr_get, +}; + +#define VEML6031_INIT(n) \ + static struct veml6031_data veml6031_data_##n = {.trig = 1, \ + .af = 1, \ + .div4 = VEML6031_SIZE_4_4, \ + .gain = VEML6031_GAIN_1, \ + .itim = VEML6031_IT_100, \ + .pers = VEML6031_PERS_1, \ + .thresh_high = 0xFFFF}; \ + \ + static const struct veml6031_config veml6031_config_##n = { \ + .bus = I2C_DT_SPEC_INST_GET(n)}; \ + \ + PM_DEVICE_DT_INST_DEFINE(n, veml6031_pm_action); \ + \ + SENSOR_DEVICE_DT_INST_DEFINE(n, veml6031_init, PM_DEVICE_DT_INST_GET(n), \ + &veml6031_data_##n, &veml6031_config_##n, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &veml6031_api); + +DT_INST_FOREACH_STATUS_OKAY(VEML6031_INIT) diff --git a/drivers/sensor/xbr818/CMakeLists.txt b/drivers/sensor/xbr818/CMakeLists.txt new file mode 100644 index 00000000000..96d6c578d64 --- /dev/null +++ b/drivers/sensor/xbr818/CMakeLists.txt @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources_ifdef(CONFIG_XBR818 xbr818.c) diff --git a/drivers/sensor/xbr818/Kconfig b/drivers/sensor/xbr818/Kconfig new file mode 100644 index 00000000000..75b91c758ac --- /dev/null +++ b/drivers/sensor/xbr818/Kconfig @@ -0,0 +1,10 @@ +# Copyright (c) 2024 MASSDRIVER EI (massdriver.space) +# SPDX-License-Identifier: Apache-2.0 + +config XBR818 + bool "XBR818 Radar" + default y + depends on DT_HAS_PHOSENSE_XBR818_ENABLED + select I2C + help + Enable driver for the Phosense XBR818 Radar Sensor diff --git a/drivers/sensor/xbr818/xbr818.c b/drivers/sensor/xbr818/xbr818.c new file mode 100644 index 00000000000..be396ec620e --- /dev/null +++ b/drivers/sensor/xbr818/xbr818.c @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2024 MASSDRIVER EI (massdriver.space) + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT phosense_xbr818 + +#include +#include + +#include +LOG_MODULE_REGISTER(XBR818, CONFIG_SENSOR_LOG_LEVEL); + +#include "xbr818.h" +#include + +static int xbr818_enable_i2c(const struct device *dev) +{ + const struct xbr818_config *config = dev->config; + int ret; + + if (config->i2c_en.port) { + ret = gpio_pin_set_dt(&config->i2c_en, 1); + if (ret != 0) { + LOG_ERR("%s: could not set i2c_en pin", dev->name); + } + k_usleep(10); + return ret; + } + return 0; +} + +static int xbr818_disable_i2c(const struct device *dev) +{ + const struct xbr818_config *config = dev->config; + int ret; + + if (config->i2c_en.port) { + ret = gpio_pin_set_dt(&config->i2c_en, 0); + if (ret != 0) { + LOG_ERR("%s: could not unset i2c_en pin", dev->name); + } + return ret; + } + return 0; +} + +static int xbr818_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + const struct xbr818_config *config = dev->config; + struct xbr818_data *data = dev->data; + int ret; + + if (chan != SENSOR_CHAN_PROX && chan != SENSOR_CHAN_ALL) { + LOG_ERR("%s: requesting unsupported channel %i", dev->name, chan); + return -ENOTSUP; + } + + ret = gpio_pin_get_dt(&config->io_val); + if (ret < 0) { + return ret; + } + data->value = (ret == 1 ? true : false); + + return 0; +} + +static int xbr818_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + struct xbr818_data *data = dev->data; + + if (chan != SENSOR_CHAN_PROX) { + LOG_ERR("%s: requesting unsupported channel %i", dev->name, chan); + return -ENOTSUP; + } + + val->val1 = (data->value ? 1 : 0); + val->val2 = 0; + + return 0; +} + +static int xbr818_attr_set(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, const struct sensor_value *val) +{ + const struct xbr818_config *config = dev->config; + int ret; + uint8_t tmp[3]; + uint64_t tmpf; + + if (chan != SENSOR_CHAN_PROX) { + LOG_ERR("%s: requesting unsupported channel %i", dev->name, chan); + return -ENOTSUP; + } + + if (val->val1 < 0) { + return -EINVAL; + } + + ret = xbr818_enable_i2c(dev); + if (ret != 0) { + return ret; + } + + if (attr == SENSOR_ATTR_LOWER_THRESH) { + if (val->val1 > 0xFFFF || val->val1 < 0) { + return -EINVAL; + } + tmp[0] = val->val1 & 0xFF; + tmp[1] = (val->val1 & 0xFF00) >> 8; + ret = i2c_burst_write_dt(&config->i2c, XBR818_THRESHOLD_1, tmp, 2); + } else if ((enum sensor_attribute_xbr818)attr == SENSOR_ATTR_XBR818_NOISE_FLOOR) { + if (val->val1 > 0xFFFF || val->val1 < 0) { + return -EINVAL; + } + tmp[0] = val->val1 & 0xFF; + tmp[1] = (val->val1 & 0xFF00) >> 8; + ret = i2c_burst_write_dt(&config->i2c, XBR818_THRESHOLD_NOISE_1, tmp, 2); + } else if ((enum sensor_attribute_xbr818)attr == SENSOR_ATTR_XBR818_DELAY_TIME) { + if (val->val1 < 0 || val->val2 < 0) { + return -EINVAL; + } + tmpf = (uint64_t)val->val1 * 1000000 + (uint64_t)val->val2; + tmpf = (tmpf * SENSOR_XBR818_CLOCKRATE) / 1000000; + if (tmpf > 0xFFFFFF) { + return -EINVAL; + } + tmp[0] = tmpf & 0xFF; + tmp[1] = (tmpf & 0xFF00) >> 8; + tmp[2] = (tmpf & 0xFF0000) >> 16; + ret = i2c_burst_write_dt(&config->i2c, XBR818_DELAY_TIME_1, tmp, 3); + } else if ((enum sensor_attribute_xbr818)attr == SENSOR_ATTR_XBR818_LOCK_TIME) { + if (val->val1 < 0 || val->val2 < 0) { + return -EINVAL; + } + tmpf = (uint64_t)val->val1 * 1000000 + (uint64_t)val->val2; + tmpf = (tmpf * SENSOR_XBR818_CLOCKRATE) / 1000000; + if (tmpf > 0xFFFFFF) { + return -EINVAL; + } + tmp[0] = tmpf & 0xFF; + tmp[1] = (tmpf & 0xFF00) >> 8; + tmp[2] = (tmpf & 0xFF0000) >> 16; + ret = i2c_burst_write_dt(&config->i2c, XBR818_LOCK_TIME_1, tmp, 3); + } else if ((enum sensor_attribute_xbr818)attr == SENSOR_ATTR_XBR818_RF_POWER) { + if (val->val1 > 0x7 || val->val1 < 0) { + return -EINVAL; + } + tmp[0] = val->val1 & 0x7; + ret = i2c_reg_write_byte_dt(&config->i2c, XBR818_RF_POWER, tmp[0]); + } else if (attr == SENSOR_ATTR_SAMPLING_FREQUENCY) { + if (val->val1 > SENSOR_XBR818_CLOCKRATE || val->val1 <= 0) { + return -EINVAL; + } + tmp[0] = SENSOR_XBR818_CLOCKRATE / val->val1; + if (tmp[0] > 0xFF) { + return -EINVAL; + } + ret = i2c_reg_write_byte_dt(&config->i2c, XBR818_SAMPLE_RATE_DIVIDER, tmp[0]); + } else { + ret = xbr818_disable_i2c(dev); + if (ret != 0) { + return ret; + } + return -ENODEV; + } + + if (ret != 0) { + return ret; + } + + ret = xbr818_disable_i2c(dev); + + return ret; +} + +static int xbr818_attr_get(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, struct sensor_value *val) +{ + const struct xbr818_config *config = dev->config; + int ret; + uint8_t tmp[3]; + uint64_t tmpf; + + if (chan != SENSOR_CHAN_PROX) { + LOG_ERR("%s: requesting unsupported channel %i", dev->name, chan); + return -ENOTSUP; + } + + ret = xbr818_enable_i2c(dev); + if (ret != 0) { + return ret; + } + + if (attr == SENSOR_ATTR_LOWER_THRESH) { + ret = i2c_burst_read_dt(&config->i2c, XBR818_THRESHOLD_1, tmp, 2); + if (ret != 0) { + return ret; + } + val->val1 = tmp[0] & 0xFF; + val->val1 |= (uint32_t)tmp[1] << 8; + } else if ((enum sensor_attribute_xbr818)attr == SENSOR_ATTR_XBR818_NOISE_FLOOR) { + ret = i2c_burst_read_dt(&config->i2c, XBR818_THRESHOLD_NOISE_1, tmp, 2); + if (ret != 0) { + return ret; + } + val->val1 = tmp[0] & 0xFF; + val->val1 |= (uint32_t)tmp[1] << 8; + } else if ((enum sensor_attribute_xbr818)attr == SENSOR_ATTR_XBR818_DELAY_TIME) { + ret = i2c_burst_read_dt(&config->i2c, XBR818_DELAY_TIME_1, tmp, 3); + if (ret != 0) { + return ret; + } + val->val1 = tmp[0] & 0xFF; + val->val1 |= (uint32_t)tmp[1] << 8; + val->val1 |= (uint32_t)tmp[2] << 16; + tmpf = (uint64_t)val->val1 * 1000000; + tmpf /= SENSOR_XBR818_CLOCKRATE; + val->val1 = tmpf / 1000000; + val->val2 = tmpf - val->val1 * 1000000; + } else if ((enum sensor_attribute_xbr818)attr == SENSOR_ATTR_XBR818_LOCK_TIME) { + ret = i2c_burst_read_dt(&config->i2c, XBR818_LOCK_TIME_1, tmp, 3); + if (ret != 0) { + return ret; + } + val->val1 = tmp[0] & 0xFF; + val->val1 |= (uint32_t)tmp[1] << 8; + val->val1 |= (uint32_t)tmp[2] << 16; + tmpf = (uint64_t)val->val1 * 1000000; + tmpf /= SENSOR_XBR818_CLOCKRATE; + val->val1 = tmpf / 1000000; + val->val2 = tmpf - val->val1 * 1000000; + } else if ((enum sensor_attribute_xbr818)attr == SENSOR_ATTR_XBR818_RF_POWER) { + ret = i2c_reg_read_byte_dt(&config->i2c, XBR818_RF_POWER, tmp); + if (ret != 0) { + return ret; + } + val->val1 = *tmp & 0x7; + } else if (attr == SENSOR_ATTR_SAMPLING_FREQUENCY) { + ret = i2c_reg_read_byte_dt(&config->i2c, XBR818_SAMPLE_RATE_DIVIDER, tmp); + if (ret != 0) { + return ret; + } + val->val1 = SENSOR_XBR818_CLOCKRATE / *tmp; + } else { + ret = xbr818_disable_i2c(dev); + if (ret != 0) { + return ret; + } + return -ENODEV; + } + + ret = xbr818_disable_i2c(dev); + + return ret; +} + +static void xbr818_work(struct k_work *work) +{ + struct xbr818_data *data = CONTAINER_OF(work, struct xbr818_data, work); + + if (likely(data->handler != NULL)) { + data->handler(data->dev, data->trigger); + } +} + +static void xbr818_gpio_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins) +{ + struct xbr818_data *data = CONTAINER_OF(cb, struct xbr818_data, gpio_cb); + + k_work_submit(&data->work); +} + +static int xbr818_trigger_set(const struct device *dev, const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + const struct xbr818_config *config = dev->config; + struct xbr818_data *data = dev->data; + int ret; + + if (trig->chan != SENSOR_CHAN_PROX) { + LOG_ERR("%s: requesting unsupported channel %i", dev->name, trig->chan); + return -ENOTSUP; + } + + if (trig->type != SENSOR_TRIG_MOTION) { + LOG_ERR("%s: requesting unsupported trigger %i", dev->name, trig->type); + return -ENOTSUP; + } + + data->handler = handler; + data->trigger = trig; + ret = gpio_pin_interrupt_configure_dt(&config->io_val, GPIO_INT_EDGE_RISING); + if (ret < 0) { + return ret; + } + + if (handler) { + ret = gpio_add_callback(config->io_val.port, &data->gpio_cb); + } else { + ret = gpio_remove_callback(config->io_val.port, &data->gpio_cb); + } + + return ret; +} + +static int xbr818_init_defaults(const struct device *dev) +{ + const struct xbr818_config *config = dev->config; + int ret = 0; + uint8_t data[3]; + + ret |= i2c_reg_write_byte_dt(&config->i2c, XBR818_IO_ACTIVE_VALUE_REG, 0x03); + __ASSERT(ret == 0, "Error sending XBR818 defaults for XBR818_IO_ACTIVE_VALUE_REG"); + ret |= i2c_reg_write_byte_dt(&config->i2c, XBR818_RF_EN_SEL, 0x20); + __ASSERT(ret == 0, "Error sending XBR818 defaults for XBR818_RF_EN_SEL"); + ret |= i2c_reg_write_byte_dt(&config->i2c, XBR818_SAMPLE_RATE_DIVIDER, 0x20); + __ASSERT(ret == 0, "Error sending XBR818 defaults for XBR818_SAMPLE_RATE_DIVIDER"); + ret |= i2c_reg_write_byte_dt(&config->i2c, XBR818_RF_POWER, 0x45); + __ASSERT(ret == 0, "Error sending XBR818 defaults for XBR818_RF_POWER"); + ret |= i2c_reg_write_byte_dt(&config->i2c, XBR818_TIMER_CTRL, 0x21); + __ASSERT(ret == 0, "Error sending XBR818 defaults for XBR818_TIMER_CTRL"); + + data[0] = 0x5a; + data[1] = 0x01; + ret |= i2c_burst_write_dt(&config->i2c, XBR818_THRESHOLD_1, data, 2); + __ASSERT(ret == 0, "Error sending XBR818 defaults for XBR818_THRESHOLD"); + + data[0] = 0x55; + data[1] = 0x01; + ret |= i2c_burst_write_dt(&config->i2c, XBR818_THRESHOLD_NOISE_1, data, 2); + __ASSERT(ret == 0, "Error sending XBR818 defaults for XBR818_THRESHOLD_NOISE"); + + /* 0.1 seconds */ + data[0] = 0x80; + data[1] = 0x0C; + data[2] = 0x00; + ret |= i2c_burst_write_dt(&config->i2c, XBR818_DELAY_TIME_1, data, 3); + __ASSERT(ret == 0, "Error sending XBR818 defaults for XBR818_DELAY_TIME"); + + /* 0.5 seconds */ + data[0] = 0x80; + data[1] = 0x3E; + data[2] = 0x00; + ret |= i2c_burst_write_dt(&config->i2c, XBR818_LOCK_TIME_1, data, 3); + __ASSERT(ret == 0, "Error sending XBR818 defaults for XBR818_LOCK_TIME"); + + ret |= i2c_reg_write_byte_dt(&config->i2c, XBR818_PIN_SETTINGS, 0x0C); + __ASSERT(ret == 0, "Error sending XBR818 defaults for XBR818_PIN_SETTINGS"); + + ret |= i2c_reg_write_byte_dt(&config->i2c, XBR818_I2C_OUT, 0x1); + __ASSERT(ret == 0, "Error sending XBR818 defaults for XBR818_I2C_OUT"); + + return ret; +} + +static int xbr818_init(const struct device *dev) +{ + const struct xbr818_config *config = dev->config; + struct xbr818_data *data = dev->data; + int ret; + + if (!i2c_is_ready_dt(&config->i2c)) { + LOG_ERR("I2C device not ready"); + return -ENODEV; + } + + data->dev = dev; + data->work.handler = xbr818_work; + + ret = gpio_pin_configure_dt(&config->io_val, GPIO_INPUT); + if (ret != 0) { + LOG_ERR("%s: could not configure io_val(int) pin", dev->name); + return ret; + } + + if (config->i2c_en.port) { + ret = gpio_pin_configure_dt(&config->i2c_en, GPIO_OUTPUT); + if (ret != 0) { + LOG_ERR("%s: could not configure i2c_en pin", dev->name); + return ret; + } + } + + ret = xbr818_enable_i2c(dev); + if (ret != 0) { + return ret; + } + + ret = xbr818_init_defaults(dev); + if (ret != 0) { + LOG_ERR("%s: unable to configure", dev->name); + } + + ret = xbr818_disable_i2c(dev); + if (ret != 0) { + return ret; + } + + ret = gpio_pin_interrupt_configure_dt(&config->io_val, GPIO_INT_DISABLE); + if (ret) { + LOG_ERR("%s: failed to configure gpio interrupt: %d", dev->name, ret); + return ret; + } + + gpio_init_callback(&data->gpio_cb, xbr818_gpio_callback, BIT(config->io_val.pin)); + + return ret; +} + +static const struct sensor_driver_api xbr818_api = { + .sample_fetch = xbr818_sample_fetch, + .channel_get = xbr818_channel_get, + .attr_set = xbr818_attr_set, + .attr_get = xbr818_attr_get, + .trigger_set = xbr818_trigger_set, +}; + +#define XBR818_INIT(inst) \ + static const struct xbr818_config xbr818_##inst##_config = { \ + .i2c = I2C_DT_SPEC_INST_GET(inst), \ + .i2c_en = GPIO_DT_SPEC_GET_OR(DT_INST(inst, phosense_xbr818), i2c_en_gpios, {0}), \ + .io_val = GPIO_DT_SPEC_GET(DT_INST(inst, phosense_xbr818), int_gpios), \ + }; \ + \ + static struct xbr818_data xbr818_##inst##_data; \ + \ + SENSOR_DEVICE_DT_INST_DEFINE(inst, xbr818_init, NULL, &xbr818_##inst##_data, \ + &xbr818_##inst##_config, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &xbr818_api); + +DT_INST_FOREACH_STATUS_OKAY(XBR818_INIT); diff --git a/drivers/sensor/xbr818/xbr818.h b/drivers/sensor/xbr818/xbr818.h new file mode 100644 index 00000000000..f30f6d5ed0d --- /dev/null +++ b/drivers/sensor/xbr818/xbr818.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024 MASSDRIVER EI (massdriver.space) + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_XBR818_XBR818_H_ +#define ZEPHYR_DRIVERS_SENSOR_XBR818_XBR818_H_ + +#include +#include +#include +#include + +/* 32Khz clockrate, most time values are multiple of this */ +#define SENSOR_XBR818_CLOCKRATE 32000 + +struct xbr818_config { + struct i2c_dt_spec i2c; + struct gpio_dt_spec i2c_en; + struct gpio_dt_spec io_val; +}; + +struct xbr818_data { + bool value; + uint32_t trigger_type; + sensor_trigger_handler_t handler; + struct gpio_callback gpio_cb; + const struct sensor_trigger *trigger; + const struct device *dev; + struct k_work work; +}; + +/* reference rd-04 module manual for more information */ +/* [0-2]: power of PA + * [4-6]: mixer trim + */ +#define XBR818_RF_POWER 0x03 +#define XBR818_RF_EN_SEL 0x04 +/* minimum value of 2 */ +#define XBR818_SAMPLE_RATE_DIVIDER 0x10 +/* [0]: enable detection + * [1-2]: readable data. 0: det_dc_sum 1: det_ac_sum 2: det_dc_used 3: det_noise + * [3]: enable read on 0x28-0x29 + * [4]: signal detection threshold. 0: auto by pin 1: register + * [7]: enable read on 0x26-0x29 + */ +#define XBR818_I2C_OUT 0x13 +/* Threshold for detection + * [0-7] + */ +#define XBR818_THRESHOLD_1 0x18 +/* [8-15] */ +#define XBR818_THRESHOLD_2 0x19 +/* Threshold for noise + * [0-7] + */ +#define XBR818_THRESHOLD_NOISE_1 0x1A +/* [8-15] */ +#define XBR818_THRESHOLD_NOISE_2 0x1B +/* Delay Time (in 1/32000 seconds) + * [0-7] + */ +#define XBR818_DELAY_TIME_1 0x1D +/* [8-15] */ +#define XBR818_DELAY_TIME_2 0x1E +/* [16-23] */ +#define XBR818_DELAY_TIME_3 0x1F +/* [0]: enable + * [1-2]: light sensor timer. 0: disabled 1: 4 sec 2: 1 minute 3: 1 hour + * [3-4]: output timer. 0: 1 sec 1: 1 minute 2: 1 hour 3: 1 day + * [5]: delay time. 0: 'configure by pin' 1: configure by register + */ +#define XBR818_TIMER_CTRL 0x1C +/* Lock Time (in 1/32000 seconds) + * [0-7] + */ +#define XBR818_LOCK_TIME_1 0x20 +/* [8-15] */ +#define XBR818_LOCK_TIME_2 0x21 +/* [16-23] */ +#define XBR818_LOCK_TIME_3 0x22 +/* Pin settings + * [0-3]: IO_VAL pin + * 0xc: io_value_out, 0xd: io_value_out inverted, 0xf: GPIO + * [4-7]: INT_IRQ pin + * 0x0: t3_int_irq, 0x9: io_value_out, 0xa: io_value_out inverted, 0xf: GPIO + */ +#define XBR818_PIN_SETTINGS 0x23 +/* [0]: ADC1 is configured for VCO trimming. 0: enable, 1: disable + * [1]: Low power mode is pin or register. 0: pin 1: register + * [2]: If IO_VAL pin is GPIO, output. 0: no 1: yes + * [3]: if INT_IRQ pin is GPIO, output. 0:no 1:yes + */ +#define XBR818_IO_ACTIVE_VALUE_REG 0x24 + +#endif /* ZEPHYR_DRIVERS_SENSOR_XBR818_XBR818_H_ */ diff --git a/dts/bindings/sensor/bosch,bmm350-i2c.yaml b/dts/bindings/sensor/bosch,bmm350-i2c.yaml new file mode 100644 index 00000000000..35434f69b54 --- /dev/null +++ b/dts/bindings/sensor/bosch,bmm350-i2c.yaml @@ -0,0 +1,11 @@ +# Copyright (c) 2024, Bosch Sensortec GmbH + +# SPDX-License-Identifier: Apache-2.0 + +description: | + Bosch BMM350 Geomagnetic sensor. See more info at: + https://www.bosch-sensortec.com/products/motion-sensors/magnetometers/bmm350/ + +compatible: "bosch,bmm350" + +include: [i2c-device.yaml, "bosch,bmm350.yaml"] diff --git a/dts/bindings/sensor/bosch,bmm350.yaml b/dts/bindings/sensor/bosch,bmm350.yaml new file mode 100644 index 00000000000..35206be2d2e --- /dev/null +++ b/dts/bindings/sensor/bosch,bmm350.yaml @@ -0,0 +1,12 @@ +# Copyright (c) 2024, Bosch Sensortec GmbH + +# SPDX-License-Identifier: Apache-2.0 + +include: sensor-device.yaml + +properties: + drdy-gpios: + type: phandle-array + description: | + This property specifies the connection for data ready pin. + The polarity default is active high when sensor data is ready. diff --git a/dts/bindings/sensor/phosense,xbr818.yaml b/dts/bindings/sensor/phosense,xbr818.yaml new file mode 100644 index 00000000000..9eeedba0584 --- /dev/null +++ b/dts/bindings/sensor/phosense,xbr818.yaml @@ -0,0 +1,19 @@ +# Copyright (c) 2024 MASSDRIVER EI (massdriver.space) +# SPDX-License-Identifier: Apache-2.0 + +description: | + Phosense XBR818 I2C-capable 10 GHz Radar Sensor + +compatible: "phosense,xbr818" + +include: [sensor-device.yaml, i2c-device.yaml] + +properties: + int-gpios: + type: phandle-array + required: true + description: GPIO pin connected to IO pin (IO_VAL) + + i2c-en-gpios: + type: phandle-array + description: GPIO pin connected to I2C enable (I2C_EN) diff --git a/dts/bindings/sensor/ti,tmp435.yaml b/dts/bindings/sensor/ti,tmp435.yaml new file mode 100644 index 00000000000..5dc71b49f09 --- /dev/null +++ b/dts/bindings/sensor/ti,tmp435.yaml @@ -0,0 +1,36 @@ +# +# Copyright (c) 2024 Bittium Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +description: Texas Instruments TMP435 temperature sensor + +compatible: "ti,tmp435" + +bus: tmp435 + +include: [sensor-device.yaml, i2c-device.yaml] + +properties: + external-channel: + description: External temperature measurement is enabled + type: boolean + resistance-correction: + description: | + Resistance correction feature for the external + temperature channel. If not enabled then conversion + is faster but with lower accuracy. + type: boolean + beta-compensation: + description: | + Beta Compensation Configuration, see Table 8 + from TMP435 data sheet, some common values. + 0x0f, Automatically selected range 7 (beta > 27.0) or + Automatically detected diode connected sensor + 0x07, Manually disabled beta correction + Default is common value for general purpose transistors. + type: int + default: 0x0f + enum: [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f] diff --git a/dts/bindings/sensor/vishay,veml6031.yaml b/dts/bindings/sensor/vishay,veml6031.yaml new file mode 100644 index 00000000000..4ebc5d25bcc --- /dev/null +++ b/dts/bindings/sensor/vishay,veml6031.yaml @@ -0,0 +1,10 @@ +# Copyright (c) 2025 Andreas Klinger +# SPDX-License-Identifier: Apache-2.0 + +description: | + Vishay VEML6031 High Accuracy Ambient Light Sensor With I2C Interface. + See: https://www.vishay.com/docs/80007/veml6031x00.pdf + +compatible: "vishay,veml6031" + +include: [sensor-device.yaml, i2c-device.yaml] diff --git a/dts/bindings/vendor-prefixes.txt b/dts/bindings/vendor-prefixes.txt index ee115b920f6..1d06e4b8201 100644 --- a/dts/bindings/vendor-prefixes.txt +++ b/dts/bindings/vendor-prefixes.txt @@ -508,6 +508,7 @@ pda Precision Design Associates, Inc. pericom Pericom Technology Inc. pervasive Pervasive Displays, Inc. phicomm PHICOMM Co., Ltd. +phosense Beijing Phosense Electronic Technology Co., Ltd. phytec PHYTEC picochip Picochip Ltd pine64 Pine64 diff --git a/include/zephyr/drivers/sensor/veml6031.h b/include/zephyr/drivers/sensor/veml6031.h new file mode 100644 index 00000000000..fe867a72a52 --- /dev/null +++ b/include/zephyr/drivers/sensor/veml6031.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2025 Andreas Klinger + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_VEML6031_H_ +#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_VEML6031_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ALS integration time settings in veml6031_it + */ +#define VEML6031_IT_COUNT 8 + +/** + * @brief Effective photodiode size in enum veml6031_size. + */ +#define VEML6031_DIV4_COUNT 2 + +/** + * @brief Gain selections in enum veml6031_gain. + */ +#define VEML6031_GAIN_COUNT 4 + +/** + * @brief VEML6031 integration time options for ambient light measurements. + */ +enum veml6031_it { + VEML6031_IT_3_125, + VEML6031_IT_6_25, + VEML6031_IT_12_5, + VEML6031_IT_25, + VEML6031_IT_50, + VEML6031_IT_100, + VEML6031_IT_200, + VEML6031_IT_400, +}; + +/** + * @brief VEML6031 size options for ambient light measurements. + */ +enum veml6031_div4 { + VEML6031_SIZE_4_4 = 0x00, /* 0b0 */ + VEML6031_SIZE_1_4 = 0x01, /* 0b1 */ +}; + +/** + * @brief VEML6031 gain options for ambient light measurements. + */ +enum veml6031_gain { + VEML6031_GAIN_1 = 0x00, /* 0b00 */ + VEML6031_GAIN_2 = 0x01, /* 0b01 */ + VEML6031_GAIN_0_66 = 0x02, /* 0b10 */ + VEML6031_GAIN_0_5 = 0x03, /* 0b11 */ +}; + +/** + * @brief VEML6031 ALS interrupt persistence protect number options. + */ +enum veml6031_pers { + VEML6031_PERS_1 = 0x00, /* 0b00 */ + VEML6031_PERS_2 = 0x01, /* 0b01 */ + VEML6031_PERS_4 = 0x02, /* 0b10 */ + VEML6031_PERS_8 = 0x03, /* 0b11 */ +}; + +/** + * @brief VEML6031 specific sensor attributes. + * + * For high and low threshold window settings (ALS_WH_L, ALS_WH_H, ALS_WL_L and + * ALS_WL_H) use the generic attributes SENSOR_ATTR_UPPER_THRESH and + * SENSOR_ATTR_LOWER_THRESH with 16-bit unsigned integer values. Both + * threshold settings are in lux and converted by the driver to a value + * compatible with the sensor. This conversion depends on the current gain, + * integration time and effective photodiode size settings. So a change in + * gain, integration time or effective photodiode size usually requires an + * update of threshold window settings. To get the correct threshold values + * into the sensor update the thresholds -after- a change of gain or + * integration time. + * + * All attributes must be set for the SENSOR_CHAN_LIGHT channel. + * + * When the sensor goes into saturation -E2BIG is returned. This + * happens when the maximum value 0xFFFF is returned as raw ALS value. + * In this case it's up to the user to reduce one or more of the following + * attributes to come back into the optimal measurement range of the sensor: + * SENSOR_ATTR_VEML6031_GAIN (gain) + * SENSOR_ATTR_VEML6031_IT (integration time) + * SENSOR_ATTR_VEML6031_DIV4 (effective photodiode size) + */ +enum sensor_attribute_veml6031 { + /** + * @brief Integration time setting for ALS measurements (IT). + * + * Use enum veml6031_it for attribute values. + */ + SENSOR_ATTR_VEML6031_IT = SENSOR_ATTR_PRIV_START, + /** + * @brief Effective photodiode size (DIV4) + * + * Use enum veml6031_div4 for attribute values. + */ + SENSOR_ATTR_VEML6031_DIV4, + /** + * @brief Gain setting for ALS measurements (GAIN). + * + * Use enum veml6031_gain for attribute values. + */ + SENSOR_ATTR_VEML6031_GAIN, + /** + * @brief ALS persistence protect number setting (PERS). + * + * Use enum veml6031_pers for attribute values. + */ + SENSOR_ATTR_VEML6031_PERS, +}; + +/** + * @brief VEML6031 specific sensor channels. + */ +enum sensor_channel_veml6031 { + /** + * @brief Channel for raw ALS sensor values. + * + * This channel represents the raw measurement counts provided by the + * sensor ALS register. It is useful for estimating good values for + * integration time, effective photodiode size and gain attributes in + * fetch and get mode. + * + * For future implementations with triggers it can also be used to + * estimate the threshold window attributes for the sensor interrupt + * handling. + * + * It cannot be fetched directly. Instead, this channel's value is + * fetched implicitly using SENSOR_CHAN_LIGHT. Trying to call + * sensor_channel_fetch_chan() with this enumerator as an + * argument will result in a -ENOTSUP. + */ + SENSOR_CHAN_VEML6031_ALS_RAW_COUNTS = SENSOR_CHAN_PRIV_START, + + /** + * @brief Channel for IR sensor values. + * + * This channel is the raw IR Channel count output of the sensor. About + * fetching the same as for + * SENSOR_CHAN_VEML6031_ALS_RAW_COUNTS applies. + */ + SENSOR_CHAN_VEML6031_IR_RAW_COUNTS, +}; + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_SENSOR_VEML6031_H_ */ diff --git a/include/zephyr/drivers/sensor/xbr818.h b/include/zephyr/drivers/sensor/xbr818.h new file mode 100644 index 00000000000..06cac96cbde --- /dev/null +++ b/include/zephyr/drivers/sensor/xbr818.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024, MASSDRIVER EI (massdriver.space) + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Extended public API for Phosense XBR818 10 GHz Radar + * + * This exposes 4 additional attributes used to configure the IC + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_XBR818_H_ +#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_XBR818_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum sensor_attribute_xbr818 { + /*! + * Time of received activity before output is triggered, in seconds + */ + SENSOR_ATTR_XBR818_DELAY_TIME = SENSOR_ATTR_PRIV_START, + /*! + * How long output stays triggered after no more activity is detected, in seconds + */ + SENSOR_ATTR_XBR818_LOCK_TIME, + /*! + * Noise floor Threshold for Radar, 16 first LSBs of the integer part. + */ + SENSOR_ATTR_XBR818_NOISE_FLOOR, + /*! + * RF Power for Radar, 0 to 7, LSB of the integer part. + */ + SENSOR_ATTR_XBR818_RF_POWER +}; + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_SENSOR_XBR818_H_ */ diff --git a/subsys/net/lib/tls_credentials/tls_credentials_trusted.c b/subsys/net/lib/tls_credentials/tls_credentials_trusted.c index 1a99ad08441..6b46b0b7680 100644 --- a/subsys/net/lib/tls_credentials/tls_credentials_trusted.c +++ b/subsys/net/lib/tls_credentials/tls_credentials_trusted.c @@ -401,6 +401,7 @@ int tls_credential_get(sec_tag_t tag, enum tls_credential_type type, if (info.size > *credlen) { ret = -EFBIG; + *credlen = info.size; goto cleanup; } diff --git a/tests/drivers/build_all/sensor/i2c.dtsi b/tests/drivers/build_all/sensor/i2c.dtsi index 9a84104e1ed..8a3c23883f1 100644 --- a/tests/drivers/build_all/sensor/i2c.dtsi +++ b/tests/drivers/build_all/sensor/i2c.dtsi @@ -1207,7 +1207,32 @@ test_i2c_tmag3001: tmag3001@a8 { status = "okay"; reg = <0xa8>; int-gpios = <&test_gpio 15 1>; - operation-mode = ; angle-magnitude-axis = ; }; + +test_i2c_tmp435: tmp435@a9 { + compatible = "ti,tmp435"; + reg = <0xa9>; + external-channel; + resistance-correction; + beta-compensation = <0x0f>; +}; + +test_i2c_xbr818: xbr818@aa { + compatible = "phosense,xbr818"; + reg = <0xaa>; + int-gpios = <&test_gpio 0 0>; + i2c-en-gpios = <&test_gpio 0 0>; +}; + +test_i2c_veml6031: veml6031@ab { + compatible = "vishay,veml6031"; + reg = <0xab>; +}; + +test_i2c_bmm350: bmm350@ac { + compatible = "bosch,bmm350"; + reg = <0xac>; + drdy-gpios = <&test_gpio 0 0>; +}; diff --git a/tests/drivers/build_all/sensor/sensors_trigger_none.conf b/tests/drivers/build_all/sensor/sensors_trigger_none.conf index 7343e805654..855d058a892 100644 --- a/tests/drivers/build_all/sensor/sensors_trigger_none.conf +++ b/tests/drivers/build_all/sensor/sensors_trigger_none.conf @@ -12,6 +12,7 @@ CONFIG_BMI08X_GYRO_TRIGGER_NONE=y CONFIG_BMI160_TRIGGER_NONE=y CONFIG_BMI270_TRIGGER_NONE=y CONFIG_BMM150_TRIGGER_NONE=y +CONFIG_BMM350_TRIGGER_NONE=y CONFIG_BMP388_TRIGGER_NONE=y CONFIG_BQ274XX_TRIGGER_NONE=y CONFIG_CCS811_TRIGGER_NONE=y diff --git a/tests/drivers/build_all/sensor/sensors_trigger_own.conf b/tests/drivers/build_all/sensor/sensors_trigger_own.conf index d206014b183..71a55a71791 100644 --- a/tests/drivers/build_all/sensor/sensors_trigger_own.conf +++ b/tests/drivers/build_all/sensor/sensors_trigger_own.conf @@ -11,6 +11,7 @@ CONFIG_BMI08X_GYRO_TRIGGER_OWN_THREAD=y CONFIG_BMI160_TRIGGER_OWN_THREAD=y CONFIG_BMI270_TRIGGER_OWN_THREAD=y CONFIG_BMM150_TRIGGER_OWN_THREAD=y +CONFIG_BMM350_TRIGGER_OWN_THREAD=y CONFIG_BMP388_TRIGGER_OWN_THREAD=y CONFIG_BQ274XX_TRIGGER_OWN_THREAD=y CONFIG_CCS811_TRIGGER_OWN_THREAD=y