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/10.2/sys/dev/mii/truephy.c 227908 2011-11-23 20:27:26Z marius $ 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> 47179896Sdelphij#include <net/if_media.h> 48179896Sdelphij#include <net/if_arp.h> 49179896Sdelphij#include <net/ethernet.h> 50179896Sdelphij#include <net/if_vlan_var.h> 51179896Sdelphij 52179896Sdelphij#include <dev/mii/mii.h> 53179896Sdelphij#include <dev/mii/miivar.h> 54179896Sdelphij#include "miidevs.h" 55179896Sdelphij 56179896Sdelphij#include <dev/mii/truephyreg.h> 57179896Sdelphij 58179896Sdelphij#include "miibus_if.h" 59179896Sdelphij 60185421Sbz#define TRUEPHY_FRAMELEN(mtu) \ 61185421Sbz (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + (mtu) + ETHER_CRC_LEN) 62179896Sdelphij 63179896Sdelphijstatic int truephy_service(struct mii_softc *, struct mii_data *, int); 64179896Sdelphijstatic int truephy_attach(device_t); 65179896Sdelphijstatic int truephy_probe(device_t); 66179896Sdelphijstatic void truephy_reset(struct mii_softc *); 67179896Sdelphijstatic void truephy_status(struct mii_softc *); 68179896Sdelphij 69179896Sdelphijstatic device_method_t truephy_methods[] = { 70179896Sdelphij /* device interface */ 71179896Sdelphij DEVMETHOD(device_probe, truephy_probe), 72179896Sdelphij DEVMETHOD(device_attach, truephy_attach), 73179896Sdelphij DEVMETHOD(device_detach, mii_phy_detach), 74179896Sdelphij DEVMETHOD(device_shutdown, bus_generic_shutdown), 75227908Smarius DEVMETHOD_END 76179896Sdelphij}; 77179896Sdelphij 78179896Sdelphijstatic const struct mii_phydesc truephys[] = { 79206563Syongari MII_PHY_DESC(AGERE, ET1011), 80179896Sdelphij MII_PHY_DESC(AGERE, ET1011C), 81179896Sdelphij MII_PHY_END 82179896Sdelphij}; 83179896Sdelphij 84179896Sdelphijstatic devclass_t truephy_devclass; 85179896Sdelphij 86179896Sdelphijstatic driver_t truephy_driver = { 87179896Sdelphij "truephy", 88179896Sdelphij truephy_methods, 89179896Sdelphij sizeof(struct mii_softc) 90179896Sdelphij}; 91179896Sdelphij 92179896SdelphijDRIVER_MODULE(truephy, miibus, truephy_driver, truephy_devclass, 0, 0); 93179896Sdelphij 94221407Smariusstatic const struct mii_phy_funcs truephy_funcs = { 95221407Smarius truephy_service, 96221407Smarius truephy_status, 97221407Smarius truephy_reset 98221407Smarius}; 99221407Smarius 100179896Sdelphijstatic const struct truephy_dsp { 101179896Sdelphij uint16_t index; 102179896Sdelphij uint16_t data; 103179896Sdelphij} truephy_dspcode[] = { 104179896Sdelphij { 0x880b, 0x0926 }, /* AfeIfCreg4B1000Msbs */ 105179896Sdelphij { 0x880c, 0x0926 }, /* AfeIfCreg4B100Msbs */ 106179896Sdelphij { 0x880d, 0x0926 }, /* AfeIfCreg4B10Msbs */ 107179896Sdelphij 108179896Sdelphij { 0x880e, 0xb4d3 }, /* AfeIfCreg4B1000Lsbs */ 109179896Sdelphij { 0x880f, 0xb4d3 }, /* AfeIfCreg4B100Lsbs */ 110179896Sdelphij { 0x8810, 0xb4d3 }, /* AfeIfCreg4B10Lsbs */ 111179896Sdelphij 112179896Sdelphij { 0x8805, 0xb03e }, /* AfeIfCreg3B1000Msbs */ 113179896Sdelphij { 0x8806, 0xb03e }, /* AfeIfCreg3B100Msbs */ 114179896Sdelphij { 0x8807, 0xff00 }, /* AfeIfCreg3B10Msbs */ 115179896Sdelphij 116179896Sdelphij { 0x8808, 0xe090 }, /* AfeIfCreg3B1000Lsbs */ 117179896Sdelphij { 0x8809, 0xe110 }, /* AfeIfCreg3B100Lsbs */ 118179896Sdelphij { 0x880a, 0x0000 }, /* AfeIfCreg3B10Lsbs */ 119179896Sdelphij 120179896Sdelphij { 0x300d, 1 }, /* DisableNorm */ 121179896Sdelphij 122179896Sdelphij { 0x280c, 0x0180 }, /* LinkHoldEnd */ 123179896Sdelphij 124179896Sdelphij { 0x1c21, 0x0002 }, /* AlphaM */ 125179896Sdelphij 126179896Sdelphij { 0x3821, 6 }, /* FfeLkgTx0 */ 127179896Sdelphij { 0x381d, 1 }, /* FfeLkg1g4 */ 128179896Sdelphij { 0x381e, 1 }, /* FfeLkg1g5 */ 129179896Sdelphij { 0x381f, 1 }, /* FfeLkg1g6 */ 130179896Sdelphij { 0x3820, 1 }, /* FfeLkg1g7 */ 131179896Sdelphij 132179896Sdelphij { 0x8402, 0x01f0 }, /* Btinact */ 133179896Sdelphij { 0x800e, 20 }, /* LftrainTime */ 134179896Sdelphij { 0x800f, 24 }, /* DvguardTime */ 135179896Sdelphij { 0x8010, 46 } /* IdlguardTime */ 136179896Sdelphij}; 137179896Sdelphij 138179896Sdelphijstatic int 139179896Sdelphijtruephy_probe(device_t dev) 140179896Sdelphij{ 141179896Sdelphij 142179896Sdelphij return (mii_phy_dev_probe(dev, truephys, BUS_PROBE_DEFAULT)); 143179896Sdelphij} 144179896Sdelphij 145179896Sdelphijstatic int 146179896Sdelphijtruephy_attach(device_t dev) 147179896Sdelphij{ 148179896Sdelphij struct mii_softc *sc; 149179896Sdelphij 150179896Sdelphij sc = device_get_softc(dev); 151179896Sdelphij 152221407Smarius mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE, 153221407Smarius &truephy_funcs, 0); 154179896Sdelphij 155221407Smarius PHY_RESET(sc); 156179896Sdelphij 157221407Smarius sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & sc->mii_capmask; 158179896Sdelphij if (sc->mii_capabilities & BMSR_EXTSTAT) { 159179896Sdelphij sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); 160179896Sdelphij /* No 1000baseT half-duplex support */ 161179896Sdelphij sc->mii_extcapabilities &= ~EXTSR_1000THDX; 162179896Sdelphij } 163179896Sdelphij 164179896Sdelphij device_printf(dev, " "); 165213364Smarius mii_phy_add_media(sc); 166179896Sdelphij printf("\n"); 167179896Sdelphij 168179896Sdelphij MIIBUS_MEDIAINIT(sc->mii_dev); 169213364Smarius return (0); 170179896Sdelphij} 171179896Sdelphij 172179896Sdelphijstatic int 173179896Sdelphijtruephy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 174179896Sdelphij{ 175179896Sdelphij struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 176179896Sdelphij int bmcr; 177179896Sdelphij 178179896Sdelphij switch (cmd) { 179179896Sdelphij case MII_POLLSTAT: 180179896Sdelphij break; 181179896Sdelphij 182179896Sdelphij case MII_MEDIACHG: 183179896Sdelphij /* 184179896Sdelphij * If the interface is not up, don't do anything. 185179896Sdelphij */ 186179896Sdelphij if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 187179896Sdelphij break; 188179896Sdelphij 189179896Sdelphij if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 190179896Sdelphij bmcr = PHY_READ(sc, MII_BMCR) & ~BMCR_AUTOEN; 191179896Sdelphij PHY_WRITE(sc, MII_BMCR, bmcr); 192179896Sdelphij PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_PDOWN); 193179896Sdelphij } 194179896Sdelphij 195179896Sdelphij mii_phy_setmedia(sc); 196179896Sdelphij 197179896Sdelphij if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 198179896Sdelphij bmcr = PHY_READ(sc, MII_BMCR) & ~BMCR_PDOWN; 199179896Sdelphij PHY_WRITE(sc, MII_BMCR, bmcr); 200179896Sdelphij 201179896Sdelphij if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) { 202179896Sdelphij PHY_WRITE(sc, MII_BMCR, 203221407Smarius bmcr | BMCR_AUTOEN | BMCR_STARTNEG); 204179896Sdelphij } 205179896Sdelphij } 206179896Sdelphij break; 207179896Sdelphij 208179896Sdelphij case MII_TICK: 209179896Sdelphij if (mii_phy_tick(sc) == EJUSTRETURN) 210213364Smarius return (0); 211179896Sdelphij break; 212179896Sdelphij } 213179896Sdelphij 214179896Sdelphij /* Update the media status. */ 215221407Smarius PHY_STATUS(sc); 216179896Sdelphij 217179896Sdelphij /* Callback if something changed. */ 218179896Sdelphij mii_phy_update(sc, cmd); 219213364Smarius return (0); 220179896Sdelphij} 221179896Sdelphij 222179896Sdelphijstatic void 223179896Sdelphijtruephy_reset(struct mii_softc *sc) 224179896Sdelphij{ 225179896Sdelphij int i; 226179896Sdelphij 227221407Smarius if (sc->mii_mpd_model == MII_MODEL_AGERE_ET1011) { 228221407Smarius mii_phy_reset(sc); 229221407Smarius return; 230221407Smarius } 231221407Smarius 232179896Sdelphij for (i = 0; i < 2; ++i) { 233179896Sdelphij PHY_READ(sc, MII_PHYIDR1); 234179896Sdelphij PHY_READ(sc, MII_PHYIDR2); 235179896Sdelphij 236179896Sdelphij PHY_READ(sc, TRUEPHY_CTRL); 237179896Sdelphij PHY_WRITE(sc, TRUEPHY_CTRL, 238179896Sdelphij TRUEPHY_CTRL_DIAG | TRUEPHY_CTRL_RSV1); 239179896Sdelphij 240179896Sdelphij PHY_WRITE(sc, TRUEPHY_INDEX, TRUEPHY_INDEX_MAGIC); 241179896Sdelphij PHY_READ(sc, TRUEPHY_DATA); 242179896Sdelphij 243179896Sdelphij PHY_WRITE(sc, TRUEPHY_CTRL, TRUEPHY_CTRL_RSV1); 244179896Sdelphij } 245179896Sdelphij 246179896Sdelphij PHY_READ(sc, MII_BMCR); 247179896Sdelphij PHY_READ(sc, TRUEPHY_CTRL); 248179896Sdelphij PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_PDOWN | BMCR_S1000); 249179896Sdelphij PHY_WRITE(sc, TRUEPHY_CTRL, 250179896Sdelphij TRUEPHY_CTRL_DIAG | TRUEPHY_CTRL_RSV1 | TRUEPHY_CTRL_RSV0); 251179896Sdelphij 252179896Sdelphij#define N(arr) (int)(sizeof(arr) / sizeof(arr[0])) 253179896Sdelphij 254179896Sdelphij for (i = 0; i < N(truephy_dspcode); ++i) { 255179896Sdelphij const struct truephy_dsp *dsp = &truephy_dspcode[i]; 256179896Sdelphij 257179896Sdelphij PHY_WRITE(sc, TRUEPHY_INDEX, dsp->index); 258179896Sdelphij PHY_WRITE(sc, TRUEPHY_DATA, dsp->data); 259179896Sdelphij 260179896Sdelphij PHY_WRITE(sc, TRUEPHY_INDEX, dsp->index); 261179896Sdelphij PHY_READ(sc, TRUEPHY_DATA); 262179896Sdelphij } 263179896Sdelphij 264179896Sdelphij#undef N 265179896Sdelphij 266179896Sdelphij PHY_READ(sc, MII_BMCR); 267179896Sdelphij PHY_READ(sc, TRUEPHY_CTRL); 268179896Sdelphij PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_S1000); 269179896Sdelphij PHY_WRITE(sc, TRUEPHY_CTRL, TRUEPHY_CTRL_RSV1); 270179896Sdelphij 271179896Sdelphij mii_phy_reset(sc); 272179896Sdelphij 273185421Sbz if (TRUEPHY_FRAMELEN(sc->mii_pdata->mii_ifp->if_mtu) > 2048) { 274179896Sdelphij int conf; 275179896Sdelphij 276179896Sdelphij conf = PHY_READ(sc, TRUEPHY_CONF); 277179896Sdelphij conf &= ~TRUEPHY_CONF_TXFIFO_MASK; 278179896Sdelphij conf |= TRUEPHY_CONF_TXFIFO_24; 279179896Sdelphij PHY_WRITE(sc, TRUEPHY_CONF, conf); 280179896Sdelphij } 281179896Sdelphij} 282179896Sdelphij 283179896Sdelphijstatic void 284179896Sdelphijtruephy_status(struct mii_softc *sc) 285179896Sdelphij{ 286179896Sdelphij struct mii_data *mii = sc->mii_pdata; 287179896Sdelphij int bmsr, bmcr, sr; 288179896Sdelphij 289179896Sdelphij mii->mii_media_status = IFM_AVALID; 290179896Sdelphij mii->mii_media_active = IFM_ETHER; 291179896Sdelphij 292179896Sdelphij sr = PHY_READ(sc, TRUEPHY_SR); 293179896Sdelphij bmcr = PHY_READ(sc, MII_BMCR); 294179896Sdelphij 295179896Sdelphij bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 296179896Sdelphij if (bmsr & BMSR_LINK) 297179896Sdelphij mii->mii_media_status |= IFM_ACTIVE; 298179896Sdelphij 299179896Sdelphij if (bmcr & BMCR_AUTOEN) { 300179896Sdelphij if ((bmsr & BMSR_ACOMP) == 0) { 301179896Sdelphij mii->mii_media_active |= IFM_NONE; 302179896Sdelphij return; 303179896Sdelphij } 304179896Sdelphij } 305179896Sdelphij 306179896Sdelphij switch (sr & TRUEPHY_SR_SPD_MASK) { 307179896Sdelphij case TRUEPHY_SR_SPD_1000T: 308179896Sdelphij mii->mii_media_active |= IFM_1000_T; 309179896Sdelphij break; 310179896Sdelphij case TRUEPHY_SR_SPD_100TX: 311179896Sdelphij mii->mii_media_active |= IFM_100_TX; 312179896Sdelphij break; 313179896Sdelphij case TRUEPHY_SR_SPD_10T: 314179896Sdelphij mii->mii_media_active |= IFM_10_T; 315179896Sdelphij break; 316179896Sdelphij default: 317179896Sdelphij /* XXX will this ever happen? */ 318179896Sdelphij printf("invalid media SR %#x\n", sr); 319179896Sdelphij mii->mii_media_active |= IFM_NONE; 320179896Sdelphij return; 321179896Sdelphij } 322179896Sdelphij 323179896Sdelphij if (sr & TRUEPHY_SR_FDX) 324221407Smarius mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc); 325179896Sdelphij else 326179896Sdelphij mii->mii_media_active |= IFM_HDX; 327179896Sdelphij} 328