/*
* 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
*/
/*
*/
/*
* 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 <sys/byteorder.h>
#include "hme_phy.h"
#include "hme_mac.h"
#include "hme.h"
typedef void (*fptrv_t)();
typedef enum {
NO_MSG = 0,
} msg_t;
static char *msg_string[] = {
"NONE ",
"AUTOCONFIG ",
"DISPLAY "
"INIT ",
"UNINIT ",
"CONFIG ",
"MII ",
"FATAL_ERR ",
"NFATAL_ERR ",
"XCVR ",
"NOXCVR ",
"ERX ",
"DDI ",
};
#define SEVERITY_NONE 0
#define SEVERITY_LOW 0
#define FEPS_URUN_BUG
#define HME_CODEVIOL_BUG
/*
*/
/*
* The following variables are used for configuring various features
*/
char *hme_priv_prop[] = {
"_ipg0",
"_ipg1",
"_ipg2",
"_lance_mode",
};
/*
* 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.
*/
/*
* All strings used by hme messaging functions
*/
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 *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.";
/* 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_bb_force_idle(struct hme *);
static void hme_mii_notify(void *, link_state_t);
/*
* 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 void hme_m_propinfo(void *, const char *, mac_prop_id_t,
const void *);
};
NULL,
NULL,
NULL,
NULL,
};
#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.
*/
DMA_ATTR_V0, /* version number. */
512, /* granularity */
0 /* attribute flags */
};
DDI_STRICTORDER_ACC, /* probably could allow merging & caching */
};
/*
* 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 */
};
};
/* <<<<<<<<<<<<<<<<<<<<<< Register operations >>>>>>>>>>>>>>>>>>>>> */
/*
* Ether_copy is not endian-correct. Define an endian-correct version.
*/
/*
* Ether-type is specifically big-endian, but data region is unknown endian
*/
/* <<<<<<<<<<<<<<<<<<<<<< Configuration Parameters >>>>>>>>>>>>>>>>>>>>> */
/*
* 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 according to the IEEE Standard
*/
static uint16_t
{
uint16_t x;
if (phyad == HME_INTERNAL_PHYAD)
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 uint16_t
{
int i;
uint32_t x;
(void) hme_bb_force_idle(hmep);
for (i = 4; i >= 0; i--) { /* <AAAAA> */
}
for (i = 4; i >= 0; i--) { /* <RRRRR> */
}
GET_BIT_STD(phyad, x);
for (i = 0xf; i >= 0; i--) { /* <DDDDDDDDDDDDDDDD> */
GET_BIT_STD(phyad, x);
data += (x << i);
}
/*
* Kludge to get the Transceiver out of hung mode
*/
GET_BIT_STD(phyad, x);
GET_BIT_STD(phyad, x);
GET_BIT_STD(phyad, x);
CHECK_MIFREG();
return (data);
}
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 uint16_t
{
switch (phyad) {
case HME_EXTERNAL_PHYAD:
break;
case HME_INTERNAL_PHYAD:
break;
default:
return (0xffff);
}
if (!hmep->hme_frame_enable) {
return (frame & 0xffff);
}
(regad << HME_MIF_FRREGAD_SHIFT));
/*
* HMEDELAY((*framerp & HME_MIF_FRTA0), HMEMAXRSTDELAY);
*/
CHECK_MIFREG();
if ((frame & HME_MIF_FRTA0) == 0) {
"MIF Read failure");
return (0xffff);
}
}
static void
{
switch (phyad) {
case HME_EXTERNAL_PHYAD:
break;
case HME_INTERNAL_PHYAD:
break;
default:
return;
}
if (!hmep->hme_frame_enable) {
return;
}
/*
* HMEDELAY((*framerp & HME_MIF_FRTA0), HMEMAXRSTDELAY);
*/
CHECK_MIFREG();
if ((frame & HME_MIF_FRTA0) == 0) {
"MIF Write failure");
}
}
static void
{
if (link == LINK_STATE_UP) {
}
}
/* <<<<<<<<<<<<<<<<<<<<<<<<<<< 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.
*/
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
{
int i, needprop = 0;
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
{
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
{
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_CONTINUE);
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, it turns out
* that it is safe to call this during our attach routine,
* because we are not a nexus device.
*
* Previously we rooted our search at our immediate parent,
* but this triggered an assertion panic in debug kernels.
*/
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);
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_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) {
"ddi_prop_create error for hm_rev");
}
/* get info via VPD */
"no promprops");
}
}
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;
}
/* our external (preferred) PHY is at address 0 */
"mii_alloc failed");
goto error_intr;
}
/* force a probe for the PHY */
"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.
*/
/*
* 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 prop_len = sizeof (int);
/*
* 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.
*/
/*
* Get the parameter values configured in .conf file.
*/
}
}
}
}
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;
"kstat_create failed");
return;
}
if (hmep->hme_intrstats)
/*
* Debugging kstats
*/
/*
* xcvr kstats
*/
}
int
void *val)
{
int value;
int rv;
return (rv);
switch (num) {
case MAC_PROP_PRIVATE:
break;
default:
return (ENOTSUP);
}
} else {
return (ENOTSUP);
}
return (0);
}
static void
{
switch (num) {
case MAC_PROP_PRIVATE: {
int default_val;
} else {
return;
}
break;
}
}
}
int
const void *val)
{
int rv;
long lval;
return (rv);
rv = 0;
switch (num) {
case MAC_PROP_PRIVATE:
break;
default:
return (ENOTSUP);
}
} else {
return (EINVAL);
}
} else {
return (EINVAL);
}
} else {
return (EINVAL);
}
} else {
return (EINVAL);
}
} else {
}
if (init) {
}
return (rv);
}
/*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
{
/* initialization failed -- really want DL_INITFAILED */
return (EIO);
} else {
return (0);
}
}
static void
{
}
static int
{
}
return (0);
}
switch (stat) {
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;
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 */
}
(start_offset > HMETMD_CSSTART_MAX) ||
(stuff_offset > HMETMD_CSSTUFF_MAX))) {
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
#endif
static int
{
uint32_t i;
int ret;
int phyad;
/*
* 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.
*/
/*
*/
/*
* 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.
*/
case -1:
goto init_fail; /* abort initialization */
case HME_INTERNAL_PHYAD:
PUT_MACREG(xifc, 0);
break;
case HME_EXTERNAL_PHYAD:
/* Isolate the Int. xcvr */
break;
}
/*
* 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;
if (phyad == HME_INTERNAL_PHYAD)
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++) {
}
}
}
}
}
/*
* 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_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
{
} 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();
}
/*
* 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
{
}