igb_main.c revision 6d1cdc09693601c358cdcd1bbd6a462920ac89ad
/*
* 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(c) 2007-2010 Intel Corporation. All rights reserved.
*/
/*
*/
#include "igb_sw.h"
static char ident[] = "Intel 1Gb Ethernet";
static char igb_version[] = "igb 1.1.14";
/*
* Local function protoypes
*/
static int igb_register_mac(igb_t *);
static int igb_identify_hardware(igb_t *);
static int igb_regs_map(igb_t *);
static void igb_init_properties(igb_t *);
static int igb_init_driver_settings(igb_t *);
static void igb_init_locks(igb_t *);
static void igb_destroy_locks(igb_t *);
static int igb_init_mac_address(igb_t *);
static int igb_init_adapter(igb_t *);
static void igb_stop_adapter(igb_t *);
static void igb_tx_clean(igb_t *);
static int igb_alloc_rings(igb_t *);
static int igb_alloc_rx_data(igb_t *);
static void igb_free_rx_data(igb_t *);
static void igb_free_rings(igb_t *);
static void igb_setup_rings(igb_t *);
static void igb_setup_rx(igb_t *);
static void igb_setup_tx(igb_t *);
static void igb_setup_rx_ring(igb_rx_ring_t *);
static void igb_setup_tx_ring(igb_tx_ring_t *);
static void igb_setup_rss(igb_t *);
static void igb_setup_mac_rss_classify(igb_t *);
static void igb_setup_mac_classify(igb_t *);
static void igb_init_unicst(igb_t *);
static void igb_setup_multicst(igb_t *);
static void igb_get_phy_state(igb_t *);
static void igb_param_sync(igb_t *);
static void igb_get_conf(igb_t *);
static int igb_get_prop(igb_t *, char *, int, int, int);
static void igb_local_timer(void *);
static void igb_link_timer(void *);
static void igb_arm_watchdog_timer(igb_t *);
static void igb_start_watchdog_timer(igb_t *);
static void igb_restart_watchdog_timer(igb_t *);
static void igb_stop_watchdog_timer(igb_t *);
static void igb_start_link_timer(igb_t *);
static void igb_stop_link_timer(igb_t *);
static void igb_disable_adapter_interrupts(igb_t *);
static void igb_enable_adapter_interrupts_82575(igb_t *);
static void igb_enable_adapter_interrupts_82576(igb_t *);
static void igb_enable_adapter_interrupts_82580(igb_t *);
static void igb_set_external_loopback(igb_t *);
static void igb_set_internal_phy_loopback(igb_t *);
static void igb_set_internal_serdes_loopback(igb_t *);
static int igb_alloc_intrs(igb_t *);
static int igb_alloc_intr_handles(igb_t *, int);
static int igb_add_intr_handlers(igb_t *);
static void igb_rem_intr_handlers(igb_t *);
static void igb_rem_intrs(igb_t *);
static int igb_enable_intrs(igb_t *);
static int igb_disable_intrs(igb_t *);
static void igb_setup_msix_82575(igb_t *);
static void igb_setup_msix_82576(igb_t *);
static void igb_setup_msix_82580(igb_t *);
static uint_t igb_intr_legacy(void *, void *);
static uint_t igb_intr_msi(void *, void *);
static uint_t igb_intr_rx(void *, void *);
static uint_t igb_intr_tx(void *, void *);
static uint_t igb_intr_tx_other(void *, void *);
static void igb_intr_rx_work(igb_rx_ring_t *);
static void igb_intr_tx_work(igb_tx_ring_t *);
static void igb_intr_link_work(igb_t *);
static void igb_get_driver_control(struct e1000_hw *);
static void igb_release_driver_control(struct e1000_hw *);
static int igb_resume(dev_info_t *);
static int igb_suspend(dev_info_t *);
static int igb_quiesce(dev_info_t *);
const void *);
static void igb_fm_init(igb_t *);
static void igb_fm_fini(igb_t *);
static void igb_release_multicast(igb_t *);
char *igb_priv_props[] = {
"_tx_copy_thresh",
"_tx_recycle_thresh",
"_tx_overload_thresh",
"_tx_resched_thresh",
"_rx_copy_thresh",
"_rx_limit_per_intr",
"_intr_throttling",
"_adv_pause_cap",
"_adv_asym_pause_cap",
};
static struct cb_ops igb_cb_ops = {
nulldev, /* cb_open */
nulldev, /* 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 */
NULL, /* cb_stream */
CB_REV, /* cb_rev */
nodev, /* cb_aread */
nodev /* cb_awrite */
};
static struct dev_ops igb_dev_ops = {
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
NULL, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
igb_attach, /* devo_attach */
igb_detach, /* devo_detach */
nodev, /* devo_reset */
&igb_cb_ops, /* devo_cb_ops */
NULL, /* devo_bus_ops */
ddi_power, /* devo_power */
igb_quiesce, /* devo_quiesce */
};
static struct modldrv igb_modldrv = {
&mod_driverops, /* Type of module. This one is a driver */
ident, /* Discription string */
&igb_dev_ops, /* driver ops */
};
static struct modlinkage igb_modlinkage = {
};
/* Access attributes for register mapping */
};
#define IGB_M_CALLBACK_FLAGS \
static mac_callbacks_t igb_m_callbacks = {
NULL,
NULL,
NULL,
NULL,
NULL,
};
/*
* Initialize capabilities of each supported adapter type
*/
static adapter_info_t igb_82575_cap = {
/* limits */
4, /* maximum number of rx queues */
1, /* minimum number of rx queues */
4, /* default number of rx queues */
4, /* maximum number of tx queues */
1, /* minimum number of tx queues */
4, /* default number of tx queues */
65535, /* maximum interrupt throttle rate */
0, /* minimum interrupt throttle rate */
200, /* default interrupt throttle rate */
/* function pointers */
/* capabilities */
(IGB_FLAG_HAS_DCA | /* capability flags */
0xffc00000 /* mask for RXDCTL register */
};
static adapter_info_t igb_82576_cap = {
/* limits */
16, /* maximum number of rx queues */
1, /* minimum number of rx queues */
4, /* default number of rx queues */
16, /* maximum number of tx queues */
1, /* minimum number of tx queues */
4, /* default number of tx queues */
65535, /* maximum interrupt throttle rate */
0, /* minimum interrupt throttle rate */
200, /* default interrupt throttle rate */
/* function pointers */
/* capabilities */
(IGB_FLAG_HAS_DCA | /* capability flags */
0xffe00000 /* mask for RXDCTL register */
};
static adapter_info_t igb_82580_cap = {
/* limits */
8, /* maximum number of rx queues */
1, /* minimum number of rx queues */
4, /* default number of rx queues */
8, /* maximum number of tx queues */
1, /* minimum number of tx queues */
4, /* default number of tx queues */
65535, /* maximum interrupt throttle rate */
0, /* minimum interrupt throttle rate */
200, /* default interrupt throttle rate */
/* function pointers */
/* capabilities */
(IGB_FLAG_HAS_DCA | /* capability flags */
0xffe00000 /* mask for RXDCTL register */
};
/*
* Module Initialization Functions
*/
int
_init(void)
{
int status;
if (status != DDI_SUCCESS) {
}
return (status);
}
int
_fini(void)
{
int status;
if (status == DDI_SUCCESS) {
}
return (status);
}
int
{
int status;
return (status);
}
/*
* igb_attach - driver attach
*
* This function is the device specific initialization entry
* point. This entry point is required and must be written.
* The DDI_ATTACH command must be provided in the attach entry
* point. When attach() is called with cmd set to DDI_ATTACH,
* all normal kernel services (such as kmem_alloc(9F)) are
* available for use by the driver.
*
* The attach() function will be called once for each instance
* of the device on the system with cmd set to DDI_ATTACH.
* Until attach() succeeds, the only driver entry points which
* may be called are open(9E) and getinfo(9E).
*/
static int
{
int instance;
/*
* Check the command and perform corresponding operations
*/
switch (cmd) {
default:
return (DDI_FAILURE);
case DDI_RESUME:
return (igb_resume(devinfo));
case DDI_ATTACH:
break;
}
/* Get the device instance */
/* Allocate memory for the instance data structure */
/* Attach the instance pointer to the dev_info data structure */
/* Initialize for fma support */
0, 0x0f,
/*
* Map PCI config space registers
*/
goto attach_fail;
}
/*
* Identify the chipset family
*/
goto attach_fail;
}
/*
* Map device registers
*/
goto attach_fail;
}
/*
* Initialize driver parameters
*/
/*
* Allocate interrupts
*/
goto attach_fail;
}
/*
* allocated interrupt vectors, so we should allocate the rings after
* interrupts are allocated.
*/
goto attach_fail;
}
/*
* Add interrupt handlers
*/
goto attach_fail;
}
/*
* Initialize driver parameters
*/
goto attach_fail;
}
goto attach_fail;
}
/*
* Initialize mutexes for this device.
* Do this before enabling the interrupt handler and
* register the softint to avoid the condition where
* interrupt handler can try using uninitialized mutex
*/
/*
* Initialize the adapter
*/
goto attach_fail;
}
/*
* Initialize statistics
*/
goto attach_fail;
}
/*
* Register the driver to the MAC
*/
goto attach_fail;
}
/*
* Now that mutex locks are initialized, and the chip is also
* initialized, enable interrupts.
*/
goto attach_fail;
}
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/*
* igb_detach - driver detach
*
* The detach() function is the complement of the attach routine.
* If cmd is set to DDI_DETACH, detach() is used to remove the
* state associated with a given instance of a device node
* prior to the removal of that instance from the system.
*
* The detach() function will be called once for each instance
* of the device for which there has been a successful attach()
* once there are no longer any opens on the device.
*
* Interrupts routine are disabled, All memory allocated by this
* driver are freed.
*/
static int
{
/*
* Check detach command
*/
switch (cmd) {
default:
return (DDI_FAILURE);
case DDI_SUSPEND:
return (igb_suspend(devinfo));
case DDI_DETACH:
break;
}
/*
* Get the pointer to the driver private data structure
*/
return (DDI_FAILURE);
/*
* Unregister MAC. If failed, we have to fail the detach
*/
return (DDI_FAILURE);
}
/*
* If the device is still running, it needs to be stopped first.
* This check is necessary because under some specific circumstances,
* the detach routine can be called without stopping the interface
* first.
*/
/* Disable and stop the watchdog timer */
} else
/*
* Check if there are still rx buffers held by the upper layer.
* If so, fail the detach.
*/
if (!igb_rx_drain(igb))
return (DDI_FAILURE);
/*
* Do the remaining unconfigure routines
*/
return (DDI_SUCCESS);
}
/*
* quiesce(9E) entry point.
*
* This function is called when the system is single-threaded at high
* PIL with preemption disabled. Therefore, this function must not be
* blocked.
*
* This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
* DDI_FAILURE indicates an error condition and should almost never happen.
*/
static int
{
return (DDI_FAILURE);
/*
* Disable the adapter interrupts
*/
/* Tell firmware driver is no longer in control */
/*
* Reset the chipset
*/
(void) e1000_reset_hw(hw);
/*
* Reset PHY if possible
*/
(void) e1000_phy_hw_reset(hw);
return (DDI_SUCCESS);
}
/*
* igb_unconfigure - release all resources held by this instance
*/
static void
{
/*
* Disable interrupt
*/
(void) igb_disable_intrs(igb);
}
/*
* Unregister MAC
*/
}
/*
* Free statistics
*/
}
/*
* Remove interrupt handlers
*/
}
/*
* Remove interrupts
*/
}
/*
* Remove driver properties
*/
(void) ddi_prop_remove_all(devinfo);
}
/*
* Stop the adapter
*/
}
/*
* Free multicast table
*/
/*
* Free register handle
*/
}
/*
* Free PCI config handle
*/
}
/*
* Free locks
*/
}
/*
*/
}
/*
* Remove FMA
*/
}
/*
* Free the driver data structure
*/
}
/*
* igb_register_mac - Register the driver and its function pointers with
* the GLD interface
*/
static int
{
int status;
return (IGB_FAILURE);
sizeof (struct ether_vlan_header) - ETHERFCSL;
}
/*
* igb_identify_hardware - Identify the type of the chipset
*/
static int
{
/*
* Get the device id
*/
hw->revision_id =
/*
* Set the mac type of the adapter based on the device id
*/
return (IGB_FAILURE);
}
/*
* Install adapter capabilities based on mac type
*/
case e1000_82575:
break;
case e1000_82576:
break;
case e1000_82580:
break;
default:
return (IGB_FAILURE);
}
return (IGB_SUCCESS);
}
/*
* igb_regs_map - Map the device registers
*/
static int
{
/*
* First get the size of device registers to be mapped.
*/
DDI_SUCCESS) {
return (IGB_FAILURE);
}
/*
* Call ddi_regs_map_setup() to map registers
*/
return (IGB_FAILURE);
}
return (IGB_SUCCESS);
}
/*
* igb_init_properties - Initialize driver properties
*/
static void
{
/*
* Get conf file properties, including link settings
* jumbo frames, ring number, descriptor number, etc.
*/
}
/*
* igb_init_driver_settings - Initialize driver settings
*
* The settings include hardware function pointers, bus information,
* need to be setup during driver initialization.
*/
static int
{
int i;
/*
* Initialize chipset specific hardware function pointers
*/
return (IGB_FAILURE);
}
/*
* Get bus information
*/
return (IGB_FAILURE);
}
/*
* Get the system page size
*/
/*
* Set rx buffer size
* The IP header alignment room is counted in the calculation.
* The rx buffer size is in unit of 1K that is required by the
* chipset hardware.
*/
/*
* Set tx buffer size
*/
/*
*/
for (i = 0; i < igb->num_rx_rings; i++) {
}
for (i = 0; i < igb->num_tx_rings; i++) {
if (igb->tx_head_wb_enable)
else
}
/*
* Initialize values of interrupt throttling rates
*/
for (i = 1; i < MAX_NUM_EITR; i++)
/*
* The initial link state should be "unknown"
*/
return (IGB_SUCCESS);
}
/*
* igb_init_locks - Initialize locks
*/
static void
{
int i;
for (i = 0; i < igb->num_rx_rings; i++) {
}
for (i = 0; i < igb->num_tx_rings; i++) {
}
}
/*
* igb_destroy_locks - Destroy locks
*/
static void
{
int i;
for (i = 0; i < igb->num_rx_rings; i++) {
}
for (i = 0; i < igb->num_tx_rings; i++) {
}
}
static int
{
return (DDI_FAILURE);
return (DDI_FAILURE);
}
/*
* Enable and start the watchdog timer
*/
}
return (DDI_SUCCESS);
}
static int
{
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* Disable and stop the watchdog timer
*/
return (DDI_SUCCESS);
}
static int
{
/*
* Initilize the adapter
*/
return (IGB_FAILURE);
}
return (IGB_SUCCESS);
}
/*
* igb_init_mac_address - Initialize the default MAC address
*
* On success, the MAC address is entered in the igb->hw.mac.addr
* and hw->mac.perm_addr fields and the adapter's RAR(0) receive
* address register.
*
* Important side effects:
* 1. adapter is reset - this is required to put it in a known state.
* 2. all of non-volatile memory (NVM) is read & checksummed - NVM is where
* MAC address and all default settings are stored, so a valid checksum
* is required.
*/
static int
{
/*
* Reset chipset to put the hardware in a known state
* before we try to get MAC address from NVM.
*/
goto init_mac_fail;
}
/*
* NVM validation
*/
if (e1000_validate_nvm_checksum(hw) < 0) {
/*
* Some PCI-E parts fail the first check due to
* the link being in sleep state. Call it again,
* if it fails a second time its a real issue.
*/
if (e1000_validate_nvm_checksum(hw) < 0) {
"Invalid NVM checksum. Please contact "
"the vendor to update the NVM.");
goto init_mac_fail;
}
}
/*
* Get the mac address
* This function should handle SPARC case correctly.
*/
if (!igb_find_mac_address(igb)) {
goto init_mac_fail;
}
/* Validate mac address */
goto init_mac_fail;
}
return (IGB_SUCCESS);
return (IGB_FAILURE);
}
/*
* igb_init_adapter - Initialize the adapter
*/
static int
{
int i;
/*
* In order to obtain the default MAC address, this will reset the
* adapter and validate the NVM that the address and many other
* default settings come from.
*/
goto init_adapter_fail;
}
/*
* Setup flow control
*
* These parameters set thresholds for the adapter's generation(Tx)
* and response(Rx) to Ethernet PAUSE frames. These are just threshold
* settings. Flow control is enabled or disabled in the configuration
* file.
* High-water mark is set down from the top of the rx fifo (not
* sensitive to max_frame_size) and low-water is set just below
* high-water mark.
* The high water mark must be low enough to fit one full frame above
* it in the rx FIFO. Should be the lower of:
* 90% of the Rx FIFO size, or the full Rx FIFO size minus one full
* frame.
*/
/*
* The default setting of PBA is correct for 82575 and other supported
* adapters do not have the E1000_PBA register, so PBA value is only
* used for calculation here and is never written to the adapter.
*/
pba = E1000_PBA_34K;
} else {
pba = E1000_PBA_64K;
}
/* 8-byte granularity */
} else {
/* 16-byte granularity */
}
(void) e1000_validate_mdi_setting(hw);
/*
* Reset the chipset hardware the second time to put PBA settings
* into effect.
*/
goto init_adapter_fail;
}
/*
* Don't wait for auto-negotiation to complete
*/
/*
* Copper options
*/
}
/*
* Initialize link settings
*/
/*
* Configure/Initialize hardware
*/
goto init_adapter_fail;
}
/*
* Start the link setup timer
*/
/*
* Disable wakeup control by default
*/
/*
* Record phy info in hw struct
*/
(void) e1000_get_phy_info(hw);
/*
* Make sure driver has control
*/
/*
* Restore LED settings to the default from EEPROM
* to meet the standard for Sun platforms.
*/
(void) e1000_cleanup_led(hw);
/*
* Setup MSI-X interrupts
*/
/*
* Initialize unicast addresses.
*/
/*
* Setup and initialize the mctable structures.
*/
/*
* Set interrupt throttling rate
*/
/*
* Save the state of the phy
*/
return (IGB_SUCCESS);
/*
* Reset PHY if possible
*/
(void) e1000_phy_hw_reset(hw);
return (IGB_FAILURE);
}
/*
* igb_stop_adapter - Stop the adapter
*/
static void
{
/* Stop the link setup timer */
/* Tell firmware driver is no longer in control */
/*
* Reset the chipset
*/
}
/*
* e1000_phy_hw_reset is not needed here, MAC reset above is sufficient
*/
}
/*
* igb_reset - Reset the chipset and restart the driver.
*
* It involves stopping and re-starting the chipset,
*/
static int
{
int i;
/*
* before draining pending data and resetting hardware.
*/
/*
* Drain the pending transmit packets
*/
(void) igb_tx_drain(igb);
for (i = 0; i < igb->num_rx_rings; i++)
for (i = 0; i < igb->num_tx_rings; i++)
/*
* Stop the adapter
*/
/*
*/
/*
* Start the adapter
*/
goto reset_failure;
}
/*
*/
/*
* Enable adapter interrupts
* The interrupts must be enabled after the driver state is START
*/
goto reset_failure;
goto reset_failure;
return (IGB_SUCCESS);
return (IGB_FAILURE);
}
/*
* igb_tx_clean - Clean the pending transmit packets and DMA resources
*/
static void
{
int i, j;
for (i = 0; i < igb->num_tx_rings; i++) {
/*
* Clean the pending tx data - the pending packets in the
* work_list that have no chances to be transmitted again.
*
* We must ensure the chipset is stopped or the link is down
* before cleaning the transmit packets.
*/
desc_num = 0;
}
}
if (desc_num > 0) {
/*
* Reset the head and tail pointers of the tbd ring;
* Reset the head write-back if it is enabled.
*/
if (igb->tx_head_wb_enable)
*tx_ring->tbd_head_wb = 0;
}
/*
* Add the tx control blocks in the pending list to
* the free list.
*/
}
}
/*
* igb_tx_drain - Drain the tx rings to allow pending packets to be transmitted
*/
static boolean_t
{
int i, j;
/*
* Wait for a specific time to allow pending tx packets
* to be transmitted.
*
* Check the counter tbd_free to see if transmission is done.
* No lock protection is needed here.
*
* Return B_TRUE if all pending packets have been transmitted;
* Otherwise return B_FALSE;
*/
for (i = 0; i < TX_DRAIN_TIME; i++) {
for (j = 0; j < igb->num_tx_rings; j++) {
}
if (done)
break;
msec_delay(1);
}
return (done);
}
/*
* igb_rx_drain - Wait for all rx buffers to be released by upper layer
*/
static boolean_t
{
int i;
/*
* Polling the rx free list to check if those rx buffers held by
* the upper layer are released.
*
* Check the counter rcb_free to see if all pending buffers are
* released. No lock protection is needed here.
*
* Return B_TRUE if all pending buffers have been released;
* Otherwise return B_FALSE;
*/
for (i = 0; i < RX_DRAIN_TIME; i++) {
if (done)
break;
msec_delay(1);
}
return (done);
}
/*
*/
int
{
int i;
if (alloc_buffer) {
"Failed to allocate software receive rings");
return (IGB_FAILURE);
}
return (IGB_FAILURE);
}
} else {
}
for (i = 0; i < igb->num_rx_rings; i++)
for (i = 0; i < igb->num_tx_rings; i++)
/*
* Start the adapter
*/
goto start_failure;
}
}
/*
*/
/*
* Enable adapter interrupts
* The interrupts must be enabled after the driver state is START
*/
goto start_failure;
goto start_failure;
return (IGB_SUCCESS);
return (IGB_FAILURE);
}
/*
*/
void
{
int i;
/*
* Disable the adapter interrupts
*/
/*
* Drain the pending tx packets
*/
(void) igb_tx_drain(igb);
for (i = 0; i < igb->num_rx_rings; i++)
for (i = 0; i < igb->num_tx_rings; i++)
/*
* Stop the adapter
*/
/*
*/
}
if (free_buffer) {
/*
*/
}
}
/*
*/
static int
{
/*
* Allocate memory space for rx rings
*/
return (IGB_FAILURE);
}
/*
* Allocate memory space for tx rings
*/
return (IGB_FAILURE);
}
/*
* Allocate memory space for rx ring groups
*/
return (IGB_FAILURE);
}
return (IGB_SUCCESS);
}
/*
*/
static void
{
}
}
}
}
static int
{
int i;
for (i = 0; i < igb->num_rx_rings; i++) {
goto alloc_rx_rings_failure;
}
return (IGB_SUCCESS);
return (IGB_FAILURE);
}
static void
{
int i;
for (i = 0; i < igb->num_rx_rings; i++) {
if (rx_data->rcb_pending == 0) {
}
}
}
}
/*
*/
static void
{
/*
*
* 1. Setup the descriptor ring and the control block buffers;
* 3. Initialize software pointers/parameters for receive/transmit;
*/
}
static void
{
union e1000_adv_rx_desc *rbd;
int i;
/*
* Initialize descriptor ring with buffer addresses
*/
for (i = 0; i < igb->rx_ring_size; i++) {
}
/*
* Initialize the base address registers
*/
/*
* Initialize the length register
*/
/*
* Initialize buffer size & descriptor type
*/
/*
* Setup the Receive Descriptor Control Register (RXDCTL)
*/
}
static void
{
int i;
/*
* Setup the Receive Control Register (RCTL), and enable the
* receiver. The initial configuration is to: enable the receiver,
* accept broadcasts, discard bad packets, accept long packets,
* disable VLAN filter checking, and set receive buffer size to
* 2k. For 82575, also set the receive descriptor minimum
* threshold size to 1/2 the ring.
*/
/*
* Clear the field used for wakeup control. This driver doesn't do
* wakeup but leave this here for completeness.
*/
E1000_RCTL_BAM | /* Accept Broadcast Packets */
E1000_RCTL_LPE | /* Large Packet Enable */
/* Multicast filter offset */
E1000_RCTL_RDMTS_HALF | /* rx descriptor threshold */
E1000_RCTL_SECRC); /* Strip Ethernet CRC */
for (i = 0; i < igb->num_rx_groups; i++) {
}
/*
* Set up all rx descriptor rings - must be called before receive unit
* enabled.
*/
for (i = 0; i < igb->num_rx_rings; i++) {
/*
* Map a ring to a group by assigning a group index
*/
}
/*
* Setup the Rx Long Packet Max Length register
*/
/*
* Hardware checksum settings
*/
if (igb->rx_hcksum_enable) {
rxcsum =
E1000_RXCSUM_TUOFL | /* TCP/UDP checksum */
E1000_RXCSUM_IPOFL; /* IP checksum */
}
/*
* Setup classify and RSS for multiple receive queues
*/
case E1000_VMDQ_OFF:
/*
* One ring group, only RSS is needed when more than
* one ring enabled.
*/
break;
case E1000_VMDQ_MAC:
/*
* Multiple groups, each group has one ring,
* only the MAC classification is needed.
*/
break;
case E1000_VMDQ_MAC_RSS:
/*
* Multiple groups and multiple rings, both
* MAC classification and RSS are needed.
*/
break;
}
/*
* Enable the receive unit - must be done after all
* the rx setup above.
*/
/*
* Initialize all adapter ring head & tail pointers - must
* be done after receive unit is enabled
*/
for (i = 0; i < igb->num_rx_rings; i++) {
}
/*
* 82575 with manageability enabled needs a special flush to make
* sure the fifos start clean.
*/
}
}
static void
{
/*
* Initialize the length register
*/
/*
* Initialize the base address registers
*/
/*
* Setup head & tail pointers
*/
/*
* Setup head write-back
*/
if (igb->tx_head_wb_enable) {
/*
* The memory of the head write-back is allocated using
* the extra tbd beyond the tail of the tbd ring.
*/
*tx_ring->tbd_head_wb = 0;
/* Set the head write-back enable bit */
/*
* Turn off relaxed ordering for head write back or it will
* cause problems with the tx recycling
*/
} else {
}
}
/*
* Enable TXDCTL per queue
*/
/*
* Initialize hardware checksum offload settings
*/
}
static void
{
int i;
for (i = 0; i < igb->num_tx_rings; i++) {
}
/*
* Setup the Transmit Control Register (TCTL)
*/
reg_val &= ~E1000_TCTL_CT;
/* Enable transmits */
reg_val |= E1000_TCTL_EN;
}
/*
* igb_setup_rss - Setup receive-side scaling feature
*/
static void
{
int shift = 0;
union e1000_reta {
} reta;
/* Setup the Redirection Table */
shift = 3;
shift = 6;
}
for (i = 0; i < (32 * 4); i++) {
if ((i & 3) == 3) {
}
}
/* Fill out hash function seeds */
for (i = 0; i < 10; i++) {
sizeof (uint32_t));
}
/* Setup the Multiple Receive Queue Control register */
/*
* Disable Packet Checksum to enable RSS for multiple receive queues.
*
* The Packet Checksum is not ethernet CRC. It is another kind of
* checksum offloading provided by the 82575 chipset besides the IP
* The Packet Checksum is by default computed over the entire packet
* from the first byte of the DA through the last byte of the CRC,
* including the Ethernet and IP headers.
*
* It is a hardware limitation that Packet Checksum is mutually
* exclusive with RSS.
*/
}
/*
* igb_setup_mac_rss_classify - Setup MAC classification and rss
*/
static void
{
int shift_group0, shift_group1;
union e1000_reta {
} reta;
/* Setup the Redirection Table, it is shared between two groups */
shift_group0 = 2;
shift_group1 = 6;
for (i = 0; i < (32 * 4); i++) {
if ((i & 3) == 3) {
}
}
/* Fill out hash function seeds */
for (i = 0; i < 10; i++) {
sizeof (uint32_t));
}
/*
* Setup the Multiple Receive Queue Control register,
* enable VMDq based on packet destination MAC address and RSS.
*/
/* Define the default group and default queues */
/*
* Disable Packet Checksum to enable RSS for multiple receive queues.
*
* The Packet Checksum is not ethernet CRC. It is another kind of
* checksum offloading provided by the 82575 chipset besides the IP
* The Packet Checksum is by default computed over the entire packet
* from the first byte of the DA through the last byte of the CRC,
* including the Ethernet and IP headers.
*
* It is a hardware limitation that Packet Checksum is mutually
* exclusive with RSS.
*/
}
/*
* igb_setup_mac_classify - Setup MAC classification feature
*/
static void
{
/*
* Setup the Multiple Receive Queue Control register,
* enable VMDq based on packet destination MAC address.
*/
/*
* Disable Packet Checksum to enable RSS for multiple receive queues.
*
* The Packet Checksum is not ethernet CRC. It is another kind of
* checksum offloading provided by the 82575 chipset besides the IP
* The Packet Checksum is by default computed over the entire packet
* from the first byte of the DA through the last byte of the CRC,
* including the Ethernet and IP headers.
*
* It is a hardware limitation that Packet Checksum is mutually
* exclusive with RSS.
*/
}
/*
* igb_init_unicst - Initialize the unicast addresses
*/
static void
{
int slot;
/*
* Here we should consider two situations:
*
* 1. Chipset is initialized the first time
* Initialize the multiple unicast addresses, and
* save the default MAC address.
*
* 2. Chipset is reset
* Recover the multiple unicast addresses from the
* software data structure to the RAR registers.
*/
/*
* Clear the default MAC address in the RAR0 rgister,
* which is loaded from EEPROM when system boot or chipreset,
* points when VMDq is enabled. For this reason, the RAR0
* must be cleared for both cases mentioned above.
*/
e1000_rar_clear(hw, 0);
if (!igb->unicst_init) {
/* Initialize the multiple unicast addresses */
} else {
/* Re-configure the RAR registers */
}
}
}
/*
* igb_unicst_find - Find the slot for the specified unicast address
*/
int
{
int slot;
mac_addr, ETHERADDRL) == 0)
return (slot);
}
return (-1);
}
/*
* igb_unicst_set - Set the unicast address to the specified slot
*/
int
int slot)
{
/*
* Save the unicast address in the software data structure
*/
/*
* Set the unicast address to the RAR register
*/
return (EIO);
}
return (0);
}
/*
* igb_multicst_add - Add a multicst address
*/
int
{
struct ether_addr *new_table;
if ((multiaddr[0] & 01) == 0) {
return (EINVAL);
}
igb->mcast_max_num);
return (ENOENT);
}
sizeof (struct ether_addr);
sizeof (struct ether_addr);
"Not enough memory to alloc mcast table");
return (ENOMEM);
}
}
}
igb->mcast_count++;
/*
* Update the multicast table in the hardware
*/
return (EIO);
}
return (0);
}
/*
* igb_multicst_remove - Remove a multicst address
*/
int
{
struct ether_addr *new_table;
int i;
for (i = 0; i < igb->mcast_count; i++) {
ETHERADDRL) == 0) {
for (i++; i < igb->mcast_count; i++) {
igb->mcast_table[i];
}
igb->mcast_count--;
break;
}
}
sizeof (struct ether_addr);
sizeof (struct ether_addr);
}
}
/*
* Update the multicast table in the hardware
*/
return (EIO);
}
return (0);
}
static void
{
}
}
/*
* igb_setup_multicast - setup multicast data structures
*
* This routine initializes all of the multicast related structures
* and save them in the hardware registers.
*/
static void
{
/*
* Update the multicase addresses to the MTA registers
*/
}
/*
* igb_get_conf - Get driver configurations set in driver.conf
*
* This routine gets user-configured values out of the configuration
* file igb.conf.
*
* For each configurable value, there is a minimum, a maximum, and a
* default.
* If user does not configure a value, use the default.
* If user configures below the minimum, use the minumum.
* If user configures above the maximum, use the maxumum.
*/
static void
{
int i;
/*
* igb driver supports the following user configurations:
*
* Link configurations:
* adv_autoneg_cap
* adv_1000fdx_cap
* adv_100fdx_cap
* adv_100hdx_cap
* adv_10fdx_cap
* adv_10hdx_cap
* Note: 1000hdx is not supported.
*
* Jumbo frame configuration:
* default_mtu
*
* Ethernet flow control configuration:
* flow_control
*
* Multiple rings configurations:
* tx_queue_number
* tx_ring_size
* rx_queue_number
* rx_ring_size
*
* Call igb_get_prop() to get the value for a specific
* configuration parameter.
*/
/*
* Link configurations
*/
/*
* Jumbo frame configurations
*/
sizeof (struct ether_vlan_header) + ETHERFCSL;
/*
* Ethernet flow control configuration
*/
if (flow_control == 4)
/*
* Multiple rings configurations
*/
/*
* Currently we do not support VMDq for 82576 and 82580.
* If it is e1000_82576, set num_rx_groups to 1.
*/
} else {
"Invalid rx groups number. Please enable multiple "
"rings first");
}
}
/*
* Check the divisibility between rx rings and rx groups.
*/
for (i = igb->num_rx_groups; i > 0; i--) {
if ((igb->num_rx_rings % i) == 0)
break;
}
if (i != igb->num_rx_groups) {
"Invalid rx groups number. Downgrade the rx group "
"number to %d.", i);
igb->num_rx_groups = i;
}
/*
* Get the ring number per group.
*/
/*
* One rx ring group, the rx ring number is num_rx_rings.
*/
} else if (ring_per_group == 1) {
/*
* Multiple rx groups, each group has one rx ring.
*/
} else {
/*
* Multiple groups and multiple rings.
*/
}
/*
* Tunable used to force an interrupt type. The only use is
* for testing of the lesser interrupt types.
* 0 = don't force interrupt type
* 1 = force interrupt type MSIX
* 2 = force interrupt type MSI
* 3 = force interrupt type Legacy
*/
0, 1, 1);
0, 1, 1);
0, 1, 1);
0, 1, 1);
/*
* igb LSO needs the tx h/w checksum support.
* Here LSO will be disabled if tx h/w checksum has been disabled.
*/
/*
* Max number of multicast addresses
*/
igb->mcast_max_num =
}
/*
* igb_get_prop - Get a property value out of the configuration file igb.conf
*
* Caller provides the name of the property, a default value, a minimum
* value, and a maximum value.
*
* Return configured value of the property, with default, minimum and
* maximum properly applied.
*/
static int
char *propname, /* name of the property */
int minval, /* minimum acceptable value */
int maxval, /* maximim acceptable value */
int defval) /* default value */
{
int value;
/*
* Call ddi_prop_get_int() to read the conf settings
*/
return (value);
}
/*
* igb_setup_link - Using the link properties to setup the link
*/
int
{
struct e1000_mac_info *mac;
struct e1000_phy_info *phy;
phy->autoneg_advertised = 0;
/*
* 1000hdx is not supported for autonegotiation
*/
if (phy->autoneg_advertised == 0)
} else {
/*
* 1000fdx and 1000hdx are not supported for forced link
*/
else
}
if (invalid) {
"autonegotiation with full link capabilities.");
}
if (setup_hw) {
return (IGB_FAILURE);
}
return (IGB_SUCCESS);
}
/*
* igb_is_link_up - Check if the link is up
*/
static boolean_t
{
/*
* get_link_status is set in the interrupt handler on link-status-change
* or rx sequence error interrupt. get_link_status will stay
* false until the e1000_check_for_link establishes link only
* for copper adapters.
*/
case e1000_media_type_copper:
(void) e1000_check_for_link(hw);
} else {
}
break;
case e1000_media_type_fiber:
(void) e1000_check_for_link(hw);
break;
(void) e1000_check_for_link(hw);
break;
}
return (link_up);
}
/*
* igb_link_check - Link status processing
*/
static boolean_t
{
if (igb_is_link_up(igb)) {
/*
* The Link is up, check whether it was marked as down earlier
*/
igb->link_down_timeout = 0;
if (!igb->link_complete)
}
} else if (igb->link_complete) {
igb->link_speed = 0;
igb->link_duplex = 0;
}
igb->link_down_timeout++;
} else if (igb->link_down_timeout ==
igb->link_down_timeout++;
}
}
}
return (B_FALSE);
}
return (link_changed);
}
/*
* igb_local_timer - driver watchdog function
*
* This function will handle the hardware stall check, link status
* check and other routines.
*/
static void
igb_local_timer(void *arg)
{
igb->reset_count++;
return;
}
igb->reset_count++;
return;
}
if (link_changed)
}
/*
* igb_link_timer - link setup timer function
*
* It is called when the timer for link setup is expired, which indicates
* the completion of the link setup. The link state will not be updated
* until the link setup is completed. And the link state will not be sent
* to the upper layer through mac_link_update() in this function. It will
* be updated in the local timer routine or the interrupts service routine
* after the interface is started (plumbed).
*/
static void
igb_link_timer(void *arg)
{
}
/*
* igb_stall_check - check for transmit stall
*
* This function checks if the adapter is stalled (in transmit).
*
* It is called each time the watchdog timeout is invoked.
* If the transmit descriptor reclaim continuously fails,
* the watchdog value will increment by 1. If the watchdog
* value exceeds the threshold, the igb is assumed to
* have stalled and need to be reset.
*/
static boolean_t
{
int i;
return (B_FALSE);
/*
* If any tx ring is stalled, we'll reset the chipset
*/
for (i = 0; i < igb->num_tx_rings; i++) {
if (tx_ring->recycle_fail > 0)
else
tx_ring->stall_watchdog = 0;
= B_TRUE;
}
break;
}
}
if (result) {
tx_ring->stall_watchdog = 0;
tx_ring->recycle_fail = 0;
}
return (result);
}
/*
* is_valid_mac_addr - Check if the mac address is valid
*/
static boolean_t
{
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
return (B_FALSE);
return (B_TRUE);
}
static boolean_t
{
#ifdef __sparc
struct ether_addr sysaddr;
int err;
/*
* The "vendor's factory-set address" may already have
* been extracted from the chip, but if the property
* "local-mac-address" is set we use that instead.
*
* We check whether it looks like an array of 6
* bytes (which it should, if OBP set it). If we can't
* make sense of it this way, we'll ignore it.
*/
if (err == DDI_PROP_SUCCESS) {
if (nelts == ETHERADDRL) {
while (nelts--)
}
}
/*
* Look up the OBP property "local-mac-address?". If the user has set
* 'local-mac-address? = false', use "the system address" instead.
*/
}
}
}
/*
* Finally(!), if there's a valid "mac-address" property (created
* if we netbooted from this interface), we must use this instead
* get confused by the address changing as Solaris takes over!
*/
if (err == DDI_PROP_SUCCESS) {
if (nelts == ETHERADDRL) {
while (nelts--)
}
}
if (found) {
return (B_TRUE);
}
#endif
/*
* Read the device MAC address from the EEPROM
*/
return (B_FALSE);
return (B_TRUE);
}
#pragma inline(igb_arm_watchdog_timer)
static void
{
/*
* Fire a watchdog timer
*/
igb->watchdog_tid =
}
/*
* igb_enable_watchdog_timer - Enable and start the driver watchdog timer
*/
void
{
if (!igb->watchdog_enable) {
}
}
/*
* igb_disable_watchdog_timer - Disable and stop the driver watchdog timer
*/
void
{
igb->watchdog_tid = 0;
if (tid != 0)
}
/*
* igb_start_watchdog_timer - Start the driver watchdog timer
*/
static void
{
if (igb->watchdog_enable) {
if (!igb->watchdog_start) {
}
}
}
/*
* igb_restart_watchdog_timer - Restart the driver watchdog timer
*/
static void
{
if (igb->watchdog_start)
}
/*
* igb_stop_watchdog_timer - Stop the driver watchdog timer
*/
static void
{
igb->watchdog_tid = 0;
if (tid != 0)
}
/*
* igb_start_link_timer - Start the link setup timer
*/
static void
{
drv_usectohz(100000);
else
} else {
}
}
/*
* igb_stop_link_timer - Stop the link setup timer
*/
static void
{
if (tid != 0)
}
/*
*/
static void
{
/*
* Set the IMC register to mask all the interrupts,
* including the tx interrupts.
*/
/*
* Additional disabling for MSI-X
*/
}
}
/*
* igb_enable_adapter_interrupts_82580 - Enable NIC interrupts for 82580
*/
static void
{
/* Clear any pending interrupts */
/* Interrupt enabling for MSI-X */
} else { /* Interrupt enabling for MSI and legacy */
}
/* Disable auto-mask for ICR interrupt bits */
}
/*
* igb_enable_adapter_interrupts_82576 - Enable NIC interrupts for 82576
*/
static void
{
/* Clear any pending interrupts */
/* Interrupt enabling for MSI-X */
} else {
/* Interrupt enabling for MSI and legacy */
}
/* Disable auto-mask for ICR interrupt bits */
}
/*
* igb_enable_adapter_interrupts_82575 - Enable NIC interrupts for 82575
*/
static void
{
/* Clear any pending interrupts */
/* Interrupt enabling for MSI-X */
/* Enable MSI-X PBA support */
/* Non-selective interrupt clear-on-read */
} else {
/* Interrupt enabling for MSI and legacy */
}
}
/*
* Loopback Support
*/
static lb_property_t lb_normal =
static lb_property_t lb_external =
static lb_property_t lb_phy =
static lb_property_t lb_serdes =
enum ioc_reply
{
return (IOC_INVAL);
default:
return (IOC_INVAL);
case LB_GET_INFO_SIZE:
size = sizeof (lb_info_sz_t);
return (IOC_INVAL);
else
value += sizeof (lb_external);
break;
case LB_GET_INFO:
else
value += sizeof (lb_external);
return (IOC_INVAL);
value = 0;
else
break;
case LB_GET_MODE:
return (IOC_INVAL);
break;
case LB_SET_MODE:
size = 0;
return (IOC_INVAL);
return (IOC_INVAL);
break;
}
return (IOC_INVAL);
}
return (IOC_REPLY);
}
/*
* igb_set_loopback_mode - Setup loopback based on the loopback mode
*/
static boolean_t
{
int i;
return (B_TRUE);
if (mode == IGB_LB_NONE) {
/* Reset the chip */
return (B_TRUE);
}
switch (mode) {
default:
return (B_FALSE);
case IGB_LB_EXTERNAL:
break;
case IGB_LB_INTERNAL_PHY:
break;
case IGB_LB_INTERNAL_SERDES:
break;
}
/*
* When external loopback is set, wait up to 1000ms to get the link up.
* According to test, 1000ms can work and it's an experimental value.
*/
if (mode == IGB_LB_EXTERNAL) {
for (i = 0; i <= 10; i++) {
(void) igb_link_check(igb);
break;
msec_delay(100);
}
/*
* Does not support external loopback.
* Reset driver to loopback none.
*/
/* Reset the chip */
"reset to loopback none.");
return (B_FALSE);
}
}
return (B_TRUE);
}
/*
* igb_set_external_loopback - Set the external loopback mode
*/
static void
{
/* Set phy to known state */
(void) e1000_phy_hw_reset(hw);
}
/*
* igb_set_internal_phy_loopback - Set the internal PHY loopback mode
*/
static void
{
/* Set link mode to PHY (00b) in the Extended Control register */
/*
* Set PHY control register (0x4140):
* Set full duplex mode
* Set loopback bit
* Clear auto-neg enable bit
* Set PHY speed
*/
/* Set the link disable bit in the Port Configuration register */
}
/*
* igb_set_internal_serdes_loopback - Set the internal SerDes loopback mode
*/
static void
{
/* Set link mode to SerDes (11b) in the Extended Control register */
/* Configure the SerDes to loopback */
/* Set Device Control register */
E1000_CTRL_SLU); /* Force link up */
E1000_CTRL_TFCE | /* Disable transmit flow control */
E1000_CTRL_LRST); /* Clear link reset */
/* Set PCS Link Control register */
}
#pragma inline(igb_intr_rx_work)
/*
* igb_intr_rx_work - rx processing of ISR
*/
static void
{
}
#pragma inline(igb_intr_tx_work)
/*
* igb_intr_tx_work - tx processing of ISR
*/
static void
{
/* Recycle the tx descriptors */
/* Schedule the re-transmit */
if (tx_ring->reschedule &&
}
}
#pragma inline(igb_intr_link_work)
/*
* igb_intr_link_work - link-status-change processing of ISR
*/
static void
{
/*
* Because we got a link-status-change interrupt, force
* e1000_check_for_link() to look at phy
*/
/* igb_link_check takes care of link status change */
/* Get new phy state */
if (link_changed)
}
/*
* igb_intr_legacy - Interrupt handler for legacy interrupts
*/
static uint_t
{
return (DDI_INTR_UNCLAIMED);
}
return (DDI_INTR_UNCLAIMED);
}
if (icr & E1000_ICR_INT_ASSERTED) {
/*
* E1000_ICR_INT_ASSERTED bit was set:
* Read(Clear) the ICR, claim this interrupt,
* look for work to do.
*/
/* Make sure all interrupt causes cleared */
if (icr & E1000_ICR_RXT0) {
}
if (icr & E1000_ICR_TXDW) {
/* Recycle the tx descriptors */
/* Schedule the re-transmit */
}
if (icr & E1000_ICR_LSC) {
/*
* Because we got a link-status-change interrupt, force
* e1000_check_for_link() to look at phy
*/
/* igb_link_check takes care of link status change */
/* Get new phy state */
}
if (icr & E1000_ICR_DRSTA) {
/* 82580 Full Device Reset needed */
}
} else {
/*
* E1000_ICR_INT_ASSERTED bit was not set:
* Don't claim this interrupt.
*/
}
/*
* Do the following work outside of the gen_lock
*/
if (tx_reschedule) {
}
if (link_changed)
return (result);
}
/*
* igb_intr_msi - Interrupt handler for MSI
*/
static uint_t
{
return (DDI_INTR_CLAIMED);
}
/* Make sure all interrupt causes cleared */
/*
* For MSI interrupt, we have only one vector,
* so we have only one rx ring and one tx ring enabled.
*/
if (icr & E1000_ICR_RXT0) {
}
if (icr & E1000_ICR_TXDW) {
}
if (icr & E1000_ICR_LSC) {
}
if (icr & E1000_ICR_DRSTA) {
/* 82580 Full Device Reset needed */
}
return (DDI_INTR_CLAIMED);
}
/*
* igb_intr_rx - Interrupt handler for rx
*/
static uint_t
{
/*
* Only used via MSI-X vector so don't check cause bits
* and only clean the given ring.
*/
return (DDI_INTR_CLAIMED);
}
/*
* igb_intr_tx - Interrupt handler for tx
*/
static uint_t
{
/*
* Only used via MSI-X vector so don't check cause bits
* and only clean the given ring.
*/
return (DDI_INTR_CLAIMED);
}
/*
* igb_intr_tx_other - Interrupt handler for both tx and other
*
*/
static uint_t
{
return (DDI_INTR_CLAIMED);
}
/*
* Look for tx reclaiming work first. Remember, in the
* case of only interrupt sharing, only one tx ring is
* used
*/
/*
* Check for "other" causes.
*/
if (icr & E1000_ICR_LSC) {
}
/*
* The DOUTSYNC bit indicates a tx packet dropped because
* DMA engine gets "out of sync". There isn't a real fix
* for this. The Intel recommendation is to count the number
* of occurrences so user can detect when it is happening.
* The issue is non-fatal and there's no recovery action
* available.
*/
if (icr & E1000_ICR_DOUTSYNC) {
}
if (icr & E1000_ICR_DRSTA) {
/* 82580 Full Device Reset needed */
}
return (DDI_INTR_CLAIMED);
}
/*
* igb_alloc_intrs - Allocate interrupts for the driver
*
* Normal sequence is to try MSI-X; if not sucessful, try MSI;
* if not successful, try Legacy.
* igb->intr_force can be used to force sequence to start with
* any of the 3 types.
*/
static int
{
int intr_types;
int rc;
/* Get supported interrupt types */
if (rc != DDI_SUCCESS) {
"Get supported interrupt types failed: %d", rc);
return (IGB_FAILURE);
}
/* Install MSI-X interrupts */
if ((intr_types & DDI_INTR_TYPE_MSIX) &&
if (rc == IGB_SUCCESS)
return (IGB_SUCCESS);
"Allocate MSI-X failed, trying MSI interrupts...");
}
/* MSI-X not used, force rings to 1 */
"MSI-X not used, force rx and tx queue number to 1");
/* Install MSI interrupts */
if ((intr_types & DDI_INTR_TYPE_MSI) &&
if (rc == IGB_SUCCESS)
return (IGB_SUCCESS);
"Allocate MSI failed, trying Legacy interrupts...");
}
/* Install legacy interrupts */
if (intr_types & DDI_INTR_TYPE_FIXED) {
if (rc == IGB_SUCCESS)
return (IGB_SUCCESS);
"Allocate Legacy interrupts failed");
}
/* If none of the 3 types succeeded, return failure */
return (IGB_FAILURE);
}
/*
* igb_alloc_intr_handles - Allocate interrupt handles.
*
* For legacy and MSI, only 1 handle is needed. For MSI-X,
* if fewer than 2 handles are available, return failure.
* Upon success, this sets the number of Rx rings to a number that
* matches the handles available for Rx interrupts.
*/
static int
{
int rc;
switch (intr_type) {
case DDI_INTR_TYPE_FIXED:
minimum = 1;
break;
case DDI_INTR_TYPE_MSI:
minimum = 1;
break;
case DDI_INTR_TYPE_MSIX:
/*
* Number of vectors for the adapter is
* # rx rings + # tx rings
* One of tx vectors is for tx & other
*/
minimum = 2;
break;
default:
"invalid call to igb_alloc_intr_handles(): %d\n",
return (IGB_FAILURE);
}
/*
* Get number of supported interrupts
*/
"Get supported interrupt number failed. "
return (IGB_FAILURE);
}
/*
* Get number of available interrupts
*/
"Get available interrupt number failed. "
return (IGB_FAILURE);
}
}
actual = 0;
/*
* Allocate an array of interrupt handles
*/
if (rc != DDI_SUCCESS) {
"return: %d, request: %d, actual: %d",
goto alloc_handle_fail;
}
actual);
goto alloc_handle_fail;
}
/*
* For MSI-X, actual might force us to reduce number of tx & rx rings
*/
"MSI-X vectors force Tx queue number to %d",
} else {
"MSI-X vectors force Tx queue number to 1");
"MSI-X vectors force Rx queue number to %d",
actual - 1);
}
}
/*
* Get priority for first vector, assume remaining are all the same
*/
if (rc != DDI_SUCCESS) {
"Get interrupt priority failed: %d", rc);
goto alloc_handle_fail;
}
if (rc != DDI_SUCCESS) {
"Get interrupt cap failed: %d", rc);
goto alloc_handle_fail;
}
return (IGB_SUCCESS);
return (IGB_FAILURE);
}
/*
* igb_add_intr_handlers - Add interrupt handlers based on the interrupt type
*
* Before adding the interrupt handlers, the interrupt vectors have
*/
static int
{
int vector;
int rc;
int i;
vector = 0;
case DDI_INTR_TYPE_MSIX:
/* Add interrupt handler for tx + other */
if (rc != DDI_SUCCESS) {
return (IGB_FAILURE);
}
vector++;
/* Add interrupt handler for each rx ring */
for (i = 0; i < igb->num_rx_rings; i++) {
if (rc != DDI_SUCCESS) {
"Add rx interrupt handler failed. "
"return: %d, rx ring: %d", rc, i);
(void) ddi_intr_remove_handler(
}
return (IGB_FAILURE);
}
vector++;
}
/* Add interrupt handler for each tx ring from 2nd ring */
if (rc != DDI_SUCCESS) {
"Add tx interrupt handler failed. "
"return: %d, tx ring: %d", rc, i);
(void) ddi_intr_remove_handler(
}
return (IGB_FAILURE);
}
vector++;
}
break;
case DDI_INTR_TYPE_MSI:
/* Add interrupt handlers for the only vector */
if (rc != DDI_SUCCESS) {
"Add MSI interrupt handler failed: %d", rc);
return (IGB_FAILURE);
}
vector++;
break;
case DDI_INTR_TYPE_FIXED:
/* Add interrupt handlers for the only vector */
if (rc != DDI_SUCCESS) {
"Add legacy interrupt handler failed: %d", rc);
return (IGB_FAILURE);
}
vector++;
break;
default:
return (IGB_FAILURE);
}
return (IGB_SUCCESS);
}
/*
* igb_setup_msix_82575 - setup 82575 adapter to use MSI-X interrupts
*
* For each vector enabled on the adapter, Set the MSIXBM register accordingly
*/
static void
{
int i, vector;
/*
* Set vector for tx ring 0 and other causes.
* NOTE assumption that it is vector 0.
*/
vector = 0;
vector++;
for (i = 0; i < igb->num_rx_rings; i++) {
/*
* Set vector for each rx ring
*/
eims = (E1000_EICR_RX_QUEUE0 << i);
/*
* Accumulate bits to enable in
* igb_enable_adapter_interrupts_82575()
*/
vector++;
}
/*
* Set vector for each tx ring from 2nd tx ring
*/
eims = (E1000_EICR_TX_QUEUE0 << i);
/*
* Accumulate bits to enable in
* igb_enable_adapter_interrupts_82575()
*/
vector++;
}
/*
* Disable IAM for ICR interrupt bits
*/
}
/*
* igb_setup_msix_82576 - setup 82576 adapter to use MSI-X interrupts
*
* 82576 uses a table based method for assigning vectors. Each queue has a
* single entry in the table to which we write a vector number along with a
* "valid" bit. The entry is a single byte in a 4-byte register. Vectors
* take a different position in the 4-byte register depending on whether
* they are numbered above or below 8.
*/
static void
{
int i;
/* must enable msi-x capability before IVAR settings */
/*
* Set vector for tx ring 0 and other causes.
* NOTE assumption that it is vector 0.
* This is also interdependent with installation of interrupt service
* routines in igb_add_intr_handlers().
*/
/* assign "other" causes to vector 0 */
vector = 0;
/* assign tx ring 0 to vector 0 */
/* prepare to enable tx & other interrupt causes */
vector ++;
for (i = 0; i < igb->num_rx_rings; i++) {
/*
* Set vector for each rx ring
*/
index = (i & 0x7);
if (i < 8) {
/* vector goes into low byte of register */
} else {
/* vector goes into third byte of register */
}
/* Accumulate interrupt-cause bits to enable */
vector ++;
}
/*
* Set vector for each tx ring from 2nd tx ring.
* Note assumption that tx vectors numericall follow rx vectors.
*/
index = (i & 0x7);
if (i < 8) {
/* vector goes into second byte of register */
} else {
/* vector goes into fourth byte of register */
}
/* Accumulate interrupt-cause bits to enable */
vector ++;
}
}
/*
* igb_setup_msix_82580 - setup 82580 adapter to use MSI-X interrupts
*
* 82580 uses same table approach at 82576 but has fewer entries. Each
* queue has a single entry in the table to which we write a vector number
* along with a "valid" bit. Vectors take a different position in the
* register depending on * whether * they are numbered above or below 4.
*/
static void
{
int i;
/* must enable msi-x capability before IVAR settings */
/*
* Set vector for tx ring 0 and other causes.
* NOTE assumption that it is vector 0.
* This is also interdependent with installation of interrupt service
* routines in igb_add_intr_handlers().
*/
/* assign "other" causes to vector 0 */
vector = 0;
/* assign tx ring 0 to vector 0 */
/* prepare to enable tx & other interrupt causes */
vector ++;
for (i = 0; i < igb->num_rx_rings; i++) {
/*
* Set vector for each rx ring
*/
index = (i >> 1);
if (i & 1) {
/* vector goes into third byte of register */
} else {
/* vector goes into low byte of register */
}
/* Accumulate interrupt-cause bits to enable */
vector ++;
}
/*
* Set vector for each tx ring from 2nd tx ring.
* Note assumption that tx vectors numericall follow rx vectors.
*/
index = (i >> 1);
if (i & 1) {
/* vector goes into high byte of register */
} else {
/* vector goes into second byte of register */
}
/* Accumulate interrupt-cause bits to enable */
vector ++;
}
}
/*
* igb_rem_intr_handlers - remove the interrupt handlers
*/
static void
{
int i;
int rc;
if (rc != DDI_SUCCESS) {
"Remove intr handler failed: %d", rc);
}
}
}
/*
* igb_rem_intrs - remove the allocated interrupts
*/
static void
{
int i;
int rc;
if (rc != DDI_SUCCESS) {
"Free intr failed: %d", rc);
}
}
}
/*
* igb_enable_intrs - enable all the ddi interrupts
*/
static int
{
int i;
int rc;
/* Enable interrupts */
/* Call ddi_intr_block_enable() for MSI */
if (rc != DDI_SUCCESS) {
"Enable block intr failed: %d", rc);
return (IGB_FAILURE);
}
} else {
if (rc != DDI_SUCCESS) {
"Enable intr failed: %d", rc);
return (IGB_FAILURE);
}
}
}
return (IGB_SUCCESS);
}
/*
* igb_disable_intrs - disable all the ddi interrupts
*/
static int
{
int i;
int rc;
/* Disable all interrupts */
if (rc != DDI_SUCCESS) {
"Disable block intr failed: %d", rc);
return (IGB_FAILURE);
}
} else {
if (rc != DDI_SUCCESS) {
"Disable intr failed: %d", rc);
return (IGB_FAILURE);
}
}
}
return (IGB_SUCCESS);
}
/*
* igb_get_phy_state - Get and save the parameters read from PHY registers
*/
static void
{
(void) e1000_read_phy_reg(hw,
((phy_ext_status & IEEE_ESR_1000T_FD_CAPS) ||
((phy_ext_status & IEEE_ESR_1000T_HD_CAPS) ||
}
} else {
/*
* 1Gig Fiber adapter only offers 1Gig Full Duplex.
*/
igb->param_autoneg_cap = 0;
igb->param_1000hdx_cap = 0;
igb->param_100t4_cap = 0;
igb->param_100fdx_cap = 0;
igb->param_100hdx_cap = 0;
igb->param_10fdx_cap = 0;
igb->param_10hdx_cap = 0;
igb->param_adv_autoneg_cap = 0;
igb->param_adv_1000hdx_cap = 0;
igb->param_adv_100t4_cap = 0;
igb->param_adv_100fdx_cap = 0;
igb->param_adv_100hdx_cap = 0;
igb->param_adv_10fdx_cap = 0;
igb->param_adv_10hdx_cap = 0;
igb->param_lp_autoneg_cap = 0;
igb->param_lp_pause_cap = 0;
igb->param_lp_asym_pause_cap = 0;
igb->param_lp_1000fdx_cap = 0;
igb->param_lp_1000hdx_cap = 0;
igb->param_lp_100t4_cap = 0;
igb->param_lp_100fdx_cap = 0;
igb->param_lp_100hdx_cap = 0;
igb->param_lp_10fdx_cap = 0;
igb->param_lp_10hdx_cap = 0;
igb->param_lp_rem_fault = 0;
}
}
/*
* synchronize the adv* and en* parameters.
*
* parameters. The usage of ndd for setting adv parameters will
* synchronize all the en parameters with the e1000g parameters,
* implicitly disabling any settings made via dladm.
*/
static void
{
}
/*
* igb_get_driver_control
*/
static void
{
/* Notify firmware that driver is in control of device */
}
/*
* igb_release_driver_control
*/
static void
{
/* Notify firmware that driver is no longer in control of device */
}
/*
* igb_atomic_reserve - Atomic decrease operation
*/
int
{
/* ATOMICALLY */
do {
if (oldval < n)
return (-1);
return (newval);
}
/*
* FMA support
*/
int
{
return (de.fme_status);
}
int
{
return (de.fme_status);
}
/*
* The IO fault service error handling callback function
*/
/*ARGSUSED*/
static int
{
/*
* as the driver can always deal with an error in any dma or
* access handle, we can just return the fme_status value.
*/
return (err->fme_status);
}
static void
{
int fma_dma_flag;
/* Only register with IO Fault Services if we have some capability */
} else {
}
fma_dma_flag = 1;
} else {
fma_dma_flag = 0;
}
(void) igb_set_fma_flags(fma_dma_flag);
if (igb->fm_capabilities) {
/* Register capabilities with IO Fault Services */
/*
* Initialize pci ereport capabilities if ereport capable
*/
/*
* Register error callback if error callback capable
*/
igb_fm_error_cb, (void*) igb);
}
}
static void
{
/* Only unregister FMA capabilities if we registered some */
if (igb->fm_capabilities) {
/*
* Release any resources allocated by pci_ereport_setup()
*/
/*
* Un-register error callback if error callback capable
*/
/* Unregister from IO Fault Services */
}
}
void
{
char buf[FM_MAX_CLASS];
}
}