/** <at> file * Support for PCIe Marvell Yukon gigabit ethernet adapter product family * * Copyright (c) 2011-2016, ARM Limited. All rights reserved. * * This program and the accompanying materials * are licensed and made available under the terms and conditions of the BSD License * which accompanies this distribution. The full text of the license may be found at * http://opensource.org/licenses/bsd-license.php * * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. * **/ /****************************************************************************** * * LICENSE: * Copyright (C) Marvell International Ltd. and/or its affiliates * * The computer program files contained in this folder ("Files") * are provided to you under the BSD-type license terms provided * below, and any use of such Files and any derivative works * thereof created by you shall be governed by the following terms * and conditions: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * - Neither the name of Marvell nor the names of its contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * /LICENSE * *****************************************************************************/ /*- * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul <wpaul <at> ctr.columbia.edu>. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ /*- * Copyright (c) 2003 Nathan L. Binkert <binkertn <at> umich.edu> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Device driver for the Marvell Yukon II Ethernet controller. * Due to lack of documentation, this driver is based on the code from * sk (4) and Marvell's myk (4) driver for FreeBSD 5.x. */ #include <Base.h> #include <Library/IoLib.h> #include <Library/MemoryAllocationLib.h> #include <Library/BaseMemoryLib.h> #include <Library/DebugLib.h> #include <Library/NetLib.h> #include <Library/PcdLib.h> #include <Library/BaseLib.h> #include <Library/TimerLib.h> #include <Library/UefiBootServicesTableLib.h> #include <Protocol/PciIo.h> #include <IndustryStandard/Pci.h> #include <IndustryStandard/Acpi.h> #include "miivar.h" #include "if_media.h" #include "if_mskreg.h" #include "if_msk.h" #define MSK_CSUM_FEATURES (CSUM_TCP | CSUM_UDP) /* * Devices supported by this driver. */ static struct msk_product { UINT16 msk_vendorid; UINT16 msk_deviceid; const CHAR8 *msk_name; } msk_products[] = { { VENDORID_SK, DEVICEID_SK_YUKON2, "SK-9Sxx Gigabit Ethernet" }, { VENDORID_SK, DEVICEID_SK_YUKON2_EXPR, "SK-9Exx Gigabit Ethernet"}, { VENDORID_MARVELL, DEVICEID_MRVL_8021CU, "Marvell Yukon 88E8021CU Gigabit Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_8021X, "Marvell Yukon 88E8021 SX/LX Gigabit Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_8022CU, "Marvell Yukon 88E8022CU Gigabit Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_8022X, "Marvell Yukon 88E8022 SX/LX Gigabit Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_8061CU, "Marvell Yukon 88E8061CU Gigabit Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_8061X, "Marvell Yukon 88E8061 SX/LX Gigabit Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_8062CU, "Marvell Yukon 88E8062CU Gigabit Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_8062X, "Marvell Yukon 88E8062 SX/LX Gigabit Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_8035, "Marvell Yukon 88E8035 Fast Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_8036, "Marvell Yukon 88E8036 Fast Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_8038, "Marvell Yukon 88E8038 Fast Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_8039, "Marvell Yukon 88E8039 Fast Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_8040, "Marvell Yukon 88E8040 Fast Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_8040T, "Marvell Yukon 88E8040T Fast Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_8042, "Marvell Yukon 88E8042 Fast Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_8048, "Marvell Yukon 88E8048 Fast Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_4361, "Marvell Yukon 88E8050 Gigabit Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_4360, "Marvell Yukon 88E8052 Gigabit Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_4362, "Marvell Yukon 88E8053 Gigabit Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_4363, "Marvell Yukon 88E8055 Gigabit Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_4364, "Marvell Yukon 88E8056 Gigabit Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_4365, "Marvell Yukon 88E8070 Gigabit Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_436A, "Marvell Yukon 88E8058 Gigabit Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_436B, "Marvell Yukon 88E8071 Gigabit Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_436C, "Marvell Yukon 88E8072 Gigabit Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_4380, "Marvell Yukon 88E8057 Gigabit Ethernet" }, { VENDORID_MARVELL, DEVICEID_MRVL_4381, "Marvell Yukon 88E8059 Gigabit Ethernet" }, { VENDORID_DLINK, DEVICEID_DLINK_DGE550SX, "D-Link 550SX Gigabit Ethernet" }, { VENDORID_DLINK, DEVICEID_DLINK_DGE560SX, "D-Link 560SX Gigabit Ethernet" }, { VENDORID_DLINK, DEVICEID_DLINK_DGE560T, "D-Link 560T Gigabit Ethernet" } }; #ifndef MDEPKG_NDEBUG static const CHAR8 *model_name[] = { "Yukon XL", "Yukon EC Ultra", "Yukon EX", "Yukon EC", "Yukon FE", "Yukon FE+", "Yukon Supreme", "Yukon Ultra 2", "Yukon Unknown", "Yukon Optima", }; #endif // // Forward declarations // STATIC VOID mskc_setup_rambuffer (struct msk_softc *); STATIC VOID mskc_reset (struct msk_softc *); EFI_STATUS mskc_attach_if (struct msk_if_softc *, UINTN); VOID mskc_detach_if (struct msk_if_softc *); static VOID mskc_tick (IN EFI_EVENT, IN VOID*); STATIC VOID msk_intr (struct msk_softc *); static VOID msk_intr_phy (struct msk_if_softc *); static VOID msk_intr_gmac (struct msk_if_softc *); static __inline VOID msk_rxput (struct msk_if_softc *); STATIC INTN msk_handle_events (struct msk_softc *); static VOID msk_handle_hwerr (struct msk_if_softc *, UINT32); STATIC VOID msk_intr_hwerr (struct msk_softc *); static VOID msk_rxeof (struct msk_if_softc *, UINT32, UINT32, INTN); static VOID msk_txeof (struct msk_if_softc *, INTN); static EFI_STATUS msk_encap (struct msk_if_softc *, MSK_SYSTEM_BUF *); STATIC VOID msk_start (struct msk_if_softc *); STATIC VOID msk_set_prefetch (struct msk_if_softc *, INTN, EFI_PHYSICAL_ADDRESS, UINT32); static VOID msk_set_rambuffer (struct msk_if_softc *); static VOID msk_set_tx_stfwd (struct msk_if_softc *); static EFI_STATUS msk_init (struct msk_if_softc *); VOID mskc_stop_if (struct msk_if_softc *); static VOID msk_phy_power (struct msk_softc *, INTN); INTN msk_phy_readreg (struct msk_if_softc *, INTN); INTN msk_phy_writereg (struct msk_if_softc *, INTN, INTN); STATIC EFI_STATUS msk_status_dma_alloc (struct msk_softc *); STATIC VOID msk_status_dma_free (struct msk_softc *); static EFI_STATUS msk_txrx_dma_alloc (struct msk_if_softc *); static VOID msk_txrx_dma_free (struct msk_if_softc *); static EFI_STATUS msk_init_rx_ring (struct msk_if_softc *); static VOID msk_init_tx_ring (struct msk_if_softc *); static __inline VOID msk_discard_rxbuf (struct msk_if_softc *, INTN); static EFI_STATUS msk_newbuf (struct msk_if_softc *, INTN); static VOID msk_rxfilter ( struct msk_if_softc *sc_if, UINT32 FilterFlags, UINTN MCastFilterCnt, EFI_MAC_ADDRESS *MCastFilter ); static VOID msk_setvlan (struct msk_if_softc *); static VOID msk_stats_clear (struct msk_if_softc *); static VOID msk_stats_update (struct msk_if_softc *); STATIC VOID clear_pci_errors (struct msk_softc *); EFI_STATUS e1000_probe_and_attach (struct mii_data *, const struct msk_mii_data *, VOID *, VOID **); VOID e1000phy_tick (VOID *); VOID e1000phy_mediachg (VOID *); EFI_STATUS e1000phy_detach (VOID *); // // Functions // INTN msk_phy_readreg ( struct msk_if_softc *sc_if, INTN reg ) { INTN i; INTN val; INTN port; struct msk_softc *sc; sc = sc_if->msk_softc; port = sc_if->msk_md.port; GMAC_WRITE_2 (sc, port, GM_SMI_CTRL, GM_SMI_CT_PHY_AD (PHY_ADDR_MARV) | GM_SMI_CT_REG_AD (reg) | GM_SMI_CT_OP_RD); for (i = 0; i < MSK_TIMEOUT; i++) { gBS->Stall (1); val = GMAC_READ_2 (sc, port, GM_SMI_CTRL); if ((val & GM_SMI_CT_RD_VAL) != 0) { val = GMAC_READ_2 (sc, port, GM_SMI_DATA); break; } } if (i == MSK_TIMEOUT) { DEBUG ((EFI_D_NET, "Marvell Yukon: phy failed to come ready\n")); val = 0; } return (val); } INTN msk_phy_writereg ( struct msk_if_softc *sc_if, INTN reg, INTN val ) { INTN i; INTN port; struct msk_softc *sc; sc = sc_if->msk_softc; port = sc_if->msk_md.port; GMAC_WRITE_2 (sc, port, GM_SMI_DATA, val); GMAC_WRITE_2 (sc, port, GM_SMI_CTRL, GM_SMI_CT_PHY_AD (PHY_ADDR_MARV) | GM_SMI_CT_REG_AD (reg)); for (i = 0; i < MSK_TIMEOUT; i++) { gBS->Stall (1); if ((GMAC_READ_2 (sc, port, GM_SMI_CTRL) & GM_SMI_CT_BUSY) == 0) { break; } } if (i == MSK_TIMEOUT) { DEBUG ((EFI_D_NET, "Marvell Yukon: phy write timeout\n")); } return (0); } VOID msk_miibus_statchg ( struct msk_if_softc *sc_if ) { struct mii_data *mii; UINT32 gmac; UINTN port; struct msk_softc *sc; sc = sc_if->msk_softc; port = sc_if->msk_md.port; mii = &sc_if->mii_d; sc_if->msk_flags &= ~MSK_FLAG_LINK; if ((mii->mii_media_status & (IFM_AVALID | IFM_ACTIVE)) == (IFM_AVALID | IFM_ACTIVE)) { DEBUG ((EFI_D_NET, "Marvell Yukon: msk_miibus_statchg, phy is active\n")); switch (IFM_SUBTYPE (mii->mii_media_active)) { case IFM_10_T: case IFM_100_TX: sc_if->msk_flags |= MSK_FLAG_LINK; break; case IFM_1000_T: case IFM_1000_SX: case IFM_1000_LX: case IFM_1000_CX: if ((sc_if->msk_flags & MSK_FLAG_FASTETHER) == 0) { sc_if->msk_flags |= MSK_FLAG_LINK; } break; default: break; } } if ((sc_if->msk_flags & MSK_FLAG_LINK) != 0) { // Enable Tx FIFO Underrun DEBUG ((EFI_D_NET, "Marvell Yukon: msk_miibus_statchg, link up\n")); CSR_WRITE_1 (sc, MR_ADDR (port, GMAC_IRQ_MSK), GM_IS_TX_FF_UR | GM_IS_RX_FF_OR); // // Because mii(4) notify msk (4) that it detected link status // change, there is no need to enable automatic // speed/flow-control/duplex updates. // gmac = GM_GPCR_AU_ALL_DIS; switch (IFM_SUBTYPE (mii->mii_media_active)) { case IFM_1000_SX: case IFM_1000_T: gmac |= GM_GPCR_SPEED_1000; break; case IFM_100_TX: gmac |= GM_GPCR_SPEED_100; break; case IFM_10_T: break; } // Disable Rx flow control if ((IFM_OPTIONS (mii->mii_media_active) & IFM_FLAG0) == 0) { gmac |= GM_GPCR_FC_RX_DIS; } // Disable Tx flow control if ((IFM_OPTIONS (mii->mii_media_active) & IFM_FLAG1) == 0) { gmac |= GM_GPCR_FC_TX_DIS; } if ((IFM_OPTIONS (mii->mii_media_active) & IFM_FDX) != 0) { gmac |= GM_GPCR_DUP_FULL; } else { gmac |= GM_GPCR_FC_RX_DIS | GM_GPCR_FC_TX_DIS; } gmac |= GM_GPCR_RX_ENA | GM_GPCR_TX_ENA; GMAC_WRITE_2 (sc, port, GM_GP_CTRL, gmac); // Read again to ensure writing GMAC_READ_2 (sc, port, GM_GP_CTRL); gmac = GMC_PAUSE_OFF; if ((IFM_OPTIONS (mii->mii_media_active) & IFM_FDX) != 0) { if ((IFM_OPTIONS (mii->mii_media_active) & IFM_FLAG0) != 0) { gmac = GMC_PAUSE_ON; } } CSR_WRITE_4 (sc, MR_ADDR (port, GMAC_CTRL), gmac); // Enable PHY interrupt for FIFO underrun/overflow msk_phy_writereg (sc_if, PHY_MARV_INT_MASK, PHY_M_IS_FIFO_ERROR); } else { // // Link state changed to down. // Disable PHY interrupts. // DEBUG ((EFI_D_NET, "Marvell Yukon: msk_miibus_statchg, link down\n")); msk_phy_writereg (sc_if, PHY_MARV_INT_MASK, 0); // Disable Rx/Tx MAC gmac = GMAC_READ_2 (sc, port, GM_GP_CTRL); if ((GM_GPCR_RX_ENA | GM_GPCR_TX_ENA) != 0) { gmac &= ~(GM_GPCR_RX_ENA | GM_GPCR_TX_ENA); GMAC_WRITE_2 (sc, port, GM_GP_CTRL, gmac); // Read again to ensure writing GMAC_READ_2 (sc, port, GM_GP_CTRL); } } } UINT32 ether_crc32_be ( const UINT8 *buf, UINTN len ) { UINTN i; UINT32 crc; UINT32 carry; INTN bit; UINT8 data; crc = 0xffffffff; // initial value for (i = 0; i < len; i++) { for (data = *buf++, bit = 0; bit < 8; bit++, data >>= 1) { carry = ((crc & 0x80000000) ? 1 : 0) ^ (data & 0x01); crc <<= 1; if (carry) { crc = (crc ^ ETHER_CRC_POLY_BE) | carry; } } } return crc; } VOID mskc_rxfilter ( struct msk_if_softc *sc_if, UINT32 FilterFlags, UINTN MCastFilterCnt, EFI_MAC_ADDRESS *MCastFilter ) { msk_rxfilter (sc_if, FilterFlags, MCastFilterCnt, MCastFilter); } static VOID msk_rxfilter ( struct msk_if_softc *sc_if, UINT32 FilterFlags, UINTN MCastFilterCnt, EFI_MAC_ADDRESS *MCastFilter ) { UINT32 mchash[2]; UINT32 crc; UINT16 mode; INTN port; struct msk_softc *sc; sc = sc_if->msk_softc; port = sc_if->msk_md.port; gBS->SetMem (mchash, sizeof (mchash), 0); mode = GMAC_READ_2 (sc, port, GM_RX_CTRL); if ((FilterFlags & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS) != 0) { mode &= ~(GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA); } else if ((FilterFlags & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) != 0) { mode |= GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA; mchash[0] = 0xffff; mchash[1] = 0xffff; } else { mode |= GM_RXCR_UCF_ENA; while (MCastFilterCnt-- > 0) { crc = ether_crc32_be (MCastFilter[MCastFilterCnt].Addr, NET_ETHER_ADDR_LEN); /* Just want the 6 least significant bits. */ crc &= 0x3f; /* Set the corresponding bit in the hash table. */ mchash[crc >> 5] |= 1 << (crc & 0x1f); } if (mchash[0] != 0 || mchash[1] != 0) { mode |= GM_RXCR_MCF_ENA; } } GMAC_WRITE_2 (sc, port, GM_MC_ADDR_H1, mchash[0] & 0xffff ); GMAC_WRITE_2 (sc, port, GM_MC_ADDR_H2, (mchash[0] >> 16) & 0xffff ); GMAC_WRITE_2 (sc, port, GM_MC_ADDR_H3, mchash[1] & 0xffff ); GMAC_WRITE_2 (sc, port, GM_MC_ADDR_H4, (mchash[1] >> 16) & 0xffff ); GMAC_WRITE_2 (sc, port, GM_RX_CTRL, mode ); } static VOID msk_setvlan ( struct msk_if_softc *sc_if ) { // // Disable automatic VLAN tagging/stripping // CSR_WRITE_4 (sc_if->msk_softc, MR_ADDR (sc_if->msk_md.port, RX_GMF_CTRL_T), RX_VLAN_STRIP_OFF); CSR_WRITE_4 (sc_if->msk_softc, MR_ADDR (sc_if->msk_md.port, TX_GMF_CTRL_T), TX_VLAN_TAG_OFF); } static EFI_STATUS msk_init_rx_ring ( struct msk_if_softc *sc_if ) { struct msk_ring_data *rd; struct msk_rxdesc *rxd; INTN i; INTN prod; INTN nbuf; EFI_STATUS Status; sc_if->msk_cdata.msk_rx_cons = 0; sc_if->msk_cdata.msk_rx_prod = 0; sc_if->msk_cdata.msk_rx_putwm = MSK_PUT_WM; rd = &sc_if->msk_rdata; gBS->SetMem (rd->msk_rx_ring, MSK_RX_RING_SZ, 0); for (i = prod = 0; i < MSK_RX_RING_CNT; i++) { rxd = &sc_if->msk_cdata.msk_rxdesc[prod]; gBS->SetMem (&rxd->rx_m, sizeof (MSK_DMA_BUF), 0); rxd->rx_le = &rd->msk_rx_ring[prod]; MSK_INC (prod, MSK_RX_RING_CNT); } nbuf = MSK_RX_BUF_CNT; prod = 0; for (i = 0; i < nbuf; i++) { Status = msk_newbuf (sc_if, prod); if (EFI_ERROR (Status)) { return Status; } MSK_RX_INC(prod, MSK_RX_RING_CNT); } // Update prefetch unit. sc_if->msk_cdata.msk_rx_prod = MSK_RX_RING_CNT - 1; CSR_WRITE_2 (sc_if->msk_softc, Y2_PREF_Q_ADDR (sc_if->msk_rxq, PREF_UNIT_PUT_IDX_REG), sc_if->msk_cdata.msk_rx_prod); return EFI_SUCCESS; } STATIC VOID msk_init_tx_ring ( struct msk_if_softc *sc_if ) { struct msk_ring_data *rd; struct msk_txdesc *txd; INTN i; sc_if->msk_cdata.msk_tx_prod = 0; sc_if->msk_cdata.msk_tx_cons = 0; sc_if->msk_cdata.msk_tx_cnt = 0; sc_if->msk_cdata.msk_tx_high_addr = 0; rd = &sc_if->msk_rdata; gBS->SetMem (rd->msk_tx_ring, sizeof (struct msk_tx_desc) * MSK_TX_RING_CNT, 0); for (i = 0; i < MSK_TX_RING_CNT; i++) { txd = &sc_if->msk_cdata.msk_txdesc[i]; gBS->SetMem (&(txd->tx_m), sizeof (MSK_DMA_BUF), 0); txd->tx_le = &rd->msk_tx_ring[i]; } } static __inline VOID msk_discard_rxbuf ( struct msk_if_softc *sc_if, INTN idx ) { struct msk_rx_desc *rx_le; struct msk_rxdesc *rxd; MSK_DMA_BUF *DmaBuffer; DEBUG ((EFI_D_NET, "Marvell Yukon: discard rxbuf\n")); #ifdef MSK_64BIT_DMA rxd = &sc_if->msk_cdata.msk_rxdesc[idx]; rx_le = rxd->rx_le; rx_le->msk_control = htole32(OP_ADDR64 | HW_OWNER); MSK_INC(idx, MSK_RX_RING_CNT); #endif rxd = &sc_if->msk_cdata.msk_rxdesc[idx]; DmaBuffer = &rxd->rx_m; rx_le = rxd->rx_le; rx_le->msk_control = htole32 (DmaBuffer->Length | OP_PACKET | HW_OWNER); } static EFI_STATUS msk_newbuf ( IN struct msk_if_softc *sc_if, IN INTN idx ) { struct msk_rx_desc *rx_le; struct msk_rxdesc *rxd; UINTN Length; VOID *Buffer; VOID *Mapping; EFI_PHYSICAL_ADDRESS PhysAddr; EFI_PCI_IO_PROTOCOL *PciIo; EFI_STATUS Status; PciIo = sc_if->msk_softc->PciIo; Length = MAX_SUPPORTED_PACKET_SIZE; rxd = &sc_if->msk_cdata.msk_rxdesc[idx]; Status = gBS->AllocatePool (EfiBootServicesData, Length, &Buffer); if (EFI_ERROR (Status)) { return Status; } gBS->SetMem (Buffer, Length, 0); Status = PciIo->Map (PciIo, EfiPciIoOperationBusMasterWrite, Buffer, &Length, &PhysAddr, &Mapping); if (EFI_ERROR (Status)) { gBS->FreePool (Buffer); return Status; } #ifdef MSK_64BIT_DMA rx_le = rxd->rx_le; rx_le->msk_addr = htole32(MSK_ADDR_HI(PhysAddr)); rx_le->msk_control = htole32(OP_ADDR64 | HW_OWNER); MSK_INC(idx, MSK_RX_RING_CNT); rxd = &sc_if->msk_cdata.msk_rxdesc[idx]; #endif rxd->rx_m.DmaMapping = Mapping; rxd->rx_m.Buf = Buffer; rxd->rx_m.Length = Length; rx_le = rxd->rx_le; rx_le->msk_addr = htole32 (MSK_ADDR_LO (PhysAddr)); rx_le->msk_control = htole32 (Length | OP_PACKET | HW_OWNER); return EFI_SUCCESS; } EFI_STATUS mskc_probe ( EFI_PCI_IO_PROTOCOL *PciIo ) { struct msk_product *mp; UINT16 vendor; UINT16 devid; UINT32 PciID; INTN i; EFI_STATUS Status; Status = PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint32, PCI_VENDOR_ID_OFFSET, 1, &PciID ); if (EFI_ERROR (Status)) { return EFI_UNSUPPORTED; } vendor = PciID & 0xFFFF; devid = PciID >> 16; mp = msk_products; for (i = 0; i < sizeof (msk_products)/sizeof (msk_products[0]); i++, mp++) { if (vendor == mp->msk_vendorid && devid == mp->msk_deviceid) { DEBUG ((EFI_D_NET, "Marvell Yukon: Probe found device %a\n", mp->msk_name)); return EFI_SUCCESS; } } return EFI_UNSUPPORTED; } static VOID mskc_setup_rambuffer ( struct msk_softc *sc ) { INTN next; INTN i; /* Get adapter SRAM size. */ sc->msk_ramsize = CSR_READ_1 (sc, B2_E_0) * 4; DEBUG ((DEBUG_NET, "Marvell Yukon: RAM buffer size : %dKB\n", sc->msk_ramsize)); if (sc->msk_ramsize == 0) { return; } sc->msk_pflags |= MSK_FLAG_RAMBUF; /* * Give receiver 2/3 of memory and round down to the multiple * of 1024. Tx/Rx RAM buffer size of Yukon II shoud be multiple * of 1024. */ sc->msk_rxqsize = (((sc->msk_ramsize * 1024 * 2) / 3) / 1024) * 1024; sc->msk_txqsize = (sc->msk_ramsize * 1024) - sc->msk_rxqsize; for (i = 0, next = 0; i < sc->msk_num_port; i++) { sc->msk_rxqstart[i] = next; sc->msk_rxqend[i] = next + sc->msk_rxqsize - 1; next = sc->msk_rxqend[i] + 1; sc->msk_txqstart[i] = next; sc->msk_txqend[i] = next + sc->msk_txqsize - 1; next = sc->msk_txqend[i] + 1; DEBUG ((EFI_D_NET, "Marvell Yukon: Port %d : Rx Queue %dKB(0x%08x:0x%08x)\n", i, sc->msk_rxqsize / 1024, sc->msk_rxqstart[i], sc->msk_rxqend[i])); DEBUG ((EFI_D_NET, "Marvell Yukon: Port %d : Tx Queue %dKB(0x%08x:0x%08x)\n", i, sc->msk_txqsize / 1024, sc->msk_txqstart[i], sc->msk_txqend[i])); } } static VOID msk_phy_power ( struct msk_softc *sc, INTN mode ) { UINT32 our; UINT32 val; INTN i; switch (mode) { case MSK_PHY_POWERUP: // Switch power to VCC (WA for VAUX problem) CSR_WRITE_1 (sc, B0_POWER_CTRL, PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_OFF | PC_VCC_ON); // Disable Core Clock Division, set Clock Select to 0 CSR_WRITE_4 (sc, B2_Y2_CLK_CTRL, Y2_CLK_DIV_DIS); val = 0; if (sc->msk_hw_id == CHIP_ID_YUKON_XL && sc->msk_hw_rev > CHIP_REV_YU_XL_A1) { // Enable bits are inverted val = Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS | Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS | Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS; } // // Enable PCI & Core Clock, enable clock gating for both Links. // CSR_WRITE_1 (sc, B2_Y2_CLK_GATE, val); val = CSR_PCI_READ_4 (sc, PCI_OUR_REG_1); val &= ~(PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD); if (sc->msk_hw_id == CHIP_ID_YUKON_XL) { if (sc->msk_hw_rev > CHIP_REV_YU_XL_A1) { // Deassert Low Power for 1st PHY val |= PCI_Y2_PHY1_COMA; if (sc->msk_num_port > 1) { val |= PCI_Y2_PHY2_COMA; } } } // Release PHY from PowerDown/COMA mode CSR_PCI_WRITE_4 (sc, PCI_OUR_REG_1, val); switch (sc->msk_hw_id) { case CHIP_ID_YUKON_EC_U: case CHIP_ID_YUKON_EX: case CHIP_ID_YUKON_FE_P: case CHIP_ID_YUKON_UL_2: case CHIP_ID_YUKON_OPT: CSR_WRITE_2 (sc, B0_CTST, Y2_HW_WOL_OFF); // Enable all clocks CSR_PCI_WRITE_4 (sc, PCI_OUR_REG_3, 0); our = CSR_PCI_READ_4 (sc, PCI_OUR_REG_4); our &= (PCI_FORCE_ASPM_REQUEST | PCI_ASPM_GPHY_LINK_DOWN | PCI_ASPM_INT_FIFO_EMPTY | PCI_ASPM_CLKRUN_REQUEST); // Set all bits to 0 except bits 15..12 CSR_PCI_WRITE_4 (sc, PCI_OUR_REG_4, our); our = CSR_PCI_READ_4 (sc, PCI_OUR_REG_5); our &= PCI_CTL_TIM_VMAIN_AV_MSK; CSR_PCI_WRITE_4 (sc, PCI_OUR_REG_5, our); CSR_PCI_WRITE_4 (sc, PCI_CFG_REG_1, 0); // // Disable status race, workaround for // Yukon EC Ultra & Yukon EX. // val = CSR_READ_4 (sc, B2_GP_IO); val |= GLB_GPIO_STAT_RACE_DIS; CSR_WRITE_4 (sc, B2_GP_IO, val); CSR_READ_4 (sc, B2_GP_IO); break; default: break; } for (i = 0; i < sc->msk_num_port; i++) { CSR_WRITE_2 (sc, MR_ADDR (i, GMAC_LINK_CTRL), GMLC_RST_SET); CSR_WRITE_2 (sc, MR_ADDR (i, GMAC_LINK_CTRL), GMLC_RST_CLR); } break; case MSK_PHY_POWERDOWN: val = CSR_PCI_READ_4 (sc, PCI_OUR_REG_1); val |= PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD; if (sc->msk_hw_id == CHIP_ID_YUKON_XL && sc->msk_hw_rev > CHIP_REV_YU_XL_A1) { val &= ~PCI_Y2_PHY1_COMA; if (sc->msk_num_port > 1) { val &= ~PCI_Y2_PHY2_COMA; } } CSR_PCI_WRITE_4 (sc, PCI_OUR_REG_1, val); val = Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS | Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS | Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS; if (sc->msk_hw_id == CHIP_ID_YUKON_XL && sc->msk_hw_rev > CHIP_REV_YU_XL_A1) { // Enable bits are inverted val = 0; } // // Disable PCI & Core Clock, disable clock gating for // both Links. // CSR_WRITE_1 (sc, B2_Y2_CLK_GATE, val); CSR_WRITE_1 (sc, B0_POWER_CTRL, PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_ON | PC_VCC_OFF); break; default: break; } } static VOID clear_pci_errors ( struct msk_softc *sc ) { EFI_STATUS Status; UINT16 val; EFI_PCI_IO_PROTOCOL *PciIo; PciIo = sc->PciIo; // Clear all error bits in the PCI status register. Status = PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint16, PCI_PRIMARY_STATUS_OFFSET, 1, &val ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Marvell Yukon: Warning - Reading PCI Status failed: %r", Status)); } CSR_WRITE_1 (sc, B2_TST_CTRL1, TST_CFG_WRITE_ON); val |= PCIM_STATUS_PERR | PCIM_STATUS_SERR | PCIM_STATUS_RMABORT | PCIM_STATUS_RTABORT | PCIM_STATUS_PERRREPORT; Status = PciIo->Pci.Write ( PciIo, EfiPciIoWidthUint16, PCI_PRIMARY_STATUS_OFFSET, 1, &val ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Marvell Yukon: Warning - Writing PCI Status failed: %r", Status)); } CSR_WRITE_2 (sc, B0_CTST, CS_MRST_CLR); } static VOID mskc_reset ( struct msk_softc *sc ) { EFI_STATUS Status; EFI_PHYSICAL_ADDRESS PhysAddr; UINT16 status; UINT32 val; INTN i; EFI_PCI_IO_PROTOCOL *PciIo; PciIo = sc->PciIo; CSR_WRITE_2 (sc, B0_CTST, CS_RST_CLR); // Disable ASF if (sc->msk_hw_id == CHIP_ID_YUKON_EX) { status = CSR_READ_2 (sc, B28_Y2_ASF_HCU_CCSR); // Clear AHB bridge & microcontroller reset status &= ~(Y2_ASF_HCU_CCSR_AHB_RST | Y2_ASF_HCU_CCSR_CPU_RST_MODE); // Clear ASF microcontroller state status &= ~ Y2_ASF_HCU_CCSR_UC_STATE_MSK; CSR_WRITE_2 (sc, B28_Y2_ASF_HCU_CCSR, status); } else { CSR_WRITE_1 (sc, B28_Y2_ASF_STAT_CMD, Y2_ASF_RESET); } CSR_WRITE_2 (sc, B0_CTST, Y2_ASF_DISABLE); // // Since we disabled ASF, S/W reset is required for Power Management. // CSR_WRITE_2 (sc, B0_CTST, CS_RST_SET); CSR_WRITE_2 (sc, B0_CTST, CS_RST_CLR); clear_pci_errors (sc); switch (sc->msk_bustype) { case MSK_PEX_BUS: // Clear all PEX errors CSR_PCI_WRITE_4 (sc, PEX_UNC_ERR_STAT, 0xffffffff); val = CSR_PCI_READ_4 (sc, PEX_UNC_ERR_STAT); if ((val & PEX_RX_OV) != 0) { sc->msk_intrmask &= ~Y2_IS_HW_ERR; sc->msk_intrhwemask &= ~Y2_IS_PCI_EXP; } break; case MSK_PCI_BUS: case MSK_PCIX_BUS: // Set Cache Line Size to 2 (8bytes) if configured to 0 Status = PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint8, PCI_CACHELINE_SIZE_OFFSET, 1, &val ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Marvell Yukon: Warning - Reading PCI cache line size failed: %r", Status)); } if (val == 0) { val = 2; Status = PciIo->Pci.Write ( PciIo, EfiPciIoWidthUint8, PCI_CACHELINE_SIZE_OFFSET, 1, &val ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Marvell Yukon: Warning - Writing PCI cache line size failed: %r", Status)); } } if (sc->msk_bustype == MSK_PCIX_BUS) { Status = PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint32, PCI_OUR_REG_1, 1, &val ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Marvell Yukon: Warning - Reading Our Reg 1 failed: %r", Status)); } val |= PCI_CLS_OPT; Status = PciIo->Pci.Write ( PciIo, EfiPciIoWidthUint32, PCI_OUR_REG_1, 1, &val ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Marvell Yukon: Warning - Writing Our Reg 1 failed: %r", Status)); } } break; } // Set PHY power state msk_phy_power (sc, MSK_PHY_POWERUP); // Reset GPHY/GMAC Control for (i = 0; i < sc->msk_num_port; i++) { // GPHY Control reset CSR_WRITE_4 (sc, MR_ADDR (i, GPHY_CTRL), GPC_RST_SET); CSR_WRITE_4 (sc, MR_ADDR (i, GPHY_CTRL), GPC_RST_CLR); if (sc->msk_hw_id == CHIP_ID_YUKON_UL_2) { // Magic value observed under Linux. CSR_WRITE_4 (sc, MR_ADDR (i, GPHY_CTRL), 0x00105226); } // GMAC Control reset CSR_WRITE_4 (sc, MR_ADDR (i, GMAC_CTRL), GMC_RST_SET); CSR_WRITE_4 (sc, MR_ADDR (i, GMAC_CTRL), GMC_RST_CLR); CSR_WRITE_4 (sc, MR_ADDR (i, GMAC_CTRL), GMC_F_LOOPB_OFF); if (sc->msk_hw_id == CHIP_ID_YUKON_EX) { CSR_WRITE_4 (sc, MR_ADDR (i, GMAC_CTRL), GMC_BYP_MACSECRX_ON | GMC_BYP_MACSECTX_ON | GMC_BYP_RETR_ON); } } if ((sc->msk_hw_id == CHIP_ID_YUKON_OPT) && (sc->msk_hw_rev == 0)) { // Disable PCIe PHY powerdown (reg 0x80, bit7) CSR_WRITE_4 (sc, Y2_PEX_PHY_DATA, (0x0080 << 16) | 0x0080); } CSR_WRITE_1 (sc, B2_TST_CTRL1, TST_CFG_WRITE_OFF); // LED On CSR_WRITE_2 (sc, B0_CTST, Y2_LED_STAT_ON); // Enable plug in go CSR_WRITE_2 (sc, B0_CTST, Y_ULTRA_2_PLUG_IN_GO_EN); // Clear TWSI IRQ CSR_WRITE_4 (sc, B2_I2C_IRQ, I2C_CLR_IRQ); // Turn off hardware timer CSR_WRITE_1 (sc, B2_TI_CTRL, TIM_STOP); CSR_WRITE_1 (sc, B2_TI_CTRL, TIM_CLR_IRQ); // Turn off descriptor polling CSR_WRITE_1 (sc, B28_DPT_CTRL, DPT_STOP); // Turn off time stamps CSR_WRITE_1 (sc, GMAC_TI_ST_CTRL, GMT_ST_STOP); CSR_WRITE_1 (sc, GMAC_TI_ST_CTRL, GMT_ST_CLR_IRQ); // Configure timeout values for (i = 0; i < sc->msk_num_port; i++) { CSR_WRITE_2 (sc, SELECT_RAM_BUFFER (i, B3_RI_CTRL), RI_RST_SET); CSR_WRITE_2 (sc, SELECT_RAM_BUFFER (i, B3_RI_CTRL), RI_RST_CLR); CSR_WRITE_1 (sc, SELECT_RAM_BUFFER (i, B3_RI_WTO_R1), MSK_RI_TO_53); CSR_WRITE_1 (sc, SELECT_RAM_BUFFER (i, B3_RI_WTO_XA1), MSK_RI_TO_53); CSR_WRITE_1 (sc, SELECT_RAM_BUFFER (i, B3_RI_WTO_XS1), MSK_RI_TO_53); CSR_WRITE_1 (sc, SELECT_RAM_BUFFER (i, B3_RI_RTO_R1), MSK_RI_TO_53); CSR_WRITE_1 (sc, SELECT_RAM_BUFFER (i, B3_RI_RTO_XA1), MSK_RI_TO_53); CSR_WRITE_1 (sc, SELECT_RAM_BUFFER (i, B3_RI_RTO_XS1), MSK_RI_TO_53); CSR_WRITE_1 (sc, SELECT_RAM_BUFFER (i, B3_RI_WTO_R2), MSK_RI_TO_53); CSR_WRITE_1 (sc, SELECT_RAM_BUFFER (i, B3_RI_WTO_XA2), MSK_RI_TO_53); CSR_WRITE_1 (sc, SELECT_RAM_BUFFER (i, B3_RI_WTO_XS2), MSK_RI_TO_53); CSR_WRITE_1 (sc, SELECT_RAM_BUFFER (i, B3_RI_RTO_R2), MSK_RI_TO_53); CSR_WRITE_1 (sc, SELECT_RAM_BUFFER (i, B3_RI_RTO_XA2), MSK_RI_TO_53); CSR_WRITE_1 (sc, SELECT_RAM_BUFFER (i, B3_RI_RTO_XS2), MSK_RI_TO_53); } // Disable all interrupts CSR_WRITE_4 (sc, B0_HWE_IMSK, 0); CSR_READ_4 (sc, B0_HWE_IMSK); CSR_WRITE_4 (sc, B0_IMSK, 0); CSR_READ_4 (sc, B0_IMSK); // Clear status list gBS->SetMem (sc->msk_stat_ring, sizeof (struct msk_stat_desc) * MSK_STAT_RING_CNT, 0); sc->msk_stat_cons = 0; CSR_WRITE_4 (sc, STAT_CTRL, SC_STAT_RST_SET); CSR_WRITE_4 (sc, STAT_CTRL, SC_STAT_RST_CLR); // Set the status list base address PhysAddr = sc->msk_stat_ring_paddr; CSR_WRITE_4 (sc, STAT_LIST_ADDR_LO, MSK_ADDR_LO (PhysAddr)); CSR_WRITE_4 (sc, STAT_LIST_ADDR_HI, MSK_ADDR_HI (PhysAddr)); // Set the status list last index CSR_WRITE_2 (sc, STAT_LAST_IDX, MSK_STAT_RING_CNT - 1); if ((sc->msk_hw_id == CHIP_ID_YUKON_EC) && (sc->msk_hw_rev == CHIP_REV_YU_EC_A1)) { // WA for dev. #4.3 CSR_WRITE_2 (sc, STAT_TX_IDX_TH, ST_TXTH_IDX_MASK); // WA for dev. #4.18 CSR_WRITE_1 (sc, STAT_FIFO_WM, 0x21); CSR_WRITE_1 (sc, STAT_FIFO_ISR_WM, 0x07); } else { CSR_WRITE_2 (sc, STAT_TX_IDX_TH, 0x0a); CSR_WRITE_1 (sc, STAT_FIFO_WM, 0x10); if ((sc->msk_hw_id == CHIP_ID_YUKON_XL) && (sc->msk_hw_rev == CHIP_REV_YU_XL_A0)) { CSR_WRITE_1 (sc, STAT_FIFO_ISR_WM, 0x04); } else { CSR_WRITE_1 (sc, STAT_FIFO_ISR_WM, 0x10); } CSR_WRITE_4 (sc, STAT_ISR_TIMER_INI, 0x0190); } // // Use default value for STAT_ISR_TIMER_INI, STAT_LEV_TIMER_INI. // CSR_WRITE_4 (sc, STAT_TX_TIMER_INI, MSK_USECS (sc, 1000)); // Enable status unit CSR_WRITE_4 (sc, STAT_CTRL, SC_STAT_OP_ON); CSR_WRITE_1 (sc, STAT_TX_TIMER_CTRL, TIM_START); CSR_WRITE_1 (sc, STAT_LEV_TIMER_CTRL, TIM_START); CSR_WRITE_1 (sc, STAT_ISR_TIMER_CTRL, TIM_START); } EFI_STATUS mskc_attach_if ( struct msk_if_softc *sc_if, UINTN Port ) { INTN i; EFI_STATUS Status; sc_if->msk_md.port = Port; sc_if->msk_flags = sc_if->msk_softc->msk_pflags; // Setup Tx/Rx queue register offsets if (Port == MSK_PORT_A) { sc_if->msk_txq = Q_XA1; sc_if->msk_txsq = Q_XS1; sc_if->msk_rxq = Q_R1; } else { sc_if->msk_txq = Q_XA2; sc_if->msk_txsq = Q_XS2; sc_if->msk_rxq = Q_R2; } Status = msk_txrx_dma_alloc (sc_if); if (EFI_ERROR (Status)) { return Status; } /* * Get station address for this interface. Note that * dual port cards actually come with three station * addresses: one for each port, plus an extra. The * extra one is used by the SysKonnect driver software * as a 'virtual' station address for when both ports * are operating in failover mode. Currently we don't * use this extra address. */ for (i = 0; i < NET_ETHER_ADDR_LEN; i++) { sc_if->MacAddress.Addr[i] = CSR_READ_1 (sc_if->msk_softc, B2_MAC_1 + (Port * 8) + i); } DEBUG ((EFI_D_NET,"Marvell Yukon: Mac Address %02x:%02x:%02x:%02x:%02x:%02x\n", sc_if->MacAddress.Addr[0], sc_if->MacAddress.Addr[1], sc_if->MacAddress.Addr[2], sc_if->MacAddress.Addr[3], sc_if->MacAddress.Addr[4], sc_if->MacAddress.Addr[5])); Status = e1000_probe_and_attach (&sc_if->mii_d, &sc_if->msk_md, sc_if, &sc_if->phy_softc); if (EFI_ERROR (Status)) { return Status; } InitializeListHead (&sc_if->TransmitQueueHead); InitializeListHead (&sc_if->TransmitFreeQueueHead); InitializeListHead (&sc_if->ReceiveQueueHead); sc_if->active = TRUE; return (Status); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ EFI_STATUS mskc_attach ( IN EFI_PCI_IO_PROTOCOL *PciIo, OUT struct msk_softc **ScData ) { struct msk_mii_data *mmd; UINT64 Supports; UINT8 *PciBarResources; EFI_STATUS Status; struct msk_if_softc *ScIf; struct msk_softc *sc; Status = gBS->AllocatePool (EfiBootServicesData, sizeof (struct msk_softc), (VOID**) &sc); if (EFI_ERROR (Status)) { return Status; } // // Save original PCI attributes // gBS->SetMem (sc, sizeof (struct msk_softc), 0); sc->PciIo = PciIo; Status = PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationGet, 0, &sc->OriginalPciAttributes ); if (EFI_ERROR (Status)) { gBS->FreePool (sc); return Status; } Status = PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationSupported, 0, &Supports ); if (!EFI_ERROR (Status)) { Supports &= EFI_PCI_DEVICE_ENABLE; Status = PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationEnable, Supports | EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE, NULL ); } if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Marvell Yukon: Failed to enable NIC controller\n")); goto RESTORE_PCI_ATTRIBS; } Status = PciIo->GetBarAttributes (PciIo, 0, &Supports, (VOID**)&PciBarResources); if (!EFI_ERROR (Status) && (((EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *)PciBarResources)->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR)) { if (((EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *)PciBarResources)->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM) { if (!(((EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *)PciBarResources)->SpecificFlag & ACPI_SPECFLAG_PREFETCHABLE)) { sc->RegBase = ((EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *)PciBarResources)->AddrRangeMin; // Should assert that Bar is 32 bits wide DEBUG ((DEBUG_NET, "Marvell Yukon: GlobalRegistersBase = 0x%x\n", sc->RegBase)); } else { Status = EFI_NOT_FOUND; } } else { Status = EFI_NOT_FOUND; } } if (EFI_ERROR (Status)) { goto RESTORE_PCI_ATTRIBS; } // Clear Software Reset CSR_WRITE_2 (sc, B0_CTST, CS_RST_CLR); // Get Hardware ID & Revision sc->msk_hw_id = CSR_READ_1 (sc, B2_CHIP_ID); sc->msk_hw_rev = (CSR_READ_1 (sc, B2_MAC_CFG) >> 4) & 0x0f; // Bail out if chip is not recognized if (sc->msk_hw_id < CHIP_ID_YUKON_XL || sc->msk_hw_id > CHIP_ID_YUKON_OPT || sc->msk_hw_id == CHIP_ID_YUKON_SUPR || sc->msk_hw_id == CHIP_ID_YUKON_UNKNOWN) { DEBUG ((DEBUG_NET, "Marvell Yukon: unknown device: id=0x%02x, rev=0x%02x\n", sc->msk_hw_id, sc->msk_hw_rev)); Status = EFI_DEVICE_ERROR; goto RESTORE_PCI_ATTRIBS; } DEBUG ((EFI_D_NET, "Marvell Yukon: Marvell Technology Group Ltd. %a Id:0x%02x Rev:0x%02x\n", model_name[sc->msk_hw_id - CHIP_ID_YUKON_XL], sc->msk_hw_id, sc->msk_hw_rev)); sc->msk_process_limit = MSK_PROC_DEFAULT; sc->msk_int_holdoff = MSK_INT_HOLDOFF_DEFAULT; // Check if MAC address is valid if ((CSR_READ_4 (sc, B2_MAC_1) == 0) && (CSR_READ_4 (sc, B2_MAC_1+4) == 0)) { DEBUG ((EFI_D_NET, "Marvell Yukon: MAC address is invalid (00:00:00:00:00:00)\n")); } // Soft reset CSR_WRITE_2 (sc, B0_CTST, CS_RST_SET); CSR_WRITE_2 (sc, B0_CTST, CS_RST_CLR); sc->msk_pmd = CSR_READ_1 (sc, B2_PMD_TYP); // Check number of MACs sc->msk_num_port = 1; if ((CSR_READ_1 (sc, B2_Y2_HW_RES) & CFG_DUAL_MAC_MSK) == CFG_DUAL_MAC_MSK) { if (!(CSR_READ_1 (sc, B2_Y2_CLK_GATE) & Y2_STATUS_LNK2_INAC)) { sc->msk_num_port++; } } /* Check bus type. */ sc->msk_bustype = MSK_PEX_BUS; /* Only support PCI Express */ sc->msk_expcap = 1; switch (sc->msk_hw_id) { case CHIP_ID_YUKON_EC: sc->msk_clock = 125; /* 125 MHz */ sc->msk_pflags |= MSK_FLAG_JUMBO; break; case CHIP_ID_YUKON_EC_U: sc->msk_clock = 125; /* 125 MHz */ sc->msk_pflags |= MSK_FLAG_JUMBO | MSK_FLAG_JUMBO_NOCSUM; break; case CHIP_ID_YUKON_EX: sc->msk_clock = 125; /* 125 MHz */ sc->msk_pflags |= MSK_FLAG_JUMBO | MSK_FLAG_DESCV2 | MSK_FLAG_AUTOTX_CSUM; /* * Yukon Extreme seems to have silicon bug for * automatic Tx checksum calculation capability. */ if (sc->msk_hw_rev == CHIP_REV_YU_EX_B0) { sc->msk_pflags &= ~MSK_FLAG_AUTOTX_CSUM; } /* * Yukon Extreme A0 could not use store-and-forward * for jumbo frames, so disable Tx checksum * offloading for jumbo frames. */ if (sc->msk_hw_rev == CHIP_REV_YU_EX_A0) { sc->msk_pflags |= MSK_FLAG_JUMBO_NOCSUM; } break; case CHIP_ID_YUKON_FE: sc->msk_clock = 100; /* 100 MHz */ sc->msk_pflags |= MSK_FLAG_FASTETHER; break; case CHIP_ID_YUKON_FE_P: sc->msk_clock = 50; /* 50 MHz */ sc->msk_pflags |= MSK_FLAG_FASTETHER | MSK_FLAG_DESCV2 | MSK_FLAG_AUTOTX_CSUM; if (sc->msk_hw_rev == CHIP_REV_YU_FE_P_A0) { /* * XXX * FE+ A0 has status LE writeback bug so msk (4) * does not rely on status word of received frame * in msk_rxeof () which in turn disables all * hardware assistance bits reported by the status * word as well as validity of the recevied frame. * Just pass received frames to upper stack with * minimal test and let upper stack handle them. */ sc->msk_pflags |= MSK_FLAG_NOHWVLAN | MSK_FLAG_NORXCHK | MSK_FLAG_NORX_CSUM; } break; case CHIP_ID_YUKON_XL: sc->msk_clock = 156; /* 156 MHz */ sc->msk_pflags |= MSK_FLAG_JUMBO; break; case CHIP_ID_YUKON_UL_2: sc->msk_clock = 125; /* 125 MHz */ sc->msk_pflags |= MSK_FLAG_JUMBO; break; case CHIP_ID_YUKON_OPT: sc->msk_clock = 125; /* 125 MHz */ sc->msk_pflags |= MSK_FLAG_JUMBO | MSK_FLAG_DESCV2; break; default: sc->msk_clock = 156; /* 156 MHz */ break; } Status = msk_status_dma_alloc (sc); if (EFI_ERROR (Status)) { goto fail; } // Set base interrupt mask sc->msk_intrmask = Y2_IS_HW_ERR | Y2_IS_STAT_BMU; sc->msk_intrhwemask = Y2_IS_TIST_OV | Y2_IS_MST_ERR | Y2_IS_IRQ_STAT | Y2_IS_PCI_EXP | Y2_IS_PCI_NEXP; // Reset the adapter mskc_reset (sc); mskc_setup_rambuffer (sc); Status = gBS->AllocatePool (EfiBootServicesData, sizeof (struct msk_if_softc), (VOID**) &ScIf); if (EFI_ERROR (Status)) { goto fail; } gBS->SetMem (ScIf, sizeof (struct msk_if_softc), 0); ScIf->msk_softc = sc; sc->msk_if[MSK_PORT_A] = ScIf; Status = mskc_attach_if (sc->msk_if[MSK_PORT_A], MSK_PORT_A); if (EFI_ERROR (Status)) { goto fail; } mmd = &ScIf->msk_md; mmd->port = MSK_PORT_A; mmd->pmd = sc->msk_pmd; if (sc->msk_pmd == 'L' || sc->msk_pmd == 'S' || sc->msk_pmd == 'P') { mmd->mii_flags |= MIIF_HAVEFIBER; } if (sc->msk_num_port > 1) { Status = gBS->AllocatePool (EfiBootServicesData, sizeof (struct msk_if_softc), (VOID**) &ScIf); if (EFI_ERROR (Status)) { goto fail; } gBS->SetMem (ScIf, sizeof (struct msk_if_softc), 0); ScIf->msk_softc = sc; sc->msk_if[MSK_PORT_B] = ScIf; Status = mskc_attach_if (sc->msk_if[MSK_PORT_B], MSK_PORT_B); if (EFI_ERROR (Status)) { goto fail; } mmd = &ScIf->msk_md; mmd->port = MSK_PORT_B; mmd->pmd = sc->msk_pmd; if (sc->msk_pmd == 'L' || sc->msk_pmd == 'S' || sc->msk_pmd == 'P') { mmd->mii_flags |= MIIF_HAVEFIBER; } } // Return new msk_softc structure *ScData = sc; // Create timer for tick Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL | EVT_TIMER, TPL_CALLBACK, mskc_tick, (VOID *)sc, &sc->Timer ); if (EFI_ERROR (Status)) { goto fail; } Status = gBS->SetTimer (sc->Timer, TimerPeriodic, TICKS_PER_SECOND); if (EFI_ERROR (Status)) { goto fail; } fail: if (EFI_ERROR (Status)) { mskc_detach (sc); } return (Status); RESTORE_PCI_ATTRIBS: // // Restore original PCI attributes // PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationSet, sc->OriginalPciAttributes, NULL ); gBS->FreePool (sc); return Status; } /* * Shutdown hardware and free up resources. This can be called any * time after the mutex has been initialized. It is called in both * the error case in attach and the normal detach case so it needs * to be careful about only freeing resources that have actually been * allocated. */ VOID mskc_detach_if ( struct msk_if_softc *sc_if ) { if (sc_if->active) { mskc_stop_if (sc_if); msk_txrx_dma_free (sc_if); e1000phy_detach (sc_if->phy_softc); sc_if->phy_softc = NULL; sc_if->active = FALSE; } } VOID mskc_detach ( struct msk_softc *sc ) { EFI_TPL OldTpl; EFI_PCI_IO_PROTOCOL *PciIo; if (sc == NULL) { return; } OldTpl = gBS->RaiseTPL (TPL_NOTIFY); PciIo = sc->PciIo; if (sc->msk_if[MSK_PORT_A] != NULL) { mskc_detach_if (sc->msk_if[MSK_PORT_A]); gBS->FreePool (sc->msk_if[MSK_PORT_A]); sc->msk_if[MSK_PORT_A] = NULL; } if (sc->msk_if[MSK_PORT_B] != NULL) { mskc_detach_if (sc->msk_if[MSK_PORT_B]); gBS->FreePool (sc->msk_if[MSK_PORT_B]); sc->msk_if[MSK_PORT_B] = NULL; } /* Disable all interrupts. */ CSR_WRITE_4 (sc, B0_IMSK, 0); CSR_READ_4 (sc, B0_IMSK); CSR_WRITE_4 (sc, B0_HWE_IMSK, 0); CSR_READ_4 (sc, B0_HWE_IMSK); // LED Off. CSR_WRITE_2 (sc, B0_CTST, Y2_LED_STAT_OFF); // Put hardware reset. CSR_WRITE_2 (sc, B0_CTST, CS_RST_SET); msk_status_dma_free (sc); if (sc->Timer != NULL) { gBS->SetTimer (sc->Timer, TimerCancel, 0); gBS->CloseEvent (sc->Timer); sc->Timer = NULL; } // // Restore original PCI attributes // PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationSet, sc->OriginalPciAttributes, NULL ); gBS->RestoreTPL (OldTpl); } /* Create status DMA region. */ static EFI_STATUS msk_status_dma_alloc ( struct msk_softc *sc ) { EFI_STATUS Status; UINTN Length; EFI_PCI_IO_PROTOCOL *PciIo; PciIo = sc->PciIo; Status = PciIo->AllocateBuffer (PciIo, AllocateAnyPages, EfiBootServicesData, EFI_SIZE_TO_PAGES (MSK_STAT_RING_SZ), (VOID**)&sc->msk_stat_ring, 0); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Marvell Yukon: failed to allocate DMA'able memory for status ring\n")); return Status; } ASSERT (sc->msk_stat_ring != NULL); Length = MSK_STAT_RING_SZ; Status = PciIo->Map (PciIo, EfiPciIoOperationBusMasterCommonBuffer, sc->msk_stat_ring, &Length, &sc->msk_stat_ring_paddr, &sc->msk_stat_map); ASSERT (Length == MSK_STAT_RING_SZ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Marvell Yukon: failed to map DMA'able memory for status ring\n")); } return Status; } static VOID msk_status_dma_free ( struct msk_softc *sc ) { EFI_PCI_IO_PROTOCOL *PciIo; PciIo = sc->PciIo; if (sc->msk_stat_map) { PciIo->Unmap (PciIo, sc->msk_stat_map); if (sc->msk_stat_ring) { PciIo->FreeBuffer (PciIo, EFI_SIZE_TO_PAGES (MSK_STAT_RING_SZ), sc->msk_stat_ring); sc->msk_stat_ring = NULL; } sc->msk_stat_map = NULL; } } static EFI_STATUS msk_txrx_dma_alloc ( struct msk_if_softc *sc_if ) { struct msk_txdesc *txd; struct msk_rxdesc *rxd; INTN i; UINTN Length; EFI_STATUS Status; EFI_PCI_IO_PROTOCOL *PciIo; PciIo = sc_if->msk_softc->PciIo; Status = PciIo->AllocateBuffer (PciIo, AllocateAnyPages, EfiBootServicesData, EFI_SIZE_TO_PAGES (MSK_TX_RING_SZ), (VOID**)&sc_if->msk_rdata.msk_tx_ring, 0); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Marvell Yukon: failed to allocate DMA'able memory for Tx ring\n")); goto fail; } ASSERT (sc_if->msk_rdata.msk_tx_ring != NULL); Length = MSK_TX_RING_SZ; Status = PciIo->Map (PciIo, EfiPciIoOperationBusMasterCommonBuffer, sc_if->msk_rdata.msk_tx_ring, &Length, &sc_if->msk_rdata.msk_tx_ring_paddr, &sc_if->msk_cdata.msk_tx_ring_map); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Marvell Yukon: failed to map DMA'able memory for Tx ring\n")); goto fail; } ASSERT (Length == MSK_TX_RING_SZ); Status = PciIo->AllocateBuffer (PciIo, AllocateAnyPages, EfiBootServicesData, EFI_SIZE_TO_PAGES (MSK_RX_RING_SZ), (VOID**)&sc_if->msk_rdata.msk_rx_ring, 0); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Marvell Yukon: failed to allocate DMA'able memory for Rx ring\n")); goto fail; } ASSERT (sc_if->msk_rdata.msk_rx_ring != NULL); Length = MSK_RX_RING_SZ; Status = PciIo->Map (PciIo, EfiPciIoOperationBusMasterCommonBuffer, sc_if->msk_rdata.msk_rx_ring, &Length, &sc_if->msk_rdata.msk_rx_ring_paddr, &sc_if->msk_cdata.msk_rx_ring_map); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Marvell Yukon: failed to map DMA'able memory for Rx ring\n")); goto fail; } ASSERT (Length == MSK_RX_RING_SZ); // Create DMA maps for Tx buffers. for (i = 0; i < MSK_TX_RING_CNT; i++) { txd = &sc_if->msk_cdata.msk_txdesc[i]; gBS->SetMem (&(txd->tx_m), sizeof (MSK_DMA_BUF), 0); } // Create DMA maps for Rx buffers. for (i = 0; i < MSK_RX_RING_CNT; i++) { rxd = &sc_if->msk_cdata.msk_rxdesc[i]; gBS->SetMem (&(rxd->rx_m), sizeof (MSK_DMA_BUF), 0); } fail: return (Status); } static VOID msk_txrx_dma_free ( struct msk_if_softc *sc_if ) { struct msk_txdesc *txd; struct msk_rxdesc *rxd; INTN i; EFI_PCI_IO_PROTOCOL *PciIo; PciIo = sc_if->msk_softc->PciIo; // Tx ring if (sc_if->msk_cdata.msk_tx_ring_map) { PciIo->Unmap (PciIo, sc_if->msk_cdata.msk_tx_ring_map); if (sc_if->msk_rdata.msk_tx_ring) { PciIo->FreeBuffer (PciIo, EFI_SIZE_TO_PAGES (MSK_TX_RING_SZ), sc_if->msk_rdata.msk_tx_ring); sc_if->msk_rdata.msk_tx_ring = NULL; } sc_if->msk_cdata.msk_tx_ring_map = NULL; } // Rx ring if (sc_if->msk_cdata.msk_rx_ring_map) { PciIo->Unmap (PciIo, sc_if->msk_cdata.msk_rx_ring_map); if (sc_if->msk_rdata.msk_rx_ring) { PciIo->FreeBuffer (PciIo, EFI_SIZE_TO_PAGES (MSK_RX_RING_SZ), sc_if->msk_rdata.msk_rx_ring); sc_if->msk_rdata.msk_rx_ring = NULL; } sc_if->msk_cdata.msk_rx_ring_map = NULL; } // Tx buffers for (i = 0; i < MSK_TX_RING_CNT; i++) { txd = &sc_if->msk_cdata.msk_txdesc[i]; if (txd->tx_m.DmaMapping) { PciIo->Unmap (PciIo, txd->tx_m.DmaMapping); gBS->SetMem (&(txd->tx_m), sizeof (MSK_DMA_BUF), 0); // We don't own the transmit buffers so don't free them } } // Rx buffers for (i = 0; i < MSK_RX_RING_CNT; i++) { rxd = &sc_if->msk_cdata.msk_rxdesc[i]; if (rxd->rx_m.DmaMapping) { PciIo->Unmap (PciIo, rxd->rx_m.DmaMapping); // Free Rx buffers as we own these if(rxd->rx_m.Buf != NULL) { gBS->FreePool (rxd->rx_m.Buf); rxd->rx_m.Buf = NULL; } gBS->SetMem (&(rxd->rx_m), sizeof (MSK_DMA_BUF), 0); } } } static EFI_STATUS msk_encap ( struct msk_if_softc *sc_if, MSK_SYSTEM_BUF *m_head ) { struct msk_txdesc *txd; struct msk_txdesc *txd_last; struct msk_tx_desc *tx_le; VOID *Mapping; EFI_PHYSICAL_ADDRESS BusPhysAddr; UINTN BusLength; UINT32 control; UINT32 prod; UINT32 si; EFI_STATUS Status; EFI_PCI_IO_PROTOCOL *PciIo; PciIo = sc_if->msk_softc->PciIo; prod = sc_if->msk_cdata.msk_tx_prod; txd = &sc_if->msk_cdata.msk_txdesc[prod]; txd_last = txd; BusLength = m_head->Length; Status = PciIo->Map (PciIo, EfiPciIoOperationBusMasterRead, m_head->Buf, &BusLength, &BusPhysAddr, &txd->tx_m.DmaMapping); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Marvell Yukon: failed to map DMA'able memory for Tx buffer\n")); return Status; } ASSERT (BusLength == m_head->Length); control = 0; #ifdef MSK_64BIT_DMA if (MSK_ADDR_HI(BusPhysAddr) != sc_if->msk_cdata.msk_tx_high_addr) { sc_if->msk_cdata.msk_tx_high_addr = MSK_ADDR_HI(BusPhysAddr); tx_le = &sc_if->msk_rdata.msk_tx_ring[prod]; tx_le->msk_addr = htole32(MSK_ADDR_HI(BusPhysAddr)); tx_le->msk_control = htole32(OP_ADDR64 | HW_OWNER); sc_if->msk_cdata.msk_tx_cnt++; MSK_INC(prod, MSK_TX_RING_CNT); } #endif si = prod; tx_le = &sc_if->msk_rdata.msk_tx_ring[prod]; tx_le->msk_addr = htole32 (MSK_ADDR_LO (BusPhysAddr)); tx_le->msk_control = htole32 (BusLength | control | OP_PACKET); sc_if->msk_cdata.msk_tx_cnt++; MSK_INC (prod, MSK_TX_RING_CNT); // Update producer index sc_if->msk_cdata.msk_tx_prod = prod; // Set EOP on the last descriptor prod = (prod + MSK_TX_RING_CNT - 1) % MSK_TX_RING_CNT; tx_le = &sc_if->msk_rdata.msk_tx_ring[prod]; tx_le->msk_control |= htole32 (EOP); // Turn the first descriptor ownership to hardware tx_le = &sc_if->msk_rdata.msk_tx_ring[si]; tx_le->msk_control |= htole32 (HW_OWNER); txd = &sc_if->msk_cdata.msk_txdesc[prod]; Mapping = txd_last->tx_m.DmaMapping; txd_last->tx_m.DmaMapping = txd->tx_m.DmaMapping; txd->tx_m.DmaMapping = Mapping; txd->tx_m.Buf = m_head->Buf; txd->tx_m.Length = m_head->Length; return EFI_SUCCESS; } EFI_STATUS mskc_transmit ( struct msk_if_softc *sc_if, UINTN BufferSize, VOID *Buffer ) { MSK_LINKED_SYSTEM_BUF *LinkedSystemBuf; EFI_STATUS Status; Status = gBS->AllocatePool (EfiBootServicesData, sizeof (MSK_LINKED_SYSTEM_BUF), (VOID**) &LinkedSystemBuf); if (EFI_ERROR (Status)) { return Status; } gBS->SetMem (LinkedSystemBuf, sizeof (MSK_LINKED_SYSTEM_BUF), 0); LinkedSystemBuf->Signature = TX_MBUF_SIGNATURE; // // Add the passed Buffer to the transmit queue. Don't copy. // LinkedSystemBuf->SystemBuf.Buf = Buffer; LinkedSystemBuf->SystemBuf.Length = BufferSize; InsertTailList (&sc_if->TransmitQueueHead, &LinkedSystemBuf->Link); msk_start (sc_if); return EFI_SUCCESS; } void mskc_getstatus ( struct msk_if_softc *sc_if, OUT UINT32 *InterruptStatus, OPTIONAL OUT VOID **TxBuf OPTIONAL ) { MSK_LINKED_SYSTEM_BUF *m_head; // Interrupt status is not read from the device when InterruptStatus is NULL if (InterruptStatus != NULL) { // Check the interrupt lines msk_intr (sc_if->msk_softc); } // The transmit buffer status is not read when TxBuf is NULL if (TxBuf != NULL) { *((UINT8 **) TxBuf) = (UINT8 *) 0; if (!IsListEmpty (&sc_if->TransmitFreeQueueHead)) { m_head = CR (GetFirstNode (&sc_if->TransmitFreeQueueHead), MSK_LINKED_SYSTEM_BUF, Link, TX_MBUF_SIGNATURE); if(m_head != NULL) { *TxBuf = m_head->SystemBuf.Buf; RemoveEntryList (&m_head->Link); gBS->FreePool (m_head); } } } } static VOID msk_start ( struct msk_if_softc *sc_if ) { EFI_STATUS Status; MSK_LINKED_SYSTEM_BUF *m_head; INTN enq; for (enq = 0; !IsListEmpty (&sc_if->TransmitQueueHead) && sc_if->msk_cdata.msk_tx_cnt < (MSK_TX_RING_CNT - MSK_RESERVED_TX_DESC_CNT); ) { m_head = CR (GetFirstNode (&sc_if->TransmitQueueHead), MSK_LINKED_SYSTEM_BUF, Link, TX_MBUF_SIGNATURE); if (m_head == NULL) { break; } // // Pack the data into the transmit ring. If we // don't have room, set the OACTIVE flag and wait // for the NIC to drain the ring. // Status = msk_encap (sc_if, &m_head->SystemBuf); if (EFI_ERROR (Status)) { break; } RemoveEntryList (&m_head->Link); InsertTailList (&sc_if->TransmitFreeQueueHead, &m_head->Link); enq++; } if (enq > 0) { // Transmit CSR_WRITE_2 (sc_if->msk_softc, Y2_PREF_Q_ADDR (sc_if->msk_txq, PREF_UNIT_PUT_IDX_REG), sc_if->msk_cdata.msk_tx_prod); } } VOID mskc_shutdown ( struct msk_softc *sc ) { INTN i; EFI_TPL OldTpl; OldTpl = gBS->RaiseTPL (TPL_NOTIFY); for (i = 0; i < sc->msk_num_port; i++) { if (sc->msk_if[i] != NULL) { mskc_stop_if (sc->msk_if[i]); } } gBS->SetTimer (sc->Timer, TimerCancel, 0); /* Put hardware reset. */ CSR_WRITE_2 (sc, B0_CTST, CS_RST_SET); gBS->RestoreTPL (OldTpl); } EFI_STATUS mskc_receive ( struct msk_if_softc *sc_if, IN OUT UINTN *BufferSize, OUT VOID *Buffer ) { MSK_LINKED_SYSTEM_BUF *mBuf; msk_intr (sc_if->msk_softc); // check the interrupt lines if (IsListEmpty (&sc_if->ReceiveQueueHead)) { *BufferSize = 0; return EFI_NOT_READY; } mBuf = CR (GetFirstNode (&sc_if->ReceiveQueueHead), MSK_LINKED_SYSTEM_BUF, Link, RX_MBUF_SIGNATURE); if (mBuf->SystemBuf.Length > *BufferSize) { *BufferSize = mBuf->SystemBuf.Length; DEBUG ((EFI_D_NET, "Marvell Yukon: Receive buffer is too small: Provided = %d, Received = %d\n", *BufferSize, mBuf->SystemBuf.Length)); return EFI_BUFFER_TOO_SMALL; } *BufferSize = mBuf->SystemBuf.Length; RemoveEntryList (&mBuf->Link); gBS->CopyMem (Buffer, mBuf->SystemBuf.Buf, *BufferSize); gBS->FreePool(mBuf->SystemBuf.Buf); gBS->FreePool (mBuf); return EFI_SUCCESS; } static VOID msk_rxeof ( struct msk_if_softc *sc_if, UINT32 status, UINT32 control, INTN len ) { EFI_STATUS Status; MSK_LINKED_SYSTEM_BUF *m_link; struct msk_rxdesc *rxd; INTN cons; INTN rxlen; MSK_DMA_BUF m; EFI_PCI_IO_PROTOCOL *PciIo; DEBUG ((EFI_D_NET, "Marvell Yukon: rxeof\n")); PciIo = sc_if->msk_softc->PciIo; cons = sc_if->msk_cdata.msk_rx_cons; do { rxlen = status >> 16; if ((sc_if->msk_flags & MSK_FLAG_NORXCHK) != 0) { // // For controllers that returns bogus status code // just do minimal check and let upper stack // handle this frame. // if (len > MAX_SUPPORTED_PACKET_SIZE || len < NET_ETHER_ADDR_LEN) { msk_discard_rxbuf (sc_if, cons); break; } } else if (len > sc_if->msk_framesize || ((status & GMR_FS_ANY_ERR) != 0) || ((status & GMR_FS_RX_OK) == 0) || (rxlen != len)) { msk_discard_rxbuf (sc_if, cons); break; } #ifdef MSK_64BIT_DMA rxd = &sc_if->msk_cdata.msk_rxdesc[(cons + 1) % MSK_RX_RING_CNT]; #else rxd = &sc_if->msk_cdata.msk_rxdesc[cons]; #endif m.Buf = rxd->rx_m.Buf; m.DmaMapping = rxd->rx_m.DmaMapping; m.Length = rxd->rx_m.Length; Status = msk_newbuf (sc_if, cons); if (EFI_ERROR (Status)) { // This is a dropped packet, but we aren't counting drops // Reuse old buffer msk_discard_rxbuf (sc_if, cons); break; } Status = PciIo->Flush (PciIo); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_NET, "Marvell Yukon: failed to Flush DMA\n")); } Status = PciIo->Unmap (PciIo, rxd->rx_m.DmaMapping); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_NET, "Marvell Yukon: failed to Unmap DMA\n")); } Status = gBS->AllocatePool (EfiBootServicesData, sizeof (MSK_LINKED_SYSTEM_BUF), (VOID**) &m_link); if (!EFI_ERROR (Status)) { gBS->SetMem (m_link, sizeof (MSK_LINKED_SYSTEM_BUF), 0); m_link->Signature = RX_MBUF_SIGNATURE; m_link->SystemBuf.Buf = m.Buf; m_link->SystemBuf.Length = len; InsertTailList (&sc_if->ReceiveQueueHead, &m_link->Link); } else { DEBUG ((EFI_D_NET, "Marvell Yukon: failed to allocate receive buffer link. Dropping Frame\n")); gBS->FreePool (m.Buf); } } while (0); MSK_RX_INC (sc_if->msk_cdata.msk_rx_cons, MSK_RX_RING_CNT); MSK_RX_INC (sc_if->msk_cdata.msk_rx_prod, MSK_RX_RING_CNT); } static VOID msk_txeof ( struct msk_if_softc *sc_if, INTN idx ) { struct msk_txdesc *txd; struct msk_tx_desc *cur_tx; UINT32 control; INTN cons; INTN prog; EFI_PCI_IO_PROTOCOL *PciIo; DEBUG ((EFI_D_NET, "Marvell Yukon: txeof\n")); PciIo = sc_if->msk_softc->PciIo; // // Go through our tx ring and free mbufs for those // frames that have been sent. // cons = sc_if->msk_cdata.msk_tx_cons; prog = 0; for (; cons != idx; MSK_INC (cons, MSK_TX_RING_CNT)) { if (sc_if->msk_cdata.msk_tx_cnt <= 0) { break; } prog++; cur_tx = &sc_if->msk_rdata.msk_tx_ring[cons]; control = le32toh (cur_tx->msk_control); sc_if->msk_cdata.msk_tx_cnt--; if ((control & EOP) == 0) { continue; } txd = &sc_if->msk_cdata.msk_txdesc[cons]; PciIo->Unmap (PciIo, txd->tx_m.DmaMapping); gBS->SetMem (&(txd->tx_m), sizeof (MSK_DMA_BUF), 0); // We don't own the transmit buffers so don't free them } if (prog > 0) { sc_if->msk_cdata.msk_tx_cons = cons; // No need to sync LEs as we didn't update LEs. } } VOID mskc_tick ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_TPL OldTpl; struct msk_softc *sc; OldTpl = gBS->RaiseTPL (TPL_NOTIFY); sc = (struct msk_softc *)Context; if (sc->msk_if[MSK_PORT_A] != NULL && sc->msk_if[MSK_PORT_A]->active) { e1000phy_tick (sc->msk_if[MSK_PORT_A]->phy_softc); } if (sc->msk_if[MSK_PORT_B] != NULL && sc->msk_if[MSK_PORT_B]->active) { e1000phy_tick (sc->msk_if[MSK_PORT_B]->phy_softc); } msk_handle_events (sc); gBS->RestoreTPL (OldTpl); } static VOID msk_intr_phy ( struct msk_if_softc *sc_if ) { UINT16 status; msk_phy_readreg (sc_if, PHY_MARV_INT_STAT); status = msk_phy_readreg (sc_if, PHY_MARV_INT_STAT); // Handle FIFO Underrun/Overflow ? if ((status & PHY_M_IS_FIFO_ERROR)) { DEBUG ((EFI_D_NET, "Marvell Yukon: PHY FIFO underrun/overflow.\n")); } } static VOID msk_intr_gmac ( struct msk_if_softc *sc_if ) { UINT8 status; struct msk_softc *sc; sc = sc_if->msk_softc; status = CSR_READ_1 (sc, MR_ADDR (sc_if->msk_md.port, GMAC_IRQ_SRC)); // GMAC Rx FIFO overrun. if ((status & GM_IS_RX_FF_OR) != 0) { CSR_WRITE_4 (sc, MR_ADDR (sc_if->msk_md.port, RX_GMF_CTRL_T), GMF_CLI_RX_FO); } // GMAC Tx FIFO underrun. if ((status & GM_IS_TX_FF_UR) != 0) { CSR_WRITE_4 (sc, MR_ADDR (sc_if->msk_md.port, TX_GMF_CTRL_T), GMF_CLI_TX_FU); //device_printf (sc_if->msk_if_dev, "Tx FIFO underrun!\n");*/ DEBUG ((EFI_D_NET, "Marvell Yukon: Tx FIFO underrun!\n")); /* * XXX * In case of Tx underrun, we may need to flush/reset * Tx MAC but that would also require resynchronization * with status LEs. Reintializing status LEs would * affect other port in dual MAC configuration so it * should be aVOIDed as possible as we can. * Due to lack of documentation it's all vague guess but * it needs more investigation. */ } } static VOID msk_handle_hwerr ( struct msk_if_softc *sc_if, UINT32 status ) { struct msk_softc *sc; sc = sc_if->msk_softc; if ((status & Y2_IS_PAR_RD1) != 0) { DEBUG ((EFI_D_NET, "Marvell Yukon: RAM buffer read parity error\n")); // Clear IRQ. CSR_WRITE_2 (sc, SELECT_RAM_BUFFER (sc_if->msk_md.port, B3_RI_CTRL), RI_CLR_RD_PERR); } if ((status & Y2_IS_PAR_WR1) != 0) { DEBUG ((EFI_D_NET, "Marvell Yukon: RAM buffer write parity error\n")); // Clear IRQ CSR_WRITE_2 (sc, SELECT_RAM_BUFFER (sc_if->msk_md.port, B3_RI_CTRL), RI_CLR_WR_PERR); } if ((status & Y2_IS_PAR_MAC1) != 0) { DEBUG ((EFI_D_NET, "Marvell Yukon: Tx MAC parity error\n")); // Clear IRQ CSR_WRITE_4 (sc, MR_ADDR (sc_if->msk_md.port, TX_GMF_CTRL_T), GMF_CLI_TX_PE); } if ((status & Y2_IS_PAR_RX1) != 0) { DEBUG ((EFI_D_NET, "Marvell Yukon: Rx parity error\n")); // Clear IRQ CSR_WRITE_4 (sc, Q_ADDR (sc_if->msk_rxq, Q_CSR), BMU_CLR_IRQ_PAR); } if ((status & (Y2_IS_TCP_TXS1 | Y2_IS_TCP_TXA1)) != 0) { DEBUG ((EFI_D_NET, "Marvell Yukon: TCP segmentation error\n")); // Clear IRQ CSR_WRITE_4 (sc, Q_ADDR (sc_if->msk_txq, Q_CSR), BMU_CLR_IRQ_TCP); } } static VOID msk_intr_hwerr ( struct msk_softc *sc ) { UINT32 status; UINT32 tlphead[4]; status = CSR_READ_4 (sc, B0_HWE_ISRC); // Time Stamp timer overflow. if ((status & Y2_IS_TIST_OV) != 0) { CSR_WRITE_1 (sc, GMAC_TI_ST_CTRL, GMT_ST_CLR_IRQ); } if ((status & Y2_IS_PCI_NEXP) != 0) { /* * PCI Express Error occured which is not described in PEX * spec. * This error is also mapped either to Master Abort ( * Y2_IS_MST_ERR) or Target Abort (Y2_IS_IRQ_STAT) bit and * can only be cleared there. */ DEBUG ((EFI_D_NET, "Marvell Yukon: PCI Express protocol violation error\n")); } if ((status & (Y2_IS_MST_ERR | Y2_IS_IRQ_STAT)) != 0) { if ((status & Y2_IS_MST_ERR) != 0) { DEBUG ((EFI_D_NET, "Marvell Yukon: unexpected IRQ Status error\n")); } else { DEBUG ((EFI_D_NET, "Marvell Yukon: unexpected IRQ Master error\n")); } // Reset all bits in the PCI status register clear_pci_errors (sc); } // Check for PCI Express Uncorrectable Error. if ((status & Y2_IS_PCI_EXP) != 0) { UINT32 v32; /* * On PCI Express bus bridges are called root complexes (RC). * PCI Express errors are recognized by the root complex too, * which requests the system to handle the problem. After * error occurrence it may be that no access to the adapter * may be performed any longer. */ v32 = CSR_PCI_READ_4 (sc, PEX_UNC_ERR_STAT); if ((v32 & PEX_UNSUP_REQ) != 0) { // Ignore unsupported request error. DEBUG ((EFI_D_NET, "Marvell Yukon: Uncorrectable PCI Express error\n")); } if ((v32 & (PEX_FATAL_ERRORS | PEX_POIS_TLP)) != 0) { INTN i; // Get TLP header form Log Registers. for (i = 0; i < 4; i++) { tlphead[i] = CSR_PCI_READ_4 (sc, PEX_HEADER_LOG + i * 4); } // Check for vendor defined broadcast message. if (!(tlphead[0] == 0x73004001 && tlphead[1] == 0x7f)) { sc->msk_intrhwemask &= ~Y2_IS_PCI_EXP; CSR_WRITE_4 (sc, B0_HWE_IMSK, sc->msk_intrhwemask); CSR_READ_4 (sc, B0_HWE_IMSK); } } // Clear the interrupt CSR_WRITE_1 (sc, B2_TST_CTRL1, TST_CFG_WRITE_ON); CSR_PCI_WRITE_4 (sc, PEX_UNC_ERR_STAT, 0xffffffff); CSR_WRITE_1 (sc, B2_TST_CTRL1, TST_CFG_WRITE_OFF); } if ((status & Y2_HWE_L1_MASK) != 0 && sc->msk_if[MSK_PORT_A] != NULL) { msk_handle_hwerr (sc->msk_if[MSK_PORT_A], status); } if ((status & Y2_HWE_L2_MASK) != 0 && sc->msk_if[MSK_PORT_B] != NULL) { msk_handle_hwerr (sc->msk_if[MSK_PORT_B], status >> 8); } } static __inline VOID msk_rxput ( struct msk_if_softc *sc_if ) { CSR_WRITE_2 (sc_if->msk_softc, Y2_PREF_Q_ADDR (sc_if->msk_rxq, PREF_UNIT_PUT_IDX_REG), sc_if->msk_cdata.msk_rx_prod); } static INTN msk_handle_events ( struct msk_softc *sc ) { INTN rxput[2]; struct msk_stat_desc *sd; UINT32 control; UINT32 status; INTN cons; INTN len; INTN port; INTN rxprog; struct msk_if_softc *sc_if; if (sc->msk_stat_cons == CSR_READ_2 (sc, STAT_PUT_IDX)) { return (0); } rxput[MSK_PORT_A] = rxput[MSK_PORT_B] = 0; rxprog = 0; cons = sc->msk_stat_cons; for (;;) { sd = &sc->msk_stat_ring[cons]; control = le32toh (sd->msk_control); if ((control & HW_OWNER) == 0) { break; } control &= ~HW_OWNER; sd->msk_control = htole32 (control); status = le32toh (sd->msk_status); len = control & STLE_LEN_MASK; port = (control >> 16) & 0x01; sc_if = sc->msk_if[port]; if (sc_if == NULL) { DEBUG ((EFI_D_NET, "Marvell Yukon: invalid port opcode 0x%08x\n", control & STLE_OP_MASK)); continue; } switch (control & STLE_OP_MASK) { case OP_RXSTAT: msk_rxeof (sc_if, status, control, len); rxprog++; // // Because there is no way to sync single Rx LE // put the DMA sync operation off until the end of // event processing. // rxput[port]++; // Update prefetch unit if we've passed water mark if (rxput[port] >= sc_if->msk_cdata.msk_rx_putwm) { msk_rxput (sc_if); rxput[port] = 0; } break; case OP_TXINDEXLE: if (sc->msk_if[MSK_PORT_A] != NULL) { msk_txeof (sc->msk_if[MSK_PORT_A], status & STLE_TXA1_MSKL); } if (sc->msk_if[MSK_PORT_B] != NULL) { msk_txeof (sc->msk_if[MSK_PORT_B], ((status & STLE_TXA2_MSKL) >> STLE_TXA2_SHIFTL) | ((len & STLE_TXA2_MSKH) << STLE_TXA2_SHIFTH)); } break; default: DEBUG ((EFI_D_NET, "Marvell Yukon: unhandled opcode 0x%08x\n", control & STLE_OP_MASK)); break; } MSK_INC (cons, MSK_STAT_RING_CNT); if (rxprog > sc->msk_process_limit) { break; } } sc->msk_stat_cons = cons; if (rxput[MSK_PORT_A] > 0) { msk_rxput (sc->msk_if[MSK_PORT_A]); } if (rxput[MSK_PORT_B] > 0) { msk_rxput (sc->msk_if[MSK_PORT_B]); } return (sc->msk_stat_cons != CSR_READ_2 (sc, STAT_PUT_IDX)); } STATIC VOID msk_intr ( struct msk_softc *sc ) { struct msk_if_softc *sc_if0; struct msk_if_softc *sc_if1; UINT32 Status; INTN domore; // Reading B0_Y2_SP_ISRC2 masks further interrupts Status = CSR_READ_4 (sc, B0_Y2_SP_ISRC2); if (Status == 0 || Status == 0xffffffff || (sc->msk_pflags & MSK_FLAG_SUSPEND) != 0 || (Status & sc->msk_intrmask) == 0) { // Leave ISR - Reenable interrupts CSR_WRITE_4 (sc, B0_Y2_SP_ICR, 2); return; } sc_if0 = sc->msk_if[MSK_PORT_A]; sc_if1 = sc->msk_if[MSK_PORT_B]; if ((Status & Y2_IS_IRQ_PHY1) != 0 && sc_if0 != NULL) { msk_intr_phy (sc_if0); } if ((Status & Y2_IS_IRQ_PHY2) != 0 && sc_if1 != NULL) { msk_intr_phy (sc_if1); } if ((Status & Y2_IS_IRQ_MAC1) != 0 && sc_if0 != NULL) { msk_intr_gmac (sc_if0); } if ((Status & Y2_IS_IRQ_MAC2) != 0 && sc_if1 != NULL) { msk_intr_gmac (sc_if1); } if ((Status & (Y2_IS_CHK_RX1 | Y2_IS_CHK_RX2)) != 0) { DEBUG ((EFI_D_NET, "Marvell Yukon: Rx descriptor error\n")); sc->msk_intrmask &= ~(Y2_IS_CHK_RX1 | Y2_IS_CHK_RX2); CSR_WRITE_4 (sc, B0_IMSK, sc->msk_intrmask); CSR_READ_4 (sc, B0_IMSK); } if ((Status & (Y2_IS_CHK_TXA1 | Y2_IS_CHK_TXA2)) != 0) { DEBUG ((EFI_D_NET, "Marvell Yukon: Tx descriptor error\n")); sc->msk_intrmask &= ~(Y2_IS_CHK_TXA1 | Y2_IS_CHK_TXA2); CSR_WRITE_4 (sc, B0_IMSK, sc->msk_intrmask); CSR_READ_4 (sc, B0_IMSK); } if ((Status & Y2_IS_HW_ERR) != 0) { msk_intr_hwerr (sc); } domore = msk_handle_events (sc); if ((Status & Y2_IS_STAT_BMU) != 0 && domore == 0) { CSR_WRITE_4 (sc, STAT_CTRL, SC_STAT_CLR_IRQ); } // Leave ISR - Reenable interrupts CSR_WRITE_4 (sc, B0_Y2_SP_ICR, 2); } static VOID msk_set_tx_stfwd ( struct msk_if_softc *sc_if ) { // Disable jumbo frames for Tx CSR_WRITE_4 (sc_if->msk_softc, MR_ADDR (sc_if->msk_md.port, TX_GMF_CTRL_T), TX_JUMBO_DIS | TX_STFW_ENA); } EFI_STATUS mskc_init ( struct msk_if_softc *sc_if ) { EFI_STATUS Status; Status = msk_init (sc_if); if (EFI_ERROR (Status)) { return Status; } return Status; } static EFI_STATUS msk_init ( IN struct msk_if_softc *sc_if ) { UINT8 *eaddr; UINT16 gmac; UINT32 reg; EFI_STATUS Status; INTN port; IN struct msk_softc *sc; sc = sc_if->msk_softc; port = sc_if->msk_md.port; // Cancel pending I/O and free all Rx/Tx buffers. mskc_stop_if (sc_if); sc_if->msk_framesize = MAX_SUPPORTED_PACKET_SIZE; // GMAC Control reset. CSR_WRITE_4 (sc, MR_ADDR (port, GMAC_CTRL), GMC_RST_SET); CSR_WRITE_4 (sc, MR_ADDR (port, GMAC_CTRL), GMC_RST_CLR); CSR_WRITE_4 (sc, MR_ADDR (port, GMAC_CTRL), GMC_F_LOOPB_OFF); if (sc->msk_hw_id == CHIP_ID_YUKON_EX) { CSR_WRITE_4 (sc, MR_ADDR (port, GMAC_CTRL), GMC_BYP_MACSECRX_ON | GMC_BYP_MACSECTX_ON | GMC_BYP_RETR_ON); } // // Initialize GMAC first such that speed/duplex/flow-control // parameters are renegotiated when interface is brought up. // GMAC_WRITE_2 (sc, port, GM_GP_CTRL, 0); // Dummy read the Interrupt Source Register CSR_READ_1 (sc, MR_ADDR (port, GMAC_IRQ_SRC)); // Clear MIB stats msk_stats_clear (sc_if); // Disable FCS GMAC_WRITE_2 (sc, port, GM_RX_CTRL, GM_RXCR_CRC_DIS); // Setup Transmit Control Register GMAC_WRITE_2 (sc, port, GM_TX_CTRL, TX_COL_THR (TX_COL_DEF)); // Setup Transmit Flow Control Register GMAC_WRITE_2 (sc, port, GM_TX_FLOW_CTRL, 0xffff); // Setup Transmit Parameter Register GMAC_WRITE_2 (sc, port, GM_TX_PARAM, TX_JAM_LEN_VAL (TX_JAM_LEN_DEF) | TX_JAM_IPG_VAL (TX_JAM_IPG_DEF) | TX_IPG_JAM_DATA(TX_IPG_JAM_DEF) | TX_BACK_OFF_LIM(TX_BOF_LIM_DEF)); gmac = DATA_BLIND_VAL (DATA_BLIND_DEF) | GM_SMOD_VLAN_ENA | IPG_DATA_VAL (IPG_DATA_DEF); GMAC_WRITE_2 (sc, port, GM_SERIAL_MODE, gmac); // Set station address eaddr = sc_if->MacAddress.Addr; GMAC_WRITE_2 (sc, port, GM_SRC_ADDR_1L, eaddr[0] | (eaddr[1] << 8)); GMAC_WRITE_2 (sc, port, GM_SRC_ADDR_1M, eaddr[2] | (eaddr[3] << 8)); GMAC_WRITE_2 (sc, port, GM_SRC_ADDR_1H, eaddr[4] | (eaddr[5] << 8)); GMAC_WRITE_2 (sc, port, GM_SRC_ADDR_2L, eaddr[0] | (eaddr[1] << 8)); GMAC_WRITE_2 (sc, port, GM_SRC_ADDR_2M, eaddr[2] | (eaddr[3] << 8)); GMAC_WRITE_2 (sc, port, GM_SRC_ADDR_2H, eaddr[4] | (eaddr[5] << 8)); // Disable interrupts for counter overflows GMAC_WRITE_2 (sc, port, GM_TX_IRQ_MSK, 0); GMAC_WRITE_2 (sc, port, GM_RX_IRQ_MSK, 0); GMAC_WRITE_2 (sc, port, GM_TR_IRQ_MSK, 0); // Configure Rx MAC FIFO CSR_WRITE_4 (sc, MR_ADDR (port, RX_GMF_CTRL_T), GMF_RST_SET); CSR_WRITE_4 (sc, MR_ADDR (port, RX_GMF_CTRL_T), GMF_RST_CLR); reg = GMF_OPER_ON | GMF_RX_F_FL_ON; if (sc->msk_hw_id == CHIP_ID_YUKON_FE_P || sc->msk_hw_id == CHIP_ID_YUKON_EX) { reg |= GMF_RX_OVER_ON; } CSR_WRITE_4 (sc, MR_ADDR (port, RX_GMF_CTRL_T), reg); if (sc->msk_hw_id == CHIP_ID_YUKON_XL) { // Clear flush mask - HW bug CSR_WRITE_4 (sc, MR_ADDR (port, RX_GMF_FL_MSK), 0); } else { // Flush Rx MAC FIFO on any flow control or error CSR_WRITE_4 (sc, MR_ADDR (port, RX_GMF_FL_MSK), GMR_FS_ANY_ERR); } // // Set Rx FIFO flush threshold to 64 bytes + 1 FIFO word // due to hardware hang on receipt of pause frames. // reg = RX_GMF_FL_THR_DEF + 1; // Another magic for Yukon FE+ - From Linux if (sc->msk_hw_id == CHIP_ID_YUKON_FE_P && sc->msk_hw_rev == CHIP_REV_YU_FE_P_A0) { reg = 0x178; } CSR_WRITE_2 (sc, MR_ADDR (port, RX_GMF_FL_THR), reg); // Configure Tx MAC FIFO CSR_WRITE_4 (sc, MR_ADDR (port, TX_GMF_CTRL_T), GMF_RST_SET); CSR_WRITE_4 (sc, MR_ADDR (port, TX_GMF_CTRL_T), GMF_RST_CLR); CSR_WRITE_4 (sc, MR_ADDR (port, TX_GMF_CTRL_T), GMF_OPER_ON); // Configure hardware VLAN tag insertion/stripping msk_setvlan (sc_if); if ((sc_if->msk_flags & MSK_FLAG_RAMBUF) == 0) { // Set Rx Pause threshould. CSR_WRITE_2 (sc, MR_ADDR (port, RX_GMF_LP_THR), MSK_ECU_LLPP); CSR_WRITE_2 (sc, MR_ADDR (port, RX_GMF_UP_THR), MSK_ECU_ULPP); // Configure store-and-forward for Tx. msk_set_tx_stfwd (sc_if); } if (sc->msk_hw_id == CHIP_ID_YUKON_FE_P && sc->msk_hw_rev == CHIP_REV_YU_FE_P_A0) { // Disable dynamic watermark - from Linux reg = CSR_READ_4 (sc, MR_ADDR (port, TX_GMF_EA)); reg &= ~0x03; CSR_WRITE_4 (sc, MR_ADDR (port, TX_GMF_EA), reg); } // // Disable Force Sync bit and Alloc bit in Tx RAM interface // arbiter as we don't use Sync Tx queue. // CSR_WRITE_1 (sc, MR_ADDR (port, TXA_CTRL), TXA_DIS_FSYNC | TXA_DIS_ALLOC | TXA_STOP_RC); // Enable the RAM Interface Arbiter CSR_WRITE_1 (sc, MR_ADDR (port, TXA_CTRL), TXA_ENA_ARB); // Setup RAM buffer msk_set_rambuffer (sc_if); // Disable Tx sync Queue CSR_WRITE_1 (sc, RB_ADDR (sc_if->msk_txsq, RB_CTRL), RB_RST_SET); // Setup Tx Queue Bus Memory Interface CSR_WRITE_4 (sc, Q_ADDR (sc_if->msk_txq, Q_CSR), BMU_CLR_RESET); CSR_WRITE_4 (sc, Q_ADDR (sc_if->msk_txq, Q_CSR), BMU_OPER_INIT); CSR_WRITE_4 (sc, Q_ADDR (sc_if->msk_txq, Q_CSR), BMU_FIFO_OP_ON); CSR_WRITE_2 (sc, Q_ADDR (sc_if->msk_txq, Q_WM), MSK_BMU_TX_WM); switch (sc->msk_hw_id) { case CHIP_ID_YUKON_EC_U: if (sc->msk_hw_rev == CHIP_REV_YU_EC_U_A0) { // Fix for Yukon-EC Ultra: set BMU FIFO level CSR_WRITE_2 (sc, Q_ADDR (sc_if->msk_txq, Q_AL), MSK_ECU_TXFF_LEV); } break; case CHIP_ID_YUKON_EX: // // Yukon Extreme seems to have silicon bug for // automatic Tx checksum calculation capability. // if (sc->msk_hw_rev == CHIP_REV_YU_EX_B0) { CSR_WRITE_4 (sc, Q_ADDR (sc_if->msk_txq, Q_F), F_TX_CHK_AUTO_OFF); } break; } // Setup Rx Queue Bus Memory Interface CSR_WRITE_4 (sc, Q_ADDR (sc_if->msk_rxq, Q_CSR), BMU_CLR_RESET); CSR_WRITE_4 (sc, Q_ADDR (sc_if->msk_rxq, Q_CSR), BMU_OPER_INIT); CSR_WRITE_4 (sc, Q_ADDR (sc_if->msk_rxq, Q_CSR), BMU_FIFO_OP_ON); CSR_WRITE_2 (sc, Q_ADDR (sc_if->msk_rxq, Q_WM), MSK_BMU_RX_WM); if (sc->msk_hw_id == CHIP_ID_YUKON_EC_U && sc->msk_hw_rev >= CHIP_REV_YU_EC_U_A1) { // MAC Rx RAM Read is controlled by hardware CSR_WRITE_4 (sc, Q_ADDR (sc_if->msk_rxq, Q_F), F_M_RX_RAM_DIS); } // truncate too-large frames - from linux CSR_WRITE_4 (sc, MR_ADDR (port, RX_GMF_TR_THR), 0x17a); CSR_WRITE_4 (sc, MR_ADDR (port, RX_GMF_CTRL_T), RX_TRUNC_ON); msk_set_prefetch (sc_if, sc_if->msk_txq, sc_if->msk_rdata.msk_tx_ring_paddr, MSK_TX_RING_CNT - 1); msk_init_tx_ring (sc_if); // Disable Rx checksum offload and RSS hash CSR_WRITE_4 (sc, Q_ADDR (sc_if->msk_rxq, Q_CSR), BMU_DIS_RX_CHKSUM | BMU_DIS_RX_RSS_HASH); msk_set_prefetch (sc_if, sc_if->msk_rxq, sc_if->msk_rdata.msk_rx_ring_paddr, MSK_RX_RING_CNT - 1); Status = msk_init_rx_ring (sc_if); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Marvell Yukon: Initialization failed: no memory for Rx buffers\n")); mskc_stop_if (sc_if); return Status; } if (sc->msk_hw_id == CHIP_ID_YUKON_EX) { // Disable flushing of non-ASF packets CSR_WRITE_4 (sc, MR_ADDR (port, RX_GMF_CTRL_T), GMF_RX_MACSEC_FLUSH_OFF); } // Configure interrupt handling if (port == MSK_PORT_A) { sc->msk_intrmask |= Y2_IS_PORT_A; sc->msk_intrhwemask |= Y2_HWE_L1_MASK; } else { sc->msk_intrmask |= Y2_IS_PORT_B; sc->msk_intrhwemask |= Y2_HWE_L2_MASK; } // Configure IRQ moderation mask. CSR_WRITE_4 (sc, B2_IRQM_MSK, sc->msk_intrmask); if (sc->msk_int_holdoff > 0) { // Configure initial IRQ moderation timer value. CSR_WRITE_4 (sc, B2_IRQM_INI, MSK_USECS (sc, sc->msk_int_holdoff)); CSR_WRITE_4 (sc, B2_IRQM_VAL, MSK_USECS (sc, sc->msk_int_holdoff)); // Start IRQ moderation. CSR_WRITE_1 (sc, B2_IRQM_CTRL, TIM_START); } CSR_WRITE_4 (sc, B0_HWE_IMSK, sc->msk_intrhwemask); CSR_READ_4 (sc, B0_HWE_IMSK); CSR_WRITE_4 (sc, B0_IMSK, sc->msk_intrmask); CSR_READ_4 (sc, B0_IMSK); sc_if->msk_flags &= ~MSK_FLAG_LINK; e1000phy_mediachg (sc_if->phy_softc); return Status; } STATIC VOID msk_set_rambuffer ( struct msk_if_softc *sc_if ) { INTN ltpp, utpp; INTN port; struct msk_softc *sc; sc = sc_if->msk_softc; port = sc_if->msk_md.port; if ((sc_if->msk_flags & MSK_FLAG_RAMBUF) == 0) return; // Setup Rx Queue CSR_WRITE_1 (sc, RB_ADDR (sc_if->msk_rxq, RB_CTRL), RB_RST_CLR); CSR_WRITE_4 (sc, RB_ADDR (sc_if->msk_rxq, RB_START), sc->msk_rxqstart[port] / 8); CSR_WRITE_4 (sc, RB_ADDR (sc_if->msk_rxq, RB_END), sc->msk_rxqend[port] / 8); CSR_WRITE_4 (sc, RB_ADDR (sc_if->msk_rxq, RB_WP), sc->msk_rxqstart[port] / 8); CSR_WRITE_4 (sc, RB_ADDR (sc_if->msk_rxq, RB_RP), sc->msk_rxqstart[port] / 8); utpp = (sc->msk_rxqend[port] + 1 - sc->msk_rxqstart[port] - MSK_RB_ULPP) / 8; ltpp = (sc->msk_rxqend[port] + 1 - sc->msk_rxqstart[port] - MSK_RB_LLPP_B) / 8; if (sc->msk_rxqsize < MSK_MIN_RXQ_SIZE) { ltpp += (MSK_RB_LLPP_B - MSK_RB_LLPP_S) / 8; } CSR_WRITE_4 (sc, RB_ADDR (sc_if->msk_rxq, RB_RX_UTPP), utpp); CSR_WRITE_4 (sc, RB_ADDR (sc_if->msk_rxq, RB_RX_LTPP), ltpp); // Set Rx priority (RB_RX_UTHP/RB_RX_LTHP) thresholds? CSR_WRITE_1 (sc, RB_ADDR (sc_if->msk_rxq, RB_CTRL), RB_ENA_OP_MD); CSR_READ_1 (sc, RB_ADDR (sc_if->msk_rxq, RB_CTRL)); // Setup Tx Queue. CSR_WRITE_1 (sc, RB_ADDR (sc_if->msk_txq, RB_CTRL), RB_RST_CLR); CSR_WRITE_4 (sc, RB_ADDR (sc_if->msk_txq, RB_START), sc->msk_txqstart[port] / 8); CSR_WRITE_4 (sc, RB_ADDR (sc_if->msk_txq, RB_END), sc->msk_txqend[port] / 8); CSR_WRITE_4 (sc, RB_ADDR (sc_if->msk_txq, RB_WP), sc->msk_txqstart[port] / 8); CSR_WRITE_4 (sc, RB_ADDR (sc_if->msk_txq, RB_RP), sc->msk_txqstart[port] / 8); // Enable Store & Forward for Tx side CSR_WRITE_1 (sc, RB_ADDR (sc_if->msk_txq, RB_CTRL), RB_ENA_STFWD); CSR_WRITE_1 (sc, RB_ADDR (sc_if->msk_txq, RB_CTRL), RB_ENA_OP_MD); CSR_READ_1 (sc, RB_ADDR (sc_if->msk_txq, RB_CTRL)); } STATIC VOID msk_set_prefetch ( struct msk_if_softc *sc_if, INTN qaddr, EFI_PHYSICAL_ADDRESS addr, UINT32 count ) { struct msk_softc *sc; sc = sc_if->msk_softc; // Reset the prefetch unit CSR_WRITE_4 (sc, Y2_PREF_Q_ADDR (qaddr, PREF_UNIT_CTRL_REG), PREF_UNIT_RST_SET); CSR_WRITE_4 (sc, Y2_PREF_Q_ADDR (qaddr, PREF_UNIT_CTRL_REG), PREF_UNIT_RST_CLR); // Set LE base address CSR_WRITE_4 (sc, Y2_PREF_Q_ADDR (qaddr, PREF_UNIT_ADDR_LOW_REG), MSK_ADDR_LO (addr)); CSR_WRITE_4 (sc, Y2_PREF_Q_ADDR (qaddr, PREF_UNIT_ADDR_HI_REG), MSK_ADDR_HI (addr)); // Set the list last index CSR_WRITE_2 (sc, Y2_PREF_Q_ADDR (qaddr, PREF_UNIT_LAST_IDX_REG), count); // Turn on prefetch unit CSR_WRITE_4 (sc, Y2_PREF_Q_ADDR (qaddr, PREF_UNIT_CTRL_REG), PREF_UNIT_OP_ON); // Dummy read to ensure write CSR_READ_4 (sc, Y2_PREF_Q_ADDR (qaddr, PREF_UNIT_CTRL_REG)); } VOID mskc_stop_if ( struct msk_if_softc *sc_if ) { struct msk_txdesc *txd; struct msk_rxdesc *rxd; UINT32 val; INTN i; INTN port; EFI_PCI_IO_PROTOCOL *PciIo; struct msk_softc *sc; sc = sc_if->msk_softc; PciIo = sc->PciIo; port = sc_if->msk_md.port; // Disable interrupts if (port == MSK_PORT_A) { sc->msk_intrmask &= ~Y2_IS_PORT_A; sc->msk_intrhwemask &= ~Y2_HWE_L1_MASK; } else { sc->msk_intrmask &= ~Y2_IS_PORT_B; sc->msk_intrhwemask &= ~Y2_HWE_L2_MASK; } CSR_WRITE_4 (sc, B0_HWE_IMSK, sc->msk_intrhwemask); CSR_READ_4 (sc, B0_HWE_IMSK); CSR_WRITE_4 (sc, B0_IMSK, sc->msk_intrmask); CSR_READ_4 (sc, B0_IMSK); // Disable Tx/Rx MAC. val = GMAC_READ_2 (sc, port, GM_GP_CTRL); val &= ~(GM_GPCR_RX_ENA | GM_GPCR_TX_ENA); GMAC_WRITE_2 (sc, port, GM_GP_CTRL, val); // Read again to ensure writing. GMAC_READ_2 (sc, port, GM_GP_CTRL); // Update stats and clear counters msk_stats_update (sc_if); // Stop Tx BMU CSR_WRITE_4 (sc, Q_ADDR (sc_if->msk_txq, Q_CSR), BMU_STOP); val = CSR_READ_4 (sc, Q_ADDR (sc_if->msk_txq, Q_CSR)); for (i = 0; i < MSK_TIMEOUT; i++) { if ((val & (BMU_STOP | BMU_IDLE)) == 0) { CSR_WRITE_4 (sc, Q_ADDR (sc_if->msk_txq, Q_CSR), BMU_STOP); val = CSR_READ_4 (sc, Q_ADDR (sc_if->msk_txq, Q_CSR)); } else { break; } gBS->Stall (1); } if (i == MSK_TIMEOUT) { DEBUG ((EFI_D_NET, "Marvell Yukon: Tx BMU stop failed\n")); } CSR_WRITE_1 (sc, RB_ADDR (sc_if->msk_txq, RB_CTRL), RB_RST_SET | RB_DIS_OP_MD); // Disable all GMAC interrupt. CSR_WRITE_1 (sc, MR_ADDR (port, GMAC_IRQ_MSK), 0); // Disable PHY interrupt. */ msk_phy_writereg (sc_if, PHY_MARV_INT_MASK, 0); // Disable the RAM Interface Arbiter. CSR_WRITE_1 (sc, MR_ADDR (port, TXA_CTRL), TXA_DIS_ARB); // Reset the PCI FIFO of the async Tx queue CSR_WRITE_4 (sc, Q_ADDR (sc_if->msk_txq, Q_CSR), BMU_RST_SET | BMU_FIFO_RST); // Reset the Tx prefetch units CSR_WRITE_4 (sc, Y2_PREF_Q_ADDR (sc_if->msk_txq, PREF_UNIT_CTRL_REG), PREF_UNIT_RST_SET); // Reset the RAM Buffer async Tx queue CSR_WRITE_1 (sc, RB_ADDR (sc_if->msk_txq, RB_CTRL), RB_RST_SET); // Reset Tx MAC FIFO. CSR_WRITE_4 (sc, MR_ADDR (port, TX_GMF_CTRL_T), GMF_RST_SET); // Set Pause Off. CSR_WRITE_4 (sc, MR_ADDR (port, GMAC_CTRL), GMC_PAUSE_OFF); /* * The Rx Stop command will not work for Yukon-2 if the BMU does not * reach the end of packet and since we can't make sure that we have * incoming data, we must reset the BMU while it is not during a DMA * transfer. Since it is possible that the Rx path is still active, * the Rx RAM buffer will be stopped first, so any possible incoming * data will not trigger a DMA. After the RAM buffer is stopped, the * BMU is polled until any DMA in progress is ended and only then it * will be reset. */ // Disable the RAM Buffer receive queue CSR_WRITE_1 (sc, RB_ADDR (sc_if->msk_rxq, RB_CTRL), RB_DIS_OP_MD); for (i = 0; i < MSK_TIMEOUT; i++) { if (CSR_READ_1 (sc, RB_ADDR (sc_if->msk_rxq, Q_RSL)) == CSR_READ_1 (sc, RB_ADDR (sc_if->msk_rxq, Q_RL))) { break; } gBS->Stall (1); } if (i == MSK_TIMEOUT) { DEBUG ((EFI_D_NET, "Marvell Yukon: Rx BMU stop failed\n")); } CSR_WRITE_4 (sc, Q_ADDR (sc_if->msk_rxq, Q_CSR), BMU_RST_SET | BMU_FIFO_RST); // Reset the Rx prefetch unit. CSR_WRITE_4 (sc, Y2_PREF_Q_ADDR (sc_if->msk_rxq, PREF_UNIT_CTRL_REG), PREF_UNIT_RST_SET); // Reset the RAM Buffer receive queue. CSR_WRITE_1 (sc, RB_ADDR (sc_if->msk_rxq, RB_CTRL), RB_RST_SET); // Reset Rx MAC FIFO. CSR_WRITE_4 (sc, MR_ADDR (port, RX_GMF_CTRL_T), GMF_RST_SET); // Free Rx and Tx mbufs still in the queues for (i = 0; i < MSK_RX_RING_CNT; i++) { rxd = &sc_if->msk_cdata.msk_rxdesc[i]; if (rxd->rx_m.Buf != NULL) { PciIo->Unmap (PciIo, rxd->rx_m.DmaMapping); if(rxd->rx_m.Buf != NULL) { gBS->FreePool (rxd->rx_m.Buf); rxd->rx_m.Buf = NULL; } gBS->SetMem (&(rxd->rx_m), sizeof (MSK_DMA_BUF), 0); } } for (i = 0; i < MSK_TX_RING_CNT; i++) { txd = &sc_if->msk_cdata.msk_txdesc[i]; if (txd->tx_m.Buf != NULL) { PciIo->Unmap (PciIo, txd->tx_m.DmaMapping); gBS->SetMem (&(txd->tx_m), sizeof (MSK_DMA_BUF), 0); // We don't own the transmit buffers so don't free them } } /* * Mark the interface down. */ sc_if->msk_flags &= ~MSK_FLAG_LINK; } /* * When GM_PAR_MIB_CLR bit of GM_PHY_ADDR is set, reading lower * counter clears high 16 bits of the counter such that accessing * lower 16 bits should be the last operation. */ #define MSK_READ_MIB32(x, y) (((UINT32)GMAC_READ_2 (sc, x, (y) + 4)) << 16) + (UINT32)GMAC_READ_2 (sc, x, y) #define MSK_READ_MIB64(x, y) (((UINT64)MSK_READ_MIB32 (x, (y) + 8)) << 32) + (UINT64)MSK_READ_MIB32 (x, y) static VOID msk_stats_clear ( struct msk_if_softc *sc_if ) { UINT16 gmac; INTN val; INTN i; INTN port; struct msk_softc *sc; sc = sc_if->msk_softc; port = sc_if->msk_md.port; // Set MIB Clear Counter Mode. gmac = GMAC_READ_2 (sc, port, GM_PHY_ADDR); GMAC_WRITE_2 (sc, port, GM_PHY_ADDR, gmac | GM_PAR_MIB_CLR); // Read all MIB Counters with Clear Mode set for (i = GM_RXF_UC_OK; i <= GM_TXE_FIFO_UR; i += sizeof (UINT32)) { val = MSK_READ_MIB32 (port, i); if (val); //Workaround: to prevent the GCC error: 'value computed is not used' } // Clear MIB Clear Counter Mode gmac &= ~GM_PAR_MIB_CLR; GMAC_WRITE_2 (sc, port, GM_PHY_ADDR, gmac); } static VOID msk_stats_update ( struct msk_if_softc *sc_if ) { struct msk_hw_stats *stats; UINT16 gmac; INTN val; INTN port; struct msk_softc *sc; sc = sc_if->msk_softc; port = sc_if->msk_md.port; stats = &sc_if->msk_stats; /* Set MIB Clear Counter Mode. */ gmac = GMAC_READ_2 (sc, port, GM_PHY_ADDR); GMAC_WRITE_2 (sc, port, GM_PHY_ADDR, gmac | GM_PAR_MIB_CLR); /* Rx stats. */ stats->rx_ucast_frames += MSK_READ_MIB32 (port, GM_RXF_UC_OK); stats->rx_bcast_frames += MSK_READ_MIB32 (port, GM_RXF_BC_OK); stats->rx_pause_frames += MSK_READ_MIB32 (port, GM_RXF_MPAUSE); stats->rx_mcast_frames += MSK_READ_MIB32 (port, GM_RXF_MC_OK); stats->rx_crc_errs += MSK_READ_MIB32 (port, GM_RXF_FCS_ERR); val = MSK_READ_MIB32 (port, GM_RXF_SPARE1); stats->rx_good_octets += MSK_READ_MIB64 (port, GM_RXO_OK_LO); stats->rx_bad_octets += MSK_READ_MIB64 (port, GM_RXO_ERR_LO); stats->rx_runts += MSK_READ_MIB32 (port, GM_RXF_SHT); stats->rx_runt_errs += MSK_READ_MIB32 (port, GM_RXE_FRAG); stats->rx_pkts_64 += MSK_READ_MIB32 (port, GM_RXF_64B); stats->rx_pkts_65_127 += MSK_READ_MIB32 (port, GM_RXF_127B); stats->rx_pkts_128_255 += MSK_READ_MIB32 (port, GM_RXF_255B); stats->rx_pkts_256_511 += MSK_READ_MIB32 (port, GM_RXF_511B); stats->rx_pkts_512_1023 += MSK_READ_MIB32 (port, GM_RXF_1023B); stats->rx_pkts_1024_1518 += MSK_READ_MIB32 (port, GM_RXF_1518B); stats->rx_pkts_1519_max += MSK_READ_MIB32 (port, GM_RXF_MAX_SZ); stats->rx_pkts_too_long += MSK_READ_MIB32 (port, GM_RXF_LNG_ERR); stats->rx_pkts_jabbers += MSK_READ_MIB32 (port, GM_RXF_JAB_PKT); val = MSK_READ_MIB32 (port, GM_RXF_SPARE2); stats->rx_fifo_oflows += MSK_READ_MIB32 (port, GM_RXE_FIFO_OV); val = MSK_READ_MIB32 (port, GM_RXF_SPARE3); /* Tx stats. */ stats->tx_ucast_frames += MSK_READ_MIB32 (port, GM_TXF_UC_OK); stats->tx_bcast_frames += MSK_READ_MIB32 (port, GM_TXF_BC_OK); stats->tx_pause_frames += MSK_READ_MIB32 (port, GM_TXF_MPAUSE); stats->tx_mcast_frames += MSK_READ_MIB32 (port, GM_TXF_MC_OK); stats->tx_octets += MSK_READ_MIB64 (port, GM_TXO_OK_LO); stats->tx_pkts_64 += MSK_READ_MIB32 (port, GM_TXF_64B); stats->tx_pkts_65_127 += MSK_READ_MIB32 (port, GM_TXF_127B); stats->tx_pkts_128_255 += MSK_READ_MIB32 (port, GM_TXF_255B); stats->tx_pkts_256_511 += MSK_READ_MIB32 (port, GM_TXF_511B); stats->tx_pkts_512_1023 += MSK_READ_MIB32 (port, GM_TXF_1023B); stats->tx_pkts_1024_1518 += MSK_READ_MIB32 (port, GM_TXF_1518B); stats->tx_pkts_1519_max += MSK_READ_MIB32 (port, GM_TXF_MAX_SZ); val = MSK_READ_MIB32 (port, GM_TXF_SPARE1); stats->tx_colls += MSK_READ_MIB32 (port, GM_TXF_COL); stats->tx_late_colls += MSK_READ_MIB32 (port, GM_TXF_LAT_COL); stats->tx_excess_colls += MSK_READ_MIB32 (port, GM_TXF_ABO_COL); stats->tx_multi_colls += MSK_READ_MIB32 (port, GM_TXF_MUL_COL); stats->tx_single_colls += MSK_READ_MIB32 (port, GM_TXF_SNG_COL); stats->tx_underflows += MSK_READ_MIB32 (port, GM_TXE_FIFO_UR); if (val); //Workaround: to prevent the GCC error: 'value computed is not used' /* Clear MIB Clear Counter Mode. */ gmac &= ~GM_PAR_MIB_CLR; GMAC_WRITE_2 (sc, port, GM_PHY_ADDR, gmac); } #undef MSK_READ_MIB32 #undef MSK_READ_MIB64