/* $NetBSD: if_ne_pbus.c,v 1.3 2000/12/07 21:24:33 bjh21 Exp $ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Mark Brinicombe of Causality Limited. * * EtherH code Copyright (c) 1998 Mike Pumford * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * This driver uses the generic ne2000 & dp8390 IC drivers * * Currently supports: * ANT EtherM network slot cards * ICubed EtherH network slot cards */ /* * TO DO List for this driver. * * EtherH - The driver currently relies on some part if the RISCOS driver * initialisation code to work correctly. This dependancy needs to be * removed. * * Needs to be converted to use media a'la if_ne_pci.c, not the home-grown * hack it currently uses. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * ne_pbus_softc: ne2000_softc plus podule, interrupt and bs tag info */ struct ne_pbus_softc { struct ne2000_softc sc_ne2000; /* ne2000 softc */ int sc_podule_number; podule_t *sc_podule; struct bus_space sc_tag; /* Patched tag */ void *sc_ih; /* Interrupt handler */ int sc_mediatype; /* Media Info */ #define NE_MEDIA_AUTO 0 #define NE_MEDIA_10BASET 1 #define NE_MEDIA_10BASE2 2 }; /* * Attach data and prototypes for driver */ static int ne_pbus_probe __P((struct device *, struct cfdata *, void *)); static void ne_pbus_attach __P((struct device *, struct device *, void *)); struct cfattach ne_pbus_ca = { sizeof(struct ne_pbus_softc), ne_pbus_probe, ne_pbus_attach }; /* * Prototypes for interface specific routines */ static u_int8_t *em_ea __P((struct ne_pbus_softc *sc, u_int8_t *buffer)); static void em_postattach __P((struct ne_pbus_softc *sc)); static void eh600_preattach __P((struct ne_pbus_softc *sc)); static void eh600_postattach __P((struct ne_pbus_softc *sc)); static u_int8_t *eh600_ea __P((struct ne_pbus_softc *sc, u_int8_t *buffer)); /* * Define a structure to hold all the information required on an NE2000 * clone interface. * We create an array of these structures to describe all the interfaces * that we can handle via the MI NE2000 driver. */ struct ne_clone { int manufacturer; /* podule manufacturer id */ int product; /* podule product id */ unsigned int cookie; /* podulebus space cookie */ unsigned int nicbase; /* byte offset of NIC */ unsigned int nicsize; /* size of NIC (regs) */ unsigned int asicbase; /* byte offset of ASIC */ unsigned int asicsize; /* size of ASIC (regs) */ unsigned char nicspace; /* fast or mod space ? */ unsigned char asicspace; /* fast or mod space ? */ #define NE_SPACE_FAST 0 #define NE_SPACE_MOD 1 unsigned char reserved0; /* not used (padding) */ unsigned char reserved1; /* not used (padding) */ const char *name; /* name */ u_int8_t * (*getea) /* do this to get the MAC */ __P((struct ne_pbus_softc *sc, u_int8_t *buffer)); void (*preattach) /* do this before attach */ __P((struct ne_pbus_softc *sc)); void (*postattach) /* do this after attach */ __P((struct ne_pbus_softc *sc)); } ne_clones[] = { /* ANT EtherM netslot interface */ { MANUFACTURER_ANT, PODULE_ANT_ETHERM, EM_REGSHIFT, EM_NIC_OFFSET, EM_NIC_SIZE, EM_ASIC_OFFSET, EM_ASIC_SIZE, NE_SPACE_FAST, NE_SPACE_FAST, 0, 0, "EtherM", em_ea, NULL, em_postattach }, /* ICubed EtherLan EtherH netslot interface */ { MANUFACTURER_ICUBED, PODULE_ICUBED_ETHERLAN600, EH600_REGSHIFT, EH600_NIC_OFFSET, EH600_NIC_SIZE, EH600_ASIC_OFFSET, EH600_ASIC_SIZE, NE_SPACE_FAST, NE_SPACE_FAST, 0, 0, "EtherLan 600", eh600_ea, eh600_preattach, eh600_postattach }, /* Acorn EtherLan EtherH netslot interface */ { MANUFACTURER_ICUBED, PODULE_ICUBED_ETHERLAN600AEH, EH600_REGSHIFT, EH600_NIC_OFFSET, EH600_NIC_SIZE, EH600_ASIC_OFFSET, EH600_ASIC_SIZE, NE_SPACE_FAST, NE_SPACE_FAST, 0, 0, "EtherLan 600A", eh600_ea , eh600_preattach, eh600_postattach }, }; /* * Determine if the device is present. */ static int ne_pbus_probe(parent, cf, aux) struct device *parent; struct cfdata *cf; void *aux; { struct podule_attach_args *pa = (void *) aux; int loop; /* Scan the list of known interfaces looking for a match */ for (loop = 0; loop < sizeof(ne_clones) / sizeof(struct ne_clone); ++loop) { if (matchpodule(pa, ne_clones[loop].manufacturer, ne_clones[loop].product, 0) != 0) return(1); } return(0); } /* * Install interface into kernel networking data structures. */ static void ne_pbus_attach(parent, self, aux) struct device *parent, *self; void *aux; { struct podule_attach_args *pa = (void *)aux; struct ne_pbus_softc *npsc = (void *)self; struct ne2000_softc *nsc = &npsc->sc_ne2000; struct dp8390_softc *dsc = &nsc->sc_dp8390; struct ne_clone *ne = NULL; u_int8_t buffer[6]; u_int8_t *myea; int loop; /* Check a few things about the attach args */ if (pa->pa_podule_number == -1) panic("Podule has disappeared !"); npsc->sc_podule_number = pa->pa_podule_number; npsc->sc_podule = pa->pa_podule; podules[npsc->sc_podule_number].attached = 1; /* XXX */ /* Scan the list of known interfaces for a match */ for (loop = 0; loop < sizeof(ne_clones) / sizeof(struct ne_clone); ++loop) { if (IS_PODULE(pa, ne_clones[loop].manufacturer, ne_clones[loop].product)) { ne = &ne_clones[loop]; break; } } #ifdef DIAGNOSTIC /* This should never fail as we must have matched at probe time */ if (ne == NULL) panic("Podule has vanished\n"); #endif /* Update the nic and asic base addresses appropriately */ switch (ne->nicspace) { case NE_SPACE_MOD: ne->nicbase += npsc->sc_podule->mod_base; break; case NE_SPACE_FAST: default: ne->nicbase += npsc->sc_podule->fast_base; break; } switch (ne->asicspace) { case NE_SPACE_MOD: ne->asicbase += npsc->sc_podule->mod_base; break; case NE_SPACE_FAST: default: ne->asicbase += npsc->sc_podule->fast_base; break; } /* Report the interface name */ printf(" %s ethernet\n", ne->name); /* * Ok we need our own bus tag as the register spacing * may not the default. * * For the podulebus, the bus tag cookie is the shift * to apply to registers * So duplicate the bus space tag and change the * cookie. */ npsc->sc_tag = *pa->pa_iot; npsc->sc_tag.bs_cookie = (void *) ne->cookie; dsc->sc_regt = &npsc->sc_tag; nsc->sc_asict = dsc->sc_regt; /* Map all the I/O space for the NIC */ if (bus_space_map(dsc->sc_regt, ne->nicbase, ne->nicsize, 0, &dsc->sc_regh)) { printf("%s: cannot map i/o space\n", dsc->sc_dev.dv_xname); return; } /* Map the I/O space for the ASIC */ if (bus_space_map(nsc->sc_asict, ne->asicbase, ne->asicsize, 0, &nsc->sc_asich)) { printf("%s: cannot map i/o space\n", dsc->sc_dev.dv_xname); return; } /* This interface is always enabled. */ dsc->sc_enabled = 1; npsc->sc_mediatype = NE_MEDIA_AUTO; /* Default */ /* * Now get the ethernet address in an interface specific manner if * specified */ if (ne->getea) myea = ne->getea(npsc, buffer); else myea = NULL; /* Does the interface need a preattach call ? */ if (ne->preattach) ne->preattach(npsc); /* * Do generic NE2000 attach. This will read the station address * from the EEPROM. */ ne2000_attach(nsc, myea, NULL, 0, 0); /* Does the interface need a postattach call ? */ if (ne->postattach) ne->postattach(npsc); /* Install an interrupt handler */ npsc->sc_ih = intr_claim(npsc->sc_podule->interrupt, IPL_NET, "if_ne", dp8390_intr, dsc); if (npsc->sc_ih == NULL) panic("%s: Cannot install interrupt handler", dsc->sc_dev.dv_xname); } /* * em_ea() * * return the ethernet address for an EtherM netslot interface. * The EtherM interface uses the machines ethernet address so just * fill it out */ static u_int8_t * em_ea(sc, buffer) struct ne_pbus_softc *sc; u_int8_t *buffer; { /* * Use the podulebus netslot_ea() function to get the netslot * ethernet address. This is generated from the machine ID. */ netslot_ea(buffer); return(buffer); } /* * em_postattach() * * The EtherM interface has a Diagnostic Status register. After attaching * the driver, print out some more information using this register. */ static void em_postattach(sc) struct ne_pbus_softc *sc; { int dsr; /* * Report information from the Diagnostic Status Register for * the EtherM card */ printf("%s: 16KB buffer memory", sc->sc_ne2000.sc_dp8390.sc_dev.dv_xname); /* Get the Diagnostic Status Register */ dsr = bus_space_read_1(sc->sc_ne2000.sc_asict, sc->sc_ne2000.sc_asich, EM_DSR_REG); /* Check for bits that indicate a fault */ if (!(dsr & EM_DSR_20M)) printf(", VCO faulty"); if (!(dsr & EM_DSR_TCOK)) printf(", TxClk faulty"); /* Report status of card */ if (dsr & EM_DSR_POL) printf(", UTP reverse polarity"); if (dsr & EM_DSR_JAB) printf(", jabber"); if (dsr & EM_DSR_LNK) printf(", link OK"); if (dsr & EM_DSR_LBK) printf(", loopback"); if (dsr & EM_DSR_UTP) printf(", UTP"); printf("\n"); } /* * eh600_preattach() * * Detect whether the BNC or UTP media attachment is being used. */ static void eh600_preattach(sc) struct ne_pbus_softc *sc; { u_int8_t tmp; bus_space_tag_t nict = sc->sc_ne2000.sc_dp8390.sc_regt; bus_space_handle_t nich = sc->sc_ne2000.sc_dp8390.sc_regh; /* now try and detect a UTP connection */ tmp = bus_space_read_1(nict, nich, EH600_MCRB); tmp = (tmp & 0xf8) | EH600_10BTSEL; bus_space_write_1(nict, nich, EH600_MCRB, tmp); tmp = bus_space_read_1(nict, nich, EH600_MCRB); if ((tmp & 0x04) == 0x04) /* UTP link detected */ sc->sc_mediatype = NE_MEDIA_10BASET; else { /* No UTP use BNC */ tmp = (tmp & 0xf8) | EH600_10B2SEL; bus_space_write_1(nict, nich, EH600_MCRB, tmp); sc->sc_mediatype = NE_MEDIA_10BASE2; } } /* * eh600_postattach() * * Report the media connection detected in the preattach routine */ static void eh600_postattach(sc) struct ne_pbus_softc *sc; { printf("%s: using ", sc->sc_ne2000.sc_dp8390.sc_dev.dv_xname); if (sc->sc_mediatype == NE_MEDIA_10BASET) printf("10BaseT/UTP\n"); else printf("10Base2/BNC\n"); } /* * extracts the station address from the Podule description string. * The description has to be re-read here since the podule description * string is not always long enough to contain the full address. * * If for any reason we cannot extract the address this routine will * use netslot_ea() to return the generic address for the network slot. */ #define POD_READ(addr) \ poduleread(podule->sync_base, addr, podule->slottype) static u_int8_t * eh600_ea(sc, buffer) struct ne_pbus_softc *sc; u_int8_t *buffer; { podule_t *podule = sc->sc_podule; u_int address; u_int id; address = 0x40; memset(buffer, 0, 6); /* read chunks from the podule */ do { id = POD_READ(address); /* check for description chunk. */ if (id == 0xf5) { u_int size; u_int pod_addr; int loop; /* read the size */ size = POD_READ(address + 4); size |= (POD_READ(address + 8) << 8); size |= (POD_READ(address + 12) << 16); /* read address of description */ pod_addr = POD_READ(address + 16); pod_addr |= (POD_READ(address + 20) << 8); pod_addr |= (POD_READ(address + 24) << 16); pod_addr |= (POD_READ(address + 28) << 24); if (pod_addr < 0x800) { u_int8_t tmp; int addr_index = 0; int found_ether = 0; /* * start scanning for ethernet address * which starts with a '(' */ for (loop = 0; loop < size; ++loop) { if (found_ether) { /* we have found a '(' so start decoding the address */ tmp = POD_READ((pod_addr + loop) * 4); if (tmp >= '0' && tmp <= '9') { buffer[addr_index >> 1] |= (tmp - '0') << ((addr_index & 1) ? 0 : 4); ++addr_index; } else if (tmp >= 'a' && tmp <= 'f'){ buffer[addr_index >> 1] |= (10 + (tmp - 'a')) << ((addr_index & 1) ? 0 : 4); ++addr_index; } else if (tmp >= 'A' && tmp <= 'F'){ buffer[addr_index >> 1] |= (10 + (tmp - 'A')) << ((addr_index & 1) ? 0 : 4); ++addr_index; } else if (tmp == ')') { /* we have read the whole address so we can stop scanning * the podule description */ break; } } /* * we have found the start of the ethernet address (decode begins * on the next run round the loop. */ if (POD_READ((pod_addr + loop) * 4) == '(') { found_ether = 1; } } /* * Failed to find the address so fall back * on the netslot address */ if (!found_ether) netslot_ea(buffer); return(buffer); } } address += 32; } while (id != 0 && address < 0x8000); /* * If we get here we failed to find the address * In this case the best solution is to go with the netslot addrness */ netslot_ea(buffer); return(buffer); }