Add in the if_mc ethernet driver for the AMD "MACE" part. This is the

internal ethernet on the Quadra/Centris 660av/840av.
Add initial support for the PSC (DMA controller) to support the above
(DMA SCSI remains unsupported).  This involved also changing the way
that several interrupts are handled.
Above from David Huang <khym@bga.com>
Since the interrupts changed somewhat, we must also make the ipls
dynamic, defaulting to their prior levels and adjusted for the AVs.
I modelled this on the hp300.
This commit is contained in:
briggs 1997-11-04 03:44:42 +00:00
parent 226f67d0d4
commit e658b8b476
12 changed files with 2458 additions and 39 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: GENERIC,v 1.68 1997/10/20 22:52:48 scottr Exp $
# $NetBSD: GENERIC,v 1.69 1997/11/04 03:44:42 briggs Exp $
#
# GENERIC
@ -80,6 +80,7 @@ config netbsd root on ? type ?
asc0 at obio? # ASC/EASC audio
intvid0 at obio? # Internal video hardware
sn* at obio? # SONIC-based internal ethernet
mc* at obio? # MACE-based internal ethernet
nubus0 at mainbus?
ae* at nubus? # 8390-based Ethernet NIC

View File

@ -1,4 +1,4 @@
# $NetBSD: files.mac68k,v 1.73 1997/10/20 08:13:26 scottr Exp $
# $NetBSD: files.mac68k,v 1.74 1997/11/04 03:44:46 briggs Exp $
# mac68k-specific configuration info
@ -51,14 +51,19 @@ device grf { }
attach grf at grfbus
file arch/mac68k/dev/grf.c grf needs-flag
device ite
attach ite at grf
file arch/mac68k/dev/ite.c ite needs-flag
device ae: ifnet, ether, arp, dp8390nic
attach ae at nubus with ae_nubus
file arch/mac68k/dev/if_ae_nubus.c ae_nubus
file arch/mac68k/dev/if_ae.c ae
device ite
attach ite at grf
file arch/mac68k/dev/ite.c ite needs-flag
device mc: ifnet, ether, arp
attach mc at obio_norm with mc_obio
file arch/mac68k/dev/if_mc.c mc
file arch/mac68k/dev/if_mc_obio.c mc_obio
device sn: ifnet, ether, arp
attach sn at obio_norm with sn_obio
@ -116,6 +121,7 @@ file arch/mac68k/mac68k/pmap.c
file arch/mac68k/mac68k/pmap_bootstrap.c
file arch/mac68k/mac68k/pram.c
file arch/mac68k/mac68k/pramasm.s
file arch/mac68k/mac68k/psc.c
file arch/mac68k/mac68k/sys_machdep.c
file arch/mac68k/mac68k/trap.c
file arch/mac68k/mac68k/via.c

810
sys/arch/mac68k/dev/if_mc.c Normal file
View File

@ -0,0 +1,810 @@
/*-
* Copyright (c) 1997 David Huang <khym@bga.com>
* All rights reserved.
*
* Portions of this code are based on code by Denton Gentry <denny1@home.com>,
* Charles M. Hannum, Yanagisawa Takeshi <yanagisw@aa.ap.titech.ac.jp>, and
* Jason R. Thorpe.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
*/
/*
* Driver for the AMD Am79C940 (MACE) ethernet chip, used for onboard
* ethernet on the Centris/Quadra 660av and Quadra 840av.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/buf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/device.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_ether.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/if_inarp.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#endif
#ifdef NS
#include <netns/ns.h>
#include <netns/ns_if.h>
#endif
#if defined(CCITT) && defined(LLC)
#include <sys/socketvar.h>
#include <netccitt/x25.h>
#include <netccitt/pk.h>
#include <netccitt/pk_var.h>
#include <netccitt/pk_extern.h>
#endif
#include <vm/vm.h>
#include "bpfilter.h"
#if NBPFILTER > 0
#include <net/bpf.h>
#include <net/bpfdesc.h>
#endif
#include <machine/bus.h>
#include <mac68k/dev/if_mcreg.h>
#include <mac68k/dev/if_mcvar.h>
hide void mcwatchdog __P((struct ifnet *));
hide int mcinit __P((struct mc_softc *sc));
hide int mcstop __P((struct mc_softc *sc));
hide int mcioctl __P((struct ifnet *ifp, u_long cmd, caddr_t data));
hide void mcstart __P((struct ifnet *ifp));
hide void mcreset __P((struct mc_softc *sc));
integrate u_int maceput __P((struct mc_softc *sc, struct mbuf *m0));
integrate void mace_read __P((struct mc_softc *, caddr_t, int));
integrate struct mbuf *mace_get __P((struct mc_softc *, caddr_t, int));
static void mace_calcladrf __P((struct ethercom *ac, u_int8_t *af));
static inline u_int16_t ether_cmp __P((void *, void *));
struct cfdriver mc_cd = {
NULL, "mc", DV_IFNET
};
/*
* Compare two Ether/802 addresses for equality, inlined and
* unrolled for speed. Use this like bcmp().
*
* XXX: Add <machine/inlines.h> for stuff like this?
* XXX: or maybe add it to libkern.h instead?
*
* "I'd love to have an inline assembler version of this."
* XXX: Who wanted that? mycroft? I wrote one, but this
* version in C is as good as hand-coded assembly. -gwr
*
* Please do NOT tweak this without looking at the actual
* assembly code generated before and after your tweaks!
*/
static inline u_int16_t
ether_cmp(one, two)
void *one, *two;
{
register u_int16_t *a = (u_short *) one;
register u_int16_t *b = (u_short *) two;
register u_int16_t diff;
#ifdef m68k
/*
* The post-increment-pointer form produces the best
* machine code for m68k. This was carefully tuned
* so it compiles to just 8 short (2-byte) op-codes!
*/
diff = *a++ - *b++;
diff |= *a++ - *b++;
diff |= *a++ - *b++;
#else
/*
* Most modern CPUs do better with a single expresion.
* Note that short-cut evaluation is NOT helpful here,
* because it just makes the code longer, not faster!
*/
diff = (a[0] - b[0]) | (a[1] - b[1]) | (a[2] - b[2]);
#endif
return (diff);
}
#define ETHER_CMP ether_cmp
/*
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
* to accept packets.
*/
int
mcsetup(sc, lladdr)
struct mc_softc *sc;
u_int8_t *lladdr;
{
struct ifnet *ifp = &sc->sc_if;
/* reset the chip and disable all interrupts */
NIC_PUT(sc, MACE_BIUCC, SWRST);
DELAY(100);
NIC_PUT(sc, MACE_IMR, ~0);
bcopy(lladdr, sc->sc_enaddr, ETHER_ADDR_LEN);
printf(": address %s\n", ether_sprintf(lladdr));
bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
ifp->if_softc = sc;
ifp->if_ioctl = mcioctl;
ifp->if_start = mcstart;
ifp->if_flags =
IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST;
ifp->if_watchdog = mcwatchdog;
#if NBPFILTER > 0
bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
#endif
if_attach(ifp);
ether_ifattach(ifp, lladdr);
return (0);
}
hide int
mcioctl(ifp, cmd, data)
struct ifnet *ifp;
u_long cmd;
caddr_t data;
{
struct mc_softc *sc = ifp->if_softc;
struct ifaddr *ifa;
struct ifreq *ifr;
int s = splnet(), err = 0;
int temp;
switch (cmd) {
case SIOCSIFADDR:
ifa = (struct ifaddr *)data;
ifp->if_flags |= IFF_UP;
switch (ifa->ifa_addr->sa_family) {
#ifdef INET
case AF_INET:
mcinit(sc);
arp_ifinit(ifp, ifa);
break;
#endif
#ifdef NS
case AF_NS:
{
register struct ns_addr *ina = &IA_SNS(ifa)->sns_addr;
if (ns_nullhost(*ina))
ina->x_host =
*(union ns_host *)LLADDR(ifp->if_sadl);
else {
bcopy(ina->x_host.c_host,
LLADDR(ifp->if_sadl),
sizeof(sc->sc_enaddr));
}
/* Set new address. */
mcinit(sc);
break;
}
#endif
default:
mcinit(sc);
break;
}
break;
case SIOCSIFFLAGS:
if ((ifp->if_flags & IFF_UP) == 0 &&
(ifp->if_flags & IFF_RUNNING) != 0) {
/*
* If interface is marked down and it is running,
* then stop it.
*/
mcstop(sc);
ifp->if_flags &= ~IFF_RUNNING;
} else if ((ifp->if_flags & IFF_UP) != 0 &&
(ifp->if_flags & IFF_RUNNING) == 0) {
/*
* If interface is marked up and it is stopped,
* then start it.
*/
(void)mcinit(sc);
} else {
/*
* reset the interface to pick up any other changes
* in flags
*/
temp = ifp->if_flags & IFF_UP;
mcreset(sc);
ifp->if_flags |= temp;
mcstart(ifp);
}
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
ifr = (struct ifreq *) data;
err = (cmd == SIOCADDMULTI) ?
ether_addmulti(ifr, &sc->sc_ethercom) :
ether_delmulti(ifr, &sc->sc_ethercom);
if (err == ENETRESET) {
/*
* Multicast list has changed; set the hardware
* filter accordingly. But remember UP flag!
*/
temp = ifp->if_flags & IFF_UP;
mcreset(sc);
ifp->if_flags |= temp;
err = 0;
}
break;
default:
err = EINVAL;
}
splx(s);
return (err);
}
/*
* Encapsulate a packet of type family for the local net.
*/
hide void
mcstart(ifp)
struct ifnet *ifp;
{
struct mc_softc *sc = ifp->if_softc;
struct mbuf *m;
if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
return;
while (1) {
if (ifp->if_flags & IFF_OACTIVE)
return;
IF_DEQUEUE(&ifp->if_snd, m);
if (m == 0)
return;
#if NBPFILTER > 0
/*
* If bpf is listening on this interface, let it
* see the packet before we commit it to the wire.
*/
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, m);
#endif
/*
* Copy the mbuf chain into the transmit buffer.
*/
ifp->if_flags |= IFF_OACTIVE;
maceput(sc, m);
ifp->if_opackets++; /* # of pkts */
}
}
/*
* reset and restart the MACE. Called in case of fatal
* hardware/software errors.
*/
hide void
mcreset(sc)
struct mc_softc *sc;
{
mcstop(sc);
mcinit(sc);
}
hide int
mcinit(sc)
struct mc_softc *sc;
{
int s;
u_int8_t maccc, ladrf[8];
if (sc->sc_if.if_flags & IFF_RUNNING)
/* already running */
return (0);
s = splnet();
NIC_PUT(sc, MACE_BIUCC, sc->sc_biucc);
NIC_PUT(sc, MACE_FIFOCC, sc->sc_fifocc);
NIC_PUT(sc, MACE_IMR, ~0); /* disable all interrupts */
NIC_PUT(sc, MACE_PLSCC, sc->sc_plscc);
NIC_PUT(sc, MACE_UTR, RTRD); /* disable reserved test registers */
/* set MAC address */
NIC_PUT(sc, MACE_IAC, ADDRCHG);
while (NIC_GET(sc, MACE_IAC) & ADDRCHG)
;
NIC_PUT(sc, MACE_IAC, PHYADDR);
bus_space_write_multi_1(sc->sc_regt, sc->sc_regh, MACE_REG(MACE_PADR),
sc->sc_enaddr, ETHER_ADDR_LEN);
/* set logical address filter */
mace_calcladrf(&sc->sc_ethercom, ladrf);
NIC_PUT(sc, MACE_IAC, ADDRCHG);
while (NIC_GET(sc, MACE_IAC) & ADDRCHG)
;
NIC_PUT(sc, MACE_IAC, LOGADDR);
bus_space_write_multi_1(sc->sc_regt, sc->sc_regh, MACE_REG(MACE_LADRF),
ladrf, 8);
NIC_PUT(sc, MACE_XMTFC, APADXMT);
/*
* No need to autostrip padding on receive... Ethernet frames
* don't have a length field, unlike 802.3 frames, so the MACE
* can't figure out the length of the packet anyways.
*/
NIC_PUT(sc, MACE_RCVFC, 0);
maccc = ENXMT | ENRCV;
if (sc->sc_if.if_flags & IFF_PROMISC)
maccc |= PROM;
NIC_PUT(sc, MACE_MACCC, maccc);
if (sc->sc_bus_init)
(*sc->sc_bus_init)(sc);
/*
* Enable all interrupts except receive, since we use the DMA
* completion interrupt for that.
*/
NIC_PUT(sc, MACE_IMR, RCVINTM);
/* flag interface as "running" */
sc->sc_if.if_flags |= IFF_RUNNING;
sc->sc_if.if_flags &= ~IFF_OACTIVE;
splx(s);
return (0);
}
/*
* close down an interface and free its buffers
* Called on final close of device, or if mcinit() fails
* part way through.
*/
hide int
mcstop(sc)
struct mc_softc *sc;
{
int s = splnet();
NIC_PUT(sc, MACE_BIUCC, SWRST);
DELAY(100);
sc->sc_if.if_timer = 0;
sc->sc_if.if_flags &= ~(IFF_RUNNING | IFF_UP);
splx(s);
return (0);
}
/*
* Called if any Tx packets remain unsent after 5 seconds,
* In all cases we just reset the chip, and any retransmission
* will be handled by higher level protocol timeouts.
*/
hide void
mcwatchdog(ifp)
struct ifnet *ifp;
{
struct mc_softc *sc = ifp->if_softc;
int temp;
printf("mcwatchdog: resetting chip\n");
temp = ifp->if_flags & IFF_UP;
mcreset(sc);
ifp->if_flags |= temp;
}
/*
* stuff packet into MACE (at splnet)
*/
integrate u_int
maceput(sc, m)
struct mc_softc *sc;
struct mbuf *m;
{
struct mbuf *n;
u_int len, totlen = 0;
u_char *buff;
buff = sc->sc_txbuf;
for (; m; m = n) {
u_char *data = mtod(m, u_char *);
len = m->m_len;
totlen += len;
bcopy(data, buff, len);
buff += len;
MFREE(m, n);
}
if (totlen > NBPG)
panic("%s: maceput: packet overflow", sc->sc_dev.dv_xname);
#if 0
if (totlen < ETHERMIN + sizeof(struct ether_header)) {
int pad = ETHERMIN + sizeof(struct ether_header) - totlen;
bzero(sc->sc_txbuf + totlen, pad);
totlen = ETHERMIN + sizeof(struct ether_header);
}
#endif
(*sc->sc_putpacket)(sc, totlen);
sc->sc_if.if_timer = 5; /* 5 seconds to watch for failing to transmit */
return (totlen);
}
void
mcintr(arg)
void *arg;
{
struct mc_softc *sc = arg;
u_int8_t ir;
ir = NIC_GET(sc, MACE_IR) & ~NIC_GET(sc, MACE_IMR);
if (ir & JAB) {
#ifdef MCDEBUG
printf("%s: jabber error\n", sc->sc_dev.dv_xname);
#endif
sc->sc_if.if_oerrors++;
}
if (ir & BABL) {
#ifdef MCDEBUG
printf("%s: babble\n", sc->sc_dev.dv_xname);
#endif
sc->sc_if.if_oerrors++;
}
if (ir & CERR) {
printf("%s: collision error\n", sc->sc_dev.dv_xname);
sc->sc_if.if_collisions++;
}
/*
* Pretend we have carrier; if we don't this will be cleared
* shortly.
*/
sc->sc_havecarrier = 1;
if (ir & XMTINT)
mc_tint(sc);
if (ir & RCVINT)
mc_rint(sc);
}
integrate void
mc_tint(sc)
struct mc_softc *sc;
{
u_int8_t xmtrc, xmtfs;
xmtrc = NIC_GET(sc, MACE_XMTRC);
xmtfs = NIC_GET(sc, MACE_XMTFS);
if ((xmtfs & XMTSV) == 0)
return;
if (xmtfs & UFLO) {
printf("%s: underflow\n", sc->sc_dev.dv_xname);
mcreset(sc);
return;
}
if (xmtfs & LCOL) {
printf("%s: late collision\n", sc->sc_dev.dv_xname);
sc->sc_if.if_oerrors++;
sc->sc_if.if_collisions++;
}
if (xmtfs & MORE)
/* Real number is unknown. */
sc->sc_if.if_collisions += 2;
else if (xmtfs & ONE)
sc->sc_if.if_collisions++;
else if (xmtfs & RTRY) {
sc->sc_if.if_collisions += 16;
sc->sc_if.if_oerrors++;
}
if (xmtfs & LCAR) {
sc->sc_havecarrier = 0;
printf("%s: lost carrier\n", sc->sc_dev.dv_xname);
sc->sc_if.if_oerrors++;
}
sc->sc_if.if_flags &= ~IFF_OACTIVE;
sc->sc_if.if_timer = 0;
mcstart(&sc->sc_if);
}
integrate void
mc_rint(sc)
struct mc_softc *sc;
{
#define rxf sc->sc_rxframe
u_int len;
len = (rxf.rx_rcvcnt | ((rxf.rx_rcvsts & 0xf) << 8)) - 4;
#ifdef MCDEBUG
if (rxf.rx_rcvsts & 0xf0)
printf("%s: rcvcnt %02x rcvsts %02x rntpc 0x%02x rcvcc 0x%02x\n",
sc->sc_dev.dv_xname, rxf.rx_rcvcnt, rxf.rx_rcvsts,
rxf.rx_rntpc, rxf.rx_rcvcc);
#endif
if (rxf.rx_rcvsts & OFLO) {
printf("%s: receive FIFO overflow\n", sc->sc_dev.dv_xname);
sc->sc_if.if_ierrors++;
return;
}
if (rxf.rx_rcvsts & CLSN)
sc->sc_if.if_collisions++;
if (rxf.rx_rcvsts & FRAM) {
#ifdef MCDEBUG
printf("%s: framing error\n", sc->sc_dev.dv_xname);
#endif
sc->sc_if.if_ierrors++;
return;
}
if (rxf.rx_rcvsts & FCS) {
#ifdef MCDEBUG
printf("%s: frame control checksum error\n", sc->sc_dev.dv_xname);
#endif
sc->sc_if.if_ierrors++;
return;
}
mace_read(sc, rxf.rx_frame, len);
#undef rxf
}
integrate void
mace_read(sc, pkt, len)
struct mc_softc *sc;
caddr_t pkt;
int len;
{
struct ifnet *ifp = &sc->sc_if;
struct ether_header *eh = (struct ether_header *)pkt;
struct mbuf *m;
if (len <= sizeof(struct ether_header) ||
len > ETHERMTU + sizeof(struct ether_header)) {
#ifdef MCDEBUG
printf("%s: invalid packet size %d; dropping\n",
sc->sc_dev.dv_xname, len);
#endif
ifp->if_ierrors++;
return;
}
#if NBPFILTER > 0
/*
* Check if there's a bpf filter listening on this interface.
* If so, hand off the raw packet to enet, then discard things
* not destined for us (but be sure to keep broadcast/multicast).
*/
if (ifp->if_bpf) {
bpf_tap(ifp->if_bpf, pkt, len);
if ((ifp->if_flags & IFF_PROMISC) != 0 &&
(eh->ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */
ETHER_CMP(eh->ether_dhost, sc->sc_enaddr))
return;
}
#endif
m = mace_get(sc, pkt, len);
if (m == NULL) {
ifp->if_ierrors++;
return;
}
ifp->if_ipackets++;
/* Pass the packet up, with the ether header sort-of removed. */
m_adj(m, sizeof(struct ether_header));
ether_input(ifp, eh, m);
}
/*
* Pull data off an interface.
* Len is length of data, with local net header stripped.
* We copy the data into mbufs. When full cluster sized units are present
* we copy into clusters.
*/
integrate struct mbuf *
mace_get(sc, pkt, totlen)
struct mc_softc *sc;
caddr_t pkt;
int totlen;
{
register struct mbuf *m;
struct mbuf *top, **mp;
int len;
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == 0)
return (0);
m->m_pkthdr.rcvif = &sc->sc_if;
m->m_pkthdr.len = totlen;
len = MHLEN;
top = 0;
mp = &top;
while (totlen > 0) {
if (top) {
MGET(m, M_DONTWAIT, MT_DATA);
if (m == 0) {
m_freem(top);
return 0;
}
len = MLEN;
}
if (totlen >= MINCLSIZE) {
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_free(m);
m_freem(top);
return 0;
}
len = MCLBYTES;
}
m->m_len = len = min(totlen, len);
bcopy(pkt, mtod(m, caddr_t), len);
pkt += len;
totlen -= len;
*mp = m;
mp = &m->m_next;
}
return (top);
}
/*
* Go through the list of multicast addresses and calculate the logical
* address filter.
*/
void
mace_calcladrf(ac, af)
struct ethercom *ac;
u_int8_t *af;
{
struct ifnet *ifp = &ac->ec_if;
struct ether_multi *enm;
register u_char *cp, c;
register u_int32_t crc;
register int i, len;
struct ether_multistep step;
/*
* Set up multicast address filter by passing all multicast addresses
* through a crc generator, and then using the high order 6 bits as an
* index into the 64 bit logical address filter. The high order bit
* selects the word, while the rest of the bits select the bit within
* the word.
*/
*((u_int32_t *)af) = *((u_int32_t *)af + 1) = 0;
ETHER_FIRST_MULTI(step, ac, enm);
while (enm != NULL) {
if (ETHER_CMP(enm->enm_addrlo, enm->enm_addrhi)) {
/*
* We must listen to a range of multicast addresses.
* For now, just accept all multicasts, rather than
* trying to set only those filter bits needed to match
* the range. (At this time, the only use of address
* ranges is for IP multicast routing, for which the
* range is big enough to require all bits set.)
*/
goto allmulti;
}
cp = enm->enm_addrlo;
crc = 0xffffffff;
for (len = sizeof(enm->enm_addrlo); --len >= 0;) {
c = *cp++;
for (i = 8; --i >= 0;) {
if ((crc & 0x01) ^ (c & 0x01)) {
crc >>= 1;
crc ^= 0xedb88320;
} else
crc >>= 1;
c >>= 1;
}
}
/* Just want the 6 most significant bits. */
crc >>= 26;
/* Set the corresponding bit in the filter. */
af[crc >> 3] |= 1 << (crc & 7);
ETHER_NEXT_MULTI(step, enm);
}
ifp->if_flags &= ~IFF_ALLMULTI;
return;
allmulti:
ifp->if_flags |= IFF_ALLMULTI;
*((u_int32_t *)af) = *((u_int32_t *)af + 1) = 0xffffffff;
}
static u_char bbr4[] = {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15};
#define bbr(v) ((bbr4[(v)&0xf] << 4) | bbr4[((v)>>4) & 0xf])
u_char
mc_get_enaddr(t, h, o, dst)
bus_space_tag_t t;
bus_space_handle_t h;
vm_offset_t o;
u_char *dst;
{
int i;
u_char b, csum;
/*
* The XOR of the 8 bytes of the ROM must be 0xff for it to be
* valid
*/
for (i = 0, csum = 0; i < 8; i++) {
b = bus_space_read_1(t, h, o+16*i);
if (i < ETHER_ADDR_LEN)
dst[i] = bbr(b);
csum ^= b;
}
return csum;
}

View File

@ -0,0 +1,406 @@
/*-
* Copyright (c) 1997 David Huang <khym@bga.com>
* All rights reserved.
*
* Portions of this code are based on code by Denton Gentry <denny1@home.com>
* and Yanagisawa Takeshi <yanagisw@aa.ap.titech.ac.jp>.
*
* 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. 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.
*
*/
/*
* Bus attachment and DMA routines for the mc driver (Centris/Quadra
* 660av and Quadra 840av onboard ethernet, based on the AMD Am79C940
* MACE ethernet chip). Also uses the PSC (Peripheral Subsystem
* Controller) for DMA to and from the MACE.
*/
#include <sys/param.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/socket.h>
#include <sys/systm.h>
#include <net/if.h>
#include <net/if_ether.h>
#include <vm/vm.h>
#include <machine/bus.h>
#include <machine/psc.h>
#include <mac68k/dev/obiovar.h>
#include <mac68k/dev/if_mcreg.h>
#include <mac68k/dev/if_mcvar.h>
#define MACE_REG_BASE 0x50F1C000
#define MACE_PROM_BASE 0x50F08000
hide int mc_obio_match __P((struct device *, struct cfdata *, void *));
hide void mc_obio_attach __P((struct device *, struct device *, void *));
hide void mc_obio_init __P((struct mc_softc *sc));
hide void mc_obio_put __P((struct mc_softc *sc, u_int len));
hide int mc_dmaintr __P((void *arg));
hide void mc_reset_rxdma __P((struct mc_softc *sc));
hide void mc_reset_rxdma_set __P((struct mc_softc *, int set));
hide void mc_reset_txdma __P((struct mc_softc *sc));
hide int mc_obio_getaddr __P((struct mc_softc *, u_int8_t *));
extern int kvtop __P((register caddr_t addr));
struct cfattach mc_obio_ca = {
sizeof(struct mc_softc), mc_obio_match, mc_obio_attach
};
hide int
mc_obio_match(parent, cf, aux)
struct device *parent;
struct cfdata *cf;
void *aux;
{
struct obio_attach_args *oa = aux;
bus_space_handle_t bsh;
int found = 0;
if (PSCBase == NULL)
return 0;
if (bus_space_map(oa->oa_tag, MACE_REG_BASE, MC_REGSIZE, 0, &bsh))
return 0;
/*
* Make sure the MACE's I/O space is readable, and if it is,
* try to read the CHIPID register. A MACE will always have
* 0x?940, where the ? depends on the chip version.
*/
if (bus_probe(oa->oa_tag, bsh, 0, 1)) {
if ((bus_space_read_1(
oa->oa_tag, bsh, MACE_REG(MACE_CHIPIDL)) == 0x40) &&
((bus_space_read_1(
oa->oa_tag, bsh, MACE_REG(MACE_CHIPIDH)) & 0xf) == 9))
found = 1;
}
bus_space_unmap(oa->oa_tag, bsh, MC_REGSIZE);
return found;
}
hide void
mc_obio_attach(parent, self, aux)
struct device *parent, *self;
void *aux;
{
struct obio_attach_args *oa = (struct obio_attach_args *)aux;
struct mc_softc *sc = (void *)self;
u_int8_t myaddr[ETHER_ADDR_LEN];
int i, noncontig = 0;
sc->sc_regt = oa->oa_tag;
sc->sc_biucc = XMTSP_64;
sc->sc_fifocc = XMTFW_16 | RCVFW_64 | XMTFWU | RCVFWU |
XMTBRST | RCVBRST;
sc->sc_plscc = PORTSEL_AUI;
if (bus_space_map(sc->sc_regt, MACE_REG_BASE, MC_REGSIZE, 0,
&sc->sc_regh)) {
printf(": failed to map space for MACE regs.\n");
return;
}
if (mc_obio_getaddr(sc, myaddr)) {
printf(": failed to get MAC address.\n");
return;
}
/* allocate memory for transmit buffer and mark it non-cacheable */
sc->sc_txbuf = malloc(NBPG, M_DEVBUF, M_WAITOK);
sc->sc_txbuf_phys = kvtop(sc->sc_txbuf);
physaccess (sc->sc_txbuf, (caddr_t)sc->sc_txbuf_phys, NBPG,
PG_V | PG_RW | PG_CI);
/*
* allocate memory for receive buffer and mark it non-cacheable
* XXX This should use the bus_dma interface, since the buffer
* needs to be physically contiguous. However, it seems that
* at least on my system, malloc() does allocate contiguous
* memory. If it's not, suggest reducing the number of buffers
* to 2, which will fit in one 4K page.
*/
sc->sc_rxbuf = malloc(MC_NPAGES * NBPG, M_DEVBUF, M_WAITOK);
sc->sc_rxbuf_phys = kvtop(sc->sc_rxbuf);
for (i = 0; i < MC_NPAGES; i++) {
int pa;
pa = kvtop(sc->sc_rxbuf + NBPG*i);
physaccess (sc->sc_rxbuf + NBPG*i, (caddr_t)pa, NBPG,
PG_V | PG_RW | PG_CI);
if (pa != sc->sc_rxbuf_phys + NBPG*i)
noncontig = 1;
}
if (noncontig) {
printf("%s: receive DMA buffer not contiguous! "
"Try compiling with \"options MC_RXDMABUFS=2\"\n",
sc->sc_dev.dv_xname);
return;
}
sc->sc_bus_init = mc_obio_init;
sc->sc_putpacket = mc_obio_put;
/* disable receive DMA */
psc_reg2(PSC_ENETRD_CTL) = 0x8800;
psc_reg2(PSC_ENETRD_CTL) = 0x1000;
psc_reg2(PSC_ENETRD_CMD + PSC_SET0) = 0x1100;
psc_reg2(PSC_ENETRD_CMD + PSC_SET1) = 0x1100;
/* disable transmit DMA */
psc_reg2(PSC_ENETWR_CTL) = 0x8800;
psc_reg2(PSC_ENETWR_CTL) = 0x1000;
psc_reg2(PSC_ENETWR_CMD + PSC_SET0) = 0x1100;
psc_reg2(PSC_ENETWR_CMD + PSC_SET1) = 0x1100;
/* install interrupt handlers */
add_psc_lev4_intr(PSCINTR_ENET_DMA, mc_dmaintr, sc);
add_psc_lev3_intr(mcintr, sc);
/* enable MACE DMA interrupts */
psc_reg1(PSC_LEV4_IER) = 0x80 | (1 << PSCINTR_ENET_DMA);
/* don't know what this does */
psc_reg2(PSC_ENETWR_CTL) = 0x9000;
psc_reg2(PSC_ENETRD_CTL) = 0x9000;
psc_reg2(PSC_ENETWR_CTL) = 0x0400;
psc_reg2(PSC_ENETRD_CTL) = 0x0400;
/* enable MACE interrupts */
psc_reg1(PSC_LEV3_IER) = 0x80 | (1 << PSCINTR_ENET);
/* mcsetup returns 1 if something fails */
if (mcsetup(sc, myaddr)) {
/* disable interrupts */
psc_reg1(PSC_LEV4_IER) = (1 << PSCINTR_ENET_DMA);
psc_reg1(PSC_LEV3_IER) = (1 << PSCINTR_ENET);
/* remove interrupt handlers */
remove_psc_lev4_intr(PSCINTR_ENET_DMA);
remove_psc_lev3_intr();
bus_space_unmap(sc->sc_regt, sc->sc_regh, MC_REGSIZE);
return;
}
}
/* Bus-specific initialization */
hide void
mc_obio_init(sc)
struct mc_softc *sc;
{
mc_reset_rxdma(sc);
mc_reset_txdma(sc);
}
hide void
mc_obio_put(sc, len)
struct mc_softc *sc;
u_int len;
{
psc_reg4(PSC_ENETWR_ADDR + sc->sc_txset) = sc->sc_txbuf_phys;
psc_reg4(PSC_ENETWR_LEN + sc->sc_txset) = len;
psc_reg2(PSC_ENETWR_CMD + sc->sc_txset) = 0x9800;
sc->sc_txset ^= 0x10;
}
/*
* Interrupt handler for the MACE DMA completion interrupts
*/
int
mc_dmaintr(arg)
void *arg;
{
struct mc_softc *sc = arg;
u_int16_t status;
u_int32_t bufsleft, which;
int head;
/*
* Not sure what this does... figure out if this interrupt is
* really ours?
*/
while ((which = psc_reg4(0x804)) != psc_reg4(0x804))
;
if ((which & 0x60000000) == 0)
return 0;
/* Get the read channel status */
status = psc_reg2(PSC_ENETRD_CTL);
if (status & 0x2000) {
/* I think this is an exceptional condition. Reset the DMA */
mc_reset_rxdma(sc);
#ifdef MCDEBUG
printf("%s: resetting receive DMA channel (status 0x%04x)\n",
sc->sc_dev.dv_xname, status);
#endif
} else if (status & 0x100) {
/* We've received some packets from the MACE */
int offset;
/* Clear the interrupt */
psc_reg2(PSC_ENETRD_CMD + sc->sc_rxset) = 0x1100;
/* See how may receive buffers are left */
bufsleft = psc_reg4(PSC_ENETRD_LEN + sc->sc_rxset);
head = MC_RXDMABUFS - bufsleft;
#if 0 /* I don't think this should ever happen */
if (head == sc->sc_tail) {
#ifdef MCDEBUG
printf("%s: head == tail: suspending DMA?\n",
sc->sc_dev.dv_xname);
#endif
psc_reg2(PSC_ENETRD_CMD + sc->sc_rxset) = 0x9000;
}
#endif
/* Loop through, processing each of the packets */
for (; sc->sc_tail < head; sc->sc_tail++) {
offset = sc->sc_tail * 0x800;
sc->sc_rxframe.rx_rcvcnt = sc->sc_rxbuf[offset];
sc->sc_rxframe.rx_rcvsts = sc->sc_rxbuf[offset+2];
sc->sc_rxframe.rx_rntpc = sc->sc_rxbuf[offset+4];
sc->sc_rxframe.rx_rcvcc = sc->sc_rxbuf[offset+6];
sc->sc_rxframe.rx_frame = sc->sc_rxbuf + offset + 16;
mc_rint(sc);
}
/*
* If we're out of buffers, reset this register set
* and switch to the other one. Otherwise, reactivate
* this set.
*/
if (bufsleft == 0) {
mc_reset_rxdma_set(sc, sc->sc_rxset);
sc->sc_rxset ^= 0x10;
} else
psc_reg2(PSC_ENETRD_CMD + sc->sc_rxset) = 0x9800;
}
/* Get the write channel status */
status = psc_reg2(PSC_ENETWR_CTL);
if (status & 0x2000) {
/* I think this is an exceptional condition. Reset the DMA */
mc_reset_txdma(sc);
#ifdef MCDEBUG
printf("%s: resetting transmit DMA channel (status 0x%04x)\n",
sc->sc_dev.dv_xname, status);
#endif
} else if (status & 0x100) {
/* Clear the interrupt and switch register sets */
psc_reg2(PSC_ENETWR_CMD + sc->sc_txseti) = 0x100;
sc->sc_txseti ^= 0x10;
}
return 1;
}
hide void
mc_reset_rxdma(sc)
struct mc_softc *sc;
{
u_int8_t maccc;
/* Disable receiver, reset the DMA channels */
maccc = NIC_GET(sc, MACE_MACCC);
NIC_PUT(sc, MACE_MACCC, maccc & ~ENRCV);
psc_reg2(PSC_ENETRD_CTL) = 0x8800;
mc_reset_rxdma_set(sc, 0);
psc_reg2(PSC_ENETRD_CTL) = 0x400;
psc_reg2(PSC_ENETRD_CTL) = 0x8800;
mc_reset_rxdma_set(sc, 0x10);
psc_reg2(PSC_ENETRD_CTL) = 0x400;
/* Reenable receiver, reenable DMA */
NIC_PUT(sc, MACE_MACCC, maccc);
sc->sc_rxset = 0;
psc_reg2(PSC_ENETRD_CMD + PSC_SET0) = 0x9800;
psc_reg2(PSC_ENETRD_CMD + PSC_SET1) = 0x9800;
}
hide void
mc_reset_rxdma_set(sc, set)
struct mc_softc *sc;
int set;
{
/* disable DMA while modifying the registers, then reenable DMA */
psc_reg2(PSC_ENETRD_CMD + set) = 0x0100;
psc_reg4(PSC_ENETRD_ADDR + set) = sc->sc_rxbuf_phys;
psc_reg4(PSC_ENETRD_LEN + set) = MC_RXDMABUFS;
psc_reg2(PSC_ENETRD_CMD + set) = 0x9800;
sc->sc_tail = 0;
}
hide void
mc_reset_txdma(sc)
struct mc_softc *sc;
{
u_int8_t maccc;
psc_reg2(PSC_ENETWR_CTL) = 0x8800;
maccc = NIC_GET(sc, MACE_MACCC);
NIC_PUT(sc, MACE_MACCC, maccc & ~ENXMT);
sc->sc_txset = sc->sc_txseti = 0;
psc_reg2(PSC_ENETWR_CTL) = 0x400;
NIC_PUT(sc, MACE_MACCC, maccc);
}
hide int
mc_obio_getaddr(sc, lladdr)
struct mc_softc *sc;
u_int8_t *lladdr;
{
bus_space_handle_t bsh;
u_char csum;
if (bus_space_map(sc->sc_regt, MACE_PROM_BASE, 8*16, 0, &bsh)) {
printf(": failed to map space to read MACE address.\n%s",
sc->sc_dev.dv_xname);
return (-1);
}
if (!bus_probe(sc->sc_regt, bsh, 0, 1)) {
bus_space_unmap(sc->sc_regt, bsh, 8*16);
return (-1);
}
csum = mc_get_enaddr(sc->sc_regt, bsh, 1, lladdr);
if (csum != 0xff)
printf(": ethernet PROM checksum failed (0x%x != 0xff)\n%s",
(int)csum, sc->sc_dev.dv_xname);
bus_space_unmap(sc->sc_regt, bsh, 8*16);
return (csum == 0xff ? 0 : -1);
}

View File

@ -0,0 +1,198 @@
/*-
* Copyright (c) 1997 David Huang <khym@bga.com>
* 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. 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.
*
*/
/*
* AMD MACE (Am79C940) register definitions
*/
#define MACE_RCVFIFO 0 /* Receive FIFO [15-00] (read only) */
#define MACE_XMTFIFO 1 /* Transmit FIFO [15-00] (write only) */
#define MACE_XMTFC 2 /* Transmit Frame Control (read/write) */
#define MACE_XMTFS 3 /* Transmit Frame Status (read only) */
#define MACE_XMTRC 4 /* Transmit Retry Count (read only) */
#define MACE_RCVFC 5 /* Receive Frame Control (read/write) */
#define MACE_RCVFS 6 /* Receive Frame Status (4 bytes) (read only) */
#define MACE_FIFOFC 7 /* FIFO Frame Count (read only) */
#define MACE_IR 8 /* Interrupt Register (read only) */
#define MACE_IMR 9 /* Interrupt Mask Register (read/write) */
#define MACE_PR 10 /* Poll Register (read only) */
#define MACE_BIUCC 11 /* BIU Configuration Control (read/write) */
#define MACE_FIFOCC 12 /* FIFO Configuration Control (read/write) */
#define MACE_MACCC 13 /* MAC Configuration Control (read/write) */
#define MACE_PLSCC 14 /* PLS Configuration Control (read/write) */
#define MACE_PHYCC 15 /* PHY Confiuration Control (read/write) */
#define MACE_CHIPIDL 16 /* Chip ID Register [07-00] (read only) */
#define MACE_CHIPIDH 17 /* Chip ID Register [15-08] (read only) */
#define MACE_IAC 18 /* Internal Address Configuration (read/write) */
/* RESERVED 19 Reserved (read/write as 0) */
#define MACE_LADRF 20 /* Logical Address Filter (8 bytes) (read/write) */
#define MACE_PADR 21 /* Physical Address (6 bytes) (read/write) */
/* RESERVED 22 Reserved (read/write as 0) */
/* RESERVED 23 Reserved (read/write as 0) */
#define MACE_MPC 24 /* Missed Packet Count (read only) */
/* RESERVED 25 Reserved (read/write as 0) */
#define MACE_RNTPC 26 /* Runt Packet Count (read only) */
#define MACE_RCVCC 27 /* Receive Collision Count (read only) */
/* RESERVED 28 Reserved (read/write as 0) */
#define MACE_UTR 29 /* User Test Register (read/write) */
#define MACE_RTR1 30 /* Reserved Test Register 1 (read/write as 0) */
#define MACE_RTR2 31 /* Reserved Test Register 2 (read/write as 0) */
#define MACE_NREGS 32
/* 2: Transmit Frame Control (XMTFC) */
#define DRTRY 0x80 /* Disable Retry */
#define DXMTFCS 0x08 /* Disable Transmit FCS */
#define APADXMT 0x01 /* Auto Pad Transmit */
/* 3: Transmit Frame Status (XMTFS) */
#define XMTSV 0x80 /* Transmit Status Valid */
#define UFLO 0x40 /* Underflow */
#define LCOL 0x20 /* Late Collision */
#define MORE 0x10 /* More than one retry needed */
#define ONE 0x08 /* Exactly one retry needed */
#define DEFER 0x04 /* Transmission deferred */
#define LCAR 0x02 /* Loss of Carrier */
#define RTRY 0x01 /* Retry Error */
/* 4: Transmit Retry Count (XMTRC) */
#define EXDEF 0x80 /* Excessive Defer */
#define XMTRC 0x0f /* Transmit Retry Count */
/* 5: Receive Frame Control (RCVFC) */
#define LLRCV 0x08 /* Low Latency Receive */
#define MR 0x04 /* Match/Reject */
#define ASTRPRCV 0x01 /* Auto Strip Receive */
/* 6: Receive Frame Status (RCVFS) */
/* 4 byte register; read 4 times to get all of the bytes */
/* Read 1: RFS0 - Receive Message Byte Count [7-0] (RCVCNT) */
/* Read 2: RFS1 - Receive Status (RCVSTS) */
#define OFLO 0x80 /* Overflow flag */
#define CLSN 0x40 /* Collision flag */
#define FRAM 0x20 /* Framing Error flag */
#define FCS 0x10 /* FCS Error flag */
#define RCVCNT 0x0f /* Receive Message Byte Count [11-8] */
/* Read 3: RFS2 - Runt Packet Count (RNTPC) [7-0] */
/* Read 4: RFS3 - Receive Collision Count (RCVCC) [7-0] */
/* 7: FIFO Frame Count (FIFOFC) */
#define RCVFC 0xf0 /* Receive Frame Count */
#define XMTFC 0x0f /* Transmit Frame Count */
/* 8: Interrupt Register (IR) */
#define JAB 0x80 /* Jabber Error */
#define BABL 0x40 /* Babble Error */
#define CERR 0x20 /* Collision Error */
#define RCVCCO 0x10 /* Receive Collision Count Overflow */
#define RNTPCO 0x08 /* Runt Packet Count Overflow */
#define MPCO 0x04 /* Missed Packet Count Overflow */
#define RCVINT 0x02 /* Receive Interrupt */
#define XMTINT 0x01 /* Transmit Interrupt */
/* 9: Interrut Mask Register (IMR) */
#define JABM 0x80 /* Jabber Error Mask */
#define BABLM 0x40 /* Babble Error Mask */
#define CERRM 0x20 /* Collision Error Mask */
#define RCVCCOM 0x10 /* Receive Collision Count Overflow Mask */
#define RNTPCOM 0x08 /* Runt Packet Count Overflow Mask */
#define MPCOM 0x04 /* Missed Packet Count Overflow Mask */
#define RCVINTM 0x02 /* Receive Interrupt Mask */
#define XMTINTM 0x01 /* Transmit Interrupt Mask */
/* 10: Poll Register (PR) */
#define XMTSV 0x80 /* Transmit Status Valid */
#define TDTREQ 0x40 /* Transmit Data Transfer Request */
#define RDTREQ 0x20 /* Receive Data Transfer Request */
/* 11: BIU Configuration Control (BIUCC) */
#define BSWP 0x40 /* Byte Swap */
#define XMTSP 0x30 /* Transmit Start Point */
#define XMTSP_4 0x00 /* 4 bytes */
#define XMTSP_16 0x10 /* 16 bytes */
#define XMTSP_64 0x20 /* 64 bytes */
#define XMTSP_112 0x30 /* 112 bytes */
#define SWRST 0x01 /* Software Reset */
/* 12: FIFO Configuration Control (FIFOCC) */
#define XMTFW 0xc0 /* Transmit FIFO Watermark */
#define XMTFW_8 0x00 /* 8 write cycles */
#define XMTFW_16 0x40 /* 16 write cycles */
#define XMTFW_32 0x80 /* 32 write cycles */
#define RCVFW 0x30 /* Receive FIFO Watermark */
#define RCVFW_16 0x00 /* 16 bytes */
#define RCVFW_32 0x10 /* 32 bytes */
#define RCVFW_64 0x20 /* 64 bytes */
#define XMTFWU 0x08 /* Transmit FIFO Watermark Update */
#define RCVFWU 0x04 /* Receive FIFO Watermark Update */
#define XMTBRST 0x02 /* Transmit Burst */
#define RCVBRST 0x01 /* Receive Burst */
/* 13: MAC Configuration (MACCC) */
#define PROM 0x80 /* Promiscuous */
#define DXMT2PD 0x40 /* Disable Transmit Two Part Deferral */
#define EMBA 0x20 /* Enable Modified Back-off Algorithm */
#define DRCVPA 0x08 /* Disable Receive Physical Address */
#define DRCVBC 0x04 /* Disable Receive Broadcast */
#define ENXMT 0x02 /* Enable Transmit */
#define ENRCV 0x01 /* Enable Receive */
/* 14: PLS Configuration Control (PLSCC) */
#define XMTSEL 0x08 /* Transmit Mode Select */
#define PORTSEL 0x06 /* Port Select */
#define PORTSEL_AUI 0x00 /* Select AUI */
#define PORTSEL_10BT 0x02 /* Select 10BASE-T */
#define PORTSEL_DAI 0x04 /* Select DAI port */
#define PORTSEL_GPSI 0x06 /* Select GPSI */
#define ENPLSIO 0x01 /* Enable PLS I/O */
/* 15: PHY Configuration (PHYCC) */
#define LNKFL 0x80 /* Link Fail */
#define DLNKTST 0x40 /* Disable Link Test */
#define REVPOL 0x20 /* Reversed Polarity */
#define DAPC 0x10 /* Disable Auto Polarity Correction */
#define LRT 0x08 /* Low Receive Threshold */
#define ASEL 0x04 /* Auto Select */
#define RWAKE 0x02 /* Remote Wake */
#define AWAKE 0x01 /* Auto Wake */
/* 18: Internal Address Configuration (IAC) */
#define ADDRCHG 0x80 /* Address Change */
#define PHYADDR 0x04 /* Physical Address Reset */
#define LOGADDR 0x02 /* Logical Address Reset */
/* 28: User Test Register (UTR) */
#define RTRE 0x80 /* Reserved Test Register Enable */
#define RTRD 0x40 /* Reserved Test Register Disable */
#define RPA 0x20 /* Run Packet Accept */
#define FCOLL 0x10 /* Force Collision */
#define RCVFCSE 0x08 /* Receive FCS Enable */
#define LOOP 0x06 /* Loopback Control */
#define LOOP_NONE 0x00 /* No Loopback */
#define LOOP_EXT 0x02 /* External Loopback */
#define LOOP_INT 0x04 /* Internal Loopback, excludes MENDEC */
#define LOOP_INT_MENDEC 0x06 /* Internal Loopback, includes MENDEC */

View File

@ -0,0 +1,91 @@
/*-
* Copyright (c) 1997 David Huang <khym@bga.com>
* 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. 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.
*
*/
#ifdef DDB
#define integrate
#define hide
#else
#define integrate static __inline
#define hide static
#endif
#define MC_REGSPACING 16
#define MC_REGSIZE MACE_NREGS * MC_REGSPACING
#define MACE_REG(x) ((x)*MC_REGSPACING)
#define NIC_GET(sc, reg) (bus_space_read_1((sc)->sc_regt, \
(sc)->sc_regh, MACE_REG(reg)))
#define NIC_PUT(sc, reg, val) (bus_space_write_1((sc)->sc_regt, \
(sc)->sc_regh, MACE_REG(reg), (val)))
#ifndef MC_RXDMABUFS
#define MC_RXDMABUFS 4
#endif
#if (MC_RXDMABUFS < 2)
#error Must have at least two buffers for DMA!
#endif
#define MC_NPAGES ((MC_RXDMABUFS * 0x800 + NBPG - 1) / NBPG)
struct mc_rxframe {
u_int8_t rx_rcvcnt;
u_int8_t rx_rcvsts;
u_int8_t rx_rntpc;
u_int8_t rx_rcvcc;
u_char *rx_frame;
};
struct mc_softc {
struct device sc_dev; /* base device glue */
struct ethercom sc_ethercom; /* Ethernet common part */
#define sc_if sc_ethercom.ec_if
struct mc_rxframe sc_rxframe;
u_int8_t sc_biucc;
u_int8_t sc_fifocc;
u_int8_t sc_plscc;
u_int8_t sc_enaddr[6];
u_int8_t sc_pad[2];
int sc_havecarrier; /* carrier status */
void (*sc_bus_init) __P((struct mc_softc *));
void (*sc_putpacket) __P((struct mc_softc *, u_int));
bus_space_tag_t sc_regt;
bus_space_handle_t sc_regh;
u_char *sc_txbuf, *sc_rxbuf;
int sc_txbuf_phys, sc_rxbuf_phys;
int sc_tail;
int sc_rxset;
int sc_txset, sc_txseti;
};
int mcsetup __P((struct mc_softc *, u_int8_t *));
void mcintr __P((void *arg));
integrate void mc_tint __P((struct mc_softc *sc));
integrate void mc_rint __P((struct mc_softc *sc));
u_char mc_get_enaddr __P((bus_space_tag_t t, bus_space_handle_t h,
vm_offset_t o, u_char *dst));

View File

@ -1,4 +1,4 @@
/* $NetBSD: intr.h,v 1.6 1997/10/10 05:54:51 scottr Exp $ */
/* $NetBSD: intr.h,v 1.7 1997/11/04 03:44:53 briggs Exp $ */
/*
* Copyright (C) 1997 Scott Reynolds
@ -79,6 +79,18 @@
#define splzs() splserial()
#define splsoft() spl1()
/*
* splnet must block hardware network interrupts
* splimp must be > spltty
*/
extern unsigned short mac68k_ttyipl;
extern unsigned short mac68k_bioipl;
extern unsigned short mac68k_netipl;
extern unsigned short mac68k_impipl;
extern unsigned short mac68k_clockipl;
extern unsigned short mac68k_statclockipl;
extern unsigned short mac68k_schedipl;
/*
* These should be used for:
* 1) ensuring mutual exclusion (why use processor level?)
@ -90,13 +102,13 @@
*/
#define splsoftclock() splsoft()
#define splsoftnet() splsoft()
#define spltty() _splraise(PSL_S|PSL_IPL1)
#define splbio() spl2()
#define splnet() spl2()
#define splimp() spl2()
#define splclock() spl2()
#define splstatclock() spl2()
#define splsched() spl3()
#define spltty() _splraise(mac68k_ttyipl)
#define splbio() _splraise(mac68k_bioipl)
#define splnet() _splraise(mac68k_netipl)
#define splimp() _splraise(mac68k_impipl)
#define splclock() _splraise(mac68k_clockipl)
#define splstatclock() _splraise(mac68k_statclockipl)
#define splsched() _splsched(mac68k_schedipl)
#define splserial() spl4()
#define splhigh() spl7()

View File

@ -0,0 +1,104 @@
/*-
* Copyright (c) 1997 David Huang <khym@bga.com>
* 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. 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.
*
*/
/*
* Some register definitions for the PSC, present only on the
* Centris/Quadra 660av and the Quadra 840av.
*/
extern volatile u_int8_t *PSCBase;
#define psc_reg1(r) (*((volatile u_int8_t *)(PSCBase+r)))
#define psc_reg2(r) (*((volatile u_int16_t *)(PSCBase+r)))
#define psc_reg4(r) (*((volatile u_int32_t *)(PSCBase+r)))
int add_psc_lev3_intr __P((void (*)(void *), void *));
int add_psc_lev4_intr __P((int, int (*)(void *), void *));
int add_psc_lev5_intr __P((int, void (*)(void *), void *));
int add_psc_lev6_intr __P((int, void (*)(void *), void *));
int remove_psc_lev3_intr __P((void));
int remove_psc_lev4_intr __P((int));
int remove_psc_lev5_intr __P((int));
int remove_psc_lev6_intr __P((int));
/*
* Reading an interrupt status register returns a mask of the
* currently interrupting devices (one bit per device). Reading an
* interrupt enable register returns a mask of the currently enabled
* devices. Writing an interrupt enable register with the MSB set
* enables the interrupts in the lower 4 bits, while writing with the
* MSB clear disables the corresponding interrupts.
* e.g. write 0x81 to enable device 0, write 0x86 to enable devices 1
* and 2, write 0x02 to disable device 1.
*
* Level 3 device 0 is MACE
* Level 4 device 0 is 3210 DSP?
* Level 4 device 1 is SCC channel A (modem port)
* Level 4 device 2 is SCC channel B (printer port)
* Level 4 device 3 is MACE DMA completion
* Level 5 device 0 is 3210 DSP?
* Level 5 device 1 is 3210 DSP?
* Level 6 device 0 is ?
* Level 6 device 1 is ?
* Level 6 device 2 is ?
*/
/* PSC interrupt registers */
#define PSC_LEV3_ISR 0x130 /* level 3 interrupt status register */
#define PSC_LEV3_IER 0x134 /* level 3 interrupt enable register */
#define PSCINTR_ENET 0 /* Ethernet interrupt */
#define PSC_LEV4_ISR 0x140 /* level 4 interrupt status register */
#define PSC_LEV4_IER 0x144 /* level 4 interrupt enable register */
#define PSCINTR_SCCA 1 /* SCC channel A interrupt */
#define PSCINTR_SCCB 2 /* SCC channel B interrupt */
#define PSCINTR_ENET_DMA 3 /* Ethernet DMA completion interrupt */
#define PSC_LEV5_ISR 0x150 /* level 5 interrupt status register */
#define PSC_LEV5_IER 0x154 /* level 5 interrupt enable register */
#define PSC_LEV6_ISR 0x160 /* level 6 interrupt status register */
#define PSC_LEV6_IER 0x164 /* level 6 interrupt enable register */
/* PSC DMA channel control registers */
#define PSC_ENETRD_CTL 0xc10 /* MACE receive DMA channel control/status */
#define PSC_ENETWR_CTL 0xc20 /* MACE transmit DMA channel control/status */
/* PSC DMA channels */
#define PSC_ENETRD_ADDR 0x1020 /* MACE receive DMA address register */
#define PSC_ENETRD_LEN 0x1024 /* MACE receive DMA buffer count */
#define PSC_ENETRD_CMD 0x1028 /* MACE receive DMA command register */
#define PSC_ENETWR_ADDR 0x1040 /* MACE transmit DMA address register */
#define PSC_ENETWR_LEN 0x1044 /* MACE transmit DMA length */
#define PSC_ENETWR_CMD 0x1048 /* MACE transmit DMA command register */
/*
* PSC DMA channels are controlled by two sets of registers (see p.29
* of the Quadra 840av and Centris 660av Developer Note). Add the
* following offsets to get the desired register set.
*/
#define PSC_SET0 0x00
#define PSC_SET1 0x10

View File

@ -1,4 +1,4 @@
/* $NetBSD: locore.s,v 1.89 1997/09/10 04:38:50 scottr Exp $ */
/* $NetBSD: locore.s,v 1.90 1997/11/04 03:44:56 briggs Exp $ */
/*
* Copyright (c) 1988 University of Utah.
@ -320,6 +320,7 @@ Lnocache0:
/* Final setup for call to main(). */
jbsr _C_LABEL(setmachdep) | Set some machine-dep stuff
jbsr _C_LABEL(via_init) | Initialize VIA hardware
jbsr _C_LABEL(psc_init) | Initialize PSC (if present)
movw #PSL_LOWIPL,sr | lower SPL ; enable interrupts
/*
@ -719,16 +720,7 @@ Lkbrkpt:
/*
* Interrupt handlers.
*
* Level 0: Spurious: ignored.
* Level 1: HIL
* Level 2:
* Level 3: Internal HP-IB
* Level 4: "Fast" HP-IBs, SCSI
* Level 5: DMA, Ethernet, Built-in RS232
* Level 6: Clock
* Level 7: Non-maskable: parity errors, RESET key
*
* ALICE: Here are our assignments:
* Most 68k-based Macintosh computers
*
* Level 0: Spurious: ignored
* Level 1: VIA1 (clock, ADB)
@ -737,9 +729,9 @@ Lkbrkpt:
* Level 4: Serial (SCC)
* Level 5:
* Level 6:
* Level 7: Non-maskable: parity errors, RESET button, FOO key
* Level 7: Non-maskable: parity errors, RESET button
*
* On the Q700, at least, in "A/UX mode," this should become:
* On the Q700, Q900 and Q950 in "A/UX mode": this should become:
*
* Level 0: Spurious: ignored
* Level 1: Software
@ -749,13 +741,20 @@ Lkbrkpt:
* Level 5: Sound
* Level 6: VIA1
* Level 7: NMIs: parity errors, RESET button, YANCC error
*
* On the 660AV and 840AV:
*
* Level 0: Spurious: ignored
* Level 1: VIA1 (clock, ADB)
* Level 2: VIA2 (NuBus, SCSI)
* Level 3: PSC device interrupt
* Level 4: PSC DMA and serial
* Level 5: ???
* Level 6: ???
* Level 7: NMIs: parity errors?, RESET button
*/
/* BARF We must re-configure this. */
ENTRY_NOPROFILE(spurintr)
ENTRY_NOPROFILE(lev3intr)
ENTRY_NOPROFILE(lev5intr)
ENTRY_NOPROFILE(lev6intr)
addql #1,_C_LABEL(intrcnt)+0
addql #1,_C_LABEL(cnt)+V_INTR
jra _ASM_LABEL(rei)
@ -785,17 +784,56 @@ ENTRY_NOPROFILE(lev2intr)
addql #1,_C_LABEL(cnt)+V_INTR
jra _ASM_LABEL(rei)
ENTRY_NOPROFILE(lev3intr)
addql #1,_C_LABEL(intrcnt)+24
clrl sp@-
moveml #0xFFFF,sp@-
movl sp, sp@-
movl _C_LABEL(lev3_intrvec),a2
jbsr a2@
addql #4,sp
moveml sp@+, #0xFFFF
addql #4,sp
addql #1,_C_LABEL(cnt)+V_INTR
jra _ASM_LABEL(rei)
ENTRY_NOPROFILE(lev4intr)
/* handle level 4 (SCC) interrupt special... */
addql #1,_C_LABEL(intrcnt)+12
clrl sp@-
moveml #0xFFFF,sp@- | save registers
clrl sp@- | push 0
jsr _C_LABEL(zshard) | call dispatch routine
addl #4,sp | throw away arg
moveml sp@+, #0xFFFF | restore registers
moveml #0xFFFF,sp@-
movl sp, sp@-
movl _C_LABEL(lev4_intrvec),a2
jbsr a2@
addql #4,sp
rte | return from exception
moveml sp@+, #0xFFFF
addql #4,sp
rte
ENTRY_NOPROFILE(lev5intr)
addql #1,_C_LABEL(intrcnt)+28
clrl sp@-
moveml #0xFFFF,sp@-
movl sp, sp@-
movl _C_LABEL(lev5_intrvec),a2
jbsr a2@
addql #4,sp
moveml sp@+, #0xFFFF
addql #4,sp
addql #1,_C_LABEL(cnt)+V_INTR
jra _ASM_LABEL(rei)
ENTRY_NOPROFILE(lev6intr)
addql #1,_C_LABEL(intrcnt)+32
clrl sp@-
moveml #0xFFFF,sp@-
movl sp, sp@-
movl _C_LABEL(lev6_intrvec),a2
jbsr a2@
addql #4,sp
moveml sp@+, #0xFFFF
addql #4,sp
addql #1,_C_LABEL(cnt)+V_INTR
jra _ASM_LABEL(rei)
ENTRY_NOPROFILE(lev7intr)
addql #1,_C_LABEL(intrcnt)+16
@ -1832,6 +1870,9 @@ GLOBAL(intrnames)
.asciz "scc"
.asciz "nmi"
.asciz "clock"
.asciz "unused1"
.asciz "unused2"
.asciz "unused3"
GLOBAL(eintrnames)
.even

View File

@ -1,4 +1,4 @@
/* $NetBSD: machdep.c,v 1.173 1997/10/27 02:49:29 briggs Exp $ */
/* $NetBSD: machdep.c,v 1.174 1997/11/04 03:45:00 briggs Exp $ */
/*-
* Copyright (c) 1996, 1997 The NetBSD Foundation, Inc.
@ -184,7 +184,7 @@ char machine[] = MACHINE; /* from <machine/param.h> */
struct mac68k_machine_S mac68k_machine;
volatile u_char *Via1Base, *Via2Base;
volatile u_char *Via1Base, *Via2Base, *PSCBase = NULL;
u_long NuBusBase = NBBASE;
u_long IOBase;
@ -244,6 +244,18 @@ int physmem = MAXMEM; /* max supported memory, changes to actual */
* during autoconfiguration or after a panic.
*/
int safepri = PSL_LOWIPL;
/*
* Some of the below are not used yet, but might be used someday on the
* Q700/900/950 where the interrupt controller may be reprogrammed to
* interrupt on different levels as listed in locore.s
*/
unsigned short mac68k_ttyipl = PSL_S | PSL_IPL1;
unsigned short mac68k_bioipl = PSL_S | PSL_IPL2;
unsigned short mac68k_netipl = PSL_S | PSL_IPL2;
unsigned short mac68k_impipl = PSL_S | PSL_IPL2;
unsigned short mac68k_clockipl = PSL_S | PSL_IPL2;
unsigned short mac68k_statclockipl = PSL_S | PSL_IPL2;
unsigned short mac68k_schedipl = PSL_S | PSL_IPL3;
/*
* Extent maps to manage all memory space, including I/O ranges. Allocate
@ -2365,6 +2377,11 @@ mac68k_set_io_offsets(base)
Via1Base = (volatile u_char *) base;
sccA = (volatile u_char *) base + 0x4000;
SCSIBase = base + 0x18000;
PSCBase = (volatile u_char *) base + 0x31000;
mac68k_bioipl = PSL_S | PSL_IPL4;
mac68k_netipl = PSL_S | PSL_IPL4;
mac68k_impipl = PSL_S | PSL_IPL4;
mac68k_statclockipl = PSL_S | PSL_IPL4;
break;
case MACH_CLASSII:
case MACH_CLASSPB:

View File

@ -0,0 +1,327 @@
/*-
* Copyright (c) 1997 David Huang <khym@bga.com>
* 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. 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.
*
*/
/*
* This handles registration/unregistration of PSC (Peripheral
* Subsystem Controller) interrupts. The PSC is used only on the
* Centris/Quadra 660av and the Quadra 840av.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include <machine/psc.h>
void psc_init __P((void));
void psc_lev3_intr __P((struct frame *));
static void psc_lev3_noint __P((void *));
int psc_lev4_intr __P((struct frame *));
static int psc_lev4_noint __P((void *));
void psc_lev5_intr __P((struct frame *));
static void psc_lev5_noint __P((void *));
void psc_lev6_intr __P((struct frame *));
static void psc_lev6_noint __P((void *));
void (*lev3_intrvec) __P((struct frame *));
int (*lev4_intrvec) __P((struct frame *));
void (*lev5_intrvec) __P((struct frame *));
void (*lev6_intrvec) __P((struct frame *));
extern void spurintr __P((struct frame *));
extern int zshard __P((void *)); /* from zs.c */
void (*psc3_ihandler) __P((void *)) = psc_lev3_noint;
void *psc3_iarg;
int (*psc4_itab[4]) __P((void *)) = {
psc_lev4_noint, /* 0 */
zshard, /* 1 */
zshard, /* 2 */
psc_lev4_noint /* 3 */
};
void *psc4_iarg[4] = {
(void *)0, (void *)1, (void *)2, (void *)3
};
void (*psc5_itab[2]) __P((void *)) = {
psc_lev5_noint, /* 0 */
psc_lev5_noint /* 1 */
};
void *psc5_iarg[2] = {
(void *)0, (void *)1
};
void (*psc6_itab[3]) __P((void *)) = {
psc_lev6_noint, /* 0 */
psc_lev6_noint, /* 1 */
psc_lev6_noint /* 2 */
};
void *psc6_iarg[3] = {
(void *)0, (void *)1, (void *)2
};
/*
* Setup the interrupt vectors and disable most of the PSC interrupts
*/
void
psc_init()
{
/*
* Only Quadra AVs have a PSC. On other machines, point the
* level 4 interrupt to zshard(), and levels 3, 5, and 6 to
* spurintr().
*/
if (current_mac_model->class == MACH_CLASSAV) {
lev3_intrvec = psc_lev3_intr;
lev4_intrvec = psc_lev4_intr;
lev5_intrvec = psc_lev5_intr;
lev6_intrvec = psc_lev6_intr;
psc_reg1(PSC_LEV3_IER) = 0x01; /* disable level 3 interrupts */
psc_reg1(PSC_LEV4_IER) = 0x09; /* disable level 4 interrupts */
psc_reg1(PSC_LEV4_IER) = 0x86; /* except for SCC */
psc_reg1(PSC_LEV5_IER) = 0x03; /* disable level 5 interrupts */
psc_reg1(PSC_LEV6_IER) = 0x07; /* disable level 6 interrupts */
} else {
lev4_intrvec = (int (*)(struct frame *))zshard;
lev3_intrvec = lev5_intrvec = lev6_intrvec = spurintr;
}
}
int
add_psc_lev3_intr(handler, arg)
void (*handler)(void *);
void *arg;
{
int s;
s = splhigh();
psc3_ihandler = handler;
psc3_iarg = arg;
splx(s);
return 1;
}
int
remove_psc_lev3_intr()
{
return add_psc_lev3_intr(psc_lev3_noint, (void *)0);
}
void
psc_lev3_intr(fp)
struct frame *fp;
{
u_int8_t intbits;
while ((intbits = psc_reg1(PSC_LEV3_ISR)) != psc_reg1(PSC_LEV3_ISR))
;
intbits &= 0x1 & psc_reg1(PSC_LEV3_IER);
if (intbits)
psc3_ihandler(psc3_iarg);
}
static void
psc_lev3_noint(arg)
void *arg;
{
printf("psc_lev3_noint\n");
}
int
psc_lev4_intr(fp)
struct frame *fp;
{
u_int8_t intbits, bitnum;
u_int mask;
while ((intbits = psc_reg1(PSC_LEV4_ISR)) != psc_reg1(PSC_LEV4_ISR))
;
intbits &= 0xf & psc_reg1(PSC_LEV4_IER);
mask = 1;
bitnum = 0;
do {
if (intbits & mask)
psc4_itab[bitnum](psc4_iarg[bitnum]);
mask <<= 1;
} while (intbits >= mask && ++bitnum);
return 0;
}
int
add_psc_lev4_intr(dev, handler, arg)
int dev;
int (*handler)(void *);
void *arg;
{
int s;
if ((dev < 0) || (dev > 3))
return 0;
s = splhigh();
psc4_itab[dev] = handler;
psc4_iarg[dev] = arg;
splx(s);
return 1;
}
int
remove_psc_lev4_intr(dev)
int dev;
{
return add_psc_lev4_intr(dev, psc_lev4_noint, (void *)dev);
}
int
psc_lev4_noint(arg)
void *arg;
{
printf("psc_lev4_noint: device %d\n", (int)arg);
return 0;
}
void
psc_lev5_intr(fp)
struct frame *fp;
{
u_int8_t intbits, bitnum;
u_int mask;
while ((intbits = psc_reg1(PSC_LEV5_ISR)) != psc_reg1(PSC_LEV5_ISR))
;
intbits &= 0x3 & psc_reg1(PSC_LEV5_IER);
mask = 1;
bitnum = 0;
do {
if (intbits & mask)
psc5_itab[bitnum](psc5_iarg[bitnum]);
mask <<= 1;
} while (intbits >= mask && ++bitnum);
}
int
add_psc_lev5_intr(dev, handler, arg)
int dev;
void (*handler)(void *);
void *arg;
{
int s;
if ((dev < 0) || (dev > 1))
return 0;
s = splhigh();
psc5_itab[dev] = handler;
psc5_iarg[dev] = arg;
splx(s);
return 1;
}
int
remove_psc_lev5_intr(dev)
int dev;
{
return add_psc_lev5_intr(dev, psc_lev5_noint, (void *)dev);
}
void
psc_lev5_noint(arg)
void *arg;
{
printf("psc_lev5_noint: device %d\n", (int)arg);
}
void
psc_lev6_intr(fp)
struct frame *fp;
{
u_int8_t intbits, bitnum;
u_int mask;
while ((intbits = psc_reg1(PSC_LEV6_ISR)) != psc_reg1(PSC_LEV6_ISR))
;
intbits &= 0x7 & psc_reg1(PSC_LEV6_IER);
mask = 1;
bitnum = 0;
do {
if (intbits & mask)
psc6_itab[bitnum](psc6_iarg[bitnum]);
mask <<= 1;
} while (intbits >= mask && ++bitnum);
}
int
add_psc_lev6_intr(dev, handler, arg)
int dev;
void (*handler)(void *);
void *arg;
{
int s;
if ((dev < 0) || (dev > 2))
return 0;
s = splhigh();
psc6_itab[dev] = handler;
psc6_iarg[dev] = arg;
splx(s);
return 1;
}
int
remove_psc_lev6_intr(dev)
int dev;
{
return add_psc_lev6_intr(dev, psc_lev6_noint, (void *)dev);
}
void
psc_lev6_noint(arg)
void *arg;
{
printf("psc_lev6_noint: device %d\n", (int)arg);
}

View File

@ -0,0 +1,406 @@
/*-
* Copyright (c) 1997 David Huang <khym@bga.com>
* All rights reserved.
*
* Portions of this code are based on code by Denton Gentry <denny1@home.com>
* and Yanagisawa Takeshi <yanagisw@aa.ap.titech.ac.jp>.
*
* 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. 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.
*
*/
/*
* Bus attachment and DMA routines for the mc driver (Centris/Quadra
* 660av and Quadra 840av onboard ethernet, based on the AMD Am79C940
* MACE ethernet chip). Also uses the PSC (Peripheral Subsystem
* Controller) for DMA to and from the MACE.
*/
#include <sys/param.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/socket.h>
#include <sys/systm.h>
#include <net/if.h>
#include <net/if_ether.h>
#include <vm/vm.h>
#include <machine/bus.h>
#include <machine/psc.h>
#include <mac68k/dev/obiovar.h>
#include <mac68k/dev/if_mcreg.h>
#include <mac68k/dev/if_mcvar.h>
#define MACE_REG_BASE 0x50F1C000
#define MACE_PROM_BASE 0x50F08000
hide int mc_obio_match __P((struct device *, struct cfdata *, void *));
hide void mc_obio_attach __P((struct device *, struct device *, void *));
hide void mc_obio_init __P((struct mc_softc *sc));
hide void mc_obio_put __P((struct mc_softc *sc, u_int len));
hide int mc_dmaintr __P((void *arg));
hide void mc_reset_rxdma __P((struct mc_softc *sc));
hide void mc_reset_rxdma_set __P((struct mc_softc *, int set));
hide void mc_reset_txdma __P((struct mc_softc *sc));
hide int mc_obio_getaddr __P((struct mc_softc *, u_int8_t *));
extern int kvtop __P((register caddr_t addr));
struct cfattach mc_obio_ca = {
sizeof(struct mc_softc), mc_obio_match, mc_obio_attach
};
hide int
mc_obio_match(parent, cf, aux)
struct device *parent;
struct cfdata *cf;
void *aux;
{
struct obio_attach_args *oa = aux;
bus_space_handle_t bsh;
int found = 0;
if (PSCBase == NULL)
return 0;
if (bus_space_map(oa->oa_tag, MACE_REG_BASE, MC_REGSIZE, 0, &bsh))
return 0;
/*
* Make sure the MACE's I/O space is readable, and if it is,
* try to read the CHIPID register. A MACE will always have
* 0x?940, where the ? depends on the chip version.
*/
if (bus_probe(oa->oa_tag, bsh, 0, 1)) {
if ((bus_space_read_1(
oa->oa_tag, bsh, MACE_REG(MACE_CHIPIDL)) == 0x40) &&
((bus_space_read_1(
oa->oa_tag, bsh, MACE_REG(MACE_CHIPIDH)) & 0xf) == 9))
found = 1;
}
bus_space_unmap(oa->oa_tag, bsh, MC_REGSIZE);
return found;
}
hide void
mc_obio_attach(parent, self, aux)
struct device *parent, *self;
void *aux;
{
struct obio_attach_args *oa = (struct obio_attach_args *)aux;
struct mc_softc *sc = (void *)self;
u_int8_t myaddr[ETHER_ADDR_LEN];
int i, noncontig = 0;
sc->sc_regt = oa->oa_tag;
sc->sc_biucc = XMTSP_64;
sc->sc_fifocc = XMTFW_16 | RCVFW_64 | XMTFWU | RCVFWU |
XMTBRST | RCVBRST;
sc->sc_plscc = PORTSEL_AUI;
if (bus_space_map(sc->sc_regt, MACE_REG_BASE, MC_REGSIZE, 0,
&sc->sc_regh)) {
printf(": failed to map space for MACE regs.\n");
return;
}
if (mc_obio_getaddr(sc, myaddr)) {
printf(": failed to get MAC address.\n");
return;
}
/* allocate memory for transmit buffer and mark it non-cacheable */
sc->sc_txbuf = malloc(NBPG, M_DEVBUF, M_WAITOK);
sc->sc_txbuf_phys = kvtop(sc->sc_txbuf);
physaccess (sc->sc_txbuf, (caddr_t)sc->sc_txbuf_phys, NBPG,
PG_V | PG_RW | PG_CI);
/*
* allocate memory for receive buffer and mark it non-cacheable
* XXX This should use the bus_dma interface, since the buffer
* needs to be physically contiguous. However, it seems that
* at least on my system, malloc() does allocate contiguous
* memory. If it's not, suggest reducing the number of buffers
* to 2, which will fit in one 4K page.
*/
sc->sc_rxbuf = malloc(MC_NPAGES * NBPG, M_DEVBUF, M_WAITOK);
sc->sc_rxbuf_phys = kvtop(sc->sc_rxbuf);
for (i = 0; i < MC_NPAGES; i++) {
int pa;
pa = kvtop(sc->sc_rxbuf + NBPG*i);
physaccess (sc->sc_rxbuf + NBPG*i, (caddr_t)pa, NBPG,
PG_V | PG_RW | PG_CI);
if (pa != sc->sc_rxbuf_phys + NBPG*i)
noncontig = 1;
}
if (noncontig) {
printf("%s: receive DMA buffer not contiguous! "
"Try compiling with \"options MC_RXDMABUFS=2\"\n",
sc->sc_dev.dv_xname);
return;
}
sc->sc_bus_init = mc_obio_init;
sc->sc_putpacket = mc_obio_put;
/* disable receive DMA */
psc_reg2(PSC_ENETRD_CTL) = 0x8800;
psc_reg2(PSC_ENETRD_CTL) = 0x1000;
psc_reg2(PSC_ENETRD_CMD + PSC_SET0) = 0x1100;
psc_reg2(PSC_ENETRD_CMD + PSC_SET1) = 0x1100;
/* disable transmit DMA */
psc_reg2(PSC_ENETWR_CTL) = 0x8800;
psc_reg2(PSC_ENETWR_CTL) = 0x1000;
psc_reg2(PSC_ENETWR_CMD + PSC_SET0) = 0x1100;
psc_reg2(PSC_ENETWR_CMD + PSC_SET1) = 0x1100;
/* install interrupt handlers */
add_psc_lev4_intr(PSCINTR_ENET_DMA, mc_dmaintr, sc);
add_psc_lev3_intr(mcintr, sc);
/* enable MACE DMA interrupts */
psc_reg1(PSC_LEV4_IER) = 0x80 | (1 << PSCINTR_ENET_DMA);
/* don't know what this does */
psc_reg2(PSC_ENETWR_CTL) = 0x9000;
psc_reg2(PSC_ENETRD_CTL) = 0x9000;
psc_reg2(PSC_ENETWR_CTL) = 0x0400;
psc_reg2(PSC_ENETRD_CTL) = 0x0400;
/* enable MACE interrupts */
psc_reg1(PSC_LEV3_IER) = 0x80 | (1 << PSCINTR_ENET);
/* mcsetup returns 1 if something fails */
if (mcsetup(sc, myaddr)) {
/* disable interrupts */
psc_reg1(PSC_LEV4_IER) = (1 << PSCINTR_ENET_DMA);
psc_reg1(PSC_LEV3_IER) = (1 << PSCINTR_ENET);
/* remove interrupt handlers */
remove_psc_lev4_intr(PSCINTR_ENET_DMA);
remove_psc_lev3_intr();
bus_space_unmap(sc->sc_regt, sc->sc_regh, MC_REGSIZE);
return;
}
}
/* Bus-specific initialization */
hide void
mc_obio_init(sc)
struct mc_softc *sc;
{
mc_reset_rxdma(sc);
mc_reset_txdma(sc);
}
hide void
mc_obio_put(sc, len)
struct mc_softc *sc;
u_int len;
{
psc_reg4(PSC_ENETWR_ADDR + sc->sc_txset) = sc->sc_txbuf_phys;
psc_reg4(PSC_ENETWR_LEN + sc->sc_txset) = len;
psc_reg2(PSC_ENETWR_CMD + sc->sc_txset) = 0x9800;
sc->sc_txset ^= 0x10;
}
/*
* Interrupt handler for the MACE DMA completion interrupts
*/
int
mc_dmaintr(arg)
void *arg;
{
struct mc_softc *sc = arg;
u_int16_t status;
u_int32_t bufsleft, which;
int head;
/*
* Not sure what this does... figure out if this interrupt is
* really ours?
*/
while ((which = psc_reg4(0x804)) != psc_reg4(0x804))
;
if ((which & 0x60000000) == 0)
return 0;
/* Get the read channel status */
status = psc_reg2(PSC_ENETRD_CTL);
if (status & 0x2000) {
/* I think this is an exceptional condition. Reset the DMA */
mc_reset_rxdma(sc);
#ifdef MCDEBUG
printf("%s: resetting receive DMA channel (status 0x%04x)\n",
sc->sc_dev.dv_xname, status);
#endif
} else if (status & 0x100) {
/* We've received some packets from the MACE */
int offset;
/* Clear the interrupt */
psc_reg2(PSC_ENETRD_CMD + sc->sc_rxset) = 0x1100;
/* See how may receive buffers are left */
bufsleft = psc_reg4(PSC_ENETRD_LEN + sc->sc_rxset);
head = MC_RXDMABUFS - bufsleft;
#if 0 /* I don't think this should ever happen */
if (head == sc->sc_tail) {
#ifdef MCDEBUG
printf("%s: head == tail: suspending DMA?\n",
sc->sc_dev.dv_xname);
#endif
psc_reg2(PSC_ENETRD_CMD + sc->sc_rxset) = 0x9000;
}
#endif
/* Loop through, processing each of the packets */
for (; sc->sc_tail < head; sc->sc_tail++) {
offset = sc->sc_tail * 0x800;
sc->sc_rxframe.rx_rcvcnt = sc->sc_rxbuf[offset];
sc->sc_rxframe.rx_rcvsts = sc->sc_rxbuf[offset+2];
sc->sc_rxframe.rx_rntpc = sc->sc_rxbuf[offset+4];
sc->sc_rxframe.rx_rcvcc = sc->sc_rxbuf[offset+6];
sc->sc_rxframe.rx_frame = sc->sc_rxbuf + offset + 16;
mc_rint(sc);
}
/*
* If we're out of buffers, reset this register set
* and switch to the other one. Otherwise, reactivate
* this set.
*/
if (bufsleft == 0) {
mc_reset_rxdma_set(sc, sc->sc_rxset);
sc->sc_rxset ^= 0x10;
} else
psc_reg2(PSC_ENETRD_CMD + sc->sc_rxset) = 0x9800;
}
/* Get the write channel status */
status = psc_reg2(PSC_ENETWR_CTL);
if (status & 0x2000) {
/* I think this is an exceptional condition. Reset the DMA */
mc_reset_txdma(sc);
#ifdef MCDEBUG
printf("%s: resetting transmit DMA channel (status 0x%04x)\n",
sc->sc_dev.dv_xname, status);
#endif
} else if (status & 0x100) {
/* Clear the interrupt and switch register sets */
psc_reg2(PSC_ENETWR_CMD + sc->sc_txseti) = 0x100;
sc->sc_txseti ^= 0x10;
}
return 1;
}
hide void
mc_reset_rxdma(sc)
struct mc_softc *sc;
{
u_int8_t maccc;
/* Disable receiver, reset the DMA channels */
maccc = NIC_GET(sc, MACE_MACCC);
NIC_PUT(sc, MACE_MACCC, maccc & ~ENRCV);
psc_reg2(PSC_ENETRD_CTL) = 0x8800;
mc_reset_rxdma_set(sc, 0);
psc_reg2(PSC_ENETRD_CTL) = 0x400;
psc_reg2(PSC_ENETRD_CTL) = 0x8800;
mc_reset_rxdma_set(sc, 0x10);
psc_reg2(PSC_ENETRD_CTL) = 0x400;
/* Reenable receiver, reenable DMA */
NIC_PUT(sc, MACE_MACCC, maccc);
sc->sc_rxset = 0;
psc_reg2(PSC_ENETRD_CMD + PSC_SET0) = 0x9800;
psc_reg2(PSC_ENETRD_CMD + PSC_SET1) = 0x9800;
}
hide void
mc_reset_rxdma_set(sc, set)
struct mc_softc *sc;
int set;
{
/* disable DMA while modifying the registers, then reenable DMA */
psc_reg2(PSC_ENETRD_CMD + set) = 0x0100;
psc_reg4(PSC_ENETRD_ADDR + set) = sc->sc_rxbuf_phys;
psc_reg4(PSC_ENETRD_LEN + set) = MC_RXDMABUFS;
psc_reg2(PSC_ENETRD_CMD + set) = 0x9800;
sc->sc_tail = 0;
}
hide void
mc_reset_txdma(sc)
struct mc_softc *sc;
{
u_int8_t maccc;
psc_reg2(PSC_ENETWR_CTL) = 0x8800;
maccc = NIC_GET(sc, MACE_MACCC);
NIC_PUT(sc, MACE_MACCC, maccc & ~ENXMT);
sc->sc_txset = sc->sc_txseti = 0;
psc_reg2(PSC_ENETWR_CTL) = 0x400;
NIC_PUT(sc, MACE_MACCC, maccc);
}
hide int
mc_obio_getaddr(sc, lladdr)
struct mc_softc *sc;
u_int8_t *lladdr;
{
bus_space_handle_t bsh;
u_char csum;
if (bus_space_map(sc->sc_regt, MACE_PROM_BASE, 8*16, 0, &bsh)) {
printf(": failed to map space to read MACE address.\n%s",
sc->sc_dev.dv_xname);
return (-1);
}
if (!bus_probe(sc->sc_regt, bsh, 0, 1)) {
bus_space_unmap(sc->sc_regt, bsh, 8*16);
return (-1);
}
csum = mc_get_enaddr(sc->sc_regt, bsh, 1, lladdr);
if (csum != 0xff)
printf(": ethernet PROM checksum failed (0x%x != 0xff)\n%s",
(int)csum, sc->sc_dev.dv_xname);
bus_space_unmap(sc->sc_regt, bsh, 8*16);
return (csum == 0xff ? 0 : -1);
}