1 /*
2  * Copyright (c) 2023 Andriy Gelman <andriy.gelman@gmail.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT adi_adt7310
8 
9 #include <string.h>
10 #include <zephyr/devicetree.h>
11 #include <zephyr/drivers/spi.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/sys/byteorder.h>
14 #include <zephyr/sys/util.h>
15 
16 #include "adt7310.h"
17 
18 #include <zephyr/logging/log.h>
19 LOG_MODULE_REGISTER(ADT7310, CONFIG_SENSOR_LOG_LEVEL);
20 
21 #define ADT7310_READ_CMD  BIT(6)
22 #define ADT7310_WRITE_CMD 0
23 
24 #define ADT7310_REG_STATUS	0x00
25 #define ADT7310_REG_CONFIG	0x01
26 #define ADT7310_REG_TEMP	0x02
27 #define ADT7310_REG_ID		0x03
28 #define ADT7310_REG_HYST	0x05
29 #define ADT7310_REG_THRESH_HIGH 0x06
30 #define ADT7310_REG_THRESH_LOW	0x07
31 
32 #define ADT7310_ID			  0xc0
33 #define ADT7310_CONFIG_OP_MODE_MASK	  (0x3 << 5)
34 #define ADT7310_CONFIG_OP_MODE_CONTINUOUS (0x0 << 5)
35 #define ADT7310_CONFIG_OP_MODE_1SPS	  (0x2 << 5)
36 
37 #define ADT7310_HYSTERESIS_TEMP_MAX	   15
38 #define ADT7310_CONFIG_RESOLUTION_16BIT	   BIT(7)
39 #define ADT7310_CONFIG_INT_COMPARATOR_MODE BIT(4)
40 
41 /* Continuous conversion time = 240ms -> 1/0.240*1000000 */
42 #define ADT7310_MAX_SAMPLE_RATE 4166666
43 
44 /* The quantization step size at 16-bit resolution is 0.0078125. */
45 /* Ref ADT7310 Reference manual */
46 #define ADT7310_SAMPLE_TO_MICRO_DEG(x) ((x) * 15625 >> 1)
47 #define ADT7310_MICRO_DEG_TO_SAMPLE(x) ((x) / 15625 << 1)
48 
adt7310_temp_reg_read(const struct device * dev,uint8_t reg,int16_t * val)49 static int adt7310_temp_reg_read(const struct device *dev, uint8_t reg, int16_t *val)
50 {
51 	const struct adt7310_dev_config *cfg = dev->config;
52 	uint8_t cmd_buf[3] = { ADT7310_READ_CMD | (reg << 3) };
53 	int ret;
54 	const struct spi_buf tx_buf = { .buf = cmd_buf, .len = sizeof(cmd_buf) };
55 	const struct spi_buf rx_buf = { .buf = cmd_buf, .len = sizeof(cmd_buf) };
56 	const struct spi_buf_set tx = { .buffers = &tx_buf, .count = 1 };
57 	const struct spi_buf_set rx = { .buffers = &rx_buf, .count = 1 };
58 
59 	ret = spi_transceive_dt(&cfg->bus, &tx, &rx);
60 	if (ret < 0) {
61 		return ret;
62 	}
63 
64 	memcpy(val, cmd_buf + 1, 2);
65 	*val = sys_be16_to_cpu(*val);
66 
67 	return 0;
68 }
69 
adt7310_temp_reg_write(const struct device * dev,uint8_t reg,int16_t val)70 static int adt7310_temp_reg_write(const struct device *dev, uint8_t reg, int16_t val)
71 {
72 	const struct adt7310_dev_config *cfg = dev->config;
73 	uint8_t cmd_buf[3];
74 	const struct spi_buf tx_buf = { .buf = cmd_buf, .len = sizeof(cmd_buf) };
75 	const struct spi_buf rx_buf = { .buf = cmd_buf, .len = sizeof(cmd_buf) };
76 	const struct spi_buf_set tx = { .buffers = &tx_buf, .count = 1 };
77 	const struct spi_buf_set rx = { .buffers = &rx_buf, .count = 1 };
78 
79 	cmd_buf[0] = ADT7310_WRITE_CMD | (reg << 3);
80 	val = sys_cpu_to_be16(val);
81 	memcpy(&cmd_buf[1], &val, sizeof(val));
82 
83 	return spi_transceive_dt(&cfg->bus, &tx, &rx);
84 }
85 
adt7310_reg_read(const struct device * dev,uint8_t reg,uint8_t * val)86 static int adt7310_reg_read(const struct device *dev, uint8_t reg, uint8_t *val)
87 {
88 	const struct adt7310_dev_config *cfg = dev->config;
89 	uint8_t cmd_buf[2] = { ADT7310_READ_CMD | (reg << 3) };
90 	const struct spi_buf tx_buf = { .buf = cmd_buf, .len = sizeof(cmd_buf) };
91 	const struct spi_buf rx_buf = { .buf = cmd_buf, .len = sizeof(cmd_buf) };
92 	const struct spi_buf_set tx = { .buffers = &tx_buf, .count = 1 };
93 	const struct spi_buf_set rx = { .buffers = &rx_buf, .count = 1 };
94 	int ret;
95 
96 	ret = spi_transceive_dt(&cfg->bus, &tx, &rx);
97 	if (ret < 0) {
98 		return ret;
99 	}
100 
101 	*val = cmd_buf[1];
102 
103 	return 0;
104 }
105 
adt7310_reg_write(const struct device * dev,uint8_t reg,uint8_t val)106 static int adt7310_reg_write(const struct device *dev, uint8_t reg, uint8_t val)
107 {
108 	const struct adt7310_dev_config *cfg = dev->config;
109 	uint8_t cmd_buf[2] = { ADT7310_WRITE_CMD | (reg << 3), val };
110 	const struct spi_buf tx_buf = { .buf = cmd_buf, .len = sizeof(cmd_buf) };
111 	const struct spi_buf rx_buf = { .buf = cmd_buf, .len = sizeof(cmd_buf) };
112 	const struct spi_buf_set tx = { .buffers = &tx_buf, .count = 1 };
113 	const struct spi_buf_set rx = { .buffers = &rx_buf, .count = 1 };
114 
115 	return spi_transceive_dt(&cfg->bus, &tx, &rx);
116 }
117 
adt7310_sample_fetch(const struct device * dev,enum sensor_channel chan)118 static int adt7310_sample_fetch(const struct device *dev, enum sensor_channel chan)
119 {
120 	struct adt7310_data *drv_data = dev->data;
121 	int16_t value;
122 	int ret;
123 
124 	if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP) {
125 		return -ENOTSUP;
126 	}
127 
128 	ret = adt7310_temp_reg_read(dev, ADT7310_REG_TEMP, &value);
129 	if (ret < 0) {
130 		return ret;
131 	}
132 
133 	drv_data->sample = value;
134 
135 	return 0;
136 }
137 
adt7310_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)138 static int adt7310_channel_get(const struct device *dev, enum sensor_channel chan,
139 			       struct sensor_value *val)
140 {
141 	int32_t value;
142 	struct adt7310_data *drv_data = dev->data;
143 
144 	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
145 		return -ENOTSUP;
146 	}
147 
148 	value = ADT7310_SAMPLE_TO_MICRO_DEG((int32_t)drv_data->sample);
149 	val->val1 = value / 1000000;
150 	val->val2 = value % 1000000;
151 
152 	return 0;
153 }
154 
adt7310_update_reg(const struct device * dev,uint8_t reg,uint8_t value,uint8_t mask)155 static int adt7310_update_reg(const struct device *dev, uint8_t reg, uint8_t value, uint8_t mask)
156 {
157 	int ret;
158 	uint8_t reg_value;
159 
160 	ret = adt7310_reg_read(dev, reg, &reg_value);
161 	if (ret < 0) {
162 		return ret;
163 	}
164 
165 	reg_value &= ~mask;
166 	reg_value |= value;
167 
168 	return adt7310_reg_write(dev, reg, reg_value);
169 }
170 
adt7310_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)171 static int adt7310_attr_set(const struct device *dev, enum sensor_channel chan,
172 			    enum sensor_attribute attr, const struct sensor_value *val)
173 {
174 	int32_t rate, value;
175 	uint8_t reg = 0;
176 
177 	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
178 		return -ENOTSUP;
179 	}
180 
181 	if (val->val1 > INT32_MAX/1000000 - 1 || val->val1 < INT32_MIN/1000000 + 1) {
182 		return -EINVAL;
183 	}
184 
185 	switch (attr) {
186 	case SENSOR_ATTR_SAMPLING_FREQUENCY:
187 		rate = val->val1 * 1000000 + val->val2;
188 
189 		if (rate > ADT7310_MAX_SAMPLE_RATE || rate < 0) {
190 			return -EINVAL;
191 		}
192 
193 		if (rate > 1000000) {
194 			return adt7310_update_reg(dev, ADT7310_REG_CONFIG,
195 						  ADT7310_CONFIG_OP_MODE_CONTINUOUS,
196 						  ADT7310_CONFIG_OP_MODE_MASK);
197 		} else {
198 			return adt7310_update_reg(dev, ADT7310_REG_CONFIG,
199 						  ADT7310_CONFIG_OP_MODE_1SPS,
200 						  ADT7310_CONFIG_OP_MODE_MASK);
201 		}
202 	case SENSOR_ATTR_HYSTERESIS:
203 		if (val->val1 < 0 || val->val1 > ADT7310_HYSTERESIS_TEMP_MAX || val->val2 != 0) {
204 			return -EINVAL;
205 		}
206 		return adt7310_reg_write(dev, ADT7310_REG_HYST, val->val1);
207 	case SENSOR_ATTR_UPPER_THRESH:
208 		reg = ADT7310_REG_THRESH_HIGH;
209 		__fallthrough;
210 	case SENSOR_ATTR_LOWER_THRESH:
211 		if (!reg) {
212 			reg = ADT7310_REG_THRESH_LOW;
213 		}
214 
215 		value = val->val1 * 1000000 + val->val2;
216 		value = ADT7310_MICRO_DEG_TO_SAMPLE(value);
217 
218 		if (value < INT16_MIN || value > INT16_MAX) {
219 			return -EINVAL;
220 		}
221 		return adt7310_temp_reg_write(dev, reg, value);
222 	default:
223 		return -ENOTSUP;
224 	}
225 
226 	return 0;
227 }
228 
adt7310_probe(const struct device * dev)229 static int adt7310_probe(const struct device *dev)
230 {
231 	uint8_t value;
232 	int ret;
233 
234 	ret = adt7310_reg_read(dev, ADT7310_REG_ID, &value);
235 
236 	if (ret) {
237 		return ret;
238 	}
239 
240 	value &= 0xf8;
241 	if (value != ADT7310_ID) {
242 		LOG_ERR("Invalid device ID");
243 		return -ENODEV;
244 	}
245 
246 	return adt7310_reg_write(dev, ADT7310_REG_CONFIG,
247 				 ADT7310_CONFIG_RESOLUTION_16BIT |
248 				 ADT7310_CONFIG_INT_COMPARATOR_MODE);
249 }
250 
251 
adt7310_init(const struct device * dev)252 static int adt7310_init(const struct device *dev)
253 {
254 	const struct adt7310_dev_config *cfg = dev->config;
255 	int ret;
256 
257 	if (!spi_is_ready_dt(&cfg->bus)) {
258 		LOG_ERR("SPI bus %s not ready", cfg->bus.bus->name);
259 		return -ENODEV;
260 	}
261 
262 	ret = adt7310_probe(dev);
263 	if (ret) {
264 		return ret;
265 	}
266 
267 #if defined(CONFIG_ADT7310_TRIGGER)
268 	if (cfg->int_gpio.port) {
269 		ret = adt7310_init_interrupt(dev);
270 		if (ret) {
271 			LOG_ERR("Failed to initialize interrupt");
272 			return ret;
273 		}
274 	}
275 #endif
276 	return ret;
277 }
278 
279 static DEVICE_API(sensor, adt7310_driver_api) = {
280 	.attr_set = adt7310_attr_set,
281 	.sample_fetch = adt7310_sample_fetch,
282 	.channel_get  = adt7310_channel_get,
283 #if defined(CONFIG_ADT7310_TRIGGER)
284 	.trigger_set = adt7310_trigger_set,
285 #endif
286 };
287 
288 #define ADT7310_DEFINE(inst)                                                                       \
289 	static struct adt7310_data adt7310_data_##inst;                                            \
290                                                                                                    \
291 	static const struct adt7310_dev_config adt7310_config_##inst = {                           \
292 		.bus = SPI_DT_SPEC_INST_GET(                                                       \
293 			inst,                                                                      \
294 			(SPI_WORD_SET(8) | SPI_TRANSFER_MSB | SPI_MODE_CPOL | SPI_MODE_CPHA), 0),  \
295                                                                                                    \
296 		IF_ENABLED(CONFIG_ADT7310_TRIGGER,                                                 \
297 			   (.int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0}),))};        \
298                                                                                                    \
299 	SENSOR_DEVICE_DT_INST_DEFINE(inst, adt7310_init, NULL, &adt7310_data_##inst,               \
300 				     &adt7310_config_##inst, POST_KERNEL,                          \
301 				     CONFIG_SENSOR_INIT_PRIORITY, &adt7310_driver_api);
302 
303 DT_INST_FOREACH_STATUS_OKAY(ADT7310_DEFINE)
304