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