#include "bpfilter.h"
#include "vlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sockio.h>
#include <sys/mbuf.h>
#include <sys/device.h>
#include <net/if.h>
#include <net/if_media.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
#include <uvm/uvm_extern.h>
#define VTOPHYS(v) vtophys((vaddr_t)(v))
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>
#include <dev/mii/miivar.h>
#define NGE_USEIOSPACE
#include <dev/pci/if_ngereg.h>
int nge_probe(struct device *, void *, void *);
void nge_attach(struct device *, struct device *, void *);
int nge_newbuf(struct nge_softc *, struct nge_desc *,
struct mbuf *);
int nge_encap(struct nge_softc *, struct mbuf *, u_int32_t *);
void nge_rxeof(struct nge_softc *);
void nge_txeof(struct nge_softc *);
int nge_intr(void *);
void nge_tick(void *);
void nge_start(struct ifnet *);
int nge_ioctl(struct ifnet *, u_long, caddr_t);
void nge_init(void *);
void nge_stop(struct nge_softc *);
void nge_watchdog(struct ifnet *);
int nge_ifmedia_mii_upd(struct ifnet *);
void nge_ifmedia_mii_sts(struct ifnet *, struct ifmediareq *);
int nge_ifmedia_tbi_upd(struct ifnet *);
void nge_ifmedia_tbi_sts(struct ifnet *, struct ifmediareq *);
void nge_delay(struct nge_softc *);
void nge_eeprom_idle(struct nge_softc *);
void nge_eeprom_putbyte(struct nge_softc *, int);
void nge_eeprom_getword(struct nge_softc *, int, u_int16_t *);
void nge_read_eeprom(struct nge_softc *, caddr_t, int, int, int);
void nge_mii_sync(struct nge_softc *);
void nge_mii_send(struct nge_softc *, u_int32_t, int);
int nge_mii_readreg(struct nge_softc *, struct nge_mii_frame *);
int nge_mii_writereg(struct nge_softc *, struct nge_mii_frame *);
int nge_miibus_readreg(struct device *, int, int);
void nge_miibus_writereg(struct device *, int, int, int);
void nge_miibus_statchg(struct device *);
void nge_setmulti(struct nge_softc *);
void nge_reset(struct nge_softc *);
int nge_list_rx_init(struct nge_softc *);
int nge_list_tx_init(struct nge_softc *);
#ifdef NGE_USEIOSPACE
#define NGE_RES SYS_RES_IOPORT
#define NGE_RID NGE_PCI_LOIO
#else
#define NGE_RES SYS_RES_MEMORY
#define NGE_RID NGE_PCI_LOMEM
#endif
#ifdef NGE_DEBUG
#define DPRINTF(x) if (ngedebug) printf x
#define DPRINTFN(n,x) if (ngedebug >= (n)) printf x
int ngedebug = 0;
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
#define NGE_SETBIT(sc, reg, x) \
CSR_WRITE_4(sc, reg, \
CSR_READ_4(sc, reg) | (x))
#define NGE_CLRBIT(sc, reg, x) \
CSR_WRITE_4(sc, reg, \
CSR_READ_4(sc, reg) & ~(x))
#define SIO_SET(x) \
CSR_WRITE_4(sc, NGE_MEAR, CSR_READ_4(sc, NGE_MEAR) | (x))
#define SIO_CLR(x) \
CSR_WRITE_4(sc, NGE_MEAR, CSR_READ_4(sc, NGE_MEAR) & ~(x))
void
nge_delay(struct nge_softc *sc)
{
int idx;
for (idx = (300 / 33) + 1; idx > 0; idx--)
CSR_READ_4(sc, NGE_CSR);
}
void
nge_eeprom_idle(struct nge_softc *sc)
{
int i;
SIO_SET(NGE_MEAR_EE_CSEL);
nge_delay(sc);
SIO_SET(NGE_MEAR_EE_CLK);
nge_delay(sc);
for (i = 0; i < 25; i++) {
SIO_CLR(NGE_MEAR_EE_CLK);
nge_delay(sc);
SIO_SET(NGE_MEAR_EE_CLK);
nge_delay(sc);
}
SIO_CLR(NGE_MEAR_EE_CLK);
nge_delay(sc);
SIO_CLR(NGE_MEAR_EE_CSEL);
nge_delay(sc);
CSR_WRITE_4(sc, NGE_MEAR, 0x00000000);
}
void
nge_eeprom_putbyte(struct nge_softc *sc, int addr)
{
int d, i;
d = addr | NGE_EECMD_READ;
for (i = 0x400; i; i >>= 1) {
if (d & i) {
SIO_SET(NGE_MEAR_EE_DIN);
} else {
SIO_CLR(NGE_MEAR_EE_DIN);
}
nge_delay(sc);
SIO_SET(NGE_MEAR_EE_CLK);
nge_delay(sc);
SIO_CLR(NGE_MEAR_EE_CLK);
nge_delay(sc);
}
}
void
nge_eeprom_getword(struct nge_softc *sc, int addr, u_int16_t *dest)
{
int i;
u_int16_t word = 0;
nge_eeprom_idle(sc);
nge_delay(sc);
SIO_CLR(NGE_MEAR_EE_CLK);
nge_delay(sc);
SIO_SET(NGE_MEAR_EE_CSEL);
nge_delay(sc);
nge_eeprom_putbyte(sc, addr);
for (i = 0x8000; i; i >>= 1) {
SIO_SET(NGE_MEAR_EE_CLK);
nge_delay(sc);
if (CSR_READ_4(sc, NGE_MEAR) & NGE_MEAR_EE_DOUT)
word |= i;
nge_delay(sc);
SIO_CLR(NGE_MEAR_EE_CLK);
nge_delay(sc);
}
nge_eeprom_idle(sc);
*dest = word;
}
void
nge_read_eeprom(struct nge_softc *sc, caddr_t dest, int off, int cnt, int swap)
{
int i;
u_int16_t word = 0, *ptr;
for (i = 0; i < cnt; i++) {
nge_eeprom_getword(sc, off + i, &word);
ptr = (u_int16_t *)(dest + (i * 2));
if (swap)
*ptr = ntohs(word);
else
*ptr = word;
}
}
void
nge_mii_sync(struct nge_softc *sc)
{
int i;
SIO_SET(NGE_MEAR_MII_DIR|NGE_MEAR_MII_DATA);
for (i = 0; i < 32; i++) {
SIO_SET(NGE_MEAR_MII_CLK);
DELAY(1);
SIO_CLR(NGE_MEAR_MII_CLK);
DELAY(1);
}
}
void
nge_mii_send(struct nge_softc *sc, u_int32_t bits, int cnt)
{
int i;
SIO_CLR(NGE_MEAR_MII_CLK);
for (i = (0x1 << (cnt - 1)); i; i >>= 1) {
if (bits & i) {
SIO_SET(NGE_MEAR_MII_DATA);
} else {
SIO_CLR(NGE_MEAR_MII_DATA);
}
DELAY(1);
SIO_CLR(NGE_MEAR_MII_CLK);
DELAY(1);
SIO_SET(NGE_MEAR_MII_CLK);
}
}
int
nge_mii_readreg(struct nge_softc *sc, struct nge_mii_frame *frame)
{
int i, ack, s;
s = splnet();
frame->mii_stdelim = NGE_MII_STARTDELIM;
frame->mii_opcode = NGE_MII_READOP;
frame->mii_turnaround = 0;
frame->mii_data = 0;
CSR_WRITE_4(sc, NGE_MEAR, 0);
SIO_SET(NGE_MEAR_MII_DIR);
nge_mii_sync(sc);
nge_mii_send(sc, frame->mii_stdelim, 2);
nge_mii_send(sc, frame->mii_opcode, 2);
nge_mii_send(sc, frame->mii_phyaddr, 5);
nge_mii_send(sc, frame->mii_regaddr, 5);
SIO_CLR((NGE_MEAR_MII_CLK|NGE_MEAR_MII_DATA));
DELAY(1);
SIO_SET(NGE_MEAR_MII_CLK);
DELAY(1);
SIO_CLR(NGE_MEAR_MII_DIR);
SIO_CLR(NGE_MEAR_MII_CLK);
DELAY(1);
ack = CSR_READ_4(sc, NGE_MEAR) & NGE_MEAR_MII_DATA;
SIO_SET(NGE_MEAR_MII_CLK);
DELAY(1);
if (ack) {
for(i = 0; i < 16; i++) {
SIO_CLR(NGE_MEAR_MII_CLK);
DELAY(1);
SIO_SET(NGE_MEAR_MII_CLK);
DELAY(1);
}
goto fail;
}
for (i = 0x8000; i; i >>= 1) {
SIO_CLR(NGE_MEAR_MII_CLK);
DELAY(1);
if (!ack) {
if (CSR_READ_4(sc, NGE_MEAR) & NGE_MEAR_MII_DATA)
frame->mii_data |= i;
DELAY(1);
}
SIO_SET(NGE_MEAR_MII_CLK);
DELAY(1);
}
fail:
SIO_CLR(NGE_MEAR_MII_CLK);
DELAY(1);
SIO_SET(NGE_MEAR_MII_CLK);
DELAY(1);
splx(s);
if (ack)
return(1);
return(0);
}
int
nge_mii_writereg(struct nge_softc *sc, struct nge_mii_frame *frame)
{
int s;
s = splnet();
frame->mii_stdelim = NGE_MII_STARTDELIM;
frame->mii_opcode = NGE_MII_WRITEOP;
frame->mii_turnaround = NGE_MII_TURNAROUND;
SIO_SET(NGE_MEAR_MII_DIR);
nge_mii_sync(sc);
nge_mii_send(sc, frame->mii_stdelim, 2);
nge_mii_send(sc, frame->mii_opcode, 2);
nge_mii_send(sc, frame->mii_phyaddr, 5);
nge_mii_send(sc, frame->mii_regaddr, 5);
nge_mii_send(sc, frame->mii_turnaround, 2);
nge_mii_send(sc, frame->mii_data, 16);
SIO_SET(NGE_MEAR_MII_CLK);
DELAY(1);
SIO_CLR(NGE_MEAR_MII_CLK);
DELAY(1);
SIO_CLR(NGE_MEAR_MII_DIR);
splx(s);
return(0);
}
int
nge_miibus_readreg(struct device *dev, int phy, int reg)
{
struct nge_softc *sc = (struct nge_softc *)dev;
struct nge_mii_frame frame;
DPRINTFN(9, ("%s: nge_miibus_readreg\n", sc->sc_dv.dv_xname));
bzero(&frame, sizeof(frame));
frame.mii_phyaddr = phy;
frame.mii_regaddr = reg;
nge_mii_readreg(sc, &frame);
return(frame.mii_data);
}
void
nge_miibus_writereg(struct device *dev, int phy, int reg, int data)
{
struct nge_softc *sc = (struct nge_softc *)dev;
struct nge_mii_frame frame;
DPRINTFN(9, ("%s: nge_miibus_writereg\n", sc->sc_dv.dv_xname));
bzero(&frame, sizeof(frame));
frame.mii_phyaddr = phy;
frame.mii_regaddr = reg;
frame.mii_data = data;
nge_mii_writereg(sc, &frame);
}
void
nge_miibus_statchg(struct device *dev)
{
struct nge_softc *sc = (struct nge_softc *)dev;
struct mii_data *mii = &sc->nge_mii;
u_int32_t txcfg, rxcfg;
txcfg = CSR_READ_4(sc, NGE_TX_CFG);
rxcfg = CSR_READ_4(sc, NGE_RX_CFG);
DPRINTFN(4, ("%s: nge_miibus_statchg txcfg=%#x, rxcfg=%#x\n",
sc->sc_dv.dv_xname, txcfg, rxcfg));
if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) {
txcfg |= (NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR);
rxcfg |= (NGE_RXCFG_RX_FDX);
} else {
txcfg &= ~(NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR);
rxcfg &= ~(NGE_RXCFG_RX_FDX);
}
txcfg |= NGE_TXCFG_AUTOPAD;
CSR_WRITE_4(sc, NGE_TX_CFG, txcfg);
CSR_WRITE_4(sc, NGE_RX_CFG, rxcfg);
if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T)
NGE_SETBIT(sc, NGE_CFG, NGE_CFG_MODE_1000);
else
NGE_CLRBIT(sc, NGE_CFG, NGE_CFG_MODE_1000);
}
void
nge_setmulti(struct nge_softc *sc)
{
struct arpcom *ac = &sc->arpcom;
struct ifnet *ifp = &ac->ac_if;
struct ether_multi *enm;
struct ether_multistep step;
u_int32_t h = 0, i, filtsave;
int bit, index;
if (ac->ac_multirangecnt > 0)
ifp->if_flags |= IFF_ALLMULTI;
if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
NGE_CLRBIT(sc, NGE_RXFILT_CTL,
NGE_RXFILTCTL_MCHASH|NGE_RXFILTCTL_UCHASH);
NGE_SETBIT(sc, NGE_RXFILT_CTL, NGE_RXFILTCTL_ALLMULTI);
return;
}
NGE_SETBIT(sc, NGE_RXFILT_CTL, NGE_RXFILTCTL_MCHASH);
NGE_CLRBIT(sc, NGE_RXFILT_CTL,
NGE_RXFILTCTL_ALLMULTI|NGE_RXFILTCTL_UCHASH);
filtsave = CSR_READ_4(sc, NGE_RXFILT_CTL);
for (i = 0; i < NGE_MCAST_FILTER_LEN; i += 2) {
CSR_WRITE_4(sc, NGE_RXFILT_CTL, NGE_FILTADDR_MCAST_LO + i);
CSR_WRITE_4(sc, NGE_RXFILT_DATA, 0);
}
ETHER_FIRST_MULTI(step, ac, enm);
while (enm != NULL) {
h = (ether_crc32_be(enm->enm_addrlo, ETHER_ADDR_LEN) >> 21) &
0x00000FFF;
index = (h >> 4) & 0x7F;
bit = h & 0xF;
CSR_WRITE_4(sc, NGE_RXFILT_CTL,
NGE_FILTADDR_MCAST_LO + (index * 2));
NGE_SETBIT(sc, NGE_RXFILT_DATA, (1 << bit));
ETHER_NEXT_MULTI(step, enm);
}
CSR_WRITE_4(sc, NGE_RXFILT_CTL, filtsave);
}
void
nge_reset(struct nge_softc *sc)
{
int i;
NGE_SETBIT(sc, NGE_CSR, NGE_CSR_RESET);
for (i = 0; i < NGE_TIMEOUT; i++) {
if (!(CSR_READ_4(sc, NGE_CSR) & NGE_CSR_RESET))
break;
}
if (i == NGE_TIMEOUT)
printf("%s: reset never completed\n", sc->sc_dv.dv_xname);
DELAY(1000);
CSR_WRITE_4(sc, NGE_CLKRUN, NGE_CLKRUN_PMESTS);
CSR_WRITE_4(sc, NGE_CLKRUN, 0);
}
int
nge_probe(struct device *parent, void *match, void *aux)
{
struct pci_attach_args *pa = (struct pci_attach_args *)aux;
if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_NS &&
PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_NS_DP83820)
return (1);
return (0);
}
void
nge_attach(struct device *parent, struct device *self, void *aux)
{
struct nge_softc *sc = (struct nge_softc *)self;
struct pci_attach_args *pa = aux;
pci_chipset_tag_t pc = pa->pa_pc;
pci_intr_handle_t ih;
const char *intrstr = NULL;
bus_size_t size;
bus_dma_segment_t seg;
bus_dmamap_t dmamap;
int rseg;
u_char eaddr[ETHER_ADDR_LEN];
#ifndef NGE_USEIOSPACE
pcireg_t memtype;
#endif
struct ifnet *ifp;
caddr_t kva;
pci_set_powerstate(pa->pa_pc, pa->pa_tag, PCI_PMCSR_STATE_D0);
DPRINTFN(5, ("%s: map control/status regs\n", sc->sc_dv.dv_xname));
#ifdef NGE_USEIOSPACE
DPRINTFN(5, ("%s: pci_mapreg_map\n", sc->sc_dv.dv_xname));
if (pci_mapreg_map(pa, NGE_PCI_LOIO, PCI_MAPREG_TYPE_IO, 0,
&sc->nge_btag, &sc->nge_bhandle, NULL, &size, 0)) {
printf(": can't map i/o space\n");
return;
}
#else
DPRINTFN(5, ("%s: pci_mapreg_map\n", sc->sc_dv.dv_xname));
memtype = pci_mapreg_type(pc, pa->pa_tag, NGE_PCI_LOMEM);
if (pci_mapreg_map(pa, NGE_PCI_LOMEM, memtype, 0, &sc->nge_btag,
&sc->nge_bhandle, NULL, &size, 0)) {
printf(": can't map mem space\n");
return;
}
#endif
CSR_WRITE_4(sc, NGE_IER, 0);
DPRINTFN(5, ("%s: pci_intr_map\n", sc->sc_dv.dv_xname));
if (pci_intr_map(pa, &ih)) {
printf(": couldn't map interrupt\n");
goto fail_1;
}
DPRINTFN(5, ("%s: pci_intr_string\n", sc->sc_dv.dv_xname));
intrstr = pci_intr_string(pc, ih);
DPRINTFN(5, ("%s: pci_intr_establish\n", sc->sc_dv.dv_xname));
sc->nge_intrhand = pci_intr_establish(pc, ih, IPL_NET, nge_intr, sc,
sc->sc_dv.dv_xname);
if (sc->nge_intrhand == NULL) {
printf(": couldn't establish interrupt");
if (intrstr != NULL)
printf(" at %s", intrstr);
printf("\n");
goto fail_1;
}
printf(": %s", intrstr);
DPRINTFN(5, ("%s: nge_reset\n", sc->sc_dv.dv_xname));
nge_reset(sc);
DPRINTFN(5, ("%s: nge_read_eeprom\n", sc->sc_dv.dv_xname));
nge_read_eeprom(sc, (caddr_t)&eaddr[4], NGE_EE_NODEADDR, 1, 0);
nge_read_eeprom(sc, (caddr_t)&eaddr[2], NGE_EE_NODEADDR + 1, 1, 0);
nge_read_eeprom(sc, (caddr_t)&eaddr[0], NGE_EE_NODEADDR + 2, 1, 0);
printf(", address %s\n", ether_sprintf(eaddr));
bcopy(eaddr, &sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
sc->sc_dmatag = pa->pa_dmat;
DPRINTFN(5, ("%s: bus_dmamem_alloc\n", sc->sc_dv.dv_xname));
if (bus_dmamem_alloc(sc->sc_dmatag, sizeof(struct nge_list_data),
PAGE_SIZE, 0, &seg, 1, &rseg, BUS_DMA_NOWAIT |
BUS_DMA_ZERO)) {
printf("%s: can't alloc rx buffers\n", sc->sc_dv.dv_xname);
goto fail_2;
}
DPRINTFN(5, ("%s: bus_dmamem_map\n", sc->sc_dv.dv_xname));
if (bus_dmamem_map(sc->sc_dmatag, &seg, rseg,
sizeof(struct nge_list_data), &kva,
BUS_DMA_NOWAIT)) {
printf("%s: can't map dma buffers (%zd bytes)\n",
sc->sc_dv.dv_xname, sizeof(struct nge_list_data));
goto fail_3;
}
DPRINTFN(5, ("%s: bus_dmamap_create\n", sc->sc_dv.dv_xname));
if (bus_dmamap_create(sc->sc_dmatag, sizeof(struct nge_list_data), 1,
sizeof(struct nge_list_data), 0,
BUS_DMA_NOWAIT, &dmamap)) {
printf("%s: can't create dma map\n", sc->sc_dv.dv_xname);
goto fail_4;
}
DPRINTFN(5, ("%s: bus_dmamap_load\n", sc->sc_dv.dv_xname));
if (bus_dmamap_load(sc->sc_dmatag, dmamap, kva,
sizeof(struct nge_list_data), NULL,
BUS_DMA_NOWAIT)) {
goto fail_5;
}
DPRINTFN(5, ("%s: bzero\n", sc->sc_dv.dv_xname));
sc->nge_ldata = (struct nge_list_data *)kva;
ifp = &sc->arpcom.ac_if;
ifp->if_softc = sc;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
ifp->if_ioctl = nge_ioctl;
ifp->if_start = nge_start;
ifp->if_watchdog = nge_watchdog;
ifp->if_hardmtu = NGE_JUMBO_MTU;
ifq_init_maxlen(&ifp->if_snd, NGE_TX_LIST_CNT - 1);
DPRINTFN(5, ("%s: bcopy\n", sc->sc_dv.dv_xname));
bcopy(sc->sc_dv.dv_xname, ifp->if_xname, IFNAMSIZ);
ifp->if_capabilities = IFCAP_VLAN_MTU;
#if NVLAN > 0
ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
#endif
DPRINTFN(5, ("%s: mii setup\n", sc->sc_dv.dv_xname));
if (CSR_READ_4(sc, NGE_CFG) & NGE_CFG_TBI_EN) {
DPRINTFN(5, ("%s: TBI mode\n", sc->sc_dv.dv_xname));
sc->nge_tbi = 1;
ifmedia_init(&sc->nge_ifmedia, 0, nge_ifmedia_tbi_upd,
nge_ifmedia_tbi_sts);
ifmedia_add(&sc->nge_ifmedia, IFM_ETHER|IFM_NONE, 0, NULL),
ifmedia_add(&sc->nge_ifmedia, IFM_ETHER|IFM_1000_SX, 0, NULL);
ifmedia_add(&sc->nge_ifmedia, IFM_ETHER|IFM_1000_SX|IFM_FDX,
0, NULL);
ifmedia_add(&sc->nge_ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL);
ifmedia_set(&sc->nge_ifmedia, IFM_ETHER|IFM_AUTO);
CSR_WRITE_4(sc, NGE_GPIO, CSR_READ_4(sc, NGE_GPIO)
| NGE_GPIO_GP4_OUT
| NGE_GPIO_GP1_OUTENB | NGE_GPIO_GP2_OUTENB
| NGE_GPIO_GP3_OUTENB | NGE_GPIO_GP4_OUTENB
| NGE_GPIO_GP5_OUTENB);
NGE_SETBIT(sc, NGE_CFG, NGE_CFG_MODE_1000);
} else {
sc->nge_mii.mii_ifp = ifp;
sc->nge_mii.mii_readreg = nge_miibus_readreg;
sc->nge_mii.mii_writereg = nge_miibus_writereg;
sc->nge_mii.mii_statchg = nge_miibus_statchg;
ifmedia_init(&sc->nge_mii.mii_media, 0, nge_ifmedia_mii_upd,
nge_ifmedia_mii_sts);
mii_attach(&sc->sc_dv, &sc->nge_mii, 0xffffffff, MII_PHY_ANY,
MII_OFFSET_ANY, 0);
if (LIST_FIRST(&sc->nge_mii.mii_phys) == NULL) {
printf("%s: no PHY found!\n", sc->sc_dv.dv_xname);
ifmedia_add(&sc->nge_mii.mii_media,
IFM_ETHER|IFM_MANUAL, 0, NULL);
ifmedia_set(&sc->nge_mii.mii_media,
IFM_ETHER|IFM_MANUAL);
}
else
ifmedia_set(&sc->nge_mii.mii_media,
IFM_ETHER|IFM_AUTO);
}
DPRINTFN(5, ("%s: if_attach\n", sc->sc_dv.dv_xname));
if_attach(ifp);
DPRINTFN(5, ("%s: ether_ifattach\n", sc->sc_dv.dv_xname));
ether_ifattach(ifp);
DPRINTFN(5, ("%s: timeout_set\n", sc->sc_dv.dv_xname));
timeout_set(&sc->nge_timeout, nge_tick, sc);
timeout_add_sec(&sc->nge_timeout, 1);
return;
fail_5:
bus_dmamap_destroy(sc->sc_dmatag, dmamap);
fail_4:
bus_dmamem_unmap(sc->sc_dmatag, kva,
sizeof(struct nge_list_data));
fail_3:
bus_dmamem_free(sc->sc_dmatag, &seg, rseg);
fail_2:
pci_intr_disestablish(pc, sc->nge_intrhand);
fail_1:
bus_space_unmap(sc->nge_btag, sc->nge_bhandle, size);
}
int
nge_list_tx_init(struct nge_softc *sc)
{
struct nge_list_data *ld;
struct nge_ring_data *cd;
int i;
cd = &sc->nge_cdata;
ld = sc->nge_ldata;
for (i = 0; i < NGE_TX_LIST_CNT; i++) {
if (i == (NGE_TX_LIST_CNT - 1)) {
ld->nge_tx_list[i].nge_nextdesc =
&ld->nge_tx_list[0];
ld->nge_tx_list[i].nge_next =
VTOPHYS(&ld->nge_tx_list[0]);
} else {
ld->nge_tx_list[i].nge_nextdesc =
&ld->nge_tx_list[i + 1];
ld->nge_tx_list[i].nge_next =
VTOPHYS(&ld->nge_tx_list[i + 1]);
}
ld->nge_tx_list[i].nge_mbuf = NULL;
ld->nge_tx_list[i].nge_ptr = 0;
ld->nge_tx_list[i].nge_ctl = 0;
}
cd->nge_tx_prod = cd->nge_tx_cons = cd->nge_tx_cnt = 0;
return(0);
}
int
nge_list_rx_init(struct nge_softc *sc)
{
struct nge_list_data *ld;
struct nge_ring_data *cd;
int i;
ld = sc->nge_ldata;
cd = &sc->nge_cdata;
for (i = 0; i < NGE_RX_LIST_CNT; i++) {
if (nge_newbuf(sc, &ld->nge_rx_list[i], NULL) == ENOBUFS)
return(ENOBUFS);
if (i == (NGE_RX_LIST_CNT - 1)) {
ld->nge_rx_list[i].nge_nextdesc =
&ld->nge_rx_list[0];
ld->nge_rx_list[i].nge_next =
VTOPHYS(&ld->nge_rx_list[0]);
} else {
ld->nge_rx_list[i].nge_nextdesc =
&ld->nge_rx_list[i + 1];
ld->nge_rx_list[i].nge_next =
VTOPHYS(&ld->nge_rx_list[i + 1]);
}
}
cd->nge_rx_prod = 0;
return(0);
}
int
nge_newbuf(struct nge_softc *sc, struct nge_desc *c, struct mbuf *m)
{
struct mbuf *m_new = NULL;
if (m == NULL) {
m_new = MCLGETL(NULL, M_DONTWAIT, NGE_MCLBYTES);
if (m_new == NULL)
return (ENOBUFS);
} else {
m_new = m;
m_new->m_data = m_new->m_ext.ext_buf;
}
m_new->m_len = m_new->m_pkthdr.len = NGE_MCLBYTES;
m_adj(m_new, sizeof(u_int64_t));
c->nge_mbuf = m_new;
c->nge_ptr = VTOPHYS(mtod(m_new, caddr_t));
DPRINTFN(7,("%s: c->nge_ptr=%#x\n", sc->sc_dv.dv_xname,
c->nge_ptr));
c->nge_ctl = m_new->m_len;
c->nge_extsts = 0;
return(0);
}
void
nge_rxeof(struct nge_softc *sc)
{
struct mbuf_list ml = MBUF_LIST_INITIALIZER();
struct mbuf *m;
struct ifnet *ifp;
struct nge_desc *cur_rx;
int i, total_len = 0;
u_int32_t rxstat;
ifp = &sc->arpcom.ac_if;
i = sc->nge_cdata.nge_rx_prod;
while (NGE_OWNDESC(&sc->nge_ldata->nge_rx_list[i])) {
struct mbuf *m0 = NULL;
u_int32_t extsts;
cur_rx = &sc->nge_ldata->nge_rx_list[i];
rxstat = cur_rx->nge_rxstat;
extsts = cur_rx->nge_extsts;
m = cur_rx->nge_mbuf;
cur_rx->nge_mbuf = NULL;
total_len = NGE_RXBYTES(cur_rx);
NGE_INC(i, NGE_RX_LIST_CNT);
if (!(rxstat & NGE_CMDSTS_PKT_OK)) {
#if NVLAN > 0
if ((rxstat & NGE_RXSTAT_RUNT) &&
total_len >= (ETHER_MIN_LEN - ETHER_CRC_LEN -
ETHER_VLAN_ENCAP_LEN)) {
} else {
#endif
ifp->if_ierrors++;
nge_newbuf(sc, cur_rx, m);
continue;
#if NVLAN > 0
}
#endif
}
#ifndef __STRICT_ALIGNMENT
if (nge_newbuf(sc, cur_rx, NULL) == ENOBUFS) {
#endif
m0 = m_devget(mtod(m, char *), total_len, ETHER_ALIGN);
nge_newbuf(sc, cur_rx, m);
if (m0 == NULL) {
ifp->if_ierrors++;
continue;
}
m_adj(m0, ETHER_ALIGN);
m = m0;
#ifndef __STRICT_ALIGNMENT
} else {
m->m_pkthdr.len = m->m_len = total_len;
}
#endif
#if NVLAN > 0
if (extsts & NGE_RXEXTSTS_VLANPKT) {
m->m_pkthdr.ether_vtag =
ntohs(extsts & NGE_RXEXTSTS_VTCI);
m->m_flags |= M_VLANTAG;
}
#endif
if (extsts & NGE_RXEXTSTS_IPPKT) {
if (!(extsts & NGE_RXEXTSTS_IPCSUMERR))
m->m_pkthdr.csum_flags |= M_IPV4_CSUM_IN_OK;
if ((extsts & NGE_RXEXTSTS_TCPPKT) &&
(!(extsts & NGE_RXEXTSTS_TCPCSUMERR)))
m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_OK;
else if ((extsts & NGE_RXEXTSTS_UDPPKT) &&
(!(extsts & NGE_RXEXTSTS_UDPCSUMERR)))
m->m_pkthdr.csum_flags |= M_UDP_CSUM_IN_OK;
}
ml_enqueue(&ml, m);
}
if_input(ifp, &ml);
sc->nge_cdata.nge_rx_prod = i;
}
void
nge_txeof(struct nge_softc *sc)
{
struct nge_desc *cur_tx;
struct ifnet *ifp;
u_int32_t idx;
ifp = &sc->arpcom.ac_if;
idx = sc->nge_cdata.nge_tx_cons;
while (idx != sc->nge_cdata.nge_tx_prod) {
cur_tx = &sc->nge_ldata->nge_tx_list[idx];
if (NGE_OWNDESC(cur_tx))
break;
if (cur_tx->nge_ctl & NGE_CMDSTS_MORE) {
sc->nge_cdata.nge_tx_cnt--;
NGE_INC(idx, NGE_TX_LIST_CNT);
continue;
}
if (!(cur_tx->nge_ctl & NGE_CMDSTS_PKT_OK)) {
ifp->if_oerrors++;
if (cur_tx->nge_txstat & NGE_TXSTAT_EXCESSCOLLS)
ifp->if_collisions++;
if (cur_tx->nge_txstat & NGE_TXSTAT_OUTOFWINCOLL)
ifp->if_collisions++;
}
ifp->if_collisions +=
(cur_tx->nge_txstat & NGE_TXSTAT_COLLCNT) >> 16;
if (cur_tx->nge_mbuf != NULL) {
m_freem(cur_tx->nge_mbuf);
cur_tx->nge_mbuf = NULL;
ifq_clr_oactive(&ifp->if_snd);
}
sc->nge_cdata.nge_tx_cnt--;
NGE_INC(idx, NGE_TX_LIST_CNT);
}
sc->nge_cdata.nge_tx_cons = idx;
if (idx == sc->nge_cdata.nge_tx_prod)
ifp->if_timer = 0;
}
void
nge_tick(void *xsc)
{
struct nge_softc *sc = xsc;
struct mii_data *mii = &sc->nge_mii;
struct ifnet *ifp = &sc->arpcom.ac_if;
int s;
s = splnet();
DPRINTFN(10, ("%s: nge_tick: link=%d\n", sc->sc_dv.dv_xname,
sc->nge_link));
timeout_add_sec(&sc->nge_timeout, 1);
if (sc->nge_link) {
splx(s);
return;
}
if (sc->nge_tbi) {
if (IFM_SUBTYPE(sc->nge_ifmedia.ifm_cur->ifm_media)
== IFM_AUTO) {
u_int32_t bmsr, anlpar, txcfg, rxcfg;
bmsr = CSR_READ_4(sc, NGE_TBI_BMSR);
DPRINTFN(2, ("%s: nge_tick: bmsr=%#x\n",
sc->sc_dv.dv_xname, bmsr));
if (!(bmsr & NGE_TBIBMSR_ANEG_DONE)) {
CSR_WRITE_4(sc, NGE_TBI_BMCR, 0);
splx(s);
return;
}
anlpar = CSR_READ_4(sc, NGE_TBI_ANLPAR);
txcfg = CSR_READ_4(sc, NGE_TX_CFG);
rxcfg = CSR_READ_4(sc, NGE_RX_CFG);
DPRINTFN(2, ("%s: nge_tick: anlpar=%#x, txcfg=%#x, "
"rxcfg=%#x\n", sc->sc_dv.dv_xname, anlpar,
txcfg, rxcfg));
if (anlpar == 0 || anlpar & NGE_TBIANAR_FDX) {
txcfg |= (NGE_TXCFG_IGN_HBEAT|
NGE_TXCFG_IGN_CARR);
rxcfg |= NGE_RXCFG_RX_FDX;
} else {
txcfg &= ~(NGE_TXCFG_IGN_HBEAT|
NGE_TXCFG_IGN_CARR);
rxcfg &= ~(NGE_RXCFG_RX_FDX);
}
txcfg |= NGE_TXCFG_AUTOPAD;
CSR_WRITE_4(sc, NGE_TX_CFG, txcfg);
CSR_WRITE_4(sc, NGE_RX_CFG, rxcfg);
}
DPRINTF(("%s: gigabit link up\n", sc->sc_dv.dv_xname));
sc->nge_link++;
if (!ifq_empty(&ifp->if_snd))
nge_start(ifp);
} else {
mii_tick(mii);
if (mii->mii_media_status & IFM_ACTIVE &&
IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
sc->nge_link++;
if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T)
DPRINTF(("%s: gigabit link up\n",
sc->sc_dv.dv_xname));
if (!ifq_empty(&ifp->if_snd))
nge_start(ifp);
}
}
splx(s);
}
int
nge_intr(void *arg)
{
struct nge_softc *sc;
struct ifnet *ifp;
u_int32_t status;
int claimed = 0;
sc = arg;
ifp = &sc->arpcom.ac_if;
if (!(ifp->if_flags & IFF_UP)) {
nge_stop(sc);
return (0);
}
CSR_WRITE_4(sc, NGE_IER, 0);
if(sc->nge_tbi)
CSR_WRITE_4(sc, NGE_GPIO, CSR_READ_4(sc, NGE_GPIO)
| NGE_GPIO_GP3_OUT);
for (;;) {
status = CSR_READ_4(sc, NGE_ISR);
if ((status & NGE_INTRS) == 0)
break;
claimed = 1;
if ((status & NGE_ISR_TX_DESC_OK) ||
(status & NGE_ISR_TX_ERR) ||
(status & NGE_ISR_TX_OK) ||
(status & NGE_ISR_TX_IDLE))
nge_txeof(sc);
if ((status & NGE_ISR_RX_DESC_OK) ||
(status & NGE_ISR_RX_ERR) ||
(status & NGE_ISR_RX_OFLOW) ||
(status & NGE_ISR_RX_FIFO_OFLOW) ||
(status & NGE_ISR_RX_IDLE) ||
(status & NGE_ISR_RX_OK))
nge_rxeof(sc);
if ((status & NGE_ISR_RX_IDLE))
NGE_SETBIT(sc, NGE_CSR, NGE_CSR_RX_ENABLE);
if (status & NGE_ISR_SYSERR) {
nge_reset(sc);
ifp->if_flags &= ~IFF_RUNNING;
nge_init(sc);
}
#if 0
if (status & NGE_IMR_PHY_INTR) {
sc->nge_link = 0;
nge_tick(sc);
}
#endif
}
CSR_WRITE_4(sc, NGE_IER, 1);
if (!ifq_empty(&ifp->if_snd))
nge_start(ifp);
if(sc->nge_tbi)
CSR_WRITE_4(sc, NGE_GPIO, CSR_READ_4(sc, NGE_GPIO)
& ~NGE_GPIO_GP3_OUT);
return claimed;
}
int
nge_encap(struct nge_softc *sc, struct mbuf *m_head, u_int32_t *txidx)
{
struct nge_desc *f = NULL;
struct mbuf *m;
int frag, cur, cnt = 0;
m = m_head;
cur = frag = *txidx;
for (m = m_head; m != NULL; m = m->m_next) {
if (m->m_len != 0) {
if ((NGE_TX_LIST_CNT -
(sc->nge_cdata.nge_tx_cnt + cnt)) < 2)
return(ENOBUFS);
f = &sc->nge_ldata->nge_tx_list[frag];
f->nge_ctl = NGE_CMDSTS_MORE | m->m_len;
f->nge_ptr = VTOPHYS(mtod(m, vaddr_t));
DPRINTFN(7,("%s: f->nge_ptr=%#x\n",
sc->sc_dv.dv_xname, f->nge_ptr));
if (cnt != 0)
f->nge_ctl |= NGE_CMDSTS_OWN;
cur = frag;
NGE_INC(frag, NGE_TX_LIST_CNT);
cnt++;
}
}
if (m != NULL)
return(ENOBUFS);
sc->nge_ldata->nge_tx_list[*txidx].nge_extsts = 0;
#if NVLAN > 0
if (m_head->m_flags & M_VLANTAG) {
sc->nge_ldata->nge_tx_list[cur].nge_extsts |=
(NGE_TXEXTSTS_VLANPKT|htons(m_head->m_pkthdr.ether_vtag));
}
#endif
sc->nge_ldata->nge_tx_list[cur].nge_mbuf = m_head;
sc->nge_ldata->nge_tx_list[cur].nge_ctl &= ~NGE_CMDSTS_MORE;
sc->nge_ldata->nge_tx_list[*txidx].nge_ctl |= NGE_CMDSTS_OWN;
sc->nge_cdata.nge_tx_cnt += cnt;
*txidx = frag;
return(0);
}
void
nge_start(struct ifnet *ifp)
{
struct nge_softc *sc;
struct mbuf *m_head = NULL;
u_int32_t idx;
int pkts = 0;
sc = ifp->if_softc;
if (!sc->nge_link)
return;
idx = sc->nge_cdata.nge_tx_prod;
if (ifq_is_oactive(&ifp->if_snd))
return;
while(sc->nge_ldata->nge_tx_list[idx].nge_mbuf == NULL) {
m_head = ifq_deq_begin(&ifp->if_snd);
if (m_head == NULL)
break;
if (nge_encap(sc, m_head, &idx)) {
ifq_deq_rollback(&ifp->if_snd, m_head);
ifq_set_oactive(&ifp->if_snd);
break;
}
ifq_deq_commit(&ifp->if_snd, m_head);
pkts++;
#if NBPFILTER > 0
if (ifp->if_bpf)
bpf_mtap_ether(ifp->if_bpf, m_head, BPF_DIRECTION_OUT);
#endif
}
if (pkts == 0)
return;
sc->nge_cdata.nge_tx_prod = idx;
NGE_SETBIT(sc, NGE_CSR, NGE_CSR_TX_ENABLE);
ifp->if_timer = 5;
}
void
nge_init(void *xsc)
{
struct nge_softc *sc = xsc;
struct ifnet *ifp = &sc->arpcom.ac_if;
struct mii_data *mii;
u_int32_t txcfg, rxcfg;
uint64_t media;
int s;
if (ifp->if_flags & IFF_RUNNING)
return;
s = splnet();
nge_stop(sc);
mii = sc->nge_tbi ? NULL: &sc->nge_mii;
CSR_WRITE_4(sc, NGE_RXFILT_CTL, NGE_FILTADDR_PAR0);
CSR_WRITE_4(sc, NGE_RXFILT_DATA,
((u_int16_t *)sc->arpcom.ac_enaddr)[0]);
CSR_WRITE_4(sc, NGE_RXFILT_CTL, NGE_FILTADDR_PAR1);
CSR_WRITE_4(sc, NGE_RXFILT_DATA,
((u_int16_t *)sc->arpcom.ac_enaddr)[1]);
CSR_WRITE_4(sc, NGE_RXFILT_CTL, NGE_FILTADDR_PAR2);
CSR_WRITE_4(sc, NGE_RXFILT_DATA,
((u_int16_t *)sc->arpcom.ac_enaddr)[2]);
if (nge_list_rx_init(sc) == ENOBUFS) {
printf("%s: initialization failed: no "
"memory for rx buffers\n", sc->sc_dv.dv_xname);
nge_stop(sc);
splx(s);
return;
}
nge_list_tx_init(sc);
NGE_SETBIT(sc, NGE_RXFILT_CTL, NGE_RXFILTCTL_ARP);
NGE_SETBIT(sc, NGE_RXFILT_CTL, NGE_RXFILTCTL_PERFECT);
if (ifp->if_flags & IFF_PROMISC)
NGE_SETBIT(sc, NGE_RXFILT_CTL, NGE_RXFILTCTL_ALLPHYS);
else
NGE_CLRBIT(sc, NGE_RXFILT_CTL, NGE_RXFILTCTL_ALLPHYS);
if (ifp->if_flags & IFF_BROADCAST)
NGE_SETBIT(sc, NGE_RXFILT_CTL, NGE_RXFILTCTL_BROAD);
else
NGE_CLRBIT(sc, NGE_RXFILT_CTL, NGE_RXFILTCTL_BROAD);
nge_setmulti(sc);
NGE_SETBIT(sc, NGE_RXFILT_CTL, NGE_RXFILTCTL_ENABLE);
CSR_WRITE_4(sc, NGE_RX_LISTPTR,
VTOPHYS(&sc->nge_ldata->nge_rx_list[0]));
CSR_WRITE_4(sc, NGE_TX_LISTPTR,
VTOPHYS(&sc->nge_ldata->nge_tx_list[0]));
CSR_WRITE_4(sc, NGE_RX_CFG, NGE_RXCFG);
CSR_WRITE_4(sc, NGE_VLAN_IP_RXCTL, NGE_VIPRXCTL_IPCSUM_ENB);
if (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING)
NGE_SETBIT(sc, NGE_VLAN_IP_RXCTL,
NGE_VIPRXCTL_TAG_DETECT_ENB | NGE_VIPRXCTL_TAG_STRIP_ENB);
CSR_WRITE_4(sc, NGE_TX_CFG, NGE_TXCFG);
if (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING)
NGE_SETBIT(sc, NGE_VLAN_IP_TXCTL, NGE_VIPTXCTL_TAG_PER_PKT);
if (sc->nge_tbi)
media = sc->nge_ifmedia.ifm_cur->ifm_media;
else
media = mii->mii_media_active;
txcfg = CSR_READ_4(sc, NGE_TX_CFG);
rxcfg = CSR_READ_4(sc, NGE_RX_CFG);
DPRINTFN(4, ("%s: nge_init txcfg=%#x, rxcfg=%#x\n",
sc->sc_dv.dv_xname, txcfg, rxcfg));
if ((media & IFM_GMASK) == IFM_FDX) {
txcfg |= (NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR);
rxcfg |= (NGE_RXCFG_RX_FDX);
} else {
txcfg &= ~(NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR);
rxcfg &= ~(NGE_RXCFG_RX_FDX);
}
txcfg |= NGE_TXCFG_AUTOPAD;
CSR_WRITE_4(sc, NGE_TX_CFG, txcfg);
CSR_WRITE_4(sc, NGE_RX_CFG, rxcfg);
nge_tick(sc);
NGE_SETBIT(sc, NGE_CFG, NGE_CFG_PHYINTR_SPD|NGE_CFG_PHYINTR_LNK|
NGE_CFG_PHYINTR_DUP|NGE_CFG_EXTSTS_ENB);
DPRINTFN(1, ("%s: nge_init: config=%#x\n", sc->sc_dv.dv_xname,
CSR_READ_4(sc, NGE_CFG)));
CSR_WRITE_4(sc, NGE_IHR, 0x01);
CSR_WRITE_4(sc, NGE_IMR, NGE_INTRS);
CSR_WRITE_4(sc, NGE_IER, 1);
NGE_CLRBIT(sc, NGE_CSR, NGE_CSR_TX_DISABLE|NGE_CSR_RX_DISABLE);
NGE_SETBIT(sc, NGE_CSR, NGE_CSR_RX_ENABLE);
if (sc->nge_tbi)
nge_ifmedia_tbi_upd(ifp);
else
nge_ifmedia_mii_upd(ifp);
ifp->if_flags |= IFF_RUNNING;
ifq_clr_oactive(&ifp->if_snd);
splx(s);
}
int
nge_ifmedia_mii_upd(struct ifnet *ifp)
{
struct nge_softc *sc = ifp->if_softc;
struct mii_data *mii = &sc->nge_mii;
DPRINTFN(2, ("%s: nge_ifmedia_mii_upd\n", sc->sc_dv.dv_xname));
sc->nge_link = 0;
if (mii->mii_instance) {
struct mii_softc *miisc;
LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
mii_phy_reset(miisc);
}
mii_mediachg(mii);
return(0);
}
void
nge_ifmedia_mii_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
{
struct nge_softc *sc = ifp->if_softc;
struct mii_data *mii = &sc->nge_mii;
DPRINTFN(2, ("%s: nge_ifmedia_mii_sts\n", sc->sc_dv.dv_xname));
mii_pollstat(mii);
ifmr->ifm_active = mii->mii_media_active;
ifmr->ifm_status = mii->mii_media_status;
}
int
nge_ifmedia_tbi_upd(struct ifnet *ifp)
{
struct nge_softc *sc = ifp->if_softc;
DPRINTFN(2, ("%s: nge_ifmedia_tbi_upd\n", sc->sc_dv.dv_xname));
sc->nge_link = 0;
if (IFM_SUBTYPE(sc->nge_ifmedia.ifm_cur->ifm_media)
== IFM_AUTO) {
u_int32_t anar, bmcr;
anar = CSR_READ_4(sc, NGE_TBI_ANAR);
anar |= (NGE_TBIANAR_HDX | NGE_TBIANAR_FDX);
CSR_WRITE_4(sc, NGE_TBI_ANAR, anar);
bmcr = CSR_READ_4(sc, NGE_TBI_BMCR);
bmcr |= (NGE_TBIBMCR_ENABLE_ANEG|NGE_TBIBMCR_RESTART_ANEG);
CSR_WRITE_4(sc, NGE_TBI_BMCR, bmcr);
bmcr &= ~(NGE_TBIBMCR_RESTART_ANEG);
CSR_WRITE_4(sc, NGE_TBI_BMCR, bmcr);
} else {
u_int32_t txcfg, rxcfg;
txcfg = CSR_READ_4(sc, NGE_TX_CFG);
rxcfg = CSR_READ_4(sc, NGE_RX_CFG);
if ((sc->nge_ifmedia.ifm_cur->ifm_media & IFM_GMASK)
== IFM_FDX) {
txcfg |= NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR;
rxcfg |= NGE_RXCFG_RX_FDX;
} else {
txcfg &= ~(NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR);
rxcfg &= ~(NGE_RXCFG_RX_FDX);
}
txcfg |= NGE_TXCFG_AUTOPAD;
CSR_WRITE_4(sc, NGE_TX_CFG, txcfg);
CSR_WRITE_4(sc, NGE_RX_CFG, rxcfg);
}
NGE_CLRBIT(sc, NGE_GPIO, NGE_GPIO_GP3_OUT);
return(0);
}
void
nge_ifmedia_tbi_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
{
struct nge_softc *sc = ifp->if_softc;
u_int32_t bmcr;
bmcr = CSR_READ_4(sc, NGE_TBI_BMCR);
if (IFM_SUBTYPE(sc->nge_ifmedia.ifm_cur->ifm_media) == IFM_AUTO) {
u_int32_t bmsr = CSR_READ_4(sc, NGE_TBI_BMSR);
DPRINTFN(2, ("%s: nge_ifmedia_tbi_sts bmsr=%#x, bmcr=%#x\n",
sc->sc_dv.dv_xname, bmsr, bmcr));
if (!(bmsr & NGE_TBIBMSR_ANEG_DONE)) {
ifmr->ifm_active = IFM_ETHER|IFM_NONE;
ifmr->ifm_status = IFM_AVALID;
return;
}
} else {
DPRINTFN(2, ("%s: nge_ifmedia_tbi_sts bmcr=%#x\n",
sc->sc_dv.dv_xname, bmcr));
}
ifmr->ifm_status = IFM_AVALID|IFM_ACTIVE;
ifmr->ifm_active = IFM_ETHER|IFM_1000_SX;
if (bmcr & NGE_TBIBMCR_LOOPBACK)
ifmr->ifm_active |= IFM_LOOP;
if (IFM_SUBTYPE(sc->nge_ifmedia.ifm_cur->ifm_media) == IFM_AUTO) {
u_int32_t anlpar = CSR_READ_4(sc, NGE_TBI_ANLPAR);
DPRINTFN(2, ("%s: nge_ifmedia_tbi_sts anlpar=%#x\n",
sc->sc_dv.dv_xname, anlpar));
ifmr->ifm_active |= IFM_AUTO;
if (anlpar & NGE_TBIANLPAR_FDX) {
ifmr->ifm_active |= IFM_FDX;
} else if (anlpar & NGE_TBIANLPAR_HDX) {
ifmr->ifm_active |= IFM_HDX;
} else
ifmr->ifm_active |= IFM_FDX;
} else if ((sc->nge_ifmedia.ifm_cur->ifm_media & IFM_GMASK) == IFM_FDX)
ifmr->ifm_active |= IFM_FDX;
else
ifmr->ifm_active |= IFM_HDX;
}
int
nge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
{
struct nge_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *) data;
struct mii_data *mii;
int s, error = 0;
s = splnet();
switch(command) {
case SIOCSIFADDR:
ifp->if_flags |= IFF_UP;
nge_init(sc);
break;
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
if (ifp->if_flags & IFF_RUNNING &&
ifp->if_flags & IFF_PROMISC &&
!(sc->nge_if_flags & IFF_PROMISC)) {
NGE_SETBIT(sc, NGE_RXFILT_CTL,
NGE_RXFILTCTL_ALLPHYS|
NGE_RXFILTCTL_ALLMULTI);
} else if (ifp->if_flags & IFF_RUNNING &&
!(ifp->if_flags & IFF_PROMISC) &&
sc->nge_if_flags & IFF_PROMISC) {
NGE_CLRBIT(sc, NGE_RXFILT_CTL,
NGE_RXFILTCTL_ALLPHYS);
if (!(ifp->if_flags & IFF_ALLMULTI))
NGE_CLRBIT(sc, NGE_RXFILT_CTL,
NGE_RXFILTCTL_ALLMULTI);
} else {
ifp->if_flags &= ~IFF_RUNNING;
nge_init(sc);
}
} else {
if (ifp->if_flags & IFF_RUNNING)
nge_stop(sc);
}
sc->nge_if_flags = ifp->if_flags;
error = 0;
break;
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
if (sc->nge_tbi) {
error = ifmedia_ioctl(ifp, ifr, &sc->nge_ifmedia,
command);
} else {
mii = &sc->nge_mii;
error = ifmedia_ioctl(ifp, ifr, &mii->mii_media,
command);
}
break;
default:
error = ether_ioctl(ifp, &sc->arpcom, command, data);
}
if (error == ENETRESET) {
if (ifp->if_flags & IFF_RUNNING)
nge_setmulti(sc);
error = 0;
}
splx(s);
return(error);
}
void
nge_watchdog(struct ifnet *ifp)
{
struct nge_softc *sc;
sc = ifp->if_softc;
ifp->if_oerrors++;
printf("%s: watchdog timeout\n", sc->sc_dv.dv_xname);
nge_stop(sc);
nge_reset(sc);
ifp->if_flags &= ~IFF_RUNNING;
nge_init(sc);
if (!ifq_empty(&ifp->if_snd))
nge_start(ifp);
}
void
nge_stop(struct nge_softc *sc)
{
int i;
struct ifnet *ifp;
struct mii_data *mii;
ifp = &sc->arpcom.ac_if;
ifp->if_timer = 0;
if (sc->nge_tbi) {
mii = NULL;
} else {
mii = &sc->nge_mii;
}
timeout_del(&sc->nge_timeout);
ifp->if_flags &= ~IFF_RUNNING;
ifq_clr_oactive(&ifp->if_snd);
CSR_WRITE_4(sc, NGE_IER, 0);
CSR_WRITE_4(sc, NGE_IMR, 0);
NGE_SETBIT(sc, NGE_CSR, NGE_CSR_TX_DISABLE|NGE_CSR_RX_DISABLE);
DELAY(1000);
CSR_WRITE_4(sc, NGE_TX_LISTPTR, 0);
CSR_WRITE_4(sc, NGE_RX_LISTPTR, 0);
if (!sc->nge_tbi)
mii_down(mii);
sc->nge_link = 0;
for (i = 0; i < NGE_RX_LIST_CNT; i++) {
if (sc->nge_ldata->nge_rx_list[i].nge_mbuf != NULL) {
m_freem(sc->nge_ldata->nge_rx_list[i].nge_mbuf);
sc->nge_ldata->nge_rx_list[i].nge_mbuf = NULL;
}
}
bzero(&sc->nge_ldata->nge_rx_list,
sizeof(sc->nge_ldata->nge_rx_list));
for (i = 0; i < NGE_TX_LIST_CNT; i++) {
if (sc->nge_ldata->nge_tx_list[i].nge_mbuf != NULL) {
m_freem(sc->nge_ldata->nge_tx_list[i].nge_mbuf);
sc->nge_ldata->nge_tx_list[i].nge_mbuf = NULL;
}
}
bzero(&sc->nge_ldata->nge_tx_list,
sizeof(sc->nge_ldata->nge_tx_list));
}
const struct cfattach nge_ca = {
sizeof(struct nge_softc), nge_probe, nge_attach
};
struct cfdriver nge_cd = {
NULL, "nge", DV_IFNET
};