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