1 /*
2  * Copyright (c) 2024 Renato Soma <renatoys08@gmail.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT lm35
8 
9 #include <zephyr/device.h>
10 #include <zephyr/devicetree.h>
11 #include <zephyr/drivers/adc.h>
12 #include <zephyr/drivers/sensor.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/logging/log.h>
15 
16 #define LM35_GAIN       ADC_GAIN_1
17 #define LM35_REF        ADC_REF_INTERNAL
18 
19 LOG_MODULE_REGISTER(LM35, CONFIG_SENSOR_LOG_LEVEL);
20 
21 struct lm35_data {
22 	uint16_t raw;
23 };
24 
25 struct lm35_config {
26 	const struct device *adc;
27 	uint8_t adc_channel;
28 	struct adc_sequence adc_seq;
29 	struct adc_channel_cfg ch_cfg;
30 };
31 
lm35_sample_fetch(const struct device * dev,enum sensor_channel chan)32 static int lm35_sample_fetch(const struct device *dev, enum sensor_channel chan)
33 {
34 	if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP) {
35 		return -ENOTSUP;
36 	}
37 
38 	const struct lm35_config *cfg = dev->config;
39 
40 	return adc_read(cfg->adc, &cfg->adc_seq);
41 }
42 
lm35_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)43 static int lm35_channel_get(const struct device *dev, enum sensor_channel chan,
44 			    struct sensor_value *val)
45 {
46 	if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP) {
47 		return -ENOTSUP;
48 	}
49 
50 	int err;
51 	struct lm35_data *data = dev->data;
52 	const struct lm35_config *cfg = dev->config;
53 	int32_t mv = data->raw;
54 
55 	err = adc_raw_to_millivolts(adc_ref_internal(cfg->adc), cfg->ch_cfg.gain,
56 				    cfg->adc_seq.resolution, &mv);
57 	if (err) {
58 		return err;
59 	}
60 
61 	/* LM35 computes linearly 10mV per 1 degree C */
62 	val->val1 = mv / 10;
63 	val->val2 = (mv % 10) * 100000;
64 
65 	return 0;
66 }
67 
68 static DEVICE_API(sensor, lm35_driver_api) = {
69 	.sample_fetch = lm35_sample_fetch,
70 	.channel_get = lm35_channel_get,
71 };
72 
lm35_init(const struct device * dev)73 static int lm35_init(const struct device *dev)
74 {
75 	const struct lm35_config *cfg = dev->config;
76 
77 	if (!device_is_ready(cfg->adc)) {
78 		LOG_ERR("ADC device is not ready.");
79 		return -EINVAL;
80 	}
81 
82 	adc_channel_setup(cfg->adc, &cfg->ch_cfg);
83 	return 0;
84 }
85 
86 #define LM35_INST(inst)                                                                            \
87 	static struct lm35_data lm35_data_##inst;                                                  \
88                                                                                                    \
89 	static const struct lm35_config lm35_cfg_##inst = {                                        \
90 		.adc = DEVICE_DT_GET(DT_INST_IO_CHANNELS_CTLR(inst)),                              \
91 		.adc_channel = DT_INST_IO_CHANNELS_INPUT(inst),                                    \
92 		.adc_seq =                                                                         \
93 			{                                                                          \
94 				.channels = BIT(DT_INST_IO_CHANNELS_INPUT(inst)),                  \
95 				.buffer = &lm35_data_##inst.raw,                                   \
96 				.buffer_size = sizeof(lm35_data_##inst.raw),                       \
97 				.resolution = DT_INST_PROP(inst, resolution),                      \
98 			},                                                                         \
99 		.ch_cfg = {                                                                        \
100 			.gain = LM35_GAIN,                                                         \
101 			.reference = LM35_REF,                                                     \
102 			.acquisition_time = ADC_ACQ_TIME_DEFAULT,                                  \
103 			.channel_id = DT_INST_IO_CHANNELS_INPUT(inst),                             \
104 		}};                                                                                \
105                                                                                                    \
106 	SENSOR_DEVICE_DT_INST_DEFINE(inst, lm35_init, NULL, &lm35_data_##inst, &lm35_cfg_##inst,   \
107 				     POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &lm35_driver_api);
108 
109 DT_INST_FOREACH_STATUS_OKAY(LM35_INST)
110