eri.c revision 6213860b943e0dc644bdec5d9f94034cab88816e
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* SunOS MT STREAMS ERI(PCI) 10/100 Mb Ethernet Device Driver
*/
#include <sys/ethernet.h>
#include <sys/mac_provider.h>
#include <sys/mac_ether.h>
#include "eri_phy.h"
#include "eri_mac.h"
#include "eri.h"
#include "eri_common.h"
#include "eri_msg.h"
/*
* **** Function Prototypes *****
*/
/*
* Entry points (man9e)
*/
/*
*/
/*
* Initialization Functions
*/
static int eri_allocthings(struct eri *);
static int eri_init_xfer_params(struct eri *);
static void eri_statinit(struct eri *);
static int eri_burstsize(struct eri *);
static void eri_init_rx(struct eri *);
static void eri_init_txmac(struct eri *);
/*
* Un-init Functions
*/
static int eri_freebufs(struct eri *);
/*
* Transceiver (xcvr) Functions
*/
static int eri_reset_xcvr(struct eri *);
#endif
static void eri_check_link(struct eri *);
/*
* Reset Functions
*/
/*
* Error Functions
*/
#ifdef ERI_TX_HUNG
static int eri_check_txhung(struct eri *);
#endif
/*
* Hardening Functions
*/
/*
* Misc Functions
*/
static void eri_savecntrs(struct eri *);
static void eri_bb_force_idle(struct eri *);
/*
* Utility Functions
*/
/*
* Functions to support ndd
*/
static void eri_param_cleanup(struct eri *);
/*
* Nemo (GLDv3) Functions.
*/
static int eri_m_start(void *);
static void eri_m_stop(void *);
static int eri_m_promisc(void *, boolean_t);
static int eri_m_unicst(void *, const uint8_t *);
static mac_callbacks_t eri_m_callbacks = {
};
/*
* Define PHY Vendors: Matches to IEEE
* Organizationally Unique Identifier (OUI)
*/
/*
* The first two are supported as Internal XCVRs
*/
#define PHY_VENDOR_LUCENT 0x601d
#define PHY_LINK_NONE 0 /* Not attempted yet or retry */
#define AUTO_SPEED 0
#define FORCE_SPEED 1
/*
*/
if (IS_BROADCAST(pkt)) { \
} else if (IS_MULTICAST(pkt)) { \
}
if (IS_BROADCAST(pkt)) { \
} else if (IS_MULTICAST(pkt)) { \
}
#define ETHERHEADER_SIZE (sizeof (struct ether_header))
#ifdef ERI_RCV_CKSUM
{ \
t_uscalar_t type; \
\
\
/* \
* update MIB II statistics \
*/ \
start_offset = 0; \
HCK_PARTIALCKSUM, 0); \
} else { \
/* \
* Strip the PADS for 802.3 \
*/ \
ETHERHEADER_SIZE + type; \
} \
}
#else
{ \
t_uscalar_t type; \
\
/* \
* update MIB II statistics \
*/ \
/* \
* Strip the PADS for 802.3 \
*/ \
type; \
}
#endif /* ERI_RCV_CKSUM */
/*
* TX Interrupt Rate
*/
static int tx_interrupt_rate = 16;
/*
* Ethernet broadcast address definition.
*/
static uint8_t etherbroadcastaddr[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
/*
* The following variables are used for configuring various features
*/
#define ERI_DESC_HANDLE_ALLOC 0x0001
#define ERI_DESC_MEM_ALLOC 0x0002
#define ERI_DESC_MEM_MAP 0x0004
#define ERI_RCV_HANDLE_ALLOC 0x0020
#define ERI_RCV_HANDLE_BIND 0x0040
#define ERI_XMIT_DVMA_ALLOC 0x0100
#define ERI_RCV_DVMA_ALLOC 0x0200
#define ERI_XBUFS_HANDLE_ALLOC 0x0400
#define ERI_XBUFS_KMEM_ALLOC 0x0800
#define ERI_XBUFS_KMEM_DMABIND 0x1000
#define ERI_DONT_STRIP_CRC
/*
* Translate a kernel virtual address to i/o address.
*/
#define ERI_IOPBIOADDR(erip, a) \
/*
* ERI Configuration Register Value
* Used to configure parameters that define DMA burst
* and internal arbitration behavior.
* for equal TX and RX bursts, set the following in global
* configuration register.
* static int global_config = 0x42;
*/
/*
* ERI ERX Interrupt Blanking Time
* Each count is about 16 us (2048 clocks) for 66 MHz PCI.
*/
/*
* ERX PAUSE Threshold Register value
* The following value is for an OFF Threshold of about 15.5 Kbytes
* and an ON Threshold of 4K bytes.
*/
static int eri_reinit_fatal = 0;
#ifdef DEBUG
static int noteri = 0;
#endif
#ifdef ERI_TX_HUNG
static int eri_reinit_txhung = 0;
#endif
#ifdef ERI_HDX_BUG_WORKAROUND
/*
* By default enable padding in hdx mode to 97 bytes.
* set eri:eri_hdx_pad_enable=0
*/
#endif
/*
* Default values to initialize the cache line size and latency timer
* registers in the PCI configuration space.
* ERI_G_CACHE_LINE_SIZE_16 is defined as 16 since RIO expects in units
* of 4 bytes.
*/
#ifdef ERI_PM_WORKAROUND_PCI
#else
#endif
/*
* Claim the device is ultra-capable of burst in the beginning. Use
* the value returned by ddi_dma_burstsizes() to actually set the ERI
* global configuration register later.
*
* PCI_ERI supports Infinite burst or 64-byte-multiple bursts.
*/
static ddi_dma_attr_t dma_attr = {
DMA_ATTR_V0, /* version number. */
(uint_t)0 /* attribute flags */
};
static ddi_dma_attr_t desc_dma_attr = {
DMA_ATTR_V0, /* version number. */
16, /* granularity */
0 /* attribute flags */
};
static ddi_device_acc_attr_t buf_attr = {
DDI_DEVICE_ATTR_V0, /* devacc_attr_version */
DDI_NEVERSWAP_ACC, /* devacc_attr_endian_flags */
DDI_STRICTORDER_ACC, /* devacc_attr_dataorder */
DDI_DEFAULT_ACC, /* devacc_attr_access */
};
1024 /* dlim_speed */
};
/*
* Link Configuration variables
*
* On Motherboard implementations, 10/100 Mbps speeds may be supported
* by using both the Serial Link and the MII on Non-serial-link interface.
* When both links are present, the driver automatically tries to bring up
* both. If both are up, the Gigabit Serial Link is selected for use, by
* default. The following configuration variable is used to force the selection
* of one of the links when both are up.
* To change the default selection to the MII link when both the Serial
* Link and the MII link are up, change eri_default_link to 1.
*
* Once a link is in use, the driver will continue to use that link till it
* goes down. When it goes down, the driver will look at the status of both the
* links again for link selection.
*
* Currently the standard is not stable w.r.t. gigabit link configuration
* using auto-negotiation procedures. Meanwhile, the link may be configured
* in "forced" mode using the "autonegotiation enable" bit (bit-12) in the
* PCS MII Command Register. In this mode the PCS sends "idles" until sees
* "idles" as initialization instead of the Link Configuration protocol
* where a Config register is exchanged. In this mode, the ERI is programmed
* for full-duplex operation with both pauseTX and pauseRX (for flow control)
* enabled.
*/
static int select_link = 0; /* automatic selection */
static int default_link = 0; /* Select Serial link if both are up */
/*
* The following variables are used for configuring link-operation
* for all the "eri" interfaces in the system.
* 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.
*/
/*
* The following variable value will be overridden by "link-pulse-disabled"
* property which may be created by OBP or eri.conf file. This property is
* applicable only for 10 Mbps links.
*/
static int link_pulse_disabled = 0; /* link pulse disabled */
/* For MII-based FastEthernet links */
static int adv_autoneg_cap = 1;
static int adv_100T4_cap = 0;
static int adv_100fdx_cap = 1;
static int adv_100hdx_cap = 1;
static int adv_10fdx_cap = 1;
static int adv_10hdx_cap = 1;
static int adv_pauseTX_cap = 0;
static int adv_pauseRX_cap = 0;
/*
* The following gap parameters are in terms of byte times.
*/
static int ipg0 = 8;
static int ipg1 = 8;
static int ipg2 = 4;
static int mifpoll_enable = 0; /* to enable mif poll */
static int ngu_enable = 0; /* to enable Never Give Up mode */
static int eri_force_mlf = 0; /* to enable mif poll */
/*
* For the MII interface, the External Transceiver is selected when present.
* The following variable is used to select the Internal Transceiver even
* when the External Transceiver is present.
*/
static int use_int_xcvr = 0;
static int pace_size = 0; /* Do not use pacing for now */
static int eri_use_dvma_rx = 0; /* =1:use dvma */
/*
* 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 "ERI_NOTUSR" is ORed with the parameter value to indicate values
* which are NOT configured by the user.
*/
#define ERI_NOTUSR 0x0f000000
#define ERI_MASK_1BIT 0x1
#define ERI_MASK_2BIT 0x3
#define ERI_MASK_8BIT 0xff
/*
* Note:
* ERI has all of the above capabilities.
* Only when an External Transceiver is selected for MII-based FastEthernet
* link operation, the capabilities depend upon the capabilities of the
* External Transceiver.
*/
/* ------------------------------------------------------------------------- */
{ 0, 2, 2, "-transceiver_inuse"},
{ 0, 1, 0, "-link_status"},
{ 0, 1, 0, "-link_speed"},
{ 0, 1, 0, "-link_mode"},
{ 0, 255, 8, "+ipg1"},
{ 0, 255, 4, "+ipg2"},
{ 0, 1, 0, "+use_int_xcvr"},
{ 0, 255, 0, "+pace_size"},
{ 0, 1, 1, "+adv_autoneg_cap"},
{ 0, 1, 1, "+adv_100T4_cap"},
{ 0, 1, 1, "+adv_100fdx_cap"},
{ 0, 1, 1, "+adv_100hdx_cap"},
{ 0, 1, 1, "+adv_10fdx_cap"},
{ 0, 1, 1, "+adv_10hdx_cap"},
{ 0, 1, 1, "-autoneg_cap"},
{ 0, 1, 1, "-100T4_cap"},
{ 0, 1, 1, "-100fdx_cap"},
{ 0, 1, 1, "-100hdx_cap"},
{ 0, 1, 1, "-10fdx_cap"},
{ 0, 1, 1, "-10hdx_cap"},
{ 0, 1, 0, "-lp_autoneg_cap"},
{ 0, 1, 0, "-lp_100T4_cap"},
{ 0, 1, 0, "-lp_100fdx_cap"},
{ 0, 1, 0, "-lp_100hdx_cap"},
{ 0, 1, 0, "-lp_10fdx_cap"},
{ 0, 1, 0, "-lp_10hdx_cap"},
{ 0, 1, 1, "+lance_mode"},
{ 0, 31, 8, "+ipg0"},
{ 0, 127, 6, "+intr_blank_time"},
{ 0, 255, 8, "+intr_blank_packets"},
{ 0, 1, 1, "!serial-link"},
{ 0, 2, 1, "!non-serial-link"},
{ 0, 1, 0, "%select-link"},
{ 0, 1, 0, "%default-link"},
{ 0, 2, 0, "!link-in-use"},
{ 0, 1, 1, "%adv_asm_dir_cap"},
{ 0, 1, 1, "%adv_pause_cap"},
{ 0, 1, 0, "!asm_dir_cap"},
{ 0, 1, 0, "!pause_cap"},
{ 0, 1, 0, "!lp_asm_dir_cap"},
{ 0, 1, 0, "!lp_pause_cap"},
};
/*
* This is the loadable module wrapper.
*/
/*
* Module linkage information for the kernel.
*/
&mod_driverops, /* Type of module. This one is a driver */
"Sun RIO 10/100 Mb Ethernet",
&eri_dev_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
};
/*
* Hardware Independent Functions
* New Section
*/
int
_init(void)
{
int status;
}
return (status);
}
int
_fini(void)
{
int status;
if (status == 0) {
}
return (status);
}
int
{
}
/*
* 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;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_FAILURE);
param_linkup = 0;
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/*
* Allocate soft device data structure
*/
/*
* Initialize as many elements as possible.
*/
erip->multi_refcnt = 0;
"mac_alloc failed");
goto attach_fail;
}
/*
* Map in the device registers.
* Separate pointers will be set up for the following
* register groups within the GEM Register Space:
* Global register set
* ETX register set
* ERX register set
* BigMAC register set.
* MIF register set
*/
"ddi_dev_nregs failed, returned %d", regno);
goto attach_fail;
}
/*
* Map the PCI config space
*/
"%s pci_config_setup()", config_space_fatal_msg);
goto attach_fail;
}
/*
* Initialize device attributes structure
*/
goto attach_fail;
}
/*
* Map the software reset register.
*/
goto attach_fail;
}
/*
* Try and stop the device.
* This is done until we want to handle interrupts.
*/
goto attach_fail;
/*
* set PCI latency timer register.
*/
if (ddi_intr_hilevel(dip, 0)) {
" high-level interrupts are not supported");
goto attach_fail;
}
/*
* Get the interrupt cookie so the mutexes can be
* Initialized.
*/
goto attach_fail;
/*
* Initialize mutex's for this device.
*/
/*
* Add interrupt to system
*/
else {
goto attach_fail;
}
/*
* Set up the ethernet mac address.
*/
if (eri_init_xfer_params(erip))
goto attach_fail;
goto attach_fail;
}
/*
* Setup fewer receive bufers.
*/
goto attach_fail;
return (DDI_SUCCESS);
if (erip->pci_config_handle)
if (mutex_inited) {
}
if (intr_add)
return (DDI_FAILURE);
}
static int
{
int i;
/*
* No resources allocated.
*/
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* Make the device quiescent
*/
/*
* Remove instance of the intr
*/
if (erip->pci_config_handle)
/*
* Destroy all mutexes and data structures allocated during
* attach time.
*/
if (erip->sw_reset_regh)
return (DDI_FAILURE);
}
if (eri_freebufs(erip))
return (DDI_FAILURE);
/* dvma handle case */
if (erip->eri_dvmarh) {
}
/*
* xmit_dma_mode, erip->ndmaxh[i]=NULL for dvma
*/
else {
for (i = 0; i < ERI_RPENDING; i++)
}
/*
* Release TX buffer
*/
if (erip->tbuf_ioaddr != 0) {
erip->tbuf_ioaddr = 0;
}
}
}
return (DDI_SUCCESS);
}
/*
* 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 *uselocal;
unsigned prop_len;
struct ether_addr factaddr;
/*
* 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;
}
}
&uselocal) == DDI_PROP_SUCCESS) {
(addrflags & ERI_FACTADDR_PRESENT)) {
return;
}
}
/*
* Get the system ethernet address.
*/
}
/*
* Calculate the bit in the multicast address filter that selects the given
* address.
* Note: For ERI, the last 8-bits are used.
*/
static uint32_t
{
/*
* Just want the 8 most significant bits.
*/
return ((~crc) >> 24);
}
static void
{
int err;
/*
* Privilege checks.
*/
case ERI_SET_LOOP_MODE:
case ERI_ND_SET:
if (err != 0) {
return;
}
break;
default:
break;
}
case ERI_ND_GET:
case ERI_ND_SET:
break;
case ERI_SET_LOOP_MODE:
case ERI_GET_LOOP_MODE:
/*
* XXX: Consider updating this to the new netlb ioctls.
*/
break;
default:
break;
}
}
static void
{
loopback_t *al;
return;
}
case ERI_SET_LOOP_MODE:
case ERI_LOOPBACK_OFF:
/* force link status to go down */
param_linkup = 0;
break;
case ERI_MAC_LOOPBACK_ON:
param_linkup = 0;
break;
case ERI_PCS_LOOPBACK_ON:
break;
case ERI_SER_LOOPBACK_ON:
/* force link status to go down */
param_linkup = 0;
break;
default:
return;
}
break;
case ERI_GET_LOOP_MODE:
break;
default:
}
}
static int
{
return (0);
}
/*
* This is to support unlimited number of members
* in Multicast.
*/
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) {
erip->multi_refcnt++;
}
} else {
erip->multi_refcnt--;
}
}
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);
}
/*ARGSUSED*/
static boolean_t
{
switch (cap) {
case MAC_CAPAB_HCKSUM: {
return (B_TRUE);
}
default:
return (B_FALSE);
}
}
static int
eri_m_start(void *arg)
{
return (EIO);
}
return (0);
}
static void
eri_m_stop(void *arg)
{
}
static int
{
}
if (macupdate)
switch (stat) {
case MAC_STAT_IFSPEED:
break;
case MAC_STAT_MULTIRCV:
break;
case MAC_STAT_BRDCSTRCV:
break;
case MAC_STAT_IPACKETS:
break;
case MAC_STAT_RBYTES:
break;
case MAC_STAT_OBYTES:
break;
case MAC_STAT_OPACKETS:
break;
case MAC_STAT_IERRORS:
break;
case MAC_STAT_OERRORS:
break;
case MAC_STAT_MULTIXMT:
break;
case MAC_STAT_BRDCSTXMT:
break;
case MAC_STAT_NORCVBUF:
break;
case MAC_STAT_NOXMTBUF:
break;
case MAC_STAT_UNDERFLOWS:
break;
case MAC_STAT_OVERFLOWS:
break;
case MAC_STAT_COLLISIONS:
break;
case ETHER_STAT_ALIGN_ERRORS:
break;
case ETHER_STAT_FCS_ERRORS:
break;
case ETHER_STAT_EX_COLLISIONS:
break;
break;
break;
case ETHER_STAT_LINK_DUPLEX:
break;
break;
break;
case ETHER_STAT_XCVR_ADDR:
break;
case ETHER_STAT_XCVR_INUSE:
break;
case ETHER_STAT_CAP_100FDX:
*val = param_bmsr_100fdx;
break;
case ETHER_STAT_CAP_100HDX:
*val = param_bmsr_100hdx;
break;
case ETHER_STAT_CAP_10FDX:
*val = param_bmsr_10fdx;
break;
case ETHER_STAT_CAP_10HDX:
*val = param_bmsr_10hdx;
break;
case ETHER_STAT_CAP_AUTONEG:
*val = param_bmsr_ancap;
break;
case ETHER_STAT_CAP_ASMPAUSE:
break;
case ETHER_STAT_CAP_PAUSE:
*val = param_bmsr_pause;
break;
*val = param_anar_100fdx;
break;
*val = param_anar_100hdx;
break;
case ETHER_STAT_ADV_CAP_10FDX:
*val = param_anar_10fdx;
break;
case ETHER_STAT_ADV_CAP_10HDX:
*val = param_anar_10hdx;
break;
*val = param_autoneg;
break;
break;
case ETHER_STAT_ADV_CAP_PAUSE:
*val = param_anar_pause;
break;
case ETHER_STAT_LP_CAP_100FDX:
break;
case ETHER_STAT_LP_CAP_100HDX:
break;
case ETHER_STAT_LP_CAP_10FDX:
break;
case ETHER_STAT_LP_CAP_10HDX:
break;
break;
break;
case ETHER_STAT_LP_CAP_PAUSE:
break;
case ETHER_STAT_LINK_PAUSE:
break;
case ETHER_STAT_LINK_ASMPAUSE:
*val = param_anar_asm_dir &&
break;
case ETHER_STAT_LINK_AUTONEG:
break;
}
return (0);
}
/*
* Hardware Functions
* New Section
*/
/*
* Initialize the MAC registers. Some of of the MAC registers are initialized
* just once since Global Reset or MAC reset doesn't clear them. Others (like
* Host MAC Address Registers) are cleared on every reset and have to be
* reinitialized.
*/
static void
{
/*
* set up the MAC parameter registers once
* setting these registers.
*/
erip->init_macregs = 0;
#ifdef ERI_RX_TAG_ERROR_WORKAROUND
#else
#endif
/* Program MAC Control address */
}
/* The counters need to be zeroed */
PUT_MACREG(nccnt, 0);
PUT_MACREG(fccnt, 0);
PUT_MACREG(excnt, 0);
PUT_MACREG(ltcnt, 0);
PUT_MACREG(dcnt, 0);
PUT_MACREG(frcnt, 0);
PUT_MACREG(lecnt, 0);
PUT_MACREG(aecnt, 0);
PUT_MACREG(fecnt, 0);
PUT_MACREG(rxcv, 0);
else
PUT_MACREG(spcmd, 0);
/*
* Program BigMAC with local individual ethernet address.
*/
/*
* Install multicast address filter.
*/
}
static int
{
uint_t i;
int status = 0;
/*
* Free and dvma_unload pending recv buffers.
* Maintaining the 1-to-1 ordered sequence of
* dvma_load() followed by dvma_unload() is critical.
* 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.
*/
for (i = 0; i < ERI_RPENDING; i++) {
if (erip->eri_dvmarh)
status = -1;
}
}
return (status);
}
static void
{
/*
* Clear TX descriptors.
*/
/*
* sync TXDMA descriptors.
*/
/*
* Reset TMD 'walking' pointers.
*/
erip->tx_cur_cnt = 0;
erip->tx_completion = 0;
}
static int
{
int i, status = 0;
/*
* clear rcv descriptors
*/
for (i = 0; i < ERI_RPENDING; i++) {
status = -1;
continue;
}
/* Load data buffer to DVMA space */
if (erip->eri_dvmarh)
2 * i, &dma_cookie);
/*
* Bind data buffer to DMA handle
*/
status = -1;
}
/*
* sync RXDMA descriptors.
*/
/*
* Reset RMD 'walking' pointers.
*/
erip->rx_completion = 0;
return (status);
}
static uint32_t
{
int n;
while (--n > 0) {
return (0);
}
return (1);
}
static uint32_t
{
int n;
n = BMACRXRSTDELAY / ERI_WAITPERIOD;
while (--n > 0) {
return (0);
}
return (1);
}
/*
* Return 0 upon success, 1 on failure.
*/
static int
{
(void) eri_erx_reset(erip);
(void) eri_etx_reset(erip);
/*
* set up cache line to 16 for 64 bytes of pci burst size
*/
} else {
param_linkup = 0;
}
/*
* workaround for RIO not resetting the interrupt mask
* register to default value 0xffffffff.
*/
return (0);
} else {
return (1);
}
}
/*
* Reset Just the RX Portion
* Return 0 upon success, 1 on failure.
*
* Resetting the rxdma while there is a rx dma transaction going on the
* bus, will cause bus hang or parity errors. To avoid this, we would first
* disable the rxdma by clearing the ENABLE bit (bit 0). To make sure it is
* disabled, we will poll it until it realy clears. Furthermore, to verify
* any RX DMA activity is subsided, we delay for 5 msec.
*/
static uint32_t
{
/* Disable the RX DMA */
/*
* Wait until the reset is completed which is indicated by
* the reset bit cleared or time out..
*/
}
/*
* Reset Just the TX Portion
* Return 0 upon success, 1 on failure.
* Resetting the txdma while there is a tx dma transaction on the bus, may cause
* bus hang or parity errors. To avoid this we would first disable the txdma by
* clearing the ENABLE bit (bit 0). To make sure it is disabled, we will poll
* it until it realy clears. Furthermore, to any TX DMA activity is subsided,
* we delay for 1 msec.
*/
static uint32_t
{
(void) eri_txmac_disable(erip);
/* Disable the TX DMA */
#ifdef ORIG
#endif
/*
* Wait until the reset is completed which is indicated by the reset bit
* cleared or time out..
*/
return (1);
} else
return (0);
}
/*
* Initialize the TX DMA registers and Enable the TX DMA.
*/
static uint32_t
{
uint32_t i;
/*
* Initialize ETX Registers:
* config, txring_lo, txring_hi
*/
/*
* Get TX Ring Size Masks.
* The ring size ERI_TPENDING is defined in eri_mac.h.
*/
switch (ERI_TPENDING) {
case 32: i = ETX_RINGSZ_32;
break;
case 64: i = ETX_RINGSZ_64;
break;
case 128: i = ETX_RINGSZ_128;
break;
case 256: i = ETX_RINGSZ_256;
break;
case 512: i = ETX_RINGSZ_512;
break;
case 1024: i = ETX_RINGSZ_1024;
break;
case 2048: i = ETX_RINGSZ_2048;
break;
case 4096: i = ETX_RINGSZ_4096;
break;
default:
return (1);
}
i <<= ERI_TX_RINGSZ_SHIFT;
return (0);
}
/*
* Initialize the RX DMA registers and Enable the RX DMA.
*/
static uint32_t
{
int i;
/*
* Initialize ERX Registers:
* rxring_lo, rxring_hi, config, rx_blanking, rx_pause_threshold.
* Also, rx_kick
* Read and save rxfifo_size.
* XXX: Use this to properly configure PAUSE threshold values.
*/
/*
* The Max ring size, ERI_RMDMAX is defined in eri_mac.h.
* More ERI_RPENDING will provide better performance but requires more
* system DVMA memory.
* eri_rx_ring_size cannot be NDD'able due to non-recoverable errors
* which cannot be detected from NDD operations
*/
/*
* get the rxring size bits
*/
switch (ERI_RPENDING) {
case 32: i = ERX_RINGSZ_32;
break;
case 64: i = ERX_RINGSZ_64;
break;
case 128: i = ERX_RINGSZ_128;
break;
case 256: i = ERX_RINGSZ_256;
break;
case 512: i = ERX_RINGSZ_512;
break;
case 1024: i = ERX_RINGSZ_1024;
break;
case 2048: i = ERX_RINGSZ_2048;
break;
case 4096: i = ERX_RINGSZ_4096;
break;
default:
return (1);
}
i <<= ERI_RX_RINGSZ_SHIFT;
i |= (ERI_FSTBYTE_OFFSET << ERI_RX_CONFIG_FBO_SHIFT) |
PUT_ERXREG(config, i);
return (0);
}
static int
{
int status = 0;
return (status);
}
static void
{
int i;
/*
* Hang out receive buffers.
*/
for (i = 0; i < ERI_RPENDING; i++) {
}
/*
* sync RXDMA descriptors.
*/
/*
* Reset RMD 'walking' pointers.
*/
erip->rx_completion = 0;
}
/*
* This routine is used to reset the RX DMA only. In the case of RX
* failures such as RX Tag Error, RX hang etc... we don't want to
* do global reset which takes down the link and clears the FIFO's
* By doing RX only reset, we leave the TX and the link intact.
*/
static uint32_t
{
(void) eri_erx_reset(erip);
if (eri_init_rxregs(erip))
return (1);
erip->rx_reset_issued = 0;
return (0);
}
static void
{
/*
* First of all make sure the Receive MAC is stop.
*/
/*
* 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 8 bits as a index into the 256 bit logical
* address filter. The high order four bits select the word,
* while the rest of the bits select the bit within the word.
*/
#ifdef ERI_DONT_STRIP_CRC
#else
#endif
/* wait after setting Hash Enable bit */
/* drv_usecwait(10); */
}
/*
* This routine is used to init the TX MAC only.
* &erip->xmitlock is held before calling this routine.
*/
void
{
uint32_t carrier_ext = 0;
/*
* Stop the Transmit MAC.
*/
(void) eri_txmac_disable(erip);
/*
* Must be Internal Transceiver
*/
if (param_mode)
BMAC_XIFC_MIIBUF_OE : 0) | BMAC_XIFC_TX_MII_OE));
else
BMAC_XIFC_MIIBUF_OE : 0) | BMAC_XIFC_TX_MII_OE |
/*
* Initialize the interpacket gap registers
*/
if (erip->ngu_enable)
BMAC_TXCFG_ENIPG0 : 0) |
(carrier_ext ? BMAC_TXCFG_CARR_EXT : 0) |
else
BMAC_TXCFG_ENIPG0 : 0) |
(carrier_ext ? BMAC_TXCFG_CARR_EXT : 0)));
}
static void
{
uint32_t i;
if (flag & ERI_DESC_MEM_MAP)
if (flag & ERI_DESC_MEM_ALLOC) {
}
if (flag & ERI_DESC_HANDLE_ALLOC)
(void) eri_freebufs(erip);
if (flag & ERI_RCV_HANDLE_ALLOC)
for (i = 0; i < erip->rcv_handle_cnt; i++)
if (flag & ERI_RCV_DVMA_ALLOC) {
}
if (flag & ERI_XBUFS_KMEM_DMABIND) {
erip->tbuf_ioaddr = 0;
}
if (flag & ERI_XBUFS_KMEM_ALLOC) {
}
if (flag & ERI_XBUFS_HANDLE_ALLOC) {
}
}
/*
* Initialize channel.
* Return true on success, false 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).
*/
/*
* lock order:
* intrlock->linklock->xmitlock->xcvrlock
*/
static boolean_t
{
uint32_t partial_init = 0;
uint32_t carrier_ext = 0;
/*
* Just return successfully if device is suspended.
* eri_init() will be called again from resume.
*/
goto init_exit;
}
}
(void) eri_new_xcvr(erip);
if (param_transceiver != NO_XCVR) {
/*
* Reset the new PHY and bring up the
* link
*/
if (eri_reset_xcvr(erip)) {
ERI_VERB_MSG, "In Init after reset");
link_timeout = 0;
goto done;
}
} else {
param_linkup = 0;
/*
* Still go on and complete the MAC initialization as
* xcvr might show up later.
* you must return to their mutex ordering.
*/
}
}
/*
* Allocate data structures.
*/
if (erip->global_reset_issued) {
/*
* Hang out/Initialize descriptors and buffers.
*/
} else {
if (init_stat)
goto done;
if (eri_freebufs(erip))
goto done;
/*
* Hang out/Initialize descriptors and buffers.
*/
if (eri_init_rxbufs(erip))
goto done;
}
}
/*
* BigMAC requires that we confirm that tx, rx and hash are in
* quiescent state.
* MAC will not reset successfully if the transceiver is not reset and
* brought out of Isolate mode correctly. TXMAC reset may fail if the
* ext. transceiver is just disconnected. If it fails, try again by
* checking the transceiver.
*/
if (eri_txmac_disable(erip)) {
param_linkup = 0; /* force init again */
goto done;
}
if (eri_rxmac_disable(erip)) {
param_linkup = 0; /* force init again */
goto done;
}
/*
* Initialize ERI Global registers :
* config
* For PCI : err_mask, bif_cfg
*
* Use user-configurable parameter for enabling 64-bit transfers.
* Note:For PCI, burst sizes are in multiples of 64-bytes.
*/
/*
* Significant performance improvements can be achieved by
* disabling transmit interrupt. Thus TMD's are reclaimed
* only very infrequently.
* The PCS Interrupt is masked here. It is enabled only when
* a PCS link is brought up because there is no second level
* mask for this interrupt..
* Init GLOBAL, TXMAC, RXMAC and MACCTL interrupt masks here.
*/
if (! partial_init) {
}
if (erip->global_reset_issued) {
/*
* Initialize ETX Registers:
* config, txring_lo, txring_hi
*/
if (eri_init_txregs(erip))
goto done;
/*
* Initialize ERX Registers:
* rxring_lo, rxring_hi, config, rx_blanking,
* rx_pause_threshold. Also, rx_kick
* Read and save rxfifo_size.
*/
if (eri_init_rxregs(erip))
goto done;
}
/*
* Set up the slottime,and rxconfig, txconfig without enabling
* the latter two at this time
*/
carrier_ext = 0;
#ifdef ERI_DONT_STRIP_CRC
(carrier_ext ? BMAC_RXCFG_CARR_EXT : 0)));
#else
(carrier_ext ? BMAC_RXCFG_CARR_EXT : 0)));
#endif
if (erip->ngu_enable)
((param_mode ? BMAC_TXCFG_FDX: 0) |
BMAC_TXCFG_ENIPG0 : 0) |
(carrier_ext ? BMAC_TXCFG_CARR_EXT : 0) |
else
((param_mode ? BMAC_TXCFG_FDX: 0) |
BMAC_TXCFG_ENIPG0 : 0) |
(carrier_ext ? BMAC_TXCFG_CARR_EXT : 0)));
/*
* Must be Internal Transceiver
*/
if (param_mode)
BMAC_XIFC_MIIBUF_OE : 0) | BMAC_XIFC_TX_MII_OE));
else {
BMAC_XIFC_MIIBUF_OE : 0) | BMAC_XIFC_TX_MII_OE |
}
/*
* if MAC int loopback flag is set, put xifc reg in mii loopback
* mode {DIAG}
*/
}
/*
* Enable TX and RX MACs.
*/
ERI_TXINIT | ERI_RXINIT);
erip->global_reset_issued = 0;
#endif
done:
if (init_stat)
if (linkupdate != LINK_STATE_UNKNOWN)
if (!ret) {
"eri_init failed");
}
return (ret);
}
/*
* 0 as burstsize upon failure as it signifies no burst size.
*/
static int
{
return (DDI_FAILURE);
if (erip->burstsizes)
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/*
* Un-initialize (STOP) ERI channel.
*/
static void
{
/*
* Allow up to 'ERI_DRAINTIME' for pending xmit's to complete.
*/
if (needind)
}
/*
* Allocate CONSISTENT memory for rmds and tmds with appropriate alignment and
* map it in IO space.
*
* The driver allocates STREAMS buffers which will be mapped in DVMA
* space using DDI DMA resources.
*
*/
static int
{
uintptr_t a;
int size;
int i;
int alloc_stat = 0;
/*
* Return if resources are already allocated.
*/
return (alloc_stat);
erip->alloc_flag = 0;
/*
* Allocate the TMD and RMD descriptors and extra for alignments.
*/
if (rval != DDI_SUCCESS) {
return (++alloc_stat);
}
if (rval != DDI_SUCCESS) {
return (++alloc_stat);
}
if (rval != DDI_DMA_MAPPED)
return (++alloc_stat);
if (cookiec != 1)
return (++alloc_stat);
a = ROUNDUP(a, ERI_GMDALIGN);
a += ERI_RPENDING * sizeof (struct rmd);
/*
* Specifically we reserve n (ERI_TPENDING + ERI_RPENDING)
* 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
*/
/*
* In the current implementation, we use the ddi compliant
* dma interface. We allocate ERI_RPENDING dma handles for receive
* activity. The actual dma mapping is done in the io function
* eri_read_dma(), by calling the ddi_dma_addr_bind_handle.
* Dma resources are deallocated by calling ddi_dma_unbind_handle
* in eri_reclaim() for transmit and eri_read_dma(), for receive io.
*/
if (eri_use_dvma_rx &&
} else {
for (i = 0; i < ERI_RPENDING; i++) {
if (rval != DDI_SUCCESS) {
alloc_stat++;
break;
}
}
erip->rcv_handle_cnt = i;
if (i)
if (alloc_stat)
return (alloc_stat);
}
/*
* Allocate TX buffer
* Note: buffers must always be allocated in the native
* ordering of the CPU (always big-endian for Sparc).
* ddi_dma_mem_alloc returns memory in the native ordering
* of the bus (big endian for SBus, little endian for PCI).
* So we cannot use ddi_dma_mem_alloc(, &erip->ge_dev_attr)
* because we'll get little endian memory on PCI.
*/
return (++alloc_stat);
}
return (++alloc_stat);
}
return (++alloc_stat);
}
if (cookiec != 1)
return (++alloc_stat);
/*
* Keep handy limit values for RMD, TMD, and Buffers.
*/
/*
* Zero out RCV holders.
*/
return (alloc_stat);
}
/* <<<<<<<<<<<<<<<<< INTERRUPT HANDLING FUNCTION >>>>>>>>>>>>>>>>>>>> */
/*
* First check to see if it is our device interrupting.
*/
static uint_t
{
/*
* Check if it is only the RX_DONE interrupt, which is
* the most frequent one.
*/
goto rx_done_int;
}
/* Claim the first interrupt after initialization */
}
/* Check for interesting events */
if ((erisbits & ERI_G_STATUS_INTR) == 0) {
#ifdef ESTAR_WORKAROUND
#endif
"eri_intr: Interrupt Not Claimed gsbits %X", erisbits);
#ifdef DEBUG
noteri++;
#endif
#ifdef ESTAR_WORKAROUND
#endif
#ifdef ESTAR_WORKAROUND
if (linkupdate != LINK_STATE_UNKNOWN)
#endif
return (serviced);
}
return (serviced);
}
if (erisbits & ERI_G_STATUS_FATAL_ERR) {
"eri_intr: fatal error: erisbits = %X", erisbits);
if (erip->rx_reset_issued) {
erip->rx_reset_issued = 0;
(void) eri_init_rx_channel(erip);
} else {
param_linkup = 0;
}
return (serviced);
}
if (erisbits & ERI_G_STATUS_NONFATAL_ERR) {
"eri_intr: non-fatal error: erisbits = %X", erisbits);
return (serviced);
}
}
if (erisbits & ERI_G_STATUS_MIF_INT) {
"eri_intr: new MIF interrupt status %X XCVR status %X",
#else
#endif
if (!erip->openloop_autoneg)
else
}
"eri_intr:May have Read Interrupt status:status %X", erisbits);
if ((erisbits & (ERI_G_STATUS_TX_INT_ME)) ||
if (macupdate)
}
if (erisbits & ERI_G_STATUS_RX_DONE) {
/*
* Sync RMD before looking at it.
*/
/*
* Loop through each RMD.
*/
/* process one packet */
}
/*
* ERI RCV DMA fetches or updates four descriptors
* a time. Also we don't want to update the desc.
* batch we just received packet on. So we update
* descriptors for every 4 packets and we update
* the group of 4 after the current batch.
*/
if (!(rmdi % 4)) {
if (eri_overflow_reset &&
loop_limit = 1;
} else {
}
}
/*
* Sync the next RMD before looking at it.
*/
loop_limit--;
}
}
if (head)
if (macupdate)
if (linkupdate != LINK_STATE_UNKNOWN)
return (serviced);
}
/*
* Handle interrupts for fatal errors
* Need reinitialization.
*/
/* called with intrlock held */
static void
{
uint32_t pci_error_int = 0;
if (erisbits & ERI_G_STATUS_RX_TAG_ERR) {
} else {
if (erisbits & ERI_G_STATUS_BUS_ERR_INT) {
pci_error_int = 1;
} else if (erisbits & ERI_G_STATUS_PERR_INT) {
} else {
}
}
/*
* PCI bus error
*/
if (pci_status & PCI_DATA_PARITY_REP)
if (pci_status & PCI_SING_TARGET_ABORT)
if (pci_status & PCI_RCV_TARGET_ABORT)
if (pci_status & PCI_RCV_MASTER_ABORT)
if (pci_status & PCI_SING_SYSTEM_ERR)
if (pci_status & PCI_DATA_PARITY_ERR)
/*
* clear it by writing the value that was read back.
*/
}
}
/*
* Handle interrupts regarding non-fatal events.
* TXMAC, RXMAC and MACCTL events
*/
static void
{
#ifdef ERI_PM_WORKAROUND
#endif
if (erisbits & ERI_G_STATUS_TX_MAC_INT) {
if (txmac_sts & BMAC_TXSTS_TX_URUN) {
}
if (txmac_sts & BMAC_TXSTS_MAXPKT_ERR) {
}
if (txmac_sts & BMAC_TXSTS_NCC_EXP) {
}
if (txmac_sts & BMAC_TXSTS_ECC_EXP) {
}
if (txmac_sts & BMAC_TXSTS_LCC_EXP) {
}
if (txmac_sts & BMAC_TXSTS_FCC_EXP) {
}
if (txmac_sts & BMAC_TXSTS_DEFER_EXP) {
}
if (txmac_sts & BMAC_TXSTS_PEAK_EXP) {
}
}
if (erisbits & ERI_G_STATUS_RX_NO_BUF) {
if (eri_overflow_reset)
}
if (erisbits & ERI_G_STATUS_RX_MAC_INT) {
if (rxmac_sts & BMAC_RXSTS_RX_OVF) {
#ifndef ERI_RMAC_HANG_WORKAROUND
erip->check_rmac_hang ++;
erip->check2_rmac_hang = 0;
"overflow intr %d: %8x wr:%2x rd:%2x",
#endif
if (eri_overflow_reset)
}
if (rxmac_sts & BMAC_RXSTS_ALE_EXP) {
}
if (rxmac_sts & BMAC_RXSTS_CRC_EXP) {
}
if (rxmac_sts & BMAC_RXSTS_LEN_EXP) {
}
if (rxmac_sts & BMAC_RXSTS_CVI_EXP) {
}
}
if (erisbits & ERI_G_STATUS_MAC_CTRL_INT) {
if (macctl_sts & ERI_MCTLSTS_PAUSE_RCVD) {
pause_time = ((macctl_sts &
ERI_MCTLSTS_PAUSE_TIME) >> 16);
"PAUSE Received. pause time = %X slot_times",
}
if (macctl_sts & ERI_MCTLSTS_PAUSE_STATE) {
}
if (macctl_sts & ERI_MCTLSTS_NONPAUSE) {
}
}
}
/*
* if this is the first init do not bother to save the
* counters.
*/
static void
{
/* XXX What all gets added in ierrors and oerrors? */
PUT_MACREG(fecnt, 0);
PUT_MACREG(aecnt, 0);
PUT_MACREG(lecnt, 0);
PUT_MACREG(rxcv, 0);
PUT_MACREG(ltcnt, 0);
PUT_MACREG(nccnt, 0);
PUT_MACREG(excnt, 0);
PUT_MACREG(fccnt, 0);
/*
* Do not add code violations to input errors.
* They are already counted in CRC errors
*/
}
mblk_t *
{
size += 128;
return (NULL);
}
return (mp);
}
mblk_t *
{
return (NULL);
}
return (mp);
}
/*
* Hardware Dependent Functions
* New Section.
*/
/* <<<<<<<<<<<<<<<< Fast Ethernet PHY Bit Bang Operations >>>>>>>>>>>>>>>>>> */
static void
{
PUT_MIFREG(mif_bbdata, x);
}
/*
* To read the MII register bits according to the IEEE Standard
*/
static uint32_t
{
uint32_t x;
if (param_transceiver == INTERNAL_XCVR)
else
return (x);
}
static void
{
int i;
(void) eri_bb_force_idle(erip);
for (i = 4; i >= 0; i--) { /* <AAAAA> */
}
for (i = 4; i >= 0; i--) { /* <RRRRR> */
}
for (i = 0xf; i >= 0; i--) { /* <DDDDDDDDDDDDDDDD> */
}
}
/* Return 0 if OK, 1 if error (Transceiver does not talk management) */
static uint32_t
{
int i;
uint32_t x;
uint32_t y;
*datap = 0;
(void) eri_bb_force_idle(erip);
for (i = 4; i >= 0; i--) { /* <AAAAA> */
}
for (i = 4; i >= 0; i--) { /* <RRRRR> */
}
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 */
/* XXX: Test if this is still needed */
GET_BIT_STD(x);
GET_BIT_STD(x);
GET_BIT_STD(x);
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 ERI_FRM_DEBUG
int frame_flag = 0;
#endif
/* Return 0 if OK, 1 if error (Transceiver does not talk management) */
static uint32_t
{
if (param_transceiver == NO_XCVR)
return (1); /* No xcvr present */
if (!erip->frame_enable)
#ifdef ERI_FRM_DEBUG
if (!frame_flag) {
frame_flag = 1;
}
#endif
(phyad << ERI_MIF_FRPHYAD_SHIFT) |
(regad << ERI_MIF_FRREGAD_SHIFT));
if ((frame & ERI_MIF_FRTA0) == 0) {
return (1);
} else {
return (0);
}
}
static void
{
if (!erip->frame_enable) {
return;
}
(phyad << ERI_MIF_FRPHYAD_SHIFT) |
(void) GET_MIFREG(mif_frame);
}
/* <<<<<<<<<<<<<<<<< PACKET TRANSMIT FUNCTIONS >>>>>>>>>>>>>>>>>>>> */
/*
* Send a single mblk. Returns B_TRUE if the packet is sent, or disposed of
* by freemsg. Returns B_FALSE if the packet was not sent or queued, and
* should be retried later (due to tx resource exhaustion.)
*/
static boolean_t
{
uint32_t i;
uint_t start_offset = 0;
uint_t stuff_offset = 0;
if (!param_linkup) {
return (B_TRUE);
}
#ifdef ERI_HWCSUM
if (flags & HCK_PARTIALCKSUM) {
} else {
}
}
#endif /* ERI_HWCSUM */
/*
* This sholdn't ever occur, as GLD should not send us
* packets that are too big.
*/
return (B_TRUE);
}
/*
* update MIB II statistics
*/
/* Check if there are enough descriptors for this packet */
else
if (i > (ERI_TPENDING - 4))
goto notmds;
~(ERI_G_MASK_TX_INT_ME));
}
}
offset = (i * ERI_BUFSIZE);
#ifdef ERI_HDX_BUG_WORKAROUND
if ((param_mode) || (eri_hdx_pad_enable == 0)) {
}
} else {
if (len_msg < 97) {
len_msg = 97;
}
}
#endif
/* first and last (and only!) descr of packet */
erip->tx_cur_cnt++;
}
return (B_TRUE);
return (B_FALSE);
}
static mblk_t *
{
break;
}
}
return (mp);
}
/*
* Transmit completion reclaiming.
*/
static boolean_t
{
/*
* Loop through each TMD starting from tcurp and upto tcomp.
*/
if (flags & (ERI_TMD_SOP))
reclaimed++;
}
}
/* <<<<<<<<<<<<<<<<<<< PACKET RECEIVE FUNCTIONS >>>>>>>>>>>>>>>>>>> */
static mblk_t *
{
int len;
#ifdef ERI_RCV_CKSUM
#endif /* ERI_RCV_CKSUM */
#ifdef ERI_DONT_STRIP_CRC
len -= 4;
#endif
/*
* In the event of RX FIFO overflow error, ERI REV 1.0 ASIC can
* corrupt packets following the descriptor corresponding the
* overflow. To detect the corrupted packets, we disable the
* dropping of the "bad" packets at the MAC. The descriptor
* then would have the "BAD" bit set. We drop the overflowing
* packet and the packet following it. We could have done some sort
* of checking to determine if the second packet was indeed bad
* (using CRC or checksum) but it would be expensive in this
* routine, since it is run in interrupt context.
*/
if ((flags & ERI_RMD_BAD) == 0)
}
return (NULL);
}
#ifdef ERI_DONT_STRIP_CRC
{
/*
* since we don't let the hardware strip the CRC in hdx
* then the driver needs to do it.
* this is to workaround a hardware bug
*/
/*
* Get the Checksum calculated by the hardware.
*/
/*
* Catch the case when the CRC starts on an odd
* boundary.
*/
}
hw_fcs &= 0xffff;
/*
* Now we can replace what the hardware wrote, make believe
* it got it right in the first place.
*/
}
#endif
/*
* Packet Processing
* Once we get a packet bp, we try allocate a new mblk, nbp
* to replace this one. If we succeed, we map it to the current
* dma handle and update the descriptor with the new cookie. We
* then put bp in our read service queue erip->ipq, if it exists
* or we just bp to the streams expecting it.
* If allocation of the new mblk fails, we implicitly drop the
* current packet, i.e do not pass up the mblk and re-use it.
* Re-mapping is not required.
*/
if (len < eri_rx_bcopy_max) {
/* Add the First Byte offset to the b_rptr */
#ifdef ERI_RCV_CKSUM
#else
#endif
} else {
/*
* mblk allocation has failed. Re-use the old mblk for
* the next packet. Re-mapping is not required since
* the same mblk and dma cookie is to be used again.
*/
}
} else {
/*
* How do we harden this, specially if unbind
* succeeds and then bind fails?
* If Unbind fails, we can leave without updating
* the descriptor but would it continue to work on
* next round?
*/
DDI_DMA_DONTWAIT, 0, &c, &ccnt);
/* Add the First Byte offset to the b_rptr */
#ifdef ERI_RCV_CKSUM
#else
#endif
} else {
/*
* mblk allocation has failed. Re-use the old mblk for
* the next packet. Re-mapping is not required since
* the same mblk and dma cookie is to be used again.
*/
}
}
return (retmp);
}
#define LINK_STAT_DISPLAY_TIME 20
static int
{
int i;
erip->xmit_dma_mode = 0;
erip->rcv_dma_mode = 0;
return (-1);
}
/*
* 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.
*/
if (i != 0) {
param_autoneg = 0; /* force speed */
param_anar_100T4 = 0;
param_anar_10fdx = 0;
param_anar_10hdx = 0;
param_anar_100fdx = 0;
param_anar_100hdx = 0;
param_anar_asm_dir = 0;
param_anar_pause = 0;
if (i == 10)
param_anar_10hdx = 1;
else if (i == 100)
param_anar_100hdx = 1;
}
/*
* Get the parameter values configured in .conf file.
*/
if (link_pulse_disabled)
return (0);
}
static void
{
switch (cmd) {
case ERI_ND_GET:
return;
}
break;
case ERI_ND_SET:
param_autoneg = 0xff;
param_anar_asm_dir = 0xff;
param_anar_pause = 0xff;
return;
}
if (param_autoneg != 0xff) {
"ndd_ioctl: new param_autoneg %d", param_autoneg);
param_linkup = 0;
} else {
if ((old_use_int_xcvr != param_use_intphy) ||
(old_default_link != param_default_link) ||
(old_select_link != param_select_link)) {
param_linkup = 0;
} else if ((old_ipg1 != param_ipg1) ||
(old_ipg2 != param_ipg2) ||
(old_ipg0 != param_ipg0) ||
(old_lance_mode != param_lance_mode)) {
param_linkup = 0;
}
}
break;
}
}
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.
*/
}
if (macupdate)
return (0);
}
static void
{
return;
}
/*
* MIB II kstat variables
*/
"pci_data_parity_err", KSTAT_DATA_ULONG);
"pci_signal_target_abort", KSTAT_DATA_ULONG);
"pci_rcvd_target_abort", KSTAT_DATA_ULONG);
"pci_rcvd_master_abort", KSTAT_DATA_ULONG);
"pci_signal_system_err", KSTAT_DATA_ULONG);
"pci_det_parity_err", KSTAT_DATA_ULONG);
}
/* <<<<<<<<<<<<<<<<<<<<<<< NDD SUPPORT FUNCTIONS >>>>>>>>>>>>>>>>>>> */
/*
*/
/* Free the Named Dispatch Table by calling eri_nd_free */
static void
{
}
/*
* Extracts the value from the eri parameter array and prints the
* parameter value. cp points to the required parameter.
*/
/* ARGSUSED */
static int
{
int param_len = 1;
int ok;
/*
* Calculate space required in mblk.
* Remember to include NULL terminator.
*/
do {
param_len++;
param_val /= 10;
} while (param_val);
if (ok == 0) {
}
return (ok);
}
/*
* Check if there is space for p_val at the end if mblk.
* If not, allocate new 1k mblk.
*/
static int
{
return (ENOMEM);
}
return (0);
}
/*
* Register each element of the parameter array with the
* named dispatch handler. Each element is loaded using
* eri_nd_load()
*/
static int
{
/* cnt gives the count of the number of */
/* elements present in the parameter array */
int i;
switch (eripa->param_name[0]) {
case '+': /* read-write */
break;
case '-': /* read-only */
break;
case '!': /* read-only, not displayed */
case '%': /* read-write, not displayed */
continue;
}
return (B_FALSE);
}
}
return (B_TRUE);
}
/*
* Sets the eri parameter to the value in the param_register using
* eri_nd_load().
*/
/* ARGSUSED */
static int
{
char *end;
long new_value;
return (EINVAL);
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:
/*
* (XXX) hack: "*valp" is size of user buffer for copyout. If result
* of action routine is too big, free excess and return ioc_rval as buf
* size needed. Return as many mblocks as will fit, free the rest. For
* backward compatibility, assume size of orig ioctl buffer if "*valp"
* bad or not given.
*/
if (valp)
{
while (mp2) {
}
}
if (!err) {
/* Tack on the null */
if (!err) {
if (excess > 0) {
&nmp, 1);
if (!err)
else
size_out = 0;
}
} else
size_out = 0;
}
break;
case ND_SET:
if (valp) {
}
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);
== NULL)
return (B_FALSE);
}
goto fill_it;
}
}
return (B_FALSE);
} else {
nd->nd_free_count--;
}
}
;
nd->nd_free_count--;
return (B_TRUE);
}
/*
* Hardening Functions
* New Section
*/
#ifdef DEBUG
/*PRINTFLIKE5*/
static void
{
char msg_buffer[255];
if (eri_msg_out & ERI_CON_MSG) {
if (erip)
else
line, msg_buffer);
}
}
}
#endif
/*PRINTFLIKE4*/
static void
const char *fmt, ...)
{
char msg_buffer[255];
return;
}
if (severity == SEVERITY_HIGH) {
} else switch (type) {
case ERI_VERB_MSG:
break;
case ERI_LOG_MSG:
break;
case ERI_BUF_MSG:
break;
case ERI_CON_MSG:
default:
break;
}
}
/*
* Transceiver (xcvr) Functions
* New Section
*/
/*
* eri_stop_timer function is used by a function before doing link-related
* processing. It locks the "linklock" to protect the link-related data
* structures. This lock will be subsequently released in eri_start_timer().
*/
static void
{
}
}
/*
* If msec parameter is zero, just release "linklock".
*/
static void
{
if (msec) {
}
}
}
static int
{
int status;
int old_transceiver;
/*
* An External Transceiver was found and it takes priority
* over an internal, given the use_int_xcvr flag
* is false.
*/
if (old_transceiver != EXTERNAL_XCVR) {
/*
* External transceiver has just been plugged
* in. Isolate the internal Transceiver.
*/
if (old_transceiver == INTERNAL_XCVR) {
}
}
/*
* Select the external Transceiver.
*/
} else if (cfg & ERI_MIF_CFGM0) {
/*
* An Internal Transceiver was found or the
* use_int_xcvr flag is true.
*/
if (old_transceiver != INTERNAL_XCVR) {
/*
* The external transceiver has just been
* disconnected or we're moving from a no
* transceiver state.
*/
if ((old_transceiver == EXTERNAL_XCVR) &&
(cfg & ERI_MIF_CFGM0)) {
}
}
/*
* Select the internal transceiver.
*/
} else {
/*
* Did not find a valid xcvr.
*/
"Eri_new_xcvr : Select None");
}
(void *)4000) == DDI_SUCCESS)
}
return (status);
}
/*
* This function is used for timers. No locks are held on timer expiry.
*/
static void
{
if (linkupdate != LINK_STATE_UNKNOWN)
}
/*
* Compare our xcvr in our structure to the xcvr that we get from
* eri_check_mii_xcvr(). If they are different then mark the
* link down, reset xcvr, and return.
*
* Note without the MII connector, conditions can not change that
* will then use a external phy, thus this code has been cleaned
* to not even call the function or to possibly change the xcvr.
*/
static uint32_t
{
uint32_t linkupdate = 0;
if (erip->openloop_autoneg) {
"eri_check_link:openloop stat %X mii_status %X",
if (!(stat & PHY_BMSR_LNKSTS) &&
if (param_speed) {
control &= ~PHY_BMCR_100M;
param_anlpar_100hdx = 0;
param_anlpar_10hdx = 1;
param_speed = 0;
} else {
control |= PHY_BMCR_100M;
param_anlpar_100hdx = 1;
param_anlpar_10hdx = 0;
param_speed = 1;
}
"eri_check_link: trying speed %X stat %X",
param_speed, stat);
erip->openloop_autoneg ++;
} else {
erip->openloop_autoneg = 0;
if (erip->openloop_autoneg)
}
return (linkupdate);
}
#ifdef ERI_RMAC_HANG_WORKAROUND
/*
* Check if rx hung.
*/
if (erip->check_rmac_hang) {
"check1 %d: macsm:%8x wr:%2x rd:%2x",
erip->check_rmac_hang = 0;
erip->check2_rmac_hang ++;
return (linkupdate);
}
if (erip->check2_rmac_hang) {
"check2 %d: macsm:%8x wr:%2x rd:%2x",
erip->check2_rmac_hang = 0;
"RX hang: Reset mac");
return (linkupdate);
}
}
}
#endif
/*
* Check if tx hung.
*/
#ifdef ERI_TX_HUNG
(eri_check_txhung(erip))) {
return (linkupdate);
}
#endif
#ifdef ERI_PM_WORKAROUND
(void *)4000) == DDI_SUCCESS)
}
#endif
else
return (linkupdate);
}
static link_state_t
{
int restart_autoneg = 0;
/*
* Now check if someone has pulled the xcvr or
* a new xcvr has shown up
* If so try to find out what the new xcvr setup is.
*/
(param_transceiver == NO_XCVR)) {
"No status transceiver gone");
if (eri_new_xcvr(erip)) {
if (param_transceiver != NO_XCVR) {
/*
* Reset the new PHY and bring up the link
*/
(void) eri_reset_xcvr(erip);
}
}
return (LINK_STATE_UNKNOWN);
}
mif_ints |= PHY_BMSR_ANC;
"eri_mif_check: Set ANC bit mif_data %X mig_ints %X",
}
/*
* Switch off Auto-negotiation interrupts and switch on
* Link ststus interrupts.
*/
"parallel detection fault");
/*
* Consider doing open loop auto-negotiation.
*/
"Going into Open loop Auto-neg");
if (param_anar_100fdx || param_anar_100hdx) {
control |= PHY_BMCR_100M;
param_anlpar_100hdx = 1;
param_anlpar_10hdx = 0;
param_speed = 1;
} else if (param_anar_10fdx || param_anar_10hdx) {
control &= ~PHY_BMCR_100M;
param_anlpar_100hdx = 0;
param_anlpar_10hdx = 1;
param_speed = 0;
} else {
"Transceiver speed set incorrectly.");
return (0);
}
param_anlpar_100fdx = 0;
param_anlpar_10fdx = 0;
param_mode = 0;
return (0);
}
param_speed = 1;
param_speed = 0;
} else an_common = 0x0;
if (!an_common) {
"Transceiver: anar not set with speed selection");
}
"Link duplex = 0x%X", param_mode);
"Link speed = 0x%X", param_speed);
/* mif_ints |= PHY_BMSR_LNKSTS; prevent double msg */
/* mif_data |= PHY_BMSR_LNKSTS; prevent double msg */
}
if (mif_ints & PHY_BMSR_LNKSTS) {
if (mif_data & PHY_BMSR_LNKSTS) {
/*
* Program Lu3X31T for mininum transition
*/
if (eri_phy_mintrans) {
}
/*
* The link is up.
*/
param_linkup = 1;
if (param_mode)
else
} else {
param_linkup = 0;
if (param_autoneg) {
restart_autoneg = 1;
}
}
} else {
if (mif_data & PHY_BMSR_LNKSTS) {
if (!param_linkup) {
"eri_mif_check: MIF data link up");
/*
* Program Lu3X31T for minimum transition
*/
if (eri_phy_mintrans) {
(void) eri_mii_read(erip, 0,
&old_mintrans);
}
/*
* The link is up.
*/
param_linkup = 1;
if (param_mode)
else
}
} else if (param_linkup) {
/*
* The link is down now.
*/
"eri_mif_check:Link was up and went down");
param_linkup = 0;
if (param_autoneg)
restart_autoneg = 1;
}
}
if (restart_autoneg) {
/*
* Restart normal auto-negotiation.
*/
"eri_mif_check:Restart AUto Negotiation");
erip->openloop_autoneg = 0;
param_mode = 0;
param_speed = 0;
param_anlpar_100T4 = 0;
param_anlpar_100fdx = 0;
param_anlpar_100hdx = 0;
param_anlpar_10fdx = 0;
param_anlpar_10hdx = 0;
param_aner_lpancap = 0;
}
if (mif_ints & PHY_BMSR_JABDET) {
if (mif_data & PHY_BMSR_JABDET) {
/*
* Reset the new PHY and bring up the link
* (Check for failure?)
*/
(void) eri_reset_xcvr(erip);
}
}
return (retv);
}
#define PHYRST_PERIOD 500
static int
{
int n;
#endif
/*
* Reset Open loop auto-negotiation this means you can try
* Normal auto-negotiation, until you get a Multiple Link fault
* at which point you try 100M half duplex then 10M half duplex
* until you get a Link up.
*/
erip->openloop_autoneg = 0;
/*
* Reset the xcvr.
*/
/* Check for transceiver reset completion */
n = 1000;
while (--n > 0) {
/* Transceiver does not talk MII */
"eri_reset_xcvr: no mii");
}
if ((control & PHY_BMCR_RESET) == 0)
goto reset_done;
}
"eri_reset_xcvr:reset_failed n == 0, control %x", control);
goto eri_reset_xcvr_failed;
"eri_reset_xcvr: reset complete in %d us",
(1000 - n) * PHYRST_PERIOD);
/*
* Initialize the read only transceiver ndd information
* the values are either 0 or 1.
*/
/*
* Match up the ndd capabilities with the transceiver.
*/
/*
* Select the operation mode of the transceiver.
*/
if (param_autoneg) {
/*
* Initialize our auto-negotiation capabilities.
*/
anar = PHY_SELECTOR;
if (param_anar_100T4)
anar |= PHY_ANAR_T4;
if (param_anar_100fdx)
anar |= PHY_ANAR_TXFDX;
if (param_anar_100hdx)
anar |= PHY_ANAR_TX;
if (param_anar_10fdx)
anar |= PHY_ANAR_10FDX;
if (param_anar_10hdx)
anar |= PHY_ANAR_10;
}
/* Place the Transceiver in normal operation mode */
(control & ~PHY_BMCR_ISOLATE));
}
/*
* If Lu3X31T then allow nonzero eri_phy_mintrans
*/
if (eri_phy_mintrans &&
eri_phy_mintrans = 0;
}
/*
* Initialize the mif interrupt mask.
*/
/*
* Establish link speeds and do necessary special stuff based
* in the speed.
*/
if (!(param_anar_10fdx) &&
(param_anar_10hdx) &&
(erip->link_pulse_disabled)) {
param_speed = 0;
param_mode = 0;
nicr &= ~PHY_NICR_LD;
param_linkup = 1;
if (param_mode)
else
}
}
/*
* Clear the autonegotitation before re-starting
*/
/* eri_mii_write(erip, ERI_PHY_BMCR, control); */
if (param_autoneg) {
/*
* Setup the transceiver for autonegotiation.
*/
/*
* Clear the Auto-negotiation before re-starting
*/
/*
* Switch on auto-negotiation.
*/
} else {
/*
* Force the transceiver.
*/
/*
* Switch off auto-negotiation.
*/
if (speed_100) {
control |= PHY_BMCR_100M;
param_aner_lpancap = 0; /* Clear LP nway */
param_anlpar_10fdx = 0;
param_anlpar_10hdx = 0;
param_speed = 1;
if (param_mode) {
param_anlpar_100hdx = 0;
} else {
}
} else if (speed_10) {
control &= ~PHY_BMCR_100M;
param_aner_lpancap = 0; /* Clear LP nway */
param_anlpar_100fdx = 0;
param_anlpar_100hdx = 0;
param_anlpar_100T4 = 0;
param_speed = 0;
if (param_mode) {
param_anlpar_10hdx = 0;
} else {
}
} else {
"Transceiver speed set incorrectly.");
}
if (param_mode) {
control |= PHY_BMCR_FDX;
}
"control = %x status = %x param_mode %d",
/*
* if (param_mode) {
* control |= PHY_BMCR_FDX;
* }
* control &= ~(PHY_BMCR_FDX | PHY_BMCR_ANE | PHY_BMCR_RAN);
* eri_mii_write(erip, ERI_PHY_BMCR, control);
*/
}
#ifdef DEBUG
#endif
return (0);
return (1);
}
static void
{
(param_anar_10fdx | param_anar_10hdx)) {
return;
}
/*
* May have to set link partner's speed and mode.
*/
"May have to set link partner's speed and duplex mode.");
}
}
#endif
static void
{
if (enable == MIF_POLL_START) {
}
} else if (enable == MIF_POLL_STOP) {
}
}
/* Decide if transmitter went dead and reinitialize everything */
#ifdef ERI_TX_HUNG
static int eri_txhung_limit = 2;
static int
{
/* Something needs to be sent out but it is not going out */
else
if (macupdate)
}
#endif