1 /*
2 * Copyright (c) 2022 STMicroelectronics
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/device.h>
8 #include <zephyr/devicetree.h>
9 #include <zephyr/drivers/sensor.h>
10 #include <zephyr/drivers/adc.h>
11 #include <zephyr/logging/log.h>
12 #include <zephyr/pm/device_runtime.h>
13 #include <stm32_ll_adc.h>
14
15 LOG_MODULE_REGISTER(stm32_vbat, CONFIG_SENSOR_LOG_LEVEL);
16
17 #if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_vbat)
18 #define DT_DRV_COMPAT st_stm32_vbat
19 #else
20 #error "No compatible devicetree node found"
21 #endif
22
23 struct stm32_vbat_data {
24 const struct device *adc;
25 const struct adc_channel_cfg adc_cfg;
26 ADC_TypeDef *adc_base;
27 struct adc_sequence adc_seq;
28 struct k_mutex mutex;
29 int16_t sample_buffer;
30 int16_t raw; /* raw adc Sensor value */
31 };
32
33 struct stm32_vbat_config {
34 int ratio;
35 };
36
stm32_vbat_sample_fetch(const struct device * dev,enum sensor_channel chan)37 static int stm32_vbat_sample_fetch(const struct device *dev, enum sensor_channel chan)
38 {
39 struct stm32_vbat_data *data = dev->data;
40 struct adc_sequence *sp = &data->adc_seq;
41 int rc;
42 uint32_t path;
43
44 if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_VOLTAGE) {
45 return -ENOTSUP;
46 }
47
48 k_mutex_lock(&data->mutex, K_FOREVER);
49 pm_device_runtime_get(data->adc);
50
51 rc = adc_channel_setup(data->adc, &data->adc_cfg);
52
53 if (rc) {
54 LOG_DBG("Setup AIN%u got %d", data->adc_cfg.channel_id, rc);
55 goto unlock;
56 }
57
58 path = LL_ADC_GetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(data->adc_base));
59 LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(data->adc_base),
60 LL_ADC_PATH_INTERNAL_VBAT | path);
61
62 rc = adc_read(data->adc, sp);
63 if (rc == 0) {
64 data->raw = data->sample_buffer;
65 }
66
67 path = LL_ADC_GetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(data->adc_base));
68 LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(data->adc_base),
69 path &= ~LL_ADC_PATH_INTERNAL_VBAT);
70
71 unlock:
72 pm_device_runtime_put(data->adc);
73 k_mutex_unlock(&data->mutex);
74
75 return rc;
76 }
77
stm32_vbat_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)78 static int stm32_vbat_channel_get(const struct device *dev, enum sensor_channel chan,
79 struct sensor_value *val)
80 {
81 struct stm32_vbat_data *data = dev->data;
82 const struct stm32_vbat_config *cfg = dev->config;
83 int32_t voltage;
84
85 if (chan != SENSOR_CHAN_VOLTAGE) {
86 return -ENOTSUP;
87 }
88
89 /* Sensor value in millivolts considering the vbat input through a resistor bridge */
90 voltage = data->raw * adc_ref_internal(data->adc) * cfg->ratio / 0x0FFF;
91
92 return sensor_value_from_milli(val, voltage);
93 }
94
95 static DEVICE_API(sensor, stm32_vbat_driver_api) = {
96 .sample_fetch = stm32_vbat_sample_fetch,
97 .channel_get = stm32_vbat_channel_get,
98 };
99
stm32_vbat_init(const struct device * dev)100 static int stm32_vbat_init(const struct device *dev)
101 {
102 struct stm32_vbat_data *data = dev->data;
103 struct adc_sequence *asp = &data->adc_seq;
104
105 k_mutex_init(&data->mutex);
106
107 if (data->adc == NULL) {
108 LOG_ERR("ADC is not enabled");
109 return -ENODEV;
110 }
111
112 if (!device_is_ready(data->adc)) {
113 LOG_ERR("Device %s is not ready", data->adc->name);
114 return -ENODEV;
115 }
116
117 *asp = (struct adc_sequence){
118 .channels = BIT(data->adc_cfg.channel_id),
119 .buffer = &data->sample_buffer,
120 .buffer_size = sizeof(data->sample_buffer),
121 .resolution = 12U,
122 };
123
124 return 0;
125 }
126
127 #define ASSERT_VBAT_ADC_ENABLED(inst) \
128 BUILD_ASSERT(DT_NODE_HAS_STATUS_OKAY(DT_INST_IO_CHANNELS_CTLR(inst)), \
129 "ADC instance '" DT_NODE_FULL_NAME(DT_INST_IO_CHANNELS_CTLR(inst)) "' needed " \
130 "by Vbat sensor '" DT_NODE_FULL_NAME(DT_DRV_INST(inst)) "' is not enabled")
131
132 #define STM32_VBAT_DEFINE(inst) \
133 ASSERT_VBAT_ADC_ENABLED(inst); \
134 \
135 static struct stm32_vbat_data stm32_vbat_dev_data_##inst = { \
136 .adc = DEVICE_DT_GET(DT_INST_IO_CHANNELS_CTLR(inst)), \
137 .adc_base = (ADC_TypeDef *)DT_REG_ADDR(DT_INST_IO_CHANNELS_CTLR(inst)), \
138 .adc_cfg = { \
139 .gain = ADC_GAIN_1, \
140 .reference = ADC_REF_INTERNAL, \
141 .acquisition_time = ADC_ACQ_TIME_MAX, \
142 .channel_id = DT_INST_IO_CHANNELS_INPUT(inst), \
143 .differential = 0, \
144 }, \
145 }; \
146 \
147 static const struct stm32_vbat_config stm32_vbat_dev_config_##inst = { \
148 .ratio = DT_INST_PROP(inst, ratio), \
149 }; \
150 \
151 SENSOR_DEVICE_DT_INST_DEFINE(inst, stm32_vbat_init, NULL, \
152 &stm32_vbat_dev_data_##inst, &stm32_vbat_dev_config_##inst, \
153 POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
154 &stm32_vbat_driver_api);
155
156 DT_INST_FOREACH_STATUS_OKAY(STM32_VBAT_DEFINE)
157