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