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/* 57281821Sglebius * 58281821Sglebius * An array of structures to map MII media types to BMCR/ANAR settings. 5995707Sphk */ 60281821Sglebiusenum { 61281821Sglebius MII_MEDIA_NONE = 0, 62281821Sglebius MII_MEDIA_10_T, 63281821Sglebius MII_MEDIA_10_T_FDX, 64281821Sglebius MII_MEDIA_100_T4, 65281821Sglebius MII_MEDIA_100_TX, 66281821Sglebius MII_MEDIA_100_TX_FDX, 67281821Sglebius MII_MEDIA_1000_X, 68281821Sglebius MII_MEDIA_1000_X_FDX, 69281821Sglebius MII_MEDIA_1000_T, 70281821Sglebius MII_MEDIA_1000_T_FDX, 71281821Sglebius MII_NMEDIA, 72281821Sglebius}; 73281821Sglebius 74281821Sglebiusstatic const struct mii_media { 75281821Sglebius u_int mm_bmcr; /* BMCR settings for this media */ 76281821Sglebius u_int mm_anar; /* ANAR settings for this media */ 77281821Sglebius u_int mm_gtcr; /* 100base-T2 or 1000base-T CR */ 78281821Sglebius} mii_media_table[MII_NMEDIA] = { 7995707Sphk /* None */ 8095707Sphk { BMCR_ISO, ANAR_CSMA, 8195707Sphk 0, }, 8295707Sphk 8395707Sphk /* 10baseT */ 8495707Sphk { BMCR_S10, ANAR_CSMA|ANAR_10, 8595707Sphk 0, }, 8695707Sphk 8795707Sphk /* 10baseT-FDX */ 8895707Sphk { BMCR_S10|BMCR_FDX, ANAR_CSMA|ANAR_10_FD, 8995707Sphk 0, }, 9095707Sphk 9195707Sphk /* 100baseT4 */ 9295707Sphk { BMCR_S100, ANAR_CSMA|ANAR_T4, 9395707Sphk 0, }, 9495707Sphk 9595707Sphk /* 100baseTX */ 9695707Sphk { BMCR_S100, ANAR_CSMA|ANAR_TX, 9795707Sphk 0, }, 9895707Sphk 9995707Sphk /* 100baseTX-FDX */ 10095707Sphk { BMCR_S100|BMCR_FDX, ANAR_CSMA|ANAR_TX_FD, 10195707Sphk 0, }, 10295707Sphk 10395707Sphk /* 1000baseX */ 10495707Sphk { BMCR_S1000, ANAR_CSMA, 10595707Sphk 0, }, 10695707Sphk 10795707Sphk /* 1000baseX-FDX */ 10895707Sphk { BMCR_S1000|BMCR_FDX, ANAR_CSMA, 10995707Sphk 0, }, 11095707Sphk 11195707Sphk /* 1000baseT */ 11295707Sphk { BMCR_S1000, ANAR_CSMA, 11395707Sphk GTCR_ADV_1000THDX }, 11495707Sphk 11595707Sphk /* 1000baseT-FDX */ 11695707Sphk { BMCR_S1000, ANAR_CSMA, 11795707Sphk GTCR_ADV_1000TFDX }, 11895707Sphk}; 11995707Sphk 12095707Sphkvoid 12195707Sphkmii_phy_setmedia(struct mii_softc *sc) 12295707Sphk{ 12395707Sphk struct mii_data *mii = sc->mii_pdata; 12495707Sphk struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 12595707Sphk int bmcr, anar, gtcr; 126281821Sglebius int index = -1; 12795707Sphk 128281821Sglebius switch (IFM_SUBTYPE(ife->ifm_media)) { 129281821Sglebius case IFM_AUTO: 130215297Smarius /* 131215297Smarius * Force renegotiation if MIIF_DOPAUSE or MIIF_FORCEANEG. 132215297Smarius * The former is necessary as we might switch from flow- 133220938Smarius * control advertisement being off to on or vice versa. 134215297Smarius */ 135164702Smarius if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0 || 136215297Smarius (sc->mii_flags & (MIIF_DOPAUSE | MIIF_FORCEANEG)) != 0) 137214606Smarius (void)mii_phy_auto(sc); 13895707Sphk return; 139281821Sglebius 140281821Sglebius case IFM_NONE: 141281821Sglebius index = MII_MEDIA_NONE; 142281821Sglebius break; 143281821Sglebius 144281821Sglebius case IFM_HPNA_1: 145281821Sglebius index = MII_MEDIA_10_T; 146281821Sglebius break; 147281821Sglebius 148281821Sglebius case IFM_10_T: 149281821Sglebius switch (IFM_OPTIONS(ife->ifm_media)) { 150281821Sglebius case 0: 151281821Sglebius index = MII_MEDIA_10_T; 152281821Sglebius break; 153281821Sglebius case IFM_FDX: 154281821Sglebius case (IFM_FDX | IFM_FLOW): 155281821Sglebius index = MII_MEDIA_10_T_FDX; 156281821Sglebius break; 157297793Spfg } 158281821Sglebius break; 159281821Sglebius 160281821Sglebius case IFM_100_TX: 161281821Sglebius case IFM_100_FX: 162281821Sglebius switch (IFM_OPTIONS(ife->ifm_media)) { 163281821Sglebius case 0: 164281821Sglebius index = MII_MEDIA_100_TX; 165281821Sglebius break; 166281821Sglebius case IFM_FDX: 167281821Sglebius case (IFM_FDX | IFM_FLOW): 168281821Sglebius index = MII_MEDIA_100_TX_FDX; 169281821Sglebius break; 170281821Sglebius } 171281821Sglebius break; 172281821Sglebius 173281821Sglebius case IFM_100_T4: 174281821Sglebius index = MII_MEDIA_100_T4; 175281821Sglebius break; 176281821Sglebius 177281821Sglebius case IFM_1000_SX: 178281821Sglebius switch (IFM_OPTIONS(ife->ifm_media)) { 179281821Sglebius case 0: 180281821Sglebius index = MII_MEDIA_1000_X; 181281821Sglebius break; 182281821Sglebius case IFM_FDX: 183281821Sglebius case (IFM_FDX | IFM_FLOW): 184281821Sglebius index = MII_MEDIA_1000_X_FDX; 185281821Sglebius break; 186281821Sglebius } 187281821Sglebius break; 188281821Sglebius 189281821Sglebius case IFM_1000_T: 190281821Sglebius switch (IFM_OPTIONS(ife->ifm_media)) { 191281821Sglebius case 0: 192281821Sglebius case IFM_ETH_MASTER: 193281821Sglebius index = MII_MEDIA_1000_T; 194281821Sglebius break; 195281821Sglebius case IFM_FDX: 196281821Sglebius case (IFM_FDX | IFM_ETH_MASTER): 197281821Sglebius case (IFM_FDX | IFM_FLOW): 198281821Sglebius case (IFM_FDX | IFM_FLOW | IFM_ETH_MASTER): 199281821Sglebius index = MII_MEDIA_1000_T_FDX; 200281821Sglebius break; 201281821Sglebius } 202281821Sglebius break; 20395707Sphk } 20495707Sphk 205281821Sglebius KASSERT(index != -1, ("%s: failed to map media word %d", 206281821Sglebius __func__, ife->ifm_media)); 20795707Sphk 208281821Sglebius anar = mii_media_table[index].mm_anar; 209281821Sglebius bmcr = mii_media_table[index].mm_bmcr; 210281821Sglebius gtcr = mii_media_table[index].mm_gtcr; 21195707Sphk 212215297Smarius if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) { 213215297Smarius gtcr |= GTCR_MAN_MS; 214215297Smarius if ((ife->ifm_media & IFM_ETH_MASTER) != 0) 215215297Smarius gtcr |= GTCR_ADV_MS; 216215297Smarius } 21795707Sphk 218217414Smarius if ((ife->ifm_media & IFM_FDX) != 0 && 219217414Smarius ((ife->ifm_media & IFM_FLOW) != 0 || 220217414Smarius (sc->mii_flags & MIIF_FORCEPAUSE) != 0)) { 221215297Smarius if ((sc->mii_flags & MIIF_IS_1000X) != 0) 222215297Smarius anar |= ANAR_X_PAUSE_TOWARDS; 223215297Smarius else { 224215297Smarius anar |= ANAR_FC; 225215297Smarius /* XXX Only 1000BASE-T has PAUSE_ASYM? */ 226215297Smarius if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0 && 227215297Smarius (sc->mii_extcapabilities & 228215297Smarius (EXTSR_1000THDX | EXTSR_1000TFDX)) != 0) 229215297Smarius anar |= ANAR_X_PAUSE_ASYM; 23095707Sphk } 23195707Sphk } 23295707Sphk 23395707Sphk PHY_WRITE(sc, MII_ANAR, anar); 23495707Sphk PHY_WRITE(sc, MII_BMCR, bmcr); 235214606Smarius if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0) 23695707Sphk PHY_WRITE(sc, MII_100T2CR, gtcr); 23795707Sphk} 23895707Sphk 23950120Swpaulint 24096026Sphkmii_phy_auto(struct mii_softc *sc) 24150120Swpaul{ 242215297Smarius struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur; 243214606Smarius int anar, gtcr; 24450120Swpaul 24596026Sphk /* 24696026Sphk * Check for 1000BASE-X. Autonegotiation is a bit 24796026Sphk * different on such devices. 24896026Sphk */ 249214606Smarius if ((sc->mii_flags & MIIF_IS_1000X) != 0) { 250214606Smarius anar = 0; 251214606Smarius if ((sc->mii_extcapabilities & EXTSR_1000XFDX) != 0) 25296026Sphk anar |= ANAR_X_FD; 253214606Smarius if ((sc->mii_extcapabilities & EXTSR_1000XHDX) != 0) 25496026Sphk anar |= ANAR_X_HD; 25595718Sphk 256215297Smarius if ((ife->ifm_media & IFM_FLOW) != 0 || 257215297Smarius (sc->mii_flags & MIIF_FORCEPAUSE) != 0) 258215297Smarius anar |= ANAR_X_PAUSE_TOWARDS; 25996026Sphk PHY_WRITE(sc, MII_ANAR, anar); 26096026Sphk } else { 26196026Sphk anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | 26296026Sphk ANAR_CSMA; 263215297Smarius if ((ife->ifm_media & IFM_FLOW) != 0 || 264215297Smarius (sc->mii_flags & MIIF_FORCEPAUSE) != 0) { 265217414Smarius if ((sc->mii_capabilities & 266217414Smarius (BMSR_10TFDX | BMSR_100TXFDX)) != 0) 267215297Smarius anar |= ANAR_FC; 268215297Smarius /* XXX Only 1000BASE-T has PAUSE_ASYM? */ 269215297Smarius if (((sc->mii_flags & MIIF_HAVE_GTCR) != 0) && 270215297Smarius (sc->mii_extcapabilities & 271215297Smarius (EXTSR_1000THDX | EXTSR_1000TFDX)) != 0) 272215297Smarius anar |= ANAR_X_PAUSE_ASYM; 273215297Smarius } 27496026Sphk PHY_WRITE(sc, MII_ANAR, anar); 275214606Smarius if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0) { 276214606Smarius gtcr = 0; 277214606Smarius if ((sc->mii_extcapabilities & EXTSR_1000TFDX) != 0) 27896026Sphk gtcr |= GTCR_ADV_1000TFDX; 279214606Smarius if ((sc->mii_extcapabilities & EXTSR_1000THDX) != 0) 28096026Sphk gtcr |= GTCR_ADV_1000THDX; 28196026Sphk PHY_WRITE(sc, MII_100T2CR, gtcr); 28295718Sphk } 28350120Swpaul } 28496026Sphk PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG); 28550120Swpaul return (EJUSTRETURN); 28650120Swpaul} 28750120Swpaul 28884140Sjlemonint 28995718Sphkmii_phy_tick(struct mii_softc *sc) 29084140Sjlemon{ 29184140Sjlemon struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur; 29284140Sjlemon int reg; 29384140Sjlemon 29484140Sjlemon /* 29584140Sjlemon * If we're not doing autonegotiation, we don't need to do 29684140Sjlemon * any extra work here. However, we need to check the link 29784140Sjlemon * status so we can generate an announcement if the status 29884140Sjlemon * changes. 29984140Sjlemon */ 300160082Soleg if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 301160082Soleg sc->mii_ticks = 0; /* reset autonegotiation timer. */ 30284140Sjlemon return (0); 303160082Soleg } 30484140Sjlemon 30595718Sphk /* Read the status register twice; BMSR_LINK is latch-low. */ 30684140Sjlemon reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 307214606Smarius if ((reg & BMSR_LINK) != 0) { 308158649Soleg sc->mii_ticks = 0; /* reset autonegotiation timer. */ 309158649Soleg /* See above. */ 31084140Sjlemon return (0); 31195718Sphk } 31284140Sjlemon 313158649Soleg /* Announce link loss right after it happens */ 314158649Soleg if (sc->mii_ticks++ == 0) 315128870Sandre return (0); 316158649Soleg 317158649Soleg /* XXX: use default value if phy driver did not set mii_anegticks */ 318158649Soleg if (sc->mii_anegticks == 0) 319158649Soleg sc->mii_anegticks = MII_ANEGTICKS_GIGE; 320158649Soleg 321158649Soleg /* Only retry autonegotiation every mii_anegticks ticks. */ 322158649Soleg if (sc->mii_ticks <= sc->mii_anegticks) 32384140Sjlemon return (EJUSTRETURN); 32484140Sjlemon 32584140Sjlemon sc->mii_ticks = 0; 326221407Smarius PHY_RESET(sc); 32796026Sphk mii_phy_auto(sc); 32884140Sjlemon return (0); 32984140Sjlemon} 33084140Sjlemon 33150120Swpaulvoid 33295718Sphkmii_phy_reset(struct mii_softc *sc) 33350120Swpaul{ 334164702Smarius struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur; 335221812Smarius int i, reg; 33650120Swpaul 337214606Smarius if ((sc->mii_flags & MIIF_NOISOLATE) != 0) 33850120Swpaul reg = BMCR_RESET; 33950120Swpaul else 34050120Swpaul reg = BMCR_RESET | BMCR_ISO; 34195718Sphk PHY_WRITE(sc, MII_BMCR, reg); 34250120Swpaul 34350120Swpaul /* Wait 100ms for it to complete. */ 34450120Swpaul for (i = 0; i < 100; i++) { 345164702Smarius reg = PHY_READ(sc, MII_BMCR); 34650120Swpaul if ((reg & BMCR_RESET) == 0) 34750120Swpaul break; 34850120Swpaul DELAY(1000); 34950120Swpaul } 35050120Swpaul 351225014Smarius /* NB: a PHY may default to being powered down and/or isolated. */ 352225014Smarius reg &= ~(BMCR_PDOWN | BMCR_ISO); 353221812Smarius if ((sc->mii_flags & MIIF_NOISOLATE) == 0 && 354221812Smarius ((ife == NULL && sc->mii_inst != 0) || 355221812Smarius (ife != NULL && IFM_INST(ife->ifm_media) != sc->mii_inst))) 356221812Smarius reg |= BMCR_ISO; 357221812Smarius if (PHY_READ(sc, MII_BMCR) != reg) 358221812Smarius PHY_WRITE(sc, MII_BMCR, reg); 35950120Swpaul} 36050120Swpaul 36184140Sjlemonvoid 36295722Sphkmii_phy_update(struct mii_softc *sc, int cmd) 36395722Sphk{ 36484140Sjlemon struct mii_data *mii = sc->mii_pdata; 36584140Sjlemon 36695722Sphk if (sc->mii_media_active != mii->mii_media_active || 36795722Sphk cmd == MII_MEDIACHG) { 36884140Sjlemon MIIBUS_STATCHG(sc->mii_dev); 36995705Sphk sc->mii_media_active = mii->mii_media_active; 37084140Sjlemon } 37195705Sphk if (sc->mii_media_status != mii->mii_media_status) { 37284140Sjlemon MIIBUS_LINKCHG(sc->mii_dev); 37395705Sphk sc->mii_media_status = mii->mii_media_status; 37484140Sjlemon } 37584140Sjlemon} 37684140Sjlemon 37750120Swpaul/* 37850120Swpaul * Initialize generic PHY media based on BMSR, called when a PHY is 37950120Swpaul * attached. We expect to be set up to print a comma-separated list 38050120Swpaul * of media names. Does not print a newline. 38150120Swpaul */ 38250120Swpaulvoid 38395718Sphkmii_phy_add_media(struct mii_softc *sc) 38495718Sphk{ 38595718Sphk struct mii_data *mii = sc->mii_pdata; 38695718Sphk const char *sep = ""; 387215297Smarius int fdx = 0; 38895718Sphk 389164703Smarius if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 && 390164703Smarius (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0) { 391164703Smarius printf("no media present"); 392164703Smarius return; 393164703Smarius } 394164703Smarius 395214606Smarius /* 396214606Smarius * Set the autonegotiation timer for 10/100 media. Gigabit media is 397214606Smarius * handled below. 398214606Smarius */ 399158649Soleg sc->mii_anegticks = MII_ANEGTICKS; 400158649Soleg 401281821Sglebius#define ADD(m) ifmedia_add(&mii->mii_media, (m), 0, NULL) 40295718Sphk#define PRINT(s) printf("%s%s", sep, s); sep = ", " 40395718Sphk 404221407Smarius if ((sc->mii_flags & MIIF_NOISOLATE) == 0) { 405281821Sglebius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst)); 406221407Smarius PRINT("none"); 407221407Smarius } 40895718Sphk 40995718Sphk /* 41095718Sphk * There are different interpretations for the bits in 41195718Sphk * HomePNA PHYs. And there is really only one media type 41295718Sphk * that is supported. 41395718Sphk */ 414214606Smarius if ((sc->mii_flags & MIIF_IS_HPNA) != 0) { 415214606Smarius if ((sc->mii_capabilities & BMSR_10THDX) != 0) { 41695718Sphk ADD(IFM_MAKEWORD(IFM_ETHER, IFM_HPNA_1, 0, 417281821Sglebius sc->mii_inst)); 41895718Sphk PRINT("HomePNA1"); 41995718Sphk } 42095718Sphk return; 42195718Sphk } 42295718Sphk 423214606Smarius if ((sc->mii_capabilities & BMSR_10THDX) != 0) { 424281821Sglebius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst)); 42595718Sphk PRINT("10baseT"); 42695718Sphk } 427214606Smarius if ((sc->mii_capabilities & BMSR_10TFDX) != 0) { 428281821Sglebius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst)); 42995718Sphk PRINT("10baseT-FDX"); 430215297Smarius if ((sc->mii_flags & MIIF_DOPAUSE) != 0 && 431215297Smarius (sc->mii_flags & MIIF_NOMANPAUSE) == 0) { 432215297Smarius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 433281821Sglebius IFM_FDX | IFM_FLOW, sc->mii_inst)); 434215297Smarius PRINT("10baseT-FDX-flow"); 435215297Smarius } 436215297Smarius fdx = 1; 43795718Sphk } 438214606Smarius if ((sc->mii_capabilities & BMSR_100TXHDX) != 0) { 439281821Sglebius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst)); 44095718Sphk PRINT("100baseTX"); 44195718Sphk } 442214606Smarius if ((sc->mii_capabilities & BMSR_100TXFDX) != 0) { 443281821Sglebius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst)); 44495718Sphk PRINT("100baseTX-FDX"); 445215297Smarius if ((sc->mii_flags & MIIF_DOPAUSE) != 0 && 446215297Smarius (sc->mii_flags & MIIF_NOMANPAUSE) == 0) { 447215297Smarius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 448281821Sglebius IFM_FDX | IFM_FLOW, sc->mii_inst)); 449215297Smarius PRINT("100baseTX-FDX-flow"); 450215297Smarius } 451215297Smarius fdx = 1; 45295718Sphk } 453214606Smarius if ((sc->mii_capabilities & BMSR_100T4) != 0) { 454281821Sglebius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, 0, sc->mii_inst)); 45595718Sphk PRINT("100baseT4"); 45695718Sphk } 45795718Sphk 458214606Smarius if ((sc->mii_extcapabilities & EXTSR_MEDIAMASK) != 0) { 45995718Sphk /* 46095718Sphk * XXX Right now only handle 1000SX and 1000TX. Need 461214606Smarius * XXX to handle 1000LX and 1000CX somehow. 46295718Sphk */ 463214606Smarius if ((sc->mii_extcapabilities & EXTSR_1000XHDX) != 0) { 464158649Soleg sc->mii_anegticks = MII_ANEGTICKS_GIGE; 46595718Sphk sc->mii_flags |= MIIF_IS_1000X; 46695718Sphk ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, 0, 467281821Sglebius sc->mii_inst)); 46895718Sphk PRINT("1000baseSX"); 46995718Sphk } 470214606Smarius if ((sc->mii_extcapabilities & EXTSR_1000XFDX) != 0) { 471158649Soleg sc->mii_anegticks = MII_ANEGTICKS_GIGE; 47295718Sphk sc->mii_flags |= MIIF_IS_1000X; 47395718Sphk ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX, 474281821Sglebius sc->mii_inst)); 47595718Sphk PRINT("1000baseSX-FDX"); 476215297Smarius if ((sc->mii_flags & MIIF_DOPAUSE) != 0 && 477215297Smarius (sc->mii_flags & MIIF_NOMANPAUSE) == 0) { 478215297Smarius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, 479281821Sglebius IFM_FDX | IFM_FLOW, sc->mii_inst)); 480215297Smarius PRINT("1000baseSX-FDX-flow"); 481215297Smarius } 482215297Smarius fdx = 1; 48395718Sphk } 48495718Sphk 48595718Sphk /* 48695718Sphk * 1000baseT media needs to be able to manipulate 487215297Smarius * master/slave mode. 48895718Sphk * 48995718Sphk * All 1000baseT PHYs have a 1000baseT control register. 49095718Sphk */ 491214606Smarius if ((sc->mii_extcapabilities & EXTSR_1000THDX) != 0) { 492158649Soleg sc->mii_anegticks = MII_ANEGTICKS_GIGE; 49395718Sphk sc->mii_flags |= MIIF_HAVE_GTCR; 49495718Sphk ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0, 495281821Sglebius sc->mii_inst)); 49695718Sphk PRINT("1000baseT"); 497215297Smarius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 498281821Sglebius IFM_ETH_MASTER, sc->mii_inst)); 499215297Smarius PRINT("1000baseT-master"); 50095718Sphk } 501214606Smarius if ((sc->mii_extcapabilities & EXTSR_1000TFDX) != 0) { 502158649Soleg sc->mii_anegticks = MII_ANEGTICKS_GIGE; 50395718Sphk sc->mii_flags |= MIIF_HAVE_GTCR; 50495718Sphk ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX, 505281821Sglebius sc->mii_inst)); 50695718Sphk PRINT("1000baseT-FDX"); 507215297Smarius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 508281821Sglebius IFM_FDX | IFM_ETH_MASTER, sc->mii_inst)); 509215297Smarius PRINT("1000baseT-FDX-master"); 510215297Smarius if ((sc->mii_flags & MIIF_DOPAUSE) != 0 && 511215297Smarius (sc->mii_flags & MIIF_NOMANPAUSE) == 0) { 512215297Smarius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 513281821Sglebius IFM_FDX | IFM_FLOW, sc->mii_inst)); 514215297Smarius PRINT("1000baseT-FDX-flow"); 515215297Smarius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 516215297Smarius IFM_FDX | IFM_FLOW | IFM_ETH_MASTER, 517281821Sglebius sc->mii_inst)); 518215297Smarius PRINT("1000baseT-FDX-flow-master"); 519215297Smarius } 520215297Smarius fdx = 1; 52195718Sphk } 52295718Sphk } 52395718Sphk 524214606Smarius if ((sc->mii_capabilities & BMSR_ANEG) != 0) { 525215297Smarius /* intentionally invalid index */ 526281821Sglebius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst)); 52795718Sphk PRINT("auto"); 528215297Smarius if (fdx != 0 && (sc->mii_flags & MIIF_DOPAUSE) != 0) { 529215297Smarius ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, IFM_FLOW, 530281821Sglebius sc->mii_inst)); 531215297Smarius PRINT("auto-flow"); 532215297Smarius } 53395718Sphk } 53495718Sphk#undef ADD 53595718Sphk#undef PRINT 53695718Sphk} 53795718Sphk 53895722Sphkint 53995722Sphkmii_phy_detach(device_t dev) 54095722Sphk{ 54195722Sphk struct mii_softc *sc; 54295722Sphk 54395722Sphk sc = device_get_softc(dev); 54495722Sphk sc->mii_dev = NULL; 54595722Sphk LIST_REMOVE(sc, mii_list); 546214606Smarius return (0); 54795722Sphk} 54895724Sphk 54995724Sphkconst struct mii_phydesc * 550150756Simpmii_phy_match_gen(const struct mii_attach_args *ma, 551150756Simp const struct mii_phydesc *mpd, size_t len) 55295724Sphk{ 55395724Sphk 554150756Simp for (; mpd->mpd_name != NULL; 555215297Smarius mpd = (const struct mii_phydesc *)((const char *)mpd + len)) { 55695724Sphk if (MII_OUI(ma->mii_id1, ma->mii_id2) == mpd->mpd_oui && 55795724Sphk MII_MODEL(ma->mii_id2) == mpd->mpd_model) 55895724Sphk return (mpd); 55995724Sphk } 56095724Sphk return (NULL); 56195724Sphk} 562150756Simp 563150756Simpconst struct mii_phydesc * 564150756Simpmii_phy_match(const struct mii_attach_args *ma, const struct mii_phydesc *mpd) 565150756Simp{ 566164702Smarius 567150756Simp return (mii_phy_match_gen(ma, mpd, sizeof(struct mii_phydesc))); 568150756Simp} 569164827Smarius 570164827Smariusint 571164827Smariusmii_phy_dev_probe(device_t dev, const struct mii_phydesc *mpd, int mrv) 572164827Smarius{ 573164827Smarius 574164827Smarius mpd = mii_phy_match(device_get_ivars(dev), mpd); 575164827Smarius if (mpd != NULL) { 576164827Smarius device_set_desc(dev, mpd->mpd_name); 577164827Smarius return (mrv); 578164827Smarius } 579164827Smarius return (ENXIO); 580164827Smarius} 581215297Smarius 582221407Smariusvoid 583221407Smariusmii_phy_dev_attach(device_t dev, u_int flags, const struct mii_phy_funcs *mpf, 584221407Smarius int add_media) 585221407Smarius{ 586221407Smarius struct mii_softc *sc; 587221407Smarius struct mii_attach_args *ma; 588221407Smarius struct mii_data *mii; 589221407Smarius 590221407Smarius sc = device_get_softc(dev); 591221407Smarius ma = device_get_ivars(dev); 592221407Smarius sc->mii_dev = device_get_parent(dev); 593221407Smarius mii = ma->mii_data; 594221407Smarius LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 595221407Smarius 596221407Smarius sc->mii_flags = flags | miibus_get_flags(dev); 597221407Smarius sc->mii_mpd_oui = MII_OUI(ma->mii_id1, ma->mii_id2); 598221407Smarius sc->mii_mpd_model = MII_MODEL(ma->mii_id2); 599221407Smarius sc->mii_mpd_rev = MII_REV(ma->mii_id2); 600221407Smarius sc->mii_capmask = ma->mii_capmask; 601221407Smarius sc->mii_inst = mii->mii_instance++; 602221407Smarius sc->mii_phy = ma->mii_phyno; 603221407Smarius sc->mii_offset = ma->mii_offset; 604221407Smarius sc->mii_funcs = mpf; 605221407Smarius sc->mii_pdata = mii; 606221407Smarius 607221407Smarius if (bootverbose) 608221407Smarius device_printf(dev, "OUI 0x%06x, model 0x%04x, rev. %d\n", 609221407Smarius sc->mii_mpd_oui, sc->mii_mpd_model, sc->mii_mpd_rev); 610221407Smarius 611221407Smarius if (add_media == 0) 612221407Smarius return; 613221407Smarius 614221407Smarius PHY_RESET(sc); 615221407Smarius 616221407Smarius sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & sc->mii_capmask; 617221407Smarius if (sc->mii_capabilities & BMSR_EXTSTAT) 618221407Smarius sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); 619221407Smarius device_printf(dev, " "); 620221407Smarius mii_phy_add_media(sc); 621221407Smarius printf("\n"); 622221407Smarius 623221407Smarius MIIBUS_MEDIAINIT(sc->mii_dev); 624221407Smarius} 625221407Smarius 626215297Smarius/* 627215297Smarius * Return the flow control status flag from MII_ANAR & MII_ANLPAR. 628215297Smarius */ 629215297Smariusu_int 630215297Smariusmii_phy_flowstatus(struct mii_softc *sc) 631215297Smarius{ 632215297Smarius int anar, anlpar; 633215297Smarius 634215297Smarius if ((sc->mii_flags & MIIF_DOPAUSE) == 0) 635215297Smarius return (0); 636215297Smarius 637215297Smarius anar = PHY_READ(sc, MII_ANAR); 638215297Smarius anlpar = PHY_READ(sc, MII_ANLPAR); 639215297Smarius 640215297Smarius /* 641215297Smarius * Check for 1000BASE-X. Autonegotiation is a bit 642215297Smarius * different on such devices. 643215297Smarius */ 644215297Smarius if ((sc->mii_flags & MIIF_IS_1000X) != 0) { 645215297Smarius anar <<= 3; 646215297Smarius anlpar <<= 3; 647215297Smarius } 648215297Smarius 649215297Smarius if ((anar & ANAR_PAUSE_SYM) != 0 && (anlpar & ANLPAR_PAUSE_SYM) != 0) 650215297Smarius return (IFM_FLOW | IFM_ETH_TXPAUSE | IFM_ETH_RXPAUSE); 651215297Smarius 652215297Smarius if ((anar & ANAR_PAUSE_SYM) == 0) { 653215297Smarius if ((anar & ANAR_PAUSE_ASYM) != 0 && 654215297Smarius (anlpar & ANLPAR_PAUSE_TOWARDS) != 0) 655215297Smarius return (IFM_FLOW | IFM_ETH_TXPAUSE); 656215297Smarius else 657215297Smarius return (0); 658215297Smarius } 659215297Smarius 660215297Smarius if ((anar & ANAR_PAUSE_ASYM) == 0) { 661215297Smarius if ((anlpar & ANLPAR_PAUSE_SYM) != 0) 662215297Smarius return (IFM_FLOW | IFM_ETH_TXPAUSE | IFM_ETH_RXPAUSE); 663215297Smarius else 664215297Smarius return (0); 665215297Smarius } 666215297Smarius 667215297Smarius switch ((anlpar & ANLPAR_PAUSE_TOWARDS)) { 668215297Smarius case ANLPAR_PAUSE_NONE: 669215297Smarius return (0); 670215297Smarius case ANLPAR_PAUSE_ASYM: 671215297Smarius return (IFM_FLOW | IFM_ETH_RXPAUSE); 672215297Smarius default: 673215297Smarius return (IFM_FLOW | IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE); 674215297Smarius } 675215297Smarius /* NOTREACHED */ 676215297Smarius} 677