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