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,