ciphy.c revision 184192
168349Sobrien/*- 268349Sobrien * Copyright (c) 2004 368349Sobrien * Bill Paul <wpaul@windriver.com>. All rights reserved. 468349Sobrien * 568349Sobrien * Redistribution and use in source and binary forms, with or without 668349Sobrien * modification, are permitted provided that the following conditions 768349Sobrien * are met: 868349Sobrien * 1. Redistributions of source code must retain the above copyright 968349Sobrien * notice, this list of conditions and the following disclaimer. 1068349Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1168349Sobrien * notice, this list of conditions and the following disclaimer in the 1268349Sobrien * documentation and/or other materials provided with the distribution. 1368349Sobrien * 3. All advertising materials mentioning features or use of this software 1468349Sobrien * must display the following acknowledgement: 1568349Sobrien * This product includes software developed by Bill Paul. 1668349Sobrien * 4. Neither the name of the author nor the names of any co-contributors 1768349Sobrien * may be used to endorse or promote products derived from this software 1868349Sobrien * without specific prior written permission. 1968349Sobrien * 20133359Sobrien * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 21133359Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2268349Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2368349Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 24133359Sobrien * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25133359Sobrien * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2668349Sobrien * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2768349Sobrien * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28133359Sobrien * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29133359Sobrien * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 3068349Sobrien * THE POSSIBILITY OF SUCH DAMAGE. 3168349Sobrien */ 3268349Sobrien 3369216Sobrien#include <sys/cdefs.h> 3469216Sobrien__FBSDID("$FreeBSD: head/sys/dev/mii/ciphy.c 184192 2008-10-23 01:27:15Z yongari $"); 3569216Sobrien 3669216Sobrien/* 3769216Sobrien * Driver for the Cicada/Vitesse CS/VSC8xxx 10/100/1000 copper PHY. 3869216Sobrien */ 3969216Sobrien 4068349Sobrien#include <sys/param.h> 4169216Sobrien#include <sys/systm.h> 4269216Sobrien#include <sys/kernel.h> 4369216Sobrien#include <sys/module.h> 4469216Sobrien#include <sys/socket.h> 4569216Sobrien#include <sys/bus.h> 4669216Sobrien 4769216Sobrien#include <net/if.h> 4869216Sobrien#include <net/if_arp.h> 4969216Sobrien#include <net/if_media.h> 5069216Sobrien 5169216Sobrien#include <dev/mii/mii.h> 5268349Sobrien#include <dev/mii/miivar.h> 5368349Sobrien#include "miidevs.h" 5468349Sobrien 5568349Sobrien#include <dev/mii/ciphyreg.h> 5668349Sobrien 5768349Sobrien#include "miibus_if.h" 5868349Sobrien 5968349Sobrien#include <machine/bus.h> 6068349Sobrien/* 6168349Sobrien#include <dev/vge/if_vgereg.h> 6268349Sobrien*/ 6368349Sobrienstatic int ciphy_probe(device_t); 6468349Sobrienstatic int ciphy_attach(device_t); 6568349Sobrien 6668349Sobrienstatic device_method_t ciphy_methods[] = { 6768349Sobrien /* device interface */ 6868349Sobrien DEVMETHOD(device_probe, ciphy_probe), 6968349Sobrien DEVMETHOD(device_attach, ciphy_attach), 7068349Sobrien DEVMETHOD(device_detach, mii_phy_detach), 7168349Sobrien DEVMETHOD(device_shutdown, bus_generic_shutdown), 7268349Sobrien { 0, 0 } 7368349Sobrien}; 7468349Sobrien 7568349Sobrienstatic devclass_t ciphy_devclass; 7668349Sobrien 7768349Sobrienstatic driver_t ciphy_driver = { 7868349Sobrien "ciphy", 7968349Sobrien ciphy_methods, 8068349Sobrien sizeof(struct mii_softc) 8168349Sobrien}; 8268349Sobrien 8368349SobrienDRIVER_MODULE(ciphy, miibus, ciphy_driver, ciphy_devclass, 0, 0); 8468349Sobrien 8568349Sobrienstatic int ciphy_service(struct mii_softc *, struct mii_data *, int); 8668349Sobrienstatic void ciphy_status(struct mii_softc *); 8768349Sobrienstatic void ciphy_reset(struct mii_softc *); 88103373Sobrienstatic void ciphy_fixup(struct mii_softc *); 8968349Sobrien 9068349Sobrienstatic const struct mii_phydesc ciphys[] = { 9168349Sobrien MII_PHY_DESC(CICADA, CS8201), 9268349Sobrien MII_PHY_DESC(CICADA, CS8201A), 9368349Sobrien MII_PHY_DESC(CICADA, CS8201B), 9468349Sobrien MII_PHY_DESC(CICADA, CS8204), 9568349Sobrien MII_PHY_DESC(CICADA, VSC8211), 9668349Sobrien MII_PHY_DESC(CICADA, CS8244), 9768349Sobrien MII_PHY_DESC(VITESSE, VSC8601), 9868349Sobrien MII_PHY_END 9968349Sobrien}; 10068349Sobrien 10168349Sobrienstatic int 10268349Sobrienciphy_probe(device_t dev) 10368349Sobrien{ 10468349Sobrien 10568349Sobrien return (mii_phy_dev_probe(dev, ciphys, BUS_PROBE_DEFAULT)); 10668349Sobrien} 10768349Sobrien 10868349Sobrienstatic int 10968349Sobrienciphy_attach(device_t dev) 11068349Sobrien{ 11168349Sobrien struct mii_softc *sc; 11268349Sobrien struct mii_attach_args *ma; 11368349Sobrien struct mii_data *mii; 11468349Sobrien 11568349Sobrien sc = device_get_softc(dev); 11668349Sobrien ma = device_get_ivars(dev); 11768349Sobrien sc->mii_dev = device_get_parent(dev); 11868349Sobrien mii = device_get_softc(sc->mii_dev); 11968349Sobrien LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 12068349Sobrien 12168349Sobrien sc->mii_inst = mii->mii_instance; 12268349Sobrien sc->mii_phy = ma->mii_phyno; 12368349Sobrien sc->mii_service = ciphy_service; 12468349Sobrien sc->mii_pdata = mii; 12568349Sobrien 12668349Sobrien sc->mii_flags |= MIIF_NOISOLATE; 12768349Sobrien mii->mii_instance++; 12868349Sobrien 12968349Sobrien ciphy_reset(sc); 13068349Sobrien 13168349Sobrien sc->mii_capabilities = 13268349Sobrien PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 13368349Sobrien if (sc->mii_capabilities & BMSR_EXTSTAT) 13468349Sobrien sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); 13568349Sobrien device_printf(dev, " "); 13668349Sobrien mii_phy_add_media(sc); 13768349Sobrien printf("\n"); 13868349Sobrien 13968349Sobrien MIIBUS_MEDIAINIT(sc->mii_dev); 14068349Sobrien return (0); 14168349Sobrien} 14268349Sobrien 14368349Sobrienstatic int 14468349Sobrienciphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 14568349Sobrien{ 14668349Sobrien struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 14768349Sobrien int reg, speed, gig; 14868349Sobrien 14968349Sobrien switch (cmd) { 15068349Sobrien case MII_POLLSTAT: 151110949Sobrien /* 152110949Sobrien * If we're not polling our PHY instance, just return. 15368349Sobrien */ 154110949Sobrien if (IFM_INST(ife->ifm_media) != sc->mii_inst) 15568349Sobrien return (0); 15668349Sobrien break; 15768349Sobrien 15868349Sobrien case MII_MEDIACHG: 15968349Sobrien /* 16068349Sobrien * If the media indicates a different PHY instance, 16168349Sobrien * isolate ourselves. 16268349Sobrien */ 16368349Sobrien if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 16468349Sobrien reg = PHY_READ(sc, MII_BMCR); 16568349Sobrien PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO); 16668349Sobrien return (0); 167133359Sobrien } 168133359Sobrien 169133359Sobrien /* 170133359Sobrien * If the interface is not up, don't do anything. 171133359Sobrien */ 17268349Sobrien if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 17368349Sobrien break; 17468349Sobrien 17568349Sobrien ciphy_fixup(sc); /* XXX hardware bug work-around */ 17668349Sobrien 17768349Sobrien switch (IFM_SUBTYPE(ife->ifm_media)) { 17868349Sobrien case IFM_AUTO: 17968349Sobrien#ifdef foo 18068349Sobrien /* 18168349Sobrien * If we're already in auto mode, just return. 18268349Sobrien */ 18368349Sobrien if (PHY_READ(sc, CIPHY_MII_BMCR) & CIPHY_BMCR_AUTOEN) 18468349Sobrien return (0); 18568349Sobrien#endif 18668349Sobrien (void) mii_phy_auto(sc); 18768349Sobrien break; 18868349Sobrien case IFM_1000_T: 18968349Sobrien speed = CIPHY_S1000; 19068349Sobrien goto setit; 19168349Sobrien case IFM_100_TX: 19268349Sobrien speed = CIPHY_S100; 19368349Sobrien goto setit; 19468349Sobrien case IFM_10_T: 19568349Sobrien speed = CIPHY_S10; 19668349Sobriensetit: 19768349Sobrien if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) { 19868349Sobrien speed |= CIPHY_BMCR_FDX; 19968349Sobrien gig = CIPHY_1000CTL_AFD; 20068349Sobrien } else { 20168349Sobrien gig = CIPHY_1000CTL_AHD; 20268349Sobrien } 20368349Sobrien 20468349Sobrien PHY_WRITE(sc, CIPHY_MII_1000CTL, 0); 20568349Sobrien PHY_WRITE(sc, CIPHY_MII_BMCR, speed); 20668349Sobrien PHY_WRITE(sc, CIPHY_MII_ANAR, CIPHY_SEL_TYPE); 20768349Sobrien 20868349Sobrien if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T) 20968349Sobrien break; 21068349Sobrien 21168349Sobrien PHY_WRITE(sc, CIPHY_MII_1000CTL, gig); 21268349Sobrien PHY_WRITE(sc, CIPHY_MII_BMCR, 21368349Sobrien speed|CIPHY_BMCR_AUTOEN|CIPHY_BMCR_STARTNEG); 21468349Sobrien 21568349Sobrien /* 21668349Sobrien * When setting the link manually, one side must 21768349Sobrien * be the master and the other the slave. However 21868349Sobrien * ifmedia doesn't give us a good way to specify 21968349Sobrien * this, so we fake it by using one of the LINK 22068349Sobrien * flags. If LINK0 is set, we program the PHY to 22168349Sobrien * be a master, otherwise it's a slave. 22268349Sobrien */ 22368349Sobrien if ((mii->mii_ifp->if_flags & IFF_LINK0)) { 22468349Sobrien PHY_WRITE(sc, CIPHY_MII_1000CTL, 22568349Sobrien gig|CIPHY_1000CTL_MSE|CIPHY_1000CTL_MSC); 22668349Sobrien } else { 22768349Sobrien PHY_WRITE(sc, CIPHY_MII_1000CTL, 22868349Sobrien gig|CIPHY_1000CTL_MSE); 22968349Sobrien } 23068349Sobrien break; 23168349Sobrien case IFM_NONE: 23268349Sobrien PHY_WRITE(sc, MII_BMCR, BMCR_ISO|BMCR_PDOWN); 23368349Sobrien break; 23468349Sobrien case IFM_100_T4: 23568349Sobrien default: 23668349Sobrien return (EINVAL); 23768349Sobrien } 23868349Sobrien break; 23968349Sobrien 24068349Sobrien case MII_TICK: 24168349Sobrien /* 24268349Sobrien * If we're not currently selected, just return. 24368349Sobrien */ 24484685Sobrien if (IFM_INST(ife->ifm_media) != sc->mii_inst) 24584685Sobrien return (0); 24684685Sobrien 24784685Sobrien /* 24868349Sobrien * Is the interface even up? 24968349Sobrien */ 25068349Sobrien if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 25168349Sobrien return (0); 25268349Sobrien 25368349Sobrien /* 25468349Sobrien * Only used for autonegotiation. 25568349Sobrien */ 25668349Sobrien if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 25768349Sobrien break; 25868349Sobrien 25968349Sobrien /* 26068349Sobrien * Check to see if we have link. If we do, we don't 26168349Sobrien * need to restart the autonegotiation process. Read 26268349Sobrien * the BMSR twice in case it's latched. 263159764Sobrien */ 26468349Sobrien reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 26568349Sobrien if (reg & BMSR_LINK) 26668349Sobrien break; 26768349Sobrien 26868349Sobrien /* Announce link loss right after it happens. */ 26968349Sobrien if (++sc->mii_ticks == 0) 27068349Sobrien break; 27168349Sobrien /* 27274784Sobrien * Only retry autonegotiation every mii_anegticks seconds. 273133359Sobrien */ 27474784Sobrien if (sc->mii_ticks <= sc->mii_anegticks) 27574784Sobrien break; 27674784Sobrien 277133359Sobrien sc->mii_ticks = 0; 27874784Sobrien mii_phy_auto(sc); 27974784Sobrien break; 28074784Sobrien } 28174784Sobrien 28274784Sobrien /* Update the media status. */ 28374784Sobrien ciphy_status(sc); 28474784Sobrien 28574784Sobrien /* 28674784Sobrien * Callback if something changed. Note that we need to poke 28774784Sobrien * apply fixups for certain PHY revs. 28874784Sobrien */ 28974784Sobrien if (sc->mii_media_active != mii->mii_media_active || 29074784Sobrien sc->mii_media_status != mii->mii_media_status || 29174784Sobrien cmd == MII_MEDIACHG) { 29274784Sobrien ciphy_fixup(sc); 29374784Sobrien } 29474784Sobrien mii_phy_update(sc, cmd); 29574784Sobrien return (0); 29674784Sobrien} 29774784Sobrien 29874784Sobrienstatic void 29974784Sobrienciphy_status(struct mii_softc *sc) 30074784Sobrien{ 30174784Sobrien struct mii_data *mii = sc->mii_pdata; 30274784Sobrien int bmsr, bmcr; 30374784Sobrien 30474784Sobrien mii->mii_media_status = IFM_AVALID; 30574784Sobrien mii->mii_media_active = IFM_ETHER; 30674784Sobrien 30774784Sobrien bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 30874784Sobrien 30980588Sobrien if (bmsr & BMSR_LINK) 310169962Sobrien mii->mii_media_status |= IFM_ACTIVE; 311169962Sobrien 312169962Sobrien bmcr = PHY_READ(sc, CIPHY_MII_BMCR); 313169962Sobrien 31484685Sobrien if (bmcr & CIPHY_BMCR_LOOP) 31584685Sobrien mii->mii_media_active |= IFM_LOOP; 31684685Sobrien 31780588Sobrien if (bmcr & CIPHY_BMCR_AUTOEN) { 31884685Sobrien if ((bmsr & CIPHY_BMSR_ACOMP) == 0) { 31984685Sobrien /* Erg, still trying, I guess... */ 32084685Sobrien mii->mii_media_active |= IFM_NONE; 32184685Sobrien return; 32284685Sobrien } 32384685Sobrien } 32484685Sobrien 32584685Sobrien bmsr = PHY_READ(sc, CIPHY_MII_AUXCSR); 32684685Sobrien switch (bmsr & CIPHY_AUXCSR_SPEED) { 32784685Sobrien case CIPHY_SPEED10: 32884685Sobrien mii->mii_media_active |= IFM_10_T; 32984685Sobrien break; 33084685Sobrien case CIPHY_SPEED100: 33184685Sobrien mii->mii_media_active |= IFM_100_TX; 33284685Sobrien break; 33384685Sobrien case CIPHY_SPEED1000: 33484685Sobrien mii->mii_media_active |= IFM_1000_T; 33584685Sobrien break; 33684685Sobrien default: 33784685Sobrien device_printf(sc->mii_dev, "unknown PHY speed %x\n", 33884685Sobrien bmsr & CIPHY_AUXCSR_SPEED); 33984685Sobrien break; 34084685Sobrien } 34184685Sobrien 34284685Sobrien if (bmsr & CIPHY_AUXCSR_FDX) 34384685Sobrien mii->mii_media_active |= IFM_FDX; 344110949Sobrien else 345110949Sobrien mii->mii_media_active |= IFM_HDX; 346110949Sobrien} 347110949Sobrien 348110949Sobrienstatic void 349110949Sobrienciphy_reset(struct mii_softc *sc) 350110949Sobrien{ 351110949Sobrien 352110949Sobrien mii_phy_reset(sc); 353110949Sobrien DELAY(1000); 354110949Sobrien} 355110949Sobrien 356110949Sobrien#define PHY_SETBIT(x, y, z) \ 357110949Sobrien PHY_WRITE(x, y, (PHY_READ(x, y) | (z))) 358110949Sobrien#define PHY_CLRBIT(x, y, z) \ 359110949Sobrien PHY_WRITE(x, y, (PHY_READ(x, y) & ~(z))) 360110949Sobrien 361110949Sobrienstatic void 362110949Sobrienciphy_fixup(struct mii_softc *sc) 363110949Sobrien{ 364110949Sobrien uint16_t model; 365110949Sobrien uint16_t status, speed; 366110949Sobrien uint16_t val; 367133359Sobrien 368133359Sobrien model = MII_MODEL(PHY_READ(sc, CIPHY_MII_PHYIDR2)); 369133359Sobrien status = PHY_READ(sc, CIPHY_MII_AUXCSR); 370133359Sobrien speed = status & CIPHY_AUXCSR_SPEED; 371133359Sobrien 372133359Sobrien if (strcmp(device_get_name(device_get_parent(sc->mii_dev)), 373133359Sobrien "nfe") == 0) { 374133359Sobrien /* need to set for 2.5V RGMII for NVIDIA adapters */ 375133359Sobrien val = PHY_READ(sc, CIPHY_MII_ECTL1); 376133359Sobrien val &= ~(CIPHY_ECTL1_IOVOL | CIPHY_ECTL1_INTSEL); 377133359Sobrien val |= (CIPHY_IOVOL_2500MV | CIPHY_INTSEL_RGMII); 378133359Sobrien PHY_WRITE(sc, CIPHY_MII_ECTL1, val); 379133359Sobrien /* From Linux. */ 380133359Sobrien val = PHY_READ(sc, CIPHY_MII_AUXCSR); 381133359Sobrien val |= CIPHY_AUXCSR_MDPPS; 382133359Sobrien PHY_WRITE(sc, CIPHY_MII_AUXCSR, val); 383133359Sobrien val = PHY_READ(sc, CIPHY_MII_10BTCSR); 384133359Sobrien val |= CIPHY_10BTCSR_ECHO; 385133359Sobrien PHY_WRITE(sc, CIPHY_MII_10BTCSR, val); 386133359Sobrien } 387133359Sobrien 388133359Sobrien switch (model) { 389133359Sobrien case MII_MODEL_CICADA_CS8204: 390133359Sobrien case MII_MODEL_CICADA_CS8201: 391133359Sobrien 392133359Sobrien /* Turn off "aux mode" (whatever that means) */ 393133359Sobrien PHY_SETBIT(sc, CIPHY_MII_AUXCSR, CIPHY_AUXCSR_MDPPS); 394133359Sobrien 395133359Sobrien /* 396133359Sobrien * Work around speed polling bug in VT3119/VT3216 397133359Sobrien * when using MII in full duplex mode. 398133359Sobrien */ 399133359Sobrien if ((speed == CIPHY_SPEED10 || speed == CIPHY_SPEED100) && 400133359Sobrien (status & CIPHY_AUXCSR_FDX)) { 401133359Sobrien PHY_SETBIT(sc, CIPHY_MII_10BTCSR, CIPHY_10BTCSR_ECHO); 402133359Sobrien } else { 403133359Sobrien PHY_CLRBIT(sc, CIPHY_MII_10BTCSR, CIPHY_10BTCSR_ECHO); 404133359Sobrien } 405133359Sobrien 406133359Sobrien /* Enable link/activity LED blink. */ 407133359Sobrien PHY_SETBIT(sc, CIPHY_MII_LED, CIPHY_LED_LINKACTBLINK); 408133359Sobrien 409133359Sobrien break; 410133359Sobrien 411133359Sobrien case MII_MODEL_CICADA_CS8201A: 412133359Sobrien case MII_MODEL_CICADA_CS8201B: 413133359Sobrien 414133359Sobrien /* 415133359Sobrien * Work around speed polling bug in VT3119/VT3216 416133359Sobrien * when using MII in full duplex mode. 417133359Sobrien */ 418133359Sobrien if ((speed == CIPHY_SPEED10 || speed == CIPHY_SPEED100) && 419133359Sobrien (status & CIPHY_AUXCSR_FDX)) { 420133359Sobrien PHY_SETBIT(sc, CIPHY_MII_10BTCSR, CIPHY_10BTCSR_ECHO); 421133359Sobrien } else { 422133359Sobrien PHY_CLRBIT(sc, CIPHY_MII_10BTCSR, CIPHY_10BTCSR_ECHO); 423133359Sobrien } 424133359Sobrien 425133359Sobrien break; 426133359Sobrien case MII_MODEL_CICADA_VSC8211: 427133359Sobrien case MII_MODEL_CICADA_CS8244: 428133359Sobrien case MII_MODEL_VITESSE_VSC8601: 429133359Sobrien break; 430133359Sobrien default: 431133359Sobrien device_printf(sc->mii_dev, "unknown CICADA PHY model %x\n", 432133359Sobrien model); 433133359Sobrien break; 434133359Sobrien } 435133359Sobrien} 436133359Sobrien