Home | History | Annotate | Line # | Download | only in mii
igphy.c revision 1.19
      1 /*	$NetBSD: igphy.c,v 1.19 2009/12/16 04:50:35 msaitoh 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  *
     59  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     60  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     61  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     62  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     63  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     64  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     65  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     66  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     67  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     68  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     69  * POSSIBILITY OF SUCH DAMAGE.
     70  */
     71 
     72 #include <sys/cdefs.h>
     73 __KERNEL_RCSID(0, "$NetBSD: igphy.c,v 1.19 2009/12/16 04:50:35 msaitoh Exp $");
     74 
     75 #include "opt_mii.h"
     76 
     77 #include <sys/param.h>
     78 #include <sys/systm.h>
     79 #include <sys/kernel.h>
     80 #include <sys/device.h>
     81 #include <sys/socket.h>
     82 #include <sys/errno.h>
     83 
     84 #include <net/if.h>
     85 #include <net/if_media.h>
     86 
     87 #include <dev/mii/mii.h>
     88 #include <dev/mii/miivar.h>
     89 #include <dev/mii/miidevs.h>
     90 #include <dev/mii/igphyreg.h>
     91 #include <dev/pci/if_wmvar.h>
     92 
     93 struct igphy_softc {
     94 	struct mii_softc sc_mii;
     95 	int sc_smartspeed;
     96 	uint32_t sc_mactype;
     97 };
     98 
     99 static void igphy_reset(struct mii_softc *);
    100 static void igphy_load_dspcode(struct mii_softc *);
    101 static void igphy_smartspeed_workaround(struct mii_softc *sc);
    102 
    103 static int	igphymatch(device_t, cfdata_t, void *);
    104 static void	igphyattach(device_t, device_t, void *);
    105 
    106 CFATTACH_DECL_NEW(igphy, sizeof(struct igphy_softc),
    107     igphymatch, igphyattach, mii_phy_detach, mii_phy_activate);
    108 
    109 static int	igphy_service(struct mii_softc *, struct mii_data *, int);
    110 static void	igphy_status(struct mii_softc *);
    111 
    112 static const struct mii_phy_funcs igphy_funcs = {
    113 	igphy_service, igphy_status, igphy_reset,
    114 };
    115 
    116 static const struct mii_phydesc igphys[] = {
    117 	{ MII_OUI_yyINTEL,		MII_MODEL_yyINTEL_IGP01E1000,
    118 	  MII_STR_yyINTEL_IGP01E1000 },
    119 
    120 	{ MII_OUI_yyINTEL,		MII_MODEL_yyINTEL_I82566,
    121 	  MII_STR_yyINTEL_I82566 },
    122 
    123 	{0,				0,
    124 	 NULL },
    125 };
    126 
    127 static int
    128 igphymatch(device_t parent, cfdata_t match, void *aux)
    129 {
    130 	struct mii_attach_args *ma = aux;
    131 
    132 	if (mii_phy_match(ma, igphys) != NULL)
    133 		return 10;
    134 
    135 	return 0;
    136 }
    137 
    138 static void
    139 igphyattach(device_t parent, device_t self, void *aux)
    140 {
    141 	struct mii_softc *sc = device_private(self);
    142 	struct mii_attach_args *ma = aux;
    143 	struct mii_data *mii = ma->mii_data;
    144 	const struct mii_phydesc *mpd;
    145 	struct igphy_softc *igsc = (struct igphy_softc *) sc;
    146 	prop_dictionary_t dict;
    147 
    148 	mpd = mii_phy_match(ma, igphys);
    149 	aprint_naive(": Media interface\n");
    150 	aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
    151 
    152 	dict = device_properties(parent);
    153 	if (!prop_dictionary_get_uint32(dict, "mactype", &igsc->sc_mactype))
    154 		aprint_error("WARNING! Failed to get mactype\n");
    155 
    156 	sc->mii_dev = self;
    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_dev(self, "");
    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 
    179 static void
    180 igphy_load_dspcode(struct mii_softc *sc)
    181 {
    182 	struct igphy_softc *igsc = (struct igphy_softc *) sc;
    183 	static const struct {
    184 		int reg;
    185 		uint16_t val;
    186 	} dspcode[] = {
    187 		{ 0x1f95, 0x0001 },
    188 		{ 0x1f71, 0xbd21 },
    189 		{ 0x1f79, 0x0018 },
    190 		{ 0x1f30, 0x1600 },
    191 		{ 0x1f31, 0x0014 },
    192 		{ 0x1f32, 0x161c },
    193 		{ 0x1f94, 0x0003 },
    194 		{ 0x1f96, 0x003f },
    195 		{ 0x2010, 0x0008 },
    196 		{ 0, 0 },
    197 	};
    198 	int i;
    199 
    200 	/* This workaround is only for 82541 and 82547 */
    201 	switch (igsc->sc_mactype) {
    202 	case WM_T_82541:
    203 	case WM_T_82547:
    204 	case WM_T_82541_2:
    205 	case WM_T_82547_2:
    206 		break;
    207 	default:
    208 		/* byebye */
    209 		return;
    210 	}
    211 
    212 	delay(10);
    213 
    214 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT, 0x0000);
    215 	PHY_WRITE(sc, 0x0000, 0x0140);
    216 
    217 	delay(5);
    218 
    219 	switch (igsc->sc_mactype) {
    220 	case WM_T_82541:
    221 	case WM_T_82547:
    222 		for (i = 0; dspcode[i].reg != 0; i++)
    223 			IGPHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
    224 		break;
    225 	case WM_T_82541_2:
    226 	case WM_T_82547_2:
    227 		IGPHY_WRITE(sc, 0x1f73, 0x0099);
    228 		break;
    229 	default:
    230 		break;
    231 	}
    232 
    233 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT,0x0000);
    234 	PHY_WRITE(sc, 0x0000, 0x3300);
    235 }
    236 
    237 static void
    238 igphy_reset(struct mii_softc *sc)
    239 {
    240 	struct igphy_softc *igsc = (struct igphy_softc *) sc;
    241 	uint16_t fused, fine, coarse;
    242 
    243 	mii_phy_reset(sc);
    244 	igphy_load_dspcode(sc);
    245 
    246 	if (igsc->sc_mactype == WM_T_82547) {
    247 		fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_SPARE_FUSE_STATUS);
    248 		if ((fused & ANALOG_SPARE_FUSE_ENABLED) == 0) {
    249 			fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_FUSE_STATUS);
    250 
    251 			fine = fused & ANALOG_FUSE_FINE_MASK;
    252 			coarse = fused & ANALOG_FUSE_COARSE_MASK;
    253 
    254 			if (coarse > ANALOG_FUSE_COARSE_THRESH) {
    255 				coarse -= ANALOG_FUSE_COARSE_10;
    256 				fine -= ANALOG_FUSE_FINE_1;
    257 			} else if (coarse == ANALOG_FUSE_COARSE_THRESH)
    258 				fine -= ANALOG_FUSE_FINE_10;
    259 
    260 			fused = (fused & ANALOG_FUSE_POLY_MASK) |
    261 			    (fine & ANALOG_FUSE_FINE_MASK) |
    262 			    (coarse & ANALOG_FUSE_COARSE_MASK);
    263 
    264 			IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_CONTROL, fused);
    265 			IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_BYPASS,
    266 			    ANALOG_FUSE_ENABLE_SW_CONTROL);
    267 		}
    268 	}
    269 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT,0x0000);
    270 }
    271 
    272 
    273 static int
    274 igphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
    275 {
    276 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
    277 	uint16_t reg;
    278 
    279 	switch (cmd) {
    280 	case MII_POLLSTAT:
    281 		/*
    282 		 * If we're not polling our PHY instance, just return.
    283 		 */
    284 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
    285 			return (0);
    286 		break;
    287 
    288 	case MII_MEDIACHG:
    289 		/*
    290 		 * If the media indicates a different PHY instance,
    291 		 * isolate ourselves.
    292 		 */
    293 		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
    294 			reg = PHY_READ(sc, MII_BMCR);
    295 			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
    296 			return (0);
    297 		}
    298 
    299 		/*
    300 		 * If the interface is not up, don't do anything.
    301 		 */
    302 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
    303 			break;
    304 
    305 		reg = PHY_READ(sc, MII_IGPHY_PORT_CTRL);
    306 		if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
    307 			reg |= PSCR_AUTO_MDIX;
    308 			reg &= ~PSCR_FORCE_MDI_MDIX;
    309 			PHY_WRITE(sc, MII_IGPHY_PORT_CTRL, reg);
    310 		} else {
    311 			reg &= ~(PSCR_AUTO_MDIX | PSCR_FORCE_MDI_MDIX);
    312 			PHY_WRITE(sc, MII_IGPHY_PORT_CTRL, reg);
    313 		}
    314 
    315 		mii_phy_setmedia(sc);
    316 		break;
    317 
    318 	case MII_TICK:
    319 		/*
    320 		 * If we're not currently selected, just return.
    321 		 */
    322 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
    323 			return (0);
    324 
    325 		igphy_smartspeed_workaround(sc);
    326 
    327 		if (mii_phy_tick(sc) == EJUSTRETURN)
    328 			return (0);
    329 		break;
    330 
    331 	case MII_DOWN:
    332 		mii_phy_down(sc);
    333 		return (0);
    334 	}
    335 
    336 	/* Update the media status. */
    337 	mii_phy_status(sc);
    338 
    339 	/* Callback if something changed. */
    340 	mii_phy_update(sc, cmd);
    341 	return (0);
    342 }
    343 
    344 
    345 static void
    346 igphy_status(struct mii_softc *sc)
    347 {
    348 	struct mii_data *mii = sc->mii_pdata;
    349 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
    350 	uint16_t bmcr, pssr, gtsr, bmsr;
    351 
    352 	mii->mii_media_status = IFM_AVALID;
    353 	mii->mii_media_active = IFM_ETHER;
    354 
    355 	pssr = PHY_READ(sc, MII_IGPHY_PORT_STATUS);
    356 
    357 	if (pssr & PSSR_LINK_UP)
    358 		mii->mii_media_status |= IFM_ACTIVE;
    359 
    360 	bmcr = PHY_READ(sc, MII_BMCR);
    361 	if (bmcr & BMCR_ISO) {
    362 		mii->mii_media_active |= IFM_NONE;
    363 		mii->mii_media_status = 0;
    364 		return;
    365 	}
    366 
    367 	if (bmcr & BMCR_LOOP)
    368 		mii->mii_media_active |= IFM_LOOP;
    369 
    370 	bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
    371 
    372 	/*
    373 	 * XXX can't check if the info is valid, no
    374 	 * 'negotiation done' bit?
    375 	 */
    376 	if (bmcr & BMCR_AUTOEN) {
    377 		if ((bmsr & BMSR_ACOMP) == 0) {
    378 			mii->mii_media_active |= IFM_NONE;
    379 			return;
    380 		}
    381 		switch (pssr & PSSR_SPEED_MASK) {
    382 		case PSSR_SPEED_1000MBPS:
    383 			mii->mii_media_active |= IFM_1000_T;
    384 			gtsr = PHY_READ(sc, MII_100T2SR);
    385 			if (gtsr & GTSR_MS_RES)
    386 				mii->mii_media_active |= IFM_ETH_MASTER;
    387 			break;
    388 
    389 		case PSSR_SPEED_100MBPS:
    390 			mii->mii_media_active |= IFM_100_TX;
    391 			break;
    392 
    393 		case PSSR_SPEED_10MBPS:
    394 			mii->mii_media_active |= IFM_10_T;
    395 			break;
    396 
    397 		default:
    398 			mii->mii_media_active |= IFM_NONE;
    399 			mii->mii_media_status = 0;
    400 			return;
    401 		}
    402 
    403 		if (pssr & PSSR_FULL_DUPLEX)
    404 			mii->mii_media_active |=
    405 			    IFM_FDX | mii_phy_flowstatus(sc);
    406 	} else
    407 		mii->mii_media_active = ife->ifm_media;
    408 }
    409 
    410 static void
    411 igphy_smartspeed_workaround(struct mii_softc *sc)
    412 {
    413 	struct igphy_softc *igsc = (struct igphy_softc *) sc;
    414 	uint16_t reg, gtsr, gtcr;
    415 
    416 
    417 	/* This workaround is only for 82541 and 82547 */
    418 	switch (igsc->sc_mactype) {
    419 	case WM_T_82541:
    420 	case WM_T_82541_2:
    421 	case WM_T_82547:
    422 	case WM_T_82547_2:
    423 		break;
    424 	default:
    425 		/* byebye */
    426 		return;
    427 	}
    428 
    429 	if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0)
    430 		return;
    431 
    432 	/* XXX Assume 1000TX-FDX is advertized if doing autonegotiation. */
    433 
    434 	reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
    435 	if ((reg & BMSR_LINK) == 0) {
    436 		switch (igsc->sc_smartspeed) {
    437 		case 0:
    438 			gtsr = PHY_READ(sc, MII_100T2SR);
    439 			if (!(gtsr & GTSR_MAN_MS_FLT))
    440 				break;
    441 			gtsr = PHY_READ(sc, MII_100T2SR);
    442 			if (gtsr & GTSR_MAN_MS_FLT) {
    443 				gtcr = PHY_READ(sc, MII_100T2CR);
    444 				if (gtcr & GTCR_MAN_MS) {
    445 					gtcr &= ~GTCR_MAN_MS;
    446 					PHY_WRITE(sc, MII_100T2CR,
    447 					    gtcr);
    448 				}
    449 				mii_phy_auto(sc, 0);
    450 			}
    451 			break;
    452 		case IGPHY_TICK_DOWNSHIFT:
    453 			gtcr = PHY_READ(sc, MII_100T2CR);
    454 			gtcr |= GTCR_MAN_MS;
    455 			PHY_WRITE(sc, MII_100T2CR, gtcr);
    456 			mii_phy_auto(sc, 0);
    457 			break;
    458 		default:
    459 			break;
    460 		}
    461 		if (igsc->sc_smartspeed++ == IGPHY_TICK_MAX)
    462 			igsc->sc_smartspeed = 0;
    463 	} else
    464 		igsc->sc_smartspeed = 0;
    465 }
    466