mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-29 18:18:54 +00:00
4f0b17a4dc
This patch is motivated by sporadic hangs during link down/up on i219 NICs handling and the fix implemented upstream in https://git.ipxe.org/ipxe.git/commit/546dd51de8459d4d09958891f426fa2c73ff090d Issue #1220
821 lines
28 KiB
Diff
821 lines
28 KiB
Diff
diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c
|
|
index 7cb268ac..dad8941a 100644
|
|
--- a/src/drivers/net/intel.c
|
|
+++ b/src/drivers/net/intel.c
|
|
@@ -15,6 +15,10 @@
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA.
|
|
+ *
|
|
+ * You can also choose to distribute this program under the terms of
|
|
+ * the Unmodified Binary Distribution Licence (as given in the file
|
|
+ * COPYING.UBDL), provided that you have satisfied its requirements.
|
|
*/
|
|
|
|
FILE_LICENCE ( GPL2_OR_LATER );
|
|
@@ -233,27 +237,6 @@ static int intel_fetch_mac ( struct intel_nic *intel, uint8_t *hw_addr ) {
|
|
return -ENOENT;
|
|
}
|
|
|
|
-/******************************************************************************
|
|
- *
|
|
- * Diagnostics
|
|
- *
|
|
- ******************************************************************************
|
|
- */
|
|
-
|
|
-/**
|
|
- * Dump diagnostic information
|
|
- *
|
|
- * @v intel Intel device
|
|
- */
|
|
-static void __attribute__ (( unused )) intel_diag ( struct intel_nic *intel ) {
|
|
-
|
|
- DBGC ( intel, "INTEL %p TDH=%04x TDT=%04x RDH=%04x RDT=%04x\n", intel,
|
|
- readl ( intel->regs + intel->tx.reg + INTEL_xDH ),
|
|
- readl ( intel->regs + intel->tx.reg + INTEL_xDT ),
|
|
- readl ( intel->regs + intel->rx.reg + INTEL_xDH ),
|
|
- readl ( intel->regs + intel->rx.reg + INTEL_xDT ) );
|
|
-}
|
|
-
|
|
/******************************************************************************
|
|
*
|
|
* Device reset
|
|
@@ -269,39 +252,61 @@ static void __attribute__ (( unused )) intel_diag ( struct intel_nic *intel ) {
|
|
*/
|
|
static int intel_reset ( struct intel_nic *intel ) {
|
|
uint32_t pbs;
|
|
+ uint32_t pba;
|
|
uint32_t ctrl;
|
|
uint32_t status;
|
|
+ uint32_t orig_ctrl;
|
|
+ uint32_t orig_status;
|
|
+
|
|
+ /* Record initial control and status register values */
|
|
+ orig_ctrl = ctrl = readl ( intel->regs + INTEL_CTRL );
|
|
+ orig_status = readl ( intel->regs + INTEL_STATUS );
|
|
|
|
/* Force RX and TX packet buffer allocation, to work around an
|
|
* errata in ICH devices.
|
|
*/
|
|
- pbs = readl ( intel->regs + INTEL_PBS );
|
|
- if ( ( pbs == 0x14 ) || ( pbs == 0x18 ) ) {
|
|
+ if ( intel->flags & INTEL_PBS_ERRATA ) {
|
|
DBGC ( intel, "INTEL %p WARNING: applying ICH PBS/PBA errata\n",
|
|
intel );
|
|
+ pbs = readl ( intel->regs + INTEL_PBS );
|
|
+ pba = readl ( intel->regs + INTEL_PBA );
|
|
writel ( 0x08, intel->regs + INTEL_PBA );
|
|
writel ( 0x10, intel->regs + INTEL_PBS );
|
|
+ DBGC ( intel, "INTEL %p PBS %#08x->%#08x PBA %#08x->%#08x\n",
|
|
+ intel, pbs, readl ( intel->regs + INTEL_PBS ),
|
|
+ pba, readl ( intel->regs + INTEL_PBA ) );
|
|
}
|
|
|
|
/* Always reset MAC. Required to reset the TX and RX rings. */
|
|
- ctrl = readl ( intel->regs + INTEL_CTRL );
|
|
writel ( ( ctrl | INTEL_CTRL_RST ), intel->regs + INTEL_CTRL );
|
|
mdelay ( INTEL_RESET_DELAY_MS );
|
|
|
|
/* Set a sensible default configuration */
|
|
- ctrl |= ( INTEL_CTRL_SLU | INTEL_CTRL_ASDE );
|
|
+ if ( ! ( intel->flags & INTEL_NO_ASDE ) )
|
|
+ ctrl |= INTEL_CTRL_ASDE;
|
|
+ ctrl |= INTEL_CTRL_SLU;
|
|
ctrl &= ~( INTEL_CTRL_LRST | INTEL_CTRL_FRCSPD | INTEL_CTRL_FRCDPLX );
|
|
writel ( ctrl, intel->regs + INTEL_CTRL );
|
|
mdelay ( INTEL_RESET_DELAY_MS );
|
|
|
|
- /* If link is already up, do not attempt to reset the PHY. On
|
|
- * some models (notably ICH), performing a PHY reset seems to
|
|
- * drop the link speed to 10Mbps.
|
|
+ /* On some models (notably ICH), the PHY reset mechanism
|
|
+ * appears to be broken. In particular, the PHY_CTRL register
|
|
+ * will be correctly loaded from NVM but the values will not
|
|
+ * be propagated to the "OEM bits" PHY register. This
|
|
+ * typically has the effect of dropping the link speed to
|
|
+ * 10Mbps.
|
|
+ *
|
|
+ * Work around this problem by skipping the PHY reset if
|
|
+ * either (a) the link is already up, or (b) this particular
|
|
+ * NIC is known to be broken.
|
|
*/
|
|
status = readl ( intel->regs + INTEL_STATUS );
|
|
- if ( status & INTEL_STATUS_LU ) {
|
|
- DBGC ( intel, "INTEL %p MAC reset (ctrl %08x)\n",
|
|
- intel, ctrl );
|
|
+ if ( ( intel->flags & INTEL_NO_PHY_RST ) ||
|
|
+ ( status & INTEL_STATUS_LU ) ) {
|
|
+ DBGC ( intel, "INTEL %p %sMAC reset (%08x/%08x was "
|
|
+ "%08x/%08x)\n", intel,
|
|
+ ( ( intel->flags & INTEL_NO_PHY_RST ) ? "forced " : "" ),
|
|
+ ctrl, status, orig_ctrl, orig_status );
|
|
return 0;
|
|
}
|
|
|
|
@@ -316,8 +321,10 @@ static int intel_reset ( struct intel_nic *intel ) {
|
|
/* PHY reset is not self-clearing on all models */
|
|
writel ( ctrl, intel->regs + INTEL_CTRL );
|
|
mdelay ( INTEL_RESET_DELAY_MS );
|
|
+ status = readl ( intel->regs + INTEL_STATUS );
|
|
|
|
- DBGC ( intel, "INTEL %p MAC+PHY reset (ctrl %08x)\n", intel, ctrl );
|
|
+ DBGC ( intel, "INTEL %p MAC+PHY reset (%08x/%08x was %08x/%08x)\n",
|
|
+ intel, ctrl, status, orig_ctrl, orig_status );
|
|
return 0;
|
|
}
|
|
|
|
@@ -349,6 +356,67 @@ static void intel_check_link ( struct net_device *netdev ) {
|
|
}
|
|
}
|
|
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * Descriptors
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * Populate transmit descriptor
|
|
+ *
|
|
+ * @v tx Transmit descriptor
|
|
+ * @v addr Data buffer address
|
|
+ * @v len Length of data
|
|
+ */
|
|
+void intel_describe_tx ( struct intel_descriptor *tx, physaddr_t addr,
|
|
+ size_t len ) {
|
|
+
|
|
+ /* Populate transmit descriptor */
|
|
+ tx->address = cpu_to_le64 ( addr );
|
|
+ tx->length = cpu_to_le16 ( len );
|
|
+ tx->flags = 0;
|
|
+ tx->command = ( INTEL_DESC_CMD_RS | INTEL_DESC_CMD_IFCS |
|
|
+ INTEL_DESC_CMD_EOP );
|
|
+ tx->status = 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Populate advanced transmit descriptor
|
|
+ *
|
|
+ * @v tx Transmit descriptor
|
|
+ * @v addr Data buffer address
|
|
+ * @v len Length of data
|
|
+ */
|
|
+void intel_describe_tx_adv ( struct intel_descriptor *tx, physaddr_t addr,
|
|
+ size_t len ) {
|
|
+
|
|
+ /* Populate advanced transmit descriptor */
|
|
+ tx->address = cpu_to_le64 ( addr );
|
|
+ tx->length = cpu_to_le16 ( len );
|
|
+ tx->flags = INTEL_DESC_FL_DTYP_DATA;
|
|
+ tx->command = ( INTEL_DESC_CMD_DEXT | INTEL_DESC_CMD_RS |
|
|
+ INTEL_DESC_CMD_IFCS | INTEL_DESC_CMD_EOP );
|
|
+ tx->status = cpu_to_le32 ( INTEL_DESC_STATUS_PAYLEN ( len ) );
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Populate receive descriptor
|
|
+ *
|
|
+ * @v rx Receive descriptor
|
|
+ * @v addr Data buffer address
|
|
+ * @v len Length of data
|
|
+ */
|
|
+void intel_describe_rx ( struct intel_descriptor *rx, physaddr_t addr,
|
|
+ size_t len __unused ) {
|
|
+
|
|
+ /* Populate transmit descriptor */
|
|
+ rx->address = cpu_to_le64 ( addr );
|
|
+ rx->length = 0;
|
|
+ rx->status = 0;
|
|
+}
|
|
+
|
|
/******************************************************************************
|
|
*
|
|
* Network device interface
|
|
@@ -356,6 +424,61 @@ static void intel_check_link ( struct net_device *netdev ) {
|
|
******************************************************************************
|
|
*/
|
|
|
|
+/**
|
|
+ * Disable descriptor ring
|
|
+ *
|
|
+ * @v intel Intel device
|
|
+ * @v reg Register block
|
|
+ * @ret rc Return status code
|
|
+ */
|
|
+static int intel_disable_ring ( struct intel_nic *intel, unsigned int reg ) {
|
|
+ uint32_t dctl;
|
|
+ unsigned int i;
|
|
+
|
|
+ /* Disable ring */
|
|
+ writel ( 0, ( intel->regs + reg + INTEL_xDCTL ) );
|
|
+
|
|
+ /* Wait for disable to complete */
|
|
+ for ( i = 0 ; i < INTEL_DISABLE_MAX_WAIT_MS ; i++ ) {
|
|
+
|
|
+ /* Check if ring is disabled */
|
|
+ dctl = readl ( intel->regs + reg + INTEL_xDCTL );
|
|
+ if ( ! ( dctl & INTEL_xDCTL_ENABLE ) )
|
|
+ return 0;
|
|
+
|
|
+ /* Delay */
|
|
+ mdelay ( 1 );
|
|
+ }
|
|
+
|
|
+ DBGC ( intel, "INTEL %p ring %05x timed out waiting for disable "
|
|
+ "(dctl %08x)\n", intel, reg, dctl );
|
|
+ return -ETIMEDOUT;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Reset descriptor ring
|
|
+ *
|
|
+ * @v intel Intel device
|
|
+ * @v reg Register block
|
|
+ * @ret rc Return status code
|
|
+ */
|
|
+void intel_reset_ring ( struct intel_nic *intel, unsigned int reg ) {
|
|
+
|
|
+ /* Disable ring. Ignore errors and continue to reset the ring anyway */
|
|
+ intel_disable_ring ( intel, reg );
|
|
+
|
|
+ /* Clear ring length */
|
|
+ writel ( 0, ( intel->regs + reg + INTEL_xDLEN ) );
|
|
+
|
|
+ /* Clear ring address */
|
|
+ writel ( 0, ( intel->regs + reg + INTEL_xDBAH ) );
|
|
+ writel ( 0, ( intel->regs + reg + INTEL_xDBAL ) );
|
|
+
|
|
+ /* Reset head and tail pointers */
|
|
+ writel ( 0, ( intel->regs + reg + INTEL_xDH ) );
|
|
+ writel ( 0, ( intel->regs + reg + INTEL_xDT ) );
|
|
+}
|
|
+
|
|
/**
|
|
* Create descriptor ring
|
|
*
|
|
@@ -416,12 +539,8 @@ int intel_create_ring ( struct intel_nic *intel, struct intel_ring *ring ) {
|
|
*/
|
|
void intel_destroy_ring ( struct intel_nic *intel, struct intel_ring *ring ) {
|
|
|
|
- /* Clear ring length */
|
|
- writel ( 0, ( intel->regs + ring->reg + INTEL_xDLEN ) );
|
|
-
|
|
- /* Clear ring address */
|
|
- writel ( 0, ( intel->regs + ring->reg + INTEL_xDBAL ) );
|
|
- writel ( 0, ( intel->regs + ring->reg + INTEL_xDBAH ) );
|
|
+ /* Reset ring */
|
|
+ intel_reset_ring ( intel, ring->reg );
|
|
|
|
/* Free descriptor ring */
|
|
free_dma ( ring->desc, ring->len );
|
|
@@ -441,39 +560,41 @@ void intel_refill_rx ( struct intel_nic *intel ) {
|
|
unsigned int rx_idx;
|
|
unsigned int rx_tail;
|
|
physaddr_t address;
|
|
+ unsigned int refilled = 0;
|
|
|
|
+ /* Refill ring */
|
|
while ( ( intel->rx.prod - intel->rx.cons ) < INTEL_RX_FILL ) {
|
|
|
|
/* Allocate I/O buffer */
|
|
iobuf = alloc_iob ( INTEL_RX_MAX_LEN );
|
|
if ( ! iobuf ) {
|
|
/* Wait for next refill */
|
|
- return;
|
|
+ break;
|
|
}
|
|
|
|
/* Get next receive descriptor */
|
|
rx_idx = ( intel->rx.prod++ % INTEL_NUM_RX_DESC );
|
|
- rx_tail = ( intel->rx.prod % INTEL_NUM_RX_DESC );
|
|
rx = &intel->rx.desc[rx_idx];
|
|
|
|
/* Populate receive descriptor */
|
|
address = virt_to_bus ( iobuf->data );
|
|
- rx->address = cpu_to_le64 ( address );
|
|
- rx->length = 0;
|
|
- rx->status = 0;
|
|
- rx->errors = 0;
|
|
- wmb();
|
|
+ intel->rx.describe ( rx, address, 0 );
|
|
|
|
/* Record I/O buffer */
|
|
assert ( intel->rx_iobuf[rx_idx] == NULL );
|
|
intel->rx_iobuf[rx_idx] = iobuf;
|
|
|
|
- /* Push descriptor to card */
|
|
- writel ( rx_tail, intel->regs + intel->rx.reg + INTEL_xDT );
|
|
-
|
|
DBGC2 ( intel, "INTEL %p RX %d is [%llx,%llx)\n", intel, rx_idx,
|
|
( ( unsigned long long ) address ),
|
|
( ( unsigned long long ) address + INTEL_RX_MAX_LEN ) );
|
|
+ refilled++;
|
|
+ }
|
|
+
|
|
+ /* Push descriptors to card, if applicable */
|
|
+ if ( refilled ) {
|
|
+ wmb();
|
|
+ rx_tail = ( intel->rx.prod % INTEL_NUM_RX_DESC );
|
|
+ writel ( rx_tail, intel->regs + intel->rx.reg + INTEL_xDT );
|
|
}
|
|
}
|
|
|
|
@@ -501,10 +622,23 @@ void intel_empty_rx ( struct intel_nic *intel ) {
|
|
static int intel_open ( struct net_device *netdev ) {
|
|
struct intel_nic *intel = netdev->priv;
|
|
union intel_receive_address mac;
|
|
+ uint32_t fextnvm11;
|
|
uint32_t tctl;
|
|
uint32_t rctl;
|
|
int rc;
|
|
|
|
+ /* Set undocumented bit in FEXTNVM11 to work around an errata
|
|
+ * in i219 devices that will otherwise cause a complete
|
|
+ * datapath hang at the next device reset.
|
|
+ */
|
|
+ if ( intel->flags & INTEL_RST_HANG ) {
|
|
+ DBGC ( intel, "INTEL %p WARNING: applying reset hang "
|
|
+ "workaround\n", intel );
|
|
+ fextnvm11 = readl ( intel->regs + INTEL_FEXTNVM11 );
|
|
+ fextnvm11 |= INTEL_FEXTNVM11_WTF;
|
|
+ writel ( fextnvm11, intel->regs + INTEL_FEXTNVM11 );
|
|
+ }
|
|
+
|
|
/* Create transmit descriptor ring */
|
|
if ( ( rc = intel_create_ring ( intel, &intel->tx ) ) != 0 )
|
|
goto err_create_tx;
|
|
@@ -540,6 +674,13 @@ static int intel_open ( struct net_device *netdev ) {
|
|
/* Update link state */
|
|
intel_check_link ( netdev );
|
|
|
|
+ /* Apply required errata */
|
|
+ if ( intel->flags & INTEL_VMWARE ) {
|
|
+ DBGC ( intel, "INTEL %p applying VMware errata workaround\n",
|
|
+ intel );
|
|
+ intel->force_icr = INTEL_IRQ_RXT0;
|
|
+ }
|
|
+
|
|
return 0;
|
|
|
|
intel_destroy_ring ( intel, &intel->rx );
|
|
@@ -589,9 +730,10 @@ int intel_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
|
|
unsigned int tx_idx;
|
|
unsigned int tx_tail;
|
|
physaddr_t address;
|
|
+ size_t len;
|
|
|
|
/* Get next transmit descriptor */
|
|
- if ( ( intel->tx.prod - intel->tx.cons ) >= INTEL_NUM_TX_DESC ) {
|
|
+ if ( ( intel->tx.prod - intel->tx.cons ) >= INTEL_TX_FILL ) {
|
|
DBGC ( intel, "INTEL %p out of transmit descriptors\n", intel );
|
|
return -ENOBUFS;
|
|
}
|
|
@@ -601,11 +743,8 @@ int intel_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
|
|
|
|
/* Populate transmit descriptor */
|
|
address = virt_to_bus ( iobuf->data );
|
|
- tx->address = cpu_to_le64 ( address );
|
|
- tx->length = cpu_to_le16 ( iob_len ( iobuf ) );
|
|
- tx->command = ( INTEL_DESC_CMD_RS | INTEL_DESC_CMD_IFCS |
|
|
- INTEL_DESC_CMD_EOP );
|
|
- tx->status = 0;
|
|
+ len = iob_len ( iobuf );
|
|
+ intel->tx.describe ( tx, address, len );
|
|
wmb();
|
|
|
|
/* Notify card that there are packets ready to transmit */
|
|
@@ -613,7 +752,7 @@ int intel_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
|
|
|
|
DBGC2 ( intel, "INTEL %p TX %d is [%llx,%llx)\n", intel, tx_idx,
|
|
( ( unsigned long long ) address ),
|
|
- ( ( unsigned long long ) address + iob_len ( iobuf ) ) );
|
|
+ ( ( unsigned long long ) address + len ) );
|
|
|
|
return 0;
|
|
}
|
|
@@ -636,7 +775,7 @@ void intel_poll_tx ( struct net_device *netdev ) {
|
|
tx = &intel->tx.desc[tx_idx];
|
|
|
|
/* Stop if descriptor is still in use */
|
|
- if ( ! ( tx->status & INTEL_DESC_STATUS_DD ) )
|
|
+ if ( ! ( tx->status & cpu_to_le32 ( INTEL_DESC_STATUS_DD ) ) )
|
|
return;
|
|
|
|
DBGC2 ( intel, "INTEL %p TX %d complete\n", intel, tx_idx );
|
|
@@ -667,7 +806,7 @@ void intel_poll_rx ( struct net_device *netdev ) {
|
|
rx = &intel->rx.desc[rx_idx];
|
|
|
|
/* Stop if descriptor is still in use */
|
|
- if ( ! ( rx->status & INTEL_DESC_STATUS_DD ) )
|
|
+ if ( ! ( rx->status & cpu_to_le32 ( INTEL_DESC_STATUS_DD ) ) )
|
|
return;
|
|
|
|
/* Populate I/O buffer */
|
|
@@ -677,10 +816,10 @@ void intel_poll_rx ( struct net_device *netdev ) {
|
|
iob_put ( iobuf, len );
|
|
|
|
/* Hand off to network stack */
|
|
- if ( rx->errors ) {
|
|
+ if ( rx->status & cpu_to_le32 ( INTEL_DESC_STATUS_RXE ) ) {
|
|
DBGC ( intel, "INTEL %p RX %d error (length %zd, "
|
|
- "errors %02x)\n",
|
|
- intel, rx_idx, len, rx->errors );
|
|
+ "status %08x)\n", intel, rx_idx, len,
|
|
+ le32_to_cpu ( rx->status ) );
|
|
netdev_rx_err ( netdev, iobuf, -EIO );
|
|
} else {
|
|
DBGC2 ( intel, "INTEL %p RX %d complete (length %zd)\n",
|
|
@@ -721,6 +860,14 @@ static void intel_poll ( struct net_device *netdev ) {
|
|
if ( icr & INTEL_IRQ_LSC )
|
|
intel_check_link ( netdev );
|
|
|
|
+ /* Check for unexpected interrupts */
|
|
+ if ( icr & ~( INTEL_IRQ_TXDW | INTEL_IRQ_TXQE | INTEL_IRQ_LSC |
|
|
+ INTEL_IRQ_RXDMT0 | INTEL_IRQ_RXT0 | INTEL_IRQ_RXO ) ) {
|
|
+ DBGC ( intel, "INTEL %p unexpected ICR %08x\n", intel, icr );
|
|
+ /* Report as a TX error */
|
|
+ netdev_tx_err ( netdev, NULL, -ENOTSUP );
|
|
+ }
|
|
+
|
|
/* Refill RX ring */
|
|
intel_refill_rx ( intel );
|
|
}
|
|
@@ -782,14 +929,21 @@ static int intel_probe ( struct pci_device *pci ) {
|
|
netdev->dev = &pci->dev;
|
|
memset ( intel, 0, sizeof ( *intel ) );
|
|
intel->port = PCI_FUNC ( pci->busdevfn );
|
|
- intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTEL_TD );
|
|
- intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTEL_RD );
|
|
+ intel->flags = pci->id->driver_data;
|
|
+ intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTEL_TD,
|
|
+ intel_describe_tx );
|
|
+ intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTEL_RD,
|
|
+ intel_describe_rx );
|
|
|
|
/* Fix up PCI device */
|
|
adjust_pci_device ( pci );
|
|
|
|
/* Map registers */
|
|
intel->regs = ioremap ( pci->membase, INTEL_BAR_SIZE );
|
|
+ if ( ! intel->regs ) {
|
|
+ rc = -ENODEV;
|
|
+ goto err_ioremap;
|
|
+ }
|
|
|
|
/* Reset the NIC */
|
|
if ( ( rc = intel_reset ( intel ) ) != 0 )
|
|
@@ -814,6 +968,7 @@ static int intel_probe ( struct pci_device *pci ) {
|
|
intel_reset ( intel );
|
|
err_reset:
|
|
iounmap ( intel->regs );
|
|
+ err_ioremap:
|
|
netdev_nullify ( netdev );
|
|
netdev_put ( netdev );
|
|
err_alloc:
|
|
@@ -855,7 +1010,7 @@ static struct pci_device_id intel_nics[] = {
|
|
PCI_ROM ( 0x8086, 0x100c, "82544gc", "82544GC (Copper)", 0 ),
|
|
PCI_ROM ( 0x8086, 0x100d, "82544gc-l", "82544GC (LOM)", 0 ),
|
|
PCI_ROM ( 0x8086, 0x100e, "82540em", "82540EM", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x100f, "82545em", "82545EM (Copper)", 0 ),
|
|
+ PCI_ROM ( 0x8086, 0x100f, "82545em", "82545EM (Copper)", INTEL_VMWARE ),
|
|
PCI_ROM ( 0x8086, 0x1010, "82546eb", "82546EB (Copper)", 0 ),
|
|
PCI_ROM ( 0x8086, 0x1011, "82545em-f", "82545EM (Fiber)", 0 ),
|
|
PCI_ROM ( 0x8086, 0x1012, "82546eb-f", "82546EB (Fiber)", 0 ),
|
|
@@ -872,11 +1027,11 @@ static struct pci_device_id intel_nics[] = {
|
|
PCI_ROM ( 0x8086, 0x1026, "82545gm", "82545GM", 0 ),
|
|
PCI_ROM ( 0x8086, 0x1027, "82545gm-1", "82545GM", 0 ),
|
|
PCI_ROM ( 0x8086, 0x1028, "82545gm-2", "82545GM", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x1049, "82566mm", "82566MM", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x104a, "82566dm", "82566DM", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x104b, "82566dc", "82566DC", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x104c, "82562v", "82562V 10/100", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x104d, "82566mc", "82566MC", 0 ),
|
|
+ PCI_ROM ( 0x8086, 0x1049, "82566mm", "82566MM", INTEL_PBS_ERRATA ),
|
|
+ PCI_ROM ( 0x8086, 0x104a, "82566dm", "82566DM", INTEL_PBS_ERRATA ),
|
|
+ PCI_ROM ( 0x8086, 0x104b, "82566dc", "82566DC", INTEL_PBS_ERRATA ),
|
|
+ PCI_ROM ( 0x8086, 0x104c, "82562v", "82562V", INTEL_PBS_ERRATA ),
|
|
+ PCI_ROM ( 0x8086, 0x104d, "82566mc", "82566MC", INTEL_PBS_ERRATA ),
|
|
PCI_ROM ( 0x8086, 0x105e, "82571eb", "82571EB", 0 ),
|
|
PCI_ROM ( 0x8086, 0x105f, "82571eb-1", "82571EB", 0 ),
|
|
PCI_ROM ( 0x8086, 0x1060, "82571eb-2", "82571EB", 0 ),
|
|
@@ -909,11 +1064,11 @@ static struct pci_device_id intel_nics[] = {
|
|
PCI_ROM ( 0x8086, 0x10bc, "82571eb", "82571EB (Copper)", 0 ),
|
|
PCI_ROM ( 0x8086, 0x10bd, "82566dm-2", "82566DM-2", 0 ),
|
|
PCI_ROM ( 0x8086, 0x10bf, "82567lf", "82567LF", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x10c0, "82562v-2", "82562V-2 10/100", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x10c2, "82562g-2", "82562G-2 10/100", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x10c3, "82562gt-2", "82562GT-2 10/100", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x10c4, "82562gt", "82562GT 10/100", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x10c5, "82562g", "82562G 10/100", 0 ),
|
|
+ PCI_ROM ( 0x8086, 0x10c0, "82562v-2", "82562V-2", 0 ),
|
|
+ PCI_ROM ( 0x8086, 0x10c2, "82562g-2", "82562G-2", 0 ),
|
|
+ PCI_ROM ( 0x8086, 0x10c3, "82562gt-2", "82562GT-2", 0 ),
|
|
+ PCI_ROM ( 0x8086, 0x10c4, "82562gt", "82562GT", INTEL_PBS_ERRATA ),
|
|
+ PCI_ROM ( 0x8086, 0x10c5, "82562g", "82562G", INTEL_PBS_ERRATA ),
|
|
PCI_ROM ( 0x8086, 0x10c9, "82576", "82576", 0 ),
|
|
PCI_ROM ( 0x8086, 0x10cb, "82567v", "82567V", 0 ),
|
|
PCI_ROM ( 0x8086, 0x10cc, "82567lm-2", "82567LM-2", 0 ),
|
|
@@ -936,8 +1091,8 @@ static struct pci_device_id intel_nics[] = {
|
|
PCI_ROM ( 0x8086, 0x10f0, "82578dc", "82578DC", 0 ),
|
|
PCI_ROM ( 0x8086, 0x10f5, "82567lm", "82567LM", 0 ),
|
|
PCI_ROM ( 0x8086, 0x10f6, "82574l", "82574L", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x1501, "82567v-3", "82567V-3", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x1502, "82579lm", "82579LM", 0 ),
|
|
+ PCI_ROM ( 0x8086, 0x1501, "82567v-3", "82567V-3", INTEL_PBS_ERRATA ),
|
|
+ PCI_ROM ( 0x8086, 0x1502, "82579lm", "82579LM", INTEL_NO_PHY_RST ),
|
|
PCI_ROM ( 0x8086, 0x1503, "82579v", "82579V", 0 ),
|
|
PCI_ROM ( 0x8086, 0x150a, "82576ns", "82576NS", 0 ),
|
|
PCI_ROM ( 0x8086, 0x150c, "82583v", "82583V", 0 ),
|
|
@@ -950,24 +1105,40 @@ static struct pci_device_id intel_nics[] = {
|
|
PCI_ROM ( 0x8086, 0x1518, "82576ns", "82576NS SerDes", 0 ),
|
|
PCI_ROM ( 0x8086, 0x1521, "i350", "I350", 0 ),
|
|
PCI_ROM ( 0x8086, 0x1522, "i350-f", "I350 Fiber", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x1523, "i350-b", "I350 Backplane", 0 ),
|
|
+ PCI_ROM ( 0x8086, 0x1523, "i350-b", "I350 Backplane", INTEL_NO_ASDE ),
|
|
PCI_ROM ( 0x8086, 0x1524, "i350-2", "I350", 0 ),
|
|
PCI_ROM ( 0x8086, 0x1525, "82567v-4", "82567V-4", 0 ),
|
|
PCI_ROM ( 0x8086, 0x1526, "82576-5", "82576", 0 ),
|
|
PCI_ROM ( 0x8086, 0x1527, "82580-f2", "82580 Fiber", 0 ),
|
|
PCI_ROM ( 0x8086, 0x1533, "i210", "I210", 0 ),
|
|
PCI_ROM ( 0x8086, 0x1539, "i211", "I211", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x153a, "i217lm", "I217LM", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x1559, "i218v", "I218V", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x155a, "i218lm", "I218LM", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x15a1, "i218v", "I218V", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x15a2, "i218lm-3", "I218-LM", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x156f, "i219lm", "I219-LM", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x15b7, "i219lm", "I219-LM", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x15d7, "i219lm", "I219-LM", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x15e3, "i219lm", "I219-LM", 0 ),
|
|
- PCI_ROM ( 0x8086, 0x1570, "i219v", "I219V", 0 ),
|
|
+ PCI_ROM ( 0x8086, 0x153a, "i217lm", "I217-LM", INTEL_NO_PHY_RST ),
|
|
+ PCI_ROM ( 0x8086, 0x153b, "i217v", "I217-V", 0 ),
|
|
+ PCI_ROM ( 0x8086, 0x1559, "i218v", "I218-V", 0),
|
|
+ PCI_ROM ( 0x8086, 0x155a, "i218lm", "I218-LM", 0),
|
|
+ PCI_ROM ( 0x8086, 0x156f, "i219lm", "I219-LM", INTEL_I219 ),
|
|
+ PCI_ROM ( 0x8086, 0x1570, "i219v", "I219-V", INTEL_I219 ),
|
|
PCI_ROM ( 0x8086, 0x157b, "i210-2", "I210", 0 ),
|
|
+ PCI_ROM ( 0x8086, 0x15a0, "i218lm-2", "I218-LM", INTEL_NO_PHY_RST ),
|
|
+ PCI_ROM ( 0x8086, 0x15a1, "i218v-2", "I218-V", 0 ),
|
|
+ PCI_ROM ( 0x8086, 0x15a2, "i218lm-3", "I218-LM", INTEL_NO_PHY_RST ),
|
|
+ PCI_ROM ( 0x8086, 0x15a3, "i218v-3", "I218-V", INTEL_NO_PHY_RST ),
|
|
+ PCI_ROM ( 0x8086, 0x15b7, "i219lm-2", "I219-LM (2)", INTEL_I219 ),
|
|
+ PCI_ROM ( 0x8086, 0x15b8, "i219v-2", "I219-V (2)", INTEL_I219 ),
|
|
+ PCI_ROM ( 0x8086, 0x15b9, "i219lm-3", "I219-LM (3)", INTEL_I219 ),
|
|
+ PCI_ROM ( 0x8086, 0x15bb, "i219lm-7", "I219-LM (7)", INTEL_I219 ),
|
|
+ PCI_ROM ( 0x8086, 0x15bc, "i219v-7", "I219-V (7)", INTEL_I219 ),
|
|
+ PCI_ROM ( 0x8086, 0x15bd, "i219lm-6", "I219-LM (6)", INTEL_I219 ),
|
|
+ PCI_ROM ( 0x8086, 0x15be, "i219v-6", "I219-V (6)", INTEL_I219 ),
|
|
+ PCI_ROM ( 0x8086, 0x15d6, "i219v-5", "I219-V (5)", INTEL_I219 ),
|
|
+ PCI_ROM ( 0x8086, 0x15d7, "i219lm-4", "I219-LM (4)", INTEL_I219 ),
|
|
+ PCI_ROM ( 0x8086, 0x15d8, "i219v-4", "I219-V (4)", INTEL_I219 ),
|
|
+ PCI_ROM ( 0x8086, 0x15df, "i219lm-8", "I219-LM (8)", INTEL_I219 ),
|
|
+ PCI_ROM ( 0x8086, 0x15e0, "i219v-8", "I219-V (8)", INTEL_I219 ),
|
|
+ PCI_ROM ( 0x8086, 0x15e1, "i219lm-9", "I219-LM (9)", INTEL_I219 ),
|
|
+ PCI_ROM ( 0x8086, 0x15e2, "i219v-9", "I219-V (9)", INTEL_I219 ),
|
|
+ PCI_ROM ( 0x8086, 0x15e3, "i219lm-5", "I219-LM (5)", INTEL_I219 ),
|
|
+ PCI_ROM ( 0x8086, 0x1f41, "i354", "I354", INTEL_NO_ASDE ),
|
|
PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ),
|
|
PCI_ROM ( 0x8086, 0x2e6e, "cemedia", "CE Media Processor", 0 ),
|
|
};
|
|
diff --git a/src/drivers/net/intel.h b/src/drivers/net/intel.h
|
|
index 2a11e1df..088a9fec 100644
|
|
--- a/src/drivers/net/intel.h
|
|
+++ b/src/drivers/net/intel.h
|
|
@@ -22,33 +22,38 @@ struct intel_descriptor {
|
|
uint64_t address;
|
|
/** Length */
|
|
uint16_t length;
|
|
- /** Reserved */
|
|
- uint8_t reserved_a;
|
|
+ /** Flags */
|
|
+ uint8_t flags;
|
|
/** Command */
|
|
uint8_t command;
|
|
/** Status */
|
|
- uint8_t status;
|
|
- /** Errors */
|
|
- uint8_t errors;
|
|
- /** Reserved */
|
|
- uint16_t reserved_b;
|
|
+ uint32_t status;
|
|
} __attribute__ (( packed ));
|
|
|
|
-/** Packet descriptor command bits */
|
|
-enum intel_descriptor_command {
|
|
- /** Report status */
|
|
- INTEL_DESC_CMD_RS = 0x08,
|
|
- /** Insert frame checksum (CRC) */
|
|
- INTEL_DESC_CMD_IFCS = 0x02,
|
|
- /** End of packet */
|
|
- INTEL_DESC_CMD_EOP = 0x01,
|
|
-};
|
|
-
|
|
-/** Packet descriptor status bits */
|
|
-enum intel_descriptor_status {
|
|
- /** Descriptor done */
|
|
- INTEL_DESC_STATUS_DD = 0x01,
|
|
-};
|
|
+/** Descriptor type */
|
|
+#define INTEL_DESC_FL_DTYP( dtyp ) ( (dtyp) << 4 )
|
|
+#define INTEL_DESC_FL_DTYP_DATA INTEL_DESC_FL_DTYP ( 0x03 )
|
|
+
|
|
+/** Descriptor extension */
|
|
+#define INTEL_DESC_CMD_DEXT 0x20
|
|
+
|
|
+/** Report status */
|
|
+#define INTEL_DESC_CMD_RS 0x08
|
|
+
|
|
+/** Insert frame checksum (CRC) */
|
|
+#define INTEL_DESC_CMD_IFCS 0x02
|
|
+
|
|
+/** End of packet */
|
|
+#define INTEL_DESC_CMD_EOP 0x01
|
|
+
|
|
+/** Descriptor done */
|
|
+#define INTEL_DESC_STATUS_DD 0x00000001UL
|
|
+
|
|
+/** Receive error */
|
|
+#define INTEL_DESC_STATUS_RXE 0x00000100UL
|
|
+
|
|
+/** Payload length */
|
|
+#define INTEL_DESC_STATUS_PAYLEN( len ) ( (len) << 14 )
|
|
|
|
/** Device Control Register */
|
|
#define INTEL_CTRL 0x00000UL
|
|
@@ -91,9 +96,11 @@ enum intel_descriptor_status {
|
|
/** Interrupt Cause Read Register */
|
|
#define INTEL_ICR 0x000c0UL
|
|
#define INTEL_IRQ_TXDW 0x00000001UL /**< Transmit descriptor done */
|
|
+#define INTEL_IRQ_TXQE 0x00000002UL /**< Transmit queue empty */
|
|
#define INTEL_IRQ_LSC 0x00000004UL /**< Link status change */
|
|
+#define INTEL_IRQ_RXDMT0 0x00000010UL /**< Receive queue low */
|
|
+#define INTEL_IRQ_RXO 0x00000040UL /**< Receive overrun */
|
|
#define INTEL_IRQ_RXT0 0x00000080UL /**< Receive timer */
|
|
-#define INTEL_IRQ_RXO 0x00000400UL /**< Receive overrun */
|
|
|
|
/** Interrupt Mask Set/Read Register */
|
|
#define INTEL_IMS 0x000d0UL
|
|
@@ -156,6 +163,9 @@ enum intel_descriptor_status {
|
|
*/
|
|
#define INTEL_NUM_TX_DESC 256
|
|
|
|
+/** Transmit descriptor ring maximum fill level */
|
|
+#define INTEL_TX_FILL ( INTEL_NUM_TX_DESC - 1 )
|
|
+
|
|
/** Receive/Transmit Descriptor Base Address Low (offset) */
|
|
#define INTEL_xDBAL 0x00
|
|
|
|
@@ -175,6 +185,9 @@ enum intel_descriptor_status {
|
|
#define INTEL_xDCTL 0x28
|
|
#define INTEL_xDCTL_ENABLE 0x02000000UL /**< Queue enable */
|
|
|
|
+/** Maximum time to wait for queue disable, in milliseconds */
|
|
+#define INTEL_DISABLE_MAX_WAIT_MS 100
|
|
+
|
|
/** Receive Address Low */
|
|
#define INTEL_RAL0 0x05400UL
|
|
|
|
@@ -182,6 +195,10 @@ enum intel_descriptor_status {
|
|
#define INTEL_RAH0 0x05404UL
|
|
#define INTEL_RAH0_AV 0x80000000UL /**< Address valid */
|
|
|
|
+/** Future Extended NVM register 11 */
|
|
+#define INTEL_FEXTNVM11 0x05bbcUL
|
|
+#define INTEL_FEXTNVM11_WTF 0x00002000UL /**< Don't ask */
|
|
+
|
|
/** Receive address */
|
|
union intel_receive_address {
|
|
struct {
|
|
@@ -204,6 +221,15 @@ struct intel_ring {
|
|
unsigned int reg;
|
|
/** Length (in bytes) */
|
|
size_t len;
|
|
+
|
|
+ /** Populate descriptor
|
|
+ *
|
|
+ * @v desc Descriptor
|
|
+ * @v addr Data buffer address
|
|
+ * @v len Length of data
|
|
+ */
|
|
+ void ( * describe ) ( struct intel_descriptor *desc, physaddr_t addr,
|
|
+ size_t len );
|
|
};
|
|
|
|
/**
|
|
@@ -212,12 +238,39 @@ struct intel_ring {
|
|
* @v ring Descriptor ring
|
|
* @v count Number of descriptors
|
|
* @v reg Descriptor register block
|
|
+ * @v describe Method to populate descriptor
|
|
*/
|
|
static inline __attribute__ (( always_inline)) void
|
|
-intel_init_ring ( struct intel_ring *ring, unsigned int count,
|
|
- unsigned int reg ) {
|
|
+intel_init_ring ( struct intel_ring *ring, unsigned int count, unsigned int reg,
|
|
+ void ( * describe ) ( struct intel_descriptor *desc,
|
|
+ physaddr_t addr, size_t len ) ) {
|
|
+
|
|
ring->len = ( count * sizeof ( ring->desc[0] ) );
|
|
ring->reg = reg;
|
|
+ ring->describe = describe;
|
|
+}
|
|
+
|
|
+/** An Intel virtual function mailbox */
|
|
+struct intel_mailbox {
|
|
+ /** Mailbox control register */
|
|
+ unsigned int ctrl;
|
|
+ /** Mailbox memory base */
|
|
+ unsigned int mem;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * Initialise mailbox
|
|
+ *
|
|
+ * @v mbox Mailbox
|
|
+ * @v ctrl Mailbox control register
|
|
+ * @v mem Mailbox memory register base
|
|
+ */
|
|
+static inline __attribute__ (( always_inline )) void
|
|
+intel_init_mbox ( struct intel_mailbox *mbox, unsigned int ctrl,
|
|
+ unsigned int mem ) {
|
|
+
|
|
+ mbox->ctrl = ctrl;
|
|
+ mbox->mem = mem;
|
|
}
|
|
|
|
/** An Intel network card */
|
|
@@ -226,6 +279,10 @@ struct intel_nic {
|
|
void *regs;
|
|
/** Port number (for multi-port devices) */
|
|
unsigned int port;
|
|
+ /** Flags */
|
|
+ unsigned int flags;
|
|
+ /** Forced interrupts */
|
|
+ unsigned int force_icr;
|
|
|
|
/** EEPROM */
|
|
struct nvs_device eeprom;
|
|
@@ -234,6 +291,9 @@ struct intel_nic {
|
|
/** EEPROM address shift */
|
|
unsigned int eerd_addr_shift;
|
|
|
|
+ /** Mailbox */
|
|
+ struct intel_mailbox mbox;
|
|
+
|
|
/** Transmit descriptor ring */
|
|
struct intel_ring tx;
|
|
/** Receive descriptor ring */
|
|
@@ -242,6 +302,49 @@ struct intel_nic {
|
|
struct io_buffer *rx_iobuf[INTEL_NUM_RX_DESC];
|
|
};
|
|
|
|
+/** Driver flags */
|
|
+enum intel_flags {
|
|
+ /** PBS/PBA errata workaround required */
|
|
+ INTEL_PBS_ERRATA = 0x0001,
|
|
+ /** VMware missing interrupt workaround required */
|
|
+ INTEL_VMWARE = 0x0002,
|
|
+ /** PHY reset is broken */
|
|
+ INTEL_NO_PHY_RST = 0x0004,
|
|
+ /** ASDE is broken */
|
|
+ INTEL_NO_ASDE = 0x0008,
|
|
+ /** Reset may cause a complete device hang */
|
|
+ INTEL_RST_HANG = 0x0010,
|
|
+};
|
|
+
|
|
+/** The i219 has a seriously broken reset mechanism */
|
|
+#define INTEL_I219 ( INTEL_NO_PHY_RST | INTEL_RST_HANG )
|
|
+
|
|
+/**
|
|
+ * Dump diagnostic information
|
|
+ *
|
|
+ * @v intel Intel device
|
|
+ */
|
|
+static inline void intel_diag ( struct intel_nic *intel ) {
|
|
+
|
|
+ DBGC ( intel, "INTEL %p TX %04x(%02x)/%04x(%02x) "
|
|
+ "RX %04x(%02x)/%04x(%02x)\n", intel,
|
|
+ ( intel->tx.cons & 0xffff ),
|
|
+ readl ( intel->regs + intel->tx.reg + INTEL_xDH ),
|
|
+ ( intel->tx.prod & 0xffff ),
|
|
+ readl ( intel->regs + intel->tx.reg + INTEL_xDT ),
|
|
+ ( intel->rx.cons & 0xffff ),
|
|
+ readl ( intel->regs + intel->rx.reg + INTEL_xDH ),
|
|
+ ( intel->rx.prod & 0xffff ),
|
|
+ readl ( intel->regs + intel->rx.reg + INTEL_xDT ) );
|
|
+}
|
|
+
|
|
+extern void intel_describe_tx ( struct intel_descriptor *tx,
|
|
+ physaddr_t addr, size_t len );
|
|
+extern void intel_describe_tx_adv ( struct intel_descriptor *tx,
|
|
+ physaddr_t addr, size_t len );
|
|
+extern void intel_describe_rx ( struct intel_descriptor *rx,
|
|
+ physaddr_t addr, size_t len );
|
|
+extern void intel_reset_ring ( struct intel_nic *intel, unsigned int reg );
|
|
extern int intel_create_ring ( struct intel_nic *intel,
|
|
struct intel_ring *ring );
|
|
extern void intel_destroy_ring ( struct intel_nic *intel,
|