diff options
author | imp <imp@FreeBSD.org> | 2006-07-14 22:47:07 +0000 |
---|---|---|
committer | imp <imp@FreeBSD.org> | 2006-07-14 22:47:07 +0000 |
commit | 514e7fc291e22139df0e31ff59dc547d2b16e5d5 (patch) | |
tree | e12e1547bbd9d90830bf10ea3984258d3ae53ddc /sys/dev | |
parent | 29ab994ef0c5f04e4d6ffe65f87a279d44b39488 (diff) | |
download | FreeBSD-src-514e7fc291e22139df0e31ff59dc547d2b16e5d5.zip FreeBSD-src-514e7fc291e22139df0e31ff59dc547d2b16e5d5.tar.gz |
MFp4:
Initial spibus support. Seems to be OK, but needs some polish.
# someone should write a bit-bang spi parallel port interface :-)
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/spibus/spi.h | 12 | ||||
-rw-r--r-- | sys/dev/spibus/spibus.c | 196 | ||||
-rw-r--r-- | sys/dev/spibus/spibus_if.m | 41 | ||||
-rw-r--r-- | sys/dev/spibus/spibusvar.h | 28 |
4 files changed, 277 insertions, 0 deletions
diff --git a/sys/dev/spibus/spi.h b/sys/dev/spibus/spi.h new file mode 100644 index 0000000..09806cb --- /dev/null +++ b/sys/dev/spibus/spi.h @@ -0,0 +1,12 @@ +/ * $FreeBSD$ */ + +struct spi_command { + void *tx_cmd; + uint32_t tx_cmd_sz; + void *rx_cmd; + uint32_t rx_cmd_sz; + void *tx_data; + uint32_t tx_data_sz; + void *rx_data; + uint32_t rx_data_sz; +}; diff --git a/sys/dev/spibus/spibus.c b/sys/dev/spibus/spibus.c new file mode 100644 index 0000000..6a5ab70 --- /dev/null +++ b/sys/dev/spibus/spibus.c @@ -0,0 +1,196 @@ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/kernel.h> +#include <sys/queue.h> +#include <sys/sysctl.h> +#include <sys/types.h> + +#include <sys/bus.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <dev/spibus/spibusvar.h> +#include <dev/spibus/spi.h> +#include "spibus_if.h" + +static int +spibus_probe(device_t dev) +{ + device_set_desc(dev, "spibus bus"); + return (0); +} + +static int +spibus_attach(device_t dev) +{ + struct spibus_softc *sc = SPIBUS_SOFTC(dev); + + sc->dev = dev; + bus_enumerate_hinted_children(dev); + return (bus_generic_attach(dev)); +} + +/* + * Since this is not a self-enumerating bus, and since we always add + * children in attach, we have to always delete children here. + */ +static int +spibus_detach(device_t dev) +{ + int err, ndevs, i; + device_t *devlist; + + if ((err = bus_generic_detach(dev)) != 0) + return (err); + if ((err = device_get_children(dev, &devlist, &ndevs)) != 0) + return (err); + for (i = 0; i < ndevs; i++) + device_delete_child(dev, devlist[i]); + free(devlist, M_TEMP); + + return (0); +} + +static int +spibus_suspend(device_t dev) +{ + return (bus_generic_suspend(dev)); +} + +static +int +spibus_resume(device_t dev) +{ + return (bus_generic_resume(dev)); +} + +static int +spibus_print_child(device_t dev, device_t child) +{ + struct spibus_ivar *devi = SPIBUS_IVAR(child); + int retval = 0; + + retval += bus_print_child_header(dev, child); + retval += printf(" at cs %d", devi->cs); + retval += bus_print_child_footer(dev, child); + + return (retval); +} + +static void +spibus_probe_nomatch(device_t bus, device_t child) +{ + struct spibus_ivar *devi = SPIBUS_IVAR(child); + + device_printf(bus, "<unknown card>"); + printf(" at cs %d\n", devi->cs); + return; +} + +static int +spibus_child_location_str(device_t bus, device_t child, char *buf, + size_t buflen) +{ + struct spibus_ivar *devi = SPIBUS_IVAR(child); + + snprintf(buf, buflen, "cs=%d", devi->cs); + return (0); +} + +static int +spibus_child_pnpinfo_str(device_t bus, device_t child, char *buf, + size_t buflen) +{ + *buf = '\0'; + return (0); +} + +static int +spibus_read_ivar(device_t bus, device_t child, int which, u_char *result) +{ + struct spibus_ivar *devi = SPIBUS_IVAR(child); + + switch (which) { + default: + return (EINVAL); + case SPIBUS_IVAR_CS: + *(uint32_t *)result = devi->cs; + break; + } + return (0); +} + +static device_t +spibus_add_child(device_t dev, int order, const char *name, int unit) +{ + device_t child; + struct spibus_ivar *devi; + + child = device_add_child_ordered(dev, order, name, unit); + if (child == NULL) + return (child); + devi = malloc(sizeof(struct spibus_ivar), M_DEVBUF, M_NOWAIT | M_ZERO); + if (devi != NULL) + return (0); + device_set_ivars(child, devi); + return (child); +} + +static void +spibus_hinted_child(device_t bus, const char *dname, int dunit) +{ + device_t child; + struct spibus_ivar *devi; + + child = BUS_ADD_CHILD(bus, 0, dname, dunit); + devi = SPIBUS_IVAR(child); + resource_int_value(dname, dunit, "cs", &devi->cs); +} + +static int +spibus_transfer_impl(device_t dev, device_t child, struct spi_command *cmd) +{ + return (SPIBUS_TRANSFER(dev, child, cmd)); +} + +static device_method_t spibus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, spibus_probe), + DEVMETHOD(device_attach, spibus_attach), + DEVMETHOD(device_detach, spibus_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, spibus_suspend), + DEVMETHOD(device_resume, spibus_resume), + + /* Bus interface */ + DEVMETHOD(bus_add_child, spibus_add_child), + DEVMETHOD(bus_print_child, spibus_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + DEVMETHOD(bus_probe_nomatch, spibus_probe_nomatch), + DEVMETHOD(bus_read_ivar, spibus_read_ivar), + DEVMETHOD(bus_child_pnpinfo_str, spibus_child_pnpinfo_str), + DEVMETHOD(bus_child_location_str, spibus_child_location_str), + DEVMETHOD(bus_hinted_child, spibus_hinted_child), + + /* spibus interface */ + DEVMETHOD(spibus_transfer, spibus_transfer_impl), + + { 0, 0 } +}; + +static driver_t spibus_driver = { + "spibus", + spibus_methods, + sizeof(struct spibus_softc) +}; + +devclass_t spibus_devclass; + +DRIVER_MODULE(spibus, at91_spi, spibus_driver, spibus_devclass, 0, 0); +MODULE_VERSION(spibus, 1); diff --git a/sys/dev/spibus/spibus_if.m b/sys/dev/spibus/spibus_if.m new file mode 100644 index 0000000..4e8fa11 --- /dev/null +++ b/sys/dev/spibus/spibus_if.m @@ -0,0 +1,41 @@ +#- +# Copyright (c) 2006 M. Warner Losh +# 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. +# +# $FreeBSD$ +# + +#include <sys/bus.h> +#include <dev/spibus/spi.h> + +INTERFACE spibus; + +# +# Do a spi command +# +METHOD int transfer { + device_t dev; + device_t child; + struct spi_command *cmd; +}; diff --git a/sys/dev/spibus/spibusvar.h b/sys/dev/spibus/spibusvar.h new file mode 100644 index 0000000..78baaac --- /dev/null +++ b/sys/dev/spibus/spibusvar.h @@ -0,0 +1,28 @@ +/ * $FreeBSD$ */ + +#define SPIBUS_IVAR(d) (struct spibus_ivar *) device_get_ivars(d) +#define SPIBUS_SOFTC(d) (struct spibus_softc *) device_get_softc(d) + +struct spibus_softc +{ + device_t dev; +}; + +struct spibus_ivar +{ + uint32_t cs; +}; + +enum { + SPIBUS_IVAR_CS /* chip select that we're on */ +}; + +#define SPIBUS_ACCESSOR(A, B, T) \ +__inline static int \ +spibus_get_ ## A(device_t dev, T *t) \ +{ \ + return BUS_READ_IVAR(device_get_parent(dev), dev, \ + SPIBUS_IVAR_ ## B, (uintptr_t *) t); \ +} + +SPIBUS_ACCESSOR(cs, CS, uint32_t) |