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