167646539SMike Smith /*- 2ddd22fb7SNicolas Souchu * Copyright (c) 1997-2000 Nicolas Souchu 3c264e80fSNicolas Souchu * Copyright (c) 2001 Alcove - Nicolas Souchu 467646539SMike Smith * All rights reserved. 567646539SMike Smith * 667646539SMike Smith * Redistribution and use in source and binary forms, with or without 767646539SMike Smith * modification, are permitted provided that the following conditions 867646539SMike Smith * are met: 967646539SMike Smith * 1. Redistributions of source code must retain the above copyright 1067646539SMike Smith * notice, this list of conditions and the following disclaimer. 1167646539SMike Smith * 2. Redistributions in binary form must reproduce the above copyright 1267646539SMike Smith * notice, this list of conditions and the following disclaimer in the 1367646539SMike Smith * documentation and/or other materials provided with the distribution. 1467646539SMike Smith * 1567646539SMike Smith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1667646539SMike Smith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1767646539SMike Smith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1867646539SMike Smith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1967646539SMike Smith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2067646539SMike Smith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2167646539SMike Smith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2267646539SMike Smith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2367646539SMike Smith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2467646539SMike Smith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2567646539SMike Smith * SUCH DAMAGE. 2667646539SMike Smith * 27c3aac50fSPeter Wemm * $FreeBSD$ 2867646539SMike Smith * 2967646539SMike Smith */ 3067646539SMike Smith 310f210c92SNicolas Souchu #include "opt_ppc.h" 320f210c92SNicolas Souchu 3367646539SMike Smith #include <sys/param.h> 3467646539SMike Smith #include <sys/systm.h> 3554ad6085SNicolas Souchu #include <sys/kernel.h> 360f210c92SNicolas Souchu #include <sys/bus.h> 370f210c92SNicolas Souchu #include <sys/malloc.h> 3867646539SMike Smith 3967646539SMike Smith #include <vm/vm.h> 4067646539SMike Smith #include <vm/pmap.h> 410f210c92SNicolas Souchu #include <machine/clock.h> 420f210c92SNicolas Souchu #include <machine/bus.h> 430f210c92SNicolas Souchu #include <machine/resource.h> 440f210c92SNicolas Souchu #include <machine/vmparam.h> 450f210c92SNicolas Souchu #include <sys/rman.h> 4667646539SMike Smith 470f210c92SNicolas Souchu #include <isa/isareg.h> 480f210c92SNicolas Souchu #include <isa/isavar.h> 4967646539SMike Smith 5067646539SMike Smith #include <dev/ppbus/ppbconf.h> 5146f3ff79SMike Smith #include <dev/ppbus/ppb_msq.h> 5246f3ff79SMike Smith 53d64d73c9SDoug Rabson #include <isa/ppcreg.h> 5467646539SMike Smith 550f210c92SNicolas Souchu #include "ppbus_if.h" 56bc35c174SNicolas Souchu 57bc35c174SNicolas Souchu #define LOG_PPC(function, ppc, string) \ 58bc35c174SNicolas Souchu if (bootverbose) printf("%s: %s\n", function, string) 59bc35c174SNicolas Souchu 6067646539SMike Smith 610f210c92SNicolas Souchu #define DEVTOSOFTC(dev) ((struct ppc_data *)device_get_softc(dev)) 620f210c92SNicolas Souchu 630f210c92SNicolas Souchu devclass_t ppc_devclass; 640f210c92SNicolas Souchu 650f210c92SNicolas Souchu static int ppc_probe(device_t dev); 660f210c92SNicolas Souchu static int ppc_attach(device_t dev); 670f210c92SNicolas Souchu static int ppc_read_ivar(device_t bus, device_t dev, int index, uintptr_t *val); 680f210c92SNicolas Souchu 690f210c92SNicolas Souchu static void ppc_reset_epp(device_t); 700f210c92SNicolas Souchu static void ppc_ecp_sync(device_t); 710f210c92SNicolas Souchu static void ppcintr(void *arg); 720f210c92SNicolas Souchu 730f210c92SNicolas Souchu static int ppc_exec_microseq(device_t, struct ppb_microseq **); 740f210c92SNicolas Souchu static int ppc_setmode(device_t, int); 750f210c92SNicolas Souchu 760f210c92SNicolas Souchu static int ppc_read(device_t, char *, int, int); 770f210c92SNicolas Souchu static int ppc_write(device_t, char *, int, int); 780f210c92SNicolas Souchu 790f210c92SNicolas Souchu static u_char ppc_io(device_t, int, u_char *, int, u_char); 800f210c92SNicolas Souchu 810f210c92SNicolas Souchu static int ppc_setup_intr(device_t, device_t, struct resource *, int, 820f210c92SNicolas Souchu void (*)(void *), void *, void **); 830f210c92SNicolas Souchu static int ppc_teardown_intr(device_t, device_t, struct resource *, void *); 840f210c92SNicolas Souchu 850f210c92SNicolas Souchu static device_method_t ppc_methods[] = { 860f210c92SNicolas Souchu /* device interface */ 870f210c92SNicolas Souchu DEVMETHOD(device_probe, ppc_probe), 880f210c92SNicolas Souchu DEVMETHOD(device_attach, ppc_attach), 890f210c92SNicolas Souchu 900f210c92SNicolas Souchu /* bus interface */ 910f210c92SNicolas Souchu DEVMETHOD(bus_read_ivar, ppc_read_ivar), 920f210c92SNicolas Souchu DEVMETHOD(bus_setup_intr, ppc_setup_intr), 930f210c92SNicolas Souchu DEVMETHOD(bus_teardown_intr, ppc_teardown_intr), 940f210c92SNicolas Souchu DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), 950f210c92SNicolas Souchu 960f210c92SNicolas Souchu /* ppbus interface */ 970f210c92SNicolas Souchu DEVMETHOD(ppbus_io, ppc_io), 980f210c92SNicolas Souchu DEVMETHOD(ppbus_exec_microseq, ppc_exec_microseq), 990f210c92SNicolas Souchu DEVMETHOD(ppbus_reset_epp, ppc_reset_epp), 1000f210c92SNicolas Souchu DEVMETHOD(ppbus_setmode, ppc_setmode), 1010f210c92SNicolas Souchu DEVMETHOD(ppbus_ecp_sync, ppc_ecp_sync), 1020f210c92SNicolas Souchu DEVMETHOD(ppbus_read, ppc_read), 1030f210c92SNicolas Souchu DEVMETHOD(ppbus_write, ppc_write), 1040f210c92SNicolas Souchu 1050f210c92SNicolas Souchu { 0, 0 } 10667646539SMike Smith }; 10767646539SMike Smith 1080f210c92SNicolas Souchu static driver_t ppc_driver = { 1090f210c92SNicolas Souchu "ppc", 1100f210c92SNicolas Souchu ppc_methods, 1110f210c92SNicolas Souchu sizeof(struct ppc_data), 1120f210c92SNicolas Souchu }; 11367646539SMike Smith 1140f210c92SNicolas Souchu static char *ppc_models[] = { 11546f3ff79SMike Smith "SMC-like", "SMC FDC37C665GT", "SMC FDC37C666GT", "PC87332", "PC87306", 1166a5be862SDoug Rabson "82091AA", "Generic", "W83877F", "W83877AF", "Winbond", "PC87334", 117ac7ba926SDoug Rabson "SMC FDC37C935", "PC87303", 0 11867646539SMike Smith }; 11967646539SMike Smith 12046f3ff79SMike Smith /* list of available modes */ 12146f3ff79SMike Smith static char *ppc_avms[] = { 12246f3ff79SMike Smith "COMPATIBLE", "NIBBLE-only", "PS2-only", "PS2/NIBBLE", "EPP-only", 12346f3ff79SMike Smith "EPP/NIBBLE", "EPP/PS2", "EPP/PS2/NIBBLE", "ECP-only", 12446f3ff79SMike Smith "ECP/NIBBLE", "ECP/PS2", "ECP/PS2/NIBBLE", "ECP/EPP", 12546f3ff79SMike Smith "ECP/EPP/NIBBLE", "ECP/EPP/PS2", "ECP/EPP/PS2/NIBBLE", 0 12646f3ff79SMike Smith }; 12746f3ff79SMike Smith 12846f3ff79SMike Smith /* list of current executing modes 12946f3ff79SMike Smith * Note that few modes do not actually exist. 13046f3ff79SMike Smith */ 13167646539SMike Smith static char *ppc_modes[] = { 13246f3ff79SMike Smith "COMPATIBLE", "NIBBLE", "PS/2", "PS/2", "EPP", 13346f3ff79SMike Smith "EPP", "EPP", "EPP", "ECP", 13446f3ff79SMike Smith "ECP", "ECP+PS2", "ECP+PS2", "ECP+EPP", 13546f3ff79SMike Smith "ECP+EPP", "ECP+EPP", "ECP+EPP", 0 13667646539SMike Smith }; 13767646539SMike Smith 13867646539SMike Smith static char *ppc_epp_protocol[] = { " (EPP 1.9)", " (EPP 1.7)", 0 }; 13967646539SMike Smith 140d64d73c9SDoug Rabson #ifdef __i386__ 14167646539SMike Smith /* 14267646539SMike Smith * BIOS printer list - used by BIOS probe. 14367646539SMike Smith */ 14467646539SMike Smith #define BIOS_PPC_PORTS 0x408 14567646539SMike Smith #define BIOS_PORTS (short *)(KERNBASE+BIOS_PPC_PORTS) 14667646539SMike Smith #define BIOS_MAX_PPC 4 147d64d73c9SDoug Rabson #endif 14867646539SMike Smith 14967646539SMike Smith /* 15067646539SMike Smith * ppc_ecp_sync() XXX 15167646539SMike Smith */ 15267646539SMike Smith static void 1530f210c92SNicolas Souchu ppc_ecp_sync(device_t dev) { 15467646539SMike Smith 15567646539SMike Smith int i, r; 1560f210c92SNicolas Souchu struct ppc_data *ppc = DEVTOSOFTC(dev); 15767646539SMike Smith 158c264e80fSNicolas Souchu if (!(ppc->ppc_avm & PPB_ECP) && !(ppc->ppc_dtm & PPB_ECP)) 159bc35c174SNicolas Souchu return; 160bc35c174SNicolas Souchu 16167646539SMike Smith r = r_ecr(ppc); 162bc35c174SNicolas Souchu if ((r & 0xe0) != PPC_ECR_EPP) 16367646539SMike Smith return; 16467646539SMike Smith 16567646539SMike Smith for (i = 0; i < 100; i++) { 16667646539SMike Smith r = r_ecr(ppc); 16767646539SMike Smith if (r & 0x1) 16867646539SMike Smith return; 16967646539SMike Smith DELAY(100); 17067646539SMike Smith } 17167646539SMike Smith 17246f3ff79SMike Smith printf("ppc%d: ECP sync failed as data still " \ 1730f210c92SNicolas Souchu "present in FIFO.\n", ppc->ppc_unit); 17467646539SMike Smith 17567646539SMike Smith return; 17667646539SMike Smith } 17767646539SMike Smith 178bc35c174SNicolas Souchu /* 179bc35c174SNicolas Souchu * ppc_detect_fifo() 180bc35c174SNicolas Souchu * 181bc35c174SNicolas Souchu * Detect parallel port FIFO 182bc35c174SNicolas Souchu */ 183bc35c174SNicolas Souchu static int 184bc35c174SNicolas Souchu ppc_detect_fifo(struct ppc_data *ppc) 18567646539SMike Smith { 186bc35c174SNicolas Souchu char ecr_sav; 187bc35c174SNicolas Souchu char ctr_sav, ctr, cc; 188bc35c174SNicolas Souchu short i; 18967646539SMike Smith 190bc35c174SNicolas Souchu /* save registers */ 191bc35c174SNicolas Souchu ecr_sav = r_ecr(ppc); 192bc35c174SNicolas Souchu ctr_sav = r_ctr(ppc); 193bc35c174SNicolas Souchu 194bc35c174SNicolas Souchu /* enter ECP configuration mode, no interrupt, no DMA */ 195bc35c174SNicolas Souchu w_ecr(ppc, 0xf4); 196bc35c174SNicolas Souchu 197bc35c174SNicolas Souchu /* read PWord size - transfers in FIFO mode must be PWord aligned */ 198bc35c174SNicolas Souchu ppc->ppc_pword = (r_cnfgA(ppc) & PPC_PWORD_MASK); 199bc35c174SNicolas Souchu 200bc35c174SNicolas Souchu /* XXX 16 and 32 bits implementations not supported */ 201bc35c174SNicolas Souchu if (ppc->ppc_pword != PPC_PWORD_8) { 2026e551fb6SDavid E. O'Brien LOG_PPC(__func__, ppc, "PWord not supported"); 203bc35c174SNicolas Souchu goto error; 204bc35c174SNicolas Souchu } 205bc35c174SNicolas Souchu 206bc35c174SNicolas Souchu w_ecr(ppc, 0x34); /* byte mode, no interrupt, no DMA */ 207bc35c174SNicolas Souchu ctr = r_ctr(ppc); 208bc35c174SNicolas Souchu w_ctr(ppc, ctr | PCD); /* set direction to 1 */ 209bc35c174SNicolas Souchu 210bc35c174SNicolas Souchu /* enter ECP test mode, no interrupt, no DMA */ 211bc35c174SNicolas Souchu w_ecr(ppc, 0xd4); 212bc35c174SNicolas Souchu 213bc35c174SNicolas Souchu /* flush the FIFO */ 214bc35c174SNicolas Souchu for (i=0; i<1024; i++) { 215bc35c174SNicolas Souchu if (r_ecr(ppc) & PPC_FIFO_EMPTY) 216bc35c174SNicolas Souchu break; 217bc35c174SNicolas Souchu cc = r_fifo(ppc); 218bc35c174SNicolas Souchu } 219bc35c174SNicolas Souchu 220bc35c174SNicolas Souchu if (i >= 1024) { 2216e551fb6SDavid E. O'Brien LOG_PPC(__func__, ppc, "can't flush FIFO"); 222bc35c174SNicolas Souchu goto error; 223bc35c174SNicolas Souchu } 224bc35c174SNicolas Souchu 225bc35c174SNicolas Souchu /* enable interrupts, no DMA */ 226bc35c174SNicolas Souchu w_ecr(ppc, 0xd0); 227bc35c174SNicolas Souchu 228bc35c174SNicolas Souchu /* determine readIntrThreshold 229bc35c174SNicolas Souchu * fill the FIFO until serviceIntr is set 230bc35c174SNicolas Souchu */ 231bc35c174SNicolas Souchu for (i=0; i<1024; i++) { 232bc35c174SNicolas Souchu w_fifo(ppc, (char)i); 233bc35c174SNicolas Souchu if (!ppc->ppc_rthr && (r_ecr(ppc) & PPC_SERVICE_INTR)) { 234bc35c174SNicolas Souchu /* readThreshold reached */ 235bc35c174SNicolas Souchu ppc->ppc_rthr = i+1; 236bc35c174SNicolas Souchu } 237bc35c174SNicolas Souchu if (r_ecr(ppc) & PPC_FIFO_FULL) { 238bc35c174SNicolas Souchu ppc->ppc_fifo = i+1; 239bc35c174SNicolas Souchu break; 240bc35c174SNicolas Souchu } 241bc35c174SNicolas Souchu } 242bc35c174SNicolas Souchu 243bc35c174SNicolas Souchu if (i >= 1024) { 2446e551fb6SDavid E. O'Brien LOG_PPC(__func__, ppc, "can't fill FIFO"); 245bc35c174SNicolas Souchu goto error; 246bc35c174SNicolas Souchu } 247bc35c174SNicolas Souchu 248bc35c174SNicolas Souchu w_ecr(ppc, 0xd4); /* test mode, no interrupt, no DMA */ 249bc35c174SNicolas Souchu w_ctr(ppc, ctr & ~PCD); /* set direction to 0 */ 250bc35c174SNicolas Souchu w_ecr(ppc, 0xd0); /* enable interrupts */ 251bc35c174SNicolas Souchu 252bc35c174SNicolas Souchu /* determine writeIntrThreshold 253bc35c174SNicolas Souchu * empty the FIFO until serviceIntr is set 254bc35c174SNicolas Souchu */ 255bc35c174SNicolas Souchu for (i=ppc->ppc_fifo; i>0; i--) { 256bc35c174SNicolas Souchu if (r_fifo(ppc) != (char)(ppc->ppc_fifo-i)) { 2576e551fb6SDavid E. O'Brien LOG_PPC(__func__, ppc, "invalid data in FIFO"); 258bc35c174SNicolas Souchu goto error; 259bc35c174SNicolas Souchu } 260bc35c174SNicolas Souchu if (r_ecr(ppc) & PPC_SERVICE_INTR) { 261bc35c174SNicolas Souchu /* writeIntrThreshold reached */ 262bc35c174SNicolas Souchu ppc->ppc_wthr = ppc->ppc_fifo - i+1; 263bc35c174SNicolas Souchu } 264bc35c174SNicolas Souchu /* if FIFO empty before the last byte, error */ 265bc35c174SNicolas Souchu if (i>1 && (r_ecr(ppc) & PPC_FIFO_EMPTY)) { 2666e551fb6SDavid E. O'Brien LOG_PPC(__func__, ppc, "data lost in FIFO"); 267bc35c174SNicolas Souchu goto error; 268bc35c174SNicolas Souchu } 269bc35c174SNicolas Souchu } 270bc35c174SNicolas Souchu 271bc35c174SNicolas Souchu /* FIFO must be empty after the last byte */ 272bc35c174SNicolas Souchu if (!(r_ecr(ppc) & PPC_FIFO_EMPTY)) { 2736e551fb6SDavid E. O'Brien LOG_PPC(__func__, ppc, "can't empty the FIFO"); 274bc35c174SNicolas Souchu goto error; 275bc35c174SNicolas Souchu } 276bc35c174SNicolas Souchu 277bc35c174SNicolas Souchu w_ctr(ppc, ctr_sav); 278bc35c174SNicolas Souchu w_ecr(ppc, ecr_sav); 279bc35c174SNicolas Souchu 280bc35c174SNicolas Souchu return (0); 281bc35c174SNicolas Souchu 282bc35c174SNicolas Souchu error: 283bc35c174SNicolas Souchu w_ctr(ppc, ctr_sav); 284bc35c174SNicolas Souchu w_ecr(ppc, ecr_sav); 285bc35c174SNicolas Souchu 286bc35c174SNicolas Souchu return (EINVAL); 28767646539SMike Smith } 28867646539SMike Smith 28946f3ff79SMike Smith static int 29046f3ff79SMike Smith ppc_detect_port(struct ppc_data *ppc) 29146f3ff79SMike Smith { 29246f3ff79SMike Smith 29346f3ff79SMike Smith w_ctr(ppc, 0x0c); /* To avoid missing PS2 ports */ 29446f3ff79SMike Smith w_dtr(ppc, 0xaa); 295a7006f89SNicolas Souchu if (r_dtr(ppc) != 0xaa) 29646f3ff79SMike Smith return (0); 29746f3ff79SMike Smith 29846f3ff79SMike Smith return (1); 29946f3ff79SMike Smith } 30046f3ff79SMike Smith 30167646539SMike Smith /* 3020f210c92SNicolas Souchu * EPP timeout, according to the PC87332 manual 3030f210c92SNicolas Souchu * Semantics of clearing EPP timeout bit. 3040f210c92SNicolas Souchu * PC87332 - reading SPP_STR does it... 3050f210c92SNicolas Souchu * SMC - write 1 to EPP timeout bit XXX 3060f210c92SNicolas Souchu * Others - (?) write 0 to EPP timeout bit 3070f210c92SNicolas Souchu */ 3080f210c92SNicolas Souchu static void 3090f210c92SNicolas Souchu ppc_reset_epp_timeout(struct ppc_data *ppc) 3100f210c92SNicolas Souchu { 3110f210c92SNicolas Souchu register char r; 3120f210c92SNicolas Souchu 3130f210c92SNicolas Souchu r = r_str(ppc); 3140f210c92SNicolas Souchu w_str(ppc, r | 0x1); 3150f210c92SNicolas Souchu w_str(ppc, r & 0xfe); 3160f210c92SNicolas Souchu 3170f210c92SNicolas Souchu return; 3180f210c92SNicolas Souchu } 3190f210c92SNicolas Souchu 3200f210c92SNicolas Souchu static int 3210f210c92SNicolas Souchu ppc_check_epp_timeout(struct ppc_data *ppc) 3220f210c92SNicolas Souchu { 3230f210c92SNicolas Souchu ppc_reset_epp_timeout(ppc); 3240f210c92SNicolas Souchu 3250f210c92SNicolas Souchu return (!(r_str(ppc) & TIMEOUT)); 3260f210c92SNicolas Souchu } 3270f210c92SNicolas Souchu 3280f210c92SNicolas Souchu /* 3290f210c92SNicolas Souchu * Configure current operating mode 3300f210c92SNicolas Souchu */ 3310f210c92SNicolas Souchu static int 3320f210c92SNicolas Souchu ppc_generic_setmode(struct ppc_data *ppc, int mode) 3330f210c92SNicolas Souchu { 3340f210c92SNicolas Souchu u_char ecr = 0; 3350f210c92SNicolas Souchu 3360f210c92SNicolas Souchu /* check if mode is available */ 3370f210c92SNicolas Souchu if (mode && !(ppc->ppc_avm & mode)) 3380f210c92SNicolas Souchu return (EINVAL); 3390f210c92SNicolas Souchu 3400f210c92SNicolas Souchu /* if ECP mode, configure ecr register */ 341c264e80fSNicolas Souchu if ((ppc->ppc_avm & PPB_ECP) || (ppc->ppc_dtm & PPB_ECP)) { 3420f210c92SNicolas Souchu /* return to byte mode (keeping direction bit), 3430f210c92SNicolas Souchu * no interrupt, no DMA to be able to change to 3440f210c92SNicolas Souchu * ECP 3450f210c92SNicolas Souchu */ 3460f210c92SNicolas Souchu w_ecr(ppc, PPC_ECR_RESET); 3470f210c92SNicolas Souchu ecr = PPC_DISABLE_INTR; 3480f210c92SNicolas Souchu 3490f210c92SNicolas Souchu if (mode & PPB_EPP) 3500f210c92SNicolas Souchu return (EINVAL); 3510f210c92SNicolas Souchu else if (mode & PPB_ECP) 3520f210c92SNicolas Souchu /* select ECP mode */ 3530f210c92SNicolas Souchu ecr |= PPC_ECR_ECP; 3540f210c92SNicolas Souchu else if (mode & PPB_PS2) 3550f210c92SNicolas Souchu /* select PS2 mode with ECP */ 3560f210c92SNicolas Souchu ecr |= PPC_ECR_PS2; 3570f210c92SNicolas Souchu else 3580f210c92SNicolas Souchu /* select COMPATIBLE/NIBBLE mode */ 3590f210c92SNicolas Souchu ecr |= PPC_ECR_STD; 3600f210c92SNicolas Souchu 3610f210c92SNicolas Souchu w_ecr(ppc, ecr); 3620f210c92SNicolas Souchu } 3630f210c92SNicolas Souchu 3640f210c92SNicolas Souchu ppc->ppc_mode = mode; 3650f210c92SNicolas Souchu 3660f210c92SNicolas Souchu return (0); 3670f210c92SNicolas Souchu } 3680f210c92SNicolas Souchu 3690f210c92SNicolas Souchu /* 3700f210c92SNicolas Souchu * The ppc driver is free to choose options like FIFO or DMA 3710f210c92SNicolas Souchu * if ECP mode is available. 3720f210c92SNicolas Souchu * 3730f210c92SNicolas Souchu * The 'RAW' option allows the upper drivers to force the ppc mode 3740f210c92SNicolas Souchu * even with FIFO, DMA available. 3750f210c92SNicolas Souchu */ 3760f210c92SNicolas Souchu static int 3770f210c92SNicolas Souchu ppc_smclike_setmode(struct ppc_data *ppc, int mode) 3780f210c92SNicolas Souchu { 3790f210c92SNicolas Souchu u_char ecr = 0; 3800f210c92SNicolas Souchu 3810f210c92SNicolas Souchu /* check if mode is available */ 3820f210c92SNicolas Souchu if (mode && !(ppc->ppc_avm & mode)) 3830f210c92SNicolas Souchu return (EINVAL); 3840f210c92SNicolas Souchu 3850f210c92SNicolas Souchu /* if ECP mode, configure ecr register */ 386c264e80fSNicolas Souchu if ((ppc->ppc_avm & PPB_ECP) || (ppc->ppc_dtm & PPB_ECP)) { 3870f210c92SNicolas Souchu /* return to byte mode (keeping direction bit), 3880f210c92SNicolas Souchu * no interrupt, no DMA to be able to change to 3890f210c92SNicolas Souchu * ECP or EPP mode 3900f210c92SNicolas Souchu */ 3910f210c92SNicolas Souchu w_ecr(ppc, PPC_ECR_RESET); 3920f210c92SNicolas Souchu ecr = PPC_DISABLE_INTR; 3930f210c92SNicolas Souchu 3940f210c92SNicolas Souchu if (mode & PPB_EPP) 3950f210c92SNicolas Souchu /* select EPP mode */ 3960f210c92SNicolas Souchu ecr |= PPC_ECR_EPP; 3970f210c92SNicolas Souchu else if (mode & PPB_ECP) 3980f210c92SNicolas Souchu /* select ECP mode */ 3990f210c92SNicolas Souchu ecr |= PPC_ECR_ECP; 4000f210c92SNicolas Souchu else if (mode & PPB_PS2) 4010f210c92SNicolas Souchu /* select PS2 mode with ECP */ 4020f210c92SNicolas Souchu ecr |= PPC_ECR_PS2; 4030f210c92SNicolas Souchu else 4040f210c92SNicolas Souchu /* select COMPATIBLE/NIBBLE mode */ 4050f210c92SNicolas Souchu ecr |= PPC_ECR_STD; 4060f210c92SNicolas Souchu 4070f210c92SNicolas Souchu w_ecr(ppc, ecr); 4080f210c92SNicolas Souchu } 4090f210c92SNicolas Souchu 4100f210c92SNicolas Souchu ppc->ppc_mode = mode; 4110f210c92SNicolas Souchu 4120f210c92SNicolas Souchu return (0); 4130f210c92SNicolas Souchu } 4140f210c92SNicolas Souchu 4150f210c92SNicolas Souchu #ifdef PPC_PROBE_CHIPSET 4160f210c92SNicolas Souchu /* 41767646539SMike Smith * ppc_pc873xx_detect 41867646539SMike Smith * 41967646539SMike Smith * Probe for a Natsemi PC873xx-family part. 42067646539SMike Smith * 42167646539SMike Smith * References in this function are to the National Semiconductor 42267646539SMike Smith * PC87332 datasheet TL/C/11930, May 1995 revision. 42367646539SMike Smith */ 42467646539SMike Smith static int pc873xx_basetab[] = {0x0398, 0x026e, 0x015c, 0x002e, 0}; 42567646539SMike Smith static int pc873xx_porttab[] = {0x0378, 0x03bc, 0x0278, 0}; 426af548787SNicolas Souchu static int pc873xx_irqtab[] = {5, 7, 5, 0}; 427af548787SNicolas Souchu 428af548787SNicolas Souchu static int pc873xx_regstab[] = { 429af548787SNicolas Souchu PC873_FER, PC873_FAR, PC873_PTR, 430af548787SNicolas Souchu PC873_FCR, PC873_PCR, PC873_PMC, 431af548787SNicolas Souchu PC873_TUP, PC873_SID, PC873_PNP0, 432af548787SNicolas Souchu PC873_PNP1, PC873_LPTBA, -1 433af548787SNicolas Souchu }; 434af548787SNicolas Souchu 435af548787SNicolas Souchu static char *pc873xx_rnametab[] = { 436af548787SNicolas Souchu "FER", "FAR", "PTR", "FCR", "PCR", 437af548787SNicolas Souchu "PMC", "TUP", "SID", "PNP0", "PNP1", 438af548787SNicolas Souchu "LPTBA", NULL 439af548787SNicolas Souchu }; 44067646539SMike Smith 44167646539SMike Smith static int 44246f3ff79SMike Smith ppc_pc873xx_detect(struct ppc_data *ppc, int chipset_mode) /* XXX mode never forced */ 44367646539SMike Smith { 44467646539SMike Smith static int index = 0; 445f1d19042SArchie Cobbs int idport, irq; 446af548787SNicolas Souchu int ptr, pcr, val, i; 44767646539SMike Smith 44867646539SMike Smith while ((idport = pc873xx_basetab[index++])) { 44967646539SMike Smith 45067646539SMike Smith /* XXX should check first to see if this location is already claimed */ 45167646539SMike Smith 45267646539SMike Smith /* 453af548787SNicolas Souchu * Pull the 873xx through the power-on ID cycle (2.2,1.). 454af548787SNicolas Souchu * We can't use this to locate the chip as it may already have 455af548787SNicolas Souchu * been used by the BIOS. 45667646539SMike Smith */ 457af548787SNicolas Souchu (void)inb(idport); (void)inb(idport); 458af548787SNicolas Souchu (void)inb(idport); (void)inb(idport); 45967646539SMike Smith 46067646539SMike Smith /* 46167646539SMike Smith * Read the SID byte. Possible values are : 46267646539SMike Smith * 463af548787SNicolas Souchu * 01010xxx PC87334 46467646539SMike Smith * 0001xxxx PC87332 46567646539SMike Smith * 01110xxx PC87306 466ac7ba926SDoug Rabson * 00110xxx PC87303 46767646539SMike Smith */ 46867646539SMike Smith outb(idport, PC873_SID); 46967646539SMike Smith val = inb(idport + 1); 47067646539SMike Smith if ((val & 0xf0) == 0x10) { 4710f210c92SNicolas Souchu ppc->ppc_model = NS_PC87332; 47267646539SMike Smith } else if ((val & 0xf8) == 0x70) { 4730f210c92SNicolas Souchu ppc->ppc_model = NS_PC87306; 474af548787SNicolas Souchu } else if ((val & 0xf8) == 0x50) { 4750f210c92SNicolas Souchu ppc->ppc_model = NS_PC87334; 476ac7ba926SDoug Rabson } else if ((val & 0xf8) == 0x40) { /* Should be 0x30 by the 477ac7ba926SDoug Rabson documentation, but probing 478ac7ba926SDoug Rabson yielded 0x40... */ 479ac7ba926SDoug Rabson ppc->ppc_model = NS_PC87303; 48067646539SMike Smith } else { 48167646539SMike Smith if (bootverbose && (val != 0xff)) 48267646539SMike Smith printf("PC873xx probe at 0x%x got unknown ID 0x%x\n", idport, val); 48367646539SMike Smith continue ; /* not recognised */ 48467646539SMike Smith } 48567646539SMike Smith 486af548787SNicolas Souchu /* print registers */ 487af548787SNicolas Souchu if (bootverbose) { 488af548787SNicolas Souchu printf("PC873xx"); 489af548787SNicolas Souchu for (i=0; pc873xx_regstab[i] != -1; i++) { 490af548787SNicolas Souchu outb(idport, pc873xx_regstab[i]); 491af548787SNicolas Souchu printf(" %s=0x%x", pc873xx_rnametab[i], 492af548787SNicolas Souchu inb(idport + 1) & 0xff); 493af548787SNicolas Souchu } 494af548787SNicolas Souchu printf("\n"); 495af548787SNicolas Souchu } 496af548787SNicolas Souchu 49767646539SMike Smith /* 49867646539SMike Smith * We think we have one. Is it enabled and where we want it to be? 49967646539SMike Smith */ 50067646539SMike Smith outb(idport, PC873_FER); 50167646539SMike Smith val = inb(idport + 1); 50267646539SMike Smith if (!(val & PC873_PPENABLE)) { 50367646539SMike Smith if (bootverbose) 50467646539SMike Smith printf("PC873xx parallel port disabled\n"); 50567646539SMike Smith continue; 50667646539SMike Smith } 50767646539SMike Smith outb(idport, PC873_FAR); 508ac7ba926SDoug Rabson val = inb(idport + 1); 50967646539SMike Smith /* XXX we should create a driver instance for every port found */ 510ac7ba926SDoug Rabson if (pc873xx_porttab[val & 0x3] != ppc->ppc_base) { 511ac7ba926SDoug Rabson 512ac7ba926SDoug Rabson /* First try to change the port address to that requested... */ 513ac7ba926SDoug Rabson 514ac7ba926SDoug Rabson switch(ppc->ppc_base) { 515ac7ba926SDoug Rabson case 0x378: 516ac7ba926SDoug Rabson val &= 0xfc; 517ac7ba926SDoug Rabson break; 518ac7ba926SDoug Rabson 519ac7ba926SDoug Rabson case 0x3bc: 520ac7ba926SDoug Rabson val &= 0xfd; 521ac7ba926SDoug Rabson break; 522ac7ba926SDoug Rabson 523ac7ba926SDoug Rabson case 0x278: 524ac7ba926SDoug Rabson val &= 0xfe; 525ac7ba926SDoug Rabson break; 526ac7ba926SDoug Rabson 527ac7ba926SDoug Rabson default: 528ac7ba926SDoug Rabson val &= 0xfd; 529ac7ba926SDoug Rabson break; 530ac7ba926SDoug Rabson } 531ac7ba926SDoug Rabson 532ac7ba926SDoug Rabson outb(idport, PC873_FAR); 533ac7ba926SDoug Rabson outb(idport + 1, val); 534ac7ba926SDoug Rabson outb(idport + 1, val); 535ac7ba926SDoug Rabson 536ac7ba926SDoug Rabson /* Check for success by reading back the value we supposedly 537ac7ba926SDoug Rabson wrote and comparing...*/ 538ac7ba926SDoug Rabson 539ac7ba926SDoug Rabson outb(idport, PC873_FAR); 540ac7ba926SDoug Rabson val = inb(idport + 1) & 0x3; 541ac7ba926SDoug Rabson 542ac7ba926SDoug Rabson /* If we fail, report the failure... */ 543ac7ba926SDoug Rabson 54467646539SMike Smith if (pc873xx_porttab[val] != ppc->ppc_base) { 54567646539SMike Smith if (bootverbose) 54667646539SMike Smith printf("PC873xx at 0x%x not for driver at port 0x%x\n", 54767646539SMike Smith pc873xx_porttab[val], ppc->ppc_base); 548ac7ba926SDoug Rabson } 54967646539SMike Smith continue; 55067646539SMike Smith } 55167646539SMike Smith 55267646539SMike Smith outb(idport, PC873_PTR); 553af548787SNicolas Souchu ptr = inb(idport + 1); 554af548787SNicolas Souchu 555af548787SNicolas Souchu /* get irq settings */ 556af548787SNicolas Souchu if (ppc->ppc_base == 0x378) 557af548787SNicolas Souchu irq = (ptr & PC873_LPTBIRQ7) ? 7 : 5; 558af548787SNicolas Souchu else 559af548787SNicolas Souchu irq = pc873xx_irqtab[val]; 560af548787SNicolas Souchu 56167646539SMike Smith if (bootverbose) 562af548787SNicolas Souchu printf("PC873xx irq %d at 0x%x\n", irq, ppc->ppc_base); 56367646539SMike Smith 564af548787SNicolas Souchu /* 565af548787SNicolas Souchu * Check if irq settings are correct 566af548787SNicolas Souchu */ 567af548787SNicolas Souchu if (irq != ppc->ppc_irq) { 568af548787SNicolas Souchu /* 569af548787SNicolas Souchu * If the chipset is not locked and base address is 0x378, 570af548787SNicolas Souchu * we have another chance 571af548787SNicolas Souchu */ 572af548787SNicolas Souchu if (ppc->ppc_base == 0x378 && !(ptr & PC873_CFGLOCK)) { 573af548787SNicolas Souchu if (ppc->ppc_irq == 7) { 574af548787SNicolas Souchu outb(idport + 1, (ptr | PC873_LPTBIRQ7)); 575af548787SNicolas Souchu outb(idport + 1, (ptr | PC873_LPTBIRQ7)); 576af548787SNicolas Souchu } else { 577af548787SNicolas Souchu outb(idport + 1, (ptr & ~PC873_LPTBIRQ7)); 578af548787SNicolas Souchu outb(idport + 1, (ptr & ~PC873_LPTBIRQ7)); 57967646539SMike Smith } 580af548787SNicolas Souchu if (bootverbose) 581af548787SNicolas Souchu printf("PC873xx irq set to %d\n", ppc->ppc_irq); 582af548787SNicolas Souchu } else { 583af548787SNicolas Souchu if (bootverbose) 584af548787SNicolas Souchu printf("PC873xx sorry, can't change irq setting\n"); 58567646539SMike Smith } 58667646539SMike Smith } else { 58767646539SMike Smith if (bootverbose) 588af548787SNicolas Souchu printf("PC873xx irq settings are correct\n"); 58967646539SMike Smith } 59067646539SMike Smith 59167646539SMike Smith outb(idport, PC873_PCR); 592af548787SNicolas Souchu pcr = inb(idport + 1); 593af548787SNicolas Souchu 594af548787SNicolas Souchu if ((ptr & PC873_CFGLOCK) || !chipset_mode) { 595af548787SNicolas Souchu if (bootverbose) 596af548787SNicolas Souchu printf("PC873xx %s", (ptr & PC873_CFGLOCK)?"locked":"unlocked"); 597af548787SNicolas Souchu 598af548787SNicolas Souchu ppc->ppc_avm |= PPB_NIBBLE; 599af548787SNicolas Souchu if (bootverbose) 600af548787SNicolas Souchu printf(", NIBBLE"); 601af548787SNicolas Souchu 602af548787SNicolas Souchu if (pcr & PC873_EPPEN) { 603af548787SNicolas Souchu ppc->ppc_avm |= PPB_EPP; 604af548787SNicolas Souchu 605af548787SNicolas Souchu if (bootverbose) 606af548787SNicolas Souchu printf(", EPP"); 607af548787SNicolas Souchu 608af548787SNicolas Souchu if (pcr & PC873_EPP19) 609af548787SNicolas Souchu ppc->ppc_epp = EPP_1_9; 610af548787SNicolas Souchu else 611af548787SNicolas Souchu ppc->ppc_epp = EPP_1_7; 612af548787SNicolas Souchu 6130f210c92SNicolas Souchu if ((ppc->ppc_model == NS_PC87332) && bootverbose) { 614af548787SNicolas Souchu outb(idport, PC873_PTR); 615af548787SNicolas Souchu ptr = inb(idport + 1); 616af548787SNicolas Souchu if (ptr & PC873_EPPRDIR) 617af548787SNicolas Souchu printf(", Regular mode"); 618af548787SNicolas Souchu else 619af548787SNicolas Souchu printf(", Automatic mode"); 620af548787SNicolas Souchu } 621af548787SNicolas Souchu } else if (pcr & PC873_ECPEN) { 622af548787SNicolas Souchu ppc->ppc_avm |= PPB_ECP; 623af548787SNicolas Souchu if (bootverbose) 624af548787SNicolas Souchu printf(", ECP"); 625af548787SNicolas Souchu 626af548787SNicolas Souchu if (pcr & PC873_ECPCLK) { /* XXX */ 627af548787SNicolas Souchu ppc->ppc_avm |= PPB_PS2; 628af548787SNicolas Souchu if (bootverbose) 629af548787SNicolas Souchu printf(", PS/2"); 630af548787SNicolas Souchu } 631af548787SNicolas Souchu } else { 632af548787SNicolas Souchu outb(idport, PC873_PTR); 633af548787SNicolas Souchu ptr = inb(idport + 1); 634af548787SNicolas Souchu if (ptr & PC873_EXTENDED) { 635af548787SNicolas Souchu ppc->ppc_avm |= PPB_SPP; 636af548787SNicolas Souchu if (bootverbose) 637af548787SNicolas Souchu printf(", SPP"); 638af548787SNicolas Souchu } 639af548787SNicolas Souchu } 640af548787SNicolas Souchu } else { 641af548787SNicolas Souchu if (bootverbose) 642af548787SNicolas Souchu printf("PC873xx unlocked"); 643af548787SNicolas Souchu 644af548787SNicolas Souchu if (chipset_mode & PPB_ECP) { 645af548787SNicolas Souchu if ((chipset_mode & PPB_EPP) && bootverbose) 646af548787SNicolas Souchu printf(", ECP+EPP not supported"); 647af548787SNicolas Souchu 648af548787SNicolas Souchu pcr &= ~PC873_EPPEN; 649af548787SNicolas Souchu pcr |= (PC873_ECPEN | PC873_ECPCLK); /* XXX */ 650af548787SNicolas Souchu outb(idport + 1, pcr); 651af548787SNicolas Souchu outb(idport + 1, pcr); 652af548787SNicolas Souchu 653af548787SNicolas Souchu if (bootverbose) 654af548787SNicolas Souchu printf(", ECP"); 655af548787SNicolas Souchu 656af548787SNicolas Souchu } else if (chipset_mode & PPB_EPP) { 657af548787SNicolas Souchu pcr &= ~(PC873_ECPEN | PC873_ECPCLK); 658af548787SNicolas Souchu pcr |= (PC873_EPPEN | PC873_EPP19); 659af548787SNicolas Souchu outb(idport + 1, pcr); 660af548787SNicolas Souchu outb(idport + 1, pcr); 661af548787SNicolas Souchu 662af548787SNicolas Souchu ppc->ppc_epp = EPP_1_9; /* XXX */ 663af548787SNicolas Souchu 664af548787SNicolas Souchu if (bootverbose) 665af548787SNicolas Souchu printf(", EPP1.9"); 66667646539SMike Smith 66767646539SMike Smith /* enable automatic direction turnover */ 6680f210c92SNicolas Souchu if (ppc->ppc_model == NS_PC87332) { 66967646539SMike Smith outb(idport, PC873_PTR); 670af548787SNicolas Souchu ptr = inb(idport + 1); 671af548787SNicolas Souchu ptr &= ~PC873_EPPRDIR; 672af548787SNicolas Souchu outb(idport + 1, ptr); 673af548787SNicolas Souchu outb(idport + 1, ptr); 67467646539SMike Smith 67567646539SMike Smith if (bootverbose) 676af548787SNicolas Souchu printf(", Automatic mode"); 67767646539SMike Smith } 678af548787SNicolas Souchu } else { 679af548787SNicolas Souchu pcr &= ~(PC873_ECPEN | PC873_ECPCLK | PC873_EPPEN); 680af548787SNicolas Souchu outb(idport + 1, pcr); 681af548787SNicolas Souchu outb(idport + 1, pcr); 682af548787SNicolas Souchu 683af548787SNicolas Souchu /* configure extended bit in PTR */ 684af548787SNicolas Souchu outb(idport, PC873_PTR); 685af548787SNicolas Souchu ptr = inb(idport + 1); 686af548787SNicolas Souchu 687af548787SNicolas Souchu if (chipset_mode & PPB_PS2) { 688af548787SNicolas Souchu ptr |= PC873_EXTENDED; 689af548787SNicolas Souchu 690af548787SNicolas Souchu if (bootverbose) 691af548787SNicolas Souchu printf(", PS/2"); 692af548787SNicolas Souchu 693af548787SNicolas Souchu } else { 694af548787SNicolas Souchu /* default to NIBBLE mode */ 695af548787SNicolas Souchu ptr &= ~PC873_EXTENDED; 696af548787SNicolas Souchu 697af548787SNicolas Souchu if (bootverbose) 698af548787SNicolas Souchu printf(", NIBBLE"); 69967646539SMike Smith } 700af548787SNicolas Souchu outb(idport + 1, ptr); 701af548787SNicolas Souchu outb(idport + 1, ptr); 702af548787SNicolas Souchu } 703af548787SNicolas Souchu 704af548787SNicolas Souchu ppc->ppc_avm = chipset_mode; 705af548787SNicolas Souchu } 706af548787SNicolas Souchu 707af548787SNicolas Souchu if (bootverbose) 708af548787SNicolas Souchu printf("\n"); 709af548787SNicolas Souchu 7100f210c92SNicolas Souchu ppc->ppc_type = PPC_TYPE_GENERIC; 7110f210c92SNicolas Souchu ppc_generic_setmode(ppc, chipset_mode); 71246f3ff79SMike Smith 71346f3ff79SMike Smith return(chipset_mode); 71467646539SMike Smith } 71546f3ff79SMike Smith return(-1); 71667646539SMike Smith } 71767646539SMike Smith 71867646539SMike Smith /* 71967646539SMike Smith * ppc_smc37c66xgt_detect 72067646539SMike Smith * 72167646539SMike Smith * SMC FDC37C66xGT configuration. 72267646539SMike Smith */ 72367646539SMike Smith static int 72446f3ff79SMike Smith ppc_smc37c66xgt_detect(struct ppc_data *ppc, int chipset_mode) 72567646539SMike Smith { 72667646539SMike Smith int s, i; 727c9ab0738SNicolas Souchu u_char r; 72867646539SMike Smith int type = -1; 72967646539SMike Smith int csr = SMC66x_CSR; /* initial value is 0x3F0 */ 73067646539SMike Smith 73167646539SMike Smith int port_address[] = { -1 /* disabled */ , 0x3bc, 0x378, 0x278 }; 73267646539SMike Smith 73367646539SMike Smith 73467646539SMike Smith #define cio csr+1 /* config IO port is either 0x3F1 or 0x371 */ 73567646539SMike Smith 73667646539SMike Smith /* 73767646539SMike Smith * Detection: enter configuration mode and read CRD register. 73867646539SMike Smith */ 73967646539SMike Smith 74067646539SMike Smith s = splhigh(); 74167646539SMike Smith outb(csr, SMC665_iCODE); 74267646539SMike Smith outb(csr, SMC665_iCODE); 74367646539SMike Smith splx(s); 74467646539SMike Smith 74567646539SMike Smith outb(csr, 0xd); 74667646539SMike Smith if (inb(cio) == 0x65) { 74767646539SMike Smith type = SMC_37C665GT; 74867646539SMike Smith goto config; 74967646539SMike Smith } 75067646539SMike Smith 75167646539SMike Smith for (i = 0; i < 2; i++) { 75267646539SMike Smith s = splhigh(); 75367646539SMike Smith outb(csr, SMC666_iCODE); 75467646539SMike Smith outb(csr, SMC666_iCODE); 75567646539SMike Smith splx(s); 75667646539SMike Smith 75767646539SMike Smith outb(csr, 0xd); 75867646539SMike Smith if (inb(cio) == 0x66) { 75967646539SMike Smith type = SMC_37C666GT; 76067646539SMike Smith break; 76167646539SMike Smith } 76267646539SMike Smith 76367646539SMike Smith /* Another chance, CSR may be hard-configured to be at 0x370 */ 76467646539SMike Smith csr = SMC666_CSR; 76567646539SMike Smith } 76667646539SMike Smith 76767646539SMike Smith config: 76867646539SMike Smith /* 76967646539SMike Smith * If chipset not found, do not continue. 77067646539SMike Smith */ 77167646539SMike Smith if (type == -1) 77246f3ff79SMike Smith return (-1); 77367646539SMike Smith 77467646539SMike Smith /* select CR1 */ 77567646539SMike Smith outb(csr, 0x1); 77667646539SMike Smith 77767646539SMike Smith /* read the port's address: bits 0 and 1 of CR1 */ 77867646539SMike Smith r = inb(cio) & SMC_CR1_ADDR; 779c9ab0738SNicolas Souchu if (port_address[(int)r] != ppc->ppc_base) 78046f3ff79SMike Smith return (-1); 78167646539SMike Smith 7820f210c92SNicolas Souchu ppc->ppc_model = type; 78367646539SMike Smith 78467646539SMike Smith /* 78567646539SMike Smith * CR1 and CR4 registers bits 3 and 0/1 for mode configuration 78646f3ff79SMike Smith * If SPP mode is detected, try to set ECP+EPP mode 78767646539SMike Smith */ 78867646539SMike Smith 78946f3ff79SMike Smith if (bootverbose) { 79046f3ff79SMike Smith outb(csr, 0x1); 79141990851SNicolas Souchu printf("ppc%d: SMC registers CR1=0x%x", ppc->ppc_unit, 79254ad6085SNicolas Souchu inb(cio) & 0xff); 79346f3ff79SMike Smith 79446f3ff79SMike Smith outb(csr, 0x4); 79546f3ff79SMike Smith printf(" CR4=0x%x", inb(cio) & 0xff); 79646f3ff79SMike Smith } 79746f3ff79SMike Smith 79846f3ff79SMike Smith /* select CR1 */ 79967646539SMike Smith outb(csr, 0x1); 80067646539SMike Smith 80146f3ff79SMike Smith if (!chipset_mode) { 80267646539SMike Smith /* autodetect mode */ 80367646539SMike Smith 80446f3ff79SMike Smith /* 666GT is ~certainly~ hardwired to an extended ECP+EPP mode */ 80546f3ff79SMike Smith if (type == SMC_37C666GT) { 80646f3ff79SMike Smith ppc->ppc_avm |= PPB_ECP | PPB_EPP | PPB_SPP; 807edcfcf27SNicolas Souchu if (bootverbose) 808edcfcf27SNicolas Souchu printf(" configuration hardwired, supposing " \ 809edcfcf27SNicolas Souchu "ECP+EPP SPP"); 81067646539SMike Smith 81146f3ff79SMike Smith } else 81246f3ff79SMike Smith if ((inb(cio) & SMC_CR1_MODE) == 0) { 81367646539SMike Smith /* already in extended parallel port mode, read CR4 */ 81467646539SMike Smith outb(csr, 0x4); 81567646539SMike Smith r = (inb(cio) & SMC_CR4_EMODE); 81667646539SMike Smith 81767646539SMike Smith switch (r) { 81867646539SMike Smith case SMC_SPP: 81946f3ff79SMike Smith ppc->ppc_avm |= PPB_SPP; 820edcfcf27SNicolas Souchu if (bootverbose) 821edcfcf27SNicolas Souchu printf(" SPP"); 82267646539SMike Smith break; 82367646539SMike Smith 82467646539SMike Smith case SMC_EPPSPP: 82546f3ff79SMike Smith ppc->ppc_avm |= PPB_EPP | PPB_SPP; 826edcfcf27SNicolas Souchu if (bootverbose) 827edcfcf27SNicolas Souchu printf(" EPP SPP"); 82867646539SMike Smith break; 82967646539SMike Smith 83067646539SMike Smith case SMC_ECP: 83146f3ff79SMike Smith ppc->ppc_avm |= PPB_ECP | PPB_SPP; 832edcfcf27SNicolas Souchu if (bootverbose) 833edcfcf27SNicolas Souchu printf(" ECP SPP"); 83467646539SMike Smith break; 83567646539SMike Smith 83667646539SMike Smith case SMC_ECPEPP: 83746f3ff79SMike Smith ppc->ppc_avm |= PPB_ECP | PPB_EPP | PPB_SPP; 838edcfcf27SNicolas Souchu if (bootverbose) 839edcfcf27SNicolas Souchu printf(" ECP+EPP SPP"); 84067646539SMike Smith break; 84167646539SMike Smith } 84246f3ff79SMike Smith } else { 84346f3ff79SMike Smith /* not an extended port mode */ 84446f3ff79SMike Smith ppc->ppc_avm |= PPB_SPP; 845edcfcf27SNicolas Souchu if (bootverbose) 846edcfcf27SNicolas Souchu printf(" SPP"); 84767646539SMike Smith } 84846f3ff79SMike Smith 84967646539SMike Smith } else { 85067646539SMike Smith /* mode forced */ 85154ad6085SNicolas Souchu ppc->ppc_avm = chipset_mode; 85267646539SMike Smith 85346f3ff79SMike Smith /* 666GT is ~certainly~ hardwired to an extended ECP+EPP mode */ 85467646539SMike Smith if (type == SMC_37C666GT) 85567646539SMike Smith goto end_detect; 85667646539SMike Smith 85767646539SMike Smith r = inb(cio); 85846f3ff79SMike Smith if ((chipset_mode & (PPB_ECP | PPB_EPP)) == 0) { 85946f3ff79SMike Smith /* do not use ECP when the mode is not forced to */ 86067646539SMike Smith outb(cio, r | SMC_CR1_MODE); 861edcfcf27SNicolas Souchu if (bootverbose) 862edcfcf27SNicolas Souchu printf(" SPP"); 86367646539SMike Smith } else { 86467646539SMike Smith /* an extended mode is selected */ 86567646539SMike Smith outb(cio, r & ~SMC_CR1_MODE); 86667646539SMike Smith 86767646539SMike Smith /* read CR4 register and reset mode field */ 86867646539SMike Smith outb(csr, 0x4); 86967646539SMike Smith r = inb(cio) & ~SMC_CR4_EMODE; 87067646539SMike Smith 87146f3ff79SMike Smith if (chipset_mode & PPB_ECP) { 87246f3ff79SMike Smith if (chipset_mode & PPB_EPP) { 87367646539SMike Smith outb(cio, r | SMC_ECPEPP); 874edcfcf27SNicolas Souchu if (bootverbose) 875edcfcf27SNicolas Souchu printf(" ECP+EPP"); 87646f3ff79SMike Smith } else { 87746f3ff79SMike Smith outb(cio, r | SMC_ECP); 878edcfcf27SNicolas Souchu if (bootverbose) 879edcfcf27SNicolas Souchu printf(" ECP"); 88046f3ff79SMike Smith } 88146f3ff79SMike Smith } else { 88246f3ff79SMike Smith /* PPB_EPP is set */ 88346f3ff79SMike Smith outb(cio, r | SMC_EPPSPP); 884edcfcf27SNicolas Souchu if (bootverbose) 885edcfcf27SNicolas Souchu printf(" EPP SPP"); 88667646539SMike Smith } 88767646539SMike Smith } 88846f3ff79SMike Smith ppc->ppc_avm = chipset_mode; 88967646539SMike Smith } 89067646539SMike Smith 891bc35c174SNicolas Souchu /* set FIFO threshold to 16 */ 892bc35c174SNicolas Souchu if (ppc->ppc_avm & PPB_ECP) { 893bc35c174SNicolas Souchu /* select CRA */ 894bc35c174SNicolas Souchu outb(csr, 0xa); 895bc35c174SNicolas Souchu outb(cio, 16); 896bc35c174SNicolas Souchu } 897bc35c174SNicolas Souchu 89867646539SMike Smith end_detect: 89946f3ff79SMike Smith 90046f3ff79SMike Smith if (bootverbose) 90146f3ff79SMike Smith printf ("\n"); 90246f3ff79SMike Smith 90354ad6085SNicolas Souchu if (ppc->ppc_avm & PPB_EPP) { 90467646539SMike Smith /* select CR4 */ 90567646539SMike Smith outb(csr, 0x4); 90667646539SMike Smith r = inb(cio); 90767646539SMike Smith 90867646539SMike Smith /* 90967646539SMike Smith * Set the EPP protocol... 91067646539SMike Smith * Low=EPP 1.9 (1284 standard) and High=EPP 1.7 91167646539SMike Smith */ 91267646539SMike Smith if (ppc->ppc_epp == EPP_1_9) 91367646539SMike Smith outb(cio, (r & ~SMC_CR4_EPPTYPE)); 91467646539SMike Smith else 91567646539SMike Smith outb(cio, (r | SMC_CR4_EPPTYPE)); 91667646539SMike Smith } 91767646539SMike Smith 91867646539SMike Smith /* end config mode */ 91967646539SMike Smith outb(csr, 0xaa); 92067646539SMike Smith 9210f210c92SNicolas Souchu ppc->ppc_type = PPC_TYPE_SMCLIKE; 9220f210c92SNicolas Souchu ppc_smclike_setmode(ppc, chipset_mode); 92367646539SMike Smith 92446f3ff79SMike Smith return (chipset_mode); 92567646539SMike Smith } 92667646539SMike Smith 92746f3ff79SMike Smith /* 9286a5be862SDoug Rabson * SMC FDC37C935 configuration 9296a5be862SDoug Rabson * Found on many Alpha machines 9306a5be862SDoug Rabson */ 9316a5be862SDoug Rabson static int 9326a5be862SDoug Rabson ppc_smc37c935_detect(struct ppc_data *ppc, int chipset_mode) 9336a5be862SDoug Rabson { 9346a5be862SDoug Rabson int s; 9356a5be862SDoug Rabson int type = -1; 9366a5be862SDoug Rabson 9376a5be862SDoug Rabson s = splhigh(); 9386a5be862SDoug Rabson outb(SMC935_CFG, 0x55); /* enter config mode */ 9396a5be862SDoug Rabson outb(SMC935_CFG, 0x55); 9406a5be862SDoug Rabson splx(s); 9416a5be862SDoug Rabson 9426a5be862SDoug Rabson outb(SMC935_IND, SMC935_ID); /* check device id */ 9436a5be862SDoug Rabson if (inb(SMC935_DAT) == 0x2) 9446a5be862SDoug Rabson type = SMC_37C935; 9456a5be862SDoug Rabson 9466a5be862SDoug Rabson if (type == -1) { 9476a5be862SDoug Rabson outb(SMC935_CFG, 0xaa); /* exit config mode */ 9486a5be862SDoug Rabson return (-1); 9496a5be862SDoug Rabson } 9506a5be862SDoug Rabson 9516a5be862SDoug Rabson ppc->ppc_model = type; 9526a5be862SDoug Rabson 9536a5be862SDoug Rabson outb(SMC935_IND, SMC935_LOGDEV); /* select parallel port, */ 9546a5be862SDoug Rabson outb(SMC935_DAT, 3); /* which is logical device 3 */ 9556a5be862SDoug Rabson 9566a5be862SDoug Rabson /* set io port base */ 9576a5be862SDoug Rabson outb(SMC935_IND, SMC935_PORTHI); 9586a5be862SDoug Rabson outb(SMC935_DAT, (u_char)((ppc->ppc_base & 0xff00) >> 8)); 9596a5be862SDoug Rabson outb(SMC935_IND, SMC935_PORTLO); 9606a5be862SDoug Rabson outb(SMC935_DAT, (u_char)(ppc->ppc_base & 0xff)); 9616a5be862SDoug Rabson 9626a5be862SDoug Rabson if (!chipset_mode) 9636a5be862SDoug Rabson ppc->ppc_avm = PPB_COMPATIBLE; /* default mode */ 9646a5be862SDoug Rabson else { 9656a5be862SDoug Rabson ppc->ppc_avm = chipset_mode; 9666a5be862SDoug Rabson outb(SMC935_IND, SMC935_PPMODE); 9676a5be862SDoug Rabson outb(SMC935_DAT, SMC935_CENT); /* start in compatible mode */ 9686a5be862SDoug Rabson 9696a5be862SDoug Rabson /* SPP + EPP or just plain SPP */ 9706a5be862SDoug Rabson if (chipset_mode & (PPB_SPP)) { 9716a5be862SDoug Rabson if (chipset_mode & PPB_EPP) { 9726a5be862SDoug Rabson if (ppc->ppc_epp == EPP_1_9) { 9736a5be862SDoug Rabson outb(SMC935_IND, SMC935_PPMODE); 9746a5be862SDoug Rabson outb(SMC935_DAT, SMC935_EPP19SPP); 9756a5be862SDoug Rabson } 9766a5be862SDoug Rabson if (ppc->ppc_epp == EPP_1_7) { 9776a5be862SDoug Rabson outb(SMC935_IND, SMC935_PPMODE); 9786a5be862SDoug Rabson outb(SMC935_DAT, SMC935_EPP17SPP); 9796a5be862SDoug Rabson } 9806a5be862SDoug Rabson } else { 9816a5be862SDoug Rabson outb(SMC935_IND, SMC935_PPMODE); 9826a5be862SDoug Rabson outb(SMC935_DAT, SMC935_SPP); 9836a5be862SDoug Rabson } 9846a5be862SDoug Rabson } 9856a5be862SDoug Rabson 9866a5be862SDoug Rabson /* ECP + EPP or just plain ECP */ 9876a5be862SDoug Rabson if (chipset_mode & PPB_ECP) { 9886a5be862SDoug Rabson if (chipset_mode & PPB_EPP) { 9896a5be862SDoug Rabson if (ppc->ppc_epp == EPP_1_9) { 9906a5be862SDoug Rabson outb(SMC935_IND, SMC935_PPMODE); 9916a5be862SDoug Rabson outb(SMC935_DAT, SMC935_ECPEPP19); 9926a5be862SDoug Rabson } 9936a5be862SDoug Rabson if (ppc->ppc_epp == EPP_1_7) { 9946a5be862SDoug Rabson outb(SMC935_IND, SMC935_PPMODE); 9956a5be862SDoug Rabson outb(SMC935_DAT, SMC935_ECPEPP17); 9966a5be862SDoug Rabson } 9976a5be862SDoug Rabson } else { 9986a5be862SDoug Rabson outb(SMC935_IND, SMC935_PPMODE); 9996a5be862SDoug Rabson outb(SMC935_DAT, SMC935_ECP); 10006a5be862SDoug Rabson } 10016a5be862SDoug Rabson } 10026a5be862SDoug Rabson } 10036a5be862SDoug Rabson 10046a5be862SDoug Rabson outb(SMC935_CFG, 0xaa); /* exit config mode */ 10056a5be862SDoug Rabson 10066a5be862SDoug Rabson ppc->ppc_type = PPC_TYPE_SMCLIKE; 10076a5be862SDoug Rabson ppc_smclike_setmode(ppc, chipset_mode); 10086a5be862SDoug Rabson 10096a5be862SDoug Rabson return (chipset_mode); 10106a5be862SDoug Rabson } 10116a5be862SDoug Rabson 10126a5be862SDoug Rabson /* 101346f3ff79SMike Smith * Winbond W83877F stuff 101446f3ff79SMike Smith * 101546f3ff79SMike Smith * EFER: extended function enable register 101646f3ff79SMike Smith * EFIR: extended function index register 101746f3ff79SMike Smith * EFDR: extended function data register 101846f3ff79SMike Smith */ 101946f3ff79SMike Smith #define efir ((efer == 0x250) ? 0x251 : 0x3f0) 102046f3ff79SMike Smith #define efdr ((efer == 0x250) ? 0x252 : 0x3f1) 102146f3ff79SMike Smith 102246f3ff79SMike Smith static int w83877f_efers[] = { 0x250, 0x3f0, 0x3f0, 0x250 }; 102346f3ff79SMike Smith static int w83877f_keys[] = { 0x89, 0x86, 0x87, 0x88 }; 102446f3ff79SMike Smith static int w83877f_keyiter[] = { 1, 2, 2, 1 }; 102546f3ff79SMike Smith static int w83877f_hefs[] = { WINB_HEFERE, WINB_HEFRAS, WINB_HEFERE | WINB_HEFRAS, 0 }; 102667646539SMike Smith 102767646539SMike Smith static int 102846f3ff79SMike Smith ppc_w83877f_detect(struct ppc_data *ppc, int chipset_mode) 102967646539SMike Smith { 1030f1d19042SArchie Cobbs int i, j, efer; 103146f3ff79SMike Smith unsigned char r, hefere, hefras; 103267646539SMike Smith 103346f3ff79SMike Smith for (i = 0; i < 4; i ++) { 103446f3ff79SMike Smith /* first try to enable configuration registers */ 103546f3ff79SMike Smith efer = w83877f_efers[i]; 103667646539SMike Smith 103746f3ff79SMike Smith /* write the key to the EFER */ 103846f3ff79SMike Smith for (j = 0; j < w83877f_keyiter[i]; j ++) 103946f3ff79SMike Smith outb (efer, w83877f_keys[i]); 104046f3ff79SMike Smith 104146f3ff79SMike Smith /* then check HEFERE and HEFRAS bits */ 104246f3ff79SMike Smith outb (efir, 0x0c); 104346f3ff79SMike Smith hefere = inb(efdr) & WINB_HEFERE; 104446f3ff79SMike Smith 104546f3ff79SMike Smith outb (efir, 0x16); 104646f3ff79SMike Smith hefras = inb(efdr) & WINB_HEFRAS; 104746f3ff79SMike Smith 104846f3ff79SMike Smith /* 104946f3ff79SMike Smith * HEFRAS HEFERE 105046f3ff79SMike Smith * 0 1 write 89h to 250h (power-on default) 105146f3ff79SMike Smith * 1 0 write 86h twice to 3f0h 105246f3ff79SMike Smith * 1 1 write 87h twice to 3f0h 105346f3ff79SMike Smith * 0 0 write 88h to 250h 105446f3ff79SMike Smith */ 105546f3ff79SMike Smith if ((hefere | hefras) == w83877f_hefs[i]) 105646f3ff79SMike Smith goto found; 105767646539SMike Smith } 105867646539SMike Smith 105946f3ff79SMike Smith return (-1); /* failed */ 106067646539SMike Smith 106146f3ff79SMike Smith found: 106246f3ff79SMike Smith /* check base port address - read from CR23 */ 106346f3ff79SMike Smith outb(efir, 0x23); 106446f3ff79SMike Smith if (ppc->ppc_base != inb(efdr) * 4) /* 4 bytes boundaries */ 106546f3ff79SMike Smith return (-1); 106646f3ff79SMike Smith 106746f3ff79SMike Smith /* read CHIP ID from CR9/bits0-3 */ 106846f3ff79SMike Smith outb(efir, 0x9); 106946f3ff79SMike Smith 107046f3ff79SMike Smith switch (inb(efdr) & WINB_CHIPID) { 107146f3ff79SMike Smith case WINB_W83877F_ID: 10720f210c92SNicolas Souchu ppc->ppc_model = WINB_W83877F; 107346f3ff79SMike Smith break; 107446f3ff79SMike Smith 107546f3ff79SMike Smith case WINB_W83877AF_ID: 10760f210c92SNicolas Souchu ppc->ppc_model = WINB_W83877AF; 107746f3ff79SMike Smith break; 107846f3ff79SMike Smith 107946f3ff79SMike Smith default: 10800f210c92SNicolas Souchu ppc->ppc_model = WINB_UNKNOWN; 108146f3ff79SMike Smith } 108246f3ff79SMike Smith 108346f3ff79SMike Smith if (bootverbose) { 108446f3ff79SMike Smith /* dump of registers */ 108546f3ff79SMike Smith printf("ppc%d: 0x%x - ", ppc->ppc_unit, w83877f_keys[i]); 108646f3ff79SMike Smith for (i = 0; i <= 0xd; i ++) { 108746f3ff79SMike Smith outb(efir, i); 108846f3ff79SMike Smith printf("0x%x ", inb(efdr)); 108946f3ff79SMike Smith } 109046f3ff79SMike Smith for (i = 0x10; i <= 0x17; i ++) { 109146f3ff79SMike Smith outb(efir, i); 109246f3ff79SMike Smith printf("0x%x ", inb(efdr)); 109346f3ff79SMike Smith } 109446f3ff79SMike Smith outb(efir, 0x1e); 109546f3ff79SMike Smith printf("0x%x ", inb(efdr)); 109646f3ff79SMike Smith for (i = 0x20; i <= 0x29; i ++) { 109746f3ff79SMike Smith outb(efir, i); 109846f3ff79SMike Smith printf("0x%x ", inb(efdr)); 109946f3ff79SMike Smith } 110046f3ff79SMike Smith printf("\n"); 1101edcfcf27SNicolas Souchu printf("ppc%d:", ppc->ppc_unit); 110246f3ff79SMike Smith } 110346f3ff79SMike Smith 11040f210c92SNicolas Souchu ppc->ppc_type = PPC_TYPE_GENERIC; 1105edcfcf27SNicolas Souchu 110646f3ff79SMike Smith if (!chipset_mode) { 110746f3ff79SMike Smith /* autodetect mode */ 110846f3ff79SMike Smith 110946f3ff79SMike Smith /* select CR0 */ 111046f3ff79SMike Smith outb(efir, 0x0); 111146f3ff79SMike Smith r = inb(efdr) & (WINB_PRTMODS0 | WINB_PRTMODS1); 111246f3ff79SMike Smith 111346f3ff79SMike Smith /* select CR9 */ 111446f3ff79SMike Smith outb(efir, 0x9); 111546f3ff79SMike Smith r |= (inb(efdr) & WINB_PRTMODS2); 111646f3ff79SMike Smith 111746f3ff79SMike Smith switch (r) { 111846f3ff79SMike Smith case WINB_W83757: 111946f3ff79SMike Smith if (bootverbose) 112046f3ff79SMike Smith printf("ppc%d: W83757 compatible mode\n", 112146f3ff79SMike Smith ppc->ppc_unit); 112246f3ff79SMike Smith return (-1); /* generic or SMC-like */ 112346f3ff79SMike Smith 112446f3ff79SMike Smith case WINB_EXTFDC: 112546f3ff79SMike Smith case WINB_EXTADP: 112646f3ff79SMike Smith case WINB_EXT2FDD: 112746f3ff79SMike Smith case WINB_JOYSTICK: 112846f3ff79SMike Smith if (bootverbose) 1129edcfcf27SNicolas Souchu printf(" not in parallel port mode\n"); 113046f3ff79SMike Smith return (-1); 113146f3ff79SMike Smith 113246f3ff79SMike Smith case (WINB_PARALLEL | WINB_EPP_SPP): 113346f3ff79SMike Smith ppc->ppc_avm |= PPB_EPP | PPB_SPP; 1134edcfcf27SNicolas Souchu if (bootverbose) 1135edcfcf27SNicolas Souchu printf(" EPP SPP"); 113646f3ff79SMike Smith break; 113746f3ff79SMike Smith 113846f3ff79SMike Smith case (WINB_PARALLEL | WINB_ECP): 113946f3ff79SMike Smith ppc->ppc_avm |= PPB_ECP | PPB_SPP; 1140edcfcf27SNicolas Souchu if (bootverbose) 1141edcfcf27SNicolas Souchu printf(" ECP SPP"); 114246f3ff79SMike Smith break; 114346f3ff79SMike Smith 114446f3ff79SMike Smith case (WINB_PARALLEL | WINB_ECP_EPP): 114546f3ff79SMike Smith ppc->ppc_avm |= PPB_ECP | PPB_EPP | PPB_SPP; 11460f210c92SNicolas Souchu ppc->ppc_type = PPC_TYPE_SMCLIKE; 1147edcfcf27SNicolas Souchu 1148edcfcf27SNicolas Souchu if (bootverbose) 1149edcfcf27SNicolas Souchu printf(" ECP+EPP SPP"); 115046f3ff79SMike Smith break; 115146f3ff79SMike Smith default: 11526e551fb6SDavid E. O'Brien printf("%s: unknown case (0x%x)!\n", __func__, r); 115346f3ff79SMike Smith } 115446f3ff79SMike Smith 115546f3ff79SMike Smith } else { 115646f3ff79SMike Smith /* mode forced */ 115746f3ff79SMike Smith 115846f3ff79SMike Smith /* select CR9 and set PRTMODS2 bit */ 115946f3ff79SMike Smith outb(efir, 0x9); 116046f3ff79SMike Smith outb(efdr, inb(efdr) & ~WINB_PRTMODS2); 116146f3ff79SMike Smith 116246f3ff79SMike Smith /* select CR0 and reset PRTMODSx bits */ 116346f3ff79SMike Smith outb(efir, 0x0); 116446f3ff79SMike Smith outb(efdr, inb(efdr) & ~(WINB_PRTMODS0 | WINB_PRTMODS1)); 116546f3ff79SMike Smith 116646f3ff79SMike Smith if (chipset_mode & PPB_ECP) { 1167edcfcf27SNicolas Souchu if (chipset_mode & PPB_EPP) { 116846f3ff79SMike Smith outb(efdr, inb(efdr) | WINB_ECP_EPP); 1169edcfcf27SNicolas Souchu if (bootverbose) 1170edcfcf27SNicolas Souchu printf(" ECP+EPP"); 1171edcfcf27SNicolas Souchu 11720f210c92SNicolas Souchu ppc->ppc_type = PPC_TYPE_SMCLIKE; 1173edcfcf27SNicolas Souchu 1174edcfcf27SNicolas Souchu } else { 117546f3ff79SMike Smith outb(efdr, inb(efdr) | WINB_ECP); 1176edcfcf27SNicolas Souchu if (bootverbose) 1177edcfcf27SNicolas Souchu printf(" ECP"); 1178edcfcf27SNicolas Souchu } 117946f3ff79SMike Smith } else { 118046f3ff79SMike Smith /* select EPP_SPP otherwise */ 118146f3ff79SMike Smith outb(efdr, inb(efdr) | WINB_EPP_SPP); 1182edcfcf27SNicolas Souchu if (bootverbose) 1183edcfcf27SNicolas Souchu printf(" EPP SPP"); 118446f3ff79SMike Smith } 118546f3ff79SMike Smith ppc->ppc_avm = chipset_mode; 118646f3ff79SMike Smith } 118746f3ff79SMike Smith 1188edcfcf27SNicolas Souchu if (bootverbose) 1189edcfcf27SNicolas Souchu printf("\n"); 1190edcfcf27SNicolas Souchu 119146f3ff79SMike Smith /* exit configuration mode */ 119246f3ff79SMike Smith outb(efer, 0xaa); 119346f3ff79SMike Smith 11940f210c92SNicolas Souchu switch (ppc->ppc_type) { 11950f210c92SNicolas Souchu case PPC_TYPE_SMCLIKE: 11960f210c92SNicolas Souchu ppc_smclike_setmode(ppc, chipset_mode); 11970f210c92SNicolas Souchu break; 11980f210c92SNicolas Souchu default: 11990f210c92SNicolas Souchu ppc_generic_setmode(ppc, chipset_mode); 12000f210c92SNicolas Souchu break; 12010f210c92SNicolas Souchu } 120246f3ff79SMike Smith 120346f3ff79SMike Smith return (chipset_mode); 120467646539SMike Smith } 12050f210c92SNicolas Souchu #endif 120667646539SMike Smith 120767646539SMike Smith /* 120867646539SMike Smith * ppc_generic_detect 120967646539SMike Smith */ 121067646539SMike Smith static int 121146f3ff79SMike Smith ppc_generic_detect(struct ppc_data *ppc, int chipset_mode) 121267646539SMike Smith { 1213edcfcf27SNicolas Souchu /* default to generic */ 12140f210c92SNicolas Souchu ppc->ppc_type = PPC_TYPE_GENERIC; 1215edcfcf27SNicolas Souchu 1216edcfcf27SNicolas Souchu if (bootverbose) 1217edcfcf27SNicolas Souchu printf("ppc%d:", ppc->ppc_unit); 1218edcfcf27SNicolas Souchu 121946f3ff79SMike Smith /* first, check for ECP */ 1220bc35c174SNicolas Souchu w_ecr(ppc, PPC_ECR_PS2); 1221bc35c174SNicolas Souchu if ((r_ecr(ppc) & 0xe0) == PPC_ECR_PS2) { 1222c264e80fSNicolas Souchu ppc->ppc_dtm |= PPB_ECP | PPB_SPP; 1223edcfcf27SNicolas Souchu if (bootverbose) 1224edcfcf27SNicolas Souchu printf(" ECP SPP"); 122546f3ff79SMike Smith 122646f3ff79SMike Smith /* search for SMC style ECP+EPP mode */ 1227bc35c174SNicolas Souchu w_ecr(ppc, PPC_ECR_EPP); 122846f3ff79SMike Smith } 122967646539SMike Smith 123067646539SMike Smith /* try to reset EPP timeout bit */ 123146f3ff79SMike Smith if (ppc_check_epp_timeout(ppc)) { 1232c264e80fSNicolas Souchu ppc->ppc_dtm |= PPB_EPP; 123367646539SMike Smith 1234c264e80fSNicolas Souchu if (ppc->ppc_dtm & PPB_ECP) { 123546f3ff79SMike Smith /* SMC like chipset found */ 12360f210c92SNicolas Souchu ppc->ppc_model = SMC_LIKE; 12370f210c92SNicolas Souchu ppc->ppc_type = PPC_TYPE_SMCLIKE; 1238edcfcf27SNicolas Souchu 1239edcfcf27SNicolas Souchu if (bootverbose) 1240edcfcf27SNicolas Souchu printf(" ECP+EPP"); 1241edcfcf27SNicolas Souchu } else { 1242edcfcf27SNicolas Souchu if (bootverbose) 1243edcfcf27SNicolas Souchu printf(" EPP"); 1244edcfcf27SNicolas Souchu } 1245edcfcf27SNicolas Souchu } else { 1246edcfcf27SNicolas Souchu /* restore to standard mode */ 1247bc35c174SNicolas Souchu w_ecr(ppc, PPC_ECR_STD); 124867646539SMike Smith } 124967646539SMike Smith 1250edcfcf27SNicolas Souchu /* XXX try to detect NIBBLE and PS2 modes */ 1251c264e80fSNicolas Souchu ppc->ppc_dtm |= PPB_NIBBLE; 125267646539SMike Smith 1253edcfcf27SNicolas Souchu if (bootverbose) 1254edcfcf27SNicolas Souchu printf(" SPP"); 125567646539SMike Smith 1256c264e80fSNicolas Souchu if (chipset_mode) 1257edcfcf27SNicolas Souchu ppc->ppc_avm = chipset_mode; 1258c264e80fSNicolas Souchu else 1259c264e80fSNicolas Souchu ppc->ppc_avm = ppc->ppc_dtm; 1260edcfcf27SNicolas Souchu 1261edcfcf27SNicolas Souchu if (bootverbose) 1262edcfcf27SNicolas Souchu printf("\n"); 1263edcfcf27SNicolas Souchu 12640f210c92SNicolas Souchu switch (ppc->ppc_type) { 12650f210c92SNicolas Souchu case PPC_TYPE_SMCLIKE: 12660f210c92SNicolas Souchu ppc_smclike_setmode(ppc, chipset_mode); 12670f210c92SNicolas Souchu break; 12680f210c92SNicolas Souchu default: 12690f210c92SNicolas Souchu ppc_generic_setmode(ppc, chipset_mode); 12700f210c92SNicolas Souchu break; 12710f210c92SNicolas Souchu } 127246f3ff79SMike Smith 127346f3ff79SMike Smith return (chipset_mode); 127467646539SMike Smith } 127567646539SMike Smith 127667646539SMike Smith /* 127767646539SMike Smith * ppc_detect() 127867646539SMike Smith * 127967646539SMike Smith * mode is the mode suggested at boot 128067646539SMike Smith */ 128167646539SMike Smith static int 128246f3ff79SMike Smith ppc_detect(struct ppc_data *ppc, int chipset_mode) { 128367646539SMike Smith 12840f210c92SNicolas Souchu #ifdef PPC_PROBE_CHIPSET 128546f3ff79SMike Smith int i, mode; 128667646539SMike Smith 128746f3ff79SMike Smith /* list of supported chipsets */ 128846f3ff79SMike Smith int (*chipset_detect[])(struct ppc_data *, int) = { 128946f3ff79SMike Smith ppc_pc873xx_detect, 129046f3ff79SMike Smith ppc_smc37c66xgt_detect, 129146f3ff79SMike Smith ppc_w83877f_detect, 12926a5be862SDoug Rabson ppc_smc37c935_detect, 129346f3ff79SMike Smith ppc_generic_detect, 129446f3ff79SMike Smith NULL 129546f3ff79SMike Smith }; 12960f210c92SNicolas Souchu #endif 129767646539SMike Smith 129846f3ff79SMike Smith /* if can't find the port and mode not forced return error */ 129946f3ff79SMike Smith if (!ppc_detect_port(ppc) && chipset_mode == 0) 130046f3ff79SMike Smith return (EIO); /* failed, port not present */ 130167646539SMike Smith 130246f3ff79SMike Smith /* assume centronics compatible mode is supported */ 130346f3ff79SMike Smith ppc->ppc_avm = PPB_COMPATIBLE; 130467646539SMike Smith 13050f210c92SNicolas Souchu #ifdef PPC_PROBE_CHIPSET 130646f3ff79SMike Smith /* we have to differenciate available chipset modes, 130746f3ff79SMike Smith * chipset running modes and IEEE-1284 operating modes 130846f3ff79SMike Smith * 130946f3ff79SMike Smith * after detection, the port must support running in compatible mode 131046f3ff79SMike Smith */ 1311af548787SNicolas Souchu if (ppc->ppc_flags & 0x40) { 1312af548787SNicolas Souchu if (bootverbose) 1313af548787SNicolas Souchu printf("ppc: chipset forced to generic\n"); 13140f210c92SNicolas Souchu #endif 1315af548787SNicolas Souchu 1316af548787SNicolas Souchu ppc->ppc_mode = ppc_generic_detect(ppc, chipset_mode); 1317af548787SNicolas Souchu 13180f210c92SNicolas Souchu #ifdef PPC_PROBE_CHIPSET 1319af548787SNicolas Souchu } else { 132046f3ff79SMike Smith for (i=0; chipset_detect[i] != NULL; i++) { 132146f3ff79SMike Smith if ((mode = chipset_detect[i](ppc, chipset_mode)) != -1) { 132246f3ff79SMike Smith ppc->ppc_mode = mode; 132346f3ff79SMike Smith break; 132446f3ff79SMike Smith } 132546f3ff79SMike Smith } 1326af548787SNicolas Souchu } 13270f210c92SNicolas Souchu #endif 132846f3ff79SMike Smith 1329bc35c174SNicolas Souchu /* configure/detect ECP FIFO */ 1330bc35c174SNicolas Souchu if ((ppc->ppc_avm & PPB_ECP) && !(ppc->ppc_flags & 0x80)) 1331bc35c174SNicolas Souchu ppc_detect_fifo(ppc); 1332bc35c174SNicolas Souchu 133346f3ff79SMike Smith return (0); 133446f3ff79SMike Smith } 133546f3ff79SMike Smith 133646f3ff79SMike Smith /* 133746f3ff79SMike Smith * ppc_exec_microseq() 133846f3ff79SMike Smith * 133946f3ff79SMike Smith * Execute a microsequence. 134046f3ff79SMike Smith * Microsequence mechanism is supposed to handle fast I/O operations. 134146f3ff79SMike Smith */ 134246f3ff79SMike Smith static int 13430f210c92SNicolas Souchu ppc_exec_microseq(device_t dev, struct ppb_microseq **p_msq) 134446f3ff79SMike Smith { 13450f210c92SNicolas Souchu struct ppc_data *ppc = DEVTOSOFTC(dev); 13460a40e22aSNicolas Souchu struct ppb_microseq *mi; 134746f3ff79SMike Smith char cc, *p; 134854ad6085SNicolas Souchu int i, iter, len; 134946f3ff79SMike Smith int error; 135046f3ff79SMike Smith 135154ad6085SNicolas Souchu register int reg; 135254ad6085SNicolas Souchu register char mask; 135354ad6085SNicolas Souchu register int accum = 0; 135454ad6085SNicolas Souchu register char *ptr = 0; 135546f3ff79SMike Smith 13560a40e22aSNicolas Souchu struct ppb_microseq *stack = 0; 135746f3ff79SMike Smith 135846f3ff79SMike Smith /* microsequence registers are equivalent to PC-like port registers */ 13593ae3f8b0SNicolas Souchu 13603ae3f8b0SNicolas Souchu #define r_reg(register,ppc) (bus_space_read_1((ppc)->bst, (ppc)->bsh, register)) 13613ae3f8b0SNicolas Souchu #define w_reg(register, ppc, byte) (bus_space_write_1((ppc)->bst, (ppc)->bsh, register, byte)) 136246f3ff79SMike Smith 13630a40e22aSNicolas Souchu #define INCR_PC (mi ++) /* increment program counter */ 136446f3ff79SMike Smith 13650a40e22aSNicolas Souchu mi = *p_msq; 136646f3ff79SMike Smith for (;;) { 136746f3ff79SMike Smith switch (mi->opcode) { 136846f3ff79SMike Smith case MS_OP_RSET: 136946f3ff79SMike Smith cc = r_reg(mi->arg[0].i, ppc); 137054ad6085SNicolas Souchu cc &= (char)mi->arg[2].i; /* clear mask */ 137154ad6085SNicolas Souchu cc |= (char)mi->arg[1].i; /* assert mask */ 137246f3ff79SMike Smith w_reg(mi->arg[0].i, ppc, cc); 137346f3ff79SMike Smith INCR_PC; 137446f3ff79SMike Smith break; 137546f3ff79SMike Smith 137646f3ff79SMike Smith case MS_OP_RASSERT_P: 137754ad6085SNicolas Souchu reg = mi->arg[1].i; 137854ad6085SNicolas Souchu ptr = ppc->ppc_ptr; 137954ad6085SNicolas Souchu 138054ad6085SNicolas Souchu if ((len = mi->arg[0].i) == MS_ACCUM) { 138154ad6085SNicolas Souchu accum = ppc->ppc_accum; 138254ad6085SNicolas Souchu for (; accum; accum--) 138354ad6085SNicolas Souchu w_reg(reg, ppc, *ptr++); 138454ad6085SNicolas Souchu ppc->ppc_accum = accum; 138554ad6085SNicolas Souchu } else 138654ad6085SNicolas Souchu for (i=0; i<len; i++) 138754ad6085SNicolas Souchu w_reg(reg, ppc, *ptr++); 138854ad6085SNicolas Souchu ppc->ppc_ptr = ptr; 138954ad6085SNicolas Souchu 139046f3ff79SMike Smith INCR_PC; 139146f3ff79SMike Smith break; 139246f3ff79SMike Smith 139346f3ff79SMike Smith case MS_OP_RFETCH_P: 139454ad6085SNicolas Souchu reg = mi->arg[1].i; 139554ad6085SNicolas Souchu mask = (char)mi->arg[2].i; 139654ad6085SNicolas Souchu ptr = ppc->ppc_ptr; 139754ad6085SNicolas Souchu 139854ad6085SNicolas Souchu if ((len = mi->arg[0].i) == MS_ACCUM) { 139954ad6085SNicolas Souchu accum = ppc->ppc_accum; 140054ad6085SNicolas Souchu for (; accum; accum--) 140154ad6085SNicolas Souchu *ptr++ = r_reg(reg, ppc) & mask; 140254ad6085SNicolas Souchu ppc->ppc_accum = accum; 140354ad6085SNicolas Souchu } else 140454ad6085SNicolas Souchu for (i=0; i<len; i++) 140554ad6085SNicolas Souchu *ptr++ = r_reg(reg, ppc) & mask; 140654ad6085SNicolas Souchu ppc->ppc_ptr = ptr; 140754ad6085SNicolas Souchu 140846f3ff79SMike Smith INCR_PC; 140946f3ff79SMike Smith break; 141046f3ff79SMike Smith 141146f3ff79SMike Smith case MS_OP_RFETCH: 141246f3ff79SMike Smith *((char *) mi->arg[2].p) = r_reg(mi->arg[0].i, ppc) & 141354ad6085SNicolas Souchu (char)mi->arg[1].i; 141446f3ff79SMike Smith INCR_PC; 141546f3ff79SMike Smith break; 141646f3ff79SMike Smith 141746f3ff79SMike Smith case MS_OP_RASSERT: 141854ad6085SNicolas Souchu case MS_OP_DELAY: 141946f3ff79SMike Smith 142046f3ff79SMike Smith /* let's suppose the next instr. is the same */ 142146f3ff79SMike Smith prefetch: 142246f3ff79SMike Smith for (;mi->opcode == MS_OP_RASSERT; INCR_PC) 142354ad6085SNicolas Souchu w_reg(mi->arg[0].i, ppc, (char)mi->arg[1].i); 142446f3ff79SMike Smith 142546f3ff79SMike Smith if (mi->opcode == MS_OP_DELAY) { 142646f3ff79SMike Smith DELAY(mi->arg[0].i); 142746f3ff79SMike Smith INCR_PC; 142846f3ff79SMike Smith goto prefetch; 142946f3ff79SMike Smith } 143046f3ff79SMike Smith break; 143146f3ff79SMike Smith 143254ad6085SNicolas Souchu case MS_OP_ADELAY: 143354ad6085SNicolas Souchu if (mi->arg[0].i) 143454ad6085SNicolas Souchu tsleep(NULL, PPBPRI, "ppbdelay", 143554ad6085SNicolas Souchu mi->arg[0].i * (hz/1000)); 143646f3ff79SMike Smith INCR_PC; 143746f3ff79SMike Smith break; 143846f3ff79SMike Smith 143946f3ff79SMike Smith case MS_OP_TRIG: 144046f3ff79SMike Smith reg = mi->arg[0].i; 144146f3ff79SMike Smith iter = mi->arg[1].i; 144246f3ff79SMike Smith p = (char *)mi->arg[2].p; 144346f3ff79SMike Smith 144454ad6085SNicolas Souchu /* XXX delay limited to 255 us */ 144546f3ff79SMike Smith for (i=0; i<iter; i++) { 144646f3ff79SMike Smith w_reg(reg, ppc, *p++); 144746f3ff79SMike Smith DELAY((unsigned char)*p++); 144846f3ff79SMike Smith } 144946f3ff79SMike Smith INCR_PC; 145046f3ff79SMike Smith break; 145146f3ff79SMike Smith 145246f3ff79SMike Smith case MS_OP_SET: 145354ad6085SNicolas Souchu ppc->ppc_accum = mi->arg[0].i; 145446f3ff79SMike Smith INCR_PC; 145546f3ff79SMike Smith break; 145646f3ff79SMike Smith 145746f3ff79SMike Smith case MS_OP_DBRA: 145854ad6085SNicolas Souchu if (--ppc->ppc_accum > 0) 14590a40e22aSNicolas Souchu mi += mi->arg[0].i; 146046f3ff79SMike Smith INCR_PC; 146146f3ff79SMike Smith break; 146246f3ff79SMike Smith 146346f3ff79SMike Smith case MS_OP_BRSET: 146446f3ff79SMike Smith cc = r_str(ppc); 146554ad6085SNicolas Souchu if ((cc & (char)mi->arg[0].i) == (char)mi->arg[0].i) 14660a40e22aSNicolas Souchu mi += mi->arg[1].i; 146746f3ff79SMike Smith INCR_PC; 146846f3ff79SMike Smith break; 146946f3ff79SMike Smith 147046f3ff79SMike Smith case MS_OP_BRCLEAR: 147146f3ff79SMike Smith cc = r_str(ppc); 147254ad6085SNicolas Souchu if ((cc & (char)mi->arg[0].i) == 0) 14730a40e22aSNicolas Souchu mi += mi->arg[1].i; 147446f3ff79SMike Smith INCR_PC; 147546f3ff79SMike Smith break; 147646f3ff79SMike Smith 147754ad6085SNicolas Souchu case MS_OP_BRSTAT: 147854ad6085SNicolas Souchu cc = r_str(ppc); 147954ad6085SNicolas Souchu if ((cc & ((char)mi->arg[0].i | (char)mi->arg[1].i)) == 148054ad6085SNicolas Souchu (char)mi->arg[0].i) 14810a40e22aSNicolas Souchu mi += mi->arg[2].i; 148254ad6085SNicolas Souchu INCR_PC; 148354ad6085SNicolas Souchu break; 148454ad6085SNicolas Souchu 148546f3ff79SMike Smith case MS_OP_C_CALL: 148646f3ff79SMike Smith /* 148746f3ff79SMike Smith * If the C call returns !0 then end the microseq. 148846f3ff79SMike Smith * The current state of ptr is passed to the C function 148946f3ff79SMike Smith */ 149054ad6085SNicolas Souchu if ((error = mi->arg[0].f(mi->arg[1].p, ppc->ppc_ptr))) 149146f3ff79SMike Smith return (error); 149246f3ff79SMike Smith 149346f3ff79SMike Smith INCR_PC; 149446f3ff79SMike Smith break; 149546f3ff79SMike Smith 149646f3ff79SMike Smith case MS_OP_PTR: 149754ad6085SNicolas Souchu ppc->ppc_ptr = (char *)mi->arg[0].p; 149846f3ff79SMike Smith INCR_PC; 149946f3ff79SMike Smith break; 150046f3ff79SMike Smith 150146f3ff79SMike Smith case MS_OP_CALL: 15020a40e22aSNicolas Souchu if (stack) 15036e551fb6SDavid E. O'Brien panic("%s: too much calls", __func__); 150446f3ff79SMike Smith 150546f3ff79SMike Smith if (mi->arg[0].p) { 150646f3ff79SMike Smith /* store the state of the actual 150746f3ff79SMike Smith * microsequence 150846f3ff79SMike Smith */ 15090a40e22aSNicolas Souchu stack = mi; 151046f3ff79SMike Smith 151146f3ff79SMike Smith /* jump to the new microsequence */ 15120a40e22aSNicolas Souchu mi = (struct ppb_microseq *)mi->arg[0].p; 151346f3ff79SMike Smith } else 151446f3ff79SMike Smith INCR_PC; 151546f3ff79SMike Smith 151646f3ff79SMike Smith break; 151746f3ff79SMike Smith 151846f3ff79SMike Smith case MS_OP_SUBRET: 151946f3ff79SMike Smith /* retrieve microseq and pc state before the call */ 15200a40e22aSNicolas Souchu mi = stack; 152146f3ff79SMike Smith 152246f3ff79SMike Smith /* reset the stack */ 15230a40e22aSNicolas Souchu stack = 0; 152446f3ff79SMike Smith 152546f3ff79SMike Smith /* XXX return code */ 152646f3ff79SMike Smith 152746f3ff79SMike Smith INCR_PC; 152846f3ff79SMike Smith break; 152946f3ff79SMike Smith 153046f3ff79SMike Smith case MS_OP_PUT: 153146f3ff79SMike Smith case MS_OP_GET: 153246f3ff79SMike Smith case MS_OP_RET: 153346f3ff79SMike Smith /* can't return to ppb level during the execution 153446f3ff79SMike Smith * of a submicrosequence */ 15350a40e22aSNicolas Souchu if (stack) 153646f3ff79SMike Smith panic("%s: can't return to ppb level", 15376e551fb6SDavid E. O'Brien __func__); 153846f3ff79SMike Smith 153946f3ff79SMike Smith /* update pc for ppb level of execution */ 15400a40e22aSNicolas Souchu *p_msq = mi; 154146f3ff79SMike Smith 154246f3ff79SMike Smith /* return to ppb level of execution */ 154346f3ff79SMike Smith return (0); 154446f3ff79SMike Smith 154546f3ff79SMike Smith default: 154646f3ff79SMike Smith panic("%s: unknown microsequence opcode 0x%x", 15476e551fb6SDavid E. O'Brien __func__, mi->opcode); 154846f3ff79SMike Smith } 154946f3ff79SMike Smith } 155046f3ff79SMike Smith 155146f3ff79SMike Smith /* unreached */ 155246f3ff79SMike Smith } 155346f3ff79SMike Smith 1554bc35c174SNicolas Souchu static void 15550f210c92SNicolas Souchu ppcintr(void *arg) 1556bc35c174SNicolas Souchu { 15570f210c92SNicolas Souchu device_t dev = (device_t)arg; 15580f210c92SNicolas Souchu struct ppc_data *ppc = (struct ppc_data *)device_get_softc(dev); 15593ab971c1SNicolas Souchu u_char ctr, ecr, str; 1560bc35c174SNicolas Souchu 15613ab971c1SNicolas Souchu str = r_str(ppc); 1562bc35c174SNicolas Souchu ctr = r_ctr(ppc); 1563bc35c174SNicolas Souchu ecr = r_ecr(ppc); 1564bc35c174SNicolas Souchu 15653ab971c1SNicolas Souchu #if PPC_DEBUG > 1 15663ab971c1SNicolas Souchu printf("![%x/%x/%x]", ctr, ecr, str); 1567bc35c174SNicolas Souchu #endif 1568bc35c174SNicolas Souchu 1569bc35c174SNicolas Souchu /* don't use ecp mode with IRQENABLE set */ 1570bc35c174SNicolas Souchu if (ctr & IRQENABLE) { 1571bc35c174SNicolas Souchu return; 1572bc35c174SNicolas Souchu } 1573bc35c174SNicolas Souchu 15743ab971c1SNicolas Souchu /* interrupts are generated by nFault signal 15753ab971c1SNicolas Souchu * only in ECP mode */ 15763ab971c1SNicolas Souchu if ((str & nFAULT) && (ppc->ppc_mode & PPB_ECP)) { 15773ab971c1SNicolas Souchu /* check if ppc driver has programmed the 15783ab971c1SNicolas Souchu * nFault interrupt */ 1579bc35c174SNicolas Souchu if (ppc->ppc_irqstat & PPC_IRQ_nFAULT) { 1580bc35c174SNicolas Souchu 1581bc35c174SNicolas Souchu w_ecr(ppc, ecr | PPC_nFAULT_INTR); 1582bc35c174SNicolas Souchu ppc->ppc_irqstat &= ~PPC_IRQ_nFAULT; 1583bc35c174SNicolas Souchu } else { 15840f210c92SNicolas Souchu /* shall be handled by underlying layers XXX */ 1585bc35c174SNicolas Souchu return; 1586bc35c174SNicolas Souchu } 1587bc35c174SNicolas Souchu } 1588bc35c174SNicolas Souchu 1589bc35c174SNicolas Souchu if (ppc->ppc_irqstat & PPC_IRQ_DMA) { 1590bc35c174SNicolas Souchu /* disable interrupts (should be done by hardware though) */ 1591bc35c174SNicolas Souchu w_ecr(ppc, ecr | PPC_SERVICE_INTR); 1592bc35c174SNicolas Souchu ppc->ppc_irqstat &= ~PPC_IRQ_DMA; 1593bc35c174SNicolas Souchu ecr = r_ecr(ppc); 1594bc35c174SNicolas Souchu 1595bc35c174SNicolas Souchu /* check if DMA completed */ 1596bc35c174SNicolas Souchu if ((ppc->ppc_avm & PPB_ECP) && (ecr & PPC_ENABLE_DMA)) { 1597bc35c174SNicolas Souchu #ifdef PPC_DEBUG 1598bc35c174SNicolas Souchu printf("a"); 1599bc35c174SNicolas Souchu #endif 1600bc35c174SNicolas Souchu /* stop DMA */ 1601bc35c174SNicolas Souchu w_ecr(ppc, ecr & ~PPC_ENABLE_DMA); 1602bc35c174SNicolas Souchu ecr = r_ecr(ppc); 1603bc35c174SNicolas Souchu 1604bc35c174SNicolas Souchu if (ppc->ppc_dmastat == PPC_DMA_STARTED) { 1605bc35c174SNicolas Souchu #ifdef PPC_DEBUG 1606bc35c174SNicolas Souchu printf("d"); 1607bc35c174SNicolas Souchu #endif 1608bc35c174SNicolas Souchu isa_dmadone( 1609bc35c174SNicolas Souchu ppc->ppc_dmaflags, 1610bc35c174SNicolas Souchu ppc->ppc_dmaddr, 1611bc35c174SNicolas Souchu ppc->ppc_dmacnt, 1612bc35c174SNicolas Souchu ppc->ppc_dmachan); 1613bc35c174SNicolas Souchu 1614bc35c174SNicolas Souchu ppc->ppc_dmastat = PPC_DMA_COMPLETE; 1615bc35c174SNicolas Souchu 1616bc35c174SNicolas Souchu /* wakeup the waiting process */ 1617bc35c174SNicolas Souchu wakeup((caddr_t)ppc); 1618bc35c174SNicolas Souchu } 1619bc35c174SNicolas Souchu } 1620bc35c174SNicolas Souchu } else if (ppc->ppc_irqstat & PPC_IRQ_FIFO) { 1621bc35c174SNicolas Souchu 1622bc35c174SNicolas Souchu /* classic interrupt I/O */ 1623bc35c174SNicolas Souchu ppc->ppc_irqstat &= ~PPC_IRQ_FIFO; 1624bc35c174SNicolas Souchu } 1625bc35c174SNicolas Souchu 1626bc35c174SNicolas Souchu return; 1627bc35c174SNicolas Souchu } 1628bc35c174SNicolas Souchu 1629bc35c174SNicolas Souchu static int 16300f210c92SNicolas Souchu ppc_read(device_t dev, char *buf, int len, int mode) 1631bc35c174SNicolas Souchu { 1632bc35c174SNicolas Souchu return (EINVAL); 1633bc35c174SNicolas Souchu } 1634bc35c174SNicolas Souchu 1635bc35c174SNicolas Souchu /* 1636bc35c174SNicolas Souchu * Call this function if you want to send data in any advanced mode 1637bc35c174SNicolas Souchu * of your parallel port: FIFO, DMA 1638bc35c174SNicolas Souchu * 1639bc35c174SNicolas Souchu * If what you want is not possible (no ECP, no DMA...), 1640bc35c174SNicolas Souchu * EINVAL is returned 1641bc35c174SNicolas Souchu */ 1642bc35c174SNicolas Souchu static int 16430f210c92SNicolas Souchu ppc_write(device_t dev, char *buf, int len, int how) 1644bc35c174SNicolas Souchu { 16450f210c92SNicolas Souchu struct ppc_data *ppc = DEVTOSOFTC(dev); 1646bc35c174SNicolas Souchu char ecr, ecr_sav, ctr, ctr_sav; 1647bc35c174SNicolas Souchu int s, error = 0; 1648bc35c174SNicolas Souchu int spin; 1649bc35c174SNicolas Souchu 1650bc35c174SNicolas Souchu #ifdef PPC_DEBUG 1651bc35c174SNicolas Souchu printf("w"); 1652bc35c174SNicolas Souchu #endif 1653bc35c174SNicolas Souchu 1654bc35c174SNicolas Souchu ecr_sav = r_ecr(ppc); 1655bc35c174SNicolas Souchu ctr_sav = r_ctr(ppc); 1656bc35c174SNicolas Souchu 1657bc35c174SNicolas Souchu /* 1658bc35c174SNicolas Souchu * Send buffer with DMA, FIFO and interrupts 1659bc35c174SNicolas Souchu */ 16600f210c92SNicolas Souchu if ((ppc->ppc_avm & PPB_ECP) && (ppc->ppc_registered)) { 1661bc35c174SNicolas Souchu 16621afd1f99SNicolas Souchu if (ppc->ppc_dmachan > 0) { 1663bc35c174SNicolas Souchu 1664bc35c174SNicolas Souchu /* byte mode, no intr, no DMA, dir=0, flush fifo 1665bc35c174SNicolas Souchu */ 1666bc35c174SNicolas Souchu ecr = PPC_ECR_STD | PPC_DISABLE_INTR; 1667bc35c174SNicolas Souchu w_ecr(ppc, ecr); 1668bc35c174SNicolas Souchu 1669bc35c174SNicolas Souchu /* disable nAck interrupts */ 1670bc35c174SNicolas Souchu ctr = r_ctr(ppc); 1671bc35c174SNicolas Souchu ctr &= ~IRQENABLE; 1672bc35c174SNicolas Souchu w_ctr(ppc, ctr); 1673bc35c174SNicolas Souchu 1674bc35c174SNicolas Souchu ppc->ppc_dmaflags = 0; 1675bc35c174SNicolas Souchu ppc->ppc_dmaddr = (caddr_t)buf; 1676bc35c174SNicolas Souchu ppc->ppc_dmacnt = (u_int)len; 1677bc35c174SNicolas Souchu 1678bc35c174SNicolas Souchu switch (ppc->ppc_mode) { 1679bc35c174SNicolas Souchu case PPB_COMPATIBLE: 1680bc35c174SNicolas Souchu /* compatible mode with FIFO, no intr, DMA, dir=0 */ 1681bc35c174SNicolas Souchu ecr = PPC_ECR_FIFO | PPC_DISABLE_INTR | PPC_ENABLE_DMA; 1682bc35c174SNicolas Souchu break; 1683bc35c174SNicolas Souchu case PPB_ECP: 1684bc35c174SNicolas Souchu ecr = PPC_ECR_ECP | PPC_DISABLE_INTR | PPC_ENABLE_DMA; 1685bc35c174SNicolas Souchu break; 1686bc35c174SNicolas Souchu default: 1687bc35c174SNicolas Souchu error = EINVAL; 1688bc35c174SNicolas Souchu goto error; 1689bc35c174SNicolas Souchu } 1690bc35c174SNicolas Souchu 1691bc35c174SNicolas Souchu w_ecr(ppc, ecr); 1692bc35c174SNicolas Souchu ecr = r_ecr(ppc); 1693bc35c174SNicolas Souchu 1694bc35c174SNicolas Souchu /* enter splhigh() not to be preempted 1695bc35c174SNicolas Souchu * by the dma interrupt, we may miss 1696bc35c174SNicolas Souchu * the wakeup otherwise 1697bc35c174SNicolas Souchu */ 1698bc35c174SNicolas Souchu s = splhigh(); 1699bc35c174SNicolas Souchu 1700bc35c174SNicolas Souchu ppc->ppc_dmastat = PPC_DMA_INIT; 1701bc35c174SNicolas Souchu 1702bc35c174SNicolas Souchu /* enable interrupts */ 1703bc35c174SNicolas Souchu ecr &= ~PPC_SERVICE_INTR; 1704bc35c174SNicolas Souchu ppc->ppc_irqstat = PPC_IRQ_DMA; 1705bc35c174SNicolas Souchu w_ecr(ppc, ecr); 1706bc35c174SNicolas Souchu 1707bc35c174SNicolas Souchu isa_dmastart( 1708bc35c174SNicolas Souchu ppc->ppc_dmaflags, 1709bc35c174SNicolas Souchu ppc->ppc_dmaddr, 1710bc35c174SNicolas Souchu ppc->ppc_dmacnt, 1711bc35c174SNicolas Souchu ppc->ppc_dmachan); 1712bc35c174SNicolas Souchu #ifdef PPC_DEBUG 1713bc35c174SNicolas Souchu printf("s%d", ppc->ppc_dmacnt); 1714bc35c174SNicolas Souchu #endif 1715bc35c174SNicolas Souchu ppc->ppc_dmastat = PPC_DMA_STARTED; 1716bc35c174SNicolas Souchu 1717bc35c174SNicolas Souchu /* Wait for the DMA completed interrupt. We hope we won't 1718bc35c174SNicolas Souchu * miss it, otherwise a signal will be necessary to unlock the 1719bc35c174SNicolas Souchu * process. 1720bc35c174SNicolas Souchu */ 1721bc35c174SNicolas Souchu do { 1722bc35c174SNicolas Souchu /* release CPU */ 1723bc35c174SNicolas Souchu error = tsleep((caddr_t)ppc, 1724bc35c174SNicolas Souchu PPBPRI | PCATCH, "ppcdma", 0); 1725bc35c174SNicolas Souchu 1726bc35c174SNicolas Souchu } while (error == EWOULDBLOCK); 1727bc35c174SNicolas Souchu 1728bc35c174SNicolas Souchu splx(s); 1729bc35c174SNicolas Souchu 1730bc35c174SNicolas Souchu if (error) { 1731bc35c174SNicolas Souchu #ifdef PPC_DEBUG 1732bc35c174SNicolas Souchu printf("i"); 1733bc35c174SNicolas Souchu #endif 1734bc35c174SNicolas Souchu /* stop DMA */ 1735bc35c174SNicolas Souchu isa_dmadone( 1736bc35c174SNicolas Souchu ppc->ppc_dmaflags, ppc->ppc_dmaddr, 1737bc35c174SNicolas Souchu ppc->ppc_dmacnt, ppc->ppc_dmachan); 1738bc35c174SNicolas Souchu 1739bc35c174SNicolas Souchu /* no dma, no interrupt, flush the fifo */ 1740bc35c174SNicolas Souchu w_ecr(ppc, PPC_ECR_RESET); 1741bc35c174SNicolas Souchu 1742bc35c174SNicolas Souchu ppc->ppc_dmastat = PPC_DMA_INTERRUPTED; 1743bc35c174SNicolas Souchu goto error; 1744bc35c174SNicolas Souchu } 1745bc35c174SNicolas Souchu 1746bc35c174SNicolas Souchu /* wait for an empty fifo */ 1747bc35c174SNicolas Souchu while (!(r_ecr(ppc) & PPC_FIFO_EMPTY)) { 1748bc35c174SNicolas Souchu 1749bc35c174SNicolas Souchu for (spin=100; spin; spin--) 1750bc35c174SNicolas Souchu if (r_ecr(ppc) & PPC_FIFO_EMPTY) 1751bc35c174SNicolas Souchu goto fifo_empty; 1752bc35c174SNicolas Souchu #ifdef PPC_DEBUG 1753bc35c174SNicolas Souchu printf("Z"); 1754bc35c174SNicolas Souchu #endif 1755bc35c174SNicolas Souchu error = tsleep((caddr_t)ppc, PPBPRI | PCATCH, "ppcfifo", hz/100); 1756bc35c174SNicolas Souchu if (error != EWOULDBLOCK) { 1757bc35c174SNicolas Souchu #ifdef PPC_DEBUG 1758bc35c174SNicolas Souchu printf("I"); 1759bc35c174SNicolas Souchu #endif 1760bc35c174SNicolas Souchu /* no dma, no interrupt, flush the fifo */ 1761bc35c174SNicolas Souchu w_ecr(ppc, PPC_ECR_RESET); 1762bc35c174SNicolas Souchu 1763bc35c174SNicolas Souchu ppc->ppc_dmastat = PPC_DMA_INTERRUPTED; 1764bc35c174SNicolas Souchu error = EINTR; 1765bc35c174SNicolas Souchu goto error; 1766bc35c174SNicolas Souchu } 1767bc35c174SNicolas Souchu } 1768bc35c174SNicolas Souchu 1769bc35c174SNicolas Souchu fifo_empty: 1770bc35c174SNicolas Souchu /* no dma, no interrupt, flush the fifo */ 1771bc35c174SNicolas Souchu w_ecr(ppc, PPC_ECR_RESET); 1772bc35c174SNicolas Souchu 1773bc35c174SNicolas Souchu } else 1774bc35c174SNicolas Souchu error = EINVAL; /* XXX we should FIFO and 1775bc35c174SNicolas Souchu * interrupts */ 1776bc35c174SNicolas Souchu } else 1777bc35c174SNicolas Souchu error = EINVAL; 1778bc35c174SNicolas Souchu 1779bc35c174SNicolas Souchu error: 1780bc35c174SNicolas Souchu 1781bc35c174SNicolas Souchu /* PDRQ must be kept unasserted until nPDACK is 1782bc35c174SNicolas Souchu * deasserted for a minimum of 350ns (SMC datasheet) 1783bc35c174SNicolas Souchu * 1784bc35c174SNicolas Souchu * Consequence may be a FIFO that never empty 1785bc35c174SNicolas Souchu */ 1786bc35c174SNicolas Souchu DELAY(1); 1787bc35c174SNicolas Souchu 1788bc35c174SNicolas Souchu w_ecr(ppc, ecr_sav); 1789bc35c174SNicolas Souchu w_ctr(ppc, ctr_sav); 1790bc35c174SNicolas Souchu 1791bc35c174SNicolas Souchu return (error); 1792bc35c174SNicolas Souchu } 1793bc35c174SNicolas Souchu 179467646539SMike Smith static void 17950f210c92SNicolas Souchu ppc_reset_epp(device_t dev) 179667646539SMike Smith { 17970f210c92SNicolas Souchu struct ppc_data *ppc = DEVTOSOFTC(dev); 179867646539SMike Smith 17990f210c92SNicolas Souchu ppc_reset_epp_timeout(ppc); 180067646539SMike Smith 180167646539SMike Smith return; 180267646539SMike Smith } 180367646539SMike Smith 180467646539SMike Smith static int 18050f210c92SNicolas Souchu ppc_setmode(device_t dev, int mode) 18060f210c92SNicolas Souchu { 18070f210c92SNicolas Souchu struct ppc_data *ppc = DEVTOSOFTC(dev); 18080f210c92SNicolas Souchu 18090f210c92SNicolas Souchu switch (ppc->ppc_type) { 18100f210c92SNicolas Souchu case PPC_TYPE_SMCLIKE: 18110f210c92SNicolas Souchu return (ppc_smclike_setmode(ppc, mode)); 18120f210c92SNicolas Souchu break; 18130f210c92SNicolas Souchu 18140f210c92SNicolas Souchu case PPC_TYPE_GENERIC: 18150f210c92SNicolas Souchu default: 18160f210c92SNicolas Souchu return (ppc_generic_setmode(ppc, mode)); 18170f210c92SNicolas Souchu break; 18180f210c92SNicolas Souchu } 18190f210c92SNicolas Souchu 18200f210c92SNicolas Souchu /* not reached */ 18210f210c92SNicolas Souchu return (ENXIO); 18220f210c92SNicolas Souchu } 18230f210c92SNicolas Souchu 18247a15d619SGarrett Wollman static struct isa_pnp_id lpc_ids[] = { 18257a15d619SGarrett Wollman { 0x0004d041, "Standard parallel printer port" }, /* PNP0400 */ 18267a15d619SGarrett Wollman { 0x0104d041, "ECP parallel printer port" }, /* PNP0401 */ 18277a15d619SGarrett Wollman { 0 } 18287a15d619SGarrett Wollman }; 18297a15d619SGarrett Wollman 18300f210c92SNicolas Souchu static int 18310f210c92SNicolas Souchu ppc_probe(device_t dev) 183267646539SMike Smith { 1833d64d73c9SDoug Rabson #ifdef __i386__ 183467646539SMike Smith static short next_bios_ppc = 0; 1835d64d73c9SDoug Rabson #endif 183667646539SMike Smith struct ppc_data *ppc; 18370f210c92SNicolas Souchu device_t parent; 1838d64d73c9SDoug Rabson int error; 1839d64d73c9SDoug Rabson u_long port; 184067646539SMike Smith 18410f210c92SNicolas Souchu parent = device_get_parent(dev); 184267646539SMike Smith 18437a15d619SGarrett Wollman error = ISA_PNP_PROBE(parent, dev, lpc_ids); 18447a15d619SGarrett Wollman if (error == ENXIO) 18457a15d619SGarrett Wollman return (ENXIO); 18467a15d619SGarrett Wollman else if (error != 0) /* XXX shall be set after detection */ 1847a9d565fcSPeter Wemm device_set_desc(dev, "Parallel port"); 1848a9d565fcSPeter Wemm 184967646539SMike Smith /* 185067646539SMike Smith * Allocate the ppc_data structure. 185167646539SMike Smith */ 18520f210c92SNicolas Souchu ppc = DEVTOSOFTC(dev); 185367646539SMike Smith bzero(ppc, sizeof(struct ppc_data)); 185467646539SMike Smith 18550f210c92SNicolas Souchu ppc->rid_irq = ppc->rid_drq = ppc->rid_ioport = 0; 18560f210c92SNicolas Souchu ppc->res_irq = ppc->res_drq = ppc->res_ioport = 0; 185767646539SMike Smith 18580f210c92SNicolas Souchu /* retrieve ISA parameters */ 1859d64d73c9SDoug Rabson error = bus_get_resource(dev, SYS_RES_IOPORT, 0, &port, NULL); 18600f210c92SNicolas Souchu 1861d64d73c9SDoug Rabson #ifdef __i386__ 18620f210c92SNicolas Souchu /* 18630f210c92SNicolas Souchu * If port not specified, use bios list. 18640f210c92SNicolas Souchu */ 1865d64d73c9SDoug Rabson if (error) { 18660f210c92SNicolas Souchu if((next_bios_ppc < BIOS_MAX_PPC) && 18670f210c92SNicolas Souchu (*(BIOS_PORTS+next_bios_ppc) != 0) ) { 18680f210c92SNicolas Souchu port = *(BIOS_PORTS+next_bios_ppc++); 18690f210c92SNicolas Souchu if (bootverbose) 18700f210c92SNicolas Souchu device_printf(dev, "parallel port found at 0x%x\n", 1871d64d73c9SDoug Rabson (int) port); 18720f210c92SNicolas Souchu } else { 18730f210c92SNicolas Souchu device_printf(dev, "parallel port not found.\n"); 18740f210c92SNicolas Souchu return ENXIO; 18750f210c92SNicolas Souchu } 18766a5be862SDoug Rabson bus_set_resource(dev, SYS_RES_IOPORT, 0, port, 18776a5be862SDoug Rabson IO_LPTSIZE_EXTENDED); 18780f210c92SNicolas Souchu } 1879d64d73c9SDoug Rabson #endif 1880d64d73c9SDoug Rabson #ifdef __alpha__ 1881d64d73c9SDoug Rabson /* 1882d64d73c9SDoug Rabson * There isn't a bios list on alpha. Put it in the usual place. 1883d64d73c9SDoug Rabson */ 1884d64d73c9SDoug Rabson if (error) { 18856a5be862SDoug Rabson bus_set_resource(dev, SYS_RES_IOPORT, 0, 0x3bc, 18866a5be862SDoug Rabson IO_LPTSIZE_NORMAL); 1887d64d73c9SDoug Rabson } 1888d64d73c9SDoug Rabson #endif 18890f210c92SNicolas Souchu 18900f210c92SNicolas Souchu /* IO port is mandatory */ 18916a5be862SDoug Rabson 18926a5be862SDoug Rabson /* Try "extended" IO port range...*/ 18930f210c92SNicolas Souchu ppc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, 1894d64d73c9SDoug Rabson &ppc->rid_ioport, 0, ~0, 18956a5be862SDoug Rabson IO_LPTSIZE_EXTENDED, RF_ACTIVE); 18966a5be862SDoug Rabson 18976a5be862SDoug Rabson if (ppc->res_ioport != 0) { 18986a5be862SDoug Rabson if (bootverbose) 18996a5be862SDoug Rabson device_printf(dev, "using extended I/O port range\n"); 19006a5be862SDoug Rabson } else { 19016a5be862SDoug Rabson /* Failed? If so, then try the "normal" IO port range... */ 19026a5be862SDoug Rabson ppc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, 19036a5be862SDoug Rabson &ppc->rid_ioport, 0, ~0, 19046a5be862SDoug Rabson IO_LPTSIZE_NORMAL, 19056a5be862SDoug Rabson RF_ACTIVE); 19066a5be862SDoug Rabson if (ppc->res_ioport != 0) { 19076a5be862SDoug Rabson if (bootverbose) 19086a5be862SDoug Rabson device_printf(dev, "using normal I/O port range\n"); 19096a5be862SDoug Rabson } else { 19100f210c92SNicolas Souchu device_printf(dev, "cannot reserve I/O port range\n"); 19110f210c92SNicolas Souchu goto error; 19120f210c92SNicolas Souchu } 19135c885c3fSDoug Rabson } 19145c885c3fSDoug Rabson 1915d64d73c9SDoug Rabson ppc->ppc_base = rman_get_start(ppc->res_ioport); 19160f210c92SNicolas Souchu 19173ae3f8b0SNicolas Souchu ppc->bsh = rman_get_bushandle(ppc->res_ioport); 19183ae3f8b0SNicolas Souchu ppc->bst = rman_get_bustag(ppc->res_ioport); 19193ae3f8b0SNicolas Souchu 19200f210c92SNicolas Souchu ppc->ppc_flags = device_get_flags(dev); 19210f210c92SNicolas Souchu 19220f210c92SNicolas Souchu if (!(ppc->ppc_flags & 0x20)) { 19230f210c92SNicolas Souchu ppc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &ppc->rid_irq, 19240f210c92SNicolas Souchu 0ul, ~0ul, 1, RF_SHAREABLE); 19250f210c92SNicolas Souchu ppc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ, &ppc->rid_drq, 19260f210c92SNicolas Souchu 0ul, ~0ul, 1, RF_ACTIVE); 19270f210c92SNicolas Souchu } 19280f210c92SNicolas Souchu 19290f210c92SNicolas Souchu if (ppc->res_irq) 1930d64d73c9SDoug Rabson ppc->ppc_irq = rman_get_start(ppc->res_irq); 19310f210c92SNicolas Souchu if (ppc->res_drq) 1932d64d73c9SDoug Rabson ppc->ppc_dmachan = rman_get_start(ppc->res_drq); 19330f210c92SNicolas Souchu 19340f210c92SNicolas Souchu ppc->ppc_unit = device_get_unit(dev); 19350f210c92SNicolas Souchu ppc->ppc_model = GENERIC; 1936af548787SNicolas Souchu 193746f3ff79SMike Smith ppc->ppc_mode = PPB_COMPATIBLE; 19380f210c92SNicolas Souchu ppc->ppc_epp = (ppc->ppc_flags & 0x10) >> 4; 193967646539SMike Smith 19400f210c92SNicolas Souchu ppc->ppc_type = PPC_TYPE_GENERIC; 1941edcfcf27SNicolas Souchu 1942edcfcf27SNicolas Souchu /* 1943dc733423SDag-Erling Smørgrav * Try to detect the chipset and its mode. 194467646539SMike Smith */ 19450f210c92SNicolas Souchu if (ppc_detect(ppc, ppc->ppc_flags & 0xf)) 194667646539SMike Smith goto error; 194767646539SMike Smith 19480f210c92SNicolas Souchu return (0); 194967646539SMike Smith 195067646539SMike Smith error: 19510f210c92SNicolas Souchu if (ppc->res_irq != 0) { 19520f210c92SNicolas Souchu bus_release_resource(dev, SYS_RES_IRQ, ppc->rid_irq, 19530f210c92SNicolas Souchu ppc->res_irq); 19540f210c92SNicolas Souchu } 19550f210c92SNicolas Souchu if (ppc->res_ioport != 0) { 19560f210c92SNicolas Souchu bus_deactivate_resource(dev, SYS_RES_IOPORT, ppc->rid_ioport, 19570f210c92SNicolas Souchu ppc->res_ioport); 19580f210c92SNicolas Souchu bus_release_resource(dev, SYS_RES_IOPORT, ppc->rid_ioport, 19590f210c92SNicolas Souchu ppc->res_ioport); 19600f210c92SNicolas Souchu } 19610f210c92SNicolas Souchu if (ppc->res_drq != 0) { 19620f210c92SNicolas Souchu bus_deactivate_resource(dev, SYS_RES_DRQ, ppc->rid_drq, 19630f210c92SNicolas Souchu ppc->res_drq); 19640f210c92SNicolas Souchu bus_release_resource(dev, SYS_RES_DRQ, ppc->rid_drq, 19650f210c92SNicolas Souchu ppc->res_drq); 19660f210c92SNicolas Souchu } 19670f210c92SNicolas Souchu return (ENXIO); 196867646539SMike Smith } 196967646539SMike Smith 197067646539SMike Smith static int 19710f210c92SNicolas Souchu ppc_attach(device_t dev) 197267646539SMike Smith { 19730f210c92SNicolas Souchu struct ppc_data *ppc = DEVTOSOFTC(dev); 197467646539SMike Smith 19750f210c92SNicolas Souchu device_t ppbus; 19760f210c92SNicolas Souchu device_t parent = device_get_parent(dev); 19770f210c92SNicolas Souchu 19780f210c92SNicolas Souchu device_printf(dev, "%s chipset (%s) in %s mode%s\n", 19790f210c92SNicolas Souchu ppc_models[ppc->ppc_model], ppc_avms[ppc->ppc_avm], 198046f3ff79SMike Smith ppc_modes[ppc->ppc_mode], (PPB_IS_EPP(ppc->ppc_mode)) ? 198167646539SMike Smith ppc_epp_protocol[ppc->ppc_epp] : ""); 198267646539SMike Smith 1983bc35c174SNicolas Souchu if (ppc->ppc_fifo) 19840f210c92SNicolas Souchu device_printf(dev, "FIFO with %d/%d/%d bytes threshold\n", 19850f210c92SNicolas Souchu ppc->ppc_fifo, ppc->ppc_wthr, ppc->ppc_rthr); 198667646539SMike Smith 1987bc35c174SNicolas Souchu if ((ppc->ppc_avm & PPB_ECP) && (ppc->ppc_dmachan > 0)) { 19880f210c92SNicolas Souchu /* acquire the DMA channel forever */ /* XXX */ 1989bc35c174SNicolas Souchu isa_dma_acquire(ppc->ppc_dmachan); 1990bc35c174SNicolas Souchu isa_dmainit(ppc->ppc_dmachan, 1024); /* nlpt.BUFSIZE */ 1991bc35c174SNicolas Souchu } 1992bc35c174SNicolas Souchu 19930f210c92SNicolas Souchu /* add ppbus as a child of this isa to parallel bridge */ 19940f210c92SNicolas Souchu ppbus = device_add_child(dev, "ppbus", -1); 19950f210c92SNicolas Souchu 199667646539SMike Smith /* 199767646539SMike Smith * Probe the ppbus and attach devices found. 199867646539SMike Smith */ 19990f210c92SNicolas Souchu device_probe_and_attach(ppbus); 200067646539SMike Smith 20010f210c92SNicolas Souchu /* register the ppc interrupt handler as default */ 20020f210c92SNicolas Souchu if (ppc->res_irq) { 20030f210c92SNicolas Souchu /* default to the tty mask for registration */ /* XXX */ 20040f210c92SNicolas Souchu if (BUS_SETUP_INTR(parent, dev, ppc->res_irq, INTR_TYPE_TTY, 20050f210c92SNicolas Souchu ppcintr, dev, &ppc->intr_cookie) == 0) { 20060f210c92SNicolas Souchu 20070f210c92SNicolas Souchu /* remember the ppcintr is registered */ 20080f210c92SNicolas Souchu ppc->ppc_registered = 1; 200967646539SMike Smith } 20100f210c92SNicolas Souchu } 20110f210c92SNicolas Souchu 20120f210c92SNicolas Souchu return (0); 20130f210c92SNicolas Souchu } 20140f210c92SNicolas Souchu 20150f210c92SNicolas Souchu static u_char 20160f210c92SNicolas Souchu ppc_io(device_t ppcdev, int iop, u_char *addr, int cnt, u_char byte) 20170f210c92SNicolas Souchu { 20180f210c92SNicolas Souchu struct ppc_data *ppc = DEVTOSOFTC(ppcdev); 20190f210c92SNicolas Souchu switch (iop) { 20200f210c92SNicolas Souchu case PPB_OUTSB_EPP: 20213ae3f8b0SNicolas Souchu bus_space_write_multi_1(ppc->bst, ppc->bsh, PPC_EPP_DATA, addr, cnt); 20220f210c92SNicolas Souchu break; 20230f210c92SNicolas Souchu case PPB_OUTSW_EPP: 20243ae3f8b0SNicolas Souchu bus_space_write_multi_2(ppc->bst, ppc->bsh, PPC_EPP_DATA, (u_int16_t *)addr, cnt); 20250f210c92SNicolas Souchu break; 20260f210c92SNicolas Souchu case PPB_OUTSL_EPP: 20273ae3f8b0SNicolas Souchu bus_space_write_multi_4(ppc->bst, ppc->bsh, PPC_EPP_DATA, (u_int32_t *)addr, cnt); 20280f210c92SNicolas Souchu break; 20290f210c92SNicolas Souchu case PPB_INSB_EPP: 20303ae3f8b0SNicolas Souchu bus_space_read_multi_1(ppc->bst, ppc->bsh, PPC_EPP_DATA, addr, cnt); 20310f210c92SNicolas Souchu break; 20320f210c92SNicolas Souchu case PPB_INSW_EPP: 20333ae3f8b0SNicolas Souchu bus_space_read_multi_2(ppc->bst, ppc->bsh, PPC_EPP_DATA, (u_int16_t *)addr, cnt); 20340f210c92SNicolas Souchu break; 20350f210c92SNicolas Souchu case PPB_INSL_EPP: 20363ae3f8b0SNicolas Souchu bus_space_read_multi_4(ppc->bst, ppc->bsh, PPC_EPP_DATA, (u_int32_t *)addr, cnt); 20370f210c92SNicolas Souchu break; 20380f210c92SNicolas Souchu case PPB_RDTR: 20390f210c92SNicolas Souchu return (r_dtr(ppc)); 20400f210c92SNicolas Souchu break; 20410f210c92SNicolas Souchu case PPB_RSTR: 20420f210c92SNicolas Souchu return (r_str(ppc)); 20430f210c92SNicolas Souchu break; 20440f210c92SNicolas Souchu case PPB_RCTR: 20450f210c92SNicolas Souchu return (r_ctr(ppc)); 20460f210c92SNicolas Souchu break; 20470f210c92SNicolas Souchu case PPB_REPP_A: 20480f210c92SNicolas Souchu return (r_epp_A(ppc)); 20490f210c92SNicolas Souchu break; 20500f210c92SNicolas Souchu case PPB_REPP_D: 20510f210c92SNicolas Souchu return (r_epp_D(ppc)); 20520f210c92SNicolas Souchu break; 20530f210c92SNicolas Souchu case PPB_RECR: 20540f210c92SNicolas Souchu return (r_ecr(ppc)); 20550f210c92SNicolas Souchu break; 20560f210c92SNicolas Souchu case PPB_RFIFO: 20570f210c92SNicolas Souchu return (r_fifo(ppc)); 20580f210c92SNicolas Souchu break; 20590f210c92SNicolas Souchu case PPB_WDTR: 20600f210c92SNicolas Souchu w_dtr(ppc, byte); 20610f210c92SNicolas Souchu break; 20620f210c92SNicolas Souchu case PPB_WSTR: 20630f210c92SNicolas Souchu w_str(ppc, byte); 20640f210c92SNicolas Souchu break; 20650f210c92SNicolas Souchu case PPB_WCTR: 20660f210c92SNicolas Souchu w_ctr(ppc, byte); 20670f210c92SNicolas Souchu break; 20680f210c92SNicolas Souchu case PPB_WEPP_A: 20690f210c92SNicolas Souchu w_epp_A(ppc, byte); 20700f210c92SNicolas Souchu break; 20710f210c92SNicolas Souchu case PPB_WEPP_D: 20720f210c92SNicolas Souchu w_epp_D(ppc, byte); 20730f210c92SNicolas Souchu break; 20740f210c92SNicolas Souchu case PPB_WECR: 20750f210c92SNicolas Souchu w_ecr(ppc, byte); 20760f210c92SNicolas Souchu break; 20770f210c92SNicolas Souchu case PPB_WFIFO: 20780f210c92SNicolas Souchu w_fifo(ppc, byte); 20790f210c92SNicolas Souchu break; 20800f210c92SNicolas Souchu default: 20816e551fb6SDavid E. O'Brien panic("%s: unknown I/O operation", __func__); 20820f210c92SNicolas Souchu break; 20830f210c92SNicolas Souchu } 20840f210c92SNicolas Souchu 20850f210c92SNicolas Souchu return (0); /* not significative */ 20860f210c92SNicolas Souchu } 20870f210c92SNicolas Souchu 20880f210c92SNicolas Souchu static int 20890f210c92SNicolas Souchu ppc_read_ivar(device_t bus, device_t dev, int index, uintptr_t *val) 20900f210c92SNicolas Souchu { 20910f210c92SNicolas Souchu struct ppc_data *ppc = (struct ppc_data *)device_get_softc(bus); 20920f210c92SNicolas Souchu 20930f210c92SNicolas Souchu switch (index) { 20940f210c92SNicolas Souchu case PPC_IVAR_EPP_PROTO: 20950f210c92SNicolas Souchu *val = (u_long)ppc->ppc_epp; 20960f210c92SNicolas Souchu break; 20970f210c92SNicolas Souchu case PPC_IVAR_IRQ: 2098d64d73c9SDoug Rabson *val = (u_long)ppc->ppc_irq; 20990f210c92SNicolas Souchu break; 21000f210c92SNicolas Souchu default: 21010f210c92SNicolas Souchu return (ENOENT); 21020f210c92SNicolas Souchu } 21030f210c92SNicolas Souchu 21040f210c92SNicolas Souchu return (0); 21050f210c92SNicolas Souchu } 21060f210c92SNicolas Souchu 21070f210c92SNicolas Souchu /* 21080f210c92SNicolas Souchu * Resource is useless here since ppbus devices' interrupt handlers are 21090f210c92SNicolas Souchu * multiplexed to the same resource initially allocated by ppc 21100f210c92SNicolas Souchu */ 21110f210c92SNicolas Souchu static int 21120f210c92SNicolas Souchu ppc_setup_intr(device_t bus, device_t child, struct resource *r, int flags, 21130f210c92SNicolas Souchu void (*ihand)(void *), void *arg, void **cookiep) 21140f210c92SNicolas Souchu { 21150f210c92SNicolas Souchu int error; 21160f210c92SNicolas Souchu struct ppc_data *ppc = DEVTOSOFTC(bus); 21170f210c92SNicolas Souchu 21180f210c92SNicolas Souchu if (ppc->ppc_registered) { 21190f210c92SNicolas Souchu /* XXX refuse registration if DMA is in progress */ 21200f210c92SNicolas Souchu 21210f210c92SNicolas Souchu /* first, unregister the default interrupt handler */ 21220f210c92SNicolas Souchu if ((error = BUS_TEARDOWN_INTR(device_get_parent(bus), 21230f210c92SNicolas Souchu bus, ppc->res_irq, ppc->intr_cookie))) 21240f210c92SNicolas Souchu return (error); 21250f210c92SNicolas Souchu 21260f210c92SNicolas Souchu /* bus_deactivate_resource(bus, SYS_RES_IRQ, ppc->rid_irq, */ 21270f210c92SNicolas Souchu /* ppc->res_irq); */ 21280f210c92SNicolas Souchu 21290f210c92SNicolas Souchu /* DMA/FIFO operation won't be possible anymore */ 21300f210c92SNicolas Souchu ppc->ppc_registered = 0; 21310f210c92SNicolas Souchu } 21320f210c92SNicolas Souchu 21330f210c92SNicolas Souchu /* pass registration to the upper layer, ignore the incoming resource */ 21340f210c92SNicolas Souchu return (BUS_SETUP_INTR(device_get_parent(bus), child, 21350f210c92SNicolas Souchu r, flags, ihand, arg, cookiep)); 21360f210c92SNicolas Souchu } 21370f210c92SNicolas Souchu 21380f210c92SNicolas Souchu /* 21390f210c92SNicolas Souchu * When no underlying device has a registered interrupt, register the ppc 21400f210c92SNicolas Souchu * layer one 21410f210c92SNicolas Souchu */ 21420f210c92SNicolas Souchu static int 21430f210c92SNicolas Souchu ppc_teardown_intr(device_t bus, device_t child, struct resource *r, void *ih) 21440f210c92SNicolas Souchu { 21450f210c92SNicolas Souchu int error; 21460f210c92SNicolas Souchu struct ppc_data *ppc = DEVTOSOFTC(bus); 21470f210c92SNicolas Souchu device_t parent = device_get_parent(bus); 21480f210c92SNicolas Souchu 21490f210c92SNicolas Souchu /* pass unregistration to the upper layer */ 21500f210c92SNicolas Souchu if ((error = BUS_TEARDOWN_INTR(parent, child, r, ih))) 21510f210c92SNicolas Souchu return (error); 21520f210c92SNicolas Souchu 21530f210c92SNicolas Souchu /* default to the tty mask for registration */ /* XXX */ 21540f210c92SNicolas Souchu if (ppc->ppc_irq && 21550f210c92SNicolas Souchu !(error = BUS_SETUP_INTR(parent, bus, ppc->res_irq, 21560f210c92SNicolas Souchu INTR_TYPE_TTY, ppcintr, bus, &ppc->intr_cookie))) { 21570f210c92SNicolas Souchu 21580f210c92SNicolas Souchu /* remember the ppcintr is registered */ 21590f210c92SNicolas Souchu ppc->ppc_registered = 1; 21600f210c92SNicolas Souchu } 21610f210c92SNicolas Souchu 21620f210c92SNicolas Souchu return (error); 21630f210c92SNicolas Souchu } 21640f210c92SNicolas Souchu 21650f210c92SNicolas Souchu DRIVER_MODULE(ppc, isa, ppc_driver, ppc_devclass, 0, 0); 21665f063c7bSMike Smith DRIVER_MODULE(ppc, acpi, ppc_driver, ppc_devclass, 0, 0); 2167