diff options
Diffstat (limited to 'sys/dev/em/if_em.c')
-rw-r--r-- | sys/dev/em/if_em.c | 535 |
1 files changed, 386 insertions, 149 deletions
diff --git a/sys/dev/em/if_em.c b/sys/dev/em/if_em.c index 1503ea2..7554120 100644 --- a/sys/dev/em/if_em.c +++ b/sys/dev/em/if_em.c @@ -1,6 +1,6 @@ /************************************************************************** -Copyright (c) 2001-2002, Intel Corporation +Copyright (c) 2001-2003, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -51,7 +51,7 @@ struct adapter *em_adapter_list = NULL; * Driver version *********************************************************************/ -char em_driver_version[] = "1.4.10"; +char em_driver_version[] = "1.5.31"; /********************************************************************* @@ -63,6 +63,7 @@ char em_driver_version[] = "1.4.10"; * * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, String Index } *********************************************************************/ + static em_vendor_info_t em_vendor_info_array[] = { /* Intel(R) PRO/1000 Network Connection */ @@ -78,10 +79,15 @@ static em_vendor_info_t em_vendor_info_array[] = { 0x8086, 0x1010, PCI_ANY_ID, PCI_ANY_ID, 0}, { 0x8086, 0x1011, PCI_ANY_ID, PCI_ANY_ID, 0}, { 0x8086, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, 0x1013, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, 0x1014, PCI_ANY_ID, PCI_ANY_ID, 0}, { 0x8086, 0x1015, PCI_ANY_ID, PCI_ANY_ID, 0}, { 0x8086, 0x1016, PCI_ANY_ID, PCI_ANY_ID, 0}, { 0x8086, 0x1017, PCI_ANY_ID, PCI_ANY_ID, 0}, - { 0x8086, 0x101E, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, 0x1018, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, 0x1019, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, 0x101A, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, 0x101E, PCI_ANY_ID, PCI_ANY_ID, 0}, /* required last entry */ { 0, 0, 0, 0, 0} }; @@ -103,7 +109,7 @@ static int em_detach(device_t); static int em_shutdown(device_t); static void em_intr(void *); static void em_start(struct ifnet *); -static int em_ioctl(struct ifnet *, IOCTL_CMD_TYPE, caddr_t); +static int em_ioctl(struct ifnet *, u_long, caddr_t); static void em_watchdog(struct ifnet *); static void em_init(void *); static void em_stop(void *); @@ -129,7 +135,7 @@ static int em_allocate_receive_structures(struct adapter *); static int em_allocate_transmit_structures(struct adapter *); static void em_process_receive_interrupts(struct adapter *, int); static void em_receive_checksum(struct adapter *, - struct em_rx_desc * rx_desc, + struct em_rx_desc *, struct mbuf *); static void em_transmit_checksum_setup(struct adapter *, struct mbuf *, @@ -142,8 +148,13 @@ static void em_print_hw_stats(struct adapter *); static void em_print_link_status(struct adapter *); static int em_get_buf(int i, struct adapter *, struct mbuf *); -static void em_enable_vlans(struct adapter *adapter); -static int em_encap(struct adapter *adapter, struct mbuf *m_head); +static void em_enable_vlans(struct adapter *); +static int em_encap(struct adapter *, struct mbuf *); +static void em_smartspeed(struct adapter *); +static int em_82547_fifo_workaround(struct adapter *, int); +static void em_82547_update_fifo_head(struct adapter *, int); +static int em_82547_tx_fifo_reset(struct adapter *); +static void em_82547_move_tail(void *arg); /********************************************************************* * FreeBSD Device Interface Entry Points @@ -254,6 +265,7 @@ em_attach(device_t dev) em_adapter_list = adapter; callout_handle_init(&adapter->timer_handle); + callout_handle_init(&adapter->tx_fifo_timer_handle); /* Determine hardware revision */ em_identify_hardware(adapter); @@ -271,30 +283,33 @@ em_attach(device_t dev) adapter->hw.tbi_compatibility_en = TRUE; adapter->rx_buffer_len = EM_RXBUFFER_2048; + /* These parameters control the automatic generation(Tx) and + * response(Rx) to Ethernet PAUSE frames. + */ adapter->hw.fc_high_water = FC_DEFAULT_HI_THRESH; adapter->hw.fc_low_water = FC_DEFAULT_LO_THRESH; adapter->hw.fc_pause_time = FC_DEFAULT_TX_TIMER; adapter->hw.fc_send_xon = TRUE; adapter->hw.fc = em_fc_full; + adapter->hw.phy_init_script = 1; - /* Set the max frame size assuming standard ethernet sized frames */ + /* + * Set the max frame size assuming standard ethernet + * sized frames + */ adapter->hw.max_frame_size = ETHERMTU + ETHER_HDR_LEN + ETHER_CRC_LEN; adapter->hw.min_frame_size = MINIMUM_ETHERNET_PACKET_SIZE + ETHER_CRC_LEN; - /* This controls when hardware reports transmit completion status. */ - if ((EM_REPORT_TX_EARLY == 0) || (EM_REPORT_TX_EARLY == 1)) { - adapter->hw.report_tx_early = EM_REPORT_TX_EARLY; - } else { - if (adapter->hw.mac_type < em_82543) { - adapter->hw.report_tx_early = 0; - } else { - adapter->hw.report_tx_early = 1; - } - } + /* + * This controls when hardware reports transmit completion + * status. + */ + adapter->hw.report_tx_early = 1; + if (em_allocate_pci_resources(adapter)) { printf("em%d: Allocation of PCI resources failed\n", @@ -305,6 +320,9 @@ em_attach(device_t dev) } + /* Initialize eeprom parameters */ + em_init_eeprom_params(&adapter->hw); + tsize = EM_ROUNDUP(adapter->num_tx_desc * sizeof(struct em_tx_desc), 4096); @@ -347,11 +365,15 @@ em_attach(device_t dev) if (em_read_mac_addr(&adapter->hw) < 0) { printf("em%d: EEPROM read error while reading mac address\n", adapter->unit); + em_free_pci_resources(adapter); + contigfree(adapter->tx_desc_base, tsize, M_DEVBUF); + contigfree(adapter->rx_desc_base, rsize, M_DEVBUF); + splx(s); return(EIO); } - memcpy(adapter->interface_data.ac_enaddr, adapter->hw.mac_addr, - ETH_LENGTH_OF_ADDRESS); + bcopy(adapter->hw.mac_addr, adapter->interface_data.ac_enaddr, + ETHER_ADDR_LEN); /* Setup OS specific network interface */ em_setup_interface(dev, adapter); @@ -373,7 +395,6 @@ em_attach(device_t dev) } else printf("em%d: Speed:N/A Duplex:N/A\n", adapter->unit); - INIT_DEBUGOUT("em_attach: end"); splx(s); return(0); @@ -403,9 +424,9 @@ em_detach(device_t dev) em_stop(adapter); em_phy_hw_reset(&adapter->hw); #if __FreeBSD_version < 500000 - ether_ifdetach(&adapter->interface_data.ac_if, ETHER_BPF_SUPPORTED); + ether_ifdetach(&adapter->interface_data.ac_if, ETHER_BPF_SUPPORTED); #else - ether_ifdetach(&adapter->interface_data.ac_if); + ether_ifdetach(&adapter->interface_data.ac_if); #endif em_free_pci_resources(adapter); @@ -470,10 +491,10 @@ em_shutdown(device_t dev) static void em_start(struct ifnet *ifp) { - int s; + int s; struct mbuf *m_head; struct adapter *adapter = ifp->if_softc; - + if (!adapter->link_active) return; @@ -484,14 +505,13 @@ em_start(struct ifnet *ifp) if (m_head == NULL) break; - if (em_encap(adapter, m_head)) { - ifp->if_flags |= IFF_OACTIVE; - IF_PREPEND(&ifp->if_snd, m_head); - break; + if (em_encap(adapter, m_head)) { + ifp->if_flags |= IFF_OACTIVE; + IF_PREPEND(&ifp->if_snd, m_head); + break; } - - /* Send a copy of the frame to the BPF listener */ + /* Send a copy of the frame to the BPF listener */ #if __FreeBSD_version < 500000 if (ifp->if_bpf) bpf_mtap(ifp, m_head); @@ -517,7 +537,7 @@ em_start(struct ifnet *ifp) **********************************************************************/ static int -em_ioctl(struct ifnet *ifp, IOCTL_CMD_TYPE command, caddr_t data) +em_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { int s, mask, error = 0; struct ifreq *ifr = (struct ifreq *) data; @@ -541,20 +561,20 @@ em_ioctl(struct ifnet *ifp, IOCTL_CMD_TYPE command, caddr_t data) em_init(adapter); } break; - case SIOCSIFFLAGS: - IOCTL_DEBUGOUT("ioctl rcv'd: SIOCSIFFLAGS (Set Interface Flags)"); - if (ifp->if_flags & IFF_UP) { - if (!(ifp->if_flags & IFF_RUNNING)) - em_init(adapter); - - em_disable_promisc(adapter); - em_set_promisc(adapter); - } else { - if (ifp->if_flags & IFF_RUNNING) { - em_stop(adapter); - } - } - break; + case SIOCSIFFLAGS: + IOCTL_DEBUGOUT("ioctl rcv'd: SIOCSIFFLAGS (Set Interface Flags)"); + if (ifp->if_flags & IFF_UP) { + if (!(ifp->if_flags & IFF_RUNNING)) + em_init(adapter); + + em_disable_promisc(adapter); + em_set_promisc(adapter); + } else { + if (ifp->if_flags & IFF_RUNNING) { + em_stop(adapter); + } + } + break; case SIOCADDMULTI: case SIOCDELMULTI: IOCTL_DEBUGOUT("ioctl rcv'd: SIOC(ADD|DEL)MULTI"); @@ -703,16 +723,17 @@ em_init(void *arg) em_disable_intr(adapter); else #endif /* DEVICE_POLLING */ - em_enable_intr(adapter); + em_enable_intr(adapter); splx(s); return; } + #ifdef DEVICE_POLLING static poll_handler_t em_poll; - -static void + +static void em_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) { struct adapter *adapter = ifp->if_softc; @@ -736,27 +757,26 @@ em_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) em_process_receive_interrupts(adapter, count); em_clean_transmit_interrupts(adapter); } + if (ifp->if_flags & IFF_RUNNING && ifp->if_snd.ifq_head != NULL) em_start(ifp); } #endif /* DEVICE_POLLING */ - /********************************************************************* * - * Interrupt Service routine + * Interrupt Service routine * **********************************************************************/ - static void em_intr(void *arg) { - u_int32_t loop_cnt = EM_MAX_INTR; - u_int32_t reg_icr; - struct ifnet *ifp; - struct adapter *adapter = arg; + u_int32_t loop_cnt = EM_MAX_INTR; + u_int32_t reg_icr; + struct ifnet *ifp; + struct adapter *adapter = arg; - ifp = &adapter->interface_data.ac_if; + ifp = &adapter->interface_data.ac_if; #ifdef DEVICE_POLLING if (ifp->if_ipending & IFF_POLLING) @@ -770,37 +790,38 @@ em_intr(void *arg) #endif /* DEVICE_POLLING */ - em_disable_intr(adapter); - while (loop_cnt > 0 && - (reg_icr = E1000_READ_REG(&adapter->hw, ICR)) != 0) { - - /* Link status change */ - if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) { - untimeout(em_local_timer, adapter, - adapter->timer_handle); - adapter->hw.get_link_status = 1; - em_check_for_link(&adapter->hw); - em_print_link_status(adapter); - adapter->timer_handle = - timeout(em_local_timer, adapter, 2*hz); - } + em_disable_intr(adapter); + while (loop_cnt > 0 && + (reg_icr = E1000_READ_REG(&adapter->hw, ICR)) != 0) { - if (ifp->if_flags & IFF_RUNNING) { - em_process_receive_interrupts(adapter, -1); - em_clean_transmit_interrupts(adapter); - } - loop_cnt--; - } + /* Link status change */ + if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) { + untimeout(em_local_timer, adapter, + adapter->timer_handle); + adapter->hw.get_link_status = 1; + em_check_for_link(&adapter->hw); + em_print_link_status(adapter); + adapter->timer_handle = + timeout(em_local_timer, adapter, 2*hz); + } - em_enable_intr(adapter); + if (ifp->if_flags & IFF_RUNNING) { + em_process_receive_interrupts(adapter, -1); + em_clean_transmit_interrupts(adapter); + } + loop_cnt--; + } - if (ifp->if_flags & IFF_RUNNING && ifp->if_snd.ifq_head != NULL) - em_start(ifp); + em_enable_intr(adapter); - return; + if (ifp->if_flags & IFF_RUNNING && ifp->if_snd.ifq_head != NULL) + em_start(ifp); + + return; } + /********************************************************************* * * Media Ioctl callback @@ -924,6 +945,10 @@ em_media_change(struct ifnet *ifp) return(0); } +#define EM_FIFO_HDR 0x10 +#define EM_82547_PKT_THRESH 0x3e0 +#define EM_82547_TX_FIFO_SIZE 0x2800 +#define EM_82547_TX_FIFO_BEGIN 0xf00 /********************************************************************* * * This routine maps the mbufs to tx descriptors. @@ -939,17 +964,20 @@ em_encap(struct adapter *adapter, struct mbuf *m_head) u_int32_t txd_lower; int txd_used, i, txd_saved; struct mbuf *mp; + #if __FreeBSD_version < 500000 struct ifvlan *ifv = NULL; #else - struct m_tag *mtag; + struct m_tag *mtag; #endif - struct em_buffer *tx_buffer = NULL; struct em_tx_desc *current_tx_desc = NULL; struct ifnet *ifp = &adapter->interface_data.ac_if; - /* Force a cleanup if number of TX descriptors available hits the threshold */ + /* + * Force a cleanup if number of TX descriptors + * available hits the threshold + */ if (adapter->num_tx_desc_avail <= EM_TX_CLEANUP_THRESHOLD) em_clean_transmit_interrupts(adapter); @@ -965,12 +993,13 @@ em_encap(struct adapter *adapter, struct mbuf *m_head) else txd_upper = txd_lower = 0; - /* Find out if we are in vlan mode */ + + /* Find out if we are in vlan mode */ #if __FreeBSD_version < 500000 - if ((m_head->m_flags & (M_PROTO1|M_PKTHDR)) == (M_PROTO1|M_PKTHDR) && - m_head->m_pkthdr.rcvif != NULL && - m_head->m_pkthdr.rcvif->if_type == IFT_L2VLAN) - ifv = m_head->m_pkthdr.rcvif->if_softc; + if ((m_head->m_flags & (M_PROTO1|M_PKTHDR)) == (M_PROTO1|M_PKTHDR) && + m_head->m_pkthdr.rcvif != NULL && + m_head->m_pkthdr.rcvif->if_type == IFT_L2VLAN) + ifv = m_head->m_pkthdr.rcvif->if_softc; #else mtag = VLAN_OUTPUT_TAG(ifp, m_head); #endif @@ -1008,17 +1037,17 @@ em_encap(struct adapter *adapter, struct mbuf *m_head) adapter->next_avail_tx_desc = i; #if __FreeBSD_version < 500000 - if (ifv != NULL) { - /* Set the vlan id */ - current_tx_desc->upper.fields.special = ifv->ifv_tag; + if (ifv != NULL) { + /* Set the vlan id */ + current_tx_desc->upper.fields.special = ifv->ifv_tag; #else - if (mtag != NULL) { - /* Set the vlan id */ - current_tx_desc->upper.fields.special = VLAN_TAG_VALUE(mtag); + if (mtag != NULL) { + /* Set the vlan id */ + current_tx_desc->upper.fields.special = VLAN_TAG_VALUE(mtag); #endif - /* Tell hardware to add tag */ - current_tx_desc->lower.data |= E1000_TXD_CMD_VLE; - } + /* Tell hardware to add tag */ + current_tx_desc->lower.data |= E1000_TXD_CMD_VLE; + } tx_buffer->m_head = m_head; @@ -1031,11 +1060,146 @@ em_encap(struct adapter *adapter, struct mbuf *m_head) * Advance the Transmit Descriptor Tail (Tdt), this tells the E1000 * that this frame is available to transmit. */ - E1000_WRITE_REG(&adapter->hw, TDT, i); + if (adapter->hw.mac_type == em_82547 && + adapter->link_duplex == HALF_DUPLEX) { + em_82547_move_tail(adapter); + } + else { + E1000_WRITE_REG(&adapter->hw, TDT, i); + if (adapter->hw.mac_type == em_82547) { + em_82547_update_fifo_head(adapter, m_head->m_pkthdr.len); + } + } return (0); } + +/********************************************************************* + * + * 82547 workaround to avoid controller hang in half-duplex environment. + * The workaround is to avoid queuing a large packet that would span + * the internal Tx FIFO ring boundary. We need to reset the FIFO pointers + * in this case. We do that only when FIFO is queiced. + * + **********************************************************************/ +static void +em_82547_move_tail(void *arg) +{ + int s; + struct adapter *adapter = arg; + uint16_t hw_tdt; + uint16_t sw_tdt; + struct em_tx_desc *tx_desc; + uint16_t length = 0; + boolean_t eop = 0; + + s = splimp(); + hw_tdt = E1000_READ_REG(&adapter->hw, TDT); + sw_tdt = adapter->next_avail_tx_desc; + + while (hw_tdt != sw_tdt) { + tx_desc = &adapter->tx_desc_base[hw_tdt]; + length += tx_desc->lower.flags.length; + eop = tx_desc->lower.data & E1000_TXD_CMD_EOP; + if(++hw_tdt == adapter->num_tx_desc) + hw_tdt = 0; + + if(eop) { + if (em_82547_fifo_workaround(adapter, length)) { + adapter->tx_fifo_wrk++; + adapter->tx_fifo_timer_handle = + timeout(em_82547_move_tail, + adapter, 1); + splx(s); + return; + } + else { + E1000_WRITE_REG(&adapter->hw, TDT, hw_tdt); + em_82547_update_fifo_head(adapter, length); + length = 0; + } + } + } + splx(s); + return; +} + +static int +em_82547_fifo_workaround(struct adapter *adapter, int len) +{ + int fifo_space, fifo_pkt_len; + + fifo_pkt_len = EM_ROUNDUP(len + EM_FIFO_HDR, EM_FIFO_HDR); + + if (adapter->link_duplex == HALF_DUPLEX) { + fifo_space = EM_82547_TX_FIFO_SIZE - adapter->tx_fifo_head; + + if (fifo_pkt_len >= (EM_82547_PKT_THRESH + fifo_space)) { + if (em_82547_tx_fifo_reset(adapter)) { + return(0); + } + else { + return(1); + } + } + } + + return(0); +} + +static void +em_82547_update_fifo_head(struct adapter *adapter, int len) +{ + int fifo_pkt_len = EM_ROUNDUP(len + EM_FIFO_HDR, EM_FIFO_HDR); + + /* tx_fifo_head is always 16 byte aligned */ + adapter->tx_fifo_head += fifo_pkt_len; + if (adapter->tx_fifo_head >= EM_82547_TX_FIFO_SIZE) { + adapter->tx_fifo_head -= EM_82547_TX_FIFO_SIZE; + } + + return; +} + + +static int +em_82547_tx_fifo_reset(struct adapter *adapter) +{ + uint32_t tctl; + + if ( (E1000_READ_REG(&adapter->hw, TDT) == + E1000_READ_REG(&adapter->hw, TDH)) && + (E1000_READ_REG(&adapter->hw, TDFT) == + E1000_READ_REG(&adapter->hw, TDFH)) && + (E1000_READ_REG(&adapter->hw, TDFTS) == + E1000_READ_REG(&adapter->hw, TDFHS)) && + (E1000_READ_REG(&adapter->hw, TDFPC) == 0)) { + + /* Disable TX unit */ + tctl = E1000_READ_REG(&adapter->hw, TCTL); + E1000_WRITE_REG(&adapter->hw, TCTL, tctl & ~E1000_TCTL_EN); + + /* Reset FIFO pointers */ + E1000_WRITE_REG(&adapter->hw, TDFT, EM_82547_TX_FIFO_BEGIN); + E1000_WRITE_REG(&adapter->hw, TDFH, EM_82547_TX_FIFO_BEGIN); + E1000_WRITE_REG(&adapter->hw, TDFTS, EM_82547_TX_FIFO_BEGIN); + E1000_WRITE_REG(&adapter->hw, TDFHS, EM_82547_TX_FIFO_BEGIN); + + /* Re-enable TX unit */ + E1000_WRITE_REG(&adapter->hw, TCTL, tctl); + E1000_WRITE_FLUSH(&adapter->hw); + + adapter->tx_fifo_head = 0; + adapter->tx_fifo_reset++; + + return(TRUE); + } + else { + return(FALSE); + } +} + static void em_set_promisc(struct adapter * adapter) { @@ -1108,12 +1272,14 @@ em_set_multi(struct adapter * adapter) if (ifma->ifma_addr->sa_family != AF_LINK) continue; + if (mcnt == MAX_NUM_MULTICAST_ADDRESSES) break; + bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), &mta[mcnt*ETH_LENGTH_OF_ADDRESS], ETH_LENGTH_OF_ADDRESS); mcnt++; } - if (mcnt > MAX_NUM_MULTICAST_ADDRESSES) { + if (mcnt >= MAX_NUM_MULTICAST_ADDRESSES) { reg_rctl = E1000_READ_REG(&adapter->hw, RCTL); reg_rctl |= E1000_RCTL_MPE; E1000_WRITE_REG(&adapter->hw, RCTL, reg_rctl); @@ -1157,6 +1323,8 @@ em_local_timer(void *arg) if (em_display_debug_stats && ifp->if_flags & IFF_RUNNING) { em_print_hw_stats(adapter); } + em_smartspeed(adapter); + adapter->timer_handle = timeout(em_local_timer, adapter, 2*hz); splx(s); @@ -1177,6 +1345,7 @@ em_print_link_status(struct adapter * adapter) ((adapter->link_duplex == FULL_DUPLEX) ? "Full Duplex" : "Half Duplex")); adapter->link_active = 1; + adapter->smartspeed = 0; } } else { if (adapter->link_active == 1) { @@ -1190,8 +1359,6 @@ em_print_link_status(struct adapter * adapter) return; } - - /********************************************************************* * * This routine disables all traffic on the adapter by issuing a @@ -1209,7 +1376,9 @@ em_stop(void *arg) INIT_DEBUGOUT("em_stop: begin\n"); em_disable_intr(adapter); em_reset_hw(&adapter->hw); - untimeout(em_local_timer, adapter, adapter->timer_handle); + untimeout(em_local_timer, adapter, adapter->timer_handle); + untimeout(em_82547_move_tail, adapter, + adapter->tx_fifo_timer_handle); em_free_transmit_structures(adapter); em_free_receive_structures(adapter); @@ -1362,6 +1531,9 @@ em_hardware_init(struct adapter * adapter) /* Issue a global reset */ em_reset_hw(&adapter->hw); + /* When hardware is reset, fifo_head is also reset */ + adapter->tx_fifo_head = 0; + /* Make sure we have a good EEPROM before we read from it */ if (em_validate_eeprom_checksum(&adapter->hw) < 0) { printf("em%d: The EEPROM Checksum Is Not Valid\n", @@ -1424,10 +1596,10 @@ em_setup_interface(device_t dev, struct adapter * adapter) ifp->if_watchdog = em_watchdog; ifp->if_snd.ifq_maxlen = adapter->num_tx_desc - 1; -#if __FreeBSD_version < 500000 - ether_ifattach(ifp, ETHER_BPF_SUPPORTED); +#if __FreeBSD_version < 500000 + ether_ifattach(ifp, ETHER_BPF_SUPPORTED); #else - ether_ifattach(ifp, adapter->interface_data.ac_enaddr); + ether_ifattach(ifp, adapter->interface_data.ac_enaddr); #endif if (adapter->hw.mac_type >= em_82543) { @@ -1435,10 +1607,11 @@ em_setup_interface(device_t dev, struct adapter * adapter) ifp->if_capenable = ifp->if_capabilities; } -#if __FreeBSD_version >= 500000 - ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU; +#if __FreeBSD_version >= 500000 + ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU; #endif + /* * Specify the media types supported by this adapter and register * callbacks to update media and link information @@ -1477,6 +1650,67 @@ em_setup_interface(device_t dev, struct adapter * adapter) /********************************************************************* * + * Workaround for SmartSpeed on 82541 and 82547 controllers + * + **********************************************************************/ +static void +em_smartspeed(struct adapter *adapter) +{ + uint16_t phy_tmp; + + if(adapter->link_active || (adapter->hw.phy_type != em_phy_igp) || + !adapter->hw.autoneg || !(adapter->hw.autoneg_advertised & ADVERTISE_1000_FULL)) + return; + + if(adapter->smartspeed == 0) { + /* If Master/Slave config fault is asserted twice, + * we assume back-to-back */ + em_read_phy_reg(&adapter->hw, PHY_1000T_STATUS, &phy_tmp); + if(!(phy_tmp & SR_1000T_MS_CONFIG_FAULT)) return; + em_read_phy_reg(&adapter->hw, PHY_1000T_STATUS, &phy_tmp); + if(phy_tmp & SR_1000T_MS_CONFIG_FAULT) { + em_read_phy_reg(&adapter->hw, PHY_1000T_CTRL, + &phy_tmp); + if(phy_tmp & CR_1000T_MS_ENABLE) { + phy_tmp &= ~CR_1000T_MS_ENABLE; + em_write_phy_reg(&adapter->hw, + PHY_1000T_CTRL, phy_tmp); + adapter->smartspeed++; + if(adapter->hw.autoneg && + !em_phy_setup_autoneg(&adapter->hw) && + !em_read_phy_reg(&adapter->hw, PHY_CTRL, + &phy_tmp)) { + phy_tmp |= (MII_CR_AUTO_NEG_EN | + MII_CR_RESTART_AUTO_NEG); + em_write_phy_reg(&adapter->hw, + PHY_CTRL, phy_tmp); + } + } + } + return; + } else if(adapter->smartspeed == EM_SMARTSPEED_DOWNSHIFT) { + /* If still no link, perhaps using 2/3 pair cable */ + em_read_phy_reg(&adapter->hw, PHY_1000T_CTRL, &phy_tmp); + phy_tmp |= CR_1000T_MS_ENABLE; + em_write_phy_reg(&adapter->hw, PHY_1000T_CTRL, phy_tmp); + if(adapter->hw.autoneg && + !em_phy_setup_autoneg(&adapter->hw) && + !em_read_phy_reg(&adapter->hw, PHY_CTRL, &phy_tmp)) { + phy_tmp |= (MII_CR_AUTO_NEG_EN | + MII_CR_RESTART_AUTO_NEG); + em_write_phy_reg(&adapter->hw, PHY_CTRL, phy_tmp); + } + } + /* Restart process after EM_SMARTSPEED_MAX iterations */ + if(adapter->smartspeed++ == EM_SMARTSPEED_MAX) + adapter->smartspeed = 0; + + return; +} + + +/********************************************************************* + * * Allocate memory for tx_buffer structures. The tx_buffer stores all * the information needed to transmit a packet on the wire. * @@ -1561,6 +1795,8 @@ em_initialize_transmit_unit(struct adapter * adapter) case em_82540: case em_82545: case em_82546: + case em_82541: + case em_82547: if (adapter->hw.media_type == em_media_type_fiber) reg_tipg = DEFAULT_82543_TIPG_IPGT_FIBER; else @@ -1593,16 +1829,11 @@ em_initialize_transmit_unit(struct adapter * adapter) E1000_WRITE_REG(&adapter->hw, TCTL, reg_tctl); /* Setup Transmit Descriptor Settings for this adapter */ - adapter->txd_cmd = E1000_TXD_CMD_IFCS; + adapter->txd_cmd = E1000_TXD_CMD_IFCS | E1000_TXD_CMD_RS; if (adapter->tx_int_delay > 0) adapter->txd_cmd |= E1000_TXD_CMD_IDE; - if (adapter->hw.report_tx_early == 1) - adapter->txd_cmd |= E1000_TXD_CMD_RS; - else - adapter->txd_cmd |= E1000_TXD_CMD_RPS; - return; } @@ -2013,12 +2244,12 @@ em_process_receive_interrupts(struct adapter * adapter, int count) { struct ifnet *ifp; struct mbuf *mp; -#if __FreeBSD_version < 500000 - struct ether_header *eh; +#if __FreeBSD_version < 500000 + struct ether_header *eh; #endif u_int8_t accept_frame = 0; - u_int8_t eop = 0; - u_int16_t len; + u_int8_t eop = 0; + u_int16_t len; int i; /* Pointer to the receive descriptor being examined. */ @@ -2036,6 +2267,7 @@ em_process_receive_interrupts(struct adapter * adapter, int count) } while ((current_desc->status & E1000_RXD_STAT_DD) && (count != 0)) { + mp = adapter->rx_buffer_area[i].m_head; accept_frame = 1; @@ -2099,37 +2331,37 @@ em_process_receive_interrupts(struct adapter * adapter, int count) adapter->fmp->m_pkthdr.len += len; } - if (eop) { - adapter->fmp->m_pkthdr.rcvif = ifp; + if (eop) { + adapter->fmp->m_pkthdr.rcvif = ifp; -#if __FreeBSD_version < 500000 - eh = mtod(adapter->fmp, struct ether_header *); - /* Remove ethernet header from mbuf */ - m_adj(adapter->fmp, sizeof(struct ether_header)); - em_receive_checksum(adapter, current_desc, - adapter->fmp); - if (current_desc->status & E1000_RXD_STAT_VP) - VLAN_INPUT_TAG(eh, adapter->fmp, - (current_desc->special & +#if __FreeBSD_version < 500000 + eh = mtod(adapter->fmp, struct ether_header *); + /* Remove ethernet header from mbuf */ + m_adj(adapter->fmp, sizeof(struct ether_header)); + em_receive_checksum(adapter, current_desc, + adapter->fmp); + if (current_desc->status & E1000_RXD_STAT_VP) + VLAN_INPUT_TAG(eh, adapter->fmp, + (current_desc->special & E1000_RXD_SPC_VLAN_MASK)); - else - ether_input(ifp, eh, adapter->fmp); + else + ether_input(ifp, eh, adapter->fmp); #else - em_receive_checksum(adapter, current_desc, - adapter->fmp); - if (current_desc->status & E1000_RXD_STAT_VP) - VLAN_INPUT_TAG(ifp, adapter->fmp, - (current_desc->special & - E1000_RXD_SPC_VLAN_MASK), + em_receive_checksum(adapter, current_desc, + adapter->fmp); + if (current_desc->status & E1000_RXD_STAT_VP) + VLAN_INPUT_TAG(ifp, adapter->fmp, + (current_desc->special & + E1000_RXD_SPC_VLAN_MASK), adapter->fmp = NULL); - - if (adapter->fmp != NULL) - (*ifp->if_input)(ifp, adapter->fmp); + + if (adapter->fmp != NULL) + (*ifp->if_input)(ifp, adapter->fmp); #endif - adapter->fmp = NULL; - adapter->lmp = NULL; - } + adapter->fmp = NULL; + adapter->lmp = NULL; + } } else { adapter->dropped_pkts++; em_get_buf(i, adapter, mp); @@ -2206,7 +2438,7 @@ em_enable_vlans(struct adapter *adapter) { uint32_t ctrl; - E1000_WRITE_REG(&adapter->hw, VET, QTAG_TYPE); + E1000_WRITE_REG(&adapter->hw, VET, ETHERTYPE_VLAN); ctrl = E1000_READ_REG(&adapter->hw, CTRL); ctrl |= E1000_CTRL_VME; @@ -2277,7 +2509,7 @@ em_io_write(struct em_hw *hw, uint32_t port, uint32_t value) { outl(port, value); return; -} +} /********************************************************************** * @@ -2294,6 +2526,7 @@ em_update_stats_counters(struct adapter *adapter) adapter->stats.mpc += E1000_READ_REG(&adapter->hw, MPC); adapter->stats.scc += E1000_READ_REG(&adapter->hw, SCC); adapter->stats.ecol += E1000_READ_REG(&adapter->hw, ECOL); + adapter->stats.mcc += E1000_READ_REG(&adapter->hw, MCC); adapter->stats.latecol += E1000_READ_REG(&adapter->hw, LATECOL); adapter->stats.colc += E1000_READ_REG(&adapter->hw, COLC); @@ -2404,14 +2637,18 @@ em_print_hw_stats(struct adapter *adapter) adapter->clean_tx_interrupts); #endif + printf("em%d: fifo workaround = %lld, fifo_reset = %lld\n", unit, + (long long)adapter->tx_fifo_wrk, + (long long)adapter->tx_fifo_reset); + printf("em%d: hw tdh = %d, hw tdt = %d\n", unit, + E1000_READ_REG(&adapter->hw, TDH), + E1000_READ_REG(&adapter->hw, TDT)); + printf("em%d: Excessive collisions = %lld\n", unit, + (long long)adapter->stats.ecol); printf("em%d: Tx Descriptors not avail1 = %ld\n", unit, adapter->no_tx_desc_avail1); printf("em%d: Tx Descriptors not avail2 = %ld\n", unit, adapter->no_tx_desc_avail2); - printf("em%d: Std Mbuf Failed = %ld\n",unit, - adapter->mbuf_alloc_failed); - printf("em%d: Std Cluster Failed = %ld\n",unit, - adapter->mbuf_cluster_failed); printf("em%d: Symbol errors = %lld\n", unit, (long long)adapter->stats.symerrs); |