diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index e2d46e88fd6..7ec05baff81 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -145,6 +145,7 @@ add_subdirectory_ifdef(CONFIG_VCNL4040 vcnl4040) add_subdirectory_ifdef(CONFIG_VEML7700 veml7700) add_subdirectory_ifdef(CONFIG_VL53L0X vl53l0x) add_subdirectory_ifdef(CONFIG_VL53L1X vl53l1x) +add_subdirectory_ifdef(CONFIG_VOLTAGE_DIVIDER voltage_divider) add_subdirectory_ifdef(CONFIG_WSEN_HIDS wsen_hids) add_subdirectory_ifdef(CONFIG_WSEN_PADS wsen_pads) add_subdirectory_ifdef(CONFIG_WSEN_PDUS wsen_pdus) diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index cd4faef46e4..942b1bee1d0 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -200,6 +200,7 @@ source "drivers/sensor/vcnl4040/Kconfig" source "drivers/sensor/veml7700/Kconfig" source "drivers/sensor/vl53l0x/Kconfig" source "drivers/sensor/vl53l1x/Kconfig" +source "drivers/sensor/voltage_divider/Kconfig" source "drivers/sensor/wsen_hids/Kconfig" source "drivers/sensor/wsen_itds/Kconfig" source "drivers/sensor/wsen_pads/Kconfig" diff --git a/drivers/sensor/voltage_divider/CMakeLists.txt b/drivers/sensor/voltage_divider/CMakeLists.txt new file mode 100644 index 00000000000..ec2af44de5c --- /dev/null +++ b/drivers/sensor/voltage_divider/CMakeLists.txt @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources(voltage.c) diff --git a/drivers/sensor/voltage_divider/Kconfig b/drivers/sensor/voltage_divider/Kconfig new file mode 100644 index 00000000000..19027aa9361 --- /dev/null +++ b/drivers/sensor/voltage_divider/Kconfig @@ -0,0 +1,13 @@ +# Voltage sensor driver +# +# Copyright (c) 2023 FTP Technologies +# +# SPDX-License-Identifier: Apache-2.0 + +config VOLTAGE_DIVIDER + bool "Voltage sensor driver" + default y + depends on DT_HAS_VOLTAGE_DIVIDER_ENABLED + depends on ADC + help + Enable voltage sensor driver. diff --git a/drivers/sensor/voltage_divider/voltage.c b/drivers/sensor/voltage_divider/voltage.c new file mode 100644 index 00000000000..ef2b34c74b3 --- /dev/null +++ b/drivers/sensor/voltage_divider/voltage.c @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2023 FTP Technologies + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT voltage_divider + +#include +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(voltage, CONFIG_SENSOR_LOG_LEVEL); + +struct voltage_config { + struct voltage_divider_dt_spec voltage; +#ifdef CONFIG_PM_DEVICE + struct gpio_dt_spec gpio_power; +#endif +}; + +struct voltage_data { + struct adc_sequence sequence; + int16_t raw; +}; + +static int fetch(const struct device *dev, enum sensor_channel chan) +{ + const struct voltage_config *config = dev->config; + struct voltage_data *data = dev->data; + int ret; + + if ((chan != SENSOR_CHAN_VOLTAGE) && (chan != SENSOR_CHAN_ALL)) { + return -ENOTSUP; + } + + ret = adc_read(config->voltage.port.dev, &data->sequence); + if (ret != 0) { + LOG_ERR("adc_read: %d", ret); + } + + return ret; +} + +static int get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val) +{ + const struct voltage_config *config = dev->config; + struct voltage_data *data = dev->data; + int32_t raw_val = data->raw; + int32_t v_mv; + int ret; + + __ASSERT_NO_MSG(val != NULL); + + if (chan != SENSOR_CHAN_VOLTAGE) { + return -ENOTSUP; + } + + ret = adc_raw_to_millivolts_dt(&config->voltage.port, &raw_val); + if (ret != 0) { + LOG_ERR("raw_to_mv: %d", ret); + return ret; + } + + v_mv = raw_val; + + /* Note if full_ohms is not specified then unscaled voltage is returned */ + (void)voltage_divider_scale_dt(&config->voltage, &v_mv); + + LOG_DBG("%d of %d, %dmV, voltage:%dmV", data->raw, + (1 << data->sequence.resolution) - 1, raw_val, v_mv); + val->val1 = v_mv / 1000; + val->val2 = (v_mv * 1000) % 1000000; + + return ret; +} + +static const struct sensor_driver_api voltage_api = { + .sample_fetch = fetch, + .channel_get = get, +}; + +#ifdef CONFIG_PM_DEVICE +static int pm_action(const struct device *dev, enum pm_device_action action) +{ + const struct voltage_config *config = dev->config; + int ret; + + if (config->gpio_power.port == NULL) { + LOG_ERR("PM not supported"); + return -ENOTSUP; + } + + switch (action) { + case PM_DEVICE_ACTION_RESUME: + ret = gpio_pin_set_dt(&config->gpio_power, 1); + if (ret != 0) { + LOG_ERR("failed to set GPIO for PM resume"); + } + break; + case PM_DEVICE_ACTION_SUSPEND: + ret = gpio_pin_set_dt(&config->gpio_power, 0); + if (ret != 0) { + LOG_ERR("failed to set GPIO for PM suspend"); + } + break; + default: + return -ENOTSUP; + } + + return ret; +} +#endif + +static int voltage_init(const struct device *dev) +{ + const struct voltage_config *config = dev->config; + struct voltage_data *data = dev->data; + int ret; + + if (!adc_is_ready_dt(&config->voltage.port)) { + LOG_ERR("ADC is not ready"); + return -ENODEV; + } + +#ifdef CONFIG_PM_DEVICE + if (config->gpio_power.port != NULL) { + if (!gpio_is_ready_dt(&config->gpio_power)) { + LOG_ERR("Power GPIO is not ready"); + return -ENODEV; + } + + ret = gpio_pin_configure_dt(&config->gpio_power, GPIO_OUTPUT_ACTIVE); + if (ret != 0) { + LOG_ERR("failed to initialize GPIO for reset"); + } + } +#endif + + ret = adc_channel_setup_dt(&config->voltage.port); + if (ret != 0) { + LOG_ERR("setup: %d", ret); + return ret; + } + + ret = adc_sequence_init_dt(&config->voltage.port, &data->sequence); + if (ret != 0) { + LOG_ERR("sequence init: %d", ret); + return ret; + } + + data->sequence.buffer = &data->raw; + data->sequence.buffer_size = sizeof(data->raw); + + return 0; +} + +#ifdef CONFIG_PM_DEVICE +#define POWER_GPIOS(inst) .gpio_power = GPIO_DT_SPEC_INST_GET_OR(inst, power_gpios, {0}), +#else +#define POWER_GPIOS(inst) +#endif + +#define VOLTAGE_INIT(inst) \ + static struct voltage_data voltage_##inst##_data; \ + \ + static const struct voltage_config voltage_##inst##_config = { \ + .voltage = VOLTAGE_DIVIDER_DT_SPEC_GET(DT_DRV_INST(inst)), \ + POWER_GPIOS(inst) \ + }; \ + \ + PM_DEVICE_DT_INST_DEFINE(inst, pm_action); \ + \ + SENSOR_DEVICE_DT_INST_DEFINE(inst, &voltage_init, PM_DEVICE_DT_INST_GET(inst), \ + &voltage_##inst##_data, &voltage_##inst##_config, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &voltage_api); + +DT_INST_FOREACH_STATUS_OKAY(VOLTAGE_INIT)