xref: /freebsd/sys/dev/ow/owc_gpiobus.c (revision ae1f3df4)
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, this list of conditions and the following 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 AND CONTRIBUTORS ``AS IS'' AND
15ae1f3df4SWarner Losh  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16ae1f3df4SWarner Losh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17ae1f3df4SWarner Losh  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18ae1f3df4SWarner Losh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19ae1f3df4SWarner Losh  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20ae1f3df4SWarner Losh  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21ae1f3df4SWarner Losh  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22ae1f3df4SWarner Losh  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23ae1f3df4SWarner Losh  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24ae1f3df4SWarner Losh  * SUCH DAMAGE.
25ae1f3df4SWarner Losh  */
26ae1f3df4SWarner Losh 
27ae1f3df4SWarner Losh #include <sys/cdefs.h>
28ae1f3df4SWarner Losh __FBSDID("$FreeBSD$");
29ae1f3df4SWarner Losh 
30ae1f3df4SWarner Losh #include "opt_platform.h"
31ae1f3df4SWarner Losh 
32ae1f3df4SWarner Losh #include <sys/param.h>
33ae1f3df4SWarner Losh #include <sys/systm.h>
34ae1f3df4SWarner Losh #include <sys/bus.h>
35ae1f3df4SWarner Losh #include <sys/gpio.h>
36ae1f3df4SWarner Losh #include <sys/kernel.h>
37ae1f3df4SWarner Losh #include <sys/lock.h>
38ae1f3df4SWarner Losh #include <sys/malloc.h>
39ae1f3df4SWarner Losh #include <sys/module.h>
40ae1f3df4SWarner Losh #include <sys/mutex.h>
41ae1f3df4SWarner Losh 
42ae1f3df4SWarner Losh #ifdef FDT
43ae1f3df4SWarner Losh #include <dev/fdt/fdt_common.h>
44ae1f3df4SWarner Losh #include <dev/ofw/ofw_bus.h>
45ae1f3df4SWarner Losh #include <dev/ofw/ofw_bus_subr.h>
46ae1f3df4SWarner Losh #endif
47ae1f3df4SWarner Losh 
48ae1f3df4SWarner Losh #include <dev/gpio/gpiobusvar.h>
49ae1f3df4SWarner Losh #include "gpiobus_if.h"
50ae1f3df4SWarner Losh 
51ae1f3df4SWarner Losh #include <dev/ow/owll.h>
52ae1f3df4SWarner Losh 
53ae1f3df4SWarner Losh #define	OW_PIN		0
54ae1f3df4SWarner Losh 
55ae1f3df4SWarner Losh #define OWC_GPIOBUS_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
56ae1f3df4SWarner Losh #define	OWC_GPIOBUS_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
57ae1f3df4SWarner Losh #define OWC_GPIOBUS_LOCK_INIT(_sc) \
58ae1f3df4SWarner Losh 	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \
59ae1f3df4SWarner Losh 	    "owc_gpiobus", MTX_DEF)
60ae1f3df4SWarner Losh #define OWC_GPIOBUS_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
61ae1f3df4SWarner Losh 
62ae1f3df4SWarner Losh struct owc_gpiobus_softc
63ae1f3df4SWarner Losh {
64ae1f3df4SWarner Losh 	device_t	sc_dev;
65ae1f3df4SWarner Losh 	device_t	sc_busdev;
66ae1f3df4SWarner Losh 	struct mtx	sc_mtx;
67ae1f3df4SWarner Losh };
68ae1f3df4SWarner Losh 
69ae1f3df4SWarner Losh static int owc_gpiobus_probe(device_t);
70ae1f3df4SWarner Losh static int owc_gpiobus_attach(device_t);
71ae1f3df4SWarner Losh static int owc_gpiobus_detach(device_t);
72ae1f3df4SWarner Losh 
73ae1f3df4SWarner Losh #ifdef FDT
74ae1f3df4SWarner Losh static void
75ae1f3df4SWarner Losh owc_gpiobus_identify(driver_t *driver, device_t bus)
76ae1f3df4SWarner Losh {
77ae1f3df4SWarner Losh 	phandle_t w1, root;
78ae1f3df4SWarner Losh 
79ae1f3df4SWarner Losh 	/*
80ae1f3df4SWarner Losh 	 * Find all the 1-wire bus pseudo-nodes that are
81ae1f3df4SWarner Losh 	 * at the top level of the FDT. Would be nice to
82ae1f3df4SWarner Losh 	 * somehow preserve the node name of these busses,
83ae1f3df4SWarner Losh 	 * but there's no good place to put it. The driver's
84ae1f3df4SWarner Losh 	 * name is used for the device name, and the 1-wire
85ae1f3df4SWarner Losh 	 * bus overwrites the description.
86ae1f3df4SWarner Losh 	 */
87ae1f3df4SWarner Losh 	root = OF_finddevice("/");
88ae1f3df4SWarner Losh 	if (root == 0)
89ae1f3df4SWarner Losh 		return;
90ae1f3df4SWarner Losh 	for (w1 = OF_child(root); w1 != 0; w1 = OF_peer(w1)) {
91ae1f3df4SWarner Losh 		if (!fdt_is_compatible_strict(w1, "w1-gpio"))
92ae1f3df4SWarner Losh 			continue;
93ae1f3df4SWarner Losh 		if (!OF_hasprop(w1, "gpios"))
94ae1f3df4SWarner Losh 			continue;
95ae1f3df4SWarner Losh 		ofw_gpiobus_add_fdt_child(bus, driver->name, w1);
96ae1f3df4SWarner Losh 	}
97ae1f3df4SWarner Losh }
98ae1f3df4SWarner Losh #endif
99ae1f3df4SWarner Losh 
100ae1f3df4SWarner Losh static int
101ae1f3df4SWarner Losh owc_gpiobus_probe(device_t dev)
102ae1f3df4SWarner Losh {
103ae1f3df4SWarner Losh #ifdef FDT
104ae1f3df4SWarner Losh 	if (!ofw_bus_status_okay(dev))
105ae1f3df4SWarner Losh 		return (ENXIO);
106ae1f3df4SWarner Losh 
107ae1f3df4SWarner Losh 	if (ofw_bus_is_compatible(dev, "w1-gpio")) {
108ae1f3df4SWarner Losh 		device_set_desc(dev, "FDT GPIO attached one-wire bus");
109ae1f3df4SWarner Losh 		return (BUS_PROBE_DEFAULT);
110ae1f3df4SWarner Losh 	}
111ae1f3df4SWarner Losh 
112ae1f3df4SWarner Losh 	return (ENXIO);
113ae1f3df4SWarner Losh #else
114ae1f3df4SWarner Losh 	device_set_desc(dev, "GPIO attached one-wire bus");
115ae1f3df4SWarner Losh 	return 0;
116ae1f3df4SWarner Losh #endif
117ae1f3df4SWarner Losh }
118ae1f3df4SWarner Losh 
119ae1f3df4SWarner Losh static int
120ae1f3df4SWarner Losh owc_gpiobus_attach(device_t dev)
121ae1f3df4SWarner Losh {
122ae1f3df4SWarner Losh 	struct owc_gpiobus_softc *sc;
123ae1f3df4SWarner Losh 	device_t *kids;
124ae1f3df4SWarner Losh 	int nkid;
125ae1f3df4SWarner Losh 
126ae1f3df4SWarner Losh 	sc = device_get_softc(dev);
127ae1f3df4SWarner Losh 	sc->sc_dev = dev;
128ae1f3df4SWarner Losh 	sc->sc_busdev = device_get_parent(dev);
129ae1f3df4SWarner Losh 	OWC_GPIOBUS_LOCK_INIT(sc);
130ae1f3df4SWarner Losh 	nkid = 0;
131ae1f3df4SWarner Losh 	if (device_get_children(dev, &kids, &nkid) == 0)
132ae1f3df4SWarner Losh 		free(kids, M_TEMP);
133ae1f3df4SWarner Losh 	if (nkid == 0)
134ae1f3df4SWarner Losh 		device_add_child(dev, "ow", -1);
135ae1f3df4SWarner Losh 	bus_generic_attach(dev);
136ae1f3df4SWarner Losh 
137ae1f3df4SWarner Losh 	return (0);
138ae1f3df4SWarner Losh }
139ae1f3df4SWarner Losh 
140ae1f3df4SWarner Losh static int
141ae1f3df4SWarner Losh owc_gpiobus_detach(device_t dev)
142ae1f3df4SWarner Losh {
143ae1f3df4SWarner Losh 	struct owc_gpiobus_softc *sc;
144ae1f3df4SWarner Losh 
145ae1f3df4SWarner Losh 	sc = device_get_softc(dev);
146ae1f3df4SWarner Losh 	OWC_GPIOBUS_LOCK_DESTROY(sc);
147ae1f3df4SWarner Losh 	bus_generic_detach(dev);
148ae1f3df4SWarner Losh 	return (0);
149ae1f3df4SWarner Losh }
150ae1f3df4SWarner Losh 
151ae1f3df4SWarner Losh /*
152ae1f3df4SWarner Losh  * In the diagrams below, R is driven by the resistor pullup, M is driven by the
153ae1f3df4SWarner Losh  * master, and S is driven by the slave / target.
154ae1f3df4SWarner Losh  */
155ae1f3df4SWarner Losh 
156ae1f3df4SWarner Losh /*
157ae1f3df4SWarner Losh  * These macros let what why we're doing stuff shine in the code
158ae1f3df4SWarner Losh  * below, and let the how be confined to here.
159ae1f3df4SWarner Losh  */
160ae1f3df4SWarner Losh #define GETBUS(sc)	GPIOBUS_ACQUIRE_BUS((sc)->sc_busdev,	\
161ae1f3df4SWarner Losh 			    (sc)->sc_dev, GPIOBUS_WAIT)
162ae1f3df4SWarner Losh #define RELBUS(sc)	GPIOBUS_RELEASE_BUS((sc)->sc_busdev,	\
163ae1f3df4SWarner Losh 			    (sc)->sc_dev)
164ae1f3df4SWarner Losh #define OUTPIN(sc)	GPIOBUS_PIN_SETFLAGS((sc)->sc_busdev, \
165ae1f3df4SWarner Losh 			    (sc)->sc_dev, OW_PIN, GPIO_PIN_OUTPUT)
166ae1f3df4SWarner Losh #define INPIN(sc)	GPIOBUS_PIN_SETFLAGS((sc)->sc_busdev, \
167ae1f3df4SWarner Losh 			    (sc)->sc_dev, OW_PIN, GPIO_PIN_INPUT)
168ae1f3df4SWarner Losh #define GETPIN(sc, bit) GPIOBUS_PIN_GET((sc)->sc_busdev, \
169ae1f3df4SWarner Losh 			    (sc)->sc_dev, OW_PIN, bit)
170ae1f3df4SWarner Losh #define LOW(sc)		GPIOBUS_PIN_SET((sc)->sc_busdev, \
171ae1f3df4SWarner Losh 			    (sc)->sc_dev, OW_PIN, GPIO_PIN_LOW)
172ae1f3df4SWarner Losh 
173ae1f3df4SWarner Losh /*
174ae1f3df4SWarner Losh  * WRITE-ONE (see owll_if.m for timings) From Figure 4-1 AN-937
175ae1f3df4SWarner Losh  *
176ae1f3df4SWarner Losh  *		       |<---------tSLOT---->|<-tREC->|
177ae1f3df4SWarner Losh  *	High	RRRRM  | 	RRRRRRRRRRRR|RRRRRRRRM
178ae1f3df4SWarner Losh  *		     M |       R |     |  |	      M
179ae1f3df4SWarner Losh  *		      M|      R	 |     |  |	       M
180ae1f3df4SWarner Losh  *	Low	       MMMMMMM	 |     |  |    	        MMMMMM...
181ae1f3df4SWarner Losh  *		       |<-tLOW1->|     |  |
182ae1f3df4SWarner Losh  *		       |<------15us--->|  |
183ae1f3df4SWarner Losh  *                     |<--------60us---->|
184ae1f3df4SWarner Losh  */
185ae1f3df4SWarner Losh static int
186ae1f3df4SWarner Losh owc_gpiobus_write_one(device_t dev, struct ow_timing *t)
187ae1f3df4SWarner Losh {
188ae1f3df4SWarner Losh 	struct owc_gpiobus_softc *sc;
189ae1f3df4SWarner Losh 	int error;
190ae1f3df4SWarner Losh 
191ae1f3df4SWarner Losh 	sc = device_get_softc(dev);
192ae1f3df4SWarner Losh 	error = GETBUS(sc);
193ae1f3df4SWarner Losh 	if (error != 0)
194ae1f3df4SWarner Losh 		return error;
195ae1f3df4SWarner Losh 
196ae1f3df4SWarner Losh 	critical_enter();
197ae1f3df4SWarner Losh 
198ae1f3df4SWarner Losh 	/* Force low */
199ae1f3df4SWarner Losh 	OUTPIN(sc);
200ae1f3df4SWarner Losh 	LOW(sc);
201ae1f3df4SWarner Losh 	DELAY(t->t_low1);
202ae1f3df4SWarner Losh 
203ae1f3df4SWarner Losh 	/* Allow resistor to float line high */
204ae1f3df4SWarner Losh 	INPIN(sc);
205ae1f3df4SWarner Losh 	DELAY(t->t_slot - t->t_low1 + t->t_rec);
206ae1f3df4SWarner Losh 
207ae1f3df4SWarner Losh 	critical_exit();
208ae1f3df4SWarner Losh 
209ae1f3df4SWarner Losh 	RELBUS(sc);
210ae1f3df4SWarner Losh 
211ae1f3df4SWarner Losh 	return 0;
212ae1f3df4SWarner Losh }
213ae1f3df4SWarner Losh 
214ae1f3df4SWarner Losh /*
215ae1f3df4SWarner Losh  * WRITE-ZERO (see owll_if.m for timings) From Figure 4-2 AN-937
216ae1f3df4SWarner Losh  *
217ae1f3df4SWarner Losh  *		       |<---------tSLOT------>|<-tREC->|
218ae1f3df4SWarner Losh  *	High	RRRRM  | 	            | |RRRRRRRM
219ae1f3df4SWarner Losh  *		     M |                    | R	       M
220ae1f3df4SWarner Losh  *		      M|       	 |     |    |R 	        M
221ae1f3df4SWarner Losh  *	Low	       MMMMMMMMMMMMMMMMMMMMMR  	         MMMMMM...
222ae1f3df4SWarner Losh  *     	       	       |<--15us->|     |    |
223ae1f3df4SWarner Losh  *     	       	       |<------60us--->|    |
224ae1f3df4SWarner Losh  *                     |<-------tLOW0------>|
225ae1f3df4SWarner Losh  */
226ae1f3df4SWarner Losh static int
227ae1f3df4SWarner Losh owc_gpiobus_write_zero(device_t dev, struct ow_timing *t)
228ae1f3df4SWarner Losh {
229ae1f3df4SWarner Losh 	struct owc_gpiobus_softc *sc;
230ae1f3df4SWarner Losh 	int error;
231ae1f3df4SWarner Losh 
232ae1f3df4SWarner Losh 	sc = device_get_softc(dev);
233ae1f3df4SWarner Losh 	error = GETBUS(sc);
234ae1f3df4SWarner Losh 	if (error != 0)
235ae1f3df4SWarner Losh 		return error;
236ae1f3df4SWarner Losh 
237ae1f3df4SWarner Losh 	critical_enter();
238ae1f3df4SWarner Losh 
239ae1f3df4SWarner Losh 	/* Force low */
240ae1f3df4SWarner Losh 	OUTPIN(sc);
241ae1f3df4SWarner Losh 	LOW(sc);
242ae1f3df4SWarner Losh 	DELAY(t->t_low0);
243ae1f3df4SWarner Losh 
244ae1f3df4SWarner Losh 	/* Allow resistor to float line high */
245ae1f3df4SWarner Losh 	INPIN(sc);
246ae1f3df4SWarner Losh 	DELAY(t->t_slot - t->t_low0 + t->t_rec);
247ae1f3df4SWarner Losh 
248ae1f3df4SWarner Losh 	critical_exit();
249ae1f3df4SWarner Losh 
250ae1f3df4SWarner Losh 	RELBUS(sc);
251ae1f3df4SWarner Losh 
252ae1f3df4SWarner Losh 	return 0;
253ae1f3df4SWarner Losh }
254ae1f3df4SWarner Losh 
255ae1f3df4SWarner Losh /*
256ae1f3df4SWarner Losh  * READ-DATA (see owll_if.m for timings) From Figure 4-3 AN-937
257ae1f3df4SWarner Losh  *
258ae1f3df4SWarner Losh  *		       |<---------tSLOT------>|<-tREC->|
259ae1f3df4SWarner Losh  *	High	RRRRM  |        rrrrrrrrrrrrrrrRRRRRRRM
260ae1f3df4SWarner Losh  *		     M |       r            | R	       M
261ae1f3df4SWarner Losh  *		      M|      r	        |   |R 	        M
262ae1f3df4SWarner Losh  *	Low	       MMMMMMMSSSSSSSSSSSSSSR  	         MMMMMM...
263ae1f3df4SWarner Losh  *     	       	       |<tLOWR>< sample	>   |
264ae1f3df4SWarner Losh  *     	       	       |<------tRDV---->|   |
265ae1f3df4SWarner Losh  *                                    ->|   |<-tRELEASE
266ae1f3df4SWarner Losh  *
267ae1f3df4SWarner Losh  * r -- allowed to pull high via the resitor when slave writes a 1-bit
268ae1f3df4SWarner Losh  *
269ae1f3df4SWarner Losh  */
270ae1f3df4SWarner Losh static int
271ae1f3df4SWarner Losh owc_gpiobus_read_data(device_t dev, struct ow_timing *t, int *bit)
272ae1f3df4SWarner Losh {
273ae1f3df4SWarner Losh 	struct owc_gpiobus_softc *sc;
274ae1f3df4SWarner Losh 	int error, sample;
275ae1f3df4SWarner Losh 	sbintime_t then, now;
276ae1f3df4SWarner Losh 
277ae1f3df4SWarner Losh 	sc = device_get_softc(dev);
278ae1f3df4SWarner Losh 	error = GETBUS(sc);
279ae1f3df4SWarner Losh 	if (error != 0)
280ae1f3df4SWarner Losh 		return error;
281ae1f3df4SWarner Losh 
282ae1f3df4SWarner Losh 	/* Force low for t_lowr microseconds */
283ae1f3df4SWarner Losh 	then = sbinuptime();
284ae1f3df4SWarner Losh 	OUTPIN(sc);
285ae1f3df4SWarner Losh 	LOW(sc);
286ae1f3df4SWarner Losh 	DELAY(t->t_lowr);
287ae1f3df4SWarner Losh 
288ae1f3df4SWarner Losh 	/*
289ae1f3df4SWarner Losh 	 * Slave is supposed to hold the line low for t_rdv microseconds for 0
290ae1f3df4SWarner Losh 	 * and immediately float it high for a 1. This is measured from the
291ae1f3df4SWarner Losh 	 * master's pushing the line low.
292ae1f3df4SWarner Losh 	 */
293ae1f3df4SWarner Losh 	INPIN(sc);
294ae1f3df4SWarner Losh 	critical_enter();
295ae1f3df4SWarner Losh 	do {
296ae1f3df4SWarner Losh 		now = sbinuptime();
297ae1f3df4SWarner Losh 		GETPIN(sc, &sample);
298ae1f3df4SWarner Losh 	} while ((now - then) / SBT_1US < t->t_rdv + 2 && sample == 0);
299ae1f3df4SWarner Losh 	critical_exit();
300ae1f3df4SWarner Losh 
301ae1f3df4SWarner Losh 	if ((now - then) / SBT_1NS < t->t_rdv * 1000)
302ae1f3df4SWarner Losh 		*bit = 1;
303ae1f3df4SWarner Losh 	else
304ae1f3df4SWarner Losh 		*bit = 0;
305ae1f3df4SWarner Losh 
306ae1f3df4SWarner Losh 	/* Wait out the rest of t_slot */
307ae1f3df4SWarner Losh 	do {
308ae1f3df4SWarner Losh 		now = sbinuptime();
309ae1f3df4SWarner Losh 	} while ((now - then) / SBT_1US < t->t_slot);
310ae1f3df4SWarner Losh 
311ae1f3df4SWarner Losh 	RELBUS(sc);
312ae1f3df4SWarner Losh 
313ae1f3df4SWarner Losh 	return 0;
314ae1f3df4SWarner Losh }
315ae1f3df4SWarner Losh 
316ae1f3df4SWarner Losh /*
317ae1f3df4SWarner Losh  * RESET AND PRESENCE PULSE (see owll_if.m for timings) From Figure 4-4 AN-937
318ae1f3df4SWarner Losh  *
319ae1f3df4SWarner Losh  *				    |<---------tRSTH------------>|
320ae1f3df4SWarner Losh  *	High RRRM  |		  | RRRRRRRS	       |  RRRR RRM
321ae1f3df4SWarner Losh  *		 M |		  |R|  	   |S  	       | R	  M
322ae1f3df4SWarner Losh  *		  M|		  R |	   | S	       |R	   M
323ae1f3df4SWarner Losh  *	Low	   MMMMMMMM MMMMMM| |	   |  SSSSSSSSSS	    MMMMMM
324ae1f3df4SWarner Losh  *     	       	   |<----tRSTL--->| |  	   |<-tPDL---->|
325ae1f3df4SWarner Losh  *		   |   	       	->| |<-tR  |	       |
326ae1f3df4SWarner Losh  *				    |<tPDH>|
327ae1f3df4SWarner Losh  *
328ae1f3df4SWarner Losh  * Note: for Regular Speed operations, tRSTL + tR should be less than 960us to
329ae1f3df4SWarner Losh  * avoid interferring with other devices on the bus
330ae1f3df4SWarner Losh  */
331ae1f3df4SWarner Losh static int
332ae1f3df4SWarner Losh owc_gpiobus_reset_and_presence(device_t dev, struct ow_timing *t, int *bit)
333ae1f3df4SWarner Losh {
334ae1f3df4SWarner Losh 	struct owc_gpiobus_softc *sc;
335ae1f3df4SWarner Losh 	int error;
336ae1f3df4SWarner Losh 	int buf = -1;
337ae1f3df4SWarner Losh 
338ae1f3df4SWarner Losh 	sc = device_get_softc(dev);
339ae1f3df4SWarner Losh 	error = GETBUS(sc);
340ae1f3df4SWarner Losh 	if (error != 0)
341ae1f3df4SWarner Losh 		return error;
342ae1f3df4SWarner Losh 
343ae1f3df4SWarner Losh 
344ae1f3df4SWarner Losh 	/*
345ae1f3df4SWarner Losh 	 * Read the current state of the bus. The steady state of an idle bus is
346ae1f3df4SWarner Losh 	 * high. Badly wired buses that are missing the required pull up, or
347ae1f3df4SWarner Losh 	 * that have a short circuit to ground cause all kinds of mischief when
348ae1f3df4SWarner Losh 	 * we try to read them later. Return EIO and release the bus if the bus
349ae1f3df4SWarner Losh 	 * is currently low.
350ae1f3df4SWarner Losh 	 */
351ae1f3df4SWarner Losh 	INPIN(sc);
352ae1f3df4SWarner Losh 	GETPIN(sc, &buf);
353ae1f3df4SWarner Losh 	if (buf == 0) {
354ae1f3df4SWarner Losh 		*bit = -1;
355ae1f3df4SWarner Losh 		RELBUS(sc);
356ae1f3df4SWarner Losh 		return EIO;
357ae1f3df4SWarner Losh 	}
358ae1f3df4SWarner Losh 
359ae1f3df4SWarner Losh 	critical_enter();
360ae1f3df4SWarner Losh 
361ae1f3df4SWarner Losh 	/* Force low */
362ae1f3df4SWarner Losh 	OUTPIN(sc);
363ae1f3df4SWarner Losh 	LOW(sc);
364ae1f3df4SWarner Losh 	DELAY(t->t_rstl);
365ae1f3df4SWarner Losh 
366ae1f3df4SWarner Losh 	/* Allow resistor to float line high and then wait for reset pulse */
367ae1f3df4SWarner Losh 	INPIN(sc);
368ae1f3df4SWarner Losh 	DELAY(t->t_pdh + t->t_pdl / 2);
369ae1f3df4SWarner Losh 
370ae1f3df4SWarner Losh 	/* Read presence pulse  */
371ae1f3df4SWarner Losh 	GETPIN(sc, &buf);
372ae1f3df4SWarner Losh 	*bit = !!buf;
373ae1f3df4SWarner Losh 
374ae1f3df4SWarner Losh 	critical_exit();
375ae1f3df4SWarner Losh 
376ae1f3df4SWarner Losh 	DELAY(t->t_rsth - (t->t_pdh + t->t_pdl / 2));	/* Timing not critical for this one */
377ae1f3df4SWarner Losh 
378ae1f3df4SWarner Losh 	/*
379ae1f3df4SWarner Losh 	 * Read the state of the bus after we've waited past the end of the rest
380ae1f3df4SWarner Losh 	 * window. It should return to high. If it is low, then we have some
381ae1f3df4SWarner Losh 	 * problem and should abort the reset.
382ae1f3df4SWarner Losh 	 */
383ae1f3df4SWarner Losh 	GETPIN(sc, &buf);
384ae1f3df4SWarner Losh 	if (buf == 0) {
385ae1f3df4SWarner Losh 		*bit = -1;
386ae1f3df4SWarner Losh 		RELBUS(sc);
387ae1f3df4SWarner Losh 		return EIO;
388ae1f3df4SWarner Losh 	}
389ae1f3df4SWarner Losh 
390ae1f3df4SWarner Losh 	RELBUS(sc);
391ae1f3df4SWarner Losh 
392ae1f3df4SWarner Losh 	return 0;
393ae1f3df4SWarner Losh }
394ae1f3df4SWarner Losh 
395ae1f3df4SWarner Losh static devclass_t owc_gpiobus_devclass;
396ae1f3df4SWarner Losh 
397ae1f3df4SWarner Losh static device_method_t owc_gpiobus_methods[] = {
398ae1f3df4SWarner Losh 	/* Device interface */
399ae1f3df4SWarner Losh #ifdef FDT
400ae1f3df4SWarner Losh 	DEVMETHOD(device_identify,	owc_gpiobus_identify),
401ae1f3df4SWarner Losh #endif
402ae1f3df4SWarner Losh 	DEVMETHOD(device_probe,		owc_gpiobus_probe),
403ae1f3df4SWarner Losh 	DEVMETHOD(device_attach,	owc_gpiobus_attach),
404ae1f3df4SWarner Losh 	DEVMETHOD(device_detach,	owc_gpiobus_detach),
405ae1f3df4SWarner Losh 
406ae1f3df4SWarner Losh 	DEVMETHOD(owll_write_one,	owc_gpiobus_write_one),
407ae1f3df4SWarner Losh 	DEVMETHOD(owll_write_zero,	owc_gpiobus_write_zero),
408ae1f3df4SWarner Losh 	DEVMETHOD(owll_read_data,	owc_gpiobus_read_data),
409ae1f3df4SWarner Losh 	DEVMETHOD(owll_reset_and_presence,	owc_gpiobus_reset_and_presence),
410ae1f3df4SWarner Losh 	{ 0, 0 }
411ae1f3df4SWarner Losh };
412ae1f3df4SWarner Losh 
413ae1f3df4SWarner Losh static driver_t owc_gpiobus_driver = {
414ae1f3df4SWarner Losh 	"owc",
415ae1f3df4SWarner Losh 	owc_gpiobus_methods,
416ae1f3df4SWarner Losh 	sizeof(struct owc_gpiobus_softc),
417ae1f3df4SWarner Losh };
418ae1f3df4SWarner Losh 
419ae1f3df4SWarner Losh DRIVER_MODULE(owc_gpiobus_fdt, gpiobus, owc_gpiobus_driver, owc_gpiobus_devclass, 0, 0);
420ae1f3df4SWarner Losh MODULE_DEPEND(owc_gpiobus_fdt, ow, 1, 1, 1);
421