1 /*
2 * Copyright (c) 2023 Google LLC
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/drivers/adc.h>
8 #include <zephyr/drivers/sensor.h>
9 #include <zephyr/pm/device.h>
10 #include <zephyr/pm/device_runtime.h>
11 #include <zephyr/logging/log.h>
12 #include "ntc_thermistor.h"
13
14 LOG_MODULE_REGISTER(NTC_THERMISTOR, CONFIG_SENSOR_LOG_LEVEL);
15
16 struct ntc_thermistor_data {
17 struct k_mutex mutex;
18 int32_t raw;
19 int32_t sample_val;
20 int32_t sample_val_max;
21 };
22
23 struct ntc_thermistor_config {
24 const struct adc_dt_spec adc_channel;
25 const struct ntc_config ntc_cfg;
26 };
27
ntc_thermistor_sample_fetch(const struct device * dev,enum sensor_channel chan)28 static int ntc_thermistor_sample_fetch(const struct device *dev, enum sensor_channel chan)
29 {
30 struct ntc_thermistor_data *data = dev->data;
31 const struct ntc_thermistor_config *cfg = dev->config;
32 enum pm_device_state pm_state;
33 int res;
34 struct adc_sequence sequence = {
35 .options = NULL,
36 .buffer = &data->raw,
37 .buffer_size = sizeof(data->raw),
38 .calibrate = false,
39 };
40
41 (void)pm_device_state_get(dev, &pm_state);
42 if (pm_state != PM_DEVICE_STATE_ACTIVE) {
43 return -EIO;
44 }
45
46 k_mutex_lock(&data->mutex, K_FOREVER);
47
48 adc_sequence_init_dt(&cfg->adc_channel, &sequence);
49 res = adc_read(cfg->adc_channel.dev, &sequence);
50 if (!res) {
51 if (cfg->ntc_cfg.pullup_mv) {
52 int32_t val_mv = data->raw;
53
54 res = adc_raw_to_millivolts_dt(&cfg->adc_channel, &val_mv);
55 data->sample_val = val_mv;
56 data->sample_val_max = cfg->ntc_cfg.pullup_mv;
57 } else {
58 data->sample_val = data->raw;
59 data->sample_val_max = BIT(cfg->adc_channel.resolution) - 1;
60 }
61 }
62
63 k_mutex_unlock(&data->mutex);
64
65 return res;
66 }
67
ntc_thermistor_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)68 static int ntc_thermistor_channel_get(const struct device *dev, enum sensor_channel chan,
69 struct sensor_value *val)
70 {
71 struct ntc_thermistor_data *data = dev->data;
72 const struct ntc_thermistor_config *cfg = dev->config;
73 uint32_t ohm;
74 int32_t temp;
75
76 switch (chan) {
77 case SENSOR_CHAN_AMBIENT_TEMP:
78 ohm = ntc_get_ohm_of_thermistor(&cfg->ntc_cfg, data->sample_val,
79 data->sample_val_max);
80 temp = ntc_get_temp_mc(&cfg->ntc_cfg.type, ohm);
81 val->val1 = temp / 1000;
82 val->val2 = (temp % 1000) * 1000;
83 break;
84 default:
85 return -ENOTSUP;
86 }
87 return 0;
88 }
89
90 static DEVICE_API(sensor, ntc_thermistor_driver_api) = {
91 .sample_fetch = ntc_thermistor_sample_fetch,
92 .channel_get = ntc_thermistor_channel_get,
93 };
94
ntc_thermistor_init(const struct device * dev)95 static int ntc_thermistor_init(const struct device *dev)
96 {
97 const struct ntc_thermistor_config *cfg = dev->config;
98 int err;
99
100 if (!adc_is_ready_dt(&cfg->adc_channel)) {
101 LOG_ERR("ADC controller device is not ready\n");
102 return -ENODEV;
103 }
104
105 err = adc_channel_setup_dt(&cfg->adc_channel);
106 if (err < 0) {
107 LOG_ERR("Could not setup channel err(%d)\n", err);
108 return err;
109 }
110
111 #ifdef CONFIG_PM_DEVICE_RUNTIME
112 pm_device_init_suspended(dev);
113
114 err = pm_device_runtime_enable(dev);
115 if (err) {
116 LOG_ERR("Failed to enable runtime power management");
117 return err;
118 }
119 #endif
120
121 return 0;
122 }
123
124 #ifdef CONFIG_PM_DEVICE
ntc_thermistor_pm_action(const struct device * dev,enum pm_device_action action)125 static int ntc_thermistor_pm_action(const struct device *dev, enum pm_device_action action)
126 {
127 switch (action) {
128 case PM_DEVICE_ACTION_TURN_ON:
129 case PM_DEVICE_ACTION_RESUME:
130 case PM_DEVICE_ACTION_TURN_OFF:
131 case PM_DEVICE_ACTION_SUSPEND:
132 return 0;
133 default:
134 return -ENOTSUP;
135 }
136 }
137 #endif
138
139 #define NTC_THERMISTOR_DEFINE0(inst, id, _comp, _n_comp) \
140 static struct ntc_thermistor_data ntc_thermistor_driver_##id##inst; \
141 \
142 static const struct ntc_thermistor_config ntc_thermistor_cfg_##id##inst = { \
143 .adc_channel = ADC_DT_SPEC_INST_GET(inst), \
144 .ntc_cfg = \
145 { \
146 .pullup_mv = DT_INST_PROP_OR(inst, pullup_uv, 0) / 1000, \
147 .pullup_ohm = DT_INST_PROP(inst, pullup_ohm), \
148 .pulldown_ohm = DT_INST_PROP(inst, pulldown_ohm), \
149 .connected_positive = DT_INST_PROP(inst, connected_positive), \
150 .type = { \
151 .comp = _comp, \
152 .n_comp = _n_comp, \
153 }, \
154 }, \
155 }; \
156 \
157 PM_DEVICE_DT_INST_DEFINE(inst, ntc_thermistor_pm_action); \
158 \
159 SENSOR_DEVICE_DT_INST_DEFINE( \
160 inst, ntc_thermistor_init, PM_DEVICE_DT_INST_GET(inst), \
161 &ntc_thermistor_driver_##id##inst, &ntc_thermistor_cfg_##id##inst, POST_KERNEL, \
162 CONFIG_SENSOR_INIT_PRIORITY, &ntc_thermistor_driver_api);
163
164 #define NTC_THERMISTOR_DEFINE(inst, id, comp) \
165 NTC_THERMISTOR_DEFINE0(inst, id, comp, ARRAY_SIZE(comp))
166
167 /* ntc-thermistor-generic */
168 #define DT_DRV_COMPAT ntc_thermistor_generic
169
170 #define NTC_THERMISTOR_GENERIC_DEFINE(inst) \
171 static const uint32_t comp_##inst[] = DT_INST_PROP(inst, zephyr_compensation_table); \
172 NTC_THERMISTOR_DEFINE0(inst, DT_DRV_COMPAT, (struct ntc_compensation *)comp_##inst, \
173 ARRAY_SIZE(comp_##inst) / 2)
174
175 DT_INST_FOREACH_STATUS_OKAY(NTC_THERMISTOR_GENERIC_DEFINE)
176
177 /* epcos,b57861s0103a039 */
178 #undef DT_DRV_COMPAT
179 #define DT_DRV_COMPAT epcos_b57861s0103a039
180
181 static __unused const struct ntc_compensation comp_epcos_b57861s0103a039[] = {
182 { -25, 146676 },
183 { -15, 78875 },
184 { -5, 44424 },
185 { 5, 26075 },
186 { 15, 15881 },
187 { 25, 10000 },
188 { 35, 6488 },
189 { 45, 4326 },
190 { 55, 2956 },
191 { 65, 2066 },
192 { 75, 1474 },
193 { 85, 1072 },
194 { 95, 793 },
195 { 105, 596 },
196 { 115, 454 },
197 { 125, 351 },
198 };
199
200 DT_INST_FOREACH_STATUS_OKAY_VARGS(NTC_THERMISTOR_DEFINE, DT_DRV_COMPAT,
201 comp_epcos_b57861s0103a039)
202
203 /* murata,ncp15wb473 */
204 #undef DT_DRV_COMPAT
205 #define DT_DRV_COMPAT murata_ncp15wb473
206
207 static __unused const struct ntc_compensation comp_murata_ncp15wb473[] = {
208 { -25, 655802 },
209 { -15, 360850 },
210 { -5, 206463 },
211 { 5, 122259 },
212 { 15, 74730 },
213 { 25, 47000 },
214 { 35, 30334 },
215 { 45, 20048 },
216 { 55, 13539 },
217 { 65, 9328 },
218 { 75, 6544 },
219 { 85, 4674 },
220 { 95, 3388 },
221 { 105, 2494 },
222 { 115, 1860 },
223 { 125, 1406 },
224 };
225
226 DT_INST_FOREACH_STATUS_OKAY_VARGS(NTC_THERMISTOR_DEFINE, DT_DRV_COMPAT,
227 comp_murata_ncp15wb473)
228
229 /* tdk,ntcg163jf103ft1 */
230 #undef DT_DRV_COMPAT
231 #define DT_DRV_COMPAT tdk_ntcg163jf103ft1
232
233 static __unused const struct ntc_compensation comp_tdk_ntcg163jf103ft1[] = {
234 { -25, 86560 },
235 { -15, 53460 },
236 { -5, 33930 },
237 { 5, 22070 },
238 { 15, 14700 },
239 { 25, 10000 },
240 { 35, 6942 },
241 { 45, 4911 },
242 { 55, 3536 },
243 { 65, 2588 },
244 { 75, 1924 },
245 { 85, 1451 },
246 { 95, 1110 },
247 { 105, 860 },
248 { 115, 674 },
249 { 125, 534 },
250 };
251
252 DT_INST_FOREACH_STATUS_OKAY_VARGS(NTC_THERMISTOR_DEFINE, DT_DRV_COMPAT,
253 comp_tdk_ntcg163jf103ft1)
254
255 /* murata,ncp15xh103 */
256 #undef DT_DRV_COMPAT
257 #define DT_DRV_COMPAT murata_ncp15xh103
258
259 static __unused const struct ntc_compensation comp_murata_ncp15xh103[] = {
260 { -25, 87558 },
261 { -15, 53649 },
262 { -5, 33892 },
263 { 5, 22021 },
264 { 15, 14673 },
265 { 25, 10000 },
266 { 35, 6947 },
267 { 45, 4916 },
268 { 55, 3535 },
269 { 64, 2586 },
270 { 75, 1924 },
271 { 85, 1452 },
272 { 95, 1109 },
273 { 105, 858 },
274 { 115, 671 },
275 { 125, 531 },
276 };
277
278 DT_INST_FOREACH_STATUS_OKAY_VARGS(NTC_THERMISTOR_DEFINE, DT_DRV_COMPAT,
279 comp_murata_ncp15xh103)
280