From 439d3b29739f7290b200bc5707c4421c542fbd68 Mon Sep 17 00:00:00 2001 From: Audun Korneliussen Date: Tue, 1 Oct 2024 15:04:07 +0200 Subject: [PATCH] drivers: sensor: npm2100: Add driver for npm2100 pmic Add sensor driver for npm2100 pmic. This pmic performs measurements of battery voltage, regulator voltage and die temperature. Configurable pmic attributes are also organized under this driver. Signed-off-by: Audun Korneliussen --- drivers/sensor/nordic/CMakeLists.txt | 1 + drivers/sensor/nordic/Kconfig | 1 + .../sensor/nordic/npm2100_vbat/CMakeLists.txt | 6 + drivers/sensor/nordic/npm2100_vbat/Kconfig | 12 + .../sensor/nordic/npm2100_vbat/npm2100_vbat.c | 563 ++++++++++++++++++ dts/bindings/sensor/nordic,npm2100-vbat.yaml | 25 + include/zephyr/drivers/sensor/npm2100_vbat.h | 25 + tests/drivers/build_all/sensor/i2c.dtsi | 9 + 8 files changed, 642 insertions(+) create mode 100644 drivers/sensor/nordic/npm2100_vbat/CMakeLists.txt create mode 100644 drivers/sensor/nordic/npm2100_vbat/Kconfig create mode 100644 drivers/sensor/nordic/npm2100_vbat/npm2100_vbat.c create mode 100644 dts/bindings/sensor/nordic,npm2100-vbat.yaml create mode 100644 include/zephyr/drivers/sensor/npm2100_vbat.h diff --git a/drivers/sensor/nordic/CMakeLists.txt b/drivers/sensor/nordic/CMakeLists.txt index 89265c0e59d..49a0a48d7fd 100644 --- a/drivers/sensor/nordic/CMakeLists.txt +++ b/drivers/sensor/nordic/CMakeLists.txt @@ -4,5 +4,6 @@ # zephyr-keep-sorted-start add_subdirectory(temp) add_subdirectory_ifdef(CONFIG_NPM1300_CHARGER npm1300_charger) +add_subdirectory_ifdef(CONFIG_NPM2100_VBAT npm2100_vbat) add_subdirectory_ifdef(CONFIG_QDEC_NRFX qdec_nrfx) # zephyr-keep-sorted-stop diff --git a/drivers/sensor/nordic/Kconfig b/drivers/sensor/nordic/Kconfig index 27f1cfedf59..0cdbaaf41e7 100644 --- a/drivers/sensor/nordic/Kconfig +++ b/drivers/sensor/nordic/Kconfig @@ -3,6 +3,7 @@ # zephyr-keep-sorted-start source "drivers/sensor/nordic/npm1300_charger/Kconfig" +source "drivers/sensor/nordic/npm2100_vbat/Kconfig" source "drivers/sensor/nordic/qdec_nrfx/Kconfig" source "drivers/sensor/nordic/temp/Kconfig" # zephyr-keep-sorted-stop diff --git a/drivers/sensor/nordic/npm2100_vbat/CMakeLists.txt b/drivers/sensor/nordic/npm2100_vbat/CMakeLists.txt new file mode 100644 index 00000000000..afc2cb36fe5 --- /dev/null +++ b/drivers/sensor/nordic/npm2100_vbat/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources(npm2100_vbat.c) diff --git a/drivers/sensor/nordic/npm2100_vbat/Kconfig b/drivers/sensor/nordic/npm2100_vbat/Kconfig new file mode 100644 index 00000000000..8a2baa658ed --- /dev/null +++ b/drivers/sensor/nordic/npm2100_vbat/Kconfig @@ -0,0 +1,12 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 + +config NPM2100_VBAT + bool "NPM2100 Battery Voltage" + default y + depends on DT_HAS_NORDIC_NPM2100_VBAT_ENABLED + select I2C + select MFD + help + Enable NPM2100 battery voltage driver. diff --git a/drivers/sensor/nordic/npm2100_vbat/npm2100_vbat.c b/drivers/sensor/nordic/npm2100_vbat/npm2100_vbat.c new file mode 100644 index 00000000000..f1ab25938c3 --- /dev/null +++ b/drivers/sensor/nordic/npm2100_vbat/npm2100_vbat.c @@ -0,0 +1,563 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nordic_npm2100_vbat + +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(vbat_npm2100, CONFIG_SENSOR_LOG_LEVEL); + +#define BOOST_START 0x20U +#define BOOST_OPER 0x24U +#define BOOST_DPSCOUNT 0x25U +#define BOOST_DPSLIMIT 0x26U +#define BOOST_DPSDUR 0x27U +#define BOOST_CTRLSET 0x2AU +#define BOOST_CTRLCLR 0x2BU +#define BOOST_VBATSEL 0x2EU +#define BOOST_VBATMINL 0x2FU +#define BOOST_VBATMINH 0x30U +#define BOOST_VOUTMIN 0x31U +#define BOOST_VOUTWRN 0x32U +#define BOOST_VOUTDPS 0x33U + +#define ADC_TASKS_ADC 0x90U +#define ADC_CONFIG 0X91U +#define ADC_DELAY 0x92U +#define ADC_OFFSETCFG 0x93U +#define ADC_CTRLSET 0x94U +#define ADC_CTRLCLR 0x95U +#define ADC_RESULTS 0x96U +#define ADC_READVBAT 0x96U +#define ADC_READTEMP 0x97U +#define ADC_READDROOP 0x98U +#define ADC_READVOUT 0x99U +#define ADC_AVERAGE 0x9BU +#define ADC_OFFSETMEASURED 0x9FU + +#define ADC_CONFIG_MODE_MASK 0x07U +#define ADC_CONFIG_MODE_INS_VBAT 0x00U +#define ADC_CONFIG_MODE_DEL_VBAT 0x01U +#define ADC_CONFIG_MODE_TEMP 0x02U +#define ADC_CONFIG_MODE_DROOP 0x03U +#define ADC_CONFIG_MODE_VOUT 0x04U +#define ADC_CONFIG_MODE_OFFSET 0x05U +#define ADC_CONFIG_AVG_MASK 0x38U + +#define ADC_SAMPLE_TIME_US 100 + +#define VBAT_SCALING_OFFSET 0 +#define VBAT_SCALING_MUL 3200000 +#define VBAT_SCALING_DIV 256 +#define VOUT_SCALING_OFFSET 1800000 +#define VOUT_SCALING_MUL 1500000 +#define VOUT_SCALING_DIV 256 +#define TEMP_SCALING_OFFSET 389500000 +#define TEMP_SCALING_MUL 2120000 +#define TEMP_SCALING_DIV -1 +#define DPS_SCALING_OFFSET 0 +#define DPS_SCALING_MUL 1000000 +#define DPS_SCALING_DIV 1 + +static const struct linear_range vbat_range = LINEAR_RANGE_INIT(650000, 50000, 0U, 50U); +static const struct linear_range vout_range = LINEAR_RANGE_INIT(1700000, 50000, 0U, 31U); +static const struct linear_range vdps_range = LINEAR_RANGE_INIT(1900000, 50000, 0U, 31U); +static const struct linear_range dpslim_range = LINEAR_RANGE_INIT(0, 1, 0U, 255U); +static const struct linear_range dpstimer_range = LINEAR_RANGE_INIT(0, 1, 0U, 3U); +static const struct linear_range oversample_range = LINEAR_RANGE_INIT(0, 1, 0U, 4U); +static const struct linear_range adcdelay_range = LINEAR_RANGE_INIT(5000, 4000, 0U, 255U); + +struct npm2100_vbat_config { + struct i2c_dt_spec i2c; + struct sensor_value voutmin; + struct sensor_value vbatmin; +}; + +struct adc_config { + const enum sensor_channel chan; + const uint8_t result_reg; + uint8_t config; + uint8_t result; + bool enabled; +}; + +struct npm2100_vbat_data { + struct adc_config adc[4U]; + uint8_t vbat_delay; + uint8_t dpsdur; +}; + +struct npm2100_attr_t { + enum sensor_channel chan; + enum sensor_attribute attr; + const struct linear_range *range; + uint8_t reg; + uint8_t reg_mask; + uint8_t ctrlsel_mask; +}; + +static const struct npm2100_attr_t npm2100_attr[] = { + {SENSOR_CHAN_GAUGE_VOLTAGE, SENSOR_ATTR_UPPER_THRESH, &vbat_range, BOOST_VBATMINH, 0xFF, 0}, + {SENSOR_CHAN_GAUGE_VOLTAGE, SENSOR_ATTR_LOWER_THRESH, &vbat_range, BOOST_VBATMINL, 0xFF, 0}, + {SENSOR_CHAN_VOLTAGE, SENSOR_ATTR_UPPER_THRESH, &vdps_range, BOOST_VOUTDPS, 0xFF, BIT(2)}, + {SENSOR_CHAN_VOLTAGE, SENSOR_ATTR_LOWER_THRESH, &vout_range, BOOST_VOUTMIN, 0xFF, BIT(0)}, + {SENSOR_CHAN_VOLTAGE, SENSOR_ATTR_ALERT, &vout_range, BOOST_VOUTWRN, 0xFF, BIT(1)}, + {(enum sensor_channel)SENSOR_CHAN_NPM2100_DPS_COUNT, SENSOR_ATTR_UPPER_THRESH, + &dpslim_range, BOOST_DPSLIMIT, 0xFF, 0}, + {(enum sensor_channel)SENSOR_CHAN_NPM2100_DPS_TIMER, SENSOR_ATTR_UPPER_THRESH, + &dpstimer_range, BOOST_OPER, 0x60, 0}, +}; + +static struct adc_config *adc_cfg_get(const struct device *dev, enum sensor_channel chan) +{ + struct npm2100_vbat_data *const data = dev->data; + + for (int idx = 0; idx < ARRAY_SIZE(data->adc); idx++) { + if (data->adc[idx].chan == chan) { + return &data->adc[idx]; + } + } + + return NULL; +} + +int npm2100_vbat_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *valp) +{ + uint8_t *result = NULL; + + if ((uint32_t)chan == SENSOR_CHAN_NPM2100_DPS_DURATION) { + struct npm2100_vbat_data *const data = dev->data; + + result = &data->dpsdur; + } else { + struct adc_config *adc_cfg = adc_cfg_get(dev, chan); + + if (!adc_cfg) { + return -ENOTSUP; + } + + result = &adc_cfg->result; + } + + if (!result) { + return -ENOTSUP; + } + + int32_t scaling_mul; + int32_t scaling_div; + int32_t scaling_off; + + switch ((uint32_t)chan) { + case SENSOR_CHAN_GAUGE_VOLTAGE: + scaling_mul = VBAT_SCALING_MUL; + scaling_div = VBAT_SCALING_DIV; + scaling_off = VBAT_SCALING_OFFSET; + break; + case SENSOR_CHAN_VOLTAGE: + /* Fall through */ + case SENSOR_CHAN_NPM2100_VOLT_DROOP: + scaling_mul = VOUT_SCALING_MUL; + scaling_div = VOUT_SCALING_DIV; + scaling_off = VOUT_SCALING_OFFSET; + break; + case SENSOR_CHAN_DIE_TEMP: + scaling_mul = TEMP_SCALING_MUL; + scaling_div = TEMP_SCALING_DIV; + scaling_off = TEMP_SCALING_OFFSET; + break; + case SENSOR_CHAN_NPM2100_DPS_DURATION: + scaling_mul = DPS_SCALING_MUL; + scaling_div = DPS_SCALING_DIV; + scaling_off = DPS_SCALING_OFFSET; + break; + default: + return -ENOTSUP; + } + + int32_t tmp = scaling_off + ((int32_t)*result * scaling_mul) / scaling_div; + + valp->val1 = tmp / 1000000; + valp->val2 = tmp % 1000000; + + return 0; +} + +int npm2100_vbat_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + const struct npm2100_vbat_config *const config = dev->config; + struct npm2100_vbat_data *data = dev->data; + int ret; + + for (int idx = 0; idx < ARRAY_SIZE(data->adc); idx++) { + if (!data->adc[idx].enabled) { + continue; + } + + /* Oversampling by 2^field */ + const uint8_t oversampling = + BIT(FIELD_GET(ADC_CONFIG_AVG_MASK, data->adc[idx].config)); + int32_t delay_usec; + + if (chan == SENSOR_CHAN_GAUGE_VOLTAGE) { + ret = linear_range_get_value(&adcdelay_range, data->vbat_delay, + &delay_usec); + if (ret < 0) { + return ret; + } + } else { + delay_usec = 0; + } + + ret = i2c_reg_write_byte_dt(&config->i2c, ADC_CONFIG, data->adc[idx].config); + if (ret < 0) { + return ret; + } + + ret = i2c_reg_write_byte_dt(&config->i2c, ADC_TASKS_ADC, 1U); + if (ret < 0) { + return ret; + } + + k_sleep(K_USEC(ADC_SAMPLE_TIME_US * oversampling + delay_usec)); + + if (oversampling > 1) { + ret = i2c_reg_read_byte_dt(&config->i2c, ADC_AVERAGE, + &data->adc[idx].result); + if (ret < 0) { + return ret; + } + } else { + ret = i2c_reg_read_byte_dt(&config->i2c, data->adc[idx].result_reg, + &data->adc[idx].result); + if (ret < 0) { + return ret; + } + } + } + + /* Fetch previous DPS duration result before triggering new one.The time it takes to get + * the DPS duration depends on many factors and cannot be predicted here. + */ + ret = i2c_reg_read_byte_dt(&config->i2c, BOOST_DPSDUR, &data->dpsdur); + if (ret < 0) { + return ret; + } + + return i2c_reg_write_byte_dt(&config->i2c, BOOST_START, 2U); +} + +static int npm2100_vbat_attr_get(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, struct sensor_value *val) +{ + const struct npm2100_vbat_config *const config = dev->config; + + if (attr == SENSOR_ATTR_FEATURE_MASK) { + struct adc_config *adc_cfg = adc_cfg_get(dev, chan); + + if (!adc_cfg) { + return -EINVAL; + } + + *val = (struct sensor_value){.val1 = adc_cfg->enabled, .val2 = 0}; + + return 0; + } + + if (attr == SENSOR_ATTR_OVERSAMPLING) { + struct adc_config *adc_cfg = adc_cfg_get(dev, chan); + + if (!adc_cfg) { + return -EINVAL; + } + + *val = (struct sensor_value){ + .val1 = FIELD_GET(ADC_CONFIG_AVG_MASK, adc_cfg->config), .val2 = 0}; + + return 0; + } + + /* Delay of vbat ADC measurement */ + if ((chan == SENSOR_CHAN_GAUGE_VOLTAGE) && + (attr == (enum sensor_attribute)SENSOR_ATTR_NPM2100_ADC_DELAY)) { + struct npm2100_vbat_data *const data = dev->data; + struct adc_config *adc_cfg = adc_cfg_get(dev, chan); + int32_t val_usec; + + if (!adc_cfg) { + return -ENOENT; + } + + if (FIELD_GET(ADC_CONFIG_MODE_MASK, adc_cfg->config) == ADC_CONFIG_MODE_INS_VBAT) { + /* Instant measurement */ + return sensor_value_from_micro(val, 0); + } + + int ret = linear_range_get_value(&adcdelay_range, data->vbat_delay, &val_usec); + + if (ret < 0) { + return ret; + } + + return sensor_value_from_micro(val, val_usec); + } + + for (int idx = 0; idx < ARRAY_SIZE(npm2100_attr); idx++) { + if ((npm2100_attr[idx].chan == chan) && (npm2100_attr[idx].attr == attr)) { + + int32_t val_mv; + uint8_t reg_data; + + int ret = i2c_reg_read_byte_dt(&config->i2c, npm2100_attr[idx].reg, + ®_data); + + if (ret < 0) { + return ret; + } + + reg_data = FIELD_GET(npm2100_attr[idx].reg_mask, reg_data); + + ret = linear_range_get_value(npm2100_attr[idx].range, reg_data, &val_mv); + if (ret < 0) { + return ret; + } + + val->val1 = val_mv / 1000000; + val->val2 = val_mv % 1000000; + + return 0; + } + } + + return -ENOTSUP; +} + +static int npm2100_vbat_attr_set(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, const struct sensor_value *val) +{ + const struct npm2100_vbat_config *const config = dev->config; + int ret; + + /* ADC sampling feature masks to enable individual measurements */ + if (attr == SENSOR_ATTR_FEATURE_MASK) { + struct adc_config *adc_cfg = adc_cfg_get(dev, chan); + + if (!adc_cfg) { + return -EINVAL; + } + + adc_cfg->enabled = val->val1 == 0 ? false : true; + + return 0; + } + + /* Delay of vbat ADC measurement */ + if ((chan == SENSOR_CHAN_GAUGE_VOLTAGE) && + (attr == (enum sensor_attribute)SENSOR_ATTR_NPM2100_ADC_DELAY)) { + struct npm2100_vbat_data *const data = dev->data; + struct adc_config *adc_cfg = adc_cfg_get(dev, chan); + int32_t val_usec = sensor_value_to_micro(val); + uint16_t delay; + + if (!adc_cfg) { + return -ENOENT; + } + + if (val_usec == 0) { + delay = 0; + } else { + ret = linear_range_get_index(&adcdelay_range, val_usec, &delay); + if (ret < 0) { + return ret; + } + + ret = i2c_reg_write_byte_dt(&config->i2c, ADC_DELAY, delay); + if (ret < 0) { + return ret; + } + } + + /* Delayed vbat measurement uses different mode */ + data->vbat_delay = delay; + adc_cfg->config &= ~ADC_CONFIG_MODE_MASK; + if (delay == 0) { + adc_cfg->config |= + FIELD_PREP(ADC_CONFIG_MODE_MASK, ADC_CONFIG_MODE_INS_VBAT); + } else { + adc_cfg->config |= + FIELD_PREP(ADC_CONFIG_MODE_MASK, ADC_CONFIG_MODE_DEL_VBAT); + } + + return 0; + } + + /* ADC per-channel oversampling */ + if (attr == SENSOR_ATTR_OVERSAMPLING) { + struct adc_config *adc_cfg = adc_cfg_get(dev, chan); + uint16_t oversample; + + if (!adc_cfg) { + return -ENOENT; + } + + /* Oversample factor is 2^value */ + ret = linear_range_get_index(&oversample_range, val->val1, &oversample); + if (ret < 0) { + return ret; + } + + adc_cfg->config &= ~ADC_CONFIG_AVG_MASK; + adc_cfg->config |= FIELD_PREP(ADC_CONFIG_AVG_MASK, oversample); + + return 0; + } + + /* Threshold attributes */ + for (int idx = 0; idx < ARRAY_SIZE(npm2100_attr); idx++) { + if ((npm2100_attr[idx].chan == chan) && (npm2100_attr[idx].attr == attr)) { + + uint16_t range_idx; + uint8_t reg_data; + + ret = linear_range_get_index(npm2100_attr[idx].range, + sensor_value_to_micro(val), &range_idx); + + if (ret < 0) { + return ret; + } + + reg_data = FIELD_PREP(npm2100_attr[idx].reg_mask, range_idx); + + if (npm2100_attr[idx].ctrlsel_mask != 0) { + /* Disable comparator */ + ret = i2c_reg_write_byte_dt(&config->i2c, BOOST_CTRLCLR, + npm2100_attr[idx].ctrlsel_mask); + if (ret < 0) { + return ret; + } + } + + /* Set threshold */ + if (npm2100_attr[idx].reg_mask == 0xFFU) { + ret = i2c_reg_write_byte_dt(&config->i2c, npm2100_attr[idx].reg, + reg_data); + } else { + ret = i2c_reg_update_byte_dt(&config->i2c, npm2100_attr[idx].reg, + npm2100_attr[idx].reg_mask, reg_data); + } + if (ret < 0) { + return ret; + } + + if (npm2100_attr[idx].ctrlsel_mask != 0) { + /* Enable comparator */ + ret = i2c_reg_write_byte_dt(&config->i2c, BOOST_CTRLSET, + npm2100_attr[idx].ctrlsel_mask); + } + + return ret; + } + } + + return -ENOTSUP; +} + +int npm2100_vbat_init(const struct device *dev) +{ + const struct npm2100_vbat_config *const config = dev->config; + int ret; + + if (!i2c_is_ready_dt(&config->i2c)) { + LOG_ERR("%s i2c not ready", dev->name); + return -ENODEV; + } + + /* Set initial voltage thresholds */ + if ((config->voutmin.val1 != 0) || (config->voutmin.val2 != 0)) { + ret = npm2100_vbat_attr_set(dev, SENSOR_CHAN_VOLTAGE, SENSOR_ATTR_LOWER_THRESH, + &config->voutmin); + if (ret < 0) { + return ret; + } + } + + if ((config->vbatmin.val1 != 0) || (config->vbatmin.val2 != 0)) { + ret = npm2100_vbat_attr_set(dev, SENSOR_CHAN_GAUGE_VOLTAGE, + SENSOR_ATTR_UPPER_THRESH, &config->vbatmin); + if (ret < 0) { + return ret; + } + + ret = npm2100_vbat_attr_set(dev, SENSOR_CHAN_GAUGE_VOLTAGE, + SENSOR_ATTR_LOWER_THRESH, &config->vbatmin); + if (ret < 0) { + return ret; + } + } + + /* Set MEE thresholds to SW control */ + ret = i2c_reg_write_byte_dt(&config->i2c, BOOST_VBATSEL, 3U); + if (ret < 0) { + return ret; + } + + /* Allow VOUTMIN comparator to select VBATMIN threshold */ + return i2c_reg_write_byte_dt(&config->i2c, BOOST_CTRLSET, 0x10U); +} + +static DEVICE_API(sensor, npm2100_vbat_battery_driver_api) = { + .sample_fetch = npm2100_vbat_sample_fetch, + .channel_get = npm2100_vbat_channel_get, + .attr_set = npm2100_vbat_attr_set, + .attr_get = npm2100_vbat_attr_get, +}; + +/* DPS-related measurements off by default. Enable via attribute feature masks. */ +#define NPM2100_VBAT_DATA_INIT() \ + { \ + .adc = \ + { \ + {.chan = SENSOR_CHAN_GAUGE_VOLTAGE, \ + .config = ADC_CONFIG_MODE_INS_VBAT, \ + .result_reg = ADC_READVBAT, \ + .enabled = true}, \ + {.chan = SENSOR_CHAN_VOLTAGE, \ + .config = ADC_CONFIG_MODE_VOUT, \ + .result_reg = ADC_READVOUT, \ + .enabled = true}, \ + {.chan = SENSOR_CHAN_DIE_TEMP, \ + .config = ADC_CONFIG_MODE_TEMP, \ + .result_reg = ADC_READTEMP, \ + .enabled = true}, \ + {.chan = (enum sensor_channel)SENSOR_CHAN_NPM2100_VOLT_DROOP, \ + .config = ADC_CONFIG_MODE_DROOP, \ + .result_reg = ADC_READDROOP, \ + .enabled = false}, \ + }, \ + .vbat_delay = 0, .dpsdur = 0, \ + } + +#define NPM2100_VBAT_INIT(n) \ + static struct npm2100_vbat_data npm2100_vbat_data_##n = NPM2100_VBAT_DATA_INIT(); \ + \ + static const struct npm2100_vbat_config npm2100_vbat_config_##n = { \ + .i2c = I2C_DT_SPEC_GET(DT_INST_PARENT(n)), \ + .voutmin = {.val1 = DT_INST_PROP_OR(n, vout_min_microvolt, 0) / 1000000, \ + .val2 = DT_INST_PROP_OR(n, vout_min_microvolt, 0) % 1000000}, \ + .vbatmin = {.val1 = DT_INST_PROP_OR(n, vbat_min_microvolt, 0) / 1000000, \ + .val2 = DT_INST_PROP_OR(n, vbat_min_microvolt, 0) % 1000000}, \ + }; \ + \ + SENSOR_DEVICE_DT_INST_DEFINE( \ + n, &npm2100_vbat_init, NULL, &npm2100_vbat_data_##n, &npm2100_vbat_config_##n, \ + POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &npm2100_vbat_battery_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(NPM2100_VBAT_INIT) diff --git a/dts/bindings/sensor/nordic,npm2100-vbat.yaml b/dts/bindings/sensor/nordic,npm2100-vbat.yaml new file mode 100644 index 00000000000..a249c2de77d --- /dev/null +++ b/dts/bindings/sensor/nordic,npm2100-vbat.yaml @@ -0,0 +1,25 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# + +description: NPM2100 PMIC Battery Voltage + +compatible: "nordic,npm2100-vbat" + +include: [sensor-device.yaml] + +properties: + vout-min-microvolt: + type: int + description: | + Minimum Vout level. + When Vout falls to this level, the PMIC will allow more current to flow + from the battery by dropping to the vbatminl battery threshold. + + vbat-min-microvolt: + type: int + description: | + Initial value for vbatminl and vbatminh. + The boost converter will not allow the battery to drop below this level. diff --git a/include/zephyr/drivers/sensor/npm2100_vbat.h b/include/zephyr/drivers/sensor/npm2100_vbat.h new file mode 100644 index 00000000000..501657756f6 --- /dev/null +++ b/include/zephyr/drivers/sensor/npm2100_vbat.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_NPM2100_VBAT_H_ +#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_NPM2100_VBAT_H_ + +#include + +/* NPM2100 vbat specific channels */ +enum sensor_channel_npm2100_vbat { + SENSOR_CHAN_NPM2100_VBAT_STATUS = SENSOR_CHAN_PRIV_START, + SENSOR_CHAN_NPM2100_VOLT_DROOP, + SENSOR_CHAN_NPM2100_DPS_COUNT, + SENSOR_CHAN_NPM2100_DPS_TIMER, + SENSOR_CHAN_NPM2100_DPS_DURATION, +}; + +/* NPM2100 vbat specific attributes */ +enum sensor_attr_npm2100_vbat { + SENSOR_ATTR_NPM2100_ADC_DELAY = SENSOR_ATTR_PRIV_START, +}; + +#endif diff --git a/tests/drivers/build_all/sensor/i2c.dtsi b/tests/drivers/build_all/sensor/i2c.dtsi index 3b34609bc4a..0aae04c9e1c 100644 --- a/tests/drivers/build_all/sensor/i2c.dtsi +++ b/tests/drivers/build_all/sensor/i2c.dtsi @@ -1139,3 +1139,12 @@ test_i2c_scd4x: scd4x@9e { reg = <0x9e>; mode = <0>; }; + +test_i2c_npm2100: npm2100@9f { + compatible = "nordic,npm2100"; + reg = <0x9f>; + + vbat { + compatible = "nordic,npm2100-vbat"; + }; +};