Home | History | Annotate | Line # | Download | only in mii
igphy.c revision 1.13
      1 /*	$NetBSD: igphy.c,v 1.13 2007/12/09 20:28:03 jmcneill Exp $	*/
      2 
      3 /*
      4  * The Intel copyright applies to the analog register setup, and the
      5  * (currently disabled) SmartSpeed workaround code.
      6  */
      7 
      8 /*******************************************************************************
      9 
     10   Copyright (c) 2001-2003, Intel Corporation
     11   All rights reserved.
     12 
     13   Redistribution and use in source and binary forms, with or without
     14   modification, are permitted provided that the following conditions are met:
     15 
     16    1. Redistributions of source code must retain the above copyright notice,
     17       this list of conditions and the following disclaimer.
     18 
     19    2. Redistributions in binary form must reproduce the above copyright
     20       notice, this list of conditions and the following disclaimer in the
     21       documentation and/or other materials provided with the distribution.
     22 
     23    3. Neither the name of the Intel Corporation nor the names of its
     24       contributors may be used to endorse or promote products derived from
     25       this software without specific prior written permission.
     26 
     27   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     28   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     29   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     30   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
     31   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     32   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     33   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     34   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     35   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     36   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     37   POSSIBILITY OF SUCH DAMAGE.
     38 
     39 *******************************************************************************/
     40 
     41 
     42 /*-
     43  * Copyright (c) 1998, 1999, 2000, 2003 The NetBSD Foundation, Inc.
     44  * All rights reserved.
     45  *
     46  * This code is derived from software contributed to The NetBSD Foundation
     47  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
     48  * NASA Ames Research Center, and by Frank van der Linden.
     49  *
     50  * Redistribution and use in source and binary forms, with or without
     51  * modification, are permitted provided that the following conditions
     52  * are met:
     53  * 1. Redistributions of source code must retain the above copyright
     54  *    notice, this list of conditions and the following disclaimer.
     55  * 2. Redistributions in binary form must reproduce the above copyright
     56  *    notice, this list of conditions and the following disclaimer in the
     57  *    documentation and/or other materials provided with the distribution.
     58  * 3. All advertising materials mentioning features or use of this software
     59  *    must display the following acknowledgement:
     60  *	This product includes software developed by the NetBSD
     61  *	Foundation, Inc. and its contributors.
     62  * 4. Neither the name of The NetBSD Foundation nor the names of its
     63  *    contributors may be used to endorse or promote products derived
     64  *    from this software without specific prior written permission.
     65  *
     66  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     67  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     68  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     69  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     70  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     71  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     72  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     73  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     74  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     75  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     76  * POSSIBILITY OF SUCH DAMAGE.
     77  */
     78 
     79 #include <sys/cdefs.h>
     80 __KERNEL_RCSID(0, "$NetBSD: igphy.c,v 1.13 2007/12/09 20:28:03 jmcneill Exp $");
     81 
     82 #include "opt_mii.h"
     83 
     84 #include <sys/param.h>
     85 #include <sys/systm.h>
     86 #include <sys/kernel.h>
     87 #include <sys/device.h>
     88 #include <sys/socket.h>
     89 #include <sys/errno.h>
     90 
     91 #include <net/if.h>
     92 #include <net/if_media.h>
     93 
     94 #include <dev/mii/mii.h>
     95 #include <dev/mii/miivar.h>
     96 #include <dev/mii/miidevs.h>
     97 
     98 #include <dev/mii/igphyreg.h>
     99 
    100 struct igphy_softc {
    101 	struct mii_softc sc_mii;
    102 	int sc_smartspeed;
    103 };
    104 
    105 static void igphy_reset(struct mii_softc *);
    106 static void igphy_load_dspcode(struct mii_softc *);
    107 static void igphy_smartspeed_workaround(struct mii_softc *sc);
    108 
    109 static int	igphymatch(struct device *, struct cfdata *, void *);
    110 static void	igphyattach(struct device *, struct device *, void *);
    111 
    112 CFATTACH_DECL(igphy, sizeof(struct igphy_softc),
    113     igphymatch, igphyattach, mii_phy_detach, mii_phy_activate);
    114 
    115 static int	igphy_service(struct mii_softc *, struct mii_data *, int);
    116 static void	igphy_status(struct mii_softc *);
    117 
    118 static const struct mii_phy_funcs igphy_funcs = {
    119 	igphy_service, igphy_status, igphy_reset,
    120 };
    121 
    122 static const struct mii_phydesc igphys[] = {
    123 	{ MII_OUI_yyINTEL,		MII_MODEL_yyINTEL_IGP01E1000,
    124 	  MII_STR_yyINTEL_IGP01E1000 },
    125 
    126 	{ MII_OUI_yyINTEL,		MII_MODEL_yyINTEL_I82566,
    127 	  MII_STR_yyINTEL_I82566 },
    128 
    129 	{0,				0,
    130 	 NULL },
    131 };
    132 
    133 static int
    134 igphymatch(struct device *parent, struct cfdata *match,
    135     void *aux)
    136 {
    137 	struct mii_attach_args *ma = aux;
    138 
    139 	if (mii_phy_match(ma, igphys) != NULL)
    140 		return 10;
    141 
    142 	return 0;
    143 }
    144 
    145 static void
    146 igphyattach(struct device *parent, struct device *self, void *aux)
    147 {
    148 	struct mii_softc *sc = device_private(self);
    149 	struct mii_attach_args *ma = aux;
    150 	struct mii_data *mii = ma->mii_data;
    151 	const struct mii_phydesc *mpd;
    152 
    153 	mpd = mii_phy_match(ma, igphys);
    154 	aprint_naive(": Media interface\n");
    155 	aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
    156 
    157 	sc->mii_inst = mii->mii_instance;
    158 	sc->mii_phy = ma->mii_phyno;
    159 	sc->mii_funcs = &igphy_funcs;
    160 	sc->mii_pdata = mii;
    161 	sc->mii_flags = ma->mii_flags;
    162 	sc->mii_anegticks = MII_ANEGTICKS_GIGE;
    163 
    164 	PHY_RESET(sc);
    165 
    166 	sc->mii_capabilities =
    167 	    PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
    168 	if (sc->mii_capabilities & BMSR_EXTSTAT)
    169 		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
    170 	aprint_normal("%s: ", sc->mii_dev.dv_xname);
    171 	if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 &&
    172 	    (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0)
    173 		aprint_error("no media present");
    174 	else
    175 		mii_phy_add_media(sc);
    176 	aprint_normal("\n");
    177 
    178 	if (!pmf_device_register(self, NULL, mii_phy_resume))
    179 		aprint_error_dev(self, "couldn't establish power handler\n");
    180 }
    181 
    182 static void
    183 igphy_load_dspcode(struct mii_softc *sc)
    184 {
    185 	static const struct {
    186 		int reg;
    187 		uint16_t val;
    188 	} dspcode[] = {
    189 		{ 0x1f95, 0x0001 },
    190 		{ 0x1f71, 0xbd21 },
    191 		{ 0x1f79, 0x0018 },
    192 		{ 0x1f30, 0x1600 },
    193 		{ 0x1f31, 0x0014 },
    194 		{ 0x1f32, 0x161c },
    195 		{ 0x1f94, 0x0003 },
    196 		{ 0x1f96, 0x003f },
    197 		{ 0x2010, 0x0008 },
    198 		{ 0, 0 },
    199 	};
    200 	int i;
    201 
    202 	delay(10);
    203 
    204 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT, 0x0000);
    205 	PHY_WRITE(sc, 0x0000, 0x0140);
    206 
    207 	delay(5);
    208 
    209 	for (i = 0; dspcode[i].reg != 0; i++)
    210 		IGPHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
    211 
    212 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT,0x0000);
    213 	PHY_WRITE(sc, 0x0000, 0x3300);
    214 }
    215 
    216 static void
    217 igphy_reset(struct mii_softc *sc)
    218 {
    219 	uint16_t fused, fine, coarse;
    220 
    221 	mii_phy_reset(sc);
    222 	igphy_load_dspcode(sc);
    223 
    224 	fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_SPARE_FUSE_STATUS);
    225 	if ((fused & ANALOG_SPARE_FUSE_ENABLED) == 0) {
    226 		fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_FUSE_STATUS);
    227 
    228 		fine = fused & ANALOG_FUSE_FINE_MASK;
    229 		coarse = fused & ANALOG_FUSE_COARSE_MASK;
    230 
    231 		if (coarse > ANALOG_FUSE_COARSE_THRESH) {
    232 			coarse -= ANALOG_FUSE_COARSE_10;
    233 			fine -= ANALOG_FUSE_FINE_1;
    234 		} else if (coarse == ANALOG_FUSE_COARSE_THRESH)
    235 			fine -= ANALOG_FUSE_FINE_10;
    236 
    237 		fused = (fused & ANALOG_FUSE_POLY_MASK) |
    238 			(fine & ANALOG_FUSE_FINE_MASK) |
    239 			(coarse & ANALOG_FUSE_COARSE_MASK);
    240 
    241 		IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_CONTROL, fused);
    242 		IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_BYPASS,
    243 		    ANALOG_FUSE_ENABLE_SW_CONTROL);
    244 	}
    245 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT,0x0000);
    246 }
    247 
    248 
    249 static int
    250 igphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
    251 {
    252 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
    253 	uint16_t reg;
    254 
    255 	switch (cmd) {
    256 	case MII_POLLSTAT:
    257 		/*
    258 		 * If we're not polling our PHY instance, just return.
    259 		 */
    260 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
    261 			return (0);
    262 		break;
    263 
    264 	case MII_MEDIACHG:
    265 		/*
    266 		 * If the media indicates a different PHY instance,
    267 		 * isolate ourselves.
    268 		 */
    269 		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
    270 			reg = PHY_READ(sc, MII_BMCR);
    271 			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
    272 			return (0);
    273 		}
    274 
    275 		/*
    276 		 * If the interface is not up, don't do anything.
    277 		 */
    278 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
    279 			break;
    280 
    281 		reg = PHY_READ(sc, MII_IGPHY_PORT_CTRL);
    282 		if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
    283 			reg |= PSCR_AUTO_MDIX;
    284 			reg &= ~PSCR_FORCE_MDI_MDIX;
    285 			PHY_WRITE(sc, MII_IGPHY_PORT_CTRL, reg);
    286 		} else {
    287 			reg &= ~(PSCR_AUTO_MDIX | PSCR_FORCE_MDI_MDIX);
    288 			PHY_WRITE(sc, MII_IGPHY_PORT_CTRL, reg);
    289 		}
    290 
    291 		mii_phy_setmedia(sc);
    292 		break;
    293 
    294 	case MII_TICK:
    295 		/*
    296 		 * If we're not currently selected, just return.
    297 		 */
    298 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
    299 			return (0);
    300 
    301 		igphy_smartspeed_workaround(sc);
    302 
    303 		if (mii_phy_tick(sc) == EJUSTRETURN)
    304 			return (0);
    305 		break;
    306 
    307 	case MII_DOWN:
    308 		mii_phy_down(sc);
    309 		return (0);
    310 	}
    311 
    312 	/* Update the media status. */
    313 	mii_phy_status(sc);
    314 
    315 	/* Callback if something changed. */
    316 	mii_phy_update(sc, cmd);
    317 	return (0);
    318 }
    319 
    320 
    321 static void
    322 igphy_status(struct mii_softc *sc)
    323 {
    324 	struct mii_data *mii = sc->mii_pdata;
    325 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
    326 	uint16_t bmcr, pssr, gtsr, bmsr;
    327 
    328 	mii->mii_media_status = IFM_AVALID;
    329 	mii->mii_media_active = IFM_ETHER;
    330 
    331 	pssr = PHY_READ(sc, MII_IGPHY_PORT_STATUS);
    332 
    333 	if (pssr & PSSR_LINK_UP)
    334 		mii->mii_media_status |= IFM_ACTIVE;
    335 
    336 	bmcr = PHY_READ(sc, MII_BMCR);
    337 	if (bmcr & BMCR_ISO) {
    338 		mii->mii_media_active |= IFM_NONE;
    339 		mii->mii_media_status = 0;
    340 		return;
    341 	}
    342 
    343 	if (bmcr & BMCR_LOOP)
    344 		mii->mii_media_active |= IFM_LOOP;
    345 
    346 	bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
    347 
    348 	/*
    349 	 * XXX can't check if the info is valid, no
    350 	 * 'negotiation done' bit?
    351 	 */
    352 	if (bmcr & BMCR_AUTOEN) {
    353 		if ((bmsr & BMSR_ACOMP) == 0) {
    354 			mii->mii_media_active |= IFM_NONE;
    355 			return;
    356 		}
    357 		switch (pssr & PSSR_SPEED_MASK) {
    358 		case PSSR_SPEED_1000MBPS:
    359 			mii->mii_media_active |= IFM_1000_T;
    360 			gtsr = PHY_READ(sc, MII_100T2SR);
    361 			if (gtsr & GTSR_MS_RES)
    362 				mii->mii_media_active |= IFM_ETH_MASTER;
    363 			break;
    364 
    365 		case PSSR_SPEED_100MBPS:
    366 			mii->mii_media_active |= IFM_100_TX;
    367 			break;
    368 
    369 		case PSSR_SPEED_10MBPS:
    370 			mii->mii_media_active |= IFM_10_T;
    371 			break;
    372 
    373 		default:
    374 			mii->mii_media_active |= IFM_NONE;
    375 			mii->mii_media_status = 0;
    376 			return;
    377 		}
    378 
    379 		if (pssr & PSSR_FULL_DUPLEX)
    380 			mii->mii_media_active |=
    381 			    IFM_FDX | mii_phy_flowstatus(sc);
    382 	} else
    383 		mii->mii_media_active = ife->ifm_media;
    384 }
    385 
    386 static void
    387 igphy_smartspeed_workaround(struct mii_softc *sc)
    388 {
    389 	struct igphy_softc *igsc = (struct igphy_softc *) sc;
    390 	uint16_t reg, gtsr, gtcr;
    391 
    392 	if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0)
    393 		return;
    394 
    395 	/* XXX Assume 1000TX-FDX is advertized if doing autonegotiation. */
    396 
    397 	reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
    398 	if ((reg & BMSR_LINK) == 0) {
    399 		switch (igsc->sc_smartspeed) {
    400 		case 0:
    401 			gtsr = PHY_READ(sc, MII_100T2SR);
    402 			if (!(gtsr & GTSR_MAN_MS_FLT))
    403 				break;
    404 			gtsr = PHY_READ(sc, MII_100T2SR);
    405 			if (gtsr & GTSR_MAN_MS_FLT) {
    406 				gtcr = PHY_READ(sc, MII_100T2CR);
    407 				if (gtcr & GTCR_MAN_MS) {
    408 					gtcr &= ~GTCR_MAN_MS;
    409 					PHY_WRITE(sc, MII_100T2CR,
    410 					    gtcr);
    411 				}
    412 				mii_phy_auto(sc, 0);
    413 			}
    414 			break;
    415 		case IGPHY_TICK_DOWNSHIFT:
    416 			gtcr = PHY_READ(sc, MII_100T2CR);
    417 			gtcr |= GTCR_MAN_MS;
    418 			PHY_WRITE(sc, MII_100T2CR, gtcr);
    419 			mii_phy_auto(sc, 0);
    420 			break;
    421 		default:
    422 			break;
    423 		}
    424 		if (igsc->sc_smartspeed++ == IGPHY_TICK_MAX)
    425 			igsc->sc_smartspeed = 0;
    426 	} else
    427 		igsc->sc_smartspeed = 0;
    428 }
    429