1 /*
2 * Copyright (c) 2023 Kenneth J. Miller <ken@miller.ec>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT st_stm32_vref
8
9 #include <zephyr/device.h>
10 #include <zephyr/devicetree.h>
11 #include <zephyr/drivers/sensor.h>
12 #include <zephyr/drivers/adc.h>
13 #include <zephyr/logging/log.h>
14 #include <zephyr/pm/device_runtime.h>
15 #include <stm32_ll_adc.h>
16 #if defined(CONFIG_SOC_SERIES_STM32H5X)
17 #include <stm32_ll_icache.h>
18 #endif /* CONFIG_SOC_SERIES_STM32H5X */
19
20 LOG_MODULE_REGISTER(stm32_vref, CONFIG_SENSOR_LOG_LEVEL);
21
22 struct stm32_vref_data {
23 const struct device *adc;
24 const struct adc_channel_cfg adc_cfg;
25 ADC_TypeDef *adc_base;
26 struct adc_sequence adc_seq;
27 struct k_mutex mutex;
28 int16_t sample_buffer;
29 int16_t raw; /* raw adc Sensor value */
30 };
31
32 struct stm32_vref_config {
33 uint16_t *cal_addr;
34 int cal_mv;
35 };
36
stm32_vref_sample_fetch(const struct device * dev,enum sensor_channel chan)37 static int stm32_vref_sample_fetch(const struct device *dev, enum sensor_channel chan)
38 {
39 struct stm32_vref_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 if (rc) {
53 LOG_DBG("Setup AIN%u got %d", data->adc_cfg.channel_id, rc);
54 goto unlock;
55 }
56
57 path = LL_ADC_GetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(data->adc_base));
58 LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(data->adc_base),
59 LL_ADC_PATH_INTERNAL_VREFINT | path);
60
61 #ifdef LL_ADC_DELAY_VREFINT_STAB_US
62 k_usleep(LL_ADC_DELAY_VREFINT_STAB_US);
63 #endif
64
65 rc = adc_read(data->adc, sp);
66 if (rc == 0) {
67 data->raw = data->sample_buffer;
68 }
69
70 path = LL_ADC_GetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(data->adc_base));
71 LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(data->adc_base),
72 path &= ~LL_ADC_PATH_INTERNAL_VREFINT);
73
74
75 unlock:
76 pm_device_runtime_put(data->adc);
77 k_mutex_unlock(&data->mutex);
78
79 return rc;
80 }
81
stm32_vref_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)82 static int stm32_vref_channel_get(const struct device *dev, enum sensor_channel chan,
83 struct sensor_value *val)
84 {
85 struct stm32_vref_data *data = dev->data;
86 const struct stm32_vref_config *cfg = dev->config;
87 int32_t vref;
88
89 if (chan != SENSOR_CHAN_VOLTAGE) {
90 return -ENOTSUP;
91 }
92
93 if (data->raw == 0) {
94 LOG_ERR("Raw ADC value is zero");
95 return -ENODATA;
96 }
97
98 /*
99 * STM32H5X: accesses to flash RO region must be done with caching disabled.
100 */
101 #if defined(CONFIG_SOC_SERIES_STM32H5X)
102 LL_ICACHE_Disable();
103 #endif /* CONFIG_SOC_SERIES_STM32H5X */
104
105 /* Calculate VREF+ using VREFINT bandgap voltage and calibration data */
106 #if defined(CONFIG_SOC_SERIES_STM32U5X)
107 /*
108 * The VREF CALIBRATION value is acquired on 14 bits
109 * and the data acquired is on 12 bits
110 * since the adc_sequence.resolution is 12
111 */
112 vref = (cfg->cal_mv * (*cfg->cal_addr) >> 2) / data->raw;
113 #else
114 vref = cfg->cal_mv * (*cfg->cal_addr) / data->raw;
115 #endif /* CONFIG_SOC_SERIES_STM32H5X */
116
117 #if defined(CONFIG_SOC_SERIES_STM32H5X)
118 LL_ICACHE_Enable();
119 #endif /* CONFIG_SOC_SERIES_STM32H5X */
120
121 return sensor_value_from_milli(val, vref);
122 }
123
124 static const struct sensor_driver_api stm32_vref_driver_api = {
125 .sample_fetch = stm32_vref_sample_fetch,
126 .channel_get = stm32_vref_channel_get,
127 };
128
stm32_vref_init(const struct device * dev)129 static int stm32_vref_init(const struct device *dev)
130 {
131 struct stm32_vref_data *data = dev->data;
132 struct adc_sequence *asp = &data->adc_seq;
133
134 k_mutex_init(&data->mutex);
135
136 if (!device_is_ready(data->adc)) {
137 LOG_ERR("Device %s is not ready", data->adc->name);
138 return -ENODEV;
139 }
140
141 *asp = (struct adc_sequence){
142 .channels = BIT(data->adc_cfg.channel_id),
143 .buffer = &data->sample_buffer,
144 .buffer_size = sizeof(data->sample_buffer),
145 .resolution = 12U,
146 };
147
148 return 0;
149 }
150
151 /**
152 * Verify that the ADC instance which this driver uses to measure internal
153 * voltage reference is enabled. On STM32 MCUs with more than one ADC, it is
154 * possible to compile this driver even if the ADC used for measurement is
155 * disabled. In such cases, fail build with an explicit error message.
156 */
157 #if !DT_NODE_HAS_STATUS(DT_INST_IO_CHANNELS_CTLR(0), okay)
158
159 /* Use BUILD_ASSERT to get preprocessing on the message */
160 BUILD_ASSERT(0, "ADC '" DT_NODE_FULL_NAME(DT_INST_IO_CHANNELS_CTLR(0)) "' needed by "
161 "Vref sensor '" DT_NODE_FULL_NAME(DT_DRV_INST(0)) "' is not enabled");
162
163 /* To reduce noise in the compiler error log, do not attempt
164 * to instantiate device if the sensor's ADC is not enabled.
165 */
166 #else
167
168 static struct stm32_vref_data stm32_vref_dev_data = {
169 .adc = DEVICE_DT_GET(DT_INST_IO_CHANNELS_CTLR(0)),
170 .adc_base = (ADC_TypeDef *)DT_REG_ADDR(DT_INST_IO_CHANNELS_CTLR(0)),
171 .adc_cfg = {.gain = ADC_GAIN_1,
172 .reference = ADC_REF_INTERNAL,
173 .acquisition_time = ADC_ACQ_TIME_MAX,
174 .channel_id = DT_INST_IO_CHANNELS_INPUT(0),
175 .differential = 0},
176 };
177
178 static const struct stm32_vref_config stm32_vref_dev_config = {
179 .cal_addr = (uint16_t *)DT_INST_PROP(0, vrefint_cal_addr),
180 .cal_mv = DT_INST_PROP(0, vrefint_cal_mv),
181 };
182
183 SENSOR_DEVICE_DT_INST_DEFINE(0, stm32_vref_init, NULL, &stm32_vref_dev_data, &stm32_vref_dev_config,
184 POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &stm32_vref_driver_api);
185
186 #endif /* !DT_NODE_HAS_STATUS(DT_INST_IO_CHANNELS_CTLR(0), okay) */
187