Author: sbruno
Date: Fri Sep  4 17:21:55 2015
New Revision: 287469
URL: https://svnweb.freebsd.org/changeset/base/287469

Log:
  em(4): Add Skylake/I219 support.
  - driver rev 7.5.2
  - use new functions em_flush* for i219 devices
  
  Differential Revision:        https://reviews.freebsd.org/D3163
  Submitted by: erj jfv
  Reviewed by:  jfv
  MFC after:    1 month
  Relnotes:     Yes
  Sponsored by: Intel Corporation

Modified:
  head/sys/dev/e1000/if_em.c
  head/sys/dev/e1000/if_em.h

Modified: head/sys/dev/e1000/if_em.c
==============================================================================
--- head/sys/dev/e1000/if_em.c  Fri Sep  4 16:59:01 2015        (r287468)
+++ head/sys/dev/e1000/if_em.c  Fri Sep  4 17:21:55 2015        (r287469)
@@ -103,7 +103,7 @@ int em_display_debug_stats = 0;
 /*********************************************************************
  *  Driver version:
  *********************************************************************/
-char em_driver_version[] = "7.4.2";
+char em_driver_version[] = "7.5.2";
 
 /*********************************************************************
  *  PCI Device ID Table
@@ -191,6 +191,11 @@ static em_vendor_info_t em_vendor_info_a
        { 0x8086, E1000_DEV_ID_PCH_I218_V2,     PCI_ANY_ID, PCI_ANY_ID, 0},
        { 0x8086, E1000_DEV_ID_PCH_I218_LM3,    PCI_ANY_ID, PCI_ANY_ID, 0},
        { 0x8086, E1000_DEV_ID_PCH_I218_V3,     PCI_ANY_ID, PCI_ANY_ID, 0},
+       { 0x8086, E1000_DEV_ID_PCH_SPT_I219_LM, PCI_ANY_ID, PCI_ANY_ID, 0},
+       { 0x8086, E1000_DEV_ID_PCH_SPT_I219_V,  PCI_ANY_ID, PCI_ANY_ID, 0},
+       { 0x8086, E1000_DEV_ID_PCH_SPT_I219_LM2,
+                                                PCI_ANY_ID, PCI_ANY_ID, 0},
+       { 0x8086, E1000_DEV_ID_PCH_SPT_I219_V2, PCI_ANY_ID, PCI_ANY_ID, 0},
        /* required last entry */
        { 0, 0, 0, 0, 0}
 };
@@ -238,6 +243,7 @@ static void em_free_pci_resources(struct
 static void    em_local_timer(void *);
 static void    em_reset(struct adapter *);
 static int     em_setup_interface(device_t, struct adapter *);
+static void    em_flush_desc_rings(struct adapter *);
 
 static void    em_setup_transmit_structures(struct adapter *);
 static void    em_initialize_transmit_unit(struct adapter *);
@@ -584,6 +590,20 @@ em_attach(device_t dev)
        }
 
        /*
+       ** In the new SPT device flash is not  a
+       ** separate BAR, rather it is also in BAR0,
+       ** so use the same tag and handle for the
+       ** FLASH read/write macros in the shared
+       ** code.
+       */
+       if (hw->mac.type == e1000_pch_spt) {
+               adapter->osdep.flash_bus_space_tag =
+                   adapter->osdep.mem_bus_space_tag;
+               adapter->osdep.flash_bus_space_handle =
+                   adapter->osdep.mem_bus_space_handle;
+       }
+
+       /*
         * Setup MSI/X or MSI if PCI Express
         */
        adapter->msix = em_setup_msix(adapter);
@@ -1168,6 +1188,7 @@ em_ioctl(if_t ifp, u_long command, caddr
                case e1000_ich10lan:
                case e1000_pch2lan:
                case e1000_pch_lpt:
+               case e1000_pch_spt:
                case e1000_82574:
                case e1000_82583:
                case e1000_80003es2lan: /* 9K Jumbo Frame size */
@@ -1369,8 +1390,15 @@ em_init_locked(struct adapter *adapter)
        if_clearhwassist(ifp);
        if (if_getcapenable(ifp) & IFCAP_TXCSUM)
                if_sethwassistbits(ifp, CSUM_TCP | CSUM_UDP, 0);
-       if (if_getcapenable(ifp) & IFCAP_TSO4)
-               if_sethwassistbits(ifp, CSUM_TSO, 0);
+       /* 
+       ** There have proven to be problems with TSO when not
+       ** at full gigabit speed, so disable the assist automatically
+       ** when at lower speeds.  -jfv
+       */
+       if (if_getcapenable(ifp) & IFCAP_TSO4) {
+               if (adapter->link_speed == SPEED_1000)
+                       if_sethwassistbits(ifp, CSUM_TSO, 0);
+       }
 
        /* Configure for OS presence */
        em_init_manageability(adapter);
@@ -2350,6 +2378,8 @@ em_update_link_status(struct adapter *ad
        switch (hw->phy.media_type) {
        case e1000_media_type_copper:
                if (hw->mac.get_link_status) {
+                       if (hw->mac.type == e1000_pch_spt)
+                               msec_delay(50);
                        /* Do the work to read phy */
                        e1000_check_for_link(hw);
                        link_check = !hw->mac.get_link_status;
@@ -2441,6 +2471,10 @@ em_stop(void *arg)
                EM_TX_UNLOCK(txr);
        }
 
+       /* I219 needs some special flushing to avoid hangs */
+       if (adapter->hw.mac.type == e1000_pch_spt)
+               em_flush_desc_rings(adapter);
+
        e1000_reset_hw(&adapter->hw);
        E1000_WRITE_REG(&adapter->hw, E1000_WUC, 0);
 
@@ -2860,6 +2894,116 @@ msi:
 }
 
 
+/*
+** The 3 following flush routines are used as a workaround in the
+** I219 client parts and only for them.
+**
+** em_flush_tx_ring - remove all descriptors from the tx_ring
+**
+** We want to clear all pending descriptors from the TX ring.
+** zeroing happens when the HW reads the regs. We  assign the ring itself as
+** the data of the next descriptor. We don't care about the data we are about
+** to reset the HW.
+*/
+static void
+em_flush_tx_ring(struct adapter *adapter)
+{
+       struct e1000_hw         *hw = &adapter->hw;
+       struct tx_ring          *txr = adapter->tx_rings;
+       struct e1000_tx_desc    *txd;
+       u32                     tctl, txd_lower = E1000_TXD_CMD_IFCS;
+       u16                     size = 512;
+
+       tctl = E1000_READ_REG(hw, E1000_TCTL);
+       E1000_WRITE_REG(hw, E1000_TCTL, tctl | E1000_TCTL_EN);
+
+       txd = &txr->tx_base[txr->next_avail_desc++];
+       if (txr->next_avail_desc == adapter->num_tx_desc)
+               txr->next_avail_desc = 0;
+
+       /* Just use the ring as a dummy buffer addr */
+       txd->buffer_addr = txr->txdma.dma_paddr;
+       txd->lower.data = htole32(txd_lower | size);
+       txd->upper.data = 0;
+
+       /* flush descriptors to memory before notifying the HW */
+       wmb();
+
+       E1000_WRITE_REG(hw, E1000_TDT(0), txr->next_avail_desc);
+       mb();
+       usec_delay(250);
+}
+
+/*
+** em_flush_rx_ring - remove all descriptors from the rx_ring
+**
+** Mark all descriptors in the RX ring as consumed and disable the rx ring
+*/
+static void
+em_flush_rx_ring(struct adapter *adapter)
+{
+       struct e1000_hw *hw = &adapter->hw;
+       u32             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);
+}
+
+/*
+** em_flush_desc_rings - remove all descriptors from the descriptor rings
+**
+** In i219, the descriptor rings must be emptied before resetting the HW
+** or before changing the device state to D3 during runtime (runtime PM).
+**
+** Failure to do this will cause the HW to enter a unit hang state which can
+** only be released by PCI reset on the device
+**
+*/
+static void
+em_flush_desc_rings(struct adapter *adapter)
+{
+       struct e1000_hw *hw = &adapter->hw;
+       device_t        dev = adapter->dev;
+       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_read_config(dev, PCICFG_DESC_RING_STATUS, 2);
+       if (!(hang_state & FLUSH_DESC_REQUIRED) || !tdlen)
+               return;
+       em_flush_tx_ring(adapter);
+
+       /* recheck, maybe the fault is caused by the rx ring */
+       hang_state = pci_read_config(dev, PCICFG_DESC_RING_STATUS, 2);
+       if (hang_state & FLUSH_DESC_REQUIRED)
+               em_flush_rx_ring(adapter);
+}
+
+
 /*********************************************************************
  *
  *  Initialize the hardware to a configuration
@@ -2921,6 +3065,7 @@ em_reset(struct adapter *adapter)
        case e1000_pchlan:
        case e1000_pch2lan:
        case e1000_pch_lpt:
+       case e1000_pch_spt:
                pba = E1000_PBA_26K;
                break;
        default:
@@ -2979,6 +3124,7 @@ em_reset(struct adapter *adapter)
                break;
        case e1000_pch2lan:
        case e1000_pch_lpt:
+       case e1000_pch_spt:
                hw->fc.high_water = 0x5C20;
                hw->fc.low_water = 0x5048;
                hw->fc.pause_time = 0x0650;
@@ -3003,6 +3149,10 @@ em_reset(struct adapter *adapter)
                break;
        }
 
+       /* I219 needs some special flushing to avoid hangs */
+       if (hw->mac.type == e1000_pch_spt)
+               em_flush_desc_rings(adapter);
+
        /* Issue a global reset */
        e1000_reset_hw(hw);
        E1000_WRITE_REG(hw, E1000_WUC, 0);
@@ -3599,6 +3749,15 @@ em_initialize_transmit_unit(struct adapt
        /* This write will effectively turn on the transmit unit. */
        E1000_WRITE_REG(&adapter->hw, E1000_TCTL, tctl);
 
+       if (hw->mac.type == e1000_pch_spt) {
+               u32 reg;
+               reg = E1000_READ_REG(hw, E1000_IOSFPC);
+               reg |= E1000_RCTL_RDMTS_HEX;
+               E1000_WRITE_REG(hw, E1000_IOSFPC, reg);
+               reg = E1000_READ_REG(hw, E1000_TARC(0));
+               reg |= E1000_TARC0_CB_MULTIQ_3_REQ;
+               E1000_WRITE_REG(hw, E1000_TARC(0), reg);
+       }
 }
 
 

Modified: head/sys/dev/e1000/if_em.h
==============================================================================
--- head/sys/dev/e1000/if_em.h  Fri Sep  4 16:59:01 2015        (r287468)
+++ head/sys/dev/e1000/if_em.h  Fri Sep  4 17:21:55 2015        (r287469)
@@ -218,6 +218,9 @@
 #define EM_TX_HUNG                     0x80000000
 #define EM_TX_MAXTRIES                 10
 
+#define PCICFG_DESC_RING_STATUS                0xe4
+#define FLUSH_DESC_REQUIRED            0x100
+
 /*
  * TDBA/RDBA should be aligned on 16 byte boundary. But TDLEN/RDLEN should be
  * multiple of 128 bytes. So we align TDBA/RDBA on 128 byte boundary. This will
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to