hme.c revision 0219346b07c8d846112a335f1543309c21e3d8da
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* SunOS MT STREAMS FEPS(SBus)/Cheerio(PCI) 10/100Mb Ethernet Device Driver
*/
#include <sys/mac_provider.h>
#include <sys/mac_ether.h>
#include <sys/ethernet.h>
#include "hme_phy.h"
#include "hme_mac.h"
#include "hme.h"
typedef void (*fptrv_t)();
typedef enum {
NO_MSG = 0,
AUTOCONFIG_MSG = 1,
STREAMS_MSG = 2,
IOCTL_MSG = 3,
PROTO_MSG = 4,
INIT_MSG = 5,
TX_MSG = 6,
RX_MSG = 7,
INTR_MSG = 8,
UNINIT_MSG = 9,
CONFIG_MSG = 10,
PROP_MSG = 11,
ENTER_MSG = 12,
RESUME_MSG = 13,
AUTONEG_MSG = 14,
NAUTONEG_MSG = 15,
FATAL_ERR_MSG = 16,
NFATAL_ERR_MSG = 17,
NDD_MSG = 18,
PHY_MSG = 19,
XCVR_MSG = 20,
NOXCVR_MSG = 21,
NSUPPORT_MSG = 22,
ERX_MSG = 23,
FREE_MSG = 24,
IPG_MSG = 25,
DDI_MSG = 26,
DEFAULT_MSG = 27,
DISPLAY_MSG = 28,
LATECOLL_MSG = 29,
MIFPOLL_MSG = 30,
LINKPULSE_MSG = 31
} msg_t;
static char *msg_string[] = {
"NONE ",
"AUTOCONFIG ",
"STREAMS ",
"IOCTL ",
"PROTO ",
"INIT ",
"TX ",
"RX ",
"INTR ",
"UNINIT ",
"CONFIG ",
"PROP ",
"ENTER ",
"RESUME ",
"AUTONEG ",
"NAUTONEG ",
"FATAL_ERR ",
"NFATAL_ERR ",
"NDD ",
"PHY ",
"XCVR ",
"NOXCVR ",
"NSUPPOR ",
"ERX ",
"FREE ",
"IPG ",
"DDI ",
"DEFAULT ",
"DISPLAY "
"LATECOLL_MSG ",
"MIFPOLL_MSG ",
"LINKPULSE_MSG "
};
#define SEVERITY_NONE 0
#define SEVERITY_LOW 0
#define SEVERITY_MID 1
#define SEVERITY_HIGH 2
#define SEVERITY_UNKNOWN 99
#define FEPS_URUN_BUG
#define HME_CODEVIOL_BUG
/*
*/
static int hme_urun_fix = 0; /* Bug fixed in Sbus/FEPS 2.0 */
/*
* The following variables are used for configuring various features
*/
/*
* The following variables are used for configuring link-operation.
* Later these parameters may be changed per interface using "ndd" command
* These parameters may also be specified as properties using the .conf
* file mechanism for each interface.
*/
static int hme_ipg0 = 16;
static int hme_ipg1 = 8;
static int hme_ipg2 = 4;
static int hme_use_int_xcvr = 0;
static int hme_pace_size = 0; /* Do not use pacing */
/*
* The following variable value will be overridden by "link-pulse-disabled"
* property which may be created by OBP or hme.conf file.
*/
static int hme_link_pulse_disabled = 0; /* link pulse disabled */
/*
* The following parameters may be configured by the user. If they are not
* configured by the user, the values will be based on the capabilities of
* the transceiver.
* The value "HME_NOTUSR" is ORed with the parameter value to indicate values
* which are NOT configured by the user.
*/
#define HME_NOTUSR 0x0f000000
#define HME_MASK_1BIT 0x1
#define HME_MASK_5BIT 0x1f
#define HME_MASK_8BIT 0xff
static int hme_adv_autoneg_cap = HME_NOTUSR | 0;
static int hme_adv_100T4_cap = HME_NOTUSR | 0;
static int hme_adv_100fdx_cap = HME_NOTUSR | 0;
static int hme_adv_100hdx_cap = HME_NOTUSR | 0;
static int hme_adv_10fdx_cap = HME_NOTUSR | 0;
static int hme_adv_10hdx_cap = HME_NOTUSR | 0;
/*
* PHY_IDR1 and PHY_IDR2 values to identify National Semiconductor's DP83840
* Rev C chip which needs some work-arounds.
*/
#define HME_NSIDR1 0x2000
/*
* PHY_IDR1 and PHY_IDR2 values to identify Quality Semiconductor's QS6612
* chip which needs some work-arounds.
* Addition Interface Technologies Group (NPG) 8/28/1997.
*/
#define HME_QSIDR1 0x0181
/*
* The least significant 4 bits of HME_NSIDR2 represent the revision
* no. of the DP83840 chip. For Rev-C of DP83840, the rev. no. is 0.
* The next revision of the chip is called DP83840A and the value of
* HME_NSIDR2 is 0x5c01 for this new chip. All the workarounds specific
* to DP83840 chip are valid for both the revisions of the chip.
* Assuming that these workarounds are valid for the future revisions
* also, we will apply these workarounds independent of the revision no.
* Hence we mask out the last 4 bits of the IDR2 register and compare
* with 0x5c00 value.
*/
/*
* Likewise for the QSI 6612 Fast ethernet phy.
* Addition Interface Technologies Group (NPG) 8/28/1997.
*/
/*
* All strings used by hme messaging functions
*/
static char *par_detect_msg =
"Parallel detection fault.";
static char *xcvr_no_mii_msg =
"Transceiver does not talk MII.";
static char *xcvr_isolate_msg =
"Transceiver isolate failed.";
static char *int_xcvr_msg =
"Internal Transceiver Selected.";
static char *ext_xcvr_msg =
"External Transceiver Selected.";
static char *no_xcvr_msg =
"No transceiver found.";
static char *burst_size_msg =
"Could not identify the burst size";
static char *unk_rx_ringsz_msg =
"Unknown receive RINGSZ";
static char *add_intr_fail_msg =
"ddi_add_intr(9F) failed";
static char *mregs_4global_reg_fail_msg =
"ddi_regs_map_setup(9F) for global reg failed";
static char *mregs_4etx_reg_fail_msg =
"ddi_map_regs for etx reg failed";
static char *mregs_4erx_reg_fail_msg =
"ddi_map_regs for erx reg failed";
static char *mregs_4bmac_reg_fail_msg =
"ddi_map_regs for bmac reg failed";
static char *mregs_4mif_reg_fail_msg =
"ddi_map_regs for mif reg failed";
static char *param_reg_fail_msg =
"parameter register error";
static char *init_fail_gen_msg =
static char *ddi_nregs_fail_msg =
"ddi_dev_nregs failed(9F), returned %d";
static char *bad_num_regs_msg =
"Invalid number of registers.";
static char *anar_not_set_msg =
"External Transceiver: anar not set with speed selection";
static char *par_detect_anar_not_set_msg =
"External Transceiver: anar not set with speed selection";
/* FATAL ERR msgs */
/*
* Function prototypes.
*/
/* these two are global so that qfe can use them */
int hmequiesce(dev_info_t *);
static void hmestatinit(struct hme *);
static int hmeallocthings(struct hme *);
static void hmefreethings(struct hme *);
static int hmeallocbufs(struct hme *);
static void hmefreebufs(struct hme *);
static void hmeget_hm_rev_property(struct hme *);
static void hmereclaim(struct hme *);
static void hmesavecntrs(struct hme *);
static int hmeburstsizes(struct hme *);
static void hme_start_mifpoll(struct hme *);
static void hme_stop_mifpoll(struct hme *);
static void hme_param_cleanup(struct hme *);
static void hme_bb_force_idle(struct hme *);
static void hme_stop_timer(struct hme *);
static int hme_select_speed(struct hme *, int);
static void hme_reset_transceiver(struct hme *);
static void hme_check_transceiver(struct hme *);
static void hme_setup_link_default(struct hme *);
static void hme_setup_link_status(struct hme *);
static void hme_setup_link_control(struct hme *);
static void hme_check_link(void *);
static void hme_init_xcvr_info(struct hme *);
static void hme_disable_link_pulse(struct hme *);
static void hme_force_speed(void *);
static void hme_get_autoinfo(struct hme *);
static int hme_try_auto_negotiation(struct hme *);
static void hme_try_speed(void *);
static void hme_link_now_up(struct hme *);
/*
* Nemo (GLDv3) Functions.
*/
static int hme_m_start(void *);
static void hme_m_stop(void *);
static int hme_m_promisc(void *, boolean_t);
static int hme_m_unicst(void *, const uint8_t *);
static mac_callbacks_t hme_m_callbacks = {
};
#define HME_FAULT_MSG1(p, s, t, f) \
hme_fault_msg((p), (s), (t), (f));
#define HME_FAULT_MSG2(p, s, t, f, a) \
hme_fault_msg((p), (s), (t), (f), (a));
#define HME_FAULT_MSG3(p, s, t, f, a, b) \
hme_fault_msg((p), (s), (t), (f), (a), (b));
#define HME_FAULT_MSG4(p, s, t, f, a, b, c) \
hme_fault_msg((p), (s), (t), (f), (a), (b), (c));
#define CHECK_MIFREG() \
#define CHECK_ETXREG() \
#define CHECK_ERXREG() \
#define CHECK_MACREG() \
#define CHECK_GLOBREG() \
/*
* Claim the device is ultra-capable of burst in the beginning. Use
* the value returned by ddi_dma_burstsizes() to actually set the HME
* global configuration register later.
*
* 32-bit and 64-bit Sbus transfers. Hence the dlim_burstsizes field contains
* the the burstsizes in both the lo and hi words.
*/
/*
* Note that rx and tx data buffers can be arbitrarily aligned, but
* that the descriptor rings need to be aligned on 2K boundaries, per
* the spec.
*/
static ddi_dma_attr_t hme_dma_attr = {
DMA_ATTR_V0, /* version number. */
512, /* granularity */
0 /* attribute flags */
};
static ddi_device_acc_attr_t hme_buf_attr = {
DDI_STRICTORDER_ACC, /* probably could allow merging & caching */
};
static uchar_t pci_latency_timer = 0;
/*
* Module linkage information for the kernel.
*/
&mod_driverops, /* Type of module. This one is a driver */
"Sun HME 10/100 Mb Ethernet",
&hme_dev_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
};
/*
* Internal PHY Id:
*/
/* <<<<<<<<<<<<<<<<<<<<<< Register operations >>>>>>>>>>>>>>>>>>>>> */
#define GET_MIFREG(reg) \
#define GET_ETXREG(reg) \
#define GET_ERXREG(reg) \
#define GET_MACREG(reg) \
#define GET_GLOBREG(reg) \
#define GET_TMD_FLAGS(ptr) \
#define GET_RMD_FLAGS(ptr) \
/*
* Ether_copy is not endian-correct. Define an endian-correct version.
*/
/*
* Ether-type is specifically big-endian, but data region is unknown endian
*/
#define get_ether_type(ptr) \
/* <<<<<<<<<<<<<<<<<<<<<< Configuration Parameters >>>>>>>>>>>>>>>>>>>>> */
static int jamsize = BMAC_DEFAULT_JAMSIZE;
/*
* Calculate the bit in the multicast address filter that selects the given
* address.
*/
static uint32_t
{
/*
* Just want the 6 most significant bits.
*/
return (crc >> 26);
}
/* <<<<<<<<<<<<<<<<<<<<<<<< Bit Bang Operations >>>>>>>>>>>>>>>>>>>>>>>> */
static void
{
PUT_MIFREG(mif_bbdata, x);
}
/*
* To read the MII register bits from the Babybac1 transceiver
*/
static uint32_t
{
uint32_t x;
else
return (x);
}
/*
* To read the MII register bits according to the IEEE Standard
*/
static uint32_t
{
uint32_t x;
else
return (x);
}
static void
{
int i;
(void) hme_bb_force_idle(hmep);
for (i = 4; i >= 0; i--) { /* <AAAAA> */
}
for (i = 4; i >= 0; i--) { /* <RRRRR> */
}
for (i = 0xf; i >= 0; i--) { /* <DDDDDDDDDDDDDDDD> */
}
CHECK_MIFREG();
}
/* Return 0 if OK, 1 if error (Transceiver does not talk management) */
static uint_t
{
int i;
uint32_t x;
uint32_t y;
*datap = 0;
(void) hme_bb_force_idle(hmep);
for (i = 4; i >= 0; i--) { /* <AAAAA> */
}
for (i = 4; i >= 0; i--) { /* <RRRRR> */
}
if ((hme_internal_phy_id == HME_BB2) ||
GET_BIT_STD(x);
GET_BIT_STD(y); /* <TA> */
for (i = 0xf; i >= 0; i--) { /* <DDDDDDDDDDDDDDDD> */
GET_BIT_STD(x);
*datap += (x << i);
}
/*
* Kludge to get the Transceiver out of hung mode
*/
GET_BIT_STD(x);
GET_BIT_STD(x);
GET_BIT_STD(x);
} else {
GET_BIT(x);
GET_BIT(y); /* <TA> */
for (i = 0xf; i >= 0; i--) { /* <DDDDDDDDDDDDDDDD> */
GET_BIT(x);
*datap += (x << i);
}
/*
* Kludge to get the Transceiver out of hung mode
*/
GET_BIT(x);
GET_BIT(x);
GET_BIT(x);
}
CHECK_MIFREG();
return (y);
}
static void
{
int i;
for (i = 0; i < 33; i++) {
SEND_BIT(1);
}
}
/* <<<<<<<<<<<<<<<<<<<<End of Bit Bang Operations >>>>>>>>>>>>>>>>>>>>>>>> */
/* <<<<<<<<<<<<< Frame Register used for MII operations >>>>>>>>>>>>>>>>>>>> */
/* Return 0 if OK, 1 if error (Transceiver does not talk management) */
static uint_t
{
return (1); /* No transceiver present */
if (!hmep->hme_frame_enable)
(regad << HME_MIF_FRREGAD_SHIFT));
/*
* HMEDELAY((*framerp & HME_MIF_FRTA0), HMEMAXRSTDELAY);
*/
CHECK_MIFREG();
if ((frame & HME_MIF_FRTA0) == 0) {
"MIF Read failure");
return (1);
} else {
return (0);
}
}
static void
{
if (!hmep->hme_frame_enable) {
return;
}
/*
* HMEDELAY((*framerp & HME_MIF_FRTA0), HMEMAXRSTDELAY);
*/
CHECK_MIFREG();
if ((frame & HME_MIF_FRTA0) == 0) {
"MIF Write failure");
}
}
/*
* hme_stop_timer function is used by a function before doing link-related
* processing. It locks the "hme_linklock" to protect the link-related data
* structures. This lock will be subsequently released in hme_start_timer().
*/
static void
{
if (hmep->hme_timerid) {
hmep->hme_timerid = 0;
}
}
static void
{
}
/*
* hme_select_speed is required only when auto-negotiation is not supported.
* It should be used only for the Internal Transceiver and not the External
* transceiver because we wouldn't know how to generate Link Down state on
* the wire.
* Currently it is required to support Electron 1.1 Build machines. When all
* these machines are upgraded to 1.2 or better, remove this function.
*
* Returns 1 if the link is up, 0 otherwise.
*/
static int
{
goto read_status;
fdx = PHY_BMCR_FDX;
else
fdx = 0;
switch (speed) {
case HME_SPEED_100:
switch (hmep->hme_transceiver) {
case HME_INTERNAL_TRANSCEIVER:
break;
case HME_EXTERNAL_TRANSCEIVER:
fdx | PHY_BMCR_100M);
}
break;
default:
break;
}
break;
case HME_SPEED_10:
switch (hmep->hme_transceiver) {
case HME_INTERNAL_TRANSCEIVER:
break;
case HME_EXTERNAL_TRANSCEIVER:
}
break;
default:
break;
}
break;
default:
return (0);
}
hmep->hme_linkup_cnt++;
return (0);
}
hmep->hme_linkup_cnt++;
if (stat & PHY_BMSR_LNKSTS)
return (1);
else
return (0);
}
/* insure a good reset of the QSI PHY */
static void
{
int n;
/* Isolate the Internal Transceiver */
goto start_again;
/* select the External transceiver */
} else if (cfg & HME_MIF_CFGM1) {
/* Isolate the External transceiver, if present */
goto start_again;
/* select the Internal transceiver */
}
/*
* Now reset the transceiver.
*/
/*
* Check for transceiver reset completion.
*/
n = HME_PHYRST_PERIOD / HMEWAITPERIOD;
while (--n > 0) {
goto start_again;
}
if ((control & PHY_BMCR_RESET) == 0)
goto reset_issued;
else
}
/*
* phy reset failure
*/
goto start_again;
/*
* Get the PHY id registers. We need this to implement work-arounds
* for bugs in transceivers which use the National DP83840 PHY chip.
* National should fix this in the next release.
*/
/*
* The strapping of AN0 and AN1 pins on DP83840 cannot select
* 10FDX, 100FDX and Auto-negotiation. So select it here for the
* Internal Transceiver.
*/
}
/*
* Modify control and bmsr based on anar for Rev-C of DP83840.
*/
if (HME_DP83840) {
n = 0;
if (anar & PHY_ANAR_TXFDX) {
stat |= PHY_BMSR_100FDX;
n++;
} else
stat &= ~PHY_BMSR_100FDX;
if (anar & PHY_ANAR_TX) {
stat |= PHY_BMSR_100HDX;
n++;
} else
stat &= ~PHY_BMSR_100HDX;
if (anar & PHY_ANAR_10FDX) {
stat |= PHY_BMSR_10FDX;
n++;
} else
stat &= ~PHY_BMSR_10FDX;
if (anar & PHY_ANAR_10) {
stat |= PHY_BMSR_10HDX;
n++;
} else
stat &= ~PHY_BMSR_10HDX;
if (n == 1) { /* only one mode. disable auto-negotiation */
stat &= ~PHY_BMSR_ACFG;
control &= ~PHY_BMCR_ANE;
}
if (n) {
}
}
/*
* Place the Transceiver in normal operation mode
*/
/*
* check if the transceiver is not in Isolate mode
*/
n = HME_PHYRST_PERIOD / HMEWAITPERIOD;
while (--n > 0) {
goto start_again; /* Transceiver does not talk MII */
}
if ((control & PHY_BMCR_ISOLATE) == 0)
goto setconn;
}
goto start_again; /* transceiver reset failure */
/*
* Work-around for the late-collision problem with 100m cables.
* National should fix this in the next release !
*/
if (HME_DP83840) {
}
hmep->hme_linkcheck = 0;
hmep->hme_linkup = 0;
hmep->hme_linkup_cnt = 0;
else {
hmep->hme_linkup_10 = 0;
}
return;
}
static void
{
/*
* If the MIF Polling is ON, and Internal transceiver is in use, just
* check for the presence of the External Transceiver.
* Otherwise:
* First check to see what transceivers are out there.
* If an external transceiver is present
* then use it, regardless of whether there is a Internal transceiver.
* If Internal transceiver is present and no external transceiver
* then use the Internal transceiver.
* If there is no external transceiver and no Internal transceiver,
* then something is wrong so print an error message.
*/
if (hmep->hme_polling_on) {
| HME_MIF_CFGPS));
}
}
}
CHECK_MIFREG();
return;
}
} else {
}
CHECK_MIFREG();
}
static void
{
if (hme_param_autoneg & HME_NOTUSR)
if (hme_param_anar_100T4 & HME_NOTUSR)
if (hme_param_anar_100fdx & HME_NOTUSR)
if (hme_param_anar_100hdx & HME_NOTUSR)
if (hme_param_anar_10fdx & HME_NOTUSR)
if (hme_param_anar_10hdx & HME_NOTUSR)
}
static void
{
else
if (tmp & PHY_BMSR_ACFG)
hme_param_bmsr_ancap = 1;
else
hme_param_bmsr_ancap = 0;
if (tmp & PHY_BMSR_100T4)
hme_param_bmsr_100T4 = 1;
else
hme_param_bmsr_100T4 = 0;
if (tmp & PHY_BMSR_100FDX)
else
if (tmp & PHY_BMSR_100HDX)
else
if (tmp & PHY_BMSR_10FDX)
hme_param_bmsr_10fdx = 1;
else
hme_param_bmsr_10fdx = 0;
if (tmp & PHY_BMSR_10HDX)
hme_param_bmsr_10hdx = 1;
else
hme_param_bmsr_10hdx = 0;
if (hmep->hme_link_pulse_disabled) {
hme_param_linkup = 1;
hme_param_speed = 0;
hme_param_mode = 0;
return;
}
if (!hmep->hme_linkup) {
hme_param_linkup = 0;
return;
}
hme_param_linkup = 1;
hme_param_mode = 1;
} else {
hme_param_mode = 0;
}
hme_param_speed = 1;
else
hme_param_speed = 0;
return;
}
hme_param_speed = 1;
else
hme_param_speed = 0;
return;
}
if (tmp & PHY_ANLPAR_T4)
else
if (tmp & PHY_ANLPAR_TXFDX)
else
if (tmp & PHY_ANLPAR_TX)
else
if (tmp & PHY_ANLPAR_10FDX)
else
if (tmp & PHY_ANLPAR_10)
else
}
static void
{
if (autoneg) {
if (anar_100T4)
anar |= PHY_ANAR_T4;
if (anar_100fdx)
anar |= PHY_ANAR_TXFDX;
if (anar_100hdx)
anar |= PHY_ANAR_TX;
if (anar_10fdx)
anar |= PHY_ANAR_10FDX;
if (anar_10hdx)
anar |= PHY_ANAR_10;
} else {
if (anar_100T4) {
} else if (anar_100fdx) {
/* 100fdx needs to be checked first for 100BaseFX */
} else if (anar_100hdx) {
} else if (anar_10hdx) {
/* 10hdx needs to be checked first for MII-AUI */
/* MII-AUI BugIds 1252776,4032280,4035106,4028558 */
} else if (anar_10fdx) {
} else {
}
}
}
/* Decide if transmitter went dead and reinitialize everything */
static int hme_txhung_limit = 3;
static int
{
/* Something needs to be sent out but it is not going out */
hmep->hme_txhung++;
else
hmep->hme_txhung = 0;
return (status);
}
/*
* hme_check_link ()
* Called as a result of HME_LINKCHECK_TIMER timeout, to poll for Transceiver
* change or when a transceiver change has been detected by the hme_try_speed
* function.
* This function will also be called from the interrupt handler when polled mode
* is used. Before calling this function the interrupt lock should be freed
* so that the hmeinit() may be called.
* Note that the hmeinit() function calls hme_select_speed() to set the link
* speed and check for link status.
*/
static void
hme_check_link(void *arg)
{
/*
* This condition was added to work around for
* Basically if the link is up but no packets
* are being received. This can be checked using
* ipackets, which in case of reception will
* continue to increment after 'hmep->hme_iipackets'
* has been made equal to it and the 'hme_check_link'
* timer has expired. Note this could also be done
* if there's no traffic on the net.
* 'hmep->hme_ipackets' is incremented in hme_read
* for successfully received packets.
*/
/*
* Receptions are occurring set 'hmep->hme_iipackets'
* to 'hmep->hme_ipackets' to monitor if receptions
* occur during the next timeout interval.
*/
else
/*
* Receptions not occurring could be due to
* Synoptics problem, try switchin of data
* scrabbling. That should bring up the link.
*/
}
/* to init the interface */
return;
}
/*
* check if the transceiver is the same.
* init to be done if the external transceiver is
*/
} else {
}
}
hmep->hme_linkcheck = 0;
return;
}
if (hmep->hme_mifpoll_enable) {
CHECK_MIFREG(); /* Verify */
if (!hmep->hme_mifpoll_flag) {
if (stat & PHY_BMSR_LNKSTS) {
return;
}
} else {
hmep->hme_mifpoll_flag = 0;
}
} else {
/* Transceiver does not talk mii */
return;
}
if (stat & PHY_BMSR_LNKSTS) {
return;
}
}
/*
* The PHY may have automatically renegotiated link speed and mode.
* Get the new link speed and mode.
*/
(void) hme_get_autoinfo(hmep);
return;
}
}
return;
}
/* Reset the PHY and bring up the link */
}
static void
{
}
/*
* Disable link pulses for the Internal Transceiver
*/
static void
{
}
static void
hme_force_speed(void *arg)
{
int linkup;
return;
}
} else {
}
return;
}
(hmep->hme_link_pulse_disabled)) {
return;
}
/*
* To interoperate with auto-negotiable capable systems
* the link should be brought down for 1 second.
* How to do this using only standard registers ?
*/
if (HME_DP83840) {
(csc | PHY_CSCR_TXOFF));
return;
(csc & ~PHY_CSCR_TXOFF));
}
} else {
return;
}
}
return;
}
if (linkup) {
} else {
}
}
static void
{
if (aner & PHY_ANER_LPNW) {
if (tmp & PHY_ANAR_TXFDX) {
} else if (tmp & PHY_ANAR_TX) {
} else if (tmp & PHY_ANLPAR_10FDX) {
} else if (tmp & PHY_ANLPAR_10) {
} else {
if (HME_DP83840) {
if (ar & PHY_AR_SPEED10)
else
} else
}
} else {
if (anlpar & PHY_ANLPAR_TX)
else if (anlpar & PHY_ANLPAR_10)
else {
if (HME_DP83840) {
if (ar & PHY_AR_SPEED10)
else
} else
}
}
}
/*
* Return 1 if the link is up or auto-negotiation being tried, 0 otherwise.
*/
static int
{
/* auto negotiation not initiated */
/*
* Transceiver does not talk mii
*/
goto hme_anfail;
}
}
/*
* Read ANER to clear status from previous operations.
*/
/*
* Transceiver does not talk mii
*/
goto hme_anfail;
}
/*
* auto-negotiation initiated
*/
/*
* auto-negotiation in progress
*/
}
/*
* Auto-negotiation has been in progress. Wait for at least
* least 3000 ms.
* Changed 8/28/97 to fix bug ID 4070989.
*/
}
/*
* Transceiver does not talk mii
*/
goto hme_anfail;
}
if ((stat & PHY_BMSR_ANC) == 0) {
/*
* wait for a maximum of 5 seconds
*/
}
if (HME_DP83840) {
if (aner & PHY_ANER_MLF) {
}
}
goto hme_anfail;
}
if (aner & PHY_ANER_MLF) {
goto hme_anfail;
}
if (!(stat & PHY_BMSR_LNKSTS)) {
/*
* wait for a maximum of 10 seconds
*/
}
goto hme_anfail;
} else {
}
}
}
/*
* This function is used to perform automatic speed detection.
* The Internal Transceiver which is based on the National PHY chip
* 83840 supports auto-negotiation functionality.
* Some External transceivers may not support auto-negotiation.
* In that case, the software performs the speed detection.
* The software tries to bring down the link for about 2 seconds to
* force the Link Partner to notice speed change.
* The software speed detection favors the 100 Mbps speed.
* It does this by setting the 100 Mbps for longer duration ( 5 seconds )
* than the 10 Mbps ( 2 seconds ). Also, even after the link is up
* in 10 Mbps once, the 100 Mbps is also tried. Only if the link
* is not up in 100 Mbps, the 10 Mbps speed is tried again.
*/
static void
hme_try_speed(void *arg)
{
int linkup;
} else {
}
return;
}
(hmep->hme_link_pulse_disabled)) {
return;
}
return; /* auto negotiation successful or being tried */
}
return;
}
if (linkup) {
switch (hmep->hme_tryspeed) {
case HME_SPEED_100:
}
} else
break;
case HME_SPEED_10:
if (hmep->hme_linkup_10) {
hmep->hme_linkup_10 = 0;
}
} else {
hmep->hme_linkup_cnt = 0;
}
} else
break;
default:
break;
}
return;
}
hmep->hme_ntries--;
hmep->hme_linkup_cnt = 0;
if (hmep->hme_ntries == 0) {
switch (hmep->hme_tryspeed) {
case HME_SPEED_100:
break;
case HME_SPEED_10:
break;
default:
break;
}
}
}
static void
{
/*
* Work-around for the scramble problem with QSI
* chip and Synoptics 28115 switch.
* Addition Interface Technologies Group (NPG) 8/28/1997.
*/
/*
* Addition of a check for 'hmep->hme_forcespeed'
* This is necessary when the autonegotiation is
* disabled by the 'hme.conf' file. In this case
* hmep->hme_tryspeed is not initialized. Resulting
* in the workaround not being applied.
*/
(btxpc | PHY_BTXPC_DSCRAM));
drv_usecwait(20);
}
}
}
/* <<<<<<<<<<<<<<<<<<<<<<<<<<< LOADABLE ENTRIES >>>>>>>>>>>>>>>>>>>>>>> */
int
_init(void)
{
int status;
}
return (status);
}
int
_fini(void)
{
int status;
}
return (status);
}
int
{
}
/*
* ddi_dma_sync() a TMD or RMD descriptor.
*/
sizeof (struct hme_rmd), \
who)
sizeof (struct hme_tmd), \
who)
/*
* Ethernet broadcast address definition.
*/
static struct ether_addr etherbroadcastaddr = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
/*
*/
if (IS_MULTICAST(pkt)) { \
if (IS_BROADCAST(pkt)) { \
hmep->hme_brdcstrcv++; \
} else { \
hmep->hme_multircv++; \
} \
}
if (IS_MULTICAST(pkt)) { \
if (IS_BROADCAST(pkt)) { \
hmep->hme_brdcstxmt++; \
} else { \
hmep->hme_multixmt++; \
} \
}
static int
{
char propstr[80];
int i, needprop = 0;
struct ether_addr local_mac;
needprop = 1;
needprop = 1;
needprop = 1;
}
if (needprop == 1) {
for (i = 0; i < ETHERADDRL; i++)
!= DDI_SUCCESS) {
return (DDI_FAILURE);
}
} else {
return (DDI_FAILURE);
}
}
}
return (0);
}
/*
* Get properties from old VPD
* for PCI cards
*/
static int
{
char kw_namestr[3];
char kw_fieldstr[256];
int i;
return (1); /* error */
} else {
vpd_len = 9;
}
/* Get local-mac-address */
kw_fieldstr[i] = '\0';
return (DDI_FAILURE);
}
} /* next keyword */
return (DDI_FAILURE);
}
return (0);
}
/*
* Get properties from new VPD
* for CompactPCI cards
*/
static int
{
char kw_namestr[3];
char kw_fieldstr[256];
int maxvpdsize, i;
break; /* no VPD found */
} else {
+ 2]) & 0xff) << 8);
}
/* Get all keywords in this VPD */
kw_len =
kw_fieldstr[i] =
kw_fieldstr[i] = '\0';
kw_fieldstr)) {
return (DDI_FAILURE);
}
} /* next keyword */
} /* next VPD */
return (0);
}
/*
* Get properties from VPD
*/
static int
{
int i, epromsrchlimit;
return (1);
}
epromsrchlimit = 4096;
for (i = 2; i < epromsrchlimit; i++) {
/* "PCIR" */
vpd_base =
break; /* VPD pointer found */
}
}
/* No VPD found */
if (vpd_base == 0) {
return (1);
}
if (v0 == 0x82) {
return (1);
return (0);
} else if (v0 == 0x90) {
/* If we are are SUNW,qfe card, look for the Nth "NA" descr */
}
return (1);
return (0);
} else
return (1); /* unknown start byte in VPD */
}
/*
* For x86, the BIOS doesn't map the PCI Rom register for the qfe
* cards, so we have to extract it from the ebus bridge that is
* function zero of the same device. This is a bit of an ugly hack.
* (The ebus bridge leaves the entire ROM mapped at base address
* register 0x10.)
*/
typedef struct {
} ebus_rom_t;
static int
{
int *regs;
unsigned nregs;
int reg;
/*
* We only want to look at our peers. Skip our parent.
*/
return (DDI_WALK_PRUNESIB);
}
return (DDI_WALK_PRUNECHILD);
}
if (nregs < 1) {
return (DDI_WALK_PRUNECHILD);
}
/*
* Look for function 0 on our bus and device. If the device doesn't
* match, it might be an alternate peer, in which case we don't want
* to examine any of its children.
*/
(PCI_REG_FUNC_G(reg) != 0)) {
return (DDI_WALK_PRUNECHILD);
}
/*
* If we can't map the registers, the caller will notice that
* the acch is NULL.
*/
return (DDI_WALK_TERMINATE);
}
static int
{
int *regs;
unsigned nregs;
/*
* For x86, the BIOS doesn't map the PCI Rom register for the qfe
* cards, so we have to extract it from the eBus bridge that is
* function zero. This is a bit of an ugly hack.
*/
return (DDI_FAILURE);
}
if (nregs < 5) {
return (DDI_FAILURE);
}
/*
* The implementation of ddi_walk_devs says that we must not
* be called during autoconfiguration. However, upon close
* examination, one will find the following is true:
*
* 1) since we're called at attach time,
* DEVI_BUSY_OWNED(ddi_get_parent(dip)) is implicitly true.
*
* 2) we carefully ensure that we prune siblings for all cases
* except our own device, so we can't wind up walking down
* a changing sibling pointer.
*
* 3) since we are attaching, our peers will already have their
* dev_info nodes on the tree... hence our own sibling pointer
* (and those of our siblings) will be stable.
*
* 4) also, because of #3, our parents child pointer will be
* stable.
*
* So it should be safe to do this, because of our carefully
* constructed restrictions.
*/
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
static int
{
int rom_bar;
struct {
} *cfg_ptr;
/*
* map configuration space
*/
return (DDI_FAILURE);
}
/*
* Enable bus-master and memory accesses
*/
/*
* Enable rom accesses
*/
if (cfg_ptr)
return (DDI_FAILURE);
} else {
if (hme_get_vpd_props(dip))
return (DDI_FAILURE);
}
if (cfg_ptr)
return (DDI_SUCCESS);
}
static void
{
int hm_rev;
switch (hm_rev) {
case HME_2P1_REVID:
case HME_2P1_REVID_OBP:
"SBus 2.1 Found (Rev Id = %x)", hm_rev);
break;
case HME_2P0_REVID:
"SBus 2.0 Found (Rev Id = %x)", hm_rev);
break;
case HME_1C0_REVID:
"PCI IO 1.0 Found (Rev Id = %x)", hm_rev);
break;
default:
"%s (Rev Id = %x) Found",
break;
}
}
/*
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
* to accept packets.
*/
int
{
int regno;
int hm_rev = 0;
int prop_len = sizeof (int);
struct {
} *cfg_ptr;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_FAILURE);
hmep->hme_linkcheck = 0;
if (hmep->hme_started)
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/*
* Allocate soft device data structure
*/
/*
* Might as well set up elements of data structure
*/
/*
* Might as well setup the driver private
* structure as part of the dip.
*/
/*
* Reject this device if it's in a slave-only slot.
*/
"Dev not used - dev in slave only slot");
goto error_state;
}
/*
* Map in the device registers.
*
* Reg # 0 is the Global register set
* Reg # 1 is the ETX register set
* Reg # 2 is the ERX register set
* Reg # 3 is the BigMAC register set.
* Reg # 4 is the MIF register set
*/
goto error_state;
}
switch (regno) {
case 5:
hmep->hme_cheerio_mode = 0;
break;
case 2:
break;
default:
goto error_state;
}
/* Initialize device attributes structure */
if (hmep->hme_cheerio_mode)
else
if (hmep->hme_cheerio_mode) {
const char *pdrvname;
/*
* Map the PCI config space
*/
DDI_SUCCESS) {
"pci_config_setup() failed..");
goto error_state;
}
goto error_unmap;
}
hmep->hme_etxregp =
hmep->hme_erxregp =
hmep->hme_bmacregp =
hmep->hme_mifregp =
/*
* Get parent pci bridge info.
*/
/*
* "set hme:pci_latency_timer=0xYY"
*/
if (pci_latency_timer)
/*
* Modify LT for simba
*/
newLT = 0xf0;
/*
* Ensure minimum cheerio latency timer of 0x50
* Usually OBP or pci bridge should set this value
* based on cheerio
* min_grant * 8(33MHz) = 0x50 = 0xa * 0x8
* Some system set cheerio LT at 0x40
*/
else if (oldLT < 0x40)
newLT = 0x50;
/*
* Now program cheerio's pci latency timer with newLT
*/
if (newLT)
} else { /* Map register sets */
if (ddi_regs_map_setup(dip, 0,
goto error_state;
}
goto error_unmap;
}
goto error_unmap;
}
goto error_unmap;
}
goto error_unmap;
}
} /* Endif cheerio_mode */
/*
* Based on the hm-rev, set some capabilities
* Set up default capabilities for HM 2.0
*/
hmep->hme_mifpoll_enable = 0;
hmep->hme_frame_enable = 0;
hmep->hme_lance_mode_enable = 0;
hmep->hme_rxcv_enable = 0;
/* NEW routine to get the properties */
} else {
/*
* hm_rev property not found so, this is
* case of hot insertion of card without interpreting fcode.
* Get it from revid in config space after mapping it.
*/
return (DDI_FAILURE);
}
/*
* Since this is cheerio-based PCI card, we write 0xC in the
* top 4 bits(4-7) of hm-rev and retain the bottom(0-3) bits
* for Cheerio version(1.0 or 2.0 = 0xC0 or 0xC1)
*/
DDI_SUCCESS) {
"hmeattach: ddi_prop_create error for hm_rev");
}
/* get info via VPD */
"hmeattach: no promprops");
}
}
if (!hme_mifpoll_enable)
hmep->hme_mifpoll_enable = 0;
if (ddi_intr_hilevel(dip, 0)) {
" high-level interrupts are not supported");
goto error_unmap;
}
/*
* Get intr. block cookie so that mutex locks can be initialized.
*/
goto error_unmap;
/*
* Initialize mutex's for this device.
*/
/*
* Quiesce the hardware.
*/
/*
* Add interrupt to system
*/
goto error_mutex;
}
/*
* Set up the ethernet mac address.
*/
if (!hmeinit_xfer_params(hmep))
goto error_intr;
goto error_intr;
}
"resource allocation failed");
goto error_intr;
}
"buffer allocation failed");
goto error_intr;
}
"mac_alloc failed");
goto error_intr;
}
goto error_intr;
}
return (DDI_SUCCESS);
/*
* Failure Exit
*/
if (hmep->hme_cookie)
if (hmep->hme_globregh)
if (hmep->hme_cheerio_mode == 0) {
if (hmep->hme_etxregh)
if (hmep->hme_erxregh)
if (hmep->hme_bmacregh)
if (hmep->hme_mifregh)
} else {
if (hmep->pci_config_handle)
}
if (hmep) {
}
return (DDI_FAILURE);
}
int
{
return (DDI_FAILURE);
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* Make driver quiescent, we don't want to prevent the
* detach on failure. Note that this should be redundant,
* since mac_stop should already have called hmeuninit().
*/
}
/*
* Remove instance of the intr
*/
/*
* Unregister kstats.
*/
/*
* Stop asynchronous timer events.
*/
/*
* Destroy all mutexes and data structures allocated during
* attach time.
*
* Note: at this time we should be the only thread accessing
* the structures for this instance.
*/
if (hmep->hme_globregh)
if (hmep->hme_cheerio_mode == 0) {
if (hmep->hme_etxregh)
if (hmep->hme_erxregh)
if (hmep->hme_bmacregh)
if (hmep->hme_mifregh)
} else {
if (hmep->pci_config_handle)
}
return (DDI_SUCCESS);
}
int
{
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
static boolean_t
{
int i;
int hme_ipg1_conf, hme_ipg2_conf;
int hme_autoneg_conf;
int hme_anar_100T4_conf;
int prop_len = sizeof (int);
for (i = 0; i < A_CNT(hme_param_arr); i++)
A_CNT(hme_param_arr))) {
return (B_FALSE);
}
/*
* Set up the start-up values for user-configurable parameters
* Get the values from the global variables first.
* Use the MASK to limit the value to allowed maximum.
*/
/*
* The link speed may be forced to either 10 Mbps or 100 Mbps using the
* property "transfer-speed". This may be done in OBP by using the
* command "apply transfer-speed=<speed> <device>". The speed may be
* either 10 or 100.
*/
hme_param_autoneg = 0; /* force speed */
hme_param_anar_100T4 = 0;
hme_param_anar_10fdx = 0;
if (i == 10) {
hme_param_anar_10hdx = 1;
} else {
hme_param_anar_10hdx = 0;
}
}
/*
* Get the parameter values configured in .conf file.
*/
}
}
}
}
}
}
}
}
}
}
}
}
== DDI_PROP_SUCCESS) {
}
return (B_TRUE);
}
/*
* Return 0 upon success, 1 on failure.
*/
static uint_t
{
/*
* Disable the Tx dma engine.
*/
/*
* Disable the Rx dma engine.
*/
/*
* By this time all things should be quiet, so hit the
* chip with a reset.
*/
if (GET_GLOBREG(reset)) {
return (1);
}
return (0);
}
static int
{
if (rw != KSTAT_READ)
return (EACCES);
/*
* Update all the stats by reading all the counter registers.
* Counter register stats are not updated till they overflow
* and interrupt.
*/
}
/*
* Debug kstats
*/
/*
* xcvr kstats
*/
return (0);
}
static void
{
const char *driver;
int instance;
char buf[16];
"kstat_create failed");
return;
}
if (hmep->hme_intrstats)
/*
* Debugging kstats
*/
/*
* I/O bus kstats
* kstat_named_init(&hkp->hk_pci_speed, "pci_bus_speed",
* KSTAT_DATA_ULONG);
* kstat_named_init(&hkp->hk_pci_size, "pci_bus_width",
* KSTAT_DATA_ULONG);
*/
/*
* xcvr kstats
*/
}
static void
{
case HME_ND_GET:
return;
}
break;
case HME_ND_SET:
hme_param_autoneg = 0xff;
return;
}
if (hme_param_autoneg != 0xff) {
hmep->hme_linkcheck = 0;
} else {
if (old_use_int_xcvr != hme_param_use_intphy) {
hmep->hme_linkcheck = 0;
} else if ((old_ipg1 != hme_param_ipg1) ||
(old_ipg2 != hme_param_ipg2) ||
(old_ipg0 != hme_param_ipg0) ||
(old_lance_mode != hme_param_lance_mode)) {
}
}
break;
default:
break;
}
}
/*ARGSUSED*/
static boolean_t
{
switch (cap) {
case MAC_CAPAB_HCKSUM:
return (B_TRUE);
default:
return (B_FALSE);
}
}
static int
{
return (0);
}
static int
{
/*
* Set new interface local address and re-init device.
* This is destructive to any other streams attached
* to this device.
*/
return (0);
}
static int
{
/*
* If this address's bit was not already set in the local address
* filter, add it and re-initialize the Hardware.
*/
if (add) {
}
} else {
}
}
if (doinit) {
}
return (0);
}
static int
hme_m_start(void *arg)
{
/* initialization failed -- really want DL_INITFAILED */
return (EIO);
} else {
return (0);
}
}
static void
hme_m_stop(void *arg)
{
}
static int
{
}
switch (stat) {
case MAC_STAT_IFSPEED:
break;
case MAC_STAT_IPACKETS:
break;
case MAC_STAT_RBYTES:
break;
case MAC_STAT_IERRORS:
break;
case MAC_STAT_OPACKETS:
break;
case MAC_STAT_OBYTES:
break;
case MAC_STAT_OERRORS:
break;
case MAC_STAT_MULTIRCV:
break;
case MAC_STAT_MULTIXMT:
break;
case MAC_STAT_BRDCSTRCV:
break;
case MAC_STAT_BRDCSTXMT:
break;
case MAC_STAT_UNDERFLOWS:
break;
case MAC_STAT_OVERFLOWS:
break;
case MAC_STAT_COLLISIONS:
break;
case MAC_STAT_NORCVBUF:
break;
case MAC_STAT_NOXMTBUF:
break;
case ETHER_STAT_LINK_DUPLEX:
break;
case ETHER_STAT_ALIGN_ERRORS:
break;
case ETHER_STAT_FCS_ERRORS:
break;
case ETHER_STAT_EX_COLLISIONS:
break;
case ETHER_STAT_DEFER_XMTS:
break;
case ETHER_STAT_SQE_ERRORS:
break;
break;
break;
break;
break;
break;
case ETHER_STAT_XCVR_ADDR:
break;
case ETHER_STAT_XCVR_ID:
break;
case ETHER_STAT_XCVR_INUSE:
switch (hmep->hme_transceiver) {
case HME_INTERNAL_TRANSCEIVER:
break;
case HME_NO_TRANSCEIVER:
break;
default:
*val = XCVR_UNDEFINED;
break;
}
break;
case ETHER_STAT_CAP_100T4:
break;
case ETHER_STAT_ADV_CAP_100T4:
break;
case ETHER_STAT_LP_CAP_100T4:
break;
case ETHER_STAT_CAP_100FDX:
break;
break;
case ETHER_STAT_LP_CAP_100FDX:
break;
case ETHER_STAT_CAP_100HDX:
break;
break;
case ETHER_STAT_LP_CAP_100HDX:
break;
case ETHER_STAT_CAP_10FDX:
break;
case ETHER_STAT_ADV_CAP_10FDX:
break;
case ETHER_STAT_LP_CAP_10FDX:
break;
case ETHER_STAT_CAP_10HDX:
break;
case ETHER_STAT_ADV_CAP_10HDX:
break;
case ETHER_STAT_LP_CAP_10HDX:
break;
case ETHER_STAT_CAP_AUTONEG:
break;
break;
break;
default:
return (EINVAL);
}
return (0);
}
static mblk_t *
{
break;
}
}
return (mp);
}
/*
* Software IP checksum, for the edge cases that the
* hardware can't handle. See hmestart for more info.
*/
static uint16_t
{
/* just add up the words */
for (i = 0; i < nwords; i++) {
}
/* pick up residual byte ... assume even half-word allocations */
if (len % 2) {
}
return (~(sum & 0xffff));
}
static boolean_t
{
if (flags & HCK_PARTIALCKSUM) {
} else {
start_offset += sizeof (struct ether_header);
stuff_offset += sizeof (struct ether_header);
}
(start_offset << HMETMD_CSSTART_SHIFT) |
}
hmep->hme_oerrors++;
goto bad;
}
}
goto notmds;
/*
* Note that for checksum offload, the hardware cannot
* generate correct checksums if the packet is smaller than
* 64-bytes. In such a case, we bcopy the packet and use
* a software checksum.
*/
if (len < 64) {
/* zero fill the padding */
}
len - start_offset);
csflags = 0;
}
DDI_FAILURE) {
"ddi_dma_sync failed");
}
/*
* update MIB II statistics
*/
hmep->hme_txindex++;
CHECK_ETXREG();
hmep->hme_starts++;
return (B_TRUE);
bad:
return (B_TRUE);
hmep->hme_notmds++;
done:
return (retval);
}
/*
* Initialize channel.
* Return 0 on success, nonzero on error.
*
* The recommended sequence for initialization is:
* 1. Issue a Global Reset command to the Ethernet Channel.
* 2. Poll the Global_Reset bits until the execution of the reset has been
* completed.
* Poll Register 0 to till the Resetbit is 0.
* 100Mbps and Non-Isolated mode. The main point here is to bring the
* PHY out of Isolate mode so that it can generate the rx_clk and tx_clk
* to the MII interface so that the Bigmac core can correctly reset
* upon a software reset.
* 2(c). Issue another Global Reset command to the Ethernet Channel and poll
* the Global_Reset bits till completion.
* 3. Set up all the data structures in the host memory.
* Register).
* Register).
* 6. Program the Transmit Descriptor Ring Base Address in the ETX.
* 7. Program the Receive Descriptor Ring Base Address in the ERX.
* 8. Program the Global Configuration and the Global Interrupt Mask Registers.
* 9. Program the ETX Configuration register (enable the Transmit DMA channel).
* 10. Program the ERX Configuration register (enable the Receive DMA channel).
* 11. Program the XIF Configuration Register (enable the XIF).
* 12. Program the RX_MAC Configuration Register (Enable the RX_MAC).
* 13. Program the TX_MAC Configuration Register (Enable the TX_MAC).
*/
#ifdef FEPS_URUN_BUG
static int hme_palen = 32;
#endif
static int
{
uint32_t i;
int ret;
/*
* Lock sequence:
* hme_intrlock, hme_xmitlock.
*/
/*
* Don't touch the hardware if we are suspended. But don't
* fail either. Some time later we may be resumed, and then
* we'll be back here to program the device using the settings
* in the soft state.
*/
return (0);
}
/*
* This should prevent us from clearing any interrupts that
* may occur by temporarily stopping interrupts from occurring
* for a short time. We need to update the interrupt mask
* later in this function.
*/
/*
* Rearranged the mutex acquisition order to solve the deadlock
* situation as described in bug ID 4065896.
*/
hmep->hme_txhung = 0;
/*
* Initializing 'hmep->hme_iipackets' to match current
* number of received packets.
*/
/*
*/
/*
* Clear all descriptors.
*/
/*
* Hang out receive buffers.
*/
for (i = 0; i < HME_RMDMAX; i++) {
}
/*
* DMA sync descriptors.
*/
/*
* Reset RMD and TMD 'walking' pointers.
*/
hmep->hme_rxindex = 0;
/*
* This is the right place to initialize MIF !!!
*/
if (!hmep->hme_frame_enable)
else
/* enable frame mode */
/*
* Depending on the transceiver detected, select the source
* of the clocks for the MAC. Without the clocks, TX_MAC does
* ASIC, it selects Internal by default.
*/
goto init_fail; /* abort initialization */
PUT_MACREG(xifc, 0);
else
/* Isolate the Int. xcvr */
/*
* Perform transceiver reset and speed selection only if
* the link is down.
*/
if (!hmep->hme_linkcheck)
/*
* Reset the PHY and bring up the link
* If it fails we will then increment a kstat.
*/
else {
if (hmep->hme_linkup)
}
/*
* Initialize BigMAC registers.
* First set the tx enable bit in tx config reg to 0 and poll on
* it till it turns to 0. Same for rx config, hash and address
* filter reg.
* Here is the sequence per the spec.
* MADD2 - MAC Address 2
* MADD1 - MAC Address 1
* MADD0 - MAC Address 0
* HASH3, HASH2, HASH1, HASH0 for group address
* AFR2, AFR1, AFR0 and AFMR for address filter mask
* Program RXMIN and RXMAX for packet length if not 802.3
* RXCFG - Rx config for not stripping CRC
* XXX Anything else to hme configured in RXCFG
* IPG1, IPG2, ALIMIT, SLOT, PALEN, PAPAT, TXSFD, JAM, TXMAX, TXMIN
* if not 802.3 compliant
* XIF register for speed selection
* MASK - Interrupt mask
* Set bit 0 of TXCFG
* Set bit 0 of RXCFG
*/
/*
* Initialize the TX_MAC registers
* Initialization of jamsize to work around rx crc bug
*/
#ifdef FEPS_URUN_BUG
if (hme_urun_fix)
#endif
/* Initialize the RX_MAC registers */
/*
* Program BigMAC with local individual ethernet address.
*/
/*
* Set up multicast address filter by passing all multicast
* addresses through a crc generator, and then using the
* low order 6 bits as a index into the 64 bit logical
* address filter. The high order three bits select the word,
* while the rest of the bits select the bit within the word.
*/
/*
* Configure parameters to support VLAN. (VLAN encapsulation adds
* four bytes.)
*/
/*
* Initialize HME Global registers, ETX registers and ERX registers.
*/
/*
* ERX registers can be written only if they have even no. of bits set.
* So, if the value written is not read back, set the lsb and write
* again.
* static int hme_erx_fix = 1; : Use the fix for erx bug
*/
{
}
/*
* Significant performance improvements can be achieved by
* disabling transmit interrupt. Thus TMD's are reclaimed only
* when we run out of them in hmestart().
*/
| HMET_CONFIG_TXFIFOTH));
/* get the rxring size bits */
switch (HME_RMDMAX) {
case 32:
break;
case 64:
break;
case 128:
break;
case 256:
break;
default:
goto init_fail;
}
i |= (HME_FSTBYTE_OFFSET << HMER_CONFIG_FBO_SHIFT)
/* h/w checks start offset in half words */
PUT_ERXREG(config, i);
/*
* Bug related to the parity handling in ERX. When erxp-config is
* read back.
* writing again.
* This fixes the RECV problem in SS5.
* static int hme_erx_fix = 1; : Use the fix for erx bug
*/
{
PUT_ERXREG(config, i);
if (GET_ERXREG(config) != i)
"error:temp = %x erxp->config = %x, should be %x",
}
/*
* Set up the rxconfig, txconfig and seed register without enabling
* them the former two at this time
*
* BigMAC strips the CRC bytes by default. Since this is
* contrary to other pieces of hardware, this bit needs to
* enabled to tell BigMAC not to strip the CRC bytes.
* Do not filter this node's own packets.
*/
if (hme_reject_own) {
} else {
}
if (hme_ngu_enable)
else
i = 0;
else
/*
* Update the interrupt mask : this will re-allow interrupts to occur
*/
/*
* Release the locks in reverse order
*/
if (ret) {
}
/*
* Hardware checks.
*/
CHECK_MIFREG();
CHECK_MACREG();
CHECK_ERXREG();
CHECK_ETXREG();
return (ret);
}
/*
* Calculate the dvma burstsize by setting up a dvma temporarily. Return
* 0 as burstsize upon failure as it signifies no burst size.
* Requests for 64-bit transfer setup, if the platform supports it.
* NOTE: Do not use ddi_dma_alloc_handle(9f) then ddi_dma_burstsize(9f),
* sun4u Ultra-2 incorrectly returns a 32bit transfer.
*/
static int
{
int burstsizes;
return (0);
}
/*
* Use user-configurable parameter for enabling 64-bit transfers
*/
if (burstsizes)
else
if (hmep->hme_cheerio_mode)
if (burstsizes & 0x40)
else if (burstsizes & 0x20)
else
return (DDI_SUCCESS);
}
static int
{
unsigned ccnt;
"cannot allocate buf dma handle - failed");
return (DDI_FAILURE);
}
"cannot allocate buf memory - failed");
return (DDI_FAILURE);
}
"cannot map buf for dma - failed");
return (DDI_FAILURE);
}
/* apparently they don't handle multiple cookies */
if (ccnt > 1) {
"too many buf dma cookies");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
{
/* Alloc RX buffers. */
for (int i = 0; i < HME_RMDMAX; i++) {
DDI_SUCCESS) {
return (DDI_FAILURE);
}
}
/* Alloc TX buffers. */
for (int i = 0; i < HME_TMDMAX; i++) {
DDI_SUCCESS) {
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
static void
{
int i;
return;
/*
* Free and unload pending xmit and recv buffers.
* Maintaining the 1-to-1 ordered sequence of
* We have written the routine to be idempotent.
*/
for (i = 0; i < HME_TMDMAX; i++) {
}
}
}
}
for (i = 0; i < HME_RMDMAX; i++) {
}
}
}
}
}
/*
* hme_start_mifpoll() - Enables the polling of the BMSR register of the PHY.
* After enabling the poll, delay for atleast 62us for one poll to be done.
* Then read the MIF status register to auto-clear the MIF status field.
* Then program the MIF interrupt mask register to enable interrupts for the
* LINK_STATUS and JABBER_DETECT bits.
*/
static void
{
if (!hmep->hme_mifpoll_enable)
return;
hmep->hme_mifpoll_flag = 0;
/* Do not poll for Jabber Detect for 100 Mbps speed */
else
CHECK_MIFREG();
}
static void
{
return;
hmep->hme_polling_on = 0;
CHECK_MIFREG();
}
/*
* Un-initialize (STOP) HME channel.
*/
static void
{
/*
* Allow up to 'HMEDRAINTIME' for pending xmit's to complete.
*/
}
/*
* Allocate CONSISTENT memory for rmds and tmds with appropriate alignment and
* map it in IO space. Allocate space for transmit and receive ddi_dma_handle
* structures to use the DMA interface.
*/
static int
{
int size;
int rval;
/*
* Allocate the TMD and RMD descriptors and extra for page alignment.
*/
&hmep->hme_rmd_dmah);
if (rval != DDI_SUCCESS) {
"cannot allocate rmd handle - failed");
return (DDI_FAILURE);
}
if (rval != DDI_SUCCESS) {
"cannot allocate rmd dma mem - failed");
return (DDI_FAILURE);
}
if (rval != DDI_DMA_MAPPED) {
"cannot allocate rmd dma - failed");
return (DDI_FAILURE);
}
if (cookiec != 1) {
"too many rmd cookies - failed");
return (DDI_FAILURE);
}
&hmep->hme_tmd_dmah);
if (rval != DDI_SUCCESS) {
"cannot allocate tmd handle - failed");
return (DDI_FAILURE);
}
if (rval != DDI_SUCCESS) {
"cannot allocate tmd dma mem - failed");
return (DDI_FAILURE);
}
if (rval != DDI_DMA_MAPPED) {
"cannot allocate tmd dma - failed");
return (DDI_FAILURE);
}
if (cookiec != 1) {
"too many tmd cookies - failed");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static void
{
if (hmep->hme_rmd_paddr) {
hmep->hme_rmd_paddr = 0;
}
if (hmep->hme_rmd_acch)
if (hmep->hme_rmd_dmah)
if (hmep->hme_tmd_paddr) {
hmep->hme_tmd_paddr = 0;
}
if (hmep->hme_tmd_acch)
if (hmep->hme_tmd_dmah)
}
/*
* First check to see if it our device interrupting.
*/
static uint_t
{
/*
* The status register auto-clears on read except for
* MIF Interrupt bit
*/
/*
* Note: TINT is sometimes enabled in thr hmereclaim()
*/
/*
* Bugid 1227832 - to handle spurious interrupts on fusion systems.
* Claim the first interrupt after initialization
*/
}
/* No interesting interrupt */
if (hmep->hme_intrstats) {
if (serviced == DDI_INTR_UNCLAIMED)
else
}
return (serviced);
}
if (hmep->hme_intrstats)
return (serviced);
}
if (hmesbits & HMEG_STATUS_FATAL_ERR) {
if (hmep->hme_intrstats)
return (serviced);
}
}
if (hmesbits & HMEG_STATUS_MIF_INTR) {
if (!(mif_status & PHY_BMSR_LNKSTS)) {
if (hmep->hme_intrstats)
return (serviced);
}
/*
*
* BugId 1261889 EscId 50699 ftp hangs @ 10 Mbps
*
* Here could be one cause:
* national PHY sees jabber, goes into "Jabber function",
* (see section 3.7.6 in PHY specs.), disables transmitter,
* and waits for internal transmit enable to be de-asserted
* for at least 750ms (the "unjab" time). Also, the PHY
* has asserted COL, the collision detect signal.
*
* continually retries, backs off 16 times as per spec,
* and restarts the transmission, so TX_EN is never
* deasserted long enough, in particular TX_EN is turned
* on approximately once every 4 microseconds on the
* average. PHY and MAC are deadlocked.
*
* Here is part of the fix:
* On seeing the jabber, treat it like a hme_fatal_err
*/
if (mif_status & (PHY_BMSR_JABDET)) {
/* national phy only defines this at 10 Mbps */
if (hme_param_speed == 0) { /* 10 Mbps speed ? */
/* treat jabber like a fatal error */
return (serviced);
}
}
}
}
if (hmesbits & HMEG_STATUS_RINT) {
/*
* This dummy PIO is required to flush the SBus
* Bridge buffers in QFE.
*/
(void) GET_GLOBREG(config);
/*
* Loop through each RMD no more than once.
*/
while (num_reads++ < HME_RMDMAX) {
int rxptr;
if (rflags & HMERMD_OWN) {
/*
* Chip still owns it. We're done.
*/
break;
}
/*
* Retrieve the packet.
*/
/*
* Return ownership of the RMD.
*/
}
/*
* Advance to the next RMD.
*/
hmep->hme_rxindex++;
}
}
if (hmep->hme_intrstats)
return (serviced);
}
/*
* Transmit completion reclaiming.
*/
static void
{
/*
* Loop through each TMD.
*/
int reclaim;
if (flags & HMETMD_OWN) {
/*
* Chip still owns it. We're done.
*/
break;
}
/*
* Count a chained packet only once.
*/
if (flags & HMETMD_SOP) {
hmep->hme_opackets++;
}
/*
* MIB II
*/
hmep->hme_txreclaim++;
}
if (reclaimed) {
/*
* we could reclaim some TMDs so turn off interrupts
*/
}
} else {
/*
* enable TINTS: so that even if there is no further activity
* hmereclaim will get called
*/
}
}
/*
* Handle interrupts for fatal errors
* Need reinitialization of the ENET channel.
*/
static void
{
if (hmesbits & HMEG_STATUS_SLV_PAR_ERR) {
hmep->hme_slvparerr++;
}
if (hmesbits & HMEG_STATUS_SLV_ERR_ACK) {
hmep->hme_slverrack++;
}
if (hmesbits & HMEG_STATUS_TX_TAG_ERR) {
hmep->hme_txtagerr++;
hmep->hme_oerrors++;
}
if (hmesbits & HMEG_STATUS_TX_PAR_ERR) {
hmep->hme_txparerr++;
hmep->hme_oerrors++;
}
if (hmesbits & HMEG_STATUS_TX_LATE_ERR) {
hmep->hme_txlaterr++;
hmep->hme_oerrors++;
}
if (hmesbits & HMEG_STATUS_TX_ERR_ACK) {
hmep->hme_txerrack++;
hmep->hme_oerrors++;
}
if (hmesbits & HMEG_STATUS_EOP_ERR) {
hmep->hme_eoperr++;
}
if (hmesbits & HMEG_STATUS_RX_TAG_ERR) {
hmep->hme_rxtagerr++;
hmep->hme_ierrors++;
}
if (hmesbits & HMEG_STATUS_RX_PAR_ERR) {
hmep->hme_rxparerr++;
hmep->hme_ierrors++;
}
if (hmesbits & HMEG_STATUS_RX_LATE_ERR) {
hmep->hme_rxlaterr++;
hmep->hme_ierrors++;
}
if (hmesbits & HMEG_STATUS_RX_ERR_ACK) {
hmep->hme_rxerrack++;
hmep->hme_ierrors++;
}
}
/*
* Handle interrupts regarding non-fatal errors.
*/
static void
{
if (hmesbits & HMEG_STATUS_RX_DROP) {
hmep->hme_missed++;
hmep->hme_ierrors++;
}
if (hmesbits & HMEG_STATUS_DEFTIMR_EXP) {
hmep->hme_defer_xmts++;
}
if (hmesbits & HMEG_STATUS_FSTCOLC_EXP) {
}
if (hmesbits & HMEG_STATUS_LATCOLC_EXP) {
}
if (hmesbits & HMEG_STATUS_EXCOLC_EXP) {
}
if (hmesbits & HMEG_STATUS_NRMCOLC_EXP) {
}
if (hmesbits & HMEG_STATUS_MXPKTSZ_ERR) {
hmep->hme_oerrors++;
}
/*
* This error is fatal and the board needs to
* be reinitialized. Comments?
*/
if (hmesbits & HMEG_STATUS_TXFIFO_UNDR) {
hmep->hme_oerrors++;
}
if (hmesbits & HMEG_STATUS_SQE_TST_ERR) {
hmep->hme_sqe_errors++;
}
if (hmesbits & HMEG_STATUS_RCV_CNT_EXP) {
if (hmep->hme_rxcv_enable) {
}
}
if (hmesbits & HMEG_STATUS_RXFIFO_OVFL) {
hmep->hme_ierrors++;
}
if (hmesbits & HMEG_STATUS_LEN_CNT_EXP) {
}
if (hmesbits & HMEG_STATUS_ALN_CNT_EXP) {
}
if (hmesbits & HMEG_STATUS_CRC_CNT_EXP) {
}
}
static mblk_t *
{
/*
* Check for short packet
* and check for overflow packet also. The processing is the
* same for both the cases - reuse the buffer. Update the Buffer
* overflow counter.
*/
else {
}
hmep->hme_ierrors++;
return (NULL);
}
/*
* Sync the received buffer before looking at it.
*/
/*
* copy the packet data and then recycle the descriptor.
*/
hmep->hme_allocbfail++;
hmep->hme_norcvbuf++;
return (NULL);
}
hmep->hme_ipackets++;
/* Add the First Byte offset to the b_rptr and copy */
/*
* update MIB II statistics
*/
/*
* TCP partial checksum in hardware
*/
}
return (bp);
}
/*VARARGS*/
static void
{
char msg_buffer[255];
} else if (type == DISPLAY_MSG) {
} else if (severity == SEVERITY_HIGH) {
} else {
}
}
/*
* if this is the first init do not bother to save the
* counters. They should be 0, but do not count on it.
*/
static void
{
/* XXX What all gets added in ierrors and oerrors? */
PUT_MACREG(fecnt, 0);
PUT_MACREG(aecnt, 0);
PUT_MACREG(lecnt, 0);
#ifdef HME_CODEVIOL_BUG
/*
*/
if (!hmep->hme_rxcv_enable) {
rxcv = 0;
}
#endif
PUT_MACREG(rxcv, 0);
PUT_MACREG(ltcnt, 0);
PUT_MACREG(excnt, 0);
PUT_MACREG(nccnt, 0);
CHECK_MACREG();
}
/*
*/
/* Free the Named Dispatch Table by calling hme_nd_free */
static void
{
}
/*
* Extracts the value from the hme parameter array and prints the
* parameter value. cp points to the required parameter.
*/
/* ARGSUSED */
static int
{
return (0);
}
/*
* Register each element of the parameter array with the
* named dispatch handler. Each element is loaded using
* hme_nd_load()
*/
/* ARGSUSED */
static int
{
int i;
/* First 4 elements are read-only */
for (i = 0; i < 4; i++, hmepa++)
return (B_FALSE);
}
/* Next 10 elements are read and write */
for (i = 0; i < 10; i++, hmepa++)
return (B_FALSE);
}
}
/* next 12 elements are read-only */
for (i = 0; i < 12; i++, hmepa++)
return (B_FALSE);
}
/* Next 3 elements are read and write */
for (i = 0; i < 3; i++, hmepa++)
return (B_FALSE);
}
}
return (B_TRUE);
}
/*
* Sets the hme parameter to the value in the hme_param_register using
* hme_nd_load().
*/
/* ARGSUSED */
static int
{
char *end;
return (EINVAL);
}
return (0);
}
/* Free the table pointed to by 'ndp' */
static void
{
}
}
static int
{
int err;
char *valp;
if (!nd_param)
return (B_FALSE);
return (B_TRUE);
}
/*
* NOTE - logic throughout nd_xxx assumes single data block for ioctl.
* However, existing code sends in some big buffers.
*/
}
return (B_FALSE);
break;
}
while (*valp++)
;
case ND_GET:
/*
* (temporary) hack: "*valp" is size of user buffer for copyout. If result
* of action routine is too big, free excess and return ioc_rval as buffer
* size needed. Return as many mblocks as will fit, free the rest. For
* backward compatibility, assume size of original ioctl buffer if "*valp"
* bad or not given.
*/
if (valp)
{
while (mp2) {
}
}
if (!err) {
/* Tack on the null */
if (excess > 0) {
}
}
break;
case ND_SET:
if (valp) {
== 0)) {
}
}
break;
default:
break;
}
return (B_TRUE);
}
/*
* Load 'name' into the named dispatch table pointed to by 'ndp'.
* 'ndp' should be the address of a char pointer cell. If the table
* does not exist (*ndp == 0), a new table is allocated and 'ndp'
* is stuffed. If there is not enough space in the table for a new
* entry, more space is allocated.
*/
static boolean_t
{
if (!nd_pparam)
return (B_FALSE);
return (B_FALSE);
}
goto fill_it;
}
}
return (B_FALSE);
} else {
nd->nd_free_count--;
}
}
;
nd->nd_free_count--;
return (B_TRUE);
}
/*
* To set up the mac address for the network interface:
* The adapter card may support a local mac address which is published
* in a device node property "local-mac-address". This mac address is
* treated as the factory-installed mac address for DLPI interface.
* If the adapter firmware has used the device for diskless boot
* operation it publishes a property called "mac-address" for use by
* inetboot and the device driver.
* If "mac-address" is not found, the system options property
* "local-mac-address" is used to select the mac-address. If this option
* is set to "true", and "local-mac-address" has been found, then
* local-mac-address is used; otherwise the system mac address is used
* by calling the "localetheraddr()" function.
*/
static void
{
char *prop;
int prop_len = sizeof (int);
hmep->hme_addrflags = 0;
/*
* Check if it is an adapter with its own local mac address
* If it is present, save it as the "factory-address"
* for this adapter.
*/
"local-mac-address",
if (prop_len == ETHERADDRL) {
"Local Ethernet address = %s",
}
}
/*
* Check if the adapter has published "mac-address" property.
* If it is present, use it as the mac address for this device.
*/
if (prop_len >= ETHERADDRL) {
return;
}
}
#ifdef __sparc
/*
* On sparc, we might be able to use the mac address from the
* system. However, on all other systems, we need to use the
* address from the PROM.
*/
"Using local MAC address");
return;
}
}
/*
* Get the system ethernet address.
*/
#else
#endif
}
/* ARGSUSED */
static void
{
}