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