1 /*
2  * Copyright (c) 2023 TOKITA Hiroshi
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 
13 #include <hardware/adc.h>
14 
15 LOG_MODULE_REGISTER(rpi_pico_temp, CONFIG_SENSOR_LOG_LEVEL);
16 
17 #define DT_DRV_COMPAT raspberrypi_pico_temp
18 
19 struct rpi_pico_temp_config {
20 	const struct device *adc;
21 	const struct adc_channel_cfg ch_cfg;
22 	const int32_t vbe;
23 	const int32_t vbe_slope;
24 };
25 
26 struct rpi_pico_temp_data {
27 	struct adc_sequence adc_seq;
28 	int16_t sample;
29 };
30 
rpi_pico_temp_sample_fetch(const struct device * dev,enum sensor_channel chan)31 static int rpi_pico_temp_sample_fetch(const struct device *dev, enum sensor_channel chan)
32 {
33 	const struct rpi_pico_temp_config *cfg = dev->config;
34 	struct rpi_pico_temp_data *data = dev->data;
35 	int rc;
36 
37 	if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_DIE_TEMP) {
38 		return -ENOTSUP;
39 	}
40 
41 	rc = adc_channel_setup(cfg->adc, &cfg->ch_cfg);
42 	if (rc) {
43 		LOG_DBG("Setup ADC channel %u failed with %d", cfg->ch_cfg.channel_id, rc);
44 		return rc;
45 	}
46 
47 	rc = adc_read(cfg->adc, &data->adc_seq);
48 
49 	return rc;
50 }
51 
rpi_pico_temp_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)52 static int rpi_pico_temp_channel_get(const struct device *dev, enum sensor_channel chan,
53 				     struct sensor_value *val)
54 {
55 	const struct rpi_pico_temp_config *cfg = dev->config;
56 	struct rpi_pico_temp_data *data = dev->data;
57 	int32_t mv = 0;
58 	int32_t work;
59 	int rc;
60 
61 	if (chan != SENSOR_CHAN_DIE_TEMP) {
62 		return -ENOTSUP;
63 	}
64 
65 	mv = data->sample;
66 	rc = adc_raw_to_millivolts(adc_ref_internal(cfg->adc), cfg->ch_cfg.gain,
67 				   data->adc_seq.resolution, &mv);
68 	if (rc) {
69 		LOG_DBG("adc_raw_to_millivolts() failed %d", rc);
70 		return rc;
71 	}
72 
73 	/*
74 	 * Calculate CPU temperature from voltage by the equation:
75 	 * T = 27 - (ADC_Voltage - 0.706)/0.001721
76 	 */
77 	work = ((27 * (-cfg->vbe_slope)) - (mv * 1000 - cfg->vbe));
78 	val->val1 = work / (-cfg->vbe_slope);
79 	work -= val->val1 * (-cfg->vbe_slope);
80 	val->val2 = work * 1000000 / (-cfg->vbe_slope);
81 
82 	return 0;
83 }
84 
85 static DEVICE_API(sensor, rpi_pico_temp_driver_api) = {
86 	.sample_fetch = rpi_pico_temp_sample_fetch,
87 	.channel_get = rpi_pico_temp_channel_get,
88 };
89 
rpi_pico_temp_init(const struct device * dev)90 static int rpi_pico_temp_init(const struct device *dev)
91 {
92 	const struct rpi_pico_temp_config *cfg = dev->config;
93 
94 	if (!device_is_ready(cfg->adc)) {
95 		LOG_ERR("Device %s is not ready", cfg->adc->name);
96 		return -ENODEV;
97 	}
98 
99 	adc_set_temp_sensor_enabled(true);
100 
101 	return 0;
102 }
103 
104 #define RPI_PICO_TEMP_DEFINE(inst)                                                                 \
105 	static const struct rpi_pico_temp_config rpi_pico_temp_config_##inst = {                   \
106 		.adc = DEVICE_DT_GET(DT_INST_IO_CHANNELS_CTLR(inst)),                              \
107 		.vbe = DT_INST_PROP(inst, vbe),                                                    \
108 		.vbe_slope = DT_INST_PROP(inst, vbe_slope),                                        \
109 		.ch_cfg =                                                                          \
110 			{                                                                          \
111 				.gain = ADC_GAIN_1,                                                \
112 				.reference = ADC_REF_INTERNAL,                                     \
113 				.acquisition_time = ADC_ACQ_TIME_DEFAULT,                          \
114 				.channel_id = DT_INST_IO_CHANNELS_INPUT(inst),                     \
115 				.differential = 0,                                                 \
116 			},                                                                         \
117 	};                                                                                         \
118 	static struct rpi_pico_temp_data rpi_pico_temp_dev_data_##inst = {                         \
119 		.adc_seq =                                                                         \
120 			{                                                                          \
121 				.channels = BIT(DT_INST_IO_CHANNELS_INPUT(inst)),                  \
122 				.buffer = &rpi_pico_temp_dev_data_##inst.sample,                   \
123 				.buffer_size = sizeof(rpi_pico_temp_dev_data_##inst.sample),       \
124 				.resolution = 12U,                                                 \
125 			},                                                                         \
126 	};                                                                                         \
127                                                                                                    \
128 	SENSOR_DEVICE_DT_INST_DEFINE(inst, rpi_pico_temp_init, NULL,                               \
129 				     &rpi_pico_temp_dev_data_##inst, &rpi_pico_temp_config_##inst, \
130 				     POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,                     \
131 				     &rpi_pico_temp_driver_api);
132 
133 DT_INST_FOREACH_STATUS_OKAY(RPI_PICO_TEMP_DEFINE)
134