diff options
author | marcel <marcel@FreeBSD.org> | 2006-04-24 23:31:51 +0000 |
---|---|---|
committer | marcel <marcel@FreeBSD.org> | 2006-04-24 23:31:51 +0000 |
commit | deb16135f3fca75d654b5678f9f331af783b6d6b (patch) | |
tree | 4c14a9bedc4bcdbb3fbd46089cf47c3af3cc2de1 /sys/dev/ppc | |
parent | eefd58df920bd36d125e2f8e0db44d74e8859b3a (diff) | |
download | FreeBSD-src-deb16135f3fca75d654b5678f9f331af783b6d6b.zip FreeBSD-src-deb16135f3fca75d654b5678f9f331af783b6d6b.tar.gz |
o Move ISA specific code from ppc.c to ppc_isa.c -- a bus front-
end for isa(4).
o Add a seperate bus frontend for acpi(4) and allow ISA DMA for
it when ISA is configured in the kernel. This allows acpi(4)
attachments in non-ISA configurations, as is possible for ia64.
o Add a seperate bus frontend for pci(4) and detect known single
port parallel cards.
o Merge PC98 specific changes under pc98/cbus into the MI driver.
The changes are minor enough for conditional compilation and
in this form invites better abstraction.
o Have ppc(4) usabled on all platforms, now that ISA specifics
are untangled enough.
Diffstat (limited to 'sys/dev/ppc')
-rw-r--r-- | sys/dev/ppc/ppc.c | 302 | ||||
-rw-r--r-- | sys/dev/ppc/ppc_acpi.c | 114 | ||||
-rw-r--r-- | sys/dev/ppc/ppc_isa.c | 281 | ||||
-rw-r--r-- | sys/dev/ppc/ppc_pci.c | 110 | ||||
-rw-r--r-- | sys/dev/ppc/ppc_puc.c | 4 | ||||
-rw-r--r-- | sys/dev/ppc/ppcreg.h | 9 | ||||
-rw-r--r-- | sys/dev/ppc/ppcvar.h | 5 |
7 files changed, 585 insertions, 240 deletions
diff --git a/sys/dev/ppc/ppc.c b/sys/dev/ppc/ppc.c index 7e1ed40..48a9889 100644 --- a/sys/dev/ppc/ppc.c +++ b/sys/dev/ppc/ppc.c @@ -36,18 +36,16 @@ __FBSDID("$FreeBSD$"); #include <sys/module.h> #include <sys/bus.h> #include <sys/malloc.h> -#include <sys/kdb.h> -#include <vm/vm.h> -#include <vm/pmap.h> -#include <machine/clock.h> #include <machine/bus.h> #include <machine/resource.h> -#include <machine/vmparam.h> #include <sys/rman.h> -#include <isa/isareg.h> -#include <isa/isavar.h> +#ifdef __i386__ +#include <vm/vm.h> +#include <vm/pmap.h> +#include <machine/vmparam.h> +#endif #include <dev/ppbus/ppbconf.h> #include <dev/ppbus/ppb_msq.h> @@ -57,47 +55,23 @@ __FBSDID("$FreeBSD$"); #include "ppbus_if.h" -static int ppc_isa_probe(device_t dev); - static void ppcintr(void *arg); +#define IO_LPTSIZE_EXTENDED 8 /* "Extended" LPT controllers */ +#define IO_LPTSIZE_NORMAL 4 /* "Normal" LPT controllers */ + #define LOG_PPC(function, ppc, string) \ if (bootverbose) printf("%s: %s\n", function, string) +#if defined(__i386__) && defined(PC98) +#define PC98_IEEE_1284_DISABLE 0x100 +#define PC98_IEEE_1284_PORT 0x140 +#endif #define DEVTOSOFTC(dev) ((struct ppc_data *)device_get_softc(dev)) devclass_t ppc_devclass; - -static device_method_t ppc_methods[] = { - /* device interface */ - DEVMETHOD(device_probe, ppc_isa_probe), - DEVMETHOD(device_attach, ppc_attach), - DEVMETHOD(device_detach, ppc_detach), - - /* bus interface */ - DEVMETHOD(bus_read_ivar, ppc_read_ivar), - DEVMETHOD(bus_setup_intr, ppc_setup_intr), - DEVMETHOD(bus_teardown_intr, ppc_teardown_intr), - DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), - - /* ppbus interface */ - DEVMETHOD(ppbus_io, ppc_io), - DEVMETHOD(ppbus_exec_microseq, ppc_exec_microseq), - DEVMETHOD(ppbus_reset_epp, ppc_reset_epp), - DEVMETHOD(ppbus_setmode, ppc_setmode), - DEVMETHOD(ppbus_ecp_sync, ppc_ecp_sync), - DEVMETHOD(ppbus_read, ppc_read), - DEVMETHOD(ppbus_write, ppc_write), - - { 0, 0 } - }; - -static driver_t ppc_driver = { - "ppc", - ppc_methods, - sizeof(struct ppc_data), -}; +const char ppc_driver_name[] = "ppc"; static char *ppc_models[] = { "SMC-like", "SMC FDC37C665GT", "SMC FDC37C666GT", "PC87332", "PC87306", @@ -1593,12 +1567,7 @@ ppcintr(void *arg) #ifdef PPC_DEBUG printf("d"); #endif - isa_dmadone( - ppc->ppc_dmaflags, - ppc->ppc_dmaddr, - ppc->ppc_dmacnt, - ppc->ppc_dmachan); - + ppc->ppc_dmadone(ppc); ppc->ppc_dmastat = PPC_DMA_COMPLETE; /* wakeup the waiting process */ @@ -1620,163 +1589,10 @@ ppc_read(device_t dev, char *buf, int len, int mode) return (EINVAL); } -/* - * Call this function if you want to send data in any advanced mode - * of your parallel port: FIFO, DMA - * - * If what you want is not possible (no ECP, no DMA...), - * EINVAL is returned - */ int ppc_write(device_t dev, char *buf, int len, int how) { - struct ppc_data *ppc = DEVTOSOFTC(dev); - char ecr, ecr_sav, ctr, ctr_sav; - int s, error = 0; - int spin; - -#ifdef PPC_DEBUG - printf("w"); -#endif - - ecr_sav = r_ecr(ppc); - ctr_sav = r_ctr(ppc); - - /* - * Send buffer with DMA, FIFO and interrupts - */ - if ((ppc->ppc_avm & PPB_ECP) && (ppc->ppc_registered)) { - - if (ppc->ppc_dmachan > 0) { - - /* byte mode, no intr, no DMA, dir=0, flush fifo - */ - ecr = PPC_ECR_STD | PPC_DISABLE_INTR; - w_ecr(ppc, ecr); - - /* disable nAck interrupts */ - ctr = r_ctr(ppc); - ctr &= ~IRQENABLE; - w_ctr(ppc, ctr); - - ppc->ppc_dmaflags = 0; - ppc->ppc_dmaddr = (caddr_t)buf; - ppc->ppc_dmacnt = (u_int)len; - - switch (ppc->ppc_mode) { - case PPB_COMPATIBLE: - /* compatible mode with FIFO, no intr, DMA, dir=0 */ - ecr = PPC_ECR_FIFO | PPC_DISABLE_INTR | PPC_ENABLE_DMA; - break; - case PPB_ECP: - ecr = PPC_ECR_ECP | PPC_DISABLE_INTR | PPC_ENABLE_DMA; - break; - default: - error = EINVAL; - goto error; - } - - w_ecr(ppc, ecr); - ecr = r_ecr(ppc); - - /* enter splhigh() not to be preempted - * by the dma interrupt, we may miss - * the wakeup otherwise - */ - s = splhigh(); - - ppc->ppc_dmastat = PPC_DMA_INIT; - - /* enable interrupts */ - ecr &= ~PPC_SERVICE_INTR; - ppc->ppc_irqstat = PPC_IRQ_DMA; - w_ecr(ppc, ecr); - - isa_dmastart( - ppc->ppc_dmaflags, - ppc->ppc_dmaddr, - ppc->ppc_dmacnt, - ppc->ppc_dmachan); -#ifdef PPC_DEBUG - printf("s%d", ppc->ppc_dmacnt); -#endif - ppc->ppc_dmastat = PPC_DMA_STARTED; - - /* Wait for the DMA completed interrupt. We hope we won't - * miss it, otherwise a signal will be necessary to unlock the - * process. - */ - do { - /* release CPU */ - error = tsleep(ppc, - PPBPRI | PCATCH, "ppcdma", 0); - - } while (error == EWOULDBLOCK); - - splx(s); - - if (error) { -#ifdef PPC_DEBUG - printf("i"); -#endif - /* stop DMA */ - isa_dmadone( - ppc->ppc_dmaflags, ppc->ppc_dmaddr, - ppc->ppc_dmacnt, ppc->ppc_dmachan); - - /* no dma, no interrupt, flush the fifo */ - w_ecr(ppc, PPC_ECR_RESET); - - ppc->ppc_dmastat = PPC_DMA_INTERRUPTED; - goto error; - } - - /* wait for an empty fifo */ - while (!(r_ecr(ppc) & PPC_FIFO_EMPTY)) { - - for (spin=100; spin; spin--) - if (r_ecr(ppc) & PPC_FIFO_EMPTY) - goto fifo_empty; -#ifdef PPC_DEBUG - printf("Z"); -#endif - error = tsleep(ppc, PPBPRI | PCATCH, "ppcfifo", hz/100); - if (error != EWOULDBLOCK) { -#ifdef PPC_DEBUG - printf("I"); -#endif - /* no dma, no interrupt, flush the fifo */ - w_ecr(ppc, PPC_ECR_RESET); - - ppc->ppc_dmastat = PPC_DMA_INTERRUPTED; - error = EINTR; - goto error; - } - } - -fifo_empty: - /* no dma, no interrupt, flush the fifo */ - w_ecr(ppc, PPC_ECR_RESET); - - } else - error = EINVAL; /* XXX we should FIFO and - * interrupts */ - } else - error = EINVAL; - -error: - - /* PDRQ must be kept unasserted until nPDACK is - * deasserted for a minimum of 350ns (SMC datasheet) - * - * Consequence may be a FIFO that never empty - */ - DELAY(1); - - w_ecr(ppc, ecr_sav); - w_ctr(ppc, ctr_sav); - - return (error); + return (EINVAL); } void @@ -1809,34 +1625,15 @@ ppc_setmode(device_t dev, int mode) return (ENXIO); } -static struct isa_pnp_id lpc_ids[] = { - { 0x0004d041, "Standard parallel printer port" }, /* PNP0400 */ - { 0x0104d041, "ECP parallel printer port" }, /* PNP0401 */ - { 0 } -}; - -static int -ppc_isa_probe(device_t dev) -{ - device_t parent; - int error; - - parent = device_get_parent(dev); - - error = ISA_PNP_PROBE(parent, dev, lpc_ids); - if (error == ENXIO) - return (ENXIO); - else if (error != 0) /* XXX shall be set after detection */ - device_set_desc(dev, "Parallel port"); - - return(ppc_probe(dev)); -} - int -ppc_probe(device_t dev) +ppc_probe(device_t dev, int rid) { #ifdef __i386__ static short next_bios_ppc = 0; +#ifdef PC98 + unsigned int pc98_ieee_mode = 0x00; + unsigned int tmp; +#endif #endif struct ppc_data *ppc; int error; @@ -1848,17 +1645,27 @@ ppc_probe(device_t dev) ppc = DEVTOSOFTC(dev); bzero(ppc, sizeof(struct ppc_data)); - ppc->rid_irq = ppc->rid_drq = ppc->rid_ioport = 0; - ppc->res_irq = ppc->res_drq = ppc->res_ioport = 0; + ppc->rid_ioport = rid; /* retrieve ISA parameters */ - error = bus_get_resource(dev, SYS_RES_IOPORT, 0, &port, NULL); + error = bus_get_resource(dev, SYS_RES_IOPORT, rid, &port, NULL); #ifdef __i386__ /* * If port not specified, use bios list. */ if (error) { +#ifdef PC98 + if (next_bios_ppc == 0) { + /* Use default IEEE-1284 port of NEC PC-98x1 */ + port = PC98_IEEE_1284_PORT; + next_bios_ppc += 1; + if (bootverbose) + device_printf(dev, + "parallel port found at 0x%x\n", + (int) port); + } +#else if((next_bios_ppc < BIOS_MAX_PPC) && (*(BIOS_PORTS+next_bios_ppc) != 0) ) { port = *(BIOS_PORTS+next_bios_ppc++); @@ -1869,7 +1676,8 @@ ppc_probe(device_t dev) device_printf(dev, "parallel port not found.\n"); return ENXIO; } - bus_set_resource(dev, SYS_RES_IOPORT, 0, port, +#endif /* PC98 */ + bus_set_resource(dev, SYS_RES_IOPORT, rid, port, IO_LPTSIZE_EXTENDED); } #endif @@ -1878,7 +1686,7 @@ ppc_probe(device_t dev) * There isn't a bios list on alpha. Put it in the usual place. */ if (error) { - bus_set_resource(dev, SYS_RES_IOPORT, 0, 0x3bc, + bus_set_resource(dev, SYS_RES_IOPORT, rid, 0x3bc, IO_LPTSIZE_NORMAL); } #endif @@ -1937,6 +1745,30 @@ ppc_probe(device_t dev) ppc->ppc_type = PPC_TYPE_GENERIC; +#if defined(__i386__) && defined(PC98) + /* + * IEEE STD 1284 Function Check and Enable + * for default IEEE-1284 port of NEC PC-98x1 + */ + if (ppc->ppc_base == PC98_IEEE_1284_PORT && + !(ppc->ppc_flags & PC98_IEEE_1284_DISABLE)) { + tmp = inb(ppc->ppc_base + PPC_1284_ENABLE); + pc98_ieee_mode = tmp; + if ((tmp & 0x10) == 0x10) { + outb(ppc->ppc_base + PPC_1284_ENABLE, tmp & ~0x10); + tmp = inb(ppc->ppc_base + PPC_1284_ENABLE); + if ((tmp & 0x10) == 0x10) + goto error; + } else { + outb(ppc->ppc_base + PPC_1284_ENABLE, tmp | 0x10); + tmp = inb(ppc->ppc_base + PPC_1284_ENABLE); + if ((tmp & 0x10) != 0x10) + goto error; + } + outb(ppc->ppc_base + PPC_1284_ENABLE, pc98_ieee_mode | 0x10); + } +#endif + /* * Try to detect the chipset and its mode. */ @@ -1946,6 +1778,12 @@ ppc_probe(device_t dev) return (0); error: +#if defined(__i386__) && defined(PC98) + if (ppc->ppc_base == PC98_IEEE_1284_PORT && + !(ppc->ppc_flags & PC98_IEEE_1284_DISABLE)) { + outb(ppc->ppc_base + PPC_1284_ENABLE, pc98_ieee_mode); + } +#endif if (ppc->res_irq != 0) { bus_release_resource(dev, SYS_RES_IRQ, ppc->rid_irq, ppc->res_irq); @@ -1981,12 +1819,6 @@ ppc_attach(device_t dev) device_printf(dev, "FIFO with %d/%d/%d bytes threshold\n", ppc->ppc_fifo, ppc->ppc_wthr, ppc->ppc_rthr); - if ((ppc->ppc_avm & PPB_ECP) && (ppc->ppc_dmachan > 0)) { - /* acquire the DMA channel forever */ /* XXX */ - isa_dma_acquire(ppc->ppc_dmachan); - isa_dmainit(ppc->ppc_dmachan, 1024); /* nlpt.BUFSIZE */ - } - /* add ppbus as a child of this isa to parallel bridge */ ppbus = device_add_child(dev, "ppbus", -1); @@ -2192,6 +2024,4 @@ ppc_teardown_intr(device_t bus, device_t child, struct resource *r, void *ih) return (error); } -DRIVER_MODULE(ppc, isa, ppc_driver, ppc_devclass, 0, 0); -DRIVER_MODULE(ppc, acpi, ppc_driver, ppc_devclass, 0, 0); MODULE_DEPEND(ppc, ppbus, 1, 1, 1); diff --git a/sys/dev/ppc/ppc_acpi.c b/sys/dev/ppc/ppc_acpi.c new file mode 100644 index 0000000..c3a42fe --- /dev/null +++ b/sys/dev/ppc/ppc_acpi.c @@ -0,0 +1,114 @@ +/*- + * 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 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 "opt_isa.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/bus.h> + +#include <machine/bus.h> + +#include <isa/isareg.h> +#include <isa/isavar.h> + +#include <dev/ppbus/ppbconf.h> +#include <dev/ppbus/ppb_msq.h> +#include <dev/ppc/ppcvar.h> +#include <dev/ppc/ppcreg.h> + +#include "ppbus_if.h" + +static int ppc_acpi_probe(device_t dev); + +int ppc_isa_attach(device_t dev); +int ppc_isa_write(device_t, char *, int, int); + +static device_method_t ppc_acpi_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, ppc_acpi_probe), +#ifdef DEV_ISA + DEVMETHOD(device_attach, ppc_isa_attach), +#else + DEVMETHOD(device_attach, ppc_attach), +#endif + DEVMETHOD(device_detach, ppc_attach), + + /* bus interface */ + DEVMETHOD(bus_read_ivar, ppc_read_ivar), + DEVMETHOD(bus_setup_intr, ppc_setup_intr), + DEVMETHOD(bus_teardown_intr, ppc_teardown_intr), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), + + /* ppbus interface */ + DEVMETHOD(ppbus_io, ppc_io), + DEVMETHOD(ppbus_exec_microseq, ppc_exec_microseq), + DEVMETHOD(ppbus_reset_epp, ppc_reset_epp), + DEVMETHOD(ppbus_setmode, ppc_setmode), + DEVMETHOD(ppbus_ecp_sync, ppc_ecp_sync), + DEVMETHOD(ppbus_read, ppc_read), +#ifdef DEV_ISA + DEVMETHOD(ppbus_write, ppc_isa_write), +#else + DEVMETHOD(ppbus_write, ppc_write), +#endif + + { 0, 0 } +}; + +static driver_t ppc_acpi_driver = { + ppc_driver_name, + ppc_acpi_methods, + sizeof(struct ppc_data), +}; + +static struct isa_pnp_id lpc_ids[] = { + { 0x0004d041, "Standard parallel printer port" }, /* PNP0400 */ + { 0x0104d041, "ECP parallel printer port" }, /* PNP0401 */ + { 0 } +}; + +static int +ppc_acpi_probe(device_t dev) +{ + device_t parent; + int error; + + parent = device_get_parent(dev); + + error = ISA_PNP_PROBE(parent, dev, lpc_ids); + if (error) + return (ENXIO); + + device_set_desc(dev, "Parallel port"); + return (ppc_probe(dev, 0)); +} + +DRIVER_MODULE(ppc, acpi, ppc_acpi_driver, ppc_devclass, 0, 0); diff --git a/sys/dev/ppc/ppc_isa.c b/sys/dev/ppc/ppc_isa.c new file mode 100644 index 0000000..0324a49 --- /dev/null +++ b/sys/dev/ppc/ppc_isa.c @@ -0,0 +1,281 @@ +/*- + * Copyright (c) 1997-2000 Nicolas Souchu + * Copyright (c) 2001 Alcove - Nicolas Souchu + * 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 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/kernel.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <machine/bus.h> +#include <sys/malloc.h> + +#if defined(__i386__) && defined(PC98) +#include <pc98/cbus/cbus.h> +#else +#include <isa/isareg.h> +#endif +#include <isa/isavar.h> + +#include <dev/ppbus/ppbconf.h> +#include <dev/ppbus/ppb_msq.h> +#include <dev/ppc/ppcvar.h> +#include <dev/ppc/ppcreg.h> + +#include "ppbus_if.h" + +static int ppc_isa_probe(device_t dev); + +int ppc_isa_attach(device_t dev); +int ppc_isa_write(device_t, char *, int, int); + +static device_method_t ppc_isa_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, ppc_isa_probe), + DEVMETHOD(device_attach, ppc_isa_attach), + DEVMETHOD(device_detach, ppc_attach), + + /* bus interface */ + DEVMETHOD(bus_read_ivar, ppc_read_ivar), + DEVMETHOD(bus_setup_intr, ppc_setup_intr), + DEVMETHOD(bus_teardown_intr, ppc_teardown_intr), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), + + /* ppbus interface */ + DEVMETHOD(ppbus_io, ppc_io), + DEVMETHOD(ppbus_exec_microseq, ppc_exec_microseq), + DEVMETHOD(ppbus_reset_epp, ppc_reset_epp), + DEVMETHOD(ppbus_setmode, ppc_setmode), + DEVMETHOD(ppbus_ecp_sync, ppc_ecp_sync), + DEVMETHOD(ppbus_read, ppc_read), + DEVMETHOD(ppbus_write, ppc_isa_write), + + { 0, 0 } +}; + +static driver_t ppc_isa_driver = { + ppc_driver_name, + ppc_isa_methods, + sizeof(struct ppc_data), +}; + +static struct isa_pnp_id lpc_ids[] = { + { 0x0004d041, "Standard parallel printer port" }, /* PNP0400 */ + { 0x0104d041, "ECP parallel printer port" }, /* PNP0401 */ + { 0 } +}; + +static void +ppc_isa_dmadone(struct ppc_data *ppc) +{ + isa_dmadone(ppc->ppc_dmaflags, ppc->ppc_dmaddr, ppc->ppc_dmacnt, + ppc->ppc_dmachan); +} + +int +ppc_isa_attach(device_t dev) +{ + struct ppc_data *ppc = device_get_softc(dev); + + if ((ppc->ppc_avm & PPB_ECP) && (ppc->ppc_dmachan > 0)) { + /* acquire the DMA channel forever */ /* XXX */ + isa_dma_acquire(ppc->ppc_dmachan); + isa_dmainit(ppc->ppc_dmachan, 1024); /* nlpt.BUFSIZE */ + ppc->ppc_dmadone = ppc_isa_dmadone; + } + + return (ppc_attach(dev)); +} + +static int +ppc_isa_probe(device_t dev) +{ + device_t parent; + int error; + + parent = device_get_parent(dev); + + error = ISA_PNP_PROBE(parent, dev, lpc_ids); + if (error == ENXIO) + return (ENXIO); + if (error != 0) /* XXX shall be set after detection */ + device_set_desc(dev, "Parallel port"); + + return (ppc_probe(dev, 0)); +} + +/* + * Call this function if you want to send data in any advanced mode + * of your parallel port: FIFO, DMA + * + * If what you want is not possible (no ECP, no DMA...), + * EINVAL is returned + */ +int +ppc_isa_write(device_t dev, char *buf, int len, int how) +{ + struct ppc_data *ppc = device_get_softc(dev); + char ecr, ecr_sav, ctr, ctr_sav; + int s, error = 0; + int spin; + + if (!(ppc->ppc_avm & PPB_ECP) || !ppc->ppc_registered) + return (EINVAL); + if (ppc->ppc_dmachan == 0) + return (EINVAL); + +#ifdef PPC_DEBUG + printf("w"); +#endif + + ecr_sav = r_ecr(ppc); + ctr_sav = r_ctr(ppc); + + /* + * Send buffer with DMA, FIFO and interrupts + */ + + /* byte mode, no intr, no DMA, dir=0, flush fifo */ + ecr = PPC_ECR_STD | PPC_DISABLE_INTR; + w_ecr(ppc, ecr); + + /* disable nAck interrupts */ + ctr = r_ctr(ppc); + ctr &= ~IRQENABLE; + w_ctr(ppc, ctr); + + ppc->ppc_dmaflags = 0; + ppc->ppc_dmaddr = (caddr_t)buf; + ppc->ppc_dmacnt = (u_int)len; + + switch (ppc->ppc_mode) { + case PPB_COMPATIBLE: + /* compatible mode with FIFO, no intr, DMA, dir=0 */ + ecr = PPC_ECR_FIFO | PPC_DISABLE_INTR | PPC_ENABLE_DMA; + break; + case PPB_ECP: + ecr = PPC_ECR_ECP | PPC_DISABLE_INTR | PPC_ENABLE_DMA; + break; + default: + error = EINVAL; + goto error; + } + + w_ecr(ppc, ecr); + ecr = r_ecr(ppc); + + /* enter splhigh() not to be preempted + * by the dma interrupt, we may miss + * the wakeup otherwise + */ + s = splhigh(); + + ppc->ppc_dmastat = PPC_DMA_INIT; + + /* enable interrupts */ + ecr &= ~PPC_SERVICE_INTR; + ppc->ppc_irqstat = PPC_IRQ_DMA; + w_ecr(ppc, ecr); + + isa_dmastart(ppc->ppc_dmaflags, ppc->ppc_dmaddr, ppc->ppc_dmacnt, + ppc->ppc_dmachan); + ppc->ppc_dmastat = PPC_DMA_STARTED; + +#ifdef PPC_DEBUG + printf("s%d", ppc->ppc_dmacnt); +#endif + + /* Wait for the DMA completed interrupt. We hope we won't + * miss it, otherwise a signal will be necessary to unlock the + * process. + */ + do { + /* release CPU */ + error = tsleep(ppc, PPBPRI | PCATCH, "ppcdma", 0); + } while (error == EWOULDBLOCK); + + splx(s); + + if (error) { +#ifdef PPC_DEBUG + printf("i"); +#endif + /* stop DMA */ + isa_dmadone(ppc->ppc_dmaflags, ppc->ppc_dmaddr, + ppc->ppc_dmacnt, ppc->ppc_dmachan); + + /* no dma, no interrupt, flush the fifo */ + w_ecr(ppc, PPC_ECR_RESET); + + ppc->ppc_dmastat = PPC_DMA_INTERRUPTED; + goto error; + } + + /* wait for an empty fifo */ + while (!(r_ecr(ppc) & PPC_FIFO_EMPTY)) { + + for (spin=100; spin; spin--) + if (r_ecr(ppc) & PPC_FIFO_EMPTY) + goto fifo_empty; +#ifdef PPC_DEBUG + printf("Z"); +#endif + error = tsleep(ppc, PPBPRI | PCATCH, "ppcfifo", hz/100); + if (error != EWOULDBLOCK) { +#ifdef PPC_DEBUG + printf("I"); +#endif + /* no dma, no interrupt, flush the fifo */ + w_ecr(ppc, PPC_ECR_RESET); + + ppc->ppc_dmastat = PPC_DMA_INTERRUPTED; + error = EINTR; + goto error; + } + } + +fifo_empty: + /* no dma, no interrupt, flush the fifo */ + w_ecr(ppc, PPC_ECR_RESET); + +error: + /* PDRQ must be kept unasserted until nPDACK is + * deasserted for a minimum of 350ns (SMC datasheet) + * + * Consequence may be a FIFO that never empty + */ + DELAY(1); + + w_ecr(ppc, ecr_sav); + w_ctr(ppc, ctr_sav); + return (error); +} + +DRIVER_MODULE(ppc, isa, ppc_isa_driver, ppc_devclass, 0, 0); diff --git a/sys/dev/ppc/ppc_pci.c b/sys/dev/ppc/ppc_pci.c new file mode 100644 index 0000000..77331e2 --- /dev/null +++ b/sys/dev/ppc/ppc_pci.c @@ -0,0 +1,110 @@ +/*- + * 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 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/kernel.h> +#include <sys/module.h> +#include <sys/bus.h> + +#include <machine/bus.h> + +#include <dev/pci/pcivar.h> + +#include <dev/ppbus/ppbconf.h> +#include <dev/ppbus/ppb_msq.h> +#include <dev/ppc/ppcvar.h> +#include <dev/ppc/ppcreg.h> + +#include "ppbus_if.h" + +static int ppc_pci_probe(device_t dev); + +static device_method_t ppc_pci_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, ppc_pci_probe), + DEVMETHOD(device_attach, ppc_attach), + DEVMETHOD(device_detach, ppc_detach), + + /* bus interface */ + DEVMETHOD(bus_read_ivar, ppc_read_ivar), + DEVMETHOD(bus_setup_intr, ppc_setup_intr), + DEVMETHOD(bus_teardown_intr, ppc_teardown_intr), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), + + /* ppbus interface */ + DEVMETHOD(ppbus_io, ppc_io), + DEVMETHOD(ppbus_exec_microseq, ppc_exec_microseq), + DEVMETHOD(ppbus_reset_epp, ppc_reset_epp), + DEVMETHOD(ppbus_setmode, ppc_setmode), + DEVMETHOD(ppbus_ecp_sync, ppc_ecp_sync), + DEVMETHOD(ppbus_read, ppc_read), + DEVMETHOD(ppbus_write, ppc_write), + + { 0, 0 } +}; + +static driver_t ppc_pci_driver = { + ppc_driver_name, + ppc_pci_methods, + sizeof(struct ppc_data), +}; + +struct pci_id { + uint32_t type; + const char *desc; + int rid; +}; + +static struct pci_id pci_ids[] = { + { 0x1020131f, "SIIG Cyber Parallel PCI (10x family)", 0x18 }, + { 0x2020131f, "SIIG Cyber Parallel PCI (20x family)", 0x10 }, + { 0x80001407, "Lava Computers 2SP-PCI parallel port", 0x10 }, + { 0x84031415, "Oxford Semiconductor OX12PCI840 Parallel port", 0x10 }, + { 0x95131415, "Oxford Semiconductor OX16PCI954 Parallel port", 0x10 }, + { 0x98059710, "NetMos NM9805 1284 Printer port", 0x10 }, + { 0xffff } +}; + +static int +ppc_pci_probe(device_t dev) +{ + struct pci_id *id; + uint32_t type; + + type = pci_get_devid(dev); + id = pci_ids; + while (id->type != 0xffff && id->type != type) + id++; + if (id->type == 0xffff) + return (ENXIO); + device_set_desc(dev, id->desc); + return (ppc_probe(dev, id->rid)); +} + +DRIVER_MODULE(ppc, pci, ppc_pci_driver, ppc_devclass, 0, 0); diff --git a/sys/dev/ppc/ppc_puc.c b/sys/dev/ppc/ppc_puc.c index 2b41bfe..760b1ce 100644 --- a/sys/dev/ppc/ppc_puc.c +++ b/sys/dev/ppc/ppc_puc.c @@ -68,7 +68,7 @@ static device_method_t ppc_puc_methods[] = { }; static driver_t ppc_puc_driver = { - "ppc", + ppc_driver_name, ppc_puc_methods, sizeof(struct ppc_data), }; @@ -78,7 +78,7 @@ ppc_puc_probe(dev) device_t dev; { device_set_desc(dev, "Parallel port"); - return (ppc_probe(dev)); + return (ppc_probe(dev, 0)); } DRIVER_MODULE(ppc, puc, ppc_puc_driver, ppc_devclass, 0, 0); diff --git a/sys/dev/ppc/ppcreg.h b/sys/dev/ppc/ppcreg.h index ea12ead..6b26ed9 100644 --- a/sys/dev/ppc/ppcreg.h +++ b/sys/dev/ppc/ppcreg.h @@ -82,6 +82,7 @@ struct ppc_data { int ppc_dmaflags; /* dma transfer flags */ caddr_t ppc_dmaddr; /* buffer address */ u_int ppc_dmacnt; /* count of bytes sent with dma */ + void (*ppc_dmadone)(struct ppc_data*); #define PPC_PWORD_MASK 0x30 #define PPC_PWORD_16 0x00 @@ -123,10 +124,18 @@ struct ppc_data { #define PPC_SPP_CTR 2 /* SPP control register */ #define PPC_EPP_ADDR 3 /* EPP address register (8 bit) */ #define PPC_EPP_DATA 4 /* EPP data register (8, 16 or 32 bit) */ +#if defined(__i386__) && defined(PC98) +#define PPC_1284_ENABLE 0x09 /* IEEE STD 1284 Enable register */ +#define PPC_ECP_D_FIFO 0x0c /* ECP Data fifo register */ +#define PPC_ECP_CNFGA 0x0c /* Configuration register A */ +#define PPC_ECP_CNFGB 0x0d /* Configuration register B */ +#define PPC_ECP_ECR 0x0e /* ECP extended control register */ +#else #define PPC_ECP_D_FIFO 0x400 /* ECP Data fifo register */ #define PPC_ECP_CNFGA 0x400 /* Configuration register A */ #define PPC_ECP_CNFGB 0x401 /* Configuration register B */ #define PPC_ECP_ECR 0x402 /* ECP extended control register */ +#endif #define PPC_FIFO_EMPTY 0x1 /* ecr register - bit 0 */ #define PPC_FIFO_FULL 0x2 /* ecr register - bit 1 */ diff --git a/sys/dev/ppc/ppcvar.h b/sys/dev/ppc/ppcvar.h index 98bc022..b7d1643 100644 --- a/sys/dev/ppc/ppcvar.h +++ b/sys/dev/ppc/ppcvar.h @@ -28,7 +28,7 @@ * */ -int ppc_probe(device_t dev); +int ppc_probe(device_t dev, int rid); int ppc_attach(device_t dev); int ppc_detach(device_t dev); int ppc_read_ivar(device_t bus, device_t dev, int index, uintptr_t *val); @@ -46,4 +46,5 @@ void ppc_reset_epp(device_t); void ppc_ecp_sync(device_t); int ppc_setmode(device_t, int); -extern devclass_t ppc_devclass; +extern devclass_t ppc_devclass; +extern const char ppc_driver_name[]; |