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__FBSDID("$FreeBSD$");
28
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/kernel.h>
32
33#include <sys/bus.h>
34#include <sys/errno.h>
35#include <sys/libkern.h>
36#include <sys/kthread.h>
37#include <sys/malloc.h>
38#include <sys/module.h>
39#include <sys/mutex.h>
40#include <sys/proc.h>
41#include <sys/sysctl.h>
42
43#include <dev/ow/ow.h>
44#include <dev/ow/own.h>
45
46#define OWT_DS1820	0x10		/* Also 18S20 */
47#define OWT_DS1822	0x22		/* Very close to 18B20 */
48#define OWT_DS18B20	0x28		/* Also MAX31820 */
49#define	OWT_DS1825	0x3B		/* Just like 18B20 with address bits */
50
51#define	CONVERT_T		0x44
52#define COPY_SCRATCHPAD		0x48
53#define WRITE_SCRATCHPAD	0x4e
54#define READ_POWER_SUPPLY	0xb4
55#define	RECALL_EE		0xb8
56#define	READ_SCRATCHPAD		0xbe
57
58#define	OW_TEMP_DONE		0x01
59#define	OW_TEMP_RUNNING		0x02
60
61struct ow_temp_softc
62{
63	device_t	dev;
64	int		type;
65	int		temp;
66	int		flags;
67	int		bad_crc;
68	int		bad_reads;
69	int		reading_interval;
70	int		parasite;
71	struct mtx	temp_lock;
72	struct proc	*event_thread;
73};
74
75static int
76ow_temp_probe(device_t dev)
77{
78
79	switch (ow_get_family(dev)) {
80	case OWT_DS1820:
81		device_set_desc(dev, "One Wire Temperature");
82		return BUS_PROBE_DEFAULT;
83	case OWT_DS1822:
84	case OWT_DS1825:
85	case OWT_DS18B20:
86		device_set_desc(dev, "Advanced One Wire Temperature");
87		return BUS_PROBE_DEFAULT;
88	default:
89		return ENXIO;
90	}
91}
92
93static int
94ow_temp_read_scratchpad(device_t dev, uint8_t *scratch, int len)
95{
96	struct ow_cmd cmd;
97
98	own_self_command(dev, &cmd, READ_SCRATCHPAD);
99	cmd.xpt_read_len = len;
100	own_command_wait(dev, &cmd);
101	memcpy(scratch, cmd.xpt_read, len);
102
103	return 0;
104}
105
106static int
107ow_temp_convert_t(device_t dev)
108{
109	struct ow_cmd cmd;
110
111	own_self_command(dev, &cmd, CONVERT_T);
112	own_command_wait(dev, &cmd);
113
114	return 0;
115}
116
117static int
118ow_temp_read_power_supply(device_t dev, int *parasite)
119{
120	struct ow_cmd cmd;
121
122	own_self_command(dev, &cmd, READ_POWER_SUPPLY);
123	cmd.flags |= OW_FLAG_READ_BIT;
124	cmd.xpt_read_len = 1;
125	own_command_wait(dev, &cmd);
126	*parasite = !cmd.xpt_read[0];	/* parasites pull bus low */
127
128	return 0;
129}
130
131static void
132ow_temp_event_thread(void *arg)
133{
134	struct ow_temp_softc *sc;
135	uint8_t scratch[8 + 1];
136	uint8_t crc;
137	int retries, rv, tmp;
138
139	sc = arg;
140	pause("owtstart", device_get_unit(sc->dev) * hz / 100);	// 10ms stagger
141	mtx_lock(&sc->temp_lock);
142	sc->flags |= OW_TEMP_RUNNING;
143	mtx_unlock(&sc->temp_lock);
144	ow_temp_read_power_supply(sc->dev, &sc->parasite);
145	if (sc->parasite)
146		device_printf(sc->dev, "Running in parasitic mode unsupported\n");
147	mtx_lock(&sc->temp_lock);
148	while ((sc->flags & OW_TEMP_DONE) == 0) {
149		mtx_unlock(&sc->temp_lock);
150		ow_temp_convert_t(sc->dev);
151		mtx_lock(&sc->temp_lock);
152		msleep(sc, &sc->temp_lock, 0, "owtcvt", hz);
153		if (sc->flags & OW_TEMP_DONE)
154			break;
155		mtx_unlock(&sc->temp_lock);
156		for (retries = 5; retries > 0; retries--) {
157			rv = ow_temp_read_scratchpad(sc->dev, scratch, sizeof(scratch));
158			if (rv == 0) {
159				crc = own_crc(sc->dev, scratch, sizeof(scratch) - 1);
160				if (crc == scratch[8]) {
161					if (sc->type == OWT_DS1820) {
162						if (scratch[7]) {
163							/*
164							 * Formula from DS18S20 datasheet, page 6
165							 * DS18S20 datasheet says count_per_c is 16, DS1820 does not
166							 */
167							tmp = (int16_t)((scratch[0] & 0xfe) |
168							    (scratch[1] << 8)) << 3;
169							tmp += 16 - scratch[6] - 4; /* count_per_c == 16 */
170						} else
171							tmp = (int16_t)(scratch[0] | (scratch[1] << 8)) << 3;
172					} else
173						tmp = (int16_t)(scratch[0] | (scratch[1] << 8));
174					sc->temp = tmp * 1000 / 16 + 273150;
175					break;
176				}
177				sc->bad_crc++;
178			} else
179				sc->bad_reads++;
180		}
181		mtx_lock(&sc->temp_lock);
182		msleep(sc, &sc->temp_lock, 0, "owtcvt", sc->reading_interval);
183	}
184	sc->flags &= ~OW_TEMP_RUNNING;
185	mtx_unlock(&sc->temp_lock);
186	kproc_exit(0);
187}
188
189static int
190ow_temp_attach(device_t dev)
191{
192	struct ow_temp_softc *sc;
193
194	sc = device_get_softc(dev);
195	sc->dev = dev;
196	sc->type = ow_get_family(dev);
197	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
198	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
199	    OID_AUTO, "temperature",
200	    CTLFLAG_RD | CTLTYPE_INT | CTLFLAG_NEEDGIANT,
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("ow_temp_attach, can't create thread");
236	}
237
238	return 0;
239}
240
241static int
242ow_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
267devclass_t ow_temp_devclass;
268
269static 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	{ 0, 0 }
275};
276
277static driver_t ow_temp_driver = {
278	"ow_temp",
279	ow_temp_methods,
280	sizeof(struct ow_temp_softc),
281};
282
283DRIVER_MODULE(ow_temp, ow, ow_temp_driver, ow_temp_devclass, 0, 0);
284MODULE_DEPEND(ow_temp, ow, 1, 1, 1);
285