1*5dfdc49fSandvar /* $NetBSD: atphy.c,v 1.32 2024/07/20 20:36:32 andvar Exp $ */ 27546e67dScegger /* $OpenBSD: atphy.c,v 1.1 2008/09/25 20:47:16 brad Exp $ */ 37546e67dScegger 47546e67dScegger /*- 57546e67dScegger * Copyright (c) 2008, Pyun YongHyeon <yongari@FreeBSD.org> 67546e67dScegger * All rights reserved. 77546e67dScegger * 87546e67dScegger * Redistribution and use in source and binary forms, with or without 97546e67dScegger * modification, are permitted provided that the following conditions 107546e67dScegger * are met: 117546e67dScegger * 1. Redistributions of source code must retain the above copyright 127546e67dScegger * notice unmodified, this list of conditions, and the following 137546e67dScegger * disclaimer. 147546e67dScegger * 2. Redistributions in binary form must reproduce the above copyright 157546e67dScegger * notice, this list of conditions and the following disclaimer in the 167546e67dScegger * documentation and/or other materials provided with the distribution. 177546e67dScegger * 187546e67dScegger * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 197546e67dScegger * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 207546e67dScegger * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 217546e67dScegger * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 227546e67dScegger * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 237546e67dScegger * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 247546e67dScegger * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 257546e67dScegger * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 267546e67dScegger * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 277546e67dScegger * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 287546e67dScegger * SUCH DAMAGE. 297546e67dScegger */ 307546e67dScegger 317546e67dScegger /* 327546e67dScegger * Driver for the Attansic F1 10/100/1000 PHY. 337546e67dScegger */ 347546e67dScegger 3531614b9aScegger #include <sys/cdefs.h> 36*5dfdc49fSandvar __KERNEL_RCSID(0, "$NetBSD: atphy.c,v 1.32 2024/07/20 20:36:32 andvar Exp $"); 3731614b9aScegger 387546e67dScegger #include <sys/param.h> 397546e67dScegger #include <sys/systm.h> 407546e67dScegger #include <sys/kernel.h> 417546e67dScegger #include <sys/device.h> 427546e67dScegger #include <sys/socket.h> 437546e67dScegger 447546e67dScegger #include <net/if.h> 457546e67dScegger #include <net/if_media.h> 467546e67dScegger 477546e67dScegger #include <dev/mii/mii.h> 487546e67dScegger #include <dev/mii/miivar.h> 497546e67dScegger #include <dev/mii/miidevs.h> 507546e67dScegger 517546e67dScegger /* Special Control Register */ 527546e67dScegger #define ATPHY_SCR 0x10 537546e67dScegger #define ATPHY_SCR_JABBER_DISABLE 0x0001 547546e67dScegger #define ATPHY_SCR_POLARITY_REVERSAL 0x0002 557546e67dScegger #define ATPHY_SCR_SQE_TEST 0x0004 567546e67dScegger #define ATPHY_SCR_MAC_PDOWN 0x0008 577546e67dScegger #define ATPHY_SCR_CLK125_DISABLE 0x0010 587546e67dScegger #define ATPHY_SCR_MDI_MANUAL_MODE 0x0000 597546e67dScegger #define ATPHY_SCR_MDIX_MANUAL_MODE 0x0020 607546e67dScegger #define ATPHY_SCR_AUTO_X_1000T 0x0040 617546e67dScegger #define ATPHY_SCR_AUTO_X_MODE 0x0060 627546e67dScegger #define ATPHY_SCR_10BT_EXT_ENABLE 0x0080 637546e67dScegger #define ATPHY_SCR_MII_5BIT_ENABLE 0x0100 647546e67dScegger #define ATPHY_SCR_SCRAMBLER_DISABLE 0x0200 657546e67dScegger #define ATPHY_SCR_FORCE_LINK_GOOD 0x0400 667546e67dScegger #define ATPHY_SCR_ASSERT_CRS_ON_TX 0x0800 677546e67dScegger 687546e67dScegger /* Special Status Register. */ 697546e67dScegger #define ATPHY_SSR 0x11 707546e67dScegger #define ATPHY_SSR_SPD_DPLX_RESOLVED 0x0800 717546e67dScegger #define ATPHY_SSR_DUPLEX 0x2000 727546e67dScegger #define ATPHY_SSR_SPEED_MASK 0xC000 737546e67dScegger #define ATPHY_SSR_10MBS 0x0000 747546e67dScegger #define ATPHY_SSR_100MBS 0x4000 757546e67dScegger #define ATPHY_SSR_1000MBS 0x8000 767546e67dScegger 772b791420Shkenken #define ATPHY_DEBUG_PORT_ADDR 0x1d 782b791420Shkenken #define ATPHY_DEBUG_PORT_DATA 0x1e 792b791420Shkenken #define ATPHY_RGMII_RX_CLK_DLY __BIT(15) 802b791420Shkenken #define ATPHY_RGMII_TX_CLK_DLY __BIT(8) 812b791420Shkenken 827546e67dScegger static int atphy_match(device_t, cfdata_t, void *); 837546e67dScegger static void atphy_attach(device_t, device_t, void *); 847546e67dScegger 857546e67dScegger static int atphy_service(struct mii_softc *, struct mii_data *, int); 867546e67dScegger static void atphy_reset(struct mii_softc *); 877546e67dScegger static void atphy_status(struct mii_softc *); 887546e67dScegger static int atphy_mii_phy_auto(struct mii_softc *); 89de012e9fSjmcneill static bool atphy_is_gige(const struct mii_phydesc *); 907546e67dScegger 912b791420Shkenken struct atphy_softc { 922b791420Shkenken struct mii_softc mii_sc; 932b791420Shkenken int mii_clk_25m; 942b791420Shkenken bool rgmii_tx_internal_delay; 952b791420Shkenken bool rgmii_rx_internal_delay; 962b791420Shkenken }; 972b791420Shkenken 982b791420Shkenken CFATTACH_DECL_NEW(atphy, sizeof(struct atphy_softc), 997546e67dScegger atphy_match, atphy_attach, mii_phy_detach, mii_phy_activate); 1007546e67dScegger 1017546e67dScegger const struct mii_phy_funcs atphy_funcs = { 1027546e67dScegger atphy_service, atphy_status, atphy_reset, 1037546e67dScegger }; 1047546e67dScegger 1054f7aaa0aSmsaitoh static const struct mii_phydesc atphys[] = { 1067b43da1bSchristos MII_PHY_DESC(ATTANSIC, L1), 1077b43da1bSchristos MII_PHY_DESC(ATTANSIC, L2), 1087b43da1bSchristos MII_PHY_DESC(ATTANSIC, AR8021), 1097b43da1bSchristos MII_PHY_DESC(ATTANSIC, AR8035), 1107b43da1bSchristos MII_PHY_END, 1117546e67dScegger }; 1127546e67dScegger 1132b791420Shkenken static void 1142b791420Shkenken atphy_clk_25m(struct atphy_softc *asc) 1152b791420Shkenken { 1162b791420Shkenken struct mii_softc *sc = &asc->mii_sc; 1172b791420Shkenken struct { 1182b791420Shkenken uint32_t hz; 1192b791420Shkenken uint16_t data; 1202b791420Shkenken } select_clk[] = { 1212b791420Shkenken { 25000000, 0x0 }, 1222b791420Shkenken { 50000000, 0x1 }, 1232b791420Shkenken { 62500000, 0x2 }, 1242b791420Shkenken { 125000000, 0x3 } 1252b791420Shkenken }; 1262b791420Shkenken uint16_t data = 0; 1272b791420Shkenken uint16_t reg = 0; 1282b791420Shkenken 1292b791420Shkenken for (int i = 0; i < __arraycount(select_clk); i++) { 1302b791420Shkenken if (asc->mii_clk_25m <= select_clk[i].hz) 1312b791420Shkenken data = select_clk[i].data; 1322b791420Shkenken } 1332b791420Shkenken 1342b791420Shkenken PHY_WRITE(sc, 0x0d, 0x0007); 1352b791420Shkenken PHY_WRITE(sc, 0x0e, 0x8016); 1362b791420Shkenken PHY_WRITE(sc, 0x0d, 0x4007); 1372b791420Shkenken PHY_READ(sc, 0x0e, ®); 1382b791420Shkenken PHY_WRITE(sc, 0x0e, reg | __SHIFTIN(data, __BITS(4, 3))); 1392b791420Shkenken } 1402b791420Shkenken 1412b791420Shkenken 142de012e9fSjmcneill static bool 143de012e9fSjmcneill atphy_is_gige(const struct mii_phydesc *mpd) 144de012e9fSjmcneill { 145de012e9fSjmcneill switch (mpd->mpd_oui) { 146de012e9fSjmcneill case MII_OUI_ATTANSIC: 147de012e9fSjmcneill switch (mpd->mpd_model) { 148de012e9fSjmcneill case MII_MODEL_ATTANSIC_L2: 149de012e9fSjmcneill return false; 150de012e9fSjmcneill } 151de012e9fSjmcneill } 152de012e9fSjmcneill 153de012e9fSjmcneill return true; 154de012e9fSjmcneill } 155de012e9fSjmcneill 1567546e67dScegger static int 1577546e67dScegger atphy_match(device_t parent, cfdata_t match, void *aux) 1587546e67dScegger { 1597546e67dScegger struct mii_attach_args *ma = aux; 1607546e67dScegger 1614f7aaa0aSmsaitoh if (mii_phy_match(ma, atphys) != NULL) 1627546e67dScegger return 10; 1637546e67dScegger 1647546e67dScegger return 0; 1657546e67dScegger } 1667546e67dScegger 1677546e67dScegger void 1687546e67dScegger atphy_attach(device_t parent, device_t self, void *aux) 1697546e67dScegger { 1702b791420Shkenken struct atphy_softc *asc = device_private(self); 1712b791420Shkenken prop_dictionary_t parent_prop = device_properties(parent); 1722b791420Shkenken prop_dictionary_t prop = device_properties(self); 1732b791420Shkenken struct mii_softc *sc = &asc->mii_sc; 1747546e67dScegger struct mii_attach_args *ma = aux; 1757546e67dScegger struct mii_data *mii = ma->mii_data; 1767546e67dScegger const struct mii_phydesc *mpd; 177c9701668Scegger uint16_t bmsr; 1787546e67dScegger 1794f7aaa0aSmsaitoh mpd = mii_phy_match(ma, atphys); 1807546e67dScegger aprint_naive(": Media interface\n"); 1817546e67dScegger aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2)); 1827546e67dScegger 1837546e67dScegger sc->mii_dev = self; 1847546e67dScegger sc->mii_inst = mii->mii_instance; 1857546e67dScegger sc->mii_phy = ma->mii_phyno; 18631ec9946Smsaitoh sc->mii_mpd_oui = MII_OUI(ma->mii_id1, ma->mii_id2); 18731ec9946Smsaitoh sc->mii_mpd_model = MII_MODEL(ma->mii_id2); 18831ec9946Smsaitoh sc->mii_mpd_rev = MII_REV(ma->mii_id2); 1897546e67dScegger sc->mii_funcs = &atphy_funcs; 1907546e67dScegger sc->mii_pdata = mii; 1917546e67dScegger sc->mii_flags = ma->mii_flags; 1927546e67dScegger sc->mii_flags |= MIIF_NOLOOP; 1937546e67dScegger 194333d7cf6Smsaitoh prop_dictionary_get_bool(parent_prop, "tx_internal_delay", 195333d7cf6Smsaitoh &asc->rgmii_tx_internal_delay); 196333d7cf6Smsaitoh prop_dictionary_get_bool(parent_prop, "rx_internal_delay", 197333d7cf6Smsaitoh &asc->rgmii_rx_internal_delay); 1982b791420Shkenken 1992b791420Shkenken prop_dictionary_get_uint32(prop, "clk_25m", &asc->mii_clk_25m); 2002b791420Shkenken if (asc->mii_clk_25m != 0) 2012b791420Shkenken atphy_clk_25m(asc); 2022b791420Shkenken 2037a9a30c5Sthorpej mii_lock(mii); 2047a9a30c5Sthorpej 2057546e67dScegger PHY_RESET(sc); 2067546e67dScegger 207a5cdd4b4Smsaitoh PHY_READ(sc, MII_BMSR, &bmsr); 208a5cdd4b4Smsaitoh PHY_READ(sc, MII_BMSR, &bmsr); 209c9701668Scegger sc->mii_capabilities = bmsr & ma->mii_capmask; 210de012e9fSjmcneill if (atphy_is_gige(mpd) && (sc->mii_capabilities & BMSR_EXTSTAT)) 211a5cdd4b4Smsaitoh PHY_READ(sc, MII_EXTSR, &sc->mii_extcapabilities); 2127546e67dScegger 2137a9a30c5Sthorpej mii_unlock(mii); 2147a9a30c5Sthorpej 2157546e67dScegger mii_phy_add_media(sc); 2167546e67dScegger } 2177546e67dScegger 2187546e67dScegger int 2197546e67dScegger atphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 2207546e67dScegger { 2217546e67dScegger struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 2227546e67dScegger uint16_t anar, bmcr, bmsr; 2237546e67dScegger 2247a9a30c5Sthorpej KASSERT(mii_locked(mii)); 2257a9a30c5Sthorpej 2267546e67dScegger switch (cmd) { 2277546e67dScegger case MII_POLLSTAT: 2288e65e831Smsaitoh /* If we're not polling our PHY instance, just return. */ 2297546e67dScegger if (IFM_INST(ife->ifm_media) != sc->mii_inst) 2307546e67dScegger return 0; 2317546e67dScegger break; 2327546e67dScegger 2337546e67dScegger case MII_MEDIACHG: 2347546e67dScegger /* 2357546e67dScegger * If the media indicates a different PHY instance, 2367546e67dScegger * isolate ourselves. 2377546e67dScegger */ 2387546e67dScegger if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 239a5cdd4b4Smsaitoh PHY_READ(sc, MII_BMCR, &bmcr); 2407546e67dScegger PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO); 2417546e67dScegger return 0; 2427546e67dScegger } 2437546e67dScegger 2448e65e831Smsaitoh /* If the interface is not up, don't do anything. */ 2457546e67dScegger if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 2467546e67dScegger break; 2477546e67dScegger 2487546e67dScegger bmcr = 0; 2497546e67dScegger switch (IFM_SUBTYPE(ife->ifm_media)) { 2507546e67dScegger case IFM_AUTO: 2517546e67dScegger case IFM_1000_T: 2527546e67dScegger atphy_mii_phy_auto(sc); 2537546e67dScegger goto done; 2547546e67dScegger case IFM_100_TX: 2557546e67dScegger bmcr = BMCR_S100; 2567546e67dScegger break; 2577546e67dScegger case IFM_10_T: 2587546e67dScegger bmcr = BMCR_S10; 2597546e67dScegger break; 2607546e67dScegger case IFM_NONE: 261a5cdd4b4Smsaitoh PHY_READ(sc, MII_BMCR, &bmcr); 2627546e67dScegger /* 2637546e67dScegger * XXX 2647546e67dScegger * Due to an unknown reason powering down PHY resulted 26522b735ebScegger * in unexpected results such as inaccessibility of 2667546e67dScegger * hardware of freshly rebooted system. Disable 2677546e67dScegger * powering down PHY until I got more information for 268*5dfdc49fSandvar * Attansic/Atheros PHY hardware. 2697546e67dScegger */ 2707546e67dScegger PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO); 2717546e67dScegger goto done; 2727546e67dScegger default: 2737546e67dScegger return EINVAL; 2747546e67dScegger } 2757546e67dScegger 27689dc313cSmsaitoh anar = mii_anar(ife); 27780082ddbSmsaitoh if ((ife->ifm_media & IFM_FDX) != 0) { 2787546e67dScegger bmcr |= BMCR_FDX; 2797546e67dScegger /* Enable pause. */ 2807546e67dScegger if (sc->mii_flags & MIIF_DOPAUSE) 281d13f0398Smsaitoh anar |= ANAR_PAUSE_TOWARDS; 2827546e67dScegger } 2837546e67dScegger 2847546e67dScegger if ((sc->mii_extcapabilities & (EXTSR_1000TFDX | 2857546e67dScegger EXTSR_1000THDX)) != 0) 2867546e67dScegger PHY_WRITE(sc, MII_100T2CR, 0); 2877546e67dScegger PHY_WRITE(sc, MII_ANAR, anar); 2887546e67dScegger 2898e65e831Smsaitoh /* Start autonegotiation. */ 290c9701668Scegger PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_AUTOEN | BMCR_STARTNEG); 2917546e67dScegger done: 2927546e67dScegger break; 2937546e67dScegger 2947546e67dScegger case MII_TICK: 2958e65e831Smsaitoh /* If we're not currently selected, just return. */ 2967546e67dScegger if (IFM_INST(ife->ifm_media) != sc->mii_inst) 2977546e67dScegger return 0; 2987546e67dScegger 2998e65e831Smsaitoh /* Is the interface even up? */ 3007546e67dScegger if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 3017546e67dScegger return 0; 3027546e67dScegger 3038e65e831Smsaitoh /* Only used for autonegotiation. */ 304a2fa7500Smsaitoh if ((IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) && 305a2fa7500Smsaitoh (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T)) { 3067546e67dScegger sc->mii_ticks = 0; 3077546e67dScegger break; 3087546e67dScegger } 3097546e67dScegger 3107546e67dScegger /* 3117546e67dScegger * Check for link. 3127546e67dScegger * Read the status register twice; BMSR_LINK is latch-low. 3137546e67dScegger */ 314a5cdd4b4Smsaitoh PHY_READ(sc, MII_BMSR, &bmsr); 315a5cdd4b4Smsaitoh PHY_READ(sc, MII_BMSR, &bmsr); 3167546e67dScegger if (bmsr & BMSR_LINK) { 3177546e67dScegger sc->mii_ticks = 0; 3187546e67dScegger break; 3197546e67dScegger } 3207546e67dScegger 3217546e67dScegger /* Announce link loss right after it happens. */ 3227546e67dScegger if (sc->mii_ticks++ == 0) 3237546e67dScegger break; 3247546e67dScegger 3258e65e831Smsaitoh /* Only retry autonegotiation every mii_anegticks seconds. */ 3269589449dSmsaitoh if (sc->mii_ticks < sc->mii_anegticks) 3277546e67dScegger break; 3287546e67dScegger 3297546e67dScegger atphy_mii_phy_auto(sc); 3307546e67dScegger break; 3317546e67dScegger } 3327546e67dScegger 3337546e67dScegger /* Update the media status. */ 3347546e67dScegger mii_phy_status(sc); 3357546e67dScegger 3367546e67dScegger /* Callback if something changed. */ 3377546e67dScegger mii_phy_update(sc, cmd); 3387546e67dScegger return 0; 3397546e67dScegger } 3407546e67dScegger 3417546e67dScegger static void 3427546e67dScegger atphy_status(struct mii_softc *sc) 3437546e67dScegger { 3447546e67dScegger struct mii_data *mii = sc->mii_pdata; 345a5cdd4b4Smsaitoh uint16_t bmsr, bmcr, gsr, ssr; 3467546e67dScegger 3477a9a30c5Sthorpej KASSERT(mii_locked(mii)); 3487a9a30c5Sthorpej 3497546e67dScegger mii->mii_media_status = IFM_AVALID; 3507546e67dScegger mii->mii_media_active = IFM_ETHER; 3517546e67dScegger 352a5cdd4b4Smsaitoh PHY_READ(sc, MII_BMSR, &bmsr); 353a5cdd4b4Smsaitoh PHY_READ(sc, MII_BMSR, &bmsr); 3547546e67dScegger if (bmsr & BMSR_LINK) 3557546e67dScegger mii->mii_media_status |= IFM_ACTIVE; 3567546e67dScegger 357a5cdd4b4Smsaitoh PHY_READ(sc, MII_BMCR, &bmcr); 3587546e67dScegger if (bmcr & BMCR_ISO) { 3597546e67dScegger mii->mii_media_active |= IFM_NONE; 3607546e67dScegger mii->mii_media_status = 0; 3617546e67dScegger return; 3627546e67dScegger } 3637546e67dScegger 3647546e67dScegger if (bmcr & BMCR_LOOP) 3657546e67dScegger mii->mii_media_active |= IFM_LOOP; 3667546e67dScegger 367a5cdd4b4Smsaitoh PHY_READ(sc, ATPHY_SSR, &ssr); 3687546e67dScegger if (!(ssr & ATPHY_SSR_SPD_DPLX_RESOLVED)) { 3697546e67dScegger /* Erg, still trying, I guess... */ 3707546e67dScegger mii->mii_media_active |= IFM_NONE; 3717546e67dScegger return; 3727546e67dScegger } 3737546e67dScegger 3747546e67dScegger switch (ssr & ATPHY_SSR_SPEED_MASK) { 3757546e67dScegger case ATPHY_SSR_1000MBS: 3767546e67dScegger mii->mii_media_active |= IFM_1000_T; 3777546e67dScegger /* 3787546e67dScegger * atphy(4) has a valid link so reset mii_ticks. 3797546e67dScegger * Resetting mii_ticks is needed in order to 3807546e67dScegger * detect link loss after auto-negotiation. 3817546e67dScegger */ 3827546e67dScegger sc->mii_ticks = 0; 3837546e67dScegger break; 3847546e67dScegger case ATPHY_SSR_100MBS: 3857546e67dScegger mii->mii_media_active |= IFM_100_TX; 3867546e67dScegger sc->mii_ticks = 0; 3877546e67dScegger break; 3887546e67dScegger case ATPHY_SSR_10MBS: 3897546e67dScegger mii->mii_media_active |= IFM_10_T; 3907546e67dScegger sc->mii_ticks = 0; 3917546e67dScegger break; 3927546e67dScegger default: 3937546e67dScegger mii->mii_media_active |= IFM_NONE; 3947546e67dScegger return; 3957546e67dScegger } 3967546e67dScegger 3977546e67dScegger if (ssr & ATPHY_SSR_DUPLEX) 3987546e67dScegger mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc); 3997546e67dScegger else 4007546e67dScegger mii->mii_media_active |= IFM_HDX; 4017546e67dScegger 402a5cdd4b4Smsaitoh PHY_READ(sc, MII_100T2SR, &gsr); 4037546e67dScegger if ((IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) && 4047546e67dScegger gsr & GTSR_MS_RES) 4057546e67dScegger mii->mii_media_active |= IFM_ETH_MASTER; 4067546e67dScegger } 4077546e67dScegger 4087546e67dScegger static void 4097546e67dScegger atphy_reset(struct mii_softc *sc) 4107546e67dScegger { 4112b791420Shkenken struct atphy_softc *asc = (struct atphy_softc *)sc; 412a5cdd4b4Smsaitoh uint16_t reg; 4137546e67dScegger int i; 4147546e67dScegger 4157a9a30c5Sthorpej KASSERT(mii_locked(sc->mii_pdata)); 4167a9a30c5Sthorpej 41779d9ef08Smsaitoh /* 41879d9ef08Smsaitoh * Take PHY out of power down mode. 41979d9ef08Smsaitoh * 42079d9ef08Smsaitoh * XXX AR8021 document has no description about the power saving 42179d9ef08Smsaitoh * control register. Shouldn't we write it? 42279d9ef08Smsaitoh */ 4237546e67dScegger PHY_WRITE(sc, 29, 0x29); 42479d9ef08Smsaitoh /* 42579d9ef08Smsaitoh * XXX AR8031 document says the lower 14 bits are reserved and the 42679d9ef08Smsaitoh * default value is 0x36d0. Shouldn't we clear those bits? 42779d9ef08Smsaitoh * I have no document neither L1(F1) nor L2(F2). 42879d9ef08Smsaitoh */ 4297546e67dScegger PHY_WRITE(sc, 30, 0); 4307546e67dScegger 43179d9ef08Smsaitoh if ((sc->mii_mpd_model == MII_MODEL_ATTANSIC_L2) 43279d9ef08Smsaitoh && (sc->mii_mpd_rev == 1)) { 43379d9ef08Smsaitoh /* 43479d9ef08Smsaitoh * On NVIDIA MCP61 with Attansic L2 rev. 1, changing debug 43579d9ef08Smsaitoh * port 0x29's value makes the next PHY read fail with error. 43679d9ef08Smsaitoh * This is observed on ASUS M2N-MX SE Plus. Read any register 43779d9ef08Smsaitoh * to ignore this problem. 43879d9ef08Smsaitoh */ 43979d9ef08Smsaitoh (void)PHY_READ(sc, ATPHY_SCR, ®); 44079d9ef08Smsaitoh } 441a5cdd4b4Smsaitoh PHY_READ(sc, ATPHY_SCR, ®); 4427546e67dScegger /* Enable automatic crossover. */ 4437546e67dScegger reg |= ATPHY_SCR_AUTO_X_MODE; 4447546e67dScegger /* Disable power down. */ 4457546e67dScegger reg &= ~ATPHY_SCR_MAC_PDOWN; 4467546e67dScegger /* Enable CRS on Tx. */ 4477546e67dScegger reg |= ATPHY_SCR_ASSERT_CRS_ON_TX; 4487546e67dScegger /* Auto correction for reversed cable polarity. */ 4497546e67dScegger reg |= ATPHY_SCR_POLARITY_REVERSAL; 4507546e67dScegger PHY_WRITE(sc, ATPHY_SCR, reg); 4517546e67dScegger 4527546e67dScegger atphy_mii_phy_auto(sc); 4537546e67dScegger 454c9701668Scegger /* Workaround F1 bug to reset phy. */ 455a5cdd4b4Smsaitoh PHY_READ(sc, MII_BMCR, ®); 456a5cdd4b4Smsaitoh reg |= BMCR_RESET; 457c9701668Scegger PHY_WRITE(sc, MII_BMCR, reg); 458c9701668Scegger 4597546e67dScegger for (i = 0; i < 1000; i++) { 4607546e67dScegger DELAY(1); 461a5cdd4b4Smsaitoh PHY_READ(sc, MII_BMCR, ®); 462a5cdd4b4Smsaitoh if ((reg & BMCR_RESET) == 0) 4637546e67dScegger break; 4647546e67dScegger } 4652b791420Shkenken 4662b791420Shkenken if (asc->rgmii_tx_internal_delay) { 4672b791420Shkenken PHY_WRITE(sc, ATPHY_DEBUG_PORT_ADDR, 0x05); 4682b791420Shkenken PHY_WRITE(sc, ATPHY_DEBUG_PORT_DATA, ATPHY_RGMII_TX_CLK_DLY); 4692b791420Shkenken } 4702b791420Shkenken if (asc->rgmii_rx_internal_delay) { 4712b791420Shkenken PHY_WRITE(sc, ATPHY_DEBUG_PORT_ADDR, 0x00); 4722b791420Shkenken PHY_WRITE(sc, ATPHY_DEBUG_PORT_DATA, ATPHY_RGMII_RX_CLK_DLY); 4732b791420Shkenken } 4747546e67dScegger } 4757546e67dScegger 4767546e67dScegger static int 4777546e67dScegger atphy_mii_phy_auto(struct mii_softc *sc) 4787546e67dScegger { 4797546e67dScegger uint16_t anar; 4807546e67dScegger 4817a9a30c5Sthorpej KASSERT(mii_locked(sc->mii_pdata)); 4827a9a30c5Sthorpej 4835cbe9dd2Smsaitoh sc->mii_ticks = 0; 4847546e67dScegger anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA; 4857546e67dScegger if (sc->mii_flags & MIIF_DOPAUSE) 486a93dfad0Smsaitoh anar |= ANAR_PAUSE_TOWARDS; 4877546e67dScegger PHY_WRITE(sc, MII_ANAR, anar); 4887546e67dScegger if (sc->mii_extcapabilities & (EXTSR_1000TFDX | EXTSR_1000THDX)) 4897546e67dScegger PHY_WRITE(sc, MII_100T2CR, GTCR_ADV_1000TFDX | 4907546e67dScegger GTCR_ADV_1000THDX); 491c9701668Scegger PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG); 4927546e67dScegger 4937546e67dScegger return EJUSTRETURN; 4947546e67dScegger } 495