summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/e1000g
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/io/e1000g')
-rw-r--r--usr/src/uts/common/io/e1000g/e1000_osdep.h25
-rw-r--r--usr/src/uts/common/io/e1000g/e1000g_main.c85
-rw-r--r--usr/src/uts/common/io/e1000g/e1000g_rx.c36
-rw-r--r--usr/src/uts/common/io/e1000g/e1000g_sw.h19
-rw-r--r--usr/src/uts/common/io/e1000g/e1000g_tx.c41
-rw-r--r--usr/src/uts/common/io/e1000g/e1000g_workarounds.c13
6 files changed, 204 insertions, 15 deletions
diff --git a/usr/src/uts/common/io/e1000g/e1000_osdep.h b/usr/src/uts/common/io/e1000g/e1000_osdep.h
index 303d24848b..32d1b8cfad 100644
--- a/usr/src/uts/common/io/e1000g/e1000_osdep.h
+++ b/usr/src/uts/common/io/e1000g/e1000_osdep.h
@@ -21,6 +21,8 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms of the CDDLv1.
+ *
+ * Copyright 2016 Joyent, Inc.
*/
#ifndef _E1000_OSDEP_H
@@ -103,8 +105,23 @@ extern "C" {
*/
#define E1000_WRITE_FLUSH(a) (void)E1000_READ_REG(a, E1000_STATUS)
+/*
+ * Note, for all of the following register defines, it's important that these be
+ * in do {} while loops that only run a single time. Previously they were formed
+ * as normal blocks. Unfortunately this would fail in the following form which
+ * is used in the common code:
+ *
+ * if (cond)
+ * E1000_WRITE_REG
+ * else
+ * ...
+ *
+ * When the E1000_WRITE_REG macros was missing the do keyword, the compiler
+ * would end up associating the outer brace of the block with the if statement
+ * and thus the else clause would get left behind.
+ */
#define E1000_WRITE_REG(hw, reg, value) \
-{\
+do { \
if ((hw)->mac.type != e1000_82542) \
ddi_put32((OS_DEP(hw))->reg_handle, \
(uint32_t *)((uintptr_t)(hw)->hw_addr + reg), \
@@ -114,7 +131,7 @@ extern "C" {
(uint32_t *)((uintptr_t)(hw)->hw_addr + \
e1000_translate_register_82542(reg)), \
value); \
-}
+} while (0)
#define E1000_READ_REG(hw, reg) (\
((hw)->mac.type != e1000_82542) ? \
@@ -125,7 +142,7 @@ extern "C" {
e1000_translate_register_82542(reg))))
#define E1000_WRITE_REG_ARRAY(hw, reg, offset, value) \
-{\
+do {\
if ((hw)->mac.type != e1000_82542) \
ddi_put32((OS_DEP(hw))->reg_handle, \
(uint32_t *)((uintptr_t)(hw)->hw_addr + \
@@ -136,7 +153,7 @@ extern "C" {
(uint32_t *)((uintptr_t)(hw)->hw_addr + \
e1000_translate_register_82542(reg) + \
((offset) << 2)), value); \
-}
+} while (0)
#define E1000_READ_REG_ARRAY(hw, reg, offset) (\
((hw)->mac.type != e1000_82542) ? \
diff --git a/usr/src/uts/common/io/e1000g/e1000g_main.c b/usr/src/uts/common/io/e1000g/e1000g_main.c
index 514b4d8e7d..f08cde4525 100644
--- a/usr/src/uts/common/io/e1000g/e1000g_main.c
+++ b/usr/src/uts/common/io/e1000g/e1000g_main.c
@@ -25,7 +25,7 @@
/*
* Copyright 2012 DEY Storage Systems, Inc. All rights reserved.
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2015, Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
*/
/*
@@ -710,6 +710,17 @@ e1000g_regs_map(struct e1000g *Adapter)
goto regs_map_fail;
}
break;
+ case e1000_pch_spt:
+ /*
+ * On the SPT, the device flash is actually in BAR0, not a
+ * separate BAR. Therefore we end up setting the
+ * ich_flash_handle to be the same as the register handle.
+ * We mark the same to reduce the confusion in the other
+ * functions and macros. Though this does make the set up and
+ * tear-down path slightly more complicated.
+ */
+ osdep->ich_flash_handle = osdep->reg_handle;
+ hw->flash_address = hw->hw_addr;
default:
break;
}
@@ -769,7 +780,7 @@ e1000g_regs_map(struct e1000g *Adapter)
regs_map_fail:
if (osdep->reg_handle != NULL)
ddi_regs_map_free(&osdep->reg_handle);
- if (osdep->ich_flash_handle != NULL)
+ if (osdep->ich_flash_handle != NULL && hw->mac.type != e1000_pch_spt)
ddi_regs_map_free(&osdep->ich_flash_handle);
return (DDI_FAILURE);
}
@@ -897,6 +908,7 @@ e1000g_setup_max_mtu(struct e1000g *Adapter)
/* pch2 can do jumbo frames up to 9K */
case e1000_pch2lan:
case e1000_pch_lpt:
+ case e1000_pch_spt:
Adapter->max_mtu = MAXIMUM_MTU_9K;
break;
/* types with a special limit */
@@ -1129,7 +1141,8 @@ e1000g_unattach(dev_info_t *devinfo, struct e1000g *Adapter)
if (Adapter->attach_progress & ATTACH_PROGRESS_REGS_MAP) {
if (Adapter->osdep.reg_handle != NULL)
ddi_regs_map_free(&Adapter->osdep.reg_handle);
- if (Adapter->osdep.ich_flash_handle != NULL)
+ if (Adapter->osdep.ich_flash_handle != NULL &&
+ Adapter->shared.mac.type != e1000_pch_spt)
ddi_regs_map_free(&Adapter->osdep.ich_flash_handle);
if (Adapter->osdep.io_reg_handle != NULL)
ddi_regs_map_free(&Adapter->osdep.io_reg_handle);
@@ -1461,6 +1474,8 @@ e1000g_init(struct e1000g *Adapter)
pba = E1000_PBA_26K;
} else if (hw->mac.type == e1000_pch_lpt) {
pba = E1000_PBA_26K;
+ } else if (hw->mac.type == e1000_pch_spt) {
+ pba = E1000_PBA_26K;
} else {
/*
* Total FIFO is 40K
@@ -1686,6 +1701,12 @@ e1000g_link_up(struct e1000g *Adapter)
switch (hw->phy.media_type) {
case e1000_media_type_copper:
if (hw->mac.get_link_status) {
+ /*
+ * SPT devices need a bit of extra time before we ask
+ * them.
+ */
+ if (hw->mac.type == e1000_pch_spt)
+ msec_delay(50);
(void) e1000_check_for_link(hw);
if ((E1000_READ_REG(hw, E1000_STATUS) &
E1000_STATUS_LU)) {
@@ -1942,6 +1963,41 @@ start_fail:
return (DDI_FAILURE);
}
+/*
+ * The I219 has the curious property that if the descriptor rings are not
+ * emptied before resetting the hardware or before changing the device state
+ * based on runtime power management, it'll cause the card to hang. This can
+ * then only be fixed by a PCI reset. As such, for the I219 and it alone, we
+ * have to flush the rings if we're in this state.
+ */
+static void
+e1000g_flush_desc_rings(struct e1000g *Adapter)
+{
+ struct e1000_hw *hw = &Adapter->shared;
+ u16 hang_state;
+ u32 fext_nvm11, tdlen;
+
+ /* First, disable MULR fix in FEXTNVM11 */
+ fext_nvm11 = E1000_READ_REG(hw, E1000_FEXTNVM11);
+ fext_nvm11 |= E1000_FEXTNVM11_DISABLE_MULR_FIX;
+ E1000_WRITE_REG(hw, E1000_FEXTNVM11, fext_nvm11);
+
+ /* do nothing if we're not in faulty state, or if the queue is empty */
+ tdlen = E1000_READ_REG(hw, E1000_TDLEN(0));
+ hang_state = pci_config_get16(Adapter->osdep.cfg_handle,
+ PCICFG_DESC_RING_STATUS);
+ if (!(hang_state & FLUSH_DESC_REQUIRED) || !tdlen)
+ return;
+ e1000g_flush_tx_ring(Adapter);
+
+ /* recheck, maybe the fault is caused by the rx ring */
+ hang_state = pci_config_get16(Adapter->osdep.cfg_handle,
+ PCICFG_DESC_RING_STATUS);
+ if (hang_state & FLUSH_DESC_REQUIRED)
+ e1000g_flush_rx_ring(Adapter);
+
+}
+
static void
e1000g_m_stop(void *arg)
{
@@ -2005,6 +2061,14 @@ e1000g_stop(struct e1000g *Adapter, boolean_t global)
/* Clean the pending rx jumbo packet fragment */
e1000g_rx_clean(Adapter);
+ /*
+ * The I219, eg. the pch_spt, has bugs such that we must ensure that
+ * rings are flushed before we do anything else. This must be done
+ * before we release DMA resources.
+ */
+ if (Adapter->shared.mac.type == e1000_pch_spt)
+ e1000g_flush_desc_rings(Adapter);
+
if (global) {
e1000g_release_dma_resources(Adapter);
@@ -2453,16 +2517,17 @@ e1000g_init_unicst(struct e1000g *Adapter)
/*
* The common code does not correctly calculate the number of
- * rar's that could be reserved by firmware for the pch_lpt
- * macs. The interface has one primary rar, and 11 additional
- * ones. Those 11 additional ones are not always available.
- * According to the datasheet, we need to check a few of the
- * bits set in the FWSM register. If the value is zero,
- * everything is available. If the value is 1, none of the
+ * rar's that could be reserved by firmware for the pch_lpt and
+ * pch_spt macs. The interface has one primary rar, and 11
+ * additional ones. Those 11 additional ones are not always
+ * available. According to the datasheet, we need to check a
+ * few of the bits set in the FWSM register. If the value is
+ * zero, everything is available. If the value is 1, none of the
* additional registers are available. If the value is 2-7, only
* that number are available.
*/
- if (hw->mac.type == e1000_pch_lpt) {
+ if (hw->mac.type == e1000_pch_lpt ||
+ hw->mac.type == e1000_pch_spt) {
uint32_t locked, rar;
locked = E1000_READ_REG(hw, E1000_FWSM) &
diff --git a/usr/src/uts/common/io/e1000g/e1000g_rx.c b/usr/src/uts/common/io/e1000g/e1000g_rx.c
index 57c1d0dc94..3e12e172ed 100644
--- a/usr/src/uts/common/io/e1000g/e1000g_rx.c
+++ b/usr/src/uts/common/io/e1000g/e1000g_rx.c
@@ -24,6 +24,7 @@
/*
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
/*
@@ -903,3 +904,38 @@ rx_drop:
return (ret_mp);
}
+
+/*
+ * This is part of a workaround for the I219, see e1000g_flush_desc_rings() for
+ * more information.
+ *
+ * Flush all descriptors in the rx ring and disable it.
+ */
+void
+e1000g_flush_rx_ring(struct e1000g *Adapter)
+{
+ struct e1000_hw *hw = &Adapter->shared;
+ uint32_t rctl, rxdctl;
+
+ rctl = E1000_READ_REG(hw, E1000_RCTL);
+ E1000_WRITE_REG(hw, E1000_RCTL, rctl & ~E1000_RCTL_EN);
+ E1000_WRITE_FLUSH(hw);
+ usec_delay(150);
+
+ rxdctl = E1000_READ_REG(hw, E1000_RXDCTL(0));
+ /* Zero the lower 14 bits (prefetch and host thresholds). */
+ rxdctl &= 0xffffc000;
+ /*
+ * Update thresholds: prefetch threshold to 31, host threshold to 1
+ * and make sure the granularity is "descriptors" and not "cache lines"
+ */
+ rxdctl |= (0x1F | (1 << 8) | E1000_RXDCTL_THRESH_UNIT_DESC);
+ E1000_WRITE_REG(hw, E1000_RXDCTL(0), rxdctl);
+
+ /* Momentarily enable the RX ring for the changes to take effect */
+ E1000_WRITE_REG(hw, E1000_RCTL, rctl | E1000_RCTL_EN);
+ E1000_WRITE_FLUSH(hw);
+ usec_delay(150);
+ E1000_WRITE_REG(hw, E1000_RCTL, rctl & ~E1000_RCTL_EN);
+
+}
diff --git a/usr/src/uts/common/io/e1000g/e1000g_sw.h b/usr/src/uts/common/io/e1000g/e1000g_sw.h
index 259c22b9b3..261be80b1d 100644
--- a/usr/src/uts/common/io/e1000g/e1000g_sw.h
+++ b/usr/src/uts/common/io/e1000g/e1000g_sw.h
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2012 David Höppner. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#ifndef _E1000G_SW_H
@@ -1072,6 +1073,24 @@ void e1000g_set_fma_flags(int dma_flag);
int e1000g_reset_link(struct e1000g *Adapter);
/*
+ * Functions for working around various problems, these used to be from the
+ * common code.
+ */
+s32 e1000_fifo_workaround_82547(struct e1000_hw *hw, u16 length);
+void e1000_update_tx_fifo_head_82547(struct e1000_hw *hw, u32 length);
+void e1000_set_ttl_workaround_state_82541(struct e1000_hw *hw, bool state);
+bool e1000_ttl_workaround_enabled_82541(struct e1000_hw *hw);
+s32 e1000_igp_ttl_workaround_82547(struct e1000_hw *hw);
+
+/*
+ * I219 specific workarounds
+ */
+#define PCICFG_DESC_RING_STATUS 0xe4
+#define FLUSH_DESC_REQUIRED 0x100
+extern void e1000g_flush_rx_ring(struct e1000g *);
+extern void e1000g_flush_tx_ring(struct e1000g *);
+
+/*
* Global variables
*/
extern boolean_t e1000g_force_detach;
diff --git a/usr/src/uts/common/io/e1000g/e1000g_tx.c b/usr/src/uts/common/io/e1000g/e1000g_tx.c
index 1f8a51d291..80ba93aef9 100644
--- a/usr/src/uts/common/io/e1000g/e1000g_tx.c
+++ b/usr/src/uts/common/io/e1000g/e1000g_tx.c
@@ -24,7 +24,7 @@
*/
/*
- * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
/*
@@ -1723,3 +1723,42 @@ e1000g_82547_tx_move_tail(e1000g_tx_ring_t *tx_ring)
tx_ring->timer_enable_82547 = B_TRUE;
e1000g_82547_tx_move_tail_work(tx_ring);
}
+
+/*
+ * This is part of a workaround for the I219, see e1000g_flush_desc_rings() for
+ * more information.
+ *
+ * We need to clear any potential pending descriptors from the tx_ring. As
+ * we're about to reset the device, we don't care about the data that we give it
+ * itself.
+ */
+void
+e1000g_flush_tx_ring(struct e1000g *Adapter)
+{
+ struct e1000_hw *hw = &Adapter->shared;
+ e1000g_tx_ring_t *tx_ring = &Adapter->tx_ring[0];
+ uint32_t tctl, txd_lower = E1000_TXD_CMD_IFCS;
+ uint16_t size = 512;
+ struct e1000_tx_desc *desc;
+
+ tctl = E1000_READ_REG(hw, E1000_TCTL);
+ E1000_WRITE_REG(hw, E1000_TCTL, tctl | E1000_TCTL_EN);
+
+ desc = tx_ring->tbd_next;
+ if (tx_ring->tbd_next == tx_ring->tbd_last)
+ tx_ring->tbd_next = tx_ring->tbd_first;
+ else
+ tx_ring->tbd_next++;
+
+ /* We just need to set any valid address, so we use the ring itself */
+ desc->buffer_addr = tx_ring->tbd_dma_addr;
+ desc->lower.data = LE_32(txd_lower | size);
+ desc->upper.data = 0;
+
+ (void) ddi_dma_sync(tx_ring->tbd_dma_handle,
+ 0, 0, DDI_DMA_SYNC_FORDEV);
+ E1000_WRITE_REG(hw, E1000_TDT(0),
+ (uint32_t)(tx_ring->tbd_next - tx_ring->tbd_first));
+ (void) E1000_READ_REG(hw, E1000_STATUS);
+ usec_delay(250);
+}
diff --git a/usr/src/uts/common/io/e1000g/e1000g_workarounds.c b/usr/src/uts/common/io/e1000g/e1000g_workarounds.c
index b035cd8097..6d2751f910 100644
--- a/usr/src/uts/common/io/e1000g/e1000g_workarounds.c
+++ b/usr/src/uts/common/io/e1000g/e1000g_workarounds.c
@@ -24,6 +24,19 @@
*/
#include "e1000_api.h"
+#define E1000_FIFO_MULTIPLIER 0x80
+#define E1000_FIFO_HDR_SIZE 0x10
+#define E1000_FIFO_GRANULARITY 0x10
+#define E1000_FIFO_PAD_82547 0x3E0
+#define E1000_ERR_FIFO_WRAP 8
+
+#define DSP_RESET_ENABLE 0x0
+#define DSP_RESET_DISABLE 0x2
+#define E1000_MAX_DSP_RESETS 10
+
+#define E1000_ROUNDUP(size, unit) (((size) + (unit) - 1) & ~((unit) - 1))
+
+
/*
* e1000_ttl_workaround_enabled_82541 - Returns current TTL workaround status
* @hw: pointer to the HW structure