/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* 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
*
* 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.
*/
#include <sys/ethernet.h>
#include <sys/mac_ether.h>
#include <sys/mac_provider.h>
#include "elxl.h"
static void elxl_probe_media(elxl_t *);
static void elxl_set_rxfilter(elxl_t *);
static void elxl_set_media(elxl_t *);
static void elxl_reset(elxl_t *);
static void elxl_getstats(elxl_t *);
static int elxl_eeprom_busy(elxl_t *);
static void elxl_setup_tx(elxl_t *);
static void elxl_mii_notify(void *, link_state_t);
static int elxl_m_start(void *);
static void elxl_m_stop(void *);
static int elxl_m_promisc(void *, boolean_t);
static int elxl_m_unicst(void *, const uint8_t *);
void *);
const void *);
static void elxl_m_propinfo(void *, const char *, mac_prop_id_t,
static void elxl_error(elxl_t *, char *, ...);
static void elxl_linkcheck(void *);
static int elxl_attach(dev_info_t *);
static void elxl_detach(elxl_t *);
static void elxl_suspend(elxl_t *);
static void elxl_resume(dev_info_t *);
static int elxl_ddi_quiesce(dev_info_t *);
};
};
/*
* In theory buffers can have more flexible DMA attributes, but since
* we're just using a preallocated region with bcopy, there is little
* reason to allow for rougher alignment. (Further, the 8-byte
* alignment can allow for more efficient bcopy and similar operations
* from the buffer.)
*/
DMA_ATTR_V0, /* dma_attr_version */
0, /* dma_attr_addr_lo */
0xFFFFFFFFU, /* dma_attr_addr_hi */
0x00FFFFFFU, /* dma_attr_count_max */
8, /* dma_attr_align */
0x7F, /* 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 */
};
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
/*
* Structure to map media-present bits in boards to ifmedia codes and
* printable media names. Used for table-driven ifmedia initialization.
*/
typedef struct ex_media {
} ex_media_t;
/*
* Media table for 3c90x chips. Note that chips with MII have no
* `native' media. This is sorted in "reverse preference".
*/
{ MEDIAOPT_AUI, XCVR_SEL_AUI },
{ MEDIAOPT_BNC, XCVR_SEL_BNC },
{ MEDIAOPT_10T, XCVR_SEL_10T },
{ MEDIAOPT_100FX, XCVR_SEL_100FX },
{ MEDIAOPT_MII, XCVR_SEL_MII },
{ MEDIAOPT_100T4, XCVR_SEL_MII },
{ 0, 0 },
};
/*
* NB: There are lots of other models that *could* be supported.
* Specifically there are cardbus and miniPCI variants that could be
* easily added here, but they require special hacks and I have no
* access to the hardware required to verify them. Especially they
* seem to require some extra work in another register window, and I
* have no supporting documentation.
*/
static const struct ex_product {
} ex_products[] = {
{ 0x4500, "3c450-TX", 0 },
{ 0x7646, "3cSOHO100-TX", 0 },
{ 0x9000, "3c900-TPO", 0 },
{ 0x9001, "3c900-COMBO", 0 },
{ 0x9004, "3c900B-TPO", 0 },
{ 0x9005, "3c900B-COMBO", 0 },
{ 0x9006, "3c900B-TPC", 0 },
{ 0x900a, "3c900B-FL", 0 },
{ 0x9050, "3c905-TX", 0 },
{ 0x9051, "3c905-T4", 0 },
{ 0x9055, "3c905B-TX", 0 },
{ 0x9056, "3c905B-T4", 0 },
{ 0x9058, "3c905B-COMBO", 0 },
{ 0x905a, "3c905B-FX", 0 },
{ 0x9200, "3c905C-TX", 0 },
{ 0x9201, "3c920B-EMB", 0 },
{ 0x9202, "3c920B-EMB-WNM", 0 },
{ 0x9800, "3c980", 0 },
{ 0x9805, "3c980C-TXM", 0 },
{ 0, NULL, 0 },
};
static char *ex_priv_prop[] = {
"_media",
"_available_media",
};
};
NULL,
NULL,
NULL,
NULL,
};
/*
* Stream information
*/
/*
* Module linkage information.
*/
&mod_driverops, /* drv_modops */
"3Com EtherLink XL", /* drv_linkinfo */
&ex_devops /* drv_dev_ops */
};
MODREV_1, /* ml_rev */
};
int
_init(void)
{
int rv;
}
return (rv);
}
int
_fini(void)
{
int rv;
}
return (rv);
}
int
{
}
static void
{
for (int i = 0; i < r->r_count; i++) {
if (ed->ed_bufaddr)
}
if (r->r_paddr)
(void) ddi_dma_unbind_handle(r->r_dmah);
if (r->r_acch)
ddi_dma_mem_free(&r->r_acch);
if (r->r_dmah)
ddi_dma_free_handle(&r->r_dmah);
}
static void
{
if (dir == DDI_DMA_WRITE) {
/* transmit ring, not linked yet */
for (int i = 0; i < r->r_count; i++) {
}
} else {
/* receive is linked into a list */
for (int i = 0; i < r->r_count; i++) {
}
r->r_avail = 0;
}
}
static boolean_t
{
int i;
int rv;
unsigned ndmac;
if (rv != DDI_SUCCESS) {
return (B_FALSE);
}
if (rv != DDI_SUCCESS) {
return (B_FALSE);
}
if (rv != DDI_DMA_MAPPED) {
return (B_FALSE);
}
for (i = 0; i < count; i++) {
/* Link the high level descriptors into a ring. */
if (rv != 0) {
return (B_FALSE);
}
if (rv != DDI_SUCCESS) {
return (B_FALSE);
}
if (rv != DDI_DMA_MAPPED) {
return (B_FALSE);
}
}
elxl_reset_ring(r, dir);
return (B_TRUE);
}
static boolean_t
{
int actual;
int rv;
return (B_FALSE);
}
return (B_FALSE);
}
DDI_SUCCESS) {
return (B_FALSE);
}
return (B_TRUE);
}
static int
{
int i;
goto fail;
}
if (venid != 0x10b7) {
/* Not a 3Com part! */
goto fail;
}
for (i = 0; ex_products[i].epp_name; i++) {
ex_products[i].epp_name);
break;
}
}
/* Not a produce we know how to support */
}
goto fail;
}
if (!elxl_add_intr(sc)) {
goto fail;
}
elxl_reset(sc);
/*
* Is this a 90XB? If bit 2 (supportsLargePackets) is set, or
* bit (supportsNoTxLength) is clear, then its a 90X.
* Otherwise its a 90XB.
*/
} else {
}
goto fail;
}
goto fail;
}
/*
* The probe may have indicated MII!
*/
goto fail;
}
/*
* Note: The 90XB models can in theory support pause,
* but we're not enabling now due to lack of units for
* testing with. If this is changed, make sure to
* update the code in elxl_mii_notify to set the flow
* control field in the W3_MAC_CONTROL register.
*/
}
goto fail;
}
/*
* Note: we don't want to start link checking
* until *after* we have added the MAC handle.
*/
if (sc->ex_mediaopt &
/* Check non-MII link state once per second. */
sc->ex_linkcheck =
}
return (DDI_SUCCESS);
}
fail:
return (DDI_FAILURE);
}
/*
* Find the media present on non-MII chips, and select the one to use.
*/
static void
{
SET_WIN(3);
/*
* We modify the media_options field so that we have a
* consistent view of the media available, without worrying
* about the version of ASIC, etc.
*/
/*
* 100BASE-TX is handled differently on 90XB from 90X. Older
* parts use the external MII to provide this support.
*/
if (media_options & MEDIAOPT_100TX) {
/*
* 3Com advises that we should only ever use the
* auto mode. Notably, it seems that there should
* never be a 90XB board with the MEDIAOPT_10T bit set
* without this bit. If it happens, the driver will
* run in compatible 10BASE-T only mode.
*/
}
} else {
if (media_options & MEDIAOPT_100TX) {
/*
* If this occurs, we really want to use it like
* an MII device. Generally in this situation we
* want to use the MII exclusively, and there ought
* not be a 10bT transceiver.
*/
/*
* Additionally, some of these devices map all
* internal PHY register at *every* address, not
* just the "allowed" address 24.
*/
}
/*
* Early versions didn't have 10FL models, and used this
* bit for something else (VCO).
*/
}
if (media_options & MEDIAOPT_100T4) {
/* 100BASE-T4 units all use the MII bus. */
}
/* Save our media options. */
if (media_options & (bit)) { \
}
if (config & XCVR_SEL_100TX) {
/* Only found on 90XB. Don't use this, use AUTO instead! */
config |= XCVR_SEL_AUTO;
config &= ~XCVR_SEL_100TX;
}
/* Sanity check that there are any media! */
if ((media_options & MEDIAOPT_MASK) == 0) {
"No media present? Attempting to use default.");
/*
* This "default" may be non-sensical. At worst it should
* cause a busted link.
*/
}
/* preferred default is present, just use it */
return;
}
/* but keep trying for other more preferred options */
}
}
}
/*
* Setup transmitter parameters.
*/
static void
{
/*
* Disable reclaim threshold for 90xB, set free threshold to
* 6 * 256 = 1536 for 90x.
*/
else
/*
* We've seen underflows at the root cause of NIC hangs on
* older cards. Use a store-and-forward model to prevent that.
*/
}
/*
* Bring device up.
*/
static void
{
if (sc->ex_suspended)
return;
/* Load Tx parameters. */
/* Configure for VLAN tag sizing. */
SET_WIN(3);
} else {
}
}
/*
* Set multicast receive filter. Also take care of promiscuous mode.
* Note that *some* of this hardware is fully capable of either a 256
* or 64 bit multicast hash. However, we can't determine what the
* size of the hash table is easily, and so we are expected to be able
* to resubmit the entire list of addresses each time. This puts an
* onerous burden on the driver to maintain its list of multicast
* addresses. Since multicast stuff is usually not that performance
* sensitive, and since we don't usually have much of it, we are just
* going to skip it. We allow the upper layers to filter it, as
* needed, by setting the all-multicast bit if the hardware can do it.
* This also reduces our test burden.
*/
static void
{
if (sc->ex_suspended)
return;
/*
* Set the station address and clear the station mask. The latter
* is needed for 90x cards, 0 is the default for 90xB cards.
*/
SET_WIN(2);
for (int i = 0; i < ETHERADDRL; i++) {
PUT8(W2_STATION_MASK + i, 0);
}
if (sc->ex_mccount) {
mask |= FILTER_ALLMULTI;
}
if (sc->ex_promisc) {
mask |= FILTER_PROMISC;
}
}
static void
{
SET_WIN(4);
PUT16(W4_MEDIASTAT, 0);
drv_usecwait(800);
/*
* Now turn on the selected media/transceiver.
*/
case XCVR_SEL_10T:
drv_usecwait(800);
break;
case XCVR_SEL_BNC:
drv_usecwait(800);
break;
case XCVR_SEL_100FX:
drv_usecwait(800);
break;
case XCVR_SEL_AUI:
drv_usecwait(800);
break;
case XCVR_SEL_AUTO:
case XCVR_SEL_MII:
/*
* This is due to paranoia. If a card claims
* to default to MII, but doesn't have it set in
* media options, then we don't want to leave
* the MII active or we'll have problems derferencing
* the "mii handle".
*/
} else {
}
break;
default:
break;
}
SET_WIN(3);
configreg &= ~(XCVR_SEL_MASK);
/*
* If we're not using MII, force the full-duplex setting. MII
* based modes handle the full-duplex setting via the MII
* notify callback.
*/
if (!sc->ex_mii_active) {
mctl |= MAC_CONTROL_FDX;
} else {
mctl &= ~MAC_CONTROL_FDX;
}
}
}
/*
* Get currently-selected media from card.
* (if_media callback, may be called before interface is brought up).
*/
static void
{
if (sc->ex_mii_active) {
return;
}
case XCVR_SEL_100FX:
/* these media we can detect link on */
SET_WIN(4);
if (stat & MEDIASTAT_LINKDETECT) {
} else {
}
break;
case XCVR_SEL_10T:
/* these media we can detect link on */
SET_WIN(4);
if (stat & MEDIASTAT_LINKDETECT) {
} else {
}
break;
case XCVR_SEL_BNC:
case XCVR_SEL_AUI:
default:
/*
* For these we don't really know the answer,
* but if we lie then at least it won't cause
* ifconfig to turn off the RUNNING flag.
* This is necessary because we might
* transition from LINK_STATE_DOWN when
* switching media.
*/
break;
}
SET_WIN(3);
} else {
}
}
static int
{
return (0);
}
static int
{
if (add) {
sc->ex_mccount++;
}
} else {
sc->ex_mccount--;
if (sc->ex_mccount == 0) {
}
}
return (0);
}
static int
{
return (0);
}
static mblk_t *
{
ex_ring_t *r;
if (sc->ex_suspended) {
sc->ex_nocarrier++;
}
return (NULL);
}
if ((stat & TXSTATUS_COMPLETE) == 0) {
break;
}
if (stat & TXSTATUS_MAXCOLLISIONS) {
}
if ((stat & TXSTATUS_ERRS) != 0) {
if (stat & TXSTATUS_JABBER) {
}
if (stat & TXSTATUS_RECLAIM_ERR) {
}
if (stat & TXSTATUS_UNDERRUN) {
}
}
PUT8(REG_TXSTATUS, 0);
}
if (reset) {
}
if (paddr) {
}
}
/* first reclaim any free descriptors */
/* still processing this one, we're done */
break;
}
if (paddr == 0) {
/* done processing the entire list! */
break;
}
r->r_avail++;
}
}
/*
* If there is already a tx list, select the next desc on the list.
* Otherwise, just pick the first descriptor.
*/
continue;
}
cflags = 0;
&pflags);
if (pflags & HCK_IPV4_HDRCKSUM) {
cflags |= EX_DPD_IPCKSUM;
}
if (pflags & HCK_FULLCKSUM) {
}
}
/* Mark this descriptor is in use. We're committed now. */
r->r_avail--;
/* Accounting stuff. */
sc->ex_opackets++;
sc->ex_multixmt++;
} else {
sc->ex_brdcstxmt++;
}
}
/*
* Zero pad the frame if its too short. This
* also avoids a checksum offload bug.
*/
if (len < 30) {
}
/*
* If this our first packet so far, record the head
* of the list.
*/
}
/*
* Write the link into the previous descriptor. Note that
* if this is the first packet (so no previous queued), this
* will be benign because the previous descriptor won't be
* on any tx list. (Furthermore, we'll clear its link field
* when we do later use it.)
*/
}
/*
* Are we submitting any packets?
*/
/* Interrupt on the last packet. */
/* No packets pending, so its a new list head! */
} else {
/* We've added frames, so don't interrupt mid-list. */
}
/* Record the last descriptor. */
/* flush the entire ring - we're stopped so its safe */
}
/* Restart transmitter. */
}
return (mp);
}
static mblk_t *
{
if (stat & EX_UPD_RUNT) {
}
if (stat & EX_UPD_OVERRUN) {
}
if (stat & EX_UPD_CRCERR) {
}
if (stat & EX_UPD_ALIGNERR) {
}
if (stat & EX_UPD_OVERFLOW) {
sc->ex_toolong++;
}
return (NULL);
}
if (len < sizeof (struct ether_header)) {
return (NULL);
}
/* Allow four bytes for the VLAN header */
sc->ex_toolong++;
return (NULL);
}
sc->ex_allocbfail++;
return (NULL);
}
sc->ex_ipackets++;
sc->ex_multircv++;
} else {
sc->ex_brdcstrcv++;
}
}
/*
* Set the incoming checksum information for the packet.
*/
((stat & EX_UPD_IPCHECKED) != 0) &&
((stat & (EX_UPD_CKSUMERR)) == 0)) {
if (stat & EX_UPD_IPCHECKED) {
}
}
}
return (mp);
}
static int
{
}
return (0);
}
static void
{
}
}
static boolean_t
{
switch (cap) {
case MAC_CAPAB_HCKSUM: {
return (B_TRUE);
}
return (B_FALSE);
}
default:
return (B_FALSE);
}
}
static int
void *val)
{
int rv;
if (sc->ex_mii_active) {
return (rv);
}
switch (num) {
case MAC_PROP_DUPLEX:
break;
case MAC_PROP_SPEED:
break;
case MAC_PROP_STATUS:
break;
case MAC_PROP_PRIVATE:
char *str;
case XCVR_SEL_AUTO:
case XCVR_SEL_MII:
str = "mii";
break;
case XCVR_SEL_10T:
break;
case XCVR_SEL_BNC:
str = "bnc";
break;
case XCVR_SEL_AUI:
} else {
str = "aui";
}
break;
case XCVR_SEL_100FX:
break;
default:
str = "unknown";
break;
}
return (0);
}
/*
* This available media property is a hack, and should
* be removed when we can provide proper support for
* querying it as proposed in PSARC 2009/235. (At the
* moment the implementation lacks support for using
* MAC_PROP_POSSIBLE with private properties.)
*/
return (0);
}
break;
}
return (ENOTSUP);
}
static int
const void *val)
{
int rv;
if (sc->ex_mii_active) {
return (rv);
}
}
switch (num) {
case MAC_PROP_PRIVATE:
if (mopt & MEDIAOPT_100TX) {
} else if (mopt & MEDIAOPT_MII) {
} else {
return (EINVAL);
}
/* select media option */
if (mopt & MEDIAOPT_10T) {
} else {
return (EINVAL);
}
/* select media option */
if (mopt & MEDIAOPT_10T) {
} else {
return (EINVAL);
}
if (mopt & MEDIAOPT_100FX) {
} else {
return (EINVAL);
}
if (mopt & MEDIAOPT_100FX) {
} else {
return (EINVAL);
}
if (mopt & MEDIAOPT_BNC) {
} else {
return (EINVAL);
}
if (mopt & MEDIAOPT_AUI) {
} else {
return (EINVAL);
}
if (mopt & MEDIAOPT_10FL) {
} else {
return (EINVAL);
}
if (mopt & MEDIAOPT_10FL) {
} else {
return (EINVAL);
}
} else {
return (EINVAL);
}
goto reset;
}
break;
default:
break;
}
return (ENOTSUP);
if (!sc->ex_suspended) {
elxl_reset(sc);
if (sc->ex_running) {
}
}
return (0);
}
static void
{
if (sc->ex_mii_active)
switch (num) {
case MAC_PROP_DUPLEX:
case MAC_PROP_SPEED:
case MAC_PROP_STATUS:
break;
case MAC_PROP_PRIVATE:
break;
}
}
static int
{
if (stat == MAC_STAT_IFSPEED) {
}
if ((sc->ex_mii_active) &&
return (0);
}
switch (stat) {
case MAC_STAT_IFSPEED:
break;
case ETHER_STAT_LINK_DUPLEX:
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_OPACKETS:
break;
case MAC_STAT_RBYTES:
break;
case MAC_STAT_OBYTES:
break;
case MAC_STAT_COLLISIONS:
break;
break;
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;
case ETHER_STAT_EX_COLLISIONS:
break;
case MAC_STAT_OVERFLOWS:
break;
case MAC_STAT_UNDERFLOWS:
break;
break;
case ETHER_STAT_JABBER_ERRORS:
break;
case MAC_STAT_NORCVBUF:
break;
case MAC_STAT_OERRORS:
break;
case MAC_STAT_IERRORS:
break;
default:
return (ENOTSUP);
}
return (0);
}
static uint_t
{
if (sc->ex_suspended) {
return (DDI_INTR_UNCLAIMED);
}
return (DDI_INTR_UNCLAIMED);
}
/*
* Acknowledge interrupts.
*/
if (stat & INT_HOST_ERROR) {
/* XXX: Potentially a good spot for FMA */
elxl_reset(sc);
if (sc->ex_running)
return (DDI_INTR_CLAIMED);
}
if (stat & INT_UP_COMPLETE) {
ex_ring_t *r;
for (;;) {
sizeof (ex_pd_t), DDI_DMA_SYNC_FORKERNEL);
if ((pktstat & EX_UPD_COMPLETE) == 0) {
break;
}
/* Advance head to next packet. */
}
/* clear the upComplete status, reset other fields */
sizeof (ex_pd_t), DDI_DMA_SYNC_FORDEV);
}
/*
* If the engine stalled processing (due to
* insufficient UPDs usually), restart it.
*/
if (GET32(REG_UPLISTPTR) == 0) {
/*
* This seems that it can happen in an RX overrun
* situation.
*/
if (sc->ex_running)
}
}
if (mphead) {
}
}
if (stat & INT_DN_COMPLETE) {
}
return (DDI_INTR_CLAIMED);
}
static void
{
if (sc->ex_suspended) {
return;
}
SET_WIN(6);
/*
* We count the packets and bytes elsewhere, but we need to
* read the registers to clear them.
*/
(void) GET8(W6_RX_FRAMES);
(void) GET8(W6_TX_FRAMES);
(void) GET8(W6_UPPER_FRAMES);
(void) GET16(W6_RX_BYTES);
(void) GET16(W6_TX_BYTES);
SET_WIN(4);
/* Note: we ought to report this somewhere... */
}
static void
{
/*
* Some ASICs need a longer time (20 ms) to come properly out
* of reset. Do not reduce this value.
*
* Note that this occurs only during attach and failure recovery,
* so it should be mostly harmless.
*/
drv_usecwait(20000);
}
static void
{
if (sc->ex_suspended)
return;
/* Disable all interrupts. (0 means "none".) */
PUT_CMD(CMD_INT_ENABLE | 0);
}
static void
{
}
}
static void
{
/* This should always succeed. */
elxl_reset(sc);
if (sc->ex_running)
}
}
static void
{
/* Detach all PHYs */
}
if (sc->ex_linkcheck) {
}
}
}
}
}
/*
* Read EEPROM data. If we can't unbusy the EEPROM, then zero will be
* returned. This will probably result in a bogus node address.
*/
static uint16_t
{
SET_WIN(0);
if (elxl_eeprom_busy(sc))
goto out;
if (elxl_eeprom_busy(sc))
goto out;
out:
return (data);
}
static int
{
int i = 2000;
while (i--) {
return (0);
drv_usecwait(100);
}
return (1);
}
static void
{
drv_usecwait(1);
if (bits & i) {
} else {
val = PHYSMGMT_DIR;
}
drv_usecwait(1);
drv_usecwait(1);
drv_usecwait(1);
}
}
static void
{
/*
* We set the data bit output, and strobe the clock 32 times.
*/
drv_usecwait(1);
for (int i = 0; i < 32; i++) {
drv_usecwait(1);
drv_usecwait(1);
}
}
static uint16_t
{
int val;
return (0xffff);
SET_WIN(4);
drv_usecwait(1);
drv_usecwait(1);
PUT16(W4_PHYSMGMT, 0);
drv_usecwait(1);
drv_usecwait(1);
PUT16(W4_PHYSMGMT, 0);
drv_usecwait(1);
}
/* strobe the clock */
drv_usecwait(1);
PUT16(W4_PHYSMGMT, 0);
drv_usecwait(1);
}
/* return to output mode */
drv_usecwait(1);
return (data);
}
static void
{
return;
SET_WIN(4);
/* return to output mode */
drv_usecwait(1);
}
static void
{
int mctl;
if (!sc->ex_mii_active) {
/* If we're using some other legacy media, bail out now */
return;
}
if (!sc->ex_suspended) {
SET_WIN(3);
if (duplex == LINK_DUPLEX_FULL)
mctl |= MAC_CONTROL_FDX;
else
mctl &= ~MAC_CONTROL_FDX;
}
}
static int
{
switch (cmd) {
case DDI_ATTACH:
return (elxl_attach(dip));
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
{
switch (cmd) {
case DDI_DETACH:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
{
if (!sc->ex_suspended)
elxl_reset(sc);
return (DDI_SUCCESS);
}
static void
{
}