1179896Sdelphij/*- 2179896Sdelphij * Copyright (c) 2007 The DragonFly Project. All rights reserved. 3179896Sdelphij * 4179896Sdelphij * This code is derived from software contributed to The DragonFly Project 5179896Sdelphij * by Sepherosa Ziehau <sepherosa@gmail.com> 6179896Sdelphij * 7179896Sdelphij * Redistribution and use in source and binary forms, with or without 8179896Sdelphij * modification, are permitted provided that the following conditions 9179896Sdelphij * are met: 10179896Sdelphij * 11179896Sdelphij * 1. Redistributions of source code must retain the above copyright 12179896Sdelphij * notice, this list of conditions and the following disclaimer. 13179896Sdelphij * 2. Redistributions in binary form must reproduce the above copyright 14179896Sdelphij * notice, this list of conditions and the following disclaimer in 15179896Sdelphij * the documentation and/or other materials provided with the 16179896Sdelphij * distribution. 17179896Sdelphij * 3. Neither the name of The DragonFly Project nor the names of its 18179896Sdelphij * contributors may be used to endorse or promote products derived 19179896Sdelphij * from this software without specific, prior written permission. 20179896Sdelphij * 21179896Sdelphij * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22179896Sdelphij * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23179896Sdelphij * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24179896Sdelphij * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25179896Sdelphij * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26179896Sdelphij * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27179896Sdelphij * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28179896Sdelphij * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29179896Sdelphij * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30179896Sdelphij * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31179896Sdelphij * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32179896Sdelphij * SUCH DAMAGE. 33179896Sdelphij * 34179896Sdelphij * $DragonFly: src/sys/dev/netif/mii_layer/truephy.c,v 1.3 2008/02/10 07:29:27 sephe Exp $ 35179896Sdelphij * $FreeBSD: releng/11.0/sys/dev/mii/truephy.c 276885 2015-01-09 12:26:08Z glebius $ 36179896Sdelphij */ 37179896Sdelphij 38179896Sdelphij#include <sys/param.h> 39179896Sdelphij#include <sys/systm.h> 40179896Sdelphij#include <sys/kernel.h> 41179896Sdelphij#include <sys/socket.h> 42179896Sdelphij#include <sys/errno.h> 43179896Sdelphij#include <sys/module.h> 44179896Sdelphij#include <sys/bus.h> 45179896Sdelphij 46179896Sdelphij#include <net/if.h> 47257184Sglebius#include <net/if_var.h> 48179896Sdelphij#include <net/if_media.h> 49179896Sdelphij#include <net/if_arp.h> 50179896Sdelphij#include <net/ethernet.h> 51179896Sdelphij#include <net/if_vlan_var.h> 52179896Sdelphij 53179896Sdelphij#include <dev/mii/mii.h> 54179896Sdelphij#include <dev/mii/miivar.h> 55179896Sdelphij#include "miidevs.h" 56179896Sdelphij 57179896Sdelphij#include <dev/mii/truephyreg.h> 58179896Sdelphij 59179896Sdelphij#include "miibus_if.h" 60179896Sdelphij 61185421Sbz#define TRUEPHY_FRAMELEN(mtu) \ 62185421Sbz (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + (mtu) + ETHER_CRC_LEN) 63179896Sdelphij 64179896Sdelphijstatic int truephy_service(struct mii_softc *, struct mii_data *, int); 65179896Sdelphijstatic int truephy_attach(device_t); 66179896Sdelphijstatic int truephy_probe(device_t); 67179896Sdelphijstatic void truephy_reset(struct mii_softc *); 68179896Sdelphijstatic void truephy_status(struct mii_softc *); 69179896Sdelphij 70179896Sdelphijstatic device_method_t truephy_methods[] = { 71179896Sdelphij /* device interface */ 72179896Sdelphij DEVMETHOD(device_probe, truephy_probe), 73179896Sdelphij DEVMETHOD(device_attach, truephy_attach), 74179896Sdelphij DEVMETHOD(device_detach, mii_phy_detach), 75179896Sdelphij DEVMETHOD(device_shutdown, bus_generic_shutdown), 76227908Smarius DEVMETHOD_END 77179896Sdelphij}; 78179896Sdelphij 79179896Sdelphijstatic const struct mii_phydesc truephys[] = { 80206563Syongari MII_PHY_DESC(AGERE, ET1011), 81179896Sdelphij MII_PHY_DESC(AGERE, ET1011C), 82179896Sdelphij MII_PHY_END 83179896Sdelphij}; 84179896Sdelphij 85179896Sdelphijstatic devclass_t truephy_devclass; 86179896Sdelphij 87179896Sdelphijstatic driver_t truephy_driver = { 88179896Sdelphij "truephy", 89179896Sdelphij truephy_methods, 90179896Sdelphij sizeof(struct mii_softc) 91179896Sdelphij}; 92179896Sdelphij 93179896SdelphijDRIVER_MODULE(truephy, miibus, truephy_driver, truephy_devclass, 0, 0); 94179896Sdelphij 95221407Smariusstatic const struct mii_phy_funcs truephy_funcs = { 96221407Smarius truephy_service, 97221407Smarius truephy_status, 98221407Smarius truephy_reset 99221407Smarius}; 100221407Smarius 101179896Sdelphijstatic const struct truephy_dsp { 102179896Sdelphij uint16_t index; 103179896Sdelphij uint16_t data; 104179896Sdelphij} truephy_dspcode[] = { 105179896Sdelphij { 0x880b, 0x0926 }, /* AfeIfCreg4B1000Msbs */ 106179896Sdelphij { 0x880c, 0x0926 }, /* AfeIfCreg4B100Msbs */ 107179896Sdelphij { 0x880d, 0x0926 }, /* AfeIfCreg4B10Msbs */ 108179896Sdelphij 109179896Sdelphij { 0x880e, 0xb4d3 }, /* AfeIfCreg4B1000Lsbs */ 110179896Sdelphij { 0x880f, 0xb4d3 }, /* AfeIfCreg4B100Lsbs */ 111179896Sdelphij { 0x8810, 0xb4d3 }, /* AfeIfCreg4B10Lsbs */ 112179896Sdelphij 113179896Sdelphij { 0x8805, 0xb03e }, /* AfeIfCreg3B1000Msbs */ 114179896Sdelphij { 0x8806, 0xb03e }, /* AfeIfCreg3B100Msbs */ 115179896Sdelphij { 0x8807, 0xff00 }, /* AfeIfCreg3B10Msbs */ 116179896Sdelphij 117179896Sdelphij { 0x8808, 0xe090 }, /* AfeIfCreg3B1000Lsbs */ 118179896Sdelphij { 0x8809, 0xe110 }, /* AfeIfCreg3B100Lsbs */ 119179896Sdelphij { 0x880a, 0x0000 }, /* AfeIfCreg3B10Lsbs */ 120179896Sdelphij 121179896Sdelphij { 0x300d, 1 }, /* DisableNorm */ 122179896Sdelphij 123179896Sdelphij { 0x280c, 0x0180 }, /* LinkHoldEnd */ 124179896Sdelphij 125179896Sdelphij { 0x1c21, 0x0002 }, /* AlphaM */ 126179896Sdelphij 127179896Sdelphij { 0x3821, 6 }, /* FfeLkgTx0 */ 128179896Sdelphij { 0x381d, 1 }, /* FfeLkg1g4 */ 129179896Sdelphij { 0x381e, 1 }, /* FfeLkg1g5 */ 130179896Sdelphij { 0x381f, 1 }, /* FfeLkg1g6 */ 131179896Sdelphij { 0x3820, 1 }, /* FfeLkg1g7 */ 132179896Sdelphij 133179896Sdelphij { 0x8402, 0x01f0 }, /* Btinact */ 134179896Sdelphij { 0x800e, 20 }, /* LftrainTime */ 135179896Sdelphij { 0x800f, 24 }, /* DvguardTime */ 136179896Sdelphij { 0x8010, 46 } /* IdlguardTime */ 137179896Sdelphij}; 138179896Sdelphij 139179896Sdelphijstatic int 140179896Sdelphijtruephy_probe(device_t dev) 141179896Sdelphij{ 142179896Sdelphij 143179896Sdelphij return (mii_phy_dev_probe(dev, truephys, BUS_PROBE_DEFAULT)); 144179896Sdelphij} 145179896Sdelphij 146179896Sdelphijstatic int 147179896Sdelphijtruephy_attach(device_t dev) 148179896Sdelphij{ 149179896Sdelphij struct mii_softc *sc; 150179896Sdelphij 151179896Sdelphij sc = device_get_softc(dev); 152179896Sdelphij 153221407Smarius mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE, 154221407Smarius &truephy_funcs, 0); 155179896Sdelphij 156221407Smarius PHY_RESET(sc); 157179896Sdelphij 158221407Smarius sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & sc->mii_capmask; 159179896Sdelphij if (sc->mii_capabilities & BMSR_EXTSTAT) { 160179896Sdelphij sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); 161179896Sdelphij /* No 1000baseT half-duplex support */ 162179896Sdelphij sc->mii_extcapabilities &= ~EXTSR_1000THDX; 163179896Sdelphij } 164179896Sdelphij 165179896Sdelphij device_printf(dev, " "); 166213364Smarius mii_phy_add_media(sc); 167179896Sdelphij printf("\n"); 168179896Sdelphij 169179896Sdelphij MIIBUS_MEDIAINIT(sc->mii_dev); 170213364Smarius return (0); 171179896Sdelphij} 172179896Sdelphij 173179896Sdelphijstatic int 174179896Sdelphijtruephy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 175179896Sdelphij{ 176179896Sdelphij struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 177179896Sdelphij int bmcr; 178179896Sdelphij 179179896Sdelphij switch (cmd) { 180179896Sdelphij case MII_POLLSTAT: 181179896Sdelphij break; 182179896Sdelphij 183179896Sdelphij case MII_MEDIACHG: 184179896Sdelphij if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 185179896Sdelphij bmcr = PHY_READ(sc, MII_BMCR) & ~BMCR_AUTOEN; 186179896Sdelphij PHY_WRITE(sc, MII_BMCR, bmcr); 187179896Sdelphij PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_PDOWN); 188179896Sdelphij } 189179896Sdelphij 190179896Sdelphij mii_phy_setmedia(sc); 191179896Sdelphij 192179896Sdelphij if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 193179896Sdelphij bmcr = PHY_READ(sc, MII_BMCR) & ~BMCR_PDOWN; 194179896Sdelphij PHY_WRITE(sc, MII_BMCR, bmcr); 195179896Sdelphij 196179896Sdelphij if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) { 197179896Sdelphij PHY_WRITE(sc, MII_BMCR, 198221407Smarius bmcr | BMCR_AUTOEN | BMCR_STARTNEG); 199179896Sdelphij } 200179896Sdelphij } 201179896Sdelphij break; 202179896Sdelphij 203179896Sdelphij case MII_TICK: 204179896Sdelphij if (mii_phy_tick(sc) == EJUSTRETURN) 205213364Smarius return (0); 206179896Sdelphij break; 207179896Sdelphij } 208179896Sdelphij 209179896Sdelphij /* Update the media status. */ 210221407Smarius PHY_STATUS(sc); 211179896Sdelphij 212179896Sdelphij /* Callback if something changed. */ 213179896Sdelphij mii_phy_update(sc, cmd); 214213364Smarius return (0); 215179896Sdelphij} 216179896Sdelphij 217179896Sdelphijstatic void 218179896Sdelphijtruephy_reset(struct mii_softc *sc) 219179896Sdelphij{ 220179896Sdelphij int i; 221179896Sdelphij 222221407Smarius if (sc->mii_mpd_model == MII_MODEL_AGERE_ET1011) { 223221407Smarius mii_phy_reset(sc); 224221407Smarius return; 225221407Smarius } 226221407Smarius 227179896Sdelphij for (i = 0; i < 2; ++i) { 228179896Sdelphij PHY_READ(sc, MII_PHYIDR1); 229179896Sdelphij PHY_READ(sc, MII_PHYIDR2); 230179896Sdelphij 231179896Sdelphij PHY_READ(sc, TRUEPHY_CTRL); 232179896Sdelphij PHY_WRITE(sc, TRUEPHY_CTRL, 233179896Sdelphij TRUEPHY_CTRL_DIAG | TRUEPHY_CTRL_RSV1); 234179896Sdelphij 235179896Sdelphij PHY_WRITE(sc, TRUEPHY_INDEX, TRUEPHY_INDEX_MAGIC); 236179896Sdelphij PHY_READ(sc, TRUEPHY_DATA); 237179896Sdelphij 238179896Sdelphij PHY_WRITE(sc, TRUEPHY_CTRL, TRUEPHY_CTRL_RSV1); 239179896Sdelphij } 240179896Sdelphij 241179896Sdelphij PHY_READ(sc, MII_BMCR); 242179896Sdelphij PHY_READ(sc, TRUEPHY_CTRL); 243179896Sdelphij PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_PDOWN | BMCR_S1000); 244179896Sdelphij PHY_WRITE(sc, TRUEPHY_CTRL, 245179896Sdelphij TRUEPHY_CTRL_DIAG | TRUEPHY_CTRL_RSV1 | TRUEPHY_CTRL_RSV0); 246179896Sdelphij 247276885Sglebius for (i = 0; i < nitems(truephy_dspcode); ++i) { 248179896Sdelphij const struct truephy_dsp *dsp = &truephy_dspcode[i]; 249179896Sdelphij 250179896Sdelphij PHY_WRITE(sc, TRUEPHY_INDEX, dsp->index); 251179896Sdelphij PHY_WRITE(sc, TRUEPHY_DATA, dsp->data); 252179896Sdelphij 253179896Sdelphij PHY_WRITE(sc, TRUEPHY_INDEX, dsp->index); 254179896Sdelphij PHY_READ(sc, TRUEPHY_DATA); 255179896Sdelphij } 256179896Sdelphij 257179896Sdelphij PHY_READ(sc, MII_BMCR); 258179896Sdelphij PHY_READ(sc, TRUEPHY_CTRL); 259179896Sdelphij PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_S1000); 260179896Sdelphij PHY_WRITE(sc, TRUEPHY_CTRL, TRUEPHY_CTRL_RSV1); 261179896Sdelphij 262179896Sdelphij mii_phy_reset(sc); 263179896Sdelphij 264266974Smarcel if (TRUEPHY_FRAMELEN((if_getmtu(sc->mii_pdata->mii_ifp)) > 2048)) { 265179896Sdelphij int conf; 266179896Sdelphij 267179896Sdelphij conf = PHY_READ(sc, TRUEPHY_CONF); 268179896Sdelphij conf &= ~TRUEPHY_CONF_TXFIFO_MASK; 269179896Sdelphij conf |= TRUEPHY_CONF_TXFIFO_24; 270179896Sdelphij PHY_WRITE(sc, TRUEPHY_CONF, conf); 271179896Sdelphij } 272179896Sdelphij} 273179896Sdelphij 274179896Sdelphijstatic void 275179896Sdelphijtruephy_status(struct mii_softc *sc) 276179896Sdelphij{ 277179896Sdelphij struct mii_data *mii = sc->mii_pdata; 278179896Sdelphij int bmsr, bmcr, sr; 279179896Sdelphij 280179896Sdelphij mii->mii_media_status = IFM_AVALID; 281179896Sdelphij mii->mii_media_active = IFM_ETHER; 282179896Sdelphij 283179896Sdelphij sr = PHY_READ(sc, TRUEPHY_SR); 284179896Sdelphij bmcr = PHY_READ(sc, MII_BMCR); 285179896Sdelphij 286179896Sdelphij bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 287179896Sdelphij if (bmsr & BMSR_LINK) 288179896Sdelphij mii->mii_media_status |= IFM_ACTIVE; 289179896Sdelphij 290179896Sdelphij if (bmcr & BMCR_AUTOEN) { 291179896Sdelphij if ((bmsr & BMSR_ACOMP) == 0) { 292179896Sdelphij mii->mii_media_active |= IFM_NONE; 293179896Sdelphij return; 294179896Sdelphij } 295179896Sdelphij } 296179896Sdelphij 297179896Sdelphij switch (sr & TRUEPHY_SR_SPD_MASK) { 298179896Sdelphij case TRUEPHY_SR_SPD_1000T: 299179896Sdelphij mii->mii_media_active |= IFM_1000_T; 300179896Sdelphij break; 301179896Sdelphij case TRUEPHY_SR_SPD_100TX: 302179896Sdelphij mii->mii_media_active |= IFM_100_TX; 303179896Sdelphij break; 304179896Sdelphij case TRUEPHY_SR_SPD_10T: 305179896Sdelphij mii->mii_media_active |= IFM_10_T; 306179896Sdelphij break; 307179896Sdelphij default: 308179896Sdelphij /* XXX will this ever happen? */ 309179896Sdelphij printf("invalid media SR %#x\n", sr); 310179896Sdelphij mii->mii_media_active |= IFM_NONE; 311179896Sdelphij return; 312179896Sdelphij } 313179896Sdelphij 314179896Sdelphij if (sr & TRUEPHY_SR_FDX) 315221407Smarius mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc); 316179896Sdelphij else 317179896Sdelphij mii->mii_media_active |= IFM_HDX; 318179896Sdelphij} 319