From 0bd304f9d9c8314ec52d61974ba87fa7d40212a0 Mon Sep 17 00:00:00 2001 From: Bartosz Bilas Date: Mon, 15 Nov 2021 20:06:14 +0100 Subject: [PATCH] drivers: sensor: ina23x: add support for INA237 Add the new driver for INA237 variant which is quite similar to the already supported INA230. Signed-off-by: Bartosz Bilas --- drivers/sensor/ina23x/CMakeLists.txt | 1 + drivers/sensor/ina23x/Kconfig | 5 + drivers/sensor/ina23x/ina237.c | 316 ++++++++++++++++++++++++ drivers/sensor/ina23x/ina237.h | 45 ++++ dts/bindings/sensor/ti,ina237.yaml | 23 ++ include/dt-bindings/sensor/ina237.h | 89 +++++++ tests/drivers/build_all/sensor/i2c.dtsi | 10 + tests/drivers/build_all/sensor/prj.conf | 1 + 8 files changed, 490 insertions(+) create mode 100644 drivers/sensor/ina23x/ina237.c create mode 100644 drivers/sensor/ina23x/ina237.h create mode 100644 dts/bindings/sensor/ti,ina237.yaml create mode 100644 include/dt-bindings/sensor/ina237.h diff --git a/drivers/sensor/ina23x/CMakeLists.txt b/drivers/sensor/ina23x/CMakeLists.txt index cd165d8db5b..361265949bd 100644 --- a/drivers/sensor/ina23x/CMakeLists.txt +++ b/drivers/sensor/ina23x/CMakeLists.txt @@ -4,4 +4,5 @@ zephyr_library() zephyr_library_sources(ina23x_common.c) zephyr_library_sources_ifdef(CONFIG_INA230 ina230.c) +zephyr_library_sources_ifdef(CONFIG_INA237 ina237.c) zephyr_library_sources_ifdef(CONFIG_INA230_TRIGGER ina230_trigger.c) diff --git a/drivers/sensor/ina23x/Kconfig b/drivers/sensor/ina23x/Kconfig index a309c085dd5..4476409970f 100644 --- a/drivers/sensor/ina23x/Kconfig +++ b/drivers/sensor/ina23x/Kconfig @@ -15,6 +15,11 @@ config INA230 help Enable driver for INA230/INA231. +config INA237 + bool "INA237" + help + Enable driver for INA237. + config INA230_TRIGGER bool "INA230 trigger mode" depends on INA230 diff --git a/drivers/sensor/ina23x/ina237.c b/drivers/sensor/ina23x/ina237.c new file mode 100644 index 00000000000..8d1c7ad235e --- /dev/null +++ b/drivers/sensor/ina23x/ina237.c @@ -0,0 +1,316 @@ +/* + * Copyright 2021 Grinn + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ti_ina237 + +#include +#include +#include "ina237.h" +#include "ina23x_common.h" + +LOG_MODULE_REGISTER(INA237, CONFIG_SENSOR_LOG_LEVEL); + +/** + * @brief Internal fixed value of INA237 that is used to ensure + * scaling is properly maintained. + * + */ +#define INA237_INTERNAL_FIXED_SCALING_VALUE 8192 + +/** + * @brief The LSB value for the bus voltage register. + * + */ +#define INA237_BUS_VOLTAGE_LSB 3125 + +/** + * @brief The LSB value for the power register. + * + */ +#define INA237_POWER_VALUE_LSB 2 + +/** + * @brief sensor value get + * + * @retval 0 for success + * @retval -ENOTSUP for unsupported channels + */ +static int ina237_channel_get(const struct device *dev, + enum sensor_channel chan, + struct sensor_value *val) +{ + struct ina237_data *data = dev->data; + const struct ina237_config *config = dev->config; + + switch (chan) { + case SENSOR_CHAN_VOLTAGE: + if (config->current_lsb == INA23X_CURRENT_LSB_1MA) { + uint32_t bus_mv = ((data->bus_voltage * + INA237_BUS_VOLTAGE_LSB) / 1000); + + val->val1 = bus_mv / 1000U; + val->val2 = (bus_mv % 1000) * 1000; + } else { + val->val1 = data->bus_voltage; + val->val2 = 0; + } + break; + + case SENSOR_CHAN_CURRENT: + if (config->current_lsb == INA23X_CURRENT_LSB_1MA) { + /** + * If current is negative, convert it to a + * magnitude and return the negative of that + * magnitude. + */ + if (data->current & INA23X_CURRENT_SIGN_BIT) { + uint16_t current_mag = (~data->current + 1); + + val->val1 = -(current_mag / 10000U); + val->val2 = -(current_mag % 10000) * 100; + + } else { + val->val1 = data->current / 10000U; + val->val2 = (data->current % 10000) * 100; + } + } else { + val->val1 = data->current; + val->val2 = 0; + } + break; + + case SENSOR_CHAN_POWER: + if (config->current_lsb == INA23X_CURRENT_LSB_1MA) { + uint32_t power_mw = ((data->power * + config->current_lsb * + INA237_POWER_VALUE_LSB) / 10); + + val->val1 = power_mw / 10000U; + val->val2 = (power_mw % 10000) * 100; + } else { + val->val1 = data->power; + val->val2 = 0; + } + break; + + default: + return -ENOTSUP; + } + + return 0; +} + +/** + * @brief sensor sample fetch + * + * @retval 0 for success + * @retval -ENOTSUP for unsupported channels + */ +static int ina237_sample_fetch(const struct device *dev, + enum sensor_channel chan) +{ + struct ina237_data *data = dev->data; + const struct ina237_config *config = dev->config; + int ret; + + if (chan != SENSOR_CHAN_ALL && + chan != SENSOR_CHAN_VOLTAGE && + chan != SENSOR_CHAN_CURRENT && + chan != SENSOR_CHAN_POWER) { + return -ENOTSUP; + } + + if ((chan == SENSOR_CHAN_ALL) || (chan == SENSOR_CHAN_VOLTAGE)) { + ret = ina23x_reg_read_16(&config->bus, INA237_REG_BUS_VOLT, &data->bus_voltage); + if (ret < 0) { + LOG_ERR("Failed to read bus voltage"); + return ret; + } + } + + if ((chan == SENSOR_CHAN_ALL) || (chan == SENSOR_CHAN_CURRENT)) { + ret = ina23x_reg_read_16(&config->bus, INA237_REG_CURRENT, &data->current); + if (ret < 0) { + LOG_ERR("Failed to read current"); + return ret; + } + } + + if ((chan == SENSOR_CHAN_ALL) || (chan == SENSOR_CHAN_POWER)) { + ret = ina23x_reg_read_24(&config->bus, INA237_REG_POWER, &data->power); + if (ret < 0) { + LOG_ERR("Failed to read power"); + return ret; + } + } + + return 0; +} + +/** + * @brief sensor attribute set + * + * @retval 0 for success + * @retval -ENOTSUP for unsupported channels + * @retval -EIO for i2c write failure + */ +static int ina237_attr_set(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + const struct ina237_config *config = dev->config; + uint16_t data = val->val1; + + switch (attr) { + case SENSOR_ATTR_CONFIGURATION: + return ina23x_reg_write(&config->bus, INA237_REG_CONFIG, data); + case SENSOR_ATTR_CALIBRATION: + return ina23x_reg_write(&config->bus, INA237_REG_CALIB, data); + default: + LOG_ERR("INA237 attribute not supported."); + return -ENOTSUP; + } +} + +/** + * @brief sensor attribute get + * + * @retval 0 for success + * @retval -ENOTSUP for unsupported channels + * @retval -EIO for i2c read failure + */ +static int ina237_attr_get(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, + struct sensor_value *val) +{ + const struct ina237_config *config = dev->config; + uint16_t data; + int ret; + + switch (attr) { + case SENSOR_ATTR_CONFIGURATION: + ret = ina23x_reg_read_16(&config->bus, INA237_REG_CONFIG, &data); + if (ret < 0) { + return ret; + } + break; + case SENSOR_ATTR_CALIBRATION: + ret = ina23x_reg_read_16(&config->bus, INA237_REG_CALIB, &data); + if (ret < 0) { + return ret; + } + break; + default: + LOG_ERR("INA237 attribute not supported."); + return -ENOTSUP; + } + + val->val1 = data; + val->val2 = 0; + + return 0; +} + +/** + * @brief sensor calibrate + * + * @retval 0 for success + * @retval -EIO for i2c write failure + */ +static int ina237_calibrate(const struct device *dev) +{ + const struct ina237_config *config = dev->config; + uint16_t val; + int ret; + + val = ((INA237_INTERNAL_FIXED_SCALING_VALUE * config->current_lsb * config->rshunt) / 100); + + ret = ina23x_reg_write(&config->bus, INA237_REG_CALIB, val); + if (ret < 0) { + return ret; + } + + return 0; +} + +/** + * @brief Initialize the INA237 + * + * @retval 0 for success + * @retval -EINVAL on error + */ +static int ina237_init(const struct device *dev) +{ + const struct ina237_config *config = dev->config; + uint16_t id; + int ret; + + if (!device_is_ready(config->bus.bus)) { + LOG_ERR("I2C bus %s is not ready", config->bus.bus->name); + return -ENODEV; + } + + ret = ina23x_reg_read_16(&config->bus, INA237_REG_MANUFACTURER_ID, &id); + if (ret < 0) { + LOG_ERR("Failed to read manufacturer register!"); + return ret; + } + + if (id != INA237_MANUFACTURER_ID) { + LOG_ERR("Manufacturer ID doesn't match!"); + return -ENODEV; + } + + ret = ina23x_reg_write(&config->bus, INA237_REG_ADC_CONFIG, config->adc_config); + if (ret < 0) { + LOG_ERR("Failed to write ADC configuration register!"); + return ret; + } + + ret = ina23x_reg_write(&config->bus, INA237_REG_CONFIG, config->config); + if (ret < 0) { + LOG_ERR("Failed to write configuration register!"); + return ret; + } + + ret = ina237_calibrate(dev); + if (ret < 0) { + LOG_ERR("Failed to write calibration register!"); + return ret; + } + + return 0; +} + +static const struct sensor_driver_api ina237_driver_api = { + .attr_set = ina237_attr_set, + .attr_get = ina237_attr_get, + .sample_fetch = ina237_sample_fetch, + .channel_get = ina237_channel_get, +}; + +BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) > 0, + "No compatible ina237 instances found"); + +#define INA237_DRIVER_INIT(inst) \ + static struct ina237_data ina237_data_##inst; \ + static const struct ina237_config ina237_config_##inst = { \ + .bus = I2C_DT_SPEC_INST_GET(inst), \ + .config = DT_INST_PROP(inst, config), \ + .adc_config = DT_INST_PROP(inst, adc_config), \ + .current_lsb = DT_INST_PROP(inst, current_lsb), \ + .rshunt = DT_INST_PROP(inst, rshunt), \ + }; \ + DEVICE_DT_INST_DEFINE(inst, \ + &ina237_init, \ + NULL, \ + &ina237_data_##inst, \ + &ina237_config_##inst, \ + POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, \ + &ina237_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(INA237_DRIVER_INIT) diff --git a/drivers/sensor/ina23x/ina237.h b/drivers/sensor/ina23x/ina237.h new file mode 100644 index 00000000000..39c1dd44377 --- /dev/null +++ b/drivers/sensor/ina23x/ina237.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Grinn + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_INA23X_INA237_H_ +#define ZEPHYR_DRIVERS_SENSOR_INA23X_INA237_H_ + +#include + +#define INA237_REG_CONFIG 0x00 +#define INA237_REG_ADC_CONFIG 0x01 +#define INA237_REG_CALIB 0x02 +#define INA237_REG_SHUNT_VOLT 0x04 +#define INA237_REG_BUS_VOLT 0x05 +#define INA237_REG_MASK 0x06 +#define INA237_REG_CURRENT 0x07 +#define INA237_REG_POWER 0x08 +#define INA237_REG_ALERT 0x0B +#define INA237_REG_SOVL 0x0C +#define INA237_REG_SUVL 0x0D +#define INA237_REG_BOVL 0x0E +#define INA237_REG_BUVL 0x0F +#define INA237_REG_TEMP_LIMIT 0x10 +#define INA237_REG_PWR_LIMIT 0x11 +#define INA237_REG_MANUFACTURER_ID 0x3E + +#define INA237_MANUFACTURER_ID 0x5449 + +struct ina237_data { + uint16_t current; + uint16_t bus_voltage; + uint32_t power; +}; + +struct ina237_config { + struct i2c_dt_spec bus; + uint16_t config; + uint16_t adc_config; + uint16_t current_lsb; + uint16_t rshunt; +}; + +#endif /* ZEPHYR_DRIVERS_SENSOR_INA23X_INA237_H_ */ diff --git a/dts/bindings/sensor/ti,ina237.yaml b/dts/bindings/sensor/ti,ina237.yaml new file mode 100644 index 00000000000..04477888cab --- /dev/null +++ b/dts/bindings/sensor/ti,ina237.yaml @@ -0,0 +1,23 @@ +# +# Copyright 2021 Grinn +# +# SPDX-License-Identifier: Apache-2.0 +# + +description: | + TI INA237 Bidirectional Current and Power Monitor. + The file should be included + in the DeviceTree and it provides macros that can be used for + initializing the configuration registers. + +compatible: "ti,ina237" + +include: ti,ina23x-common.yaml + +properties: + adc-config: + type: int + required: true + description: | + Value of the ADC configuration register (ADC conversion times, + averaging, operating mode and etc). diff --git a/include/dt-bindings/sensor/ina237.h b/include/dt-bindings/sensor/ina237.h new file mode 100644 index 00000000000..f8a7046303f --- /dev/null +++ b/include/dt-bindings/sensor/ina237.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2021 Grinn + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_INA237_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_INA237_H_ + +#include + +/* Operating Mode */ +#define INA237_OPER_MODE_SHUTDOWN 0x00 +#define INA237_OPER_MODE_TRIG_BUS_VOLTAGE_SINGLE_SHOT 0x01 +#define INA237_OPER_MODE_TRIG_SHUNT_VOLTAGE_TRIG_SINGLE_SHOT 0x02 +#define INA237_OPER_MODE_TRIG_SHUNT_BUS_VOLTAGE_SINGLE_SHOT 0x03 +#define INA237_OPER_MODE_TRIG_TEMP_SINGLE_SHOT 0x05 +#define INA237_OPER_MODE_TRIG_TEMP_BUS_SINGLE_SHOT 0x06 +#define INA237_OPER_MODE_TRIG_TEMP_SHUNT_SINGLE_SHOT 0x07 +#define INA237_OPER_MODE_CONT_BUS_VOLTAGE 0x09 +#define INA237_OPER_MODE_CONT_SHUNT_VOLTAGE 0x0A +#define INA237_OPER_MODE_CONT_SHUNT_BUS_VOLTAGE 0x0B +#define INA237_OPER_MODE_CONT_TEMP 0x0C +#define INA237_OPER_MODE_CONT_BUS_VOLTAGE_TEMP 0x0D +#define INA237_OPER_MODE_CONT_TEMP_SHUNT_VOLTAGE 0x0E +#define INA237_OPER_MODE_CONT_BUS_SHUNT_VOLTAGE_TEMP 0x0F + +/* Conversion time for bus, shunt and temp in micro-seconds */ +#define INA237_CONV_TIME_50 0x00 +#define INA237_CONV_TIME_84 0x01 +#define INA237_CONV_TIME_150 0x02 +#define INA237_CONV_TIME_280 0x03 +#define INA237_CONV_TIME_540 0x04 +#define INA237_CONV_TIME_1052 0x05 +#define INA237_CONV_TIME_2074 0x06 +#define INA237_CONV_TIME_4120 0x07 + +/* Averaging Mode */ +#define INA237_AVG_MODE_1 0x00 +#define INA237_AVG_MODE_4 0x01 +#define INA237_AVG_MODE_16 0x02 +#define INA237_AVG_MODE_64 0x03 +#define INA237_AVG_MODE_128 0x04 +#define INA237_AVG_MODE_256 0x05 +#define INA237_AVG_MODE_512 0x06 +#define INA237_AVG_MODE_1024 0x07 + +/* Reset Mode */ +#define INA237_RST_NORMAL_OPERATION 0x00 +#define INA237_RST_SYSTEM_RESET 0x01 + +/* Delay for initial ADC conversion in steps of 2 ms */ +#define INA237_INIT_ADC_DELAY_0_S 0x00 +#define INA237_INIT_ADC_DELAY_2_MS 0x01 +#define INA237_INIT_ADC_DELAY_510_MS 0xFF + +/* Shunt full scale range selection across IN+ and IN–. */ +#define INA237_ADC_RANGE_163_84 0x00 +#define INA237_ADC_RANGE_40_96 0x01 + +/** + * @brief Macro for creating the INA237 configuration value + * + * @param rst_mode Reset mode. + * @param convdly Delay for initial ADC conversion in steps of 2 ms. + * @param adc_range Shunt full scale range selection across IN+ and IN–. + * + */ +#define INA237_CONFIG(rst_mode, \ + convdly, \ + adc_range) \ + (((rst_mode) << 15) | ((convdly) << 6) | ((adc_range) << 4)) + +/** + * @brief Macro for creating the INA237 ADC configuration value + * + * @param mode Operating mode. + * @param vshct Conversion time for shunt voltage. + * @param vbusct Conversion time for bus voltage. + * @param vtct Conversion time for temperature. + * @param avg Averaging mode. + */ +#define INA237_ADC_CONFIG(mode, \ + vshct, \ + vbusct, \ + vtct, \ + avg) \ + (((mode) << 12) | ((vbusct) << 9) | ((vshct) << 6) | ((vtct) << 3) | (avg)) + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_INA237_H_ */ diff --git a/tests/drivers/build_all/sensor/i2c.dtsi b/tests/drivers/build_all/sensor/i2c.dtsi index e1b23ad4186..dd151ea32b7 100644 --- a/tests/drivers/build_all/sensor/i2c.dtsi +++ b/tests/drivers/build_all/sensor/i2c.dtsi @@ -696,3 +696,13 @@ test_i2c_ina231: ina231@51 { alert-limit = <0>; irq-gpios = <&test_gpio 0 0>; }; + +test_i2c_ina237: ina237@52 { + compatible = "ti,ina237"; + label = "INA237"; + reg = <0x52>; + config = <0>; + current-lsb = <1>; + adc-config = <0>; + rshunt = <0>; +}; diff --git a/tests/drivers/build_all/sensor/prj.conf b/tests/drivers/build_all/sensor/prj.conf index bdb51cb22b6..a33d4355814 100644 --- a/tests/drivers/build_all/sensor/prj.conf +++ b/tests/drivers/build_all/sensor/prj.conf @@ -100,3 +100,4 @@ CONFIG_VCNL4040=y CONFIG_VL53L0X=y CONFIG_INA23X=y CONFIG_INA230=y +CONFIG_INA237=y