atphy.c revision 221817
1238106Sdes/*- 2238106Sdes * Copyright (c) 2008, Pyun YongHyeon <yongari@FreeBSD.org> 3238106Sdes * All rights reserved. 4238106Sdes * 5238106Sdes * Redistribution and use in source and binary forms, with or without 6238106Sdes * modification, are permitted provided that the following conditions 7238106Sdes * are met: 8238106Sdes * 1. Redistributions of source code must retain the above copyright 9238106Sdes * notice unmodified, this list of conditions, and the following 10238106Sdes * disclaimer. 11238106Sdes * 2. Redistributions in binary form must reproduce the above copyright 12238106Sdes * notice, this list of conditions and the following disclaimer in the 13238106Sdes * documentation and/or other materials provided with the distribution. 14238106Sdes * 15238106Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16238106Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17238106Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18238106Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19238106Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20238106Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21238106Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22238106Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23238106Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24269257Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25269257Sdes * SUCH DAMAGE. 26269257Sdes */ 27269257Sdes 28269257Sdes#include <sys/cdefs.h> 29269257Sdes__FBSDID("$FreeBSD: head/sys/dev/mii/atphy.c 221817 2011-05-12 17:11:31Z yongari $"); 30269257Sdes 31269257Sdes/* 32269257Sdes * Driver for the Attansic/Atheros F1 10/100/1000 PHY. 33269257Sdes */ 34238106Sdes 35238106Sdes#include <sys/param.h> 36238106Sdes#include <sys/systm.h> 37238106Sdes#include <sys/kernel.h> 38238106Sdes#include <sys/module.h> 39238106Sdes#include <sys/socket.h> 40238106Sdes#include <sys/bus.h> 41238106Sdes 42238106Sdes#include <net/if.h> 43238106Sdes#include <net/if_media.h> 44238106Sdes 45238106Sdes#include <dev/mii/mii.h> 46238106Sdes#include <dev/mii/miivar.h> 47238106Sdes#include "miidevs.h" 48238106Sdes 49238106Sdes#include <dev/mii/atphyreg.h> 50238106Sdes 51238106Sdes#include "miibus_if.h" 52238106Sdes 53238106Sdesstatic int atphy_probe(device_t); 54291767Sdesstatic int atphy_attach(device_t); 55238106Sdes 56275853Sdelphijstatic device_method_t atphy_methods[] = { 57291767Sdes /* Device interface. */ 58238106Sdes DEVMETHOD(device_probe, atphy_probe), 59238106Sdes DEVMETHOD(device_attach, atphy_attach), 60238106Sdes DEVMETHOD(device_detach, mii_phy_detach), 61238106Sdes DEVMETHOD(device_shutdown, bus_generic_shutdown), 62238106Sdes { NULL, NULL } 63285206Sdes}; 64238106Sdes 65238106Sdesstatic devclass_t atphy_devclass; 66238106Sdesstatic driver_t atphy_driver = { 67238106Sdes "atphy", 68238106Sdes atphy_methods, 69238106Sdes sizeof(struct mii_softc) 70238106Sdes}; 71238106Sdes 72238106SdesDRIVER_MODULE(atphy, miibus, atphy_driver, atphy_devclass, 0, 0); 73238106Sdes 74238106Sdesstatic int atphy_service(struct mii_softc *, struct mii_data *, int); 75238106Sdesstatic void atphy_status(struct mii_softc *); 76238106Sdesstatic void atphy_reset(struct mii_softc *); 77238106Sdesstatic uint16_t atphy_anar(struct ifmedia_entry *); 78238106Sdesstatic int atphy_setmedia(struct mii_softc *, int); 79238106Sdes 80238106Sdesstatic const struct mii_phydesc atphys[] = { 81238106Sdes MII_PHY_DESC(xxATHEROS, F1), 82238106Sdes MII_PHY_DESC(xxATHEROS, F1_7), 83238106Sdes MII_PHY_DESC(xxATHEROS, F2), 84238106Sdes MII_PHY_END 85238106Sdes}; 86238106Sdes 87238106Sdesstatic const struct mii_phy_funcs atphy_funcs = { 88238106Sdes atphy_service, 89238106Sdes atphy_status, 90238106Sdes atphy_reset 91238106Sdes}; 92238106Sdes 93238106Sdesstatic int 94238106Sdesatphy_probe(device_t dev) 95238106Sdes{ 96238106Sdes 97238106Sdes return (mii_phy_dev_probe(dev, atphys, BUS_PROBE_DEFAULT)); 98238106Sdes} 99238106Sdes 100291767Sdesstatic int 101291767Sdesatphy_attach(device_t dev) 102291767Sdes{ 103238106Sdes 104238106Sdes mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &atphy_funcs, 1); 105238106Sdes return (0); 106238106Sdes} 107238106Sdes 108238106Sdesstatic int 109238106Sdesatphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 110238106Sdes{ 111238106Sdes struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 112238106Sdes uint16_t anar, bmcr, bmsr; 113238106Sdes 114238106Sdes switch (cmd) { 115294190Sdes case MII_POLLSTAT: 116294190Sdes break; 117294190Sdes 118238106Sdes case MII_MEDIACHG: 119238106Sdes /* 120238106Sdes * If the interface is not up, don't do anything. 121294190Sdes */ 122294190Sdes if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 123294190Sdes break; 124294190Sdes 125294190Sdes if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO || 126294190Sdes IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) { 127294190Sdes atphy_setmedia(sc, ife->ifm_media); 128294190Sdes break; 129294190Sdes } 130294190Sdes 131294190Sdes bmcr = 0; 132294190Sdes switch (IFM_SUBTYPE(ife->ifm_media)) { 133294190Sdes case IFM_100_TX: 134294190Sdes bmcr = BMCR_S100; 135294190Sdes break; 136294190Sdes case IFM_10_T: 137294190Sdes bmcr = BMCR_S10; 138294190Sdes break; 139294190Sdes case IFM_NONE: 140294190Sdes bmcr = PHY_READ(sc, MII_BMCR); 141294190Sdes /* 142294190Sdes * XXX 143294190Sdes * Due to an unknown reason powering down PHY resulted 144238106Sdes * in unexpected results such as inaccessibility of 145238106Sdes * hardware of freshly rebooted system. Disable 146238106Sdes * powering down PHY until I got more information for 147238106Sdes * Attansic/Atheros PHY hardwares. 148238106Sdes */ 149238106Sdes PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO); 150238106Sdes goto done; 151238106Sdes default: 152238106Sdes return (EINVAL); 153238106Sdes } 154238106Sdes 155238106Sdes anar = atphy_anar(ife); 156238106Sdes if ((ife->ifm_media & IFM_FDX) != 0) { 157238106Sdes bmcr |= BMCR_FDX; 158238106Sdes if ((ife->ifm_media & IFM_FLOW) != 0 || 159238106Sdes (sc->mii_flags & MIIF_FORCEPAUSE) != 0) 160238106Sdes anar |= ANAR_PAUSE_TOWARDS; 161238106Sdes } 162238106Sdes 163238106Sdes if ((sc->mii_extcapabilities & (EXTSR_1000TFDX | 164238106Sdes EXTSR_1000THDX)) != 0) 165238106Sdes PHY_WRITE(sc, MII_100T2CR, 0); 166238106Sdes PHY_WRITE(sc, MII_ANAR, anar | ANAR_CSMA); 167238106Sdes 168238106Sdes /* 169238106Sdes * Reset the PHY so all changes take effect. 170238106Sdes */ 171238106Sdes PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_RESET | BMCR_AUTOEN | 172238106Sdes BMCR_STARTNEG); 173238106Sdesdone: 174238106Sdes break; 175238106Sdes 176238106Sdes case MII_TICK: 177238106Sdes /* 178238106Sdes * Is the interface even up? 179238106Sdes */ 180238106Sdes if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 181238106Sdes return (0); 182238106Sdes 183238106Sdes /* 184238106Sdes * Only used for autonegotiation. 185238106Sdes */ 186238106Sdes if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 187238106Sdes sc->mii_ticks = 0; 188238106Sdes break; 189238106Sdes } 190238106Sdes 191238106Sdes /* 192238106Sdes * Check for link. 193238106Sdes * Read the status register twice; BMSR_LINK is latch-low. 194238106Sdes */ 195238106Sdes bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 196238106Sdes if (bmsr & BMSR_LINK) { 197238106Sdes sc->mii_ticks = 0; 198238106Sdes break; 199238106Sdes } 200238106Sdes 201238106Sdes /* Announce link loss right after it happens. */ 202238106Sdes if (sc->mii_ticks++ == 0) 203238106Sdes break; 204238106Sdes if (sc->mii_ticks <= sc->mii_anegticks) 205238106Sdes return (0); 206238106Sdes 207238106Sdes sc->mii_ticks = 0; 208238106Sdes atphy_setmedia(sc, ife->ifm_media); 209238106Sdes break; 210238106Sdes } 211238106Sdes 212238106Sdes /* Update the media status. */ 213238106Sdes PHY_STATUS(sc); 214238106Sdes 215238106Sdes /* Callback if something changed. */ 216238106Sdes mii_phy_update(sc, cmd); 217238106Sdes return (0); 218238106Sdes} 219238106Sdes 220238106Sdesstatic void 221238106Sdesatphy_status(struct mii_softc *sc) 222238106Sdes{ 223238106Sdes struct mii_data *mii = sc->mii_pdata; 224238106Sdes uint32_t bmsr, bmcr, ssr; 225238106Sdes 226238106Sdes mii->mii_media_status = IFM_AVALID; 227238106Sdes mii->mii_media_active = IFM_ETHER; 228238106Sdes 229238106Sdes bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 230238106Sdes if ((bmsr & BMSR_LINK) != 0) 231238106Sdes mii->mii_media_status |= IFM_ACTIVE; 232238106Sdes 233238106Sdes bmcr = PHY_READ(sc, MII_BMCR); 234238106Sdes if ((bmcr & BMCR_ISO) != 0) { 235238106Sdes mii->mii_media_active |= IFM_NONE; 236238106Sdes mii->mii_media_status = 0; 237238106Sdes return; 238238106Sdes } 239238106Sdes 240238106Sdes if ((bmcr & BMCR_LOOP) != 0) 241238106Sdes mii->mii_media_active |= IFM_LOOP; 242238106Sdes 243238106Sdes ssr = PHY_READ(sc, ATPHY_SSR); 244238106Sdes if ((ssr & ATPHY_SSR_SPD_DPLX_RESOLVED) == 0) { 245238106Sdes /* Erg, still trying, I guess... */ 246238106Sdes mii->mii_media_active |= IFM_NONE; 247238106Sdes return; 248238106Sdes } 249238106Sdes 250238106Sdes switch (ssr & ATPHY_SSR_SPEED_MASK) { 251238106Sdes case ATPHY_SSR_1000MBS: 252238106Sdes mii->mii_media_active |= IFM_1000_T; 253238106Sdes /* 254238106Sdes * atphy(4) has a valid link so reset mii_ticks. 255238106Sdes * Resetting mii_ticks is needed in order to 256238106Sdes * detect link loss after auto-negotiation. 257238106Sdes */ 258238106Sdes sc->mii_ticks = 0; 259238106Sdes break; 260238106Sdes case ATPHY_SSR_100MBS: 261238106Sdes mii->mii_media_active |= IFM_100_TX; 262238106Sdes sc->mii_ticks = 0; 263238106Sdes break; 264238106Sdes case ATPHY_SSR_10MBS: 265285206Sdes mii->mii_media_active |= IFM_10_T; 266285206Sdes sc->mii_ticks = 0; 267238106Sdes break; 268291767Sdes default: 269238106Sdes mii->mii_media_active |= IFM_NONE; 270238106Sdes return; 271238106Sdes } 272238106Sdes 273238106Sdes if ((ssr & ATPHY_SSR_DUPLEX) != 0) 274238106Sdes mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc); 275238106Sdes else 276238106Sdes mii->mii_media_active |= IFM_HDX; 277238106Sdes 278238106Sdes if ((IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) && 279238106Sdes (PHY_READ(sc, MII_100T2SR) & GTSR_MS_RES) != 0) 280238106Sdes mii->mii_media_active |= IFM_ETH_MASTER; 281238106Sdes} 282238106Sdes 283238106Sdesstatic void 284238106Sdesatphy_reset(struct mii_softc *sc) 285238106Sdes{ 286238106Sdes struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur; 287275853Sdelphij uint32_t reg; 288275853Sdelphij int i; 289275853Sdelphij 290275853Sdelphij /* Take PHY out of power down mode. */ 291238106Sdes PHY_WRITE(sc, 29, 0x29); 292291767Sdes PHY_WRITE(sc, 30, 0); 293291767Sdes 294291767Sdes reg = PHY_READ(sc, ATPHY_SCR); 295238106Sdes /* Enable automatic crossover. */ 296238106Sdes reg |= ATPHY_SCR_AUTO_X_MODE; 297238106Sdes /* Disable power down. */ 298238106Sdes reg &= ~ATPHY_SCR_MAC_PDOWN; 299238106Sdes /* Enable CRS on Tx. */ 300238106Sdes reg |= ATPHY_SCR_ASSERT_CRS_ON_TX; 301238106Sdes /* Auto correction for reversed cable polarity. */ 302238106Sdes reg |= ATPHY_SCR_POLARITY_REVERSAL; 303238106Sdes PHY_WRITE(sc, ATPHY_SCR, reg); 304238106Sdes 305238106Sdes /* Workaround F1 bug to reset phy. */ 306238106Sdes atphy_setmedia(sc, ife == NULL ? IFM_AUTO : ife->ifm_media); 307238106Sdes 308238106Sdes for (i = 0; i < 1000; i++) { 309238106Sdes DELAY(1); 310238106Sdes if ((PHY_READ(sc, MII_BMCR) & BMCR_RESET) == 0) 311238106Sdes break; 312238106Sdes } 313238106Sdes} 314238106Sdes 315238106Sdesstatic uint16_t 316238106Sdesatphy_anar(struct ifmedia_entry *ife) 317238106Sdes{ 318238106Sdes uint16_t anar; 319238106Sdes 320238106Sdes anar = 0; 321238106Sdes switch (IFM_SUBTYPE(ife->ifm_media)) { 322238106Sdes case IFM_AUTO: 323238106Sdes anar |= ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10; 324238106Sdes return (anar); 325238106Sdes case IFM_1000_T: 326238106Sdes return (anar); 327238106Sdes case IFM_100_TX: 328238106Sdes anar |= ANAR_TX; 329238106Sdes break; 330238106Sdes case IFM_10_T: 331238106Sdes anar |= ANAR_10; 332238106Sdes break; 333238106Sdes default: 334238106Sdes return (0); 335238106Sdes } 336238106Sdes 337238106Sdes if ((ife->ifm_media & IFM_FDX) != 0) { 338238106Sdes if (IFM_SUBTYPE(ife->ifm_media) == IFM_100_TX) 339238106Sdes anar |= ANAR_TX_FD; 340238106Sdes else 341238106Sdes anar |= ANAR_10_FD; 342238106Sdes } 343238106Sdes 344238106Sdes return (anar); 345238106Sdes} 346238106Sdes 347238106Sdesstatic int 348238106Sdesatphy_setmedia(struct mii_softc *sc, int media) 349238106Sdes{ 350238106Sdes uint16_t anar; 351294190Sdes 352294190Sdes anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA; 353294190Sdes if ((IFM_SUBTYPE(media) == IFM_AUTO || (media & IFM_FDX) != 0) && 354294190Sdes ((media & IFM_FLOW) != 0 || 355294190Sdes (sc->mii_flags & MIIF_FORCEPAUSE) != 0)) 356294190Sdes anar |= ANAR_PAUSE_TOWARDS; 357294190Sdes PHY_WRITE(sc, MII_ANAR, anar); 358294190Sdes if ((sc->mii_extcapabilities & 359294190Sdes (EXTSR_1000TFDX | EXTSR_1000THDX)) != 0) 360238106Sdes PHY_WRITE(sc, MII_100T2CR, GTCR_ADV_1000TFDX | 361238106Sdes GTCR_ADV_1000THDX); 362238106Sdes else if (sc->mii_mpd_model == MII_MODEL_xxATHEROS_F1) { 363238106Sdes /* 364238106Sdes * AR8132 has 10/100 PHY and the PHY uses the same 365238106Sdes * model number of F1 gigabit PHY. The PHY has no 366238106Sdes * ability to establish gigabit link so explicitly 367238106Sdes * disable 1000baseT configuration for the PHY. 368238106Sdes * Otherwise, there is a case that atphy(4) could 369238106Sdes * not establish a link against gigabit link partner 370238106Sdes * unless the link partner supports down-shifting. 371238106Sdes */ 372238106Sdes PHY_WRITE(sc, MII_100T2CR, 0); 373238106Sdes } 374238106Sdes PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN | BMCR_STARTNEG); 375238106Sdes 376238106Sdes return (EJUSTRETURN); 377238106Sdes} 378238106Sdes