jmphy.c revision 221407
144963Sjb/*- 244963Sjb * Copyright (c) 2008, Pyun YongHyeon <yongari@FreeBSD.org> 344963Sjb * All rights reserved. 444963Sjb * 544963Sjb * Redistribution and use in source and binary forms, with or without 644963Sjb * modification, are permitted provided that the following conditions 744963Sjb * are met: 844963Sjb * 1. Redistributions of source code must retain the above copyright 944963Sjb * notice unmodified, this list of conditions, and the following 1044963Sjb * disclaimer. 1144963Sjb * 2. Redistributions in binary form must reproduce the above copyright 1244963Sjb * notice, this list of conditions and the following disclaimer in the 1344963Sjb * documentation and/or other materials provided with the distribution. 1444963Sjb * 1544963Sjb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1644963Sjb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1744963Sjb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1844963Sjb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1944963Sjb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2044963Sjb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2144963Sjb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2244963Sjb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2344963Sjb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2444963Sjb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2544963Sjb * SUCH DAMAGE. 2644963Sjb */ 2744963Sjb 2844963Sjb#include <sys/cdefs.h> 2944963Sjb__FBSDID("$FreeBSD: head/sys/dev/mii/jmphy.c 221407 2011-05-03 19:51:29Z marius $"); 3044963Sjb 3144963Sjb/* 3250476Speter * Driver for the JMicron JMP211 10/100/1000, JMP202 10/100 PHY. 3344963Sjb */ 34174112Sdeischen 35174112Sdeischen#include <sys/param.h> 3644963Sjb#include <sys/systm.h> 3744963Sjb#include <sys/kernel.h> 3844963Sjb#include <sys/module.h> 3944963Sjb#include <sys/socket.h> 40174112Sdeischen#include <sys/bus.h> 41103388Smini 4244963Sjb#include <net/if.h> 4344963Sjb#include <net/if_media.h> 4444963Sjb 4544963Sjb#include <dev/mii/mii.h> 4648046Sjb#include <dev/mii/miivar.h> 4744963Sjb#include "miidevs.h" 48113658Sdeischen 4948046Sjb#include <dev/mii/jmphyreg.h> 50113658Sdeischen 51113658Sdeischen#include "miibus_if.h" 52113658Sdeischen 53113658Sdeischenstatic int jmphy_probe(device_t); 5448046Sjbstatic int jmphy_attach(device_t); 5548046Sjbstatic void jmphy_reset(struct mii_softc *); 56113658Sdeischenstatic uint16_t jmphy_anar(struct ifmedia_entry *); 57113658Sdeischenstatic int jmphy_setmedia(struct mii_softc *, struct ifmedia_entry *); 5848046Sjb 5948046Sjbstatic device_method_t jmphy_methods[] = { 60113658Sdeischen /* Device interface. */ 61113658Sdeischen DEVMETHOD(device_probe, jmphy_probe), 6248046Sjb DEVMETHOD(device_attach, jmphy_attach), 6348046Sjb DEVMETHOD(device_detach, mii_phy_detach), 64113658Sdeischen DEVMETHOD(device_shutdown, bus_generic_shutdown), 65113658Sdeischen { NULL, NULL } 6648046Sjb}; 6748046Sjb 68113658Sdeischenstatic devclass_t jmphy_devclass; 69113658Sdeischenstatic driver_t jmphy_driver = { 7048046Sjb "jmphy", 7148046Sjb jmphy_methods, 7248046Sjb sizeof(struct mii_softc) 7348046Sjb}; 7448046Sjb 75113658SdeischenDRIVER_MODULE(jmphy, miibus, jmphy_driver, jmphy_devclass, 0, 0); 76113658Sdeischen 77113658Sdeischenstatic int jmphy_service(struct mii_softc *, struct mii_data *, int); 78113658Sdeischenstatic void jmphy_status(struct mii_softc *); 79113658Sdeischen 80113658Sdeischenstatic const struct mii_phydesc jmphys[] = { 81113658Sdeischen MII_PHY_DESC(JMICRON, JMP202), 8248046Sjb MII_PHY_DESC(JMICRON, JMP211), 8348046Sjb MII_PHY_END 8448046Sjb}; 8544963Sjb 8648046Sjbstatic const struct mii_phy_funcs jmphy_funcs = { 8744963Sjb jmphy_service, 8855194Sdeischen jmphy_status, 8944963Sjb jmphy_reset 9044963Sjb}; 9144963Sjb 9244963Sjbstatic int 9344963Sjbjmphy_probe(device_t dev) 9444963Sjb{ 9544963Sjb 9644963Sjb return (mii_phy_dev_probe(dev, jmphys, BUS_PROBE_DEFAULT)); 9744963Sjb} 9844963Sjb 9944963Sjbstatic int 10048046Sjbjmphy_attach(device_t dev) 10148046Sjb{ 10248046Sjb struct mii_attach_args *ma; 10348046Sjb u_int flags; 10448046Sjb 10548046Sjb ma = device_get_ivars(dev); 10648046Sjb flags = 0; 107113661Sdeischen if (strcmp(ma->mii_data->mii_ifp->if_dname, "jme") == 0 && 108113661Sdeischen (miibus_get_flags(dev) & MIIF_MACPRIV0) != 0) 109113661Sdeischen flags |= MIIF_PHYPRIV0; 110113661Sdeischen mii_phy_dev_attach(dev, flags, &jmphy_funcs, 1); 111113661Sdeischen return (0); 112113661Sdeischen} 113113661Sdeischen 11448046Sjbstatic int 11548046Sjbjmphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 11648046Sjb{ 11748046Sjb struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 11848046Sjb 11948046Sjb switch (cmd) { 12048046Sjb case MII_POLLSTAT: 12148046Sjb break; 12248046Sjb 12344963Sjb case MII_MEDIACHG: 12448046Sjb /* 12544963Sjb * If the interface is not up, don't do anything. 12644963Sjb */ 12744963Sjb if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 12844963Sjb break; 12944963Sjb 13044963Sjb if (jmphy_setmedia(sc, ife) != EJUSTRETURN) 131113658Sdeischen return (EINVAL); 132114187Sdeischen break; 13344963Sjb 13444963Sjb case MII_TICK: 13544963Sjb /* 13644963Sjb * Is the interface even up? 13744963Sjb */ 13844963Sjb if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 13944963Sjb return (0); 14044963Sjb 14144963Sjb /* 14248046Sjb * Only used for autonegotiation. 14348046Sjb */ 14448046Sjb if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 145113658Sdeischen sc->mii_ticks = 0; 146113658Sdeischen break; 147113658Sdeischen } 14848046Sjb 14948046Sjb /* Check for link. */ 15048046Sjb if ((PHY_READ(sc, JMPHY_SSR) & JMPHY_SSR_LINK_UP) != 0) { 15148046Sjb sc->mii_ticks = 0; 15248046Sjb break; 15348046Sjb } 15448046Sjb 15548046Sjb /* Announce link loss right after it happens. */ 15648046Sjb if (sc->mii_ticks++ == 0) 15744963Sjb break; 158114187Sdeischen if (sc->mii_ticks <= sc->mii_anegticks) 15948046Sjb return (0); 160113658Sdeischen 161114187Sdeischen sc->mii_ticks = 0; 162113658Sdeischen (void)jmphy_setmedia(sc, ife); 16344963Sjb break; 16444963Sjb } 16544963Sjb 16644963Sjb /* Update the media status. */ 16744963Sjb PHY_STATUS(sc); 16844963Sjb 16997204Sdeischen /* Callback if something changed. */ 17044963Sjb mii_phy_update(sc, cmd); 17148046Sjb return (0); 172113658Sdeischen} 17348046Sjb 174113658Sdeischenstatic void 175113658Sdeischenjmphy_status(struct mii_softc *sc) 176113658Sdeischen{ 177113658Sdeischen struct mii_data *mii = sc->mii_pdata; 17848046Sjb int bmcr, ssr; 179113658Sdeischen 180113658Sdeischen mii->mii_media_status = IFM_AVALID; 181113658Sdeischen mii->mii_media_active = IFM_ETHER; 182113658Sdeischen 183113658Sdeischen ssr = PHY_READ(sc, JMPHY_SSR); 184114187Sdeischen if ((ssr & JMPHY_SSR_LINK_UP) != 0) 185113658Sdeischen mii->mii_media_status |= IFM_ACTIVE; 186113658Sdeischen 18748046Sjb bmcr = PHY_READ(sc, MII_BMCR); 188113658Sdeischen if ((bmcr & BMCR_ISO) != 0) { 18944963Sjb mii->mii_media_active |= IFM_NONE; 19044963Sjb mii->mii_media_status = 0; 19144963Sjb return; 19244963Sjb } 19344963Sjb 19444963Sjb if ((bmcr & BMCR_LOOP) != 0) 19597204Sdeischen mii->mii_media_active |= IFM_LOOP; 19644963Sjb 19748046Sjb if ((ssr & JMPHY_SSR_SPD_DPLX_RESOLVED) == 0) { 198113658Sdeischen /* Erg, still trying, I guess... */ 19948046Sjb mii->mii_media_active |= IFM_NONE; 200113658Sdeischen return; 201113658Sdeischen } 202113658Sdeischen 203113658Sdeischen switch ((ssr & JMPHY_SSR_SPEED_MASK)) { 20448046Sjb case JMPHY_SSR_SPEED_1000: 205113658Sdeischen mii->mii_media_active |= IFM_1000_T; 206113658Sdeischen /* 207113658Sdeischen * jmphy(4) got a valid link so reset mii_ticks. 208113658Sdeischen * Resetting mii_ticks is needed in order to 209113658Sdeischen * detect link loss after auto-negotiation. 210114187Sdeischen */ 211113658Sdeischen sc->mii_ticks = 0; 212113658Sdeischen break; 21348046Sjb case JMPHY_SSR_SPEED_100: 214113658Sdeischen mii->mii_media_active |= IFM_100_TX; 21544963Sjb sc->mii_ticks = 0; 21644963Sjb break; 21744963Sjb case JMPHY_SSR_SPEED_10: 21844963Sjb mii->mii_media_active |= IFM_10_T; 21944963Sjb sc->mii_ticks = 0; 22044963Sjb break; 22144963Sjb default: 22244963Sjb mii->mii_media_active |= IFM_NONE; 22344963Sjb return; 22448046Sjb } 22548046Sjb 22648046Sjb if ((ssr & JMPHY_SSR_DUPLEX) != 0) 227113658Sdeischen mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc); 228113658Sdeischen else 22948046Sjb mii->mii_media_active |= IFM_HDX; 23044963Sjb 23144963Sjb if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) { 23244963Sjb if ((PHY_READ(sc, MII_100T2SR) & GTSR_MS_RES) != 0) 23344963Sjb mii->mii_media_active |= IFM_ETH_MASTER; 23444963Sjb } 23544963Sjb} 23644963Sjb 23744963Sjbstatic void 23844963Sjbjmphy_reset(struct mii_softc *sc) 23944963Sjb{ 24044963Sjb uint16_t t2cr, val; 24144963Sjb int i; 24244963Sjb 24348046Sjb /* Disable sleep mode. */ 244113658Sdeischen PHY_WRITE(sc, JMPHY_TMCTL, 24544963Sjb PHY_READ(sc, JMPHY_TMCTL) & ~JMPHY_TMCTL_SLEEP_ENB); 24644963Sjb PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN); 24744963Sjb 248132120Sdavidxu for (i = 0; i < 1000; i++) { 249132120Sdavidxu DELAY(1); 250132120Sdavidxu if ((PHY_READ(sc, MII_BMCR) & BMCR_RESET) == 0) 251132120Sdavidxu break; 252132120Sdavidxu } 253132120Sdavidxu /* Perform vendor recommended PHY calibration. */ 254132120Sdavidxu if ((sc->mii_flags & MIIF_PHYPRIV0) != 0) { 255132120Sdavidxu /* Select PHY test mode 1. */ 256132120Sdavidxu t2cr = PHY_READ(sc, MII_100T2CR); 257132120Sdavidxu t2cr &= ~GTCR_TEST_MASK; 25844963Sjb t2cr |= 0x2000; 259132120Sdavidxu PHY_WRITE(sc, MII_100T2CR, t2cr); 260132120Sdavidxu /* Apply calibration patch. */ 261132120Sdavidxu PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_READ | 262132120Sdavidxu JMPHY_EXT_COMM_2); 263132120Sdavidxu val = PHY_READ(sc, JMPHY_SPEC_DATA); 264132120Sdavidxu val &= ~0x0002; 265132120Sdavidxu val |= 0x0010 | 0x0001; 266132120Sdavidxu PHY_WRITE(sc, JMPHY_SPEC_DATA, val); 267132120Sdavidxu PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_WRITE | 268132120Sdavidxu JMPHY_EXT_COMM_2); 269132120Sdavidxu 270132120Sdavidxu /* XXX 20ms to complete recalibration. */ 271132120Sdavidxu DELAY(20 * 1000); 272132120Sdavidxu 273132120Sdavidxu PHY_READ(sc, MII_100T2CR); 274132120Sdavidxu PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_READ | 275132120Sdavidxu JMPHY_EXT_COMM_2); 276132120Sdavidxu val = PHY_READ(sc, JMPHY_SPEC_DATA); 277132120Sdavidxu val &= ~(0x0001 | 0x0002 | 0x0010); 278132120Sdavidxu PHY_WRITE(sc, JMPHY_SPEC_DATA, val); 279132120Sdavidxu PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_WRITE | 280133047Sdavidxu JMPHY_EXT_COMM_2); 281132120Sdavidxu /* Disable PHY test mode. */ 282132120Sdavidxu PHY_READ(sc, MII_100T2CR); 283132120Sdavidxu t2cr &= ~GTCR_TEST_MASK; 284132120Sdavidxu PHY_WRITE(sc, MII_100T2CR, t2cr); 285132120Sdavidxu } 286132120Sdavidxu} 287132120Sdavidxu 288132120Sdavidxustatic uint16_t 289132120Sdavidxujmphy_anar(struct ifmedia_entry *ife) 290132120Sdavidxu{ 291132120Sdavidxu uint16_t anar; 292132120Sdavidxu 293132120Sdavidxu anar = 0; 294132120Sdavidxu switch (IFM_SUBTYPE(ife->ifm_media)) { 295132120Sdavidxu case IFM_AUTO: 296132120Sdavidxu anar |= ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10; 297132120Sdavidxu break; 298132120Sdavidxu case IFM_1000_T: 299132120Sdavidxu break; 30044963Sjb case IFM_100_TX: 30144963Sjb anar |= ANAR_TX | ANAR_TX_FD; 30244963Sjb break; 30344963Sjb case IFM_10_T: 30444963Sjb anar |= ANAR_10 | ANAR_10_FD; 30544963Sjb break; 30648046Sjb default: 30748046Sjb break; 308113658Sdeischen } 30948046Sjb 31048046Sjb return (anar); 31144963Sjb} 31244963Sjb 31348046Sjbstatic int 31444963Sjbjmphy_setmedia(struct mii_softc *sc, struct ifmedia_entry *ife) 31544963Sjb{ 31644963Sjb uint16_t anar, bmcr, gig; 31744963Sjb 31844963Sjb gig = 0; 31944963Sjb bmcr = PHY_READ(sc, MII_BMCR); 32044963Sjb switch (IFM_SUBTYPE(ife->ifm_media)) { 32144963Sjb case IFM_AUTO: 32244963Sjb gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX; 32344963Sjb break; 32444963Sjb case IFM_1000_T: 32544963Sjb gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX; 32644963Sjb break; 32744963Sjb case IFM_100_TX: 328 case IFM_10_T: 329 break; 330 case IFM_NONE: 331 PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO | BMCR_PDOWN); 332 return (EJUSTRETURN); 333 default: 334 return (EINVAL); 335 } 336 337 anar = jmphy_anar(ife); 338 if ((IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO || 339 (ife->ifm_media & IFM_FDX) != 0) && 340 ((ife->ifm_media & IFM_FLOW) != 0 || 341 (sc->mii_flags & MIIF_FORCEPAUSE) != 0)) 342 anar |= ANAR_PAUSE_TOWARDS; 343 344 if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0) { 345 if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) { 346 gig |= GTCR_MAN_MS; 347 if ((ife->ifm_media & IFM_ETH_MASTER) != 0) 348 gig |= GTCR_ADV_MS; 349 } 350 PHY_WRITE(sc, MII_100T2CR, gig); 351 } 352 PHY_WRITE(sc, MII_ANAR, anar | ANAR_CSMA); 353 PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_AUTOEN | BMCR_STARTNEG); 354 355 return (EJUSTRETURN); 356} 357