Home | History | Annotate | Line # | Download | only in mii
igphy.c revision 1.14.4.2
      1 /*	$NetBSD: igphy.c,v 1.14.4.2 2009/05/04 08:12:52 yamt 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.14.4.2 2009/05/04 08:12:52 yamt 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 
     91 #include <dev/mii/igphyreg.h>
     92 
     93 struct igphy_softc {
     94 	struct mii_softc sc_mii;
     95 	int sc_smartspeed;
     96 };
     97 
     98 static void igphy_reset(struct mii_softc *);
     99 static void igphy_load_dspcode(struct mii_softc *);
    100 static void igphy_smartspeed_workaround(struct mii_softc *sc);
    101 
    102 static int	igphymatch(device_t, cfdata_t, void *);
    103 static void	igphyattach(device_t, device_t, void *);
    104 
    105 CFATTACH_DECL_NEW(igphy, sizeof(struct igphy_softc),
    106     igphymatch, igphyattach, mii_phy_detach, mii_phy_activate);
    107 
    108 static int	igphy_service(struct mii_softc *, struct mii_data *, int);
    109 static void	igphy_status(struct mii_softc *);
    110 
    111 static const struct mii_phy_funcs igphy_funcs = {
    112 	igphy_service, igphy_status, igphy_reset,
    113 };
    114 
    115 static const struct mii_phydesc igphys[] = {
    116 	{ MII_OUI_yyINTEL,		MII_MODEL_yyINTEL_IGP01E1000,
    117 	  MII_STR_yyINTEL_IGP01E1000 },
    118 
    119 	{ MII_OUI_yyINTEL,		MII_MODEL_yyINTEL_I82566,
    120 	  MII_STR_yyINTEL_I82566 },
    121 
    122 	{0,				0,
    123 	 NULL },
    124 };
    125 
    126 static int
    127 igphymatch(device_t parent, cfdata_t match, void *aux)
    128 {
    129 	struct mii_attach_args *ma = aux;
    130 
    131 	if (mii_phy_match(ma, igphys) != NULL)
    132 		return 10;
    133 
    134 	return 0;
    135 }
    136 
    137 static void
    138 igphyattach(device_t parent, device_t self, void *aux)
    139 {
    140 	struct mii_softc *sc = device_private(self);
    141 	struct mii_attach_args *ma = aux;
    142 	struct mii_data *mii = ma->mii_data;
    143 	const struct mii_phydesc *mpd;
    144 
    145 	mpd = mii_phy_match(ma, igphys);
    146 	aprint_naive(": Media interface\n");
    147 	aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
    148 
    149 	sc->mii_dev = self;
    150 	sc->mii_inst = mii->mii_instance;
    151 	sc->mii_phy = ma->mii_phyno;
    152 	sc->mii_funcs = &igphy_funcs;
    153 	sc->mii_pdata = mii;
    154 	sc->mii_flags = ma->mii_flags;
    155 	sc->mii_anegticks = MII_ANEGTICKS_GIGE;
    156 
    157 	PHY_RESET(sc);
    158 
    159 	sc->mii_capabilities =
    160 	    PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
    161 	if (sc->mii_capabilities & BMSR_EXTSTAT)
    162 		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
    163 	aprint_normal_dev(self, "");
    164 	if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 &&
    165 	    (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0)
    166 		aprint_error("no media present");
    167 	else
    168 		mii_phy_add_media(sc);
    169 	aprint_normal("\n");
    170 }
    171 
    172 static void
    173 igphy_load_dspcode(struct mii_softc *sc)
    174 {
    175 	static const struct {
    176 		int reg;
    177 		uint16_t val;
    178 	} dspcode[] = {
    179 		{ 0x1f95, 0x0001 },
    180 		{ 0x1f71, 0xbd21 },
    181 		{ 0x1f79, 0x0018 },
    182 		{ 0x1f30, 0x1600 },
    183 		{ 0x1f31, 0x0014 },
    184 		{ 0x1f32, 0x161c },
    185 		{ 0x1f94, 0x0003 },
    186 		{ 0x1f96, 0x003f },
    187 		{ 0x2010, 0x0008 },
    188 		{ 0, 0 },
    189 	};
    190 	int i;
    191 
    192 	delay(10);
    193 
    194 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT, 0x0000);
    195 	PHY_WRITE(sc, 0x0000, 0x0140);
    196 
    197 	delay(5);
    198 
    199 	for (i = 0; dspcode[i].reg != 0; i++)
    200 		IGPHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
    201 
    202 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT,0x0000);
    203 	PHY_WRITE(sc, 0x0000, 0x3300);
    204 }
    205 
    206 static void
    207 igphy_reset(struct mii_softc *sc)
    208 {
    209 	uint16_t fused, fine, coarse;
    210 
    211 	mii_phy_reset(sc);
    212 	igphy_load_dspcode(sc);
    213 
    214 	fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_SPARE_FUSE_STATUS);
    215 	if ((fused & ANALOG_SPARE_FUSE_ENABLED) == 0) {
    216 		fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_FUSE_STATUS);
    217 
    218 		fine = fused & ANALOG_FUSE_FINE_MASK;
    219 		coarse = fused & ANALOG_FUSE_COARSE_MASK;
    220 
    221 		if (coarse > ANALOG_FUSE_COARSE_THRESH) {
    222 			coarse -= ANALOG_FUSE_COARSE_10;
    223 			fine -= ANALOG_FUSE_FINE_1;
    224 		} else if (coarse == ANALOG_FUSE_COARSE_THRESH)
    225 			fine -= ANALOG_FUSE_FINE_10;
    226 
    227 		fused = (fused & ANALOG_FUSE_POLY_MASK) |
    228 			(fine & ANALOG_FUSE_FINE_MASK) |
    229 			(coarse & ANALOG_FUSE_COARSE_MASK);
    230 
    231 		IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_CONTROL, fused);
    232 		IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_BYPASS,
    233 		    ANALOG_FUSE_ENABLE_SW_CONTROL);
    234 	}
    235 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT,0x0000);
    236 }
    237 
    238 
    239 static int
    240 igphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
    241 {
    242 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
    243 	uint16_t reg;
    244 
    245 	switch (cmd) {
    246 	case MII_POLLSTAT:
    247 		/*
    248 		 * If we're not polling our PHY instance, just return.
    249 		 */
    250 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
    251 			return (0);
    252 		break;
    253 
    254 	case MII_MEDIACHG:
    255 		/*
    256 		 * If the media indicates a different PHY instance,
    257 		 * isolate ourselves.
    258 		 */
    259 		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
    260 			reg = PHY_READ(sc, MII_BMCR);
    261 			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
    262 			return (0);
    263 		}
    264 
    265 		/*
    266 		 * If the interface is not up, don't do anything.
    267 		 */
    268 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
    269 			break;
    270 
    271 		reg = PHY_READ(sc, MII_IGPHY_PORT_CTRL);
    272 		if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
    273 			reg |= PSCR_AUTO_MDIX;
    274 			reg &= ~PSCR_FORCE_MDI_MDIX;
    275 			PHY_WRITE(sc, MII_IGPHY_PORT_CTRL, reg);
    276 		} else {
    277 			reg &= ~(PSCR_AUTO_MDIX | PSCR_FORCE_MDI_MDIX);
    278 			PHY_WRITE(sc, MII_IGPHY_PORT_CTRL, reg);
    279 		}
    280 
    281 		mii_phy_setmedia(sc);
    282 		break;
    283 
    284 	case MII_TICK:
    285 		/*
    286 		 * If we're not currently selected, just return.
    287 		 */
    288 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
    289 			return (0);
    290 
    291 		igphy_smartspeed_workaround(sc);
    292 
    293 		if (mii_phy_tick(sc) == EJUSTRETURN)
    294 			return (0);
    295 		break;
    296 
    297 	case MII_DOWN:
    298 		mii_phy_down(sc);
    299 		return (0);
    300 	}
    301 
    302 	/* Update the media status. */
    303 	mii_phy_status(sc);
    304 
    305 	/* Callback if something changed. */
    306 	mii_phy_update(sc, cmd);
    307 	return (0);
    308 }
    309 
    310 
    311 static void
    312 igphy_status(struct mii_softc *sc)
    313 {
    314 	struct mii_data *mii = sc->mii_pdata;
    315 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
    316 	uint16_t bmcr, pssr, gtsr, bmsr;
    317 
    318 	mii->mii_media_status = IFM_AVALID;
    319 	mii->mii_media_active = IFM_ETHER;
    320 
    321 	pssr = PHY_READ(sc, MII_IGPHY_PORT_STATUS);
    322 
    323 	if (pssr & PSSR_LINK_UP)
    324 		mii->mii_media_status |= IFM_ACTIVE;
    325 
    326 	bmcr = PHY_READ(sc, MII_BMCR);
    327 	if (bmcr & BMCR_ISO) {
    328 		mii->mii_media_active |= IFM_NONE;
    329 		mii->mii_media_status = 0;
    330 		return;
    331 	}
    332 
    333 	if (bmcr & BMCR_LOOP)
    334 		mii->mii_media_active |= IFM_LOOP;
    335 
    336 	bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
    337 
    338 	/*
    339 	 * XXX can't check if the info is valid, no
    340 	 * 'negotiation done' bit?
    341 	 */
    342 	if (bmcr & BMCR_AUTOEN) {
    343 		if ((bmsr & BMSR_ACOMP) == 0) {
    344 			mii->mii_media_active |= IFM_NONE;
    345 			return;
    346 		}
    347 		switch (pssr & PSSR_SPEED_MASK) {
    348 		case PSSR_SPEED_1000MBPS:
    349 			mii->mii_media_active |= IFM_1000_T;
    350 			gtsr = PHY_READ(sc, MII_100T2SR);
    351 			if (gtsr & GTSR_MS_RES)
    352 				mii->mii_media_active |= IFM_ETH_MASTER;
    353 			break;
    354 
    355 		case PSSR_SPEED_100MBPS:
    356 			mii->mii_media_active |= IFM_100_TX;
    357 			break;
    358 
    359 		case PSSR_SPEED_10MBPS:
    360 			mii->mii_media_active |= IFM_10_T;
    361 			break;
    362 
    363 		default:
    364 			mii->mii_media_active |= IFM_NONE;
    365 			mii->mii_media_status = 0;
    366 			return;
    367 		}
    368 
    369 		if (pssr & PSSR_FULL_DUPLEX)
    370 			mii->mii_media_active |=
    371 			    IFM_FDX | mii_phy_flowstatus(sc);
    372 	} else
    373 		mii->mii_media_active = ife->ifm_media;
    374 }
    375 
    376 static void
    377 igphy_smartspeed_workaround(struct mii_softc *sc)
    378 {
    379 	struct igphy_softc *igsc = (struct igphy_softc *) sc;
    380 	uint16_t reg, gtsr, gtcr;
    381 
    382 	if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0)
    383 		return;
    384 
    385 	/* XXX Assume 1000TX-FDX is advertized if doing autonegotiation. */
    386 
    387 	reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
    388 	if ((reg & BMSR_LINK) == 0) {
    389 		switch (igsc->sc_smartspeed) {
    390 		case 0:
    391 			gtsr = PHY_READ(sc, MII_100T2SR);
    392 			if (!(gtsr & GTSR_MAN_MS_FLT))
    393 				break;
    394 			gtsr = PHY_READ(sc, MII_100T2SR);
    395 			if (gtsr & GTSR_MAN_MS_FLT) {
    396 				gtcr = PHY_READ(sc, MII_100T2CR);
    397 				if (gtcr & GTCR_MAN_MS) {
    398 					gtcr &= ~GTCR_MAN_MS;
    399 					PHY_WRITE(sc, MII_100T2CR,
    400 					    gtcr);
    401 				}
    402 				mii_phy_auto(sc, 0);
    403 			}
    404 			break;
    405 		case IGPHY_TICK_DOWNSHIFT:
    406 			gtcr = PHY_READ(sc, MII_100T2CR);
    407 			gtcr |= GTCR_MAN_MS;
    408 			PHY_WRITE(sc, MII_100T2CR, gtcr);
    409 			mii_phy_auto(sc, 0);
    410 			break;
    411 		default:
    412 			break;
    413 		}
    414 		if (igsc->sc_smartspeed++ == IGPHY_TICK_MAX)
    415 			igsc->sc_smartspeed = 0;
    416 	} else
    417 		igsc->sc_smartspeed = 0;
    418 }
    419