xref: /freebsd/sys/dev/ow/ow_temp.c (revision 6ae1554a5d9b318f8ad53ccc39fa5a961403da73)
1 /*-
2  * Copyright (c) 2015 M. Warner Losh <imp@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice unmodified, this list of conditions, and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 
34 #include <sys/bus.h>
35 #include <sys/errno.h>
36 #include <sys/libkern.h>
37 #include <sys/kthread.h>
38 #include <sys/malloc.h>
39 #include <sys/module.h>
40 #include <sys/mutex.h>
41 #include <sys/proc.h>
42 #include <sys/sysctl.h>
43 
44 #include <dev/ow/ow.h>
45 #include <dev/ow/own.h>
46 
47 #define OWT_DS1820	0x10		/* Also 18S20 */
48 #define OWT_DS1822	0x22		/* Very close to 18B20 */
49 #define OWT_DS18B20	0x28		/* Also MAX31820 */
50 #define	OWT_DS1825	0x3B		/* Just like 18B20 with address bits */
51 
52 #define	CONVERT_T		0x44
53 #define COPY_SCRATCHPAD		0x48
54 #define WRITE_SCRATCHPAD	0x4e
55 #define READ_POWER_SUPPLY	0xb4
56 #define	RECALL_EE		0xb8
57 #define	READ_SCRATCHPAD		0xbe
58 
59 
60 #define	OW_TEMP_DONE		0x01
61 #define	OW_TEMP_RUNNING		0x02
62 
63 struct ow_temp_softc
64 {
65 	device_t	dev;
66 	int		type;
67 	int		temp;
68 	int		flags;
69 	int		bad_crc;
70 	int		bad_reads;
71 	int		reading_interval;
72 	int		parasite;
73 	struct mtx	temp_lock;
74 	struct proc	*event_thread;
75 };
76 
77 static int
78 ow_temp_probe(device_t dev)
79 {
80 
81 	switch (ow_get_family(dev)) {
82 	case OWT_DS1820:
83 		device_set_desc(dev, "One Wire Temperature");
84 		return BUS_PROBE_DEFAULT;
85 	case OWT_DS1822:
86 	case OWT_DS1825:
87 	case OWT_DS18B20:
88 		device_set_desc(dev, "Advanced One Wire Temperature");
89 		return BUS_PROBE_DEFAULT;
90 	default:
91 		return ENXIO;
92 	}
93 }
94 
95 static int
96 ow_temp_read_scratchpad(device_t dev, uint8_t *scratch, int len)
97 {
98 	struct ow_cmd cmd;
99 
100 	own_self_command(dev, &cmd, READ_SCRATCHPAD);
101 	cmd.xpt_read_len = len;
102 	own_command_wait(dev, &cmd);
103 	memcpy(scratch, cmd.xpt_read, len);
104 
105 	return 0;
106 }
107 
108 static int
109 ow_temp_convert_t(device_t dev)
110 {
111 	struct ow_cmd cmd;
112 
113 	own_self_command(dev, &cmd, CONVERT_T);
114 	own_command_wait(dev, &cmd);
115 
116 	return 0;
117 }
118 
119 
120 static int
121 ow_temp_read_power_supply(device_t dev, int *parasite)
122 {
123 	struct ow_cmd cmd;
124 
125 	own_self_command(dev, &cmd, READ_POWER_SUPPLY);
126 	cmd.flags |= OW_FLAG_READ_BIT;
127 	cmd.xpt_read_len = 1;
128 	own_command_wait(dev, &cmd);
129 	*parasite = !cmd.xpt_read[0];	/* parasites pull bus low */
130 
131 	return 0;
132 }
133 
134 static void
135 ow_temp_event_thread(void *arg)
136 {
137 	struct ow_temp_softc *sc;
138 	uint8_t scratch[8 + 1];
139 	uint8_t crc;
140 	int retries, rv;
141 
142 	sc = arg;
143 	pause("owtstart", device_get_unit(sc->dev) * hz / 100);	// 10ms stagger
144 	mtx_lock(&sc->temp_lock);
145 	sc->flags |= OW_TEMP_RUNNING;
146 	ow_temp_read_power_supply(sc->dev, &sc->parasite);
147 	if (sc->parasite)
148 		device_printf(sc->dev, "Running in parasitic mode unsupported\n");
149 	while ((sc->flags & OW_TEMP_DONE) == 0) {
150 		mtx_unlock(&sc->temp_lock);
151 		ow_temp_convert_t(sc->dev);
152 		mtx_lock(&sc->temp_lock);
153 		msleep(sc, &sc->temp_lock, 0, "owtcvt", hz);
154 		if (sc->flags & OW_TEMP_DONE)
155 			break;
156 		for (retries = 5; retries > 0; retries--) {
157 			mtx_unlock(&sc->temp_lock);
158 			rv = ow_temp_read_scratchpad(sc->dev, scratch, sizeof(scratch));
159 			mtx_lock(&sc->temp_lock);
160 			if (rv == 0) {
161 				crc = own_crc(sc->dev, scratch, sizeof(scratch) - 1);
162 				if (crc == scratch[8]) {
163 					if (sc->type == OWT_DS1820) {
164 						if (scratch[7]) {
165 							/*
166 							 * Formula from DS18S20 datasheet, page 6
167 							 * DS18S20 datahseet says count_per_c is 16, DS1820 does not
168 							 */
169 							sc->temp = (int16_t)((scratch[0] & 0xfe) |
170 							    (scratch[1] << 8)) << 3;
171 							sc->temp += 16 - scratch[6] - 4; /* count_per_c == 16 */
172 						} else
173 							sc->temp = (int16_t)(scratch[0] | (scratch[1] << 8)) << 3;
174 					} else
175 						sc->temp = (int16_t)(scratch[0] | (scratch[1] << 8));
176 					sc->temp = sc->temp * 1000 / 16 + 273150;
177 					break;
178 				}
179 				sc->bad_crc++;
180 			} else
181 				sc->bad_reads++;
182 		}
183 		msleep(sc, &sc->temp_lock, 0, "owtcvt", sc->reading_interval);
184 	}
185 	sc->flags &= ~OW_TEMP_RUNNING;
186 	mtx_unlock(&sc->temp_lock);
187 	kproc_exit(0);
188 }
189 
190 static int
191 ow_temp_attach(device_t dev)
192 {
193 	struct ow_temp_softc *sc;
194 
195 	sc = device_get_softc(dev);
196 	sc->dev = dev;
197 	sc->type = ow_get_family(dev);
198 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
199 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
200 	    OID_AUTO, "temperature", CTLFLAG_RD | CTLTYPE_INT,
201 	    &sc->temp, 0, sysctl_handle_int,
202 	    "IK3", "Current Temperature");
203 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
204 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
205 	    OID_AUTO, "badcrc", CTLFLAG_RD,
206 	    &sc->bad_crc, 0,
207 	    "Number of Bad CRC on reading scratchpad");
208 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
209 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
210 	    OID_AUTO, "badread", CTLFLAG_RD,
211 	    &sc->bad_reads, 0,
212 	    "Number of errors on reading scratchpad");
213 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
214 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
215 	    OID_AUTO, "reading_interval", CTLFLAG_RW,
216 	    &sc->reading_interval, 0,
217 	    "ticks between reads");
218 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
219 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
220 	    OID_AUTO, "parasite", CTLFLAG_RW,
221 	    &sc->parasite, 0,
222 	    "In Parasite mode");
223 	/*
224 	 * Just do this for unit 0 to avoid locking
225 	 * the ow bus until that code can be put
226 	 * into place.
227 	 */
228 	sc->temp = -1;
229 	sc->reading_interval = 10 * hz;
230 	mtx_init(&sc->temp_lock, "lock for doing temperature", NULL, MTX_DEF);
231 	/* Start the thread */
232 	if (kproc_create(ow_temp_event_thread, sc, &sc->event_thread, 0, 0,
233 	    "%s event thread", device_get_nameunit(dev))) {
234 		device_printf(dev, "unable to create event thread.\n");
235 		panic("cbb_create_event_thread");
236 	}
237 
238 	return 0;
239 }
240 
241 static int
242 ow_temp_detach(device_t dev)
243 {
244 	struct ow_temp_softc *sc;
245 
246 	sc = device_get_softc(dev);
247 
248 	/*
249 	 * Wait for the thread to die.  kproc_exit will do a wakeup
250 	 * on the event thread's struct thread * so that we know it is
251 	 * safe to proceed.  IF the thread is running, set the please
252 	 * die flag and wait for it to comply.  Since the wakeup on
253 	 * the event thread happens only in kproc_exit, we don't
254 	 * need to loop here.
255 	 */
256 	mtx_lock(&sc->temp_lock);
257 	sc->flags |= OW_TEMP_DONE;
258 	while (sc->flags & OW_TEMP_RUNNING) {
259 		wakeup(sc);
260 		msleep(sc->event_thread, &sc->temp_lock, PWAIT, "owtun", 0);
261 	}
262 	mtx_destroy(&sc->temp_lock);
263 
264 	return 0;
265 }
266 
267 devclass_t ow_temp_devclass;
268 
269 static device_method_t ow_temp_methods[] = {
270 	/* Device interface */
271 	DEVMETHOD(device_probe,		ow_temp_probe),
272 	DEVMETHOD(device_attach,	ow_temp_attach),
273 	DEVMETHOD(device_detach,	ow_temp_detach),
274 
275 	{ 0, 0 }
276 };
277 
278 static driver_t ow_temp_driver = {
279 	"ow_temp",
280 	ow_temp_methods,
281 	sizeof(struct ow_temp_softc),
282 };
283 
284 DRIVER_MODULE(ow_temp, ow, ow_temp_driver, ow_temp_devclass, 0, 0);
285 MODULE_DEPEND(ow_temp, ow, 1, 1, 1);
286