Index: head/sys/alpha/alpha/autoconf.c =================================================================== --- head/sys/alpha/alpha/autoconf.c (revision 54072) +++ head/sys/alpha/alpha/autoconf.c (revision 54073) @@ -1,226 +1,226 @@ /*- * Copyright (c) 1998 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "opt_bootp.h" #include "opt_ffs.h" #include "opt_cd9660.h" #include "opt_nfsroot.h" #include #include #include #include #include /* for BASE_SLICE, MAX_SLICES */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void configure __P((void *)); SYSINIT(configure, SI_SUB_CONFIGURE, SI_ORDER_THIRD, configure, NULL) static void configure_finish __P((void)); static void configure_start __P((void)); #include "isa.h" #if NISA > 0 #include device_t isa_bus_device = 0; #endif extern int nfs_diskless_valid; dev_t rootdev = NODEV; dev_t dumpdev = NODEV; static void configure_start() { } static void configure_finish() { } #if 0 static int atoi(const char *s) { int n = 0; while (*s >= '0' && *s <= '9') n = n * 10 + (*s++ - '0'); return n; } static const char * bootdev_field(int which) { char *p = bootinfo.booted_dev; char *q; static char field[128]; /* Skip characters to find the right field */ for (; which; which--) { while (*p != ' ' && *p != '\0') p++; if (*p) p++; } /* Copy out the field and return it */ q = field; while (*p != ' ' && *p != '\0') *q++ = *p++; *q = '\0'; return field; } static const char * bootdev_protocol(void) { return bootdev_field(0); } static int bootdev_slot(void) { return atoi(bootdev_field(2)); } static int bootdev_unit(void) { return atoi(bootdev_field(5)); } static int bootdev_bus(void) { return atoi(bootdev_field(1)); } static int bootdev_channel(void) { return atoi(bootdev_field(3)); } static const char * bootdev_remote_address(void) { return bootdev_field(4); } static int bootdev_boot_dev_type(void) { return atoi(bootdev_field(6)); } static const char * bootdev_ctrl_dev_type(void) { return bootdev_field(7); } #endif /* * Determine i/o configuration for a machine. */ static void configure(void *dummy) { configure_start(); - device_add_child(root_bus, platform.iobus, 0, 0); + device_add_child(root_bus, platform.iobus, 0); root_bus_configure(); if((hwrpb->rpb_type != ST_DEC_3000_300) && (hwrpb->rpb_type != ST_DEC_3000_500)){ /* * Probe ISA devices after everything. */ #if NISA > 0 if (isa_bus_device) isa_probe_children(isa_bus_device); #endif } configure_finish(); cninit_finish(); /* * Now we're ready to handle (pending) interrupts. * XXX this is slightly misplaced. */ spl0(); cold = 0; } /* * Do legacy root filesystem discovery. This isn't really * needed on the Alpha, which has always used the loader. */ void cpu_rootconf() { int order = 0; #if defined(NFS) && defined(NFS_ROOT) #if !defined(BOOTP_NFSROOT) if (nfs_diskless_valid) #endif rootdevnames[order++] = "nfs:"; #endif #if defined(FFS) && defined(FFS_ROOT) rootdevnames[order++] = "ufs:da0a"; #endif } SYSINIT(cpu_rootconf, SI_SUB_ROOT_CONF, SI_ORDER_FIRST, cpu_rootconf, NULL) Index: head/sys/alpha/pci/apecs.c =================================================================== --- head/sys/alpha/pci/apecs.c (revision 54072) +++ head/sys/alpha/pci/apecs.c (revision 54073) @@ -1,681 +1,681 @@ /*- * Copyright (c) 1998 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Copyright (c) 1995, 1996 Carnegie-Mellon University. * All rights reserved. * * Author: Chris G. Demetriou * * Permission to use, copy, modify and distribute this software and * its documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ /* * Additional Copyright (c) 1998 by Andrew Gallatin for Duke University */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define KV(pa) ALPHA_PHYS_TO_K0SEG(pa) static devclass_t apecs_devclass; static device_t apecs0; /* XXX only one for now */ struct apecs_softc { vm_offset_t dmem_base; /* dense memory */ vm_offset_t smem_base; /* sparse memory */ vm_offset_t io_base; /* dense i/o */ vm_offset_t cfg0_base; /* dense pci0 config */ vm_offset_t cfg1_base; /* dense pci1 config */ }; #define APECS_SOFTC(dev) (struct apecs_softc*) device_get_softc(dev) static alpha_chipset_inb_t apecs_swiz_inb; static alpha_chipset_inw_t apecs_swiz_inw; static alpha_chipset_inl_t apecs_swiz_inl; static alpha_chipset_outb_t apecs_swiz_outb; static alpha_chipset_outw_t apecs_swiz_outw; static alpha_chipset_outl_t apecs_swiz_outl; static alpha_chipset_readb_t apecs_swiz_readb; static alpha_chipset_readw_t apecs_swiz_readw; static alpha_chipset_readl_t apecs_swiz_readl; static alpha_chipset_writeb_t apecs_swiz_writeb; static alpha_chipset_writew_t apecs_swiz_writew; static alpha_chipset_writel_t apecs_swiz_writel; static alpha_chipset_maxdevs_t apecs_swiz_maxdevs; static alpha_chipset_cfgreadb_t apecs_swiz_cfgreadb; static alpha_chipset_cfgreadw_t apecs_swiz_cfgreadw; static alpha_chipset_cfgreadl_t apecs_swiz_cfgreadl; static alpha_chipset_cfgwriteb_t apecs_swiz_cfgwriteb; static alpha_chipset_cfgwritew_t apecs_swiz_cfgwritew; static alpha_chipset_cfgwritel_t apecs_swiz_cfgwritel; static alpha_chipset_addrcvt_t apecs_cvt_dense; static alpha_chipset_read_hae_t apecs_read_hae; static alpha_chipset_write_hae_t apecs_write_hae; static alpha_chipset_t apecs_swiz_chipset = { apecs_swiz_inb, apecs_swiz_inw, apecs_swiz_inl, apecs_swiz_outb, apecs_swiz_outw, apecs_swiz_outl, apecs_swiz_readb, apecs_swiz_readw, apecs_swiz_readl, apecs_swiz_writeb, apecs_swiz_writew, apecs_swiz_writel, apecs_swiz_maxdevs, apecs_swiz_cfgreadb, apecs_swiz_cfgreadw, apecs_swiz_cfgreadl, apecs_swiz_cfgwriteb, apecs_swiz_cfgwritew, apecs_swiz_cfgwritel, apecs_cvt_dense, NULL, apecs_read_hae, apecs_write_hae, }; static int apecs_swiz_maxdevs(u_int b) { return 12; /* XXX */ } static u_int8_t apecs_swiz_inb(u_int32_t port) { alpha_mb(); return SPARSE_READ_BYTE(KV(APECS_PCI_SIO), port); } static u_int16_t apecs_swiz_inw(u_int32_t port) { alpha_mb(); return SPARSE_READ_WORD(KV(APECS_PCI_SIO), port); } static u_int32_t apecs_swiz_inl(u_int32_t port) { alpha_mb(); return SPARSE_READ_LONG(KV(APECS_PCI_SIO), port); } static void apecs_swiz_outb(u_int32_t port, u_int8_t data) { SPARSE_WRITE_BYTE(KV(APECS_PCI_SIO), port, data); alpha_wmb(); } static void apecs_swiz_outw(u_int32_t port, u_int16_t data) { SPARSE_WRITE_WORD(KV(APECS_PCI_SIO), port, data); alpha_wmb(); } static void apecs_swiz_outl(u_int32_t port, u_int32_t data) { SPARSE_WRITE_LONG(KV(APECS_PCI_SIO), port, data); alpha_wmb(); } /* * Memory functions. * * XXX linux does 32-bit reads/writes via dense space. This doesn't * appear to work for devices behind a ppb. I'm using sparse * accesses & they appear to work just fine everywhere. */ static u_int32_t apecs_hae_mem; #define REG1 (1UL << 24) static __inline void apecs_swiz_set_hae_mem(u_int32_t *pa) { int s; u_int32_t msb; if(*pa >= REG1){ msb = *pa & 0xf8000000; *pa -= msb; s = splhigh(); if (msb != apecs_hae_mem) { apecs_hae_mem = msb; REGVAL(EPIC_HAXR1) = apecs_hae_mem; alpha_mb(); apecs_hae_mem = REGVAL(EPIC_HAXR1); } splx(s); } } static u_int8_t apecs_swiz_readb(u_int32_t pa) { alpha_mb(); apecs_swiz_set_hae_mem(&pa); return SPARSE_READ_BYTE(KV(APECS_PCI_SPARSE), pa); } static u_int16_t apecs_swiz_readw(u_int32_t pa) { alpha_mb(); apecs_swiz_set_hae_mem(&pa); return SPARSE_READ_WORD(KV(APECS_PCI_SPARSE), pa); } static u_int32_t apecs_swiz_readl(u_int32_t pa) { alpha_mb(); apecs_swiz_set_hae_mem(&pa); return SPARSE_READ_LONG(KV(APECS_PCI_SPARSE), pa); } static void apecs_swiz_writeb(u_int32_t pa, u_int8_t data) { apecs_swiz_set_hae_mem(&pa); SPARSE_WRITE_BYTE(KV(APECS_PCI_SPARSE), pa, data); alpha_wmb(); } static void apecs_swiz_writew(u_int32_t pa, u_int16_t data) { apecs_swiz_set_hae_mem(&pa); SPARSE_WRITE_WORD(KV(APECS_PCI_SPARSE), pa, data); alpha_wmb(); } static void apecs_swiz_writel(u_int32_t pa, u_int32_t data) { apecs_swiz_set_hae_mem(&pa); SPARSE_WRITE_LONG(KV(APECS_PCI_SPARSE), pa, data); alpha_wmb(); } #define APECS_SWIZ_CFGOFF(b, s, f, r) \ (((b) << 16) | ((s) << 11) | ((f) << 8) | (r)) #define APECS_TYPE1_SETUP(b,s,old_haxr2) if((b)) { \ do { \ (s) = splhigh(); \ (old_haxr2) = REGVAL(EPIC_HAXR2); \ alpha_mb(); \ REGVAL(EPIC_HAXR2) = (old_haxr2) | 0x1; \ alpha_mb(); \ } while(0); \ } #define APECS_TYPE1_TEARDOWN(b,s,old_haxr2) if((b)) { \ do { \ alpha_mb(); \ REGVAL(EPIC_HAXR2) = (old_haxr2); \ alpha_mb(); \ splx((s)); \ } while(0); \ } #define SWIZ_CFGREAD(b, s, f, r, width, type) \ type val = ~0; \ int ipl = 0; \ u_int32_t old_haxr2 = 0; \ struct apecs_softc* sc = APECS_SOFTC(apecs0); \ vm_offset_t off = APECS_SWIZ_CFGOFF(b, s, f, r); \ vm_offset_t kv = SPARSE_##width##_ADDRESS(sc->cfg0_base, off); \ alpha_mb(); \ APECS_TYPE1_SETUP(b,ipl,old_haxr2); \ if (!badaddr((caddr_t)kv, sizeof(type))) { \ val = SPARSE_##width##_EXTRACT(off, SPARSE_READ(kv)); \ } \ APECS_TYPE1_TEARDOWN(b,ipl,old_haxr2); \ return val; #define SWIZ_CFGWRITE(b, s, f, r, data, width, type) \ int ipl = 0; \ u_int32_t old_haxr2 = 0; \ struct apecs_softc* sc = APECS_SOFTC(apecs0); \ vm_offset_t off = APECS_SWIZ_CFGOFF(b, s, f, r); \ vm_offset_t kv = SPARSE_##width##_ADDRESS(sc->cfg0_base, off); \ alpha_mb(); \ APECS_TYPE1_SETUP(b,ipl,old_haxr2); \ if (!badaddr((caddr_t)kv, sizeof(type))) { \ SPARSE_WRITE(kv, SPARSE_##width##_INSERT(off, data)); \ alpha_wmb(); \ } \ APECS_TYPE1_TEARDOWN(b,ipl,old_haxr2); \ return; #if 1 static u_int8_t apecs_swiz_cfgreadb(u_int h, u_int b, u_int s, u_int f, u_int r) { SWIZ_CFGREAD(b, s, f, r, BYTE, u_int8_t); } static u_int16_t apecs_swiz_cfgreadw(u_int h, u_int b, u_int s, u_int f, u_int r) { SWIZ_CFGREAD(b, s, f, r, WORD, u_int16_t); } static u_int32_t apecs_swiz_cfgreadl(u_int h, u_int b, u_int s, u_int f, u_int r) { SWIZ_CFGREAD(b, s, f, r, LONG, u_int32_t); } static void apecs_swiz_cfgwriteb(u_int h, u_int b, u_int s, u_int f, u_int r, u_int8_t data) { SWIZ_CFGWRITE(b, s, f, r, data, BYTE, u_int8_t); } static void apecs_swiz_cfgwritew(u_int h, u_int b, u_int s, u_int f, u_int r, u_int16_t data) { SWIZ_CFGWRITE(b, s, f, r, data, WORD, u_int16_t); } static void apecs_swiz_cfgwritel(u_int h, u_int b, u_int s, u_int f, u_int r, u_int32_t data) { SWIZ_CFGWRITE(b, s, f, r, data, LONG, u_int32_t); } #else static u_int8_t apecs_swiz_cfgreadb(u_int h, u_int b, u_int s, u_int f, u_int r) { struct apecs_softc* sc = APECS_SOFTC(apecs0); vm_offset_t off = APECS_SWIZ_CFGOFF(b, s, f, r); alpha_mb(); if (badaddr((caddr_t)(sc->cfg0_base + SPARSE_BYTE_OFFSET(off)), 1)) return ~0; return SPARSE_READ_BYTE(sc->cfg0_base, off); } static u_int16_t apecs_swiz_cfgreadw(u_int h, u_int b, u_int s, u_int f, u_int r) { struct apecs_softc* sc = APECS_SOFTC(apecs0); vm_offset_t off = APECS_SWIZ_CFGOFF(b, s, f, r); alpha_mb(); if (badaddr((caddr_t)(sc->cfg0_base + SPARSE_WORD_OFFSET(off)), 2)) return ~0; return SPARSE_READ_WORD(sc->cfg0_base, off); } static u_int32_t apecs_swiz_cfgreadl(u_int h, u_int b, u_int s, u_int f, u_int r) { struct apecs_softc* sc = APECS_SOFTC(apecs0); vm_offset_t off = APECS_SWIZ_CFGOFF(b, s, f, r); alpha_mb(); if (badaddr((caddr_t)(sc->cfg0_base + SPARSE_LONG_OFFSET(off)), 4)) return ~0; return SPARSE_READ_LONG(sc->cfg0_base, off); } static void apecs_swiz_cfgwriteb(u_int h, u_int b, u_int s, u_int f, u_int r, u_int8_t data) { struct apecs_softc* sc = APECS_SOFTC(apecs0); vm_offset_t off = APECS_SWIZ_CFGOFF(b, s, f, r); if (badaddr((caddr_t)(sc->cfg0_base + SPARSE_BYTE_OFFSET(off)), 1)) return; SPARSE_WRITE_BYTE(sc->cfg0_base, off, data); alpha_wmb(); } static void apecs_swiz_cfgwritew(u_int h, u_int b, u_int s, u_int f, u_int r, u_int16_t data) { struct apecs_softc* sc = APECS_SOFTC(apecs0); vm_offset_t off = APECS_SWIZ_CFGOFF(b, s, f, r); if (badaddr((caddr_t)(sc->cfg0_base + SPARSE_WORD_OFFSET(off)), 2)) return; SPARSE_WRITE_WORD(sc->cfg0_base, off, data); alpha_wmb(); } static void apecs_swiz_cfgwritel(u_int h, u_int b, u_int s, u_int f, u_int r, u_int32_t data) { struct apecs_softc* sc = APECS_SOFTC(apecs0); vm_offset_t off = APECS_SWIZ_CFGOFF(b, s, f, r); if (badaddr((caddr_t)(sc->cfg0_base + SPARSE_LONG_OFFSET(off)), 4)) return; SPARSE_WRITE_LONG(sc->cfg0_base, off, data); alpha_wmb(); } #endif static vm_offset_t apecs_cvt_dense(vm_offset_t addr) { addr &= 0xffffffffUL; return (addr | APECS_PCI_DENSE); } static u_int64_t apecs_read_hae(void) { return apecs_hae_mem & 0xf8000000; } static void apecs_write_hae(u_int64_t hae) { u_int32_t pa = hae; apecs_swiz_set_hae_mem(&pa); } static int apecs_probe(device_t dev); static int apecs_attach(device_t dev); static struct resource *apecs_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); static int apecs_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r); static int apecs_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep); static int apecs_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie); static device_method_t apecs_methods[] = { /* Device interface */ DEVMETHOD(device_probe, apecs_probe), DEVMETHOD(device_attach, apecs_attach), /* Bus interface */ DEVMETHOD(bus_alloc_resource, apecs_alloc_resource), DEVMETHOD(bus_release_resource, apecs_release_resource), DEVMETHOD(bus_activate_resource, pci_activate_resource), DEVMETHOD(bus_deactivate_resource, pci_deactivate_resource), DEVMETHOD(bus_setup_intr, apecs_setup_intr), DEVMETHOD(bus_teardown_intr, apecs_teardown_intr), { 0, 0 } }; static driver_t apecs_driver = { "apecs", apecs_methods, sizeof(struct apecs_softc), }; #define APECS_SGMAP_BASE (8*1024*1024) #define APECS_SGMAP_SIZE (8*1024*1024) static void apecs_sgmap_invalidate(void) { alpha_mb(); REGVAL(EPIC_TBIA) = 0; alpha_mb(); } static void apecs_sgmap_map(void *arg, vm_offset_t ba, vm_offset_t pa) { u_int64_t *sgtable = arg; int index = alpha_btop(ba - APECS_SGMAP_BASE); if (pa) { if (pa > (1L<<32)) panic("apecs_sgmap_map: can't map address 0x%lx", pa); sgtable[index] = ((pa >> 13) << 1) | 1; } else { sgtable[index] = 0; } alpha_mb(); apecs_sgmap_invalidate(); } static void apecs_init_sgmap(void) { void *sgtable; /* * First setup Window 0 to map 8Mb to 16Mb with an * sgmap. Allocate the map aligned to a 32 boundary. */ REGVAL(EPIC_PCI_BASE_1) = APECS_SGMAP_BASE | EPIC_PCI_BASE_SGEN | EPIC_PCI_BASE_WENB; alpha_mb(); REGVAL(EPIC_PCI_MASK_1) = EPIC_PCI_MASK_8M; alpha_mb(); sgtable = contigmalloc(8192, M_DEVBUF, M_NOWAIT, 0, (1L<<34), 32*1024, (1L<<34)); if (!sgtable) panic("apecs_init_sgmap: can't allocate page table"); REGVAL(EPIC_TBASE_1) = (pmap_kextract((vm_offset_t) sgtable) >> EPIC_TBASE_SHIFT); chipset.sgmap = sgmap_map_create(APECS_SGMAP_BASE, APECS_SGMAP_BASE + APECS_SGMAP_SIZE, apecs_sgmap_map, sgtable); } void apecs_init() { static int initted = 0; if (initted) return; initted = 1; chipset = apecs_swiz_chipset; if (platform.pci_intr_init) platform.pci_intr_init(); } static int apecs_probe(device_t dev) { int memwidth; if (apecs0) return ENXIO; apecs0 = dev; memwidth = (REGVAL(COMANCHE_GCR) & COMANCHE_GCR_WIDEMEM) != 0 ? 128 : 64; if(memwidth == 64){ device_set_desc(dev, "DECchip 21071 Core Logic chipset"); } else { device_set_desc(dev, "DECchip 21072 Core Logic chipset"); } apecs_hae_mem = REGVAL(EPIC_HAXR1); pci_init_resources(); isa_init_intr(); apecs_init_sgmap(); - device_add_child(dev, "pcib", 0, 0); + device_add_child(dev, "pcib", 0); return 0; } static int apecs_attach(device_t dev) { struct apecs_softc* sc = APECS_SOFTC(dev); apecs_init(); sc->dmem_base = APECS_PCI_DENSE; sc->smem_base = APECS_PCI_SPARSE; sc->io_base = APECS_PCI_SIO; sc->cfg0_base = KV(APECS_PCI_CONF); sc->cfg1_base = NULL; set_iointr(alpha_dispatch_intr); snprintf(chipset_type, sizeof(chipset_type), "apecs"); chipset_bwx = 0; chipset_ports = APECS_PCI_SIO; chipset_memory = APECS_PCI_SPARSE; chipset_dense = APECS_PCI_DENSE; chipset_hae_mask = EPIC_HAXR1_EADDR; bus_generic_attach(dev); return 0; } static struct resource * apecs_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { if ((hwrpb->rpb_type == ST_DEC_2100_A50) && (type == SYS_RES_IRQ)) return isa_alloc_intr(bus, child, start); else return pci_alloc_resource(bus, child, type, rid, start, end, count, flags); } static int apecs_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { if ((hwrpb->rpb_type == ST_DEC_2100_A50) && (type == SYS_RES_IRQ)) return isa_release_intr(bus, child, r); else return pci_release_resource(bus, child, type, rid, r); } static int apecs_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep) { int error; /* * the avanti routes interrupts through the isa interrupt * controller, so we need to special case it */ if(hwrpb->rpb_type == ST_DEC_2100_A50) return isa_setup_intr(dev, child, irq, flags, intr, arg, cookiep); error = rman_activate_resource(irq); if (error) return error; error = alpha_setup_intr(0x900 + (irq->r_start << 4), intr, arg, cookiep, &intrcnt[INTRCNT_EB64PLUS_IRQ + irq->r_start]); if (error) return error; /* Enable PCI interrupt */ platform.pci_intr_enable(irq->r_start); device_printf(child, "interrupting at APECS irq %d\n", (int) irq->r_start); return 0; } static int apecs_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie) { /* * the avanti routes interrupts through the isa interrupt * controller, so we need to special case it */ if(hwrpb->rpb_type == ST_DEC_2100_A50) return isa_teardown_intr(dev, child, irq, cookie); alpha_teardown_intr(cookie); return rman_deactivate_resource(irq); } DRIVER_MODULE(apecs, root, apecs_driver, apecs_devclass, 0, 0); Index: head/sys/alpha/pci/apecs_pci.c =================================================================== --- head/sys/alpha/pci/apecs_pci.c (revision 54072) +++ head/sys/alpha/pci/apecs_pci.c (revision 54073) @@ -1,84 +1,84 @@ /*- * Copyright (c) 1998 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPEAPECSL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include static devclass_t pcib_devclass; static int apecs_pcib_probe(device_t dev) { device_set_desc(dev, "2107x PCI host bus adapter"); - device_add_child(dev, "pci", 0, 0); + device_add_child(dev, "pci", 0); return 0; } static int apecs_pcib_read_ivar(device_t dev, device_t child, int which, u_long *result) { if (which == PCIB_IVAR_HOSE) { *result = 0; return 0; } return ENOENT; } static device_method_t apecs_pcib_methods[] = { /* Device interface */ DEVMETHOD(device_probe, apecs_pcib_probe), DEVMETHOD(device_attach, bus_generic_attach), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, apecs_pcib_read_ivar), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t apecs_pcib_driver = { "pcib", apecs_pcib_methods, 1, }; DRIVER_MODULE(pcib, apecs, apecs_pcib_driver, pcib_devclass, 0, 0); Index: head/sys/alpha/pci/cia.c =================================================================== --- head/sys/alpha/pci/cia.c (revision 54072) +++ head/sys/alpha/pci/cia.c (revision 54073) @@ -1,1016 +1,1016 @@ /*- * Copyright (c) 1998 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, * NASA Ames Research Center. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 1995, 1996 Carnegie-Mellon University. * All rights reserved. * * Author: Chris G. Demetriou * * Permission to use, copy, modify and distribute this software and * its documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include "opt_cpu.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define KV(pa) ALPHA_PHYS_TO_K0SEG(pa) static devclass_t cia_devclass; static device_t cia0; /* XXX only one for now */ static u_int32_t cia_hae_mem; static int cia_rev, cia_ispyxis, cia_config; struct cia_softc { int junk; /* no softc */ }; #define CIA_SOFTC(dev) (struct cia_softc*) device_get_softc(dev) static alpha_chipset_inb_t cia_bwx_inb, cia_swiz_inb; static alpha_chipset_inw_t cia_bwx_inw, cia_swiz_inw; static alpha_chipset_inl_t cia_bwx_inl, cia_swiz_inl; static alpha_chipset_outb_t cia_bwx_outb, cia_swiz_outb; static alpha_chipset_outw_t cia_bwx_outw, cia_swiz_outw; static alpha_chipset_outl_t cia_bwx_outl, cia_swiz_outl; static alpha_chipset_readb_t cia_bwx_readb, cia_swiz_readb; static alpha_chipset_readw_t cia_bwx_readw, cia_swiz_readw; static alpha_chipset_readl_t cia_bwx_readl, cia_swiz_readl; static alpha_chipset_writeb_t cia_bwx_writeb, cia_swiz_writeb; static alpha_chipset_writew_t cia_bwx_writew, cia_swiz_writew; static alpha_chipset_writel_t cia_bwx_writel, cia_swiz_writel; static alpha_chipset_maxdevs_t cia_bwx_maxdevs, cia_swiz_maxdevs; static alpha_chipset_cfgreadb_t cia_bwx_cfgreadb, cia_swiz_cfgreadb; static alpha_chipset_cfgreadw_t cia_bwx_cfgreadw, cia_swiz_cfgreadw; static alpha_chipset_cfgreadl_t cia_bwx_cfgreadl, cia_swiz_cfgreadl; static alpha_chipset_cfgwriteb_t cia_bwx_cfgwriteb, cia_swiz_cfgwriteb; static alpha_chipset_cfgwritew_t cia_bwx_cfgwritew, cia_swiz_cfgwritew; static alpha_chipset_cfgwritel_t cia_bwx_cfgwritel, cia_swiz_cfgwritel; static alpha_chipset_addrcvt_t cia_cvt_dense, cia_cvt_bwx; static alpha_chipset_read_hae_t cia_read_hae; static alpha_chipset_write_hae_t cia_write_hae; static alpha_chipset_t cia_bwx_chipset = { cia_bwx_inb, cia_bwx_inw, cia_bwx_inl, cia_bwx_outb, cia_bwx_outw, cia_bwx_outl, cia_bwx_readb, cia_bwx_readw, cia_bwx_readl, cia_bwx_writeb, cia_bwx_writew, cia_bwx_writel, cia_bwx_maxdevs, cia_bwx_cfgreadb, cia_bwx_cfgreadw, cia_bwx_cfgreadl, cia_bwx_cfgwriteb, cia_bwx_cfgwritew, cia_bwx_cfgwritel, cia_cvt_dense, cia_cvt_bwx, cia_read_hae, cia_write_hae, }; static alpha_chipset_t cia_swiz_chipset = { cia_swiz_inb, cia_swiz_inw, cia_swiz_inl, cia_swiz_outb, cia_swiz_outw, cia_swiz_outl, cia_swiz_readb, cia_swiz_readw, cia_swiz_readl, cia_swiz_writeb, cia_swiz_writew, cia_swiz_writel, cia_swiz_maxdevs, cia_swiz_cfgreadb, cia_swiz_cfgreadw, cia_swiz_cfgreadl, cia_swiz_cfgwriteb, cia_swiz_cfgwritew, cia_swiz_cfgwritel, cia_cvt_dense, NULL, cia_read_hae, cia_write_hae, }; static u_int8_t cia_bwx_inb(u_int32_t port) { alpha_mb(); return ldbu(KV(CIA_EV56_BWIO+BWX_EV56_INT1 + port)); } static u_int16_t cia_bwx_inw(u_int32_t port) { alpha_mb(); return ldwu(KV(CIA_EV56_BWIO+BWX_EV56_INT2 + port)); } static u_int32_t cia_bwx_inl(u_int32_t port) { alpha_mb(); return ldl(KV(CIA_EV56_BWIO+BWX_EV56_INT4 + port)); } static void cia_bwx_outb(u_int32_t port, u_int8_t data) { stb(KV(CIA_EV56_BWIO+BWX_EV56_INT1 + port), data); alpha_wmb(); } static void cia_bwx_outw(u_int32_t port, u_int16_t data) { stw(KV(CIA_EV56_BWIO+BWX_EV56_INT2 + port), data); alpha_wmb(); } static void cia_bwx_outl(u_int32_t port, u_int32_t data) { stl(KV(CIA_EV56_BWIO+BWX_EV56_INT4 + port), data); alpha_wmb(); } static u_int8_t cia_bwx_readb(u_int32_t pa) { alpha_mb(); return ldbu(KV(CIA_EV56_BWMEM+BWX_EV56_INT1 + pa)); } static u_int16_t cia_bwx_readw(u_int32_t pa) { alpha_mb(); return ldwu(KV(CIA_EV56_BWMEM+BWX_EV56_INT2 + pa)); } static u_int32_t cia_bwx_readl(u_int32_t pa) { alpha_mb(); return ldl(KV(CIA_EV56_BWMEM+BWX_EV56_INT4 + pa)); } static void cia_bwx_writeb(u_int32_t pa, u_int8_t data) { stb(KV(CIA_EV56_BWMEM+BWX_EV56_INT1 + pa), data); alpha_wmb(); } static void cia_bwx_writew(u_int32_t pa, u_int16_t data) { stw(KV(CIA_EV56_BWMEM+BWX_EV56_INT2 + pa), data); alpha_wmb(); } static void cia_bwx_writel(u_int32_t pa, u_int32_t data) { stl(KV(CIA_EV56_BWMEM+BWX_EV56_INT4 + pa), data); alpha_wmb(); } static int cia_bwx_maxdevs(u_int b) { return 12; /* XXX */ } static void cia_clear_abort(void) { /* * Some (apparently-common) revisions of EB164 and AlphaStation * firmware do the Wrong thing with PCI master and target aborts, * which are caused by accesing the configuration space of devices * that don't exist (for example). * * To work around this, we clear the CIA error register's PCI * master and target abort bits before touching PCI configuration * space and check it afterwards. If it indicates a master or target * abort, the device wasn't there so we return 0xffffffff. */ REGVAL(CIA_CSR_CIA_ERR) = CIA_ERR_RCVD_MAS_ABT|CIA_ERR_RCVD_TAR_ABT; alpha_mb(); alpha_pal_draina(); } static int cia_check_abort(void) { u_int32_t errbits; int ba = 0; alpha_pal_draina(); alpha_mb(); errbits = REGVAL(CIA_CSR_CIA_ERR); if (errbits & (CIA_ERR_RCVD_MAS_ABT|CIA_ERR_RCVD_TAR_ABT)) ba = 1; if (errbits) { REGVAL(CIA_CSR_CIA_ERR) = errbits; alpha_mb(); alpha_pal_draina(); } return ba; } #define CIA_BWX_CFGADDR(b, s, f, r) \ KV(((b) ? CIA_EV56_BWCONF1 : CIA_EV56_BWCONF0) \ | ((b) << 16) | ((s) << 11) | ((f) << 8) | (r)) static u_int8_t cia_bwx_cfgreadb(u_int h, u_int b, u_int s, u_int f, u_int r) { vm_offset_t va = CIA_BWX_CFGADDR(b, s, f, r); u_int8_t data; cia_clear_abort(); if (badaddr((caddr_t)va, 1)) { cia_check_abort(); return ~0; } data = ldbu(va+BWX_EV56_INT1); if (cia_check_abort()) return ~0; return data; } static u_int16_t cia_bwx_cfgreadw(u_int h, u_int b, u_int s, u_int f, u_int r) { vm_offset_t va = CIA_BWX_CFGADDR(b, s, f, r); u_int16_t data; cia_clear_abort(); if (badaddr((caddr_t)va, 2)) { cia_check_abort(); return ~0; } data = ldwu(va+BWX_EV56_INT2); if (cia_check_abort()) return ~0; return data; } static u_int32_t cia_bwx_cfgreadl(u_int h, u_int b, u_int s, u_int f, u_int r) { vm_offset_t va = CIA_BWX_CFGADDR(b, s, f, r); u_int32_t data; cia_clear_abort(); if (badaddr((caddr_t)va, 4)) { cia_check_abort(); return ~0; } data = ldl(va+BWX_EV56_INT4); if (cia_check_abort()) return ~0; return data; } static void cia_bwx_cfgwriteb(u_int h, u_int b, u_int s, u_int f, u_int r, u_int8_t data) { vm_offset_t va = CIA_BWX_CFGADDR(b, s, f, r); cia_clear_abort(); if (badaddr((caddr_t)va, 1)) return; stb(va+BWX_EV56_INT1, data); cia_check_abort(); } static void cia_bwx_cfgwritew(u_int h, u_int b, u_int s, u_int f, u_int r, u_int16_t data) { vm_offset_t va = CIA_BWX_CFGADDR(b, s, f, r); if (badaddr((caddr_t)va, 2)) return; stw(va+BWX_EV56_INT2, data); cia_check_abort(); } static void cia_bwx_cfgwritel(u_int h, u_int b, u_int s, u_int f, u_int r, u_int32_t data) { vm_offset_t va = CIA_BWX_CFGADDR(b, s, f, r); if (badaddr((caddr_t)va, 4)) return; stl(va+BWX_EV56_INT4, data); cia_check_abort(); } static u_int8_t cia_swiz_inb(u_int32_t port) { alpha_mb(); return SPARSE_READ_BYTE(KV(CIA_PCI_SIO1), port); } static u_int16_t cia_swiz_inw(u_int32_t port) { alpha_mb(); return SPARSE_READ_WORD(KV(CIA_PCI_SIO1), port); } static u_int32_t cia_swiz_inl(u_int32_t port) { alpha_mb(); return SPARSE_READ_LONG(KV(CIA_PCI_SIO1), port); } static void cia_swiz_outb(u_int32_t port, u_int8_t data) { SPARSE_WRITE_BYTE(KV(CIA_PCI_SIO1), port, data); alpha_wmb(); } static void cia_swiz_outw(u_int32_t port, u_int16_t data) { SPARSE_WRITE_WORD(KV(CIA_PCI_SIO1), port, data); alpha_wmb(); } static void cia_swiz_outl(u_int32_t port, u_int32_t data) { SPARSE_WRITE_LONG(KV(CIA_PCI_SIO1), port, data); alpha_wmb(); } static __inline void cia_swiz_set_hae_mem(u_int32_t *pa) { /* Only bother with region 1 */ #define REG1 (7 << 29) if ((cia_hae_mem & REG1) != (*pa & REG1)) { /* * Seems fairly paranoid but this is what Linux does... */ u_int32_t msb = *pa & REG1; int s = splhigh(); cia_hae_mem = (cia_hae_mem & ~REG1) | msb; REGVAL(CIA_CSR_HAE_MEM) = cia_hae_mem; alpha_mb(); cia_hae_mem = REGVAL(CIA_CSR_HAE_MEM); splx(s); *pa -= msb; } } static u_int8_t cia_swiz_readb(u_int32_t pa) { alpha_mb(); cia_swiz_set_hae_mem(&pa); return SPARSE_READ_BYTE(KV(CIA_PCI_SMEM1), pa); } static u_int16_t cia_swiz_readw(u_int32_t pa) { alpha_mb(); cia_swiz_set_hae_mem(&pa); return SPARSE_READ_WORD(KV(CIA_PCI_SMEM1), pa); } static u_int32_t cia_swiz_readl(u_int32_t pa) { alpha_mb(); cia_swiz_set_hae_mem(&pa); return SPARSE_READ_LONG(KV(CIA_PCI_SMEM1), pa); } static void cia_swiz_writeb(u_int32_t pa, u_int8_t data) { cia_swiz_set_hae_mem(&pa); SPARSE_WRITE_BYTE(KV(CIA_PCI_SMEM1), pa, data); alpha_wmb(); } static void cia_swiz_writew(u_int32_t pa, u_int16_t data) { cia_swiz_set_hae_mem(&pa); SPARSE_WRITE_WORD(KV(CIA_PCI_SMEM1), pa, data); alpha_wmb(); } static void cia_swiz_writel(u_int32_t pa, u_int32_t data) { cia_swiz_set_hae_mem(&pa); SPARSE_WRITE_LONG(KV(CIA_PCI_SMEM1), pa, data); alpha_wmb(); } static int cia_swiz_maxdevs(u_int b) { return 12; /* XXX */ } #define CIA_SWIZ_CFGOFF(b, s, f, r) \ (((b) << 16) | ((s) << 11) | ((f) << 8) | (r)) /* when doing a type 1 pci configuration space access, we * must set a bit in the CIA_CSR_CFG register & clear it * when we're done */ #define CIA_TYPE1_SETUP(b,s,old_cfg) if((b)) { \ do { \ (s) = splhigh(); \ (old_cfg) = REGVAL(CIA_CSR_CFG); \ alpha_mb(); \ REGVAL(CIA_CSR_CFG) = (old_cfg) | 0x1; \ alpha_mb(); \ } while(0); \ } #define CIA_TYPE1_TEARDOWN(b,s,old_cfg) if((b)) { \ do { \ alpha_mb(); \ REGVAL(CIA_CSR_CFG) = (old_cfg); \ alpha_mb(); \ splx((s)); \ } while(0); \ } /* * From NetBSD: * Some (apparently-common) revisions of EB164 and AlphaStation * firmware do the Wrong thing with PCI master and target aborts, * which are caused by accesing the configuration space of devices * that don't exist (for example). * * To work around this, we clear the CIA error register's PCI * master and target abort bits before touching PCI configuration * space and check it afterwards. If it indicates a master or target * abort, the device wasn't there so we return ~0 */ #define SWIZ_CFGREAD(b, s, f, r, width, type) \ type val = ~0; \ int ipl = 0; \ u_int32_t old_cfg = 0, errbits; \ vm_offset_t off = CIA_SWIZ_CFGOFF(b, s, f, r); \ vm_offset_t kv = SPARSE_##width##_ADDRESS(KV(CIA_PCI_CONF), off); \ REGVAL(CIA_CSR_CIA_ERR) = CIA_ERR_RCVD_MAS_ABT|CIA_ERR_RCVD_TAR_ABT;\ alpha_mb(); \ CIA_TYPE1_SETUP(b,ipl,old_cfg); \ if (!badaddr((caddr_t)kv, sizeof(type))) { \ val = SPARSE_##width##_EXTRACT(off, SPARSE_READ(kv)); \ } \ CIA_TYPE1_TEARDOWN(b,ipl,old_cfg); \ errbits = REGVAL(CIA_CSR_CIA_ERR); \ if (errbits & (CIA_ERR_RCVD_MAS_ABT|CIA_ERR_RCVD_TAR_ABT)) \ val = ~0; \ if (errbits) { \ REGVAL(CIA_CSR_CIA_ERR) = errbits; \ alpha_mb(); \ alpha_pal_draina(); \ } \ return val; #define SWIZ_CFGWRITE(b, s, f, r, data, width, type) \ int ipl = 0; \ u_int32_t old_cfg = 0; \ vm_offset_t off = CIA_SWIZ_CFGOFF(b, s, f, r); \ vm_offset_t kv = SPARSE_##width##_ADDRESS(KV(CIA_PCI_CONF), off); \ alpha_mb(); \ CIA_TYPE1_SETUP(b,ipl,old_cfg); \ if (!badaddr((caddr_t)kv, sizeof(type))) { \ SPARSE_WRITE(kv, SPARSE_##width##_INSERT(off, data)); \ alpha_wmb(); \ } \ CIA_TYPE1_TEARDOWN(b,ipl,old_cfg); \ return; static u_int8_t cia_swiz_cfgreadb(u_int h, u_int b, u_int s, u_int f, u_int r) { SWIZ_CFGREAD(b, s, f, r, BYTE, u_int8_t); } static u_int16_t cia_swiz_cfgreadw(u_int h, u_int b, u_int s, u_int f, u_int r) { SWIZ_CFGREAD(b, s, f, r, WORD, u_int16_t); } static u_int32_t cia_swiz_cfgreadl(u_int h, u_int b, u_int s, u_int f, u_int r) { SWIZ_CFGREAD(b, s, f, r, LONG, u_int32_t); } static void cia_swiz_cfgwriteb(u_int h, u_int b, u_int s, u_int f, u_int r, u_int8_t data) { SWIZ_CFGWRITE(b, s, f, r, data, BYTE, u_int8_t); } static void cia_swiz_cfgwritew(u_int h, u_int b, u_int s, u_int f, u_int r, u_int16_t data) { SWIZ_CFGWRITE(b, s, f, r, data, WORD, u_int16_t); } static void cia_swiz_cfgwritel(u_int h, u_int b, u_int s, u_int f, u_int r, u_int32_t data) { SWIZ_CFGWRITE(b, s, f, r, data, LONG, u_int32_t); } vm_offset_t cia_cvt_dense(vm_offset_t addr) { addr &= 0xffffffffUL; return (addr | CIA_PCI_DENSE); } vm_offset_t cia_cvt_bwx(vm_offset_t addr) { addr &= 0xffffffffUL; return (addr |= CIA_EV56_BWMEM); } static u_int64_t cia_read_hae(void) { return cia_hae_mem & REG1; } static void cia_write_hae(u_int64_t hae) { u_int32_t pa = hae; cia_swiz_set_hae_mem(&pa); } static int cia_probe(device_t dev); static int cia_attach(device_t dev); static int cia_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep); static int cia_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie); static device_method_t cia_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cia_probe), DEVMETHOD(device_attach, cia_attach), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_alloc_resource, pci_alloc_resource), DEVMETHOD(bus_release_resource, pci_release_resource), DEVMETHOD(bus_activate_resource, pci_activate_resource), DEVMETHOD(bus_deactivate_resource, pci_deactivate_resource), DEVMETHOD(bus_setup_intr, cia_setup_intr), DEVMETHOD(bus_teardown_intr, cia_teardown_intr), { 0, 0 } }; static driver_t cia_driver = { "cia", cia_methods, sizeof(struct cia_softc), }; #define CIA_SGMAP_BASE (8*1024*1024) #define CIA_SGMAP_SIZE (8*1024*1024) #define CIA_PYXIS_BUG_BASE (128*1024*1024) #define CIA_PYXIS_BUG_SIZE (2*1024*1024) static void cia_sgmap_invalidate(void) { REGVAL(CIA_PCI_TBIA) = CIA_PCI_TBIA_ALL; alpha_mb(); } static void cia_sgmap_invalidate_pyxis(void) { volatile u_int64_t dummy; u_int32_t ctrl; int i, s; s = splhigh(); /* * Put the Pyxis into PCI loopback mode. */ alpha_mb(); ctrl = REGVAL(CIA_CSR_CTRL); REGVAL(CIA_CSR_CTRL) = ctrl | CTRL_PCI_LOOP_EN; alpha_mb(); /* * Now, read from PCI dense memory space at offset 128M (our * target window base), skipping 64k on each read. This forces * S/G TLB misses. * * XXX Looks like the TLB entries are `not quite LRU'. We need * XXX to read more times than there are actual tags! */ for (i = 0; i < CIA_TLB_NTAGS + 4; i++) { dummy = *((volatile u_int64_t *) ALPHA_PHYS_TO_K0SEG(CIA_PCI_DENSE + CIA_PYXIS_BUG_BASE + (i * 65536))); } /* * Restore normal PCI operation. */ alpha_mb(); REGVAL(CIA_CSR_CTRL) = ctrl; alpha_mb(); splx(s); } static void cia_sgmap_map(void *arg, vm_offset_t ba, vm_offset_t pa) { u_int64_t *sgtable = arg; int index = alpha_btop(ba - CIA_SGMAP_BASE); if (pa) { if (pa > (1L<<32)) panic("cia_sgmap_map: can't map address 0x%lx", pa); sgtable[index] = ((pa >> 13) << 1) | 1; } else { sgtable[index] = 0; } alpha_mb(); if (cia_ispyxis) cia_sgmap_invalidate_pyxis(); else cia_sgmap_invalidate(); } static void cia_init_sgmap(void) { void *sgtable; /* * First setup Window 0 to map 8Mb to 16Mb with an * sgmap. Allocate the map aligned to a 32k boundary. */ REGVAL(CIA_PCI_W0BASE) = (CIA_SGMAP_BASE | CIA_PCI_WnBASE_SG_EN | CIA_PCI_WnBASE_W_EN); alpha_mb(); REGVAL(CIA_PCI_W0MASK) = CIA_PCI_WnMASK_8M; alpha_mb(); sgtable = contigmalloc(8192, M_DEVBUF, M_NOWAIT, 0, (1L<<34), 32*1024, (1L<<34)); if (!sgtable) panic("cia_init_sgmap: can't allocate page table"); REGVAL(CIA_PCI_T0BASE) = (pmap_kextract((vm_offset_t) sgtable) >> CIA_PCI_TnBASE_SHIFT); chipset.sgmap = sgmap_map_create(CIA_SGMAP_BASE, CIA_SGMAP_BASE + CIA_SGMAP_SIZE - 1, cia_sgmap_map, sgtable); if (cia_ispyxis) { /* * Pyxis has broken TLB invalidate. We use the NetBSD * workaround of using another region to spill entries * out of the TLB. The 'bug' region is 2Mb mapped at * 128Mb. */ int i; vm_offset_t pa; u_int64_t *bugtable; REGVAL(CIA_PCI_W2BASE) = CIA_PYXIS_BUG_BASE | CIA_PCI_WnBASE_SG_EN | CIA_PCI_WnBASE_W_EN; alpha_mb(); REGVAL(CIA_PCI_W2MASK) = CIA_PCI_WnMASK_2M; alpha_mb(); bugtable = contigmalloc(8192, M_DEVBUF, M_NOWAIT, 0, (1L<<34), 2*1024, (1L<<34)); if (!bugtable) panic("cia_init_sgmap: can't allocate page table"); REGVAL(CIA_PCI_T2BASE) = (pmap_kextract((vm_offset_t) bugtable) >> CIA_PCI_TnBASE_SHIFT); pa = sgmap_overflow_page(); for (i = 0; i < alpha_btop(CIA_PYXIS_BUG_SIZE); i++) bugtable[i] = ((pa >> 13) << 1) | 1; } } void cia_init() { static int initted = 0; if (initted) return; initted = 1; cia_rev = REGVAL(CIA_CSR_REV) & REV_MASK; /* * Determine if we have a Pyxis. Only two systypes can * have this: the EB164 systype (AlphaPC164LX and AlphaPC164SX) * and the DEC_ST550 systype (Miata). */ if ((hwrpb->rpb_type == ST_EB164 && (hwrpb->rpb_variation & SV_ST_MASK) >= SV_ST_ALPHAPC164LX_400) || hwrpb->rpb_type == ST_DEC_550) cia_ispyxis = TRUE; else cia_ispyxis = FALSE; /* * ALCOR/ALCOR2 Revisions >= 2 and Pyxis have the CNFG register. */ if (cia_rev >= 2 || cia_ispyxis) cia_config = REGVAL(CIA_CSR_CNFG); else cia_config = 0; if (alpha_implver() != ALPHA_IMPLVER_EV5 || alpha_amask(ALPHA_AMASK_BWX) || !(cia_config & CNFG_BWEN)) chipset = cia_swiz_chipset; else chipset = cia_bwx_chipset; cia_hae_mem = REGVAL(CIA_CSR_HAE_MEM); #if 0 chipset = cia_swiz_chipset; /* XXX */ cia_ispyxis = 0; #endif if (platform.pci_intr_init) platform.pci_intr_init(); } static int cia_probe(device_t dev) { if (cia0) return ENXIO; cia0 = dev; device_set_desc(dev, "2117x Core Logic chipset"); /* XXX */ pci_init_resources(); isa_init_intr(); cia_init_sgmap(); - device_add_child(dev, "pcib", 0, 0); + device_add_child(dev, "pcib", 0); return 0; } static int cia_attach(device_t dev) { char* name; int pass; cia_init(); name = cia_ispyxis ? "Pyxis" : "ALCOR/ALCOR2"; if (cia_ispyxis) { name = "Pyxis"; pass = cia_rev; } else { name = "ALCOR/ALCOR2"; pass = cia_rev+1; } printf("cia0: %s, pass %d\n", name, pass); if (cia_config) printf("cia0: extended capabilities: %b\n", cia_config, CIA_CSR_CNFG_BITS); #ifdef DEC_ST550 if (hwrpb->rpb_type == ST_DEC_550 && (hwrpb->rpb_variation & SV_ST_MASK) < SV_ST_MIATA_1_5) { /* * Miata 1 systems have a bug: DMA cannot cross * an 8k boundary! Make sure PCI read prefetching * is disabled on these chips. Note that secondary * PCI busses don't have this problem, because of * the way PPBs handle PCI read requests. * * In the 21174 Technical Reference Manual, this is * actually documented as "Pyxis Pass 1", but apparently * there are chips that report themselves as "Pass 1" * which do not have the bug! Miatas with the Cypress * PCI-ISA bridge (i.e. Miata 1.5 and Miata 2) do not * have the bug, so we use this check. * * XXX We also need to deal with this boundary constraint * XXX in the PCI bus 0 (and ISA) DMA tags, but some * XXX drivers are going to need to be changed first. */ u_int32_t ctrl; /* XXX no bets... */ printf("cia0: WARNING: Pyxis pass 1 DMA bug; no bets...\n"); alpha_mb(); ctrl = REGVAL(CIA_CSR_CTRL); ctrl &= ~(CTRL_RD_TYPE|CTRL_RL_TYPE|CTRL_RM_TYPE); REGVAL(CIA_CSR_CTRL) = ctrl; alpha_mb(); } #endif if (!platform.iointr) /* XXX */ set_iointr(alpha_dispatch_intr); if (cia_ispyxis) { snprintf(chipset_type, sizeof(chipset_type), "pyxis"); chipset_bwx = 1; chipset_ports = CIA_EV56_BWIO; chipset_memory = CIA_EV56_BWMEM; chipset_dense = CIA_PCI_DENSE; } else { snprintf(chipset_type, sizeof(chipset_type), "cia"); chipset_bwx = 0; chipset_ports = CIA_PCI_SIO1; chipset_memory = CIA_PCI_SMEM1; chipset_dense = CIA_PCI_DENSE; chipset_hae_mask = 7L << 29; } bus_generic_attach(dev); return 0; } static int cia_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep) { int error; error = rman_activate_resource(irq); if (error) return error; error = alpha_setup_intr(0x900 + (irq->r_start << 4), intr, arg, cookiep, &intrcnt[INTRCNT_EB164_IRQ + irq->r_start]); if (error) return error; /* Enable PCI interrupt */ platform.pci_intr_enable(irq->r_start); device_printf(child, "interrupting at CIA irq %d\n", (int) irq->r_start); return 0; } static int cia_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie) { alpha_teardown_intr(cookie); return rman_deactivate_resource(irq); } DRIVER_MODULE(cia, root, cia_driver, cia_devclass, 0, 0); Index: head/sys/alpha/pci/cia_pci.c =================================================================== --- head/sys/alpha/pci/cia_pci.c (revision 54072) +++ head/sys/alpha/pci/cia_pci.c (revision 54073) @@ -1,84 +1,84 @@ /*- * Copyright (c) 1998 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include static devclass_t pcib_devclass; static int cia_pcib_probe(device_t dev) { device_set_desc(dev, "2117x PCI host bus adapter"); - device_add_child(dev, "pci", 0, 0); + device_add_child(dev, "pci", 0); return 0; } static int cia_pcib_read_ivar(device_t dev, device_t child, int which, u_long *result) { if (which == PCIB_IVAR_HOSE) { *result = 0; return 0; } return ENOENT; } static device_method_t cia_pcib_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cia_pcib_probe), DEVMETHOD(device_attach, bus_generic_attach), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, cia_pcib_read_ivar), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t cia_pcib_driver = { "pcib", cia_pcib_methods, 1, }; DRIVER_MODULE(pcib, cia, cia_pcib_driver, pcib_devclass, 0, 0); Index: head/sys/alpha/pci/lca.c =================================================================== --- head/sys/alpha/pci/lca.c (revision 54072) +++ head/sys/alpha/pci/lca.c (revision 54073) @@ -1,508 +1,508 @@ /*- * Copyright (c) 1998 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define KV(pa) ALPHA_PHYS_TO_K0SEG(pa) static devclass_t lca_devclass; static device_t lca0; /* XXX only one for now */ struct lca_softc { int junk; }; #define LCA_SOFTC(dev) (struct lca_softc*) device_get_softc(dev) static alpha_chipset_inb_t lca_inb; static alpha_chipset_inw_t lca_inw; static alpha_chipset_inl_t lca_inl; static alpha_chipset_outb_t lca_outb; static alpha_chipset_outw_t lca_outw; static alpha_chipset_outl_t lca_outl; static alpha_chipset_readb_t lca_readb; static alpha_chipset_readw_t lca_readw; static alpha_chipset_readl_t lca_readl; static alpha_chipset_writeb_t lca_writeb; static alpha_chipset_writew_t lca_writew; static alpha_chipset_writel_t lca_writel; static alpha_chipset_maxdevs_t lca_maxdevs; static alpha_chipset_cfgreadb_t lca_cfgreadb; static alpha_chipset_cfgreadw_t lca_cfgreadw; static alpha_chipset_cfgreadl_t lca_cfgreadl; static alpha_chipset_cfgwriteb_t lca_cfgwriteb; static alpha_chipset_cfgwritew_t lca_cfgwritew; static alpha_chipset_cfgwritel_t lca_cfgwritel; static alpha_chipset_addrcvt_t lca_cvt_dense; static alpha_chipset_read_hae_t lca_read_hae; static alpha_chipset_write_hae_t lca_write_hae; static alpha_chipset_t lca_chipset = { lca_inb, lca_inw, lca_inl, lca_outb, lca_outw, lca_outl, lca_readb, lca_readw, lca_readl, lca_writeb, lca_writew, lca_writel, lca_maxdevs, lca_cfgreadb, lca_cfgreadw, lca_cfgreadl, lca_cfgwriteb, lca_cfgwritew, lca_cfgwritel, lca_cvt_dense, NULL, lca_read_hae, lca_write_hae, }; static u_int8_t lca_inb(u_int32_t port) { alpha_mb(); return SPARSE_READ_BYTE(KV(LCA_PCI_SIO), port); } static u_int16_t lca_inw(u_int32_t port) { alpha_mb(); return SPARSE_READ_WORD(KV(LCA_PCI_SIO), port); } static u_int32_t lca_inl(u_int32_t port) { alpha_mb(); return SPARSE_READ_LONG(KV(LCA_PCI_SIO), port); } static void lca_outb(u_int32_t port, u_int8_t data) { SPARSE_WRITE_BYTE(KV(LCA_PCI_SIO), port, data); alpha_wmb(); } static void lca_outw(u_int32_t port, u_int16_t data) { SPARSE_WRITE_WORD(KV(LCA_PCI_SIO), port, data); alpha_wmb(); } static void lca_outl(u_int32_t port, u_int32_t data) { SPARSE_WRITE_LONG(KV(LCA_PCI_SIO), port, data); alpha_wmb(); } /* * The LCA HAE is write-only. According to NetBSD, this is where it starts. */ static u_int32_t lca_hae_mem = 0x80000000; /* * The first 16Mb ignores the HAE. The next 112Mb uses the HAE to set * the high bits of the PCI address. */ #define REG1 (1UL << 24) static __inline void lca_set_hae_mem(u_int32_t *pa) { int s; u_int32_t msb; if(*pa >= REG1){ msb = *pa & 0xf8000000; *pa -= msb; s = splhigh(); if (msb != lca_hae_mem) { lca_hae_mem = msb; REGVAL(LCA_IOC_HAE) = lca_hae_mem; alpha_mb(); alpha_mb(); } splx(s); } } static u_int8_t lca_readb(u_int32_t pa) { alpha_mb(); lca_set_hae_mem(&pa); return SPARSE_READ_BYTE(KV(LCA_PCI_SPARSE), pa); } static u_int16_t lca_readw(u_int32_t pa) { alpha_mb(); lca_set_hae_mem(&pa); return SPARSE_READ_WORD(KV(LCA_PCI_SPARSE), pa); } static u_int32_t lca_readl(u_int32_t pa) { alpha_mb(); lca_set_hae_mem(&pa); return SPARSE_READ_LONG(KV(LCA_PCI_SPARSE), pa); } static void lca_writeb(u_int32_t pa, u_int8_t data) { lca_set_hae_mem(&pa); SPARSE_WRITE_BYTE(KV(LCA_PCI_SPARSE), pa, data); alpha_wmb(); } static void lca_writew(u_int32_t pa, u_int16_t data) { lca_set_hae_mem(&pa); SPARSE_WRITE_WORD(KV(LCA_PCI_SPARSE), pa, data); alpha_wmb(); } static void lca_writel(u_int32_t pa, u_int32_t data) { lca_set_hae_mem(&pa); SPARSE_WRITE_LONG(KV(LCA_PCI_SPARSE), pa, data); alpha_wmb(); } static int lca_maxdevs(u_int b) { return 12; /* XXX */ } #define LCA_CFGOFF(b, s, f, r) \ ((b) ? (((b) << 16) | ((s) << 11) | ((f) << 8) | (r)) \ : ((1 << ((s) + 11)) | ((f) << 8) | (r))) #define LCA_TYPE1_SETUP(b,s) if ((b)) { \ do { \ (s) = splhigh(); \ alpha_mb(); \ REGVAL(LCA_IOC_CONF) = 1; \ alpha_mb(); \ } while(0); \ } #define LCA_TYPE1_TEARDOWN(b,s) if ((b)) { \ do { \ alpha_mb(); \ REGVAL(LCA_IOC_CONF) = 0; \ alpha_mb(); \ splx((s)); \ } while(0); \ } #define CFGREAD(b, s, f, r, width, type) \ type val = ~0; \ int ipl = 0; \ vm_offset_t off = LCA_CFGOFF(b, s, f, r); \ vm_offset_t kv = SPARSE_##width##_ADDRESS(KV(LCA_PCI_CONF), off); \ alpha_mb(); \ LCA_TYPE1_SETUP(b,ipl); \ if (!badaddr((caddr_t)kv, sizeof(type))) { \ val = SPARSE_##width##_EXTRACT(off, SPARSE_READ(kv)); \ } \ LCA_TYPE1_TEARDOWN(b,ipl); \ return val #define CFGWRITE(b, s, f, r, data, width, type) \ int ipl = 0; \ vm_offset_t off = LCA_CFGOFF(b, s, f, r); \ vm_offset_t kv = SPARSE_##width##_ADDRESS(KV(LCA_PCI_CONF), off); \ alpha_mb(); \ LCA_TYPE1_SETUP(b,ipl); \ if (!badaddr((caddr_t)kv, sizeof(type))) { \ SPARSE_WRITE(kv, SPARSE_##width##_INSERT(off, data)); \ alpha_wmb(); \ } \ LCA_TYPE1_TEARDOWN(b,ipl); \ return static u_int8_t lca_cfgreadb(u_int h, u_int b, u_int s, u_int f, u_int r) { CFGREAD(b, s, f, r, BYTE, u_int8_t); } static u_int16_t lca_cfgreadw(u_int h, u_int b, u_int s, u_int f, u_int r) { CFGREAD(b, s, f, r, WORD, u_int16_t); } static u_int32_t lca_cfgreadl(u_int h, u_int b, u_int s, u_int f, u_int r) { CFGREAD(b, s, f, r, LONG, u_int32_t); } static void lca_cfgwriteb(u_int h, u_int b, u_int s, u_int f, u_int r, u_int8_t data) { CFGWRITE(b, s, f, r, data, BYTE, u_int8_t); } static void lca_cfgwritew(u_int h, u_int b, u_int s, u_int f, u_int r, u_int16_t data) { CFGWRITE(b, s, f, r, data, WORD, u_int16_t); } static void lca_cfgwritel(u_int h, u_int b, u_int s, u_int f, u_int r, u_int32_t data) { CFGWRITE(b, s, f, r, data, LONG, u_int16_t); } static vm_offset_t lca_cvt_dense(vm_offset_t addr) { addr &= 0xffffffffUL; return (addr | LCA_PCI_DENSE); } static u_int64_t lca_read_hae(void) { return lca_hae_mem & 0xf8000000; } static void lca_write_hae(u_int64_t hae) { u_int32_t pa = hae; lca_set_hae_mem(&pa); } static int lca_probe(device_t dev); static int lca_attach(device_t dev); static struct resource *lca_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); static int lca_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r); static device_method_t lca_methods[] = { /* Device interface */ DEVMETHOD(device_probe, lca_probe), DEVMETHOD(device_attach, lca_attach), /* Bus interface */ DEVMETHOD(bus_alloc_resource, lca_alloc_resource), DEVMETHOD(bus_release_resource, lca_release_resource), DEVMETHOD(bus_activate_resource, pci_activate_resource), DEVMETHOD(bus_deactivate_resource, pci_deactivate_resource), DEVMETHOD(bus_setup_intr, isa_setup_intr), DEVMETHOD(bus_teardown_intr, isa_teardown_intr), { 0, 0 } }; static driver_t lca_driver = { "lca", lca_methods, sizeof(struct lca_softc), }; #define LCA_SGMAP_BASE (8*1024*1024) #define LCA_SGMAP_SIZE (8*1024*1024) static void lca_sgmap_invalidate(void) { alpha_mb(); REGVAL(LCA_IOC_TBIA) = 0; alpha_mb(); } static void lca_sgmap_map(void *arg, vm_offset_t ba, vm_offset_t pa) { u_int64_t *sgtable = arg; int index = alpha_btop(ba - LCA_SGMAP_BASE); if (pa) { if (pa > (1L<<32)) panic("lca_sgmap_map: can't map address 0x%lx", pa); sgtable[index] = ((pa >> 13) << 1) | 1; } else { sgtable[index] = 0; } alpha_mb(); lca_sgmap_invalidate(); } static void lca_init_sgmap(void) { void *sgtable; /* * First setup Window 0 to map 8Mb to 16Mb with an * sgmap. Allocate the map aligned to a 32 boundary. */ REGVAL64(LCA_IOC_W_BASE0) = LCA_SGMAP_BASE | IOC_W_BASE_SG | IOC_W_BASE_WEN; alpha_mb(); REGVAL64(LCA_IOC_W_MASK0) = IOC_W_MASK_8M; alpha_mb(); sgtable = contigmalloc(8192, M_DEVBUF, M_NOWAIT, 0, (1L<<34), 32*1024, (1L<<34)); if (!sgtable) panic("lca_init_sgmap: can't allocate page table"); chipset.sgmap = sgmap_map_create(LCA_SGMAP_BASE, LCA_SGMAP_BASE + LCA_SGMAP_SIZE, lca_sgmap_map, sgtable); REGVAL64(LCA_IOC_W_T_BASE0) = pmap_kextract((vm_offset_t) sgtable); alpha_mb(); REGVAL64(LCA_IOC_TB_ENA) = IOC_TB_ENA_TEN; alpha_mb(); lca_sgmap_invalidate(); } void lca_init() { static int initted = 0; if (initted) return; initted = 1; /* Type 0 PCI conf access. */ REGVAL64(LCA_IOC_CONF) = 0; if (platform.pci_intr_init) platform.pci_intr_init(); chipset = lca_chipset; } static int lca_probe(device_t dev) { if (lca0) return ENXIO; lca0 = dev; device_set_desc(dev, "21066 Core Logic chipset"); /* XXX */ pci_init_resources(); isa_init_intr(); lca_init_sgmap(); - device_add_child(dev, "pcib", 0, 0); + device_add_child(dev, "pcib", 0); return 0; } static int lca_attach(device_t dev) { lca_init(); set_iointr(alpha_dispatch_intr); snprintf(chipset_type, sizeof(chipset_type), "lca"); chipset_bwx = 0; chipset_ports = LCA_PCI_SIO; chipset_memory = LCA_PCI_SPARSE; chipset_dense = LCA_PCI_DENSE; chipset_hae_mask = IOC_HAE_ADDREXT; bus_generic_attach(dev); return 0; } static struct resource * lca_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { if (type == SYS_RES_IRQ) return isa_alloc_intr(bus, child, start); else return pci_alloc_resource(bus, child, type, rid, start, end, count, flags); } static int lca_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { if (type == SYS_RES_IRQ) return isa_release_intr(bus, child, r); else return pci_release_resource(bus, child, type, rid, r); } DRIVER_MODULE(lca, root, lca_driver, lca_devclass, 0, 0); Index: head/sys/alpha/pci/lca_pci.c =================================================================== --- head/sys/alpha/pci/lca_pci.c (revision 54072) +++ head/sys/alpha/pci/lca_pci.c (revision 54073) @@ -1,84 +1,84 @@ /*- * Copyright (c) 1998 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPELCAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include static devclass_t pcib_devclass; static int lca_pcib_probe(device_t dev) { device_set_desc(dev, "21066 PCI host bus adapter"); - device_add_child(dev, "pci", 0, 0); + device_add_child(dev, "pci", 0); return 0; } static int lca_pcib_read_ivar(device_t dev, device_t child, int which, u_long *result) { if (which == PCIB_IVAR_HOSE) { *result = 0; return 0; } return ENOENT; } static device_method_t lca_pcib_methods[] = { /* Device interface */ DEVMETHOD(device_probe, lca_pcib_probe), DEVMETHOD(device_attach, bus_generic_attach), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, lca_pcib_read_ivar), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t lca_pcib_driver = { "pcib", lca_pcib_methods, 1, }; DRIVER_MODULE(pcib, lca, lca_pcib_driver, pcib_devclass, 0, 0); Index: head/sys/alpha/pci/tsunami.c =================================================================== --- head/sys/alpha/pci/tsunami.c (revision 54072) +++ head/sys/alpha/pci/tsunami.c (revision 54073) @@ -1,667 +1,669 @@ /*- * Copyright (c) 1999 Andrew Gallatin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "opt_cpu.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define KV(pa) ALPHA_PHYS_TO_K0SEG(pa) static devclass_t tsunami_devclass; static device_t tsunami0; /* XXX only one for now */ struct tsunami_softc { int junk; /* no softc */ }; static int num_pchips = 0; static volatile tsunami_pchip *pchip[2] = {pchip0, pchip1}; #define TSUNAMI_SOFTC(dev) (struct tsunami_softc*) device_get_softc(dev) static alpha_chipset_inb_t tsunami_inb; static alpha_chipset_inw_t tsunami_inw; static alpha_chipset_inl_t tsunami_inl; static alpha_chipset_outb_t tsunami_outb; static alpha_chipset_outw_t tsunami_outw; static alpha_chipset_outl_t tsunami_outl; static alpha_chipset_readb_t tsunami_readb; static alpha_chipset_readw_t tsunami_readw; static alpha_chipset_readl_t tsunami_readl; static alpha_chipset_writeb_t tsunami_writeb; static alpha_chipset_writew_t tsunami_writew; static alpha_chipset_writel_t tsunami_writel; static alpha_chipset_maxdevs_t tsunami_maxdevs; static alpha_chipset_cfgreadb_t tsunami_cfgreadb; static alpha_chipset_cfgreadw_t tsunami_cfgreadw; static alpha_chipset_cfgreadl_t tsunami_cfgreadl; static alpha_chipset_cfgwriteb_t tsunami_cfgwriteb; static alpha_chipset_cfgwritew_t tsunami_cfgwritew; static alpha_chipset_cfgwritel_t tsunami_cfgwritel; static alpha_chipset_addrcvt_t tsunami_cvt_dense, tsunami_cvt_bwx; static alpha_chipset_read_hae_t tsunami_read_hae; static alpha_chipset_write_hae_t tsunami_write_hae; static alpha_chipset_t tsunami_chipset = { tsunami_inb, tsunami_inw, tsunami_inl, tsunami_outb, tsunami_outw, tsunami_outl, tsunami_readb, tsunami_readw, tsunami_readl, tsunami_writeb, tsunami_writew, tsunami_writel, tsunami_maxdevs, tsunami_cfgreadb, tsunami_cfgreadw, tsunami_cfgreadl, tsunami_cfgwriteb, tsunami_cfgwritew, tsunami_cfgwritel, tsunami_cvt_dense, tsunami_cvt_bwx, tsunami_read_hae, tsunami_write_hae, }; /* * This setup will only allow for one additional hose */ #define ADDR_TO_HOSE(x) ((x) >> 31) #define STRIP_HOSE(x) ((x) & 0x7fffffff) static void tsunami_intr_enable __P((int)); static void tsunami_intr_disable __P((int)); static u_int8_t tsunami_inb(u_int32_t port) { int hose = ADDR_TO_HOSE(port); port = STRIP_HOSE(port); alpha_mb(); return ldbu(KV(TSUNAMI_IO(hose) + port)); } static u_int16_t tsunami_inw(u_int32_t port) { int hose = ADDR_TO_HOSE(port); port = STRIP_HOSE(port); alpha_mb(); return ldwu(KV(TSUNAMI_IO(hose) + port)); } static u_int32_t tsunami_inl(u_int32_t port) { int hose = ADDR_TO_HOSE(port); port = STRIP_HOSE(port); alpha_mb(); return ldl(KV(TSUNAMI_IO(hose) + port)); } static void tsunami_outb(u_int32_t port, u_int8_t data) { int hose = ADDR_TO_HOSE(port); port = STRIP_HOSE(port); stb(KV(TSUNAMI_IO(hose) + port), data); alpha_mb(); } static void tsunami_outw(u_int32_t port, u_int16_t data) { int hose = ADDR_TO_HOSE(port); port = STRIP_HOSE(port); stw(KV(TSUNAMI_IO(hose) + port), data); alpha_mb(); } static void tsunami_outl(u_int32_t port, u_int32_t data) { int hose = ADDR_TO_HOSE(port); port = STRIP_HOSE(port); stl(KV(TSUNAMI_IO(hose) + port), data); alpha_mb(); } static u_int8_t tsunami_readb(u_int32_t pa) { int hose = ADDR_TO_HOSE(pa); pa = STRIP_HOSE(pa); alpha_mb(); return ldbu(KV(TSUNAMI_MEM(hose) + pa)); } static u_int16_t tsunami_readw(u_int32_t pa) { int hose = ADDR_TO_HOSE(pa); pa = STRIP_HOSE(pa); alpha_mb(); return ldwu(KV(TSUNAMI_MEM(hose) + pa)); } static u_int32_t tsunami_readl(u_int32_t pa) { int hose = ADDR_TO_HOSE(pa); pa = STRIP_HOSE(pa); alpha_mb(); return ldl(KV(TSUNAMI_MEM(hose) + pa)); } static void tsunami_writeb(u_int32_t pa, u_int8_t data) { int hose = ADDR_TO_HOSE(pa); pa = STRIP_HOSE(pa); stb(KV(TSUNAMI_MEM(hose) + pa), data); alpha_mb(); } static void tsunami_writew(u_int32_t pa, u_int16_t data) { int hose = ADDR_TO_HOSE(pa); pa = STRIP_HOSE(pa); stw(KV(TSUNAMI_MEM(hose) + pa), data); alpha_mb(); } static void tsunami_writel(u_int32_t pa, u_int32_t data) { int hose = ADDR_TO_HOSE(pa); pa = STRIP_HOSE(pa); stl(KV(TSUNAMI_MEM(hose) + pa), data); alpha_mb(); } static int tsunami_maxdevs(u_int b) { return 12; /* XXX */ } static void tsunami_clear_abort(void) { alpha_mb(); alpha_pal_draina(); } static int tsunami_check_abort(void) { /* u_int32_t errbits;*/ int ba = 0; alpha_pal_draina(); alpha_mb(); #if 0 errbits = REGVAL(TSUNAMI_CSR_TSUNAMI_ERR); if (errbits & (TSUNAMI_ERR_RCVD_MAS_ABT|TSUNAMI_ERR_RCVD_TAR_ABT)) ba = 1; if (errbits) { REGVAL(TSUNAMI_CSR_TSUNAMI_ERR) = errbits; alpha_mb(); alpha_pal_draina(); } #endif return ba; } #define TSUNAMI_CFGADDR(b, s, f, r, h) \ KV(TSUNAMI_CONF(h) | ((b) << 16) | ((s) << 11) | ((f) << 8) | (r)) #define CFGREAD(h, b, s, f, r, op, width, type) \ int bus; \ vm_offset_t va; \ type data; \ if (h == (u_int8_t)-1) \ h = tsunami_hose_from_bus(b); \ bus = tsunami_bus_within_hose(h, b) ? b : 0; \ va = TSUNAMI_CFGADDR(bus, s, f, r, h); \ tsunami_clear_abort(); \ if (badaddr((caddr_t)va, width)) { \ tsunami_check_abort(); \ return ~0; \ } \ data = ##op##(va); \ if (tsunami_check_abort()) \ return ~0; \ return data; #define CFWRITE(h, b, s, f, r, data, op, width) \ int bus; \ vm_offset_t va; \ if (h == (u_int8_t)-1) \ h = tsunami_hose_from_bus(b); \ bus = tsunami_bus_within_hose(h, b) ? b : 0; \ va = TSUNAMI_CFGADDR(bus, s, f, r, h); \ tsunami_clear_abort(); \ if (badaddr((caddr_t)va, width)) \ return; \ ##op##(va, data); \ tsunami_check_abort(); static u_int8_t tsunami_cfgreadb(u_int h, u_int b, u_int s, u_int f, u_int r) { CFGREAD(h, b, s, f, r, ldbu, 1, u_int8_t) } static u_int16_t tsunami_cfgreadw(u_int h, u_int b, u_int s, u_int f, u_int r) { CFGREAD(h, b, s, f, r, ldwu, 2, u_int16_t) } static u_int32_t tsunami_cfgreadl(u_int h, u_int b, u_int s, u_int f, u_int r) { CFGREAD(h, b, s, f, r, ldl, 4, u_int32_t) } static void tsunami_cfgwriteb(u_int h, u_int b, u_int s, u_int f, u_int r, u_int8_t data) { CFWRITE(h, b, s, f, r, data, stb, 1) } static void tsunami_cfgwritew(u_int h, u_int b, u_int s, u_int f, u_int r, u_int16_t data) { CFWRITE(h, b, s, f, r, data, stw, 2) } static void tsunami_cfgwritel(u_int h, u_int b, u_int s, u_int f, u_int r, u_int32_t data) { CFWRITE(h, b, s, f, r, data, stl, 4) } vm_offset_t tsunami_cvt_bwx(vm_offset_t addr) { int hose; vm_offset_t laddr; laddr = addr & 0xffffffffUL; hose = ADDR_TO_HOSE(laddr); laddr = STRIP_HOSE(addr); laddr |= TSUNAMI_MEM(hose); return (KV(laddr)); } vm_offset_t tsunami_cvt_dense(vm_offset_t addr) { return tsunami_cvt_bwx(addr); } /* * There doesn't appear to be an hae on this platform */ static u_int64_t tsunami_read_hae(void) { return 0; } static void tsunami_write_hae(u_int64_t hae) { } static int tsunami_probe(device_t dev); static int tsunami_attach(device_t dev); static int tsunami_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep); static int tsunami_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie); static device_method_t tsunami_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tsunami_probe), DEVMETHOD(device_attach, tsunami_attach), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_alloc_resource, pci_alloc_resource), DEVMETHOD(bus_release_resource, pci_release_resource), DEVMETHOD(bus_activate_resource, pci_activate_resource), DEVMETHOD(bus_deactivate_resource, pci_deactivate_resource), DEVMETHOD(bus_setup_intr, tsunami_setup_intr), DEVMETHOD(bus_teardown_intr, tsunami_teardown_intr), { 0, 0 } }; static driver_t tsunami_driver = { "tsunami", tsunami_methods, sizeof(struct tsunami_softc), }; static void pchip_init(volatile tsunami_pchip *pchip, int index) { int i; /* * initialize the direct map DMA windows. * * leave window 0 untouched; we'll set that up for S/G DMA for * isa devices later in the boot process * * window 1 goes at 2GB and has a length of 1 GB. It maps * physical address 0 - 1GB. The SRM console typically sets * this window up here. */ pchip->wsba[1].reg = (2UL*1024*1024*1024) | WINDOW_ENABLE; pchip->wsm[1].reg = (1UL*1024*1024*1024 - 1) & 0xfff00000UL; pchip->tba[1].reg = 0; /* * window 2 goes at 3GB and has a length of 1 GB. It maps * physical address 1GB-2GB. */ pchip->wsba[2].reg = (3UL*1024*1024*1024) | WINDOW_ENABLE; pchip->wsm[2].reg = (1UL*1024*1024*1024 - 1) & 0xfff00000UL; pchip->tba[2].reg = 1UL*1024*1024*1024; /* * window 3 is disabled. The SRM console typically leaves it * disabled */ pchip->wsba[3].reg = 0; alpha_mb(); if(bootverbose) { printf("pchip%d:\n", index); for (i = 0; i < 4; i++) { printf("\twsba[%d].reg = 0x%lx\n", i, pchip->wsba[i].reg); printf("\t wsm[%d].reg = 0x%lx\n", i, pchip->wsm[i].reg); printf("\t tba[%d].reg = 0x%lx\n", i, pchip->tba[i].reg); } } } #define TSUNAMI_SGMAP_BASE (8*1024*1024) #define TSUNAMI_SGMAP_SIZE (8*1024*1024) static void tsunami_sgmap_invalidate(void) { alpha_mb(); switch (num_pchips) { case 2: pchip[1]->tlbia.reg = (u_int64_t)0; case 1: pchip[0]->tlbia.reg = (u_int64_t)0; } alpha_mb(); } static void tsunami_sgmap_map(void *arg, vm_offset_t ba, vm_offset_t pa) { u_int64_t *sgtable = arg; int index = alpha_btop(ba - TSUNAMI_SGMAP_BASE); if (pa) { if (pa > (1L<<32)) panic("tsunami_sgmap_map: can't map address 0x%lx", pa); sgtable[index] = ((pa >> 13) << 1) | 1; } else { sgtable[index] = 0; } alpha_mb(); tsunami_sgmap_invalidate(); } static void tsunami_init_sgmap(void) { void *sgtable; int i; sgtable = contigmalloc(8192, M_DEVBUF, M_NOWAIT, 0, (1L<<34), 32*1024, (1L<<34)); if (!sgtable) panic("tsunami_init_sgmap: can't allocate page table"); for(i=0; i < num_pchips; i++){ pchip[i]->tba[0].reg = pmap_kextract((vm_offset_t) sgtable); pchip[i]->wsba[0].reg |= WINDOW_ENABLE | WINDOW_SCATTER_GATHER; } chipset.sgmap = sgmap_map_create(TSUNAMI_SGMAP_BASE, TSUNAMI_SGMAP_BASE + TSUNAMI_SGMAP_SIZE, tsunami_sgmap_map, sgtable); } void tsunami_init() { static int initted = 0; if (initted) return; initted = 1; chipset = tsunami_chipset; platform.pci_intr_enable = tsunami_intr_enable; platform.pci_intr_disable = tsunami_intr_disable; alpha_XXX_dmamap_or = 2UL * 1024UL * 1024UL * 1024UL; if (platform.pci_intr_init) platform.pci_intr_init(); } static int tsunami_probe(device_t dev) { + device_t child; int *hose; int i; if (tsunami0) return ENXIO; tsunami0 = dev; device_set_desc(dev, "21271 Core Logic chipset"); if(cchip->csc.reg & CSC_P1P) num_pchips = 2; else num_pchips = 1; pci_init_resources(); isa_init_intr(); for(i = 0; i < num_pchips; i++) { hose = malloc(sizeof(int), M_DEVBUF, M_NOWAIT); *hose = i; - device_add_child(dev, "pcib", i, hose); + child = device_add_child(dev, "pcib", i); + device_set_ivars(child, hose); pchip_init(pchip[i], i); } return 0; } static int tsunami_attach(device_t dev) { tsunami_init(); if (!platform.iointr) /* XXX */ set_iointr(alpha_dispatch_intr); snprintf(chipset_type, sizeof(chipset_type), "tsunami"); chipset_bwx = 1; chipset_ports = TSUNAMI_IO(0); chipset_memory = TSUNAMI_MEM(0); chipset_dense = TSUNAMI_MEM(0); bus_generic_attach(dev); tsunami_init_sgmap(); return 0; } static int tsunami_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep) { int error; error = rman_activate_resource(irq); if (error) return error; error = alpha_setup_intr(0x900 + (irq->r_start << 4), intr, arg, cookiep, &intrcnt[INTRCNT_EB164_IRQ + irq->r_start]); if (error) return error; /* Enable PCI interrupt */ platform.pci_intr_enable(irq->r_start); device_printf(child, "interrupting at TSUNAMI irq %d\n", (int) irq->r_start); return 0; } static int tsunami_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie) { alpha_teardown_intr(cookie); return rman_deactivate_resource(irq); } /* * Currently, all interrupts will be funneled through CPU 0 */ static void tsunami_intr_enable(int irq) { volatile u_int64_t *mask; u_int64_t saved_mask; mask = &cchip->dim0.reg; saved_mask = *mask; saved_mask |= (1UL << (unsigned long)irq); *mask = saved_mask; alpha_mb(); alpha_mb(); saved_mask = *mask; alpha_mb(); alpha_mb(); } static void tsunami_intr_disable(int irq) { volatile u_int64_t *mask; u_int64_t saved_mask; mask = &cchip->dim0.reg; saved_mask = *mask; saved_mask &= ~(1UL << (unsigned long)irq); *mask = saved_mask; alpha_mb(); saved_mask = *mask; alpha_mb(); alpha_mb(); } DRIVER_MODULE(tsunami, root, tsunami_driver, tsunami_devclass, 0, 0); Index: head/sys/alpha/pci/tsunami_pci.c =================================================================== --- head/sys/alpha/pci/tsunami_pci.c (revision 54072) +++ head/sys/alpha/pci/tsunami_pci.c (revision 54073) @@ -1,119 +1,119 @@ /*- * Copyright (c) 1999 Andrew Gallatin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include static devclass_t pcib_devclass; int tsunami_hoses[TSUNAMI_MAXHOSES+1] = {0,-1,-1,-1,-1}; int tsunami_bus_within_hose(int hose, int bus) { return(bus - tsunami_hoses[hose]); } int tsunami_hose_from_bus(int bus) { int i; for(i = 1; i <= TSUNAMI_MAXHOSES && tsunami_hoses[i] != -1; i++){ if(tsunami_hoses[i] > bus) return i-1; } return i-1; } static int tsunami_pcib_probe(device_t dev) { static int hoseno = 0; device_t child; device_set_desc(dev, "21271 PCI host bus adapter"); - child = device_add_child(dev, "pci", -1, 0); + child = device_add_child(dev, "pci", -1); if(hoseno) tsunami_hoses[hoseno] = device_get_unit(child); hoseno++; return 0; } static int tsunami_pcib_read_ivar(device_t dev, device_t child, int which, u_long *result) { if (which == PCIB_IVAR_HOSE) { *result = *(int*) device_get_ivars(dev); return 0; } return ENOENT; } static device_method_t tsunami_pcib_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tsunami_pcib_probe), DEVMETHOD(device_attach, bus_generic_attach), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, tsunami_pcib_read_ivar), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t tsunami_pcib_driver = { "pcib", tsunami_pcib_methods, 1, }; DRIVER_MODULE(pcib, tsunami, tsunami_pcib_driver, pcib_devclass, 0, 0); Index: head/sys/alpha/tc/ioasic.c =================================================================== --- head/sys/alpha/tc/ioasic.c (revision 54072) +++ head/sys/alpha/tc/ioasic.c (revision 54073) @@ -1,383 +1,387 @@ /* $FreeBSD$ */ /* from $NetBSD: ioasic.c,v 1.19 1998/05/27 00:18:13 thorpej Exp $ */ /*- * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, * NASA Ames Research Center. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University. * All rights reserved. * * Author: Keith Bostic, Chris G. Demetriou * * Permission to use, copy, modify and distribute this software and * its documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include "opt_cpu.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define KV(pa) ALPHA_PHYS_TO_K0SEG(pa) static devclass_t ioasic_devclass; static device_t ioasic0; /* there can be only one */ struct ioasic_softc { device_t sc_dv; vm_offset_t sc_base; void *sc_cookie; }; #define IOASIC_SOFTC(dev) (struct ioasic_softc*) device_get_softc(dev) static int ioasic_probe(device_t dev); static int ioasic_attach(device_t dev); static driver_intr_t ioasic_intrnull; static int ioasic_print_child(device_t bus, device_t dev); static void ioasic_lance_dma_setup(void *v); int ioasic_intr __P((void *)); caddr_t le_iomem = 0; static device_method_t ioasic_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ioasic_probe), DEVMETHOD(device_attach, ioasic_attach), DEVMETHOD(bus_print_child, ioasic_print_child), { 0, 0 } }; static driver_t ioasic_driver = { "ioasic", ioasic_methods, sizeof(struct ioasic_softc), }; #define IOASIC_DEV_LANCE 0 #define IOASIC_DEV_SCC0 1 #define IOASIC_DEV_SCC1 2 #define IOASIC_DEV_ISDN 3 #define IOASIC_DEV_BOGUS -1 #define IOASIC_NCOOKIES 4 #define C(x) ((void *)(u_long)x) struct ioasic_dev ioasic_devs[] = { { "le", 0x000c0000, 0, C(IOASIC_DEV_LANCE), IOASIC_INTR_LANCE, }, #ifdef notyet { "z8530 ", 0x00100000, 0, C(IOASIC_DEV_SCC0), IOASIC_INTR_SCC_0, }, { "z8530 ", 0x00180000, 0, C(IOASIC_DEV_SCC1), IOASIC_INTR_SCC_1, }, #endif { "mcclock", 0x00200000, 0, C(IOASIC_DEV_BOGUS), 0, }, #ifdef notyet { "AMD79c30", 0x00240000, 0, C(IOASIC_DEV_ISDN), IOASIC_INTR_ISDN, }, #endif }; int ioasic_ndevs = sizeof(ioasic_devs) / sizeof(ioasic_devs[0]); struct ioasicintr { void (*iai_func) __P((void *)); void *iai_arg; } ioasicintrs[IOASIC_NCOOKIES]; tc_addr_t ioasic_base; /* XXX XXX XXX */ static int ioasic_probe(device_t dev) { if (ioasic0) return ENXIO; if((hwrpb->rpb_type != ST_DEC_3000_300) && (hwrpb->rpb_type != ST_DEC_3000_500)) return ENXIO; if(strcmp(device_get_name(dev),"ioasic")){ return ENXIO; } ioasic0 = dev; if (hwrpb->rpb_type == ST_DEC_3000_300) device_set_desc(dev, "Turbochannel ioasic: slow mode"); else device_set_desc(dev, "Turbochannel ioasic: fast mode"); return 0; } static int ioasic_attach(device_t dev) { + device_t child; struct ioasic_softc* sc = IOASIC_SOFTC(dev); struct tc_attach_args *ta = device_get_ivars(dev); device_t parent = device_get_parent(dev); u_long i; ioasic0 = dev; sc->sc_base = ta->ta_addr; sc->sc_cookie = ta->ta_cookie; ioasic_base = sc->sc_base; #ifdef DEC_3000_300 if (hwrpb->rpb_type == ST_DEC_3000_300) { *(volatile u_int *)IOASIC_REG_CSR(sc->sc_base) |= IOASIC_CSR_FASTMODE; tc_mb(); } #endif /* * Turn off all device interrupt bits. * (This does _not_ include 3000/300 TC option slot bits. */ for (i = 0; i < ioasic_ndevs; i++) *(volatile u_int32_t *)IOASIC_REG_IMSK(ioasic_base) &= ~ioasic_devs[i].iad_intrbits; tc_mb(); /* * Set up interrupt handlers. */ for (i = 0; i < IOASIC_NCOOKIES; i++) { ioasicintrs[i].iai_func = ioasic_intrnull; ioasicintrs[i].iai_arg = (void *)i; } tc_intr_establish(parent, sc->sc_cookie, 0, ioasic_intr, sc); #define LANCE_DMA_SIZE 128*1024 #define LANCE_DMA_ALIGN 128*1024 /* * Set up the LANCE DMA area. */ le_iomem = (caddr_t)vm_page_alloc_contig(round_page(LANCE_DMA_SIZE), 0, 0xffffffff,LANCE_DMA_ALIGN); le_iomem = (caddr_t)ALPHA_PHYS_TO_K0SEG(vtophys(le_iomem)); ioasic_lance_dma_setup((void *)le_iomem); /* * round up our children */ for (i = 0; i < ioasic_ndevs; i++) { ioasic_devs[i].iada_addr = sc->sc_base + ioasic_devs[i].iad_offset; - device_probe_and_attach(device_add_child(dev, ioasic_devs[i].iad_modname, -1, &ioasic_devs[i])); + + child = device_add_child(dev, ioasic_devs[i].iad_modname, -1); + device_set_ivars(child, &ioasic_devs[i]); + device_probe_and_attach(child); } return 0; } static void ioasic_intrnull(void *val) { panic("ioasic_intrnull: uncaught IOASIC intr for cookie %ld\n", (u_long)val); } static int ioasic_print_child(device_t bus, device_t dev) { struct ioasic_dev *ioasic = device_get_ivars(dev); int retval = 0; retval += bus_print_child_header(bus, dev); retval += printf(" on %s offset 0x%x\n", device_get_nameunit(bus), ioasic->iad_offset); return (retval); } char * ioasic_lance_ether_address() { return (u_char *)IOASIC_SYS_ETHER_ADDRESS(ioasic_base); } static void ioasic_lance_dma_setup(void *v) { volatile u_int32_t *ldp; tc_addr_t tca; tca = (tc_addr_t)v; tca &= 0xffffffff; ldp = (volatile u_int *)IOASIC_REG_LANCE_DMAPTR(ioasic_base); *ldp = ((tca << 3) & ~(tc_addr_t)0x1f) | ((tca >> 29) & 0x1f); tc_wmb(); *(volatile u_int32_t *)IOASIC_REG_CSR(ioasic_base) |= IOASIC_CSR_DMAEN_LANCE; tc_mb(); } void ioasic_intr_establish(ioa, cookie, level, func, arg) device_t ioa; void *cookie, *arg; tc_intrlevel_t level; void (*func) __P((void *)); { u_long dev, i; dev = (u_long)cookie; #ifdef DIAGNOSTIC /* XXX check cookie. */ #endif if (ioasicintrs[dev].iai_func != ioasic_intrnull) panic("ioasic_intr_establish: cookie %ld twice", dev); ioasicintrs[dev].iai_func = func; ioasicintrs[dev].iai_arg = arg; /* Enable interrupts for the device. */ for (i = 0; i < ioasic_ndevs; i++) if (ioasic_devs[i].iad_cookie == cookie) break; if (i == ioasic_ndevs) panic("ioasic_intr_establish: invalid cookie."); *(volatile u_int32_t *)IOASIC_REG_IMSK(ioasic_base) |= ioasic_devs[i].iad_intrbits; tc_mb(); } void ioasic_intr_disestablish(ioa, cookie) device_t ioa; void *cookie; { u_long dev, i; dev = (u_long)cookie; #ifdef DIAGNOSTIC /* XXX check cookie. */ #endif if (ioasicintrs[dev].iai_func == ioasic_intrnull) panic("ioasic_intr_disestablish: cookie %ld missing intr", dev); /* Enable interrupts for the device. */ for (i = 0; i < ioasic_ndevs; i++) if (ioasic_devs[i].iad_cookie == cookie) break; if (i == ioasic_ndevs) panic("ioasic_intr_disestablish: invalid cookie."); *(volatile u_int32_t *)IOASIC_REG_IMSK(ioasic_base) &= ~ioasic_devs[i].iad_intrbits; tc_mb(); ioasicintrs[dev].iai_func = ioasic_intrnull; ioasicintrs[dev].iai_arg = (void *)dev; } /* * asic_intr -- * ASIC interrupt handler. */ int ioasic_intr(val) void *val; { register struct ioasic_softc *sc = val; register int ifound; int gifound; u_int32_t sir; volatile u_int32_t *sirp; sirp = (volatile u_int32_t *)IOASIC_REG_INTR(sc->sc_base); gifound = 0; do { ifound = 0; tc_syncbus(); sir = *sirp; /* XXX DUPLICATION OF INTERRUPT BIT INFORMATION... */ #define CHECKINTR(slot, bits) \ if (sir & bits) { \ ifound = 1; \ (*ioasicintrs[slot].iai_func) \ (ioasicintrs[slot].iai_arg); \ } CHECKINTR(IOASIC_DEV_SCC0, IOASIC_INTR_SCC_0); CHECKINTR(IOASIC_DEV_SCC1, IOASIC_INTR_SCC_1); CHECKINTR(IOASIC_DEV_LANCE, IOASIC_INTR_LANCE); CHECKINTR(IOASIC_DEV_ISDN, IOASIC_INTR_ISDN); gifound |= ifound; } while (ifound); return (gifound); } DRIVER_MODULE(ioasic, tc, ioasic_driver, ioasic_devclass, 0, 0); Index: head/sys/alpha/tc/tc.c =================================================================== --- head/sys/alpha/tc/tc.c (revision 54072) +++ head/sys/alpha/tc/tc.c (revision 54073) @@ -1,687 +1,688 @@ /* $FreeBSD$ */ /* * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University. * All rights reserved. * * Author: Chris G. Demetriou * * Permission to use, copy, modify and distribute this software and * its documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include "opt_cpu.h" #include #include #include #include #include #include #include #include #include #include #include /*#include */ #define KV(pa) ALPHA_PHYS_TO_K0SEG(pa) static devclass_t tc_devclass; device_t tc0; /* XXX only one for now */ struct tc_softc { device_t sc_dv; int sc_speed; int sc_nslots; int nbuiltins; struct tc_builtin *builtins; struct tc_slotdesc *sc_slots; void (*sc_intr_establish) __P((struct device *, void *, tc_intrlevel_t, int (*)(void *), void *)); void (*sc_intr_disestablish) __P((struct device *, void *)); /* bus_dma_tag_t (*sc_get_dma_tag) __P((int)); */ }; #define NTC_ROMOFFS 2 static tc_offset_t tc_slot_romoffs[NTC_ROMOFFS] = { TC_SLOT_ROM, TC_SLOT_PROTOROM, }; #define TC_SOFTC(dev) (struct tc_softc*) device_get_softc(dev) static int tc_probe(device_t dev); static int tc_attach(device_t dev); int tc_checkslot( tc_addr_t slotbase, char *namep); static device_method_t tc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tc_probe), DEVMETHOD(device_attach, tc_attach), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), { 0, 0 }, }; static driver_t tc_driver = { "tc", tc_methods, sizeof(struct tc_softc), }; #define C(x) ((void *)(u_long)x) int tc_intrnull __P((void *)); struct tcintr { int (*tci_func) __P((void *)); void *tci_arg; }; #ifdef DEC_3000_300 void tc_3000_300_intr_setup __P((void)); void tc_3000_300_intr_establish __P((struct device *, void *, tc_intrlevel_t, int (*)(void *), void *)); void tc_3000_300_intr_disestablish __P((struct device *, void *)); void tc_3000_300_iointr __P((void *, unsigned long)); #define DEC_3000_300_IOASIC_ADDR KV(0x1a0000000) struct tc_slotdesc tc_3000_300_slots[] = { { KV(0x100000000), C(TC_3000_300_DEV_OPT0), }, /* 0 - opt slot 0 */ { KV(0x120000000), C(TC_3000_300_DEV_OPT1), }, /* 1 - opt slot 1 */ { KV(0x180000000), C(TC_3000_300_DEV_BOGUS), }, /* 2 - TCDS ASIC */ { KV(0x1a0000000), C(TC_3000_300_DEV_BOGUS), }, /* 3 - IOCTL ASIC */ { KV(0x1c0000000), C(TC_3000_300_DEV_CXTURBO), }, /* 4 - CXTurbo */ }; int tc_3000_300_nslots = sizeof(tc_3000_300_slots) / sizeof(tc_3000_300_slots[0]); struct tc_builtin tc_3000_300_builtins[] = { #ifdef notyet { "PMAGB-BA", 4, 0x02000000, C(TC_3000_300_DEV_CXTURBO), }, #endif { "ioasic", 3, 0x00000000, C(TC_3000_300_DEV_IOASIC), }, { "tcds", 2, 0x00000000, C(TC_3000_300_DEV_TCDS), }, }; int tc_3000_300_nbuiltins = sizeof(tc_3000_300_builtins) / sizeof(tc_3000_300_builtins[0]); struct tcintr tc_3000_300_intr[TC_3000_300_NCOOKIES]; #endif /* DEC_3000_300 */ #ifdef DEC_3000_500 void tc_3000_500_intr_setup __P((void)); void tc_3000_500_intr_establish __P((struct device *, void *, tc_intrlevel_t, int (*)(void *), void *)); void tc_3000_500_intr_disestablish __P((struct device *, void *)); void tc_3000_500_iointr __P((void *, unsigned long)); struct tc_slotdesc tc_3000_500_slots[] = { { KV(0x100000000), C(TC_3000_500_DEV_OPT0), }, /* 0 - opt slot 0 */ { KV(0x120000000), C(TC_3000_500_DEV_OPT1), }, /* 1 - opt slot 1 */ { KV(0x140000000), C(TC_3000_500_DEV_OPT2), }, /* 2 - opt slot 2 */ { KV(0x160000000), C(TC_3000_500_DEV_OPT3), }, /* 3 - opt slot 3 */ { KV(0x180000000), C(TC_3000_500_DEV_OPT4), }, /* 4 - opt slot 4 */ { KV(0x1a0000000), C(TC_3000_500_DEV_OPT5), }, /* 5 - opt slot 5 */ { KV(0x1c0000000), C(TC_3000_500_DEV_BOGUS), }, /* 6 - TCDS ASIC */ { KV(0x1e0000000), C(TC_3000_500_DEV_BOGUS), }, /* 7 - IOCTL ASIC */ }; int tc_3000_500_nslots = sizeof(tc_3000_500_slots) / sizeof(tc_3000_500_slots[0]); struct tc_builtin tc_3000_500_builtins[] = { { "ioasic", 7, 0x00000000, C(TC_3000_500_DEV_IOASIC), }, #ifdef notyet { "PMAGB-BA", 7, 0x02000000, C(TC_3000_500_DEV_CXTURBO), }, #endif { "tcds", 6, 0x00000000, C(TC_3000_500_DEV_TCDS), }, }; int tc_3000_500_nbuiltins = sizeof(tc_3000_500_builtins) / sizeof(tc_3000_500_builtins[0]); u_int32_t tc_3000_500_intrbits[TC_3000_500_NCOOKIES] = { TC_3000_500_IR_OPT0, TC_3000_500_IR_OPT1, TC_3000_500_IR_OPT2, TC_3000_500_IR_OPT3, TC_3000_500_IR_OPT4, TC_3000_500_IR_OPT5, TC_3000_500_IR_TCDS, TC_3000_500_IR_IOASIC, TC_3000_500_IR_CXTURBO, }; struct tcintr tc_3000_500_intr[TC_3000_500_NCOOKIES]; u_int32_t tc_3000_500_imask; /* intrs we want to ignore; mirrors IMR. */ #endif /* DEC_3000_500 */ #ifdef DEC_3000_300 void tc_3000_300_intr_setup() { volatile u_int32_t *imskp; u_long i; /* * Disable all interrupts that we can (can't disable builtins). */ imskp = (volatile u_int32_t *)IOASIC_REG_IMSK(DEC_3000_300_IOASIC_ADDR); *imskp &= ~(IOASIC_INTR_300_OPT0 | IOASIC_INTR_300_OPT1); /* * Set up interrupt handlers. */ for (i = 0; i < TC_3000_300_NCOOKIES; i++) { tc_3000_300_intr[i].tci_func = tc_intrnull; tc_3000_300_intr[i].tci_arg = (void *)i; } } void tc_3000_300_intr_establish(tcadev, cookie, level, func, arg) struct device *tcadev; void *cookie, *arg; tc_intrlevel_t level; int (*func) __P((void *)); { volatile u_int32_t *imskp; u_long dev = (u_long)cookie; #ifdef DIAGNOSTIC /* XXX bounds-check cookie. */ #endif if (tc_3000_300_intr[dev].tci_func != tc_intrnull) panic("tc_3000_300_intr_establish: cookie %ld twice", dev); tc_3000_300_intr[dev].tci_func = func; tc_3000_300_intr[dev].tci_arg = arg; imskp = (volatile u_int32_t *)IOASIC_REG_IMSK(DEC_3000_300_IOASIC_ADDR); switch (dev) { case TC_3000_300_DEV_OPT0: *imskp |= IOASIC_INTR_300_OPT0; break; case TC_3000_300_DEV_OPT1: *imskp |= IOASIC_INTR_300_OPT1; break; default: /* interrupts for builtins always enabled */ break; } } void tc_3000_300_intr_disestablish(tcadev, cookie) struct device *tcadev; void *cookie; { volatile u_int32_t *imskp; u_long dev = (u_long)cookie; #ifdef DIAGNOSTIC /* XXX bounds-check cookie. */ #endif if (tc_3000_300_intr[dev].tci_func == tc_intrnull) panic("tc_3000_300_intr_disestablish: cookie %ld bad intr", dev); imskp = (volatile u_int32_t *)IOASIC_REG_IMSK(DEC_3000_300_IOASIC_ADDR); switch (dev) { case TC_3000_300_DEV_OPT0: *imskp &= ~IOASIC_INTR_300_OPT0; break; case TC_3000_300_DEV_OPT1: *imskp &= ~IOASIC_INTR_300_OPT1; break; default: /* interrupts for builtins always enabled */ break; } tc_3000_300_intr[dev].tci_func = tc_intrnull; tc_3000_300_intr[dev].tci_arg = (void *)dev; } void tc_3000_300_iointr(framep, vec) void *framep; unsigned long vec; { u_int32_t tcir, ioasicir, ioasicimr; int ifound; #ifdef DIAGNOSTIC int s; if (vec != 0x800) panic("INVALID ASSUMPTION: vec 0x%lx, not 0x800", vec); s = splhigh(); if (s != ALPHA_PSL_IPL_IO) panic("INVALID ASSUMPTION: IPL %d, not %d", s, ALPHA_PSL_IPL_IO); splx(s); #endif do { tc_syncbus(); /* find out what interrupts/errors occurred */ tcir = *(volatile u_int32_t *)TC_3000_300_IR; ioasicir = *(volatile u_int32_t *) IOASIC_REG_INTR(DEC_3000_300_IOASIC_ADDR); ioasicimr = *(volatile u_int32_t *) IOASIC_REG_IMSK(DEC_3000_300_IOASIC_ADDR); tc_mb(); /* Ignore interrupts that aren't enabled out. */ ioasicir &= ioasicimr; /* clear the interrupts/errors we found. */ *(volatile u_int32_t *)TC_3000_300_IR = tcir; /* XXX can't clear TC option slot interrupts here? */ tc_wmb(); ifound = 0; #define CHECKINTR(slot, flag) \ if (flag) { \ ifound = 1; \ (*tc_3000_300_intr[slot].tci_func) \ (tc_3000_300_intr[slot].tci_arg); \ } /* Do them in order of priority; highest slot # first. */ CHECKINTR(TC_3000_300_DEV_CXTURBO, tcir & TC_3000_300_IR_CXTURBO); CHECKINTR(TC_3000_300_DEV_IOASIC, (tcir & TC_3000_300_IR_IOASIC) && (ioasicir & ~(IOASIC_INTR_300_OPT1|IOASIC_INTR_300_OPT0))); CHECKINTR(TC_3000_300_DEV_TCDS, tcir & TC_3000_300_IR_TCDS); CHECKINTR(TC_3000_300_DEV_OPT1, ioasicir & IOASIC_INTR_300_OPT1); CHECKINTR(TC_3000_300_DEV_OPT0, ioasicir & IOASIC_INTR_300_OPT0); #undef CHECKINTR #ifdef DIAGNOSTIC #define PRINTINTR(msg, bits) \ if (tcir & bits) \ printf(msg); PRINTINTR("BCache tag parity error\n", TC_3000_300_IR_BCTAGPARITY); PRINTINTR("TC overrun error\n", TC_3000_300_IR_TCOVERRUN); PRINTINTR("TC I/O timeout\n", TC_3000_300_IR_TCTIMEOUT); PRINTINTR("Bcache parity error\n", TC_3000_300_IR_BCACHEPARITY); PRINTINTR("Memory parity error\n", TC_3000_300_IR_MEMPARITY); #undef PRINTINTR #endif } while (ifound); } #endif /* DEC_3000_300 */ #ifdef DEC_3000_500 void tc_3000_500_intr_setup() { u_long i; /* * Disable all slot interrupts. Note that this cannot * actually disable CXTurbo, TCDS, and IOASIC interrupts. */ tc_3000_500_imask = *(volatile u_int32_t *)TC_3000_500_IMR_READ; for (i = 0; i < TC_3000_500_NCOOKIES; i++) tc_3000_500_imask |= tc_3000_500_intrbits[i]; *(volatile u_int32_t *)TC_3000_500_IMR_WRITE = tc_3000_500_imask; tc_mb(); /* * Set up interrupt handlers. */ for (i = 0; i < TC_3000_500_NCOOKIES; i++) { tc_3000_500_intr[i].tci_func = tc_intrnull; tc_3000_500_intr[i].tci_arg = (void *)i; } } void tc_3000_500_intr_establish(tcadev, cookie, level, func, arg) struct device *tcadev; void *cookie, *arg; tc_intrlevel_t level; int (*func) __P((void *)); { u_long dev = (u_long)cookie; #ifdef DIAGNOSTIC /* XXX bounds-check cookie. */ #endif if (tc_3000_500_intr[dev].tci_func != tc_intrnull) panic("tc_3000_500_intr_establish: cookie %ld twice", dev); tc_3000_500_intr[dev].tci_func = func; tc_3000_500_intr[dev].tci_arg = arg; tc_3000_500_imask &= ~tc_3000_500_intrbits[dev]; *(volatile u_int32_t *)TC_3000_500_IMR_WRITE = tc_3000_500_imask; tc_mb(); } void tc_3000_500_intr_disestablish(tcadev, cookie) struct device *tcadev; void *cookie; { u_long dev = (u_long)cookie; #ifdef DIAGNOSTIC /* XXX bounds-check cookie. */ #endif if (tc_3000_500_intr[dev].tci_func == tc_intrnull) panic("tc_3000_500_intr_disestablish: cookie %ld bad intr", dev); tc_3000_500_imask |= tc_3000_500_intrbits[dev]; *(volatile u_int32_t *)TC_3000_500_IMR_WRITE = tc_3000_500_imask; tc_mb(); tc_3000_500_intr[dev].tci_func = tc_intrnull; tc_3000_500_intr[dev].tci_arg = (void *)dev; } void tc_3000_500_iointr(framep, vec) void *framep; unsigned long vec; { u_int32_t ir; int ifound; #ifdef DIAGNOSTIC int s; if (vec != 0x800) panic("INVALID ASSUMPTION: vec 0x%lx, not 0x800", vec); s = splhigh(); if (s != ALPHA_PSL_IPL_IO) panic("INVALID ASSUMPTION: IPL %d, not %d", s, ALPHA_PSL_IPL_IO); splx(s); #endif do { tc_syncbus(); ir = *(volatile u_int32_t *)TC_3000_500_IR_CLEAR; /* Ignore interrupts that we haven't enabled. */ ir &= ~(tc_3000_500_imask & 0x1ff); ifound = 0; #define CHECKINTR(slot) \ if (ir & tc_3000_500_intrbits[slot]) { \ ifound = 1; \ (*tc_3000_500_intr[slot].tci_func) \ (tc_3000_500_intr[slot].tci_arg); \ } /* Do them in order of priority; highest slot # first. */ CHECKINTR(TC_3000_500_DEV_CXTURBO); CHECKINTR(TC_3000_500_DEV_IOASIC); CHECKINTR(TC_3000_500_DEV_TCDS); CHECKINTR(TC_3000_500_DEV_OPT5); CHECKINTR(TC_3000_500_DEV_OPT4); CHECKINTR(TC_3000_500_DEV_OPT3); CHECKINTR(TC_3000_500_DEV_OPT2); CHECKINTR(TC_3000_500_DEV_OPT1); CHECKINTR(TC_3000_500_DEV_OPT0); #undef CHECKINTR #ifdef DIAGNOSTIC #define PRINTINTR(msg, bits) \ if (ir & bits) \ printf(msg); PRINTINTR("Second error occurred\n", TC_3000_500_IR_ERR2); PRINTINTR("DMA buffer error\n", TC_3000_500_IR_DMABE); PRINTINTR("DMA cross 2K boundary\n", TC_3000_500_IR_DMA2K); PRINTINTR("TC reset in progress\n", TC_3000_500_IR_TCRESET); PRINTINTR("TC parity error\n", TC_3000_500_IR_TCPAR); PRINTINTR("DMA tag error\n", TC_3000_500_IR_DMATAG); PRINTINTR("Single-bit error\n", TC_3000_500_IR_DMASBE); PRINTINTR("Double-bit error\n", TC_3000_500_IR_DMADBE); PRINTINTR("TC I/O timeout\n", TC_3000_500_IR_TCTIMEOUT); PRINTINTR("DMA block too long\n", TC_3000_500_IR_DMABLOCK); PRINTINTR("Invalid I/O address\n", TC_3000_500_IR_IOADDR); PRINTINTR("DMA scatter/gather invalid\n", TC_3000_500_IR_DMASG); PRINTINTR("Scatter/gather parity error\n", TC_3000_500_IR_SGPAR); #undef PRINTINTR #endif } while (ifound); } #if 0 /* * tc_3000_500_ioslot -- * Set the PBS bits for devices on the TC. */ void tc_3000_500_ioslot(slot, flags, set) u_int32_t slot, flags; int set; { volatile u_int32_t *iosp; u_int32_t ios; int s; iosp = (volatile u_int32_t *)TC_3000_500_IOSLOT; ios = *iosp; flags <<= (slot * 3); if (set) ios |= flags; else ios &= ~flags; s = splhigh(); *iosp = ios; tc_mb(); splx(s); } #endif #endif /* DEC_3000_500 */ int tc_intrnull(val) void *val; { panic("tc_intrnull: uncaught TC intr for cookie %ld\n", (u_long)val); } static int tc_probe(device_t dev) { if((hwrpb->rpb_type != ST_DEC_3000_300) && (hwrpb->rpb_type != ST_DEC_3000_500)) return ENXIO; tc0 = dev; if(hwrpb->rpb_type == ST_DEC_3000_300) { device_set_desc(dev, "12.5 Mhz Turbochannel Bus"); } else { device_set_desc(dev, "25 Mhz Turbochannel Bus"); } return 0; } static int tc_attach(device_t dev) { struct tc_softc* sc = TC_SOFTC(dev); tc_addr_t tcaddr; const struct tc_builtin *builtin; struct tc_attach_args *ta; int i; device_t child = NULL; tc0 = dev; switch(hwrpb->rpb_type){ #ifdef DEC_3000_300 case ST_DEC_3000_300: sc->sc_speed = TC_SPEED_12_5_MHZ; sc->sc_nslots = tc_3000_300_nslots; sc->sc_slots = tc_3000_300_slots; sc->nbuiltins = tc_3000_300_nbuiltins; sc->builtins = tc_3000_300_builtins; tc_3000_300_intr_setup(); set_iointr(tc_3000_300_iointr); sc->sc_intr_establish = tc_3000_300_intr_establish; sc->sc_intr_disestablish = tc_3000_300_intr_disestablish; break; #endif /* DEC_3000_500 */ #ifdef DEC_3000_500 case ST_DEC_3000_500: sc->sc_speed = TC_SPEED_25_MHZ; sc->sc_nslots = tc_3000_500_nslots; sc->sc_slots = tc_3000_500_slots; sc->nbuiltins = tc_3000_500_nbuiltins; sc->builtins = tc_3000_500_builtins; tc_3000_500_intr_setup(); set_iointr(tc_3000_500_iointr); sc->sc_intr_establish = tc_3000_500_intr_establish; sc->sc_intr_disestablish = tc_3000_500_intr_disestablish; break; #endif /* DEC_3000_500 */ default: panic("tcattach: bad cpu type"); } /* * Try to configure each built-in device */ for (i = 0; i < sc->nbuiltins; i++) { builtin = &sc->builtins[i]; tcaddr = sc->sc_slots[builtin->tcb_slot].tcs_addr + builtin->tcb_offset; if (tc_badaddr(tcaddr)) continue; ta = malloc(sizeof(struct tc_attach_args), M_DEVBUF, M_NOWAIT); if (!ta) continue; ta->ta_slot = builtin->tcb_slot; ta->ta_offset = builtin->tcb_offset; ta->ta_addr = tcaddr; ta->ta_cookie = builtin->tcb_cookie; ta->ta_busspeed = sc->sc_speed; - child = device_add_child(dev, builtin->tcb_modname, 0, ta); + child = device_add_child(dev, builtin->tcb_modname, 0); + device_set_ivars(child, ta); device_probe_and_attach(child); } return 0; } int tc_checkslot(slotbase, namep) tc_addr_t slotbase; char *namep; { struct tc_rommap *romp; int i, j; for (i = 0; i < NTC_ROMOFFS; i++) { romp = (struct tc_rommap *) (slotbase + tc_slot_romoffs[i]); switch (romp->tcr_width.v) { case 1: case 2: case 4: break; default: continue; } if (romp->tcr_stride.v != 4) continue; for (j = 0; j < 4; j++) if (romp->tcr_test[j+0*romp->tcr_stride.v] != 0x55 || romp->tcr_test[j+1*romp->tcr_stride.v] != 0x00 || romp->tcr_test[j+2*romp->tcr_stride.v] != 0xaa || romp->tcr_test[j+3*romp->tcr_stride.v] != 0xff) continue; for (j = 0; j < TC_ROM_LLEN; j++) namep[j] = romp->tcr_modname[j].v; namep[j] = '\0'; return (1); } return (0); } void tc_intr_establish(dev, cookie, level, handler, arg) struct device *dev; void *cookie, *arg; tc_intrlevel_t level; int (*handler) __P((void *)); { struct tc_softc *sc = (struct tc_softc *)device_get_softc(dev); (*sc->sc_intr_establish)(device_get_parent(dev), cookie, level, handler, arg); } void tc_intr_disestablish(dev, cookie) struct device *dev; void *cookie; { struct tc_softc *sc = (struct tc_softc *)device_get_softc(dev); (*sc->sc_intr_disestablish)(device_get_parent(dev), cookie); } DRIVER_MODULE(tc, tcasic, tc_driver, tc_devclass, 0, 0); Index: head/sys/alpha/tc/tcasic.c =================================================================== --- head/sys/alpha/tc/tcasic.c (revision 54072) +++ head/sys/alpha/tc/tcasic.c (revision 54073) @@ -1,100 +1,100 @@ /* $FreeBSD$ */ /* from $NetBSD: tcasic.c,v 1.23 1998/05/14 00:01:31 thorpej Exp $ */ /* * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University. * All rights reserved. * * Author: Chris G. Demetriou * * Permission to use, copy, modify and distribute this software and * its documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include "opt_cpu.h" #include #include #include #include #include #include /*#include */ #define KV(pa) ALPHA_PHYS_TO_K0SEG(pa) static devclass_t tcasic_devclass; static device_t tcasic0; /* XXX only one for now */ struct tcasic_softc { vm_offset_t dmem_base; /* dense memory */ vm_offset_t smem_base; /* sparse memory */ vm_offset_t io_base; /* sparse i/o */ vm_offset_t cfg_base; /* sparse pci config */ }; #define TCASIC_SOFTC(dev) (struct tcasic_softc*) device_get_softc(dev) static int tcasic_probe(device_t dev); static int tcasic_attach(device_t dev); static device_method_t tcasic_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tcasic_probe), DEVMETHOD(device_attach, tcasic_attach), DEVMETHOD(bus_print_child, bus_generic_print_child), { 0, 0 } }; static driver_t tcasic_driver = { "tcasic", tcasic_methods, sizeof(struct tcasic_softc), }; extern device_t tc0; static int tcasic_probe(device_t dev) { if (tcasic0) return ENXIO; if((hwrpb->rpb_type != ST_DEC_3000_300) && (hwrpb->rpb_type != ST_DEC_3000_500)) return ENXIO; tcasic0 = dev; device_set_desc(dev, "Turbochannel Host Bus Adapter"); - tc0 = device_add_child(dev, "tc", 0, 0); + tc0 = device_add_child(dev, "tc", 0); return 0; } static int tcasic_attach(device_t dev) { tcasic0 = dev; /* chipset = tcasic_chipset;*/ device_probe_and_attach(tc0); return 0; } DRIVER_MODULE(tcasic, root, tcasic_driver, tcasic_devclass, 0, 0); Index: head/sys/alpha/tc/tcds.c =================================================================== --- head/sys/alpha/tc/tcds.c (revision 54072) +++ head/sys/alpha/tc/tcds.c (revision 54073) @@ -1,449 +1,454 @@ /* $FreeBSD$ */ /* from $NetBSD: tcds.c,v 1.25 1998/05/26 23:43:05 thorpej Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, * NASA Ames Research Center. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University. * All rights reserved. * * Author: Keith Bostic, Chris G. Demetriou * * Permission to use, copy, modify and distribute this software and * its documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include "opt_cpu.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define KV(pa) ALPHA_PHYS_TO_K0SEG(pa) static devclass_t tcds_devclass; static device_t tcds0; /* there can be only one */ struct tcds_softc { device_t sc_dv; vm_offset_t sc_base; void *sc_cookie; volatile u_int32_t *sc_cir; volatile u_int32_t *sc_imer; struct tcds_slotconfig sc_slots[2]; }; #define TCDS_SOFTC(dev) (struct tcds_softc*) device_get_softc(dev) static int tcds_probe(device_t dev); static int tcds_attach(device_t dev); static void tcds_intrnull __P((void *)); static void tcds_lance_dma_setup(void *v); static int tcds_intr __P((void *)); static device_method_t tcds_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tcds_probe), DEVMETHOD(device_attach, tcds_attach), DEVMETHOD(bus_print_child, bus_generic_print_child), { 0, 0 } }; static driver_t tcds_driver = { "tcds", tcds_methods, sizeof(struct tcds_softc), }; extern device_t tc0; static int tcds_probe(device_t dev) { if((hwrpb->rpb_type != ST_DEC_3000_300) && (hwrpb->rpb_type != ST_DEC_3000_500)) return ENXIO; if(strcmp(device_get_name(dev),"tcds")){ return ENXIO; } tcds0 = dev; device_set_desc(dev, "Turbochannel Dual Scsi"); return 0; } struct tcdsdev_attach_args tcdsdev; static int tcds_attach(device_t dev) { struct tcds_softc* sc = TCDS_SOFTC(dev); struct tc_attach_args *ta = device_get_ivars(dev); device_t parent = device_get_parent(dev); + device_t child; vm_offset_t regs,va; u_long i; struct tcds_slotconfig *slotc; struct tcdsdev_attach_args *tcdsdev; tcds0 = dev; /* XXXXXX */ sc->sc_base = ta->ta_addr; sc->sc_cookie = ta->ta_cookie; sc->sc_cir = TCDS_REG(sc->sc_base, TCDS_CIR); sc->sc_imer = TCDS_REG(sc->sc_base, TCDS_IMER); tc_intr_establish(device_get_parent(dev), sc->sc_cookie, 0, tcds_intr, sc); /* * XXX * IMER apparently has some random (or, not so random, but still * not useful) bits set in it when the system boots. Clear it. */ *sc->sc_imer = 0; alpha_wmb(); /* fill in common information first */ for (i = 0; i < 2; i++) { slotc = &sc->sc_slots[i]; bzero(slotc, sizeof *slotc); /* clear everything */ slotc->sc_slot = i; slotc->sc_tcds = sc; slotc->sc_esp = NULL; slotc->sc_intrhand = tcds_intrnull; slotc->sc_intrarg = (void *)(long)i; } /* information for slot 0 */ slotc = &sc->sc_slots[0]; slotc->sc_resetbits = TCDS_CIR_SCSI0_RESET; slotc->sc_intrmaskbits = TCDS_IMER_SCSI0_MASK | TCDS_IMER_SCSI0_ENB; slotc->sc_intrbits = TCDS_CIR_SCSI0_INT; slotc->sc_dmabits = TCDS_CIR_SCSI0_DMAENA; slotc->sc_errorbits = 0; /* XXX */ slotc->sc_sda = TCDS_REG(sc->sc_base, TCDS_SCSI0_DMA_ADDR); slotc->sc_dic = TCDS_REG(sc->sc_base, TCDS_SCSI0_DMA_INTR); slotc->sc_dud0 = TCDS_REG(sc->sc_base, TCDS_SCSI0_DMA_DUD0); slotc->sc_dud1 = TCDS_REG(sc->sc_base, TCDS_SCSI0_DMA_DUD1); /* information for slot 1 */ slotc = &sc->sc_slots[1]; slotc->sc_resetbits = TCDS_CIR_SCSI1_RESET; slotc->sc_intrmaskbits = TCDS_IMER_SCSI1_MASK | TCDS_IMER_SCSI1_ENB; slotc->sc_intrbits = TCDS_CIR_SCSI1_INT; slotc->sc_dmabits = TCDS_CIR_SCSI1_DMAENA; slotc->sc_errorbits = 0; /* XXX */ slotc->sc_sda = TCDS_REG(sc->sc_base, TCDS_SCSI1_DMA_ADDR); slotc->sc_dic = TCDS_REG(sc->sc_base, TCDS_SCSI1_DMA_INTR); slotc->sc_dud0 = TCDS_REG(sc->sc_base, TCDS_SCSI1_DMA_DUD0); slotc->sc_dud1 = TCDS_REG(sc->sc_base, TCDS_SCSI1_DMA_DUD1); /* find the hardware attached to the TCDS ASIC */ tcdsdev = malloc(sizeof(struct tcdsdev_attach_args), M_DEVBUF, M_NOWAIT); if (tcdsdev) { strncpy(tcdsdev->tcdsda_modname, "PMAZ-AA ", TC_ROM_LLEN); tcdsdev->tcdsda_slot = 0; tcdsdev->tcdsda_offset = 0; tcdsdev->tcdsda_addr = (tc_addr_t) TC_DENSE_TO_SPARSE(sc->sc_base + TCDS_SCSI0_OFFSET); tcdsdev->tcdsda_cookie = (void *)(long)0; tcdsdev->tcdsda_sc = &sc->sc_slots[0]; tcdsdev->tcdsda_id = 7; /* XXX */ tcdsdev->tcdsda_freq = 25000000; /* XXX */ tcds_scsi_reset(tcdsdev->tcdsda_sc); - device_probe_and_attach(device_add_child(dev, "esp", -1, tcdsdev)); + child = device_add_child(dev, "esp", -1); + device_set_ivars(child, tcdsdev); + device_probe_and_attach(child); } /* the second SCSI chip isn't present on the 3000/300 series. */ if (hwrpb->rpb_type != ST_DEC_3000_300) { tcdsdev = malloc(sizeof(struct tcdsdev_attach_args), M_DEVBUF, M_NOWAIT); if (tcdsdev) { strncpy(tcdsdev->tcdsda_modname, "PMAZ-AA ", TC_ROM_LLEN); tcdsdev->tcdsda_slot = 1; tcdsdev->tcdsda_offset = 0; tcdsdev->tcdsda_addr = (tc_addr_t) TC_DENSE_TO_SPARSE(sc->sc_base + TCDS_SCSI1_OFFSET); tcdsdev->tcdsda_cookie = (void *)(long)1; tcdsdev->tcdsda_sc = &sc->sc_slots[1]; tcdsdev->tcdsda_id = 7; /* XXX */ tcdsdev->tcdsda_freq = 25000000; /* XXX */ tcds_scsi_reset(tcdsdev->tcdsda_sc); - device_probe_and_attach(device_add_child(dev, "esp", -1, tcdsdev)); + child = device_add_child(dev, "esp", -1); + device_set_ivars(child, tcdsdev); + device_probe_and_attach(child); } } return 0; } void tcds_scsi_reset(sc) struct tcds_slotconfig *sc; { tcds_dma_enable(sc, 0); tcds_scsi_enable(sc, 0); TCDS_CIR_CLR(*sc->sc_tcds->sc_cir, sc->sc_resetbits); alpha_mb(); DELAY(1); TCDS_CIR_SET(*sc->sc_tcds->sc_cir, sc->sc_resetbits); alpha_mb(); tcds_scsi_enable(sc, 1); tcds_dma_enable(sc, 1); } void tcds_scsi_enable(sc, on) struct tcds_slotconfig *sc; int on; { if (on) *sc->sc_tcds->sc_imer |= sc->sc_intrmaskbits; else *sc->sc_tcds->sc_imer &= ~sc->sc_intrmaskbits; alpha_mb(); } void tcds_dma_enable(sc, on) struct tcds_slotconfig *sc; int on; { /* XXX Clear/set IOSLOT/PBS bits. */ if (on) TCDS_CIR_SET(*sc->sc_tcds->sc_cir, sc->sc_dmabits); else TCDS_CIR_CLR(*sc->sc_tcds->sc_cir, sc->sc_dmabits); alpha_mb(); } int tcds_scsi_isintr(sc, clear) struct tcds_slotconfig *sc; int clear; { if ((*sc->sc_tcds->sc_cir & sc->sc_intrbits) != 0) { if (clear) { TCDS_CIR_CLR(*sc->sc_tcds->sc_cir, sc->sc_intrbits); alpha_mb(); } return (1); } else return (0); } int tcds_scsi_iserr(sc) struct tcds_slotconfig *sc; { return ((*sc->sc_tcds->sc_cir & sc->sc_errorbits) != 0); } static void tcds_intrnull(void *val) { panic("tcds_intrnull: uncaught IOASIC intr for cookie %ld\n", (u_long)val); } void tcds_intr_establish(tcds, cookie, level, func, arg) device_t tcds; void *cookie, *arg; tc_intrlevel_t level; int (*func) __P((void *)); { struct tcds_softc *sc = device_get_softc(tcds); u_long slot; slot = (u_long)cookie; #ifdef DIAGNOSTIC /* XXX check cookie. */ #endif if (sc->sc_slots[slot].sc_intrhand != tcds_intrnull){ panic("tcds_intr_establish: cookie %d twice, intrhdlr = 0x%lx", slot,sc->sc_slots[slot].sc_intrhand ); } sc->sc_slots[slot].sc_intrhand = func; sc->sc_slots[slot].sc_intrarg = arg; tcds_scsi_reset(&sc->sc_slots[slot]); } void tcds_intr_disestablish(tcds, cookie) device_t tcds; void *cookie; { struct tcds_softc *sc = device_get_softc(tcds); u_long slot; slot = (u_long)cookie; #ifdef DIAGNOSTIC /* XXX check cookie. */ #endif if (sc->sc_slots[slot].sc_intrhand == tcds_intrnull) panic("tcds_intr_disestablish: cookie %d missing intr", slot); sc->sc_slots[slot].sc_intrhand = tcds_intrnull; sc->sc_slots[slot].sc_intrarg = (void *)slot; tcds_dma_enable(&sc->sc_slots[slot], 0); tcds_scsi_enable(&sc->sc_slots[slot], 0); } static int tcds_intr(val) void *val; { struct tcds_softc *sc; u_int32_t ir; sc = val; /* * XXX * Copy and clear (gag!) the interrupts. */ ir = *sc->sc_cir; alpha_mb(); TCDS_CIR_CLR(*sc->sc_cir, TCDS_CIR_ALLINTR); alpha_mb(); tc_syncbus(); alpha_mb(); #define CHECKINTR(slot) \ if (ir & sc->sc_slots[slot].sc_intrbits) { \ (void)(*sc->sc_slots[slot].sc_intrhand) \ (sc->sc_slots[slot].sc_intrarg); \ } CHECKINTR(0); CHECKINTR(1); #undef CHECKINTR #ifdef DIAGNOSTIC /* * Interrupts not currently handled, but would like to know if they * occur. * * XXX * Don't know if we have to set the interrupt mask and enable bits * in the IMER to allow some of them to happen? */ #define PRINTINTR(msg, bits) \ if (ir & bits) \ printf(msg); PRINTINTR("SCSI0 DREQ interrupt.\n", TCDS_CIR_SCSI0_DREQ); PRINTINTR("SCSI1 DREQ interrupt.\n", TCDS_CIR_SCSI1_DREQ); PRINTINTR("SCSI0 prefetch interrupt.\n", TCDS_CIR_SCSI0_PREFETCH); PRINTINTR("SCSI1 prefetch interrupt.\n", TCDS_CIR_SCSI1_PREFETCH); PRINTINTR("SCSI0 DMA error.\n", TCDS_CIR_SCSI0_DMA); PRINTINTR("SCSI1 DMA error.\n", TCDS_CIR_SCSI1_DMA); PRINTINTR("SCSI0 DB parity error.\n", TCDS_CIR_SCSI0_DB); PRINTINTR("SCSI1 DB parity error.\n", TCDS_CIR_SCSI1_DB); PRINTINTR("SCSI0 DMA buffer parity error.\n", TCDS_CIR_SCSI0_DMAB_PAR); PRINTINTR("SCSI1 DMA buffer parity error.\n", TCDS_CIR_SCSI1_DMAB_PAR); PRINTINTR("SCSI0 DMA read parity error.\n", TCDS_CIR_SCSI0_DMAR_PAR); PRINTINTR("SCSI1 DMA read parity error.\n", TCDS_CIR_SCSI1_DMAR_PAR); PRINTINTR("TC write parity error.\n", TCDS_CIR_TCIOW_PAR); PRINTINTR("TC I/O address parity error.\n", TCDS_CIR_TCIOA_PAR); #undef PRINTINTR #endif /* * XXX * The MACH source had this, with the comment: * This is wrong, but machine keeps dying. */ DELAY(1); return 1; } DRIVER_MODULE(tcds, tc, tcds_driver, tcds_devclass, 0, 0); Index: head/sys/alpha/tlsb/gbus.c =================================================================== --- head/sys/alpha/tlsb/gbus.c (revision 54072) +++ head/sys/alpha/tlsb/gbus.c (revision 54073) @@ -1,158 +1,161 @@ /* $FreeBSD$ */ /* $NetBSD: gbus.c,v 1.8 1998/05/13 22:13:35 thorpej Exp $ */ /* * Copyright (c) 1997 by Matthew Jacob * NASA AMES Research Center. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice immediately at the beginning of the file, without modification, * this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Autoconfiguration and support routines for the Gbus: the internal * bus on AlphaServer CPU modules. */ #include #include #include #include #include #include #include #include #include #include #include extern int cputype; #define KV(_addr) ((caddr_t)ALPHA_PHYS_TO_K0SEG((_addr))) /* * The structure used to attach devices to the Gbus. */ struct gbus_device { const char* gd_name; int gd_offset; }; #define DEVTOGBUS(dev) ((struct gbus_device*) device_get_ivars(dev)) struct gbus_device gbus_children[] = { { "zsc", GBUS_DUART0_OFFSET }, /* { "zsc", GBUS_DUART1_OFFSET },*/ { "mcclock", GBUS_CLOCK_OFFSET }, { NULL, 0 }, }; static devclass_t gbus_devclass; /* * Device methods */ static int gbus_probe(device_t dev); static int gbus_print_child(device_t dev, device_t child); static int gbus_read_ivar(device_t dev, device_t child, int which, u_long *result);; static device_method_t gbus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, gbus_probe), DEVMETHOD(device_attach, bus_generic_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, gbus_print_child), DEVMETHOD(bus_read_ivar, gbus_read_ivar), DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t gbus_driver = { "gbus", gbus_methods, 1, /* no softc */ }; /* * At 'probe' time, we add all the devices which we know about to the * bus. The generic attach routine will probe and attach them if they * are alive. */ static int gbus_probe(device_t dev) { + device_t child; struct gbus_device *gdev; /* * Make sure we're looking for a Gbus. * Right now, only Gbus could be a * child of a TLSB CPU Node. */ if (!TLDEV_ISCPU(tlsb_get_dtype(dev))) return ENXIO; - for (gdev = gbus_children; gdev->gd_name; gdev++) - device_add_child(dev, gdev->gd_name, -1, gdev); + for (gdev = gbus_children; gdev->gd_name; gdev++) { + child = device_add_child(dev, gdev->gd_name, -1); + device_set_ivars(child, gdev); + } return 0; } static int gbus_print_child(device_t bus, device_t dev) { struct gbus_device* gdev = DEVTOGBUS(dev); int retval = 0; retval += bus_print_child_header(bus, dev); retval += printf(" on %s offset 0x%x\n", device_get_nameunit(bus), gdev->gd_offset); return (retval); } static int gbus_read_ivar(device_t bus, device_t dev, int index, u_long* result) { struct gbus_device* gdev = DEVTOGBUS(dev); switch (index) { case GBUS_IVAR_OFFSET: *result = gdev->gd_offset; break; } return ENOENT; } DRIVER_MODULE(gbus, tlsb, gbus_driver, gbus_devclass, 0, 0); Index: head/sys/alpha/tlsb/kftxx.c =================================================================== --- head/sys/alpha/tlsb/kftxx.c (revision 54072) +++ head/sys/alpha/tlsb/kftxx.c (revision 54073) @@ -1,195 +1,197 @@ /* $FreeBSD$ */ /* $NetBSD: kftxx.c,v 1.9 1998/05/14 00:01:32 thorpej Exp $ */ /* * Copyright (c) 1997 by Matthew Jacob * NASA AMES Research Center. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice immediately at the beginning of the file, without modification, * this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * KFTIA and KFTHA Bus Adapter Node for I/O hoses * found on AlphaServer 8200 and 8400 systems. * * i.e., handler for all TLSB I/O nodes. */ #include #include #include #include #include #include #include #include #include #include #include struct kft_softc { int sc_node; /* TLSB node */ u_int16_t sc_dtype; /* device type */ }; /* * Instance variables for kft devices. */ struct kft_device { char * kd_name; /* name */ int kd_node; /* node number */ u_int16_t kd_dtype; /* device type */ u_int16_t kd_hosenum; /* hose number */ }; #define KV(_addr) ((caddr_t)ALPHA_PHYS_TO_K0SEG((_addr))) static devclass_t kft_devclass; /* * Device methods */ static int kft_probe(device_t dev); static int kft_print_child(device_t dev, device_t child); static int kft_read_ivar(device_t dev, device_t child, int which, u_long *result);; static device_method_t kft_methods[] = { /* Device interface */ DEVMETHOD(device_probe, kft_probe), DEVMETHOD(device_attach, bus_generic_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, kft_print_child), DEVMETHOD(bus_read_ivar, kft_read_ivar), DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t kft_driver = { "kft", kft_methods, 1, /* no softc */ }; static int kft_probe(device_t dev) { + device_t child; struct kft_softc *sc = (struct kft_softc *) device_get_softc(dev); struct kft_device* kd; int hoseno; if (!TLDEV_ISIOPORT(tlsb_get_dtype(dev))) return ENXIO; sc->sc_node = tlsb_get_node(dev); sc->sc_dtype = tlsb_get_dtype(dev); for (hoseno = 0; hoseno < MAXHOSE; hoseno++) { u_int32_t value = TLSB_GET_NODEREG(sc->sc_node, KFT_IDPNSEX(hoseno)); if (value & 0x0E000000) { printf("%s%d: Hose %d IDPNSE has %x\n", device_get_name(dev), device_get_unit(dev), hoseno, value); continue; } if ((value & 0x1) != 0x0) { printf("%s%d: Hose %d has a Bad Cable (0x%x)\n", device_get_name(dev), device_get_unit(dev), hoseno, value); continue; } if ((value & 0x6) != 0x6) { if (value) printf("%s%d: Hose %d is missing PWROK (0x%x)\n", device_get_name(dev), device_get_unit(dev), hoseno, value); continue; } kd = (struct kft_device*) malloc(sizeof(struct kft_device), M_DEVBUF, M_NOWAIT); if (!kd) continue; kd->kd_name = "dwlpx"; kd->kd_node = sc->sc_node; kd->kd_dtype = sc->sc_dtype; kd->kd_hosenum = hoseno; - device_add_child(dev, kd->kd_name, -1, kd); + child = device_add_child(dev, kd->kd_name, -1); + device_set_ivars(child, kd); } return 0; } static int kft_print_child(device_t bus, device_t dev) { struct kft_device *kd = (struct kft_device*) device_get_ivars(dev); int retval = 0; retval += bus_print_child_header(bus, dev); retval += printf(" on %s hose %d\n", device_get_nameunit(bus), kd->kd_hosenum); return (retval); } static int kft_read_ivar(device_t bus, device_t dev, int index, u_long* result) { struct kft_device *kd = (struct kft_device*) device_get_ivars(dev); switch (index) { case KFT_IVAR_NAME: *result = (u_long) kd->kd_name; return 0; case KFT_IVAR_NODE: *result = (u_long) kd->kd_node; return 0; case KFT_IVAR_DTYPE: *result = (u_long) kd->kd_dtype; return 0; case KFT_IVAR_HOSENUM: *result = (u_long) kd->kd_hosenum; return 0; default: return ENOENT; } } DRIVER_MODULE(kft, tlsb, kft_driver, kft_devclass, 0, 0); Index: head/sys/alpha/tlsb/tlsb.c =================================================================== --- head/sys/alpha/tlsb/tlsb.c (revision 54072) +++ head/sys/alpha/tlsb/tlsb.c (revision 54073) @@ -1,352 +1,353 @@ /* $FreeBSD$ */ /* $NetBSD: tlsb.c,v 1.10 1998/05/14 00:01:32 thorpej Exp $ */ /* * Copyright (c) 1997 by Matthew Jacob * NASA AMES Research Center. * All rights reserved. * * Based in part upon a prototype version by Jason Thorpe * Copyright (c) 1996 by Jason Thorpe. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice immediately at the beginning of the file, without modification, * this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Autoconfiguration and support routines for the TurboLaser System Bus * found on AlphaServer 8200 and 8400 systems. */ #include "opt_simos.h" #include #include #include #include #include #include #include #include #include #include /* #include "locators.h" */ extern int cputype; #define KV(_addr) ((caddr_t)ALPHA_PHYS_TO_K0SEG((_addr))) /* * The structure used to attach devices to the TurboLaser. */ struct tlsb_device { int td_node; /* node number */ u_int16_t td_dtype; /* device type */ u_int8_t td_swrev; /* software revision */ u_int8_t td_hwrev; /* hardware revision */ }; #define DEVTOTLSB(dev) ((struct tlsb_device*) device_get_ivars(dev)) struct intr_mapping { STAILQ_ENTRY(intr_mapping) queue; driver_intr_t* intr; void* arg; }; struct tlsb_softc { STAILQ_HEAD(, intr_mapping) intr_handlers; }; static char *tlsb_node_type_str(u_int32_t); static void tlsb_intr(void* frame, u_long vector); /* There can be only one. */ static int tlsb_found; static struct tlsb_softc* tlsb0_softc; static devclass_t tlsb_devclass; /* * Device methods */ static int tlsb_probe(device_t dev); static int tlsb_print_child(device_t dev, device_t child); static int tlsb_read_ivar(device_t dev, device_t child, int which, u_long* result); static int tlsb_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep); static int tlsb_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie); static device_method_t tlsb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tlsb_probe), DEVMETHOD(device_attach, bus_generic_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, tlsb_print_child), DEVMETHOD(bus_read_ivar, tlsb_read_ivar), DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), DEVMETHOD(bus_setup_intr, tlsb_setup_intr), DEVMETHOD(bus_teardown_intr, tlsb_teardown_intr), { 0, 0 } }; static driver_t tlsb_driver = { "tlsb", tlsb_methods, sizeof(struct tlsb_softc), }; /* * At 'probe' time, we add all the devices which we know about to the * bus. The generic attach routine will probe and attach them if they * are alive. */ static int tlsb_probe(device_t dev) { struct tlsb_softc* sc = device_get_softc(dev); struct tlsb_device *tdev; u_int32_t tldev; u_int8_t vid; int node; device_t child; device_set_desc(dev, "TurboLaser bus"); STAILQ_INIT(&sc->intr_handlers); tlsb0_softc = sc; set_iointr(tlsb_intr); printf("Probing for devices on the TurboLaser bus:\n"); tlsb_found = 1; /* * Attempt to find all devices on the bus, including * CPUs, memory modules, and I/O modules. */ /* * Sigh. I would like to just start off nicely, * but I need to treat I/O modules differently- * The highest priority I/O node has to be in * node #8, and I want to find it *first*, since * it will have the primary disks (most likely) * on it. */ /* * XXX dfr: I don't see why I need to do this */ for (node = 0; node <= TLSB_NODE_MAX; ++node) { /* * Check for invalid address. This may not really * be necessary, but what the heck... */ if (badaddr(TLSB_NODE_REG_ADDR(node, TLDEV), sizeof(u_int32_t))) continue; tldev = TLSB_GET_NODEREG(node, TLDEV); #ifdef SIMOS if (node != 0 && node != 8) continue; #endif if (tldev == 0) { /* Nothing at this node. */ continue; } #if 0 if (TLDEV_ISIOPORT(tldev)) continue; /* not interested right now */ #endif tdev = (struct tlsb_device*) malloc(sizeof(struct tlsb_device), M_DEVBUF, M_NOWAIT); if (!tdev) continue; tdev->td_node = node; #ifdef SIMOS if (node == 0) tdev->td_dtype = TLDEV_DTYPE_SCPU4; else if (node == 8) tdev->td_dtype = TLDEV_DTYPE_KFTIA; #else tdev->td_dtype = TLDEV_DTYPE(tldev); #endif tdev->td_swrev = TLDEV_SWREV(tldev); tdev->td_hwrev = TLDEV_HWREV(tldev); - child = device_add_child(dev, NULL, -1, tdev); + child = device_add_child(dev, NULL, -1); + device_set_ivars(child, tdev); device_set_desc(child, tlsb_node_type_str(tdev->td_dtype)); /* * Deal with hooking CPU instances to TurboLaser nodes. */ if (TLDEV_ISCPU(tldev)) { printf("%s%d node %d: %s", device_get_name(dev), device_get_unit(dev), node, tlsb_node_type_str(tldev)); /* * Hook in the first CPU unit. */ vid = (TLSB_GET_NODEREG(node, TLVID) & TLVID_VIDA_MASK) >> TLVID_VIDA_SHIFT; printf(", VID %d\n", vid); TLSB_PUT_NODEREG(node, TLCPUMASK, (1<td_node); return (retval); } static int tlsb_read_ivar(device_t dev, device_t child, int index, u_long* result) { struct tlsb_device* tdev = DEVTOTLSB(child); switch (index) { case TLSB_IVAR_NODE: *result = tdev->td_node; break; case TLSB_IVAR_DTYPE: *result = tdev->td_dtype; break; case TLSB_IVAR_SWREV: *result = tdev->td_swrev; break; case TLSB_IVAR_HWREV: *result = tdev->td_hwrev; break; } return ENOENT; } static int tlsb_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep) { struct tlsb_softc* sc = device_get_softc(dev); struct intr_mapping* i; i = malloc(sizeof(struct intr_mapping), M_DEVBUF, M_NOWAIT); if (!i) return ENOMEM; i->intr = intr; i->arg = arg; STAILQ_INSERT_TAIL(&sc->intr_handlers, i, queue); *cookiep = i; return 0; } static int tlsb_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie) { struct tlsb_softc* sc = device_get_softc(dev); struct intr_mapping* i = cookie; STAILQ_REMOVE(&sc->intr_handlers, i, intr_mapping, queue); free(i, M_DEVBUF); return 0; } static void tlsb_intr(void* frame, u_long vector) { struct tlsb_softc* sc = tlsb0_softc; struct intr_mapping* i; /* * XXX for SimOS, broadcast the interrupt. A real implementation * will decode the vector to extract node and host etc. */ for (i = STAILQ_FIRST(&sc->intr_handlers); i; i = STAILQ_NEXT(i, queue)) i->intr(i->arg); } DRIVER_MODULE(tlsb, root, tlsb_driver, tlsb_devclass, 0, 0); static char * tlsb_node_type_str(u_int32_t dtype) { static char tlsb_line[64]; switch (dtype & TLDEV_DTYPE_MASK) { case TLDEV_DTYPE_KFTHA: return ("KFTHA I/O interface"); case TLDEV_DTYPE_KFTIA: return ("KFTIA I/O interface"); case TLDEV_DTYPE_MS7CC: return ("MS7CC Memory Module"); case TLDEV_DTYPE_SCPU4: return ("Single CPU, 4MB cache"); case TLDEV_DTYPE_SCPU16: return ("Single CPU, 16MB cache"); case TLDEV_DTYPE_DCPU4: return ("Dual CPU, 4MB cache"); case TLDEV_DTYPE_DCPU16: return ("Dual CPU, 16MB cache"); default: bzero(tlsb_line, sizeof(tlsb_line)); snprintf(tlsb_line, sizeof(tlsb_line), "unknown, dtype 0x%x", dtype); return (tlsb_line); } /* NOTREACHED */ } Index: head/sys/alpha/tlsb/zs_tlsb.c =================================================================== --- head/sys/alpha/tlsb/zs_tlsb.c (revision 54072) +++ head/sys/alpha/tlsb/zs_tlsb.c (revision 54073) @@ -1,512 +1,512 @@ /*- * Copyright (c) 1998 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * This driver is a hopeless hack to get the SimOS console working. A real * driver would use the zs driver source from NetBSD. */ #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include /* XXX */ #include #include #define KV(_addr) ((caddr_t)ALPHA_PHYS_TO_K0SEG((_addr))) static int zsc_get_channel(device_t dev); static caddr_t zsc_get_base(device_t dev); struct zs_softc { struct tty tty; int channel; caddr_t base; }; #define ZS_SOFTC(unit) \ ((struct zs_softc*)devclass_get_softc(zs_devclass, (unit))) static d_open_t zsopen; static d_close_t zsclose; static d_ioctl_t zsioctl; #define CDEV_MAJOR 135 static struct cdevsw zs_cdevsw = { /* open */ zsopen, /* close */ zsclose, /* read */ ttyread, /* write */ ttywrite, /* ioctl */ zsioctl, /* poll */ ttypoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "zs", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; static void zsstart __P((struct tty *)); static int zsparam __P((struct tty *, struct termios *)); static void zsstop __P((struct tty *tp, int flag)); /* * Helpers for console support. */ int zs_cngetc __P((dev_t)); void zs_cnputc __P((dev_t, int)); static void zs_cnpollc __P((dev_t, int)); struct consdev zs_cons = { NULL, NULL, zs_cngetc, NULL, zs_cnputc, NULL, 0, CN_NORMAL, }; static caddr_t zs_console_addr; static int zs_console; static int zs_probe(device_t); static int zs_attach(device_t); static devclass_t zs_devclass; static devclass_t zsc_devclass; static device_method_t zs_methods[] = { /* Device interface */ DEVMETHOD(device_probe, zs_probe), DEVMETHOD(device_attach, zs_attach), { 0, 0 } }; static driver_t zs_driver = { "zs", zs_methods, sizeof(struct zs_softc), }; static int zs_probe(device_t dev) { return 0; } static int zs_attach(device_t dev) { struct zs_softc *sc = device_get_softc(dev); sc->channel = zsc_get_channel(dev); sc->base = zsc_get_base(dev); return 0; } static caddr_t zs_statusreg(caddr_t base, int chan) { if (chan == 0) return base + ZSC_CHANNELA + ZSC_STATUS; if (chan == 1) return base + ZSC_CHANNELB + ZSC_STATUS; panic("zs_statusreg: bogus channel"); } static caddr_t zs_datareg(caddr_t base, int chan) { if (chan == 0) return base + ZSC_CHANNELA + ZSC_DATA; if (chan == 1) return base + ZSC_CHANNELB + ZSC_DATA; panic("zs_statusreg: bogus channel"); } static int zs_get_status(caddr_t base, int chan) { return *(u_int32_t*) zs_statusreg(base, chan) & 0xff; } static void zs_put_status(caddr_t base, int chan, int v) { *(u_int32_t*) zs_statusreg(base, chan) = v; alpha_mb(); } static int zs_get_rr3(caddr_t base, int chan) { if (chan != 0) panic("zs_get_rr3: bad channel"); zs_put_status(base, chan, 3); return zs_get_status(base, chan); } static int zs_get_data(caddr_t base, int chan) { return *(u_int32_t*) zs_datareg(base, chan) & 0xff; } static void zs_put_data(caddr_t base, int chan, int v) { *(u_int32_t*) zs_datareg(base, chan) = v; alpha_mb(); } static int zs_getc(caddr_t base, int chan) { while (!(zs_get_status(base, chan) & 1)) DELAY(5); return zs_get_data(base, chan); } static void zs_putc(caddr_t base, int chan, int c) { while (!(zs_get_status(base, chan) & 4)) DELAY(5); zs_put_data(base, chan, c); } extern struct consdev* cn_tab; int zs_cnattach(vm_offset_t base, vm_offset_t offset) { zs_console_addr = (caddr_t) ALPHA_PHYS_TO_K0SEG(base + offset); zs_console = 1; zs_cons.cn_dev = makedev(CDEV_MAJOR, 0); cn_tab = &zs_cons; return 0; } int zs_cngetc(dev_t dev) { int s = spltty(); int c = zs_getc(zs_console_addr, minor(dev)); splx(s); return c; } void zs_cnputc(dev_t dev, int c) { int s = spltty(); zs_putc(zs_console_addr, minor(dev), c); splx(s); } static void zs_cnpollc(dev_t dev, int onoff) { } static int zsopen(dev_t dev, int flag, int mode, struct proc *p) { struct zs_softc* sc = ZS_SOFTC(minor(dev)); struct tty *tp; int s; int error = 0; if (!sc) return ENXIO; s = spltty(); tp = &sc->tty; dev->si_tty = tp; tp->t_oproc = zsstart; tp->t_param = zsparam; tp->t_stop = zsstop; tp->t_dev = dev; if ((tp->t_state & TS_ISOPEN) == 0) { tp->t_state |= TS_CARR_ON; ttychars(tp); tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_cflag = TTYDEF_CFLAG|CLOCAL; tp->t_lflag = TTYDEF_LFLAG; tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; ttsetwater(tp); } else if (tp->t_state & TS_XCLUDE && suser(p)) { splx(s); return EBUSY; } splx(s); error = (*linesw[tp->t_line].l_open)(dev, tp); return error; } static int zsclose(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp = &ZS_SOFTC(minor(dev))->tty; (*linesw[tp->t_line].l_close)(tp, flag); ttyclose(tp); return 0; } static int zsioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { struct tty *tp = &ZS_SOFTC(minor(dev))->tty; int error; error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error != ENOIOCTL) return error; error = ttioctl(tp, cmd, data, flag); if (error != ENOIOCTL) return error; return ENOTTY; } static int zsparam(struct tty *tp, struct termios *t) { return 0; } static void zsstart(struct tty *tp) { struct zs_softc* sc = (struct zs_softc*) tp; int s; s = spltty(); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { ttwwakeup(tp); splx(s); return; } tp->t_state |= TS_BUSY; while (tp->t_outq.c_cc != 0) zs_putc(sc->base, minor(tp->t_dev), getc(&tp->t_outq)); tp->t_state &= ~TS_BUSY; ttwwakeup(tp); splx(s); } /* * Stop output on a line. */ static void zsstop(struct tty *tp, int flag) { int s; s = spltty(); if (tp->t_state & TS_BUSY) if ((tp->t_state & TS_TTSTOP) == 0) tp->t_state |= TS_FLUSH; splx(s); } DRIVER_MODULE(zs, zsc, zs_driver, zs_devclass, 0, 0); /* * The zsc bus holds two zs devices, one for channel A, one for channel B. */ struct zsc_softc { caddr_t base; struct zs_softc* sc_a; struct zs_softc* sc_b; void *intr; }; static int zsc_tlsb_probe(device_t dev); static int zsc_tlsb_attach(device_t dev); static int zsc_tlsb_print_child(device_t dev, device_t child); static driver_intr_t zsc_tlsb_intr; static device_method_t zsc_tlsb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, zsc_tlsb_probe), DEVMETHOD(device_attach, zsc_tlsb_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, zsc_tlsb_print_child), DEVMETHOD(bus_read_ivar, bus_generic_read_ivar), DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t zsc_tlsb_driver = { "zsc", zsc_tlsb_methods, sizeof(struct zsc_softc), }; static int zsc_get_channel(device_t dev) { return (long) device_get_ivars(dev); } static caddr_t zsc_get_base(device_t dev) { device_t bus = device_get_parent(dev); struct zsc_softc* sc = device_get_softc(bus); return sc->base; } static int zsc_tlsb_probe(device_t dev) { struct zsc_softc* sc = device_get_softc(dev); device_set_desc(dev, "Z8530 uart"); sc->base = (caddr_t) ALPHA_PHYS_TO_K0SEG(TLSB_GBUS_BASE + gbus_get_offset(dev)); /* * Add channel A and channel B */ - device_add_child(dev, "zs", 0, (void*) 0); - device_add_child(dev, "zs", 1, (void*) 0); + device_add_child(dev, "zs", 0); + device_add_child(dev, "zs", 1); return 0; } static int zsc_tlsb_attach(device_t dev) { struct zsc_softc* sc = device_get_softc(dev); device_t parent = device_get_parent(dev); void *ih; cdevsw_add(&zs_cdevsw); bus_generic_attach(dev); /* XXX */ sc->sc_a = ZS_SOFTC(0); sc->sc_b = ZS_SOFTC(1); /* XXX should use resource argument to communicate vector */ return BUS_SETUP_INTR(parent, dev, NULL, INTR_TYPE_TTY, zsc_tlsb_intr, sc, &sc->intr); return 0; } static int zsc_tlsb_print_child(device_t bus, device_t dev) { int retval = 0; retval += bus_print_child_header(bus, dev); retval += printf(" on %s channel %c\n", device_get_nameunit(bus), 'A' + (device_get_unit(dev) & 1)); return (retval); } static void zsc_tlsb_intr(void* arg) { struct zsc_softc* sc = arg; caddr_t base = sc->base; int rr3 = zs_get_rr3(base, 0); if (rr3 & 0x20) { struct tty* tp = &sc->sc_a->tty; int c; while (zs_get_status(base, 0) & 1) { c = zs_get_data(base, 0); #ifdef DDB if (c == CTRL('\\')) Debugger("manual escape to debugger"); #endif if (tp->t_state & TS_ISOPEN) (*linesw[tp->t_line].l_rint)(c, tp); DELAY(5); } } if (rr3 & 0x04) { struct tty* tp = &sc->sc_b->tty; int c; while (zs_get_status(base, 1) & 1) { c = zs_get_data(base, 1); #ifdef DDB if (c == CTRL('\\')) Debugger("manual escape to debugger"); #endif if (tp->t_state & TS_ISOPEN) (*linesw[tp->t_line].l_rint)(c, tp); DELAY(5); } } } DRIVER_MODULE(zsc, gbus, zsc_tlsb_driver, zsc_devclass, 0, 0); Index: head/sys/amd64/amd64/autoconf.c =================================================================== --- head/sys/amd64/amd64/autoconf.c (revision 54072) +++ head/sys/amd64/amd64/autoconf.c (revision 54073) @@ -1,322 +1,322 @@ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)autoconf.c 7.1 (Berkeley) 5/9/91 * $FreeBSD$ */ /* * Setup the system to run on the current machine. * * Configure() is called at boot time and initializes the vba * device tables and the memory controller monitoring. Available * devices are determined (from possibilities mentioned in ioconf.c), * and the drivers are initialized. */ #include "opt_bootp.h" #include "opt_ffs.h" #include "opt_cd9660.h" #include "opt_nfs.h" #include "opt_nfsroot.h" #include "opt_bus.h" #include "opt_rootdevname.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef APIC_IO #include #else #include #endif /* APIC_IO */ #include "isa.h" #include device_t isa_bus_device = 0; static void configure_first __P((void *)); static void configure __P((void *)); static void configure_final __P((void *)); #if defined(FFS) && defined(FFS_ROOT) static void setroot __P((void)); #endif SYSINIT(configure1, SI_SUB_CONFIGURE, SI_ORDER_FIRST, configure_first, NULL); /* SI_ORDER_SECOND is hookable */ SYSINIT(configure2, SI_SUB_CONFIGURE, SI_ORDER_THIRD, configure, NULL); /* SI_ORDER_MIDDLE is hookable */ SYSINIT(configure3, SI_SUB_CONFIGURE, SI_ORDER_ANY, configure_final, NULL); dev_t rootdev = NODEV; dev_t dumpdev = NODEV; device_t nexus_dev; /* * Determine i/o configuration for a machine. */ static void configure_first(dummy) void *dummy; { } static void configure(dummy) void *dummy; { /* * Activate the ICU's. Note that we are explicitly at splhigh() * at present as we have no way to disable stray PCI level triggered * interrupts until the devices have had a driver attached. This * is particularly a problem when the interrupts are shared. For * example, if IRQ 10 is shared between a disk and network device * and the disk device generates an interrupt, if we "activate" * IRQ 10 when the network driver is set up, then we will get * recursive interrupt 10's as nothing will know how to turn off * the disk device's interrupt. * * Having the ICU's active means we can probe interrupt routing to * see if a device causes the corresponding pending bit to be set. * * This is all rather inconvenient. */ #ifdef APIC_IO bsp_apic_configure(); enable_intr(); #else enable_intr(); INTREN(IRQ_SLAVE); #endif /* APIC_IO */ /* nexus0 is the top of the i386 device tree */ - device_add_child(root_bus, "nexus", 0, 0); + device_add_child(root_bus, "nexus", 0); /* initialize new bus architecture */ root_bus_configure(); #if NISA > 0 /* * Explicitly probe and attach ISA last. The isa bus saves * it's device node at attach time for us here. */ if (isa_bus_device) isa_probe_children(isa_bus_device); #endif /* * Now we're ready to handle (pending) interrupts. * XXX this is slightly misplaced. */ spl0(); /* * Allow lowering of the ipl to the lowest kernel level if we * panic (or call tsleep() before clearing `cold'). No level is * completely safe (since a panic may occur in a critical region * at splhigh()), but we want at least bio interrupts to work. */ safepri = cpl; } static void configure_final(dummy) void *dummy; { int i; cninit_finish(); if (bootverbose) { #ifdef APIC_IO imen_dump(); #endif /* APIC_IO */ /* * Print out the BIOS's idea of the disk geometries. */ printf("BIOS Geometries:\n"); for (i = 0; i < N_BIOS_GEOM; i++) { unsigned long bios_geom; int max_cylinder, max_head, max_sector; bios_geom = bootinfo.bi_bios_geom[i]; /* * XXX the bootstrap punts a 1200K floppy geometry * when the get-disk-geometry interrupt fails. Skip * drives that have this geometry. */ if (bios_geom == 0x4f010f) continue; printf(" %x:%08lx ", i, bios_geom); max_cylinder = bios_geom >> 16; max_head = (bios_geom >> 8) & 0xff; max_sector = bios_geom & 0xff; printf( "0..%d=%d cylinders, 0..%d=%d heads, 1..%d=%d sectors\n", max_cylinder, max_cylinder + 1, max_head, max_head + 1, max_sector, max_sector); } printf(" %d accounted for\n", bootinfo.bi_n_bios_used); printf("Device configuration finished.\n"); } cold = 0; } #ifdef BOOTP extern void bootpc_init(void); #endif /* * Do legacy root filesystem discovery. */ void cpu_rootconf() { #ifdef BOOTP bootpc_init(); #endif #if defined(NFS) && defined(NFS_ROOT) #if !defined(BOOTP_NFSROOT) if (nfs_diskless_valid) #endif rootdevnames[0] = "nfs:"; #endif #if defined(FFS) && defined(FFS_ROOT) if (!rootdevnames[0]) setroot(); #endif } SYSINIT(cpu_rootconf, SI_SUB_ROOT_CONF, SI_ORDER_FIRST, cpu_rootconf, NULL) u_long bootdev = 0; /* not a dev_t - encoding is different */ #if defined(FFS) && defined(FFS_ROOT) #define FDMAJOR 2 #define FDUNITSHIFT 6 /* * Attempt to find the device from which we were booted. * If we can do so, and not instructed not to do so, * set rootdevs[] and rootdevnames[] to correspond to the * boot device(s). * * This code survives in order to allow the system to be * booted from legacy environments that do not correctly * populate the kernel environment. There are significant * restrictions on the bootability of the system in this * situation; it can only be mounting root from a 'da' * 'wd' or 'fd' device, and the root filesystem must be ufs. */ static void setroot() { int majdev, mindev, unit, slice, part; dev_t newrootdev, dev; char partname[2]; char *sname; if ((bootdev & B_MAGICMASK) != B_DEVMAGIC) { printf("no B_DEVMAGIC (bootdev=%#lx)\n", bootdev); return; } majdev = B_TYPE(bootdev); dev = makebdev(majdev, 0); if (devsw(dev) == NULL) { printf("no devsw (majdev=%d bootdev=%#lx)\n", majdev, bootdev); return; } unit = B_UNIT(bootdev); slice = B_SLICE(bootdev); if (slice == WHOLE_DISK_SLICE) slice = COMPATIBILITY_SLICE; if (slice < 0 || slice >= MAX_SLICES) { printf("bad slice\n"); return; } /* * XXX kludge for inconsistent unit numbering and lack of slice * support for floppies. */ if (majdev == FDMAJOR) { slice = COMPATIBILITY_SLICE; part = RAW_PART; mindev = unit << FDUNITSHIFT; } else { part = B_PARTITION(bootdev); mindev = dkmakeminor(unit, slice, part); } newrootdev = makebdev(majdev, mindev); sname = dsname(newrootdev, unit, slice, part, partname); rootdevnames[0] = malloc(strlen(sname) + 6, M_DEVBUF, M_NOWAIT); sprintf(rootdevnames[0], "ufs:%s%s", sname, partname); /* * For properly dangerously dedicated disks (ones with a historical * bogus partition table), the boot blocks will give slice = 4, but * the kernel will only provide the compatibility slice since it * knows that slice 4 is not a real slice. Arrange to try mounting * the compatibility slice as root if mounting the slice passed by * the boot blocks fails. This handles the dangerously dedicated * case and perhaps others. */ if (slice == COMPATIBILITY_SLICE) return; slice = COMPATIBILITY_SLICE; sname = dsname(newrootdev, unit, slice, part, partname); rootdevnames[1] = malloc(strlen(sname) + 6, M_DEVBUF, M_NOWAIT); sprintf(rootdevnames[1], "ufs:%s%s", sname, partname); } #endif Index: head/sys/amd64/amd64/legacy.c =================================================================== --- head/sys/amd64/amd64/legacy.c (revision 54072) +++ head/sys/amd64/amd64/legacy.c (revision 54073) @@ -1,415 +1,416 @@ /* * Copyright 1998 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * This code implements a `root nexus' for Intel Architecture * machines. The function of the root nexus is to serve as an * attachment point for both processors and buses, and to manage * resources which are common to all of them. In particular, * this code implements the core resource managers for interrupt * requests, DMA requests (which rightfully should be a part of the * ISA code but it's easier to do it here for now), I/O port addresses, * and I/O memory address space. */ #include "opt_smp.h" #include "mca.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef APIC_IO #include #include #endif #ifdef PC98 #include #else #include #endif #include static struct rman irq_rman, drq_rman, port_rman, mem_rman; static int nexus_probe(device_t); static int nexus_attach(device_t); static int nexus_print_child(device_t, device_t); static device_t nexus_add_child(device_t bus, int order, const char *name, int unit); static struct resource *nexus_alloc_resource(device_t, device_t, int, int *, u_long, u_long, u_long, u_int); static int nexus_activate_resource(device_t, device_t, int, int, struct resource *); static int nexus_deactivate_resource(device_t, device_t, int, int, struct resource *); static int nexus_release_resource(device_t, device_t, int, int, struct resource *); static int nexus_setup_intr(device_t, device_t, struct resource *, int flags, void (*)(void *), void *, void **); static int nexus_teardown_intr(device_t, device_t, struct resource *, void *); static device_method_t nexus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, nexus_probe), DEVMETHOD(device_attach, nexus_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, nexus_print_child), DEVMETHOD(bus_add_child, nexus_add_child), DEVMETHOD(bus_read_ivar, bus_generic_read_ivar), DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), DEVMETHOD(bus_alloc_resource, nexus_alloc_resource), DEVMETHOD(bus_release_resource, nexus_release_resource), DEVMETHOD(bus_activate_resource, nexus_activate_resource), DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource), DEVMETHOD(bus_setup_intr, nexus_setup_intr), DEVMETHOD(bus_teardown_intr, nexus_teardown_intr), { 0, 0 } }; static driver_t nexus_driver = { "nexus", nexus_methods, 1, /* no softc */ }; static devclass_t nexus_devclass; DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); #ifdef APIC_IO #define LASTIRQ (NINTR - 1) #else #define LASTIRQ 15 #endif static int nexus_probe(device_t dev) { device_quiet(dev); /* suppress attach message for neatness */ /* * IRQ's are on the mainboard on old systems, but on the ISA part * of PCI->ISA bridges. There would be multiple sets of IRQs on * multi-ISA-bus systems. PCI interrupts are routed to the ISA * component, so in a way, PCI can be a partial child of an ISA bus(!). * APIC interrupts are global though. */ irq_rman.rm_start = 0; irq_rman.rm_end = LASTIRQ; irq_rman.rm_type = RMAN_ARRAY; irq_rman.rm_descr = "Interrupt request lines"; if (rman_init(&irq_rman) || rman_manage_region(&irq_rman, 0, 1) || rman_manage_region(&irq_rman, 3, LASTIRQ)) panic("nexus_probe irq_rman"); /* * ISA DMA on PCI systems is implemented in the ISA part of each * PCI->ISA bridge and the channels can be duplicated if there are * multiple bridges. (eg: laptops with docking stations) */ drq_rman.rm_start = 0; drq_rman.rm_end = 7; drq_rman.rm_type = RMAN_ARRAY; drq_rman.rm_descr = "DMA request lines"; /* XXX drq 0 not available on some machines */ if (rman_init(&drq_rman) || rman_manage_region(&drq_rman, 0, 7)) panic("nexus_probe drq_rman"); /* * However, IO ports and Memory truely are global at this level, * as are APIC interrupts (however many IO APICS there turn out * to be on large systems..) */ port_rman.rm_start = 0; port_rman.rm_end = 0xffff; port_rman.rm_type = RMAN_ARRAY; port_rman.rm_descr = "I/O ports"; if (rman_init(&port_rman) || rman_manage_region(&port_rman, 0, 0xffff)) panic("nexus_probe port_rman"); mem_rman.rm_start = 0; mem_rman.rm_end = ~0u; mem_rman.rm_type = RMAN_ARRAY; mem_rman.rm_descr = "I/O memory addresses"; if (rman_init(&mem_rman) || rman_manage_region(&mem_rman, 0, ~0)) panic("nexus_probe mem_rman"); return bus_generic_probe(dev); } static int nexus_attach(device_t dev) { device_t child; /* * First, deal with the children we know about already */ bus_generic_attach(dev); /* * And if we didn't see EISA or ISA on a pci bridge, create some * connection points now so they show up "on motherboard". */ if (!devclass_get_device(devclass_find("eisa"), 0)) { - child = device_add_child(dev, "eisa", 0, 0); + child = device_add_child(dev, "eisa", 0); if (child == NULL) panic("nexus_attach eisa"); device_probe_and_attach(child); } #if NMCA > 0 if (!devclass_get_device(devclass_find("mca"), 0)) { - child = device_add_child(dev, "mca", 0, 0); + child = device_add_child(dev, "mca", 0); if (child == 0) panic("nexus_probe mca"); device_probe_and_attach(child); } #endif if (!devclass_get_device(devclass_find("isa"), 0)) { - child = device_add_child(dev, "isa", 0, 0); + child = device_add_child(dev, "isa", 0); if (child == NULL) panic("nexus_attach isa"); device_probe_and_attach(child); } + return 0; } static int nexus_print_child(device_t bus, device_t child) { int retval = 0; retval += bus_print_child_header(bus, child); retval += printf(" on motherboard\n"); return (retval); } static device_t nexus_add_child(device_t bus, int order, const char *name, int unit) { - return device_add_child_ordered(bus, order, name, unit, 0); + return device_add_child_ordered(bus, order, name, unit); } /* * Allocate a resource on behalf of child. NB: child is usually going to be a * child of one of our descendants, not a direct child of nexus0. * (Exceptions include npx.) */ static struct resource * nexus_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct resource *rv; struct rman *rm; int needactivate = flags & RF_ACTIVE; flags &= ~RF_ACTIVE; switch (type) { case SYS_RES_IRQ: rm = &irq_rman; break; case SYS_RES_DRQ: rm = &drq_rman; break; case SYS_RES_IOPORT: rm = &port_rman; break; case SYS_RES_MEMORY: rm = &mem_rman; break; default: return 0; } rv = rman_reserve_resource(rm, start, end, count, flags, child); if (rv == 0) return 0; if (type == SYS_RES_MEMORY) { caddr_t vaddr = 0; if (rv->r_end < 1024 * 1024) { /* * The first 1Mb is mapped at KERNBASE. */ vaddr = (caddr_t)(uintptr_t)(KERNBASE + rv->r_start); } else { u_int32_t paddr; u_int32_t psize; u_int32_t poffs; paddr = rv->r_start; psize = rv->r_end - rv->r_start; poffs = paddr - trunc_page(paddr); vaddr = (caddr_t) pmap_mapdev(paddr-poffs, psize+poffs) + poffs; } rman_set_virtual(rv, vaddr); rman_set_bustag(rv, I386_BUS_SPACE_MEM); rman_set_bushandle(rv, (bus_space_handle_t) vaddr); } else if (type == SYS_RES_IOPORT) { rman_set_bustag(rv, I386_BUS_SPACE_IO); rman_set_bushandle(rv, rv->r_start); } if (needactivate) { if (bus_activate_resource(child, type, *rid, rv)) { rman_release_resource(rv); return 0; } } return rv; } static int nexus_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { return (rman_activate_resource(r)); } static int nexus_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { return (rman_deactivate_resource(r)); } static int nexus_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { if (r->r_flags & RF_ACTIVE) { int error = bus_deactivate_resource(child, type, rid, r); if (error) return error; } return (rman_release_resource(r)); } /* * Currently this uses the really grody interface from kern/kern_intr.c * (which really doesn't belong in kern/anything.c). Eventually, all of * the code in kern_intr.c and machdep_intr.c should get moved here, since * this is going to be the official interface. */ static int nexus_setup_intr(device_t bus, device_t child, struct resource *irq, int flags, void (*ihand)(void *), void *arg, void **cookiep) { intrmask_t *mask; driver_t *driver; int error, icflags; /* somebody tried to setup an irq that failed to allocate! */ if (irq == NULL) panic("nexus_setup_intr: NULL irq resource!"); *cookiep = 0; if (irq->r_flags & RF_SHAREABLE) icflags = 0; else icflags = INTR_EXCL; driver = device_get_driver(child); switch (flags) { case INTR_TYPE_TTY: mask = &tty_imask; break; case (INTR_TYPE_TTY | INTR_TYPE_FAST): mask = &tty_imask; icflags |= INTR_FAST; break; case INTR_TYPE_BIO: mask = &bio_imask; break; case INTR_TYPE_NET: mask = &net_imask; break; case INTR_TYPE_CAM: mask = &cam_imask; break; case INTR_TYPE_MISC: mask = 0; break; default: panic("still using grody create_intr interface"); } /* * We depend here on rman_activate_resource() being idempotent. */ error = rman_activate_resource(irq); if (error) return (error); *cookiep = inthand_add(device_get_nameunit(child), irq->r_start, ihand, arg, mask, icflags); if (*cookiep == NULL) error = EINVAL; /* XXX ??? */ return (error); } static int nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih) { return (inthand_remove(ih)); } Index: head/sys/amd64/amd64/nexus.c =================================================================== --- head/sys/amd64/amd64/nexus.c (revision 54072) +++ head/sys/amd64/amd64/nexus.c (revision 54073) @@ -1,415 +1,416 @@ /* * Copyright 1998 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * This code implements a `root nexus' for Intel Architecture * machines. The function of the root nexus is to serve as an * attachment point for both processors and buses, and to manage * resources which are common to all of them. In particular, * this code implements the core resource managers for interrupt * requests, DMA requests (which rightfully should be a part of the * ISA code but it's easier to do it here for now), I/O port addresses, * and I/O memory address space. */ #include "opt_smp.h" #include "mca.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef APIC_IO #include #include #endif #ifdef PC98 #include #else #include #endif #include static struct rman irq_rman, drq_rman, port_rman, mem_rman; static int nexus_probe(device_t); static int nexus_attach(device_t); static int nexus_print_child(device_t, device_t); static device_t nexus_add_child(device_t bus, int order, const char *name, int unit); static struct resource *nexus_alloc_resource(device_t, device_t, int, int *, u_long, u_long, u_long, u_int); static int nexus_activate_resource(device_t, device_t, int, int, struct resource *); static int nexus_deactivate_resource(device_t, device_t, int, int, struct resource *); static int nexus_release_resource(device_t, device_t, int, int, struct resource *); static int nexus_setup_intr(device_t, device_t, struct resource *, int flags, void (*)(void *), void *, void **); static int nexus_teardown_intr(device_t, device_t, struct resource *, void *); static device_method_t nexus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, nexus_probe), DEVMETHOD(device_attach, nexus_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, nexus_print_child), DEVMETHOD(bus_add_child, nexus_add_child), DEVMETHOD(bus_read_ivar, bus_generic_read_ivar), DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), DEVMETHOD(bus_alloc_resource, nexus_alloc_resource), DEVMETHOD(bus_release_resource, nexus_release_resource), DEVMETHOD(bus_activate_resource, nexus_activate_resource), DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource), DEVMETHOD(bus_setup_intr, nexus_setup_intr), DEVMETHOD(bus_teardown_intr, nexus_teardown_intr), { 0, 0 } }; static driver_t nexus_driver = { "nexus", nexus_methods, 1, /* no softc */ }; static devclass_t nexus_devclass; DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); #ifdef APIC_IO #define LASTIRQ (NINTR - 1) #else #define LASTIRQ 15 #endif static int nexus_probe(device_t dev) { device_quiet(dev); /* suppress attach message for neatness */ /* * IRQ's are on the mainboard on old systems, but on the ISA part * of PCI->ISA bridges. There would be multiple sets of IRQs on * multi-ISA-bus systems. PCI interrupts are routed to the ISA * component, so in a way, PCI can be a partial child of an ISA bus(!). * APIC interrupts are global though. */ irq_rman.rm_start = 0; irq_rman.rm_end = LASTIRQ; irq_rman.rm_type = RMAN_ARRAY; irq_rman.rm_descr = "Interrupt request lines"; if (rman_init(&irq_rman) || rman_manage_region(&irq_rman, 0, 1) || rman_manage_region(&irq_rman, 3, LASTIRQ)) panic("nexus_probe irq_rman"); /* * ISA DMA on PCI systems is implemented in the ISA part of each * PCI->ISA bridge and the channels can be duplicated if there are * multiple bridges. (eg: laptops with docking stations) */ drq_rman.rm_start = 0; drq_rman.rm_end = 7; drq_rman.rm_type = RMAN_ARRAY; drq_rman.rm_descr = "DMA request lines"; /* XXX drq 0 not available on some machines */ if (rman_init(&drq_rman) || rman_manage_region(&drq_rman, 0, 7)) panic("nexus_probe drq_rman"); /* * However, IO ports and Memory truely are global at this level, * as are APIC interrupts (however many IO APICS there turn out * to be on large systems..) */ port_rman.rm_start = 0; port_rman.rm_end = 0xffff; port_rman.rm_type = RMAN_ARRAY; port_rman.rm_descr = "I/O ports"; if (rman_init(&port_rman) || rman_manage_region(&port_rman, 0, 0xffff)) panic("nexus_probe port_rman"); mem_rman.rm_start = 0; mem_rman.rm_end = ~0u; mem_rman.rm_type = RMAN_ARRAY; mem_rman.rm_descr = "I/O memory addresses"; if (rman_init(&mem_rman) || rman_manage_region(&mem_rman, 0, ~0)) panic("nexus_probe mem_rman"); return bus_generic_probe(dev); } static int nexus_attach(device_t dev) { device_t child; /* * First, deal with the children we know about already */ bus_generic_attach(dev); /* * And if we didn't see EISA or ISA on a pci bridge, create some * connection points now so they show up "on motherboard". */ if (!devclass_get_device(devclass_find("eisa"), 0)) { - child = device_add_child(dev, "eisa", 0, 0); + child = device_add_child(dev, "eisa", 0); if (child == NULL) panic("nexus_attach eisa"); device_probe_and_attach(child); } #if NMCA > 0 if (!devclass_get_device(devclass_find("mca"), 0)) { - child = device_add_child(dev, "mca", 0, 0); + child = device_add_child(dev, "mca", 0); if (child == 0) panic("nexus_probe mca"); device_probe_and_attach(child); } #endif if (!devclass_get_device(devclass_find("isa"), 0)) { - child = device_add_child(dev, "isa", 0, 0); + child = device_add_child(dev, "isa", 0); if (child == NULL) panic("nexus_attach isa"); device_probe_and_attach(child); } + return 0; } static int nexus_print_child(device_t bus, device_t child) { int retval = 0; retval += bus_print_child_header(bus, child); retval += printf(" on motherboard\n"); return (retval); } static device_t nexus_add_child(device_t bus, int order, const char *name, int unit) { - return device_add_child_ordered(bus, order, name, unit, 0); + return device_add_child_ordered(bus, order, name, unit); } /* * Allocate a resource on behalf of child. NB: child is usually going to be a * child of one of our descendants, not a direct child of nexus0. * (Exceptions include npx.) */ static struct resource * nexus_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct resource *rv; struct rman *rm; int needactivate = flags & RF_ACTIVE; flags &= ~RF_ACTIVE; switch (type) { case SYS_RES_IRQ: rm = &irq_rman; break; case SYS_RES_DRQ: rm = &drq_rman; break; case SYS_RES_IOPORT: rm = &port_rman; break; case SYS_RES_MEMORY: rm = &mem_rman; break; default: return 0; } rv = rman_reserve_resource(rm, start, end, count, flags, child); if (rv == 0) return 0; if (type == SYS_RES_MEMORY) { caddr_t vaddr = 0; if (rv->r_end < 1024 * 1024) { /* * The first 1Mb is mapped at KERNBASE. */ vaddr = (caddr_t)(uintptr_t)(KERNBASE + rv->r_start); } else { u_int32_t paddr; u_int32_t psize; u_int32_t poffs; paddr = rv->r_start; psize = rv->r_end - rv->r_start; poffs = paddr - trunc_page(paddr); vaddr = (caddr_t) pmap_mapdev(paddr-poffs, psize+poffs) + poffs; } rman_set_virtual(rv, vaddr); rman_set_bustag(rv, I386_BUS_SPACE_MEM); rman_set_bushandle(rv, (bus_space_handle_t) vaddr); } else if (type == SYS_RES_IOPORT) { rman_set_bustag(rv, I386_BUS_SPACE_IO); rman_set_bushandle(rv, rv->r_start); } if (needactivate) { if (bus_activate_resource(child, type, *rid, rv)) { rman_release_resource(rv); return 0; } } return rv; } static int nexus_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { return (rman_activate_resource(r)); } static int nexus_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { return (rman_deactivate_resource(r)); } static int nexus_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { if (r->r_flags & RF_ACTIVE) { int error = bus_deactivate_resource(child, type, rid, r); if (error) return error; } return (rman_release_resource(r)); } /* * Currently this uses the really grody interface from kern/kern_intr.c * (which really doesn't belong in kern/anything.c). Eventually, all of * the code in kern_intr.c and machdep_intr.c should get moved here, since * this is going to be the official interface. */ static int nexus_setup_intr(device_t bus, device_t child, struct resource *irq, int flags, void (*ihand)(void *), void *arg, void **cookiep) { intrmask_t *mask; driver_t *driver; int error, icflags; /* somebody tried to setup an irq that failed to allocate! */ if (irq == NULL) panic("nexus_setup_intr: NULL irq resource!"); *cookiep = 0; if (irq->r_flags & RF_SHAREABLE) icflags = 0; else icflags = INTR_EXCL; driver = device_get_driver(child); switch (flags) { case INTR_TYPE_TTY: mask = &tty_imask; break; case (INTR_TYPE_TTY | INTR_TYPE_FAST): mask = &tty_imask; icflags |= INTR_FAST; break; case INTR_TYPE_BIO: mask = &bio_imask; break; case INTR_TYPE_NET: mask = &net_imask; break; case INTR_TYPE_CAM: mask = &cam_imask; break; case INTR_TYPE_MISC: mask = 0; break; default: panic("still using grody create_intr interface"); } /* * We depend here on rman_activate_resource() being idempotent. */ error = rman_activate_resource(irq); if (error) return (error); *cookiep = inthand_add(device_get_nameunit(child), irq->r_start, ihand, arg, mask, icflags); if (*cookiep == NULL) error = EINVAL; /* XXX ??? */ return (error); } static int nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih) { return (inthand_remove(ih)); } Index: head/sys/amd64/pci/pci_bus.c =================================================================== --- head/sys/amd64/pci/pci_bus.c (revision 54072) +++ head/sys/amd64/pci/pci_bus.c (revision 54073) @@ -1,514 +1,514 @@ /* * Copyright (c) 1997, Stefan Esser * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * */ #include #include #include #include #include #include #include #ifdef PCI_COMPAT /* XXX this is a terrible hack, which keeps the Tekram AMD SCSI driver happy */ #define cfgmech pci_mechanism int cfgmech; #else static int cfgmech; #endif /* PCI_COMPAT */ static int devmax; /* enable configuration space accesses and return data port address */ static int pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) { int dataport = 0; if (bus <= PCI_BUSMAX && slot < devmax && func <= PCI_FUNCMAX && reg <= PCI_REGMAX && bytes != 3 && (unsigned) bytes <= 4 && (reg & (bytes -1)) == 0) { switch (cfgmech) { case 1: outl(CONF1_ADDR_PORT, (1 << 31) | (bus << 16) | (slot << 11) | (func << 8) | (reg & ~0x03)); dataport = CONF1_DATA_PORT + (reg & 0x03); break; case 2: outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); outb(CONF2_FORWARD_PORT, bus); dataport = 0xc000 | (slot << 8) | reg; break; } } return (dataport); } /* disable configuration space accesses */ static void pci_cfgdisable(void) { switch (cfgmech) { case 1: outl(CONF1_ADDR_PORT, 0); break; case 2: outb(CONF2_ENABLE_PORT, 0); outb(CONF2_FORWARD_PORT, 0); break; } } /* read configuration space register */ int pci_cfgread(pcicfgregs *cfg, int reg, int bytes) { int data = -1; int port; port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); if (port != 0) { switch (bytes) { case 1: data = inb(port); break; case 2: data = inw(port); break; case 4: data = inl(port); break; } pci_cfgdisable(); } return (data); } /* write configuration space register */ void pci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes) { int port; port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); if (port != 0) { switch (bytes) { case 1: outb(port, data); break; case 2: outw(port, data); break; case 4: outl(port, data); break; } pci_cfgdisable(); } } /* check whether the configuration mechanism has been correct identified */ static int pci_cfgcheck(int maxdev) { u_char device; if (bootverbose) printf("pci_cfgcheck:\tdevice "); for (device = 0; device < maxdev; device++) { unsigned id, class, header; if (bootverbose) printf("%d ", device); id = inl(pci_cfgenable(0, device, 0, 0, 4)); if (id == 0 || id == -1) continue; class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8; if (bootverbose) printf("[class=%06x] ", class); if (class == 0 || (class & 0xf870ff) != 0) continue; header = inb(pci_cfgenable(0, device, 0, 14, 1)); if (bootverbose) printf("[hdr=%02x] ", header); if ((header & 0x7e) != 0) continue; if (bootverbose) printf("is there (id=%08x)\n", id); pci_cfgdisable(); return (1); } if (bootverbose) printf("-- nothing found\n"); pci_cfgdisable(); return (0); } static int pci_cfgopen(void) { unsigned long mode1res,oldval1; unsigned char mode2res,oldval2; oldval1 = inl(CONF1_ADDR_PORT); if (bootverbose) { printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", oldval1); } if ((oldval1 & CONF1_ENABLE_MSK) == 0) { cfgmech = 1; devmax = 32; outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); outb(CONF1_ADDR_PORT +3, 0); mode1res = inl(CONF1_ADDR_PORT); outl(CONF1_ADDR_PORT, oldval1); if (bootverbose) printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n", mode1res, CONF1_ENABLE_CHK); if (mode1res) { if (pci_cfgcheck(32)) return (cfgmech); } outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); mode1res = inl(CONF1_ADDR_PORT); outl(CONF1_ADDR_PORT, oldval1); if (bootverbose) printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n", mode1res, CONF1_ENABLE_CHK1); if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { if (pci_cfgcheck(32)) return (cfgmech); } } oldval2 = inb(CONF2_ENABLE_PORT); if (bootverbose) { printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2); } if ((oldval2 & 0xf0) == 0) { cfgmech = 2; devmax = 16; outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); mode2res = inb(CONF2_ENABLE_PORT); outb(CONF2_ENABLE_PORT, oldval2); if (bootverbose) printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", mode2res, CONF2_ENABLE_CHK); if (mode2res == CONF2_ENABLE_RES) { if (bootverbose) printf("pci_open(2a):\tnow trying mechanism 2\n"); if (pci_cfgcheck(16)) return (cfgmech); } } cfgmech = 0; devmax = 0; return (cfgmech); } static devclass_t pcib_devclass; static const char * nexus_pcib_is_host_bridge(pcicfgregs *cfg, u_int32_t id, u_int8_t class, u_int8_t subclass, u_int8_t *busnum) { const char *s = NULL; static u_int8_t pxb[4]; /* hack for 450nx */ *busnum = 0; switch (id) { case 0x12258086: s = "Intel 824?? host to PCI bridge"; /* XXX This is a guess */ *busnum = pci_cfgread(cfg, 0x41, 1); break; case 0x71808086: s = "Intel 82443LX (440 LX) host to PCI bridge"; break; case 0x71908086: s = "Intel 82443BX (440 BX) host to PCI bridge"; break; case 0x71928086: s = "Intel 82443BX host to PCI bridge (AGP disabled)"; break; case 0x71a08086: s = "Intel 82443GX host to PCI bridge"; break; case 0x71a18086: s = "Intel 82443GX host to AGP bridge"; break; case 0x71a28086: s = "Intel 82443GX host to PCI bridge (AGP disabled)"; break; case 0x84c48086: s = "Intel 82454KX/GX (Orion) host to PCI bridge"; *busnum = pci_cfgread(cfg, 0x4a, 1); break; case 0x84ca8086: /* * For the 450nx chipset, there is a whole bundle of * things pretending to be host bridges. The MIOC will * be seen first and isn't really a pci bridge (the * actual busses are attached to the PXB's). We need to * read the registers of the MIOC to figure out the * bus numbers for the PXB channels. * * Since the MIOC doesn't have a pci bus attached, we * pretend it wasn't there. */ pxb[0] = pci_cfgread(cfg, 0xd0, 1); /* BUSNO[0] */ pxb[1] = pci_cfgread(cfg, 0xd1, 1) + 1; /* SUBA[0]+1 */ pxb[2] = pci_cfgread(cfg, 0xd3, 1); /* BUSNO[1] */ pxb[3] = pci_cfgread(cfg, 0xd4, 1) + 1; /* SUBA[1]+1 */ return NULL; case 0x84cb8086: switch (cfg->slot) { case 0x12: s = "Intel 82454NX PXB#0, Bus#A"; *busnum = pxb[0]; break; case 0x13: s = "Intel 82454NX PXB#0, Bus#B"; *busnum = pxb[1]; break; case 0x14: s = "Intel 82454NX PXB#1, Bus#A"; *busnum = pxb[2]; break; case 0x15: s = "Intel 82454NX PXB#1, Bus#B"; *busnum = pxb[3]; break; } break; /* AMD -- vendor 0x1022 */ case 0x70061022: s = "AMD-751 host to PCI bridge"; break; /* SiS -- vendor 0x1039 */ case 0x04961039: s = "SiS 85c496"; break; case 0x04061039: s = "SiS 85c501"; break; case 0x06011039: s = "SiS 85c601"; break; case 0x55911039: s = "SiS 5591 host to PCI bridge"; break; case 0x00011039: s = "SiS 5591 host to AGP bridge"; break; /* VLSI -- vendor 0x1004 */ case 0x00051004: s = "VLSI 82C592 Host to PCI bridge"; break; /* XXX Here is MVP3, I got the datasheet but NO M/B to test it */ /* totally. Please let me know if anything wrong. -F */ /* XXX need info on the MVP3 -- any takers? */ case 0x05981106: s = "VIA 82C598MVP (Apollo MVP3) host bridge"; break; /* AcerLabs -- vendor 0x10b9 */ /* Funny : The datasheet told me vendor id is "10b8",sub-vendor */ /* id is '10b9" but the register always shows "10b9". -Foxfair */ case 0x154110b9: s = "AcerLabs M1541 (Aladdin-V) PCI host bridge"; break; /* OPTi -- vendor 0x1045 */ case 0xc8221045: s = "OPTi 82C822 host to PCI Bridge"; break; /* Ross (?) -- vendor 0x1166 */ case 0x00051166: s = "Ross (?) host to PCI bridge"; /* just guessing the secondary bus register number ... */ *busnum = pci_cfgread(cfg, 0x45, 1); break; /* Integrated Micro Solutions -- vendor 0x10e0 */ case 0x884910e0: s = "Integrated Micro Solutions VL Bridge"; break; default: if (class == PCIC_BRIDGE && subclass == PCIS_BRIDGE_HOST) s = "Host to PCI bridge"; break; } return s; } /* * Scan the first pci bus for host-pci bridges and add pcib instances * to the nexus for each bridge. */ static void nexus_pcib_identify(driver_t *driver, device_t parent) { pcicfgregs probe; int found = 0; if (pci_cfgopen() == 0) return; probe.hose = 0; probe.bus = 0; for (probe.slot = 0; probe.slot <= PCI_SLOTMAX; probe.slot++) { int pcifunchigh = 0; for (probe.func = 0; probe.func <= pcifunchigh; probe.func++) { /* * Read the IDs and class from the device. */ u_int32_t id; u_int8_t class, subclass, busnum; device_t child; const char *s; id = pci_cfgread(&probe, PCIR_DEVVENDOR, 4); if (id == -1) continue; class = pci_cfgread(&probe, PCIR_CLASS, 1); subclass = pci_cfgread(&probe, PCIR_SUBCLASS, 1); s = nexus_pcib_is_host_bridge(&probe, id, class, subclass, &busnum); if (s) { /* * Add at priority 100 to make sure we * go after any motherboard resources */ child = BUS_ADD_CHILD(parent, 100, "pcib", busnum); device_set_desc(child, s); found = 1; } } } /* * Make sure we add at least one bridge since some old * hardware doesn't actually have a host-pci bridge device. * Note that pci_cfgopen() thinks we have PCI devices.. */ if (!found) { if (bootverbose) printf( "nexus_pcib_identify: no bridge found, adding pcib0 anyway\n"); BUS_ADD_CHILD(parent, 100, "pcib", 0); } } static int nexus_pcib_probe(device_t dev) { if (pci_cfgopen() != 0) { - device_add_child(dev, "pci", device_get_unit(dev), 0); + device_add_child(dev, "pci", device_get_unit(dev)); return 0; } return ENXIO; } static device_method_t nexus_pcib_methods[] = { /* Device interface */ DEVMETHOD(device_identify, nexus_pcib_identify), DEVMETHOD(device_probe, nexus_pcib_probe), DEVMETHOD(device_attach, bus_generic_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t nexus_pcib_driver = { "pcib", nexus_pcib_methods, 1, }; DRIVER_MODULE(pcib, nexus, nexus_pcib_driver, pcib_devclass, 0, 0); Index: head/sys/amd64/pci/pci_cfgreg.c =================================================================== --- head/sys/amd64/pci/pci_cfgreg.c (revision 54072) +++ head/sys/amd64/pci/pci_cfgreg.c (revision 54073) @@ -1,514 +1,514 @@ /* * Copyright (c) 1997, Stefan Esser * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * */ #include #include #include #include #include #include #include #ifdef PCI_COMPAT /* XXX this is a terrible hack, which keeps the Tekram AMD SCSI driver happy */ #define cfgmech pci_mechanism int cfgmech; #else static int cfgmech; #endif /* PCI_COMPAT */ static int devmax; /* enable configuration space accesses and return data port address */ static int pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) { int dataport = 0; if (bus <= PCI_BUSMAX && slot < devmax && func <= PCI_FUNCMAX && reg <= PCI_REGMAX && bytes != 3 && (unsigned) bytes <= 4 && (reg & (bytes -1)) == 0) { switch (cfgmech) { case 1: outl(CONF1_ADDR_PORT, (1 << 31) | (bus << 16) | (slot << 11) | (func << 8) | (reg & ~0x03)); dataport = CONF1_DATA_PORT + (reg & 0x03); break; case 2: outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); outb(CONF2_FORWARD_PORT, bus); dataport = 0xc000 | (slot << 8) | reg; break; } } return (dataport); } /* disable configuration space accesses */ static void pci_cfgdisable(void) { switch (cfgmech) { case 1: outl(CONF1_ADDR_PORT, 0); break; case 2: outb(CONF2_ENABLE_PORT, 0); outb(CONF2_FORWARD_PORT, 0); break; } } /* read configuration space register */ int pci_cfgread(pcicfgregs *cfg, int reg, int bytes) { int data = -1; int port; port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); if (port != 0) { switch (bytes) { case 1: data = inb(port); break; case 2: data = inw(port); break; case 4: data = inl(port); break; } pci_cfgdisable(); } return (data); } /* write configuration space register */ void pci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes) { int port; port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); if (port != 0) { switch (bytes) { case 1: outb(port, data); break; case 2: outw(port, data); break; case 4: outl(port, data); break; } pci_cfgdisable(); } } /* check whether the configuration mechanism has been correct identified */ static int pci_cfgcheck(int maxdev) { u_char device; if (bootverbose) printf("pci_cfgcheck:\tdevice "); for (device = 0; device < maxdev; device++) { unsigned id, class, header; if (bootverbose) printf("%d ", device); id = inl(pci_cfgenable(0, device, 0, 0, 4)); if (id == 0 || id == -1) continue; class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8; if (bootverbose) printf("[class=%06x] ", class); if (class == 0 || (class & 0xf870ff) != 0) continue; header = inb(pci_cfgenable(0, device, 0, 14, 1)); if (bootverbose) printf("[hdr=%02x] ", header); if ((header & 0x7e) != 0) continue; if (bootverbose) printf("is there (id=%08x)\n", id); pci_cfgdisable(); return (1); } if (bootverbose) printf("-- nothing found\n"); pci_cfgdisable(); return (0); } static int pci_cfgopen(void) { unsigned long mode1res,oldval1; unsigned char mode2res,oldval2; oldval1 = inl(CONF1_ADDR_PORT); if (bootverbose) { printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", oldval1); } if ((oldval1 & CONF1_ENABLE_MSK) == 0) { cfgmech = 1; devmax = 32; outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); outb(CONF1_ADDR_PORT +3, 0); mode1res = inl(CONF1_ADDR_PORT); outl(CONF1_ADDR_PORT, oldval1); if (bootverbose) printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n", mode1res, CONF1_ENABLE_CHK); if (mode1res) { if (pci_cfgcheck(32)) return (cfgmech); } outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); mode1res = inl(CONF1_ADDR_PORT); outl(CONF1_ADDR_PORT, oldval1); if (bootverbose) printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n", mode1res, CONF1_ENABLE_CHK1); if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { if (pci_cfgcheck(32)) return (cfgmech); } } oldval2 = inb(CONF2_ENABLE_PORT); if (bootverbose) { printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2); } if ((oldval2 & 0xf0) == 0) { cfgmech = 2; devmax = 16; outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); mode2res = inb(CONF2_ENABLE_PORT); outb(CONF2_ENABLE_PORT, oldval2); if (bootverbose) printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", mode2res, CONF2_ENABLE_CHK); if (mode2res == CONF2_ENABLE_RES) { if (bootverbose) printf("pci_open(2a):\tnow trying mechanism 2\n"); if (pci_cfgcheck(16)) return (cfgmech); } } cfgmech = 0; devmax = 0; return (cfgmech); } static devclass_t pcib_devclass; static const char * nexus_pcib_is_host_bridge(pcicfgregs *cfg, u_int32_t id, u_int8_t class, u_int8_t subclass, u_int8_t *busnum) { const char *s = NULL; static u_int8_t pxb[4]; /* hack for 450nx */ *busnum = 0; switch (id) { case 0x12258086: s = "Intel 824?? host to PCI bridge"; /* XXX This is a guess */ *busnum = pci_cfgread(cfg, 0x41, 1); break; case 0x71808086: s = "Intel 82443LX (440 LX) host to PCI bridge"; break; case 0x71908086: s = "Intel 82443BX (440 BX) host to PCI bridge"; break; case 0x71928086: s = "Intel 82443BX host to PCI bridge (AGP disabled)"; break; case 0x71a08086: s = "Intel 82443GX host to PCI bridge"; break; case 0x71a18086: s = "Intel 82443GX host to AGP bridge"; break; case 0x71a28086: s = "Intel 82443GX host to PCI bridge (AGP disabled)"; break; case 0x84c48086: s = "Intel 82454KX/GX (Orion) host to PCI bridge"; *busnum = pci_cfgread(cfg, 0x4a, 1); break; case 0x84ca8086: /* * For the 450nx chipset, there is a whole bundle of * things pretending to be host bridges. The MIOC will * be seen first and isn't really a pci bridge (the * actual busses are attached to the PXB's). We need to * read the registers of the MIOC to figure out the * bus numbers for the PXB channels. * * Since the MIOC doesn't have a pci bus attached, we * pretend it wasn't there. */ pxb[0] = pci_cfgread(cfg, 0xd0, 1); /* BUSNO[0] */ pxb[1] = pci_cfgread(cfg, 0xd1, 1) + 1; /* SUBA[0]+1 */ pxb[2] = pci_cfgread(cfg, 0xd3, 1); /* BUSNO[1] */ pxb[3] = pci_cfgread(cfg, 0xd4, 1) + 1; /* SUBA[1]+1 */ return NULL; case 0x84cb8086: switch (cfg->slot) { case 0x12: s = "Intel 82454NX PXB#0, Bus#A"; *busnum = pxb[0]; break; case 0x13: s = "Intel 82454NX PXB#0, Bus#B"; *busnum = pxb[1]; break; case 0x14: s = "Intel 82454NX PXB#1, Bus#A"; *busnum = pxb[2]; break; case 0x15: s = "Intel 82454NX PXB#1, Bus#B"; *busnum = pxb[3]; break; } break; /* AMD -- vendor 0x1022 */ case 0x70061022: s = "AMD-751 host to PCI bridge"; break; /* SiS -- vendor 0x1039 */ case 0x04961039: s = "SiS 85c496"; break; case 0x04061039: s = "SiS 85c501"; break; case 0x06011039: s = "SiS 85c601"; break; case 0x55911039: s = "SiS 5591 host to PCI bridge"; break; case 0x00011039: s = "SiS 5591 host to AGP bridge"; break; /* VLSI -- vendor 0x1004 */ case 0x00051004: s = "VLSI 82C592 Host to PCI bridge"; break; /* XXX Here is MVP3, I got the datasheet but NO M/B to test it */ /* totally. Please let me know if anything wrong. -F */ /* XXX need info on the MVP3 -- any takers? */ case 0x05981106: s = "VIA 82C598MVP (Apollo MVP3) host bridge"; break; /* AcerLabs -- vendor 0x10b9 */ /* Funny : The datasheet told me vendor id is "10b8",sub-vendor */ /* id is '10b9" but the register always shows "10b9". -Foxfair */ case 0x154110b9: s = "AcerLabs M1541 (Aladdin-V) PCI host bridge"; break; /* OPTi -- vendor 0x1045 */ case 0xc8221045: s = "OPTi 82C822 host to PCI Bridge"; break; /* Ross (?) -- vendor 0x1166 */ case 0x00051166: s = "Ross (?) host to PCI bridge"; /* just guessing the secondary bus register number ... */ *busnum = pci_cfgread(cfg, 0x45, 1); break; /* Integrated Micro Solutions -- vendor 0x10e0 */ case 0x884910e0: s = "Integrated Micro Solutions VL Bridge"; break; default: if (class == PCIC_BRIDGE && subclass == PCIS_BRIDGE_HOST) s = "Host to PCI bridge"; break; } return s; } /* * Scan the first pci bus for host-pci bridges and add pcib instances * to the nexus for each bridge. */ static void nexus_pcib_identify(driver_t *driver, device_t parent) { pcicfgregs probe; int found = 0; if (pci_cfgopen() == 0) return; probe.hose = 0; probe.bus = 0; for (probe.slot = 0; probe.slot <= PCI_SLOTMAX; probe.slot++) { int pcifunchigh = 0; for (probe.func = 0; probe.func <= pcifunchigh; probe.func++) { /* * Read the IDs and class from the device. */ u_int32_t id; u_int8_t class, subclass, busnum; device_t child; const char *s; id = pci_cfgread(&probe, PCIR_DEVVENDOR, 4); if (id == -1) continue; class = pci_cfgread(&probe, PCIR_CLASS, 1); subclass = pci_cfgread(&probe, PCIR_SUBCLASS, 1); s = nexus_pcib_is_host_bridge(&probe, id, class, subclass, &busnum); if (s) { /* * Add at priority 100 to make sure we * go after any motherboard resources */ child = BUS_ADD_CHILD(parent, 100, "pcib", busnum); device_set_desc(child, s); found = 1; } } } /* * Make sure we add at least one bridge since some old * hardware doesn't actually have a host-pci bridge device. * Note that pci_cfgopen() thinks we have PCI devices.. */ if (!found) { if (bootverbose) printf( "nexus_pcib_identify: no bridge found, adding pcib0 anyway\n"); BUS_ADD_CHILD(parent, 100, "pcib", 0); } } static int nexus_pcib_probe(device_t dev) { if (pci_cfgopen() != 0) { - device_add_child(dev, "pci", device_get_unit(dev), 0); + device_add_child(dev, "pci", device_get_unit(dev)); return 0; } return ENXIO; } static device_method_t nexus_pcib_methods[] = { /* Device interface */ DEVMETHOD(device_identify, nexus_pcib_identify), DEVMETHOD(device_probe, nexus_pcib_probe), DEVMETHOD(device_attach, bus_generic_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t nexus_pcib_driver = { "pcib", nexus_pcib_methods, 1, }; DRIVER_MODULE(pcib, nexus, nexus_pcib_driver, pcib_devclass, 0, 0); Index: head/sys/dev/amr/amr.c =================================================================== --- head/sys/dev/amr/amr.c (revision 54072) +++ head/sys/dev/amr/amr.c (revision 54073) @@ -1,1506 +1,1507 @@ /*- * Copyright (c) 1999 Michael Smith * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Driver for the AMI MegaRaid family of controllers */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if 0 #define debug(fmt, args...) printf("%s: " fmt "\n", __FUNCTION__ , ##args) #else #define debug(fmt, args...) #endif #define AMR_CDEV_MAJOR 132 static struct cdevsw amr_cdevsw = { /* open */ amr_open, /* close */ amr_close, /* read */ noread, /* write */ nowrite, /* ioctl */ amr_ioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "amr", /* maj */ AMR_CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ 254 /* XXX magic no-bdev */ }; static int cdev_registered = 0; devclass_t amr_devclass; /* * Command wrappers */ static int amr_query_controller(struct amr_softc *sc); static void *amr_enquiry(struct amr_softc *sc, size_t bufsize, u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual); static int amr_flush(struct amr_softc *sc); static void amr_startio(struct amr_softc *sc); static void amr_completeio(struct amr_command *ac); /* * Command processing. */ static int amr_wait_command(struct amr_command *ac); static int amr_poll_command(struct amr_command *ac); static int amr_getslot(struct amr_command *ac); static void amr_mapcmd(struct amr_command *ac); static void amr_unmapcmd(struct amr_command *ac); static int amr_start(struct amr_command *ac); static int amr_done(struct amr_softc *sc); static void amr_complete(struct amr_softc *sc); /* * Command buffer allocation. */ static struct amr_command *amr_alloccmd(struct amr_softc *sc); static void amr_releasecmd(struct amr_command *ac); static void amr_freecmd(struct amr_command *ac); /* * Interface-specific shims */ static void amr_quartz_submit_command(struct amr_softc *sc); static int amr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave); static void amr_quartz_attach_mailbox(struct amr_softc *sc); static void amr_std_submit_command(struct amr_softc *sc); static int amr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave); static void amr_std_attach_mailbox(struct amr_softc *sc); /* * Debugging */ static void amr_printcommand(struct amr_command *ac); /******************************************************************************** ******************************************************************************** Public Interfaces ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Free all of the resources associated with (sc) * * Should not be called if the controller is active. */ void amr_free(struct amr_softc *sc) { struct amr_command *ac; u_int8_t *p; debug("called"); /* throw away any command buffers */ while ((ac = TAILQ_FIRST(&sc->amr_freecmds)) != NULL) { TAILQ_REMOVE(&sc->amr_freecmds, ac, ac_link); amr_freecmd(ac); } /* destroy data-transfer DMA tag */ if (sc->amr_buffer_dmat) bus_dma_tag_destroy(sc->amr_buffer_dmat); /* free and destroy DMA memory and tag for s/g lists */ if (sc->amr_sgtable) bus_dmamem_free(sc->amr_sg_dmat, sc->amr_sgtable, sc->amr_sg_dmamap); if (sc->amr_sg_dmat) bus_dma_tag_destroy(sc->amr_sg_dmat); /* free and destroy DMA memory and tag for mailbox */ if (sc->amr_mailbox) { p = (u_int8_t *)sc->amr_mailbox; bus_dmamem_free(sc->amr_sg_dmat, p - 16, sc->amr_sg_dmamap); } if (sc->amr_sg_dmat) bus_dma_tag_destroy(sc->amr_sg_dmat); /* disconnect the interrupt handler */ if (sc->amr_intr) bus_teardown_intr(sc->amr_dev, sc->amr_irq, sc->amr_intr); if (sc->amr_irq != NULL) bus_release_resource(sc->amr_dev, SYS_RES_IRQ, 0, sc->amr_irq); /* destroy the parent DMA tag */ if (sc->amr_parent_dmat) bus_dma_tag_destroy(sc->amr_parent_dmat); /* release the register window mapping */ if (sc->amr_reg != NULL) bus_release_resource(sc->amr_dev, (sc->amr_type == AMR_TYPE_QUARTZ) ? SYS_RES_MEMORY : SYS_RES_IOPORT, AMR_CFG_BASE, sc->amr_reg); } /******************************************************************************** * Allocate and map the scatter/gather table in bus space. */ static void amr_dma_map_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct amr_softc *sc = (struct amr_softc *)arg; debug("called"); /* save base of s/g table's address in bus space */ sc->amr_sgbusaddr = segs->ds_addr; } static int amr_sglist_map(struct amr_softc *sc) { size_t segsize; int error; debug("called"); /* destroy any existing mappings */ if (sc->amr_sgtable) bus_dmamem_free(sc->amr_sg_dmat, sc->amr_sgtable, sc->amr_sg_dmamap); if (sc->amr_sg_dmat) bus_dma_tag_destroy(sc->amr_sg_dmat); /* * Create a single tag describing a region large enough to hold all of * the s/g lists we will need. */ segsize = sizeof(struct amr_sgentry) * AMR_NSEG * sc->amr_maxio; error = bus_dma_tag_create(sc->amr_parent_dmat, /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ segsize, 1, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ &sc->amr_sg_dmat); if (error != 0) { device_printf(sc->amr_dev, "can't allocate scatter/gather DMA tag\n"); return(ENOMEM); } /* * Allocate enough s/g maps for all commands and permanently map them into * controller-visible space. * * XXX this assumes we can get enough space for all the s/g maps in one * contiguous slab. We may need to switch to a more complex arrangement where * we allocate in smaller chunks and keep a lookup table from slot to bus address. */ error = bus_dmamem_alloc(sc->amr_sg_dmat, (void **)&sc->amr_sgtable, BUS_DMA_NOWAIT, &sc->amr_sg_dmamap); if (error) { device_printf(sc->amr_dev, "can't allocate s/g table\n"); return(ENOMEM); } bus_dmamap_load(sc->amr_sg_dmat, sc->amr_sg_dmamap, sc->amr_sgtable, segsize, amr_dma_map_sg, sc, 0); return(0); } /******************************************************************************** * Allocate and set up mailbox areas for the controller (sc) * * The basic mailbox structure should be 16-byte aligned. This means that the * mailbox64 structure has 4 bytes hanging off the bottom. */ static void amr_map_mailbox(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct amr_softc *sc = (struct amr_softc *)arg; debug("called"); /* save phsyical base of the basic mailbox structure */ sc->amr_mailboxphys = segs->ds_addr + 16; } static int amr_setup_mbox(struct amr_softc *sc) { int error; u_int8_t *p; debug("called"); /* * Create a single tag describing a region large enough to hold the entire * mailbox. */ error = bus_dma_tag_create(sc->amr_parent_dmat, /* parent */ 16, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ sizeof(struct amr_mailbox) + 16, 1, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ &sc->amr_mailbox_dmat); if (error != 0) { device_printf(sc->amr_dev, "can't allocate mailbox tag\n"); return(ENOMEM); } /* * Allocate the mailbox structure and permanently map it into * controller-visible space. */ error = bus_dmamem_alloc(sc->amr_mailbox_dmat, (void **)&p, BUS_DMA_NOWAIT, &sc->amr_mailbox_dmamap); if (error) { device_printf(sc->amr_dev, "can't allocate mailbox memory\n"); return(ENOMEM); } bus_dmamap_load(sc->amr_mailbox_dmat, sc->amr_mailbox_dmamap, p, sizeof(struct amr_mailbox64), amr_map_mailbox, sc, 0); /* * Conventional mailbox is inside the mailbox64 region. */ bzero(p, sizeof(struct amr_mailbox64)); sc->amr_mailbox64 = (struct amr_mailbox64 *)(p + 12); sc->amr_mailbox = (struct amr_mailbox *)(p + 16); if (sc->amr_type == AMR_TYPE_STD) { /* XXX we have to tell the controller where we put it */ } return(0); } /******************************************************************************** * Initialise the controller and softc. */ int amr_attach(struct amr_softc *sc) { int rid, error; /* * Initialise per-controller queues. */ TAILQ_INIT(&sc->amr_work); TAILQ_INIT(&sc->amr_freecmds); bufq_init(&sc->amr_bufq); /* * Configure for this controller type. */ if (sc->amr_type == AMR_TYPE_QUARTZ) { sc->amr_submit_command = amr_quartz_submit_command; sc->amr_get_work = amr_quartz_get_work; sc->amr_attach_mailbox = amr_quartz_attach_mailbox; } else { sc->amr_submit_command = amr_std_submit_command; sc->amr_get_work = amr_std_get_work; sc->amr_attach_mailbox = amr_std_attach_mailbox; } /* * Allocate and connect our interrupt. */ rid = 0; sc->amr_irq = bus_alloc_resource(sc->amr_dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); if (sc->amr_irq == NULL) { device_printf(sc->amr_dev, "couldn't allocate interrupt\n"); amr_free(sc); return(ENXIO); } error = bus_setup_intr(sc->amr_dev, sc->amr_irq, INTR_TYPE_BIO, amr_intr, sc, &sc->amr_intr); if (error) { device_printf(sc->amr_dev, "couldn't set up interrupt\n"); amr_free(sc); return(ENXIO); } /* * Create DMA tag for mapping buffers into controller-addressable space. */ error = bus_dma_tag_create(sc->amr_parent_dmat, /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, AMR_NSEG, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ &sc->amr_buffer_dmat); if (error != 0) { device_printf(sc->amr_dev, "can't allocate buffer DMA tag\n"); return(ENOMEM); } /* * Allocate and set up mailbox in a bus-visible fashion, attach to controller. */ if ((error = amr_setup_mbox(sc)) != 0) return(error); sc->amr_attach_mailbox(sc); /* * Build a temporary set of scatter/gather buffers. */ sc->amr_maxio = 2; if (amr_sglist_map(sc)) return(ENXIO); /* * Quiz controller for features and limits. */ if (amr_query_controller(sc)) return(ENXIO); /* * Rebuild the scatter/gather buffers now we know how many we need. */ if (amr_sglist_map(sc)) return(ENXIO); return(0); } /******************************************************************************** * Locate disk resources and attach children to them. */ void amr_startup(struct amr_softc *sc) { struct amr_logdrive *dr; int i, error; debug("called"); /* get up-to-date drive information */ if (amr_query_controller(sc)) { device_printf(sc->amr_dev, "couldn't scan controller for drives\n"); return; } /* iterate over available drives */ for (i = 0, dr = &sc->amr_drive[0]; (i < AMR_MAXLD) && (dr->al_size != 0xffffffff); i++, dr++) { /* are we already attached to this drive? */ if (dr->al_disk == 0) { /* generate geometry information */ if (dr->al_size > 0x200000) { /* extended translation? */ dr->al_heads = 255; dr->al_sectors = 63; } else { dr->al_heads = 64; dr->al_sectors = 32; } dr->al_cylinders = dr->al_size / (dr->al_heads * dr->al_sectors); - dr->al_disk = device_add_child(sc->amr_dev, NULL, -1, dr); + dr->al_disk = device_add_child(sc->amr_dev, NULL, -1); if (dr->al_disk == 0) device_printf(sc->amr_dev, "device_add_child failed\n"); + device_set_ivars(dr->al_disk, dr); } } if ((error = bus_generic_attach(sc->amr_dev)) != 0) device_printf(sc->amr_dev, "bus_generic_attach returned %d\n", error); /* mark controller back up */ sc->amr_state &= ~AMR_STATE_SHUTDOWN; /* interrupts will be enabled before we do anything more */ sc->amr_state |= AMR_STATE_INTEN; } /******************************************************************************** * Disconnect from the controller completely, in preparation for unload. */ int amr_detach(device_t dev) { struct amr_softc *sc = device_get_softc(dev); int error; debug("called"); if (sc->amr_state & AMR_STATE_OPEN) return(EBUSY); if ((error = amr_shutdown(dev))) return(error); amr_free(sc); /* * Deregister the control device on last detach. */ if (--cdev_registered == 0) cdevsw_remove(&amr_cdevsw); return(0); } /******************************************************************************** * Bring the controller down to a dormant state and detach all child devices. * * This function is called before detach, system shutdown, or before performing * an operation which may add or delete system disks. (Call amr_startup to * resume normal operation.) * * Note that we can assume that the bufq on the controller is empty, as we won't * allow shutdown if any device is open. */ int amr_shutdown(device_t dev) { struct amr_softc *sc = device_get_softc(dev); struct amrd_softc *ad; int i, s, error; debug("called"); s = splbio(); error = 0; /* assume we're going to shut down */ sc->amr_state |= AMR_STATE_SHUTDOWN; for (i = 0; i < AMR_MAXLD; i++) { if (sc->amr_drive[i].al_disk != 0) { ad = device_get_softc(sc->amr_drive[i].al_disk); if (ad->amrd_flags & AMRD_OPEN) { /* drive is mounted, abort shutdown */ sc->amr_state &= ~AMR_STATE_SHUTDOWN; device_printf(sc->amr_drive[i].al_disk, "still open, can't shutdown\n"); error = EBUSY; goto out; } } } /* flush controller */ device_printf(sc->amr_dev, "flushing cache..."); if (amr_flush(sc)) { printf("failed\n"); } else { printf("done\n"); } /* delete all our child devices */ for (i = 0; i < AMR_MAXLD; i++) { if (sc->amr_drive[i].al_disk != 0) { if ((error = device_delete_child(sc->amr_dev, sc->amr_drive[i].al_disk)) != 0) goto out; sc->amr_drive[i].al_disk = 0; } } bus_generic_detach(sc->amr_dev); out: splx(s); return(error); } /******************************************************************************** * Bring the controller to a quiescent state, ready for system suspend. */ int amr_suspend(device_t dev) { struct amr_softc *sc = device_get_softc(dev); debug("called"); sc->amr_state |= AMR_STATE_SUSPEND; /* flush controller */ device_printf(sc->amr_dev, "flushing cache..."); printf("%s\n", amr_flush(sc) ? "failed" : "done"); return(0); } /******************************************************************************** * Bring the controller back to a state ready for operation. */ int amr_resume(device_t dev) { struct amr_softc *sc = device_get_softc(dev); debug("called"); sc->amr_state &= ~AMR_STATE_SUSPEND; return(0); } /******************************************************************************* * Take an interrupt, or be poked by other code to look for interrupt-worthy * status. */ void amr_intr(void *arg) { struct amr_softc *sc = (struct amr_softc *)arg; int worked; debug("called on %p", sc); /* spin collecting finished commands, process them if we find anything */ worked = 0; while (amr_done(sc)) worked = 1; if (worked) amr_complete(sc); }; /******************************************************************************* * Receive a buf structure from a child device and queue it on a particular * disk resource, then poke the disk resource to start as much work as it can. */ int amr_submit_buf(struct amr_softc *sc, struct buf *bp) { int s; debug("called"); s = splbio(); bufq_insert_tail(&sc->amr_bufq, bp); splx(s); sc->amr_waitbufs++; amr_startio(sc); return(0); } /******************************************************************************** * Accept an open operation on the control device. */ int amr_open(dev_t dev, int flags, int fmt, struct proc *p) { int unit = minor(dev); struct amr_softc *sc = devclass_get_softc(amr_devclass, unit); sc->amr_state |= AMR_STATE_OPEN; return(0); } /******************************************************************************** * Accept the last close on the control device. */ int amr_close(dev_t dev, int flags, int fmt, struct proc *p) { int unit = minor(dev); struct amr_softc *sc = devclass_get_softc(amr_devclass, unit); sc->amr_state &= ~AMR_STATE_OPEN; return (0); } /******************************************************************************** * Handle controller-specific control operations. */ int amr_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p) { switch(cmd) { default: return(ENOTTY); } } /******************************************************************************** * Handle operations requested by a drive connected to this controller. */ int amr_submit_ioctl(struct amr_softc *sc, struct amr_logdrive *drive, u_long cmd, caddr_t addr, int32_t flag, struct proc *p) { return(ENOTTY); } /******************************************************************************** ******************************************************************************** Command Wrappers ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Interrogate the controller for the operational parameters we require. */ static int amr_query_controller(struct amr_softc *sc) { void *buf; int i; /* try to issue an ENQUIRY3 command */ if ((buf = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_ENQ3, AMR_CONFIG_ENQ3_SOLICITED_FULL)) == NULL) { struct amr_enquiry *ae; /* failed, try the old ENQUIRY command */ if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0)) == NULL) { device_printf(sc->amr_dev, "could not obtain configuration data from controller\n"); return(1); } /* first-time enquiry? */ if (sc->amr_maxdrives == 0) { device_printf(sc->amr_dev, "firmware %.4s bios %.4s %dMB memory\n", ae->ae_adapter.aa_firmware, ae->ae_adapter.aa_bios, ae->ae_adapter.aa_memorysize); } sc->amr_maxdrives = 8; sc->amr_maxio = ae->ae_adapter.aa_maxio; for (i = 0; i < ae->ae_ldrv.al_numdrives; i++) { sc->amr_drive[i].al_size = ae->ae_ldrv.al_size[i]; sc->amr_drive[i].al_state = ae->ae_ldrv.al_state[i]; sc->amr_drive[i].al_properties = ae->ae_ldrv.al_properties[i]; debug(" drive %d: %d state %x properties %x\n", i, sc->amr_drive[i].al_size, sc->amr_drive[i].al_state, sc->amr_drive[i].al_properties); } for (; i < AMR_MAXLD; i++) sc->amr_drive[i].al_size = 0xffffffff; free(ae, M_DEVBUF); } else { free(buf, M_DEVBUF); sc->amr_maxdrives = 40; /* get static product info */ if ((buf = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODINFO, 0)) == NULL) { device_printf(sc->amr_dev, "controller supports 40ld but CONFIG_PRODINFO failed\n"); return(1); } free(buf, M_DEVBUF); device_printf(sc->amr_dev, "40LD firmware unsupported; send controller to msmith@freebsd.org\n"); return(1); } return(0); } /******************************************************************************** * Run a generic enquiry-style command. */ static void * amr_enquiry(struct amr_softc *sc, size_t bufsize, u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual) { struct amr_command *ac; void *result; u_int8_t *mbox; int error; debug("called"); error = 1; result = NULL; /* get ourselves a command buffer */ if ((ac = amr_alloccmd(sc)) == NULL) goto out; /* allocate the response structure */ if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL) goto out; /* get a command slot */ ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT; if (amr_getslot(ac)) goto out; /* map the command so the controller can see it */ ac->ac_data = result; ac->ac_length = bufsize; amr_mapcmd(ac); /* build the command proper */ mbox = (u_int8_t *)&ac->ac_mailbox; /* XXX want a real structure for this? */ mbox[0] = cmd; mbox[2] = cmdsub; mbox[3] = cmdqual; ac->ac_mailbox.mb_physaddr = ac->ac_dataphys; /* run the command in polled/wait mode as suits the current mode */ if ((sc->amr_state & AMR_STATE_INTEN) ? amr_wait_command(ac) : amr_poll_command(ac)) goto out; error = ac->ac_status; out: if (ac != NULL) amr_releasecmd(ac); if ((error != 0) && (result != NULL)) { free(result, M_DEVBUF); result = NULL; } return(result); } /******************************************************************************** * Flush the controller's internal cache, return status. */ static int amr_flush(struct amr_softc *sc) { struct amr_command *ac; int error; /* get ourselves a command buffer */ error = 1; if ((ac = amr_alloccmd(sc)) == NULL) goto out; /* get a command slot */ ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT; if (amr_getslot(ac)) goto out; /* build the command proper */ ac->ac_mailbox.mb_command = AMR_CMD_FLUSH; /* run the command in polled/wait mode as suits the current mode */ if ((sc->amr_state & AMR_STATE_INTEN) ? amr_wait_command(ac) : amr_poll_command(ac)) goto out; error = ac->ac_status; out: if (ac != NULL) amr_releasecmd(ac); return(error); } /******************************************************************************** * Pull as much work off the softc's work queue as possible and give it to the * controller. Leave a couple of slots free for emergencies. * * We avoid running at splbio() whenever possible. */ static void amr_startio(struct amr_softc *sc) { struct amr_command *ac; struct amrd_softc *amrd; struct buf *bp; int blkcount; int driveno; int cmd; int s; /* spin until something prevents us from doing any work */ s = splbio(); for (;;) { /* see if there's work to be done */ if ((bp = bufq_first(&sc->amr_bufq)) == NULL) break; /* get a command */ if ((ac = amr_alloccmd(sc)) == NULL) break; /* get a slot for the command */ if (amr_getslot(ac) != 0) { amr_releasecmd(ac); break; } /* get the buf containing our work */ bufq_remove(&sc->amr_bufq, bp); sc->amr_waitbufs--; splx(s); /* connect the buf to the command */ ac->ac_complete = amr_completeio; ac->ac_private = bp; ac->ac_data = bp->b_data; ac->ac_length = bp->b_bcount; if (bp->b_flags & B_READ) { ac->ac_flags |= AMR_CMD_DATAIN; cmd = AMR_CMD_LREAD; } else { ac->ac_flags |= AMR_CMD_DATAOUT; cmd = AMR_CMD_LWRITE; } /* map the command so the controller can work with it */ amr_mapcmd(ac); /* build a suitable I/O command (assumes 512-byte rounded transfers) */ amrd = (struct amrd_softc *)bp->b_driver1; driveno = amrd->amrd_drive - &sc->amr_drive[0]; blkcount = bp->b_bcount / AMR_BLKSIZE; if ((bp->b_pblkno + blkcount) > sc->amr_drive[driveno].al_size) device_printf(sc->amr_dev, "I/O beyond end of unit (%u,%d > %u)\n", bp->b_pblkno, blkcount, sc->amr_drive[driveno].al_size); /* * Build the I/O command. */ ac->ac_mailbox.mb_command = cmd; ac->ac_mailbox.mb_blkcount = blkcount; ac->ac_mailbox.mb_lba = bp->b_pblkno; ac->ac_mailbox.mb_physaddr = ac->ac_sgphys; ac->ac_mailbox.mb_drive = driveno; ac->ac_mailbox.mb_nsgelem = ac->ac_nsgent; /* try to give command to controller */ if (amr_start(ac) != 0) { /* fail the command */ ac->ac_status = AMR_STATUS_WEDGED; amr_completeio(ac); } s = splbio(); } splx(s); } /******************************************************************************** * Handle completion of an I/O command. */ static void amr_completeio(struct amr_command *ac) { struct amr_softc *sc = ac->ac_sc; struct buf *bp = (struct buf *)ac->ac_private; if (ac->ac_status != AMR_STATUS_SUCCESS) { /* could be more verbose here? */ bp->b_error = EIO; bp->b_flags |= B_ERROR; switch(ac->ac_status) { /* XXX need more information on I/O error reasons */ default: device_printf(sc->amr_dev, "I/O error - %x\n", ac->ac_status); amr_printcommand(ac); break; } } amr_releasecmd(ac); amrd_intr(bp); } /******************************************************************************** ******************************************************************************** Command Processing ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Take a command, submit it to the controller and sleep until it completes * or fails. Interrupts must be enabled, returns nonzero on error. */ static int amr_wait_command(struct amr_command *ac) { struct amr_softc *sc = ac->ac_sc; int error, count; debug("called"); ac->ac_complete = NULL; ac->ac_private = ac; if ((error = amr_start(ac)) != 0) return(error); count = 0; /* XXX better timeout? */ while ((ac->ac_status == AMR_STATUS_BUSY) && (count < 30)) { tsleep(ac->ac_private, PRIBIO | PCATCH, "amrwcmd", hz); } if (ac->ac_status != 0) { device_printf(sc->amr_dev, "I/O error 0x%x\n", ac->ac_status); return(EIO); } return(0); } /******************************************************************************** * Take a command, submit it to the controller and busy-wait for it to return. * Returns nonzero on error. Can be safely called with interrupts enabled. */ static int amr_poll_command(struct amr_command *ac) { struct amr_softc *sc = ac->ac_sc; int error, count, s; debug("called"); ac->ac_complete = NULL; ac->ac_private = NULL; if ((error = amr_start(ac)) != 0) return(error); count = 0; do { /* * Poll for completion, although the interrupt handler may beat us to it. * Note that the timeout here is somewhat arbitrary. */ amr_done(sc); } while ((ac->ac_status == AMR_STATUS_BUSY) && (count++ < 100000)); s = splbio(); if (ac->ac_status != AMR_STATUS_BUSY) { TAILQ_REMOVE(&sc->amr_work, ac, ac_link); sc->amr_workcount--; error = 0; } else { /* take the command out of the busy list, mark slot as bogus */ sc->amr_busycmd[ac->ac_slot] = (struct amr_command *)sc; error = EIO; device_printf(sc->amr_dev, "I/O error 0x%x\n", ac->ac_status); } splx(s); return(error); } /******************************************************************************** * Get a free command slot. */ static int amr_getslot(struct amr_command *ac) { struct amr_softc *sc = ac->ac_sc; int s, slot, limit; debug("called"); /* enforce slot usage limit */ limit = (ac->ac_flags & AMR_CMD_PRIORITY) ? sc->amr_maxio : sc->amr_maxio - 4; if (sc->amr_busycmdcount > limit) return(EBUSY); /* * Allocate a slot */ s = splbio(); for (slot = 0; slot < sc->amr_maxio; slot++) { if (sc->amr_busycmd[slot] == NULL) break; } if (slot < sc->amr_maxio) { sc->amr_busycmdcount++; sc->amr_busycmd[slot] = ac; } splx(s); /* out of slots? */ if (slot >= sc->amr_maxio) return(EBUSY); ac->ac_slot = slot; return(0); } /******************************************************************************** * Map/unmap (ac)'s data in the controller's addressable space. */ static void amr_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) { struct amr_command *ac = (struct amr_command *)arg; struct amr_softc *sc = ac->ac_sc; struct amr_sgentry *sg; int i; debug("called"); /* get base address of s/g table */ sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG); /* save s/g table information in command */ ac->ac_nsgent = nsegments; ac->ac_sgphys = sc->amr_sgbusaddr + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry)); ac->ac_dataphys = segs[0].ds_addr; /* populate s/g table */ for (i = 0; i < nsegments; i++, sg++) { sg->sg_addr = segs[i].ds_addr; sg->sg_count = segs[i].ds_len; } } static void amr_mapcmd(struct amr_command *ac) { struct amr_softc *sc = ac->ac_sc; debug("called"); /* if the command involves data at all */ if (ac->ac_data != NULL) { /* map the data buffer into bus space and build the s/g list */ bus_dmamap_load(sc->amr_buffer_dmat, ac->ac_dmamap, ac->ac_data, ac->ac_length, amr_setup_dmamap, ac, 0); if (ac->ac_flags & AMR_CMD_DATAIN) bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_PREREAD); if (ac->ac_flags & AMR_CMD_DATAOUT) bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_PREWRITE); } } static void amr_unmapcmd(struct amr_command *ac) { struct amr_softc *sc = ac->ac_sc; debug("called"); /* if the command involved data at all */ if (ac->ac_data != NULL) { if (ac->ac_flags & AMR_CMD_DATAIN) bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_POSTREAD); if (ac->ac_flags & AMR_CMD_DATAOUT) bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_dmamap); } } /******************************************************************************** * Take a command and give it to the controller. Take care of any completed * commands we encouter while waiting. */ static int amr_start(struct amr_command *ac) { struct amr_softc *sc = ac->ac_sc; int worked, done, s, i; debug("called"); /* * Save the slot number so that we can locate this command when complete. * Note that ident = 0 seems to be special, so we don't use it. */ ac->ac_mailbox.mb_ident = ac->ac_slot + 1; /* set the busy flag when we copy the mailbox in */ ac->ac_mailbox.mb_busy = 1; /* set impossible status so that a woken sleeper can tell the command is busy */ ac->ac_status = AMR_STATUS_BUSY; /* spin waiting for the mailbox */ debug("wait for mailbox"); for (i = 10000, done = 0, worked = 0; (i > 0) && !done; i--) { s = splbio(); /* is the mailbox free? */ if (sc->amr_mailbox->mb_busy == 0) { debug("got mailbox"); sc->amr_mailbox64->mb64_segment = 0; bcopy(&ac->ac_mailbox, sc->amr_mailbox, AMR_MBOX_CMDSIZE); sc->amr_submit_command(sc); done = 1; sc->amr_workcount++; TAILQ_INSERT_TAIL(&sc->amr_work, ac, ac_link); /* not free, try to clean up while we wait */ } else { debug("busy flag %x\n", sc->amr_mailbox->mb_busy); worked = amr_done(sc); } splx(s); } /* do completion processing if we picked anything up */ if (worked) amr_complete(sc); /* command is enqueued? */ if (done) { ac->ac_stamp = time_second; debug("posted command"); return(0); } /* * The controller wouldn't take the command. Revoke the slot * that the command was given and return with a bad status. */ sc->amr_busycmd[ac->ac_slot] = NULL; device_printf(sc->amr_dev, "controller wedged (not taking commands)\n"); ac->ac_status = AMR_STATUS_WEDGED; return(EIO); } /******************************************************************************** * Extract one or more completed commands from the controller (sc) * * Returns nonzero if any commands on the work queue were marked as completed. */ static int amr_done(struct amr_softc *sc) { struct amr_command *ac; struct amr_mailbox mbox; int i, idx, result; debug("called"); /* See if there's anything for us to do */ result = 0; if (sc->amr_get_work(sc, &mbox)) { /* iterate over completed commands */ for (i = 0; i < mbox.mb_nstatus; i++) { /* get pointer to busy command */ idx = mbox.mb_completed[i] - 1; ac = sc->amr_busycmd[idx]; /* really a busy command? */ if (ac != NULL) { /* pull the command from the busy index */ sc->amr_busycmd[idx] = NULL; sc->amr_busycmdcount--; /* unmap data buffer */ amr_unmapcmd(ac); /* aborted command? */ if (ac == (struct amr_command *)sc) { device_printf(sc->amr_dev, "aborted command completed (%d)\n", idx); ac = NULL; /* completed normally, save status */ } else { ac->ac_status = mbox.mb_status; debug("completed command with status %x", mbox.mb_status); } result = 1; } } } return(result); } /******************************************************************************** * Do completion processing on done commands on (sc) */ static void amr_complete(struct amr_softc *sc) { struct amr_command *ac, *nc; int s, count; debug("called"); count = 0; s = splbio(); ac = TAILQ_FIRST(&sc->amr_work); while (ac != NULL) { nc = TAILQ_NEXT(ac, ac_link); /* Skip if command is still active */ if (ac->ac_status != AMR_STATUS_BUSY) { /* * Is there a completion handler? */ if (ac->ac_complete != NULL) { /* remove and give to completion handler */ TAILQ_REMOVE(&sc->amr_work, ac, ac_link); sc->amr_workcount--; ac->ac_complete(ac); /* * Is someone sleeping on this one? */ } else if (ac->ac_private != NULL) { /* remove and wake up */ TAILQ_REMOVE(&sc->amr_work, ac, ac_link); sc->amr_workcount--; wakeup_one(ac->ac_private); /* * Leave it for a polling caller. */ } else { } } ac = nc; } splx(s); /* queue more work if we can */ amr_startio(sc); } /******************************************************************************** ******************************************************************************** Command Buffer Management ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Get a new command buffer. * * This may return NULL in low-memory cases. * * Note that using malloc() is expensive (the command buffer is << 1 page) but * necessary if we are to be a loadable module before the zone allocator is fixed. * * If possible, we recycle a command buffer that's been used before. * * XXX Note that command buffers are not cleaned out - it is the caller's * responsibility to ensure that all required fields are filled in before * using a buffer. */ static struct amr_command * amr_alloccmd(struct amr_softc *sc) { struct amr_command *ac; int error; int s; debug("called"); s = splbio(); if ((ac = TAILQ_FIRST(&sc->amr_freecmds)) != NULL) TAILQ_REMOVE(&sc->amr_freecmds, ac, ac_link); splx(s); /* allocate a new command buffer? */ if (ac == NULL) { ac = (struct amr_command *)malloc(sizeof(*ac), M_DEVBUF, M_NOWAIT); if (ac != NULL) { bzero(ac, sizeof(*ac)); ac->ac_sc = sc; error = bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_dmamap); if (error) { free(ac, M_DEVBUF); return(NULL); } } } bzero(&ac->ac_mailbox, sizeof(struct amr_mailbox)); return(ac); } /******************************************************************************** * Release a command buffer for recycling. * * XXX It might be a good idea to limit the number of commands we save for reuse * if it's shown that this list bloats out massively. */ static void amr_releasecmd(struct amr_command *ac) { int s; debug("called"); s = splbio(); TAILQ_INSERT_HEAD(&ac->ac_sc->amr_freecmds, ac, ac_link); splx(s); } /******************************************************************************** * Permanently discard a command buffer. */ static void amr_freecmd(struct amr_command *ac) { struct amr_softc *sc = ac->ac_sc; debug("called"); bus_dmamap_destroy(sc->amr_buffer_dmat, ac->ac_dmamap); free(ac, M_DEVBUF); } /******************************************************************************** ******************************************************************************** Interface-specific Shims ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Tell the controller that the mailbox contains a valid command */ static void amr_quartz_submit_command(struct amr_softc *sc) { debug("called"); sc->amr_mailbox->mb_poll = 0; sc->amr_mailbox->mb_ack = 0; /* XXX write barrier? */ while(AMR_QGET_IDB(sc) & AMR_QIDB_SUBMIT) ; /* XXX aiee! what if it dies? */ AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_SUBMIT); } static void amr_std_submit_command(struct amr_softc *sc) { debug("called"); /* XXX write barrier? */ while (AMR_SGET_MBSTAT(sc) & AMR_SMBOX_BUSYFLAG) ; /* XXX aiee! what if it dies? */ AMR_SPOST_COMMAND(sc); } /******************************************************************************** * Claim any work that the controller has completed; acknowledge completion, * save details of the completion in (mbsave) */ static int amr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave) { int s, worked; u_int32_t outd; debug("called"); worked = 0; s = splbio(); /* work waiting for us? */ if ((outd = AMR_QGET_ODB(sc)) == AMR_QODB_READY) { AMR_QPUT_ODB(sc, AMR_QODB_READY); /* save mailbox, which contains a list of completed commands */ /* XXX read barrier? */ bcopy(sc->amr_mailbox, mbsave, sizeof(*mbsave)); /* acknowledge that we have the commands */ AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_ACK); while(AMR_QGET_IDB(sc) & AMR_QIDB_ACK) ; /* XXX aiee! what if it dies? */ worked = 1; /* got some work */ } splx(s); return(worked); } static int amr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave) { int s, worked; u_int8_t istat; debug("called"); worked = 0; s = splbio(); /* check for valid interrupt status */ istat = AMR_SGET_ISTAT(sc); if ((istat & AMR_SINTR_VALID) != 0) { AMR_SPUT_ISTAT(sc, istat); /* ack interrupt status */ /* save mailbox, which contains a list of completed commands */ /* XXX read barrier? */ bcopy(sc->amr_mailbox, mbsave, sizeof(*mbsave)); AMR_SACK_INTERRUPT(sc); /* acknowledge we have the mailbox */ worked = 1; } splx(s); return(worked); } /******************************************************************************** * Notify the controller of the mailbox location. */ static void amr_quartz_attach_mailbox(struct amr_softc *sc) { /* Quartz is given the mailbox location when a command is submitted */ } static void amr_std_attach_mailbox(struct amr_softc *sc) { /* program the mailbox physical address */ AMR_SBYTE_SET(sc, AMR_SMBOX_0, sc->amr_mailboxphys & 0xff); AMR_SBYTE_SET(sc, AMR_SMBOX_1, (sc->amr_mailboxphys >> 8) & 0xff); AMR_SBYTE_SET(sc, AMR_SMBOX_2, (sc->amr_mailboxphys >> 16) & 0xff); AMR_SBYTE_SET(sc, AMR_SMBOX_3, (sc->amr_mailboxphys >> 24) & 0xff); AMR_SBYTE_SET(sc, AMR_SMBOX_ENABLE, AMR_SMBOX_ADDR); /* clear any outstanding interrupt and enable interrupts proper */ AMR_SACK_INTERRUPT(sc); AMR_SENABLE_INTR(sc); } /******************************************************************************** ******************************************************************************** Debugging ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Print the command (ac) in human-readable format */ static void amr_printcommand(struct amr_command *ac) { struct amr_softc *sc = ac->ac_sc; struct amr_sgentry *sg; int i; device_printf(sc->amr_dev, "cmd %x ident %d drive %d\n", ac->ac_mailbox.mb_command, ac->ac_mailbox.mb_ident, ac->ac_mailbox.mb_drive); device_printf(sc->amr_dev, "blkcount %d lba %d\n", ac->ac_mailbox.mb_blkcount, ac->ac_mailbox.mb_lba); device_printf(sc->amr_dev, "virtaddr %p length %d\n", ac->ac_data, ac->ac_length); device_printf(sc->amr_dev, "physaddr %08x nsg %d\n", ac->ac_mailbox.mb_physaddr, ac->ac_mailbox.mb_nsgelem); /* get base address of s/g table */ sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG); for (i = 0; i < ac->ac_mailbox.mb_nsgelem; i++, sg++) device_printf(sc->amr_dev, " %x/%d\n", sg->sg_addr, sg->sg_count); } Index: head/sys/dev/atkbdc/atkbdc_isa.c =================================================================== --- head/sys/dev/atkbdc/atkbdc_isa.c (revision 54072) +++ head/sys/dev/atkbdc/atkbdc_isa.c (revision 54073) @@ -1,268 +1,269 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include "atkbdc.h" #include "opt_kbd.h" #if NATKBDC > 0 #include #include #include #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_ATKBDDEV, "atkbddev", "AT Keyboard device"); /* children */ typedef struct atkbdc_device { int flags; /* configuration flags */ int port; /* port number (same as the controller's) */ int irq; /* ISA IRQ mask */ } atkbdc_device_t; /* kbdc */ devclass_t atkbdc_devclass; static int atkbdc_probe(device_t dev); static int atkbdc_attach(device_t dev); static int atkbdc_print_child(device_t bus, device_t dev); static int atkbdc_read_ivar(device_t bus, device_t dev, int index, u_long *val); static int atkbdc_write_ivar(device_t bus, device_t dev, int index, u_long val); static device_method_t atkbdc_methods[] = { DEVMETHOD(device_probe, atkbdc_probe), DEVMETHOD(device_attach, atkbdc_attach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(bus_print_child, atkbdc_print_child), DEVMETHOD(bus_read_ivar, atkbdc_read_ivar), DEVMETHOD(bus_write_ivar, atkbdc_write_ivar), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t atkbdc_driver = { ATKBDC_DRIVER_NAME, atkbdc_methods, sizeof(atkbdc_softc_t *), }; static int atkbdc_probe(device_t dev) { int error; int rid; struct resource *port; /* Check isapnp ids */ if (isa_get_vendorid(dev)) return (ENXIO); device_set_desc(dev, "keyboard controller (i8042)"); rid = 0; port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, IO_KBDSIZE, RF_ACTIVE); if (!port) return ENXIO; error = atkbdc_probe_unit(device_get_unit(dev), rman_get_start(port)); bus_release_resource(dev, SYS_RES_IOPORT, rid, port); return error; } static void atkbdc_add_device(device_t dev, const char *name, int unit) { atkbdc_softc_t *sc = *(atkbdc_softc_t **)device_get_softc(dev); atkbdc_device_t *kdev; device_t child; int t; kdev = malloc(sizeof(struct atkbdc_device), M_ATKBDDEV, M_NOWAIT); if (!kdev) return; bzero(kdev, sizeof *kdev); kdev->port = sc->port; if (resource_int_value(name, unit, "irq", &t) == 0) kdev->irq = t; else kdev->irq = -1; if (resource_int_value(name, unit, "flags", &t) == 0) kdev->flags = t; else kdev->flags = 0; - child = device_add_child(dev, name, unit, kdev); + child = device_add_child(dev, name, unit); + device_set_ivars(child, kdev); } static int atkbdc_attach(device_t dev) { atkbdc_softc_t *sc; struct resource *port; int unit; int error; int rid; int i; unit = device_get_unit(dev); sc = *(atkbdc_softc_t **)device_get_softc(dev); if (sc == NULL) { /* * We have to maintain two copies of the kbdc_softc struct, * as the low-level console needs to have access to the * keyboard controller before kbdc is probed and attached. * kbdc_soft[] contains the default entry for that purpose. * See atkbdc.c. XXX */ sc = atkbdc_get_softc(unit); if (sc == NULL) return ENOMEM; } /* XXX should track resource in softc */ rid = 0; port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, IO_KBDSIZE, RF_ACTIVE); if (!port) return ENXIO; error = atkbdc_attach_unit(unit, sc, rman_get_start(port)); if (error) return error; *(atkbdc_softc_t **)device_get_softc(dev) = sc; /* * Add all devices configured to be attached to atkbdc0. */ for (i = resource_query_string(-1, "at", "atkbdc0"); i != -1; i = resource_query_string(i, "at", "atkbdc0")) { atkbdc_add_device(dev, resource_query_name(i), resource_query_unit(i)); } /* * and atkbdc? */ for (i = resource_query_string(-1, "at", "atkbdc"); i != -1; i = resource_query_string(i, "at", "atkbdc")) { atkbdc_add_device(dev, resource_query_name(i), resource_query_unit(i)); } bus_generic_attach(dev); return 0; } static int atkbdc_print_child(device_t bus, device_t dev) { atkbdc_device_t *kbdcdev; int retval = 0; kbdcdev = (atkbdc_device_t *)device_get_ivars(dev); retval += bus_print_child_header(bus, dev); if (kbdcdev->flags != 0) retval += printf(" flags 0x%x", kbdcdev->flags); if (kbdcdev->irq != -1) retval += printf(" irq %d", kbdcdev->irq); retval += bus_print_child_footer(bus, dev); return (retval); } static int atkbdc_read_ivar(device_t bus, device_t dev, int index, u_long *val) { atkbdc_device_t *ivar; ivar = (atkbdc_device_t *)device_get_ivars(dev); switch (index) { case KBDC_IVAR_PORT: *val = (u_long)ivar->port; break; case KBDC_IVAR_IRQ: *val = (u_long)ivar->irq; break; case KBDC_IVAR_FLAGS: *val = (u_long)ivar->flags; break; default: return ENOENT; } return 0; } static int atkbdc_write_ivar(device_t bus, device_t dev, int index, u_long val) { atkbdc_device_t *ivar; ivar = (atkbdc_device_t *)device_get_ivars(dev); switch (index) { case KBDC_IVAR_PORT: ivar->port = (int)val; break; case KBDC_IVAR_IRQ: ivar->irq = (int)val; break; case KBDC_IVAR_FLAGS: ivar->flags = (int)val; break; default: return ENOENT; } return 0; } DRIVER_MODULE(atkbdc, isa, atkbdc_driver, atkbdc_devclass, 0, 0); #endif /* NATKBDC > 0 */ Index: head/sys/dev/atkbdc/atkbdc_subr.c =================================================================== --- head/sys/dev/atkbdc/atkbdc_subr.c (revision 54072) +++ head/sys/dev/atkbdc/atkbdc_subr.c (revision 54073) @@ -1,268 +1,269 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include "atkbdc.h" #include "opt_kbd.h" #if NATKBDC > 0 #include #include #include #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_ATKBDDEV, "atkbddev", "AT Keyboard device"); /* children */ typedef struct atkbdc_device { int flags; /* configuration flags */ int port; /* port number (same as the controller's) */ int irq; /* ISA IRQ mask */ } atkbdc_device_t; /* kbdc */ devclass_t atkbdc_devclass; static int atkbdc_probe(device_t dev); static int atkbdc_attach(device_t dev); static int atkbdc_print_child(device_t bus, device_t dev); static int atkbdc_read_ivar(device_t bus, device_t dev, int index, u_long *val); static int atkbdc_write_ivar(device_t bus, device_t dev, int index, u_long val); static device_method_t atkbdc_methods[] = { DEVMETHOD(device_probe, atkbdc_probe), DEVMETHOD(device_attach, atkbdc_attach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(bus_print_child, atkbdc_print_child), DEVMETHOD(bus_read_ivar, atkbdc_read_ivar), DEVMETHOD(bus_write_ivar, atkbdc_write_ivar), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t atkbdc_driver = { ATKBDC_DRIVER_NAME, atkbdc_methods, sizeof(atkbdc_softc_t *), }; static int atkbdc_probe(device_t dev) { int error; int rid; struct resource *port; /* Check isapnp ids */ if (isa_get_vendorid(dev)) return (ENXIO); device_set_desc(dev, "keyboard controller (i8042)"); rid = 0; port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, IO_KBDSIZE, RF_ACTIVE); if (!port) return ENXIO; error = atkbdc_probe_unit(device_get_unit(dev), rman_get_start(port)); bus_release_resource(dev, SYS_RES_IOPORT, rid, port); return error; } static void atkbdc_add_device(device_t dev, const char *name, int unit) { atkbdc_softc_t *sc = *(atkbdc_softc_t **)device_get_softc(dev); atkbdc_device_t *kdev; device_t child; int t; kdev = malloc(sizeof(struct atkbdc_device), M_ATKBDDEV, M_NOWAIT); if (!kdev) return; bzero(kdev, sizeof *kdev); kdev->port = sc->port; if (resource_int_value(name, unit, "irq", &t) == 0) kdev->irq = t; else kdev->irq = -1; if (resource_int_value(name, unit, "flags", &t) == 0) kdev->flags = t; else kdev->flags = 0; - child = device_add_child(dev, name, unit, kdev); + child = device_add_child(dev, name, unit); + device_set_ivars(child, kdev); } static int atkbdc_attach(device_t dev) { atkbdc_softc_t *sc; struct resource *port; int unit; int error; int rid; int i; unit = device_get_unit(dev); sc = *(atkbdc_softc_t **)device_get_softc(dev); if (sc == NULL) { /* * We have to maintain two copies of the kbdc_softc struct, * as the low-level console needs to have access to the * keyboard controller before kbdc is probed and attached. * kbdc_soft[] contains the default entry for that purpose. * See atkbdc.c. XXX */ sc = atkbdc_get_softc(unit); if (sc == NULL) return ENOMEM; } /* XXX should track resource in softc */ rid = 0; port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, IO_KBDSIZE, RF_ACTIVE); if (!port) return ENXIO; error = atkbdc_attach_unit(unit, sc, rman_get_start(port)); if (error) return error; *(atkbdc_softc_t **)device_get_softc(dev) = sc; /* * Add all devices configured to be attached to atkbdc0. */ for (i = resource_query_string(-1, "at", "atkbdc0"); i != -1; i = resource_query_string(i, "at", "atkbdc0")) { atkbdc_add_device(dev, resource_query_name(i), resource_query_unit(i)); } /* * and atkbdc? */ for (i = resource_query_string(-1, "at", "atkbdc"); i != -1; i = resource_query_string(i, "at", "atkbdc")) { atkbdc_add_device(dev, resource_query_name(i), resource_query_unit(i)); } bus_generic_attach(dev); return 0; } static int atkbdc_print_child(device_t bus, device_t dev) { atkbdc_device_t *kbdcdev; int retval = 0; kbdcdev = (atkbdc_device_t *)device_get_ivars(dev); retval += bus_print_child_header(bus, dev); if (kbdcdev->flags != 0) retval += printf(" flags 0x%x", kbdcdev->flags); if (kbdcdev->irq != -1) retval += printf(" irq %d", kbdcdev->irq); retval += bus_print_child_footer(bus, dev); return (retval); } static int atkbdc_read_ivar(device_t bus, device_t dev, int index, u_long *val) { atkbdc_device_t *ivar; ivar = (atkbdc_device_t *)device_get_ivars(dev); switch (index) { case KBDC_IVAR_PORT: *val = (u_long)ivar->port; break; case KBDC_IVAR_IRQ: *val = (u_long)ivar->irq; break; case KBDC_IVAR_FLAGS: *val = (u_long)ivar->flags; break; default: return ENOENT; } return 0; } static int atkbdc_write_ivar(device_t bus, device_t dev, int index, u_long val) { atkbdc_device_t *ivar; ivar = (atkbdc_device_t *)device_get_ivars(dev); switch (index) { case KBDC_IVAR_PORT: ivar->port = (int)val; break; case KBDC_IVAR_IRQ: ivar->irq = (int)val; break; case KBDC_IVAR_FLAGS: ivar->flags = (int)val; break; default: return ENOENT; } return 0; } DRIVER_MODULE(atkbdc, isa, atkbdc_driver, atkbdc_devclass, 0, 0); #endif /* NATKBDC > 0 */ Index: head/sys/dev/bktr/bktr_i2c.c =================================================================== --- head/sys/dev/bktr/bktr_i2c.c (revision 54072) +++ head/sys/dev/bktr/bktr_i2c.c (revision 54073) @@ -1,417 +1,417 @@ /*- * Copyright (c) 1998 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * */ /* * I2C support for the bti2c chipset. * * From brooktree848.c */ #include "bktr.h" #include "smbus.h" #if (NBKTR > 0 && NSMBUS > 0) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* extensions to ioctl_meteor.h */ #include #include #include #include #include #include "iicbb_if.h" #include "smbus_if.h" #define I2C_DELAY 40 #define BTI2C_DEBUG(x) if (bti2c_debug) (x) static int bti2c_debug = 0; struct bti2c_softc { bt848_ptr_t base; int iic_owned; /* 1 if we own the iicbus */ int smb_owned; /* 1 if we own the smbbus */ device_t smbus; device_t iicbus; }; struct bt_data { bt848_ptr_t base; }; struct bt_data btdata[NBKTR]; static int bti2c_probe(device_t); static int bti2c_attach(device_t); static int bti2c_iic_callback(device_t, int, caddr_t *); static void bti2c_iic_setlines(device_t, int, int); static int bti2c_iic_getdataline(device_t); static int bti2c_iic_reset(device_t, u_char, u_char, u_char *); static int bti2c_smb_callback(device_t, int, caddr_t *); static int bti2c_smb_writeb(device_t dev, u_char slave, char cmd, char byte); static int bti2c_smb_writew(device_t dev, u_char slave, char cmd, short word); static int bti2c_smb_readb(device_t dev, u_char slave, char cmd, char *byte); static devclass_t bti2c_devclass; static device_method_t bti2c_methods[] = { /* device interface */ DEVMETHOD(device_probe, bti2c_probe), DEVMETHOD(device_attach, bti2c_attach), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), /* iicbb interface */ DEVMETHOD(iicbb_callback, bti2c_iic_callback), DEVMETHOD(iicbb_setlines, bti2c_iic_setlines), DEVMETHOD(iicbb_getdataline, bti2c_iic_getdataline), DEVMETHOD(iicbb_reset, bti2c_iic_reset), /* smbus interface */ DEVMETHOD(smbus_callback, bti2c_smb_callback), DEVMETHOD(smbus_writeb, bti2c_smb_writeb), DEVMETHOD(smbus_writew, bti2c_smb_writew), DEVMETHOD(smbus_readb, bti2c_smb_readb), { 0, 0 } }; #if (__FreeBSD_version < 400000) /* FreeBSD 3.x needs DRIVER_TYPE_MISC */ static driver_t bti2c_driver = { "bti2c", bti2c_methods, DRIVER_TYPE_MISC, sizeof(struct bti2c_softc), }; #endif #if (__FreeBSD_version >=400000) static driver_t bti2c_driver = { "bti2c", bti2c_methods, sizeof(struct bti2c_softc), }; #endif /* * Call this to pass the base address of the bktr device to the * bti2c_i2c layer and initialize all the I2C bus architecture */ int bt848_i2c_attach(int unit, bt848_ptr_t base, struct bktr_i2c_softc *i2c_sc) { device_t interface; device_t bitbang; btdata[unit].base = base; /* XXX add the I2C interface to the root_bus until pcibus is ready */ - interface = device_add_child(root_bus, "bti2c", unit, NULL); + interface = device_add_child(root_bus, "bti2c", unit); /* add bit-banging generic code onto bti2c interface */ - bitbang = device_add_child(interface, "iicbb", -1, NULL); + bitbang = device_add_child(interface, "iicbb", -1); /* probe and attach the interface, we need it NOW * bit-banging code is also probed and attached */ device_probe_and_attach(interface); device_probe_and_attach(bitbang); /* smb and i2c interfaces are available for the bt848 chip * connect bit-banging generic code to an iicbus */ if ((i2c_sc->iicbus = iicbus_alloc_bus(bitbang))) device_probe_and_attach(i2c_sc->iicbus); /* hardware i2c is actually smb over the bti2c interface */ if ((i2c_sc->smbus = smbus_alloc_bus(interface))) device_probe_and_attach(i2c_sc->smbus); return (0); }; /* * Not a real probe, we know the device exists since the device has * been added after the successfull pci probe. */ static int bti2c_probe(device_t dev) { device_set_desc(dev, "bt848 Hard/Soft I2C controller"); return (0); } static int bti2c_attach(device_t dev) { struct bti2c_softc *sc = (struct bti2c_softc *)device_get_softc(dev); /* XXX should use ivars with pcibus or pcibus methods to access * onboard memory */ sc->base = btdata[device_get_unit(dev)].base; return (0); } static int bti2c_smb_callback(device_t dev, int index, caddr_t *data) { struct bti2c_softc *sc = (struct bti2c_softc *)device_get_softc(dev); int error = 0; int how; /* test each time if we already have/haven't the iicbus * to avoid deadlocks */ switch (index) { case SMB_REQUEST_BUS: if (!sc->iic_owned) { /* request the iicbus */ how = *(int *)data; error = iicbus_request_bus(sc->iicbus, dev, how); if (!error) sc->iic_owned = 1; } break; case SMB_RELEASE_BUS: if (sc->iic_owned) { /* release the iicbus */ error = iicbus_release_bus(sc->iicbus, dev); if (!error) sc->iic_owned = 0; } break; default: error = EINVAL; } return (error); } static int bti2c_iic_callback(device_t dev, int index, caddr_t *data) { struct bti2c_softc *sc = (struct bti2c_softc *)device_get_softc(dev); int error = 0; int how; /* test each time if we already have/haven't the smbus * to avoid deadlocks */ switch (index) { case IIC_REQUEST_BUS: if (!sc->smb_owned) { /* request the smbus */ how = *(int *)data; error = smbus_request_bus(sc->smbus, dev, how); if (!error) sc->smb_owned = 1; } break; case IIC_RELEASE_BUS: if (sc->smb_owned) { /* release the smbus */ error = smbus_release_bus(sc->smbus, dev); if (!error) sc->smb_owned = 0; } break; default: error = EINVAL; } return (error); } static int bti2c_iic_reset(device_t dev, u_char speed, u_char addr, u_char * oldaddr) { if (oldaddr) *oldaddr = 0; /* XXX */ return (IIC_ENOADDR); } static void bti2c_iic_setlines(device_t dev, int ctrl, int data) { struct bti2c_softc *sc = (struct bti2c_softc *)device_get_softc(dev); bt848_ptr_t bti2c; bti2c = sc->base; bti2c->i2c_data_ctl = (ctrl << 1) | data; DELAY(I2C_DELAY); return; } static int bti2c_iic_getdataline(device_t dev) { struct bti2c_softc *sc = (struct bti2c_softc *)device_get_softc(dev); bt848_ptr_t bti2c; bti2c = sc->base; return (bti2c->i2c_data_ctl & 0x1); } static int bti2c_write(bt848_ptr_t bti2c, u_long data) { u_long x; /* clear status bits */ bti2c->int_stat = (BT848_INT_RACK | BT848_INT_I2CDONE); BTI2C_DEBUG(printf("w%lx", data)); /* write the address and data */ bti2c->i2c_data_ctl = data; /* wait for completion */ for ( x = 0x7fffffff; x; --x ) { /* safety valve */ if ( bti2c->int_stat & BT848_INT_I2CDONE ) break; } /* check for ACK */ if ( !x || !(bti2c->int_stat & BT848_INT_RACK) ) { BTI2C_DEBUG(printf("%c%c", (!x)?'+':'-', (!(bti2c->int_stat & BT848_INT_RACK))?'+':'-')); return (SMB_ENOACK); } BTI2C_DEBUG(printf("+")); /* return OK */ return( 0 ); } static int bti2c_smb_writeb(device_t dev, u_char slave, char cmd, char byte) { struct bti2c_softc *sc = (struct bti2c_softc *)device_get_softc(dev); u_long data; data = ((slave & 0xff) << 24) | ((byte & 0xff) << 16) | (u_char)cmd; return (bti2c_write(sc->base, data)); } /* * byte1 becomes low byte of word * byte2 becomes high byte of word */ static int bti2c_smb_writew(device_t dev, u_char slave, char cmd, short word) { struct bti2c_softc *sc = (struct bti2c_softc *)device_get_softc(dev); u_long data; char low, high; low = (char)(word & 0xff); high = (char)((word & 0xff00) >> 8); data = ((slave & 0xff) << 24) | ((low & 0xff) << 16) | ((high & 0xff) << 8) | BT848_DATA_CTL_I2CW3B | (u_char)cmd; return (bti2c_write(sc->base, data)); } /* * The Bt878 and Bt879 differed on the treatment of i2c commands */ static int bti2c_smb_readb(device_t dev, u_char slave, char cmd, char *byte) { struct bti2c_softc *sc = (struct bti2c_softc *)device_get_softc(dev); bt848_ptr_t bti2c; u_long x; bti2c = sc->base; /* clear status bits */ bti2c->int_stat = (BT848_INT_RACK | BT848_INT_I2CDONE); bti2c->i2c_data_ctl = ((slave & 0xff) << 24) | (u_char)cmd; BTI2C_DEBUG(printf("r%lx/", (u_long)(((slave & 0xff) << 24) | (u_char)cmd))); /* wait for completion */ for ( x = 0x7fffffff; x; --x ) { /* safety valve */ if ( bti2c->int_stat & BT848_INT_I2CDONE ) break; } /* check for ACK */ if ( !x || !(bti2c->int_stat & BT848_INT_RACK) ) { BTI2C_DEBUG(printf("r%c%c", (!x)?'+':'-', (!(bti2c->int_stat & BT848_INT_RACK))?'+':'-')); return (SMB_ENOACK); } *byte = (char)((bti2c->i2c_data_ctl >> 8) & 0xff); BTI2C_DEBUG(printf("r%x+", *byte)); return (0); } DRIVER_MODULE(bti2c, root, bti2c_driver, bti2c_devclass, 0, 0); #endif Index: head/sys/dev/eisa/eisaconf.c =================================================================== --- head/sys/dev/eisa/eisaconf.c (revision 54072) +++ head/sys/dev/eisa/eisaconf.c (revision 54073) @@ -1,624 +1,626 @@ /* * EISA bus probe and attach routines * * Copyright (c) 1995, 1996 Justin T. Gibbs. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice immediately at the beginning of the file, without modification, * this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "opt_eisa.h" #include #include #include #include #include #include #include #include #include #include #include #include typedef struct resvaddr { u_long addr; /* start address */ u_long size; /* size of reserved area */ int flags; struct resource *res; /* resource manager handle */ LIST_ENTRY(resvaddr) links; /* List links */ } resvaddr_t; LIST_HEAD(resvlist, resvaddr); struct irq_node { int irq_no; int irq_trigger; void *idesc; TAILQ_ENTRY(irq_node) links; }; TAILQ_HEAD(irqlist, irq_node); struct eisa_ioconf { int slot; struct resvlist ioaddrs; /* list of reserved I/O ranges */ struct resvlist maddrs; /* list of reserved memory ranges */ struct irqlist irqs; /* list of reserved irqs */ }; /* To be replaced by the "super device" generic device structure... */ struct eisa_device { eisa_id_t id; struct eisa_ioconf ioconf; }; /* Global variable, so UserConfig can change it. */ #define MAX_COL 79 #ifndef EISA_SLOTS #define EISA_SLOTS 10 /* PCI clashes with higher ones.. fix later */ #endif int num_eisa_slots = EISA_SLOTS; static devclass_t eisa_devclass; static void eisa_reg_print (device_t, char *, char *, int *); static struct irq_node * eisa_find_irq(struct eisa_device *e_dev, int rid); static struct resvaddr * eisa_find_maddr(struct eisa_device *e_dev, int rid); static struct resvaddr * eisa_find_ioaddr(struct eisa_device *e_dev, int rid); static int mainboard_probe(device_t dev) { char *idstring; eisa_id_t id = eisa_get_id(dev); if (eisa_get_slot(dev) != 0) return (ENXIO); idstring = (char *)malloc(8 + sizeof(" (System Board)") + 1, M_DEVBUF, M_NOWAIT); if (idstring == NULL) { panic("Eisa probe unable to malloc"); } sprintf(idstring, "%c%c%c%03x%01x (System Board)", EISA_MFCTR_CHAR0(id), EISA_MFCTR_CHAR1(id), EISA_MFCTR_CHAR2(id), EISA_PRODUCT_ID(id), EISA_REVISION_ID(id)); device_set_desc(dev, idstring); return (0); } static int mainboard_attach(device_t dev) { return (0); } static device_method_t mainboard_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mainboard_probe), DEVMETHOD(device_attach, mainboard_attach), { 0, 0 } }; static driver_t mainboard_driver = { "mainboard", mainboard_methods, 1, }; static devclass_t mainboard_devclass; DRIVER_MODULE(mainboard, eisa, mainboard_driver, mainboard_devclass, 0, 0); /* ** probe for EISA devices */ static int eisa_probe(device_t dev) { int i,slot; struct eisa_device *e_dev; + device_t child; int eisaBase = 0xc80; eisa_id_t eisa_id; int devices_found = 0; device_set_desc(dev, "EISA bus"); for (slot = 0; slot < num_eisa_slots; eisaBase+=0x1000, slot++) { int id_size = sizeof(eisa_id); eisa_id = 0; for( i = 0; i < id_size; i++ ) { outb(eisaBase,0x80 + i); /*Some cards require priming*/ eisa_id |= inb(eisaBase+i) << ((id_size-i-1)*CHAR_BIT); } if (eisa_id & 0x80000000) continue; /* no EISA card in slot */ devices_found++; /* Prepare an eisa_device_node for this slot */ e_dev = (struct eisa_device *)malloc(sizeof(*e_dev), M_DEVBUF, M_NOWAIT); if (!e_dev) { device_printf(dev, "cannot malloc eisa_device"); break; /* Try to attach what we have already */ } bzero(e_dev, sizeof(*e_dev)); e_dev->id = eisa_id; e_dev->ioconf.slot = slot; /* Initialize our lists of reserved addresses */ LIST_INIT(&(e_dev->ioconf.ioaddrs)); LIST_INIT(&(e_dev->ioconf.maddrs)); TAILQ_INIT(&(e_dev->ioconf.irqs)); - device_add_child(dev, NULL, -1, e_dev); + child = device_add_child(dev, NULL, -1); + device_set_ivars(child, e_dev); } /* * EISA busses themselves are not easily detectable, the easiest way * to tell if there is an eisa bus is if we found something - there * should be a motherboard "card" there somewhere. */ return devices_found ? 0 : ENXIO; } static void eisa_probe_nomatch(device_t dev, device_t child) { u_int32_t eisa_id = eisa_get_id(child); u_int8_t slot = eisa_get_slot(child); device_printf(dev, "unknown card %c%c%c%03x%01x (0x%08x) at slot %d\n", EISA_MFCTR_CHAR0(eisa_id), EISA_MFCTR_CHAR1(eisa_id), EISA_MFCTR_CHAR2(eisa_id), EISA_PRODUCT_ID(eisa_id), EISA_REVISION_ID(eisa_id), eisa_id, slot); return; } static void eisa_reg_print (dev, string, separator, column) device_t dev; char * string; char * separator; int * column; { int length = strlen(string); length += (separator ? 2 : 1); if (((*column) + length) >= MAX_COL) { printf("\n"); (*column) = 0; } else if ((*column) != 0) { if (separator) { printf("%c", *separator); (*column)++; } printf(" "); (*column)++; } if ((*column) == 0) { (*column) += device_printf(dev, "%s", string); } else { (*column) += printf("%s", string); } return; } static int eisa_print_child(device_t dev, device_t child) { char buf[81]; struct eisa_device * e_dev = device_get_ivars(child); int rid; struct irq_node * irq; struct resvaddr * resv; char separator = ','; int column = 0; int retval = 0; if (device_get_desc(child)) { snprintf(buf, sizeof(buf), "<%s>", device_get_desc(child)); eisa_reg_print(child, buf, NULL, &column); } rid = 0; while ((resv = eisa_find_ioaddr(e_dev, rid++))) { if ((resv->size == 1) || (resv->flags & RESVADDR_BITMASK)) { snprintf(buf, sizeof(buf), "%s%lx", ((rid == 1) ? "at 0x" : "0x"), resv->addr); } else { snprintf(buf, sizeof(buf), "%s%lx-0x%lx", ((rid == 1) ? "at 0x" : "0x"), resv->addr, (resv->addr + (resv->size - 1))); } eisa_reg_print(child, buf, ((rid == 2) ? &separator : NULL), &column); } rid = 0; while ((resv = eisa_find_maddr(e_dev, rid++))) { if ((resv->size == 1) || (resv->flags & RESVADDR_BITMASK)) { snprintf(buf, sizeof(buf), "%s%lx", ((rid == 1) ? "at 0x" : "0x"), resv->addr); } else { snprintf(buf, sizeof(buf), "%s%lx-0x%lx", ((rid == 1) ? "at 0x" : "0x"), resv->addr, (resv->addr + (resv->size - 1))); } eisa_reg_print(child, buf, ((rid == 2) ? &separator : NULL), &column); } rid = 0; while ((irq = eisa_find_irq(e_dev, rid++)) != NULL) { snprintf(buf, sizeof(buf), "irq %d (%s)", irq->irq_no, (irq->irq_trigger ? "level" : "edge")); eisa_reg_print(child, buf, ((rid == 1) ? &separator : NULL), &column); } snprintf(buf, sizeof(buf), "on %s slot %d\n", device_get_nameunit(dev), eisa_get_slot(child)); eisa_reg_print(child, buf, NULL, &column); return (retval); } static struct irq_node * eisa_find_irq(struct eisa_device *e_dev, int rid) { int i; struct irq_node *irq; for (i = 0, irq = TAILQ_FIRST(&e_dev->ioconf.irqs); i < rid && irq; i++, irq = TAILQ_NEXT(irq, links)) ; if (irq) return (irq); else return (NULL); } static struct resvaddr * eisa_find_maddr(struct eisa_device *e_dev, int rid) { int i; struct resvaddr *resv; for (i = 0, resv = LIST_FIRST(&e_dev->ioconf.maddrs); i < rid && resv; i++, resv = LIST_NEXT(resv, links)) ; return resv; } static struct resvaddr * eisa_find_ioaddr(struct eisa_device *e_dev, int rid) { int i; struct resvaddr *resv; for (i = 0, resv = LIST_FIRST(&e_dev->ioconf.ioaddrs); i < rid && resv; i++, resv = LIST_NEXT(resv, links)) ; return resv; } static int eisa_read_ivar(device_t dev, device_t child, int which, u_long *result) { struct eisa_device *e_dev = device_get_ivars(child); struct irq_node *irq; switch (which) { case EISA_IVAR_SLOT: *result = e_dev->ioconf.slot; break; case EISA_IVAR_ID: *result = e_dev->id; break; case EISA_IVAR_IRQ: /* XXX only first irq */ if ((irq = eisa_find_irq(e_dev, 0)) != NULL) { *result = irq->irq_no; } else { *result = -1; } break; default: return (ENOENT); } return (0); } static int eisa_write_ivar(device_t dev, device_t child, int which, uintptr_t value) { return (EINVAL); } static struct resource * eisa_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { int isdefault; struct eisa_device *e_dev = device_get_ivars(child); struct resource *rv, **rvp = 0; isdefault = (device_get_parent(child) == dev && start == 0UL && end == ~0UL && count == 1); switch (type) { case SYS_RES_IRQ: if (isdefault) { struct irq_node * irq = eisa_find_irq(e_dev, *rid); if (irq == NULL) return 0; start = end = irq->irq_no; count = 1; if (irq->irq_trigger == EISA_TRIGGER_LEVEL) { flags |= RF_SHAREABLE; } else { flags &= ~RF_SHAREABLE; } } break; case SYS_RES_MEMORY: if (isdefault) { struct resvaddr *resv; resv = eisa_find_maddr(e_dev, *rid); if (!resv) return 0; start = resv->addr; end = resv->addr + (resv->size - 1); count = resv->size; rvp = &resv->res; } break; case SYS_RES_IOPORT: if (isdefault) { struct resvaddr *resv; resv = eisa_find_ioaddr(e_dev, *rid); if (!resv) return 0; start = resv->addr; end = resv->addr + (resv->size - 1); count = resv->size; rvp = &resv->res; } break; default: return 0; } rv = BUS_ALLOC_RESOURCE(device_get_parent(dev), child, type, rid, start, end, count, flags); if (rvp) *rvp = rv; return rv; } static int eisa_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { int rv; struct eisa_device *e_dev = device_get_ivars(child); struct resvaddr *resv = 0; switch (type) { case SYS_RES_IRQ: if (eisa_find_irq(e_dev, rid) == NULL) return EINVAL; break; case SYS_RES_MEMORY: if (device_get_parent(child) == dev) resv = eisa_find_maddr(e_dev, rid); break; case SYS_RES_IOPORT: if (device_get_parent(child) == dev) resv = eisa_find_ioaddr(e_dev, rid); break; default: return (ENOENT); } rv = BUS_RELEASE_RESOURCE(device_get_parent(dev), child, type, rid, r); if (rv == 0) { if (resv) resv->res = 0; } return rv; } int eisa_add_intr(device_t dev, int irq, int trigger) { struct eisa_device *e_dev = device_get_ivars(dev); struct irq_node *irq_info; irq_info = (struct irq_node *)malloc(sizeof(*irq_info), M_DEVBUF, M_NOWAIT); if (irq_info == NULL) return (1); irq_info->irq_no = irq; irq_info->irq_trigger = trigger; irq_info->idesc = NULL; TAILQ_INSERT_TAIL(&e_dev->ioconf.irqs, irq_info, links); return 0; } static int eisa_add_resvaddr(struct eisa_device *e_dev, struct resvlist *head, u_long base, u_long size, int flags) { resvaddr_t *reservation; reservation = (resvaddr_t *)malloc(sizeof(resvaddr_t), M_DEVBUF, M_NOWAIT); if(!reservation) return (ENOMEM); reservation->addr = base; reservation->size = size; reservation->flags = flags; if (!head->lh_first) { LIST_INSERT_HEAD(head, reservation, links); } else { resvaddr_t *node; for(node = head->lh_first; node; node = node->links.le_next) { if (node->addr > reservation->addr) { /* * List is sorted in increasing * address order. */ LIST_INSERT_BEFORE(node, reservation, links); break; } if (node->addr == reservation->addr) { /* * If the entry we want to add * matches any already in here, * fail. */ free(reservation, M_DEVBUF); return (EEXIST); } if (!node->links.le_next) { LIST_INSERT_AFTER(node, reservation, links); break; } } } return (0); } int eisa_add_mspace(device_t dev, u_long mbase, u_long msize, int flags) { struct eisa_device *e_dev = device_get_ivars(dev); return eisa_add_resvaddr(e_dev, &(e_dev->ioconf.maddrs), mbase, msize, flags); } int eisa_add_iospace(device_t dev, u_long iobase, u_long iosize, int flags) { struct eisa_device *e_dev = device_get_ivars(dev); return eisa_add_resvaddr(e_dev, &(e_dev->ioconf.ioaddrs), iobase, iosize, flags); } static device_method_t eisa_methods[] = { /* Device interface */ DEVMETHOD(device_probe, eisa_probe), DEVMETHOD(device_attach, bus_generic_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, eisa_print_child), DEVMETHOD(bus_probe_nomatch, eisa_probe_nomatch), DEVMETHOD(bus_read_ivar, eisa_read_ivar), DEVMETHOD(bus_write_ivar, eisa_write_ivar), DEVMETHOD(bus_driver_added, bus_generic_driver_added), DEVMETHOD(bus_alloc_resource, eisa_alloc_resource), DEVMETHOD(bus_release_resource, eisa_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t eisa_driver = { "eisa", eisa_methods, 1, /* no softc */ }; DRIVER_MODULE(eisa, isab, eisa_driver, eisa_devclass, 0, 0); DRIVER_MODULE(eisa, nexus, eisa_driver, eisa_devclass, 0, 0); Index: head/sys/dev/fdc/fdc.c =================================================================== --- head/sys/dev/fdc/fdc.c (revision 54072) +++ head/sys/dev/fdc/fdc.c (revision 54073) @@ -1,2357 +1,2358 @@ /* * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Don Ahn. * * Libretto PCMCIA floppy support by David Horwitt (dhorwitt@ucsd.edu) * aided by the Linux floppy driver modifications from David Bateman * (dbateman@eng.uts.edu.au). * * Copyright (c) 1993, 1994 by * jc@irbs.UUCP (John Capo) * vak@zebub.msk.su (Serge Vakulenko) * ache@astral.msk.su (Andrew A. Chernov) * * Copyright (c) 1993, 1994, 1995 by * joerg_wunsch@uriah.sax.de (Joerg Wunsch) * dufault@hda.com (Peter Dufault) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 * $FreeBSD$ * */ #include "opt_fdc.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef FDC_YE #undef FDC_YE #warning "fix FDC_YE! - newbus casualty" #endif /* misuse a flag to identify format operation */ #define B_FORMAT B_XXX /* configuration flags */ #define FDC_PRETEND_D0 (1 << 0) /* pretend drive 0 to be there */ #ifdef FDC_YE #define FDC_IS_PCMCIA (1 << 1) /* if successful probe, then it's a PCMCIA device */ #endif /* internally used only, not really from CMOS: */ #define RTCFDT_144M_PRETENDED 0x1000 /* error returns for fd_cmd() */ #define FD_FAILED -1 #define FD_NOT_VALID -2 #define FDC_ERRMAX 100 /* do not log more */ #define NUMTYPES 14 #define NUMDENS (NUMTYPES - 6) /* These defines (-1) must match index for fd_types */ #define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ #define FD_1720 1 #define FD_1480 2 #define FD_1440 3 #define FD_1200 4 #define FD_820 5 #define FD_800 6 #define FD_720 7 #define FD_360 8 #define FD_1480in5_25 9 #define FD_1440in5_25 10 #define FD_820in5_25 11 #define FD_800in5_25 12 #define FD_720in5_25 13 #define FD_360in5_25 14 static struct fd_type fd_types[NUMTYPES] = { { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */ { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */ { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */ { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */ { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */ { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */ { 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */ { 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */ { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */ { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */ { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */ { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */ { 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */ { 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */ }; #define DRVS_PER_CTLR 2 /* 2 floppies */ /***********************************************************************\ * Per controller structure. * \***********************************************************************/ static devclass_t fdc_devclass; /***********************************************************************\ * Per drive structure. * * N per controller (DRVS_PER_CTLR) * \***********************************************************************/ struct fd_data { struct fdc_data *fdc; /* pointer to controller structure */ int fdsu; /* this units number on this controller */ int type; /* Drive type (FD_1440...) */ struct fd_type *ft; /* pointer to the type descriptor */ int flags; #define FD_OPEN 0x01 /* it's open */ #define FD_ACTIVE 0x02 /* it's active */ #define FD_MOTOR 0x04 /* motor should be on */ #define FD_MOTOR_WAIT 0x08 /* motor coming up */ int skip; int hddrv; #define FD_NO_TRACK -2 int track; /* where we think the head is */ int options; /* user configurable options, see ioctl_fd.h */ struct callout_handle toffhandle; struct callout_handle tohandle; struct devstat device_stats; device_t dev; fdu_t fdu; }; static devclass_t fd_devclass; /***********************************************************************\ * Throughout this file the following conventions will be used: * * fd is a pointer to the fd_data struct for the drive in question * * fdc is a pointer to the fdc_data struct for the controller * * fdu is the floppy drive unit number * * fdcu is the floppy controller unit number * * fdsu is the floppy drive unit number on that controller. (sub-unit) * \***********************************************************************/ #ifdef FDC_YE #include "card.h" static int yeattach(struct isa_device *); #endif /* needed for ft driver, thus exported */ int in_fdc(struct fdc_data *); int out_fdc(struct fdc_data *, int); /* internal functions */ static void fdc_add_device(device_t, const char *, int); static void fdc_intr(void *); static void set_motor(struct fdc_data *, int, int); # define TURNON 1 # define TURNOFF 0 static timeout_t fd_turnoff; static timeout_t fd_motor_on; static void fd_turnon(struct fd_data *); static void fdc_reset(fdc_p); static int fd_in(struct fdc_data *, int *); static void fdstart(struct fdc_data *); static timeout_t fd_iotimeout; static timeout_t fd_pseudointr; static int fdstate(struct fdc_data *); static int retrier(struct fdc_data *); static int fdformat(dev_t, struct fd_formb *, struct proc *); static int enable_fifo(fdc_p fdc); static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */ #define DEVIDLE 0 #define FINDWORK 1 #define DOSEEK 2 #define SEEKCOMPLETE 3 #define IOCOMPLETE 4 #define RECALCOMPLETE 5 #define STARTRECAL 6 #define RESETCTLR 7 #define SEEKWAIT 8 #define RECALWAIT 9 #define MOTORWAIT 10 #define IOTIMEDOUT 11 #define RESETCOMPLETE 12 #ifdef FDC_YE #define PIOREAD 13 #endif #ifdef FDC_DEBUG static char const * const fdstates[] = { "DEVIDLE", "FINDWORK", "DOSEEK", "SEEKCOMPLETE", "IOCOMPLETE", "RECALCOMPLETE", "STARTRECAL", "RESETCTLR", "SEEKWAIT", "RECALWAIT", "MOTORWAIT", "IOTIMEDOUT", "RESETCOMPLETE", #ifdef FDC_YE "PIOREAD", #endif }; /* CAUTION: fd_debug causes huge amounts of logging output */ static int volatile fd_debug = 0; #define TRACE0(arg) if(fd_debug) printf(arg) #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) #else /* FDC_DEBUG */ #define TRACE0(arg) #define TRACE1(arg1, arg2) #endif /* FDC_DEBUG */ static void fdout_wr(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->portt, fdc->porth, FDOUT+fdc->port_off, v); } static u_int8_t fdsts_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDSTS+fdc->port_off); } static void fddata_wr(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off, v); } static u_int8_t fddata_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off); } static void fdctl_wr(fdc_p fdc, u_int8_t v) { if (fdc->flags & FDC_ISPNP) bus_space_write_1(fdc->ctlt, fdc->ctlh, 0, v); else bus_space_write_1(fdc->portt, fdc->porth, FDCTL, v); } #if 0 static u_int8_t fdin_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDIN); } #endif #ifdef FDC_YE #if NCARD > 0 #include #include #include #include #include /* * PC-Card (PCMCIA) specific code. */ static int yeinit(struct pccard_devinfo *); /* init device */ static void yeunload(struct pccard_devinfo *); /* Disable driver */ static int yeintr(struct pccard_devinfo *); /* Interrupt handler */ PCCARD_MODULE(fdc, yeinit, yeunload, yeintr, 0, bio_imask); /* * this is the secret PIO data port (offset from base) */ #define FDC_YE_DATAPORT 6 /* * Initialize the device - called from Slot manager. */ static int yeinit(struct pccard_devinfo *devi) { fdc_p fdc = &fdc_data[devi->isahd.id_unit]; fdc->baseport = devi->isahd.id_iobase; /* * reset controller */ fdout_wr(fdc, 0); DELAY(100); fdout_wr(fdc, FDO_FRST); /* * wire into system */ if (yeattach(&devi->isahd) == 0) return(ENXIO); return(0); } /* * yeunload - unload the driver and clear the table. * XXX TODO: * This is usually called when the card is ejected, but * can be caused by a modunload of a controller driver. * The idea is to reset the driver's view of the device * and ensure that any driver entry points such as * read and write do not hang. */ static void yeunload(struct pccard_devinfo *devi) { if (fd_data[devi->isahd.id_unit].type == NO_TYPE) return; /* * this prevents Fdopen() and fdstrategy() from attempting * to access unloaded controller */ fd_data[devi->isahd.id_unit].type = NO_TYPE; printf("fdc%d: unload\n", devi->isahd.id_unit); } /* * yeintr - Shared interrupt called from * front end of PC-Card handler. */ static int yeintr(struct pccard_devinfo *devi) { fdintr((fdcu_t)devi->isahd.id_unit); return(1); } #endif /* NCARD > 0 */ #endif /* FDC_YE */ static d_open_t Fdopen; /* NOTE, not fdopen */ static d_close_t fdclose; static d_ioctl_t fdioctl; static d_strategy_t fdstrategy; #define CDEV_MAJOR 9 #define BDEV_MAJOR 2 static struct cdevsw fd_cdevsw = { /* open */ Fdopen, /* close */ fdclose, /* read */ physread, /* write */ physwrite, /* ioctl */ fdioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ fdstrategy, /* name */ "fd", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ D_DISK, /* bmaj */ BDEV_MAJOR }; static int fdc_err(struct fdc_data *fdc, const char *s) { fdc->fdc_errs++; if (s) { if (fdc->fdc_errs < FDC_ERRMAX) { device_print_prettyname(fdc->fdc_dev); printf("%s", s); } else if (fdc->fdc_errs == FDC_ERRMAX) { device_print_prettyname(fdc->fdc_dev); printf("too many errors, not logging any more\n"); } } return FD_FAILED; } /* * fd_cmd: Send a command to the chip. Takes a varargs with this structure: * Unit number, * # of output bytes, output bytes as ints ..., * # of input bytes, input bytes as ints ... */ static int fd_cmd(struct fdc_data *fdc, int n_out, ...) { u_char cmd; int n_in; int n; va_list ap; va_start(ap, n_out); cmd = (u_char)(va_arg(ap, int)); va_end(ap); va_start(ap, n_out); for (n = 0; n < n_out; n++) { if (out_fdc(fdc, va_arg(ap, int)) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %x failed at out byte %d of %d\n", cmd, n + 1, n_out); return fdc_err(fdc, msg); } } n_in = va_arg(ap, int); for (n = 0; n < n_in; n++) { int *ptr = va_arg(ap, int *); if (fd_in(fdc, ptr) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %02x failed at in byte %d of %d\n", cmd, n + 1, n_in); return fdc_err(fdc, msg); } } return 0; } static int enable_fifo(fdc_p fdc) { int i, j; if ((fdc->flags & FDC_HAS_FIFO) == 0) { /* * XXX: * Cannot use fd_cmd the normal way here, since * this might be an invalid command. Thus we send the * first byte, and check for an early turn of data directon. */ if (out_fdc(fdc, I8207X_CONFIGURE) < 0) return fdc_err(fdc, "Enable FIFO failed\n"); /* If command is invalid, return */ j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO | NE7_RQM)) != NE7_RQM && j-- > 0) if (i == (NE7_DIO | NE7_RQM)) { fdc_reset(fdc); return FD_FAILED; } if (j<0 || fd_cmd(fdc, 3, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) { fdc_reset(fdc); return fdc_err(fdc, "Enable FIFO failed\n"); } fdc->flags |= FDC_HAS_FIFO; return 0; } if (fd_cmd(fdc, 4, I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) return fdc_err(fdc, "Re-enable FIFO failed\n"); return 0; } static int fd_sense_drive_status(fdc_p fdc, int *st3p) { int st3; if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) { return fdc_err(fdc, "Sense Drive Status failed\n"); } if (st3p) *st3p = st3; return 0; } static int fd_sense_int(fdc_p fdc, int *st0p, int *cylp) { int cyl, st0, ret; ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0); if (ret) { (void)fdc_err(fdc, "sense intr err reading stat reg 0\n"); return ret; } if (st0p) *st0p = st0; if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) { /* * There doesn't seem to have been an interrupt. */ return FD_NOT_VALID; } if (fd_in(fdc, &cyl) < 0) { return fdc_err(fdc, "can't get cyl num\n"); } if (cylp) *cylp = cyl; return 0; } static int fd_read_status(fdc_p fdc, int fdsu) { int i, ret; for (i = 0; i < 7; i++) { /* * XXX types are poorly chosen. Only bytes can by read * from the hardware, but fdc->status[] wants u_ints and * fd_in() gives ints. */ int status; ret = fd_in(fdc, &status); fdc->status[i] = status; if (ret != 0) break; } if (ret == 0) fdc->flags |= FDC_STAT_VALID; else fdc->flags &= ~FDC_STAT_VALID; return ret; } /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ static struct isa_pnp_id fdc_ids[] = { {0x0007d041, "PC standard floppy disk controller"}, /* PNP0700 */ {0x0107d041, "Standard floppy controller supporting MS Device Bay Spec"}, /* PNP0701 */ {0} }; /* * fdc controller section. */ static int fdc_probe(device_t dev) { int error, ispnp, ic_type; struct fdc_data *fdc; /* Check pnp ids */ error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids); if (error == ENXIO) return ENXIO; ispnp = (error == 0); fdc = device_get_softc(dev); bzero(fdc, sizeof *fdc); fdc->fdc_dev = dev; fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0; fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0; fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, &fdc->rid_ioport, 0ul, ~0ul, ispnp ? 1 : IO_FDCSIZE, RF_ACTIVE); if (fdc->res_ioport == 0) { device_printf(dev, "cannot reserve I/O port range\n"); error = ENXIO; goto out; } fdc->portt = rman_get_bustag(fdc->res_ioport); fdc->porth = rman_get_bushandle(fdc->res_ioport); if (ispnp) { /* * Some bios' report the device at 0x3f2-0x3f5,0x3f7 * and some at 0x3f0-0x3f5,0x3f7. We detect the former * by checking the size and adjust the port address * accordingly. */ if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4) fdc->port_off = -2; fdc->flags |= FDC_ISPNP; fdc->rid_ctl = 1; fdc->res_ctl = bus_alloc_resource(dev, SYS_RES_IOPORT, &fdc->rid_ctl, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_ctl == 0) { device_printf(dev, "cannot reserve I/O port range\n"); error = ENXIO; goto out; } fdc->ctlt = rman_get_bustag(fdc->res_ctl); fdc->ctlh = rman_get_bushandle(fdc->res_ctl); } fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &fdc->rid_irq, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_irq == 0) { device_printf(dev, "cannot reserve interrupt line\n"); error = ENXIO; goto out; } fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ, &fdc->rid_drq, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_drq == 0) { device_printf(dev, "cannot reserve DMA request line\n"); error = ENXIO; goto out; } fdc->dmachan = fdc->res_drq->r_start; error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq, INTR_TYPE_BIO, fdc_intr, fdc, &fdc->fdc_intr); /* First - lets reset the floppy controller */ fdout_wr(fdc, 0); DELAY(100); fdout_wr(fdc, FDO_FRST); /* see if it can handle a command */ if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0)) { error = ENXIO; goto out; } if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { ic_type = (u_char)ic_type; switch (ic_type) { case 0x80: device_set_desc(dev, "NEC 765 or clone"); fdc->fdct = FDC_NE765; break; case 0x81: device_set_desc(dev, "Intel 82077 or clone"); fdc->fdct = FDC_I82077; break; case 0x90: device_set_desc(dev, "NEC 72065B or clone"); fdc->fdct = FDC_NE72065; break; default: device_set_desc(dev, "generic floppy controller"); fdc->fdct = FDC_UNKNOWN; break; } } #ifdef FDC_YE /* * don't succeed on probe; wait * for PCCARD subsystem to do it */ if (dev->id_flags & FDC_IS_PCMCIA) return(0); #endif return (0); out: if (fdc->fdc_intr) BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq, fdc->fdc_intr); if (fdc->res_irq != 0) { bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq, fdc->res_irq); bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq, fdc->res_irq); } if (fdc->res_ctl != 0) { bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, fdc->res_ctl); bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, fdc->res_ctl); } if (fdc->res_ioport != 0) { bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, fdc->res_ioport); bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, fdc->res_ioport); } if (fdc->res_drq != 0) { bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq, fdc->res_drq); bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq, fdc->res_drq); } return (error); } /* * Aped dfr@freebsd.org's isa_add_device(). */ static void fdc_add_device(device_t dev, const char *name, int unit) { int disabled, *ivar; device_t child; ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT); if (ivar == 0) return; if (resource_int_value(name, unit, "drive", ivar) != 0) *ivar = 0; - child = device_add_child(dev, name, unit, ivar); + child = device_add_child(dev, name, unit); + device_set_ivars(child, ivar); if (child == 0) return; if (resource_int_value(name, unit, "disabled", &disabled) == 0 && disabled != 0) device_disable(child); } static int fdc_attach(device_t dev) { struct fdc_data *fdc = device_get_softc(dev); fdcu_t fdcu = device_get_unit(dev); int i; for (i = resource_query_string(-1, "at", device_get_nameunit(dev)); i != -1; i = resource_query_string(i, "at", device_get_nameunit(dev))) fdc_add_device(dev, resource_query_name(i), resource_query_unit(i)); fdc->fdcu = fdcu; fdc->flags |= FDC_ATTACHED; /* Acquire the DMA channel forever, The driver will do the rest */ /* XXX should integrate with rman */ isa_dma_acquire(fdc->dmachan); isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */); fdc->state = DEVIDLE; /* reset controller, turn motor off, clear fdout mirror reg */ fdout_wr(fdc, ((fdc->fdout = 0))); bufq_init(&fdc->head); /* * Probe and attach any children as were configured above. */ return (bus_generic_attach(dev)); } static int fdc_print_child(device_t me, device_t child) { int retval = 0; retval += bus_print_child_header(me, child); retval += printf(" on %s drive %d\n", device_get_nameunit(me), *(int *)device_get_ivars(child)); return (retval); } static device_method_t fdc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fdc_probe), DEVMETHOD(device_attach, fdc_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, fdc_print_child), /* Our children never use any other bus interface methods. */ { 0, 0 } }; static driver_t fdc_driver = { "fdc", fdc_methods, sizeof(struct fdc_data) }; DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0); /******************************************************************/ /* * devices attached to the controller section. */ static int fd_probe(device_t dev) { int i; u_int fdt, st0, st3; struct fd_data *fd; struct fdc_data *fdc; fdsu_t fdsu; static int fd_fifo = 0; fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */ fd = device_get_softc(dev); fdc = device_get_softc(device_get_parent(dev)); bzero(fd, sizeof *fd); fd->dev = dev; fd->fdc = fdc; fd->fdsu = fdsu; fd->fdu = device_get_unit(dev); #ifdef __i386__ /* look up what bios thinks we have */ switch (fd->fdu) { case 0: if (device_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0) fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED; else fdt = (rtcin(RTC_FDISKETTE) & 0xf0); break; case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); break; default: fdt = RTCFDT_NONE; break; } #else fdt = RTCFDT_144M; /* XXX probably */ #endif /* is there a unit? */ if (fdt == RTCFDT_NONE) return (ENXIO); /* select it */ set_motor(fdc, fdsu, TURNON); DELAY(1000000); /* 1 sec */ /* XXX This doesn't work before the first set_motor() */ if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN && enable_fifo(fdc) == 0) { device_print_prettyname(device_get_parent(dev)); printf("FIFO enabled, %d bytes threshold\n", fifo_threshold); } fd_fifo = 1; if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* if at track 0, first seek inwards */ /* seek some steps: */ fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0); DELAY(300000); /* ...wait a moment... */ fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ } /* If we're at track 0 first seek inwards. */ if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* Seek some steps... */ if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { /* ...wait a moment... */ DELAY(300000); /* make ctrlr happy: */ fd_sense_int(fdc, 0, 0); } } for (i = 0; i < 2; i++) { /* * we must recalibrate twice, just in case the * heads have been beyond cylinder 76, since most * FDCs still barf when attempting to recalibrate * more than 77 steps */ /* go back to 0: */ if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) { /* a second being enough for full stroke seek*/ DELAY(i == 0 ? 1000000 : 300000); /* anything responding? */ if (fd_sense_int(fdc, &st0, 0) == 0 && (st0 & NE7_ST0_EC) == 0) break; /* already probed succesfully */ } } set_motor(fdc, fdsu, TURNOFF); if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ return (ENXIO); fd->track = FD_NO_TRACK; fd->fdc = fdc; fd->fdsu = fdsu; fd->options = 0; callout_handle_init(&fd->toffhandle); callout_handle_init(&fd->tohandle); switch (fdt) { case RTCFDT_12M: device_set_desc(dev, "1200-KB 5.25\" drive"); fd->type = FD_1200; break; case RTCFDT_144M | RTCFDT_144M_PRETENDED: device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive"); fdt = RTCFDT_144M; fd->type = FD_1440; case RTCFDT_144M: device_set_desc(dev, "1440-KB 3.5\" drive"); fd->type = FD_1440; break; case RTCFDT_288M: case RTCFDT_288M_1: device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); fd->type = FD_1440; break; case RTCFDT_360K: device_set_desc(dev, "360-KB 5.25\" drive"); fd->type = FD_360; break; case RTCFDT_720K: printf("720-KB 3.5\" drive"); fd->type = FD_720; break; default: return (ENXIO); } return (0); } static int fd_attach(device_t dev) { struct fd_data *fd; #if 0 int i; int mynor; int typemynor; int typesize; #endif fd = device_get_softc(dev); cdevsw_add(&fd_cdevsw); /* XXX */ make_dev(&fd_cdevsw, (fd->fdu << 6), UID_ROOT, GID_OPERATOR, 0640, "rfd%d", fd->fdu); #if 0 /* Other make_dev() go here. */ #endif /* * Export the drive to the devstat interface. */ devstat_add_entry(&fd->device_stats, device_get_name(dev), device_get_unit(dev), 512, DEVSTAT_NO_ORDERED_TAGS, DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, DEVSTAT_PRIORITY_FD); return (0); } static device_method_t fd_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fd_probe), DEVMETHOD(device_attach, fd_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX */ DEVMETHOD(device_resume, bus_generic_resume), /* XXX */ { 0, 0 } }; static driver_t fd_driver = { "fd", fd_methods, sizeof(struct fd_data) }; DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, 0, 0); /******************************************************************/ #ifdef FDC_YE /* * this is a subset of fdattach() optimized for the Y-E Data * PCMCIA floppy drive. */ static int yeattach(struct isa_device *dev) { fdcu_t fdcu = dev->id_unit; fdc_p fdc = fdc_data + fdcu; fdsu_t fdsu = 0; /* assume 1 drive per YE controller */ fdu_t fdu; fd_p fd; int st0, st3, i; fdc->fdcu = fdcu; /* * the FDC_PCMCIA flag is used to to indicate special PIO is used * instead of DMA */ fdc->flags = FDC_ATTACHED|FDC_PCMCIA; fdc->state = DEVIDLE; /* reset controller, turn motor off, clear fdout mirror reg */ fdout_wr(fdc, ((fdc->fdout = 0))); bufq_init(&fdc->head); /* * assume 2 drives/ "normal" controller */ fdu = fdcu * 2; if (fdu >= NFD) { printf("fdu %d >= NFD\n",fdu); return(0); }; fd = &fd_data[fdu]; set_motor(fdcu, fdsu, TURNON); DELAY(1000000); /* 1 sec */ fdc->fdct = FDC_NE765; if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* if at track 0, first seek inwards */ /* seek some steps: */ (void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0); DELAY(300000); /* ...wait a moment... */ (void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ } /* If we're at track 0 first seek inwards. */ if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* Seek some steps... */ if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { /* ...wait a moment... */ DELAY(300000); /* make ctrlr happy: */ (void)fd_sense_int(fdc, 0, 0); } } for(i = 0; i < 2; i++) { /* * we must recalibrate twice, just in case the * heads have been beyond cylinder 76, since most * FDCs still barf when attempting to recalibrate * more than 77 steps */ /* go back to 0: */ if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) { /* a second being enough for full stroke seek*/ DELAY(i == 0? 1000000: 300000); /* anything responding? */ if (fd_sense_int(fdc, &st0, 0) == 0 && (st0 & NE7_ST0_EC) == 0) break; /* already probed succesfully */ } } set_motor(fdcu, fdsu, TURNOFF); if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ return(0); fd->track = FD_NO_TRACK; fd->fdc = fdc; fd->fdsu = fdsu; fd->options = 0; printf("fdc%d: 1.44MB 3.5in PCMCIA\n", fdcu); fd->type = FD_1440; return (1); } #endif /****************************************************************************/ /* motor control stuff */ /* remember to not deselect the drive we're working on */ /****************************************************************************/ static void set_motor(struct fdc_data *fdc, int fdsu, int turnon) { int fdout = fdc->fdout; int needspecify = 0; if(turnon) { fdout &= ~FDO_FDSEL; fdout |= (FDO_MOEN0 << fdsu) + fdsu; } else fdout &= ~(FDO_MOEN0 << fdsu); if(!turnon && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) /* gonna turn off the last drive, put FDC to bed */ fdout &= ~ (FDO_FRST|FDO_FDMAEN); else { /* make sure controller is selected and specified */ if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) needspecify = 1; fdout |= (FDO_FRST|FDO_FDMAEN); } fdout_wr(fdc, fdout); fdc->fdout = fdout; TRACE1("[0x%x->FDOUT]", fdout); if (needspecify) { /* * XXX * special case: since we have just woken up the FDC * from its sleep, we silently assume the command will * be accepted, and do not test for a timeout */ (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); if (fdc->flags & FDC_HAS_FIFO) (void) enable_fifo(fdc); } } static void fd_turnoff(void *xfd) { int s; fd_p fd = xfd; TRACE1("[fd%d: turnoff]", fd->fdu); /* * Don't turn off the motor yet if the drive is active. * XXX shouldn't even schedule turnoff until drive is inactive * and nothing is queued on it. */ if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) { fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); return; } s = splbio(); fd->flags &= ~FD_MOTOR; set_motor(fd->fdc, fd->fdsu, TURNOFF); splx(s); } static void fd_motor_on(void *xfd) { int s; fd_p fd = xfd; s = splbio(); fd->flags &= ~FD_MOTOR_WAIT; if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) { fdc_intr(fd->fdc); } splx(s); } static void fd_turnon(fd_p fd) { if(!(fd->flags & FD_MOTOR)) { fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); set_motor(fd->fdc, fd->fdsu, TURNON); timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */ } } static void fdc_reset(fdc_p fdc) { /* Try a reset, keep motor on */ fdout_wr(fdc, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); DELAY(100); /* enable FDC, but defer interrupts a moment */ fdout_wr(fdc, fdc->fdout & ~FDO_FDMAEN); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); DELAY(100); fdout_wr(fdc, fdc->fdout); TRACE1("[0x%x->FDOUT]", fdc->fdout); /* XXX after a reset, silently believe the FDC will accept commands */ (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); if (fdc->flags & FDC_HAS_FIFO) (void) enable_fifo(fdc); } /****************************************************************************/ /* fdc in/out */ /****************************************************************************/ int in_fdc(struct fdc_data *fdc) { int i, j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = fddata_rd(fdc); TRACE1("[FDDATA->0x%x]", (unsigned char)i); return(i); #else /* !FDC_DEBUG */ return fddata_rd(fdc); #endif /* FDC_DEBUG */ } /* * fd_in: Like in_fdc, but allows you to see if it worked. */ static int fd_in(struct fdc_data *fdc, int *ptr) { int i, j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = fddata_rd(fdc); TRACE1("[FDDATA->0x%x]", (unsigned char)i); *ptr = i; return 0; #else /* !FDC_DEBUG */ i = fddata_rd(fdc); if (ptr) *ptr = i; return 0; #endif /* FDC_DEBUG */ } int out_fdc(struct fdc_data *fdc, int x) { int i; /* Check that the direction bit is set */ i = 100000; while ((fdsts_rd(fdc) & NE7_DIO) && i-- > 0); if (i <= 0) return fdc_err(fdc, "direction bit not set\n"); /* Check that the floppy controller is ready for a command */ i = 100000; while ((fdsts_rd(fdc) & NE7_RQM) == 0 && i-- > 0); if (i <= 0) return fdc_err(fdc, bootverbose? "output ready timeout\n": 0); /* Send the command and return */ fddata_wr(fdc, x); TRACE1("[0x%x->FDDATA]", x); return (0); } /****************************************************************************/ /* fdopen/fdclose */ /****************************************************************************/ int Fdopen(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); int type = FDTYPE(minor(dev)); fd_p fd; fdc_p fdc; /* check bounds */ if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0) return (ENXIO); fdc = fd->fdc; if ((fdc == NULL) || (fd->type == NO_TYPE)) return (ENXIO); if (type > NUMDENS) return (ENXIO); if (type == 0) type = fd->type; else { /* * For each type of basic drive, make sure we are trying * to open a type it can do, */ if (type != fd->type) { switch (fd->type) { case FD_360: return (ENXIO); case FD_720: if ( type != FD_820 && type != FD_800 ) return (ENXIO); break; case FD_1200: switch (type) { case FD_1480: type = FD_1480in5_25; break; case FD_1440: type = FD_1440in5_25; break; case FD_820: type = FD_820in5_25; break; case FD_800: type = FD_800in5_25; break; case FD_720: type = FD_720in5_25; break; case FD_360: type = FD_360in5_25; break; default: return(ENXIO); } break; case FD_1440: if ( type != FD_1720 && type != FD_1480 && type != FD_1200 && type != FD_820 && type != FD_800 && type != FD_720 ) return(ENXIO); break; } } } fd->ft = fd_types + type - 1; fd->flags |= FD_OPEN; device_busy(fd->dev); device_busy(fd->fdc->fdc_dev); return 0; } int fdclose(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); struct fd_data *fd; fd = devclass_get_softc(fd_devclass, fdu); fd->flags &= ~FD_OPEN; fd->options &= ~FDOPT_NORETRY; return (0); } /****************************************************************************/ /* fdstrategy */ /****************************************************************************/ void fdstrategy(struct buf *bp) { unsigned nblocks, blknum, cando; int s; fdu_t fdu; fdc_p fdc; fd_p fd; size_t fdblk; fdu = FDUNIT(minor(bp->b_dev)); fd = devclass_get_softc(fd_devclass, fdu); if (fd == 0) panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)", (u_long)major(bp->b_dev), (u_long)minor(bp->b_dev)); fdc = fd->fdc; #ifdef FDC_YE if (fd->type == NO_TYPE) { bp->b_error = ENXIO; bp->b_flags |= B_ERROR; /* * I _refuse_ to use a goto */ biodone(bp); return; }; #endif fdblk = 128 << (fd->ft->secsize); if (!(bp->b_flags & B_FORMAT)) { if (bp->b_blkno < 0) { printf( "fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n", fdu, (u_long)bp->b_blkno, bp->b_bcount); bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } if ((bp->b_bcount % fdblk) != 0) { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } } /* * Set up block calculations. */ if (bp->b_blkno > 20000000) { /* * Reject unreasonably high block number, prevent the * multiplication below from overflowing. */ bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk; nblocks = fd->ft->size; bp->b_resid = 0; if (blknum + (bp->b_bcount / fdblk) > nblocks) { if (blknum <= nblocks) { cando = (nblocks - blknum) * fdblk; bp->b_resid = bp->b_bcount - cando; if (cando == 0) goto bad; /* not actually bad but EOF */ } else { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } } bp->b_pblkno = bp->b_blkno; s = splbio(); bufqdisksort(&fdc->head, bp); untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */ /* Tell devstat we are starting on the transaction */ devstat_start_transaction(&fd->device_stats); fdstart(fdc); splx(s); return; bad: biodone(bp); } /***************************************************************\ * fdstart * * We have just queued something.. if the controller is not busy * * then simulate the case where it has just finished a command * * So that it (the interrupt routine) looks on the queue for more* * work to do and picks up what we just added. * * If the controller is already busy, we need do nothing, as it * * will pick up our work when the present work completes * \***************************************************************/ static void fdstart(struct fdc_data *fdc) { int s; s = splbio(); if(fdc->state == DEVIDLE) { fdc_intr(fdc); } splx(s); } static void fd_iotimeout(void *xfdc) { fdc_p fdc; int s; fdc = xfdc; TRACE1("fd%d[fd_iotimeout()]", fdc->fdu); /* * Due to IBM's brain-dead design, the FDC has a faked ready * signal, hardwired to ready == true. Thus, any command * issued if there's no diskette in the drive will _never_ * complete, and must be aborted by resetting the FDC. * Many thanks, Big Blue! * The FDC must not be reset directly, since that would * interfere with the state machine. Instead, pretend that * the command completed but was invalid. The state machine * will reset the FDC and retry once. */ s = splbio(); fdc->status[0] = NE7_ST0_IC_IV; fdc->flags &= ~FDC_STAT_VALID; fdc->state = IOTIMEDOUT; fdc_intr(fdc); splx(s); } /* just ensure it has the right spl */ static void fd_pseudointr(void *xfdc) { int s; s = splbio(); fdc_intr(xfdc); splx(s); } /***********************************************************************\ * fdintr * * keep calling the state machine until it returns a 0 * * ALWAYS called at SPLBIO * \***********************************************************************/ static void fdc_intr(void *xfdc) { fdc_p fdc = xfdc; while(fdstate(fdc)) ; } #ifdef FDC_YE /* * magic pseudo-DMA initialization for YE FDC. Sets count and * direction */ #define SET_BCDR(wr,cnt,port) outb(port,(((cnt)-1) & 0xff)); \ outb(port+1,((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f))) /* * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy */ static int fdcpio(fdcu_t fdcu, long flags, caddr_t addr, u_int count) { u_char *cptr = (u_char *)addr; fdc_p fdc = &fdc_data[fdcu]; int io = fdc->baseport; if (flags & B_READ) { if (fdc->state != PIOREAD) { fdc->state = PIOREAD; return(0); }; SET_BCDR(0,count,io); insb(io+FDC_YE_DATAPORT,cptr,count); } else { outsb(io+FDC_YE_DATAPORT,cptr,count); SET_BCDR(0,count,io); }; return(1); } #endif /* FDC_YE */ /***********************************************************************\ * The controller state machine. * * if it returns a non zero value, it should be called again immediatly * \***********************************************************************/ static int fdstate(fdc_p fdc) { int read, format, head, i, sec = 0, sectrac, st0, cyl, st3; unsigned blknum = 0, b_cylinder = 0; fdu_t fdu = fdc->fdu; fd_p fd; register struct buf *bp; struct fd_formb *finfo = NULL; size_t fdblk; bp = fdc->bp; if (bp == NULL) { bp = bufq_first(&fdc->head); if (bp != NULL) { bufq_remove(&fdc->head, bp); fdc->bp = bp; } } if (bp == NULL) { /***********************************************\ * nothing left for this controller to do * * Force into the IDLE state, * \***********************************************/ fdc->state = DEVIDLE; if (fdc->fd) { device_print_prettyname(fdc->fdc_dev); printf("unexpected valid fd pointer\n"); fdc->fd = (fd_p) 0; fdc->fdu = -1; } TRACE1("[fdc%d IDLE]", fdc->fdcu); return (0); } fdu = FDUNIT(minor(bp->b_dev)); fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; if (fdc->fd && (fd != fdc->fd)) { device_print_prettyname(fd->dev); printf("confused fd pointers\n"); } read = bp->b_flags & B_READ; format = bp->b_flags & B_FORMAT; if (format) { finfo = (struct fd_formb *)bp->b_data; fd->skip = (char *)&(finfo->fd_formb_cylno(0)) - (char *)finfo; } if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) { blknum = (unsigned) bp->b_pblkno * DEV_BSIZE/fdblk + fd->skip/fdblk; b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); } TRACE1("fd%d", fdu); TRACE1("[%s]", fdstates[fdc->state]); TRACE1("(0x%x)", fd->flags); untimeout(fd_turnoff, fd, fd->toffhandle); fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); switch (fdc->state) { case DEVIDLE: case FINDWORK: /* we have found new work */ fdc->retry = 0; fd->skip = 0; fdc->fd = fd; fdc->fdu = fdu; fdctl_wr(fdc, fd->ft->trans); TRACE1("[0x%x->FDCTL]", fd->ft->trans); /*******************************************************\ * If the next drive has a motor startup pending, then * * it will start up in its own good time * \*******************************************************/ if(fd->flags & FD_MOTOR_WAIT) { fdc->state = MOTORWAIT; return (0); /* come back later */ } /*******************************************************\ * Maybe if it's not starting, it SHOULD be starting * \*******************************************************/ if (!(fd->flags & FD_MOTOR)) { fdc->state = MOTORWAIT; fd_turnon(fd); return (0); } else /* at least make sure we are selected */ { set_motor(fdc, fd->fdsu, TURNON); } if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else fdc->state = DOSEEK; break; case DOSEEK: if (b_cylinder == (unsigned)fd->track) { fdc->state = SEEKCOMPLETE; break; } if (fd_cmd(fdc, 3, NE7CMD_SEEK, fd->fdsu, b_cylinder * fd->ft->steptrac, 0)) { /* * seek command not accepted, looks like * the FDC went off to the Saints... */ fdc->retry = 6; /* try a reset */ return(retrier(fdc)); } fd->track = FD_NO_TRACK; fdc->state = SEEKWAIT; return(0); /* will return later */ case SEEKWAIT: /* allow heads to settle */ timeout(fd_pseudointr, fdc, hz / 16); fdc->state = SEEKCOMPLETE; return(0); /* will return later */ case SEEKCOMPLETE : /* SEEK DONE, START DMA */ /* Make sure seek really happened*/ if(fd->track == FD_NO_TRACK) { int descyl = b_cylinder * fd->ft->steptrac; do { /* * This might be a "ready changed" interrupt, * which cannot really happen since the * RDY pin is hardwired to + 5 volts. This * generally indicates a "bouncing" intr * line, so do one of the following: * * When running on an enhanced FDC that is * known to not go stuck after responding * with INVALID, fetch all interrupt states * until seeing either an INVALID or a * real interrupt condition. * * When running on a dumb old NE765, give * up immediately. The controller will * provide up to four dummy RC interrupt * conditions right after reset (for the * corresponding four drives), so this is * our only chance to get notice that it * was not the FDC that caused the interrupt. */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); if (0 == descyl) { int failed = 0; /* * seek to cyl 0 requested; make sure we are * really there */ if (fd_sense_drive_status(fdc, &st3)) failed = 1; if ((st3 & NE7_ST3_T0) == 0) { printf( "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", fdu, st3, NE7_ST3BITS); failed = 1; } if (failed) { if(fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } } if (cyl != descyl) { printf( "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", fdu, descyl, cyl, st0); if (fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } } fd->track = b_cylinder; #ifdef FDC_YE if (!(fdc->flags & FDC_PCMCIA)) #endif isa_dmastart(bp->b_flags, bp->b_data+fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; fd->hddrv = ((head&1)<<2)+fdu; if(format || !read) { /* make sure the drive is writable */ if(fd_sense_drive_status(fdc, &st3) != 0) { /* stuck controller? */ isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); fdc->retry = 6; /* reset the beast */ return (retrier(fdc)); } if(st3 & NE7_ST3_WP) { /* * XXX YES! this is ugly. * in order to force the current operation * to fail, we will have to fake an FDC * error - all error handling is done * by the retrier() */ fdc->status[0] = NE7_ST0_IC_AT; fdc->status[1] = NE7_ST1_NW; fdc->status[2] = 0; fdc->status[3] = fd->track; fdc->status[4] = head; fdc->status[5] = sec; fdc->retry = 8; /* break out immediately */ fdc->state = IOTIMEDOUT; /* not really... */ return (1); } } if (format) { #ifdef FDC_YE if (fdc->flags & FDC_PCMCIA) (void)fdcpio(fdcu,bp->b_flags, bp->b_data+fd->skip, bp->b_bcount); #endif /* formatting */ if(fd_cmd(fdc, 6, NE7CMD_FORMAT, head << 2 | fdu, finfo->fd_formb_secshift, finfo->fd_formb_nsecs, finfo->fd_formb_gaplen, finfo->fd_formb_fillbyte, 0)) { /* controller fell over */ isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); fdc->retry = 6; return (retrier(fdc)); } } else { #ifdef FDC_YE if (fdc->flags & FDC_PCMCIA) { /* * this seems to be necessary even when * reading data */ SET_BCDR(1,fdblk,fdc->baseport); /* * perform the write pseudo-DMA before * the WRITE command is sent */ if (!read) (void)fdcpio(fdcu,bp->b_flags, bp->b_data+fd->skip, fdblk); } #endif if (fd_cmd(fdc, 9, (read ? NE7CMD_READ : NE7CMD_WRITE), head << 2 | fdu, /* head & unit */ fd->track, /* track */ head, sec, /* sector + 1 */ fd->ft->secsize, /* sector size */ sectrac, /* sectors/track */ fd->ft->gap, /* gap size */ fd->ft->datalen, /* data length */ 0)) { /* the beast is sleeping again */ isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); fdc->retry = 6; return (retrier(fdc)); } } #ifdef FDC_YE if (fdc->flags & FDC_PCMCIA) /* * if this is a read, then simply await interrupt * before performing PIO */ if (read && !fdcpio(fdcu,bp->b_flags, bp->b_data+fd->skip,fdblk)) { fd->tohandle = timeout(fd_iotimeout, (caddr_t)fdcu, hz); return(0); /* will return later */ }; /* * write (or format) operation will fall through and * await completion interrupt */ #endif fdc->state = IOCOMPLETE; fd->tohandle = timeout(fd_iotimeout, fdc, hz); return (0); /* will return later */ #ifdef FDC_YE case PIOREAD: /* * actually perform the PIO read. The IOCOMPLETE case * removes the timeout for us. */ (void)fdcpio(fdcu,bp->b_flags,bp->b_data+fd->skip,fdblk); fdc->state = IOCOMPLETE; /* FALLTHROUGH */ #endif case IOCOMPLETE: /* IO DONE, post-analyze */ untimeout(fd_iotimeout, fdc, fd->tohandle); if (fd_read_status(fdc, fd->fdsu)) { isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); if (fdc->retry < 6) fdc->retry = 6; /* force a reset */ return (retrier(fdc)); } fdc->state = IOTIMEDOUT; /* FALLTHROUGH */ case IOTIMEDOUT: #ifdef FDC_YE if (!(fdc->flags & FDC_PCMCIA)) #endif isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); if (fdc->status[0] & NE7_ST0_IC) { if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[1] & NE7_ST1_OR) { /* * DMA overrun. Someone hogged the bus * and didn't release it in time for the * next FDC transfer. * Just restart it, don't increment retry * count. (vak) */ fdc->state = SEEKCOMPLETE; return (1); } else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV && fdc->retry < 6) fdc->retry = 6; /* force a reset */ else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[2] & NE7_ST2_WC && fdc->retry < 3) fdc->retry = 3; /* force recalibrate */ return (retrier(fdc)); } /* All OK */ fd->skip += fdblk; if (!format && fd->skip < bp->b_bcount - bp->b_resid) { /* set up next transfer */ fdc->state = DOSEEK; } else { /* ALL DONE */ fd->skip = 0; fdc->bp = NULL; devstat_end_transaction_buf(&fd->device_stats, bp); biodone(bp); fdc->fd = (fd_p) 0; fdc->fdu = -1; fdc->state = FINDWORK; } return (1); case RESETCTLR: fdc_reset(fdc); fdc->retry++; fdc->state = RESETCOMPLETE; return (0); case RESETCOMPLETE: /* * Discard all the results from the reset so that they * can't cause an unexpected interrupt later. */ for (i = 0; i < 4; i++) (void)fd_sense_int(fdc, &st0, &cyl); fdc->state = STARTRECAL; /* Fall through. */ case STARTRECAL: if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) { /* arrgl */ fdc->retry = 6; return (retrier(fdc)); } fdc->state = RECALWAIT; return (0); /* will return later */ case RECALWAIT: /* allow heads to settle */ timeout(fd_pseudointr, fdc, hz / 8); fdc->state = RECALCOMPLETE; return (0); /* will return later */ case RECALCOMPLETE: do { /* * See SEEKCOMPLETE for a comment on this: */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) { if(fdc->retry > 3) /* * a recalibrate from beyond cylinder 77 * will "fail" due to the FDC limitations; * since people used to complain much about * the failure message, try not logging * this one if it seems to be the first * time in a line */ printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, st0, NE7_ST0BITS, cyl); if(fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } fd->track = 0; /* Seek (probably) necessary */ fdc->state = DOSEEK; return (1); /* will return immediatly */ case MOTORWAIT: if(fd->flags & FD_MOTOR_WAIT) { return (0); /* time's not up yet */ } if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else { /* * If all motors were off, then the controller was * reset, so it has lost track of the current * cylinder. Recalibrate to handle this case. * But first, discard the results of the reset. */ fdc->state = RESETCOMPLETE; } return (1); /* will return immediatly */ default: device_print_prettyname(fdc->fdc_dev); printf("unexpected FD int->"); if (fd_read_status(fdc, fd->fdsu) == 0) printf("FDC status :%x %x %x %x %x %x %x ", fdc->status[0], fdc->status[1], fdc->status[2], fdc->status[3], fdc->status[4], fdc->status[5], fdc->status[6] ); else printf("No status available "); if (fd_sense_int(fdc, &st0, &cyl) != 0) { printf("[controller is dead now]\n"); return (0); } printf("ST0 = %x, PCN = %x\n", st0, cyl); return (0); } /*XXX confusing: some branches return immediately, others end up here*/ return (1); /* Come back immediatly to new state */ } static int retrier(struct fdc_data *fdc) { register struct buf *bp; struct fd_data *fd; int fdu; bp = fdc->bp; /* XXX shouldn't this be cached somewhere? */ fdu = FDUNIT(minor(bp->b_dev)); fd = devclass_get_softc(fd_devclass, fdu); if (fd->options & FDOPT_NORETRY) goto fail; switch (fdc->retry) { case 0: case 1: case 2: fdc->state = SEEKCOMPLETE; break; case 3: case 4: case 5: fdc->state = STARTRECAL; break; case 6: fdc->state = RESETCTLR; break; case 7: break; default: fail: { dev_t sav_b_dev = bp->b_dev; /* Trick diskerr */ bp->b_dev = makedev(major(bp->b_dev), (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART); diskerr(bp, "hard error", LOG_PRINTF, fdc->fd->skip / DEV_BSIZE, (struct disklabel *)NULL); bp->b_dev = sav_b_dev; if (fdc->flags & FDC_STAT_VALID) { printf( " (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n", fdc->status[0], NE7_ST0BITS, fdc->status[1], NE7_ST1BITS, fdc->status[2], NE7_ST2BITS, fdc->status[3], fdc->status[4], fdc->status[5]); } else printf(" (No status)\n"); } bp->b_flags |= B_ERROR; bp->b_error = EIO; bp->b_resid += bp->b_bcount - fdc->fd->skip; fdc->bp = NULL; fdc->fd->skip = 0; devstat_end_transaction_buf(&fdc->fd->device_stats, bp); biodone(bp); fdc->state = FINDWORK; fdc->flags |= FDC_NEEDS_RESET; fdc->fd = (fd_p) 0; fdc->fdu = -1; return (1); } fdc->retry++; return (1); } static int fdformat(dev, finfo, p) dev_t dev; struct fd_formb *finfo; struct proc *p; { fdu_t fdu; fd_p fd; struct buf *bp; int rv = 0, s; size_t fdblk; fdu = FDUNIT(minor(dev)); fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; /* set up a buffer header for fdstrategy() */ bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); if(bp == 0) return ENOBUFS; /* * keep the process from being swapped */ PHOLD(p); bzero((void *)bp, sizeof(struct buf)); BUF_LOCKINIT(bp); BUF_LOCK(bp, LK_EXCLUSIVE); bp->b_flags = B_PHYS | B_FORMAT; /* * calculate a fake blkno, so fdstrategy() would initiate a * seek to the requested cylinder */ bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; bp->b_data = (caddr_t)finfo; /* now do the format */ bp->b_dev = dev; BUF_STRATEGY(bp, 0); /* ...and wait for it to complete */ s = splbio(); while(!(bp->b_flags & B_DONE)) { rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz); if (rv == EWOULDBLOCK) break; } splx(s); if (rv == EWOULDBLOCK) { /* timed out */ rv = EIO; biodone(bp); } if (bp->b_flags & B_ERROR) rv = bp->b_error; /* * allow the process to be swapped */ PRELE(p); BUF_UNLOCK(bp); BUF_LOCKFREE(bp); free(bp, M_TEMP); return rv; } /* * TODO: don't allocate buffer on stack. */ static int fdioctl(dev, cmd, addr, flag, p) dev_t dev; u_long cmd; caddr_t addr; int flag; struct proc *p; { fdu_t fdu = FDUNIT(minor(dev)); fd_p fd = devclass_get_softc(fd_devclass, fdu); size_t fdblk; struct fd_type *fdt; struct disklabel *dl; char buffer[DEV_BSIZE]; int error = 0; fdblk = 128 << fd->ft->secsize; switch (cmd) { case DIOCGDINFO: bzero(buffer, sizeof (buffer)); dl = (struct disklabel *)buffer; dl->d_secsize = fdblk; fdt = fd->ft; dl->d_secpercyl = fdt->size / fdt->tracks; dl->d_type = DTYPE_FLOPPY; if (readdisklabel(dkmodpart(dev, RAW_PART), dl) == NULL) error = 0; else error = EINVAL; *(struct disklabel *)addr = *dl; break; case DIOCSDINFO: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWLABEL: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWDINFO: if ((flag & FWRITE) == 0) { error = EBADF; break; } dl = (struct disklabel *)addr; if ((error = setdisklabel((struct disklabel *)buffer, dl, (u_long)0)) != 0) break; error = writedisklabel(dev, (struct disklabel *)buffer); break; case FD_FORM: if ((flag & FWRITE) == 0) error = EBADF; /* must be opened for writing */ else if (((struct fd_formb *)addr)->format_version != FD_FORMAT_VERSION) error = EINVAL; /* wrong version of formatting prog */ else error = fdformat(dev, (struct fd_formb *)addr, p); break; case FD_GTYPE: /* get drive type */ *(struct fd_type *)addr = *fd->ft; break; case FD_STYPE: /* set drive type */ /* this is considered harmful; only allow for superuser */ if (suser(p) != 0) return EPERM; *fd->ft = *(struct fd_type *)addr; break; case FD_GOPTS: /* get drive options */ *(int *)addr = fd->options; break; case FD_SOPTS: /* set drive options */ fd->options = *(int *)addr; break; default: error = ENOTTY; break; } return (error); } /* * Hello emacs, these are the * Local Variables: * c-indent-level: 8 * c-continued-statement-offset: 8 * c-continued-brace-offset: 0 * c-brace-offset: -8 * c-brace-imaginary-offset: 0 * c-argdecl-indent: 8 * c-label-offset: -8 * c++-hanging-braces: 1 * c++-access-specifier-offset: -8 * c++-empty-arglist-indent: 8 * c++-friend-offset: 0 * End: */ Index: head/sys/dev/ida/ida.c =================================================================== --- head/sys/dev/ida/ida.c (revision 54072) +++ head/sys/dev/ida/ida.c (revision 54073) @@ -1,513 +1,513 @@ /*- * Copyright (c) 1999 Jonathan Lemon * All rights reserved. * # Derived from the original IDA Compaq RAID driver, which is * Copyright (c) 1996, 1997, 1998, 1999 * Mark Dawson and David James. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Generic driver for Compaq SMART RAID adapters. * * Specific probe routines are in: * pci/ida_pci.c * i386/eisa/ida_eisa.c */ #include #include #include #include #include #include #include #include #if NPCI > 0 #include #endif #include #include #include #include #include #include #define ida_inl(ida, port) \ bus_space_read_4((ida)->tag, (ida)->bsh, port) #define ida_outl(ida, port, val) \ bus_space_write_4((ida)->tag, (ida)->bsh, port, val) /* prototypes */ static void ida_alloc_qcb(struct ida_softc *ida); static void ida_construct_qcb(struct ida_softc *ida); static void ida_start(struct ida_softc *ida); static void ida_done(struct ida_softc *ida, struct ida_qcb *qcb); static void ida_wait(struct ida_softc *ida, struct ida_qcb *qcb, int delay); void ida_free(struct ida_softc *ida) { /* * still need to call bus_dmamap_destroy() for each map created * in ida_alloc_qcb(). */ if (ida->hwqcb_busaddr) bus_dmamap_unload(ida->hwqcb_dmat, ida->hwqcb_dmamap); if (ida->hwqcbs) bus_dmamem_free(ida->hwqcb_dmat, ida->hwqcbs, ida->hwqcb_dmamap); if (ida->buffer_dmat) bus_dma_tag_destroy(ida->buffer_dmat); if (ida->hwqcb_dmat) bus_dma_tag_destroy(ida->hwqcb_dmat); if (ida->qcbs != NULL) free(ida->qcbs, M_DEVBUF); if (ida->ih != NULL) bus_teardown_intr(ida->dev, ida->irq, ida->ih); if (ida->irq != NULL) bus_release_resource(ida->dev, ida->irq_res_type, 0, ida->irq); if (ida->parent_dmat != NULL) bus_dma_tag_destroy(ida->parent_dmat); if (ida->regs != NULL) bus_release_resource(ida->dev, ida->regs_res_type, ida->regs_res_id, ida->regs); } /* * record bus address from bus_dmamap_load */ static void ida_dma_map_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) { bus_addr_t *baddr; baddr = (bus_addr_t *)arg; *baddr = segs->ds_addr; } static __inline struct ida_qcb * ida_get_qcb(struct ida_softc *ida) { struct ida_qcb *qcb; if ((qcb = SLIST_FIRST(&ida->free_qcbs)) != NULL) { SLIST_REMOVE_HEAD(&ida->free_qcbs, link.sle); } else { ida_alloc_qcb(ida); if ((qcb = SLIST_FIRST(&ida->free_qcbs)) != NULL) SLIST_REMOVE_HEAD(&ida->free_qcbs, link.sle); } return (qcb); } /* * XXX * since we allocate all QCB space up front during initialization, then * why bother with this routine? */ static void ida_alloc_qcb(struct ida_softc *ida) { struct ida_qcb *qcb; int error; if (ida->num_qcbs >= IDA_QCB_MAX) return; qcb = &ida->qcbs[ida->num_qcbs]; error = bus_dmamap_create(ida->buffer_dmat, /*flags*/0, &qcb->dmamap); if (error != 0) return; qcb->flags = QCB_FREE; qcb->hwqcb = &ida->hwqcbs[ida->num_qcbs]; qcb->hwqcb->qcb = qcb; SLIST_INSERT_HEAD(&ida->free_qcbs, qcb, link.sle); ida->num_qcbs++; } int ida_init(struct ida_softc *ida) { int error; ida->unit = device_get_unit(ida->dev); ida->tag = rman_get_bustag(ida->regs); ida->bsh = rman_get_bushandle(ida->regs); SLIST_INIT(&ida->free_qcbs); STAILQ_INIT(&ida->qcb_queue); bufq_init(&ida->buf_queue); ida->qcbs = (struct ida_qcb *) malloc(IDA_QCB_MAX * sizeof(struct ida_qcb), M_DEVBUF, M_NOWAIT); if (ida->qcbs == NULL) return (ENOMEM); bzero(ida->qcbs, IDA_QCB_MAX * sizeof(struct ida_qcb)); /* * Create our DMA tags */ /* DMA tag for our hardware QCB structures */ error = bus_dma_tag_create(ida->parent_dmat, /*alignment*/1, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, IDA_QCB_MAX * sizeof(struct ida_hardware_qcb), /*nsegments*/1, /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT, /*flags*/0, &ida->hwqcb_dmat); if (error) return (ENOMEM); /* DMA tag for mapping buffers into device space */ error = bus_dma_tag_create(ida->parent_dmat, /*alignment*/1, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/MAXBSIZE, /*nsegments*/IDA_NSEG, /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT, /*flags*/0, &ida->buffer_dmat); if (error) return (ENOMEM); /* Allocation of hardware QCBs */ /* XXX allocation is rounded to hardware page size */ error = bus_dmamem_alloc(ida->hwqcb_dmat, (void **)&ida->hwqcbs, BUS_DMA_NOWAIT, &ida->hwqcb_dmamap); if (error) return (ENOMEM); /* And permanently map them in */ bus_dmamap_load(ida->hwqcb_dmat, ida->hwqcb_dmamap, ida->hwqcbs, IDA_QCB_MAX * sizeof(struct ida_hardware_qcb), ida_dma_map_cb, &ida->hwqcb_busaddr, /*flags*/0); bzero(ida->hwqcbs, IDA_QCB_MAX * sizeof(struct ida_hardware_qcb)); ida_alloc_qcb(ida); /* allocate an initial qcb */ return (0); } void ida_attach(struct ida_softc *ida) { struct ida_controller_info cinfo; int error, i; ida_outl(ida, R_INT_MASK, INT_DISABLE); error = ida_command(ida, CMD_GET_CTRL_INFO, &cinfo, sizeof(cinfo), IDA_CONTROLLER, DMA_DATA_IN); if (error) { device_printf(ida->dev, "CMD_GET_CTRL_INFO failed.\n"); return; } device_printf(ida->dev, "drives=%d firm_rev=%c%c%c%c\n", cinfo.num_drvs, cinfo.firm_rev[0], cinfo.firm_rev[1], cinfo.firm_rev[2], cinfo.firm_rev[3]); ida->num_drives = cinfo.num_drvs; for (i = 0; i < ida->num_drives; i++) - device_add_child(ida->dev, "id", i, NULL); + device_add_child(ida->dev, "id", i); bus_generic_attach(ida->dev); ida_outl(ida, R_INT_MASK, INT_ENABLE); } static void ida_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) { struct ida_hardware_qcb *hwqcb = (struct ida_hardware_qcb *)arg; int i; hwqcb->hdr.size = (sizeof(struct ida_req) + sizeof(struct ida_sgb) * IDA_NSEG) >> 2; for (i = 0; i < nsegments; i++) { hwqcb->seg[i].addr = segs[i].ds_addr; hwqcb->seg[i].length = segs[i].ds_len; } hwqcb->req.sgcount = nsegments; } int ida_command(struct ida_softc *ida, int command, void *data, int datasize, int drive, int flags) { struct ida_hardware_qcb *hwqcb; struct ida_qcb *qcb; bus_dmasync_op_t op; int s; s = splbio(); qcb = ida_get_qcb(ida); splx(s); if (qcb == NULL) { printf("ida_command: out of QCBs"); return (1); } hwqcb = qcb->hwqcb; bzero(hwqcb, sizeof(struct ida_hdr) + sizeof(struct ida_req)); bus_dmamap_load(ida->buffer_dmat, qcb->dmamap, (void *)data, datasize, ida_setup_dmamap, hwqcb, 0); op = qcb->flags & DMA_DATA_IN ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE; bus_dmamap_sync(ida->buffer_dmat, qcb->dmamap, op); hwqcb->hdr.drive = drive; /* XXX */ hwqcb->req.bcount = howmany(datasize, DEV_BSIZE); hwqcb->req.command = command; qcb->flags = flags | IDA_COMMAND; s = splbio(); STAILQ_INSERT_TAIL(&ida->qcb_queue, qcb, link.stqe); ida_start(ida); ida_wait(ida, qcb, 500); splx(s); /* XXX should have status returned here? */ /* XXX have "status pointer" area in QCB? */ return (0); } void ida_submit_buf(struct ida_softc *ida, struct buf *bp) { bufq_insert_tail(&ida->buf_queue, bp); ida_construct_qcb(ida); ida_start(ida); } static void ida_construct_qcb(struct ida_softc *ida) { struct ida_hardware_qcb *hwqcb; struct ida_qcb *qcb; bus_dmasync_op_t op; struct buf *bp; bp = bufq_first(&ida->buf_queue); if (bp == NULL) return; /* no more buffers */ qcb = ida_get_qcb(ida); if (qcb == NULL) return; /* out of resources */ bufq_remove(&ida->buf_queue, bp); qcb->buf = bp; qcb->flags = 0; hwqcb = qcb->hwqcb; bzero(hwqcb, sizeof(struct ida_hdr) + sizeof(struct ida_req)); bus_dmamap_load(ida->buffer_dmat, qcb->dmamap, (void *)bp->b_data, bp->b_bcount, ida_setup_dmamap, hwqcb, 0); op = qcb->flags & DMA_DATA_IN ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE; bus_dmamap_sync(ida->buffer_dmat, qcb->dmamap, op); /* * XXX */ { struct id_softc *drv = (struct id_softc *)bp->b_driver1; hwqcb->hdr.drive = drv->unit; } hwqcb->req.blkno = bp->b_pblkno; hwqcb->req.bcount = howmany(bp->b_bcount, DEV_BSIZE); hwqcb->req.command = bp->b_flags & B_READ ? CMD_READ : CMD_WRITE; STAILQ_INSERT_TAIL(&ida->qcb_queue, qcb, link.stqe); } static __inline bus_addr_t idahwqcbvtop(struct ida_softc *ida, struct ida_hardware_qcb *hwqcb) { return (ida->hwqcb_busaddr + ((bus_addr_t)hwqcb - (bus_addr_t)ida->hwqcbs)); } static __inline struct ida_qcb * idahwqcbptov(struct ida_softc *ida, bus_addr_t hwqcb_addr) { struct ida_hardware_qcb *hwqcb; hwqcb = (struct ida_hardware_qcb *) ((bus_addr_t)ida->hwqcbs + (hwqcb_addr - ida->hwqcb_busaddr)); return (hwqcb->qcb); } /* * This routine will be called from ida_intr in order to queue up more * I/O, meaning that we may be in an interrupt context. Hence, we should * not muck around with spl() in this routine. */ static void ida_start(struct ida_softc *ida) { struct ida_qcb *qcb; while ((qcb = STAILQ_FIRST(&ida->qcb_queue)) != NULL) { if (ida_inl(ida, R_CMD_FIFO) == 0) break; /* fifo is full */ STAILQ_REMOVE_HEAD(&ida->qcb_queue, link.stqe); /* * XXX * place the qcb on an active list and set a timeout? */ qcb->state = QCB_ACTIVE; /* * XXX * cache the physaddr so we don't keep doing this? */ ida_outl(ida, R_CMD_FIFO, idahwqcbvtop(ida, qcb->hwqcb)); } } static void ida_wait(struct ida_softc *ida, struct ida_qcb *qcb, int delay) { struct ida_qcb *qcb_done = NULL; bus_addr_t completed; if (ida->flags & IDA_ATTACHED) { if (tsleep((caddr_t)qcb, PRIBIO, "idacmd", delay)) panic("ida_command: timeout waiting for interrupt"); return; } while ((completed = ida_inl(ida, R_DONE_FIFO)) == 0) { if (delay-- == 0) panic("ida_wait: timeout waiting for completion"); DELAY(10); } qcb_done = idahwqcbptov(ida, completed & ~3); if (qcb_done != qcb) panic("ida_wait: incorrect qcb returned"); ida_done(ida, qcb); return; } void ida_intr(void *data) { struct ida_softc *ida; struct ida_qcb *qcb; bus_addr_t completed; ida = (struct ida_softc *)data; if (ida_inl(ida, R_INT_PENDING) == 0) return; /* not our interrupt */ while ((completed = ida_inl(ida, R_DONE_FIFO)) != 0) { qcb = idahwqcbptov(ida, completed & ~3); if (qcb == NULL || qcb->state != QCB_ACTIVE) { device_printf(ida->dev, "ignoring completion %x\n", completed); continue; } ida_done(ida, qcb); } ida_start(ida); } /* * should switch out command type; may be status, not just I/O. */ static void ida_done(struct ida_softc *ida, struct ida_qcb *qcb) { int error = 0; /* * finish up command */ if (qcb->flags & DMA_DATA_TRANSFER) { bus_dmasync_op_t op; op = qcb->flags & DMA_DATA_IN ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE; bus_dmamap_sync(ida->buffer_dmat, qcb->dmamap, op); bus_dmamap_unload(ida->buffer_dmat, qcb->dmamap); } if (qcb->hwqcb->req.error & SOFT_ERROR) device_printf(ida->dev, "soft error\n"); if (qcb->hwqcb->req.error & HARD_ERROR) { error = 1; device_printf(ida->dev, "hard error\n"); } if (qcb->hwqcb->req.error & CMD_REJECTED) { error = 1; device_printf(ida->dev, "invalid request\n"); } if (qcb->flags & IDA_COMMAND) { if (ida->flags & IDA_ATTACHED) wakeup(qcb); } else { if (error) qcb->buf->b_flags |= B_ERROR; id_intr(qcb->buf); } qcb->state = QCB_FREE; SLIST_INSERT_HEAD(&ida->free_qcbs, qcb, link.sle); ida_construct_qcb(ida); } Index: head/sys/dev/iicbus/iicbus.c =================================================================== --- head/sys/dev/iicbus/iicbus.c (revision 54072) +++ head/sys/dev/iicbus/iicbus.c (revision 54073) @@ -1,289 +1,289 @@ /*- * Copyright (c) 1998 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * */ /* * Autoconfiguration and support routines for the Philips serial I2C bus */ #include #include #include #include #include #include #include #include #include "iicbus_if.h" #define DEVTOIICBUS(dev) ((struct iicbus_device*)device_get_ivars(dev)) /* * structure used to attach devices to the I2C bus */ struct iicbus_device { const char *iicd_name; /* device name */ int iicd_class; /* driver or slave device class */ const char *iicd_desc; /* device descriptor */ u_char iicd_addr; /* address of the device */ int iicd_waitack; /* wait for ack timeout or delay */ int iicd_alive; /* 1 if device found */ }; /* * Common I2C addresses */ #define I2C_GENERAL_CALL 0x0 #define PCF_MASTER_ADDRESS 0xaa #define FIRST_SLAVE_ADDR 0x2 #define LAST_SLAVE_ADDR 255 #define IICBUS_UNKNOWN_CLASS 0 #define IICBUS_DEVICE_CLASS 1 #define IICBUS_DRIVER_CLASS 2 /* * list of known devices * * XXX only one smb driver should exist for each I2C interface */ static struct iicbus_device iicbus_children[] = { { "iicsmb", IICBUS_DRIVER_CLASS, "I2C to SMB bridge" }, { "iic", IICBUS_DRIVER_CLASS, "I2C general purpose I/O" }, #if 0 { "ic", IICBUS_DEVICE_CLASS, "network interface", PCF_MASTER_ADDRESS }, #endif { NULL, 0 } }; static devclass_t iicbus_devclass; /* * Device methods */ static int iicbus_probe(device_t); static int iicbus_attach(device_t); static int iicbus_print_child(device_t, device_t); static int iicbus_read_ivar(device_t , device_t, int, u_long *); static int iicbus_write_ivar(device_t , device_t, int, u_long); static device_method_t iicbus_methods[] = { /* device interface */ DEVMETHOD(device_probe, iicbus_probe), DEVMETHOD(device_attach, iicbus_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, iicbus_print_child), DEVMETHOD(bus_read_ivar, iicbus_read_ivar), DEVMETHOD(bus_write_ivar, iicbus_write_ivar), { 0, 0 } }; static driver_t iicbus_driver = { "iicbus", iicbus_methods, sizeof(struct iicbus_softc), }; static int iicbus_probe(device_t dev) { device_set_desc(dev, "Philips I2C bus"); return (0); } #if 0 static int iic_probe_device(device_t dev, u_char addr) { int count; char byte; if ((addr & 1) == 0) { /* is device writable? */ if (!iicbus_start(dev, (u_char)addr, 0)) { iicbus_stop(dev); return (1); } } else { /* is device readable? */ if (!iicbus_block_read(dev, (u_char)addr, &byte, 1, &count)) return (1); } return (0); } #endif /* * We add all the devices which we know about. * The generic attach routine will attach them if they are alive. */ static int iicbus_attach(device_t dev) { struct iicbus_device *iicdev; device_t child; iicbus_reset(dev, IIC_FASTEST, 0, NULL); /* device probing is meaningless since the bus is supposed to be * hot-plug. Moreover, some I2C chips do not appreciate random * accesses like stop after start to fast, reads for less than * x bytes... */ #if 0 printf("Probing for devices on iicbus%d:", device_get_unit(dev)); /* probe any devices */ for (addr = FIRST_SLAVE_ADDR; addr <= LAST_SLAVE_ADDR; addr++) { if (iic_probe_device(dev, (u_char)addr)) { printf(" <%x>", addr); } } printf("\n"); #endif /* attach known devices */ for (iicdev = iicbus_children; iicdev->iicd_name; iicdev++) { switch (iicdev->iicd_class) { case IICBUS_DEVICE_CLASS: /* check if the devclass exists */ if (devclass_find(iicdev->iicd_name)) iicdev->iicd_alive = 1; else if (bootverbose) printf("iicbus: %s devclass not found\n", iicdev->iicd_name); break; case IICBUS_DRIVER_CLASS: /* check if the devclass exists */ if (devclass_find(iicdev->iicd_name)) iicdev->iicd_alive = 1; else if (bootverbose) printf("iicbus: %s devclass not found\n", iicdev->iicd_name); break; default: panic("%s: unknown class!", __FUNCTION__); } if (iicdev->iicd_alive) { - child = device_add_child(dev, iicdev->iicd_name, - -1, iicdev); + child = device_add_child(dev, iicdev->iicd_name, -1); + device_set_ivars(child, iicdev); device_set_desc(child, iicdev->iicd_desc); } } bus_generic_attach(dev); return (0); } int iicbus_generic_intr(device_t dev, int event, char *buf) { return (0); } int iicbus_null_callback(device_t dev, int index, caddr_t data) { return (0); } int iicbus_null_repeated_start(device_t dev, u_char addr) { return (IIC_ENOTSUPP); } static int iicbus_print_child(device_t bus, device_t dev) { struct iicbus_device* iicdev = DEVTOIICBUS(dev); int retval = 0; retval += bus_print_child_header(bus, dev); switch (iicdev->iicd_class) { case IICBUS_DEVICE_CLASS: retval += printf(" on %s addr 0x%x\n", device_get_nameunit(bus), iicdev->iicd_addr); break; case IICBUS_DRIVER_CLASS: retval += bus_print_child_footer(bus, dev); break; default: panic("%s: unknown class!", __FUNCTION__); } return (retval); } static int iicbus_read_ivar(device_t bus, device_t dev, int index, u_long* result) { struct iicbus_device* iicdev = DEVTOIICBUS(dev); switch (index) { case IICBUS_IVAR_ADDR: *result = (u_long)iicdev->iicd_addr; break; default: return (ENOENT); } return (0); } static int iicbus_write_ivar(device_t bus, device_t dev, int index, u_long val) { switch (index) { default: return (ENOENT); } return (0); } DRIVER_MODULE(iicbus, pcf, iicbus_driver, iicbus_devclass, 0, 0); DRIVER_MODULE(iicbus, iicbb, iicbus_driver, iicbus_devclass, 0, 0); DRIVER_MODULE(iicbus, bti2c, iicbus_driver, iicbus_devclass, 0, 0); Index: head/sys/dev/iicbus/iiconf.c =================================================================== --- head/sys/dev/iicbus/iiconf.c (revision 54072) +++ head/sys/dev/iicbus/iiconf.c (revision 54073) @@ -1,367 +1,367 @@ /*- * Copyright (c) 1998 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * */ #include #include #include #include #include #include #include #include #include "iicbus_if.h" /* * iicbus_intr() */ void iicbus_intr(device_t bus, int event, char *buf) { struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); /* call owner's intr routine */ if (sc->owner) IICBUS_INTR(sc->owner, event, buf); return; } /* * iicbus_alloc_bus() * * Allocate a new bus connected to the given parent device */ device_t iicbus_alloc_bus(device_t parent) { device_t child; /* add the bus to the parent */ - child = device_add_child(parent, "iicbus", -1, NULL); + child = device_add_child(parent, "iicbus", -1); return (child); } static int iicbus_poll(struct iicbus_softc *sc, int how) { int error; switch (how) { case (IIC_WAIT | IIC_INTR): error = tsleep(sc, IICPRI|PCATCH, "iicreq", 0); break; case (IIC_WAIT | IIC_NOINTR): error = tsleep(sc, IICPRI, "iicreq", 0); break; default: return (EWOULDBLOCK); break; } return (error); } /* * iicbus_request_bus() * * Allocate the device to perform transfers. * * how : IIC_WAIT or IIC_DONTWAIT */ int iicbus_request_bus(device_t bus, device_t dev, int how) { struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); int s, error = 0; /* first, ask the underlying layers if the request is ok */ do { error = IICBUS_CALLBACK(device_get_parent(bus), IIC_REQUEST_BUS, (caddr_t)&how); if (error) error = iicbus_poll(sc, how); } while (error == EWOULDBLOCK); while (!error) { s = splhigh(); if (sc->owner && sc->owner != dev) { splx(s); error = iicbus_poll(sc, how); } else { sc->owner = dev; splx(s); return (0); } /* free any allocated resource */ if (error) IICBUS_CALLBACK(device_get_parent(bus), IIC_RELEASE_BUS, (caddr_t)&how); } return (error); } /* * iicbus_release_bus() * * Release the device allocated with iicbus_request_dev() */ int iicbus_release_bus(device_t bus, device_t dev) { struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); int s, error; /* first, ask the underlying layers if the release is ok */ error = IICBUS_CALLBACK(device_get_parent(bus), IIC_RELEASE_BUS, NULL); if (error) return (error); s = splhigh(); if (sc->owner != dev) { splx(s); return (EACCES); } sc->owner = 0; splx(s); /* wakeup waiting processes */ wakeup(sc); return (0); } /* * iicbus_started() * * Test if the iicbus is started by the controller */ int iicbus_started(device_t bus) { struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); return (sc->started); } /* * iicbus_start() * * Send start condition to the slave addressed by 'slave' */ int iicbus_start(device_t bus, u_char slave, int timeout) { struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); int error = 0; if (sc->started) return (EINVAL); /* bus already started */ if (!(error = IICBUS_START(device_get_parent(bus), slave, timeout))) sc->started = slave; else sc->started = 0; return (error); } /* * iicbus_repeated_start() * * Send start condition to the slave addressed by 'slave' */ int iicbus_repeated_start(device_t bus, u_char slave, int timeout) { struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); int error = 0; if (!sc->started) return (EINVAL); /* bus should have been already started */ if (!(error = IICBUS_REPEATED_START(device_get_parent(bus), slave, timeout))) sc->started = slave; else sc->started = 0; return (error); } /* * iicbus_stop() * * Send stop condition to the bus */ int iicbus_stop(device_t bus) { struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); int error = 0; if (!sc->started) return (EINVAL); /* bus not started */ error = IICBUS_STOP(device_get_parent(bus)); /* refuse any further access */ sc->started = 0; return (error); } /* * iicbus_write() * * Write a block of data to the slave previously started by * iicbus_start() call */ int iicbus_write(device_t bus, char *buf, int len, int *sent, int timeout) { struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); /* a slave must have been started with the appropriate address */ if (!sc->started || (sc->started & LSB)) return (EINVAL); return (IICBUS_WRITE(device_get_parent(bus), buf, len, sent, timeout)); } /* * iicbus_read() * * Read a block of data from the slave previously started by * iicbus_read() call */ int iicbus_read(device_t bus, char *buf, int len, int *read, int last, int delay) { struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); /* a slave must have been started with the appropriate address */ if (!sc->started || !(sc->started & LSB)) return (EINVAL); return (IICBUS_READ(device_get_parent(bus), buf, len, read, last, delay)); } /* * iicbus_write_byte() * * Write a byte to the slave previously started by iicbus_start() call */ int iicbus_write_byte(device_t bus, char byte, int timeout) { char data = byte; int sent; return (iicbus_write(bus, &data, 1, &sent, timeout)); } /* * iicbus_read_byte() * * Read a byte from the slave previously started by iicbus_start() call */ int iicbus_read_byte(device_t bus, char *byte, int timeout) { int read; return (iicbus_read(bus, byte, 1, &read, IIC_LAST_READ, timeout)); } /* * iicbus_block_write() * * Write a block of data to slave ; start/stop protocol managed */ int iicbus_block_write(device_t bus, u_char slave, char *buf, int len, int *sent) { u_char addr = slave & ~LSB; int error; if ((error = iicbus_start(bus, addr, 0))) return (error); error = iicbus_write(bus, buf, len, sent, 0); iicbus_stop(bus); return (error); } /* * iicbus_block_read() * * Read a block of data from slave ; start/stop protocol managed */ int iicbus_block_read(device_t bus, u_char slave, char *buf, int len, int *read) { u_char addr = slave | LSB; int error; if ((error = iicbus_start(bus, addr, 0))) return (error); error = iicbus_read(bus, buf, len, read, IIC_LAST_READ, 0); iicbus_stop(bus); return (error); } /* * iicbus_get_addr() * * Get the I2C 7 bits address of the device */ u_char iicbus_get_addr(device_t dev) { uintptr_t addr; device_t parent = device_get_parent(dev); BUS_READ_IVAR(parent, dev, IICBUS_IVAR_ADDR, &addr); return ((u_char)addr); } Index: head/sys/dev/mca/mca_bus.c =================================================================== --- head/sys/dev/mca/mca_bus.c (revision 54072) +++ head/sys/dev/mca/mca_bus.c (revision 54073) @@ -1,533 +1,535 @@ /*- * Copyright (c) 1999 Matthew N. Dodd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * References: * The CMU Mach3 microkernel * NetBSD MCA patches by Scott Telford * Linux MCA code. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_COL 79 static void mca_reg_print (device_t, char *, char *, int *); struct mca_device { struct resource_list rl; /* Resources */ mca_id_t id; u_int8_t slot; u_int8_t enabled; u_int8_t pos[8]; /* Programable Option Select Regs. */ }; /* Not supposed to use this function! */ void mca_pos_set (dev, reg, data) device_t dev; u_int8_t reg; u_int8_t data; { struct mca_device * m_dev = device_get_ivars(dev); u_int8_t slot = mca_get_slot(dev); if ((slot > MCA_MAX_ADAPTERS) || (reg > MCA_POS7)) return; /* Disable motherboard setup */ outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS); /* Select adapter setup regs */ outb(MCA_ADAP_SETUP_REG, ((slot & 0x0f) | MCA_ADAP_SET)); /* Write the register */ outb(MCA_POS_REG(reg), data); /* Disable adapter setup */ outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS); /* Update the IVAR copy */ m_dev->pos[reg] = data; return; } u_int8_t mca_pos_get (dev, reg) device_t dev; u_int8_t reg; { u_int8_t slot = mca_get_slot(dev); u_int8_t data = 0; if ((slot > MCA_MAX_ADAPTERS) || (reg > MCA_POS7)) return (0); /* Disable motherboard setup */ outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS); switch (slot) { case MCA_MB_SCSI_SLOT: /* Disable adapter setup */ outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS); /* Select motherboard video setup regs */ outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_SCSI); /* read the register */ data = inb(MCA_POS_REG(reg)); /* Disable motherboard setup */ outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS); break; case MCA_MB_VIDEO_SLOT: /* Disable adapter setup */ outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS); /* Select motherboard scsi setup regs */ outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_VIDEO); /* read the register */ data = inb(MCA_POS_REG(reg)); /* Disable motherboard setup */ outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS); break; default: /* Select adapter setup regs */ outb(MCA_ADAP_SETUP_REG, ((slot & 0x0f) | MCA_ADAP_SET)); /* read the register */ data = inb(MCA_POS_REG(reg)); /* Disable adapter setup */ outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS); break; } return (data); } const char * mca_match_id (id, mca_devs) u_int16_t id; struct mca_ident * mca_devs; { struct mca_ident * m = mca_devs; while(m->name != NULL) { if (id == m->id) return (m->name); m++; } return (NULL); } u_int8_t mca_pos_read (dev, reg) device_t dev; u_int8_t reg; { struct mca_device * m_dev = device_get_ivars(dev); if (reg > MCA_POS7) return (0); return (m_dev->pos[reg]); } void mca_add_irq (dev, irq) device_t dev; int irq; { struct mca_device * m_dev = device_get_ivars(dev); int rid = 0; while (resource_list_find(&(m_dev->rl), SYS_RES_IRQ, rid)) rid++; resource_list_add(&(m_dev->rl), SYS_RES_IRQ, rid, irq, irq, 1); return; } void mca_add_drq (dev, drq) device_t dev; int drq; { struct mca_device * m_dev = device_get_ivars(dev); int rid = 0; while (resource_list_find(&(m_dev->rl), SYS_RES_DRQ, rid)) rid++; resource_list_add(&(m_dev->rl), SYS_RES_DRQ, rid, drq, drq, 1); return; } void mca_add_mspace (dev, mbase, msize) device_t dev; u_long mbase; u_long msize; { struct mca_device * m_dev = device_get_ivars(dev); int rid = 0; while (resource_list_find(&(m_dev->rl), SYS_RES_MEMORY, rid)) rid++; resource_list_add(&(m_dev->rl), SYS_RES_MEMORY, rid, mbase, (mbase + msize), msize); return; } void mca_add_iospace (dev, iobase, iosize) device_t dev; u_long iobase; u_long iosize; { struct mca_device * m_dev = device_get_ivars(dev); int rid = 0; while (resource_list_find(&(m_dev->rl), SYS_RES_IOPORT, rid)) rid++; resource_list_add(&(m_dev->rl), SYS_RES_IOPORT, rid, iobase, (iobase + iosize), iosize); return; } static int mca_probe (device_t dev) { + device_t child; struct mca_device * m_dev = NULL; int devices_found = 0; u_int8_t slot; u_int8_t reg; device_set_desc(dev, "MCA bus"); /* Disable adapter setup */ outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS); /* Disable motherboard setup */ outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS); if (bootverbose) { printf("POS REG 00 01 02 03 04 05 06 07\n"); printf("-----------------------------------\n"); } for (slot = 0; slot < MCA_MAX_SLOTS; slot++) { if (!m_dev) { m_dev = (struct mca_device *)malloc(sizeof(*m_dev), M_DEVBUF, M_NOWAIT); if (!m_dev) { device_printf(dev, "cannot malloc mca_device"); break; } } bzero(m_dev, sizeof(*m_dev)); /* Select adapter setup regs */ outb(MCA_ADAP_SETUP_REG, ((slot & 0x0f) | MCA_ADAP_SET)); /* Read the POS registers */ for (reg = MCA_POS0; reg <= MCA_POS7; reg++) { m_dev->pos[reg] = inb(MCA_POS_REG(reg)); } /* Disable adapter setup */ outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS); if (bootverbose) { printf("mca slot %d:", slot + 1); for (reg = MCA_POS0; reg <= MCA_POS7; reg++) { printf(" %02x", m_dev->pos[reg]); } printf("\n"); } m_dev->id = (u_int16_t)m_dev->pos[MCA_POS0] | ((u_int16_t)m_dev->pos[MCA_POS1] << 8); if (m_dev->id == 0xffff) { continue; } devices_found++; m_dev->enabled = (m_dev->pos[MCA_POS2] & MCA_POS2_ENABLE); m_dev->slot = slot; resource_list_init(&(m_dev->rl)); - device_add_child(dev, NULL, -1, m_dev); + child = device_add_child(dev, NULL, -1); + device_set_ivars(child, m_dev); m_dev = NULL; } if (m_dev) { free(m_dev, M_DEVBUF); } return (devices_found ? 0 : ENXIO); } static void mca_reg_print (dev, string, separator, column) device_t dev; char * string; char * separator; int * column; { int length = strlen(string); length += (separator ? 2 : 1); if (((*column) + length) >= MAX_COL) { printf("\n"); (*column) = 0; } else if ((*column) != 0) { if (separator) { printf("%c", *separator); (*column)++; } printf(" "); (*column)++; } if ((*column) == 0) { (*column) += device_printf(dev, "%s", string); } else { (*column) += printf("%s", string); } return; } static int mca_print_child (device_t dev, device_t child) { char buf[MAX_COL+1]; struct mca_device * m_dev = device_get_ivars(child); int rid; struct resource_list_entry * rle; char separator = ','; int column = 0; int retval = 0; if (device_get_desc(child)) { snprintf(buf, sizeof(buf), "<%s>", device_get_desc(child)); mca_reg_print(child, buf, NULL, &column); } rid = 0; while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_IOPORT, rid++))) { if (rle->count == 1) { snprintf(buf, sizeof(buf), "%s%lx", ((rid == 1) ? "io 0x" : "0x"), rle->start); } else { snprintf(buf, sizeof(buf), "%s%lx-0x%lx", ((rid == 1) ? "io 0x" : "0x"), rle->start, (rle->start + rle->count)); } mca_reg_print(child, buf, ((rid == 2) ? &separator : NULL), &column); } rid = 0; while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_MEMORY, rid++))) { if (rle->count == 1) { snprintf(buf, sizeof(buf), "%s%lx", ((rid == 1) ? "mem 0x" : "0x"), rle->start); } else { snprintf(buf, sizeof(buf), "%s%lx-0x%lx", ((rid == 1) ? "mem 0x" : "0x"), rle->start, (rle->start + rle->count)); } mca_reg_print(child, buf, ((rid == 2) ? &separator : NULL), &column); } rid = 0; while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_IRQ, rid++))) { snprintf(buf, sizeof(buf), "irq %ld", rle->start); mca_reg_print(child, buf, ((rid == 1) ? &separator : NULL), &column); } rid = 0; while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_DRQ, rid++))) { snprintf(buf, sizeof(buf), "drq %lx", rle->start); mca_reg_print(child, buf, ((rid == 1) ? &separator : NULL), &column); } snprintf(buf, sizeof(buf), "on %s id %04x slot %d\n", device_get_nameunit(dev), mca_get_id(child), mca_get_slot(child)+1); mca_reg_print(child, buf, NULL, &column); return (retval); } static void mca_probe_nomatch (device_t dev, device_t child) { mca_id_t mca_id = mca_get_id(child); u_int8_t slot = mca_get_slot(child); u_int8_t enabled = mca_get_enabled(child); device_printf(dev, "unknown card (id 0x%04x, %s) at slot %d\n", mca_id, (enabled ? "enabled" : "disabled"), slot + 1); return; } static int mca_read_ivar (device_t dev, device_t child, int which, u_long * result) { struct mca_device * m_dev = device_get_ivars(child); switch (which) { case MCA_IVAR_SLOT: *result = m_dev->slot; break; case MCA_IVAR_ID: *result = m_dev->id; break; case MCA_IVAR_ENABLED: *result = m_dev->enabled; break; default: return (ENOENT); break; } return (0); } static int mca_write_ivar(device_t dev, device_t child, int which, uintptr_t value) { return (EINVAL); } static struct resource * mca_alloc_resource (device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct mca_device * m_dev = device_get_ivars(child); struct resource_list_entry * rle; int isdefault; int passthrough; isdefault = (start == 0UL && end == ~0UL); passthrough = (device_get_parent(child) != dev); if (!passthrough && !isdefault) { rle = resource_list_find(&(m_dev->rl), type, *rid); if (!rle) { resource_list_add(&(m_dev->rl), type, *rid, start, end, count); } } return (resource_list_alloc(&(m_dev->rl), dev, child, type, rid, start, end, count, flags)); } static int mca_release_resource (device_t dev, device_t child, int type, int rid, struct resource * r) { struct mca_device * m_dev = device_get_ivars(child); return (resource_list_release(&(m_dev->rl), dev, child, type, rid, r)); } static device_method_t mca_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mca_probe), DEVMETHOD(device_attach, bus_generic_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, mca_print_child), DEVMETHOD(bus_probe_nomatch, mca_probe_nomatch), DEVMETHOD(bus_read_ivar, mca_read_ivar), DEVMETHOD(bus_write_ivar, mca_write_ivar), DEVMETHOD(bus_driver_added, bus_generic_driver_added), DEVMETHOD(bus_alloc_resource, mca_alloc_resource), DEVMETHOD(bus_release_resource, mca_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t mca_driver = { "mca", mca_methods, 1, /* no softc */ }; static devclass_t mca_devclass; DRIVER_MODULE(mca, nexus, mca_driver, mca_devclass, 0, 0); Index: head/sys/dev/mii/mii.c =================================================================== --- head/sys/dev/mii/mii.c (revision 54072) +++ head/sys/dev/mii/mii.c (revision 54073) @@ -1,327 +1,329 @@ /* $NetBSD: mii.c,v 1.12 1999/08/03 19:41:49 drochner Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, * NASA Ames Research Center. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * MII bus layer, glues MII-capable network interface drivers to sharable * PHY drivers. This exports an interface compatible with BSD/OS 3.0's, * plus some NetBSD extensions. */ #include #include #include #include #include #include #include #include #include #include #include "miibus_if.h" #if !defined(lint) static const char rcsid[] = "$FreeBSD$"; #endif static int miibus_readreg __P((device_t, int, int)); static int miibus_writereg __P((device_t, int, int, int)); static void miibus_statchg __P((device_t)); static void miibus_mediainit __P((device_t)); static device_method_t miibus_methods[] = { /* device interface */ DEVMETHOD(device_probe, miibus_probe), DEVMETHOD(device_attach, miibus_attach), DEVMETHOD(device_detach, miibus_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, miibus_readreg), DEVMETHOD(miibus_writereg, miibus_writereg), DEVMETHOD(miibus_statchg, miibus_statchg), DEVMETHOD(miibus_mediainit, miibus_mediainit), { 0, 0 } }; devclass_t miibus_devclass; driver_t miibus_driver = { "miibus", miibus_methods, sizeof(struct mii_data) }; /* * Helper function used by network interface drivers, attaches PHYs * to the network interface driver parent. */ int miibus_probe(dev) device_t dev; { struct mii_attach_args ma, *args; struct mii_data *mii; device_t child = NULL, parent; int bmsr, capmask = 0xFFFFFFFF; mii = device_get_softc(dev); parent = device_get_parent(dev); LIST_INIT(&mii->mii_phys); for (ma.mii_phyno = 0; ma.mii_phyno < MII_NPHY; ma.mii_phyno++) { /* * Check to see if there is a PHY at this address. Note, * many braindead PHYs report 0/0 in their ID registers, * so we test for media in the BMSR. */ bmsr = MIIBUS_READREG(parent, ma.mii_phyno, MII_BMSR); if (bmsr == 0 || bmsr == 0xffff || (bmsr & BMSR_MEDIAMASK) == 0) { /* Assume no PHY at this address. */ continue; } /* * Extract the IDs. Braindead PHYs will be handled by * the `ukphy' driver, as we have no ID information to * match on. */ ma.mii_id1 = MIIBUS_READREG(parent, ma.mii_phyno, MII_PHYIDR1); ma.mii_id2 = MIIBUS_READREG(parent, ma.mii_phyno, MII_PHYIDR2); ma.mii_data = mii; ma.mii_capmask = capmask; args = malloc(sizeof(struct mii_attach_args), M_DEVBUF, M_NOWAIT); bcopy((char *)&ma, (char *)args, sizeof(ma)); - child = device_add_child(dev, NULL, -1, args); + child = device_add_child(dev, NULL, -1); + device_set_ivars(child, args); } if (child == NULL) return(ENXIO); device_set_desc(dev, "MII bus"); return(0); } int miibus_attach(dev) device_t dev; { void **v; ifm_change_cb_t ifmedia_upd; ifm_stat_cb_t ifmedia_sts; struct mii_data *mii; mii = device_get_softc(dev); mii->mii_ifp = device_get_softc(device_get_parent(dev)); v = device_get_ivars(dev); ifmedia_upd = v[0]; ifmedia_sts = v[1]; ifmedia_init(&mii->mii_media, IFM_IMASK, ifmedia_upd, ifmedia_sts); bus_generic_attach(dev); return(0); } int miibus_detach(dev) device_t dev; { struct mii_data *mii; bus_generic_detach(dev); mii = device_get_softc(dev); ifmedia_removeall(&mii->mii_media); mii->mii_ifp = NULL; return(0); } static int miibus_readreg(dev, phy, reg) device_t dev; int phy, reg; { device_t parent; parent = device_get_parent(dev); return(MIIBUS_READREG(parent, phy, reg)); } static int miibus_writereg(dev, phy, reg, data) device_t dev; int phy, reg, data; { device_t parent; parent = device_get_parent(dev); return(MIIBUS_WRITEREG(parent, phy, reg, data)); } static void miibus_statchg(dev) device_t dev; { device_t parent; parent = device_get_parent(dev); MIIBUS_STATCHG(parent); return; } static void miibus_mediainit(dev) device_t dev; { struct mii_data *mii; struct ifmedia_entry *m; int media = 0; /* Poke the parent in case it has any media of its own to add. */ MIIBUS_MEDIAINIT(device_get_parent(dev)); mii = device_get_softc(dev); for (m = LIST_FIRST(&mii->mii_media.ifm_list); m != NULL; m = LIST_NEXT(m, ifm_list)) { media = m->ifm_media; if (media == (IFM_ETHER|IFM_AUTO)) break; } ifmedia_set(&mii->mii_media, media); return; } int mii_phy_probe(dev, child, ifmedia_upd, ifmedia_sts) device_t dev; device_t *child; ifm_change_cb_t ifmedia_upd; ifm_stat_cb_t ifmedia_sts; { void **v; int bmsr, i; v = malloc(sizeof(vm_offset_t) * 2, M_DEVBUF, M_NOWAIT); v[0] = ifmedia_upd; v[1] = ifmedia_sts; - *child = device_add_child(dev, "miibus", -1, v); + *child = device_add_child(dev, "miibus", -1); + device_set_ivars(*child, v); for (i = 0; i < MII_NPHY; i++) { bmsr = MIIBUS_READREG(dev, i, MII_BMSR); if (bmsr == 0 || bmsr == 0xffff || (bmsr & BMSR_MEDIAMASK) == 0) { /* Assume no PHY at this address. */ continue; } else break; } if (i == MII_NPHY) { device_delete_child(dev, *child); *child = NULL; return(ENXIO); } bus_generic_attach(dev); return(0); } /* * Media changed; notify all PHYs. */ int mii_mediachg(mii) struct mii_data *mii; { struct mii_softc *child; int rv; mii->mii_media_status = 0; mii->mii_media_active = IFM_NONE; for (child = LIST_FIRST(&mii->mii_phys); child != NULL; child = LIST_NEXT(child, mii_list)) { rv = (*child->mii_service)(child, mii, MII_MEDIACHG); if (rv) return (rv); } return (0); } /* * Call the PHY tick routines, used during autonegotiation. */ void mii_tick(mii) struct mii_data *mii; { struct mii_softc *child; for (child = LIST_FIRST(&mii->mii_phys); child != NULL; child = LIST_NEXT(child, mii_list)) (void) (*child->mii_service)(child, mii, MII_TICK); } /* * Get media status from PHYs. */ void mii_pollstat(mii) struct mii_data *mii; { struct mii_softc *child; mii->mii_media_status = 0; mii->mii_media_active = IFM_NONE; for (child = LIST_FIRST(&mii->mii_phys); child != NULL; child = LIST_NEXT(child, mii_list)) (void) (*child->mii_service)(child, mii, MII_POLLSTAT); } Index: head/sys/dev/mlx/mlx.c =================================================================== --- head/sys/dev/mlx/mlx.c (revision 54072) +++ head/sys/dev/mlx/mlx.c (revision 54073) @@ -1,2411 +1,2412 @@ /*- * Copyright (c) 1999 Michael Smith * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Driver for the Mylex DAC960 family of RAID controllers. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if 0 #define debug(fmt, args...) printf("%s: " fmt "\n", __FUNCTION__ , ##args) #else #define debug(fmt, args...) #endif #define MLX_CDEV_MAJOR 130 static struct cdevsw mlx_cdevsw = { /* open */ mlx_open, /* close */ mlx_close, /* read */ noread, /* write */ nowrite, /* ioctl */ mlx_ioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "mlx", /* maj */ MLX_CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; static int cdev_registered = 0; devclass_t mlx_devclass; /* * Per-interface accessor methods */ static int mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc); static int mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status); static void mlx_v3_intaction(struct mlx_softc *sc, int action); static int mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc); static int mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status); static void mlx_v4_intaction(struct mlx_softc *sc, int action); /* * Status monitoring */ static void mlx_periodic(void *data); static void mlx_periodic_enquiry(struct mlx_command *mc); static void mlx_periodic_eventlog_poll(struct mlx_softc *sc); static void mlx_periodic_eventlog_respond(struct mlx_command *mc); static void mlx_periodic_rebuild(struct mlx_command *mc); /* * Channel Pause */ static void mlx_pause_action(struct mlx_softc *sc); static void mlx_pause_done(struct mlx_command *mc); /* * Command submission. */ static void *mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, void (*complete)(struct mlx_command *mc)); static int mlx_flush(struct mlx_softc *sc); static int mlx_rebuild(struct mlx_softc *sc, int channel, int target); static int mlx_wait_command(struct mlx_command *mc); static int mlx_poll_command(struct mlx_command *mc); static void mlx_startio(struct mlx_softc *sc); static void mlx_completeio(struct mlx_command *mc); static int mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu); /* * Command buffer allocation. */ static struct mlx_command *mlx_alloccmd(struct mlx_softc *sc); static void mlx_releasecmd(struct mlx_command *mc); static void mlx_freecmd(struct mlx_command *mc); /* * Command management. */ static int mlx_getslot(struct mlx_command *mc); static void mlx_mapcmd(struct mlx_command *mc); static void mlx_unmapcmd(struct mlx_command *mc); static int mlx_start(struct mlx_command *mc); static int mlx_done(struct mlx_softc *sc); static void mlx_complete(struct mlx_softc *sc); /* * Debugging. */ static char *mlx_diagnose_command(struct mlx_command *mc); static char *mlx_name_controller(u_int32_t hwid); /* * Utility functions. */ static struct mlx_sysdrive *mlx_findunit(struct mlx_softc *sc, int unit); /******************************************************************************** ******************************************************************************** Public Interfaces ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Free all of the resources associated with (sc) * * Should not be called if the controller is active. */ void mlx_free(struct mlx_softc *sc) { struct mlx_command *mc; debug("called"); /* cancel status timeout */ untimeout(mlx_periodic, sc, sc->mlx_timeout); /* throw away any command buffers */ while ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) { TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link); mlx_freecmd(mc); } /* destroy data-transfer DMA tag */ if (sc->mlx_buffer_dmat) bus_dma_tag_destroy(sc->mlx_buffer_dmat); /* free and destroy DMA memory and tag for s/g lists */ if (sc->mlx_sgtable) bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap); if (sc->mlx_sg_dmat) bus_dma_tag_destroy(sc->mlx_sg_dmat); /* disconnect the interrupt handler */ if (sc->mlx_intr) bus_teardown_intr(sc->mlx_dev, sc->mlx_irq, sc->mlx_intr); if (sc->mlx_irq != NULL) bus_release_resource(sc->mlx_dev, SYS_RES_IRQ, 0, sc->mlx_irq); /* destroy the parent DMA tag */ if (sc->mlx_parent_dmat) bus_dma_tag_destroy(sc->mlx_parent_dmat); /* release the register window mapping */ if (sc->mlx_mem != NULL) bus_release_resource(sc->mlx_dev, SYS_RES_MEMORY, (sc->mlx_iftype == MLX_IFTYPE_3) ? MLX_CFG_BASE1 : MLX_CFG_BASE0, sc->mlx_mem); } /******************************************************************************** * Map the scatter/gather table into bus space */ static void mlx_dma_map_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct mlx_softc *sc = (struct mlx_softc *)arg; debug("called"); /* save base of s/g table's address in bus space */ sc->mlx_sgbusaddr = segs->ds_addr; } static int mlx_sglist_map(struct mlx_softc *sc) { size_t segsize; int error; debug("called"); /* destroy any existing mappings */ if (sc->mlx_sgtable) bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap); if (sc->mlx_sg_dmat) bus_dma_tag_destroy(sc->mlx_sg_dmat); /* * Create a single tag describing a region large enough to hold all of * the s/g lists we will need. */ segsize = sizeof(struct mlx_sgentry) * MLX_NSEG * sc->mlx_maxiop; error = bus_dma_tag_create(sc->mlx_parent_dmat, /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ segsize, 1, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ &sc->mlx_sg_dmat); if (error != 0) { device_printf(sc->mlx_dev, "can't allocate scatter/gather DMA tag\n"); return(ENOMEM); } /* * Allocate enough s/g maps for all commands and permanently map them into * controller-visible space. * * XXX this assumes we can get enough space for all the s/g maps in one * contiguous slab. We may need to switch to a more complex arrangement where * we allocate in smaller chunks and keep a lookup table from slot to bus address. */ error = bus_dmamem_alloc(sc->mlx_sg_dmat, (void **)&sc->mlx_sgtable, BUS_DMA_NOWAIT, &sc->mlx_sg_dmamap); if (error) { device_printf(sc->mlx_dev, "can't allocate s/g table\n"); return(ENOMEM); } bus_dmamap_load(sc->mlx_sg_dmat, sc->mlx_sg_dmamap, sc->mlx_sgtable, segsize, mlx_dma_map_sg, sc, 0); return(0); } /******************************************************************************** * Initialise the controller and softc */ int mlx_attach(struct mlx_softc *sc) { struct mlx_enquiry *me; struct mlx_enquiry2 *me2; int rid, error; debug("called"); /* * Initialise per-controller queues. */ TAILQ_INIT(&sc->mlx_work); TAILQ_INIT(&sc->mlx_freecmds); bufq_init(&sc->mlx_bufq); /* * Select accessor methods based on controller interface type. */ switch(sc->mlx_iftype) { case MLX_IFTYPE_3: sc->mlx_tryqueue = mlx_v3_tryqueue; sc->mlx_findcomplete = mlx_v3_findcomplete; sc->mlx_intaction = mlx_v3_intaction; break; case MLX_IFTYPE_4: sc->mlx_tryqueue = mlx_v4_tryqueue; sc->mlx_findcomplete = mlx_v4_findcomplete; sc->mlx_intaction = mlx_v4_intaction; break; default: device_printf(sc->mlx_dev, "attaching unsupported interface version %d\n", sc->mlx_iftype); return(ENXIO); /* should never happen */ } /* disable interrupts before we start talking to the controller */ sc->mlx_intaction(sc, MLX_INTACTION_DISABLE); /* * Allocate and connect our interrupt. */ rid = 0; sc->mlx_irq = bus_alloc_resource(sc->mlx_dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); if (sc->mlx_irq == NULL) { device_printf(sc->mlx_dev, "couldn't allocate interrupt\n"); mlx_free(sc); return(ENXIO); } error = bus_setup_intr(sc->mlx_dev, sc->mlx_irq, INTR_TYPE_BIO, mlx_intr, sc, &sc->mlx_intr); if (error) { device_printf(sc->mlx_dev, "couldn't set up interrupt\n"); mlx_free(sc); return(ENXIO); } /* * Create DMA tag for mapping buffers into controller-addressable space. */ error = bus_dma_tag_create(sc->mlx_parent_dmat, /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, MLX_NSEG, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ &sc->mlx_buffer_dmat); if (error != 0) { device_printf(sc->mlx_dev, "can't allocate buffer DMA tag\n"); return(ENOMEM); } /* * Create an initial set of s/g mappings. */ sc->mlx_maxiop = 2; error = mlx_sglist_map(sc); if (error != 0) { device_printf(sc->mlx_dev, "couldn't make initial s/g list mapping\n"); return(error); } /* * Probe the controller for more information. */ /* send an ENQUIRY to the controller */ if ((me = mlx_enquire(sc, MLX_CMD_ENQUIRY, sizeof(*me), NULL)) == NULL) { device_printf(sc->mlx_dev, "ENQUIRY failed\n"); return(ENXIO); } /* pull information out of the ENQUIRY result */ sc->mlx_fwminor = me->me_fwminor; sc->mlx_fwmajor = me->me_fwmajor; sc->mlx_maxiop = me->me_max_commands; sc->mlx_lastevent = sc->mlx_currevent = me->me_event_log_seq_num; free(me, M_DEVBUF); /* send an ENQUIRY2 to the controller */ if ((me2 = mlx_enquire(sc, MLX_CMD_ENQUIRY2, sizeof(*me2), NULL)) == NULL) { device_printf(sc->mlx_dev, "ENQUIRY2 failed\n"); return(ENXIO); } /* pull information out of the ENQUIRY2 result */ sc->mlx_nchan = me2->me_configured_channels; sc->mlx_maxiosize = me2->me_maxblk; sc->mlx_maxtarg = me2->me_max_targets; sc->mlx_maxtags = me2->me_max_tags; sc->mlx_scsicap = me2->me_scsi_cap; sc->mlx_hwid = me2->me_hardware_id; /* print a little information about the controller and ourselves */ device_printf(sc->mlx_dev, "Mylex %s, firmware %d.%02d, %dMB RAM\n", mlx_name_controller(sc->mlx_hwid), sc->mlx_fwmajor, sc->mlx_fwminor, me2->me_mem_size / (1024 * 1024)); free(me2, M_DEVBUF); /* * Do quirk/feature related things. */ switch(sc->mlx_iftype) { case MLX_IFTYPE_3: /* XXX certify 3.52? */ if (sc->mlx_fwminor < 51) { device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n"); device_printf(sc->mlx_dev, " *** WARNING *** Use revision 3.51 or later\n"); } break; case MLX_IFTYPE_4: /* XXX certify firmware versions? */ if (sc->mlx_fwminor < 6) { device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n"); device_printf(sc->mlx_dev, " *** WARNING *** Use revision 4.06 or later\n"); } break; default: device_printf(sc->mlx_dev, "interface version corrupted to %d\n", sc->mlx_iftype); return(ENXIO); /* should never happen */ } /* * Create the final set of s/g mappings now that we know how many commands * the controller actually supports. */ error = mlx_sglist_map(sc); if (error != 0) { device_printf(sc->mlx_dev, "couldn't make initial s/g list mapping\n"); return(error); } /* * No rebuild or check is in progress. */ sc->mlx_rebuild = -1; sc->mlx_check = -1; /* * Register the control device on first attach. */ if (cdev_registered++ == 0) cdevsw_add(&mlx_cdevsw); /* * Start the timeout routine. */ sc->mlx_timeout = timeout(mlx_periodic, sc, hz); return(0); } /******************************************************************************** * Locate disk resources and attach children to them. */ void mlx_startup(struct mlx_softc *sc) { struct mlx_enq_sys_drive *mes; struct mlx_sysdrive *dr; int i, error; debug("called"); /* * Scan all the system drives and attach children for those that * don't currently have them. */ mes = mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(*mes) * MLX_MAXDRIVES, NULL); if (mes == NULL) { device_printf(sc->mlx_dev, "error fetching drive status"); return; } /* iterate over drives returned */ for (i = 0, dr = &sc->mlx_sysdrive[0]; (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff); i++, dr++) { /* are we already attached to this drive? */ if (dr->ms_disk == 0) { /* pick up drive information */ dr->ms_size = mes[i].sd_size; dr->ms_raidlevel = mes[i].sd_raidlevel & 0xf; dr->ms_state = mes[i].sd_state; /* generate geometry information */ if (sc->mlx_geom == MLX_GEOM_128_32) { dr->ms_heads = 128; dr->ms_sectors = 32; dr->ms_cylinders = dr->ms_size / (128 * 32); } else { /* MLX_GEOM_255/63 */ dr->ms_heads = 255; dr->ms_sectors = 63; dr->ms_cylinders = dr->ms_size / (255 * 63); } - dr->ms_disk = device_add_child(sc->mlx_dev, /*"mlxd"*/NULL, -1, dr); + dr->ms_disk = device_add_child(sc->mlx_dev, /*"mlxd"*/NULL, -1); if (dr->ms_disk == 0) device_printf(sc->mlx_dev, "device_add_child failed\n"); + device_set_ivars(dr->ms_disk, dr); } } free(mes, M_DEVBUF); if ((error = bus_generic_attach(sc->mlx_dev)) != 0) device_printf(sc->mlx_dev, "bus_generic_attach returned %d", error); /* mark controller back up */ sc->mlx_state &= ~MLX_STATE_SHUTDOWN; /* enable interrupts */ sc->mlx_intaction(sc, MLX_INTACTION_ENABLE); } /******************************************************************************** * Disconnect from the controller completely, in preparation for unload. */ int mlx_detach(device_t dev) { struct mlx_softc *sc = device_get_softc(dev); int error; debug("called"); if (sc->mlx_state & MLX_STATE_OPEN) return(EBUSY); if ((error = mlx_shutdown(dev))) return(error); mlx_free(sc); /* * Deregister the control device on last detach. */ if (--cdev_registered == 0) cdevsw_remove(&mlx_cdevsw); return(0); } /******************************************************************************** * Bring the controller down to a dormant state and detach all child devices. * * This function is called before detach, system shutdown, or before performing * an operation which may add or delete system disks. (Call mlx_startup to * resume normal operation.) * * Note that we can assume that the bufq on the controller is empty, as we won't * allow shutdown if any device is open. */ int mlx_shutdown(device_t dev) { struct mlx_softc *sc = device_get_softc(dev); struct mlxd_softc *mlxd; int i, s, error; debug("called"); s = splbio(); error = 0; /* assume we're going to shut down */ sc->mlx_state |= MLX_STATE_SHUTDOWN; for (i = 0; i < MLX_MAXDRIVES; i++) { if (sc->mlx_sysdrive[i].ms_disk != 0) { mlxd = device_get_softc(sc->mlx_sysdrive[i].ms_disk); if (mlxd->mlxd_flags & MLXD_OPEN) { /* drive is mounted, abort shutdown */ sc->mlx_state &= ~MLX_STATE_SHUTDOWN; device_printf(sc->mlx_sysdrive[i].ms_disk, "still open, can't shutdown\n"); error = EBUSY; goto out; } } } /* flush controller */ device_printf(sc->mlx_dev, "flushing cache..."); if (mlx_flush(sc)) { printf("failed\n"); } else { printf("done\n"); } /* delete all our child devices */ for (i = 0; i < MLX_MAXDRIVES; i++) { if (sc->mlx_sysdrive[i].ms_disk != 0) { if ((error = device_delete_child(sc->mlx_dev, sc->mlx_sysdrive[i].ms_disk)) != 0) goto out; sc->mlx_sysdrive[i].ms_disk = 0; } } bus_generic_detach(sc->mlx_dev); out: splx(s); return(error); } /******************************************************************************** * Bring the controller to a quiescent state, ready for system suspend. */ int mlx_suspend(device_t dev) { struct mlx_softc *sc = device_get_softc(dev); int s; debug("called"); s = splbio(); sc->mlx_state |= MLX_STATE_SUSPEND; /* flush controller */ device_printf(sc->mlx_dev, "flushing cache..."); printf("%s\n", mlx_flush(sc) ? "failed" : "done"); sc->mlx_intaction(sc, MLX_INTACTION_DISABLE); splx(s); return(0); } /******************************************************************************** * Bring the controller back to a state ready for operation. */ int mlx_resume(device_t dev) { struct mlx_softc *sc = device_get_softc(dev); debug("called"); sc->mlx_state &= ~MLX_STATE_SUSPEND; sc->mlx_intaction(sc, MLX_INTACTION_ENABLE); return(0); } /******************************************************************************* * Take an interrupt, or be poked by other code to look for interrupt-worthy * status. */ void mlx_intr(void *arg) { struct mlx_softc *sc = (struct mlx_softc *)arg; int worked; debug("called"); /* spin collecting finished commands */ worked = 0; while (mlx_done(sc)) worked = 1; /* did we do anything? */ if (worked) mlx_complete(sc); }; /******************************************************************************* * Receive a buf structure from a child device and queue it on a particular * disk resource, then poke the disk resource to start as much work as it can. */ int mlx_submit_buf(struct mlx_softc *sc, struct buf *bp) { int s; debug("called"); s = splbio(); bufq_insert_tail(&sc->mlx_bufq, bp); sc->mlx_waitbufs++; splx(s); mlx_startio(sc); return(0); } /******************************************************************************** * Accept an open operation on the control device. */ int mlx_open(dev_t dev, int flags, int fmt, struct proc *p) { int unit = minor(dev); struct mlx_softc *sc = devclass_get_softc(mlx_devclass, unit); sc->mlx_state |= MLX_STATE_OPEN; return(0); } /******************************************************************************** * Accept the last close on the control device. */ int mlx_close(dev_t dev, int flags, int fmt, struct proc *p) { int unit = minor(dev); struct mlx_softc *sc = devclass_get_softc(mlx_devclass, unit); sc->mlx_state &= ~MLX_STATE_OPEN; return (0); } /******************************************************************************** * Handle controller-specific control operations. */ int mlx_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p) { int unit = minor(dev); struct mlx_softc *sc = devclass_get_softc(mlx_devclass, unit); int *arg = (int *)addr; struct mlx_pause *mp; struct mlx_sysdrive *dr; struct mlxd_softc *mlxd; int i, error; switch(cmd) { /* * Enumerate connected system drives; returns the first system drive's * unit number if *arg is -1, or the next unit after *arg if it's * a valid unit on this controller. */ case MLX_NEXT_CHILD: /* search system drives */ for (i = 0; i < MLX_MAXDRIVES; i++) { /* is this one attached? */ if (sc->mlx_sysdrive[i].ms_disk != 0) { /* looking for the next one we come across? */ if (*arg == -1) { *arg = device_get_unit(sc->mlx_sysdrive[i].ms_disk); return(0); } /* we want the one after this one */ if (*arg == device_get_unit(sc->mlx_sysdrive[i].ms_disk)) *arg = -1; } } return(ENOENT); /* * Scan the controller to see whether new drives have appeared. */ case MLX_RESCAN_DRIVES: mlx_startup(sc); return(0); /* * Disconnect from the specified drive; it may be about to go * away. */ case MLX_DETACH_DRIVE: /* detach one drive */ if (((dr = mlx_findunit(sc, *arg)) == NULL) || ((mlxd = device_get_softc(dr->ms_disk)) == NULL)) return(ENOENT); device_printf(dr->ms_disk, "detaching..."); error = 0; if (mlxd->mlxd_flags & MLXD_OPEN) { error = EBUSY; goto detach_out; } /* flush controller */ if (mlx_flush(sc)) { error = EBUSY; goto detach_out; } /* nuke drive */ if ((error = device_delete_child(sc->mlx_dev, dr->ms_disk)) != 0) goto detach_out; dr->ms_disk = 0; bus_generic_detach(sc->mlx_dev); detach_out: if (error) { printf("failed\n"); } else { printf("done\n"); } return(error); /* * Pause one or more SCSI channels for a period of time, to assist * in the process of hot-swapping devices. * * Note that at least the 3.51 firmware on the DAC960PL doesn't seem * to do this right. */ case MLX_PAUSE_CHANNEL: /* schedule a channel pause */ /* Does this command work on this firmware? */ if (!(sc->mlx_feature & MLX_FEAT_PAUSEWORKS)) return(EOPNOTSUPP); mp = (struct mlx_pause *)addr; if ((mp->mp_which == MLX_PAUSE_CANCEL) && (sc->mlx_pause.mp_when != 0)) { /* cancel a pending pause operation */ sc->mlx_pause.mp_which = 0; } else { /* fix for legal channels */ mp->mp_which &= ((1 << sc->mlx_nchan) -1); /* check time values */ if ((mp->mp_when < 0) || (mp->mp_when > 3600)) return(EINVAL); if ((mp->mp_howlong < 1) || (mp->mp_howlong > (0xf * 30))) return(EINVAL); /* check for a pause currently running */ if ((sc->mlx_pause.mp_which != 0) && (sc->mlx_pause.mp_when == 0)) return(EBUSY); /* looks ok, go with it */ sc->mlx_pause.mp_which = mp->mp_which; sc->mlx_pause.mp_when = time_second + mp->mp_when; sc->mlx_pause.mp_howlong = sc->mlx_pause.mp_when + mp->mp_howlong; } return(0); /* * Accept a command passthrough-style. */ case MLX_COMMAND: return(mlx_user_command(sc, (struct mlx_usercommand *)addr)); default: return(ENOTTY); } } /******************************************************************************** * Handle operations requested by a System Drive connected to this controller. */ int mlx_submit_ioctl(struct mlx_softc *sc, struct mlx_sysdrive *drive, u_long cmd, caddr_t addr, int32_t flag, struct proc *p) { struct mlxd_rebuild *mr = (struct mlxd_rebuild *)addr; struct mlxd_rebuild_status *mp = (struct mlxd_rebuild_status *)addr; int *arg = (int *)addr; int error; switch(cmd) { /* * Return the current status of this drive. */ case MLXD_STATUS: *arg = drive->ms_state; return(0); /* * Start a background rebuild on this drive. */ case MLXD_REBUILDASYNC: /* XXX lock? */ if (sc->mlx_rebuild >= 0) return(EBUSY); sc->mlx_rebuild = drive - &sc->mlx_sysdrive[0]; switch (mlx_rebuild(sc, mr->rb_channel, mr->rb_target)) { case 0: drive->ms_state = MLX_SYSD_REBUILD; error = 0; break; case 0x10000: error = ENOMEM; /* couldn't set up the command */ break; case 0x0002: case 0x0106: error = EBUSY; break; case 0x0004: error = EIO; break; case 0x0105: error = ERANGE; break; default: error = EINVAL; break; } if (error != 0) sc->mlx_rebuild = -1; return(error); /* * Start a background consistency check on this drive. */ case MLXD_CHECKASYNC: /* start a background consistency check */ /* XXX implement */ break; /* * Get the status of the current rebuild or consistency check. */ case MLXD_REBUILDSTAT: if (sc->mlx_rebuild >= 0) { /* may be a second or so out of date */ mp->rs_drive = sc->mlx_rebuild; mp->rs_size = sc->mlx_sysdrive[sc->mlx_rebuild].ms_size; mp->rs_remaining = sc->mlx_rebuildstat; return(0); } else if (sc->mlx_check >= 0) { /* XXX implement */ } else { /* XXX should return status of last completed operation? */ return(EINVAL); } } return(ENOIOCTL); } /******************************************************************************** ******************************************************************************** Status Monitoring ******************************************************************************** ********************************************************************************/ #define MLX_PERIODIC_ISBUSY(sc) (sc->mlx_polling <= 0) #define MLX_PERIODIC_BUSY(sc) atomic_add_int(&sc->mlx_polling, 1); #define MLX_PERIODIC_UNBUSY(sc) atomic_subtract_int(&sc->mlx_polling, 1); /******************************************************************************** * Fire off commands to periodically check the status of connected drives. */ static void mlx_periodic(void *data) { struct mlx_softc *sc = (struct mlx_softc *)data; debug("called"); /* * Run a bus pause? */ if ((sc->mlx_pause.mp_which != 0) && (sc->mlx_pause.mp_when > 0) && (time_second >= sc->mlx_pause.mp_when)){ mlx_pause_action(sc); /* pause is running */ sc->mlx_pause.mp_when = 0; sysbeep(500, hz); /* * Bus pause still running? */ } else if ((sc->mlx_pause.mp_which != 0) && (sc->mlx_pause.mp_when == 0)) { /* time to stop bus pause? */ if (time_second >= sc->mlx_pause.mp_howlong) { mlx_pause_action(sc); sc->mlx_pause.mp_which = 0; /* pause is complete */ sysbeep(500, hz); } else { sysbeep((time_second % 5) * 100 + 500, hz/8); } /* * Run normal periodic activities? */ } else if (MLX_PERIODIC_ISBUSY(sc)) { /* time to perform a periodic status poll? XXX tuneable interval? */ if (time_second > (sc->mlx_lastpoll + 10)) { sc->mlx_lastpoll = time_second; /* for caution's sake */ if (sc->mlx_polling < 0) { device_printf(sc->mlx_dev, "mlx_polling < 0\n"); atomic_set_int(&sc->mlx_polling, 0); } /* * Check controller status. */ MLX_PERIODIC_BUSY(sc); mlx_enquire(sc, MLX_CMD_ENQUIRY, sizeof(struct mlx_enquiry), mlx_periodic_enquiry); /* * Check system drive status. * * XXX This might be better left to event-driven detection, eg. I/O to an offline * drive will detect it's offline, rebuilds etc. should detect the drive is back * online. */ MLX_PERIODIC_BUSY(sc); mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(struct mlx_enq_sys_drive) * MLX_MAXDRIVES, mlx_periodic_enquiry); } /* * Get drive rebuild/check status */ if (sc->mlx_rebuild >= 0) { MLX_PERIODIC_BUSY(sc); mlx_enquire(sc, MLX_CMD_REBUILDSTAT, sizeof(struct mlx_rebuild_stat), mlx_periodic_rebuild); } } else { /* * If things are still running from the last poll, complain about it. * * XXX If this becomes an issue, we should have some way of telling what * has become stuck. */ device_printf(sc->mlx_dev, "poll still busy (%d)\n", sc->mlx_polling); } /* XXX check for wedged/timed out commands? */ /* reschedule another poll next second or so */ sc->mlx_timeout = timeout(mlx_periodic, sc, hz); } /******************************************************************************** * Handle the result of an ENQUIRY command instigated by periodic status polling. */ static void mlx_periodic_enquiry(struct mlx_command *mc) { struct mlx_softc *sc = mc->mc_sc; debug("called"); /* Command completed OK? */ if (mc->mc_status != 0) { device_printf(sc->mlx_dev, "periodic enquiry failed\n"); goto out; } /* respond to command */ switch(mc->mc_mailbox[0]) { /* * Generic controller status update. We could do more with this than just * checking the event log. */ case MLX_CMD_ENQUIRY: { struct mlx_enquiry *me = (struct mlx_enquiry *)mc->mc_data; /* New stuff in the event log? */ if (me->me_event_log_seq_num != sc->mlx_lastevent) { /* record where current events are up to */ sc->mlx_currevent = me->me_event_log_seq_num; device_printf(sc->mlx_dev, "event log pointer was %d, now %d\n", sc->mlx_lastevent, sc->mlx_currevent); /* start poll of event log */ mlx_periodic_eventlog_poll(sc); } break; } case MLX_CMD_ENQSYSDRIVE: { struct mlx_enq_sys_drive *mes = (struct mlx_enq_sys_drive *)mc->mc_data; struct mlx_sysdrive *dr; int i; for (i = 0, dr = &sc->mlx_sysdrive[0]; (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff); i++) { /* if disk is being rebuilt, we should not check it */ if (dr->ms_state == MLX_SYSD_REBUILD) { /* has state been changed by controller? */ if (dr->ms_state != mes[i].sd_state) { switch(mes[i].sd_state) { case MLX_SYSD_OFFLINE: device_printf(dr->ms_disk, "drive offline\n"); break; case MLX_SYSD_ONLINE: device_printf(dr->ms_disk, "drive online\n"); break; case MLX_SYSD_CRITICAL: device_printf(dr->ms_disk, "drive critical\n"); break; } /* save new state */ dr->ms_state = mes[i].sd_state; } } } break; } default: break; } out: free(mc->mc_data, M_DEVBUF); mlx_releasecmd(mc); /* this event is done */ MLX_PERIODIC_UNBUSY(sc); } /******************************************************************************** * Instigate a poll for one event log message on (sc). * We only poll for one message at a time, to keep our command usage down. */ static void mlx_periodic_eventlog_poll(struct mlx_softc *sc) { struct mlx_command *mc; void *result = NULL; int error; debug("called"); /* presume we are going to create another event */ MLX_PERIODIC_BUSY(sc); /* get ourselves a command buffer */ error = 1; if ((mc = mlx_alloccmd(sc)) == NULL) goto out; /* allocate the response structure */ if ((result = malloc(/*sizeof(struct mlx_eventlog_entry)*/1024, M_DEVBUF, M_NOWAIT)) == NULL) goto out; /* get a command slot */ if (mlx_getslot(mc)) goto out; /* map the command so the controller can see it */ mc->mc_data = result; mc->mc_length = /*sizeof(struct mlx_eventlog_entry)*/1024; mlx_mapcmd(mc); /* build the command to get one entry */ mlx_make_type3(mc, MLX_CMD_LOGOP, MLX_LOGOP_GET, 1, sc->mlx_lastevent, 0, 0, mc->mc_dataphys, 0); mc->mc_complete = mlx_periodic_eventlog_respond; mc->mc_private = mc; /* start the command */ if ((error = mlx_start(mc)) != 0) goto out; error = 0; /* success */ out: if (error != 0) { if (mc != NULL) mlx_releasecmd(mc); if (result != NULL) free(result, M_DEVBUF); /* abort this event */ MLX_PERIODIC_UNBUSY(sc); } } /******************************************************************************** * Handle the result of polling for a log message, generate diagnostic output. * If this wasn't the last message waiting for us, we'll go collect another. */ static char *mlx_sense_messages[] = { "because write recovery failed", "because of SCSI bus reset failure", "because of double check condition", "because it was removed", "because of gross error on SCSI chip", "because of bad tag returned from drive", "because of timeout on SCSI command", "because of reset SCSI command issued from system", "because busy or parity error count exceeded limit", "because of 'kill drive' command from system", "because of selection timeout", "due to SCSI phase sequence error", "due to unknown status" }; static void mlx_periodic_eventlog_respond(struct mlx_command *mc) { struct mlx_softc *sc = mc->mc_sc; struct mlx_eventlog_entry *el = (struct mlx_eventlog_entry *)mc->mc_data; char *reason; debug("called"); if (mc->mc_status == 0) { sc->mlx_lastevent++; /* got the message OK */ /* handle event log message */ switch(el->el_type) { /* * This is the only sort of message we understand at the moment. * The tests here are probably incomplete. */ case MLX_LOGMSG_SENSE: /* sense data */ /* Mylex vendor-specific message indicating a drive was killed? */ if ((el->el_sensekey == 9) && (el->el_asc == 0x80)) { if (el->el_asq < (sizeof(mlx_sense_messages) / sizeof(mlx_sense_messages[0]))) { reason = mlx_sense_messages[el->el_asq]; } else { reason = "for unknown reason"; } device_printf(sc->mlx_dev, "physical drive %d:%d killed %s\n", el->el_channel, el->el_target, reason); } /* SCSI drive was reset? */ if ((el->el_sensekey == 6) && (el->el_asc == 0x29)) { device_printf(sc->mlx_dev, "physical drive %d:%d reset\n", el->el_channel, el->el_target); } /* SCSI drive error? */ if (!((el->el_sensekey == 0) || ((el->el_sensekey == 2) && (el->el_asc == 0x04) && ((el->el_asq == 0x01) || (el->el_asq == 0x02))))) { device_printf(sc->mlx_dev, "physical drive %d:%d error log: sense = %d asc = %x asq = %x\n", el->el_channel, el->el_target, el->el_sensekey, el->el_asc, el->el_asq); device_printf(sc->mlx_dev, " info %4D csi %4D\n", el->el_information, ":", el->el_csi, ":"); } break; default: device_printf(sc->mlx_dev, "unknown log message type 0x%x\n", el->el_type); break; } } else { device_printf(sc->mlx_dev, "error reading message log - %s\n", mlx_diagnose_command(mc)); } /* dispose of command and data */ free(mc->mc_data, M_DEVBUF); mlx_releasecmd(mc); /* is there another message to obtain? */ if (sc->mlx_lastevent != sc->mlx_currevent) mlx_periodic_eventlog_poll(sc); /* this event is done */ MLX_PERIODIC_UNBUSY(sc); } /******************************************************************************** * Handle the completion of a rebuild operation. */ static void mlx_periodic_rebuild(struct mlx_command *mc) { struct mlx_softc *sc = mc->mc_sc; struct mlx_rebuild_stat *mr = (struct mlx_rebuild_stat *)mc->mc_private; switch(mc->mc_status) { case 0: /* all OK, rebuild still running */ sc->mlx_rebuildstat = mr->rb_remaining; break; case 0x0105: /* rebuild/check finished */ if (sc->mlx_rebuild >= 0) { device_printf(sc->mlx_sysdrive[sc->mlx_rebuild].ms_disk, "rebuild completed\n"); sc->mlx_rebuild = -1; } else if (sc->mlx_check >= 0) { device_printf(sc->mlx_sysdrive[sc->mlx_check].ms_disk, "consistency check completed\n"); sc->mlx_check = -1; } else { device_printf(sc->mlx_dev, "consistency check completed\n"); } break; } free(mc->mc_data, M_DEVBUF); mlx_releasecmd(mc); /* this event is done */ MLX_PERIODIC_UNBUSY(sc); } /******************************************************************************** ******************************************************************************** Channel Pause ******************************************************************************** ********************************************************************************/ /******************************************************************************** * It's time to perform a channel pause action for (sc), either start or stop * the pause. */ static void mlx_pause_action(struct mlx_softc *sc) { struct mlx_command *mc; int failsafe, i, command; /* What are we doing here? */ if (sc->mlx_pause.mp_when == 0) { command = MLX_CMD_STARTCHANNEL; failsafe = 0; } else { command = MLX_CMD_STOPCHANNEL; /* * Channels will always start again after the failsafe period, * which is specified in multiples of 30 seconds. * This constrains us to a maximum pause of 450 seconds. */ failsafe = ((sc->mlx_pause.mp_howlong - time_second) + 5) / 30; if (failsafe > 0xf) { failsafe = 0xf; sc->mlx_pause.mp_howlong = time_second + (0xf * 30) - 5; } } /* build commands for every channel requested */ for (i = 0; i < sc->mlx_nchan; i++) { if ((1 << i) & sc->mlx_pause.mp_which) { /* get ourselves a command buffer */ if ((mc = mlx_alloccmd(sc)) == NULL) goto fail; /* get a command slot */ mc->mc_flags |= MLX_CMD_PRIORITY; if (mlx_getslot(mc)) goto fail; /* build the command */ mlx_make_type2(mc, command, (failsafe << 4) | i, 0, 0, 0, 0, 0, 0, 0); mc->mc_complete = mlx_pause_done; mc->mc_private = sc; /* XXX not needed */ if (mlx_start(mc)) goto fail; /* command submitted OK */ return; fail: device_printf(sc->mlx_dev, "%s failed for channel %d\n", command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", i); if (mc != NULL) mlx_releasecmd(mc); } } } static void mlx_pause_done(struct mlx_command *mc) { struct mlx_softc *sc = mc->mc_sc; int command = mc->mc_mailbox[0]; int channel = mc->mc_mailbox[2] & 0xf; if (mc->mc_status != 0) { device_printf(sc->mlx_dev, "%s command failed - %s\n", command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", mlx_diagnose_command(mc)); } else if (command == MLX_CMD_STOPCHANNEL) { device_printf(sc->mlx_dev, "channel %d pausing for %ld seconds\n", channel, sc->mlx_pause.mp_howlong - time_second); } else { device_printf(sc->mlx_dev, "channel %d resuming\n", channel); } mlx_releasecmd(mc); } /******************************************************************************** ******************************************************************************** Command Submission ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Perform an Enquiry command using a type-3 command buffer and a return a single * linear result buffer. If the completion function is specified, it will * be called with the completed command (and the result response will not be * valid until that point). Otherwise, the command will either be busy-waited * for (interrupts not enabled), or slept for. */ static void * mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, void (* complete)(struct mlx_command *mc)) { struct mlx_command *mc; void *result; int error; debug("called"); /* get ourselves a command buffer */ error = 1; result = NULL; if ((mc = mlx_alloccmd(sc)) == NULL) goto out; /* allocate the response structure */ if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL) goto out; /* get a command slot */ mc->mc_flags |= MLX_CMD_PRIORITY | MLX_CMD_DATAOUT; if (mlx_getslot(mc)) goto out; /* map the command so the controller can see it */ mc->mc_data = result; mc->mc_length = bufsize; mlx_mapcmd(mc); /* build an enquiry command */ mlx_make_type2(mc, command, 0, 0, 0, 0, 0, 0, mc->mc_dataphys, 0); /* do we want a completion callback? */ if (complete != NULL) { mc->mc_complete = complete; mc->mc_private = mc; if ((error = mlx_start(mc)) != 0) goto out; } else { /* run the command in either polled or wait mode */ if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) : mlx_poll_command(mc)) goto out; /* command completed OK? */ if (mc->mc_status != 0) { device_printf(sc->mlx_dev, "ENQUIRY failed - %s\n", mlx_diagnose_command(mc)); goto out; } } error = 0; /* success */ out: /* we got a command, but nobody else will free it */ if ((complete == NULL) && (mc != NULL)) mlx_releasecmd(mc); /* we got an error, and we allocated a result */ if ((error != 0) && (result != NULL)) { free(result, M_DEVBUF); result = NULL; } return(result); } /******************************************************************************** * Perform a Flush command on the nominated controller. * * May be called with interrupts enabled or disabled; will not return until * the flush operation completes or fails. */ static int mlx_flush(struct mlx_softc *sc) { struct mlx_command *mc; int error; debug("called"); /* get ourselves a command buffer */ error = 1; if ((mc = mlx_alloccmd(sc)) == NULL) goto out; /* get a command slot */ if (mlx_getslot(mc)) goto out; /* build a flush command */ mlx_make_type2(mc, MLX_CMD_FLUSH, 0, 0, 0, 0, 0, 0, 0, 0); /* run the command in either polled or wait mode */ if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) : mlx_poll_command(mc)) goto out; /* command completed OK? */ if (mc->mc_status != 0) { device_printf(sc->mlx_dev, "FLUSH failed - %s\n", mlx_diagnose_command(mc)); goto out; } error = 0; /* success */ out: if (mc != NULL) mlx_releasecmd(mc); return(error); } /******************************************************************************** * Start a background rebuild on the nominated controller/channel/target. * * May be called with interrupts enabled or disabled; will return as soon as the * operation has started or been refused. */ static int mlx_rebuild(struct mlx_softc *sc, int channel, int target) { struct mlx_command *mc; int error; debug("called"); /* get ourselves a command buffer */ error = 0x10000; if ((mc = mlx_alloccmd(sc)) == NULL) goto out; /* get a command slot */ if (mlx_getslot(mc)) goto out; /* build a rebuild command */ mlx_make_type2(mc, MLX_CMD_REBUILDASYNC, channel, target, 0, 0, 0, 0, 0, 0); /* run the command in either polled or wait mode */ if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) : mlx_poll_command(mc)) goto out; /* command completed OK? */ if (mc->mc_status != 0) { device_printf(sc->mlx_dev, "REBUILD ASYNC failed - %s\n", mlx_diagnose_command(mc)); } else { device_printf(sc->mlx_sysdrive[sc->mlx_rebuild].ms_disk, "rebuild started"); } error = mc->mc_status; out: if (mc != NULL) mlx_releasecmd(mc); return(error); } /******************************************************************************** * Run the command (mc) and return when it completes. * * Interrupts need to be enabled; returns nonzero on error. */ static int mlx_wait_command(struct mlx_command *mc) { struct mlx_softc *sc = mc->mc_sc; int error, count; debug("called"); mc->mc_complete = NULL; mc->mc_private = mc; /* wake us when you're done */ if ((error = mlx_start(mc)) != 0) return(error); count = 0; /* XXX better timeout? */ while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 30)) { tsleep(mc->mc_private, PRIBIO | PCATCH, "mlxwcmd", hz); } if (mc->mc_status != 0) { device_printf(sc->mlx_dev, "I/O error 0x%x\n", mc->mc_status); return(EIO); } return(0); } /******************************************************************************** * Start the command (mc) and busy-wait for it to complete. * * Should only be used when interrupts are not available. Returns 0 on * success, nonzero on error. * Successfully completed commands are dequeued. */ static int mlx_poll_command(struct mlx_command *mc) { struct mlx_softc *sc = mc->mc_sc; int error, count, s; debug("called"); mc->mc_complete = NULL; mc->mc_private = NULL; /* we will poll for it */ if ((error = mlx_start(mc)) != 0) return(error); count = 0; do { /* poll for completion */ mlx_done(mc->mc_sc); } while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 10000)); if (mc->mc_status != MLX_STATUS_BUSY) { s = splbio(); TAILQ_REMOVE(&sc->mlx_work, mc, mc_link); splx(s); return(0); } device_printf(sc->mlx_dev, "I/O error 0x%x\n", mc->mc_status); return(EIO); } /******************************************************************************** * Pull as much work off the softc's work queue as possible and give it to the * controller. Leave a couple of slots free for emergencies. * * Must be called at splbio or in an equivalent fashion that prevents * reentry or activity on the bufq.. */ static void mlx_startio(struct mlx_softc *sc) { struct mlx_command *mc; struct mlxd_softc *mlxd; struct buf *bp; int blkcount; int driveno; int cmd; int s; /* spin until something prevents us from doing any work */ s = splbio(); for (;;) { /* see if there's work to be done */ if ((bp = bufq_first(&sc->mlx_bufq)) == NULL) break; /* get a command */ if ((mc = mlx_alloccmd(sc)) == NULL) break; /* get a slot for the command */ if (mlx_getslot(mc) != 0) { mlx_releasecmd(mc); break; } /* get the buf containing our work */ bufq_remove(&sc->mlx_bufq, bp); sc->mlx_waitbufs--; splx(s); /* connect the buf to the command */ mc->mc_complete = mlx_completeio; mc->mc_private = bp; mc->mc_data = bp->b_data; mc->mc_length = bp->b_bcount; if (bp->b_flags & B_READ) { mc->mc_flags |= MLX_CMD_DATAIN; cmd = MLX_CMD_READOLDSG; } else { mc->mc_flags |= MLX_CMD_DATAOUT; cmd = MLX_CMD_WRITEOLDSG; } /* map the command so the controller can work with it */ mlx_mapcmd(mc); /* build a suitable I/O command (assumes 512-byte rounded transfers) */ mlxd = (struct mlxd_softc *)bp->b_dev->si_drv1; driveno = mlxd->mlxd_drive - &sc->mlx_sysdrive[0]; blkcount = (bp->b_bcount + MLX_BLKSIZE - 1) / MLX_BLKSIZE; if ((bp->b_pblkno + blkcount) > sc->mlx_sysdrive[driveno].ms_size) device_printf(sc->mlx_dev, "I/O beyond end of unit (%u,%d > %u)\n", bp->b_pblkno, blkcount, sc->mlx_sysdrive[driveno].ms_size); /* * Build the I/O command. Note that the SG list type bits are set to zero, * denoting the format of SG list that we are using. */ mlx_make_type5(mc, cmd, blkcount & 0xff, /* xfer length low byte */ (driveno << 3) | ((blkcount >> 8) & 0x07), /* target and length high 3 bits */ bp->b_pblkno, /* physical block number */ mc->mc_sgphys, /* location of SG list */ mc->mc_nsgent & 0x3f); /* size of SG list (top 2 bits clear) */ /* try to give command to controller */ if (mlx_start(mc) != 0) { /* fail the command */ mc->mc_status = MLX_STATUS_WEDGED; mlx_completeio(mc); } s = splbio(); } splx(s); } /******************************************************************************** * Handle completion of an I/O command. */ static void mlx_completeio(struct mlx_command *mc) { struct mlx_softc *sc = mc->mc_sc; struct buf *bp = (struct buf *)mc->mc_private; struct mlxd_softc *mlxd = (struct mlxd_softc *)bp->b_dev->si_drv1; if (mc->mc_status != MLX_STATUS_OK) { /* could be more verbose here? */ bp->b_error = EIO; bp->b_flags |= B_ERROR; switch(mc->mc_status) { case MLX_STATUS_RDWROFFLINE: /* system drive has gone offline */ device_printf(mlxd->mlxd_dev, "drive offline\n"); /* should signal this with a return code */ mlxd->mlxd_drive->ms_state = MLX_SYSD_OFFLINE; break; default: /* other I/O error */ device_printf(sc->mlx_dev, "I/O error - %s\n", mlx_diagnose_command(mc)); #if 0 device_printf(sc->mlx_dev, " b_bcount %ld blkcount %ld b_pblkno %d\n", bp->b_bcount, bp->b_bcount / MLX_BLKSIZE, bp->b_pblkno); device_printf(sc->mlx_dev, " %13D\n", mc->mc_mailbox, " "); #endif break; } } mlx_releasecmd(mc); mlxd_intr(bp); } /******************************************************************************** * Take a command from user-space and try to run it. */ static int mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu) { struct mlx_command *mc; void *kbuf; int error; kbuf = NULL; mc = NULL; error = ENOMEM; /* get a kernel buffer for the transfer */ if (mu->mu_datasize > 0) { if ((kbuf = malloc(mu->mu_datasize, M_DEVBUF, M_WAITOK)) == NULL) goto out; if ((mu->mu_bufptr < 0) || (mu->mu_bufptr > (sizeof(mu->mu_command) < sizeof(u_int32_t)))) { error = EINVAL; goto out; } } /* get ourselves a command buffer */ if ((mc = mlx_alloccmd(sc)) == NULL) goto out; /* copy the command and data */ bcopy(mu->mu_command, mc->mc_mailbox, sizeof(mc->mc_mailbox)); if ((mu->mu_datasize > 0) && ((error = copyin(mu->mu_buf, kbuf, mu->mu_datasize)))) goto out; /* get a command slot */ if (mlx_getslot(mc)) goto out; /* map the command so the controller can see it */ mc->mc_data = kbuf; mc->mc_length = mu->mu_datasize; mlx_mapcmd(mc); /* if there's a data buffer, fix up the command */ if (mu->mu_datasize > 0) { mc->mc_mailbox[mu->mu_bufptr ] = mc->mc_length & 0xff; mc->mc_mailbox[mu->mu_bufptr + 1] = (mc->mc_length >> 8) & 0xff; mc->mc_mailbox[mu->mu_bufptr + 2] = (mc->mc_length >> 16) & 0xff; mc->mc_mailbox[mu->mu_bufptr + 3] = (mc->mc_length >> 24) & 0xff; } /* submit the command and wait */ if ((error = mlx_wait_command(mc)) != 0) goto out; /* copy out status and data */ mu->mu_status = mc->mc_status; if ((mu->mu_datasize > 0) && ((error = copyout(kbuf, mu->mu_buf, mu->mu_datasize)))) goto out; error = 0; out: mlx_releasecmd(mc); if (kbuf != NULL) free(kbuf, M_DEVBUF); return(error); } /******************************************************************************** ******************************************************************************** Command I/O to Controller ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Find a free command slot for (mc). * * Don't hand out a slot to a normal-priority command unless there are at least * 4 slots free for priority commands. */ static int mlx_getslot(struct mlx_command *mc) { struct mlx_softc *sc = mc->mc_sc; int s, slot, limit; debug("called mc %p sc %p", mc, sc); /* enforce slot-usage limit */ limit = (mc->mc_flags & MLX_CMD_PRIORITY) ? sc->mlx_maxiop : sc->mlx_maxiop - 4; if (sc->mlx_busycmds > limit) return(EBUSY); /* * Allocate an outstanding command slot * * XXX linear search is slow */ s = splbio(); for (slot = 0; slot < sc->mlx_maxiop; slot++) { debug("try slot %d", slot); if (sc->mlx_busycmd[slot] == NULL) break; } if (slot < sc->mlx_maxiop) { sc->mlx_busycmd[slot] = mc; sc->mlx_busycmds++; } splx(s); /* out of slots? */ if (slot >= sc->mlx_maxiop) return(EBUSY); debug("got slot %d", slot); mc->mc_slot = slot; return(0); } /******************************************************************************** * Map/unmap (mc)'s data in the controller's addressable space. */ static void mlx_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) { struct mlx_command *mc = (struct mlx_command *)arg; struct mlx_softc *sc = mc->mc_sc; struct mlx_sgentry *sg; int i; debug("called"); /* get base address of s/g table */ sg = sc->mlx_sgtable + (mc->mc_slot * MLX_NSEG); /* save s/g table information in command */ mc->mc_nsgent = nsegments; mc->mc_sgphys = sc->mlx_sgbusaddr + (mc->mc_slot * MLX_NSEG * sizeof(struct mlx_sgentry)); mc->mc_dataphys = segs[0].ds_addr; /* populate s/g table */ for (i = 0; i < nsegments; i++, sg++) { sg->sg_addr = segs[i].ds_addr; sg->sg_count = segs[i].ds_len; } } static void mlx_mapcmd(struct mlx_command *mc) { struct mlx_softc *sc = mc->mc_sc; debug("called"); /* if the command involves data at all */ if (mc->mc_data != NULL) { /* map the data buffer into bus space and build the s/g list */ bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data, mc->mc_length, mlx_setup_dmamap, mc, 0); if (mc->mc_flags & MLX_CMD_DATAIN) bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_PREREAD); if (mc->mc_flags & MLX_CMD_DATAOUT) bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_PREWRITE); } } static void mlx_unmapcmd(struct mlx_command *mc) { struct mlx_softc *sc = mc->mc_sc; debug("called"); /* if the command involved data at all */ if (mc->mc_data != NULL) { if (mc->mc_flags & MLX_CMD_DATAIN) bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTREAD); if (mc->mc_flags & MLX_CMD_DATAOUT) bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->mlx_buffer_dmat, mc->mc_dmamap); } } /******************************************************************************** * Try to deliver (mc) to the controller. Take care of any completed commands * that we encounter while doing so. * * Can be called at any interrupt level, with or without interrupts enabled. */ static int mlx_start(struct mlx_command *mc) { struct mlx_softc *sc = mc->mc_sc; int i, s, done, worked; debug("called"); /* save the slot number as ident so we can handle this command when complete */ mc->mc_mailbox[0x1] = mc->mc_slot; /* mark the command as currently being processed */ mc->mc_status = MLX_STATUS_BUSY; /* assume we don't collect any completed commands */ worked = 0; /* spin waiting for the mailbox */ for (i = 100000, done = 0; (i > 0) && !done; i--) { s = splbio(); if (sc->mlx_tryqueue(sc, mc)) { done = 1; /* move command to work queue */ TAILQ_INSERT_TAIL(&sc->mlx_work, mc, mc_link); } splx(s); /* check for command completion while we're at it */ if (mlx_done(sc)) worked = 1; } /* check to see if we picked up any completed commands */ if (worked) mlx_complete(sc); /* command is enqueued */ if (done) return(0); /* * We couldn't get the controller to take the command. Revoke the slot * that the command was given and return it with a bad status. */ sc->mlx_busycmd[mc->mc_slot] = NULL; device_printf(sc->mlx_dev, "controller wedged (not taking commands)\n"); mc->mc_status = MLX_STATUS_WEDGED; return(EIO); } /******************************************************************************** * Look at the controller (sc) and see if a command has been completed. * If so, move the command buffer to the done queue for later collection * and free the slot for immediate reuse. * * Returns nonzero if anything was added to the done queue. */ static int mlx_done(struct mlx_softc *sc) { struct mlx_command *mc; int s; u_int8_t slot; u_int16_t status; debug("called"); mc = NULL; slot = 0; /* poll for a completed command's identifier and status */ s = splbio(); if (sc->mlx_findcomplete(sc, &slot, &status)) { mc = sc->mlx_busycmd[slot]; /* find command */ if (mc != NULL) { /* paranoia */ if (mc->mc_status == MLX_STATUS_BUSY) { mc->mc_status = status; /* save status */ /* free slot for reuse */ sc->mlx_busycmd[slot] = NULL; sc->mlx_busycmds--; } else { device_printf(sc->mlx_dev, "duplicate done event for slot %d\n", slot); mc = NULL; } } else { device_printf(sc->mlx_dev, "done event for nonbusy slot %d\n", slot); } } splx(s); if (mc != NULL) { /* unmap the command's data buffer */ mlx_unmapcmd(mc); return(1); } return(0); } /******************************************************************************** * Handle completion for all commands on (sc)'s done queue. */ static void mlx_complete(struct mlx_softc *sc) { struct mlx_command *mc, *nc; int s, count; debug("called"); s = splbio(); count = 0; /* scan the list of done commands */ mc = TAILQ_FIRST(&sc->mlx_work); while (mc != NULL) { nc = TAILQ_NEXT(mc, mc_link); /* XXX this is slightly bogus */ if (count++ > (sc->mlx_maxiop * 2)) panic("mlx_work list corrupt!"); /* Skip commands that are still busy */ if (mc->mc_status != MLX_STATUS_BUSY) { /* * Does the command have a completion handler? */ if (mc->mc_complete != NULL) { /* remove from list and give to handler */ TAILQ_REMOVE(&sc->mlx_work, mc, mc_link); mc->mc_complete(mc); /* * Is there a sleeper waiting on this command? */ } else if (mc->mc_private != NULL) { /* sleeping caller wants to know about it */ /* remove from list and wake up sleeper */ TAILQ_REMOVE(&sc->mlx_work, mc, mc_link); wakeup_one(mc->mc_private); /* * Leave the command for a caller that's polling for it. */ } else { } } mc = nc; } splx(s); /* queue some more work if there is any */ mlx_startio(sc); } /******************************************************************************** ******************************************************************************** Command Buffer Management ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Get a new command buffer. * * This may return NULL in low-memory cases. * * Note that using malloc() is expensive (the command buffer is << 1 page) but * necessary if we are to be a loadable module before the zone allocator is fixed. * * If possible, we recycle a command buffer that's been used before. * * XXX Note that command buffers are not cleaned out - it is the caller's * responsibility to ensure that all required fields are filled in before * using a buffer. */ static struct mlx_command * mlx_alloccmd(struct mlx_softc *sc) { struct mlx_command *mc; int error; int s; debug("called"); s = splbio(); if ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link); splx(s); /* allocate a new command buffer? */ if (mc == NULL) { mc = (struct mlx_command *)malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT); if (mc != NULL) { bzero(mc, sizeof(*mc)); mc->mc_sc = sc; error = bus_dmamap_create(sc->mlx_buffer_dmat, 0, &mc->mc_dmamap); if (error) { free(mc, M_DEVBUF); return(NULL); } } } return(mc); } /******************************************************************************** * Release a command buffer for recycling. * * XXX It might be a good idea to limit the number of commands we save for reuse * if it's shown that this list bloats out massively. */ static void mlx_releasecmd(struct mlx_command *mc) { int s; debug("called"); s = splbio(); TAILQ_INSERT_HEAD(&mc->mc_sc->mlx_freecmds, mc, mc_link); splx(s); } /******************************************************************************** * Permanently discard a command buffer. */ static void mlx_freecmd(struct mlx_command *mc) { struct mlx_softc *sc = mc->mc_sc; debug("called"); bus_dmamap_destroy(sc->mlx_buffer_dmat, mc->mc_dmamap); free(mc, M_DEVBUF); } /******************************************************************************** ******************************************************************************** Type 3 interface accessor methods ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Try to give (mc) to the controller. Returns 1 if successful, 0 on failure * (the controller is not ready to take a command). * * Must be called at splbio or in a fashion that prevents reentry. */ static int mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc) { int i; debug("called"); /* ready for our command? */ if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_FULL)) { /* copy mailbox data to window */ for (i = 0; i < 13; i++) MLX_V3_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]); /* post command */ MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_FULL); return(1); } return(0); } /******************************************************************************** * See if a command has been completed, if so acknowledge its completion * and recover the slot number and status code. * * Must be called at splbio or in a fashion that prevents reentry. */ static int mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status) { debug("called"); /* status available? */ if (MLX_V3_GET_ODBR(sc) & MLX_V3_ODB_SAVAIL) { *slot = MLX_V3_GET_STATUS_IDENT(sc); /* get command identifier */ *status = MLX_V3_GET_STATUS(sc); /* get status */ /* acknowledge completion */ MLX_V3_PUT_ODBR(sc, MLX_V3_ODB_SAVAIL); MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK); return(1); } return(0); } /******************************************************************************** * Enable/disable interrupts as requested. (No acknowledge required) * * Must be called at splbio or in a fashion that prevents reentry. */ static void mlx_v3_intaction(struct mlx_softc *sc, int action) { debug("called"); switch(action) { case MLX_INTACTION_DISABLE: MLX_V3_PUT_IER(sc, 0); sc->mlx_state &= ~MLX_STATE_INTEN; break; case MLX_INTACTION_ENABLE: MLX_V3_PUT_IER(sc, 1); sc->mlx_state |= MLX_STATE_INTEN; break; } } /******************************************************************************** ******************************************************************************** Type 4 interface accessor methods ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Try to give (mc) to the controller. Returns 1 if successful, 0 on failure * (the controller is not ready to take a command). * * Must be called at splbio or in a fashion that prevents reentry. */ static int mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc) { int i; debug("called"); /* ready for our command? */ if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_FULL)) { /* copy mailbox data to window */ for (i = 0; i < 13; i++) MLX_V4_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]); /* post command */ MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_HWMBOX_CMD); return(1); } return(0); } /******************************************************************************** * See if a command has been completed, if so acknowledge its completion * and recover the slot number and status code. * * Must be called at splbio or in a fashion that prevents reentry. */ static int mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status) { debug("called"); /* status available? */ if (MLX_V4_GET_ODBR(sc) & MLX_V4_ODB_HWSAVAIL) { *slot = MLX_V4_GET_STATUS_IDENT(sc); /* get command identifier */ *status = MLX_V4_GET_STATUS(sc); /* get status */ /* acknowledge completion */ MLX_V4_PUT_ODBR(sc, MLX_V4_ODB_HWMBOX_ACK); MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK); return(1); } return(0); } /******************************************************************************** * Enable/disable interrupts as requested. * * Must be called at splbio or in a fashion that prevents reentry. */ static void mlx_v4_intaction(struct mlx_softc *sc, int action) { debug("called"); switch(action) { case MLX_INTACTION_DISABLE: MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK | MLX_V4_IER_DISINT); sc->mlx_state &= ~MLX_STATE_INTEN; break; case MLX_INTACTION_ENABLE: MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK & ~MLX_V4_IER_DISINT); sc->mlx_state |= MLX_STATE_INTEN; break; } } /******************************************************************************** ******************************************************************************** Debugging ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Return a status message describing (mc) */ static char *mlx_status_messages[] = { "normal completion", /* 00 */ "irrecoverable data error", /* 01 */ "drive does not exist, or is offline", /* 02 */ "attempt to write beyond end of drive", /* 03 */ "bad data encountered", /* 04 */ "invalid log entry request", /* 05 */ "attempt to rebuild online drive", /* 06 */ "new disk failed during rebuild", /* 07 */ "invalid channel/target", /* 08 */ "rebuild/check already in progress", /* 09 */ "one or more disks are dead", /* 10 */ "invalid or non-redundant drive", /* 11 */ "channel is busy", /* 12 */ "channel is not stopped", /* 13 */ }; static struct { int command; u_int16_t status; int msg; } mlx_messages[] = { {MLX_CMD_READOLDSG, 0x0001, 1}, {MLX_CMD_READOLDSG, 0x0002, 1}, {MLX_CMD_READOLDSG, 0x0105, 3}, {MLX_CMD_READOLDSG, 0x010c, 4}, {MLX_CMD_WRITEOLDSG, 0x0001, 1}, {MLX_CMD_WRITEOLDSG, 0x0002, 1}, {MLX_CMD_WRITEOLDSG, 0x0105, 3}, {MLX_CMD_LOGOP, 0x0105, 5}, {MLX_CMD_REBUILDASYNC, 0x0002, 6}, {MLX_CMD_REBUILDASYNC, 0x0004, 7}, {MLX_CMD_REBUILDASYNC, 0x0105, 8}, {MLX_CMD_REBUILDASYNC, 0x0106, 9}, {MLX_CMD_CHECKASYNC, 0x0002, 10}, {MLX_CMD_CHECKASYNC, 0x0105, 11}, {MLX_CMD_CHECKASYNC, 0x0106, 9}, {MLX_CMD_STOPCHANNEL, 0x0106, 12}, {MLX_CMD_STOPCHANNEL, 0x0105, 8}, {MLX_CMD_STARTCHANNEL, 0x0005, 13}, {MLX_CMD_STARTCHANNEL, 0x0105, 8}, {-1, 0, 0} }; static char * mlx_diagnose_command(struct mlx_command *mc) { static char unkmsg[80]; int i; /* look up message in table */ for (i = 0; mlx_messages[i].command != -1; i++) if ((mc->mc_mailbox[0] == mlx_messages[i].command) && (mc->mc_status == mlx_messages[i].status)) return(mlx_status_messages[mlx_messages[i].msg]); sprintf(unkmsg, "unknown response 0x%x for command 0x%x", (int)mc->mc_status, (int)mc->mc_mailbox[0]); return(unkmsg); } /******************************************************************************* * Return a string describing the controller (hwid) */ static char * mlx_name_controller(u_int32_t hwid) { static char buf[80]; char smbuf[16]; char *submodel; int nchn; switch(hwid & 0xff) { case 0x01: submodel = "P/PD"; break; case 0x02: submodel = "PL"; break; case 0x10: submodel = "PG"; break; case 0x11: submodel = "PJ"; break; case 0x16: submodel = "PTL"; break; default: sprintf(smbuf, " model 0x%x", hwid & 0xff); submodel = smbuf; break; } nchn = (hwid >> 8) & 0xff; sprintf(buf, "DAC960%s, %d channel%s", submodel, nchn, nchn > 1 ? "s" : ""); return(buf); } /******************************************************************************** ******************************************************************************** Utility Functions ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Find the disk whose unit number is (unit) on this controller */ static struct mlx_sysdrive * mlx_findunit(struct mlx_softc *sc, int unit) { int i; /* search system drives */ for (i = 0; i < MLX_MAXDRIVES; i++) { /* is this one attached? */ if (sc->mlx_sysdrive[i].ms_disk != 0) { /* is this the one? */ if (unit == device_get_unit(sc->mlx_sysdrive[i].ms_disk)) return(&sc->mlx_sysdrive[i]); } } return(NULL); } Index: head/sys/dev/pccard/pccard.c =================================================================== --- head/sys/dev/pccard/pccard.c (revision 54072) +++ head/sys/dev/pccard/pccard.c (revision 54073) @@ -1,998 +1,998 @@ /* $NetBSD: pcmcia.c,v 1.13 1998/12/24 04:51:59 marc Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1997 Marc Horowitz. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Marc Horowitz. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PCCARDDEBUG int pccard_debug = 0; #define DPRINTF(arg) if (pccard_debug) printf arg int pccardintr_debug = 0; /* this is done this way to avoid doing lots of conditionals at interrupt level. */ #define PCCARD_CARD_INTR (pccardintr_debug?pccard_card_intrdebug:pccard_card_intr) #else #define DPRINTF(arg) #define PCCARD_CARD_INTR (pccard_card_intr) #endif #ifdef PCCARDVERBOSE int pccard_verbose = 1; #else int pccard_verbose = 0; #endif int pccard_print __P((void *, const char *)); static __inline void pccard_socket_enable __P((pccard_chipset_tag_t, pccard_chipset_handle_t *)); static __inline void pccard_socket_disable __P((pccard_chipset_tag_t, pccard_chipset_handle_t *)); int pccard_card_intr __P((void *)); #ifdef PCCARDDEBUG int pccard_card_intrdebug __P((void *)); #endif int pccard_ccr_read(pf, ccr) struct pccard_function *pf; int ccr; { return (bus_space_read_1(pf->pf_ccrt, pf->pf_ccrh, pf->pf_ccr_offset + ccr)); } void pccard_ccr_write(pf, ccr, val) struct pccard_function *pf; int ccr; int val; { if ((pf->ccr_mask) & (1 << (ccr / 2))) { bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh, pf->pf_ccr_offset + ccr, val); } } int pccard_card_attach(device_t dev) { struct pccard_softc *sc = (struct pccard_softc *) device_get_softc(dev); struct pccard_function *pf; struct pccard_attach_args paa; int attached; /* * this is here so that when socket_enable calls gettype, trt happens */ STAILQ_INIT(&sc->card.pf_head); pccard_chip_socket_enable(sc->pct, sc->pch); pccard_read_cis(sc); pccard_chip_socket_disable(sc->pct, sc->pch); pccard_check_cis_quirks(dev); /* * bail now if the card has no functions, or if there was an error in * the cis. */ if (sc->card.error) return (1); if (STAILQ_EMPTY(&sc->card.pf_head)) return (1); if (pccard_verbose) pccard_print_cis(dev); attached = 0; STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { if (STAILQ_EMPTY(&pf->cfe_head)) continue; #ifdef DIAGNOSTIC if (pf->child != NULL) { printf("%s: %s still attached to function %d!\n", sc->dev.dv_xname, pf->child->dv_xname, pf->number); panic("pccard_card_attach"); } #endif pf->sc = sc; pf->child = NULL; pf->cfe = NULL; pf->ih_fct = NULL; pf->ih_arg = NULL; } STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { if (STAILQ_EMPTY(&pf->cfe_head)) continue; paa.manufacturer = sc->card.manufacturer; paa.product = sc->card.product; paa.card = &sc->card; paa.pf = pf; #if XXX if (attach_child()) { attached++; DPRINTF(("%s: function %d CCR at %d " "offset %lx: %x %x %x %x, %x %x %x %x, %x\n", sc->dev.dv_xname, pf->number, pf->pf_ccr_window, pf->pf_ccr_offset, pccard_ccr_read(pf, 0x00), pccard_ccr_read(pf, 0x02), pccard_ccr_read(pf, 0x04), pccard_ccr_read(pf, 0x06), pccard_ccr_read(pf, 0x0A), pccard_ccr_read(pf, 0x0C), pccard_ccr_read(pf, 0x0E), pccard_ccr_read(pf, 0x10), pccard_ccr_read(pf, 0x12))); } #endif } return (attached ? 0 : 1); } void pccard_card_detach(device_t dev, int flags) { struct pccard_softc *sc = (struct pccard_softc *) device_get_softc(dev); struct pccard_function *pf; #if XXX int error; #endif /* * We are running on either the PCCARD socket's event thread * or in user context detaching a device by user request. */ STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { if (STAILQ_FIRST(&pf->cfe_head) == NULL) continue; if (pf->child == NULL) continue; DPRINTF(("%s: detaching %s (function %d)\n", sc->dev.dv_xname, pf->child->dv_xname, pf->number)); #if XXX if ((error = config_detach(pf->child, flags)) != 0) { printf("%s: error %d detaching %s (function %d)\n", sc->dev.dv_xname, error, pf->child->dv_xname, pf->number); } else pf->child = NULL; #endif } } void pccard_card_deactivate(device_t dev) { struct pccard_softc *sc = (struct pccard_softc *) device_get_softc(dev); struct pccard_function *pf; /* * We're in the chip's card removal interrupt handler. * Deactivate the child driver. The PCCARD socket's * event thread will run later to finish the detach. */ STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { if (STAILQ_FIRST(&pf->cfe_head) == NULL) continue; if (pf->child == NULL) continue; DPRINTF(("%s: deactivating %s (function %d)\n", sc->dev.dv_xname, pf->child->dv_xname, pf->number)); #if XXX config_deactivate(pf->child); #endif } } int pccard_card_gettype(device_t dev) { struct pccard_softc *sc = (struct pccard_softc *) device_get_softc(dev); struct pccard_function *pf; /* * set the iftype to memory if this card has no functions (not yet * probed), or only one function, and that is not initialized yet or * that is memory. */ pf = STAILQ_FIRST(&sc->card.pf_head); if (pf == NULL || (STAILQ_NEXT(pf, pf_list) == NULL && (pf->cfe == NULL || pf->cfe->iftype == PCCARD_IFTYPE_MEMORY))) return (PCCARD_IFTYPE_MEMORY); else return (PCCARD_IFTYPE_IO); } /* * Initialize a PCCARD function. May be called as long as the function is * disabled. */ void pccard_function_init(pf, cfe) struct pccard_function *pf; struct pccard_config_entry *cfe; { if (pf->pf_flags & PFF_ENABLED) panic("pccard_function_init: function is enabled"); /* Remember which configuration entry we are using. */ pf->cfe = cfe; } static __inline void pccard_socket_enable(pct, pch) pccard_chipset_tag_t pct; pccard_chipset_handle_t *pch; { pccard_chip_socket_enable(pct, pch); } static __inline void pccard_socket_disable(pct, pch) pccard_chipset_tag_t pct; pccard_chipset_handle_t *pch; { pccard_chip_socket_disable(pct, pch); } /* Enable a PCCARD function */ int pccard_function_enable(pf) struct pccard_function *pf; { struct pccard_function *tmp; int reg; if (pf->cfe == NULL) panic("pccard_function_enable: function not initialized"); /* * Increase the reference count on the socket, enabling power, if * necessary. */ if (pf->sc->sc_enabled_count++ == 0) pccard_chip_socket_enable(pf->sc->pct, pf->sc->pch); DPRINTF(("%s: ++enabled_count = %d\n", pf->sc->dev.dv_xname, pf->sc->sc_enabled_count)); if (pf->pf_flags & PFF_ENABLED) { /* * Don't do anything if we're already enabled. */ return (0); } /* * it's possible for different functions' CCRs to be in the same * underlying page. Check for that. */ STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) { if ((tmp->pf_flags & PFF_ENABLED) && (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) && ((pf->ccr_base + PCCARD_CCR_SIZE) <= (tmp->ccr_base - tmp->pf_ccr_offset + tmp->pf_ccr_realsize))) { pf->pf_ccrt = tmp->pf_ccrt; pf->pf_ccrh = tmp->pf_ccrh; pf->pf_ccr_realsize = tmp->pf_ccr_realsize; /* * pf->pf_ccr_offset = (tmp->pf_ccr_offset - * tmp->ccr_base) + pf->ccr_base; */ pf->pf_ccr_offset = (tmp->pf_ccr_offset + pf->ccr_base) - tmp->ccr_base; pf->pf_ccr_window = tmp->pf_ccr_window; break; } } if (tmp == NULL) { if (pccard_mem_alloc(pf, PCCARD_CCR_SIZE, &pf->pf_pcmh)) goto bad; if (pccard_mem_map(pf, PCCARD_MEM_ATTR, pf->ccr_base, PCCARD_CCR_SIZE, &pf->pf_pcmh, &pf->pf_ccr_offset, &pf->pf_ccr_window)) { pccard_mem_free(pf, &pf->pf_pcmh); goto bad; } } reg = (pf->cfe->number & PCCARD_CCR_OPTION_CFINDEX); reg |= PCCARD_CCR_OPTION_LEVIREQ; if (pccard_mfc(pf->sc)) { reg |= (PCCARD_CCR_OPTION_FUNC_ENABLE | PCCARD_CCR_OPTION_ADDR_DECODE); if (pf->ih_fct) reg |= PCCARD_CCR_OPTION_IREQ_ENABLE; } pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg); reg = 0; if ((pf->cfe->flags & PCCARD_CFE_IO16) == 0) reg |= PCCARD_CCR_STATUS_IOIS8; if (pf->cfe->flags & PCCARD_CFE_AUDIO) reg |= PCCARD_CCR_STATUS_AUDIO; pccard_ccr_write(pf, PCCARD_CCR_STATUS, reg); pccard_ccr_write(pf, PCCARD_CCR_SOCKETCOPY, 0); if (pccard_mfc(pf->sc)) { long tmp, iosize; tmp = pf->pf_mfc_iomax - pf->pf_mfc_iobase; /* round up to nearest (2^n)-1 */ for (iosize = 1; iosize < tmp; iosize <<= 1) ; iosize--; pccard_ccr_write(pf, PCCARD_CCR_IOBASE0, pf->pf_mfc_iobase & 0xff); pccard_ccr_write(pf, PCCARD_CCR_IOBASE1, (pf->pf_mfc_iobase >> 8) & 0xff); pccard_ccr_write(pf, PCCARD_CCR_IOBASE2, 0); pccard_ccr_write(pf, PCCARD_CCR_IOBASE3, 0); pccard_ccr_write(pf, PCCARD_CCR_IOSIZE, iosize); } #ifdef PCCARDDEBUG if (pccard_debug) { STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) { printf("%s: function %d CCR at %d offset %lx: " "%x %x %x %x, %x %x %x %x, %x\n", tmp->sc->dev.dv_xname, tmp->number, tmp->pf_ccr_window, tmp->pf_ccr_offset, pccard_ccr_read(tmp, 0x00), pccard_ccr_read(tmp, 0x02), pccard_ccr_read(tmp, 0x04), pccard_ccr_read(tmp, 0x06), pccard_ccr_read(tmp, 0x0A), pccard_ccr_read(tmp, 0x0C), pccard_ccr_read(tmp, 0x0E), pccard_ccr_read(tmp, 0x10), pccard_ccr_read(tmp, 0x12)); } } #endif pf->pf_flags |= PFF_ENABLED; return (0); bad: /* * Decrement the reference count, and power down the socket, if * necessary. */ if (--pf->sc->sc_enabled_count == 0) pccard_chip_socket_disable(pf->sc->pct, pf->sc->pch); DPRINTF(("%s: --enabled_count = %d\n", pf->sc->dev.dv_xname, pf->sc->sc_enabled_count)); return (1); } /* Disable PCCARD function. */ void pccard_function_disable(pf) struct pccard_function *pf; { struct pccard_function *tmp; if (pf->cfe == NULL) panic("pccard_function_enable: function not initialized"); if ((pf->pf_flags & PFF_ENABLED) == 0) { /* * Don't do anything if we're already disabled. */ return; } /* * it's possible for different functions' CCRs to be in the same * underlying page. Check for that. Note we mark us as disabled * first to avoid matching ourself. */ pf->pf_flags &= ~PFF_ENABLED; STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) { if ((tmp->pf_flags & PFF_ENABLED) && (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) && ((pf->ccr_base + PCCARD_CCR_SIZE) <= (tmp->ccr_base - tmp->pf_ccr_offset + tmp->pf_ccr_realsize))) break; } /* Not used by anyone else; unmap the CCR. */ if (tmp == NULL) { pccard_mem_unmap(pf, pf->pf_ccr_window); pccard_mem_free(pf, &pf->pf_pcmh); } /* * Decrement the reference count, and power down the socket, if * necessary. */ if (--pf->sc->sc_enabled_count == 0) pccard_chip_socket_disable(pf->sc->pct, pf->sc->pch); DPRINTF(("%s: --enabled_count = %d\n", pf->sc->dev.dv_xname, pf->sc->sc_enabled_count)); } int pccard_io_map(pf, width, offset, size, pcihp, windowp) struct pccard_function *pf; int width; bus_addr_t offset; bus_size_t size; struct pccard_io_handle *pcihp; int *windowp; { int reg; if (pccard_chip_io_map(pf->sc->pct, pf->sc->pch, width, offset, size, pcihp, windowp)) return (1); /* * XXX in the multifunction multi-iospace-per-function case, this * needs to cooperate with io_alloc to make sure that the spaces * don't overlap, and that the ccr's are set correctly */ if (pccard_mfc(pf->sc)) { long tmp, iosize; if (pf->pf_mfc_iomax == 0) { pf->pf_mfc_iobase = pcihp->addr + offset; pf->pf_mfc_iomax = pf->pf_mfc_iobase + size; } else { /* this makes the assumption that nothing overlaps */ if (pf->pf_mfc_iobase > pcihp->addr + offset) pf->pf_mfc_iobase = pcihp->addr + offset; if (pf->pf_mfc_iomax < pcihp->addr + offset + size) pf->pf_mfc_iomax = pcihp->addr + offset + size; } tmp = pf->pf_mfc_iomax - pf->pf_mfc_iobase; /* round up to nearest (2^n)-1 */ for (iosize = 1; iosize >= tmp; iosize <<= 1) ; iosize--; pccard_ccr_write(pf, PCCARD_CCR_IOBASE0, pf->pf_mfc_iobase & 0xff); pccard_ccr_write(pf, PCCARD_CCR_IOBASE1, (pf->pf_mfc_iobase >> 8) & 0xff); pccard_ccr_write(pf, PCCARD_CCR_IOBASE2, 0); pccard_ccr_write(pf, PCCARD_CCR_IOBASE3, 0); pccard_ccr_write(pf, PCCARD_CCR_IOSIZE, iosize); reg = pccard_ccr_read(pf, PCCARD_CCR_OPTION); reg |= PCCARD_CCR_OPTION_ADDR_DECODE; pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg); } return (0); } void pccard_io_unmap(pf, window) struct pccard_function *pf; int window; { pccard_chip_io_unmap(pf->sc->pct, pf->sc->pch, window); /* XXX Anything for multi-function cards? */ } void * pccard_intr_establish(pf, ipl, ih_fct, ih_arg) struct pccard_function *pf; int ipl; int (*ih_fct) __P((void *)); void *ih_arg; { void *ret; /* behave differently if this is a multifunction card */ if (pccard_mfc(pf->sc)) { int s, ihcnt, hiipl, reg; struct pccard_function *pf2; /* * mask all the ipl's which are already used by this card, * and find the highest ipl number (lowest priority) */ ihcnt = 0; s = 0; /* this is only here to keep the compiler happy */ hiipl = 0; /* this is only here to keep the compiler happy */ STAILQ_FOREACH(pf2, &pf->sc->card.pf_head, pf_list) { if (pf2->ih_fct) { DPRINTF(("%s: function %d has ih_fct %p\n", pf->sc->dev.dv_xname, pf2->number, pf2->ih_fct)); if (ihcnt == 0) { hiipl = pf2->ih_ipl; } else { if (pf2->ih_ipl > hiipl) hiipl = pf2->ih_ipl; } ihcnt++; } } /* * establish the real interrupt, changing the ipl if * necessary */ if (ihcnt == 0) { #ifdef DIAGNOSTIC if (pf->sc->ih != NULL) panic("card has intr handler, but no function does"); #endif s = splhigh(); /* set up the handler for the new function */ pf->ih_fct = ih_fct; pf->ih_arg = ih_arg; pf->ih_ipl = ipl; pf->sc->ih = pccard_chip_intr_establish(pf->sc->pct, pf->sc->pch, pf, ipl, PCCARD_CARD_INTR, pf->sc); splx(s); } else if (ipl > hiipl) { #ifdef DIAGNOSTIC if (pf->sc->ih == NULL) panic("functions have ih, but the card does not"); #endif /* XXX need #ifdef for splserial on x86 */ s = splhigh(); pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch, pf->sc->ih); /* set up the handler for the new function */ pf->ih_fct = ih_fct; pf->ih_arg = ih_arg; pf->ih_ipl = ipl; pf->sc->ih = pccard_chip_intr_establish(pf->sc->pct, pf->sc->pch, pf, ipl, PCCARD_CARD_INTR, pf->sc); splx(s); } else { s = splhigh(); /* set up the handler for the new function */ pf->ih_fct = ih_fct; pf->ih_arg = ih_arg; pf->ih_ipl = ipl; splx(s); } ret = pf->sc->ih; if (ret != NULL) { reg = pccard_ccr_read(pf, PCCARD_CCR_OPTION); reg |= PCCARD_CCR_OPTION_IREQ_ENABLE; pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg); reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); reg |= PCCARD_CCR_STATUS_INTRACK; pccard_ccr_write(pf, PCCARD_CCR_STATUS, reg); } } else { ret = pccard_chip_intr_establish(pf->sc->pct, pf->sc->pch, pf, ipl, ih_fct, ih_arg); } return (ret); } void pccard_intr_disestablish(pf, ih) struct pccard_function *pf; void *ih; { /* behave differently if this is a multifunction card */ if (pccard_mfc(pf->sc)) { int s, ihcnt, hiipl; struct pccard_function *pf2; /* * mask all the ipl's which are already used by this card, * and find the highest ipl number (lowest priority). Skip * the current function. */ ihcnt = 0; s = 0; /* this is only here to keep the compipler happy */ hiipl = 0; /* this is only here to keep the compipler happy */ STAILQ_FOREACH(pf2, &pf->sc->card.pf_head, pf_list) { if (pf2 == pf) continue; if (pf2->ih_fct) { if (ihcnt == 0) { hiipl = pf2->ih_ipl; } else { if (pf2->ih_ipl > hiipl) hiipl = pf2->ih_ipl; } ihcnt++; } } /* * if the ih being removed is lower priority than the lowest * priority remaining interrupt, up the priority. */ /* ihcnt is the number of interrupt handlers *not* including the one about to be removed. */ if (ihcnt == 0) { int reg; #ifdef DIAGNOSTIC if (pf->sc->ih == NULL) panic("disestablishing last function, but card has no ih"); #endif pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch, pf->sc->ih); reg = pccard_ccr_read(pf, PCCARD_CCR_OPTION); reg &= ~PCCARD_CCR_OPTION_IREQ_ENABLE; pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg); pf->ih_fct = NULL; pf->ih_arg = NULL; pf->sc->ih = NULL; } else if (pf->ih_ipl > hiipl) { #ifdef DIAGNOSTIC if (pf->sc->ih == NULL) panic("changing ih ipl, but card has no ih"); #endif /* XXX need #ifdef for splserial on x86 */ s = splhigh(); pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch, pf->sc->ih); pf->sc->ih = pccard_chip_intr_establish(pf->sc->pct, pf->sc->pch, pf, hiipl, PCCARD_CARD_INTR, pf->sc); /* null out the handler for this function */ pf->ih_fct = NULL; pf->ih_arg = NULL; splx(s); } else { s = splhigh(); pf->ih_fct = NULL; pf->ih_arg = NULL; splx(s); } } else { pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch, ih); } } int pccard_card_intr(arg) void *arg; { struct pccard_softc *sc = arg; struct pccard_function *pf; int reg, ret, ret2; ret = 0; STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { if (pf->ih_fct != NULL && (pf->ccr_mask & (1 << (PCCARD_CCR_STATUS / 2)))) { reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); if (reg & PCCARD_CCR_STATUS_INTR) { ret2 = (*pf->ih_fct)(pf->ih_arg); if (ret2 != 0 && ret == 0) ret = ret2; reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); pccard_ccr_write(pf, PCCARD_CCR_STATUS, reg & ~PCCARD_CCR_STATUS_INTR); } } } return (ret); } #ifdef PCCARDDEBUG int pccard_card_intrdebug(arg) void *arg; { struct pccard_softc *sc = arg; struct pccard_function *pf; int reg, ret, ret2; ret = 0; STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { printf("%s: intr flags=%x fct=%d cor=%02x csr=%02x pin=%02x", sc->dev.dv_xname, pf->pf_flags, pf->number, pccard_ccr_read(pf, PCCARD_CCR_OPTION), pccard_ccr_read(pf, PCCARD_CCR_STATUS), pccard_ccr_read(pf, PCCARD_CCR_PIN)); if (pf->ih_fct != NULL && (pf->ccr_mask & (1 << (PCCARD_CCR_STATUS / 2)))) { reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); if (reg & PCCARD_CCR_STATUS_INTR) { ret2 = (*pf->ih_fct)(pf->ih_arg); if (ret2 != 0 && ret == 0) ret = ret2; reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); printf("; csr %02x->%02x", reg, reg & ~PCCARD_CCR_STATUS_INTR); pccard_ccr_write(pf, PCCARD_CCR_STATUS, reg & ~PCCARD_CCR_STATUS_INTR); } } printf("\n"); } return (ret); } #endif #define PCCARD_NPORT 2 #define PCCARD_NMEM 5 #define PCCARD_NIRQ 1 #define PCCARD_NDRQ 0 static int pccard_add_children(device_t dev, int busno) { - device_add_child(dev, NULL, -1, NULL); + device_add_child(dev, NULL, -1); return 0; } static int pccard_probe(device_t dev) { device_set_desc(dev, "PC Card bus -- newconfig version"); return pccard_add_children(dev, device_get_unit(dev)); } static void pccard_print_resources(struct resource_list *rl, const char *name, int type, int count, const char *format) { struct resource_list_entry *rle; int printed; int i; printed = 0; for (i = 0; i < count; i++) { rle = resource_list_find(rl, type, i); if (rle) { if (printed == 0) printf(" %s ", name); else if (printed > 0) printf(","); printed++; printf(format, rle->start); if (rle->count > 1) { printf("-"); printf(format, rle->start + rle->count - 1); } } else if (i > 3) { /* check the first few regardless */ break; } } } static int pccard_print_child(device_t dev, device_t child) { struct pccard_ivar *devi = (struct pccard_ivar *) device_get_ivars(child); struct resource_list *rl = &devi->resources; int retval = 0; retval += bus_print_child_header(dev, child); retval += printf(" at"); if (devi) { pccard_print_resources(rl, "port", SYS_RES_IOPORT, PCCARD_NPORT, "%#lx"); pccard_print_resources(rl, "iomem", SYS_RES_MEMORY, PCCARD_NMEM, "%#lx"); pccard_print_resources(rl, "irq", SYS_RES_IRQ, PCCARD_NIRQ, "%ld"); pccard_print_resources(rl, "drq", SYS_RES_DRQ, PCCARD_NDRQ, "%ld"); retval += printf(" slot %d", devi->slotnum); } retval += bus_print_child_footer(dev, child); return (retval); } static int pccard_set_resource(device_t dev, device_t child, int type, int rid, u_long start, u_long count) { struct pccard_ivar *devi = (struct pccard_ivar *) device_get_ivars(child); struct resource_list *rl = &devi->resources; if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY && type != SYS_RES_IRQ && type != SYS_RES_DRQ) return EINVAL; if (rid < 0) return EINVAL; if (type == SYS_RES_IOPORT && rid >= PCCARD_NPORT) return EINVAL; if (type == SYS_RES_MEMORY && rid >= PCCARD_NMEM) return EINVAL; if (type == SYS_RES_IRQ && rid >= PCCARD_NIRQ) return EINVAL; if (type == SYS_RES_DRQ && rid >= PCCARD_NDRQ) return EINVAL; resource_list_add(rl, type, rid, start, start + count - 1, count); return 0; } static int pccard_get_resource(device_t dev, device_t child, int type, int rid, u_long *startp, u_long *countp) { struct pccard_ivar *devi = (struct pccard_ivar *) device_get_ivars(child); struct resource_list *rl = &devi->resources; struct resource_list_entry *rle; rle = resource_list_find(rl, type, rid); if (!rle) return ENOENT; if (startp) *startp = rle->start; if (countp) *countp = rle->count; return 0; } static void pccard_delete_resource(device_t dev, device_t child, int type, int rid) { struct pccard_ivar *devi = (struct pccard_ivar *) device_get_ivars(child); struct resource_list *rl = &devi->resources; resource_list_delete(rl, type, rid); } static device_method_t pccard_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pccard_probe), DEVMETHOD(device_attach, bus_generic_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, pccard_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_set_resource, pccard_set_resource), DEVMETHOD(bus_get_resource, pccard_get_resource), DEVMETHOD(bus_delete_resource, pccard_delete_resource), { 0, 0 } }; static driver_t pccard_driver = { "pccard", pccard_methods, 1, /* no softc */ }; devclass_t pccard_devclass; DRIVER_MODULE(pccard, pcic, pccard_driver, pccard_devclass, 0, 0); DRIVER_MODULE(pccard, pc98pcic, pccard_driver, pccard_devclass, 0, 0); DRIVER_MODULE(pccard, pccbb, pccard_driver, pccard_devclass, 0, 0); DRIVER_MODULE(pccard, tcic, pccard_driver, pccard_devclass, 0, 0); Index: head/sys/dev/pcf/pcf.c =================================================================== --- head/sys/dev/pcf/pcf.c (revision 54072) +++ head/sys/dev/pcf/pcf.c (revision 54073) @@ -1,646 +1,646 @@ /*- * Copyright (c) 1998 Nicolas Souchu, Marc Bouget * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * */ #include #include #include #include #include #include #include #include #include #include "iicbus_if.h" #define TIMEOUT 9999 /* XXX */ /* Status bits of S1 register (read only) */ #define nBB 0x01 /* busy when low set/reset by STOP/START*/ #define LAB 0x02 /* lost arbitration bit in multi-master mode */ #define AAS 0x04 /* addressed as slave */ #define LRB 0x08 /* last received byte when not AAS */ #define AD0 0x08 /* general call received when AAS */ #define BER 0x10 /* bus error, misplaced START or STOP */ #define STS 0x20 /* STOP detected in slave receiver mode */ #define PIN 0x80 /* pending interrupt not (r/w) */ /* Control bits of S1 register (write only) */ #define ACK 0x01 #define STO 0x02 #define STA 0x04 #define ENI 0x08 #define ES2 0x10 #define ES1 0x20 #define ES0 0x40 #define BUFSIZE 2048 #define SLAVE_TRANSMITTER 0x1 #define SLAVE_RECEIVER 0x2 #define PCF_DEFAULT_ADDR 0xaa struct pcf_softc { int pcf_base; /* isa port */ u_char pcf_addr; /* interface I2C address */ int pcf_slave_mode; /* receiver or transmitter */ int pcf_started; /* 1 if start condition sent */ device_t iicbus; /* the corresponding iicbus */ }; struct pcf_isa_softc { int pcf_unit; /* unit of the isa device */ int pcf_base; /* isa port */ int pcf_irq; /* isa irq or null if polled */ unsigned int pcf_flags; /* boot flags */ }; #define MAXPCF 2 static struct pcf_isa_softc *pcfdata[MAXPCF]; static int npcf = 0; static int pcfprobe_isa(struct isa_device *); static int pcfattach_isa(struct isa_device *); struct isa_driver pcfdriver = { pcfprobe_isa, pcfattach_isa, "pcf" }; static int pcf_probe(device_t); static int pcf_attach(device_t); static int pcf_print_child(device_t, device_t); static int pcf_repeated_start(device_t, u_char, int); static int pcf_start(device_t, u_char, int); static int pcf_stop(device_t); static int pcf_write(device_t, char *, int, int *, int); static int pcf_read(device_t, char *, int, int *, int, int); static ointhand2_t pcfintr; static int pcf_rst_card(device_t, u_char, u_char, u_char *); static device_method_t pcf_methods[] = { /* device interface */ DEVMETHOD(device_probe, pcf_probe), DEVMETHOD(device_attach, pcf_attach), /* bus interface */ DEVMETHOD(bus_print_child, pcf_print_child), /* iicbus interface */ DEVMETHOD(iicbus_callback, iicbus_null_callback), DEVMETHOD(iicbus_repeated_start, pcf_repeated_start), DEVMETHOD(iicbus_start, pcf_start), DEVMETHOD(iicbus_stop, pcf_stop), DEVMETHOD(iicbus_write, pcf_write), DEVMETHOD(iicbus_read, pcf_read), DEVMETHOD(iicbus_reset, pcf_rst_card), { 0, 0 } }; static driver_t pcf_driver = { "pcf", pcf_methods, sizeof(struct pcf_softc), }; static devclass_t pcf_devclass; #define DEVTOSOFTC(dev) ((struct pcf_softc *)device_get_softc(dev)) static int pcfprobe_isa(struct isa_device *dvp) { device_t pcfdev; struct pcf_isa_softc *pcf; if (npcf >= MAXPCF) return (0); if ((pcf = (struct pcf_isa_softc *)malloc(sizeof(struct pcf_isa_softc), M_DEVBUF, M_NOWAIT)) == NULL) return (0); pcf->pcf_base = dvp->id_iobase; /* XXX should be ivars */ pcf->pcf_unit = dvp->id_unit; if (!(dvp->id_flags & IIC_POLLED)) pcf->pcf_irq = (dvp->id_irq); pcfdata[npcf++] = pcf; /* XXX add the pcf device to the root_bus until isa bus exists */ - pcfdev = device_add_child(root_bus, "pcf", pcf->pcf_unit, NULL); + pcfdev = device_add_child(root_bus, "pcf", pcf->pcf_unit); if (!pcfdev) goto error; return (1); error: free(pcf, M_DEVBUF); return (0); } static int pcfattach_isa(struct isa_device *isdp) { isdp->id_ointr = pcfintr; return (1); /* ok */ } static int pcf_probe(device_t pcfdev) { struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(pcfdev); int unit = device_get_unit(pcfdev); /* retrieve base address from isa initialization * * XXX should use ivars with isabus */ pcf->pcf_base = pcfdata[unit]->pcf_base; /* reset the chip */ pcf_rst_card(pcfdev, IIC_FASTEST, PCF_DEFAULT_ADDR, NULL); /* XXX try do detect chipset */ device_set_desc(pcfdev, "PCF8584 I2C bus controller"); return (0); } static int pcf_attach(device_t pcfdev) { struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(pcfdev); pcf->iicbus = iicbus_alloc_bus(pcfdev); /* probe and attach the iicbus */ device_probe_and_attach(pcf->iicbus); return (0); } static int pcf_print_child(device_t bus, device_t dev) { struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(bus); int retval = 0; retval += bus_print_child_header(bus, dev); retval += printf(" on %s addr 0x%x\n", device_get_nameunit(bus), (int)pcf->pcf_addr); return (retval); } /* * PCF8584 datasheet : when operate at 8 MHz or more, a minimun time of * 6 clocks cycles must be left between two consecutives access */ #define pcf_nops() DELAY(10) #define dummy_read(pcf) PCF_GET_S0(pcf) #define dummy_write(pcf) PCF_SET_S0(pcf, 0) /* * Specific register access to PCF8584 */ static void PCF_SET_S0(struct pcf_softc *pcf, int data) { outb(pcf->pcf_base, data); pcf_nops(); } static void PCF_SET_S1(struct pcf_softc *pcf, int data) { outb(pcf->pcf_base+1, data); pcf_nops(); } static char PCF_GET_S0(struct pcf_softc *pcf) { char data; data = inb(pcf->pcf_base); pcf_nops(); return (data); } static char PCF_GET_S1(struct pcf_softc *pcf) { char data; data = inb(pcf->pcf_base+1); pcf_nops(); return (data); } /* * Polling mode for master operations wait for a new * byte incomming or outgoing */ static int pcf_wait_byte(struct pcf_softc *pcf) { int counter = TIMEOUT; while (counter--) { if ((PCF_GET_S1(pcf) & PIN) == 0) return (0); } return (IIC_ETIMEOUT); } static int pcf_stop(device_t pcfdev) { struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); /* * Send STOP condition iff the START condition was previously sent. * STOP is sent only once even if a iicbus_stop() is called after * an iicbus_read()... see pcf_read(): the pcf needs to send the stop * before the last char is read. */ if (pcf->pcf_started) { /* set stop condition and enable IT */ PCF_SET_S1(pcf, PIN|ES0|ENI|STO|ACK); pcf->pcf_started = 0; } return (0); } static int pcf_noack(struct pcf_softc *pcf, int timeout) { int noack; int k = timeout/10; do { noack = PCF_GET_S1(pcf) & LRB; if (!noack) break; DELAY(10); /* XXX wait 10 us */ } while (k--); return (noack); } static int pcf_repeated_start(device_t pcfdev, u_char slave, int timeout) { struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); int error = 0; /* repeated start */ PCF_SET_S1(pcf, ES0|STA|STO|ACK); /* set slave address to PCF. Last bit (LSB) must be set correctly * according to transfer direction */ PCF_SET_S0(pcf, slave); /* wait for address sent, polling */ if ((error = pcf_wait_byte(pcf))) goto error; /* check for ack */ if (pcf_noack(pcf, timeout)) { error = IIC_ENOACK; goto error; } return (0); error: pcf_stop(pcfdev); return (error); } static int pcf_start(device_t pcfdev, u_char slave, int timeout) { struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); int error = 0; if ((PCF_GET_S1(pcf) & nBB) == 0) return (IIC_EBUSBSY); /* set slave address to PCF. Last bit (LSB) must be set correctly * according to transfer direction */ PCF_SET_S0(pcf, slave); /* START only */ PCF_SET_S1(pcf, PIN|ES0|STA|ACK); pcf->pcf_started = 1; /* wait for address sent, polling */ if ((error = pcf_wait_byte(pcf))) goto error; /* check for ACK */ if (pcf_noack(pcf, timeout)) { error = IIC_ENOACK; goto error; } return (0); error: pcf_stop(pcfdev); return (error); } static void pcfintr(unit) { struct pcf_softc *pcf = (struct pcf_softc *)devclass_get_softc(pcf_devclass, unit); char data, status, addr; char error = 0; status = PCF_GET_S1(pcf); if (status & PIN) { printf("pcf%d: spurious interrupt, status=0x%x\n", unit, status & 0xff); goto error; } if (status & LAB) printf("pcf%d: bus arbitration lost!\n", unit); if (status & BER) { error = IIC_EBUSERR; iicbus_intr(pcf->iicbus, INTR_ERROR, &error); goto error; } do { status = PCF_GET_S1(pcf); switch(pcf->pcf_slave_mode) { case SLAVE_TRANSMITTER: if (status & LRB) { /* ack interrupt line */ dummy_write(pcf); /* no ack, don't send anymore */ pcf->pcf_slave_mode = SLAVE_RECEIVER; iicbus_intr(pcf->iicbus, INTR_NOACK, NULL); break; } /* get data from upper code */ iicbus_intr(pcf->iicbus, INTR_TRANSMIT, &data); PCF_SET_S0(pcf, data); break; case SLAVE_RECEIVER: if (status & AAS) { addr = PCF_GET_S0(pcf); if (status & AD0) iicbus_intr(pcf->iicbus, INTR_GENERAL, &addr); else iicbus_intr(pcf->iicbus, INTR_START, &addr); if (addr & LSB) { pcf->pcf_slave_mode = SLAVE_TRANSMITTER; /* get the first char from upper code */ iicbus_intr(pcf->iicbus, INTR_TRANSMIT, &data); /* send first data byte */ PCF_SET_S0(pcf, data); } break; } /* stop condition received? */ if (status & STS) { /* ack interrupt line */ dummy_read(pcf); /* emulate intr stop condition */ iicbus_intr(pcf->iicbus, INTR_STOP, NULL); } else { /* get data, ack interrupt line */ data = PCF_GET_S0(pcf); /* deliver the character */ iicbus_intr(pcf->iicbus, INTR_RECEIVE, &data); } break; default: panic("%s: unknown slave mode (%d)!", __FUNCTION__, pcf->pcf_slave_mode); } } while ((PCF_GET_S1(pcf) & PIN) == 0); return; error: /* unknown event on bus...reset PCF */ PCF_SET_S1(pcf, PIN|ES0|ENI|ACK); pcf->pcf_slave_mode = SLAVE_RECEIVER; return; } static int pcf_rst_card(device_t pcfdev, u_char speed, u_char addr, u_char *oldaddr) { struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); if (oldaddr) *oldaddr = pcf->pcf_addr; /* retrieve own address from bus level */ if (!addr) pcf->pcf_addr = PCF_DEFAULT_ADDR; else pcf->pcf_addr = addr; PCF_SET_S1(pcf, PIN); /* initialize S1 */ /* own address S'O<>0 */ PCF_SET_S0(pcf, pcf->pcf_addr >> 1); /* select clock register */ PCF_SET_S1(pcf, PIN|ES1); /* select bus speed : 18=90kb, 19=45kb, 1A=11kb, 1B=1.5kb */ switch (speed) { case IIC_SLOW: PCF_SET_S0(pcf, 0x1b); break; case IIC_FAST: PCF_SET_S0(pcf, 0x19); break; case IIC_UNKNOWN: case IIC_FASTEST: default: PCF_SET_S0(pcf, 0x18); break; } /* set bus on, ack=yes, INT=yes */ PCF_SET_S1(pcf, PIN|ES0|ENI|ACK); pcf->pcf_slave_mode = SLAVE_RECEIVER; return (0); } static int pcf_write(device_t pcfdev, char *buf, int len, int *sent, int timeout /* us */) { struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); int bytes, error = 0; #ifdef PCFDEBUG printf("pcf%d: >> writing %d bytes\n", device_get_unit(pcfdev), len); #endif bytes = 0; while (len) { PCF_SET_S0(pcf, *buf++); /* wait for the byte to be send */ if ((error = pcf_wait_byte(pcf))) goto error; /* check if ack received */ if (pcf_noack(pcf, timeout)) { error = IIC_ENOACK; goto error; } len --; bytes ++; } error: *sent = bytes; #ifdef PCFDEBUG printf("pcf%d: >> %d bytes written (%d)\n", device_get_unit(pcfdev), bytes, error); #endif return (error); } static int pcf_read(device_t pcfdev, char *buf, int len, int *read, int last, int delay /* us */) { struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); int bytes, error = 0; #ifdef PCFDEBUG printf("pcf%d: << reading %d bytes\n", device_get_unit(pcfdev), len); #endif /* trig the bus to get the first data byte in S0 */ if (len) { if (len == 1 && last) /* just one byte to read */ PCF_SET_S1(pcf, ES0); /* no ack */ dummy_read(pcf); } bytes = 0; while (len) { /* XXX delay needed here */ /* wait for trigged byte */ if ((error = pcf_wait_byte(pcf))) { pcf_stop(pcfdev); goto error; } if (len == 1 && last) /* ok, last data byte already in S0, no I2C activity * on next PCF_GET_S0() */ pcf_stop(pcfdev); else if (len == 2 && last) /* next trigged byte with no ack */ PCF_SET_S1(pcf, ES0); /* receive byte, trig next byte */ *buf++ = PCF_GET_S0(pcf); len --; bytes ++; }; error: *read = bytes; #ifdef PCFDEBUG printf("pcf%d: << %d bytes read (%d)\n", device_get_unit(pcfdev), bytes, error); #endif return (error); } DRIVER_MODULE(pcf, root, pcf_driver, pcf_devclass, 0, 0); Index: head/sys/dev/pci/pci.c =================================================================== --- head/sys/dev/pci/pci.c (revision 54072) +++ head/sys/dev/pci/pci.c (revision 54073) @@ -1,1433 +1,1433 @@ /* * Copyright (c) 1997, Stefan Esser * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * */ #include "opt_bus.h" #include "opt_simos.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* For the Alpha */ #include #include #include #ifdef APIC_IO #include #endif /* APIC_IO */ struct pci_quirk { u_int32_t devid; /* Vendor/device of the card */ int type; #define PCI_QUIRK_MAP_REG 1 /* PCI map register in wierd place */ int arg1; int arg2; }; struct pci_quirk pci_quirks[] = { /* * The Intel 82371AB has a map register at offset 0x90. */ { 0x71138086, PCI_QUIRK_MAP_REG, 0x90, 0 }, { 0 } }; /* map register information */ #define PCI_MAPMEM 0x01 /* memory map */ #define PCI_MAPMEMP 0x02 /* prefetchable memory map */ #define PCI_MAPPORT 0x04 /* port map */ struct pci_devinfo { STAILQ_ENTRY(pci_devinfo) pci_links; struct resource_list resources; pcicfgregs cfg; struct pci_conf conf; }; static STAILQ_HEAD(devlist, pci_devinfo) pci_devq; u_int32_t pci_numdevs = 0; static u_int32_t pci_generation = 0; /* return base address of memory or port map */ static u_int32_t pci_mapbase(unsigned mapreg) { int mask = 0x03; if ((mapreg & 0x01) == 0) mask = 0x0f; return (mapreg & ~mask); } /* return map type of memory or port map */ static int pci_maptype(unsigned mapreg) { static u_int8_t maptype[0x10] = { PCI_MAPMEM, PCI_MAPPORT, PCI_MAPMEM, 0, PCI_MAPMEM, PCI_MAPPORT, 0, 0, PCI_MAPMEM|PCI_MAPMEMP, PCI_MAPPORT, PCI_MAPMEM|PCI_MAPMEMP, 0, PCI_MAPMEM|PCI_MAPMEMP, PCI_MAPPORT, 0, 0, }; return maptype[mapreg & 0x0f]; } /* return log2 of map size decoded for memory or port map */ static int pci_mapsize(unsigned testval) { int ln2size; testval = pci_mapbase(testval); ln2size = 0; if (testval != 0) { while ((testval & 1) == 0) { ln2size++; testval >>= 1; } } return (ln2size); } /* return log2 of address range supported by map register */ static int pci_maprange(unsigned mapreg) { int ln2range = 0; switch (mapreg & 0x07) { case 0x00: case 0x01: case 0x05: ln2range = 32; break; case 0x02: ln2range = 20; break; case 0x04: ln2range = 64; break; } return (ln2range); } /* adjust some values from PCI 1.0 devices to match 2.0 standards ... */ static void pci_fixancient(pcicfgregs *cfg) { if (cfg->hdrtype != 0) return; /* PCI to PCI bridges use header type 1 */ if (cfg->baseclass == PCIC_BRIDGE && cfg->subclass == PCIS_BRIDGE_PCI) cfg->hdrtype = 1; } /* read config data specific to header type 1 device (PCI to PCI bridge) */ static void * pci_readppb(pcicfgregs *cfg) { pcih1cfgregs *p; p = malloc(sizeof (pcih1cfgregs), M_DEVBUF, M_WAITOK); if (p == NULL) return (NULL); bzero(p, sizeof *p); p->secstat = pci_cfgread(cfg, PCIR_SECSTAT_1, 2); p->bridgectl = pci_cfgread(cfg, PCIR_BRIDGECTL_1, 2); p->seclat = pci_cfgread(cfg, PCIR_SECLAT_1, 1); p->iobase = PCI_PPBIOBASE (pci_cfgread(cfg, PCIR_IOBASEH_1, 2), pci_cfgread(cfg, PCIR_IOBASEL_1, 1)); p->iolimit = PCI_PPBIOLIMIT (pci_cfgread(cfg, PCIR_IOLIMITH_1, 2), pci_cfgread(cfg, PCIR_IOLIMITL_1, 1)); p->membase = PCI_PPBMEMBASE (0, pci_cfgread(cfg, PCIR_MEMBASE_1, 2)); p->memlimit = PCI_PPBMEMLIMIT (0, pci_cfgread(cfg, PCIR_MEMLIMIT_1, 2)); p->pmembase = PCI_PPBMEMBASE ( (pci_addr_t)pci_cfgread(cfg, PCIR_PMBASEH_1, 4), pci_cfgread(cfg, PCIR_PMBASEL_1, 2)); p->pmemlimit = PCI_PPBMEMLIMIT ( (pci_addr_t)pci_cfgread(cfg, PCIR_PMLIMITH_1, 4), pci_cfgread(cfg, PCIR_PMLIMITL_1, 2)); return (p); } /* read config data specific to header type 2 device (PCI to CardBus bridge) */ static void * pci_readpcb(pcicfgregs *cfg) { pcih2cfgregs *p; p = malloc(sizeof (pcih2cfgregs), M_DEVBUF, M_WAITOK); if (p == NULL) return (NULL); bzero(p, sizeof *p); p->secstat = pci_cfgread(cfg, PCIR_SECSTAT_2, 2); p->bridgectl = pci_cfgread(cfg, PCIR_BRIDGECTL_2, 2); p->seclat = pci_cfgread(cfg, PCIR_SECLAT_2, 1); p->membase0 = pci_cfgread(cfg, PCIR_MEMBASE0_2, 4); p->memlimit0 = pci_cfgread(cfg, PCIR_MEMLIMIT0_2, 4); p->membase1 = pci_cfgread(cfg, PCIR_MEMBASE1_2, 4); p->memlimit1 = pci_cfgread(cfg, PCIR_MEMLIMIT1_2, 4); p->iobase0 = pci_cfgread(cfg, PCIR_IOBASE0_2, 4); p->iolimit0 = pci_cfgread(cfg, PCIR_IOLIMIT0_2, 4); p->iobase1 = pci_cfgread(cfg, PCIR_IOBASE1_2, 4); p->iolimit1 = pci_cfgread(cfg, PCIR_IOLIMIT1_2, 4); p->pccardif = pci_cfgread(cfg, PCIR_PCCARDIF_2, 4); return p; } /* extract header type specific config data */ static void pci_hdrtypedata(pcicfgregs *cfg) { switch (cfg->hdrtype) { case 0: cfg->subvendor = pci_cfgread(cfg, PCIR_SUBVEND_0, 2); cfg->subdevice = pci_cfgread(cfg, PCIR_SUBDEV_0, 2); cfg->nummaps = PCI_MAXMAPS_0; break; case 1: cfg->subvendor = pci_cfgread(cfg, PCIR_SUBVEND_1, 2); cfg->subdevice = pci_cfgread(cfg, PCIR_SUBDEV_1, 2); cfg->secondarybus = pci_cfgread(cfg, PCIR_SECBUS_1, 1); cfg->subordinatebus = pci_cfgread(cfg, PCIR_SUBBUS_1, 1); cfg->nummaps = PCI_MAXMAPS_1; cfg->hdrspec = pci_readppb(cfg); break; case 2: cfg->subvendor = pci_cfgread(cfg, PCIR_SUBVEND_2, 2); cfg->subdevice = pci_cfgread(cfg, PCIR_SUBDEV_2, 2); cfg->secondarybus = pci_cfgread(cfg, PCIR_SECBUS_2, 1); cfg->subordinatebus = pci_cfgread(cfg, PCIR_SUBBUS_2, 1); cfg->nummaps = PCI_MAXMAPS_2; cfg->hdrspec = pci_readpcb(cfg); break; } } /* read configuration header into pcicfgrect structure */ static struct pci_devinfo * pci_readcfg(pcicfgregs *probe) { pcicfgregs *cfg = NULL; struct pci_devinfo *devlist_entry; struct devlist *devlist_head; devlist_head = &pci_devq; devlist_entry = NULL; if (pci_cfgread(probe, PCIR_DEVVENDOR, 4) != -1) { devlist_entry = malloc(sizeof(struct pci_devinfo), M_DEVBUF, M_WAITOK); if (devlist_entry == NULL) return (NULL); bzero(devlist_entry, sizeof *devlist_entry); cfg = &devlist_entry->cfg; cfg->hose = probe->hose; cfg->bus = probe->bus; cfg->slot = probe->slot; cfg->func = probe->func; cfg->vendor = pci_cfgread(cfg, PCIR_VENDOR, 2); cfg->device = pci_cfgread(cfg, PCIR_DEVICE, 2); cfg->cmdreg = pci_cfgread(cfg, PCIR_COMMAND, 2); cfg->statreg = pci_cfgread(cfg, PCIR_STATUS, 2); cfg->baseclass = pci_cfgread(cfg, PCIR_CLASS, 1); cfg->subclass = pci_cfgread(cfg, PCIR_SUBCLASS, 1); cfg->progif = pci_cfgread(cfg, PCIR_PROGIF, 1); cfg->revid = pci_cfgread(cfg, PCIR_REVID, 1); cfg->hdrtype = pci_cfgread(cfg, PCIR_HEADERTYPE, 1); cfg->cachelnsz = pci_cfgread(cfg, PCIR_CACHELNSZ, 1); cfg->lattimer = pci_cfgread(cfg, PCIR_LATTIMER, 1); cfg->intpin = pci_cfgread(cfg, PCIR_INTPIN, 1); cfg->intline = pci_cfgread(cfg, PCIR_INTLINE, 1); #ifdef __alpha__ alpha_platform_assign_pciintr(cfg); #endif #ifdef APIC_IO if (cfg->intpin != 0) { int airq; airq = pci_apic_irq(cfg->bus, cfg->slot, cfg->intpin); if (airq >= 0) { /* PCI specific entry found in MP table */ if (airq != cfg->intline) { undirect_pci_irq(cfg->intline); cfg->intline = airq; } } else { /* * PCI interrupts might be redirected to the * ISA bus according to some MP tables. Use the * same methods as used by the ISA devices * devices to find the proper IOAPIC int pin. */ airq = isa_apic_irq(cfg->intline); if ((airq >= 0) && (airq != cfg->intline)) { /* XXX: undirect_pci_irq() ? */ undirect_isa_irq(cfg->intline); cfg->intline = airq; } } } #endif /* APIC_IO */ cfg->mingnt = pci_cfgread(cfg, PCIR_MINGNT, 1); cfg->maxlat = pci_cfgread(cfg, PCIR_MAXLAT, 1); cfg->mfdev = (cfg->hdrtype & PCIM_MFDEV) != 0; cfg->hdrtype &= ~PCIM_MFDEV; pci_fixancient(cfg); pci_hdrtypedata(cfg); STAILQ_INSERT_TAIL(devlist_head, devlist_entry, pci_links); devlist_entry->conf.pc_sel.pc_bus = cfg->bus; devlist_entry->conf.pc_sel.pc_dev = cfg->slot; devlist_entry->conf.pc_sel.pc_func = cfg->func; devlist_entry->conf.pc_hdr = cfg->hdrtype; devlist_entry->conf.pc_subvendor = cfg->subvendor; devlist_entry->conf.pc_subdevice = cfg->subdevice; devlist_entry->conf.pc_vendor = cfg->vendor; devlist_entry->conf.pc_device = cfg->device; devlist_entry->conf.pc_class = cfg->baseclass; devlist_entry->conf.pc_subclass = cfg->subclass; devlist_entry->conf.pc_progif = cfg->progif; devlist_entry->conf.pc_revid = cfg->revid; pci_numdevs++; pci_generation++; } return (devlist_entry); } #if 0 /* free pcicfgregs structure and all depending data structures */ static int pci_freecfg(struct pci_devinfo *dinfo) { struct devlist *devlist_head; devlist_head = &pci_devq; if (dinfo->cfg.hdrspec != NULL) free(dinfo->cfg.hdrspec, M_DEVBUF); if (dinfo->cfg.map != NULL) free(dinfo->cfg.map, M_DEVBUF); /* XXX this hasn't been tested */ STAILQ_REMOVE(devlist_head, dinfo, pci_devinfo, pci_links); free(dinfo, M_DEVBUF); /* increment the generation count */ pci_generation++; /* we're losing one device */ pci_numdevs--; return (0); } #endif /* * This is the user interface to PCI configuration space. */ static int pci_open(dev_t dev, int oflags, int devtype, struct proc *p) { if ((oflags & FWRITE) && securelevel > 0) { return EPERM; } return 0; } static int pci_close(dev_t dev, int flag, int devtype, struct proc *p) { return 0; } /* * Match a single pci_conf structure against an array of pci_match_conf * structures. The first argument, 'matches', is an array of num_matches * pci_match_conf structures. match_buf is a pointer to the pci_conf * structure that will be compared to every entry in the matches array. * This function returns 1 on failure, 0 on success. */ static int pci_conf_match(struct pci_match_conf *matches, int num_matches, struct pci_conf *match_buf) { int i; if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) return(1); for (i = 0; i < num_matches; i++) { /* * I'm not sure why someone would do this...but... */ if (matches[i].flags == PCI_GETCONF_NO_MATCH) continue; /* * Look at each of the match flags. If it's set, do the * comparison. If the comparison fails, we don't have a * match, go on to the next item if there is one. */ if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0) && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0) && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0) && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0) && (match_buf->pc_vendor != matches[i].pc_vendor)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0) && (match_buf->pc_device != matches[i].pc_device)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0) && (match_buf->pc_class != matches[i].pc_class)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0) && (match_buf->pd_unit != matches[i].pd_unit)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0) && (strncmp(matches[i].pd_name, match_buf->pd_name, sizeof(match_buf->pd_name)) != 0)) continue; return(0); } return(1); } /* * Locate the parent of a PCI device by scanning the PCI devlist * and return the entry for the parent. * For devices on PCI Bus 0 (the host bus), this is the PCI Host. * For devices on secondary PCI busses, this is that bus' PCI-PCI Bridge. */ pcicfgregs * pci_devlist_get_parent(pcicfgregs *cfg) { struct devlist *devlist_head; struct pci_devinfo *dinfo; pcicfgregs *bridge_cfg; int i; dinfo = STAILQ_FIRST(devlist_head = &pci_devq); /* If the device is on PCI bus 0, look for the host */ if (cfg->bus == 0) { for (i = 0; (dinfo != NULL) && (i < pci_numdevs); dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { bridge_cfg = &dinfo->cfg; if (bridge_cfg->baseclass == PCIC_BRIDGE && bridge_cfg->subclass == PCIS_BRIDGE_HOST && bridge_cfg->bus == cfg->bus) { return bridge_cfg; } } } /* If the device is not on PCI bus 0, look for the PCI-PCI bridge */ if (cfg->bus > 0) { for (i = 0; (dinfo != NULL) && (i < pci_numdevs); dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { bridge_cfg = &dinfo->cfg; if (bridge_cfg->baseclass == PCIC_BRIDGE && bridge_cfg->subclass == PCIS_BRIDGE_PCI && bridge_cfg->secondarybus == cfg->bus) { return bridge_cfg; } } } return NULL; } static int pci_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { struct pci_io *io; const char *name; int error; if (!(flag & FWRITE)) return EPERM; switch(cmd) { case PCIOCGETCONF: { struct pci_devinfo *dinfo; struct pci_conf_io *cio; struct devlist *devlist_head; struct pci_match_conf *pattern_buf; int num_patterns; size_t iolen; int ionum, i; cio = (struct pci_conf_io *)data; num_patterns = 0; dinfo = NULL; /* * Hopefully the user won't pass in a null pointer, but it * can't hurt to check. */ if (cio == NULL) { error = EINVAL; break; } /* * If the user specified an offset into the device list, * but the list has changed since they last called this * ioctl, tell them that the list has changed. They will * have to get the list from the beginning. */ if ((cio->offset != 0) && (cio->generation != pci_generation)){ cio->num_matches = 0; cio->status = PCI_GETCONF_LIST_CHANGED; error = 0; break; } /* * Check to see whether the user has asked for an offset * past the end of our list. */ if (cio->offset >= pci_numdevs) { cio->num_matches = 0; cio->status = PCI_GETCONF_LAST_DEVICE; error = 0; break; } /* get the head of the device queue */ devlist_head = &pci_devq; /* * Determine how much room we have for pci_conf structures. * Round the user's buffer size down to the nearest * multiple of sizeof(struct pci_conf) in case the user * didn't specify a multiple of that size. */ iolen = min(cio->match_buf_len - (cio->match_buf_len % sizeof(struct pci_conf)), pci_numdevs * sizeof(struct pci_conf)); /* * Since we know that iolen is a multiple of the size of * the pciconf union, it's okay to do this. */ ionum = iolen / sizeof(struct pci_conf); /* * If this test is true, the user wants the pci_conf * structures returned to match the supplied entries. */ if ((cio->num_patterns > 0) && (cio->pat_buf_len > 0)) { /* * pat_buf_len needs to be: * num_patterns * sizeof(struct pci_match_conf) * While it is certainly possible the user just * allocated a large buffer, but set the number of * matches correctly, it is far more likely that * their kernel doesn't match the userland utility * they're using. It's also possible that the user * forgot to initialize some variables. Yes, this * may be overly picky, but I hazard to guess that * it's far more likely to just catch folks that * updated their kernel but not their userland. */ if ((cio->num_patterns * sizeof(struct pci_match_conf)) != cio->pat_buf_len){ /* The user made a mistake, return an error*/ cio->status = PCI_GETCONF_ERROR; printf("pci_ioctl: pat_buf_len %d != " "num_patterns (%d) * sizeof(struct " "pci_match_conf) (%d)\npci_ioctl: " "pat_buf_len should be = %d\n", cio->pat_buf_len, cio->num_patterns, (int)sizeof(struct pci_match_conf), (int)sizeof(struct pci_match_conf) * cio->num_patterns); printf("pci_ioctl: do your headers match your " "kernel?\n"); cio->num_matches = 0; error = EINVAL; break; } /* * Check the user's buffer to make sure it's readable. */ if (!useracc((caddr_t)cio->patterns, cio->pat_buf_len, VM_PROT_READ)) { printf("pci_ioctl: pattern buffer %p, " "length %u isn't user accessible for" " READ\n", cio->patterns, cio->pat_buf_len); error = EACCES; break; } /* * Allocate a buffer to hold the patterns. */ pattern_buf = malloc(cio->pat_buf_len, M_TEMP, M_WAITOK); error = copyin(cio->patterns, pattern_buf, cio->pat_buf_len); if (error != 0) break; num_patterns = cio->num_patterns; } else if ((cio->num_patterns > 0) || (cio->pat_buf_len > 0)) { /* * The user made a mistake, spit out an error. */ cio->status = PCI_GETCONF_ERROR; cio->num_matches = 0; printf("pci_ioctl: invalid GETCONF arguments\n"); error = EINVAL; break; } else pattern_buf = NULL; /* * Make sure we can write to the match buffer. */ if (!useracc((caddr_t)cio->matches, cio->match_buf_len, VM_PROT_WRITE)) { printf("pci_ioctl: match buffer %p, length %u " "isn't user accessible for WRITE\n", cio->matches, cio->match_buf_len); error = EACCES; break; } /* * Go through the list of devices and copy out the devices * that match the user's criteria. */ for (cio->num_matches = 0, error = 0, i = 0, dinfo = STAILQ_FIRST(devlist_head); (dinfo != NULL) && (cio->num_matches < ionum) && (error == 0) && (i < pci_numdevs); dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { if (i < cio->offset) continue; /* Populate pd_name and pd_unit */ name = NULL; if (dinfo->cfg.dev && dinfo->conf.pd_name[0] == '\0') name = device_get_name(dinfo->cfg.dev); if (name) { strncpy(dinfo->conf.pd_name, name, sizeof(dinfo->conf.pd_name)); dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0; dinfo->conf.pd_unit = device_get_unit(dinfo->cfg.dev); } if ((pattern_buf == NULL) || (pci_conf_match(pattern_buf, num_patterns, &dinfo->conf) == 0)) { /* * If we've filled up the user's buffer, * break out at this point. Since we've * got a match here, we'll pick right back * up at the matching entry. We can also * tell the user that there are more matches * left. */ if (cio->num_matches >= ionum) break; error = copyout(&dinfo->conf, &cio->matches[cio->num_matches], sizeof(struct pci_conf)); cio->num_matches++; } } /* * Set the pointer into the list, so if the user is getting * n records at a time, where n < pci_numdevs, */ cio->offset = i; /* * Set the generation, the user will need this if they make * another ioctl call with offset != 0. */ cio->generation = pci_generation; /* * If this is the last device, inform the user so he won't * bother asking for more devices. If dinfo isn't NULL, we * know that there are more matches in the list because of * the way the traversal is done. */ if (dinfo == NULL) cio->status = PCI_GETCONF_LAST_DEVICE; else cio->status = PCI_GETCONF_MORE_DEVS; if (pattern_buf != NULL) free(pattern_buf, M_TEMP); break; } case PCIOCREAD: io = (struct pci_io *)data; switch(io->pi_width) { pcicfgregs probe; case 4: case 2: case 1: probe.hose = -1; probe.bus = io->pi_sel.pc_bus; probe.slot = io->pi_sel.pc_dev; probe.func = io->pi_sel.pc_func; io->pi_data = pci_cfgread(&probe, io->pi_reg, io->pi_width); error = 0; break; default: error = ENODEV; break; } break; case PCIOCWRITE: io = (struct pci_io *)data; switch(io->pi_width) { pcicfgregs probe; case 4: case 2: case 1: probe.hose = -1; probe.bus = io->pi_sel.pc_bus; probe.slot = io->pi_sel.pc_dev; probe.func = io->pi_sel.pc_func; pci_cfgwrite(&probe, io->pi_reg, io->pi_data, io->pi_width); error = 0; break; default: error = ENODEV; break; } break; default: error = ENOTTY; break; } return (error); } #define PCI_CDEV 78 static struct cdevsw pcicdev = { /* open */ pci_open, /* close */ pci_close, /* read */ noread, /* write */ nowrite, /* ioctl */ pci_ioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "pci", /* maj */ PCI_CDEV, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; #include "pci_if.h" /* * A simple driver to wrap the old pci driver mechanism for back-compat. */ static int pci_compat_probe(device_t dev) { struct pci_device *dvp; struct pci_devinfo *dinfo; pcicfgregs *cfg; const char *name; int error; dinfo = device_get_ivars(dev); cfg = &dinfo->cfg; dvp = device_get_driver(dev)->priv; /* * Do the wrapped probe. */ error = ENXIO; if (dvp && dvp->pd_probe) { name = dvp->pd_probe(cfg, (cfg->device << 16) + cfg->vendor); if (name) { device_set_desc_copy(dev, name); /* Allow newbus drivers to match "better" */ error = -1000; } } return error; } static int pci_compat_attach(device_t dev) { struct pci_device *dvp; struct pci_devinfo *dinfo; pcicfgregs *cfg; int unit; dinfo = device_get_ivars(dev); cfg = &dinfo->cfg; dvp = device_get_driver(dev)->priv; unit = device_get_unit(dev); if (unit > *dvp->pd_count) *dvp->pd_count = unit; if (dvp->pd_attach) dvp->pd_attach(cfg, unit); return 0; } static device_method_t pci_compat_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pci_compat_probe), DEVMETHOD(device_attach, pci_compat_attach), { 0, 0 } }; static devclass_t pci_devclass; /* * Create a new style driver around each old pci driver. */ int compat_pci_handler(module_t mod, int type, void *data) { struct pci_device *dvp = (struct pci_device *)data; driver_t *driver; switch (type) { case MOD_LOAD: driver = malloc(sizeof(driver_t), M_DEVBUF, M_NOWAIT); if (!driver) return ENOMEM; bzero(driver, sizeof(driver_t)); driver->name = dvp->pd_name; driver->methods = pci_compat_methods; driver->softc = sizeof(struct pci_devinfo *); driver->priv = dvp; devclass_add_driver(pci_devclass, driver); break; case MOD_UNLOAD: printf("%s: module unload not supported!\n", dvp->pd_name); return EOPNOTSUPP; default: break; } return 0; } /* * New style pci driver. Parent device is either a pci-host-bridge or a * pci-pci-bridge. Both kinds are represented by instances of pcib. */ static void pci_print_verbose(struct pci_devinfo *dinfo) { if (bootverbose) { pcicfgregs *cfg = &dinfo->cfg; printf("found->\tvendor=0x%04x, dev=0x%04x, revid=0x%02x\n", cfg->vendor, cfg->device, cfg->revid); printf("\tclass=%02x-%02x-%02x, hdrtype=0x%02x, mfdev=%d\n", cfg->baseclass, cfg->subclass, cfg->progif, cfg->hdrtype, cfg->mfdev); printf("\tsubordinatebus=%x \tsecondarybus=%x\n", cfg->subordinatebus, cfg->secondarybus); #ifdef PCI_DEBUG printf("\tcmdreg=0x%04x, statreg=0x%04x, cachelnsz=%d (dwords)\n", cfg->cmdreg, cfg->statreg, cfg->cachelnsz); printf("\tlattimer=0x%02x (%d ns), mingnt=0x%02x (%d ns), maxlat=0x%02x (%d ns)\n", cfg->lattimer, cfg->lattimer * 30, cfg->mingnt, cfg->mingnt * 250, cfg->maxlat, cfg->maxlat * 250); #endif /* PCI_DEBUG */ if (cfg->intpin > 0) printf("\tintpin=%c, irq=%d\n", cfg->intpin +'a' -1, cfg->intline); } } static int pci_porten(pcicfgregs *cfg) { return ((cfg->cmdreg & PCIM_CMD_PORTEN) != 0); } static int pci_memen(pcicfgregs *cfg) { return ((cfg->cmdreg & PCIM_CMD_MEMEN) != 0); } /* * Add a resource based on a pci map register. Return 1 if the map * register is a 32bit map register or 2 if it is a 64bit register. */ static int pci_add_map(device_t dev, pcicfgregs* cfg, int reg) { struct pci_devinfo *dinfo = device_get_ivars(dev); struct resource_list *rl = &dinfo->resources; u_int32_t map; u_int64_t base; u_int8_t ln2size; u_int8_t ln2range; u_int32_t testval; int type; map = pci_cfgread(cfg, reg, 4); if (map == 0 || map == 0xffffffff) return 1; /* skip invalid entry */ pci_cfgwrite(cfg, reg, 0xffffffff, 4); testval = pci_cfgread(cfg, reg, 4); pci_cfgwrite(cfg, reg, map, 4); base = pci_mapbase(map); if (pci_maptype(map) & PCI_MAPMEM) type = SYS_RES_MEMORY; else type = SYS_RES_IOPORT; ln2size = pci_mapsize(testval); ln2range = pci_maprange(testval); if (ln2range == 64) { /* Read the other half of a 64bit map register */ base |= (u_int64_t) pci_cfgread(cfg, reg + 4, 4) << 32; } #ifdef __alpha__ /* * XXX: encode hose number in the base addr, * This will go away once the bus_space functions * can deal with multiple hoses */ if(cfg->hose){ if (base & 0x80000000) { printf("base addr = 0x%lx\n", base); printf("hacked addr = 0x%lx\n", base | ((u_int64_t)cfg->hose << 31)); panic("hose encoding hack would clobber base addr"); } if (cfg->hose > 1) panic("only one hose supported!"); base |= ((u_int64_t)cfg->hose << 31); } #endif if (type == SYS_RES_IOPORT && !pci_porten(cfg)) return 1; if (type == SYS_RES_MEMORY && !pci_memen(cfg)) return 1; resource_list_add(rl, type, reg, base, base + (1 << ln2size) - 1, (1 << ln2size)); if (bootverbose) { printf("\tmap[%02x]: type %x, range %2d, base %08x, size %2d\n", reg, pci_maptype(base), ln2range, (unsigned int) base, ln2size); } return (ln2range == 64) ? 2 : 1; } static void pci_add_resources(device_t dev, pcicfgregs* cfg) { struct pci_devinfo *dinfo = device_get_ivars(dev); struct resource_list *rl = &dinfo->resources; struct pci_quirk *q; int i; for (i = 0; i < cfg->nummaps;) { i += pci_add_map(dev, cfg, PCIR_MAPS + i*4); } for (q = &pci_quirks[0]; q->devid; q++) { if (q->devid == ((cfg->device << 16) | cfg->vendor) && q->type == PCI_QUIRK_MAP_REG) pci_add_map(dev, cfg, q->arg1); } if (cfg->intline != 255) resource_list_add(rl, SYS_RES_IRQ, 0, cfg->intline, cfg->intline, 1); } static void pci_add_children(device_t dev, int busno) { pcicfgregs probe; #ifdef SIMOS #undef PCI_SLOTMAX #define PCI_SLOTMAX 0 #endif bzero(&probe, sizeof probe); #ifdef __alpha__ probe.hose = pcib_get_hose(dev); #endif #ifdef __i386__ probe.hose = 0; #endif probe.bus = busno; for (probe.slot = 0; probe.slot <= PCI_SLOTMAX; probe.slot++) { int pcifunchigh = 0; for (probe.func = 0; probe.func <= pcifunchigh; probe.func++) { struct pci_devinfo *dinfo = pci_readcfg(&probe); if (dinfo != NULL) { if (dinfo->cfg.mfdev) pcifunchigh = 7; pci_print_verbose(dinfo); - dinfo->cfg.dev = - device_add_child(dev, NULL, -1, dinfo); + dinfo->cfg.dev = device_add_child(dev, NULL, -1); + device_set_ivars(dinfo->cfg.dev, dinfo); pci_add_resources(dinfo->cfg.dev, &dinfo->cfg); } } } } static int pci_new_probe(device_t dev) { static int once; device_set_desc(dev, "PCI bus"); pci_add_children(dev, device_get_unit(dev)); if (!once) { make_dev(&pcicdev, 0, UID_ROOT, GID_WHEEL, 0644, "pci"); once++; } return 0; } static int pci_print_child(device_t dev, device_t child) { struct pci_devinfo *dinfo; pcicfgregs *cfg; int retval = 0; dinfo = device_get_ivars(child); cfg = &dinfo->cfg; retval += bus_print_child_header(dev, child); if (cfg->intpin > 0 && cfg->intline != 255) retval += printf(" irq %d", cfg->intline); retval += printf(" at device %d.%d", pci_get_slot(child), pci_get_function(child)); retval += bus_print_child_footer(dev, child); return (retval); } static void pci_probe_nomatch(device_t dev, device_t child) { struct pci_devinfo *dinfo; pcicfgregs *cfg; /* a few 'known' devices */ if (pci_get_class(child) == PCIC_SERIALBUS && pci_get_subclass(child) == PCIS_SERIALBUS_USB) { if (pci_get_progif(child) == 0x00 /* UHCI */ ) { device_printf(dev, "UHCI USB controller"); } else if (pci_get_progif(child) == 0x10 /* OHCI */ ) { device_printf(dev, "OHCI USB controller"); } else { device_printf(dev, "USB controller"); } } else { device_printf(dev, "unknown card"); } dinfo = device_get_ivars(child); cfg = &dinfo->cfg; printf(" (vendor=0x%04x, dev=0x%04x) at %d.%d", cfg->vendor, cfg->device, pci_get_slot(child), pci_get_function(child)); if (cfg->intpin > 0 && cfg->intline != 255) { printf(" irq %d", cfg->intline); } printf("\n"); return; } static int pci_read_ivar(device_t dev, device_t child, int which, u_long *result) { struct pci_devinfo *dinfo; pcicfgregs *cfg; dinfo = device_get_ivars(child); cfg = &dinfo->cfg; switch (which) { case PCI_IVAR_SUBVENDOR: *result = cfg->subvendor; break; case PCI_IVAR_SUBDEVICE: *result = cfg->subdevice; break; case PCI_IVAR_VENDOR: *result = cfg->vendor; break; case PCI_IVAR_DEVICE: *result = cfg->device; break; case PCI_IVAR_DEVID: *result = (cfg->device << 16) | cfg->vendor; break; case PCI_IVAR_CLASS: *result = cfg->baseclass; break; case PCI_IVAR_SUBCLASS: *result = cfg->subclass; break; case PCI_IVAR_PROGIF: *result = cfg->progif; break; case PCI_IVAR_REVID: *result = cfg->revid; break; case PCI_IVAR_INTPIN: *result = cfg->intpin; break; case PCI_IVAR_IRQ: *result = cfg->intline; break; case PCI_IVAR_BUS: *result = cfg->bus; break; case PCI_IVAR_SLOT: *result = cfg->slot; break; case PCI_IVAR_FUNCTION: *result = cfg->func; break; case PCI_IVAR_SECONDARYBUS: *result = cfg->secondarybus; break; case PCI_IVAR_SUBORDINATEBUS: *result = cfg->subordinatebus; break; case PCI_IVAR_HOSE: /* * Pass up to parent bridge. */ *result = pcib_get_hose(dev); break; default: return ENOENT; } return 0; } static int pci_write_ivar(device_t dev, device_t child, int which, uintptr_t value) { struct pci_devinfo *dinfo; pcicfgregs *cfg; dinfo = device_get_ivars(child); cfg = &dinfo->cfg; switch (which) { case PCI_IVAR_SUBVENDOR: case PCI_IVAR_SUBDEVICE: case PCI_IVAR_VENDOR: case PCI_IVAR_DEVICE: case PCI_IVAR_DEVID: case PCI_IVAR_CLASS: case PCI_IVAR_SUBCLASS: case PCI_IVAR_PROGIF: case PCI_IVAR_REVID: case PCI_IVAR_INTPIN: case PCI_IVAR_IRQ: case PCI_IVAR_BUS: case PCI_IVAR_SLOT: case PCI_IVAR_FUNCTION: return EINVAL; /* disallow for now */ case PCI_IVAR_SECONDARYBUS: cfg->secondarybus = value; break; case PCI_IVAR_SUBORDINATEBUS: cfg->subordinatebus = value; break; default: return ENOENT; } return 0; } static struct resource * pci_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct pci_devinfo *dinfo = device_get_ivars(child); struct resource_list *rl = &dinfo->resources; return resource_list_alloc(rl, dev, child, type, rid, start, end, count, flags); } static int pci_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { struct pci_devinfo *dinfo = device_get_ivars(child); struct resource_list *rl = &dinfo->resources; return resource_list_release(rl, dev, child, type, rid, r); } static int pci_set_resource(device_t dev, device_t child, int type, int rid, u_long start, u_long count) { struct pci_devinfo *dinfo = device_get_ivars(child); struct resource_list *rl = &dinfo->resources; resource_list_add(rl, type, rid, start, start + count - 1, count); return 0; } static int pci_get_resource(device_t dev, device_t child, int type, int rid, u_long *startp, u_long *countp) { struct pci_devinfo *dinfo = device_get_ivars(child); struct resource_list *rl = &dinfo->resources; struct resource_list_entry *rle; rle = resource_list_find(rl, type, rid); if (!rle) return ENOENT; if (startp) *startp = rle->start; if (countp) *countp = rle->count; return 0; } static void pci_delete_resource(device_t dev, device_t child, int type, int rid) { printf("pci_set_resource: PCI resources can not be deleted\n"); } static u_int32_t pci_read_config_method(device_t dev, device_t child, int reg, int width) { struct pci_devinfo *dinfo = device_get_ivars(child); pcicfgregs *cfg = &dinfo->cfg; return pci_cfgread(cfg, reg, width); } static void pci_write_config_method(device_t dev, device_t child, int reg, u_int32_t val, int width) { struct pci_devinfo *dinfo = device_get_ivars(child); pcicfgregs *cfg = &dinfo->cfg; pci_cfgwrite(cfg, reg, val, width); } static int pci_modevent(module_t mod, int what, void *arg) { switch (what) { case MOD_LOAD: STAILQ_INIT(&pci_devq); break; case MOD_UNLOAD: break; } return 0; } static device_method_t pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pci_new_probe), DEVMETHOD(device_attach, bus_generic_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, pci_print_child), DEVMETHOD(bus_probe_nomatch, pci_probe_nomatch), DEVMETHOD(bus_read_ivar, pci_read_ivar), DEVMETHOD(bus_write_ivar, pci_write_ivar), DEVMETHOD(bus_driver_added, bus_generic_driver_added), DEVMETHOD(bus_alloc_resource, pci_alloc_resource), DEVMETHOD(bus_release_resource, pci_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_set_resource, pci_set_resource), DEVMETHOD(bus_get_resource, pci_get_resource), DEVMETHOD(bus_delete_resource, pci_delete_resource), /* PCI interface */ DEVMETHOD(pci_read_config, pci_read_config_method), DEVMETHOD(pci_write_config, pci_write_config_method), { 0, 0 } }; static driver_t pci_driver = { "pci", pci_methods, 1, /* no softc */ }; DRIVER_MODULE(pci, pcib, pci_driver, pci_devclass, pci_modevent, 0); Index: head/sys/dev/ppbus/lpbb.c =================================================================== --- head/sys/dev/ppbus/lpbb.c (revision 54072) +++ head/sys/dev/ppbus/lpbb.c (revision 54073) @@ -1,309 +1,309 @@ /*- * Copyright (c) 1998 Nicolas Souchu, Marc Bouget * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * */ /* * I2C Bit-Banging over parallel port * * See the Official Philips interface description in lpbb(4) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iicbb_if.h" /* iicbus softc */ struct lpbb_softc { struct ppb_device lpbb_dev; }; static int lpbb_detect(struct lpbb_softc *); static int lpbb_probe(device_t); static int lpbb_attach(device_t); static int lpbb_callback(device_t, int, caddr_t *); static void lpbb_setlines(device_t, int, int); static int lpbb_getdataline(device_t); static int lpbb_reset(device_t, u_char, u_char, u_char *); static devclass_t lpbb_devclass; static device_method_t lpbb_methods[] = { /* device interface */ DEVMETHOD(device_probe, lpbb_probe), DEVMETHOD(device_attach, lpbb_attach), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), /* iicbb interface */ DEVMETHOD(iicbb_callback, lpbb_callback), DEVMETHOD(iicbb_setlines, lpbb_setlines), DEVMETHOD(iicbb_getdataline, lpbb_getdataline), DEVMETHOD(iicbb_reset, lpbb_reset), { 0, 0 } }; static driver_t lpbb_driver = { "lpbb", lpbb_methods, sizeof(struct lpbb_softc), }; /* * Make ourselves visible as a ppbus driver */ static struct ppb_device *lpbb_ppb_probe(struct ppb_data *ppb); static int lpbb_ppb_attach(struct ppb_device *dev); #define MAXLPBB 8 /* XXX not much better! */ static struct lpbb_softc *lpbbdata[MAXLPBB]; static int nlpbb = 0; #ifdef KERNEL static struct ppb_driver lpbbdriver = { lpbb_ppb_probe, lpbb_ppb_attach, "lpbb" }; DATA_SET(ppbdriver_set, lpbbdriver); #endif /* KERNEL */ static int lpbb_probe(device_t dev) { struct lpbb_softc *sc = lpbbdata[device_get_unit(dev)]; struct lpbb_softc *scdst = (struct lpbb_softc *)device_get_softc(dev); /* XXX copy softc. Yet, ppbus device is sc->lpbb_dev, but will be * dev->parent when ppbus will be ported to the new bus architecture */ bcopy(sc, scdst, sizeof(struct lpbb_softc)); device_set_desc(dev, "parallel I2C bit-banging interface"); /* probe done by ppbus initialization */ return (0); } static int lpbb_attach(device_t dev) { device_t bitbang, iicbus; /* add generic bit-banging code */ - bitbang = device_add_child(dev, "iicbb", -1, NULL); + bitbang = device_add_child(dev, "iicbb", -1); /* add the iicbus to the tree */ iicbus = iicbus_alloc_bus(bitbang); device_probe_and_attach(bitbang); /* XXX should be in iicbb_attach! */ device_probe_and_attach(iicbus); return (0); } /* * lppbb_ppb_probe() */ static struct ppb_device * lpbb_ppb_probe(struct ppb_data *ppb) { struct lpbb_softc *sc; sc = (struct lpbb_softc *) malloc(sizeof(struct lpbb_softc), M_TEMP, M_NOWAIT); if (!sc) { printf("lpbb: cannot malloc!\n"); return (0); } bzero(sc, sizeof(struct lpbb_softc)); lpbbdata[nlpbb] = sc; /* * ppbus dependent initialisation. */ sc->lpbb_dev.id_unit = nlpbb; sc->lpbb_dev.name = lpbbdriver.name; sc->lpbb_dev.ppb = ppb; sc->lpbb_dev.intr = 0; if (!lpbb_detect(sc)) { free(sc, M_TEMP); return (NULL); } /* Ok, go to next device on next probe */ nlpbb ++; /* XXX wrong according to new bus architecture. ppbus needs to be * ported */ return (&sc->lpbb_dev); } static int lpbb_ppb_attach(struct ppb_device *dev) { /* add the parallel port I2C interface to the bus tree */ - if (!device_add_child(root_bus, "lpbb", dev->id_unit, NULL)) + if (!device_add_child(root_bus, "lpbb", dev->id_unit)) return (0); return (1); } static int lpbb_callback(device_t dev, int index, caddr_t *data) { struct lpbb_softc *sc = (struct lpbb_softc *)device_get_softc(dev); int error = 0; int how; switch (index) { case IIC_REQUEST_BUS: /* request the ppbus */ how = *(int *)data; error = ppb_request_bus(&sc->lpbb_dev, how); break; case IIC_RELEASE_BUS: /* release the ppbus */ error = ppb_release_bus(&sc->lpbb_dev); break; default: error = EINVAL; } return (error); } #define SDA_out 0x80 #define SCL_out 0x08 #define SDA_in 0x80 #define SCL_in 0x08 #define ALIM 0x20 #define I2CKEY 0x50 static int getSDA(struct lpbb_softc *sc) { if((ppb_rstr(&sc->lpbb_dev)&SDA_in)==SDA_in) return 1; else return 0; } static void setSDA(struct lpbb_softc *sc, char val) { if(val==0) ppb_wdtr(&sc->lpbb_dev, (u_char)SDA_out); else ppb_wdtr(&sc->lpbb_dev, (u_char)~SDA_out); } static void setSCL(struct lpbb_softc *sc, unsigned char val) { if(val==0) ppb_wctr(&sc->lpbb_dev, (u_char)(ppb_rctr(&sc->lpbb_dev)&~SCL_out)); else ppb_wctr(&sc->lpbb_dev, (u_char)(ppb_rctr(&sc->lpbb_dev)|SCL_out)); } static int lpbb_detect(struct lpbb_softc *sc) { if (ppb_request_bus(&sc->lpbb_dev, PPB_DONTWAIT)) { printf("lpbb: can't allocate ppbus\n"); return (0); } /* reset bus */ setSDA(sc, 1); setSCL(sc, 1); if ((ppb_rstr(&sc->lpbb_dev) & I2CKEY) || ((ppb_rstr(&sc->lpbb_dev) & ALIM) != ALIM)) { ppb_release_bus(&sc->lpbb_dev); return (0); } ppb_release_bus(&sc->lpbb_dev); return (1); } static int lpbb_reset(device_t dev, u_char speed, u_char addr, u_char * oldaddr) { struct lpbb_softc *sc = (struct lpbb_softc *)device_get_softc(dev); /* reset bus */ setSDA(sc, 1); setSCL(sc, 1); return (IIC_ENOADDR); } static void lpbb_setlines(device_t dev, int ctrl, int data) { struct lpbb_softc *sc = (struct lpbb_softc *)device_get_softc(dev); setSCL(sc, ctrl); setSDA(sc, data); } static int lpbb_getdataline(device_t dev) { struct lpbb_softc *sc = (struct lpbb_softc *)device_get_softc(dev); return (getSDA(sc)); } DRIVER_MODULE(lpbb, root, lpbb_driver, lpbb_devclass, 0, 0); Index: head/sys/dev/smbus/smbconf.c =================================================================== --- head/sys/dev/smbus/smbconf.c (revision 54072) +++ head/sys/dev/smbus/smbconf.c (revision 54073) @@ -1,210 +1,210 @@ /*- * Copyright (c) 1998 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * */ #include #include #include #include #include #include #include #include #include "smbus_if.h" /* * smbus_intr() */ void smbus_intr(device_t bus, u_char devaddr, char low, char high, int error) { struct smbus_softc *sc = (struct smbus_softc *)device_get_softc(bus); /* call owner's intr routine */ if (sc->owner) SMBUS_INTR(sc->owner, devaddr, low, high, error); return; } /* * smbus_error() * * Converts an smbus error to a unix error. */ int smbus_error(int smb_error) { int error = 0; if (smb_error == SMB_ENOERR) return (0); if (smb_error & (SMB_ENOTSUPP)) { error = ENODEV; } else if (smb_error & (SMB_ENOACK)) { error = ENXIO; } else if (smb_error & (SMB_ETIMEOUT)) { error = EWOULDBLOCK; } else if (smb_error & (SMB_EBUSY)) { error = EBUSY; } else { error = EINVAL; } return (error); } /* * smbus_alloc_bus() * * Allocate a new bus connected to the given parent device */ device_t smbus_alloc_bus(device_t parent) { device_t child; /* add the bus to the parent */ - child = device_add_child(parent, "smbus", -1, NULL); + child = device_add_child(parent, "smbus", -1); return (child); } static int smbus_poll(struct smbus_softc *sc, int how) { int error; switch (how) { case (SMB_WAIT | SMB_INTR): error = tsleep(sc, SMBPRI|PCATCH, "smbreq", 0); break; case (SMB_WAIT | SMB_NOINTR): error = tsleep(sc, SMBPRI, "smbreq", 0); break; default: return (EWOULDBLOCK); break; } return (error); } /* * smbus_request_bus() * * Allocate the device to perform transfers. * * how : SMB_WAIT or SMB_DONTWAIT */ int smbus_request_bus(device_t bus, device_t dev, int how) { struct smbus_softc *sc = (struct smbus_softc *)device_get_softc(bus); int s, error = 0; /* first, ask the underlying layers if the request is ok */ do { error = SMBUS_CALLBACK(device_get_parent(bus), SMB_REQUEST_BUS, (caddr_t)&how); if (error) error = smbus_poll(sc, how); } while (error == EWOULDBLOCK); while (!error) { s = splhigh(); if (sc->owner && sc->owner != dev) { splx(s); error = smbus_poll(sc, how); } else { sc->owner = dev; splx(s); return (0); } /* free any allocated resource */ if (error) SMBUS_CALLBACK(device_get_parent(bus), SMB_RELEASE_BUS, (caddr_t)&how); } return (error); } /* * smbus_release_bus() * * Release the device allocated with smbus_request_dev() */ int smbus_release_bus(device_t bus, device_t dev) { struct smbus_softc *sc = (struct smbus_softc *)device_get_softc(bus); int s, error; /* first, ask the underlying layers if the release is ok */ error = SMBUS_CALLBACK(device_get_parent(bus), SMB_RELEASE_BUS, NULL); if (error) return (error); s = splhigh(); if (sc->owner != dev) { splx(s); return (EACCES); } sc->owner = 0; splx(s); /* wakeup waiting processes */ wakeup(sc); return (0); } /* * smbus_get_addr() * * Get the I2C 7 bits address of the device */ u_char smbus_get_addr(device_t dev) { uintptr_t addr; device_t parent = device_get_parent(dev); BUS_READ_IVAR(parent, dev, SMBUS_IVAR_ADDR, &addr); return ((u_char)addr); } Index: head/sys/dev/smbus/smbus.c =================================================================== --- head/sys/dev/smbus/smbus.c (revision 54072) +++ head/sys/dev/smbus/smbus.c (revision 54073) @@ -1,151 +1,151 @@ /*- * Copyright (c) 1998 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * */ #include #include #include #include #include #include #include /* * Autoconfiguration and support routines for the Philips serial I2C bus */ #define DEVTOSMBUS(dev) ((struct smbus_device*)device_get_ivars(dev)) /* * structure used to attach devices to the I2C bus */ struct smbus_device { const char *smbd_name; /* device name */ const char *smbd_desc; /* device descriptor */ }; /* * list of known devices */ struct smbus_device smbus_children[] = { { "smb", "SMBus general purpose I/O" }, { NULL, 0 } }; static devclass_t smbus_devclass; /* * Device methods */ static int smbus_probe(device_t); static int smbus_attach(device_t); #if 0 static int smbus_read_ivar(device_t , device_t, int, u_long *); #endif static device_method_t smbus_methods[] = { /* device interface */ DEVMETHOD(device_probe, smbus_probe), DEVMETHOD(device_attach, smbus_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, bus_generic_read_ivar), DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), { 0, 0 } }; static driver_t smbus_driver = { "smbus", smbus_methods, sizeof(struct smbus_softc), }; /* * At 'probe' time, we add all the devices which we know about to the * bus. The generic attach routine will probe and attach them if they * are alive. */ static int smbus_probe(device_t dev) { device_set_desc(dev, "System Management Bus"); return (0); } static int smbus_attach(device_t dev) { struct smbus_device *smbdev; /* add known devices */ for (smbdev = smbus_children; smbdev->smbd_name; smbdev++) { device_t child; if (devclass_find(smbdev->smbd_name)) { - child = device_add_child(dev, smbdev->smbd_name, - -1, smbdev); + child = device_add_child(dev, smbdev->smbd_name, -1); + device_set_ivars(child, smbdev); device_set_desc(child, smbdev->smbd_desc); } else if (bootverbose) printf("smbus: %s devclass not found\n", smbdev->smbd_name); } bus_generic_attach(dev); return (0); } void smbus_generic_intr(device_t dev, u_char devaddr, char low, char high) { return; } #if 0 static int smbus_read_ivar(device_t bus, device_t dev, int index, u_long* result) { struct smbus_device* smbdev = DEVTOSMBUS(dev); switch (index) { default: break; } return (ENOENT); } #endif DRIVER_MODULE(smbus, iicsmb, smbus_driver, smbus_devclass, 0, 0); DRIVER_MODULE(smbus, bti2c, smbus_driver, smbus_devclass, 0, 0); DRIVER_MODULE(smbus, intsmb, smbus_driver, smbus_devclass, 0, 0); DRIVER_MODULE(smbus, alsmb, smbus_driver, smbus_devclass, 0, 0); Index: head/sys/dev/sound/isa/gusc.c =================================================================== --- head/sys/dev/sound/isa/gusc.c (revision 54072) +++ head/sys/dev/sound/isa/gusc.c (revision 54073) @@ -1,679 +1,686 @@ /*- * Copyright (c) 1999 Seigo Tanimura * Copyright (c) 1999 Ville-Pertti Keinonen * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "gusc.h" #include "isa.h" #include "pnp.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bus_if.h" #if NISA > 0 #include #include #ifdef __alpha__ /* XXX workaround a stupid warning */ #include #endif #endif /* NISA > 0 */ #if NGUSC > 0 #define LOGICALID_NOPNP 0 #define LOGICALID_PCM 0x0000561e #define LOGICALID_OPL 0x0300561e #define LOGICALID_MIDI 0x0400561e /* Interrupt handler. */ struct gusc_ihandler { void (*intr)(void *); void *arg; }; /* Here is the parameter structure per a device. */ struct gusc_softc { device_t dev; /* device */ int io_rid[3]; /* io port rids */ struct resource *io[3]; /* io port resources */ int io_alloced[3]; /* io port alloc flag */ int irq_rid; /* irq rids */ struct resource *irq; /* irq resources */ int irq_alloced; /* irq alloc flag */ int drq_rid[2]; /* drq rids */ struct resource *drq[2]; /* drq resources */ int drq_alloced[2]; /* drq alloc flag */ /* Interrupts are shared (XXX non-PnP only?) */ struct gusc_ihandler midi_intr; struct gusc_ihandler pcm_intr; }; typedef struct gusc_softc *sc_p; #if NISA > 0 static int gusc_probe(device_t dev); static int gusc_attach(device_t dev); static int gusisa_probe(device_t dev); static void gusc_intr(void *); #endif /* NISA > 0 */ static struct resource *gusc_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); static int gusc_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r); #if notyet static device_t find_masterdev(sc_p scp); #endif /* notyet */ static int alloc_resource(sc_p scp); static int release_resource(sc_p scp); static devclass_t gusc_devclass; #if NISA > 0 static int gusc_probe(device_t dev) { + device_t child; u_int32_t vend_id, logical_id; char *s; struct sndcard_func *func; vend_id = isa_get_vendorid(dev); if (vend_id == 0) return gusisa_probe(dev); #if NPNP > 0 logical_id = isa_get_logicalid(dev); s = NULL; if (vend_id == 0x0100561e) { /* Gravis */ switch (logical_id) { case LOGICALID_PCM: s = "Gravis UltraSound Plug & Play PCM"; func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); if (func == NULL) return (ENOMEM); bzero(func, sizeof(*func)); func->func = SCF_PCM; - device_add_child(dev, "pcm", -1, func); + child = device_add_child(dev, "pcm", -1); + device_set_ivars(child, func); break; #if notyet case LOGICALID_OPL: s = "Gravis UltraSound Plug & Play OPL"; func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); if (func == NULL) return (ENOMEM); bzero(func, sizeof(*func)); func->func = SCF_SYNTH; - device_add_child(dev, "midi", -1, func); + child = device_add_child(dev, "midi", -1); + device_set_ivars(child, func); break; case LOGICALID_MIDI: s = "Gravis UltraSound Plug & Play MIDI"; func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); if (func == NULL) return (ENOMEM); bzero(func, sizeof(*func)); func->func = SCF_MIDI; - device_add_child(dev, "midi", -1, func); + child = device_add_child(dev, "midi", -1); + device_set_ivars(child, func); break; #endif /* notyet */ } } if (s != NULL) { device_set_desc(dev, s); return (0); } #endif /* NPNP > 0 */ return (ENXIO); } static void port_wr(struct resource *r, int i, unsigned char v) { bus_space_write_1(rman_get_bustag(r), rman_get_bushandle(r), i, v); } static int port_rd(struct resource *r, int i) { return bus_space_read_1(rman_get_bustag(r), rman_get_bushandle(r), i); } /* * Probe for an old (non-PnP) GUS card on the ISA bus. */ static int gusisa_probe(device_t dev) { + device_t child; struct resource *res, *res2; int base, rid, rid2, s, flags; unsigned char val; base = isa_get_port(dev); flags = device_get_flags(dev); rid = 1; res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, base + 0x100, base + 0x107, 8, RF_ACTIVE); if (res == NULL) return ENXIO; res2 = NULL; /* * Check for the presence of some GUS card. Reset the card, * then see if we can access the memory on it. */ port_wr(res, 3, 0x4c); port_wr(res, 5, 0); DELAY(30 * 1000); port_wr(res, 3, 0x4c); port_wr(res, 5, 1); DELAY(30 * 1000); s = splhigh(); /* Write to DRAM. */ port_wr(res, 3, 0x43); /* Register select */ port_wr(res, 4, 0); /* Low addr */ port_wr(res, 5, 0); /* Med addr */ port_wr(res, 3, 0x44); /* Register select */ port_wr(res, 4, 0); /* High addr */ port_wr(res, 7, 0x55); /* DRAM */ /* Read from DRAM. */ port_wr(res, 3, 0x43); /* Register select */ port_wr(res, 4, 0); /* Low addr */ port_wr(res, 5, 0); /* Med addr */ port_wr(res, 3, 0x44); /* Register select */ port_wr(res, 4, 0); /* High addr */ val = port_rd(res, 7); /* DRAM */ splx(s); if (val != 0x55) goto fail; rid2 = 0; res2 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid2, base, base, 1, RF_ACTIVE); if (res2 == NULL) goto fail; s = splhigh(); port_wr(res2, 0x0f, 0x20); val = port_rd(res2, 0x0f); splx(s); if (val == 0xff || (val & 0x06) == 0) val = 0; else { val = port_rd(res2, 0x506); /* XXX Out of range. */ if (val == 0xff) val = 0; } bus_release_resource(dev, SYS_RES_IOPORT, rid2, res2); bus_release_resource(dev, SYS_RES_IOPORT, rid, res); if (val >= 10) { struct sndcard_func *func; /* Looks like a GUS MAX. Set the rest of the resources. */ bus_set_resource(dev, SYS_RES_IOPORT, 2, base + 0x10c, 8); if (flags & DV_F_DUAL_DMA) bus_set_resource(dev, SYS_RES_DRQ, 1, flags & DV_F_DRQ_MASK, 1); #if notyet /* We can support the CS4231 and MIDI devices. */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); if (func == NULL) return ENOMEM; bzero(func, sizeof *func); func->func = SCF_MIDI; - device_add_child(dev, "midi", -1, func); + child = device_add_child(dev, "midi", -1); + device_set_ivars(child, func); #endif /* notyet */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); if (func == NULL) printf("xxx: gus pcm not attached, out of memory\n"); else { bzero(func, sizeof *func); func->func = SCF_PCM; - device_add_child(dev, "pcm", -1, func); + child = device_add_child(dev, "pcm", -1); + device_set_ivars(child, func); } device_set_desc(dev, "Gravis UltraSound MAX"); return 0; } else { /* * TODO: Support even older GUS cards. MIDI should work on * all models. */ return ENXIO; } fail: bus_release_resource(dev, SYS_RES_IOPORT, rid, res); return ENXIO; } static int gusc_attach(device_t dev) { sc_p scp; int unit; void *ih; scp = device_get_softc(dev); unit = device_get_unit(dev); bzero(scp, sizeof(*scp)); scp->dev = dev; if (alloc_resource(scp)) { release_resource(scp); return (ENXIO); } bus_setup_intr(dev, scp->irq, INTR_TYPE_TTY, gusc_intr, scp, &ih); bus_generic_attach(dev); return (0); } /* * Handle interrupts on GUS devices until there aren't any left. */ static void gusc_intr(void *arg) { sc_p scp = (sc_p)arg; int did_something; do { did_something = 0; if (scp->pcm_intr.intr != NULL && (port_rd(scp->io[2], 2) & 1)) { (*scp->pcm_intr.intr)(scp->pcm_intr.arg); did_something = 1; } #if notyet if (scp->midi_intr.intr != NULL && (port_rd(scp->io[1], 0) & 0x80)) { (*scp->midi_intr.intr)(scp->midi_intr.arg); did_something = 1; } #endif /* notyet */ } while (did_something != 0); } #endif /* NISA > 0 */ static struct resource * gusc_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { sc_p scp; int *alloced, rid_max, alloced_max; struct resource **res; scp = device_get_softc(bus); switch (type) { case SYS_RES_IOPORT: alloced = scp->io_alloced; res = scp->io; rid_max = 2; alloced_max = 2; /* pcm + midi (more to include synth) */ break; case SYS_RES_IRQ: alloced = &scp->irq_alloced; res = &scp->irq; rid_max = 0; alloced_max = 2; /* pcm and midi share the single irq. */ break; case SYS_RES_DRQ: alloced = scp->drq_alloced; res = scp->drq; rid_max = 1; alloced_max = 1; break; default: return (NULL); } if (*rid > rid_max || alloced[*rid] == alloced_max) return (NULL); alloced[*rid]++; return (res[*rid]); } static int gusc_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { sc_p scp; int *alloced, rid_max; scp = device_get_softc(bus); switch (type) { case SYS_RES_IOPORT: alloced = scp->io_alloced; rid_max = 2; break; case SYS_RES_IRQ: alloced = &scp->irq_alloced; rid_max = 0; break; case SYS_RES_DRQ: alloced = scp->drq_alloced; rid_max = 1; break; default: return (1); } if (rid > rid_max || alloced[rid] == 0) return (1); alloced[rid]--; return (0); } static int gusc_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep) { sc_p scp = (sc_p)device_get_softc(dev); devclass_t devclass; devclass = device_get_devclass(child); if (strcmp(devclass_get_name(devclass), "midi") == 0) { scp->midi_intr.intr = intr; scp->midi_intr.arg = arg; return 0; } else if (strcmp(devclass_get_name(devclass), "pcm") == 0) { scp->pcm_intr.intr = intr; scp->pcm_intr.arg = arg; return 0; } return bus_generic_setup_intr(dev, child, irq, flags, intr, arg, cookiep); } #if notyet static device_t find_masterdev(sc_p scp) { int i, units; devclass_t devclass; device_t dev; devclass = device_get_devclass(scp->dev); units = devclass_get_maxunit(devclass); dev = NULL; for (i = 0 ; i < units ; i++) { dev = devclass_get_device(devclass, i); if (isa_get_vendorid(dev) == isa_get_vendorid(scp->dev) && isa_get_logicalid(dev) == LOGICALID_PCM && isa_get_serial(dev) == isa_get_serial(scp->dev)) break; } if (i == units) return (NULL); return (dev); } #endif /* notyet */ static int io_range[3] = {0x10, 0x8 , 0x4 }; static int io_offset[3] = {0x0 , 0x100, 0x10c}; static int alloc_resource(sc_p scp) { int i, base, lid, flags; #if notyet device_t dev; #endif /* notyet */ flags = 0; if (isa_get_vendorid(scp->dev)) lid = isa_get_logicalid(scp->dev); else { lid = LOGICALID_NOPNP; flags = device_get_flags(scp->dev); } switch(lid) { case LOGICALID_PCM: case LOGICALID_NOPNP: /* XXX Non-PnP */ if (lid == LOGICALID_NOPNP) base = isa_get_port(scp->dev); else base = 0; for (i = 0 ; i < sizeof(scp->io) / sizeof(*scp->io) ; i++) { if (scp->io[i] == NULL) { scp->io_rid[i] = i; if (base == 0) scp->io[i] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[i], 0, ~0, io_range[i], RF_ACTIVE); else scp->io[i] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[i], base + io_offset[i], base + io_offset[i] + io_range[i] - 1 , io_range[i], RF_ACTIVE); if (scp->io[i] == NULL) return (1); scp->io_alloced[i] = 0; } } if (scp->irq == NULL) { scp->irq_rid = 0; scp->irq = bus_alloc_resource(scp->dev, SYS_RES_IRQ, &scp->irq_rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); if (scp->irq == NULL) return (1); scp->irq_alloced = 0; } for (i = 0 ; i < sizeof(scp->drq) / sizeof(*scp->drq) ; i++) { if (scp->drq[i] == NULL) { scp->drq_rid[i] = i; if (base == 0 || i == 0) scp->drq[i] = bus_alloc_resource(scp->dev, SYS_RES_DRQ, &scp->drq_rid[i], 0, ~0, 1, RF_ACTIVE); else if ((flags & DV_F_DUAL_DMA) != 0) /* XXX The secondary drq is specified in the flag. */ scp->drq[i] = bus_alloc_resource(scp->dev, SYS_RES_DRQ, &scp->drq_rid[i], flags & DV_F_DRQ_MASK, flags & DV_F_DRQ_MASK, 1, RF_ACTIVE); if (scp->drq[i] == NULL) return (1); scp->drq_alloced[i] = 0; } } break; #if notyet case LOGICALID_OPL: if (scp->io[0] == NULL) { scp->io_rid[0] = 0; scp->io[0] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[0], 0, ~0, io_range[0], RF_ACTIVE); if (scp->io[0] == NULL) return (1); scp->io_alloced[0] = 0; } break; case LOGICALID_MIDI: if (scp->io[0] == NULL) { scp->io_rid[0] = 0; scp->io[0] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[0], 0, ~0, io_range[0], RF_ACTIVE); if (scp->io[0] == NULL) return (1); scp->io_alloced[0] = 0; } if (scp->irq == NULL) { /* The irq is shared with pcm audio. */ dev = find_masterdev(scp); if (dev == NULL) return (1); scp->irq_rid = 0; scp->irq = BUS_ALLOC_RESOURCE(dev, NULL, SYS_RES_IRQ, &scp->irq_rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); if (scp->irq == NULL) return (1); scp->irq_alloced = 0; } break; #endif /* notyet */ } return (0); } static int release_resource(sc_p scp) { int i, lid, flags; #if notyet device_t dev; #endif /* notyet */ flags = 0; if (isa_get_vendorid(scp->dev)) lid = isa_get_logicalid(scp->dev); else { lid = LOGICALID_NOPNP; flags = device_get_flags(scp->dev); } switch(lid) { case LOGICALID_PCM: case LOGICALID_NOPNP: /* XXX Non-PnP */ for (i = 0 ; i < sizeof(scp->io) / sizeof(*scp->io) ; i++) { if (scp->io[i] != NULL) { bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[i], scp->io[i]); scp->io[i] = NULL; } } if (scp->irq != NULL) { bus_release_resource(scp->dev, SYS_RES_IRQ, scp->irq_rid, scp->irq); scp->irq = NULL; } for (i = 0 ; i < sizeof(scp->drq) / sizeof(*scp->drq) ; i++) { if (scp->drq[i] != NULL) { bus_release_resource(scp->dev, SYS_RES_DRQ, scp->drq_rid[i], scp->drq[i]); scp->drq[i] = NULL; } } break; #if notyet case LOGICALID_OPL: if (scp->io[0] != NULL) { bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[0], scp->io[0]); scp->io[0] = NULL; } break; case LOGICALID_MIDI: if (scp->io[0] != NULL) { bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[0], scp->io[0]); scp->io[0] = NULL; } if (scp->irq != NULL) { /* The irq is shared with pcm audio. */ dev = find_masterdev(scp); if (dev == NULL) return (1); BUS_RELEASE_RESOURCE(dev, NULL, SYS_RES_IOPORT, scp->irq_rid, scp->irq); scp->irq = NULL; } break; #endif /* notyet */ } return (0); } #if NISA > 0 static device_method_t gusc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, gusc_probe), DEVMETHOD(device_attach, gusc_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_alloc_resource, gusc_alloc_resource), DEVMETHOD(bus_release_resource, gusc_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, gusc_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t gusc_driver = { "gusc", gusc_methods, sizeof(struct gusc_softc), }; /* * gusc can be attached to an isa bus. */ DRIVER_MODULE(gusc, isa, gusc_driver, gusc_devclass, 0, 0); #endif /* NISA > 0 */ #endif /* NGUSC > 0 */ Index: head/sys/dev/sound/isa/sbc.c =================================================================== --- head/sys/dev/sound/isa/sbc.c (revision 54072) +++ head/sys/dev/sound/isa/sbc.c (revision 54073) @@ -1,395 +1,399 @@ /*- * Copyright (c) 1999 Seigo Tanimura * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "sbc.h" #include "isa.h" #include "pnp.h" #include #include #include #include #include #include #include #include #include #include #include #if NISA > 0 #include #include #ifdef __alpha__ /* XXX workaround a stupid warning */ #include #endif #endif /* NISA > 0 */ #if NSBC > 0 /* Here is the parameter structure per a device. */ struct sbc_softc { device_t dev; /* device */ int io_rid[3]; /* io port rids */ struct resource *io[3]; /* io port resources */ int io_alloced[3]; /* io port alloc flag */ int irq_rid; /* irq rids */ struct resource *irq; /* irq resources */ int irq_alloced; /* irq alloc flag */ int drq_rid[2]; /* drq rids */ struct resource *drq[2]; /* drq resources */ int drq_alloced[2]; /* drq alloc flag */ }; typedef struct sbc_softc *sc_p; #if NISA > 0 && NPNP > 0 static int sbc_probe(device_t dev); static int sbc_attach(device_t dev); #endif /* NISA > 0 && NPNP > 0 */ static struct resource *sbc_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); static int sbc_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r); static int alloc_resource(sc_p scp); static int release_resource(sc_p scp); static devclass_t sbc_devclass; #if NISA > 0 && NPNP > 0 static int sbc_probe(device_t dev) { + device_t child; u_int32_t vend_id, logical_id, vend_id2; char *s; struct sndcard_func *func; vend_id = isa_get_vendorid(dev); vend_id2 = vend_id & 0xff00ffff; logical_id = isa_get_logicalid(dev); s = NULL; switch (logical_id) { #if notdef case 0x0000630e: /* Crystal Semiconductor */ if (vend_id2 ==0x3600630e) /* CS4236 */ s = "CS4236"; else if (vend_id2 ==0x3200630e) /* CS4232 */ s = "CS4232"; else if (vend_id2 ==0x3500630e) /* CS4236B */ s = "CS4236B"; break; #endif /* notdef */ case 0x01008c0e: /* Creative ViBRA16C */ if (vend_id2 == 0x70008c0e) s = "Creative ViBRA16C PnP"; break; case 0x43008c0e: /* Creative ViBRA16X */ if (vend_id2 == 0xf0008c0e) s = "Creative ViBRA16C PnP"; break; case 0x31008c0e: /* Creative SB */ if (vend_id2 == 0x26008c0e) s = "Creative SB16 PnP"; else if (vend_id2 == 0x42008c0e) s = "Creative SB32 (CTL0042)"; else if (vend_id2 == 0x44008c0e) s = "Creative SB32 (CTL0044)"; else if (vend_id2 == 0x48008c0e) s = "Creative SB32 (CTL0048)"; else if (vend_id2 == 0x49008c0e) s = "Creative SB32 (CTL0049)"; else if (vend_id2 == 0xf1008c0e) s = "Creative SB32 (CTL00f1)"; break; case 0x42008c0e: /* Creative SB AWE64 (CTL00c1) */ if (vend_id2 == 0xc1008c0e) s = "Creative SB AWE64 (CTL00c1)"; break; case 0x45008c0e: /* Creative SB AWE64 (CTL0045) */ if (vend_id2 == 0xe4008c0e) s = "Creative SB AWE64 (CTL0045)"; break; #if notdef case 0x01200001: /* Avance Logic */ if (vend_id2 == 0x20009305) s = "Avance Logic ALS120"; break; case 0x01100001: /* Avance Asound */ if (vend_id2 == 0x10009305) s = "Avance Asound 110"; break; case 0x68187316: /* ESS1868 */ if (vend_id2 == 0x68007316) s = "ESS ES1868 Plug and Play AudioDrive"; break; case 0x79187316: /* ESS1879 */ if (vend_id2 == 0x79007316) s = "ESS ES1879 Plug and Play AudioDrive"; break; case 0x2100a865: /* Yamaha */ if (vend_id2 == 0x2000a865) s = "Yamaha OPL3-SA2/SAX Sound Board"; break; case 0x80719304: /* Terratec */ if (vend_id2 == 0x1114b250) s = "Terratec Soundsystem Base 1"; break; case 0x0300561e: /* Gravis */ if (vend_id2 == 0x0100561e) s = "Gravis UltraSound Plug & Play"; break; #endif /* notdef */ } if (s != NULL) { device_set_desc(dev, s); /* PCM Audio */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); if (func == NULL) return (ENOMEM); bzero(func, sizeof(*func)); func->func = SCF_PCM; - device_add_child(dev, "pcm", -1, func); + child = device_add_child(dev, "pcm", -1); + device_set_ivars(child, func); #if notyet /* Midi Interface */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); if (func == NULL) return (ENOMEM); bzero(func, sizeof(*func)); func->func = SCF_MIDI; - device_add_child(dev, "midi", -1, func); + child = device_add_child(dev, "midi", -1); + device_set_ivars(child, func); /* OPL FM Synthesizer */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); if (func == NULL) return (ENOMEM); bzero(func, sizeof(*func)); func->func = SCF_SYNTH; - device_add_child(dev, "midi", -1, func); + child = device_add_child(dev, "midi", -1); + device_set_ivars(child, func); #endif /* notyet */ return (0); } return (ENXIO); } static int sbc_attach(device_t dev) { sc_p scp; int unit; scp = device_get_softc(dev); unit = device_get_unit(dev); bzero(scp, sizeof(*scp)); scp->dev = dev; if (alloc_resource(scp)) { release_resource(scp); return (ENXIO); } bus_generic_attach(dev); return (0); } #endif /* NISA > 0 && NPNP > 0 */ static struct resource * sbc_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { sc_p scp; int *alloced, rid_max, alloced_max; struct resource **res; scp = device_get_softc(bus); switch (type) { case SYS_RES_IOPORT: alloced = scp->io_alloced; res = scp->io; rid_max = 2; alloced_max = 1; break; case SYS_RES_IRQ: alloced = &scp->irq_alloced; res = &scp->irq; rid_max = 0; alloced_max = 2; /* pcm and mpu may share the irq. */ break; case SYS_RES_DRQ: alloced = scp->drq_alloced; res = scp->drq; rid_max = 1; alloced_max = 1; break; default: return (NULL); } if (*rid > rid_max || alloced[*rid] == alloced_max) return (NULL); alloced[*rid]++; return (res[*rid]); } static int sbc_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { sc_p scp; int *alloced, rid_max; scp = device_get_softc(bus); switch (type) { case SYS_RES_IOPORT: alloced = scp->io_alloced; rid_max = 2; break; case SYS_RES_IRQ: alloced = &scp->irq_alloced; rid_max = 0; break; case SYS_RES_DRQ: alloced = scp->drq_alloced; rid_max = 1; break; default: return (1); } if (rid > rid_max || alloced[rid] == 0) return (1); alloced[rid]--; return (0); } static int io_range[3] = {0x10, 0x4, 0x4}; static int alloc_resource(sc_p scp) { int i; for (i = 0 ; i < sizeof(scp->io) / sizeof(*scp->io) ; i++) { if (scp->io[i] == NULL) { scp->io_rid[i] = i; scp->io[i] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[i], 0, ~0, io_range[i], RF_ACTIVE); if (scp->io[i] == NULL) return (1); scp->io_alloced[i] = 0; } } if (scp->irq == NULL) { scp->irq_rid = 0; scp->irq = bus_alloc_resource(scp->dev, SYS_RES_IRQ, &scp->irq_rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); if (scp->irq == NULL) return (1); scp->irq_alloced = 0; } for (i = 0 ; i < sizeof(scp->drq) / sizeof(*scp->drq) ; i++) { if (scp->drq[i] == NULL) { scp->drq_rid[i] = i; scp->drq[i] = bus_alloc_resource(scp->dev, SYS_RES_DRQ, &scp->drq_rid[i], 0, ~0, 1, RF_ACTIVE); if (scp->drq[i] == NULL) return (1); scp->drq_alloced[i] = 0; } } return (0); } static int release_resource(sc_p scp) { int i; for (i = 0 ; i < sizeof(scp->io) / sizeof(*scp->io) ; i++) { if (scp->io[i] != NULL) { bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[i], scp->io[i]); scp->io[i] = NULL; } } if (scp->irq != NULL) { bus_release_resource(scp->dev, SYS_RES_IRQ, scp->irq_rid, scp->irq); scp->irq = NULL; } for (i = 0 ; i < sizeof(scp->drq) / sizeof(*scp->drq) ; i++) { if (scp->drq[i] != NULL) { bus_release_resource(scp->dev, SYS_RES_DRQ, scp->drq_rid[i], scp->drq[i]); scp->drq[i] = NULL; } } return (0); } #if NISA > 0 && NPNP > 0 static device_method_t sbc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, sbc_probe), DEVMETHOD(device_attach, sbc_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_alloc_resource, sbc_alloc_resource), DEVMETHOD(bus_release_resource, sbc_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t sbc_driver = { "sbc", sbc_methods, sizeof(struct sbc_softc), }; /* * sbc can be attached to an isa bus. */ DRIVER_MODULE(sbc, isa, sbc_driver, sbc_devclass, 0, 0); #endif /* NISA > 0 && NPNP > 0 */ #endif /* NSBC > 0 */ Index: head/sys/dev/sound/pci/csa.c =================================================================== --- head/sys/dev/sound/pci/csa.c (revision 54072) +++ head/sys/dev/sound/pci/csa.c (revision 54073) @@ -1,793 +1,796 @@ /*- * Copyright (c) 1999 Seigo Tanimura * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "csa.h" #include "pci.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if NPCI > 0 #include #include #endif /* NPCI > 0 */ #if NCSA > 0 #include /* Here is the parameter structure per a device. */ struct csa_softc { device_t dev; /* device */ csa_res res; /* resources */ device_t pcm; /* pcm device */ driver_intr_t* pcmintr; /* pcm intr */ void *pcmintr_arg; /* pcm intr arg */ #if notyet device_t midi; /* midi device */ driver_intr_t* midiintr; /* midi intr */ void *midiintr_arg; /* midi intr arg */ #endif /* notyet */ void *ih; /* cookie */ }; typedef struct csa_softc *sc_p; #if NPCI > 0 static int csa_probe(device_t dev); static int csa_attach(device_t dev); #endif /* NPCI > 0 */ static struct resource *csa_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); static int csa_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r); static int csa_initialize(sc_p scp); static void csa_clearserialfifos(csa_res *resp); static void csa_resetdsp(csa_res *resp); static int csa_downloadimage(csa_res *resp); static int csa_transferimage(csa_res *resp, u_long *src, u_long dest, u_long len); static devclass_t csa_devclass; #if NPCI > 0 static int csa_probe(device_t dev) { + device_t child; char *s; struct sndcard_func *func; s = NULL; switch (pci_get_devid(dev)) { case CS4610_PCI_ID: s = "Crystal Semiconductor CS4610/4611 Audio accelerator"; break; case CS4614_PCI_ID: s = "Crystal Semiconductor CS4614/4622/4624 Audio accelerator/4280 Audio controller"; break; case CS4615_PCI_ID: s = "Crystal Semiconductor CS4615 Audio accelerator"; break; case CS4281_PCI_ID: s = "Crystal Semiconductor CS4281 Audio controller"; break; } if (s != NULL) { device_set_desc(dev, s); /* PCM Audio */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); if (func == NULL) return (ENOMEM); bzero(func, sizeof(*func)); func->func = SCF_PCM; - device_add_child(dev, "pcm", -1, func); + child = device_add_child(dev, "pcm", -1); + device_set_ivars(child, func); #if notyet /* Midi Interface */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); if (func == NULL) return (ENOMEM); bzero(func, sizeof(*func)); func->func = SCF_MIDI; - device_add_child(dev, "midi", -1, func); + child = device_add_child(dev, "midi", -1); + device_set_ivars(child, func); #endif /* notyet */ return (0); } return (ENXIO); } static int csa_attach(device_t dev) { u_int32_t stcmd; sc_p scp; csa_res *resp; scp = device_get_softc(dev); /* Fill in the softc. */ bzero(scp, sizeof(*scp)); scp->dev = dev; /* Wake up the device. */ stcmd = pci_read_config(dev, PCIR_COMMAND, 4); if ((stcmd & PCIM_CMD_MEMEN) == 0 || (stcmd & PCIM_CMD_BUSMASTEREN) == 0) { stcmd |= (PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); pci_write_config(dev, PCIR_COMMAND, 4, stcmd); } stcmd = pci_read_config(dev, PCIR_LATTIMER, 4); if (stcmd < 32) stcmd = 32; pci_write_config(dev, PCIR_LATTIMER, 4, stcmd); /* Allocate the resources. */ resp = &scp->res; resp->io_rid = CS461x_IO_OFFSET; resp->io = bus_alloc_resource(dev, SYS_RES_MEMORY, &resp->io_rid, 0, ~0, CS461x_IO_SIZE, RF_ACTIVE); if (resp->io == NULL) return (ENXIO); resp->mem_rid = CS461x_MEM_OFFSET; resp->mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &resp->mem_rid, 0, ~0, CS461x_MEM_SIZE, RF_ACTIVE); if (resp->mem == NULL) { bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io); return (ENXIO); } resp->irq_rid = 0; resp->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &resp->irq_rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); if (resp->irq == NULL) { bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io); bus_release_resource(dev, SYS_RES_MEMORY, resp->mem_rid, resp->mem); return (ENXIO); } /* Initialize the chip. */ if (csa_initialize(scp)) { bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io); bus_release_resource(dev, SYS_RES_MEMORY, resp->mem_rid, resp->mem); bus_release_resource(dev, SYS_RES_IRQ, resp->irq_rid, resp->irq); return (ENXIO); } /* Reset the Processor. */ csa_resetdsp(resp); /* Download the Processor Image to the processor. */ if (csa_downloadimage(resp)) { bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io); bus_release_resource(dev, SYS_RES_MEMORY, resp->mem_rid, resp->mem); bus_release_resource(dev, SYS_RES_IRQ, resp->irq_rid, resp->irq); return (ENXIO); } bus_generic_attach(dev); return (0); } #endif /* NPCI > 0 */ static struct resource * csa_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { sc_p scp; csa_res *resp; struct resource *res; scp = device_get_softc(bus); resp = &scp->res; switch (type) { case SYS_RES_IRQ: if (*rid != 0) return (NULL); res = resp->irq; break; case SYS_RES_MEMORY: switch (*rid) { case CS461x_IO_OFFSET: res = resp->io; break; case CS461x_MEM_OFFSET: res = resp->mem; break; default: return (NULL); } break; default: return (NULL); } return res; } static int csa_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { return (0); } static int csa_initialize(sc_p scp) { int i; u_int32_t acsts, acisv; csa_res *resp; resp = &scp->res; /* * First, blast the clock control register to zero so that the PLL starts * out in a known state, and blast the master serial port control register * to zero so that the serial ports also start out in a known state. */ csa_writeio(resp, BA0_CLKCR1, 0); csa_writeio(resp, BA0_SERMC1, 0); /* * If we are in AC97 mode, then we must set the part to a host controlled * AC-link. Otherwise, we won't be able to bring up the link. */ #if 1 csa_writeio(resp, BA0_SERACC, SERACC_HSP | SERACC_CODEC_TYPE_1_03); /* 1.03 codec */ #else csa_writeio(resp, BA0_SERACC, SERACC_HSP | SERACC_CODEC_TYPE_2_0); /* 2.0 codec */ #endif /* 1 */ /* * Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97 * spec) and then drive it high. This is done for non AC97 modes since * there might be logic external to the CS461x that uses the ARST# line * for a reset. */ csa_writeio(resp, BA0_ACCTL, 0); DELAY(250); csa_writeio(resp, BA0_ACCTL, ACCTL_RSTN); /* * The first thing we do here is to enable sync generation. As soon * as we start receiving bit clock, we'll start producing the SYNC * signal. */ csa_writeio(resp, BA0_ACCTL, ACCTL_ESYN | ACCTL_RSTN); /* * Now wait for a short while to allow the AC97 part to start * generating bit clock (so we don't try to start the PLL without an * input clock). */ DELAY(50000); /* * Set the serial port timing configuration, so that * the clock control circuit gets its clock from the correct place. */ csa_writeio(resp, BA0_SERMC1, SERMC1_PTC_AC97); /* * Write the selected clock control setup to the hardware. Do not turn on * SWCE yet (if requested), so that the devices clocked by the output of * PLL are not clocked until the PLL is stable. */ csa_writeio(resp, BA0_PLLCC, PLLCC_LPF_1050_2780_KHZ | PLLCC_CDR_73_104_MHZ); csa_writeio(resp, BA0_PLLM, 0x3a); csa_writeio(resp, BA0_CLKCR2, CLKCR2_PDIVS_8); /* * Power up the PLL. */ csa_writeio(resp, BA0_CLKCR1, CLKCR1_PLLP); /* * Wait until the PLL has stabilized. */ DELAY(50000); /* * Turn on clocking of the core so that we can setup the serial ports. */ csa_writeio(resp, BA0_CLKCR1, csa_readio(resp, BA0_CLKCR1) | CLKCR1_SWCE); /* * Fill the serial port FIFOs with silence. */ csa_clearserialfifos(resp); /* * Set the serial port FIFO pointer to the first sample in the FIFO. */ #if notdef csa_writeio(resp, BA0_SERBSP, 0); #endif /* notdef */ /* * Write the serial port configuration to the part. The master * enable bit is not set until all other values have been written. */ csa_writeio(resp, BA0_SERC1, SERC1_SO1F_AC97 | SERC1_SO1EN); csa_writeio(resp, BA0_SERC2, SERC2_SI1F_AC97 | SERC1_SO1EN); csa_writeio(resp, BA0_SERMC1, SERMC1_PTC_AC97 | SERMC1_MSPE); /* * Wait for the codec ready signal from the AC97 codec. */ acsts = 0; for (i = 0 ; i < 1000 ; i++) { /* * First, lets wait a short while to let things settle out a bit, * and to prevent retrying the read too quickly. */ DELAY(250); /* * Read the AC97 status register to see if we've seen a CODEC READY * signal from the AC97 codec. */ acsts = csa_readio(resp, BA0_ACSTS); if ((acsts & ACSTS_CRDY) != 0) break; } /* * Make sure we sampled CODEC READY. */ if ((acsts & ACSTS_CRDY) == 0) return (ENXIO); /* * Assert the vaid frame signal so that we can start sending commands * to the AC97 codec. */ csa_writeio(resp, BA0_ACCTL, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN); /* * Wait until we've sampled input slots 3 and 4 as valid, meaning that * the codec is pumping ADC data across the AC-link. */ acisv = 0; for (i = 0 ; i < 1000 ; i++) { /* * First, lets wait a short while to let things settle out a bit, * and to prevent retrying the read too quickly. */ #if notdef DELAY(10000000L); /* clw */ #else DELAY(2500); #endif /* notdef */ /* * Read the input slot valid register and see if input slots 3 and * 4 are valid yet. */ acisv = csa_readio(resp, BA0_ACISV); if ((acisv & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4)) break; } /* * Make sure we sampled valid input slots 3 and 4. If not, then return * an error. */ if ((acisv & (ACISV_ISV3 | ACISV_ISV4)) != (ACISV_ISV3 | ACISV_ISV4)) return (ENXIO); /* * Now, assert valid frame and the slot 3 and 4 valid bits. This will * commense the transfer of digital audio data to the AC97 codec. */ csa_writeio(resp, BA0_ACOSV, ACOSV_SLV3 | ACOSV_SLV4); /* * Power down the DAC and ADC. We will power them up (if) when we need * them. */ #if notdef csa_writeio(resp, BA0_AC97_POWERDOWN, 0x300); #endif /* notdef */ /* * Turn off the Processor by turning off the software clock enable flag in * the clock control register. */ #if notdef clkcr1 = csa_readio(resp, BA0_CLKCR1) & ~CLKCR1_SWCE; csa_writeio(resp, BA0_CLKCR1, clkcr1); #endif /* notdef */ /* * Enable interrupts on the part. */ #if notdef csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM); #endif /* notdef */ return (0); } static void csa_clearserialfifos(csa_res *resp) { int i, j, pwr; u_int8_t clkcr1, serbst; /* * See if the devices are powered down. If so, we must power them up first * or they will not respond. */ pwr = 1; clkcr1 = csa_readio(resp, BA0_CLKCR1); if ((clkcr1 & CLKCR1_SWCE) == 0) { csa_writeio(resp, BA0_CLKCR1, clkcr1 | CLKCR1_SWCE); pwr = 0; } /* * We want to clear out the serial port FIFOs so we don't end up playing * whatever random garbage happens to be in them. We fill the sample FIFOs * with zero (silence). */ csa_writeio(resp, BA0_SERBWP, 0); /* Fill all 256 sample FIFO locations. */ serbst = 0; for (i = 0 ; i < 256 ; i++) { /* Make sure the previous FIFO write operation has completed. */ for (j = 0 ; j < 5 ; j++) { DELAY(250); serbst = csa_readio(resp, BA0_SERBST); if ((serbst & SERBST_WBSY) == 0) break; } if ((serbst & SERBST_WBSY) != 0) { if (!pwr) csa_writeio(resp, BA0_CLKCR1, clkcr1); } /* Write the serial port FIFO index. */ csa_writeio(resp, BA0_SERBAD, i); /* Tell the serial port to load the new value into the FIFO location. */ csa_writeio(resp, BA0_SERBCM, SERBCM_WRC); } /* * Now, if we powered up the devices, then power them back down again. * This is kinda ugly, but should never happen. */ if (!pwr) csa_writeio(resp, BA0_CLKCR1, clkcr1); } static void csa_resetdsp(csa_res *resp) { int i; /* * Write the reset bit of the SP control register. */ csa_writemem(resp, BA1_SPCR, SPCR_RSTSP); /* * Write the control register. */ csa_writemem(resp, BA1_SPCR, SPCR_DRQEN); /* * Clear the trap registers. */ for (i = 0 ; i < 8 ; i++) { csa_writemem(resp, BA1_DREG, DREG_REGID_TRAP_SELECT + i); csa_writemem(resp, BA1_TWPR, 0xffff); } csa_writemem(resp, BA1_DREG, 0); /* * Set the frame timer to reflect the number of cycles per frame. */ csa_writemem(resp, BA1_FRMT, 0xadf); } static int csa_downloadimage(csa_res *resp) { int ret; u_long ul, offset; for (ul = 0, offset = 0 ; ul < INKY_MEMORY_COUNT ; ul++) { /* * DMA this block from host memory to the appropriate * memory on the CSDevice. */ ret = csa_transferimage( resp, BA1Struct.BA1Array + offset, BA1Struct.MemoryStat[ul].ulDestByteOffset, BA1Struct.MemoryStat[ul].ulSourceByteSize); if (ret) return (ret); offset += BA1Struct.MemoryStat[ul].ulSourceByteSize >> 2; } return (0); } static int csa_transferimage(csa_res *resp, u_long *src, u_long dest, u_long len) { u_long ul; /* * We do not allow DMAs from host memory to host memory (although the DMA * can do it) and we do not allow DMAs which are not a multiple of 4 bytes * in size (because that DMA can not do that). Return an error if either * of these conditions exist. */ if ((len & 0x3) != 0) return (EINVAL); /* Check the destination address that it is a multiple of 4 */ if ((dest & 0x3) != 0) return (EINVAL); /* Write the buffer out. */ for (ul = 0 ; ul < len ; ul += 4) csa_writemem(resp, dest + ul, src[ul >> 2]); return (0); } int csa_readcodec(csa_res *resp, u_long offset, u_int32_t *data) { int i; u_int32_t acsda, acctl, acsts; /* * Make sure that there is not data sitting around from a previous * uncompleted access. ACSDA = Status Data Register = 47Ch */ acsda = csa_readio(resp, BA0_ACSDA); /* * Setup the AC97 control registers on the CS461x to send the * appropriate command to the AC97 to perform the read. * ACCAD = Command Address Register = 46Ch * ACCDA = Command Data Register = 470h * ACCTL = Control Register = 460h * set DCV - will clear when process completed * set CRW - Read command * set VFRM - valid frame enabled * set ESYN - ASYNC generation enabled * set RSTN - ARST# inactive, AC97 codec not reset */ /* * Get the actual AC97 register from the offset */ csa_writeio(resp, BA0_ACCAD, offset - BA0_AC97_RESET); csa_writeio(resp, BA0_ACCDA, 0); csa_writeio(resp, BA0_ACCTL, ACCTL_DCV | ACCTL_CRW | ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN); /* * Wait for the read to occur. */ acctl = 0; for (i = 0 ; i < 10 ; i++) { /* * First, we want to wait for a short time. */ DELAY(25); /* * Now, check to see if the read has completed. * ACCTL = 460h, DCV should be reset by now and 460h = 17h */ acctl = csa_readio(resp, BA0_ACCTL); if ((acctl & ACCTL_DCV) == 0) break; } /* * Make sure the read completed. */ if ((acctl & ACCTL_DCV) != 0) return (EAGAIN); /* * Wait for the valid status bit to go active. */ acsts = 0; for (i = 0 ; i < 10 ; i++) { /* * Read the AC97 status register. * ACSTS = Status Register = 464h */ acsts = csa_readio(resp, BA0_ACSTS); /* * See if we have valid status. * VSTS - Valid Status */ if ((acsts & ACSTS_VSTS) != 0) break; /* * Wait for a short while. */ DELAY(25); } /* * Make sure we got valid status. */ if ((acsts & ACSTS_VSTS) == 0) return (EAGAIN); /* * Read the data returned from the AC97 register. * ACSDA = Status Data Register = 474h */ *data = csa_readio(resp, BA0_ACSDA); return (0); } int csa_writecodec(csa_res *resp, u_long offset, u_int32_t data) { int i; u_int32_t acctl; /* * Setup the AC97 control registers on the CS461x to send the * appropriate command to the AC97 to perform the write. * ACCAD = Command Address Register = 46Ch * ACCDA = Command Data Register = 470h * ACCTL = Control Register = 460h * set DCV - will clear when process completed * set VFRM - valid frame enabled * set ESYN - ASYNC generation enabled * set RSTN - ARST# inactive, AC97 codec not reset */ /* * Get the actual AC97 register from the offset */ csa_writeio(resp, BA0_ACCAD, offset - BA0_AC97_RESET); csa_writeio(resp, BA0_ACCDA, data); csa_writeio(resp, BA0_ACCTL, ACCTL_DCV | ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN); /* * Wait for the write to occur. */ acctl = 0; for (i = 0 ; i < 10 ; i++) { /* * First, we want to wait for a short time. */ DELAY(25); /* * Now, check to see if the read has completed. * ACCTL = 460h, DCV should be reset by now and 460h = 17h */ acctl = csa_readio(resp, BA0_ACCTL); if ((acctl & ACCTL_DCV) == 0) break; } /* * Make sure the write completed. */ if ((acctl & ACCTL_DCV) != 0) return (EAGAIN); return (0); } u_int32_t csa_readio(csa_res *resp, u_long offset) { u_int32_t ul; if (offset < BA0_AC97_RESET) return bus_space_read_4(rman_get_bustag(resp->io), rman_get_bushandle(resp->io), offset) & 0xffffffff; else { if (csa_readcodec(resp, offset, &ul)) ul = 0; return (ul); } } void csa_writeio(csa_res *resp, u_long offset, u_int32_t data) { if (offset < BA0_AC97_RESET) bus_space_write_4(rman_get_bustag(resp->io), rman_get_bushandle(resp->io), offset, data); else csa_writecodec(resp, offset, data); } u_int32_t csa_readmem(csa_res *resp, u_long offset) { return bus_space_read_4(rman_get_bustag(resp->mem), rman_get_bushandle(resp->mem), offset) & 0xffffffff; } void csa_writemem(csa_res *resp, u_long offset, u_int32_t data) { bus_space_write_4(rman_get_bustag(resp->mem), rman_get_bushandle(resp->mem), offset, data); } #if NPCI > 0 static device_method_t csa_methods[] = { /* Device interface */ DEVMETHOD(device_probe, csa_probe), DEVMETHOD(device_attach, csa_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_alloc_resource, csa_alloc_resource), DEVMETHOD(bus_release_resource, csa_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t csa_driver = { "csa", csa_methods, sizeof(struct csa_softc), }; /* * csa can be attached to a pci bus. */ DRIVER_MODULE(csa, pci, csa_driver, csa_devclass, 0, 0); #endif /* NPCI > 0 */ #endif /* NCSA > 0 */ Index: head/sys/dev/usb/ohci_pci.c =================================================================== --- head/sys/dev/usb/ohci_pci.c (revision 54072) +++ head/sys/dev/usb/ohci_pci.c (revision 54073) @@ -1,266 +1,267 @@ /* $FreeBSD$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * USB Open Host Controller driver. * * OHCI spec: http://www.intel.com/design/usb/ohci11d.pdf */ /* The low level controller code for OHCI has been split into * PCI probes and OHCI specific code. This was done to facilitate the * sharing of code between *BSD's */ #include "opt_bus.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PCI_OHCI_VENDORID_ALI 0x10b9 #define PCI_OHCI_VENDORID_CMDTECH 0x1095 #define PCI_OHCI_VENDORID_COMPAQ 0x0e11 #define PCI_OHCI_VENDORID_NEC 0x1033 #define PCI_OHCI_VENDORID_OPTI 0x1045 #define PCI_OHCI_VENDORID_SIS 0x1039 #define PCI_OHCI_DEVICEID_ALADDIN_V 0x523710b9 static const char *ohci_device_aladdin_v = "AcerLabs M5237 (Aladdin-V) USB controller"; #define PCI_OHCI_DEVICEID_FIRELINK 0xc8611045 static const char *ohci_device_firelink = "OPTi 82C861 (FireLink) USB controller"; #define PCI_OHCI_DEVICEID_NEC 0x00351033 static const char *ohci_device_nec = "NEC uPD 9210 USB controller"; #define PCI_OHCI_DEVICEID_USB0670 0x06701095 static const char *ohci_device_usb0670 = "CMD Tech 670 (USB0670) USB controller"; #define PCI_OHCI_DEVICEID_USB0673 0x06731095 static const char *ohci_device_usb0673 = "CMD Tech 673 (USB0673) USB controller"; static const char *ohci_device_generic = "OHCI (generic) USB controller"; #define PCI_OHCI_BASE_REG 0x10 static const char * ohci_pci_match(device_t self) { u_int32_t device_id = pci_get_devid(self); switch(device_id) { case PCI_OHCI_DEVICEID_ALADDIN_V: return (ohci_device_aladdin_v); case PCI_OHCI_DEVICEID_USB0670: return (ohci_device_usb0670); case PCI_OHCI_DEVICEID_USB0673: return (ohci_device_usb0673); case PCI_OHCI_DEVICEID_FIRELINK: return (ohci_device_firelink); case PCI_OHCI_DEVICEID_NEC: return (ohci_device_nec); default: if ( pci_get_class(self) == PCIC_SERIALBUS && pci_get_subclass(self) == PCIS_SERIALBUS_USB && pci_get_progif(self) == PCI_INTERFACE_OHCI) { return (ohci_device_generic); } } return NULL; /* dunno */ } static int ohci_pci_probe(device_t self) { const char *desc = ohci_pci_match(self); if (desc) { device_set_desc(self, desc); return 0; } else { return ENXIO; } } static int ohci_pci_attach(device_t self) { device_t parent = device_get_parent(self); ohci_softc_t *sc = device_get_softc(self); device_t usbus; int err; int rid; struct resource *res; void *ih; int intr; /* XXX where does it say so in the spec? */ sc->sc_bus.usbrev = USBREV_1_0; rid = PCI_CBMEM; res = bus_alloc_resource(self, SYS_RES_MEMORY, &rid, 0, ~0, 1, RF_ACTIVE); if (!res) { device_printf(self, "could not map memory\n"); return ENXIO; } sc->iot = rman_get_bustag(res); sc->ioh = rman_get_bushandle(res); rid = 0; res = bus_alloc_resource(self, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); if (res == NULL) { device_printf(self, "could not allocate irq\n"); return ENOMEM; } - usbus = device_add_child(self, "usb", -1, sc); + usbus = device_add_child(self, "usb", -1); + device_set_ivars(usbus, sc); if (!usbus) { device_printf(self, "could not add USB device\n"); return ENOMEM; } switch (pci_get_devid(self)) { case PCI_OHCI_DEVICEID_ALADDIN_V: device_set_desc(usbus, ohci_device_aladdin_v); sprintf(sc->sc_vendor, "AcerLabs"); break; case PCI_OHCI_DEVICEID_FIRELINK: device_set_desc(usbus, ohci_device_firelink); sprintf(sc->sc_vendor, "OPTi"); break; case PCI_OHCI_DEVICEID_NEC: device_set_desc(usbus, ohci_device_nec); sprintf(sc->sc_vendor, "NEC"); break; case PCI_OHCI_DEVICEID_USB0670: device_set_desc(usbus, ohci_device_usb0670); sprintf(sc->sc_vendor, "CMDTECH"); break; case PCI_OHCI_DEVICEID_USB0673: device_set_desc(usbus, ohci_device_usb0673); sprintf(sc->sc_vendor, "CMDTECH"); break; default: if (bootverbose) device_printf(self, "(New OHCI DeviceId=0x%08x)\n", pci_get_devid(self)); device_set_desc(usbus, ohci_device_generic); sprintf(sc->sc_vendor, "(unknown)"); } intr = pci_read_config(self, PCIR_INTLINE, 1); if (intr == 0 || intr == 255) { device_printf(self, "Invalid irq %d\n", intr); device_printf(self, "Please switch on USB support and switch PNP-OS to 'No' in BIOS\n"); device_delete_child(self, usbus); return ENXIO; } err = BUS_SETUP_INTR(parent, self, res, INTR_TYPE_BIO, (driver_intr_t *) ohci_intr, sc, &ih); if (err) { device_printf(self, "could not setup irq, %d\n", err); device_delete_child(self, usbus); return err; } sc->sc_bus.bdev = usbus; err = ohci_init(sc); if (!err) err = device_probe_and_attach(sc->sc_bus.bdev); if (err) { device_printf(self, "init failed\n"); /* disable interrupts */ bus_space_write_4(sc->iot, sc->ioh, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); err = BUS_TEARDOWN_INTR(parent, self, res, ih); if (err) /* XXX or should we panic? */ device_printf(self, "could not tear down irq, %d\n", err); device_delete_child(self, usbus); return EIO; } return 0; } static device_method_t ohci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ohci_pci_probe), DEVMETHOD(device_attach, ohci_pci_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), { 0, 0 } }; static driver_t ohci_driver = { "ohci", ohci_methods, sizeof(ohci_softc_t), }; static devclass_t ohci_devclass; DRIVER_MODULE(ohci, pci, ohci_driver, ohci_devclass, 0, 0); Index: head/sys/dev/usb/uhci_pci.c =================================================================== --- head/sys/dev/usb/uhci_pci.c (revision 54072) +++ head/sys/dev/usb/uhci_pci.c (revision 54073) @@ -1,298 +1,299 @@ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* Universal Host Controller Interface * * UHCI spec: http://www.intel.com/ */ /* The low level controller code for UHCI has been split into * PCI probes and UHCI specific code. This was done to facilitate the * sharing of code between *BSD's */ #include "opt_bus.h" #include #include #include #include #include #include #include #if defined(__FreeBSD__) #include #include #include #include #endif #include #include #include #include #include #include #include #include #define PCI_UHCI_VENDORID_INTEL 0x8086 #define PCI_UHCI_VENDORID_VIA 0x1106 #define PCI_UHCI_DEVICEID_PIIX3 0x70208086 static const char *uhci_device_piix3 = "Intel 82371SB (PIIX3) USB controller"; #define PCI_UHCI_DEVICEID_PIIX4 0x71128086 #define PCI_UHCI_DEVICEID_PIIX4E 0x71128086 /* no separate stepping */ static const char *uhci_device_piix4 = "Intel 82371AB/EB (PIIX4) USB controller"; #define PCI_UHCI_DEVICEID_VT83C572 0x30381106 static const char *uhci_device_vt83c572 = "VIA 83C572 USB controller"; static const char *uhci_device_generic = "UHCI (generic) USB controller"; #define PCI_UHCI_BASE_REG 0x20 static int uhci_pci_suspend(device_t self) { uhci_softc_t *sc = device_get_softc(self); bus_generic_suspend(self); return 0; } static int uhci_pci_resume(device_t self) { uhci_softc_t *sc = device_get_softc(self); #if 0 uhci_reset(sc); #endif bus_generic_resume(self); return 0; } static const char * uhci_pci_match(device_t self) { u_int32_t device_id = pci_get_devid(self); if (device_id == PCI_UHCI_DEVICEID_PIIX3) { return (uhci_device_piix3); } else if (device_id == PCI_UHCI_DEVICEID_PIIX4) { return (uhci_device_piix4); } else if (device_id == PCI_UHCI_DEVICEID_VT83C572) { return (uhci_device_vt83c572); } else { if ( pci_get_class(self) == PCIC_SERIALBUS && pci_get_subclass(self) == PCIS_SERIALBUS_USB && pci_get_progif(self) == PCI_INTERFACE_UHCI) { return (uhci_device_generic); } } return NULL; /* dunno... */ } static int uhci_pci_probe(device_t self) { const char *desc = uhci_pci_match(self); if (desc) { device_set_desc(self, desc); return 0; } else { return ENXIO; } } static int uhci_pci_attach(device_t self) { uhci_softc_t *sc = device_get_softc(self); device_t parent = device_get_parent(self); int rid; void *ih; struct resource *res; device_t usbus; int intr; int legsup; int err; rid = PCI_UHCI_BASE_REG; res = bus_alloc_resource(self, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); if (!res) { device_printf(self, "could not map ports\n"); return ENXIO; } sc->iot = rman_get_bustag(res); sc->ioh = rman_get_bushandle(res); /* disable interrupts */ bus_space_write_2(sc->iot, sc->ioh, UHCI_INTR, 0); rid = 0; res = bus_alloc_resource(self, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); if (res == NULL) { device_printf(self, "could not allocate irq\n"); return ENOMEM; } - usbus = device_add_child(self, "usb", -1, sc); + usbus = device_add_child(self, "usb", -1); + device_set_ivars(usbus, sc); if (!usbus) { device_printf(self, "could not add USB device\n"); return ENOMEM; } switch (pci_get_devid(self)) { case PCI_UHCI_DEVICEID_PIIX3: device_set_desc(usbus, uhci_device_piix3); sprintf(sc->sc_vendor, "Intel"); break; case PCI_UHCI_DEVICEID_PIIX4: device_set_desc(usbus, uhci_device_piix4); sprintf(sc->sc_vendor, "Intel"); break; case PCI_UHCI_DEVICEID_VT83C572: device_set_desc(usbus, uhci_device_vt83c572); sprintf(sc->sc_vendor, "VIA"); break; default: device_printf(self, "(New UHCI DeviceId=0x%08x)\n", pci_get_devid(self)); device_set_desc(usbus, uhci_device_generic); sprintf(sc->sc_vendor, "(0x%08x)", pci_get_devid(self)); } switch(pci_read_config(self, PCI_USBREV, 4) & PCI_USBREV_MASK) { case PCI_USBREV_PRE_1_0: sc->sc_bus.usbrev = USBREV_PRE_1_0; break; case PCI_USBREV_1_0: sc->sc_bus.usbrev = USBREV_1_0; break; default: sc->sc_bus.usbrev = USBREV_UNKNOWN; break; } intr = pci_read_config(self, PCIR_INTLINE, 1); if (intr == 0 || intr == 255) { device_printf(self, "Invalid irq %d\n", intr); device_printf(self, "Please switch on USB support and switch PNP-OS to 'No' in BIOS\n"); device_delete_child(self, usbus); return ENXIO; } err = BUS_SETUP_INTR(parent, self, res, INTR_TYPE_BIO, (driver_intr_t *) uhci_intr, sc, &ih); if (err) { device_printf(self, "could not setup irq, %d\n", err); device_delete_child(self, usbus); return err; } /* Verify that the PIRQD enable bit is set, some BIOS's don't do that */ legsup = pci_read_config(self, PCI_LEGSUP, 4); if ( !(legsup & PCI_LEGSUP_USBPIRQDEN) ) { #ifndef USB_DEBUG if (bootverbose) #endif device_printf(self, "PIRQD enable not set\n"); legsup |= PCI_LEGSUP_USBPIRQDEN; pci_write_config(self, PCI_LEGSUP, legsup, 4); } sc->sc_bus.bdev = usbus; err = uhci_init(sc); if (!err) err = device_probe_and_attach(sc->sc_bus.bdev); if (err) { device_printf(self, "init failed\n"); /* disable interrupts that might have been switched on * in uhci_init */ bus_space_write_2(sc->iot, sc->ioh, UHCI_INTR, 0); err = BUS_TEARDOWN_INTR(parent, self, res, ih); if (err) /* XXX or should we panic? */ device_printf(self, "could not tear down irq, %d\n", err); device_delete_child(self, usbus); return EIO; /* XXX arbitrary value */ } return 0; } static device_method_t uhci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uhci_pci_probe), DEVMETHOD(device_attach, uhci_pci_attach), DEVMETHOD(device_suspend, uhci_pci_suspend), DEVMETHOD(device_resume, uhci_pci_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), { 0, 0 } }; static driver_t uhci_driver = { "uhci", uhci_methods, sizeof(uhci_softc_t), }; static devclass_t uhci_devclass; DRIVER_MODULE(uhci, pci, uhci_driver, uhci_devclass, 0, 0); Index: head/sys/dev/usb/usb_subr.c =================================================================== --- head/sys/dev/usb/usb_subr.c (revision 54072) +++ head/sys/dev/usb/usb_subr.c (revision 54073) @@ -1,1299 +1,1301 @@ /* $NetBSD: usb_subr.c,v 1.56 1999/11/18 23:32:32 augustss Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #if defined(__NetBSD__) || defined(__OpenBSD__) #include #include #elif defined(__FreeBSD__) #include #include #endif #include #include #include #include #include #include #include #include #if defined(__FreeBSD__) #include #define delay(d) DELAY(d) #endif #ifdef USB_DEBUG #define DPRINTF(x) if (usbdebug) logprintf x #define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x extern int usbdebug; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif static usbd_status usbd_set_config __P((usbd_device_handle, int)); static char *usbd_get_string __P((usbd_device_handle, int, char *)); static int usbd_getnewaddr __P((usbd_bus_handle bus)); #if defined(__NetBSD__) static int usbd_print __P((void *aux, const char *pnp)); static int usbd_submatch __P((device_ptr_t, struct cfdata *cf, void *)); #elif defined(__OpenBSD__) static int usbd_submatch __P((device_ptr_t, void *, void *)); #endif static void usbd_free_iface_data __P((usbd_device_handle dev, int ifcno)); static void usbd_kill_pipe __P((usbd_pipe_handle)); static usbd_status usbd_probe_and_attach __P((device_ptr_t parent, usbd_device_handle dev, int port, int addr)); static u_int32_t usb_cookie_no = 0; #ifdef USBVERBOSE typedef u_int16_t usb_vendor_id_t; typedef u_int16_t usb_product_id_t; /* * Descriptions of of known vendors and devices ("products"). */ struct usb_knowndev { usb_vendor_id_t vendor; usb_product_id_t product; int flags; char *vendorname, *productname; }; #define USB_KNOWNDEV_NOPROD 0x01 /* match on vendor only */ #include #endif /* USBVERBOSE */ static const char *usbd_error_strs[] = { "NORMAL_COMPLETION", "IN_PROGRESS", "PENDING_REQUESTS", "NOT_STARTED", "INVAL", "NOMEM", "CANCELLED", "BAD_ADDRESS", "IN_USE", "NO_ADDR", "SET_ADDR_FAILED", "NO_POWER", "TOO_DEEP", "IOERROR", "NOT_CONFIGURED", "TIMEOUT", "SHORT_XFER", "STALLED", "INTERRUPTED", "XXX", }; const char * usbd_errstr(err) usbd_status err; { static char buffer[5]; if (err < USBD_ERROR_MAX) { return usbd_error_strs[err]; } else { snprintf(buffer, sizeof buffer, "%d", err); return buffer; } } usbd_status usbd_get_string_desc(dev, sindex, langid, sdesc) usbd_device_handle dev; int sindex; int langid; usb_string_descriptor_t *sdesc; { usb_device_request_t req; usbd_status err; req.bmRequestType = UT_READ_DEVICE; req.bRequest = UR_GET_DESCRIPTOR; USETW2(req.wValue, UDESC_STRING, sindex); USETW(req.wIndex, langid); USETW(req.wLength, 1); /* only size byte first */ err = usbd_do_request(dev, &req, sdesc); if (err) return (err); USETW(req.wLength, sdesc->bLength); /* the whole string */ return (usbd_do_request(dev, &req, sdesc)); } char * usbd_get_string(dev, si, buf) usbd_device_handle dev; int si; char *buf; { int swap = dev->quirks->uq_flags & UQ_SWAP_UNICODE; usb_string_descriptor_t us; char *s; int i, n; u_int16_t c; usbd_status err; if (si == 0) return (0); if (dev->quirks->uq_flags & UQ_NO_STRINGS) return (0); if (dev->langid == USBD_NOLANG) { /* Set up default language */ err = usbd_get_string_desc(dev, USB_LANGUAGE_TABLE, 0, &us); if (err || us.bLength < 4) { dev->langid = 0; /* Well, just pick English then */ } else { /* Pick the first language as the default. */ dev->langid = UGETW(us.bString[0]); } } err = usbd_get_string_desc(dev, si, dev->langid, &us); if (err) return (0); s = buf; n = us.bLength / 2 - 1; for (i = 0; i < n; i++) { c = UGETW(us.bString[i]); /* Convert from Unicode, handle buggy strings. */ if ((c & 0xff00) == 0) *s++ = c; else if ((c & 0x00ff) == 0 && swap) *s++ = c >> 8; else *s++ = '?'; } *s++ = 0; return (buf); } void usbd_devinfo_vp(dev, v, p) usbd_device_handle dev; char *v, *p; { usb_device_descriptor_t *udd = &dev->ddesc; char *vendor = 0, *product = 0; #ifdef USBVERBOSE struct usb_knowndev *kdp; #endif if (dev == NULL) { v[0] = p[0] = '\0'; return; } vendor = usbd_get_string(dev, udd->iManufacturer, v); product = usbd_get_string(dev, udd->iProduct, p); #ifdef USBVERBOSE if (vendor == NULL) { for(kdp = usb_knowndevs; kdp->vendorname != NULL; kdp++) { if (kdp->vendor == UGETW(udd->idVendor) && (kdp->product == UGETW(udd->idProduct) || (kdp->flags & USB_KNOWNDEV_NOPROD) != 0)) break; } if (kdp->vendorname == NULL) vendor = product = NULL; else { vendor = kdp->vendorname; product = (kdp->flags & USB_KNOWNDEV_NOPROD) == 0 ? kdp->productname : NULL; } } #endif if (vendor != NULL) strcpy(v, vendor); else sprintf(v, "vendor 0x%04x", UGETW(udd->idVendor)); if (product != NULL) strcpy(p, product); else sprintf(p, "product 0x%04x", UGETW(udd->idProduct)); } int usbd_printBCD(cp, bcd) char *cp; int bcd; { return (sprintf(cp, "%x.%02x", bcd >> 8, bcd & 0xff)); } void usbd_devinfo(dev, showclass, cp) usbd_device_handle dev; int showclass; char *cp; { usb_device_descriptor_t *udd = &dev->ddesc; char vendor[USB_MAX_STRING_LEN]; char product[USB_MAX_STRING_LEN]; int bcdDevice, bcdUSB; usbd_devinfo_vp(dev, vendor, product); cp += sprintf(cp, "%s %s", vendor, product); if (showclass) cp += sprintf(cp, ", class %d/%d", udd->bDeviceClass, udd->bDeviceSubClass); bcdUSB = UGETW(udd->bcdUSB); bcdDevice = UGETW(udd->bcdDevice); cp += sprintf(cp, ", rev "); cp += usbd_printBCD(cp, bcdUSB); *cp++ = '/'; cp += usbd_printBCD(cp, bcdDevice); cp += sprintf(cp, ", addr %d", dev->address); *cp = 0; } /* Delay for a certain number of ms */ void usb_delay_ms(bus, ms) usbd_bus_handle bus; u_int ms; { /* Wait at least two clock ticks so we know the time has passed. */ if (bus->use_polling) delay((ms+1) * 1000); else tsleep(&ms, PRIBIO, "usbdly", (ms*hz+999)/1000 + 1); } /* Delay given a device handle. */ void usbd_delay_ms(dev, ms) usbd_device_handle dev; u_int ms; { usb_delay_ms(dev->bus, ms); } usbd_status usbd_reset_port(dev, port, ps) usbd_device_handle dev; int port; usb_port_status_t *ps; { usb_device_request_t req; usbd_status err; int n; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, UHF_PORT_RESET); USETW(req.wIndex, port); USETW(req.wLength, 0); err = usbd_do_request(dev, &req, 0); DPRINTFN(1,("usbd_reset_port: port %d reset done, error=%s\n", port, usbd_errstr(err))); if (err) return (err); n = 10; do { /* Wait for device to recover from reset. */ usbd_delay_ms(dev, USB_PORT_RESET_DELAY); err = usbd_get_port_status(dev, port, ps); if (err) { DPRINTF(("usbd_reset_port: get status failed %d\n", err)); return (err); } } while ((UGETW(ps->wPortChange) & UPS_C_PORT_RESET) == 0 && --n > 0); if (n == 0) { printf("usbd_reset_port: timeout\n"); return (USBD_IOERROR); } err = usbd_clear_port_feature(dev, port, UHF_C_PORT_RESET); #ifdef USB_DEBUG if (err) DPRINTF(("usbd_reset_port: clear port feature failed %d\n", err)); #endif /* Wait for the device to recover from reset. */ usbd_delay_ms(dev, USB_PORT_RESET_RECOVERY); return (err); } usb_interface_descriptor_t * usbd_find_idesc(cd, ifaceidx, altidx) usb_config_descriptor_t *cd; int ifaceidx; int altidx; { char *p = (char *)cd; char *end = p + UGETW(cd->wTotalLength); usb_interface_descriptor_t *d; int curidx, lastidx, curaidx = 0; for (curidx = lastidx = -1; p < end; ) { d = (usb_interface_descriptor_t *)p; DPRINTFN(4,("usbd_find_idesc: idx=%d(%d) altidx=%d(%d) len=%d " "type=%d\n", ifaceidx, curidx, altidx, curaidx, d->bLength, d->bDescriptorType)); if (d->bLength == 0) /* bad descriptor */ break; p += d->bLength; if (p <= end && d->bDescriptorType == UDESC_INTERFACE) { if (d->bInterfaceNumber != lastidx) { lastidx = d->bInterfaceNumber; curidx++; curaidx = 0; } else curaidx++; if (ifaceidx == curidx && altidx == curaidx) return (d); } } return (0); } usb_endpoint_descriptor_t * usbd_find_edesc(cd, ifaceidx, altidx, endptidx) usb_config_descriptor_t *cd; int ifaceidx; int altidx; int endptidx; { char *p = (char *)cd; char *end = p + UGETW(cd->wTotalLength); usb_interface_descriptor_t *d; usb_endpoint_descriptor_t *e; int curidx; d = usbd_find_idesc(cd, ifaceidx, altidx); if (d == NULL) return (0); if (endptidx >= d->bNumEndpoints) /* quick exit */ return (0); curidx = -1; for (p = (char *)d + d->bLength; p < end; ) { e = (usb_endpoint_descriptor_t *)p; if (e->bLength == 0) /* bad descriptor */ break; p += e->bLength; if (p <= end && e->bDescriptorType == UDESC_INTERFACE) return (0); if (p <= end && e->bDescriptorType == UDESC_ENDPOINT) { curidx++; if (curidx == endptidx) return (e); } } return (0); } usbd_status usbd_fill_iface_data(dev, ifaceidx, altidx) usbd_device_handle dev; int ifaceidx; int altidx; { usbd_interface_handle ifc = &dev->ifaces[ifaceidx]; char *p, *end; int endpt, nendpt; DPRINTFN(4,("usbd_fill_iface_data: ifaceidx=%d altidx=%d\n", ifaceidx, altidx)); ifc->device = dev; ifc->idesc = usbd_find_idesc(dev->cdesc, ifaceidx, altidx); if (ifc->idesc == 0) return (USBD_INVAL); ifc->index = ifaceidx; ifc->altindex = altidx; nendpt = ifc->idesc->bNumEndpoints; DPRINTFN(10,("usbd_fill_iface_data: found idesc n=%d\n", nendpt)); if (nendpt != 0) { ifc->endpoints = malloc(nendpt * sizeof(struct usbd_endpoint), M_USB, M_NOWAIT); if (ifc->endpoints == 0) return (USBD_NOMEM); } else ifc->endpoints = 0; ifc->priv = 0; p = (char *)ifc->idesc + ifc->idesc->bLength; end = (char *)dev->cdesc + UGETW(dev->cdesc->wTotalLength); #define ed ((usb_endpoint_descriptor_t *)p) for (endpt = 0; endpt < nendpt; endpt++) { DPRINTFN(10,("usbd_fill_iface_data: endpt=%d\n", endpt)); for (; p < end; p += ed->bLength) { ed = (usb_endpoint_descriptor_t *)p; DPRINTFN(10,("usbd_fill_iface_data: p=%p end=%p " "len=%d type=%d\n", p, end, ed->bLength, ed->bDescriptorType)); if (p + ed->bLength <= end && ed->bLength != 0 && ed->bDescriptorType == UDESC_ENDPOINT) goto found; if (ed->bDescriptorType == UDESC_INTERFACE || ed->bLength == 0) break; } /* passed end, or bad desc */ goto bad; found: ifc->endpoints[endpt].edesc = ed; ifc->endpoints[endpt].refcnt = 0; p += ed->bLength; } #undef ed LIST_INIT(&ifc->pipes); return (USBD_NORMAL_COMPLETION); bad: free(ifc->endpoints, M_USB); return (USBD_INVAL); } void usbd_free_iface_data(dev, ifcno) usbd_device_handle dev; int ifcno; { usbd_interface_handle ifc = &dev->ifaces[ifcno]; if (ifc->endpoints) free(ifc->endpoints, M_USB); } static usbd_status usbd_set_config(dev, conf) usbd_device_handle dev; int conf; { usb_device_request_t req; req.bmRequestType = UT_WRITE_DEVICE; req.bRequest = UR_SET_CONFIG; USETW(req.wValue, conf); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(dev, &req, 0)); } usbd_status usbd_set_config_no(dev, no, msg) usbd_device_handle dev; int no; int msg; { int index; usb_config_descriptor_t cd; usbd_status err; DPRINTFN(5,("usbd_set_config_no: %d\n", no)); /* Figure out what config index to use. */ for (index = 0; index < dev->ddesc.bNumConfigurations; index++) { err = usbd_get_config_desc(dev, index, &cd); if (err) return (err); if (cd.bConfigurationValue == no) return (usbd_set_config_index(dev, index, msg)); } return (USBD_INVAL); } usbd_status usbd_set_config_index(dev, index, msg) usbd_device_handle dev; int index; int msg; { usb_status_t ds; usb_config_descriptor_t cd, *cdp; usbd_status err; int ifcidx, nifc, len, selfpowered, power; DPRINTFN(5,("usbd_set_config_index: dev=%p index=%d\n", dev, index)); /* XXX check that all interfaces are idle */ if (dev->config != 0) { DPRINTF(("usbd_set_config_index: free old config\n")); /* Free all configuration data structures. */ nifc = dev->cdesc->bNumInterface; for (ifcidx = 0; ifcidx < nifc; ifcidx++) usbd_free_iface_data(dev, ifcidx); free(dev->ifaces, M_USB); free(dev->cdesc, M_USB); dev->ifaces = NULL; dev->cdesc = NULL; dev->config = 0; } /* Figure out what config number to use. */ err = usbd_get_config_desc(dev, index, &cd); if (err) return (err); len = UGETW(cd.wTotalLength); cdp = malloc(len, M_USB, M_NOWAIT); if (cdp == NULL) return (USBD_NOMEM); err = usbd_get_desc(dev, UDESC_CONFIG, index, len, cdp); if (err) goto bad; if (cdp->bDescriptorType != UDESC_CONFIG) { DPRINTFN(-1,("usbd_set_config_index: bad desc %d\n", cdp->bDescriptorType)); err = USBD_INVAL; goto bad; } selfpowered = 0; if (!(dev->quirks->uq_flags & UQ_BUS_POWERED) && cdp->bmAttributes & UC_SELF_POWERED) { /* May be self powered. */ if (cdp->bmAttributes & UC_BUS_POWERED) { /* Must ask device. */ err = usbd_get_device_status(dev, &ds); if (!err && (UGETW(ds.wStatus) & UDS_SELF_POWERED)) selfpowered = 1; DPRINTF(("usbd_set_config_index: status=0x%04x, " "error=%s\n", UGETW(ds.wStatus), usbd_errstr(err))); } else selfpowered = 1; } DPRINTF(("usbd_set_config_index: (addr %d) attr=0x%02x, " "selfpowered=%d, power=%d\n", dev->address, cdp->bmAttributes, selfpowered, cdp->bMaxPower * 2)); #ifdef USB_DEBUG if (dev->powersrc == NULL) { DPRINTF(("usbd_set_config_index: No power source?\n")); return (USBD_IOERROR); } #endif power = cdp->bMaxPower * 2; if (power > dev->powersrc->power) { /* XXX print nicer message. */ if (msg) printf("%s: device addr %d (config %d) exceeds power " "budget, %d mA > %d mA\n", USBDEVNAME(dev->bus->bdev), dev->address, cdp->bConfigurationValue, power, dev->powersrc->power); err = USBD_NO_POWER; goto bad; } dev->power = power; dev->self_powered = selfpowered; DPRINTF(("usbd_set_config_index: set config %d\n", cdp->bConfigurationValue)); err = usbd_set_config(dev, cdp->bConfigurationValue); if (err) { DPRINTF(("usbd_set_config_index: setting config=%d failed, " "error=%s\n", cdp->bConfigurationValue, usbd_errstr(err))); goto bad; } DPRINTF(("usbd_set_config_index: setting new config %d\n", cdp->bConfigurationValue)); nifc = cdp->bNumInterface; dev->ifaces = malloc(nifc * sizeof(struct usbd_interface), M_USB, M_NOWAIT); if (dev->ifaces == 0) { err = USBD_NOMEM; goto bad; } DPRINTFN(5,("usbd_set_config_index: dev=%p cdesc=%p\n", dev, cdp)); dev->cdesc = cdp; dev->config = cdp->bConfigurationValue; for (ifcidx = 0; ifcidx < nifc; ifcidx++) { err = usbd_fill_iface_data(dev, ifcidx, 0); if (err) { while (--ifcidx >= 0) usbd_free_iface_data(dev, ifcidx); goto bad; } } return (USBD_NORMAL_COMPLETION); bad: free(cdp, M_USB); return (err); } /* XXX add function for alternate settings */ usbd_status usbd_setup_pipe(dev, iface, ep, pipe) usbd_device_handle dev; usbd_interface_handle iface; struct usbd_endpoint *ep; usbd_pipe_handle *pipe; { usbd_pipe_handle p; usbd_status err; DPRINTFN(1,("usbd_setup_pipe: dev=%p iface=%p ep=%p pipe=%p\n", dev, iface, ep, pipe)); p = malloc(dev->bus->pipe_size, M_USB, M_NOWAIT); if (p == NULL) return (USBD_NOMEM); p->device = dev; p->iface = iface; p->endpoint = ep; ep->refcnt++; p->refcnt = 1; p->intrxfer = 0; p->running = 0; p->repeat = 0; SIMPLEQ_INIT(&p->queue); err = dev->bus->methods->open_pipe(p); if (err) { DPRINTFN(-1,("usbd_setup_pipe: endpoint=0x%x failed, error=" "%s\n", ep->edesc->bEndpointAddress, usbd_errstr(err))); free(p, M_USB); return (err); } /* Clear any stall and make sure DATA0 toggle will be used next. */ if (UE_GET_ADDR(ep->edesc->bEndpointAddress) != USB_CONTROL_ENDPOINT) usbd_clear_endpoint_stall(p); *pipe = p; return (USBD_NORMAL_COMPLETION); } /* Abort the device control pipe. */ void usbd_kill_pipe(pipe) usbd_pipe_handle pipe; { pipe->methods->close(pipe); pipe->endpoint->refcnt--; free(pipe, M_USB); } int usbd_getnewaddr(bus) usbd_bus_handle bus; { int addr; for (addr = 1; addr < USB_MAX_DEVICES; addr++) if (bus->devices[addr] == 0) return (addr); return (-1); } usbd_status usbd_probe_and_attach(parent, dev, port, addr) device_ptr_t parent; usbd_device_handle dev; int port; int addr; { struct usb_attach_arg uaa; usb_device_descriptor_t *dd = &dev->ddesc; int found, i, confi, nifaces; usbd_status err; device_ptr_t dv; usbd_interface_handle ifaces[256]; /* 256 is the absolute max */ #if defined(__FreeBSD__) /* * XXX uaa is a static var. Not a problem as it _should_ be used only * during probe and attach. Should be changed however. */ device_t bdev; - bdev = device_add_child(parent, NULL, -1, &uaa); + bdev = device_add_child(parent, NULL, -1); + device_set_ivars(bdev, &uaa); if (!bdev) { printf("%s: Device creation failed\n", USBDEVNAME(dev->bus->bdev)); return (USBD_INVAL); } device_quiet(bdev); #endif uaa.device = dev; uaa.iface = 0; uaa.ifaces = 0; uaa.nifaces = 0; uaa.usegeneric = 0; uaa.port = port; uaa.configno = UHUB_UNK_CONFIGURATION; uaa.ifaceno = UHUB_UNK_INTERFACE; uaa.vendor = UGETW(dd->idVendor); uaa.product = UGETW(dd->idProduct); uaa.release = UGETW(dd->bcdDevice); /* First try with device specific drivers. */ dv = USB_DO_ATTACH(dev, bdev, parent, &uaa, usbd_print, usbd_submatch); if (dv) { dev->subdevs = malloc(2 * sizeof dv, M_USB, M_NOWAIT); if (dev->subdevs == NULL) return (USBD_NOMEM); dev->subdevs[0] = dv; dev->subdevs[1] = 0; return (USBD_NORMAL_COMPLETION); } DPRINTF(("usbd_probe_and_attach: no device specific driver found\n")); /* Next try with interface drivers. */ for (confi = 0; confi < dd->bNumConfigurations; confi++) { DPRINTFN(1,("usbd_probe_and_attach: trying config idx=%d\n", confi)); err = usbd_set_config_index(dev, confi, 1); if (err) { #ifdef USB_DEBUG DPRINTF(("%s: port %d, set config at addr %d failed, " "error=%s\n", USBDEVPTRNAME(parent), port, addr, usbd_errstr(err))); #else printf("%s: port %d, set config at addr %d failed\n", USBDEVPTRNAME(parent), port, addr); #endif #if defined(__FreeBSD__) device_delete_child(parent, bdev); #endif return (err); } nifaces = dev->cdesc->bNumInterface; uaa.configno = dev->cdesc->bConfigurationValue; for (i = 0; i < nifaces; i++) ifaces[i] = &dev->ifaces[i]; uaa.ifaces = ifaces; uaa.nifaces = nifaces; dev->subdevs = malloc((nifaces+1) * sizeof dv, M_USB,M_NOWAIT); if (dev->subdevs == NULL) { #if defined(__FreeBSD__) device_delete_child(parent, bdev); #endif return (USBD_NOMEM); } found = 0; for (i = 0; i < nifaces; i++) { if (ifaces[i] == NULL) continue; /* interface already claimed */ uaa.iface = ifaces[i]; uaa.ifaceno = ifaces[i]->idesc->bInterfaceNumber; dv = USB_DO_ATTACH(dev, bdev, parent, &uaa, usbd_print, usbd_submatch); if (dv != NULL) { dev->subdevs[found++] = dv; dev->subdevs[found] = 0; ifaces[i] = 0; /* consumed */ #if defined(__FreeBSD__) - /* create another child for the next iface */ - bdev = device_add_child(parent, NULL, -1,&uaa); + /* create another device for the next iface */ + bdev = device_add_child(parent, NULL, -1); + device_set_ivars(bdev, &uaa); if (!bdev) { printf("%s: Device creation failed\n", USBDEVNAME(dev->bus->bdev)); return (USBD_NORMAL_COMPLETION); } device_quiet(bdev); #endif } } if (found != 0) { #if defined(__FreeBSD__) /* remove the last created child again; it is unused */ device_delete_child(parent, bdev); #endif return (USBD_NORMAL_COMPLETION); } free(dev->subdevs, M_USB); dev->subdevs = 0; } /* No interfaces were attached in any of the configurations. */ if (dd->bNumConfigurations > 1) /* don't change if only 1 config */ usbd_set_config_index(dev, 0, 0); DPRINTF(("usbd_probe_and_attach: no interface drivers found\n")); /* Finally try the generic driver. */ uaa.iface = 0; uaa.usegeneric = 1; uaa.configno = UHUB_UNK_CONFIGURATION; uaa.ifaceno = UHUB_UNK_INTERFACE; uaa.vendor = UHUB_UNK_VENDOR; uaa.product = UHUB_UNK_PRODUCT; uaa.release = UHUB_UNK_RELEASE; dv = USB_DO_ATTACH(dev, bdev, parent, &uaa, usbd_print, usbd_submatch); if (dv != NULL) { dev->subdevs = malloc(2 * sizeof dv, M_USB, M_NOWAIT); if (dev->subdevs == 0) return (USBD_NOMEM); dev->subdevs[0] = dv; dev->subdevs[1] = 0; return (USBD_NORMAL_COMPLETION); } /* * The generic attach failed, but leave the device as it is. * We just did not find any drivers, that's all. The device is * fully operational and not harming anyone. */ DPRINTF(("usbd_probe_and_attach: generic attach failed\n")); #if defined(__FreeBSD__) device_delete_child(parent, bdev); #endif return (USBD_NORMAL_COMPLETION); } /* * Called when a new device has been put in the powered state, * but not yet in the addressed state. * Get initial descriptor, set the address, get full descriptor, * and attach a driver. */ usbd_status usbd_new_device(parent, bus, depth, lowspeed, port, up) device_ptr_t parent; usbd_bus_handle bus; int depth; int lowspeed; int port; struct usbd_port *up; { usbd_device_handle dev; usb_device_descriptor_t *dd; usbd_status err; int addr; int i; DPRINTF(("usbd_new_device bus=%p depth=%d lowspeed=%d\n", bus, depth, lowspeed)); addr = usbd_getnewaddr(bus); if (addr < 0) { printf("%s: No free USB addresses, new device ignored.\n", USBDEVNAME(bus->bdev)); return (USBD_NO_ADDR); } dev = malloc(sizeof *dev, M_USB, M_NOWAIT); if (dev == NULL) return (USBD_NOMEM); memset(dev, 0, sizeof(*dev)); dev->bus = bus; /* Set up default endpoint handle. */ dev->def_ep.edesc = &dev->def_ep_desc; /* Set up default endpoint descriptor. */ dev->def_ep_desc.bLength = USB_ENDPOINT_DESCRIPTOR_SIZE; dev->def_ep_desc.bDescriptorType = UDESC_ENDPOINT; dev->def_ep_desc.bEndpointAddress = USB_CONTROL_ENDPOINT; dev->def_ep_desc.bmAttributes = UE_CONTROL; USETW(dev->def_ep_desc.wMaxPacketSize, USB_MAX_IPACKET); dev->def_ep_desc.bInterval = 0; dev->quirks = &usbd_no_quirk; dev->address = USB_START_ADDR; dev->ddesc.bMaxPacketSize = 0; dev->lowspeed = lowspeed != 0; dev->depth = depth; dev->powersrc = up; dev->langid = USBD_NOLANG; dev->cookie.cookie = ++usb_cookie_no; /* Establish the the default pipe. */ err = usbd_setup_pipe(dev, 0, &dev->def_ep, &dev->default_pipe); if (err) { usbd_remove_device(dev, up); return (err); } up->device = dev; dd = &dev->ddesc; /* Try a few times in case the device is slow (i.e. outside specs.) */ for (i = 0; i < 3; i++) { /* Get the first 8 bytes of the device descriptor. */ err = usbd_get_desc(dev, UDESC_DEVICE, 0, USB_MAX_IPACKET, dd); if (!err) break; usbd_delay_ms(dev, 200); } if (err) { DPRINTFN(-1, ("usbd_new_device: addr=%d, getting first desc " "failed\n", addr)); usbd_remove_device(dev, up); return (err); } DPRINTF(("usbd_new_device: adding unit addr=%d, rev=%02x, class=%d, " "subclass=%d, protocol=%d, maxpacket=%d, len=%d, ls=%d\n", addr,UGETW(dd->bcdUSB), dd->bDeviceClass, dd->bDeviceSubClass, dd->bDeviceProtocol, dd->bMaxPacketSize, dd->bLength, dev->lowspeed)); if (dd->bDescriptorType != UDESC_DEVICE) { /* Illegal device descriptor */ DPRINTFN(-1,("usbd_new_device: illegal descriptor %d\n", dd->bDescriptorType)); usbd_remove_device(dev, up); return (USBD_INVAL); } if (dd->bLength < USB_DEVICE_DESCRIPTOR_SIZE) { DPRINTFN(-1,("usbd_new_device: bad length %d\n", dd->bLength)); usbd_remove_device(dev, up); return (USBD_INVAL); } USETW(dev->def_ep_desc.wMaxPacketSize, dd->bMaxPacketSize); /* Get the full device descriptor. */ err = usbd_get_device_desc(dev, dd); if (err) { DPRINTFN(-1, ("usbd_new_device: addr=%d, getting full desc " "failed\n", addr)); usbd_remove_device(dev, up); return (err); } /* Figure out what's wrong with this device. */ dev->quirks = usbd_find_quirk(dd); /* Set the address */ err = usbd_set_address(dev, addr); if (err) { DPRINTFN(-1,("usb_new_device: set address %d failed\n",addr)); err = USBD_SET_ADDR_FAILED; usbd_remove_device(dev, up); return (err); } /* Allow device time to set new address */ usbd_delay_ms(dev, USB_SET_ADDRESS_SETTLE); dev->address = addr; /* New device address now */ bus->devices[addr] = dev; /* Assume 100mA bus powered for now. Changed when configured. */ dev->power = USB_MIN_POWER; dev->self_powered = 0; DPRINTF(("usbd_new_device: new dev (addr %d), dev=%p, parent=%p\n", addr, dev, parent)); err = usbd_probe_and_attach(parent, dev, port, addr); if (err) { usbd_remove_device(dev, up); return (err); } usbd_add_event(USB_EVENT_ATTACH, dev); return (USBD_NORMAL_COMPLETION); } void usbd_remove_device(dev, up) usbd_device_handle dev; struct usbd_port *up; { DPRINTF(("usbd_remove_device: %p\n", dev)); if (dev->default_pipe != NULL) usbd_kill_pipe(dev->default_pipe); up->device = 0; dev->bus->devices[dev->address] = 0; free(dev, M_USB); } #if defined(__NetBSD__) || defined(__OpenBSD__) int usbd_print(aux, pnp) void *aux; const char *pnp; { struct usb_attach_arg *uaa = aux; char devinfo[1024]; DPRINTFN(15, ("usbd_print dev=%p\n", uaa->device)); if (pnp) { if (!uaa->usegeneric) return (QUIET); usbd_devinfo(uaa->device, 1, devinfo); printf("%s, %s", devinfo, pnp); } if (uaa->port != 0) printf(" port %d", uaa->port); if (uaa->configno != UHUB_UNK_CONFIGURATION) printf(" configuration %d", uaa->configno); if (uaa->ifaceno != UHUB_UNK_INTERFACE) printf(" interface %d", uaa->ifaceno); #if 0 /* * It gets very crowded with these locators on the attach line. * They are not really needed since they are printed in the clear * by each driver. */ if (uaa->vendor != UHUB_UNK_VENDOR) printf(" vendor 0x%04x", uaa->vendor); if (uaa->product != UHUB_UNK_PRODUCT) printf(" product 0x%04x", uaa->product); if (uaa->release != UHUB_UNK_RELEASE) printf(" release 0x%04x", uaa->release); #endif return (UNCONF); } #if defined(__NetBSD__) int usbd_submatch(parent, cf, aux) struct device *parent; struct cfdata *cf; void *aux; { #elif defined(__OpenBSD__) int usbd_submatch(parent, match, aux) struct device *parent; void *match; void *aux; { struct cfdata *cf = match; #endif struct usb_attach_arg *uaa = aux; if ((uaa->port != 0 && cf->uhubcf_port != UHUB_UNK_PORT && cf->uhubcf_port != uaa->port) || (uaa->configno != UHUB_UNK_CONFIGURATION && cf->uhubcf_configuration != UHUB_UNK_CONFIGURATION && cf->uhubcf_configuration != uaa->configno) || (uaa->ifaceno != UHUB_UNK_INTERFACE && cf->uhubcf_interface != UHUB_UNK_INTERFACE && cf->uhubcf_interface != uaa->ifaceno) || (uaa->vendor != UHUB_UNK_VENDOR && cf->uhubcf_vendor != UHUB_UNK_VENDOR && cf->uhubcf_vendor != uaa->vendor) || (uaa->product != UHUB_UNK_PRODUCT && cf->uhubcf_product != UHUB_UNK_PRODUCT && cf->uhubcf_product != uaa->product) || (uaa->release != UHUB_UNK_RELEASE && cf->uhubcf_release != UHUB_UNK_RELEASE && cf->uhubcf_release != uaa->release) ) return 0; return ((*cf->cf_attach->ca_match)(parent, cf, aux)); } #endif void usbd_fill_deviceinfo(dev, di) usbd_device_handle dev; struct usb_device_info *di; { struct usbd_port *p; int i, err, s; di->bus = USBDEVUNIT(dev->bus->bdev); di->addr = dev->address; if (dev->subdevs) { for (i = 0; dev->subdevs[i] && i < MAXDEVNAMES; i++) { strncpy(di->devnames[i], USBDEVPTRNAME(dev->subdevs[i]), MAXDEVNAMELEN); di->devnames[i][MAXDEVNAMELEN-1] = '\0'; /* terminate */ } } else { i = 0; } for (/*i is set */; i < MAXDEVNAMES; i++) di->devnames[i][0] = 0; /* empty */ usbd_devinfo_vp(dev, di->vendor, di->product); usbd_printBCD(di->release, UGETW(dev->ddesc.bcdDevice)); di->vendorNo = UGETW(dev->ddesc.idVendor); di->productNo = UGETW(dev->ddesc.idProduct); di->releaseNo = UGETW(dev->ddesc.bcdDevice); di->class = dev->ddesc.bDeviceClass; di->subclass = dev->ddesc.bDeviceSubClass; di->protocol = dev->ddesc.bDeviceProtocol; di->config = dev->config; di->power = dev->self_powered ? 0 : dev->power; di->lowspeed = dev->lowspeed; if (dev->hub) { for (i = 0; i < sizeof(di->ports) / sizeof(di->ports[0]) && i < dev->hub->hubdesc.bNbrPorts; i++) { p = &dev->hub->ports[i]; if (p->device) err = p->device->address; else { s = UGETW(p->status.wPortStatus); if (s & UPS_PORT_ENABLED) err = USB_PORT_ENABLED; else if (s & UPS_SUSPEND) err = USB_PORT_SUSPENDED; else if (s & UPS_PORT_POWER) err = USB_PORT_POWERED; else err = USB_PORT_DISABLED; } di->ports[i] = err; } di->nports = dev->hub->hubdesc.bNbrPorts; } else di->nports = 0; } void usb_free_device(dev) usbd_device_handle dev; { int ifcidx, nifc; if (dev->default_pipe != NULL) usbd_kill_pipe(dev->default_pipe); if (dev->ifaces != NULL) { nifc = dev->cdesc->bNumInterface; for (ifcidx = 0; ifcidx < nifc; ifcidx++) usbd_free_iface_data(dev, ifcidx); free(dev->ifaces, M_USB); } if (dev->cdesc != NULL) free(dev->cdesc, M_USB); if (dev->subdevs != NULL) free(dev->subdevs, M_USB); free(dev, M_USB); } /* * The general mechanism for detaching drivers works as follows: Each * driver is responsible for maintaining a reference count on the * number of outstanding references to its softc (e.g. from * processing hanging in a read or write). The detach method of the * driver decrements this counter and flags in the softc that the * driver is dying and then wakes any sleepers. It then sleeps on the * softc. Each place that can sleep must maintain the reference * count. When the reference count drops to -1 (0 is the normal value * of the reference count) the a wakeup on the softc is performed * signaling to the detach waiter that all references are gone. */ /* * Called from process context when we discover that a port has * been disconnected. */ void usb_disconnect_port(up, parent) struct usbd_port *up; device_ptr_t parent; { usbd_device_handle dev = up->device; const char *hubname = USBDEVPTRNAME(parent); int i; DPRINTFN(3,("uhub_disconnect: up=%p dev=%p port=%d\n", up, dev, up->portno)); #ifdef DIAGNOSTIC if (dev == NULL) { printf("usb_disconnect_port: no device\n"); return; } #endif if (dev->cdesc == NULL) { /* Partially attached device, just drop it. */ dev->bus->devices[dev->address] = 0; up->device = 0; return; } usbd_add_event(USB_EVENT_DETACH, dev); if (dev->subdevs != NULL) { DPRINTFN(3,("usb_disconnect_port: disconnect subdevs\n")); for (i = 0; dev->subdevs[i]; i++) { printf("%s: at %s", USBDEVPTRNAME(dev->subdevs[i]), hubname); if (up->portno != 0) printf(" port %d", up->portno); printf(" (addr %d) disconnected\n", dev->address); #if defined(__NetBSD__) || defined(__OpenBSD__) config_detach(dev->subdevs[i], DETACH_FORCE); #elif defined(__FreeBSD__) device_delete_child(device_get_parent(dev->subdevs[i]), dev->subdevs[i]); #endif } } dev->bus->devices[dev->address] = NULL; up->device = NULL; usb_free_device(dev); } #ifdef __OpenBSD__ void *usb_realloc(p, size, pool, flags) void *p; u_int size; int pool; int flags; { void *q; q = malloc(size, pool, flags); if (q == NULL) return (NULL); bcopy(p, q, size); free(p, pool); return (q); } #endif Index: head/sys/i386/eisa/eisaconf.c =================================================================== --- head/sys/i386/eisa/eisaconf.c (revision 54072) +++ head/sys/i386/eisa/eisaconf.c (revision 54073) @@ -1,624 +1,626 @@ /* * EISA bus probe and attach routines * * Copyright (c) 1995, 1996 Justin T. Gibbs. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice immediately at the beginning of the file, without modification, * this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "opt_eisa.h" #include #include #include #include #include #include #include #include #include #include #include #include typedef struct resvaddr { u_long addr; /* start address */ u_long size; /* size of reserved area */ int flags; struct resource *res; /* resource manager handle */ LIST_ENTRY(resvaddr) links; /* List links */ } resvaddr_t; LIST_HEAD(resvlist, resvaddr); struct irq_node { int irq_no; int irq_trigger; void *idesc; TAILQ_ENTRY(irq_node) links; }; TAILQ_HEAD(irqlist, irq_node); struct eisa_ioconf { int slot; struct resvlist ioaddrs; /* list of reserved I/O ranges */ struct resvlist maddrs; /* list of reserved memory ranges */ struct irqlist irqs; /* list of reserved irqs */ }; /* To be replaced by the "super device" generic device structure... */ struct eisa_device { eisa_id_t id; struct eisa_ioconf ioconf; }; /* Global variable, so UserConfig can change it. */ #define MAX_COL 79 #ifndef EISA_SLOTS #define EISA_SLOTS 10 /* PCI clashes with higher ones.. fix later */ #endif int num_eisa_slots = EISA_SLOTS; static devclass_t eisa_devclass; static void eisa_reg_print (device_t, char *, char *, int *); static struct irq_node * eisa_find_irq(struct eisa_device *e_dev, int rid); static struct resvaddr * eisa_find_maddr(struct eisa_device *e_dev, int rid); static struct resvaddr * eisa_find_ioaddr(struct eisa_device *e_dev, int rid); static int mainboard_probe(device_t dev) { char *idstring; eisa_id_t id = eisa_get_id(dev); if (eisa_get_slot(dev) != 0) return (ENXIO); idstring = (char *)malloc(8 + sizeof(" (System Board)") + 1, M_DEVBUF, M_NOWAIT); if (idstring == NULL) { panic("Eisa probe unable to malloc"); } sprintf(idstring, "%c%c%c%03x%01x (System Board)", EISA_MFCTR_CHAR0(id), EISA_MFCTR_CHAR1(id), EISA_MFCTR_CHAR2(id), EISA_PRODUCT_ID(id), EISA_REVISION_ID(id)); device_set_desc(dev, idstring); return (0); } static int mainboard_attach(device_t dev) { return (0); } static device_method_t mainboard_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mainboard_probe), DEVMETHOD(device_attach, mainboard_attach), { 0, 0 } }; static driver_t mainboard_driver = { "mainboard", mainboard_methods, 1, }; static devclass_t mainboard_devclass; DRIVER_MODULE(mainboard, eisa, mainboard_driver, mainboard_devclass, 0, 0); /* ** probe for EISA devices */ static int eisa_probe(device_t dev) { int i,slot; struct eisa_device *e_dev; + device_t child; int eisaBase = 0xc80; eisa_id_t eisa_id; int devices_found = 0; device_set_desc(dev, "EISA bus"); for (slot = 0; slot < num_eisa_slots; eisaBase+=0x1000, slot++) { int id_size = sizeof(eisa_id); eisa_id = 0; for( i = 0; i < id_size; i++ ) { outb(eisaBase,0x80 + i); /*Some cards require priming*/ eisa_id |= inb(eisaBase+i) << ((id_size-i-1)*CHAR_BIT); } if (eisa_id & 0x80000000) continue; /* no EISA card in slot */ devices_found++; /* Prepare an eisa_device_node for this slot */ e_dev = (struct eisa_device *)malloc(sizeof(*e_dev), M_DEVBUF, M_NOWAIT); if (!e_dev) { device_printf(dev, "cannot malloc eisa_device"); break; /* Try to attach what we have already */ } bzero(e_dev, sizeof(*e_dev)); e_dev->id = eisa_id; e_dev->ioconf.slot = slot; /* Initialize our lists of reserved addresses */ LIST_INIT(&(e_dev->ioconf.ioaddrs)); LIST_INIT(&(e_dev->ioconf.maddrs)); TAILQ_INIT(&(e_dev->ioconf.irqs)); - device_add_child(dev, NULL, -1, e_dev); + child = device_add_child(dev, NULL, -1); + device_set_ivars(child, e_dev); } /* * EISA busses themselves are not easily detectable, the easiest way * to tell if there is an eisa bus is if we found something - there * should be a motherboard "card" there somewhere. */ return devices_found ? 0 : ENXIO; } static void eisa_probe_nomatch(device_t dev, device_t child) { u_int32_t eisa_id = eisa_get_id(child); u_int8_t slot = eisa_get_slot(child); device_printf(dev, "unknown card %c%c%c%03x%01x (0x%08x) at slot %d\n", EISA_MFCTR_CHAR0(eisa_id), EISA_MFCTR_CHAR1(eisa_id), EISA_MFCTR_CHAR2(eisa_id), EISA_PRODUCT_ID(eisa_id), EISA_REVISION_ID(eisa_id), eisa_id, slot); return; } static void eisa_reg_print (dev, string, separator, column) device_t dev; char * string; char * separator; int * column; { int length = strlen(string); length += (separator ? 2 : 1); if (((*column) + length) >= MAX_COL) { printf("\n"); (*column) = 0; } else if ((*column) != 0) { if (separator) { printf("%c", *separator); (*column)++; } printf(" "); (*column)++; } if ((*column) == 0) { (*column) += device_printf(dev, "%s", string); } else { (*column) += printf("%s", string); } return; } static int eisa_print_child(device_t dev, device_t child) { char buf[81]; struct eisa_device * e_dev = device_get_ivars(child); int rid; struct irq_node * irq; struct resvaddr * resv; char separator = ','; int column = 0; int retval = 0; if (device_get_desc(child)) { snprintf(buf, sizeof(buf), "<%s>", device_get_desc(child)); eisa_reg_print(child, buf, NULL, &column); } rid = 0; while ((resv = eisa_find_ioaddr(e_dev, rid++))) { if ((resv->size == 1) || (resv->flags & RESVADDR_BITMASK)) { snprintf(buf, sizeof(buf), "%s%lx", ((rid == 1) ? "at 0x" : "0x"), resv->addr); } else { snprintf(buf, sizeof(buf), "%s%lx-0x%lx", ((rid == 1) ? "at 0x" : "0x"), resv->addr, (resv->addr + (resv->size - 1))); } eisa_reg_print(child, buf, ((rid == 2) ? &separator : NULL), &column); } rid = 0; while ((resv = eisa_find_maddr(e_dev, rid++))) { if ((resv->size == 1) || (resv->flags & RESVADDR_BITMASK)) { snprintf(buf, sizeof(buf), "%s%lx", ((rid == 1) ? "at 0x" : "0x"), resv->addr); } else { snprintf(buf, sizeof(buf), "%s%lx-0x%lx", ((rid == 1) ? "at 0x" : "0x"), resv->addr, (resv->addr + (resv->size - 1))); } eisa_reg_print(child, buf, ((rid == 2) ? &separator : NULL), &column); } rid = 0; while ((irq = eisa_find_irq(e_dev, rid++)) != NULL) { snprintf(buf, sizeof(buf), "irq %d (%s)", irq->irq_no, (irq->irq_trigger ? "level" : "edge")); eisa_reg_print(child, buf, ((rid == 1) ? &separator : NULL), &column); } snprintf(buf, sizeof(buf), "on %s slot %d\n", device_get_nameunit(dev), eisa_get_slot(child)); eisa_reg_print(child, buf, NULL, &column); return (retval); } static struct irq_node * eisa_find_irq(struct eisa_device *e_dev, int rid) { int i; struct irq_node *irq; for (i = 0, irq = TAILQ_FIRST(&e_dev->ioconf.irqs); i < rid && irq; i++, irq = TAILQ_NEXT(irq, links)) ; if (irq) return (irq); else return (NULL); } static struct resvaddr * eisa_find_maddr(struct eisa_device *e_dev, int rid) { int i; struct resvaddr *resv; for (i = 0, resv = LIST_FIRST(&e_dev->ioconf.maddrs); i < rid && resv; i++, resv = LIST_NEXT(resv, links)) ; return resv; } static struct resvaddr * eisa_find_ioaddr(struct eisa_device *e_dev, int rid) { int i; struct resvaddr *resv; for (i = 0, resv = LIST_FIRST(&e_dev->ioconf.ioaddrs); i < rid && resv; i++, resv = LIST_NEXT(resv, links)) ; return resv; } static int eisa_read_ivar(device_t dev, device_t child, int which, u_long *result) { struct eisa_device *e_dev = device_get_ivars(child); struct irq_node *irq; switch (which) { case EISA_IVAR_SLOT: *result = e_dev->ioconf.slot; break; case EISA_IVAR_ID: *result = e_dev->id; break; case EISA_IVAR_IRQ: /* XXX only first irq */ if ((irq = eisa_find_irq(e_dev, 0)) != NULL) { *result = irq->irq_no; } else { *result = -1; } break; default: return (ENOENT); } return (0); } static int eisa_write_ivar(device_t dev, device_t child, int which, uintptr_t value) { return (EINVAL); } static struct resource * eisa_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { int isdefault; struct eisa_device *e_dev = device_get_ivars(child); struct resource *rv, **rvp = 0; isdefault = (device_get_parent(child) == dev && start == 0UL && end == ~0UL && count == 1); switch (type) { case SYS_RES_IRQ: if (isdefault) { struct irq_node * irq = eisa_find_irq(e_dev, *rid); if (irq == NULL) return 0; start = end = irq->irq_no; count = 1; if (irq->irq_trigger == EISA_TRIGGER_LEVEL) { flags |= RF_SHAREABLE; } else { flags &= ~RF_SHAREABLE; } } break; case SYS_RES_MEMORY: if (isdefault) { struct resvaddr *resv; resv = eisa_find_maddr(e_dev, *rid); if (!resv) return 0; start = resv->addr; end = resv->addr + (resv->size - 1); count = resv->size; rvp = &resv->res; } break; case SYS_RES_IOPORT: if (isdefault) { struct resvaddr *resv; resv = eisa_find_ioaddr(e_dev, *rid); if (!resv) return 0; start = resv->addr; end = resv->addr + (resv->size - 1); count = resv->size; rvp = &resv->res; } break; default: return 0; } rv = BUS_ALLOC_RESOURCE(device_get_parent(dev), child, type, rid, start, end, count, flags); if (rvp) *rvp = rv; return rv; } static int eisa_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { int rv; struct eisa_device *e_dev = device_get_ivars(child); struct resvaddr *resv = 0; switch (type) { case SYS_RES_IRQ: if (eisa_find_irq(e_dev, rid) == NULL) return EINVAL; break; case SYS_RES_MEMORY: if (device_get_parent(child) == dev) resv = eisa_find_maddr(e_dev, rid); break; case SYS_RES_IOPORT: if (device_get_parent(child) == dev) resv = eisa_find_ioaddr(e_dev, rid); break; default: return (ENOENT); } rv = BUS_RELEASE_RESOURCE(device_get_parent(dev), child, type, rid, r); if (rv == 0) { if (resv) resv->res = 0; } return rv; } int eisa_add_intr(device_t dev, int irq, int trigger) { struct eisa_device *e_dev = device_get_ivars(dev); struct irq_node *irq_info; irq_info = (struct irq_node *)malloc(sizeof(*irq_info), M_DEVBUF, M_NOWAIT); if (irq_info == NULL) return (1); irq_info->irq_no = irq; irq_info->irq_trigger = trigger; irq_info->idesc = NULL; TAILQ_INSERT_TAIL(&e_dev->ioconf.irqs, irq_info, links); return 0; } static int eisa_add_resvaddr(struct eisa_device *e_dev, struct resvlist *head, u_long base, u_long size, int flags) { resvaddr_t *reservation; reservation = (resvaddr_t *)malloc(sizeof(resvaddr_t), M_DEVBUF, M_NOWAIT); if(!reservation) return (ENOMEM); reservation->addr = base; reservation->size = size; reservation->flags = flags; if (!head->lh_first) { LIST_INSERT_HEAD(head, reservation, links); } else { resvaddr_t *node; for(node = head->lh_first; node; node = node->links.le_next) { if (node->addr > reservation->addr) { /* * List is sorted in increasing * address order. */ LIST_INSERT_BEFORE(node, reservation, links); break; } if (node->addr == reservation->addr) { /* * If the entry we want to add * matches any already in here, * fail. */ free(reservation, M_DEVBUF); return (EEXIST); } if (!node->links.le_next) { LIST_INSERT_AFTER(node, reservation, links); break; } } } return (0); } int eisa_add_mspace(device_t dev, u_long mbase, u_long msize, int flags) { struct eisa_device *e_dev = device_get_ivars(dev); return eisa_add_resvaddr(e_dev, &(e_dev->ioconf.maddrs), mbase, msize, flags); } int eisa_add_iospace(device_t dev, u_long iobase, u_long iosize, int flags) { struct eisa_device *e_dev = device_get_ivars(dev); return eisa_add_resvaddr(e_dev, &(e_dev->ioconf.ioaddrs), iobase, iosize, flags); } static device_method_t eisa_methods[] = { /* Device interface */ DEVMETHOD(device_probe, eisa_probe), DEVMETHOD(device_attach, bus_generic_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, eisa_print_child), DEVMETHOD(bus_probe_nomatch, eisa_probe_nomatch), DEVMETHOD(bus_read_ivar, eisa_read_ivar), DEVMETHOD(bus_write_ivar, eisa_write_ivar), DEVMETHOD(bus_driver_added, bus_generic_driver_added), DEVMETHOD(bus_alloc_resource, eisa_alloc_resource), DEVMETHOD(bus_release_resource, eisa_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t eisa_driver = { "eisa", eisa_methods, 1, /* no softc */ }; DRIVER_MODULE(eisa, isab, eisa_driver, eisa_devclass, 0, 0); DRIVER_MODULE(eisa, nexus, eisa_driver, eisa_devclass, 0, 0); Index: head/sys/i386/i386/autoconf.c =================================================================== --- head/sys/i386/i386/autoconf.c (revision 54072) +++ head/sys/i386/i386/autoconf.c (revision 54073) @@ -1,322 +1,322 @@ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)autoconf.c 7.1 (Berkeley) 5/9/91 * $FreeBSD$ */ /* * Setup the system to run on the current machine. * * Configure() is called at boot time and initializes the vba * device tables and the memory controller monitoring. Available * devices are determined (from possibilities mentioned in ioconf.c), * and the drivers are initialized. */ #include "opt_bootp.h" #include "opt_ffs.h" #include "opt_cd9660.h" #include "opt_nfs.h" #include "opt_nfsroot.h" #include "opt_bus.h" #include "opt_rootdevname.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef APIC_IO #include #else #include #endif /* APIC_IO */ #include "isa.h" #include device_t isa_bus_device = 0; static void configure_first __P((void *)); static void configure __P((void *)); static void configure_final __P((void *)); #if defined(FFS) && defined(FFS_ROOT) static void setroot __P((void)); #endif SYSINIT(configure1, SI_SUB_CONFIGURE, SI_ORDER_FIRST, configure_first, NULL); /* SI_ORDER_SECOND is hookable */ SYSINIT(configure2, SI_SUB_CONFIGURE, SI_ORDER_THIRD, configure, NULL); /* SI_ORDER_MIDDLE is hookable */ SYSINIT(configure3, SI_SUB_CONFIGURE, SI_ORDER_ANY, configure_final, NULL); dev_t rootdev = NODEV; dev_t dumpdev = NODEV; device_t nexus_dev; /* * Determine i/o configuration for a machine. */ static void configure_first(dummy) void *dummy; { } static void configure(dummy) void *dummy; { /* * Activate the ICU's. Note that we are explicitly at splhigh() * at present as we have no way to disable stray PCI level triggered * interrupts until the devices have had a driver attached. This * is particularly a problem when the interrupts are shared. For * example, if IRQ 10 is shared between a disk and network device * and the disk device generates an interrupt, if we "activate" * IRQ 10 when the network driver is set up, then we will get * recursive interrupt 10's as nothing will know how to turn off * the disk device's interrupt. * * Having the ICU's active means we can probe interrupt routing to * see if a device causes the corresponding pending bit to be set. * * This is all rather inconvenient. */ #ifdef APIC_IO bsp_apic_configure(); enable_intr(); #else enable_intr(); INTREN(IRQ_SLAVE); #endif /* APIC_IO */ /* nexus0 is the top of the i386 device tree */ - device_add_child(root_bus, "nexus", 0, 0); + device_add_child(root_bus, "nexus", 0); /* initialize new bus architecture */ root_bus_configure(); #if NISA > 0 /* * Explicitly probe and attach ISA last. The isa bus saves * it's device node at attach time for us here. */ if (isa_bus_device) isa_probe_children(isa_bus_device); #endif /* * Now we're ready to handle (pending) interrupts. * XXX this is slightly misplaced. */ spl0(); /* * Allow lowering of the ipl to the lowest kernel level if we * panic (or call tsleep() before clearing `cold'). No level is * completely safe (since a panic may occur in a critical region * at splhigh()), but we want at least bio interrupts to work. */ safepri = cpl; } static void configure_final(dummy) void *dummy; { int i; cninit_finish(); if (bootverbose) { #ifdef APIC_IO imen_dump(); #endif /* APIC_IO */ /* * Print out the BIOS's idea of the disk geometries. */ printf("BIOS Geometries:\n"); for (i = 0; i < N_BIOS_GEOM; i++) { unsigned long bios_geom; int max_cylinder, max_head, max_sector; bios_geom = bootinfo.bi_bios_geom[i]; /* * XXX the bootstrap punts a 1200K floppy geometry * when the get-disk-geometry interrupt fails. Skip * drives that have this geometry. */ if (bios_geom == 0x4f010f) continue; printf(" %x:%08lx ", i, bios_geom); max_cylinder = bios_geom >> 16; max_head = (bios_geom >> 8) & 0xff; max_sector = bios_geom & 0xff; printf( "0..%d=%d cylinders, 0..%d=%d heads, 1..%d=%d sectors\n", max_cylinder, max_cylinder + 1, max_head, max_head + 1, max_sector, max_sector); } printf(" %d accounted for\n", bootinfo.bi_n_bios_used); printf("Device configuration finished.\n"); } cold = 0; } #ifdef BOOTP extern void bootpc_init(void); #endif /* * Do legacy root filesystem discovery. */ void cpu_rootconf() { #ifdef BOOTP bootpc_init(); #endif #if defined(NFS) && defined(NFS_ROOT) #if !defined(BOOTP_NFSROOT) if (nfs_diskless_valid) #endif rootdevnames[0] = "nfs:"; #endif #if defined(FFS) && defined(FFS_ROOT) if (!rootdevnames[0]) setroot(); #endif } SYSINIT(cpu_rootconf, SI_SUB_ROOT_CONF, SI_ORDER_FIRST, cpu_rootconf, NULL) u_long bootdev = 0; /* not a dev_t - encoding is different */ #if defined(FFS) && defined(FFS_ROOT) #define FDMAJOR 2 #define FDUNITSHIFT 6 /* * Attempt to find the device from which we were booted. * If we can do so, and not instructed not to do so, * set rootdevs[] and rootdevnames[] to correspond to the * boot device(s). * * This code survives in order to allow the system to be * booted from legacy environments that do not correctly * populate the kernel environment. There are significant * restrictions on the bootability of the system in this * situation; it can only be mounting root from a 'da' * 'wd' or 'fd' device, and the root filesystem must be ufs. */ static void setroot() { int majdev, mindev, unit, slice, part; dev_t newrootdev, dev; char partname[2]; char *sname; if ((bootdev & B_MAGICMASK) != B_DEVMAGIC) { printf("no B_DEVMAGIC (bootdev=%#lx)\n", bootdev); return; } majdev = B_TYPE(bootdev); dev = makebdev(majdev, 0); if (devsw(dev) == NULL) { printf("no devsw (majdev=%d bootdev=%#lx)\n", majdev, bootdev); return; } unit = B_UNIT(bootdev); slice = B_SLICE(bootdev); if (slice == WHOLE_DISK_SLICE) slice = COMPATIBILITY_SLICE; if (slice < 0 || slice >= MAX_SLICES) { printf("bad slice\n"); return; } /* * XXX kludge for inconsistent unit numbering and lack of slice * support for floppies. */ if (majdev == FDMAJOR) { slice = COMPATIBILITY_SLICE; part = RAW_PART; mindev = unit << FDUNITSHIFT; } else { part = B_PARTITION(bootdev); mindev = dkmakeminor(unit, slice, part); } newrootdev = makebdev(majdev, mindev); sname = dsname(newrootdev, unit, slice, part, partname); rootdevnames[0] = malloc(strlen(sname) + 6, M_DEVBUF, M_NOWAIT); sprintf(rootdevnames[0], "ufs:%s%s", sname, partname); /* * For properly dangerously dedicated disks (ones with a historical * bogus partition table), the boot blocks will give slice = 4, but * the kernel will only provide the compatibility slice since it * knows that slice 4 is not a real slice. Arrange to try mounting * the compatibility slice as root if mounting the slice passed by * the boot blocks fails. This handles the dangerously dedicated * case and perhaps others. */ if (slice == COMPATIBILITY_SLICE) return; slice = COMPATIBILITY_SLICE; sname = dsname(newrootdev, unit, slice, part, partname); rootdevnames[1] = malloc(strlen(sname) + 6, M_DEVBUF, M_NOWAIT); sprintf(rootdevnames[1], "ufs:%s%s", sname, partname); } #endif Index: head/sys/i386/i386/legacy.c =================================================================== --- head/sys/i386/i386/legacy.c (revision 54072) +++ head/sys/i386/i386/legacy.c (revision 54073) @@ -1,415 +1,416 @@ /* * Copyright 1998 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * This code implements a `root nexus' for Intel Architecture * machines. The function of the root nexus is to serve as an * attachment point for both processors and buses, and to manage * resources which are common to all of them. In particular, * this code implements the core resource managers for interrupt * requests, DMA requests (which rightfully should be a part of the * ISA code but it's easier to do it here for now), I/O port addresses, * and I/O memory address space. */ #include "opt_smp.h" #include "mca.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef APIC_IO #include #include #endif #ifdef PC98 #include #else #include #endif #include static struct rman irq_rman, drq_rman, port_rman, mem_rman; static int nexus_probe(device_t); static int nexus_attach(device_t); static int nexus_print_child(device_t, device_t); static device_t nexus_add_child(device_t bus, int order, const char *name, int unit); static struct resource *nexus_alloc_resource(device_t, device_t, int, int *, u_long, u_long, u_long, u_int); static int nexus_activate_resource(device_t, device_t, int, int, struct resource *); static int nexus_deactivate_resource(device_t, device_t, int, int, struct resource *); static int nexus_release_resource(device_t, device_t, int, int, struct resource *); static int nexus_setup_intr(device_t, device_t, struct resource *, int flags, void (*)(void *), void *, void **); static int nexus_teardown_intr(device_t, device_t, struct resource *, void *); static device_method_t nexus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, nexus_probe), DEVMETHOD(device_attach, nexus_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, nexus_print_child), DEVMETHOD(bus_add_child, nexus_add_child), DEVMETHOD(bus_read_ivar, bus_generic_read_ivar), DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), DEVMETHOD(bus_alloc_resource, nexus_alloc_resource), DEVMETHOD(bus_release_resource, nexus_release_resource), DEVMETHOD(bus_activate_resource, nexus_activate_resource), DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource), DEVMETHOD(bus_setup_intr, nexus_setup_intr), DEVMETHOD(bus_teardown_intr, nexus_teardown_intr), { 0, 0 } }; static driver_t nexus_driver = { "nexus", nexus_methods, 1, /* no softc */ }; static devclass_t nexus_devclass; DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); #ifdef APIC_IO #define LASTIRQ (NINTR - 1) #else #define LASTIRQ 15 #endif static int nexus_probe(device_t dev) { device_quiet(dev); /* suppress attach message for neatness */ /* * IRQ's are on the mainboard on old systems, but on the ISA part * of PCI->ISA bridges. There would be multiple sets of IRQs on * multi-ISA-bus systems. PCI interrupts are routed to the ISA * component, so in a way, PCI can be a partial child of an ISA bus(!). * APIC interrupts are global though. */ irq_rman.rm_start = 0; irq_rman.rm_end = LASTIRQ; irq_rman.rm_type = RMAN_ARRAY; irq_rman.rm_descr = "Interrupt request lines"; if (rman_init(&irq_rman) || rman_manage_region(&irq_rman, 0, 1) || rman_manage_region(&irq_rman, 3, LASTIRQ)) panic("nexus_probe irq_rman"); /* * ISA DMA on PCI systems is implemented in the ISA part of each * PCI->ISA bridge and the channels can be duplicated if there are * multiple bridges. (eg: laptops with docking stations) */ drq_rman.rm_start = 0; drq_rman.rm_end = 7; drq_rman.rm_type = RMAN_ARRAY; drq_rman.rm_descr = "DMA request lines"; /* XXX drq 0 not available on some machines */ if (rman_init(&drq_rman) || rman_manage_region(&drq_rman, 0, 7)) panic("nexus_probe drq_rman"); /* * However, IO ports and Memory truely are global at this level, * as are APIC interrupts (however many IO APICS there turn out * to be on large systems..) */ port_rman.rm_start = 0; port_rman.rm_end = 0xffff; port_rman.rm_type = RMAN_ARRAY; port_rman.rm_descr = "I/O ports"; if (rman_init(&port_rman) || rman_manage_region(&port_rman, 0, 0xffff)) panic("nexus_probe port_rman"); mem_rman.rm_start = 0; mem_rman.rm_end = ~0u; mem_rman.rm_type = RMAN_ARRAY; mem_rman.rm_descr = "I/O memory addresses"; if (rman_init(&mem_rman) || rman_manage_region(&mem_rman, 0, ~0)) panic("nexus_probe mem_rman"); return bus_generic_probe(dev); } static int nexus_attach(device_t dev) { device_t child; /* * First, deal with the children we know about already */ bus_generic_attach(dev); /* * And if we didn't see EISA or ISA on a pci bridge, create some * connection points now so they show up "on motherboard". */ if (!devclass_get_device(devclass_find("eisa"), 0)) { - child = device_add_child(dev, "eisa", 0, 0); + child = device_add_child(dev, "eisa", 0); if (child == NULL) panic("nexus_attach eisa"); device_probe_and_attach(child); } #if NMCA > 0 if (!devclass_get_device(devclass_find("mca"), 0)) { - child = device_add_child(dev, "mca", 0, 0); + child = device_add_child(dev, "mca", 0); if (child == 0) panic("nexus_probe mca"); device_probe_and_attach(child); } #endif if (!devclass_get_device(devclass_find("isa"), 0)) { - child = device_add_child(dev, "isa", 0, 0); + child = device_add_child(dev, "isa", 0); if (child == NULL) panic("nexus_attach isa"); device_probe_and_attach(child); } + return 0; } static int nexus_print_child(device_t bus, device_t child) { int retval = 0; retval += bus_print_child_header(bus, child); retval += printf(" on motherboard\n"); return (retval); } static device_t nexus_add_child(device_t bus, int order, const char *name, int unit) { - return device_add_child_ordered(bus, order, name, unit, 0); + return device_add_child_ordered(bus, order, name, unit); } /* * Allocate a resource on behalf of child. NB: child is usually going to be a * child of one of our descendants, not a direct child of nexus0. * (Exceptions include npx.) */ static struct resource * nexus_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct resource *rv; struct rman *rm; int needactivate = flags & RF_ACTIVE; flags &= ~RF_ACTIVE; switch (type) { case SYS_RES_IRQ: rm = &irq_rman; break; case SYS_RES_DRQ: rm = &drq_rman; break; case SYS_RES_IOPORT: rm = &port_rman; break; case SYS_RES_MEMORY: rm = &mem_rman; break; default: return 0; } rv = rman_reserve_resource(rm, start, end, count, flags, child); if (rv == 0) return 0; if (type == SYS_RES_MEMORY) { caddr_t vaddr = 0; if (rv->r_end < 1024 * 1024) { /* * The first 1Mb is mapped at KERNBASE. */ vaddr = (caddr_t)(uintptr_t)(KERNBASE + rv->r_start); } else { u_int32_t paddr; u_int32_t psize; u_int32_t poffs; paddr = rv->r_start; psize = rv->r_end - rv->r_start; poffs = paddr - trunc_page(paddr); vaddr = (caddr_t) pmap_mapdev(paddr-poffs, psize+poffs) + poffs; } rman_set_virtual(rv, vaddr); rman_set_bustag(rv, I386_BUS_SPACE_MEM); rman_set_bushandle(rv, (bus_space_handle_t) vaddr); } else if (type == SYS_RES_IOPORT) { rman_set_bustag(rv, I386_BUS_SPACE_IO); rman_set_bushandle(rv, rv->r_start); } if (needactivate) { if (bus_activate_resource(child, type, *rid, rv)) { rman_release_resource(rv); return 0; } } return rv; } static int nexus_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { return (rman_activate_resource(r)); } static int nexus_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { return (rman_deactivate_resource(r)); } static int nexus_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { if (r->r_flags & RF_ACTIVE) { int error = bus_deactivate_resource(child, type, rid, r); if (error) return error; } return (rman_release_resource(r)); } /* * Currently this uses the really grody interface from kern/kern_intr.c * (which really doesn't belong in kern/anything.c). Eventually, all of * the code in kern_intr.c and machdep_intr.c should get moved here, since * this is going to be the official interface. */ static int nexus_setup_intr(device_t bus, device_t child, struct resource *irq, int flags, void (*ihand)(void *), void *arg, void **cookiep) { intrmask_t *mask; driver_t *driver; int error, icflags; /* somebody tried to setup an irq that failed to allocate! */ if (irq == NULL) panic("nexus_setup_intr: NULL irq resource!"); *cookiep = 0; if (irq->r_flags & RF_SHAREABLE) icflags = 0; else icflags = INTR_EXCL; driver = device_get_driver(child); switch (flags) { case INTR_TYPE_TTY: mask = &tty_imask; break; case (INTR_TYPE_TTY | INTR_TYPE_FAST): mask = &tty_imask; icflags |= INTR_FAST; break; case INTR_TYPE_BIO: mask = &bio_imask; break; case INTR_TYPE_NET: mask = &net_imask; break; case INTR_TYPE_CAM: mask = &cam_imask; break; case INTR_TYPE_MISC: mask = 0; break; default: panic("still using grody create_intr interface"); } /* * We depend here on rman_activate_resource() being idempotent. */ error = rman_activate_resource(irq); if (error) return (error); *cookiep = inthand_add(device_get_nameunit(child), irq->r_start, ihand, arg, mask, icflags); if (*cookiep == NULL) error = EINVAL; /* XXX ??? */ return (error); } static int nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih) { return (inthand_remove(ih)); } Index: head/sys/i386/i386/nexus.c =================================================================== --- head/sys/i386/i386/nexus.c (revision 54072) +++ head/sys/i386/i386/nexus.c (revision 54073) @@ -1,415 +1,416 @@ /* * Copyright 1998 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * This code implements a `root nexus' for Intel Architecture * machines. The function of the root nexus is to serve as an * attachment point for both processors and buses, and to manage * resources which are common to all of them. In particular, * this code implements the core resource managers for interrupt * requests, DMA requests (which rightfully should be a part of the * ISA code but it's easier to do it here for now), I/O port addresses, * and I/O memory address space. */ #include "opt_smp.h" #include "mca.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef APIC_IO #include #include #endif #ifdef PC98 #include #else #include #endif #include static struct rman irq_rman, drq_rman, port_rman, mem_rman; static int nexus_probe(device_t); static int nexus_attach(device_t); static int nexus_print_child(device_t, device_t); static device_t nexus_add_child(device_t bus, int order, const char *name, int unit); static struct resource *nexus_alloc_resource(device_t, device_t, int, int *, u_long, u_long, u_long, u_int); static int nexus_activate_resource(device_t, device_t, int, int, struct resource *); static int nexus_deactivate_resource(device_t, device_t, int, int, struct resource *); static int nexus_release_resource(device_t, device_t, int, int, struct resource *); static int nexus_setup_intr(device_t, device_t, struct resource *, int flags, void (*)(void *), void *, void **); static int nexus_teardown_intr(device_t, device_t, struct resource *, void *); static device_method_t nexus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, nexus_probe), DEVMETHOD(device_attach, nexus_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, nexus_print_child), DEVMETHOD(bus_add_child, nexus_add_child), DEVMETHOD(bus_read_ivar, bus_generic_read_ivar), DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), DEVMETHOD(bus_alloc_resource, nexus_alloc_resource), DEVMETHOD(bus_release_resource, nexus_release_resource), DEVMETHOD(bus_activate_resource, nexus_activate_resource), DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource), DEVMETHOD(bus_setup_intr, nexus_setup_intr), DEVMETHOD(bus_teardown_intr, nexus_teardown_intr), { 0, 0 } }; static driver_t nexus_driver = { "nexus", nexus_methods, 1, /* no softc */ }; static devclass_t nexus_devclass; DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); #ifdef APIC_IO #define LASTIRQ (NINTR - 1) #else #define LASTIRQ 15 #endif static int nexus_probe(device_t dev) { device_quiet(dev); /* suppress attach message for neatness */ /* * IRQ's are on the mainboard on old systems, but on the ISA part * of PCI->ISA bridges. There would be multiple sets of IRQs on * multi-ISA-bus systems. PCI interrupts are routed to the ISA * component, so in a way, PCI can be a partial child of an ISA bus(!). * APIC interrupts are global though. */ irq_rman.rm_start = 0; irq_rman.rm_end = LASTIRQ; irq_rman.rm_type = RMAN_ARRAY; irq_rman.rm_descr = "Interrupt request lines"; if (rman_init(&irq_rman) || rman_manage_region(&irq_rman, 0, 1) || rman_manage_region(&irq_rman, 3, LASTIRQ)) panic("nexus_probe irq_rman"); /* * ISA DMA on PCI systems is implemented in the ISA part of each * PCI->ISA bridge and the channels can be duplicated if there are * multiple bridges. (eg: laptops with docking stations) */ drq_rman.rm_start = 0; drq_rman.rm_end = 7; drq_rman.rm_type = RMAN_ARRAY; drq_rman.rm_descr = "DMA request lines"; /* XXX drq 0 not available on some machines */ if (rman_init(&drq_rman) || rman_manage_region(&drq_rman, 0, 7)) panic("nexus_probe drq_rman"); /* * However, IO ports and Memory truely are global at this level, * as are APIC interrupts (however many IO APICS there turn out * to be on large systems..) */ port_rman.rm_start = 0; port_rman.rm_end = 0xffff; port_rman.rm_type = RMAN_ARRAY; port_rman.rm_descr = "I/O ports"; if (rman_init(&port_rman) || rman_manage_region(&port_rman, 0, 0xffff)) panic("nexus_probe port_rman"); mem_rman.rm_start = 0; mem_rman.rm_end = ~0u; mem_rman.rm_type = RMAN_ARRAY; mem_rman.rm_descr = "I/O memory addresses"; if (rman_init(&mem_rman) || rman_manage_region(&mem_rman, 0, ~0)) panic("nexus_probe mem_rman"); return bus_generic_probe(dev); } static int nexus_attach(device_t dev) { device_t child; /* * First, deal with the children we know about already */ bus_generic_attach(dev); /* * And if we didn't see EISA or ISA on a pci bridge, create some * connection points now so they show up "on motherboard". */ if (!devclass_get_device(devclass_find("eisa"), 0)) { - child = device_add_child(dev, "eisa", 0, 0); + child = device_add_child(dev, "eisa", 0); if (child == NULL) panic("nexus_attach eisa"); device_probe_and_attach(child); } #if NMCA > 0 if (!devclass_get_device(devclass_find("mca"), 0)) { - child = device_add_child(dev, "mca", 0, 0); + child = device_add_child(dev, "mca", 0); if (child == 0) panic("nexus_probe mca"); device_probe_and_attach(child); } #endif if (!devclass_get_device(devclass_find("isa"), 0)) { - child = device_add_child(dev, "isa", 0, 0); + child = device_add_child(dev, "isa", 0); if (child == NULL) panic("nexus_attach isa"); device_probe_and_attach(child); } + return 0; } static int nexus_print_child(device_t bus, device_t child) { int retval = 0; retval += bus_print_child_header(bus, child); retval += printf(" on motherboard\n"); return (retval); } static device_t nexus_add_child(device_t bus, int order, const char *name, int unit) { - return device_add_child_ordered(bus, order, name, unit, 0); + return device_add_child_ordered(bus, order, name, unit); } /* * Allocate a resource on behalf of child. NB: child is usually going to be a * child of one of our descendants, not a direct child of nexus0. * (Exceptions include npx.) */ static struct resource * nexus_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct resource *rv; struct rman *rm; int needactivate = flags & RF_ACTIVE; flags &= ~RF_ACTIVE; switch (type) { case SYS_RES_IRQ: rm = &irq_rman; break; case SYS_RES_DRQ: rm = &drq_rman; break; case SYS_RES_IOPORT: rm = &port_rman; break; case SYS_RES_MEMORY: rm = &mem_rman; break; default: return 0; } rv = rman_reserve_resource(rm, start, end, count, flags, child); if (rv == 0) return 0; if (type == SYS_RES_MEMORY) { caddr_t vaddr = 0; if (rv->r_end < 1024 * 1024) { /* * The first 1Mb is mapped at KERNBASE. */ vaddr = (caddr_t)(uintptr_t)(KERNBASE + rv->r_start); } else { u_int32_t paddr; u_int32_t psize; u_int32_t poffs; paddr = rv->r_start; psize = rv->r_end - rv->r_start; poffs = paddr - trunc_page(paddr); vaddr = (caddr_t) pmap_mapdev(paddr-poffs, psize+poffs) + poffs; } rman_set_virtual(rv, vaddr); rman_set_bustag(rv, I386_BUS_SPACE_MEM); rman_set_bushandle(rv, (bus_space_handle_t) vaddr); } else if (type == SYS_RES_IOPORT) { rman_set_bustag(rv, I386_BUS_SPACE_IO); rman_set_bushandle(rv, rv->r_start); } if (needactivate) { if (bus_activate_resource(child, type, *rid, rv)) { rman_release_resource(rv); return 0; } } return rv; } static int nexus_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { return (rman_activate_resource(r)); } static int nexus_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { return (rman_deactivate_resource(r)); } static int nexus_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { if (r->r_flags & RF_ACTIVE) { int error = bus_deactivate_resource(child, type, rid, r); if (error) return error; } return (rman_release_resource(r)); } /* * Currently this uses the really grody interface from kern/kern_intr.c * (which really doesn't belong in kern/anything.c). Eventually, all of * the code in kern_intr.c and machdep_intr.c should get moved here, since * this is going to be the official interface. */ static int nexus_setup_intr(device_t bus, device_t child, struct resource *irq, int flags, void (*ihand)(void *), void *arg, void **cookiep) { intrmask_t *mask; driver_t *driver; int error, icflags; /* somebody tried to setup an irq that failed to allocate! */ if (irq == NULL) panic("nexus_setup_intr: NULL irq resource!"); *cookiep = 0; if (irq->r_flags & RF_SHAREABLE) icflags = 0; else icflags = INTR_EXCL; driver = device_get_driver(child); switch (flags) { case INTR_TYPE_TTY: mask = &tty_imask; break; case (INTR_TYPE_TTY | INTR_TYPE_FAST): mask = &tty_imask; icflags |= INTR_FAST; break; case INTR_TYPE_BIO: mask = &bio_imask; break; case INTR_TYPE_NET: mask = &net_imask; break; case INTR_TYPE_CAM: mask = &cam_imask; break; case INTR_TYPE_MISC: mask = 0; break; default: panic("still using grody create_intr interface"); } /* * We depend here on rman_activate_resource() being idempotent. */ error = rman_activate_resource(irq); if (error) return (error); *cookiep = inthand_add(device_get_nameunit(child), irq->r_start, ihand, arg, mask, icflags); if (*cookiep == NULL) error = EINVAL; /* XXX ??? */ return (error); } static int nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih) { return (inthand_remove(ih)); } Index: head/sys/i386/isa/pcf.c =================================================================== --- head/sys/i386/isa/pcf.c (revision 54072) +++ head/sys/i386/isa/pcf.c (revision 54073) @@ -1,646 +1,646 @@ /*- * Copyright (c) 1998 Nicolas Souchu, Marc Bouget * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * */ #include #include #include #include #include #include #include #include #include #include "iicbus_if.h" #define TIMEOUT 9999 /* XXX */ /* Status bits of S1 register (read only) */ #define nBB 0x01 /* busy when low set/reset by STOP/START*/ #define LAB 0x02 /* lost arbitration bit in multi-master mode */ #define AAS 0x04 /* addressed as slave */ #define LRB 0x08 /* last received byte when not AAS */ #define AD0 0x08 /* general call received when AAS */ #define BER 0x10 /* bus error, misplaced START or STOP */ #define STS 0x20 /* STOP detected in slave receiver mode */ #define PIN 0x80 /* pending interrupt not (r/w) */ /* Control bits of S1 register (write only) */ #define ACK 0x01 #define STO 0x02 #define STA 0x04 #define ENI 0x08 #define ES2 0x10 #define ES1 0x20 #define ES0 0x40 #define BUFSIZE 2048 #define SLAVE_TRANSMITTER 0x1 #define SLAVE_RECEIVER 0x2 #define PCF_DEFAULT_ADDR 0xaa struct pcf_softc { int pcf_base; /* isa port */ u_char pcf_addr; /* interface I2C address */ int pcf_slave_mode; /* receiver or transmitter */ int pcf_started; /* 1 if start condition sent */ device_t iicbus; /* the corresponding iicbus */ }; struct pcf_isa_softc { int pcf_unit; /* unit of the isa device */ int pcf_base; /* isa port */ int pcf_irq; /* isa irq or null if polled */ unsigned int pcf_flags; /* boot flags */ }; #define MAXPCF 2 static struct pcf_isa_softc *pcfdata[MAXPCF]; static int npcf = 0; static int pcfprobe_isa(struct isa_device *); static int pcfattach_isa(struct isa_device *); struct isa_driver pcfdriver = { pcfprobe_isa, pcfattach_isa, "pcf" }; static int pcf_probe(device_t); static int pcf_attach(device_t); static int pcf_print_child(device_t, device_t); static int pcf_repeated_start(device_t, u_char, int); static int pcf_start(device_t, u_char, int); static int pcf_stop(device_t); static int pcf_write(device_t, char *, int, int *, int); static int pcf_read(device_t, char *, int, int *, int, int); static ointhand2_t pcfintr; static int pcf_rst_card(device_t, u_char, u_char, u_char *); static device_method_t pcf_methods[] = { /* device interface */ DEVMETHOD(device_probe, pcf_probe), DEVMETHOD(device_attach, pcf_attach), /* bus interface */ DEVMETHOD(bus_print_child, pcf_print_child), /* iicbus interface */ DEVMETHOD(iicbus_callback, iicbus_null_callback), DEVMETHOD(iicbus_repeated_start, pcf_repeated_start), DEVMETHOD(iicbus_start, pcf_start), DEVMETHOD(iicbus_stop, pcf_stop), DEVMETHOD(iicbus_write, pcf_write), DEVMETHOD(iicbus_read, pcf_read), DEVMETHOD(iicbus_reset, pcf_rst_card), { 0, 0 } }; static driver_t pcf_driver = { "pcf", pcf_methods, sizeof(struct pcf_softc), }; static devclass_t pcf_devclass; #define DEVTOSOFTC(dev) ((struct pcf_softc *)device_get_softc(dev)) static int pcfprobe_isa(struct isa_device *dvp) { device_t pcfdev; struct pcf_isa_softc *pcf; if (npcf >= MAXPCF) return (0); if ((pcf = (struct pcf_isa_softc *)malloc(sizeof(struct pcf_isa_softc), M_DEVBUF, M_NOWAIT)) == NULL) return (0); pcf->pcf_base = dvp->id_iobase; /* XXX should be ivars */ pcf->pcf_unit = dvp->id_unit; if (!(dvp->id_flags & IIC_POLLED)) pcf->pcf_irq = (dvp->id_irq); pcfdata[npcf++] = pcf; /* XXX add the pcf device to the root_bus until isa bus exists */ - pcfdev = device_add_child(root_bus, "pcf", pcf->pcf_unit, NULL); + pcfdev = device_add_child(root_bus, "pcf", pcf->pcf_unit); if (!pcfdev) goto error; return (1); error: free(pcf, M_DEVBUF); return (0); } static int pcfattach_isa(struct isa_device *isdp) { isdp->id_ointr = pcfintr; return (1); /* ok */ } static int pcf_probe(device_t pcfdev) { struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(pcfdev); int unit = device_get_unit(pcfdev); /* retrieve base address from isa initialization * * XXX should use ivars with isabus */ pcf->pcf_base = pcfdata[unit]->pcf_base; /* reset the chip */ pcf_rst_card(pcfdev, IIC_FASTEST, PCF_DEFAULT_ADDR, NULL); /* XXX try do detect chipset */ device_set_desc(pcfdev, "PCF8584 I2C bus controller"); return (0); } static int pcf_attach(device_t pcfdev) { struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(pcfdev); pcf->iicbus = iicbus_alloc_bus(pcfdev); /* probe and attach the iicbus */ device_probe_and_attach(pcf->iicbus); return (0); } static int pcf_print_child(device_t bus, device_t dev) { struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(bus); int retval = 0; retval += bus_print_child_header(bus, dev); retval += printf(" on %s addr 0x%x\n", device_get_nameunit(bus), (int)pcf->pcf_addr); return (retval); } /* * PCF8584 datasheet : when operate at 8 MHz or more, a minimun time of * 6 clocks cycles must be left between two consecutives access */ #define pcf_nops() DELAY(10) #define dummy_read(pcf) PCF_GET_S0(pcf) #define dummy_write(pcf) PCF_SET_S0(pcf, 0) /* * Specific register access to PCF8584 */ static void PCF_SET_S0(struct pcf_softc *pcf, int data) { outb(pcf->pcf_base, data); pcf_nops(); } static void PCF_SET_S1(struct pcf_softc *pcf, int data) { outb(pcf->pcf_base+1, data); pcf_nops(); } static char PCF_GET_S0(struct pcf_softc *pcf) { char data; data = inb(pcf->pcf_base); pcf_nops(); return (data); } static char PCF_GET_S1(struct pcf_softc *pcf) { char data; data = inb(pcf->pcf_base+1); pcf_nops(); return (data); } /* * Polling mode for master operations wait for a new * byte incomming or outgoing */ static int pcf_wait_byte(struct pcf_softc *pcf) { int counter = TIMEOUT; while (counter--) { if ((PCF_GET_S1(pcf) & PIN) == 0) return (0); } return (IIC_ETIMEOUT); } static int pcf_stop(device_t pcfdev) { struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); /* * Send STOP condition iff the START condition was previously sent. * STOP is sent only once even if a iicbus_stop() is called after * an iicbus_read()... see pcf_read(): the pcf needs to send the stop * before the last char is read. */ if (pcf->pcf_started) { /* set stop condition and enable IT */ PCF_SET_S1(pcf, PIN|ES0|ENI|STO|ACK); pcf->pcf_started = 0; } return (0); } static int pcf_noack(struct pcf_softc *pcf, int timeout) { int noack; int k = timeout/10; do { noack = PCF_GET_S1(pcf) & LRB; if (!noack) break; DELAY(10); /* XXX wait 10 us */ } while (k--); return (noack); } static int pcf_repeated_start(device_t pcfdev, u_char slave, int timeout) { struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); int error = 0; /* repeated start */ PCF_SET_S1(pcf, ES0|STA|STO|ACK); /* set slave address to PCF. Last bit (LSB) must be set correctly * according to transfer direction */ PCF_SET_S0(pcf, slave); /* wait for address sent, polling */ if ((error = pcf_wait_byte(pcf))) goto error; /* check for ack */ if (pcf_noack(pcf, timeout)) { error = IIC_ENOACK; goto error; } return (0); error: pcf_stop(pcfdev); return (error); } static int pcf_start(device_t pcfdev, u_char slave, int timeout) { struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); int error = 0; if ((PCF_GET_S1(pcf) & nBB) == 0) return (IIC_EBUSBSY); /* set slave address to PCF. Last bit (LSB) must be set correctly * according to transfer direction */ PCF_SET_S0(pcf, slave); /* START only */ PCF_SET_S1(pcf, PIN|ES0|STA|ACK); pcf->pcf_started = 1; /* wait for address sent, polling */ if ((error = pcf_wait_byte(pcf))) goto error; /* check for ACK */ if (pcf_noack(pcf, timeout)) { error = IIC_ENOACK; goto error; } return (0); error: pcf_stop(pcfdev); return (error); } static void pcfintr(unit) { struct pcf_softc *pcf = (struct pcf_softc *)devclass_get_softc(pcf_devclass, unit); char data, status, addr; char error = 0; status = PCF_GET_S1(pcf); if (status & PIN) { printf("pcf%d: spurious interrupt, status=0x%x\n", unit, status & 0xff); goto error; } if (status & LAB) printf("pcf%d: bus arbitration lost!\n", unit); if (status & BER) { error = IIC_EBUSERR; iicbus_intr(pcf->iicbus, INTR_ERROR, &error); goto error; } do { status = PCF_GET_S1(pcf); switch(pcf->pcf_slave_mode) { case SLAVE_TRANSMITTER: if (status & LRB) { /* ack interrupt line */ dummy_write(pcf); /* no ack, don't send anymore */ pcf->pcf_slave_mode = SLAVE_RECEIVER; iicbus_intr(pcf->iicbus, INTR_NOACK, NULL); break; } /* get data from upper code */ iicbus_intr(pcf->iicbus, INTR_TRANSMIT, &data); PCF_SET_S0(pcf, data); break; case SLAVE_RECEIVER: if (status & AAS) { addr = PCF_GET_S0(pcf); if (status & AD0) iicbus_intr(pcf->iicbus, INTR_GENERAL, &addr); else iicbus_intr(pcf->iicbus, INTR_START, &addr); if (addr & LSB) { pcf->pcf_slave_mode = SLAVE_TRANSMITTER; /* get the first char from upper code */ iicbus_intr(pcf->iicbus, INTR_TRANSMIT, &data); /* send first data byte */ PCF_SET_S0(pcf, data); } break; } /* stop condition received? */ if (status & STS) { /* ack interrupt line */ dummy_read(pcf); /* emulate intr stop condition */ iicbus_intr(pcf->iicbus, INTR_STOP, NULL); } else { /* get data, ack interrupt line */ data = PCF_GET_S0(pcf); /* deliver the character */ iicbus_intr(pcf->iicbus, INTR_RECEIVE, &data); } break; default: panic("%s: unknown slave mode (%d)!", __FUNCTION__, pcf->pcf_slave_mode); } } while ((PCF_GET_S1(pcf) & PIN) == 0); return; error: /* unknown event on bus...reset PCF */ PCF_SET_S1(pcf, PIN|ES0|ENI|ACK); pcf->pcf_slave_mode = SLAVE_RECEIVER; return; } static int pcf_rst_card(device_t pcfdev, u_char speed, u_char addr, u_char *oldaddr) { struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); if (oldaddr) *oldaddr = pcf->pcf_addr; /* retrieve own address from bus level */ if (!addr) pcf->pcf_addr = PCF_DEFAULT_ADDR; else pcf->pcf_addr = addr; PCF_SET_S1(pcf, PIN); /* initialize S1 */ /* own address S'O<>0 */ PCF_SET_S0(pcf, pcf->pcf_addr >> 1); /* select clock register */ PCF_SET_S1(pcf, PIN|ES1); /* select bus speed : 18=90kb, 19=45kb, 1A=11kb, 1B=1.5kb */ switch (speed) { case IIC_SLOW: PCF_SET_S0(pcf, 0x1b); break; case IIC_FAST: PCF_SET_S0(pcf, 0x19); break; case IIC_UNKNOWN: case IIC_FASTEST: default: PCF_SET_S0(pcf, 0x18); break; } /* set bus on, ack=yes, INT=yes */ PCF_SET_S1(pcf, PIN|ES0|ENI|ACK); pcf->pcf_slave_mode = SLAVE_RECEIVER; return (0); } static int pcf_write(device_t pcfdev, char *buf, int len, int *sent, int timeout /* us */) { struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); int bytes, error = 0; #ifdef PCFDEBUG printf("pcf%d: >> writing %d bytes\n", device_get_unit(pcfdev), len); #endif bytes = 0; while (len) { PCF_SET_S0(pcf, *buf++); /* wait for the byte to be send */ if ((error = pcf_wait_byte(pcf))) goto error; /* check if ack received */ if (pcf_noack(pcf, timeout)) { error = IIC_ENOACK; goto error; } len --; bytes ++; } error: *sent = bytes; #ifdef PCFDEBUG printf("pcf%d: >> %d bytes written (%d)\n", device_get_unit(pcfdev), bytes, error); #endif return (error); } static int pcf_read(device_t pcfdev, char *buf, int len, int *read, int last, int delay /* us */) { struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); int bytes, error = 0; #ifdef PCFDEBUG printf("pcf%d: << reading %d bytes\n", device_get_unit(pcfdev), len); #endif /* trig the bus to get the first data byte in S0 */ if (len) { if (len == 1 && last) /* just one byte to read */ PCF_SET_S1(pcf, ES0); /* no ack */ dummy_read(pcf); } bytes = 0; while (len) { /* XXX delay needed here */ /* wait for trigged byte */ if ((error = pcf_wait_byte(pcf))) { pcf_stop(pcfdev); goto error; } if (len == 1 && last) /* ok, last data byte already in S0, no I2C activity * on next PCF_GET_S0() */ pcf_stop(pcfdev); else if (len == 2 && last) /* next trigged byte with no ack */ PCF_SET_S1(pcf, ES0); /* receive byte, trig next byte */ *buf++ = PCF_GET_S0(pcf); len --; bytes ++; }; error: *read = bytes; #ifdef PCFDEBUG printf("pcf%d: << %d bytes read (%d)\n", device_get_unit(pcfdev), bytes, error); #endif return (error); } DRIVER_MODULE(pcf, root, pcf_driver, pcf_devclass, 0, 0); Index: head/sys/i386/isa/pcibus.c =================================================================== --- head/sys/i386/isa/pcibus.c (revision 54072) +++ head/sys/i386/isa/pcibus.c (revision 54073) @@ -1,514 +1,514 @@ /* * Copyright (c) 1997, Stefan Esser * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * */ #include #include #include #include #include #include #include #ifdef PCI_COMPAT /* XXX this is a terrible hack, which keeps the Tekram AMD SCSI driver happy */ #define cfgmech pci_mechanism int cfgmech; #else static int cfgmech; #endif /* PCI_COMPAT */ static int devmax; /* enable configuration space accesses and return data port address */ static int pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) { int dataport = 0; if (bus <= PCI_BUSMAX && slot < devmax && func <= PCI_FUNCMAX && reg <= PCI_REGMAX && bytes != 3 && (unsigned) bytes <= 4 && (reg & (bytes -1)) == 0) { switch (cfgmech) { case 1: outl(CONF1_ADDR_PORT, (1 << 31) | (bus << 16) | (slot << 11) | (func << 8) | (reg & ~0x03)); dataport = CONF1_DATA_PORT + (reg & 0x03); break; case 2: outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); outb(CONF2_FORWARD_PORT, bus); dataport = 0xc000 | (slot << 8) | reg; break; } } return (dataport); } /* disable configuration space accesses */ static void pci_cfgdisable(void) { switch (cfgmech) { case 1: outl(CONF1_ADDR_PORT, 0); break; case 2: outb(CONF2_ENABLE_PORT, 0); outb(CONF2_FORWARD_PORT, 0); break; } } /* read configuration space register */ int pci_cfgread(pcicfgregs *cfg, int reg, int bytes) { int data = -1; int port; port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); if (port != 0) { switch (bytes) { case 1: data = inb(port); break; case 2: data = inw(port); break; case 4: data = inl(port); break; } pci_cfgdisable(); } return (data); } /* write configuration space register */ void pci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes) { int port; port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); if (port != 0) { switch (bytes) { case 1: outb(port, data); break; case 2: outw(port, data); break; case 4: outl(port, data); break; } pci_cfgdisable(); } } /* check whether the configuration mechanism has been correct identified */ static int pci_cfgcheck(int maxdev) { u_char device; if (bootverbose) printf("pci_cfgcheck:\tdevice "); for (device = 0; device < maxdev; device++) { unsigned id, class, header; if (bootverbose) printf("%d ", device); id = inl(pci_cfgenable(0, device, 0, 0, 4)); if (id == 0 || id == -1) continue; class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8; if (bootverbose) printf("[class=%06x] ", class); if (class == 0 || (class & 0xf870ff) != 0) continue; header = inb(pci_cfgenable(0, device, 0, 14, 1)); if (bootverbose) printf("[hdr=%02x] ", header); if ((header & 0x7e) != 0) continue; if (bootverbose) printf("is there (id=%08x)\n", id); pci_cfgdisable(); return (1); } if (bootverbose) printf("-- nothing found\n"); pci_cfgdisable(); return (0); } static int pci_cfgopen(void) { unsigned long mode1res,oldval1; unsigned char mode2res,oldval2; oldval1 = inl(CONF1_ADDR_PORT); if (bootverbose) { printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", oldval1); } if ((oldval1 & CONF1_ENABLE_MSK) == 0) { cfgmech = 1; devmax = 32; outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); outb(CONF1_ADDR_PORT +3, 0); mode1res = inl(CONF1_ADDR_PORT); outl(CONF1_ADDR_PORT, oldval1); if (bootverbose) printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n", mode1res, CONF1_ENABLE_CHK); if (mode1res) { if (pci_cfgcheck(32)) return (cfgmech); } outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); mode1res = inl(CONF1_ADDR_PORT); outl(CONF1_ADDR_PORT, oldval1); if (bootverbose) printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n", mode1res, CONF1_ENABLE_CHK1); if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { if (pci_cfgcheck(32)) return (cfgmech); } } oldval2 = inb(CONF2_ENABLE_PORT); if (bootverbose) { printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2); } if ((oldval2 & 0xf0) == 0) { cfgmech = 2; devmax = 16; outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); mode2res = inb(CONF2_ENABLE_PORT); outb(CONF2_ENABLE_PORT, oldval2); if (bootverbose) printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", mode2res, CONF2_ENABLE_CHK); if (mode2res == CONF2_ENABLE_RES) { if (bootverbose) printf("pci_open(2a):\tnow trying mechanism 2\n"); if (pci_cfgcheck(16)) return (cfgmech); } } cfgmech = 0; devmax = 0; return (cfgmech); } static devclass_t pcib_devclass; static const char * nexus_pcib_is_host_bridge(pcicfgregs *cfg, u_int32_t id, u_int8_t class, u_int8_t subclass, u_int8_t *busnum) { const char *s = NULL; static u_int8_t pxb[4]; /* hack for 450nx */ *busnum = 0; switch (id) { case 0x12258086: s = "Intel 824?? host to PCI bridge"; /* XXX This is a guess */ *busnum = pci_cfgread(cfg, 0x41, 1); break; case 0x71808086: s = "Intel 82443LX (440 LX) host to PCI bridge"; break; case 0x71908086: s = "Intel 82443BX (440 BX) host to PCI bridge"; break; case 0x71928086: s = "Intel 82443BX host to PCI bridge (AGP disabled)"; break; case 0x71a08086: s = "Intel 82443GX host to PCI bridge"; break; case 0x71a18086: s = "Intel 82443GX host to AGP bridge"; break; case 0x71a28086: s = "Intel 82443GX host to PCI bridge (AGP disabled)"; break; case 0x84c48086: s = "Intel 82454KX/GX (Orion) host to PCI bridge"; *busnum = pci_cfgread(cfg, 0x4a, 1); break; case 0x84ca8086: /* * For the 450nx chipset, there is a whole bundle of * things pretending to be host bridges. The MIOC will * be seen first and isn't really a pci bridge (the * actual busses are attached to the PXB's). We need to * read the registers of the MIOC to figure out the * bus numbers for the PXB channels. * * Since the MIOC doesn't have a pci bus attached, we * pretend it wasn't there. */ pxb[0] = pci_cfgread(cfg, 0xd0, 1); /* BUSNO[0] */ pxb[1] = pci_cfgread(cfg, 0xd1, 1) + 1; /* SUBA[0]+1 */ pxb[2] = pci_cfgread(cfg, 0xd3, 1); /* BUSNO[1] */ pxb[3] = pci_cfgread(cfg, 0xd4, 1) + 1; /* SUBA[1]+1 */ return NULL; case 0x84cb8086: switch (cfg->slot) { case 0x12: s = "Intel 82454NX PXB#0, Bus#A"; *busnum = pxb[0]; break; case 0x13: s = "Intel 82454NX PXB#0, Bus#B"; *busnum = pxb[1]; break; case 0x14: s = "Intel 82454NX PXB#1, Bus#A"; *busnum = pxb[2]; break; case 0x15: s = "Intel 82454NX PXB#1, Bus#B"; *busnum = pxb[3]; break; } break; /* AMD -- vendor 0x1022 */ case 0x70061022: s = "AMD-751 host to PCI bridge"; break; /* SiS -- vendor 0x1039 */ case 0x04961039: s = "SiS 85c496"; break; case 0x04061039: s = "SiS 85c501"; break; case 0x06011039: s = "SiS 85c601"; break; case 0x55911039: s = "SiS 5591 host to PCI bridge"; break; case 0x00011039: s = "SiS 5591 host to AGP bridge"; break; /* VLSI -- vendor 0x1004 */ case 0x00051004: s = "VLSI 82C592 Host to PCI bridge"; break; /* XXX Here is MVP3, I got the datasheet but NO M/B to test it */ /* totally. Please let me know if anything wrong. -F */ /* XXX need info on the MVP3 -- any takers? */ case 0x05981106: s = "VIA 82C598MVP (Apollo MVP3) host bridge"; break; /* AcerLabs -- vendor 0x10b9 */ /* Funny : The datasheet told me vendor id is "10b8",sub-vendor */ /* id is '10b9" but the register always shows "10b9". -Foxfair */ case 0x154110b9: s = "AcerLabs M1541 (Aladdin-V) PCI host bridge"; break; /* OPTi -- vendor 0x1045 */ case 0xc8221045: s = "OPTi 82C822 host to PCI Bridge"; break; /* Ross (?) -- vendor 0x1166 */ case 0x00051166: s = "Ross (?) host to PCI bridge"; /* just guessing the secondary bus register number ... */ *busnum = pci_cfgread(cfg, 0x45, 1); break; /* Integrated Micro Solutions -- vendor 0x10e0 */ case 0x884910e0: s = "Integrated Micro Solutions VL Bridge"; break; default: if (class == PCIC_BRIDGE && subclass == PCIS_BRIDGE_HOST) s = "Host to PCI bridge"; break; } return s; } /* * Scan the first pci bus for host-pci bridges and add pcib instances * to the nexus for each bridge. */ static void nexus_pcib_identify(driver_t *driver, device_t parent) { pcicfgregs probe; int found = 0; if (pci_cfgopen() == 0) return; probe.hose = 0; probe.bus = 0; for (probe.slot = 0; probe.slot <= PCI_SLOTMAX; probe.slot++) { int pcifunchigh = 0; for (probe.func = 0; probe.func <= pcifunchigh; probe.func++) { /* * Read the IDs and class from the device. */ u_int32_t id; u_int8_t class, subclass, busnum; device_t child; const char *s; id = pci_cfgread(&probe, PCIR_DEVVENDOR, 4); if (id == -1) continue; class = pci_cfgread(&probe, PCIR_CLASS, 1); subclass = pci_cfgread(&probe, PCIR_SUBCLASS, 1); s = nexus_pcib_is_host_bridge(&probe, id, class, subclass, &busnum); if (s) { /* * Add at priority 100 to make sure we * go after any motherboard resources */ child = BUS_ADD_CHILD(parent, 100, "pcib", busnum); device_set_desc(child, s); found = 1; } } } /* * Make sure we add at least one bridge since some old * hardware doesn't actually have a host-pci bridge device. * Note that pci_cfgopen() thinks we have PCI devices.. */ if (!found) { if (bootverbose) printf( "nexus_pcib_identify: no bridge found, adding pcib0 anyway\n"); BUS_ADD_CHILD(parent, 100, "pcib", 0); } } static int nexus_pcib_probe(device_t dev) { if (pci_cfgopen() != 0) { - device_add_child(dev, "pci", device_get_unit(dev), 0); + device_add_child(dev, "pci", device_get_unit(dev)); return 0; } return ENXIO; } static device_method_t nexus_pcib_methods[] = { /* Device interface */ DEVMETHOD(device_identify, nexus_pcib_identify), DEVMETHOD(device_probe, nexus_pcib_probe), DEVMETHOD(device_attach, bus_generic_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t nexus_pcib_driver = { "pcib", nexus_pcib_methods, 1, }; DRIVER_MODULE(pcib, nexus, nexus_pcib_driver, pcib_devclass, 0, 0); Index: head/sys/i386/pci/pci_bus.c =================================================================== --- head/sys/i386/pci/pci_bus.c (revision 54072) +++ head/sys/i386/pci/pci_bus.c (revision 54073) @@ -1,514 +1,514 @@ /* * Copyright (c) 1997, Stefan Esser * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * */ #include #include #include #include #include #include #include #ifdef PCI_COMPAT /* XXX this is a terrible hack, which keeps the Tekram AMD SCSI driver happy */ #define cfgmech pci_mechanism int cfgmech; #else static int cfgmech; #endif /* PCI_COMPAT */ static int devmax; /* enable configuration space accesses and return data port address */ static int pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) { int dataport = 0; if (bus <= PCI_BUSMAX && slot < devmax && func <= PCI_FUNCMAX && reg <= PCI_REGMAX && bytes != 3 && (unsigned) bytes <= 4 && (reg & (bytes -1)) == 0) { switch (cfgmech) { case 1: outl(CONF1_ADDR_PORT, (1 << 31) | (bus << 16) | (slot << 11) | (func << 8) | (reg & ~0x03)); dataport = CONF1_DATA_PORT + (reg & 0x03); break; case 2: outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); outb(CONF2_FORWARD_PORT, bus); dataport = 0xc000 | (slot << 8) | reg; break; } } return (dataport); } /* disable configuration space accesses */ static void pci_cfgdisable(void) { switch (cfgmech) { case 1: outl(CONF1_ADDR_PORT, 0); break; case 2: outb(CONF2_ENABLE_PORT, 0); outb(CONF2_FORWARD_PORT, 0); break; } } /* read configuration space register */ int pci_cfgread(pcicfgregs *cfg, int reg, int bytes) { int data = -1; int port; port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); if (port != 0) { switch (bytes) { case 1: data = inb(port); break; case 2: data = inw(port); break; case 4: data = inl(port); break; } pci_cfgdisable(); } return (data); } /* write configuration space register */ void pci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes) { int port; port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); if (port != 0) { switch (bytes) { case 1: outb(port, data); break; case 2: outw(port, data); break; case 4: outl(port, data); break; } pci_cfgdisable(); } } /* check whether the configuration mechanism has been correct identified */ static int pci_cfgcheck(int maxdev) { u_char device; if (bootverbose) printf("pci_cfgcheck:\tdevice "); for (device = 0; device < maxdev; device++) { unsigned id, class, header; if (bootverbose) printf("%d ", device); id = inl(pci_cfgenable(0, device, 0, 0, 4)); if (id == 0 || id == -1) continue; class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8; if (bootverbose) printf("[class=%06x] ", class); if (class == 0 || (class & 0xf870ff) != 0) continue; header = inb(pci_cfgenable(0, device, 0, 14, 1)); if (bootverbose) printf("[hdr=%02x] ", header); if ((header & 0x7e) != 0) continue; if (bootverbose) printf("is there (id=%08x)\n", id); pci_cfgdisable(); return (1); } if (bootverbose) printf("-- nothing found\n"); pci_cfgdisable(); return (0); } static int pci_cfgopen(void) { unsigned long mode1res,oldval1; unsigned char mode2res,oldval2; oldval1 = inl(CONF1_ADDR_PORT); if (bootverbose) { printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", oldval1); } if ((oldval1 & CONF1_ENABLE_MSK) == 0) { cfgmech = 1; devmax = 32; outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); outb(CONF1_ADDR_PORT +3, 0); mode1res = inl(CONF1_ADDR_PORT); outl(CONF1_ADDR_PORT, oldval1); if (bootverbose) printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n", mode1res, CONF1_ENABLE_CHK); if (mode1res) { if (pci_cfgcheck(32)) return (cfgmech); } outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); mode1res = inl(CONF1_ADDR_PORT); outl(CONF1_ADDR_PORT, oldval1); if (bootverbose) printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n", mode1res, CONF1_ENABLE_CHK1); if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { if (pci_cfgcheck(32)) return (cfgmech); } } oldval2 = inb(CONF2_ENABLE_PORT); if (bootverbose) { printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2); } if ((oldval2 & 0xf0) == 0) { cfgmech = 2; devmax = 16; outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); mode2res = inb(CONF2_ENABLE_PORT); outb(CONF2_ENABLE_PORT, oldval2); if (bootverbose) printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", mode2res, CONF2_ENABLE_CHK); if (mode2res == CONF2_ENABLE_RES) { if (bootverbose) printf("pci_open(2a):\tnow trying mechanism 2\n"); if (pci_cfgcheck(16)) return (cfgmech); } } cfgmech = 0; devmax = 0; return (cfgmech); } static devclass_t pcib_devclass; static const char * nexus_pcib_is_host_bridge(pcicfgregs *cfg, u_int32_t id, u_int8_t class, u_int8_t subclass, u_int8_t *busnum) { const char *s = NULL; static u_int8_t pxb[4]; /* hack for 450nx */ *busnum = 0; switch (id) { case 0x12258086: s = "Intel 824?? host to PCI bridge"; /* XXX This is a guess */ *busnum = pci_cfgread(cfg, 0x41, 1); break; case 0x71808086: s = "Intel 82443LX (440 LX) host to PCI bridge"; break; case 0x71908086: s = "Intel 82443BX (440 BX) host to PCI bridge"; break; case 0x71928086: s = "Intel 82443BX host to PCI bridge (AGP disabled)"; break; case 0x71a08086: s = "Intel 82443GX host to PCI bridge"; break; case 0x71a18086: s = "Intel 82443GX host to AGP bridge"; break; case 0x71a28086: s = "Intel 82443GX host to PCI bridge (AGP disabled)"; break; case 0x84c48086: s = "Intel 82454KX/GX (Orion) host to PCI bridge"; *busnum = pci_cfgread(cfg, 0x4a, 1); break; case 0x84ca8086: /* * For the 450nx chipset, there is a whole bundle of * things pretending to be host bridges. The MIOC will * be seen first and isn't really a pci bridge (the * actual busses are attached to the PXB's). We need to * read the registers of the MIOC to figure out the * bus numbers for the PXB channels. * * Since the MIOC doesn't have a pci bus attached, we * pretend it wasn't there. */ pxb[0] = pci_cfgread(cfg, 0xd0, 1); /* BUSNO[0] */ pxb[1] = pci_cfgread(cfg, 0xd1, 1) + 1; /* SUBA[0]+1 */ pxb[2] = pci_cfgread(cfg, 0xd3, 1); /* BUSNO[1] */ pxb[3] = pci_cfgread(cfg, 0xd4, 1) + 1; /* SUBA[1]+1 */ return NULL; case 0x84cb8086: switch (cfg->slot) { case 0x12: s = "Intel 82454NX PXB#0, Bus#A"; *busnum = pxb[0]; break; case 0x13: s = "Intel 82454NX PXB#0, Bus#B"; *busnum = pxb[1]; break; case 0x14: s = "Intel 82454NX PXB#1, Bus#A"; *busnum = pxb[2]; break; case 0x15: s = "Intel 82454NX PXB#1, Bus#B"; *busnum = pxb[3]; break; } break; /* AMD -- vendor 0x1022 */ case 0x70061022: s = "AMD-751 host to PCI bridge"; break; /* SiS -- vendor 0x1039 */ case 0x04961039: s = "SiS 85c496"; break; case 0x04061039: s = "SiS 85c501"; break; case 0x06011039: s = "SiS 85c601"; break; case 0x55911039: s = "SiS 5591 host to PCI bridge"; break; case 0x00011039: s = "SiS 5591 host to AGP bridge"; break; /* VLSI -- vendor 0x1004 */ case 0x00051004: s = "VLSI 82C592 Host to PCI bridge"; break; /* XXX Here is MVP3, I got the datasheet but NO M/B to test it */ /* totally. Please let me know if anything wrong. -F */ /* XXX need info on the MVP3 -- any takers? */ case 0x05981106: s = "VIA 82C598MVP (Apollo MVP3) host bridge"; break; /* AcerLabs -- vendor 0x10b9 */ /* Funny : The datasheet told me vendor id is "10b8",sub-vendor */ /* id is '10b9" but the register always shows "10b9". -Foxfair */ case 0x154110b9: s = "AcerLabs M1541 (Aladdin-V) PCI host bridge"; break; /* OPTi -- vendor 0x1045 */ case 0xc8221045: s = "OPTi 82C822 host to PCI Bridge"; break; /* Ross (?) -- vendor 0x1166 */ case 0x00051166: s = "Ross (?) host to PCI bridge"; /* just guessing the secondary bus register number ... */ *busnum = pci_cfgread(cfg, 0x45, 1); break; /* Integrated Micro Solutions -- vendor 0x10e0 */ case 0x884910e0: s = "Integrated Micro Solutions VL Bridge"; break; default: if (class == PCIC_BRIDGE && subclass == PCIS_BRIDGE_HOST) s = "Host to PCI bridge"; break; } return s; } /* * Scan the first pci bus for host-pci bridges and add pcib instances * to the nexus for each bridge. */ static void nexus_pcib_identify(driver_t *driver, device_t parent) { pcicfgregs probe; int found = 0; if (pci_cfgopen() == 0) return; probe.hose = 0; probe.bus = 0; for (probe.slot = 0; probe.slot <= PCI_SLOTMAX; probe.slot++) { int pcifunchigh = 0; for (probe.func = 0; probe.func <= pcifunchigh; probe.func++) { /* * Read the IDs and class from the device. */ u_int32_t id; u_int8_t class, subclass, busnum; device_t child; const char *s; id = pci_cfgread(&probe, PCIR_DEVVENDOR, 4); if (id == -1) continue; class = pci_cfgread(&probe, PCIR_CLASS, 1); subclass = pci_cfgread(&probe, PCIR_SUBCLASS, 1); s = nexus_pcib_is_host_bridge(&probe, id, class, subclass, &busnum); if (s) { /* * Add at priority 100 to make sure we * go after any motherboard resources */ child = BUS_ADD_CHILD(parent, 100, "pcib", busnum); device_set_desc(child, s); found = 1; } } } /* * Make sure we add at least one bridge since some old * hardware doesn't actually have a host-pci bridge device. * Note that pci_cfgopen() thinks we have PCI devices.. */ if (!found) { if (bootverbose) printf( "nexus_pcib_identify: no bridge found, adding pcib0 anyway\n"); BUS_ADD_CHILD(parent, 100, "pcib", 0); } } static int nexus_pcib_probe(device_t dev) { if (pci_cfgopen() != 0) { - device_add_child(dev, "pci", device_get_unit(dev), 0); + device_add_child(dev, "pci", device_get_unit(dev)); return 0; } return ENXIO; } static device_method_t nexus_pcib_methods[] = { /* Device interface */ DEVMETHOD(device_identify, nexus_pcib_identify), DEVMETHOD(device_probe, nexus_pcib_probe), DEVMETHOD(device_attach, bus_generic_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t nexus_pcib_driver = { "pcib", nexus_pcib_methods, 1, }; DRIVER_MODULE(pcib, nexus, nexus_pcib_driver, pcib_devclass, 0, 0); Index: head/sys/i386/pci/pci_cfgreg.c =================================================================== --- head/sys/i386/pci/pci_cfgreg.c (revision 54072) +++ head/sys/i386/pci/pci_cfgreg.c (revision 54073) @@ -1,514 +1,514 @@ /* * Copyright (c) 1997, Stefan Esser * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * */ #include #include #include #include #include #include #include #ifdef PCI_COMPAT /* XXX this is a terrible hack, which keeps the Tekram AMD SCSI driver happy */ #define cfgmech pci_mechanism int cfgmech; #else static int cfgmech; #endif /* PCI_COMPAT */ static int devmax; /* enable configuration space accesses and return data port address */ static int pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) { int dataport = 0; if (bus <= PCI_BUSMAX && slot < devmax && func <= PCI_FUNCMAX && reg <= PCI_REGMAX && bytes != 3 && (unsigned) bytes <= 4 && (reg & (bytes -1)) == 0) { switch (cfgmech) { case 1: outl(CONF1_ADDR_PORT, (1 << 31) | (bus << 16) | (slot << 11) | (func << 8) | (reg & ~0x03)); dataport = CONF1_DATA_PORT + (reg & 0x03); break; case 2: outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); outb(CONF2_FORWARD_PORT, bus); dataport = 0xc000 | (slot << 8) | reg; break; } } return (dataport); } /* disable configuration space accesses */ static void pci_cfgdisable(void) { switch (cfgmech) { case 1: outl(CONF1_ADDR_PORT, 0); break; case 2: outb(CONF2_ENABLE_PORT, 0); outb(CONF2_FORWARD_PORT, 0); break; } } /* read configuration space register */ int pci_cfgread(pcicfgregs *cfg, int reg, int bytes) { int data = -1; int port; port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); if (port != 0) { switch (bytes) { case 1: data = inb(port); break; case 2: data = inw(port); break; case 4: data = inl(port); break; } pci_cfgdisable(); } return (data); } /* write configuration space register */ void pci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes) { int port; port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); if (port != 0) { switch (bytes) { case 1: outb(port, data); break; case 2: outw(port, data); break; case 4: outl(port, data); break; } pci_cfgdisable(); } } /* check whether the configuration mechanism has been correct identified */ static int pci_cfgcheck(int maxdev) { u_char device; if (bootverbose) printf("pci_cfgcheck:\tdevice "); for (device = 0; device < maxdev; device++) { unsigned id, class, header; if (bootverbose) printf("%d ", device); id = inl(pci_cfgenable(0, device, 0, 0, 4)); if (id == 0 || id == -1) continue; class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8; if (bootverbose) printf("[class=%06x] ", class); if (class == 0 || (class & 0xf870ff) != 0) continue; header = inb(pci_cfgenable(0, device, 0, 14, 1)); if (bootverbose) printf("[hdr=%02x] ", header); if ((header & 0x7e) != 0) continue; if (bootverbose) printf("is there (id=%08x)\n", id); pci_cfgdisable(); return (1); } if (bootverbose) printf("-- nothing found\n"); pci_cfgdisable(); return (0); } static int pci_cfgopen(void) { unsigned long mode1res,oldval1; unsigned char mode2res,oldval2; oldval1 = inl(CONF1_ADDR_PORT); if (bootverbose) { printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", oldval1); } if ((oldval1 & CONF1_ENABLE_MSK) == 0) { cfgmech = 1; devmax = 32; outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); outb(CONF1_ADDR_PORT +3, 0); mode1res = inl(CONF1_ADDR_PORT); outl(CONF1_ADDR_PORT, oldval1); if (bootverbose) printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n", mode1res, CONF1_ENABLE_CHK); if (mode1res) { if (pci_cfgcheck(32)) return (cfgmech); } outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); mode1res = inl(CONF1_ADDR_PORT); outl(CONF1_ADDR_PORT, oldval1); if (bootverbose) printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n", mode1res, CONF1_ENABLE_CHK1); if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { if (pci_cfgcheck(32)) return (cfgmech); } } oldval2 = inb(CONF2_ENABLE_PORT); if (bootverbose) { printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2); } if ((oldval2 & 0xf0) == 0) { cfgmech = 2; devmax = 16; outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); mode2res = inb(CONF2_ENABLE_PORT); outb(CONF2_ENABLE_PORT, oldval2); if (bootverbose) printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", mode2res, CONF2_ENABLE_CHK); if (mode2res == CONF2_ENABLE_RES) { if (bootverbose) printf("pci_open(2a):\tnow trying mechanism 2\n"); if (pci_cfgcheck(16)) return (cfgmech); } } cfgmech = 0; devmax = 0; return (cfgmech); } static devclass_t pcib_devclass; static const char * nexus_pcib_is_host_bridge(pcicfgregs *cfg, u_int32_t id, u_int8_t class, u_int8_t subclass, u_int8_t *busnum) { const char *s = NULL; static u_int8_t pxb[4]; /* hack for 450nx */ *busnum = 0; switch (id) { case 0x12258086: s = "Intel 824?? host to PCI bridge"; /* XXX This is a guess */ *busnum = pci_cfgread(cfg, 0x41, 1); break; case 0x71808086: s = "Intel 82443LX (440 LX) host to PCI bridge"; break; case 0x71908086: s = "Intel 82443BX (440 BX) host to PCI bridge"; break; case 0x71928086: s = "Intel 82443BX host to PCI bridge (AGP disabled)"; break; case 0x71a08086: s = "Intel 82443GX host to PCI bridge"; break; case 0x71a18086: s = "Intel 82443GX host to AGP bridge"; break; case 0x71a28086: s = "Intel 82443GX host to PCI bridge (AGP disabled)"; break; case 0x84c48086: s = "Intel 82454KX/GX (Orion) host to PCI bridge"; *busnum = pci_cfgread(cfg, 0x4a, 1); break; case 0x84ca8086: /* * For the 450nx chipset, there is a whole bundle of * things pretending to be host bridges. The MIOC will * be seen first and isn't really a pci bridge (the * actual busses are attached to the PXB's). We need to * read the registers of the MIOC to figure out the * bus numbers for the PXB channels. * * Since the MIOC doesn't have a pci bus attached, we * pretend it wasn't there. */ pxb[0] = pci_cfgread(cfg, 0xd0, 1); /* BUSNO[0] */ pxb[1] = pci_cfgread(cfg, 0xd1, 1) + 1; /* SUBA[0]+1 */ pxb[2] = pci_cfgread(cfg, 0xd3, 1); /* BUSNO[1] */ pxb[3] = pci_cfgread(cfg, 0xd4, 1) + 1; /* SUBA[1]+1 */ return NULL; case 0x84cb8086: switch (cfg->slot) { case 0x12: s = "Intel 82454NX PXB#0, Bus#A"; *busnum = pxb[0]; break; case 0x13: s = "Intel 82454NX PXB#0, Bus#B"; *busnum = pxb[1]; break; case 0x14: s = "Intel 82454NX PXB#1, Bus#A"; *busnum = pxb[2]; break; case 0x15: s = "Intel 82454NX PXB#1, Bus#B"; *busnum = pxb[3]; break; } break; /* AMD -- vendor 0x1022 */ case 0x70061022: s = "AMD-751 host to PCI bridge"; break; /* SiS -- vendor 0x1039 */ case 0x04961039: s = "SiS 85c496"; break; case 0x04061039: s = "SiS 85c501"; break; case 0x06011039: s = "SiS 85c601"; break; case 0x55911039: s = "SiS 5591 host to PCI bridge"; break; case 0x00011039: s = "SiS 5591 host to AGP bridge"; break; /* VLSI -- vendor 0x1004 */ case 0x00051004: s = "VLSI 82C592 Host to PCI bridge"; break; /* XXX Here is MVP3, I got the datasheet but NO M/B to test it */ /* totally. Please let me know if anything wrong. -F */ /* XXX need info on the MVP3 -- any takers? */ case 0x05981106: s = "VIA 82C598MVP (Apollo MVP3) host bridge"; break; /* AcerLabs -- vendor 0x10b9 */ /* Funny : The datasheet told me vendor id is "10b8",sub-vendor */ /* id is '10b9" but the register always shows "10b9". -Foxfair */ case 0x154110b9: s = "AcerLabs M1541 (Aladdin-V) PCI host bridge"; break; /* OPTi -- vendor 0x1045 */ case 0xc8221045: s = "OPTi 82C822 host to PCI Bridge"; break; /* Ross (?) -- vendor 0x1166 */ case 0x00051166: s = "Ross (?) host to PCI bridge"; /* just guessing the secondary bus register number ... */ *busnum = pci_cfgread(cfg, 0x45, 1); break; /* Integrated Micro Solutions -- vendor 0x10e0 */ case 0x884910e0: s = "Integrated Micro Solutions VL Bridge"; break; default: if (class == PCIC_BRIDGE && subclass == PCIS_BRIDGE_HOST) s = "Host to PCI bridge"; break; } return s; } /* * Scan the first pci bus for host-pci bridges and add pcib instances * to the nexus for each bridge. */ static void nexus_pcib_identify(driver_t *driver, device_t parent) { pcicfgregs probe; int found = 0; if (pci_cfgopen() == 0) return; probe.hose = 0; probe.bus = 0; for (probe.slot = 0; probe.slot <= PCI_SLOTMAX; probe.slot++) { int pcifunchigh = 0; for (probe.func = 0; probe.func <= pcifunchigh; probe.func++) { /* * Read the IDs and class from the device. */ u_int32_t id; u_int8_t class, subclass, busnum; device_t child; const char *s; id = pci_cfgread(&probe, PCIR_DEVVENDOR, 4); if (id == -1) continue; class = pci_cfgread(&probe, PCIR_CLASS, 1); subclass = pci_cfgread(&probe, PCIR_SUBCLASS, 1); s = nexus_pcib_is_host_bridge(&probe, id, class, subclass, &busnum); if (s) { /* * Add at priority 100 to make sure we * go after any motherboard resources */ child = BUS_ADD_CHILD(parent, 100, "pcib", busnum); device_set_desc(child, s); found = 1; } } } /* * Make sure we add at least one bridge since some old * hardware doesn't actually have a host-pci bridge device. * Note that pci_cfgopen() thinks we have PCI devices.. */ if (!found) { if (bootverbose) printf( "nexus_pcib_identify: no bridge found, adding pcib0 anyway\n"); BUS_ADD_CHILD(parent, 100, "pcib", 0); } } static int nexus_pcib_probe(device_t dev) { if (pci_cfgopen() != 0) { - device_add_child(dev, "pci", device_get_unit(dev), 0); + device_add_child(dev, "pci", device_get_unit(dev)); return 0; } return ENXIO; } static device_method_t nexus_pcib_methods[] = { /* Device interface */ DEVMETHOD(device_identify, nexus_pcib_identify), DEVMETHOD(device_probe, nexus_pcib_probe), DEVMETHOD(device_attach, bus_generic_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t nexus_pcib_driver = { "pcib", nexus_pcib_methods, 1, }; DRIVER_MODULE(pcib, nexus, nexus_pcib_driver, pcib_devclass, 0, 0); Index: head/sys/i386/pci/pci_pir.c =================================================================== --- head/sys/i386/pci/pci_pir.c (revision 54072) +++ head/sys/i386/pci/pci_pir.c (revision 54073) @@ -1,514 +1,514 @@ /* * Copyright (c) 1997, Stefan Esser * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * */ #include #include #include #include #include #include #include #ifdef PCI_COMPAT /* XXX this is a terrible hack, which keeps the Tekram AMD SCSI driver happy */ #define cfgmech pci_mechanism int cfgmech; #else static int cfgmech; #endif /* PCI_COMPAT */ static int devmax; /* enable configuration space accesses and return data port address */ static int pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) { int dataport = 0; if (bus <= PCI_BUSMAX && slot < devmax && func <= PCI_FUNCMAX && reg <= PCI_REGMAX && bytes != 3 && (unsigned) bytes <= 4 && (reg & (bytes -1)) == 0) { switch (cfgmech) { case 1: outl(CONF1_ADDR_PORT, (1 << 31) | (bus << 16) | (slot << 11) | (func << 8) | (reg & ~0x03)); dataport = CONF1_DATA_PORT + (reg & 0x03); break; case 2: outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); outb(CONF2_FORWARD_PORT, bus); dataport = 0xc000 | (slot << 8) | reg; break; } } return (dataport); } /* disable configuration space accesses */ static void pci_cfgdisable(void) { switch (cfgmech) { case 1: outl(CONF1_ADDR_PORT, 0); break; case 2: outb(CONF2_ENABLE_PORT, 0); outb(CONF2_FORWARD_PORT, 0); break; } } /* read configuration space register */ int pci_cfgread(pcicfgregs *cfg, int reg, int bytes) { int data = -1; int port; port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); if (port != 0) { switch (bytes) { case 1: data = inb(port); break; case 2: data = inw(port); break; case 4: data = inl(port); break; } pci_cfgdisable(); } return (data); } /* write configuration space register */ void pci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes) { int port; port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); if (port != 0) { switch (bytes) { case 1: outb(port, data); break; case 2: outw(port, data); break; case 4: outl(port, data); break; } pci_cfgdisable(); } } /* check whether the configuration mechanism has been correct identified */ static int pci_cfgcheck(int maxdev) { u_char device; if (bootverbose) printf("pci_cfgcheck:\tdevice "); for (device = 0; device < maxdev; device++) { unsigned id, class, header; if (bootverbose) printf("%d ", device); id = inl(pci_cfgenable(0, device, 0, 0, 4)); if (id == 0 || id == -1) continue; class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8; if (bootverbose) printf("[class=%06x] ", class); if (class == 0 || (class & 0xf870ff) != 0) continue; header = inb(pci_cfgenable(0, device, 0, 14, 1)); if (bootverbose) printf("[hdr=%02x] ", header); if ((header & 0x7e) != 0) continue; if (bootverbose) printf("is there (id=%08x)\n", id); pci_cfgdisable(); return (1); } if (bootverbose) printf("-- nothing found\n"); pci_cfgdisable(); return (0); } static int pci_cfgopen(void) { unsigned long mode1res,oldval1; unsigned char mode2res,oldval2; oldval1 = inl(CONF1_ADDR_PORT); if (bootverbose) { printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", oldval1); } if ((oldval1 & CONF1_ENABLE_MSK) == 0) { cfgmech = 1; devmax = 32; outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); outb(CONF1_ADDR_PORT +3, 0); mode1res = inl(CONF1_ADDR_PORT); outl(CONF1_ADDR_PORT, oldval1); if (bootverbose) printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n", mode1res, CONF1_ENABLE_CHK); if (mode1res) { if (pci_cfgcheck(32)) return (cfgmech); } outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); mode1res = inl(CONF1_ADDR_PORT); outl(CONF1_ADDR_PORT, oldval1); if (bootverbose) printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n", mode1res, CONF1_ENABLE_CHK1); if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { if (pci_cfgcheck(32)) return (cfgmech); } } oldval2 = inb(CONF2_ENABLE_PORT); if (bootverbose) { printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2); } if ((oldval2 & 0xf0) == 0) { cfgmech = 2; devmax = 16; outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); mode2res = inb(CONF2_ENABLE_PORT); outb(CONF2_ENABLE_PORT, oldval2); if (bootverbose) printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", mode2res, CONF2_ENABLE_CHK); if (mode2res == CONF2_ENABLE_RES) { if (bootverbose) printf("pci_open(2a):\tnow trying mechanism 2\n"); if (pci_cfgcheck(16)) return (cfgmech); } } cfgmech = 0; devmax = 0; return (cfgmech); } static devclass_t pcib_devclass; static const char * nexus_pcib_is_host_bridge(pcicfgregs *cfg, u_int32_t id, u_int8_t class, u_int8_t subclass, u_int8_t *busnum) { const char *s = NULL; static u_int8_t pxb[4]; /* hack for 450nx */ *busnum = 0; switch (id) { case 0x12258086: s = "Intel 824?? host to PCI bridge"; /* XXX This is a guess */ *busnum = pci_cfgread(cfg, 0x41, 1); break; case 0x71808086: s = "Intel 82443LX (440 LX) host to PCI bridge"; break; case 0x71908086: s = "Intel 82443BX (440 BX) host to PCI bridge"; break; case 0x71928086: s = "Intel 82443BX host to PCI bridge (AGP disabled)"; break; case 0x71a08086: s = "Intel 82443GX host to PCI bridge"; break; case 0x71a18086: s = "Intel 82443GX host to AGP bridge"; break; case 0x71a28086: s = "Intel 82443GX host to PCI bridge (AGP disabled)"; break; case 0x84c48086: s = "Intel 82454KX/GX (Orion) host to PCI bridge"; *busnum = pci_cfgread(cfg, 0x4a, 1); break; case 0x84ca8086: /* * For the 450nx chipset, there is a whole bundle of * things pretending to be host bridges. The MIOC will * be seen first and isn't really a pci bridge (the * actual busses are attached to the PXB's). We need to * read the registers of the MIOC to figure out the * bus numbers for the PXB channels. * * Since the MIOC doesn't have a pci bus attached, we * pretend it wasn't there. */ pxb[0] = pci_cfgread(cfg, 0xd0, 1); /* BUSNO[0] */ pxb[1] = pci_cfgread(cfg, 0xd1, 1) + 1; /* SUBA[0]+1 */ pxb[2] = pci_cfgread(cfg, 0xd3, 1); /* BUSNO[1] */ pxb[3] = pci_cfgread(cfg, 0xd4, 1) + 1; /* SUBA[1]+1 */ return NULL; case 0x84cb8086: switch (cfg->slot) { case 0x12: s = "Intel 82454NX PXB#0, Bus#A"; *busnum = pxb[0]; break; case 0x13: s = "Intel 82454NX PXB#0, Bus#B"; *busnum = pxb[1]; break; case 0x14: s = "Intel 82454NX PXB#1, Bus#A"; *busnum = pxb[2]; break; case 0x15: s = "Intel 82454NX PXB#1, Bus#B"; *busnum = pxb[3]; break; } break; /* AMD -- vendor 0x1022 */ case 0x70061022: s = "AMD-751 host to PCI bridge"; break; /* SiS -- vendor 0x1039 */ case 0x04961039: s = "SiS 85c496"; break; case 0x04061039: s = "SiS 85c501"; break; case 0x06011039: s = "SiS 85c601"; break; case 0x55911039: s = "SiS 5591 host to PCI bridge"; break; case 0x00011039: s = "SiS 5591 host to AGP bridge"; break; /* VLSI -- vendor 0x1004 */ case 0x00051004: s = "VLSI 82C592 Host to PCI bridge"; break; /* XXX Here is MVP3, I got the datasheet but NO M/B to test it */ /* totally. Please let me know if anything wrong. -F */ /* XXX need info on the MVP3 -- any takers? */ case 0x05981106: s = "VIA 82C598MVP (Apollo MVP3) host bridge"; break; /* AcerLabs -- vendor 0x10b9 */ /* Funny : The datasheet told me vendor id is "10b8",sub-vendor */ /* id is '10b9" but the register always shows "10b9". -Foxfair */ case 0x154110b9: s = "AcerLabs M1541 (Aladdin-V) PCI host bridge"; break; /* OPTi -- vendor 0x1045 */ case 0xc8221045: s = "OPTi 82C822 host to PCI Bridge"; break; /* Ross (?) -- vendor 0x1166 */ case 0x00051166: s = "Ross (?) host to PCI bridge"; /* just guessing the secondary bus register number ... */ *busnum = pci_cfgread(cfg, 0x45, 1); break; /* Integrated Micro Solutions -- vendor 0x10e0 */ case 0x884910e0: s = "Integrated Micro Solutions VL Bridge"; break; default: if (class == PCIC_BRIDGE && subclass == PCIS_BRIDGE_HOST) s = "Host to PCI bridge"; break; } return s; } /* * Scan the first pci bus for host-pci bridges and add pcib instances * to the nexus for each bridge. */ static void nexus_pcib_identify(driver_t *driver, device_t parent) { pcicfgregs probe; int found = 0; if (pci_cfgopen() == 0) return; probe.hose = 0; probe.bus = 0; for (probe.slot = 0; probe.slot <= PCI_SLOTMAX; probe.slot++) { int pcifunchigh = 0; for (probe.func = 0; probe.func <= pcifunchigh; probe.func++) { /* * Read the IDs and class from the device. */ u_int32_t id; u_int8_t class, subclass, busnum; device_t child; const char *s; id = pci_cfgread(&probe, PCIR_DEVVENDOR, 4); if (id == -1) continue; class = pci_cfgread(&probe, PCIR_CLASS, 1); subclass = pci_cfgread(&probe, PCIR_SUBCLASS, 1); s = nexus_pcib_is_host_bridge(&probe, id, class, subclass, &busnum); if (s) { /* * Add at priority 100 to make sure we * go after any motherboard resources */ child = BUS_ADD_CHILD(parent, 100, "pcib", busnum); device_set_desc(child, s); found = 1; } } } /* * Make sure we add at least one bridge since some old * hardware doesn't actually have a host-pci bridge device. * Note that pci_cfgopen() thinks we have PCI devices.. */ if (!found) { if (bootverbose) printf( "nexus_pcib_identify: no bridge found, adding pcib0 anyway\n"); BUS_ADD_CHILD(parent, 100, "pcib", 0); } } static int nexus_pcib_probe(device_t dev) { if (pci_cfgopen() != 0) { - device_add_child(dev, "pci", device_get_unit(dev), 0); + device_add_child(dev, "pci", device_get_unit(dev)); return 0; } return ENXIO; } static device_method_t nexus_pcib_methods[] = { /* Device interface */ DEVMETHOD(device_identify, nexus_pcib_identify), DEVMETHOD(device_probe, nexus_pcib_probe), DEVMETHOD(device_attach, bus_generic_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t nexus_pcib_driver = { "pcib", nexus_pcib_methods, 1, }; DRIVER_MODULE(pcib, nexus, nexus_pcib_driver, pcib_devclass, 0, 0); Index: head/sys/isa/atkbdc_isa.c =================================================================== --- head/sys/isa/atkbdc_isa.c (revision 54072) +++ head/sys/isa/atkbdc_isa.c (revision 54073) @@ -1,268 +1,269 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include "atkbdc.h" #include "opt_kbd.h" #if NATKBDC > 0 #include #include #include #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_ATKBDDEV, "atkbddev", "AT Keyboard device"); /* children */ typedef struct atkbdc_device { int flags; /* configuration flags */ int port; /* port number (same as the controller's) */ int irq; /* ISA IRQ mask */ } atkbdc_device_t; /* kbdc */ devclass_t atkbdc_devclass; static int atkbdc_probe(device_t dev); static int atkbdc_attach(device_t dev); static int atkbdc_print_child(device_t bus, device_t dev); static int atkbdc_read_ivar(device_t bus, device_t dev, int index, u_long *val); static int atkbdc_write_ivar(device_t bus, device_t dev, int index, u_long val); static device_method_t atkbdc_methods[] = { DEVMETHOD(device_probe, atkbdc_probe), DEVMETHOD(device_attach, atkbdc_attach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(bus_print_child, atkbdc_print_child), DEVMETHOD(bus_read_ivar, atkbdc_read_ivar), DEVMETHOD(bus_write_ivar, atkbdc_write_ivar), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t atkbdc_driver = { ATKBDC_DRIVER_NAME, atkbdc_methods, sizeof(atkbdc_softc_t *), }; static int atkbdc_probe(device_t dev) { int error; int rid; struct resource *port; /* Check isapnp ids */ if (isa_get_vendorid(dev)) return (ENXIO); device_set_desc(dev, "keyboard controller (i8042)"); rid = 0; port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, IO_KBDSIZE, RF_ACTIVE); if (!port) return ENXIO; error = atkbdc_probe_unit(device_get_unit(dev), rman_get_start(port)); bus_release_resource(dev, SYS_RES_IOPORT, rid, port); return error; } static void atkbdc_add_device(device_t dev, const char *name, int unit) { atkbdc_softc_t *sc = *(atkbdc_softc_t **)device_get_softc(dev); atkbdc_device_t *kdev; device_t child; int t; kdev = malloc(sizeof(struct atkbdc_device), M_ATKBDDEV, M_NOWAIT); if (!kdev) return; bzero(kdev, sizeof *kdev); kdev->port = sc->port; if (resource_int_value(name, unit, "irq", &t) == 0) kdev->irq = t; else kdev->irq = -1; if (resource_int_value(name, unit, "flags", &t) == 0) kdev->flags = t; else kdev->flags = 0; - child = device_add_child(dev, name, unit, kdev); + child = device_add_child(dev, name, unit); + device_set_ivars(child, kdev); } static int atkbdc_attach(device_t dev) { atkbdc_softc_t *sc; struct resource *port; int unit; int error; int rid; int i; unit = device_get_unit(dev); sc = *(atkbdc_softc_t **)device_get_softc(dev); if (sc == NULL) { /* * We have to maintain two copies of the kbdc_softc struct, * as the low-level console needs to have access to the * keyboard controller before kbdc is probed and attached. * kbdc_soft[] contains the default entry for that purpose. * See atkbdc.c. XXX */ sc = atkbdc_get_softc(unit); if (sc == NULL) return ENOMEM; } /* XXX should track resource in softc */ rid = 0; port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, IO_KBDSIZE, RF_ACTIVE); if (!port) return ENXIO; error = atkbdc_attach_unit(unit, sc, rman_get_start(port)); if (error) return error; *(atkbdc_softc_t **)device_get_softc(dev) = sc; /* * Add all devices configured to be attached to atkbdc0. */ for (i = resource_query_string(-1, "at", "atkbdc0"); i != -1; i = resource_query_string(i, "at", "atkbdc0")) { atkbdc_add_device(dev, resource_query_name(i), resource_query_unit(i)); } /* * and atkbdc? */ for (i = resource_query_string(-1, "at", "atkbdc"); i != -1; i = resource_query_string(i, "at", "atkbdc")) { atkbdc_add_device(dev, resource_query_name(i), resource_query_unit(i)); } bus_generic_attach(dev); return 0; } static int atkbdc_print_child(device_t bus, device_t dev) { atkbdc_device_t *kbdcdev; int retval = 0; kbdcdev = (atkbdc_device_t *)device_get_ivars(dev); retval += bus_print_child_header(bus, dev); if (kbdcdev->flags != 0) retval += printf(" flags 0x%x", kbdcdev->flags); if (kbdcdev->irq != -1) retval += printf(" irq %d", kbdcdev->irq); retval += bus_print_child_footer(bus, dev); return (retval); } static int atkbdc_read_ivar(device_t bus, device_t dev, int index, u_long *val) { atkbdc_device_t *ivar; ivar = (atkbdc_device_t *)device_get_ivars(dev); switch (index) { case KBDC_IVAR_PORT: *val = (u_long)ivar->port; break; case KBDC_IVAR_IRQ: *val = (u_long)ivar->irq; break; case KBDC_IVAR_FLAGS: *val = (u_long)ivar->flags; break; default: return ENOENT; } return 0; } static int atkbdc_write_ivar(device_t bus, device_t dev, int index, u_long val) { atkbdc_device_t *ivar; ivar = (atkbdc_device_t *)device_get_ivars(dev); switch (index) { case KBDC_IVAR_PORT: ivar->port = (int)val; break; case KBDC_IVAR_IRQ: ivar->irq = (int)val; break; case KBDC_IVAR_FLAGS: ivar->flags = (int)val; break; default: return ENOENT; } return 0; } DRIVER_MODULE(atkbdc, isa, atkbdc_driver, atkbdc_devclass, 0, 0); #endif /* NATKBDC > 0 */ Index: head/sys/isa/fd.c =================================================================== --- head/sys/isa/fd.c (revision 54072) +++ head/sys/isa/fd.c (revision 54073) @@ -1,2357 +1,2358 @@ /* * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Don Ahn. * * Libretto PCMCIA floppy support by David Horwitt (dhorwitt@ucsd.edu) * aided by the Linux floppy driver modifications from David Bateman * (dbateman@eng.uts.edu.au). * * Copyright (c) 1993, 1994 by * jc@irbs.UUCP (John Capo) * vak@zebub.msk.su (Serge Vakulenko) * ache@astral.msk.su (Andrew A. Chernov) * * Copyright (c) 1993, 1994, 1995 by * joerg_wunsch@uriah.sax.de (Joerg Wunsch) * dufault@hda.com (Peter Dufault) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 * $FreeBSD$ * */ #include "opt_fdc.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef FDC_YE #undef FDC_YE #warning "fix FDC_YE! - newbus casualty" #endif /* misuse a flag to identify format operation */ #define B_FORMAT B_XXX /* configuration flags */ #define FDC_PRETEND_D0 (1 << 0) /* pretend drive 0 to be there */ #ifdef FDC_YE #define FDC_IS_PCMCIA (1 << 1) /* if successful probe, then it's a PCMCIA device */ #endif /* internally used only, not really from CMOS: */ #define RTCFDT_144M_PRETENDED 0x1000 /* error returns for fd_cmd() */ #define FD_FAILED -1 #define FD_NOT_VALID -2 #define FDC_ERRMAX 100 /* do not log more */ #define NUMTYPES 14 #define NUMDENS (NUMTYPES - 6) /* These defines (-1) must match index for fd_types */ #define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ #define FD_1720 1 #define FD_1480 2 #define FD_1440 3 #define FD_1200 4 #define FD_820 5 #define FD_800 6 #define FD_720 7 #define FD_360 8 #define FD_1480in5_25 9 #define FD_1440in5_25 10 #define FD_820in5_25 11 #define FD_800in5_25 12 #define FD_720in5_25 13 #define FD_360in5_25 14 static struct fd_type fd_types[NUMTYPES] = { { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */ { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */ { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */ { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */ { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */ { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */ { 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */ { 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */ { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */ { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */ { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */ { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */ { 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */ { 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */ }; #define DRVS_PER_CTLR 2 /* 2 floppies */ /***********************************************************************\ * Per controller structure. * \***********************************************************************/ static devclass_t fdc_devclass; /***********************************************************************\ * Per drive structure. * * N per controller (DRVS_PER_CTLR) * \***********************************************************************/ struct fd_data { struct fdc_data *fdc; /* pointer to controller structure */ int fdsu; /* this units number on this controller */ int type; /* Drive type (FD_1440...) */ struct fd_type *ft; /* pointer to the type descriptor */ int flags; #define FD_OPEN 0x01 /* it's open */ #define FD_ACTIVE 0x02 /* it's active */ #define FD_MOTOR 0x04 /* motor should be on */ #define FD_MOTOR_WAIT 0x08 /* motor coming up */ int skip; int hddrv; #define FD_NO_TRACK -2 int track; /* where we think the head is */ int options; /* user configurable options, see ioctl_fd.h */ struct callout_handle toffhandle; struct callout_handle tohandle; struct devstat device_stats; device_t dev; fdu_t fdu; }; static devclass_t fd_devclass; /***********************************************************************\ * Throughout this file the following conventions will be used: * * fd is a pointer to the fd_data struct for the drive in question * * fdc is a pointer to the fdc_data struct for the controller * * fdu is the floppy drive unit number * * fdcu is the floppy controller unit number * * fdsu is the floppy drive unit number on that controller. (sub-unit) * \***********************************************************************/ #ifdef FDC_YE #include "card.h" static int yeattach(struct isa_device *); #endif /* needed for ft driver, thus exported */ int in_fdc(struct fdc_data *); int out_fdc(struct fdc_data *, int); /* internal functions */ static void fdc_add_device(device_t, const char *, int); static void fdc_intr(void *); static void set_motor(struct fdc_data *, int, int); # define TURNON 1 # define TURNOFF 0 static timeout_t fd_turnoff; static timeout_t fd_motor_on; static void fd_turnon(struct fd_data *); static void fdc_reset(fdc_p); static int fd_in(struct fdc_data *, int *); static void fdstart(struct fdc_data *); static timeout_t fd_iotimeout; static timeout_t fd_pseudointr; static int fdstate(struct fdc_data *); static int retrier(struct fdc_data *); static int fdformat(dev_t, struct fd_formb *, struct proc *); static int enable_fifo(fdc_p fdc); static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */ #define DEVIDLE 0 #define FINDWORK 1 #define DOSEEK 2 #define SEEKCOMPLETE 3 #define IOCOMPLETE 4 #define RECALCOMPLETE 5 #define STARTRECAL 6 #define RESETCTLR 7 #define SEEKWAIT 8 #define RECALWAIT 9 #define MOTORWAIT 10 #define IOTIMEDOUT 11 #define RESETCOMPLETE 12 #ifdef FDC_YE #define PIOREAD 13 #endif #ifdef FDC_DEBUG static char const * const fdstates[] = { "DEVIDLE", "FINDWORK", "DOSEEK", "SEEKCOMPLETE", "IOCOMPLETE", "RECALCOMPLETE", "STARTRECAL", "RESETCTLR", "SEEKWAIT", "RECALWAIT", "MOTORWAIT", "IOTIMEDOUT", "RESETCOMPLETE", #ifdef FDC_YE "PIOREAD", #endif }; /* CAUTION: fd_debug causes huge amounts of logging output */ static int volatile fd_debug = 0; #define TRACE0(arg) if(fd_debug) printf(arg) #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) #else /* FDC_DEBUG */ #define TRACE0(arg) #define TRACE1(arg1, arg2) #endif /* FDC_DEBUG */ static void fdout_wr(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->portt, fdc->porth, FDOUT+fdc->port_off, v); } static u_int8_t fdsts_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDSTS+fdc->port_off); } static void fddata_wr(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off, v); } static u_int8_t fddata_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off); } static void fdctl_wr(fdc_p fdc, u_int8_t v) { if (fdc->flags & FDC_ISPNP) bus_space_write_1(fdc->ctlt, fdc->ctlh, 0, v); else bus_space_write_1(fdc->portt, fdc->porth, FDCTL, v); } #if 0 static u_int8_t fdin_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDIN); } #endif #ifdef FDC_YE #if NCARD > 0 #include #include #include #include #include /* * PC-Card (PCMCIA) specific code. */ static int yeinit(struct pccard_devinfo *); /* init device */ static void yeunload(struct pccard_devinfo *); /* Disable driver */ static int yeintr(struct pccard_devinfo *); /* Interrupt handler */ PCCARD_MODULE(fdc, yeinit, yeunload, yeintr, 0, bio_imask); /* * this is the secret PIO data port (offset from base) */ #define FDC_YE_DATAPORT 6 /* * Initialize the device - called from Slot manager. */ static int yeinit(struct pccard_devinfo *devi) { fdc_p fdc = &fdc_data[devi->isahd.id_unit]; fdc->baseport = devi->isahd.id_iobase; /* * reset controller */ fdout_wr(fdc, 0); DELAY(100); fdout_wr(fdc, FDO_FRST); /* * wire into system */ if (yeattach(&devi->isahd) == 0) return(ENXIO); return(0); } /* * yeunload - unload the driver and clear the table. * XXX TODO: * This is usually called when the card is ejected, but * can be caused by a modunload of a controller driver. * The idea is to reset the driver's view of the device * and ensure that any driver entry points such as * read and write do not hang. */ static void yeunload(struct pccard_devinfo *devi) { if (fd_data[devi->isahd.id_unit].type == NO_TYPE) return; /* * this prevents Fdopen() and fdstrategy() from attempting * to access unloaded controller */ fd_data[devi->isahd.id_unit].type = NO_TYPE; printf("fdc%d: unload\n", devi->isahd.id_unit); } /* * yeintr - Shared interrupt called from * front end of PC-Card handler. */ static int yeintr(struct pccard_devinfo *devi) { fdintr((fdcu_t)devi->isahd.id_unit); return(1); } #endif /* NCARD > 0 */ #endif /* FDC_YE */ static d_open_t Fdopen; /* NOTE, not fdopen */ static d_close_t fdclose; static d_ioctl_t fdioctl; static d_strategy_t fdstrategy; #define CDEV_MAJOR 9 #define BDEV_MAJOR 2 static struct cdevsw fd_cdevsw = { /* open */ Fdopen, /* close */ fdclose, /* read */ physread, /* write */ physwrite, /* ioctl */ fdioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ fdstrategy, /* name */ "fd", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ D_DISK, /* bmaj */ BDEV_MAJOR }; static int fdc_err(struct fdc_data *fdc, const char *s) { fdc->fdc_errs++; if (s) { if (fdc->fdc_errs < FDC_ERRMAX) { device_print_prettyname(fdc->fdc_dev); printf("%s", s); } else if (fdc->fdc_errs == FDC_ERRMAX) { device_print_prettyname(fdc->fdc_dev); printf("too many errors, not logging any more\n"); } } return FD_FAILED; } /* * fd_cmd: Send a command to the chip. Takes a varargs with this structure: * Unit number, * # of output bytes, output bytes as ints ..., * # of input bytes, input bytes as ints ... */ static int fd_cmd(struct fdc_data *fdc, int n_out, ...) { u_char cmd; int n_in; int n; va_list ap; va_start(ap, n_out); cmd = (u_char)(va_arg(ap, int)); va_end(ap); va_start(ap, n_out); for (n = 0; n < n_out; n++) { if (out_fdc(fdc, va_arg(ap, int)) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %x failed at out byte %d of %d\n", cmd, n + 1, n_out); return fdc_err(fdc, msg); } } n_in = va_arg(ap, int); for (n = 0; n < n_in; n++) { int *ptr = va_arg(ap, int *); if (fd_in(fdc, ptr) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %02x failed at in byte %d of %d\n", cmd, n + 1, n_in); return fdc_err(fdc, msg); } } return 0; } static int enable_fifo(fdc_p fdc) { int i, j; if ((fdc->flags & FDC_HAS_FIFO) == 0) { /* * XXX: * Cannot use fd_cmd the normal way here, since * this might be an invalid command. Thus we send the * first byte, and check for an early turn of data directon. */ if (out_fdc(fdc, I8207X_CONFIGURE) < 0) return fdc_err(fdc, "Enable FIFO failed\n"); /* If command is invalid, return */ j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO | NE7_RQM)) != NE7_RQM && j-- > 0) if (i == (NE7_DIO | NE7_RQM)) { fdc_reset(fdc); return FD_FAILED; } if (j<0 || fd_cmd(fdc, 3, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) { fdc_reset(fdc); return fdc_err(fdc, "Enable FIFO failed\n"); } fdc->flags |= FDC_HAS_FIFO; return 0; } if (fd_cmd(fdc, 4, I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) return fdc_err(fdc, "Re-enable FIFO failed\n"); return 0; } static int fd_sense_drive_status(fdc_p fdc, int *st3p) { int st3; if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) { return fdc_err(fdc, "Sense Drive Status failed\n"); } if (st3p) *st3p = st3; return 0; } static int fd_sense_int(fdc_p fdc, int *st0p, int *cylp) { int cyl, st0, ret; ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0); if (ret) { (void)fdc_err(fdc, "sense intr err reading stat reg 0\n"); return ret; } if (st0p) *st0p = st0; if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) { /* * There doesn't seem to have been an interrupt. */ return FD_NOT_VALID; } if (fd_in(fdc, &cyl) < 0) { return fdc_err(fdc, "can't get cyl num\n"); } if (cylp) *cylp = cyl; return 0; } static int fd_read_status(fdc_p fdc, int fdsu) { int i, ret; for (i = 0; i < 7; i++) { /* * XXX types are poorly chosen. Only bytes can by read * from the hardware, but fdc->status[] wants u_ints and * fd_in() gives ints. */ int status; ret = fd_in(fdc, &status); fdc->status[i] = status; if (ret != 0) break; } if (ret == 0) fdc->flags |= FDC_STAT_VALID; else fdc->flags &= ~FDC_STAT_VALID; return ret; } /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ static struct isa_pnp_id fdc_ids[] = { {0x0007d041, "PC standard floppy disk controller"}, /* PNP0700 */ {0x0107d041, "Standard floppy controller supporting MS Device Bay Spec"}, /* PNP0701 */ {0} }; /* * fdc controller section. */ static int fdc_probe(device_t dev) { int error, ispnp, ic_type; struct fdc_data *fdc; /* Check pnp ids */ error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids); if (error == ENXIO) return ENXIO; ispnp = (error == 0); fdc = device_get_softc(dev); bzero(fdc, sizeof *fdc); fdc->fdc_dev = dev; fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0; fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0; fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, &fdc->rid_ioport, 0ul, ~0ul, ispnp ? 1 : IO_FDCSIZE, RF_ACTIVE); if (fdc->res_ioport == 0) { device_printf(dev, "cannot reserve I/O port range\n"); error = ENXIO; goto out; } fdc->portt = rman_get_bustag(fdc->res_ioport); fdc->porth = rman_get_bushandle(fdc->res_ioport); if (ispnp) { /* * Some bios' report the device at 0x3f2-0x3f5,0x3f7 * and some at 0x3f0-0x3f5,0x3f7. We detect the former * by checking the size and adjust the port address * accordingly. */ if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4) fdc->port_off = -2; fdc->flags |= FDC_ISPNP; fdc->rid_ctl = 1; fdc->res_ctl = bus_alloc_resource(dev, SYS_RES_IOPORT, &fdc->rid_ctl, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_ctl == 0) { device_printf(dev, "cannot reserve I/O port range\n"); error = ENXIO; goto out; } fdc->ctlt = rman_get_bustag(fdc->res_ctl); fdc->ctlh = rman_get_bushandle(fdc->res_ctl); } fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &fdc->rid_irq, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_irq == 0) { device_printf(dev, "cannot reserve interrupt line\n"); error = ENXIO; goto out; } fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ, &fdc->rid_drq, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_drq == 0) { device_printf(dev, "cannot reserve DMA request line\n"); error = ENXIO; goto out; } fdc->dmachan = fdc->res_drq->r_start; error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq, INTR_TYPE_BIO, fdc_intr, fdc, &fdc->fdc_intr); /* First - lets reset the floppy controller */ fdout_wr(fdc, 0); DELAY(100); fdout_wr(fdc, FDO_FRST); /* see if it can handle a command */ if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0)) { error = ENXIO; goto out; } if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { ic_type = (u_char)ic_type; switch (ic_type) { case 0x80: device_set_desc(dev, "NEC 765 or clone"); fdc->fdct = FDC_NE765; break; case 0x81: device_set_desc(dev, "Intel 82077 or clone"); fdc->fdct = FDC_I82077; break; case 0x90: device_set_desc(dev, "NEC 72065B or clone"); fdc->fdct = FDC_NE72065; break; default: device_set_desc(dev, "generic floppy controller"); fdc->fdct = FDC_UNKNOWN; break; } } #ifdef FDC_YE /* * don't succeed on probe; wait * for PCCARD subsystem to do it */ if (dev->id_flags & FDC_IS_PCMCIA) return(0); #endif return (0); out: if (fdc->fdc_intr) BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq, fdc->fdc_intr); if (fdc->res_irq != 0) { bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq, fdc->res_irq); bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq, fdc->res_irq); } if (fdc->res_ctl != 0) { bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, fdc->res_ctl); bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, fdc->res_ctl); } if (fdc->res_ioport != 0) { bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, fdc->res_ioport); bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, fdc->res_ioport); } if (fdc->res_drq != 0) { bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq, fdc->res_drq); bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq, fdc->res_drq); } return (error); } /* * Aped dfr@freebsd.org's isa_add_device(). */ static void fdc_add_device(device_t dev, const char *name, int unit) { int disabled, *ivar; device_t child; ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT); if (ivar == 0) return; if (resource_int_value(name, unit, "drive", ivar) != 0) *ivar = 0; - child = device_add_child(dev, name, unit, ivar); + child = device_add_child(dev, name, unit); + device_set_ivars(child, ivar); if (child == 0) return; if (resource_int_value(name, unit, "disabled", &disabled) == 0 && disabled != 0) device_disable(child); } static int fdc_attach(device_t dev) { struct fdc_data *fdc = device_get_softc(dev); fdcu_t fdcu = device_get_unit(dev); int i; for (i = resource_query_string(-1, "at", device_get_nameunit(dev)); i != -1; i = resource_query_string(i, "at", device_get_nameunit(dev))) fdc_add_device(dev, resource_query_name(i), resource_query_unit(i)); fdc->fdcu = fdcu; fdc->flags |= FDC_ATTACHED; /* Acquire the DMA channel forever, The driver will do the rest */ /* XXX should integrate with rman */ isa_dma_acquire(fdc->dmachan); isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */); fdc->state = DEVIDLE; /* reset controller, turn motor off, clear fdout mirror reg */ fdout_wr(fdc, ((fdc->fdout = 0))); bufq_init(&fdc->head); /* * Probe and attach any children as were configured above. */ return (bus_generic_attach(dev)); } static int fdc_print_child(device_t me, device_t child) { int retval = 0; retval += bus_print_child_header(me, child); retval += printf(" on %s drive %d\n", device_get_nameunit(me), *(int *)device_get_ivars(child)); return (retval); } static device_method_t fdc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fdc_probe), DEVMETHOD(device_attach, fdc_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, fdc_print_child), /* Our children never use any other bus interface methods. */ { 0, 0 } }; static driver_t fdc_driver = { "fdc", fdc_methods, sizeof(struct fdc_data) }; DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0); /******************************************************************/ /* * devices attached to the controller section. */ static int fd_probe(device_t dev) { int i; u_int fdt, st0, st3; struct fd_data *fd; struct fdc_data *fdc; fdsu_t fdsu; static int fd_fifo = 0; fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */ fd = device_get_softc(dev); fdc = device_get_softc(device_get_parent(dev)); bzero(fd, sizeof *fd); fd->dev = dev; fd->fdc = fdc; fd->fdsu = fdsu; fd->fdu = device_get_unit(dev); #ifdef __i386__ /* look up what bios thinks we have */ switch (fd->fdu) { case 0: if (device_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0) fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED; else fdt = (rtcin(RTC_FDISKETTE) & 0xf0); break; case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); break; default: fdt = RTCFDT_NONE; break; } #else fdt = RTCFDT_144M; /* XXX probably */ #endif /* is there a unit? */ if (fdt == RTCFDT_NONE) return (ENXIO); /* select it */ set_motor(fdc, fdsu, TURNON); DELAY(1000000); /* 1 sec */ /* XXX This doesn't work before the first set_motor() */ if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN && enable_fifo(fdc) == 0) { device_print_prettyname(device_get_parent(dev)); printf("FIFO enabled, %d bytes threshold\n", fifo_threshold); } fd_fifo = 1; if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* if at track 0, first seek inwards */ /* seek some steps: */ fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0); DELAY(300000); /* ...wait a moment... */ fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ } /* If we're at track 0 first seek inwards. */ if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* Seek some steps... */ if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { /* ...wait a moment... */ DELAY(300000); /* make ctrlr happy: */ fd_sense_int(fdc, 0, 0); } } for (i = 0; i < 2; i++) { /* * we must recalibrate twice, just in case the * heads have been beyond cylinder 76, since most * FDCs still barf when attempting to recalibrate * more than 77 steps */ /* go back to 0: */ if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) { /* a second being enough for full stroke seek*/ DELAY(i == 0 ? 1000000 : 300000); /* anything responding? */ if (fd_sense_int(fdc, &st0, 0) == 0 && (st0 & NE7_ST0_EC) == 0) break; /* already probed succesfully */ } } set_motor(fdc, fdsu, TURNOFF); if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ return (ENXIO); fd->track = FD_NO_TRACK; fd->fdc = fdc; fd->fdsu = fdsu; fd->options = 0; callout_handle_init(&fd->toffhandle); callout_handle_init(&fd->tohandle); switch (fdt) { case RTCFDT_12M: device_set_desc(dev, "1200-KB 5.25\" drive"); fd->type = FD_1200; break; case RTCFDT_144M | RTCFDT_144M_PRETENDED: device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive"); fdt = RTCFDT_144M; fd->type = FD_1440; case RTCFDT_144M: device_set_desc(dev, "1440-KB 3.5\" drive"); fd->type = FD_1440; break; case RTCFDT_288M: case RTCFDT_288M_1: device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); fd->type = FD_1440; break; case RTCFDT_360K: device_set_desc(dev, "360-KB 5.25\" drive"); fd->type = FD_360; break; case RTCFDT_720K: printf("720-KB 3.5\" drive"); fd->type = FD_720; break; default: return (ENXIO); } return (0); } static int fd_attach(device_t dev) { struct fd_data *fd; #if 0 int i; int mynor; int typemynor; int typesize; #endif fd = device_get_softc(dev); cdevsw_add(&fd_cdevsw); /* XXX */ make_dev(&fd_cdevsw, (fd->fdu << 6), UID_ROOT, GID_OPERATOR, 0640, "rfd%d", fd->fdu); #if 0 /* Other make_dev() go here. */ #endif /* * Export the drive to the devstat interface. */ devstat_add_entry(&fd->device_stats, device_get_name(dev), device_get_unit(dev), 512, DEVSTAT_NO_ORDERED_TAGS, DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, DEVSTAT_PRIORITY_FD); return (0); } static device_method_t fd_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fd_probe), DEVMETHOD(device_attach, fd_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX */ DEVMETHOD(device_resume, bus_generic_resume), /* XXX */ { 0, 0 } }; static driver_t fd_driver = { "fd", fd_methods, sizeof(struct fd_data) }; DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, 0, 0); /******************************************************************/ #ifdef FDC_YE /* * this is a subset of fdattach() optimized for the Y-E Data * PCMCIA floppy drive. */ static int yeattach(struct isa_device *dev) { fdcu_t fdcu = dev->id_unit; fdc_p fdc = fdc_data + fdcu; fdsu_t fdsu = 0; /* assume 1 drive per YE controller */ fdu_t fdu; fd_p fd; int st0, st3, i; fdc->fdcu = fdcu; /* * the FDC_PCMCIA flag is used to to indicate special PIO is used * instead of DMA */ fdc->flags = FDC_ATTACHED|FDC_PCMCIA; fdc->state = DEVIDLE; /* reset controller, turn motor off, clear fdout mirror reg */ fdout_wr(fdc, ((fdc->fdout = 0))); bufq_init(&fdc->head); /* * assume 2 drives/ "normal" controller */ fdu = fdcu * 2; if (fdu >= NFD) { printf("fdu %d >= NFD\n",fdu); return(0); }; fd = &fd_data[fdu]; set_motor(fdcu, fdsu, TURNON); DELAY(1000000); /* 1 sec */ fdc->fdct = FDC_NE765; if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* if at track 0, first seek inwards */ /* seek some steps: */ (void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0); DELAY(300000); /* ...wait a moment... */ (void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ } /* If we're at track 0 first seek inwards. */ if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* Seek some steps... */ if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { /* ...wait a moment... */ DELAY(300000); /* make ctrlr happy: */ (void)fd_sense_int(fdc, 0, 0); } } for(i = 0; i < 2; i++) { /* * we must recalibrate twice, just in case the * heads have been beyond cylinder 76, since most * FDCs still barf when attempting to recalibrate * more than 77 steps */ /* go back to 0: */ if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) { /* a second being enough for full stroke seek*/ DELAY(i == 0? 1000000: 300000); /* anything responding? */ if (fd_sense_int(fdc, &st0, 0) == 0 && (st0 & NE7_ST0_EC) == 0) break; /* already probed succesfully */ } } set_motor(fdcu, fdsu, TURNOFF); if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ return(0); fd->track = FD_NO_TRACK; fd->fdc = fdc; fd->fdsu = fdsu; fd->options = 0; printf("fdc%d: 1.44MB 3.5in PCMCIA\n", fdcu); fd->type = FD_1440; return (1); } #endif /****************************************************************************/ /* motor control stuff */ /* remember to not deselect the drive we're working on */ /****************************************************************************/ static void set_motor(struct fdc_data *fdc, int fdsu, int turnon) { int fdout = fdc->fdout; int needspecify = 0; if(turnon) { fdout &= ~FDO_FDSEL; fdout |= (FDO_MOEN0 << fdsu) + fdsu; } else fdout &= ~(FDO_MOEN0 << fdsu); if(!turnon && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) /* gonna turn off the last drive, put FDC to bed */ fdout &= ~ (FDO_FRST|FDO_FDMAEN); else { /* make sure controller is selected and specified */ if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) needspecify = 1; fdout |= (FDO_FRST|FDO_FDMAEN); } fdout_wr(fdc, fdout); fdc->fdout = fdout; TRACE1("[0x%x->FDOUT]", fdout); if (needspecify) { /* * XXX * special case: since we have just woken up the FDC * from its sleep, we silently assume the command will * be accepted, and do not test for a timeout */ (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); if (fdc->flags & FDC_HAS_FIFO) (void) enable_fifo(fdc); } } static void fd_turnoff(void *xfd) { int s; fd_p fd = xfd; TRACE1("[fd%d: turnoff]", fd->fdu); /* * Don't turn off the motor yet if the drive is active. * XXX shouldn't even schedule turnoff until drive is inactive * and nothing is queued on it. */ if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) { fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); return; } s = splbio(); fd->flags &= ~FD_MOTOR; set_motor(fd->fdc, fd->fdsu, TURNOFF); splx(s); } static void fd_motor_on(void *xfd) { int s; fd_p fd = xfd; s = splbio(); fd->flags &= ~FD_MOTOR_WAIT; if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) { fdc_intr(fd->fdc); } splx(s); } static void fd_turnon(fd_p fd) { if(!(fd->flags & FD_MOTOR)) { fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); set_motor(fd->fdc, fd->fdsu, TURNON); timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */ } } static void fdc_reset(fdc_p fdc) { /* Try a reset, keep motor on */ fdout_wr(fdc, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); DELAY(100); /* enable FDC, but defer interrupts a moment */ fdout_wr(fdc, fdc->fdout & ~FDO_FDMAEN); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); DELAY(100); fdout_wr(fdc, fdc->fdout); TRACE1("[0x%x->FDOUT]", fdc->fdout); /* XXX after a reset, silently believe the FDC will accept commands */ (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); if (fdc->flags & FDC_HAS_FIFO) (void) enable_fifo(fdc); } /****************************************************************************/ /* fdc in/out */ /****************************************************************************/ int in_fdc(struct fdc_data *fdc) { int i, j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = fddata_rd(fdc); TRACE1("[FDDATA->0x%x]", (unsigned char)i); return(i); #else /* !FDC_DEBUG */ return fddata_rd(fdc); #endif /* FDC_DEBUG */ } /* * fd_in: Like in_fdc, but allows you to see if it worked. */ static int fd_in(struct fdc_data *fdc, int *ptr) { int i, j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = fddata_rd(fdc); TRACE1("[FDDATA->0x%x]", (unsigned char)i); *ptr = i; return 0; #else /* !FDC_DEBUG */ i = fddata_rd(fdc); if (ptr) *ptr = i; return 0; #endif /* FDC_DEBUG */ } int out_fdc(struct fdc_data *fdc, int x) { int i; /* Check that the direction bit is set */ i = 100000; while ((fdsts_rd(fdc) & NE7_DIO) && i-- > 0); if (i <= 0) return fdc_err(fdc, "direction bit not set\n"); /* Check that the floppy controller is ready for a command */ i = 100000; while ((fdsts_rd(fdc) & NE7_RQM) == 0 && i-- > 0); if (i <= 0) return fdc_err(fdc, bootverbose? "output ready timeout\n": 0); /* Send the command and return */ fddata_wr(fdc, x); TRACE1("[0x%x->FDDATA]", x); return (0); } /****************************************************************************/ /* fdopen/fdclose */ /****************************************************************************/ int Fdopen(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); int type = FDTYPE(minor(dev)); fd_p fd; fdc_p fdc; /* check bounds */ if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0) return (ENXIO); fdc = fd->fdc; if ((fdc == NULL) || (fd->type == NO_TYPE)) return (ENXIO); if (type > NUMDENS) return (ENXIO); if (type == 0) type = fd->type; else { /* * For each type of basic drive, make sure we are trying * to open a type it can do, */ if (type != fd->type) { switch (fd->type) { case FD_360: return (ENXIO); case FD_720: if ( type != FD_820 && type != FD_800 ) return (ENXIO); break; case FD_1200: switch (type) { case FD_1480: type = FD_1480in5_25; break; case FD_1440: type = FD_1440in5_25; break; case FD_820: type = FD_820in5_25; break; case FD_800: type = FD_800in5_25; break; case FD_720: type = FD_720in5_25; break; case FD_360: type = FD_360in5_25; break; default: return(ENXIO); } break; case FD_1440: if ( type != FD_1720 && type != FD_1480 && type != FD_1200 && type != FD_820 && type != FD_800 && type != FD_720 ) return(ENXIO); break; } } } fd->ft = fd_types + type - 1; fd->flags |= FD_OPEN; device_busy(fd->dev); device_busy(fd->fdc->fdc_dev); return 0; } int fdclose(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); struct fd_data *fd; fd = devclass_get_softc(fd_devclass, fdu); fd->flags &= ~FD_OPEN; fd->options &= ~FDOPT_NORETRY; return (0); } /****************************************************************************/ /* fdstrategy */ /****************************************************************************/ void fdstrategy(struct buf *bp) { unsigned nblocks, blknum, cando; int s; fdu_t fdu; fdc_p fdc; fd_p fd; size_t fdblk; fdu = FDUNIT(minor(bp->b_dev)); fd = devclass_get_softc(fd_devclass, fdu); if (fd == 0) panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)", (u_long)major(bp->b_dev), (u_long)minor(bp->b_dev)); fdc = fd->fdc; #ifdef FDC_YE if (fd->type == NO_TYPE) { bp->b_error = ENXIO; bp->b_flags |= B_ERROR; /* * I _refuse_ to use a goto */ biodone(bp); return; }; #endif fdblk = 128 << (fd->ft->secsize); if (!(bp->b_flags & B_FORMAT)) { if (bp->b_blkno < 0) { printf( "fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n", fdu, (u_long)bp->b_blkno, bp->b_bcount); bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } if ((bp->b_bcount % fdblk) != 0) { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } } /* * Set up block calculations. */ if (bp->b_blkno > 20000000) { /* * Reject unreasonably high block number, prevent the * multiplication below from overflowing. */ bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk; nblocks = fd->ft->size; bp->b_resid = 0; if (blknum + (bp->b_bcount / fdblk) > nblocks) { if (blknum <= nblocks) { cando = (nblocks - blknum) * fdblk; bp->b_resid = bp->b_bcount - cando; if (cando == 0) goto bad; /* not actually bad but EOF */ } else { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } } bp->b_pblkno = bp->b_blkno; s = splbio(); bufqdisksort(&fdc->head, bp); untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */ /* Tell devstat we are starting on the transaction */ devstat_start_transaction(&fd->device_stats); fdstart(fdc); splx(s); return; bad: biodone(bp); } /***************************************************************\ * fdstart * * We have just queued something.. if the controller is not busy * * then simulate the case where it has just finished a command * * So that it (the interrupt routine) looks on the queue for more* * work to do and picks up what we just added. * * If the controller is already busy, we need do nothing, as it * * will pick up our work when the present work completes * \***************************************************************/ static void fdstart(struct fdc_data *fdc) { int s; s = splbio(); if(fdc->state == DEVIDLE) { fdc_intr(fdc); } splx(s); } static void fd_iotimeout(void *xfdc) { fdc_p fdc; int s; fdc = xfdc; TRACE1("fd%d[fd_iotimeout()]", fdc->fdu); /* * Due to IBM's brain-dead design, the FDC has a faked ready * signal, hardwired to ready == true. Thus, any command * issued if there's no diskette in the drive will _never_ * complete, and must be aborted by resetting the FDC. * Many thanks, Big Blue! * The FDC must not be reset directly, since that would * interfere with the state machine. Instead, pretend that * the command completed but was invalid. The state machine * will reset the FDC and retry once. */ s = splbio(); fdc->status[0] = NE7_ST0_IC_IV; fdc->flags &= ~FDC_STAT_VALID; fdc->state = IOTIMEDOUT; fdc_intr(fdc); splx(s); } /* just ensure it has the right spl */ static void fd_pseudointr(void *xfdc) { int s; s = splbio(); fdc_intr(xfdc); splx(s); } /***********************************************************************\ * fdintr * * keep calling the state machine until it returns a 0 * * ALWAYS called at SPLBIO * \***********************************************************************/ static void fdc_intr(void *xfdc) { fdc_p fdc = xfdc; while(fdstate(fdc)) ; } #ifdef FDC_YE /* * magic pseudo-DMA initialization for YE FDC. Sets count and * direction */ #define SET_BCDR(wr,cnt,port) outb(port,(((cnt)-1) & 0xff)); \ outb(port+1,((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f))) /* * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy */ static int fdcpio(fdcu_t fdcu, long flags, caddr_t addr, u_int count) { u_char *cptr = (u_char *)addr; fdc_p fdc = &fdc_data[fdcu]; int io = fdc->baseport; if (flags & B_READ) { if (fdc->state != PIOREAD) { fdc->state = PIOREAD; return(0); }; SET_BCDR(0,count,io); insb(io+FDC_YE_DATAPORT,cptr,count); } else { outsb(io+FDC_YE_DATAPORT,cptr,count); SET_BCDR(0,count,io); }; return(1); } #endif /* FDC_YE */ /***********************************************************************\ * The controller state machine. * * if it returns a non zero value, it should be called again immediatly * \***********************************************************************/ static int fdstate(fdc_p fdc) { int read, format, head, i, sec = 0, sectrac, st0, cyl, st3; unsigned blknum = 0, b_cylinder = 0; fdu_t fdu = fdc->fdu; fd_p fd; register struct buf *bp; struct fd_formb *finfo = NULL; size_t fdblk; bp = fdc->bp; if (bp == NULL) { bp = bufq_first(&fdc->head); if (bp != NULL) { bufq_remove(&fdc->head, bp); fdc->bp = bp; } } if (bp == NULL) { /***********************************************\ * nothing left for this controller to do * * Force into the IDLE state, * \***********************************************/ fdc->state = DEVIDLE; if (fdc->fd) { device_print_prettyname(fdc->fdc_dev); printf("unexpected valid fd pointer\n"); fdc->fd = (fd_p) 0; fdc->fdu = -1; } TRACE1("[fdc%d IDLE]", fdc->fdcu); return (0); } fdu = FDUNIT(minor(bp->b_dev)); fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; if (fdc->fd && (fd != fdc->fd)) { device_print_prettyname(fd->dev); printf("confused fd pointers\n"); } read = bp->b_flags & B_READ; format = bp->b_flags & B_FORMAT; if (format) { finfo = (struct fd_formb *)bp->b_data; fd->skip = (char *)&(finfo->fd_formb_cylno(0)) - (char *)finfo; } if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) { blknum = (unsigned) bp->b_pblkno * DEV_BSIZE/fdblk + fd->skip/fdblk; b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); } TRACE1("fd%d", fdu); TRACE1("[%s]", fdstates[fdc->state]); TRACE1("(0x%x)", fd->flags); untimeout(fd_turnoff, fd, fd->toffhandle); fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); switch (fdc->state) { case DEVIDLE: case FINDWORK: /* we have found new work */ fdc->retry = 0; fd->skip = 0; fdc->fd = fd; fdc->fdu = fdu; fdctl_wr(fdc, fd->ft->trans); TRACE1("[0x%x->FDCTL]", fd->ft->trans); /*******************************************************\ * If the next drive has a motor startup pending, then * * it will start up in its own good time * \*******************************************************/ if(fd->flags & FD_MOTOR_WAIT) { fdc->state = MOTORWAIT; return (0); /* come back later */ } /*******************************************************\ * Maybe if it's not starting, it SHOULD be starting * \*******************************************************/ if (!(fd->flags & FD_MOTOR)) { fdc->state = MOTORWAIT; fd_turnon(fd); return (0); } else /* at least make sure we are selected */ { set_motor(fdc, fd->fdsu, TURNON); } if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else fdc->state = DOSEEK; break; case DOSEEK: if (b_cylinder == (unsigned)fd->track) { fdc->state = SEEKCOMPLETE; break; } if (fd_cmd(fdc, 3, NE7CMD_SEEK, fd->fdsu, b_cylinder * fd->ft->steptrac, 0)) { /* * seek command not accepted, looks like * the FDC went off to the Saints... */ fdc->retry = 6; /* try a reset */ return(retrier(fdc)); } fd->track = FD_NO_TRACK; fdc->state = SEEKWAIT; return(0); /* will return later */ case SEEKWAIT: /* allow heads to settle */ timeout(fd_pseudointr, fdc, hz / 16); fdc->state = SEEKCOMPLETE; return(0); /* will return later */ case SEEKCOMPLETE : /* SEEK DONE, START DMA */ /* Make sure seek really happened*/ if(fd->track == FD_NO_TRACK) { int descyl = b_cylinder * fd->ft->steptrac; do { /* * This might be a "ready changed" interrupt, * which cannot really happen since the * RDY pin is hardwired to + 5 volts. This * generally indicates a "bouncing" intr * line, so do one of the following: * * When running on an enhanced FDC that is * known to not go stuck after responding * with INVALID, fetch all interrupt states * until seeing either an INVALID or a * real interrupt condition. * * When running on a dumb old NE765, give * up immediately. The controller will * provide up to four dummy RC interrupt * conditions right after reset (for the * corresponding four drives), so this is * our only chance to get notice that it * was not the FDC that caused the interrupt. */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); if (0 == descyl) { int failed = 0; /* * seek to cyl 0 requested; make sure we are * really there */ if (fd_sense_drive_status(fdc, &st3)) failed = 1; if ((st3 & NE7_ST3_T0) == 0) { printf( "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", fdu, st3, NE7_ST3BITS); failed = 1; } if (failed) { if(fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } } if (cyl != descyl) { printf( "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", fdu, descyl, cyl, st0); if (fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } } fd->track = b_cylinder; #ifdef FDC_YE if (!(fdc->flags & FDC_PCMCIA)) #endif isa_dmastart(bp->b_flags, bp->b_data+fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; fd->hddrv = ((head&1)<<2)+fdu; if(format || !read) { /* make sure the drive is writable */ if(fd_sense_drive_status(fdc, &st3) != 0) { /* stuck controller? */ isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); fdc->retry = 6; /* reset the beast */ return (retrier(fdc)); } if(st3 & NE7_ST3_WP) { /* * XXX YES! this is ugly. * in order to force the current operation * to fail, we will have to fake an FDC * error - all error handling is done * by the retrier() */ fdc->status[0] = NE7_ST0_IC_AT; fdc->status[1] = NE7_ST1_NW; fdc->status[2] = 0; fdc->status[3] = fd->track; fdc->status[4] = head; fdc->status[5] = sec; fdc->retry = 8; /* break out immediately */ fdc->state = IOTIMEDOUT; /* not really... */ return (1); } } if (format) { #ifdef FDC_YE if (fdc->flags & FDC_PCMCIA) (void)fdcpio(fdcu,bp->b_flags, bp->b_data+fd->skip, bp->b_bcount); #endif /* formatting */ if(fd_cmd(fdc, 6, NE7CMD_FORMAT, head << 2 | fdu, finfo->fd_formb_secshift, finfo->fd_formb_nsecs, finfo->fd_formb_gaplen, finfo->fd_formb_fillbyte, 0)) { /* controller fell over */ isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); fdc->retry = 6; return (retrier(fdc)); } } else { #ifdef FDC_YE if (fdc->flags & FDC_PCMCIA) { /* * this seems to be necessary even when * reading data */ SET_BCDR(1,fdblk,fdc->baseport); /* * perform the write pseudo-DMA before * the WRITE command is sent */ if (!read) (void)fdcpio(fdcu,bp->b_flags, bp->b_data+fd->skip, fdblk); } #endif if (fd_cmd(fdc, 9, (read ? NE7CMD_READ : NE7CMD_WRITE), head << 2 | fdu, /* head & unit */ fd->track, /* track */ head, sec, /* sector + 1 */ fd->ft->secsize, /* sector size */ sectrac, /* sectors/track */ fd->ft->gap, /* gap size */ fd->ft->datalen, /* data length */ 0)) { /* the beast is sleeping again */ isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); fdc->retry = 6; return (retrier(fdc)); } } #ifdef FDC_YE if (fdc->flags & FDC_PCMCIA) /* * if this is a read, then simply await interrupt * before performing PIO */ if (read && !fdcpio(fdcu,bp->b_flags, bp->b_data+fd->skip,fdblk)) { fd->tohandle = timeout(fd_iotimeout, (caddr_t)fdcu, hz); return(0); /* will return later */ }; /* * write (or format) operation will fall through and * await completion interrupt */ #endif fdc->state = IOCOMPLETE; fd->tohandle = timeout(fd_iotimeout, fdc, hz); return (0); /* will return later */ #ifdef FDC_YE case PIOREAD: /* * actually perform the PIO read. The IOCOMPLETE case * removes the timeout for us. */ (void)fdcpio(fdcu,bp->b_flags,bp->b_data+fd->skip,fdblk); fdc->state = IOCOMPLETE; /* FALLTHROUGH */ #endif case IOCOMPLETE: /* IO DONE, post-analyze */ untimeout(fd_iotimeout, fdc, fd->tohandle); if (fd_read_status(fdc, fd->fdsu)) { isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); if (fdc->retry < 6) fdc->retry = 6; /* force a reset */ return (retrier(fdc)); } fdc->state = IOTIMEDOUT; /* FALLTHROUGH */ case IOTIMEDOUT: #ifdef FDC_YE if (!(fdc->flags & FDC_PCMCIA)) #endif isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); if (fdc->status[0] & NE7_ST0_IC) { if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[1] & NE7_ST1_OR) { /* * DMA overrun. Someone hogged the bus * and didn't release it in time for the * next FDC transfer. * Just restart it, don't increment retry * count. (vak) */ fdc->state = SEEKCOMPLETE; return (1); } else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV && fdc->retry < 6) fdc->retry = 6; /* force a reset */ else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[2] & NE7_ST2_WC && fdc->retry < 3) fdc->retry = 3; /* force recalibrate */ return (retrier(fdc)); } /* All OK */ fd->skip += fdblk; if (!format && fd->skip < bp->b_bcount - bp->b_resid) { /* set up next transfer */ fdc->state = DOSEEK; } else { /* ALL DONE */ fd->skip = 0; fdc->bp = NULL; devstat_end_transaction_buf(&fd->device_stats, bp); biodone(bp); fdc->fd = (fd_p) 0; fdc->fdu = -1; fdc->state = FINDWORK; } return (1); case RESETCTLR: fdc_reset(fdc); fdc->retry++; fdc->state = RESETCOMPLETE; return (0); case RESETCOMPLETE: /* * Discard all the results from the reset so that they * can't cause an unexpected interrupt later. */ for (i = 0; i < 4; i++) (void)fd_sense_int(fdc, &st0, &cyl); fdc->state = STARTRECAL; /* Fall through. */ case STARTRECAL: if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) { /* arrgl */ fdc->retry = 6; return (retrier(fdc)); } fdc->state = RECALWAIT; return (0); /* will return later */ case RECALWAIT: /* allow heads to settle */ timeout(fd_pseudointr, fdc, hz / 8); fdc->state = RECALCOMPLETE; return (0); /* will return later */ case RECALCOMPLETE: do { /* * See SEEKCOMPLETE for a comment on this: */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) { if(fdc->retry > 3) /* * a recalibrate from beyond cylinder 77 * will "fail" due to the FDC limitations; * since people used to complain much about * the failure message, try not logging * this one if it seems to be the first * time in a line */ printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, st0, NE7_ST0BITS, cyl); if(fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } fd->track = 0; /* Seek (probably) necessary */ fdc->state = DOSEEK; return (1); /* will return immediatly */ case MOTORWAIT: if(fd->flags & FD_MOTOR_WAIT) { return (0); /* time's not up yet */ } if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else { /* * If all motors were off, then the controller was * reset, so it has lost track of the current * cylinder. Recalibrate to handle this case. * But first, discard the results of the reset. */ fdc->state = RESETCOMPLETE; } return (1); /* will return immediatly */ default: device_print_prettyname(fdc->fdc_dev); printf("unexpected FD int->"); if (fd_read_status(fdc, fd->fdsu) == 0) printf("FDC status :%x %x %x %x %x %x %x ", fdc->status[0], fdc->status[1], fdc->status[2], fdc->status[3], fdc->status[4], fdc->status[5], fdc->status[6] ); else printf("No status available "); if (fd_sense_int(fdc, &st0, &cyl) != 0) { printf("[controller is dead now]\n"); return (0); } printf("ST0 = %x, PCN = %x\n", st0, cyl); return (0); } /*XXX confusing: some branches return immediately, others end up here*/ return (1); /* Come back immediatly to new state */ } static int retrier(struct fdc_data *fdc) { register struct buf *bp; struct fd_data *fd; int fdu; bp = fdc->bp; /* XXX shouldn't this be cached somewhere? */ fdu = FDUNIT(minor(bp->b_dev)); fd = devclass_get_softc(fd_devclass, fdu); if (fd->options & FDOPT_NORETRY) goto fail; switch (fdc->retry) { case 0: case 1: case 2: fdc->state = SEEKCOMPLETE; break; case 3: case 4: case 5: fdc->state = STARTRECAL; break; case 6: fdc->state = RESETCTLR; break; case 7: break; default: fail: { dev_t sav_b_dev = bp->b_dev; /* Trick diskerr */ bp->b_dev = makedev(major(bp->b_dev), (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART); diskerr(bp, "hard error", LOG_PRINTF, fdc->fd->skip / DEV_BSIZE, (struct disklabel *)NULL); bp->b_dev = sav_b_dev; if (fdc->flags & FDC_STAT_VALID) { printf( " (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n", fdc->status[0], NE7_ST0BITS, fdc->status[1], NE7_ST1BITS, fdc->status[2], NE7_ST2BITS, fdc->status[3], fdc->status[4], fdc->status[5]); } else printf(" (No status)\n"); } bp->b_flags |= B_ERROR; bp->b_error = EIO; bp->b_resid += bp->b_bcount - fdc->fd->skip; fdc->bp = NULL; fdc->fd->skip = 0; devstat_end_transaction_buf(&fdc->fd->device_stats, bp); biodone(bp); fdc->state = FINDWORK; fdc->flags |= FDC_NEEDS_RESET; fdc->fd = (fd_p) 0; fdc->fdu = -1; return (1); } fdc->retry++; return (1); } static int fdformat(dev, finfo, p) dev_t dev; struct fd_formb *finfo; struct proc *p; { fdu_t fdu; fd_p fd; struct buf *bp; int rv = 0, s; size_t fdblk; fdu = FDUNIT(minor(dev)); fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; /* set up a buffer header for fdstrategy() */ bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); if(bp == 0) return ENOBUFS; /* * keep the process from being swapped */ PHOLD(p); bzero((void *)bp, sizeof(struct buf)); BUF_LOCKINIT(bp); BUF_LOCK(bp, LK_EXCLUSIVE); bp->b_flags = B_PHYS | B_FORMAT; /* * calculate a fake blkno, so fdstrategy() would initiate a * seek to the requested cylinder */ bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; bp->b_data = (caddr_t)finfo; /* now do the format */ bp->b_dev = dev; BUF_STRATEGY(bp, 0); /* ...and wait for it to complete */ s = splbio(); while(!(bp->b_flags & B_DONE)) { rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz); if (rv == EWOULDBLOCK) break; } splx(s); if (rv == EWOULDBLOCK) { /* timed out */ rv = EIO; biodone(bp); } if (bp->b_flags & B_ERROR) rv = bp->b_error; /* * allow the process to be swapped */ PRELE(p); BUF_UNLOCK(bp); BUF_LOCKFREE(bp); free(bp, M_TEMP); return rv; } /* * TODO: don't allocate buffer on stack. */ static int fdioctl(dev, cmd, addr, flag, p) dev_t dev; u_long cmd; caddr_t addr; int flag; struct proc *p; { fdu_t fdu = FDUNIT(minor(dev)); fd_p fd = devclass_get_softc(fd_devclass, fdu); size_t fdblk; struct fd_type *fdt; struct disklabel *dl; char buffer[DEV_BSIZE]; int error = 0; fdblk = 128 << fd->ft->secsize; switch (cmd) { case DIOCGDINFO: bzero(buffer, sizeof (buffer)); dl = (struct disklabel *)buffer; dl->d_secsize = fdblk; fdt = fd->ft; dl->d_secpercyl = fdt->size / fdt->tracks; dl->d_type = DTYPE_FLOPPY; if (readdisklabel(dkmodpart(dev, RAW_PART), dl) == NULL) error = 0; else error = EINVAL; *(struct disklabel *)addr = *dl; break; case DIOCSDINFO: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWLABEL: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWDINFO: if ((flag & FWRITE) == 0) { error = EBADF; break; } dl = (struct disklabel *)addr; if ((error = setdisklabel((struct disklabel *)buffer, dl, (u_long)0)) != 0) break; error = writedisklabel(dev, (struct disklabel *)buffer); break; case FD_FORM: if ((flag & FWRITE) == 0) error = EBADF; /* must be opened for writing */ else if (((struct fd_formb *)addr)->format_version != FD_FORMAT_VERSION) error = EINVAL; /* wrong version of formatting prog */ else error = fdformat(dev, (struct fd_formb *)addr, p); break; case FD_GTYPE: /* get drive type */ *(struct fd_type *)addr = *fd->ft; break; case FD_STYPE: /* set drive type */ /* this is considered harmful; only allow for superuser */ if (suser(p) != 0) return EPERM; *fd->ft = *(struct fd_type *)addr; break; case FD_GOPTS: /* get drive options */ *(int *)addr = fd->options; break; case FD_SOPTS: /* set drive options */ fd->options = *(int *)addr; break; default: error = ENOTTY; break; } return (error); } /* * Hello emacs, these are the * Local Variables: * c-indent-level: 8 * c-continued-statement-offset: 8 * c-continued-brace-offset: 0 * c-brace-offset: -8 * c-brace-imaginary-offset: 0 * c-argdecl-indent: 8 * c-label-offset: -8 * c++-hanging-braces: 1 * c++-access-specifier-offset: -8 * c++-empty-arglist-indent: 8 * c++-friend-offset: 0 * End: */ Index: head/sys/isa/isa_common.c =================================================================== --- head/sys/isa/isa_common.c (revision 54072) +++ head/sys/isa/isa_common.c (revision 54073) @@ -1,1040 +1,1044 @@ /*- * Copyright (c) 1999 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Modifications for Intel architecture by Garrett A. Wollman. * Copyright 1998 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Parts of the ISA bus implementation common to all architectures. */ #include #include #include #include #include #include #include #include #include #include #include #ifdef __alpha__ /* XXX workaround a stupid warning */ #include #endif MALLOC_DEFINE(M_ISADEV, "isadev", "ISA device"); static devclass_t isa_devclass; static int isa_running; /* * At 'probe' time, we add all the devices which we know about to the * bus. The generic attach routine will probe and attach them if they * are alive. */ static int isa_probe(device_t dev) { device_set_desc(dev, "ISA bus"); isa_init(); /* Allow machdep code to initialise */ return 0; } extern device_t isa_bus_device; static int isa_attach(device_t dev) { /* * Arrange for isa_probe_children(dev) to be called later. XXX */ isa_bus_device = dev; return 0; } /* * Find a working set of memory regions for a child using the ranges * in *config and return the regions in *result. Returns non-zero if * a set of ranges was found. */ static int isa_find_memory(device_t child, struct isa_config *config, struct isa_config *result) { int success, i; struct resource *res[ISA_NMEM]; /* * First clear out any existing resource definitions. */ for (i = 0; i < ISA_NMEM; i++) { bus_delete_resource(child, SYS_RES_MEMORY, i); res[i] = NULL; } success = 1; result->ic_nmem = config->ic_nmem; for (i = 0; i < config->ic_nmem; i++) { u_int32_t start, end, size, align; for (start = config->ic_mem[i].ir_start, end = config->ic_mem[i].ir_end, size = config->ic_mem[i].ir_size, align = config->ic_mem[i].ir_align; start + size - 1 <= end; start += align) { bus_set_resource(child, SYS_RES_MEMORY, i, start, size); res[i] = bus_alloc_resource(child, SYS_RES_MEMORY, &i, 0, ~0, 1, RF_ACTIVE); if (res[i]) { result->ic_mem[i].ir_start = start; result->ic_mem[i].ir_end = start + size - 1; result->ic_mem[i].ir_size = size; result->ic_mem[i].ir_align = align; break; } } /* * If we didn't find a place for memory range i, then * give up now. */ if (!res[i]) { success = 0; break; } } for (i = 0; i < ISA_NMEM; i++) { if (res[i]) bus_release_resource(child, SYS_RES_MEMORY, i, res[i]); } return success; } /* * Find a working set of port regions for a child using the ranges * in *config and return the regions in *result. Returns non-zero if * a set of ranges was found. */ static int isa_find_port(device_t child, struct isa_config *config, struct isa_config *result) { int success, i; struct resource *res[ISA_NPORT]; /* * First clear out any existing resource definitions. */ for (i = 0; i < ISA_NPORT; i++) { bus_delete_resource(child, SYS_RES_IOPORT, i); res[i] = NULL; } success = 1; result->ic_nport = config->ic_nport; for (i = 0; i < config->ic_nport; i++) { u_int32_t start, end, size, align; for (start = config->ic_port[i].ir_start, end = config->ic_port[i].ir_end, size = config->ic_port[i].ir_size, align = config->ic_port[i].ir_align; start + size - 1 <= end; start += align) { bus_set_resource(child, SYS_RES_IOPORT, i, start, size); res[i] = bus_alloc_resource(child, SYS_RES_IOPORT, &i, 0, ~0, 1, RF_ACTIVE); if (res[i]) { result->ic_port[i].ir_start = start; result->ic_port[i].ir_end = start + size - 1; result->ic_port[i].ir_size = size; result->ic_port[i].ir_align = align; break; } } /* * If we didn't find a place for port range i, then * give up now. */ if (!res[i]) { success = 0; break; } } for (i = 0; i < ISA_NPORT; i++) { if (res[i]) bus_release_resource(child, SYS_RES_IOPORT, i, res[i]); } return success; } /* * Return the index of the first bit in the mask (or -1 if mask is empty. */ static int find_first_bit(u_int32_t mask) { return ffs(mask) - 1; } /* * Return the index of the next bit in the mask, or -1 if there are no more. */ static int find_next_bit(u_int32_t mask, int bit) { bit++; while (bit < 32 && !(mask & (1 << bit))) bit++; if (bit != 32) return bit; return -1; } /* * Find a working set of irqs for a child using the masks in *config * and return the regions in *result. Returns non-zero if a set of * irqs was found. */ static int isa_find_irq(device_t child, struct isa_config *config, struct isa_config *result) { int success, i; struct resource *res[ISA_NIRQ]; /* * First clear out any existing resource definitions. */ for (i = 0; i < ISA_NIRQ; i++) { bus_delete_resource(child, SYS_RES_IRQ, i); res[i] = NULL; } success = 1; result->ic_nirq = config->ic_nirq; for (i = 0; i < config->ic_nirq; i++) { u_int32_t mask = config->ic_irqmask[i]; int irq; for (irq = find_first_bit(mask); irq != -1; irq = find_next_bit(mask, irq)) { bus_set_resource(child, SYS_RES_IRQ, i, irq, 1); res[i] = bus_alloc_resource(child, SYS_RES_IRQ, &i, 0, ~0, 1, RF_ACTIVE); if (res[i]) { result->ic_irqmask[i] = (1 << irq); break; } } /* * If we didn't find a place for irq range i, then * give up now. */ if (!res[i]) { success = 0; break; } } for (i = 0; i < ISA_NIRQ; i++) { if (res[i]) bus_release_resource(child, SYS_RES_IRQ, i, res[i]); } return success; } /* * Find a working set of drqs for a child using the masks in *config * and return the regions in *result. Returns non-zero if a set of * drqs was found. */ static int isa_find_drq(device_t child, struct isa_config *config, struct isa_config *result) { int success, i; struct resource *res[ISA_NDRQ]; /* * First clear out any existing resource definitions. */ for (i = 0; i < ISA_NDRQ; i++) { bus_delete_resource(child, SYS_RES_DRQ, i); res[i] = NULL; } success = 1; result->ic_ndrq = config->ic_ndrq; for (i = 0; i < config->ic_ndrq; i++) { u_int32_t mask = config->ic_drqmask[i]; int drq; for (drq = find_first_bit(mask); drq != -1; drq = find_next_bit(mask, drq)) { bus_set_resource(child, SYS_RES_DRQ, i, drq, 1); res[i] = bus_alloc_resource(child, SYS_RES_DRQ, &i, 0, ~0, 1, RF_ACTIVE); if (res[i]) { result->ic_drqmask[i] = (1 << drq); break; } } /* * If we didn't find a place for drq range i, then * give up now. */ if (!res[i]) { success = 0; break; } } for (i = 0; i < ISA_NDRQ; i++) { if (res[i]) bus_release_resource(child, SYS_RES_DRQ, i, res[i]); } return success; } /* * Attempt to find a working set of resources for a device. Return * non-zero if a working configuration is found. */ static int isa_assign_resources(device_t child) { struct isa_device *idev = DEVTOISA(child); struct isa_config_entry *ice; struct isa_config config; bzero(&config, sizeof config); TAILQ_FOREACH(ice, &idev->id_configs, ice_link) { if (!isa_find_memory(child, &ice->ice_config, &config)) continue; if (!isa_find_port(child, &ice->ice_config, &config)) continue; if (!isa_find_irq(child, &ice->ice_config, &config)) continue; if (!isa_find_drq(child, &ice->ice_config, &config)) continue; /* * A working configuration was found enable the device * with this configuration. */ if (idev->id_config_cb) { idev->id_config_cb(idev->id_config_arg, &config, 1); return 1; } } /* * Disable the device. */ if (device_get_desc(child)) device_printf(child, "<%s> can't assign resources\n", device_get_desc(child)); else device_printf(child, "can't assign resources\n"); bzero(&config, sizeof config); if (idev->id_config_cb) idev->id_config_cb(idev->id_config_arg, &config, 0); device_disable(child); return 0; } /* * Called after other devices have initialised to probe for isa devices. */ void isa_probe_children(device_t dev) { device_t *children; int nchildren, i; /* * Create all the children by calling driver's identify methods. */ bus_generic_probe(dev); if (device_get_children(dev, &children, &nchildren)) return; /* * First disable all pnp devices so that they don't get * matched by legacy probes. */ if (bootverbose) printf("isa_probe_children: disabling PnP devices\n"); for (i = 0; i < nchildren; i++) { device_t child = children[i]; struct isa_device *idev = DEVTOISA(child); struct isa_config config; bzero(&config, sizeof config); if (idev->id_config_cb) idev->id_config_cb(idev->id_config_arg, &config, 0); } /* * Next probe all non-pnp devices so that they claim their * resources first. */ if (bootverbose) printf("isa_probe_children: probing non-PnP devices\n"); for (i = 0; i < nchildren; i++) { device_t child = children[i]; struct isa_device *idev = DEVTOISA(child); if (TAILQ_FIRST(&idev->id_configs)) continue; device_probe_and_attach(child); } /* * Finally assign resource to pnp devices and probe them. */ if (bootverbose) printf("isa_probe_children: probing PnP devices\n"); for (i = 0; i < nchildren; i++) { device_t child = children[i]; struct isa_device* idev = DEVTOISA(child); if (!TAILQ_FIRST(&idev->id_configs)) continue; if (isa_assign_resources(child)) { struct resource_list *rl = &idev->id_resources; struct resource_list_entry *rle; device_probe_and_attach(child); /* * Claim any unallocated resources to keep other * devices from using them. */ SLIST_FOREACH(rle, rl, link) { if (!rle->res) { int rid = rle->rid; resource_list_alloc(rl, dev, child, rle->type, &rid, 0, ~0, 1, RF_ACTIVE); } } } } free(children, M_TEMP); isa_running = 1; } /* * Add a new child with default ivars. */ static device_t isa_add_child(device_t dev, int order, const char *name, int unit) { + device_t child; struct isa_device *idev; idev = malloc(sizeof(struct isa_device), M_ISADEV, M_NOWAIT); if (!idev) return 0; bzero(idev, sizeof *idev); resource_list_init(&idev->id_resources); TAILQ_INIT(&idev->id_configs); - return device_add_child_ordered(dev, order, name, unit, idev); + child = device_add_child_ordered(dev, order, name, unit); + device_set_ivars(child, idev); + + return child; } static void isa_print_resources(struct resource_list *rl, const char *name, int type, int count, const char *format) { struct resource_list_entry *rle; int printed; int i; printed = 0; for (i = 0; i < count; i++) { rle = resource_list_find(rl, type, i); if (rle) { if (printed == 0) printf(" %s ", name); else if (printed > 0) printf(","); printed++; printf(format, rle->start); if (rle->count > 1) { printf("-"); printf(format, rle->start + rle->count - 1); } } else if (i > 3) { /* check the first few regardless */ break; } } } static int isa_print_child(device_t bus, device_t dev) { struct isa_device *idev = DEVTOISA(dev); struct resource_list *rl = &idev->id_resources; int retval = 0; retval += bus_print_child_header(bus, dev); if (SLIST_FIRST(rl) || device_get_flags(dev)) retval += printf(" at"); isa_print_resources(rl, "port", SYS_RES_IOPORT, ISA_NPORT, "%#lx"); isa_print_resources(rl, "iomem", SYS_RES_MEMORY, ISA_NMEM, "%#lx"); isa_print_resources(rl, "irq", SYS_RES_IRQ, ISA_NIRQ, "%ld"); isa_print_resources(rl, "drq", SYS_RES_DRQ, ISA_NDRQ, "%ld"); if (device_get_flags(dev)) retval += printf(" flags %#x", device_get_flags(dev)); retval += bus_print_child_footer(bus, dev); return (retval); } static int isa_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result) { struct isa_device* idev = DEVTOISA(dev); struct resource_list *rl = &idev->id_resources; struct resource_list_entry *rle; switch (index) { case ISA_IVAR_PORT_0: rle = resource_list_find(rl, SYS_RES_IOPORT, 0); if (rle) *result = rle->start; else *result = -1; break; case ISA_IVAR_PORT_1: rle = resource_list_find(rl, SYS_RES_IOPORT, 1); if (rle) *result = rle->start; else *result = -1; break; case ISA_IVAR_PORTSIZE_0: rle = resource_list_find(rl, SYS_RES_IOPORT, 0); if (rle) *result = rle->count; else *result = 0; break; case ISA_IVAR_PORTSIZE_1: rle = resource_list_find(rl, SYS_RES_IOPORT, 1); if (rle) *result = rle->count; else *result = 0; break; case ISA_IVAR_MADDR_0: rle = resource_list_find(rl, SYS_RES_MEMORY, 0); if (rle) *result = rle->start; else *result = -1; break; case ISA_IVAR_MADDR_1: rle = resource_list_find(rl, SYS_RES_MEMORY, 1); if (rle) *result = rle->start; else *result = -1; break; case ISA_IVAR_MSIZE_0: rle = resource_list_find(rl, SYS_RES_MEMORY, 0); if (rle) *result = rle->count; else *result = 0; break; case ISA_IVAR_MSIZE_1: rle = resource_list_find(rl, SYS_RES_MEMORY, 1); if (rle) *result = rle->count; else *result = 0; break; case ISA_IVAR_IRQ_0: rle = resource_list_find(rl, SYS_RES_IRQ, 0); if (rle) *result = rle->start; else *result = -1; break; case ISA_IVAR_IRQ_1: rle = resource_list_find(rl, SYS_RES_IRQ, 1); if (rle) *result = rle->start; else *result = -1; break; case ISA_IVAR_DRQ_0: rle = resource_list_find(rl, SYS_RES_DRQ, 0); if (rle) *result = rle->start; else *result = -1; break; case ISA_IVAR_DRQ_1: rle = resource_list_find(rl, SYS_RES_DRQ, 1); if (rle) *result = rle->start; else *result = -1; break; case ISA_IVAR_VENDORID: *result = idev->id_vendorid; break; case ISA_IVAR_SERIAL: *result = idev->id_serial; break; case ISA_IVAR_LOGICALID: *result = idev->id_logicalid; break; case ISA_IVAR_COMPATID: *result = idev->id_compatid; break; default: return ENOENT; } return 0; } static int isa_write_ivar(device_t bus, device_t dev, int index, uintptr_t value) { struct isa_device* idev = DEVTOISA(dev); switch (index) { case ISA_IVAR_PORT_0: case ISA_IVAR_PORT_1: case ISA_IVAR_PORTSIZE_0: case ISA_IVAR_PORTSIZE_1: case ISA_IVAR_MADDR_0: case ISA_IVAR_MADDR_1: case ISA_IVAR_MSIZE_0: case ISA_IVAR_MSIZE_1: case ISA_IVAR_IRQ_0: case ISA_IVAR_IRQ_1: case ISA_IVAR_DRQ_0: case ISA_IVAR_DRQ_1: return EINVAL; case ISA_IVAR_VENDORID: idev->id_vendorid = value; break; case ISA_IVAR_SERIAL: idev->id_serial = value; break; case ISA_IVAR_LOGICALID: idev->id_logicalid = value; break; case ISA_IVAR_COMPATID: idev->id_compatid = value; break; default: return (ENOENT); } return (0); } /* * Free any resources which the driver missed or which we were holding for * it (see isa_probe_children). */ static void isa_child_detached(device_t dev, device_t child) { struct isa_device* idev = DEVTOISA(child); struct resource_list *rl = &idev->id_resources; struct resource_list_entry *rle; SLIST_FOREACH(rle, &idev->id_resources, link) { if (rle->res) resource_list_release(rl, dev, child, rle->type, rle->rid, rle->res); } } static void isa_driver_added(device_t dev, driver_t *driver) { device_t *children; int nchildren, i; /* * Don't do anything if drivers are dynamically * added during autoconfiguration (cf. ymf724). * since that would end up calling identify * twice. */ if (!isa_running) return; DEVICE_IDENTIFY(driver, dev); if (device_get_children(dev, &children, &nchildren)) return; for (i = 0; i < nchildren; i++) { device_t child = children[i]; struct isa_device *idev = DEVTOISA(child); struct resource_list *rl = &idev->id_resources; struct resource_list_entry *rle; if (device_get_state(child) != DS_NOTPRESENT) continue; if (TAILQ_FIRST(&idev->id_configs)) if (!isa_assign_resources(child)) continue; device_probe_and_attach(child); if (TAILQ_FIRST(&idev->id_configs)) { /* * Claim any unallocated resources to keep other * devices from using them. */ SLIST_FOREACH(rle, rl, link) { if (!rle->res) { int rid = rle->rid; resource_list_alloc(rl, dev, child, rle->type, &rid, 0, ~0, 1, RF_ACTIVE); } } } } free(children, M_TEMP); } static int isa_set_resource(device_t dev, device_t child, int type, int rid, u_long start, u_long count) { struct isa_device* idev = DEVTOISA(child); struct resource_list *rl = &idev->id_resources; if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY && type != SYS_RES_IRQ && type != SYS_RES_DRQ) return EINVAL; if (rid < 0) return EINVAL; if (type == SYS_RES_IOPORT && rid >= ISA_NPORT) return EINVAL; if (type == SYS_RES_MEMORY && rid >= ISA_NMEM) return EINVAL; if (type == SYS_RES_IRQ && rid >= ISA_NIRQ) return EINVAL; if (type == SYS_RES_DRQ && rid >= ISA_NDRQ) return EINVAL; resource_list_add(rl, type, rid, start, start + count - 1, count); return 0; } static int isa_get_resource(device_t dev, device_t child, int type, int rid, u_long *startp, u_long *countp) { struct isa_device* idev = DEVTOISA(child); struct resource_list *rl = &idev->id_resources; struct resource_list_entry *rle; rle = resource_list_find(rl, type, rid); if (!rle) return ENOENT; if (startp) *startp = rle->start; if (countp) *countp = rle->count; return 0; } static void isa_delete_resource(device_t dev, device_t child, int type, int rid) { struct isa_device* idev = DEVTOISA(child); struct resource_list *rl = &idev->id_resources; resource_list_delete(rl, type, rid); } static int isa_add_config(device_t dev, device_t child, int priority, struct isa_config *config) { struct isa_device* idev = DEVTOISA(child); struct isa_config_entry *newice, *ice; newice = malloc(sizeof *ice, M_DEVBUF, M_NOWAIT); if (!newice) return ENOMEM; newice->ice_priority = priority; newice->ice_config = *config; TAILQ_FOREACH(ice, &idev->id_configs, ice_link) { if (ice->ice_priority > priority) break; } if (ice) TAILQ_INSERT_BEFORE(ice, newice, ice_link); else TAILQ_INSERT_TAIL(&idev->id_configs, newice, ice_link); return 0; } static void isa_set_config_callback(device_t dev, device_t child, isa_config_cb *fn, void *arg) { struct isa_device* idev = DEVTOISA(child); idev->id_config_cb = fn; idev->id_config_arg = arg; } static int isa_pnp_probe(device_t dev, device_t child, struct isa_pnp_id *ids) { struct isa_device* idev = DEVTOISA(child); if (!idev->id_vendorid) return ENOENT; while (ids->ip_id) { /* * Really ought to support >1 compat id per device. */ if (idev->id_logicalid == ids->ip_id || idev->id_compatid == ids->ip_id) { if (ids->ip_desc) device_set_desc(child, ids->ip_desc); return 0; } ids++; } return ENXIO; } static device_method_t isa_methods[] = { /* Device interface */ DEVMETHOD(device_probe, isa_probe), DEVMETHOD(device_attach, isa_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_add_child, isa_add_child), DEVMETHOD(bus_print_child, isa_print_child), DEVMETHOD(bus_read_ivar, isa_read_ivar), DEVMETHOD(bus_write_ivar, isa_write_ivar), DEVMETHOD(bus_child_detached, isa_child_detached), DEVMETHOD(bus_driver_added, isa_driver_added), DEVMETHOD(bus_alloc_resource, isa_alloc_resource), DEVMETHOD(bus_release_resource, isa_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, isa_setup_intr), DEVMETHOD(bus_teardown_intr, isa_teardown_intr), DEVMETHOD(bus_set_resource, isa_set_resource), DEVMETHOD(bus_get_resource, isa_get_resource), DEVMETHOD(bus_delete_resource, isa_delete_resource), /* ISA interface */ DEVMETHOD(isa_add_config, isa_add_config), DEVMETHOD(isa_set_config_callback, isa_set_config_callback), DEVMETHOD(isa_pnp_probe, isa_pnp_probe), { 0, 0 } }; static driver_t isa_driver = { "isa", isa_methods, 1, /* no softc */ }; /* * ISA can be attached to a PCI-ISA bridge or directly to the nexus. */ DRIVER_MODULE(isa, isab, isa_driver, isa_devclass, 0, 0); #ifdef __i386__ DRIVER_MODULE(isa, nexus, isa_driver, isa_devclass, 0, 0); #endif /* * A fallback driver for reporting un-matched pnp devices. */ static int unknown_probe(device_t dev) { /* * Only match pnp devices. */ if (isa_get_vendorid(dev) != 0) return -100; return ENXIO; } static int unknown_attach(device_t dev) { return 0; } static int unknown_detach(device_t dev) { return 0; } static device_method_t unknown_methods[] = { /* Device interface */ DEVMETHOD(device_probe, unknown_probe), DEVMETHOD(device_attach, unknown_attach), DEVMETHOD(device_detach, unknown_detach), { 0, 0 } }; static driver_t unknown_driver = { "unknown", unknown_methods, 1, /* no softc */ }; static devclass_t unknown_devclass; DRIVER_MODULE(unknown, isa, unknown_driver, unknown_devclass, 0, 0); Index: head/sys/isa/vga_isa.c =================================================================== --- head/sys/isa/vga_isa.c (revision 54072) +++ head/sys/isa/vga_isa.c (revision 54073) @@ -1,223 +1,223 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include "vga.h" #include "opt_vga.h" #include "opt_fb.h" #include "opt_syscons.h" /* should be removed in the future, XXX */ #if NVGA > 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define VGA_SOFTC(unit) \ ((vga_softc_t *)devclass_get_softc(isavga_devclass, unit)) static devclass_t isavga_devclass; static int isavga_probe(device_t dev); static int isavga_attach(device_t dev); static device_method_t isavga_methods[] = { DEVMETHOD(device_probe, isavga_probe), DEVMETHOD(device_attach, isavga_attach), DEVMETHOD(bus_print_child, bus_generic_print_child), { 0, 0 } }; static driver_t isavga_driver = { VGA_DRIVER_NAME, isavga_methods, sizeof(vga_softc_t), }; DRIVER_MODULE(vga, isa, isavga_driver, isavga_devclass, 0, 0); #ifdef FB_INSTALL_CDEV static d_open_t isavga_open; static d_close_t isavga_close; static d_read_t isavga_read; static d_write_t isavga_write; static d_ioctl_t isavga_ioctl; static d_mmap_t isavga_mmap; static struct cdevsw isavga_cdevsw = { /* open */ isavga_open, /* close */ isavga_close, /* read */ isavga_read, /* write */ isavga_write, /* ioctl */ isavga_ioctl, /* poll */ nopoll, /* mmap */ isavga_mmap, /* strategy */ nostrategy, /* name */ VGA_DRIVER_NAME, /* maj */ -1, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; #endif /* FB_INSTALL_CDEV */ static int isavga_probe(device_t dev) { video_adapter_t adp; device_t bus; int error; /* No pnp support */ if (isa_get_vendorid(dev)) return (ENXIO); device_set_desc(dev, "Generic ISA VGA"); error = vga_probe_unit(device_get_unit(dev), &adp, device_get_flags(dev)); if (error == 0) { bus = device_get_parent(dev); bus_set_resource(dev, SYS_RES_IOPORT, 0, adp.va_io_base, adp.va_io_size); bus_set_resource(dev, SYS_RES_MEMORY, 0, adp.va_mem_base, adp.va_mem_size); #if 0 isa_set_port(dev, adp.va_io_base); isa_set_portsize(dev, adp.va_io_size); isa_set_maddr(dev, adp.va_mem_base); isa_set_msize(dev, adp.va_mem_size); #endif } return error; } static int isavga_attach(device_t dev) { vga_softc_t *sc; struct resource *port; struct resource *mem; int unit; int rid; int error; unit = device_get_unit(dev); sc = device_get_softc(dev); rid = 0; port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 0, RF_ACTIVE | RF_SHAREABLE); rid = 0; mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, ~0, 0, RF_ACTIVE | RF_SHAREABLE); error = vga_attach_unit(unit, sc, device_get_flags(dev)); if (error) return error; #ifdef FB_INSTALL_CDEV /* attach a virtual frame buffer device */ error = fb_attach(makedev(0, VGA_MKMINOR(unit)), sc->adp, &isavga_cdevsw); if (error) return error; #endif /* FB_INSTALL_CDEV */ if (bootverbose) (*vidsw[sc->adp->va_index]->diag)(sc->adp, bootverbose); #if experimental - device_add_child(dev, "fb", -1, NULL); + device_add_child(dev, "fb", -1); bus_generic_attach(dev); #endif return 0; } #ifdef FB_INSTALL_CDEV static int isavga_open(dev_t dev, int flag, int mode, struct proc *p) { return vga_open(dev, VGA_SOFTC(VGA_UNIT(dev)), flag, mode, p); } static int isavga_close(dev_t dev, int flag, int mode, struct proc *p) { return vga_close(dev, VGA_SOFTC(VGA_UNIT(dev)), flag, mode, p); } static int isavga_read(dev_t dev, struct uio *uio, int flag) { return vga_read(dev, VGA_SOFTC(VGA_UNIT(dev)), uio, flag); } static int isavga_write(dev_t dev, struct uio *uio, int flag) { return vga_write(dev, VGA_SOFTC(VGA_UNIT(dev)), uio, flag); } static int isavga_ioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *p) { return vga_ioctl(dev, VGA_SOFTC(VGA_UNIT(dev)), cmd, arg, flag, p); } static int isavga_mmap(dev_t dev, vm_offset_t offset, int prot) { return vga_mmap(dev, VGA_SOFTC(VGA_UNIT(dev)), offset, prot); } #endif /* FB_INSTALL_CDEV */ #endif /* NVGA > 0 */ Index: head/sys/kern/subr_bus.c =================================================================== --- head/sys/kern/subr_bus.c (revision 54072) +++ head/sys/kern/subr_bus.c (revision 54073) @@ -1,2451 +1,2459 @@ /*- * Copyright (c) 1997,1998 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "opt_bus.h" #include #include #include #include #include #ifdef DEVICE_SYSCTLS #include #endif #include #include #include #include #include /* for device_printf() */ MALLOC_DEFINE(M_BUS, "bus", "Bus data structures"); #ifdef BUS_DEBUG #define PDEBUG(a) (printf(__FUNCTION__ ":%d: ", __LINE__), printf a, printf("\n")) #define DEVICENAME(d) ((d)? device_get_name(d): "no device") #define DRIVERNAME(d) ((d)? d->name : "no driver") #define DEVCLANAME(d) ((d)? d->name : "no devclass") /* Produce the indenting, indent*2 spaces plus a '.' ahead of that to * prevent syslog from deleting initial spaces */ #define indentprintf(p) do { int iJ; printf("."); for (iJ=0; iJmethod) { desc->method->refs++; return; } /* * Make sure that desc->deflt is always valid to simplify dispatch. */ if (!desc->deflt) desc->deflt = error_method; for (m = LIST_FIRST(&methods); m; m = LIST_NEXT(m, link)) { if (!strcmp(m->name, desc->name)) { desc->offset = m->offset; desc->method = m; m->refs++; PDEBUG(("method %p has the same name, %s, with offset %d", (void *)m, desc->name, desc->offset)); return; } } m = (struct method *) malloc(sizeof(struct method) + strlen(desc->name) + 1, M_BUS, M_NOWAIT); if (!m) panic("register_method: out of memory"); bzero(m, sizeof(struct method) + strlen(desc->name) + 1); m->offset = next_method_offset++; m->refs = 1; m->deflt = desc->deflt; m->name = (char*) (m + 1); strcpy(m->name, desc->name); LIST_INSERT_HEAD(&methods, m, link); desc->offset = m->offset; desc->method = m; } static void unregister_method(struct device_op_desc *desc) { struct method *m = desc->method; m->refs--; if (m->refs == 0) { PDEBUG(("method %s, reached refcount 0", desc->name)); LIST_REMOVE(m, link); free(m, M_BUS); desc->method = 0; } } static int error_method(void) { return ENXIO; } static struct device_ops null_ops = { 1, { error_method } }; static void compile_methods(driver_t *driver) { device_ops_t ops; struct device_method *m; struct method *cm; int i; /* * First register any methods which need it. */ for (i = 0, m = driver->methods; m->desc; i++, m++) register_method(m->desc); /* * Then allocate the compiled op table. */ ops = malloc(sizeof(struct device_ops) + (next_method_offset-1) * sizeof(devop_t), M_BUS, M_NOWAIT); if (!ops) panic("compile_methods: out of memory"); bzero(ops, sizeof(struct device_ops) + (next_method_offset-1) * sizeof(devop_t)); ops->maxoffset = next_method_offset; /* Fill in default methods and then overwrite with driver methods */ for (i = 0; i < next_method_offset; i++) ops->methods[i] = error_method; for (cm = LIST_FIRST(&methods); cm; cm = LIST_NEXT(cm, link)) { if (cm->deflt) ops->methods[cm->offset] = cm->deflt; } for (i = 0, m = driver->methods; m->desc; i++, m++) ops->methods[m->desc->offset] = m->func; PDEBUG(("%s has %d method%s, wasting %d bytes", DRIVERNAME(driver), i, (i==1?"":"s"), (next_method_offset-i)*sizeof(devop_t))); driver->ops = ops; } static void free_methods(driver_t *driver) { int i; struct device_method *m; /* * Unregister any methods which are no longer used. */ for (i = 0, m = driver->methods; m->desc; i++, m++) unregister_method(m->desc); /* * Free memory and clean up. */ free(driver->ops, M_BUS); driver->ops = 0; } /* * Devclass implementation */ static devclass_list_t devclasses = TAILQ_HEAD_INITIALIZER(devclasses); static devclass_t devclass_find_internal(const char *classname, int create) { devclass_t dc; PDEBUG(("looking for %s", classname)); if (!classname) return NULL; for (dc = TAILQ_FIRST(&devclasses); dc; dc = TAILQ_NEXT(dc, link)) if (!strcmp(dc->name, classname)) return dc; PDEBUG(("%s not found%s", classname, (create? ", creating": ""))); if (create) { dc = malloc(sizeof(struct devclass) + strlen(classname) + 1, M_BUS, M_NOWAIT); if (!dc) return NULL; bzero(dc, sizeof(struct devclass) + strlen(classname) + 1); dc->name = (char*) (dc + 1); strcpy(dc->name, classname); dc->devices = NULL; dc->maxunit = 0; dc->nextunit = 0; TAILQ_INIT(&dc->drivers); TAILQ_INSERT_TAIL(&devclasses, dc, link); } return dc; } devclass_t devclass_create(const char *classname) { return devclass_find_internal(classname, TRUE); } devclass_t devclass_find(const char *classname) { return devclass_find_internal(classname, FALSE); } int devclass_add_driver(devclass_t dc, driver_t *driver) { driverlink_t dl; int i; PDEBUG(("%s", DRIVERNAME(driver))); dl = malloc(sizeof *dl, M_BUS, M_NOWAIT); if (!dl) return ENOMEM; bzero(dl, sizeof *dl); /* * Compile the driver's methods. */ if (!driver->ops) compile_methods(driver); /* * Make sure the devclass which the driver is implementing exists. */ devclass_find_internal(driver->name, TRUE); dl->driver = driver; TAILQ_INSERT_TAIL(&dc->drivers, dl, link); driver->refs++; /* * Call BUS_DRIVER_ADDED for any existing busses in this class. */ for (i = 0; i < dc->maxunit; i++) if (dc->devices[i]) BUS_DRIVER_ADDED(dc->devices[i], driver); return 0; } int devclass_delete_driver(devclass_t busclass, driver_t *driver) { devclass_t dc = devclass_find(driver->name); driverlink_t dl; device_t dev; int i; int error; PDEBUG(("%s from devclass %s", driver->name, DEVCLANAME(busclass))); if (!dc) return 0; /* * Find the link structure in the bus' list of drivers. */ for (dl = TAILQ_FIRST(&busclass->drivers); dl; dl = TAILQ_NEXT(dl, link)) { if (dl->driver == driver) break; } if (!dl) { PDEBUG(("%s not found in %s list", driver->name, busclass->name)); return ENOENT; } /* * Disassociate from any devices. We iterate through all the * devices in the devclass of the driver and detach any which are * using the driver and which have a parent in the devclass which * we are deleting from. * * Note that since a driver can be in multiple devclasses, we * should not detach devices which are not children of devices in * the affected devclass. */ for (i = 0; i < dc->maxunit; i++) { if (dc->devices[i]) { dev = dc->devices[i]; if (dev->driver == driver && dev->parent && dev->parent->devclass == busclass) { if ((error = device_detach(dev)) != 0) return error; device_set_driver(dev, NULL); } } } TAILQ_REMOVE(&busclass->drivers, dl, link); free(dl, M_BUS); driver->refs--; if (driver->refs == 0) free_methods(driver); return 0; } static driverlink_t devclass_find_driver_internal(devclass_t dc, const char *classname) { driverlink_t dl; PDEBUG(("%s in devclass %s", classname, DEVCLANAME(dc))); for (dl = TAILQ_FIRST(&dc->drivers); dl; dl = TAILQ_NEXT(dl, link)) { if (!strcmp(dl->driver->name, classname)) return dl; } PDEBUG(("not found")); return NULL; } driver_t * devclass_find_driver(devclass_t dc, const char *classname) { driverlink_t dl; dl = devclass_find_driver_internal(dc, classname); if (dl) return dl->driver; else return NULL; } const char * devclass_get_name(devclass_t dc) { return dc->name; } device_t devclass_get_device(devclass_t dc, int unit) { if (dc == NULL || unit < 0 || unit >= dc->maxunit) return NULL; return dc->devices[unit]; } void * devclass_get_softc(devclass_t dc, int unit) { device_t dev; dev = devclass_get_device(dc, unit); if (!dev) return (NULL); return (device_get_softc(dev)); } int devclass_get_devices(devclass_t dc, device_t **devlistp, int *devcountp) { int i; int count; device_t *list; count = 0; for (i = 0; i < dc->maxunit; i++) if (dc->devices[i]) count++; list = malloc(count * sizeof(device_t), M_TEMP, M_NOWAIT); if (!list) return ENOMEM; bzero(list, count * sizeof(device_t)); count = 0; for (i = 0; i < dc->maxunit; i++) if (dc->devices[i]) { list[count] = dc->devices[i]; count++; } *devlistp = list; *devcountp = count; return 0; } int devclass_get_maxunit(devclass_t dc) { return dc->maxunit; } static int devclass_alloc_unit(devclass_t dc, int *unitp) { int unit = *unitp; PDEBUG(("unit %d in devclass %s", unit, DEVCLANAME(dc))); /* * If we have been given a wired unit number, check for existing * device. */ if (unit != -1) { device_t dev; dev = devclass_get_device(dc, unit); if (dev) { printf("devclass_alloc_unit: %s%d already exists, using next available unit number\n", dc->name, unit); unit = -1; } } if (unit == -1) { unit = dc->nextunit; dc->nextunit++; } else if (dc->nextunit <= unit) dc->nextunit = unit + 1; if (unit >= dc->maxunit) { device_t *newlist; int newsize; newsize = (dc->maxunit ? 2 * dc->maxunit : MINALLOCSIZE / sizeof(device_t)); newlist = malloc(sizeof(device_t) * newsize, M_BUS, M_NOWAIT); if (!newlist) return ENOMEM; bcopy(dc->devices, newlist, sizeof(device_t) * dc->maxunit); bzero(newlist + dc->maxunit, sizeof(device_t) * (newsize - dc->maxunit)); if (dc->devices) free(dc->devices, M_BUS); dc->devices = newlist; dc->maxunit = newsize; } PDEBUG(("now: unit %d in devclass %s", unit, DEVCLANAME(dc))); *unitp = unit; return 0; } static int devclass_add_device(devclass_t dc, device_t dev) { int buflen, error; PDEBUG(("%s in devclass %s", DEVICENAME(dev), DEVCLANAME(dc))); buflen = strlen(dc->name) + 5; dev->nameunit = malloc(buflen, M_BUS, M_NOWAIT); if (!dev->nameunit) return ENOMEM; bzero(dev->nameunit, buflen); if ((error = devclass_alloc_unit(dc, &dev->unit)) != 0) { free(dev->nameunit, M_BUS); dev->nameunit = NULL; return error; } dc->devices[dev->unit] = dev; dev->devclass = dc; snprintf(dev->nameunit, buflen, "%s%d", dc->name, dev->unit); #ifdef DEVICE_SYSCTLS device_register_oids(dev); #endif return 0; } static int devclass_delete_device(devclass_t dc, device_t dev) { if (!dc || !dev) return 0; PDEBUG(("%s in devclass %s", DEVICENAME(dev), DEVCLANAME(dc))); if (dev->devclass != dc || dc->devices[dev->unit] != dev) panic("devclass_delete_device: inconsistent device class"); dc->devices[dev->unit] = NULL; if (dev->flags & DF_WILDCARD) dev->unit = -1; dev->devclass = NULL; free(dev->nameunit, M_BUS); dev->nameunit = NULL; while (dc->nextunit > 0 && dc->devices[dc->nextunit - 1] == NULL) dc->nextunit--; #ifdef DEVICE_SYSCTLS device_unregister_oids(dev); #endif return 0; } static device_t -make_device(device_t parent, const char *name, - int unit, void *ivars) +make_device(device_t parent, const char *name, int unit) { device_t dev; devclass_t dc; - PDEBUG(("%s at %s as unit %d with%s ivars", - name, DEVICENAME(parent), unit, (ivars? "":"out"))); + PDEBUG(("%s at %s as unit %d", name, DEVICENAME(parent), unit)); if (name) { dc = devclass_find_internal(name, TRUE); if (!dc) { printf("make_device: can't find device class %s\n", name); return NULL; } } else dc = NULL; dev = malloc(sizeof(struct device), M_BUS, M_NOWAIT); if (!dev) return 0; bzero(dev, sizeof(struct device)); dev->parent = parent; TAILQ_INIT(&dev->children); dev->ops = &null_ops; dev->driver = NULL; dev->devclass = NULL; dev->unit = unit; dev->nameunit = NULL; dev->desc = NULL; dev->busy = 0; dev->devflags = 0; dev->flags = DF_ENABLED; dev->order = 0; if (unit == -1) dev->flags |= DF_WILDCARD; if (name) { dev->flags |= DF_FIXEDCLASS; devclass_add_device(dc, dev); } - dev->ivars = ivars; + dev->ivars = NULL; dev->softc = NULL; dev->state = DS_NOTPRESENT; return dev; } static int device_print_child(device_t dev, device_t child) { int retval = 0; if (device_is_alive(child)) { retval += BUS_PRINT_CHILD(dev, child); } else retval += device_printf(child, " not found\n"); return (retval); } device_t -device_add_child(device_t dev, const char *name, int unit, void *ivars) +device_add_child(device_t dev, const char *name, int unit) { - return device_add_child_ordered(dev, 0, name, unit, ivars); + return device_add_child_ordered(dev, 0, name, unit); } device_t -device_add_child_ordered(device_t dev, int order, - const char *name, int unit, void *ivars) +device_add_child_ordered(device_t dev, int order, const char *name, int unit) { device_t child; device_t place; - PDEBUG(("%s at %s with order %d as unit %d with%s ivars", - name, DEVICENAME(dev), order, unit, (ivars? "":"out"))); + PDEBUG(("%s at %s with order %d as unit %d", + name, DEVICENAME(dev), order, unit)); - child = make_device(dev, name, unit, ivars); + child = make_device(dev, name, unit); if (child == NULL) return child; child->order = order; TAILQ_FOREACH(place, &dev->children, link) if (place->order > order) break; if (place) { /* * The device 'place' is the first device whose order is * greater than the new child. */ TAILQ_INSERT_BEFORE(place, child, link); } else { /* * The new child's order is greater or equal to the order of * any existing device. Add the child to the tail of the list. */ TAILQ_INSERT_TAIL(&dev->children, child, link); } return child; } int device_delete_child(device_t dev, device_t child) { int error; device_t grandchild; PDEBUG(("%s from %s", DEVICENAME(child), DEVICENAME(dev))); /* remove children first */ while ( (grandchild = TAILQ_FIRST(&child->children)) ) { error = device_delete_child(child, grandchild); if (error) return error; } if ((error = device_detach(child)) != 0) return error; if (child->devclass) devclass_delete_device(child->devclass, child); TAILQ_REMOVE(&dev->children, child, link); device_set_desc(child, NULL); free(child, M_BUS); return 0; } /* * Find only devices attached to this bus. */ device_t device_find_child(device_t dev, const char *classname, int unit) { devclass_t dc; device_t child; dc = devclass_find(classname); if (!dc) return NULL; child = devclass_get_device(dc, unit); if (child && child->parent == dev) return child; return NULL; } static driverlink_t first_matching_driver(devclass_t dc, device_t dev) { if (dev->devclass) return devclass_find_driver_internal(dc, dev->devclass->name); else return TAILQ_FIRST(&dc->drivers); } static driverlink_t next_matching_driver(devclass_t dc, device_t dev, driverlink_t last) { if (dev->devclass) { driverlink_t dl; for (dl = TAILQ_NEXT(last, link); dl; dl = TAILQ_NEXT(dl, link)) if (!strcmp(dev->devclass->name, dl->driver->name)) return dl; return NULL; } else return TAILQ_NEXT(last, link); } static int device_probe_child(device_t dev, device_t child) { devclass_t dc; driverlink_t best = 0; driverlink_t dl; int result, pri = 0; dc = dev->devclass; if (dc == NULL) panic("device_probe_child: parent device has no devclass"); if (child->state == DS_ALIVE) return 0; for (dl = first_matching_driver(dc, child); dl; dl = next_matching_driver(dc, child, dl)) { PDEBUG(("Trying %s", DRIVERNAME(dl->driver))); device_set_driver(child, dl->driver); result = DEVICE_PROBE(child); /* * If the driver returns SUCCESS, there can be no higher match * for this device. */ if (result == 0) { best = dl; pri = 0; break; } /* * The driver returned an error so it certainly doesn't match. */ if (result > 0) continue; /* * A priority lower than SUCCESS, remember the best matching * driver. Initialise the value of pri for the first match. */ if (best == 0 || result > pri) { best = dl; pri = result; continue; } } /* * If we found a driver, change state and initialise the devclass. */ if (best) { if (!child->devclass) device_set_devclass(child, best->driver->name); device_set_driver(child, best->driver); if (pri < 0) { /* * A bit bogus. Call the probe method again to make sure * that we have the right description. */ DEVICE_PROBE(child); } child->state = DS_ALIVE; return 0; } return ENXIO; } device_t device_get_parent(device_t dev) { return dev->parent; } int device_get_children(device_t dev, device_t **devlistp, int *devcountp) { int count; device_t child; device_t *list; count = 0; for (child = TAILQ_FIRST(&dev->children); child; child = TAILQ_NEXT(child, link)) count++; list = malloc(count * sizeof(device_t), M_TEMP, M_NOWAIT); if (!list) return ENOMEM; bzero(list, count * sizeof(device_t)); count = 0; for (child = TAILQ_FIRST(&dev->children); child; child = TAILQ_NEXT(child, link)) { list[count] = child; count++; } *devlistp = list; *devcountp = count; return 0; } driver_t * device_get_driver(device_t dev) { return dev->driver; } devclass_t device_get_devclass(device_t dev) { return dev->devclass; } const char * device_get_name(device_t dev) { if (dev->devclass) return devclass_get_name(dev->devclass); return NULL; } const char * device_get_nameunit(device_t dev) { return dev->nameunit; } int device_get_unit(device_t dev) { return dev->unit; } const char * device_get_desc(device_t dev) { return dev->desc; } u_int32_t device_get_flags(device_t dev) { return dev->devflags; } int device_print_prettyname(device_t dev) { const char *name = device_get_name(dev); if (name == 0) return printf("unknown: "); else return printf("%s%d: ", name, device_get_unit(dev)); } int device_printf(device_t dev, const char * fmt, ...) { va_list ap; int retval; retval = device_print_prettyname(dev); va_start(ap, fmt); retval += vprintf(fmt, ap); va_end(ap); return retval; } static void device_set_desc_internal(device_t dev, const char* desc, int copy) { if (dev->desc && (dev->flags & DF_DESCMALLOCED)) { free(dev->desc, M_BUS); dev->flags &= ~DF_DESCMALLOCED; dev->desc = NULL; } if (copy && desc) { dev->desc = malloc(strlen(desc) + 1, M_BUS, M_NOWAIT); if (dev->desc) { strcpy(dev->desc, desc); dev->flags |= DF_DESCMALLOCED; } } else /* Avoid a -Wcast-qual warning */ dev->desc = (char *)(uintptr_t) desc; #ifdef DEVICE_SYSCTLS { struct sysctl_oid *oid = &dev->oid[1]; oid->oid_arg1 = dev->desc ? dev->desc : ""; oid->oid_arg2 = dev->desc ? strlen(dev->desc) : 0; } #endif } void device_set_desc(device_t dev, const char* desc) { device_set_desc_internal(dev, desc, FALSE); } void device_set_desc_copy(device_t dev, const char* desc) { device_set_desc_internal(dev, desc, TRUE); } void device_set_flags(device_t dev, u_int32_t flags) { dev->devflags = flags; } void * device_get_softc(device_t dev) { return dev->softc; } void * device_get_ivars(device_t dev) { return dev->ivars; +} + +void +device_set_ivars(device_t dev, void * ivars) +{ + if (!dev) + return; + + dev->ivars = ivars; + + return; } device_state_t device_get_state(device_t dev) { return dev->state; } void device_enable(device_t dev) { dev->flags |= DF_ENABLED; } void device_disable(device_t dev) { dev->flags &= ~DF_ENABLED; } void device_busy(device_t dev) { if (dev->state < DS_ATTACHED) panic("device_busy: called for unattached device"); if (dev->busy == 0 && dev->parent) device_busy(dev->parent); dev->busy++; dev->state = DS_BUSY; } void device_unbusy(device_t dev) { if (dev->state != DS_BUSY) panic("device_unbusy: called for non-busy device"); dev->busy--; if (dev->busy == 0) { if (dev->parent) device_unbusy(dev->parent); dev->state = DS_ATTACHED; } } void device_quiet(device_t dev) { dev->flags |= DF_QUIET; } void device_verbose(device_t dev) { dev->flags &= ~DF_QUIET; } int device_is_quiet(device_t dev) { return (dev->flags & DF_QUIET) != 0; } int device_is_enabled(device_t dev) { return (dev->flags & DF_ENABLED) != 0; } int device_is_alive(device_t dev) { return dev->state >= DS_ALIVE; } int device_set_devclass(device_t dev, const char *classname) { devclass_t dc; if (dev->devclass) { printf("device_set_devclass: device class already set\n"); return EINVAL; } dc = devclass_find_internal(classname, TRUE); if (!dc) return ENOMEM; return devclass_add_device(dc, dev); } int device_set_driver(device_t dev, driver_t *driver) { if (dev->state >= DS_ATTACHED) return EBUSY; if (dev->driver == driver) return 0; if (dev->softc) { free(dev->softc, M_BUS); dev->softc = NULL; } dev->ops = &null_ops; dev->driver = driver; if (driver) { dev->ops = driver->ops; dev->softc = malloc(driver->softc, M_BUS, M_NOWAIT); if (!dev->softc) { dev->ops = &null_ops; dev->driver = NULL; return ENOMEM; } bzero(dev->softc, driver->softc); } return 0; } int device_probe_and_attach(device_t dev) { device_t bus = dev->parent; int error = 0; if (dev->state >= DS_ALIVE) return 0; if (dev->flags & DF_ENABLED) { error = device_probe_child(bus, dev); if (!error) { if (!device_is_quiet(dev)) device_print_child(bus, dev); error = DEVICE_ATTACH(dev); if (!error) dev->state = DS_ATTACHED; else { printf("device_probe_and_attach: %s%d attach returned %d\n", dev->driver->name, dev->unit, error); device_set_driver(dev, NULL); dev->state = DS_NOTPRESENT; } } else { BUS_PROBE_NOMATCH(bus, dev); } } else { device_print_prettyname(dev); printf("not probed (disabled)\n"); } return error; } int device_detach(device_t dev) { int error; PDEBUG(("%s", DEVICENAME(dev))); if (dev->state == DS_BUSY) return EBUSY; if (dev->state != DS_ATTACHED) return 0; if ((error = DEVICE_DETACH(dev)) != 0) return error; if (dev->parent) BUS_CHILD_DETACHED(dev->parent, dev); if (!(dev->flags & DF_FIXEDCLASS)) devclass_delete_device(dev->devclass, dev); dev->state = DS_NOTPRESENT; device_set_driver(dev, NULL); return 0; } int device_shutdown(device_t dev) { if (dev->state < DS_ATTACHED) return 0; return DEVICE_SHUTDOWN(dev); } #ifdef DEVICE_SYSCTLS /* * Sysctl nodes for devices. */ SYSCTL_NODE(_hw, OID_AUTO, devices, CTLFLAG_RW, 0, "A list of all devices"); static int sysctl_handle_children SYSCTL_HANDLER_ARGS { device_t dev = arg1; device_t child; int first = 1, error = 0; for (child = TAILQ_FIRST(&dev->children); child; child = TAILQ_NEXT(child, link)) { if (child->nameunit) { if (!first) { error = SYSCTL_OUT(req, ",", 1); if (error) return error; } else { first = 0; } error = SYSCTL_OUT(req, child->nameunit, strlen(child->nameunit)); if (error) return error; } } error = SYSCTL_OUT(req, "", 1); return error; } static int sysctl_handle_state SYSCTL_HANDLER_ARGS { device_t dev = arg1; switch (dev->state) { case DS_NOTPRESENT: return SYSCTL_OUT(req, "notpresent", sizeof("notpresent")); case DS_ALIVE: return SYSCTL_OUT(req, "alive", sizeof("alive")); case DS_ATTACHED: return SYSCTL_OUT(req, "attached", sizeof("attached")); case DS_BUSY: return SYSCTL_OUT(req, "busy", sizeof("busy")); } return 0; } static void device_register_oids(device_t dev) { struct sysctl_oid* oid; oid = &dev->oid[0]; bzero(oid, sizeof(*oid)); oid->oid_parent = &sysctl__hw_devices_children; oid->oid_number = OID_AUTO; oid->oid_kind = CTLTYPE_NODE | CTLFLAG_RW; oid->oid_arg1 = &dev->oidlist[0]; oid->oid_arg2 = 0; oid->oid_name = dev->nameunit; oid->oid_handler = 0; oid->oid_fmt = "N"; SLIST_INIT(&dev->oidlist[0]); sysctl_register_oid(oid); oid = &dev->oid[1]; bzero(oid, sizeof(*oid)); oid->oid_parent = &dev->oidlist[0]; oid->oid_number = OID_AUTO; oid->oid_kind = CTLTYPE_STRING | CTLFLAG_RD; oid->oid_arg1 = dev->desc ? dev->desc : ""; oid->oid_arg2 = dev->desc ? strlen(dev->desc) : 0; oid->oid_name = "desc"; oid->oid_handler = sysctl_handle_string; oid->oid_fmt = "A"; sysctl_register_oid(oid); oid = &dev->oid[2]; bzero(oid, sizeof(*oid)); oid->oid_parent = &dev->oidlist[0]; oid->oid_number = OID_AUTO; oid->oid_kind = CTLTYPE_INT | CTLFLAG_RD; oid->oid_arg1 = dev; oid->oid_arg2 = 0; oid->oid_name = "children"; oid->oid_handler = sysctl_handle_children; oid->oid_fmt = "A"; sysctl_register_oid(oid); oid = &dev->oid[3]; bzero(oid, sizeof(*oid)); oid->oid_parent = &dev->oidlist[0]; oid->oid_number = OID_AUTO; oid->oid_kind = CTLTYPE_INT | CTLFLAG_RD; oid->oid_arg1 = dev; oid->oid_arg2 = 0; oid->oid_name = "state"; oid->oid_handler = sysctl_handle_state; oid->oid_fmt = "A"; sysctl_register_oid(oid); } static void device_unregister_oids(device_t dev) { sysctl_unregister_oid(&dev->oid[0]); sysctl_unregister_oid(&dev->oid[1]); sysctl_unregister_oid(&dev->oid[2]); } #endif /*======================================*/ /* * Access functions for device resources. */ /* Supplied by config(8) in ioconf.c */ extern struct config_device config_devtab[]; extern int devtab_count; /* Runtime version */ struct config_device *devtab = config_devtab; static int resource_new_name(const char *name, int unit) { struct config_device *new; new = malloc((devtab_count + 1) * sizeof(*new), M_TEMP, M_NOWAIT); if (new == NULL) return -1; if (devtab && devtab_count > 0) bcopy(devtab, new, devtab_count * sizeof(*new)); bzero(&new[devtab_count], sizeof(*new)); new[devtab_count].name = malloc(strlen(name) + 1, M_TEMP, M_NOWAIT); if (new[devtab_count].name == NULL) { free(new, M_TEMP); return -1; } strcpy(new[devtab_count].name, name); new[devtab_count].unit = unit; new[devtab_count].resource_count = 0; new[devtab_count].resources = NULL; devtab = new; return devtab_count++; } static int resource_new_resname(int j, const char *resname, resource_type type) { struct config_resource *new; int i; i = devtab[j].resource_count; new = malloc((i + 1) * sizeof(*new), M_TEMP, M_NOWAIT); if (new == NULL) return -1; if (devtab[j].resources && i > 0) bcopy(devtab[j].resources, new, i * sizeof(*new)); bzero(&new[i], sizeof(*new)); new[i].name = malloc(strlen(resname) + 1, M_TEMP, M_NOWAIT); if (new[i].name == NULL) { free(new, M_TEMP); return -1; } strcpy(new[i].name, resname); new[i].type = type; if (devtab[j].resources) free(devtab[j].resources, M_TEMP); devtab[j].resources = new; devtab[j].resource_count = i + 1; return i; } static int resource_match_string(int i, const char *resname, const char *value) { int j; struct config_resource *res; for (j = 0, res = devtab[i].resources; j < devtab[i].resource_count; j++, res++) if (!strcmp(res->name, resname) && res->type == RES_STRING && !strcmp(res->u.stringval, value)) return j; return -1; } static int resource_find(const char *name, int unit, const char *resname, struct config_resource **result) { int i, j; struct config_resource *res; /* * First check specific instances, then generic. */ for (i = 0; i < devtab_count; i++) { if (devtab[i].unit < 0) continue; if (!strcmp(devtab[i].name, name) && devtab[i].unit == unit) { res = devtab[i].resources; for (j = 0; j < devtab[i].resource_count; j++, res++) if (!strcmp(res->name, resname)) { *result = res; return 0; } } } for (i = 0; i < devtab_count; i++) { if (devtab[i].unit >= 0) continue; /* XXX should this `&& devtab[i].unit == unit' be here? */ /* XXX if so, then the generic match does nothing */ if (!strcmp(devtab[i].name, name) && devtab[i].unit == unit) { res = devtab[i].resources; for (j = 0; j < devtab[i].resource_count; j++, res++) if (!strcmp(res->name, resname)) { *result = res; return 0; } } } return ENOENT; } int resource_int_value(const char *name, int unit, const char *resname, int *result) { int error; struct config_resource *res; if ((error = resource_find(name, unit, resname, &res)) != 0) return error; if (res->type != RES_INT) return EFTYPE; *result = res->u.intval; return 0; } int resource_long_value(const char *name, int unit, const char *resname, long *result) { int error; struct config_resource *res; if ((error = resource_find(name, unit, resname, &res)) != 0) return error; if (res->type != RES_LONG) return EFTYPE; *result = res->u.longval; return 0; } int resource_string_value(const char *name, int unit, const char *resname, char **result) { int error; struct config_resource *res; if ((error = resource_find(name, unit, resname, &res)) != 0) return error; if (res->type != RES_STRING) return EFTYPE; *result = res->u.stringval; return 0; } int resource_query_string(int i, const char *resname, const char *value) { if (i < 0) i = 0; else i = i + 1; for (; i < devtab_count; i++) if (resource_match_string(i, resname, value) >= 0) return i; return -1; } int resource_locate(int i, const char *resname) { if (i < 0) i = 0; else i = i + 1; for (; i < devtab_count; i++) if (!strcmp(devtab[i].name, resname)) return i; return -1; } int resource_count(void) { return devtab_count; } char * resource_query_name(int i) { return devtab[i].name; } int resource_query_unit(int i) { return devtab[i].unit; } static int resource_create(const char *name, int unit, const char *resname, resource_type type, struct config_resource **result) { int i, j; struct config_resource *res = NULL; for (i = 0; i < devtab_count; i++) { if (!strcmp(devtab[i].name, name) && devtab[i].unit == unit) { res = devtab[i].resources; break; } } if (res == NULL) { i = resource_new_name(name, unit); if (i < 0) return ENOMEM; res = devtab[i].resources; } for (j = 0; j < devtab[i].resource_count; j++, res++) { if (!strcmp(res->name, resname)) { *result = res; return 0; } } j = resource_new_resname(i, resname, type); if (j < 0) return ENOMEM; res = &devtab[i].resources[j]; *result = res; return 0; } int resource_set_int(const char *name, int unit, const char *resname, int value) { int error; struct config_resource *res; error = resource_create(name, unit, resname, RES_INT, &res); if (error) return error; if (res->type != RES_INT) return EFTYPE; res->u.intval = value; return 0; } int resource_set_long(const char *name, int unit, const char *resname, long value) { int error; struct config_resource *res; error = resource_create(name, unit, resname, RES_LONG, &res); if (error) return error; if (res->type != RES_LONG) return EFTYPE; res->u.longval = value; return 0; } int resource_set_string(const char *name, int unit, const char *resname, const char *value) { int error; struct config_resource *res; error = resource_create(name, unit, resname, RES_STRING, &res); if (error) return error; if (res->type != RES_STRING) return EFTYPE; if (res->u.stringval) free(res->u.stringval, M_TEMP); res->u.stringval = malloc(strlen(value) + 1, M_TEMP, M_NOWAIT); if (res->u.stringval == NULL) return ENOMEM; strcpy(res->u.stringval, value); return 0; } static void resource_cfgload(void *dummy __unused) { struct config_resource *res, *cfgres; int i, j; int error; char *name, *resname; int unit; resource_type type; char *stringval; int config_devtab_count; config_devtab_count = devtab_count; devtab = NULL; devtab_count = 0; for (i = 0; i < config_devtab_count; i++) { name = config_devtab[i].name; unit = config_devtab[i].unit; for (j = 0; j < config_devtab[i].resource_count; j++) { cfgres = config_devtab[i].resources; resname = cfgres[j].name; type = cfgres[j].type; error = resource_create(name, unit, resname, type, &res); if (error) { printf("create resource %s%d: error %d\n", name, unit, error); continue; } if (res->type != type) { printf("type mismatch %s%d: %d != %d\n", name, unit, res->type, type); continue; } switch (type) { case RES_INT: res->u.intval = cfgres[j].u.intval; break; case RES_LONG: res->u.longval = cfgres[j].u.longval; break; case RES_STRING: if (res->u.stringval) free(res->u.stringval, M_TEMP); stringval = cfgres[j].u.stringval; res->u.stringval = malloc(strlen(stringval) + 1, M_TEMP, M_NOWAIT); if (res->u.stringval == NULL) break; strcpy(res->u.stringval, stringval); break; default: panic("unknown resource type %d\n", type); } } } } SYSINIT(cfgload, SI_SUB_KMEM, SI_ORDER_ANY + 50, resource_cfgload, 0) /*======================================*/ /* * Some useful method implementations to make life easier for bus drivers. */ void resource_list_init(struct resource_list *rl) { SLIST_INIT(rl); } void resource_list_free(struct resource_list *rl) { struct resource_list_entry *rle; while ((rle = SLIST_FIRST(rl)) != NULL) { if (rle->res) panic("resource_list_free: resource entry is busy"); SLIST_REMOVE_HEAD(rl, link); free(rle, M_BUS); } } void resource_list_add(struct resource_list *rl, int type, int rid, u_long start, u_long end, u_long count) { struct resource_list_entry *rle; rle = resource_list_find(rl, type, rid); if (!rle) { rle = malloc(sizeof(struct resource_list_entry), M_BUS, M_NOWAIT); if (!rle) panic("resource_list_add: can't record entry"); SLIST_INSERT_HEAD(rl, rle, link); rle->type = type; rle->rid = rid; rle->res = NULL; } if (rle->res) panic("resource_list_add: resource entry is busy"); rle->start = start; rle->end = end; rle->count = count; } struct resource_list_entry* resource_list_find(struct resource_list *rl, int type, int rid) { struct resource_list_entry *rle; SLIST_FOREACH(rle, rl, link) if (rle->type == type && rle->rid == rid) return rle; return NULL; } void resource_list_delete(struct resource_list *rl, int type, int rid) { struct resource_list_entry *rle = resource_list_find(rl, type, rid); if (rle) { SLIST_REMOVE(rl, rle, resource_list_entry, link); free(rle, M_BUS); } } struct resource * resource_list_alloc(struct resource_list *rl, device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct resource_list_entry *rle = 0; int passthrough = (device_get_parent(child) != bus); int isdefault = (start == 0UL && end == ~0UL); if (passthrough) { return BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, rid, start, end, count, flags); } rle = resource_list_find(rl, type, *rid); if (!rle) return 0; /* no resource of that type/rid */ if (rle->res) panic("resource_list_alloc: resource entry is busy"); if (isdefault) { start = rle->start; count = max(count, rle->count); end = max(rle->end, start + count - 1); } rle->res = BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, rid, start, end, count, flags); /* * Record the new range. */ if (rle->res) { rle->start = rman_get_start(rle->res); rle->end = rman_get_end(rle->res); rle->count = count; } return rle->res; } int resource_list_release(struct resource_list *rl, device_t bus, device_t child, int type, int rid, struct resource *res) { struct resource_list_entry *rle = 0; int passthrough = (device_get_parent(child) != bus); int error; if (passthrough) { return BUS_RELEASE_RESOURCE(device_get_parent(bus), child, type, rid, res); } rle = resource_list_find(rl, type, rid); if (!rle) panic("resource_list_release: can't find resource"); if (!rle->res) panic("resource_list_release: resource entry is not busy"); error = BUS_RELEASE_RESOURCE(device_get_parent(bus), child, type, rid, res); if (error) return error; rle->res = NULL; return 0; } /* * Call DEVICE_IDENTIFY for each driver. */ int bus_generic_probe(device_t dev) { devclass_t dc = dev->devclass; driverlink_t dl; for (dl = TAILQ_FIRST(&dc->drivers); dl; dl = TAILQ_NEXT(dl, link)) DEVICE_IDENTIFY(dl->driver, dev); return 0; } int bus_generic_attach(device_t dev) { device_t child; for (child = TAILQ_FIRST(&dev->children); child; child = TAILQ_NEXT(child, link)) device_probe_and_attach(child); return 0; } int bus_generic_detach(device_t dev) { device_t child; int error; if (dev->state != DS_ATTACHED) return EBUSY; for (child = TAILQ_FIRST(&dev->children); child; child = TAILQ_NEXT(child, link)) if ((error = device_detach(child)) != 0) return error; return 0; } int bus_generic_shutdown(device_t dev) { device_t child; for (child = TAILQ_FIRST(&dev->children); child; child = TAILQ_NEXT(child, link)) device_shutdown(child); return 0; } int bus_generic_suspend(device_t dev) { int error; device_t child, child2; for (child = TAILQ_FIRST(&dev->children); child; child = TAILQ_NEXT(child, link)) { error = DEVICE_SUSPEND(child); if (error) { for (child2 = TAILQ_FIRST(&dev->children); child2 && child2 != child; child2 = TAILQ_NEXT(child2, link)) DEVICE_RESUME(child2); return (error); } } return 0; } int bus_generic_resume(device_t dev) { device_t child; for (child = TAILQ_FIRST(&dev->children); child; child = TAILQ_NEXT(child, link)) { DEVICE_RESUME(child); /* if resume fails, there's nothing we can usefully do... */ } return 0; } int bus_print_child_header (device_t dev, device_t child) { int retval = 0; if (device_get_desc(child)) { retval += device_printf(child, "<%s>", device_get_desc(child)); } else { retval += printf("%s", device_get_nameunit(child)); } return (retval); } int bus_print_child_footer (device_t dev, device_t child) { return(printf(" on %s\n", device_get_nameunit(dev))); } int bus_generic_print_child(device_t dev, device_t child) { int retval = 0; retval += bus_print_child_header(dev, child); retval += bus_print_child_footer(dev, child); return (retval); } int bus_generic_read_ivar(device_t dev, device_t child, int index, uintptr_t * result) { return ENOENT; } int bus_generic_write_ivar(device_t dev, device_t child, int index, uintptr_t value) { return ENOENT; } void bus_generic_driver_added(device_t dev, driver_t *driver) { device_t child; DEVICE_IDENTIFY(driver, dev); for (child = TAILQ_FIRST(&dev->children); child; child = TAILQ_NEXT(child, link)) if (child->state == DS_NOTPRESENT) device_probe_and_attach(child); } int bus_generic_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep) { /* Propagate up the bus hierarchy until someone handles it. */ if (dev->parent) return (BUS_SETUP_INTR(dev->parent, child, irq, flags, intr, arg, cookiep)); else return (EINVAL); } int bus_generic_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie) { /* Propagate up the bus hierarchy until someone handles it. */ if (dev->parent) return (BUS_TEARDOWN_INTR(dev->parent, child, irq, cookie)); else return (EINVAL); } struct resource * bus_generic_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { /* Propagate up the bus hierarchy until someone handles it. */ if (dev->parent) return (BUS_ALLOC_RESOURCE(dev->parent, child, type, rid, start, end, count, flags)); else return (NULL); } int bus_generic_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { /* Propagate up the bus hierarchy until someone handles it. */ if (dev->parent) return (BUS_RELEASE_RESOURCE(dev->parent, child, type, rid, r)); else return (EINVAL); } int bus_generic_activate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { /* Propagate up the bus hierarchy until someone handles it. */ if (dev->parent) return (BUS_ACTIVATE_RESOURCE(dev->parent, child, type, rid, r)); else return (EINVAL); } int bus_generic_deactivate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { /* Propagate up the bus hierarchy until someone handles it. */ if (dev->parent) return (BUS_DEACTIVATE_RESOURCE(dev->parent, child, type, rid, r)); else return (EINVAL); } /* * Some convenience functions to make it easier for drivers to use the * resource-management functions. All these really do is hide the * indirection through the parent's method table, making for slightly * less-wordy code. In the future, it might make sense for this code * to maintain some sort of a list of resources allocated by each device. */ struct resource * bus_alloc_resource(device_t dev, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { if (dev->parent == 0) return (0); return (BUS_ALLOC_RESOURCE(dev->parent, dev, type, rid, start, end, count, flags)); } int bus_activate_resource(device_t dev, int type, int rid, struct resource *r) { if (dev->parent == 0) return (EINVAL); return (BUS_ACTIVATE_RESOURCE(dev->parent, dev, type, rid, r)); } int bus_deactivate_resource(device_t dev, int type, int rid, struct resource *r) { if (dev->parent == 0) return (EINVAL); return (BUS_DEACTIVATE_RESOURCE(dev->parent, dev, type, rid, r)); } int bus_release_resource(device_t dev, int type, int rid, struct resource *r) { if (dev->parent == 0) return (EINVAL); return (BUS_RELEASE_RESOURCE(dev->parent, dev, type, rid, r)); } int bus_setup_intr(device_t dev, struct resource *r, int flags, driver_intr_t handler, void *arg, void **cookiep) { if (dev->parent == 0) return (EINVAL); return (BUS_SETUP_INTR(dev->parent, dev, r, flags, handler, arg, cookiep)); } int bus_teardown_intr(device_t dev, struct resource *r, void *cookie) { if (dev->parent == 0) return (EINVAL); return (BUS_TEARDOWN_INTR(dev->parent, dev, r, cookie)); } int bus_set_resource(device_t dev, int type, int rid, u_long start, u_long count) { return BUS_SET_RESOURCE(device_get_parent(dev), dev, type, rid, start, count); } int bus_get_resource(device_t dev, int type, int rid, u_long *startp, u_long *countp) { return BUS_GET_RESOURCE(device_get_parent(dev), dev, type, rid, startp, countp); } u_long bus_get_resource_start(device_t dev, int type, int rid) { u_long start, count; int error; error = BUS_GET_RESOURCE(device_get_parent(dev), dev, type, rid, &start, &count); if (error) return 0; return start; } u_long bus_get_resource_count(device_t dev, int type, int rid) { u_long start, count; int error; error = BUS_GET_RESOURCE(device_get_parent(dev), dev, type, rid, &start, &count); if (error) return 0; return count; } void bus_delete_resource(device_t dev, int type, int rid) { BUS_DELETE_RESOURCE(device_get_parent(dev), dev, type, rid); } static int root_print_child(device_t dev, device_t child) { return (0); } static int root_setup_intr(device_t dev, device_t child, driver_intr_t *intr, void *arg, void **cookiep) { /* * If an interrupt mapping gets to here something bad has happened. */ panic("root_setup_intr"); } static device_method_t root_methods[] = { /* Device interface */ DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, root_print_child), DEVMETHOD(bus_read_ivar, bus_generic_read_ivar), DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), DEVMETHOD(bus_setup_intr, root_setup_intr), { 0, 0 } }; static driver_t root_driver = { "root", root_methods, 1, /* no softc */ }; device_t root_bus; devclass_t root_devclass; static int root_bus_module_handler(module_t mod, int what, void* arg) { switch (what) { case MOD_LOAD: compile_methods(&root_driver); root_bus = make_device(NULL, "root", 0, NULL); root_bus->desc = "System root bus"; root_bus->ops = root_driver.ops; root_bus->driver = &root_driver; root_bus->state = DS_ATTACHED; root_devclass = devclass_find_internal("root", FALSE); return 0; case MOD_SHUTDOWN: device_shutdown(root_bus); return 0; } return 0; } static moduledata_t root_bus_mod = { "rootbus", root_bus_module_handler, 0 }; DECLARE_MODULE(rootbus, root_bus_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); void root_bus_configure(void) { device_t dev; PDEBUG((".")); for (dev = TAILQ_FIRST(&root_bus->children); dev; dev = TAILQ_NEXT(dev, link)) { device_probe_and_attach(dev); } } int driver_module_handler(module_t mod, int what, void *arg) { int error, i; struct driver_module_data *dmd; devclass_t bus_devclass; dmd = (struct driver_module_data *)arg; bus_devclass = devclass_find_internal(dmd->dmd_busname, TRUE); error = 0; switch (what) { case MOD_LOAD: if (dmd->dmd_chainevh) error = dmd->dmd_chainevh(mod,what,dmd->dmd_chainarg); for (i = 0; !error && i < dmd->dmd_ndrivers; i++) { PDEBUG(("Loading module: driver %s on bus %s", DRIVERNAME(dmd->dmd_drivers[i]), dmd->dmd_busname)); error = devclass_add_driver(bus_devclass, dmd->dmd_drivers[i]); } if (error) break; /* * The drivers loaded in this way are assumed to all * implement the same devclass. */ *dmd->dmd_devclass = devclass_find_internal(dmd->dmd_drivers[0]->name, TRUE); break; case MOD_UNLOAD: for (i = 0; !error && i < dmd->dmd_ndrivers; i++) { PDEBUG(("Unloading module: driver %s from bus %s", DRIVERNAME(dmd->dmd_drivers[i]), dmd->dmd_busname)); error = devclass_delete_driver(bus_devclass, dmd->dmd_drivers[i]); } if (!error && dmd->dmd_chainevh) error = dmd->dmd_chainevh(mod,what,dmd->dmd_chainarg); break; } return (error); } #ifdef BUS_DEBUG /* the _short versions avoid iteration by not calling anything that prints * more than oneliners. I love oneliners. */ static void print_method_list(device_method_t *m, int indent) { int i; if (!m) return; for (i = 0; m->desc; i++, m++) indentprintf(("method %d: %s, offset=%d\n", i, m->desc->name, m->desc->offset)); } static void print_device_ops(device_ops_t ops, int indent) { int i; int count = 0; if (!ops) return; /* we present a list of the methods that are pointing to the * error_method, but ignore the 0'th elements; it is always * error_method. */ for (i = 1; i < ops->maxoffset; i++) { if (ops->methods[i] == error_method) { if (count == 0) indentprintf(("error_method:")); printf(" %d", i); count++; } } if (count) printf("\n"); indentprintf(("(%d method%s, %d valid, %d error_method%s)\n", ops->maxoffset-1, (ops->maxoffset-1 == 1? "":"s"), ops->maxoffset-1-count, count, (count == 1? "":"'s"))); } static void print_device_short(device_t dev, int indent) { if (!dev) return; indentprintf(("device %d: <%s> %sparent,%schildren,%s%s%s%s,%sivars,%ssoftc,busy=%d\n", dev->unit, dev->desc, (dev->parent? "":"no "), (TAILQ_EMPTY(&dev->children)? "no ":""), (dev->flags&DF_ENABLED? "enabled,":"disabled,"), (dev->flags&DF_FIXEDCLASS? "fixed,":""), (dev->flags&DF_WILDCARD? "wildcard,":""), (dev->flags&DF_DESCMALLOCED? "descmalloced,":""), (dev->ivars? "":"no "), (dev->softc? "":"no "), dev->busy)); } static void print_device(device_t dev, int indent) { if (!dev) return; print_device_short(dev, indent); indentprintf(("Parent:\n")); print_device_short(dev->parent, indent+1); indentprintf(("Methods:\n")); print_device_ops(dev->ops, indent+1); indentprintf(("Driver:\n")); print_driver_short(dev->driver, indent+1); indentprintf(("Devclass:\n")); print_devclass_short(dev->devclass, indent+1); } void print_device_tree_short(device_t dev, int indent) /* print the device and all its children (indented) */ { device_t child; if (!dev) return; print_device_short(dev, indent); for (child = TAILQ_FIRST(&dev->children); child; child = TAILQ_NEXT(child, link)) print_device_tree_short(child, indent+1); } void print_device_tree(device_t dev, int indent) /* print the device and all its children (indented) */ { device_t child; if (!dev) return; print_device(dev, indent); for (child = TAILQ_FIRST(&dev->children); child; child = TAILQ_NEXT(child, link)) print_device_tree(child, indent+1); } static void print_driver_short(driver_t *driver, int indent) { if (!driver) return; indentprintf(("driver %s: softc size = %d\n", driver->name, driver->softc)); } static void print_driver(driver_t *driver, int indent) { if (!driver) return; print_driver_short(driver, indent); indentprintf(("Methods:\n")); print_method_list(driver->methods, indent+1); indentprintf(("Operations:\n")); print_device_ops(driver->ops, indent+1); } static void print_driver_list(driver_list_t drivers, int indent) { driverlink_t driver; for (driver = TAILQ_FIRST(&drivers); driver; driver = TAILQ_NEXT(driver, link)) print_driver(driver->driver, indent); } static void print_devclass_short(devclass_t dc, int indent) { if ( !dc ) return; indentprintf(("devclass %s: max units = %d, next unit = %d\n", dc->name, dc->maxunit, dc->nextunit)); } static void print_devclass(devclass_t dc, int indent) { int i; if ( !dc ) return; print_devclass_short(dc, indent); indentprintf(("Drivers:\n")); print_driver_list(dc->drivers, indent+1); indentprintf(("Devices:\n")); for (i = 0; i < dc->maxunit; i++) if (dc->devices[i]) print_device(dc->devices[i], indent+1); } void print_devclass_list_short(void) { devclass_t dc; printf("Short listing of devclasses, drivers & devices:\n"); for (dc = TAILQ_FIRST(&devclasses); dc; dc = TAILQ_NEXT(dc, link)) print_devclass_short(dc, 0); } void print_devclass_list(void) { devclass_t dc; printf("Full listing of devclasses, drivers & devices:\n"); for (dc = TAILQ_FIRST(&devclasses); dc; dc = TAILQ_NEXT(dc, link)) print_devclass(dc, 0); } #endif Index: head/sys/pc98/cbus/fdc.c =================================================================== --- head/sys/pc98/cbus/fdc.c (revision 54072) +++ head/sys/pc98/cbus/fdc.c (revision 54073) @@ -1,2774 +1,2775 @@ /* * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Don Ahn. * * Libretto PCMCIA floppy support by David Horwitt (dhorwitt@ucsd.edu) * aided by the Linux floppy driver modifications from David Bateman * (dbateman@eng.uts.edu.au). * * Copyright (c) 1993, 1994 by * jc@irbs.UUCP (John Capo) * vak@zebub.msk.su (Serge Vakulenko) * ache@astral.msk.su (Andrew A. Chernov) * * Copyright (c) 1993, 1994, 1995 by * joerg_wunsch@uriah.sax.de (Joerg Wunsch) * dufault@hda.com (Peter Dufault) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 * $FreeBSD$ * */ #include "opt_fdc.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PC98 #include #include #include #include #include #else #include #include #include #include #endif #ifdef FDC_YE #undef FDC_YE #warning "fix FDC_YE! - newbus casualty" #endif /* misuse a flag to identify format operation */ #define B_FORMAT B_XXX /* configuration flags */ #define FDC_PRETEND_D0 (1 << 0) /* pretend drive 0 to be there */ #ifdef FDC_YE #define FDC_IS_PCMCIA (1 << 1) /* if successful probe, then it's a PCMCIA device */ #endif /* internally used only, not really from CMOS: */ #define RTCFDT_144M_PRETENDED 0x1000 /* error returns for fd_cmd() */ #define FD_FAILED -1 #define FD_NOT_VALID -2 #define FDC_ERRMAX 100 /* do not log more */ #ifdef PC98 #define NUMTYPES 12 #define NUMDENS NUMTYPES #else #define NUMTYPES 14 #define NUMDENS (NUMTYPES - 6) #endif /* These defines (-1) must match index for fd_types */ #define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ #define FD_1720 1 #define FD_1480 2 #define FD_1440 3 #define FD_1200 4 #define FD_820 5 #define FD_800 6 #define FD_720 7 #define FD_360 8 #ifdef PC98 #define FD_640 9 #define FD_1232 10 #define FD_1280 11 #define FD_1476 12 #define FDT_NONE 0 /* none present */ #define FDT_12M 1 /* 1M/640K FDD */ #define FDT_144M 2 /* 1.44M/1M/640K FDD */ #else #define FD_1480in5_25 9 #define FD_1440in5_25 10 #define FD_820in5_25 11 #define FD_800in5_25 12 #define FD_720in5_25 13 #define FD_360in5_25 14 #endif static struct fd_type fd_types[NUMTYPES] = { #ifdef PC98 { 21,2,0xFF,0x04,82,3444,1,2,2,0x0C,2 }, /* 1.72M in 3mode */ { 18,2,0xFF,0x1B,82,2952,1,2,2,0x54,1 }, /* 1.48M in 3mode */ { 18,2,0xFF,0x1B,80,2880,1,2,2,0x54,1 }, /* 1.44M in 3mode */ { 15,2,0xFF,0x1B,80,2400,1,0,2,0x54,1 }, /* 1.2M */ { 10,2,0xFF,0x10,82,1640,1,1,2,0x30,1 }, /* 820K */ { 10,2,0xFF,0x10,80,1600,1,1,2,0x30,1 }, /* 800K */ { 9,2,0xFF,0x20,80,1440,1,1,2,0x50,1 }, /* 720K */ { 9,2,0xFF,0x20,40, 720,2,1,2,0x50,1 }, /* 360K */ { 8,2,0xFF,0x2A,80,1280,1,1,2,0x50,1 }, /* 640K */ { 8,3,0xFF,0x35,77,1232,1,0,2,0x74,1 }, /* 1.23M 1024/sec */ { 8,3,0xFF,0x35,80,1280,1,0,2,0x74,1 }, /* 1.28M 1024/sec */ { 9,3,0xFF,0x35,82,1476,1,0,2,0x47,1 }, /* 1.48M 1024/sec 9sec */ #if 0 { 10,3,0xFF,0x1B,82,1640,1,2,2,0x54,1 }, /* 1.64M in 3mode - Reserve */ #endif #else { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */ { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */ { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */ { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */ { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */ { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */ { 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */ { 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */ { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */ { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */ { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */ { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */ { 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */ { 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */ #endif }; #ifdef PC98 #define DRVS_PER_CTLR 4 /* 4 floppies */ #else #define DRVS_PER_CTLR 2 /* 2 floppies */ #endif /***********************************************************************\ * Per controller structure. * \***********************************************************************/ static devclass_t fdc_devclass; /***********************************************************************\ * Per drive structure. * * N per controller (DRVS_PER_CTLR) * \***********************************************************************/ struct fd_data { struct fdc_data *fdc; /* pointer to controller structure */ int fdsu; /* this units number on this controller */ int type; /* Drive type (FD_1440...) */ struct fd_type *ft; /* pointer to the type descriptor */ int flags; #define FD_OPEN 0x01 /* it's open */ #define FD_ACTIVE 0x02 /* it's active */ #define FD_MOTOR 0x04 /* motor should be on */ #define FD_MOTOR_WAIT 0x08 /* motor coming up */ int skip; int hddrv; #define FD_NO_TRACK -2 int track; /* where we think the head is */ int options; /* user configurable options, see ioctl_fd.h */ struct callout_handle toffhandle; struct callout_handle tohandle; struct devstat device_stats; #ifdef PC98 int pc98_trans; #endif device_t dev; fdu_t fdu; }; static devclass_t fd_devclass; #ifdef EPSON_NRDISK typedef unsigned int nrd_t; #define P_NRD_ADDRH 0xc24 #define P_NRD_ADDRM 0xc22 #define P_NRD_ADDRL 0xc20 #define P_NRD_CHECK 0xc20 #define P_NRD_DATA 0xc26 #define P_NRD_LED 0xc36 #define B_NRD_CHK 0x80 #define B_NRD_LED 0x40 #define A_NRD_INFO 0x2 #define A_NRD_BASE 0x400 #define NRD_STATUS 0x0 #define NRD_ST0_HD 0x04 static fdu_t nrdu=-1; static int nrdsec=0; static nrd_t nrdblkn=0; static nrd_t nrdaddr=0x0; #define nrd_check_ready() ({ \ (epson_inb(P_NRD_CHECK) & B_NRD_CHK) ? 0 : 1; \ }) #define nrd_LED_on() epson_outb(P_NRD_LED, B_NRD_LED) #define nrd_LED_off() epson_outb(P_NRD_LED, ~B_NRD_LED) #define nrd_trac() ((int)(nrd_info(nrdaddr) & 0xff)) #define nrd_head() ((int)((nrd_info(nrdaddr) >> 8) & 0xff)) #define nrd_sec() ((int)(nrd_info(nrdaddr + 2) & 0xff)) #define nrd_secsize() ((int)((nrd_info(A_NRD_INFO) >> 8) & 0xff)) #define nrd_addrset(p) nrd_addr((nrd_t)((nrd_t)p+A_NRD_BASE)) static inline void nrd_addr(addr) nrd_t addr; { epson_outb(P_NRD_ADDRH, (u_char)((addr >> 16) & 0x1f)); epson_outb(P_NRD_ADDRM, (u_char)((addr >> 8) & 0xff)); epson_outb(P_NRD_ADDRL, (u_char)(addr & 0xff)); } static inline u_short nrd_info(addr) nrd_t addr; { u_short tmp; nrd_addr(addr); outb(0x43f, 0x42); tmp = (short)inw(P_NRD_DATA); outb(0x43f, 0x40); return ((u_short)tmp); } #endif /* EPSON_NRDISK */ /***********************************************************************\ * Throughout this file the following conventions will be used: * * fd is a pointer to the fd_data struct for the drive in question * * fdc is a pointer to the fdc_data struct for the controller * * fdu is the floppy drive unit number * * fdcu is the floppy controller unit number * * fdsu is the floppy drive unit number on that controller. (sub-unit) * \***********************************************************************/ #ifdef FDC_YE #include "card.h" static int yeattach(struct isa_device *); #endif /* needed for ft driver, thus exported */ int in_fdc(struct fdc_data *); int out_fdc(struct fdc_data *, int); /* internal functions */ static void fdc_add_device(device_t, const char *, int); static void fdc_intr(void *); static void set_motor(struct fdc_data *, int, int); # define TURNON 1 # define TURNOFF 0 static timeout_t fd_turnoff; static timeout_t fd_motor_on; static void fd_turnon(struct fd_data *); static void fdc_reset(fdc_p); static int fd_in(struct fdc_data *, int *); static void fdstart(struct fdc_data *); static timeout_t fd_iotimeout; static timeout_t fd_pseudointr; static int fdstate(struct fdc_data *); static int retrier(struct fdc_data *); static int fdformat(dev_t, struct fd_formb *, struct proc *); static int enable_fifo(fdc_p fdc); static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */ #define DEVIDLE 0 #define FINDWORK 1 #define DOSEEK 2 #define SEEKCOMPLETE 3 #define IOCOMPLETE 4 #define RECALCOMPLETE 5 #define STARTRECAL 6 #define RESETCTLR 7 #define SEEKWAIT 8 #define RECALWAIT 9 #define MOTORWAIT 10 #define IOTIMEDOUT 11 #define RESETCOMPLETE 12 #ifdef FDC_YE #define PIOREAD 13 #endif #ifdef FDC_DEBUG static char const * const fdstates[] = { "DEVIDLE", "FINDWORK", "DOSEEK", "SEEKCOMPLETE", "IOCOMPLETE", "RECALCOMPLETE", "STARTRECAL", "RESETCTLR", "SEEKWAIT", "RECALWAIT", "MOTORWAIT", "IOTIMEDOUT", "RESETCOMPLETE", #ifdef FDC_YE "PIOREAD", #endif }; /* CAUTION: fd_debug causes huge amounts of logging output */ static int volatile fd_debug = 0; #define TRACE0(arg) if(fd_debug) printf(arg) #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) #else /* FDC_DEBUG */ #define TRACE0(arg) #define TRACE1(arg1, arg2) #endif /* FDC_DEBUG */ static void fdout_wr(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->portt, fdc->porth, FDOUT+fdc->port_off, v); } static u_int8_t fdsts_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDSTS+fdc->port_off); } static void fddata_wr(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off, v); } static u_int8_t fddata_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off); } #ifndef PC98 static void fdctl_wr(fdc_p fdc, u_int8_t v) { if (fdc->flags & FDC_ISPNP) bus_space_write_1(fdc->ctlt, fdc->ctlh, 0, v); else bus_space_write_1(fdc->portt, fdc->porth, FDCTL, v); } #endif #if 0 static u_int8_t fdin_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDIN); } #endif #ifdef FDC_YE #if NCARD > 0 #include #include #include #include #include /* * PC-Card (PCMCIA) specific code. */ static int yeinit(struct pccard_devinfo *); /* init device */ static void yeunload(struct pccard_devinfo *); /* Disable driver */ static int yeintr(struct pccard_devinfo *); /* Interrupt handler */ PCCARD_MODULE(fdc, yeinit, yeunload, yeintr, 0, bio_imask); /* * this is the secret PIO data port (offset from base) */ #define FDC_YE_DATAPORT 6 /* * Initialize the device - called from Slot manager. */ static int yeinit(struct pccard_devinfo *devi) { fdc_p fdc = &fdc_data[devi->isahd.id_unit]; fdc->baseport = devi->isahd.id_iobase; /* * reset controller */ fdout_wr(fdc, 0); DELAY(100); fdout_wr(fdc, FDO_FRST); /* * wire into system */ if (yeattach(&devi->isahd) == 0) return(ENXIO); return(0); } /* * yeunload - unload the driver and clear the table. * XXX TODO: * This is usually called when the card is ejected, but * can be caused by a modunload of a controller driver. * The idea is to reset the driver's view of the device * and ensure that any driver entry points such as * read and write do not hang. */ static void yeunload(struct pccard_devinfo *devi) { if (fd_data[devi->isahd.id_unit].type == NO_TYPE) return; /* * this prevents Fdopen() and fdstrategy() from attempting * to access unloaded controller */ fd_data[devi->isahd.id_unit].type = NO_TYPE; printf("fdc%d: unload\n", devi->isahd.id_unit); } /* * yeintr - Shared interrupt called from * front end of PC-Card handler. */ static int yeintr(struct pccard_devinfo *devi) { fdintr((fdcu_t)devi->isahd.id_unit); return(1); } #endif /* NCARD > 0 */ #endif /* FDC_YE */ static d_open_t Fdopen; /* NOTE, not fdopen */ static d_close_t fdclose; static d_ioctl_t fdioctl; static d_strategy_t fdstrategy; #define CDEV_MAJOR 9 #define BDEV_MAJOR 2 static struct cdevsw fd_cdevsw = { /* open */ Fdopen, /* close */ fdclose, /* read */ physread, /* write */ physwrite, /* ioctl */ fdioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ fdstrategy, /* name */ "fd", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ D_DISK, /* bmaj */ BDEV_MAJOR }; static int fdc_err(struct fdc_data *fdc, const char *s) { fdc->fdc_errs++; if (s) { if (fdc->fdc_errs < FDC_ERRMAX) { device_print_prettyname(fdc->fdc_dev); printf("%s", s); } else if (fdc->fdc_errs == FDC_ERRMAX) { device_print_prettyname(fdc->fdc_dev); printf("too many errors, not logging any more\n"); } } return FD_FAILED; } /* * fd_cmd: Send a command to the chip. Takes a varargs with this structure: * Unit number, * # of output bytes, output bytes as ints ..., * # of input bytes, input bytes as ints ... */ static int fd_cmd(struct fdc_data *fdc, int n_out, ...) { u_char cmd; int n_in; int n; va_list ap; va_start(ap, n_out); cmd = (u_char)(va_arg(ap, int)); va_end(ap); va_start(ap, n_out); for (n = 0; n < n_out; n++) { if (out_fdc(fdc, va_arg(ap, int)) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %x failed at out byte %d of %d\n", cmd, n + 1, n_out); return fdc_err(fdc, msg); } } n_in = va_arg(ap, int); for (n = 0; n < n_in; n++) { int *ptr = va_arg(ap, int *); if (fd_in(fdc, ptr) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %02x failed at in byte %d of %d\n", cmd, n + 1, n_in); return fdc_err(fdc, msg); } } return 0; } static int enable_fifo(fdc_p fdc) { int i, j; if ((fdc->flags & FDC_HAS_FIFO) == 0) { /* * XXX: * Cannot use fd_cmd the normal way here, since * this might be an invalid command. Thus we send the * first byte, and check for an early turn of data directon. */ if (out_fdc(fdc, I8207X_CONFIGURE) < 0) return fdc_err(fdc, "Enable FIFO failed\n"); /* If command is invalid, return */ j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO | NE7_RQM)) != NE7_RQM && j-- > 0) if (i == (NE7_DIO | NE7_RQM)) { fdc_reset(fdc); return FD_FAILED; } if (j<0 || fd_cmd(fdc, 3, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) { fdc_reset(fdc); return fdc_err(fdc, "Enable FIFO failed\n"); } fdc->flags |= FDC_HAS_FIFO; return 0; } if (fd_cmd(fdc, 4, I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) return fdc_err(fdc, "Re-enable FIFO failed\n"); return 0; } static int fd_sense_drive_status(fdc_p fdc, int *st3p) { int st3; if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) { return fdc_err(fdc, "Sense Drive Status failed\n"); } if (st3p) *st3p = st3; return 0; } static int fd_sense_int(fdc_p fdc, int *st0p, int *cylp) { int cyl, st0, ret; #ifdef EPSON_NRDISK if (fdc->fdu == nrdu) { if (fdc->fd->track >= 0) nrdaddr = (fdc->fd->track + 1) * 8; else nrdaddr = 0x0; *st0p = nrd_head() ? NRD_ST0_HD : NRD_STATUS; *cylp = nrd_trac(); } else { #endif /* EPSON_NRDISK */ ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0); if (ret) { (void)fdc_err(fdc, "sense intr err reading stat reg 0\n"); return ret; } if (st0p) *st0p = st0; if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) { /* * There doesn't seem to have been an interrupt. */ return FD_NOT_VALID; } if (fd_in(fdc, &cyl) < 0) { return fdc_err(fdc, "can't get cyl num\n"); } if (cylp) *cylp = cyl; #ifdef EPSON_NRDISK } #endif /* EPSON_NRDISK */ return 0; } static int fd_read_status(fdc_p fdc, int fdsu) { int i, ret; for (i = 0; i < 7; i++) { /* * XXX types are poorly chosen. Only bytes can by read * from the hardware, but fdc->status[] wants u_ints and * fd_in() gives ints. */ int status; #ifdef EPSON_NRDISK if (fdc->fdu == nrdu) { switch (i) { case 0: fdc->status[i] = nrd_head() ? NRD_ST0_HD : NRD_STATUS; break; case 1: fdc->status[i] = NRD_STATUS; break; case 2: fdc->status[i] = NRD_STATUS; break; case 3: fdc->status[i] = nrd_trac(); break; case 4: fdc->status[i] = nrd_head(); break; case 5: fdc->status[i] = nrdsec; break; case 6: fdc->status[i] = nrd_secsize(); break; } ret = 0; } else { #endif /* EPSON_NRDISK */ ret = fd_in(fdc, &status); fdc->status[i] = status; if (ret != 0) break; #ifdef EPSON_NRDISK } #endif /* EPSON_NRDISK */ } if (ret == 0) fdc->flags |= FDC_STAT_VALID; else fdc->flags &= ~FDC_STAT_VALID; return ret; } /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ #ifdef PC98 static int pc98_trans = 0; /* 0 : HD , 1 : DD , 2 : 1.44 */ static int pc98_trans_prev = 0; static void set_density(fdc_p fdc) { /* always motor on */ outb(IO_FDPORT, (pc98_trans != 1 ? FDP_FDDEXC : 0) | FDP_PORTEXC); DELAY(100); fdout_wr(fdc, FDO_RST | FDO_DMAE); /* in the case of note W, always inhibit 100ms timer */ } static int pc98_fd_check_ready(fdu_t fdu) { fd_p fd = devclass_get_softc(fd_devclass, fdu); struct fdc_data *fdc = fd->fdc; int retry = 0; #ifdef EPSON_NRDISK if (fdu == nrdu) { if (nrd_check_ready()) return 0; else return -1; } #endif while (retry++ < 30000) { set_motor(fdc, fd->fdsu, TURNON); out_fdc(fdc, NE7CMD_SENSED); /* Sense Drive Status */ DELAY(100); out_fdc(fdc, fdu); /* Drive number */ DELAY(100); if ((in_fdc(fdc) & NE7_ST3_RD)){ fdout_wr(fdc, FDO_DMAE | FDO_MTON); DELAY(10); return 0; } } return -1; } #endif static struct isa_pnp_id fdc_ids[] = { {0x0007d041, "PC standard floppy disk controller"}, /* PNP0700 */ {0x0107d041, "Standard floppy controller supporting MS Device Bay Spec"}, /* PNP0701 */ {0} }; /* * fdc controller section. */ static int fdc_probe(device_t dev) { int error, ispnp, ic_type; struct fdc_data *fdc; /* Check pnp ids */ error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids); if (error == ENXIO) return ENXIO; ispnp = (error == 0); fdc = device_get_softc(dev); bzero(fdc, sizeof *fdc); fdc->fdc_dev = dev; fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0; fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0; fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, &fdc->rid_ioport, 0ul, ~0ul, ispnp ? 1 : IO_FDCSIZE, RF_ACTIVE); if (fdc->res_ioport == 0) { device_printf(dev, "cannot reserve I/O port range\n"); error = ENXIO; goto out; } fdc->portt = rman_get_bustag(fdc->res_ioport); fdc->porth = rman_get_bushandle(fdc->res_ioport); #ifndef PC98 if (ispnp) { /* * Some bios' report the device at 0x3f2-0x3f5,0x3f7 * and some at 0x3f0-0x3f5,0x3f7. We detect the former * by checking the size and adjust the port address * accordingly. */ if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4) fdc->port_off = -2; fdc->flags |= FDC_ISPNP; fdc->rid_ctl = 1; fdc->res_ctl = bus_alloc_resource(dev, SYS_RES_IOPORT, &fdc->rid_ctl, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_ctl == 0) { device_printf(dev, "cannot reserve I/O port range\n"); error = ENXIO; goto out; } fdc->ctlt = rman_get_bustag(fdc->res_ctl); fdc->ctlh = rman_get_bushandle(fdc->res_ctl); } #endif fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &fdc->rid_irq, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_irq == 0) { device_printf(dev, "cannot reserve interrupt line\n"); error = ENXIO; goto out; } fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ, &fdc->rid_drq, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_drq == 0) { device_printf(dev, "cannot reserve DMA request line\n"); error = ENXIO; goto out; } fdc->dmachan = fdc->res_drq->r_start; error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq, INTR_TYPE_BIO, fdc_intr, fdc, &fdc->fdc_intr); #ifndef PC98 /* First - lets reset the floppy controller */ fdout_wr(fdc, 0); DELAY(100); fdout_wr(fdc, FDO_FRST); #endif /* see if it can handle a command */ #ifdef PC98 if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0), 0)) { error = ENXIO; goto out; } #else if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0)) { error = ENXIO; goto out; } #endif #ifndef PC98 if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { ic_type = (u_char)ic_type; switch (ic_type) { case 0x80: device_set_desc(dev, "NEC 765 or clone"); fdc->fdct = FDC_NE765; break; case 0x81: device_set_desc(dev, "Intel 82077 or clone"); fdc->fdct = FDC_I82077; break; case 0x90: device_set_desc(dev, "NEC 72065B or clone"); fdc->fdct = FDC_NE72065; break; default: device_set_desc(dev, "generic floppy controller"); fdc->fdct = FDC_UNKNOWN; break; } } #endif #ifdef FDC_YE /* * don't succeed on probe; wait * for PCCARD subsystem to do it */ if (dev->id_flags & FDC_IS_PCMCIA) return(0); #endif return (0); out: if (fdc->fdc_intr) BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq, fdc->fdc_intr); if (fdc->res_irq != 0) { bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq, fdc->res_irq); bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq, fdc->res_irq); } if (fdc->res_ctl != 0) { bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, fdc->res_ctl); bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, fdc->res_ctl); } if (fdc->res_ioport != 0) { bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, fdc->res_ioport); bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, fdc->res_ioport); } if (fdc->res_drq != 0) { bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq, fdc->res_drq); bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq, fdc->res_drq); } return (error); } /* * Aped dfr@freebsd.org's isa_add_device(). */ static void fdc_add_device(device_t dev, const char *name, int unit) { int disabled, *ivar; device_t child; ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT); if (ivar == 0) return; if (resource_int_value(name, unit, "drive", ivar) != 0) *ivar = 0; - child = device_add_child(dev, name, unit, ivar); + child = device_add_child(dev, name, unit); + device_set_ivars(child, ivar); if (child == 0) return; if (resource_int_value(name, unit, "disabled", &disabled) == 0 && disabled != 0) device_disable(child); } static int fdc_attach(device_t dev) { struct fdc_data *fdc = device_get_softc(dev); fdcu_t fdcu = device_get_unit(dev); int i; for (i = resource_query_string(-1, "at", device_get_nameunit(dev)); i != -1; i = resource_query_string(i, "at", device_get_nameunit(dev))) fdc_add_device(dev, resource_query_name(i), resource_query_unit(i)); fdc->fdcu = fdcu; fdc->flags |= FDC_ATTACHED; /* Acquire the DMA channel forever, The driver will do the rest */ /* XXX should integrate with rman */ isa_dma_acquire(fdc->dmachan); isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */); fdc->state = DEVIDLE; #ifdef PC98 /* reset controller, turn motor off, clear fdout mirror reg */ fdc_reset(fdc); #else /* reset controller, turn motor off, clear fdout mirror reg */ fdout_wr(fdc, ((fdc->fdout = 0))); #endif bufq_init(&fdc->head); /* * Probe and attach any children as were configured above. */ return (bus_generic_attach(dev)); } static int fdc_print_child(device_t me, device_t child) { int retval = 0; retval += bus_print_child_header(me, child); retval += printf(" on %s drive %d\n", device_get_nameunit(me), *(int *)device_get_ivars(child)); return (retval); } static device_method_t fdc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fdc_probe), DEVMETHOD(device_attach, fdc_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, fdc_print_child), /* Our children never use any other bus interface methods. */ { 0, 0 } }; static driver_t fdc_driver = { "fdc", fdc_methods, sizeof(struct fdc_data) }; DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0); /******************************************************************/ /* * devices attached to the controller section. */ static int fd_probe(device_t dev) { int i; u_int fdt, st0, st3; struct fd_data *fd; struct fdc_data *fdc; fdsu_t fdsu; static int fd_fifo = 0; fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */ fd = device_get_softc(dev); fdc = device_get_softc(device_get_parent(dev)); bzero(fd, sizeof *fd); fd->dev = dev; fd->fdc = fdc; fd->fdsu = fdsu; fd->fdu = device_get_unit(dev); #ifdef PC98 /* look up what bios thinks we have */ switch (fd->fdu) { case 0: case 1: case 2: case 3: if ((PC98_SYSTEM_PARAMETER(0x5ae) >> fd->fdu) & 0x01) fdt = FDT_144M; #ifdef EPSON_NRDISK else if ((PC98_SYSTEM_PARAMETER(0x55c) >> fd->fdu) & 0x01) { fdt = FDT_12M; switch (epson_machine_id) { case 0x20: case 0x27: if ((PC98_SYSTEM_PARAMETER(0x488) >> fd->fdu) & 0x01) { if (nrd_check_ready()) { nrd_LED_on(); nrdu = fd->fdu; } else { fdt = FDT_NONE; } } } } #else /* !EPSON_NRDISK */ else if ((PC98_SYSTEM_PARAMETER(0x55c) >> fd->fdu) & 0x01) { fdt = FDT_12M; switch (epson_machine_id) { case 0x20: case 0x27: if ((PC98_SYSTEM_PARAMETER(0x488) >> fd->fdu) & 0x01) fdt = FDT_NONE; } } #endif /* EPSON_NRDISK */ else fdt = FDT_NONE; break; default: fdt = FDT_NONE; break; } #else #ifdef __i386__ /* look up what bios thinks we have */ switch (fd->fdu) { case 0: if (device_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0) fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED; else fdt = (rtcin(RTC_FDISKETTE) & 0xf0); break; case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); break; default: fdt = RTCFDT_NONE; break; } #else fdt = RTCFDT_144M; /* XXX probably */ #endif #endif /* is there a unit? */ #ifdef PC98 if (fdt == FDT_NONE) return (ENXIO); #else if (fdt == RTCFDT_NONE) return (ENXIO); #endif #ifndef PC98 /* select it */ set_motor(fdc, fdsu, TURNON); DELAY(1000000); /* 1 sec */ /* XXX This doesn't work before the first set_motor() */ if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN && enable_fifo(fdc) == 0) { device_print_prettyname(device_get_parent(dev)); printf("FIFO enabled, %d bytes threshold\n", fifo_threshold); } fd_fifo = 1; if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* if at track 0, first seek inwards */ /* seek some steps: */ fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0); DELAY(300000); /* ...wait a moment... */ fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ } /* If we're at track 0 first seek inwards. */ if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* Seek some steps... */ if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { /* ...wait a moment... */ DELAY(300000); /* make ctrlr happy: */ fd_sense_int(fdc, 0, 0); } } for (i = 0; i < 2; i++) { /* * we must recalibrate twice, just in case the * heads have been beyond cylinder 76, since most * FDCs still barf when attempting to recalibrate * more than 77 steps */ /* go back to 0: */ if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) { /* a second being enough for full stroke seek*/ DELAY(i == 0 ? 1000000 : 300000); /* anything responding? */ if (fd_sense_int(fdc, &st0, 0) == 0 && (st0 & NE7_ST0_EC) == 0) break; /* already probed succesfully */ } } set_motor(fdc, fdsu, TURNOFF); if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ return (ENXIO); #endif /* PC98 */ fd->track = FD_NO_TRACK; fd->fdc = fdc; fd->fdsu = fdsu; fd->options = 0; callout_handle_init(&fd->toffhandle); callout_handle_init(&fd->tohandle); #ifdef PC98 switch (fdt) { case FDT_144M: /* Check 3mode I/F */ fd->pc98_trans = 0; outb(0x4be, (fd->fdu << 5) | 0x10); if (!(inb(0x4be) & 0x01)) { device_set_desc(dev, "1.44M FDD"); fd->type = FD_1440; break; } printf("Warning: can't control 3mode I/F, " "fallback to 2mode.\n" "fd%d: ", fd->fdu); /* FALLTHROUGH */ case FDT_12M: #ifdef EPSON_NRDISK if (fd->fdu == nrdu) { device_set_desc(dev, "EPSON RAM DRIVE"); nrd_LED_off(); } else #endif device_set_desc(dev, "1M/640K FDD"); fd->type = FD_1200; fd->pc98_trans = 0; break; default: return (ENXIO); } #else switch (fdt) { case RTCFDT_12M: device_set_desc(dev, "1200-KB 5.25\" drive"); fd->type = FD_1200; break; case RTCFDT_144M | RTCFDT_144M_PRETENDED: device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive"); fdt = RTCFDT_144M; fd->type = FD_1440; case RTCFDT_144M: device_set_desc(dev, "1440-KB 3.5\" drive"); fd->type = FD_1440; break; case RTCFDT_288M: case RTCFDT_288M_1: device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); fd->type = FD_1440; break; case RTCFDT_360K: device_set_desc(dev, "360-KB 5.25\" drive"); fd->type = FD_360; break; case RTCFDT_720K: printf("720-KB 3.5\" drive"); fd->type = FD_720; break; default: return (ENXIO); } #endif return (0); } static int fd_attach(device_t dev) { struct fd_data *fd; #if 0 int i; int mynor; int typemynor; int typesize; #endif fd = device_get_softc(dev); cdevsw_add(&fd_cdevsw); /* XXX fill in devices */ make_dev(&fd_cdevsw, (fd->fdu << 6), UID_ROOT, GID_OPERATOR, 0640, "rfd%d", fd->fdu); #if 0 /* Other make_dev() go here. */ #endif /* * Export the drive to the devstat interface. */ devstat_add_entry(&fd->device_stats, device_get_name(dev), device_get_unit(dev), 512, DEVSTAT_NO_ORDERED_TAGS, DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, DEVSTAT_PRIORITY_FD); return (0); } static device_method_t fd_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fd_probe), DEVMETHOD(device_attach, fd_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX */ DEVMETHOD(device_resume, bus_generic_resume), /* XXX */ { 0, 0 } }; static driver_t fd_driver = { "fd", fd_methods, sizeof(struct fd_data) }; DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, 0, 0); /******************************************************************/ #ifdef FDC_YE /* * this is a subset of fdattach() optimized for the Y-E Data * PCMCIA floppy drive. */ static int yeattach(struct isa_device *dev) { fdcu_t fdcu = dev->id_unit; fdc_p fdc = fdc_data + fdcu; fdsu_t fdsu = 0; /* assume 1 drive per YE controller */ fdu_t fdu; fd_p fd; int st0, st3, i; fdc->fdcu = fdcu; /* * the FDC_PCMCIA flag is used to to indicate special PIO is used * instead of DMA */ fdc->flags = FDC_ATTACHED|FDC_PCMCIA; fdc->state = DEVIDLE; /* reset controller, turn motor off, clear fdout mirror reg */ fdout_wr(fdc, ((fdc->fdout = 0))); bufq_init(&fdc->head); /* * assume 2 drives/ "normal" controller */ fdu = fdcu * 2; if (fdu >= NFD) { printf("fdu %d >= NFD\n",fdu); return(0); }; fd = &fd_data[fdu]; set_motor(fdcu, fdsu, TURNON); DELAY(1000000); /* 1 sec */ fdc->fdct = FDC_NE765; if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* if at track 0, first seek inwards */ /* seek some steps: */ (void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0); DELAY(300000); /* ...wait a moment... */ (void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ } /* If we're at track 0 first seek inwards. */ if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* Seek some steps... */ if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { /* ...wait a moment... */ DELAY(300000); /* make ctrlr happy: */ (void)fd_sense_int(fdc, 0, 0); } } for(i = 0; i < 2; i++) { /* * we must recalibrate twice, just in case the * heads have been beyond cylinder 76, since most * FDCs still barf when attempting to recalibrate * more than 77 steps */ /* go back to 0: */ if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) { /* a second being enough for full stroke seek*/ DELAY(i == 0? 1000000: 300000); /* anything responding? */ if (fd_sense_int(fdc, &st0, 0) == 0 && (st0 & NE7_ST0_EC) == 0) break; /* already probed succesfully */ } } set_motor(fdcu, fdsu, TURNOFF); if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ return(0); fd->track = FD_NO_TRACK; fd->fdc = fdc; fd->fdsu = fdsu; fd->options = 0; printf("fdc%d: 1.44MB 3.5in PCMCIA\n", fdcu); fd->type = FD_1440; return (1); } #endif /****************************************************************************/ /* motor control stuff */ /* remember to not deselect the drive we're working on */ /****************************************************************************/ static void set_motor(struct fdc_data *fdc, int fdsu, int turnon) { int fdout = fdc->fdout; int needspecify = 0; #ifdef PC98 outb(IO_FDPORT, (pc98_trans != 1 ? FDP_FDDEXC : 0)|FDP_PORTEXC); DELAY(10); fdout = FDO_DMAE|FDO_MTON; #else if(turnon) { fdout &= ~FDO_FDSEL; fdout |= (FDO_MOEN0 << fdsu) + fdsu; } else fdout &= ~(FDO_MOEN0 << fdsu); if(!turnon && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) /* gonna turn off the last drive, put FDC to bed */ fdout &= ~ (FDO_FRST|FDO_FDMAEN); else { /* make sure controller is selected and specified */ if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) needspecify = 1; fdout |= (FDO_FRST|FDO_FDMAEN); } #endif fdout_wr(fdc, fdout); fdc->fdout = fdout; TRACE1("[0x%x->FDOUT]", fdout); if (needspecify) { /* * XXX * special case: since we have just woken up the FDC * from its sleep, we silently assume the command will * be accepted, and do not test for a timeout */ #ifdef PC98 (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0), 0); #else (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); #endif if (fdc->flags & FDC_HAS_FIFO) (void) enable_fifo(fdc); } } static void fd_turnoff(void *xfd) { int s; fd_p fd = xfd; TRACE1("[fd%d: turnoff]", fd->fdu); /* * Don't turn off the motor yet if the drive is active. * XXX shouldn't even schedule turnoff until drive is inactive * and nothing is queued on it. */ if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) { fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); return; } s = splbio(); fd->flags &= ~FD_MOTOR; set_motor(fd->fdc, fd->fdsu, TURNOFF); splx(s); } static void fd_motor_on(void *xfd) { int s; fd_p fd = xfd; s = splbio(); fd->flags &= ~FD_MOTOR_WAIT; if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) { fdc_intr(fd->fdc); } splx(s); } static void fd_turnon(fd_p fd) { if(!(fd->flags & FD_MOTOR)) { fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); set_motor(fd->fdc, fd->fdsu, TURNON); timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */ } } static void fdc_reset(fdc_p fdc) { /* Try a reset, keep motor on */ #ifdef PC98 set_density(fdc); if (pc98_machine_type & M_EPSON_PC98) fdout_wr(fdc, 0xe8); else fdout_wr(fdc, 0xd8); DELAY(200); fdout_wr(fdc, 0x18); DELAY(10); #else fdout_wr(fdc, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); DELAY(100); /* enable FDC, but defer interrupts a moment */ fdout_wr(fdc, fdc->fdout & ~FDO_FDMAEN); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); DELAY(100); fdout_wr(fdc, fdc->fdout); TRACE1("[0x%x->FDOUT]", fdc->fdout); #endif /* XXX after a reset, silently believe the FDC will accept commands */ #ifdef PC98 (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0), 0); #else (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); #endif if (fdc->flags & FDC_HAS_FIFO) (void) enable_fifo(fdc); } /****************************************************************************/ /* fdc in/out */ /****************************************************************************/ int in_fdc(struct fdc_data *fdc) { int i, j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = fddata_rd(fdc); TRACE1("[FDDATA->0x%x]", (unsigned char)i); return(i); #else /* !FDC_DEBUG */ return fddata_rd(fdc); #endif /* FDC_DEBUG */ } /* * fd_in: Like in_fdc, but allows you to see if it worked. */ static int fd_in(struct fdc_data *fdc, int *ptr) { int i, j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = fddata_rd(fdc); TRACE1("[FDDATA->0x%x]", (unsigned char)i); *ptr = i; return 0; #else /* !FDC_DEBUG */ i = fddata_rd(fdc); if (ptr) *ptr = i; return 0; #endif /* FDC_DEBUG */ } int out_fdc(struct fdc_data *fdc, int x) { int i; /* Check that the direction bit is set */ i = 100000; while ((fdsts_rd(fdc) & NE7_DIO) && i-- > 0); if (i <= 0) return fdc_err(fdc, "direction bit not set\n"); /* Check that the floppy controller is ready for a command */ i = 100000; while ((fdsts_rd(fdc) & NE7_RQM) == 0 && i-- > 0); if (i <= 0) return fdc_err(fdc, bootverbose? "output ready timeout\n": 0); /* Send the command and return */ fddata_wr(fdc, x); TRACE1("[0x%x->FDDATA]", x); return (0); } /****************************************************************************/ /* fdopen/fdclose */ /****************************************************************************/ int Fdopen(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); int type = FDTYPE(minor(dev)); fd_p fd; fdc_p fdc; /* check bounds */ if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0) return (ENXIO); fdc = fd->fdc; if ((fdc == NULL) || (fd->type == NO_TYPE)) return (ENXIO); if (type > NUMDENS) return (ENXIO); #ifdef PC98 if (type == 0) type = FD_1200; /* XXX backward compatibility */ else ; /* XXX any types are OK for PC-98 */ if (pc98_fd_check_ready(fdu) == -1) return(EIO); #else if (type == 0) type = fd->type; else { /* * For each type of basic drive, make sure we are trying * to open a type it can do, */ if (type != fd->type) { switch (fd->type) { case FD_360: return (ENXIO); case FD_720: if ( type != FD_820 && type != FD_800 ) return (ENXIO); break; case FD_1200: switch (type) { case FD_1480: type = FD_1480in5_25; break; case FD_1440: type = FD_1440in5_25; break; case FD_820: type = FD_820in5_25; break; case FD_800: type = FD_800in5_25; break; case FD_720: type = FD_720in5_25; break; case FD_360: type = FD_360in5_25; break; default: return(ENXIO); } break; case FD_1440: if ( type != FD_1720 && type != FD_1480 && type != FD_1200 && type != FD_820 && type != FD_800 && type != FD_720 ) return(ENXIO); break; } } } #endif fd->ft = fd_types + type - 1; fd->flags |= FD_OPEN; device_busy(fd->dev); device_busy(fd->fdc->fdc_dev); return 0; } int fdclose(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); struct fd_data *fd; fd = devclass_get_softc(fd_devclass, fdu); fd->flags &= ~FD_OPEN; fd->options &= ~FDOPT_NORETRY; return (0); } /****************************************************************************/ /* fdstrategy */ /****************************************************************************/ void fdstrategy(struct buf *bp) { unsigned nblocks, blknum, cando; int s; fdu_t fdu; fdc_p fdc; fd_p fd; size_t fdblk; fdu = FDUNIT(minor(bp->b_dev)); fd = devclass_get_softc(fd_devclass, fdu); if (fd == 0) panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)", (u_long)major(bp->b_dev), (u_long)minor(bp->b_dev)); fdc = fd->fdc; #ifdef FDC_YE if (fd->type == NO_TYPE) { bp->b_error = ENXIO; bp->b_flags |= B_ERROR; /* * I _refuse_ to use a goto */ biodone(bp); return; }; #endif fdblk = 128 << (fd->ft->secsize); if (!(bp->b_flags & B_FORMAT)) { if (bp->b_blkno < 0) { printf( "fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n", fdu, (u_long)bp->b_blkno, bp->b_bcount); bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } if ((bp->b_bcount % fdblk) != 0) { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } } /* * Set up block calculations. */ if (bp->b_blkno > 20000000) { /* * Reject unreasonably high block number, prevent the * multiplication below from overflowing. */ bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk; nblocks = fd->ft->size; bp->b_resid = 0; if (blknum + (bp->b_bcount / fdblk) > nblocks) { if (blknum <= nblocks) { cando = (nblocks - blknum) * fdblk; bp->b_resid = bp->b_bcount - cando; if (cando == 0) goto bad; /* not actually bad but EOF */ } else { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } } bp->b_pblkno = bp->b_blkno; s = splbio(); bufqdisksort(&fdc->head, bp); untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */ /* Tell devstat we are starting on the transaction */ devstat_start_transaction(&fd->device_stats); fdstart(fdc); splx(s); return; bad: biodone(bp); } /***************************************************************\ * fdstart * * We have just queued something.. if the controller is not busy * * then simulate the case where it has just finished a command * * So that it (the interrupt routine) looks on the queue for more* * work to do and picks up what we just added. * * If the controller is already busy, we need do nothing, as it * * will pick up our work when the present work completes * \***************************************************************/ static void fdstart(struct fdc_data *fdc) { int s; s = splbio(); if(fdc->state == DEVIDLE) { fdc_intr(fdc); } splx(s); } static void fd_iotimeout(void *xfdc) { fdc_p fdc; int s; fdc = xfdc; TRACE1("fd%d[fd_iotimeout()]", fdc->fdu); /* * Due to IBM's brain-dead design, the FDC has a faked ready * signal, hardwired to ready == true. Thus, any command * issued if there's no diskette in the drive will _never_ * complete, and must be aborted by resetting the FDC. * Many thanks, Big Blue! * The FDC must not be reset directly, since that would * interfere with the state machine. Instead, pretend that * the command completed but was invalid. The state machine * will reset the FDC and retry once. */ s = splbio(); fdc->status[0] = NE7_ST0_IC_IV; fdc->flags &= ~FDC_STAT_VALID; fdc->state = IOTIMEDOUT; fdc_intr(fdc); splx(s); } /* just ensure it has the right spl */ static void fd_pseudointr(void *xfdc) { int s; s = splbio(); fdc_intr(xfdc); splx(s); } /***********************************************************************\ * fdintr * * keep calling the state machine until it returns a 0 * * ALWAYS called at SPLBIO * \***********************************************************************/ static void fdc_intr(void *xfdc) { fdc_p fdc = xfdc; while(fdstate(fdc)) ; } #ifdef FDC_YE /* * magic pseudo-DMA initialization for YE FDC. Sets count and * direction */ #define SET_BCDR(wr,cnt,port) outb(port,(((cnt)-1) & 0xff)); \ outb(port+1,((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f))) /* * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy */ static int fdcpio(fdcu_t fdcu, long flags, caddr_t addr, u_int count) { u_char *cptr = (u_char *)addr; fdc_p fdc = &fdc_data[fdcu]; int io = fdc->baseport; if (flags & B_READ) { if (fdc->state != PIOREAD) { fdc->state = PIOREAD; return(0); }; SET_BCDR(0,count,io); insb(io+FDC_YE_DATAPORT,cptr,count); } else { outsb(io+FDC_YE_DATAPORT,cptr,count); SET_BCDR(0,count,io); }; return(1); } #endif /* FDC_YE */ /***********************************************************************\ * The controller state machine. * * if it returns a non zero value, it should be called again immediatly * \***********************************************************************/ static int fdstate(fdc_p fdc) { int read, format, head, i, sec = 0, sectrac, st0, cyl, st3; unsigned blknum = 0, b_cylinder = 0; fdu_t fdu = fdc->fdu; fd_p fd; register struct buf *bp; struct fd_formb *finfo = NULL; size_t fdblk; bp = fdc->bp; if (bp == NULL) { bp = bufq_first(&fdc->head); if (bp != NULL) { bufq_remove(&fdc->head, bp); fdc->bp = bp; } } if (bp == NULL) { /***********************************************\ * nothing left for this controller to do * * Force into the IDLE state, * \***********************************************/ fdc->state = DEVIDLE; if (fdc->fd) { device_print_prettyname(fdc->fdc_dev); printf("unexpected valid fd pointer\n"); fdc->fd = (fd_p) 0; fdc->fdu = -1; } TRACE1("[fdc%d IDLE]", fdc->fdcu); return (0); } fdu = FDUNIT(minor(bp->b_dev)); fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; if (fdc->fd && (fd != fdc->fd)) { device_print_prettyname(fd->dev); printf("confused fd pointers\n"); } read = bp->b_flags & B_READ; format = bp->b_flags & B_FORMAT; if (format) { finfo = (struct fd_formb *)bp->b_data; fd->skip = (char *)&(finfo->fd_formb_cylno(0)) - (char *)finfo; } if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) { blknum = (unsigned) bp->b_pblkno * DEV_BSIZE/fdblk + fd->skip/fdblk; b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); } TRACE1("fd%d", fdu); TRACE1("[%s]", fdstates[fdc->state]); TRACE1("(0x%x)", fd->flags); untimeout(fd_turnoff, fd, fd->toffhandle); fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); switch (fdc->state) { case DEVIDLE: case FINDWORK: /* we have found new work */ fdc->retry = 0; fd->skip = 0; fdc->fd = fd; fdc->fdu = fdu; #ifdef PC98 pc98_trans = fd->ft->trans; if (pc98_trans_prev != pc98_trans) { int i; set_density(fdc); for (i = 0; i < 10; i++) { outb(0x5f, 0); outb(0x5f, 0); } pc98_trans_prev = pc98_trans; } if (pc98_trans != fd->pc98_trans) { if (fd->type == FD_1440) { outb(0x4be, (fdu << 5) | 0x10 | (pc98_trans >> 1)); outb(0x5f, 0); outb(0x5f, 0); } fd->pc98_trans = pc98_trans; } #else fdctl_wr(fdc, fd->ft->trans); #endif TRACE1("[0x%x->FDCTL]", fd->ft->trans); /*******************************************************\ * If the next drive has a motor startup pending, then * * it will start up in its own good time * \*******************************************************/ if(fd->flags & FD_MOTOR_WAIT) { fdc->state = MOTORWAIT; return (0); /* come back later */ } /*******************************************************\ * Maybe if it's not starting, it SHOULD be starting * \*******************************************************/ #ifdef EPSON_NRDISK if (fdu != nrdu) { if (!(fd->flags & FD_MOTOR)) { fdc->state = MOTORWAIT; fd_turnon(fdu); return(0); } else /* at least make sure we are selected */ { set_motor(fdcu, fd->fdsu, TURNON); } } #else /* !EPSON_NRDISK */ if (!(fd->flags & FD_MOTOR)) { fdc->state = MOTORWAIT; fd_turnon(fd); return (0); } else /* at least make sure we are selected */ { set_motor(fdc, fd->fdsu, TURNON); } #endif if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else fdc->state = DOSEEK; break; case DOSEEK: if (b_cylinder == (unsigned)fd->track) { fdc->state = SEEKCOMPLETE; break; } #ifdef PC98 pc98_fd_check_ready(fdu); #endif if (fd_cmd(fdc, 3, NE7CMD_SEEK, fd->fdsu, b_cylinder * fd->ft->steptrac, 0)) { /* * seek command not accepted, looks like * the FDC went off to the Saints... */ fdc->retry = 6; /* try a reset */ return(retrier(fdc)); } fd->track = FD_NO_TRACK; fdc->state = SEEKWAIT; return(0); /* will return later */ case SEEKWAIT: /* allow heads to settle */ timeout(fd_pseudointr, fdc, hz / 16); fdc->state = SEEKCOMPLETE; return(0); /* will return later */ case SEEKCOMPLETE : /* SEEK DONE, START DMA */ /* Make sure seek really happened*/ if(fd->track == FD_NO_TRACK) { int descyl = b_cylinder * fd->ft->steptrac; do { /* * This might be a "ready changed" interrupt, * which cannot really happen since the * RDY pin is hardwired to + 5 volts. This * generally indicates a "bouncing" intr * line, so do one of the following: * * When running on an enhanced FDC that is * known to not go stuck after responding * with INVALID, fetch all interrupt states * until seeing either an INVALID or a * real interrupt condition. * * When running on a dumb old NE765, give * up immediately. The controller will * provide up to four dummy RC interrupt * conditions right after reset (for the * corresponding four drives), so this is * our only chance to get notice that it * was not the FDC that caused the interrupt. */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); if (0 == descyl) { int failed = 0; /* * seek to cyl 0 requested; make sure we are * really there */ if (fd_sense_drive_status(fdc, &st3)) failed = 1; #ifdef EPSON_NRDISK if (fdu == nrdu) st3 = NE7_ST3_T0; #endif /* EPSON_NRDISK */ if ((st3 & NE7_ST3_T0) == 0) { printf( "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", fdu, st3, NE7_ST3BITS); failed = 1; } if (failed) { if(fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } } #ifdef EPSON_NRDISK if (fdu == nrdu) cyl = descyl; #endif if (cyl != descyl) { printf( "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", fdu, descyl, cyl, st0); if (fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } } fd->track = b_cylinder; #ifdef EPSON_NRDISK if (fdu != nrdu) { #endif /* EPSON_NRDISK */ #ifdef FDC_YE if (!(fdc->flags & FDC_PCMCIA)) #endif isa_dmastart(bp->b_flags, bp->b_data+fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; fd->hddrv = ((head&1)<<2)+fdu; if(format || !read) { /* make sure the drive is writable */ if(fd_sense_drive_status(fdc, &st3) != 0) { /* stuck controller? */ isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); fdc->retry = 6; /* reset the beast */ return (retrier(fdc)); } if(st3 & NE7_ST3_WP) { /* * XXX YES! this is ugly. * in order to force the current operation * to fail, we will have to fake an FDC * error - all error handling is done * by the retrier() */ fdc->status[0] = NE7_ST0_IC_AT; fdc->status[1] = NE7_ST1_NW; fdc->status[2] = 0; fdc->status[3] = fd->track; fdc->status[4] = head; fdc->status[5] = sec; fdc->retry = 8; /* break out immediately */ fdc->state = IOTIMEDOUT; /* not really... */ return (1); } } if (format) { #ifdef FDC_YE if (fdc->flags & FDC_PCMCIA) (void)fdcpio(fdcu,bp->b_flags, bp->b_data+fd->skip, bp->b_bcount); #endif /* formatting */ if(fd_cmd(fdc, 6, NE7CMD_FORMAT, head << 2 | fdu, finfo->fd_formb_secshift, finfo->fd_formb_nsecs, finfo->fd_formb_gaplen, finfo->fd_formb_fillbyte, 0)) { /* controller fell over */ isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); fdc->retry = 6; return (retrier(fdc)); } } else { #ifdef FDC_YE if (fdc->flags & FDC_PCMCIA) { /* * this seems to be necessary even when * reading data */ SET_BCDR(1,fdblk,fdc->baseport); /* * perform the write pseudo-DMA before * the WRITE command is sent */ if (!read) (void)fdcpio(fdcu,bp->b_flags, bp->b_data+fd->skip, fdblk); } #endif if (fd_cmd(fdc, 9, (read ? NE7CMD_READ : NE7CMD_WRITE), head << 2 | fdu, /* head & unit */ fd->track, /* track */ head, sec, /* sector + 1 */ fd->ft->secsize, /* sector size */ sectrac, /* sectors/track */ fd->ft->gap, /* gap size */ fd->ft->datalen, /* data length */ 0)) { /* the beast is sleeping again */ isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); fdc->retry = 6; return (retrier(fdc)); } } #ifdef FDC_YE if (fdc->flags & FDC_PCMCIA) /* * if this is a read, then simply await interrupt * before performing PIO */ if (read && !fdcpio(fdcu,bp->b_flags, bp->b_data+fd->skip,fdblk)) { fd->tohandle = timeout(fd_iotimeout, (caddr_t)fdcu, hz); return(0); /* will return later */ }; /* * write (or format) operation will fall through and * await completion interrupt */ #endif fdc->state = IOCOMPLETE; fd->tohandle = timeout(fd_iotimeout, fdc, hz); return (0); /* will return later */ #ifdef EPSON_NRDISK } else { nrdblkn = (nrd_t)((unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + fd->skip/fdblk); nrd_LED_on(); nrd_addrset(fdblk * nrdblkn); while (!nrd_check_ready()) DELAY(1); if (read) epson_insw(P_NRD_DATA, bp->b_data + fd->skip, fdblk / sizeof(short)); else epson_outsw(P_NRD_DATA, bp->b_data + fd->skip, (format ? bp->b_bcount : fdblk) / sizeof(short)); blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + fd->skip/fdblk; sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; fd->hddrv = ((head&1)<<2)+fdu; if (nrdsec++ >= nrd_sec()) nrdaddr = (nrd_t)(fd->track * 8 + head * 4); nrdsec = sec; fdc->state = IOCOMPLETE; } #endif #ifdef FDC_YE case PIOREAD: /* * actually perform the PIO read. The IOCOMPLETE case * removes the timeout for us. */ (void)fdcpio(fdcu,bp->b_flags,bp->b_data+fd->skip,fdblk); fdc->state = IOCOMPLETE; /* FALLTHROUGH */ #endif case IOCOMPLETE: /* IO DONE, post-analyze */ #ifdef EPSON_NRDISK if (fdu != nrdu) untimeout(fd_iotimeout, fdc, fd->tohandle); #else untimeout(fd_iotimeout, fdc, fd->tohandle); #endif if (fd_read_status(fdc, fd->fdsu)) { isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); if (fdc->retry < 6) fdc->retry = 6; /* force a reset */ return (retrier(fdc)); } fdc->state = IOTIMEDOUT; /* FALLTHROUGH */ case IOTIMEDOUT: #ifdef EPSON_NRDISK if (fdu != nrdu) { #endif /* EPSON_NRDISK */ #ifdef FDC_YE if (!(fdc->flags & FDC_PCMCIA)) #endif isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); #ifdef EPSON_NRDISK } else nrd_LED_off(); #endif /* EPSON_NRDISK */ if (fdc->status[0] & NE7_ST0_IC) { if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[1] & NE7_ST1_OR) { /* * DMA overrun. Someone hogged the bus * and didn't release it in time for the * next FDC transfer. * Just restart it, don't increment retry * count. (vak) */ fdc->state = SEEKCOMPLETE; return (1); } else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV && fdc->retry < 6) fdc->retry = 6; /* force a reset */ else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[2] & NE7_ST2_WC && fdc->retry < 3) fdc->retry = 3; /* force recalibrate */ return (retrier(fdc)); } /* All OK */ fd->skip += fdblk; if (!format && fd->skip < bp->b_bcount - bp->b_resid) { /* set up next transfer */ fdc->state = DOSEEK; } else { /* ALL DONE */ fd->skip = 0; fdc->bp = NULL; devstat_end_transaction_buf(&fd->device_stats, bp); biodone(bp); fdc->fd = (fd_p) 0; fdc->fdu = -1; fdc->state = FINDWORK; } return (1); case RESETCTLR: fdc_reset(fdc); fdc->retry++; fdc->state = RESETCOMPLETE; return (0); case RESETCOMPLETE: /* * Discard all the results from the reset so that they * can't cause an unexpected interrupt later. */ for (i = 0; i < 4; i++) (void)fd_sense_int(fdc, &st0, &cyl); fdc->state = STARTRECAL; /* Fall through. */ case STARTRECAL: #ifdef PC98 pc98_fd_check_ready(fdu); #endif if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) { /* arrgl */ fdc->retry = 6; return (retrier(fdc)); } fdc->state = RECALWAIT; return (0); /* will return later */ case RECALWAIT: /* allow heads to settle */ timeout(fd_pseudointr, fdc, hz / 8); fdc->state = RECALCOMPLETE; return (0); /* will return later */ case RECALCOMPLETE: do { /* * See SEEKCOMPLETE for a comment on this: */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); #ifdef EPSON_NRDISK if (fdu == nrdu) { st0 = NE7_ST0_IC_NT; cyl = 0; } #endif if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) { if(fdc->retry > 3) /* * a recalibrate from beyond cylinder 77 * will "fail" due to the FDC limitations; * since people used to complain much about * the failure message, try not logging * this one if it seems to be the first * time in a line */ printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, st0, NE7_ST0BITS, cyl); if(fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } fd->track = 0; /* Seek (probably) necessary */ fdc->state = DOSEEK; return (1); /* will return immediatly */ case MOTORWAIT: if(fd->flags & FD_MOTOR_WAIT) { return (0); /* time's not up yet */ } if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else { /* * If all motors were off, then the controller was * reset, so it has lost track of the current * cylinder. Recalibrate to handle this case. * But first, discard the results of the reset. */ fdc->state = RESETCOMPLETE; } return (1); /* will return immediatly */ default: device_print_prettyname(fdc->fdc_dev); printf("unexpected FD int->"); if (fd_read_status(fdc, fd->fdsu) == 0) printf("FDC status :%x %x %x %x %x %x %x ", fdc->status[0], fdc->status[1], fdc->status[2], fdc->status[3], fdc->status[4], fdc->status[5], fdc->status[6] ); else printf("No status available "); if (fd_sense_int(fdc, &st0, &cyl) != 0) { printf("[controller is dead now]\n"); return (0); } printf("ST0 = %x, PCN = %x\n", st0, cyl); return (0); } /*XXX confusing: some branches return immediately, others end up here*/ return (1); /* Come back immediatly to new state */ } static int retrier(struct fdc_data *fdc) { register struct buf *bp; struct fd_data *fd; int fdu; bp = fdc->bp; /* XXX shouldn't this be cached somewhere? */ fdu = FDUNIT(minor(bp->b_dev)); fd = devclass_get_softc(fd_devclass, fdu); if (fd->options & FDOPT_NORETRY) goto fail; switch (fdc->retry) { case 0: case 1: case 2: fdc->state = SEEKCOMPLETE; break; case 3: case 4: case 5: fdc->state = STARTRECAL; break; case 6: fdc->state = RESETCTLR; break; case 7: break; default: fail: { dev_t sav_b_dev = bp->b_dev; /* Trick diskerr */ bp->b_dev = makedev(major(bp->b_dev), (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART); diskerr(bp, "hard error", LOG_PRINTF, fdc->fd->skip / DEV_BSIZE, (struct disklabel *)NULL); bp->b_dev = sav_b_dev; if (fdc->flags & FDC_STAT_VALID) { printf( " (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n", fdc->status[0], NE7_ST0BITS, fdc->status[1], NE7_ST1BITS, fdc->status[2], NE7_ST2BITS, fdc->status[3], fdc->status[4], fdc->status[5]); } else printf(" (No status)\n"); } bp->b_flags |= B_ERROR; bp->b_error = EIO; bp->b_resid += bp->b_bcount - fdc->fd->skip; fdc->bp = NULL; fdc->fd->skip = 0; devstat_end_transaction_buf(&fdc->fd->device_stats, bp); biodone(bp); fdc->state = FINDWORK; fdc->flags |= FDC_NEEDS_RESET; fdc->fd = (fd_p) 0; fdc->fdu = -1; return (1); } fdc->retry++; return (1); } static int fdformat(dev, finfo, p) dev_t dev; struct fd_formb *finfo; struct proc *p; { fdu_t fdu; fd_p fd; struct buf *bp; int rv = 0, s; size_t fdblk; fdu = FDUNIT(minor(dev)); fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; /* set up a buffer header for fdstrategy() */ bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); if(bp == 0) return ENOBUFS; /* * keep the process from being swapped */ PHOLD(p); bzero((void *)bp, sizeof(struct buf)); BUF_LOCKINIT(bp); BUF_LOCK(bp, LK_EXCLUSIVE); bp->b_flags = B_PHYS | B_FORMAT; /* * calculate a fake blkno, so fdstrategy() would initiate a * seek to the requested cylinder */ bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; bp->b_data = (caddr_t)finfo; /* now do the format */ bp->b_dev = dev; BUF_STRATEGY(bp, 0); /* ...and wait for it to complete */ s = splbio(); while(!(bp->b_flags & B_DONE)) { rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz); if (rv == EWOULDBLOCK) break; } splx(s); if (rv == EWOULDBLOCK) { /* timed out */ rv = EIO; biodone(bp); } if (bp->b_flags & B_ERROR) rv = bp->b_error; /* * allow the process to be swapped */ PRELE(p); BUF_UNLOCK(bp); BUF_LOCKFREE(bp); free(bp, M_TEMP); return rv; } /* * TODO: don't allocate buffer on stack. */ static int fdioctl(dev, cmd, addr, flag, p) dev_t dev; u_long cmd; caddr_t addr; int flag; struct proc *p; { fdu_t fdu = FDUNIT(minor(dev)); fd_p fd = devclass_get_softc(fd_devclass, fdu); size_t fdblk; struct fd_type *fdt; struct disklabel *dl; char buffer[DEV_BSIZE]; int error = 0; fdblk = 128 << fd->ft->secsize; #ifdef PC98 pc98_fd_check_ready(fdu); #endif switch (cmd) { case DIOCGDINFO: bzero(buffer, sizeof (buffer)); dl = (struct disklabel *)buffer; dl->d_secsize = fdblk; fdt = fd->ft; dl->d_secpercyl = fdt->size / fdt->tracks; dl->d_type = DTYPE_FLOPPY; if (readdisklabel(dkmodpart(dev, RAW_PART), dl) == NULL) error = 0; else error = EINVAL; *(struct disklabel *)addr = *dl; break; case DIOCSDINFO: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWLABEL: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWDINFO: if ((flag & FWRITE) == 0) { error = EBADF; break; } dl = (struct disklabel *)addr; if ((error = setdisklabel((struct disklabel *)buffer, dl, (u_long)0)) != 0) break; error = writedisklabel(dev, (struct disklabel *)buffer); break; case FD_FORM: if ((flag & FWRITE) == 0) error = EBADF; /* must be opened for writing */ else if (((struct fd_formb *)addr)->format_version != FD_FORMAT_VERSION) error = EINVAL; /* wrong version of formatting prog */ else error = fdformat(dev, (struct fd_formb *)addr, p); break; case FD_GTYPE: /* get drive type */ *(struct fd_type *)addr = *fd->ft; break; case FD_STYPE: /* set drive type */ /* this is considered harmful; only allow for superuser */ if (suser(p) != 0) return EPERM; *fd->ft = *(struct fd_type *)addr; break; case FD_GOPTS: /* get drive options */ *(int *)addr = fd->options; break; case FD_SOPTS: /* set drive options */ fd->options = *(int *)addr; break; default: error = ENOTTY; break; } return (error); } /* * Hello emacs, these are the * Local Variables: * c-indent-level: 8 * c-continued-statement-offset: 8 * c-continued-brace-offset: 0 * c-brace-offset: -8 * c-brace-imaginary-offset: 0 * c-argdecl-indent: 8 * c-label-offset: -8 * c++-hanging-braces: 1 * c++-access-specifier-offset: -8 * c++-empty-arglist-indent: 8 * c++-friend-offset: 0 * End: */ Index: head/sys/pc98/pc98/fd.c =================================================================== --- head/sys/pc98/pc98/fd.c (revision 54072) +++ head/sys/pc98/pc98/fd.c (revision 54073) @@ -1,2774 +1,2775 @@ /* * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Don Ahn. * * Libretto PCMCIA floppy support by David Horwitt (dhorwitt@ucsd.edu) * aided by the Linux floppy driver modifications from David Bateman * (dbateman@eng.uts.edu.au). * * Copyright (c) 1993, 1994 by * jc@irbs.UUCP (John Capo) * vak@zebub.msk.su (Serge Vakulenko) * ache@astral.msk.su (Andrew A. Chernov) * * Copyright (c) 1993, 1994, 1995 by * joerg_wunsch@uriah.sax.de (Joerg Wunsch) * dufault@hda.com (Peter Dufault) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 * $FreeBSD$ * */ #include "opt_fdc.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PC98 #include #include #include #include #include #else #include #include #include #include #endif #ifdef FDC_YE #undef FDC_YE #warning "fix FDC_YE! - newbus casualty" #endif /* misuse a flag to identify format operation */ #define B_FORMAT B_XXX /* configuration flags */ #define FDC_PRETEND_D0 (1 << 0) /* pretend drive 0 to be there */ #ifdef FDC_YE #define FDC_IS_PCMCIA (1 << 1) /* if successful probe, then it's a PCMCIA device */ #endif /* internally used only, not really from CMOS: */ #define RTCFDT_144M_PRETENDED 0x1000 /* error returns for fd_cmd() */ #define FD_FAILED -1 #define FD_NOT_VALID -2 #define FDC_ERRMAX 100 /* do not log more */ #ifdef PC98 #define NUMTYPES 12 #define NUMDENS NUMTYPES #else #define NUMTYPES 14 #define NUMDENS (NUMTYPES - 6) #endif /* These defines (-1) must match index for fd_types */ #define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ #define FD_1720 1 #define FD_1480 2 #define FD_1440 3 #define FD_1200 4 #define FD_820 5 #define FD_800 6 #define FD_720 7 #define FD_360 8 #ifdef PC98 #define FD_640 9 #define FD_1232 10 #define FD_1280 11 #define FD_1476 12 #define FDT_NONE 0 /* none present */ #define FDT_12M 1 /* 1M/640K FDD */ #define FDT_144M 2 /* 1.44M/1M/640K FDD */ #else #define FD_1480in5_25 9 #define FD_1440in5_25 10 #define FD_820in5_25 11 #define FD_800in5_25 12 #define FD_720in5_25 13 #define FD_360in5_25 14 #endif static struct fd_type fd_types[NUMTYPES] = { #ifdef PC98 { 21,2,0xFF,0x04,82,3444,1,2,2,0x0C,2 }, /* 1.72M in 3mode */ { 18,2,0xFF,0x1B,82,2952,1,2,2,0x54,1 }, /* 1.48M in 3mode */ { 18,2,0xFF,0x1B,80,2880,1,2,2,0x54,1 }, /* 1.44M in 3mode */ { 15,2,0xFF,0x1B,80,2400,1,0,2,0x54,1 }, /* 1.2M */ { 10,2,0xFF,0x10,82,1640,1,1,2,0x30,1 }, /* 820K */ { 10,2,0xFF,0x10,80,1600,1,1,2,0x30,1 }, /* 800K */ { 9,2,0xFF,0x20,80,1440,1,1,2,0x50,1 }, /* 720K */ { 9,2,0xFF,0x20,40, 720,2,1,2,0x50,1 }, /* 360K */ { 8,2,0xFF,0x2A,80,1280,1,1,2,0x50,1 }, /* 640K */ { 8,3,0xFF,0x35,77,1232,1,0,2,0x74,1 }, /* 1.23M 1024/sec */ { 8,3,0xFF,0x35,80,1280,1,0,2,0x74,1 }, /* 1.28M 1024/sec */ { 9,3,0xFF,0x35,82,1476,1,0,2,0x47,1 }, /* 1.48M 1024/sec 9sec */ #if 0 { 10,3,0xFF,0x1B,82,1640,1,2,2,0x54,1 }, /* 1.64M in 3mode - Reserve */ #endif #else { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */ { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */ { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */ { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */ { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */ { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */ { 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */ { 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */ { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */ { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */ { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */ { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */ { 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */ { 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */ #endif }; #ifdef PC98 #define DRVS_PER_CTLR 4 /* 4 floppies */ #else #define DRVS_PER_CTLR 2 /* 2 floppies */ #endif /***********************************************************************\ * Per controller structure. * \***********************************************************************/ static devclass_t fdc_devclass; /***********************************************************************\ * Per drive structure. * * N per controller (DRVS_PER_CTLR) * \***********************************************************************/ struct fd_data { struct fdc_data *fdc; /* pointer to controller structure */ int fdsu; /* this units number on this controller */ int type; /* Drive type (FD_1440...) */ struct fd_type *ft; /* pointer to the type descriptor */ int flags; #define FD_OPEN 0x01 /* it's open */ #define FD_ACTIVE 0x02 /* it's active */ #define FD_MOTOR 0x04 /* motor should be on */ #define FD_MOTOR_WAIT 0x08 /* motor coming up */ int skip; int hddrv; #define FD_NO_TRACK -2 int track; /* where we think the head is */ int options; /* user configurable options, see ioctl_fd.h */ struct callout_handle toffhandle; struct callout_handle tohandle; struct devstat device_stats; #ifdef PC98 int pc98_trans; #endif device_t dev; fdu_t fdu; }; static devclass_t fd_devclass; #ifdef EPSON_NRDISK typedef unsigned int nrd_t; #define P_NRD_ADDRH 0xc24 #define P_NRD_ADDRM 0xc22 #define P_NRD_ADDRL 0xc20 #define P_NRD_CHECK 0xc20 #define P_NRD_DATA 0xc26 #define P_NRD_LED 0xc36 #define B_NRD_CHK 0x80 #define B_NRD_LED 0x40 #define A_NRD_INFO 0x2 #define A_NRD_BASE 0x400 #define NRD_STATUS 0x0 #define NRD_ST0_HD 0x04 static fdu_t nrdu=-1; static int nrdsec=0; static nrd_t nrdblkn=0; static nrd_t nrdaddr=0x0; #define nrd_check_ready() ({ \ (epson_inb(P_NRD_CHECK) & B_NRD_CHK) ? 0 : 1; \ }) #define nrd_LED_on() epson_outb(P_NRD_LED, B_NRD_LED) #define nrd_LED_off() epson_outb(P_NRD_LED, ~B_NRD_LED) #define nrd_trac() ((int)(nrd_info(nrdaddr) & 0xff)) #define nrd_head() ((int)((nrd_info(nrdaddr) >> 8) & 0xff)) #define nrd_sec() ((int)(nrd_info(nrdaddr + 2) & 0xff)) #define nrd_secsize() ((int)((nrd_info(A_NRD_INFO) >> 8) & 0xff)) #define nrd_addrset(p) nrd_addr((nrd_t)((nrd_t)p+A_NRD_BASE)) static inline void nrd_addr(addr) nrd_t addr; { epson_outb(P_NRD_ADDRH, (u_char)((addr >> 16) & 0x1f)); epson_outb(P_NRD_ADDRM, (u_char)((addr >> 8) & 0xff)); epson_outb(P_NRD_ADDRL, (u_char)(addr & 0xff)); } static inline u_short nrd_info(addr) nrd_t addr; { u_short tmp; nrd_addr(addr); outb(0x43f, 0x42); tmp = (short)inw(P_NRD_DATA); outb(0x43f, 0x40); return ((u_short)tmp); } #endif /* EPSON_NRDISK */ /***********************************************************************\ * Throughout this file the following conventions will be used: * * fd is a pointer to the fd_data struct for the drive in question * * fdc is a pointer to the fdc_data struct for the controller * * fdu is the floppy drive unit number * * fdcu is the floppy controller unit number * * fdsu is the floppy drive unit number on that controller. (sub-unit) * \***********************************************************************/ #ifdef FDC_YE #include "card.h" static int yeattach(struct isa_device *); #endif /* needed for ft driver, thus exported */ int in_fdc(struct fdc_data *); int out_fdc(struct fdc_data *, int); /* internal functions */ static void fdc_add_device(device_t, const char *, int); static void fdc_intr(void *); static void set_motor(struct fdc_data *, int, int); # define TURNON 1 # define TURNOFF 0 static timeout_t fd_turnoff; static timeout_t fd_motor_on; static void fd_turnon(struct fd_data *); static void fdc_reset(fdc_p); static int fd_in(struct fdc_data *, int *); static void fdstart(struct fdc_data *); static timeout_t fd_iotimeout; static timeout_t fd_pseudointr; static int fdstate(struct fdc_data *); static int retrier(struct fdc_data *); static int fdformat(dev_t, struct fd_formb *, struct proc *); static int enable_fifo(fdc_p fdc); static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */ #define DEVIDLE 0 #define FINDWORK 1 #define DOSEEK 2 #define SEEKCOMPLETE 3 #define IOCOMPLETE 4 #define RECALCOMPLETE 5 #define STARTRECAL 6 #define RESETCTLR 7 #define SEEKWAIT 8 #define RECALWAIT 9 #define MOTORWAIT 10 #define IOTIMEDOUT 11 #define RESETCOMPLETE 12 #ifdef FDC_YE #define PIOREAD 13 #endif #ifdef FDC_DEBUG static char const * const fdstates[] = { "DEVIDLE", "FINDWORK", "DOSEEK", "SEEKCOMPLETE", "IOCOMPLETE", "RECALCOMPLETE", "STARTRECAL", "RESETCTLR", "SEEKWAIT", "RECALWAIT", "MOTORWAIT", "IOTIMEDOUT", "RESETCOMPLETE", #ifdef FDC_YE "PIOREAD", #endif }; /* CAUTION: fd_debug causes huge amounts of logging output */ static int volatile fd_debug = 0; #define TRACE0(arg) if(fd_debug) printf(arg) #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) #else /* FDC_DEBUG */ #define TRACE0(arg) #define TRACE1(arg1, arg2) #endif /* FDC_DEBUG */ static void fdout_wr(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->portt, fdc->porth, FDOUT+fdc->port_off, v); } static u_int8_t fdsts_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDSTS+fdc->port_off); } static void fddata_wr(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off, v); } static u_int8_t fddata_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off); } #ifndef PC98 static void fdctl_wr(fdc_p fdc, u_int8_t v) { if (fdc->flags & FDC_ISPNP) bus_space_write_1(fdc->ctlt, fdc->ctlh, 0, v); else bus_space_write_1(fdc->portt, fdc->porth, FDCTL, v); } #endif #if 0 static u_int8_t fdin_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDIN); } #endif #ifdef FDC_YE #if NCARD > 0 #include #include #include #include #include /* * PC-Card (PCMCIA) specific code. */ static int yeinit(struct pccard_devinfo *); /* init device */ static void yeunload(struct pccard_devinfo *); /* Disable driver */ static int yeintr(struct pccard_devinfo *); /* Interrupt handler */ PCCARD_MODULE(fdc, yeinit, yeunload, yeintr, 0, bio_imask); /* * this is the secret PIO data port (offset from base) */ #define FDC_YE_DATAPORT 6 /* * Initialize the device - called from Slot manager. */ static int yeinit(struct pccard_devinfo *devi) { fdc_p fdc = &fdc_data[devi->isahd.id_unit]; fdc->baseport = devi->isahd.id_iobase; /* * reset controller */ fdout_wr(fdc, 0); DELAY(100); fdout_wr(fdc, FDO_FRST); /* * wire into system */ if (yeattach(&devi->isahd) == 0) return(ENXIO); return(0); } /* * yeunload - unload the driver and clear the table. * XXX TODO: * This is usually called when the card is ejected, but * can be caused by a modunload of a controller driver. * The idea is to reset the driver's view of the device * and ensure that any driver entry points such as * read and write do not hang. */ static void yeunload(struct pccard_devinfo *devi) { if (fd_data[devi->isahd.id_unit].type == NO_TYPE) return; /* * this prevents Fdopen() and fdstrategy() from attempting * to access unloaded controller */ fd_data[devi->isahd.id_unit].type = NO_TYPE; printf("fdc%d: unload\n", devi->isahd.id_unit); } /* * yeintr - Shared interrupt called from * front end of PC-Card handler. */ static int yeintr(struct pccard_devinfo *devi) { fdintr((fdcu_t)devi->isahd.id_unit); return(1); } #endif /* NCARD > 0 */ #endif /* FDC_YE */ static d_open_t Fdopen; /* NOTE, not fdopen */ static d_close_t fdclose; static d_ioctl_t fdioctl; static d_strategy_t fdstrategy; #define CDEV_MAJOR 9 #define BDEV_MAJOR 2 static struct cdevsw fd_cdevsw = { /* open */ Fdopen, /* close */ fdclose, /* read */ physread, /* write */ physwrite, /* ioctl */ fdioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ fdstrategy, /* name */ "fd", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ D_DISK, /* bmaj */ BDEV_MAJOR }; static int fdc_err(struct fdc_data *fdc, const char *s) { fdc->fdc_errs++; if (s) { if (fdc->fdc_errs < FDC_ERRMAX) { device_print_prettyname(fdc->fdc_dev); printf("%s", s); } else if (fdc->fdc_errs == FDC_ERRMAX) { device_print_prettyname(fdc->fdc_dev); printf("too many errors, not logging any more\n"); } } return FD_FAILED; } /* * fd_cmd: Send a command to the chip. Takes a varargs with this structure: * Unit number, * # of output bytes, output bytes as ints ..., * # of input bytes, input bytes as ints ... */ static int fd_cmd(struct fdc_data *fdc, int n_out, ...) { u_char cmd; int n_in; int n; va_list ap; va_start(ap, n_out); cmd = (u_char)(va_arg(ap, int)); va_end(ap); va_start(ap, n_out); for (n = 0; n < n_out; n++) { if (out_fdc(fdc, va_arg(ap, int)) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %x failed at out byte %d of %d\n", cmd, n + 1, n_out); return fdc_err(fdc, msg); } } n_in = va_arg(ap, int); for (n = 0; n < n_in; n++) { int *ptr = va_arg(ap, int *); if (fd_in(fdc, ptr) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %02x failed at in byte %d of %d\n", cmd, n + 1, n_in); return fdc_err(fdc, msg); } } return 0; } static int enable_fifo(fdc_p fdc) { int i, j; if ((fdc->flags & FDC_HAS_FIFO) == 0) { /* * XXX: * Cannot use fd_cmd the normal way here, since * this might be an invalid command. Thus we send the * first byte, and check for an early turn of data directon. */ if (out_fdc(fdc, I8207X_CONFIGURE) < 0) return fdc_err(fdc, "Enable FIFO failed\n"); /* If command is invalid, return */ j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO | NE7_RQM)) != NE7_RQM && j-- > 0) if (i == (NE7_DIO | NE7_RQM)) { fdc_reset(fdc); return FD_FAILED; } if (j<0 || fd_cmd(fdc, 3, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) { fdc_reset(fdc); return fdc_err(fdc, "Enable FIFO failed\n"); } fdc->flags |= FDC_HAS_FIFO; return 0; } if (fd_cmd(fdc, 4, I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) return fdc_err(fdc, "Re-enable FIFO failed\n"); return 0; } static int fd_sense_drive_status(fdc_p fdc, int *st3p) { int st3; if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) { return fdc_err(fdc, "Sense Drive Status failed\n"); } if (st3p) *st3p = st3; return 0; } static int fd_sense_int(fdc_p fdc, int *st0p, int *cylp) { int cyl, st0, ret; #ifdef EPSON_NRDISK if (fdc->fdu == nrdu) { if (fdc->fd->track >= 0) nrdaddr = (fdc->fd->track + 1) * 8; else nrdaddr = 0x0; *st0p = nrd_head() ? NRD_ST0_HD : NRD_STATUS; *cylp = nrd_trac(); } else { #endif /* EPSON_NRDISK */ ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0); if (ret) { (void)fdc_err(fdc, "sense intr err reading stat reg 0\n"); return ret; } if (st0p) *st0p = st0; if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) { /* * There doesn't seem to have been an interrupt. */ return FD_NOT_VALID; } if (fd_in(fdc, &cyl) < 0) { return fdc_err(fdc, "can't get cyl num\n"); } if (cylp) *cylp = cyl; #ifdef EPSON_NRDISK } #endif /* EPSON_NRDISK */ return 0; } static int fd_read_status(fdc_p fdc, int fdsu) { int i, ret; for (i = 0; i < 7; i++) { /* * XXX types are poorly chosen. Only bytes can by read * from the hardware, but fdc->status[] wants u_ints and * fd_in() gives ints. */ int status; #ifdef EPSON_NRDISK if (fdc->fdu == nrdu) { switch (i) { case 0: fdc->status[i] = nrd_head() ? NRD_ST0_HD : NRD_STATUS; break; case 1: fdc->status[i] = NRD_STATUS; break; case 2: fdc->status[i] = NRD_STATUS; break; case 3: fdc->status[i] = nrd_trac(); break; case 4: fdc->status[i] = nrd_head(); break; case 5: fdc->status[i] = nrdsec; break; case 6: fdc->status[i] = nrd_secsize(); break; } ret = 0; } else { #endif /* EPSON_NRDISK */ ret = fd_in(fdc, &status); fdc->status[i] = status; if (ret != 0) break; #ifdef EPSON_NRDISK } #endif /* EPSON_NRDISK */ } if (ret == 0) fdc->flags |= FDC_STAT_VALID; else fdc->flags &= ~FDC_STAT_VALID; return ret; } /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ #ifdef PC98 static int pc98_trans = 0; /* 0 : HD , 1 : DD , 2 : 1.44 */ static int pc98_trans_prev = 0; static void set_density(fdc_p fdc) { /* always motor on */ outb(IO_FDPORT, (pc98_trans != 1 ? FDP_FDDEXC : 0) | FDP_PORTEXC); DELAY(100); fdout_wr(fdc, FDO_RST | FDO_DMAE); /* in the case of note W, always inhibit 100ms timer */ } static int pc98_fd_check_ready(fdu_t fdu) { fd_p fd = devclass_get_softc(fd_devclass, fdu); struct fdc_data *fdc = fd->fdc; int retry = 0; #ifdef EPSON_NRDISK if (fdu == nrdu) { if (nrd_check_ready()) return 0; else return -1; } #endif while (retry++ < 30000) { set_motor(fdc, fd->fdsu, TURNON); out_fdc(fdc, NE7CMD_SENSED); /* Sense Drive Status */ DELAY(100); out_fdc(fdc, fdu); /* Drive number */ DELAY(100); if ((in_fdc(fdc) & NE7_ST3_RD)){ fdout_wr(fdc, FDO_DMAE | FDO_MTON); DELAY(10); return 0; } } return -1; } #endif static struct isa_pnp_id fdc_ids[] = { {0x0007d041, "PC standard floppy disk controller"}, /* PNP0700 */ {0x0107d041, "Standard floppy controller supporting MS Device Bay Spec"}, /* PNP0701 */ {0} }; /* * fdc controller section. */ static int fdc_probe(device_t dev) { int error, ispnp, ic_type; struct fdc_data *fdc; /* Check pnp ids */ error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids); if (error == ENXIO) return ENXIO; ispnp = (error == 0); fdc = device_get_softc(dev); bzero(fdc, sizeof *fdc); fdc->fdc_dev = dev; fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0; fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0; fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, &fdc->rid_ioport, 0ul, ~0ul, ispnp ? 1 : IO_FDCSIZE, RF_ACTIVE); if (fdc->res_ioport == 0) { device_printf(dev, "cannot reserve I/O port range\n"); error = ENXIO; goto out; } fdc->portt = rman_get_bustag(fdc->res_ioport); fdc->porth = rman_get_bushandle(fdc->res_ioport); #ifndef PC98 if (ispnp) { /* * Some bios' report the device at 0x3f2-0x3f5,0x3f7 * and some at 0x3f0-0x3f5,0x3f7. We detect the former * by checking the size and adjust the port address * accordingly. */ if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4) fdc->port_off = -2; fdc->flags |= FDC_ISPNP; fdc->rid_ctl = 1; fdc->res_ctl = bus_alloc_resource(dev, SYS_RES_IOPORT, &fdc->rid_ctl, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_ctl == 0) { device_printf(dev, "cannot reserve I/O port range\n"); error = ENXIO; goto out; } fdc->ctlt = rman_get_bustag(fdc->res_ctl); fdc->ctlh = rman_get_bushandle(fdc->res_ctl); } #endif fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &fdc->rid_irq, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_irq == 0) { device_printf(dev, "cannot reserve interrupt line\n"); error = ENXIO; goto out; } fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ, &fdc->rid_drq, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_drq == 0) { device_printf(dev, "cannot reserve DMA request line\n"); error = ENXIO; goto out; } fdc->dmachan = fdc->res_drq->r_start; error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq, INTR_TYPE_BIO, fdc_intr, fdc, &fdc->fdc_intr); #ifndef PC98 /* First - lets reset the floppy controller */ fdout_wr(fdc, 0); DELAY(100); fdout_wr(fdc, FDO_FRST); #endif /* see if it can handle a command */ #ifdef PC98 if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0), 0)) { error = ENXIO; goto out; } #else if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0)) { error = ENXIO; goto out; } #endif #ifndef PC98 if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { ic_type = (u_char)ic_type; switch (ic_type) { case 0x80: device_set_desc(dev, "NEC 765 or clone"); fdc->fdct = FDC_NE765; break; case 0x81: device_set_desc(dev, "Intel 82077 or clone"); fdc->fdct = FDC_I82077; break; case 0x90: device_set_desc(dev, "NEC 72065B or clone"); fdc->fdct = FDC_NE72065; break; default: device_set_desc(dev, "generic floppy controller"); fdc->fdct = FDC_UNKNOWN; break; } } #endif #ifdef FDC_YE /* * don't succeed on probe; wait * for PCCARD subsystem to do it */ if (dev->id_flags & FDC_IS_PCMCIA) return(0); #endif return (0); out: if (fdc->fdc_intr) BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq, fdc->fdc_intr); if (fdc->res_irq != 0) { bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq, fdc->res_irq); bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq, fdc->res_irq); } if (fdc->res_ctl != 0) { bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, fdc->res_ctl); bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, fdc->res_ctl); } if (fdc->res_ioport != 0) { bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, fdc->res_ioport); bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, fdc->res_ioport); } if (fdc->res_drq != 0) { bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq, fdc->res_drq); bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq, fdc->res_drq); } return (error); } /* * Aped dfr@freebsd.org's isa_add_device(). */ static void fdc_add_device(device_t dev, const char *name, int unit) { int disabled, *ivar; device_t child; ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT); if (ivar == 0) return; if (resource_int_value(name, unit, "drive", ivar) != 0) *ivar = 0; - child = device_add_child(dev, name, unit, ivar); + child = device_add_child(dev, name, unit); + device_set_ivars(child, ivar); if (child == 0) return; if (resource_int_value(name, unit, "disabled", &disabled) == 0 && disabled != 0) device_disable(child); } static int fdc_attach(device_t dev) { struct fdc_data *fdc = device_get_softc(dev); fdcu_t fdcu = device_get_unit(dev); int i; for (i = resource_query_string(-1, "at", device_get_nameunit(dev)); i != -1; i = resource_query_string(i, "at", device_get_nameunit(dev))) fdc_add_device(dev, resource_query_name(i), resource_query_unit(i)); fdc->fdcu = fdcu; fdc->flags |= FDC_ATTACHED; /* Acquire the DMA channel forever, The driver will do the rest */ /* XXX should integrate with rman */ isa_dma_acquire(fdc->dmachan); isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */); fdc->state = DEVIDLE; #ifdef PC98 /* reset controller, turn motor off, clear fdout mirror reg */ fdc_reset(fdc); #else /* reset controller, turn motor off, clear fdout mirror reg */ fdout_wr(fdc, ((fdc->fdout = 0))); #endif bufq_init(&fdc->head); /* * Probe and attach any children as were configured above. */ return (bus_generic_attach(dev)); } static int fdc_print_child(device_t me, device_t child) { int retval = 0; retval += bus_print_child_header(me, child); retval += printf(" on %s drive %d\n", device_get_nameunit(me), *(int *)device_get_ivars(child)); return (retval); } static device_method_t fdc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fdc_probe), DEVMETHOD(device_attach, fdc_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, fdc_print_child), /* Our children never use any other bus interface methods. */ { 0, 0 } }; static driver_t fdc_driver = { "fdc", fdc_methods, sizeof(struct fdc_data) }; DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0); /******************************************************************/ /* * devices attached to the controller section. */ static int fd_probe(device_t dev) { int i; u_int fdt, st0, st3; struct fd_data *fd; struct fdc_data *fdc; fdsu_t fdsu; static int fd_fifo = 0; fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */ fd = device_get_softc(dev); fdc = device_get_softc(device_get_parent(dev)); bzero(fd, sizeof *fd); fd->dev = dev; fd->fdc = fdc; fd->fdsu = fdsu; fd->fdu = device_get_unit(dev); #ifdef PC98 /* look up what bios thinks we have */ switch (fd->fdu) { case 0: case 1: case 2: case 3: if ((PC98_SYSTEM_PARAMETER(0x5ae) >> fd->fdu) & 0x01) fdt = FDT_144M; #ifdef EPSON_NRDISK else if ((PC98_SYSTEM_PARAMETER(0x55c) >> fd->fdu) & 0x01) { fdt = FDT_12M; switch (epson_machine_id) { case 0x20: case 0x27: if ((PC98_SYSTEM_PARAMETER(0x488) >> fd->fdu) & 0x01) { if (nrd_check_ready()) { nrd_LED_on(); nrdu = fd->fdu; } else { fdt = FDT_NONE; } } } } #else /* !EPSON_NRDISK */ else if ((PC98_SYSTEM_PARAMETER(0x55c) >> fd->fdu) & 0x01) { fdt = FDT_12M; switch (epson_machine_id) { case 0x20: case 0x27: if ((PC98_SYSTEM_PARAMETER(0x488) >> fd->fdu) & 0x01) fdt = FDT_NONE; } } #endif /* EPSON_NRDISK */ else fdt = FDT_NONE; break; default: fdt = FDT_NONE; break; } #else #ifdef __i386__ /* look up what bios thinks we have */ switch (fd->fdu) { case 0: if (device_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0) fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED; else fdt = (rtcin(RTC_FDISKETTE) & 0xf0); break; case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); break; default: fdt = RTCFDT_NONE; break; } #else fdt = RTCFDT_144M; /* XXX probably */ #endif #endif /* is there a unit? */ #ifdef PC98 if (fdt == FDT_NONE) return (ENXIO); #else if (fdt == RTCFDT_NONE) return (ENXIO); #endif #ifndef PC98 /* select it */ set_motor(fdc, fdsu, TURNON); DELAY(1000000); /* 1 sec */ /* XXX This doesn't work before the first set_motor() */ if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN && enable_fifo(fdc) == 0) { device_print_prettyname(device_get_parent(dev)); printf("FIFO enabled, %d bytes threshold\n", fifo_threshold); } fd_fifo = 1; if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* if at track 0, first seek inwards */ /* seek some steps: */ fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0); DELAY(300000); /* ...wait a moment... */ fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ } /* If we're at track 0 first seek inwards. */ if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* Seek some steps... */ if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { /* ...wait a moment... */ DELAY(300000); /* make ctrlr happy: */ fd_sense_int(fdc, 0, 0); } } for (i = 0; i < 2; i++) { /* * we must recalibrate twice, just in case the * heads have been beyond cylinder 76, since most * FDCs still barf when attempting to recalibrate * more than 77 steps */ /* go back to 0: */ if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) { /* a second being enough for full stroke seek*/ DELAY(i == 0 ? 1000000 : 300000); /* anything responding? */ if (fd_sense_int(fdc, &st0, 0) == 0 && (st0 & NE7_ST0_EC) == 0) break; /* already probed succesfully */ } } set_motor(fdc, fdsu, TURNOFF); if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ return (ENXIO); #endif /* PC98 */ fd->track = FD_NO_TRACK; fd->fdc = fdc; fd->fdsu = fdsu; fd->options = 0; callout_handle_init(&fd->toffhandle); callout_handle_init(&fd->tohandle); #ifdef PC98 switch (fdt) { case FDT_144M: /* Check 3mode I/F */ fd->pc98_trans = 0; outb(0x4be, (fd->fdu << 5) | 0x10); if (!(inb(0x4be) & 0x01)) { device_set_desc(dev, "1.44M FDD"); fd->type = FD_1440; break; } printf("Warning: can't control 3mode I/F, " "fallback to 2mode.\n" "fd%d: ", fd->fdu); /* FALLTHROUGH */ case FDT_12M: #ifdef EPSON_NRDISK if (fd->fdu == nrdu) { device_set_desc(dev, "EPSON RAM DRIVE"); nrd_LED_off(); } else #endif device_set_desc(dev, "1M/640K FDD"); fd->type = FD_1200; fd->pc98_trans = 0; break; default: return (ENXIO); } #else switch (fdt) { case RTCFDT_12M: device_set_desc(dev, "1200-KB 5.25\" drive"); fd->type = FD_1200; break; case RTCFDT_144M | RTCFDT_144M_PRETENDED: device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive"); fdt = RTCFDT_144M; fd->type = FD_1440; case RTCFDT_144M: device_set_desc(dev, "1440-KB 3.5\" drive"); fd->type = FD_1440; break; case RTCFDT_288M: case RTCFDT_288M_1: device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); fd->type = FD_1440; break; case RTCFDT_360K: device_set_desc(dev, "360-KB 5.25\" drive"); fd->type = FD_360; break; case RTCFDT_720K: printf("720-KB 3.5\" drive"); fd->type = FD_720; break; default: return (ENXIO); } #endif return (0); } static int fd_attach(device_t dev) { struct fd_data *fd; #if 0 int i; int mynor; int typemynor; int typesize; #endif fd = device_get_softc(dev); cdevsw_add(&fd_cdevsw); /* XXX fill in devices */ make_dev(&fd_cdevsw, (fd->fdu << 6), UID_ROOT, GID_OPERATOR, 0640, "rfd%d", fd->fdu); #if 0 /* Other make_dev() go here. */ #endif /* * Export the drive to the devstat interface. */ devstat_add_entry(&fd->device_stats, device_get_name(dev), device_get_unit(dev), 512, DEVSTAT_NO_ORDERED_TAGS, DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, DEVSTAT_PRIORITY_FD); return (0); } static device_method_t fd_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fd_probe), DEVMETHOD(device_attach, fd_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX */ DEVMETHOD(device_resume, bus_generic_resume), /* XXX */ { 0, 0 } }; static driver_t fd_driver = { "fd", fd_methods, sizeof(struct fd_data) }; DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, 0, 0); /******************************************************************/ #ifdef FDC_YE /* * this is a subset of fdattach() optimized for the Y-E Data * PCMCIA floppy drive. */ static int yeattach(struct isa_device *dev) { fdcu_t fdcu = dev->id_unit; fdc_p fdc = fdc_data + fdcu; fdsu_t fdsu = 0; /* assume 1 drive per YE controller */ fdu_t fdu; fd_p fd; int st0, st3, i; fdc->fdcu = fdcu; /* * the FDC_PCMCIA flag is used to to indicate special PIO is used * instead of DMA */ fdc->flags = FDC_ATTACHED|FDC_PCMCIA; fdc->state = DEVIDLE; /* reset controller, turn motor off, clear fdout mirror reg */ fdout_wr(fdc, ((fdc->fdout = 0))); bufq_init(&fdc->head); /* * assume 2 drives/ "normal" controller */ fdu = fdcu * 2; if (fdu >= NFD) { printf("fdu %d >= NFD\n",fdu); return(0); }; fd = &fd_data[fdu]; set_motor(fdcu, fdsu, TURNON); DELAY(1000000); /* 1 sec */ fdc->fdct = FDC_NE765; if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* if at track 0, first seek inwards */ /* seek some steps: */ (void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0); DELAY(300000); /* ...wait a moment... */ (void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ } /* If we're at track 0 first seek inwards. */ if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* Seek some steps... */ if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { /* ...wait a moment... */ DELAY(300000); /* make ctrlr happy: */ (void)fd_sense_int(fdc, 0, 0); } } for(i = 0; i < 2; i++) { /* * we must recalibrate twice, just in case the * heads have been beyond cylinder 76, since most * FDCs still barf when attempting to recalibrate * more than 77 steps */ /* go back to 0: */ if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) { /* a second being enough for full stroke seek*/ DELAY(i == 0? 1000000: 300000); /* anything responding? */ if (fd_sense_int(fdc, &st0, 0) == 0 && (st0 & NE7_ST0_EC) == 0) break; /* already probed succesfully */ } } set_motor(fdcu, fdsu, TURNOFF); if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ return(0); fd->track = FD_NO_TRACK; fd->fdc = fdc; fd->fdsu = fdsu; fd->options = 0; printf("fdc%d: 1.44MB 3.5in PCMCIA\n", fdcu); fd->type = FD_1440; return (1); } #endif /****************************************************************************/ /* motor control stuff */ /* remember to not deselect the drive we're working on */ /****************************************************************************/ static void set_motor(struct fdc_data *fdc, int fdsu, int turnon) { int fdout = fdc->fdout; int needspecify = 0; #ifdef PC98 outb(IO_FDPORT, (pc98_trans != 1 ? FDP_FDDEXC : 0)|FDP_PORTEXC); DELAY(10); fdout = FDO_DMAE|FDO_MTON; #else if(turnon) { fdout &= ~FDO_FDSEL; fdout |= (FDO_MOEN0 << fdsu) + fdsu; } else fdout &= ~(FDO_MOEN0 << fdsu); if(!turnon && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) /* gonna turn off the last drive, put FDC to bed */ fdout &= ~ (FDO_FRST|FDO_FDMAEN); else { /* make sure controller is selected and specified */ if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) needspecify = 1; fdout |= (FDO_FRST|FDO_FDMAEN); } #endif fdout_wr(fdc, fdout); fdc->fdout = fdout; TRACE1("[0x%x->FDOUT]", fdout); if (needspecify) { /* * XXX * special case: since we have just woken up the FDC * from its sleep, we silently assume the command will * be accepted, and do not test for a timeout */ #ifdef PC98 (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0), 0); #else (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); #endif if (fdc->flags & FDC_HAS_FIFO) (void) enable_fifo(fdc); } } static void fd_turnoff(void *xfd) { int s; fd_p fd = xfd; TRACE1("[fd%d: turnoff]", fd->fdu); /* * Don't turn off the motor yet if the drive is active. * XXX shouldn't even schedule turnoff until drive is inactive * and nothing is queued on it. */ if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) { fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); return; } s = splbio(); fd->flags &= ~FD_MOTOR; set_motor(fd->fdc, fd->fdsu, TURNOFF); splx(s); } static void fd_motor_on(void *xfd) { int s; fd_p fd = xfd; s = splbio(); fd->flags &= ~FD_MOTOR_WAIT; if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) { fdc_intr(fd->fdc); } splx(s); } static void fd_turnon(fd_p fd) { if(!(fd->flags & FD_MOTOR)) { fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); set_motor(fd->fdc, fd->fdsu, TURNON); timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */ } } static void fdc_reset(fdc_p fdc) { /* Try a reset, keep motor on */ #ifdef PC98 set_density(fdc); if (pc98_machine_type & M_EPSON_PC98) fdout_wr(fdc, 0xe8); else fdout_wr(fdc, 0xd8); DELAY(200); fdout_wr(fdc, 0x18); DELAY(10); #else fdout_wr(fdc, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); DELAY(100); /* enable FDC, but defer interrupts a moment */ fdout_wr(fdc, fdc->fdout & ~FDO_FDMAEN); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); DELAY(100); fdout_wr(fdc, fdc->fdout); TRACE1("[0x%x->FDOUT]", fdc->fdout); #endif /* XXX after a reset, silently believe the FDC will accept commands */ #ifdef PC98 (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0), 0); #else (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); #endif if (fdc->flags & FDC_HAS_FIFO) (void) enable_fifo(fdc); } /****************************************************************************/ /* fdc in/out */ /****************************************************************************/ int in_fdc(struct fdc_data *fdc) { int i, j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = fddata_rd(fdc); TRACE1("[FDDATA->0x%x]", (unsigned char)i); return(i); #else /* !FDC_DEBUG */ return fddata_rd(fdc); #endif /* FDC_DEBUG */ } /* * fd_in: Like in_fdc, but allows you to see if it worked. */ static int fd_in(struct fdc_data *fdc, int *ptr) { int i, j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = fddata_rd(fdc); TRACE1("[FDDATA->0x%x]", (unsigned char)i); *ptr = i; return 0; #else /* !FDC_DEBUG */ i = fddata_rd(fdc); if (ptr) *ptr = i; return 0; #endif /* FDC_DEBUG */ } int out_fdc(struct fdc_data *fdc, int x) { int i; /* Check that the direction bit is set */ i = 100000; while ((fdsts_rd(fdc) & NE7_DIO) && i-- > 0); if (i <= 0) return fdc_err(fdc, "direction bit not set\n"); /* Check that the floppy controller is ready for a command */ i = 100000; while ((fdsts_rd(fdc) & NE7_RQM) == 0 && i-- > 0); if (i <= 0) return fdc_err(fdc, bootverbose? "output ready timeout\n": 0); /* Send the command and return */ fddata_wr(fdc, x); TRACE1("[0x%x->FDDATA]", x); return (0); } /****************************************************************************/ /* fdopen/fdclose */ /****************************************************************************/ int Fdopen(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); int type = FDTYPE(minor(dev)); fd_p fd; fdc_p fdc; /* check bounds */ if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0) return (ENXIO); fdc = fd->fdc; if ((fdc == NULL) || (fd->type == NO_TYPE)) return (ENXIO); if (type > NUMDENS) return (ENXIO); #ifdef PC98 if (type == 0) type = FD_1200; /* XXX backward compatibility */ else ; /* XXX any types are OK for PC-98 */ if (pc98_fd_check_ready(fdu) == -1) return(EIO); #else if (type == 0) type = fd->type; else { /* * For each type of basic drive, make sure we are trying * to open a type it can do, */ if (type != fd->type) { switch (fd->type) { case FD_360: return (ENXIO); case FD_720: if ( type != FD_820 && type != FD_800 ) return (ENXIO); break; case FD_1200: switch (type) { case FD_1480: type = FD_1480in5_25; break; case FD_1440: type = FD_1440in5_25; break; case FD_820: type = FD_820in5_25; break; case FD_800: type = FD_800in5_25; break; case FD_720: type = FD_720in5_25; break; case FD_360: type = FD_360in5_25; break; default: return(ENXIO); } break; case FD_1440: if ( type != FD_1720 && type != FD_1480 && type != FD_1200 && type != FD_820 && type != FD_800 && type != FD_720 ) return(ENXIO); break; } } } #endif fd->ft = fd_types + type - 1; fd->flags |= FD_OPEN; device_busy(fd->dev); device_busy(fd->fdc->fdc_dev); return 0; } int fdclose(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); struct fd_data *fd; fd = devclass_get_softc(fd_devclass, fdu); fd->flags &= ~FD_OPEN; fd->options &= ~FDOPT_NORETRY; return (0); } /****************************************************************************/ /* fdstrategy */ /****************************************************************************/ void fdstrategy(struct buf *bp) { unsigned nblocks, blknum, cando; int s; fdu_t fdu; fdc_p fdc; fd_p fd; size_t fdblk; fdu = FDUNIT(minor(bp->b_dev)); fd = devclass_get_softc(fd_devclass, fdu); if (fd == 0) panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)", (u_long)major(bp->b_dev), (u_long)minor(bp->b_dev)); fdc = fd->fdc; #ifdef FDC_YE if (fd->type == NO_TYPE) { bp->b_error = ENXIO; bp->b_flags |= B_ERROR; /* * I _refuse_ to use a goto */ biodone(bp); return; }; #endif fdblk = 128 << (fd->ft->secsize); if (!(bp->b_flags & B_FORMAT)) { if (bp->b_blkno < 0) { printf( "fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n", fdu, (u_long)bp->b_blkno, bp->b_bcount); bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } if ((bp->b_bcount % fdblk) != 0) { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } } /* * Set up block calculations. */ if (bp->b_blkno > 20000000) { /* * Reject unreasonably high block number, prevent the * multiplication below from overflowing. */ bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk; nblocks = fd->ft->size; bp->b_resid = 0; if (blknum + (bp->b_bcount / fdblk) > nblocks) { if (blknum <= nblocks) { cando = (nblocks - blknum) * fdblk; bp->b_resid = bp->b_bcount - cando; if (cando == 0) goto bad; /* not actually bad but EOF */ } else { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } } bp->b_pblkno = bp->b_blkno; s = splbio(); bufqdisksort(&fdc->head, bp); untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */ /* Tell devstat we are starting on the transaction */ devstat_start_transaction(&fd->device_stats); fdstart(fdc); splx(s); return; bad: biodone(bp); } /***************************************************************\ * fdstart * * We have just queued something.. if the controller is not busy * * then simulate the case where it has just finished a command * * So that it (the interrupt routine) looks on the queue for more* * work to do and picks up what we just added. * * If the controller is already busy, we need do nothing, as it * * will pick up our work when the present work completes * \***************************************************************/ static void fdstart(struct fdc_data *fdc) { int s; s = splbio(); if(fdc->state == DEVIDLE) { fdc_intr(fdc); } splx(s); } static void fd_iotimeout(void *xfdc) { fdc_p fdc; int s; fdc = xfdc; TRACE1("fd%d[fd_iotimeout()]", fdc->fdu); /* * Due to IBM's brain-dead design, the FDC has a faked ready * signal, hardwired to ready == true. Thus, any command * issued if there's no diskette in the drive will _never_ * complete, and must be aborted by resetting the FDC. * Many thanks, Big Blue! * The FDC must not be reset directly, since that would * interfere with the state machine. Instead, pretend that * the command completed but was invalid. The state machine * will reset the FDC and retry once. */ s = splbio(); fdc->status[0] = NE7_ST0_IC_IV; fdc->flags &= ~FDC_STAT_VALID; fdc->state = IOTIMEDOUT; fdc_intr(fdc); splx(s); } /* just ensure it has the right spl */ static void fd_pseudointr(void *xfdc) { int s; s = splbio(); fdc_intr(xfdc); splx(s); } /***********************************************************************\ * fdintr * * keep calling the state machine until it returns a 0 * * ALWAYS called at SPLBIO * \***********************************************************************/ static void fdc_intr(void *xfdc) { fdc_p fdc = xfdc; while(fdstate(fdc)) ; } #ifdef FDC_YE /* * magic pseudo-DMA initialization for YE FDC. Sets count and * direction */ #define SET_BCDR(wr,cnt,port) outb(port,(((cnt)-1) & 0xff)); \ outb(port+1,((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f))) /* * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy */ static int fdcpio(fdcu_t fdcu, long flags, caddr_t addr, u_int count) { u_char *cptr = (u_char *)addr; fdc_p fdc = &fdc_data[fdcu]; int io = fdc->baseport; if (flags & B_READ) { if (fdc->state != PIOREAD) { fdc->state = PIOREAD; return(0); }; SET_BCDR(0,count,io); insb(io+FDC_YE_DATAPORT,cptr,count); } else { outsb(io+FDC_YE_DATAPORT,cptr,count); SET_BCDR(0,count,io); }; return(1); } #endif /* FDC_YE */ /***********************************************************************\ * The controller state machine. * * if it returns a non zero value, it should be called again immediatly * \***********************************************************************/ static int fdstate(fdc_p fdc) { int read, format, head, i, sec = 0, sectrac, st0, cyl, st3; unsigned blknum = 0, b_cylinder = 0; fdu_t fdu = fdc->fdu; fd_p fd; register struct buf *bp; struct fd_formb *finfo = NULL; size_t fdblk; bp = fdc->bp; if (bp == NULL) { bp = bufq_first(&fdc->head); if (bp != NULL) { bufq_remove(&fdc->head, bp); fdc->bp = bp; } } if (bp == NULL) { /***********************************************\ * nothing left for this controller to do * * Force into the IDLE state, * \***********************************************/ fdc->state = DEVIDLE; if (fdc->fd) { device_print_prettyname(fdc->fdc_dev); printf("unexpected valid fd pointer\n"); fdc->fd = (fd_p) 0; fdc->fdu = -1; } TRACE1("[fdc%d IDLE]", fdc->fdcu); return (0); } fdu = FDUNIT(minor(bp->b_dev)); fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; if (fdc->fd && (fd != fdc->fd)) { device_print_prettyname(fd->dev); printf("confused fd pointers\n"); } read = bp->b_flags & B_READ; format = bp->b_flags & B_FORMAT; if (format) { finfo = (struct fd_formb *)bp->b_data; fd->skip = (char *)&(finfo->fd_formb_cylno(0)) - (char *)finfo; } if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) { blknum = (unsigned) bp->b_pblkno * DEV_BSIZE/fdblk + fd->skip/fdblk; b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); } TRACE1("fd%d", fdu); TRACE1("[%s]", fdstates[fdc->state]); TRACE1("(0x%x)", fd->flags); untimeout(fd_turnoff, fd, fd->toffhandle); fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); switch (fdc->state) { case DEVIDLE: case FINDWORK: /* we have found new work */ fdc->retry = 0; fd->skip = 0; fdc->fd = fd; fdc->fdu = fdu; #ifdef PC98 pc98_trans = fd->ft->trans; if (pc98_trans_prev != pc98_trans) { int i; set_density(fdc); for (i = 0; i < 10; i++) { outb(0x5f, 0); outb(0x5f, 0); } pc98_trans_prev = pc98_trans; } if (pc98_trans != fd->pc98_trans) { if (fd->type == FD_1440) { outb(0x4be, (fdu << 5) | 0x10 | (pc98_trans >> 1)); outb(0x5f, 0); outb(0x5f, 0); } fd->pc98_trans = pc98_trans; } #else fdctl_wr(fdc, fd->ft->trans); #endif TRACE1("[0x%x->FDCTL]", fd->ft->trans); /*******************************************************\ * If the next drive has a motor startup pending, then * * it will start up in its own good time * \*******************************************************/ if(fd->flags & FD_MOTOR_WAIT) { fdc->state = MOTORWAIT; return (0); /* come back later */ } /*******************************************************\ * Maybe if it's not starting, it SHOULD be starting * \*******************************************************/ #ifdef EPSON_NRDISK if (fdu != nrdu) { if (!(fd->flags & FD_MOTOR)) { fdc->state = MOTORWAIT; fd_turnon(fdu); return(0); } else /* at least make sure we are selected */ { set_motor(fdcu, fd->fdsu, TURNON); } } #else /* !EPSON_NRDISK */ if (!(fd->flags & FD_MOTOR)) { fdc->state = MOTORWAIT; fd_turnon(fd); return (0); } else /* at least make sure we are selected */ { set_motor(fdc, fd->fdsu, TURNON); } #endif if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else fdc->state = DOSEEK; break; case DOSEEK: if (b_cylinder == (unsigned)fd->track) { fdc->state = SEEKCOMPLETE; break; } #ifdef PC98 pc98_fd_check_ready(fdu); #endif if (fd_cmd(fdc, 3, NE7CMD_SEEK, fd->fdsu, b_cylinder * fd->ft->steptrac, 0)) { /* * seek command not accepted, looks like * the FDC went off to the Saints... */ fdc->retry = 6; /* try a reset */ return(retrier(fdc)); } fd->track = FD_NO_TRACK; fdc->state = SEEKWAIT; return(0); /* will return later */ case SEEKWAIT: /* allow heads to settle */ timeout(fd_pseudointr, fdc, hz / 16); fdc->state = SEEKCOMPLETE; return(0); /* will return later */ case SEEKCOMPLETE : /* SEEK DONE, START DMA */ /* Make sure seek really happened*/ if(fd->track == FD_NO_TRACK) { int descyl = b_cylinder * fd->ft->steptrac; do { /* * This might be a "ready changed" interrupt, * which cannot really happen since the * RDY pin is hardwired to + 5 volts. This * generally indicates a "bouncing" intr * line, so do one of the following: * * When running on an enhanced FDC that is * known to not go stuck after responding * with INVALID, fetch all interrupt states * until seeing either an INVALID or a * real interrupt condition. * * When running on a dumb old NE765, give * up immediately. The controller will * provide up to four dummy RC interrupt * conditions right after reset (for the * corresponding four drives), so this is * our only chance to get notice that it * was not the FDC that caused the interrupt. */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); if (0 == descyl) { int failed = 0; /* * seek to cyl 0 requested; make sure we are * really there */ if (fd_sense_drive_status(fdc, &st3)) failed = 1; #ifdef EPSON_NRDISK if (fdu == nrdu) st3 = NE7_ST3_T0; #endif /* EPSON_NRDISK */ if ((st3 & NE7_ST3_T0) == 0) { printf( "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", fdu, st3, NE7_ST3BITS); failed = 1; } if (failed) { if(fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } } #ifdef EPSON_NRDISK if (fdu == nrdu) cyl = descyl; #endif if (cyl != descyl) { printf( "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", fdu, descyl, cyl, st0); if (fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } } fd->track = b_cylinder; #ifdef EPSON_NRDISK if (fdu != nrdu) { #endif /* EPSON_NRDISK */ #ifdef FDC_YE if (!(fdc->flags & FDC_PCMCIA)) #endif isa_dmastart(bp->b_flags, bp->b_data+fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; fd->hddrv = ((head&1)<<2)+fdu; if(format || !read) { /* make sure the drive is writable */ if(fd_sense_drive_status(fdc, &st3) != 0) { /* stuck controller? */ isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); fdc->retry = 6; /* reset the beast */ return (retrier(fdc)); } if(st3 & NE7_ST3_WP) { /* * XXX YES! this is ugly. * in order to force the current operation * to fail, we will have to fake an FDC * error - all error handling is done * by the retrier() */ fdc->status[0] = NE7_ST0_IC_AT; fdc->status[1] = NE7_ST1_NW; fdc->status[2] = 0; fdc->status[3] = fd->track; fdc->status[4] = head; fdc->status[5] = sec; fdc->retry = 8; /* break out immediately */ fdc->state = IOTIMEDOUT; /* not really... */ return (1); } } if (format) { #ifdef FDC_YE if (fdc->flags & FDC_PCMCIA) (void)fdcpio(fdcu,bp->b_flags, bp->b_data+fd->skip, bp->b_bcount); #endif /* formatting */ if(fd_cmd(fdc, 6, NE7CMD_FORMAT, head << 2 | fdu, finfo->fd_formb_secshift, finfo->fd_formb_nsecs, finfo->fd_formb_gaplen, finfo->fd_formb_fillbyte, 0)) { /* controller fell over */ isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); fdc->retry = 6; return (retrier(fdc)); } } else { #ifdef FDC_YE if (fdc->flags & FDC_PCMCIA) { /* * this seems to be necessary even when * reading data */ SET_BCDR(1,fdblk,fdc->baseport); /* * perform the write pseudo-DMA before * the WRITE command is sent */ if (!read) (void)fdcpio(fdcu,bp->b_flags, bp->b_data+fd->skip, fdblk); } #endif if (fd_cmd(fdc, 9, (read ? NE7CMD_READ : NE7CMD_WRITE), head << 2 | fdu, /* head & unit */ fd->track, /* track */ head, sec, /* sector + 1 */ fd->ft->secsize, /* sector size */ sectrac, /* sectors/track */ fd->ft->gap, /* gap size */ fd->ft->datalen, /* data length */ 0)) { /* the beast is sleeping again */ isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); fdc->retry = 6; return (retrier(fdc)); } } #ifdef FDC_YE if (fdc->flags & FDC_PCMCIA) /* * if this is a read, then simply await interrupt * before performing PIO */ if (read && !fdcpio(fdcu,bp->b_flags, bp->b_data+fd->skip,fdblk)) { fd->tohandle = timeout(fd_iotimeout, (caddr_t)fdcu, hz); return(0); /* will return later */ }; /* * write (or format) operation will fall through and * await completion interrupt */ #endif fdc->state = IOCOMPLETE; fd->tohandle = timeout(fd_iotimeout, fdc, hz); return (0); /* will return later */ #ifdef EPSON_NRDISK } else { nrdblkn = (nrd_t)((unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + fd->skip/fdblk); nrd_LED_on(); nrd_addrset(fdblk * nrdblkn); while (!nrd_check_ready()) DELAY(1); if (read) epson_insw(P_NRD_DATA, bp->b_data + fd->skip, fdblk / sizeof(short)); else epson_outsw(P_NRD_DATA, bp->b_data + fd->skip, (format ? bp->b_bcount : fdblk) / sizeof(short)); blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + fd->skip/fdblk; sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; fd->hddrv = ((head&1)<<2)+fdu; if (nrdsec++ >= nrd_sec()) nrdaddr = (nrd_t)(fd->track * 8 + head * 4); nrdsec = sec; fdc->state = IOCOMPLETE; } #endif #ifdef FDC_YE case PIOREAD: /* * actually perform the PIO read. The IOCOMPLETE case * removes the timeout for us. */ (void)fdcpio(fdcu,bp->b_flags,bp->b_data+fd->skip,fdblk); fdc->state = IOCOMPLETE; /* FALLTHROUGH */ #endif case IOCOMPLETE: /* IO DONE, post-analyze */ #ifdef EPSON_NRDISK if (fdu != nrdu) untimeout(fd_iotimeout, fdc, fd->tohandle); #else untimeout(fd_iotimeout, fdc, fd->tohandle); #endif if (fd_read_status(fdc, fd->fdsu)) { isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); if (fdc->retry < 6) fdc->retry = 6; /* force a reset */ return (retrier(fdc)); } fdc->state = IOTIMEDOUT; /* FALLTHROUGH */ case IOTIMEDOUT: #ifdef EPSON_NRDISK if (fdu != nrdu) { #endif /* EPSON_NRDISK */ #ifdef FDC_YE if (!(fdc->flags & FDC_PCMCIA)) #endif isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); #ifdef EPSON_NRDISK } else nrd_LED_off(); #endif /* EPSON_NRDISK */ if (fdc->status[0] & NE7_ST0_IC) { if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[1] & NE7_ST1_OR) { /* * DMA overrun. Someone hogged the bus * and didn't release it in time for the * next FDC transfer. * Just restart it, don't increment retry * count. (vak) */ fdc->state = SEEKCOMPLETE; return (1); } else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV && fdc->retry < 6) fdc->retry = 6; /* force a reset */ else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[2] & NE7_ST2_WC && fdc->retry < 3) fdc->retry = 3; /* force recalibrate */ return (retrier(fdc)); } /* All OK */ fd->skip += fdblk; if (!format && fd->skip < bp->b_bcount - bp->b_resid) { /* set up next transfer */ fdc->state = DOSEEK; } else { /* ALL DONE */ fd->skip = 0; fdc->bp = NULL; devstat_end_transaction_buf(&fd->device_stats, bp); biodone(bp); fdc->fd = (fd_p) 0; fdc->fdu = -1; fdc->state = FINDWORK; } return (1); case RESETCTLR: fdc_reset(fdc); fdc->retry++; fdc->state = RESETCOMPLETE; return (0); case RESETCOMPLETE: /* * Discard all the results from the reset so that they * can't cause an unexpected interrupt later. */ for (i = 0; i < 4; i++) (void)fd_sense_int(fdc, &st0, &cyl); fdc->state = STARTRECAL; /* Fall through. */ case STARTRECAL: #ifdef PC98 pc98_fd_check_ready(fdu); #endif if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) { /* arrgl */ fdc->retry = 6; return (retrier(fdc)); } fdc->state = RECALWAIT; return (0); /* will return later */ case RECALWAIT: /* allow heads to settle */ timeout(fd_pseudointr, fdc, hz / 8); fdc->state = RECALCOMPLETE; return (0); /* will return later */ case RECALCOMPLETE: do { /* * See SEEKCOMPLETE for a comment on this: */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); #ifdef EPSON_NRDISK if (fdu == nrdu) { st0 = NE7_ST0_IC_NT; cyl = 0; } #endif if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) { if(fdc->retry > 3) /* * a recalibrate from beyond cylinder 77 * will "fail" due to the FDC limitations; * since people used to complain much about * the failure message, try not logging * this one if it seems to be the first * time in a line */ printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, st0, NE7_ST0BITS, cyl); if(fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } fd->track = 0; /* Seek (probably) necessary */ fdc->state = DOSEEK; return (1); /* will return immediatly */ case MOTORWAIT: if(fd->flags & FD_MOTOR_WAIT) { return (0); /* time's not up yet */ } if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else { /* * If all motors were off, then the controller was * reset, so it has lost track of the current * cylinder. Recalibrate to handle this case. * But first, discard the results of the reset. */ fdc->state = RESETCOMPLETE; } return (1); /* will return immediatly */ default: device_print_prettyname(fdc->fdc_dev); printf("unexpected FD int->"); if (fd_read_status(fdc, fd->fdsu) == 0) printf("FDC status :%x %x %x %x %x %x %x ", fdc->status[0], fdc->status[1], fdc->status[2], fdc->status[3], fdc->status[4], fdc->status[5], fdc->status[6] ); else printf("No status available "); if (fd_sense_int(fdc, &st0, &cyl) != 0) { printf("[controller is dead now]\n"); return (0); } printf("ST0 = %x, PCN = %x\n", st0, cyl); return (0); } /*XXX confusing: some branches return immediately, others end up here*/ return (1); /* Come back immediatly to new state */ } static int retrier(struct fdc_data *fdc) { register struct buf *bp; struct fd_data *fd; int fdu; bp = fdc->bp; /* XXX shouldn't this be cached somewhere? */ fdu = FDUNIT(minor(bp->b_dev)); fd = devclass_get_softc(fd_devclass, fdu); if (fd->options & FDOPT_NORETRY) goto fail; switch (fdc->retry) { case 0: case 1: case 2: fdc->state = SEEKCOMPLETE; break; case 3: case 4: case 5: fdc->state = STARTRECAL; break; case 6: fdc->state = RESETCTLR; break; case 7: break; default: fail: { dev_t sav_b_dev = bp->b_dev; /* Trick diskerr */ bp->b_dev = makedev(major(bp->b_dev), (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART); diskerr(bp, "hard error", LOG_PRINTF, fdc->fd->skip / DEV_BSIZE, (struct disklabel *)NULL); bp->b_dev = sav_b_dev; if (fdc->flags & FDC_STAT_VALID) { printf( " (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n", fdc->status[0], NE7_ST0BITS, fdc->status[1], NE7_ST1BITS, fdc->status[2], NE7_ST2BITS, fdc->status[3], fdc->status[4], fdc->status[5]); } else printf(" (No status)\n"); } bp->b_flags |= B_ERROR; bp->b_error = EIO; bp->b_resid += bp->b_bcount - fdc->fd->skip; fdc->bp = NULL; fdc->fd->skip = 0; devstat_end_transaction_buf(&fdc->fd->device_stats, bp); biodone(bp); fdc->state = FINDWORK; fdc->flags |= FDC_NEEDS_RESET; fdc->fd = (fd_p) 0; fdc->fdu = -1; return (1); } fdc->retry++; return (1); } static int fdformat(dev, finfo, p) dev_t dev; struct fd_formb *finfo; struct proc *p; { fdu_t fdu; fd_p fd; struct buf *bp; int rv = 0, s; size_t fdblk; fdu = FDUNIT(minor(dev)); fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; /* set up a buffer header for fdstrategy() */ bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); if(bp == 0) return ENOBUFS; /* * keep the process from being swapped */ PHOLD(p); bzero((void *)bp, sizeof(struct buf)); BUF_LOCKINIT(bp); BUF_LOCK(bp, LK_EXCLUSIVE); bp->b_flags = B_PHYS | B_FORMAT; /* * calculate a fake blkno, so fdstrategy() would initiate a * seek to the requested cylinder */ bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; bp->b_data = (caddr_t)finfo; /* now do the format */ bp->b_dev = dev; BUF_STRATEGY(bp, 0); /* ...and wait for it to complete */ s = splbio(); while(!(bp->b_flags & B_DONE)) { rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz); if (rv == EWOULDBLOCK) break; } splx(s); if (rv == EWOULDBLOCK) { /* timed out */ rv = EIO; biodone(bp); } if (bp->b_flags & B_ERROR) rv = bp->b_error; /* * allow the process to be swapped */ PRELE(p); BUF_UNLOCK(bp); BUF_LOCKFREE(bp); free(bp, M_TEMP); return rv; } /* * TODO: don't allocate buffer on stack. */ static int fdioctl(dev, cmd, addr, flag, p) dev_t dev; u_long cmd; caddr_t addr; int flag; struct proc *p; { fdu_t fdu = FDUNIT(minor(dev)); fd_p fd = devclass_get_softc(fd_devclass, fdu); size_t fdblk; struct fd_type *fdt; struct disklabel *dl; char buffer[DEV_BSIZE]; int error = 0; fdblk = 128 << fd->ft->secsize; #ifdef PC98 pc98_fd_check_ready(fdu); #endif switch (cmd) { case DIOCGDINFO: bzero(buffer, sizeof (buffer)); dl = (struct disklabel *)buffer; dl->d_secsize = fdblk; fdt = fd->ft; dl->d_secpercyl = fdt->size / fdt->tracks; dl->d_type = DTYPE_FLOPPY; if (readdisklabel(dkmodpart(dev, RAW_PART), dl) == NULL) error = 0; else error = EINVAL; *(struct disklabel *)addr = *dl; break; case DIOCSDINFO: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWLABEL: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWDINFO: if ((flag & FWRITE) == 0) { error = EBADF; break; } dl = (struct disklabel *)addr; if ((error = setdisklabel((struct disklabel *)buffer, dl, (u_long)0)) != 0) break; error = writedisklabel(dev, (struct disklabel *)buffer); break; case FD_FORM: if ((flag & FWRITE) == 0) error = EBADF; /* must be opened for writing */ else if (((struct fd_formb *)addr)->format_version != FD_FORMAT_VERSION) error = EINVAL; /* wrong version of formatting prog */ else error = fdformat(dev, (struct fd_formb *)addr, p); break; case FD_GTYPE: /* get drive type */ *(struct fd_type *)addr = *fd->ft; break; case FD_STYPE: /* set drive type */ /* this is considered harmful; only allow for superuser */ if (suser(p) != 0) return EPERM; *fd->ft = *(struct fd_type *)addr; break; case FD_GOPTS: /* get drive options */ *(int *)addr = fd->options; break; case FD_SOPTS: /* set drive options */ fd->options = *(int *)addr; break; default: error = ENOTTY; break; } return (error); } /* * Hello emacs, these are the * Local Variables: * c-indent-level: 8 * c-continued-statement-offset: 8 * c-continued-brace-offset: 0 * c-brace-offset: -8 * c-brace-imaginary-offset: 0 * c-argdecl-indent: 8 * c-label-offset: -8 * c++-hanging-braces: 1 * c++-access-specifier-offset: -8 * c++-empty-arglist-indent: 8 * c++-friend-offset: 0 * End: */ Index: head/sys/pccard/pccard.c =================================================================== --- head/sys/pccard/pccard.c (revision 54072) +++ head/sys/pccard/pccard.c (revision 54073) @@ -1,781 +1,782 @@ /* * pccard.c - Interface code for PC-CARD controllers. * * June 1995, Andrew McRae (andrew@mega.com.au) *------------------------------------------------------------------------- * * Copyright (c) 1995 Andrew McRae. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include "opt_pcic.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "apm.h" #if NAPM > 0 #include #endif /* NAPM > 0 */ #include #include #include #include #include #include /* * XXX We shouldn't be using processor-specific/bus-specific code in * here, but we need the start of the ISA hole (IOM_BEGIN). */ #ifdef PC98 #include #else #include #endif SYSCTL_NODE(_machdep, OID_AUTO, pccard, CTLFLAG_RW, 0, "pccard"); static int pcic_resume_reset = #ifdef PCIC_RESUME_RESET /* opt_pcic.h */ 1; #else 0; #endif SYSCTL_INT(_machdep_pccard, OID_AUTO, pcic_resume_reset, CTLFLAG_RW, &pcic_resume_reset, 0, ""); #define PCCARD_MEMSIZE (4*1024) #define MIN(a,b) ((a)<(b)?(a):(b)) static int allocate_driver(struct slot *, struct dev_desc *); static void inserted(void *); static void disable_slot(struct slot *); static void disable_slot_spl0(struct slot *); static void disable_slot_to(void *); static int invalid_io_memory(unsigned long, int); static void power_off_slot(void *); #if NAPM > 0 /* * For the APM stuff, the apmhook structure is kept * separate from the slot structure so that the slot * drivers do not need to know about the hooks (or the * data structures). */ static int slot_suspend(void *arg); static int slot_resume(void *arg); static struct apmhook s_hook[MAXSLOT]; /* APM suspend */ static struct apmhook r_hook[MAXSLOT]; /* APM resume */ #endif /* NAPM > 0 */ static struct slot *pccard_slots[MAXSLOT]; /* slot entries */ static struct slot *slot_list; static struct slot_ctrl *cont_list; /* * The driver interface for read/write uses a block * of memory in the ISA I/O memory space allocated via * an ioctl setting. */ static unsigned long pccard_mem; /* Physical memory */ static unsigned char *pccard_kmem; /* Kernel virtual address */ static d_open_t crdopen; static d_close_t crdclose; static d_read_t crdread; static d_write_t crdwrite; static d_ioctl_t crdioctl; static d_poll_t crdpoll; #define CDEV_MAJOR 50 static struct cdevsw crd_cdevsw = { /* open */ crdopen, /* close */ crdclose, /* read */ crdread, /* write */ crdwrite, /* ioctl */ crdioctl, /* poll */ crdpoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "crd", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; /* * Power off the slot. * (doing it immediately makes the removal of some cards unstable) */ static void power_off_slot(void *arg) { struct slot *slt = (struct slot *)arg; int s; /* * The following will generate an interrupt. So, to hold off * the interrupt unitl after disable runs so that we can get rid * rid of the interrupt before it becomes unsafe to touch the * device. */ s = splhigh(); /* Power off the slot. */ slt->pwr_off_pending = 0; slt->ctrl->disable(slt); splx(s); } /* * disable_slot - Disables the slot by removing * the power and unmapping the I/O */ static void disable_slot(struct slot *slt) { /* XXX Need to store pccarddev in slt. */ device_t pccarddev; device_t *kids; int nkids; int i; int ret; /* * Note that a race condition is possible here; if a * driver is accessing the device and it is removed, then * all bets are off... */ pccarddev = devclass_get_device(pccard_devclass, slt->slotnum); device_get_children(pccarddev, &kids, &nkids); for (i = 0; i < nkids; i++) { if ((ret = device_delete_child(pccarddev, kids[i])) != 0) printf("pccard: delete of %s failed: %d\n", device_get_nameunit(kids[i]), ret); } free(kids, M_TEMP); /* Power off the slot 1/2 second after removal of the card */ slt->poff_ch = timeout(power_off_slot, (caddr_t)slt, hz / 2); slt->pwr_off_pending = 1; } static void disable_slot_to(void *argp) { struct slot *slt = (struct slot *) argp; disable_slot(slt); slt->state = empty; printf("pccard: card removed, slot %d\n", slt->slotnum); pccard_remove_beep(); selwakeup(&slt->selp); } /* * Disables the slot later when we drop to spl0 via a timeout. */ static void disable_slot_spl0(struct slot *slt) { slt->disable_ch = timeout(disable_slot_to, (caddr_t) slt, 0); } /* * APM hooks for suspending and resuming. */ #if NAPM > 0 static int slot_suspend(void *arg) { struct slot *slt = arg; /* This code stolen from pccard_event:card_removed */ if (slt->state == filled) { int s = splhigh(); disable_slot(slt); slt->laststate = filled; slt->state = suspend; splx(s); printf("pccard: card disabled, slot %d\n", slt->slotnum); } /* * Disable any pending timeouts for this slot since we're * powering it down/disabling now. */ untimeout(power_off_slot, (caddr_t)slt, slt->disable_ch); untimeout(power_off_slot, (caddr_t)slt, slt->poff_ch); slt->ctrl->disable(slt); return (0); } static int slot_resume(void *arg) { struct slot *slt = arg; if (pcic_resume_reset) slt->ctrl->resume(slt); /* This code stolen from pccard_event:card_inserted */ if (slt->state == suspend) { slt->laststate = suspend; slt->state = empty; slt->insert_seq = 1; untimeout(inserted, (void *)slt, slt->insert_ch); inserted((void *) slt); selwakeup(&slt->selp); } return (0); } #endif /* NAPM > 0 */ /* * pccard_alloc_slot - Called from controller probe * routine, this function allocates a new PC-CARD slot * and initialises the data structures using the data provided. * It returns the allocated structure to the probe routine * to allow the controller specific data to be initialised. */ struct slot * pccard_alloc_slot(struct slot_ctrl *ctrl) { struct slot *slt; int slotno; for (slotno = 0; slotno < MAXSLOT; slotno++) if (pccard_slots[slotno] == 0) break; if (slotno == MAXSLOT) return(0); MALLOC(slt, struct slot *, sizeof(*slt), M_DEVBUF, M_WAITOK); bzero(slt, sizeof(*slt)); make_dev(&crd_cdevsw, slotno, 0, 0, 0600, "card%d", slotno); if (ctrl->extra) { MALLOC(slt->cdata, void *, ctrl->extra, M_DEVBUF, M_WAITOK); bzero(slt->cdata, ctrl->extra); } slt->ctrl = ctrl; slt->slotnum = slotno; pccard_slots[slotno] = slt; slt->next = slot_list; slot_list = slt; /* * If this controller hasn't been seen before, then * link it into the list of controllers. */ if (ctrl->slots++ == 0) { ctrl->next = cont_list; cont_list = ctrl; if (ctrl->maxmem > NUM_MEM_WINDOWS) ctrl->maxmem = NUM_MEM_WINDOWS; if (ctrl->maxio > NUM_IO_WINDOWS) ctrl->maxio = NUM_IO_WINDOWS; } callout_handle_init(&slt->insert_ch); callout_handle_init(&slt->poff_ch); callout_handle_init(&slt->disable_ch); #if NAPM > 0 { struct apmhook *ap; ap = &s_hook[slt->slotnum]; ap->ah_fun = slot_suspend; ap->ah_arg = (void *)slt; ap->ah_name = "pcccard"; ap->ah_order = APM_MID_ORDER; apm_hook_establish(APM_HOOK_SUSPEND, ap); ap = &r_hook[slt->slotnum]; ap->ah_fun = slot_resume; ap->ah_arg = (void *)slt; ap->ah_name = "pccard"; ap->ah_order = APM_MID_ORDER; apm_hook_establish(APM_HOOK_RESUME, ap); } #endif /* NAPM > 0 */ return(slt); } /* * allocate_driver - Create a new device entry for this * slot, and attach a driver to it. */ static int allocate_driver(struct slot *slt, struct dev_desc *desc) { struct pccard_devinfo *devi; device_t pccarddev; int err, irq = 0; device_t child; pccarddev = devclass_get_device(pccard_devclass, 0); irq = ffs(desc->irqmask) - 1; MALLOC(devi, struct pccard_devinfo *, sizeof(*devi), M_DEVBUF, M_WAITOK); bzero(devi, sizeof(*devi)); strcpy(devi->name, desc->name); /* * Create an entry for the device under this slot. */ devi->running = 1; devi->slt = slt; bcopy(desc->misc, devi->misc, sizeof(desc->misc)); resource_list_init(&devi->resources); child = devi->isahd.id_device = device_add_child(pccarddev, devi->name, - desc->unit, devi); + desc->unit); device_set_flags(child, desc->flags); + device_set_ivars(child, devi); err = bus_set_resource(child, SYS_RES_IOPORT, 0, desc->iobase, desc->iosize); if (err) goto err; if (irq) err = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1); if (err) goto err; if (desc->memsize) { err = bus_set_resource(child, SYS_RES_MEMORY, 0, desc->mem, desc->memsize); if (err) goto err; } err = device_probe_and_attach(child); err: if (err) device_delete_child(pccarddev, child); return (err); } /* * card insert routine - Called from a timeout to debounce * insertion events. */ static void inserted(void *arg) { struct slot *slt = arg; slt->state = filled; /* * Enable 5V to the card so that the CIS can be read. */ slt->pwr.vcc = 50; slt->pwr.vpp = 0; /* * Disable any pending timeouts for this slot, and explicitly * power it off right now. Then, re-enable the power using * the (possibly new) power settings. */ untimeout(power_off_slot, (caddr_t)slt, slt->disable_ch); untimeout(power_off_slot, (caddr_t)slt, slt->poff_ch); power_off_slot(slt); slt->ctrl->power(slt); printf("pccard: card inserted, slot %d\n", slt->slotnum); /* * Now start resetting the card. */ slt->ctrl->reset(slt); } /* * Card event callback. Called at splhigh to prevent * device interrupts from interceding. */ void pccard_event(struct slot *slt, enum card_event event) { if (slt->insert_seq) { slt->insert_seq = 0; untimeout(inserted, (void *)slt, slt->insert_ch); } switch(event) { case card_removed: /* * The slot and devices are disabled, but the * data structures are not unlinked. */ if (slt->state == filled) { disable_slot_spl0(slt); } break; case card_inserted: slt->insert_seq = 1; slt->insert_ch = timeout(inserted, (void *)slt, hz/4); pccard_insert_beep(); break; } } /* * Device driver interface. */ static int crdopen(dev_t dev, int oflags, int devtype, struct proc *p) { struct slot *slt; if (minor(dev) >= MAXSLOT) return(ENXIO); slt = pccard_slots[minor(dev)]; if (slt == 0) return(ENXIO); if (slt->rwmem == 0) slt->rwmem = MDF_ATTR; return(0); } /* * Close doesn't de-allocate any resources, since * slots may be assigned to drivers already. */ static int crdclose(dev_t dev, int fflag, int devtype, struct proc *p) { return(0); } /* * read interface. Map memory at lseek offset, * then transfer to user space. */ static int crdread(dev_t dev, struct uio *uio, int ioflag) { struct slot *slt = pccard_slots[minor(dev)]; struct mem_desc *mp, oldmap; unsigned char *p; unsigned int offs; int error = 0, win, count; if (slt == 0 || slt->state != filled) return(ENXIO); if (pccard_mem == 0) return(ENOMEM); for (win = 0; win < slt->ctrl->maxmem; win++) if ((slt->mem[win].flags & MDF_ACTIVE) == 0) break; if (win >= slt->ctrl->maxmem) return(EBUSY); mp = &slt->mem[win]; oldmap = *mp; mp->flags = slt->rwmem|MDF_ACTIVE; while (uio->uio_resid && error == 0) { mp->card = uio->uio_offset; mp->size = PCCARD_MEMSIZE; mp->start = (caddr_t)(void *)(uintptr_t)pccard_mem; if ((error = slt->ctrl->mapmem(slt, win)) != 0) break; offs = (unsigned int)uio->uio_offset & (PCCARD_MEMSIZE - 1); p = pccard_kmem + offs; count = MIN(PCCARD_MEMSIZE - offs, uio->uio_resid); error = uiomove(p, count, uio); } /* * Restore original map. */ *mp = oldmap; slt->ctrl->mapmem(slt, win); return(error); } /* * crdwrite - Write data to card memory. * Handles wrap around so that only one memory * window is used. */ static int crdwrite(dev_t dev, struct uio *uio, int ioflag) { struct slot *slt = pccard_slots[minor(dev)]; struct mem_desc *mp, oldmap; unsigned char *p; unsigned int offs; int error = 0, win, count; if (slt == 0 || slt->state != filled) return(ENXIO); if (pccard_mem == 0) return(ENOMEM); for (win = 0; win < slt->ctrl->maxmem; win++) if ((slt->mem[win].flags & MDF_ACTIVE) == 0) break; if (win >= slt->ctrl->maxmem) return(EBUSY); mp = &slt->mem[win]; oldmap = *mp; mp->flags = slt->rwmem|MDF_ACTIVE; while (uio->uio_resid && error == 0) { mp->card = uio->uio_offset; mp->size = PCCARD_MEMSIZE; mp->start = (caddr_t)(void *)(uintptr_t)pccard_mem; if ((error = slt->ctrl->mapmem(slt, win)) != 0) break; offs = (unsigned int)uio->uio_offset & (PCCARD_MEMSIZE - 1); p = pccard_kmem + offs; count = MIN(PCCARD_MEMSIZE - offs, uio->uio_resid); error = uiomove(p, count, uio); } /* * Restore original map. */ *mp = oldmap; slt->ctrl->mapmem(slt, win); return(error); } /* * ioctl calls - allows setting/getting of memory and I/O * descriptors, and assignment of drivers. */ static int crdioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p) { struct slot *slt = pccard_slots[minor(dev)]; struct mem_desc *mp; struct io_desc *ip; int s, err; int pwval; if (slt == 0 && cmd != PIOCRWMEM) return(ENXIO); switch(cmd) { default: if (slt->ctrl->ioctl) return(slt->ctrl->ioctl(slt, cmd, data)); return(ENOTTY); /* * Get slot state. */ case PIOCGSTATE: s = splhigh(); ((struct slotstate *)data)->state = slt->state; ((struct slotstate *)data)->laststate = slt->laststate; slt->laststate = slt->state; splx(s); ((struct slotstate *)data)->maxmem = slt->ctrl->maxmem; ((struct slotstate *)data)->maxio = slt->ctrl->maxio; ((struct slotstate *)data)->irqs = 0; break; /* * Get memory context. */ case PIOCGMEM: s = ((struct mem_desc *)data)->window; if (s < 0 || s >= slt->ctrl->maxmem) return(EINVAL); mp = &slt->mem[s]; ((struct mem_desc *)data)->flags = mp->flags; ((struct mem_desc *)data)->start = mp->start; ((struct mem_desc *)data)->size = mp->size; ((struct mem_desc *)data)->card = mp->card; break; /* * Set memory context. If context already active, then unmap it. * It is hard to see how the parameters can be checked. * At the very least, we only allow root to set the context. */ case PIOCSMEM: if (suser(p)) return(EPERM); if (slt->state != filled) return(ENXIO); s = ((struct mem_desc *)data)->window; if (s < 0 || s >= slt->ctrl->maxmem) return(EINVAL); slt->mem[s] = *((struct mem_desc *)data); return(slt->ctrl->mapmem(slt, s)); /* * Get I/O port context. */ case PIOCGIO: s = ((struct io_desc *)data)->window; if (s < 0 || s >= slt->ctrl->maxio) return(EINVAL); ip = &slt->io[s]; ((struct io_desc *)data)->flags = ip->flags; ((struct io_desc *)data)->start = ip->start; ((struct io_desc *)data)->size = ip->size; break; /* * Set I/O port context. */ case PIOCSIO: if (suser(p)) return(EPERM); if (slt->state != filled) return(ENXIO); s = ((struct io_desc *)data)->window; if (s < 0 || s >= slt->ctrl->maxio) return(EINVAL); slt->io[s] = *((struct io_desc *)data); /* XXX Don't actually map */ return 0; break; /* * Set memory window flags for read/write interface. */ case PIOCRWFLAG: slt->rwmem = *(int *)data; break; /* * Set the memory window to be used for the read/write interface. */ case PIOCRWMEM: if (*(unsigned long *)data == 0) { if (pccard_mem) *(unsigned long *)data = pccard_mem; break; } if (suser(p)) return(EPERM); /* * Validate the memory by checking it against the I/O * memory range. It must also start on an aligned block size. */ if (invalid_io_memory(*(unsigned long *)data, PCCARD_MEMSIZE)) return(EINVAL); if (*(unsigned long *)data & (PCCARD_MEMSIZE-1)) return(EINVAL); /* * Map it to kernel VM. */ pccard_mem = *(unsigned long *)data; pccard_kmem = (unsigned char *)(void *)(uintptr_t) (pccard_mem + atdevbase - IOM_BEGIN); break; /* * Set power values. */ case PIOCSPOW: slt->pwr = *(struct power *)data; return(slt->ctrl->power(slt)); /* * Allocate a driver to this slot. */ case PIOCSDRV: if (suser(p)) return(EPERM); err = allocate_driver(slt, (struct dev_desc *)data); if (!err) pccard_success_beep(); else pccard_failure_beep(); return err; /* * Virtual removal/insertion */ case PIOCSVIR: pwval = *(int *)data; if (!pwval) { if (slt->state != filled) return EINVAL; } else { if (slt->state != empty) return EINVAL; } pccard_event(slt, pwval == 0 ? card_removed : card_inserted); break; case PIOCSBEEP: if (pccard_beep_select(*(int *)data)) { return EINVAL; } break; } return(0); } /* * poll - Poll on exceptions will return true * when a change in card status occurs. */ static int crdpoll(dev_t dev, int events, struct proc *p) { int s; struct slot *slt = pccard_slots[minor(dev)]; int revents = 0; if (events & (POLLIN | POLLRDNORM)) revents |= events & (POLLIN | POLLRDNORM); if (events & (POLLOUT | POLLWRNORM)) revents |= events & (POLLIN | POLLRDNORM); s = splhigh(); /* * select for exception - card event. */ if (events & POLLRDBAND) if (slt == 0 || slt->laststate != slt->state) revents |= POLLRDBAND; if (revents == 0) selrecord(p, &slt->selp); splx(s); return (revents); } /* * invalid_io_memory - verify that the ISA I/O memory block * is a valid and unallocated address. * A simple check of the range is done, and then a * search of the current devices is done to check for * overlapping regions. */ static int invalid_io_memory(unsigned long adr, int size) { /* XXX - What's magic about 0xC0000?? */ if (adr < 0xC0000 || (adr+size) > IOM_END) return(1); return(0); } Index: head/sys/pccard/pcic.c =================================================================== --- head/sys/pccard/pcic.c (revision 54072) +++ head/sys/pccard/pcic.c (revision 54073) @@ -1,880 +1,880 @@ /* * Intel PCIC or compatible Controller driver * May be built to make a loadable module. *------------------------------------------------------------------------- * * Copyright (c) 1995 Andrew McRae. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #if 0 #include #include #include #endif #include #include #include #include #include #ifdef APIC_IO #include #endif /* * Prototypes for interrupt handler. */ static inthand2_t pcicintr; static int pcic_ioctl __P((struct slot *, int, caddr_t)); static int pcic_power __P((struct slot *)); static timeout_t pcic_reset; static void pcic_resume(struct slot *); static void pcic_disable __P((struct slot *)); static void pcic_mapirq __P((struct slot *, int)); static timeout_t pcictimeout; static struct callout_handle pcictimeout_ch = CALLOUT_HANDLE_INITIALIZER(&pcictimeout_ch); static int pcic_memory(struct slot *, int); static int pcic_io(struct slot *, int); /* * Per-slot data table. */ static struct pcic_slot { int slotnum; /* My slot number */ int index; /* Index register */ int data; /* Data register */ int offset; /* Offset value for index */ char controller; /* Device type */ char revision; /* Device Revision */ struct slot *slt; /* Back ptr to slot */ u_char (*getb)(struct pcic_slot *, int); void (*putb)(struct pcic_slot *, int, u_char); u_char *regs; /* Pointer to regs in mem */ } pcic_slots[PCIC_MAX_SLOTS]; static int pcic_irq; static struct slot_ctrl cinfo; /* * Internal inline functions for accessing the PCIC. */ /* * Read a register from the PCIC. */ static __inline unsigned char getb1(struct pcic_slot *sp, int reg) { outb(sp->index, sp->offset + reg); return inb(sp->data); } /* * Write a register on the PCIC */ static __inline void putb1(struct pcic_slot *sp, int reg, unsigned char val) { outb(sp->index, sp->offset + reg); outb(sp->data, val); } /* * Clear bit(s) of a register. */ static __inline void clrb(struct pcic_slot *sp, int reg, unsigned char mask) { sp->putb(sp, reg, sp->getb(sp, reg) & ~mask); } /* * Set bit(s) of a register */ static __inline void setb(struct pcic_slot *sp, int reg, unsigned char mask) { sp->putb(sp, reg, sp->getb(sp, reg) | mask); } /* * Write a 16 bit value to 2 adjacent PCIC registers */ static __inline void putw(struct pcic_slot *sp, int reg, unsigned short word) { sp->putb(sp, reg, word & 0xFF); sp->putb(sp, reg + 1, (word >> 8) & 0xff); } /* * entry point from main code to map/unmap memory context. */ static int pcic_memory(struct slot *slt, int win) { struct pcic_slot *sp = slt->cdata; struct mem_desc *mp = &slt->mem[win]; int reg = mp->window * PCIC_MEMSIZE + PCIC_MEMBASE; if (mp->flags & MDF_ACTIVE) { unsigned long sys_addr = (uintptr_t)(void *)mp->start >> 12; /* * Write the addresses, card offsets and length. * The values are all stored as the upper 12 bits of the * 24 bit address i.e everything is allocated as 4 Kb chunks. */ putw(sp, reg, sys_addr & 0xFFF); putw(sp, reg+2, (sys_addr + (mp->size >> 12) - 1) & 0xFFF); putw(sp, reg+4, ((mp->card >> 12) - sys_addr) & 0x3FFF); /* * Each 16 bit register has some flags in the upper bits. */ if (mp->flags & MDF_16BITS) setb(sp, reg+1, PCIC_DATA16); if (mp->flags & MDF_ZEROWS) setb(sp, reg+1, PCIC_ZEROWS); if (mp->flags & MDF_WS0) setb(sp, reg+3, PCIC_MW0); if (mp->flags & MDF_WS1) setb(sp, reg+3, PCIC_MW1); if (mp->flags & MDF_ATTR) setb(sp, reg+5, PCIC_REG); if (mp->flags & MDF_WP) setb(sp, reg+5, PCIC_WP); /* * Enable the memory window. By experiment, we need a delay. */ setb(sp, PCIC_ADDRWINE, (1<cdata; struct io_desc *ip = &slt->io[win]; switch (win) { case 0: mask = PCIC_IO0_EN; reg = PCIC_IO0; break; case 1: mask = PCIC_IO1_EN; reg = PCIC_IO1; break; default: panic("Illegal PCIC I/O window request!"); } if (ip->flags & IODF_ACTIVE) { unsigned char x, ioctlv; putw(sp, reg, ip->start); putw(sp, reg+2, ip->start+ip->size-1); x = 0; if (ip->flags & IODF_ZEROWS) x |= PCIC_IO_0WS; if (ip->flags & IODF_WS) x |= PCIC_IO_WS; if (ip->flags & IODF_CS16) x |= PCIC_IO_CS16; if (ip->flags & IODF_16BIT) x |= PCIC_IO_16BIT; /* * Extract the current flags and merge with new flags. * Flags for window 0 in lower nybble, and in upper nybble * for window 1. */ ioctlv = sp->getb(sp, PCIC_IOCTL); DELAY(100); switch (win) { case 0: sp->putb(sp, PCIC_IOCTL, x | (ioctlv & 0xf0)); break; case 1: sp->putb(sp, PCIC_IOCTL, (x << 4) | (ioctlv & 0xf)); break; } DELAY(100); setb(sp, PCIC_ADDRWINE, mask); DELAY(100); } else { clrb(sp, PCIC_ADDRWINE, mask); DELAY(100); putw(sp, reg, 0); putw(sp, reg + 2, 0); } return(0); } /* * Look for an Intel PCIC (or compatible). * For each available slot, allocate a PC-CARD slot. */ /* * VLSI 82C146 has incompatibilities about the I/O address * of slot 1. Assume it's the only PCIC whose vendor ID is 0x84, * contact Nate Williams if incorrect. */ static int pcic_probe(device_t dev) { int slotnum, validslots = 0; struct slot *slt; struct pcic_slot *sp; unsigned char c; void *ih; char *name; int i; int error; struct resource *res; int rid; static int maybe_vlsi = 0; if (device_get_unit(dev) != 0) return ENXIO; /* * Initialise controller information structure. */ cinfo.mapmem = pcic_memory; cinfo.mapio = pcic_io; cinfo.ioctl = pcic_ioctl; cinfo.power = pcic_power; cinfo.mapirq = pcic_mapirq; cinfo.reset = pcic_reset; cinfo.disable = pcic_disable; cinfo.resume = pcic_resume; cinfo.maxmem = PCIC_MEM_WIN; cinfo.maxio = PCIC_IO_WIN; sp = pcic_slots; for (slotnum = 0; slotnum < PCIC_MAX_SLOTS; slotnum++, sp++) { /* * Initialise the PCIC slot table. */ sp->getb = getb1; sp->putb = putb1; if (slotnum < 4) { sp->index = PCIC_INDEX_0; sp->data = PCIC_DATA_0; sp->offset = slotnum * PCIC_SLOT_SIZE; } else { sp->index = PCIC_INDEX_1; sp->data = PCIC_DATA_1; sp->offset = (slotnum - 4) * PCIC_SLOT_SIZE; } /* * XXX - Screwed up slot 1 on the VLSI chips. According to * the Linux PCMCIA code from David Hinds, working chipsets * return 0x84 from their (correct) ID ports, while the broken * ones would need to be probed at the new offset we set after * we assume it's broken. */ if (slotnum == 1 && maybe_vlsi && sp->getb(sp, PCIC_ID_REV) != 0x84) { sp->index += 4; sp->data += 4; sp->offset = PCIC_SLOT_SIZE << 1; } /* * see if there's a PCMCIA controller here * Intel PCMCIA controllers use 0x82 and 0x83 * IBM clone chips use 0x88 and 0x89, apparently */ c = sp->getb(sp, PCIC_ID_REV); sp->revision = -1; switch(c) { /* * 82365 or clones. */ case 0x82: case 0x83: sp->controller = PCIC_I82365; sp->revision = c & 1; /* * Now check for VADEM chips. */ outb(sp->index, 0x0E); outb(sp->index, 0x37); setb(sp, 0x3A, 0x40); c = sp->getb(sp, PCIC_ID_REV); if (c & 0x08) { switch (sp->revision = c & 7) { case 1: sp->controller = PCIC_VG365; break; case 2: sp->controller = PCIC_VG465; break; case 3: sp->controller = PCIC_VG468; break; default: sp->controller = PCIC_VG469; break; } clrb(sp, 0x3A, 0x40); } /* * Check for RICOH RF5C396 PCMCIA Controller */ c = sp->getb(sp, 0x3a); if (c == 0xb2) { sp->controller = PCIC_RF5C396; } break; /* * VLSI chips. */ case 0x84: sp->controller = PCIC_VLSI; maybe_vlsi = 1; break; case 0x88: case 0x89: sp->controller = PCIC_IBM; sp->revision = c & 1; break; case 0x8a: sp->controller = PCIC_IBM_KING; sp->revision = c & 1; break; default: continue; } /* * Check for Cirrus logic chips. */ sp->putb(sp, 0x1F, 0); c = sp->getb(sp, 0x1F); if ((c & 0xC0) == 0xC0) { c = sp->getb(sp, 0x1F); if ((c & 0xC0) == 0) { if (c & 0x20) sp->controller = PCIC_PD672X; else sp->controller = PCIC_PD6710; sp->revision = 8 - ((c & 0x1F) >> 2); } } switch(sp->controller) { case PCIC_I82365: name = "Intel i82365"; break; case PCIC_IBM: name = "IBM PCIC"; break; case PCIC_IBM_KING: name = "IBM KING PCMCIA Controller"; break; case PCIC_PD672X: name = "Cirrus Logic PD672X"; break; case PCIC_PD6710: name = "Cirrus Logic PD6710"; break; case PCIC_VG365: name = "Vadem 365"; break; case PCIC_VG465: name = "Vadem 465"; break; case PCIC_VG468: name = "Vadem 468"; break; case PCIC_VG469: name = "Vadem 469"; break; case PCIC_RF5C396: name = "Ricoh RF5C396"; break; case PCIC_VLSI: name = "VLSI 82C146"; break; default: name = "Unknown!"; break; } device_set_desc(dev, name); /* * OK it seems we have a PCIC or lookalike. * Allocate a slot and initialise the data structures. */ validslots++; sp->slotnum = slotnum; slt = pccard_alloc_slot(&cinfo); if (slt == 0) continue; slt->cdata = sp; sp->slt = slt; /* * If we haven't allocated an interrupt for the controller, * then attempt to get one. */ if (pcic_irq == 0) { /* See if the user has requested a specific IRQ */ if (!getenv_int("machdep.pccard.pcic_irq", &pcic_irq)) pcic_irq = 0; rid = 0; res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, pcic_irq, ~0, 1, RF_ACTIVE); if (res) { error = bus_setup_intr(dev, res, INTR_TYPE_MISC, pcicintr, NULL, &ih); if (error) { bus_release_resource(dev, SYS_RES_IRQ, rid, res); return error; } pcic_irq = rman_get_start(res); } else { printf("pcic: polling, can't alloc %d\n", pcic_irq); } } /* * Modem cards send the speaker audio (dialing noises) * to the host's speaker. Cirrus Logic PCIC chips must * enable this. There is also a Low Power Dynamic Mode bit * that claims to reduce power consumption by 30%, so * enable it and hope for the best. */ if (sp->controller == PCIC_PD672X) { setb(sp, PCIC_MISC1, PCIC_SPKR_EN); setb(sp, PCIC_MISC2, PCIC_LPDM_EN); } /* * Check for a card in this slot. */ setb(sp, PCIC_POWER, PCIC_PCPWRE| PCIC_DISRST); if ((sp->getb(sp, PCIC_STATUS) & PCIC_CD) != PCIC_CD) { slt->laststate = slt->state = empty; } else { slt->laststate = slt->state = filled; pccard_event(sp->slt, card_inserted); } /* * Assign IRQ for slot changes */ if (pcic_irq > 0) sp->putb(sp, PCIC_STAT_INT, (pcic_irq << 4) | 0xF); } if (validslots && pcic_irq <= 0) pcictimeout_ch = timeout(pcictimeout, 0, hz/2); if (validslots) { for (i = 0; i < validslots; i++) { - device_add_child(dev, NULL, -1, NULL); + device_add_child(dev, NULL, -1); } } return(validslots ? 0 : ENXIO); } /* * ioctl calls - Controller specific ioctls */ static int pcic_ioctl(struct slot *slt, int cmd, caddr_t data) { struct pcic_slot *sp = slt->cdata; switch(cmd) { default: return(ENOTTY); /* * Get/set PCIC registers */ case PIOCGREG: ((struct pcic_reg *)data)->value = sp->getb(sp, ((struct pcic_reg *)data)->reg); break; case PIOCSREG: sp->putb(sp, ((struct pcic_reg *)data)->reg, ((struct pcic_reg *)data)->value); break; } return(0); } /* * pcic_power - Enable the power of the slot according to * the parameters in the power structure(s). */ static int pcic_power(struct slot *slt) { unsigned char reg = PCIC_DISRST|PCIC_PCPWRE; struct pcic_slot *sp = slt->cdata; switch(sp->controller) { case PCIC_PD672X: case PCIC_PD6710: case PCIC_VG365: case PCIC_VG465: case PCIC_VG468: case PCIC_VG469: case PCIC_RF5C396: case PCIC_VLSI: case PCIC_IBM_KING: switch(slt->pwr.vpp) { default: return(EINVAL); case 0: break; case 50: case 33: reg |= PCIC_VPP_5V; break; case 120: reg |= PCIC_VPP_12V; break; } switch(slt->pwr.vcc) { default: return(EINVAL); case 0: break; case 33: if (sp->controller == PCIC_IBM_KING) { reg |= PCIC_VCC_5V_KING; break; } reg |= PCIC_VCC_3V; if ((sp->controller == PCIC_VG468) || (sp->controller == PCIC_VG469) || (sp->controller == PCIC_VG465) || (sp->controller == PCIC_VG365)) setb(sp, 0x2f, 0x03) ; else setb(sp, 0x16, 0x02); break; case 50: if (sp->controller == PCIC_IBM_KING) { reg |= PCIC_VCC_5V_KING; break; } reg |= PCIC_VCC_5V; if ((sp->controller == PCIC_VG468) || (sp->controller == PCIC_VG469) || (sp->controller == PCIC_VG465) || (sp->controller == PCIC_VG365)) clrb(sp, 0x2f, 0x03) ; else clrb(sp, 0x16, 0x02); break; } break; } sp->putb(sp, PCIC_POWER, reg); DELAY(300*1000); if (slt->pwr.vcc) { reg |= PCIC_OUTENA; sp->putb(sp, PCIC_POWER, reg); DELAY(100*1000); } /* Some chips are smarter than us it seems, so if we weren't * allowed to use 5V, try 3.3 instead */ if (!(sp->getb(sp, PCIC_STATUS) & 0x40) && slt->pwr.vcc == 50) { slt->pwr.vcc = 33; slt->pwr.vpp = 0; return (pcic_power(slt)); } return(0); } /* * tell the PCIC which irq we want to use. only the following are legal: * 3, 4, 5, 7, 9, 10, 11, 12, 14, 15 */ static void pcic_mapirq(struct slot *slt, int irq) { struct pcic_slot *sp = slt->cdata; if (irq == 0) clrb(sp, PCIC_INT_GEN, 0xF); else sp->putb(sp, PCIC_INT_GEN, (sp->getb(sp, PCIC_INT_GEN) & 0xF0) | irq); } /* * pcic_reset - Reset the card and enable initial power. */ static void pcic_reset(void *chan) { struct slot *slt = chan; struct pcic_slot *sp = slt->cdata; switch (slt->insert_seq) { case 0: /* Something funny happended on the way to the pub... */ return; case 1: /* Assert reset */ clrb(sp, PCIC_INT_GEN, PCIC_CARDRESET); slt->insert_seq = 2; timeout(pcic_reset, (void *)slt, hz/4); return; case 2: /* Deassert it again */ setb(sp, PCIC_INT_GEN, PCIC_CARDRESET|PCIC_IOCARD); slt->insert_seq = 3; timeout(pcic_reset, (void *)slt, hz/4); return; case 3: /* Wait if card needs more time */ if (!sp->getb(sp, PCIC_STATUS) & PCIC_READY) { timeout(pcic_reset, (void *)slt, hz/10); return; } } slt->insert_seq = 0; if (sp->controller == PCIC_PD672X || sp->controller == PCIC_PD6710) { sp->putb(sp, PCIC_TIME_SETUP0, 0x1); sp->putb(sp, PCIC_TIME_CMD0, 0x6); sp->putb(sp, PCIC_TIME_RECOV0, 0x0); sp->putb(sp, PCIC_TIME_SETUP1, 1); sp->putb(sp, PCIC_TIME_CMD1, 0xf); sp->putb(sp, PCIC_TIME_RECOV1, 0); } selwakeup(&slt->selp); } /* * pcic_disable - Disable the slot. */ static void pcic_disable(struct slot *slt) { struct pcic_slot *sp = slt->cdata; sp->putb(sp, PCIC_INT_GEN, 0); sp->putb(sp, PCIC_POWER, 0); } /* * PCIC timer. If the controller doesn't have a free IRQ to use * or if interrupt steering doesn't work, poll the controller for * insertion/removal events. */ static void pcictimeout(void *chan) { pcicintr(0); pcictimeout_ch = timeout(pcictimeout, 0, hz/2); } /* * PCIC Interrupt handler. * Check each slot in turn, and read the card status change * register. If this is non-zero, then a change has occurred * on this card, so send an event to the main code. */ static void pcicintr(void *arg) { int slot, s; unsigned char chg; struct pcic_slot *sp = pcic_slots; s = splhigh(); for (slot = 0; slot < PCIC_MAX_SLOTS; slot++, sp++) { if (sp->slt && (chg = sp->getb(sp, PCIC_STAT_CHG)) != 0) { if (chg & PCIC_CDTCH) { if ((sp->getb(sp, PCIC_STATUS) & PCIC_CD) == PCIC_CD) { pccard_event(sp->slt, card_inserted); } else { pccard_event(sp->slt, card_removed); } } } } splx(s); } /* * pcic_resume - Suspend/resume support for PCIC */ static void pcic_resume(struct slot *slt) { struct pcic_slot *sp = slt->cdata; if (pcic_irq > 0) sp->putb(sp, PCIC_STAT_INT, (pcic_irq << 4) | 0xF); if (sp->controller == PCIC_PD672X) { setb(sp, PCIC_MISC1, PCIC_SPKR_EN); setb(sp, PCIC_MISC2, PCIC_LPDM_EN); } } static int pcic_activate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { struct pccard_devinfo *devi = device_get_ivars(child); int err; switch (type) { case SYS_RES_IOPORT: { struct io_desc *ip = &devi->slt->io[rid]; ip->flags |= IODF_ACTIVE; ip->start = rman_get_start(r); ip->size = rman_get_end(r) - rman_get_start(r) + 1; err = pcic_io(devi->slt, rid); if (err) return err; break; } case SYS_RES_IRQ: pcic_mapirq(devi->slt, rman_get_start(r)); break; case SYS_RES_MEMORY: { struct mem_desc *mp = &devi->slt->mem[rid]; mp->flags |= IODF_ACTIVE; mp->start = (caddr_t) rman_get_start(r); mp->size = rman_get_end(r) - rman_get_start(r) + 1; err = pcic_memory(devi->slt, rid); if (err) return err; break; } default: break; } err = bus_generic_activate_resource(dev, child, type, rid, r); return err; } static int pcic_deactivate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { struct pccard_devinfo *devi = device_get_ivars(child); int err; switch (type) { case SYS_RES_IOPORT: { struct io_desc *ip = &devi->slt->io[rid]; ip->flags &= ~IODF_ACTIVE; err = pcic_io(devi->slt, rid); if (err) { return err; } break; } case SYS_RES_IRQ: pcic_mapirq(devi->slt, 0); break; case SYS_RES_MEMORY: { struct mem_desc *mp = &devi->slt->mem[rid]; mp->flags &= ~IODF_ACTIVE; err = pcic_memory(devi->slt, rid); if (err) { return err; } break; } default: break; } err = bus_generic_deactivate_resource(dev, child, type, rid, r); return err; } static int pcic_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep) { return (bus_generic_setup_intr(dev, child, irq, flags, intr, arg, cookiep)); } static int pcic_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie) { return (bus_generic_teardown_intr(dev, child, irq, cookie)); } static device_method_t pcic_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pcic_probe), DEVMETHOD(device_attach, bus_generic_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, pcic_activate_resource), DEVMETHOD(bus_deactivate_resource, pcic_deactivate_resource), DEVMETHOD(bus_setup_intr, pcic_setup_intr), DEVMETHOD(bus_teardown_intr, pcic_teardown_intr), { 0, 0 } }; devclass_t pcic_devclass; static driver_t pcic_driver = { "pcic", pcic_methods, 1, /* no softc */ }; DRIVER_MODULE(pcic, isa, pcic_driver, pcic_devclass, 0, 0); Index: head/sys/pci/alpm.c =================================================================== --- head/sys/pci/alpm.c (revision 54072) +++ head/sys/pci/alpm.c (revision 54073) @@ -1,666 +1,666 @@ /*- * Copyright (c) 1998, 1999 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * */ /* * Power Management support for the Acer M15x3 chipsets */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smbus_if.h" #include "alpm.h" #define ALPM_DEBUG(x) if (alpm_debug) (x) #ifdef DEBUG static int alpm_debug = 1; #else static int alpm_debug = 0; #endif #define ACER_M1543_PMU_ID 0x710110b9 /* Uncomment this line to force another I/O base address for SMB */ /* #define ALPM_SMBIO_BASE_ADDR 0x3a80 */ /* I/O registers offsets - the base address is programmed via the * SMBBA PCI configuration register */ #define SMBSTS 0x0 /* SMBus host/slave status register */ #define SMBCMD 0x1 /* SMBus host/slave command register */ #define SMBSTART 0x2 /* start to generate programmed cycle */ #define SMBHADDR 0x3 /* host address register */ #define SMBHDATA 0x4 /* data A register for host controller */ #define SMBHDATB 0x5 /* data B register for host controller */ #define SMBHBLOCK 0x6 /* block register for host controller */ #define SMBHCMD 0x7 /* command register for host controller */ /* SMBSTS masks */ #define TERMINATE 0x80 #define BUS_COLLI 0x40 #define DEVICE_ERR 0x20 #define SMI_I_STS 0x10 #define HST_BSY 0x08 #define IDL_STS 0x04 #define HSTSLV_STS 0x02 #define HSTSLV_BSY 0x01 /* SMBCMD masks */ #define SMB_BLK_CLR 0x80 #define T_OUT_CMD 0x08 #define ABORT_HOST 0x04 /* SMBus commands */ #define SMBQUICK 0x00 #define SMBSRBYTE 0x10 /* send/receive byte */ #define SMBWRBYTE 0x20 /* write/read byte */ #define SMBWRWORD 0x30 /* write/read word */ #define SMBWRBLOCK 0x40 /* write/read block */ /* PCI configuration registers and masks */ #define COM 0x4 #define COM_ENABLE_IO 0x1 #define SMBBA 0x14 #define ATPC 0x5b #define ATPC_SMBCTRL 0x04 #define SMBHSI 0xe0 #define SMBHSI_SLAVE 0x2 #define SMBHSI_HOST 0x1 #define SMBHCBC 0xe2 #define SMBHCBC_CLOCK 0x70 #define SMBCLOCK_149K 0x0 #define SMBCLOCK_74K 0x20 #define SMBCLOCK_37K 0x40 #define SMBCLOCK_223K 0x80 #define SMBCLOCK_111K 0xa0 #define SMBCLOCK_55K 0xc0 struct alpm_data { int base; bus_space_tag_t smbst; bus_space_handle_t smbsh; pcici_t tag; }; struct alpm_data alpmdata[NALPM]; struct alsmb_softc { int base; device_t smbus; struct alpm_data *alpm; }; #define ALPM_SMBINB(alsmb,register) \ (bus_space_read_1(alsmb->alpm->smbst, alsmb->alpm->smbsh, register)) #define ALPM_SMBOUTB(alsmb,register,value) \ (bus_space_write_1(alsmb->alpm->smbst, alsmb->alpm->smbsh, register, value)) static int alsmb_probe(device_t); static int alsmb_attach(device_t); static int alsmb_smb_callback(device_t, int, caddr_t *); static int alsmb_smb_quick(device_t dev, u_char slave, int how); static int alsmb_smb_sendb(device_t dev, u_char slave, char byte); static int alsmb_smb_recvb(device_t dev, u_char slave, char *byte); static int alsmb_smb_writeb(device_t dev, u_char slave, char cmd, char byte); static int alsmb_smb_readb(device_t dev, u_char slave, char cmd, char *byte); static int alsmb_smb_writew(device_t dev, u_char slave, char cmd, short word); static int alsmb_smb_readw(device_t dev, u_char slave, char cmd, short *word); static int alsmb_smb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf); static int alsmb_smb_bread(device_t dev, u_char slave, char cmd, u_char count, char *byte); static devclass_t alsmb_devclass; static device_method_t alsmb_methods[] = { /* device interface */ DEVMETHOD(device_probe, alsmb_probe), DEVMETHOD(device_attach, alsmb_attach), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), /* smbus interface */ DEVMETHOD(smbus_callback, alsmb_smb_callback), DEVMETHOD(smbus_quick, alsmb_smb_quick), DEVMETHOD(smbus_sendb, alsmb_smb_sendb), DEVMETHOD(smbus_recvb, alsmb_smb_recvb), DEVMETHOD(smbus_writeb, alsmb_smb_writeb), DEVMETHOD(smbus_readb, alsmb_smb_readb), DEVMETHOD(smbus_writew, alsmb_smb_writew), DEVMETHOD(smbus_readw, alsmb_smb_readw), DEVMETHOD(smbus_bwrite, alsmb_smb_bwrite), DEVMETHOD(smbus_bread, alsmb_smb_bread), { 0, 0 } }; static driver_t alsmb_driver = { "alsmb", alsmb_methods, sizeof(struct alsmb_softc), }; static const char* alpm_pci_probe(pcici_t tag, pcidi_t type); static void alpm_pci_attach(pcici_t tag, int unit); static u_long alpm_count; static struct pci_device alpm_device = { "alpm", alpm_pci_probe, alpm_pci_attach, &alpm_count }; COMPAT_PCI_DRIVER (alpm, alpm_device); static const char* alpm_pci_probe(pcici_t tag, pcidi_t type) { if (type == ACER_M1543_PMU_ID) return ("AcerLabs M15x3 Power Management Unit"); return ((char *)0); } static void alpm_pci_attach(pcici_t tag, int unit) { struct alpm_data *alpm; u_long l; if (unit >= NALPM) { printf("alpm%d: attach: only %d units configured.\n", unit, NALPM); return; } alpm = &alpmdata[unit]; alpm->tag = tag; /* Unlock SMBIO base register access */ l = pci_cfgread(tag, ATPC, 1); pci_cfgwrite(tag, ATPC, l & ~ATPC_SMBCTRL, 1); if (bootverbose) { l = pci_cfgread(tag, SMBHSI, 1); printf("alsmb%d: %s/%s", unit, (l & SMBHSI_HOST) ? "host":"nohost", (l & SMBHSI_SLAVE) ? "slave":"noslave"); l = pci_cfgread(tag, SMBHCBC, 1); switch (l & SMBHCBC_CLOCK) { case SMBCLOCK_149K: printf(" 149K"); break; case SMBCLOCK_74K: printf(" 74K"); break; case SMBCLOCK_37K: printf(" 37K"); break; case SMBCLOCK_223K: printf(" 223K"); break; case SMBCLOCK_111K: printf(" 111K"); break; case SMBCLOCK_55K: printf(" 55K"); break; } } alpm->smbst = I386_BUS_SPACE_IO; #ifdef ALPM_SMBIO_BASE_ADDR /* disable I/O */ l = pci_cfgread(tag, COM, 2); pci_cfgwrite(tag, COM, l & ~COM_ENABLE_IO, 2); /* set the I/O base address */ pci_cfgwrite(tag, SMBBA, ALPM_SMBIO_BASE_ADDR | 0x1, 4); /* enable I/O */ pci_cfgwrite(tag, COM, l | COM_ENABLE_IO, 2); alpm->smbsh = ALPM_SMBIO_BASE_ADDR; #else alpm->smbsh = pci_cfgread(tag, SMBBA, 4) & ~0x1; #endif if (bootverbose) printf(" at 0x%x\n", alpm->smbsh); /* XXX add the I2C interface to the root_bus until pcibus is ready */ - device_add_child(root_bus, "alsmb", unit, NULL); + device_add_child(root_bus, "alsmb", unit); return; } /* * Not a real probe, we know the device exists since the device has * been added after the successfull pci probe. */ static int alsmb_probe(device_t dev) { struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev); sc->alpm = &alpmdata[device_get_unit(dev)]; device_set_desc(dev, "Aladdin IV/V/Pro2 SMBus controller"); return (0); } static int alsmb_attach(device_t dev) { struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev); /* allocate a new smbus device */ sc->smbus = smbus_alloc_bus(dev); /* probe and attach the smbus */ device_probe_and_attach(sc->smbus); return (0); } static int alsmb_smb_callback(device_t dev, int index, caddr_t *data) { int error = 0; switch (index) { case SMB_REQUEST_BUS: case SMB_RELEASE_BUS: /* ok, bus allocation accepted */ break; default: error = EINVAL; } return (error); } static int alsmb_clear(struct alsmb_softc *sc) { ALPM_SMBOUTB(sc, SMBSTS, 0xff); DELAY(10); return (0); } #if 0 static int alsmb_abort(struct alsmb_softc *sc) { ALPM_SMBOUTB(sc, SMBCMD, T_OUT_CMD | ABORT_HOST); return (0); } #endif static int alsmb_idle(struct alsmb_softc *sc) { u_char sts; sts = ALPM_SMBINB(sc, SMBSTS); ALPM_DEBUG(printf("alpm: idle? STS=0x%x\n", sts)); return (sts & IDL_STS); } /* * Poll the SMBus controller */ static int alsmb_wait(struct alsmb_softc *sc) { int count = 10000; u_char sts; int error; /* wait for command to complete and SMBus controller is idle */ while(count--) { DELAY(10); sts = ALPM_SMBINB(sc, SMBSTS); if (sts & SMI_I_STS) break; } ALPM_DEBUG(printf("alpm: STS=0x%x\n", sts)); error = SMB_ENOERR; if (!count) error |= SMB_ETIMEOUT; if (sts & TERMINATE) error |= SMB_EABORT; if (sts & BUS_COLLI) error |= SMB_ENOACK; if (sts & DEVICE_ERR) error |= SMB_EBUSERR; if (error != SMB_ENOERR) alsmb_clear(sc); return (error); } static int alsmb_smb_quick(device_t dev, u_char slave, int how) { struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev); int error; alsmb_clear(sc); if (!alsmb_idle(sc)) return (EBUSY); switch (how) { case SMB_QWRITE: ALPM_DEBUG(printf("alpm: QWRITE to 0x%x", slave)); ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); break; case SMB_QREAD: ALPM_DEBUG(printf("alpm: QREAD to 0x%x", slave)); ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); break; default: panic("%s: unknown QUICK command (%x)!", __FUNCTION__, how); } ALPM_SMBOUTB(sc, SMBCMD, SMBQUICK); ALPM_SMBOUTB(sc, SMBSTART, 0xff); error = alsmb_wait(sc); ALPM_DEBUG(printf(", error=0x%x\n", error)); return (error); } static int alsmb_smb_sendb(device_t dev, u_char slave, char byte) { struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev); int error; alsmb_clear(sc); if (!alsmb_idle(sc)) return (SMB_EBUSY); ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE); ALPM_SMBOUTB(sc, SMBHDATA, byte); ALPM_SMBOUTB(sc, SMBSTART, 0xff); error = alsmb_wait(sc); ALPM_DEBUG(printf("alpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error)); return (error); } static int alsmb_smb_recvb(device_t dev, u_char slave, char *byte) { struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev); int error; alsmb_clear(sc); if (!alsmb_idle(sc)) return (SMB_EBUSY); ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE); ALPM_SMBOUTB(sc, SMBSTART, 0xff); if ((error = alsmb_wait(sc)) == SMB_ENOERR) *byte = ALPM_SMBINB(sc, SMBHDATA); ALPM_DEBUG(printf("alpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error)); return (error); } static int alsmb_smb_writeb(device_t dev, u_char slave, char cmd, char byte) { struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev); int error; alsmb_clear(sc); if (!alsmb_idle(sc)) return (SMB_EBUSY); ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE); ALPM_SMBOUTB(sc, SMBHDATA, byte); ALPM_SMBOUTB(sc, SMBHCMD, cmd); ALPM_SMBOUTB(sc, SMBSTART, 0xff); error = alsmb_wait(sc); ALPM_DEBUG(printf("alpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error)); return (error); } static int alsmb_smb_readb(device_t dev, u_char slave, char cmd, char *byte) { struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev); int error; alsmb_clear(sc); if (!alsmb_idle(sc)) return (SMB_EBUSY); ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE); ALPM_SMBOUTB(sc, SMBHCMD, cmd); ALPM_SMBOUTB(sc, SMBSTART, 0xff); if ((error = alsmb_wait(sc)) == SMB_ENOERR) *byte = ALPM_SMBINB(sc, SMBHDATA); ALPM_DEBUG(printf("alpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error)); return (error); } static int alsmb_smb_writew(device_t dev, u_char slave, char cmd, short word) { struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev); int error; alsmb_clear(sc); if (!alsmb_idle(sc)) return (SMB_EBUSY); ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD); ALPM_SMBOUTB(sc, SMBHDATA, word & 0x00ff); ALPM_SMBOUTB(sc, SMBHDATB, (word & 0xff00) >> 8); ALPM_SMBOUTB(sc, SMBHCMD, cmd); ALPM_SMBOUTB(sc, SMBSTART, 0xff); error = alsmb_wait(sc); ALPM_DEBUG(printf("alpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error)); return (error); } static int alsmb_smb_readw(device_t dev, u_char slave, char cmd, short *word) { struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev); int error; u_char high, low; alsmb_clear(sc); if (!alsmb_idle(sc)) return (SMB_EBUSY); ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD); ALPM_SMBOUTB(sc, SMBHCMD, cmd); ALPM_SMBOUTB(sc, SMBSTART, 0xff); if ((error = alsmb_wait(sc)) == SMB_ENOERR) { low = ALPM_SMBINB(sc, SMBHDATA); high = ALPM_SMBINB(sc, SMBHDATB); *word = ((high & 0xff) << 8) | (low & 0xff); } ALPM_DEBUG(printf("alpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error)); return (error); } static int alsmb_smb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) { struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev); u_char remain, len, i; int error = SMB_ENOERR; alsmb_clear(sc); if(!alsmb_idle(sc)) return (SMB_EBUSY); remain = count; while (remain) { len = min(remain, 32); ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); /* set the cmd and reset the * 32-byte long internal buffer */ ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR); ALPM_SMBOUTB(sc, SMBHDATA, len); /* fill the 32-byte internal buffer */ for (i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smbus_if.h" /*This should be removed if force_pci_map_int supported*/ #include #include #include #include #include "opt_intpm.h" static struct _pcsid { pcidi_t type; char *desc; } pci_ids[] = { { 0x71138086,"Intel 82371AB Power management controller"}, { 0x00000000, NULL } }; static int intsmb_probe(device_t); static int intsmb_attach(device_t); static int intsmb_intr(device_t dev); static int intsmb_slvintr(device_t dev); static void intsmb_alrintr(device_t dev); static int intsmb_callback(device_t dev, int index, caddr_t data); static int intsmb_quick(device_t dev, u_char slave, int how); static int intsmb_sendb(device_t dev, u_char slave, char byte); static int intsmb_recvb(device_t dev, u_char slave, char *byte); static int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte); static int intsmb_writew(device_t dev, u_char slave, char cmd, short word); static int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte); static int intsmb_readw(device_t dev, u_char slave, char cmd, short *word); static int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata); static int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf); static int intsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf); static void intsmb_start(device_t dev,u_char cmd,int nointr); static int intsmb_stop(device_t dev); static int intsmb_stop_poll(device_t dev); static int intsmb_free(device_t dev); static int intpm_probe (device_t dev); static int intpm_attach (device_t dev); static devclass_t intsmb_devclass; static device_method_t intpm_methods[]={ DEVMETHOD(device_probe,intsmb_probe), DEVMETHOD(device_attach,intsmb_attach), DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(smbus_callback,intsmb_callback), DEVMETHOD(smbus_quick,intsmb_quick), DEVMETHOD(smbus_sendb,intsmb_sendb), DEVMETHOD(smbus_recvb,intsmb_recvb), DEVMETHOD(smbus_writeb,intsmb_writeb), DEVMETHOD(smbus_writew,intsmb_writew), DEVMETHOD(smbus_readb,intsmb_readb), DEVMETHOD(smbus_readw,intsmb_readw), DEVMETHOD(smbus_pcall,intsmb_pcall), DEVMETHOD(smbus_bwrite,intsmb_bwrite), DEVMETHOD(smbus_bread,intsmb_bread), {0,0} }; struct intpm_pci_softc{ bus_space_tag_t smbst; bus_space_handle_t smbsh; bus_space_tag_t pmst; bus_space_handle_t pmsh; pcici_t cfg; device_t smbus; }; struct intsmb_softc{ struct intpm_pci_softc *pci_sc; bus_space_tag_t st; bus_space_handle_t sh; device_t smbus; int isbusy; }; static driver_t intpm_driver = { "intsmb", intpm_methods, sizeof(struct intsmb_softc), }; static devclass_t intpm_devclass; static device_method_t intpm_pci_methods[] = { DEVMETHOD(device_probe,intpm_probe), DEVMETHOD(device_attach,intpm_attach), {0,0} }; static driver_t intpm_pci_driver = { "intpm", intpm_pci_methods, sizeof(struct intpm_pci_softc) }; static int intsmb_probe(device_t dev) { struct intsmb_softc *sc =(struct intsmb_softc *) device_get_softc(dev); sc->smbus=smbus_alloc_bus(dev); if (!sc->smbus) return (EINVAL); /* XXX don't know what to return else */ device_set_desc(dev,"Intel PIIX4 SMBUS Interface"); return (0); /* XXX don't know what to return else */ } static int intsmb_attach(device_t dev) { struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); sc->pci_sc=device_get_softc(device_get_parent(dev)); sc->isbusy=0; sc->sh=sc->pci_sc->smbsh; sc->st=sc->pci_sc->smbst; sc->pci_sc->smbus=dev; device_probe_and_attach(sc->smbus); #ifdef ENABLE_ALART /*Enable Arart*/ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN); #endif return (0); } static int intsmb_callback(device_t dev, int index, caddr_t data) { int error = 0; intrmask_t s; s=splnet(); switch (index) { case SMB_REQUEST_BUS: break; case SMB_RELEASE_BUS: break; default: error = EINVAL; } splx(s); return (error); } /*counterpart of smbtx_smb_free*/ static int intsmb_free(device_t dev){ intrmask_t s; struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); if((bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS)& PIIX4_SMBHSTSTAT_BUSY) #ifdef ENABLE_ALART ||(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVSTS)& PIIX4_SMBSLVSTS_BUSY) #endif || sc->isbusy) return EBUSY; s=splhigh(); sc->isbusy=1; /*Disable Intrrupt in slave part*/ #ifndef ENABLE_ALART bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,0); #endif /*Reset INTR Flag to prepare INTR*/ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTSTS, (PIIX4_SMBHSTSTAT_INTR| PIIX4_SMBHSTSTAT_ERR| PIIX4_SMBHSTSTAT_BUSC| PIIX4_SMBHSTSTAT_FAIL) ); splx(s); return 0; } static int intsmb_intr(device_t dev) { struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); int status; status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS); if(status&PIIX4_SMBHSTSTAT_BUSY){ return 1; } if(status&(PIIX4_SMBHSTSTAT_INTR| PIIX4_SMBHSTSTAT_ERR| PIIX4_SMBHSTSTAT_BUSC| PIIX4_SMBHSTSTAT_FAIL)){ int tmp; tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT); bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT, tmp&~PIIX4_SMBHSTCNT_INTREN); if(sc->isbusy){ sc->isbusy=0; wakeup(sc); } return 0; } return 1;/* Not Completed*/ } static int intsmb_slvintr(device_t dev) { struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); int status,retval; retval=1; status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVSTS); if(status&PIIX4_SMBSLVSTS_BUSY) return retval; if(status&PIIX4_SMBSLVSTS_ALART){ intsmb_alrintr(dev); retval=0; }else if(status&~(PIIX4_SMBSLVSTS_ALART|PIIX4_SMBSLVSTS_SDW2 |PIIX4_SMBSLVSTS_SDW1)){ retval=0; } /*Reset Status Register*/ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVSTS,PIIX4_SMBSLVSTS_ALART| PIIX4_SMBSLVSTS_SDW2|PIIX4_SMBSLVSTS_SDW1| PIIX4_SMBSLVSTS_SLV); return retval; } static void intsmb_alrintr(device_t dev) { struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); int slvcnt; #ifdef ENABLE_ALART int error; #endif /*stop generating INTR from ALART*/ slvcnt=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVCNT); #ifdef ENABLE_ALART bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT, slvcnt&~PIIX4_SMBSLVCNT_ALTEN) ; #endif DELAY(5); /*ask bus who assert it and then ask it what's the matter. */ #ifdef ENABLE_ALART error=intsmb_free(dev); if(!error){ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,SMBALTRESP |LSB); intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,1); if(!(error=intsmb_stop_poll(dev))){ volatile u_int8_t *addr; addr=bus_space_read_1(sc->st,sc->sh, PIIX4_SMBHSTDAT0); printf("ALART_RESPONSE: %p\n", addr); } }else{ printf("ERROR\n"); } /*Re-enable INTR from ALART*/ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT, slvcnt|PIIX4_SMBSLVCNT_ALTEN) ; DELAY(5); #endif return; } static void intsmb_start(device_t dev,unsigned char cmd,int nointr) { struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); unsigned char tmp; tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT); tmp&= 0xe0; tmp |= cmd; tmp |=PIIX4_SMBHSTCNT_START; /*While not in autoconfiguration Intrrupt Enabled*/ if(!cold||!nointr) tmp |=PIIX4_SMBHSTCNT_INTREN; bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT,tmp); } /*Polling Code. Polling is not encouraged * because It is required to wait for the device get busy. *(29063505.pdf from Intel) * But during boot,intrrupt cannot be used. * so use polling code while in autoconfiguration. */ static int intsmb_stop_poll(device_t dev){ int error,i; struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); /* * In smbtx driver ,Simply waiting. * This loops 100-200 times. */ for(i=0;i<0x7fff;i++){ if((bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS) &PIIX4_SMBHSTSTAT_BUSY)){ break; } } for(i=0;i<0x7fff;i++){ int status; status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS); if(!(status&PIIX4_SMBHSTSTAT_BUSY)){ sc->isbusy=0; error=(status&PIIX4_SMBHSTSTAT_ERR)?EIO : (status&PIIX4_SMBHSTSTAT_BUSC)?EBUSY: (status&PIIX4_SMBHSTSTAT_FAIL)?EIO:0; if(error==0&&!(status&PIIX4_SMBHSTSTAT_INTR)){ printf("unknown cause why?"); } return error; } } { int tmp; sc->isbusy=0; tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT); bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT, tmp&~PIIX4_SMBHSTCNT_INTREN); } return EIO; } /* *wait for completion and return result. */ static int intsmb_stop(device_t dev){ int error; intrmask_t s; struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); if(cold){ /*So that it can use device during probing device on SMBus.*/ error=intsmb_stop_poll(dev); return error; }else{ if(!tsleep(sc,(PWAIT)|PCATCH,"SMBWAI",hz/8)){ int status; status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS); if(!(status&PIIX4_SMBHSTSTAT_BUSY)){ error=(status&PIIX4_SMBHSTSTAT_ERR)?EIO : (status&PIIX4_SMBHSTSTAT_BUSC)?EBUSY: (status&PIIX4_SMBHSTSTAT_FAIL)?EIO:0; if(error==0&&!(status&PIIX4_SMBHSTSTAT_INTR)){ printf("intsmb%d:unknown cause why?\n", device_get_unit(dev)); } #ifdef ENABLE_ALART bus_space_write_1(sc->st,sc->sh, PIIX4_SMBSLVCNT,PIIX4_SMBSLVCNT_ALTEN); #endif return error; } } } /*Timeout Procedure*/ s=splhigh(); sc->isbusy=0; /*Re-enable supressed intrrupt from slave part*/ bus_space_write_1(sc->st,sc->sh, PIIX4_SMBSLVCNT,PIIX4_SMBSLVCNT_ALTEN); splx(s); return EIO; } static int intsmb_quick(device_t dev, u_char slave, int how) { int error=0; u_char data; struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); data=slave; /*Quick command is part of Address, I think*/ switch(how){ case SMB_QWRITE: data&=~LSB; break; case SMB_QREAD: data|=LSB; break; default: error=EINVAL; } if(!error){ error=intsmb_free(dev); if(!error){ bus_space_write_1(sc->st,sc->sh, PIIX4_SMBHSTADD,data); intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_QUICK,0); error=intsmb_stop(dev); } } return (error); } static int intsmb_sendb(device_t dev, u_char slave, char byte) { int error; struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); error=intsmb_free(dev); if(!error){ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB); bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,byte); intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,0); error=intsmb_stop(dev); } return (error); } static int intsmb_recvb(device_t dev, u_char slave, char *byte) { int error; struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); error=intsmb_free(dev); if(!error){ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave |LSB); intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,0); if(!(error=intsmb_stop(dev))){ #ifdef RECV_IS_IN_CMD /*Linux SMBus stuff also troubles Because Intel's datasheet will not make clear. */ *byte=bus_space_read_1(sc->st,sc->sh, PIIX4_SMBHSTCMD); #else *byte=bus_space_read_1(sc->st,sc->sh, PIIX4_SMBHSTDAT0); #endif } } return (error); } static int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte) { int error; struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); error=intsmb_free(dev); if(!error){ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB); bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,byte); intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BDATA,0); error=intsmb_stop(dev); } return (error); } static int intsmb_writew(device_t dev, u_char slave, char cmd, short word) { int error; struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); error=intsmb_free(dev); if(!error){ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB); bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0, word&0xff); bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1, (word>>8)&0xff); intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0); error=intsmb_stop(dev); } return (error); } static int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte) { int error; struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); error=intsmb_free(dev); if(!error){ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB); bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BDATA,0); if(!(error=intsmb_stop(dev))){ *byte=bus_space_read_1(sc->st,sc->sh, PIIX4_SMBHSTDAT0); } } return (error); } static int intsmb_readw(device_t dev, u_char slave, char cmd, short *word) { int error; struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); error=intsmb_free(dev); if(!error){ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB); bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0); if(!(error=intsmb_stop(dev))){ *word=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0)&0xff; *word|=(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1)&0xff)<<8; } } return (error); } /* * Data sheet claims that it implements all function, but also claims * that it implements 7 function and not mention PCALL. So I don't know * whether it will work. */ static int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata) { #ifdef PROCCALL_TEST int error; struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); error=intsmb_free(dev); if(!error){ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB); bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,sdata&0xff); bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1,(sdata&0xff)>>8); intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0); } if(!(error=intsmb_stop(dev))){ *rdata=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0)&0xff; *rdata|=(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1)&0xff)<<8; } return error; #else return 0; #endif } static int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) { int error,i; struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); error=intsmb_free(dev); if(count>SMBBLOCKTRANS_MAX||count==0) error=EINVAL; if(!error){ /*Reset internal array index*/ bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT); bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB); bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); for(i=0;ist,sc->sh,PIIX4_SMBBLKDAT,buf[i]); } bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,count); intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BLOCK,0); error=intsmb_stop(dev); } return (error); } static int intsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf) { int error,i; struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); error=intsmb_free(dev); if(count>SMBBLOCKTRANS_MAX||count==0) error=EINVAL; if(!error){ /*Reset internal array index*/ bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT); bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB); bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,count); intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BLOCK,0); error=intsmb_stop(dev); if(!error){ bzero(buf,count);/*Is it needed?*/ count= bus_space_read_1(sc->st,sc->sh, PIIX4_SMBHSTDAT0); if(count!=0&&count<=SMBBLOCKTRANS_MAX){ for(i=0;ist, sc->sh, PIIX4_SMBBLKDAT); } } else{ error=EIO; } } } return (error); } DRIVER_MODULE(intsmb, intpm , intpm_driver, intsmb_devclass, 0, 0); static void intpm_intr __P((void *arg)); static int intpm_attach(device_t dev) { int value; int unit=device_get_unit(dev); void *ih; int error; char * str; { struct intpm_pci_softc *sciic; device_t smbinterface; int rid; struct resource *res; sciic=device_get_softc(dev); if(sciic==NULL){ return ENOMEM; } rid=PCI_BASE_ADDR_SMB; res=bus_alloc_resource(dev,SYS_RES_IOPORT,&rid, 0,~0,1,RF_ACTIVE); if(res==NULL){ device_printf(dev,"Could not allocate Bus space\n"); return ENXIO; } sciic->smbst=rman_get_bustag(res); sciic->smbsh=rman_get_bushandle(res); device_printf(dev,"%s %x\n", (sciic->smbst==I386_BUS_SPACE_IO)? "I/O mapped":"Memory", sciic->smbsh); #ifndef NO_CHANGE_PCICONF pci_write_config(dev,PCIR_INTLINE,0x9,1); pci_write_config(dev,PCI_HST_CFG_SMB, PCI_INTR_SMB_IRQ9|PCI_INTR_SMB_ENABLE,1); #endif value=pci_read_config(dev,PCI_HST_CFG_SMB,1); switch(value&0xe){ case PCI_INTR_SMB_SMI: str="SMI"; break; case PCI_INTR_SMB_IRQ9: str="IRQ 9"; break; default: str="BOGUS"; } device_printf(dev,"intr %s %s ",str,((value&1)? "enabled":"disabled")); value=pci_read_config(dev,PCI_REVID_SMB,1); printf("revision %d\n",value); /* * Install intr HANDLER here */ rid=0; res=bus_alloc_resource(dev,SYS_RES_IRQ,&rid,9,9,1,RF_SHAREABLE|RF_ACTIVE); if(res==NULL){ device_printf(dev,"could not allocate irq"); return ENOMEM; } error=bus_setup_intr(dev,res,INTR_TYPE_MISC, (driver_intr_t *) intpm_intr,sciic,&ih); if(error){ device_printf(dev,"Failed to map intr\n"); return error; } - smbinterface=device_add_child(dev,"intsmb",unit,NULL); + smbinterface=device_add_child(dev,"intsmb",unit); if(!smbinterface){ printf("intsmb%d:could not add SMBus device\n",unit); } device_probe_and_attach(smbinterface); } value=pci_read_config(dev,PCI_BASE_ADDR_PM,4); printf("intpm%d: PM %s %x \n",unit,(value&1)?"I/O mapped":"Memory",value&0xfffe); return 0; } static int intpm_probe(device_t dev) { struct _pcsid *ep =pci_ids; u_int32_t device_id=pci_get_devid(dev); while (ep->type && ep->type != device_id) ++ep; if(ep->desc!=NULL){ device_set_desc(dev,ep->desc); bus_set_resource(dev,SYS_RES_IRQ,0,9,1); /* XXX setup intr resource */ return 0; }else{ return ENXIO; } } DRIVER_MODULE(intpm, pci , intpm_pci_driver, intpm_devclass, 0, 0); static void intpm_intr(void *arg) { struct intpm_pci_softc *sc; sc=(struct intpm_pci_softc *)arg; intsmb_intr(sc->smbus); intsmb_slvintr(sc->smbus); } Index: head/sys/pci/ohci_pci.c =================================================================== --- head/sys/pci/ohci_pci.c (revision 54072) +++ head/sys/pci/ohci_pci.c (revision 54073) @@ -1,266 +1,267 @@ /* $FreeBSD$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * USB Open Host Controller driver. * * OHCI spec: http://www.intel.com/design/usb/ohci11d.pdf */ /* The low level controller code for OHCI has been split into * PCI probes and OHCI specific code. This was done to facilitate the * sharing of code between *BSD's */ #include "opt_bus.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PCI_OHCI_VENDORID_ALI 0x10b9 #define PCI_OHCI_VENDORID_CMDTECH 0x1095 #define PCI_OHCI_VENDORID_COMPAQ 0x0e11 #define PCI_OHCI_VENDORID_NEC 0x1033 #define PCI_OHCI_VENDORID_OPTI 0x1045 #define PCI_OHCI_VENDORID_SIS 0x1039 #define PCI_OHCI_DEVICEID_ALADDIN_V 0x523710b9 static const char *ohci_device_aladdin_v = "AcerLabs M5237 (Aladdin-V) USB controller"; #define PCI_OHCI_DEVICEID_FIRELINK 0xc8611045 static const char *ohci_device_firelink = "OPTi 82C861 (FireLink) USB controller"; #define PCI_OHCI_DEVICEID_NEC 0x00351033 static const char *ohci_device_nec = "NEC uPD 9210 USB controller"; #define PCI_OHCI_DEVICEID_USB0670 0x06701095 static const char *ohci_device_usb0670 = "CMD Tech 670 (USB0670) USB controller"; #define PCI_OHCI_DEVICEID_USB0673 0x06731095 static const char *ohci_device_usb0673 = "CMD Tech 673 (USB0673) USB controller"; static const char *ohci_device_generic = "OHCI (generic) USB controller"; #define PCI_OHCI_BASE_REG 0x10 static const char * ohci_pci_match(device_t self) { u_int32_t device_id = pci_get_devid(self); switch(device_id) { case PCI_OHCI_DEVICEID_ALADDIN_V: return (ohci_device_aladdin_v); case PCI_OHCI_DEVICEID_USB0670: return (ohci_device_usb0670); case PCI_OHCI_DEVICEID_USB0673: return (ohci_device_usb0673); case PCI_OHCI_DEVICEID_FIRELINK: return (ohci_device_firelink); case PCI_OHCI_DEVICEID_NEC: return (ohci_device_nec); default: if ( pci_get_class(self) == PCIC_SERIALBUS && pci_get_subclass(self) == PCIS_SERIALBUS_USB && pci_get_progif(self) == PCI_INTERFACE_OHCI) { return (ohci_device_generic); } } return NULL; /* dunno */ } static int ohci_pci_probe(device_t self) { const char *desc = ohci_pci_match(self); if (desc) { device_set_desc(self, desc); return 0; } else { return ENXIO; } } static int ohci_pci_attach(device_t self) { device_t parent = device_get_parent(self); ohci_softc_t *sc = device_get_softc(self); device_t usbus; int err; int rid; struct resource *res; void *ih; int intr; /* XXX where does it say so in the spec? */ sc->sc_bus.usbrev = USBREV_1_0; rid = PCI_CBMEM; res = bus_alloc_resource(self, SYS_RES_MEMORY, &rid, 0, ~0, 1, RF_ACTIVE); if (!res) { device_printf(self, "could not map memory\n"); return ENXIO; } sc->iot = rman_get_bustag(res); sc->ioh = rman_get_bushandle(res); rid = 0; res = bus_alloc_resource(self, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); if (res == NULL) { device_printf(self, "could not allocate irq\n"); return ENOMEM; } - usbus = device_add_child(self, "usb", -1, sc); + usbus = device_add_child(self, "usb", -1); + device_set_ivars(usbus, sc); if (!usbus) { device_printf(self, "could not add USB device\n"); return ENOMEM; } switch (pci_get_devid(self)) { case PCI_OHCI_DEVICEID_ALADDIN_V: device_set_desc(usbus, ohci_device_aladdin_v); sprintf(sc->sc_vendor, "AcerLabs"); break; case PCI_OHCI_DEVICEID_FIRELINK: device_set_desc(usbus, ohci_device_firelink); sprintf(sc->sc_vendor, "OPTi"); break; case PCI_OHCI_DEVICEID_NEC: device_set_desc(usbus, ohci_device_nec); sprintf(sc->sc_vendor, "NEC"); break; case PCI_OHCI_DEVICEID_USB0670: device_set_desc(usbus, ohci_device_usb0670); sprintf(sc->sc_vendor, "CMDTECH"); break; case PCI_OHCI_DEVICEID_USB0673: device_set_desc(usbus, ohci_device_usb0673); sprintf(sc->sc_vendor, "CMDTECH"); break; default: if (bootverbose) device_printf(self, "(New OHCI DeviceId=0x%08x)\n", pci_get_devid(self)); device_set_desc(usbus, ohci_device_generic); sprintf(sc->sc_vendor, "(unknown)"); } intr = pci_read_config(self, PCIR_INTLINE, 1); if (intr == 0 || intr == 255) { device_printf(self, "Invalid irq %d\n", intr); device_printf(self, "Please switch on USB support and switch PNP-OS to 'No' in BIOS\n"); device_delete_child(self, usbus); return ENXIO; } err = BUS_SETUP_INTR(parent, self, res, INTR_TYPE_BIO, (driver_intr_t *) ohci_intr, sc, &ih); if (err) { device_printf(self, "could not setup irq, %d\n", err); device_delete_child(self, usbus); return err; } sc->sc_bus.bdev = usbus; err = ohci_init(sc); if (!err) err = device_probe_and_attach(sc->sc_bus.bdev); if (err) { device_printf(self, "init failed\n"); /* disable interrupts */ bus_space_write_4(sc->iot, sc->ioh, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); err = BUS_TEARDOWN_INTR(parent, self, res, ih); if (err) /* XXX or should we panic? */ device_printf(self, "could not tear down irq, %d\n", err); device_delete_child(self, usbus); return EIO; } return 0; } static device_method_t ohci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ohci_pci_probe), DEVMETHOD(device_attach, ohci_pci_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), { 0, 0 } }; static driver_t ohci_driver = { "ohci", ohci_methods, sizeof(ohci_softc_t), }; static devclass_t ohci_devclass; DRIVER_MODULE(ohci, pci, ohci_driver, ohci_devclass, 0, 0); Index: head/sys/pci/pci.c =================================================================== --- head/sys/pci/pci.c (revision 54072) +++ head/sys/pci/pci.c (revision 54073) @@ -1,1433 +1,1433 @@ /* * Copyright (c) 1997, Stefan Esser * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * */ #include "opt_bus.h" #include "opt_simos.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* For the Alpha */ #include #include #include #ifdef APIC_IO #include #endif /* APIC_IO */ struct pci_quirk { u_int32_t devid; /* Vendor/device of the card */ int type; #define PCI_QUIRK_MAP_REG 1 /* PCI map register in wierd place */ int arg1; int arg2; }; struct pci_quirk pci_quirks[] = { /* * The Intel 82371AB has a map register at offset 0x90. */ { 0x71138086, PCI_QUIRK_MAP_REG, 0x90, 0 }, { 0 } }; /* map register information */ #define PCI_MAPMEM 0x01 /* memory map */ #define PCI_MAPMEMP 0x02 /* prefetchable memory map */ #define PCI_MAPPORT 0x04 /* port map */ struct pci_devinfo { STAILQ_ENTRY(pci_devinfo) pci_links; struct resource_list resources; pcicfgregs cfg; struct pci_conf conf; }; static STAILQ_HEAD(devlist, pci_devinfo) pci_devq; u_int32_t pci_numdevs = 0; static u_int32_t pci_generation = 0; /* return base address of memory or port map */ static u_int32_t pci_mapbase(unsigned mapreg) { int mask = 0x03; if ((mapreg & 0x01) == 0) mask = 0x0f; return (mapreg & ~mask); } /* return map type of memory or port map */ static int pci_maptype(unsigned mapreg) { static u_int8_t maptype[0x10] = { PCI_MAPMEM, PCI_MAPPORT, PCI_MAPMEM, 0, PCI_MAPMEM, PCI_MAPPORT, 0, 0, PCI_MAPMEM|PCI_MAPMEMP, PCI_MAPPORT, PCI_MAPMEM|PCI_MAPMEMP, 0, PCI_MAPMEM|PCI_MAPMEMP, PCI_MAPPORT, 0, 0, }; return maptype[mapreg & 0x0f]; } /* return log2 of map size decoded for memory or port map */ static int pci_mapsize(unsigned testval) { int ln2size; testval = pci_mapbase(testval); ln2size = 0; if (testval != 0) { while ((testval & 1) == 0) { ln2size++; testval >>= 1; } } return (ln2size); } /* return log2 of address range supported by map register */ static int pci_maprange(unsigned mapreg) { int ln2range = 0; switch (mapreg & 0x07) { case 0x00: case 0x01: case 0x05: ln2range = 32; break; case 0x02: ln2range = 20; break; case 0x04: ln2range = 64; break; } return (ln2range); } /* adjust some values from PCI 1.0 devices to match 2.0 standards ... */ static void pci_fixancient(pcicfgregs *cfg) { if (cfg->hdrtype != 0) return; /* PCI to PCI bridges use header type 1 */ if (cfg->baseclass == PCIC_BRIDGE && cfg->subclass == PCIS_BRIDGE_PCI) cfg->hdrtype = 1; } /* read config data specific to header type 1 device (PCI to PCI bridge) */ static void * pci_readppb(pcicfgregs *cfg) { pcih1cfgregs *p; p = malloc(sizeof (pcih1cfgregs), M_DEVBUF, M_WAITOK); if (p == NULL) return (NULL); bzero(p, sizeof *p); p->secstat = pci_cfgread(cfg, PCIR_SECSTAT_1, 2); p->bridgectl = pci_cfgread(cfg, PCIR_BRIDGECTL_1, 2); p->seclat = pci_cfgread(cfg, PCIR_SECLAT_1, 1); p->iobase = PCI_PPBIOBASE (pci_cfgread(cfg, PCIR_IOBASEH_1, 2), pci_cfgread(cfg, PCIR_IOBASEL_1, 1)); p->iolimit = PCI_PPBIOLIMIT (pci_cfgread(cfg, PCIR_IOLIMITH_1, 2), pci_cfgread(cfg, PCIR_IOLIMITL_1, 1)); p->membase = PCI_PPBMEMBASE (0, pci_cfgread(cfg, PCIR_MEMBASE_1, 2)); p->memlimit = PCI_PPBMEMLIMIT (0, pci_cfgread(cfg, PCIR_MEMLIMIT_1, 2)); p->pmembase = PCI_PPBMEMBASE ( (pci_addr_t)pci_cfgread(cfg, PCIR_PMBASEH_1, 4), pci_cfgread(cfg, PCIR_PMBASEL_1, 2)); p->pmemlimit = PCI_PPBMEMLIMIT ( (pci_addr_t)pci_cfgread(cfg, PCIR_PMLIMITH_1, 4), pci_cfgread(cfg, PCIR_PMLIMITL_1, 2)); return (p); } /* read config data specific to header type 2 device (PCI to CardBus bridge) */ static void * pci_readpcb(pcicfgregs *cfg) { pcih2cfgregs *p; p = malloc(sizeof (pcih2cfgregs), M_DEVBUF, M_WAITOK); if (p == NULL) return (NULL); bzero(p, sizeof *p); p->secstat = pci_cfgread(cfg, PCIR_SECSTAT_2, 2); p->bridgectl = pci_cfgread(cfg, PCIR_BRIDGECTL_2, 2); p->seclat = pci_cfgread(cfg, PCIR_SECLAT_2, 1); p->membase0 = pci_cfgread(cfg, PCIR_MEMBASE0_2, 4); p->memlimit0 = pci_cfgread(cfg, PCIR_MEMLIMIT0_2, 4); p->membase1 = pci_cfgread(cfg, PCIR_MEMBASE1_2, 4); p->memlimit1 = pci_cfgread(cfg, PCIR_MEMLIMIT1_2, 4); p->iobase0 = pci_cfgread(cfg, PCIR_IOBASE0_2, 4); p->iolimit0 = pci_cfgread(cfg, PCIR_IOLIMIT0_2, 4); p->iobase1 = pci_cfgread(cfg, PCIR_IOBASE1_2, 4); p->iolimit1 = pci_cfgread(cfg, PCIR_IOLIMIT1_2, 4); p->pccardif = pci_cfgread(cfg, PCIR_PCCARDIF_2, 4); return p; } /* extract header type specific config data */ static void pci_hdrtypedata(pcicfgregs *cfg) { switch (cfg->hdrtype) { case 0: cfg->subvendor = pci_cfgread(cfg, PCIR_SUBVEND_0, 2); cfg->subdevice = pci_cfgread(cfg, PCIR_SUBDEV_0, 2); cfg->nummaps = PCI_MAXMAPS_0; break; case 1: cfg->subvendor = pci_cfgread(cfg, PCIR_SUBVEND_1, 2); cfg->subdevice = pci_cfgread(cfg, PCIR_SUBDEV_1, 2); cfg->secondarybus = pci_cfgread(cfg, PCIR_SECBUS_1, 1); cfg->subordinatebus = pci_cfgread(cfg, PCIR_SUBBUS_1, 1); cfg->nummaps = PCI_MAXMAPS_1; cfg->hdrspec = pci_readppb(cfg); break; case 2: cfg->subvendor = pci_cfgread(cfg, PCIR_SUBVEND_2, 2); cfg->subdevice = pci_cfgread(cfg, PCIR_SUBDEV_2, 2); cfg->secondarybus = pci_cfgread(cfg, PCIR_SECBUS_2, 1); cfg->subordinatebus = pci_cfgread(cfg, PCIR_SUBBUS_2, 1); cfg->nummaps = PCI_MAXMAPS_2; cfg->hdrspec = pci_readpcb(cfg); break; } } /* read configuration header into pcicfgrect structure */ static struct pci_devinfo * pci_readcfg(pcicfgregs *probe) { pcicfgregs *cfg = NULL; struct pci_devinfo *devlist_entry; struct devlist *devlist_head; devlist_head = &pci_devq; devlist_entry = NULL; if (pci_cfgread(probe, PCIR_DEVVENDOR, 4) != -1) { devlist_entry = malloc(sizeof(struct pci_devinfo), M_DEVBUF, M_WAITOK); if (devlist_entry == NULL) return (NULL); bzero(devlist_entry, sizeof *devlist_entry); cfg = &devlist_entry->cfg; cfg->hose = probe->hose; cfg->bus = probe->bus; cfg->slot = probe->slot; cfg->func = probe->func; cfg->vendor = pci_cfgread(cfg, PCIR_VENDOR, 2); cfg->device = pci_cfgread(cfg, PCIR_DEVICE, 2); cfg->cmdreg = pci_cfgread(cfg, PCIR_COMMAND, 2); cfg->statreg = pci_cfgread(cfg, PCIR_STATUS, 2); cfg->baseclass = pci_cfgread(cfg, PCIR_CLASS, 1); cfg->subclass = pci_cfgread(cfg, PCIR_SUBCLASS, 1); cfg->progif = pci_cfgread(cfg, PCIR_PROGIF, 1); cfg->revid = pci_cfgread(cfg, PCIR_REVID, 1); cfg->hdrtype = pci_cfgread(cfg, PCIR_HEADERTYPE, 1); cfg->cachelnsz = pci_cfgread(cfg, PCIR_CACHELNSZ, 1); cfg->lattimer = pci_cfgread(cfg, PCIR_LATTIMER, 1); cfg->intpin = pci_cfgread(cfg, PCIR_INTPIN, 1); cfg->intline = pci_cfgread(cfg, PCIR_INTLINE, 1); #ifdef __alpha__ alpha_platform_assign_pciintr(cfg); #endif #ifdef APIC_IO if (cfg->intpin != 0) { int airq; airq = pci_apic_irq(cfg->bus, cfg->slot, cfg->intpin); if (airq >= 0) { /* PCI specific entry found in MP table */ if (airq != cfg->intline) { undirect_pci_irq(cfg->intline); cfg->intline = airq; } } else { /* * PCI interrupts might be redirected to the * ISA bus according to some MP tables. Use the * same methods as used by the ISA devices * devices to find the proper IOAPIC int pin. */ airq = isa_apic_irq(cfg->intline); if ((airq >= 0) && (airq != cfg->intline)) { /* XXX: undirect_pci_irq() ? */ undirect_isa_irq(cfg->intline); cfg->intline = airq; } } } #endif /* APIC_IO */ cfg->mingnt = pci_cfgread(cfg, PCIR_MINGNT, 1); cfg->maxlat = pci_cfgread(cfg, PCIR_MAXLAT, 1); cfg->mfdev = (cfg->hdrtype & PCIM_MFDEV) != 0; cfg->hdrtype &= ~PCIM_MFDEV; pci_fixancient(cfg); pci_hdrtypedata(cfg); STAILQ_INSERT_TAIL(devlist_head, devlist_entry, pci_links); devlist_entry->conf.pc_sel.pc_bus = cfg->bus; devlist_entry->conf.pc_sel.pc_dev = cfg->slot; devlist_entry->conf.pc_sel.pc_func = cfg->func; devlist_entry->conf.pc_hdr = cfg->hdrtype; devlist_entry->conf.pc_subvendor = cfg->subvendor; devlist_entry->conf.pc_subdevice = cfg->subdevice; devlist_entry->conf.pc_vendor = cfg->vendor; devlist_entry->conf.pc_device = cfg->device; devlist_entry->conf.pc_class = cfg->baseclass; devlist_entry->conf.pc_subclass = cfg->subclass; devlist_entry->conf.pc_progif = cfg->progif; devlist_entry->conf.pc_revid = cfg->revid; pci_numdevs++; pci_generation++; } return (devlist_entry); } #if 0 /* free pcicfgregs structure and all depending data structures */ static int pci_freecfg(struct pci_devinfo *dinfo) { struct devlist *devlist_head; devlist_head = &pci_devq; if (dinfo->cfg.hdrspec != NULL) free(dinfo->cfg.hdrspec, M_DEVBUF); if (dinfo->cfg.map != NULL) free(dinfo->cfg.map, M_DEVBUF); /* XXX this hasn't been tested */ STAILQ_REMOVE(devlist_head, dinfo, pci_devinfo, pci_links); free(dinfo, M_DEVBUF); /* increment the generation count */ pci_generation++; /* we're losing one device */ pci_numdevs--; return (0); } #endif /* * This is the user interface to PCI configuration space. */ static int pci_open(dev_t dev, int oflags, int devtype, struct proc *p) { if ((oflags & FWRITE) && securelevel > 0) { return EPERM; } return 0; } static int pci_close(dev_t dev, int flag, int devtype, struct proc *p) { return 0; } /* * Match a single pci_conf structure against an array of pci_match_conf * structures. The first argument, 'matches', is an array of num_matches * pci_match_conf structures. match_buf is a pointer to the pci_conf * structure that will be compared to every entry in the matches array. * This function returns 1 on failure, 0 on success. */ static int pci_conf_match(struct pci_match_conf *matches, int num_matches, struct pci_conf *match_buf) { int i; if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) return(1); for (i = 0; i < num_matches; i++) { /* * I'm not sure why someone would do this...but... */ if (matches[i].flags == PCI_GETCONF_NO_MATCH) continue; /* * Look at each of the match flags. If it's set, do the * comparison. If the comparison fails, we don't have a * match, go on to the next item if there is one. */ if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0) && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0) && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0) && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0) && (match_buf->pc_vendor != matches[i].pc_vendor)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0) && (match_buf->pc_device != matches[i].pc_device)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0) && (match_buf->pc_class != matches[i].pc_class)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0) && (match_buf->pd_unit != matches[i].pd_unit)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0) && (strncmp(matches[i].pd_name, match_buf->pd_name, sizeof(match_buf->pd_name)) != 0)) continue; return(0); } return(1); } /* * Locate the parent of a PCI device by scanning the PCI devlist * and return the entry for the parent. * For devices on PCI Bus 0 (the host bus), this is the PCI Host. * For devices on secondary PCI busses, this is that bus' PCI-PCI Bridge. */ pcicfgregs * pci_devlist_get_parent(pcicfgregs *cfg) { struct devlist *devlist_head; struct pci_devinfo *dinfo; pcicfgregs *bridge_cfg; int i; dinfo = STAILQ_FIRST(devlist_head = &pci_devq); /* If the device is on PCI bus 0, look for the host */ if (cfg->bus == 0) { for (i = 0; (dinfo != NULL) && (i < pci_numdevs); dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { bridge_cfg = &dinfo->cfg; if (bridge_cfg->baseclass == PCIC_BRIDGE && bridge_cfg->subclass == PCIS_BRIDGE_HOST && bridge_cfg->bus == cfg->bus) { return bridge_cfg; } } } /* If the device is not on PCI bus 0, look for the PCI-PCI bridge */ if (cfg->bus > 0) { for (i = 0; (dinfo != NULL) && (i < pci_numdevs); dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { bridge_cfg = &dinfo->cfg; if (bridge_cfg->baseclass == PCIC_BRIDGE && bridge_cfg->subclass == PCIS_BRIDGE_PCI && bridge_cfg->secondarybus == cfg->bus) { return bridge_cfg; } } } return NULL; } static int pci_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { struct pci_io *io; const char *name; int error; if (!(flag & FWRITE)) return EPERM; switch(cmd) { case PCIOCGETCONF: { struct pci_devinfo *dinfo; struct pci_conf_io *cio; struct devlist *devlist_head; struct pci_match_conf *pattern_buf; int num_patterns; size_t iolen; int ionum, i; cio = (struct pci_conf_io *)data; num_patterns = 0; dinfo = NULL; /* * Hopefully the user won't pass in a null pointer, but it * can't hurt to check. */ if (cio == NULL) { error = EINVAL; break; } /* * If the user specified an offset into the device list, * but the list has changed since they last called this * ioctl, tell them that the list has changed. They will * have to get the list from the beginning. */ if ((cio->offset != 0) && (cio->generation != pci_generation)){ cio->num_matches = 0; cio->status = PCI_GETCONF_LIST_CHANGED; error = 0; break; } /* * Check to see whether the user has asked for an offset * past the end of our list. */ if (cio->offset >= pci_numdevs) { cio->num_matches = 0; cio->status = PCI_GETCONF_LAST_DEVICE; error = 0; break; } /* get the head of the device queue */ devlist_head = &pci_devq; /* * Determine how much room we have for pci_conf structures. * Round the user's buffer size down to the nearest * multiple of sizeof(struct pci_conf) in case the user * didn't specify a multiple of that size. */ iolen = min(cio->match_buf_len - (cio->match_buf_len % sizeof(struct pci_conf)), pci_numdevs * sizeof(struct pci_conf)); /* * Since we know that iolen is a multiple of the size of * the pciconf union, it's okay to do this. */ ionum = iolen / sizeof(struct pci_conf); /* * If this test is true, the user wants the pci_conf * structures returned to match the supplied entries. */ if ((cio->num_patterns > 0) && (cio->pat_buf_len > 0)) { /* * pat_buf_len needs to be: * num_patterns * sizeof(struct pci_match_conf) * While it is certainly possible the user just * allocated a large buffer, but set the number of * matches correctly, it is far more likely that * their kernel doesn't match the userland utility * they're using. It's also possible that the user * forgot to initialize some variables. Yes, this * may be overly picky, but I hazard to guess that * it's far more likely to just catch folks that * updated their kernel but not their userland. */ if ((cio->num_patterns * sizeof(struct pci_match_conf)) != cio->pat_buf_len){ /* The user made a mistake, return an error*/ cio->status = PCI_GETCONF_ERROR; printf("pci_ioctl: pat_buf_len %d != " "num_patterns (%d) * sizeof(struct " "pci_match_conf) (%d)\npci_ioctl: " "pat_buf_len should be = %d\n", cio->pat_buf_len, cio->num_patterns, (int)sizeof(struct pci_match_conf), (int)sizeof(struct pci_match_conf) * cio->num_patterns); printf("pci_ioctl: do your headers match your " "kernel?\n"); cio->num_matches = 0; error = EINVAL; break; } /* * Check the user's buffer to make sure it's readable. */ if (!useracc((caddr_t)cio->patterns, cio->pat_buf_len, VM_PROT_READ)) { printf("pci_ioctl: pattern buffer %p, " "length %u isn't user accessible for" " READ\n", cio->patterns, cio->pat_buf_len); error = EACCES; break; } /* * Allocate a buffer to hold the patterns. */ pattern_buf = malloc(cio->pat_buf_len, M_TEMP, M_WAITOK); error = copyin(cio->patterns, pattern_buf, cio->pat_buf_len); if (error != 0) break; num_patterns = cio->num_patterns; } else if ((cio->num_patterns > 0) || (cio->pat_buf_len > 0)) { /* * The user made a mistake, spit out an error. */ cio->status = PCI_GETCONF_ERROR; cio->num_matches = 0; printf("pci_ioctl: invalid GETCONF arguments\n"); error = EINVAL; break; } else pattern_buf = NULL; /* * Make sure we can write to the match buffer. */ if (!useracc((caddr_t)cio->matches, cio->match_buf_len, VM_PROT_WRITE)) { printf("pci_ioctl: match buffer %p, length %u " "isn't user accessible for WRITE\n", cio->matches, cio->match_buf_len); error = EACCES; break; } /* * Go through the list of devices and copy out the devices * that match the user's criteria. */ for (cio->num_matches = 0, error = 0, i = 0, dinfo = STAILQ_FIRST(devlist_head); (dinfo != NULL) && (cio->num_matches < ionum) && (error == 0) && (i < pci_numdevs); dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { if (i < cio->offset) continue; /* Populate pd_name and pd_unit */ name = NULL; if (dinfo->cfg.dev && dinfo->conf.pd_name[0] == '\0') name = device_get_name(dinfo->cfg.dev); if (name) { strncpy(dinfo->conf.pd_name, name, sizeof(dinfo->conf.pd_name)); dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0; dinfo->conf.pd_unit = device_get_unit(dinfo->cfg.dev); } if ((pattern_buf == NULL) || (pci_conf_match(pattern_buf, num_patterns, &dinfo->conf) == 0)) { /* * If we've filled up the user's buffer, * break out at this point. Since we've * got a match here, we'll pick right back * up at the matching entry. We can also * tell the user that there are more matches * left. */ if (cio->num_matches >= ionum) break; error = copyout(&dinfo->conf, &cio->matches[cio->num_matches], sizeof(struct pci_conf)); cio->num_matches++; } } /* * Set the pointer into the list, so if the user is getting * n records at a time, where n < pci_numdevs, */ cio->offset = i; /* * Set the generation, the user will need this if they make * another ioctl call with offset != 0. */ cio->generation = pci_generation; /* * If this is the last device, inform the user so he won't * bother asking for more devices. If dinfo isn't NULL, we * know that there are more matches in the list because of * the way the traversal is done. */ if (dinfo == NULL) cio->status = PCI_GETCONF_LAST_DEVICE; else cio->status = PCI_GETCONF_MORE_DEVS; if (pattern_buf != NULL) free(pattern_buf, M_TEMP); break; } case PCIOCREAD: io = (struct pci_io *)data; switch(io->pi_width) { pcicfgregs probe; case 4: case 2: case 1: probe.hose = -1; probe.bus = io->pi_sel.pc_bus; probe.slot = io->pi_sel.pc_dev; probe.func = io->pi_sel.pc_func; io->pi_data = pci_cfgread(&probe, io->pi_reg, io->pi_width); error = 0; break; default: error = ENODEV; break; } break; case PCIOCWRITE: io = (struct pci_io *)data; switch(io->pi_width) { pcicfgregs probe; case 4: case 2: case 1: probe.hose = -1; probe.bus = io->pi_sel.pc_bus; probe.slot = io->pi_sel.pc_dev; probe.func = io->pi_sel.pc_func; pci_cfgwrite(&probe, io->pi_reg, io->pi_data, io->pi_width); error = 0; break; default: error = ENODEV; break; } break; default: error = ENOTTY; break; } return (error); } #define PCI_CDEV 78 static struct cdevsw pcicdev = { /* open */ pci_open, /* close */ pci_close, /* read */ noread, /* write */ nowrite, /* ioctl */ pci_ioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "pci", /* maj */ PCI_CDEV, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; #include "pci_if.h" /* * A simple driver to wrap the old pci driver mechanism for back-compat. */ static int pci_compat_probe(device_t dev) { struct pci_device *dvp; struct pci_devinfo *dinfo; pcicfgregs *cfg; const char *name; int error; dinfo = device_get_ivars(dev); cfg = &dinfo->cfg; dvp = device_get_driver(dev)->priv; /* * Do the wrapped probe. */ error = ENXIO; if (dvp && dvp->pd_probe) { name = dvp->pd_probe(cfg, (cfg->device << 16) + cfg->vendor); if (name) { device_set_desc_copy(dev, name); /* Allow newbus drivers to match "better" */ error = -1000; } } return error; } static int pci_compat_attach(device_t dev) { struct pci_device *dvp; struct pci_devinfo *dinfo; pcicfgregs *cfg; int unit; dinfo = device_get_ivars(dev); cfg = &dinfo->cfg; dvp = device_get_driver(dev)->priv; unit = device_get_unit(dev); if (unit > *dvp->pd_count) *dvp->pd_count = unit; if (dvp->pd_attach) dvp->pd_attach(cfg, unit); return 0; } static device_method_t pci_compat_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pci_compat_probe), DEVMETHOD(device_attach, pci_compat_attach), { 0, 0 } }; static devclass_t pci_devclass; /* * Create a new style driver around each old pci driver. */ int compat_pci_handler(module_t mod, int type, void *data) { struct pci_device *dvp = (struct pci_device *)data; driver_t *driver; switch (type) { case MOD_LOAD: driver = malloc(sizeof(driver_t), M_DEVBUF, M_NOWAIT); if (!driver) return ENOMEM; bzero(driver, sizeof(driver_t)); driver->name = dvp->pd_name; driver->methods = pci_compat_methods; driver->softc = sizeof(struct pci_devinfo *); driver->priv = dvp; devclass_add_driver(pci_devclass, driver); break; case MOD_UNLOAD: printf("%s: module unload not supported!\n", dvp->pd_name); return EOPNOTSUPP; default: break; } return 0; } /* * New style pci driver. Parent device is either a pci-host-bridge or a * pci-pci-bridge. Both kinds are represented by instances of pcib. */ static void pci_print_verbose(struct pci_devinfo *dinfo) { if (bootverbose) { pcicfgregs *cfg = &dinfo->cfg; printf("found->\tvendor=0x%04x, dev=0x%04x, revid=0x%02x\n", cfg->vendor, cfg->device, cfg->revid); printf("\tclass=%02x-%02x-%02x, hdrtype=0x%02x, mfdev=%d\n", cfg->baseclass, cfg->subclass, cfg->progif, cfg->hdrtype, cfg->mfdev); printf("\tsubordinatebus=%x \tsecondarybus=%x\n", cfg->subordinatebus, cfg->secondarybus); #ifdef PCI_DEBUG printf("\tcmdreg=0x%04x, statreg=0x%04x, cachelnsz=%d (dwords)\n", cfg->cmdreg, cfg->statreg, cfg->cachelnsz); printf("\tlattimer=0x%02x (%d ns), mingnt=0x%02x (%d ns), maxlat=0x%02x (%d ns)\n", cfg->lattimer, cfg->lattimer * 30, cfg->mingnt, cfg->mingnt * 250, cfg->maxlat, cfg->maxlat * 250); #endif /* PCI_DEBUG */ if (cfg->intpin > 0) printf("\tintpin=%c, irq=%d\n", cfg->intpin +'a' -1, cfg->intline); } } static int pci_porten(pcicfgregs *cfg) { return ((cfg->cmdreg & PCIM_CMD_PORTEN) != 0); } static int pci_memen(pcicfgregs *cfg) { return ((cfg->cmdreg & PCIM_CMD_MEMEN) != 0); } /* * Add a resource based on a pci map register. Return 1 if the map * register is a 32bit map register or 2 if it is a 64bit register. */ static int pci_add_map(device_t dev, pcicfgregs* cfg, int reg) { struct pci_devinfo *dinfo = device_get_ivars(dev); struct resource_list *rl = &dinfo->resources; u_int32_t map; u_int64_t base; u_int8_t ln2size; u_int8_t ln2range; u_int32_t testval; int type; map = pci_cfgread(cfg, reg, 4); if (map == 0 || map == 0xffffffff) return 1; /* skip invalid entry */ pci_cfgwrite(cfg, reg, 0xffffffff, 4); testval = pci_cfgread(cfg, reg, 4); pci_cfgwrite(cfg, reg, map, 4); base = pci_mapbase(map); if (pci_maptype(map) & PCI_MAPMEM) type = SYS_RES_MEMORY; else type = SYS_RES_IOPORT; ln2size = pci_mapsize(testval); ln2range = pci_maprange(testval); if (ln2range == 64) { /* Read the other half of a 64bit map register */ base |= (u_int64_t) pci_cfgread(cfg, reg + 4, 4) << 32; } #ifdef __alpha__ /* * XXX: encode hose number in the base addr, * This will go away once the bus_space functions * can deal with multiple hoses */ if(cfg->hose){ if (base & 0x80000000) { printf("base addr = 0x%lx\n", base); printf("hacked addr = 0x%lx\n", base | ((u_int64_t)cfg->hose << 31)); panic("hose encoding hack would clobber base addr"); } if (cfg->hose > 1) panic("only one hose supported!"); base |= ((u_int64_t)cfg->hose << 31); } #endif if (type == SYS_RES_IOPORT && !pci_porten(cfg)) return 1; if (type == SYS_RES_MEMORY && !pci_memen(cfg)) return 1; resource_list_add(rl, type, reg, base, base + (1 << ln2size) - 1, (1 << ln2size)); if (bootverbose) { printf("\tmap[%02x]: type %x, range %2d, base %08x, size %2d\n", reg, pci_maptype(base), ln2range, (unsigned int) base, ln2size); } return (ln2range == 64) ? 2 : 1; } static void pci_add_resources(device_t dev, pcicfgregs* cfg) { struct pci_devinfo *dinfo = device_get_ivars(dev); struct resource_list *rl = &dinfo->resources; struct pci_quirk *q; int i; for (i = 0; i < cfg->nummaps;) { i += pci_add_map(dev, cfg, PCIR_MAPS + i*4); } for (q = &pci_quirks[0]; q->devid; q++) { if (q->devid == ((cfg->device << 16) | cfg->vendor) && q->type == PCI_QUIRK_MAP_REG) pci_add_map(dev, cfg, q->arg1); } if (cfg->intline != 255) resource_list_add(rl, SYS_RES_IRQ, 0, cfg->intline, cfg->intline, 1); } static void pci_add_children(device_t dev, int busno) { pcicfgregs probe; #ifdef SIMOS #undef PCI_SLOTMAX #define PCI_SLOTMAX 0 #endif bzero(&probe, sizeof probe); #ifdef __alpha__ probe.hose = pcib_get_hose(dev); #endif #ifdef __i386__ probe.hose = 0; #endif probe.bus = busno; for (probe.slot = 0; probe.slot <= PCI_SLOTMAX; probe.slot++) { int pcifunchigh = 0; for (probe.func = 0; probe.func <= pcifunchigh; probe.func++) { struct pci_devinfo *dinfo = pci_readcfg(&probe); if (dinfo != NULL) { if (dinfo->cfg.mfdev) pcifunchigh = 7; pci_print_verbose(dinfo); - dinfo->cfg.dev = - device_add_child(dev, NULL, -1, dinfo); + dinfo->cfg.dev = device_add_child(dev, NULL, -1); + device_set_ivars(dinfo->cfg.dev, dinfo); pci_add_resources(dinfo->cfg.dev, &dinfo->cfg); } } } } static int pci_new_probe(device_t dev) { static int once; device_set_desc(dev, "PCI bus"); pci_add_children(dev, device_get_unit(dev)); if (!once) { make_dev(&pcicdev, 0, UID_ROOT, GID_WHEEL, 0644, "pci"); once++; } return 0; } static int pci_print_child(device_t dev, device_t child) { struct pci_devinfo *dinfo; pcicfgregs *cfg; int retval = 0; dinfo = device_get_ivars(child); cfg = &dinfo->cfg; retval += bus_print_child_header(dev, child); if (cfg->intpin > 0 && cfg->intline != 255) retval += printf(" irq %d", cfg->intline); retval += printf(" at device %d.%d", pci_get_slot(child), pci_get_function(child)); retval += bus_print_child_footer(dev, child); return (retval); } static void pci_probe_nomatch(device_t dev, device_t child) { struct pci_devinfo *dinfo; pcicfgregs *cfg; /* a few 'known' devices */ if (pci_get_class(child) == PCIC_SERIALBUS && pci_get_subclass(child) == PCIS_SERIALBUS_USB) { if (pci_get_progif(child) == 0x00 /* UHCI */ ) { device_printf(dev, "UHCI USB controller"); } else if (pci_get_progif(child) == 0x10 /* OHCI */ ) { device_printf(dev, "OHCI USB controller"); } else { device_printf(dev, "USB controller"); } } else { device_printf(dev, "unknown card"); } dinfo = device_get_ivars(child); cfg = &dinfo->cfg; printf(" (vendor=0x%04x, dev=0x%04x) at %d.%d", cfg->vendor, cfg->device, pci_get_slot(child), pci_get_function(child)); if (cfg->intpin > 0 && cfg->intline != 255) { printf(" irq %d", cfg->intline); } printf("\n"); return; } static int pci_read_ivar(device_t dev, device_t child, int which, u_long *result) { struct pci_devinfo *dinfo; pcicfgregs *cfg; dinfo = device_get_ivars(child); cfg = &dinfo->cfg; switch (which) { case PCI_IVAR_SUBVENDOR: *result = cfg->subvendor; break; case PCI_IVAR_SUBDEVICE: *result = cfg->subdevice; break; case PCI_IVAR_VENDOR: *result = cfg->vendor; break; case PCI_IVAR_DEVICE: *result = cfg->device; break; case PCI_IVAR_DEVID: *result = (cfg->device << 16) | cfg->vendor; break; case PCI_IVAR_CLASS: *result = cfg->baseclass; break; case PCI_IVAR_SUBCLASS: *result = cfg->subclass; break; case PCI_IVAR_PROGIF: *result = cfg->progif; break; case PCI_IVAR_REVID: *result = cfg->revid; break; case PCI_IVAR_INTPIN: *result = cfg->intpin; break; case PCI_IVAR_IRQ: *result = cfg->intline; break; case PCI_IVAR_BUS: *result = cfg->bus; break; case PCI_IVAR_SLOT: *result = cfg->slot; break; case PCI_IVAR_FUNCTION: *result = cfg->func; break; case PCI_IVAR_SECONDARYBUS: *result = cfg->secondarybus; break; case PCI_IVAR_SUBORDINATEBUS: *result = cfg->subordinatebus; break; case PCI_IVAR_HOSE: /* * Pass up to parent bridge. */ *result = pcib_get_hose(dev); break; default: return ENOENT; } return 0; } static int pci_write_ivar(device_t dev, device_t child, int which, uintptr_t value) { struct pci_devinfo *dinfo; pcicfgregs *cfg; dinfo = device_get_ivars(child); cfg = &dinfo->cfg; switch (which) { case PCI_IVAR_SUBVENDOR: case PCI_IVAR_SUBDEVICE: case PCI_IVAR_VENDOR: case PCI_IVAR_DEVICE: case PCI_IVAR_DEVID: case PCI_IVAR_CLASS: case PCI_IVAR_SUBCLASS: case PCI_IVAR_PROGIF: case PCI_IVAR_REVID: case PCI_IVAR_INTPIN: case PCI_IVAR_IRQ: case PCI_IVAR_BUS: case PCI_IVAR_SLOT: case PCI_IVAR_FUNCTION: return EINVAL; /* disallow for now */ case PCI_IVAR_SECONDARYBUS: cfg->secondarybus = value; break; case PCI_IVAR_SUBORDINATEBUS: cfg->subordinatebus = value; break; default: return ENOENT; } return 0; } static struct resource * pci_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct pci_devinfo *dinfo = device_get_ivars(child); struct resource_list *rl = &dinfo->resources; return resource_list_alloc(rl, dev, child, type, rid, start, end, count, flags); } static int pci_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { struct pci_devinfo *dinfo = device_get_ivars(child); struct resource_list *rl = &dinfo->resources; return resource_list_release(rl, dev, child, type, rid, r); } static int pci_set_resource(device_t dev, device_t child, int type, int rid, u_long start, u_long count) { struct pci_devinfo *dinfo = device_get_ivars(child); struct resource_list *rl = &dinfo->resources; resource_list_add(rl, type, rid, start, start + count - 1, count); return 0; } static int pci_get_resource(device_t dev, device_t child, int type, int rid, u_long *startp, u_long *countp) { struct pci_devinfo *dinfo = device_get_ivars(child); struct resource_list *rl = &dinfo->resources; struct resource_list_entry *rle; rle = resource_list_find(rl, type, rid); if (!rle) return ENOENT; if (startp) *startp = rle->start; if (countp) *countp = rle->count; return 0; } static void pci_delete_resource(device_t dev, device_t child, int type, int rid) { printf("pci_set_resource: PCI resources can not be deleted\n"); } static u_int32_t pci_read_config_method(device_t dev, device_t child, int reg, int width) { struct pci_devinfo *dinfo = device_get_ivars(child); pcicfgregs *cfg = &dinfo->cfg; return pci_cfgread(cfg, reg, width); } static void pci_write_config_method(device_t dev, device_t child, int reg, u_int32_t val, int width) { struct pci_devinfo *dinfo = device_get_ivars(child); pcicfgregs *cfg = &dinfo->cfg; pci_cfgwrite(cfg, reg, val, width); } static int pci_modevent(module_t mod, int what, void *arg) { switch (what) { case MOD_LOAD: STAILQ_INIT(&pci_devq); break; case MOD_UNLOAD: break; } return 0; } static device_method_t pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pci_new_probe), DEVMETHOD(device_attach, bus_generic_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, pci_print_child), DEVMETHOD(bus_probe_nomatch, pci_probe_nomatch), DEVMETHOD(bus_read_ivar, pci_read_ivar), DEVMETHOD(bus_write_ivar, pci_write_ivar), DEVMETHOD(bus_driver_added, bus_generic_driver_added), DEVMETHOD(bus_alloc_resource, pci_alloc_resource), DEVMETHOD(bus_release_resource, pci_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_set_resource, pci_set_resource), DEVMETHOD(bus_get_resource, pci_get_resource), DEVMETHOD(bus_delete_resource, pci_delete_resource), /* PCI interface */ DEVMETHOD(pci_read_config, pci_read_config_method), DEVMETHOD(pci_write_config, pci_write_config_method), { 0, 0 } }; static driver_t pci_driver = { "pci", pci_methods, 1, /* no softc */ }; DRIVER_MODULE(pci, pcib, pci_driver, pci_devclass, pci_modevent, 0); Index: head/sys/pci/pcisupport.c =================================================================== --- head/sys/pci/pcisupport.c (revision 54072) +++ head/sys/pci/pcisupport.c (revision 54073) @@ -1,1650 +1,1650 @@ /************************************************************************** ** ** $FreeBSD$ ** ** Device driver for DEC/INTEL PCI chipsets. ** ** FreeBSD ** **------------------------------------------------------------------------- ** ** Written for FreeBSD by ** wolf@cologne.de Wolfgang Stanglmeier ** se@mi.Uni-Koeln.de Stefan Esser ** **------------------------------------------------------------------------- ** ** Copyright (c) 1994,1995 Stefan Esser. All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ** *************************************************************************** */ #include "opt_bus.h" #include "opt_pci.h" #include "opt_smp.h" #include #include #include #include #include #include #include #include #include #include /*--------------------------------------------------------- ** ** Intel chipsets for 486 / Pentium processor ** **--------------------------------------------------------- */ static void chipset_attach(device_t dev, int unit); struct condmsg { unsigned char port; unsigned char mask; unsigned char value; char flags; const char *text; }; static void fixbushigh_i1225(device_t dev) { int sublementarybus; sublementarybus = pci_read_config(dev, 0x41, 1); if (sublementarybus != 0xff) { pci_set_secondarybus(dev, sublementarybus + 1); pci_set_subordinatebus(dev, sublementarybus + 1); } } static void fixwsc_natoma(device_t dev) { int pmccfg; pmccfg = pci_read_config(dev, 0x50, 2); #if defined(SMP) if (pmccfg & 0x8000) { printf("Correcting Natoma config for SMP\n"); pmccfg &= ~0x8000; pci_write_config(dev, 0x50, 2, pmccfg); } #else if ((pmccfg & 0x8000) == 0) { printf("Correcting Natoma config for non-SMP\n"); pmccfg |= 0x8000; pci_write_config(dev, 0x50, 2, pmccfg); } #endif } #ifndef PCI_QUIET #define M_XX 0 /* end of list */ #define M_EQ 1 /* mask and return true if equal */ #define M_NE 2 /* mask and return true if not equal */ #define M_TR 3 /* don't read config, always true */ #define M_EN 4 /* mask and print "enabled" if true, "disabled" if false */ #define M_NN 5 /* opposite sense of M_EN */ static const struct condmsg conf82425ex[] = { { 0x00, 0x00, 0x00, M_TR, "\tClock " }, { 0x50, 0x06, 0x00, M_EQ, "25" }, { 0x50, 0x06, 0x02, M_EQ, "33" }, { 0x50, 0x04, 0x04, M_EQ, "??", }, { 0x00, 0x00, 0x00, M_TR, "MHz, L1 Cache " }, { 0x50, 0x01, 0x00, M_EQ, "Disabled\n" }, { 0x50, 0x09, 0x01, M_EQ, "Write-through\n" }, { 0x50, 0x09, 0x09, M_EQ, "Write-back\n" }, { 0x00, 0x00, 0x00, M_TR, "\tL2 Cache " }, { 0x52, 0x07, 0x00, M_EQ, "Disabled" }, { 0x52, 0x0f, 0x01, M_EQ, "64KB Write-through" }, { 0x52, 0x0f, 0x02, M_EQ, "128KB Write-through" }, { 0x52, 0x0f, 0x03, M_EQ, "256KB Write-through" }, { 0x52, 0x0f, 0x04, M_EQ, "512KB Write-through" }, { 0x52, 0x0f, 0x01, M_EQ, "64KB Write-back" }, { 0x52, 0x0f, 0x02, M_EQ, "128KB Write-back" }, { 0x52, 0x0f, 0x03, M_EQ, "256KB Write-back" }, { 0x52, 0x0f, 0x04, M_EQ, "512KB Write-back" }, { 0x53, 0x01, 0x00, M_EQ, ", 3-" }, { 0x53, 0x01, 0x01, M_EQ, ", 2-" }, { 0x53, 0x06, 0x00, M_EQ, "3-3-3" }, { 0x53, 0x06, 0x02, M_EQ, "2-2-2" }, { 0x53, 0x06, 0x04, M_EQ, "1-1-1" }, { 0x53, 0x06, 0x06, M_EQ, "?-?-?" }, { 0x53, 0x18, 0x00, M_EQ, "/4-2-2-2\n" }, { 0x53, 0x18, 0x08, M_EQ, "/3-2-2-2\n" }, { 0x53, 0x18, 0x10, M_EQ, "/?-?-?-?\n" }, { 0x53, 0x18, 0x18, M_EQ, "/2-1-1-1\n" }, { 0x56, 0x00, 0x00, M_TR, "\tDRAM: " }, { 0x56, 0x02, 0x02, M_EQ, "Fast Code Read, " }, { 0x56, 0x04, 0x04, M_EQ, "Fast Data Read, " }, { 0x56, 0x08, 0x08, M_EQ, "Fast Write, " }, { 0x57, 0x20, 0x20, M_EQ, "Pipelined CAS" }, { 0x57, 0x2e, 0x00, M_NE, "\n\t" }, { 0x57, 0x00, 0x00, M_TR, "Timing: RAS: " }, { 0x57, 0x07, 0x00, M_EQ, "4" }, { 0x57, 0x07, 0x01, M_EQ, "3" }, { 0x57, 0x07, 0x02, M_EQ, "2" }, { 0x57, 0x07, 0x04, M_EQ, "1.5" }, { 0x57, 0x07, 0x05, M_EQ, "1" }, { 0x57, 0x00, 0x00, M_TR, " Clocks, CAS Read: " }, { 0x57, 0x18, 0x00, M_EQ, "3/1", }, { 0x57, 0x18, 0x00, M_EQ, "2/1", }, { 0x57, 0x18, 0x00, M_EQ, "1.5/0.5", }, { 0x57, 0x18, 0x00, M_EQ, "1/1", }, { 0x57, 0x00, 0x00, M_TR, ", CAS Write: " }, { 0x57, 0x20, 0x00, M_EQ, "2/1", }, { 0x57, 0x20, 0x20, M_EQ, "1/1", }, { 0x57, 0x00, 0x00, M_TR, "\n" }, { 0x40, 0x01, 0x01, M_EQ, "\tCPU-to-PCI Byte Merging\n" }, { 0x40, 0x02, 0x02, M_EQ, "\tCPU-to-PCI Bursting\n" }, { 0x40, 0x04, 0x04, M_EQ, "\tPCI Posted Writes\n" }, { 0x40, 0x20, 0x00, M_EQ, "\tDRAM Parity Disabled\n" }, { 0x48, 0x03, 0x01, M_EQ, "\tPCI IDE controller: Primary (1F0h-1F7h,3F6h,3F7h)" }, { 0x48, 0x03, 0x02, M_EQ, "\tPCI IDE controller: Secondary (170h-177h,376h,377h)" }, { 0x4d, 0x01, 0x01, M_EQ, "\tRTC (70-77h)\n" }, { 0x4d, 0x02, 0x02, M_EQ, "\tKeyboard (60,62,64,66h)\n" }, { 0x4d, 0x08, 0x08, M_EQ, "\tIRQ12/M Mouse Function\n" }, /* end marker */ { 0 } }; static const struct condmsg conf82424zx[] = { { 0x00, 0x00, 0x00, M_TR, "\tCPU: " }, { 0x50, 0xe0, 0x00, M_EQ, "486DX" }, { 0x50, 0xe0, 0x20, M_EQ, "486SX" }, { 0x50, 0xe0, 0x40, M_EQ, "486DX2 or 486DX4" }, { 0x50, 0xe0, 0x80, M_EQ, "Overdrive (writeback)" }, { 0x00, 0x00, 0x00, M_TR, ", bus=" }, { 0x50, 0x03, 0x00, M_EQ, "25MHz" }, { 0x50, 0x03, 0x01, M_EQ, "33MHz" }, { 0x53, 0x01, 0x01, M_TR, ", CPU->Memory posting "}, { 0x53, 0x01, 0x00, M_EQ, "OFF" }, { 0x53, 0x01, 0x01, M_EQ, "ON" }, { 0x56, 0x30, 0x00, M_NE, "\n\tWarning:" }, { 0x56, 0x20, 0x00, M_NE, " NO cache parity!" }, { 0x56, 0x10, 0x00, M_NE, " NO DRAM parity!" }, { 0x55, 0x04, 0x04, M_EQ, "\n\tWarning: refresh OFF! " }, { 0x00, 0x00, 0x00, M_TR, "\n\tCache: " }, { 0x52, 0x01, 0x00, M_EQ, "None" }, { 0x52, 0xc1, 0x01, M_EQ, "64KB" }, { 0x52, 0xc1, 0x41, M_EQ, "128KB" }, { 0x52, 0xc1, 0x81, M_EQ, "256KB" }, { 0x52, 0xc1, 0xc1, M_EQ, "512KB" }, { 0x52, 0x03, 0x01, M_EQ, " writethrough" }, { 0x52, 0x03, 0x03, M_EQ, " writeback" }, { 0x52, 0x01, 0x01, M_EQ, ", cache clocks=" }, { 0x52, 0x05, 0x01, M_EQ, "3-1-1-1" }, { 0x52, 0x05, 0x05, M_EQ, "2-1-1-1" }, { 0x00, 0x00, 0x00, M_TR, "\n\tDRAM:" }, { 0x55, 0x43, 0x00, M_NE, " page mode" }, { 0x55, 0x02, 0x02, M_EQ, " code fetch" }, { 0x55, 0x43, 0x43, M_EQ, "," }, { 0x55, 0x43, 0x42, M_EQ, " and" }, { 0x55, 0x40, 0x40, M_EQ, " read" }, { 0x55, 0x03, 0x03, M_EQ, " and" }, { 0x55, 0x43, 0x41, M_EQ, " and" }, { 0x55, 0x01, 0x01, M_EQ, " write" }, { 0x55, 0x43, 0x00, M_NE, "," }, { 0x00, 0x00, 0x00, M_TR, " memory clocks=" }, { 0x55, 0x20, 0x00, M_EQ, "X-2-2-2" }, { 0x55, 0x20, 0x20, M_EQ, "X-1-2-1" }, { 0x00, 0x00, 0x00, M_TR, "\n\tCPU->PCI: posting " }, { 0x53, 0x02, 0x00, M_NE, "ON" }, { 0x53, 0x02, 0x00, M_EQ, "OFF" }, { 0x00, 0x00, 0x00, M_TR, ", burst mode " }, { 0x54, 0x02, 0x00, M_NE, "ON" }, { 0x54, 0x02, 0x00, M_EQ, "OFF" }, { 0x00, 0x00, 0x00, M_TR, "\n\tPCI->Memory: posting " }, { 0x54, 0x01, 0x00, M_NE, "ON" }, { 0x54, 0x01, 0x00, M_EQ, "OFF" }, { 0x00, 0x00, 0x00, M_TR, "\n" }, /* end marker */ { 0 } }; static const struct condmsg conf82434lx[] = { { 0x00, 0x00, 0x00, M_TR, "\tCPU: " }, { 0x50, 0xe3, 0x82, M_EQ, "Pentium, 60MHz" }, { 0x50, 0xe3, 0x83, M_EQ, "Pentium, 66MHz" }, { 0x50, 0xe3, 0xa2, M_EQ, "Pentium, 90MHz" }, { 0x50, 0xe3, 0xa3, M_EQ, "Pentium, 100MHz" }, { 0x50, 0xc2, 0x82, M_NE, "(unknown)" }, { 0x50, 0x04, 0x00, M_EQ, " (primary cache OFF)" }, { 0x53, 0x01, 0x01, M_TR, ", CPU->Memory posting "}, { 0x53, 0x01, 0x01, M_NE, "OFF" }, { 0x53, 0x01, 0x01, M_EQ, "ON" }, { 0x53, 0x08, 0x00, M_NE, ", read around write"}, { 0x70, 0x04, 0x00, M_EQ, "\n\tWarning: Cache parity disabled!" }, { 0x57, 0x20, 0x00, M_NE, "\n\tWarning: DRAM parity mask!" }, { 0x57, 0x01, 0x00, M_EQ, "\n\tWarning: refresh OFF! " }, { 0x00, 0x00, 0x00, M_TR, "\n\tCache: " }, { 0x52, 0x01, 0x00, M_EQ, "None" }, { 0x52, 0x81, 0x01, M_EQ, "" }, { 0x52, 0xc1, 0x81, M_EQ, "256KB" }, { 0x52, 0xc1, 0xc1, M_EQ, "512KB" }, { 0x52, 0x03, 0x01, M_EQ, " writethrough" }, { 0x52, 0x03, 0x03, M_EQ, " writeback" }, { 0x52, 0x01, 0x01, M_EQ, ", cache clocks=" }, { 0x52, 0x21, 0x01, M_EQ, "3-2-2-2/4-2-2-2" }, { 0x52, 0x21, 0x21, M_EQ, "3-1-1-1" }, { 0x52, 0x01, 0x01, M_EQ, "\n\tCache flags: " }, { 0x52, 0x11, 0x11, M_EQ, " cache-all" }, { 0x52, 0x09, 0x09, M_EQ, " byte-control" }, { 0x52, 0x05, 0x05, M_EQ, " powersaver" }, { 0x00, 0x00, 0x00, M_TR, "\n\tDRAM:" }, { 0x57, 0x10, 0x00, M_EQ, " page mode" }, { 0x00, 0x00, 0x00, M_TR, " memory clocks=" }, { 0x57, 0xc0, 0x00, M_EQ, "X-4-4-4 (70ns)" }, { 0x57, 0xc0, 0x40, M_EQ, "X-4-4-4/X-3-3-3 (60ns)" }, { 0x57, 0xc0, 0x80, M_EQ, "???" }, { 0x57, 0xc0, 0xc0, M_EQ, "X-3-3-3 (50ns)" }, { 0x58, 0x02, 0x02, M_EQ, ", RAS-wait" }, { 0x58, 0x01, 0x01, M_EQ, ", CAS-wait" }, { 0x00, 0x00, 0x00, M_TR, "\n\tCPU->PCI: posting " }, { 0x53, 0x02, 0x02, M_EQ, "ON" }, { 0x53, 0x02, 0x00, M_EQ, "OFF" }, { 0x00, 0x00, 0x00, M_TR, ", burst mode " }, { 0x54, 0x02, 0x00, M_NE, "ON" }, { 0x54, 0x02, 0x00, M_EQ, "OFF" }, { 0x54, 0x04, 0x00, M_TR, ", PCI clocks=" }, { 0x54, 0x04, 0x00, M_EQ, "2-2-2-2" }, { 0x54, 0x04, 0x00, M_NE, "2-1-1-1" }, { 0x00, 0x00, 0x00, M_TR, "\n\tPCI->Memory: posting " }, { 0x54, 0x01, 0x00, M_NE, "ON" }, { 0x54, 0x01, 0x00, M_EQ, "OFF" }, { 0x57, 0x01, 0x01, M_EQ, "\n\tRefresh:" }, { 0x57, 0x03, 0x03, M_EQ, " CAS#/RAS#(Hidden)" }, { 0x57, 0x03, 0x01, M_EQ, " RAS#Only" }, { 0x57, 0x05, 0x05, M_EQ, " BurstOf4" }, { 0x00, 0x00, 0x00, M_TR, "\n" }, /* end marker */ { 0 } }; static const struct condmsg conf82378[] = { { 0x00, 0x00, 0x00, M_TR, "\tBus Modes:" }, { 0x41, 0x04, 0x04, M_EQ, " Bus Park," }, { 0x41, 0x02, 0x02, M_EQ, " Bus Lock," }, { 0x41, 0x02, 0x00, M_EQ, " Resource Lock," }, { 0x41, 0x01, 0x01, M_EQ, " GAT" }, { 0x4d, 0x20, 0x20, M_EQ, "\n\tCoprocessor errors enabled" }, { 0x4d, 0x10, 0x10, M_EQ, "\n\tMouse function enabled" }, { 0x4e, 0x30, 0x10, M_EQ, "\n\tIDE controller: Primary (1F0h-1F7h,3F6h,3F7h)" }, { 0x4e, 0x30, 0x30, M_EQ, "\n\tIDE controller: Secondary (170h-177h,376h,377h)" }, { 0x4e, 0x28, 0x08, M_EQ, "\n\tFloppy controller: 3F0h,3F1h " }, { 0x4e, 0x24, 0x04, M_EQ, "\n\tFloppy controller: 3F2h-3F7h " }, { 0x4e, 0x28, 0x28, M_EQ, "\n\tFloppy controller: 370h,371h " }, { 0x4e, 0x24, 0x24, M_EQ, "\n\tFloppy controller: 372h-377h " }, { 0x4e, 0x02, 0x02, M_EQ, "\n\tKeyboard controller: 60h,62h,64h,66h" }, { 0x4e, 0x01, 0x01, M_EQ, "\n\tRTC: 70h-77h" }, { 0x4f, 0x80, 0x80, M_EQ, "\n\tConfiguration RAM: 0C00h,0800h-08FFh" }, { 0x4f, 0x40, 0x40, M_EQ, "\n\tPort 92: enabled" }, { 0x4f, 0x03, 0x00, M_EQ, "\n\tSerial Port A: COM1 (3F8h-3FFh)" }, { 0x4f, 0x03, 0x01, M_EQ, "\n\tSerial Port A: COM2 (2F8h-2FFh)" }, { 0x4f, 0x0c, 0x00, M_EQ, "\n\tSerial Port B: COM1 (3F8h-3FFh)" }, { 0x4f, 0x0c, 0x04, M_EQ, "\n\tSerial Port B: COM2 (2F8h-2FFh)" }, { 0x4f, 0x30, 0x00, M_EQ, "\n\tParallel Port: LPT1 (3BCh-3BFh)" }, { 0x4f, 0x30, 0x04, M_EQ, "\n\tParallel Port: LPT2 (378h-37Fh)" }, { 0x4f, 0x30, 0x20, M_EQ, "\n\tParallel Port: LPT3 (278h-27Fh)" }, { 0x00, 0x00, 0x00, M_TR, "\n" }, /* end marker */ { 0 } }; static const struct condmsg conf82437fx[] = { /* PCON -- PCI Control Register */ { 0x00, 0x00, 0x00, M_TR, "\tCPU Inactivity timer: " }, { 0x50, 0xe0, 0xe0, M_EQ, "8" }, { 0x50, 0xe0, 0xd0, M_EQ, "7" }, { 0x50, 0xe0, 0xc0, M_EQ, "6" }, { 0x50, 0xe0, 0xb0, M_EQ, "5" }, { 0x50, 0xe0, 0xa0, M_EQ, "4" }, { 0x50, 0xe0, 0x90, M_EQ, "3" }, { 0x50, 0xe0, 0x80, M_EQ, "2" }, { 0x50, 0xe0, 0x00, M_EQ, "1" }, { 0x00, 0x00, 0x00, M_TR, " clocks\n\tPeer Concurrency: " }, { 0x50, 0x08, 0x08, M_EN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n\tCPU-to-PCI Write Bursting: " }, { 0x50, 0x04, 0x00, M_NN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n\tPCI Streaming: " }, { 0x50, 0x02, 0x00, M_NN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n\tBus Concurrency: " }, { 0x50, 0x01, 0x00, M_NN, 0 }, /* CC -- Cache Control Regsiter */ { 0x00, 0x00, 0x00, M_TR, "\n\tCache:" }, { 0x52, 0xc0, 0x80, M_EQ, " 512K" }, { 0x52, 0xc0, 0x40, M_EQ, " 256K" }, { 0x52, 0xc0, 0x00, M_EQ, " NO" }, { 0x52, 0x30, 0x00, M_EQ, " pipelined-burst" }, { 0x52, 0x30, 0x10, M_EQ, " burst" }, { 0x52, 0x30, 0x20, M_EQ, " asynchronous" }, { 0x52, 0x30, 0x30, M_EQ, " dual-bank pipelined-burst" }, { 0x00, 0x00, 0x00, M_TR, " secondary; L1 " }, { 0x52, 0x01, 0x00, M_EN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n" }, /* DRAMC -- DRAM Control Register */ { 0x57, 0x07, 0x00, M_EQ, "Warning: refresh OFF!\n" }, { 0x00, 0x00, 0x00, M_TR, "\tDRAM:" }, { 0x57, 0xc0, 0x00, M_EQ, " no memory hole" }, { 0x57, 0xc0, 0x40, M_EQ, " 512K-640K memory hole" }, { 0x57, 0xc0, 0x80, M_EQ, " 15M-16M memory hole" }, { 0x57, 0x07, 0x01, M_EQ, ", 50 MHz refresh" }, { 0x57, 0x07, 0x02, M_EQ, ", 60 MHz refresh" }, { 0x57, 0x07, 0x03, M_EQ, ", 66 MHz refresh" }, /* DRAMT = DRAM Timing Register */ { 0x00, 0x00, 0x00, M_TR, "\n\tRead burst timing: " }, { 0x58, 0x60, 0x00, M_EQ, "x-4-4-4/x-4-4-4" }, { 0x58, 0x60, 0x20, M_EQ, "x-3-3-3/x-4-4-4" }, { 0x58, 0x60, 0x40, M_EQ, "x-2-2-2/x-3-3-3" }, { 0x58, 0x60, 0x60, M_EQ, "???" }, { 0x00, 0x00, 0x00, M_TR, "\n\tWrite burst timing: " }, { 0x58, 0x18, 0x00, M_EQ, "x-4-4-4" }, { 0x58, 0x18, 0x08, M_EQ, "x-3-3-3" }, { 0x58, 0x18, 0x10, M_EQ, "x-2-2-2" }, { 0x58, 0x18, 0x18, M_EQ, "???" }, { 0x00, 0x00, 0x00, M_TR, "\n\tRAS-CAS delay: " }, { 0x58, 0x04, 0x00, M_EQ, "3" }, { 0x58, 0x04, 0x04, M_EQ, "2" }, { 0x00, 0x00, 0x00, M_TR, " clocks\n" }, /* end marker */ { 0 } }; static const struct condmsg conf82437vx[] = { /* PCON -- PCI Control Register */ { 0x00, 0x00, 0x00, M_TR, "\n\tPCI Concurrency: " }, { 0x50, 0x08, 0x08, M_EN, 0 }, /* CC -- Cache Control Regsiter */ { 0x00, 0x00, 0x00, M_TR, "\n\tCache:" }, { 0x52, 0xc0, 0x80, M_EQ, " 512K" }, { 0x52, 0xc0, 0x40, M_EQ, " 256K" }, { 0x52, 0xc0, 0x00, M_EQ, " NO" }, { 0x52, 0x30, 0x00, M_EQ, " pipelined-burst" }, { 0x52, 0x30, 0x10, M_EQ, " burst" }, { 0x52, 0x30, 0x20, M_EQ, " asynchronous" }, { 0x52, 0x30, 0x30, M_EQ, " dual-bank pipelined-burst" }, { 0x00, 0x00, 0x00, M_TR, " secondary; L1 " }, { 0x52, 0x01, 0x00, M_EN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n" }, /* DRAMC -- DRAM Control Register */ { 0x57, 0x07, 0x00, M_EQ, "Warning: refresh OFF!\n" }, { 0x00, 0x00, 0x00, M_TR, "\tDRAM:" }, { 0x57, 0xc0, 0x00, M_EQ, " no memory hole" }, { 0x57, 0xc0, 0x40, M_EQ, " 512K-640K memory hole" }, { 0x57, 0xc0, 0x80, M_EQ, " 15M-16M memory hole" }, { 0x57, 0x07, 0x01, M_EQ, ", 50 MHz refresh" }, { 0x57, 0x07, 0x02, M_EQ, ", 60 MHz refresh" }, { 0x57, 0x07, 0x03, M_EQ, ", 66 MHz refresh" }, /* DRAMT = DRAM Timing Register */ { 0x00, 0x00, 0x00, M_TR, "\n\tRead burst timing: " }, { 0x58, 0x60, 0x00, M_EQ, "x-4-4-4/x-4-4-4" }, { 0x58, 0x60, 0x20, M_EQ, "x-3-3-3/x-4-4-4" }, { 0x58, 0x60, 0x40, M_EQ, "x-2-2-2/x-3-3-3" }, { 0x58, 0x60, 0x60, M_EQ, "???" }, { 0x00, 0x00, 0x00, M_TR, "\n\tWrite burst timing: " }, { 0x58, 0x18, 0x00, M_EQ, "x-4-4-4" }, { 0x58, 0x18, 0x08, M_EQ, "x-3-3-3" }, { 0x58, 0x18, 0x10, M_EQ, "x-2-2-2" }, { 0x58, 0x18, 0x18, M_EQ, "???" }, { 0x00, 0x00, 0x00, M_TR, "\n\tRAS-CAS delay: " }, { 0x58, 0x04, 0x00, M_EQ, "3" }, { 0x58, 0x04, 0x04, M_EQ, "2" }, { 0x00, 0x00, 0x00, M_TR, " clocks\n" }, /* end marker */ { 0 } }; static const struct condmsg conf82371fb[] = { /* IORT -- ISA I/O Recovery Timer Register */ { 0x00, 0x00, 0x00, M_TR, "\tI/O Recovery Timing: 8-bit " }, { 0x4c, 0x40, 0x00, M_EQ, "3.5" }, { 0x4c, 0x78, 0x48, M_EQ, "1" }, { 0x4c, 0x78, 0x50, M_EQ, "2" }, { 0x4c, 0x78, 0x58, M_EQ, "3" }, { 0x4c, 0x78, 0x60, M_EQ, "4" }, { 0x4c, 0x78, 0x68, M_EQ, "5" }, { 0x4c, 0x78, 0x70, M_EQ, "6" }, { 0x4c, 0x78, 0x78, M_EQ, "7" }, { 0x4c, 0x78, 0x40, M_EQ, "8" }, { 0x00, 0x00, 0x00, M_TR, " clocks, 16-bit " }, { 0x4c, 0x04, 0x00, M_EQ, "3.5" }, { 0x4c, 0x07, 0x05, M_EQ, "1" }, { 0x4c, 0x07, 0x06, M_EQ, "2" }, { 0x4c, 0x07, 0x07, M_EQ, "3" }, { 0x4c, 0x07, 0x04, M_EQ, "4" }, { 0x00, 0x00, 0x00, M_TR, " clocks\n" }, /* XBCS -- X-Bus Chip Select Register */ { 0x00, 0x00, 0x00, M_TR, "\tExtended BIOS: " }, { 0x4e, 0x80, 0x80, M_EN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n\tLower BIOS: " }, { 0x4e, 0x40, 0x40, M_EN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n\tCoprocessor IRQ13: " }, { 0x4e, 0x20, 0x20, M_EN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n\tMouse IRQ12: " }, { 0x4e, 0x10, 0x10, M_EN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n" }, { 0x00, 0x00, 0x00, M_TR, "\tInterrupt Routing: " }, #define PIRQ(x, n) \ { 0x00, 0x00, 0x00, M_TR, n ": " }, \ { x, 0x80, 0x80, M_EQ, "disabled" }, \ { x, 0xc0, 0x40, M_EQ, "[shared] " }, \ { x, 0x8f, 0x03, M_EQ, "IRQ3" }, \ { x, 0x8f, 0x04, M_EQ, "IRQ4" }, \ { x, 0x8f, 0x05, M_EQ, "IRQ5" }, \ { x, 0x8f, 0x06, M_EQ, "IRQ6" }, \ { x, 0x8f, 0x07, M_EQ, "IRQ7" }, \ { x, 0x8f, 0x09, M_EQ, "IRQ9" }, \ { x, 0x8f, 0x0a, M_EQ, "IRQ10" }, \ { x, 0x8f, 0x0b, M_EQ, "IRQ11" }, \ { x, 0x8f, 0x0c, M_EQ, "IRQ12" }, \ { x, 0x8f, 0x0e, M_EQ, "IRQ14" }, \ { x, 0x8f, 0x0f, M_EQ, "IRQ15" } /* Interrupt routing */ PIRQ(0x60, "A"), PIRQ(0x61, ", B"), PIRQ(0x62, ", C"), PIRQ(0x63, ", D"), PIRQ(0x70, "\n\t\tMB0"), PIRQ(0x71, ", MB1"), { 0x00, 0x00, 0x00, M_TR, "\n" }, #undef PIRQ /* XXX - do DMA routing, too? */ { 0 } }; static const struct condmsg conf82371fb2[] = { /* IDETM -- IDE Timing Register */ { 0x00, 0x00, 0x00, M_TR, "\tPrimary IDE: " }, { 0x41, 0x80, 0x80, M_EN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n\tSecondary IDE: " }, { 0x43, 0x80, 0x80, M_EN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n" }, /* end of list */ { 0 } }; static void writeconfig (device_t dev, const struct condmsg *tbl) { while (tbl->flags != M_XX) { const char *text = 0; if (tbl->flags == M_TR) { text = tbl->text; } else { unsigned char v = pci_read_config(dev, tbl->port, 1); switch (tbl->flags) { case M_EQ: if ((v & tbl->mask) == tbl->value) text = tbl->text; break; case M_NE: if ((v & tbl->mask) != tbl->value) text = tbl->text; break; case M_EN: text = (v & tbl->mask) ? "enabled" : "disabled"; break; case M_NN: text = (v & tbl->mask) ? "disabled" : "enabled"; } } if (text) printf ("%s", text); tbl++; } } #ifdef DUMPCONFIGSPACE static void dumpconfigspace (device_t dev) { int reg; printf ("configuration space registers:"); for (reg = 0; reg < 0x100; reg+=4) { if ((reg & 0x0f) == 0) printf ("\n%02x:\t", reg); printf ("%08x ", pci_read_config(dev, reg, 4)); } printf ("\n"); } #endif /* DUMPCONFIGSPACE */ #endif /* PCI_QUIET */ const char * ide_pci_match(device_t dev) { switch (pci_get_devid(dev)) { case 0x12308086: return ("Intel PIIX IDE controller"); case 0x70108086: return ("Intel PIIX3 IDE controller"); case 0x71118086: return ("Intel PIIX4 IDE controller"); case 0x4d33105a: return ("Promise Ultra/33 IDE controller"); case 0x522910b9: return ("AcerLabs Aladdin IDE controller"); case 0x15711106: case 0x05711106: return ("VIA Apollo IDE controller"); case 0x06401095: return ("CMD 640 IDE controller"); case 0x06461095: return ("CMD 646 IDE controller"); case 0xc6931080: return ("Cypress 82C693 IDE controller"); case 0x01021078: return ("Cyrix 5530 IDE controller"); case 0x55131039: return ("SiS 5591 IDE controller"); default: if (pci_get_class(dev) == PCIC_STORAGE && pci_get_subclass(dev) == PCIS_STORAGE_IDE) return ("Unknown PCI IDE controller"); } return NULL; } static void chipset_attach (device_t dev, int unit) { #ifndef PCI_QUIET if (!bootverbose) return; switch (pci_get_devid(dev)) { case 0x04868086: writeconfig (dev, conf82425ex); break; case 0x04838086: writeconfig (dev, conf82424zx); break; case 0x04a38086: writeconfig (dev, conf82434lx); break; case 0x04848086: writeconfig (dev, conf82378); break; case 0x122d8086: writeconfig (dev, conf82437fx); break; case 0x70308086: writeconfig (dev, conf82437vx); break; case 0x70008086: case 0x122e8086: writeconfig (dev, conf82371fb); break; case 0x70108086: case 0x12308086: writeconfig (dev, conf82371fb2); break; #if 0 case 0x00011011: /* DEC 21050 */ case 0x00221014: /* IBM xxx */ writeconfig (dev, conf_pci2pci); break; #endif }; #endif /* PCI_QUIET */ } static const char * pci_bridge_type(device_t dev) { char *descr, tmpbuf[120]; if (pci_get_class(dev) != PCIC_BRIDGE) return NULL; switch (pci_get_subclass(dev)) { case PCIS_BRIDGE_HOST: strcpy(tmpbuf, "Host to PCI"); break; case PCIS_BRIDGE_ISA: strcpy(tmpbuf, "PCI to ISA"); break; case PCIS_BRIDGE_EISA: strcpy(tmpbuf, "PCI to EISA"); break; case PCIS_BRIDGE_MCA: strcpy(tmpbuf, "PCI to MCA"); break; case PCIS_BRIDGE_PCI: strcpy(tmpbuf, "PCI to PCI"); break; case PCIS_BRIDGE_PCMCIA: strcpy(tmpbuf, "PCI to PCMCIA"); break; case PCIS_BRIDGE_NUBUS: strcpy(tmpbuf, "PCI to NUBUS"); break; case PCIS_BRIDGE_CARDBUS: strcpy(tmpbuf, "PCI to CardBus"); break; case PCIS_BRIDGE_OTHER: strcpy(tmpbuf, "PCI to Other"); break; default: snprintf(tmpbuf, sizeof(tmpbuf), "PCI to 0x%x", pci_get_subclass(dev)); break; } snprintf(tmpbuf+strlen(tmpbuf), sizeof(tmpbuf)-strlen(tmpbuf), " bridge (vendor=%04x device=%04x)", pci_get_vendor(dev), pci_get_device(dev)); descr = malloc (strlen(tmpbuf) +1, M_DEVBUF, M_WAITOK); strcpy(descr, tmpbuf); return descr; } static const char* pcib_match(device_t dev) { switch (pci_get_devid(dev)) { /* Intel -- vendor 0x8086 */ case 0x71818086: return ("Intel 82443LX (440 LX) PCI-PCI (AGP) bridge"); case 0x71918086: return ("Intel 82443BX (440 BX) PCI-PCI (AGP) bridge"); case 0x71A18086: return ("Intel 82443GX (440 GX) PCI-PCI (AGP) bridge"); case 0x84cb8086: return ("Intel 82454NX PCI Expander Bridge"); case 0x124b8086: return ("Intel 82380FB mobile PCI to PCI bridge"); /* VLSI -- vendor 0x1004 */ case 0x01021004: return ("VLSI 82C534 Eagle II PCI Bus bridge"); case 0x01031004: return ("VLSI 82C538 Eagle II PCI Docking bridge"); /* VIA Technologies -- vendor 0x1106 */ case 0x85981106: return ("VIA 82C598MVP (Apollo MVP3) PCI-PCI (AGP) bridge"); /* AcerLabs -- vendor 0x10b9 */ /* Funny : The datasheet told me vendor id is "10b8",sub-vendor */ /* id is '10b9" but the register always shows "10b9". -Foxfair */ case 0x524710b9: return ("AcerLabs M5247 PCI-PCI(AGP Supported) bridge"); case 0x524310b9:/* 5243 seems like 5247, need more info to divide*/ return ("AcerLabs M5243 PCI-PCI bridge"); /* AMD -- vendor 0x1022 */ case 0x70071022: return ("AMD-751 PCI-PCI (AGP) bridge"); /* Others */ case 0x00221014: return ("IBM 82351 PCI-PCI bridge"); case 0x00011011: return ("DEC 21050 PCI-PCI bridge"); }; if (pci_get_class(dev) == PCIC_BRIDGE && pci_get_subclass(dev) == PCIS_BRIDGE_PCI) return pci_bridge_type(dev); return NULL; } static int pcib_probe(device_t dev) { const char *desc; desc = pcib_match(dev); if (desc) { device_set_desc_copy(dev, desc); return 0; } return ENXIO; } static int pcib_attach(device_t dev) { u_int8_t secondary; chipset_attach(dev, device_get_unit(dev)); secondary = pci_get_secondarybus(dev); if (secondary) { - device_add_child(dev, "pci", secondary, 0); + device_add_child(dev, "pci", secondary); return bus_generic_attach(dev); } else return 0; } static int pcib_read_ivar(device_t dev, device_t child, int which, u_long *result) { if (which == PCIB_IVAR_HOSE) { /* * Pass up to parent bus. */ *result = pci_get_hose(dev); return(0); } return ENOENT; } static device_method_t pcib_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pcib_probe), DEVMETHOD(device_attach, pcib_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, pcib_read_ivar), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t pcib_driver = { "pcib", pcib_methods, 1, }; static devclass_t pcib_devclass; DRIVER_MODULE(pcib, pci, pcib_driver, pcib_devclass, 0, 0); static const char * eisab_match(device_t dev) { switch (pci_get_devid(dev)) { case 0x04828086: /* Recognize this specifically, it has PCI-HOST class (!) */ return ("Intel 82375EB PCI-EISA bridge"); } if (pci_get_class(dev) == PCIC_BRIDGE && pci_get_subclass(dev) == PCIS_BRIDGE_EISA) return pci_bridge_type(dev); return NULL; } static const char * isab_match(device_t dev) { unsigned rev; switch (pci_get_devid(dev)) { case 0x04848086: rev = pci_get_revid(dev); if (rev == 3) return ("Intel 82378ZB PCI to ISA bridge"); return ("Intel 82378IB PCI to ISA bridge"); case 0x122e8086: return ("Intel 82371FB PCI to ISA bridge"); case 0x70008086: return ("Intel 82371SB PCI to ISA bridge"); case 0x71108086: return ("Intel 82371AB PCI to ISA bridge"); /* VLSI -- vendor 0x1004 */ case 0x00061004: return ("VLSI 82C593 PCI to ISA bridge"); /* VIA Technologies -- vendor 0x1106 */ case 0x05861106: /* south bridge section */ return ("VIA 82C586 PCI-ISA bridge"); case 0x06861106: return ("VIA 82C686 PCI-ISA bridge"); /* AcerLabs -- vendor 0x10b9 */ /* Funny : The datasheet told me vendor id is "10b8",sub-vendor */ /* id is '10b9" but the register always shows "10b9". -Foxfair */ case 0x153310b9: return ("AcerLabs M1533 portable PCI-ISA bridge"); case 0x154310b9: return ("AcerLabs M1543 desktop PCI-ISA bridge"); /* SiS -- vendor 0x1039 */ case 0x00081039: return ("SiS 85c503 PCI-ISA bridge"); /* Cyrix -- vendor 0x1078 */ case 0x00001078: return ("Cyrix Cx5510 PCI-ISA bridge"); case 0x01001078: return ("Cyrix Cx5530 PCI-ISA bridge"); /* NEC -- vendor 0x1033 */ /* The "C-bus" is 16-bits bus on PC98. */ case 0x00011033: return ("NEC 0001 PCI to PC-98 C-bus bridge"); case 0x002c1033: return ("NEC 002C PCI to PC-98 C-bus bridge"); case 0x003b1033: return ("NEC 003B PCI to PC-98 C-bus bridge"); } if (pci_get_class(dev) == PCIC_BRIDGE && pci_get_subclass(dev) == PCIS_BRIDGE_ISA) return pci_bridge_type(dev); return NULL; } static int isab_probe(device_t dev) { const char *desc; int is_eisa; is_eisa = 0; desc = eisab_match(dev); if (desc) is_eisa = 1; else desc = isab_match(dev); if (desc) { /* * For a PCI-EISA bridge, add both eisa and isa. * Only add one instance of eisa or isa for now. */ device_set_desc_copy(dev, desc); if (is_eisa && !devclass_get_device(devclass_find("eisa"), 0)) - device_add_child(dev, "eisa", -1, 0); + device_add_child(dev, "eisa", -1); if (!devclass_get_device(devclass_find("isa"), 0)) - device_add_child(dev, "isa", -1, 0); + device_add_child(dev, "isa", -1); return 0; } return ENXIO; } static int isab_attach(device_t dev) { chipset_attach(dev, device_get_unit(dev)); return bus_generic_attach(dev); } static device_method_t isab_methods[] = { /* Device interface */ DEVMETHOD(device_probe, isab_probe), DEVMETHOD(device_attach, isab_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t isab_driver = { "isab", isab_methods, 1, }; static devclass_t isab_devclass; DRIVER_MODULE(isab, pci, isab_driver, isab_devclass, 0, 0); static const char* chip_match(device_t dev) { unsigned rev; switch (pci_get_devid(dev)) { /* Intel -- vendor 0x8086 */ case 0x00088086: /* Silently ignore this one! What is it, anyway ??? */ return (""); case 0x71108086: /* * On my laptop (Tecra 8000DVD), this device has a * bogus subclass 0x80 so make sure that it doesn't * match the generic 'chip' driver by accident. */ return NULL; case 0x12258086: fixbushigh_i1225(dev); return ("Intel 824?? host to PCI bridge"); case 0x71808086: return ("Intel 82443LX (440 LX) host to PCI bridge"); case 0x71908086: return ("Intel 82443BX (440 BX) host to PCI bridge"); case 0x71928086: return ("Intel 82443BX host to PCI bridge (AGP disabled)"); case 0x71a08086: return ("Intel 82443GX host to PCI bridge"); case 0x71a18086: return ("Intel 82443GX host to AGP bridge"); case 0x71a28086: return ("Intel 82443GX host to PCI bridge (AGP disabled)"); case 0x84c48086: return ("Intel 82454KX/GX (Orion) host to PCI bridge"); case 0x84ca8086: return ("Intel 82451NX Memory and I/O controller"); case 0x04868086: return ("Intel 82425EX PCI system controller"); case 0x04838086: return ("Intel 82424ZX (Saturn) cache DRAM controller"); case 0x04a38086: rev = pci_get_revid(dev); if (rev == 16 || rev == 17) return ("Intel 82434NX (Neptune) PCI cache memory controller"); return ("Intel 82434LX (Mercury) PCI cache memory controller"); case 0x122d8086: return ("Intel 82437FX PCI cache memory controller"); case 0x12348086: return ("Intel 82371MX mobile PCI I/O IDE accelerator (MPIIX)"); case 0x12358086: return ("Intel 82437MX mobile PCI cache memory controller"); case 0x12508086: return ("Intel 82439HX PCI cache memory controller"); case 0x70308086: return ("Intel 82437VX PCI cache memory controller"); case 0x71008086: return ("Intel 82439TX System controller (MTXC)"); case 0x71138086: return ("Intel 82371AB Power management controller"); case 0x12378086: fixwsc_natoma(dev); return ("Intel 82440FX (Natoma) PCI and memory controller"); #if 0 case 0x70208086: return ("Intel 82371SB (PIIX3) USB controller"); case 0x71128086: return ("Intel 82371AB/EB (PIIX4) USB controller"); #endif case 0x84c58086: return ("Intel 82453KX/GX (Orion) PCI memory controller"); /* Sony -- vendor 0x104d */ case 0x8009104d: return ("Sony CXD1847A FireWire Host Controller"); /* SiS -- vendor 0x1039 */ case 0x04961039: return ("SiS 85c496"); case 0x04061039: return ("SiS 85c501"); case 0x06011039: return ("SiS 85c601"); case 0x55911039: return ("SiS 5591 host to PCI bridge"); case 0x00011039: return ("SiS 5591 host to AGP bridge"); /* VLSI -- vendor 0x1004 */ case 0x00051004: return ("VLSI 82C592 Host to PCI bridge"); case 0x01011004: return ("VLSI 82C532 Eagle II Peripheral controller"); case 0x01041004: return ("VLSI 82C535 Eagle II System controller"); case 0x01051004: return ("VLSI 82C147 IrDA controller"); /* VIA Technologies -- vendor 0x1106 (0x1107 on the Apollo Master) */ case 0x15761107: return ("VIA 82C570 (Apollo Master) system controller"); case 0x05851106: return ("VIA 82C585 (Apollo VP1/VPX) system controller"); case 0x05951106: case 0x15951106: return ("VIA 82C595 (Apollo VP2) system controller"); case 0x05971106: return ("VIA 82C597 (Apollo VP3) system controller"); /* XXX Here is MVP3, I got the datasheet but NO M/B to test it */ /* totally. Please let me know if anything wrong. -F */ /* XXX need info on the MVP3 -- any takers? */ case 0x05981106: return ("VIA 82C598MVP (Apollo MVP3) host bridge"); case 0x30401106: return ("VIA 82C586B ACPI interface"); case 0x05711106: return ("VIA 82C586B IDE controller"); case 0x30571106: return ("VIA 82C686 ACPI interface"); case 0x30581106: return ("VIA 82C686 AC97 Audio"); case 0x30681106: return ("VIA 82C686 AC97 Modem"); #if 0 case 0x30381106: return ("VIA 83C572 USB controller"); #endif /* AMD -- vendor 0x1022 */ case 0x70061022: return ("AMD-751 host to PCI bridge"); /* NEC -- vendor 0x1033 */ case 0x00021033: return ("NEC 0002 PCI to PC-98 local bus bridge"); case 0x00161033: return ("NEC 0016 PCI to PC-98 local bus bridge"); /* AcerLabs -- vendor 0x10b9 */ /* Funny : The datasheet told me vendor id is "10b8",sub-vendor */ /* id is '10b9" but the register always shows "10b9". -Foxfair */ case 0x154110b9: return ("AcerLabs M1541 (Aladdin-V) PCI host bridge"); #if 0 case 0x523710b9: return ("AcerLabs M5237 (Aladdin-V) USB controller"); #endif case 0x710110b9: return ("AcerLabs M15x3 Power Management Unit"); /* OPTi -- vendor 0x1045 */ case 0xc8221045: return ("OPTi 82C822 host to PCI Bridge"); #if 0 case 0xc8611045: return ("OPTi 82C861 (FireLink) USB controller"); #endif /* NEC -- vendor 0x1033 */ /* PCI to C-bus bridge */ /* The following chipsets are PCI to PC98 C-bus bridge. * The C-bus is the 16-bits bus on PC98 and it should be probed as * PCI to ISA bridge. Because class of the C-bus is not defined, * C-bus bridges are recognized as "other bridge." To make C-bus * bridge be recognized as ISA bridge, this function returns NULL. */ case 0x00011033: case 0x002c1033: case 0x003b1033: return NULL; #if 0 case 0x00351033: return ("NEC uPD 9210 USB controller"); #endif #if 0 /* CMD Tech -- vendor 0x1095 */ case 0x06701095: return ("CMD Tech 670 (USB0670) USB controller"); case 0x06731095: return ("CMD Tech 673 (USB0673) USB controller"); #endif }; if (pci_get_class(dev) == PCIC_BRIDGE && pci_get_subclass(dev) != PCIS_BRIDGE_PCI && pci_get_subclass(dev) != PCIS_BRIDGE_ISA && pci_get_subclass(dev) != PCIS_BRIDGE_EISA) return pci_bridge_type(dev); #if 0 if (pci_get_class(dev) == PCIC_SERIALBUS && pci_get_subclass(dev) == PCIS_SERIALBUS_USB) { if (pci_get_progif(dev) == 0x00 /* UHCI */ ) { return ("UHCI USB controller"); } else if (pci_get_progif(dev) == 0x10 /* OHCI */ ) { return ("OHCI USB controller"); } else { return ("USB controller"); } } #endif return NULL; } static int chip_probe(device_t dev) { const char *desc; desc = chip_match(dev); if (desc == NULL) desc = ide_pci_match(dev); if (desc) { if (pci_get_class(dev) == PCIC_BRIDGE && pci_get_subclass(dev) == PCIS_BRIDGE_HOST) { /* * Suppress printing this device since the nexus * has already described it. */ device_quiet(dev); } device_set_desc_copy(dev, desc); return -100; /* Low match priority */ } return ENXIO; } static int chip_attach(device_t dev) { chipset_attach(dev, device_get_unit(dev)); return 0; } static device_method_t chip_methods[] = { /* Device interface */ DEVMETHOD(device_probe, chip_probe), DEVMETHOD(device_attach, chip_attach), { 0, 0 } }; static driver_t chip_driver = { "chip", chip_methods, 1, }; static devclass_t chip_devclass; DRIVER_MODULE(chip, pci, chip_driver, chip_devclass, 0, 0); /*--------------------------------------------------------- ** ** Catchall driver for VGA devices ** ** By Garrett Wollman ** ** **--------------------------------------------------------- */ static const char* vga_match(device_t dev) { u_int id = pci_get_devid(dev); const char *vendor, *chip, *type; vendor = chip = type = 0; switch (id & 0xffff) { case 0x10c8: vendor = "NeoMagic"; switch (id >> 16) { case 0x0004: chip = "NM2160 laptop"; break; case 0x0005: chip = "MagicMedia 256AV"; break; } break; case 0x121a: vendor = "3Dfx"; type = "graphics accelerator"; switch (id >> 16) { case 0x0003: chip = "Voodoo Banshee"; break; case 0x0005: chip = "Voodoo 3"; break; } break; case 0x102b: vendor = "Matrox"; type = "graphics accelerator"; switch (id >> 16) { case 0x0518: chip = "MGA 2085PX"; break; case 0x0519: chip = "MGA 2064W"; break; case 0x051a: chip = "MGA 1024SG/1064SG/1164SG"; break; case 0x051b: chip = "MGA 2164W"; break; } break; case 0x1002: vendor = "ATI"; type = "graphics accelerator"; switch (id >> 16) { case 0x4158: chip = "Mach32"; break; case 0x4758: chip = "Mach64-GX"; break; case 0x4358: chip = "Mach64-CX"; break; case 0x4354: chip = "Mach64-CT"; break; case 0x4554: chip = "Mach64-ET"; break; case 0x5654: chip = "Mach64-VT"; break; case 0x4754: chip = "Mach64-GT"; break; } break; case 0x1005: vendor = "Avance Logic"; switch (id >> 16) { case 0x2301: chip = "ALG2301"; break; } break; case 0x100c: vendor = "Tseng Labs"; type = "graphics accelerator"; switch (id >> 16) { case 0x3202: case 0x3205: case 0x3206: case 0x3207: chip = "ET4000 W32P"; break; case 0x3208: chip = "ET6000"; break; case 0x4702: chip = "ET6300"; break; } break; case 0x100e: vendor = "Weitek"; type = "graphics accelerator"; switch (id >> 16) { case 0x9001: chip = "P9000"; break; case 0x9100: chip = "P9100"; break; } break; case 0x1013: vendor = "Cirrus Logic"; switch (id >> 16) { case 0x0038: chip = "GD7548"; break; case 0x00a0: chip = "GD5430"; break; case 0x00a4: case 0x00a8: chip = "GD5434"; break; case 0x00ac: chip = "GD5436"; break; case 0x00b8: chip = "GD5446"; break; case 0x00d0: chip = "GD5462"; break; case 0x00d4: chip = "GD5464"; break; case 0x1200: chip = "GD7542"; break; case 0x1202: chip = "GD7543"; break; case 0x1204: chip = "GD7541"; break; } break; case 0x1023: vendor = "Trident"; break; /* let default deal with it */ case 0x102c: vendor = "Chips & Technologies"; if ((id >> 16) == 0x00d8) chip = "65545"; break; case 0x1033: vendor = "NEC"; switch (id >> 16) { case 0x0009: type = "PCI to PC-98 Core Graph bridge"; break; } break; case 0x1039: vendor = "SiS"; switch (id >> 16) { case 0x0001: chip = "86c201"; break; case 0x0002: chip = "86c202"; break; case 0x0205: chip = "86c205"; break; } break; case 0x105d: vendor = "Number Nine"; type = "graphics accelerator"; switch (id >> 16) { case 0x2309: case 0x2339: chip = "Imagine 128"; break; } break; case 0x1142: vendor = "Alliance"; switch (id >> 16) { case 0x3210: chip = "PM6410"; break; case 0x6422: chip = "PM6422"; break; case 0x6424: chip = "PMAT24"; break; } break; case 0x1163: vendor = "Rendition Verite"; switch (id >> 16) { case 0x0001: chip = "V1000"; break; case 0x2000: chip = "V2000"; break; } break; case 0x1236: vendor = "Sigma Designs"; if ((id >> 16) == 0x6401) chip = "64GX"; break; case 0x5333: vendor = "S3"; type = "graphics accelerator"; switch (id >> 16) { case 0x8811: chip = "Trio"; break; case 0x8812: chip = "Aurora 64"; break; case 0x8814: case 0x8901: chip = "Trio 64"; break; case 0x8902: chip = "Plato"; break; case 0x8880: chip = "868"; break; case 0x88b0: chip = "928"; break; case 0x88c0: case 0x88c1: chip = "864"; break; case 0x88d0: case 0x88d1: chip = "964"; break; case 0x88f0: chip = "968"; break; case 0x5631: chip = "ViRGE"; break; case 0x883d: chip = "ViRGE VX"; break; case 0x8a01: chip = "ViRGE DX/GX"; break; } break; case 0xedd8: vendor = "ARK Logic"; switch (id >> 16) { case 0xa091: chip = "1000PV"; break; case 0xa099: chip = "2000PV"; break; case 0xa0a1: chip = "2000MT"; break; case 0xa0a9: chip = "2000MI"; break; } break; case 0x3d3d: vendor = "3D Labs"; type = "graphics accelerator"; switch (id >> 16) { case 0x0001: chip = "300SX"; break; case 0x0002: chip = "500TX"; break; case 0x0003: chip = "Delta"; break; case 0x0004: chip = "PerMedia"; break; } break; case 0x10de: vendor = "NVidia"; type = "graphics accelerator"; switch (id >> 16) { case 0x0020: chip = "Riva TNT"; break; case 0x0028: chip = "Riva TNT2"; break; } break; case 0x12d2: vendor = "NVidia"; type = "graphics accelerator"; switch (id >> 16) { case 0x0018: chip = "Riva128"; break; } break; } if (vendor && chip) { char *buf; int len; if (type == 0) type = "SVGA controller"; len = strlen(vendor) + strlen(chip) + strlen(type) + 4; MALLOC(buf, char *, len, M_TEMP, M_NOWAIT); if (buf) sprintf(buf, "%s %s %s", vendor, chip, type); return buf; } switch (pci_get_class(dev)) { case PCIC_OLD: if (pci_get_subclass(dev) != PCIS_OLD_VGA) return 0; if (type == 0) type = "VGA-compatible display device"; break; case PCIC_DISPLAY: if (type == 0) { if (pci_get_subclass(dev) == PCIS_DISPLAY_VGA) type = "VGA-compatible display device"; else { /* * If it isn't a vga display device, * don't pretend we found one. */ return 0; } } break; default: return 0; }; /* * If we got here, we know for sure it's some sort of display * device, but we weren't able to identify it specifically. * At a minimum we can return the type, but we'd like to * identify the vendor and chip ID if at all possible. * (Some of the checks above intentionally don't bother for * vendors where we know the chip ID is the same as the * model number.) */ if (vendor) { char *buf; int len; len = strlen(vendor) + strlen(type) + 2 + 6 + 4 + 1; MALLOC(buf, char *, len, M_TEMP, M_NOWAIT); if (buf) sprintf(buf, "%s model %04x %s", vendor, id >> 16, type); return buf; } return type; } static int vga_probe(device_t dev) { const char *desc; desc = vga_match(dev); if (desc) { device_set_desc(dev, desc); return -100; /* Low match priority */ } return ENXIO; } static int vga_attach(device_t dev) { /* ** If the assigned addresses are remapped, ** the console driver has to be informed about the new address. */ #if 0 vm_offset_t va; vm_offset_t pa; int reg; for (reg = PCI_MAP_REG_START; reg < PCI_MAP_REG_END; reg += 4) (void) pci_map_mem (tag, reg, &va, &pa); #endif return 0; } static device_method_t vga_methods[] = { /* Device interface */ DEVMETHOD(device_probe, vga_probe), DEVMETHOD(device_attach, vga_attach), { 0, 0 } }; static driver_t vga_driver = { "vga-pci", vga_methods, 1, }; static devclass_t vga_devclass; DRIVER_MODULE(vga, pci, vga_driver, vga_devclass, 0, 0); /*--------------------------------------------------------- ** ** Devices to ignore ** **--------------------------------------------------------- */ static int ign_probe (device_t dev) { switch (pci_get_devid(dev)) { case 0x10001042ul: /* wd */ return 0; /* return ("SMC FDC 37c665");*/ }; return ENXIO; } static int ign_attach (device_t dev) { return 0; } static device_method_t ign_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ign_probe), DEVMETHOD(device_attach, ign_attach), { 0, 0 } }; static driver_t ign_driver = { "ign", ign_methods, 1, }; static devclass_t ign_devclass; DRIVER_MODULE(ign, pci, ign_driver, ign_devclass, 0, 0); Index: head/sys/pci/uhci_pci.c =================================================================== --- head/sys/pci/uhci_pci.c (revision 54072) +++ head/sys/pci/uhci_pci.c (revision 54073) @@ -1,298 +1,299 @@ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* Universal Host Controller Interface * * UHCI spec: http://www.intel.com/ */ /* The low level controller code for UHCI has been split into * PCI probes and UHCI specific code. This was done to facilitate the * sharing of code between *BSD's */ #include "opt_bus.h" #include #include #include #include #include #include #include #if defined(__FreeBSD__) #include #include #include #include #endif #include #include #include #include #include #include #include #include #define PCI_UHCI_VENDORID_INTEL 0x8086 #define PCI_UHCI_VENDORID_VIA 0x1106 #define PCI_UHCI_DEVICEID_PIIX3 0x70208086 static const char *uhci_device_piix3 = "Intel 82371SB (PIIX3) USB controller"; #define PCI_UHCI_DEVICEID_PIIX4 0x71128086 #define PCI_UHCI_DEVICEID_PIIX4E 0x71128086 /* no separate stepping */ static const char *uhci_device_piix4 = "Intel 82371AB/EB (PIIX4) USB controller"; #define PCI_UHCI_DEVICEID_VT83C572 0x30381106 static const char *uhci_device_vt83c572 = "VIA 83C572 USB controller"; static const char *uhci_device_generic = "UHCI (generic) USB controller"; #define PCI_UHCI_BASE_REG 0x20 static int uhci_pci_suspend(device_t self) { uhci_softc_t *sc = device_get_softc(self); bus_generic_suspend(self); return 0; } static int uhci_pci_resume(device_t self) { uhci_softc_t *sc = device_get_softc(self); #if 0 uhci_reset(sc); #endif bus_generic_resume(self); return 0; } static const char * uhci_pci_match(device_t self) { u_int32_t device_id = pci_get_devid(self); if (device_id == PCI_UHCI_DEVICEID_PIIX3) { return (uhci_device_piix3); } else if (device_id == PCI_UHCI_DEVICEID_PIIX4) { return (uhci_device_piix4); } else if (device_id == PCI_UHCI_DEVICEID_VT83C572) { return (uhci_device_vt83c572); } else { if ( pci_get_class(self) == PCIC_SERIALBUS && pci_get_subclass(self) == PCIS_SERIALBUS_USB && pci_get_progif(self) == PCI_INTERFACE_UHCI) { return (uhci_device_generic); } } return NULL; /* dunno... */ } static int uhci_pci_probe(device_t self) { const char *desc = uhci_pci_match(self); if (desc) { device_set_desc(self, desc); return 0; } else { return ENXIO; } } static int uhci_pci_attach(device_t self) { uhci_softc_t *sc = device_get_softc(self); device_t parent = device_get_parent(self); int rid; void *ih; struct resource *res; device_t usbus; int intr; int legsup; int err; rid = PCI_UHCI_BASE_REG; res = bus_alloc_resource(self, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); if (!res) { device_printf(self, "could not map ports\n"); return ENXIO; } sc->iot = rman_get_bustag(res); sc->ioh = rman_get_bushandle(res); /* disable interrupts */ bus_space_write_2(sc->iot, sc->ioh, UHCI_INTR, 0); rid = 0; res = bus_alloc_resource(self, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); if (res == NULL) { device_printf(self, "could not allocate irq\n"); return ENOMEM; } - usbus = device_add_child(self, "usb", -1, sc); + usbus = device_add_child(self, "usb", -1); + device_set_ivars(usbus, sc); if (!usbus) { device_printf(self, "could not add USB device\n"); return ENOMEM; } switch (pci_get_devid(self)) { case PCI_UHCI_DEVICEID_PIIX3: device_set_desc(usbus, uhci_device_piix3); sprintf(sc->sc_vendor, "Intel"); break; case PCI_UHCI_DEVICEID_PIIX4: device_set_desc(usbus, uhci_device_piix4); sprintf(sc->sc_vendor, "Intel"); break; case PCI_UHCI_DEVICEID_VT83C572: device_set_desc(usbus, uhci_device_vt83c572); sprintf(sc->sc_vendor, "VIA"); break; default: device_printf(self, "(New UHCI DeviceId=0x%08x)\n", pci_get_devid(self)); device_set_desc(usbus, uhci_device_generic); sprintf(sc->sc_vendor, "(0x%08x)", pci_get_devid(self)); } switch(pci_read_config(self, PCI_USBREV, 4) & PCI_USBREV_MASK) { case PCI_USBREV_PRE_1_0: sc->sc_bus.usbrev = USBREV_PRE_1_0; break; case PCI_USBREV_1_0: sc->sc_bus.usbrev = USBREV_1_0; break; default: sc->sc_bus.usbrev = USBREV_UNKNOWN; break; } intr = pci_read_config(self, PCIR_INTLINE, 1); if (intr == 0 || intr == 255) { device_printf(self, "Invalid irq %d\n", intr); device_printf(self, "Please switch on USB support and switch PNP-OS to 'No' in BIOS\n"); device_delete_child(self, usbus); return ENXIO; } err = BUS_SETUP_INTR(parent, self, res, INTR_TYPE_BIO, (driver_intr_t *) uhci_intr, sc, &ih); if (err) { device_printf(self, "could not setup irq, %d\n", err); device_delete_child(self, usbus); return err; } /* Verify that the PIRQD enable bit is set, some BIOS's don't do that */ legsup = pci_read_config(self, PCI_LEGSUP, 4); if ( !(legsup & PCI_LEGSUP_USBPIRQDEN) ) { #ifndef USB_DEBUG if (bootverbose) #endif device_printf(self, "PIRQD enable not set\n"); legsup |= PCI_LEGSUP_USBPIRQDEN; pci_write_config(self, PCI_LEGSUP, legsup, 4); } sc->sc_bus.bdev = usbus; err = uhci_init(sc); if (!err) err = device_probe_and_attach(sc->sc_bus.bdev); if (err) { device_printf(self, "init failed\n"); /* disable interrupts that might have been switched on * in uhci_init */ bus_space_write_2(sc->iot, sc->ioh, UHCI_INTR, 0); err = BUS_TEARDOWN_INTR(parent, self, res, ih); if (err) /* XXX or should we panic? */ device_printf(self, "could not tear down irq, %d\n", err); device_delete_child(self, usbus); return EIO; /* XXX arbitrary value */ } return 0; } static device_method_t uhci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uhci_pci_probe), DEVMETHOD(device_attach, uhci_pci_attach), DEVMETHOD(device_suspend, uhci_pci_suspend), DEVMETHOD(device_resume, uhci_pci_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), { 0, 0 } }; static driver_t uhci_driver = { "uhci", uhci_methods, sizeof(uhci_softc_t), }; static devclass_t uhci_devclass; DRIVER_MODULE(uhci, pci, uhci_driver, uhci_devclass, 0, 0); Index: head/sys/sys/bus.h =================================================================== --- head/sys/sys/bus.h (revision 54072) +++ head/sys/sys/bus.h (revision 54073) @@ -1,370 +1,369 @@ /*- * Copyright (c) 1997,1998 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _SYS_BUS_H_ #define _SYS_BUS_H_ #ifdef KERNEL #include /* * Forward declarations */ typedef struct device *device_t; typedef struct driver driver_t; typedef struct device_method device_method_t; typedef struct devclass *devclass_t; typedef struct device_ops *device_ops_t; typedef struct device_op_desc *device_op_desc_t; typedef void driver_intr_t(void*); /* * We define this in terms of bits because some devices may belong * to multiple classes (and therefore need to be included in * multiple interrupt masks, which is what this really serves to * indicate. Buses which do interrupt remapping will want to * change their type to reflect what sort of devices are underneath. */ enum intr_type { INTR_TYPE_TTY = 1, INTR_TYPE_BIO = 2, INTR_TYPE_NET = 4, INTR_TYPE_CAM = 8, INTR_TYPE_MISC = 16, INTR_TYPE_FAST = 128 }; typedef int (*devop_t)(void); struct device_method { device_op_desc_t desc; devop_t func; }; struct driver { const char *name; /* driver name */ device_method_t *methods; /* method table */ size_t softc; /* size of device softc struct */ void *priv; /* driver private data */ device_ops_t ops; /* compiled method table */ int refs; /* # devclasses containing driver */ }; typedef enum device_state { DS_NOTPRESENT, /* not probed or probe failed */ DS_ALIVE, /* probe succeeded */ DS_ATTACHED, /* attach method called */ DS_BUSY /* device is open */ } device_state_t; /* * Definitions for drivers which need to keep simple lists of resources * for their child devices. */ struct resource; struct resource_list_entry { SLIST_ENTRY(resource_list_entry) link; int type; /* type argument to alloc_resource */ int rid; /* resource identifier */ struct resource *res; /* the real resource when allocated */ u_long start; /* start of resource range */ u_long end; /* end of resource range */ u_long count; /* count within range */ }; SLIST_HEAD(resource_list, resource_list_entry); /* * Initialise a resource list. */ void resource_list_init(struct resource_list *rl); /* * Reclaim memory used by a resource list. */ void resource_list_free(struct resource_list *rl); /* * Add a resource entry or modify an existing entry if one exists with * the same type and rid. */ void resource_list_add(struct resource_list *rl, int type, int rid, u_long start, u_long end, u_long count); /* * Find a resource entry by type and rid. */ struct resource_list_entry* resource_list_find(struct resource_list *rl, int type, int rid); /* * Delete a resource entry. */ void resource_list_delete(struct resource_list *rl, int type, int rid); /* * Implement BUS_ALLOC_RESOURCE by looking up a resource from the list * and passing the allocation up to the parent of bus. This assumes * that the first entry of device_get_ivars(child) is a struct * resource_list. This also handles 'passthrough' allocations where a * child is a remote descendant of bus by passing the allocation up to * the parent of bus. */ struct resource * resource_list_alloc(struct resource_list *rl, device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); /* * Implement BUS_RELEASE_RESOURCE. */ int resource_list_release(struct resource_list *rl, device_t bus, device_t child, int type, int rid, struct resource *res); /* * The root bus, to which all top-level busses are attached. */ extern device_t root_bus; extern devclass_t root_devclass; void root_bus_configure(void); /* * Useful functions for implementing busses. */ int bus_generic_activate_resource(device_t dev, device_t child, int type, int rid, struct resource *r); struct resource *bus_generic_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); int bus_generic_attach(device_t dev); int bus_generic_deactivate_resource(device_t dev, device_t child, int type, int rid, struct resource *r); int bus_generic_detach(device_t dev); void bus_generic_driver_added(device_t dev, driver_t *driver); int bus_print_child_header(device_t dev, device_t child); int bus_print_child_footer(device_t dev, device_t child); int bus_generic_print_child(device_t dev, device_t child); int bus_generic_probe(device_t dev); int bus_generic_read_ivar(device_t dev, device_t child, int which, uintptr_t *result); int bus_generic_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r); int bus_generic_resume(device_t dev); int bus_generic_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep); int bus_generic_shutdown(device_t dev); int bus_generic_suspend(device_t dev); int bus_generic_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie); int bus_generic_write_ivar(device_t dev, device_t child, int which, uintptr_t value); /* * Wrapper functions for the BUS_*_RESOURCE methods to make client code * a little simpler. */ struct resource *bus_alloc_resource(device_t dev, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); int bus_activate_resource(device_t dev, int type, int rid, struct resource *r); int bus_deactivate_resource(device_t dev, int type, int rid, struct resource *r); int bus_release_resource(device_t dev, int type, int rid, struct resource *r); int bus_setup_intr(device_t dev, struct resource *r, int flags, driver_intr_t handler, void *arg, void **cookiep); int bus_teardown_intr(device_t dev, struct resource *r, void *cookie); int bus_set_resource(device_t dev, int type, int rid, u_long start, u_long count); int bus_get_resource(device_t dev, int type, int rid, u_long *startp, u_long *countp); u_long bus_get_resource_start(device_t dev, int type, int rid); u_long bus_get_resource_count(device_t dev, int type, int rid); void bus_delete_resource(device_t dev, int type, int rid); /* * Access functions for device. */ -device_t device_add_child(device_t dev, const char *name, int unit, - void *ivp); +device_t device_add_child(device_t dev, const char *name, int unit); device_t device_add_child_ordered(device_t dev, int order, - const char *name, int unit, - void *ivp); + const char *name, int unit); void device_busy(device_t dev); int device_delete_child(device_t dev, device_t child); int device_detach(device_t dev); void device_disable(device_t dev); void device_enable(device_t dev); device_t device_find_child(device_t dev, const char *classname, int unit); const char *device_get_desc(device_t dev); devclass_t device_get_devclass(device_t dev); driver_t *device_get_driver(device_t dev); u_int32_t device_get_flags(device_t dev); device_t device_get_parent(device_t dev); int device_get_children(device_t dev, device_t **listp, int *countp); void *device_get_ivars(device_t dev); +void device_set_ivars(device_t dev, void *ivars); const char *device_get_name(device_t dev); const char *device_get_nameunit(device_t dev); void *device_get_softc(device_t dev); device_state_t device_get_state(device_t dev); int device_get_unit(device_t dev); int device_is_alive(device_t dev); /* did probe succeed? */ int device_is_enabled(device_t dev); int device_is_quiet(device_t dev); int device_print_prettyname(device_t dev); int device_printf(device_t dev, const char *, ...) __printflike(2, 3); int device_probe_and_attach(device_t dev); void device_quiet(device_t dev); void device_set_desc(device_t dev, const char* desc); void device_set_desc_copy(device_t dev, const char* desc); int device_set_devclass(device_t dev, const char *classname); int device_set_driver(device_t dev, driver_t *driver); void device_set_flags(device_t dev, u_int32_t flags); int device_shutdown(device_t dev); void device_unbusy(device_t dev); void device_verbose(device_t dev); /* * Access functions for devclass. */ int devclass_add_driver(devclass_t dc, driver_t *driver); int devclass_delete_driver(devclass_t dc, driver_t *driver); devclass_t devclass_create(const char *classname); devclass_t devclass_find(const char *classname); driver_t *devclass_find_driver(devclass_t dc, const char *classname); const char *devclass_get_name(devclass_t dc); device_t devclass_get_device(devclass_t dc, int unit); void *devclass_get_softc(devclass_t dc, int unit); int devclass_get_devices(devclass_t dc, device_t **listp, int *countp); int devclass_get_maxunit(devclass_t dc); /* * Access functions for device resources. */ int resource_int_value(const char *name, int unit, const char *resname, int *result); int resource_long_value(const char *name, int unit, const char *resname, long *result); int resource_string_value(const char *name, int unit, const char *resname, char **result); int resource_query_string(int i, const char *resname, const char *value); char *resource_query_name(int i); int resource_query_unit(int i); int resource_locate(int i, const char *resname); int resource_set_int(const char *name, int unit, const char *resname, int value); int resource_set_long(const char *name, int unit, const char *resname, long value); int resource_set_string(const char *name, int unit, const char *resname, const char *value); int resource_count(void); /* * Shorthand for constructing method tables. */ #define DEVMETHOD(NAME, FUNC) { &NAME##_desc, (devop_t) FUNC } /* * Some common device interfaces. */ #include "device_if.h" #include "bus_if.h" struct module; int driver_module_handler(struct module *, int, void *); /* * Module support for automatically adding drivers to busses. */ struct driver_module_data { int (*dmd_chainevh)(struct module *, int, void *); void *dmd_chainarg; const char *dmd_busname; driver_t **dmd_drivers; int dmd_ndrivers; devclass_t *dmd_devclass; }; #define DRIVER_MODULE(name, busname, driver, devclass, evh, arg) \ \ static driver_t *name##_##busname##_driver_list[] = { &driver }; \ static struct driver_module_data name##_##busname##_driver_mod = { \ evh, arg, \ #busname, \ name##_##busname##_driver_list, \ (sizeof name##_##busname##_driver_list) / \ (sizeof name##_##busname##_driver_list[0]), \ &devclass \ }; \ \ static moduledata_t name##_##busname##_mod = { \ #busname "/" #name, \ driver_module_handler, \ &name##_##busname##_driver_mod \ }; \ DECLARE_MODULE(name##_##busname, name##_##busname##_mod, \ SI_SUB_DRIVERS, SI_ORDER_MIDDLE) #define MULTI_DRIVER_MODULE(name, busname, drivers, devclass, evh, arg) \ \ static driver_t name##_##busname##_driver_list[] = drivers; \ static struct driver_module_data name##_##busname##_driver_mod = { \ evh, arg, \ #busname, \ name##_##busname##_driver_list, \ (sizeof name##_##busname##_driver_list) / \ (sizeof name##_##busname##_driver_list[0]), \ &devclass \ }; \ \ static moduledata_t name##_##busname##_mod = { \ #busname "/" #name, \ driver_module_handler, \ &name##_##busname##_driver_mod \ }; \ DECLARE_MODULE(name##_##busname, name##_##busname##_mod, \ SI_SUB_DRIVERS, SI_ORDER_MIDDLE) #endif /* KERNEL */ #endif /* !_SYS_BUS_H_ */