From: aaron Date: Sat, 8 Apr 2000 05:50:49 +0000 (+0000) Subject: Initial check-in for support of 32-bit CardBus PC Cards; from NetBSD. On many X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=e77beeccf54d23cd429572966814704a7fd08e4f;p=openbsd Initial check-in for support of 32-bit CardBus PC Cards; from NetBSD. On many machines, this code needs the new PCIBIOS* options enabled in the kernel config file to work, but your mileage may vary. Included is a working 3c575 driver for 3Com 10/100 CardBus PC Card NICs (tested only with the 'C' revision). The 3c575 is the pccard version of the PCI EtherLink XL cards, and thus the xl driver has been split into /sys/dev/ic. --- diff --git a/sys/arch/i386/conf/GENERIC b/sys/arch/i386/conf/GENERIC index 8e2b6c63234..d1d635cbfa4 100644 --- a/sys/arch/i386/conf/GENERIC +++ b/sys/arch/i386/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.174 2000/04/07 22:25:43 aaron Exp $ +# $OpenBSD: GENERIC,v 1.175 2000/04/08 05:50:49 aaron Exp $ # $NetBSD: GENERIC,v 1.48 1996/05/20 18:17:23 mrg Exp $ # # GENERIC -- everything that's currently supported @@ -64,6 +64,12 @@ pcic2 at isa? port 0x3e4 iomem 0xe0000 iosiz 0x4000 # PCMCIA bus support pcmcia* at pcic? controller ? socket ? +# CardBus bus support +#cardbus* at cardslot? +#pcmcia* at cardslot? +#cbb* at pci? dev ? function ? +#cardslot* at cbb? + # PCI USB Controllers #uhci* at pci? # Universal Host Controller (Intel) #ohci* at pci? # Open Host Controller @@ -260,6 +266,7 @@ sm* at pcmcia? function ? # PCMCIA based sm ethernet xe* at pcmcia? function ? # Xircom ethernet fpa* at pci? dev ? function ? # DEC DEFPA FDDI xl* at pci? dev ? function ? # 3c9xx ethernet +#xl* at cardbus? dev ? function ? # 3c575 ethernet rl* at pci? dev ? function ? # RealTek 81[23]9 ethernet tx* at pci? dev ? function ? # SMC 83C170 EPIC ethernet tl* at pci? dev ? function ? # Compaq Thunderlan ethernet diff --git a/sys/arch/i386/conf/files.i386 b/sys/arch/i386/conf/files.i386 index 809930770b0..778c72658c1 100644 --- a/sys/arch/i386/conf/files.i386 +++ b/sys/arch/i386/conf/files.i386 @@ -1,4 +1,4 @@ -# $OpenBSD: files.i386,v 1.63 2000/03/26 22:38:32 mickey Exp $ +# $OpenBSD: files.i386,v 1.64 2000/04/08 05:50:49 aaron Exp $ # $NetBSD: files.i386,v 1.73 1996/05/07 00:58:36 thorpej Exp $ # # new style config file for i386 architecture @@ -255,6 +255,12 @@ file arch/i386/i386/apmcall.S apm file arch/i386/i386/bios32.c bios32 +# +# CARDBUS +# +include "dev/cardbus/files.cardbus" +file arch/i386/i386/rbus_machdep.c cardbus + # XXXX pcic here because it needs to be late. The catch: pcic needs # to be late, so devices which attach to it are attached late. But it # needs to be before its isa and pci attachments. This answer is @@ -264,7 +270,7 @@ file arch/i386/i386/bios32.c bios32 # XXX this needs to be done very late, so it's done here. This feels # like a kludge, but it might be for the best. -device pcic {[controller = -1], [socket = -1]} +device pcic: pcmciabus file dev/ic/i82365.c pcic # PCIC pcmcia controller on ISA bus. diff --git a/sys/arch/i386/i386/machdep.c b/sys/arch/i386/i386/machdep.c index 7835c4c89d5..6dc18b614a0 100644 --- a/sys/arch/i386/i386/machdep.c +++ b/sys/arch/i386/i386/machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: machdep.c,v 1.128 2000/03/23 09:59:54 art Exp $ */ +/* $OpenBSD: machdep.c,v 1.129 2000/04/08 05:50:50 aaron Exp $ */ /* $NetBSD: machdep.c,v 1.214 1996/11/10 03:16:17 thorpej Exp $ */ /*- @@ -2386,6 +2386,29 @@ bus_space_map(t, bpa, size, cacheable, bshp) return (error); } +int +_bus_space_map(t, bpa, size, cacheable, bshp) + bus_space_tag_t t; + bus_addr_t bpa; + bus_size_t size; + int cacheable; + bus_space_handle_t *bshp; +{ + /* + * For I/O space, that's all she wrote. + */ + if (t == I386_BUS_SPACE_IO) { + *bshp = bpa; + return (0); + } + + /* + * For memory space, map the bus physical address to + * a kernel virtual address. + */ + return (bus_mem_add_mapping(bpa, size, cacheable, bshp)); +} + int bus_space_alloc(t, rstart, rend, size, alignment, boundary, cacheable, bpap, bshp) diff --git a/sys/arch/i386/i386/rbus_machdep.c b/sys/arch/i386/i386/rbus_machdep.c new file mode 100644 index 00000000000..d38072ea295 --- /dev/null +++ b/sys/arch/i386/i386/rbus_machdep.c @@ -0,0 +1,163 @@ +/* $OpenBSD: rbus_machdep.c,v 1.1 2000/04/08 05:50:50 aaron Exp $ */ +/* $NetBSD: rbus_machdep.c,v 1.2 1999/10/15 06:43:06 haya Exp $ */ + +/* + * Copyright (c) 1999 + * HAYAKAWA Koichi. 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 HAYAKAWA Koichi. + * 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. + */ + +/* $Id: rbus_machdep.c,v 1.1 2000/04/08 05:50:50 aaron Exp $ */ + +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include + +#include +#include +#include + +#include + + + +/********************************************************************** + * void _bus_space_unmap(bus_space_tag bst, bus_space_handle bsh, + * bus_size_t size, bus_addr_t *adrp) + * + * This function unmaps memory- or io-space mapped by the function + * _bus_space_map(). This function works nearly as same as + * bus_space_map(), but this function does not ask kernel + * built-in extents and returns physical address of the bus space, + * for the convenience of the extra extent manager. + * + * I suppose this function should be in arch/i386/i386/machdep.c, + * but it is not. + **********************************************************************/ +void +_bus_space_unmap(t, bsh, size, adrp) + bus_space_tag_t t; + bus_space_handle_t bsh; + bus_size_t size; + bus_addr_t *adrp; +{ + u_long va, endva; + bus_addr_t bpa; + + /* + * Find the correct extent and bus physical address. + */ + if (t == I386_BUS_SPACE_IO) { + bpa = bsh; + } else if (t == I386_BUS_SPACE_MEM) { + if (bsh >= atdevbase && (bsh + size) <= (atdevbase + IOM_SIZE)) { + bpa = (bus_addr_t)ISA_PHYSADDR(bsh); + } else { + + va = i386_trunc_page(bsh); + endva = i386_round_page(bsh + size); + +#ifdef DIAGNOSTIC + if (endva <= va) { + panic("_i386_memio_unmap: overflow"); + } +#endif + +#if __NetBSD_Version__ > 104050000 + if (pmap_extract(pmap_kernel(), va, &bpa) == FALSE) { + panic("_i386_memio_unmap:i386/rbus_machdep.c wrong virtual address"); + } + bpa += (bsh & PGOFSET); +#else + bpa = pmap_extract(pmap_kernel(), va) + (bsh & PGOFSET); +#endif + + /* + * Free the kernel virtual mapping. + */ + uvm_km_free(kernel_map, va, endva - va); + } + } else { + panic("_i386_memio_unmap: bad bus space tag"); + } + + if (adrp != NULL) { + *adrp = bpa; + } +} + + + + +/********************************************************************** + * rbus_tag_t rbus_fakeparent_mem(struct pci_attach_args *pa) + * + * This function allocates a memory space from 1 GB to 1.25 GB. + **********************************************************************/ +rbus_tag_t +rbus_pccbb_parent_mem(pa) + struct pci_attach_args *pa; +{ + bus_addr_t start = 0x40000000; /* 1 GB */ + bus_size_t size = 0x08000000; /* 128 MB */ + bus_space_handle_t memh; /* fake */ + + start += pa->pa_function * size; + + bus_space_map(pa->pa_memt, start, size, 0, &memh); + + return rbus_new_root_delegate(pa->pa_memt, start, size, 0); +} + + +/********************************************************************** + * rbus_tag_t rbus_pccbb_parent_io(struct pci_attach_args *pa) + **********************************************************************/ +rbus_tag_t +rbus_pccbb_parent_io(pa) + struct pci_attach_args *pa; +{ + bus_addr_t start = 0x2000; + bus_size_t size = 0x0800; + bus_space_handle_t ioh; + + start += pa->pa_function * size; + + bus_space_map(pa->pa_iot, start, size, 0, &ioh); + + return rbus_new_root_delegate(pa->pa_iot, start, size, 0); +} diff --git a/sys/arch/i386/include/bus.h b/sys/arch/i386/include/bus.h index 0ee26b6ad18..99df8f1dd0a 100644 --- a/sys/arch/i386/include/bus.h +++ b/sys/arch/i386/include/bus.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bus.h,v 1.19 2000/03/15 03:56:49 todd Exp $ */ +/* $OpenBSD: bus.h,v 1.20 2000/04/08 05:50:50 aaron Exp $ */ /* $NetBSD: bus.h,v 1.6 1996/11/10 03:19:25 thorpej Exp $ */ /*- @@ -95,6 +95,9 @@ typedef u_long bus_space_handle_t; int bus_space_map __P((bus_space_tag_t t, bus_addr_t addr, bus_size_t size, int cacheable, bus_space_handle_t *bshp)); +/* like bus_space_map(), but without extent map checking/allocation */ +int _bus_space_map __P((bus_space_tag_t t, bus_addr_t addr, + bus_size_t size, int cacheable, bus_space_handle_t *bshp)); void bus_space_unmap __P((bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t size)); int bus_space_subregion __P((bus_space_tag_t t, bus_space_handle_t bsh, diff --git a/sys/arch/i386/include/rbus_machdep.h b/sys/arch/i386/include/rbus_machdep.h new file mode 100644 index 00000000000..7bb855d1589 --- /dev/null +++ b/sys/arch/i386/include/rbus_machdep.h @@ -0,0 +1,53 @@ +/* $OpenBSD: rbus_machdep.h,v 1.1 2000/04/08 05:50:50 aaron Exp $ */ +/* $NetBSD: rbus_machdep.h,v 1.2 1999/10/15 06:43:05 haya Exp $ */ + +/* + * Copyright (c) 1999 + * HAYAKAWA Koichi. 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 HAYAKAWA Koichi. + * 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. + */ + + +#if !defined _ARCH_I386_I386_RBUS_MACHDEP_H_ +#define _ARCH_I386_I386_RBUS_MACHDEP_H_ + +struct pci_attach_args; /* XXX */ + +void _bus_space_unmap __P((bus_space_tag_t, bus_space_handle_t, + bus_size_t, bus_addr_t *)); + +#define md_space_map(bt, physaddr, size, flags, bshp) \ + _bus_space_map((bt), (physaddr), (size), (flags), (bshp)) + +#define md_space_unmap(bt, bsh, size, adrp) \ + _bus_space_unmap((bt), (bsh), (size), (adrp)) + + +rbus_tag_t rbus_pccbb_parent_io __P((struct pci_attach_args *pa)); +rbus_tag_t rbus_pccbb_parent_mem __P((struct pci_attach_args *pa)); + +#endif /* _ARCH_I386_I386_RBUS_MACHDEP_H_ */ diff --git a/sys/conf/files b/sys/conf/files index 2bad87b3bd6..6715a328cd8 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.156 2000/04/03 01:02:00 mickey Exp $ +# $OpenBSD: files,v 1.157 2000/04/08 05:50:50 aaron Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -116,6 +116,9 @@ file dev/ic/rlnsubr.c rln device le: ether, ifnet, ifmedia file dev/ic/am7990.c le +device xl: ether, ifnet, ifmedia, mii +file dev/ic/xl.c xl + # SMC 91Cxx Ethernet Controller device sm: ether, ifnet, ifmedia file dev/ic/smc91cxx.c sm @@ -170,9 +173,11 @@ file dev/ic/font_8x16.c vgafb & (vgafb_isa | vgafb_pci) define isabus { } # ISA attachment define eisabus { } # EISA attachment define pcibus {[bus = -1]} # PCI attachment -define pcmciabus { } # PCMCIA attachment define tcbus { } # TurboChannel attachment define usbus { } # USB attachment +define pcmciabus { [controller = -1], [socket = -1]} # PCMCIA attachment +define cbbus {[slot = -1]} # CardBus attachment +define pcmciaslot {[slot = -1]} # PCMCIA slot itself # UHCI USB controller device uhci: usbus diff --git a/sys/dev/cardbus/cardbus.c b/sys/dev/cardbus/cardbus.c new file mode 100644 index 00000000000..20546bf90de --- /dev/null +++ b/sys/dev/cardbus/cardbus.c @@ -0,0 +1,934 @@ +/* $OpenBSD: cardbus.c,v 1.1 2000/04/08 05:50:52 aaron Exp $ */ +/* $NetBSD: cardbus.c,v 1.24 2000/04/02 19:11:37 mycroft Exp $ */ + +/* + * Copyright (c) 1997, 1998, 1999 and 2000 + * HAYAKAWA Koichi. 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 HAYAKAWA Koichi. + * 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 /* XXX */ +#include /* XXX */ + +#include + +#if defined CARDBUS_DEBUG +#define STATIC +#define DPRINTF(a) printf a +#else +#define STATIC static +#define DPRINTF(a) +#endif + +extern int cold; + +STATIC void cardbusattach __P((struct device *, struct device *, void *)); +/* STATIC int cardbusprint __P((void *, const char *)); */ +int cardbus_attach_card __P((struct cardbus_softc *)); + +STATIC int cardbusmatch __P((struct device *, void *, void *)); +static int cardbussubmatch __P((struct device *, void *, void *)); +static int cardbusprint __P((void *, const char *)); + +typedef void (*tuple_decode_func)(u_int8_t*, int, void*); + +static int decode_tuples __P((u_int8_t *, int, tuple_decode_func, void*)); +#ifdef CARDBUS_DEBUG +static void print_tuple __P((u_int8_t*, int, void*)); +#endif + +static int cardbus_read_tuples __P((struct cardbus_attach_args *, + cardbusreg_t, u_int8_t *, size_t)); + +static void enable_function __P((struct cardbus_softc *, int, int)); +static void disable_function __P((struct cardbus_softc *, int)); + + +struct cfattach cardbus_ca = { + sizeof(struct cardbus_softc), cardbusmatch, cardbusattach +}; + +#ifndef __NetBSD_Version__ +struct cfdriver cardbus_cd = { + NULL, "cardbus", DV_DULL +}; +#endif + + +STATIC int +cardbusmatch(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct cfdata *cf = match; + struct cbslot_attach_args *cba = aux; + + if (strcmp(cba->cba_busname, cf->cf_driver->cd_name)) { + DPRINTF(("cardbusmatch: busname differs %s <=> %s\n", + cba->cba_busname, cf->cf_driver->cd_name)); + return 0; + } + + return 1; +} + + + +STATIC void +cardbusattach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct cardbus_softc *sc = (void *)self; + struct cbslot_attach_args *cba = aux; + int cdstatus; + + sc->sc_bus = cba->cba_bus; + sc->sc_device = 0; + sc->sc_intrline = cba->cba_intrline; + sc->sc_cacheline = cba->cba_cacheline; + sc->sc_lattimer = cba->cba_lattimer; + + printf(": bus %d device %d", sc->sc_bus, sc->sc_device); + printf(" cacheline 0x%x, lattimer 0x%x\n", sc->sc_cacheline,sc->sc_lattimer); + + sc->sc_iot = cba->cba_iot; /* CardBus I/O space tag */ + sc->sc_memt = cba->cba_memt; /* CardBus MEM space tag */ + sc->sc_dmat = cba->cba_dmat; /* DMA tag */ + sc->sc_cc = cba->cba_cc; + sc->sc_cf = cba->cba_cf; + +#if rbus + sc->sc_rbus_iot = cba->cba_rbus_iot; + sc->sc_rbus_memt = cba->cba_rbus_memt; +#endif + + sc->sc_funcs = NULL; + + cdstatus = 0; +} + +static int +cardbus_read_tuples(ca, cis_ptr, tuples, len) + struct cardbus_attach_args *ca; + cardbusreg_t cis_ptr; + u_int8_t *tuples; + size_t len; +{ + struct cardbus_softc *sc = ca->ca_ct->ct_sc; + cardbus_chipset_tag_t cc = ca->ca_ct->ct_cc; + cardbus_function_tag_t cf = ca->ca_ct->ct_cf; + cardbustag_t tag = ca->ca_tag; + cardbusreg_t command; + int found = 0; + + int i, j; + int cardbus_space = cis_ptr & CARDBUS_CIS_ASIMASK; + bus_space_handle_t bar_memh; + bus_size_t bar_size; + bus_addr_t bar_addr; + + int reg; + + memset(tuples, 0, len); + + cis_ptr = cis_ptr & CARDBUS_CIS_ADDRMASK; + + switch(cardbus_space) { + case CARDBUS_CIS_ASI_TUPLE: + DPRINTF(("%s: reading CIS data from configuration space\n", + sc->sc_dev.dv_xname)); + for (i = cis_ptr, j = 0; i < 0xff; i += 4) { + u_int32_t e = (cf->cardbus_conf_read)(cc, tag, i); + tuples[j] = 0xff & e; + e >>= 8; + tuples[j + 1] = 0xff & e; + e >>= 8; + tuples[j + 2] = 0xff & e; + e >>= 8; + tuples[j + 3] = 0xff & e; + j += 4; + } + found++; + break; + + case CARDBUS_CIS_ASI_BAR0: + case CARDBUS_CIS_ASI_BAR1: + case CARDBUS_CIS_ASI_BAR2: + case CARDBUS_CIS_ASI_BAR3: + case CARDBUS_CIS_ASI_BAR4: + case CARDBUS_CIS_ASI_BAR5: + case CARDBUS_CIS_ASI_ROM: + if(cardbus_space == CARDBUS_CIS_ASI_ROM) { + reg = CARDBUS_ROM_REG; + DPRINTF(("%s: reading CIS data from ROM\n", + sc->sc_dev.dv_xname)); + } else { + reg = CARDBUS_BASE0_REG + (cardbus_space - 1) * 4; + DPRINTF(("%s: reading CIS data from BAR%d\n", + sc->sc_dev.dv_xname, cardbus_space - 1)); + } + + /* XXX zero register so mapreg_map doesn't get confused by old + contents */ + cardbus_conf_write(cc, cf, tag, reg, 0); + if(Cardbus_mapreg_map(ca->ca_ct, reg, + CARDBUS_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT, + 0, + NULL, &bar_memh, &bar_addr, &bar_size)) { + printf("%s: failed to map memory\n", sc->sc_dev.dv_xname); + return 1; + } + + + if(cardbus_space == CARDBUS_CIS_ASI_ROM) { + cardbusreg_t exrom; + int save; + struct cardbus_rom_image_head rom_image; + struct cardbus_rom_image *p; + + save = splhigh(); + /* enable rom address decoder */ + exrom = cardbus_conf_read(cc, cf, tag, reg); + cardbus_conf_write(cc, cf, tag, reg, exrom | 1); + + command = cardbus_conf_read(cc, cf, tag, CARDBUS_COMMAND_STATUS_REG); + cardbus_conf_write(cc, cf, tag, CARDBUS_COMMAND_STATUS_REG, + command | CARDBUS_COMMAND_MEM_ENABLE); + + if(cardbus_read_exrom(ca->ca_memt, bar_memh, &rom_image)) + goto out; + + for(p = SIMPLEQ_FIRST(&rom_image); + p; + p = SIMPLEQ_NEXT(p, next)) { + if(p->rom_image == CARDBUS_CIS_ASI_ROM_IMAGE(cis_ptr)) { + bus_space_read_region_1(p->romt, p->romh, + CARDBUS_CIS_ADDR(cis_ptr), + tuples, 256); + found++; + } + break; + } + while((p = SIMPLEQ_FIRST(&rom_image)) != NULL) { + SIMPLEQ_REMOVE_HEAD(&rom_image, p, next); + free(p, M_DEVBUF); + } + out: + exrom = cardbus_conf_read(cc, cf, tag, reg); + cardbus_conf_write(cc, cf, tag, reg, exrom & ~1); + splx(save); + } else { + command = cardbus_conf_read(cc, cf, tag, CARDBUS_COMMAND_STATUS_REG); + cardbus_conf_write(cc, cf, tag, CARDBUS_COMMAND_STATUS_REG, + command | CARDBUS_COMMAND_MEM_ENABLE); + /* XXX byte order? */ + bus_space_read_region_1(ca->ca_memt, bar_memh, + cis_ptr, tuples, 256); + found++; + } + command = cardbus_conf_read(cc, cf, tag, CARDBUS_COMMAND_STATUS_REG); + cardbus_conf_write(cc, cf, tag, CARDBUS_COMMAND_STATUS_REG, + command & ~CARDBUS_COMMAND_MEM_ENABLE); + cardbus_conf_write(cc, cf, tag, reg, 0); +#if 0 + /* XXX unmap memory */ + (*ca->ca_ct->ct_cf->cardbus_space_free)(ca->ca_ct, + ca->ca_ct->ct_sc->sc_rbus_memt, + bar_memh, bar_size); +#endif + break; + +#ifdef DIAGNOSTIC + default: + panic("%s: bad CIS space (%d)", sc->sc_dev.dv_xname, cardbus_space); +#endif + } + return !found; +} + +static void +parse_tuple(u_int8_t *tuple, int len, void *data) +{ +#ifdef CARDBUS_DEBUG + static const char func[] = "parse_tuple"; +#endif + struct cardbus_cis_info *cis = data; + int bar_index; + int i; + char *p; + switch(tuple[0]) { + case PCMCIA_CISTPL_MANFID: + if(tuple[1] != 5) { + DPRINTF(("%s: wrong length manufacturer id (%d)\n", + func, tuple[1])); + break; + } + cis->manufacturer = tuple[2] | (tuple[3] << 8); + cis->product = tuple[4] | (tuple[5] << 8); + break; + case PCMCIA_CISTPL_VERS_1: + bcopy(tuple + 2, cis->cis1_info_buf, tuple[1]); + i = 0; + p = cis->cis1_info_buf + 2; + while(i < sizeof(cis->cis1_info) / sizeof(cis->cis1_info[0])) { + cis->cis1_info[i++] = p; + while(*p != '\0' && *p != '\xff') + p++; + if(*p == '\xff') + break; + p++; + } + break; + case PCMCIA_CISTPL_BAR: + if(tuple[1] != 6) { + DPRINTF(("%s: BAR with short length (%d)\n", func, tuple[1])); + break; + } + bar_index = tuple[2] & 7; + if(bar_index == 0) { + DPRINTF(("%s: invalid ASI in BAR tuple\n", func)); + break; + } + bar_index--; + cis->bar[bar_index].flags = tuple[2]; + cis->bar[bar_index].size = (tuple[4] << 0) | + (tuple[5] << 8) | + (tuple[6] << 16) | + (tuple[7] << 24); + break; + case PCMCIA_CISTPL_FUNCID: + cis->funcid = tuple[2]; + break; + + case PCMCIA_CISTPL_FUNCE: + if(cis->funcid == PCMCIA_FUNCTION_NETWORK && tuple[1] >= 8) { + if(tuple[2] == PCMCIA_TPLFE_TYPE_LAN_NID) { + if(tuple[3] > sizeof(cis->funce.network.netid)) { + DPRINTF(("%s: unknown network id type (len = %d)\n", + func, tuple[3])); + } else { + cis->funce.network.netid_present = 1; + bcopy(tuple + 4, cis->funce.network.netid, + tuple[3]); + } + } + } + break; + } +} + +/* + * int cardbus_attach_card(struct cardbus_softc *sc) + * + * This function attaches the card on the slot: turns on power, + * reads and analyses tuple, sets consifuration index. + * + * This function returns the number of recognised device functions. + * If no functions are recognised, return 0. + */ +int +cardbus_attach_card(sc) + struct cardbus_softc *sc; +{ + cardbus_chipset_tag_t cc; + cardbus_function_tag_t cf; + int cdstatus; + cardbustag_t tag; + cardbusreg_t id, class, cis_ptr; + cardbusreg_t bhlc; + u_int8_t tuple[2048]; + int function, nfunction; + struct cardbus_devfunc **previous_next = &(sc->sc_funcs); + struct device *csc; + int no_work_funcs = 0; + cardbus_devfunc_t ct; + + cc = sc->sc_cc; + cf = sc->sc_cf; + + DPRINTF(("cardbus_attach_card: cb%d start\n", sc->sc_dev.dv_unit)); + + /* inspect initial voltage */ + if (0 == (cdstatus = (cf->cardbus_ctrl)(cc, CARDBUS_CD))) { + DPRINTF(("cardbusattach: no CardBus card on cb%d\n", sc->sc_dev.dv_unit)); + return 0; + } + + enable_function(sc, cdstatus, 8); /* XXX use fake function 8 to + keep power on during whole + configuration */ + + function = 0; + + tag = cardbus_make_tag(cc, cf, sc->sc_bus, sc->sc_device, function); + + /* + * Wait until power comes up. Maxmum 500 ms. + */ + { + int i; + for (i = 0; i < 5; ++i) { + id = cardbus_conf_read(cc, cf, tag, CARDBUS_ID_REG); + if (id != 0xffffffff && id != 0) { + break; + } + if (cold) { /* before kernel thread invoked */ + delay(100*1000); + } else { /* thread context */ + if (tsleep((void *)sc, PCATCH, "cardbus", hz/10) != EWOULDBLOCK) { + break; + } + } + } + if (i == 5) { + return 0; + } + } + + bhlc = cardbus_conf_read(cc, cf, tag, CARDBUS_BHLC_REG); + if (CARDBUS_LATTIMER(bhlc) < 0x10) { + bhlc &= ~(CARDBUS_LATTIMER_MASK << CARDBUS_LATTIMER_SHIFT); + bhlc |= (0x10 << CARDBUS_LATTIMER_SHIFT); + cardbus_conf_write(cc, cf, tag, CARDBUS_BHLC_REG, bhlc); + } + + nfunction = CARDBUS_HDRTYPE_MULTIFN(bhlc) ? 8 : 1; + + for(function = 0; function < nfunction; function++) { + struct cardbus_attach_args ca; + + tag = cardbus_make_tag(cc, cf, sc->sc_bus, sc->sc_device, function); + + id = cardbus_conf_read(cc, cf, tag, CARDBUS_ID_REG); + class = cardbus_conf_read(cc, cf, tag, CARDBUS_CLASS_REG); + cis_ptr = cardbus_conf_read(cc, cf, tag, CARDBUS_CIS_REG); + + /* Invalid vendor ID value? */ + if (CARDBUS_VENDOR(id) == CARDBUS_VENDOR_INVALID) { + continue; + } + + DPRINTF(("cardbus_attach_card: Vendor 0x%x, Product 0x%x, CIS 0x%x\n", + CARDBUS_VENDOR(id), CARDBUS_PRODUCT(id), cis_ptr)); + + enable_function(sc, cdstatus, function); + + /* clean up every BAR */ + cardbus_conf_write(cc, cf, tag, CARDBUS_BASE0_REG, 0); + cardbus_conf_write(cc, cf, tag, CARDBUS_BASE1_REG, 0); + cardbus_conf_write(cc, cf, tag, CARDBUS_BASE2_REG, 0); + cardbus_conf_write(cc, cf, tag, CARDBUS_BASE3_REG, 0); + cardbus_conf_write(cc, cf, tag, CARDBUS_BASE4_REG, 0); + cardbus_conf_write(cc, cf, tag, CARDBUS_BASE5_REG, 0); + cardbus_conf_write(cc, cf, tag, CARDBUS_ROM_REG, 0); + + /* + * We need to allocate the ct here, since we might + * need it when reading the CIS + */ + if (NULL == (ct = (cardbus_devfunc_t)malloc(sizeof(struct cardbus_devfunc), + M_DEVBUF, M_NOWAIT))) { + panic("no room for cardbus_tag"); + } + + ct->ct_cc = sc->sc_cc; + ct->ct_cf = sc->sc_cf; + ct->ct_bus = sc->sc_bus; + ct->ct_dev = sc->sc_device; + ct->ct_func = function; + ct->ct_sc = sc; + ct->ct_next = NULL; + *previous_next = ct; + + memset(&ca, 0, sizeof(ca)); + + ca.ca_unit = sc->sc_dev.dv_unit; + ca.ca_ct = ct; + + ca.ca_iot = sc->sc_iot; + ca.ca_memt = sc->sc_memt; + ca.ca_dmat = sc->sc_dmat; + + ca.ca_tag = tag; + ca.ca_device = sc->sc_device; + ca.ca_function = function; + ca.ca_id = id; + ca.ca_class = class; + + ca.ca_intrline = sc->sc_intrline; + + bzero(tuple, 2048); + + if(cardbus_read_tuples(&ca, cis_ptr, tuple, sizeof(tuple))) { + printf("cardbus_attach_card: failed to read CIS\n"); + } else { +#ifdef CARDBUS_DEBUG + decode_tuples(tuple, 2048, print_tuple, NULL); +#endif + decode_tuples(tuple, 2048, parse_tuple, &ca.ca_cis); + } + + if (NULL == (csc = config_found_sm((void *)sc, &ca, cardbusprint, cardbussubmatch))) { + /* do not match */ + disable_function(sc, function); + free(ct, M_DEVBUF); + *previous_next = NULL; + } else { + /* found */ + previous_next = &(ct->ct_next); + ct->ct_device = csc; + ++no_work_funcs; + } + } + /* + * XXX power down pseudo function 8 (this will power down the card + * if no functions were attached). + */ + disable_function(sc, 8); + + return no_work_funcs; +} + + +static int +cardbussubmatch(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct cfdata *cf = match; + struct cardbus_attach_args *ca = aux; + + if (cf->cardbuscf_dev != CARDBUS_UNK_DEV && + cf->cardbuscf_dev != ca->ca_unit) { + return 0; + } + if (cf->cardbuscf_function != CARDBUS_UNK_FUNCTION && + cf->cardbuscf_function != ca->ca_function) { + return 0; + } + + return ((*cf->cf_attach->ca_match)(parent, cf, aux)); +} + + + +static int +cardbusprint(aux, pnp) + void *aux; + const char *pnp; +{ + struct cardbus_attach_args *ca = aux; + char devinfo[256]; + int i; + if (pnp) { + pci_devinfo(ca->ca_id, ca->ca_class, 1, devinfo); + for (i = 0; i < 4; i++) { + if (ca->ca_cis.cis1_info[i] == NULL) + break; + if (i) + printf(", "); + printf("%s", ca->ca_cis.cis1_info[i]); + } + if (i) + printf(" "); + printf("(manufacturer 0x%x, product 0x%x)", ca->ca_cis.manufacturer, + ca->ca_cis.product); + printf(" %s at %s", devinfo, pnp); + } + printf(" dev %d function %d", ca->ca_device, ca->ca_function); + + return UNCONF; +} + + + + + + +/* + * void cardbus_detach_card(struct cardbus_softc *sc) + * + * This function detaches the card on the slot: detach device data + * structure and turns off the power. + * + * This function must not be called under interrupt context. + */ +void +cardbus_detach_card(sc) + struct cardbus_softc *sc; +{ + struct cardbus_devfunc *ct, *ct_next, **prev_next; + + prev_next = &(sc->sc_funcs->ct_next); + + for (ct = sc->sc_funcs; ct != NULL; ct = ct_next) { + struct device *fndev = ct->ct_device; + ct_next = ct->ct_next; + + DPRINTF(("%s: detaching %s\n", sc->sc_dev.dv_xname, fndev->dv_xname)); + /* call device detach function */ + + if (0 != config_detach(fndev, 0)) { + printf("%s: cannot detaching dev %s, function %d\n", + sc->sc_dev.dv_xname, fndev->dv_xname, ct->ct_func); + prev_next = &(ct->ct_next); + } else { + sc->sc_poweron_func &= ~(1 << ct->ct_func); + *prev_next = ct->ct_next; + free(ct, M_DEVBUF); + } + } + + sc->sc_poweron_func = 0; + sc->sc_cf->cardbus_power(sc->sc_cc, CARDBUS_VCC_0V | CARDBUS_VPP_0V); +} + + + + +/* + * void *cardbus_intr_establish(cc, cf, irq, level, func, arg) + * Interrupt handler of pccard. + * args: + * cardbus_chipset_tag_t *cc + * int irq: + */ +void * +cardbus_intr_establish(cc, cf, irq, level, func, arg) + cardbus_chipset_tag_t cc; + cardbus_function_tag_t cf; + cardbus_intr_handle_t irq; + int level; + int (*func) __P((void *)); + void *arg; +{ + DPRINTF(("- cardbus_intr_establish: irq %d\n", irq)); + + return (*cf->cardbus_intr_establish)(cc, irq, level, func, arg); +} + + + +/* + * void cardbus_intr_disestablish(cc, cf, handler) + * Interrupt handler of pccard. + * args: + * cardbus_chipset_tag_t *cc + */ +void +cardbus_intr_disestablish(cc, cf, handler) + cardbus_chipset_tag_t cc; + cardbus_function_tag_t cf; + void *handler; +{ + DPRINTF(("- pccard_intr_disestablish\n")); + + (*cf->cardbus_intr_disestablish)(cc, handler); + return; +} + + + +/* XXX this should be merged with cardbus_function_{enable,disable}, + but we don't have a ct when these functions are called */ + +static void +enable_function(sc, cdstatus, function) + struct cardbus_softc *sc; + int cdstatus; + int function; +{ + + if (sc->sc_poweron_func == 0) { + /* switch to 3V and/or wait for power to stabilize */ + if (cdstatus & CARDBUS_3V_CARD) { + sc->sc_cf->cardbus_power(sc->sc_cc, CARDBUS_VCC_3V); + } else { + /* No cards other than 3.3V cards. */ + return; + } + (sc->sc_cf->cardbus_ctrl)(sc->sc_cc, CARDBUS_RESET); + } + sc->sc_poweron_func |= (1 << function); +} + +static void +disable_function(sc, function) + struct cardbus_softc *sc; + int function; +{ + + sc->sc_poweron_func &= ~(1 << function); + if (sc->sc_poweron_func == 0) { + /* power-off because no functions are enabled */ + sc->sc_cf->cardbus_power(sc->sc_cc, CARDBUS_VCC_0V); + } +} + +/* + * int cardbus_function_enable(struct cardbus_softc *sc, int func) + * + * This function enables a function on a card. When no power is + * applied on the card, power will be applied on it. + */ +int +cardbus_function_enable(sc, func) + struct cardbus_softc *sc; + int func; +{ + cardbus_chipset_tag_t cc = sc->sc_cc; + cardbus_function_tag_t cf = sc->sc_cf; + cardbusreg_t command; + cardbustag_t tag; + + DPRINTF(("entering cardbus_function_enable... ")); + + /* entering critical area */ + + enable_function(sc, CARDBUS_3V_CARD, func); /* XXX: sc_vold should be used */ + + /* exiting critical area */ + + tag = cardbus_make_tag(cc, cf, sc->sc_bus, sc->sc_device, func); + + command = cardbus_conf_read(cc, cf, tag, CARDBUS_COMMAND_STATUS_REG); + command |= (CARDBUS_COMMAND_MEM_ENABLE | CARDBUS_COMMAND_IO_ENABLE | CARDBUS_COMMAND_MASTER_ENABLE); /* XXX: good guess needed */ + + cardbus_conf_write(cc, cf, tag, CARDBUS_COMMAND_STATUS_REG, command); + + cardbus_free_tag(cc, cf, tag); + + DPRINTF(("%x\n", sc->sc_poweron_func)); + + return 0; +} + + +/* + * int cardbus_function_disable(struct cardbus_softc *, int func) + * + * This function disable a function on a card. When no functions are + * enabled, it turns off the power. + */ +int +cardbus_function_disable(sc, func) + struct cardbus_softc *sc; + int func; +{ + + DPRINTF(("entering cardbus_function_disable... ")); + + disable_function(sc, func); + + return 0; +} + + +/* + * int cardbus_get_capability(cardbus_chipset_tag_t cc, + * cardbus_function_tag_t cf, cardbustag_t tag, int capid, int *offset, + * cardbusreg_t *value) + * + * Find the specified PCI capability. + */ +int +cardbus_get_capability(cc, cf, tag, capid, offset, value) + cardbus_chipset_tag_t cc; + cardbus_function_tag_t cf; + cardbustag_t tag; + int capid; + int *offset; + cardbusreg_t *value; +{ + cardbusreg_t reg; + unsigned int ofs; + + reg = cardbus_conf_read(cc, cf, tag, PCI_COMMAND_STATUS_REG); + if (!(reg & PCI_STATUS_CAPLIST_SUPPORT)) + return (0); + + ofs = PCI_CAPLIST_PTR(cardbus_conf_read(cc, cf, tag, + PCI_CAPLISTPTR_REG)); + while (ofs != 0) { +#ifdef DIAGNOSTIC + if ((ofs & 3) || (ofs < 0x40)) + panic("cardbus_get_capability"); +#endif + reg = cardbus_conf_read(cc, cf, tag, ofs); + if (PCI_CAPLIST_CAP(reg) == capid) { + if (offset) + *offset = ofs; + if (value) + *value = reg; + return (1); + } + ofs = PCI_CAPLIST_NEXT(reg); + } + + return (0); +} + + +/* + * below this line, there are some functions for decoding tuples. + * They should go out from this file. + */ + +static u_int8_t * +decode_tuple __P((u_int8_t *tuple, tuple_decode_func func, void *data)); + +static int +decode_tuples(tuple, buflen, func, data) + u_int8_t *tuple; + int buflen; + tuple_decode_func func; + void *data; +{ + u_int8_t *tp = tuple; + + if (PCMCIA_CISTPL_LINKTARGET != *tuple) { + DPRINTF(("WRONG TUPLE: 0x%x\n", *tuple)); + return 0; + } + + while (NULL != (tp = decode_tuple(tp, func, data))) { + if (tuple + buflen < tp) { + break; + } + } + + return 1; +} + + +static u_int8_t * +decode_tuple(tuple, func, data) + u_int8_t *tuple; + tuple_decode_func func; + void *data; +{ + u_int8_t type; + u_int8_t len; + + type = tuple[0]; + len = tuple[1] + 2; + + (*func)(tuple, len, data); + + if (PCMCIA_CISTPL_END == type) { + return NULL; + } + + return tuple + len; +} + + +#ifdef CARDBUS_DEBUG +static char *tuple_name __P((int type)); + +static char * +tuple_name(type) + int type; +{ + static char *tuple_name_s [] = { + "TPL_NULL", "TPL_DEVICE", "Reserved", "Reserved", /* 0-3 */ + "CONFIG_CB", "CFTABLE_ENTRY_CB", "Reserved", "BAR", /* 4-7 */ + "Reserved", "Reserved", "Reserved", "Reserved", /* 8-B */ + "Reserved", "Reserved", "Reserved", "Reserved", /* C-F */ + "CHECKSUM", "LONGLINK_A", "LONGLINK_C", "LINKTARGET", /* 10-13 */ + "NO_LINK", "VERS_1", "ALTSTR", "DEVICE_A", + "JEDEC_C", "JEDEC_A", "CONFIG", "CFTABLE_ENTRY", + "DEVICE_OC", "DEVICE_OA", "DEVICE_GEO", "DEVICE_GEO_A", + "MANFID", "FUNCID", "FUNCE", "SWIL", /* 20-23 */ + "Reserved", "Reserved", "Reserved", "Reserved", /* 24-27 */ + "Reserved", "Reserved", "Reserved", "Reserved", /* 28-2B */ + "Reserved", "Reserved", "Reserved", "Reserved", /* 2C-2F */ + "Reserved", "Reserved", "Reserved", "Reserved", /* 30-33 */ + "Reserved", "Reserved", "Reserved", "Reserved", /* 34-37 */ + "Reserved", "Reserved", "Reserved", "Reserved", /* 38-3B */ + "Reserved", "Reserved", "Reserved", "Reserved", /* 3C-3F */ + "VERS_2", "FORMAT", "GEOMETRY", "BYTEORDER", + "DATE", "BATTERY", "ORG" + }; +#define NAME_LEN(x) (sizeof x / sizeof(x[0])) + + if (type > 0 && type < NAME_LEN(tuple_name_s)) { + return tuple_name_s[type]; + } else if (0xff == type) { + return "END"; + } else { + return "Reserved"; + } +} + +static void +print_tuple(tuple, len, data) + u_int8_t *tuple; + int len; + void *data; +{ + int i; + + printf("tuple: %s len %d\n", tuple_name(tuple[0]), len); + + for (i = 0; i < len; ++i) { + if (i % 16 == 0) { + printf(" 0x%2x:", i); + } + printf(" %x",tuple[i]); + if (i % 16 == 15) { + printf("\n"); + } + } + if (i % 16 != 0) { + printf("\n"); + } +} + +#endif diff --git a/sys/dev/cardbus/cardbus_exrom.c b/sys/dev/cardbus/cardbus_exrom.c new file mode 100644 index 00000000000..a33d60996fc --- /dev/null +++ b/sys/dev/cardbus/cardbus_exrom.c @@ -0,0 +1,183 @@ +/* $OpenBSD: cardbus_exrom.c,v 1.1 2000/04/08 05:50:52 aaron Exp $ */ +/* $NetBSD: cardbus_exrom.c,v 1.4 2000/02/03 06:47:31 thorpej Exp $ */ + +/* + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to + * The NetBSD Foundation by Johan Danielsson. + * + * 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. 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 + +#include + +#include + +#define READ_INT16(T, H, O) \ +(bus_space_read_1((T), (H), (O)) | (bus_space_read_1((T), (H), (O) + 1) << 8)) + +/* A PCI ROM is divided into a number of images. Each image has two + * data structures, a header located at the start of the image, and a + * `data structure' at some offset into it. + * + * The header is a 26 byte structure: + * + * Offset Length Description + * 0x00 1 signature byte 1 (0x55) + * 0x01 1 signature byte 2 (0xAA) + * 0x02 22 processor architecture data + * 0x18 2 pointer to the data structure + * + * The data structure is a 24 byte structure: + * + * Offset Length Description + * 0x00 4 signature (PCIR) + * 0x04 2 vendor id + * 0x06 2 device id + * 0x08 2 reserved + * 0x0A 2 data structure length + * 0x0C 1 data structure revision (0) + * 0x0D 3 class code + * 0x10 2 image length (in 512 byte blocks) + * 0x12 2 code revision level + * 0x14 1 code type + * 0x15 1 indicator (bit 7 indicates final image) + * 0x16 2 reserved + * + */ + +/* + * Scan through a PCI expansion ROM, and create subregions for each + * ROM image. This function assumes that the ROM is mapped at + * (tag,handle), and that the expansion ROM address decoder is + * enabled. The PCI specification requires that no other BAR should + * be accessed while the ROM is enabled, so interrupts should be + * disabled. + */ + +int +cardbus_read_exrom(romt, romh, head) + bus_space_tag_t romt; + bus_space_handle_t romh; + struct cardbus_rom_image_head *head; +{ + static const char func[] = "cardbus_read_exrom"; + + size_t addr = 0; /* offset of current rom image */ + size_t dataptr; + unsigned int rom_image = 0; + + SIMPLEQ_INIT(head); + do { + size_t image_size; + struct cardbus_rom_image *image; + u_int16_t val; + + val = READ_INT16(romt, romh, addr + CARDBUS_EXROM_SIGNATURE); + if(val != 0xaa55) { + printf("%s: bad header signature in ROM image %u: 0x%04x\n", + func, rom_image, val); + return 1; + } + dataptr = addr + READ_INT16(romt, romh, addr + CARDBUS_EXROM_DATA_PTR); + /* get the ROM image size, in blocks */ + image_size = READ_INT16(romt, romh, + dataptr + CARDBUS_EXROM_DATA_IMAGE_LENGTH); + if(image_size == 0) + /* XXX some ROMs seem to have this as zero, can we assume + this means 1 block? */ + image_size = 1; + image_size <<= 9; + image = malloc(sizeof(*image), M_DEVBUF, M_NOWAIT); + if(image == NULL) { + printf("%s: out of memory\n", func); + return 1; + } + image->rom_image = rom_image; + image->image_size = image_size; + image->romt = romt; + if(bus_space_subregion(romt, romh, addr, + image_size, &image->romh)) { + printf("%s: bus_space_subregion failed", func); + free(image, M_DEVBUF); + return 1; + } + SIMPLEQ_INSERT_TAIL(head, image, next); + addr += image_size; + rom_image++; + } while ((bus_space_read_1(romt, romh, + dataptr + CARDBUS_EXROM_DATA_INDICATOR) & 0x80) == 0); + return 0; +} + + +#if 0 +struct cardbus_exrom_data_structure { + char signature[4]; + cardbusreg_t id; /* vendor & device id */ + u_int16_t structure_length; + u_int8_t structure_revision; + cardbusreg_t class; /* class code in upper 24 bits */ + u_int16_t image_length; + u_int16_t data_revision; + u_int8_t code_type; + u_int8_t indicator; +}; + +pci_exrom_parse_data_structure(bus_space_tag_t tag, + bus_space_handle_t handle, + struct pci_exrom_data_structure *ds) +{ + unsigned char hdr[16]; + bus_space_read_region_1(tag, handle, dataptr, hdr, sizeof(hdr)); + memcpy(header->signature, hdr + PCI_EXROM_DATA_SIGNATURE, 4); +#define LEINT16(B, O) ((B)[(O)] | ((B)[(O) + 1] << 8)) + header->id = LEINT16(hdr, PCI_EXROM_DATA_VENDOR_ID) | + (LEINT16(hdr, PCI_EXROM_DATA_DEVICE_ID) << 16); + header->structure_length = LEINT16(hdr, PCI_EXROM_DATA_LENGTH); + header->structure_rev = hdr[PCI_EXROM_DATA_REV]; + header->class = (hdr[PCI_EXROM_DATA_CLASS_CODE] << 8) | + (hdr[PCI_EXROM_DATA_CLASS_CODE + 1] << 16) | + (hdr[PCI_EXROM_DATA_CLASS_CODE + 2] << 24); + header->image_length = LEINT16(hdr, PCI_EXROM_DATA_IMAGE_LENGTH) << 16; + header->data_revision = LEINT16(hdr, PCI_EXROM_DATA_DATA_REV); + header->code_type = hdr[PCI_EXROM_DATA_CODE_TYPE]; + header->indicator = hdr[PCI_EXROM_DATA_INDICATOR]; + length = min(length, header->image_length - 0x18 - offset); + bus_space_read_region_1(tag, handle, dataptr + 0x18 + offset, + buf, length); + ret = length; +} +#endif diff --git a/sys/dev/cardbus/cardbus_exrom.h b/sys/dev/cardbus/cardbus_exrom.h new file mode 100644 index 00000000000..1021c208101 --- /dev/null +++ b/sys/dev/cardbus/cardbus_exrom.h @@ -0,0 +1,73 @@ +/* $OpenBSD: cardbus_exrom.h,v 1.1 2000/04/08 05:50:52 aaron Exp $ */ +/* $NetBSD: cardbus_exrom.h,v 1.2 1999/12/15 12:28:54 kleink Exp $ */ + +/* + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to + * The NetBSD Foundation by Johan Danielsson. + * + * 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. 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. + */ + +#ifndef _DEV_CARDBUS_CARDBUS_EXROM_H_ +#define _DEV_CARDBUS_CARDBUS_EXROM_H_ + +/* PCI ROM header fields */ +#define CARDBUS_EXROM_SIGNATURE 0x00 +#define CARDBUS_EXROM_DATA_PTR 0x18 + +/* PCI ROM data structure fields */ +#define CARDBUS_EXROM_DATA_SIGNATURE 0x00 /* Signature ("PCIR") */ +#define CARDBUS_EXROM_DATA_VENDOR_ID 0x04 /* Vendor Identification */ +#define CARDBUS_EXROM_DATA_DEVICE_ID 0x06 /* Device Identification */ +#define CARDBUS_EXROM_DATA_LENGTH 0x0a /* PCI Data Structure Length */ +#define CARDBUS_EXROM_DATA_REV 0x0c /* PCI Data Structure Revision */ +#define CARDBUS_EXROM_DATA_CLASS_CODE 0x0d /* Class Code */ +#define CARDBUS_EXROM_DATA_IMAGE_LENGTH 0x10 /* Image Length */ +#define CARDBUS_EXROM_DATA_DATA_REV 0x12 /* Revision Level of Code/Data */ +#define CARDBUS_EXROM_DATA_CODE_TYPE 0x14 /* Code Type */ +#define CARDBUS_EXROM_DATA_INDICATOR 0x15 /* Indicator */ + + +struct cardbus_rom_image { + unsigned int rom_image; /* image number */ + size_t image_size; + bus_space_tag_t romt; + bus_space_handle_t romh; /* subregion */ + SIMPLEQ_ENTRY(cardbus_rom_image) next; +}; + +SIMPLEQ_HEAD(cardbus_rom_image_head, cardbus_rom_image); + +int +cardbus_read_exrom __P((bus_space_tag_t, bus_space_handle_t, + struct cardbus_rom_image_head*)); + +#endif /* !_DEV_CARDBUS_CARDBUS_EXROM_H_ */ diff --git a/sys/dev/cardbus/cardbus_map.c b/sys/dev/cardbus/cardbus_map.c new file mode 100644 index 00000000000..f9251b75a32 --- /dev/null +++ b/sys/dev/cardbus/cardbus_map.c @@ -0,0 +1,442 @@ +/* $OpenBSD: cardbus_map.c,v 1.1 2000/04/08 05:50:52 aaron Exp $ */ +/* $NetBSD: cardbus_map.c,v 1.10 2000/03/07 00:31:46 mycroft Exp $ */ + +/* + * Copyright (c) 1999 and 2000 + * HAYAKAWA Koichi. 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 HAYAKAWA Koichi. + * 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 /* XXX */ + +#if defined DEBUG && !defined CARDBUS_MAP_DEBUG +#define CARDBUS_MAP_DEBUG +#endif + +#if defined CARDBUS_MAP_DEBUG +#define STATIC +#define DPRINTF(a) printf a +#else +#define STATIC static +#define DPRINTF(a) +#endif + + +static int cardbus_io_find __P((cardbus_chipset_tag_t, cardbus_function_tag_t, + cardbustag_t, int, cardbusreg_t, + bus_addr_t *, bus_size_t *, int *)); +static int cardbus_mem_find __P((cardbus_chipset_tag_t, cardbus_function_tag_t, + cardbustag_t, int, cardbusreg_t, + bus_addr_t *, bus_size_t *, int *)); + +/* + * static int cardbus_io_find(cardbus_chipset_tag_t cc, + * cardbus_function_tag_t cf, cardbustag_t tag, + * int reg, cardbusreg_t type, bus_addr_t *basep, + * bus_size_t *sizep, int *flagsp) + * This code is stallen from sys/dev/pci_map.c. + */ +static int +cardbus_io_find(cc, cf, tag, reg, type, basep, sizep, flagsp) + cardbus_chipset_tag_t cc; + cardbus_function_tag_t cf; + cardbustag_t tag; + int reg; + cardbusreg_t type; + bus_addr_t *basep; + bus_size_t *sizep; + int *flagsp; +{ + cardbusreg_t address, mask; + int s; + + /* EXT ROM is able to map on memory space ONLY. */ + if (reg == CARDBUS_ROM_REG) { + return 1; + } + + if(reg < PCI_MAPREG_START || reg >= PCI_MAPREG_END || (reg & 3)) { + panic("cardbus_io_find: bad request"); + } + + /* + * Section 6.2.5.1, `Address Maps', tells us that: + * + * 1) The builtin software should have already mapped the device in a + * reasonable way. + * + * 2) A device which wants 2^n bytes of memory will hardwire the bottom + * n bits of the address to 0. As recommended, we write all 1s and see + * what we get back. + */ + s = splhigh(); + address = cardbus_conf_read(cc, cf, tag, reg); + cardbus_conf_write(cc, cf, tag, reg, 0xffffffff); + mask = cardbus_conf_read(cc, cf, tag, reg); + cardbus_conf_write(cc, cf, tag, reg, address); + splx(s); + + if (PCI_MAPREG_TYPE(address) != PCI_MAPREG_TYPE_IO) { + printf("cardbus_io_find: expected type i/o, found mem\n"); + return 1; + } + + if (PCI_MAPREG_IO_SIZE(mask) == 0) { + printf("cardbus_io_find: void region\n"); + return 1; + } + + if (basep != 0) { + *basep = PCI_MAPREG_IO_ADDR(address); + } + if (sizep != 0) { + *sizep = PCI_MAPREG_IO_SIZE(mask); + } + if (flagsp != 0) { + *flagsp = 0; + } + + return 0; +} + + + +/* + * static int cardbus_mem_find(cardbus_chipset_tag_t cc, + * cardbus_function_tag_t cf, cardbustag_t tag, + * int reg, cardbusreg_t type, bus_addr_t *basep, + * bus_size_t *sizep, int *flagsp) + * This code is stallen from sys/dev/pci_map.c. + */ +static int +cardbus_mem_find(cc, cf, tag, reg, type, basep, sizep, flagsp) + cardbus_chipset_tag_t cc; + cardbus_function_tag_t cf; + cardbustag_t tag; + int reg; + cardbusreg_t type; + bus_addr_t *basep; + bus_size_t *sizep; + int *flagsp; +{ + cardbusreg_t address, mask; + int s; + + if (reg != CARDBUS_ROM_REG && + (reg < PCI_MAPREG_START || reg >= PCI_MAPREG_END || (reg & 3))) { + panic("cardbus_mem_find: bad request"); + } + + /* + * Section 6.2.5.1, `Address Maps', tells us that: + * + * 1) The builtin software should have already mapped the device in a + * reasonable way. + * + * 2) A device which wants 2^n bytes of memory will hardwire the bottom + * n bits of the address to 0. As recommended, we write all 1s and see + * what we get back. + */ + s = splhigh(); + address = cardbus_conf_read(cc, cf, tag, reg); + cardbus_conf_write(cc, cf, tag, reg, 0xffffffff); + mask = cardbus_conf_read(cc, cf, tag, reg); + cardbus_conf_write(cc, cf, tag, reg, address); + splx(s); + + if (reg != CARDBUS_ROM_REG) { + /* memory space BAR */ + + if (PCI_MAPREG_TYPE(address) != PCI_MAPREG_TYPE_MEM) { + printf("cardbus_mem_find: expected type mem, found i/o\n"); + return 1; + } + if (PCI_MAPREG_MEM_TYPE(address) != PCI_MAPREG_MEM_TYPE(type)) { + printf("cardbus_mem_find: expected mem type %08x, found %08x\n", + PCI_MAPREG_MEM_TYPE(type), + PCI_MAPREG_MEM_TYPE(address)); + return 1; + } + } + + if (PCI_MAPREG_MEM_SIZE(mask) == 0) { + printf("cardbus_mem_find: void region\n"); + return 1; + } + + switch (PCI_MAPREG_MEM_TYPE(address)) { + case PCI_MAPREG_MEM_TYPE_32BIT: + case PCI_MAPREG_MEM_TYPE_32BIT_1M: + break; + case PCI_MAPREG_MEM_TYPE_64BIT: + printf("cardbus_mem_find: 64-bit memory mapping register\n"); + return 1; + default: + printf("cardbus_mem_find: reserved mapping register type\n"); + return 1; + } + + if (basep != 0) { + *basep = PCI_MAPREG_MEM_ADDR(address); + } + if (sizep != 0) { + *sizep = PCI_MAPREG_MEM_SIZE(mask); + } + if (flagsp != 0) { + *flagsp = PCI_MAPREG_MEM_CACHEABLE(address); + } + + return 0; +} + + + + +/* + * int cardbus_mapreg_map(struct cardbus_softc *, int, int, cardbusreg_t, + * int bus_space_tag_t *, bus_space_handle_t *, + * bus_addr_t *, bus_size_t *) + * This function maps bus-space on the value of Base Address + * Register (BAR) indexed by the argument `reg' (the second argument). + * When the value of the BAR is not valid, such as 0x00000000, a new + * address should be allocated for the BAR and new address values is + * written on the BAR. + */ +int +cardbus_mapreg_map(sc, func, reg, type, busflags, tagp, handlep, basep, sizep) + struct cardbus_softc *sc; + int func, reg, busflags; + cardbusreg_t type; + bus_space_tag_t *tagp; + bus_space_handle_t *handlep; + bus_addr_t *basep; + bus_size_t *sizep; +{ + cardbus_chipset_tag_t cc = sc->sc_cc; + cardbus_function_tag_t cf = sc->sc_cf; + bus_space_tag_t bustag; +#if rbus + rbus_tag_t rbustag; +#endif + bus_space_handle_t handle; + bus_addr_t base; + bus_size_t size; + int flags; + int status = 0; + + cardbustag_t tag = cardbus_make_tag(cc, cf, sc->sc_bus, sc->sc_device, func); + + DPRINTF(("cardbus_mapreg_map called: %s %x\n", sc->sc_dev.dv_xname, + type)); + + if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_IO) { + if (cardbus_io_find(cc, cf, tag, reg, type, &base, &size, &flags)) { + status = 1; + } + bustag = sc->sc_iot; +#if rbus + rbustag = sc->sc_rbus_iot; +#endif + } else { + if (cardbus_mem_find(cc, cf, tag, reg, type, &base, &size, &flags)){ + status = 1; + } + bustag = sc->sc_memt; +#if rbus + rbustag = sc->sc_rbus_memt; +#endif + } + if (status == 0) { +#if rbus + bus_addr_t mask = size - 1; + if (base != 0) { + mask = 0xffffffff; + } + if ((*cf->cardbus_space_alloc)(cc, rbustag, base, size, mask, + size, busflags | flags, &base, &handle)) { + panic("io alloc"); + } +#else + bus_addr_t start = 0x8300; + bus_addr_t end = 0x8400; + if (base != 0) { + bus_addr_t start = base; + bus_addr_t end = base + size; + } + if (bus_space_alloc(bustag, start, end, size, size, 0, 0, &base, &handle)) { + panic("io alloc"); + } +#endif + } + cardbus_conf_write(cc, cf, tag, reg, base); + + DPRINTF(("cardbus_mapreg_map: physaddr %lx\n", base)); + + if (tagp != 0) { + *tagp = bustag; + } + if (handlep != 0) { + *handlep = handle; + } + if (basep != 0) { + *basep = base; + } + if (sizep != 0) { + *sizep = size; + } + cardbus_free_tag(cc, cf, tag); + + return 0; +} + + + + + +/* + * int cardbus_mapreg_unmap(struct cardbus_softc *sc, int func, int reg, + * bus_space_tag_t tag, bus_space_handle_t handle, + * bus_size_t size) + * + * This function releases bus-space region and close memory or io + * window on the bridge. + * + * Arguments: + * struct cardbus_softc *sc; the pointer to the device structure of cardbus. + * int func; the number of function on the device. + * int reg; the offset of BAR register. + */ +int +cardbus_mapreg_unmap(sc, func, reg, tag, handle, size) + struct cardbus_softc *sc; + int func, reg; + bus_space_tag_t tag; + bus_space_handle_t handle; + bus_size_t size; +{ + cardbus_chipset_tag_t cc = sc->sc_cc; + cardbus_function_tag_t cf = sc->sc_cf; + int st = 1; + cardbustag_t cardbustag; +#if rbus + rbus_tag_t rbustag; + + if (sc->sc_iot == tag) { + /* bus space is io space */ + DPRINTF(("%s: unmap i/o space\n", sc->sc_dev.dv_xname)); + rbustag = sc->sc_rbus_iot; + } else if (sc->sc_memt == tag) { + /* bus space is memory space */ + DPRINTF(("%s: unmap mem space\n", sc->sc_dev.dv_xname)); + rbustag = sc->sc_rbus_memt; + } else { + return 1; + } +#endif + + cardbustag = cardbus_make_tag(cc, cf, sc->sc_bus, sc->sc_device, func); + + cardbus_conf_write(cc, cf, cardbustag, reg, 0); + +#if rbus + (*cf->cardbus_space_free)(cc, rbustag, handle, size); +#endif + + cardbus_free_tag(cc, cf, cardbustag); + + return st; +} + + + + + +/* + * int cardbus_save_bar(cardbus_devfunc_t); + * + * This function saves the Base Address Registers at the CardBus + * function denoted by the argument. + */ +int cardbus_save_bar(ct) + cardbus_devfunc_t ct; +{ + cardbustag_t tag = Cardbus_make_tag(ct); + cardbus_chipset_tag_t cc = ct->ct_cc; + cardbus_function_tag_t cf = ct->ct_cf; + + ct->ct_bar[0] = cardbus_conf_read(cc, cf, tag, CARDBUS_BASE0_REG); + ct->ct_bar[1] = cardbus_conf_read(cc, cf, tag, CARDBUS_BASE1_REG); + ct->ct_bar[2] = cardbus_conf_read(cc, cf, tag, CARDBUS_BASE2_REG); + ct->ct_bar[3] = cardbus_conf_read(cc, cf, tag, CARDBUS_BASE3_REG); + ct->ct_bar[4] = cardbus_conf_read(cc, cf, tag, CARDBUS_BASE4_REG); + ct->ct_bar[5] = cardbus_conf_read(cc, cf, tag, CARDBUS_BASE5_REG); + + DPRINTF(("cardbus_save_bar: %x %x\n", ct->ct_bar[0], ct->ct_bar[1])); + + Cardbus_free_tag(ct, tag); + + return 0; +} + + + +/* + * int cardbus_restore_bar(cardbus_devfunc_t); + * + * This function saves the Base Address Registers at the CardBus + * function denoted by the argument. + */ +int cardbus_restore_bar(ct) + cardbus_devfunc_t ct; +{ + cardbustag_t tag = Cardbus_make_tag(ct); + cardbus_chipset_tag_t cc = ct->ct_cc; + cardbus_function_tag_t cf = ct->ct_cf; + + cardbus_conf_write(cc, cf, tag, CARDBUS_BASE0_REG, ct->ct_bar[0]); + cardbus_conf_write(cc, cf, tag, CARDBUS_BASE1_REG, ct->ct_bar[1]); + cardbus_conf_write(cc, cf, tag, CARDBUS_BASE2_REG, ct->ct_bar[2]); + cardbus_conf_write(cc, cf, tag, CARDBUS_BASE3_REG, ct->ct_bar[3]); + cardbus_conf_write(cc, cf, tag, CARDBUS_BASE4_REG, ct->ct_bar[4]); + cardbus_conf_write(cc, cf, tag, CARDBUS_BASE5_REG, ct->ct_bar[5]); + + Cardbus_free_tag(ct, tag); + + return 0; +} diff --git a/sys/dev/cardbus/cardbusvar.h b/sys/dev/cardbus/cardbusvar.h new file mode 100644 index 00000000000..abd8e2a2d00 --- /dev/null +++ b/sys/dev/cardbus/cardbusvar.h @@ -0,0 +1,450 @@ +/* $OpenBSD */ +/* $NetBSD: cardbusvar.h,v 1.17 2000/04/02 19:11:37 mycroft Exp $ */ + +/* + * Copyright (c) 1998, 1999 and 2000 + * HAYAKAWA Koichi. 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 the author. + * 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. + */ + +#ifndef _DEV_CARDBUS_CARDBUSVAR_H_ +#define _DEV_CARDBUS_CARDBUSVAR_H_ + +#include /* for pcitag_t */ + +#if 1 +#include +#endif + + + +typedef void *cardbus_chipset_tag_t; +typedef int cardbus_intr_handle_t; + + +/* XXX they must be in cardbusreg.h */ +typedef u_int32_t cardbusreg_t; +typedef pcitag_t cardbustag_t; +typedef int cardbus_intr_line_t; + +#define CARDBUS_ID_REG 0x00 + +typedef u_int16_t cardbus_vendor_id_t; +typedef u_int16_t cardbus_product_id_t; + +# define CARDBUS_VENDOR_SHIFT 0 +# define CARDBUS_VENDOR_MASK 0xffff +# define CARDBUS_VENDOR(id) \ + (((id) >> CARDBUS_VENDOR_SHIFT) & CARDBUS_VENDOR_MASK) + +# define CARDBUS_PRODUCT_SHIFT 16 +# define CARDBUS_PRODUCT_MASK 0xffff +# define CARDBUS_PRODUCT(id) \ + (((id) >> CARDBUS_PRODUCT_SHIFT) & CARDBUS_PRODUCT_MASK) + + +#define CARDBUS_COMMAND_STATUS_REG 0x04 + +# define CARDBUS_COMMAND_IO_ENABLE 0x00000001 +# define CARDBUS_COMMAND_MEM_ENABLE 0x00000002 +# define CARDBUS_COMMAND_MASTER_ENABLE 0x00000004 + + +#define CARDBUS_CLASS_REG 0x08 + +#define CARDBUS_CLASS_SHIFT 24 +#define CARDBUS_CLASS_MASK 0xff +#define CARDBUS_CLASS(cr) \ + (((cr) >> CARDBUS_CLASS_SHIFT) & CARDBUS_CLASS_MASK) + +#define CARDBUS_SUBCLASS_SHIFT 16 +#define CARDBUS_SUBCLASS_MASK 0xff +#define CARDBUS_SUBCLASS(cr) \ + (((cr) >> CARDBUS_SUBCLASS_SHIFT) & CARDBUS_SUBCLASS_MASK) + +#define CARDBUS_INTERFACE_SHIFT 8 +#define CARDBUS_INTERFACE_MASK 0xff +#define CARDBUS_INTERFACE(cr) \ + (((cr) >> CARDBUS_INTERFACE_SHIFT) & CARDBUS_INTERFACE_MASK) + +#define CARDBUS_REVISION_SHIFT 0 +#define CARDBUS_REVISION_MASK 0xff +#define CARDBUS_REVISION(cr) \ + (((cr) >> CARDBUS_REVISION_SHIFT) & CARDBUS_REVISION_MASK) + +/* base classes */ +#define CARDBUS_CLASS_PREHISTORIC 0x00 +#define CARDBUS_CLASS_MASS_STORAGE 0x01 +#define CARDBUS_CLASS_NETWORK 0x02 +#define CARDBUS_CLASS_DISPLAY 0x03 +#define CARDBUS_CLASS_MULTIMEDIA 0x04 +#define CARDBUS_CLASS_MEMORY 0x05 +#define CARDBUS_CLASS_BRIDGE 0x06 +#define CARDBUS_CLASS_COMMUNICATIONS 0x07 +#define CARDBUS_CLASS_SYSTEM 0x08 +#define CARDBUS_CLASS_INPUT 0x09 +#define CARDBUS_CLASS_DOCK 0x0a +#define CARDBUS_CLASS_PROCESSOR 0x0b +#define CARDBUS_CLASS_SERIALBUS 0x0c +#define CARDBUS_CLASS_UNDEFINED 0xff + +/* 0x0c serial bus subclasses */ +#define CARDBUS_SUBCLASS_SERIALBUS_FIREWIRE 0x00 +#define CARDBUS_SUBCLASS_SERIALBUS_ACCESS 0x01 +#define CARDBUS_SUBCLASS_SERIALBUS_SSA 0x02 +#define CARDBUS_SUBCLASS_SERIALBUS_USB 0x03 +#define CARDBUS_SUBCLASS_SERIALBUS_FIBER 0x04 + +/* BIST, Header Type, Latency Timer, Cache Line Size */ +#define CARDBUS_BHLC_REG 0x0c + +#define CARDBUS_BIST_SHIFT 24 +#define CARDBUS_BIST_MASK 0xff +#define CARDBUS_BIST(bhlcr) \ + (((bhlcr) >> CARDBUS_BIST_SHIFT) & CARDBUS_BIST_MASK) + +#define CARDBUS_HDRTYPE_SHIFT 16 +#define CARDBUS_HDRTYPE_MASK 0xff +#define CARDBUS_HDRTYPE(bhlcr) \ + (((bhlcr) >> CARDBUS_HDRTYPE_SHIFT) & CARDBUS_HDRTYPE_MASK) + +#define CARDBUS_HDRTYPE_TYPE(bhlcr) \ + (CARDBUS_HDRTYPE(bhlcr) & 0x7f) +#define CARDBUS_HDRTYPE_MULTIFN(bhlcr) \ + ((CARDBUS_HDRTYPE(bhlcr) & 0x80) != 0) + +#define CARDBUS_LATTIMER_SHIFT 8 +#define CARDBUS_LATTIMER_MASK 0xff +#define CARDBUS_LATTIMER(bhlcr) \ + (((bhlcr) >> CARDBUS_LATTIMER_SHIFT) & CARDBUS_LATTIMER_MASK) + +#define CARDBUS_CACHELINE_SHIFT 0 +#define CARDBUS_CACHELINE_MASK 0xff +#define CARDBUS_CACHELINE(bhlcr) \ + (((bhlcr) >> CARDBUS_CACHELINE_SHIFT) & CARDBUS_CACHELINE_MASK) + + +/* Base Resisters */ +#define CARDBUS_BASE0_REG 0x10 +#define CARDBUS_BASE1_REG 0x14 +#define CARDBUS_BASE2_REG 0x18 +#define CARDBUS_BASE3_REG 0x1C +#define CARDBUS_BASE4_REG 0x20 +#define CARDBUS_BASE5_REG 0x24 +#define CARDBUS_CIS_REG 0x28 +#define CARDBUS_ROM_REG 0x30 +# define CARDBUS_CIS_ASIMASK 0x07 +# define CARDBUS_CIS_ASI(x) (CARDBUS_CIS_ASIMASK & (x)) +# define CARDBUS_CIS_ASI_TUPLE 0x00 +# define CARDBUS_CIS_ASI_BAR0 0x01 +# define CARDBUS_CIS_ASI_BAR1 0x02 +# define CARDBUS_CIS_ASI_BAR2 0x03 +# define CARDBUS_CIS_ASI_BAR3 0x04 +# define CARDBUS_CIS_ASI_BAR4 0x05 +# define CARDBUS_CIS_ASI_BAR5 0x06 +# define CARDBUS_CIS_ASI_ROM 0x07 +# define CARDBUS_CIS_ADDRMASK 0x0ffffff8 +# define CARDBUS_CIS_ADDR(x) (CARDBUS_CIS_ADDRMASK & (x)) +# define CARDBUS_CIS_ASI_BAR(x) (((CARDBUS_CIS_ASIMASK & (x))-1)*4+0x10) +# define CARDBUS_CIS_ASI_ROM_IMAGE(x) (((x) >> 28) & 0xf) + +#define CARDBUS_INTERRUPT_REG 0x3c + +#define CARDBUS_MAPREG_TYPE_MEM 0x00000000 +#define CARDBUS_MAPREG_TYPE_IO 0x00000001 + +/* XXX end */ + +#if rbus + +typedef struct cardbus_functions { + int (*cardbus_space_alloc) __P((cardbus_chipset_tag_t, rbus_tag_t, + bus_addr_t addr, bus_size_t size, + bus_addr_t mask, bus_size_t align, + int flags, bus_addr_t *addrp, + bus_space_handle_t *bshp)); + int (*cardbus_space_free) __P((cardbus_chipset_tag_t, rbus_tag_t, + bus_space_handle_t, bus_size_t)); + void *(*cardbus_intr_establish) __P((cardbus_chipset_tag_t, int irq, int level, int (*ih)(void *), void *sc)); + void (*cardbus_intr_disestablish) __P((cardbus_chipset_tag_t ct, void *ih)); + int (*cardbus_ctrl) __P((cardbus_chipset_tag_t, int)); + int (*cardbus_power) __P((cardbus_chipset_tag_t, int)); + + cardbustag_t (*cardbus_make_tag) __P((cardbus_chipset_tag_t, int, int, int)); + void (*cardbus_free_tag) __P((cardbus_chipset_tag_t, cardbustag_t)); + cardbusreg_t (*cardbus_conf_read) __P((cardbus_chipset_tag_t, cardbustag_t, int)); + void (*cardbus_conf_write) __P((cardbus_chipset_tag_t, cardbustag_t, int, cardbusreg_t)); +} cardbus_function_t, *cardbus_function_tag_t; + +#else + +typedef struct cardbus_functions { + int (*cardbus_ctrl) __P((cardbus_chipset_tag_t, int)); + int (*cardbus_power) __P((cardbus_chipset_tag_t, int)); + int (*cardbus_mem_open) __P((cardbus_chipset_tag_t, int, u_int32_t, u_int32_t)); + int (*cardbus_mem_close) __P((cardbus_chipset_tag_t, int)); + int (*cardbus_io_open) __P((cardbus_chipset_tag_t, int, u_int32_t, u_int32_t)); + int (*cardbus_io_close) __P((cardbus_chipset_tag_t, int)); + void *(*cardbus_intr_establish) __P((cardbus_chipset_tag_t, int irq, int level, int (*ih)(void *), void *sc)); + void (*cardbus_intr_disestablish) __P((cardbus_chipset_tag_t ct, void *ih)); + + cardbustag_t (*cardbus_make_tag) __P((cardbus_chipset_tag_t, int, int, int)); cardbusreg_t (*cardbus_conf_read) __P((cardbus_chipset_tag_t, cardbustag_t, int)); + void (*cardbus_conf_write) __P((cardbus_chipset_tag_t, cardbustag_t, int, cardbusreg_t)); +} cardbus_function_t, *cardbus_function_tag_t; +#endif /* rbus */ + +/* + * struct cbslot_attach_args is the attach argument for cardbus card. + */ +struct cbslot_attach_args { + char *cba_busname; + bus_space_tag_t cba_iot; /* cardbus i/o space tag */ + bus_space_tag_t cba_memt; /* cardbus mem space tag */ + bus_dma_tag_t cba_dmat; /* DMA tag */ + + int cba_bus; /* cardbus bus number */ + + cardbus_chipset_tag_t cba_cc; /* cardbus chipset */ + cardbus_function_tag_t cba_cf; /* cardbus functions */ + int cba_intrline; /* interrupt line */ + +#if rbus + rbus_tag_t cba_rbus_iot; /* CardBus i/o rbus tag */ + rbus_tag_t cba_rbus_memt; /* CardBus mem rbus tag */ +#endif + + int cba_cacheline; /* cache line size */ + int cba_lattimer; /* latency timer */ +}; + + +#define cbslotcf_dev cf_loc[0] +#define cbslotcf_func cf_loc[1] +#define CBSLOT_UNK_DEV -1 +#define CBSLOT_UNK_FUNC -1 + + +struct cardbus_devfunc; + +/* + * struct cardbus_softc is the softc for cardbus card. + */ +struct cardbus_softc { + struct device sc_dev; /* fundamental device structure */ + + int sc_bus; /* cardbus bus number */ + int sc_device; /* cardbus device number */ + int sc_intrline; /* CardBus intrline */ + + bus_space_tag_t sc_iot; /* CardBus I/O space tag */ + bus_space_tag_t sc_memt; /* CardBus MEM space tag */ + bus_dma_tag_t sc_dmat; /* DMA tag */ + + cardbus_chipset_tag_t sc_cc; /* CardBus chipset */ + cardbus_function_tag_t sc_cf; /* CardBus function */ + +#if rbus + rbus_tag_t sc_rbus_iot; /* CardBus i/o rbus tag */ + rbus_tag_t sc_rbus_memt; /* CardBus mem rbus tag */ +#endif + + int sc_cacheline; /* cache line size */ + int sc_lattimer; /* latency timer */ + int sc_volt; /* applied Vcc voltage */ +#define PCCARD_33V 0x02 +#define PCCARD_XXV 0x04 +#define PCCARD_YYV 0x08 + int sc_poweron_func; + struct cardbus_devfunc *sc_funcs; /* list of cardbus device functions */ +}; + + +/* + * struct cardbus_devfunc: + * + * This is the data deposit for each function of a CardBus device. + * This structure is used for memory or i/o space allocation and + * disallocation. + */ +typedef struct cardbus_devfunc { + cardbus_chipset_tag_t ct_cc; + cardbus_function_tag_t ct_cf; + struct cardbus_softc *ct_sc; /* pointer to the parent */ + int ct_bus; /* bus number */ + int ct_dev; /* device number */ + int ct_func; /* function number */ + +#if rbus + rbus_tag_t ct_rbus_iot; /* CardBus i/o rbus tag */ + rbus_tag_t ct_rbus_memt; /* CardBus mem rbus tag */ +#endif + + u_int32_t ct_bar[6]; /* Base Address Regs 0 to 6 */ + u_int32_t ct_lc; /* Latency timer and cache line size */ + /* u_int32_t ct_cisreg; */ /* CIS reg: is it needed??? */ + + struct device *ct_device; /* pointer to the device */ + + struct cardbus_devfunc *ct_next; + + /* some data structure needed for tuple??? */ +} *cardbus_devfunc_t; + + +/* XXX various things extracted from CIS */ +struct cardbus_cis_info { + int32_t manufacturer; + int32_t product; + char cis1_info_buf[256]; + char* cis1_info[4]; + struct cb_bar_info { + unsigned int flags; + unsigned int size; + } bar[7]; + unsigned int funcid; + union { + struct { + char netid[6]; + char netid_present; + char __filler; + } network; + } funce; +}; + +struct cardbus_attach_args { + int ca_unit; + cardbus_devfunc_t ca_ct; + + bus_space_tag_t ca_iot; /* CardBus I/O space tag */ + bus_space_tag_t ca_memt; /* CardBus MEM space tag */ + bus_dma_tag_t ca_dmat; /* DMA tag */ + + u_int ca_device; + u_int ca_function; + cardbustag_t ca_tag; + cardbusreg_t ca_id; + cardbusreg_t ca_class; + + /* interrupt information */ + cardbus_intr_line_t ca_intrline; + +#if rbus + rbus_tag_t ca_rbus_iot; /* CardBus i/o rbus tag */ + rbus_tag_t ca_rbus_memt; /* CardBus mem rbus tag */ +#endif + + struct cardbus_cis_info ca_cis; +}; + + +#define CARDBUS_ENABLE 1 /* enable the channel */ +#define CARDBUS_DISABLE 2 /* disable the channel */ +#define CARDBUS_RESET 4 +#define CARDBUS_CD 7 +# define CARDBUS_NOCARD 0 +# define CARDBUS_5V_CARD 0x01 /* XXX: It must not exist */ +# define CARDBUS_3V_CARD 0x02 +# define CARDBUS_XV_CARD 0x04 +# define CARDBUS_YV_CARD 0x08 +#define CARDBUS_IO_ENABLE 100 +#define CARDBUS_IO_DISABLE 101 +#define CARDBUS_MEM_ENABLE 102 +#define CARDBUS_MEM_DISABLE 103 +#define CARDBUS_BM_ENABLE 104 /* bus master */ +#define CARDBUS_BM_DISABLE 105 + +#define CARDBUS_VCC_UC 0x0000 +#define CARDBUS_VCC_3V 0x0001 +#define CARDBUS_VCC_XV 0x0002 +#define CARDBUS_VCC_YV 0x0003 +#define CARDBUS_VCC_0V 0x0004 +#define CARDBUS_VCC_5V 0x0005 /* ??? */ +#define CARDBUS_VCCMASK 0x000f +#define CARDBUS_VPP_UC 0x0000 +#define CARDBUS_VPP_VCC 0x0010 +#define CARDBUS_VPP_12V 0x0030 +#define CARDBUS_VPP_0V 0x0040 +#define CARDBUS_VPPMASK 0x00f0 + +#define CARDBUSCF_DEV 0 +#define CARDBUSCF_DEV_DEFAULT -1 +#define CARDBUSCF_FUNCTION 1 +#define CARDBUSCF_FUNCTION_DEFAULT -1 + +/* + * Locators devies that attach to 'cardbus', as specified to config. + */ +#define cardbuscf_dev cf_loc[CARDBUSCF_DEV] +#define CARDBUS_UNK_DEV CARDBUSCF_DEV_DEFAULT + +#define cardbuscf_function cf_loc[CARDBUSCF_FUNCTION] +#define CARDBUS_UNK_FUNCTION CARDBUSCF_FUNCTION_DEFAULT + +int cardbus_attach_card __P((struct cardbus_softc *)); +void cardbus_detach_card __P((struct cardbus_softc *)); +void *cardbus_intr_establish __P((cardbus_chipset_tag_t, cardbus_function_tag_t, cardbus_intr_handle_t irq, int level, int (*func) (void *), void *arg)); +void cardbus_intr_disestablish __P((cardbus_chipset_tag_t, cardbus_function_tag_t, void *handler)); + +int cardbus_mapreg_map __P((struct cardbus_softc *, int, int, cardbusreg_t, + int, bus_space_tag_t *, bus_space_handle_t *, bus_addr_t *, bus_size_t *)); +int cardbus_mapreg_unmap __P((struct cardbus_softc *, int, int, + bus_space_tag_t, bus_space_handle_t, bus_size_t)); + +int cardbus_save_bar __P((cardbus_devfunc_t)); +int cardbus_restore_bar __P((cardbus_devfunc_t)); + +int cardbus_function_enable __P((struct cardbus_softc *, int function)); +int cardbus_function_disable __P((struct cardbus_softc *, int function)); + +int cardbus_get_capability __P((cardbus_chipset_tag_t, cardbus_function_tag_t, + cardbustag_t, int, int *, cardbusreg_t *)); + +#define Cardbus_function_enable(ct) cardbus_function_enable((ct)->ct_sc, (ct)->ct_func) +#define Cardbus_function_disable(ct) cardbus_function_disable((ct)->ct_sc, (ct)->ct_func) + + + +#define Cardbus_mapreg_map(ct, reg, type, busflags, tagp, handlep, basep, sizep) \ + cardbus_mapreg_map((ct)->ct_sc, (ct->ct_func), (reg), (type),\ + (busflags), (tagp), (handlep), (basep), (sizep)) +#define Cardbus_mapreg_unmap(ct, reg, tag, handle, size) \ + cardbus_mapreg_unmap((ct)->ct_sc, (ct->ct_func), (reg), (tag), (handle), (size)) + +#define Cardbus_make_tag(ct) (*(ct)->ct_cf->cardbus_make_tag)((ct)->ct_cc, (ct)->ct_bus, (ct)->ct_dev, (ct)->ct_func) +#define cardbus_make_tag(cc, cf, bus, device, function) ((cf)->cardbus_make_tag)((cc), (bus), (device), (function)) + +#define Cardbus_free_tag(ct, tag) (*(ct)->ct_cf->cardbus_free_tag)((ct)->ct_cc, (tag)) +#define cardbus_free_tag(cc, cf, tag) (*(cf)->cardbus_free_tag)(cc, (tag)) + +#define Cardbus_conf_read(ct, tag, offs) (*(ct)->ct_cf->cardbus_conf_read)((ct)->ct_cf, (tag), (offs)) +#define cardbus_conf_read(cc, cf, tag, offs) ((cf)->cardbus_conf_read)((cc), (tag), (offs)) +#define Cardbus_conf_write(ct, tag, offs, val) (*(ct)->ct_cf->cardbus_conf_write)((ct)->ct_cf, (tag), (offs), (val)) +#define cardbus_conf_write(cc, cf, tag, offs, val) ((cf)->cardbus_conf_write)((cc), (tag), (offs), (val)) + +#endif /* !_DEV_CARDBUS_CARDBUSVAR_H_ */ diff --git a/sys/dev/cardbus/cardslot.c b/sys/dev/cardbus/cardslot.c new file mode 100644 index 00000000000..1bed01f97b1 --- /dev/null +++ b/sys/dev/cardbus/cardslot.c @@ -0,0 +1,439 @@ +/* $OpenBSD: cardslot.c,v 1.1 2000/04/08 05:50:52 aaron Exp $ */ +/* $NetBSD: cardslot.c,v 1.9 2000/03/22 09:35:06 haya Exp $ */ + +/* + * Copyright (c) 1999 and 2000 + * HAYAKAWA Koichi. 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 HAYAKAWA Koichi. + * 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 + +#if defined CARDSLOT_DEBUG +#define STATIC +#define DPRINTF(a) printf a +#else +#define STATIC static +#define DPRINTF(a) +#endif + + + +STATIC void cardslotattach __P((struct device *, struct device *, void *)); + +STATIC int cardslotmatch __P((struct device *, void *, void *)); +static void create_slot_manager __P((void *)); +static void cardslot_event_thread __P((void *arg)); + +STATIC int cardslot_cb_print __P((void *aux, const char *pcic)); +static int cardslot_16_print __P((void *, const char *)); +static int cardslot_16_submatch __P((struct device *, void *,void *)); + +struct cfattach cardslot_ca = { + sizeof(struct cardslot_softc), cardslotmatch, cardslotattach +}; + +#ifndef __NetBSD_Version__ +struct cfdriver cardslot_cd = { + NULL, "cardslot", DV_DULL +}; +#endif + + +STATIC int +cardslotmatch(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct cardslot_attach_args *caa = aux; + + if (caa->caa_cb_attach == NULL && caa->caa_16_attach == NULL) { + /* Neither CardBus nor 16-bit PCMCIA are defined. */ + return 0; + } + + return 1; +} + + + +STATIC void +cardslotattach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct cardslot_softc *sc = (struct cardslot_softc *)self; + struct cardslot_attach_args *caa = aux; + + struct cbslot_attach_args *cba = caa->caa_cb_attach; + struct pcmciabus_attach_args *pa = caa->caa_16_attach; + + struct cardbus_softc *csc; + struct pcmcia_softc *psc; + + int card_attach_now; + + sc->sc_slot = sc->sc_dev.dv_unit; + sc->sc_cb_softc = NULL; + sc->sc_16_softc = NULL; + SIMPLEQ_INIT(&sc->sc_events); + sc->sc_th_enable = 0; + + printf(" slot %d flags %x\n", sc->sc_slot, sc->sc_dev.dv_cfdata->cf_flags); + + DPRINTF(("%s attaching CardBus bus...\n", sc->sc_dev.dv_xname)); + if (cba != NULL) { + if (NULL != (csc = (void *)config_found(self, cba, cardslot_cb_print))) { + /* cardbus found */ + DPRINTF(("cardslotattach: found cardbus on %s\n", sc->sc_dev.dv_xname)); + sc->sc_cb_softc = csc; + } + } + + if (pa != NULL) { + if (NULL != (psc = (void *)config_found_sm(self, pa, cardslot_16_print, + cardslot_16_submatch))) { + /* pcmcia 16-bit bus found */ + DPRINTF(("cardslotattach: found 16-bit pcmcia bus\n")); + sc->sc_16_softc = psc; + /* XXX: dirty. This code should be removed to achieve MI */ + caa->caa_ph->pcmcia = (struct device *)psc; + } + } + + if (csc != NULL || psc != NULL) + kthread_create_deferred(create_slot_manager, (void *)sc); + + card_attach_now = sc->sc_dev.dv_cfdata->cf_flags & 0x01; + + if (csc && (csc->sc_cf->cardbus_ctrl)(csc->sc_cc, CARDBUS_CD)) { + DPRINTF(("cardslotattach: CardBus card found\n")); + if (card_attach_now) { + if (cardbus_attach_card(sc->sc_cb_softc) > 0) { + /* at least one function works */ + CARDSLOT_SET_WORK(sc->sc_status, CARDSLOT_STATUS_WORKING); + } else { + /* no functions work or this card is not known */ + CARDSLOT_SET_WORK(sc->sc_status, CARDSLOT_STATUS_NOTWORK); + } + CARDSLOT_SET_CARDTYPE(sc->sc_status, CARDSLOT_STATUS_CARD_CB); + } else { + /* attach deffered */ + cardslot_event_throw(sc, CARDSLOT_EVENT_INSERTION_CB); + } + } + + if (psc && (psc->pct->card_detect)(psc->pch)) { + DPRINTF(("cardbusattach: 16-bit card found\n")); + if (card_attach_now) { + /* attach now */ + pcmcia_card_attach((struct device *)sc->sc_16_softc); + CARDSLOT_SET_CARDTYPE(sc->sc_status, CARDSLOT_STATUS_CARD_16); + } else { + /* attach deffered */ + cardslot_event_throw(sc, CARDSLOT_EVENT_INSERTION_16); + } + } +} + + + +STATIC int +cardslot_cb_print(aux, pnp) + void *aux; + const char *pnp; +{ + struct cbslot_attach_args *cba = aux; + + if (pnp) { + printf("cardbus at %s subordinate bus %d", pnp, cba->cba_bus); + } + + return UNCONF; +} + + +static int +cardslot_16_submatch(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct cfdata *cf = match; + + if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != 0) + return 0; + + if (cf->cf_loc[0] == -1) { + return ((*cf->cf_attach->ca_match)(parent, cf, aux)); + } + + return 0; +} + + + +static int +cardslot_16_print(arg, pnp) + void *arg; + const char *pnp; +{ + + if (pnp) { + printf("pcmciabus at %s", pnp); + } + + return UNCONF; +} + + + + +static void +create_slot_manager(arg) + void *arg; +{ + struct cardslot_softc *sc = (struct cardslot_softc *)arg; + + sc->sc_th_enable = 1; + + if (kthread_create(cardslot_event_thread, sc, &sc->sc_event_thread, "%s", + sc->sc_dev.dv_xname)) { + printf("%s: unable to create event thread for slot %d\n", + sc->sc_dev.dv_xname, sc->sc_slot); + panic("create_slot_manager"); + } +} + + + + +/* + * void cardslot_event_throw(struct cardslot_softc *sc, int ev) + * + * This function throws an event to the event handler. If the state + * of a slot is changed, it should be noticed using this function. + */ +void +cardslot_event_throw(sc, ev) + struct cardslot_softc *sc; + int ev; +{ + struct cardslot_event *ce; + + DPRINTF(("cardslot_event_throw: an event %s comes\n", + ev == CARDSLOT_EVENT_INSERTION_CB ? "CardBus Card inserted" : + ev == CARDSLOT_EVENT_INSERTION_16 ? "16-bit Card inserted" : + ev == CARDSLOT_EVENT_REMOVAL_CB ? "CardBus Card removed" : + ev == CARDSLOT_EVENT_REMOVAL_16 ? "16-bit Card removed" : "???")); + + if (NULL == (ce = (struct cardslot_event *)malloc(sizeof (struct cardslot_event), M_TEMP, M_NOWAIT))) { + panic("cardslot_enevt"); + } + + ce->ce_type = ev; + + { + int s = spltty(); + SIMPLEQ_INSERT_TAIL(&sc->sc_events, ce, ce_q); + splx(s); + } + + wakeup(&sc->sc_events); + + return; +} + + +/* + * static void cardslot_event_thread(void *arg) + * + * This function is the main routine handing cardslot events such as + * insertions and removals. + * + */ +static void +cardslot_event_thread(arg) + void *arg; +{ + struct cardslot_softc *sc = arg; + struct cardslot_event *ce; + int s; + static int antonym_ev[4] = { + CARDSLOT_EVENT_REMOVAL_16, CARDSLOT_EVENT_INSERTION_16, + CARDSLOT_EVENT_REMOVAL_CB, CARDSLOT_EVENT_INSERTION_CB + }; + + while (sc->sc_th_enable) { + s = spltty(); + if ((ce = SIMPLEQ_FIRST(&sc->sc_events)) == NULL) { + splx(s); + (void) tsleep(&sc->sc_events, PWAIT, "cardslotev", 0); + continue; + } + SIMPLEQ_REMOVE_HEAD(&sc->sc_events, ce, ce_q); + splx(s); + + if (IS_CARDSLOT_INSERT_REMOVE_EV(ce->ce_type)) { + /* Chattering supression */ + s = spltty(); + while (1) { + struct cardslot_event *ce1, *ce2; + + if ((ce1 = SIMPLEQ_FIRST(&sc->sc_events)) == NULL) { + break; + } + if (ce1->ce_type != antonym_ev[ce->ce_type]) { + break; + } + if ((ce2 = SIMPLEQ_NEXT(ce1, ce_q)) == NULL) { + break; + } + if (ce2->ce_type == ce->ce_type) { + SIMPLEQ_REMOVE_HEAD(&sc->sc_events, ce1, ce_q); + free(ce1, M_TEMP); + SIMPLEQ_REMOVE_HEAD(&sc->sc_events, ce2, ce_q); + free(ce2, M_TEMP); + } + } + splx(s); + } + + switch (ce->ce_type) { + case CARDSLOT_EVENT_INSERTION_CB: + if ((CARDSLOT_CARDTYPE(sc->sc_status) == CARDSLOT_STATUS_CARD_CB) + || (CARDSLOT_CARDTYPE(sc->sc_status) == CARDSLOT_STATUS_CARD_16)) { + if (CARDSLOT_WORK(sc->sc_status) == CARDSLOT_STATUS_WORKING) { + /* A card has already been inserted and work. */ + break; + } + } + + if (sc->sc_cb_softc) { + CARDSLOT_SET_CARDTYPE(sc->sc_status, CARDSLOT_STATUS_CARD_CB); + if (cardbus_attach_card(sc->sc_cb_softc) > 0) { + /* at least one function works */ + CARDSLOT_SET_WORK(sc->sc_status, CARDSLOT_STATUS_WORKING); + } else { + /* no functions work or this card is not known */ + CARDSLOT_SET_WORK(sc->sc_status, CARDSLOT_STATUS_NOTWORK); + } + } else { + panic("no cardbus on %s", sc->sc_dev.dv_xname); + } + + break; + + case CARDSLOT_EVENT_INSERTION_16: + if ((CARDSLOT_CARDTYPE(sc->sc_status) == CARDSLOT_STATUS_CARD_CB) + || (CARDSLOT_CARDTYPE(sc->sc_status) == CARDSLOT_STATUS_CARD_16)) { + if (CARDSLOT_WORK(sc->sc_status) == CARDSLOT_STATUS_WORKING) { + /* A card has already been inserted and work. */ + break; + } + } + if (sc->sc_16_softc) { + CARDSLOT_SET_CARDTYPE(sc->sc_status, CARDSLOT_STATUS_CARD_16); + if (pcmcia_card_attach((struct device *)sc->sc_16_softc)) { + /* Do not attach */ + CARDSLOT_SET_WORK(sc->sc_status, CARDSLOT_STATUS_NOTWORK); + } else { + /* working */ + CARDSLOT_SET_WORK(sc->sc_status, CARDSLOT_STATUS_WORKING); + } + } else { + panic("no 16-bit pcmcia on %s", sc->sc_dev.dv_xname); + } + + break; + + case CARDSLOT_EVENT_REMOVAL_CB: + if (CARDSLOT_CARDTYPE(sc->sc_status) == CARDSLOT_STATUS_CARD_CB) { + /* CardBus card has not been inserted. */ + if (CARDSLOT_WORK(sc->sc_status) == CARDSLOT_STATUS_WORKING) { + cardbus_detach_card(sc->sc_cb_softc); + CARDSLOT_SET_WORK(sc->sc_status, CARDSLOT_STATUS_NOTWORK); + CARDSLOT_SET_WORK(sc->sc_status, CARDSLOT_STATUS_CARD_NONE); + } + CARDSLOT_SET_CARDTYPE(sc->sc_status, CARDSLOT_STATUS_CARD_NONE); + } else if (CARDSLOT_CARDTYPE(sc->sc_status) != CARDSLOT_STATUS_CARD_16) { + /* Unknown card... */ + CARDSLOT_SET_CARDTYPE(sc->sc_status, CARDSLOT_STATUS_CARD_NONE); + } + CARDSLOT_SET_WORK(sc->sc_status, CARDSLOT_STATUS_NOTWORK); + break; + + case CARDSLOT_EVENT_REMOVAL_16: + DPRINTF(("%s: removal event\n", sc->sc_dev.dv_xname)); + if (CARDSLOT_CARDTYPE(sc->sc_status) != CARDSLOT_STATUS_CARD_16) { + /* 16-bit card has not been inserted. */ + break; + } + if ((sc->sc_16_softc != NULL) + && (CARDSLOT_WORK(sc->sc_status) == CARDSLOT_STATUS_WORKING)) { + struct pcmcia_softc *psc = sc->sc_16_softc; + + pcmcia_card_deactivate((struct device *)psc); + pcmcia_chip_socket_disable(psc->pct, psc->pch); + pcmcia_card_detach((struct device *)psc, DETACH_FORCE); + } + CARDSLOT_SET_CARDTYPE(sc->sc_status, CARDSLOT_STATUS_CARD_NONE); + CARDSLOT_SET_WORK(sc->sc_status, CARDSLOT_STATUS_NOTWORK); + break; + + default: + panic("cardslot_event_thread: unknown event %d", ce->ce_type); + } + free(ce, M_TEMP); + } + + sc->sc_event_thread = NULL; + + /* In case parent is waiting for us to exit. */ + wakeup(sc); + + kthread_exit(0); +} diff --git a/sys/dev/cardbus/cardslotvar.h b/sys/dev/cardbus/cardslotvar.h new file mode 100644 index 00000000000..be1e30c862a --- /dev/null +++ b/sys/dev/cardbus/cardslotvar.h @@ -0,0 +1,125 @@ +/* $OpenBSD: cardslotvar.h,v 1.1 2000/04/08 05:50:52 aaron Exp $ */ +/* $NetBSD: cardslotvar.h,v 1.5 2000/03/13 23:52:38 soren Exp $ */ + +/* + * Copyright (c) 1999 + * HAYAKAWA Koichi. 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 the author. + * 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. + */ + +#ifndef _DEV_CARDBUS_CARDSLOTVAR_H_ +#define _DEV_CARDBUS_CARDSLOTVAR_H_ + +/* require sys/device.h */ +/* require sys/queue.h */ + +struct cardslot_event; + +/* + * The data structure cardslot_attach_args is the attach argument for + * PCMCIA (including CardBus and 16-bit card) slot. + */ +struct cardslot_attach_args { + char *caa_busname; + + int caa_slot; + + /* for cardbus... */ + struct cbslot_attach_args *caa_cb_attach; + + /* for 16-bit pcmcia */ + struct pcmciabus_attach_args *caa_16_attach; + + /* XXX: for 16-bit pcmcia. dirty! This should be removed to achieve MI. */ + struct pcic_handle *caa_ph; +}; + + +/* + * The data structure cardslot_attach_args is the attach argument for + * PCMCIA (including CardBus and 16-bit card) slot. + */ +struct cardslot_softc { + struct device sc_dev; + + int sc_slot; /* slot number */ + int sc_status; /* the status of slot */ + + struct cardbus_softc *sc_cb_softc; + struct pcmcia_softc *sc_16_softc; + + struct proc *sc_event_thread; + int sc_th_enable; /* true if the thread is enabled */ + + /* An event queue for the thread which processes slot state events. */ + + SIMPLEQ_HEAD(, cardslot_event) sc_events; +}; + +#define CARDSLOT_STATUS_CARD_MASK 0x07 +#define CARDSLOT_STATUS_CARD_NONE 0x00 /* NO card inserted */ +#define CARDSLOT_STATUS_CARD_16 0x01 /* 16-bit pcmcia card inserted */ +#define CARDSLOT_STATUS_CARD_CB 0x02 /* CardBus pcmcia card inserted */ +#define CARDSLOT_STATUS_UNKNOWN 0x07 /* Unknown card inserted */ + +#define CARDSLOT_CARDTYPE(x) ((x) & CARDSLOT_STATUS_CARD_MASK) +#define CARDSLOT_SET_CARDTYPE(x, type) \ + do {(x) &= ~CARDSLOT_STATUS_CARD_MASK;\ + (x) |= (CARDSLOT_STATUS_CARD_MASK & (type));} while(0) + +#define CARDSLOT_STATUS_WORK_MASK 0x08 +#define CARDSLOT_STATUS_WORKING 0x08 /* at least one function works */ +#define CARDSLOT_STATUS_NOTWORK 0x00 /* no functions are working */ + +#define CARDSLOT_WORK(x) ((x) & CARDSLOT_STATUS_WORK_MASK) +#define CARDSLOT_SET_WORK(x, type) \ + do {(x) &= ~CARDSLOT_STATUS_WORK_MASK;\ + (x) |= (CARDSLOT_STATUS_WORK_MASK & (type));} while(0) + + +struct cardslot_event { + SIMPLEQ_ENTRY(cardslot_event) ce_q; + + int ce_type; +}; + +typedef struct cardslot_softc *cardslot_t; + +/* ce_type */ +#define CARDSLOT_EVENT_INSERTION_16 0 +#define CARDSLOT_EVENT_REMOVAL_16 1 + +#define CARDSLOT_EVENT_INSERTION_CB 2 +#define CARDSLOT_EVENT_REMOVAL_CB 3 + +#define IS_CARDSLOT_INSERT_REMOVE_EV(x) (0 <= (x) && (x) <= 3) + +void cardslot_event_throw __P((cardslot_t cs, int ev)); + +#endif /* !_DEV_CARDBUS_CARDSLOTVAR_H_ */ diff --git a/sys/dev/cardbus/files.cardbus b/sys/dev/cardbus/files.cardbus new file mode 100644 index 00000000000..f3dc58bacb1 --- /dev/null +++ b/sys/dev/cardbus/files.cardbus @@ -0,0 +1,51 @@ +# $OpenBSD: files.cardbus,v 1.1 2000/04/08 05:50:52 aaron Exp $ +# $NetBSD: files.cardbus,v 1.8 2000/01/26 06:37:24 thorpej Exp $ +# +# files.cardbus +# + +device cardslot: cbbus, pcmciabus +attach cardslot at pcmciaslot +file dev/cardbus/cardslot.c cardslot needs-flag + +device cardbus {[dev = -1], [function = -1]} +attach cardbus at cbbus +file dev/cardbus/cardbus.c cardbus needs-flag +file dev/cardbus/cardbus_map.c cardbus +file dev/cardbus/cardbus_exrom.c cardbus +file dev/cardbus/rbus.c cardbus + +# +# 3Com 3C575TX, 3C575BTX, and 3C575CTX +# +attach xl at cardbus with xl_cardbus +file dev/cardbus/if_xl_cardbus.c xl_cardbus + +# +# Intel PRO/100 8255x based CardBus cards. +# +#attach fxp at cardbus with fxp_cardbus +#file dev/cardbus/if_fxp_cardbus.c fxp_cardbus + +# +# +#attach com at cardbus with com_cardbus +#file dev/cardbus/com_cardbus.c com_cardbus + +# +# DECchip 21143 and clones. +# +#attach tlp at cardbus with tlp_cardbus +#file dev/cardbus/if_tlp_cardbus.c tlp_cardbus + +# +# OHCI USB controller +# +#attach ohci at cardbus with ohci_cardbus +#file dev/cardbus/ohci_cardbus.c ohci_cardbus + +# +# Adaptec ADP-1480 SCSI controller +# +#attach ahc at cardbus with ahc_cardbus: ahc_seeprom, smc93cx6 +#file dev/cardbus/ahc_cardbus.c ahc_cardbus diff --git a/sys/dev/cardbus/if_xl_cardbus.c b/sys/dev/cardbus/if_xl_cardbus.c new file mode 100644 index 00000000000..d24fd484f94 --- /dev/null +++ b/sys/dev/cardbus/if_xl_cardbus.c @@ -0,0 +1,426 @@ +/* $OpenBSD: if_xl_cardbus.c,v 1.1 2000/04/08 05:50:52 aaron Exp $ */ +/* $NetBSD: if_xl_cardbus.c,v 1.13 2000/03/07 00:32:52 mycroft Exp $ */ + +/* + * CardBus specific routines for 3Com 3C575-family CardBus ethernet adapter + * + * Copyright (c) 1998 and 1999 + * HAYAKAWA Koichi. 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 the author. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY HAYAKAWA KOICHI ``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 TAKESHI OHASHI 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. + * + * + */ + +/* #define XL_DEBUG 4 */ /* define to report infomation for debugging */ + +#define XL_POWER_STATIC /* do not use enable/disable functions */ + /* I'm waiting elinkxl.c uses + sc->enable and sc->disable + functions. */ + +#include "bpfilter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef INET +#include +#include +#include +#include +#include +#endif + +#include +#include + +#include +#include + +#include + +#include + +#if defined DEBUG && !defined XL_DEBUG +#define XL_DEBUG +#endif + +#if defined XL_DEBUG +#define DPRINTF(a) printf a +#else +#define DPRINTF(a) +#endif + +#define CARDBUS_3C575BTX_FUNCSTAT_PCIREG CARDBUS_BASE2_REG /* means 0x18 */ +#define XL_CB_INTR 4 /* intr acknowledge reg. CardBus only */ +#define XL_CB_INTR_ACK 0x8000 /* intr acknowledge bit */ + +int xl_cardbus_match __P((struct device *, void *, void *)); +void xl_cardbus_attach __P((struct device *, struct device *,void *)); +int xl_cardbus_detach __P((struct device *, int)); +void xl_cardbus_intr_ack __P((struct xl_softc *)); + +#if !defined XL_POWER_STATIC +int xl_cardbus_enable __P((struct xl_softc *sc)); +void xl_cardbus_disable __P((struct xl_softc *sc)); +#endif /* !defined XL_POWER_STATIC */ + +struct xl_cardbus_softc { + struct xl_softc sc_softc; + + cardbus_devfunc_t sc_ct; + int sc_intrline; + u_int8_t sc_cardbus_flags; +#define XL_REATTACH 0x01 +#define XL_ABSENT 0x02 + u_int8_t sc_cardtype; +#define XL_3C575 1 +#define XL_3C575B 2 + + /* CardBus function status space. 575B requests it. */ + bus_space_tag_t sc_funct; + bus_space_handle_t sc_funch; + bus_size_t sc_funcsize; + + bus_size_t sc_mapsize; /* the size of mapped bus space region */ +}; + +struct cfattach xl_cardbus_ca = { + sizeof(struct xl_cardbus_softc), xl_cardbus_match, + xl_cardbus_attach, xl_cardbus_detach +}; + +const struct xl_cardbus_product { + u_int32_t ecp_prodid; /* CardBus product ID */ + int ecp_flags; /* initial softc flags */ + pcireg_t ecp_csr; /* PCI CSR flags */ + int ecp_cardtype; /* card type */ + const char *ecp_name; /* device name */ +} xl_cardbus_products[] = { + { CARDBUS_PRODUCT_3COM_3C575TX, + /* XL_CONF_MII, */ 0, + CARDBUS_COMMAND_IO_ENABLE | CARDBUS_COMMAND_MASTER_ENABLE, + XL_3C575, + "3c575-TX Ethernet" }, + + { CARDBUS_PRODUCT_3COM_3C575BTX, + /* XL_CONF_90XB|XL_CONF_MII, */ 0, + CARDBUS_COMMAND_IO_ENABLE | CARDBUS_COMMAND_MEM_ENABLE | + CARDBUS_COMMAND_MASTER_ENABLE, + XL_3C575B, + "3c575B-TX Ethernet" }, + + { CARDBUS_PRODUCT_3COM_3CCFE575CT, + /* XL_CONF_90XB|XL_CONF_MII, */ 0, + CARDBUS_COMMAND_IO_ENABLE | CARDBUS_COMMAND_MEM_ENABLE | + CARDBUS_COMMAND_MASTER_ENABLE, + XL_3C575B, + "3c575C-TX Ethernet" }, + + { 0, + 0, + 0, + NULL }, +}; + +const struct xl_cardbus_product *xl_cardbus_lookup + __P((const struct cardbus_attach_args *)); + +const struct xl_cardbus_product * +xl_cardbus_lookup(ca) + const struct cardbus_attach_args *ca; +{ + const struct xl_cardbus_product *ecp; + + if (CARDBUS_VENDOR(ca->ca_id) != CARDBUS_VENDOR_3COM) + return (NULL); + + for (ecp = xl_cardbus_products; ecp->ecp_name != NULL; ecp++) + if (CARDBUS_PRODUCT(ca->ca_id) == ecp->ecp_prodid) + return (ecp); + return (NULL); +} + +int +xl_cardbus_match(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct cardbus_attach_args *ca = aux; + + if (xl_cardbus_lookup(ca) != NULL) + return (1); + + return (0); +} + +void +xl_cardbus_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct xl_cardbus_softc *psc = (void *)self; + struct xl_softc *sc = &psc->sc_softc; + struct cardbus_attach_args *ca = aux; + cardbus_devfunc_t ct = ca->ca_ct; + cardbus_chipset_tag_t cc = ct->ct_cc; + cardbus_function_tag_t cf = ct->ct_cf; + cardbusreg_t iob, command, bhlc; + const struct xl_cardbus_product *ecp; + bus_space_handle_t ioh; + bus_addr_t adr; + + if (Cardbus_mapreg_map(ct, CARDBUS_BASE0_REG, CARDBUS_MAPREG_TYPE_IO, 0, + &sc->xl_btag, &ioh, &adr, &psc->sc_mapsize)) { + printf(": can't map i/o space\n"); + return; + } + + ecp = xl_cardbus_lookup(ca); + if (ecp == NULL) { + printf("\n"); + panic("xl_cardbus_attach: impossible"); + } + + printf(": 3Com %s", ecp->ecp_name); + +#if 0 +#if !defined XL_POWER_STATIC + sc->enable = xl_cardbus_enable; + sc->disable = xl_cardbus_disable; +#else + sc->enable = NULL; + sc->disable = NULL; +#endif + sc->enabled = 1; + sc->sc_dmat = ca->ca_dmat; + sc->xl_conf = ecp->ecp_flags; +#endif + sc->xl_bustype = XL_BUS_CARDBUS; + + iob = adr; + sc->xl_bhandle = ioh; + +#if rbus +#else + (ct->ct_cf->cardbus_io_open)(cc, 0, iob, iob + 0x40); +#endif + (ct->ct_cf->cardbus_ctrl)(cc, CARDBUS_IO_ENABLE); + + command = cardbus_conf_read(cc, cf, ca->ca_tag, + CARDBUS_COMMAND_STATUS_REG); + command |= ecp->ecp_csr; + psc->sc_cardtype = ecp->ecp_cardtype; + + if (psc->sc_cardtype == XL_3C575B) { + /* Map CardBus function status window. */ + if (Cardbus_mapreg_map(ct, CARDBUS_3C575BTX_FUNCSTAT_PCIREG, + CARDBUS_MAPREG_TYPE_MEM, 0, &psc->sc_funct, + &psc->sc_funch, 0, &psc->sc_funcsize)) { + printf("%s: unable to map function status window\n", + self->dv_xname); + return; + } + + /* + * Make sure CardBus brigde can access memory space. Usually + * memory access is enabled by BIOS, but some BIOSes do not + * enable it. + */ + (ct->ct_cf->cardbus_ctrl)(cc, CARDBUS_MEM_ENABLE); + + /* Setup interrupt acknowledge hook */ + sc->intr_ack = xl_cardbus_intr_ack; + } + + (ct->ct_cf->cardbus_ctrl)(cc, CARDBUS_BM_ENABLE); + cardbus_conf_write(cc, cf, ca->ca_tag, CARDBUS_COMMAND_STATUS_REG, + command); + + /* + * set latency timmer + */ + bhlc = cardbus_conf_read(cc, cf, ca->ca_tag, CARDBUS_BHLC_REG); + if (CARDBUS_LATTIMER(bhlc) < 0x20) { + /* at least the value of latency timer should 0x20. */ + DPRINTF(("if_xl_cardbus: lattimer 0x%x -> 0x20\n", + CARDBUS_LATTIMER(bhlc))); + bhlc &= ~(CARDBUS_LATTIMER_MASK << CARDBUS_LATTIMER_SHIFT); + bhlc |= (0x20 << CARDBUS_LATTIMER_SHIFT); + cardbus_conf_write(cc, cf, ca->ca_tag, CARDBUS_BHLC_REG, bhlc); + } + + psc->sc_ct = ca->ca_ct; + psc->sc_intrline = ca->ca_intrline; + +#if defined XL_POWER_STATIC + /* Map and establish the interrupt. */ + + sc->xl_intrhand = cardbus_intr_establish(cc, cf, ca->ca_intrline, + IPL_NET, xl_intr, psc); + + if (sc->xl_intrhand == NULL) { + printf(": couldn't establish interrupt"); + printf(" at %d", ca->ca_intrline); + printf("\n"); + return; + } + printf(": irq %d", ca->ca_intrline); +#endif + + bus_space_write_2(sc->xl_btag, sc->xl_bhandle, XL_COMMAND, XL_CMD_RESET); + delay(400); + { + int i = 0; + while (bus_space_read_2(sc->xl_btag, sc->xl_bhandle, XL_STATUS) & + XL_STAT_CMDBUSY) { + if (++i > 10000) { + printf("ex: timeout %x\n", + bus_space_read_2(sc->xl_btag, sc->xl_bhandle, + XL_STATUS)); + printf("ex: addr %x\n", + cardbus_conf_read(cc, cf, ca->ca_tag, + CARDBUS_BASE0_REG)); + return; /* emergency exit */ + } + } + } + + xl_attach(sc); + + if (psc->sc_cardtype == XL_3C575B) + bus_space_write_4(psc->sc_funct, psc->sc_funch, + XL_CB_INTR, XL_CB_INTR_ACK); + +#if !defined XL_POWER_STATIC + cardbus_function_disable(psc->sc_ct); + sc->enabled = 0; +#endif +} + +void +xl_cardbus_intr_ack(sc) + struct xl_softc *sc; +{ + struct xl_cardbus_softc *psc = (struct xl_cardbus_softc *)sc; + + bus_space_write_4(psc->sc_funct, psc->sc_funch, XL_CB_INTR, + XL_CB_INTR_ACK); +} + +int +xl_cardbus_detach(self, arg) + struct device *self; + int arg; +{ + struct xl_cardbus_softc *psc = (void *)self; + struct xl_softc *sc = &psc->sc_softc; + struct cardbus_devfunc *ct = psc->sc_ct; + int rv = 0; + +#if defined(DIAGNOSTIC) + if (ct == NULL) { + panic("%s: data structure lacks\n", sc->sc_dev.dv_xname); + } +#endif + +#if 0 + rv = xl_detach(sc); +#endif + if (rv == 0) { + /* + * Unhook the interrupt handler. + */ + cardbus_intr_disestablish(ct->ct_cc, ct->ct_cf, sc->xl_intrhand); + + if (psc->sc_cardtype == XL_3C575B) { + Cardbus_mapreg_unmap(ct, + CARDBUS_3C575BTX_FUNCSTAT_PCIREG, + psc->sc_funct, psc->sc_funch, psc->sc_funcsize); + } + + Cardbus_mapreg_unmap(ct, CARDBUS_BASE0_REG, sc->xl_btag, + sc->xl_bhandle, psc->sc_mapsize); + } + return (rv); +} + +#if !defined XL_POWER_STATIC +int +xl_cardbus_enable(sc) + struct xl_softc *sc; +{ + struct xl_cardbus_softc *csc = (struct xl_cardbus_softc *)sc; + cardbus_function_tag_t cf = csc->sc_ct->ct_cf; + cardbus_chipset_tag_t cc = csc->sc_ct->ct_cc; + + Cardbus_function_enable(csc->sc_ct); + cardbus_restore_bar(csc->sc_ct); + + sc->xl_intrhand = cardbus_intr_establish(cc, cf, csc->sc_intrline, + IPL_NET, xl_intr, sc); + if (NULL == sc->xl_intrhand) { + printf("%s: couldn't establish interrupt\n", + sc->sc_dev.dv_xname); + return (1); + } + + return (0); +} + +void +xl_cardbus_disable(sc) + struct xl_softc *sc; +{ + struct xl_cardbus_softc *csc = (struct xl_cardbus_softc *)sc; + cardbus_function_tag_t cf = csc->sc_ct->ct_cf; + cardbus_chipset_tag_t cc = csc->sc_ct->ct_cc; + + cardbus_save_bar(csc->sc_ct); + + Cardbus_function_disable(csc->sc_ct); + + cardbus_intr_disestablish(cc, cf, sc->xl_intrhand); +} +#endif /* XL_POWER_STATIC */ diff --git a/sys/dev/cardbus/rbus.c b/sys/dev/cardbus/rbus.c new file mode 100644 index 00000000000..5ca2b2529a4 --- /dev/null +++ b/sys/dev/cardbus/rbus.c @@ -0,0 +1,387 @@ +/* $OpenBSD: rbus.c,v 1.1 2000/04/08 05:50:52 aaron Exp $ */ +/* $NetBSD: rbus.c,v 1.3 1999/11/06 06:20:53 soren Exp $ */ +/* + * Copyright (c) 1999 + * HAYAKAWA Koichi. 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 HAYAKAWA Koichi. + * 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 + +/* #define RBUS_DEBUG */ + +#if defined RBUS_DEBUG +#define STATIC +#define DPRINTF(a) printf a +#define DDELAY(x) delay((x)*1000*1000) +#else +#define STATIC static +#define DPRINTF(a) +#endif + + + +static rbus_tag_t rbus_new_body __P((bus_space_tag_t bt, rbus_tag_t parent, + struct extent *ex, bus_addr_t start, + bus_addr_t end, bus_addr_t offset, + int flags)); + + +int +rbus_space_alloc(rbt, addr, size, mask, align, flags, addrp, bshp) + rbus_tag_t rbt; + bus_addr_t addr; + bus_size_t size; + bus_addr_t mask, align; + int flags; + bus_addr_t *addrp; + bus_space_handle_t *bshp; +{ + return rbus_space_alloc_subregion(rbt, rbt->rb_start, rbt->rb_end, addr, + size, mask, align, flags, addrp, bshp); +} + + + + +int +rbus_space_alloc_subregion(rbt, substart, subend, addr, size, mask, align, flags, addrp, bshp) + rbus_tag_t rbt; + bus_addr_t addr; + bus_addr_t substart; + bus_addr_t subend; + bus_size_t size; + bus_addr_t mask, align; + int flags; + bus_addr_t *addrp; + bus_space_handle_t *bshp; +{ + bus_addr_t decodesize = mask + 1; + bus_addr_t boundary, search_addr; + int val = 0; + bus_addr_t result; + int exflags = EX_FAST | EX_NOWAIT; + + DPRINTF(("rbus_space_alloc: addr %lx, size %lx, mask %lx, align %lx\n", + addr, size, mask, align)); + + addr += rbt->rb_offset; + + if (mask == 0) { + /* FULL Decode */ + decodesize = 0; + } + + if (rbt->rb_flags == RBUS_SPACE_ASK_PARENT) { + return rbus_space_alloc(rbt->rb_parent, addr, size, mask, align, flags, + addrp, bshp); + } else if (rbt->rb_flags == RBUS_SPACE_SHARE || + rbt->rb_flags == RBUS_SPACE_DEDICATE) { + /* rbt has its own sh_extent */ + + /* sanity check: the subregion [substart, subend] should be + smaller than the region included in sh_extent */ + if (substart < rbt->rb_ext->ex_start || subend > rbt->rb_ext->ex_end) { + return 1; + } + + if (decodesize == align) { + if(extent_alloc_subregion(rbt->rb_ext, substart, subend, size, align, 0, + exflags, (u_long *)&result)) { + return 1; + } + } else if (decodesize == 0) { + /* maybe, the resister is overflowed. */ + + if (extent_alloc_subregion(rbt->rb_ext, addr, addr + size, size, + 0, 0, exflags, (u_long *)&result)) { + return 1; + } + } else { + + boundary = decodesize > align ? decodesize : align; + + search_addr = (substart & ~(boundary - 1)) + addr; + + if (search_addr < substart) { + search_addr += boundary; + } + + for (; search_addr + size <= subend; search_addr += boundary) { + val = extent_alloc_subregion(rbt->rb_ext,search_addr, search_addr+size, + size, align, 0, exflags, (u_long *)&result); + if (val == 0) { + break; + } + } + if (val) { + return 1; + } + } + + if(md_space_map(rbt->rb_bt, result, size, flags, bshp)) { + /* map failed */ + extent_free(rbt->rb_ext, result, size, exflags); + return 1; + } + + if (addrp != NULL) { + *addrp = result + rbt->rb_offset; + } + return 0; + + } else { + /* error!! */ + return 1; + } + return 1; +} + + + + + +int +rbus_space_free(rbt, bsh, size, addrp) + rbus_tag_t rbt; + bus_space_handle_t bsh; + bus_size_t size; + bus_addr_t *addrp; +{ + int exflags = EX_FAST | EX_NOWAIT; + bus_addr_t addr; + int status = 1; + + if (rbt->rb_flags == RBUS_SPACE_ASK_PARENT) { + status = rbus_space_free(rbt->rb_parent, bsh, size, &addr); + } else if (rbt->rb_flags == RBUS_SPACE_SHARE || + rbt->rb_flags == RBUS_SPACE_DEDICATE) { + md_space_unmap(rbt->rb_bt, bsh, size, &addr); + + extent_free(rbt->rb_ext, addr, size, exflags); + + status = 0; + } else { + /* error. INVALID rbustag */ + status = 1; + } + if (addrp != NULL) { + *addrp = addr; + } + return status; +} + + + +/* + * static rbus_tag_t + * rbus_new_body(bus_space_tag_t bt, rbus_tag_t parent, + * struct extent *ex, bus_addr_t start, bus_size_t end, + * bus_addr_t offset, int flags) + * + */ +static rbus_tag_t +rbus_new_body(bt, parent, ex, start, end, offset, flags) + bus_space_tag_t bt; + rbus_tag_t parent; + struct extent *ex; + bus_addr_t start, end, offset; + int flags; +{ + rbus_tag_t rb; + + /* sanity check */ + if (parent != NULL) { + if (start < parent->rb_start || end > parent->rb_end) { + /* out of range: [start, size] should be containd in parent space */ + return 0; + /* Should I invoke panic? */ + } + } + + if (NULL == (rb = (rbus_tag_t)malloc(sizeof(struct rbustag), M_DEVBUF, + M_NOWAIT))) { + panic("no memory for rbus instance"); + } + + rb->rb_bt = bt; + rb->rb_parent = parent; + rb->rb_start = start; + rb->rb_end = end; + rb->rb_offset = offset; + rb->rb_flags = flags; + rb->rb_ext = ex; + + DPRINTF(("rbus_new_body: [%lx, %lx] type %s name [%s]\n", start, end, + flags == RBUS_SPACE_SHARE ? "share" : + flags == RBUS_SPACE_DEDICATE ? "dedicated" : + flags == RBUS_SPACE_ASK_PARENT ? "parent" : "invalid", + ex != NULL ? ex->ex_name : "noname")); + + return rb; +} + + + +/* + * rbus_tag_t rbus_new(rbus_tag_t parent, bus_addr_t start, bus_size_t + * size, bus_addr_t offset, int flags) + * + * This function makes a new child rbus instance. + */ +rbus_tag_t +rbus_new(parent, start, size, offset, flags) + rbus_tag_t parent; + bus_addr_t start; + bus_size_t size; + bus_addr_t offset; + int flags; +{ + rbus_tag_t rb; + struct extent *ex = NULL; + bus_addr_t end = start + size; + + if (flags == RBUS_SPACE_SHARE) { + ex = parent->rb_ext; + } else if (flags == RBUS_SPACE_DEDICATE) { + if (NULL == (ex = extent_create("rbus", start, end, M_DEVBUF, NULL, 0, + EX_NOCOALESCE|EX_NOWAIT))) { + free(rb, M_DEVBUF); + return NULL; + } + } else if (flags == RBUS_SPACE_ASK_PARENT) { + ex = NULL; + } else { + /* Invalid flag */ + return 0; + } + + rb = rbus_new_body(parent->rb_bt, parent, ex, start, start + size, + offset, flags); + + if ((rb == NULL) && (flags == RBUS_SPACE_DEDICATE)) { + extent_destroy(ex); + } + + return rb; +} + + + + +/* + * rbus_tag_t rbus_new_root_delegate(bus_space_tag, bus_addr_t, + * bus_size_t, bus_addr_t offset) + * + * This function makes a root rbus instance. + */ +rbus_tag_t +rbus_new_root_delegate(bt, start, size, offset) + bus_space_tag_t bt; + bus_addr_t start; + bus_size_t size; + bus_addr_t offset; +{ + rbus_tag_t rb; + struct extent *ex; + + if (NULL == (ex = extent_create("rbus root", start, start + size, M_DEVBUF, + NULL, 0, EX_NOCOALESCE|EX_NOWAIT))) { + return NULL; + } + + rb = rbus_new_body(bt, NULL, ex, start, start + size, offset, + RBUS_SPACE_DEDICATE); + + if (rb == NULL) { + extent_destroy(ex); + } + + return rb; +} + + + +/* + * rbus_tag_t rbus_new_root_share(bus_space_tag, struct extent *, + * bus_addr_t, bus_size_t, bus_addr_t offset) + * + * This function makes a root rbus instance. + */ +rbus_tag_t +rbus_new_root_share(bt, ex, start, size, offset) + bus_space_tag_t bt; + struct extent *ex; + bus_addr_t start; + bus_size_t size; + bus_addr_t offset; +{ + /* sanity check */ + if (start < ex->ex_start || start + size > ex->ex_end) { + /* out of range: [start, size] should be containd in parent space */ + return 0; + /* Should I invoke panic? */ + } + + return rbus_new_body(bt, NULL, ex, start, start + size, offset, + RBUS_SPACE_SHARE); +} + + + + + +/* + * int rbus_delete (rbus_tag_t rb) + * + * This function deletes the rbus structure pointed in the argument. + */ +int +rbus_delete(rb) + rbus_tag_t rb; +{ + DPRINTF(("rbus_delete called [%s]\n", + rb->rb_ext != NULL ? rb->rb_ext->ex_name : "noname")); + if (rb->rb_flags == RBUS_SPACE_DEDICATE) { + extent_destroy(rb->rb_ext); + } + + free(rb, M_DEVBUF); + + return 0; +} diff --git a/sys/dev/cardbus/rbus.h b/sys/dev/cardbus/rbus.h new file mode 100644 index 00000000000..89a6ac17d6f --- /dev/null +++ b/sys/dev/cardbus/rbus.h @@ -0,0 +1,166 @@ +/* $OpenBSD: rbus.h,v 1.1 2000/04/08 05:50:53 aaron Exp $ */ +/* $NetBSD: rbus.h,v 1.3 1999/12/15 12:28:55 kleink Exp $ */ +/* + * Copyright (c) 1999 + * HAYAKAWA Koichi. 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 the author. + * 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. + */ + +#ifndef _DEV_CARDBUS_RBUS_H_ +#define _DEV_CARDBUS_RBUS_H_ + +/* + * This file defines rbus (pseudo) class + * + * What is rbus? + * + * Ths rbus is a recursive bus-space administrator. This means a + * parent bus-space administrator, which usually belongs to a bus + * bridge, makes some child bus-space administorators and gives + * (restricted) bus-space for children. There are a root bus-space + * administrator which maintains whole bus-space. + * + * Why recursive? + * + * The recursive bus-space administration has two virtues. The + * former is this modelling matches the actual memory and io space + * management of bridge devices well. The latter is the rbus is + * distributed management system, so it matches well with + * multi-thread kernel. + * + * Abstraction + * + * The rbus models bus-to-bus bridge into three way: dedicate, share + * and slave. Dedicate means that the bridge has dedicate bus space. + * Share means that the bridge has bus space, but this bus space is + * shared with other bus bridges. Slave means the bus bridge which + * does not have it own bus space and ask a parent bus bridge for bus + * space when a client requests bus space to the bridge. + */ + + +/* require sys/extent.h */ +/* require machine/bus.h */ + +#define rbus 1 + + +struct extent; + + +/* + * General rule + * + * 1) When a rbustag has no space for child (it means rb_extent is + * NULL), ask bus-space for parent through rb_parent. + * + * 2) When a rbustag has its own space (whether shared or dedicated), + * allocate from rb_ext. + */ +struct rbustag { + bus_space_tag_t rb_bt; + struct rbustag *rb_parent; + struct extent *rb_ext; + bus_addr_t rb_start; + bus_addr_t rb_end; + bus_addr_t rb_offset; +#if notyet + int (*rb_space_alloc) __P((struct rbustag *, + bus_addr_t start, bus_addr_t end, + bus_addr_t addr, bus_size_t size, + bus_addr_t mask, bus_addr_t align, + int flags, + bus_addr_t *addrp, bus_space_handle_t *bshp)); + int (*rbus_space_free) __P((struct rbustag *, bus_space_handle_t, + bus_size_t size, bus_addr_t *addrp)); +#endif + int rb_flags; +#define RBUS_SPACE_INVALID 0x00 +#define RBUS_SPACE_SHARE 0x01 +#define RBUS_SPACE_DEDICATE 0x02 +#define RBUS_SPACE_MASK 0x03 +#define RBUS_SPACE_ASK_PARENT 0x04 + /* your own data below */ + void *rb_md; +}; + +typedef struct rbustag *rbus_tag_t; + + + + +/* + * These functions sugarcoat rbus interface to make rbus being used + * easier. These functions should be member functions of rbus + * `class'. + */ +int rbus_space_alloc __P((rbus_tag_t, + bus_addr_t addr, bus_size_t size, bus_addr_t mask, + bus_addr_t align, int flags, + bus_addr_t *addrp, bus_space_handle_t *bshp)); + +int rbus_space_alloc_subregion __P((rbus_tag_t, + bus_addr_t start, bus_addr_t end, + bus_addr_t addr, bus_size_t size, + bus_addr_t mask, bus_addr_t align, + int flags, + bus_addr_t *addrp, bus_space_handle_t *bshp)); + +int rbus_space_free __P((rbus_tag_t, bus_space_handle_t, bus_size_t size, + bus_addr_t *addrp)); + + +/* + * These functions create rbus instance. These functions are + * so-called-as a constructor of rbus. + * + * rbus_new is a constructor which make an rbus instance from a parent + * rbus. + */ +rbus_tag_t rbus_new __P((rbus_tag_t parent, bus_addr_t start, bus_size_t size, + bus_addr_t offset, int flags)); + +rbus_tag_t rbus_new_root_delegate __P((bus_space_tag_t, bus_addr_t, bus_size_t, + bus_addr_t offset)); +rbus_tag_t rbus_new_root_share __P((bus_space_tag_t, struct extent *, + bus_addr_t, bus_size_t,bus_addr_t offset)); + +/* + * This function release bus-space used by the argument. This + * function is so-called-as a destructor. + */ +int rbus_delete __P((rbus_tag_t)); + + +/* + * Machine-dependent definitions. + */ +#include + +#endif /* !_DEV_CARDBUS_RBUS_H_ */ diff --git a/sys/dev/ic/i82365.c b/sys/dev/ic/i82365.c index 8dacec68c01..b2d74edd8a7 100644 --- a/sys/dev/ic/i82365.c +++ b/sys/dev/ic/i82365.c @@ -1,4 +1,4 @@ -/* $OpenBSD: i82365.c,v 1.10 2000/02/02 16:49:05 fgsch Exp $ */ +/* $OpenBSD: i82365.c,v 1.11 2000/04/08 05:50:50 aaron Exp $ */ /* $NetBSD: i82365.c,v 1.10 1998/06/09 07:36:55 thorpej Exp $ */ /* @@ -104,6 +104,9 @@ void pcic_queue_event __P((struct pcic_handle *, int)); void pcic_wait_ready __P((struct pcic_handle *)); +u_int8_t st_pcic_read __P((struct pcic_handle *, int)); +void st_pcic_write __P((struct pcic_handle *, int, u_int8_t)); + struct cfdriver pcic_cd = { NULL, "pcic", DV_DULL }; @@ -209,8 +212,13 @@ pcic_attach(sc) DPRINTF(("pcic ident regs:")); - sc->handle[0].sc = sc; + sc->handle[0].ph_parent = (struct device *)sc; sc->handle[0].sock = C0SA; + /* initialise pcic_read and pcic_write functions */ + sc->handle[0].ph_read = st_pcic_read; + sc->handle[0].ph_write = st_pcic_write; + sc->handle[0].ph_bus_t = sc->iot; + sc->handle[0].ph_bus_h = sc->ioh; if (pcic_ident_ok(reg = pcic_read(&sc->handle[0], PCIC_IDENT))) { sc->handle[0].flags = PCIC_FLAG_SOCKETP; count++; @@ -221,8 +229,13 @@ pcic_attach(sc) DPRINTF((" 0x%02x", reg)); - sc->handle[1].sc = sc; + sc->handle[1].ph_parent = (struct device *)sc; sc->handle[1].sock = C0SB; + /* initialise pcic_read and pcic_write functions */ + sc->handle[1].ph_read = st_pcic_read; + sc->handle[1].ph_write = st_pcic_write; + sc->handle[1].ph_bus_t = sc->iot; + sc->handle[1].ph_bus_h = sc->ioh; if (pcic_ident_ok(reg = pcic_read(&sc->handle[1], PCIC_IDENT))) { sc->handle[1].flags = PCIC_FLAG_SOCKETP; count++; @@ -238,8 +251,13 @@ pcic_attach(sc) * if you try to read from the second one. Maybe pcic_ident_ok * shouldn't accept 0? */ - sc->handle[2].sc = sc; + sc->handle[2].ph_parent = (struct device *)sc; sc->handle[2].sock = C1SA; + /* initialise pcic_read and pcic_write functions */ + sc->handle[2].ph_read = st_pcic_read; + sc->handle[2].ph_write = st_pcic_write; + sc->handle[2].ph_bus_t = sc->iot; + sc->handle[2].ph_bus_h = sc->ioh; if (pcic_vendor(&sc->handle[0]) != PCIC_VENDOR_CIRRUS_PD672X || pcic_read(&sc->handle[2], PCIC_IDENT) != 0) { if (pcic_ident_ok(reg = pcic_read(&sc->handle[2], @@ -253,8 +271,13 @@ pcic_attach(sc) DPRINTF((" 0x%02x", reg)); - sc->handle[3].sc = sc; + sc->handle[3].ph_parent = (struct device *)sc; sc->handle[3].sock = C1SB; + /* initialise pcic_read and pcic_write functions */ + sc->handle[3].ph_read = st_pcic_read; + sc->handle[3].ph_write = st_pcic_write; + sc->handle[3].ph_bus_t = sc->iot; + sc->handle[3].ph_bus_h = sc->ioh; if (pcic_ident_ok(reg = pcic_read(&sc->handle[3], PCIC_IDENT))) { sc->handle[3].flags = PCIC_FLAG_SOCKETP; @@ -330,6 +353,7 @@ pcic_attach_socket(h) struct pcic_handle *h; { struct pcmciabus_attach_args paa; + struct pcic_softc *sc = (struct pcic_softc *)(h->ph_parent); /* initialize the rest of the handle */ @@ -340,18 +364,21 @@ pcic_attach_socket(h) /* now, config one pcmcia device per socket */ - paa.pct = (pcmcia_chipset_tag_t) h->sc->pct; + paa.paa_busname = "pcmcia"; + paa.pct = (pcmcia_chipset_tag_t) sc->pct; paa.pch = (pcmcia_chipset_handle_t) h; - paa.iobase = h->sc->iobase; - paa.iosize = h->sc->iosize; + paa.iobase = sc->iobase; + paa.iosize = sc->iosize; - h->pcmcia = config_found_sm(&h->sc->dev, &paa, pcic_print, + h->pcmcia = config_found_sm(&sc->dev, &paa, pcic_print, pcic_submatch); /* if there's actually a pcmcia device attached, initialize the slot */ if (h->pcmcia) pcic_init_socket(h); + else + h->flags &= ~PCIC_FLAG_SOCKETP; } void @@ -379,9 +406,9 @@ pcic_create_event_thread(arg) } if (kthread_create(pcic_event_thread, h, &h->event_thread, - "%s,%s", h->sc->dev.dv_xname, cs)) { + "%s,%s", h->ph_parent->dv_xname, cs)) { printf("%s: unable to create event thread for sock 0x%02x\n", - h->sc->dev.dv_xname, h->sock); + h->ph_parent->dv_xname, h->sock); panic("pcic_create_event_thread"); } } @@ -393,6 +420,7 @@ pcic_event_thread(arg) struct pcic_handle *h = arg; struct pcic_event *pe; int s; + struct pcic_softc *sc = (struct pcic_softc *)(h->ph_parent); while (h->shutdown == 0) { s = splhigh(); @@ -433,7 +461,7 @@ pcic_event_thread(arg) } splx(s); - DPRINTF(("%s: insertion event\n", h->sc->dev.dv_xname)); + DPRINTF(("%s: insertion event\n", h->ph_parent->dv_xname)); pcic_attach_card(h); break; @@ -459,7 +487,7 @@ pcic_event_thread(arg) } splx(s); - DPRINTF(("%s: removal event\n", h->sc->dev.dv_xname)); + DPRINTF(("%s: removal event\n", h->ph_parent->dv_xname)); pcic_detach_card(h, DETACH_FORCE); break; @@ -473,7 +501,7 @@ pcic_event_thread(arg) h->event_thread = NULL; /* In case parent is waiting for us to exit. */ - wakeup(h->sc); + wakeup(sc); kthread_exit(0); } @@ -483,6 +511,7 @@ pcic_init_socket(h) struct pcic_handle *h; { int reg; + struct pcic_softc *sc = (struct pcic_softc *)(h->ph_parent); /* * queue creation of a kernel thread to handle insert/removal events. @@ -495,7 +524,7 @@ pcic_init_socket(h) /* set up the card to interrupt on card detect */ - pcic_write(h, PCIC_CSC_INTR, (h->sc->irq << PCIC_CSC_INTR_IRQ_SHIFT) | + pcic_write(h, PCIC_CSC_INTR, (sc->irq << PCIC_CSC_INTR_IRQ_SHIFT) | PCIC_CSC_INTR_CD_ENABLE); pcic_write(h, PCIC_INTR, 0); pcic_read(h, PCIC_CSC); @@ -507,7 +536,7 @@ pcic_init_socket(h) reg = pcic_read(h, PCIC_CIRRUS_MISC_CTL_2); if (reg & PCIC_CIRRUS_MISC_CTL_2_SUSPEND) { DPRINTF(("%s: socket %02x was suspended\n", - h->sc->dev.dv_xname, h->sock)); + h->ph_parent->dv_xname, h->sock)); reg &= ~PCIC_CIRRUS_MISC_CTL_2_SUSPEND; pcic_write(h, PCIC_CIRRUS_MISC_CTL_2, reg); } @@ -648,21 +677,21 @@ pcic_intr_socket(h) PCIC_CSC_BATTDEAD); if (cscreg & PCIC_CSC_GPI) { - DPRINTF(("%s: %02x GPI\n", h->sc->dev.dv_xname, h->sock)); + DPRINTF(("%s: %02x GPI\n", h->ph_parent->dv_xname, h->sock)); } if (cscreg & PCIC_CSC_CD) { int statreg; statreg = pcic_read(h, PCIC_IF_STATUS); - DPRINTF(("%s: %02x CD %x\n", h->sc->dev.dv_xname, h->sock, + DPRINTF(("%s: %02x CD %x\n", h->ph_parent->dv_xname, h->sock, statreg)); if ((statreg & PCIC_IF_STATUS_CARDDETECT_MASK) == PCIC_IF_STATUS_CARDDETECT_PRESENT) { if (h->laststate != PCIC_LASTSTATE_PRESENT) { DPRINTF(("%s: enqueing INSERTION event\n", - h->sc->dev.dv_xname)); + h->ph_parent->dv_xname)); pcic_queue_event(h, PCIC_EVENT_INSERTION); } h->laststate = PCIC_LASTSTATE_PRESENT; @@ -670,11 +699,11 @@ pcic_intr_socket(h) if (h->laststate == PCIC_LASTSTATE_PRESENT) { /* Deactivate the card now. */ DPRINTF(("%s: deactivating card\n", - h->sc->dev.dv_xname)); + h->ph_parent->dv_xname)); pcic_deactivate_card(h); DPRINTF(("%s: enqueing REMOVAL event\n", - h->sc->dev.dv_xname)); + h->ph_parent->dv_xname)); pcic_queue_event(h, PCIC_EVENT_REMOVAL); } h->laststate = @@ -683,14 +712,14 @@ pcic_intr_socket(h) } } if (cscreg & PCIC_CSC_READY) { - DPRINTF(("%s: %02x READY\n", h->sc->dev.dv_xname, h->sock)); + DPRINTF(("%s: %02x READY\n", h->ph_parent->dv_xname, h->sock)); /* shouldn't happen */ } if (cscreg & PCIC_CSC_BATTWARN) { - DPRINTF(("%s: %02x BATTWARN\n", h->sc->dev.dv_xname, h->sock)); + DPRINTF(("%s: %02x BATTWARN\n", h->ph_parent->dv_xname, h->sock)); } if (cscreg & PCIC_CSC_BATTDEAD) { - DPRINTF(("%s: %02x BATTDEAD\n", h->sc->dev.dv_xname, h->sock)); + DPRINTF(("%s: %02x BATTDEAD\n", h->ph_parent->dv_xname, h->sock)); } return (cscreg ? 1 : 0); } @@ -770,6 +799,7 @@ pcic_chip_mem_alloc(pch, size, pcmhp) bus_addr_t addr; bus_size_t sizepg; int i, mask, mhandle; + struct pcic_softc *sc = (struct pcic_softc *)(h->ph_parent); /* out of sc->memh, allocate as many pages as necessary */ @@ -782,14 +812,14 @@ pcic_chip_mem_alloc(pch, size, pcmhp) mhandle = 0; /* XXX gcc -Wuninitialized */ for (i = 0; i < (PCIC_MEM_PAGES + 1 - sizepg); i++) { - if ((h->sc->subregionmask & (mask << i)) == (mask << i)) { - if (bus_space_subregion(h->sc->memt, h->sc->memh, + if ((sc->subregionmask & (mask << i)) == (mask << i)) { + if (bus_space_subregion(sc->memt, sc->memh, i * PCIC_MEM_PAGESIZE, sizepg * PCIC_MEM_PAGESIZE, &memh)) return (1); mhandle = mask << i; - addr = h->sc->membase + (i * PCIC_MEM_PAGESIZE); - h->sc->subregionmask &= ~(mhandle); + addr = sc->membase + (i * PCIC_MEM_PAGESIZE); + sc->subregionmask &= ~(mhandle); break; } } @@ -800,7 +830,7 @@ pcic_chip_mem_alloc(pch, size, pcmhp) DPRINTF(("pcic_chip_mem_alloc bus addr 0x%lx+0x%lx\n", (u_long) addr, (u_long) size)); - pcmhp->memt = h->sc->memt; + pcmhp->memt = sc->memt; pcmhp->memh = memh; pcmhp->addr = addr; pcmhp->size = size; @@ -816,8 +846,9 @@ pcic_chip_mem_free(pch, pcmhp) struct pcmcia_mem_handle *pcmhp; { struct pcic_handle *h = (struct pcic_handle *) pch; + struct pcic_softc *sc = (struct pcic_softc *)(h->ph_parent); - h->sc->subregionmask |= pcmhp->mhandle; + sc->subregionmask |= pcmhp->mhandle; } static struct mem_map_index_st { @@ -882,17 +913,17 @@ pcic_chip_do_mem_map(h, win) int win; { int reg; + int kind = h->mem[win].kind & ~PCMCIA_WIDTH_MEM_MASK; + int mem8 = + (h->mem[win].kind & PCMCIA_WIDTH_MEM_MASK) == PCMCIA_WIDTH_MEM8 + || (kind == PCMCIA_MEM_ATTR); pcic_write(h, mem_map_index[win].sysmem_start_lsb, (h->mem[win].addr >> PCIC_SYSMEM_ADDRX_SHIFT) & 0xff); pcic_write(h, mem_map_index[win].sysmem_start_msb, ((h->mem[win].addr >> (PCIC_SYSMEM_ADDRX_SHIFT + 8)) & - PCIC_SYSMEM_ADDRX_START_MSB_ADDR_MASK)); - -#if 0 - /* XXX do I want 16 bit all the time? */ - PCIC_SYSMEM_ADDRX_START_MSB_DATASIZE_16BIT; -#endif + PCIC_SYSMEM_ADDRX_START_MSB_ADDR_MASK) | + (mem8 ? 0 : PCIC_SYSMEM_ADDRX_START_MSB_DATASIZE_16BIT)); pcic_write(h, mem_map_index[win].sysmem_stop_lsb, ((h->mem[win].addr + h->mem[win].size) >> @@ -908,7 +939,7 @@ pcic_chip_do_mem_map(h, win) pcic_write(h, mem_map_index[win].cardmem_msb, ((h->mem[win].offset >> (PCIC_CARDMEM_ADDRX_SHIFT + 8)) & PCIC_CARDMEM_ADDRX_MSB_ADDR_MASK) | - ((h->mem[win].kind == PCMCIA_MEM_ATTR) ? + ((kind == PCMCIA_MEM_ATTR) ? PCIC_CARDMEM_ADDRX_MSB_REGACTIVE_ATTR : 0)); reg = pcic_read(h, PCIC_ADDRWIN_ENABLE); @@ -946,6 +977,7 @@ pcic_chip_mem_map(pch, kind, card_addr, size, pcmhp, offsetp, windowp) bus_addr_t busaddr; long card_offset; int i, win; + struct pcic_softc *sc = (struct pcic_softc *)(h->ph_parent); win = -1; for (i = 0; i < (sizeof(mem_map_index) / sizeof(mem_map_index[0])); @@ -964,7 +996,7 @@ pcic_chip_mem_map(pch, kind, card_addr, size, pcmhp, offsetp, windowp) /* XXX this is pretty gross */ - if (h->sc->memt != pcmhp->memt) + if (sc->memt != pcmhp->memt) panic("pcic_chip_mem_map memt is bogus"); busaddr = pcmhp->addr; @@ -1032,13 +1064,14 @@ pcic_chip_io_alloc(pch, start, size, align, pcihp) bus_space_handle_t ioh; bus_addr_t ioaddr, beg, fin; int flags = 0; + struct pcic_softc *sc = (struct pcic_softc *)(h->ph_parent); struct pcic_ranges *range; /* * Allocate some arbitrary I/O space. */ - iot = h->sc->iot; + iot = sc->iot; if (start) { ioaddr = start; @@ -1046,28 +1079,28 @@ pcic_chip_io_alloc(pch, start, size, align, pcihp) return (1); DPRINTF(("pcic_chip_io_alloc map port %lx+%lx\n", (u_long)ioaddr, (u_long)size)); - } else if (h->sc->ranges) { + } else if (sc->ranges) { flags |= PCMCIA_IO_ALLOCATED; /* * In this case, we know the "size" and "align" that * we want. So we need to start walking down - * h->sc->ranges, searching for a similar space that + * sc->ranges, searching for a similar space that * is (1) large enough for the size and alignment * (2) then we need to try to allocate * (3) if it fails to allocate, we try next range. * * We must also check that the start/size of each * allocation we are about to do is within the bounds - * of "h->sc->iobase" and "h->sc->iosize". + * of "sc->iobase" and "sc->iosize". * (Some pcmcia controllers handle a 12 bits of addressing, * but we want to use the same range structure) */ - for (range = h->sc->ranges; range->start; range++) { + for (range = sc->ranges; range->start; range++) { /* Potentially trim the range because of bounds. */ - beg = max(range->start, h->sc->iobase); + beg = max(range->start, sc->iobase); fin = min(range->start + range->len, - h->sc->iobase + h->sc->iosize); + sc->iobase + sc->iosize); /* Short-circuit easy cases. */ if (fin < beg || fin - beg < size) @@ -1090,8 +1123,8 @@ pcic_chip_io_alloc(pch, start, size, align, pcihp) } else { flags |= PCMCIA_IO_ALLOCATED; - if (bus_space_alloc(iot, h->sc->iobase, - h->sc->iobase + h->sc->iosize, size, align, 0, 0, + if (bus_space_alloc(iot, sc->iobase, + sc->iobase + sc->iosize, size, align, 0, 0, &ioaddr, &ioh)) return (1); DPRINTF(("pcic_chip_io_alloc alloc port %lx+%lx\n", @@ -1211,6 +1244,7 @@ pcic_chip_io_map(pch, width, offset, size, pcihp, windowp) #ifdef PCICDEBUG static char *width_names[] = { "auto", "io8", "io16" }; #endif + struct pcic_softc *sc = (struct pcic_softc *)(h->ph_parent); /* XXX Sanity check offset/size. */ @@ -1230,7 +1264,7 @@ pcic_chip_io_map(pch, width, offset, size, pcihp, windowp) /* XXX this is pretty gross */ - if (h->sc->iot != pcihp->iot) + if (sc->iot != pcihp->iot) panic("pcic_chip_io_map iot is bogus"); DPRINTF(("pcic_chip_io_map window %d %s port %lx+%lx\n", @@ -1369,7 +1403,7 @@ pcic_chip_socket_enable(pch) pcic_write(h, PCIC_INTR, reg); DPRINTF(("%s: pcic_chip_socket_enable %02x cardtype %s %02x\n", - h->sc->dev.dv_xname, h->sock, + h->ph_parent->dv_xname, h->sock, ((cardtype == PCMCIA_IFTYPE_IO) ? "io" : "mem"), reg)); /* reinstall all the memory and io mappings */ @@ -1400,3 +1434,28 @@ pcic_chip_socket_disable(pch) */ delay(300 * 1000); } + +u_int8_t +st_pcic_read(h, idx) + struct pcic_handle *h; + int idx; +{ + if (idx != -1) { + bus_space_write_1(h->ph_bus_t, h->ph_bus_h, PCIC_REG_INDEX, h->sock + idx); + } + + return bus_space_read_1(h->ph_bus_t, h->ph_bus_h, PCIC_REG_DATA); +} + +void +st_pcic_write(h, idx, data) + struct pcic_handle *h; + int idx; + u_int8_t data; +{ + if (idx != -1) { + bus_space_write_1(h->ph_bus_t, h->ph_bus_h, PCIC_REG_INDEX, h->sock + idx); + } + + bus_space_write_1(h->ph_bus_t, h->ph_bus_h, PCIC_REG_DATA, data); +} diff --git a/sys/dev/ic/i82365var.h b/sys/dev/ic/i82365var.h index 81af7b771f2..72792836c86 100644 --- a/sys/dev/ic/i82365var.h +++ b/sys/dev/ic/i82365var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: i82365var.h,v 1.5 1999/08/08 01:07:02 niklas Exp $ */ +/* $OpenBSD: i82365var.h,v 1.6 2000/04/08 05:50:50 aaron Exp $ */ /* $NetBSD: i82365var.h,v 1.4 1998/05/23 18:32:29 matt Exp $ */ /* @@ -49,7 +49,12 @@ struct pcic_event { #define PCIC_EVENT_REMOVAL 1 struct pcic_handle { - struct pcic_softc *sc; + struct device *ph_parent; + bus_space_tag_t ph_bus_t; + bus_space_handle_t ph_bus_h; + u_int8_t (*ph_read) __P((struct pcic_handle *, int)); + void (*ph_write) __P((struct pcic_handle *, int, u_int8_t)); + int vendor; int sock; int flags; @@ -146,9 +151,6 @@ void pcic_attach __P((struct pcic_softc *)); void pcic_attach_sockets __P((struct pcic_softc *)); int pcic_intr __P((void *arg)); -static inline int pcic_read __P((struct pcic_handle *, int)); -static inline void pcic_write __P((struct pcic_handle *, int, int)); - int pcic_chip_mem_alloc __P((pcmcia_chipset_handle_t, bus_size_t, struct pcmcia_mem_handle *)); void pcic_chip_mem_free __P((pcmcia_chipset_handle_t, @@ -168,29 +170,8 @@ void pcic_chip_io_unmap __P((pcmcia_chipset_handle_t, int)); void pcic_chip_socket_enable __P((pcmcia_chipset_handle_t)); void pcic_chip_socket_disable __P((pcmcia_chipset_handle_t)); -static __inline int pcic_read __P((struct pcic_handle *, int)); -static __inline int -pcic_read(h, idx) - struct pcic_handle *h; - int idx; -{ - if (idx != -1) - bus_space_write_1(h->sc->iot, h->sc->ioh, PCIC_REG_INDEX, - h->sock + idx); - return (bus_space_read_1(h->sc->iot, h->sc->ioh, PCIC_REG_DATA)); -} - -static __inline void pcic_write __P((struct pcic_handle *, int, int)); -static __inline void -pcic_write(h, idx, data) - struct pcic_handle *h; - int idx; - int data; -{ - if (idx != -1) - bus_space_write_1(h->sc->iot, h->sc->ioh, PCIC_REG_INDEX, - h->sock + idx); - if (data != -1) - bus_space_write_1(h->sc->iot, h->sc->ioh, PCIC_REG_DATA, - (data)); -} +#define pcic_read(h, idx) \ + (*(h)->ph_read)((h), (idx)) + +#define pcic_write(h, idx, data) \ + (*(h)->ph_write)((h), (idx), (data)) diff --git a/sys/dev/ic/xl.c b/sys/dev/ic/xl.c new file mode 100644 index 00000000000..5ea3fa6a541 --- /dev/null +++ b/sys/dev/ic/xl.c @@ -0,0 +1,2601 @@ +/* $OpenBSD: xl.c,v 1.1 2000/04/08 05:50:50 aaron Exp $ */ + +/* + * Copyright (c) 1997, 1998, 1999 + * Bill Paul . 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 Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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: if_xl.c,v 1.72 2000/01/09 21:12:59 wpaul Exp $ + */ + +/* + * 3Com 3c90x Etherlink XL PCI NIC driver + * + * Supports the 3Com "boomerang", "cyclone", and "hurricane" PCI + * bus-master chips (3c90x cards and embedded controllers) including + * the following: + * + * 3Com 3c900-TPO 10Mbps/RJ-45 + * 3Com 3c900-COMBO 10Mbps/RJ-45,AUI,BNC + * 3Com 3c905-TX 10/100Mbps/RJ-45 + * 3Com 3c905-T4 10/100Mbps/RJ-45 + * 3Com 3c900B-TPO 10Mbps/RJ-45 + * 3Com 3c900B-COMBO 10Mbps/RJ-45,AUI,BNC + * 3Com 3c900B-TPC 10Mbps/RJ-45,BNC + * 3Com 3c900B-FL 10Mbps/Fiber-optic + * 3Com 3c905B-COMBO 10/100Mbps/RJ-45,AUI,BNC + * 3Com 3c905B-TX 10/100Mbps/RJ-45 + * 3Com 3c900-FL/FX 10/100Mbps/Fiber-optic + * 3Com 3c905C-TX 10/100Mbps/RJ-45 (Tornado ASIC) + * 3Com 3c450-TX 10/100Mbps/RJ-45 (Tornado ASIC) + * 3Com 3c980-TX 10/100Mbps server adapter (Hurricane ASIC) + * 3Com 3c980C-TX 10/100Mbps server adapter (Tornado ASIC) + * 3Com 3CCFE575CT 10/100Mbps LAN CardBus PC Card + * 3Com 3cSOHO100-TX 10/100Mbps/RJ-45 (Hurricane ASIC) + * Dell Optiplex GX1 on-board 3c918 10/100Mbps/RJ-45 + * Dell on-board 3c920 10/100Mbps/RJ-45 + * Dell Precision on-board 3c905B 10/100Mbps/RJ-45 + * Dell Latitude laptop docking station embedded 3c905-TX + * + * Written by Bill Paul + * Electrical Engineering Department + * Columbia University, New York City + */ + +/* + * The 3c90x series chips use a bus-master DMA interface for transfering + * packets to and from the controller chip. Some of the "vortex" cards + * (3c59x) also supported a bus master mode, however for those chips + * you could only DMA packets to/from a contiguous memory buffer. For + * transmission this would mean copying the contents of the queued mbuf + * chain into a an mbuf cluster and then DMAing the cluster. This extra + * copy would sort of defeat the purpose of the bus master support for + * any packet that doesn't fit into a single mbuf. + * + * By contrast, the 3c90x cards support a fragment-based bus master + * mode where mbuf chains can be encapsulated using TX descriptors. + * This is similar to other PCI chips such as the Texas Instruments + * ThunderLAN and the Intel 82557/82558. + * + * The "vortex" driver (if_vx.c) happens to work for the "boomerang" + * bus master chips because they maintain the old PIO interface for + * backwards compatibility, but starting with the 3c905B and the + * "cyclone" chips, the compatibility interface has been dropped. + * Since using bus master DMA is a big win, we use this driver to + * support the PCI "boomerang" chips even though they work with the + * "vortex" driver in order to obtain better performance. + * + * This driver is in the /sys/pci directory because it only supports + * PCI-based NICs. + */ + +#include "bpfilter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* only for declaration of wakeup() used by vm.h */ +#include + +#include +#include +#include +#include + +#ifdef INET +#include +#include +#include +#include +#include +#endif + +#include +#include + +#include +#include +#include + +#if NBPFILTER > 0 +#include +#endif + +#include /* for vtophys */ +#include /* for vtophys */ + +#include + +int xl_newbuf __P((struct xl_softc *, struct xl_chain_onefrag *)); +void xl_stats_update __P((void *)); +int xl_encap __P((struct xl_softc *, struct xl_chain *, + struct mbuf * )); +int xl_encap_90xB __P((struct xl_softc *, struct xl_chain *, + struct mbuf * )); +void xl_rxeof __P((struct xl_softc *)); +int xl_rx_resync __P((struct xl_softc *)); +void xl_txeof __P((struct xl_softc *)); +void xl_txeof_90xB __P((struct xl_softc *)); +void xl_txeoc __P((struct xl_softc *)); +int xl_intr __P((void *)); +void xl_start __P((struct ifnet *)); +void xl_start_90xB __P((struct ifnet *)); +int xl_ioctl __P((struct ifnet *, u_long, caddr_t)); +void xl_init __P((void *)); +void xl_stop __P((struct xl_softc *)); +void xl_watchdog __P((struct ifnet *)); +void xl_shutdown __P((void *)); +int xl_ifmedia_upd __P((struct ifnet *)); +void xl_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); + +int xl_eeprom_wait __P((struct xl_softc *)); +int xl_read_eeprom __P((struct xl_softc *, caddr_t, int, int, int)); +void xl_mii_sync __P((struct xl_softc *)); +void xl_mii_send __P((struct xl_softc *, u_int32_t, int)); +int xl_mii_readreg __P((struct xl_softc *, struct xl_mii_frame *)); +int xl_mii_writereg __P((struct xl_softc *, struct xl_mii_frame *)); + +void xl_setcfg __P((struct xl_softc *)); +void xl_setmode __P((struct xl_softc *, int)); +u_int8_t xl_calchash __P((caddr_t)); +void xl_setmulti __P((struct xl_softc *)); +void xl_setmulti_hash __P((struct xl_softc *)); +void xl_reset __P((struct xl_softc *, int)); +int xl_list_rx_init __P((struct xl_softc *)); +int xl_list_tx_init __P((struct xl_softc *)); +int xl_list_tx_init_90xB __P((struct xl_softc *)); +void xl_wait __P((struct xl_softc *)); +void xl_mediacheck __P((struct xl_softc *)); +void xl_choose_xcvr __P((struct xl_softc *, int)); +#ifdef notdef +void xl_testpacket __P((struct xl_softc *)); +#endif + +int xl_miibus_readreg __P((struct device *, int, int)); +void xl_miibus_writereg __P((struct device *, int, int, int)); +void xl_miibus_statchg __P((struct device *)); + +/* + * Murphy's law says that it's possible the chip can wedge and + * the 'command in progress' bit may never clear. Hence, we wait + * only a finite amount of time to avoid getting caught in an + * infinite loop. Normally this delay routine would be a macro, + * but it isn't called during normal operation so we can afford + * to make it a function. + */ +void xl_wait(sc) + struct xl_softc *sc; +{ + register int i; + + for (i = 0; i < XL_TIMEOUT; i++) { + if (!(CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY)) + break; + } + +#ifdef DIAGNOSTIC + if (i == XL_TIMEOUT) + printf("xl%d: command never completed!\n", sc->xl_unit); +#endif + + return; +} + +/* + * MII access routines are provided for adapters with external + * PHYs (3c905-TX, 3c905-T4, 3c905B-T4) and those with built-in + * autoneg logic that's faked up to look like a PHY (3c905B-TX). + * Note: if you don't perform the MDIO operations just right, + * it's possible to end up with code that works correctly with + * some chips/CPUs/processor speeds/bus speeds/etc but not + * with others. + */ +#define MII_SET(x) \ + CSR_WRITE_2(sc, XL_W4_PHY_MGMT, \ + CSR_READ_2(sc, XL_W4_PHY_MGMT) | x) + +#define MII_CLR(x) \ + CSR_WRITE_2(sc, XL_W4_PHY_MGMT, \ + CSR_READ_2(sc, XL_W4_PHY_MGMT) & ~x) + +/* + * Sync the PHYs by setting data bit and strobing the clock 32 times. + */ +void xl_mii_sync(sc) + struct xl_softc *sc; +{ + register int i; + + XL_SEL_WIN(4); + MII_SET(XL_MII_DIR|XL_MII_DATA); + + for (i = 0; i < 32; i++) { + MII_SET(XL_MII_CLK); + DELAY(1); + MII_CLR(XL_MII_CLK); + DELAY(1); + } + + return; +} + +/* + * Clock a series of bits through the MII. + */ +void xl_mii_send(sc, bits, cnt) + struct xl_softc *sc; + u_int32_t bits; + int cnt; +{ + int i; + + XL_SEL_WIN(4); + MII_CLR(XL_MII_CLK); + + for (i = (0x1 << (cnt - 1)); i; i >>= 1) { + if (bits & i) { + MII_SET(XL_MII_DATA); + } else { + MII_CLR(XL_MII_DATA); + } + DELAY(1); + MII_CLR(XL_MII_CLK); + DELAY(1); + MII_SET(XL_MII_CLK); + } +} + +/* + * Read an PHY register through the MII. + */ +int xl_mii_readreg(sc, frame) + struct xl_softc *sc; + struct xl_mii_frame *frame; + +{ + int i, ack, s; + + s = splimp(); + + /* + * Set up frame for RX. + */ + frame->mii_stdelim = XL_MII_STARTDELIM; + frame->mii_opcode = XL_MII_READOP; + frame->mii_turnaround = 0; + frame->mii_data = 0; + + /* + * Select register window 4. + */ + + XL_SEL_WIN(4); + + CSR_WRITE_2(sc, XL_W4_PHY_MGMT, 0); + /* + * Turn on data xmit. + */ + MII_SET(XL_MII_DIR); + + xl_mii_sync(sc); + + /* + * Send command/address info. + */ + xl_mii_send(sc, frame->mii_stdelim, 2); + xl_mii_send(sc, frame->mii_opcode, 2); + xl_mii_send(sc, frame->mii_phyaddr, 5); + xl_mii_send(sc, frame->mii_regaddr, 5); + + /* Idle bit */ + MII_CLR((XL_MII_CLK|XL_MII_DATA)); + DELAY(1); + MII_SET(XL_MII_CLK); + DELAY(1); + + /* Turn off xmit. */ + MII_CLR(XL_MII_DIR); + + /* Check for ack */ + MII_CLR(XL_MII_CLK); + DELAY(1); + MII_SET(XL_MII_CLK); + DELAY(1); + ack = CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA; + + /* + * Now try reading data bits. If the ack failed, we still + * need to clock through 16 cycles to keep the PHY(s) in sync. + */ + if (ack) { + for(i = 0; i < 16; i++) { + MII_CLR(XL_MII_CLK); + DELAY(1); + MII_SET(XL_MII_CLK); + DELAY(1); + } + goto fail; + } + + for (i = 0x8000; i; i >>= 1) { + MII_CLR(XL_MII_CLK); + DELAY(1); + if (!ack) { + if (CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA) + frame->mii_data |= i; + DELAY(1); + } + MII_SET(XL_MII_CLK); + DELAY(1); + } + +fail: + + MII_CLR(XL_MII_CLK); + DELAY(1); + MII_SET(XL_MII_CLK); + DELAY(1); + + splx(s); + + if (ack) + return(1); + return(0); +} + +/* + * Write to a PHY register through the MII. + */ +int xl_mii_writereg(sc, frame) + struct xl_softc *sc; + struct xl_mii_frame *frame; + +{ + int s; + + s = splimp(); + /* + * Set up frame for TX. + */ + + frame->mii_stdelim = XL_MII_STARTDELIM; + frame->mii_opcode = XL_MII_WRITEOP; + frame->mii_turnaround = XL_MII_TURNAROUND; + + /* + * Select the window 4. + */ + XL_SEL_WIN(4); + + /* + * Turn on data output. + */ + MII_SET(XL_MII_DIR); + + xl_mii_sync(sc); + + xl_mii_send(sc, frame->mii_stdelim, 2); + xl_mii_send(sc, frame->mii_opcode, 2); + xl_mii_send(sc, frame->mii_phyaddr, 5); + xl_mii_send(sc, frame->mii_regaddr, 5); + xl_mii_send(sc, frame->mii_turnaround, 2); + xl_mii_send(sc, frame->mii_data, 16); + + /* Idle bit. */ + MII_SET(XL_MII_CLK); + DELAY(1); + MII_CLR(XL_MII_CLK); + DELAY(1); + + /* + * Turn off xmit. + */ + MII_CLR(XL_MII_DIR); + + splx(s); + + return(0); +} + +int +xl_miibus_readreg(self, phy, reg) + struct device *self; + int phy, reg; +{ + struct xl_softc *sc = (struct xl_softc *)self; + struct xl_mii_frame frame; + + if (sc->xl_bustype != XL_BUS_CARDBUS && phy != 24) + return (0); + + bzero((char *)&frame, sizeof(frame)); + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + xl_mii_readreg(sc, &frame); + + return(frame.mii_data); +} + +void +xl_miibus_writereg(self, phy, reg, data) + struct device *self; + int phy, reg, data; +{ + struct xl_softc *sc = (struct xl_softc *)self; + struct xl_mii_frame frame; + + if (sc->xl_bustype != XL_BUS_CARDBUS && phy != 24) + return; + + bzero((char *)&frame, sizeof(frame)); + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + frame.mii_data = data; + + xl_mii_writereg(sc, &frame); +} + +void +xl_miibus_statchg(self) + struct device *self; +{ + struct xl_softc *sc = (struct xl_softc *)self; + + xl_setcfg(sc); + + XL_SEL_WIN(3); + if ((sc->sc_mii.mii_media_active & IFM_GMASK) == IFM_FDX) + CSR_WRITE_1(sc, XL_W3_MAC_CTRL, XL_MACCTRL_DUPLEX); + else + CSR_WRITE_1(sc, XL_W3_MAC_CTRL, + (CSR_READ_1(sc, XL_W3_MAC_CTRL) & ~XL_MACCTRL_DUPLEX)); +} + +/* + * The EEPROM is slow: give it time to come ready after issuing + * it a command. + */ +int xl_eeprom_wait(sc) + struct xl_softc *sc; +{ + int i; + + for (i = 0; i < 100; i++) { + if (CSR_READ_2(sc, XL_W0_EE_CMD) & XL_EE_BUSY) + DELAY(162); + else + break; + } + + if (i == 100) { + printf("xl%d: eeprom failed to come ready\n", sc->xl_unit); + return(1); + } + + return(0); +} + +/* + * Read a sequence of words from the EEPROM. Note that ethernet address + * data is stored in the EEPROM in network byte order. + */ +int xl_read_eeprom(sc, dest, off, cnt, swap) + struct xl_softc *sc; + caddr_t dest; + int off; + int cnt; + int swap; +{ + int err = 0, i; + u_int16_t word = 0, *ptr; + + XL_SEL_WIN(0); + + if (xl_eeprom_wait(sc)) + return(1); + + for (i = 0; i < cnt; i++) { + switch (sc->xl_bustype) { + case XL_BUS_PCI: + CSR_WRITE_2(sc, XL_W0_EE_CMD, XL_EE_READ | (off + i)); + break; + case XL_BUS_CARDBUS: + CSR_WRITE_2(sc, XL_W0_EE_CMD, 0x230 + (off + i)); + break; + } + err = xl_eeprom_wait(sc); + if (err) + break; + word = CSR_READ_2(sc, XL_W0_EE_DATA); + ptr = (u_int16_t *)(dest + (i * 2)); + if (swap) + *ptr = ntohs(word); + else + *ptr = word; + } + + return(err ? 1 : 0); +} + +/* + * This routine is taken from the 3Com Etherlink XL manual, + * page 10-7. It calculates a CRC of the supplied multicast + * group address and returns the lower 8 bits, which are used + * as the multicast filter position. + * Note: the 3c905B currently only supports a 64-bit hash table, + * which means we really only need 6 bits, but the manual indicates + * that future chip revisions will have a 256-bit hash table, + * hence the routine is set up to calculate 8 bits of position + * info in case we need it some day. + * Note II, The Sequel: _CURRENT_ versions of the 3c905B have a + * 256 bit hash table. This means we have to use all 8 bits regardless. + * On older cards, the upper 2 bits will be ignored. Grrrr.... + */ +u_int8_t xl_calchash(addr) + caddr_t addr; +{ + u_int32_t crc, carry; + int i, j; + u_int8_t c; + + /* Compute CRC for the address value. */ + crc = 0xFFFFFFFF; /* initial value */ + + for (i = 0; i < 6; i++) { + c = *(addr + i); + for (j = 0; j < 8; j++) { + carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); + crc <<= 1; + c >>= 1; + if (carry) + crc = (crc ^ 0x04c11db6) | carry; + } + } + + /* return the filter bit position */ + return(crc & 0x000000FF); +} + +/* + * NICs older than the 3c905B have only one multicast option, which + * is to enable reception of all multicast frames. + */ +void xl_setmulti(sc) + struct xl_softc *sc; +{ + struct ifnet *ifp; + struct arpcom *ac = &sc->arpcom; + struct ether_multi *enm; + struct ether_multistep step; + u_int8_t rxfilt; + int mcnt = 0; + + ifp = &sc->arpcom.ac_if; + + XL_SEL_WIN(5); + rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER); + + if (ifp->if_flags & IFF_ALLMULTI) { + rxfilt |= XL_RXFILTER_ALLMULTI; + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); + return; + } + + ETHER_FIRST_MULTI(step, ac, enm); + while (enm != NULL) { + mcnt++; + ETHER_NEXT_MULTI(step, enm); + } + + if (mcnt) + rxfilt |= XL_RXFILTER_ALLMULTI; + else + rxfilt &= ~XL_RXFILTER_ALLMULTI; + + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); + + return; +} + +/* + * 3c905B adapters have a hash filter that we can program. + */ +void xl_setmulti_hash(sc) + struct xl_softc *sc; +{ + struct ifnet *ifp; + int h = 0, i; + struct arpcom *ac = &sc->arpcom; + struct ether_multi *enm; + struct ether_multistep step; + u_int8_t rxfilt; + int mcnt = 0; + + ifp = &sc->arpcom.ac_if; + + XL_SEL_WIN(5); + rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER); + + if (ifp->if_flags & IFF_ALLMULTI) { +allmulti: + rxfilt |= XL_RXFILTER_ALLMULTI; + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); + return; + } else + rxfilt &= ~XL_RXFILTER_ALLMULTI; + + + /* first, zot all the existing hash bits */ + for (i = 0; i < XL_HASHFILT_SIZE; i++) + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_HASH|i); + + /* now program new ones */ + ETHER_FIRST_MULTI(step, ac, enm); + while (enm != NULL) { + if (bcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) { + ifp->if_flags |= IFF_ALLMULTI; + goto allmulti; + } + h = xl_calchash(enm->enm_addrlo); + mcnt++; + ETHER_NEXT_MULTI(step, enm); + } + + if (mcnt) + rxfilt |= XL_RXFILTER_MULTIHASH; + else + rxfilt &= ~XL_RXFILTER_MULTIHASH; + + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); + + return; +} + +#ifdef notdef +void xl_testpacket(sc) + struct xl_softc *sc; +{ + struct mbuf *m; + struct ifnet *ifp; + + ifp = &sc->arpcom.ac_if; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + + if (m == NULL) + return; + + bcopy(&sc->arpcom.ac_enaddr, + mtod(m, struct ether_header *)->ether_dhost, ETHER_ADDR_LEN); + bcopy(&sc->arpcom.ac_enaddr, + mtod(m, struct ether_header *)->ether_shost, ETHER_ADDR_LEN); + mtod(m, struct ether_header *)->ether_type = htons(3); + mtod(m, unsigned char *)[14] = 0; + mtod(m, unsigned char *)[15] = 0; + mtod(m, unsigned char *)[16] = 0xE3; + m->m_len = m->m_pkthdr.len = sizeof(struct ether_header) + 3; + IF_ENQUEUE(&ifp->if_snd, m); + xl_start(ifp); + + return; +} +#endif + +void xl_setcfg(sc) + struct xl_softc *sc; +{ + u_int32_t icfg; + + XL_SEL_WIN(3); + icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG); + icfg &= ~XL_ICFG_CONNECTOR_MASK; + if (sc->xl_media & XL_MEDIAOPT_MII || + sc->xl_media & XL_MEDIAOPT_BT4) + icfg |= (XL_XCVR_MII << XL_ICFG_CONNECTOR_BITS); + if (sc->xl_media & XL_MEDIAOPT_BTX) + icfg |= (XL_XCVR_AUTO << XL_ICFG_CONNECTOR_BITS); + + CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg); + CSR_WRITE_4(sc, XL_COMMAND, XL_CMD_COAX_STOP); +} + +void xl_setmode(sc, media) + struct xl_softc *sc; + int media; +{ + u_int32_t icfg; + u_int16_t mediastat; + + printf("xl%d: selecting ", sc->xl_unit); + + XL_SEL_WIN(4); + mediastat = CSR_READ_2(sc, XL_W4_MEDIA_STATUS); + XL_SEL_WIN(3); + icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG); + + if (sc->xl_media & XL_MEDIAOPT_BT) { + if (IFM_SUBTYPE(media) == IFM_10_T) { + printf("10baseT transceiver, "); + sc->xl_xcvr = XL_XCVR_10BT; + icfg &= ~XL_ICFG_CONNECTOR_MASK; + icfg |= (XL_XCVR_10BT << XL_ICFG_CONNECTOR_BITS); + mediastat |= XL_MEDIASTAT_LINKBEAT| + XL_MEDIASTAT_JABGUARD; + mediastat &= ~XL_MEDIASTAT_SQEENB; + } + } + + if (sc->xl_media & XL_MEDIAOPT_BFX) { + if (IFM_SUBTYPE(media) == IFM_100_FX) { + printf("100baseFX port, "); + sc->xl_xcvr = XL_XCVR_100BFX; + icfg &= ~XL_ICFG_CONNECTOR_MASK; + icfg |= (XL_XCVR_100BFX << XL_ICFG_CONNECTOR_BITS); + mediastat |= XL_MEDIASTAT_LINKBEAT; + mediastat &= ~XL_MEDIASTAT_SQEENB; + } + } + + if (sc->xl_media & (XL_MEDIAOPT_AUI|XL_MEDIAOPT_10FL)) { + if (IFM_SUBTYPE(media) == IFM_10_5) { + printf("AUI port, "); + sc->xl_xcvr = XL_XCVR_AUI; + icfg &= ~XL_ICFG_CONNECTOR_MASK; + icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS); + mediastat &= ~(XL_MEDIASTAT_LINKBEAT| + XL_MEDIASTAT_JABGUARD); + mediastat |= ~XL_MEDIASTAT_SQEENB; + } + if (IFM_SUBTYPE(media) == IFM_10_FL) { + printf("10baseFL transceiver, "); + sc->xl_xcvr = XL_XCVR_AUI; + icfg &= ~XL_ICFG_CONNECTOR_MASK; + icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS); + mediastat &= ~(XL_MEDIASTAT_LINKBEAT| + XL_MEDIASTAT_JABGUARD); + mediastat |= ~XL_MEDIASTAT_SQEENB; + } + } + + if (sc->xl_media & XL_MEDIAOPT_BNC) { + if (IFM_SUBTYPE(media) == IFM_10_2) { + printf("BNC port, "); + sc->xl_xcvr = XL_XCVR_COAX; + icfg &= ~XL_ICFG_CONNECTOR_MASK; + icfg |= (XL_XCVR_COAX << XL_ICFG_CONNECTOR_BITS); + mediastat &= ~(XL_MEDIASTAT_LINKBEAT| + XL_MEDIASTAT_JABGUARD| + XL_MEDIASTAT_SQEENB); + } + } + + if ((media & IFM_GMASK) == IFM_FDX || + IFM_SUBTYPE(media) == IFM_100_FX) { + printf("full duplex\n"); + XL_SEL_WIN(3); + CSR_WRITE_1(sc, XL_W3_MAC_CTRL, XL_MACCTRL_DUPLEX); + } else { + printf("half duplex\n"); + XL_SEL_WIN(3); + CSR_WRITE_1(sc, XL_W3_MAC_CTRL, + (CSR_READ_1(sc, XL_W3_MAC_CTRL) & ~XL_MACCTRL_DUPLEX)); + } + + if (IFM_SUBTYPE(media) == IFM_10_2) + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START); + else + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); + CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg); + XL_SEL_WIN(4); + CSR_WRITE_2(sc, XL_W4_MEDIA_STATUS, mediastat); + DELAY(800); + XL_SEL_WIN(7); +} + +void xl_reset(sc, hard) + struct xl_softc *sc; +{ + register int i; + + XL_SEL_WIN(0); + if (hard) + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RESET); + else + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RESET | 0x0010); + xl_wait(sc); + + for (i = 0; i < XL_TIMEOUT; i++) { + DELAY(10); + if (!(CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY)) + break; + } + + DELAY(100000); + + /* Reset TX and RX. */ + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET); + xl_wait(sc); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); + xl_wait(sc); + + /* Wait a little while for the chip to get its brains in order. */ + DELAY(100000); + return; +} + +/* + * This routine is a kludge to work around possible hardware faults + * or manufacturing defects that can cause the media options register + * (or reset options register, as it's called for the first generation + * 3c90x adapters) to return an incorrect result. I have encountered + * one Dell Latitude laptop docking station with an integrated 3c905-TX + * which doesn't have any of the 'mediaopt' bits set. This screws up + * the attach routine pretty badly because it doesn't know what media + * to look for. If we find ourselves in this predicament, this routine + * will try to guess the media options values and warn the user of a + * possible manufacturing defect with his adapter/system/whatever. + */ +void xl_mediacheck(sc) + struct xl_softc *sc; +{ + /* + * If some of the media options bits are set, assume they are + * correct. If not, try to figure it out down below. + * XXX I should check for 10baseFL, but I don't have an adapter + * to test with. + */ + if (sc->xl_media & (XL_MEDIAOPT_MASK & ~XL_MEDIAOPT_VCO)) { + /* + * Check the XCVR value. If it's not in the normal range + * of values, we need to fake it up here. + */ + if (sc->xl_xcvr <= XL_XCVR_AUTO) + return; + else { + printf("xl%d: bogus xcvr value " + "in EEPROM (%x)\n", sc->xl_unit, sc->xl_xcvr); + printf("xl%d: choosing new default based " + "on card type\n", sc->xl_unit); + } + } else { + if (sc->xl_type == XL_TYPE_905B && + sc->xl_media & XL_MEDIAOPT_10FL) + return; + printf("xl%d: WARNING: no media options bits set in " + "the media options register!!\n", sc->xl_unit); + printf("xl%d: this could be a manufacturing defect in " + "your adapter or system\n", sc->xl_unit); + printf("xl%d: attempting to guess media type; you " + "should probably consult your vendor\n", sc->xl_unit); + } + + xl_choose_xcvr(sc, 1); +} + +void xl_choose_xcvr(sc, verbose) + struct xl_softc *sc; + int verbose; +{ + u_int16_t devid; + + /* + * Read the device ID from the EEPROM. + * This is what's loaded into the PCI device ID register, so it has + * to be correct otherwise we wouldn't have gotten this far. + */ + xl_read_eeprom(sc, (caddr_t)&devid, XL_EE_PRODID, 1, 0); + + switch(devid) { + case TC_DEVICEID_BOOMERANG_10BT: /* 3c900-TPO */ + case TC_DEVICEID_KRAKATOA_10BT: /* 3c900B-TPO */ + sc->xl_media = XL_MEDIAOPT_BT; + sc->xl_xcvr = XL_XCVR_10BT; + if (verbose) + printf("xl%d: guessing 10BaseT transceiver\n", + sc->xl_unit); + break; + case TC_DEVICEID_BOOMERANG_10BT_COMBO: /* 3c900-COMBO */ + case TC_DEVICEID_KRAKATOA_10BT_COMBO: /* 3c900B-COMBO */ + sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI; + sc->xl_xcvr = XL_XCVR_10BT; + if (verbose) + printf("xl%d: guessing COMBO (AUI/BNC/TP)\n", + sc->xl_unit); + break; + case TC_DEVICEID_KRAKATOA_10BT_TPC: /* 3c900B-TPC */ + sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC; + sc->xl_xcvr = XL_XCVR_10BT; + if (verbose) + printf("xl%d: guessing TPC (BNC/TP)\n", sc->xl_unit); + break; + case TC_DEVICEID_CYCLONE_10FL: /* 3c900B-FL */ + sc->xl_media = XL_MEDIAOPT_10FL; + sc->xl_xcvr = XL_XCVR_AUI; + if (verbose) + printf("xl%d: guessing 10baseFL\n", sc->xl_unit); + break; + case TC_DEVICEID_BOOMERANG_10_100BT: /* 3c905-TX */ + sc->xl_media = XL_MEDIAOPT_MII; + sc->xl_xcvr = XL_XCVR_MII; + if (verbose) + printf("xl%d: guessing MII\n", sc->xl_unit); + break; + case TC_DEVICEID_BOOMERANG_100BT4: /* 3c905-T4 */ + case TC_DEVICEID_CYCLONE_10_100BT4: /* 3c905B-T4 */ + sc->xl_media = XL_MEDIAOPT_BT4; + sc->xl_xcvr = XL_XCVR_MII; + if (verbose) + printf("xl%d: guessing 100BaseT4/MII\n", sc->xl_unit); + break; + case TC_DEVICEID_HURRICANE_10_100BT: /* 3c905B-TX */ + case TC_DEVICEID_HURRICANE_10_100BT_SERV:/* 3c980-TX */ + case TC_DEVICEID_TORNADO_10_100BT_SERV: /* 3c980C-TX */ + case TC_DEVICEID_HURRICANE_SOHO100TX: /* 3cSOHO100-TX */ + case TC_DEVICEID_TORNADO_10_100BT: /* 3c905C-TX */ + case TC_DEVICEID_TORNADO_HOMECONNECT: /* 3c450-TX */ + sc->xl_media = XL_MEDIAOPT_BTX; + sc->xl_xcvr = XL_XCVR_AUTO; + if (verbose) + printf("xl%d: guessing 10/100 internal\n", + sc->xl_unit); + break; + case TC_DEVICEID_CYCLONE_10_100_COMBO: /* 3c905B-COMBO */ + sc->xl_media = XL_MEDIAOPT_BTX|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI; + sc->xl_xcvr = XL_XCVR_AUTO; + if (verbose) + printf("xl%d: guessing 10/100 plus BNC/AUI\n", + sc->xl_unit); + break; + case TC_DEVICEID_3CCFE575CT_CARDBUS: + sc->xl_media = XL_MEDIAOPT_MII; + sc->xl_xcvr = XL_XCVR_MII; + break; + default: + printf("xl%d: unknown device ID: %x -- " + "defaulting to 10baseT\n", sc->xl_unit, devid); + sc->xl_media = XL_MEDIAOPT_BT; + break; + } + + return; +} + +/* + * Initialize the transmit descriptors. + */ +int xl_list_tx_init(sc) + struct xl_softc *sc; +{ + struct xl_chain_data *cd; + struct xl_list_data *ld; + int i; + + cd = &sc->xl_cdata; + ld = sc->xl_ldata; + for (i = 0; i < XL_TX_LIST_CNT; i++) { + cd->xl_tx_chain[i].xl_ptr = &ld->xl_tx_list[i]; + if (i == (XL_TX_LIST_CNT - 1)) + cd->xl_tx_chain[i].xl_next = NULL; + else + cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[i + 1]; + } + + cd->xl_tx_free = &cd->xl_tx_chain[0]; + cd->xl_tx_tail = cd->xl_tx_head = NULL; + + return(0); +} + +/* + * Initialize the transmit desriptors. + */ +int +xl_list_tx_init_90xB(sc) + struct xl_softc *sc; +{ + struct xl_chain_data *cd; + struct xl_list_data *ld; + int i; + + cd = &sc->xl_cdata; + ld = sc->xl_ldata; + for (i = 0; i < XL_TX_LIST_CNT; i++) { + cd->xl_tx_chain[i].xl_ptr = &ld->xl_tx_list[i]; + cd->xl_tx_chain[i].xl_phys = vtophys(&ld->xl_tx_list[i]); + if (i == (XL_TX_LIST_CNT - 1)) + cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[0]; + else + cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[i + 1]; + if (i == 0) + cd->xl_tx_chain[i].xl_prev = + &cd->xl_tx_chain[XL_TX_LIST_CNT - 1]; + else + cd->xl_tx_chain[i].xl_prev = + &cd->xl_tx_chain[i - 1]; + } + + bzero((char *)ld->xl_tx_list, sizeof(struct xl_list) * XL_TX_LIST_CNT); + ld->xl_tx_list[0].xl_status = XL_TXSTAT_EMPTY; + + cd->xl_tx_prod = 1; + cd->xl_tx_cons = 1; + cd->xl_tx_cnt = 0; + + return (0); +} + +/* + * Initialize the RX descriptors and allocate mbufs for them. Note that + * we arrange the descriptors in a closed ring, so that the last descriptor + * points back to the first. + */ +int xl_list_rx_init(sc) + struct xl_softc *sc; +{ + struct xl_chain_data *cd; + struct xl_list_data *ld; + int i; + + cd = &sc->xl_cdata; + ld = sc->xl_ldata; + + for (i = 0; i < XL_RX_LIST_CNT; i++) { + cd->xl_rx_chain[i].xl_ptr = + (struct xl_list_onefrag *)&ld->xl_rx_list[i]; + if (xl_newbuf(sc, &cd->xl_rx_chain[i]) == ENOBUFS) + return(ENOBUFS); + if (i == (XL_RX_LIST_CNT - 1)) { + cd->xl_rx_chain[i].xl_next = &cd->xl_rx_chain[0]; + ld->xl_rx_list[i].xl_next = + vtophys(&ld->xl_rx_list[0]); + } else { + cd->xl_rx_chain[i].xl_next = &cd->xl_rx_chain[i + 1]; + ld->xl_rx_list[i].xl_next = + vtophys(&ld->xl_rx_list[i + 1]); + } + } + + cd->xl_rx_head = &cd->xl_rx_chain[0]; + + return(0); +} + +/* + * Initialize an RX descriptor and attach an MBUF cluster. + */ +int xl_newbuf(sc, c) + struct xl_softc *sc; + struct xl_chain_onefrag *c; +{ + struct mbuf *m_new = NULL; + + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) + return(ENOBUFS); + + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + return(ENOBUFS); + } + + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + + /* Force longword alignment for packet payload. */ + m_adj(m_new, ETHER_ALIGN); + + c->xl_mbuf = m_new; + c->xl_ptr->xl_frag.xl_addr = vtophys(mtod(m_new, caddr_t)); + c->xl_ptr->xl_frag.xl_len = MCLBYTES | XL_LAST_FRAG; + c->xl_ptr->xl_status = 0; + + return(0); +} + +int xl_rx_resync(sc) + struct xl_softc *sc; +{ + struct xl_chain_onefrag *pos; + int i; + + pos = sc->xl_cdata.xl_rx_head; + + for (i = 0; i < XL_RX_LIST_CNT; i++) { + if (pos->xl_ptr->xl_status) + break; + pos = pos->xl_next; + } + + if (i == XL_RX_LIST_CNT) + return (0); + + sc->xl_cdata.xl_rx_head = pos; + + return (EAGAIN); +} + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + */ +void xl_rxeof(sc) + struct xl_softc *sc; +{ + struct ether_header *eh; + struct mbuf *m; + struct ifnet *ifp; + struct xl_chain_onefrag *cur_rx; + int total_len = 0; + u_int16_t rxstat; + + ifp = &sc->arpcom.ac_if; + +again: + + while((rxstat = sc->xl_cdata.xl_rx_head->xl_ptr->xl_status)) { + cur_rx = sc->xl_cdata.xl_rx_head; + sc->xl_cdata.xl_rx_head = cur_rx->xl_next; + + /* + * If an error occurs, update stats, clear the + * status word and leave the mbuf cluster in place: + * it should simply get re-used next time this descriptor + * comes up in the ring. + */ + if (rxstat & XL_RXSTAT_UP_ERROR) { + ifp->if_ierrors++; + cur_rx->xl_ptr->xl_status = 0; + continue; + } + + /* + * If there error bit was not set, the upload complete + * bit should be set which means we have a valid packet. + * If not, something truly strange has happened. + */ + if (!(rxstat & XL_RXSTAT_UP_CMPLT)) { + printf("xl%d: bad receive status -- " + "packet dropped", sc->xl_unit); + ifp->if_ierrors++; + cur_rx->xl_ptr->xl_status = 0; + continue; + } + + /* No errors; receive the packet. */ + m = cur_rx->xl_mbuf; + total_len = cur_rx->xl_ptr->xl_status & XL_RXSTAT_LENMASK; + + /* + * Try to conjure up a new mbuf cluster. If that + * fails, it means we have an out of memory condition and + * should leave the buffer in place and continue. This will + * result in a lost packet, but there's little else we + * can do in this situation. + */ + if (xl_newbuf(sc, cur_rx) == ENOBUFS) { + ifp->if_ierrors++; + cur_rx->xl_ptr->xl_status = 0; + continue; + } + + ifp->if_ipackets++; + eh = mtod(m, struct ether_header *); + m->m_pkthdr.rcvif = ifp; +#if NBPFILTER > 0 + /* + * Handle BPF listeners. Let the BPF user see the packet. + */ + if (ifp->if_bpf) { + m->m_pkthdr.len = m->m_len = total_len; + bpf_mtap(ifp->if_bpf, m); + } +#endif + /* Remove header from mbuf and pass it on. */ + m->m_pkthdr.len = m->m_len = + total_len - sizeof(struct ether_header); + m->m_data += sizeof(struct ether_header); + ether_input(ifp, eh, m); + } + + /* + * Handle the 'end of channel' condition. When the upload + * engine hits the end of the RX ring, it will stall. This + * is our cue to flush the RX ring, reload the uplist pointer + * register and unstall the engine. + * XXX This is actually a little goofy. With the ThunderLAN + * chip, you get an interrupt when the receiver hits the end + * of the receive ring, which tells you exactly when you + * you need to reload the ring pointer. Here we have to + * fake it. I'm mad at myself for not being clever enough + * to avoid the use of a goto here. + */ + if (CSR_READ_4(sc, XL_UPLIST_PTR) == 0 || + CSR_READ_4(sc, XL_UPLIST_STATUS) & XL_PKTSTAT_UP_STALLED) { + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL); + xl_wait(sc); + CSR_WRITE_4(sc, XL_UPLIST_PTR, + vtophys(&sc->xl_ldata->xl_rx_list[0])); + sc->xl_cdata.xl_rx_head = &sc->xl_cdata.xl_rx_chain[0]; + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL); + goto again; + } + + return; +} + +/* + * A frame was downloaded to the chip. It's safe for us to clean up + * the list buffers. + */ +void xl_txeof(sc) + struct xl_softc *sc; +{ + struct xl_chain *cur_tx; + struct ifnet *ifp; + + ifp = &sc->arpcom.ac_if; + + /* Clear the timeout timer. */ + ifp->if_timer = 0; + + /* + * Go through our tx list and free mbufs for those + * frames that have been uploaded. Note: the 3c905B + * sets a special bit in the status word to let us + * know that a frame has been downloaded, but the + * original 3c900/3c905 adapters don't do that. + * Consequently, we have to use a different test if + * xl_type != XL_TYPE_905B. + */ + while(sc->xl_cdata.xl_tx_head != NULL) { + cur_tx = sc->xl_cdata.xl_tx_head; + + if (CSR_READ_4(sc, XL_DOWNLIST_PTR)) + break; + + sc->xl_cdata.xl_tx_head = cur_tx->xl_next; + m_freem(cur_tx->xl_mbuf); + cur_tx->xl_mbuf = NULL; + ifp->if_opackets++; + + cur_tx->xl_next = sc->xl_cdata.xl_tx_free; + sc->xl_cdata.xl_tx_free = cur_tx; + } + + if (sc->xl_cdata.xl_tx_head == NULL) { + ifp->if_flags &= ~IFF_OACTIVE; + sc->xl_cdata.xl_tx_tail = NULL; + } else { + if (CSR_READ_4(sc, XL_DMACTL) & XL_DMACTL_DOWN_STALLED || + !CSR_READ_4(sc, XL_DOWNLIST_PTR)) { + CSR_WRITE_4(sc, XL_DOWNLIST_PTR, + vtophys(sc->xl_cdata.xl_tx_head->xl_ptr)); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); + } + } + + return; +} + +void +xl_txeof_90xB(sc) + struct xl_softc *sc; +{ + struct xl_chain *cur_tx = NULL; + struct ifnet *ifp; + int idx; + + ifp = &sc->arpcom.ac_if; + + idx = sc->xl_cdata.xl_tx_cons; + while(idx != sc->xl_cdata.xl_tx_prod) { + + cur_tx = &sc->xl_cdata.xl_tx_chain[idx]; + + if (!(cur_tx->xl_ptr->xl_status & XL_TXSTAT_DL_COMPLETE)) + break; + + if (cur_tx->xl_mbuf != NULL) { + m_freem(cur_tx->xl_mbuf); + cur_tx->xl_mbuf = NULL; + } + + ifp->if_opackets++; + + sc->xl_cdata.xl_tx_cnt--; + XL_INC(idx, XL_TX_LIST_CNT); + ifp->if_timer = 0; + } + + sc->xl_cdata.xl_tx_cons = idx; + + if (cur_tx != NULL) + ifp->if_flags &= ~IFF_OACTIVE; +} + +/* + * TX 'end of channel' interrupt handler. Actually, we should + * only get a 'TX complete' interrupt if there's a transmit error, + * so this is really TX error handler. + */ +void xl_txeoc(sc) + struct xl_softc *sc; +{ + u_int8_t txstat; + + while((txstat = CSR_READ_1(sc, XL_TX_STATUS))) { + if (txstat & XL_TXSTATUS_UNDERRUN || + txstat & XL_TXSTATUS_JABBER || + txstat & XL_TXSTATUS_RECLAIM) { + printf("xl%d: transmission error: %x\n", + sc->xl_unit, txstat); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); + xl_wait(sc); + if (sc->xl_type == XL_TYPE_905B) { + int i; + struct xl_chain *c; + i = sc->xl_cdata.xl_tx_cons; + c = &sc->xl_cdata.xl_tx_chain[i]; + CSR_WRITE_4(sc, XL_DOWNLIST_PTR, c->xl_phys); + CSR_WRITE_1(sc, XL_DOWN_POLL, 64); + } else { + if (sc->xl_cdata.xl_tx_head != NULL) + CSR_WRITE_4(sc, XL_DOWNLIST_PTR, + vtophys(sc->xl_cdata.xl_tx_head->xl_ptr)); + } + /* + * Remember to set this for the + * first generation 3c90X chips. + */ + CSR_WRITE_1(sc, XL_TX_FREETHRESH, XL_PACKET_SIZE >> 8); + if (txstat & XL_TXSTATUS_UNDERRUN && + sc->xl_tx_thresh < XL_PACKET_SIZE) { + sc->xl_tx_thresh += XL_MIN_FRAMELEN; + printf("xl%d: tx underrun, increasing tx start" + " threshold to %d\n", sc->xl_unit, + sc->xl_tx_thresh); + } + CSR_WRITE_2(sc, XL_COMMAND, + XL_CMD_TX_SET_START|sc->xl_tx_thresh); + if (sc->xl_type == XL_TYPE_905B) { + CSR_WRITE_2(sc, XL_COMMAND, + XL_CMD_SET_TX_RECLAIM|(XL_PACKET_SIZE >> 4)); + } + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); + } else { + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); + } + /* + * Write an arbitrary byte to the TX_STATUS register + * to clear this interrupt/error and advance to the next. + */ + CSR_WRITE_1(sc, XL_TX_STATUS, 0x01); + } + + return; +} + +int xl_intr(arg) + void *arg; +{ + struct xl_softc *sc; + struct ifnet *ifp; + u_int16_t status; + int claimed = 0; + + sc = arg; + ifp = &sc->arpcom.ac_if; + + while ((status = CSR_READ_2(sc, XL_STATUS)) & XL_INTRS) { + + claimed = 1; + + CSR_WRITE_2(sc, XL_COMMAND, + XL_CMD_INTR_ACK|(status & XL_INTRS)); + + if (sc->xl_bustype == XL_BUS_CARDBUS) + bus_space_write_4(sc->xl_funct,sc->xl_funch, 4, 0x8000); + + if (status & XL_STAT_UP_COMPLETE) { + int curpkts; + + curpkts = ifp->if_ipackets; + xl_rxeof(sc); + if (curpkts == ifp->if_ipackets) { + while (xl_rx_resync(sc)) + xl_rxeof(sc); + } + } + + if (status & XL_STAT_DOWN_COMPLETE) { + if (sc->xl_type == XL_TYPE_905B) + xl_txeof_90xB(sc); + else + xl_txeof(sc); + } + + if (status & XL_STAT_TX_COMPLETE) { + ifp->if_oerrors++; + xl_txeoc(sc); + } + + if (status & XL_STAT_ADFAIL) { + xl_reset(sc, 0); + xl_init(sc); + } + + if (status & XL_STAT_STATSOFLOW) { + sc->xl_stats_no_timeout = 1; + xl_stats_update(sc); + sc->xl_stats_no_timeout = 0; + } + } + + if (ifp->if_snd.ifq_head != NULL) + (*ifp->if_start)(ifp); + + return (claimed); +} + +void xl_stats_update(xsc) + void *xsc; +{ + struct xl_softc *sc; + struct ifnet *ifp; + struct xl_stats xl_stats; + u_int8_t *p; + int i; + struct mii_data *mii = NULL; + + bzero((char *)&xl_stats, sizeof(struct xl_stats)); + + sc = xsc; + ifp = &sc->arpcom.ac_if; + if (sc->xl_hasmii) + mii = &sc->sc_mii; + + p = (u_int8_t *)&xl_stats; + + /* Read all the stats registers. */ + XL_SEL_WIN(6); + + for (i = 0; i < 16; i++) + *p++ = CSR_READ_1(sc, XL_W6_CARRIER_LOST + i); + + ifp->if_ierrors += xl_stats.xl_rx_overrun; + + ifp->if_collisions += xl_stats.xl_tx_multi_collision + + xl_stats.xl_tx_single_collision + + xl_stats.xl_tx_late_collision; + + /* + * Boomerang and cyclone chips have an extra stats counter + * in window 4 (BadSSD). We have to read this too in order + * to clear out all the stats registers and avoid a statsoflow + * interrupt. + */ + XL_SEL_WIN(4); + CSR_READ_1(sc, XL_W4_BADSSD); + + if (mii != NULL) + mii_tick(mii); + + XL_SEL_WIN(7); + + if (!sc->xl_stats_no_timeout) + timeout(xl_stats_update, sc, hz); + + return; +} + +/* + * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data + * pointers to the fragment pointers. + */ +int xl_encap(sc, c, m_head) + struct xl_softc *sc; + struct xl_chain *c; + struct mbuf *m_head; +{ + int frag = 0; + struct xl_frag *f = NULL; + int total_len; + struct mbuf *m; + + /* + * Start packing the mbufs in this chain into + * the fragment pointers. Stop when we run out + * of fragments or hit the end of the mbuf chain. + */ + m = m_head; + total_len = 0; + + for (m = m_head, frag = 0; m != NULL; m = m->m_next) { + if (m->m_len != 0) { + if (frag == XL_MAXFRAGS) + break; + total_len+= m->m_len; + c->xl_ptr->xl_frag[frag].xl_addr = + vtophys(mtod(m, vm_offset_t)); + c->xl_ptr->xl_frag[frag].xl_len = m->m_len; + frag++; + } + } + + /* + * Handle special case: we used up all 63 fragments, + * but we have more mbufs left in the chain. Copy the + * data into an mbuf cluster. Note that we don't + * bother clearing the values in the other fragment + * pointers/counters; it wouldn't gain us anything, + * and would waste cycles. + */ + if (m != NULL) { + struct mbuf *m_new = NULL; + + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) + return(1); + if (m_head->m_pkthdr.len > MHLEN) { + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + return(1); + } + } + m_copydata(m_head, 0, m_head->m_pkthdr.len, + mtod(m_new, caddr_t)); + m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; + m_freem(m_head); + m_head = m_new; + f = &c->xl_ptr->xl_frag[0]; + f->xl_addr = vtophys(mtod(m_new, caddr_t)); + f->xl_len = total_len = m_new->m_len; + frag = 1; + } + + c->xl_mbuf = m_head; + c->xl_ptr->xl_frag[frag - 1].xl_len |= XL_LAST_FRAG; + c->xl_ptr->xl_status = total_len; + c->xl_ptr->xl_next = 0; + + return(0); +} + +/* + * Main transmit routine. To avoid having to do mbuf copies, we put pointers + * to the mbuf data regions directly in the transmit lists. We also save a + * copy of the pointers since the transmit list fragment pointers are + * physical addresses. + */ +void xl_start(ifp) + struct ifnet *ifp; +{ + struct xl_softc *sc; + struct mbuf *m_head = NULL; + struct xl_chain *prev = NULL, *cur_tx = NULL, *start_tx; + + sc = ifp->if_softc; + + /* + * Check for an available queue slot. If there are none, + * punt. + */ + if (sc->xl_cdata.xl_tx_free == NULL) { + xl_txeoc(sc); + xl_txeof(sc); + if (sc->xl_cdata.xl_tx_free == NULL) { + ifp->if_flags |= IFF_OACTIVE; + return; + } + } + + start_tx = sc->xl_cdata.xl_tx_free; + + while(sc->xl_cdata.xl_tx_free != NULL) { + IF_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + /* Pick a descriptor off the free list. */ + cur_tx = sc->xl_cdata.xl_tx_free; + sc->xl_cdata.xl_tx_free = cur_tx->xl_next; + + cur_tx->xl_next = NULL; + + /* Pack the data into the descriptor. */ + xl_encap(sc, cur_tx, m_head); + + /* Chain it together. */ + if (prev != NULL) { + prev->xl_next = cur_tx; + prev->xl_ptr->xl_next = vtophys(cur_tx->xl_ptr); + } + prev = cur_tx; + +#if NBPFILTER > 0 + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, cur_tx->xl_mbuf); +#endif + } + + /* + * If there are no packets queued, bail. + */ + if (cur_tx == NULL) + return; + + /* + * Place the request for the upload interrupt + * in the last descriptor in the chain. This way, if + * we're chaining several packets at once, we'll only + * get an interupt once for the whole chain rather than + * once for each packet. + */ + cur_tx->xl_ptr->xl_status |= XL_TXSTAT_DL_INTR; + + /* + * Queue the packets. If the TX channel is clear, update + * the downlist pointer register. + */ + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_STALL); + xl_wait(sc); + + if (sc->xl_cdata.xl_tx_head != NULL) { + sc->xl_cdata.xl_tx_tail->xl_next = start_tx; + sc->xl_cdata.xl_tx_tail->xl_ptr->xl_next = + vtophys(start_tx->xl_ptr); + sc->xl_cdata.xl_tx_tail->xl_ptr->xl_status &= + ~XL_TXSTAT_DL_INTR; + sc->xl_cdata.xl_tx_tail = cur_tx; + } else { + sc->xl_cdata.xl_tx_head = start_tx; + sc->xl_cdata.xl_tx_tail = cur_tx; + } + if (!CSR_READ_4(sc, XL_DOWNLIST_PTR)) + CSR_WRITE_4(sc, XL_DOWNLIST_PTR, vtophys(start_tx->xl_ptr)); + + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); + + XL_SEL_WIN(7); + + /* + * Set a timeout in case the chip goes out to lunch. + */ + ifp->if_timer = 5; + + /* + * XXX Under certain conditions, usually on slower machines + * where interrupts may be dropped, it's possible for the + * adapter to chew up all the buffers in the receive ring + * and stall, without us being able to do anything about it. + * To guard against this, we need to make a pass over the + * RX queue to make sure there aren't any packets pending. + * Doing it here means we can flush the receive ring at the + * same time the chip is DMAing the transmit descriptors we + * just gave it. + * + * 3Com goes to some lengths to emphasize the Parallel Tasking (tm) + * nature of their chips in all their marketing literature; + * we may as well take advantage of it. :) + */ + xl_rxeof(sc); + + return; +} + +int xl_encap_90xB(sc, c, m_head) + struct xl_softc *sc; + struct xl_chain *c; + struct mbuf *m_head; +{ + int frag = 0; + struct xl_frag *f = NULL; + struct mbuf *m; + struct xl_list *d; + + /* + * Start packing the mbufs in this chain into + * the fragment pointers. Stop when we run out + * of fragments or hit the end of the mbuf chain. + */ + d = c->xl_ptr; + d->xl_status = 0; + d->xl_next = 0; + + for (m = m_head, frag = 0; m != NULL; m = m->m_next) { + if (m->m_len != 0) { + if (frag == XL_MAXFRAGS) + break; + f = &d->xl_frag[frag]; + f->xl_addr = vtophys(mtod(m, vm_offset_t)); + f->xl_len = m->m_len; + frag++; + } + } + + c->xl_mbuf = m_head; + c->xl_ptr->xl_frag[frag - 1].xl_len |= XL_LAST_FRAG; + c->xl_ptr->xl_status = XL_TXSTAT_RND_DEFEAT; + + return(0); +} + +void +xl_start_90xB(ifp) + struct ifnet *ifp; +{ + struct xl_softc *sc; + struct mbuf *m_head = NULL; + struct xl_chain *prev = NULL, *cur_tx = NULL, *start_tx; + int idx; + + sc = ifp->if_softc; + + if (ifp->if_flags & IFF_OACTIVE) + return; + + idx = sc->xl_cdata.xl_tx_prod; + start_tx = &sc->xl_cdata.xl_tx_chain[idx]; + + while (sc->xl_cdata.xl_tx_chain[idx].xl_mbuf == NULL) { + + if ((XL_TX_LIST_CNT - sc->xl_cdata.xl_tx_cnt) < 3) { + ifp->if_flags |= IFF_OACTIVE; + break; + } + + IF_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + cur_tx = &sc->xl_cdata.xl_tx_chain[idx]; + + /* Pack the data into the descriptor. */ + xl_encap_90xB(sc, cur_tx, m_head); + + /* Chain it together. */ + if (prev != NULL) + prev->xl_ptr->xl_next = cur_tx->xl_phys; + prev = cur_tx; + +#if NBPFILTER > 0 + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, cur_tx->xl_mbuf); +#endif + + XL_INC(idx, XL_TX_LIST_CNT); + sc->xl_cdata.xl_tx_cnt++; + } + + /* + * If there are no packets queued, bail. + */ + if (cur_tx == NULL) + return; + + /* + * Place the request for the upload interrupt + * in the last descriptor in the chain. This way, if + * we're chaining several packets at once, we'll only + * get an interupt once for the whole chain rather than + * once for each packet. + */ + cur_tx->xl_ptr->xl_status |= XL_TXSTAT_DL_INTR; + + /* Start transmission */ + sc->xl_cdata.xl_tx_prod = idx; + start_tx->xl_prev->xl_ptr->xl_next = start_tx->xl_phys; + + /* + * Set a timeout in case the chip goes out to lunch. + */ + ifp->if_timer = 5; +} + +void xl_init(xsc) + void *xsc; +{ + struct xl_softc *sc = xsc; + struct ifnet *ifp = &sc->arpcom.ac_if; + int s, i; + u_int16_t rxfilt = 0; + struct mii_data *mii = NULL; + + s = splimp(); + + /* + * Cancel pending I/O and free all RX/TX buffers. + */ + xl_stop(sc); + + if (sc->xl_hasmii) + mii = &sc->sc_mii; + + if (mii == NULL) { + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET); + xl_wait(sc); + } + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); + xl_wait(sc); + DELAY(10000); + + + /* Init our MAC address */ + XL_SEL_WIN(2); + for (i = 0; i < ETHER_ADDR_LEN; i++) { + CSR_WRITE_1(sc, XL_W2_STATION_ADDR_LO + i, + sc->arpcom.ac_enaddr[i]); + } + + /* Clear the station mask. */ + for (i = 0; i < 3; i++) + CSR_WRITE_2(sc, XL_W2_STATION_MASK_LO + (i * 2), 0); +#ifdef notdef + /* Reset TX and RX. */ + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET); + xl_wait(sc); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); + xl_wait(sc); +#endif + /* Init circular RX list. */ + if (xl_list_rx_init(sc) == ENOBUFS) { + printf("xl%d: initialization failed: no " + "memory for rx buffers\n", sc->xl_unit); + xl_stop(sc); + return; + } + + /* Init TX descriptors. */ + if (sc->xl_type == XL_TYPE_905B) + xl_list_tx_init_90xB(sc); + else + xl_list_tx_init(sc); + + /* + * Set the TX freethresh value. + * Note that this has no effect on 3c905B "cyclone" + * cards but is required for 3c900/3c905 "boomerang" + * cards in order to enable the download engine. + */ + CSR_WRITE_1(sc, XL_TX_FREETHRESH, XL_PACKET_SIZE >> 8); + + /* Set the TX start threshold for best performance. */ + sc->xl_tx_thresh = XL_MIN_FRAMELEN; + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_SET_START|sc->xl_tx_thresh); + + /* + * If this is a 3c905B, also set the tx reclaim threshold. + * This helps cut down on the number of tx reclaim errors + * that could happen on a busy network. The chip multiplies + * the register value by 16 to obtain the actual threshold + * in bytes, so we divide by 16 when setting the value here. + * The existing threshold value can be examined by reading + * the register at offset 9 in window 5. + */ + if (sc->xl_type == XL_TYPE_905B) { + CSR_WRITE_2(sc, XL_COMMAND, + XL_CMD_SET_TX_RECLAIM|(XL_PACKET_SIZE >> 4)); + } + + /* Set RX filter bits. */ + XL_SEL_WIN(5); + rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER); + + /* Set the individual bit to receive frames for this host only. */ + rxfilt |= XL_RXFILTER_INDIVIDUAL; + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) { + rxfilt |= XL_RXFILTER_ALLFRAMES; + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); + } else { + rxfilt &= ~XL_RXFILTER_ALLFRAMES; + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); + } + + /* + * Set capture broadcast bit to capture broadcast frames. + */ + if (ifp->if_flags & IFF_BROADCAST) { + rxfilt |= XL_RXFILTER_BROADCAST; + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); + } else { + rxfilt &= ~XL_RXFILTER_BROADCAST; + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); + } + + /* + * Program the multicast filter, if necessary. + */ + if (sc->xl_type == XL_TYPE_905B) + xl_setmulti_hash(sc); + else + xl_setmulti(sc); + + /* + * Load the address of the RX list. We have to + * stall the upload engine before we can manipulate + * the uplist pointer register, then unstall it when + * we're finished. We also have to wait for the + * stall command to complete before proceeding. + * Note that we have to do this after any RX resets + * have completed since the uplist register is cleared + * by a reset. + */ + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL); + xl_wait(sc); + CSR_WRITE_4(sc, XL_UPLIST_PTR, vtophys(&sc->xl_ldata->xl_rx_list[0])); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL); + xl_wait(sc); + + if (sc->xl_type == XL_TYPE_905B) { + /* Set polling interval */ + CSR_WRITE_1(sc, XL_DOWN_POLL, 64); + /* Load the address of the TX list */ + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_STALL); + xl_wait(sc); + CSR_WRITE_4(sc, XL_DOWNLIST_PTR, + vtophys(&sc->xl_ldata->xl_tx_list[0])); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); + xl_wait(sc); + } + + /* + * If the coax transceiver is on, make sure to enable + * the DC-DC converter. + */ + XL_SEL_WIN(3); + if (sc->xl_xcvr == XL_XCVR_COAX) + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START); + else + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); + + /* Clear out the stats counters. */ + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE); + sc->xl_stats_no_timeout = 1; + xl_stats_update(sc); + sc->xl_stats_no_timeout = 0; + XL_SEL_WIN(4); + CSR_WRITE_2(sc, XL_W4_NET_DIAG, XL_NETDIAG_UPPER_BYTES_ENABLE); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_ENABLE); + + /* + * Enable interrupts. + */ + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|0xFF); + + if (sc->xl_bustype == XL_BUS_CARDBUS) + bus_space_write_4(sc->xl_funct, sc->xl_funch, 4, 0x8000); + + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|XL_INTRS); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|XL_INTRS); + + /* Set the RX early threshold */ + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_THRESH|(XL_PACKET_SIZE >>2)); + CSR_WRITE_2(sc, XL_DMACTL, XL_DMACTL_UP_RX_EARLY); + + /* Enable receiver and transmitter. */ + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE); + xl_wait(sc); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_ENABLE); + xl_wait(sc); + + /* Restore state of BMCR */ + if (mii != NULL) + mii_mediachg(mii); + + /* Select window 7 for normal operations. */ + XL_SEL_WIN(7); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + (void)splx(s); + + timeout(xl_stats_update, sc, hz); + + return; +} + +/* + * Set media options. + */ +int xl_ifmedia_upd(ifp) + struct ifnet *ifp; +{ + struct xl_softc *sc; + struct ifmedia *ifm = NULL; + struct mii_data *mii = NULL; + + sc = ifp->if_softc; + + if (sc->xl_hasmii) + mii = &sc->sc_mii; + if (mii == NULL) + ifm = &sc->ifmedia; + else + ifm = &mii->mii_media; + + switch(IFM_SUBTYPE(ifm->ifm_media)) { + case IFM_100_FX: + case IFM_10_FL: + case IFM_10_2: + case IFM_10_5: + xl_setmode(sc, ifm->ifm_media); + return (0); + break; + default: + break; + } + + if (sc->xl_media & XL_MEDIAOPT_MII || sc->xl_media & XL_MEDIAOPT_BTX + || sc->xl_media & XL_MEDIAOPT_BT4) { + xl_init(sc); + } else { + xl_setmode(sc, ifm->ifm_media); + } + + return(0); +} + +/* + * Report current media status. + */ +void xl_ifmedia_sts(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct xl_softc *sc; + u_int32_t icfg; + struct mii_data *mii = NULL; + + sc = ifp->if_softc; + if (sc->xl_hasmii != 0) + mii = &sc->sc_mii; + + XL_SEL_WIN(3); + icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG) & XL_ICFG_CONNECTOR_MASK; + icfg >>= XL_ICFG_CONNECTOR_BITS; + + ifmr->ifm_active = IFM_ETHER; + + switch(icfg) { + case XL_XCVR_10BT: + ifmr->ifm_active = IFM_ETHER|IFM_10_T; + if (CSR_READ_1(sc, XL_W3_MAC_CTRL) & XL_MACCTRL_DUPLEX) + ifmr->ifm_active |= IFM_FDX; + else + ifmr->ifm_active |= IFM_HDX; + break; + case XL_XCVR_AUI: + if (sc->xl_type == XL_TYPE_905B && + sc->xl_media == XL_MEDIAOPT_10FL) { + ifmr->ifm_active = IFM_ETHER|IFM_10_FL; + if (CSR_READ_1(sc, XL_W3_MAC_CTRL) & XL_MACCTRL_DUPLEX) + ifmr->ifm_active |= IFM_FDX; + else + ifmr->ifm_active |= IFM_FDX; + } else + ifmr->ifm_active = IFM_ETHER|IFM_10_5; + break; + case XL_XCVR_COAX: + ifmr->ifm_active = IFM_ETHER|IFM_10_2; + break; + /* + * XXX MII and BTX/AUTO should be separate cases. + */ + + case XL_XCVR_100BTX: + case XL_XCVR_AUTO: + case XL_XCVR_MII: + if (mii != NULL) { + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + } + break; + case XL_XCVR_100BFX: + ifmr->ifm_active = IFM_ETHER|IFM_100_FX; + break; + default: + printf("xl%d: unknown XCVR type: %d\n", sc->xl_unit, icfg); + break; + } + + return; +} + +int +xl_ioctl(ifp, command, data) + struct ifnet *ifp; + u_long command; + caddr_t data; +{ + struct xl_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + struct ifaddr *ifa = (struct ifaddr *)data; + int s, error = 0; + struct mii_data *mii = NULL; + u_int8_t rxfilt; + + s = splimp(); + + if ((error = ether_ioctl(ifp, &sc->arpcom, command, data)) > 0) { + splx(s); + return error; + } + + switch(command) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + xl_init(sc); + arp_ifinit(&sc->arpcom, ifa); + break; +#endif /* INET */ + default: + xl_init(sc); + break; + } + break; + case SIOCSIFFLAGS: + XL_SEL_WIN(5); + rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER); + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING && + ifp->if_flags & IFF_PROMISC && + !(sc->xl_if_flags & IFF_PROMISC)) { + rxfilt |= XL_RXFILTER_ALLFRAMES; + CSR_WRITE_2(sc, XL_COMMAND, + XL_CMD_RX_SET_FILT|rxfilt); + XL_SEL_WIN(7); + } else if (ifp->if_flags & IFF_RUNNING && + !(ifp->if_flags & IFF_PROMISC) && + sc->xl_if_flags & IFF_PROMISC) { + rxfilt &= ~XL_RXFILTER_ALLFRAMES; + CSR_WRITE_2(sc, XL_COMMAND, + XL_CMD_RX_SET_FILT|rxfilt); + XL_SEL_WIN(7); + } else + xl_init(sc); + } else { + if (ifp->if_flags & IFF_RUNNING) + xl_stop(sc); + } + sc->xl_if_flags = ifp->if_flags; + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + error = (command == SIOCADDMULTI) ? + ether_addmulti(ifr, &sc->arpcom) : + ether_delmulti(ifr, &sc->arpcom); + + if (error == ENETRESET) { + /* + * Multicast list has changed; set the hardware + * filter accordingly. + */ + if (sc->xl_type == XL_TYPE_905B) + xl_setmulti_hash(sc); + else + xl_setmulti(sc); + error = 0; + } + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + if (sc->xl_hasmii != 0) + mii = &sc->sc_mii; + if (mii == NULL) + error = ifmedia_ioctl(ifp, ifr, + &sc->ifmedia, command); + else + error = ifmedia_ioctl(ifp, ifr, + &mii->mii_media, command); + break; + default: + error = EINVAL; + break; + } + + (void)splx(s); + + return(error); +} + +void xl_watchdog(ifp) + struct ifnet *ifp; +{ + struct xl_softc *sc; + u_int16_t status = 0; + + sc = ifp->if_softc; + + ifp->if_oerrors++; + XL_SEL_WIN(4); + status = CSR_READ_2(sc, XL_W4_MEDIA_STATUS); + printf("xl%d: watchdog timeout\n", sc->xl_unit); + + if (status & XL_MEDIASTAT_CARRIER) + printf("xl%d: no carrier - transceiver cable problem?\n", + sc->xl_unit); + xl_txeoc(sc); + xl_txeof(sc); + xl_rxeof(sc); + xl_reset(sc, 0); + xl_init(sc); + + if (ifp->if_snd.ifq_head != NULL) + (*ifp->if_start)(ifp); + + return; +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +void xl_stop(sc) + struct xl_softc *sc; +{ + int i; + struct ifnet *ifp; + + ifp = &sc->arpcom.ac_if; + ifp->if_timer = 0; + + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISABLE); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISCARD); + xl_wait(sc); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_DISABLE); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); + DELAY(800); + +#ifdef foo + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET); + xl_wait(sc); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); + xl_wait(sc); +#endif + + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|XL_STAT_INTLATCH); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|0); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|0); + + if (sc->xl_bustype == XL_BUS_CARDBUS) + bus_space_write_4(sc->xl_funct, sc->xl_funch, 4, 0x8000); + + /* Stop the stats updater. */ + untimeout(xl_stats_update, sc); + + /* + * Free data in the RX lists. + */ + for (i = 0; i < XL_RX_LIST_CNT; i++) { + if (sc->xl_cdata.xl_rx_chain[i].xl_mbuf != NULL) { + m_freem(sc->xl_cdata.xl_rx_chain[i].xl_mbuf); + sc->xl_cdata.xl_rx_chain[i].xl_mbuf = NULL; + } + } + bzero((char *)&sc->xl_ldata->xl_rx_list, + sizeof(sc->xl_ldata->xl_rx_list)); + /* + * Free the TX list buffers. + */ + for (i = 0; i < XL_TX_LIST_CNT; i++) { + if (sc->xl_cdata.xl_tx_chain[i].xl_mbuf != NULL) { + m_freem(sc->xl_cdata.xl_tx_chain[i].xl_mbuf); + sc->xl_cdata.xl_tx_chain[i].xl_mbuf = NULL; + } + } + bzero((char *)&sc->xl_ldata->xl_tx_list, + sizeof(sc->xl_ldata->xl_tx_list)); + + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + + return; +} + +void +xl_attach(sc) + struct xl_softc *sc; +{ + u_int8_t enaddr[ETHER_ADDR_LEN]; + struct ifnet *ifp = &sc->arpcom.ac_if; + caddr_t roundptr; + u_int round; + int i, media = IFM_ETHER|IFM_100_TX|IFM_FDX; + struct ifmedia *ifm; + + sc->xl_unit = sc->sc_dev.dv_unit; + xl_reset(sc, 1); + + /* + * Get station address from the EEPROM. + */ + if (xl_read_eeprom(sc, (caddr_t)&enaddr, XL_EE_OEM_ADR0, 3, 1)) { + printf("\n%s: failed to read station address\n", + sc->sc_dev.dv_xname); + return; + } + bcopy(enaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); + + printf(" address %s\n", ether_sprintf(sc->arpcom.ac_enaddr)); + + if (sc->xl_bustype == XL_BUS_CARDBUS) { + u_int16_t devid; + u_int16_t n; + + XL_SEL_WIN(2); + n = CSR_READ_2(sc, 12); + xl_read_eeprom(sc, (caddr_t)&devid, XL_EE_PRODID, 1, 0); + + if (devid != 0x5257) + n |= 0x0010; + if (devid == 0x5257 || devid == 0x6560 || devid == 0x6562) + n |= 0x4000; + + CSR_WRITE_2(sc, 12, n); + } + + sc->xl_ldata_ptr = malloc(sizeof(struct xl_list_data) + 8, + M_DEVBUF, M_NOWAIT); + if (sc->xl_ldata_ptr == NULL) { + printf("%s: no memory for list buffers\n",sc->sc_dev.dv_xname); + return; + } + + sc->xl_ldata = (struct xl_list_data *)sc->xl_ldata_ptr; +#ifdef __alpha__ + round = (u_int64_t)sc->xl_ldata_ptr & 0xf; +#else + round = (u_int32_t)sc->xl_ldata_ptr & 0xf; +#endif + roundptr = sc->xl_ldata_ptr; + for (i = 0; i < 8; i++) { + if (round % 8) { + round++; + roundptr++; + } else + break; + } + sc->xl_ldata = (struct xl_list_data *)roundptr; + bzero(sc->xl_ldata, sizeof(struct xl_list_data)); + + /* + * Figure out the card type. 3c905B adapters have the + * 'supportsNoTxLength' bit set in the capabilities + * word in the EEPROM. + */ + xl_read_eeprom(sc, (caddr_t)&sc->xl_caps, XL_EE_CAPS, 1, 0); + if (sc->xl_caps & XL_CAPS_NO_TXLENGTH) + sc->xl_type = XL_TYPE_905B; + else + sc->xl_type = XL_TYPE_90X; + + ifp->if_softc = sc; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = xl_ioctl; + ifp->if_output = ether_output; + if (sc->xl_type == XL_TYPE_905B) + ifp->if_start = xl_start_90xB; + else + ifp->if_start = xl_start; + ifp->if_watchdog = xl_watchdog; + ifp->if_baudrate = 10000000; + ifp->if_snd.ifq_maxlen = XL_TX_LIST_CNT - 1; + bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); + + XL_SEL_WIN(3); + sc->xl_media = CSR_READ_2(sc, XL_W3_MEDIA_OPT); + + xl_read_eeprom(sc, (char *)&sc->xl_xcvr, XL_EE_ICFG_0, 2, 0); + sc->xl_xcvr &= XL_ICFG_CONNECTOR_MASK; + sc->xl_xcvr >>= XL_ICFG_CONNECTOR_BITS; + + if (sc->xl_bustype == XL_BUS_CARDBUS) { + XL_SEL_WIN(2); + CSR_WRITE_2(sc, 12, 0x4000 | CSR_READ_2(sc, 12)); + } + DELAY(100000); + + xl_mediacheck(sc); + + if (sc->xl_bustype == XL_BUS_CARDBUS) { + XL_SEL_WIN(2); + CSR_WRITE_2(sc, 12, 0x4000 | CSR_READ_2(sc, 12)); + } + DELAY(100000); + + if (sc->xl_media & XL_MEDIAOPT_MII || sc->xl_media & XL_MEDIAOPT_BTX + || sc->xl_media & XL_MEDIAOPT_BT4) { + ifmedia_init(&sc->sc_mii.mii_media, 0, + xl_ifmedia_upd, xl_ifmedia_sts); + sc->xl_hasmii = 1; + sc->sc_mii.mii_ifp = ifp; + sc->sc_mii.mii_readreg = xl_miibus_readreg; + sc->sc_mii.mii_writereg = xl_miibus_writereg; + sc->sc_mii.mii_statchg = xl_miibus_statchg; + xl_setcfg(sc); + mii_phy_probe((struct device *)sc, &sc->sc_mii, 0xffffffff); + + if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) { + ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE, + 0, NULL); + ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE); + } + else { + ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO); + } + ifm = &sc->sc_mii.mii_media; + } + else { + ifmedia_init(&sc->ifmedia, 0, xl_ifmedia_upd, xl_ifmedia_sts); + sc->xl_hasmii = 0; + ifm = &sc->ifmedia; + } + + /* + * Sanity check. If the user has selected "auto" and this isn't + * a 10/100 card of some kind, we need to force the transceiver + * type to something sane. + */ + if (sc->xl_xcvr == XL_XCVR_AUTO) { + xl_choose_xcvr(sc, 0); + xl_reset(sc, 0); + } + + if (sc->xl_media & XL_MEDIAOPT_BT) { + ifmedia_add(ifm, IFM_ETHER|IFM_10_T, 0, NULL); + ifmedia_add(ifm, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); + if (sc->xl_caps & XL_CAPS_FULL_DUPLEX) + ifmedia_add(&sc->ifmedia, + IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); + } + + if (sc->xl_media & (XL_MEDIAOPT_AUI|XL_MEDIAOPT_10FL)) { + /* + * Check for a 10baseFL board in disguise. + */ + if (sc->xl_type == XL_TYPE_905B && + sc->xl_media == XL_MEDIAOPT_10FL) { + ifmedia_add(ifm, IFM_ETHER|IFM_10_FL, 0, NULL); + ifmedia_add(ifm, IFM_ETHER|IFM_10_FL|IFM_HDX, + 0, NULL); + if (sc->xl_caps & XL_CAPS_FULL_DUPLEX) + ifmedia_add(ifm, + IFM_ETHER|IFM_10_FL|IFM_FDX, 0, NULL); + } else { + ifmedia_add(ifm, IFM_ETHER|IFM_10_5, 0, NULL); + } + } + + if (sc->xl_media & XL_MEDIAOPT_BNC) { + ifmedia_add(ifm, IFM_ETHER|IFM_10_2, 0, NULL); + } + + if (sc->xl_media & XL_MEDIAOPT_BFX) { + ifp->if_baudrate = 100000000; + ifmedia_add(ifm, IFM_ETHER|IFM_100_FX, 0, NULL); + } + + /* Choose a default media. */ + switch(sc->xl_xcvr) { + case XL_XCVR_10BT: + media = IFM_ETHER|IFM_10_T; + xl_setmode(sc, media); + break; + case XL_XCVR_AUI: + if (sc->xl_type == XL_TYPE_905B && + sc->xl_media == XL_MEDIAOPT_10FL) { + media = IFM_ETHER|IFM_10_FL; + xl_setmode(sc, media); + } else { + media = IFM_ETHER|IFM_10_5; + xl_setmode(sc, media); + } + break; + case XL_XCVR_COAX: + media = IFM_ETHER|IFM_10_2; + xl_setmode(sc, media); + break; + case XL_XCVR_AUTO: + case XL_XCVR_100BTX: + case XL_XCVR_MII: + /* Chosen by miibus */ + break; + case XL_XCVR_100BFX: + media = IFM_ETHER|IFM_100_FX; + xl_setmode(sc, media); + break; + default: + printf("xl%d: unknown XCVR type: %d\n", sc->xl_unit, + sc->xl_xcvr); + /* + * This will probably be wrong, but it prevents + * the ifmedia code from panicking. + */ + media = IFM_ETHER | IFM_10_T; + break; + } + + if (sc->xl_hasmii == 0) + ifmedia_set(&sc->ifmedia, media); + + /* + * Call MI attach routines. + */ + if_attach(ifp); + ether_ifattach(ifp); + +#if NBPFILTER > 0 + bpfattach(&sc->arpcom.ac_if.if_bpf, ifp, + DLT_EN10MB, sizeof(struct ether_header)); +#endif + shutdownhook_establish(xl_shutdown, sc); +} + +void +xl_shutdown(v) + void *v; +{ + struct xl_softc *sc = (struct xl_softc *)v; + + xl_reset(sc, 1); + xl_stop(sc); +} + +struct cfdriver xl_cd = { + 0, "xl", DV_IFNET +}; diff --git a/sys/dev/ic/xlreg.h b/sys/dev/ic/xlreg.h new file mode 100644 index 00000000000..2024d97788e --- /dev/null +++ b/sys/dev/ic/xlreg.h @@ -0,0 +1,702 @@ +/* $OpenBSD: xlreg.h,v 1.1 2000/04/08 05:50:50 aaron Exp $ */ + +/* + * Copyright (c) 1997, 1998 + * Bill Paul . 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 Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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: if_xlreg.h,v 1.17 1999/05/30 18:09:17 wpaul Exp $ + */ + +#define XL_BUS_PCI 0x00 +#define XL_BUS_CARDBUS 0x01 + +#define XL_EE_READ 0x0080 /* read, 5 bit address */ +#define XL_EE_WRITE 0x0040 /* write, 5 bit address */ +#define XL_EE_ERASE 0x00c0 /* erase, 5 bit address */ +#define XL_EE_EWEN 0x0030 /* erase, no data needed */ +#define XL_EE_BUSY 0x8000 + +#define XL_EE_EADDR0 0x00 /* station address, first word */ +#define XL_EE_EADDR1 0x01 /* station address, next word, */ +#define XL_EE_EADDR2 0x02 /* station address, last word */ +#define XL_EE_PRODID 0x03 /* product ID code */ +#define XL_EE_MDATA_DATE 0x04 /* manufacturing data, date */ +#define XL_EE_MDATA_DIV 0x05 /* manufacturing data, division */ +#define XL_EE_MDATA_PCODE 0x06 /* manufacturing data, product code */ +#define XL_EE_MFG_ID 0x07 +#define XL_EE_PCI_PARM 0x08 +#define XL_EE_ROM_ONFO 0x09 +#define XL_EE_OEM_ADR0 0x0A +#define XL_EE_OEM_ADR1 0x0B +#define XL_EE_OEM_ADR2 0x0C +#define XL_EE_SOFTINFO1 0x0D +#define XL_EE_COMPAT 0x0E +#define XL_EE_SOFTINFO2 0x0F +#define XL_EE_CAPS 0x10 /* capabilities word */ +#define XL_EE_RSVD0 0x11 +#define XL_EE_ICFG_0 0x12 +#define XL_EE_ICFG_1 0x13 +#define XL_EE_RSVD1 0x14 +#define XL_EE_SOFTINFO3 0x15 +#define XL_EE_RSVD_2 0x16 + +/* + * Bits in the capabilities word + */ +#define XL_CAPS_PNP 0x0001 +#define XL_CAPS_FULL_DUPLEX 0x0002 +#define XL_CAPS_LARGE_PKTS 0x0004 +#define XL_CAPS_SLAVE_DMA 0x0008 +#define XL_CAPS_SECOND_DMA 0x0010 +#define XL_CAPS_FULL_BM 0x0020 +#define XL_CAPS_FRAG_BM 0x0040 +#define XL_CAPS_CRC_PASSTHRU 0x0080 +#define XL_CAPS_TXDONE 0x0100 +#define XL_CAPS_NO_TXLENGTH 0x0200 +#define XL_CAPS_RX_REPEAT 0x0400 +#define XL_CAPS_SNOOPING 0x0800 +#define XL_CAPS_100MBPS 0x1000 +#define XL_CAPS_PWRMGMT 0x2000 + +#define XL_PACKET_SIZE 1536 + +/* + * Register layouts. + */ +#define XL_COMMAND 0x0E +#define XL_STATUS 0x0E + +#define XL_TX_STATUS 0x1B +#define XL_TX_FREE 0x1C +#define XL_DMACTL 0x20 +#define XL_DOWNLIST_PTR 0x24 +#define XL_DOWN_POLL 0x2D /* 3c90xB only */ +#define XL_TX_FREETHRESH 0x2F +#define XL_UPLIST_PTR 0x38 +#define XL_UPLIST_STATUS 0x30 +#define XL_UP_POLL 0x3D /* 3c90xB only */ + +#define XL_PKTSTAT_UP_STALLED 0x00002000 +#define XL_PKTSTAT_UP_ERROR 0x00004000 +#define XL_PKTSTAT_UP_CMPLT 0x00008000 + +#define XL_DMACTL_DN_CMPLT_REQ 0x00000002 +#define XL_DMACTL_DOWN_STALLED 0x00000004 +#define XL_DMACTL_UP_CMPLT 0x00000008 +#define XL_DMACTL_DOWN_CMPLT 0x00000010 +#define XL_DMACTL_UP_RX_EARLY 0x00000020 +#define XL_DMACTL_ARM_COUNTDOWN 0x00000040 +#define XL_DMACTL_DOWN_INPROG 0x00000080 +#define XL_DMACTL_COUNTER_SPEED 0x00000100 +#define XL_DMACTL_DOWNDOWN_MODE 0x00000200 +#define XL_DMACTL_TARGET_ABORT 0x40000000 +#define XL_DMACTL_MASTER_ABORT 0x80000000 + +/* + * Command codes. Some command codes require that we wait for + * the CMD_BUSY flag to clear. Those codes are marked as 'mustwait.' + */ +#define XL_CMD_RESET 0x0000 /* mustwait */ +#define XL_CMD_WINSEL 0x0800 +#define XL_CMD_COAX_START 0x1000 +#define XL_CMD_RX_DISABLE 0x1800 +#define XL_CMD_RX_ENABLE 0x2000 +#define XL_CMD_RX_RESET 0x2800 /* mustwait */ +#define XL_CMD_UP_STALL 0x3000 /* mustwait */ +#define XL_CMD_UP_UNSTALL 0x3001 +#define XL_CMD_DOWN_STALL 0x3002 /* mustwait */ +#define XL_CMD_DOWN_UNSTALL 0x3003 +#define XL_CMD_RX_DISCARD 0x4000 +#define XL_CMD_TX_ENABLE 0x4800 +#define XL_CMD_TX_DISABLE 0x5000 +#define XL_CMD_TX_RESET 0x5800 /* mustwait */ +#define XL_CMD_INTR_FAKE 0x6000 +#define XL_CMD_INTR_ACK 0x6800 +#define XL_CMD_INTR_ENB 0x7000 +#define XL_CMD_STAT_ENB 0x7800 +#define XL_CMD_RX_SET_FILT 0x8000 +#define XL_CMD_RX_SET_THRESH 0x8800 +#define XL_CMD_TX_SET_THRESH 0x9000 +#define XL_CMD_TX_SET_START 0x9800 +#define XL_CMD_DMA_UP 0xA000 +#define XL_CMD_DMA_STOP 0xA001 +#define XL_CMD_STATS_ENABLE 0xA800 +#define XL_CMD_STATS_DISABLE 0xB000 +#define XL_CMD_COAX_STOP 0xB800 + +#define XL_CMD_SET_TX_RECLAIM 0xC000 /* 3c905B only */ +#define XL_CMD_RX_SET_HASH 0xC800 /* 3c905B only */ + +#define XL_HASH_SET 0x0400 +#define XL_HASHFILT_SIZE 256 + +/* + * status codes + * Note that bits 15 to 13 indicate the currently visible register window + * which may be anything from 0 to 7. + */ +#define XL_STAT_INTLATCH 0x0001 /* 0 */ +#define XL_STAT_ADFAIL 0x0002 /* 1 */ +#define XL_STAT_TX_COMPLETE 0x0004 /* 2 */ +#define XL_STAT_TX_AVAIL 0x0008 /* 3 first generation */ +#define XL_STAT_RX_COMPLETE 0x0010 /* 4 */ +#define XL_STAT_RX_EARLY 0x0020 /* 5 */ +#define XL_STAT_INTREQ 0x0040 /* 6 */ +#define XL_STAT_STATSOFLOW 0x0080 /* 7 */ +#define XL_STAT_DMADONE 0x0100 /* 8 first generation */ +#define XL_STAT_LINKSTAT 0x0100 /* 8 3c509B */ +#define XL_STAT_DOWN_COMPLETE 0x0200 /* 9 */ +#define XL_STAT_UP_COMPLETE 0x0400 /* 10 */ +#define XL_STAT_DMABUSY 0x0800 /* 11 first generation */ +#define XL_STAT_CMDBUSY 0x1000 /* 12 */ + +/* + * Interrupts we normally want enabled. + */ +#define XL_INTRS \ + (XL_STAT_UP_COMPLETE|XL_STAT_STATSOFLOW|XL_STAT_ADFAIL| \ + XL_STAT_DOWN_COMPLETE|XL_STAT_TX_COMPLETE|XL_STAT_INTLATCH) + +/* + * Window 0 registers + */ +#define XL_W0_EE_DATA 0x0C +#define XL_W0_EE_CMD 0x0A +#define XL_W0_RSRC_CFG 0x08 +#define XL_W0_ADDR_CFG 0x06 +#define XL_W0_CFG_CTRL 0x04 + +#define XL_W0_PROD_ID 0x02 +#define XL_W0_MFG_ID 0x00 + +/* + * Window 1 + */ + +#define XL_W1_TX_FIFO 0x10 + +#define XL_W1_FREE_TX 0x0C +#define XL_W1_TX_STATUS 0x0B +#define XL_W1_TX_TIMER 0x0A +#define XL_W1_RX_STATUS 0x08 +#define XL_W1_RX_FIFO 0x00 + +/* + * RX status codes + */ +#define XL_RXSTATUS_OVERRUN 0x01 +#define XL_RXSTATUS_RUNT 0x02 +#define XL_RXSTATUS_ALIGN 0x04 +#define XL_RXSTATUS_CRC 0x08 +#define XL_RXSTATUS_OVERSIZE 0x10 +#define XL_RXSTATUS_DRIBBLE 0x20 + +/* + * TX status codes + */ +#define XL_TXSTATUS_RECLAIM 0x02 /* 3c905B only */ +#define XL_TXSTATUS_OVERFLOW 0x04 +#define XL_TXSTATUS_MAXCOLS 0x08 +#define XL_TXSTATUS_UNDERRUN 0x10 +#define XL_TXSTATUS_JABBER 0x20 +#define XL_TXSTATUS_INTREQ 0x40 +#define XL_TXSTATUS_COMPLETE 0x80 + +/* + * Window 2 + */ +#define XL_W2_RESET_OPTIONS 0x0C /* 3c905B only */ +#define XL_W2_STATION_MASK_HI 0x0A +#define XL_W2_STATION_MASK_MID 0x08 +#define XL_W2_STATION_MASK_LO 0x06 +#define XL_W2_STATION_ADDR_HI 0x04 +#define XL_W2_STATION_ADDR_MID 0x02 +#define XL_W2_STATION_ADDR_LO 0x00 + +#define XL_RESETOPT_FEATUREMASK 0x0001|0x0002|0x004 +#define XL_RESETOPT_D3RESETDIS 0x0008 +#define XL_RESETOPT_DISADVFD 0x0010 +#define XL_RESETOPT_DISADV100 0x0020 +#define XL_RESETOPT_DISAUTONEG 0x0040 +#define XL_RESETOPT_DEBUGMODE 0x0080 +#define XL_RESETOPT_FASTAUTO 0x0100 +#define XL_RESETOPT_FASTEE 0x0200 +#define XL_RESETOPT_FORCEDCONF 0x0400 +#define XL_RESETOPT_TESTPDTPDR 0x0800 +#define XL_RESETOPT_TEST100TX 0x1000 +#define XL_RESETOPT_TEST100RX 0x2000 + +/* + * Window 3 (fifo management) + */ +#define XL_W3_INTERNAL_CFG 0x00 +#define XL_W3_RESET_OPT 0x08 +#define XL_W3_FREE_TX 0x0C +#define XL_W3_FREE_RX 0x0A +#define XL_W3_MAC_CTRL 0x06 + +#define XL_ICFG_CONNECTOR_MASK 0x00F00000 +#define XL_ICFG_CONNECTOR_BITS 20 + +#define XL_ICFG_RAMSIZE_MASK 0x00000007 +#define XL_ICFG_RAMWIDTH 0x00000008 +#define XL_ICFG_ROMSIZE_MASK (0x00000040|0x00000080) +#define XL_ICFG_DISABLE_BASSD 0x00000100 +#define XL_ICFG_RAMLOC 0x00000200 +#define XL_ICFG_RAMPART (0x00010000|0x00020000) +#define XL_ICFG_XCVRSEL (0x00100000|0x00200000|0x00400000) +#define XL_ICFG_AUTOSEL 0x01000000 + +#define XL_XCVR_10BT 0x00 +#define XL_XCVR_AUI 0x01 +#define XL_XCVR_RSVD_0 0x02 +#define XL_XCVR_COAX 0x03 +#define XL_XCVR_100BTX 0x04 +#define XL_XCVR_100BFX 0x05 +#define XL_XCVR_MII 0x06 +#define XL_XCVR_RSVD_1 0x07 +#define XL_XCVR_AUTO 0x08 /* 3c905B only */ +#define XL_XCVR_NWAY 0x09 /* 3CCFE575CT CardBus */ + +#define XL_MACCTRL_DEFER_EXT_END 0x0001 +#define XL_MACCTRL_DEFER_0 0x0002 +#define XL_MACCTRL_DEFER_1 0x0004 +#define XL_MACCTRL_DEFER_2 0x0008 +#define XL_MACCTRL_DEFER_3 0x0010 +#define XL_MACCTRL_DUPLEX 0x0020 +#define XL_MACCTRL_ALLOW_LARGE_PACK 0x0040 +#define XL_MACCTRL_EXTEND_AFTER_COL 0x0080 (3c905B only) +#define XL_MACCTRL_FLOW_CONTROL_ENB 0x0100 (3c905B only) +#define XL_MACCTRL_VLT_END 0x0200 (3c905B only) + +/* + * The 'reset options' register contains power-on reset values + * loaded from the EEPROM. This includes the supported media + * types on the card. It is also known as the media options register. + */ +#define XL_W3_MEDIA_OPT 0x08 + +#define XL_MEDIAOPT_BT4 0x0001 /* MII */ +#define XL_MEDIAOPT_BTX 0x0002 /* on-chip */ +#define XL_MEDIAOPT_BFX 0x0004 /* on-chip */ +#define XL_MEDIAOPT_BT 0x0008 /* on-chip */ +#define XL_MEDIAOPT_BNC 0x0010 /* on-chip */ +#define XL_MEDIAOPT_AUI 0x0020 /* on-chip */ +#define XL_MEDIAOPT_MII 0x0040 /* MII */ +#define XL_MEDIAOPT_VCO 0x0100 /* 1st gen chip only */ + +#define XL_MEDIAOPT_10FL 0x0100 /* 3x905B only, on-chip */ +#define XL_MEDIAOPT_MASK 0x01FF + +/* + * Window 4 (diagnostics) + */ +#define XL_W4_UPPERBYTESOK 0x0D +#define XL_W4_BADSSD 0x0C +#define XL_W4_MEDIA_STATUS 0x0A +#define XL_W4_PHY_MGMT 0x08 +#define XL_W4_NET_DIAG 0x06 +#define XL_W4_FIFO_DIAG 0x04 +#define XL_W4_VCO_DIAG 0x02 + +#define XL_W4_CTRLR_STAT 0x08 +#define XL_W4_TX_DIAG 0x00 + +#define XL_MII_CLK 0x01 +#define XL_MII_DATA 0x02 +#define XL_MII_DIR 0x04 + +#define XL_MEDIA_SQE 0x0008 +#define XL_MEDIA_10TP 0x00C0 +#define XL_MEDIA_LNK 0x0080 +#define XL_MEDIA_LNKBEAT 0x0800 + +#define XL_MEDIASTAT_CRCSTRIP 0x0004 +#define XL_MEDIASTAT_SQEENB 0x0008 +#define XL_MEDIASTAT_COLDET 0x0010 +#define XL_MEDIASTAT_CARRIER 0x0020 +#define XL_MEDIASTAT_JABGUARD 0x0040 +#define XL_MEDIASTAT_LINKBEAT 0x0080 +#define XL_MEDIASTAT_JABDETECT 0x0200 +#define XL_MEDIASTAT_POLREVERS 0x0400 +#define XL_MEDIASTAT_LINKDETECT 0x0800 +#define XL_MEDIASTAT_TXINPROG 0x1000 +#define XL_MEDIASTAT_DCENB 0x4000 +#define XL_MEDIASTAT_AUIDIS 0x8000 + +#define XL_NETDIAG_TEST_LOWVOLT 0x0001 +#define XL_NETDIAG_ASIC_REVMASK (0x0002|0x0004|0x0008|0x0010|0x0020) +#define XL_NETDIAG_UPPER_BYTES_ENABLE 0x0040 +#define XL_NETDIAG_STATS_ENABLED 0x0080 +#define XL_NETDIAG_TX_FATALERR 0x0100 +#define XL_NETDIAG_TRANSMITTING 0x0200 +#define XL_NETDIAG_RX_ENABLED 0x0400 +#define XL_NETDIAG_TX_ENABLED 0x0800 +#define XL_NETDIAG_FIFO_LOOPBACK 0x1000 +#define XL_NETDIAG_MAC_LOOPBACK 0x2000 +#define XL_NETDIAG_ENDEC_LOOPBACK 0x4000 +#define XL_NETDIAG_EXTERNAL_LOOP 0x8000 + +/* + * Window 5 + */ +#define XL_W5_STAT_ENB 0x0C +#define XL_W5_INTR_ENB 0x0A +#define XL_W5_RECLAIM_THRESH 0x09 /* 3c905B only */ +#define XL_W5_RX_FILTER 0x08 +#define XL_W5_RX_EARLYTHRESH 0x06 +#define XL_W5_TX_AVAILTHRESH 0x02 +#define XL_W5_TX_STARTTHRESH 0x00 + +/* + * RX filter bits + */ +#define XL_RXFILTER_INDIVIDUAL 0x01 +#define XL_RXFILTER_ALLMULTI 0x02 +#define XL_RXFILTER_BROADCAST 0x04 +#define XL_RXFILTER_ALLFRAMES 0x08 +#define XL_RXFILTER_MULTIHASH 0x10 /* 3c905B only */ + +/* + * Window 6 (stats) + */ +#define XL_W6_TX_BYTES_OK 0x0C +#define XL_W6_RX_BYTES_OK 0x0A +#define XL_W6_UPPER_FRAMES_OK 0x09 +#define XL_W6_DEFERRED 0x08 +#define XL_W6_RX_OK 0x07 +#define XL_W6_TX_OK 0x06 +#define XL_W6_RX_OVERRUN 0x05 +#define XL_W6_COL_LATE 0x04 +#define XL_W6_COL_SINGLE 0x03 +#define XL_W6_COL_MULTIPLE 0x02 +#define XL_W6_SQE_ERRORS 0x01 +#define XL_W6_CARRIER_LOST 0x00 + +/* + * Window 7 (bus master control) + */ +#define XL_W7_BM_ADDR 0x00 +#define XL_W7_BM_LEN 0x06 +#define XL_W7_BM_STATUS 0x0B +#define XL_W7_BM_TIMEr 0x0A + +/* + * bus master control registers + */ +#define XL_BM_PKTSTAT 0x20 +#define XL_BM_DOWNLISTPTR 0x24 +#define XL_BM_FRAGADDR 0x28 +#define XL_BM_FRAGLEN 0x2C +#define XL_BM_TXFREETHRESH 0x2F +#define XL_BM_UPPKTSTAT 0x30 +#define XL_BM_UPLISTPTR 0x38 + +#define XL_LAST_FRAG 0x80000000 + +/* + * Boomerang/Cyclone TX/RX list structure. + * For the TX lists, bits 0 to 12 of the status word indicate + * length. + * This looks suspiciously like the ThunderLAN, doesn't it. + */ +struct xl_frag { + u_int32_t xl_addr; /* 63 addr/len pairs */ + u_int32_t xl_len; +}; + +struct xl_list { + u_int32_t xl_next; /* final entry has 0 nextptr */ + u_int32_t xl_status; + struct xl_frag xl_frag[63]; +}; + +struct xl_list_onefrag { + u_int32_t xl_next; /* final entry has 0 nextptr */ + u_int32_t xl_status; + struct xl_frag xl_frag; +}; + +#define XL_MAXFRAGS 63 +#define XL_RX_LIST_CNT 32 +#define XL_TX_LIST_CNT 32 +#define XL_MIN_FRAMELEN 60 +#define XL_INC(x, y) (x) = (x + 1) % (y) + +struct xl_list_data { + struct xl_list_onefrag xl_rx_list[XL_RX_LIST_CNT]; + struct xl_list xl_tx_list[XL_TX_LIST_CNT]; + unsigned char xl_pad[XL_MIN_FRAMELEN]; +}; + +struct xl_chain { + struct xl_list *xl_ptr; + struct mbuf *xl_mbuf; + struct xl_chain *xl_next; + struct xl_chain *xl_prev; + u_int32_t xl_phys; +}; + +struct xl_chain_onefrag { + struct xl_list_onefrag *xl_ptr; + struct mbuf *xl_mbuf; + struct xl_chain_onefrag *xl_next; +}; + +struct xl_chain_data { + struct xl_chain_onefrag xl_rx_chain[XL_RX_LIST_CNT]; + struct xl_chain xl_tx_chain[XL_TX_LIST_CNT]; + + struct xl_chain_onefrag *xl_rx_head; + + /* 3c90x "boomerang" queuing stuff */ + struct xl_chain *xl_tx_head; + struct xl_chain *xl_tx_tail; + struct xl_chain *xl_tx_free; + + /* 3c90xB "cyclone/hurricane/tornade" stuff */ + int xl_tx_prod; + int xl_tx_cons; + int xl_tx_cnt; +}; + +#define XL_RXSTAT_LENMASK 0x00001FFF +#define XL_RXSTAT_UP_ERROR 0x00004000 +#define XL_RXSTAT_UP_CMPLT 0x00008000 +#define XL_RXSTAT_UP_OVERRUN 0x00010000 +#define XL_RXSTAT_RUNT 0x00020000 +#define XL_RXSTAT_ALIGN 0x00040000 +#define XL_RXSTAT_CRC 0x00080000 +#define XL_RXSTAT_OVERSIZE 0x00100000 +#define XL_RXSTAT_DRIBBLE 0x00800000 +#define XL_RXSTAT_UP_OFLOW 0x01000000 +#define XL_RXSTAT_IPCKERR 0x02000000 /* 3c905B only */ +#define XL_RXSTAT_TCPCKERR 0x04000000 /* 3c905B only */ +#define XL_RXSTAT_UDPCKERR 0x08000000 /* 3c905B only */ +#define XL_RXSTAT_BUFEN 0x10000000 /* 3c905B only */ +#define XL_RXSTAT_IPCKOK 0x20000000 /* 3c905B only */ +#define XL_RXSTAT_TCPCOK 0x40000000 /* 3c905B only */ +#define XL_RXSTAT_UDPCKOK 0x80000000 /* 3c905B only */ + +#define XL_TXSTAT_LENMASK 0x00001FFF +#define XL_TXSTAT_CRCDIS 0x00002000 +#define XL_TXSTAT_TX_INTR 0x00008000 +#define XL_TXSTAT_DL_COMPLETE 0x00010000 +#define XL_TXSTAT_IPCKSUM 0x02000000 /* 3c905B only */ +#define XL_TXSTAT_TCPCKSUM 0x04000000 /* 3c905B only */ +#define XL_TXSTAT_UDPCKSUM 0x08000000 /* 3c905B only */ +#define XL_TXSTAT_RND_DEFEAT 0x10000000 /* 3c905B only */ +#define XL_TXSTAT_EMPTY 0x20000000 /* 3c905B only */ +#define XL_TXSTAT_DL_INTR 0x80000000 + +#define XL_CAPABILITY_BM 0x20 + +struct xl_type { + u_int16_t xl_vid; + u_int16_t xl_did; + char *xl_name; +}; + +struct xl_mii_frame { + u_int8_t mii_stdelim; + u_int8_t mii_opcode; + u_int8_t mii_phyaddr; + u_int8_t mii_regaddr; + u_int8_t mii_turnaround; + u_int16_t mii_data; +}; + +/* + * MII constants + */ +#define XL_MII_STARTDELIM 0x01 +#define XL_MII_READOP 0x02 +#define XL_MII_WRITEOP 0x01 +#define XL_MII_TURNAROUND 0x02 + +/* + * The 3C905B adapters implement a few features that we want to + * take advantage of, namely the multicast hash filter. With older + * chips, you only have the option of turning on reception of all + * multicast frames, which is kind of lame. + * + * We also use this to decide on a transmit strategy. For the 3c90xB + * cards, we can use polled descriptor mode, which reduces CPU overhead. + */ +#define XL_TYPE_905B 1 +#define XL_TYPE_90X 2 + +struct xl_softc { + struct device sc_dev; /* generic device structure */ + void * xl_intrhand; /* interrupt handler cookie */ + struct arpcom arpcom; /* interface info */ + struct ifmedia ifmedia; /* media info */ + mii_data_t sc_mii; /* mii bus */ + bus_space_handle_t xl_bhandle; + bus_space_tag_t xl_btag; + bus_space_handle_t xl_funch; + bus_space_tag_t xl_funct; + struct xl_type *xl_info; /* 3Com adapter info */ + u_int8_t xl_hasmii; /* whether we have mii or not */ + u_int8_t xl_unit; /* interface number */ + u_int8_t xl_type; + u_int32_t xl_xcvr; + u_int16_t xl_media; + u_int16_t xl_caps; + u_int8_t xl_stats_no_timeout; + u_int16_t xl_tx_thresh; + u_int8_t xl_bustype; /* i.e., PCI or CardBus? */ + int xl_if_flags; + caddr_t xl_ldata_ptr; + struct xl_list_data *xl_ldata; + struct xl_chain_data xl_cdata; + void (*intr_ack) __P((struct xl_softc *)); +}; + +#define xl_rx_goodframes(x) \ + ((x.xl_upper_frames_ok & 0x03) << 8) | x.xl_rx_frames_ok + +#define xl_tx_goodframes(x) \ + ((x.xl_upper_frames_ok & 0x30) << 4) | x.xl_tx_frames_ok + +struct xl_stats { + u_int8_t xl_carrier_lost; + u_int8_t xl_sqe_errs; + u_int8_t xl_tx_multi_collision; + u_int8_t xl_tx_single_collision; + u_int8_t xl_tx_late_collision; + u_int8_t xl_rx_overrun; + u_int8_t xl_tx_frames_ok; + u_int8_t xl_rx_frames_ok; + u_int8_t xl_tx_deferred; + u_int8_t xl_upper_frames_ok; + u_int16_t xl_rx_bytes_ok; + u_int16_t xl_tx_bytes_ok; + u_int16_t status; +}; + +/* + * register space access macros + */ +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->xl_btag, sc->xl_bhandle, reg, val) +#define CSR_WRITE_2(sc, reg, val) \ + bus_space_write_2(sc->xl_btag, sc->xl_bhandle, reg, val) +#define CSR_WRITE_1(sc, reg, val) \ + bus_space_write_1(sc->xl_btag, sc->xl_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->xl_btag, sc->xl_bhandle, reg) +#define CSR_READ_2(sc, reg) \ + bus_space_read_2(sc->xl_btag, sc->xl_bhandle, reg) +#define CSR_READ_1(sc, reg) \ + bus_space_read_1(sc->xl_btag, sc->xl_bhandle, reg) + +#define XL_SEL_WIN(x) \ + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_WINSEL | x) +#define XL_TIMEOUT 1000 + +/* + * General constants that are fun to know. + * + * 3Com PCI vendor ID + */ +#define TC_VENDORID 0x10B7 + +/* + * 3Com chip device IDs. + */ +#define TC_DEVICEID_TORNADO_HOMECONNECT 0x4500 +#define TC_DEVICEID_BOOMERANG_10BT 0x9000 +#define TC_DEVICEID_BOOMERANG_10BT_COMBO 0x9001 +#define TC_DEVICEID_BOOMERANG_10_100BT 0x9050 +#define TC_DEVICEID_BOOMERANG_100BT4 0x9051 +#define TC_DEVICEID_KRAKATOA_10BT 0x9004 +#define TC_DEVICEID_KRAKATOA_10BT_COMBO 0x9005 +#define TC_DEVICEID_KRAKATOA_10BT_TPC 0x9006 +#define TC_DEVICEID_CYCLONE_10FL 0x900A +#define TC_DEVICEID_HURRICANE_10_100BT 0x9055 +#define TC_DEVICEID_CYCLONE_10_100BT4 0x9056 +#define TC_DEVICEID_CYCLONE_10_100_COMBO 0x9058 +#define TC_DEVICEID_CYCLONE_10_100FX 0x905A +#define TC_DEVICEID_TORNADO_10_100BT 0x9200 +#define TC_DEVICEID_HURRICANE_10_100BT_SERV 0x9800 +#define TC_DEVICEID_TORNADO_10_100BT_SERV 0x9805 +#define TC_DEVICEID_HURRICANE_SOHO100TX 0x7646 +#define TC_DEVICEID_3CCFE575CT_CARDBUS 0x5257 + +/* + * PCI low memory base and low I/O base register, and + * other PCI registers. Note: some are only available on + * the 3c905B, in particular those that related to power management. + */ + +#define XL_PCI_VENDOR_ID 0x00 +#define XL_PCI_DEVICE_ID 0x02 +#define XL_PCI_COMMAND 0x04 +#define XL_PCI_STATUS 0x06 +#define XL_PCI_CLASSCODE 0x09 +#define XL_PCI_LATENCY_TIMER 0x0D +#define XL_PCI_HEADER_TYPE 0x0E +#define XL_PCI_LOIO 0x10 +#define XL_PCI_LOMEM 0x14 +#define XL_PCI_BIOSROM 0x30 +#define XL_PCI_INTLINE 0x3C +#define XL_PCI_INTPIN 0x3D +#define XL_PCI_MINGNT 0x3E +#define XL_PCI_MINLAT 0x0F +#define XL_PCI_RESETOPT 0x48 +#define XL_PCI_EEPROM_DATA 0x4C + +/* 3c905B-only registers */ +#define XL_PCI_CAPID 0xDC /* 8 bits */ +#define XL_PCI_NEXTPTR 0xDD /* 8 bits */ +#define XL_PCI_PWRMGMTCAP 0xDE /* 16 bits */ +#define XL_PCI_PWRMGMTCTRL 0xE0 /* 16 bits */ + +#define XL_PSTATE_MASK 0x0003 +#define XL_PSTATE_D0 0x0000 +#define XL_PSTATE_D1 0x0002 +#define XL_PSTATE_D2 0x0002 +#define XL_PSTATE_D3 0x0003 +#define XL_PME_EN 0x0010 +#define XL_PME_STATUS 0x8000 + +#ifdef __alpha__ +#undef vtophys +#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) +#endif + +#ifndef ETHER_ALIGN +#define ETHER_ALIGN 2 +#endif + +extern int xl_intr __P((void *)); +extern void xl_attach __P((struct xl_softc *)); diff --git a/sys/dev/isa/i82365_isasubr.c b/sys/dev/isa/i82365_isasubr.c index 5ceb3ef4bd7..929239cd995 100644 --- a/sys/dev/isa/i82365_isasubr.c +++ b/sys/dev/isa/i82365_isasubr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: i82365_isasubr.c,v 1.10 1999/08/11 12:02:07 niklas Exp $ */ +/* $OpenBSD: i82365_isasubr.c,v 1.11 2000/04/08 05:50:53 aaron Exp $ */ /* $NetBSD: i82365_isasubr.c,v 1.1 1998/06/07 18:28:31 sommerfe Exp $ */ /* @@ -192,7 +192,8 @@ pcic_isa_chip_intr_establish(pch, pf, ipl, fct, arg) void *arg; { struct pcic_handle *h = (struct pcic_handle *)pch; - isa_chipset_tag_t ic = h->sc->intr_est; + struct pcic_softc *sc = (struct pcic_softc *)(h->ph_parent); + isa_chipset_tag_t ic = sc->intr_est; int irq, ist; void *ih; @@ -203,7 +204,7 @@ pcic_isa_chip_intr_establish(pch, pf, ipl, fct, arg) else ist = IST_LEVEL; - irq = pcic_intr_find(h->sc, ist); + irq = pcic_intr_find(sc, ist); if (!irq) return (NULL); @@ -226,7 +227,8 @@ pcic_isa_chip_intr_disestablish(pch, ih) void *ih; { struct pcic_handle *h = (struct pcic_handle *) pch; - isa_chipset_tag_t ic = h->sc->intr_est; + struct pcic_softc *sc = (struct pcic_softc *)(h->ph_parent); + isa_chipset_tag_t ic = sc->intr_est; int reg; h->ih_irq = 0; diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index a7190a278df..a60e99daeef 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.67 2000/03/27 00:34:15 aaron Exp $ +# $OpenBSD: files.pci,v 1.68 2000/04/08 05:50:51 aaron Exp $ # $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $ # # Config file and device description for machine-independent PCI code. @@ -161,9 +161,8 @@ attach bktr at pci file dev/pci/brooktree848.c bktr needs-count # 3C90x -device xl: ether, ifnet, mii, ifmedia -attach xl at pci -file dev/pci/if_xl.c xl +attach xl at pci with xl_pci +file dev/pci/if_xl_pci.c xl_pci # SMC EPIC, 83c170 device tx: ether, ifnet, ifmedia @@ -222,6 +221,12 @@ file dev/pci/uhci_pci.c uhci attach ohci at pci with ohci_pci file dev/pci/ohci_pci.c ohci +# YENTA PCI-CardBus bridge +#device cbb: cbbus, pcmciabus +device cbb: pcmciaslot +attach cbb at pci with cbb_pci +file dev/pci/pccbb.c cbb + # SysKonnect 984x gigabit ethernet device skc {} attach skc at pci diff --git a/sys/dev/pci/if_xl.c b/sys/dev/pci/if_xl.c deleted file mode 100644 index a06904cbfb8..00000000000 --- a/sys/dev/pci/if_xl.c +++ /dev/null @@ -1,2722 +0,0 @@ -/* $OpenBSD: if_xl.c,v 1.38 2000/02/15 13:47:52 jason Exp $ */ - -/* - * Copyright (c) 1997, 1998, 1999 - * Bill Paul . 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 Bill Paul. - * 4. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD - * 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: if_xl.c,v 1.72 2000/01/09 21:12:59 wpaul Exp $ - */ - -/* - * 3Com 3c90x Etherlink XL PCI NIC driver - * - * Supports the 3Com "boomerang", "cyclone", and "hurricane" PCI - * bus-master chips (3c90x cards and embedded controllers) including - * the following: - * - * 3Com 3c900-TPO 10Mbps/RJ-45 - * 3Com 3c900-COMBO 10Mbps/RJ-45,AUI,BNC - * 3Com 3c905-TX 10/100Mbps/RJ-45 - * 3Com 3c905-T4 10/100Mbps/RJ-45 - * 3Com 3c900B-TPO 10Mbps/RJ-45 - * 3Com 3c900B-COMBO 10Mbps/RJ-45,AUI,BNC - * 3Com 3c900B-TPC 10Mbps/RJ-45,BNC - * 3Com 3c900B-FL 10Mbps/Fiber-optic - * 3Com 3c905B-COMBO 10/100Mbps/RJ-45,AUI,BNC - * 3Com 3c905B-TX 10/100Mbps/RJ-45 - * 3Com 3c900-FL/FX 10/100Mbps/Fiber-optic - * 3Com 3c905C-TX 10/100Mbps/RJ-45 (Tornado ASIC) - * 3Com 3c450-TX 10/100Mbps/RJ-45 (Tornado ASIC) - * 3Com 3c980-TX 10/100Mbps server adapter (Hurricane ASIC) - * 3Com 3c980C-TX 10/100Mbps server adapter (Tornado ASIC) - * 3Com 3cSOHO100-TX 10/100Mbps/RJ-45 (Hurricane ASIC) - * Dell Optiplex GX1 on-board 3c918 10/100Mbps/RJ-45 - * Dell on-board 3c920 10/100Mbps/RJ-45 - * Dell Precision on-board 3c905B 10/100Mbps/RJ-45 - * Dell Latitude laptop docking station embedded 3c905-TX - * - * Written by Bill Paul - * Electrical Engineering Department - * Columbia University, New York City - */ - -/* - * The 3c90x series chips use a bus-master DMA interface for transfering - * packets to and from the controller chip. Some of the "vortex" cards - * (3c59x) also supported a bus master mode, however for those chips - * you could only DMA packets to/from a contiguous memory buffer. For - * transmission this would mean copying the contents of the queued mbuf - * chain into a an mbuf cluster and then DMAing the cluster. This extra - * copy would sort of defeat the purpose of the bus master support for - * any packet that doesn't fit into a single mbuf. - * - * By contrast, the 3c90x cards support a fragment-based bus master - * mode where mbuf chains can be encapsulated using TX descriptors. - * This is similar to other PCI chips such as the Texas Instruments - * ThunderLAN and the Intel 82557/82558. - * - * The "vortex" driver (if_vx.c) happens to work for the "boomerang" - * bus master chips because they maintain the old PIO interface for - * backwards compatibility, but starting with the 3c905B and the - * "cyclone" chips, the compatibility interface has been dropped. - * Since using bus master DMA is a big win, we use this driver to - * support the PCI "boomerang" chips even though they work with the - * "vortex" driver in order to obtain better performance. - * - * This driver is in the /sys/pci directory because it only supports - * PCI-based NICs. - */ - -#include "bpfilter.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* only for declaration of wakeup() used by vm.h */ -#include - -#include -#include -#include -#include - -#ifdef INET -#include -#include -#include -#include -#include -#endif - -#include -#include -#include -#include -#include - -#if NBPFILTER > 0 -#include -#endif - -#include /* for vtophys */ -#include /* for vtophys */ - -/* - * The following #define causes the code to use PIO to access the - * chip's registers instead of memory mapped mode. The reason PIO mode - * is on by default is that the Etherlink XL manual seems to indicate - * that only the newer revision chips (3c905B) support both PIO and - * memory mapped access. Since we want to be compatible with the older - * bus master chips, we use PIO here. If you comment this out, the - * driver will use memory mapped I/O, which may be faster but which - * might not work on some devices. - */ -#define XL_USEIOSPACE - -#include - -int xl_probe __P((struct device *, void *, void *)); -void xl_attach __P((struct device *, struct device *, void *)); - -int xl_newbuf __P((struct xl_softc *, struct xl_chain_onefrag *)); -void xl_stats_update __P((void *)); -int xl_encap __P((struct xl_softc *, struct xl_chain *, - struct mbuf * )); -int xl_encap_90xB __P((struct xl_softc *, struct xl_chain *, - struct mbuf * )); -void xl_rxeof __P((struct xl_softc *)); -int xl_rx_resync __P((struct xl_softc *)); -void xl_txeof __P((struct xl_softc *)); -void xl_txeof_90xB __P((struct xl_softc *)); -void xl_txeoc __P((struct xl_softc *)); -int xl_intr __P((void *)); -void xl_start __P((struct ifnet *)); -void xl_start_90xB __P((struct ifnet *)); -int xl_ioctl __P((struct ifnet *, u_long, caddr_t)); -void xl_init __P((void *)); -void xl_stop __P((struct xl_softc *)); -void xl_watchdog __P((struct ifnet *)); -void xl_shutdown __P((void *)); -int xl_ifmedia_upd __P((struct ifnet *)); -void xl_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); - -int xl_eeprom_wait __P((struct xl_softc *)); -int xl_read_eeprom __P((struct xl_softc *, caddr_t, int, int, int)); -void xl_mii_sync __P((struct xl_softc *)); -void xl_mii_send __P((struct xl_softc *, u_int32_t, int)); -int xl_mii_readreg __P((struct xl_softc *, struct xl_mii_frame *)); -int xl_mii_writereg __P((struct xl_softc *, struct xl_mii_frame *)); - -void xl_setcfg __P((struct xl_softc *)); -void xl_setmode __P((struct xl_softc *, int)); -u_int8_t xl_calchash __P((caddr_t)); -void xl_setmulti __P((struct xl_softc *)); -void xl_setmulti_hash __P((struct xl_softc *)); -void xl_reset __P((struct xl_softc *, int)); -int xl_list_rx_init __P((struct xl_softc *)); -int xl_list_tx_init __P((struct xl_softc *)); -int xl_list_tx_init_90xB __P((struct xl_softc *)); -void xl_wait __P((struct xl_softc *)); -void xl_mediacheck __P((struct xl_softc *)); -void xl_choose_xcvr __P((struct xl_softc *, int)); -#ifdef notdef -void xl_testpacket __P((struct xl_softc *)); -#endif - -int xl_miibus_readreg __P((struct device *, int, int)); -void xl_miibus_writereg __P((struct device *, int, int, int)); -void xl_miibus_statchg __P((struct device *)); - -/* - * Murphy's law says that it's possible the chip can wedge and - * the 'command in progress' bit may never clear. Hence, we wait - * only a finite amount of time to avoid getting caught in an - * infinite loop. Normally this delay routine would be a macro, - * but it isn't called during normal operation so we can afford - * to make it a function. - */ -void xl_wait(sc) - struct xl_softc *sc; -{ - register int i; - - for (i = 0; i < XL_TIMEOUT; i++) { - if (!(CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY)) - break; - } - -#ifdef DIAGNOSTIC - if (i == XL_TIMEOUT) - printf("xl%d: command never completed!\n", sc->xl_unit); -#endif - - return; -} - -/* - * MII access routines are provided for adapters with external - * PHYs (3c905-TX, 3c905-T4, 3c905B-T4) and those with built-in - * autoneg logic that's faked up to look like a PHY (3c905B-TX). - * Note: if you don't perform the MDIO operations just right, - * it's possible to end up with code that works correctly with - * some chips/CPUs/processor speeds/bus speeds/etc but not - * with others. - */ -#define MII_SET(x) \ - CSR_WRITE_2(sc, XL_W4_PHY_MGMT, \ - CSR_READ_2(sc, XL_W4_PHY_MGMT) | x) - -#define MII_CLR(x) \ - CSR_WRITE_2(sc, XL_W4_PHY_MGMT, \ - CSR_READ_2(sc, XL_W4_PHY_MGMT) & ~x) - -/* - * Sync the PHYs by setting data bit and strobing the clock 32 times. - */ -void xl_mii_sync(sc) - struct xl_softc *sc; -{ - register int i; - - XL_SEL_WIN(4); - MII_SET(XL_MII_DIR|XL_MII_DATA); - - for (i = 0; i < 32; i++) { - MII_SET(XL_MII_CLK); - DELAY(1); - MII_CLR(XL_MII_CLK); - DELAY(1); - } - - return; -} - -/* - * Clock a series of bits through the MII. - */ -void xl_mii_send(sc, bits, cnt) - struct xl_softc *sc; - u_int32_t bits; - int cnt; -{ - int i; - - XL_SEL_WIN(4); - MII_CLR(XL_MII_CLK); - - for (i = (0x1 << (cnt - 1)); i; i >>= 1) { - if (bits & i) { - MII_SET(XL_MII_DATA); - } else { - MII_CLR(XL_MII_DATA); - } - DELAY(1); - MII_CLR(XL_MII_CLK); - DELAY(1); - MII_SET(XL_MII_CLK); - } -} - -/* - * Read an PHY register through the MII. - */ -int xl_mii_readreg(sc, frame) - struct xl_softc *sc; - struct xl_mii_frame *frame; - -{ - int i, ack, s; - - s = splimp(); - - /* - * Set up frame for RX. - */ - frame->mii_stdelim = XL_MII_STARTDELIM; - frame->mii_opcode = XL_MII_READOP; - frame->mii_turnaround = 0; - frame->mii_data = 0; - - /* - * Select register window 4. - */ - - XL_SEL_WIN(4); - - CSR_WRITE_2(sc, XL_W4_PHY_MGMT, 0); - /* - * Turn on data xmit. - */ - MII_SET(XL_MII_DIR); - - xl_mii_sync(sc); - - /* - * Send command/address info. - */ - xl_mii_send(sc, frame->mii_stdelim, 2); - xl_mii_send(sc, frame->mii_opcode, 2); - xl_mii_send(sc, frame->mii_phyaddr, 5); - xl_mii_send(sc, frame->mii_regaddr, 5); - - /* Idle bit */ - MII_CLR((XL_MII_CLK|XL_MII_DATA)); - DELAY(1); - MII_SET(XL_MII_CLK); - DELAY(1); - - /* Turn off xmit. */ - MII_CLR(XL_MII_DIR); - - /* Check for ack */ - MII_CLR(XL_MII_CLK); - DELAY(1); - MII_SET(XL_MII_CLK); - DELAY(1); - ack = CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA; - - /* - * Now try reading data bits. If the ack failed, we still - * need to clock through 16 cycles to keep the PHY(s) in sync. - */ - if (ack) { - for(i = 0; i < 16; i++) { - MII_CLR(XL_MII_CLK); - DELAY(1); - MII_SET(XL_MII_CLK); - DELAY(1); - } - goto fail; - } - - for (i = 0x8000; i; i >>= 1) { - MII_CLR(XL_MII_CLK); - DELAY(1); - if (!ack) { - if (CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA) - frame->mii_data |= i; - DELAY(1); - } - MII_SET(XL_MII_CLK); - DELAY(1); - } - -fail: - - MII_CLR(XL_MII_CLK); - DELAY(1); - MII_SET(XL_MII_CLK); - DELAY(1); - - splx(s); - - if (ack) - return(1); - return(0); -} - -/* - * Write to a PHY register through the MII. - */ -int xl_mii_writereg(sc, frame) - struct xl_softc *sc; - struct xl_mii_frame *frame; - -{ - int s; - - s = splimp(); - /* - * Set up frame for TX. - */ - - frame->mii_stdelim = XL_MII_STARTDELIM; - frame->mii_opcode = XL_MII_WRITEOP; - frame->mii_turnaround = XL_MII_TURNAROUND; - - /* - * Select the window 4. - */ - XL_SEL_WIN(4); - - /* - * Turn on data output. - */ - MII_SET(XL_MII_DIR); - - xl_mii_sync(sc); - - xl_mii_send(sc, frame->mii_stdelim, 2); - xl_mii_send(sc, frame->mii_opcode, 2); - xl_mii_send(sc, frame->mii_phyaddr, 5); - xl_mii_send(sc, frame->mii_regaddr, 5); - xl_mii_send(sc, frame->mii_turnaround, 2); - xl_mii_send(sc, frame->mii_data, 16); - - /* Idle bit. */ - MII_SET(XL_MII_CLK); - DELAY(1); - MII_CLR(XL_MII_CLK); - DELAY(1); - - /* - * Turn off xmit. - */ - MII_CLR(XL_MII_DIR); - - splx(s); - - return(0); -} - -int -xl_miibus_readreg(self, phy, reg) - struct device *self; - int phy, reg; -{ - struct xl_softc *sc = (struct xl_softc *)self; - struct xl_mii_frame frame; - - if (phy != 24) - return (0); - - bzero((char *)&frame, sizeof(frame)); - - frame.mii_phyaddr = phy; - frame.mii_regaddr = reg; - xl_mii_readreg(sc, &frame); - - return(frame.mii_data); -} - -void -xl_miibus_writereg(self, phy, reg, data) - struct device *self; - int phy, reg, data; -{ - struct xl_softc *sc = (struct xl_softc *)self; - struct xl_mii_frame frame; - - if (phy != 24) - return; - - bzero((char *)&frame, sizeof(frame)); - - frame.mii_phyaddr = phy; - frame.mii_regaddr = reg; - frame.mii_data = data; - - xl_mii_writereg(sc, &frame); -} - -void -xl_miibus_statchg(self) - struct device *self; -{ - struct xl_softc *sc = (struct xl_softc *)self; - - xl_setcfg(sc); - - XL_SEL_WIN(3); - if ((sc->sc_mii.mii_media_active & IFM_GMASK) == IFM_FDX) - CSR_WRITE_1(sc, XL_W3_MAC_CTRL, XL_MACCTRL_DUPLEX); - else - CSR_WRITE_1(sc, XL_W3_MAC_CTRL, - (CSR_READ_1(sc, XL_W3_MAC_CTRL) & ~XL_MACCTRL_DUPLEX)); -} - -/* - * The EEPROM is slow: give it time to come ready after issuing - * it a command. - */ -int xl_eeprom_wait(sc) - struct xl_softc *sc; -{ - int i; - - for (i = 0; i < 100; i++) { - if (CSR_READ_2(sc, XL_W0_EE_CMD) & XL_EE_BUSY) - DELAY(162); - else - break; - } - - if (i == 100) { - printf("xl%d: eeprom failed to come ready\n", sc->xl_unit); - return(1); - } - - return(0); -} - -/* - * Read a sequence of words from the EEPROM. Note that ethernet address - * data is stored in the EEPROM in network byte order. - */ -int xl_read_eeprom(sc, dest, off, cnt, swap) - struct xl_softc *sc; - caddr_t dest; - int off; - int cnt; - int swap; -{ - int err = 0, i; - u_int16_t word = 0, *ptr; - - XL_SEL_WIN(0); - - if (xl_eeprom_wait(sc)) - return(1); - - for (i = 0; i < cnt; i++) { - CSR_WRITE_2(sc, XL_W0_EE_CMD, XL_EE_READ | (off + i)); - err = xl_eeprom_wait(sc); - if (err) - break; - word = CSR_READ_2(sc, XL_W0_EE_DATA); - ptr = (u_int16_t *)(dest + (i * 2)); - if (swap) - *ptr = ntohs(word); - else - *ptr = word; - } - - return(err ? 1 : 0); -} - -/* - * This routine is taken from the 3Com Etherlink XL manual, - * page 10-7. It calculates a CRC of the supplied multicast - * group address and returns the lower 8 bits, which are used - * as the multicast filter position. - * Note: the 3c905B currently only supports a 64-bit hash table, - * which means we really only need 6 bits, but the manual indicates - * that future chip revisions will have a 256-bit hash table, - * hence the routine is set up to calculate 8 bits of position - * info in case we need it some day. - * Note II, The Sequel: _CURRENT_ versions of the 3c905B have a - * 256 bit hash table. This means we have to use all 8 bits regardless. - * On older cards, the upper 2 bits will be ignored. Grrrr.... - */ -u_int8_t xl_calchash(addr) - caddr_t addr; -{ - u_int32_t crc, carry; - int i, j; - u_int8_t c; - - /* Compute CRC for the address value. */ - crc = 0xFFFFFFFF; /* initial value */ - - for (i = 0; i < 6; i++) { - c = *(addr + i); - for (j = 0; j < 8; j++) { - carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); - crc <<= 1; - c >>= 1; - if (carry) - crc = (crc ^ 0x04c11db6) | carry; - } - } - - /* return the filter bit position */ - return(crc & 0x000000FF); -} - -/* - * NICs older than the 3c905B have only one multicast option, which - * is to enable reception of all multicast frames. - */ -void xl_setmulti(sc) - struct xl_softc *sc; -{ - struct ifnet *ifp; - struct arpcom *ac = &sc->arpcom; - struct ether_multi *enm; - struct ether_multistep step; - u_int8_t rxfilt; - int mcnt = 0; - - ifp = &sc->arpcom.ac_if; - - XL_SEL_WIN(5); - rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER); - - if (ifp->if_flags & IFF_ALLMULTI) { - rxfilt |= XL_RXFILTER_ALLMULTI; - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); - return; - } - - ETHER_FIRST_MULTI(step, ac, enm); - while (enm != NULL) { - mcnt++; - ETHER_NEXT_MULTI(step, enm); - } - - if (mcnt) - rxfilt |= XL_RXFILTER_ALLMULTI; - else - rxfilt &= ~XL_RXFILTER_ALLMULTI; - - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); - - return; -} - -/* - * 3c905B adapters have a hash filter that we can program. - */ -void xl_setmulti_hash(sc) - struct xl_softc *sc; -{ - struct ifnet *ifp; - int h = 0, i; - struct arpcom *ac = &sc->arpcom; - struct ether_multi *enm; - struct ether_multistep step; - u_int8_t rxfilt; - int mcnt = 0; - - ifp = &sc->arpcom.ac_if; - - XL_SEL_WIN(5); - rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER); - - if (ifp->if_flags & IFF_ALLMULTI) { -allmulti: - rxfilt |= XL_RXFILTER_ALLMULTI; - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); - return; - } else - rxfilt &= ~XL_RXFILTER_ALLMULTI; - - - /* first, zot all the existing hash bits */ - for (i = 0; i < XL_HASHFILT_SIZE; i++) - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_HASH|i); - - /* now program new ones */ - ETHER_FIRST_MULTI(step, ac, enm); - while (enm != NULL) { - if (bcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) { - ifp->if_flags |= IFF_ALLMULTI; - goto allmulti; - } - h = xl_calchash(enm->enm_addrlo); - mcnt++; - ETHER_NEXT_MULTI(step, enm); - } - - if (mcnt) - rxfilt |= XL_RXFILTER_MULTIHASH; - else - rxfilt &= ~XL_RXFILTER_MULTIHASH; - - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); - - return; -} - -#ifdef notdef -void xl_testpacket(sc) - struct xl_softc *sc; -{ - struct mbuf *m; - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - - MGETHDR(m, M_DONTWAIT, MT_DATA); - - if (m == NULL) - return; - - bcopy(&sc->arpcom.ac_enaddr, - mtod(m, struct ether_header *)->ether_dhost, ETHER_ADDR_LEN); - bcopy(&sc->arpcom.ac_enaddr, - mtod(m, struct ether_header *)->ether_shost, ETHER_ADDR_LEN); - mtod(m, struct ether_header *)->ether_type = htons(3); - mtod(m, unsigned char *)[14] = 0; - mtod(m, unsigned char *)[15] = 0; - mtod(m, unsigned char *)[16] = 0xE3; - m->m_len = m->m_pkthdr.len = sizeof(struct ether_header) + 3; - IF_ENQUEUE(&ifp->if_snd, m); - xl_start(ifp); - - return; -} -#endif - -void xl_setcfg(sc) - struct xl_softc *sc; -{ - u_int32_t icfg; - - XL_SEL_WIN(3); - icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG); - icfg &= ~XL_ICFG_CONNECTOR_MASK; - if (sc->xl_media & XL_MEDIAOPT_MII || - sc->xl_media & XL_MEDIAOPT_BT4) - icfg |= (XL_XCVR_MII << XL_ICFG_CONNECTOR_BITS); - if (sc->xl_media & XL_MEDIAOPT_BTX) - icfg |= (XL_XCVR_AUTO << XL_ICFG_CONNECTOR_BITS); - - CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg); - CSR_WRITE_4(sc, XL_COMMAND, XL_CMD_COAX_STOP); -} - -void xl_setmode(sc, media) - struct xl_softc *sc; - int media; -{ - u_int32_t icfg; - u_int16_t mediastat; - - printf("xl%d: selecting ", sc->xl_unit); - - XL_SEL_WIN(4); - mediastat = CSR_READ_2(sc, XL_W4_MEDIA_STATUS); - XL_SEL_WIN(3); - icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG); - - if (sc->xl_media & XL_MEDIAOPT_BT) { - if (IFM_SUBTYPE(media) == IFM_10_T) { - printf("10baseT transceiver, "); - sc->xl_xcvr = XL_XCVR_10BT; - icfg &= ~XL_ICFG_CONNECTOR_MASK; - icfg |= (XL_XCVR_10BT << XL_ICFG_CONNECTOR_BITS); - mediastat |= XL_MEDIASTAT_LINKBEAT| - XL_MEDIASTAT_JABGUARD; - mediastat &= ~XL_MEDIASTAT_SQEENB; - } - } - - if (sc->xl_media & XL_MEDIAOPT_BFX) { - if (IFM_SUBTYPE(media) == IFM_100_FX) { - printf("100baseFX port, "); - sc->xl_xcvr = XL_XCVR_100BFX; - icfg &= ~XL_ICFG_CONNECTOR_MASK; - icfg |= (XL_XCVR_100BFX << XL_ICFG_CONNECTOR_BITS); - mediastat |= XL_MEDIASTAT_LINKBEAT; - mediastat &= ~XL_MEDIASTAT_SQEENB; - } - } - - if (sc->xl_media & (XL_MEDIAOPT_AUI|XL_MEDIAOPT_10FL)) { - if (IFM_SUBTYPE(media) == IFM_10_5) { - printf("AUI port, "); - sc->xl_xcvr = XL_XCVR_AUI; - icfg &= ~XL_ICFG_CONNECTOR_MASK; - icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS); - mediastat &= ~(XL_MEDIASTAT_LINKBEAT| - XL_MEDIASTAT_JABGUARD); - mediastat |= ~XL_MEDIASTAT_SQEENB; - } - if (IFM_SUBTYPE(media) == IFM_10_FL) { - printf("10baseFL transceiver, "); - sc->xl_xcvr = XL_XCVR_AUI; - icfg &= ~XL_ICFG_CONNECTOR_MASK; - icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS); - mediastat &= ~(XL_MEDIASTAT_LINKBEAT| - XL_MEDIASTAT_JABGUARD); - mediastat |= ~XL_MEDIASTAT_SQEENB; - } - } - - if (sc->xl_media & XL_MEDIAOPT_BNC) { - if (IFM_SUBTYPE(media) == IFM_10_2) { - printf("BNC port, "); - sc->xl_xcvr = XL_XCVR_COAX; - icfg &= ~XL_ICFG_CONNECTOR_MASK; - icfg |= (XL_XCVR_COAX << XL_ICFG_CONNECTOR_BITS); - mediastat &= ~(XL_MEDIASTAT_LINKBEAT| - XL_MEDIASTAT_JABGUARD| - XL_MEDIASTAT_SQEENB); - } - } - - if ((media & IFM_GMASK) == IFM_FDX || - IFM_SUBTYPE(media) == IFM_100_FX) { - printf("full duplex\n"); - XL_SEL_WIN(3); - CSR_WRITE_1(sc, XL_W3_MAC_CTRL, XL_MACCTRL_DUPLEX); - } else { - printf("half duplex\n"); - XL_SEL_WIN(3); - CSR_WRITE_1(sc, XL_W3_MAC_CTRL, - (CSR_READ_1(sc, XL_W3_MAC_CTRL) & ~XL_MACCTRL_DUPLEX)); - } - - if (IFM_SUBTYPE(media) == IFM_10_2) - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START); - else - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); - CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg); - XL_SEL_WIN(4); - CSR_WRITE_2(sc, XL_W4_MEDIA_STATUS, mediastat); - DELAY(800); - XL_SEL_WIN(7); -} - -void xl_reset(sc, hard) - struct xl_softc *sc; -{ - register int i; - - XL_SEL_WIN(0); - if (hard) - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RESET); - else - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RESET | 0x0010); - xl_wait(sc); - - for (i = 0; i < XL_TIMEOUT; i++) { - DELAY(10); - if (!(CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY)) - break; - } - - DELAY(100000); - - /* Reset TX and RX. */ - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET); - xl_wait(sc); - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); - xl_wait(sc); - - /* Wait a little while for the chip to get its brains in order. */ - DELAY(100000); - return; -} - -int -xl_probe(parent, match, aux) - struct device *parent; - void *match; - void *aux; -{ - struct pci_attach_args *pa = (struct pci_attach_args *) aux; - - if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_3COM) - return (0); - - switch (PCI_PRODUCT(pa->pa_id)) { - case PCI_PRODUCT_3COM_3CSOHO100TX: - case PCI_PRODUCT_3COM_3C900TPO: - case PCI_PRODUCT_3COM_3C900COMBO: - case PCI_PRODUCT_3COM_3C900B: - case PCI_PRODUCT_3COM_3C900BCOMBO: - case PCI_PRODUCT_3COM_3C900BTPC: - case PCI_PRODUCT_3COM_3C900BFL: - case PCI_PRODUCT_3COM_3C905TX: - case PCI_PRODUCT_3COM_3C905T4: - case PCI_PRODUCT_3COM_3C905BTX: - case PCI_PRODUCT_3COM_3C905BT4: - case PCI_PRODUCT_3COM_3C905BCOMBO: - case PCI_PRODUCT_3COM_3C905BFX: - case PCI_PRODUCT_3COM_3C980TX: - case PCI_PRODUCT_3COM_3C980CTX: - case PCI_PRODUCT_3COM_3C905CTX: - case PCI_PRODUCT_3COM_3C450: - return (1); - } - - return (0); -} - -/* - * This routine is a kludge to work around possible hardware faults - * or manufacturing defects that can cause the media options register - * (or reset options register, as it's called for the first generation - * 3c90x adapters) to return an incorrect result. I have encountered - * one Dell Latitude laptop docking station with an integrated 3c905-TX - * which doesn't have any of the 'mediaopt' bits set. This screws up - * the attach routine pretty badly because it doesn't know what media - * to look for. If we find ourselves in this predicament, this routine - * will try to guess the media options values and warn the user of a - * possible manufacturing defect with his adapter/system/whatever. - */ -void xl_mediacheck(sc) - struct xl_softc *sc; -{ - /* - * If some of the media options bits are set, assume they are - * correct. If not, try to figure it out down below. - * XXX I should check for 10baseFL, but I don't have an adapter - * to test with. - */ - if (sc->xl_media & (XL_MEDIAOPT_MASK & ~XL_MEDIAOPT_VCO)) { - /* - * Check the XCVR value. If it's not in the normal range - * of values, we need to fake it up here. - */ - if (sc->xl_xcvr <= XL_XCVR_AUTO) - return; - else { - printf("xl%d: bogus xcvr value " - "in EEPROM (%x)\n", sc->xl_unit, sc->xl_xcvr); - printf("xl%d: choosing new default based " - "on card type\n", sc->xl_unit); - } - } else { - if (sc->xl_type == XL_TYPE_905B && - sc->xl_media & XL_MEDIAOPT_10FL) - return; - printf("xl%d: WARNING: no media options bits set in " - "the media options register!!\n", sc->xl_unit); - printf("xl%d: this could be a manufacturing defect in " - "your adapter or system\n", sc->xl_unit); - printf("xl%d: attempting to guess media type; you " - "should probably consult your vendor\n", sc->xl_unit); - } - - xl_choose_xcvr(sc, 1); -} - -void xl_choose_xcvr(sc, verbose) - struct xl_softc *sc; - int verbose; -{ - u_int16_t devid; - - /* - * Read the device ID from the EEPROM. - * This is what's loaded into the PCI device ID register, so it has - * to be correct otherwise we wouldn't have gotten this far. - */ - xl_read_eeprom(sc, (caddr_t)&devid, XL_EE_PRODID, 1, 0); - - switch(devid) { - case TC_DEVICEID_BOOMERANG_10BT: /* 3c900-TPO */ - case TC_DEVICEID_KRAKATOA_10BT: /* 3c900B-TPO */ - sc->xl_media = XL_MEDIAOPT_BT; - sc->xl_xcvr = XL_XCVR_10BT; - if (verbose) - printf("xl%d: guessing 10BaseT transceiver\n", - sc->xl_unit); - break; - case TC_DEVICEID_BOOMERANG_10BT_COMBO: /* 3c900-COMBO */ - case TC_DEVICEID_KRAKATOA_10BT_COMBO: /* 3c900B-COMBO */ - sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI; - sc->xl_xcvr = XL_XCVR_10BT; - if (verbose) - printf("xl%d: guessing COMBO (AUI/BNC/TP)\n", - sc->xl_unit); - break; - case TC_DEVICEID_KRAKATOA_10BT_TPC: /* 3c900B-TPC */ - sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC; - sc->xl_xcvr = XL_XCVR_10BT; - if (verbose) - printf("xl%d: guessing TPC (BNC/TP)\n", sc->xl_unit); - break; - case TC_DEVICEID_CYCLONE_10FL: /* 3c900B-FL */ - sc->xl_media = XL_MEDIAOPT_10FL; - sc->xl_xcvr = XL_XCVR_AUI; - if (verbose) - printf("xl%d: guessing 10baseFL\n", sc->xl_unit); - break; - case TC_DEVICEID_BOOMERANG_10_100BT: /* 3c905-TX */ - sc->xl_media = XL_MEDIAOPT_MII; - sc->xl_xcvr = XL_XCVR_MII; - if (verbose) - printf("xl%d: guessing MII\n", sc->xl_unit); - break; - case TC_DEVICEID_BOOMERANG_100BT4: /* 3c905-T4 */ - case TC_DEVICEID_CYCLONE_10_100BT4: /* 3c905B-T4 */ - sc->xl_media = XL_MEDIAOPT_BT4; - sc->xl_xcvr = XL_XCVR_MII; - if (verbose) - printf("xl%d: guessing 100BaseT4/MII\n", sc->xl_unit); - break; - case TC_DEVICEID_HURRICANE_10_100BT: /* 3c905B-TX */ - case TC_DEVICEID_HURRICANE_10_100BT_SERV:/* 3c980-TX */ - case TC_DEVICEID_TORNADO_10_100BT_SERV: /* 3c980C-TX */ - case TC_DEVICEID_HURRICANE_SOHO100TX: /* 3cSOHO100-TX */ - case TC_DEVICEID_TORNADO_10_100BT: /* 3c905C-TX */ - case TC_DEVICEID_TORNADO_HOMECONNECT: /* 3c450-TX */ - sc->xl_media = XL_MEDIAOPT_BTX; - sc->xl_xcvr = XL_XCVR_AUTO; - if (verbose) - printf("xl%d: guessing 10/100 internal\n", - sc->xl_unit); - break; - case TC_DEVICEID_CYCLONE_10_100_COMBO: /* 3c905B-COMBO */ - sc->xl_media = XL_MEDIAOPT_BTX|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI; - sc->xl_xcvr = XL_XCVR_AUTO; - if (verbose) - printf("xl%d: guessing 10/100 plus BNC/AUI\n", - sc->xl_unit); - break; - default: - printf("xl%d: unknown device ID: %x -- " - "defaulting to 10baseT\n", sc->xl_unit, devid); - sc->xl_media = XL_MEDIAOPT_BT; - break; - } - - return; -} - -/* - * Initialize the transmit descriptors. - */ -int xl_list_tx_init(sc) - struct xl_softc *sc; -{ - struct xl_chain_data *cd; - struct xl_list_data *ld; - int i; - - cd = &sc->xl_cdata; - ld = sc->xl_ldata; - for (i = 0; i < XL_TX_LIST_CNT; i++) { - cd->xl_tx_chain[i].xl_ptr = &ld->xl_tx_list[i]; - if (i == (XL_TX_LIST_CNT - 1)) - cd->xl_tx_chain[i].xl_next = NULL; - else - cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[i + 1]; - } - - cd->xl_tx_free = &cd->xl_tx_chain[0]; - cd->xl_tx_tail = cd->xl_tx_head = NULL; - - return(0); -} - -/* - * Initialize the transmit desriptors. - */ -int -xl_list_tx_init_90xB(sc) - struct xl_softc *sc; -{ - struct xl_chain_data *cd; - struct xl_list_data *ld; - int i; - - cd = &sc->xl_cdata; - ld = sc->xl_ldata; - for (i = 0; i < XL_TX_LIST_CNT; i++) { - cd->xl_tx_chain[i].xl_ptr = &ld->xl_tx_list[i]; - cd->xl_tx_chain[i].xl_phys = vtophys(&ld->xl_tx_list[i]); - if (i == (XL_TX_LIST_CNT - 1)) - cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[0]; - else - cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[i + 1]; - if (i == 0) - cd->xl_tx_chain[i].xl_prev = - &cd->xl_tx_chain[XL_TX_LIST_CNT - 1]; - else - cd->xl_tx_chain[i].xl_prev = - &cd->xl_tx_chain[i - 1]; - } - - bzero((char *)ld->xl_tx_list, sizeof(struct xl_list) * XL_TX_LIST_CNT); - ld->xl_tx_list[0].xl_status = XL_TXSTAT_EMPTY; - - cd->xl_tx_prod = 1; - cd->xl_tx_cons = 1; - cd->xl_tx_cnt = 0; - - return (0); -} - -/* - * Initialize the RX descriptors and allocate mbufs for them. Note that - * we arrange the descriptors in a closed ring, so that the last descriptor - * points back to the first. - */ -int xl_list_rx_init(sc) - struct xl_softc *sc; -{ - struct xl_chain_data *cd; - struct xl_list_data *ld; - int i; - - cd = &sc->xl_cdata; - ld = sc->xl_ldata; - - for (i = 0; i < XL_RX_LIST_CNT; i++) { - cd->xl_rx_chain[i].xl_ptr = - (struct xl_list_onefrag *)&ld->xl_rx_list[i]; - if (xl_newbuf(sc, &cd->xl_rx_chain[i]) == ENOBUFS) - return(ENOBUFS); - if (i == (XL_RX_LIST_CNT - 1)) { - cd->xl_rx_chain[i].xl_next = &cd->xl_rx_chain[0]; - ld->xl_rx_list[i].xl_next = - vtophys(&ld->xl_rx_list[0]); - } else { - cd->xl_rx_chain[i].xl_next = &cd->xl_rx_chain[i + 1]; - ld->xl_rx_list[i].xl_next = - vtophys(&ld->xl_rx_list[i + 1]); - } - } - - cd->xl_rx_head = &cd->xl_rx_chain[0]; - - return(0); -} - -/* - * Initialize an RX descriptor and attach an MBUF cluster. - */ -int xl_newbuf(sc, c) - struct xl_softc *sc; - struct xl_chain_onefrag *c; -{ - struct mbuf *m_new = NULL; - - MGETHDR(m_new, M_DONTWAIT, MT_DATA); - if (m_new == NULL) - return(ENOBUFS); - - MCLGET(m_new, M_DONTWAIT); - if (!(m_new->m_flags & M_EXT)) { - m_freem(m_new); - return(ENOBUFS); - } - - m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; - - /* Force longword alignment for packet payload. */ - m_adj(m_new, ETHER_ALIGN); - - c->xl_mbuf = m_new; - c->xl_ptr->xl_frag.xl_addr = vtophys(mtod(m_new, caddr_t)); - c->xl_ptr->xl_frag.xl_len = MCLBYTES | XL_LAST_FRAG; - c->xl_ptr->xl_status = 0; - - return(0); -} - -int xl_rx_resync(sc) - struct xl_softc *sc; -{ - struct xl_chain_onefrag *pos; - int i; - - pos = sc->xl_cdata.xl_rx_head; - - for (i = 0; i < XL_RX_LIST_CNT; i++) { - if (pos->xl_ptr->xl_status) - break; - pos = pos->xl_next; - } - - if (i == XL_RX_LIST_CNT) - return (0); - - sc->xl_cdata.xl_rx_head = pos; - - return (EAGAIN); -} - -/* - * A frame has been uploaded: pass the resulting mbuf chain up to - * the higher level protocols. - */ -void xl_rxeof(sc) - struct xl_softc *sc; -{ - struct ether_header *eh; - struct mbuf *m; - struct ifnet *ifp; - struct xl_chain_onefrag *cur_rx; - int total_len = 0; - u_int16_t rxstat; - - ifp = &sc->arpcom.ac_if; - -again: - - while((rxstat = sc->xl_cdata.xl_rx_head->xl_ptr->xl_status)) { - cur_rx = sc->xl_cdata.xl_rx_head; - sc->xl_cdata.xl_rx_head = cur_rx->xl_next; - - /* - * If an error occurs, update stats, clear the - * status word and leave the mbuf cluster in place: - * it should simply get re-used next time this descriptor - * comes up in the ring. - */ - if (rxstat & XL_RXSTAT_UP_ERROR) { - ifp->if_ierrors++; - cur_rx->xl_ptr->xl_status = 0; - continue; - } - - /* - * If there error bit was not set, the upload complete - * bit should be set which means we have a valid packet. - * If not, something truly strange has happened. - */ - if (!(rxstat & XL_RXSTAT_UP_CMPLT)) { - printf("xl%d: bad receive status -- " - "packet dropped", sc->xl_unit); - ifp->if_ierrors++; - cur_rx->xl_ptr->xl_status = 0; - continue; - } - - /* No errors; receive the packet. */ - m = cur_rx->xl_mbuf; - total_len = cur_rx->xl_ptr->xl_status & XL_RXSTAT_LENMASK; - - /* - * Try to conjure up a new mbuf cluster. If that - * fails, it means we have an out of memory condition and - * should leave the buffer in place and continue. This will - * result in a lost packet, but there's little else we - * can do in this situation. - */ - if (xl_newbuf(sc, cur_rx) == ENOBUFS) { - ifp->if_ierrors++; - cur_rx->xl_ptr->xl_status = 0; - continue; - } - - ifp->if_ipackets++; - eh = mtod(m, struct ether_header *); - m->m_pkthdr.rcvif = ifp; -#if NBPFILTER > 0 - /* - * Handle BPF listeners. Let the BPF user see the packet. - */ - if (ifp->if_bpf) { - m->m_pkthdr.len = m->m_len = total_len; - bpf_mtap(ifp->if_bpf, m); - } -#endif - /* Remove header from mbuf and pass it on. */ - m->m_pkthdr.len = m->m_len = - total_len - sizeof(struct ether_header); - m->m_data += sizeof(struct ether_header); - ether_input(ifp, eh, m); - } - - /* - * Handle the 'end of channel' condition. When the upload - * engine hits the end of the RX ring, it will stall. This - * is our cue to flush the RX ring, reload the uplist pointer - * register and unstall the engine. - * XXX This is actually a little goofy. With the ThunderLAN - * chip, you get an interrupt when the receiver hits the end - * of the receive ring, which tells you exactly when you - * you need to reload the ring pointer. Here we have to - * fake it. I'm mad at myself for not being clever enough - * to avoid the use of a goto here. - */ - if (CSR_READ_4(sc, XL_UPLIST_PTR) == 0 || - CSR_READ_4(sc, XL_UPLIST_STATUS) & XL_PKTSTAT_UP_STALLED) { - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL); - xl_wait(sc); - CSR_WRITE_4(sc, XL_UPLIST_PTR, - vtophys(&sc->xl_ldata->xl_rx_list[0])); - sc->xl_cdata.xl_rx_head = &sc->xl_cdata.xl_rx_chain[0]; - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL); - goto again; - } - - return; -} - -/* - * A frame was downloaded to the chip. It's safe for us to clean up - * the list buffers. - */ -void xl_txeof(sc) - struct xl_softc *sc; -{ - struct xl_chain *cur_tx; - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - - /* Clear the timeout timer. */ - ifp->if_timer = 0; - - /* - * Go through our tx list and free mbufs for those - * frames that have been uploaded. Note: the 3c905B - * sets a special bit in the status word to let us - * know that a frame has been downloaded, but the - * original 3c900/3c905 adapters don't do that. - * Consequently, we have to use a different test if - * xl_type != XL_TYPE_905B. - */ - while(sc->xl_cdata.xl_tx_head != NULL) { - cur_tx = sc->xl_cdata.xl_tx_head; - - if (CSR_READ_4(sc, XL_DOWNLIST_PTR)) - break; - - sc->xl_cdata.xl_tx_head = cur_tx->xl_next; - m_freem(cur_tx->xl_mbuf); - cur_tx->xl_mbuf = NULL; - ifp->if_opackets++; - - cur_tx->xl_next = sc->xl_cdata.xl_tx_free; - sc->xl_cdata.xl_tx_free = cur_tx; - } - - if (sc->xl_cdata.xl_tx_head == NULL) { - ifp->if_flags &= ~IFF_OACTIVE; - sc->xl_cdata.xl_tx_tail = NULL; - } else { - if (CSR_READ_4(sc, XL_DMACTL) & XL_DMACTL_DOWN_STALLED || - !CSR_READ_4(sc, XL_DOWNLIST_PTR)) { - CSR_WRITE_4(sc, XL_DOWNLIST_PTR, - vtophys(sc->xl_cdata.xl_tx_head->xl_ptr)); - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); - } - } - - return; -} - -void -xl_txeof_90xB(sc) - struct xl_softc *sc; -{ - struct xl_chain *cur_tx = NULL; - struct ifnet *ifp; - int idx; - - ifp = &sc->arpcom.ac_if; - - idx = sc->xl_cdata.xl_tx_cons; - while(idx != sc->xl_cdata.xl_tx_prod) { - - cur_tx = &sc->xl_cdata.xl_tx_chain[idx]; - - if (!(cur_tx->xl_ptr->xl_status & XL_TXSTAT_DL_COMPLETE)) - break; - - if (cur_tx->xl_mbuf != NULL) { - m_freem(cur_tx->xl_mbuf); - cur_tx->xl_mbuf = NULL; - } - - ifp->if_opackets++; - - sc->xl_cdata.xl_tx_cnt--; - XL_INC(idx, XL_TX_LIST_CNT); - ifp->if_timer = 0; - } - - sc->xl_cdata.xl_tx_cons = idx; - - if (cur_tx != NULL) - ifp->if_flags &= ~IFF_OACTIVE; -} - -/* - * TX 'end of channel' interrupt handler. Actually, we should - * only get a 'TX complete' interrupt if there's a transmit error, - * so this is really TX error handler. - */ -void xl_txeoc(sc) - struct xl_softc *sc; -{ - u_int8_t txstat; - - while((txstat = CSR_READ_1(sc, XL_TX_STATUS))) { - if (txstat & XL_TXSTATUS_UNDERRUN || - txstat & XL_TXSTATUS_JABBER || - txstat & XL_TXSTATUS_RECLAIM) { - printf("xl%d: transmission error: %x\n", - sc->xl_unit, txstat); - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); - xl_wait(sc); - if (sc->xl_type == XL_TYPE_905B) { - int i; - struct xl_chain *c; - i = sc->xl_cdata.xl_tx_cons; - c = &sc->xl_cdata.xl_tx_chain[i]; - CSR_WRITE_4(sc, XL_DOWNLIST_PTR, c->xl_phys); - CSR_WRITE_1(sc, XL_DOWN_POLL, 64); - } else { - if (sc->xl_cdata.xl_tx_head != NULL) - CSR_WRITE_4(sc, XL_DOWNLIST_PTR, - vtophys(sc->xl_cdata.xl_tx_head->xl_ptr)); - } - /* - * Remember to set this for the - * first generation 3c90X chips. - */ - CSR_WRITE_1(sc, XL_TX_FREETHRESH, XL_PACKET_SIZE >> 8); - if (txstat & XL_TXSTATUS_UNDERRUN && - sc->xl_tx_thresh < XL_PACKET_SIZE) { - sc->xl_tx_thresh += XL_MIN_FRAMELEN; - printf("xl%d: tx underrun, increasing tx start" - " threshold to %d\n", sc->xl_unit, - sc->xl_tx_thresh); - } - CSR_WRITE_2(sc, XL_COMMAND, - XL_CMD_TX_SET_START|sc->xl_tx_thresh); - if (sc->xl_type == XL_TYPE_905B) { - CSR_WRITE_2(sc, XL_COMMAND, - XL_CMD_SET_TX_RECLAIM|(XL_PACKET_SIZE >> 4)); - } - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE); - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); - } else { - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE); - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); - } - /* - * Write an arbitrary byte to the TX_STATUS register - * to clear this interrupt/error and advance to the next. - */ - CSR_WRITE_1(sc, XL_TX_STATUS, 0x01); - } - - return; -} - -int xl_intr(arg) - void *arg; -{ - struct xl_softc *sc; - struct ifnet *ifp; - u_int16_t status; - int claimed = 0; - - sc = arg; - ifp = &sc->arpcom.ac_if; - - while ((status = CSR_READ_2(sc, XL_STATUS)) & XL_INTRS) { - - claimed = 1; - - CSR_WRITE_2(sc, XL_COMMAND, - XL_CMD_INTR_ACK|(status & XL_INTRS)); - - if (status & XL_STAT_UP_COMPLETE) { - int curpkts; - - curpkts = ifp->if_ipackets; - xl_rxeof(sc); - if (curpkts == ifp->if_ipackets) { - while (xl_rx_resync(sc)) - xl_rxeof(sc); - } - } - - if (status & XL_STAT_DOWN_COMPLETE) { - if (sc->xl_type == XL_TYPE_905B) - xl_txeof_90xB(sc); - else - xl_txeof(sc); - } - - if (status & XL_STAT_TX_COMPLETE) { - ifp->if_oerrors++; - xl_txeoc(sc); - } - - if (status & XL_STAT_ADFAIL) { - xl_reset(sc, 0); - xl_init(sc); - } - - if (status & XL_STAT_STATSOFLOW) { - sc->xl_stats_no_timeout = 1; - xl_stats_update(sc); - sc->xl_stats_no_timeout = 0; - } - } - - if (ifp->if_snd.ifq_head != NULL) - (*ifp->if_start)(ifp); - - return (claimed); -} - -void xl_stats_update(xsc) - void *xsc; -{ - struct xl_softc *sc; - struct ifnet *ifp; - struct xl_stats xl_stats; - u_int8_t *p; - int i; - struct mii_data *mii = NULL; - - bzero((char *)&xl_stats, sizeof(struct xl_stats)); - - sc = xsc; - ifp = &sc->arpcom.ac_if; - if (sc->xl_hasmii) - mii = &sc->sc_mii; - - p = (u_int8_t *)&xl_stats; - - /* Read all the stats registers. */ - XL_SEL_WIN(6); - - for (i = 0; i < 16; i++) - *p++ = CSR_READ_1(sc, XL_W6_CARRIER_LOST + i); - - ifp->if_ierrors += xl_stats.xl_rx_overrun; - - ifp->if_collisions += xl_stats.xl_tx_multi_collision + - xl_stats.xl_tx_single_collision + - xl_stats.xl_tx_late_collision; - - /* - * Boomerang and cyclone chips have an extra stats counter - * in window 4 (BadSSD). We have to read this too in order - * to clear out all the stats registers and avoid a statsoflow - * interrupt. - */ - XL_SEL_WIN(4); - CSR_READ_1(sc, XL_W4_BADSSD); - - if (mii != NULL) - mii_tick(mii); - - XL_SEL_WIN(7); - - if (!sc->xl_stats_no_timeout) - timeout(xl_stats_update, sc, hz); - - return; -} - -/* - * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data - * pointers to the fragment pointers. - */ -int xl_encap(sc, c, m_head) - struct xl_softc *sc; - struct xl_chain *c; - struct mbuf *m_head; -{ - int frag = 0; - struct xl_frag *f = NULL; - int total_len; - struct mbuf *m; - - /* - * Start packing the mbufs in this chain into - * the fragment pointers. Stop when we run out - * of fragments or hit the end of the mbuf chain. - */ - m = m_head; - total_len = 0; - - for (m = m_head, frag = 0; m != NULL; m = m->m_next) { - if (m->m_len != 0) { - if (frag == XL_MAXFRAGS) - break; - total_len+= m->m_len; - c->xl_ptr->xl_frag[frag].xl_addr = - vtophys(mtod(m, vm_offset_t)); - c->xl_ptr->xl_frag[frag].xl_len = m->m_len; - frag++; - } - } - - /* - * Handle special case: we used up all 63 fragments, - * but we have more mbufs left in the chain. Copy the - * data into an mbuf cluster. Note that we don't - * bother clearing the values in the other fragment - * pointers/counters; it wouldn't gain us anything, - * and would waste cycles. - */ - if (m != NULL) { - struct mbuf *m_new = NULL; - - MGETHDR(m_new, M_DONTWAIT, MT_DATA); - if (m_new == NULL) - return(1); - if (m_head->m_pkthdr.len > MHLEN) { - MCLGET(m_new, M_DONTWAIT); - if (!(m_new->m_flags & M_EXT)) { - m_freem(m_new); - return(1); - } - } - m_copydata(m_head, 0, m_head->m_pkthdr.len, - mtod(m_new, caddr_t)); - m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; - m_freem(m_head); - m_head = m_new; - f = &c->xl_ptr->xl_frag[0]; - f->xl_addr = vtophys(mtod(m_new, caddr_t)); - f->xl_len = total_len = m_new->m_len; - frag = 1; - } - - c->xl_mbuf = m_head; - c->xl_ptr->xl_frag[frag - 1].xl_len |= XL_LAST_FRAG; - c->xl_ptr->xl_status = total_len; - c->xl_ptr->xl_next = 0; - - return(0); -} - -/* - * Main transmit routine. To avoid having to do mbuf copies, we put pointers - * to the mbuf data regions directly in the transmit lists. We also save a - * copy of the pointers since the transmit list fragment pointers are - * physical addresses. - */ -void xl_start(ifp) - struct ifnet *ifp; -{ - struct xl_softc *sc; - struct mbuf *m_head = NULL; - struct xl_chain *prev = NULL, *cur_tx = NULL, *start_tx; - - sc = ifp->if_softc; - - /* - * Check for an available queue slot. If there are none, - * punt. - */ - if (sc->xl_cdata.xl_tx_free == NULL) { - xl_txeoc(sc); - xl_txeof(sc); - if (sc->xl_cdata.xl_tx_free == NULL) { - ifp->if_flags |= IFF_OACTIVE; - return; - } - } - - start_tx = sc->xl_cdata.xl_tx_free; - - while(sc->xl_cdata.xl_tx_free != NULL) { - IF_DEQUEUE(&ifp->if_snd, m_head); - if (m_head == NULL) - break; - - /* Pick a descriptor off the free list. */ - cur_tx = sc->xl_cdata.xl_tx_free; - sc->xl_cdata.xl_tx_free = cur_tx->xl_next; - - cur_tx->xl_next = NULL; - - /* Pack the data into the descriptor. */ - xl_encap(sc, cur_tx, m_head); - - /* Chain it together. */ - if (prev != NULL) { - prev->xl_next = cur_tx; - prev->xl_ptr->xl_next = vtophys(cur_tx->xl_ptr); - } - prev = cur_tx; - -#if NBPFILTER > 0 - /* - * If there's a BPF listener, bounce a copy of this frame - * to him. - */ - if (ifp->if_bpf) - bpf_mtap(ifp->if_bpf, cur_tx->xl_mbuf); -#endif - } - - /* - * If there are no packets queued, bail. - */ - if (cur_tx == NULL) - return; - - /* - * Place the request for the upload interrupt - * in the last descriptor in the chain. This way, if - * we're chaining several packets at once, we'll only - * get an interupt once for the whole chain rather than - * once for each packet. - */ - cur_tx->xl_ptr->xl_status |= XL_TXSTAT_DL_INTR; - - /* - * Queue the packets. If the TX channel is clear, update - * the downlist pointer register. - */ - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_STALL); - xl_wait(sc); - - if (sc->xl_cdata.xl_tx_head != NULL) { - sc->xl_cdata.xl_tx_tail->xl_next = start_tx; - sc->xl_cdata.xl_tx_tail->xl_ptr->xl_next = - vtophys(start_tx->xl_ptr); - sc->xl_cdata.xl_tx_tail->xl_ptr->xl_status &= - ~XL_TXSTAT_DL_INTR; - sc->xl_cdata.xl_tx_tail = cur_tx; - } else { - sc->xl_cdata.xl_tx_head = start_tx; - sc->xl_cdata.xl_tx_tail = cur_tx; - } - if (!CSR_READ_4(sc, XL_DOWNLIST_PTR)) - CSR_WRITE_4(sc, XL_DOWNLIST_PTR, vtophys(start_tx->xl_ptr)); - - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); - - XL_SEL_WIN(7); - - /* - * Set a timeout in case the chip goes out to lunch. - */ - ifp->if_timer = 5; - - /* - * XXX Under certain conditions, usually on slower machines - * where interrupts may be dropped, it's possible for the - * adapter to chew up all the buffers in the receive ring - * and stall, without us being able to do anything about it. - * To guard against this, we need to make a pass over the - * RX queue to make sure there aren't any packets pending. - * Doing it here means we can flush the receive ring at the - * same time the chip is DMAing the transmit descriptors we - * just gave it. - * - * 3Com goes to some lengths to emphasize the Parallel Tasking (tm) - * nature of their chips in all their marketing literature; - * we may as well take advantage of it. :) - */ - xl_rxeof(sc); - - return; -} - -int xl_encap_90xB(sc, c, m_head) - struct xl_softc *sc; - struct xl_chain *c; - struct mbuf *m_head; -{ - int frag = 0; - struct xl_frag *f = NULL; - struct mbuf *m; - struct xl_list *d; - - /* - * Start packing the mbufs in this chain into - * the fragment pointers. Stop when we run out - * of fragments or hit the end of the mbuf chain. - */ - d = c->xl_ptr; - d->xl_status = 0; - d->xl_next = 0; - - for (m = m_head, frag = 0; m != NULL; m = m->m_next) { - if (m->m_len != 0) { - if (frag == XL_MAXFRAGS) - break; - f = &d->xl_frag[frag]; - f->xl_addr = vtophys(mtod(m, vm_offset_t)); - f->xl_len = m->m_len; - frag++; - } - } - - c->xl_mbuf = m_head; - c->xl_ptr->xl_frag[frag - 1].xl_len |= XL_LAST_FRAG; - c->xl_ptr->xl_status = XL_TXSTAT_RND_DEFEAT; - - return(0); -} - -void -xl_start_90xB(ifp) - struct ifnet *ifp; -{ - struct xl_softc *sc; - struct mbuf *m_head = NULL; - struct xl_chain *prev = NULL, *cur_tx = NULL, *start_tx; - int idx; - - sc = ifp->if_softc; - - if (ifp->if_flags & IFF_OACTIVE) - return; - - idx = sc->xl_cdata.xl_tx_prod; - start_tx = &sc->xl_cdata.xl_tx_chain[idx]; - - while (sc->xl_cdata.xl_tx_chain[idx].xl_mbuf == NULL) { - - if ((XL_TX_LIST_CNT - sc->xl_cdata.xl_tx_cnt) < 3) { - ifp->if_flags |= IFF_OACTIVE; - break; - } - - IF_DEQUEUE(&ifp->if_snd, m_head); - if (m_head == NULL) - break; - - cur_tx = &sc->xl_cdata.xl_tx_chain[idx]; - - /* Pack the data into the descriptor. */ - xl_encap_90xB(sc, cur_tx, m_head); - - /* Chain it together. */ - if (prev != NULL) - prev->xl_ptr->xl_next = cur_tx->xl_phys; - prev = cur_tx; - -#if NBPFILTER > 0 - /* - * If there's a BPF listener, bounce a copy of this frame - * to him. - */ - if (ifp->if_bpf) - bpf_mtap(ifp->if_bpf, cur_tx->xl_mbuf); -#endif - - XL_INC(idx, XL_TX_LIST_CNT); - sc->xl_cdata.xl_tx_cnt++; - } - - /* - * If there are no packets queued, bail. - */ - if (cur_tx == NULL) - return; - - /* - * Place the request for the upload interrupt - * in the last descriptor in the chain. This way, if - * we're chaining several packets at once, we'll only - * get an interupt once for the whole chain rather than - * once for each packet. - */ - cur_tx->xl_ptr->xl_status |= XL_TXSTAT_DL_INTR; - - /* Start transmission */ - sc->xl_cdata.xl_tx_prod = idx; - start_tx->xl_prev->xl_ptr->xl_next = start_tx->xl_phys; - - /* - * Set a timeout in case the chip goes out to lunch. - */ - ifp->if_timer = 5; -} - -void xl_init(xsc) - void *xsc; -{ - struct xl_softc *sc = xsc; - struct ifnet *ifp = &sc->arpcom.ac_if; - int s, i; - u_int16_t rxfilt = 0; - struct mii_data *mii = NULL; - - s = splimp(); - - /* - * Cancel pending I/O and free all RX/TX buffers. - */ - xl_stop(sc); - - if (sc->xl_hasmii) - mii = &sc->sc_mii; - - if (mii == NULL) { - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET); - xl_wait(sc); - } - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); - xl_wait(sc); - DELAY(10000); - - - /* Init our MAC address */ - XL_SEL_WIN(2); - for (i = 0; i < ETHER_ADDR_LEN; i++) { - CSR_WRITE_1(sc, XL_W2_STATION_ADDR_LO + i, - sc->arpcom.ac_enaddr[i]); - } - - /* Clear the station mask. */ - for (i = 0; i < 3; i++) - CSR_WRITE_2(sc, XL_W2_STATION_MASK_LO + (i * 2), 0); -#ifdef notdef - /* Reset TX and RX. */ - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET); - xl_wait(sc); - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); - xl_wait(sc); -#endif - /* Init circular RX list. */ - if (xl_list_rx_init(sc) == ENOBUFS) { - printf("xl%d: initialization failed: no " - "memory for rx buffers\n", sc->xl_unit); - xl_stop(sc); - return; - } - - /* Init TX descriptors. */ - if (sc->xl_type == XL_TYPE_905B) - xl_list_tx_init_90xB(sc); - else - xl_list_tx_init(sc); - - /* - * Set the TX freethresh value. - * Note that this has no effect on 3c905B "cyclone" - * cards but is required for 3c900/3c905 "boomerang" - * cards in order to enable the download engine. - */ - CSR_WRITE_1(sc, XL_TX_FREETHRESH, XL_PACKET_SIZE >> 8); - - /* Set the TX start threshold for best performance. */ - sc->xl_tx_thresh = XL_MIN_FRAMELEN; - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_SET_START|sc->xl_tx_thresh); - - /* - * If this is a 3c905B, also set the tx reclaim threshold. - * This helps cut down on the number of tx reclaim errors - * that could happen on a busy network. The chip multiplies - * the register value by 16 to obtain the actual threshold - * in bytes, so we divide by 16 when setting the value here. - * The existing threshold value can be examined by reading - * the register at offset 9 in window 5. - */ - if (sc->xl_type == XL_TYPE_905B) { - CSR_WRITE_2(sc, XL_COMMAND, - XL_CMD_SET_TX_RECLAIM|(XL_PACKET_SIZE >> 4)); - } - - /* Set RX filter bits. */ - XL_SEL_WIN(5); - rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER); - - /* Set the individual bit to receive frames for this host only. */ - rxfilt |= XL_RXFILTER_INDIVIDUAL; - - /* If we want promiscuous mode, set the allframes bit. */ - if (ifp->if_flags & IFF_PROMISC) { - rxfilt |= XL_RXFILTER_ALLFRAMES; - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); - } else { - rxfilt &= ~XL_RXFILTER_ALLFRAMES; - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); - } - - /* - * Set capture broadcast bit to capture broadcast frames. - */ - if (ifp->if_flags & IFF_BROADCAST) { - rxfilt |= XL_RXFILTER_BROADCAST; - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); - } else { - rxfilt &= ~XL_RXFILTER_BROADCAST; - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); - } - - /* - * Program the multicast filter, if necessary. - */ - if (sc->xl_type == XL_TYPE_905B) - xl_setmulti_hash(sc); - else - xl_setmulti(sc); - - /* - * Load the address of the RX list. We have to - * stall the upload engine before we can manipulate - * the uplist pointer register, then unstall it when - * we're finished. We also have to wait for the - * stall command to complete before proceeding. - * Note that we have to do this after any RX resets - * have completed since the uplist register is cleared - * by a reset. - */ - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL); - xl_wait(sc); - CSR_WRITE_4(sc, XL_UPLIST_PTR, vtophys(&sc->xl_ldata->xl_rx_list[0])); - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL); - xl_wait(sc); - - if (sc->xl_type == XL_TYPE_905B) { - /* Set polling interval */ - CSR_WRITE_1(sc, XL_DOWN_POLL, 64); - /* Load the address of the TX list */ - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_STALL); - xl_wait(sc); - CSR_WRITE_4(sc, XL_DOWNLIST_PTR, - vtophys(&sc->xl_ldata->xl_tx_list[0])); - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); - xl_wait(sc); - } - - /* - * If the coax transceiver is on, make sure to enable - * the DC-DC converter. - */ - XL_SEL_WIN(3); - if (sc->xl_xcvr == XL_XCVR_COAX) - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START); - else - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); - - /* Clear out the stats counters. */ - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE); - sc->xl_stats_no_timeout = 1; - xl_stats_update(sc); - sc->xl_stats_no_timeout = 0; - XL_SEL_WIN(4); - CSR_WRITE_2(sc, XL_W4_NET_DIAG, XL_NETDIAG_UPPER_BYTES_ENABLE); - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_ENABLE); - - /* - * Enable interrupts. - */ - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|0xFF); - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|XL_INTRS); - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|XL_INTRS); - - /* Set the RX early threshold */ - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_THRESH|(XL_PACKET_SIZE >>2)); - CSR_WRITE_2(sc, XL_DMACTL, XL_DMACTL_UP_RX_EARLY); - - /* Enable receiver and transmitter. */ - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE); - xl_wait(sc); - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_ENABLE); - xl_wait(sc); - - /* Restore state of BMCR */ - if (mii != NULL) - mii_mediachg(mii); - - /* Select window 7 for normal operations. */ - XL_SEL_WIN(7); - - ifp->if_flags |= IFF_RUNNING; - ifp->if_flags &= ~IFF_OACTIVE; - - (void)splx(s); - - timeout(xl_stats_update, sc, hz); - - return; -} - -/* - * Set media options. - */ -int xl_ifmedia_upd(ifp) - struct ifnet *ifp; -{ - struct xl_softc *sc; - struct ifmedia *ifm = NULL; - struct mii_data *mii = NULL; - - sc = ifp->if_softc; - - if (sc->xl_hasmii) - mii = &sc->sc_mii; - if (mii == NULL) - ifm = &sc->ifmedia; - else - ifm = &mii->mii_media; - - switch(IFM_SUBTYPE(ifm->ifm_media)) { - case IFM_100_FX: - case IFM_10_FL: - case IFM_10_2: - case IFM_10_5: - xl_setmode(sc, ifm->ifm_media); - return (0); - break; - default: - break; - } - - if (sc->xl_media & XL_MEDIAOPT_MII || sc->xl_media & XL_MEDIAOPT_BTX - || sc->xl_media & XL_MEDIAOPT_BT4) { - xl_init(sc); - } else { - xl_setmode(sc, ifm->ifm_media); - } - - return(0); -} - -/* - * Report current media status. - */ -void xl_ifmedia_sts(ifp, ifmr) - struct ifnet *ifp; - struct ifmediareq *ifmr; -{ - struct xl_softc *sc; - u_int32_t icfg; - struct mii_data *mii = NULL; - - sc = ifp->if_softc; - if (sc->xl_hasmii != 0) - mii = &sc->sc_mii; - - XL_SEL_WIN(3); - icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG) & XL_ICFG_CONNECTOR_MASK; - icfg >>= XL_ICFG_CONNECTOR_BITS; - - ifmr->ifm_active = IFM_ETHER; - - switch(icfg) { - case XL_XCVR_10BT: - ifmr->ifm_active = IFM_ETHER|IFM_10_T; - if (CSR_READ_1(sc, XL_W3_MAC_CTRL) & XL_MACCTRL_DUPLEX) - ifmr->ifm_active |= IFM_FDX; - else - ifmr->ifm_active |= IFM_HDX; - break; - case XL_XCVR_AUI: - if (sc->xl_type == XL_TYPE_905B && - sc->xl_media == XL_MEDIAOPT_10FL) { - ifmr->ifm_active = IFM_ETHER|IFM_10_FL; - if (CSR_READ_1(sc, XL_W3_MAC_CTRL) & XL_MACCTRL_DUPLEX) - ifmr->ifm_active |= IFM_FDX; - else - ifmr->ifm_active |= IFM_FDX; - } else - ifmr->ifm_active = IFM_ETHER|IFM_10_5; - break; - case XL_XCVR_COAX: - ifmr->ifm_active = IFM_ETHER|IFM_10_2; - break; - /* - * XXX MII and BTX/AUTO should be separate cases. - */ - - case XL_XCVR_100BTX: - case XL_XCVR_AUTO: - case XL_XCVR_MII: - if (mii != NULL) { - mii_pollstat(mii); - ifmr->ifm_active = mii->mii_media_active; - ifmr->ifm_status = mii->mii_media_status; - } - break; - case XL_XCVR_100BFX: - ifmr->ifm_active = IFM_ETHER|IFM_100_FX; - break; - default: - printf("xl%d: unknown XCVR type: %d\n", sc->xl_unit, icfg); - break; - } - - return; -} - -int -xl_ioctl(ifp, command, data) - struct ifnet *ifp; - u_long command; - caddr_t data; -{ - struct xl_softc *sc = ifp->if_softc; - struct ifreq *ifr = (struct ifreq *)data; - struct ifaddr *ifa = (struct ifaddr *)data; - int s, error = 0; - struct mii_data *mii = NULL; - u_int8_t rxfilt; - - s = splimp(); - - if ((error = ether_ioctl(ifp, &sc->arpcom, command, data)) > 0) { - splx(s); - return error; - } - - switch(command) { - case SIOCSIFADDR: - ifp->if_flags |= IFF_UP; - switch (ifa->ifa_addr->sa_family) { -#ifdef INET - case AF_INET: - xl_init(sc); - arp_ifinit(&sc->arpcom, ifa); - break; -#endif /* INET */ - default: - xl_init(sc); - break; - } - break; - case SIOCSIFFLAGS: - XL_SEL_WIN(5); - rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER); - if (ifp->if_flags & IFF_UP) { - if (ifp->if_flags & IFF_RUNNING && - ifp->if_flags & IFF_PROMISC && - !(sc->xl_if_flags & IFF_PROMISC)) { - rxfilt |= XL_RXFILTER_ALLFRAMES; - CSR_WRITE_2(sc, XL_COMMAND, - XL_CMD_RX_SET_FILT|rxfilt); - XL_SEL_WIN(7); - } else if (ifp->if_flags & IFF_RUNNING && - !(ifp->if_flags & IFF_PROMISC) && - sc->xl_if_flags & IFF_PROMISC) { - rxfilt &= ~XL_RXFILTER_ALLFRAMES; - CSR_WRITE_2(sc, XL_COMMAND, - XL_CMD_RX_SET_FILT|rxfilt); - XL_SEL_WIN(7); - } else - xl_init(sc); - } else { - if (ifp->if_flags & IFF_RUNNING) - xl_stop(sc); - } - sc->xl_if_flags = ifp->if_flags; - error = 0; - break; - case SIOCADDMULTI: - case SIOCDELMULTI: - error = (command == SIOCADDMULTI) ? - ether_addmulti(ifr, &sc->arpcom) : - ether_delmulti(ifr, &sc->arpcom); - - if (error == ENETRESET) { - /* - * Multicast list has changed; set the hardware - * filter accordingly. - */ - if (sc->xl_type == XL_TYPE_905B) - xl_setmulti_hash(sc); - else - xl_setmulti(sc); - error = 0; - } - break; - case SIOCGIFMEDIA: - case SIOCSIFMEDIA: - if (sc->xl_hasmii != 0) - mii = &sc->sc_mii; - if (mii == NULL) - error = ifmedia_ioctl(ifp, ifr, - &sc->ifmedia, command); - else - error = ifmedia_ioctl(ifp, ifr, - &mii->mii_media, command); - break; - default: - error = EINVAL; - break; - } - - (void)splx(s); - - return(error); -} - -void xl_watchdog(ifp) - struct ifnet *ifp; -{ - struct xl_softc *sc; - u_int16_t status = 0; - - sc = ifp->if_softc; - - ifp->if_oerrors++; - XL_SEL_WIN(4); - status = CSR_READ_2(sc, XL_W4_MEDIA_STATUS); - printf("xl%d: watchdog timeout\n", sc->xl_unit); - - if (status & XL_MEDIASTAT_CARRIER) - printf("xl%d: no carrier - transceiver cable problem?\n", - sc->xl_unit); - xl_txeoc(sc); - xl_txeof(sc); - xl_rxeof(sc); - xl_reset(sc, 0); - xl_init(sc); - - if (ifp->if_snd.ifq_head != NULL) - (*ifp->if_start)(ifp); - - return; -} - -/* - * Stop the adapter and free any mbufs allocated to the - * RX and TX lists. - */ -void xl_stop(sc) - struct xl_softc *sc; -{ - int i; - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - ifp->if_timer = 0; - - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISABLE); - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE); - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB); - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISCARD); - xl_wait(sc); - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_DISABLE); - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); - DELAY(800); - -#ifdef foo - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET); - xl_wait(sc); - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); - xl_wait(sc); -#endif - - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|XL_STAT_INTLATCH); - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|0); - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|0); - - /* Stop the stats updater. */ - untimeout(xl_stats_update, sc); - - /* - * Free data in the RX lists. - */ - for (i = 0; i < XL_RX_LIST_CNT; i++) { - if (sc->xl_cdata.xl_rx_chain[i].xl_mbuf != NULL) { - m_freem(sc->xl_cdata.xl_rx_chain[i].xl_mbuf); - sc->xl_cdata.xl_rx_chain[i].xl_mbuf = NULL; - } - } - bzero((char *)&sc->xl_ldata->xl_rx_list, - sizeof(sc->xl_ldata->xl_rx_list)); - /* - * Free the TX list buffers. - */ - for (i = 0; i < XL_TX_LIST_CNT; i++) { - if (sc->xl_cdata.xl_tx_chain[i].xl_mbuf != NULL) { - m_freem(sc->xl_cdata.xl_tx_chain[i].xl_mbuf); - sc->xl_cdata.xl_tx_chain[i].xl_mbuf = NULL; - } - } - bzero((char *)&sc->xl_ldata->xl_tx_list, - sizeof(sc->xl_ldata->xl_tx_list)); - - ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); - - return; -} - -void -xl_attach(parent, self, aux) - struct device *parent, *self; - void *aux; -{ - struct xl_softc *sc = (struct xl_softc *)self; - struct pci_attach_args *pa = aux; - pci_chipset_tag_t pc = pa->pa_pc; - pci_intr_handle_t ih; - const char *intrstr = NULL; - u_int8_t enaddr[ETHER_ADDR_LEN]; - struct ifnet *ifp = &sc->arpcom.ac_if; - bus_addr_t iobase; - bus_size_t iosize; - u_int32_t command; - caddr_t roundptr; - u_int round; - int i, media = IFM_ETHER|IFM_100_TX|IFM_FDX; - struct ifmedia *ifm; - - sc->xl_unit = sc->sc_dev.dv_unit; - - /* - * If this is a 3c905B, we have to check one extra thing. - * The 905B supports power management and may be placed in - * a low-power mode (D3 mode), typically by certain operating - * systems which shall not be named. The PCI BIOS is supposed - * to reset the NIC and bring it out of low-power mode, but - * some do not. Consequently, we have to see if this chip - * supports power management, and if so, make sure it's not - * in low-power mode. If power management is available, the - * capid byte will be 0x01. - * - * I _think_ that what actually happens is that the chip - * loses its PCI configuration during the transition from - * D3 back to D0; this means that it should be possible for - * us to save the PCI iobase, membase and IRQ, put the chip - * back in the D0 state, then restore the PCI config ourselves. - */ - command = pci_conf_read(pc, pa->pa_tag, XL_PCI_CAPID) & 0xff; - if (command == 0x01) { - - command = pci_conf_read(pc, pa->pa_tag, - XL_PCI_PWRMGMTCTRL); - if (command & XL_PSTATE_MASK) { - u_int32_t io, mem, irq; - - /* Save PCI config */ - io = pci_conf_read(pc, pa->pa_tag, XL_PCI_LOIO); - mem = pci_conf_read(pc, pa->pa_tag, XL_PCI_LOMEM); - irq = pci_conf_read(pc, pa->pa_tag, XL_PCI_INTLINE); - - /* Reset the power state. */ - printf("%s: chip is in D%d power mode " - "-- setting to D0\n", - sc->sc_dev.dv_xname, command & XL_PSTATE_MASK); - command &= 0xFFFFFFFC; - pci_conf_write(pc, pa->pa_tag, - XL_PCI_PWRMGMTCTRL, command); - - pci_conf_write(pc, pa->pa_tag, XL_PCI_LOIO, io); - pci_conf_write(pc, pa->pa_tag, XL_PCI_LOMEM, mem); - pci_conf_write(pc, pa->pa_tag, XL_PCI_INTLINE, irq); - } - } - - /* - * Map control/status registers. - */ - command = pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); - command |= PCI_COMMAND_IO_ENABLE | - PCI_COMMAND_MEM_ENABLE | - PCI_COMMAND_MASTER_ENABLE; - pci_conf_write(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, command); - command = pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); - -#ifdef XL_USEIOSPACE - if (!(command & PCI_COMMAND_IO_ENABLE)) { - printf("%s: failed to enable i/o ports\n", - sc->sc_dev.dv_xname); - return; - } - /* - * Map control/status registers. - */ - if (pci_io_find(pc, pa->pa_tag, XL_PCI_LOIO, &iobase, &iosize)) { - printf(": can't find i/o space\n"); - return; - } - if (bus_space_map(pa->pa_iot, iobase, iosize, 0, &sc->xl_bhandle)) { - printf(": can't map i/o space\n"); - return; - } - sc->xl_btag = pa->pa_iot; -#else - if (!(command & PCI_COMMAND_MEM_ENABLE)) { - printf(": failed to enable memory mapping\n"); - return; - } - if (pci_mem_find(pc, pa->pa_tag, XL_PCI_LOMEM, &iobase, &iosize, NULL)){ - printf(": can't find mem space\n"); - return; - } - if (bus_space_map(pa->pa_memt, iobase, iosize, 0, &sc->xl_bhandle)) { - printf(": can't map mem space\n"); - return; - } - sc->xl_btag = pa->pa_memt; -#endif - - /* - * Allocate our interrupt. - */ - if (pci_intr_map(pc, pa->pa_intrtag, pa->pa_intrpin, - pa->pa_intrline, &ih)) { - printf(": couldn't map interrupt\n"); - return; - } - - intrstr = pci_intr_string(pc, ih); - sc->xl_intrhand = pci_intr_establish(pc, ih, IPL_NET, xl_intr, sc, - self->dv_xname); - if (sc->xl_intrhand == NULL) { - printf(": couldn't establish interrupt"); - if (intrstr != NULL) - printf(" at %s", intrstr); - return; - } - printf(": %s", intrstr); - - xl_reset(sc, 1); - - /* - * Get station address from the EEPROM. - */ - if (xl_read_eeprom(sc, (caddr_t)&enaddr, XL_EE_OEM_ADR0, 3, 1)) { - printf("\n%s: failed to read station address\n", - sc->sc_dev.dv_xname); - return; - } - bcopy(enaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); - - printf(" address %s\n", ether_sprintf(sc->arpcom.ac_enaddr)); - - sc->xl_ldata_ptr = malloc(sizeof(struct xl_list_data) + 8, - M_DEVBUF, M_NOWAIT); - if (sc->xl_ldata_ptr == NULL) { - printf("%s: no memory for list buffers\n",sc->sc_dev.dv_xname); - return; - } - - sc->xl_ldata = (struct xl_list_data *)sc->xl_ldata_ptr; -#ifdef __alpha__ - round = (u_int64_t)sc->xl_ldata_ptr & 0xf; -#else - round = (u_int32_t)sc->xl_ldata_ptr & 0xf; -#endif - roundptr = sc->xl_ldata_ptr; - for (i = 0; i < 8; i++) { - if (round % 8) { - round++; - roundptr++; - } else - break; - } - sc->xl_ldata = (struct xl_list_data *)roundptr; - bzero(sc->xl_ldata, sizeof(struct xl_list_data)); - - /* - * Figure out the card type. 3c905B adapters have the - * 'supportsNoTxLength' bit set in the capabilities - * word in the EEPROM. - */ - xl_read_eeprom(sc, (caddr_t)&sc->xl_caps, XL_EE_CAPS, 1, 0); - if (sc->xl_caps & XL_CAPS_NO_TXLENGTH) - sc->xl_type = XL_TYPE_905B; - else - sc->xl_type = XL_TYPE_90X; - - ifp->if_softc = sc; - ifp->if_mtu = ETHERMTU; - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; - ifp->if_ioctl = xl_ioctl; - ifp->if_output = ether_output; - if (sc->xl_type == XL_TYPE_905B) - ifp->if_start = xl_start_90xB; - else - ifp->if_start = xl_start; - ifp->if_watchdog = xl_watchdog; - ifp->if_baudrate = 10000000; - ifp->if_snd.ifq_maxlen = XL_TX_LIST_CNT - 1; - bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); - - XL_SEL_WIN(3); - sc->xl_media = CSR_READ_2(sc, XL_W3_MEDIA_OPT); - - xl_read_eeprom(sc, (char *)&sc->xl_xcvr, XL_EE_ICFG_0, 2, 0); - sc->xl_xcvr &= XL_ICFG_CONNECTOR_MASK; - sc->xl_xcvr >>= XL_ICFG_CONNECTOR_BITS; - - xl_mediacheck(sc); - - if (sc->xl_media & XL_MEDIAOPT_MII || sc->xl_media & XL_MEDIAOPT_BTX - || sc->xl_media & XL_MEDIAOPT_BT4) { - ifmedia_init(&sc->sc_mii.mii_media, 0, - xl_ifmedia_upd, xl_ifmedia_sts); - sc->xl_hasmii = 1; - sc->sc_mii.mii_ifp = ifp; - sc->sc_mii.mii_readreg = xl_miibus_readreg; - sc->sc_mii.mii_writereg = xl_miibus_writereg; - sc->sc_mii.mii_statchg = xl_miibus_statchg; - xl_setcfg(sc); - mii_phy_probe(self, &sc->sc_mii, 0xffffffff); - - if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) { - ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE, - 0, NULL); - ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE); - } - else { - ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO); - } - ifm = &sc->sc_mii.mii_media; - } - else { - ifmedia_init(&sc->ifmedia, 0, xl_ifmedia_upd, xl_ifmedia_sts); - sc->xl_hasmii = 0; - ifm = &sc->ifmedia; - } - - /* - * Sanity check. If the user has selected "auto" and this isn't - * a 10/100 card of some kind, we need to force the transceiver - * type to something sane. - */ - if (sc->xl_xcvr == XL_XCVR_AUTO) { - xl_choose_xcvr(sc, 0); - xl_reset(sc, 0); - } - - if (sc->xl_media & XL_MEDIAOPT_BT) { - ifmedia_add(ifm, IFM_ETHER|IFM_10_T, 0, NULL); - ifmedia_add(ifm, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); - if (sc->xl_caps & XL_CAPS_FULL_DUPLEX) - ifmedia_add(&sc->ifmedia, - IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); - } - - if (sc->xl_media & (XL_MEDIAOPT_AUI|XL_MEDIAOPT_10FL)) { - /* - * Check for a 10baseFL board in disguise. - */ - if (sc->xl_type == XL_TYPE_905B && - sc->xl_media == XL_MEDIAOPT_10FL) { - ifmedia_add(ifm, IFM_ETHER|IFM_10_FL, 0, NULL); - ifmedia_add(ifm, IFM_ETHER|IFM_10_FL|IFM_HDX, - 0, NULL); - if (sc->xl_caps & XL_CAPS_FULL_DUPLEX) - ifmedia_add(ifm, - IFM_ETHER|IFM_10_FL|IFM_FDX, 0, NULL); - } else { - ifmedia_add(ifm, IFM_ETHER|IFM_10_5, 0, NULL); - } - } - - if (sc->xl_media & XL_MEDIAOPT_BNC) { - ifmedia_add(ifm, IFM_ETHER|IFM_10_2, 0, NULL); - } - - if (sc->xl_media & XL_MEDIAOPT_BFX) { - ifp->if_baudrate = 100000000; - ifmedia_add(ifm, IFM_ETHER|IFM_100_FX, 0, NULL); - } - - /* Choose a default media. */ - switch(sc->xl_xcvr) { - case XL_XCVR_10BT: - media = IFM_ETHER|IFM_10_T; - xl_setmode(sc, media); - break; - case XL_XCVR_AUI: - if (sc->xl_type == XL_TYPE_905B && - sc->xl_media == XL_MEDIAOPT_10FL) { - media = IFM_ETHER|IFM_10_FL; - xl_setmode(sc, media); - } else { - media = IFM_ETHER|IFM_10_5; - xl_setmode(sc, media); - } - break; - case XL_XCVR_COAX: - media = IFM_ETHER|IFM_10_2; - xl_setmode(sc, media); - break; - case XL_XCVR_AUTO: - case XL_XCVR_100BTX: - case XL_XCVR_MII: - /* Chosen by miibus */ - break; - case XL_XCVR_100BFX: - media = IFM_ETHER|IFM_100_FX; - xl_setmode(sc, media); - break; - default: - printf("xl%d: unknown XCVR type: %d\n", sc->xl_unit, - sc->xl_xcvr); - /* - * This will probably be wrong, but it prevents - * the ifmedia code from panicking. - */ - media = IFM_ETHER | IFM_10_T; - break; - } - - if (sc->xl_hasmii == 0) - ifmedia_set(&sc->ifmedia, media); - - /* - * Call MI attach routines. - */ - if_attach(ifp); - ether_ifattach(ifp); - -#if NBPFILTER > 0 - bpfattach(&sc->arpcom.ac_if.if_bpf, ifp, - DLT_EN10MB, sizeof(struct ether_header)); -#endif - shutdownhook_establish(xl_shutdown, sc); -} - -void -xl_shutdown(v) - void *v; -{ - struct xl_softc *sc = (struct xl_softc *)v; - - xl_reset(sc, 1); - xl_stop(sc); -} - -struct cfattach xl_ca = { - sizeof(struct xl_softc), xl_probe, xl_attach, -}; - -struct cfdriver xl_cd = { - 0, "xl", DV_IFNET -}; diff --git a/sys/dev/pci/if_xl_pci.c b/sys/dev/pci/if_xl_pci.c new file mode 100644 index 00000000000..266ea7aeae9 --- /dev/null +++ b/sys/dev/pci/if_xl_pci.c @@ -0,0 +1,225 @@ +/* $OpenBSD: if_xl_pci.c,v 1.1 2000/04/08 05:50:51 aaron Exp $ */ + + +#include "bpfilter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* only for declaration of wakeup() used by vm.h */ +#include + +#include +#include +#include +#include + +#ifdef INET +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +#if NBPFILTER > 0 +#include +#endif + +#include /* for vtophys */ +#include /* for vtophys */ + +/* + * The following #define causes the code to use PIO to access the + * chip's registers instead of memory mapped mode. The reason PIO mode + * is on by default is that the Etherlink XL manual seems to indicate + * that only the newer revision chips (3c905B) support both PIO and + * memory mapped access. Since we want to be compatible with the older + * bus master chips, we use PIO here. If you comment this out, the + * driver will use memory mapped I/O, which may be faster but which + * might not work on some devices. + */ +#define XL_USEIOSPACE + +#include + +int xl_pci_match __P((struct device *, void *, void *)); +void xl_pci_attach __P((struct device *, struct device *, void *)); + +int +xl_pci_match(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct pci_attach_args *pa = (struct pci_attach_args *) aux; + + if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_3COM) + return (0); + + switch (PCI_PRODUCT(pa->pa_id)) { + case PCI_PRODUCT_3COM_3CSOHO100TX: + case PCI_PRODUCT_3COM_3C900TPO: + case PCI_PRODUCT_3COM_3C900COMBO: + case PCI_PRODUCT_3COM_3C900B: + case PCI_PRODUCT_3COM_3C900BCOMBO: + case PCI_PRODUCT_3COM_3C900BTPC: + case PCI_PRODUCT_3COM_3C900BFL: + case PCI_PRODUCT_3COM_3C905TX: + case PCI_PRODUCT_3COM_3C905T4: + case PCI_PRODUCT_3COM_3C905BTX: + case PCI_PRODUCT_3COM_3C905BT4: + case PCI_PRODUCT_3COM_3C905BCOMBO: + case PCI_PRODUCT_3COM_3C905BFX: + case PCI_PRODUCT_3COM_3C980TX: + case PCI_PRODUCT_3COM_3C980CTX: + case PCI_PRODUCT_3COM_3C905CTX: + case PCI_PRODUCT_3COM_3C450: + return (1); + } + + return (0); +} + +void +xl_pci_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct xl_softc *sc = (struct xl_softc *)self; + struct pci_attach_args *pa = aux; + pci_chipset_tag_t pc = pa->pa_pc; + pci_intr_handle_t ih; + const char *intrstr = NULL; + bus_addr_t iobase; + bus_size_t iosize; + u_int32_t command; + + sc->xl_unit = sc->sc_dev.dv_unit; + + /* + * If this is a 3c905B, we have to check one extra thing. + * The 905B supports power management and may be placed in + * a low-power mode (D3 mode), typically by certain operating + * systems which shall not be named. The PCI BIOS is supposed + * to reset the NIC and bring it out of low-power mode, but + * some do not. Consequently, we have to see if this chip + * supports power management, and if so, make sure it's not + * in low-power mode. If power management is available, the + * capid byte will be 0x01. + * + * I _think_ that what actually happens is that the chip + * loses its PCI configuration during the transition from + * D3 back to D0; this means that it should be possible for + * us to save the PCI iobase, membase and IRQ, put the chip + * back in the D0 state, then restore the PCI config ourselves. + */ + command = pci_conf_read(pc, pa->pa_tag, XL_PCI_CAPID) & 0xff; + if (command == 0x01) { + + command = pci_conf_read(pc, pa->pa_tag, + XL_PCI_PWRMGMTCTRL); + if (command & XL_PSTATE_MASK) { + u_int32_t io, mem, irq; + + /* Save PCI config */ + io = pci_conf_read(pc, pa->pa_tag, XL_PCI_LOIO); + mem = pci_conf_read(pc, pa->pa_tag, XL_PCI_LOMEM); + irq = pci_conf_read(pc, pa->pa_tag, XL_PCI_INTLINE); + + /* Reset the power state. */ + printf("%s: chip is in D%d power mode " + "-- setting to D0\n", + sc->sc_dev.dv_xname, command & XL_PSTATE_MASK); + command &= 0xFFFFFFFC; + pci_conf_write(pc, pa->pa_tag, + XL_PCI_PWRMGMTCTRL, command); + + pci_conf_write(pc, pa->pa_tag, XL_PCI_LOIO, io); + pci_conf_write(pc, pa->pa_tag, XL_PCI_LOMEM, mem); + pci_conf_write(pc, pa->pa_tag, XL_PCI_INTLINE, irq); + } + } + + /* + * Map control/status registers. + */ + command = pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); + command |= PCI_COMMAND_IO_ENABLE | + PCI_COMMAND_MEM_ENABLE | + PCI_COMMAND_MASTER_ENABLE; + pci_conf_write(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, command); + command = pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); + +#ifdef XL_USEIOSPACE + if (!(command & PCI_COMMAND_IO_ENABLE)) { + printf("%s: failed to enable i/o ports\n", + sc->sc_dev.dv_xname); + return; + } + /* + * Map control/status registers. + */ + if (pci_io_find(pc, pa->pa_tag, XL_PCI_LOIO, &iobase, &iosize)) { + printf(": can't find i/o space\n"); + return; + } + if (bus_space_map(pa->pa_iot, iobase, iosize, 0, &sc->xl_bhandle)) { + printf(": can't map i/o space\n"); + return; + } + sc->xl_btag = pa->pa_iot; +#else + if (!(command & PCI_COMMAND_MEM_ENABLE)) { + printf(": failed to enable memory mapping\n"); + return; + } + if (pci_mem_find(pc, pa->pa_tag, XL_PCI_LOMEM, &iobase, &iosize, NULL)){ + printf(": can't find mem space\n"); + return; + } + if (bus_space_map(pa->pa_memt, iobase, iosize, 0, &sc->xl_bhandle)) { + printf(": can't map mem space\n"); + return; + } + sc->xl_btag = pa->pa_memt; +#endif + + /* + * Allocate our interrupt. + */ + if (pci_intr_map(pc, pa->pa_intrtag, pa->pa_intrpin, + pa->pa_intrline, &ih)) { + printf(": couldn't map interrupt\n"); + return; + } + + intrstr = pci_intr_string(pc, ih); + sc->xl_intrhand = pci_intr_establish(pc, ih, IPL_NET, xl_intr, sc, + self->dv_xname); + if (sc->xl_intrhand == NULL) { + printf(": couldn't establish interrupt"); + if (intrstr != NULL) + printf(" at %s", intrstr); + return; + } + printf(": %s", intrstr); + + xl_attach(sc); +} + +struct cfattach xl_pci_ca = { + sizeof(struct xl_softc), xl_pci_match, xl_pci_attach, +}; diff --git a/sys/dev/pci/if_xlreg.h b/sys/dev/pci/if_xlreg.h deleted file mode 100644 index 7536935ef51..00000000000 --- a/sys/dev/pci/if_xlreg.h +++ /dev/null @@ -1,690 +0,0 @@ -/* $OpenBSD: if_xlreg.h,v 1.17 1999/12/16 22:15:45 deraadt Exp $ */ - -/* - * Copyright (c) 1997, 1998 - * Bill Paul . 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 Bill Paul. - * 4. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD - * 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: if_xlreg.h,v 1.17 1999/05/30 18:09:17 wpaul Exp $ - */ - -#define XL_EE_READ 0x0080 /* read, 5 bit address */ -#define XL_EE_WRITE 0x0040 /* write, 5 bit address */ -#define XL_EE_ERASE 0x00c0 /* erase, 5 bit address */ -#define XL_EE_EWEN 0x0030 /* erase, no data needed */ -#define XL_EE_BUSY 0x8000 - -#define XL_EE_EADDR0 0x00 /* station address, first word */ -#define XL_EE_EADDR1 0x01 /* station address, next word, */ -#define XL_EE_EADDR2 0x02 /* station address, last word */ -#define XL_EE_PRODID 0x03 /* product ID code */ -#define XL_EE_MDATA_DATE 0x04 /* manufacturing data, date */ -#define XL_EE_MDATA_DIV 0x05 /* manufacturing data, division */ -#define XL_EE_MDATA_PCODE 0x06 /* manufacturing data, product code */ -#define XL_EE_MFG_ID 0x07 -#define XL_EE_PCI_PARM 0x08 -#define XL_EE_ROM_ONFO 0x09 -#define XL_EE_OEM_ADR0 0x0A -#define XL_EE_OEM_ADR1 0x0B -#define XL_EE_OEM_ADR2 0x0C -#define XL_EE_SOFTINFO1 0x0D -#define XL_EE_COMPAT 0x0E -#define XL_EE_SOFTINFO2 0x0F -#define XL_EE_CAPS 0x10 /* capabilities word */ -#define XL_EE_RSVD0 0x11 -#define XL_EE_ICFG_0 0x12 -#define XL_EE_ICFG_1 0x13 -#define XL_EE_RSVD1 0x14 -#define XL_EE_SOFTINFO3 0x15 -#define XL_EE_RSVD_2 0x16 - -/* - * Bits in the capabilities word - */ -#define XL_CAPS_PNP 0x0001 -#define XL_CAPS_FULL_DUPLEX 0x0002 -#define XL_CAPS_LARGE_PKTS 0x0004 -#define XL_CAPS_SLAVE_DMA 0x0008 -#define XL_CAPS_SECOND_DMA 0x0010 -#define XL_CAPS_FULL_BM 0x0020 -#define XL_CAPS_FRAG_BM 0x0040 -#define XL_CAPS_CRC_PASSTHRU 0x0080 -#define XL_CAPS_TXDONE 0x0100 -#define XL_CAPS_NO_TXLENGTH 0x0200 -#define XL_CAPS_RX_REPEAT 0x0400 -#define XL_CAPS_SNOOPING 0x0800 -#define XL_CAPS_100MBPS 0x1000 -#define XL_CAPS_PWRMGMT 0x2000 - -#define XL_PACKET_SIZE 1536 - -/* - * Register layouts. - */ -#define XL_COMMAND 0x0E -#define XL_STATUS 0x0E - -#define XL_TX_STATUS 0x1B -#define XL_TX_FREE 0x1C -#define XL_DMACTL 0x20 -#define XL_DOWNLIST_PTR 0x24 -#define XL_DOWN_POLL 0x2D /* 3c90xB only */ -#define XL_TX_FREETHRESH 0x2F -#define XL_UPLIST_PTR 0x38 -#define XL_UPLIST_STATUS 0x30 -#define XL_UP_POLL 0x3D /* 3c90xB only */ - -#define XL_PKTSTAT_UP_STALLED 0x00002000 -#define XL_PKTSTAT_UP_ERROR 0x00004000 -#define XL_PKTSTAT_UP_CMPLT 0x00008000 - -#define XL_DMACTL_DN_CMPLT_REQ 0x00000002 -#define XL_DMACTL_DOWN_STALLED 0x00000004 -#define XL_DMACTL_UP_CMPLT 0x00000008 -#define XL_DMACTL_DOWN_CMPLT 0x00000010 -#define XL_DMACTL_UP_RX_EARLY 0x00000020 -#define XL_DMACTL_ARM_COUNTDOWN 0x00000040 -#define XL_DMACTL_DOWN_INPROG 0x00000080 -#define XL_DMACTL_COUNTER_SPEED 0x00000100 -#define XL_DMACTL_DOWNDOWN_MODE 0x00000200 -#define XL_DMACTL_TARGET_ABORT 0x40000000 -#define XL_DMACTL_MASTER_ABORT 0x80000000 - -/* - * Command codes. Some command codes require that we wait for - * the CMD_BUSY flag to clear. Those codes are marked as 'mustwait.' - */ -#define XL_CMD_RESET 0x0000 /* mustwait */ -#define XL_CMD_WINSEL 0x0800 -#define XL_CMD_COAX_START 0x1000 -#define XL_CMD_RX_DISABLE 0x1800 -#define XL_CMD_RX_ENABLE 0x2000 -#define XL_CMD_RX_RESET 0x2800 /* mustwait */ -#define XL_CMD_UP_STALL 0x3000 /* mustwait */ -#define XL_CMD_UP_UNSTALL 0x3001 -#define XL_CMD_DOWN_STALL 0x3002 /* mustwait */ -#define XL_CMD_DOWN_UNSTALL 0x3003 -#define XL_CMD_RX_DISCARD 0x4000 -#define XL_CMD_TX_ENABLE 0x4800 -#define XL_CMD_TX_DISABLE 0x5000 -#define XL_CMD_TX_RESET 0x5800 /* mustwait */ -#define XL_CMD_INTR_FAKE 0x6000 -#define XL_CMD_INTR_ACK 0x6800 -#define XL_CMD_INTR_ENB 0x7000 -#define XL_CMD_STAT_ENB 0x7800 -#define XL_CMD_RX_SET_FILT 0x8000 -#define XL_CMD_RX_SET_THRESH 0x8800 -#define XL_CMD_TX_SET_THRESH 0x9000 -#define XL_CMD_TX_SET_START 0x9800 -#define XL_CMD_DMA_UP 0xA000 -#define XL_CMD_DMA_STOP 0xA001 -#define XL_CMD_STATS_ENABLE 0xA800 -#define XL_CMD_STATS_DISABLE 0xB000 -#define XL_CMD_COAX_STOP 0xB800 - -#define XL_CMD_SET_TX_RECLAIM 0xC000 /* 3c905B only */ -#define XL_CMD_RX_SET_HASH 0xC800 /* 3c905B only */ - -#define XL_HASH_SET 0x0400 -#define XL_HASHFILT_SIZE 256 - -/* - * status codes - * Note that bits 15 to 13 indicate the currently visible register window - * which may be anything from 0 to 7. - */ -#define XL_STAT_INTLATCH 0x0001 /* 0 */ -#define XL_STAT_ADFAIL 0x0002 /* 1 */ -#define XL_STAT_TX_COMPLETE 0x0004 /* 2 */ -#define XL_STAT_TX_AVAIL 0x0008 /* 3 first generation */ -#define XL_STAT_RX_COMPLETE 0x0010 /* 4 */ -#define XL_STAT_RX_EARLY 0x0020 /* 5 */ -#define XL_STAT_INTREQ 0x0040 /* 6 */ -#define XL_STAT_STATSOFLOW 0x0080 /* 7 */ -#define XL_STAT_DMADONE 0x0100 /* 8 first generation */ -#define XL_STAT_LINKSTAT 0x0100 /* 8 3c509B */ -#define XL_STAT_DOWN_COMPLETE 0x0200 /* 9 */ -#define XL_STAT_UP_COMPLETE 0x0400 /* 10 */ -#define XL_STAT_DMABUSY 0x0800 /* 11 first generation */ -#define XL_STAT_CMDBUSY 0x1000 /* 12 */ - -/* - * Interrupts we normally want enabled. - */ -#define XL_INTRS \ - (XL_STAT_UP_COMPLETE|XL_STAT_STATSOFLOW|XL_STAT_ADFAIL| \ - XL_STAT_DOWN_COMPLETE|XL_STAT_TX_COMPLETE|XL_STAT_INTLATCH) - -/* - * Window 0 registers - */ -#define XL_W0_EE_DATA 0x0C -#define XL_W0_EE_CMD 0x0A -#define XL_W0_RSRC_CFG 0x08 -#define XL_W0_ADDR_CFG 0x06 -#define XL_W0_CFG_CTRL 0x04 - -#define XL_W0_PROD_ID 0x02 -#define XL_W0_MFG_ID 0x00 - -/* - * Window 1 - */ - -#define XL_W1_TX_FIFO 0x10 - -#define XL_W1_FREE_TX 0x0C -#define XL_W1_TX_STATUS 0x0B -#define XL_W1_TX_TIMER 0x0A -#define XL_W1_RX_STATUS 0x08 -#define XL_W1_RX_FIFO 0x00 - -/* - * RX status codes - */ -#define XL_RXSTATUS_OVERRUN 0x01 -#define XL_RXSTATUS_RUNT 0x02 -#define XL_RXSTATUS_ALIGN 0x04 -#define XL_RXSTATUS_CRC 0x08 -#define XL_RXSTATUS_OVERSIZE 0x10 -#define XL_RXSTATUS_DRIBBLE 0x20 - -/* - * TX status codes - */ -#define XL_TXSTATUS_RECLAIM 0x02 /* 3c905B only */ -#define XL_TXSTATUS_OVERFLOW 0x04 -#define XL_TXSTATUS_MAXCOLS 0x08 -#define XL_TXSTATUS_UNDERRUN 0x10 -#define XL_TXSTATUS_JABBER 0x20 -#define XL_TXSTATUS_INTREQ 0x40 -#define XL_TXSTATUS_COMPLETE 0x80 - -/* - * Window 2 - */ -#define XL_W2_RESET_OPTIONS 0x0C /* 3c905B only */ -#define XL_W2_STATION_MASK_HI 0x0A -#define XL_W2_STATION_MASK_MID 0x08 -#define XL_W2_STATION_MASK_LO 0x06 -#define XL_W2_STATION_ADDR_HI 0x04 -#define XL_W2_STATION_ADDR_MID 0x02 -#define XL_W2_STATION_ADDR_LO 0x00 - -#define XL_RESETOPT_FEATUREMASK 0x0001|0x0002|0x004 -#define XL_RESETOPT_D3RESETDIS 0x0008 -#define XL_RESETOPT_DISADVFD 0x0010 -#define XL_RESETOPT_DISADV100 0x0020 -#define XL_RESETOPT_DISAUTONEG 0x0040 -#define XL_RESETOPT_DEBUGMODE 0x0080 -#define XL_RESETOPT_FASTAUTO 0x0100 -#define XL_RESETOPT_FASTEE 0x0200 -#define XL_RESETOPT_FORCEDCONF 0x0400 -#define XL_RESETOPT_TESTPDTPDR 0x0800 -#define XL_RESETOPT_TEST100TX 0x1000 -#define XL_RESETOPT_TEST100RX 0x2000 - -/* - * Window 3 (fifo management) - */ -#define XL_W3_INTERNAL_CFG 0x00 -#define XL_W3_RESET_OPT 0x08 -#define XL_W3_FREE_TX 0x0C -#define XL_W3_FREE_RX 0x0A -#define XL_W3_MAC_CTRL 0x06 - -#define XL_ICFG_CONNECTOR_MASK 0x00F00000 -#define XL_ICFG_CONNECTOR_BITS 20 - -#define XL_ICFG_RAMSIZE_MASK 0x00000007 -#define XL_ICFG_RAMWIDTH 0x00000008 -#define XL_ICFG_ROMSIZE_MASK (0x00000040|0x00000080) -#define XL_ICFG_DISABLE_BASSD 0x00000100 -#define XL_ICFG_RAMLOC 0x00000200 -#define XL_ICFG_RAMPART (0x00010000|0x00020000) -#define XL_ICFG_XCVRSEL (0x00100000|0x00200000|0x00400000) -#define XL_ICFG_AUTOSEL 0x01000000 - -#define XL_XCVR_10BT 0x00 -#define XL_XCVR_AUI 0x01 -#define XL_XCVR_RSVD_0 0x02 -#define XL_XCVR_COAX 0x03 -#define XL_XCVR_100BTX 0x04 -#define XL_XCVR_100BFX 0x05 -#define XL_XCVR_MII 0x06 -#define XL_XCVR_RSVD_1 0x07 -#define XL_XCVR_AUTO 0x08 /* 3c905B only */ - -#define XL_MACCTRL_DEFER_EXT_END 0x0001 -#define XL_MACCTRL_DEFER_0 0x0002 -#define XL_MACCTRL_DEFER_1 0x0004 -#define XL_MACCTRL_DEFER_2 0x0008 -#define XL_MACCTRL_DEFER_3 0x0010 -#define XL_MACCTRL_DUPLEX 0x0020 -#define XL_MACCTRL_ALLOW_LARGE_PACK 0x0040 -#define XL_MACCTRL_EXTEND_AFTER_COL 0x0080 (3c905B only) -#define XL_MACCTRL_FLOW_CONTROL_ENB 0x0100 (3c905B only) -#define XL_MACCTRL_VLT_END 0x0200 (3c905B only) - -/* - * The 'reset options' register contains power-on reset values - * loaded from the EEPROM. This includes the supported media - * types on the card. It is also known as the media options register. - */ -#define XL_W3_MEDIA_OPT 0x08 - -#define XL_MEDIAOPT_BT4 0x0001 /* MII */ -#define XL_MEDIAOPT_BTX 0x0002 /* on-chip */ -#define XL_MEDIAOPT_BFX 0x0004 /* on-chip */ -#define XL_MEDIAOPT_BT 0x0008 /* on-chip */ -#define XL_MEDIAOPT_BNC 0x0010 /* on-chip */ -#define XL_MEDIAOPT_AUI 0x0020 /* on-chip */ -#define XL_MEDIAOPT_MII 0x0040 /* MII */ -#define XL_MEDIAOPT_VCO 0x0100 /* 1st gen chip only */ - -#define XL_MEDIAOPT_10FL 0x0100 /* 3x905B only, on-chip */ -#define XL_MEDIAOPT_MASK 0x01FF - -/* - * Window 4 (diagnostics) - */ -#define XL_W4_UPPERBYTESOK 0x0D -#define XL_W4_BADSSD 0x0C -#define XL_W4_MEDIA_STATUS 0x0A -#define XL_W4_PHY_MGMT 0x08 -#define XL_W4_NET_DIAG 0x06 -#define XL_W4_FIFO_DIAG 0x04 -#define XL_W4_VCO_DIAG 0x02 - -#define XL_W4_CTRLR_STAT 0x08 -#define XL_W4_TX_DIAG 0x00 - -#define XL_MII_CLK 0x01 -#define XL_MII_DATA 0x02 -#define XL_MII_DIR 0x04 - -#define XL_MEDIA_SQE 0x0008 -#define XL_MEDIA_10TP 0x00C0 -#define XL_MEDIA_LNK 0x0080 -#define XL_MEDIA_LNKBEAT 0x0800 - -#define XL_MEDIASTAT_CRCSTRIP 0x0004 -#define XL_MEDIASTAT_SQEENB 0x0008 -#define XL_MEDIASTAT_COLDET 0x0010 -#define XL_MEDIASTAT_CARRIER 0x0020 -#define XL_MEDIASTAT_JABGUARD 0x0040 -#define XL_MEDIASTAT_LINKBEAT 0x0080 -#define XL_MEDIASTAT_JABDETECT 0x0200 -#define XL_MEDIASTAT_POLREVERS 0x0400 -#define XL_MEDIASTAT_LINKDETECT 0x0800 -#define XL_MEDIASTAT_TXINPROG 0x1000 -#define XL_MEDIASTAT_DCENB 0x4000 -#define XL_MEDIASTAT_AUIDIS 0x8000 - -#define XL_NETDIAG_TEST_LOWVOLT 0x0001 -#define XL_NETDIAG_ASIC_REVMASK (0x0002|0x0004|0x0008|0x0010|0x0020) -#define XL_NETDIAG_UPPER_BYTES_ENABLE 0x0040 -#define XL_NETDIAG_STATS_ENABLED 0x0080 -#define XL_NETDIAG_TX_FATALERR 0x0100 -#define XL_NETDIAG_TRANSMITTING 0x0200 -#define XL_NETDIAG_RX_ENABLED 0x0400 -#define XL_NETDIAG_TX_ENABLED 0x0800 -#define XL_NETDIAG_FIFO_LOOPBACK 0x1000 -#define XL_NETDIAG_MAC_LOOPBACK 0x2000 -#define XL_NETDIAG_ENDEC_LOOPBACK 0x4000 -#define XL_NETDIAG_EXTERNAL_LOOP 0x8000 - -/* - * Window 5 - */ -#define XL_W5_STAT_ENB 0x0C -#define XL_W5_INTR_ENB 0x0A -#define XL_W5_RECLAIM_THRESH 0x09 /* 3c905B only */ -#define XL_W5_RX_FILTER 0x08 -#define XL_W5_RX_EARLYTHRESH 0x06 -#define XL_W5_TX_AVAILTHRESH 0x02 -#define XL_W5_TX_STARTTHRESH 0x00 - -/* - * RX filter bits - */ -#define XL_RXFILTER_INDIVIDUAL 0x01 -#define XL_RXFILTER_ALLMULTI 0x02 -#define XL_RXFILTER_BROADCAST 0x04 -#define XL_RXFILTER_ALLFRAMES 0x08 -#define XL_RXFILTER_MULTIHASH 0x10 /* 3c905B only */ - -/* - * Window 6 (stats) - */ -#define XL_W6_TX_BYTES_OK 0x0C -#define XL_W6_RX_BYTES_OK 0x0A -#define XL_W6_UPPER_FRAMES_OK 0x09 -#define XL_W6_DEFERRED 0x08 -#define XL_W6_RX_OK 0x07 -#define XL_W6_TX_OK 0x06 -#define XL_W6_RX_OVERRUN 0x05 -#define XL_W6_COL_LATE 0x04 -#define XL_W6_COL_SINGLE 0x03 -#define XL_W6_COL_MULTIPLE 0x02 -#define XL_W6_SQE_ERRORS 0x01 -#define XL_W6_CARRIER_LOST 0x00 - -/* - * Window 7 (bus master control) - */ -#define XL_W7_BM_ADDR 0x00 -#define XL_W7_BM_LEN 0x06 -#define XL_W7_BM_STATUS 0x0B -#define XL_W7_BM_TIMEr 0x0A - -/* - * bus master control registers - */ -#define XL_BM_PKTSTAT 0x20 -#define XL_BM_DOWNLISTPTR 0x24 -#define XL_BM_FRAGADDR 0x28 -#define XL_BM_FRAGLEN 0x2C -#define XL_BM_TXFREETHRESH 0x2F -#define XL_BM_UPPKTSTAT 0x30 -#define XL_BM_UPLISTPTR 0x38 - -#define XL_LAST_FRAG 0x80000000 - -/* - * Boomerang/Cyclone TX/RX list structure. - * For the TX lists, bits 0 to 12 of the status word indicate - * length. - * This looks suspiciously like the ThunderLAN, doesn't it. - */ -struct xl_frag { - u_int32_t xl_addr; /* 63 addr/len pairs */ - u_int32_t xl_len; -}; - -struct xl_list { - u_int32_t xl_next; /* final entry has 0 nextptr */ - u_int32_t xl_status; - struct xl_frag xl_frag[63]; -}; - -struct xl_list_onefrag { - u_int32_t xl_next; /* final entry has 0 nextptr */ - u_int32_t xl_status; - struct xl_frag xl_frag; -}; - -#define XL_MAXFRAGS 63 -#define XL_RX_LIST_CNT 32 -#define XL_TX_LIST_CNT 32 -#define XL_MIN_FRAMELEN 60 -#define XL_INC(x, y) (x) = (x + 1) % (y) - -struct xl_list_data { - struct xl_list_onefrag xl_rx_list[XL_RX_LIST_CNT]; - struct xl_list xl_tx_list[XL_TX_LIST_CNT]; - unsigned char xl_pad[XL_MIN_FRAMELEN]; -}; - -struct xl_chain { - struct xl_list *xl_ptr; - struct mbuf *xl_mbuf; - struct xl_chain *xl_next; - struct xl_chain *xl_prev; - u_int32_t xl_phys; -}; - -struct xl_chain_onefrag { - struct xl_list_onefrag *xl_ptr; - struct mbuf *xl_mbuf; - struct xl_chain_onefrag *xl_next; -}; - -struct xl_chain_data { - struct xl_chain_onefrag xl_rx_chain[XL_RX_LIST_CNT]; - struct xl_chain xl_tx_chain[XL_TX_LIST_CNT]; - - struct xl_chain_onefrag *xl_rx_head; - - /* 3c90x "boomerang" queuing stuff */ - struct xl_chain *xl_tx_head; - struct xl_chain *xl_tx_tail; - struct xl_chain *xl_tx_free; - - /* 3c90xB "cyclone/hurricane/tornade" stuff */ - int xl_tx_prod; - int xl_tx_cons; - int xl_tx_cnt; -}; - -#define XL_RXSTAT_LENMASK 0x00001FFF -#define XL_RXSTAT_UP_ERROR 0x00004000 -#define XL_RXSTAT_UP_CMPLT 0x00008000 -#define XL_RXSTAT_UP_OVERRUN 0x00010000 -#define XL_RXSTAT_RUNT 0x00020000 -#define XL_RXSTAT_ALIGN 0x00040000 -#define XL_RXSTAT_CRC 0x00080000 -#define XL_RXSTAT_OVERSIZE 0x00100000 -#define XL_RXSTAT_DRIBBLE 0x00800000 -#define XL_RXSTAT_UP_OFLOW 0x01000000 -#define XL_RXSTAT_IPCKERR 0x02000000 /* 3c905B only */ -#define XL_RXSTAT_TCPCKERR 0x04000000 /* 3c905B only */ -#define XL_RXSTAT_UDPCKERR 0x08000000 /* 3c905B only */ -#define XL_RXSTAT_BUFEN 0x10000000 /* 3c905B only */ -#define XL_RXSTAT_IPCKOK 0x20000000 /* 3c905B only */ -#define XL_RXSTAT_TCPCOK 0x40000000 /* 3c905B only */ -#define XL_RXSTAT_UDPCKOK 0x80000000 /* 3c905B only */ - -#define XL_TXSTAT_LENMASK 0x00001FFF -#define XL_TXSTAT_CRCDIS 0x00002000 -#define XL_TXSTAT_TX_INTR 0x00008000 -#define XL_TXSTAT_DL_COMPLETE 0x00010000 -#define XL_TXSTAT_IPCKSUM 0x02000000 /* 3c905B only */ -#define XL_TXSTAT_TCPCKSUM 0x04000000 /* 3c905B only */ -#define XL_TXSTAT_UDPCKSUM 0x08000000 /* 3c905B only */ -#define XL_TXSTAT_RND_DEFEAT 0x10000000 /* 3c905B only */ -#define XL_TXSTAT_EMPTY 0x20000000 /* 3c905B only */ -#define XL_TXSTAT_DL_INTR 0x80000000 - -#define XL_CAPABILITY_BM 0x20 - -struct xl_type { - u_int16_t xl_vid; - u_int16_t xl_did; - char *xl_name; -}; - -struct xl_mii_frame { - u_int8_t mii_stdelim; - u_int8_t mii_opcode; - u_int8_t mii_phyaddr; - u_int8_t mii_regaddr; - u_int8_t mii_turnaround; - u_int16_t mii_data; -}; - -/* - * MII constants - */ -#define XL_MII_STARTDELIM 0x01 -#define XL_MII_READOP 0x02 -#define XL_MII_WRITEOP 0x01 -#define XL_MII_TURNAROUND 0x02 - -/* - * The 3C905B adapters implement a few features that we want to - * take advantage of, namely the multicast hash filter. With older - * chips, you only have the option of turning on reception of all - * multicast frames, which is kind of lame. - * - * We also use this to decide on a transmit strategy. For the 3c90xB - * cards, we can use polled descriptor mode, which reduces CPU overhead. - */ -#define XL_TYPE_905B 1 -#define XL_TYPE_90X 2 - -struct xl_softc { - struct device sc_dev; /* generic device structure */ - void * xl_intrhand; /* interrupt handler cookie */ - struct arpcom arpcom; /* interface info */ - struct ifmedia ifmedia; /* media info */ - mii_data_t sc_mii; /* mii bus */ - bus_space_handle_t xl_bhandle; - bus_space_tag_t xl_btag; - struct xl_type *xl_info; /* 3Com adapter info */ - u_int8_t xl_hasmii; /* whether we have mii or not */ - u_int8_t xl_unit; /* interface number */ - u_int8_t xl_type; - u_int32_t xl_xcvr; - u_int16_t xl_media; - u_int16_t xl_caps; - u_int8_t xl_stats_no_timeout; - u_int16_t xl_tx_thresh; - int xl_if_flags; - caddr_t xl_ldata_ptr; - struct xl_list_data *xl_ldata; - struct xl_chain_data xl_cdata; -}; - -#define xl_rx_goodframes(x) \ - ((x.xl_upper_frames_ok & 0x03) << 8) | x.xl_rx_frames_ok - -#define xl_tx_goodframes(x) \ - ((x.xl_upper_frames_ok & 0x30) << 4) | x.xl_tx_frames_ok - -struct xl_stats { - u_int8_t xl_carrier_lost; - u_int8_t xl_sqe_errs; - u_int8_t xl_tx_multi_collision; - u_int8_t xl_tx_single_collision; - u_int8_t xl_tx_late_collision; - u_int8_t xl_rx_overrun; - u_int8_t xl_tx_frames_ok; - u_int8_t xl_rx_frames_ok; - u_int8_t xl_tx_deferred; - u_int8_t xl_upper_frames_ok; - u_int16_t xl_rx_bytes_ok; - u_int16_t xl_tx_bytes_ok; - u_int16_t status; -}; - -/* - * register space access macros - */ -#define CSR_WRITE_4(sc, reg, val) \ - bus_space_write_4(sc->xl_btag, sc->xl_bhandle, reg, val) -#define CSR_WRITE_2(sc, reg, val) \ - bus_space_write_2(sc->xl_btag, sc->xl_bhandle, reg, val) -#define CSR_WRITE_1(sc, reg, val) \ - bus_space_write_1(sc->xl_btag, sc->xl_bhandle, reg, val) - -#define CSR_READ_4(sc, reg) \ - bus_space_read_4(sc->xl_btag, sc->xl_bhandle, reg) -#define CSR_READ_2(sc, reg) \ - bus_space_read_2(sc->xl_btag, sc->xl_bhandle, reg) -#define CSR_READ_1(sc, reg) \ - bus_space_read_1(sc->xl_btag, sc->xl_bhandle, reg) - -#define XL_SEL_WIN(x) \ - CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_WINSEL | x) -#define XL_TIMEOUT 1000 - -/* - * General constants that are fun to know. - * - * 3Com PCI vendor ID - */ -#define TC_VENDORID 0x10B7 - -/* - * 3Com chip device IDs. - */ -#define TC_DEVICEID_TORNADO_HOMECONNECT 0x4500 -#define TC_DEVICEID_BOOMERANG_10BT 0x9000 -#define TC_DEVICEID_BOOMERANG_10BT_COMBO 0x9001 -#define TC_DEVICEID_BOOMERANG_10_100BT 0x9050 -#define TC_DEVICEID_BOOMERANG_100BT4 0x9051 -#define TC_DEVICEID_KRAKATOA_10BT 0x9004 -#define TC_DEVICEID_KRAKATOA_10BT_COMBO 0x9005 -#define TC_DEVICEID_KRAKATOA_10BT_TPC 0x9006 -#define TC_DEVICEID_CYCLONE_10FL 0x900A -#define TC_DEVICEID_HURRICANE_10_100BT 0x9055 -#define TC_DEVICEID_CYCLONE_10_100BT4 0x9056 -#define TC_DEVICEID_CYCLONE_10_100_COMBO 0x9058 -#define TC_DEVICEID_CYCLONE_10_100FX 0x905A -#define TC_DEVICEID_TORNADO_10_100BT 0x9200 -#define TC_DEVICEID_HURRICANE_10_100BT_SERV 0x9800 -#define TC_DEVICEID_TORNADO_10_100BT_SERV 0x9805 -#define TC_DEVICEID_HURRICANE_SOHO100TX 0x7646 - -/* - * PCI low memory base and low I/O base register, and - * other PCI registers. Note: some are only available on - * the 3c905B, in particular those that related to power management. - */ - -#define XL_PCI_VENDOR_ID 0x00 -#define XL_PCI_DEVICE_ID 0x02 -#define XL_PCI_COMMAND 0x04 -#define XL_PCI_STATUS 0x06 -#define XL_PCI_CLASSCODE 0x09 -#define XL_PCI_LATENCY_TIMER 0x0D -#define XL_PCI_HEADER_TYPE 0x0E -#define XL_PCI_LOIO 0x10 -#define XL_PCI_LOMEM 0x14 -#define XL_PCI_BIOSROM 0x30 -#define XL_PCI_INTLINE 0x3C -#define XL_PCI_INTPIN 0x3D -#define XL_PCI_MINGNT 0x3E -#define XL_PCI_MINLAT 0x0F -#define XL_PCI_RESETOPT 0x48 -#define XL_PCI_EEPROM_DATA 0x4C - -/* 3c905B-only registers */ -#define XL_PCI_CAPID 0xDC /* 8 bits */ -#define XL_PCI_NEXTPTR 0xDD /* 8 bits */ -#define XL_PCI_PWRMGMTCAP 0xDE /* 16 bits */ -#define XL_PCI_PWRMGMTCTRL 0xE0 /* 16 bits */ - -#define XL_PSTATE_MASK 0x0003 -#define XL_PSTATE_D0 0x0000 -#define XL_PSTATE_D1 0x0002 -#define XL_PSTATE_D2 0x0002 -#define XL_PSTATE_D3 0x0003 -#define XL_PME_EN 0x0010 -#define XL_PME_STATUS 0x8000 - -#ifdef __alpha__ -#undef vtophys -#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) -#endif - -#ifndef ETHER_ALIGN -#define ETHER_ALIGN 2 -#endif diff --git a/sys/dev/pci/pccbb.c b/sys/dev/pci/pccbb.c new file mode 100644 index 00000000000..e6d2ae43517 --- /dev/null +++ b/sys/dev/pci/pccbb.c @@ -0,0 +1,3100 @@ +/* $OpenBSD: pccbb.c,v 1.1 2000/04/08 05:50:51 aaron Exp $ */ +/* $NetBSD: pccbb.c,v 1.37 2000/03/23 07:01:40 thorpej Exp $ */ + +/* + * Copyright (c) 1998, 1999 and 2000 + * HAYAKAWA Koichi. 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 HAYAKAWA Koichi. + * 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. + */ + +/* +#define CBB_DEBUG +#define SHOW_REGS +#define PCCBB_PCMCIA_POLL +*/ +/* #define CBB_DEBUG */ + +/* +#define CB_PCMCIA_POLL +#define CB_PCMCIA_POLL_ONLY +#define LEVEL2 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include + +#include + +#include +#include + +#include +#include +#include + +#ifndef __NetBSD_Version__ +struct cfdriver cbb_cd = { + NULL, "cbb", DV_DULL +}; +#endif + +#if defined CBB_DEBUG +#define DPRINTF(x) printf x +#define STATIC +#else +#define DPRINTF(x) +#define STATIC static +#endif + +int pcicbbmatch __P((struct device *, void *, void *)); +void pccbbattach __P((struct device *, struct device *, void *)); +int pccbbintr __P((void *)); +static void pci113x_insert __P((void *)); +static int pccbbintr_function __P((struct pccbb_softc *)); + +static int pccbb_detect_card __P((struct pccbb_softc *)); + +static void pccbb_pcmcia_write __P((struct pcic_handle *, int, u_int8_t)); +static u_int8_t pccbb_pcmcia_read __P((struct pcic_handle *, int)); +#define Pcic_read(ph, reg) ((ph)->ph_read((ph), (reg))) +#define Pcic_write(ph, reg, val) ((ph)->ph_write((ph), (reg), (val))) + +STATIC int cb_reset __P((struct pccbb_softc *)); +STATIC int cb_detect_voltage __P((struct pccbb_softc *)); +STATIC int cbbprint __P((void *, const char *)); + +static int cb_chipset __P((u_int32_t, int *)); +STATIC void pccbb_pcmcia_attach_setup __P((struct pccbb_softc *, + struct pcmciabus_attach_args *)); +#if 0 +STATIC void pccbb_pcmcia_attach_card __P((struct pcic_handle *)); +STATIC void pccbb_pcmcia_detach_card __P((struct pcic_handle *, int)); +STATIC void pccbb_pcmcia_deactivate_card __P((struct pcic_handle *)); +#endif + +STATIC int pccbb_ctrl __P((cardbus_chipset_tag_t, int)); +STATIC int pccbb_power __P((cardbus_chipset_tag_t, int)); +STATIC int pccbb_cardenable __P((struct pccbb_softc * sc, int function)); +#if !rbus +static int pccbb_io_open __P((cardbus_chipset_tag_t, int, u_int32_t, + u_int32_t)); +static int pccbb_io_close __P((cardbus_chipset_tag_t, int)); +static int pccbb_mem_open __P((cardbus_chipset_tag_t, int, u_int32_t, + u_int32_t)); +static int pccbb_mem_close __P((cardbus_chipset_tag_t, int)); +#endif /* !rbus */ +static void *pccbb_intr_establish __P((struct pccbb_softc *, int irq, + int level, int (*ih) (void *), void *sc)); +static void pccbb_intr_disestablish __P((struct pccbb_softc *, void *ih)); + +static void *pccbb_cb_intr_establish __P((cardbus_chipset_tag_t, int irq, + int level, int (*ih) (void *), void *sc)); +static void pccbb_cb_intr_disestablish __P((cardbus_chipset_tag_t ct, void *ih)); + +static cardbustag_t pccbb_make_tag __P((cardbus_chipset_tag_t, int, int, int)); +static void pccbb_free_tag __P((cardbus_chipset_tag_t, cardbustag_t)); +static cardbusreg_t pccbb_conf_read __P((cardbus_chipset_tag_t, cardbustag_t, + int)); +static void pccbb_conf_write __P((cardbus_chipset_tag_t, cardbustag_t, int, + cardbusreg_t)); +static void pccbb_chipinit __P((struct pccbb_softc *)); + +STATIC int pccbb_pcmcia_mem_alloc __P((pcmcia_chipset_handle_t, bus_size_t, + struct pcmcia_mem_handle *)); +STATIC void pccbb_pcmcia_mem_free __P((pcmcia_chipset_handle_t, + struct pcmcia_mem_handle *)); +STATIC int pccbb_pcmcia_mem_map __P((pcmcia_chipset_handle_t, int, bus_addr_t, + bus_size_t, struct pcmcia_mem_handle *, bus_addr_t *, int *)); +STATIC void pccbb_pcmcia_mem_unmap __P((pcmcia_chipset_handle_t, int)); +STATIC int pccbb_pcmcia_io_alloc __P((pcmcia_chipset_handle_t, bus_addr_t, + bus_size_t, bus_size_t, struct pcmcia_io_handle *)); +STATIC void pccbb_pcmcia_io_free __P((pcmcia_chipset_handle_t, + struct pcmcia_io_handle *)); +STATIC int pccbb_pcmcia_io_map __P((pcmcia_chipset_handle_t, int, bus_addr_t, + bus_size_t, struct pcmcia_io_handle *, int *)); +STATIC void pccbb_pcmcia_io_unmap __P((pcmcia_chipset_handle_t, int)); +STATIC void *pccbb_pcmcia_intr_establish __P((pcmcia_chipset_handle_t, + struct pcmcia_function *, int, int (*)(void *), void *)); +STATIC void pccbb_pcmcia_intr_disestablish __P((pcmcia_chipset_handle_t, + void *)); +STATIC void pccbb_pcmcia_socket_enable __P((pcmcia_chipset_handle_t)); +STATIC void pccbb_pcmcia_socket_disable __P((pcmcia_chipset_handle_t)); +STATIC int pccbb_pcmcia_card_detect __P((pcmcia_chipset_handle_t pch)); + +static void pccbb_pcmcia_do_io_map __P((struct pcic_handle *, int)); +static void pccbb_pcmcia_wait_ready __P((struct pcic_handle *)); +static void pccbb_pcmcia_do_mem_map __P((struct pcic_handle *, int)); +static void pccbb_powerhook __P((int, void *)); + +/* bus-space allocation and deallocation functions */ +#if rbus + +static int pccbb_rbus_cb_space_alloc __P((cardbus_chipset_tag_t, rbus_tag_t, + bus_addr_t addr, bus_size_t size, bus_addr_t mask, bus_size_t align, + int flags, bus_addr_t * addrp, bus_space_handle_t * bshp)); +static int pccbb_rbus_cb_space_free __P((cardbus_chipset_tag_t, rbus_tag_t, + bus_space_handle_t, bus_size_t)); + +#endif /* rbus */ + +#if rbus + +static int pccbb_open_win __P((struct pccbb_softc *, bus_space_tag_t, + bus_addr_t, bus_size_t, bus_space_handle_t, int flags)); +static int pccbb_close_win __P((struct pccbb_softc *, bus_space_tag_t, + bus_space_handle_t, bus_size_t)); +static int pccbb_winlist_insert __P((struct pccbb_win_chain_head *, bus_addr_t, + bus_size_t, bus_space_handle_t, int)); +static int pccbb_winlist_delete __P((struct pccbb_win_chain_head *, + bus_space_handle_t, bus_size_t)); +static void pccbb_winset __P((bus_addr_t align, struct pccbb_softc *, + bus_space_tag_t)); +void pccbb_winlist_show(struct pccbb_win_chain *); + +#endif /* rbus */ + +/* for config_defer */ +static void pccbb_pci_callback __P((struct device *)); + +#if defined SHOW_REGS +static void cb_show_regs __P((pci_chipset_tag_t pc, pcitag_t tag, + bus_space_tag_t memt, bus_space_handle_t memh)); +#endif + +struct cfattach cbb_pci_ca = { + sizeof(struct pccbb_softc), pcicbbmatch, pccbbattach +}; + +static struct pcmcia_chip_functions pccbb_pcmcia_funcs = { + pccbb_pcmcia_mem_alloc, + pccbb_pcmcia_mem_free, + pccbb_pcmcia_mem_map, + pccbb_pcmcia_mem_unmap, + pccbb_pcmcia_io_alloc, + pccbb_pcmcia_io_free, + pccbb_pcmcia_io_map, + pccbb_pcmcia_io_unmap, + pccbb_pcmcia_intr_establish, + pccbb_pcmcia_intr_disestablish, + pccbb_pcmcia_socket_enable, + pccbb_pcmcia_socket_disable, + pccbb_pcmcia_card_detect +}; + +#if rbus +static struct cardbus_functions pccbb_funcs = { + pccbb_rbus_cb_space_alloc, + pccbb_rbus_cb_space_free, + pccbb_cb_intr_establish, + pccbb_cb_intr_disestablish, + pccbb_ctrl, + pccbb_power, + pccbb_make_tag, + pccbb_free_tag, + pccbb_conf_read, + pccbb_conf_write, +}; +#else +static struct cardbus_functions pccbb_funcs = { + pccbb_ctrl, + pccbb_power, + pccbb_mem_open, + pccbb_mem_close, + pccbb_io_open, + pccbb_io_close, + pccbb_cb_intr_establish, + pccbb_cb_intr_disestablish, + pccbb_make_tag, + pccbb_conf_read, + pccbb_conf_write, +}; +#endif + +int +pcicbbmatch(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct pci_attach_args *pa = (struct pci_attach_args *)aux; + + if (PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE && + PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_BRIDGE_CARDBUS && + PCI_INTERFACE(pa->pa_class) == 0) { + return 1; + } + + return 0; +} + +#define MAKEID(vendor, prod) (((vendor) << PCI_VENDOR_SHIFT) \ + | ((prod) << PCI_PRODUCT_SHIFT)) + +struct yenta_chipinfo { + pcireg_t yc_id; /* vendor tag | product tag */ + int yc_chiptype; + int yc_flags; +} yc_chipsets[] = { + /* Texas Instruments chips */ + { MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1130), CB_TI113X, + PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32}, + { MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1131), CB_TI113X, + PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32}, + { MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1250), CB_TI12XX, + PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32}, + { MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1220), CB_TI12XX, + PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32}, + { MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1221), CB_TI12XX, + PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32}, + { MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1225), CB_TI12XX, + PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32}, + { MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1251), CB_TI12XX, + PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32}, + { MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1251B), CB_TI12XX, + PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32}, + { MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1211), CB_TI12XX, + PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32}, + { MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1420), CB_TI12XX, + PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32}, + { MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1450), CB_TI12XX, + PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32}, + { MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1451), CB_TI12XX, + PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32}, + + /* Ricoh chips */ + { MAKEID(PCI_VENDOR_RICOH, PCI_PRODUCT_RICOH_RF5C475), CB_RX5C47X, + PCCBB_PCMCIA_MEM_32}, + { MAKEID(PCI_VENDOR_RICOH, PCI_PRODUCT_RICOH_RF5C476), CB_RX5C47X, + PCCBB_PCMCIA_MEM_32}, + { MAKEID(PCI_VENDOR_RICOH, PCI_PRODUCT_RICOH_RF5C477), CB_RX5C47X, + PCCBB_PCMCIA_MEM_32}, + { MAKEID(PCI_VENDOR_RICOH, PCI_PRODUCT_RICOH_RF5C478), CB_RX5C47X, + PCCBB_PCMCIA_MEM_32}, + { MAKEID(PCI_VENDOR_RICOH, PCI_PRODUCT_RICOH_RF5C465), CB_RX5C46X, + PCCBB_PCMCIA_MEM_32}, + { MAKEID(PCI_VENDOR_RICOH, PCI_PRODUCT_RICOH_RF5C466), CB_RX5C46X, + PCCBB_PCMCIA_MEM_32}, + + /* Toshiba products */ + { MAKEID(PCI_VENDOR_TOSHIBA2, PCI_PRODUCT_TOSHIBA2_ToPIC95), + CB_TOPIC95, PCCBB_PCMCIA_MEM_32}, + { MAKEID(PCI_VENDOR_TOSHIBA2, PCI_PRODUCT_TOSHIBA2_ToPIC95B), + CB_TOPIC95B, PCCBB_PCMCIA_MEM_32}, + { MAKEID(PCI_VENDOR_TOSHIBA2, PCI_PRODUCT_TOSHIBA2_ToPIC97), + CB_TOPIC97, PCCBB_PCMCIA_MEM_32}, + { MAKEID(PCI_VENDOR_TOSHIBA2, PCI_PRODUCT_TOSHIBA2_ToPIC100), + CB_TOPIC97, PCCBB_PCMCIA_MEM_32}, + + /* Cirrus Logic products */ + { MAKEID(PCI_VENDOR_CIRRUS, PCI_PRODUCT_CIRRUS_CL_PD6832), + CB_CIRRUS, PCCBB_PCMCIA_MEM_32}, + { MAKEID(PCI_VENDOR_CIRRUS, PCI_PRODUCT_CIRRUS_CL_PD6833), + CB_CIRRUS, PCCBB_PCMCIA_MEM_32}, + + /* sentinel, or Generic chip */ + { 0 /* null id */ , CB_UNKNOWN, PCCBB_PCMCIA_MEM_32}, +}; + +static int +cb_chipset(pci_id, flagp) + u_int32_t pci_id; + int *flagp; +{ + struct yenta_chipinfo *yc; + + /* Loop over except the last default entry. */ + for (yc = yc_chipsets; yc < yc_chipsets + + sizeof(yc_chipsets) / sizeof(yc_chipsets[0]) - 1; yc++) + if (pci_id != yc->yc_id) + break; + + if (flagp != NULL) + *flagp = yc->yc_flags; + + return (yc->yc_chiptype); +} + +static void +pccbb_shutdown(void *arg) +{ + struct pccbb_softc *sc = arg; + pcireg_t command; + + DPRINTF(("%s: shutdown\n", sc->sc_dev.dv_xname)); + bus_space_write_4(sc->sc_base_memt, sc->sc_base_memh, CB_SOCKET_MASK, + 0); + + command = pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG); + + command &= ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | + PCI_COMMAND_MASTER_ENABLE); + pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG, command); + +} + +void +pccbbattach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct pccbb_softc *sc = (void *)self; + struct pci_attach_args *pa = aux; + pci_chipset_tag_t pc = pa->pa_pc; + pcireg_t sock_base, busreg; + bus_addr_t sockbase; + int flags; + + sc->sc_chipset = cb_chipset(pa->pa_id, &flags); + +#ifdef CBB_DEBUG + printf(" (chipflags %x)", flags); +#endif + + TAILQ_INIT(&sc->sc_memwindow); + TAILQ_INIT(&sc->sc_iowindow); + +#if rbus + sc->sc_rbus_iot = rbus_pccbb_parent_io(pa); + sc->sc_rbus_memt = rbus_pccbb_parent_mem(pa); +#endif /* rbus */ + + sc->sc_base_memh = 0; + + /* + * MAP socket registers and ExCA registers on memory-space + * When no valid address is set on socket base registers (on pci + * config space), get it not polite way. + */ + sock_base = pci_conf_read(pc, pa->pa_tag, PCI_SOCKBASE); + + if (PCI_MAPREG_MEM_ADDR(sock_base) >= 0x100000 && + PCI_MAPREG_MEM_ADDR(sock_base) != 0xfffffff0) { + /* The address must be valid. */ + if (pci_mapreg_map(pa, PCI_SOCKBASE, PCI_MAPREG_TYPE_MEM, 0, + &sc->sc_base_memt, &sc->sc_base_memh, &sockbase, NULL)) { + printf("%s: can't map socket base address 0x%x\n", + sc->sc_dev.dv_xname, sock_base); + /* + * I think it's funny: socket base registers must be + * mapped on memory space, but ... + */ + if (pci_mapreg_map(pa, PCI_SOCKBASE, PCI_MAPREG_TYPE_IO, + 0, &sc->sc_base_memt, &sc->sc_base_memh, &sockbase, + NULL)) { + printf("%s: can't map socket base address" + " 0x%lx: io mode\n", sc->sc_dev.dv_xname, + sockbase); + /* give up... allocate reg space via rbus. */ + printf("***** HOI!\n"); + sc->sc_base_memh = 0; + pci_conf_write(pc, pa->pa_tag, PCI_SOCKBASE, 0); + } + } else { + DPRINTF(("%s: socket base address 0x%lx\n", + sc->sc_dev.dv_xname, sockbase)); + } + } + + sc->sc_mem_start = 0; /* XXX */ + sc->sc_mem_end = 0xffffffff; /* XXX */ + + /* + * When interrupt isn't routed correctly, give up probing cbb and do + * not kill pcic-compatible port. + */ + if ((0 == pa->pa_intrline) || (255 == pa->pa_intrline)) { + printf("\n%s: NOT USED because of unconfigured interrupt\n", + sc->sc_dev.dv_xname); + return; + } + + /* + * When bus number isn't set correctly, give up using 32-bit CardBus + * mode. + */ + busreg = pci_conf_read(pc, pa->pa_tag, PCI_BUSNUM); +#if notyet + if (((busreg >> 8) & 0xff) == 0) { + printf("%s: CardBus support disabled because of unconfigured bus number\n", + sc->sc_dev.dv_xname); + flags |= PCCBB_PCMCIA_16BITONLY; + } +#endif + + /* pccbb_machdep.c end */ + +#if defined CBB_DEBUG + { + static char *intrname[5] = { "NON", "A", "B", "C", "D" }; + printf("%s: intrpin %s, intrtag %d\n", sc->sc_dev.dv_xname, + intrname[pa->pa_intrpin], pa->pa_intrline); + } +#endif + + /* setup softc */ + sc->sc_pc = pc; + sc->sc_iot = pa->pa_iot; + sc->sc_memt = pa->pa_memt; + sc->sc_dmat = pa->pa_dmat; + sc->sc_tag = pa->pa_tag; + sc->sc_function = pa->pa_function; + + sc->sc_intrline = pa->pa_intrline; + sc->sc_intrtag = pa->pa_intrtag; + sc->sc_intrpin = pa->pa_intrpin; + + sc->sc_pcmcia_flags = flags; /* set PCMCIA facility */ + + shutdownhook_establish(pccbb_shutdown, sc); + +#if 0 + config_defer(self, pccbb_pci_callback); +#endif + pccbb_pci_callback(self); +} + + + + +/* + * static void pccbb_pci_callback(struct device *self) + * + * The actual attach routine: get memory space for YENTA register + * space, setup YENTA register and route interrupt. + * + * This function should be deferred because this device may obtain + * memory space dynamically. This function must avoid obtaining + * memory area which has already kept for another device. Also, + * this function MUST be done before ISA attach process because this + * function kills pcic compatible port used by ISA pcic. + */ +static void +pccbb_pci_callback(self) + struct device *self; +{ + struct pccbb_softc *sc = (void *)self; + pci_chipset_tag_t pc = sc->sc_pc; + bus_space_tag_t base_memt; + bus_space_handle_t base_memh; + u_int32_t maskreg; + pci_intr_handle_t ih; + const char *intrstr = NULL; + bus_addr_t sockbase; + struct cbslot_attach_args cba; + struct pcmciabus_attach_args paa; + struct cardslot_attach_args caa; + struct cardslot_softc *csc; + + if (0 == sc->sc_base_memh) { + /* The socket registers aren't mapped correctly. */ +#if rbus + if (rbus_space_alloc(sc->sc_rbus_memt, 0, 0x1000, 0x0fff, + (sc->sc_chipset == CB_RX5C47X + || sc->sc_chipset == CB_TI113X) ? 0x10000 : 0x1000, + 0, &sockbase, &sc->sc_base_memh)) { + return; + } + sc->sc_base_memt = sc->sc_memt; + pci_conf_write(pc, sc->sc_tag, PCI_SOCKBASE, sockbase); + DPRINTF(("%s: CardBus resister address 0x%lx -> 0x%x\n", + sc->sc_dev.dv_xname, sockbase, pci_conf_read(pc, sc->sc_tag, + PCI_SOCKBASE))); +#else + sc->sc_base_memt = sc->sc_memt; +#if !defined CBB_PCI_BASE +#define CBB_PCI_BASE 0x20000000 +#endif + if (bus_space_alloc(sc->sc_base_memt, CBB_PCI_BASE, 0xffffffff, + 0x1000, 0x1000, 0, 0, &sockbase, &sc->sc_base_memh)) { + /* cannot allocate memory space */ + return; + } + pci_conf_write(pc, sc->sc_tag, PCI_SOCKBASE, sockbase); + DPRINTF(("%s: CardBus resister address 0x%x -> 0x%x\n", + sc->sc_dev.dv_xname, sock_base, pci_conf_read(pc, + sc->sc_tag, PCI_SOCKBASE))); +#endif + } + + /* bus bridge initialization */ + pccbb_chipinit(sc); + + base_memt = sc->sc_base_memt; /* socket regs memory tag */ + base_memh = sc->sc_base_memh; /* socket regs memory handle */ + + /* CSC Interrupt: Card detect interrupt on */ + maskreg = bus_space_read_4(base_memt, base_memh, CB_SOCKET_MASK); + maskreg |= CB_SOCKET_MASK_CD; /* Card detect intr is turned on. */ + bus_space_write_4(base_memt, base_memh, CB_SOCKET_MASK, maskreg); + /* reset interrupt */ + bus_space_write_4(base_memt, base_memh, CB_SOCKET_EVENT, + bus_space_read_4(base_memt, base_memh, CB_SOCKET_EVENT)); + + /* Map and establish the interrupt. */ + if (pci_intr_map(pc, sc->sc_intrtag, sc->sc_intrpin, + sc->sc_intrline, &ih)) { + printf("%s: couldn't map interrupt\n", sc->sc_dev.dv_xname); + return; + } + intrstr = pci_intr_string(pc, ih); + sc->sc_ih = pci_intr_establish(pc, ih, IPL_BIO, pccbbintr, sc, + sc->sc_dev.dv_xname); + + if (sc->sc_ih == NULL) { + printf("%s: couldn't establish interrupt", sc->sc_dev.dv_xname); + if (intrstr != NULL) { + printf(" at %s", intrstr); + } + printf("\n"); + return; + } + + printf(": %s\n", intrstr); + powerhook_establish(pccbb_powerhook, sc); + + { + u_int32_t sockstat = + bus_space_read_4(base_memt, base_memh, CB_SOCKET_STAT); + if (0 == (sockstat & CB_SOCKET_STAT_CD)) { + sc->sc_flags |= CBB_CARDEXIST; + } + } + + /* + * attach cardbus + */ + if (!(sc->sc_pcmcia_flags & PCCBB_PCMCIA_16BITONLY)) { + pcireg_t busreg = pci_conf_read(pc, sc->sc_tag, PCI_BUSNUM); + pcireg_t bhlc = pci_conf_read(pc, sc->sc_tag, PCI_BHLC_REG); + + /* initialize cbslot_attach */ + cba.cba_busname = "cardbus"; + cba.cba_iot = sc->sc_iot; + cba.cba_memt = sc->sc_memt; + cba.cba_dmat = sc->sc_dmat; + cba.cba_bus = (busreg >> 8) & 0x0ff; + cba.cba_cc = (void *)sc; + cba.cba_cf = &pccbb_funcs; + cba.cba_intrline = sc->sc_intrline; + +#if rbus + cba.cba_rbus_iot = sc->sc_rbus_iot; + cba.cba_rbus_memt = sc->sc_rbus_memt; +#endif + + cba.cba_cacheline = PCI_CACHELINE(bhlc); + cba.cba_lattimer = PCI_CB_LATENCY(busreg); + +#if defined CBB_DEBUG + printf("%s: cacheline 0x%x lattimer 0x%x\n", + sc->sc_dev.dv_xname, cba.cba_cacheline, cba.cba_lattimer); + printf("%s: bhlc 0x%x lscp 0x%x\n", sc->sc_dev.dv_xname, bhlc, + busreg); +#endif +#if defined SHOW_REGS + cb_show_regs(sc->sc_pc, sc->sc_tag, sc->sc_base_memt, + sc->sc_base_memh); +#endif + } + + pccbb_pcmcia_attach_setup(sc, &paa); + caa.caa_cb_attach = NULL; + if (!(sc->sc_pcmcia_flags & PCCBB_PCMCIA_16BITONLY)) { + caa.caa_cb_attach = &cba; + } + caa.caa_16_attach = &paa; + caa.caa_ph = &sc->sc_pcmcia_h; + + if (NULL != (csc = (void *)config_found(self, &caa, cbbprint))) { + DPRINTF(("pccbbattach: found cardslot\n")); + sc->sc_csc = csc; + } + + return; +} + + + + + +/* + * static void pccbb_chipinit(struct pccbb_softc *sc) + * + * This function initialize YENTA chip registers listed below: + * 1) PCI command reg, + * 2) PCI and CardBus latency timer, + * 3) disable legacy (PCIC-compatible) io, + * 4) route PCI interrupt, + * 5) close all memory and io windows. + */ +static void +pccbb_chipinit(sc) + struct pccbb_softc *sc; +{ + pci_chipset_tag_t pc = sc->sc_pc; + pcitag_t tag = sc->sc_tag; + pcireg_t reg; + + /* + * Set PCI command reg. + * Some laptop's BIOSes (i.e. TICO) do not enable CardBus chip. + */ + reg = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); + /* I believe it is harmless. */ + reg |= (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | + PCI_COMMAND_MASTER_ENABLE); + pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, reg); + + /* + * Set CardBus latency timer. + */ + reg = pci_conf_read(pc, tag, PCI_CB_LSCP_REG); + if (PCI_CB_LATENCY(reg) < 0x20) { + reg &= ~(PCI_CB_LATENCY_MASK << PCI_CB_LATENCY_SHIFT); + reg |= (0x20 << PCI_CB_LATENCY_SHIFT); + pci_conf_write(pc, tag, PCI_CB_LSCP_REG, reg); + } + DPRINTF(("CardBus latency timer 0x%x (%x)\n", + PCI_CB_LATENCY(reg), pci_conf_read(pc, tag, PCI_CB_LSCP_REG))); + + /* + * Set PCI latency timer. + */ + reg = pci_conf_read(pc, tag, PCI_BHLC_REG); + if (PCI_LATTIMER(reg) < 0x10) { + reg &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT); + reg |= (0x10 << PCI_LATTIMER_SHIFT); + pci_conf_write(pc, tag, PCI_BHLC_REG, reg); + } + DPRINTF(("PCI latency timer 0x%x (%x)\n", + PCI_LATTIMER(reg), pci_conf_read(pc, tag, PCI_BHLC_REG))); + + /* Disable legacy register mapping. */ + switch (sc->sc_chipset) { + case CB_RX5C46X: /* fallthrough */ +#if 0 + case CB_RX5C47X: +#endif + /* + * The legacy pcic io-port on Ricoh CardBus bridges cannot be + * disabled by substituting 0 into PCI_LEGACY register. Ricoh + * CardBus bridges have special bits on Bridge control reg (addr + * 0x3e on PCI config space). + */ + reg = pci_conf_read(pc, tag, PCI_BCR_INTR); + reg &= ~(CB_BCRI_RL_3E0_ENA | CB_BCRI_RL_3E2_ENA); + pci_conf_write(pc, tag, PCI_BCR_INTR, reg); + break; + + default: + /* XXX I don't know proper way to kill legacy I/O. */ + pci_conf_write(pc, tag, PCI_LEGACY, 0x0); + break; + } + + /* Route functional interrupts to PCI. */ + reg = pci_conf_read(pc, tag, PCI_BCR_INTR); + reg &= ~CB_BCR_INTR_IREQ_ENABLE; /* use PCI Intr */ + reg |= CB_BCR_WRITE_POST_ENABLE; /* enable write post */ + pci_conf_write(pc, tag, PCI_BCR_INTR, reg); + + switch (sc->sc_chipset) { + case CB_TI113X: + reg = pci_conf_read(pc, tag, PCI_CBCTRL); + /* This bit is shared, but may read as 0 on some chips, so set + it explicitly on both functions. */ + reg |= PCI113X_CBCTRL_PCI_IRQ_ENA; + /* CSC intr enable */ + reg |= PCI113X_CBCTRL_PCI_CSC; + /* functional intr prohibit */ + reg &= ~PCI113X_CBCTRL_PCI_INTR; + pci_conf_write(pc, tag, PCI_CBCTRL, reg); + break; + + case CB_TOPIC95B: + reg = pci_conf_read(pc, tag, TOPIC_SOCKET_CTRL); + reg |= TOPIC_SOCKET_CTRL_SCR_IRQSEL; + pci_conf_write(pc, tag, TOPIC_SOCKET_CTRL, reg); + + reg = pci_conf_read(pc, tag, TOPIC_SLOT_CTRL); + DPRINTF(("%s: topic slot ctrl reg 0x%x -> ", + sc->sc_dev.dv_xname, reg)); + reg |= (TOPIC_SLOT_CTRL_SLOTON | TOPIC_SLOT_CTRL_SLOTEN | + TOPIC_SLOT_CTRL_ID_LOCK | TOPIC_SLOT_CTRL_CARDBUS); + reg &= ~TOPIC_SLOT_CTRL_SWDETECT; + DPRINTF(("0x%x\n", reg)); + pci_conf_write(pc, tag, TOPIC_SLOT_CTRL, reg); + break; + } + + /* Close all memory and I/O windows. */ + pci_conf_write(pc, tag, PCI_CB_MEMBASE0, 0xffffffff); + pci_conf_write(pc, tag, PCI_CB_MEMLIMIT0, 0); + pci_conf_write(pc, tag, PCI_CB_MEMBASE1, 0xffffffff); + pci_conf_write(pc, tag, PCI_CB_MEMLIMIT1, 0); + pci_conf_write(pc, tag, PCI_CB_IOBASE0, 0xffffffff); + pci_conf_write(pc, tag, PCI_CB_IOLIMIT0, 0); + pci_conf_write(pc, tag, PCI_CB_IOBASE1, 0xffffffff); + pci_conf_write(pc, tag, PCI_CB_IOLIMIT1, 0); +} + + + + +/* + * STATIC void pccbb_pcmcia_attach_setup(struct pccbb_softc *sc, + * struct pcmciabus_attach_args *paa) + * + * This function attaches 16-bit PCcard bus. + */ +STATIC void +pccbb_pcmcia_attach_setup(sc, paa) + struct pccbb_softc *sc; + struct pcmciabus_attach_args *paa; +{ + struct pcic_handle *ph = &sc->sc_pcmcia_h; +#if rbus + rbus_tag_t rb; +#endif + + /* initialize pcmcia part in pccbb_softc */ + ph->ph_parent = (struct device *)sc; + ph->sock = sc->sc_function; + ph->flags = 0; + ph->shutdown = 0; + ph->ih_irq = sc->sc_intrline; + ph->ph_bus_t = sc->sc_base_memt; + ph->ph_bus_h = sc->sc_base_memh; + ph->ph_read = pccbb_pcmcia_read; + ph->ph_write = pccbb_pcmcia_write; + sc->sc_pct = &pccbb_pcmcia_funcs; + + /* + * We need to do a few things here: + * 1) Disable routing of CSC and functional interrupts to ISA IRQs by + * setting the IRQ numbers to 0. + * 2) Set bit 4 of PCIC_INTR, which is needed on some chips to enable + * routing of CSC interrupts (e.g. card removal) to PCI while in + * PCMCIA mode. We just leave this set all the time. + * 3) Enable card insertion/removal interrupts in case the chip also + * needs that while in PCMCIA mode. + * 4) Clear any pending CSC interrupt. + */ + Pcic_write(ph, PCIC_INTR, PCIC_INTR_ENABLE | PCIC_INTR_RESET); + Pcic_write(ph, PCIC_CSC_INTR, PCIC_CSC_INTR_CD_ENABLE); + Pcic_read(ph, PCIC_CSC); + + /* initialize pcmcia bus attachment */ + paa->paa_busname = "pcmcia"; + paa->pct = sc->sc_pct; + paa->pch = ph; + paa->iobase = 0; /* I don't use them */ + paa->iosize = 0; +#if rbus + rb = ((struct pccbb_softc *)(ph->ph_parent))->sc_rbus_iot; + paa->iobase = rb->rb_start + rb->rb_offset; + paa->iosize = rb->rb_end - rb->rb_start; +#endif + + return; +} + +#if 0 +STATIC void +pccbb_pcmcia_attach_card(ph) + struct pcic_handle *ph; +{ + if (ph->flags & PCIC_FLAG_CARDP) { + panic("pccbb_pcmcia_attach_card: already attached"); + } + + /* call the MI attach function */ + pcmcia_card_attach(ph->pcmcia); + + ph->flags |= PCIC_FLAG_CARDP; +} + +STATIC void +pccbb_pcmcia_detach_card(ph, flags) + struct pcic_handle *ph; + int flags; +{ + if (!(ph->flags & PCIC_FLAG_CARDP)) { + panic("pccbb_pcmcia_detach_card: already detached"); + } + + ph->flags &= ~PCIC_FLAG_CARDP; + + /* call the MI detach function */ + pcmcia_card_detach(ph->pcmcia, flags); +} +#endif + +/* + * int pccbbintr(arg) + * void *arg; + * This routine handles the interrupt from Yenta PCI-CardBus bridge + * itself. + */ +int +pccbbintr(arg) + void *arg; +{ + struct pccbb_softc *sc = (struct pccbb_softc *)arg; + u_int32_t sockevent, sockstate; + bus_space_tag_t memt = sc->sc_base_memt; + bus_space_handle_t memh = sc->sc_base_memh; + struct pcic_handle *ph = &sc->sc_pcmcia_h; + + sockevent = bus_space_read_4(memt, memh, CB_SOCKET_EVENT); + bus_space_write_4(memt, memh, CB_SOCKET_EVENT, sockevent); + Pcic_read(ph, PCIC_CSC); + + if (sockevent == 0) { + /* This intr is not for me: it may be for my child devices. */ + return (pccbbintr_function(sc)); + } + + if (sockevent & CB_SOCKET_EVENT_CD) { + sockstate = bus_space_read_4(memt, memh, CB_SOCKET_STAT); + if (CB_SOCKET_STAT_CD == (sockstate & CB_SOCKET_STAT_CD)) { + /* A card should be removed. */ + if (sc->sc_flags & CBB_CARDEXIST) { + DPRINTF(("%s: 0x%08x", sc->sc_dev.dv_xname, + sockevent)); + DPRINTF((" card removed, 0x%08x\n", sockstate)); + sc->sc_flags &= ~CBB_CARDEXIST; + if (sc->sc_csc->sc_status & + CARDSLOT_STATUS_CARD_16) { +#if 0 + struct pcic_handle *ph = + &sc->sc_pcmcia_h; + + pcmcia_card_deactivate(ph->pcmcia); + pccbb_pcmcia_socket_disable(ph); + pccbb_pcmcia_detach_card(ph, + DETACH_FORCE); +#endif + cardslot_event_throw(sc->sc_csc, + CARDSLOT_EVENT_REMOVAL_16); + } else if (sc->sc_csc->sc_status & + CARDSLOT_STATUS_CARD_CB) { + /* Cardbus intr removed */ + cardslot_event_throw(sc->sc_csc, + CARDSLOT_EVENT_REMOVAL_CB); + } + } + } else if (0x00 == (sockstate & CB_SOCKET_STAT_CD) && + /* + * The pccbbintr may called from powerdown hook when + * the system resumed, to detect the card + * insertion/removal during suspension. + */ + (sc->sc_flags & CBB_CARDEXIST) == 0) { + if (sc->sc_flags & CBB_INSERTING) { + untimeout(pci113x_insert, sc); + } + timeout(pci113x_insert, sc, hz / 10); + sc->sc_flags |= CBB_INSERTING; + } + } + + return (1); +} + +/* + * static int pccbbintr_function(struct pccbb_softc *sc) + * + * This function calls each interrupt handler registered at the + * bridge. The interrupt handlers are called in registered order. + */ +static int +pccbbintr_function(sc) + struct pccbb_softc *sc; +{ + int retval = 0, val; + struct pccbb_intrhand_list *pil; + + for (pil = sc->sc_pil; pil != NULL; pil = pil->pil_next) { + val = (*pil->pil_func) (pil->pil_arg); + retval = retval == 1 ? 1 : + retval == 0 ? val : val != 0 ? val : retval; + } + + return retval; +} + +static void +pci113x_insert(arg) + void *arg; +{ + struct pccbb_softc *sc = (struct pccbb_softc *)arg; + u_int32_t sockevent, sockstate; + + sockevent = bus_space_read_4(sc->sc_base_memt, sc->sc_base_memh, + CB_SOCKET_EVENT); + sockstate = bus_space_read_4(sc->sc_base_memt, sc->sc_base_memh, + CB_SOCKET_STAT); + + if (0 == (sockstate & CB_SOCKET_STAT_CD)) { /* card exist */ + DPRINTF(("%s: 0x%08x", sc->sc_dev.dv_xname, sockevent)); + DPRINTF((" card inserted, 0x%08x\n", sockstate)); + sc->sc_flags |= CBB_CARDEXIST; + /* call pccard interrupt handler here */ + if (sockstate & CB_SOCKET_STAT_16BIT) { + /* 16-bit card found */ +/* pccbb_pcmcia_attach_card(&sc->sc_pcmcia_h); */ + cardslot_event_throw(sc->sc_csc, + CARDSLOT_EVENT_INSERTION_16); + } else if (sockstate & CB_SOCKET_STAT_CB) { + /* cardbus card found */ +/* cardbus_attach_card(sc->sc_csc); */ + cardslot_event_throw(sc->sc_csc, + CARDSLOT_EVENT_INSERTION_CB); + } else { + /* who are you? */ + } + } else { + timeout(pci113x_insert, sc, hz / 10); + } +} + +#define PCCBB_PCMCIA_OFFSET 0x800 +static u_int8_t +pccbb_pcmcia_read(ph, reg) + struct pcic_handle *ph; + int reg; +{ + return bus_space_read_1(ph->ph_bus_t, ph->ph_bus_h, + PCCBB_PCMCIA_OFFSET + reg); +} + +static void +pccbb_pcmcia_write(ph, reg, val) + struct pcic_handle *ph; + int reg; + u_int8_t val; +{ + bus_space_write_1(ph->ph_bus_t, ph->ph_bus_h, PCCBB_PCMCIA_OFFSET + reg, + val); +} + +/* + * STATIC int pccbb_ctrl(cardbus_chipset_tag_t, int) + */ +STATIC int +pccbb_ctrl(ct, command) + cardbus_chipset_tag_t ct; + int command; +{ + struct pccbb_softc *sc = (struct pccbb_softc *)ct; + + switch (command) { + case CARDBUS_CD: + if (2 == pccbb_detect_card(sc)) { + int retval = 0; + int status = cb_detect_voltage(sc); + if (PCCARD_VCC_5V & status) { + retval |= CARDBUS_5V_CARD; + } + if (PCCARD_VCC_3V & status) { + retval |= CARDBUS_3V_CARD; + } + if (PCCARD_VCC_XV & status) { + retval |= CARDBUS_XV_CARD; + } + if (PCCARD_VCC_YV & status) { + retval |= CARDBUS_YV_CARD; + } + return retval; + } else { + return 0; + } + break; + case CARDBUS_RESET: + return cb_reset(sc); + break; + case CARDBUS_IO_ENABLE: /* fallthrough */ + case CARDBUS_IO_DISABLE: /* fallthrough */ + case CARDBUS_MEM_ENABLE: /* fallthrough */ + case CARDBUS_MEM_DISABLE: /* fallthrough */ + case CARDBUS_BM_ENABLE: /* fallthrough */ + case CARDBUS_BM_DISABLE: /* fallthrough */ + return pccbb_cardenable(sc, command); + break; + } + + return 0; +} + +/* + * STATIC int pccbb_power(cardbus_chipset_tag_t, int) + * This function returns true when it succeeds and returns false when + * it fails. + */ +STATIC int +pccbb_power(ct, command) + cardbus_chipset_tag_t ct; + int command; +{ + struct pccbb_softc *sc = (struct pccbb_softc *)ct; + + u_int32_t status, sock_ctrl; + bus_space_tag_t memt = sc->sc_base_memt; + bus_space_handle_t memh = sc->sc_base_memh; + + DPRINTF(("pccbb_power: %s and %s [%x]\n", + (command & CARDBUS_VCCMASK) == CARDBUS_VCC_UC ? "CARDBUS_VCC_UC" : + (command & CARDBUS_VCCMASK) == CARDBUS_VCC_5V ? "CARDBUS_VCC_5V" : + (command & CARDBUS_VCCMASK) == CARDBUS_VCC_3V ? "CARDBUS_VCC_3V" : + (command & CARDBUS_VCCMASK) == CARDBUS_VCC_XV ? "CARDBUS_VCC_XV" : + (command & CARDBUS_VCCMASK) == CARDBUS_VCC_YV ? "CARDBUS_VCC_YV" : + (command & CARDBUS_VCCMASK) == CARDBUS_VCC_0V ? "CARDBUS_VCC_0V" : + "UNKNOWN", + (command & CARDBUS_VPPMASK) == CARDBUS_VPP_UC ? "CARDBUS_VPP_UC" : + (command & CARDBUS_VPPMASK) == CARDBUS_VPP_12V ? "CARDBUS_VPP_12V" : + (command & CARDBUS_VPPMASK) == CARDBUS_VPP_VCC ? "CARDBUS_VPP_VCC" : + (command & CARDBUS_VPPMASK) == CARDBUS_VPP_0V ? "CARDBUS_VPP_0V" : + "UNKNOWN", command)); + + status = bus_space_read_4(memt, memh, CB_SOCKET_STAT); + sock_ctrl = bus_space_read_4(memt, memh, CB_SOCKET_CTRL); + + switch (command & CARDBUS_VCCMASK) { + case CARDBUS_VCC_UC: + break; + case CARDBUS_VCC_5V: + if (CB_SOCKET_STAT_5VCARD & status) { /* check 5 V card */ + sock_ctrl &= ~CB_SOCKET_CTRL_VCCMASK; + sock_ctrl |= CB_SOCKET_CTRL_VCC_5V; + } else { + printf("%s: BAD voltage request: no 5 V card\n", + sc->sc_dev.dv_xname); + } + break; + case CARDBUS_VCC_3V: + if (CB_SOCKET_STAT_3VCARD & status) { + sock_ctrl &= ~CB_SOCKET_CTRL_VCCMASK; + sock_ctrl |= CB_SOCKET_CTRL_VCC_3V; + } else { + printf("%s: BAD voltage request: no 3.3 V card\n", + sc->sc_dev.dv_xname); + } + break; + case CARDBUS_VCC_0V: + sock_ctrl &= ~CB_SOCKET_CTRL_VCCMASK; + break; + default: + return 0; /* power NEVER changed */ + break; + } + + switch (command & CARDBUS_VPPMASK) { + case CARDBUS_VPP_UC: + break; + case CARDBUS_VPP_0V: + sock_ctrl &= ~CB_SOCKET_CTRL_VPPMASK; + break; + case CARDBUS_VPP_VCC: + sock_ctrl &= ~CB_SOCKET_CTRL_VPPMASK; + sock_ctrl |= ((sock_ctrl >> 4) & 0x07); + break; + case CARDBUS_VPP_12V: + sock_ctrl &= ~CB_SOCKET_CTRL_VPPMASK; + sock_ctrl |= CB_SOCKET_CTRL_VPP_12V; + break; + } + +#if 0 + DPRINTF(("sock_ctrl: %x\n", sock_ctrl)); +#endif + bus_space_write_4(memt, memh, CB_SOCKET_CTRL, sock_ctrl); + status = bus_space_read_4(memt, memh, CB_SOCKET_STAT); + + delay(20 * 1000); /* wait 20 ms: Vcc setup time */ + /* + * XXX delay 200 ms: though the standard defines that the Vcc set-up + * time is 20 ms, some PC-Card bridge requires longer duration. + */ + delay(200 * 1000); + + if (status & CB_SOCKET_STAT_BADVCC) { /* bad Vcc request */ + printf + ("%s: bad Vcc request. sock_ctrl 0x%x, sock_status 0x%x\n", + sc->sc_dev.dv_xname, sock_ctrl, status); + DPRINTF(("pccbb_power: %s and %s [%x]\n", + (command & CARDBUS_VCCMASK) == + CARDBUS_VCC_UC ? "CARDBUS_VCC_UC" : (command & + CARDBUS_VCCMASK) == + CARDBUS_VCC_5V ? "CARDBUS_VCC_5V" : (command & + CARDBUS_VCCMASK) == + CARDBUS_VCC_3V ? "CARDBUS_VCC_3V" : (command & + CARDBUS_VCCMASK) == + CARDBUS_VCC_XV ? "CARDBUS_VCC_XV" : (command & + CARDBUS_VCCMASK) == + CARDBUS_VCC_YV ? "CARDBUS_VCC_YV" : (command & + CARDBUS_VCCMASK) == + CARDBUS_VCC_0V ? "CARDBUS_VCC_0V" : "UNKNOWN", + (command & CARDBUS_VPPMASK) == + CARDBUS_VPP_UC ? "CARDBUS_VPP_UC" : (command & + CARDBUS_VPPMASK) == + CARDBUS_VPP_12V ? "CARDBUS_VPP_12V" : (command & + CARDBUS_VPPMASK) == + CARDBUS_VPP_VCC ? "CARDBUS_VPP_VCC" : (command & + CARDBUS_VPPMASK) == + CARDBUS_VPP_0V ? "CARDBUS_VPP_0V" : "UNKNOWN", command)); +#if 0 + if (command == (CARDBUS_VCC_0V | CARDBUS_VPP_0V)) { + u_int32_t force = + bus_space_read_4(memt, memh, CB_SOCKET_FORCE); + /* Reset Bad Vcc request */ + force &= ~CB_SOCKET_FORCE_BADVCC; + bus_space_write_4(memt, memh, CB_SOCKET_FORCE, force); + printf("new status 0x%x\n", bus_space_read_4(memt, memh, + CB_SOCKET_STAT)); + return 1; + } +#endif + return 0; + } + return 1; /* power changed correctly */ +} + +#if defined CB_PCMCIA_POLL +struct cb_poll_str { + void *arg; + int (*func) __P((void *)); + int level; + pccard_chipset_tag_t ct; + int count; +}; + +static struct cb_poll_str cb_poll[10]; +static int cb_poll_n = 0; + +static void cb_pcmcia_poll __P((void *arg)); + +static void +cb_pcmcia_poll(arg) + void *arg; +{ + struct cb_poll_str *poll = arg; + struct cbb_pcmcia_softc *psc = (void *)poll->ct->v; + struct pccbb_softc *sc = psc->cpc_parent; + int s; + u_int32_t spsr; /* socket present-state reg */ + + timeout(cb_pcmcia_poll, arg, hz / 10); + switch (poll->level) { + case IPL_NET: + s = splnet(); + break; + case IPL_BIO: + s = splbio(); + break; + case IPL_TTY: /* fallthrough */ + default: + s = spltty(); + break; + } + + spsr = + bus_space_read_4(sc->sc_base_memt, sc->sc_base_memh, + CB_SOCKET_STAT); + +#if defined CB_PCMCIA_POLL_ONLY && defined LEVEL2 + if (!(spsr & 0x40)) { /* CINT low */ +#else + if (1) { +#endif + if ((*poll->func) (poll->arg) == 1) { + ++poll->count; + printf("intr: reported from poller, 0x%x\n", spsr); +#if defined LEVEL2 + } else { + printf("intr: miss! 0x%x\n", spsr); +#endif + } + } + splx(s); +} +#endif /* defined CB_PCMCIA_POLL */ + +/* + * static int pccbb_detect_card(struct pccbb_softc *sc) + * return value: 0 if no card exists. + * 1 if 16-bit card exists. + * 2 if cardbus card exists. + */ +static int +pccbb_detect_card(sc) + struct pccbb_softc *sc; +{ + bus_space_handle_t base_memh = sc->sc_base_memh; + bus_space_tag_t base_memt = sc->sc_base_memt; + u_int32_t sockstat = + bus_space_read_4(base_memt, base_memh, CB_SOCKET_STAT); + int retval = 0; + + /* CD1 and CD2 asserted */ + if (0x00 == (sockstat & CB_SOCKET_STAT_CD)) { + /* card must be present */ + if (!(CB_SOCKET_STAT_NOTCARD & sockstat)) { + /* NOTACARD DEASSERTED */ + if (CB_SOCKET_STAT_CB & sockstat) { + /* CardBus mode */ + retval = 2; + } else if (CB_SOCKET_STAT_16BIT & sockstat) { + /* 16-bit mode */ + retval = 1; + } + } + } + return retval; +} + +/* + * STATIC int cb_reset(struct pccbb_softc *sc) + * This function resets CardBus card. + */ +STATIC int +cb_reset(sc) + struct pccbb_softc *sc; +{ + /* + * Reset Assert at least 20 ms + * Some machines request longer duration. + */ + int reset_duration = + (sc->sc_chipset == CB_RX5C47X ? 400 * 1000 : 40 * 1000); + u_int32_t bcr = pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_BCR_INTR); + + bcr |= (0x40 << 16); /* Reset bit Assert (bit 6 at 0x3E) */ + pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_BCR_INTR, bcr); + delay(reset_duration); + + if (CBB_CARDEXIST & sc->sc_flags) { /* A card exists. Reset it! */ + bcr &= ~(0x40 << 16); /* Reset bit Deassert (bit 6 at 0x3E) */ + pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_BCR_INTR, bcr); + delay(reset_duration); + } + /* No card found on the slot. Keep Reset. */ + return 1; +} + +/* + * STATIC int cb_detect_voltage(struct pccbb_softc *sc) + * This function detect card Voltage. + */ +STATIC int +cb_detect_voltage(sc) + struct pccbb_softc *sc; +{ + u_int32_t psr; /* socket present-state reg */ + bus_space_tag_t iot = sc->sc_base_memt; + bus_space_handle_t ioh = sc->sc_base_memh; + int vol = PCCARD_VCC_UKN; /* set 0 */ + + psr = bus_space_read_4(iot, ioh, CB_SOCKET_STAT); + + if (0x400u & psr) { + vol |= PCCARD_VCC_5V; + } + if (0x800u & psr) { + vol |= PCCARD_VCC_3V; + } + + return vol; +} + +STATIC int +cbbprint(aux, pcic) + void *aux; + const char *pcic; +{ +/* + struct cbslot_attach_args *cba = aux; + + if (cba->cba_slot >= 0) { + printf(" slot %d", cba->cba_slot); + } +*/ + return UNCONF; +} + +/* + * STATIC int pccbb_cardenable(struct pccbb_softc *sc, int function) + * This function enables and disables the card + */ +STATIC int +pccbb_cardenable(sc, function) + struct pccbb_softc *sc; + int function; +{ + u_int32_t command = + pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG); + + DPRINTF(("pccbb_cardenable:")); + switch (function) { + case CARDBUS_IO_ENABLE: + command |= PCI_COMMAND_IO_ENABLE; + break; + case CARDBUS_IO_DISABLE: + command &= ~PCI_COMMAND_IO_ENABLE; + break; + case CARDBUS_MEM_ENABLE: + command |= PCI_COMMAND_MEM_ENABLE; + break; + case CARDBUS_MEM_DISABLE: + command &= ~PCI_COMMAND_MEM_ENABLE; + break; + case CARDBUS_BM_ENABLE: + command |= PCI_COMMAND_MASTER_ENABLE; + break; + case CARDBUS_BM_DISABLE: + command &= ~PCI_COMMAND_MASTER_ENABLE; + break; + default: + return 0; + } + + pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG, command); + DPRINTF((" command reg 0x%x\n", command)); + return 1; +} + +#if !rbus +/* + * int pccbb_io_open(cardbus_chipset_tag_t, int, u_int32_t, u_int32_t) + */ +static int +pccbb_io_open(ct, win, start, end) + cardbus_chipset_tag_t ct; + int win; + u_int32_t start, end; +{ + struct pccbb_softc *sc = (struct pccbb_softc *)ct; + int basereg; + int limitreg; + + if ((win < 0) || (win > 2)) { +#if defined DIAGNOSTIC + printf("cardbus_io_open: window out of range %d\n", win); +#endif + return 0; + } + + basereg = win * 8 + 0x2c; + limitreg = win * 8 + 0x30; + + DPRINTF(("pccbb_io_open: 0x%x[0x%x] - 0x%x[0x%x]\n", + start, basereg, end, limitreg)); + + pci_conf_write(sc->sc_pc, sc->sc_tag, basereg, start); + pci_conf_write(sc->sc_pc, sc->sc_tag, limitreg, end); + return 1; +} + +/* + * int pccbb_io_close(cardbus_chipset_tag_t, int) + */ +static int +pccbb_io_close(ct, win) + cardbus_chipset_tag_t ct; + int win; +{ + struct pccbb_softc *sc = (struct pccbb_softc *)ct; + int basereg; + int limitreg; + + if ((win < 0) || (win > 2)) { +#if defined DIAGNOSTIC + printf("cardbus_io_close: window out of range %d\n", win); +#endif + return 0; + } + + basereg = win * 8 + 0x2c; + limitreg = win * 8 + 0x30; + + pci_conf_write(sc->sc_pc, sc->sc_tag, basereg, 0); + pci_conf_write(sc->sc_pc, sc->sc_tag, limitreg, 0); + return 1; +} + +/* + * int pccbb_mem_open(cardbus_chipset_tag_t, int, u_int32_t, u_int32_t) + */ +static int +pccbb_mem_open(ct, win, start, end) + cardbus_chipset_tag_t ct; + int win; + u_int32_t start, end; +{ + struct pccbb_softc *sc = (struct pccbb_softc *)ct; + int basereg; + int limitreg; + + if ((win < 0) || (win > 2)) { +#if defined DIAGNOSTIC + printf("cardbus_mem_open: window out of range %d\n", win); +#endif + return 0; + } + + basereg = win * 8 + 0x1c; + limitreg = win * 8 + 0x20; + + pci_conf_write(sc->sc_pc, sc->sc_tag, basereg, start); + pci_conf_write(sc->sc_pc, sc->sc_tag, limitreg, end); + return 1; +} + +/* + * int pccbb_mem_close(cardbus_chipset_tag_t, int) + */ +static int +pccbb_mem_close(ct, win) + cardbus_chipset_tag_t ct; + int win; +{ + struct pccbb_softc *sc = (struct pccbb_softc *)ct; + int basereg; + int limitreg; + + if ((win < 0) || (win > 2)) { +#if defined DIAGNOSTIC + printf("cardbus_mem_close: window out of range %d\n", win); +#endif + return 0; + } + + basereg = win * 8 + 0x1c; + limitreg = win * 8 + 0x20; + + pci_conf_write(sc->sc_pc, sc->sc_tag, basereg, 0); + pci_conf_write(sc->sc_pc, sc->sc_tag, limitreg, 0); + return 1; +} +#endif + +/* + * static void *pccbb_cb_intr_establish(cardbus_chipset_tag_t ct, + * int irq, + * int level, + * int (* func) __P((void *)), + * void *arg) + * + * This function registers an interrupt handler at the bridge, in + * order not to call the interrupt handlers of child devices when + * a card-deletion interrupt occurs. + * + * The arguments irq and level are not used. + */ +static void * +pccbb_cb_intr_establish(ct, irq, level, func, arg) + cardbus_chipset_tag_t ct; + int irq, level; + int (*func) __P((void *)); + void *arg; +{ + struct pccbb_softc *sc = (struct pccbb_softc *)ct; + + return pccbb_intr_establish(sc, irq, level, func, arg); +} + + +/* + * static void *pccbb_cb_intr_disestablish(cardbus_chipset_tag_t ct, + * void *ih) + * + * This function removes an interrupt handler pointed by ih. + */ +static void +pccbb_cb_intr_disestablish(ct, ih) + cardbus_chipset_tag_t ct; + void *ih; +{ + struct pccbb_softc *sc = (struct pccbb_softc *)ct; + + pccbb_intr_disestablish(sc, ih); +} + + +/* + * static void *pccbb_intr_establish(struct pccbb_softc *sc, + * int irq, + * int level, + * int (* func) __P((void *)), + * void *arg) + * + * This function registers an interrupt handler at the bridge, in + * order not to call the interrupt handlers of child devices when + * a card-deletion interrupt occurs. + * + * The arguments irq and level are not used. + */ +static void * +pccbb_intr_establish(sc, irq, level, func, arg) + struct pccbb_softc *sc; + int irq, level; + int (*func) __P((void *)); + void *arg; +{ + struct pccbb_intrhand_list *pil, *newpil; + + DPRINTF(("pccbb_intr_establish start. %p\n", sc->sc_pil)); + + if (sc->sc_pil == NULL) { + /* initialize bridge intr routing */ + + switch (sc->sc_chipset) { + case CB_TI113X: + { + pcireg_t cbctrl = + pci_conf_read(sc->sc_pc, sc->sc_tag, + PCI_CBCTRL); + /* functional intr enabled */ + cbctrl |= PCI113X_CBCTRL_PCI_INTR; + pci_conf_write(sc->sc_pc, sc->sc_tag, + PCI_CBCTRL, cbctrl); + break; + } + default: + break; + } + } + + /* + * Allocate a room for interrupt handler structure. + */ + if (NULL == (newpil = + (struct pccbb_intrhand_list *)malloc(sizeof(struct + pccbb_intrhand_list), M_DEVBUF, M_WAITOK))) { + return NULL; + } + + newpil->pil_func = func; + newpil->pil_arg = arg; + newpil->pil_next = NULL; + + if (sc->sc_pil == NULL) { + sc->sc_pil = newpil; + } else { + for (pil = sc->sc_pil; pil->pil_next != NULL; + pil = pil->pil_next); + pil->pil_next = newpil; + } + + DPRINTF(("pccbb_intr_establish add pil. %p\n", sc->sc_pil)); + + return newpil; +} + +/* + * static void *pccbb_intr_disestablish(struct pccbb_softc *sc, + * void *ih) + * + * This function removes an interrupt handler pointed by ih. + */ +static void +pccbb_intr_disestablish(sc, ih) + struct pccbb_softc *sc; + void *ih; +{ + struct pccbb_intrhand_list *pil, **pil_prev; + + DPRINTF(("pccbb_intr_disestablish start. %p\n", sc->sc_pil)); + + pil_prev = &sc->sc_pil; + + for (pil = sc->sc_pil; pil != NULL; pil = pil->pil_next) { + if (pil == ih) { + *pil_prev = pil->pil_next; + free(pil, M_DEVBUF); + DPRINTF(("pccbb_intr_disestablish frees one pil\n")); + break; + } + pil_prev = &pil->pil_next; + } + + if (sc->sc_pil == NULL) { + /* No interrupt handlers */ + + DPRINTF(("pccbb_intr_disestablish: no interrupt handler\n")); + + switch (sc->sc_chipset) { + case CB_TI113X: + { + pcireg_t cbctrl = + pci_conf_read(sc->sc_pc, sc->sc_tag, + PCI_CBCTRL); + /* functional intr disabled */ + cbctrl &= ~PCI113X_CBCTRL_PCI_INTR; + pci_conf_write(sc->sc_pc, sc->sc_tag, + PCI_CBCTRL, cbctrl); + break; + } + default: + break; + } + } +} + +#if defined SHOW_REGS +static void +cb_show_regs(pc, tag, memt, memh) + pci_chipset_tag_t pc; + pcitag_t tag; + bus_space_tag_t memt; + bus_space_handle_t memh; +{ + int i; + printf("PCI config regs:"); + for (i = 0; i < 0x50; i += 4) { + if (i % 16 == 0) { + printf("\n 0x%02x:", i); + } + printf(" %08x", pci_conf_read(pc, tag, i)); + } + for (i = 0x80; i < 0xb0; i += 4) { + if (i % 16 == 0) { + printf("\n 0x%02x:", i); + } + printf(" %08x", pci_conf_read(pc, tag, i)); + } + + if (memh == 0) { + printf("\n"); + return; + } + + printf("\nsocket regs:"); + for (i = 0; i <= 0x10; i += 0x04) { + printf(" %08x", bus_space_read_4(memt, memh, i)); + } + printf("\nExCA regs:"); + for (i = 0; i < 0x08; ++i) { + printf(" %02x", bus_space_read_1(memt, memh, 0x800 + i)); + } + printf("\n"); + return; +} +#endif + +/* + * static cardbustag_t pccbb_make_tag(cardbus_chipset_tag_t cc, + * int busno, int devno, int function) + * This is the function to make a tag to access config space of + * a CardBus Card. It works same as pci_conf_read. + */ +static cardbustag_t +pccbb_make_tag(cc, busno, devno, function) + cardbus_chipset_tag_t cc; + int busno, devno, function; +{ + struct pccbb_softc *sc = (struct pccbb_softc *)cc; + + return pci_make_tag(sc->sc_pc, busno, devno, function); +} + +static void +pccbb_free_tag(cc, tag) + cardbus_chipset_tag_t cc; + cardbustag_t tag; +{ +} + +/* + * static cardbusreg_t pccbb_conf_read(cardbus_chipset_tag_t cc, + * cardbustag_t tag, int offset) + * This is the function to read the config space of a CardBus Card. + * It works same as pci_conf_read. + */ +static cardbusreg_t +pccbb_conf_read(cc, tag, offset) + cardbus_chipset_tag_t cc; + cardbustag_t tag; + int offset; /* register offset */ +{ + struct pccbb_softc *sc = (struct pccbb_softc *)cc; + + return pci_conf_read(sc->sc_pc, tag, offset); +} + +/* + * static void pccbb_conf_write(cardbus_chipset_tag_t cc, cardbustag_t tag, + * int offs, cardbusreg_t val) + * This is the function to write the config space of a CardBus Card. + * It works same as pci_conf_write. + */ +static void +pccbb_conf_write(cc, tag, reg, val) + cardbus_chipset_tag_t cc; + cardbustag_t tag; + int reg; /* register offset */ + cardbusreg_t val; +{ + struct pccbb_softc *sc = (struct pccbb_softc *)cc; + + pci_conf_write(sc->sc_pc, tag, reg, val); +} + +#if 0 +STATIC int +pccbb_new_pcmcia_io_alloc(pcmcia_chipset_handle_t pch, + bus_addr_t start, bus_size_t size, bus_size_t align, bus_addr_t mask, + int speed, int flags, + bus_space_handle_t * iohp) +#endif +/* + * STATIC int pccbb_pcmcia_io_alloc(pcmcia_chipset_handle_t pch, + * bus_addr_t start, bus_size_t size, + * bus_size_t align, + * struct pcmcia_io_handle *pcihp + * + * This function only allocates I/O region for pccard. This function + * never maps the allocated region to pccard I/O area. + * + * XXX: The interface of this function is not very good, I believe. + */ +STATIC int +pccbb_pcmcia_io_alloc(pch, start, size, align, pcihp) + pcmcia_chipset_handle_t pch; + bus_addr_t start; /* start address */ + bus_size_t size; + bus_size_t align; + struct pcmcia_io_handle *pcihp; +{ + struct pcic_handle *ph = (struct pcic_handle *)pch; + bus_addr_t ioaddr; + int flags = 0; + bus_space_tag_t iot; + bus_space_handle_t ioh; +#if rbus + rbus_tag_t rb; +#endif + if (align == 0) { + align = size; /* XXX: funny??? */ + } + + /* + * Allocate some arbitrary I/O space. + */ + + iot = ((struct pccbb_softc *)(ph->ph_parent))->sc_iot; + +#if rbus + rb = ((struct pccbb_softc *)(ph->ph_parent))->sc_rbus_iot; + /* XXX: I assume all card decode lower 10 bits by its hardware */ + if (rbus_space_alloc(rb, start, size, 0x3ff, align, 0, &ioaddr, &ioh)) { + return 1; + } +#else + if (start) { + ioaddr = start; + if (bus_space_map(iot, start, size, 0, &ioh)) { + return 1; + } + DPRINTF(("pccbb_pcmcia_io_alloc map port %lx+%lx\n", + (u_long) ioaddr, (u_long) size)); + } else { + flags |= PCMCIA_IO_ALLOCATED; + if (bus_space_alloc(iot, 0x700 /* ph->sc->sc_iobase */ , + 0x800, /* ph->sc->sc_iobase + ph->sc->sc_iosize */ + size, align, 0, 0, &ioaddr, &ioh)) { + /* No room be able to be get. */ + return 1; + } + DPRINTF(("pccbb_pcmmcia_io_alloc alloc port 0x%lx+0x%lx\n", + (u_long) ioaddr, (u_long) size)); + } +#endif + + pcihp->iot = iot; + pcihp->ioh = ioh; + pcihp->addr = ioaddr; + pcihp->size = size; + pcihp->flags = flags; + + return 0; +} + +/* + * STATIC int pccbb_pcmcia_io_free(pcmcia_chipset_handle_t pch, + * struct pcmcia_io_handle *pcihp) + * + * This function only frees I/O region for pccard. + * + * XXX: The interface of this function is not very good, I believe. + */ +void +pccbb_pcmcia_io_free(pch, pcihp) + pcmcia_chipset_handle_t pch; + struct pcmcia_io_handle *pcihp; +{ +#if !rbus + bus_space_tag_t iot = pcihp->iot; +#endif + bus_space_handle_t ioh = pcihp->ioh; + bus_size_t size = pcihp->size; + +#if rbus + struct pccbb_softc *sc = + (struct pccbb_softc *)((struct pcic_handle *)pch)->ph_parent; + rbus_tag_t rb = sc->sc_rbus_iot; + + rbus_space_free(rb, ioh, size, NULL); +#else + if (pcihp->flags & PCMCIA_IO_ALLOCATED) + bus_space_free(iot, ioh, size); + else + bus_space_unmap(iot, ioh, size); +#endif +} + +/* + * STATIC int pccbb_pcmcia_io_map(pcmcia_chipset_handle_t pch, int width, + * bus_addr_t offset, bus_size_t size, + * struct pcmcia_io_handle *pcihp, + * int *windowp) + * + * This function maps the allocated I/O region to pccard. This function + * never allocates any I/O region for pccard I/O area. I don't + * understand why the original authors of pcmciabus separated alloc and + * map. I believe the two must be unite. + * + * XXX: no wait timing control? + */ +int +pccbb_pcmcia_io_map(pch, width, offset, size, pcihp, windowp) + pcmcia_chipset_handle_t pch; + int width; + bus_addr_t offset; + bus_size_t size; + struct pcmcia_io_handle *pcihp; + int *windowp; +{ + struct pcic_handle *ph = (struct pcic_handle *)pch; + bus_addr_t ioaddr = pcihp->addr + offset; + int i, win; +#if defined CBB_DEBUG + static char *width_names[] = { "dynamic", "io8", "io16" }; +#endif + + /* Sanity check I/O handle. */ + + if (((struct pccbb_softc *)ph->ph_parent)->sc_iot != pcihp->iot) { + panic("pccbb_pcmcia_io_map iot is bogus"); + } + + /* XXX Sanity check offset/size. */ + + win = -1; + for (i = 0; i < PCIC_IO_WINS; i++) { + if ((ph->ioalloc & (1 << i)) == 0) { + win = i; + ph->ioalloc |= (1 << i); + break; + } + } + + if (win == -1) { + return 1; + } + + *windowp = win; + + /* XXX this is pretty gross */ + + DPRINTF(("pccbb_pcmcia_io_map window %d %s port %lx+%lx\n", + win, width_names[width], (u_long) ioaddr, (u_long) size)); + + /* XXX wtf is this doing here? */ + +#if 0 + printf(" port 0x%lx", (u_long) ioaddr); + if (size > 1) { + printf("-0x%lx", (u_long) ioaddr + (u_long) size - 1); + } +#endif + + ph->io[win].addr = ioaddr; + ph->io[win].size = size; + ph->io[win].width = width; + + /* actual dirty register-value changing in the function below. */ + pccbb_pcmcia_do_io_map(ph, win); + + return 0; +} + +/* + * STATIC void pccbb_pcmcia_do_io_map(struct pcic_handle *h, int win) + * + * This function changes register-value to map I/O region for pccard. + */ +static void +pccbb_pcmcia_do_io_map(ph, win) + struct pcic_handle *ph; + int win; +{ + static u_int8_t pcic_iowidth[3] = { + PCIC_IOCTL_IO0_IOCS16SRC_CARD, + PCIC_IOCTL_IO0_IOCS16SRC_DATASIZE | + PCIC_IOCTL_IO0_DATASIZE_8BIT, + PCIC_IOCTL_IO0_IOCS16SRC_DATASIZE | + PCIC_IOCTL_IO0_DATASIZE_16BIT, + }; + +#define PCIC_SIA_START_LOW 0 +#define PCIC_SIA_START_HIGH 1 +#define PCIC_SIA_STOP_LOW 2 +#define PCIC_SIA_STOP_HIGH 3 + + int regbase_win = 0x8 + win * 0x04; + u_int8_t ioctl, enable; + + DPRINTF( + ("pccbb_pcmcia_do_io_map win %d addr 0x%lx size 0x%lx width %d\n", + win, (long)ph->io[win].addr, (long)ph->io[win].size, + ph->io[win].width * 8)); + + Pcic_write(ph, regbase_win + PCIC_SIA_START_LOW, + ph->io[win].addr & 0xff); + Pcic_write(ph, regbase_win + PCIC_SIA_START_HIGH, + (ph->io[win].addr >> 8) & 0xff); + + Pcic_write(ph, regbase_win + PCIC_SIA_STOP_LOW, + (ph->io[win].addr + ph->io[win].size - 1) & 0xff); + Pcic_write(ph, regbase_win + PCIC_SIA_STOP_HIGH, + ((ph->io[win].addr + ph->io[win].size - 1) >> 8) & 0xff); + + ioctl = Pcic_read(ph, PCIC_IOCTL); + enable = Pcic_read(ph, PCIC_ADDRWIN_ENABLE); + switch (win) { + case 0: + ioctl &= ~(PCIC_IOCTL_IO0_WAITSTATE | PCIC_IOCTL_IO0_ZEROWAIT | + PCIC_IOCTL_IO0_IOCS16SRC_MASK | + PCIC_IOCTL_IO0_DATASIZE_MASK); + ioctl |= pcic_iowidth[ph->io[win].width]; + enable |= PCIC_ADDRWIN_ENABLE_IO0; + break; + case 1: + ioctl &= ~(PCIC_IOCTL_IO1_WAITSTATE | PCIC_IOCTL_IO1_ZEROWAIT | + PCIC_IOCTL_IO1_IOCS16SRC_MASK | + PCIC_IOCTL_IO1_DATASIZE_MASK); + ioctl |= (pcic_iowidth[ph->io[win].width] << 4); + enable |= PCIC_ADDRWIN_ENABLE_IO1; + break; + } + Pcic_write(ph, PCIC_IOCTL, ioctl); + Pcic_write(ph, PCIC_ADDRWIN_ENABLE, enable); +#if defined CBB_DEBUG + { + u_int8_t start_low = + Pcic_read(ph, regbase_win + PCIC_SIA_START_LOW); + u_int8_t start_high = + Pcic_read(ph, regbase_win + PCIC_SIA_START_HIGH); + u_int8_t stop_low = + Pcic_read(ph, regbase_win + PCIC_SIA_STOP_LOW); + u_int8_t stop_high = + Pcic_read(ph, regbase_win + PCIC_SIA_STOP_HIGH); + printf + (" start %02x %02x, stop %02x %02x, ioctl %02x enable %02x\n", + start_low, start_high, stop_low, stop_high, ioctl, enable); + } +#endif +} + +/* + * STATIC void pccbb_pcmcia_io_unmap(pcmcia_chipset_handle_t *h, int win) + * + * This function unmaps I/O region. No return value. + */ +STATIC void +pccbb_pcmcia_io_unmap(pch, win) + pcmcia_chipset_handle_t pch; + int win; +{ + struct pcic_handle *ph = (struct pcic_handle *)pch; + int reg; + + if (win >= PCIC_IO_WINS || win < 0) { + panic("pccbb_pcmcia_io_unmap: window out of range"); + } + + reg = Pcic_read(ph, PCIC_ADDRWIN_ENABLE); + switch (win) { + case 0: + reg &= ~PCIC_ADDRWIN_ENABLE_IO0; + break; + case 1: + reg &= ~PCIC_ADDRWIN_ENABLE_IO1; + break; + } + Pcic_write(ph, PCIC_ADDRWIN_ENABLE, reg); + + ph->ioalloc &= ~(1 << win); +} + +/* + * static void pccbb_pcmcia_wait_ready(struct pcic_handle *ph) + * + * This function enables the card. All information is stored in + * the first argument, pcmcia_chipset_handle_t. + */ +static void +pccbb_pcmcia_wait_ready(ph) + struct pcic_handle *ph; +{ + int i; + + DPRINTF(("pccbb_pcmcia_wait_ready: status 0x%02x\n", + Pcic_read(ph, PCIC_IF_STATUS))); + + for (i = 0; i < 10000; i++) { + if (Pcic_read(ph, PCIC_IF_STATUS) & PCIC_IF_STATUS_READY) { + return; + } + delay(500); +#ifdef CBB_DEBUG + if ((i > 5000) && (i % 100 == 99)) + printf("."); +#endif + } + +#ifdef DIAGNOSTIC + printf("pcic_wait_ready: ready never happened, status = %02x\n", + Pcic_read(ph, PCIC_IF_STATUS)); +#endif +} + +/* + * STATIC void pccbb_pcmcia_socket_enable(pcmcia_chipset_handle_t pch) + * + * This function enables the card. All information is stored in + * the first argument, pcmcia_chipset_handle_t. + */ +STATIC void +pccbb_pcmcia_socket_enable(pch) + pcmcia_chipset_handle_t pch; +{ + struct pcic_handle *ph = (struct pcic_handle *)pch; + struct pccbb_softc *sc = (struct pccbb_softc *)ph->ph_parent; + int cardtype, win; + u_int8_t power, intr; + pcireg_t spsr; + int voltage; + + /* this bit is mostly stolen from pcic_attach_card */ + + DPRINTF(("pccbb_pcmcia_socket_enable: ")); + + /* get card Vcc info */ + + spsr = + bus_space_read_4(sc->sc_base_memt, sc->sc_base_memh, + CB_SOCKET_STAT); + if (spsr & CB_SOCKET_STAT_5VCARD) { + DPRINTF(("5V card\n")); + voltage = CARDBUS_VCC_5V | CARDBUS_VPP_VCC; + } else if (spsr & CB_SOCKET_STAT_3VCARD) { + DPRINTF(("3V card\n")); + voltage = CARDBUS_VCC_3V | CARDBUS_VPP_VCC; + } else { + printf("?V card, 0x%x\n", spsr); /* XXX */ + return; + } + + /* assert reset bit */ + intr = Pcic_read(ph, PCIC_INTR); + intr &= ~(PCIC_INTR_RESET | PCIC_INTR_CARDTYPE_MASK); + Pcic_write(ph, PCIC_INTR, intr); + + /* disable socket i/o: negate output enable bit */ + + power = Pcic_read(ph, PCIC_PWRCTL); + power &= ~PCIC_PWRCTL_OE; + Pcic_write(ph, PCIC_PWRCTL, power); + + /* power down the socket to reset it, clear the card reset pin */ + + pccbb_power(sc, CARDBUS_VCC_0V | CARDBUS_VPP_0V); + + /* + * wait 200ms until power fails (Tpf). Then, wait 100ms since + * we are changing Vcc (Toff). + */ + /* delay(300*1000); too much */ + + /* power up the socket */ + pccbb_power(sc, voltage); + + /* + * wait 100ms until power raise (Tpr) and 20ms to become + * stable (Tsu(Vcc)). + * + * some machines require some more time to be settled + * (another 200ms is added here). + */ + /* delay((100 + 20 + 200)*1000); too much */ + + power = Pcic_read(ph, PCIC_PWRCTL); + power |= PCIC_PWRCTL_OE; + Pcic_write(ph, PCIC_PWRCTL, power); + + /* + * hold RESET at least 10us. + */ + delay(10); + delay(2 * 1000); /* XXX: TI1130 requires it. */ + delay(20 * 1000); /* XXX: TI1130 requires it. */ + + /* clear the reset flag */ + + intr |= PCIC_INTR_RESET; + Pcic_write(ph, PCIC_INTR, intr); + + /* wait 20ms as per pc card standard (r2.01) section 4.3.6 */ + + delay(20000); + + /* wait for the chip to finish initializing */ + + pccbb_pcmcia_wait_ready(ph); + + /* zero out the address windows */ + + Pcic_write(ph, PCIC_ADDRWIN_ENABLE, 0); + + /* set the card type */ + + cardtype = pcmcia_card_gettype(ph->pcmcia); + + intr |= ((cardtype == PCMCIA_IFTYPE_IO) ? + PCIC_INTR_CARDTYPE_IO : PCIC_INTR_CARDTYPE_MEM); + Pcic_write(ph, PCIC_INTR, intr); + + DPRINTF(("%s: pccbb_pcmcia_socket_enable %02x cardtype %s %02x\n", + ph->ph_parent->dv_xname, ph->sock, + ((cardtype == PCMCIA_IFTYPE_IO) ? "io" : "mem"), intr)); + + /* reinstall all the memory and io mappings */ + + for (win = 0; win < PCIC_MEM_WINS; ++win) { + if (ph->memalloc & (1 << win)) { + pccbb_pcmcia_do_mem_map(ph, win); + } + } + + for (win = 0; win < PCIC_IO_WINS; ++win) { + if (ph->ioalloc & (1 << win)) { + pccbb_pcmcia_do_io_map(ph, win); + } + } +} + +/* + * STATIC void pccbb_pcmcia_socket_disable(pcmcia_chipset_handle_t *ph) + * + * This function disables the card. All information is stored in + * the first argument, pcmcia_chipset_handle_t. + */ +STATIC void +pccbb_pcmcia_socket_disable(pch) + pcmcia_chipset_handle_t pch; +{ + struct pcic_handle *ph = (struct pcic_handle *)pch; + struct pccbb_softc *sc = (struct pccbb_softc *)ph->ph_parent; + u_int8_t power, intr; + + DPRINTF(("pccbb_pcmcia_socket_disable\n")); + + /* reset signal asserting... */ + + intr = Pcic_read(ph, PCIC_INTR); + intr &= ~(PCIC_INTR_CARDTYPE_MASK); + Pcic_write(ph, PCIC_INTR, intr); + delay(2 * 1000); + + /* power down the socket */ + power = Pcic_read(ph, PCIC_PWRCTL); + power &= ~PCIC_PWRCTL_OE; + Pcic_write(ph, PCIC_PWRCTL, power); + pccbb_power(sc, CARDBUS_VCC_0V | CARDBUS_VPP_0V); + /* + * wait 300ms until power fails (Tpf). + */ + delay(300 * 1000); +} + +/* + * STATIC int pccbb_pcmcia_card_detect(pcmcia_chipset_handle_t *ph) + * + * This function detects whether a card is in the slot or not. + * If a card is inserted, return 1. Otherwise, return 0. + */ +STATIC int +pccbb_pcmcia_card_detect(pch) + pcmcia_chipset_handle_t pch; +{ + struct pcic_handle *ph = (struct pcic_handle *)pch; + struct pccbb_softc *sc = (struct pccbb_softc *)ph->ph_parent; + + DPRINTF(("pccbb_pcmcia_card_detect\n")); + return pccbb_detect_card(sc) == 1 ? 1 : 0; +} + +#if 0 +STATIC int +pccbb_new_pcmcia_mem_alloc(pcmcia_chipset_handle_t pch, + bus_addr_t start, bus_size_t size, bus_size_t align, int speed, int flags, + bus_space_tag_t * memtp bus_space_handle_t * memhp) +#endif +/* + * STATIC int pccbb_pcmcia_mem_alloc(pcmcia_chipset_handle_t pch, + * bus_size_t size, + * struct pcmcia_mem_handle *pcmhp) + * + * This function only allocates memory region for pccard. This + * function never maps the allocated region to pccard memory area. + * + * XXX: Why the argument of start address is not in? + */ +STATIC int +pccbb_pcmcia_mem_alloc(pch, size, pcmhp) + pcmcia_chipset_handle_t pch; + bus_size_t size; + struct pcmcia_mem_handle *pcmhp; +{ + struct pcic_handle *ph = (struct pcic_handle *)pch; + bus_space_handle_t memh; + bus_addr_t addr; + bus_size_t sizepg; + struct pccbb_softc *sc = (struct pccbb_softc *)ph->ph_parent; +#if rbus + rbus_tag_t rb; +#endif + + /* out of sc->memh, allocate as many pages as necessary */ + + /* convert size to PCIC pages */ + /* + * This is not enough; when the requested region is on the page + * boundaries, this may calculate wrong result. + */ + sizepg = (size + (PCIC_MEM_PAGESIZE - 1)) / PCIC_MEM_PAGESIZE; +#if 0 + if (sizepg > PCIC_MAX_MEM_PAGES) { + return 1; + } +#endif + + if (!(sc->sc_pcmcia_flags & PCCBB_PCMCIA_MEM_32)) { + return 1; + } + + addr = 0; /* XXX gcc -Wuninitialized */ + +#if rbus + rb = sc->sc_rbus_memt; + if (rbus_space_alloc(rb, 0, sizepg * PCIC_MEM_PAGESIZE, + sizepg * PCIC_MEM_PAGESIZE - 1, PCIC_MEM_PAGESIZE, 0, + &addr, &memh)) { + return 1; + } +#else + if (bus_space_alloc(sc->sc_memt, sc->sc_mem_start, sc->sc_mem_end, + sizepg * PCIC_MEM_PAGESIZE, PCIC_MEM_PAGESIZE, + 0, /* boundary */ + 0, /* flags */ + &addr, &memh)) { + return 1; + } +#endif + + DPRINTF( + ("pccbb_pcmcia_alloc_mem: addr 0x%lx size 0x%lx, realsize 0x%lx\n", + addr, size, sizepg * PCIC_MEM_PAGESIZE)); + + pcmhp->memt = sc->sc_memt; + pcmhp->memh = memh; + pcmhp->addr = addr; + pcmhp->size = size; + pcmhp->realsize = sizepg * PCIC_MEM_PAGESIZE; + /* What is mhandle? I feel it is very dirty and it must go trush. */ + pcmhp->mhandle = 0; + /* No offset??? Funny. */ + + return 0; +} + +/* + * STATIC void pccbb_pcmcia_mem_free(pcmcia_chipset_handle_t pch, + * struct pcmcia_mem_handle *pcmhp) + * + * This function release the memory space allocated by the function + * pccbb_pcmcia_mem_alloc(). + */ +STATIC void +pccbb_pcmcia_mem_free(pch, pcmhp) + pcmcia_chipset_handle_t pch; + struct pcmcia_mem_handle *pcmhp; +{ +#if rbus + struct pcic_handle *ph = (struct pcic_handle *)pch; + struct pccbb_softc *sc = (struct pccbb_softc *)ph->ph_parent; + + rbus_space_free(sc->sc_rbus_memt, pcmhp->memh, pcmhp->realsize, NULL); +#else + bus_space_free(pcmhp->memt, pcmhp->memh, pcmhp->realsize); +#endif +} + +/* + * STATIC void pccbb_pcmcia_do_mem_map(struct pcic_handle *ph, int win) + * + * This function release the memory space allocated by the function + * pccbb_pcmcia_mem_alloc(). + */ +STATIC void +pccbb_pcmcia_do_mem_map(ph, win) + struct pcic_handle *ph; + int win; +{ + int regbase_win; + bus_addr_t phys_addr; + bus_addr_t phys_end; + +#define PCIC_SMM_START_LOW 0 +#define PCIC_SMM_START_HIGH 1 +#define PCIC_SMM_STOP_LOW 2 +#define PCIC_SMM_STOP_HIGH 3 +#define PCIC_CMA_LOW 4 +#define PCIC_CMA_HIGH 5 + + u_int8_t start_low, start_high = 0; + u_int8_t stop_low, stop_high; + u_int8_t off_low, off_high; + u_int8_t mem_window; + int reg; + + int kind = ph->mem[win].kind & ~PCMCIA_WIDTH_MEM_MASK; + int mem8 = + (ph->mem[win].kind & PCMCIA_WIDTH_MEM_MASK) == PCMCIA_WIDTH_MEM8 + || (kind == PCMCIA_MEM_ATTR); + + regbase_win = 0x10 + win * 0x08; + + phys_addr = ph->mem[win].addr; + phys_end = phys_addr + ph->mem[win].size; + + DPRINTF(("pccbb_pcmcia_do_mem_map: start 0x%lx end 0x%lx off 0x%lx\n", + phys_addr, phys_end, ph->mem[win].offset)); + +#define PCIC_MEMREG_LSB_SHIFT PCIC_SYSMEM_ADDRX_SHIFT +#define PCIC_MEMREG_MSB_SHIFT (PCIC_SYSMEM_ADDRX_SHIFT + 8) +#define PCIC_MEMREG_WIN_SHIFT (PCIC_SYSMEM_ADDRX_SHIFT + 12) + + /* bit 19:12 */ + start_low = (phys_addr >> PCIC_MEMREG_LSB_SHIFT) & 0xff; + /* bit 23:20 and bit 7 on */ + start_high = ((phys_addr >> PCIC_MEMREG_MSB_SHIFT) & 0x0f) + |(mem8 ? 0 : PCIC_SYSMEM_ADDRX_START_MSB_DATASIZE_16BIT); + /* bit 31:24, for 32-bit address */ + mem_window = (phys_addr >> PCIC_MEMREG_WIN_SHIFT) & 0xff; + + Pcic_write(ph, regbase_win + PCIC_SMM_START_LOW, start_low); + Pcic_write(ph, regbase_win + PCIC_SMM_START_HIGH, start_high); + + if (((struct pccbb_softc *)ph-> + ph_parent)->sc_pcmcia_flags & PCCBB_PCMCIA_MEM_32) { + Pcic_write(ph, 0x40 + win, mem_window); + } + + stop_low = (phys_end >> PCIC_MEMREG_LSB_SHIFT) & 0xff; + stop_high = ((phys_end >> PCIC_MEMREG_MSB_SHIFT) & 0x0f) + | PCIC_SYSMEM_ADDRX_STOP_MSB_WAIT2; /* wait 2 cycles */ + /* XXX Geee, WAIT2!! Crazy!! I must rewrite this routine. */ + + Pcic_write(ph, regbase_win + PCIC_SMM_STOP_LOW, stop_low); + Pcic_write(ph, regbase_win + PCIC_SMM_STOP_HIGH, stop_high); + + off_low = (ph->mem[win].offset >> PCIC_CARDMEM_ADDRX_SHIFT) & 0xff; + off_high = ((ph->mem[win].offset >> (PCIC_CARDMEM_ADDRX_SHIFT + 8)) + & PCIC_CARDMEM_ADDRX_MSB_ADDR_MASK) + | ((kind == PCMCIA_MEM_ATTR) ? + PCIC_CARDMEM_ADDRX_MSB_REGACTIVE_ATTR : 0); + + Pcic_write(ph, regbase_win + PCIC_CMA_LOW, off_low); + Pcic_write(ph, regbase_win + PCIC_CMA_HIGH, off_high); + + reg = Pcic_read(ph, PCIC_ADDRWIN_ENABLE); + reg |= ((1 << win) | PCIC_ADDRWIN_ENABLE_MEMCS16); + Pcic_write(ph, PCIC_ADDRWIN_ENABLE, reg); + +#if defined CBB_DEBUG + { + int r1, r2, r3, r4, r5, r6, r7 = 0; + + r1 = Pcic_read(ph, regbase_win + PCIC_SMM_START_LOW); + r2 = Pcic_read(ph, regbase_win + PCIC_SMM_START_HIGH); + r3 = Pcic_read(ph, regbase_win + PCIC_SMM_STOP_LOW); + r4 = Pcic_read(ph, regbase_win + PCIC_SMM_STOP_HIGH); + r5 = Pcic_read(ph, regbase_win + PCIC_CMA_LOW); + r6 = Pcic_read(ph, regbase_win + PCIC_CMA_HIGH); + if (((struct pccbb_softc *)(ph-> + ph_parent))->sc_pcmcia_flags & PCCBB_PCMCIA_MEM_32) { + r7 = Pcic_read(ph, 0x40 + win); + } + + DPRINTF(("pccbb_pcmcia_do_mem_map window %d: %02x%02x %02x%02x " + "%02x%02x", win, r1, r2, r3, r4, r5, r6)); + if (((struct pccbb_softc *)(ph-> + ph_parent))->sc_pcmcia_flags & PCCBB_PCMCIA_MEM_32) { + DPRINTF((" %02x", r7)); + } + DPRINTF(("\n")); + } +#endif +} + +/* + * STATIC int pccbb_pcmcia_mem_map(pcmcia_chipset_handle_t pch, int kind, + * bus_addr_t card_addr, bus_size_t size, + * struct pcmcia_mem_handle *pcmhp, + * bus_addr_t *offsetp, int *windowp) + * + * This function maps memory space allocated by the function + * pccbb_pcmcia_mem_alloc(). + */ +STATIC int +pccbb_pcmcia_mem_map(pch, kind, card_addr, size, pcmhp, offsetp, windowp) + pcmcia_chipset_handle_t pch; + int kind; + bus_addr_t card_addr; + bus_size_t size; + struct pcmcia_mem_handle *pcmhp; + bus_addr_t *offsetp; + int *windowp; +{ + struct pcic_handle *ph = (struct pcic_handle *)pch; + bus_addr_t busaddr; + long card_offset; + int win; + + for (win = 0; win < PCIC_MEM_WINS; ++win) { + if ((ph->memalloc & (1 << win)) == 0) { + ph->memalloc |= (1 << win); + break; + } + } + + if (win == PCIC_MEM_WINS) { + return 1; + } + + *windowp = win; + + /* XXX this is pretty gross */ + + if (((struct pccbb_softc *)ph->ph_parent)->sc_memt != pcmhp->memt) { + panic("pccbb_pcmcia_mem_map memt is bogus"); + } + + busaddr = pcmhp->addr; + + /* + * compute the address offset to the pcmcia address space for the + * pcic. this is intentionally signed. The masks and shifts below + * will cause TRT to happen in the pcic registers. Deal with making + * sure the address is aligned, and return the alignment offset. + */ + + *offsetp = card_addr % PCIC_MEM_PAGESIZE; + card_addr -= *offsetp; + + DPRINTF(("pccbb_pcmcia_mem_map window %d bus %lx+%lx+%lx at card addr " + "%lx\n", win, (u_long) busaddr, (u_long) * offsetp, (u_long) size, + (u_long) card_addr)); + + /* + * include the offset in the size, and decrement size by one, since + * the hw wants start/stop + */ + size += *offsetp - 1; + + card_offset = (((long)card_addr) - ((long)busaddr)); + + ph->mem[win].addr = busaddr; + ph->mem[win].size = size; + ph->mem[win].offset = card_offset; + ph->mem[win].kind = kind; + + pccbb_pcmcia_do_mem_map(ph, win); + + return 0; +} + +/* + * STATIC int pccbb_pcmcia_mem_unmap(pcmcia_chipset_handle_t pch, + * int window) + * + * This function unmaps memory space which mapped by the function + * pccbb_pcmcia_mem_map(). + */ +STATIC void +pccbb_pcmcia_mem_unmap(pch, window) + pcmcia_chipset_handle_t pch; + int window; +{ + struct pcic_handle *ph = (struct pcic_handle *)pch; + int reg; + + if (window >= PCIC_MEM_WINS) { + panic("pccbb_pcmcia_mem_unmap: window out of range"); + } + + reg = Pcic_read(ph, PCIC_ADDRWIN_ENABLE); + reg &= ~(1 << window); + Pcic_write(ph, PCIC_ADDRWIN_ENABLE, reg); + + ph->memalloc &= ~(1 << window); +} + +#if defined PCCBB_PCMCIA_POLL +struct pccbb_poll_str { + void *arg; + int (*func) __P((void *)); + int level; + struct pcic_handle *ph; + int count; + int num; +}; + +static struct pccbb_poll_str pccbb_poll[10]; +static int pccbb_poll_n = 0; + +static void pccbb_pcmcia_poll __P((void *arg)); + +static void +pccbb_pcmcia_poll(arg) + void *arg; +{ + struct pccbb_poll_str *poll = arg; + struct pcic_handle *ph = poll->ph; + struct pccbb_softc *sc = ph->sc; + int s; + u_int32_t spsr; /* socket present-state reg */ + + timeout(pccbb_pcmcia_poll, arg, hz * 2); + switch (poll->level) { + case IPL_NET: + s = splnet(); + break; + case IPL_BIO: + s = splbio(); + break; + case IPL_TTY: /* fallthrough */ + default: + s = spltty(); + break; + } + + spsr = + bus_space_read_4(sc->sc_base_memt, sc->sc_base_memh, + CB_SOCKET_STAT); + +#if defined PCCBB_PCMCIA_POLL_ONLY && defined LEVEL2 + if (!(spsr & 0x40)) /* CINT low */ +#else + if (1) +#endif + { + if ((*poll->func) (poll->arg) > 0) { + ++poll->count; +// printf("intr: reported from poller, 0x%x\n", spsr); +#if defined LEVEL2 + } else { + printf("intr: miss! 0x%x\n", spsr); +#endif + } + } + splx(s); +} +#endif /* defined CB_PCMCIA_POLL */ + +/* + * STATIC void *pccbb_pcmcia_intr_establish(pcmcia_chipset_handle_t pch, + * struct pcmcia_function *pf, + * int ipl, + * int (*func)(void *), + * void *arg); + * + * This function enables PC-Card interrupt. PCCBB uses PCI interrupt line. + */ +STATIC void * +pccbb_pcmcia_intr_establish(pch, pf, ipl, func, arg) + pcmcia_chipset_handle_t pch; + struct pcmcia_function *pf; + int ipl; + int (*func) __P((void *)); + void *arg; +{ + struct pcic_handle *ph = (struct pcic_handle *)pch; + struct pccbb_softc *sc = (struct pccbb_softc *)ph->ph_parent; + + if (!(pf->cfe->flags & PCMCIA_CFE_IRQLEVEL)) { + /* what should I do? */ + if ((pf->cfe->flags & PCMCIA_CFE_IRQLEVEL)) { + DPRINTF( + ("%s does not provide edge nor pulse interrupt\n", + sc->sc_dev.dv_xname)); + return NULL; + } + /* + * XXX Noooooo! The interrupt flag must set properly!! + * dumb pcmcia driver!! + */ + } + + return pccbb_intr_establish(sc, IST_LEVEL, ipl, func, arg); +} + +/* + * STATIC void pccbb_pcmcia_intr_disestablish(pcmcia_chipset_handle_t pch, + * void *ih) + * + * This function disables PC-Card interrupt. + */ +STATIC void +pccbb_pcmcia_intr_disestablish(pch, ih) + pcmcia_chipset_handle_t pch; + void *ih; +{ + struct pcic_handle *ph = (struct pcic_handle *)pch; + struct pccbb_softc *sc = (struct pccbb_softc *)ph->ph_parent; + + pccbb_intr_disestablish(sc, ih); +} + +#if rbus +/* + * static int + * pccbb_rbus_cb_space_alloc(cardbus_chipset_tag_t ct, rbus_tag_t rb, + * bus_addr_t addr, bus_size_t size, + * bus_addr_t mask, bus_size_t align, + * int flags, bus_addr_t *addrp; + * bus_space_handle_t *bshp) + * + * This function allocates a portion of memory or io space for + * clients. This function is called from CardBus card drivers. + */ +static int +pccbb_rbus_cb_space_alloc(ct, rb, addr, size, mask, align, flags, addrp, bshp) + cardbus_chipset_tag_t ct; + rbus_tag_t rb; + bus_addr_t addr; + bus_size_t size; + bus_addr_t mask; + bus_size_t align; + int flags; + bus_addr_t *addrp; + bus_space_handle_t *bshp; +{ + struct pccbb_softc *sc = (struct pccbb_softc *)ct; + + DPRINTF( + ("pccbb_rbus_cb_space_alloc: adr %lx, size %lx, mask %lx, align %lx\n", + addr, size, mask, align)); + + if (align == 0) { + align = size; + } + + if (rb->rb_bt == sc->sc_memt) { + if (align < 16) { + return 1; + } + } else if (rb->rb_bt == sc->sc_iot) { + if (align < 4) { + return 1; + } + /* XXX: hack for avoiding ISA image */ + if (mask < 0x0100) { + mask = 0x3ff; + addr = 0x300; + } + + } else { + DPRINTF( + ("pccbb_rbus_cb_space_alloc: Bus space tag %x is NOT used.\n", + rb->rb_bt)); + return 1; + /* XXX: panic here? */ + } + + if (rbus_space_alloc(rb, addr, size, mask, align, flags, addrp, bshp)) { + printf("%s: no bus space\n", sc->sc_dev.dv_xname); + return 1; + } + + pccbb_open_win(sc, rb->rb_bt, *addrp, size, *bshp, 0); + + return 0; +} + +/* + * static int + * pccbb_rbus_cb_space_free(cardbus_chipset_tag_t *ct, rbus_tag_t rb, + * bus_space_handle_t *bshp, bus_size_t size); + * + * This function is called from CardBus card drivers. + */ +static int +pccbb_rbus_cb_space_free(ct, rb, bsh, size) + cardbus_chipset_tag_t ct; + rbus_tag_t rb; + bus_space_handle_t bsh; + bus_size_t size; +{ + struct pccbb_softc *sc = (struct pccbb_softc *)ct; + bus_space_tag_t bt = rb->rb_bt; + + pccbb_close_win(sc, bt, bsh, size); + + if (bt == sc->sc_memt) { + } else if (bt == sc->sc_iot) { + } else { + return 1; + /* XXX: panic here? */ + } + + return rbus_space_free(rb, bsh, size, NULL); +} +#endif /* rbus */ + +#if rbus + +static int +pccbb_open_win(sc, bst, addr, size, bsh, flags) + struct pccbb_softc *sc; + bus_space_tag_t bst; + bus_addr_t addr; + bus_size_t size; + bus_space_handle_t bsh; + int flags; +{ + struct pccbb_win_chain_head *head; + bus_addr_t align; + + head = &sc->sc_iowindow; + align = 0x04; + if (sc->sc_memt == bst) { + head = &sc->sc_memwindow; + align = 0x1000; + DPRINTF(("using memory window, %x %x %x\n\n", + sc->sc_iot, sc->sc_memt, bst)); + } + + if (pccbb_winlist_insert(head, addr, size, bsh, flags)) { + printf("%s: pccbb_open_win: %s winlist insert failed\n", + sc->sc_dev.dv_xname, + (head == &sc->sc_memwindow) ? "mem" : "io"); + } + pccbb_winset(align, sc, bst); + + return 0; +} + +static int +pccbb_close_win(sc, bst, bsh, size) + struct pccbb_softc *sc; + bus_space_tag_t bst; + bus_space_handle_t bsh; + bus_size_t size; +{ + struct pccbb_win_chain_head *head; + bus_addr_t align; + + head = &sc->sc_iowindow; + align = 0x04; + if (sc->sc_memt == bst) { + head = &sc->sc_memwindow; + align = 0x1000; + } + + if (pccbb_winlist_delete(head, bsh, size)) { + printf("%s: pccbb_close_win: %s winlist delete failed\n", + sc->sc_dev.dv_xname, + (head == &sc->sc_memwindow) ? "mem" : "io"); + } + pccbb_winset(align, sc, bst); + + return 0; +} + +static int +pccbb_winlist_insert(head, start, size, bsh, flags) + struct pccbb_win_chain_head *head; + bus_addr_t start; + bus_size_t size; + bus_space_handle_t bsh; + int flags; +{ + struct pccbb_win_chain *chainp, *elem; + + if ((elem = malloc(sizeof(struct pccbb_win_chain), M_DEVBUF, + M_NOWAIT)) == NULL) + return (1); /* fail */ + + elem->wc_start = start; + elem->wc_end = start + (size - 1); + elem->wc_handle = bsh; + elem->wc_flags = flags; + + for (chainp = TAILQ_FIRST(head); chainp != NULL; + chainp = TAILQ_NEXT(chainp, wc_list)) { + if (chainp->wc_end < start) + continue; + TAILQ_INSERT_AFTER(head, chainp, elem, wc_list); + return (0); + } + + TAILQ_INSERT_TAIL(head, elem, wc_list); + return (0); +} + +static int +pccbb_winlist_delete(head, bsh, size) + struct pccbb_win_chain_head *head; + bus_space_handle_t bsh; + bus_size_t size; +{ + struct pccbb_win_chain *chainp; + + for (chainp = TAILQ_FIRST(head); chainp != NULL; + chainp = TAILQ_NEXT(chainp, wc_list)) { + if (chainp->wc_handle != bsh) + continue; + if ((chainp->wc_end - chainp->wc_start) != (size - 1)) { + printf("pccbb_winlist_delete: window 0x%lx size " + "inconsistent: 0x%lx, 0x%lx\n", + chainp->wc_start, + chainp->wc_end - chainp->wc_start, + size - 1); + return 1; + } + + TAILQ_REMOVE(head, chainp, wc_list); + free(chainp, M_DEVBUF); + + return 0; + } + + return 1; /* fail: no candidate to remove */ +} + +static void +pccbb_winset(align, sc, bst) + bus_addr_t align; + struct pccbb_softc *sc; + bus_space_tag_t bst; +{ + pci_chipset_tag_t pc; + pcitag_t tag; + bus_addr_t mask = ~(align - 1); + struct { + cardbusreg_t win_start; + cardbusreg_t win_limit; + int win_flags; + } win[2]; + struct pccbb_win_chain *chainp; + int offs; + + win[0].win_start = 0xffffffff; + win[0].win_limit = 0; + win[1].win_start = 0xffffffff; + win[1].win_limit = 0; + + chainp = TAILQ_FIRST(&sc->sc_iowindow); + offs = 0x2c; + if (sc->sc_memt == bst) { + chainp = TAILQ_FIRST(&sc->sc_memwindow); + offs = 0x1c; + } + + if (chainp != NULL) { + win[0].win_start = chainp->wc_start & mask; + win[0].win_limit = chainp->wc_end & mask; + win[0].win_flags = chainp->wc_flags; + chainp = TAILQ_NEXT(chainp, wc_list); + } + + for (; chainp != NULL; chainp = TAILQ_NEXT(chainp, wc_list)) { + if (win[1].win_start == 0xffffffff) { + /* window 1 is not used */ + if ((win[0].win_flags == chainp->wc_flags) && + (win[0].win_limit + align >= + (chainp->wc_start & mask))) { + /* concatenate */ + win[0].win_limit = chainp->wc_end & mask; + } else { + /* make new window */ + win[1].win_start = chainp->wc_start & mask; + win[1].win_limit = chainp->wc_end & mask; + win[1].win_flags = chainp->wc_flags; + } + continue; + } + + /* Both windows are engaged. */ + if (win[0].win_flags == win[1].win_flags) { + /* same flags */ + if (win[0].win_flags == chainp->wc_flags) { + if (win[1].win_start - (win[0].win_limit + + align) < + (chainp->wc_start & mask) - + ((chainp->wc_end & mask) + align)) { + /* + * merge window 0 and 1, and set win1 + * to chainp + */ + win[0].win_limit = win[1].win_limit; + win[1].win_start = + chainp->wc_start & mask; + win[1].win_limit = + chainp->wc_end & mask; + } else { + win[1].win_limit = + chainp->wc_end & mask; + } + } else { + /* different flags */ + + /* concatenate win0 and win1 */ + win[0].win_limit = win[1].win_limit; + /* allocate win[1] to new space */ + win[1].win_start = chainp->wc_start & mask; + win[1].win_limit = chainp->wc_end & mask; + win[1].win_flags = chainp->wc_flags; + } + } else { + /* the flags of win[0] and win[1] is different */ + if (win[0].win_flags == chainp->wc_flags) { + win[0].win_limit = chainp->wc_end & mask; + /* + * XXX this creates overlapping windows, so + * what should the poor bridge do if one is + * cachable, and the other is not? + */ + printf("%s: overlapping windows\n", + sc->sc_dev.dv_xname); + } else { + win[1].win_limit = chainp->wc_end & mask; + } + } + } + + pc = sc->sc_pc; + tag = sc->sc_tag; + pci_conf_write(pc, tag, offs, win[0].win_start); + pci_conf_write(pc, tag, offs + 4, win[0].win_limit); + pci_conf_write(pc, tag, offs + 8, win[1].win_start); + pci_conf_write(pc, tag, offs + 12, win[1].win_limit); + DPRINTF(("--pccbb_winset: win0 [%x, %lx), win1 [%x, %lx)\n", + pci_conf_read(pc, tag, offs), + pci_conf_read(pc, tag, offs + 4) + align, + pci_conf_read(pc, tag, offs + 8), + pci_conf_read(pc, tag, offs + 12) + align)); + + if (bst == sc->sc_memt) { + if (win[0].win_flags & PCCBB_MEM_CACHABLE) { + pcireg_t bcr = pci_conf_read(pc, tag, PCI_BCR_INTR); + bcr |= CB_BCR_PREFETCH_MEMWIN0; + pci_conf_write(pc, tag, PCI_BCR_INTR, bcr); + } + if (win[1].win_flags & PCCBB_MEM_CACHABLE) { + pcireg_t bcr = pci_conf_read(pc, tag, PCI_BCR_INTR); + bcr |= CB_BCR_PREFETCH_MEMWIN1; + pci_conf_write(pc, tag, PCI_BCR_INTR, bcr); + } + } +} + +#endif /* rbus */ + +static void +pccbb_powerhook(why, arg) + int why; + void *arg; +{ + struct pccbb_softc *sc = arg; + u_int32_t reg; + bus_space_tag_t base_memt = sc->sc_base_memt; /* socket regs memory */ + bus_space_handle_t base_memh = sc->sc_base_memh; + + DPRINTF(("%s: power: why %d\n", sc->sc_dev.dv_xname, why)); + + if (why == PWR_RESUME) { + /* CSC Interrupt: Card detect interrupt on */ + reg = bus_space_read_4(base_memt, base_memh, CB_SOCKET_MASK); + /* Card detect intr is turned on. */ + reg |= CB_SOCKET_MASK_CD; + bus_space_write_4(base_memt, base_memh, CB_SOCKET_MASK, reg); + /* reset interrupt */ + reg = bus_space_read_4(base_memt, base_memh, CB_SOCKET_EVENT); + bus_space_write_4(base_memt, base_memh, CB_SOCKET_EVENT, reg); + + /* + * check for card insertion or removal during suspend period. + * XXX: the code can't cope with card swap (remove then + * insert). how can we detect such situation? + */ + (void)pccbbintr(sc); + } +} diff --git a/sys/dev/pci/pccbbreg.h b/sys/dev/pci/pccbbreg.h new file mode 100644 index 00000000000..d027482a875 --- /dev/null +++ b/sys/dev/pci/pccbbreg.h @@ -0,0 +1,225 @@ +/* $OpenBSD: pccbbreg.h,v 1.1 2000/04/08 05:50:51 aaron Exp $ */ +/* $NetBSD: pccbbreg.h,v 1.4 2000/01/13 08:46:46 joda Exp $ */ +/* + * Copyright (c) 1999 HAYAKAWA Koichi. 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 HAYAKAWA Koichi. + * 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. + */ + + +#ifndef _DEV_PCI_PCCBBREG_H_ +#define _DEV_PCI_PCCBBREG_H_ + + + + +#define PCI_SOCKBASE 0x10 /* Socket Base Address Register */ +#define PCI_BUSNUM 0x18 /* latency timer, Subordinate bus number */ +#define PCI_BCR_INTR 0x3C /* intr line, intr pin, bridge control regs */ +#define PCI_LEGACY 0x44 /* legacy IO register address (32 bits) */ +#define PCI_CBCTRL 0x90 /* Retry status, Card ctrl, Device ctrl */ + +#define PCI_CLASS_INTERFACE_MASK 0xffffff00 +#define PCI_CLASS_INTERFACE_YENTA 0x06070000 + +#define CB_SOCKET_EVENT 0x00 /* offset of cardbus socket event reg */ +#define CB_SOCKET_MASK 0x04 /* offset of cardbus socket mask register */ +#define CB_SOCKET_STAT 0x08 /* offset of cardbus socket present-state */ +#define CB_SOCKET_FORCE 0x0c /* offset of cardbus socket force event */ +#define CB_SOCKET_CTRL 0x10 /* offset of cardbus socket control reg */ + +#define PCCBB_SOCKEVENT_BITS "\020\001CSTS\002CD1\003CD2\004PWR" +#define PCCBB_SOCKSTATE_BITS "\020\001CSTS\002CD1\003CD3\004PWR" \ + "\00516BIT\006CB\007CINT\010NOTA\011DLOST\012BADVCC" \ + "\0135v\0143v\015Xv\016Yv\0355vS\0363vS\037XvS\040YvS" + +/* CardBus latency timer, Subordinate bus no, CardBus bus no and PCI bus no */ +#define PCI_CB_LSCP_REG 0x18 +/* CardBus memory and io windows */ +#define PCI_CB_MEMBASE0 0x1c +#define PCI_CB_MEMLIMIT0 0x20 +#define PCI_CB_MEMBASE1 0x24 +#define PCI_CB_MEMLIMIT1 0x28 +#define PCI_CB_IOBASE0 0x2c +#define PCI_CB_IOLIMIT0 0x30 +#define PCI_CB_IOBASE1 0x34 +#define PCI_CB_IOLIMIT1 0x38 + +/* PCI_CB_LSCP_REG */ +#define PCI_CB_LATENCY_SHIFT 24 +#define PCI_CB_LATENCY_MASK 0xff +#define PCI_CB_LATENCY(x) (((x) >> PCI_CB_LATENCY_SHIFT) & PCI_CB_LATENCY_MASK) + + + +/* PCI_BCR_INTR bits for generic PCI-CardBus bridge */ +#define CB_BCR_INTR_IREQ_ENABLE 0x00800000 +#define CB_BCR_PREFETCH_MEMWIN0 0x01000000 +#define CB_BCR_PREFETCH_MEMWIN1 0x02000000 +#define CB_BCR_WRITE_POST_ENABLE 0x04000000 + +/* PCI_CBCTRL bits for TI PCI113X */ +#define PCI113X_CBCTRL_INT_SERIAL 0x040000 +#define PCI113X_CBCTRL_INT_ISA 0x020000 +#define PCI113X_CBCTRL_INT_MASK 0x060000 +#define PCI113X_CBCTRL_RIENB 0x8000 /* Ring indicate output enable */ +#define PCI113X_CBCTRL_ZVENAB 0x4000 /* ZV mode enable */ +#define PCI113X_CBCTRL_PCI_IRQ_ENA 0x2000 /* PCI intr enable (funct and CSC) */ +#define PCI113X_CBCTRL_PCI_INTR 0x1000 /* PCI functional intr req */ +#define PCI113X_CBCTRL_PCI_CSC 0x0800 /* CSC intr route to PCI */ +#define PCI113X_CBCTRL_PCI_CSC_D 0x0400 /* unknown */ +#define PCI113X_CBCTRL_SPK_ENA 0x0200 /* Speaker enable */ +#define PCI113X_CBCTRL_INTR_DET 0x0100 /* functional interrupt detect */ + +/* PCI_CBCTRL bits for TI PCI12XX */ +#define PCI12XX_CBCTRL_INT_SERIAL 0x040000 +#define PCI12XX_CBCTRL_INT_ISA 0x020000 +#define PCI12XX_CBCTRL_INT_PCI 0x000000 +#define PCI12XX_CBCTRL_INT_MASK 0x060000 +#define PCI12XX_CBCTRL_RIENB 0x8000 /* Ring indicate output enable */ +#define PCI12XX_CBCTRL_ZVENAB 0x4000 /* ZV mode enable */ +#define PCI12XX_CBCTRL_AUD2MUX 0x0400 /* unknown */ +#define PCI12XX_CBCTRL_SPK_ENA 0x0200 /* Speaker enable */ +#define PCI12XX_CBCTRL_INTR_DET 0x0100 /* functional interrupt detect */ + + +/* PCI_BCR_INTR additional bit for Rx5C46[567] */ +#define CB_BCRI_RL_3E0_ENA 0x08000000 +#define CB_BCRI_RL_3E2_ENA 0x10000000 + +/* + * Special resister definition for Toshiba ToPIC95/97 + * These values are borrowed from pcmcia-cs/Linux. + */ +#define TOPIC_SOCKET_CTRL 0x90 +# define TOPIC_SOCKET_CTRL_SCR_IRQSEL 0x00000001 /* PCI intr */ + +#define TOPIC_SLOT_CTRL 0xa0 +# define TOPIC_SLOT_CTRL_SLOTON 0x00000080 +# define TOPIC_SLOT_CTRL_SLOTEN 0x00000040 +# define TOPIC_SLOT_CTRL_ID_LOCK 0x00000020 +# define TOPIC_SLOT_CTRL_ID_WP 0x00000010 +# define TOPIC_SLOT_CTRL_PORT_MASK 0x0000000c +# define TOPIC_SLOT_CTRL_PORT_SHIFT 2 +# define TOPIC_SLOT_CTRL_OSF_MASK 0x00000003 +# define TOPIC_SLOT_CTRL_OSF_SHIFT 0 + +# define TOPIC_SLOT_CTRL_INTB 0x00002000 +# define TOPIC_SLOT_CTRL_INTA 0x00001000 +# define TOPIC_SLOT_CTRL_INT_MASK 0x00003000 +# define TOPIC_SLOT_CTRL_CLOCK_MASK 0x00000c00 +# define TOPIC_SLOT_CTRL_CLOCK_2 0x00000800 /* PCI Clock/2 */ +# define TOPIC_SLOT_CTRL_CLOCK_1 0x00000400 /* PCI Clock */ +# define TOPIC_SLOT_CTRL_CLOCK_0 0x00000000 /* no clock */ + +# define TOPIC_SLOT_CTRL_CARDBUS 0x80000000 +# define TOPIC_SLOT_CTRL_VS1 0x04000000 +# define TOPIC_SLOT_CTRL_VS2 0x02000000 +# define TOPIC_SLOT_CTRL_SWDETECT 0x01000000 + +#define TOPIC_REG_CTRL 0x00a4 +# define TOPIC_REG_CTRL_RESUME_RESET 0x80000000 +# define TOPIC_REG_CTRL_REMOVE_RESET 0x40000000 +# define TOPIC97_REG_CTRL_CLKRUN_ENA 0x20000000 +# define TOPIC97_REG_CTRL_TESTMODE 0x10000000 +# define TOPIC97_REG_CTRL_IOPLUP 0x08000000 +# define TOPIC_REG_CTRL_BUFOFF_PWROFF 0x02000000 +# define TOPIC_REG_CTRL_BUFOFF_SIGOFF 0x01000000 +# define TOPIC97_REG_CTRL_CB_DEV_MASK 0x0000f800 +# define TOPIC97_REG_CTRL_CB_DEV_SHIFT 11 +# define TOPIC97_REG_CTRL_RI_DISABLE 0x00000004 +# define TOPIC97_REG_CTRL_CAUDIO_OFF 0x00000002 +# define TOPIC_REG_CTRL_CAUDIO_INVERT 0x00000001 + + + +/* socket event register (CB_SOCKET_EVENT) elements */ +#define CB_SOCKET_EVENT_CSTS 0x01 /* CARDSTS event occurs */ +#define CB_SOCKET_EVENT_CD 0x06 /* CD event occurs */ +#define CB_SOCKET_EVENT_CD1 0x02 /* CD1 event occurs */ +#define CB_SOCKET_EVENT_CD2 0x04 /* CD2 event occurs */ +#define CB_SOCKET_EVENT_POWER 0x08 /* Power cycle event occurs */ + + +/* socket mask register (CB_SOCKET_MASK) elements */ +#define CB_SOCKET_MASK_CSTS 0x01 /* CARDSTS event mask */ +#define CB_SOCKET_MASK_CD 0x06 /* CD event mask */ +#define CB_SOCKET_MASK_POWER 0x08 /* Power cycle event mask */ + +/* socket present-state register (CB_SOCKET_STAT) elements */ +#define CB_SOCKET_STAT_CARDSTS 0x01 /* card status change bit */ +#define CB_SOCKET_STAT_CD1 0x02 /* card detect 1 */ +#define CB_SOCKET_STAT_CD2 0x04 /* card detect 2 */ +#define CB_SOCKET_STAT_CD 0x06 /* card detect 1 and 2 */ +#define CB_SOCKET_STAT_PWRCYCLE 0x08 /* power cycle */ +#define CB_SOCKET_STAT_16BIT 0x010 /* 16-bit card */ +#define CB_SOCKET_STAT_CB 0x020 /* cardbus card */ +#define CB_SOCKET_STAT_IREQ 0x040 /* READY(~IREQ)//(~CINT) bit */ +#define CB_SOCKET_STAT_NOTCARD 0x080 /* Inserted card is unrecognisable */ +#define CB_SOCKET_STAT_DATALOST 0x0100 /* data lost */ +#define CB_SOCKET_STAT_BADVCC 0x0200 /* Bad Vcc Request */ +#define CB_SOCKET_STAT_5VCARD 0x0400 /* 5 V Card */ +#define CB_SOCKET_STAT_3VCARD 0x0800 /* 3.3 V Card */ +#define CB_SOCKET_STAT_XVCARD 0x01000 /* X.X V Card */ +#define CB_SOCKET_STAT_YVCARD 0x02000 /* Y.Y V Card */ +#define CB_SOCKET_STAT_5VSOCK 0x10000000 /* 5 V Socket */ +#define CB_SOCKET_STAT_3VSOCK 0x20000000 /* 3.3 V Socket */ +#define CB_SOCKET_STAT_XVSOCK 0x20000000 /* X.X V Socket */ +#define CB_SOCKET_STAT_YVSOCK 0x20000000 /* Y.Y V Socket */ + +/* socket force event register (CB_SOCKET_FORCE) elements */ +#define CB_SOCKET_FORCE_BADVCC 0x0200 /* Bad Vcc Request */ + + +/* socket control register (CB_SOCKET_CTRL) elements */ +#define CB_SOCKET_CTRL_VPPMASK 0x07 +#define CB_SOCKET_CTRL_VPP_OFF 0x00 +#define CB_SOCKET_CTRL_VPP_12V 0x01 +#define CB_SOCKET_CTRL_VPP_5V 0x02 +#define CB_SOCKET_CTRL_VPP_3V 0x03 +#define CB_SOCKET_CTRL_VPP_XV 0x04 +#define CB_SOCKET_CTRL_VPP_YV 0x05 + +#define CB_SOCKET_CTRL_VCCMASK 0x070 +#define CB_SOCKET_CTRL_VCC_OFF 0x000 +#define CB_SOCKET_CTRL_VCC_5V 0x020 +#define CB_SOCKET_CTRL_VCC_3V 0x030 +#define CB_SOCKET_CTRL_VCC_XV 0x040 +#define CB_SOCKET_CTRL_VCC_YV 0x050 + +#define CB_SOCKET_CTRL_STOPCLK 0x080 + + + +/* PCCARD VOLTAGE */ +#define PCCARD_VCC_UKN 0x00 /* unknown */ +#define PCCARD_VCC_5V 0x01 +#define PCCARD_VCC_3V 0x02 +#define PCCARD_VCC_XV 0x04 +#define PCCARD_VCC_YV 0x08 + + +#endif /* _DEV_PCI_PCCBBREG_H_ */ diff --git a/sys/dev/pci/pccbbvar.h b/sys/dev/pci/pccbbvar.h new file mode 100644 index 00000000000..089f1f5b11c --- /dev/null +++ b/sys/dev/pci/pccbbvar.h @@ -0,0 +1,174 @@ +/* $OpenBSD: pccbbvar.h,v 1.1 2000/04/08 05:50:51 aaron Exp $ */ +/* $NetBSD: pccbbvar.h,v 1.12 2000/03/23 07:01:40 thorpej Exp $ */ +/* + * Copyright (c) 1999 HAYAKAWA Koichi. 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 HAYAKAWA Koichi. + * 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. + */ + +/* require sys/device.h */ +/* require sys/queue.h */ +/* require sys/callout.h */ +/* require dev/ic/i82365reg.h */ +/* require dev/ic/i82365var.h */ + +#ifndef _DEV_PCI_PCCBBVAR_H_ +#define _DEV_PCI_PCCBBVAR_H_ + +#define PCIC_FLAG_SOCKETP 0x0001 +#define PCIC_FLAG_CARDP 0x0002 + +/* Chipset ID */ +#define CB_UNKNOWN 0 /* NOT Cardbus-PCI bridge */ +#define CB_TI113X 1 /* TI PCI1130/1131 */ +#define CB_TI12XX 2 /* TI PCI1250/1220 */ +#define CB_RX5C47X 3 /* RICOH RX5C475/476/477 */ +#define CB_RX5C46X 4 /* RICOH RX5C465/466/467 */ +#define CB_TOPIC95 5 /* Toshiba ToPIC95 */ +#define CB_TOPIC95B 6 /* Toshiba ToPIC95B */ +#define CB_TOPIC97 7 /* Toshiba ToPIC97 */ +#define CB_CIRRUS 8 /* Cirrus Logic CL-PD683X */ +#define CB_CHIPS_LAST 9 /* Sentinel */ + +#if 0 +static char *cb_chipset_name[CB_CHIPS_LAST] = { + "unknown", "TI 113X", "TI 12XX", "RF5C47X", "RF5C46X", "ToPIC95", + "ToPIC95B", "ToPIC97", "CL-PD 683X", +}; +#endif + +struct pccbb_softc; +struct pccbb_intrhand_list; + + +struct cbb_pcic_handle { + struct device *ph_parent; + bus_space_tag_t ph_base_t; + bus_space_handle_t ph_base_h; + u_int8_t (*ph_read) __P((struct cbb_pcic_handle *, int)); + void (*ph_write) __P((struct cbb_pcic_handle *, int, u_int8_t)); + int sock; + + int vendor; + int flags; + int memalloc; + struct { + bus_addr_t addr; + bus_size_t size; + long offset; + int kind; + } mem[PCIC_MEM_WINS]; + int ioalloc; + struct { + bus_addr_t addr; + bus_size_t size; + int width; + } io[PCIC_IO_WINS]; + int ih_irq; + struct device *pcmcia; + + int shutdown; +}; + +struct pccbb_win_chain { + bus_addr_t wc_start; /* Caution: region [start, end], */ + bus_addr_t wc_end; /* instead of [start, end). */ + int wc_flags; + bus_space_handle_t wc_handle; + TAILQ_ENTRY(pccbb_win_chain) wc_list; +}; +#define PCCBB_MEM_CACHABLE 1 + +TAILQ_HEAD(pccbb_win_chain_head, pccbb_win_chain); + +struct pccbb_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_tag_t sc_memt; + bus_dma_tag_t sc_dmat; + +#if rbus + rbus_tag_t sc_rbus_iot; /* rbus for i/o donated from parent */ + rbus_tag_t sc_rbus_memt; /* rbus for mem donated from parent */ +#endif + + bus_space_tag_t sc_base_memt; + bus_space_handle_t sc_base_memh; + + void *sc_ih; /* interrupt handler */ + int sc_intrline; /* interrupt line */ + pcitag_t sc_intrtag; /* copy of pa->pa_intrtag */ + pci_intr_pin_t sc_intrpin; /* copy of pa->pa_intrpin */ + int sc_function; + u_int32_t sc_flags; +#define CBB_CARDEXIST 0x01 +#define CBB_INSERTING 0x01000000 +#define CBB_16BITCARD 0x04 +#define CBB_32BITCARD 0x08 + + pci_chipset_tag_t sc_pc; + pcitag_t sc_tag; + int sc_chipset; /* chipset id */ + + bus_addr_t sc_mem_start; /* CardBus/PCMCIA memory start */ + bus_addr_t sc_mem_end; /* CardBus/PCMCIA memory end */ + bus_addr_t sc_io_start; /* CardBus/PCMCIA io start */ + bus_addr_t sc_io_end; /* CardBus/PCMCIA io end */ + + /* CardBus stuff */ + struct cardslot_softc *sc_csc; + + struct pccbb_win_chain_head sc_memwindow; + struct pccbb_win_chain_head sc_iowindow; + + /* pcmcia stuff */ + struct pcic_handle sc_pcmcia_h; + pcmcia_chipset_tag_t sc_pct; + int sc_pcmcia_flags; +#define PCCBB_PCMCIA_IO_RELOC 0x01 /* IO addr relocatable stuff exists */ +#define PCCBB_PCMCIA_MEM_32 0x02 /* 32-bit memory address ready */ +#define PCCBB_PCMCIA_16BITONLY 0x04 /* 32-bit mode disable */ + + struct proc *sc_event_thread; + SIMPLEQ_HEAD(, pcic_event) sc_events; + + /* interrupt handler list on the bridge */ + struct pccbb_intrhand_list *sc_pil; + int sc_pil_intr_enable; /* can i call intr handler for child device? */ +}; + +/* + * struct pccbb_intrhand_list holds interrupt handler and argument for + * child devices. + */ + +struct pccbb_intrhand_list { + int (*pil_func) __P((void *)); + void *pil_arg; + struct pccbb_intrhand_list *pil_next; +}; + +#endif /* _DEV_PCI_PCCBBREG_H_ */ diff --git a/sys/dev/pcmcia/files.pcmcia b/sys/dev/pcmcia/files.pcmcia index a852cbc6322..c54a35d137a 100644 --- a/sys/dev/pcmcia/files.pcmcia +++ b/sys/dev/pcmcia/files.pcmcia @@ -1,4 +1,4 @@ -# $OpenBSD: files.pcmcia,v 1.28 2000/04/03 01:02:00 mickey Exp $ +# $OpenBSD: files.pcmcia,v 1.29 2000/04/08 05:50:51 aaron Exp $ # $NetBSD: files.pcmcia,v 1.9 1998/06/21 18:45:41 christos Exp $ # # Config.new file and device description for machine-independent PCMCIA code. @@ -10,7 +10,7 @@ file dev/pcmcia/pcmcia_cis.c pcmcia file dev/pcmcia/pcmcia_cis_quirks.c pcmcia # device declaration in sys/conf/files -attach pcmcia at pcic +attach pcmcia at pcmciabus # 3Com 3c589 Ethernet, 3c562 multifunction Ethernet, and 3CXEM556 # multifunction Ethernet controllers diff --git a/sys/dev/pcmcia/pcmcia.c b/sys/dev/pcmcia/pcmcia.c index 24d2aefff23..f9b5710d5a0 100644 --- a/sys/dev/pcmcia/pcmcia.c +++ b/sys/dev/pcmcia/pcmcia.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pcmcia.c,v 1.25 2000/02/05 22:10:50 deraadt Exp $ */ +/* $OpenBSD: pcmcia.c,v 1.26 2000/04/08 05:50:51 aaron Exp $ */ /* $NetBSD: pcmcia.c,v 1.9 1998/08/13 02:10:55 eeh Exp $ */ /* @@ -102,6 +102,12 @@ pcmcia_match(parent, match, aux) struct device *parent; void *match, *aux; { + struct cfdata *cf = match; + struct pcmciabus_attach_args *paa = aux; + + if (strcmp(paa->paa_busname, cf->cf_driver->cd_name)) + return 0; + /* If the autoconfiguration got this far, there's a socket here. */ return (1); } diff --git a/sys/dev/pcmcia/pcmciachip.h b/sys/dev/pcmcia/pcmciachip.h index bbe25659f85..65821a53138 100644 --- a/sys/dev/pcmcia/pcmciachip.h +++ b/sys/dev/pcmcia/pcmciachip.h @@ -1,5 +1,5 @@ -/* $OpenBSD: pcmciachip.h,v 1.2 1999/08/08 01:00:14 niklas Exp $ */ -/* $NetBSD: pcmciachip.h,v 1.2 1997/10/16 23:27:36 thorpej Exp $ */ +/* $OpenBSD: pcmciachip.h,v 1.3 2000/04/08 05:50:51 aaron Exp $ */ +/* $NetBSD: pcmciachip.h,v 1.5 2000/01/13 08:58:51 joda Exp $ */ /* * Copyright (c) 1997 Marc Horowitz. All rights reserved. @@ -48,6 +48,11 @@ typedef int pcmcia_mem_handle_t; #define PCMCIA_MEM_ATTR 1 #define PCMCIA_MEM_COMMON 2 +#define PCMCIA_WIDTH_MEM8 8 +#define PCMCIA_WIDTH_MEM16 16 + +#define PCMCIA_WIDTH_MEM_MASK 24 + #define PCMCIA_WIDTH_AUTO 0 #define PCMCIA_WIDTH_IO8 1 #define PCMCIA_WIDTH_IO16 2 @@ -84,6 +89,9 @@ struct pcmcia_chip_functions { /* card enable/disable */ void (*socket_enable) __P((pcmcia_chipset_handle_t)); void (*socket_disable) __P((pcmcia_chipset_handle_t)); + + /* card detection */ + int (*card_detect) __P((pcmcia_chipset_handle_t)); }; /* Memory space functions. */ @@ -130,6 +138,7 @@ struct pcmcia_chip_functions { ((*(tag)->socket_disable)((handle))) struct pcmciabus_attach_args { + char *paa_busname; /* Bus name */ pcmcia_chipset_tag_t pct; pcmcia_chipset_handle_t pch; bus_addr_t iobase; /* start i/o space allocation here */ diff --git a/sys/kern/Makefile b/sys/kern/Makefile index b5eeebf62c8..8d0f21f1b62 100644 --- a/sys/kern/Makefile +++ b/sys/kern/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.7 1997/02/24 14:19:55 niklas Exp $ +# $OpenBSD: Makefile,v 1.8 2000/04/08 05:50:52 aaron Exp $ # Makefile for kernel tags files, init_sysent, etc. @@ -33,8 +33,8 @@ DGEN= adosfs \ compat/linux compat/osf1 compat/sunos compat/svr4 compat/ultrix \ conf \ ddb \ - dev dev/eisa dev/ic dev/isa dev/pci dev/pcmcia dev/rcons dev/sun \ - dev/tc \ + dev dev/eisa dev/ic dev/isa dev/pci dev/pcmcia dev/cardbus dev/rcons \ + dev/sun dev/tc \ gnu \ isofs isofs/cd9660 \ kern \