afe.c revision 38415b019e7f06a9abdc4c863acc6ee8263c68a1
/*
* Solaris driver for ethernet cards based on the ADMtek Centaur
*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/ethernet.h>
#include <sys/mac_ether.h>
#include "afe.h"
#include "afeimpl.h"
/*
* Driver globals.
*/
/* patchable debug flag ... must not be static! */
#ifdef DEBUG
#endif
/* table of supported devices */
static afe_card_t afe_cards[] = {
/*
* ADMtek Centaur and Comet
*/
/*
* Accton just relabels other companies' controllers
*/
/*
* Models listed here.
*/
};
/*
* Function prototypes
*/
static int afe_resume(dev_info_t *);
static int afe_quiesce(dev_info_t *);
static int afe_m_unicst(void *, const uint8_t *);
static int afe_m_promisc(void *, boolean_t);
static int afe_m_start(void *);
static void afe_m_stop(void *);
const void *);
static void afe_startmac(afe_t *);
static void afe_stopmac(afe_t *);
static void afe_resetrings(afe_t *);
static void afe_startall(afe_t *);
static void afe_stopall(afe_t *);
static void afe_resetall(afe_t *);
static void afe_destroytxbuf(afe_txbuf_t *);
static void afe_destroyrxbuf(afe_rxbuf_t *);
static int afe_allocrxring(afe_t *);
static void afe_freerxring(afe_t *);
static int afe_alloctxring(afe_t *);
static void afe_freetxring(afe_t *);
static void afe_error(dev_info_t *, char *, ...);
static void afe_setrxfilt(afe_t *);
static void afe_readsrom(afe_t *, unsigned, unsigned, char *);
static void afe_miitristate(afe_t *);
static void afe_startphy(afe_t *);
static void afe_stopphy(afe_t *);
static void afe_reportlink(afe_t *);
static void afe_checklink(afe_t *);
static void afe_checklinkcomet(afe_t *);
static void afe_checklinkcentaur(afe_t *);
static void afe_checklinkmii(afe_t *);
static void afe_disableinterrupts(afe_t *);
static void afe_enableinterrupts(afe_t *);
static void afe_reclaim(afe_t *);
#ifdef DEBUG
static void afe_dprintf(afe_t *, const char *, int, char *, ...);
#endif
static mac_callbacks_t afe_m_callbacks = {
NULL, /* mc_ioctl */
NULL, /* mc_getcapab */
NULL, /* mc_open */
NULL, /* mc_close */
};
/*
* Stream information
*/
/*
* Module linkage information.
*/
static struct modldrv afe_modldrv = {
&mod_driverops, /* drv_modops */
"ADMtek Fast Ethernet", /* drv_linkinfo */
&afe_devops /* drv_dev_ops */
};
static struct modlinkage afe_modlinkage = {
MODREV_1, /* ml_rev */
};
/*
* Device attributes.
*/
static ddi_device_acc_attr_t afe_devattr = {
};
static ddi_device_acc_attr_t afe_bufattr = {
};
static ddi_dma_attr_t afe_dma_attr = {
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.
*/
static ddi_dma_attr_t afe_dma_txattr = {
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 (afe_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);
}
/*
* Note: ADMtek boards seem to misprogram themselves with bogus
* timings, which do not seem to work properly on SPARC. We
* reprogram them zero (but only if they appear to be broken),
* which seems to at least work. Its unclear that this is a
* legal or wise practice to me, but it certainly works better
* than the original values. (I would love to hear
* suggestions for better values, or a better strategy.)
*/
}
/*
* the last entry in the card table matches every possible
* card, so the for-loop always terminates properly.
*/
for (i = 0; i < (sizeof (afe_cards) / sizeof (afe_card_t)); 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 */
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);
"fiber", 0);
/*
* 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 (!afe_initialize(afep)) {
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).
*/
/* make sure we add configure the initial filter */
/*
* Establish interrupt handler.
*/
DDI_SUCCESS) {
goto failed;
}
/* TODO: do the power management stuff */
goto failed;
}
return (DDI_SUCCESS);
}
/* failed to register with MAC */
}
if (afep->afe_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 (!afe_initialize(afep)) {
return (DDI_SUCCESS);
}
/* start the chip */
}
/* drop locks */
return (DDI_SUCCESS);
}
int
{
return (DDI_FAILURE);
}
/*
* At 66 MHz it is 16 nsec per access or more (always more)
* So we need 3,333 times to retry for 50 usec. We just
* round up to 5000 times. Unless the hardware is horked,
* it will always terminate *well* before that anyway.
*/
for (int i = 0; i < 5000; i++) {
return (DDI_SUCCESS);
}
}
/* hardware didn't quiesce - force a full reboot (PCI reset) */
return (DDI_FAILURE);
}
void
{
/* don't touch a suspended interface */
return;
}
/* stop receiver */
if (rxen) {
}
/* program promiscuous mode */
if (afep->afe_promisc)
else
/* program mac address */
if (rxen) {
}
/* program multicast filter */
} else {
}
} else {
}
/* restart receiver */
if (rxen) {
}
}
int
{
int index;
crc %= AFE_MCHASH;
/* bit within a 32-bit word */
if (add) {
} else {
}
}
return (0);
}
int
{
/* exclusive access to the card while we reprogram it */
/* save current promiscuous mode state for replay in resume */
return (0);
}
int
{
/* exclusive access to the card while we reprogram it */
return (0);
}
mblk_t *
{
}
return (NULL);
}
break;
}
}
return (mp);
}
/*
* Hardware management.
*/
static boolean_t
{
int i;
unsigned val;
for (i = 1; i < 10; i++) {
drv_usecwait(5);
break;
}
}
if (i == 10) {
return (B_FALSE);
}
/*
* Updated Centaur data sheets show that the Comet and Centaur are
* alike here (contrary to earlier versions of the data sheet).
*/
/* XXX:? chip problems */
/* par = PAR_MRLE | PAR_MRME | PAR_MWIE; */
par = 0;
switch (afep->afe_cachesize) {
case 8:
break;
case 16:
break;
case 32:
break;
default:
par |= PAR_BURST_32;
break;
}
/* enable transmit underrun auto-recovery */
/* clear the lost packet counter (cleared on read) */
return (B_TRUE);
}
/*
* Serial EEPROM access - inspired by the FreeBSD implementation.
*/
{
int i;
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++;
}
}
void
{
"factory ethernet address = %02x:%02x:%02x:%02x:%02x:%02x",
}
/*
* MII management.
*/
void
{
unsigned phyaddr;
unsigned bmcr;
unsigned bmsr;
unsigned anar;
unsigned phyidr1;
unsigned phyidr2;
unsigned nosqe = 0;
int retries;
int fiber;
int cnt;
/* ADMtek devices just use the PHY at address 1 */
if ((phyidr1 == 0x0022) &&
nosqe = 1;
/* only 983B has fiber support */
}
/* 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 |
fiber = 0;
/* if fiber is being forced, and device supports fiber... */
switch (afep->afe_forcefiber) {
case 0:
/* UTP Port */
fiber = 0;
break;
case 1:
/* Fiber Port */
fiber = 1;
break;
default:
/* fiber is 100 Mb FDX */
drv_usecwait(50);
drv_usecwait(500);
/* if fiber is active, use it */
fiber = 1;
} else {
fiber = 0;
}
break;
}
drv_usecwait(500);
}
if (fiber) {
/* fiber only supports 100FDX(?) */
bmsr &= ~(MII_STATUS_100_BASE_T4 |
}
/* assume full support for everything to start */
/* disable modes not supported in hardware */
if (!(bmsr & MII_STATUS_100_BASEX_FD)) {
afep->afe_adv_100fdx = 0;
afep->afe_cap_100fdx = 0;
}
if (!(bmsr & MII_STATUS_100_BASE_T4)) {
afep->afe_adv_100T4 = 0;
afep->afe_cap_100T4 = 0;
}
if (!(bmsr & MII_STATUS_100_BASEX)) {
afep->afe_adv_100hdx = 0;
afep->afe_cap_100hdx = 0;
}
if (!(bmsr & MII_STATUS_10_FD)) {
afep->afe_adv_10fdx = 0;
afep->afe_cap_10fdx = 0;
}
if (!(bmsr & MII_STATUS_10)) {
afep->afe_adv_10hdx = 0;
afep->afe_cap_10hdx = 0;
}
if (!(bmsr & MII_STATUS_CANAUTONEG)) {
afep->afe_adv_aneg = 0;
afep->afe_cap_aneg = 0;
}
cnt = 0;
if (afep->afe_adv_100fdx) {
cnt++;
}
if (afep->afe_adv_100T4) {
cnt++;
}
if (afep->afe_adv_100hdx) {
cnt++;
}
if (afep->afe_adv_10fdx) {
cnt++;
}
if (afep->afe_adv_10hdx) {
cnt++;
}
/*
* Make certain at least one valid link mode is selected.
*/
if (!cnt) {
return;
}
if (fiber) {
} else {
if (afep->afe_adv_100fdx) {
} else if (afep->afe_adv_100hdx) {
} else if (afep->afe_adv_10fdx) {
} else {
/* 10HDX */
bmcr = 0;
}
}
if (nosqe) {
/*
* work around for errata 983B_0416 -- duplex light flashes
* in 10 HDX. we just disable SQE testing on the device.
*/
pilr |= PILR_NOSQE;
}
/*
* schedule a query of the link status
*/
}
void
{
/* stop the phy timer */
/*
* phy in isolate & powerdown mode...
*/
/*
* mark the link state unknown
*/
if (!afep->afe_resetting) {
afep->afe_ifspeed = 0;
}
}
void
{
int changed = 0;
changed++;
}
changed++;
}
if (changed)
}
void
{
return;
if ((afep->afe_txstall_time != 0) &&
afep->afe_txstall_time = 0;
return;
}
case MODEL_COMET:
break;
case MODEL_CENTAUR:
break;
}
}
void
{
int reinit = 0;
}
}
if (xciis & XCIIS_LFAIL) {
reinit++;
}
afep->afe_ifspeed = 0;
if (reinit) {
}
return;
}
if (xciis & XCIIS_DUPLEX) {
} else {
}
}
void
{
unsigned opmode;
int reinit = 0;
return;
}
reinit++;
}
afep->afe_ifspeed = 0;
if (reinit) {
}
return;
}
if (opmode & OPM_DUPLEX) {
} else {
}
}
void
{
/* read MII state registers */
int reinit = 0;
/* 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 */
reinit = 1;
}
afep->afe_ifspeed = 0;
if (reinit) {
}
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))) {
afep->afe_ifspeed = 0;
} else {
afep->afe_ifspeed = 0;
}
}
void
{
drv_usecwait(1);
drv_usecwait(1);
}
void
{
drv_usecwait(1);
drv_usecwait(1);
}
{
drv_usecwait(1);
drv_usecwait(1);
return (bit);
}
{
/*
* ADMtek bugs ignore address decode bits -- they only
* support PHY at 1.
*/
if (phy != 1) {
return (0xffff);
}
case MODEL_COMET:
case MODEL_CENTAUR:
}
return (0xffff);
}
{
int i;
/* send the 32 bit preamble */
for (i = 0; i < 32; i++) {
}
/* send the start code - 01b */
afe_miiwritebit(afep, 0);
/* send the opcode for read, - 10b */
afe_miiwritebit(afep, 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 */
afe_miiwritebit(afep, 0);
/* read the 16 bit register value */
for (i = 0x8000; i > 0; i >>= 1) {
value <<= 1;
}
return (value);
}
{
if (phy != 1) {
return (0xffff);
}
switch (reg) {
case MII_CONTROL:
break;
case MII_STATUS:
break;
case MII_PHYIDH:
reg = CSR_PHYIDR1;
break;
case MII_PHYIDL:
reg = CSR_PHYIDR2;
break;
case MII_AN_ADVERT:
break;
case MII_AN_LPABLE:
reg = CSR_ANLPAR;
break;
case MII_AN_EXPANSION:
break;
default:
return (0);
}
}
void
{
/*
* ADMtek bugs ignore address decode bits -- they only
* support PHY at 1.
*/
if (phy != 1) {
return;
}
case MODEL_COMET:
break;
case MODEL_CENTAUR:
break;
}
}
void
{
int i;
/* send the 32 bit preamble */
for (i = 0; i < 32; i++) {
}
/* send the start code - 01b */
afe_miiwritebit(afep, 0);
/* send the opcode for write, - 01b */
afe_miiwritebit(afep, 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 */
afe_miiwritebit(afep, 0);
/* now write out our data (16 bits) */
for (i = 0x8000; i > 0; i >>= 1) {
}
/* idle mode */
}
void
{
if (phy != 1) {
return;
}
switch (reg) {
case MII_CONTROL:
break;
case MII_STATUS:
break;
case MII_PHYIDH:
reg = CSR_PHYIDR1;
break;
case MII_PHYIDL:
reg = CSR_PHYIDR2;
break;
case MII_AN_ADVERT:
break;
case MII_AN_LPABLE:
reg = CSR_ANLPAR;
break;
case MII_AN_EXPANSION:
break;
default:
return;
}
}
int
afe_m_start(void *arg)
{
/* grab exclusive access to the card */
return (0);
}
void
afe_m_stop(void *arg)
{
/* 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 */
afep->afe_rxhead = 0;
afep->afe_txreclaim = 0;
afep->afe_txsend = 0;
/* set up transmit descriptor ring */
for (i = 0; i < AFE_TXRING; i++) {
unsigned control = 0;
if (i == (AFE_TXRING - 1)) {
control |= TXCTL_ENDRING;
}
}
/* make the receive buffers available */
for (i = 0; i < AFE_RXRING; i++) {
unsigned control;
if (i == (AFE_RXRING - 1)) {
control |= RXCTL_ENDRING;
}
}
}
void
{
/* stop the phy */
}
void
{
/* make sure interrupts are disabled to begin */
/* initialize the chip */
(void) afe_initialize(afep);
/* 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
{
if (rxb) {
}
}
/*
* 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);
}
&afep->afe_rxdesc_acch);
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 afe_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 < AFE_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);
}
&afep->afe_txdesc_acch);
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 afe_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 < AFE_TXRING; i++) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
void
{
int i;
for (i = 0; i < AFE_RXRING; i++) {
}
if (afep->afe_rxbufs) {
AFE_RXRING * sizeof (afe_rxbuf_t *));
}
if (afep->afe_rxdesc_paddr)
if (afep->afe_rxdesc_acch)
if (afep->afe_rxdesc_dmah)
}
void
{
int i;
for (i = 0; i < AFE_TXRING; i++) {
}
if (afep->afe_txbufs) {
AFE_TXRING * sizeof (afe_txbuf_t *));
}
if (afep->afe_txdesc_paddr)
if (afep->afe_txdesc_acch)
if (afep->afe_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 */
if (status & INT_RXNOBUF)
}
/* transmit completed */
}
}
afep->afe_jabber++;
}
}
if (status & INT_BUSERR) {
case SR_BERR_PARITY:
break;
case SR_BERR_TARGET_ABORT:
break;
case SR_BERR_MASTER_ABORT:
break;
default:
break;
}
/* 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
{
unsigned mask = INT_WANTED;
/*
* On the Comet, this is the internal transceiver
* interrupt. We program the Comet's built-in PHY to
* enable certain interrupts.
*/
}
}
void
{
/* disable further interrupts */
/* clear any pending interrupts */
}
{
int txsend;
if (len > ETHERVLANMTU) {
return (B_TRUE);
}
if (afep->afe_txavail == 0) {
/* no more tmds */
/* enable TX interrupt */
return (B_FALSE);
}
/*
* For simplicity, we just do a copy into a preallocated
* DMA buffer.
*/
/*
* Statistics.
*/
afep->afe_opackets++;
afep->afe_multixmt++;
else
afep->afe_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.
*/
afep->afe_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;
}
afep->afe_txavail++;
/* in the most common successful case, all bits are clear */
if (status == 0)
continue;
if ((control & TXCTL_LAST) == 0)
continue;
if (status & TXSTAT_TXERR) {
afep->afe_errxmt++;
if (status & TXSTAT_JABBER) {
/* transmit jabber timeout */
}
if (status &
(TXSTAT_CARRLOST | TXSTAT_NOCARR)) {
}
if (status & TXSTAT_UFLOW) {
afep->afe_underflow++;
}
if (status & TXSTAT_LATECOL) {
}
if (status & TXSTAT_EXCOLL) {
}
}
if (status & TXSTAT_DEFER) {
afep->afe_defer_xmts++;
}
/* collision counting */
afep->afe_collisions++;
}
}
/*
* we were able to reclaim some packets, so
* disable tx interrupts
*/
}
}
}
mblk_t *
{
unsigned len;
/* limit the number of packets we process to a half ring size */
if (status & RXSTAT_OWN) {
/* chip is still chewing on it */
break;
}
/* discard the ethernet frame checksum */
(RXSTAT_FIRST | RXSTAT_LAST)) {
afep->afe_errrcv++;
/*
* Abnormal status bits detected, analyze further.
*/
(RXSTAT_LAST|RXSTAT_FIRST)) {
if (status & RXSTAT_FIRST) {
}
} else if (status & RXSTAT_DESCERR) {
} else if (status & RXSTAT_RUNT) {
} else if (status & RXSTAT_COLLSEEN) {
/* this should really be rx_late_collisions */
} else if (status & RXSTAT_DRIBBLE) {
afep->afe_align_errors++;
} else if (status & RXSTAT_CRCERR) {
afep->afe_fcs_errors++;
} else if (status & RXSTAT_OFLOW) {
afep->afe_overflow++;
}
}
else if (len > ETHERVLANMTU) {
afep->afe_errrcv++;
}
/*
* At this point, the chip thinks the packet is OK.
*/
else {
afep->afe_errrcv++;
afep->afe_norcvbuf++;
goto skip;
}
/* sync the buffer before we look at it */
afep->afe_ipackets++;
if (status & RXSTAT_GROUP) {
ETHERADDRL) == 0)
afep->afe_brdcstrcv++;
else
afep->afe_multircv++;
}
}
skip:
/* return ring entry to the hardware */
/* advance to next RMD */
}
return (mpchain);
}
int
{
unsigned val;
/* device is suspended */
return (0);
}
}
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:
*val = 0;
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_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;
case ETHER_STAT_ADV_CAP_100T4:
break;
break;
break;
case ETHER_STAT_ADV_CAP_10FDX:
break;
case ETHER_STAT_ADV_CAP_10HDX:
break;
break;
case ETHER_STAT_LP_CAP_100T4:
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;
default:
return (ENOTSUP);
}
return (0);
}
/*ARGSUSED*/
int
{
int err = 0;
if (sz == 0)
return (EINVAL);
*perm = MAC_PROP_PERM_RW;
switch (num) {
case MAC_PROP_DUPLEX:
if (sz >= sizeof (link_duplex_t)) {
} else {
}
break;
case MAC_PROP_SPEED:
} else {
}
break;
case MAC_PROP_AUTONEG:
break;
#if 0
case MAC_PROP_ADV_1000FDX_CAP:
case MAC_PROP_EN_1000FDX_CAP:
case MAC_PROP_ADV_1000HDX_CAP:
case MAC_PROP_EN_1000HDX_CAP:
/* We don't support gigabit! */
break;
#endif
case MAC_PROP_ADV_100FDX_CAP:
break;
case MAC_PROP_EN_100FDX_CAP:
break;
case MAC_PROP_ADV_100HDX_CAP:
break;
case MAC_PROP_EN_100HDX_CAP:
break;
case MAC_PROP_ADV_10FDX_CAP:
break;
case MAC_PROP_EN_10FDX_CAP:
break;
case MAC_PROP_ADV_10HDX_CAP:
break;
case MAC_PROP_EN_10HDX_CAP:
break;
case MAC_PROP_ADV_100T4_CAP:
break;
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);
AFE_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);
}
/*
* Debugging and error reporting.
*/
void
{
char buf[256];
if (dip) {
} else {
}
}
#ifdef DEBUG
void
{
char tag[64];
char buf[256];
} else {
}
}
}
#endif