diff options
Diffstat (limited to 'usr/src/uts/common/io/e1000g')
-rw-r--r-- | usr/src/uts/common/io/e1000g/e1000_osdep.h | 25 | ||||
-rw-r--r-- | usr/src/uts/common/io/e1000g/e1000g_main.c | 85 | ||||
-rw-r--r-- | usr/src/uts/common/io/e1000g/e1000g_rx.c | 36 | ||||
-rw-r--r-- | usr/src/uts/common/io/e1000g/e1000g_sw.h | 19 | ||||
-rw-r--r-- | usr/src/uts/common/io/e1000g/e1000g_tx.c | 41 | ||||
-rw-r--r-- | usr/src/uts/common/io/e1000g/e1000g_workarounds.c | 13 |
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 |