150120Swpaul/* $NetBSD: mii_physubr.c,v 1.5 1999/08/03 19:41:49 drochner Exp $ */ 250120Swpaul 350120Swpaul/*- 495718Sphk * Copyright (c) 1998, 1999, 2000, 2001 The NetBSD Foundation, Inc. 550120Swpaul * All rights reserved. 650120Swpaul * 750120Swpaul * This code is derived from software contributed to The NetBSD Foundation 850120Swpaul * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 950120Swpaul * NASA Ames Research Center. 1050120Swpaul * 1150120Swpaul * Redistribution and use in source and binary forms, with or without 1250120Swpaul * modification, are permitted provided that the following conditions 1350120Swpaul * are met: 1450120Swpaul * 1. Redistributions of source code must retain the above copyright 1550120Swpaul * notice, this list of conditions and the following disclaimer. 1650120Swpaul * 2. Redistributions in binary form must reproduce the above copyright 1750120Swpaul * notice, this list of conditions and the following disclaimer in the 1850120Swpaul * documentation and/or other materials provided with the distribution. 1950120Swpaul * 2050120Swpaul * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2150120Swpaul * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2250120Swpaul * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2350120Swpaul * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2450120Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2550120Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2650120Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2750120Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2850120Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2950120Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3050120Swpaul * POSSIBILITY OF SUCH DAMAGE. 3150120Swpaul */ 3250120Swpaul 33119418Sobrien#include <sys/cdefs.h> 34119418Sobrien__FBSDID("$FreeBSD$"); 35119418Sobrien 3650120Swpaul/* 3750120Swpaul * Subroutines common to all PHYs. 3850120Swpaul */ 3950120Swpaul 4050120Swpaul#include <sys/param.h> 4150120Swpaul#include <sys/systm.h> 4250120Swpaul#include <sys/kernel.h> 4350120Swpaul#include <sys/socket.h> 4450120Swpaul#include <sys/errno.h> 4550120Swpaul#include <sys/module.h> 4650120Swpaul#include <sys/bus.h> 4750120Swpaul 4850120Swpaul#include <net/if.h> 4950120Swpaul#include <net/if_media.h> 5050120Swpaul 5150120Swpaul#include <dev/mii/mii.h> 5250120Swpaul#include <dev/mii/miivar.h> 5350120Swpaul 5450120Swpaul#include "miibus_if.h" 5550120Swpaul 5695707Sphk/* 5795707Sphk * Media to register setting conversion table. Order matters. 5895707Sphk */ 59214265Smariusstatic const struct mii_media mii_media_table[MII_NMEDIA] = { 6095707Sphk /* None */ 6195707Sphk { BMCR_ISO, ANAR_CSMA, 6295707Sphk 0, }, 6395707Sphk 6495707Sphk /* 10baseT */ 6595707Sphk { BMCR_S10, ANAR_CSMA|ANAR_10, 6695707Sphk 0, }, 6795707Sphk 6895707Sphk /* 10baseT-FDX */ 6995707Sphk { BMCR_S10|BMCR_FDX, ANAR_CSMA|ANAR_10_FD, 7095707Sphk 0, }, 7195707Sphk 7295707Sphk /* 100baseT4 */ 7395707Sphk { BMCR_S100, ANAR_CSMA|ANAR_T4, 7495707Sphk 0, }, 7595707Sphk 7695707Sphk /* 100baseTX */ 7795707Sphk { BMCR_S100, ANAR_CSMA|ANAR_TX, 7895707Sphk 0, }, 7995707Sphk 8095707Sphk /* 100baseTX-FDX */ 8195707Sphk { BMCR_S100|BMCR_FDX, ANAR_CSMA|ANAR_TX_FD, 8295707Sphk 0, }, 8395707Sphk 8495707Sphk /* 1000baseX */ 8595707Sphk { BMCR_S1000, ANAR_CSMA, 8695707Sphk 0, }, 8795707Sphk 8895707Sphk /* 1000baseX-FDX */ 8995707Sphk { BMCR_S1000|BMCR_FDX, ANAR_CSMA, 9095707Sphk 0, }, 9195707Sphk 9295707Sphk /* 1000baseT */ 9395707Sphk { BMCR_S1000, ANAR_CSMA, 9495707Sphk GTCR_ADV_1000THDX }, 9595707Sphk 9695707Sphk /* 1000baseT-FDX */ 9795707Sphk { BMCR_S1000, ANAR_CSMA, 9895707Sphk GTCR_ADV_1000TFDX }, 9995707Sphk}; 10095707Sphk 10195707Sphkvoid 10295707Sphkmii_phy_setmedia(struct mii_softc *sc) 10395707Sphk{ 10495707Sphk struct mii_data *mii = sc->mii_pdata; 10595707Sphk struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 10695707Sphk int bmcr, anar, gtcr; 10795707Sphk 10895707Sphk if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) { 109215297Smarius /* 110215297Smarius * Force renegotiation if MIIF_DOPAUSE or MIIF_FORCEANEG. 111215297Smarius * The former is necessary as we might switch from flow- 112220938Smarius * control advertisement being off to on or vice versa. 113215297Smarius */ 114164702Smarius if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0 || 115215297Smarius (sc->mii_flags & (MIIF_DOPAUSE | MIIF_FORCEANEG)) != 0) 116214606Smarius (void)mii_phy_auto(sc); 11795707Sphk return; 11895707Sphk } 11995707Sphk 12095707Sphk /* 12195707Sphk * Table index is stored in the media entry. 12295707Sphk */ 12395707Sphk 12495718Sphk KASSERT(ife->ifm_data >=0 && ife->ifm_data < MII_NMEDIA, 12595718Sphk ("invalid ife->ifm_data (0x%x) in mii_phy_setmedia", 12695718Sphk ife->ifm_data)); 12795707Sphk 12895707Sphk anar = mii_media_table[ife->ifm_data].mm_anar; 12995707Sphk bmcr = mii_media_table[ife->ifm_data].mm_bmcr; 13095707Sphk gtcr = mii_media_table[ife->ifm_data].mm_gtcr; 13195707Sphk 132215297Smarius if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) { 133215297Smarius gtcr |= GTCR_MAN_MS; 134215297Smarius if ((ife->ifm_media & IFM_ETH_MASTER) != 0) 135215297Smarius gtcr |= GTCR_ADV_MS; 136215297Smarius } 13795707Sphk 138217414Smarius if ((ife->ifm_media & IFM_FDX) != 0 && 139217414Smarius ((ife->ifm_media & IFM_FLOW) != 0 || 140217414Smarius (sc->mii_flags & MIIF_FORCEPAUSE) != 0)) { 141215297Smarius if ((sc->mii_flags & MIIF_IS_1000X) != 0) 142215297Smarius anar |= ANAR_X_PAUSE_TOWARDS; 143215297Smarius else { 144215297Smarius anar |= ANAR_FC; 145215297Smarius /* XXX Only 1000BASE-T has PAUSE_ASYM? */ 146215297Smarius if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0 && 147215297Smarius (sc->mii_extcapabilities & 148215297Smarius (EXTSR_1000THDX | EXTSR_1000TFDX)) != 0) 149215297Smarius anar |= ANAR_X_PAUSE_ASYM; 15095707Sphk } 15195707Sphk } 15295707Sphk 15395707Sphk PHY_WRITE(sc, MII_ANAR, anar); 15495707Sphk PHY_WRITE(sc, MII_BMCR, bmcr); 155214606Smarius if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0) 15695707Sphk PHY_WRITE(sc, MII_100T2CR, gtcr); 15795707Sphk} 15895707Sphk 15950120Swpaulint 16096026Sphkmii_phy_auto(struct mii_softc *sc) 16150120Swpaul{ 162215297Smarius struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur; 163214606Smarius int anar, gtcr; 16450120Swpaul 16596026Sphk /* 16696026Sphk * Check for 1000BASE-X. Autonegotiation is a bit 16796026Sphk * different on such devices. 16896026Sphk */ 169214606Smarius if ((sc->mii_flags & MIIF_IS_1000X) != 0) { 170214606Smarius anar = 0; 171214606Smarius if ((sc->mii_extcapabilities & EXTSR_1000XFDX) != 0) 17296026Sphk anar |= ANAR_X_FD; 173214606Smarius if ((sc->mii_extcapabilities & EXTSR_1000XHDX) != 0) 17496026Sphk anar |= ANAR_X_HD; 17595718Sphk 176215297Smarius if ((ife->ifm_media & IFM_FLOW) != 0 || 177215297Smarius (sc->mii_flags & MIIF_FORCEPAUSE) != 0) 178215297Smarius anar |= ANAR_X_PAUSE_TOWARDS; 17996026Sphk PHY_WRITE(sc, MII_ANAR, anar); 18096026Sphk } else { 18196026Sphk anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | 18296026Sphk ANAR_CSMA; 183215297Smarius if ((ife->ifm_media & IFM_FLOW) != 0 || 184215297Smarius (sc->mii_flags & MIIF_FORCEPAUSE) != 0) { 185217414Smarius if ((sc->mii_capabilities & 186217414Smarius (BMSR_10TFDX | BMSR_100TXFDX)) != 0) 187215297Smarius anar |= ANAR_FC; 188215297Smarius /* XXX Only 1000BASE-T has PAUSE_ASYM? */ 189215297Smarius if (((sc->mii_flags & MIIF_HAVE_GTCR) != 0) && 190215297Smarius (sc->mii_extcapabilities & 191215297Smarius (EXTSR_1000THDX | EXTSR_1000TFDX)) != 0) 192215297Smarius anar |= ANAR_X_PAUSE_ASYM; 193215297Smarius } 19496026Sphk PHY_WRITE(sc, MII_ANAR, anar); 195214606Smarius if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0) { 196214606Smarius gtcr = 0; 197214606Smarius if ((sc->mii_extcapabilities & EXTSR_1000TFDX) != 0) 19896026Sphk gtcr |= GTCR_ADV_1000TFDX; 199214606Smarius if ((sc->mii_extcapabilities & EXTSR_1000THDX) != 0) 20096026Sphk gtcr |= GTCR_ADV_1000THDX; 20196026Sphk PHY_WRITE(sc, MII_100T2CR, gtcr); 20295718Sphk } 20350120Swpaul } 20496026Sphk PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG); 20550120Swpaul return (EJUSTRETURN); 20650120Swpaul} 20750120Swpaul 20884140Sjlemonint 20995718Sphkmii_phy_tick(struct mii_softc *sc) 21084140Sjlemon{ 21184140Sjlemon struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur; 21284140Sjlemon struct ifnet *ifp = sc->mii_pdata->mii_ifp; 21384140Sjlemon int reg; 21484140Sjlemon 21595718Sphk /* Just bail now if the interface is down. */ 21684140Sjlemon if ((ifp->if_flags & IFF_UP) == 0) 21784140Sjlemon return (EJUSTRETURN); 21884140Sjlemon 21984140Sjlemon /* 22084140Sjlemon * If we're not doing autonegotiation, we don't need to do 22184140Sjlemon * any extra work here. However, we need to check the link 22284140Sjlemon * status so we can generate an announcement if the status 22384140Sjlemon * changes. 22484140Sjlemon */ 225160082Soleg if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 226160082Soleg sc->mii_ticks = 0; /* reset autonegotiation timer. */ 22784140Sjlemon return (0); 228160082Soleg } 22984140Sjlemon 23095718Sphk /* Read the status register twice; BMSR_LINK is latch-low. */ 23184140Sjlemon reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 232214606Smarius if ((reg & BMSR_LINK) != 0) { 233158649Soleg sc->mii_ticks = 0; /* reset autonegotiation timer. */ 234158649Soleg /* See above. */ 23584140Sjlemon return (0); 23695718Sphk } 23784140Sjlemon 238158649Soleg /* Announce link loss right after it happens */ 239158649Soleg if (sc->mii_ticks++ == 0) 240128870Sandre return (0); 241158649Soleg 242158649Soleg /* XXX: use default value if phy driver did not set mii_anegticks */ 243158649Soleg if (sc->mii_anegticks == 0) 244158649Soleg sc->mii_anegticks = MII_ANEGTICKS_GIGE; 245158649Soleg 246158649Soleg /* Only retry autonegotiation every mii_anegticks ticks. */ 247158649Soleg if (sc->mii_ticks <= sc->mii_anegticks) 24884140Sjlemon return (EJUSTRETURN); 24984140Sjlemon 25084140Sjlemon sc->mii_ticks = 0; 251221407Smarius PHY_RESET(sc); 25296026Sphk mii_phy_auto(sc); 25384140Sjlemon return (0); 25484140Sjlemon} 25584140Sjlemon 25650120Swpaulvoid 25795718Sphkmii_phy_reset(struct mii_softc *sc) 25850120Swpaul{ 259164702Smarius struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur; 260221812Smarius int i, reg; 26150120Swpaul 262214606Smarius if ((sc->mii_flags & MIIF_NOISOLATE) != 0) 26350120Swpaul reg = BMCR_RESET; 26450120Swpaul else 26550120Swpaul reg = BMCR_RESET | BMCR_ISO; 26695718Sphk PHY_WRITE(sc, MII_BMCR, reg); 26750120Swpaul 26850120Swpaul /* Wait 100ms for it to complete. */ 26950120Swpaul for (i = 0; i < 100; i++) { 270164702Smarius reg = PHY_READ(sc, MII_BMCR); 27150120Swpaul if ((reg & BMCR_RESET) == 0) 27250120Swpaul break; 27350120Swpaul DELAY(1000); 27450120Swpaul } 27550120Swpaul 276225014Smarius /* NB: a PHY may default to being powered down and/or isolated. */ 277225014Smarius reg &= ~(BMCR_PDOWN | BMCR_ISO); 278221812Smarius if ((sc->mii_flags & MIIF_NOISOLATE) == 0 && 279221812Smarius ((ife == NULL && sc->mii_inst != 0) || 280221812Smarius (ife != NULL && IFM_INST(ife->ifm_media) != sc->mii_inst))) 281221812Smarius reg |= BMCR_ISO; 282221812Smarius if (PHY_READ(sc, MII_BMCR) != reg) 283221812Smarius PHY_WRITE(sc, MII_BMCR, reg); 28450120Swpaul} 28550120Swpaul 28684140Sjlemonvoid 28795722Sphkmii_phy_down(struct mii_softc *sc) 28884140Sjlemon{ 28995722Sphk 29095722Sphk} 29195722Sphk 29295722Sphkvoid 29395722Sphkmii_phy_update(struct mii_softc *sc, int cmd) 29495722Sphk{ 29584140Sjlemon struct mii_data *mii = sc->mii_pdata; 29684140Sjlemon 29795722Sphk if (sc->mii_media_active != mii->mii_media_active || 29895722Sphk cmd == MII_MEDIACHG) { 29984140Sjlemon MIIBUS_STATCHG(sc->mii_dev); 30095705Sphk sc->mii_media_active = mii->mii_media_active; 30184140Sjlemon } 30295705Sphk if (sc->mii_media_status != mii->mii_media_status) { 30384140Sjlemon MIIBUS_LINKCHG(sc->mii_dev); 30495705Sphk sc->mii_media_status = mii->mii_media_status; 30584140Sjlemon } 30684140Sjlemon} 30784140Sjlemon 30850120Swpaul/* 30950120Swpaul * Initialize generic PHY media based on BMSR, called when a PHY is 31050120Swpaul * attached. We expect to be set up to print a comma-separated list 31150120Swpaul * of media names. Does not print a newline. 31250120Swpaul */ 31350120Swpaulvoid 31495718Sphkmii_phy_add_media(struct mii_softc *sc) 31595718Sphk{ 31695718Sphk struct mii_data *mii = sc->mii_pdata; 31795718Sphk const char *sep = ""; 318215297Smarius int fdx = 0; 31995718Sphk 320164703Smarius if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 && 321164703Smarius (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0) { 322164703Smarius printf("no media present"); 323164703Smarius return; 324164703Smarius } 325164703Smarius 326214606Smarius /* 327214606Smarius * Set the autonegotiation timer for 10/100 media. Gigabit media is 328214606Smarius * handled below. 329214606Smarius */ 330158649Soleg sc->mii_anegticks = MII_ANEGTICKS; 331158649Soleg 33295718Sphk#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) 33395718Sphk#define PRINT(s) printf("%s%s", sep, s); sep = ", " 33495718Sphk 335221407Smarius if ((sc->mii_flags & MIIF_NOISOLATE) == 0) { 33695718Sphk ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst), 33795718Sphk MII_MEDIA_NONE); 338221407Smarius PRINT("none"); 339221407Smarius } 34095718Sphk 34195718Sphk /* 34295718Sphk * There are different interpretations for the bits in 34395718Sphk * HomePNA PHYs. And there is really only one media type 34495718Sphk * that is supported. 34595718Sphk */ 346214606Smarius if ((sc->mii_flags & MIIF_IS_HPNA) != 0) { 347214606Smarius if ((sc->mii_capabilities & BMSR_10THDX) != 0) { 34895718Sphk ADD(IFM_MAKEWORD(IFM_ETHER, IFM_HPNA_1, 0, 349214606Smarius sc->mii_inst), MII_MEDIA_10_T); 35095718Sphk PRINT("HomePNA1"); 35195718Sphk } 35295718Sphk return; 35395718Sphk } 35495718Sphk 355214606Smarius if ((sc->mii_capabilities & BMSR_10THDX) != 0) { 35695718Sphk ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst), 35795718Sphk MII_MEDIA_10_T); 35895718Sphk PRINT("10baseT"); 35995718Sphk } 360214606Smarius if ((sc->mii_capabilities & BMSR_10TFDX) != 0) { 36195718Sphk ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst), 36295718Sphk MII_MEDIA_10_T_FDX); 36395718Sphk PRINT("10baseT-FDX"); 364215297Smarius if ((sc->mii_flags & MIIF_DOPAUSE) != 0 && 365215297Smarius (sc->mii_flags & MIIF_NOMANPAUSE) == 0) { 366215297Smarius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 367215297Smarius IFM_FDX | IFM_FLOW, sc->mii_inst), 368215297Smarius MII_MEDIA_10_T_FDX); 369215297Smarius PRINT("10baseT-FDX-flow"); 370215297Smarius } 371215297Smarius fdx = 1; 37295718Sphk } 373214606Smarius if ((sc->mii_capabilities & BMSR_100TXHDX) != 0) { 37495718Sphk ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst), 37595718Sphk MII_MEDIA_100_TX); 37695718Sphk PRINT("100baseTX"); 37795718Sphk } 378214606Smarius if ((sc->mii_capabilities & BMSR_100TXFDX) != 0) { 37995718Sphk ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst), 38095718Sphk MII_MEDIA_100_TX_FDX); 38195718Sphk PRINT("100baseTX-FDX"); 382215297Smarius if ((sc->mii_flags & MIIF_DOPAUSE) != 0 && 383215297Smarius (sc->mii_flags & MIIF_NOMANPAUSE) == 0) { 384215297Smarius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 385215297Smarius IFM_FDX | IFM_FLOW, sc->mii_inst), 386215297Smarius MII_MEDIA_100_TX_FDX); 387215297Smarius PRINT("100baseTX-FDX-flow"); 388215297Smarius } 389215297Smarius fdx = 1; 39095718Sphk } 391214606Smarius if ((sc->mii_capabilities & BMSR_100T4) != 0) { 39295718Sphk ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, 0, sc->mii_inst), 39395718Sphk MII_MEDIA_100_T4); 39495718Sphk PRINT("100baseT4"); 39595718Sphk } 39695718Sphk 397214606Smarius if ((sc->mii_extcapabilities & EXTSR_MEDIAMASK) != 0) { 39895718Sphk /* 39995718Sphk * XXX Right now only handle 1000SX and 1000TX. Need 400214606Smarius * XXX to handle 1000LX and 1000CX somehow. 40195718Sphk */ 402214606Smarius if ((sc->mii_extcapabilities & EXTSR_1000XHDX) != 0) { 403158649Soleg sc->mii_anegticks = MII_ANEGTICKS_GIGE; 40495718Sphk sc->mii_flags |= MIIF_IS_1000X; 40595718Sphk ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, 0, 40695718Sphk sc->mii_inst), MII_MEDIA_1000_X); 40795718Sphk PRINT("1000baseSX"); 40895718Sphk } 409214606Smarius if ((sc->mii_extcapabilities & EXTSR_1000XFDX) != 0) { 410158649Soleg sc->mii_anegticks = MII_ANEGTICKS_GIGE; 41195718Sphk sc->mii_flags |= MIIF_IS_1000X; 41295718Sphk ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX, 41395718Sphk sc->mii_inst), MII_MEDIA_1000_X_FDX); 41495718Sphk PRINT("1000baseSX-FDX"); 415215297Smarius if ((sc->mii_flags & MIIF_DOPAUSE) != 0 && 416215297Smarius (sc->mii_flags & MIIF_NOMANPAUSE) == 0) { 417215297Smarius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, 418215297Smarius IFM_FDX | IFM_FLOW, sc->mii_inst), 419215297Smarius MII_MEDIA_1000_X_FDX); 420215297Smarius PRINT("1000baseSX-FDX-flow"); 421215297Smarius } 422215297Smarius fdx = 1; 42395718Sphk } 42495718Sphk 42595718Sphk /* 42695718Sphk * 1000baseT media needs to be able to manipulate 427215297Smarius * master/slave mode. 42895718Sphk * 42995718Sphk * All 1000baseT PHYs have a 1000baseT control register. 43095718Sphk */ 431214606Smarius if ((sc->mii_extcapabilities & EXTSR_1000THDX) != 0) { 432158649Soleg sc->mii_anegticks = MII_ANEGTICKS_GIGE; 43395718Sphk sc->mii_flags |= MIIF_HAVE_GTCR; 43495718Sphk ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0, 43595718Sphk sc->mii_inst), MII_MEDIA_1000_T); 43695718Sphk PRINT("1000baseT"); 437215297Smarius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 438215297Smarius IFM_ETH_MASTER, sc->mii_inst), MII_MEDIA_1000_T); 439215297Smarius PRINT("1000baseT-master"); 44095718Sphk } 441214606Smarius if ((sc->mii_extcapabilities & EXTSR_1000TFDX) != 0) { 442158649Soleg sc->mii_anegticks = MII_ANEGTICKS_GIGE; 44395718Sphk sc->mii_flags |= MIIF_HAVE_GTCR; 44495718Sphk ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX, 44595718Sphk sc->mii_inst), MII_MEDIA_1000_T_FDX); 44695718Sphk PRINT("1000baseT-FDX"); 447215297Smarius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 448215297Smarius IFM_FDX | IFM_ETH_MASTER, sc->mii_inst), 449215297Smarius MII_MEDIA_1000_T_FDX); 450215297Smarius PRINT("1000baseT-FDX-master"); 451215297Smarius if ((sc->mii_flags & MIIF_DOPAUSE) != 0 && 452215297Smarius (sc->mii_flags & MIIF_NOMANPAUSE) == 0) { 453215297Smarius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 454215297Smarius IFM_FDX | IFM_FLOW, sc->mii_inst), 455215297Smarius MII_MEDIA_1000_T_FDX); 456215297Smarius PRINT("1000baseT-FDX-flow"); 457215297Smarius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 458215297Smarius IFM_FDX | IFM_FLOW | IFM_ETH_MASTER, 459215297Smarius sc->mii_inst), MII_MEDIA_1000_T_FDX); 460215297Smarius PRINT("1000baseT-FDX-flow-master"); 461215297Smarius } 462215297Smarius fdx = 1; 46395718Sphk } 46495718Sphk } 46595718Sphk 466214606Smarius if ((sc->mii_capabilities & BMSR_ANEG) != 0) { 467215297Smarius /* intentionally invalid index */ 46895718Sphk ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 469215297Smarius MII_NMEDIA); 47095718Sphk PRINT("auto"); 471215297Smarius if (fdx != 0 && (sc->mii_flags & MIIF_DOPAUSE) != 0) { 472215297Smarius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, IFM_FLOW, 473215297Smarius sc->mii_inst), MII_NMEDIA); 474215297Smarius PRINT("auto-flow"); 475215297Smarius } 47695718Sphk } 47795718Sphk#undef ADD 47895718Sphk#undef PRINT 47995718Sphk} 48095718Sphk 48195722Sphkint 48295722Sphkmii_phy_detach(device_t dev) 48395722Sphk{ 48495722Sphk struct mii_softc *sc; 48595722Sphk 48695722Sphk sc = device_get_softc(dev); 48795722Sphk mii_phy_down(sc); 48895722Sphk sc->mii_dev = NULL; 48995722Sphk LIST_REMOVE(sc, mii_list); 490214606Smarius return (0); 49195722Sphk} 49295724Sphk 49395724Sphkconst struct mii_phydesc * 494150756Simpmii_phy_match_gen(const struct mii_attach_args *ma, 495150756Simp const struct mii_phydesc *mpd, size_t len) 49695724Sphk{ 49795724Sphk 498150756Simp for (; mpd->mpd_name != NULL; 499215297Smarius mpd = (const struct mii_phydesc *)((const char *)mpd + len)) { 50095724Sphk if (MII_OUI(ma->mii_id1, ma->mii_id2) == mpd->mpd_oui && 50195724Sphk MII_MODEL(ma->mii_id2) == mpd->mpd_model) 50295724Sphk return (mpd); 50395724Sphk } 50495724Sphk return (NULL); 50595724Sphk} 506150756Simp 507150756Simpconst struct mii_phydesc * 508150756Simpmii_phy_match(const struct mii_attach_args *ma, const struct mii_phydesc *mpd) 509150756Simp{ 510164702Smarius 511150756Simp return (mii_phy_match_gen(ma, mpd, sizeof(struct mii_phydesc))); 512150756Simp} 513164827Smarius 514164827Smariusint 515164827Smariusmii_phy_dev_probe(device_t dev, const struct mii_phydesc *mpd, int mrv) 516164827Smarius{ 517164827Smarius 518164827Smarius mpd = mii_phy_match(device_get_ivars(dev), mpd); 519164827Smarius if (mpd != NULL) { 520164827Smarius device_set_desc(dev, mpd->mpd_name); 521164827Smarius return (mrv); 522164827Smarius } 523164827Smarius return (ENXIO); 524164827Smarius} 525215297Smarius 526221407Smariusvoid 527221407Smariusmii_phy_dev_attach(device_t dev, u_int flags, const struct mii_phy_funcs *mpf, 528221407Smarius int add_media) 529221407Smarius{ 530221407Smarius struct mii_softc *sc; 531221407Smarius struct mii_attach_args *ma; 532221407Smarius struct mii_data *mii; 533221407Smarius 534221407Smarius sc = device_get_softc(dev); 535221407Smarius ma = device_get_ivars(dev); 536221407Smarius sc->mii_dev = device_get_parent(dev); 537221407Smarius mii = ma->mii_data; 538221407Smarius LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 539221407Smarius 540221407Smarius sc->mii_flags = flags | miibus_get_flags(dev); 541221407Smarius sc->mii_mpd_oui = MII_OUI(ma->mii_id1, ma->mii_id2); 542221407Smarius sc->mii_mpd_model = MII_MODEL(ma->mii_id2); 543221407Smarius sc->mii_mpd_rev = MII_REV(ma->mii_id2); 544221407Smarius sc->mii_capmask = ma->mii_capmask; 545221407Smarius sc->mii_inst = mii->mii_instance++; 546221407Smarius sc->mii_phy = ma->mii_phyno; 547221407Smarius sc->mii_offset = ma->mii_offset; 548221407Smarius sc->mii_funcs = mpf; 549221407Smarius sc->mii_pdata = mii; 550221407Smarius 551221407Smarius if (bootverbose) 552221407Smarius device_printf(dev, "OUI 0x%06x, model 0x%04x, rev. %d\n", 553221407Smarius sc->mii_mpd_oui, sc->mii_mpd_model, sc->mii_mpd_rev); 554221407Smarius 555221407Smarius if (add_media == 0) 556221407Smarius return; 557221407Smarius 558221407Smarius PHY_RESET(sc); 559221407Smarius 560221407Smarius sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & sc->mii_capmask; 561221407Smarius if (sc->mii_capabilities & BMSR_EXTSTAT) 562221407Smarius sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); 563221407Smarius device_printf(dev, " "); 564221407Smarius mii_phy_add_media(sc); 565221407Smarius printf("\n"); 566221407Smarius 567221407Smarius MIIBUS_MEDIAINIT(sc->mii_dev); 568221407Smarius} 569221407Smarius 570215297Smarius/* 571215297Smarius * Return the flow control status flag from MII_ANAR & MII_ANLPAR. 572215297Smarius */ 573215297Smariusu_int 574215297Smariusmii_phy_flowstatus(struct mii_softc *sc) 575215297Smarius{ 576215297Smarius int anar, anlpar; 577215297Smarius 578215297Smarius if ((sc->mii_flags & MIIF_DOPAUSE) == 0) 579215297Smarius return (0); 580215297Smarius 581215297Smarius anar = PHY_READ(sc, MII_ANAR); 582215297Smarius anlpar = PHY_READ(sc, MII_ANLPAR); 583215297Smarius 584215297Smarius /* 585215297Smarius * Check for 1000BASE-X. Autonegotiation is a bit 586215297Smarius * different on such devices. 587215297Smarius */ 588215297Smarius if ((sc->mii_flags & MIIF_IS_1000X) != 0) { 589215297Smarius anar <<= 3; 590215297Smarius anlpar <<= 3; 591215297Smarius } 592215297Smarius 593215297Smarius if ((anar & ANAR_PAUSE_SYM) != 0 && (anlpar & ANLPAR_PAUSE_SYM) != 0) 594215297Smarius return (IFM_FLOW | IFM_ETH_TXPAUSE | IFM_ETH_RXPAUSE); 595215297Smarius 596215297Smarius if ((anar & ANAR_PAUSE_SYM) == 0) { 597215297Smarius if ((anar & ANAR_PAUSE_ASYM) != 0 && 598215297Smarius (anlpar & ANLPAR_PAUSE_TOWARDS) != 0) 599215297Smarius return (IFM_FLOW | IFM_ETH_TXPAUSE); 600215297Smarius else 601215297Smarius return (0); 602215297Smarius } 603215297Smarius 604215297Smarius if ((anar & ANAR_PAUSE_ASYM) == 0) { 605215297Smarius if ((anlpar & ANLPAR_PAUSE_SYM) != 0) 606215297Smarius return (IFM_FLOW | IFM_ETH_TXPAUSE | IFM_ETH_RXPAUSE); 607215297Smarius else 608215297Smarius return (0); 609215297Smarius } 610215297Smarius 611215297Smarius switch ((anlpar & ANLPAR_PAUSE_TOWARDS)) { 612215297Smarius case ANLPAR_PAUSE_NONE: 613215297Smarius return (0); 614215297Smarius case ANLPAR_PAUSE_ASYM: 615215297Smarius return (IFM_FLOW | IFM_ETH_RXPAUSE); 616215297Smarius default: 617215297Smarius return (IFM_FLOW | IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE); 618215297Smarius } 619215297Smarius /* NOTREACHED */ 620215297Smarius} 621