diff options
author | raj <raj@FreeBSD.org> | 2008-03-03 18:20:17 +0000 |
---|---|---|
committer | raj <raj@FreeBSD.org> | 2008-03-03 18:20:17 +0000 |
commit | ddcbd7a1c933343caf6997ea48416ce7af082d83 (patch) | |
tree | 4bfcfaef6ba2a3d7ebf036eb0d0325e2431535cc /sys | |
parent | 0757a4afb5d18c5b874cc918eb56d7264456bd20 (diff) | |
download | FreeBSD-src-ddcbd7a1c933343caf6997ea48416ce7af082d83.zip FreeBSD-src-ddcbd7a1c933343caf6997ea48416ce7af082d83.tar.gz |
Support for Freescale QUad Integrated Communications Controller.
The QUICC engine is found on various Freescale parts including MPC85xx, and
provides multiple generic time-division serial channel resources, which are in
turn muxed/demuxed by the Serial Communications Controller (SCC).
Along with core QUICC/SCC functionality a uart(4)-compliant device driver is
provided which allows for serial ports over QUICC/SCC.
Approved by: cognet (mentor)
Obtained from: Juniper
MFp4: e500
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/ic/quicc.h | 111 | ||||
-rw-r--r-- | sys/dev/quicc/quicc_bfe.h | 73 | ||||
-rw-r--r-- | sys/dev/quicc/quicc_bfe_ocp.c | 94 | ||||
-rw-r--r-- | sys/dev/quicc/quicc_bus.h | 39 | ||||
-rw-r--r-- | sys/dev/quicc/quicc_core.c | 401 | ||||
-rw-r--r-- | sys/dev/scc/scc_bfe.h | 1 | ||||
-rw-r--r-- | sys/dev/scc/scc_bfe_quicc.c | 95 | ||||
-rw-r--r-- | sys/dev/scc/scc_bus.h | 1 | ||||
-rw-r--r-- | sys/dev/scc/scc_dev_quicc.c | 151 | ||||
-rw-r--r-- | sys/dev/uart/uart_bus_scc.c | 3 | ||||
-rw-r--r-- | sys/dev/uart/uart_dev_quicc.c | 487 |
11 files changed, 1456 insertions, 0 deletions
diff --git a/sys/dev/ic/quicc.h b/sys/dev/ic/quicc.h new file mode 100644 index 0000000..7db6c03 --- /dev/null +++ b/sys/dev/ic/quicc.h @@ -0,0 +1,111 @@ +/*- + * Copyright (c) 2006 Juniper Networks + * 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 ``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 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$ + */ + +#ifndef _DEV_IC_QUICC_H_ +#define _DEV_IC_QUICC_H_ + +/* + * Device parameter RAM + */ +#define QUICC_PRAM_BASE 0x8000 + +#define QUICC_PRAM_REV_NUM (QUICC_PRAM_BASE + 0xaf0) + +/* SCC parameter RAM. */ +#define QUICC_PRAM_SIZE_SCC 256 +#define QUICC_PRAM_BASE_SCC(u) (QUICC_PRAM_BASE + QUICC_PRAM_SIZE_SCC * (u)) + +/* SCC parameters that are common for all modes. */ +#define QUICC_PRAM_SCC_RBASE(u) (QUICC_PRAM_BASE_SCC(u) + 0x00) +#define QUICC_PRAM_SCC_TBASE(u) (QUICC_PRAM_BASE_SCC(u) + 0x02) +#define QUICC_PRAM_SCC_RFCR(u) (QUICC_PRAM_BASE_SCC(u) + 0x04) +#define QUICC_PRAM_SCC_TFCR(u) (QUICC_PRAM_BASE_SCC(u) + 0x05) +#define QUICC_PRAM_SCC_MRBLR(u) (QUICC_PRAM_BASE_SCC(u) + 0x06) +#define QUICC_PRAM_SCC_RBPTR(u) (QUICC_PRAM_BASE_SCC(u) + 0x10) +#define QUICC_PRAM_SCC_TBPTR(u) (QUICC_PRAM_BASE_SCC(u) + 0x20) + +/* + * SCC parameters that are specific to UART/ASYNC mode. + */ +#define QUICC_PRAM_SIZE_SCC_UART 0x68 /* Rounded up. */ + +#define QUICC_PRAM_SCC_UART_MAX_IDL(u) (QUICC_PRAM_BASE_SCC(u) + 0x38) +#define QUICC_PRAM_SCC_UART_IDLC(u) (QUICC_PRAM_BASE_SCC(u) + 0x3a) +#define QUICC_PRAM_SCC_UART_BRKCR(u) (QUICC_PRAM_BASE_SCC(u) + 0x3c) +#define QUICC_PRAM_SCC_UART_PAREC(u) (QUICC_PRAM_BASE_SCC(u) + 0x3e) +#define QUICC_PRAM_SCC_UART_FRMEC(u) (QUICC_PRAM_BASE_SCC(u) + 0x40) +#define QUICC_PRAM_SCC_UART_NOSEC(u) (QUICC_PRAM_BASE_SCC(u) + 0x42) +#define QUICC_PRAM_SCC_UART_BRKEC(u) (QUICC_PRAM_BASE_SCC(u) + 0x44) +#define QUICC_PRAM_SCC_UART_BRKLN(u) (QUICC_PRAM_BASE_SCC(u) + 0x46) +#define QUICC_PRAM_SCC_UART_UADDR1(u) (QUICC_PRAM_BASE_SCC(u) + 0x48) +#define QUICC_PRAM_SCC_UART_UADDR2(u) (QUICC_PRAM_BASE_SCC(u) + 0x4a) +#define QUICC_PRAM_SCC_UART_TOSEQ(u) (QUICC_PRAM_BASE_SCC(u) + 0x4e) +#define QUICC_PRAM_SCC_UART_CC(u,n) (QUICC_PRAM_BASE_SCC(u) + 0x50 + (n)*2) +#define QUICC_PRAM_SCC_UART_RCCM(u) (QUICC_PRAM_BASE_SCC(u) + 0x60) +#define QUICC_PRAM_SCC_UART_RCCR(u) (QUICC_PRAM_BASE_SCC(u) + 0x62) +#define QUICC_PRAM_SCC_UART_RLBC(u) (QUICC_PRAM_BASE_SCC(u) + 0x64) + +/* + * Interrupt controller. + */ +#define QUICC_REG_SICR 0x10c00 +#define QUICC_REG_SIVEC 0x10c04 +#define QUICC_REG_SIPNR_H 0x10c08 +#define QUICC_REG_SIPNR_L 0x10c0c +#define QUICC_REG_SCPRR_H 0x10c14 +#define QUICC_REG_SCPRR_L 0x10c18 +#define QUICC_REG_SIMR_H 0x10c1c +#define QUICC_REG_SIMR_L 0x10c20 +#define QUICC_REG_SIEXR 0x10c24 + +/* + * System clock control register. + */ +#define QUICC_REG_SCCR 0x10c80 + +/* + * Baudrate generator registers. + */ +#define QUICC_REG_BRG(u) (0x119f0 + ((u) & 3) * 4 - ((u) & 4) * 0x100) + +/* + * SCC registers. + */ +#define QUICC_REG_SIZE_SCC 0x20 +#define QUICC_REG_BASE_SCC(u) (0x11a00 + QUICC_REG_SIZE_SCC * (u)) + +#define QUICC_REG_SCC_GSMR_L(u) (QUICC_REG_BASE_SCC(u) + 0x00) +#define QUICC_REG_SCC_GSMR_H(u) (QUICC_REG_BASE_SCC(u) + 0x04) +#define QUICC_REG_SCC_PSMR(u) (QUICC_REG_BASE_SCC(u) + 0x08) +#define QUICC_REG_SCC_TODR(u) (QUICC_REG_BASE_SCC(u) + 0x0c) +#define QUICC_REG_SCC_DSR(u) (QUICC_REG_BASE_SCC(u) + 0x0e) +#define QUICC_REG_SCC_SCCE(u) (QUICC_REG_BASE_SCC(u) + 0x10) +#define QUICC_REG_SCC_SCCM(u) (QUICC_REG_BASE_SCC(u) + 0x14) +#define QUICC_REG_SCC_SCCS(u) (QUICC_REG_BASE_SCC(u) + 0x17) + +#endif /* _DEV_IC_QUICC_H_ */ diff --git a/sys/dev/quicc/quicc_bfe.h b/sys/dev/quicc/quicc_bfe.h new file mode 100644 index 0000000..9ba622c --- /dev/null +++ b/sys/dev/quicc/quicc_bfe.h @@ -0,0 +1,73 @@ +/*- + * Copyright 2006 by Juniper Networks. + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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$ + */ + +#ifndef _DEV_QUICC_BFE_H_ +#define _DEV_QUICC_BFE_H_ + +struct quicc_device; + +struct quicc_softc { + device_t sc_dev; + + struct resource *sc_rres; /* Register resource. */ + int sc_rrid; + int sc_rtype; /* SYS_RES_{IOPORT|MEMORY}. */ + + struct resource *sc_ires; /* Interrupt resource. */ + void *sc_icookie; + int sc_irid; + + struct rman sc_rman; + struct quicc_device *sc_device; + + u_int sc_clock; + + int sc_fastintr:1; + int sc_leaving:1; + int sc_polled:1; +}; + +extern devclass_t quicc_devclass; +extern char quicc_driver_name[]; + +int quicc_bfe_attach(device_t); +int quicc_bfe_detach(device_t); +int quicc_bfe_probe(device_t, u_int); + +struct resource *quicc_bus_alloc_resource(device_t, device_t, int, int *, + u_long, u_long, u_long, u_int); +int quicc_bus_get_resource(device_t, device_t, int, int, u_long *, u_long *); +int quicc_bus_read_ivar(device_t, device_t, int, uintptr_t *); +int quicc_bus_release_resource(device_t, device_t, int, int, struct resource *); +int quicc_bus_setup_intr(device_t, device_t, struct resource *, int, + driver_filter_t *, void (*)(void *), void *, void **); +int quicc_bus_teardown_intr(device_t, device_t, struct resource *, void *); + +#endif /* _DEV_QUICC_BFE_H_ */ diff --git a/sys/dev/quicc/quicc_bfe_ocp.c b/sys/dev/quicc/quicc_bfe_ocp.c new file mode 100644 index 0000000..e0a98e0 --- /dev/null +++ b/sys/dev/quicc/quicc_bfe_ocp.c @@ -0,0 +1,94 @@ +/*- + * Copyright (c) 2006 Juniper Networks. + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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/conf.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/rman.h> +#include <sys/tty.h> +#include <machine/bus.h> + +#include <machine/ocpbus.h> + +#include <dev/quicc/quicc_bfe.h> + +static int quicc_ocp_probe(device_t dev); + +static device_method_t quicc_ocp_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, quicc_ocp_probe), + DEVMETHOD(device_attach, quicc_bfe_attach), + DEVMETHOD(device_detach, quicc_bfe_detach), + + DEVMETHOD(bus_alloc_resource, quicc_bus_alloc_resource), + DEVMETHOD(bus_release_resource, quicc_bus_release_resource), + DEVMETHOD(bus_get_resource, quicc_bus_get_resource), + DEVMETHOD(bus_read_ivar, quicc_bus_read_ivar), + DEVMETHOD(bus_setup_intr, quicc_bus_setup_intr), + DEVMETHOD(bus_teardown_intr, quicc_bus_teardown_intr), + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + { 0, 0 } +}; + +static driver_t quicc_ocp_driver = { + quicc_driver_name, + quicc_ocp_methods, + sizeof(struct quicc_softc), +}; + +static int +quicc_ocp_probe(device_t dev) +{ + device_t parent; + uintptr_t clock, devtype; + int error; + + parent = device_get_parent(dev); + + error = BUS_READ_IVAR(parent, dev, OCPBUS_IVAR_DEVTYPE, &devtype); + if (error) + return (error); + if (devtype != OCPBUS_DEVTYPE_QUICC) + return (ENXIO); + + if (BUS_READ_IVAR(parent, dev, OCPBUS_IVAR_CLOCK, &clock)) + clock = 0; + return (quicc_bfe_probe(dev, clock)); +} + +DRIVER_MODULE(quicc, ocpbus, quicc_ocp_driver, quicc_devclass, 0, 0); diff --git a/sys/dev/quicc/quicc_bus.h b/sys/dev/quicc/quicc_bus.h new file mode 100644 index 0000000..6271ba5 --- /dev/null +++ b/sys/dev/quicc/quicc_bus.h @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 2006 Marcel Moolenaar + * 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 ``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 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$ + */ + +#ifndef _DEV_QUICC_BUS_H_ +#define _DEV_QUICC_BUS_H_ + +#define QUICC_IVAR_CLOCK 1 /* The CPM clock. */ +#define QUICC_IVAR_BRGCLK 2 /* The BRG clock affected by SCCR. */ +#define QUICC_IVAR_DEVTYPE 3 + +/* Device types. */ +#define QUICC_DEVTYPE_SCC 1 + +#endif /* _DEV_QUICC_BUS_H_ */ diff --git a/sys/dev/quicc/quicc_core.c b/sys/dev/quicc/quicc_core.c new file mode 100644 index 0000000..21cfdb3 --- /dev/null +++ b/sys/dev/quicc/quicc_core.c @@ -0,0 +1,401 @@ +/*- + * Copyright 2006 by Juniper Networks. + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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/conf.h> +#include <sys/endian.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/queue.h> +#include <sys/serial.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <dev/ic/quicc.h> + +#include <dev/quicc/quicc_bfe.h> +#include <dev/quicc/quicc_bus.h> + +#define quicc_read2(r, o) \ + bus_space_read_2((r)->r_bustag, (r)->r_bushandle, o) +#define quicc_read4(r, o) \ + bus_space_read_4((r)->r_bustag, (r)->r_bushandle, o) + +#define quicc_write2(r, o, v) \ + bus_space_write_2((r)->r_bustag, (r)->r_bushandle, o, v) +#define quicc_write4(r, o, v) \ + bus_space_write_4((r)->r_bustag, (r)->r_bushandle, o, v) + +devclass_t quicc_devclass; +char quicc_driver_name[] = "quicc"; + +MALLOC_DEFINE(M_QUICC, "QUICC", "QUICC driver"); + +struct quicc_device { + struct rman *qd_rman; + struct resource_list qd_rlist; + device_t qd_dev; + int qd_devtype; + + driver_filter_t *qd_ih; + void *qd_ih_arg; +}; + +static int +quicc_bfe_intr(void *arg) +{ + struct quicc_device *qd; + struct quicc_softc *sc = arg; + uint32_t sipnr; + + sipnr = quicc_read4(sc->sc_rres, QUICC_REG_SIPNR_L); + if (sipnr & 0x00f00000) + qd = sc->sc_device; + else + qd = NULL; + + if (qd == NULL || qd->qd_ih == NULL) { + device_printf(sc->sc_dev, "Stray interrupt %08x\n", sipnr); + return (FILTER_STRAY); + } + + return ((*qd->qd_ih)(qd->qd_ih_arg)); +} + +int +quicc_bfe_attach(device_t dev) +{ + struct quicc_device *qd; + struct quicc_softc *sc; + struct resource_list_entry *rle; + const char *sep; + u_long size, start; + int error; + + sc = device_get_softc(dev); + + /* + * Re-allocate. We expect that the softc contains the information + * collected by quicc_bfe_probe() intact. + */ + sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype, &sc->sc_rrid, + 0, ~0, 0, RF_ACTIVE); + if (sc->sc_rres == NULL) + return (ENXIO); + + start = rman_get_start(sc->sc_rres); + size = rman_get_size(sc->sc_rres); + + sc->sc_rman.rm_start = start; + sc->sc_rman.rm_end = start + size - 1; + sc->sc_rman.rm_type = RMAN_ARRAY; + sc->sc_rman.rm_descr = "QUICC resources"; + error = rman_init(&sc->sc_rman); + if (!error) + error = rman_manage_region(&sc->sc_rman, start, + start + size - 1); + if (error) { + bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, + sc->sc_rres); + return (error); + } + + /* + * Allocate interrupt resource. + */ + sc->sc_irid = 0; + sc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irid, + RF_ACTIVE | RF_SHAREABLE); + + if (sc->sc_ires != NULL) { + error = bus_setup_intr(dev, sc->sc_ires, + INTR_TYPE_TTY, quicc_bfe_intr, NULL, sc, &sc->sc_icookie); + if (error) { + error = bus_setup_intr(dev, sc->sc_ires, + INTR_TYPE_TTY | INTR_MPSAFE, NULL, + (driver_intr_t *)quicc_bfe_intr, sc, + &sc->sc_icookie); + } else + sc->sc_fastintr = 1; + if (error) { + device_printf(dev, "could not activate interrupt\n"); + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, + sc->sc_ires); + sc->sc_ires = NULL; + } + } + + if (sc->sc_ires == NULL) + sc->sc_polled = 1; + + if (bootverbose && (sc->sc_fastintr || sc->sc_polled)) { + sep = ""; + device_print_prettyname(dev); + if (sc->sc_fastintr) { + printf("%sfast interrupt", sep); + sep = ", "; + } + if (sc->sc_polled) { + printf("%spolled mode", sep); + sep = ", "; + } + printf("\n"); + } + + sc->sc_device = qd = malloc(sizeof(struct quicc_device), M_QUICC, + M_WAITOK | M_ZERO); + + qd->qd_devtype = QUICC_DEVTYPE_SCC; + qd->qd_rman = &sc->sc_rman; + resource_list_init(&qd->qd_rlist); + + resource_list_add(&qd->qd_rlist, sc->sc_rtype, 0, start, + start + size - 1, size); + + resource_list_add(&qd->qd_rlist, SYS_RES_IRQ, 0, 0xf00, 0xf00, 1); + rle = resource_list_find(&qd->qd_rlist, SYS_RES_IRQ, 0); + rle->res = sc->sc_ires; + + qd->qd_dev = device_add_child(dev, NULL, -1); + device_set_ivars(qd->qd_dev, (void *)qd); + error = device_probe_and_attach(qd->qd_dev); + + /* Enable all SCC interrupts. */ + quicc_write4(sc->sc_rres, QUICC_REG_SIMR_L, 0x00f00000); + + /* Clear all pending interrupts. */ + quicc_write4(sc->sc_rres, QUICC_REG_SIPNR_H, ~0); + quicc_write4(sc->sc_rres, QUICC_REG_SIPNR_L, ~0); + return (error); +} + +int +quicc_bfe_detach(device_t dev) +{ + struct quicc_softc *sc; + + sc = device_get_softc(dev); + + bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie); + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, sc->sc_ires); + bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres); + return (0); +} + +int +quicc_bfe_probe(device_t dev, u_int clock) +{ + struct quicc_softc *sc; + uint16_t rev; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + if (device_get_desc(dev) == NULL) + device_set_desc(dev, + "Quad integrated communications controller"); + + sc->sc_rrid = 0; + sc->sc_rtype = SYS_RES_MEMORY; + sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype, &sc->sc_rrid, + 0, ~0, 0, RF_ACTIVE); + if (sc->sc_rres == NULL) { + sc->sc_rrid = 0; + sc->sc_rtype = SYS_RES_IOPORT; + sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype, + &sc->sc_rrid, 0, ~0, 0, RF_ACTIVE); + if (sc->sc_rres == NULL) + return (ENXIO); + } + + sc->sc_clock = clock; + + /* + * Check that the microcode revision is 0x00e8, as documented + * in the MPC8555E PowerQUICC III Integrated Processor Family + * Reference Manual. + */ + rev = quicc_read2(sc->sc_rres, QUICC_PRAM_REV_NUM); + + bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres); + return ((rev == 0x00e8) ? BUS_PROBE_DEFAULT : ENXIO); +} + +struct resource * +quicc_bus_alloc_resource(device_t dev, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + struct quicc_device *qd; + struct resource_list_entry *rle; + + if (device_get_parent(child) != dev) + return (NULL); + + /* We only support default allocations. */ + if (start != 0UL || end != ~0UL) + return (NULL); + + qd = device_get_ivars(child); + rle = resource_list_find(&qd->qd_rlist, type, *rid); + if (rle == NULL) + return (NULL); + + if (rle->res == NULL) { + rle->res = rman_reserve_resource(qd->qd_rman, rle->start, + rle->start + rle->count - 1, rle->count, flags, child); + if (rle->res != NULL) { + rman_set_bustag(rle->res, &bs_be_tag); + rman_set_bushandle(rle->res, rle->start); + } + } + return (rle->res); +} + +int +quicc_bus_get_resource(device_t dev, device_t child, int type, int rid, + u_long *startp, u_long *countp) +{ + struct quicc_device *qd; + struct resource_list_entry *rle; + + if (device_get_parent(child) != dev) + return (EINVAL); + + qd = device_get_ivars(child); + rle = resource_list_find(&qd->qd_rlist, type, rid); + if (rle == NULL) + return (EINVAL); + + if (startp != NULL) + *startp = rle->start; + if (countp != NULL) + *countp = rle->count; + return (0); +} + +int +quicc_bus_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) +{ + struct quicc_device *qd; + struct quicc_softc *sc; + uint32_t sccr; + + if (device_get_parent(child) != dev) + return (EINVAL); + + sc = device_get_softc(dev); + qd = device_get_ivars(child); + + switch (index) { + case QUICC_IVAR_CLOCK: + *result = sc->sc_clock; + break; + case QUICC_IVAR_BRGCLK: + sccr = quicc_read4(sc->sc_rres, QUICC_REG_SCCR) & 3; + *result = sc->sc_clock / ((1 << (sccr + 1)) << sccr); + break; + case QUICC_IVAR_DEVTYPE: + *result = qd->qd_devtype; + break; + default: + return (EINVAL); + } + return (0); +} + +int +quicc_bus_release_resource(device_t dev, device_t child, int type, int rid, + struct resource *res) +{ + struct quicc_device *qd; + struct resource_list_entry *rle; + + if (device_get_parent(child) != dev) + return (EINVAL); + + qd = device_get_ivars(child); + rle = resource_list_find(&qd->qd_rlist, type, rid); + return ((rle == NULL) ? EINVAL : 0); +} + +int +quicc_bus_setup_intr(device_t dev, device_t child, struct resource *r, + int flags, driver_filter_t *filt, void (*ihand)(void *), void *arg, + void **cookiep) +{ + struct quicc_device *qd; + struct quicc_softc *sc; + + if (device_get_parent(child) != dev) + return (EINVAL); + + /* Interrupt handlers must be FAST or MPSAFE. */ + if (filt == NULL && !(flags & INTR_MPSAFE)) + return (EINVAL); + + sc = device_get_softc(dev); + if (sc->sc_polled) + return (ENXIO); + + if (sc->sc_fastintr && filt == NULL) { + sc->sc_fastintr = 0; + bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie); + bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_TTY | INTR_MPSAFE, + NULL, (driver_intr_t *)quicc_bfe_intr, sc, &sc->sc_icookie); + } + + qd = device_get_ivars(child); + qd->qd_ih = (filt != NULL) ? filt : (driver_filter_t *)ihand; + qd->qd_ih_arg = arg; + *cookiep = ihand; + return (0); +} + +int +quicc_bus_teardown_intr(device_t dev, device_t child, struct resource *r, + void *cookie) +{ + struct quicc_device *qd; + + if (device_get_parent(child) != dev) + return (EINVAL); + + qd = device_get_ivars(child); + if (qd->qd_ih != cookie) + return (EINVAL); + + qd->qd_ih = NULL; + qd->qd_ih_arg = NULL; + return (0); +} diff --git a/sys/dev/scc/scc_bfe.h b/sys/dev/scc/scc_bfe.h index 8209592..56d2bea0 100644 --- a/sys/dev/scc/scc_bfe.h +++ b/sys/dev/scc/scc_bfe.h @@ -110,6 +110,7 @@ struct scc_class { int cl_range; }; +extern struct scc_class scc_quicc_class; extern struct scc_class scc_sab82532_class; extern struct scc_class scc_z8530_class; diff --git a/sys/dev/scc/scc_bfe_quicc.c b/sys/dev/scc/scc_bfe_quicc.c new file mode 100644 index 0000000..4dc7024 --- /dev/null +++ b/sys/dev/scc/scc_bfe_quicc.c @@ -0,0 +1,95 @@ +/*- + * Copyright (c) 2006 Marcel Moolenaar + * 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 ``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 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$"); + +#define __RMAN_RESOURCE_VISIBLE + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/module.h> + +#include <dev/quicc/quicc_bus.h> + +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <dev/scc/scc_bfe.h> + +static int +scc_quicc_probe(device_t dev) +{ + device_t parent; + struct scc_softc *sc; + uintptr_t devtype, rclk; + int error; + + parent = device_get_parent(dev); + + error = BUS_READ_IVAR(parent, dev, QUICC_IVAR_DEVTYPE, &devtype); + if (error) + return (error); + if (devtype != QUICC_DEVTYPE_SCC) + return (ENXIO); + + device_set_desc(dev, "QUICC quad channel SCC"); + + sc = device_get_softc(dev); + sc->sc_class = &scc_quicc_class; + if (BUS_READ_IVAR(parent, dev, QUICC_IVAR_BRGCLK, &rclk)) + rclk = 0; + return (scc_bfe_probe(dev, 0, rclk, 0)); +} + +static device_method_t scc_quicc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, scc_quicc_probe), + DEVMETHOD(device_attach, scc_bfe_attach), + DEVMETHOD(device_detach, scc_bfe_detach), + + DEVMETHOD(bus_alloc_resource, scc_bus_alloc_resource), + DEVMETHOD(bus_release_resource, scc_bus_release_resource), + DEVMETHOD(bus_get_resource, scc_bus_get_resource), + DEVMETHOD(bus_read_ivar, scc_bus_read_ivar), + DEVMETHOD(bus_setup_intr, scc_bus_setup_intr), + DEVMETHOD(bus_teardown_intr, scc_bus_teardown_intr), + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + { 0, 0 } +}; + +static driver_t scc_quicc_driver = { + scc_driver_name, + scc_quicc_methods, + sizeof(struct scc_softc), +}; + +DRIVER_MODULE(scc, quicc, scc_quicc_driver, scc_devclass, 0, 0); diff --git a/sys/dev/scc/scc_bus.h b/sys/dev/scc/scc_bus.h index ed16309..1e5138a 100644 --- a/sys/dev/scc/scc_bus.h +++ b/sys/dev/scc/scc_bus.h @@ -42,6 +42,7 @@ /* Hardware class -- the SCC type. */ #define SCC_CLASS_SAB82532 0 #define SCC_CLASS_Z8530 1 +#define SCC_CLASS_QUICC 2 /* The possible modes supported by the SCC. */ #define SCC_MODE_ASYNC 0x01 diff --git a/sys/dev/scc/scc_dev_quicc.c b/sys/dev/scc/scc_dev_quicc.c new file mode 100644 index 0000000..6f337b8 --- /dev/null +++ b/sys/dev/scc/scc_dev_quicc.c @@ -0,0 +1,151 @@ +/*- + * Copyright (c) 2004-2006 Marcel Moolenaar + * 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 ``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 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$"); + +#define __RMAN_RESOURCE_VISIBLE + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/endian.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <sys/serial.h> + +#include <dev/scc/scc_bfe.h> +#include <dev/scc/scc_bus.h> + +#include <dev/ic/quicc.h> + +#include "scc_if.h" + +#define quicc_read2(bas, reg) \ + bus_space_read_2((bas)->bst, (bas)->bsh, reg) + +#define quicc_write2(bas, reg, val) \ + bus_space_write_2((bas)->bst, (bas)->bsh, reg, val) + +static int quicc_bfe_attach(struct scc_softc *, int); +static int quicc_bfe_enabled(struct scc_softc *, struct scc_chan *); +static int quicc_bfe_iclear(struct scc_softc *, struct scc_chan *); +static int quicc_bfe_ipend(struct scc_softc *); +static int quicc_bfe_probe(struct scc_softc *); + +static kobj_method_t quicc_methods[] = { + KOBJMETHOD(scc_attach, quicc_bfe_attach), + KOBJMETHOD(scc_enabled, quicc_bfe_enabled), + KOBJMETHOD(scc_iclear, quicc_bfe_iclear), + KOBJMETHOD(scc_ipend, quicc_bfe_ipend), + KOBJMETHOD(scc_probe, quicc_bfe_probe), + { 0, 0 } +}; + +struct scc_class scc_quicc_class = { + "QUICC class", + quicc_methods, + sizeof(struct scc_softc), + .cl_channels = 4, + .cl_class = SCC_CLASS_QUICC, + .cl_modes = SCC_MODE_ASYNC | SCC_MODE_BISYNC | SCC_MODE_HDLC, + .cl_range = 0, +}; + +static int +quicc_bfe_attach(struct scc_softc *sc, int reset) +{ + struct scc_bas *bas; + + bas = &sc->sc_bas; + return (0); +} + +static int +quicc_bfe_enabled(struct scc_softc *sc, struct scc_chan *ch) +{ + struct scc_bas *bas; + int unit; + uint16_t val0, val1; + + bas = &sc->sc_bas; + unit = ch->ch_nr - 1; + val0 = quicc_read2(bas, QUICC_REG_SCC_TODR(unit)); + quicc_write2(bas, QUICC_REG_SCC_TODR(unit), ~val0); + val1 = quicc_read2(bas, QUICC_REG_SCC_TODR(unit)); + quicc_write2(bas, QUICC_REG_SCC_TODR(unit), val0); + return (((val0 | val1) == 0x8000) ? 1 : 0); +} + +static int +quicc_bfe_iclear(struct scc_softc *sc, struct scc_chan *ch) +{ + + return (0); +} + +static int +quicc_bfe_ipend(struct scc_softc *sc) +{ + struct scc_bas *bas; + struct scc_chan *ch; + int c, ipend; + uint16_t scce; + + bas = &sc->sc_bas; + ipend = 0; + for (c = 0; c < 4; c++) { + ch = &sc->sc_chan[c]; + if (!ch->ch_enabled) + continue; + ch->ch_ipend = 0; + mtx_lock_spin(&sc->sc_hwmtx); + scce = quicc_read2(bas, QUICC_REG_SCC_SCCE(c)); + quicc_write2(bas, QUICC_REG_SCC_SCCE(c), ~0); + mtx_unlock_spin(&sc->sc_hwmtx); + if (scce & 0x0001) + ch->ch_ipend |= SER_INT_RXREADY; + if (scce & 0x0002) + ch->ch_ipend |= SER_INT_TXIDLE; + if (scce & 0x0004) + ch->ch_ipend |= SER_INT_OVERRUN; + if (scce & 0x0020) + ch->ch_ipend |= SER_INT_BREAK; + /* XXX SIGNALS */ + ipend |= ch->ch_ipend; + } + return (ipend); +} + +static int +quicc_bfe_probe(struct scc_softc *sc) +{ + struct scc_bas *bas; + + bas = &sc->sc_bas; + return (0); +} diff --git a/sys/dev/uart/uart_bus_scc.c b/sys/dev/uart/uart_bus_scc.c index 9fdcb8b..07808e6 100644 --- a/sys/dev/uart/uart_bus_scc.c +++ b/sys/dev/uart/uart_bus_scc.c @@ -95,6 +95,9 @@ uart_scc_probe(device_t dev) if (md != SCC_MODE_ASYNC) return (ENXIO); switch (cl) { + case SCC_CLASS_QUICC: + sc->sc_class = &uart_quicc_class; + break; case SCC_CLASS_SAB82532: sc->sc_class = &uart_sab82532_class; break; diff --git a/sys/dev/uart/uart_dev_quicc.c b/sys/dev/uart/uart_dev_quicc.c new file mode 100644 index 0000000..3a4abc6 --- /dev/null +++ b/sys/dev/uart/uart_dev_quicc.c @@ -0,0 +1,487 @@ +/*- + * Copyright (c) 2006 Juniper Networks + * 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 ``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 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/conf.h> +#include <sys/endian.h> +#include <machine/bus.h> + +#include <dev/ic/quicc.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_cpu.h> +#include <dev/uart/uart_bus.h> + +#include "uart_if.h" + +#define DEFAULT_RCLK ((266000000 * 2) / 16) + +#define quicc_read2(bas, reg) \ + bus_space_read_2((bas)->bst, (bas)->bsh, reg) +#define quicc_read4(bas, reg) \ + bus_space_read_4((bas)->bst, (bas)->bsh, reg) + +#define quicc_write2(bas, reg, val) \ + bus_space_write_2((bas)->bst, (bas)->bsh, reg, val) +#define quicc_write4(bas, reg, val) \ + bus_space_write_4((bas)->bst, (bas)->bsh, reg, val) + +static int +quicc_divisor(int rclk, int baudrate) +{ + int act_baud, divisor, error; + + if (baudrate == 0) + return (-1); + + divisor = rclk / baudrate / 16; + if (divisor > 4096) + divisor = ((divisor >> 3) - 2) | 1; + else if (divisor >= 0) + divisor = (divisor - 1) << 1; + if (divisor < 0 || divisor >= 8192) + return (-1); + act_baud = rclk / (((divisor >> 1) + 1) << ((divisor & 1) ? 8 : 4)); + + /* 10 times error in percent: */ + error = ((act_baud - baudrate) * 2000 / baudrate + 1) >> 1; + + /* 3.0% maximum error tolerance: */ + if (error < -30 || error > 30) + return (-1); + + return (divisor); +} + +static int +quicc_param(struct uart_bas *bas, int baudrate, int databits, int stopbits, + int parity) +{ + int divisor; + uint16_t psmr; + + if (baudrate > 0) { + divisor = quicc_divisor(bas->rclk, baudrate); + if (divisor == -1) + return (EINVAL); + quicc_write4(bas, QUICC_REG_BRG(bas->chan - 1), + divisor | 0x10000); + } + + psmr = 0; + switch (databits) { + case 5: psmr |= 0x0000; break; + case 6: psmr |= 0x1000; break; + case 7: psmr |= 0x2000; break; + case 8: psmr |= 0x3000; break; + default: return (EINVAL); + } + switch (stopbits) { + case 1: psmr |= 0x0000; break; + case 2: psmr |= 0x4000; break; + default: return (EINVAL); + } + switch (parity) { + case UART_PARITY_EVEN: psmr |= 0x1a; break; + case UART_PARITY_MARK: psmr |= 0x1f; break; + case UART_PARITY_NONE: psmr |= 0x00; break; + case UART_PARITY_ODD: psmr |= 0x10; break; + case UART_PARITY_SPACE: psmr |= 0x15; break; + default: return (EINVAL); + } + quicc_write2(bas, QUICC_REG_SCC_PSMR(bas->chan - 1), psmr); + return (0); +} + +static void +quicc_setup(struct uart_bas *bas, int baudrate, int databits, int stopbits, + int parity) +{ + + if (bas->rclk == 0) + bas->rclk = DEFAULT_RCLK; + + /* + * GSMR_L = 0x00028034 + * GSMR_H = 0x00000020 + */ + quicc_param(bas, baudrate, databits, stopbits, parity); + + quicc_write2(bas, QUICC_REG_SCC_SCCE(bas->chan - 1), ~0); + quicc_write2(bas, QUICC_REG_SCC_SCCM(bas->chan - 1), 0x0027); +} + +/* + * Low-level UART interface. + */ +static int quicc_probe(struct uart_bas *bas); +static void quicc_init(struct uart_bas *bas, int, int, int, int); +static void quicc_term(struct uart_bas *bas); +static void quicc_putc(struct uart_bas *bas, int); +static int quicc_rxready(struct uart_bas *bas); +static int quicc_getc(struct uart_bas *bas, struct mtx *); + +static struct uart_ops uart_quicc_ops = { + .probe = quicc_probe, + .init = quicc_init, + .term = quicc_term, + .putc = quicc_putc, + .rxready = quicc_rxready, + .getc = quicc_getc, +}; + +static int +quicc_probe(struct uart_bas *bas) +{ + + return (0); +} + +static void +quicc_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, + int parity) +{ + + quicc_setup(bas, baudrate, databits, stopbits, parity); +} + +static void +quicc_term(struct uart_bas *bas) +{ +} + +static void +quicc_putc(struct uart_bas *bas, int c) +{ + int unit; + uint16_t toseq; + + unit = bas->chan - 1; + while (quicc_read2(bas, QUICC_PRAM_SCC_UART_TOSEQ(unit)) & 0x2000) + DELAY(10); + + toseq = 0x2000 | (c & 0xff); + quicc_write2(bas, QUICC_PRAM_SCC_UART_TOSEQ(unit), toseq); +} + +static int +quicc_rxready(struct uart_bas *bas) +{ + uint16_t rb; + + rb = quicc_read2(bas, QUICC_PRAM_SCC_RBASE(bas->chan - 1)); + return ((quicc_read2(bas, rb) & 0x8000) ? 0 : 1); +} + +static int +quicc_getc(struct uart_bas *bas, struct mtx *hwmtx) +{ + volatile char *buf; + int c; + uint16_t rb, sc; + + uart_lock(hwmtx); + + rb = quicc_read2(bas, QUICC_PRAM_SCC_RBASE(bas->chan - 1)); + + while ((sc = quicc_read2(bas, rb)) & 0x8000) { + uart_unlock(hwmtx); + DELAY(4); + uart_lock(hwmtx); + } + + buf = (void *)quicc_read4(bas, rb + 4); + c = *buf; + quicc_write2(bas, rb, sc | 0x8000); + + uart_unlock(hwmtx); + + return (c); +} + +/* + * High-level UART interface. + */ +struct quicc_softc { + struct uart_softc base; +}; + +static int quicc_bus_attach(struct uart_softc *); +static int quicc_bus_detach(struct uart_softc *); +static int quicc_bus_flush(struct uart_softc *, int); +static int quicc_bus_getsig(struct uart_softc *); +static int quicc_bus_ioctl(struct uart_softc *, int, intptr_t); +static int quicc_bus_ipend(struct uart_softc *); +static int quicc_bus_param(struct uart_softc *, int, int, int, int); +static int quicc_bus_probe(struct uart_softc *); +static int quicc_bus_receive(struct uart_softc *); +static int quicc_bus_setsig(struct uart_softc *, int); +static int quicc_bus_transmit(struct uart_softc *); + +static kobj_method_t quicc_methods[] = { + KOBJMETHOD(uart_attach, quicc_bus_attach), + KOBJMETHOD(uart_detach, quicc_bus_detach), + KOBJMETHOD(uart_flush, quicc_bus_flush), + KOBJMETHOD(uart_getsig, quicc_bus_getsig), + KOBJMETHOD(uart_ioctl, quicc_bus_ioctl), + KOBJMETHOD(uart_ipend, quicc_bus_ipend), + KOBJMETHOD(uart_param, quicc_bus_param), + KOBJMETHOD(uart_probe, quicc_bus_probe), + KOBJMETHOD(uart_receive, quicc_bus_receive), + KOBJMETHOD(uart_setsig, quicc_bus_setsig), + KOBJMETHOD(uart_transmit, quicc_bus_transmit), + { 0, 0 } +}; + +struct uart_class uart_quicc_class = { + "quicc", + quicc_methods, + sizeof(struct quicc_softc), + .uc_ops = &uart_quicc_ops, + .uc_range = 2, + .uc_rclk = DEFAULT_RCLK +}; + +#define SIGCHG(c, i, s, d) \ + if (c) { \ + i |= (i & s) ? s : s | d; \ + } else { \ + i = (i & s) ? (i & ~s) | d : i; \ + } + +static int +quicc_bus_attach(struct uart_softc *sc) +{ + struct uart_bas *bas; + struct uart_devinfo *di; + uint16_t st, rb; + + bas = &sc->sc_bas; + if (sc->sc_sysdev != NULL) { + di = sc->sc_sysdev; + quicc_param(bas, di->baudrate, di->databits, di->stopbits, + di->parity); + } else { + quicc_setup(bas, 9600, 8, 1, UART_PARITY_NONE); + } + + sc->sc_rxfifosz = 1; + sc->sc_txfifosz = 1; + + /* Enable interrupts on the receive buffer. */ + rb = quicc_read2(bas, QUICC_PRAM_SCC_RBASE(bas->chan - 1)); + st = quicc_read2(bas, rb); + quicc_write2(bas, rb, st | 0x9000); + + (void)quicc_bus_getsig(sc); + + return (0); +} + +static int +quicc_bus_detach(struct uart_softc *sc) +{ + + return (0); +} + +static int +quicc_bus_flush(struct uart_softc *sc, int what) +{ + + return (0); +} + +static int +quicc_bus_getsig(struct uart_softc *sc) +{ + uint32_t new, old, sig; + uint32_t dummy; + + do { + old = sc->sc_hwsig; + sig = old; + uart_lock(sc->sc_hwmtx); + /* XXX SIGNALS */ + dummy = 0; + uart_unlock(sc->sc_hwmtx); + SIGCHG(dummy, sig, SER_CTS, SER_DCTS); + SIGCHG(dummy, sig, SER_DCD, SER_DDCD); + SIGCHG(dummy, sig, SER_DSR, SER_DDSR); + new = sig & ~SER_MASK_DELTA; + } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); + return (sig); +} + +static int +quicc_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) +{ + struct uart_bas *bas; + uint32_t brg; + int baudrate, error; + + bas = &sc->sc_bas; + error = 0; + uart_lock(sc->sc_hwmtx); + switch (request) { + case UART_IOCTL_BREAK: + break; + case UART_IOCTL_BAUD: + brg = quicc_read4(bas, QUICC_REG_BRG(bas->chan - 1)) & 0x1fff; + brg = (brg & 1) ? (brg + 1) << 3 : (brg + 2) >> 1; + baudrate = bas->rclk / (brg * 16); + *(int*)data = baudrate; + break; + default: + error = EINVAL; + break; + } + uart_unlock(sc->sc_hwmtx); + return (error); +} + +static int +quicc_bus_ipend(struct uart_softc *sc) +{ + struct uart_bas *bas; + int ipend; + uint16_t scce; + + bas = &sc->sc_bas; + ipend = 0; + + uart_lock(sc->sc_hwmtx); + scce = quicc_read2(bas, QUICC_REG_SCC_SCCE(bas->chan - 1)); + quicc_write2(bas, QUICC_REG_SCC_SCCE(bas->chan - 1), ~0); + uart_unlock(sc->sc_hwmtx); + if (scce & 0x0001) + ipend |= SER_INT_RXREADY; + if (scce & 0x0002) + ipend |= SER_INT_TXIDLE; + if (scce & 0x0004) + ipend |= SER_INT_OVERRUN; + if (scce & 0x0020) + ipend |= SER_INT_BREAK; + /* XXX SIGNALS */ + return (ipend); +} + +static int +quicc_bus_param(struct uart_softc *sc, int baudrate, int databits, + int stopbits, int parity) +{ + int error; + + uart_lock(sc->sc_hwmtx); + error = quicc_param(&sc->sc_bas, baudrate, databits, stopbits, + parity); + uart_unlock(sc->sc_hwmtx); + return (error); +} + +static int +quicc_bus_probe(struct uart_softc *sc) +{ + char buf[80]; + int error; + + error = quicc_probe(&sc->sc_bas); + if (error) + return (error); + + snprintf(buf, sizeof(buf), "quicc, channel %d", sc->sc_bas.chan); + device_set_desc_copy(sc->sc_dev, buf); + return (0); +} + +static int +quicc_bus_receive(struct uart_softc *sc) +{ + struct uart_bas *bas; + volatile char *buf; + uint16_t st, rb; + + bas = &sc->sc_bas; + uart_lock(sc->sc_hwmtx); + rb = quicc_read2(bas, QUICC_PRAM_SCC_RBASE(bas->chan - 1)); + st = quicc_read2(bas, rb); + buf = (void *)quicc_read4(bas, rb + 4); + uart_rx_put(sc, *buf); + quicc_write2(bas, rb, st | 0x9000); + uart_unlock(sc->sc_hwmtx); + return (0); +} + +static int +quicc_bus_setsig(struct uart_softc *sc, int sig) +{ + struct uart_bas *bas; + uint32_t new, old; + + bas = &sc->sc_bas; + do { + old = sc->sc_hwsig; + new = old; + if (sig & SER_DDTR) { + SIGCHG(sig & SER_DTR, new, SER_DTR, + SER_DDTR); + } + if (sig & SER_DRTS) { + SIGCHG(sig & SER_RTS, new, SER_RTS, + SER_DRTS); + } + } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); + + uart_lock(sc->sc_hwmtx); + /* XXX SIGNALS */ + uart_unlock(sc->sc_hwmtx); + return (0); +} + +static int +quicc_bus_transmit(struct uart_softc *sc) +{ + volatile char *buf; + struct uart_bas *bas; + uint16_t st, tb; + + bas = &sc->sc_bas; + uart_lock(sc->sc_hwmtx); + tb = quicc_read2(bas, QUICC_PRAM_SCC_TBASE(bas->chan - 1)); + st = quicc_read2(bas, tb); + buf = (void *)quicc_read4(bas, tb + 4); + *buf = sc->sc_txbuf[0]; + quicc_write2(bas, tb + 2, 1); + quicc_write2(bas, tb, st | 0x9000); + sc->sc_txbusy = 1; + uart_unlock(sc->sc_hwmtx); + return (0); +} |