1 /*
2  * Copyright (c) 2020 Vestas Wind Systems A/S
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nxp_kinetis_temperature
8 
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/sensor.h>
11 #include <zephyr/drivers/adc.h>
12 #include <zephyr/logging/log.h>
13 
14 LOG_MODULE_REGISTER(temp_kinetis, CONFIG_SENSOR_LOG_LEVEL);
15 
16 /*
17  * Driver assumptions:
18  * - ADC samples are in uint16_t format
19  * - Both ADC channels (sensor and bandgap) are on the same ADC instance
20  *
21  * See NXP Application Note AN3031 for details on calculations.
22  */
23 
24 /* Two ADC samples required for each reading, sensor value and bandgap value */
25 #define TEMP_KINETIS_ADC_SAMPLES 2
26 
27 struct temp_kinetis_config {
28 	const struct device *adc;
29 	uint8_t sensor_adc_ch;
30 	uint8_t bandgap_adc_ch;
31 	int bandgap_mv;
32 	int vtemp25_mv;
33 	int slope_cold_uv;
34 	int slope_hot_uv;
35 	struct adc_sequence adc_seq;
36 };
37 
38 struct temp_kinetis_data {
39 	uint16_t buffer[TEMP_KINETIS_ADC_SAMPLES];
40 };
41 
temp_kinetis_sample_fetch(const struct device * dev,enum sensor_channel chan)42 static int temp_kinetis_sample_fetch(const struct device *dev,
43 				     enum sensor_channel chan)
44 {
45 	const struct temp_kinetis_config *config = dev->config;
46 	struct temp_kinetis_data *data = dev->data;
47 #ifdef CONFIG_TEMP_KINETIS_FILTER
48 	uint16_t previous[TEMP_KINETIS_ADC_SAMPLES];
49 	int i;
50 #endif /* CONFIG_TEMP_KINETIS_FILTER */
51 	int err;
52 
53 	/* Always read both sensor and bandgap voltage in one go */
54 	if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_DIE_TEMP &&
55 	    chan != SENSOR_CHAN_VOLTAGE) {
56 		return -ENOTSUP;
57 	}
58 
59 #ifdef CONFIG_TEMP_KINETIS_FILTER
60 	memcpy(previous, data->buffer, sizeof(previous));
61 #endif /* CONFIG_TEMP_KINETIS_FILTER */
62 
63 	err = adc_read(config->adc, &config->adc_seq);
64 	if (err) {
65 		LOG_ERR("failed to read ADC channels (err %d)", err);
66 		return err;
67 	}
68 
69 	LOG_DBG("sensor = %d, bandgap = %d", data->buffer[0], data->buffer[1]);
70 
71 #ifdef CONFIG_TEMP_KINETIS_FILTER
72 	if (previous[0] != 0 && previous[1] != 0) {
73 		for (i = 0; i < ARRAY_SIZE(previous); i++) {
74 			data->buffer[i] = (data->buffer[i] >> 1) +
75 				(previous[i] >> 1);
76 		}
77 
78 		LOG_DBG("sensor = %d, bandgap = %d (filtered)", data->buffer[0],
79 			data->buffer[1]);
80 	}
81 #endif /* CONFIG_TEMP_KINETIS_FILTER */
82 
83 	return 0;
84 }
85 
temp_kinetis_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)86 static int temp_kinetis_channel_get(const struct device *dev,
87 				    enum sensor_channel chan,
88 				    struct sensor_value *val)
89 {
90 	const struct temp_kinetis_config *config = dev->config;
91 	struct temp_kinetis_data *data = dev->data;
92 	uint16_t adcr_vdd = BIT_MASK(config->adc_seq.resolution);
93 	uint16_t adcr_temp25;
94 	int32_t temp_cc;
95 	int32_t vdd_mv;
96 	int slope_uv;
97 	uint16_t adcr_100m;
98 
99 	if (chan != SENSOR_CHAN_VOLTAGE && chan != SENSOR_CHAN_DIE_TEMP) {
100 		return -ENOTSUP;
101 	}
102 
103 	/* VDD (or VREF, but AN3031 calls it VDD) in millivolts */
104 	vdd_mv = (adcr_vdd * config->bandgap_mv) / data->buffer[1];
105 
106 	if (chan == SENSOR_CHAN_VOLTAGE) {
107 		val->val1 = vdd_mv / 1000;
108 		val->val2 = (vdd_mv % 1000) * 1000;
109 		return 0;
110 	}
111 
112 	/* ADC result for temperature = 25 degrees Celsius */
113 	adcr_temp25 = (adcr_vdd * config->vtemp25_mv) / vdd_mv;
114 
115 	/* Determine which slope to use */
116 	if (data->buffer[0] > adcr_temp25) {
117 		slope_uv = config->slope_cold_uv;
118 	} else {
119 		slope_uv = config->slope_hot_uv;
120 	}
121 
122 	adcr_100m = (adcr_vdd * slope_uv) / (vdd_mv * 10);
123 
124 	/* Temperature in centi degrees Celsius */
125 	temp_cc = 2500 -
126 		(((data->buffer[0] - adcr_temp25) * 10000) / adcr_100m);
127 
128 	val->val1 = temp_cc / 100;
129 	val->val2 = (temp_cc % 100) * 10000;
130 
131 	return 0;
132 }
133 
134 static const struct sensor_driver_api temp_kinetis_driver_api = {
135 	.sample_fetch = temp_kinetis_sample_fetch,
136 	.channel_get = temp_kinetis_channel_get,
137 };
138 
temp_kinetis_init(const struct device * dev)139 static int temp_kinetis_init(const struct device *dev)
140 {
141 	const struct temp_kinetis_config *config = dev->config;
142 	struct temp_kinetis_data *data = dev->data;
143 	int err;
144 	int i;
145 	const struct adc_channel_cfg ch_cfg[] = {
146 		{
147 			.gain = ADC_GAIN_1,
148 			.reference = ADC_REF_INTERNAL,
149 			.acquisition_time = ADC_ACQ_TIME_DEFAULT,
150 			.channel_id = config->sensor_adc_ch,
151 			.differential = 0,
152 		},
153 		{
154 			.gain = ADC_GAIN_1,
155 			.reference = ADC_REF_INTERNAL,
156 			.acquisition_time = ADC_ACQ_TIME_DEFAULT,
157 			.channel_id = config->bandgap_adc_ch,
158 			.differential = 0,
159 		},
160 	};
161 
162 	memset(&data->buffer, 0, ARRAY_SIZE(data->buffer));
163 
164 	if (!device_is_ready(config->adc)) {
165 		LOG_ERR("ADC device is not ready");
166 		return -EINVAL;
167 	}
168 
169 	for (i = 0; i < ARRAY_SIZE(ch_cfg); i++) {
170 		err = adc_channel_setup(config->adc, &ch_cfg[i]);
171 		if (err) {
172 			LOG_ERR("failed to configure ADC channel (err %d)",
173 				err);
174 			return err;
175 		}
176 	}
177 
178 	return 0;
179 }
180 
181 BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) <= 1,
182 	     "unsupported temp instance");
183 
184 #define TEMP_KINETIS_INIT(inst)						\
185 	BUILD_ASSERT(DT_INST_IO_CHANNELS_INPUT_BY_NAME(inst, sensor) <	\
186 		     DT_INST_IO_CHANNELS_INPUT_BY_NAME(inst, bandgap),	\
187 		     "This driver assumes sensor ADC channel to come before "\
188 		     "bandgap ADC channel");				\
189 									\
190 	static struct temp_kinetis_data temp_kinetis_data_0;		\
191 									\
192 	static const struct temp_kinetis_config temp_kinetis_config_0 = {\
193 		.adc = DEVICE_DT_GET(DT_INST_IO_CHANNELS_CTLR(inst)),\
194 		.sensor_adc_ch =					\
195 			DT_INST_IO_CHANNELS_INPUT_BY_NAME(inst, sensor),\
196 		.bandgap_adc_ch =					\
197 			DT_INST_IO_CHANNELS_INPUT_BY_NAME(inst, bandgap),\
198 		.bandgap_mv = DT_INST_PROP(0, bandgap_voltage) / 1000,	\
199 		.vtemp25_mv = DT_INST_PROP(0, vtemp25) / 1000,		\
200 		.slope_cold_uv = DT_INST_PROP(0, sensor_slope_cold),	\
201 		.slope_hot_uv = DT_INST_PROP(0, sensor_slope_hot),	\
202 		.adc_seq = {						\
203 			.options = NULL,				\
204 			.channels =					\
205 		BIT(DT_INST_IO_CHANNELS_INPUT_BY_NAME(inst, sensor)) |	\
206 		BIT(DT_INST_IO_CHANNELS_INPUT_BY_NAME(inst, bandgap)),	\
207 			.buffer = &temp_kinetis_data_0.buffer,		\
208 			.buffer_size = sizeof(temp_kinetis_data_0.buffer),\
209 			.resolution = CONFIG_TEMP_KINETIS_RESOLUTION,	\
210 			.oversampling = CONFIG_TEMP_KINETIS_OVERSAMPLING,\
211 			.calibrate = false,				\
212 		},							\
213 	};								\
214 									\
215 	SENSOR_DEVICE_DT_INST_DEFINE(inst, temp_kinetis_init,		\
216 			    NULL,					\
217 			    &temp_kinetis_data_0,			\
218 			    &temp_kinetis_config_0, POST_KERNEL,	\
219 			    CONFIG_SENSOR_INIT_PRIORITY,		\
220 			    &temp_kinetis_driver_api);
221 
222 DT_INST_FOREACH_STATUS_OKAY(TEMP_KINETIS_INIT)
223