Home | History | Annotate | Line # | Download | only in mii
igphy.c revision 1.16
      1 /*	$NetBSD: igphy.c,v 1.16 2008/05/04 17:06:09 xtraeme 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.16 2008/05/04 17:06:09 xtraeme 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 	if (!pmf_device_register(self, NULL, mii_phy_resume))
    172 		aprint_error_dev(self, "couldn't establish power handler\n");
    173 }
    174 
    175 static void
    176 igphy_load_dspcode(struct mii_softc *sc)
    177 {
    178 	static const struct {
    179 		int reg;
    180 		uint16_t val;
    181 	} dspcode[] = {
    182 		{ 0x1f95, 0x0001 },
    183 		{ 0x1f71, 0xbd21 },
    184 		{ 0x1f79, 0x0018 },
    185 		{ 0x1f30, 0x1600 },
    186 		{ 0x1f31, 0x0014 },
    187 		{ 0x1f32, 0x161c },
    188 		{ 0x1f94, 0x0003 },
    189 		{ 0x1f96, 0x003f },
    190 		{ 0x2010, 0x0008 },
    191 		{ 0, 0 },
    192 	};
    193 	int i;
    194 
    195 	delay(10);
    196 
    197 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT, 0x0000);
    198 	PHY_WRITE(sc, 0x0000, 0x0140);
    199 
    200 	delay(5);
    201 
    202 	for (i = 0; dspcode[i].reg != 0; i++)
    203 		IGPHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
    204 
    205 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT,0x0000);
    206 	PHY_WRITE(sc, 0x0000, 0x3300);
    207 }
    208 
    209 static void
    210 igphy_reset(struct mii_softc *sc)
    211 {
    212 	uint16_t fused, fine, coarse;
    213 
    214 	mii_phy_reset(sc);
    215 	igphy_load_dspcode(sc);
    216 
    217 	fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_SPARE_FUSE_STATUS);
    218 	if ((fused & ANALOG_SPARE_FUSE_ENABLED) == 0) {
    219 		fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_FUSE_STATUS);
    220 
    221 		fine = fused & ANALOG_FUSE_FINE_MASK;
    222 		coarse = fused & ANALOG_FUSE_COARSE_MASK;
    223 
    224 		if (coarse > ANALOG_FUSE_COARSE_THRESH) {
    225 			coarse -= ANALOG_FUSE_COARSE_10;
    226 			fine -= ANALOG_FUSE_FINE_1;
    227 		} else if (coarse == ANALOG_FUSE_COARSE_THRESH)
    228 			fine -= ANALOG_FUSE_FINE_10;
    229 
    230 		fused = (fused & ANALOG_FUSE_POLY_MASK) |
    231 			(fine & ANALOG_FUSE_FINE_MASK) |
    232 			(coarse & ANALOG_FUSE_COARSE_MASK);
    233 
    234 		IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_CONTROL, fused);
    235 		IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_BYPASS,
    236 		    ANALOG_FUSE_ENABLE_SW_CONTROL);
    237 	}
    238 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT,0x0000);
    239 }
    240 
    241 
    242 static int
    243 igphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
    244 {
    245 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
    246 	uint16_t reg;
    247 
    248 	switch (cmd) {
    249 	case MII_POLLSTAT:
    250 		/*
    251 		 * If we're not polling our PHY instance, just return.
    252 		 */
    253 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
    254 			return (0);
    255 		break;
    256 
    257 	case MII_MEDIACHG:
    258 		/*
    259 		 * If the media indicates a different PHY instance,
    260 		 * isolate ourselves.
    261 		 */
    262 		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
    263 			reg = PHY_READ(sc, MII_BMCR);
    264 			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
    265 			return (0);
    266 		}
    267 
    268 		/*
    269 		 * If the interface is not up, don't do anything.
    270 		 */
    271 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
    272 			break;
    273 
    274 		reg = PHY_READ(sc, MII_IGPHY_PORT_CTRL);
    275 		if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
    276 			reg |= PSCR_AUTO_MDIX;
    277 			reg &= ~PSCR_FORCE_MDI_MDIX;
    278 			PHY_WRITE(sc, MII_IGPHY_PORT_CTRL, reg);
    279 		} else {
    280 			reg &= ~(PSCR_AUTO_MDIX | PSCR_FORCE_MDI_MDIX);
    281 			PHY_WRITE(sc, MII_IGPHY_PORT_CTRL, reg);
    282 		}
    283 
    284 		mii_phy_setmedia(sc);
    285 		break;
    286 
    287 	case MII_TICK:
    288 		/*
    289 		 * If we're not currently selected, just return.
    290 		 */
    291 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
    292 			return (0);
    293 
    294 		igphy_smartspeed_workaround(sc);
    295 
    296 		if (mii_phy_tick(sc) == EJUSTRETURN)
    297 			return (0);
    298 		break;
    299 
    300 	case MII_DOWN:
    301 		mii_phy_down(sc);
    302 		return (0);
    303 	}
    304 
    305 	/* Update the media status. */
    306 	mii_phy_status(sc);
    307 
    308 	/* Callback if something changed. */
    309 	mii_phy_update(sc, cmd);
    310 	return (0);
    311 }
    312 
    313 
    314 static void
    315 igphy_status(struct mii_softc *sc)
    316 {
    317 	struct mii_data *mii = sc->mii_pdata;
    318 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
    319 	uint16_t bmcr, pssr, gtsr, bmsr;
    320 
    321 	mii->mii_media_status = IFM_AVALID;
    322 	mii->mii_media_active = IFM_ETHER;
    323 
    324 	pssr = PHY_READ(sc, MII_IGPHY_PORT_STATUS);
    325 
    326 	if (pssr & PSSR_LINK_UP)
    327 		mii->mii_media_status |= IFM_ACTIVE;
    328 
    329 	bmcr = PHY_READ(sc, MII_BMCR);
    330 	if (bmcr & BMCR_ISO) {
    331 		mii->mii_media_active |= IFM_NONE;
    332 		mii->mii_media_status = 0;
    333 		return;
    334 	}
    335 
    336 	if (bmcr & BMCR_LOOP)
    337 		mii->mii_media_active |= IFM_LOOP;
    338 
    339 	bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
    340 
    341 	/*
    342 	 * XXX can't check if the info is valid, no
    343 	 * 'negotiation done' bit?
    344 	 */
    345 	if (bmcr & BMCR_AUTOEN) {
    346 		if ((bmsr & BMSR_ACOMP) == 0) {
    347 			mii->mii_media_active |= IFM_NONE;
    348 			return;
    349 		}
    350 		switch (pssr & PSSR_SPEED_MASK) {
    351 		case PSSR_SPEED_1000MBPS:
    352 			mii->mii_media_active |= IFM_1000_T;
    353 			gtsr = PHY_READ(sc, MII_100T2SR);
    354 			if (gtsr & GTSR_MS_RES)
    355 				mii->mii_media_active |= IFM_ETH_MASTER;
    356 			break;
    357 
    358 		case PSSR_SPEED_100MBPS:
    359 			mii->mii_media_active |= IFM_100_TX;
    360 			break;
    361 
    362 		case PSSR_SPEED_10MBPS:
    363 			mii->mii_media_active |= IFM_10_T;
    364 			break;
    365 
    366 		default:
    367 			mii->mii_media_active |= IFM_NONE;
    368 			mii->mii_media_status = 0;
    369 			return;
    370 		}
    371 
    372 		if (pssr & PSSR_FULL_DUPLEX)
    373 			mii->mii_media_active |=
    374 			    IFM_FDX | mii_phy_flowstatus(sc);
    375 	} else
    376 		mii->mii_media_active = ife->ifm_media;
    377 }
    378 
    379 static void
    380 igphy_smartspeed_workaround(struct mii_softc *sc)
    381 {
    382 	struct igphy_softc *igsc = (struct igphy_softc *) sc;
    383 	uint16_t reg, gtsr, gtcr;
    384 
    385 	if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0)
    386 		return;
    387 
    388 	/* XXX Assume 1000TX-FDX is advertized if doing autonegotiation. */
    389 
    390 	reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
    391 	if ((reg & BMSR_LINK) == 0) {
    392 		switch (igsc->sc_smartspeed) {
    393 		case 0:
    394 			gtsr = PHY_READ(sc, MII_100T2SR);
    395 			if (!(gtsr & GTSR_MAN_MS_FLT))
    396 				break;
    397 			gtsr = PHY_READ(sc, MII_100T2SR);
    398 			if (gtsr & GTSR_MAN_MS_FLT) {
    399 				gtcr = PHY_READ(sc, MII_100T2CR);
    400 				if (gtcr & GTCR_MAN_MS) {
    401 					gtcr &= ~GTCR_MAN_MS;
    402 					PHY_WRITE(sc, MII_100T2CR,
    403 					    gtcr);
    404 				}
    405 				mii_phy_auto(sc, 0);
    406 			}
    407 			break;
    408 		case IGPHY_TICK_DOWNSHIFT:
    409 			gtcr = PHY_READ(sc, MII_100T2CR);
    410 			gtcr |= GTCR_MAN_MS;
    411 			PHY_WRITE(sc, MII_100T2CR, gtcr);
    412 			mii_phy_auto(sc, 0);
    413 			break;
    414 		default:
    415 			break;
    416 		}
    417 		if (igsc->sc_smartspeed++ == IGPHY_TICK_MAX)
    418 			igsc->sc_smartspeed = 0;
    419 	} else
    420 		igsc->sc_smartspeed = 0;
    421 }
    422