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