/*-
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Frank van der Linden.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
int ex_mii_readreg(device_t, int, int, uint16_t *);
int ex_mii_writereg(device_t, int, int, uint16_t);
void ex_mii_statchg(struct ifnet *);
void ex_probemedia(struct ex_softc *);
/*
* Structure to map media-present bits in boards to ifmedia codes and
* printable media names. Used for table-driven ifmedia initialization.
*/
struct ex_media {
int exm_mpbit; /* media present bit */
const char *exm_name; /* name of medium */
int exm_ifmedia; /* ifmedia word for medium */
int exm_epmedia; /* ELINKMEDIA_* constant */
};
val = ex_read_eeprom(sc, EEPROM_OEM_ADDR0);
macaddr[0] = val >> 8;
macaddr[1] = val & 0xff;
val = ex_read_eeprom(sc, EEPROM_OEM_ADDR1);
macaddr[2] = val >> 8;
macaddr[3] = val & 0xff;
val = ex_read_eeprom(sc, EEPROM_OEM_ADDR2);
macaddr[4] = val >> 8;
macaddr[5] = val & 0xff;
/*
* Create the transmit buffer DMA maps.
*/
for (i = 0; i < EX_NDPD; i++) {
if ((error = bus_dmamap_create(sc->sc_dmat, MCLBYTES,
EX_NTFRAGS, MCLBYTES, 0, BUS_DMA_NOWAIT,
&sc->sc_tx_dmamaps[i])) != 0) {
aprint_error_dev(sc->sc_dev,
"can't create tx DMA map %d, error = %d\n",
i, error);
goto fail;
}
}
attach_stage = 9;
/*
* Create the receive buffer DMA maps.
*/
for (i = 0; i < EX_NUPD; i++) {
if ((error = bus_dmamap_create(sc->sc_dmat, MCLBYTES,
EX_NRFRAGS, MCLBYTES, 0, BUS_DMA_NOWAIT,
&sc->sc_rx_dmamaps[i])) != 0) {
aprint_error_dev(sc->sc_dev,
"can't create rx DMA map %d, error = %d\n",
i, error);
goto fail;
}
}
attach_stage = 10;
/*
* Create ring of upload descriptors, only once. The DMA engine
* will loop over this when receiving packets, stalling if it
* hits an UPD with a finished receive.
*/
for (i = 0; i < EX_NUPD; i++) {
sc->sc_rxdescs[i].rx_dmamap = sc->sc_rx_dmamaps[i];
sc->sc_rxdescs[i].rx_upd = &sc->sc_upd[i];
sc->sc_upd[i].upd_frags[0].fr_len =
htole32((MCLBYTES - 2) | EX_FR_LAST);
if (ex_add_rxbuf(sc, &sc->sc_rxdescs[i]) != 0) {
aprint_error_dev(sc->sc_dev,
"can't allocate or map rx buffers\n");
goto fail;
}
}
GO_WINDOW(3);
val = bus_space_read_2(iot, ioh, ELINK_W3_RESET_OPTIONS);
if (val & ELINK_MEDIACAP_MII)
sc->ex_conf |= EX_CONF_MII;
ifp = &sc->sc_ethercom.ec_if;
/*
* Initialize our media structures and MII info. We'll
* probe the MII if we discover that we have one.
*/
mii->mii_ifp = ifp;
mii->mii_readreg = ex_mii_readreg;
mii->mii_writereg = ex_mii_writereg;
mii->mii_statchg = ex_mii_statchg;
sc->sc_ethercom.ec_mii = mii;
ifmedia_init(&mii->mii_media, IFM_IMASK, ex_media_chg, ex_media_stat);
if (sc->ex_conf & EX_CONF_MII) {
/*
* Find PHY, extract media information from it.
* First, select the right transceiver.
*/
ex_set_xcvr(sc, val);
if (pmf_device_register1(sc->sc_dev, NULL, NULL, ex_shutdown))
pmf_class_network_register(sc->sc_dev, &sc->sc_ethercom.ec_if);
else
aprint_error_dev(sc->sc_dev,
"couldn't establish power handler\n");
/* The attach is successful. */
sc->ex_flags |= EX_FLAGS_ATTACHED;
return;
fail:
/*
* Free any resources we've allocated during the failed attach
* attempt. Do this in reverse order and fall though.
*/
switch (attach_stage) {
case 11:
{
struct ex_rxdesc *rxd;
for (i = 0; i < EX_NUPD; i++) {
rxd = &sc->sc_rxdescs[i];
if (rxd->rx_mbhead != NULL) {
bus_dmamap_unload(sc->sc_dmat, rxd->rx_dmamap);
m_freem(rxd->rx_mbhead);
}
}
}
/* FALLTHROUGH */
case 10:
for (i = 0; i < EX_NUPD; i++)
bus_dmamap_destroy(sc->sc_dmat, sc->sc_rx_dmamaps[i]);
/* FALLTHROUGH */
case 9:
for (i = 0; i < EX_NDPD; i++)
bus_dmamap_destroy(sc->sc_dmat, sc->sc_tx_dmamaps[i]);
/* FALLTHROUGH */
case 8:
bus_dmamap_unload(sc->sc_dmat, sc->sc_dpd_dmamap);
/* FALLTHROUGH */
case 7:
bus_dmamap_destroy(sc->sc_dmat, sc->sc_dpd_dmamap);
/* FALLTHROUGH */
/* Sanity check that there are any media! */
if ((reset_options & ELINK_PCI_MEDIAMASK) == 0) {
aprint_error_dev(sc->sc_dev, "no media present!\n");
ifmedia_add(ifm, IFM_ETHER | IFM_NONE, 0, NULL);
ifmedia_set(ifm, IFM_ETHER | IFM_NONE);
return;
}
/* Turn on PHY power. */
if (sc->ex_conf & (EX_CONF_PHY_POWER | EX_CONF_INV_LED_POLARITY)) {
val = bus_space_read_2(iot, ioh, ELINK_W2_RESET_OPTIONS);
if (sc->ex_conf & EX_CONF_PHY_POWER)
val |= ELINK_RESET_OPT_PHYPOWER; /* turn on PHY power */
if (sc->ex_conf & EX_CONF_INV_LED_POLARITY)
val |= ELINK_RESET_OPT_LEDPOLAR; /* invert LED polarity */
bus_space_write_2(iot, ioh, ELINK_W2_RESET_OPTIONS, val);
}
/*
* Set the station address and clear the station mask. The latter
* is needed for 90x cards, 0 is the default for 90xB cards.
*/
for (i = 0; i < ETHER_ADDR_LEN; i++) {
bus_space_write_1(iot, ioh, ELINK_W2_ADDR_0 + i,
CLLADDR(ifp->if_sadl)[i]);
bus_space_write_1(iot, ioh, ELINK_W2_RECVMASK_0 + i, 0);
}
/*
* The Tx Complete interrupts occur only on errors,
* and this is the error handler.
*/
static void
ex_txstat(struct ex_softc *sc)
{
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
int i, err = 0;
/*
* We need to read+write TX_STATUS until we get a 0 status
* in order to turn off the interrupt flag.
* ELINK_TXSTATUS is in the upper byte of 2 with ELINK_TIMER.
*/
for (;;) {
i = bus_space_read_2(iot, ioh, ELINK_TIMER);
if ((i & TXS_COMPLETE) == 0)
break;
bus_space_write_2(iot, ioh, ELINK_TIMER, 0x0);
err |= i;
}
err &= ~TXS_TIMER;
if ((err & (TXS_UNDERRUN | TXS_JABBER | TXS_RECLAIM))
|| err == 0 /* should not happen, just in case */) {
/*
* Make sure the transmission is stopped.
*/
bus_space_write_2(iot, ioh, ELINK_COMMAND, ELINK_DNSTALL);
for (i = 1000; i > 0; i--)
if ((bus_space_read_4(iot, ioh, ELINK_DMACTRL) &
ELINK_DMAC_DNINPROG) == 0)
break;
/*
* Reset the transmitter.
*/
bus_space_write_2(iot, ioh, ELINK_COMMAND, TX_RESET);
/* Resetting takes a while and we will do more than wait. */
/*
* Get currently-selected media from card.
* (if_media callback, may be called before interface is brought up).
*/
void
ex_media_stat(struct ifnet *ifp, struct ifmediareq *req)
{
struct ex_softc *sc = ifp->if_softc;
uint16_t help;
/*
* We're finished if there is nothing more to add to the list or if
* we're all filled up with buffers to transmit.
*/
while (sc->tx_free != NULL) {
/*
* Grab a packet to transmit.
*/
IFQ_DEQUEUE(&ifp->if_snd, mb_head);
if (mb_head == NULL)
break;
/*
* mb_head might be updated later,
* so preserve csum_flags here.
*/
m_csumflags = mb_head->m_pkthdr.csum_flags;
/*
* Get pointer to next available tx desc.
*/
txp = sc->tx_free;
dmamap = txp->tx_dmamap;
/*
* Go through each of the mbufs in the chain and initialize
* the transmit buffer descriptors with the physical address
* and size of the mbuf.
*/
reload:
error = bus_dmamap_load_mbuf(sc->sc_dmat, dmamap,
mb_head, BUS_DMA_WRITE | BUS_DMA_NOWAIT);
switch (error) {
case 0:
/* Success. */
break;
case EFBIG:
{
struct mbuf *mn;
/*
* We ran out of segments. We have to recopy this
* mbuf chain first. Bail out if we can't get the
* new buffers.
*/
aprint_error_dev(sc->sc_dev, "too many segments, ");
if (stat & HOST_ERROR) {
aprint_error_dev(sc->sc_dev,
"adapter failure (%x)\n", stat);
ex_reset(sc);
ex_init(ifp);
return 1;
}
if (stat & UPD_STATS) {
ex_getstats(sc);
}
if (stat & TX_COMPLETE) {
ex_txstat(sc);
#if 0
if (stat & DN_COMPLETE)
aprint_error_dev(sc->sc_dev,
"Ignoring Dn interrupt (%x)\n", stat);
#endif
/*
* In some rare cases, both Tx Complete and
* Dn Complete bits are set. However, the packet
* has been reloaded in ex_txstat() and should not
* handle the Dn Complete event here.
* Hence the "else" below.
*/
} else if (stat & DN_COMPLETE) {
struct ex_txdesc *txp, *ptxp = NULL;
bus_dmamap_t txmap;
/* reset watchdog timer, was set in ex_start() */
ifp->if_timer = 0;
if (pktstat & EX_UPD_COMPLETE) {
/*
* Remove first packet from the chain.
*/
sc->rx_head = rxd->rx_next;
rxd->rx_next = NULL;
/*
* Add a new buffer to the receive chain.
* If this fails, the old buffer is recycled
* instead.
*/
if (ex_add_rxbuf(sc, rxd) == 0) {
uint16_t total_len;
GO_WINDOW(6);
upperok = bus_space_read_1(iot, ioh, UPPER_FRAMES_OK);
if_statadd_ref(ifp, nsr, if_opackets,
bus_space_read_1(iot, ioh, TX_FRAMES_OK));
if_statadd_ref(ifp, nsr, if_opackets, (upperok & 0x30) << 4);
if_statadd_ref(ifp, nsr, if_ierrors,
bus_space_read_1(iot, ioh, RX_OVERRUNS));
if_statadd_ref(ifp, nsr, if_collisions,
bus_space_read_1(iot, ioh, TX_COLLISIONS));
/*
* There seems to be no way to get the exact number of collisions,
* this is the number that occurred at the very least.
*/
if_statadd_ref(ifp, nsr, if_collisions,
2 * bus_space_read_1(iot, ioh, TX_AFTER_X_COLLISIONS));
IF_STAT_PUTREF(ifp);
/*
* Interface byte counts are counted by ether_input() and
* ether_output(), so don't accumulate them here. Just
* read the NIC counters so they don't generate overflow interrupts.
* Upper byte counters are latched from reading the totals, so
* they don't need to be read if we don't need their values.
*/
(void)bus_space_read_2(iot, ioh, RX_TOTAL_OK);
(void)bus_space_read_2(iot, ioh, TX_TOTAL_OK);
if (sc->ex_conf & EX_CONF_MII)
mii_tick(&sc->ex_mii);
if (!(bus_space_read_2((sc)->sc_iot, (sc)->sc_ioh, ELINK_STATUS)
& COMMAND_IN_PROGRESS))
ex_getstats(sc);
splx(s);
callout_schedule(&sc->ex_mii_callout, hz);
}
void
ex_reset(struct ex_softc *sc)
{
uint16_t val = GLOBAL_RESET;
if (sc->ex_conf & EX_CONF_RESETHACK)
val |= 0x10;
bus_space_write_2(sc->sc_iot, sc->sc_ioh, ELINK_COMMAND, val);
/*
* XXX apparently the command in progress bit can't be trusted
* during a reset, so we just always wait this long. Fortunately
* we normally only reset the chip during autoconfig.
*/
delay(100000);
ex_waitcmd(sc);
}
/* Delete all remaining media. */
ifmedia_fini(&sc->ex_mii.mii_media);
for (i = 0; i < EX_NUPD; i++) {
rxd = &sc->sc_rxdescs[i];
if (rxd->rx_mbhead != NULL) {
bus_dmamap_unload(sc->sc_dmat, rxd->rx_dmamap);
m_freem(rxd->rx_mbhead);
rxd->rx_mbhead = NULL;
}
}
for (i = 0; i < EX_NUPD; i++)
bus_dmamap_destroy(sc->sc_dmat, sc->sc_rx_dmamaps[i]);
for (i = 0; i < EX_NDPD; i++)
bus_dmamap_destroy(sc->sc_dmat, sc->sc_tx_dmamaps[i]);
bus_dmamap_unload(sc->sc_dmat, sc->sc_dpd_dmamap);
bus_dmamap_destroy(sc->sc_dmat, sc->sc_dpd_dmamap);
bus_dmamem_unmap(sc->sc_dmat, (void *)sc->sc_dpd,
EX_NDPD * sizeof (struct ex_dpd));
bus_dmamem_free(sc->sc_dmat, &sc->sc_dseg, sc->sc_drseg);
bus_dmamap_unload(sc->sc_dmat, sc->sc_upd_dmamap);
bus_dmamap_destroy(sc->sc_dmat, sc->sc_upd_dmamap);
bus_dmamem_unmap(sc->sc_dmat, (void *)sc->sc_upd,
EX_NUPD * sizeof (struct ex_upd));
bus_dmamem_free(sc->sc_dmat, &sc->sc_useg, sc->sc_urseg);
pmf_device_deregister(sc->sc_dev);
return (0);
}
/*
* Before reboots, reset card completely.
*/
static bool
ex_shutdown(device_t self, int flags)
{
struct ex_softc *sc = device_private(self);
ex_stop(&sc->sc_ethercom.ec_if, 1);
/*
* Make sure the interface is powered up when we reboot,
* otherwise firmware on some systems gets really confused.
*/
(void) ex_enable(sc);
return true;
}
/*
* Read EEPROM data.
* XXX what to do if EEPROM doesn't unbusy?
*/
uint16_t
ex_read_eeprom(struct ex_softc *sc, int offset)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
uint16_t data = 0, cmd = READ_EEPROM;
int off;
GO_WINDOW(0);
if (ex_eeprom_busy(sc))
goto out;
bus_space_write_2(iot, ioh, ELINK_W0_EEPROM_COMMAND,
cmd | (off + (offset & 0x3f)));
if (ex_eeprom_busy(sc))
goto out;
data = bus_space_read_2(iot, ioh, ELINK_W0_EEPROM_DATA);
out:
return data;
}
static int
ex_eeprom_busy(struct ex_softc *sc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
int i = 100;
while (i--) {
if (!(bus_space_read_2(iot, ioh, ELINK_W0_EEPROM_COMMAND) &
EEPROM_BUSY))
return 0;
delay(100);
}
aprint_error_dev(sc->sc_dev, "eeprom stays busy.\n");
return (1);
}
/*
* Create a new rx buffer and add it to the 'soft' rx list.
*/
static int
ex_add_rxbuf(struct ex_softc *sc, struct ex_rxdesc *rxd)
{
struct mbuf *m, *oldm;
bus_dmamap_t rxmap;
int error, rval = 0;
oldm = rxd->rx_mbhead;
rxmap = rxd->rx_dmamap;
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m != NULL) {
MCLAIM(m, &sc->sc_ethercom.ec_rx_mowner);
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_freem(m);
if (oldm == NULL)
return 1;
m = oldm;
MRESETDATA(m);
rval = 1;
}
} else {
if (oldm == NULL)
return 1;
m = oldm;
MRESETDATA(m);
rval = 1;
}
/*
* Setup the DMA map for this receive buffer.
*/
if (m != oldm) {
if (oldm != NULL)
bus_dmamap_unload(sc->sc_dmat, rxmap);
error = bus_dmamap_load(sc->sc_dmat, rxmap,
m->m_ext.ext_buf, MCLBYTES, NULL,
BUS_DMA_READ | BUS_DMA_NOWAIT);
if (error) {
aprint_error_dev(sc->sc_dev, "can't load rx buffer, error = %d\n",
error);
panic("ex_add_rxbuf"); /* XXX */
}
}
/*
* Align for data after 14 byte header.
*/
m->m_data += 2;