1 /*
2  * Copyright (c) 2022 Thomas Stranger
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * Driver for DS18B20 and DS18S20 1-Wire temperature sensors
9  * Datasheets for the compatible sensors are available at:
10  * - https://www.analog.com/media/en/technical-documentation/data-sheets/ds18b20.pdf
11  * - https://www.analog.com/media/en/technical-documentation/data-sheets/ds18s20.pdf
12  *
13  * Parasite power configuration is not supported by the driver.
14  */
15 
16 #include <stdbool.h>
17 
18 #include <zephyr/device.h>
19 #include <zephyr/devicetree.h>
20 #include <zephyr/drivers/sensor.h>
21 #include <zephyr/drivers/sensor/w1_sensor.h>
22 #include "zephyr/drivers/w1.h"
23 #include <zephyr/kernel.h>
24 #include <zephyr/logging/log.h>
25 #include <zephyr/sys/__assert.h>
26 #include <zephyr/sys/util_macro.h>
27 
28 LOG_MODULE_REGISTER(DS18B20, CONFIG_SENSOR_LOG_LEVEL);
29 
30 #define DS18B20_CMD_CONVERT_T         0x44
31 #define DS18B20_CMD_WRITE_SCRATCHPAD  0x4E
32 #define DS18B20_CMD_READ_SCRATCHPAD   0xBE
33 #define DS18B20_CMD_COPY_SCRATCHPAD   0x48
34 #define DS18B20_CMD_RECALL_EEPROM     0xB8
35 #define DS18B20_CMD_READ_POWER_SUPPLY 0xB4
36 
37 /* resolution is set using bit 5 and 6 of configuration register
38  * macro only valid for values 9 to 12
39  */
40 #define DS18B20_RESOLUTION_POS		5
41 #define DS18B20_RESOLUTION_MASK		(BIT_MASK(2) << DS18B20_RESOLUTION_POS)
42 /* convert resolution in bits to scratchpad config format */
43 #define DS18B20_RESOLUTION(res)		((res - 9) << DS18B20_RESOLUTION_POS)
44 /* convert resolution in bits to array index (for resolution specific elements) */
45 #define DS18B20_RESOLUTION_INDEX(res)	(res - 9)
46 
47 #define DS18B20_FAMILYCODE 0x28
48 #define DS18S20_FAMILYCODE 0x10
49 
50 enum chip_type {type_ds18b20, type_ds18s20};
51 
52 struct ds18b20_scratchpad {
53 	int16_t temp;
54 	uint8_t alarm_temp_high;
55 	uint8_t alarm_temp_low;
56 	uint8_t config;
57 	uint8_t res[3];
58 	uint8_t crc;
59 } __packed;
60 
61 struct ds18b20_config {
62 	const struct device *bus;
63 	uint8_t family;
64 	uint8_t resolution;
65 	enum chip_type chip;
66 };
67 
68 struct ds18b20_data {
69 	struct w1_slave_config config;
70 	struct ds18b20_scratchpad scratchpad;
71 	bool lazy_loaded;
72 };
73 
74 static int ds18b20_configure(const struct device *dev);
75 static int ds18b20_read_scratchpad(const struct device *dev, struct ds18b20_scratchpad *scratchpad);
76 
77 /* measure wait time for 9-bit, 10-bit, 11-bit, 12-bit resolution respectively */
78 static const uint16_t measure_wait_ds18b20_ms[4] = { 94, 188, 376, 750 };
79 
80 /* ds18s20 always needs 750ms */
81 static const uint16_t measure_wait_ds18s20_ms = { 750 };
82 
ds18b20_temperature_from_raw(const struct device * dev,uint8_t * temp_raw,struct sensor_value * val)83 static inline void ds18b20_temperature_from_raw(const struct device *dev,
84 						uint8_t *temp_raw,
85 						struct sensor_value *val)
86 {
87 	const struct ds18b20_config *cfg = dev->config;
88 	int16_t temp = sys_get_le16 (temp_raw);
89 
90 	if (cfg->chip == type_ds18s20) {
91 		val->val1 = temp / 2;
92 		val->val2 = (temp % 2) * 5000000;
93 	} else {
94 		val->val1 = temp / 16;
95 		val->val2 = (temp % 16) * 1000000 / 16;
96 	}
97 }
98 
slave_responded(uint8_t * rx_buf,size_t len)99 static inline bool slave_responded(uint8_t *rx_buf, size_t len)
100 {
101 	uint8_t cmp_byte = 0xff;
102 
103 	for (int i = 0; i < len; i++) {
104 		cmp_byte &= rx_buf[i];
105 	}
106 
107 	return (cmp_byte == 0xff) ? false : true;
108 }
109 
110 /*
111  * Write scratch pad, read back, then copy to eeprom
112  */
ds18b20_write_scratchpad(const struct device * dev,struct ds18b20_scratchpad scratchpad)113 static int ds18b20_write_scratchpad(const struct device *dev,
114 				    struct ds18b20_scratchpad scratchpad)
115 {
116 	struct ds18b20_data *data = dev->data;
117 	const struct ds18b20_config *cfg = dev->config;
118 	const struct device *bus = cfg->bus;
119 	int ret;
120 	uint8_t sp_data[4] = {
121 		DS18B20_CMD_WRITE_SCRATCHPAD,
122 		scratchpad.alarm_temp_high,
123 		scratchpad.alarm_temp_low,
124 		scratchpad.config
125 	};
126 
127 	ret = w1_write_read(bus, &data->config, sp_data, sizeof(sp_data), NULL, 0);
128 	if (ret != 0) {
129 		return ret;
130 	}
131 
132 	ret = ds18b20_read_scratchpad(dev, &scratchpad);
133 	if (ret != 0) {
134 		return ret;
135 	}
136 
137 	if ((sp_data[3] & DS18B20_RESOLUTION_MASK) !=
138 	    (scratchpad.config & DS18B20_RESOLUTION_MASK)) {
139 		return -EIO;
140 	}
141 
142 	return 0;
143 }
144 
ds18b20_read_scratchpad(const struct device * dev,struct ds18b20_scratchpad * scratchpad)145 static int ds18b20_read_scratchpad(const struct device *dev,
146 				   struct ds18b20_scratchpad *scratchpad)
147 {
148 	struct ds18b20_data *data = dev->data;
149 	const struct ds18b20_config *cfg = dev->config;
150 	const struct device *bus = cfg->bus;
151 	int ret;
152 	uint8_t cmd = DS18B20_CMD_READ_SCRATCHPAD;
153 	uint8_t crc;
154 
155 	memset(scratchpad, 0, sizeof(*scratchpad));
156 	ret = w1_write_read(bus, &data->config, &cmd, 1,
157 			     (uint8_t *)scratchpad, sizeof(*scratchpad));
158 	if (ret != 0) {
159 		return ret;
160 	}
161 
162 	if (!slave_responded((uint8_t *)scratchpad, sizeof(*scratchpad))) {
163 		LOG_WRN("Slave not reachable");
164 		return -ENODEV;
165 	}
166 
167 	crc = w1_crc8((uint8_t *)scratchpad, sizeof(*scratchpad) - 1);
168 	if (crc != scratchpad->crc) {
169 		LOG_WRN("CRC does not match");
170 		return -EIO;
171 	}
172 
173 	return 0;
174 }
175 
176 /* Starts sensor temperature conversion without waiting for completion. */
ds18b20_temperature_convert(const struct device * dev)177 static int ds18b20_temperature_convert(const struct device *dev)
178 {
179 	int ret;
180 	struct ds18b20_data *data = dev->data;
181 	const struct ds18b20_config *cfg = dev->config;
182 	const struct device *bus = cfg->bus;
183 
184 	(void)w1_lock_bus(bus);
185 	ret = w1_reset_select(bus, &data->config);
186 	if (ret != 0) {
187 		goto out;
188 	}
189 	ret = w1_write_byte(bus, DS18B20_CMD_CONVERT_T);
190 out:
191 	(void)w1_unlock_bus(bus);
192 	return ret;
193 }
194 
195 /*
196  * Write resolution into configuration struct,
197  * but don't write it to the sensor yet.
198  */
ds18b20_set_resolution(const struct device * dev,uint8_t resolution)199 static void ds18b20_set_resolution(const struct device *dev, uint8_t resolution)
200 {
201 	struct ds18b20_data *data = dev->data;
202 
203 	data->scratchpad.config &= ~DS18B20_RESOLUTION_MASK;
204 	data->scratchpad.config |= DS18B20_RESOLUTION(resolution);
205 }
206 
measure_wait_ms(const struct device * dev)207 static uint16_t measure_wait_ms(const struct device *dev)
208 {
209 	const struct ds18b20_config *cfg = dev->config;
210 
211 	if (cfg->chip == type_ds18s20) {
212 		return measure_wait_ds18s20_ms;
213 	}
214 
215 	return measure_wait_ds18b20_ms[DS18B20_RESOLUTION_INDEX(cfg->resolution)];
216 }
217 
ds18b20_sample_fetch(const struct device * dev,enum sensor_channel chan)218 static int ds18b20_sample_fetch(const struct device *dev,
219 				enum sensor_channel chan)
220 {
221 	struct ds18b20_data *data = dev->data;
222 	int status;
223 
224 	__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL ||
225 			chan == SENSOR_CHAN_AMBIENT_TEMP);
226 
227 	if (!data->lazy_loaded) {
228 		status = ds18b20_configure(dev);
229 		if (status < 0) {
230 			return status;
231 		}
232 		data->lazy_loaded = true;
233 	}
234 
235 	status = ds18b20_temperature_convert(dev);
236 	if (status < 0) {
237 		LOG_DBG("W1 fetch error");
238 		return status;
239 	}
240 	k_msleep(measure_wait_ms(dev));
241 	return ds18b20_read_scratchpad(dev, &data->scratchpad);
242 }
243 
ds18b20_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)244 static int ds18b20_channel_get(const struct device *dev,
245 			       enum sensor_channel chan,
246 			       struct sensor_value *val)
247 {
248 	struct ds18b20_data *data = dev->data;
249 
250 	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
251 		return -ENOTSUP;
252 	}
253 
254 	ds18b20_temperature_from_raw(dev, (uint8_t *)&data->scratchpad.temp, val);
255 	return 0;
256 }
257 
ds18b20_configure(const struct device * dev)258 static int ds18b20_configure(const struct device *dev)
259 {
260 	const struct ds18b20_config *cfg = dev->config;
261 	struct ds18b20_data *data = dev->data;
262 	int ret;
263 
264 	if (w1_reset_bus(cfg->bus) <= 0) {
265 		LOG_ERR("No 1-Wire slaves connected");
266 		return -ENODEV;
267 	}
268 
269 	/* In single drop configurations the rom can be read from device */
270 	if (w1_get_slave_count(cfg->bus) == 1) {
271 		if (w1_rom_to_uint64(&data->config.rom) == 0ULL) {
272 			(void)w1_read_rom(cfg->bus, &data->config.rom);
273 		}
274 	} else if (w1_rom_to_uint64(&data->config.rom) == 0ULL) {
275 		LOG_DBG("nr: %d", w1_get_slave_count(cfg->bus));
276 		LOG_ERR("ROM required, because multiple slaves are on the bus");
277 		return -EINVAL;
278 	}
279 
280 	if ((cfg->family != 0) && (cfg->family != data->config.rom.family)) {
281 		LOG_ERR("Found 1-Wire slave is not a %s", dev->name);
282 		return -EINVAL;
283 	}
284 
285 	/* write default configuration */
286 	if (cfg->chip == type_ds18b20) {
287 		ds18b20_set_resolution(dev, cfg->resolution);
288 		ret = ds18b20_write_scratchpad(dev, data->scratchpad);
289 		if (ret < 0) {
290 			return ret;
291 		}
292 	}
293 	LOG_DBG("Init %s: ROM=%016llx\n", dev->name,
294 		w1_rom_to_uint64(&data->config.rom));
295 
296 	return 0;
297 }
298 
ds18b20_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * thr)299 int ds18b20_attr_set(const struct device *dev, enum sensor_channel chan,
300 		     enum sensor_attribute attr, const struct sensor_value *thr)
301 {
302 	struct ds18b20_data *data = dev->data;
303 
304 	if ((enum sensor_attribute_w1)attr != SENSOR_ATTR_W1_ROM) {
305 		return -ENOTSUP;
306 	}
307 
308 	data->lazy_loaded = false;
309 	w1_sensor_value_to_rom(thr, &data->config.rom);
310 	return 0;
311 }
312 
313 static DEVICE_API(sensor, ds18b20_driver_api) = {
314 	.attr_set = ds18b20_attr_set,
315 	.sample_fetch = ds18b20_sample_fetch,
316 	.channel_get = ds18b20_channel_get,
317 };
318 
ds18b20_init(const struct device * dev)319 static int ds18b20_init(const struct device *dev)
320 {
321 	const struct ds18b20_config *cfg = dev->config;
322 	struct ds18b20_data *data = dev->data;
323 
324 	if (device_is_ready(cfg->bus) == 0) {
325 		LOG_DBG("w1 bus is not ready");
326 		return -ENODEV;
327 	}
328 
329 	w1_uint64_to_rom(0ULL, &data->config.rom);
330 	data->lazy_loaded = false;
331 	/* in multidrop configurations the rom is need, but is not set during
332 	 * driver initialization, therefore do lazy initialization in all cases.
333 	 */
334 
335 	return 0;
336 }
337 
338 #define DS18B20_CONFIG_INIT(inst, default_family_code, chip_type)    \
339 	{								\
340 		.bus = DEVICE_DT_GET(DT_INST_BUS(inst)),		       \
341 		.family = (uint8_t)DT_INST_PROP_OR(inst, family_code, default_family_code),   \
342 		.resolution = DT_INST_PROP_OR(inst, resolution, 12),		\
343 		.chip = chip_type,	\
344 	}
345 
346 #define DS18B20_DEFINE(inst, name, family_code, chip_type)		\
347 	static struct ds18b20_data data_##name##_##inst;			\
348 	static const struct ds18b20_config config_##name##_##inst =	\
349 		DS18B20_CONFIG_INIT(inst, family_code, chip_type);	\
350 	SENSOR_DEVICE_DT_INST_DEFINE(inst,				\
351 			      ds18b20_init,				\
352 			      NULL,					\
353 			      &data_##name##_##inst,			\
354 			      &config_##name##_##inst,			\
355 			      POST_KERNEL,				\
356 			      CONFIG_SENSOR_INIT_PRIORITY,		\
357 			      &ds18b20_driver_api);
358 
359 #define DT_DRV_COMPAT maxim_ds18b20
360 DT_INST_FOREACH_STATUS_OKAY_VARGS(DS18B20_DEFINE, DT_DRV_COMPAT,
361 				DS18B20_FAMILYCODE,
362 				type_ds18b20)
363 #undef DT_DRV_COMPAT
364 
365 #define DT_DRV_COMPAT maxim_ds18s20
366 DT_INST_FOREACH_STATUS_OKAY_VARGS(DS18B20_DEFINE, DT_DRV_COMPAT,
367 				DS18S20_FAMILYCODE,
368 				type_ds18s20)
369 #undef DT_DRV_COMPAT
370