hme.c revision 447e4a639cd3f814fbae624e7188d5adaa83f841
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* SunOS MT STREAMS FEPS(SBus)/Cheerio(PCI) 10/100Mb Ethernet Device Driver
*/
#include <sys/ethernet.h>
typedef int (*fptri_t)();
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,
EXIT_MSG = 32
} 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 ",
"EXIT_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
/* temp: stats from adb */
static int hme_reinit_txhung;
static int hme_reinit_fatal;
static int hme_reinit_jabber;
/*
*/
static int hme_urun_fix = 0; /* Bug fixed in Sbus/FEPS 2.0 */
/*
* Initialize hmestruplock and hmewenlock just once during attach
*/
/*
* The following variables are used for configuring various features
*/
/*
* The following variables are used for performance tuning.
*/
static int hme_rx_bcopy_max = RX_BCOPY_MAX;
/*
* 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 *link_down_msg =
"No response from Ethernet network : Link down -- cable problem?";
static char *busy_msg =
"Driver is BUSY with upper layer";
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 *slave_slot_msg =
"Dev not used - dev in slave only slot";
static char *burst_size_msg =
"Could not identify the burst size";
static char *unk_rx_ringsz_msg =
"Unknown receive RINGSZ";
static char *lmac_addr_msg =
"Using local MAC address";
static char *lether_addr_msg =
"Local Ethernet address = %s";
static char *add_intr_fail_msg =
"ddi_add_intr(9F) failed";
static char *create_minor_node_fail_msg =
"ddi_create_minor_node(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 *mif_read_fail_msg =
"MIF Read failure";
static char *mif_write_fail_msg =
"MIF Write failure";
static char *kstat_create_fail_msg =
"kstat_create 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";
#ifdef HME_DEBUG
static char *mregs_4config_fail_msg =
"ddi_regs_map_setup(9F) for config space failed";
static char *attach_fail_msg =
"Attach entry point failed";
static char *attach_bad_cmd_msg =
"Attach entry point rcv'd a bad command";
static char *detach_bad_cmd_msg =
"Detach entry point rcv'd a bad command";
static char *phy_msg =
"Phy, Vendor Id: %x";
static char *no_phy_msg =
static char *unk_rx_descr_sze_msg =
"Unknown Rx descriptor size %x.";
static char *disable_txmac_msg =
"Txmac could not be disabled.";
static char *disable_rxmac_msg =
"Rxmac could not be disabled.";
static char *config_space_fatal_msg =
"Configuration space failed in routine.";
static char *mregs_4soft_reset_fail_msg =
"ddi_regs_map_setup(9F) for soft reset failed";
static char *disable_erx_msg =
"Can not disable Rx.";
static char *disable_etx_msg =
"Can not disable Tx.";
static char *unk_tx_descr_sze_msg =
"Unknown Tx descriptor size %x.";
static char *alloc_tx_dmah_msg =
"Can not allocate Tx dma handle.";
static char *alloc_rx_dmah_msg =
"Can not allocate Rx dma handle.";
static char *phy_speed_bad_msg =
static char *par_detect_fault_msg =
"Parallel Detection Fault";
static char *autoneg_speed_bad_msg =
"Autonegotiated speed is bad";
#endif
/*
* "MIF Read failure: data = %X";
*/
/*
* SunVTS Loopback messaging support
*
* static char *loopback_val_default =
* "Loopback Value: Error In Value.";
*
* static char *loopback_cmd_default =
* "Loopback Command: Error In Value.";
*/
/* FATAL ERR msgs */
/*
* Function prototypes.
*/
static int hmeinit_xfer_params(struct hme *);
static void hmestatinit(struct hme *);
static int hmeallocthings(struct hme *);
static void hmefreebufs(struct hme *);
static void hmeget_hm_rev_property(struct hme *);
static void hmedodetach(struct hmestr *);
static void hmewenable(struct hme *);
static void hmereclaim(struct hme *);
static char *hme_ether_sprintf(struct ether_addr *);
struct ether_addr *, int, uint32_t);
struct ether_addr *);
struct ether_addr *);
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 *);
msg_t, char *, ...);
#define HME_FAULT_MSG1(p, s, t, f) \
#define HME_FAULT_MSG2(p, s, t, f, a) \
#define HME_FAULT_MSG3(p, s, t, f, a, b) \
#define HME_FAULT_MSG4(p, s, t, f, a, b, c) \
#ifdef HME_DEBUG
msg_t, char *, ...);
#define HME_DEBUG_MSG1(p, s, t, f) \
#define HME_DEBUG_MSG2(p, s, t, f, a) \
#define HME_DEBUG_MSG3(p, s, t, f, a, b) \
#define HME_DEBUG_MSG4(p, s, t, f, a, b, c) \
#define HME_DEBUG_MSG5(p, s, t, f, a, b, c, d) \
#define HME_DEBUG_MSG6(p, s, t, f, a, b, c, d, e) \
(d), (e))
#else
#define HME_DEBUG_MSG1(p, s, t, f)
#define HME_DEBUG_MSG2(p, s, t, f, a)
#define HME_DEBUG_MSG3(p, s, t, f, a, b)
#define HME_DEBUG_MSG4(p, s, t, f, a, b, c)
#define HME_DEBUG_MSG5(p, s, t, f, a, b, c, d)
#define HME_DEBUG_MSG6(p, s, t, f, a, b, c, d, e)
#endif
#define CHECK_MIFREG() \
#define CHECK_ETXREG() \
#define CHECK_ERXREG() \
#define CHECK_MACREG() \
#define CHECK_GLOBREG() \
#define DEV_REPORT_FAULT1(p, i, l, f)
#define DEV_REPORT_FAULT2(p, i, l, f, a)
#define DEV_REPORT_FAULT3(p, i, l, f, a, b)
#define DEV_REPORT_FAULT4(p, i, l, f, a, b, c)
/*
* Module linkage structures.
*/
static struct module_info hmeminfo = {
HMEIDNUM, /* mi_idnum */
HMENAME, /* mi_idname */
HMEMINPSZ, /* mi_minpsz */
HMEMAXPSZ, /* mi_maxpsz */
HMEHIWAT, /* mi_hiwat */
HMELOWAT /* mi_lowat */
};
NULL, /* qi_putp */
NULL, /* qi_srvp */
hmeopen, /* qi_qopen */
hmeclose, /* qi_qclose */
NULL, /* qi_qadmin */
&hmeminfo, /* qi_minfo */
NULL /* qi_mstat */
};
hmewput, /* qi_putp */
hmewsrv, /* qi_srvp */
NULL, /* qi_qopen */
NULL, /* qi_qclose */
NULL, /* qi_qadmin */
&hmeminfo, /* qi_minfo */
NULL /* qi_mstat */
};
&hmerinit, /* st_rdinit */
&hmewinit, /* st_wrinit */
NULL, /* st_muxrinit */
NULL /* st_muxwrinit */
};
static struct cb_ops cb_hme_ops = {
nodev, /* cb_open */
nodev, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
nodev, /* cb_read */
nodev, /* cb_write */
nodev, /* cb_ioctl */
nodev, /* cb_devmap */
nodev, /* cb_mmap */
nodev, /* cb_segmap */
nochpoll, /* cb_chpoll */
ddi_prop_op, /* cb_prop_op */
&hme_info, /* cb_stream */
CB_REV, /* rev */
nodev, /* int (*cb_aread)() */
nodev /* int (*cb_awrite)() */
};
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
ddi_no_info, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
hmeattach, /* devo_attach */
hmedetach, /* devo_detach */
nodev, /* devo_reset */
&cb_hme_ops, /* devo_cb_ops */
NULL /* devo_power */
};
#ifndef lint
char _depends_on[] = "drv/ip";
#endif /* lint */
/*
* 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.
*/
static ddi_dma_attr_t hme_dma_attr = {
DMA_ATTR_V0, /* version number. */
512, /* granularity */
0 /* attribute flags */
};
static ddi_dma_lim_t hme_dma_limits = {
1024 /* dlim_speed */
};
static uchar_t pci_latency_timer = 0;
/*
* This is the loadable module wrapper.
*/
/*
* Module linkage information for the kernel.
*/
&mod_driverops, /* Type of module. This one is a driver */
"10/100Mb Ethernet Driver v%I%",
&hme_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
};
/*
* Autoconfiguration lock: We want to initialize all the global
* locks at _init(). However, we do not have the cookie required which
* is returned in ddi_add_intr(), which in turn is usually called at attach
* time.
*/
static kmutex_t hmeautolock;
/*
* Linked list of active (inuse) driver Streams.
*/
static krwlock_t hmestruplock;
/*
* Single private "global" lock for the few rare conditions
* we want single-threaded.
*/
static kmutex_t hmewenlock;
static int hme_device = -1;
/*
* 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
*/
typedef struct ether_header *eehp;
#define get_ether_type(ptr) (\
/* <<<<<<<<<<<<<<<<<<<<<< Configuration Parameters >>>>>>>>>>>>>>>>>>>>> */
static int jamsize = BMAC_DEFAULT_JAMSIZE;
/*
* The following code is used for performance metering and debugging;
* This routine is invoked via "TIME_POINT(label)" macros, which will
* store the label and a timestamp. This allows to execution sequences
* and timestamps associated with them.
*/
#ifdef TPOINTS
/* Time trace points */
int time_point_active;
static int time_point_offset, time_point_loc;
#define POINTS 1024
int time_points[POINTS];
void
hme_time_point(int loc)
{
static hrtime_t time_point_base;
if (time_point_base == 0) {
time_point_offset = 0;
} else {
time_point_offset += 2;
if (time_point_offset >= POINTS)
time_point_offset = 0; /* wrap at end */
/* time_point_active = 0; disable at end */
}
}
#else
#define TPOINT(x)
#endif
/*
* 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 >>>>>>>>>>>>>>>>>>>> */
#ifdef HME_FRM_DEBUG
int hme_frame_flag = 0;
#endif
/* 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)
#ifdef HME_FRM_DEBUG
if (!hme_frame_flag) {
"Frame Register used for MII");
hme_frame_flag = 1;
}
#endif
(regad << HME_MIF_FRREGAD_SHIFT);
/*
* HMEDELAY((*framerp & HME_MIF_FRTA0), HMEMAXRSTDELAY);
*/
CHECK_MIFREG();
if ((frame & HME_MIF_FRTA0) == 0) {
return (1);
} else {
"Frame Reg :mii_read: successful:data = %X ", *datap);
return (0);
}
}
static void
{
if (!hmep->hme_frame_enable) {
return;
}
"FRame Reg :mii_write: phyad = %X \
/*
* HMEDELAY((*framerp & HME_MIF_FRTA0), HMEMAXRSTDELAY);
*/
CHECK_MIFREG();
if ((frame & HME_MIF_FRTA0) == 0) {
}
#if HME_DEBUG
else {
"Frame Reg :mii_write: successful");
}
#endif
}
/*
* 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:
"Default in select speed 100");
break;
}
break;
case HME_SPEED_10:
switch (hmep->hme_transceiver) {
case HME_INTERNAL_TRANSCEIVER:
break;
case HME_EXTERNAL_TRANSCEIVER:
}
break;
default:
"Default in select speed 10");
break;
}
break;
default:
"Default in select speed : Neither speed");
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;
"reset_trans: reset complete.");
/*
* 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.
*/
"reset_trans: control = %x status = %x idr1 = %x idr2 = %x anar = %x",
/*
* 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) {
"DP83840 Rev-C found: Modified bmsr = %x "
}
}
/*
* 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 */
"reset_trans: isolate complete.");
/*
* Work-around for the late-collision problem with 100m cables.
* National should fix this in the next release !
*/
if (HME_DP83840) {
"hme_reset_trans: CSC read = %x written = %x",
}
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) {
"check_trans: polling_on: cfg = %X", cfgsav);
| HME_MIF_CFGPS));
}
"Extern Transcvr Disconnected");
& ~HME_MIF_CFGPS));
}
}
CHECK_MIFREG();
return;
}
"check_trans: polling_off: cfg = %X", cfgsav);
} 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;
hmep->hme_link_up = 0;
hmep->link_duplex = 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) {
"hme_link_control: force 100T4 hdx");
} else if (anar_100fdx) {
/* 100fdx needs to be checked first for 100BaseFX */
} else if (anar_100hdx) {
"hme_link_control: force 100 hdx");
} 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 {
"hme_link_control: force 10 hdx");
}
}
}
/* 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)
{
"link_check entered:");
/*
* 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.
*/
}
"txhung: re-init MAC");
/* 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;
/* to init the interface */
return;
}
if (hmep->hme_mifpoll_enable) {
CHECK_MIFREG(); /* Verify */
"int_flag = %X old_stat = %X stat = %X",
if (!hmep->hme_mifpoll_flag) {
if (stat & PHY_BMSR_LNKSTS) {
return;
}
"hme_check_link:DOWN polled data = %X\n", stat);
"hme_check_link:after poll-stop: stat = %X",
temp);
} else {
hmep->hme_mifpoll_flag = 0;
}
} else {
/* Transceiver does not talk mii */
return;
}
if (stat & PHY_BMSR_LNKSTS) {
return;
}
}
"mifpoll_flag = %x first stat = %X",
"second stat = %X", stat);
/*
* 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
{
}
static void
{
switch (hmep->hme_transceiver) {
case HME_INTERNAL_TRANSCEIVER:
break;
case HME_EXTERNAL_TRANSCEIVER:
break;
default:
break;
}
}
/*
* Disable link pulses for the Internal Transceiver
*/
static void
{
"hme_disable_link_pulse: NICR read = %x written = %x",
}
static void
hme_force_speed(void *arg)
{
int linkup;
"hme_force_speed entered");
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 {
#ifdef HME_100T4_DEBUG
{
"hme_force_speed: begin:control ="
" %X stat = %X aner = %X anar = %X"
" anlpar = %X",
}
#endif
return;
}
}
return;
}
if (linkup) {
#ifdef HME_100T4_DEBUG
{
"hme_force_speed:end: control ="
"%X stat = %X aner = %X anar = %X anlpar = %X",
}
#endif
} else {
}
}
static void
{
if (aner & PHY_ANER_LPNW) {
"hme_try_autoneg: Link Partner AN able");
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) {
"hme_try_autoneg: anar not set with speed selection");
"ar = %X", ar);
if (ar & PHY_AR_SPEED10)
else
} else
}
} else {
" hme_try_autoneg: parallel detection done");
if (anlpar & PHY_ANLPAR_TX)
else if (anlpar & PHY_ANLPAR_10)
else {
if (HME_DP83840) {
" hme_try_autoneg: parallel detection: anar not set with speed selection");
"ar = %X", ar);
if (ar & PHY_AR_SPEED10)
else
} else
}
}
}
/*
* Return 1 if the link is up or auto-negotiation being tried, 0 otherwise.
*/
static int
{
#ifdef HME_AUTONEG_DEBUG
#endif
/* auto negotiation not initiated */
/*
* Transceiver does not talk mii
*/
goto hme_anfail;
}
" PHY status reg = %X", stat);
" Auto-negotiation not supported");
}
/*
* 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
*/
}
#ifdef HME_AUTONEG_DEBUG
"Auto-negotiation not completed in 5 seconds");
" PHY status reg = %X", stat);
" PHY control reg = %x", control);
" PHY anar reg = %x", anar);
" PHY aner reg = %x", aner);
" PHY anlpar reg = %x", anlpar);
#endif
if (HME_DP83840) {
if (aner & PHY_ANER_MLF) {
" hme_try_autoneg: MLF Detected"
" after 5 seconds");
}
}
goto hme_anfail;
}
if (aner & PHY_ANER_MLF) {
goto hme_anfail;
}
if (!(stat & PHY_BMSR_LNKSTS)) {
/*
* wait for a maximum of 10 seconds
*/
}
"Link not Up in 10 seconds: stat = %X", stat);
goto hme_anfail;
} else {
}
"Retry Auto-negotiation.");
}
/*
* 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:
"Default: Try speed");
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:
"Default: Try speed");
break;
}
}
}
static void
{
/*
* Work-around for the scramble problem with QSI
* chip and Synoptics 28115 switch.
* Addition Interface Technologies Group (NPG) 8/28/1997.
*/
if ((HME_QS6612) &&
/*
* 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;
if (status != 0) {
}
return (status);
}
int
_fini(void)
{
int status;
if (status != 0)
return (status);
if (init_once) {
}
return (status);
}
int
{
}
#define HMERINDEX(i) (i % HMERPENDING)
#define DONT_FLUSH -1
/*
* Allocate and zero-out "number" structures
* each of type "structure" in kernel memory.
*/
((structure *)kmem_zalloc(\
/*
* Translate a kernel virtual address to i/o address.
*/
#define HMEIOPBIOADDR(hmep, a) \
/*
* ddi_dma_sync() a TMD or RMD descriptor.
*/
(who))
#define CHECK_IOPB() \
/*
* Ethernet broadcast address definition.
*/
static struct ether_addr etherbroadcastaddr = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
/*
*/
#define IS_BROADCAST(ehp) \
#define IS_MULTICAST(ehp) \
if (IS_BROADCAST(ehp)) { \
hmep->hme_brdcstrcv++; \
} else if (IS_MULTICAST(ehp)) { \
hmep->hme_multircv++; \
}
if (IS_BROADCAST(ehp)) { \
hmep->hme_brdcstxmt++; \
} else if (IS_MULTICAST(ehp)) { \
hmep->hme_multixmt++; \
}
/*
* Linked list of hme structures - one per card.
*/
/*
* force the fallback to ddi_dma routines
*/
/*
* Our DL_INFO_ACK template.
*/
static dl_info_ack_t hmeinfoack = {
DL_INFO_ACK, /* dl_primitive */
ETHERMTU, /* dl_max_sdu */
0, /* dl_min_sdu */
HMEADDRL, /* dl_addr_length */
DL_ETHER, /* dl_mac_type */
0, /* dl_reserved */
0, /* dl_current_state */
-2, /* dl_sap_length */
DL_CLDLS, /* dl_service_mode */
0, /* dl_qos_length */
0, /* dl_qos_offset */
0, /* dl_range_length */
0, /* dl_range_offset */
DL_STYLE2, /* dl_provider_style */
sizeof (dl_info_ack_t), /* dl_addr_offset */
DL_VERSION_2, /* dl_version */
ETHERADDRL, /* dl_brdcst_addr_length */
0 /* dl_growth */
};
static int
{
char propstr[80];
int i, needprop = 0;
struct ether_addr local_mac;
#ifdef HME_DEBUG
#endif
needprop = 1;
needprop = 1;
needprop = 1;
}
if (needprop == 1) {
for (i = 0; i < ETHERADDRL; i++)
!= DDI_SUCCESS) {
AUTOCONFIG_MSG, "hme_create_newvpd_props: \
ddi_prop_create error");
return (DDI_FAILURE);
}
} else {
AUTOCONFIG_MSG, "hme_create_newvpd_props: \
ddi_prop_create error");
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';
"cannot create_prop_from_kw %s", kw_namestr);
return (DDI_FAILURE);
}
} /* next keyword */
"hme_get_oldvpd model: ddi_prop_create error");
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 */
0xff);
kw_fieldstr[i] = GET_ROM8
kw_fieldstr[i] = '\0';
kw_fieldstr)) {
"cannot create_prop_from_kw %s", kw_namestr);
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++) {
& 0xff) == 0x50) /* 'P' */
& 0xff) == 0x43) /* 'C' */
& 0xff) == 0x49) /* 'I' */
& 0xff) == 0x52) { /* 'R' */
& 0xff) |
& 0xff) << 8);
break; /* VPD pointer found */
}
}
/* No VPD found */
if (vpd_base == 0) {
return (1);
}
if (v0 == 0x82) {
return (1);
return (0);
} else if (v0 == 0x90) {
return (1);
return (0);
} else
return (1); /* unknown start byte in VPD */
}
static int
{
int rom_bar;
struct {
} *cfg_ptr;
/*
* map configuration space
*/
"ddi_map_regs for config space failed");
return (DDI_FAILURE);
}
/*
* Enable bus-master and memory accesses
*/
/*
* Enable rom accesses
*/
"reg mapping failed: Check reg property ");
if (cfg_ptr)
return (DDI_FAILURE);
} else {
if (hme_get_vpd_props(dip))
return (1);
}
if (cfg_ptr)
return (0); /* 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",
(hm_rev == HME_2C0_REVID) ?
"PCI IO 2.0" :
"Sbus",
hm_rev);
break;
}
}
/*
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
* to accept packets.
*/
static int
{
int regno;
int hm_rev = 0;
int prop_len = sizeof (int);
struct {
} *cfg_ptr;
"hmeattach: Entered");
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_FAILURE);
hmep->hme_linkcheck = 0;
{
int dohmeinit = 0;
/* Do hmeinit() only for active interface */
dohmeinit = 1;
break;
}
}
if (dohmeinit)
}
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.
*/
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;
}
/*
* 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 */
== DDI_PROP_SUCCESS) {
} 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.
*/
"hmeattach: ddi_map_regs for config space failed");
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 */
if (hmeget_promprops(dip)) {
"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.
*/
(void *)hmep->hme_cookie);
(void *)hmep->hme_cookie);
(void *)hmep->hme_cookie);
if (!init_once) {
init_once = 1;
(void *)hmep->hme_cookie);
(void *)hmep->hme_cookie);
}
/*
* Quiesce the hardware.
*/
/*
* Add interrupt to system
*/
goto error_mutex;
}
/*
* Set up the ethernet mac address.
*/
/*
* Create the filesystem device node.
*/
goto error_intr;
}
if (!hmeinit_xfer_params(hmep))
goto error_minor;
goto error_minor;
}
/* lock hme structure while manipulating link list of hme structs */
return (DDI_SUCCESS);
/*
* Failure Exit
*/
if (hmep->hme_cookie)
/*
* hmewenlock and hmestruplock are destroy-ed in _fini()
*/
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) {
}
"hmeattach: Unsuccessful Exiting");
return (DDI_FAILURE);
}
static int
{
/*
* No resources allocated
*/
return (DDI_FAILURE);
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/*
* Bug ID 4013267
* This bug manifests by allowing the driver to allow detach
* while the driver is busy and subsequent packets cause
* the driver to panic.
*/
return (DDI_FAILURE);
}
/*
* Make driver quiescent, we don't want to prevent the
* detach on failure.
*/
/*
* Remove instance of the intr
*/
/*
* Destroy all mutexes and data structures allocated during
* attach time.
*/
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)
}
/*
* Remove hmep from the link list of device structures
*/
if (hmetmp->hme_intrstats)
if (unval == DDI_FAILURE)
"dma_unbind_handle failed");
}
/*
* dvma handle case.
*/
if (hmetmp->hme_dvmarh) {
}
/*
* dma handle case.
*/
(HME_TMDMAX + HMERPENDING) *
(sizeof (ddi_dma_handle_t)));
}
/*
* Generated when there was only dma.
* else HME_FAULT_MSG1(NULL, SEVERITY_HIGH,
* "expected dmarh");
*/
/*
* Reset hme_device to default value(-1) when ndd is
* referencing the instance being detached.
*/
hme_device = -1;
break;
}
return (DDI_SUCCESS);
}
static int
{
int i;
int hme_ipg1_conf, hme_ipg2_conf;
int hme_autoneg_conf;
int hme_anar_100T4_conf;
int prop_len = sizeof (int);
"==> hmeinit_xfer_params");
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.
*/
== DDI_PROP_SUCCESS) {
"params: transfer-speed property = %X", i);
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) {
"params: hme_ipg1 property = %X", hme_ipg1_conf);
}
== DDI_PROP_SUCCESS) {
}
== DDI_PROP_SUCCESS) {
}
== DDI_PROP_SUCCESS) {
}
== DDI_PROP_SUCCESS) {
}
== DDI_PROP_SUCCESS) {
}
== DDI_PROP_SUCCESS) {
}
== DDI_PROP_SUCCESS) {
}
== DDI_PROP_SUCCESS) {
}
== DDI_PROP_SUCCESS) {
}
== DDI_PROP_SUCCESS) {
}
== DDI_PROP_SUCCESS) {
}
== DDI_PROP_SUCCESS) {
"params: link-pulse-disable property found.");
}
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)) {
"cannot stop hme - failed to access device");
return (1);
}
return (0);
}
static int
{
/*
* Update all the stats by reading all the counter registers.
* Counter register stats are not updated till they overflow
* and interrupt.
*/
if (rw == KSTAT_WRITE) {
/*
* MIB II kstat variables
*/
#ifdef kstat
#endif /* kstat */
/*
* PSARC 1997/198 : 64 bit kstats
*/
/*
* PSARC 1997/247 : RFC 1643
*/
/* first collisions */
/* tx_late_collisions */
/*
* RFE's (Request for Enhancement)
*/
/*
* Debug Kstats
*/
/*
* I/O bus kstats
* hmep->hme_pci_speed = hkp->hk_pci_peed.value.ul;
*/
/*
* xcvr kstats
*/
/*
* Link Status
*/
return (0);
} else {
/*
* MIB II kstat variables
*/
/*
* PSARC 1997/198
*/
/*
* PSARC 1997/247 : RFC 1643
*/
/* first_collisions */
/* tx_late_collisions */
/*
* RFE's (Request for Enhancements)
*/
/*
* Debug kstats
*/
/*
* I/O bus kstats
* hkp->hk_pci_speed.value.ul = hmep->pci_speed;
*/
/*
* xcvr kstats
*/
/*
* Link Status
*/
}
return (0);
}
static void
{
int instance;
char buf[16];
#ifdef kstat
sizeof (struct hmekstat) / sizeof (kstat_named_t),
KSTAT_FLAG_PERSISTENT)) == NULL) {
#else
#endif /* kstat */
return;
}
if (hmep->hme_intrstats)
/*
* MIB II kstat variables
*/
/*
* PSARC 1997/198
*/
/*
* PSARC 1997/247 : RFC 1643
*/
/* first_collisions */
/* tx_late_collisions */
/*
* RFE kstats
*/
/*
* 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
*/
/*
* Link Status
*/
}
/*
* Assorted DLPI V2 routines.
*/
/* ARGSUSED */
static int
{
return (EINVAL);
/*
* Serialize all driver open and closes.
*/
/*
* Determine minor device number.
*/
minordev = 0;
break;
minordev++;
}
} else
goto done;
}
"hmeopen: sbp = %X\n", sbp);
/*
* Link new entry into the list of active entries.
*/
/*
* Disable automatic enabling of our write service procedure.
* We control this explicitly.
*/
done:
/* inform framework we are a good citizen */
return (0);
}
static int
{
/*
* Implicit detach Stream from interface.
*/
}
/*
* Unlink the per-Stream entry from the active list and free it.
*/
break;
return (0);
}
static int
{
"hmewput start: wq %p", wq);
case M_DATA: /* "fastpath" */
break;
}
/*
* If any msgs already enqueued or the interface will
* loop back up the message (due to HMEPROMISC), then
* enqueue the msg. Otherwise just xmit it directly.
*/
!= 0) {
} else
break;
case M_PROTO:
case M_PCPROTO:
/*
* Break the association between the current thread and
* the thread that calls hmeproto() to resolve the
* problem of hmeintr() threads which loop back around
* to call hmeproto and try to recursively acquire
* internal locks.
*/
break;
case M_IOCTL:
break;
case M_FLUSH:
}
else
break;
default:
"Default in message type");
break;
}
return (0);
}
/*
*
* internal locks that are held across upstream putnext calls.
* Specifically there's the problem of hmeintr() holding hme_intrlock
* and hmestruplock when it calls putnext() and that thread looping
* back around to call hmewput and, eventually, hmeinit() to create a
* recursive lock panic. There are two obvious ways of solving this
* problem: (1) have hmeintr() do putq instead of putnext which provides
* the loopback "cutout" right at the rq, or (2) allow hmeintr() to putnext
* and put the loopback "cutout" around hmeproto(). We choose the latter
* for performance reasons.
*
* M_DATA messages are enqueued on the wq *only* when the xmit side
* is out of tbufs or tmds. Once the xmit resource is available again,
* wsrv() is enabled and tries to xmit all the messages on the wq.
*/
static int
{
case M_DATA:
if (hmep) {
return (0);
} else
break;
case M_PROTO:
case M_PCPROTO:
break;
default:
ASSERT(0);
break;
}
return (0);
}
static void
{
union DL_primitives *dlp;
switch (prim) {
case DL_UNITDATA_REQ:
break;
case DL_ATTACH_REQ:
break;
case DL_DETACH_REQ:
break;
case DL_BIND_REQ:
break;
case DL_UNBIND_REQ:
break;
case DL_INFO_REQ:
break;
case DL_PROMISCON_REQ:
break;
case DL_PROMISCOFF_REQ:
break;
case DL_ENABMULTI_REQ:
break;
case DL_DISABMULTI_REQ:
break;
case DL_PHYS_ADDR_REQ:
break;
case DL_SET_PHYS_ADDR_REQ:
break;
case DL_NOTIFY_REQ:
break;
default:
break;
}
}
static struct hme *
{
int instance;
if (hmeup)
}
return (NULL);
}
break;
return (hmep);
}
static void
{
int error;
case DLIOCRAW: /* raw M_DATA mode */
break;
case DL_IOC_HDR_INFO: /* M_DATA "fastpath" info request */
break;
case HME_ND_GET:
return;
}
"hmeioctl:ND_GET");
"hmeioctl:false ret from hme_nd_getset");
return;
}
"hmeioctl:true ret from hme_nd_getset");
break;
case HME_ND_SET:
return;
}
"hmeioctl:ND_SET");
hme_param_autoneg = 0xff;
return;
}
if (old_device != hme_param_device) {
if ((new_device == -1) ||
return;
}
break;
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;
case HME_IOC:
if (error != 0) {
return;
}
case HME_IOC_GET_SPEED:
case HME_AUTO_SPEED:
break;
case HME_FORCE_SPEED:
break;
default:
"HME_IOC default get speed");
break;
}
break;
case HME_IOC_SET_SPEED:
hmep->hme_linkup = 0;
hmep->hme_linkup_cnt = 0;
/* Enable display of linkup message */
case HME_AUTO_SPEED:
"ioctl: AUTO_SPEED");
hmep->hme_linkup_10 = 0;
break;
case HME_FORCE_SPEED:
"ioctl: FORCE_SPEED");
break;
default:
"HME_IOC default set speed");
return;
}
break;
default:
break;
}
break;
default:
"HME_IOC default command");
break;
}
}
/*
* M_DATA "fastpath" info request.
* Following the M_IOCTL mblk should come a DL_UNITDATA_REQ mblk.
* We ack with an M_IOCACK pointing to the original DL_UNITDATA_REQ mblk
* followed by an mblk containing the raw ethernet header corresponding
* to the destination address. Subsequently, we may receive M_DATA
* msgs which start with this header and may send up
* up M_DATA msgs with b_rptr pointing to a (ulong_t) group address
* indicator followed by the network-layer data (IP packet header).
* This is all selectable on a per-Stream basis.
*/
static void
{
struct ether_header *headerp;
int error;
return;
}
if (error != 0) {
return;
}
/*
* Sanity check the DL_UNITDATA_REQ destination address
* offset and length values.
*/
return;
}
/*
* Allocate a new mblk to hold the ether header.
*/
return;
}
/*
* Fill in the ether header.
*/
/*
* Link new mblk in after the "request" mblks.
*/
}
static void
{
union DL_primitives *dlp;
return;
}
return;
}
/*
*/
/*
* Valid ppa?
*/
break;
}
}
}
return;
}
/* Set link to device and update our state. */
/*
* Has device been initialized? Do so if necessary.
* Also check if promiscuous mode is set via the ALLPHYS and
* ALLMULTI flags, for the stream. If so, initialize the
* interface.
*/
((promisc_multi_cnt + promisc_phys_cnt) > 0)) ||
/*
* Initialize the Interrupt mask
* The init will clear upon entry
* and reset upon success.
*/
return;
}
}
}
static void
{
return;
}
return;
}
}
/*
* Detach a Stream from an interface.
*/
static void
{
uint_t i;
/* Disable promiscuous mode if on. */
reinit = 1;
}
/* Disable ALLSAP mode if on. */
reinit = 1;
}
/* Disable ALLMULTI mode if on. */
reinit = 1;
}
/* Disable any Multicast Addresses. */
for (i = 0; i < NMCHASH; i++) {
reinit = 1;
sizeof (struct ether_addr));
}
}
for (i = 0; i < 4; i++)
for (i = 0; i < 64; i++)
sbp->sb_ladrf_refcnt[i] = 0;
/*
* Detach from device structure.
* Uninit the device
* when no other streams are attached to it.
*/
break;
else if (reinit) {
== 0) && ((promisc_phys_cnt + promisc_multi_cnt) > 0)) ||
}
}
static void
{
union DL_primitives *dlp;
return;
}
return;
}
return;
}
if (xidtest) {
return;
}
if (sap > ETHERTYPE_MAX) {
return;
}
/*
* Save SAP value for this Stream and change state.
*/
}
static void
{
return;
}
return;
}
}
static void
{
struct ether_addr *ep;
return;
}
/* Exchange current msg for a DL_INFO_ACK. */
return;
/* Fill in the DL_INFO_ACK fields and reply. */
*dlip = hmeinfoack;
} else {
}
}
static void
{
return;
}
case DL_PROMISC_PHYS:
break;
case DL_PROMISC_SAP:
break;
case DL_PROMISC_MULTI:
break;
default:
DL_NOTSUPPORTED, 0);
return;
}
if (hmep) {
}
}
static void
{
int flag;
return;
}
case DL_PROMISC_PHYS:
flag = HMESALLPHYS;
break;
case DL_PROMISC_SAP:
flag = HMESALLSAP;
break;
case DL_PROMISC_MULTI:
flag = HMESALLMULTI;
break;
default:
DL_NOTSUPPORTED, 0);
return;
}
return;
}
if (hmep) {
== 0) && ((promisc_multi_cnt + promisc_phys_cnt) > 0)) ||
}
}
/*
* This is to support unlimited number of members
* is MC.
*/
static void
{
union DL_primitives *dlp;
struct ether_addr *addrp;
struct ether_addr *mcbucket;
return;
}
return;
}
if ((len != ETHERADDRL) ||
return;
}
/*
* Calculate hash value and bucket.
*/
/*
* Allocate hash bucket if it's not there.
*/
KM_SLEEP);
}
/*
* We no longer bother checking to see if the address is already
* in the table (bugid 1209733). We won't reinitialize the
* hardware, since we'll find the mc bit is already set.
*/
/*
* Expand table if necessary.
*/
struct ether_addr *newbucket;
KM_SLEEP);
sizeof (struct ether_addr));
}
/*
* Add address to the table.
*/
/*
* If this address's bit was not already set in the local address
* filter, add it and re-initialize the Hardware.
*/
}
}
static void
{
union DL_primitives *dlp;
struct ether_addr *addrp;
int i;
struct ether_addr *mcbucket;
return;
}
return;
}
return;
}
/*
* Calculate hash value, get pointer to hash bucket for this address.
*/
/*
* Try and delete the address if we can find it.
*/
if (mcbucket) {
/*
* If there's more than one address in this
* bucket, delete the unwanted one by moving
* the last one in the list over top of it;
* otherwise, just free the bucket.
*/
mcbucket[i] =
} else {
sizeof (struct ether_addr));
}
/*
* If this address's bit should no longer be
* set in the local address filter, clear it and
* re-initialize the Hardware
*/
}
return;
}
}
}
}
static void
{
union DL_primitives *dlp;
struct ether_addr addr;
return;
}
return;
}
switch (type) {
case DL_FACT_PHYS_ADDR:
else
break;
case DL_CURR_PHYS_ADDR:
break;
default:
return;
}
}
static void
{
union DL_primitives *dlp;
struct ether_addr *addrp;
return;
}
return;
}
/*
* Error if length of address isn't right or the address
* specified is a multicast or broadcast address.
*/
if ((len != ETHERADDRL) ||
return;
}
/*
* Error if this stream is not attached to a device.
*/
return;
}
/*
* Set new interface local address and re-init device.
* This is destructive to any other streams attached
* to this device.
*/
}
static void
{
struct ether_header *headerp;
int hdrlen;
return;
}
/*
* Validate destination address format.
*/
return;
}
/*
* Error if no M_DATA follows.
*/
return;
}
hdrlen = sizeof (struct ether_header);
/*
* Create ethernet header by either prepending it onto the
* next mblk if possible, or reusing the M_PROTO block if not.
*/
/*
* Space available for ethernet hdr in M_DATA payload
*/
/*
* Space available in dl_unitdata_req M_PROTO blk. Reuse it.
*/
} else {
/*
* M_PROTO and M_DATA did not have space for ethernet hdr.
* Allocate new mblk.
*/
"allocb failed\n");
return;
}
}
/*
* In 802.3 mode, the driver looks at the
* sap field of the DL_BIND_REQ being 0 in addition to the destination
* sap field in the range [0-1500]. If either is true, then the driver
* computes the length of the message, not including initial M_PROTO
* mblk (message block), of all subsequent DL_UNITDATA_REQ messages and
* transmits 802.3 frames that have this value in the MAC frame header
* length field.
*/
} else {
}
}
static void
{
return;
}
return;
}
while (dl_notification) {
break;
dlnip->dl_notification = 0;
dlnip->dl_addr_length = 0;
dlnip->dl_addr_offset = 0;
if (dl_notification & DL_NOTE_PROMISC_ON_PHYS) {
if (hmep->promisc_phys_cnt)
} else if (dl_notification & DL_NOTE_PROMISC_OFF_PHYS) {
if (hmep->promisc_phys_cnt == 0)
} else if (dl_notification & DL_NOTE_LINK_DOWN) {
if (!hmep->hme_link_up)
} else if (dl_notification & DL_NOTE_LINK_UP) {
if (hmep->hme_link_up)
} else if (dl_notification & DL_NOTE_SPEED) {
/*
*/
}
if (dlnip->dl_notification) {
} else {
}
}
}
static void
{
/*
* Notify streams that are attached to *this* instance only.
*/
continue;
break;
if (notification == DL_NOTE_SPEED)
/*
*/
else
dlnip->dl_addr_length = 0;
dlnip->dl_addr_offset = 0;
}
}
}
static int
{
ulong_t i, j;
if (!hmep->hme_linkup) {
if ((hmep->hme_linkup_msg) &&
}
return (0);
}
hmep->hme_allocbfail++;
hmep->hme_noxmtbuf++;
}
} else {
if (i && (i < (HME_TMDMAX - HMETPENDING)))
}
goto notmds;
/*
* here we deal with 3 cases.
* 1. pkt has exactly one mblk
* 2. pkt has exactly two mblks
* 3. pkt has more than 2 mblks. Since this almost
* always never happens, we copy all of them
* into a msh with one mblk.
* for each mblk in the message, we allocate a tmd and
* figure out the tmd index. The index is then used to bind
* a DMA handle to the mblk and set up an IO mapping..
*/
len2 = 0;
"hmestart: 1 buf: len = %ld b_rptr = %p",
goto notmds;
"hmestart: 2 buf: len = %ld b_rptr = %p, "
"len = %ld b_rptr = %p",
} else {
hmep->hme_allocbfail++;
goto bad;
}
len2 = 0;
"hmestart: > 1 buf: len = %ld b_rptr = %p",
}
"ddi_dma_alloc_handle failed");
goto done;
}
"ddi_dma_addr_bind_handle failed");
goto done;
}
/* apparently they don't handle multiple cookies */
if (cnt > 1) {
"dmaxh crossed page boundary - failed");
goto done;
}
if (syncval == DDI_FAILURE)
"ddi_dma_sync failed");
if (bp) {
temp_addr = c.dmac_address;
"ddi_dma_alloc_handle failed");
goto done;
}
"ddi_dma_addr_bind_handle failed");
goto done;
}
/* apparently they don't handle multiple cookies */
if (cnt > 1) {
"dmaxh crossed page boundary - failed");
goto done;
}
if (syncval == DDI_FAILURE)
"ddi_dma_sync failed");
}
if (bp) {
} else {
HMETMD_SOP | HMETMD_EOP);
}
CHECK_IOPB();
CHECK_ETXREG();
nmp) {
}
hmep->hme_starts++;
return (0);
bad:
if (nmp)
return (1);
hmep->hme_notmds++;
done:
if (nmp)
return (1);
}
/*
* Start transmission.
* Return zero on success,
* otherwise put msg on wq, set 'want' flag and return nonzero.
*/
static int
{
uint32_t i, j;
struct ether_header *ehp;
/*
* update MIB II statistics
*/
if (!hmep->hme_linkup) {
if ((hmep->hme_linkup_msg) &&
}
return (0);
}
hmep->hme_allocbfail++;
hmep->hme_noxmtbuf++;
}
/*
* reclaim if there are more than HMETPENDING descriptors
* to be reclaimed.
*/
}
} else {
if (i && (i < (HME_TMDMAX - HMETPENDING))) {
}
}
goto notmds;
/*
* here we deal with 3 cases.
* 1. pkt has exactly one mblk
* 2. pkt has exactly two mblks
* 3. pkt has more than 2 mblks. Since this almost
* always never happens, we copy all of them
* into a msh with one mblk.
* for each mblk in the message, we allocate a tmd and
* figure out the tmd index. This index also passed to
* dvma_kaddr_load(), which establishes the IO mapping
* for the mblk data. This index is used as a index into
* the ptes reserved by dvma_reserve
*/
len1, 2 * i, &c);
} else {
/*
* Check with HW: The minimum len restriction different
* for 64-bit burst ?
*/
goto notmds;
len1, 2 * i, &c);
temp_addr = c.dmac_address;
len2, 2 * j, &c);
} else {
hmep->hme_allocbfail++;
hmep->hme_noxmtbuf++;
goto bad;
}
HMETMD_SOP | HMETMD_EOP);
}
}
CHECK_IOPB();
CHECK_ETXREG();
"hmestart: Transmitted a frame");
nmp) {
}
hmep->hme_starts++;
return (0);
bad:
if (nmp)
return (1);
hmep->hme_notmds++;
done:
if (nmp)
return (1);
}
/*
* 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;
int alloc_ret; /* hmeallocthings() return value */
"hmeinit start: hmep %p #0805", hmep);
/*
* 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.
*/
/*
* Lock sequence:
* hme_intrlock, hmestruplock and hme_xmitlock.
*/
/*
* 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.
*/
/*
*/
/*
* Allocate data structures.
*/
if (alloc_ret) {
/*
* Failed
*/
goto init_fail;
}
/*
* Clear all descriptors.
*/
/*
* Hang out receive buffers.
*/
for (i = 0; i < HMERPENDING; i++) {
"allocb failed");
goto init_fail;
}
/*
* dvma case
*/
if (hmep->hme_dvmarh) {
2 * i, &dma_cookie);
} else {
/*
* dma case
*/
!= DDI_SUCCESS) {
"ddi_dma_alloc_handle of bufs failed");
goto init_fail;
}
!= DDI_DMA_MAPPED) {
"ddi_dma_addr_bind_handle of bufs failed");
goto init_fail;
}
/* apparently they don't handle multiple cookies */
if (dmac_cnt > 1) {
"dmarh crossed page boundary - failed");
goto init_fail;
}
}
}
/*
* DMA sync descriptors.
*/
CHECK_IOPB();
/*
* Reset RMD and TMD 'walking' pointers.
*/
/*
* Determine if promiscuous mode.
*/
break;
}
}
/*
* 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
"hmeinit: ipg1 = %d ipg2 = %d", hme_param_ipg1,
/* 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.
*/
/*
* Here we initialize the MC Hash bits
*/
for (i = 0; i < 4; i++) {
ladrf[i] = 0xffff;
}
break; /* All bits are already on */
}
for (i = 0; i < 4; i++)
}
}
/*
* Set up the address filter now?
*/
/*
* 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
*/
{
}
"rxring written = %X",
"rxring read = %X",
GET_ERXREG(rxring));
/*
* 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)
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) {
| BMAC_RXCFG_MYOWN | BMAC_RXCFG_HASH));
} else {
| BMAC_RXCFG_HASH));
}
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 void
{
int i;
/*
* Free and dvma_unload pending xmit and recv buffers.
* Maintaining the 1-to-1 ordered sequence of
* Always unload anything before loading it again.
* Never unload anything twice. Always unload
* before freeing the buffer. We satisfy these
* requirements by unloading only those descriptors
* which currently have an mblk associated with them.
*/
/*
* Keep the ddi_dma_free() before the freeb()
* with the dma handles.
* Race condition with snoop.
*/
/* dma case */
for (i = 0; i < HME_TMDMAX; i++) {
if (freeval == DDI_FAILURE)
"ddi_dma_unbind_handle"
" failed");
}
}
for (i = 0; i < HMERPENDING; i++) {
if (freeval == DDI_FAILURE)
"ddi_dma_unbind_handle"
" failure");
}
}
}
/*
* This was generated when only a dma handle is expected.
* else HME_FAULT_MSG1(NULL, SEVERITY_HIGH, FREE_MSG,
* "hme: Expected a dma read handle:failed");
*/
for (i = 0; i < HME_TMDMAX; i++) {
if (hmep->hme_tmblkp[i]) {
if (hmep->hme_dvmaxh)
2 * i, DONT_FLUSH);
}
}
for (i = 0; i < HME_RMDMAX; i++) {
if (hmep->hme_rmblkp[i]) {
if (hmep->hme_dvmarh)
}
}
}
/*
* 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();
"mifpoll started: mif_cfg = %X mif_bsts = %X",
}
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
{
uintptr_t a;
int size;
int rval;
/*
* Return if resources are already allocated.
*/
return (0);
/*
* Allocate the TMD and RMD descriptors and extra for page alignment.
*/
+ HME_TMDMAX * sizeof (struct hme_tmd));
if (rval != DDI_SUCCESS) {
"cannot allocate rmd handle - failed");
return (1);
}
if (rval != DDI_SUCCESS) {
"cannot allocate trmd dma mem - failed");
return (1);
}
+ HME_TMDMAX * sizeof (struct hme_tmd));
DDI_DMA_DONTWAIT, 0,
if (rval != DDI_DMA_MAPPED) {
"cannot allocate trmd dma - failed");
return (1);
}
if (cookiec != 1) {
"trmds crossed page boundary - failed");
return (2);
return (1);
}
a = hmep->hme_iopbkbase;
a = ROUNDUP(a, HME_HMDALIGN);
a += HME_RMDMAX * sizeof (struct hme_rmd);
/*
* dvma_reserve() reserves DVMA space for private man
* device driver.
*/
/*
* Specifically we reserve n (HME_TMDMAX + HME_RMDMAX)
* pagetable entries. Therefore we have 2 ptes for each
* descriptor. Since the ethernet buffers are 1518 bytes
* so they can at most use 2 ptes.
* Will do a ddi_dma_addr_setup for each bufer
*/
/*
* We will now do a dma, due to the fact that
* dvma_reserve failied.
*/
(sizeof (ddi_dma_handle_t))), KM_SLEEP);
hmep->dmaxh_init++;
hmep->dmarh_init++;
} else {
/*
* Reserve dvma space for the receive side. If
* this call fails, we have to release the resources
* and fall back to the dma case.
*/
(sizeof (ddi_dma_handle_t))), KM_SLEEP);
hmep->dmaxh_init++;
hmep->dmarh_init++;
}
}
/*
* Keep handy limit values for RMD, TMD, and Buffers.
*/
/*
* Zero out xmit and rcv holders.
*/
return (0);
}
/*
* 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)
"hmeintr: hme not running");
return (serviced);
}
if (hmesbits & HMEG_STATUS_FATAL_ERR) {
"hmeintr: fatal error:hmesbits = %X", hmesbits);
if (hmep->hme_intrstats)
"fatal %x: re-init MAC", hmesbits);
return (serviced);
}
"hmeintr: non-fatal error:hmesbits = %X", hmesbits);
}
if (hmesbits & HMEG_STATUS_MIF_INTR) {
if (!(mif_status & PHY_BMSR_LNKSTS)) {
"hmeintr: mif interrupt: Link Down");
if (hmep->hme_intrstats)
hmep->hme_link_up = 0;
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)) {
"jabber detected");
/* national phy only defines this at 10 Mbps */
if (hme_param_speed == 0) { /* 10 Mbps speed ? */
"hmeintr: mif interrupt: Jabber detected");
/* treat jabber like a fatal error */
"jabber: re-init PHY & MAC");
return (serviced);
}
}
}
"hmeintr: packet transmitted");
}
if (hmesbits & HMEG_STATUS_RINT) {
/*
* This dummy PIO is required to flush the SBus
* Bridge buffers in QFE.
*/
#ifdef lint
#endif
"hmeintr: packet received: rmdp = %X", rmdp);
/*
* Sync RMD before looking at it.
*/
/*
* Loop through each RMD.
*/
(num_reads++ < HMERPENDING)) {
/*
* Increment to next RMD.
*/
/*
* Sync the next RMD before looking at it.
*/
}
CHECK_IOPB();
}
if (hmep->hme_intrstats)
return (serviced);
}
/*
* Transmit completion reclaiming.
*/
static void
{
int i;
int nbytes;
/*
* Sync TMDs before looking at them.
*/
* sizeof (struct hme_tmd));
} else {
* sizeof (struct hme_tmd));
* sizeof (struct hme_tmd));
}
CHECK_IOPB();
/*
* Loop through each TMD.
*/
/*
* count a chained packet only once.
*/
hmep->hme_opackets++;
hmep->hme_opackets64++;
}
/*
* MIB II
*/
"reclaim: tmdp = %X index = %d", tmdp, i);
/*
* dvma handle case.
*/
if (hmep->hme_dvmaxh)
(uint_t)DONT_FLUSH);
/*
* dma handle case.
*/
if (freeval == DDI_FAILURE)
"reclaim:ddi_dma_unbind_handle failure");
"reclaim: expected dmaxh");
if (hmep->hme_tmblkp[i]) {
}
}
/*
* 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
*/
}
}
/*
* Send packet upstream.
* Assume mp->b_rptr points to ether_header.
*/
static void
{
int type;
/*
* While holding a reader lock on the linked list of streams structures,
* attempt to match the address criteria for each stream
* and pass up the raw M_DATA ("fastpath") or a DL_UNITDATA_IND.
*/
return;
}
/*
* Loop on matching open streams until (*acceptfunc)() returns NULL.
*/
!isgroupaddr) {
sizeof (struct ether_header);
} else
hmep->hme_allocbfail++;
else
hmep->hme_nocanput++;
/*
* Do the last one.
*/
type, isgroupaddr)))
} else {
hmep->hme_nocanput++;
hmep->hme_norcvbuf++;
}
}
/*
* Test upstream destination sap and address match.
*/
static struct hmestr *
struct ether_addr *addrp)
{
(flags & HMESALLPHYS) ||
return (sbp);
}
return (NULL);
}
/*
* Test upstream destination sap and address match for HMESALLPHYS only.
*/
/* ARGSUSED3 */
static struct hmestr *
struct ether_addr *addrp)
{
(flags & HMESALLPHYS))
return (sbp);
}
return (NULL);
}
/*
* Set or clear the device ipq pointer.
* Assumes IPv4 and IPv6 are HMESFAST.
*/
static void
{
int ok4 = 1;
int ok6 = 1;
ok4 = 0;
ok6 = 0;
break;
}
else
ok4 = 0;
}
else
ok6 = 0;
}
}
}
if (ok4)
else
if (ok6)
else
}
/*
* Prefix msg with a DL_UNITDATA_IND mblk and return the new msg.
*/
static mblk_t *
{
int size;
/*
* Allocate an M_PROTO mblk for the DL_UNITDATA_IND.
*/
hmep->hme_allocbfail++;
"allocb failed");
return (NULL);
}
/*
* Construct a DL_UNITDATA_IND primitive.
*/
+ HMEADDRL);
/*
* Link the M_PROTO and M_DATA together.
*/
return (nmp);
}
/*
* Return TRUE if the given multicast address is one
* of those that this particular Stream is interested in.
*/
static int
{
struct ether_addr *mcbucket;
uint32_t i;
/*
* Return FALSE if not a multicast address.
*/
return (0);
/*
* Check if all multicasts have been enabled for this Stream
*/
return (1);
/*
* Compute the hash value for the address and
* grab the bucket and the number of entries in the
* bucket.
*/
/*
* Return FALSE if no multicast addresses enabled for this Stream.
*/
if (mccount == 0)
return (0);
/*
* Otherwise, find it in the table.
*/
if (mcbucket)
for (i = 0; i < mccount; i++)
return (1);
return (0);
}
/*
* Handle interrupts for fatal errors
* Need reinitialization of the ENET channel.
*/
static void
{
if (hmesbits & HMEG_STATUS_SLV_PAR_ERR) {
"sbus slave parity error");
hmep->hme_slvparerr++;
}
if (hmesbits & HMEG_STATUS_SLV_ERR_ACK) {
"sbus slave error ack");
hmep->hme_slverrack++;
}
if (hmesbits & HMEG_STATUS_TX_TAG_ERR) {
"tx tag error");
hmep->hme_txtagerr++;
hmep->hme_oerrors++;
}
if (hmesbits & HMEG_STATUS_TX_PAR_ERR) {
"sbus tx parity error");
hmep->hme_txparerr++;
hmep->hme_oerrors++;
}
if (hmesbits & HMEG_STATUS_TX_LATE_ERR) {
"sbus tx late error");
hmep->hme_txlaterr++;
hmep->hme_oerrors++;
}
if (hmesbits & HMEG_STATUS_TX_ERR_ACK) {
"sbus tx error ack");
hmep->hme_txerrack++;
hmep->hme_oerrors++;
}
if (hmesbits & HMEG_STATUS_EOP_ERR) {
"chained packet descriptor error");
hmep->hme_eoperr++;
}
if (hmesbits & HMEG_STATUS_RX_TAG_ERR) {
"rx tag error");
hmep->hme_rxtagerr++;
hmep->hme_ierrors++;
}
if (hmesbits & HMEG_STATUS_RX_PAR_ERR) {
"sbus rx parity error");
hmep->hme_rxparerr++;
hmep->hme_ierrors++;
}
if (hmesbits & HMEG_STATUS_RX_LATE_ERR) {
"sbus rx late error");
hmep->hme_rxlaterr++;
hmep->hme_ierrors++;
}
if (hmesbits & HMEG_STATUS_RX_ERR_ACK) {
"sbus rx error 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) {
"defer timer expired");
hmep->hme_defer_xmts++;
}
if (hmesbits & HMEG_STATUS_FSTCOLC_EXP) {
"first collision counter expired");
}
if (hmesbits & HMEG_STATUS_LATCOLC_EXP) {
"late collision");
}
if (hmesbits & HMEG_STATUS_EXCOLC_EXP) {
"retry error");
}
if (hmesbits & HMEG_STATUS_NRMCOLC_EXP) {
"first collision counter expired");
}
if (hmesbits & HMEG_STATUS_MXPKTSZ_ERR) {
"babble");
hmep->hme_oerrors++;
}
/*
* This error is fatal and the board needs to
* be reinitialized. Comments?
*/
if (hmesbits & HMEG_STATUS_TXFIFO_UNDR) {
"tx fifo underflow");
hmep->hme_oerrors++;
}
if (hmesbits & HMEG_STATUS_SQE_TST_ERR) {
"sqe test error");
hmep->hme_sqe_errors++;
}
if (hmesbits & HMEG_STATUS_RCV_CNT_EXP) {
if (hmep->hme_rxcv_enable) {
"code violation counter expired");
}
}
if (hmesbits & HMEG_STATUS_RXFIFO_OVFL) {
"rx fifo overflow");
hmep->hme_ierrors++;
}
if (hmesbits & HMEG_STATUS_LEN_CNT_EXP) {
"length error counter expired");
}
if (hmesbits & HMEG_STATUS_ALN_CNT_EXP) {
}
if (hmesbits & HMEG_STATUS_CRC_CNT_EXP) {
"rx crc error");
}
}
static void
{
long rmdi;
struct ether_header *ehp;
long nrmdi;
/*
* 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++;
CHECK_IOPB();
return;
}
/*
* Sync the received buffer before looking at it.
*/
"read: null handle!");
return;
}
if (syncval == DDI_FAILURE)
"read: ddi_dma_sync failure");
/*
* copy the packet data and then recycle the descriptor.
*/
CHECK_IOPB();
hmep->hme_ipackets++;
hmep->hme_ipackets64++;
/* Add the First Byte offset to the b_rptr and copy */
/*
* update MIB II statistics
*/
if ((type == ETHERTYPE_IP) &&
HMESFAST)) {
if (canputnext(ip4q)) {
} else {
hmep->hme_nocanput++;
hmep->hme_newfree++;
}
} else if ((type == ETHERTYPE_IPV6) &&
HMESFAST)) {
if (canputnext(ip6q)) {
} else {
hmep->hme_nocanput++;
hmep->hme_newfree++;
}
} else {
/* Strip the PADs for 802.3 */
+ sizeof (struct ether_header)
+ type;
}
} else {
CHECK_IOPB();
hmep->hme_allocbfail++;
hmep->hme_norcvbuf++;
"allocb failure");
}
}
static void
{
long rmdi;
struct ether_header *ehp;
long nrmdi;
return;
}
/*
* HMERMD_OWN has been cleared by the Happymeal hardware.
*/
/*
* 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++;
CHECK_IOPB();
return;
}
/*
* Copy small incoming packets to reduce memory consumption. The
* performance loss is compensated by the reduced overhead for
* DMA setup. The extra bytes before the actual data are copied
* to maintain the alignment of the payload.
*/
if ((len <= hme_rx_bcopy_max) &&
CHECK_IOPB();
hmep->hme_ipackets++;
hmep->hme_ipackets64++;
} else {
&c);
CHECK_IOPB();
hmep->hme_ipackets++;
hmep->hme_ipackets64++;
/*
* Add the First Byte offset to the b_rptr
*/
} else {
&c);
CHECK_IOPB();
hmep->hme_allocbfail++;
hmep->hme_norcvbuf++;
"allocb fail");
}
}
/*
* update MIB II statistics
*/
if ((type == ETHERTYPE_IP) &&
HMESFAST)) {
if (canputnext(ip4q)) {
} else {
hmep->hme_newfree++;
hmep->hme_nocanput++;
}
} else if ((type == ETHERTYPE_IPV6) &&
HMESFAST)) {
if (canputnext(ip6q)) {
} else {
hmep->hme_newfree++;
hmep->hme_nocanput++;
}
} else {
/*
* Strip the PADs for 802.3
*/
+ sizeof (struct ether_header)
+ type;
}
}
}
/*
* Start xmit on any msgs previously enqueued on any write queues.
*/
static void
{
/*
* Order of wantw accesses is important.
*/
do {
}
#ifdef HME_DEBUG
/*VARARGS*/
static void
{
char msg_buffer[255];
#ifdef HIGH_SEVERITY
if (severity != SEVERITY_HIGH)
return;
#endif
if (hme_debug_level >= type) {
}
}
#endif
/*VARARGS*/
/* ARGSUSED */
static void
{
char msg_buffer[255];
else if (severity == SEVERITY_HIGH)
"%s%d : %s, SEVERITY_HIGH, %s\n",
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);
}
/*
* Convert Ethernet address to printable (loggable) representation.
*/
char *
{
int i;
static char etherbuf[18];
static char digits[] = "0123456789abcdef";
for (i = 0; i < 6; i++) {
if (*ap > 0x0f)
*cp++ = ':';
}
*--cp = 0;
return (etherbuf);
}
/*
* 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.
*/
if (prop_len == ETHERADDRL) {
}
}
/*
* 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;
}
}
return;
}
}
/*
* Get the system ethernet address.
*/
}
static void
{
char linkup_msg[64];
if (speed == HME_SPEED_100)
else if (speed == HME_SPEED_10)
else
linkup_msg[0] = '\0';
else
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static void
{
}
static void *
{
return (NULL);
}
return (mp);
}