From c8e46cc378340867bb1d948a2a4ae3ebe9b64db2 Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Wed, 31 Jan 2024 15:12:39 -0600 Subject: [PATCH] drivers: sensors: Add driver for LM95234 temperature sensor Add a driver for the National/TI LM95234 Quad Remote Diode and Local Temperature Sensor with SMBus Interface and TruTherm Technology. Signed-off-by: Robert Hancock --- drivers/sensor/ti/CMakeLists.txt | 1 + drivers/sensor/ti/Kconfig | 1 + drivers/sensor/ti/lm95234/CMakeLists.txt | 5 + drivers/sensor/ti/lm95234/Kconfig | 13 + drivers/sensor/ti/lm95234/lm95234.c | 322 ++++++++++++++++++++++ dts/bindings/sensor/national,lm95234.yaml | 8 + include/zephyr/drivers/sensor/lm95234.h | 20 ++ tests/drivers/build_all/sensor/i2c.dtsi | 6 + 8 files changed, 376 insertions(+) create mode 100644 drivers/sensor/ti/lm95234/CMakeLists.txt create mode 100644 drivers/sensor/ti/lm95234/Kconfig create mode 100644 drivers/sensor/ti/lm95234/lm95234.c create mode 100644 dts/bindings/sensor/national,lm95234.yaml create mode 100644 include/zephyr/drivers/sensor/lm95234.h diff --git a/drivers/sensor/ti/CMakeLists.txt b/drivers/sensor/ti/CMakeLists.txt index 4af7aa8996d0..3ea219ad0bcb 100644 --- a/drivers/sensor/ti/CMakeLists.txt +++ b/drivers/sensor/ti/CMakeLists.txt @@ -8,6 +8,7 @@ add_subdirectory_ifdef(CONFIG_INA219 ina219) add_subdirectory_ifdef(CONFIG_INA226 ina226) add_subdirectory_ifdef(CONFIG_INA23X ina23x) add_subdirectory_ifdef(CONFIG_INA3221 ina3221) +add_subdirectory_ifdef(CONFIG_LM95234 lm95234) add_subdirectory_ifdef(CONFIG_OPT3001 opt3001) add_subdirectory_ifdef(CONFIG_TI_HDC ti_hdc) add_subdirectory_ifdef(CONFIG_TI_HDC20XX ti_hdc20xx) diff --git a/drivers/sensor/ti/Kconfig b/drivers/sensor/ti/Kconfig index 02c5a6e9780f..00f6de6a94a4 100644 --- a/drivers/sensor/ti/Kconfig +++ b/drivers/sensor/ti/Kconfig @@ -8,6 +8,7 @@ source "drivers/sensor/ti/ina219/Kconfig" source "drivers/sensor/ti/ina226/Kconfig" source "drivers/sensor/ti/ina23x/Kconfig" source "drivers/sensor/ti/ina3221/Kconfig" +source "drivers/sensor/ti/lm95234/Kconfig" source "drivers/sensor/ti/opt3001/Kconfig" source "drivers/sensor/ti/ti_hdc/Kconfig" source "drivers/sensor/ti/ti_hdc20xx/Kconfig" diff --git a/drivers/sensor/ti/lm95234/CMakeLists.txt b/drivers/sensor/ti/lm95234/CMakeLists.txt new file mode 100644 index 000000000000..1eeeec6132b4 --- /dev/null +++ b/drivers/sensor/ti/lm95234/CMakeLists.txt @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources(lm95234.c) diff --git a/drivers/sensor/ti/lm95234/Kconfig b/drivers/sensor/ti/lm95234/Kconfig new file mode 100644 index 000000000000..e7e8528405d0 --- /dev/null +++ b/drivers/sensor/ti/lm95234/Kconfig @@ -0,0 +1,13 @@ +# LM95234 temperature sensor config + +# Copyright (c) 2024 Calian Advanced Technologies +# SPDX-License-Identifier: Apache-2.0 + +config LM95234 + bool "LM95234 Temperature Sensor" + default y + depends on DT_HAS_NATIONAL_LM95234_ENABLED + select I2C + help + Enable the driver for the LM95234 Quad Remote Diode and Local + Temperature Sensor with SMBus Interface. diff --git a/drivers/sensor/ti/lm95234/lm95234.c b/drivers/sensor/ti/lm95234/lm95234.c new file mode 100644 index 000000000000..9beabc6da28d --- /dev/null +++ b/drivers/sensor/ti/lm95234/lm95234.c @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2024 Calian Advanced Technologies + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT national_lm95234 + +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(LM95234, CONFIG_SENSOR_LOG_LEVEL); + +#define LM95234_REG_LOCAL_TEMP_SIGNED_MSB 0x10 +#define LM95234_REG_LOCAL_TEMP_SIGNED_LSB 0x20 +#define LM95234_REG_REMOTE_TEMP_1_SIGNED_MSB 0x11 +#define LM95234_REG_REMOTE_TEMP_1_SIGNED_LSB 0x21 +#define LM95234_REG_REMOTE_TEMP_2_SIGNED_MSB 0x12 +#define LM95234_REG_REMOTE_TEMP_2_SIGNED_LSB 0x22 +#define LM95234_REG_REMOTE_TEMP_3_SIGNED_MSB 0x13 +#define LM95234_REG_REMOTE_TEMP_3_SIGNED_LSB 0x23 +#define LM95234_REG_REMOTE_TEMP_4_SIGNED_MSB 0x14 +#define LM95234_REG_REMOTE_TEMP_4_SIGNED_LSB 0x24 +#define LM95234_REG_REMOTE_TEMP_1_UNSIGNED_MSB 0x19 +#define LM95234_REG_REMOTE_TEMP_1_UNSIGNED_LSB 0x29 +#define LM95234_REG_REMOTE_TEMP_2_UNSIGNED_MSB 0x1a +#define LM95234_REG_REMOTE_TEMP_2_UNSIGNED_LSB 0x2a +#define LM95234_REG_REMOTE_TEMP_3_UNSIGNED_MSB 0x1b +#define LM95234_REG_REMOTE_TEMP_3_UNSIGNED_LSB 0x2b +#define LM95234_REG_REMOTE_TEMP_4_UNSIGNED_MSB 0x1c +#define LM95234_REG_REMOTE_TEMP_4_UNSIGNED_LSB 0x2c +#define LM95234_REG_DIODE_MODEL_SELECT 0x30 +#define LM95234_REG_REMOTE_1_OFFSET 0x31 +#define LM95234_REG_REMOTE_2_OFFSET 0x32 +#define LM95234_REG_REMOTE_3_OFFSET 0x33 +#define LM95234_REG_REMOTE_4_OFFSET 0x34 +#define LM95234_REG_CONFIG 0x03 +#define LM95234_REG_CONV_RATE 0x04 +#define LM95234_REG_CHANNEL_CONV_ENABLE 0x05 +#define LM95234_REG_FILTER_SETTING 0x06 +#define LM95234_REG_ONESHOT 0x0f +#define LM95234_REG_COMMON_STATUS 0x02 +#define LM95234_REG_STATUS_1 0x07 +#define LM95234_REG_STATUS_2 0x08 +#define LM95234_REG_STATUS_3 0x09 +#define LM95234_REG_STATUS_4 0x0a +#define LM95234_REG_DIODE_MODEL_STATUS 0x38 +#define LM95234_REG_TCRIT1_MASK 0x0c +#define LM95234_REG_TCRIT2_MASK 0x0d +#define LM95234_REG_TCRIT3_MASK 0x0e +#define LM95234_REG_LOCAL_TCRIT_LIMIT 0x40 +#define LM95234_REG_REMOTE1_TCRIT1_LIMIT 0x41 +#define LM95234_REG_REMOTE2_TCRIT1_LIMIT 0x42 +#define LM95234_REG_REMOTE3_TCRIT_LIMIT 0x43 +#define LM95234_REG_REMOTE4_TCRIT_LIMIT 0x44 +#define LM95234_REG_REMOTE1_TCRIT23_LIMIT 0x49 +#define LM95234_REG_REMOTE2_TCRIT23_LIMIT 0x4a +#define LM95234_REG_COMMON_TCRIT_HYSTERESIS 0x5a +#define LM95234_REG_MANUF_ID 0xfe +#define LM95234_REG_REV_ID 0xff + +#define LM95234_MAN_ID 0x01 +#define LM95234_CHIP_ID 0x79 + +#define LM95234_CONFIG_STANDBY BIT(6) + +struct lm95234_data { + /** Temperatures in raw format read from sensor */ + int32_t local; + int32_t remote[4]; +}; + +struct lm95234_config { + struct i2c_dt_spec i2c; +}; + +static inline int lm95234_fetch_temp(const struct lm95234_config *cfg, struct lm95234_data *data, + enum sensor_channel chan, int32_t *output) +{ + int ret; + uint8_t val; + int32_t result = 0; + + if (chan != SENSOR_CHAN_AMBIENT_TEMP) { + uint16_t temp; + int offset = (chan - SENSOR_CHAN_LM95234_REMOTE_TEMP_1); + + ret = i2c_reg_read_byte_dt(&cfg->i2c, + LM95234_REG_REMOTE_TEMP_1_UNSIGNED_MSB + offset, &val); + if (ret) { + return ret; + } + temp = val << 8; + ret = i2c_reg_read_byte_dt(&cfg->i2c, + LM95234_REG_REMOTE_TEMP_1_UNSIGNED_LSB + offset, &val); + if (ret) { + return ret; + } + temp |= val; + result = temp; + } + + /* Read signed temperature if unsigned temperature is 0, or for local sensor */ + if (chan == SENSOR_CHAN_AMBIENT_TEMP || result == 0) { + int offset = chan == SENSOR_CHAN_AMBIENT_TEMP ? 0 : + (chan - SENSOR_CHAN_LM95234_REMOTE_TEMP_1 + 1); + int16_t temp; + + ret = i2c_reg_read_byte_dt(&cfg->i2c, + LM95234_REG_LOCAL_TEMP_SIGNED_MSB + offset, &val); + if (ret) { + return ret; + } + temp = val << 8; + ret = i2c_reg_read_byte_dt(&cfg->i2c, + LM95234_REG_LOCAL_TEMP_SIGNED_LSB + offset, &val); + if (ret) { + return ret; + } + temp |= val; + result = temp; + } + *output = result; + return 0; +} + +static int lm95234_sample_fetch(const struct device *dev, + enum sensor_channel chan) +{ + struct lm95234_data *data = dev->data; + const struct lm95234_config *cfg = dev->config; + enum pm_device_state pm_state; + int ret; + + (void)pm_device_state_get(dev, &pm_state); + if (pm_state != PM_DEVICE_STATE_ACTIVE) { + ret = -EIO; + return ret; + } + + switch ((uint32_t)chan) { + case SENSOR_CHAN_ALL: + ret = lm95234_fetch_temp(cfg, data, SENSOR_CHAN_AMBIENT_TEMP, &data->local); + if (ret) + return ret; + for (int i = 0; i < ARRAY_SIZE(data->remote); i++) { + ret = lm95234_fetch_temp(cfg, data, + SENSOR_CHAN_LM95234_REMOTE_TEMP_1 + i, + &data->remote[i]); + if (ret) + return ret; + } + break; + case SENSOR_CHAN_AMBIENT_TEMP: + ret = lm95234_fetch_temp(cfg, data, chan, &data->local); + break; + case SENSOR_CHAN_LM95234_REMOTE_TEMP_1: + case SENSOR_CHAN_LM95234_REMOTE_TEMP_2: + case SENSOR_CHAN_LM95234_REMOTE_TEMP_3: + case SENSOR_CHAN_LM95234_REMOTE_TEMP_4: + ret = lm95234_fetch_temp(cfg, data, chan, + &data->remote[chan - SENSOR_CHAN_LM95234_REMOTE_TEMP_1]); + break; + default: + ret = -ENOTSUP; + break; + } + + return ret; +} + +static int lm95234_channel_get(const struct device *dev, + enum sensor_channel chan, + struct sensor_value *val) +{ + struct lm95234_data *data = dev->data; + int32_t raw_temp; + + switch ((uint32_t)chan) { + case SENSOR_CHAN_AMBIENT_TEMP: + raw_temp = data->local; + break; + case SENSOR_CHAN_LM95234_REMOTE_TEMP_1: + case SENSOR_CHAN_LM95234_REMOTE_TEMP_2: + case SENSOR_CHAN_LM95234_REMOTE_TEMP_3: + case SENSOR_CHAN_LM95234_REMOTE_TEMP_4: + raw_temp = data->remote[chan - SENSOR_CHAN_LM95234_REMOTE_TEMP_1]; + break; + default: + return -ENOTSUP; + } + + /* Raw data format is 8 bits integer, 5 bits fractional, 3 bits zero */ + val->val1 = raw_temp / 256; + val->val2 = (raw_temp % 256) * 1000000 / 256; + return 0; +} + +static const struct sensor_driver_api lm95234_driver_api = { + .sample_fetch = lm95234_sample_fetch, + .channel_get = lm95234_channel_get, +}; + +static int lm95234_init(const struct device *dev) +{ + const struct lm95234_config *cfg = dev->config; + int ret = 0; + uint8_t value, model_select, model_status; + + if (!i2c_is_ready_dt(&cfg->i2c)) { + LOG_ERR("I2C dev not ready"); + return -ENODEV; + } + + ret = i2c_reg_read_byte_dt(&cfg->i2c, LM95234_REG_MANUF_ID, &value); + if (ret) { + LOG_ERR("Could not read manufacturer ID: %d", ret); + return ret; + } + if (value != LM95234_MAN_ID) { + LOG_ERR("Invalid manufacturer ID: %02x", value); + return -ENODEV; + } + ret = i2c_reg_read_byte_dt(&cfg->i2c, LM95234_REG_REV_ID, &value); + if (ret) { + LOG_ERR("Could not read revision ID: %d", ret); + return ret; + } + if (value != LM95234_CHIP_ID) { + LOG_ERR("Invalid chip ID: %02x", value); + return -ENODEV; + } + ret = i2c_reg_read_byte_dt(&cfg->i2c, LM95234_REG_CONFIG, &value); + if (ret) { + LOG_ERR("Could not read config: %d", ret); + return ret; + } + if (value & LM95234_CONFIG_STANDBY) { + value &= ~LM95234_CONFIG_STANDBY; + ret = i2c_reg_write_byte_dt(&cfg->i2c, LM95234_REG_CONFIG, value); + if (ret) { + LOG_ERR("Could not write config: %d", ret); + return ret; + } + } + ret = i2c_reg_read_byte_dt(&cfg->i2c, LM95234_REG_DIODE_MODEL_SELECT, &model_select); + if (ret) { + LOG_ERR("Could not read diode model select: %d", ret); + return ret; + } + ret = i2c_reg_read_byte_dt(&cfg->i2c, LM95234_REG_DIODE_MODEL_STATUS, &model_status); + if (ret) { + LOG_ERR("Could not read diode model status: %d", ret); + return ret; + } + /** + * Check if any remote inputs have a 3904 transistor detected but are not configured + * as such. If so, configure them as 3904 transistors. + */ + if (model_select & model_status) { + model_select &= ~model_status; + ret = i2c_reg_write_byte_dt(&cfg->i2c, LM95234_REG_DIODE_MODEL_SELECT, + model_select); + if (ret) { + LOG_ERR("Could not write diode model select: %d", ret); + return ret; + } + } + +#ifdef CONFIG_PM_DEVICE_RUNTIME + pm_device_init_suspended(dev); + + ret = pm_device_runtime_enable(dev); + if (ret < 0 && ret != -ENOTSUP) { + LOG_ERR("Failed to enable runtime power management"); + return ret; + } +#endif + + return 0; +} + +#ifdef CONFIG_PM_DEVICE + +static int lm95234_pm_action(const struct device *dev, enum pm_device_action action) +{ + switch (action) { + case PM_DEVICE_ACTION_TURN_ON: + case PM_DEVICE_ACTION_RESUME: + case PM_DEVICE_ACTION_TURN_OFF: + case PM_DEVICE_ACTION_SUSPEND: + break; + default: + return -ENOTSUP; + } + + return 0; +} + +#endif + +#define LM95234_INST(inst) \ +static struct lm95234_data lm95234_data_##inst; \ +static const struct lm95234_config lm95234_config_##inst = { \ + .i2c = I2C_DT_SPEC_INST_GET(inst), \ +}; \ +PM_DEVICE_DT_INST_DEFINE(inst, lm95234_pm_action); \ +SENSOR_DEVICE_DT_INST_DEFINE(inst, lm95234_init, \ + PM_DEVICE_DT_INST_GET(inst), \ + &lm95234_data_##inst, \ + &lm95234_config_##inst, \ + POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, \ + &lm95234_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(LM95234_INST) diff --git a/dts/bindings/sensor/national,lm95234.yaml b/dts/bindings/sensor/national,lm95234.yaml new file mode 100644 index 000000000000..a13577aca8f0 --- /dev/null +++ b/dts/bindings/sensor/national,lm95234.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Calian Advanced Technologies +# SPDX-License-Identifier: Apache-2.0 + +description: LM95234 Quad Remote Diode and Local Temperature Sensor with SMBus Interface + +compatible: "national,lm95234" + +include: [sensor-device.yaml, i2c-device.yaml] diff --git a/include/zephyr/drivers/sensor/lm95234.h b/include/zephyr/drivers/sensor/lm95234.h new file mode 100644 index 000000000000..6d25ceee7048 --- /dev/null +++ b/include/zephyr/drivers/sensor/lm95234.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2024, Calian Advanced Technologies + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_LM95234_H_ +#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_LM95234_H_ + +#include + +enum sensor_channel_lm95234 { + /* External temperature inputs */ + SENSOR_CHAN_LM95234_REMOTE_TEMP_1 = SENSOR_CHAN_PRIV_START, + SENSOR_CHAN_LM95234_REMOTE_TEMP_2, + SENSOR_CHAN_LM95234_REMOTE_TEMP_3, + SENSOR_CHAN_LM95234_REMOTE_TEMP_4 +}; + +#endif /* ZEPHYR_INCLUDE_DRIVERS_SENSOR_LM95234_H_ */ diff --git a/tests/drivers/build_all/sensor/i2c.dtsi b/tests/drivers/build_all/sensor/i2c.dtsi index c1689a21ca9c..08b5d8b72e19 100644 --- a/tests/drivers/build_all/sensor/i2c.dtsi +++ b/tests/drivers/build_all/sensor/i2c.dtsi @@ -1055,3 +1055,9 @@ test_i2c_shtc1: shtc1@8e { measure-mode = "low-power"; clock-stretching; }; + +test_i2c_lm95234: lm95234@8f { + compatible = "national,lm95234"; + reg = <0x8f>; + status = "okay"; +};