xref: /freebsd/sys/dev/ow/ow.c (revision 63aa6c0cd9bc0e1ed1d7876987ed0a106a8f876b)
1ae1f3df4SWarner Losh /*-
2ae1f3df4SWarner Losh  * Copyright (c) 2015 M. Warner Losh <imp@freebsd.org>
3ae1f3df4SWarner Losh  * All rights reserved.
4ae1f3df4SWarner Losh  *
5ae1f3df4SWarner Losh  * Redistribution and use in source and binary forms, with or without
6ae1f3df4SWarner Losh  * modification, are permitted provided that the following conditions
7ae1f3df4SWarner Losh  * are met:
8ae1f3df4SWarner Losh  * 1. Redistributions of source code must retain the above copyright
9ae1f3df4SWarner Losh  *    notice unmodified, this list of conditions, and the following
10ae1f3df4SWarner Losh  *    disclaimer.
11ae1f3df4SWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
12ae1f3df4SWarner Losh  *    notice, this list of conditions and the following disclaimer in the
13ae1f3df4SWarner Losh  *    documentation and/or other materials provided with the distribution.
14ae1f3df4SWarner Losh  *
15ae1f3df4SWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16ae1f3df4SWarner Losh  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17ae1f3df4SWarner Losh  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18ae1f3df4SWarner Losh  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19ae1f3df4SWarner Losh  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20ae1f3df4SWarner Losh  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21ae1f3df4SWarner Losh  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22ae1f3df4SWarner Losh  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23ae1f3df4SWarner Losh  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24ae1f3df4SWarner Losh  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25ae1f3df4SWarner Losh  */
26ae1f3df4SWarner Losh 
27ae1f3df4SWarner Losh #include <sys/cdefs.h>
28ae1f3df4SWarner Losh __FBSDID("$FreeBSD$");
29ae1f3df4SWarner Losh 
30ae1f3df4SWarner Losh #include <sys/param.h>
31ae1f3df4SWarner Losh #include <sys/systm.h>
32ae1f3df4SWarner Losh #include <sys/kernel.h>
33ae1f3df4SWarner Losh 
34ae1f3df4SWarner Losh #include <sys/bus.h>
35ae1f3df4SWarner Losh #include <sys/errno.h>
36ae1f3df4SWarner Losh #include <sys/libkern.h>
37ae1f3df4SWarner Losh #include <sys/malloc.h>
38ae1f3df4SWarner Losh #include <sys/module.h>
39ae1f3df4SWarner Losh 
40ae1f3df4SWarner Losh #include <dev/ow/ow.h>
41ae1f3df4SWarner Losh #include <dev/ow/owll.h>
42ae1f3df4SWarner Losh #include <dev/ow/own.h>
43ae1f3df4SWarner Losh 
44ae1f3df4SWarner Losh /*
45ae1f3df4SWarner Losh  * lldev - link level device
46ae1f3df4SWarner Losh  * ndev - network / transport device (this module)
47ae1f3df4SWarner Losh  * pdev - presentation device (children of this module)
48ae1f3df4SWarner Losh  */
49ae1f3df4SWarner Losh 
50ae1f3df4SWarner Losh typedef int ow_enum_fn(device_t, device_t);
51ae1f3df4SWarner Losh typedef int ow_found_fn(device_t, romid_t);
52ae1f3df4SWarner Losh 
53ae1f3df4SWarner Losh struct ow_softc
54ae1f3df4SWarner Losh {
55ae1f3df4SWarner Losh 	device_t	dev;		/* Newbus driver back pointer */
56ae1f3df4SWarner Losh 	struct mtx	mtx;		/* bus mutex */
57ae1f3df4SWarner Losh 	device_t	owner;		/* bus owner, if != NULL */
58ae1f3df4SWarner Losh };
59ae1f3df4SWarner Losh 
60ae1f3df4SWarner Losh struct ow_devinfo
61ae1f3df4SWarner Losh {
62ae1f3df4SWarner Losh 	romid_t	romid;
63ae1f3df4SWarner Losh };
64ae1f3df4SWarner Losh 
65ae1f3df4SWarner Losh static int ow_acquire_bus(device_t ndev, device_t pdev, int how);
66ae1f3df4SWarner Losh static void ow_release_bus(device_t ndev, device_t pdev);
67ae1f3df4SWarner Losh 
68ae1f3df4SWarner Losh #define	OW_LOCK(_sc) mtx_lock(&(_sc)->mtx)
69ae1f3df4SWarner Losh #define	OW_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx)
70ae1f3df4SWarner Losh #define	OW_LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx)
71ae1f3df4SWarner Losh #define	OW_ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED)
72ae1f3df4SWarner Losh #define	OW_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED)
73ae1f3df4SWarner Losh 
74ae1f3df4SWarner Losh static MALLOC_DEFINE(M_OW, "ow", "House keeping data for 1wire bus");
75ae1f3df4SWarner Losh 
76ae1f3df4SWarner Losh static struct ow_timing timing_regular = {
77ae1f3df4SWarner Losh 	.t_slot = 60,		/* 60 to 120 */
78ae1f3df4SWarner Losh 	.t_low0 = 60,		/* really 60 to 120 */
79ae1f3df4SWarner Losh 	.t_low1 = 1,		/* really 1 to 15 */
80ae1f3df4SWarner Losh 	.t_release = 45,	/* <= 45us */
81ae1f3df4SWarner Losh 	.t_rec = 1,		/* at least 1us */
82ae1f3df4SWarner Losh 	.t_rdv = 15,		/* 15us */
83ae1f3df4SWarner Losh 	.t_rstl = 480,		/* 480us or more */
84ae1f3df4SWarner Losh 	.t_rsth = 480,		/* 480us or more */
85ae1f3df4SWarner Losh 	.t_pdl = 60,		/* 60us to 240us */
86ae1f3df4SWarner Losh  	.t_pdh = 60,		/* 15us to 60us */
87ae1f3df4SWarner Losh 	.t_lowr = 1,		/* 1us */
88ae1f3df4SWarner Losh };
89ae1f3df4SWarner Losh 
90ae1f3df4SWarner Losh /* NB: Untested */
91ae1f3df4SWarner Losh static struct ow_timing timing_overdrive = {
92ae1f3df4SWarner Losh 	.t_slot = 11,		/* 6us to 16us */
93ae1f3df4SWarner Losh 	.t_low0 = 6,		/* really 6 to 16 */
94ae1f3df4SWarner Losh 	.t_low1 = 1,		/* really 1 to 2 */
95ae1f3df4SWarner Losh 	.t_release = 4,		/* <= 4us */
96ae1f3df4SWarner Losh 	.t_rec = 1,		/* at least 1us */
97ae1f3df4SWarner Losh 	.t_rdv = 2,		/* 2us */
98ae1f3df4SWarner Losh 	.t_rstl = 48,		/* 48us to 80us */
99ae1f3df4SWarner Losh 	.t_rsth = 48,		/* 48us or more  */
100ae1f3df4SWarner Losh 	.t_pdl = 8,		/* 8us to 24us */
101ae1f3df4SWarner Losh 	.t_pdh = 2,		/* 2us to 6us */
102ae1f3df4SWarner Losh 	.t_lowr = 1,		/* 1us */
103ae1f3df4SWarner Losh };
104ae1f3df4SWarner Losh 
105ae1f3df4SWarner Losh static void
106ae1f3df4SWarner Losh ow_send_byte(device_t lldev, struct ow_timing *t, uint8_t byte)
107ae1f3df4SWarner Losh {
108ae1f3df4SWarner Losh 	int i;
109ae1f3df4SWarner Losh 
110ae1f3df4SWarner Losh 	for (i = 0; i < 8; i++)
111ae1f3df4SWarner Losh 		if (byte & (1 << i))
112ae1f3df4SWarner Losh 			OWLL_WRITE_ONE(lldev, t);
113ae1f3df4SWarner Losh 		else
114ae1f3df4SWarner Losh 			OWLL_WRITE_ZERO(lldev, t);
115ae1f3df4SWarner Losh }
116ae1f3df4SWarner Losh 
117ae1f3df4SWarner Losh static void
118ae1f3df4SWarner Losh ow_read_byte(device_t lldev, struct ow_timing *t, uint8_t *bytep)
119ae1f3df4SWarner Losh {
120ae1f3df4SWarner Losh 	int i;
121ae1f3df4SWarner Losh 	uint8_t byte = 0;
122ae1f3df4SWarner Losh 	int bit;
123ae1f3df4SWarner Losh 
124ae1f3df4SWarner Losh 	for (i = 0; i < 8; i++) {
125ae1f3df4SWarner Losh 		OWLL_READ_DATA(lldev, t, &bit);
126ae1f3df4SWarner Losh 		byte |= bit << i;
127ae1f3df4SWarner Losh 	}
128ae1f3df4SWarner Losh 	*bytep = byte;
129ae1f3df4SWarner Losh }
130ae1f3df4SWarner Losh 
131ae1f3df4SWarner Losh static int
132ae1f3df4SWarner Losh ow_send_command(device_t ndev, device_t pdev, struct ow_cmd *cmd)
133ae1f3df4SWarner Losh {
134ae1f3df4SWarner Losh 	int present, i, bit, tries;
135ae1f3df4SWarner Losh 	device_t lldev;
136ae1f3df4SWarner Losh 	struct ow_timing *t;
137ae1f3df4SWarner Losh 
138ae1f3df4SWarner Losh 	lldev = device_get_parent(ndev);
139ae1f3df4SWarner Losh 
140ae1f3df4SWarner Losh 	/*
141ae1f3df4SWarner Losh 	 * Retry the reset a couple of times before giving up.
142ae1f3df4SWarner Losh 	 */
143ae1f3df4SWarner Losh 	tries = 4;
144ae1f3df4SWarner Losh 	do {
145ae1f3df4SWarner Losh 		OWLL_RESET_AND_PRESENCE(lldev, &timing_regular, &present);
146ae1f3df4SWarner Losh 		if (present == 1)
147ae1f3df4SWarner Losh 			device_printf(ndev, "Reset said no device on bus?.\n");
148ae1f3df4SWarner Losh 	} while (present == 1 && tries-- > 0);
149ae1f3df4SWarner Losh 	if (present == 1) {
150ae1f3df4SWarner Losh 		device_printf(ndev, "Reset said the device wasn't there.\n");
151ae1f3df4SWarner Losh 		return ENOENT;		/* No devices acked the RESET */
152ae1f3df4SWarner Losh 	}
153ae1f3df4SWarner Losh 	if (present == -1) {
154ae1f3df4SWarner Losh 		device_printf(ndev, "Reset discovered bus wired wrong.\n");
155ae1f3df4SWarner Losh 		return ENOENT;
156ae1f3df4SWarner Losh 	}
157ae1f3df4SWarner Losh 
158ae1f3df4SWarner Losh 	for (i = 0; i < cmd->rom_len; i++)
159ae1f3df4SWarner Losh 		ow_send_byte(lldev, &timing_regular, cmd->rom_cmd[i]);
160ae1f3df4SWarner Losh 	for (i = 0; i < cmd->rom_read_len; i++)
161ae1f3df4SWarner Losh 		ow_read_byte(lldev, &timing_regular, cmd->rom_read + i);
162ae1f3df4SWarner Losh 	if (cmd->xpt_len) {
163ae1f3df4SWarner Losh 		/*
164ae1f3df4SWarner Losh 		 * Per AN937, the reset pulse and ROM level are always
165ae1f3df4SWarner Losh 		 * done with the regular timings. Certain ROM commands
166ae1f3df4SWarner Losh 		 * put the device into overdrive mode for the remainder
167ae1f3df4SWarner Losh 		 * of the data transfer, which is why we have to pass the
168ae1f3df4SWarner Losh 		 * timings here. Commands that need to be handled like this
169ae1f3df4SWarner Losh 		 * are expected to be flagged by the client.
170ae1f3df4SWarner Losh 		 */
171ae1f3df4SWarner Losh 		t = (cmd->flags & OW_FLAG_OVERDRIVE) ?
172ae1f3df4SWarner Losh 		    &timing_overdrive : &timing_regular;
173ae1f3df4SWarner Losh 		for (i = 0; i < cmd->xpt_len; i++)
174ae1f3df4SWarner Losh 			ow_send_byte(lldev, t, cmd->xpt_cmd[i]);
175ae1f3df4SWarner Losh 		if (cmd->flags & OW_FLAG_READ_BIT) {
176ae1f3df4SWarner Losh 			memset(cmd->xpt_read, 0, (cmd->xpt_read_len + 7) / 8);
177ae1f3df4SWarner Losh 			for (i = 0; i < cmd->xpt_read_len; i++) {
178ae1f3df4SWarner Losh 				OWLL_READ_DATA(lldev, t, &bit);
179ae1f3df4SWarner Losh 				cmd->xpt_read[i / 8] |= bit << (i % 8);
180ae1f3df4SWarner Losh 			}
181ae1f3df4SWarner Losh 		} else {
182ae1f3df4SWarner Losh 			for (i = 0; i < cmd->xpt_read_len; i++)
183ae1f3df4SWarner Losh 				ow_read_byte(lldev, t, cmd->xpt_read + i);
184ae1f3df4SWarner Losh 		}
185ae1f3df4SWarner Losh 	}
186ae1f3df4SWarner Losh 	return 0;
187ae1f3df4SWarner Losh }
188ae1f3df4SWarner Losh 
189ae1f3df4SWarner Losh static int
190ae1f3df4SWarner Losh ow_search_rom(device_t lldev, device_t dev)
191ae1f3df4SWarner Losh {
192ae1f3df4SWarner Losh 	struct ow_cmd cmd;
193ae1f3df4SWarner Losh 
194ae1f3df4SWarner Losh 	memset(&cmd, 0, sizeof(cmd));
195ae1f3df4SWarner Losh 	cmd.rom_cmd[0] = SEARCH_ROM;
196ae1f3df4SWarner Losh 	cmd.rom_len = 1;
197ae1f3df4SWarner Losh 	return ow_send_command(lldev, dev, &cmd);
198ae1f3df4SWarner Losh }
199ae1f3df4SWarner Losh 
200ae1f3df4SWarner Losh #if 0
201ae1f3df4SWarner Losh static int
202ae1f3df4SWarner Losh ow_alarm_search(device_t lldev, device_t dev)
203ae1f3df4SWarner Losh {
204ae1f3df4SWarner Losh 	struct ow_cmd cmd;
205ae1f3df4SWarner Losh 
206ae1f3df4SWarner Losh 	memset(&cmd, 0, sizeof(cmd));
207ae1f3df4SWarner Losh 	cmd.rom_cmd[0] = ALARM_SEARCH;
208ae1f3df4SWarner Losh 	cmd.rom_len = 1;
209ae1f3df4SWarner Losh 	return ow_send_command(lldev, dev, &cmd);
210ae1f3df4SWarner Losh }
211ae1f3df4SWarner Losh #endif
212ae1f3df4SWarner Losh 
213ae1f3df4SWarner Losh static int
214ae1f3df4SWarner Losh ow_add_child(device_t dev, romid_t romid)
215ae1f3df4SWarner Losh {
216ae1f3df4SWarner Losh 	struct ow_devinfo *di;
217ae1f3df4SWarner Losh 	device_t child;
218ae1f3df4SWarner Losh 
219ae1f3df4SWarner Losh 	di = malloc(sizeof(*di), M_OW, M_WAITOK);
220ae1f3df4SWarner Losh 	di->romid = romid;
221ae1f3df4SWarner Losh 	child = device_add_child(dev, NULL, -1);
222ae1f3df4SWarner Losh 	if (child == NULL) {
223ae1f3df4SWarner Losh 		free(di, M_OW);
224ae1f3df4SWarner Losh 		return ENOMEM;
225ae1f3df4SWarner Losh 	}
226ae1f3df4SWarner Losh 	device_set_ivars(child, di);
227ae1f3df4SWarner Losh 	return (0);
228ae1f3df4SWarner Losh }
229ae1f3df4SWarner Losh 
230ae1f3df4SWarner Losh static device_t
231ae1f3df4SWarner Losh ow_child_by_romid(device_t dev, romid_t romid)
232ae1f3df4SWarner Losh {
233ae1f3df4SWarner Losh 	device_t *children, retval, child;
234ae1f3df4SWarner Losh 	int nkid, i;
235ae1f3df4SWarner Losh 	struct ow_devinfo *di;
236ae1f3df4SWarner Losh 
237ae1f3df4SWarner Losh 	if (device_get_children(dev, &children, &nkid) != 0)
238ae1f3df4SWarner Losh 		return (NULL);
239ae1f3df4SWarner Losh 	retval = NULL;
240ae1f3df4SWarner Losh 	for (i = 0; i < nkid; i++) {
241ae1f3df4SWarner Losh 		child = children[i];
242ae1f3df4SWarner Losh 		di = device_get_ivars(child);
243ae1f3df4SWarner Losh 		if (di->romid == romid) {
244ae1f3df4SWarner Losh 			retval = child;
245ae1f3df4SWarner Losh 			break;
246ae1f3df4SWarner Losh 		}
247ae1f3df4SWarner Losh 	}
248ae1f3df4SWarner Losh 	free(children, M_TEMP);
249ae1f3df4SWarner Losh 
250ae1f3df4SWarner Losh 	return (retval);
251ae1f3df4SWarner Losh }
252ae1f3df4SWarner Losh 
253ae1f3df4SWarner Losh /*
254ae1f3df4SWarner Losh  * CRC generator table -- taken from AN937 DOW CRC LOOKUP FUNCTION Table 2
255ae1f3df4SWarner Losh  */
256ae1f3df4SWarner Losh const uint8_t ow_crc_table[] = {
257ae1f3df4SWarner Losh 	0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65,
258ae1f3df4SWarner Losh 	157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220,
259ae1f3df4SWarner Losh 	35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98,
260ae1f3df4SWarner Losh 	190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255,
261ae1f3df4SWarner Losh 	70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7,
262ae1f3df4SWarner Losh 	219, 133,103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154,
263ae1f3df4SWarner Losh 	101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36,
264ae1f3df4SWarner Losh 	248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185,
265ae1f3df4SWarner Losh 	140,210, 48, 110, 237, 179, 81, 15, 78, 16, 242,  172, 47, 113,147, 205,
266ae1f3df4SWarner Losh 	17, 79, 173, 243, 112, 46, 204, 146, 211,141, 111, 49, 178, 236, 14, 80,
267ae1f3df4SWarner Losh 	175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82,176, 238,
268ae1f3df4SWarner Losh 	50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115,
269ae1f3df4SWarner Losh 	202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139,
270ae1f3df4SWarner Losh 	87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22,
271ae1f3df4SWarner Losh 	233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168,
272ae1f3df4SWarner Losh 	116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53
273ae1f3df4SWarner Losh };
274ae1f3df4SWarner Losh 
275ae1f3df4SWarner Losh /*
276ae1f3df4SWarner Losh  * Converted from DO_CRC page 131 ANN937
277ae1f3df4SWarner Losh  */
278ae1f3df4SWarner Losh static uint8_t
279ae1f3df4SWarner Losh ow_crc(device_t ndev, device_t pdev, uint8_t *buffer, size_t len)
280ae1f3df4SWarner Losh {
281ae1f3df4SWarner Losh 	uint8_t crc = 0;
282ae1f3df4SWarner Losh 	int i;
283ae1f3df4SWarner Losh 
284ae1f3df4SWarner Losh 	for (i = 0; i < len; i++)
285ae1f3df4SWarner Losh 		crc = ow_crc_table[crc ^ buffer[i]];
286ae1f3df4SWarner Losh 	return crc;
287ae1f3df4SWarner Losh }
288ae1f3df4SWarner Losh 
289ae1f3df4SWarner Losh static int
290ae1f3df4SWarner Losh ow_check_crc(romid_t romid)
291ae1f3df4SWarner Losh {
292ae1f3df4SWarner Losh 	return ow_crc(NULL, NULL, (uint8_t *)&romid, sizeof(romid)) == 0;
293ae1f3df4SWarner Losh }
294ae1f3df4SWarner Losh 
295ae1f3df4SWarner Losh static int
296ae1f3df4SWarner Losh ow_device_found(device_t dev, romid_t romid)
297ae1f3df4SWarner Losh {
298ae1f3df4SWarner Losh 
299ae1f3df4SWarner Losh 	/* XXX Move this up into enumerate? */
300ae1f3df4SWarner Losh 	/*
301ae1f3df4SWarner Losh 	 * All valid ROM IDs have a valid CRC. Check that first.
302ae1f3df4SWarner Losh 	 */
303ae1f3df4SWarner Losh 	if (!ow_check_crc(romid)) {
304ae1f3df4SWarner Losh 		device_printf(dev, "Device romid %8D failed CRC.\n",
305ae1f3df4SWarner Losh 		    &romid, ":");
306ae1f3df4SWarner Losh 		return EINVAL;
307ae1f3df4SWarner Losh 	}
308ae1f3df4SWarner Losh 
309ae1f3df4SWarner Losh 	/*
310ae1f3df4SWarner Losh 	 * If we've seen this child before, don't add a new one for it.
311ae1f3df4SWarner Losh 	 */
312ae1f3df4SWarner Losh 	if (ow_child_by_romid(dev, romid) != NULL)
313ae1f3df4SWarner Losh 		return 0;
314ae1f3df4SWarner Losh 
315ae1f3df4SWarner Losh 	return ow_add_child(dev, romid);
316ae1f3df4SWarner Losh }
317ae1f3df4SWarner Losh 
318ae1f3df4SWarner Losh static int
319ae1f3df4SWarner Losh ow_enumerate(device_t dev, ow_enum_fn *enumfp, ow_found_fn *foundfp)
320ae1f3df4SWarner Losh {
321ae1f3df4SWarner Losh 	device_t lldev = device_get_parent(dev);
322ae1f3df4SWarner Losh 	int first, second, i, dir, prior, last, err, retries;
323ae1f3df4SWarner Losh 	uint64_t probed, last_mask;
324ae1f3df4SWarner Losh 	int sanity = 10;
325ae1f3df4SWarner Losh 
326ae1f3df4SWarner Losh 	prior = -1;
327ae1f3df4SWarner Losh 	last_mask = 0;
328ae1f3df4SWarner Losh 	retries = 0;
329ae1f3df4SWarner Losh 	last = -2;
330ae1f3df4SWarner Losh 	err = ow_acquire_bus(dev, dev, OWN_DONTWAIT);
331ae1f3df4SWarner Losh 	if (err != 0)
332ae1f3df4SWarner Losh 		return err;
333ae1f3df4SWarner Losh 	while (last != -1) {
334ae1f3df4SWarner Losh 		if (sanity-- < 0) {
335ae1f3df4SWarner Losh 			printf("Reached the sanity limit\n");
336ae1f3df4SWarner Losh 			return EIO;
337ae1f3df4SWarner Losh 		}
338ae1f3df4SWarner Losh again:
339ae1f3df4SWarner Losh 		probed = 0;
340ae1f3df4SWarner Losh 		last = -1;
341ae1f3df4SWarner Losh 
342ae1f3df4SWarner Losh 		/*
343ae1f3df4SWarner Losh 		 * See AN397 section 5.II.C.3 for the algorithm (though a bit
344ae1f3df4SWarner Losh 		 * poorly stated). The search command forces each device to
345ae1f3df4SWarner Losh 		 * send ROM ID bits one at a time (first the bit, then the
346ae1f3df4SWarner Losh 		 * complement) the the master (us) sends back a bit. If the
347ae1f3df4SWarner Losh 		 * device's bit doesn't match what we send back, that device
348ae1f3df4SWarner Losh 		 * stops sending bits back. So each time through we remember
349ae1f3df4SWarner Losh 		 * where we made the last decision (always 0). If there's a
350ae1f3df4SWarner Losh 		 * conflict there this time (and there will be in the absence
351ae1f3df4SWarner Losh 		 * of a hardware failure) we go with 1. This way, we prune the
352ae1f3df4SWarner Losh 		 * devices on the bus and wind up with a unique ROM. We know
353ae1f3df4SWarner Losh 		 * we're done when we detect no new conflicts. The same
354ae1f3df4SWarner Losh 		 * algorithm is used for devices in alarm state as well.
355ae1f3df4SWarner Losh 		 *
356ae1f3df4SWarner Losh 		 * In addition, experience has shown that sometimes devices
357ae1f3df4SWarner Losh 		 * stop responding in the middle of enumeration, so try this
358ae1f3df4SWarner Losh 		 * step again a few times when that happens. It is unclear if
359ae1f3df4SWarner Losh 		 * this is due to a nosiy electrical environment or some odd
360ae1f3df4SWarner Losh 		 * timing issue.
361ae1f3df4SWarner Losh 		 */
362ae1f3df4SWarner Losh 
363ae1f3df4SWarner Losh 		/*
364ae1f3df4SWarner Losh 		 * The enumeration command should be successfully sent, if not,
365ae1f3df4SWarner Losh 		 * we have big issues on the bus so punt. Lower layers report
366ae1f3df4SWarner Losh 		 * any unusual errors, so we don't need to here.
367ae1f3df4SWarner Losh 		 */
368ae1f3df4SWarner Losh 		err = enumfp(dev, dev);
369ae1f3df4SWarner Losh 		if (err != 0)
370ae1f3df4SWarner Losh 			return (err);
371ae1f3df4SWarner Losh 
372ae1f3df4SWarner Losh 		for (i = 0; i < 64; i++) {
373ae1f3df4SWarner Losh 			OWLL_READ_DATA(lldev, &timing_regular, &first);
374ae1f3df4SWarner Losh 			OWLL_READ_DATA(lldev, &timing_regular, &second);
375ae1f3df4SWarner Losh 			switch (first | second << 1) {
376ae1f3df4SWarner Losh 			case 0: /* Conflict */
377ae1f3df4SWarner Losh 				if (i < prior)
378ae1f3df4SWarner Losh 					dir = (last_mask >> i) & 1;
379ae1f3df4SWarner Losh 				else
380ae1f3df4SWarner Losh 					dir = i == prior;
381ae1f3df4SWarner Losh 
382ae1f3df4SWarner Losh 				if (dir == 0)
383ae1f3df4SWarner Losh 					last = i;
384ae1f3df4SWarner Losh 				break;
385ae1f3df4SWarner Losh 			case 1: /* 1 then 0 -> 1 for all */
386ae1f3df4SWarner Losh 				dir = 1;
387ae1f3df4SWarner Losh 				break;
388ae1f3df4SWarner Losh 			case 2: /* 0 then 1 -> 0 for all */
389ae1f3df4SWarner Losh 				dir = 0;
390ae1f3df4SWarner Losh 				break;
391ae1f3df4SWarner Losh 			case 3:
392ae1f3df4SWarner Losh 				/*
393ae1f3df4SWarner Losh 				 * No device responded. This is unexpected, but
394ae1f3df4SWarner Losh 				 * experience has shown that on some platforms
395ae1f3df4SWarner Losh 				 * we miss a timing window, or otherwise have
396ae1f3df4SWarner Losh 				 * an issue. Start this step over. Since we've
397ae1f3df4SWarner Losh 				 * not updated prior yet, we can just jump to
398ae1f3df4SWarner Losh 				 * the top of the loop for a re-do of this step.
399ae1f3df4SWarner Losh 				 */
400ae1f3df4SWarner Losh 				printf("oops, starting over\n");
401ae1f3df4SWarner Losh 				if (++retries > 5)
402ae1f3df4SWarner Losh 					return (EIO);
403ae1f3df4SWarner Losh 				goto again;
404*63aa6c0cSPedro F. Giffuni 			default: /* NOTREACHED */
405*63aa6c0cSPedro F. Giffuni 				__unreachable();
406ae1f3df4SWarner Losh 			}
407ae1f3df4SWarner Losh 			if (dir) {
408ae1f3df4SWarner Losh 				OWLL_WRITE_ONE(lldev, &timing_regular);
409ae1f3df4SWarner Losh 				probed |= 1ull << i;
410ae1f3df4SWarner Losh 			} else {
411ae1f3df4SWarner Losh 				OWLL_WRITE_ZERO(lldev, &timing_regular);
412ae1f3df4SWarner Losh 			}
413ae1f3df4SWarner Losh 		}
414ae1f3df4SWarner Losh 		retries = 0;
415ae1f3df4SWarner Losh 		foundfp(dev, probed);
416ae1f3df4SWarner Losh 		last_mask = probed;
417ae1f3df4SWarner Losh 		prior = last;
41874b8d63dSPedro F. Giffuni 	}
419ae1f3df4SWarner Losh 	ow_release_bus(dev, dev);
420ae1f3df4SWarner Losh 
421ae1f3df4SWarner Losh 	return (0);
422ae1f3df4SWarner Losh }
423ae1f3df4SWarner Losh 
424ae1f3df4SWarner Losh static int
425ae1f3df4SWarner Losh ow_probe(device_t dev)
426ae1f3df4SWarner Losh {
427ae1f3df4SWarner Losh 
428ae1f3df4SWarner Losh 	device_set_desc(dev, "1 Wire Bus");
429ae1f3df4SWarner Losh 	return (BUS_PROBE_GENERIC);
430ae1f3df4SWarner Losh }
431ae1f3df4SWarner Losh 
432ae1f3df4SWarner Losh static int
433ae1f3df4SWarner Losh ow_attach(device_t ndev)
434ae1f3df4SWarner Losh {
435ae1f3df4SWarner Losh 	struct ow_softc *sc;
436ae1f3df4SWarner Losh 
437ae1f3df4SWarner Losh 	/*
438ae1f3df4SWarner Losh 	 * Find all the devices on the bus. We don't probe / attach them in the
439ae1f3df4SWarner Losh 	 * enumeration phase. We do this because we want to allow the probe /
440ae1f3df4SWarner Losh 	 * attach routines of the child drivers to have as full an access to the
441ae1f3df4SWarner Losh 	 * bus as possible. While we reset things before the next step of the
442ae1f3df4SWarner Losh 	 * search (so it would likely be OK to allow access by the clients to
443ae1f3df4SWarner Losh 	 * the bus), it is more conservative to find them all, then to do the
444ae1f3df4SWarner Losh 	 * attach of the devices. This also allows the child devices to have
445ae1f3df4SWarner Losh 	 * more knowledge of the bus. We also ignore errors from the enumeration
446ae1f3df4SWarner Losh 	 * because they might happen after we've found a few devices.
447ae1f3df4SWarner Losh 	 */
448ae1f3df4SWarner Losh 	sc = device_get_softc(ndev);
449ae1f3df4SWarner Losh 	sc->dev = ndev;
450ae1f3df4SWarner Losh 	mtx_init(&sc->mtx, device_get_nameunit(sc->dev), "ow", MTX_DEF);
451ae1f3df4SWarner Losh 	ow_enumerate(ndev, ow_search_rom, ow_device_found);
452ae1f3df4SWarner Losh 	return bus_generic_attach(ndev);
453ae1f3df4SWarner Losh }
454ae1f3df4SWarner Losh 
455ae1f3df4SWarner Losh static int
456ae1f3df4SWarner Losh ow_detach(device_t ndev)
457ae1f3df4SWarner Losh {
458ae1f3df4SWarner Losh 	device_t *children, child;
459ae1f3df4SWarner Losh 	int nkid, i;
460ae1f3df4SWarner Losh 	struct ow_devinfo *di;
461ae1f3df4SWarner Losh 	struct ow_softc *sc;
462ae1f3df4SWarner Losh 
463ae1f3df4SWarner Losh 	sc = device_get_softc(ndev);
464ae1f3df4SWarner Losh 	/*
465ae1f3df4SWarner Losh 	 * detach all the children first. This is blocking until any threads
466ae1f3df4SWarner Losh 	 * have stopped, etc.
467ae1f3df4SWarner Losh 	 */
468ae1f3df4SWarner Losh 	bus_generic_detach(ndev);
469ae1f3df4SWarner Losh 
470ae1f3df4SWarner Losh 	/*
471ae1f3df4SWarner Losh 	 * We delete all the children, and free up the ivars
472ae1f3df4SWarner Losh 	 */
473ae1f3df4SWarner Losh 	if (device_get_children(ndev, &children, &nkid) != 0)
474ae1f3df4SWarner Losh 		return ENOMEM;
475ae1f3df4SWarner Losh 	for (i = 0; i < nkid; i++) {
476ae1f3df4SWarner Losh 		child = children[i];
477ae1f3df4SWarner Losh 		di = device_get_ivars(child);
478ae1f3df4SWarner Losh 		free(di, M_OW);
479ae1f3df4SWarner Losh 		device_delete_child(ndev, child);
480ae1f3df4SWarner Losh 	}
481ae1f3df4SWarner Losh 	free(children, M_TEMP);
482ae1f3df4SWarner Losh 
483ae1f3df4SWarner Losh 	OW_LOCK_DESTROY(sc);
484ae1f3df4SWarner Losh 	return 0;
485ae1f3df4SWarner Losh }
486ae1f3df4SWarner Losh 
487ae1f3df4SWarner Losh /*
488ae1f3df4SWarner Losh  * Not sure this is really needed. I'm having trouble figuring out what
489ae1f3df4SWarner Losh  * location means in the context of the one wire bus.
490ae1f3df4SWarner Losh  */
491ae1f3df4SWarner Losh static int
492ae1f3df4SWarner Losh ow_child_location_str(device_t dev, device_t child, char *buf,
493ae1f3df4SWarner Losh     size_t buflen)
494ae1f3df4SWarner Losh {
495ae1f3df4SWarner Losh 
496d63180f2SWarner Losh 	*buf = '\0';
497ae1f3df4SWarner Losh 	return (0);
498ae1f3df4SWarner Losh }
499ae1f3df4SWarner Losh 
500ae1f3df4SWarner Losh static int
501ae1f3df4SWarner Losh ow_child_pnpinfo_str(device_t dev, device_t child, char *buf,
502ae1f3df4SWarner Losh     size_t buflen)
503ae1f3df4SWarner Losh {
504ae1f3df4SWarner Losh 	struct ow_devinfo *di;
505ae1f3df4SWarner Losh 
506ae1f3df4SWarner Losh 	di = device_get_ivars(child);
507ae1f3df4SWarner Losh 	snprintf(buf, buflen, "romid=%8D", &di->romid, ":");
508ae1f3df4SWarner Losh 	return (0);
509ae1f3df4SWarner Losh }
510ae1f3df4SWarner Losh 
511ae1f3df4SWarner Losh static int
512ae1f3df4SWarner Losh ow_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
513ae1f3df4SWarner Losh {
514ae1f3df4SWarner Losh 	struct ow_devinfo *di;
515ae1f3df4SWarner Losh 	romid_t **ptr;
516ae1f3df4SWarner Losh 
517ae1f3df4SWarner Losh 	di = device_get_ivars(child);
518ae1f3df4SWarner Losh 	switch (which) {
519ae1f3df4SWarner Losh 	case OW_IVAR_FAMILY:
520ae1f3df4SWarner Losh 		*result = di->romid & 0xff;
521ae1f3df4SWarner Losh 		break;
522ae1f3df4SWarner Losh 	case OW_IVAR_ROMID:
523ae1f3df4SWarner Losh 		ptr = (romid_t **)result;
524ae1f3df4SWarner Losh 		*ptr = &di->romid;
525ae1f3df4SWarner Losh 		break;
526ae1f3df4SWarner Losh 	default:
527ae1f3df4SWarner Losh 		return EINVAL;
528ae1f3df4SWarner Losh 	}
529ae1f3df4SWarner Losh 
530ae1f3df4SWarner Losh 	return 0;
531ae1f3df4SWarner Losh }
532ae1f3df4SWarner Losh 
533ae1f3df4SWarner Losh static int
534ae1f3df4SWarner Losh ow_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
535ae1f3df4SWarner Losh {
536ae1f3df4SWarner Losh 
537ae1f3df4SWarner Losh 	return EINVAL;
538ae1f3df4SWarner Losh }
539ae1f3df4SWarner Losh 
540ae1f3df4SWarner Losh static int
541ae1f3df4SWarner Losh ow_print_child(device_t ndev, device_t pdev)
542ae1f3df4SWarner Losh {
543ae1f3df4SWarner Losh 	int retval = 0;
544ae1f3df4SWarner Losh 	struct ow_devinfo *di;
545ae1f3df4SWarner Losh 
546ae1f3df4SWarner Losh 	di = device_get_ivars(pdev);
547ae1f3df4SWarner Losh 
548ae1f3df4SWarner Losh 	retval += bus_print_child_header(ndev, pdev);
549ae1f3df4SWarner Losh 	retval += printf(" romid %8D", &di->romid, ":");
550ae1f3df4SWarner Losh 	retval += bus_print_child_footer(ndev, pdev);
551ae1f3df4SWarner Losh 
552ae1f3df4SWarner Losh 	return retval;
553ae1f3df4SWarner Losh }
554ae1f3df4SWarner Losh 
555ae1f3df4SWarner Losh static void
556ae1f3df4SWarner Losh ow_probe_nomatch(device_t ndev, device_t pdev)
557ae1f3df4SWarner Losh {
558ae1f3df4SWarner Losh 	struct ow_devinfo *di;
559ae1f3df4SWarner Losh 
560ae1f3df4SWarner Losh 	di = device_get_ivars(pdev);
561ae1f3df4SWarner Losh 	device_printf(ndev, "romid %8D: no driver\n", &di->romid, ":");
562ae1f3df4SWarner Losh }
563ae1f3df4SWarner Losh 
564ae1f3df4SWarner Losh static int
565ae1f3df4SWarner Losh ow_acquire_bus(device_t ndev, device_t pdev, int how)
566ae1f3df4SWarner Losh {
567ae1f3df4SWarner Losh 	struct ow_softc *sc;
568ae1f3df4SWarner Losh 
569ae1f3df4SWarner Losh 	sc = device_get_softc(ndev);
570ae1f3df4SWarner Losh 	OW_ASSERT_UNLOCKED(sc);
571ae1f3df4SWarner Losh 	OW_LOCK(sc);
572ae1f3df4SWarner Losh 	if (sc->owner != NULL) {
573ae1f3df4SWarner Losh 		if (sc->owner == pdev)
574ae1f3df4SWarner Losh 			panic("%s: %s recursively acquiring the bus.\n",
575ae1f3df4SWarner Losh 			    device_get_nameunit(ndev),
576ae1f3df4SWarner Losh 			    device_get_nameunit(pdev));
577ae1f3df4SWarner Losh 		if (how == OWN_DONTWAIT) {
578ae1f3df4SWarner Losh 			OW_UNLOCK(sc);
579ae1f3df4SWarner Losh 			return EWOULDBLOCK;
580ae1f3df4SWarner Losh 		}
581ae1f3df4SWarner Losh 		while (sc->owner != NULL)
582ae1f3df4SWarner Losh 			mtx_sleep(sc, &sc->mtx, 0, "owbuswait", 0);
583ae1f3df4SWarner Losh 	}
584ae1f3df4SWarner Losh 	sc->owner = pdev;
585ae1f3df4SWarner Losh 	OW_UNLOCK(sc);
586ae1f3df4SWarner Losh 
587ae1f3df4SWarner Losh 	return 0;
588ae1f3df4SWarner Losh }
589ae1f3df4SWarner Losh 
590ae1f3df4SWarner Losh static void
591ae1f3df4SWarner Losh ow_release_bus(device_t ndev, device_t pdev)
592ae1f3df4SWarner Losh {
593ae1f3df4SWarner Losh 	struct ow_softc *sc;
594ae1f3df4SWarner Losh 
595ae1f3df4SWarner Losh 	sc = device_get_softc(ndev);
596ae1f3df4SWarner Losh 	OW_ASSERT_UNLOCKED(sc);
597ae1f3df4SWarner Losh 	OW_LOCK(sc);
598ae1f3df4SWarner Losh 	if (sc->owner == NULL)
599ae1f3df4SWarner Losh 		panic("%s: %s releasing unowned bus.", device_get_nameunit(ndev),
600ae1f3df4SWarner Losh 		    device_get_nameunit(pdev));
601ae1f3df4SWarner Losh 	if (sc->owner != pdev)
602ae1f3df4SWarner Losh 		panic("%s: %s don't own the bus. %s does. game over.",
603ae1f3df4SWarner Losh 		    device_get_nameunit(ndev), device_get_nameunit(pdev),
604ae1f3df4SWarner Losh 		    device_get_nameunit(sc->owner));
605ae1f3df4SWarner Losh 	sc->owner = NULL;
606ae1f3df4SWarner Losh 	wakeup(sc);
607ae1f3df4SWarner Losh 	OW_UNLOCK(sc);
608ae1f3df4SWarner Losh }
609ae1f3df4SWarner Losh 
610ae1f3df4SWarner Losh devclass_t ow_devclass;
611ae1f3df4SWarner Losh 
612ae1f3df4SWarner Losh static device_method_t ow_methods[] = {
613ae1f3df4SWarner Losh 	/* Device interface */
614ae1f3df4SWarner Losh 	DEVMETHOD(device_probe,		ow_probe),
615ae1f3df4SWarner Losh 	DEVMETHOD(device_attach,	ow_attach),
616ae1f3df4SWarner Losh 	DEVMETHOD(device_detach,	ow_detach),
617ae1f3df4SWarner Losh 
618ae1f3df4SWarner Losh 	/* Bus interface */
619ae1f3df4SWarner Losh 	DEVMETHOD(bus_child_pnpinfo_str, ow_child_pnpinfo_str),
620ae1f3df4SWarner Losh 	DEVMETHOD(bus_child_location_str, ow_child_location_str),
621ae1f3df4SWarner Losh 	DEVMETHOD(bus_read_ivar,	ow_read_ivar),
622ae1f3df4SWarner Losh 	DEVMETHOD(bus_write_ivar,	ow_write_ivar),
623ae1f3df4SWarner Losh 	DEVMETHOD(bus_print_child,	ow_print_child),
624ae1f3df4SWarner Losh 	DEVMETHOD(bus_probe_nomatch,	ow_probe_nomatch),
625ae1f3df4SWarner Losh 
626ae1f3df4SWarner Losh 	/* One Wire Network/Transport layer interface */
627ae1f3df4SWarner Losh 	DEVMETHOD(own_send_command,	ow_send_command),
628ae1f3df4SWarner Losh 	DEVMETHOD(own_acquire_bus,	ow_acquire_bus),
629ae1f3df4SWarner Losh 	DEVMETHOD(own_release_bus,	ow_release_bus),
630ae1f3df4SWarner Losh 	DEVMETHOD(own_crc,		ow_crc),
631ae1f3df4SWarner Losh 	{ 0, 0 }
632ae1f3df4SWarner Losh };
633ae1f3df4SWarner Losh 
634ae1f3df4SWarner Losh static driver_t ow_driver = {
635ae1f3df4SWarner Losh 	"ow",
636ae1f3df4SWarner Losh 	ow_methods,
637ae1f3df4SWarner Losh 	sizeof(struct ow_softc),
638ae1f3df4SWarner Losh };
639ae1f3df4SWarner Losh 
640ae1f3df4SWarner Losh DRIVER_MODULE(ow, owc, ow_driver, ow_devclass, 0, 0);
641ae1f3df4SWarner Losh MODULE_VERSION(ow, 1);
642