aboutsummaryrefslogtreecommitdiffstats
path: root/Drivers/Net/MarvellYukonDxe/e1000phy.c
diff options
context:
space:
mode:
Diffstat (limited to 'Drivers/Net/MarvellYukonDxe/e1000phy.c')
-rw-r--r--Drivers/Net/MarvellYukonDxe/e1000phy.c621
1 files changed, 621 insertions, 0 deletions
diff --git a/Drivers/Net/MarvellYukonDxe/e1000phy.c b/Drivers/Net/MarvellYukonDxe/e1000phy.c
new file mode 100644
index 0000000..c71ef3d
--- /dev/null
+++ b/Drivers/Net/MarvellYukonDxe/e1000phy.c
@@ -0,0 +1,621 @@
+/** <at> file
+* Support for Marvell 88E1000 Series PHYs
+*
+* 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.
+*
+**/
+/*-
+ * Principal Author: Parag Patel
+ * Copyright (c) 2001
+ * 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 unmodified, 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * Additonal Copyright (c) 2001 by Traakan Software under same licence.
+ * Secondary Author: Matthew Jacob
+ */
+
+/*
+ * driver for the Marvell 88E1000 series external 1000/100/10-BT PHY.
+ */
+
+/*
+ * Support added for the Marvell 88E1011 (Alaska) 1000/100/10baseTX and
+ * 1000baseSX PHY.
+ * Nathan Binkert <nate <at> openbsd.org>
+ * Jung-uk Kim <jkim <at> niksun.com>
+ */
+
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "if_media.h"
+
+#include "miivar.h"
+
+#include "e1000phyreg.h"
+
+static EFI_STATUS e1000phy_probe (const struct mii_attach_args *ma);
+STATIC VOID e1000phy_attach (struct e1000phy_softc *, const struct mii_attach_args *ma);
+STATIC VOID e1000phy_service (struct e1000phy_softc *, INTN);
+STATIC VOID e1000phy_status (struct e1000phy_softc *);
+STATIC VOID e1000phy_reset (struct e1000phy_softc *);
+STATIC VOID e1000phy_mii_phy_auto (struct e1000phy_softc *);
+
+INTN msk_phy_readreg (VOID *, INTN);
+INTN msk_phy_writereg (VOID *, INTN, INTN);
+VOID msk_miibus_statchg (VOID *);
+
+static const struct mii_phydesc * mii_phy_match (const struct mii_attach_args *ma,
+ const struct mii_phydesc *mpd);
+static const struct mii_phydesc * mii_phy_match_gen (const struct mii_attach_args *ma,
+ const struct mii_phydesc *mpd, UINTN endlen);
+static EFI_STATUS mii_phy_dev_probe (const struct mii_attach_args *ma, const struct mii_phydesc *mpd);
+STATIC VOID mii_phy_update (struct e1000phy_softc *, INTN);
+
+static const struct mii_phydesc e1000phys[] = {
+ MII_PHY_DESC (MARVELL, E1000),
+ MII_PHY_DESC (MARVELL, E1011),
+ MII_PHY_DESC (MARVELL, E1000_3),
+ MII_PHY_DESC (MARVELL, E1000S),
+ MII_PHY_DESC (MARVELL, E1000_5),
+ MII_PHY_DESC (MARVELL, E1000_6),
+ MII_PHY_DESC (MARVELL, E3082),
+ MII_PHY_DESC (MARVELL, E1112),
+ MII_PHY_DESC (MARVELL, E1149),
+ MII_PHY_DESC (MARVELL, E1111),
+ MII_PHY_DESC (MARVELL, E1116),
+ MII_PHY_DESC (MARVELL, E1116R),
+ MII_PHY_DESC (MARVELL, E1118),
+ MII_PHY_DESC (MARVELL, E3016),
+ MII_PHY_DESC (MARVELL, PHYG65G),
+ MII_PHY_DESC (xxMARVELL, E1000),
+ MII_PHY_DESC (xxMARVELL, E1011),
+ MII_PHY_DESC (xxMARVELL, E1000_3),
+ MII_PHY_DESC (xxMARVELL, E1000_5),
+ MII_PHY_DESC (xxMARVELL, E1111),
+ MII_PHY_END
+};
+
+EFI_STATUS
+e1000_probe_and_attach (
+ struct mii_data *mii,
+ const struct msk_mii_data *mmd,
+ VOID *sc_if,
+ VOID **rsc_phy
+ )
+{
+ struct mii_attach_args ma;
+ INTN bmsr;
+ EFI_STATUS Status;
+ struct e1000phy_softc *sc_phy;
+
+ Status = gBS->AllocatePool (EfiBootServicesData,
+ sizeof (struct e1000phy_softc),
+ (VOID**) &sc_phy);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ gBS->SetMem (sc_phy, sizeof (struct e1000phy_softc), 0);
+ sc_phy->mmd = mmd;
+
+ sc_phy->sc_if = sc_if;
+
+ /*
+ * Check to see if there is a PHY at this address. Note,
+ * many braindead PHYs report 0/0 in their ID registers,
+ * so we test for media in the BMSR.
+ */
+ bmsr = PHY_READ (sc_phy, E1000_SR);
+ if (bmsr == 0 || bmsr == 0xffff || (bmsr & (E1000_SR_EXTENDED_STATUS|E1000_SR_MEDIAMASK)) == 0) {
+ /* Assume no PHY at this address. */
+ gBS->FreePool (sc_phy);
+ return EFI_DEVICE_ERROR;
+ }
+
+ /*
+ * Extract the IDs.
+ */
+ ma.mii_id1 = PHY_READ (sc_phy, E1000_ID1);
+ ma.mii_id2 = PHY_READ (sc_phy, E1000_ID2);
+
+ ma.mii_data = mii;
+
+ Status = e1000phy_probe (&ma);
+ if (EFI_ERROR (Status)) {
+ gBS->FreePool (sc_phy);
+ return Status;
+ }
+
+ e1000phy_attach (sc_phy, &ma);
+
+ *rsc_phy = sc_phy;
+
+ return EFI_SUCCESS;
+}
+
+VOID
+e1000phy_detach (
+ struct e1000phy_softc *sc_phy
+ )
+{
+ if (sc_phy != NULL) {
+ gBS->FreePool (sc_phy);
+ }
+}
+
+EFI_STATUS
+e1000phy_probe (
+ const struct mii_attach_args *ma
+ )
+{
+ return (mii_phy_dev_probe (ma, e1000phys));
+}
+
+static void
+e1000phy_attach (
+ struct e1000phy_softc *sc_phy,
+ const struct mii_attach_args *ma
+ )
+{
+ struct mii_softc *sc;
+
+ sc = &sc_phy->mii_sc;
+ sc->mii_pdata = ma->mii_data;
+ sc->mii_anegticks = MII_ANEGTICKS_GIGE;
+
+ sc_phy->mii_model = MII_MODEL (ma->mii_id2);
+
+ if (sc_phy->mmd != NULL && (sc_phy->mmd->mii_flags & MIIF_HAVEFIBER) != 0) {
+ sc->mii_flags |= MIIF_HAVEFIBER;
+ }
+
+ switch (sc_phy->mii_model) {
+ case MII_MODEL_MARVELL_E1011:
+ case MII_MODEL_MARVELL_E1112:
+ if (PHY_READ (sc_phy, E1000_ESSR) & E1000_ESSR_FIBER_LINK) {
+ sc->mii_flags |= MIIF_HAVEFIBER;
+ }
+ break;
+ case MII_MODEL_MARVELL_E1149:
+ /*
+ * Some 88E1149 PHY's page select is initialized to
+ * point to other bank instead of copper/fiber bank
+ * which in turn resulted in wrong registers were
+ * accessed during PHY operation. It is believed that
+ * page 0 should be used for copper PHY so reinitialize
+ * E1000_EADR to select default copper PHY. If parent
+ * device know the type of PHY(either copper or fiber),
+ * that information should be used to select default
+ * type of PHY.
+ */
+ PHY_WRITE (sc_phy, E1000_EADR, 0);
+ break;
+ }
+
+ e1000phy_reset (sc_phy);
+
+ sc->mii_capabilities = PHY_READ (sc_phy, E1000_SR) & 0xFFFFFFFF;
+ if (sc->mii_capabilities & E1000_SR_EXTENDED_STATUS) {
+ sc->mii_extcapabilities = PHY_READ (sc_phy, E1000_ESR);
+ }
+}
+
+static void
+e1000phy_reset (
+ struct e1000phy_softc *sc_phy
+ )
+{
+ UINT16 reg;
+ UINT16 page;
+ struct mii_softc *sc;
+
+ sc = &sc_phy->mii_sc;
+
+ reg = PHY_READ (sc_phy, E1000_SCR);
+ if ((sc->mii_flags & MIIF_HAVEFIBER) != 0) {
+ reg &= ~E1000_SCR_AUTO_X_MODE;
+ PHY_WRITE (sc_phy, E1000_SCR, reg);
+ if (sc_phy->mii_model == MII_MODEL_MARVELL_E1112) {
+ // Select 1000BASE-X only mode.
+ page = PHY_READ (sc_phy, E1000_EADR);
+ PHY_WRITE (sc_phy, E1000_EADR, 2);
+ reg = PHY_READ (sc_phy, E1000_SCR);
+ reg &= ~E1000_SCR_MODE_MASK;
+ reg |= E1000_SCR_MODE_1000BX;
+ PHY_WRITE (sc_phy, E1000_SCR, reg);
+ if (sc_phy->mmd != NULL && sc_phy->mmd->pmd == 'P') {
+ // Set SIGDET polarity low for SFP module
+ PHY_WRITE (sc_phy, E1000_EADR, 1);
+ reg = PHY_READ (sc_phy, E1000_SCR);
+ reg |= E1000_SCR_FIB_SIGDET_POLARITY;
+ PHY_WRITE (sc_phy, E1000_SCR, reg);
+ }
+ PHY_WRITE (sc_phy, E1000_EADR, page);
+ }
+ } else {
+ switch (sc_phy->mii_model) {
+ case MII_MODEL_MARVELL_E1111:
+ case MII_MODEL_MARVELL_E1112:
+ case MII_MODEL_MARVELL_E1116:
+ case MII_MODEL_MARVELL_E1118:
+ case MII_MODEL_MARVELL_E1149:
+ case MII_MODEL_MARVELL_PHYG65G:
+ // Disable energy detect mode
+ reg &= ~E1000_SCR_EN_DETECT_MASK;
+ reg |= E1000_SCR_AUTO_X_MODE;
+ if (sc_phy->mii_model == MII_MODEL_MARVELL_E1116)
+ reg &= ~E1000_SCR_POWER_DOWN;
+ reg |= E1000_SCR_ASSERT_CRS_ON_TX;
+ break;
+ case MII_MODEL_MARVELL_E3082:
+ reg |= (E1000_SCR_AUTO_X_MODE >> 1);
+ reg |= E1000_SCR_ASSERT_CRS_ON_TX;
+ break;
+ case MII_MODEL_MARVELL_E3016:
+ reg |= E1000_SCR_AUTO_MDIX;
+ reg &= ~(E1000_SCR_EN_DETECT |
+ E1000_SCR_SCRAMBLER_DISABLE);
+ reg |= E1000_SCR_LPNP;
+ // XXX Enable class A driver for Yukon FE+ A0
+ PHY_WRITE (sc_phy, 0x1C, PHY_READ (sc_phy, 0x1C) | 0x0001);
+ break;
+ default:
+ reg &= ~E1000_SCR_AUTO_X_MODE;
+ reg |= E1000_SCR_ASSERT_CRS_ON_TX;
+ break;
+ }
+ if (sc_phy->mii_model != MII_MODEL_MARVELL_E3016) {
+ /* Auto correction for reversed cable polarity. */
+ reg &= ~E1000_SCR_POLARITY_REVERSAL;
+ }
+ PHY_WRITE (sc_phy, E1000_SCR, reg);
+
+ if (sc_phy->mii_model == MII_MODEL_MARVELL_E1116 ||
+ sc_phy->mii_model == MII_MODEL_MARVELL_E1149) {
+ PHY_WRITE (sc_phy, E1000_EADR, 2);
+ reg = PHY_READ (sc_phy, E1000_SCR);
+ reg |= E1000_SCR_RGMII_POWER_UP;
+ PHY_WRITE (sc_phy, E1000_SCR, reg);
+ PHY_WRITE (sc_phy, E1000_EADR, 0);
+ }
+ }
+
+ switch (sc_phy->mii_model) {
+ case MII_MODEL_MARVELL_E3082:
+ case MII_MODEL_MARVELL_E1112:
+ case MII_MODEL_MARVELL_E1118:
+ break;
+ case MII_MODEL_MARVELL_E1116:
+ page = PHY_READ (sc_phy, E1000_EADR);
+ /* Select page 3, LED control register. */
+ PHY_WRITE (sc_phy, E1000_EADR, 3);
+ PHY_WRITE (sc_phy, E1000_SCR,
+ E1000_SCR_LED_LOS (1) | /* Link/Act */
+ E1000_SCR_LED_INIT (8) | /* 10Mbps */
+ E1000_SCR_LED_STAT1 (7) | /* 100Mbps */
+ E1000_SCR_LED_STAT0 (7)); /* 1000Mbps */
+ /* Set blink rate. */
+ PHY_WRITE (sc_phy, E1000_IER, E1000_PULSE_DUR (E1000_PULSE_170MS) | E1000_BLINK_RATE (E1000_BLINK_84MS));
+ PHY_WRITE (sc_phy, E1000_EADR, page);
+ break;
+ case MII_MODEL_MARVELL_E3016:
+ /* LED2 -> ACT, LED1 -> LINK, LED0 -> SPEED. */
+ PHY_WRITE (sc_phy, 0x16, 0x0B << 8 | 0x05 << 4 | 0x04);
+ /* Integrated register calibration workaround. */
+ PHY_WRITE (sc_phy, 0x1D, 17);
+ PHY_WRITE (sc_phy, 0x1E, 0x3F60);
+ break;
+ default:
+ /* Force TX_CLK to 25MHz clock. */
+ reg = PHY_READ (sc_phy, E1000_ESCR);
+ reg |= E1000_ESCR_TX_CLK_25;
+ PHY_WRITE (sc_phy, E1000_ESCR, reg);
+ break;
+ }
+
+ /* Reset the PHY so all changes take effect. */
+ reg = PHY_READ (sc_phy, E1000_CR);
+ reg |= E1000_CR_RESET;
+ PHY_WRITE (sc_phy, E1000_CR, reg);
+}
+
+static void
+mii_phy_update (
+ struct e1000phy_softc *sc_phy,
+ INTN cmd
+ )
+{
+ struct mii_softc *sc = &sc_phy->mii_sc;
+ struct mii_data *mii = sc->mii_pdata;
+
+ if (sc->mii_media_active != mii->mii_media_active ||
+ sc->mii_media_status != mii->mii_media_status ||
+ cmd == MII_MEDIACHG)
+ {
+ msk_miibus_statchg (sc_phy->sc_if);
+ sc->mii_media_active = mii->mii_media_active;
+ sc->mii_media_status = mii->mii_media_status;
+ }
+}
+
+void
+e1000phy_tick (
+ struct e1000phy_softc *sc_phy
+ )
+{
+ e1000phy_service (sc_phy, MII_TICK);
+}
+
+void
+e1000phy_mediachg (
+ struct e1000phy_softc *sc_phy
+ )
+{
+ struct mii_data *mii;
+
+ mii = sc_phy->mii_sc.mii_pdata;
+
+ mii->mii_media_status = 0;
+ mii->mii_media_active = IFM_NONE;
+ e1000phy_service (sc_phy, MII_MEDIACHG);
+}
+
+static void
+e1000phy_service (
+ struct e1000phy_softc *sc_phy,
+ INTN cmd
+ )
+{
+ struct mii_softc *sc;
+ INTN reg;
+
+ sc = &sc_phy->mii_sc;
+
+ switch (cmd) {
+ case MII_POLLSTAT:
+ break;
+
+ case MII_MEDIACHG:
+ //
+ // Always try to auto-negotiate
+ //
+ e1000phy_mii_phy_auto (sc_phy);
+ break;
+
+ case MII_TICK:
+ /*
+ * check for link.
+ * Read the status register twice; Link Status is latch-low.
+ */
+ reg = PHY_READ (sc_phy, E1000_SR) | PHY_READ (sc_phy, E1000_SR);
+ if (reg & E1000_SR_LINK_STATUS) {
+ sc->mii_ticks = 0;
+ break;
+ }
+
+ /* Announce link loss right after it happens. */
+ if (sc->mii_ticks++ == 0) {
+ break;
+ }
+ if (sc->mii_ticks <= sc->mii_anegticks) {
+ break;
+ }
+
+ //
+ // Restart the auto-negotiation
+ //
+ sc->mii_ticks = 0;
+ e1000phy_reset (sc_phy);
+ e1000phy_mii_phy_auto (sc_phy);
+ break;
+ }
+
+ /* Update the media status. */
+ e1000phy_status (sc_phy);
+
+ /* Callback if something changed. */
+ mii_phy_update (sc_phy, cmd);
+}
+
+static void
+e1000phy_status (
+ struct e1000phy_softc *sc_phy
+ )
+{
+ struct mii_softc *sc;
+ struct mii_data *mii;
+ INTN bmcr;
+ INTN bmsr;
+ INTN gsr;
+ INTN ssr;
+ INTN ar;
+ INTN lpar;
+
+ sc = &sc_phy->mii_sc;
+ mii = sc->mii_pdata;
+
+ mii->mii_media_status = IFM_AVALID;
+ mii->mii_media_active = IFM_ETHER;
+
+ bmsr = PHY_READ (sc_phy, E1000_SR) | PHY_READ (sc_phy, E1000_SR);
+ bmcr = PHY_READ (sc_phy, E1000_CR);
+ ssr = PHY_READ (sc_phy, E1000_SSR);
+
+ if (bmsr & E1000_SR_LINK_STATUS) {
+ DEBUG ((EFI_D_NET, "Marvell Yukon: e1000phy_status, link up\n"));
+ mii->mii_media_status |= IFM_ACTIVE;
+ }
+
+ if (bmcr & E1000_CR_LOOPBACK) {
+ mii->mii_media_active |= IFM_LOOP;
+ }
+
+ if ((bmcr & E1000_CR_AUTO_NEG_ENABLE) != 0 && (ssr & E1000_SSR_SPD_DPLX_RESOLVED) == 0) {
+ /* Erg, still trying, I guess... */
+ DEBUG ((EFI_D_NET, "Marvell Yukon: e1000phy_status, auto negotiation not complete\n"));
+ mii->mii_media_active |= IFM_NONE;
+ return;
+ }
+
+ if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) {
+ switch (ssr & E1000_SSR_SPEED) {
+ case E1000_SSR_1000MBS:
+ mii->mii_media_active |= IFM_1000_T;
+ break;
+ case E1000_SSR_100MBS:
+ mii->mii_media_active |= IFM_100_TX;
+ break;
+ case E1000_SSR_10MBS:
+ mii->mii_media_active |= IFM_10_T;
+ break;
+ default:
+ mii->mii_media_active |= IFM_NONE;
+ return;
+ }
+ } else {
+ /*
+ * Some fiber PHY(88E1112) does not seem to set resolved
+ * speed so always assume we've got IFM_1000_SX.
+ */
+ mii->mii_media_active |= IFM_1000_SX;
+ }
+
+ if (ssr & E1000_SSR_DUPLEX) {
+ mii->mii_media_active |= IFM_FDX;
+ } else {
+ mii->mii_media_active |= IFM_HDX;
+ }
+
+ if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) {
+ ar = PHY_READ (sc_phy, E1000_AR);
+ lpar = PHY_READ (sc_phy, E1000_LPAR);
+ /* FLAG0==rx-flow-control FLAG1==tx-flow-control */
+ if ((ar & E1000_AR_PAUSE) && (lpar & E1000_LPAR_PAUSE)) {
+ mii->mii_media_active |= IFM_FLAG0 | IFM_FLAG1;
+ } else if (!(ar & E1000_AR_PAUSE) && (ar & E1000_AR_ASM_DIR) &&
+ (lpar & E1000_LPAR_PAUSE) && (lpar & E1000_LPAR_ASM_DIR)) {
+ mii->mii_media_active |= IFM_FLAG1;
+ } else if ((ar & E1000_AR_PAUSE) && (ar & E1000_AR_ASM_DIR) &&
+ !(lpar & E1000_LPAR_PAUSE) && (lpar & E1000_LPAR_ASM_DIR)) {
+ mii->mii_media_active |= IFM_FLAG0;
+ }
+ }
+
+ /* FLAG2 : local PHY resolved to MASTER */
+ if ((IFM_SUBTYPE (mii->mii_media_active) == IFM_1000_T) ||
+ (IFM_SUBTYPE (mii->mii_media_active) == IFM_1000_SX)) {
+ PHY_READ (sc_phy, E1000_1GSR);
+ gsr = PHY_READ (sc_phy, E1000_1GSR);
+ if ((gsr & E1000_1GSR_MS_CONFIG_RES) != 0) {
+ mii->mii_media_active |= IFM_FLAG2;
+ }
+ }
+}
+
+static void
+e1000phy_mii_phy_auto (
+ struct e1000phy_softc *sc_phy
+ )
+{
+ struct mii_softc *sc;
+ UINT16 reg;
+
+ DEBUG ((EFI_D_NET, "Marvell Yukon: e1000phy_mii_phy_auto negotiation started\n"));
+ sc = &sc_phy->mii_sc;
+ if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) {
+ reg = PHY_READ (sc_phy, E1000_AR);
+ reg |= E1000_AR_10T | E1000_AR_10T_FD |
+ E1000_AR_100TX | E1000_AR_100TX_FD |
+ E1000_AR_PAUSE | E1000_AR_ASM_DIR;
+ PHY_WRITE (sc_phy, E1000_AR, reg | E1000_AR_SELECTOR_FIELD);
+ } else {
+ PHY_WRITE (sc_phy, E1000_AR, E1000_FA_1000X_FD | E1000_FA_1000X | E1000_FA_SYM_PAUSE | E1000_FA_ASYM_PAUSE);
+ }
+
+ if ((sc->mii_extcapabilities & (E1000_ESR_1000T_FD | E1000_ESR_1000T)) != 0) {
+ PHY_WRITE (sc_phy, E1000_1GCR, E1000_1GCR_1000T_FD | E1000_1GCR_1000T);
+ }
+
+ PHY_WRITE (sc_phy, E1000_CR, E1000_CR_AUTO_NEG_ENABLE | E1000_CR_RESTART_AUTO_NEG);
+}
+
+//
+// Generic helper functions
+//
+
+const struct mii_phydesc *
+ mii_phy_match_gen (
+ const struct mii_attach_args *ma,
+ const struct mii_phydesc *mpd,
+ UINTN len
+ )
+{
+
+ for (; mpd->mpd_name != NULL;
+ mpd = (const struct mii_phydesc *) ((const CHAR8 *) mpd + len)) {
+ if (MII_OUI (ma->mii_id1, ma->mii_id2) == mpd->mpd_oui &&
+ MII_MODEL (ma->mii_id2) == mpd->mpd_model) {
+ return (mpd);
+ }
+ }
+ return (NULL);
+}
+
+const struct mii_phydesc *
+ mii_phy_match (
+ const struct mii_attach_args *ma,
+ const struct mii_phydesc *mpd
+ )
+{
+
+ return (mii_phy_match_gen (ma, mpd, sizeof (struct mii_phydesc)));
+}
+
+EFI_STATUS
+mii_phy_dev_probe (
+ const struct mii_attach_args *ma,
+ const struct mii_phydesc *mpd
+ )
+{
+
+ mpd = mii_phy_match (ma, mpd);
+ if (mpd != NULL) {
+ DEBUG ((EFI_D_NET, "Marvell Yukon: Found PHY (%a)\n", mpd->mpd_name));
+ return EFI_SUCCESS;
+ }
+
+ DEBUG ((DEBUG_NET, "Marvell Yukon: PHY not found (OUI=0x%x, MODEL=0x%x)\n",
+ MII_OUI (ma->mii_id1, ma->mii_id2), MII_MODEL (ma->mii_id2)));
+ return EFI_NOT_FOUND;
+}