Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/ow/owc_gpiobus_fdt.c
- This file was added.
/*- | |||||
* Copyright (c) 2015 M. Warner Losh <imp@freebsd.org> | |||||
* All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
*/ | |||||
#include <sys/cdefs.h> | |||||
__FBSDID("$FreeBSD$"); | |||||
#include <sys/param.h> | |||||
#include <sys/systm.h> | |||||
#include <sys/bus.h> | |||||
#include <sys/gpio.h> | |||||
#include <sys/kernel.h> | |||||
#include <sys/lock.h> | |||||
#include <sys/malloc.h> | |||||
#include <sys/module.h> | |||||
#include <sys/mutex.h> | |||||
#include <dev/fdt/fdt_common.h> | |||||
#include <dev/ofw/ofw_bus.h> | |||||
#include <dev/ofw/ofw_bus_subr.h> | |||||
#define FDT 1 /* Can't use this w/o fdt, sorry */ | |||||
#include <dev/gpio/gpiobusvar.h> | |||||
#include "gpiobus_if.h" | |||||
#include "owll_if.h" | |||||
#define OW_PIN 0 | |||||
#define OWC_GPIOBUS_FDT_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) | |||||
#define OWC_GPIOBUS_FDT_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) | |||||
#define OWC_GPIOBUS_FDT_LOCK_INIT(_sc) \ | |||||
mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ | |||||
"owc_gpiobus_fdt", MTX_DEF) | |||||
#define OWC_GPIOBUS_FDT_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); | |||||
struct owc_gpiobus_fdt_softc | |||||
{ | |||||
device_t sc_dev; | |||||
device_t sc_busdev; | |||||
struct mtx sc_mtx; | |||||
}; | |||||
static int owc_gpiobus_fdt_probe(device_t); | |||||
static int owc_gpiobus_fdt_attach(device_t); | |||||
static int owc_gpiobus_fdt_detach(device_t); | |||||
static void | |||||
owc_gpiobus_fdt_identify(driver_t *driver, device_t bus) | |||||
{ | |||||
phandle_t w1, root; | |||||
/* | |||||
* | |||||
*/ | |||||
/* | |||||
* Find all the 1-wire bus pseudo-nodes that are | |||||
* at the top level of the FDT. Would be nice to | |||||
* somehow preserve the node name of these busses, | |||||
* but there's no good place to put it. The driver's | |||||
* name is used for the device name, and the 1-wire | |||||
* bus overwrites the description. | |||||
*/ | |||||
root = OF_finddevice("/"); | |||||
if (root == 0) | |||||
return; | |||||
for (w1 = OF_child(root); w1 != 0; w1 = OF_peer(w1)) { | |||||
if (!fdt_is_compatible_strict(w1, "w1-gpio")) | |||||
continue; | |||||
if (!OF_hasprop(w1, "gpios")) | |||||
continue; | |||||
ofw_gpiobus_add_fdt_child(bus, driver->name, w1); | |||||
} | |||||
} | |||||
static int | |||||
owc_gpiobus_fdt_probe(device_t dev) | |||||
{ | |||||
if (!ofw_bus_status_okay(dev)) | |||||
return (ENXIO); | |||||
if (ofw_bus_is_compatible(dev, "w1-gpio")) { | |||||
device_set_desc(dev, "FDT GPIO attached one-wire bus"); | |||||
return (BUS_PROBE_DEFAULT); | |||||
} | |||||
return (ENXIO); | |||||
} | |||||
static int | |||||
owc_gpiobus_fdt_attach(device_t dev) | |||||
{ | |||||
struct owc_gpiobus_fdt_softc *sc; | |||||
device_t *kids; | |||||
int nkid; | |||||
sc = device_get_softc(dev); | |||||
sc->sc_dev = dev; | |||||
sc->sc_busdev = device_get_parent(dev); | |||||
OWC_GPIOBUS_FDT_LOCK_INIT(sc); | |||||
nkid = 0; | |||||
if (device_get_children(dev, &kids, &nkid) == 0) | |||||
free(kids, M_TEMP); | |||||
if (nkid == 0) | |||||
device_add_child(dev, "ow", -1); | |||||
free(kids, M_TEMP); | |||||
bus_generic_attach(dev); | |||||
return (0); | |||||
} | |||||
static int | |||||
owc_gpiobus_fdt_detach(device_t dev) | |||||
{ | |||||
struct owc_gpiobus_fdt_softc *sc; | |||||
sc = device_get_softc(dev); | |||||
OWC_GPIOBUS_FDT_LOCK_DESTROY(sc); | |||||
bus_generic_detach(dev); | |||||
return (0); | |||||
} | |||||
/* | |||||
* In the diagrams below, R is driven by the resitor pullup, M is driven by the | |||||
* master, and S is driven by the slave / target. | |||||
*/ | |||||
/* Should come from a table */ | |||||
#define tLOW1 1 | |||||
#define tLOW0 60 /* > 60us */ | |||||
#define tREC 1 | |||||
#define tSLOT 60 /* 60us to 120us */ | |||||
#define tLOWR 1 | |||||
#define tRDV 15 | |||||
#define tRSTL 480 | |||||
#define tRSTH 480 | |||||
#define tPDL 60 | |||||
#define tPDH 60 | |||||
/* | |||||
* These macros let what why we're doing stuff shine in the code | |||||
* below, and let the how be confined to here. | |||||
*/ | |||||
#define GETBUS(sc) GPIOBUS_ACQUIRE_BUS((sc)->sc_busdev, \ | |||||
(sc)->sc_dev, GPIOBUS_WAIT) | |||||
#define RELBUS(sc) GPIOBUS_RELEASE_BUS((sc)->sc_busdev, \ | |||||
(sc)->sc_dev) | |||||
#define OUTPIN(sc) GPIOBUS_PIN_SETFLAGS((sc)->sc_busdev, \ | |||||
(sc)->sc_dev, OW_PIN, GPIO_PIN_OUTPUT) | |||||
#define INPIN(sc) GPIOBUS_PIN_SETFLAGS((sc)->sc_busdev, \ | |||||
(sc)->sc_dev, OW_PIN, GPIO_PIN_INPUT) | |||||
#define GETPIN(sc, bit) GPIOBUS_PIN_GET((sc)->sc_busdev, \ | |||||
(sc)->sc_dev, OW_PIN, bit) | |||||
#define LOW(sc) GPIOBUS_PIN_SET((sc)->sc_busdev, \ | |||||
(sc)->sc_dev, OW_PIN, GPIO_PIN_LOW) | |||||
/* | |||||
* WRITE-ONE (see owll_if.m for timings) From Figure 4-1 AN-937 | |||||
* | |||||
* |<---------tSLOT---->|<-tREC->| | |||||
* High RRRRM | RRRRRRRRRRRR|RRRRRRRRM | |||||
* M | R | | | M | |||||
* M| R | | | M | |||||
* Low MMMMMMM | | | MMMMMM... | |||||
* |<-tLOW1->| | | | |||||
* |<------15us--->| | | |||||
* |<--------60us---->| | |||||
*/ | |||||
static int | |||||
owc_gpiobus_fdt_write_one(device_t dev) | |||||
{ | |||||
struct owc_gpiobus_fdt_softc *sc; | |||||
int error; | |||||
sc = device_get_softc(dev); | |||||
error = GETBUS(sc); | |||||
if (error != 0) | |||||
return error; | |||||
critical_enter(); | |||||
/* Force low */ | |||||
OUTPIN(sc); | |||||
LOW(sc); | |||||
DELAY(tLOW1); | |||||
/* Allow resister to float line high */ | |||||
INPIN(sc); | |||||
DELAY(tSLOT - tLOW1 + tREC); | |||||
critical_exit(); | |||||
RELBUS(sc); | |||||
return 0; | |||||
} | |||||
/* | |||||
* WRITE-ZERO (see owll_if.m for timings) From Figure 4-2 AN-937 | |||||
* | |||||
* |<---------tSLOT------>|<-tREC->| | |||||
* High RRRRM | | |RRRRRRRM | |||||
* M | | R M | |||||
* M| | | |R M | |||||
* Low MMMMMMMMMMMMMMMMMMMMMR MMMMMM... | |||||
* |<--15us->| | | | |||||
* |<------60us--->| | | |||||
* |<-------tLOW0------>| | |||||
*/ | |||||
static int | |||||
owc_gpiobus_fdt_write_zero(device_t dev) | |||||
{ | |||||
struct owc_gpiobus_fdt_softc *sc; | |||||
int error; | |||||
sc = device_get_softc(dev); | |||||
error = GETBUS(sc); | |||||
if (error != 0) | |||||
return error; | |||||
critical_enter(); | |||||
/* Force low */ | |||||
OUTPIN(sc); | |||||
LOW(sc); | |||||
DELAY(tLOW0); | |||||
/* Allow resister to float line high */ | |||||
INPIN(sc); | |||||
DELAY(tSLOT - tLOW0 + tREC); | |||||
critical_exit(); | |||||
RELBUS(sc); | |||||
return 0; | |||||
} | |||||
/* | |||||
* READ-DATA (see owll_if.m for timings) From Figure 4-3 AN-937 | |||||
* | |||||
* |<---------tSLOT------>|<-tREC->| | |||||
* High RRRRM | rrrrrrrrrrrrrrrRRRRRRRM | |||||
* M | r | R M | |||||
* M| r | |R M | |||||
* Low MMMMMMMSSSSSSSSSSSSSSR MMMMMM... | |||||
* |<tLOWR>< sample > | | |||||
* |<------tRDV---->| | | |||||
* ->| |<-tRELEASE | |||||
* | |||||
* r -- allowed to pull high via the resitor when slave writes a 1-bit | |||||
* | |||||
*/ | |||||
static int | |||||
owc_gpiobus_fdt_read_data(device_t dev, int *bit) | |||||
{ | |||||
struct owc_gpiobus_fdt_softc *sc; | |||||
int error; | |||||
sc = device_get_softc(dev); | |||||
error = GETBUS(sc); | |||||
if (error != 0) | |||||
return error; | |||||
critical_enter(); | |||||
/* Force low */ | |||||
OUTPIN(sc); | |||||
LOW(sc); | |||||
DELAY(tLOWR); | |||||
/* Allow resister to float line high or not */ | |||||
INPIN(sc); | |||||
DELAY(tRDV - tLOWR - 3); | |||||
/* Read */ | |||||
GETPIN(sc, bit); | |||||
DELAY(tSLOT); | |||||
critical_exit(); | |||||
RELBUS(sc); | |||||
return 0; | |||||
} | |||||
/* | |||||
* RESET AND PRESENCE PULSE (see owll_if.m for timings) From Figure 4-4 AN-937 | |||||
* | |||||
* |<---------tRSTH------------>| | |||||
* High RRRM | | RRRRRRRS | RRRR RRM | |||||
* M | |R| |S | R M | |||||
* M| R | | S |R M | |||||
* Low MMMMMMMM MMMMMM| | | SSSSSSSSSS MMMMMM | |||||
* |<----tRSTL--->| | |<-tPDL---->| | |||||
* | ->| |<-tR | | | |||||
* |<tPDH>| | |||||
* | |||||
* Note: for Regular Speed operations, tRSTL + tR should be less than 960us to | |||||
* avoid interferring with other devives on the bus | |||||
*/ | |||||
static int | |||||
owc_gpiobus_fdt_reset_and_presence(device_t dev, int *bit) | |||||
{ | |||||
struct owc_gpiobus_fdt_softc *sc; | |||||
int error; | |||||
int buf = -1; | |||||
sc = device_get_softc(dev); | |||||
error = GETBUS(sc); | |||||
if (error != 0) | |||||
return error; | |||||
critical_enter(); | |||||
/* Force low */ | |||||
OUTPIN(sc); | |||||
LOW(sc); | |||||
DELAY(tRSTL); | |||||
/* Allow resister to float line high and then wait for reset pulse */ | |||||
INPIN(sc); | |||||
DELAY(tPDH + 5); | |||||
/* Read presence pulse */ | |||||
GETPIN(sc, &buf); | |||||
critical_exit(); | |||||
DELAY(tRSTH - (tPDH + 5)); /* Timing not critical for this one */ | |||||
*bit = !!buf; | |||||
RELBUS(sc); | |||||
return 0; | |||||
} | |||||
static devclass_t owc_gpiobus_fdt_devclass; | |||||
static device_method_t owc_gpiobus_fdt_methods[] = { | |||||
/* Device interface */ | |||||
DEVMETHOD(device_identify, owc_gpiobus_fdt_identify), | |||||
DEVMETHOD(device_probe, owc_gpiobus_fdt_probe), | |||||
DEVMETHOD(device_attach, owc_gpiobus_fdt_attach), | |||||
DEVMETHOD(device_detach, owc_gpiobus_fdt_detach), | |||||
DEVMETHOD(owll_write_one, owc_gpiobus_fdt_write_one), | |||||
DEVMETHOD(owll_write_zero, owc_gpiobus_fdt_write_zero), | |||||
DEVMETHOD(owll_read_data, owc_gpiobus_fdt_read_data), | |||||
DEVMETHOD(owll_reset_and_presence, owc_gpiobus_fdt_reset_and_presence), | |||||
{ 0, 0 } | |||||
}; | |||||
static driver_t owc_gpiobus_fdt_driver = { | |||||
"owc", | |||||
owc_gpiobus_fdt_methods, | |||||
sizeof(struct owc_gpiobus_fdt_softc), | |||||
}; | |||||
DRIVER_MODULE(owc_gpiobus_fdt, gpiobus, owc_gpiobus_fdt_driver, owc_gpiobus_fdt_devclass, 0, 0); | |||||
MODULE_DEPEND(owc_gpiobus_fdt, ow, 1, 1, 1); |