diff options
author | nsouch <nsouch@FreeBSD.org> | 2000-01-14 00:18:06 +0000 |
---|---|---|
committer | nsouch <nsouch@FreeBSD.org> | 2000-01-14 00:18:06 +0000 |
commit | 59fc142474158cbbfbae06872a4e3efaa40e777f (patch) | |
tree | 3dd5303c8a8a1e20337965402c872462f141a217 /sys/isa | |
parent | 83719ce7419aa6d99e02f397ba20a42d7406963b (diff) | |
download | FreeBSD-src-59fc142474158cbbfbae06872a4e3efaa40e777f.zip FreeBSD-src-59fc142474158cbbfbae06872a4e3efaa40e777f.tar.gz |
Port of ppbus standalone framework to the newbus system.
Note1: the correct interrupt level is invoked correctly for each driver.
For this purpose, drivers request the bus before being able to
call BUS_SETUP_INTR and BUS_TEARDOWN_INTR call is forced by the ppbus
core when drivers release it. Thus, when BUS_SETUP_INTR is called
at ppbus driver level, ppbus checks that the caller owns the
bus and stores the interrupt handler cookie (in order to unregister
it later).
Printing is impossible while plip link is up is still TRUE.
vpo (ZIP driver) and lpt are make in such a way that
using the ZIP and printing concurrently is permitted is also TRUE.
Note2: specific chipset detection is not done by default. PPC_PROBE_CHIPSET
is now needed to force chipset detection. If set, the flags 0x40
still avoid detection at boot.
Port of the pcf(4) driver to the newbus system (was previously directly
connected to the rootbus and attached by a bogus pcf_isa_probe function).
Diffstat (limited to 'sys/isa')
-rw-r--r-- | sys/isa/ppc.c | 818 | ||||
-rw-r--r-- | sys/isa/ppcreg.h | 29 |
2 files changed, 529 insertions, 318 deletions
diff --git a/sys/isa/ppc.c b/sys/isa/ppc.c index 436caeb..0ee1eb6 100644 --- a/sys/isa/ppc.c +++ b/sys/isa/ppc.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 1997, 1998 Nicolas Souchu + * Copyright (c) 1997-2000 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,42 +30,90 @@ #if NPPC > 0 +#include "opt_ppc.h" + #include <sys/param.h> #include <sys/systm.h> -#include <sys/conf.h> -#include <sys/malloc.h> #include <sys/kernel.h> - -#include <machine/clock.h> - +#include <sys/bus.h> +#include <sys/malloc.h> + #include <vm/vm.h> -#include <vm/vm_param.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 <i386/isa/isa_device.h> -#include <i386/isa/isa.h> +#include <isa/isareg.h> +#include <isa/isavar.h> #include <dev/ppbus/ppbconf.h> #include <dev/ppbus/ppb_msq.h> #include <i386/isa/ppcreg.h> -#include "opt_ppc.h" +#include "ppbus_if.h" #define LOG_PPC(function, ppc, string) \ if (bootverbose) printf("%s: %s\n", function, string) -static int ppcprobe(struct isa_device *); -static int ppcattach(struct isa_device *); -struct isa_driver ppcdriver = { - ppcprobe, ppcattach, "ppc" +#define DEVTOSOFTC(dev) ((struct ppc_data *)device_get_softc(dev)) + +devclass_t ppc_devclass; + +static int ppc_probe(device_t dev); +static int ppc_attach(device_t dev); +static int ppc_read_ivar(device_t bus, device_t dev, int index, uintptr_t *val); + +static void ppc_reset_epp(device_t); +static void ppc_ecp_sync(device_t); +static void ppcintr(void *arg); + +static int ppc_exec_microseq(device_t, struct ppb_microseq **); +static int ppc_setmode(device_t, int); + +static int ppc_read(device_t, char *, int, int); +static int ppc_write(device_t, char *, int, int); + +static u_char ppc_io(device_t, int, u_char *, int, u_char); + +static int ppc_setup_intr(device_t, device_t, struct resource *, int, + void (*)(void *), void *, void **); +static int ppc_teardown_intr(device_t, device_t, struct resource *, void *); + +static device_method_t ppc_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, ppc_probe), + DEVMETHOD(device_attach, 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_write), + + { 0, 0 } + }; + +static driver_t ppc_driver = { + "ppc", + ppc_methods, + sizeof(struct ppc_data), }; - -static struct ppc_data *ppcdata[NPPC]; -static int nppc = 0; - -static char *ppc_types[] = { + +static char *ppc_models[] = { "SMC-like", "SMC FDC37C665GT", "SMC FDC37C666GT", "PC87332", "PC87306", "82091AA", "Generic", "W83877F", "W83877AF", "Winbond", "PC87334", 0 }; @@ -98,91 +146,13 @@ static char *ppc_epp_protocol[] = { " (EPP 1.9)", " (EPP 1.7)", 0 }; #define BIOS_MAX_PPC 4 /* - * All these functions are default actions for IN/OUT operations. - * They may be redefined if needed. - */ -static void ppc_outsb_epp(int unit, char *addr, int cnt) { - outsb(ppcdata[unit]->ppc_base + PPC_EPP_DATA, addr, cnt); } -static void ppc_outsw_epp(int unit, char *addr, int cnt) { - outsw(ppcdata[unit]->ppc_base + PPC_EPP_DATA, addr, cnt); } -static void ppc_outsl_epp(int unit, char *addr, int cnt) { - outsl(ppcdata[unit]->ppc_base + PPC_EPP_DATA, addr, cnt); } -static void ppc_insb_epp(int unit, char *addr, int cnt) { - insb(ppcdata[unit]->ppc_base + PPC_EPP_DATA, addr, cnt); } -static void ppc_insw_epp(int unit, char *addr, int cnt) { - insw(ppcdata[unit]->ppc_base + PPC_EPP_DATA, addr, cnt); } -static void ppc_insl_epp(int unit, char *addr, int cnt) { - insl(ppcdata[unit]->ppc_base + PPC_EPP_DATA, addr, cnt); } - -static u_char ppc_rdtr(int unit) { return r_dtr(ppcdata[unit]); } -static u_char ppc_rstr(int unit) { return r_str(ppcdata[unit]); } -static u_char ppc_rctr(int unit) { return r_ctr(ppcdata[unit]); } -static u_char ppc_repp_A(int unit) { return r_epp_A(ppcdata[unit]); } -static u_char ppc_repp_D(int unit) { return r_epp_D(ppcdata[unit]); } -static u_char ppc_recr(int unit) { return r_ecr(ppcdata[unit]); } -static u_char ppc_rfifo(int unit) { return r_fifo(ppcdata[unit]); } - -static void ppc_wdtr(int unit, char byte) { w_dtr(ppcdata[unit], byte); } -static void ppc_wstr(int unit, char byte) { w_str(ppcdata[unit], byte); } -static void ppc_wctr(int unit, char byte) { w_ctr(ppcdata[unit], byte); } -static void ppc_wepp_A(int unit, char byte) { w_epp_A(ppcdata[unit], byte); } -static void ppc_wepp_D(int unit, char byte) { w_epp_D(ppcdata[unit], byte); } -static void ppc_wecr(int unit, char byte) { w_ecr(ppcdata[unit], byte); } -static void ppc_wfifo(int unit, char byte) { w_fifo(ppcdata[unit], byte); } - -static void ppc_reset_epp_timeout(int); -static void ppc_ecp_sync(int); -static ointhand2_t ppcintr; - -static int ppc_exec_microseq(int, struct ppb_microseq **); -static int ppc_generic_setmode(int, int); -static int ppc_smclike_setmode(int, int); - -static int ppc_read(int, char *, int, int); -static int ppc_write(int, char *, int, int); - -static struct ppb_adapter ppc_smclike_adapter = { - - 0, /* no intr handler, filled by chipset dependent code */ - - ppc_reset_epp_timeout, ppc_ecp_sync, - - ppc_exec_microseq, - - ppc_smclike_setmode, ppc_read, ppc_write, - - ppc_outsb_epp, ppc_outsw_epp, ppc_outsl_epp, - ppc_insb_epp, ppc_insw_epp, ppc_insl_epp, - - ppc_rdtr, ppc_rstr, ppc_rctr, ppc_repp_A, ppc_repp_D, ppc_recr, ppc_rfifo, - ppc_wdtr, ppc_wstr, ppc_wctr, ppc_wepp_A, ppc_wepp_D, ppc_wecr, ppc_wfifo -}; - -static struct ppb_adapter ppc_generic_adapter = { - - 0, /* no intr handler, filled by chipset dependent code */ - - ppc_reset_epp_timeout, ppc_ecp_sync, - - ppc_exec_microseq, - - ppc_generic_setmode, ppc_read, ppc_write, - - ppc_outsb_epp, ppc_outsw_epp, ppc_outsl_epp, - ppc_insb_epp, ppc_insw_epp, ppc_insl_epp, - - ppc_rdtr, ppc_rstr, ppc_rctr, ppc_repp_A, ppc_repp_D, ppc_recr, ppc_rfifo, - ppc_wdtr, ppc_wstr, ppc_wctr, ppc_wepp_A, ppc_wepp_D, ppc_wecr, ppc_wfifo -}; - -/* * ppc_ecp_sync() XXX */ static void -ppc_ecp_sync(int unit) { +ppc_ecp_sync(device_t dev) { - struct ppc_data *ppc = ppcdata[unit]; int i, r; + struct ppc_data *ppc = DEVTOSOFTC(dev); if (!(ppc->ppc_avm & PPB_ECP)) return; @@ -199,7 +169,7 @@ ppc_ecp_sync(int unit) { } printf("ppc%d: ECP sync failed as data still " \ - "present in FIFO.\n", unit); + "present in FIFO.\n", ppc->ppc_unit); return; } @@ -328,6 +298,121 @@ ppc_detect_port(struct ppc_data *ppc) } /* + * EPP timeout, according to the PC87332 manual + * Semantics of clearing EPP timeout bit. + * PC87332 - reading SPP_STR does it... + * SMC - write 1 to EPP timeout bit XXX + * Others - (?) write 0 to EPP timeout bit + */ +static void +ppc_reset_epp_timeout(struct ppc_data *ppc) +{ + register char r; + + r = r_str(ppc); + w_str(ppc, r | 0x1); + w_str(ppc, r & 0xfe); + + return; +} + +static int +ppc_check_epp_timeout(struct ppc_data *ppc) +{ + ppc_reset_epp_timeout(ppc); + + return (!(r_str(ppc) & TIMEOUT)); +} + +/* + * Configure current operating mode + */ +static int +ppc_generic_setmode(struct ppc_data *ppc, int mode) +{ + u_char ecr = 0; + + /* check if mode is available */ + if (mode && !(ppc->ppc_avm & mode)) + return (EINVAL); + + /* if ECP mode, configure ecr register */ + if (ppc->ppc_avm & PPB_ECP) { + /* return to byte mode (keeping direction bit), + * no interrupt, no DMA to be able to change to + * ECP + */ + w_ecr(ppc, PPC_ECR_RESET); + ecr = PPC_DISABLE_INTR; + + if (mode & PPB_EPP) + return (EINVAL); + else if (mode & PPB_ECP) + /* select ECP mode */ + ecr |= PPC_ECR_ECP; + else if (mode & PPB_PS2) + /* select PS2 mode with ECP */ + ecr |= PPC_ECR_PS2; + else + /* select COMPATIBLE/NIBBLE mode */ + ecr |= PPC_ECR_STD; + + w_ecr(ppc, ecr); + } + + ppc->ppc_mode = mode; + + return (0); +} + +/* + * The ppc driver is free to choose options like FIFO or DMA + * if ECP mode is available. + * + * The 'RAW' option allows the upper drivers to force the ppc mode + * even with FIFO, DMA available. + */ +static int +ppc_smclike_setmode(struct ppc_data *ppc, int mode) +{ + u_char ecr = 0; + + /* check if mode is available */ + if (mode && !(ppc->ppc_avm & mode)) + return (EINVAL); + + /* if ECP mode, configure ecr register */ + if (ppc->ppc_avm & PPB_ECP) { + /* return to byte mode (keeping direction bit), + * no interrupt, no DMA to be able to change to + * ECP or EPP mode + */ + w_ecr(ppc, PPC_ECR_RESET); + ecr = PPC_DISABLE_INTR; + + if (mode & PPB_EPP) + /* select EPP mode */ + ecr |= PPC_ECR_EPP; + else if (mode & PPB_ECP) + /* select ECP mode */ + ecr |= PPC_ECR_ECP; + else if (mode & PPB_PS2) + /* select PS2 mode with ECP */ + ecr |= PPC_ECR_PS2; + else + /* select COMPATIBLE/NIBBLE mode */ + ecr |= PPC_ECR_STD; + + w_ecr(ppc, ecr); + } + + ppc->ppc_mode = mode; + + return (0); +} + +#ifdef PPC_PROBE_CHIPSET +/* * ppc_pc873xx_detect * * Probe for a Natsemi PC873xx-family part. @@ -381,11 +466,11 @@ ppc_pc873xx_detect(struct ppc_data *ppc, int chipset_mode) /* XXX mode never for outb(idport, PC873_SID); val = inb(idport + 1); if ((val & 0xf0) == 0x10) { - ppc->ppc_type = NS_PC87332; + ppc->ppc_model = NS_PC87332; } else if ((val & 0xf8) == 0x70) { - ppc->ppc_type = NS_PC87306; + ppc->ppc_model = NS_PC87306; } else if ((val & 0xf8) == 0x50) { - ppc->ppc_type = NS_PC87334; + ppc->ppc_model = NS_PC87334; } else { if (bootverbose && (val != 0xff)) printf("PC873xx probe at 0x%x got unknown ID 0x%x\n", idport, val); @@ -484,7 +569,7 @@ ppc_pc873xx_detect(struct ppc_data *ppc, int chipset_mode) /* XXX mode never for else ppc->ppc_epp = EPP_1_7; - if ((ppc->ppc_type == NS_PC87332) && bootverbose) { + if ((ppc->ppc_model == NS_PC87332) && bootverbose) { outb(idport, PC873_PTR); ptr = inb(idport + 1); if (ptr & PC873_EPPRDIR) @@ -539,7 +624,7 @@ ppc_pc873xx_detect(struct ppc_data *ppc, int chipset_mode) /* XXX mode never for printf(", EPP1.9"); /* enable automatic direction turnover */ - if (ppc->ppc_type == NS_PC87332) { + if (ppc->ppc_model == NS_PC87332) { outb(idport, PC873_PTR); ptr = inb(idport + 1); ptr &= ~PC873_EPPRDIR; @@ -581,22 +666,14 @@ ppc_pc873xx_detect(struct ppc_data *ppc, int chipset_mode) /* XXX mode never for if (bootverbose) printf("\n"); - ppc->ppc_link.adapter = &ppc_generic_adapter; - ppc_generic_setmode(ppc->ppc_unit, chipset_mode); + ppc->ppc_type = PPC_TYPE_GENERIC; + ppc_generic_setmode(ppc, chipset_mode); return(chipset_mode); } return(-1); } -static int -ppc_check_epp_timeout(struct ppc_data *ppc) -{ - ppc_reset_epp_timeout(ppc->ppc_unit); - - return (!(r_str(ppc) & TIMEOUT)); -} - /* * ppc_smc37c66xgt_detect * @@ -661,7 +738,7 @@ config: if (port_address[(int)r] != ppc->ppc_base) return (-1); - ppc->ppc_type = type; + ppc->ppc_model = type; /* * CR1 and CR4 registers bits 3 and 0/1 for mode configuration @@ -800,8 +877,8 @@ end_detect: /* end config mode */ outb(csr, 0xaa); - ppc->ppc_link.adapter = &ppc_smclike_adapter; - ppc_smclike_setmode(ppc->ppc_unit, chipset_mode); + ppc->ppc_type = PPC_TYPE_SMCLIKE; + ppc_smclike_setmode(ppc, chipset_mode); return (chipset_mode); } @@ -866,15 +943,15 @@ found: switch (inb(efdr) & WINB_CHIPID) { case WINB_W83877F_ID: - ppc->ppc_type = WINB_W83877F; + ppc->ppc_model = WINB_W83877F; break; case WINB_W83877AF_ID: - ppc->ppc_type = WINB_W83877AF; + ppc->ppc_model = WINB_W83877AF; break; default: - ppc->ppc_type = WINB_UNKNOWN; + ppc->ppc_model = WINB_UNKNOWN; } if (bootverbose) { @@ -898,7 +975,7 @@ found: printf("ppc%d:", ppc->ppc_unit); } - ppc->ppc_link.adapter = &ppc_generic_adapter; + ppc->ppc_type = PPC_TYPE_GENERIC; if (!chipset_mode) { /* autodetect mode */ @@ -940,7 +1017,7 @@ found: case (WINB_PARALLEL | WINB_ECP_EPP): ppc->ppc_avm |= PPB_ECP | PPB_EPP | PPB_SPP; - ppc->ppc_link.adapter = &ppc_smclike_adapter; + ppc->ppc_type = PPC_TYPE_SMCLIKE; if (bootverbose) printf(" ECP+EPP SPP"); @@ -966,7 +1043,7 @@ found: if (bootverbose) printf(" ECP+EPP"); - ppc->ppc_link.adapter = &ppc_smclike_adapter; + ppc->ppc_type = PPC_TYPE_SMCLIKE; } else { outb(efdr, inb(efdr) | WINB_ECP); @@ -988,10 +1065,18 @@ found: /* exit configuration mode */ outb(efer, 0xaa); - ppc->ppc_link.adapter->setmode(ppc->ppc_unit, chipset_mode); + switch (ppc->ppc_type) { + case PPC_TYPE_SMCLIKE: + ppc_smclike_setmode(ppc, chipset_mode); + break; + default: + ppc_generic_setmode(ppc, chipset_mode); + break; + } return (chipset_mode); } +#endif /* * ppc_generic_detect @@ -1000,7 +1085,7 @@ static int ppc_generic_detect(struct ppc_data *ppc, int chipset_mode) { /* default to generic */ - ppc->ppc_link.adapter = &ppc_generic_adapter; + ppc->ppc_type = PPC_TYPE_GENERIC; if (bootverbose) printf("ppc%d:", ppc->ppc_unit); @@ -1023,8 +1108,8 @@ ppc_generic_detect(struct ppc_data *ppc, int chipset_mode) if (ppc->ppc_avm & PPB_ECP) { /* SMC like chipset found */ - ppc->ppc_type = SMC_LIKE; - ppc->ppc_link.adapter = &ppc_smclike_adapter; + ppc->ppc_model = SMC_LIKE; + ppc->ppc_type = PPC_TYPE_SMCLIKE; if (bootverbose) printf(" ECP+EPP"); @@ -1050,7 +1135,14 @@ ppc_generic_detect(struct ppc_data *ppc, int chipset_mode) if (bootverbose) printf("\n"); - ppc->ppc_link.adapter->setmode(ppc->ppc_unit, chipset_mode); + switch (ppc->ppc_type) { + case PPC_TYPE_SMCLIKE: + ppc_smclike_setmode(ppc, chipset_mode); + break; + default: + ppc_generic_setmode(ppc, chipset_mode); + break; + } return (chipset_mode); } @@ -1063,6 +1155,7 @@ ppc_generic_detect(struct ppc_data *ppc, int chipset_mode) static int ppc_detect(struct ppc_data *ppc, int chipset_mode) { +#ifdef PPC_PROBE_CHIPSET int i, mode; /* list of supported chipsets */ @@ -1073,6 +1166,7 @@ ppc_detect(struct ppc_data *ppc, int chipset_mode) { ppc_generic_detect, NULL }; +#endif /* if can't find the port and mode not forced return error */ if (!ppc_detect_port(ppc) && chipset_mode == 0) @@ -1081,6 +1175,7 @@ ppc_detect(struct ppc_data *ppc, int chipset_mode) { /* assume centronics compatible mode is supported */ ppc->ppc_avm = PPB_COMPATIBLE; +#ifdef PPC_PROBE_CHIPSET /* we have to differenciate available chipset modes, * chipset running modes and IEEE-1284 operating modes * @@ -1089,9 +1184,11 @@ ppc_detect(struct ppc_data *ppc, int chipset_mode) { if (ppc->ppc_flags & 0x40) { if (bootverbose) printf("ppc: chipset forced to generic\n"); +#endif ppc->ppc_mode = ppc_generic_detect(ppc, chipset_mode); +#ifdef PPC_PROBE_CHIPSET } else { for (i=0; chipset_detect[i] != NULL; i++) { if ((mode = chipset_detect[i](ppc, chipset_mode)) != -1) { @@ -1100,6 +1197,7 @@ ppc_detect(struct ppc_data *ppc, int chipset_mode) { } } } +#endif /* configure/detect ECP FIFO */ if ((ppc->ppc_avm & PPB_ECP) && !(ppc->ppc_flags & 0x80)) @@ -1115,9 +1213,9 @@ ppc_detect(struct ppc_data *ppc, int chipset_mode) { * Microsequence mechanism is supposed to handle fast I/O operations. */ static int -ppc_exec_microseq(int unit, struct ppb_microseq **p_msq) +ppc_exec_microseq(device_t dev, struct ppb_microseq **p_msq) { - struct ppc_data *ppc = ppcdata[unit]; + struct ppc_data *ppc = DEVTOSOFTC(dev); struct ppb_microseq *mi; char cc, *p; int i, iter, len; @@ -1326,9 +1424,10 @@ ppc_exec_microseq(int unit, struct ppb_microseq **p_msq) } static void -ppcintr(int unit) +ppcintr(void *arg) { - struct ppc_data *ppc = ppcdata[unit]; + device_t dev = (device_t)arg; + struct ppc_data *ppc = (struct ppc_data *)device_get_softc(dev); u_char ctr, ecr, str; str = r_str(ppc); @@ -1341,8 +1440,6 @@ ppcintr(int unit) /* don't use ecp mode with IRQENABLE set */ if (ctr & IRQENABLE) { - /* call upper code */ - ppb_intr(&ppc->ppc_link); return; } @@ -1356,8 +1453,7 @@ ppcintr(int unit) w_ecr(ppc, ecr | PPC_nFAULT_INTR); ppc->ppc_irqstat &= ~PPC_IRQ_nFAULT; } else { - /* call upper code */ - ppb_intr(&ppc->ppc_link); + /* shall be handled by underlying layers XXX */ return; } } @@ -1397,14 +1493,13 @@ ppcintr(int unit) /* classic interrupt I/O */ ppc->ppc_irqstat &= ~PPC_IRQ_FIFO; - } return; } static int -ppc_read(int unit, char *buf, int len, int mode) +ppc_read(device_t dev, char *buf, int len, int mode) { return (EINVAL); } @@ -1417,9 +1512,9 @@ ppc_read(int unit, char *buf, int len, int mode) * EINVAL is returned */ static int -ppc_write(int unit, char *buf, int len, int how) +ppc_write(device_t dev, char *buf, int len, int how) { - struct ppc_data *ppc = ppcdata[unit]; + struct ppc_data *ppc = DEVTOSOFTC(dev); char ecr, ecr_sav, ctr, ctr_sav; int s, error = 0; int spin; @@ -1434,7 +1529,7 @@ ppc_write(int unit, char *buf, int len, int how) /* * Send buffer with DMA, FIFO and interrupts */ - if (ppc->ppc_avm & PPB_ECP) { + if ((ppc->ppc_avm & PPB_ECP) && (ppc->ppc_registered)) { if (ppc->ppc_dmachan >= 0) { @@ -1568,231 +1663,334 @@ error: return (error); } -/* - * Configure current operating mode - */ -static int -ppc_generic_setmode(int unit, int mode) +static void +ppc_reset_epp(device_t dev) { - struct ppc_data *ppc = ppcdata[unit]; - u_char ecr = 0; + struct ppc_data *ppc = DEVTOSOFTC(dev); + + ppc_reset_epp_timeout(ppc); - /* check if mode is available */ - if (mode && !(ppc->ppc_avm & mode)) - return (EINVAL); + return; +} - /* if ECP mode, configure ecr register */ - if (ppc->ppc_avm & PPB_ECP) { - /* return to byte mode (keeping direction bit), - * no interrupt, no DMA to be able to change to - * ECP - */ - w_ecr(ppc, PPC_ECR_RESET); - ecr = PPC_DISABLE_INTR; +static int +ppc_setmode(device_t dev, int mode) +{ + struct ppc_data *ppc = DEVTOSOFTC(dev); - if (mode & PPB_EPP) - return (EINVAL); - else if (mode & PPB_ECP) - /* select ECP mode */ - ecr |= PPC_ECR_ECP; - else if (mode & PPB_PS2) - /* select PS2 mode with ECP */ - ecr |= PPC_ECR_PS2; - else - /* select COMPATIBLE/NIBBLE mode */ - ecr |= PPC_ECR_STD; + switch (ppc->ppc_type) { + case PPC_TYPE_SMCLIKE: + return (ppc_smclike_setmode(ppc, mode)); + break; - w_ecr(ppc, ecr); + case PPC_TYPE_GENERIC: + default: + return (ppc_generic_setmode(ppc, mode)); + break; } - ppc->ppc_mode = mode; - - return (0); + /* not reached */ + return (ENXIO); } -/* - * The ppc driver is free to choose options like FIFO or DMA - * if ECP mode is available. - * - * The 'RAW' option allows the upper drivers to force the ppc mode - * even with FIFO, DMA available. - */ -int -ppc_smclike_setmode(int unit, int mode) +static int +ppc_probe(device_t dev) { - struct ppc_data *ppc = ppcdata[unit]; - u_char ecr = 0; - - /* check if mode is available */ - if (mode && !(ppc->ppc_avm & mode)) - return (EINVAL); - - /* if ECP mode, configure ecr register */ - if (ppc->ppc_avm & PPB_ECP) { - /* return to byte mode (keeping direction bit), - * no interrupt, no DMA to be able to change to - * ECP or EPP mode - */ - w_ecr(ppc, PPC_ECR_RESET); - ecr = PPC_DISABLE_INTR; + static short next_bios_ppc = 0; + struct ppc_data *ppc; + device_t parent; + int port; - if (mode & PPB_EPP) - /* select EPP mode */ - ecr |= PPC_ECR_EPP; - else if (mode & PPB_ECP) - /* select ECP mode */ - ecr |= PPC_ECR_ECP; - else if (mode & PPB_PS2) - /* select PS2 mode with ECP */ - ecr |= PPC_ECR_PS2; - else - /* select COMPATIBLE/NIBBLE mode */ - ecr |= PPC_ECR_STD; + device_set_desc(dev, "Parallel port"); - w_ecr(ppc, ecr); - } + /* XXX shall be connected to pnpbios - from Peter Wemm */ + if (isa_get_logicalid(dev)) + return ENXIO; - ppc->ppc_mode = mode; + parent = device_get_parent(dev); - return (0); -} - -/* - * EPP timeout, according to the PC87332 manual - * Semantics of clearing EPP timeout bit. - * PC87332 - reading SPP_STR does it... - * SMC - write 1 to EPP timeout bit XXX - * Others - (?) write 0 to EPP timeout bit - */ -static void -ppc_reset_epp_timeout(int unit) -{ - struct ppc_data *ppc = ppcdata[unit]; - register char r; + /* + * Allocate the ppc_data structure. + */ + ppc = DEVTOSOFTC(dev); + bzero(ppc, sizeof(struct ppc_data)); - r = r_str(ppc); - w_str(ppc, r | 0x1); - w_str(ppc, r & 0xfe); + ppc->rid_irq = ppc->rid_drq = ppc->rid_ioport = 0; + ppc->res_irq = ppc->res_drq = ppc->res_ioport = 0; - return; -} - -static int -ppcprobe(struct isa_device *dvp) -{ - static short next_bios_ppc = 0; - struct ppc_data *ppc; + /* retrieve ISA parameters */ + BUS_READ_IVAR(parent, dev, ISA_IVAR_PORT, &port); /* * If port not specified, use bios list. */ - if(dvp->id_iobase < 0) { + if (port < 0) { if((next_bios_ppc < BIOS_MAX_PPC) && (*(BIOS_PORTS+next_bios_ppc) != 0) ) { - dvp->id_iobase = *(BIOS_PORTS+next_bios_ppc++); + port = *(BIOS_PORTS+next_bios_ppc++); if (bootverbose) - printf("ppc: parallel port found at 0x%x\n", - dvp->id_iobase); - } else - return (0); + device_printf(dev, "parallel port found at 0x%x\n", + port); + } else { + device_printf(dev, "parallel port not found.\n"); + return ENXIO; + } } - - /* - * Port was explicitly specified. - * This allows probing of ports unknown to the BIOS. - */ - - /* - * Allocate the ppc_data structure. - */ - ppc = malloc(sizeof(struct ppc_data), M_DEVBUF, M_NOWAIT); - if (!ppc) { - printf("ppc: cannot malloc!\n"); + ppc->ppc_base = port; + + /* IO port is mandatory */ + ppc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, + &ppc->rid_ioport, port, port, + IO_LPTSIZE, RF_ACTIVE); + if (ppc->res_ioport == 0) { + device_printf(dev, "cannot reserve I/O port range\n"); goto error; } - bzero(ppc, sizeof(struct ppc_data)); - ppc->ppc_base = dvp->id_iobase; - ppc->ppc_unit = dvp->id_unit; - ppc->ppc_type = GENERIC; + ppc->ppc_flags = device_get_flags(dev); - /* store boot flags */ - ppc->ppc_flags = dvp->id_flags; + if (!(ppc->ppc_flags & 0x20)) { + ppc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &ppc->rid_irq, + 0ul, ~0ul, 1, RF_SHAREABLE); + ppc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ, &ppc->rid_drq, + 0ul, ~0ul, 1, RF_ACTIVE); + } - ppc->ppc_mode = PPB_COMPATIBLE; - ppc->ppc_epp = (dvp->id_flags & 0x10) >> 4; + if (ppc->res_irq) + BUS_READ_IVAR(parent, dev, ISA_IVAR_IRQ, &ppc->ppc_irq); + if (ppc->res_drq) + BUS_READ_IVAR(parent, dev, ISA_IVAR_DRQ, &ppc->ppc_dmachan); - /* - * XXX Try and detect if interrupts are working - */ - if (!(dvp->id_flags & 0x20) && dvp->id_irq) - ppc->ppc_irq = ffs(dvp->id_irq) - 1; + ppc->ppc_unit = device_get_unit(dev); + ppc->ppc_model = GENERIC; - ppc->ppc_dmachan = dvp->id_drq; + ppc->ppc_mode = PPB_COMPATIBLE; + ppc->ppc_epp = (ppc->ppc_flags & 0x10) >> 4; - ppcdata[ppc->ppc_unit] = ppc; - nppc ++; - - /* - * Link the Parallel Port Chipset (adapter) to - * the future ppbus. Default to a generic chipset - */ - ppc->ppc_link.adapter_unit = ppc->ppc_unit; - ppc->ppc_link.adapter = &ppc_generic_adapter; + ppc->ppc_type = PPC_TYPE_GENERIC; /* * Try to detect the chipset and its mode. */ - if (ppc_detect(ppc, dvp->id_flags & 0xf)) + if (ppc_detect(ppc, ppc->ppc_flags & 0xf)) goto error; - return (IO_LPTSIZE); + return (0); error: - return (0); + if (ppc->res_irq != 0) { + bus_release_resource(dev, SYS_RES_IRQ, ppc->rid_irq, + ppc->res_irq); + } + if (ppc->res_ioport != 0) { + bus_deactivate_resource(dev, SYS_RES_IOPORT, ppc->rid_ioport, + ppc->res_ioport); + bus_release_resource(dev, SYS_RES_IOPORT, ppc->rid_ioport, + ppc->res_ioport); + } + if (ppc->res_drq != 0) { + bus_deactivate_resource(dev, SYS_RES_DRQ, ppc->rid_drq, + ppc->res_drq); + bus_release_resource(dev, SYS_RES_DRQ, ppc->rid_drq, + ppc->res_drq); + } + return (ENXIO); } static int -ppcattach(struct isa_device *isdp) +ppc_attach(device_t dev) { - struct ppc_data *ppc = ppcdata[isdp->id_unit]; - struct ppb_data *ppbus; + struct ppc_data *ppc = DEVTOSOFTC(dev); - printf("ppc%d: %s chipset (%s) in %s mode%s\n", ppc->ppc_unit, - ppc_types[ppc->ppc_type], ppc_avms[ppc->ppc_avm], - ppc_modes[ppc->ppc_mode], (PPB_IS_EPP(ppc->ppc_mode)) ? - ppc_epp_protocol[ppc->ppc_epp] : ""); + device_t ppbus; + device_t parent = device_get_parent(dev); + device_printf(dev, "%s chipset (%s) in %s mode%s\n", + ppc_models[ppc->ppc_model], ppc_avms[ppc->ppc_avm], + ppc_modes[ppc->ppc_mode], (PPB_IS_EPP(ppc->ppc_mode)) ? + ppc_epp_protocol[ppc->ppc_epp] : ""); + if (ppc->ppc_fifo) - printf("ppc%d: FIFO with %d/%d/%d bytes threshold\n", - ppc->ppc_unit, ppc->ppc_fifo, ppc->ppc_wthr, - ppc->ppc_rthr); + device_printf(dev, "FIFO with %d/%d/%d bytes threshold\n", + ppc->ppc_fifo, ppc->ppc_wthr, ppc->ppc_rthr); - isdp->id_ointr = ppcintr; + 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); /* - * Prepare ppbus data area for upper level code. + * Probe the ppbus and attach devices found. */ - ppbus = ppb_alloc_bus(); + device_probe_and_attach(ppbus); - if (!ppbus) - return (0); + /* register the ppc interrupt handler as default */ + if (ppc->res_irq) { + /* default to the tty mask for registration */ /* XXX */ + if (BUS_SETUP_INTR(parent, dev, ppc->res_irq, INTR_TYPE_TTY, + ppcintr, dev, &ppc->intr_cookie) == 0) { - ppc->ppc_link.ppbus = ppbus; - ppbus->ppb_link = &ppc->ppc_link; + /* remember the ppcintr is registered */ + ppc->ppc_registered = 1; + } + } - if ((ppc->ppc_avm & PPB_ECP) && (ppc->ppc_dmachan > 0)) { + return (0); +} - /* acquire the DMA channel forever */ - isa_dma_acquire(ppc->ppc_dmachan); - isa_dmainit(ppc->ppc_dmachan, 1024); /* nlpt.BUFSIZE */ +static u_char +ppc_io(device_t ppcdev, int iop, u_char *addr, int cnt, u_char byte) +{ + struct ppc_data *ppc = DEVTOSOFTC(ppcdev); + switch (iop) { + case PPB_OUTSB_EPP: + outsb(ppc->ppc_base + PPC_EPP_DATA, addr, cnt); + break; + case PPB_OUTSW_EPP: + outsw(ppc->ppc_base + PPC_EPP_DATA, addr, cnt); + break; + case PPB_OUTSL_EPP: + outsl(ppc->ppc_base + PPC_EPP_DATA, addr, cnt); + break; + case PPB_INSB_EPP: + insb(ppc->ppc_base + PPC_EPP_DATA, addr, cnt); + break; + case PPB_INSW_EPP: + insw(ppc->ppc_base + PPC_EPP_DATA, addr, cnt); + break; + case PPB_INSL_EPP: + insl(ppc->ppc_base + PPC_EPP_DATA, addr, cnt); + break; + case PPB_RDTR: + return (r_dtr(ppc)); + break; + case PPB_RSTR: + return (r_str(ppc)); + break; + case PPB_RCTR: + return (r_ctr(ppc)); + break; + case PPB_REPP_A: + return (r_epp_A(ppc)); + break; + case PPB_REPP_D: + return (r_epp_D(ppc)); + break; + case PPB_RECR: + return (r_ecr(ppc)); + break; + case PPB_RFIFO: + return (r_fifo(ppc)); + break; + case PPB_WDTR: + w_dtr(ppc, byte); + break; + case PPB_WSTR: + w_str(ppc, byte); + break; + case PPB_WCTR: + w_ctr(ppc, byte); + break; + case PPB_WEPP_A: + w_epp_A(ppc, byte); + break; + case PPB_WEPP_D: + w_epp_D(ppc, byte); + break; + case PPB_WECR: + w_ecr(ppc, byte); + break; + case PPB_WFIFO: + w_fifo(ppc, byte); + break; + default: + panic("%s: unknown I/O operation", __FUNCTION__); + break; } - /* - * Probe the ppbus and attach devices found. - */ - ppb_attachdevs(ppbus); + return (0); /* not significative */ +} - return (1); +static int +ppc_read_ivar(device_t bus, device_t dev, int index, uintptr_t *val) +{ + struct ppc_data *ppc = (struct ppc_data *)device_get_softc(bus); + + switch (index) { + case PPC_IVAR_EPP_PROTO: + *val = (u_long)ppc->ppc_epp; + break; + case PPC_IVAR_IRQ: + BUS_READ_IVAR(device_get_parent(bus), bus, ISA_IVAR_IRQ, val); + break; + default: + return (ENOENT); + } + + return (0); +} + +/* + * Resource is useless here since ppbus devices' interrupt handlers are + * multiplexed to the same resource initially allocated by ppc + */ +static int +ppc_setup_intr(device_t bus, device_t child, struct resource *r, int flags, + void (*ihand)(void *), void *arg, void **cookiep) +{ + int error; + struct ppc_data *ppc = DEVTOSOFTC(bus); + + if (ppc->ppc_registered) { + /* XXX refuse registration if DMA is in progress */ + + /* first, unregister the default interrupt handler */ + if ((error = BUS_TEARDOWN_INTR(device_get_parent(bus), + bus, ppc->res_irq, ppc->intr_cookie))) + return (error); + +/* bus_deactivate_resource(bus, SYS_RES_IRQ, ppc->rid_irq, */ +/* ppc->res_irq); */ + + /* DMA/FIFO operation won't be possible anymore */ + ppc->ppc_registered = 0; + } + + /* pass registration to the upper layer, ignore the incoming resource */ + return (BUS_SETUP_INTR(device_get_parent(bus), child, + r, flags, ihand, arg, cookiep)); +} + +/* + * When no underlying device has a registered interrupt, register the ppc + * layer one + */ +static int +ppc_teardown_intr(device_t bus, device_t child, struct resource *r, void *ih) +{ + int error; + struct ppc_data *ppc = DEVTOSOFTC(bus); + device_t parent = device_get_parent(bus); + + /* pass unregistration to the upper layer */ + if ((error = BUS_TEARDOWN_INTR(parent, child, r, ih))) + return (error); + + /* default to the tty mask for registration */ /* XXX */ + if (ppc->ppc_irq && + !(error = BUS_SETUP_INTR(parent, bus, ppc->res_irq, + INTR_TYPE_TTY, ppcintr, bus, &ppc->intr_cookie))) { + + /* remember the ppcintr is registered */ + ppc->ppc_registered = 1; + } + + return (error); } + +DRIVER_MODULE(ppc, isa, ppc_driver, ppc_devclass, 0, 0); #endif diff --git a/sys/isa/ppcreg.h b/sys/isa/ppcreg.h index faecb97..ec17806 100644 --- a/sys/isa/ppcreg.h +++ b/sys/isa/ppcreg.h @@ -45,12 +45,19 @@ #define NS_PC87334 10 /* + * Parallel Port Chipset Type. SMC versus GENERIC (others) + */ +#define PPC_TYPE_SMCLIKE 0 +#define PPC_TYPE_GENERIC 1 + +/* * Generic structure to hold parallel port chipset info. */ struct ppc_data { int ppc_unit; - int ppc_type; + int ppc_model; /* chipset model if detected */ + int ppc_type; /* generic or smclike chipset type */ int ppc_mode; /* chipset current mode */ int ppc_avm; /* chipset available modes */ @@ -83,16 +90,22 @@ struct ppc_data { short ppc_wthr; /* writeIntrThresold */ short ppc_rthr; /* readIntrThresold */ -#define ppc_base ppc_link.base -#define ppc_epp ppc_link.epp_protocol -#define ppc_irq ppc_link.id_irq -#define ppc_subm ppc_link.submicroseq -#define ppc_ptr ppc_link.ptr -#define ppc_accum ppc_link.accum + char *ppc_ptr; /* microseq current pointer */ + int ppc_accum; /* microseq accumulator */ + int ppc_base; /* parallel port base address */ + int ppc_epp; /* EPP mode (1.7 or 1.9) */ + int ppc_irq; unsigned char ppc_flags; - struct ppb_link ppc_link; + device_t ppbus; /* parallel port chipset corresponding ppbus */ + + int rid_irq, rid_drq, rid_ioport; + struct resource *res_irq, *res_drq, *res_ioport; + + void *intr_cookie; + + int ppc_registered; /* 1 if ppcintr() is the registered interrupt */ }; /* |