1 /*
2  * Copyright (c) 2023 FTP Technologies
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT microchip_mcp970x
8 
9 #include <zephyr/drivers/adc.h>
10 #include <zephyr/drivers/sensor.h>
11 
12 #include <zephyr/logging/log.h>
13 LOG_MODULE_REGISTER(mcp970x, CONFIG_SENSOR_LOG_LEVEL);
14 
15 enum ic_family {
16 	FAMILY_MCP9700_9700A,
17 	FAMILY_MCP9701_9701A
18 };
19 
20 /* Milli degrees C per degree C */
21 #define MC_PER_C 1000
22 
23 #define MV_AT_0C_MCP9700_9700A 500
24 #define MV_AT_0C_MCP9701_9701A 400
25 
26 #define T_COEFF_MCP9700_9700A 10
27 #define T_COEFF_MCP9701_9701A 19.5
28 
29 struct mcp970x_config {
30 	struct adc_dt_spec adc;
31 	enum ic_family family;
32 };
33 
34 struct mcp970x_data {
35 	struct adc_sequence sequence;
36 	int16_t raw;
37 };
38 
fetch(const struct device * dev,enum sensor_channel chan)39 static int fetch(const struct device *dev, enum sensor_channel chan)
40 {
41 	const struct mcp970x_config *config = dev->config;
42 	struct mcp970x_data *data = dev->data;
43 	int ret;
44 
45 	if ((chan != SENSOR_CHAN_AMBIENT_TEMP) && (chan != SENSOR_CHAN_ALL)) {
46 		return -ENOTSUP;
47 	}
48 
49 	ret = adc_read_dt(&config->adc, &data->sequence);
50 	if (ret != 0) {
51 		LOG_ERR("adc_read: %d", ret);
52 	}
53 
54 	return ret;
55 }
56 
get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)57 static int get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val)
58 {
59 	const struct mcp970x_config *config = dev->config;
60 	struct mcp970x_data *data = dev->data;
61 	int32_t raw_val = data->raw;
62 	int32_t t;
63 	int ret;
64 
65 	__ASSERT_NO_MSG(val != NULL);
66 
67 	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
68 		return -ENOTSUP;
69 	}
70 
71 	ret = adc_raw_to_millivolts_dt(&config->adc, &raw_val);
72 	if (ret != 0) {
73 		LOG_ERR("to_mv: %d", ret);
74 		return ret;
75 	}
76 
77 	if (config->family == FAMILY_MCP9700_9700A) {
78 		t = (MC_PER_C * (raw_val - MV_AT_0C_MCP9700_9700A)) / T_COEFF_MCP9700_9700A;
79 	} else {
80 		int32_t t_coeff = 10 * T_COEFF_MCP9701_9701A; /* float to int */
81 
82 		t = (MC_PER_C * 10 * (raw_val - MV_AT_0C_MCP9701_9701A)) / t_coeff;
83 	}
84 
85 	val->val1 = t / MC_PER_C;
86 	val->val2 = 1000 * (t % MC_PER_C);
87 
88 	LOG_DBG("%d of %d, %dmV, %dmC", data->raw, (1 << data->sequence.resolution) - 1, raw_val,
89 		t);
90 
91 	return 0;
92 }
93 
94 static DEVICE_API(sensor, mcp970x_api) = {
95 	.sample_fetch = fetch,
96 	.channel_get = get,
97 };
98 
init(const struct device * dev)99 static int init(const struct device *dev)
100 {
101 	const struct mcp970x_config *config = dev->config;
102 	struct mcp970x_data *data = dev->data;
103 	int ret;
104 
105 	if (!adc_is_ready_dt(&config->adc)) {
106 		LOG_ERR("ADC is not ready");
107 		return -ENODEV;
108 	}
109 
110 	ret = adc_channel_setup_dt(&config->adc);
111 	if (ret != 0) {
112 		LOG_ERR("setup: %d", ret);
113 		return ret;
114 	}
115 
116 	ret = adc_sequence_init_dt(&config->adc, &data->sequence);
117 	if (ret != 0) {
118 		LOG_ERR("sequence: %d", ret);
119 		return ret;
120 	}
121 
122 	data->sequence.buffer = &data->raw;
123 	data->sequence.buffer_size = sizeof(data->raw);
124 
125 	return 0;
126 }
127 
128 #define MCP970X_INIT(inst)                                                                         \
129 	static struct mcp970x_data mcp970x_##inst##_data = {0};                                    \
130                                                                                                    \
131 	static const struct mcp970x_config mcp970x_##inst##_config = {                             \
132 		.adc = ADC_DT_SPEC_INST_GET(inst),                                                 \
133 		.family = DT_INST_ENUM_IDX(inst, family),                                          \
134 	};                                                                                         \
135                                                                                                    \
136 	SENSOR_DEVICE_DT_INST_DEFINE(inst, &init, NULL, &mcp970x_##inst##_data,                    \
137 				     &mcp970x_##inst##_config, POST_KERNEL,                        \
138 				     CONFIG_SENSOR_INIT_PRIORITY, &mcp970x_api);
139 
140 DT_INST_FOREACH_STATUS_OKAY(MCP970X_INIT)
141