Home | History | Annotate | Line # | Download | only in mii
igphy.c revision 1.5.10.1
      1 /*	$NetBSD: igphy.c,v 1.5.10.1 2007/03/31 15:25:35 bouyer 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.5.10.1 2007/03/31 15:25:35 bouyer 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 	{0,				0,
    127 	 NULL },
    128 };
    129 
    130 static int
    131 igphymatch(struct device *parent, struct cfdata *match, void *aux)
    132 {
    133 	struct mii_attach_args *ma = aux;
    134 
    135 	if (mii_phy_match(ma, igphys) != NULL)
    136 		return 10;
    137 
    138 	return 0;
    139 }
    140 
    141 static void
    142 igphyattach(struct device *parent, struct device *self, void *aux)
    143 {
    144 	struct mii_softc *sc = (struct mii_softc *)self;
    145 	struct mii_attach_args *ma = aux;
    146 	struct mii_data *mii = ma->mii_data;
    147 	const struct mii_phydesc *mpd;
    148 
    149 	mpd = mii_phy_match(ma, igphys);
    150 	aprint_naive(": Media interface\n");
    151 	aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
    152 
    153 	sc->mii_inst = mii->mii_instance;
    154 	sc->mii_phy = ma->mii_phyno;
    155 	sc->mii_funcs = &igphy_funcs;
    156 	sc->mii_pdata = mii;
    157 	sc->mii_flags = ma->mii_flags;
    158 	sc->mii_anegticks = 10;
    159 
    160 	PHY_RESET(sc);
    161 
    162 	sc->mii_capabilities =
    163 	    PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
    164 	if (sc->mii_capabilities & BMSR_EXTSTAT)
    165 		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
    166 	aprint_normal("%s: ", sc->mii_dev.dv_xname);
    167 	if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 &&
    168 	    (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0)
    169 		aprint_error("no media present");
    170 	else
    171 		mii_phy_add_media(sc);
    172 	aprint_normal("\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