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