diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index f5808b7cd5dac6e..44e0ee56bd148c9 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -13,6 +13,7 @@ add_subdirectory(ite) add_subdirectory(jedec) add_subdirectory(maxim) add_subdirectory(meas) +add_subdirectory(melexis) add_subdirectory(memsic) add_subdirectory(microchip) add_subdirectory(nordic) diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index 0fc8252408d8d4c..ea963fb770bb2c6 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -97,6 +97,7 @@ source "drivers/sensor/ite/Kconfig" source "drivers/sensor/jedec/Kconfig" source "drivers/sensor/maxim/Kconfig" source "drivers/sensor/meas/Kconfig" +source "drivers/sensor/melexis/Kconfig" source "drivers/sensor/memsic/Kconfig" source "drivers/sensor/microchip/Kconfig" source "drivers/sensor/nordic/Kconfig" diff --git a/drivers/sensor/melexis/CMakeLists.txt b/drivers/sensor/melexis/CMakeLists.txt new file mode 100644 index 000000000000000..0089edeef4d9939 --- /dev/null +++ b/drivers/sensor/melexis/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (c) 2024 Florian Weber +# SPDX-License-Identifier: Apache-2.0 + +# zephyr-keep-sorted-start +add_subdirectory_ifdef(CONFIG_MLX90394 MLX90394) +# zephyr-keep-sorted-stop diff --git a/drivers/sensor/melexis/Kconfig b/drivers/sensor/melexis/Kconfig new file mode 100644 index 000000000000000..ebc4ee4bc6afc71 --- /dev/null +++ b/drivers/sensor/melexis/Kconfig @@ -0,0 +1,6 @@ +# Copyright (c) 2024 Florian Weber +# SPDX-License-Identifier: Apache-2.0 + +# zephyr-keep-sorted-start +source "drivers/sensor/melexis/MLX90394/Kconfig" +# zephyr-keep-sorted-stop diff --git a/drivers/sensor/melexis/MLX90394/CMakeLists.txt b/drivers/sensor/melexis/MLX90394/CMakeLists.txt new file mode 100644 index 000000000000000..800212bb85c438f --- /dev/null +++ b/drivers/sensor/melexis/MLX90394/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (c) Florian Weber +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources(mlx90394.c) +zephyr_library_sources_ifdef(CONFIG_SENSOR_ASYNC_API mlx90394_async.c mlx90394_decoder.c) diff --git a/drivers/sensor/melexis/MLX90394/Kconfig b/drivers/sensor/melexis/MLX90394/Kconfig new file mode 100644 index 000000000000000..2fbb670c6e10d43 --- /dev/null +++ b/drivers/sensor/melexis/MLX90394/Kconfig @@ -0,0 +1,11 @@ +# Copyright (c) 2024 Florian Weber +# SPDX-License-Identifier: Apache-2.0 + + +config MLX90394 + bool "MLX90394 Magnetometer" + default y + depends on DT_HAS_MELEXIS_MLX90394_ENABLED + select I2C + help + Enable driver for MLX90394 magnetometer. diff --git a/drivers/sensor/melexis/MLX90394/mlx90394.c b/drivers/sensor/melexis/MLX90394/mlx90394.c new file mode 100644 index 000000000000000..f4bb4dda2035a27 --- /dev/null +++ b/drivers/sensor/melexis/MLX90394/mlx90394.c @@ -0,0 +1,601 @@ +/* + * Copyright (c) 2024 Florian Weber + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT melexis_mlx90394 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mlx90394.h" +#include "mlx90394_reg.h" + +LOG_MODULE_REGISTER(MLX90394, CONFIG_SENSOR_LOG_LEVEL); + +static void mlx90394_convert_magn(enum mlx90394_reg_config_val config, struct sensor_value *val, + uint8_t sample_l, uint8_t sample_h) +{ + int64_t scale; + + if (config == MLX90394_CTRL2_CONFIG_HIGH_SENSITIVITY_LOW_NOISE) { + scale = MLX90394_HIGH_SENSITIVITY_MICRO_GAUSS_PER_BIT; + } else { + scale = MLX90394_HIGH_RANGE_MICRO_GAUSS_PER_BIT; + } + int64_t conv_val = (int16_t)((uint16_t)sample_l | (uint16_t)(sample_h << 8)) * scale; + + val->val1 = conv_val / 1000000; /* G */ + val->val2 = conv_val - (val->val1 * 1000000); /* uG */ +} + +static void mlx90394_convert_temp(struct sensor_value *val, uint8_t sample_l, uint8_t sample_h) +{ + int64_t conv_val = + sys_le16_to_cpu(sample_l | (sample_h << 8)) * MLX90394_MICRO_CELSIUS_PER_BIT; + val->val1 = conv_val / 1000000; /* C */ + val->val2 = (conv_val - (val->val1 * 1000000)); /* uC */ +} + +/* + * The user has to take care about that the requested channel was fetched before. Else the data will + * be random. Magnetic Flux Density is in Gauss, Temperature in Celsius + */ +static int mlx90394_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + struct mlx90394_data *data = dev->data; + + switch (chan) { + case SENSOR_CHAN_MAGN_X: { + mlx90394_convert_magn(data->config_val, val, data->sample.x_l, data->sample.x_h); + } break; + case SENSOR_CHAN_MAGN_Y: { + mlx90394_convert_magn(data->config_val, val, data->sample.y_l, data->sample.y_h); + } break; + case SENSOR_CHAN_MAGN_Z: { + mlx90394_convert_magn(data->config_val, val, data->sample.z_l, data->sample.z_h); + } break; + case SENSOR_CHAN_AMBIENT_TEMP: { + mlx90394_convert_temp(val, data->sample.temp_l, data->sample.temp_h); + } + case SENSOR_CHAN_MAGN_XYZ: { + mlx90394_convert_magn(data->config_val, val, data->sample.x_l, data->sample.x_h); + mlx90394_convert_magn(data->config_val, val + 1, data->sample.y_l, + data->sample.y_h); + mlx90394_convert_magn(data->config_val, val + 2, data->sample.z_l, + data->sample.z_h); + } break; + case SENSOR_CHAN_ALL: { + mlx90394_convert_magn(data->config_val, val, data->sample.x_l, data->sample.x_h); + mlx90394_convert_magn(data->config_val, val + 1, data->sample.y_l, + data->sample.y_h); + mlx90394_convert_magn(data->config_val, val + 2, data->sample.z_l, + data->sample.z_h); + mlx90394_convert_temp(val + 3, data->sample.temp_l, data->sample.temp_h); + } + default: { + LOG_DBG("Invalid channel %d", chan); + return -ENOTSUP; + } + } + return 0; +} + +static inline int mlx90394_update_register(const struct device *dev, const uint8_t reg_addr, + const uint8_t new_val, uint8_t *old_value) +{ + const struct mlx90394_config *cfg = dev->config; + + if (new_val != *old_value) { + *old_value = new_val; + return i2c_reg_write_byte_dt(&cfg->i2c, reg_addr, new_val); + } else { + return 0; + } +} + +static inline int mlx90394_fs_set(const struct device *dev, const struct sensor_value *val) +{ + struct mlx90394_data *data = dev->data; + + if (data->config_val > MLX90394_CTRL2_CONFIG_HIGH_RANGE_LOW_NOISE) { + /* + * in low noise mode --> different sensitivities possible + */ + + /* if the requested range is greater the driver switches from HIGH_SENSITIVITY to + * HIGH_RANGE + */ + uint8_t updated_ctrl2; + + if (val->val1 >= MLX90394_ATTR_FS_LOW_G) { + updated_ctrl2 = MLX90394_FIELD_MOD( + MLX90394_CTRL2_CONFIG, MLX90394_CTRL2_CONFIG_HIGH_RANGE_LOW_NOISE, + data->ctrl_reg_values.ctrl2); + } else { + updated_ctrl2 = + MLX90394_FIELD_MOD(MLX90394_CTRL2_CONFIG, + MLX90394_CTRL2_CONFIG_HIGH_SENSITIVITY_LOW_NOISE, + data->ctrl_reg_values.ctrl2); + } + return mlx90394_update_register(dev, MLX90394_REG_CTRL2, updated_ctrl2, + &data->ctrl_reg_values.ctrl2); + + } else { + LOG_ERR("different FS values only supported in low noise mode"); + return -ENOTSUP; + } +} +static inline int mlx90394_fs_get(const struct device *dev, struct sensor_value *val) +{ + struct mlx90394_data *data = dev->data; + + val->val2 = 0; + if (data->config_val == MLX90394_CTRL2_CONFIG_HIGH_SENSITIVITY_LOW_NOISE) { + val->val1 = MLX90394_ATTR_FS_LOW_G; + } else { + val->val1 = MLX90394_ATTR_FS_HIGH_G; + } + return 0; +} + +static inline int mlx90394_low_noise_set(const struct device *dev, struct sensor_value *val) +{ + struct mlx90394_data *data = dev->data; + + if (val->val1) { + if (data->config_val == MLX90394_CTRL2_CONFIG_HIGH_RANGE_LOW_CURRENT) { + return mlx90394_update_register(dev, MLX90394_REG_CTRL2, + MLX90394_CTRL2_CONFIG_HIGH_RANGE_LOW_NOISE, + &data->ctrl_reg_values.ctrl2); + } else { + return 0; /* already in Low Noise config - nothing to do */ + } + } else { + switch (data->config_val) { + case MLX90394_CTRL2_CONFIG_HIGH_RANGE_LOW_CURRENT: { + return 0; /* already in Low-Current config - nothing to do */ + } break; + case MLX90394_CTRL2_CONFIG_HIGH_RANGE_LOW_NOISE: { + return mlx90394_update_register( + dev, MLX90394_REG_CTRL2, + MLX90394_CTRL2_CONFIG_HIGH_RANGE_LOW_CURRENT, + &data->ctrl_reg_values.ctrl2); + } break; + case MLX90394_CTRL2_CONFIG_HIGH_SENSITIVITY_LOW_NOISE: { + LOG_ERR("High Sensitivity only supported in Low-Noise config, therefore " + "changing now to Low-Current config is not possible"); + return -ENOTSUP; + } break; + } + } + return 0; +} + +static inline int mlx90394_low_noise_get(const struct device *dev, struct sensor_value *val) +{ + struct mlx90394_data *data = dev->data; + + val->val2 = 0; + if (data->config_val == MLX90394_CTRL2_CONFIG_HIGH_RANGE_LOW_CURRENT) { + val->val1 = 0; + } else { + val->val1 = 1; + } + return 0; +} + +/* + * helper function to get/set attributes if set is 0 it will be a get, otherwise is interpreted + * as set + */ +static int mlx90394_attr_helper(const struct device *dev, enum sensor_channel chan, + unsigned int attr, struct sensor_value *val, uint8_t set) +{ + struct mlx90394_data *data = dev->data; + + switch (attr) { + case SENSOR_ATTR_FULL_SCALE: { + if (chan == SENSOR_CHAN_ACCEL_XYZ) { + if (set) { + return mlx90394_fs_set(dev, val); + } else { + return mlx90394_fs_get(dev, val); + } + } else { + return -ENOTSUP; + } + } break; + case MLX90394_SENSOR_ATTR_MAGN_LOW_NOISE: { + if (chan == SENSOR_CHAN_ACCEL_XYZ) { + if (set) { + return mlx90394_low_noise_set(dev, val); + } else { + return mlx90394_low_noise_get(dev, val); + } + } + } break; + case MLX90394_SENSOR_ATTR_MAGN_FILTER_XY: { + if (set) { + if (val->val1 > 7 || val->val1 < 0) { + return -EINVAL; + } + return mlx90394_update_register( + dev, MLX90394_REG_CTRL3, + MLX90394_FIELD_MOD(MLX90394_CTRL3_DIG_FILT_HALL_XY, val->val1, + data->ctrl_reg_values.ctrl3), + &data->ctrl_reg_values.ctrl3); + } else { + val->val1 = FIELD_GET(MLX90394_CTRL3_DIG_FILT_HALL_XY, + data->ctrl_reg_values.ctrl3); + val->val2 = 0; + } + } break; + case MLX90394_SENSOR_ATTR_MAGN_FILTER_Z: { + if (set) { + if (val->val1 > 7 || val->val1 < 0) { + return -EINVAL; + } + return mlx90394_update_register( + dev, MLX90394_REG_CTRL4, + MLX90394_FIELD_MOD(MLX90394_CTRL4_DIG_FILT_HALL_Z, val->val1, + data->ctrl_reg_values.ctrl4), + &data->ctrl_reg_values.ctrl4); + } else { + val->val1 = FIELD_GET(MLX90394_CTRL4_DIG_FILT_HALL_Z, + data->ctrl_reg_values.ctrl4); + val->val2 = 0; + } + } break; + case MLX90394_SENSOR_ATTR_MAGN_OSR: { + if (set) { + if (val->val1 > 1 || val->val1 < 0) { + return -EINVAL; + } + return mlx90394_update_register( + dev, MLX90394_REG_CTRL3, + MLX90394_FIELD_MOD(MLX90394_CTRL3_OSR_HALL, val->val1, + data->ctrl_reg_values.ctrl3), + &data->ctrl_reg_values.ctrl3); + } else { + val->val1 = FIELD_GET(MLX90394_CTRL3_OSR_HALL, data->ctrl_reg_values.ctrl4); + val->val2 = 0; + } + } break; + case MLX90394_SENSOR_ATTR_TEMP_FILTER: { + if (set) { + if (val->val1 > 7 || val->val1 < 0) { + return -EINVAL; + } + return mlx90394_update_register( + dev, MLX90394_REG_CTRL3, + MLX90394_FIELD_MOD(MLX90394_CTRL3_DIG_FILT_TEMP, val->val1, + data->ctrl_reg_values.ctrl3), + &data->ctrl_reg_values.ctrl3); + } else { + val->val1 = FIELD_GET(MLX90394_CTRL3_DIG_FILT_TEMP, + data->ctrl_reg_values.ctrl3); + val->val2 = 0; + } + } break; + case MLX90394_SENSOR_ATTR_TEMP_OSR: { + if (set) { + if (val->val1 > 1 || val->val1 < 0) { + return -EINVAL; + } + return mlx90394_update_register( + dev, MLX90394_REG_CTRL3, + MLX90394_FIELD_MOD(MLX90394_CTRL3_OSR_TEMP, val->val1, + data->ctrl_reg_values.ctrl3), + &data->ctrl_reg_values.ctrl3); + } else { + val->val1 = FIELD_GET(MLX90394_CTRL3_OSR_TEMP, data->ctrl_reg_values.ctrl4); + val->val2 = 0; + } + } break; + default: { + return -ENOTSUP; + } + } + + return 0; +} +static int mlx90394_attr_get(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, struct sensor_value *val) +{ + return mlx90394_attr_helper(dev, chan, (unsigned int)attr, val, 0); +} + +static int mlx90394_attr_set(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, const struct sensor_value *val) +{ + /* + * must be a copy because val is const + */ + struct sensor_value val_copy = *val; + + return mlx90394_attr_helper(dev, chan, (unsigned int)attr, &val_copy, 1); +} +static inline int mlx90394_check_who_am_i(const struct i2c_dt_spec *i2c) +{ + uint8_t buffer[2]; + int rc; + + rc = i2c_burst_read_dt(i2c, MLX90394_REG_CID, buffer, ARRAY_SIZE(buffer)); + if (rc != 0) { + LOG_ERR("Failed to read who-am-i register (rc=%d)", rc); + return -EIO; + } + + if (buffer[0] != MLX90394_CID || buffer[1] != MLX90394_DID) { + LOG_ERR("Wrong who-am-i value"); + return -EINVAL; + } + + return 0; +} + +static int mlx90394_write_read_dt(const struct i2c_dt_spec *i2c, uint8_t start_addr, + uint8_t *buffer_write, uint8_t *buffer_read, size_t cnt) +{ + int rc; + + rc = i2c_burst_write_dt(i2c, start_addr, buffer_write, cnt); + if (rc != 0) { + LOG_ERR("Failed to write %d bytes to register %d (rc=%d)", cnt, start_addr, rc); + return -EIO; + } + rc = i2c_burst_read_dt(i2c, start_addr, buffer_read, cnt); + if (rc != 0) { + LOG_ERR("Failed to read %d bytes from register %d (rc=%d)", cnt, start_addr, rc); + return -EIO; + } + + return 0; +} + +int mlx90394_sample_fetch_internal(const struct device *dev, enum sensor_channel chan) +{ + const struct mlx90394_config *cfg = dev->config; + struct mlx90394_data *data = dev->data; + int rc; + + rc = i2c_burst_read_dt(&cfg->i2c, MLX90394_REG_STAT1, (uint8_t *)&data->sample, + MLX90394_REG_TH + 1); + if (rc != 0) { + LOG_ERR("Failed to read bytes"); + return rc; + } + + if (FIELD_GET(MLX90394_STAT1_DRDY, data->sample.stat1) != 1) { + LOG_ERR("Data was not ready during fetch. In continues mode consider to " + "adjust " + "sample frequency"); + return -EIO; + } + return 0; +} + +int mlx90394_trigger_measurement_internal(const struct device *dev, enum sensor_channel chan) +{ + const struct mlx90394_config *cfg = dev->config; + struct mlx90394_data *data = dev->data; + int rc; + + /* + * set single measurement mode as default if not already done + */ + if (FIELD_GET(MLX90394_CTRL1_MODE, data->ctrl_reg_values.ctrl1) != + MLX90394_CTRL1_MODE_SINGLE) { + data->ctrl_reg_values.ctrl1 = + MLX90394_FIELD_MOD(MLX90394_CTRL1_MODE, MLX90394_CTRL1_MODE_SINGLE, + data->ctrl_reg_values.ctrl1); + } + + /* + * change channel bits and update ctrl4 if necessary only if the channel is another + * that during the Last Measurement + */ + if (chan != data->channel) { + switch (chan) { + case SENSOR_CHAN_MAGN_X: { + WRITE_BIT(data->ctrl_reg_values.ctrl1, MLX90394_CTRL1_X_EN_BIT, 1); + WRITE_BIT(data->ctrl_reg_values.ctrl1, MLX90394_CTRL1_Y_EN_BIT, 0); + WRITE_BIT(data->ctrl_reg_values.ctrl1, MLX90394_CTRL1_Z_EN_BIT, 0); + WRITE_BIT(data->ctrl_reg_values.ctrl4, MLX90394_CTRL4_T_EN_BIT, 0); + } break; + case SENSOR_CHAN_MAGN_Y: { + WRITE_BIT(data->ctrl_reg_values.ctrl1, MLX90394_CTRL1_X_EN_BIT, 0); + WRITE_BIT(data->ctrl_reg_values.ctrl1, MLX90394_CTRL1_Y_EN_BIT, 1); + WRITE_BIT(data->ctrl_reg_values.ctrl1, MLX90394_CTRL1_Z_EN_BIT, 0); + WRITE_BIT(data->ctrl_reg_values.ctrl4, MLX90394_CTRL4_T_EN_BIT, 0); + } break; + case SENSOR_CHAN_MAGN_Z: { + WRITE_BIT(data->ctrl_reg_values.ctrl1, MLX90394_CTRL1_X_EN_BIT, 0); + WRITE_BIT(data->ctrl_reg_values.ctrl1, MLX90394_CTRL1_Y_EN_BIT, 0); + WRITE_BIT(data->ctrl_reg_values.ctrl1, MLX90394_CTRL1_Z_EN_BIT, 1); + WRITE_BIT(data->ctrl_reg_values.ctrl4, MLX90394_CTRL4_T_EN_BIT, 0); + } break; + case SENSOR_CHAN_MAGN_XYZ: { + WRITE_BIT(data->ctrl_reg_values.ctrl1, MLX90394_CTRL1_X_EN_BIT, 1); + WRITE_BIT(data->ctrl_reg_values.ctrl1, MLX90394_CTRL1_Y_EN_BIT, 1); + WRITE_BIT(data->ctrl_reg_values.ctrl1, MLX90394_CTRL1_Z_EN_BIT, 1); + WRITE_BIT(data->ctrl_reg_values.ctrl4, MLX90394_CTRL4_T_EN_BIT, 0); + } break; + case SENSOR_CHAN_AMBIENT_TEMP: { + WRITE_BIT(data->ctrl_reg_values.ctrl1, MLX90394_CTRL1_X_EN_BIT, 0); + WRITE_BIT(data->ctrl_reg_values.ctrl1, MLX90394_CTRL1_Y_EN_BIT, 0); + WRITE_BIT(data->ctrl_reg_values.ctrl1, MLX90394_CTRL1_Z_EN_BIT, 0); + WRITE_BIT(data->ctrl_reg_values.ctrl4, MLX90394_CTRL4_T_EN_BIT, 1); + } break; + case SENSOR_CHAN_ALL: { + WRITE_BIT(data->ctrl_reg_values.ctrl1, MLX90394_CTRL1_X_EN_BIT, 1); + WRITE_BIT(data->ctrl_reg_values.ctrl1, MLX90394_CTRL1_Y_EN_BIT, 1); + WRITE_BIT(data->ctrl_reg_values.ctrl1, MLX90394_CTRL1_Z_EN_BIT, 1); + WRITE_BIT(data->ctrl_reg_values.ctrl4, MLX90394_CTRL4_T_EN_BIT, 1); + } break; + default: { + return -ENOTSUP; + } + } + rc = i2c_reg_write_byte_dt(&cfg->i2c, MLX90394_REG_CTRL4, + data->ctrl_reg_values.ctrl4); + if (rc != 0) { + LOG_ERR("Failed to write ctrl4"); + return rc; + } + data->channel = chan; + } + rc = i2c_reg_write_byte_dt(&cfg->i2c, MLX90394_REG_CTRL1, data->ctrl_reg_values.ctrl1); + if (rc != 0) { + LOG_ERR("Failed to write ctrl1"); + return rc; + } + return 0; +} + +static int mlx90394_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + int rt; + struct mlx90394_data *data = dev->data; + + rt = mlx90394_trigger_measurement_internal(dev, chan); + if (rt != 0) { + return rt; + } + k_usleep(data->measurement_time_us); + rt = mlx90394_sample_fetch_internal(dev, chan); + if (rt != 0) { + return rt; + } + return 0; +} + +static void mlx90394_calc_measurement_Time_us(struct mlx90394_data *data) +{ + int32_t enX = FIELD_GET(MLX90394_CTRL1_X_EN, data->ctrl_reg_values.ctrl1); + int32_t enY = FIELD_GET(MLX90394_CTRL1_Y_EN, data->ctrl_reg_values.ctrl1); + int32_t enZ = FIELD_GET(MLX90394_CTRL1_Z_EN, data->ctrl_reg_values.ctrl1); + int32_t enTemp = FIELD_GET(MLX90394_CTRL4_T_EN, data->ctrl_reg_values.ctrl4); + int32_t filterHallXY = + FIELD_GET(MLX90394_CTRL3_DIG_FILT_HALL_XY, data->ctrl_reg_values.ctrl3); + int32_t filterHallZ = + FIELD_GET(MLX90394_CTRL4_DIG_FILT_HALL_Z, data->ctrl_reg_values.ctrl4); + int32_t filterTemp = FIELD_GET(MLX90394_CTRL3_DIG_FILT_TEMP, data->ctrl_reg_values.ctrl3); + int32_t osrTemp = FIELD_GET(MLX90394_CTRL3_OSR_TEMP, data->ctrl_reg_values.ctrl3); + int32_t osrHall = FIELD_GET(MLX90394_CTRL3_OSR_HALL, data->ctrl_reg_values.ctrl3); + + int32_t conversion_time_us = + (osrHall + 1) * ((enX + enY) * MLX90394_CONVERSION_TIME_US_AXIS[filterHallXY] + + enZ * MLX90394_CONVERSION_TIME_US_AXIS[filterHallZ]) + + (osrTemp + 1) * enTemp * MLX90394_CONVERSION_TIME_US_AXIS[filterTemp]; + int32_t dsp_time_us = MLX90394_DSP_TIME_US[enTemp][enX + enY + enZ]; + + /* + * adding 5% tollerance from datasheet + */ + data->measurement_time_us = (conversion_time_us + dsp_time_us) * 105 / 100; +} + +static int mlx90394_init(const struct device *dev) +{ + const struct mlx90394_config *cfg = dev->config; + struct mlx90394_data *data = dev->data; + int rc; + + if (!i2c_is_ready_dt(&cfg->i2c)) { + LOG_ERR("I2C bus device not ready"); + return -ENODEV; + } + + /* + * Soft reset the chip + */ + rc = i2c_reg_write_byte_dt(&cfg->i2c, MLX90394_REG_RST, MLX90394_RST); + if (rc != 0) { + LOG_ERR("Failed to soft reset"); + return -EIO; + } + k_usleep(MLX90394_STARTUP_TIME_US); + + /* + * check chip ID + */ + rc = mlx90394_check_who_am_i(&cfg->i2c); + if (rc != 0) { + return rc; + } + + /* + * set all to default and read the settings back + */ + rc = mlx90394_write_read_dt(&cfg->i2c, MLX90394_REG_CTRL1, + (uint8_t *)&data->ctrl_reg_values.ctrl1, + (uint8_t *)&data->ctrl_reg_values.ctrl1, 2); + if (rc != 0) { + return rc; + } + + rc = mlx90394_write_read_dt(&cfg->i2c, MLX90394_REG_CTRL3, + (uint8_t *)&data->ctrl_reg_values.ctrl3, + (uint8_t *)&data->ctrl_reg_values.ctrl3, 2); + if (rc != 0) { + return rc; + } + + mlx90394_calc_measurement_Time_us(data); + +#ifdef CONFIG_SENSOR_ASYNC_API + data->dev = dev; + /* + * init work for fetching after measurement has completed + */ + k_work_init_delayable(&data->async_fetch_work, mlx90394_async_fetch); +#endif + + return 0; +} + +static const struct sensor_driver_api mlx90394_driver_api = { + .sample_fetch = mlx90394_sample_fetch, + .channel_get = mlx90394_channel_get, + .attr_get = mlx90394_attr_get, + .attr_set = mlx90394_attr_set, +#ifdef CONFIG_SENSOR_ASYNC_API + .submit = mlx90394_submit, + .get_decoder = mlx90394_get_decoder, +#endif +}; + +#define MLX90394_DEFINE(inst) \ + static struct mlx90394_data mlx90394_data_##inst = { \ + .sample = {.x_l = 0, \ + .x_h = 0, \ + .y_l = 0, \ + .y_h = 0, \ + .z_l = 0, \ + .z_h = 0, \ + .temp_l = 0, \ + .temp_h = 0}, \ + .channel = SENSOR_CHAN_MAGN_XYZ, \ + .config_val = FIELD_GET(MLX90394_CTRL2_CONFIG, MLX90394_CTRL2_DEFAULT), \ + .measurement_time_us = 0, \ + .ctrl_reg_values = {.ctrl1 = MLX90394_CTRL1_DEFAULT, \ + .ctrl2 = MLX90394_CTRL2_DEFAULT, \ + .ctrl3 = MLX90394_CTRL3_DEFAULT, \ + .ctrl4 = MLX90394_CTRL4_DEFAULT}}; \ + static const struct mlx90394_config mlx90394_config_##inst = { \ + .i2c = I2C_DT_SPEC_INST_GET(inst)}; \ + SENSOR_DEVICE_DT_INST_DEFINE(inst, mlx90394_init, NULL, &mlx90394_data_##inst, \ + &mlx90394_config_##inst, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &mlx90394_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(MLX90394_DEFINE) diff --git a/drivers/sensor/melexis/MLX90394/mlx90394.h b/drivers/sensor/melexis/MLX90394/mlx90394.h new file mode 100644 index 000000000000000..4d4bead0cc2f03f --- /dev/null +++ b/drivers/sensor/melexis/MLX90394/mlx90394.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2024 Florian Weber + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_MLX90394_MLX90394_H_ +#define ZEPHYR_DRIVERS_SENSOR_MLX90394_MLX90394_H_ + +#include +#include +#include +#include + +#include "mlx90394_reg.h" + +enum { + WOC = SENSOR_ATTR_PRIV_START +}; +/* + * Time it takes to start-up the device and switch to powerdown mode (after powercycle or soft + * reset) + */ +#define MLX90394_STARTUP_TIME_US 400 + +/* + * conversion time of one single axis for different filter orders if osr is enabled the value has + * to be doubled + */ +static const int32_t MLX90394_CONVERSION_TIME_US_AXIS[] = {111, 170, 270, 490, + 910, 1770, 3470, 6890}; + +/* conversion time per axis dependent of filter order */ + +/* + * DSP Time per measurement first index:temperature sensor count ( 0..1 ) second index: + * magnetic axis count (0..3) in us + */ +static const int32_t MLX90394_DSP_TIME_US[2][4] = {{0, 27, 50, 73}, {20, 63, 86, 110}}; + +/* Conversion values */ +#define MLX90394_HIGH_RANGE_MICRO_GAUSS_PER_BIT INT64_C(15000) +#define MLX90394_HIGH_SENSITIVITY_MICRO_GAUSS_PER_BIT INT64_C(1500) +#define MLX90394_MICRO_CELSIUS_PER_BIT INT64_C(20000) + +/* values for setting SENSOR_ATTR_FULL_SCALE */ +#define MLX90394_ATTR_FS_HIGH_G INT32_C(500) +#define MLX90394_ATTR_FS_LOW_G INT32_C(50) + +struct mlx90394_data { + struct __packed { + uint8_t stat1; + uint8_t x_l; + uint8_t x_h; + uint8_t y_l; + uint8_t y_h; + uint8_t z_l; + uint8_t z_h; + uint8_t stat2; + uint8_t temp_l; + uint8_t temp_h; + } sample; + enum sensor_channel channel; + enum mlx90394_reg_config_val config_val; + int32_t measurement_time_us; + struct __packed { + uint8_t ctrl1; + uint8_t ctrl2; + uint8_t ctrl3; + uint8_t ctrl4; + } ctrl_reg_values; +#ifdef CONFIG_SENSOR_ASYNC_API + struct { + struct rtio_iodev_sqe *iodev_sqe; + uint64_t timestamp; + enum mlx90394_reg_config_val config_val; + } work_ctx; + struct k_work_delayable async_fetch_work; + const struct device *dev; +#endif +}; + +struct mlx90394_config { + struct i2c_dt_spec i2c; +}; + +enum mlx90394_sensor_attribute { + MLX90394_SENSOR_ATTR_MAGN_LOW_NOISE = SENSOR_ATTR_PRIV_START, + MLX90394_SENSOR_ATTR_MAGN_FILTER_XY, + MLX90394_SENSOR_ATTR_MAGN_FILTER_Z, + MLX90394_SENSOR_ATTR_MAGN_OSR, + MLX90394_SENSOR_ATTR_TEMP_FILTER, + MLX90394_SENSOR_ATTR_TEMP_OSR +}; + +int mlx90394_sample_fetch_internal(const struct device *dev, enum sensor_channel chan); +int mlx90394_trigger_measurement_internal(const struct device *dev, enum sensor_channel chan); + +/* RTIO types and defines */ +#ifdef CONFIG_SENSOR_ASYNC_API + +/* shift value to use. */ +#define MLX90394_SHIFT_MAGN_HIGH_SENSITIVITY (6) +#define MLX90394_SHIFT_MAGN_HIGH_RANGE (9) +#define MLX90394_SHIFT_TEMP (10) + +void mlx90394_async_fetch(struct k_work *work); + +struct mlx90394_decoder_header { + uint64_t timestamp; + enum mlx90394_reg_config_val config_val; +} __packed; + +struct mlx90394_encoded_data { + struct mlx90394_decoder_header header; + int16_t readings[4]; +}; + +int mlx90394_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder); +void mlx90394_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe); +#endif + +#endif /* ZEPHYR_DRIVERS_SENSOR_MLX90394_MLX90394_H_ */ diff --git a/drivers/sensor/melexis/MLX90394/mlx90394_async.c b/drivers/sensor/melexis/MLX90394/mlx90394_async.c new file mode 100644 index 000000000000000..1772ff819b9c0cf --- /dev/null +++ b/drivers/sensor/melexis/MLX90394/mlx90394_async.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2024 Florian Weber + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT melexis_mlx90394 + +#include +#include +#include +#include + +#include "mlx90394.h" +#include "mlx90394_reg.h" + +#include + +LOG_MODULE_DECLARE(MLX90394, CONFIG_SENSOR_LOG_LEVEL); + +void mlx90394_async_fetch(struct k_work *work) +{ + + int rc; + struct k_work_delayable *dwork = k_work_delayable_from_work(work); + struct mlx90394_data *data = CONTAINER_OF(dwork, struct mlx90394_data, async_fetch_work); + const struct device *dev = data->dev; + const struct sensor_read_config *cfg = + data->work_ctx.iodev_sqe->sqe.iodev->data; /* hier weiter --> null pointer?!? */ + struct mlx90394_encoded_data *edata; + uint32_t buf_len = sizeof(struct mlx90394_encoded_data); + uint8_t *buf; + + rc = mlx90394_sample_fetch_internal(dev, cfg->channels->chan_type); + if (rc != 0) { + LOG_ERR("Failed to fetch samples"); + rtio_iodev_sqe_err(data->work_ctx.iodev_sqe, rc); + return; + } + /* Get the buffer for the frame, it may be allocated dynamically by the rtio context */ + rc = rtio_sqe_rx_buf(data->work_ctx.iodev_sqe, buf_len, buf_len, &buf, &buf_len); + if (rc != 0) { + LOG_ERR("Failed to get a read buffer of size %u bytes", buf_len); + rtio_iodev_sqe_err(data->work_ctx.iodev_sqe, rc); + return; + } + + edata = (struct mlx90394_encoded_data *)buf; + + /* buffered from submit */ + edata->header.timestamp = data->work_ctx.timestamp; + edata->header.config_val = data->work_ctx.config_val; + + switch (cfg->channels->chan_type) { + case SENSOR_CHAN_MAGN_X: { + edata->readings[0] = + (int16_t)((uint16_t)data->sample.x_l | (uint16_t)(data->sample.x_h << 8)); + } break; + case SENSOR_CHAN_MAGN_Y: { + edata->readings[1] = + (int16_t)((uint16_t)data->sample.y_l | (uint16_t)(data->sample.y_h << 8)); + } break; + case SENSOR_CHAN_MAGN_Z: { + edata->readings[2] = + (int16_t)((uint16_t)data->sample.z_l | (uint16_t)(data->sample.z_h << 8)); + } break; + case SENSOR_CHAN_AMBIENT_TEMP: { + edata->readings[3] = (int16_t)((uint16_t)data->sample.temp_l | + (uint16_t)(data->sample.temp_h << 8)); + } + case SENSOR_CHAN_MAGN_XYZ: { + edata->readings[0] = + (int16_t)((uint16_t)data->sample.x_l | (uint16_t)(data->sample.x_h << 8)); + edata->readings[1] = + (int16_t)((uint16_t)data->sample.y_l | (uint16_t)(data->sample.y_h << 8)); + edata->readings[2] = + (int16_t)((uint16_t)data->sample.z_l | (uint16_t)(data->sample.z_h << 8)); + } break; + case SENSOR_CHAN_ALL: { + edata->readings[0] = + (int16_t)((uint16_t)data->sample.x_l | (uint16_t)(data->sample.x_h << 8)); + edata->readings[1] = + (int16_t)((uint16_t)data->sample.y_l | (uint16_t)(data->sample.y_h << 8)); + edata->readings[2] = + (int16_t)((uint16_t)data->sample.z_l | (uint16_t)(data->sample.z_h << 8)); + edata->readings[3] = (int16_t)((uint16_t)data->sample.temp_l | + (uint16_t)(data->sample.temp_h << 8)); + } + default: { + LOG_DBG("Invalid channel %d", cfg->channels->chan_type); + rtio_iodev_sqe_err(data->work_ctx.iodev_sqe, -ENOTSUP); + return; + } + } + rtio_iodev_sqe_ok(data->work_ctx.iodev_sqe, 0); +} + +void mlx90394_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) +{ + int rc; + const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data; + struct mlx90394_data *data = dev->data; + + rc = mlx90394_trigger_measurement_internal(dev, cfg->channels->chan_type); + if (rc != 0) { + LOG_ERR("Failed to trigger measurement"); + rtio_iodev_sqe_err(iodev_sqe, rc); + } + + /* save information for the work item */ + data->work_ctx.timestamp = k_ticks_to_ns_floor64(k_uptime_ticks()); + data->work_ctx.iodev_sqe = iodev_sqe; + data->work_ctx.config_val = data->config_val; + + /* schedule work to read out sensor and inform the executor about completion with success */ + k_work_schedule(&data->async_fetch_work, K_USEC(data->measurement_time_us)); +} diff --git a/drivers/sensor/melexis/MLX90394/mlx90394_decoder.c b/drivers/sensor/melexis/MLX90394/mlx90394_decoder.c new file mode 100644 index 000000000000000..47c6f33d5fd72e8 --- /dev/null +++ b/drivers/sensor/melexis/MLX90394/mlx90394_decoder.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2024 Florian Weber + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "mlx90394.h" +#include + +#define DT_DRV_COMPAT melexis_mlx90394 + +static int mlx90394_decoder_get_frame_count(const uint8_t *buffer, struct sensor_chan_spec channel, + uint16_t *frame_count) +{ + ARG_UNUSED(buffer); + ARG_UNUSED(channel); + + /* This sensor lacks a FIFO; there will always only be one frame at a time. */ + *frame_count = 1; + return 0; +} + +static int mlx90394_decoder_get_size_info(struct sensor_chan_spec channel, size_t *base_size, + size_t *frame_size) +{ + switch (channel.chan_type) { + case SENSOR_CHAN_MAGN_X: + case SENSOR_CHAN_MAGN_Y: + case SENSOR_CHAN_MAGN_Z: + case SENSOR_CHAN_MAGN_XYZ: { + *base_size = sizeof(struct sensor_three_axis_data); + *frame_size = sizeof(struct sensor_three_axis_sample_data); + return 0; + } break; + case SENSOR_CHAN_AMBIENT_TEMP: { + *base_size = sizeof(struct sensor_q31_data); + *frame_size = sizeof(struct sensor_q31_sample_data); + return 0; + } break; + default: + return -ENOTSUP; + } +} + +static int mlx90394_convert_raw_magn_to_q31(int16_t reading, q31_t *out, + const enum mlx90394_reg_config_val config_val) +{ + int64_t intermediate; + + if (config_val == MLX90394_CTRL2_CONFIG_HIGH_SENSITIVITY_LOW_NOISE) { + intermediate = ((int64_t)reading * MLX90394_HIGH_SENSITIVITY_MICRO_GAUSS_PER_BIT) * + ((int64_t)INT32_MAX + 1) / + ((1 << MLX90394_SHIFT_MAGN_HIGH_SENSITIVITY) * INT64_C(1000000)); + } else { + intermediate = ((int64_t)reading * MLX90394_HIGH_RANGE_MICRO_GAUSS_PER_BIT) * + ((int64_t)INT32_MAX + 1) / + ((1 << MLX90394_SHIFT_MAGN_HIGH_RANGE) * INT64_C(1000000)); + } + + *out = CLAMP(intermediate, INT32_MIN, INT32_MAX); + return 0; +} +static int mlx90394_convert_raw_temp_to_q31(int16_t reading, q31_t *out) +{ + + int64_t intermediate = ((int64_t)reading * MLX90394_MICRO_CELSIUS_PER_BIT) * + ((int64_t)INT32_MAX + 1) / + ((1 << MLX90394_SHIFT_TEMP) * INT64_C(1000000)); + + /* make it dependent of resolution */ + + *out = CLAMP(intermediate, INT32_MIN, INT32_MAX); + return 0; +} + +static int mlx90394_decoder_decode(const uint8_t *buffer, struct sensor_chan_spec channel, + uint32_t *fit, uint16_t max_count, void *data_out) +{ + const struct mlx90394_encoded_data *edata = (const struct mlx90394_encoded_data *)buffer; + + if (*fit != 0) { + return 0; + } + + switch (channel.chan_type) { + case SENSOR_CHAN_MAGN_X: + case SENSOR_CHAN_MAGN_Y: + case SENSOR_CHAN_MAGN_Z: + case SENSOR_CHAN_MAGN_XYZ: { + struct sensor_three_axis_data *out = data_out; + + out->header.base_timestamp_ns = edata->header.timestamp; + out->header.reading_count = 1; + if (edata->header.config_val == MLX90394_CTRL2_CONFIG_HIGH_SENSITIVITY_LOW_NOISE) { + out->shift = MLX90394_SHIFT_MAGN_HIGH_SENSITIVITY; + } else { + out->shift = MLX90394_SHIFT_MAGN_HIGH_RANGE; + } + + mlx90394_convert_raw_magn_to_q31(edata->readings[0], &out->readings[0].x, + edata->header.config_val); + mlx90394_convert_raw_magn_to_q31(edata->readings[1], &out->readings[0].y, + edata->header.config_val); + mlx90394_convert_raw_magn_to_q31(edata->readings[2], &out->readings[0].z, + edata->header.config_val); + *fit = 1; + return 1; + } break; + case SENSOR_CHAN_AMBIENT_TEMP: { + struct sensor_q31_data *out = data_out; + + out->header.base_timestamp_ns = edata->header.timestamp; + out->header.reading_count = 1; + out->shift = MLX90394_SHIFT_TEMP; + mlx90394_convert_raw_temp_to_q31(edata->readings[3], &out->readings[0].temperature); + *fit = 1; + return 1; + } break; + default: + return -ENOTSUP; + } +} + +SENSOR_DECODER_API_DT_DEFINE() = { + .get_frame_count = mlx90394_decoder_get_frame_count, + .get_size_info = mlx90394_decoder_get_size_info, + .decode = mlx90394_decoder_decode, +}; + +int mlx90394_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder) +{ + ARG_UNUSED(dev); + *decoder = &SENSOR_DECODER_NAME(); + + return 0; +} diff --git a/drivers/sensor/melexis/MLX90394/mlx90394_reg.h b/drivers/sensor/melexis/MLX90394/mlx90394_reg.h new file mode 100644 index 000000000000000..ea3845fd5040b7f --- /dev/null +++ b/drivers/sensor/melexis/MLX90394/mlx90394_reg.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2024 Florian Weber + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_MLX90394_MLX90394_REG_H +#define ZEPHYR_DRIVERS_SENSOR_MLX90394_MLX90394_REG_H + +#include +/* REGISTERS */ +/* Status and measurent output */ +#define MLX90394_REG_STAT1 0x00 +#define MLX90394_REG_BXL 0x01 +#define MLX90394_REG_BXH 0x02 +#define MLX90394_REG_BYL 0x03 +#define MLX90394_REG_BYH 0x04 +#define MLX90394_REG_BZL 0x05 +#define MLX90394_REG_BZH 0x06 +#define MLX90394_REG_STAT2 0x07 +#define MLX90394_REG_TL 0x08 +#define MLX90394_REG_TH 0x09 + +/* Who Am I registers */ +#define MLX90394_REG_CID 0x0A +#define MLX90394_REG_DID 0x0B + +/* Control registers */ +#define MLX90394_REG_CTRL1 0x0E +#define MLX90394_REG_CTRL2 0x0F +#define MLX90394_REG_CTRL3 0x14 +#define MLX90394_REG_CTRL4 0x15 + +/* Reset register */ +#define MLX90394_REG_RST 0x11 + +/* Wake On Change registers */ +#define MLX90394_REG_WOC_XL 0x58 +#define MLX90394_REG_WOC_XH 0x59 +#define MLX90394_REG_WOC_YL 0x5A +#define MLX90394_REG_WOC_YH 0x5B +#define MLX90394_REG_WOC_ZL 0x5C +#define MLX90394_REG_WOC_ZH 0x5D + +/* VALUES*/ +/* STAT1 values RO */ +#define MLX90394_STAT1_DRDY BIT(0) +#define MLX90394_STAT1_DOR BIT(3) +#define MLX90394_STAT1_RT BIT(3) +#define MLX90394_STAT1_INT BIT(4) +#define MLX90394_STAT1_DEFAULT (MLX90394_STAT1_RT) + +/* STAT2 values RO */ +#define MLX90394_STAT2_HOVF_X BIT(0) +#define MLX90394_STAT2_HOVF_Y BIT(1) +#define MLX90394_STAT2_HOVF_Z BIT(2) +#define MLX90394_STAT2_DOR BIT(3) +#define MLX90394_STAT2_DEFAULT 0 + +/* Who-I-Am register values RO */ +#define MLX90394_CID 0x94 +#define MLX90394_DID 0xaa + +/* Write this value to reset Register soft resets the chip RW */ +#define MLX90394_RST 0x06 + +/* CTRL1 values RW */ +#define MLX90394_CTRL1_X_EN_BIT 4 +#define MLX90394_CTRL1_Y_EN_BIT 5 +#define MLX90394_CTRL1_Z_EN_BIT 6 +#define MLX90394_CTRL1_MODE GENMASK(3, 0) +#define MLX90394_CTRL1_MODE_SINGLE 1 +#define MLX90394_CTRL1_X_EN BIT(MLX90394_CTRL1_X_EN_BIT) +#define MLX90394_CTRL1_Y_EN BIT(MLX90394_CTRL1_Y_EN_BIT) +#define MLX90394_CTRL1_Z_EN BIT(MLX90394_CTRL1_Z_EN_BIT) +#define MLX90394_CTRL1_SWOK BIT(7) +#define MLX90394_CTRL1_PREP(MODE, X_EN, Y_EN, Z_EN, SWOK) \ + (FIELD_PREP(MLX90394_CTRL1_MODE, MODE) | FIELD_PREP(MLX90394_CTRL1_X_EN, X_EN) | \ + FIELD_PREP(MLX90394_CTRL1_Y_EN, Y_EN) | FIELD_PREP(MLX90394_CTRL1_Z_EN, Z_EN) | \ + FIELD_PREP(MLX90394_CTRL1_SWOK, SWOK)) +#define MLX90394_CTRL1_DEFAULT MLX90394_CTRL1_PREP(0, 1, 1, 1, 0) + +/* CTRL2 values RW */ +enum mlx90394_reg_config_val { + MLX90394_CTRL2_CONFIG_HIGH_RANGE_LOW_CURRENT = 0, + MLX90394_CTRL2_CONFIG_HIGH_RANGE_LOW_NOISE, + MLX90394_CTRL2_CONFIG_HIGH_SENSITIVITY_LOW_NOISE +}; +#define MLX90394_CTRL2_WOC_MODE GENMASK(1, 0) +#define MLX90394_CTRL2_INTREPB BIT(2) +#define MLX90394_CTRL2_INTB_SCL_B BIT(3) +#define MLX90394_CTRL2_INTDUR GENMASK(5, 4) +#define MLX90394_CTRL2_CONFIG GENMASK(7, 6) +#define MLX90394_CTRL2_PREP(WOC_MODE, INTREPB, INTB_SCL_B, INTDUR, CONFIG) \ + (FIELD_PREP(MLX90394_CTRL2_WOC_MODE, WOC_MODE) | \ + FIELD_PREP(MLX90394_CTRL2_INTREPB, INTREPB) | \ + FIELD_PREP(MLX90394_CTRL2_INTB_SCL_B, INTB_SCL_B) | \ + FIELD_PREP(MLX90394_CTRL2_INTDUR, INTDUR) | FIELD_PREP(MLX90394_CTRL2_CONFIG, CONFIG)) +#define MLX90394_CTRL2_DEFAULT \ + MLX90394_CTRL2_PREP(0, 0, 1, 0, MLX90394_CTRL2_CONFIG_HIGH_RANGE_LOW_NOISE) + +/* CTRL3 values RW */ +#define MLX90394_CTRL3_DIG_FILT_TEMP GENMASK(2, 0) +#define MLX90394_CTRL3_DIG_FILT_HALL_XY GENMASK(5, 3) +#define MLX90394_CTRL3_OSR_TEMP BIT(6) +#define MLX90394_CTRL3_OSR_HALL BIT(7) +#define MLX90394_CTRL3_PREP(DIG_FILT_TEMP, DIG_FILT_HALL_XY, OSR_TEMP, OSR_HALL) \ + (FIELD_PREP(MLX90394_CTRL3_DIG_FILT_TEMP, DIG_FILT_TEMP) | \ + FIELD_PREP(MLX90394_CTRL3_DIG_FILT_HALL_XY, DIG_FILT_HALL_XY) | \ + FIELD_PREP(MLX90394_CTRL3_OSR_TEMP, OSR_TEMP) | \ + FIELD_PREP(MLX90394_CTRL3_OSR_HALL, OSR_HALL)) +#define MLX90394_CTRL3_DEFAULT MLX90394_CTRL3_PREP(1, 4, 1, 1) + +/* CTRL4 values RW BIT(6) has to be always 0 so it is not included here */ +#define MLX90394_CTRL4_T_EN_BIT 5 +#define MLX90394_CTRL4_DIG_FILT_HALL_Z GENMASK(2, 0) +#define MLX90394_CTRL4_DRDY_EN BIT(3) +#define MLX90394_CTRL4_T_EN BIT(MLX90394_CTRL4_T_EN_BIT) +#define MLX90394_CTRL4_PREP(DIG_FILT_HALL_Z, DRDY_EN, T_EN) \ + (FIELD_PREP(MLX90394_CTRL4_DIG_FILT_HALL_Z, DIG_FILT_HALL_Z) | \ + FIELD_PREP(MLX90394_CTRL4_DRDY_EN, DRDY_EN) | FIELD_PREP(MLX90394_CTRL4_T_EN, T_EN) | \ + BIT(4) | BIT(7)) +#define MLX90394_CTRL4_DEFAULT MLX90394_CTRL4_PREP(5, 0, 0) + +/* helper function to modify only one field */ +#define MLX90394_FIELD_MOD(mask, new_field_val, val) \ + ((val & ~mask) | FIELD_PREP(mask, new_field_val)) + +#endif /* ZEPHYR_DRIVERS_SENSOR_MLX90394_MLX90394_REG_H */ diff --git a/dts/bindings/sensor/melexis,mlx90394.yaml b/dts/bindings/sensor/melexis,mlx90394.yaml new file mode 100644 index 000000000000000..367e43134043c16 --- /dev/null +++ b/dts/bindings/sensor/melexis,mlx90394.yaml @@ -0,0 +1,10 @@ +# Copyright (c) Florian Weber +# SPDX-License-Identifier: Apache-2.0 + +description: | + Melexis MLX90394 Magnetometer. See datasheet at + https://media.melexis.com/-/media/files/documents/datasheets/mlx90394-datasheet-melexis.pdf + +compatible: "melexis,mlx90394" + +include: [sensor-device.yaml, i2c-device.yaml]