NetBSD/sys/arch/macppc/dev/if_gm.c
sommerfeld 851de295eb Change pci_intr_map to get interrupt source information from a "struct
pci_attach_args *" instead of from four separate parameters which in
all cases were extracted from the same "struct pci_attach_args".

This both simplifies the driver api, and allows for alternate PCI
interrupt mapping schemes, such as one using the tables described in
the Intel Multiprocessor Spec which describe interrupt wirings for
devices behind pci-pci bridges based on the device's location rather
the bridge's location.

Tested on alpha and i386; welcome to 1.5Q
2000-12-28 22:59:06 +00:00

992 lines
21 KiB
C

/* $NetBSD: if_gm.c,v 1.10 2000/12/28 22:59:09 sommerfeld Exp $ */
/*-
* Copyright (c) 2000 Tsubai Masanari. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "opt_inet.h"
#include "opt_ns.h"
#include "bpfilter.h"
#include <sys/param.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/systm.h>
#include <sys/callout.h>
#include <uvm/uvm_extern.h>
#include <net/if.h>
#include <net/if_ether.h>
#include <net/if_media.h>
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
#ifdef INET
#include <netinet/in.h>
#include <netinet/if_inarp.h>
#endif
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcidevs.h>
#include <dev/ofw/openfirm.h>
#include <macppc/dev/if_gmreg.h>
#include <machine/pio.h>
#define NTXBUF 4
#define NRXBUF 32
struct gmac_softc {
struct device sc_dev;
struct ethercom sc_ethercom;
vaddr_t sc_reg;
struct gmac_dma *sc_txlist;
struct gmac_dma *sc_rxlist;
int sc_txnext;
int sc_rxlast;
caddr_t sc_txbuf[NTXBUF];
caddr_t sc_rxbuf[NRXBUF];
struct mii_data sc_mii;
struct callout sc_tick_ch;
char sc_laddr[6];
};
#define sc_if sc_ethercom.ec_if
int gmac_match __P((struct device *, struct cfdata *, void *));
void gmac_attach __P((struct device *, struct device *, void *));
static __inline u_int gmac_read_reg __P((struct gmac_softc *, int));
static __inline void gmac_write_reg __P((struct gmac_softc *, int, u_int));
static __inline void gmac_start_txdma __P((struct gmac_softc *));
static __inline void gmac_start_rxdma __P((struct gmac_softc *));
static __inline void gmac_stop_txdma __P((struct gmac_softc *));
static __inline void gmac_stop_rxdma __P((struct gmac_softc *));
int gmac_intr __P((void *));
void gmac_tint __P((struct gmac_softc *));
void gmac_rint __P((struct gmac_softc *));
struct mbuf * gmac_get __P((struct gmac_softc *, caddr_t, int));
void gmac_start __P((struct ifnet *));
int gmac_put __P((struct gmac_softc *, caddr_t, struct mbuf *));
void gmac_stop __P((struct gmac_softc *));
void gmac_reset __P((struct gmac_softc *));
void gmac_init __P((struct gmac_softc *));
void gmac_init_mac __P((struct gmac_softc *));
void gmac_setladrf __P((struct gmac_softc *));
int gmac_ioctl __P((struct ifnet *, u_long, caddr_t));
void gmac_watchdog __P((struct ifnet *));
int gmac_mediachange __P((struct ifnet *));
void gmac_mediastatus __P((struct ifnet *, struct ifmediareq *));
int gmac_mii_readreg __P((struct device *, int, int));
void gmac_mii_writereg __P((struct device *, int, int, int));
void gmac_mii_statchg __P((struct device *));
void gmac_mii_tick __P((void *));
struct cfattach gm_ca = {
sizeof(struct gmac_softc), gmac_match, gmac_attach
};
int
gmac_match(parent, match, aux)
struct device *parent;
struct cfdata *match;
void *aux;
{
struct pci_attach_args *pa = aux;
if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_APPLE &&
PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_GMAC)
return 1;
return 0;
}
void
gmac_attach(parent, self, aux)
struct device *parent, *self;
void *aux;
{
struct gmac_softc *sc = (void *)self;
struct pci_attach_args *pa = aux;
struct ifnet *ifp = &sc->sc_if;
struct mii_data *mii = &sc->sc_mii;
pci_intr_handle_t ih;
const char *intrstr = NULL;
int node, i;
char *p;
struct gmac_dma *dp;
u_int32_t reg[10];
u_char laddr[6];
node = pcidev_to_ofdev(pa->pa_pc, pa->pa_tag);
if (node == 0) {
printf(": cannot find gmac node\n");
return;
}
OF_getprop(node, "local-mac-address", laddr, sizeof laddr);
OF_getprop(node, "assigned-addresses", reg, sizeof reg);
bcopy(laddr, sc->sc_laddr, sizeof laddr);
sc->sc_reg = reg[2];
if (pci_intr_map(pa, &ih)) {
printf(": unable to map interrupt\n");
return;
}
intrstr = pci_intr_string(pa->pa_pc, ih);
if (pci_intr_establish(pa->pa_pc, ih, IPL_NET, gmac_intr, sc) == NULL) {
printf(": unable to establish interrupt");
if (intrstr)
printf(" at %s", intrstr);
printf("\n");
return;
}
/* Setup packet buffers and dma descriptors. */
p = malloc((NRXBUF + NTXBUF) * 2048 + 3 * 0x800, M_DEVBUF, M_NOWAIT);
if (p == NULL) {
printf(": cannot malloc buffers\n");
return;
}
p = (void *)roundup((vaddr_t)p, 0x800);
bzero(p, 2048 * (NRXBUF + NTXBUF) + 2 * 0x800);
sc->sc_rxlist = (void *)p;
p += 0x800;
sc->sc_txlist = (void *)p;
p += 0x800;
dp = sc->sc_rxlist;
for (i = 0; i < NRXBUF; i++) {
sc->sc_rxbuf[i] = p;
dp->address = htole32(vtophys((vaddr_t)p));
dp->cmd = htole32(GMAC_OWN);
dp++;
p += 2048;
}
dp = sc->sc_txlist;
for (i = 0; i < NTXBUF; i++) {
sc->sc_txbuf[i] = p;
dp->address = htole32(vtophys((vaddr_t)p));
dp++;
p += 2048;
}
printf(": Ethernet address %s\n", ether_sprintf(laddr));
printf("%s: interrupting at %s\n", sc->sc_dev.dv_xname, intrstr);
callout_init(&sc->sc_tick_ch);
gmac_reset(sc);
gmac_init_mac(sc);
bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
ifp->if_softc = sc;
ifp->if_ioctl = gmac_ioctl;
ifp->if_start = gmac_start;
ifp->if_watchdog = gmac_watchdog;
ifp->if_flags =
IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST;
mii->mii_ifp = ifp;
mii->mii_readreg = gmac_mii_readreg;
mii->mii_writereg = gmac_mii_writereg;
mii->mii_statchg = gmac_mii_statchg;
ifmedia_init(&mii->mii_media, 0, gmac_mediachange, gmac_mediastatus);
mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0);
/* Choose a default media. */
if (LIST_FIRST(&mii->mii_phys) == NULL) {
ifmedia_add(&mii->mii_media, IFM_ETHER|IFM_NONE, 0, NULL);
ifmedia_set(&mii->mii_media, IFM_ETHER|IFM_NONE);
} else
ifmedia_set(&mii->mii_media, IFM_ETHER|IFM_AUTO);
if_attach(ifp);
ether_ifattach(ifp, laddr);
}
u_int
gmac_read_reg(sc, reg)
struct gmac_softc *sc;
int reg;
{
return in32rb(sc->sc_reg + reg);
}
void
gmac_write_reg(sc, reg, val)
struct gmac_softc *sc;
int reg;
u_int val;
{
out32rb(sc->sc_reg + reg, val);
}
void
gmac_start_txdma(sc)
struct gmac_softc *sc;
{
u_int x;
x = gmac_read_reg(sc, GMAC_TXDMACONFIG);
x |= 1;
gmac_write_reg(sc, GMAC_TXDMACONFIG, x);
x = gmac_read_reg(sc, GMAC_TXMACCONFIG);
x |= 1;
gmac_write_reg(sc, GMAC_TXMACCONFIG, x);
}
void
gmac_start_rxdma(sc)
struct gmac_softc *sc;
{
u_int x;
x = gmac_read_reg(sc, GMAC_RXDMACONFIG);
x |= 1;
gmac_write_reg(sc, GMAC_RXDMACONFIG, x);
x = gmac_read_reg(sc, GMAC_RXMACCONFIG);
x |= 1;
gmac_write_reg(sc, GMAC_RXMACCONFIG, x);
}
void
gmac_stop_txdma(sc)
struct gmac_softc *sc;
{
u_int x;
x = gmac_read_reg(sc, GMAC_TXDMACONFIG);
x &= ~1;
gmac_write_reg(sc, GMAC_TXDMACONFIG, x);
x = gmac_read_reg(sc, GMAC_TXMACCONFIG);
x &= ~1;
gmac_write_reg(sc, GMAC_TXMACCONFIG, x);
}
void
gmac_stop_rxdma(sc)
struct gmac_softc *sc;
{
u_int x;
x = gmac_read_reg(sc, GMAC_RXDMACONFIG);
x &= ~1;
gmac_write_reg(sc, GMAC_RXDMACONFIG, x);
x = gmac_read_reg(sc, GMAC_RXMACCONFIG);
x &= ~1;
gmac_write_reg(sc, GMAC_RXMACCONFIG, x);
}
int
gmac_intr(v)
void *v;
{
struct gmac_softc *sc = v;
u_int status;
status = gmac_read_reg(sc, GMAC_STATUS) & 0xff;
if (status == 0)
return 0;
if (status & GMAC_INT_RXDONE)
gmac_rint(sc);
if (status & GMAC_INT_TXEMPTY)
gmac_tint(sc);
return 1;
}
void
gmac_tint(sc)
struct gmac_softc *sc;
{
struct ifnet *ifp = &sc->sc_if;
ifp->if_flags &= ~IFF_OACTIVE;
ifp->if_timer = 0;
gmac_start(ifp);
}
void
gmac_rint(sc)
struct gmac_softc *sc;
{
struct ifnet *ifp = &sc->sc_if;
volatile struct gmac_dma *dp;
struct mbuf *m;
int i, len;
u_int cmd;
for (i = sc->sc_rxlast;; i++) {
if (i == NRXBUF)
i = 0;
dp = &sc->sc_rxlist[i];
cmd = le32toh(dp->cmd);
if (cmd & GMAC_OWN)
break;
len = (cmd >> 16) & GMAC_LEN_MASK;
len -= 4; /* CRC */
if (le32toh(dp->cmd_hi) & 0x40000000) {
ifp->if_ierrors++;
goto next;
}
m = gmac_get(sc, sc->sc_rxbuf[i], len);
if (m == NULL) {
ifp->if_ierrors++;
goto next;
}
#if NBPFILTER > 0
/*
* Check if there's a BPF listener on this interface.
* If so, hand off the raw packet to BPF.
*/
if (ifp->if_bpf)
bpf_tap(ifp->if_bpf, sc->sc_rxbuf[i], len);
#endif
(*ifp->if_input)(ifp, m);
ifp->if_ipackets++;
next:
dp->cmd_hi = 0;
__asm __volatile ("sync");
dp->cmd = htole32(GMAC_OWN);
}
sc->sc_rxlast = i;
}
struct mbuf *
gmac_get(sc, pkt, totlen)
struct gmac_softc *sc;
caddr_t pkt;
int totlen;
{
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;
}
void
gmac_start(ifp)
struct ifnet *ifp;
{
struct gmac_softc *sc = ifp->if_softc;
struct mbuf *m;
caddr_t buff;
int i, tlen;
volatile struct gmac_dma *dp;
if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
return;
for (;;) {
if (ifp->if_flags & IFF_OACTIVE)
break;
IF_DEQUEUE(&ifp->if_snd, m);
if (m == 0)
break;
/* 5 seconds to watch for failing to transmit */
ifp->if_timer = 5;
ifp->if_opackets++; /* # of pkts */
i = sc->sc_txnext;
buff = sc->sc_txbuf[i];
tlen = gmac_put(sc, buff, m);
dp = &sc->sc_txlist[i];
dp->cmd_hi = 0;
dp->address_hi = 0;
dp->cmd = htole32(tlen | GMAC_OWN | GMAC_SOP);
i++;
if (i == NTXBUF)
i = 0;
__asm __volatile ("sync");
gmac_write_reg(sc, GMAC_TXDMAKICK, i);
sc->sc_txnext = i;
#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_tap(ifp->if_bpf, buff, tlen);
#endif
i++;
if (i == NTXBUF)
i = 0;
if (i == gmac_read_reg(sc, GMAC_TXDMACOMPLETE)) {
ifp->if_flags |= IFF_OACTIVE;
break;
}
}
}
int
gmac_put(sc, buff, m)
struct gmac_softc *sc;
caddr_t buff;
struct mbuf *m;
{
struct mbuf *n;
int len, tlen = 0;
for (; m; m = n) {
len = m->m_len;
if (len == 0) {
MFREE(m, n);
continue;
}
bcopy(mtod(m, caddr_t), buff, len);
buff += len;
tlen += len;
MFREE(m, n);
}
if (tlen > 2048)
panic("%s: gmac_put packet overflow", sc->sc_dev.dv_xname);
return tlen;
}
void
gmac_reset(sc)
struct gmac_softc *sc;
{
int i, s;
s = splnet();
gmac_stop_txdma(sc);
gmac_stop_rxdma(sc);
gmac_write_reg(sc, GMAC_SOFTWARERESET, 3);
for (i = 10; i > 0; i--) {
delay(300000); /* XXX long delay */
if ((gmac_read_reg(sc, GMAC_SOFTWARERESET) & 3) == 0)
break;
}
if (i == 0)
printf("%s: reset timeout\n", sc->sc_dev.dv_xname);
sc->sc_txnext = 0;
sc->sc_rxlast = 0;
for (i = 0; i < NRXBUF; i++)
sc->sc_rxlist[i].cmd = htole32(GMAC_OWN);
__asm __volatile ("sync");
gmac_write_reg(sc, GMAC_TXDMADESCBASEHI, 0);
gmac_write_reg(sc, GMAC_TXDMADESCBASELO,
vtophys((vaddr_t)sc->sc_txlist));
gmac_write_reg(sc, GMAC_RXDMADESCBASEHI, 0);
gmac_write_reg(sc, GMAC_RXDMADESCBASELO,
vtophys((vaddr_t)sc->sc_rxlist));
gmac_write_reg(sc, GMAC_RXDMAKICK, NRXBUF);
splx(s);
}
void
gmac_stop(sc)
struct gmac_softc *sc;
{
struct ifnet *ifp = &sc->sc_if;
int s;
s = splnet();
callout_stop(&sc->sc_tick_ch);
mii_down(&sc->sc_mii);
gmac_stop_txdma(sc);
gmac_stop_rxdma(sc);
gmac_write_reg(sc, GMAC_INTMASK, 0xffffffff);
ifp->if_flags &= ~(IFF_UP | IFF_RUNNING);
ifp->if_timer = 0;
splx(s);
}
void
gmac_init_mac(sc)
struct gmac_softc *sc;
{
int i, tb;
char *laddr = sc->sc_laddr;
__asm ("mftb %0" : "=r"(tb));
gmac_write_reg(sc, GMAC_RANDOMSEED, tb);
/* init-mii */
gmac_write_reg(sc, GMAC_DATAPATHMODE, 4);
gmac_mii_writereg(&sc->sc_dev, 0, 0, 0x1000);
gmac_write_reg(sc, GMAC_TXDMACONFIG, 0xffc00);
gmac_write_reg(sc, GMAC_RXDMACONFIG, 0);
gmac_write_reg(sc, GMAC_MACPAUSE, 0x1bf0);
gmac_write_reg(sc, GMAC_INTERPACKETGAP0, 0);
gmac_write_reg(sc, GMAC_INTERPACKETGAP1, 8);
gmac_write_reg(sc, GMAC_INTERPACKETGAP2, 4);
gmac_write_reg(sc, GMAC_MINFRAMESIZE, ETHER_MIN_LEN);
gmac_write_reg(sc, GMAC_MAXFRAMESIZE, ETHER_MAX_LEN);
gmac_write_reg(sc, GMAC_PASIZE, 7);
gmac_write_reg(sc, GMAC_JAMSIZE, 4);
gmac_write_reg(sc, GMAC_ATTEMPTLIMIT,0x10);
gmac_write_reg(sc, GMAC_MACCNTLTYPE, 0x8808);
gmac_write_reg(sc, GMAC_MACADDRESS0, (laddr[4] << 8) | laddr[5]);
gmac_write_reg(sc, GMAC_MACADDRESS1, (laddr[2] << 8) | laddr[3]);
gmac_write_reg(sc, GMAC_MACADDRESS2, (laddr[0] << 8) | laddr[1]);
gmac_write_reg(sc, GMAC_MACADDRESS3, 0);
gmac_write_reg(sc, GMAC_MACADDRESS4, 0);
gmac_write_reg(sc, GMAC_MACADDRESS5, 0);
gmac_write_reg(sc, GMAC_MACADDRESS6, 1);
gmac_write_reg(sc, GMAC_MACADDRESS7, 0xc200);
gmac_write_reg(sc, GMAC_MACADDRESS8, 0x0180);
gmac_write_reg(sc, GMAC_MACADDRFILT0, 0);
gmac_write_reg(sc, GMAC_MACADDRFILT1, 0);
gmac_write_reg(sc, GMAC_MACADDRFILT2, 0);
gmac_write_reg(sc, GMAC_MACADDRFILT2_1MASK, 0);
gmac_write_reg(sc, GMAC_MACADDRFILT0MASK, 0);
for (i = 0; i < 0x6c; i += 4)
gmac_write_reg(sc, GMAC_HASHTABLE0 + i, 0);
gmac_write_reg(sc, GMAC_SLOTTIME, 0x40);
if (IFM_OPTIONS(sc->sc_mii.mii_media_active) & IFM_FDX) {
gmac_write_reg(sc, GMAC_TXMACCONFIG, 6);
gmac_write_reg(sc, GMAC_XIFCONFIG, 1);
} else {
gmac_write_reg(sc, GMAC_TXMACCONFIG, 0);
gmac_write_reg(sc, GMAC_XIFCONFIG, 5);
}
if (0) /* g-bit? */
gmac_write_reg(sc, GMAC_MACCTRLCONFIG, 3);
else
gmac_write_reg(sc, GMAC_MACCTRLCONFIG, 0);
}
void
gmac_setladrf(sc)
struct gmac_softc *sc;
{
struct ifnet *ifp = &sc->sc_if;
struct ether_multi *enm;
struct ether_multistep step;
struct ethercom *ec = &sc->sc_ethercom;
u_int32_t crc;
u_int32_t hash[16];
u_int v;
int i;
/* Clear hash table */
for (i = 0; i < 16; i++)
hash[i] = 0;
/* Get current RX configuration */
v = gmac_read_reg(sc, GMAC_RXMACCONFIG);
if ((ifp->if_flags & IFF_PROMISC) != 0) {
/* Turn on promiscuous mode; turn off the hash filter */
v |= GMAC_RXMAC_PR;
v &= ~GMAC_RXMAC_HEN;
ifp->if_flags |= IFF_ALLMULTI;
goto chipit;
}
/* Turn off promiscuous mode; turn on the hash filter */
v &= ~GMAC_RXMAC_PR;
v |= GMAC_RXMAC_HEN;
/*
* Set up multicast address filter by passing all multicast addresses
* through a crc generator, and then using the high order 8 bits as an
* index into the 256 bit logical address filter. The high order bit
* selects the word, while the rest of the bits select the bit within
* the word.
*/
ETHER_FIRST_MULTI(step, ec, enm);
while (enm != NULL) {
if (bcmp(enm->enm_addrlo, enm->enm_addrhi, 6)) {
/*
* 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.)
*/
for (i = 0; i < 16; i++)
hash[i] = 0xffff;
ifp->if_flags |= IFF_ALLMULTI;
goto chipit;
}
crc = ether_crc32_le(enm->enm_addrlo, ETHER_ADDR_LEN);
/* Just want the 8 most significant bits. */
crc >>= 24;
/* Set the corresponding bit in the filter. */
hash[crc >> 4] |= 1 << (crc & 0xf);
ETHER_NEXT_MULTI(step, enm);
}
ifp->if_flags &= ~IFF_ALLMULTI;
chipit:
/* Now load the hash table into the chip */
for (i = 0; i < 16; i++)
gmac_write_reg(sc, GMAC_HASHTABLE0 + i * 4, hash[i]);
gmac_write_reg(sc, GMAC_RXMACCONFIG, v);
}
void
gmac_init(sc)
struct gmac_softc *sc;
{
struct ifnet *ifp = &sc->sc_if;
gmac_stop_txdma(sc);
gmac_stop_rxdma(sc);
gmac_init_mac(sc);
gmac_setladrf(sc);
gmac_start_txdma(sc);
gmac_start_rxdma(sc);
gmac_write_reg(sc, GMAC_INTMASK, ~(GMAC_INT_TXEMPTY | GMAC_INT_RXDONE));
ifp->if_flags |= IFF_RUNNING;
ifp->if_flags &= ~IFF_OACTIVE;
ifp->if_timer = 0;
callout_reset(&sc->sc_tick_ch, 1, gmac_mii_tick, sc);
gmac_start(ifp);
}
int
gmac_ioctl(ifp, cmd, data)
struct ifnet *ifp;
u_long cmd;
caddr_t data;
{
struct gmac_softc *sc = ifp->if_softc;
struct ifaddr *ifa = (struct ifaddr *)data;
struct ifreq *ifr = (struct ifreq *)data;
int s, error = 0;
s = splnet();
switch (cmd) {
case SIOCSIFADDR:
ifp->if_flags |= IFF_UP;
switch (ifa->ifa_addr->sa_family) {
#ifdef INET
case AF_INET:
gmac_init(sc);
arp_ifinit(ifp, ifa);
break;
#endif
#ifdef NS
case AF_NS:
{
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. */
gmac_init(sc);
break;
}
#endif
default:
gmac_init(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.
*/
gmac_stop(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.
*/
gmac_init(sc);
} else {
/*
* Reset the interface to pick up changes in any other
* flags that affect hardware registers.
*/
gmac_reset(sc);
gmac_init(sc);
}
#ifdef GMAC_DEBUG
if (ifp->if_flags & IFF_DEBUG)
sc->sc_flags |= GMAC_DEBUGFLAG;
#endif
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
error = (cmd == SIOCADDMULTI) ?
ether_addmulti(ifr, &sc->sc_ethercom) :
ether_delmulti(ifr, &sc->sc_ethercom);
if (error == ENETRESET) {
/*
* Multicast list has changed; set the hardware filter
* accordingly.
*/
gmac_init(sc);
/* gmac_setladrf(sc); */
error = 0;
}
break;
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd);
break;
default:
error = EINVAL;
}
splx(s);
return error;
}
void
gmac_watchdog(ifp)
struct ifnet *ifp;
{
struct gmac_softc *sc = ifp->if_softc;
printf("%s: device timeout\n", ifp->if_xname);
ifp->if_oerrors++;
gmac_reset(sc);
gmac_init(sc);
}
int
gmac_mediachange(ifp)
struct ifnet *ifp;
{
struct gmac_softc *sc = ifp->if_softc;
return mii_mediachg(&sc->sc_mii);
}
void
gmac_mediastatus(ifp, ifmr)
struct ifnet *ifp;
struct ifmediareq *ifmr;
{
struct gmac_softc *sc = ifp->if_softc;
mii_pollstat(&sc->sc_mii);
ifmr->ifm_status = sc->sc_mii.mii_media_status;
ifmr->ifm_active = sc->sc_mii.mii_media_active;
}
int
gmac_mii_readreg(dev, phy, reg)
struct device *dev;
int phy, reg;
{
struct gmac_softc *sc = (void *)dev;
int i;
gmac_write_reg(sc, GMAC_MIFFRAMEOUTPUT,
0x60020000 | (phy << 23) | (reg << 18));
for (i = 1000; i >= 0; i -= 10) {
if (gmac_read_reg(sc, GMAC_MIFFRAMEOUTPUT) & 0x10000)
break;
delay(10);
}
if (i < 0) {
printf("%s: gmac_mii_readreg: timeout\n", sc->sc_dev.dv_xname);
return 0;
}
return gmac_read_reg(sc, GMAC_MIFFRAMEOUTPUT) & 0xffff;
}
void
gmac_mii_writereg(dev, phy, reg, val)
struct device *dev;
int phy, reg, val;
{
struct gmac_softc *sc = (void *)dev;
int i;
gmac_write_reg(sc, GMAC_MIFFRAMEOUTPUT,
0x50020000 | (phy << 23) | (reg << 18) | (val & 0xffff));
for (i = 1000; i >= 0; i -= 10) {
if (gmac_read_reg(sc, GMAC_MIFFRAMEOUTPUT) & 0x10000)
break;
delay(10);
}
if (i < 0)
printf("%s: gmac_mii_writereg: timeout\n", sc->sc_dev.dv_xname);
}
void
gmac_mii_statchg(dev)
struct device *dev;
{
struct gmac_softc *sc = (void *)dev;
gmac_stop_txdma(sc);
gmac_stop_rxdma(sc);
if (IFM_OPTIONS(sc->sc_mii.mii_media_active) & IFM_FDX) {
gmac_write_reg(sc, GMAC_TXMACCONFIG, 6);
gmac_write_reg(sc, GMAC_XIFCONFIG, 1);
} else {
gmac_write_reg(sc, GMAC_TXMACCONFIG, 0);
gmac_write_reg(sc, GMAC_XIFCONFIG, 5);
}
if (0) /* g-bit? */
gmac_write_reg(sc, GMAC_MACCTRLCONFIG, 3);
else
gmac_write_reg(sc, GMAC_MACCTRLCONFIG, 0);
gmac_start_txdma(sc);
gmac_start_rxdma(sc);
}
void
gmac_mii_tick(v)
void *v;
{
struct gmac_softc *sc = v;
int s;
s = splnet();
mii_tick(&sc->sc_mii);
splx(s);
callout_reset(&sc->sc_tick_ch, hz, gmac_mii_tick, sc);
}