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