NetBSD/sys/arch/arm32/podulebus/if_ne_pbus.c
bjh21 2d96994642 It seems that i-cubed used the "A" suffix on card types (along with "Lk" and
probably some others) to indicate the software mix on the card, while we
were using it to indicate Acorn-branded variants.  Change over to using "AEH"
for the latter.
2000-12-07 21:24:33 +00:00

530 lines
15 KiB
C

/* $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 <sys/param.h>
#include <sys/device.h>
#include <sys/socket.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_ether.h>
#include <net/if_media.h>
#include <machine/bus.h>
#include <machine/irqhandler.h>
#include <machine/io.h>
#include <dev/ic/dp8390reg.h>
#include <dev/ic/dp8390var.h>
#include <dev/ic/ne2000reg.h>
#include <dev/ic/ne2000var.h>
#include <arch/arm32/podulebus/podulebus.h>
#include <arch/arm32/podulebus/podules.h>
#include <arch/arm32/podulebus/if_ne_pbusreg.h>
/*
* 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);
}