Home | History | Annotate | Line # | Download | only in mii
igphy.c revision 1.1.2.1
      1 /*	$NetBSD: igphy.c,v 1.1.2.1 2005/01/07 11:42:14 jdc 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.1.2.1 2005/01/07 11:42:14 jdc 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 int	igphymatch(struct device *, struct cfdata *, void *);
    110 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 int	igphy_service(struct mii_softc *, struct mii_data *, int);
    116 void	igphy_status(struct mii_softc *);
    117 
    118 const struct mii_phy_funcs igphy_funcs = {
    119 	igphy_service, igphy_status, igphy_reset,
    120 };
    121 
    122 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 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 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 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 		mii_phy_setmedia(sc);
    275 		break;
    276 
    277 	case MII_TICK:
    278 		/*
    279 		 * If we're not currently selected, just return.
    280 		 */
    281 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
    282 			return (0);
    283 
    284 		igphy_smartspeed_workaround(sc);
    285 
    286 		if (mii_phy_tick(sc) == EJUSTRETURN)
    287 			return (0);
    288 		break;
    289 
    290 	case MII_DOWN:
    291 		mii_phy_down(sc);
    292 		return (0);
    293 	}
    294 
    295 	/* Update the media status. */
    296 	mii_phy_status(sc);
    297 
    298 	/* Callback if something changed. */
    299 	mii_phy_update(sc, cmd);
    300 	return (0);
    301 }
    302 
    303 
    304 void
    305 igphy_status(struct mii_softc *sc)
    306 {
    307 	struct mii_data *mii = sc->mii_pdata;
    308 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
    309 	uint16_t bmcr, pssr, gtsr, bmsr;
    310 
    311 	mii->mii_media_status = IFM_AVALID;
    312 	mii->mii_media_active = IFM_ETHER;
    313 
    314 	pssr = PHY_READ(sc, MII_IGPHY_PORT_STATUS);
    315 
    316 	if (pssr & PSSR_LINK_UP)
    317 		mii->mii_media_status |= IFM_ACTIVE;
    318 
    319 	bmcr = PHY_READ(sc, MII_BMCR);
    320 	if (bmcr & BMCR_ISO) {
    321 		mii->mii_media_active |= IFM_NONE;
    322 		mii->mii_media_status = 0;
    323 		return;
    324 	}
    325 
    326 	if (bmcr & BMCR_LOOP)
    327 		mii->mii_media_active |= IFM_LOOP;
    328 
    329 	bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
    330 
    331 	/*
    332 	 * XXX can't check if the info is valid, no
    333 	 * 'negotiation done' bit?
    334 	 */
    335 	if (bmcr & BMCR_AUTOEN) {
    336 		if ((bmsr & BMSR_ACOMP) == 0) {
    337 			mii->mii_media_active |= IFM_NONE;
    338 			return;
    339 		}
    340 		switch (pssr & PSSR_SPEED_MASK) {
    341 		case PSSR_SPEED_1000MBPS:
    342 			mii->mii_media_active |= IFM_1000_T;
    343 			gtsr = PHY_READ(sc, MII_100T2SR);
    344 			if (gtsr & GTSR_MS_RES)
    345 				mii->mii_media_active |= IFM_ETH_MASTER;
    346 			break;
    347 
    348 		case PSSR_SPEED_100MBPS:
    349 			mii->mii_media_active |= IFM_100_TX;
    350 			break;
    351 
    352 		case PSSR_SPEED_10MBPS:
    353 			mii->mii_media_active |= IFM_10_T;
    354 			break;
    355 
    356 		default:
    357 			mii->mii_media_active |= IFM_NONE;
    358 			mii->mii_media_status = 0;
    359 			return;
    360 		}
    361 
    362 		if (pssr & PSSR_FULL_DUPLEX)
    363 			mii->mii_media_active |= IFM_FDX;
    364 	} else
    365 		mii->mii_media_active = ife->ifm_media;
    366 }
    367 
    368 static void
    369 igphy_smartspeed_workaround(struct mii_softc *sc)
    370 {
    371 	struct igphy_softc *igsc = (struct igphy_softc *) sc;
    372 	uint16_t reg, gtsr, gtcr;
    373 
    374 	if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0)
    375 		return;
    376 
    377 	/* XXX Assume 1000TX-FDX is advertized if doing autonegotiation. */
    378 
    379 	reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
    380 	if ((reg & BMSR_LINK) == 0) {
    381 		switch (igsc->sc_smartspeed) {
    382 		case 0:
    383 			gtsr = PHY_READ(sc, MII_100T2SR);
    384 			if (!(gtsr & GTSR_MAN_MS_FLT))
    385 				break;
    386 			gtsr = PHY_READ(sc, MII_100T2SR);
    387 			if (gtsr & GTSR_MAN_MS_FLT) {
    388 				gtcr = PHY_READ(sc, MII_100T2CR);
    389 				if (gtcr & GTCR_MAN_MS) {
    390 					gtcr &= ~GTCR_MAN_MS;
    391 					PHY_WRITE(sc, MII_100T2CR,
    392 					    gtcr);
    393 				}
    394 				mii_phy_auto(sc, 0);
    395 			}
    396 			break;
    397 		case IGPHY_TICK_DOWNSHIFT:
    398 			gtcr = PHY_READ(sc, MII_100T2CR);
    399 			gtcr |= GTCR_MAN_MS;
    400 			PHY_WRITE(sc, MII_100T2CR, gtcr);
    401 			mii_phy_auto(sc, 0);
    402 			break;
    403 		default:
    404 			break;
    405 		}
    406 		if (igsc->sc_smartspeed++ == IGPHY_TICK_MAX)
    407 			igsc->sc_smartspeed = 0;
    408 	} else
    409 		igsc->sc_smartspeed = 0;
    410 }
    411