/*
* Copyright (c) 2001 Wasabi Systems, Inc.
* All rights reserved.
*
* Written by Jason R. Thorpe for Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed for the NetBSD Project by
* Wasabi Systems, Inc.
* 4. The name of Wasabi Systems, Inc. may not be used to endorse
* or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
* 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.
*/
/*
* Device driver for the AMD PCnet-PCI series of Ethernet
* chips:
*
* * Am79c970 PCnet-PCI Single-Chip Ethernet Controller for PCI
* Local Bus
*
* * Am79c970A PCnet-PCI II Single-Chip Full-Duplex Ethernet Controller
* for PCI Local Bus
*
* * Am79c971 PCnet-FAST Single-Chip Full-Duplex 10/100Mbps
* Ethernet Controller for PCI Local Bus
*
* * Am79c972 PCnet-FAST+ Enhanced 10/100Mbps PCI Ethernet Controller
* with OnNow Support
*
* * Am79c973/Am79c975 PCnet-FAST III Single-Chip 10/100Mbps PCI
* Ethernet Controller with Integrated PHY
*
* This also supports the virtual PCnet-PCI Ethernet interface found
* in VMware.
*
* TODO:
*
* * Split this into bus-specific and bus-independent portions.
* The core could also be used for the ILACC (Am79900) 32-bit
* Ethernet chip (XXX only if we use an ILACC-compatible SWSTYLE).
*/
/*
* Transmit descriptor list size. This is arbitrary, but allocate
* enough descriptors for 128 pending transmissions, and 4 segments
* per packet. This MUST work out to a power of 2.
*
* NOTE: We can't have any more than 512 Tx descriptors, SO BE CAREFUL!
*
* So we play a little trick here. We give each packet up to 16
* DMA segments, but only allocate the max of 512 descriptors. The
* transmit logic can deal with this, we just are hoping to sneak by.
*/
#define PCN_NTXSEGS 16
#define PCN_NTXSEGS_VMWARE 8 /* bug in VMware's emulation */
/* Tx interrupt every N + 1 packets. */
#define PCN_TXINTR_MASK 7
/*
* Receive descriptor list size. We have one Rx buffer per incoming
* packet, so this logic is a little simpler.
*/
#define PCN_NRXDESC 128
#define PCN_NRXDESC_MASK (PCN_NRXDESC - 1)
#define PCN_NEXTRX(x) (((x) + 1) & PCN_NRXDESC_MASK)
/*
* Control structures are DMA'd to the PCnet chip. We allocate them in
* a single clump that maps to a single DMA segment to make several things
* easier.
*/
struct pcn_control_data {
/* The transmit descriptors. */
struct letmd pcd_txdescs[PCN_NTXDESC];
/* The receive descriptors. */
struct lermd pcd_rxdescs[PCN_NRXDESC];
/* The init block. */
struct leinit pcd_initblock;
};
/*
* Software state for transmit jobs.
*/
struct pcn_txsoft {
struct mbuf *txs_mbuf; /* head of our mbuf chain */
bus_dmamap_t txs_dmamap; /* our DMA map */
int txs_firstdesc; /* first descriptor in packet */
int txs_lastdesc; /* last descriptor in packet */
};
/*
* Software state for receive jobs.
*/
struct pcn_rxsoft {
struct mbuf *rxs_mbuf; /* head of our mbuf chain */
bus_dmamap_t rxs_dmamap; /* our DMA map */
};
/*
* Description of Rx FIFO watermarks for various revisions.
*/
static const char * const pcn_79c970_rcvfw[] = {
"16 bytes",
"64 bytes",
"128 bytes",
NULL,
};
/*
* Software state per device.
*/
struct pcn_softc {
device_t sc_dev; /* generic device information */
bus_space_tag_t sc_st; /* bus space tag */
bus_space_handle_t sc_sh; /* bus space handle */
bus_dma_tag_t sc_dmat; /* bus DMA tag */
struct ethercom sc_ethercom; /* Ethernet common data */
/* Points to our media routines, etc. */
const struct pcn_variant *sc_variant;
void *sc_ih; /* interrupt cookie */
struct mii_data sc_mii; /* MII/media information */
callout_t sc_tick_ch; /* tick callout */
bus_dmamap_t sc_cddmamap; /* control data DMA map */
#define sc_cddma sc_cddmamap->dm_segs[0].ds_addr
/* Software state for transmit and receive descriptors. */
struct pcn_txsoft sc_txsoft[PCN_TXQUEUELEN];
struct pcn_rxsoft sc_rxsoft[PCN_NRXDESC];
/* Control data structures */
struct pcn_control_data *sc_control_data;
#define sc_txdescs sc_control_data->pcd_txdescs
#define sc_rxdescs sc_control_data->pcd_rxdescs
#define sc_initblock sc_control_data->pcd_initblock
#ifdef PCN_EVENT_COUNTERS
/* Event counters. */
struct evcnt sc_ev_txdstall; /* Tx stalled due to no txd */
struct evcnt sc_ev_txintr; /* Tx interrupts */
struct evcnt sc_ev_rxintr; /* Rx interrupts */
struct evcnt sc_ev_babl; /* BABL in pcn_intr() */
struct evcnt sc_ev_miss; /* MISS in pcn_intr() */
struct evcnt sc_ev_merr; /* MERR in pcn_intr() */
/*
* Description of a PCnet-PCI variant. Used to select media access
* method, mostly, and to print a nice description of the chip.
*/
static const struct pcn_variant {
const char *pcv_desc;
void (*pcv_mediainit)(struct pcn_softc *);
uint16_t pcv_chipid;
} pcn_variants[] = {
{ "Am79c970 PCnet-PCI",
pcn_79c970_mediainit,
PARTID_Am79c970 },
/*
* IBM Makes a PCI variant of this card which shows up as a
* Trident Microsystems 4DWAVE DX (ethernet network, revision 0x25)
* this card is truly a pcn card, so we have a special case match for
* it
*/
/* Make sure bus mastering is enabled. */
pci_conf_write(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG,
pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG) |
PCI_COMMAND_MASTER_ENABLE);
/* power up chip */
if ((error = pci_activate(pa->pa_pc, pa->pa_tag, self,
NULL)) && error != EOPNOTSUPP) {
aprint_error_dev(self, "cannot activate %d\n", error);
return;
}
/*
* Reset the chip to a known state. This also puts the
* chip into 32-bit mode.
*/
pcn_reset(sc);
/*
* On some systems with the chip is an on-board device, the
* EEPROM is not used. Handle this by reading the MAC address
* from the CSRs (assuming that boot firmware has written
* it there).
*/
obj = prop_dictionary_get(device_properties(sc->sc_dev),
"am79c970-no-eeprom");
if (prop_bool_true(obj)) {
for (i = 0; i < 3; i++) {
uint32_t val;
val = pcn_csr_read(sc, LE_CSR12 + i);
enaddr[2 * i] = val & 0xff;
enaddr[2 * i + 1] = (val >> 8) & 0xff;
}
} else {
for (i = 0; i < ETHER_ADDR_LEN; i++) {
enaddr[i] = bus_space_read_1(sc->sc_st, sc->sc_sh,
PCN32_APROM + i);
}
}
/* Check to see if this is a VMware emulated network interface. */
is_vmware = pcn_is_vmware(enaddr);
/*
* Now that the device is mapped, attempt to figure out what
* kind of chip we have. Note that IDL has all 32 bits of
* the chip ID when we're in 32-bit mode.
*/
chipid = pcn_csr_read(sc, LE_CSR88);
sc->sc_variant = pcn_lookup_variant(CHIPID_PARTID(chipid));
/*
* VMware has a bug in its network interface emulation; we must
* limit the number of Tx segments.
*/
if (is_vmware) {
ntxsegs = PCN_NTXSEGS_VMWARE;
prop_dictionary_set_bool(device_properties(sc->sc_dev),
"am79c970-vmware-tx-bug", TRUE);
aprint_verbose_dev(self,
"VMware Tx segment count bug detected\n");
} else {
ntxsegs = PCN_NTXSEGS;
}
/*
* Map and establish our interrupt.
*/
if (pci_intr_map(pa, &ih)) {
aprint_error_dev(self, "unable to map interrupt\n");
return;
}
intrstr = pci_intr_string(pc, ih, intrbuf, sizeof(intrbuf));
sc->sc_ih = pci_intr_establish_xname(pc, ih, IPL_NET, pcn_intr, sc,
device_xname(self));
if (sc->sc_ih == NULL) {
aprint_error_dev(self, "unable to establish interrupt");
if (intrstr != NULL)
aprint_error(" at %s", intrstr);
aprint_error("\n");
return;
}
aprint_normal_dev(self, "interrupting at %s\n", intrstr);
/*
* Allocate the control data structures, and create and load the
* DMA map for it.
*/
if ((error = bus_dmamem_alloc(sc->sc_dmat,
sizeof(struct pcn_control_data), PAGE_SIZE, 0, &seg, 1, &rseg,
0)) != 0) {
aprint_error_dev(self, "unable to allocate control data, "
"error = %d\n", error);
goto fail_0;
}
if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg,
sizeof(struct pcn_control_data), (void **)&sc->sc_control_data,
BUS_DMA_COHERENT)) != 0) {
aprint_error_dev(self, "unable to map control data, "
"error = %d\n", error);
goto fail_1;
}
if ((error = bus_dmamap_create(sc->sc_dmat,
sizeof(struct pcn_control_data), 1,
sizeof(struct pcn_control_data), 0, 0, &sc->sc_cddmamap)) != 0) {
aprint_error_dev(self, "unable to create control data DMA map, "
"error = %d\n", error);
goto fail_2;
}
if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_cddmamap,
sc->sc_control_data, sizeof(struct pcn_control_data), NULL,
0)) != 0) {
aprint_error_dev(self,
"unable to load control data DMA map, error = %d\n", error);
goto fail_3;
}
/* Create the transmit buffer DMA maps. */
for (i = 0; i < PCN_TXQUEUELEN; i++) {
if ((error = bus_dmamap_create(sc->sc_dmat, MCLBYTES,
ntxsegs, MCLBYTES, 0, 0,
&sc->sc_txsoft[i].txs_dmamap)) != 0) {
aprint_error_dev(self,
"unable to create tx DMA map %d, error = %d\n",
i, error);
goto fail_4;
}
}
/* Create the receive buffer DMA maps. */
for (i = 0; i < PCN_NRXDESC; i++) {
if ((error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1,
MCLBYTES, 0, 0, &sc->sc_rxsoft[i].rxs_dmamap)) != 0) {
aprint_error_dev(self,
"unable to create rx DMA map %d, error = %d\n",
i, error);
goto fail_5;
}
sc->sc_rxsoft[i].rxs_mbuf = NULL;
}
/* Initialize our media structures. */
(*sc->sc_variant->pcv_mediainit)(sc);
default:
sc->sc_rcvfw_desc = pcn_79c971_rcvfw;
/*
* Read BCR25 to determine how much SRAM is
* on the board. If > 0, then we the chip
* uses different Start Point thresholds.
*
* Note BCR25 and BCR26 are loaded from the
* EEPROM on RST, and unaffected by S_RESET,
* so we don't really have to worry about
* them except for this.
*/
reg = pcn_bcr_read(sc, LE_BCR25) & 0x00ff;
if (reg != 0)
sc->sc_xmtsp_desc = pcn_79c971_xmtsp_sram;
else
sc->sc_xmtsp_desc = pcn_79c971_xmtsp;
sc->sc_xmtfw_desc = pcn_79c971_xmtfw;
break;
}
/*
* Set up defaults -- see the tables above for what these
* values mean.
*
* XXX How should we tune RCVFW and XMTFW?
*/
sc->sc_rcvfw = 1; /* minimum for full-duplex */
sc->sc_xmtsp = 1;
sc->sc_xmtfw = 0;
/*
* Establish power handler with shutdown hook, to make sure
* the interface is shutdown during reboot.
*/
if (pmf_device_register1(self, NULL, NULL, pcn_shutdown))
pmf_class_network_register(self, ifp);
else
aprint_error_dev(self, "couldn't establish power handler\n");
return;
/*
* Free any resources we've allocated during the failed attach
* attempt. Do this in reverse order and fall through.
*/
fail_5:
for (i = 0; i < PCN_NRXDESC; i++) {
if (sc->sc_rxsoft[i].rxs_dmamap != NULL)
bus_dmamap_destroy(sc->sc_dmat,
sc->sc_rxsoft[i].rxs_dmamap);
}
fail_4:
for (i = 0; i < PCN_TXQUEUELEN; i++) {
if (sc->sc_txsoft[i].txs_dmamap != NULL)
bus_dmamap_destroy(sc->sc_dmat,
sc->sc_txsoft[i].txs_dmamap);
}
bus_dmamap_unload(sc->sc_dmat, sc->sc_cddmamap);
fail_3:
bus_dmamap_destroy(sc->sc_dmat, sc->sc_cddmamap);
fail_2:
bus_dmamem_unmap(sc->sc_dmat, (void *)sc->sc_control_data,
sizeof(struct pcn_control_data));
fail_1:
bus_dmamem_free(sc->sc_dmat, &seg, rseg);
fail_0:
return;
}
/*
* pcn_shutdown:
*
* Make sure the interface is stopped at reboot time.
*/
static bool
pcn_shutdown(device_t self, int howto)
{
struct pcn_softc *sc = device_private(self);
pcn_stop(&sc->sc_ethercom.ec_if, 1);
/* explicitly reset the chip for some onboard one with lazy firmware */
pcn_reset(sc);
if ((ifp->if_flags & IFF_RUNNING) != IFF_RUNNING)
return;
/*
* Remember the previous number of free descriptors and
* the first descriptor we'll use.
*/
ofree = sc->sc_txfree;
/*
* Loop through the send queue, setting up transmit descriptors
* until we drain the queue, or use up all available transmit
* descriptors.
*/
while (sc->sc_txsfree != 0) {
/* Grab a packet off the queue. */
IFQ_POLL(&ifp->if_snd, m0);
if (m0 == NULL)
break;
m = NULL;
/*
* Load the DMA map. If this fails, the packet either
* didn't fit in the allotted number of segments, or we
* were short on resources. In this case, we'll copy
* and try again.
*/
if (bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, m0,
BUS_DMA_WRITE | BUS_DMA_NOWAIT) != 0) {
PCN_EVCNT_INCR(&sc->sc_ev_txcopy);
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL) {
printf("%s: unable to allocate Tx mbuf\n",
device_xname(sc->sc_dev));
break;
}
MCLAIM(m, &sc->sc_ethercom.ec_rx_mowner);
if (m0->m_pkthdr.len > MHLEN) {
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
printf("%s: unable to allocate Tx "
"cluster\n",
device_xname(sc->sc_dev));
m_freem(m);
break;
}
}
m_copydata(m0, 0, m0->m_pkthdr.len, mtod(m, void *));
m->m_pkthdr.len = m->m_len = m0->m_pkthdr.len;
error = bus_dmamap_load_mbuf(sc->sc_dmat, dmamap,
m, BUS_DMA_WRITE | BUS_DMA_NOWAIT);
if (error) {
printf("%s: unable to load Tx buffer, "
"error = %d\n", device_xname(sc->sc_dev),
error);
m_freem(m);
break;
}
}
/*
* Ensure we have enough descriptors free to describe
* the packet. Note, we always reserve one descriptor
* at the end of the ring as a termination point, to
* prevent wrap-around.
*/
if (dmamap->dm_nsegs > (sc->sc_txfree - 1)) {
/*
* Not enough free descriptors to transmit this
* packet.
*/
bus_dmamap_unload(sc->sc_dmat, dmamap);
m_freem(m);
PCN_EVCNT_INCR(&sc->sc_ev_txdstall);
break;
}
IFQ_DEQUEUE(&ifp->if_snd, m0);
if (m != NULL) {
m_freem(m0);
m0 = m;
}
/*
* WE ARE NOW COMMITTED TO TRANSMITTING THE PACKET.
*/
#ifdef PCN_EVENT_COUNTERS
switch (dmamap->dm_nsegs) {
case 1:
PCN_EVCNT_INCR(&sc->sc_ev_txseg1);
break;
case 2:
PCN_EVCNT_INCR(&sc->sc_ev_txseg2);
break;
case 3:
PCN_EVCNT_INCR(&sc->sc_ev_txseg3);
break;
case 4:
PCN_EVCNT_INCR(&sc->sc_ev_txseg4);
break;
case 5:
PCN_EVCNT_INCR(&sc->sc_ev_txseg5);
break;
default:
PCN_EVCNT_INCR(&sc->sc_ev_txsegmore);
break;
}
#endif /* PCN_EVENT_COUNTERS */
/*
* Initialize the transmit descriptors.
*/
if (sc->sc_swstyle == LE_B20_SSTYLE_PCNETPCI3) {
for (nexttx = sc->sc_txnext, seg = 0;
seg < dmamap->dm_nsegs;
seg++, nexttx = PCN_NEXTTX(nexttx)) {
/*
* If this is the first descriptor we're
* enqueueing, don't set the OWN bit just
* yet. That could cause a race condition.
* We'll do it below.
*/
sc->sc_txdescs[nexttx].tmd0 = 0;
sc->sc_txdescs[nexttx].tmd2 =
htole32(dmamap->dm_segs[seg].ds_addr);
sc->sc_txdescs[nexttx].tmd1 =
htole32(LE_T1_ONES |
(nexttx == sc->sc_txnext ? 0 : LE_T1_OWN) |
(LE_BCNT(dmamap->dm_segs[seg].ds_len) &
LE_T1_BCNT_MASK));
lasttx = nexttx;
}
} else {
for (nexttx = sc->sc_txnext, seg = 0;
seg < dmamap->dm_nsegs;
seg++, nexttx = PCN_NEXTTX(nexttx)) {
/*
* If this is the first descriptor we're
* enqueueing, don't set the OWN bit just
* yet. That could cause a race condition.
* We'll do it below.
*/
sc->sc_txdescs[nexttx].tmd0 =
htole32(dmamap->dm_segs[seg].ds_addr);
sc->sc_txdescs[nexttx].tmd2 = 0;
sc->sc_txdescs[nexttx].tmd1 =
htole32(LE_T1_ONES |
(nexttx == sc->sc_txnext ? 0 : LE_T1_OWN) |
(LE_BCNT(dmamap->dm_segs[seg].ds_len) &
LE_T1_BCNT_MASK));
lasttx = nexttx;
}
}
KASSERT(lasttx != -1);
/* Interrupt on the packet, if appropriate. */
if ((sc->sc_txsnext & PCN_TXINTR_MASK) == 0)
sc->sc_txdescs[lasttx].tmd1 |= htole32(LE_T1_LTINT);
/* Set `start of packet' and `end of packet' appropriately. */
sc->sc_txdescs[lasttx].tmd1 |= htole32(LE_T1_ENP);
sc->sc_txdescs[sc->sc_txnext].tmd1 |=
htole32(LE_T1_OWN | LE_T1_STP);
/* Kick the transmitter. */
pcn_csr_write(sc, LE_CSR0, LE_C0_INEA | LE_C0_TDMD);
/*
* Store a pointer to the packet so we can free it later,
* and remember what txdirty will be once the packet is
* done.
*/
txs->txs_mbuf = m0;
txs->txs_firstdesc = sc->sc_txnext;
txs->txs_lastdesc = lasttx;
/*
* Go through our Tx list and free mbufs for those
* frames which have been transmitted.
*/
for (i = sc->sc_txsdirty; sc->sc_txsfree != PCN_TXQUEUELEN;
i = PCN_NEXTTXS(i), sc->sc_txsfree++) {
txs = &sc->sc_txsoft[i];
for (i = sc->sc_rxptr;; i = PCN_NEXTRX(i)) {
rxs = &sc->sc_rxsoft[i];
PCN_CDRXSYNC(sc, i,
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
rmd1 = le32toh(sc->sc_rxdescs[i].rmd1);
if (rmd1 & LE_R1_OWN)
break;
/*
* Check for errors and make sure the packet fit into
* a single buffer. We have structured this block of
* code the way it is in order to compress it into
* one test in the common case (no error).
*/
if (__predict_false((rmd1 & (LE_R1_STP | LE_R1_ENP |LE_R1_ERR))
!= (LE_R1_STP | LE_R1_ENP))) {
/* Make sure the packet is in a single buffer. */
if ((rmd1 & (LE_R1_STP | LE_R1_ENP)) !=
(LE_R1_STP | LE_R1_ENP)) {
printf("%s: packet spilled into next buffer\n",
device_xname(sc->sc_dev));
return 1; /* pcn_intr() will re-init */
}
/*
* If the packet had an error, simple recycle the
* buffer.
*/
if (rmd1 & LE_R1_ERR) {
if_statinc(ifp, if_ierrors);
/*
* If we got an overflow error, chances
* are there will be a CRC error. In
* this case, just print the overflow
* error, and skip the others.
*/
if (rmd1 & LE_R1_OFLO)
printf("%s: overflow error\n",
device_xname(sc->sc_dev));
else {
#define PRINTIT(x, str) \
if (rmd1 & (x)) \
printf("%s: %s\n", \
device_xname(sc->sc_dev), \
str);
PRINTIT(LE_R1_FRAM, "framing error");
PRINTIT(LE_R1_CRC, "CRC error");
PRINTIT(LE_R1_BUFF, "buffer error");
}
#undef PRINTIT
PCN_INIT_RXDESC(sc, i);
continue;
}
}
/*
* No errors; receive the packet.
*/
if (sc->sc_swstyle == LE_B20_SSTYLE_PCNETPCI3)
len = le32toh(sc->sc_rxdescs[i].rmd0) & LE_R1_BCNT_MASK;
else
len = le32toh(sc->sc_rxdescs[i].rmd2) & LE_R1_BCNT_MASK;
/*
* The LANCE family includes the CRC with every packet;
* trim it off here.
*/
len -= ETHER_CRC_LEN;
/*
* If the packet is small enough to fit in a
* single header mbuf, allocate one and copy
* the data into it. This greatly reduces
* memory consumption when we receive lots
* of small packets.
*
* Otherwise, we add a new buffer to the receive
* chain. If this fails, we drop the packet and
* recycle the old buffer.
*/
if (pcn_copy_small != 0 && len <= (MHLEN - 2)) {
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL)
goto dropit;
MCLAIM(m, &sc->sc_ethercom.ec_rx_mowner);
m->m_data += 2;
memcpy(mtod(m, void *),
mtod(rxs->rxs_mbuf, void *), len);
PCN_INIT_RXDESC(sc, i);
bus_dmamap_sync(sc->sc_dmat, rxs->rxs_dmamap, 0,
rxs->rxs_dmamap->dm_mapsize,
BUS_DMASYNC_PREREAD);
} else {
m = rxs->rxs_mbuf;
if (pcn_add_rxbuf(sc, i) != 0) {
dropit:
if_statinc(ifp, if_ierrors);
PCN_INIT_RXDESC(sc, i);
bus_dmamap_sync(sc->sc_dmat,
rxs->rxs_dmamap, 0,
rxs->rxs_dmamap->dm_mapsize,
BUS_DMASYNC_PREREAD);
continue;
}
}
/*
* pcn_tick:
*
* One second timer, used to tick the MII.
*/
static void
pcn_tick(void *arg)
{
struct pcn_softc *sc = arg;
int s;
s = splnet();
mii_tick(&sc->sc_mii);
splx(s);
callout_schedule(&sc->sc_tick_ch, hz);
}
/*
* pcn_reset:
*
* Perform a soft reset on the PCnet-PCI.
*/
static void
pcn_reset(struct pcn_softc *sc)
{
/*
* The PCnet-PCI chip is reset by reading from the
* RESET register. Note that while the NE2100 LANCE
* boards require a write after the read, the PCnet-PCI
* chips do not require this.
*
* Since we don't know if we're in 16-bit or 32-bit
* mode right now, issue both (it's safe) in the
* hopes that one will succeed.
*/
(void) bus_space_read_2(sc->sc_st, sc->sc_sh, PCN16_RESET);
(void) bus_space_read_4(sc->sc_st, sc->sc_sh, PCN32_RESET);
/* Wait 1ms for it to finish. */
delay(1000);
/*
* Select 32-bit I/O mode by issuing a 32-bit write to the
* RDP. Since the RAP is 0 after a reset, writing a 0
* to RDP is safe (since it simply clears CSR0).
*/
bus_space_write_4(sc->sc_st, sc->sc_sh, PCN32_RDP, 0);
}
/*
* pcn_init: [ifnet interface function]
*
* Initialize the interface. Must be called at splnet().
*/
static int
pcn_init(struct ifnet *ifp)
{
struct pcn_softc *sc = ifp->if_softc;
struct pcn_rxsoft *rxs;
const uint8_t *enaddr = CLLADDR(ifp->if_sadl);
int i, error = 0;
uint32_t reg;
/* Cancel any pending I/O. */
pcn_stop(ifp, 0);
/* Reset the chip to a known state. */
pcn_reset(sc);
/*
* On the Am79c970, select SSTYLE 2, and SSTYLE 3 on everything
* else.
*
* XXX It'd be really nice to use SSTYLE 2 on all the chips,
* because the structure layout is compatible with ILACC,
* but the burst mode is only available in SSTYLE 3, and
* burst mode should provide some performance enhancement.
*/
if (sc->sc_variant->pcv_chipid == PARTID_Am79c970)
sc->sc_swstyle = LE_B20_SSTYLE_PCNETPCI2;
else
sc->sc_swstyle = LE_B20_SSTYLE_PCNETPCI3;
pcn_bcr_write(sc, LE_BCR20, sc->sc_swstyle);
/* Initialize the transmit job descriptors. */
for (i = 0; i < PCN_TXQUEUELEN; i++)
sc->sc_txsoft[i].txs_mbuf = NULL;
sc->sc_txsfree = PCN_TXQUEUELEN;
sc->sc_txsnext = 0;
sc->sc_txsdirty = 0;
/*
* Initialize the receive descriptor and receive job
* descriptor rings.
*/
for (i = 0; i < PCN_NRXDESC; i++) {
rxs = &sc->sc_rxsoft[i];
if (rxs->rxs_mbuf == NULL) {
if ((error = pcn_add_rxbuf(sc, i)) != 0) {
printf("%s: unable to allocate or map rx "
"buffer %d, error = %d\n",
device_xname(sc->sc_dev), i, error);
/*
* XXX Should attempt to run with fewer receive
* XXX buffers instead of just failing.
*/
pcn_rxdrain(sc);
goto out;
}
} else
PCN_INIT_RXDESC(sc, i);
}
sc->sc_rxptr = 0;
/* Initialize MODE for the initialization block. */
sc->sc_mode = 0;
if (ifp->if_flags & IFF_PROMISC)
sc->sc_mode |= LE_C15_PROM;
if ((ifp->if_flags & IFF_BROADCAST) == 0)
sc->sc_mode |= LE_C15_DRCVBC;
/*
* If we have MII, simply select MII in the MODE register,
* and clear ASEL. Otherwise, let ASEL stand (for now),
* and leave PORTSEL alone (it is ignored with ASEL is set).
*/
if (sc->sc_flags & PCN_F_HAS_MII) {
pcn_bcr_write(sc, LE_BCR2,
pcn_bcr_read(sc, LE_BCR2) & ~LE_B2_ASEL);
sc->sc_mode |= LE_C15_PORTSEL(PORTSEL_MII);
/*
* Disable MII auto-negotiation. We handle that in
* our own MII layer.
*/
pcn_bcr_write(sc, LE_BCR32,
pcn_bcr_read(sc, LE_BCR32) | LE_B32_DANAS);
}
/*
* Set the Tx and Rx descriptor ring addresses in the init
* block, the TLEN and RLEN other fields of the init block
* MODE register.
*/
sc->sc_initblock.init_rdra = htole32(PCN_CDRXADDR(sc, 0));
sc->sc_initblock.init_tdra = htole32(PCN_CDTXADDR(sc, 0));
sc->sc_initblock.init_mode = htole32(sc->sc_mode |
(((uint32_t)ffs(PCN_NTXDESC) - 1) << 28) |
((ffs(PCN_NRXDESC) - 1) << 20));
/* Set the station address in the init block. */
sc->sc_initblock.init_padr[0] = htole32(enaddr[0] |
(enaddr[1] << 8) | (enaddr[2] << 16) |
((uint32_t)enaddr[3] << 24));
sc->sc_initblock.init_padr[1] = htole32(enaddr[4] |
(enaddr[5] << 8));
/* Set the multicast filter in the init block. */
pcn_set_filter(sc);
/*
* If we have an Am79c971 or greater, initialize CSR7.
*
* XXX Might be nice to use the MII auto-poll interrupt someday.
*/
switch (sc->sc_variant->pcv_chipid) {
case PARTID_Am79c970:
case PARTID_Am79c970A:
/* Not available on these chips. */
break;
/*
* On the Am79c970A and greater, initialize BCR18 to
* enable burst mode.
*
* Also enable the "no underflow" option on the Am79c971 and
* higher, which prevents the chip from generating transmit
* underflows, yet sill provides decent performance. Note if
* chip is not connected to external SRAM, then we still have
* to handle underflow errors (the NOUFLO bit is ignored in
* that case).
*/
reg = pcn_bcr_read(sc, LE_BCR18);
switch (sc->sc_variant->pcv_chipid) {
case PARTID_Am79c970:
break;
case PARTID_Am79c970A:
reg |= LE_B18_BREADE | LE_B18_BWRITE;
break;
/*
* Set up the 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 bits select the word, while the rest
* of the bits select the bit within the word.
*/
ETHER_LOCK(ec);
ETHER_FIRST_MULTI(step, ec, enm);
while (enm != NULL) {
if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) {
/*
* 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.)
*/
ETHER_UNLOCK(ec);
goto allmulti;
}
/*
* pcn_79c970_mediastatus: [ifmedia interface function]
*
* Get the current interface media status (Am79c970 version).
*/
static void
pcn_79c970_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
{
struct pcn_softc *sc = ifp->if_softc;
/*
* The currently selected media is always the active media.
* Note: We have no way to determine what media the AUTO
* process picked.
*/
ifmr->ifm_active = sc->sc_mii.mii_media.ifm_media;
}
/*
* pcn_79c970_mediachange: [ifmedia interface function]
*
* Set hardware to newly-selected media (Am79c970 version).
*/
static int
pcn_79c970_mediachange(struct ifnet *ifp)
{
struct pcn_softc *sc = ifp->if_softc;
uint32_t reg;
if (IFM_SUBTYPE(sc->sc_mii.mii_media.ifm_media) == IFM_AUTO) {
/*
* CSR15:PORTSEL doesn't matter. Just set BCR2:ASEL.
*/
reg = pcn_bcr_read(sc, LE_BCR2);
reg |= LE_B2_ASEL;
pcn_bcr_write(sc, LE_BCR2, reg);
} else {
/*
* Clear BCR2:ASEL and set the new CSR15:PORTSEL value.
*/
reg = pcn_bcr_read(sc, LE_BCR2);
reg &= ~LE_B2_ASEL;
pcn_bcr_write(sc, LE_BCR2, reg);
/*
* pcn_79c971_mediainit:
*
* Initialize media for the Am79c971.
*/
static void
pcn_79c971_mediainit(struct pcn_softc *sc)
{
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
struct mii_data * const mii = &sc->sc_mii;
/* We have MII. */
sc->sc_flags |= PCN_F_HAS_MII;
/*
* The built-in 10BASE-T interface is mapped to the MII
* on the PCNet-FAST. Unfortunately, there's no EEPROM
* word that tells us which PHY to use.
* This driver used to ignore all but the first PHY to
* answer, but this code was removed to support multiple
* external PHYs. As the default instance will be the first
* one to answer, no harm is done by letting the possibly
* non-connected internal PHY show up.
*/
/* Initialize our media structures and probe the MII. */
mii->mii_ifp = ifp;
mii->mii_readreg = pcn_mii_readreg;
mii->mii_writereg = pcn_mii_writereg;
mii->mii_statchg = pcn_mii_statchg;