/*
* Solaris driver for ethernet cards based on the Macronix 98715
*
* Copyright (c) 2007 by Garrett D'Amore <garrett@damore.org>.
* 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
* 3. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER 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 COPYRIGHT HOLDER 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.
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/ethernet.h>
#include <sys/mac_ether.h>
#include "mxfe.h"
#include "mxfeimpl.h"
/*
* Driver globals.
*/
/* patchable debug flag ... must not be static! */
#ifdef DEBUG
#endif
/* table of supported devices */
/*
* Lite-On products
*/
/*
* Macronix chips
*/
/*
* Compex (relabeled Macronix products)
*/
/*
* Models listed here
*/
};
/*
* Function prototypes
*/
static int mxfe_resume(dev_info_t *);
static int mxfe_quiesce(dev_info_t *);
static int mxfe_m_unicst(void *, const uint8_t *);
static int mxfe_m_promisc(void *, boolean_t);
static int mxfe_m_start(void *);
static void mxfe_m_stop(void *);
void *);
const void *);
static void mxfe_m_propinfo(void *, const char *, mac_prop_id_t,
static void mxfe_startmac(mxfe_t *);
static void mxfe_stopmac(mxfe_t *);
static void mxfe_resetrings(mxfe_t *);
static void mxfe_startall(mxfe_t *);
static void mxfe_stopall(mxfe_t *);
static void mxfe_resetall(mxfe_t *);
static void mxfe_destroytxbuf(mxfe_txbuf_t *);
static void mxfe_destroyrxbuf(mxfe_rxbuf_t *);
static void mxfe_send_setup(mxfe_t *);
static int mxfe_allocrxring(mxfe_t *);
static void mxfe_freerxring(mxfe_t *);
static int mxfe_alloctxring(mxfe_t *);
static void mxfe_freetxring(mxfe_t *);
static void mxfe_error(dev_info_t *, char *, ...);
static void mxfe_readsrom(mxfe_t *, unsigned, unsigned, void *);
static void mxfe_miitristate(mxfe_t *);
static void mxfe_startphy(mxfe_t *);
static void mxfe_stopphy(mxfe_t *);
static void mxfe_startphymii(mxfe_t *);
static void mxfe_startphynway(mxfe_t *);
static void mxfe_startnway(mxfe_t *);
static void mxfe_reportlink(mxfe_t *);
static void mxfe_checklink(mxfe_t *);
static void mxfe_checklinkmii(mxfe_t *);
static void mxfe_checklinknway(mxfe_t *);
static void mxfe_disableinterrupts(mxfe_t *);
static void mxfe_enableinterrupts(mxfe_t *);
static void mxfe_reclaim(mxfe_t *);
#ifdef DEBUG
static void mxfe_dprintf(mxfe_t *, const char *, int, char *, ...);
#endif
NULL,
NULL, /* mc_ioctl */
NULL, /* mc_getcapab */
NULL, /* mc_open */
NULL, /* mc_close */
};
/*
* Stream information
*/
/*
* Module linkage information.
*/
&mod_driverops, /* drv_modops */
"Macronix Fast Ethernet", /* drv_linkinfo */
&mxfe_devops /* drv_dev_ops */
};
MODREV_1, /* ml_rev */
};
/*
* Device attributes.
*/
};
};
DMA_ATTR_V0, /* dma_attr_version */
0, /* dma_attr_addr_lo */
0xFFFFFFFFU, /* dma_attr_addr_hi */
0x7FFFFFFFU, /* dma_attr_count_max */
4, /* dma_attr_align */
0x3F, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
0xFFFFFFFFU, /* dma_attr_maxxfer */
0xFFFFFFFFU, /* dma_attr_seg */
1, /* dma_attr_sgllen */
1, /* dma_attr_granular */
0 /* dma_attr_flags */
};
/*
* Tx buffers can be arbitrarily aligned. Additionally, they can
* cross a page boundary, so we use the two buffer addresses of the
* chip to provide a two-entry scatter-gather list.
*/
DMA_ATTR_V0, /* dma_attr_version */
0, /* dma_attr_addr_lo */
0xFFFFFFFFU, /* dma_attr_addr_hi */
0x7FFFFFFFU, /* dma_attr_count_max */
1, /* dma_attr_align */
0x3F, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
0xFFFFFFFFU, /* dma_attr_maxxfer */
0xFFFFFFFFU, /* dma_attr_seg */
2, /* dma_attr_sgllen */
1, /* dma_attr_granular */
0 /* dma_attr_flags */
};
/*
* Ethernet addresses.
*/
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
/*
* DDI entry points.
*/
int
_init(void)
{
int rv;
}
return (rv);
}
int
_fini(void)
{
int rv;
}
return (rv);
}
int
{
}
int
{
int i;
switch (cmd) {
case DDI_RESUME:
return (mxfe_resume(dip));
case DDI_ATTACH:
break;
default:
return (DDI_FAILURE);
}
/* this card is a bus master, reject any slave-only slot */
return (DDI_FAILURE);
}
/* PCI devices shouldn't generate hilevel interrupts */
if (ddi_intr_hilevel(dip, 0) != 0) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* the last entry in the card table matches every possible
* card, so the for-loop always terminates properly.
*/
for (i = 0; i < (sizeof (mxfe_cards) / sizeof (mxfe_card_t)); i++) {
mxfe_cards[i].card_revid)) {
cardp = &mxfe_cards[i];
}
mxfe_cards[i].card_revid)) {
cardp = &mxfe_cards[i];
break;
}
}
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* Grab the PCI cachesize -- we use this to program the
* cache-optimization bus access bits.
*/
/* this cannot fail */
/* get the interrupt block cookie */
!= DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* default properties */
"adv_autoneg_cap", 1);
"adv_100T4_cap", 1);
"adv_100fdx_cap", 1);
"adv_100hdx_cap", 1);
"adv_10fdx_cap", 1);
"adv_10hdx_cap", 1);
/*
* Enable bus master, IO space, and memory space accesses.
*/
/* we're done with this now, drop it */
/*
* Initialize interrupt kstat. This should not normally fail, since
* we don't use a persistent stat. We do it this way to avoid having
* to test for it at run time on the hot path.
*/
KSTAT_TYPE_INTR, 1, 0);
goto failed;
}
/*
* Map in the device registers.
*/
goto failed;
}
/*
* Allocate DMA resources (descriptor rings and buffers).
*/
goto failed;
}
/* Initialize the chip. */
if (!mxfe_initialize(mxfep)) {
goto failed;
}
/* Determine the number of address bits to our EEPROM. */
/*
* Get the factory ethernet address. This becomes the current
* ethernet address (it can be overridden later via ifconfig).
*/
/*
* Establish interrupt handler.
*/
DDI_SUCCESS) {
goto failed;
}
/* TODO: do the power management stuff */
goto failed;
}
return (DDI_SUCCESS);
}
/* failed to register with MAC */
}
if (mxfep->mxfe_intrstat) {
}
}
return (DDI_FAILURE);
}
int
{
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_DETACH:
return (DDI_FAILURE);
}
/* make sure hardware is quiesced */
/* clean up and shut down device */
/* clean up kstats */
/* free up any left over buffers or DMA resources */
return (DDI_SUCCESS);
case DDI_SUSPEND:
/* quiesce the hardware */
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
int
{
return (DDI_FAILURE);
}
/* re-initialize chip */
if (!mxfe_initialize(mxfep)) {
return (DDI_SUCCESS);
}
/* start the chip */
}
/* drop locks */
return (DDI_SUCCESS);
}
int
{
return (DDI_FAILURE);
}
/* just do a hard reset of everything */
return (DDI_SUCCESS);
}
/*ARGSUSED*/
int
{
/* we already receive all multicast frames */
return (0);
}
int
{
/* exclusive access to the card while we reprogram it */
/* save current promiscuous mode state for replay in resume */
MXFE_RUNNING) {
if (on)
else
}
return (0);
}
int
{
return (0);
}
mblk_t *
{
return (mp);
}
break;
}
}
return (mp);
}
/*
* Hardware management.
*/
{
int i;
unsigned val;
for (i = 1; i < 10; i++) {
drv_usecwait(5);
break;
}
}
if (i == 10) {
return (B_FALSE);
}
/* initialize busctl register */
/* set the cache alignment if its supported */
switch (mxfep->mxfe_cachesize) {
case 8:
par |= PAR_CALIGN_8;
break;
case 16:
par |= PAR_CALIGN_16;
break;
case 32:
par |= PAR_CALIGN_32;
break;
default:
}
/* leave the burst length at zero, indicating infinite burst */
/* clear the lost packet counter (cleared on read) */
/* a few other NAR bits */
if (mxfep->mxfe_promisc) {
nar |= NAR_RX_PROMISC;
} else {
nar &= ~NAR_RX_PROMISC;
}
return (B_TRUE);
}
/*
* Serial EEPROM access - inspired by the FreeBSD implementation.
*/
{
int i;
int eeread;
drv_usecwait(1);
/* command bits first */
for (i = 4; i != 0; i >>= 1) {
drv_usecwait(1);
drv_usecwait(1);
}
drv_usecwait(1);
drv_usecwait(1);
break;
}
drv_usecwait(1);
}
/* turn off accesses to the EEPROM */
}
/*
* The words in EEPROM are stored in little endian order. We
* shift bits out in big endian order, though. This requires
* a byte swap on some platforms.
*/
{
int i;
int eeread;
int readcmd;
/* too big to fit! */
return (0);
}
/* command and address bits */
for (i = 4 + addrlen; i >= 0; i--) {
drv_usecwait(1);
drv_usecwait(1);
}
for (i = 0; i < 16; i++) {
drv_usecwait(1);
word <<= 1;
word |= 1;
}
drv_usecwait(1);
}
/* turn off accesses to the EEPROM */
/*
* Fix up the endianness thing. Note that the values
* are stored in little endian format on the SROM.
*/
return (retval);
}
void
{
int i;
for (i = 0; i < len; i++) {
ptr += 2;
}
}
void
{
/* first read to get the location of mac address in srom */
/* then read the actual mac address */
"factory ethernet address = %02x:%02x:%02x:%02x:%02x:%02x",
}
void
{
switch (MXFE_MODEL(mxfep)) {
case MXFE_98713A:
break;
default:
break;
}
}
void
{
int i;
/* stop the phy timer */
switch (MXFE_MODEL(mxfep)) {
case MXFE_98713A:
for (i = 0; i < 32; i++) {
}
break;
default:
drv_usecwait(500);
break;
}
/*
* mark the link state unknown
*/
if (!mxfep->mxfe_resetting) {
mxfep->mxfe_ifspeed = 0;
}
}
/*
* NWay support.
*/
void
{
unsigned nar;
unsigned tctl;
unsigned restart;
/* this should not happen in a healthy system */
return;
}
if (mxfep->mxfe_adv_aneg == 0) {
/* not done for forced mode */
return;
}
if (restart != 0)
if (mxfep->mxfe_adv_100fdx) {
tctl |= TCTL_100FDX;
}
if (mxfep->mxfe_adv_100hdx) {
tctl |= TCTL_100HDX;
}
if (mxfep->mxfe_adv_10fdx) {
}
if (mxfep->mxfe_adv_10hdx) {
}
/* possibly we should add in support for PAUSE frames */
/* restart autonegotation */
if (restart != 0)
/* Macronix initializations from Bolo Tsai */
}
void
{
unsigned tstat;
} else {
}
/* autoneg did not complete */
} else {
}
mxfep->mxfe_ifspeed = 0;
return;
}
/*
* if the link is newly up, then we might need to set various
* mode bits, or negotiate for parameters, etc.
*/
if (mxfep->mxfe_adv_aneg) {
/* partner has NWay */
if ((anlpar & MII_ABILITY_100BASE_TX_FD) &&
mxfep->mxfe_adv_100fdx) {
} else if ((anlpar & MII_ABILITY_100BASE_TX) &&
mxfep->mxfe_adv_100hdx) {
} else if ((anlpar & MII_ABILITY_10BASE_T_FD) &&
mxfep->mxfe_adv_10fdx) {
} else if ((anlpar & MII_ABILITY_10BASE_T) &&
mxfep->mxfe_adv_10hdx) {
} else {
mxfep->mxfe_ifspeed = 0;
}
} else {
/* link partner does not have NWay */
/* just assume half duplex, since we can't detect */
if (!(tstat & TSTAT_100F)) {
} else {
}
}
} else {
/* forced modes */
if (mxfep->mxfe_adv_100fdx) {
} else if (mxfep->mxfe_adv_100hdx) {
} else if (mxfep->mxfe_adv_10fdx) {
} else if (mxfep->mxfe_adv_10hdx) {
} else {
mxfep->mxfe_ifspeed = 0;
}
}
}
void
{
/* take NWay and PHY out of reset */
drv_usecwait(500);
/* lie about the transceiver... its not really 802.3u compliant */
mxfep->mxfe_phyaddr = 0;
mxfep->mxfe_phyid = 0;
/* 100-T4 not supported with NWay */
mxfep->mxfe_adv_100T4 = 0;
mxfep->mxfe_cap_100T4 = 0;
/* make sure at least one valid mode is selected */
if ((!mxfep->mxfe_adv_100fdx) &&
(!mxfep->mxfe_adv_100hdx) &&
(!mxfep->mxfe_adv_10fdx) &&
(!mxfep->mxfe_adv_10hdx)) {
return;
}
if (mxfep->mxfe_adv_aneg == 0) {
/* forced mode */
unsigned nar;
unsigned tctl;
if (mxfep->mxfe_adv_100fdx) {
} else if (mxfep->mxfe_adv_100hdx) {
} else if (mxfep->mxfe_adv_10fdx) {
} else { /* mxfep->mxfe_adv_10hdx */
}
/* Macronix initializations from Bolo Tsai */
} else {
}
}
/*
* MII management.
*/
void
{
unsigned phyaddr;
unsigned bmcr;
unsigned bmsr;
unsigned anar;
unsigned phyidr1;
unsigned phyidr2;
int retries;
int cnt;
/* search for first PHY we can find */
break;
}
}
/*
* Generally, all Macronix based devices use an internal
* 100BASE-TX internal transceiver. If we ever run into a
* variation on this, then the following logic will need to be
* enhanced.
*
* One could question the value of the XCVR_INUSE field in the
* MII statistics.
*/
if (bmsr & MII_STATUS_100_BASE_T4) {
} else {
}
/* assume we support everything to start */
/* we reset the phy block */
/*
* wait for it to complete -- 500usec is still to short to
* bother getting the system clock involved.
*/
drv_usecwait(500);
drv_usecwait(500);
continue;
}
break;
}
if (retries == 100) {
return;
}
anar &= ~(MII_ABILITY_100BASE_T4 |
/* disable modes not supported in hardware */
if (!(bmsr & MII_STATUS_100_BASE_T4)) {
mxfep->mxfe_adv_100T4 = 0;
mxfep->mxfe_cap_100T4 = 0;
}
if (!(bmsr & MII_STATUS_100_BASEX_FD)) {
mxfep->mxfe_adv_100fdx = 0;
mxfep->mxfe_cap_100fdx = 0;
}
if (!(bmsr & MII_STATUS_100_BASEX)) {
mxfep->mxfe_adv_100hdx = 0;
mxfep->mxfe_cap_100hdx = 0;
}
if (!(bmsr & MII_STATUS_10_FD)) {
mxfep->mxfe_adv_10fdx = 0;
mxfep->mxfe_cap_10fdx = 0;
}
if (!(bmsr & MII_STATUS_10)) {
mxfep->mxfe_adv_10hdx = 0;
mxfep->mxfe_cap_10hdx = 0;
}
if (!(bmsr & MII_STATUS_CANAUTONEG)) {
mxfep->mxfe_adv_aneg = 0;
mxfep->mxfe_cap_aneg = 0;
}
cnt = 0;
if (mxfep->mxfe_adv_100T4) {
cnt++;
}
if (mxfep->mxfe_adv_100fdx) {
cnt++;
}
if (mxfep->mxfe_adv_100hdx) {
cnt++;
}
if (mxfep->mxfe_adv_10fdx) {
cnt++;
}
if (mxfep->mxfe_adv_10hdx) {
cnt++;
}
/*
* Make certain at least one valid link mode is selected.
*/
if (!cnt) {
return;
}
} else {
if (mxfep->mxfe_adv_100fdx) {
} else if (mxfep->mxfe_adv_100hdx) {
} else if (mxfep->mxfe_adv_10fdx) {
} else {
/* 10HDX */
bmcr = 0;
}
}
/*
* schedule a query of the link status
*/
}
void
{
int changed = 0;
changed++;
}
changed++;
}
changed++;
}
if (changed)
}
void
{
return;
if ((mxfep->mxfe_txstall_time != 0) &&
mxfep->mxfe_txstall_time = 0;
return;
}
switch (MXFE_MODEL(mxfep)) {
case MXFE_98713A:
break;
default:
}
}
void
{
/* read MII state registers */
/* read this twice, to clear latched link state */
if (bmsr & MII_STATUS_REMFAULT) {
}
if (bmsr & MII_STATUS_JABBERING) {
}
if ((bmsr & MII_STATUS_LINKUP) == 0) {
/* no link */
mxfep->mxfe_ifspeed = 0;
return;
}
if (!(bmcr & MII_CONTROL_ANE)) {
/* forced mode */
if (bmcr & MII_CONTROL_100MB) {
} else {
}
if (bmcr & MII_CONTROL_FDUPLEX) {
} else {
}
} else if ((!(bmsr & MII_STATUS_CANAUTONEG)) ||
(!(bmsr & MII_STATUS_ANDONE))) {
mxfep->mxfe_ifspeed = 0;
} else {
mxfep->mxfe_ifspeed = 0;
}
}
void
{
drv_usecwait(1);
drv_usecwait(1);
}
void
{
drv_usecwait(1);
drv_usecwait(1);
}
{
drv_usecwait(1);
drv_usecwait(1);
return (bit);
}
{
switch (MXFE_MODEL(mxfep)) {
case MXFE_98713A:
default:
return (0xffff);
}
}
{
int i;
/* send the 32 bit preamble */
for (i = 0; i < 32; i++) {
}
/* send the start code - 01b */
mxfe_miiwritebit(mxfep, 0);
/* send the opcode for read, - 10b */
mxfe_miiwritebit(mxfep, 0);
/* next we send the 5 bit phy address */
for (i = 0x10; i > 0; i >>= 1) {
}
/* the 5 bit register address goes next */
for (i = 0x10; i > 0; i >>= 1) {
}
/* turnaround - tristate followed by logic 0 */
mxfe_miiwritebit(mxfep, 0);
/* read the 16 bit register value */
for (i = 0x8000; i > 0; i >>= 1) {
value <<= 1;
}
return (value);
}
{
unsigned nar;
/*
* like an ordinary MII, but we have to turn off portsel while
* we read it.
*/
return (retval);
}
void
{
switch (MXFE_MODEL(mxfep)) {
case MXFE_98713A:
break;
default:
break;
}
}
void
{
int i;
/* send the 32 bit preamble */
for (i = 0; i < 32; i++) {
}
/* send the start code - 01b */
mxfe_miiwritebit(mxfep, 0);
/* send the opcode for write, - 01b */
mxfe_miiwritebit(mxfep, 0);
/* next we send the 5 bit phy address */
for (i = 0x10; i > 0; i >>= 1) {
}
/* the 5 bit register address goes next */
for (i = 0x10; i > 0; i >>= 1) {
}
/* turnaround - tristate followed by logic 0 */
mxfe_miiwritebit(mxfep, 0);
/* now write out our data (16 bits) */
for (i = 0x8000; i > 0; i >>= 1) {
}
/* idle mode */
}
void
{
unsigned nar;
/*
* like an ordinary MII, but we have to turn off portsel while
* we read it.
*/
}
int
{
/* grab exclusive access to the card */
return (0);
}
void
{
/* exclusive access to the hardware! */
}
void
{
/* verify exclusive access to the card */
/* start the card */
/* tell the mac that we are ready to go! */
}
void
{
int i;
/* exclusive access to the hardware! */
/*
* A 1518 byte frame at 10Mbps takes about 1.2 msec to drain.
* We just add up to the nearest msec (2), which should be
* plenty to complete.
*
* Note that some chips never seem to indicate the transition to
* the stopped state properly. Experience shows that we can safely
* proceed anyway, after waiting the requisite timeout.
*/
for (i = 2000; i != 0; i -= 10) {
break;
drv_usecwait(10);
}
/* prevent an interrupt */
}
void
{
int i;
/* now we need to reset the pointers... */
/* reset the descriptor ring pointers */
mxfep->mxfe_rxhead = 0;
mxfep->mxfe_txreclaim = 0;
mxfep->mxfe_txsend = 0;
/* set up transmit descriptor ring */
for (i = 0; i < MXFE_TXRING; i++) {
unsigned control = 0;
if (i == (MXFE_TXRING - 1)) {
control |= TXCTL_ENDRING;
}
}
/* make the receive buffers available */
for (i = 0; i < MXFE_RXRING; i++) {
unsigned control;
if (i == (MXFE_RXRING - 1)) {
control |= RXCTL_ENDRING;
}
}
}
void
{
/* stop the phy */
}
void
{
/* make sure interrupts are disabled to begin */
/* initialize the chip */
(void) mxfe_initialize(mxfep);
/* now we can enable interrupts */
/* start up the phy */
/* start up the mac */
}
void
{
}
{
unsigned ncookies;
return (NULL);
}
return (NULL);
}
return (NULL);
}
return (txb);
}
void
{
}
}
{
unsigned ccnt;
return (NULL);
}
return (NULL);
}
&ccnt) != DDI_DMA_MAPPED) {
return (NULL);
}
return (rxb);
}
void
{
}
}
/*
* Allocate receive resources.
*/
int
{
int rval;
int i;
unsigned ncookies;
if (rval != DDI_SUCCESS) {
"unable to allocate DMA handle for rx descriptors");
return (DDI_FAILURE);
}
if (rval != DDI_SUCCESS) {
"unable to allocate DMA memory for rx descriptors");
return (DDI_FAILURE);
}
if (rval != DDI_DMA_MAPPED) {
"unable to bind DMA for rx descriptors");
return (DDI_FAILURE);
}
/* because of mxfe_dma_attr */
/* we take the 32-bit physical address out of the cookie */
/* allocate buffer pointers (not the buffers themselves, yet) */
KM_SLEEP);
/* now allocate rx buffers */
for (i = 0; i < MXFE_RXRING; i++) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* Allocate transmit resources.
*/
int
{
int rval;
int i;
unsigned ncookies;
if (rval != DDI_SUCCESS) {
"unable to allocate DMA handle for tx descriptors");
return (DDI_FAILURE);
}
if (rval != DDI_SUCCESS) {
"unable to allocate DMA memory for tx descriptors");
return (DDI_FAILURE);
}
if (rval != DDI_DMA_MAPPED) {
"unable to bind DMA for tx descriptors");
return (DDI_FAILURE);
}
/* because of mxfe_dma_attr */
/* we take the 32-bit physical address out of the cookie */
/* allocate buffer pointers (not the buffers themselves, yet) */
KM_SLEEP);
/* now allocate tx buffers */
for (i = 0; i < MXFE_TXRING; i++) {
return (DDI_FAILURE);
/* stick it in the stack */
}
return (DDI_SUCCESS);
}
void
{
int i;
for (i = 0; i < MXFE_RXRING; i++) {
}
if (mxfep->mxfe_rxbufs) {
MXFE_RXRING * sizeof (mxfe_rxbuf_t *));
}
if (mxfep->mxfe_rxdesc_paddr)
if (mxfep->mxfe_rxdesc_acch)
if (mxfep->mxfe_rxdesc_dmah)
}
void
{
int i;
for (i = 0; i < MXFE_TXRING; i++) {
}
if (mxfep->mxfe_txbufs) {
MXFE_TXRING * sizeof (mxfe_txbuf_t *));
}
if (mxfep->mxfe_txdesc_paddr)
if (mxfep->mxfe_txdesc_acch)
if (mxfep->mxfe_txdesc_dmah)
}
/*
* Interrupt service routine.
*/
unsigned
{
/* we cannot receive interrupts! */
return (DDI_INTR_UNCLAIMED);
}
/* check interrupt status bits, did we interrupt? */
if (status == 0) {
return (DDI_INTR_UNCLAIMED);
}
/* ack the interrupt */
/* not running, don't touch anything */
return (DDI_INTR_CLAIMED);
}
/* receive packets */
}
}
/* transmit completed */
}
/* rescan the link */
}
mxfep->mxfe_jabber++;
}
}
if (status & INT_BUSERR) {
switch (status & SR_BERR_TYPE) {
case SR_BERR_PARITY:
break;
case SR_BERR_TARGET_ABORT:
break;
case SR_BERR_MASTER_ABORT:
break;
default:
break;
}
}
if (error) {
/* reset the chip in an attempt to fix things */
}
/*
* Send up packets. We do this outside of the intrlock.
*/
if (mp) {
}
return (DDI_INTR_CLAIMED);
}
void
{
if (mxfep->mxfe_wantw)
mask |= INT_LINKSTATUS;
}
void
{
/* disable further interrupts */
/* clear any pending interrupts */
}
void
{
/* setup frame -- must be at head of list -- guaranteed by caller! */
/* program the unicast address */
/* make sure that the hardware can see it */
/* sync the descriptor out to the device */
/*
* wake up the chip ... inside the lock to protect against DR suspend,
* etc.
*/
mxfep->mxfe_txsend++;
mxfep->mxfe_txavail--;
/*
* Program promiscuous mode.
*/
if (mxfep->mxfe_promisc) {
} else {
}
}
{
int txsend;
if (len > ETHERVLANMTU) {
return (B_TRUE);
}
if (mxfep->mxfe_txavail == 0) {
/* no more tmds */
/* enable TX interrupt */
return (B_FALSE);
}
/*
* For simplicity, we just do a copy into a preallocated
* DMA buffer.
*/
/*
* Statistics.
*/
mxfep->mxfe_opackets++;
mxfep->mxfe_multixmt++;
else
mxfep->mxfe_brdcstxmt++;
}
/* note len is already known to be a small unsigned */
control |= TXCTL_ENDRING;
/* sync the descriptor out to the device */
/*
* Note the new values of txavail and txsend.
*/
mxfep->mxfe_txavail--;
/*
* It should never, ever take more than 5 seconds to drain
* the ring. If it happens, then we are stuck!
*/
/*
* wake up the chip ... inside the lock to protect against DR suspend,
* etc.
*/
return (B_TRUE);
}
/*
* Reclaim buffers that have completed transmission.
*/
void
{
/* sync it before we read it */
if (status & TXSTAT_OWN) {
/* chip is still working on it, we're done */
break;
}
mxfep->mxfe_txavail++;
/* in the most common successful case, all bits are clear */
if (status == 0)
continue;
if (((control & TXCTL_SETUP) != 0) ||
((control & TXCTL_LAST) == 0)) {
/* no interesting statistics here */
continue;
}
if (status & TXSTAT_TXERR) {
mxfep->mxfe_errxmt++;
if (status & TXSTAT_JABBER) {
/* transmit jabber timeout */
}
}
if (status & TXSTAT_UFLOW) {
mxfep->mxfe_underflow++;
}
if (status & TXSTAT_LATECOL) {
}
if (status & TXSTAT_EXCOLL) {
}
}
if (status & TXSTAT_DEFER) {
mxfep->mxfe_defer_xmts++;
}
/* collision counting */
mxfep->mxfe_collisions++;
}
}
if (mxfep->mxfe_wantw) {
/*
* we were able to reclaim some packets, so
* disable tx interrupts
*/
}
}
}
{
unsigned len;
/* limit the number of packets we process to a ring size */
if (status & RXSTAT_OWN) {
/* chip is still chewing on it */
break;
}
/* discard the ethernet frame checksum */
(RXSTAT_FIRST | RXSTAT_LAST)) {
mxfep->mxfe_errrcv++;
/*
* Abnormal status bits detected, analyze further.
*/
(RXSTAT_LAST|RXSTAT_FIRST)) {
/* someone trying to send jumbo frames? */
if (status & RXSTAT_FIRST) {
}
} else if (status & RXSTAT_DESCERR) {
/* this should never occur! */
} else if (status & RXSTAT_RUNT) {
} else if (status & RXSTAT_COLLSEEN) {
/* this should really be rx_late_collisions */
} else if (status & RXSTAT_DRIBBLE) {
} else if (status & RXSTAT_CRCERR) {
mxfep->mxfe_fcs_errors++;
} else if (status & RXSTAT_OFLOW) {
/* this is a MAC FIFO error, need to reset */
mxfep->mxfe_overflow++;
}
}
else if (len > ETHERVLANMTU) {
mxfep->mxfe_errrcv++;
}
/*
* At this point, the chip thinks the packet is OK.
*/
else {
mxfep->mxfe_errrcv++;
mxfep->mxfe_norcvbuf++;
goto skip;
}
/* sync the buffer before we look at it */
mxfep->mxfe_ipackets++;
if (status & RXSTAT_GROUP) {
ETHERADDRL) == 0)
mxfep->mxfe_brdcstrcv++;
else
mxfep->mxfe_multircv++;
}
}
skip:
/* return ring entry to the hardware */
/* advance to next RMD */
}
return (error);
}
int
{
switch (stat) {
case MAC_STAT_IFSPEED:
break;
case MAC_STAT_MULTIRCV:
break;
case MAC_STAT_BRDCSTRCV:
break;
case MAC_STAT_MULTIXMT:
break;
case MAC_STAT_BRDCSTXMT:
break;
case MAC_STAT_IPACKETS:
break;
case MAC_STAT_RBYTES:
break;
case MAC_STAT_OPACKETS:
break;
case MAC_STAT_OBYTES:
break;
case MAC_STAT_NORCVBUF:
break;
case MAC_STAT_NOXMTBUF:
break;
case MAC_STAT_COLLISIONS:
break;
case MAC_STAT_IERRORS:
break;
case MAC_STAT_OERRORS:
break;
case ETHER_STAT_LINK_DUPLEX:
break;
case ETHER_STAT_ALIGN_ERRORS:
break;
case ETHER_STAT_FCS_ERRORS:
break;
case ETHER_STAT_SQE_ERRORS:
break;
case ETHER_STAT_DEFER_XMTS:
break;
break;
break;
break;
case ETHER_STAT_EX_COLLISIONS:
break;
case ETHER_STAT_MACXMT_ERRORS:
break;
break;
break;
case ETHER_STAT_MACRCV_ERRORS:
break;
case MAC_STAT_OVERFLOWS:
break;
case MAC_STAT_UNDERFLOWS:
break;
break;
case ETHER_STAT_JABBER_ERRORS:
break;
case ETHER_STAT_ADV_CAP_100T4:
break;
case ETHER_STAT_LP_CAP_100T4:
break;
case ETHER_STAT_CAP_100T4:
break;
case ETHER_STAT_CAP_100FDX:
break;
case ETHER_STAT_CAP_100HDX:
break;
case ETHER_STAT_CAP_10FDX:
break;
case ETHER_STAT_CAP_10HDX:
break;
case ETHER_STAT_CAP_AUTONEG:
break;
case ETHER_STAT_LINK_AUTONEG:
break;
break;
break;
case ETHER_STAT_ADV_CAP_10FDX:
break;
case ETHER_STAT_ADV_CAP_10HDX:
break;
break;
case ETHER_STAT_LP_CAP_100FDX:
break;
case ETHER_STAT_LP_CAP_100HDX:
break;
case ETHER_STAT_LP_CAP_10FDX:
break;
case ETHER_STAT_LP_CAP_10HDX:
break;
break;
case ETHER_STAT_XCVR_ADDR:
break;
case ETHER_STAT_XCVR_ID:
break;
case ETHER_STAT_XCVR_INUSE:
break;
default:
return (ENOTSUP);
}
return (0);
}
/*ARGSUSED*/
int
void *val)
{
int err = 0;
switch (num) {
case MAC_PROP_DUPLEX:
break;
case MAC_PROP_SPEED:
break;
case MAC_PROP_AUTONEG:
break;
case MAC_PROP_ADV_100FDX_CAP:
case MAC_PROP_EN_100FDX_CAP:
break;
case MAC_PROP_ADV_100HDX_CAP:
case MAC_PROP_EN_100HDX_CAP:
break;
case MAC_PROP_ADV_10FDX_CAP:
case MAC_PROP_EN_10FDX_CAP:
break;
case MAC_PROP_ADV_10HDX_CAP:
case MAC_PROP_EN_10HDX_CAP:
break;
case MAC_PROP_ADV_100T4_CAP:
case MAC_PROP_EN_100T4_CAP:
break;
default:
}
return (err);
}
/*ARGSUSED*/
int
const void *val)
{
switch (num) {
case MAC_PROP_EN_100FDX_CAP:
break;
case MAC_PROP_EN_100HDX_CAP:
break;
case MAC_PROP_EN_10FDX_CAP:
break;
case MAC_PROP_EN_10HDX_CAP:
break;
case MAC_PROP_EN_100T4_CAP:
break;
case MAC_PROP_AUTONEG:
break;
default:
return (ENOTSUP);
}
if (*capp == 0) /* ensure phy can support value */
return (ENOTSUP);
MXFE_RUNNING) {
/*
* This re-initializes the phy, but it also
* restarts transmit and receive rings.
* Needless to say, changing the link
* parameters is destructive to traffic in
* progress.
*/
}
}
return (0);
}
static void
{
switch (num) {
case MAC_PROP_DUPLEX:
case MAC_PROP_SPEED:
case MAC_PROP_ADV_100FDX_CAP:
case MAC_PROP_ADV_100HDX_CAP:
case MAC_PROP_ADV_10FDX_CAP:
case MAC_PROP_ADV_10HDX_CAP:
case MAC_PROP_ADV_100T4_CAP:
break;
case MAC_PROP_AUTONEG:
break;
case MAC_PROP_EN_100FDX_CAP:
break;
case MAC_PROP_EN_100HDX_CAP:
break;
case MAC_PROP_EN_10FDX_CAP:
break;
case MAC_PROP_EN_10HDX_CAP:
break;
case MAC_PROP_EN_100T4_CAP:
break;
}
}
/*
* Debugging and error reporting.
*/
void
{
if (dip) {
} else {
}
}
#ifdef DEBUG
void
{
if (mxfe_debug & level) {
} else {
}
}
}
#endif