xref: /netbsd/sys/dev/mii/igphy.c (revision 4dff0d0b)
1*4dff0d0bSmsaitoh /*	$NetBSD: igphy.c,v 1.37 2020/11/04 09:15:10 msaitoh Exp $	*/
244954feaSfvdl 
344954feaSfvdl /*
444954feaSfvdl  * The Intel copyright applies to the analog register setup, and the
5f3909175Smsaitoh  * SmartSpeed workaround code.
644954feaSfvdl  */
744954feaSfvdl 
844954feaSfvdl /*******************************************************************************
944954feaSfvdl 
1044954feaSfvdl   Copyright (c) 2001-2003, Intel Corporation
1144954feaSfvdl   All rights reserved.
1244954feaSfvdl 
1344954feaSfvdl   Redistribution and use in source and binary forms, with or without
1444954feaSfvdl   modification, are permitted provided that the following conditions are met:
1544954feaSfvdl 
1644954feaSfvdl    1. Redistributions of source code must retain the above copyright notice,
1744954feaSfvdl       this list of conditions and the following disclaimer.
1844954feaSfvdl 
1944954feaSfvdl    2. Redistributions in binary form must reproduce the above copyright
2044954feaSfvdl       notice, this list of conditions and the following disclaimer in the
2144954feaSfvdl       documentation and/or other materials provided with the distribution.
2244954feaSfvdl 
2344954feaSfvdl    3. Neither the name of the Intel Corporation nor the names of its
2444954feaSfvdl       contributors may be used to endorse or promote products derived from
2544954feaSfvdl       this software without specific prior written permission.
2644954feaSfvdl 
2744954feaSfvdl   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2844954feaSfvdl   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2944954feaSfvdl   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3044954feaSfvdl   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
3144954feaSfvdl   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
3244954feaSfvdl   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3344954feaSfvdl   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3444954feaSfvdl   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3544954feaSfvdl   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3644954feaSfvdl   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3744954feaSfvdl   POSSIBILITY OF SUCH DAMAGE.
3844954feaSfvdl 
3944954feaSfvdl *******************************************************************************/
4044954feaSfvdl 
4144954feaSfvdl 
4244954feaSfvdl /*-
4344954feaSfvdl  * Copyright (c) 1998, 1999, 2000, 2003 The NetBSD Foundation, Inc.
4444954feaSfvdl  * All rights reserved.
4544954feaSfvdl  *
4644954feaSfvdl  * This code is derived from software contributed to The NetBSD Foundation
4744954feaSfvdl  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
4844954feaSfvdl  * NASA Ames Research Center, and by Frank van der Linden.
4944954feaSfvdl  *
5044954feaSfvdl  * Redistribution and use in source and binary forms, with or without
5144954feaSfvdl  * modification, are permitted provided that the following conditions
5244954feaSfvdl  * are met:
5344954feaSfvdl  * 1. Redistributions of source code must retain the above copyright
5444954feaSfvdl  *    notice, this list of conditions and the following disclaimer.
5544954feaSfvdl  * 2. Redistributions in binary form must reproduce the above copyright
5644954feaSfvdl  *    notice, this list of conditions and the following disclaimer in the
5744954feaSfvdl  *    documentation and/or other materials provided with the distribution.
5844954feaSfvdl  *
5944954feaSfvdl  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
6044954feaSfvdl  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
6144954feaSfvdl  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
6244954feaSfvdl  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
6344954feaSfvdl  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
6444954feaSfvdl  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
6544954feaSfvdl  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
6644954feaSfvdl  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
6744954feaSfvdl  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
6844954feaSfvdl  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
6944954feaSfvdl  * POSSIBILITY OF SUCH DAMAGE.
7044954feaSfvdl  */
7144954feaSfvdl 
7244954feaSfvdl #include <sys/cdefs.h>
73*4dff0d0bSmsaitoh __KERNEL_RCSID(0, "$NetBSD: igphy.c,v 1.37 2020/11/04 09:15:10 msaitoh Exp $");
7444954feaSfvdl 
75d304a501Spooka #ifdef _KERNEL_OPT
7644954feaSfvdl #include "opt_mii.h"
77d304a501Spooka #endif
7844954feaSfvdl 
7944954feaSfvdl #include <sys/param.h>
8044954feaSfvdl #include <sys/systm.h>
8144954feaSfvdl #include <sys/kernel.h>
8244954feaSfvdl #include <sys/device.h>
8344954feaSfvdl #include <sys/socket.h>
8444954feaSfvdl #include <sys/errno.h>
8544954feaSfvdl 
8644954feaSfvdl #include <net/if.h>
8744954feaSfvdl #include <net/if_media.h>
8844954feaSfvdl 
8944954feaSfvdl #include <dev/mii/mii.h>
9044954feaSfvdl #include <dev/mii/miivar.h>
9144954feaSfvdl #include <dev/mii/miidevs.h>
9244954feaSfvdl #include <dev/mii/igphyreg.h>
93c70d45b7Smsaitoh #include <dev/mii/igphyvar.h>
94f03b8365Smsaitoh #include <dev/pci/if_wmvar.h>
9544954feaSfvdl 
9644954feaSfvdl static void igphy_reset(struct mii_softc *);
9744954feaSfvdl static void igphy_load_dspcode(struct mii_softc *);
9805b467b5Smsaitoh static void igphy_load_dspcode_igp3(struct mii_softc *);
9944954feaSfvdl static void igphy_smartspeed_workaround(struct mii_softc *sc);
10044954feaSfvdl 
1017db0e577Sxtraeme static int	igphymatch(device_t, cfdata_t, void *);
1027db0e577Sxtraeme static void	igphyattach(device_t, device_t, void *);
10344954feaSfvdl 
1047db0e577Sxtraeme CFATTACH_DECL_NEW(igphy, sizeof(struct igphy_softc),
10544954feaSfvdl     igphymatch, igphyattach, mii_phy_detach, mii_phy_activate);
10644954feaSfvdl 
1071efb3da0Sthorpej static int	igphy_service(struct mii_softc *, struct mii_data *, int);
1081efb3da0Sthorpej static void	igphy_status(struct mii_softc *);
10944954feaSfvdl 
1101efb3da0Sthorpej static const struct mii_phy_funcs igphy_funcs = {
11144954feaSfvdl 	igphy_service, igphy_status, igphy_reset,
11244954feaSfvdl };
11344954feaSfvdl 
1141efb3da0Sthorpej static const struct mii_phydesc igphys[] = {
115d23de6ffSchristos 	MII_PHY_DESC(yyINTEL, IGP01E1000),
116d23de6ffSchristos 	MII_PHY_DESC(yyINTEL, I82566),
117d23de6ffSchristos 	MII_PHY_END,
11844954feaSfvdl };
11944954feaSfvdl 
1201efb3da0Sthorpej static int
igphymatch(device_t parent,cfdata_t match,void * aux)1217db0e577Sxtraeme igphymatch(device_t parent, cfdata_t match, void *aux)
12244954feaSfvdl {
12344954feaSfvdl 	struct mii_attach_args *ma = aux;
12444954feaSfvdl 
12544954feaSfvdl 	if (mii_phy_match(ma, igphys) != NULL)
12644954feaSfvdl 		return 10;
12744954feaSfvdl 
12844954feaSfvdl 	return 0;
12944954feaSfvdl }
13044954feaSfvdl 
1311efb3da0Sthorpej static void
igphyattach(device_t parent,device_t self,void * aux)1327db0e577Sxtraeme igphyattach(device_t parent, device_t self, void *aux)
13344954feaSfvdl {
134838ee1e0Sthorpej 	struct mii_softc *sc = device_private(self);
13544954feaSfvdl 	struct mii_attach_args *ma = aux;
13644954feaSfvdl 	struct mii_data *mii = ma->mii_data;
13744954feaSfvdl 	const struct mii_phydesc *mpd;
138f03b8365Smsaitoh 	struct igphy_softc *igsc = (struct igphy_softc *)sc;
139f03b8365Smsaitoh 	prop_dictionary_t dict;
14044954feaSfvdl 
14144954feaSfvdl 	mpd = mii_phy_match(ma, igphys);
14244954feaSfvdl 	aprint_naive(": Media interface\n");
14344954feaSfvdl 	aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
14444954feaSfvdl 
145f03b8365Smsaitoh 	dict = device_properties(parent);
146f03b8365Smsaitoh 	if (!prop_dictionary_get_uint32(dict, "mactype", &igsc->sc_mactype))
147f03b8365Smsaitoh 		aprint_error("WARNING! Failed to get mactype\n");
14805b467b5Smsaitoh 	if (!prop_dictionary_get_uint32(dict, "macflags", &igsc->sc_macflags))
14905b467b5Smsaitoh 		aprint_error("WARNING! Failed to get macflags\n");
150f03b8365Smsaitoh 
1517db0e577Sxtraeme 	sc->mii_dev = self;
15244954feaSfvdl 	sc->mii_inst = mii->mii_instance;
15344954feaSfvdl 	sc->mii_phy = ma->mii_phyno;
15448297c12Smsaitoh 	sc->mii_mpd_oui = MII_OUI(ma->mii_id1, ma->mii_id2);
15548297c12Smsaitoh 	sc->mii_mpd_model = MII_MODEL(ma->mii_id2);
15648297c12Smsaitoh 	sc->mii_mpd_rev = MII_REV(ma->mii_id2);
15744954feaSfvdl 	sc->mii_funcs = &igphy_funcs;
15844954feaSfvdl 	sc->mii_pdata = mii;
15944954feaSfvdl 	sc->mii_flags = ma->mii_flags;
16044954feaSfvdl 
16161b37dcbSthorpej 	mii_lock(mii);
16261b37dcbSthorpej 
16344954feaSfvdl 	PHY_RESET(sc);
16444954feaSfvdl 
165e746222fSmsaitoh 	PHY_READ(sc, MII_BMSR, &sc->mii_capabilities);
166e746222fSmsaitoh 	sc->mii_capabilities &= ma->mii_capmask;
16744954feaSfvdl 	if (sc->mii_capabilities & BMSR_EXTSTAT)
168e746222fSmsaitoh 		PHY_READ(sc, MII_EXTSR, &sc->mii_extcapabilities);
169b2a008adSmsaitoh 
17061b37dcbSthorpej 	mii_unlock(mii);
17161b37dcbSthorpej 
17244954feaSfvdl 	mii_phy_add_media(sc);
17344954feaSfvdl }
17444954feaSfvdl 
17505b467b5Smsaitoh typedef struct {
17644954feaSfvdl 	int reg;
17744954feaSfvdl 	uint16_t val;
17805b467b5Smsaitoh } dspcode;
17905b467b5Smsaitoh 
18005b467b5Smsaitoh static const dspcode igp1code[] = {
18144954feaSfvdl 	{ 0x1f95, 0x0001 },
18244954feaSfvdl 	{ 0x1f71, 0xbd21 },
18344954feaSfvdl 	{ 0x1f79, 0x0018 },
18444954feaSfvdl 	{ 0x1f30, 0x1600 },
18544954feaSfvdl 	{ 0x1f31, 0x0014 },
18644954feaSfvdl 	{ 0x1f32, 0x161c },
18744954feaSfvdl 	{ 0x1f94, 0x0003 },
18844954feaSfvdl 	{ 0x1f96, 0x003f },
18944954feaSfvdl 	{ 0x2010, 0x0008 },
19044954feaSfvdl 	{ 0, 0 },
19144954feaSfvdl };
19205b467b5Smsaitoh 
19305b467b5Smsaitoh static const dspcode igp1code_r2[] = {
19405b467b5Smsaitoh 	{ 0x1f73, 0x0099 },
19505b467b5Smsaitoh 	{ 0, 0 },
19605b467b5Smsaitoh };
19705b467b5Smsaitoh 
19805b467b5Smsaitoh static const dspcode igp3code[] = {
19905b467b5Smsaitoh 	{ 0x2f5b, 0x9018},
20005b467b5Smsaitoh 	{ 0x2f52, 0x0000},
20105b467b5Smsaitoh 	{ 0x2fb1, 0x8b24},
20205b467b5Smsaitoh 	{ 0x2fb2, 0xf8f0},
20305b467b5Smsaitoh 	{ 0x2010, 0x10b0},
20405b467b5Smsaitoh 	{ 0x2011, 0x0000},
20505b467b5Smsaitoh 	{ 0x20dd, 0x249a},
20605b467b5Smsaitoh 	{ 0x20de, 0x00d3},
20705b467b5Smsaitoh 	{ 0x28b4, 0x04ce},
20805b467b5Smsaitoh 	{ 0x2f70, 0x29e4},
20905b467b5Smsaitoh 	{ 0x0000, 0x0140},
21005b467b5Smsaitoh 	{ 0x1f30, 0x1606},
21105b467b5Smsaitoh 	{ 0x1f31, 0xb814},
21205b467b5Smsaitoh 	{ 0x1f35, 0x002a},
21305b467b5Smsaitoh 	{ 0x1f3e, 0x0067},
21405b467b5Smsaitoh 	{ 0x1f54, 0x0065},
21505b467b5Smsaitoh 	{ 0x1f55, 0x002a},
21605b467b5Smsaitoh 	{ 0x1f56, 0x002a},
21705b467b5Smsaitoh 	{ 0x1f72, 0x3fb0},
21805b467b5Smsaitoh 	{ 0x1f76, 0xc0ff},
21905b467b5Smsaitoh 	{ 0x1f77, 0x1dec},
22005b467b5Smsaitoh 	{ 0x1f78, 0xf9ef},
22105b467b5Smsaitoh 	{ 0x1f79, 0x0210},
22205b467b5Smsaitoh 	{ 0x1895, 0x0003},
22305b467b5Smsaitoh 	{ 0x1796, 0x0008},
22405b467b5Smsaitoh 	{ 0x1798, 0xd008},
22505b467b5Smsaitoh 	{ 0x1898, 0xd918},
22605b467b5Smsaitoh 	{ 0x187a, 0x0800},
22705b467b5Smsaitoh 	{ 0x0019, 0x008d},
22805b467b5Smsaitoh 	{ 0x001b, 0x2080},
22905b467b5Smsaitoh 	{ 0x0014, 0x0045},
23005b467b5Smsaitoh 	{ 0x0000, 0x1340},
23105b467b5Smsaitoh 	{ 0, 0 },
23205b467b5Smsaitoh };
23305b467b5Smsaitoh 
23405b467b5Smsaitoh /* DSP patch for igp1 and igp2 */
23505b467b5Smsaitoh static void
igphy_load_dspcode(struct mii_softc * sc)23605b467b5Smsaitoh igphy_load_dspcode(struct mii_softc *sc)
23705b467b5Smsaitoh {
23805b467b5Smsaitoh 	struct igphy_softc *igsc = (struct igphy_softc *)sc;
23905b467b5Smsaitoh 	const dspcode *code;
24005b467b5Smsaitoh 	uint16_t reg;
24144954feaSfvdl 	int i;
24244954feaSfvdl 
243f03b8365Smsaitoh 	/* This workaround is only for 82541 and 82547 */
244f03b8365Smsaitoh 	switch (igsc->sc_mactype) {
245f03b8365Smsaitoh 	case WM_T_82541:
246f03b8365Smsaitoh 	case WM_T_82547:
24705b467b5Smsaitoh 		code = igp1code;
24805b467b5Smsaitoh 		break;
249f03b8365Smsaitoh 	case WM_T_82541_2:
250f03b8365Smsaitoh 	case WM_T_82547_2:
25105b467b5Smsaitoh 		code = igp1code_r2;
252f03b8365Smsaitoh 		break;
253f03b8365Smsaitoh 	default:
25405b467b5Smsaitoh 		return;	/* byebye */
255f03b8365Smsaitoh 	}
256f03b8365Smsaitoh 
25705b467b5Smsaitoh 	/* Delay after phy reset to enable NVM configuration to load */
25805b467b5Smsaitoh 	delay(20000);
25905b467b5Smsaitoh 
26005b467b5Smsaitoh 	/*
26105b467b5Smsaitoh 	 * Save off the current value of register 0x2F5B to be restored at
26205b467b5Smsaitoh 	 * the end of this routine.
26305b467b5Smsaitoh 	 */
264e746222fSmsaitoh 	IGPHY_READ(sc, 0x2f5b, &reg);
26505b467b5Smsaitoh 
26605b467b5Smsaitoh 	/* Disabled the PHY transmitter */
26705b467b5Smsaitoh 	IGPHY_WRITE(sc, 0x2f5b, 0x0003);
26805b467b5Smsaitoh 
26905b467b5Smsaitoh 	delay(20000);
27044954feaSfvdl 
27172951728Smsaitoh 	PHY_WRITE(sc, IGPHY_PAGE_SELECT, 0x0000);
27244954feaSfvdl 	PHY_WRITE(sc, 0x0000, 0x0140);
27344954feaSfvdl 
27405b467b5Smsaitoh 	delay(5000);
27544954feaSfvdl 
27605b467b5Smsaitoh 	for (i = 0; !((code[i].reg == 0) && (code[i].val == 0)); i++)
27705b467b5Smsaitoh 		IGPHY_WRITE(sc, code[i].reg, code[i].val);
27844954feaSfvdl 
27972951728Smsaitoh 	PHY_WRITE(sc, IGPHY_PAGE_SELECT, 0x0000);
28044954feaSfvdl 	PHY_WRITE(sc, 0x0000, 0x3300);
28105b467b5Smsaitoh 
28205b467b5Smsaitoh 	delay(20000);
28305b467b5Smsaitoh 
28405b467b5Smsaitoh 	/* Now enable the transmitter */
28505b467b5Smsaitoh 	IGPHY_WRITE(sc, 0x2f5b, reg);
28605b467b5Smsaitoh }
28705b467b5Smsaitoh 
28805b467b5Smsaitoh static void
igphy_load_dspcode_igp3(struct mii_softc * sc)28905b467b5Smsaitoh igphy_load_dspcode_igp3(struct mii_softc *sc)
29005b467b5Smsaitoh {
29105b467b5Smsaitoh 	const dspcode *code = igp3code;
29205b467b5Smsaitoh 	int i;
29305b467b5Smsaitoh 
29405b467b5Smsaitoh 	for (i = 0; !((code[i].reg == 0) && (code[i].val == 0)); i++)
29505b467b5Smsaitoh 		IGPHY_WRITE(sc, code[i].reg, code[i].val);
29644954feaSfvdl }
29744954feaSfvdl 
29844954feaSfvdl static void
igphy_reset(struct mii_softc * sc)29944954feaSfvdl igphy_reset(struct mii_softc *sc)
30044954feaSfvdl {
301f03b8365Smsaitoh 	struct igphy_softc *igsc = (struct igphy_softc *)sc;
30244954feaSfvdl 	uint16_t fused, fine, coarse;
30344954feaSfvdl 
30461b37dcbSthorpej 	KASSERT(mii_locked(sc->mii_pdata));
30561b37dcbSthorpej 
30644954feaSfvdl 	mii_phy_reset(sc);
30705b467b5Smsaitoh 	delay(150);
30805b467b5Smsaitoh 
30905b467b5Smsaitoh 	switch (igsc->sc_mactype) {
31005b467b5Smsaitoh 	case WM_T_82541:
31105b467b5Smsaitoh 	case WM_T_82547:
31205b467b5Smsaitoh 	case WM_T_82541_2:
31305b467b5Smsaitoh 	case WM_T_82547_2:
31444954feaSfvdl 		igphy_load_dspcode(sc);
31505b467b5Smsaitoh 		break;
31605b467b5Smsaitoh 	case WM_T_ICH8:
31705b467b5Smsaitoh 	case WM_T_ICH9:
31805b467b5Smsaitoh 		if ((igsc->sc_macflags & WM_F_EEPROM_INVALID) != 0)
31905b467b5Smsaitoh 			igphy_load_dspcode_igp3(sc);
32005b467b5Smsaitoh 		break;
32105b467b5Smsaitoh 	default:	/* Not for ICH10, PCH and 8257[12] */
32205b467b5Smsaitoh 		break;
32305b467b5Smsaitoh 	}
32444954feaSfvdl 
325f03b8365Smsaitoh 	if (igsc->sc_mactype == WM_T_82547) {
32672951728Smsaitoh 		IGPHY_READ(sc, IGPHY_ANALOG_SPARE_FUSE_STATUS, &fused);
32744954feaSfvdl 		if ((fused & ANALOG_SPARE_FUSE_ENABLED) == 0) {
32872951728Smsaitoh 			IGPHY_READ(sc, IGPHY_ANALOG_FUSE_STATUS, &fused);
32944954feaSfvdl 
33044954feaSfvdl 			fine = fused & ANALOG_FUSE_FINE_MASK;
33144954feaSfvdl 			coarse = fused & ANALOG_FUSE_COARSE_MASK;
33244954feaSfvdl 
33344954feaSfvdl 			if (coarse > ANALOG_FUSE_COARSE_THRESH) {
33444954feaSfvdl 				coarse -= ANALOG_FUSE_COARSE_10;
33544954feaSfvdl 				fine -= ANALOG_FUSE_FINE_1;
33644954feaSfvdl 			} else if (coarse == ANALOG_FUSE_COARSE_THRESH)
33744954feaSfvdl 				fine -= ANALOG_FUSE_FINE_10;
33844954feaSfvdl 
33944954feaSfvdl 			fused = (fused & ANALOG_FUSE_POLY_MASK) |
34044954feaSfvdl 			    (fine & ANALOG_FUSE_FINE_MASK) |
34144954feaSfvdl 			    (coarse & ANALOG_FUSE_COARSE_MASK);
34244954feaSfvdl 
34372951728Smsaitoh 			IGPHY_WRITE(sc, IGPHY_ANALOG_FUSE_CONTROL, fused);
34472951728Smsaitoh 			IGPHY_WRITE(sc, IGPHY_ANALOG_FUSE_BYPASS,
34544954feaSfvdl 			    ANALOG_FUSE_ENABLE_SW_CONTROL);
34644954feaSfvdl 		}
347f03b8365Smsaitoh 	}
34872951728Smsaitoh 	PHY_WRITE(sc, IGPHY_PAGE_SELECT, 0x0000);
34944954feaSfvdl }
35044954feaSfvdl 
35144954feaSfvdl 
3521efb3da0Sthorpej static int
igphy_service(struct mii_softc * sc,struct mii_data * mii,int cmd)35344954feaSfvdl igphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
35444954feaSfvdl {
35544954feaSfvdl 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
35644954feaSfvdl 	uint16_t reg;
35744954feaSfvdl 
35861b37dcbSthorpej 	KASSERT(mii_locked(mii));
35961b37dcbSthorpej 
36044954feaSfvdl 	switch (cmd) {
36144954feaSfvdl 	case MII_POLLSTAT:
3629846d3fdSmsaitoh 		/* If we're not polling our PHY instance, just return. */
36344954feaSfvdl 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
3649846d3fdSmsaitoh 			return 0;
36544954feaSfvdl 		break;
36644954feaSfvdl 
36744954feaSfvdl 	case MII_MEDIACHG:
36844954feaSfvdl 		/*
36944954feaSfvdl 		 * If the media indicates a different PHY instance,
37044954feaSfvdl 		 * isolate ourselves.
37144954feaSfvdl 		 */
37244954feaSfvdl 		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
373e746222fSmsaitoh 			PHY_READ(sc, MII_BMCR, &reg);
37444954feaSfvdl 			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
3759846d3fdSmsaitoh 			return 0;
37644954feaSfvdl 		}
37744954feaSfvdl 
3789846d3fdSmsaitoh 		/* If the interface is not up, don't do anything. */
37944954feaSfvdl 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
38044954feaSfvdl 			break;
38144954feaSfvdl 
38272951728Smsaitoh 		PHY_READ(sc, IGPHY_PORT_CTRL, &reg);
383f0a2eb28Smsaitoh 		if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
384f0a2eb28Smsaitoh 			reg |= PSCR_AUTO_MDIX;
385f0a2eb28Smsaitoh 			reg &= ~PSCR_FORCE_MDI_MDIX;
38672951728Smsaitoh 			PHY_WRITE(sc, IGPHY_PORT_CTRL, reg);
387f0a2eb28Smsaitoh 		} else {
388f0a2eb28Smsaitoh 			reg &= ~(PSCR_AUTO_MDIX | PSCR_FORCE_MDI_MDIX);
38972951728Smsaitoh 			PHY_WRITE(sc, IGPHY_PORT_CTRL, reg);
390f0a2eb28Smsaitoh 		}
391f0a2eb28Smsaitoh 
39244954feaSfvdl 		mii_phy_setmedia(sc);
39344954feaSfvdl 		break;
39444954feaSfvdl 
39544954feaSfvdl 	case MII_TICK:
3969846d3fdSmsaitoh 		/* If we're not currently selected, just return. */
39744954feaSfvdl 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
3989846d3fdSmsaitoh 			return 0;
39944954feaSfvdl 
40044954feaSfvdl 		igphy_smartspeed_workaround(sc);
40144954feaSfvdl 
40244954feaSfvdl 		if (mii_phy_tick(sc) == EJUSTRETURN)
4039846d3fdSmsaitoh 			return 0;
40444954feaSfvdl 		break;
40544954feaSfvdl 
40644954feaSfvdl 	case MII_DOWN:
40744954feaSfvdl 		mii_phy_down(sc);
4089846d3fdSmsaitoh 		return 0;
40944954feaSfvdl 	}
41044954feaSfvdl 
41144954feaSfvdl 	/* Update the media status. */
41244954feaSfvdl 	mii_phy_status(sc);
41344954feaSfvdl 
41444954feaSfvdl 	/* Callback if something changed. */
41544954feaSfvdl 	mii_phy_update(sc, cmd);
4169846d3fdSmsaitoh 	return 0;
41744954feaSfvdl }
41844954feaSfvdl 
41944954feaSfvdl 
4201efb3da0Sthorpej static void
igphy_status(struct mii_softc * sc)42144954feaSfvdl igphy_status(struct mii_softc *sc)
42244954feaSfvdl {
42344954feaSfvdl 	struct mii_data *mii = sc->mii_pdata;
42444954feaSfvdl 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
42544954feaSfvdl 	uint16_t bmcr, pssr, gtsr, bmsr;
42644954feaSfvdl 
42761b37dcbSthorpej 	KASSERT(mii_locked(mii));
42861b37dcbSthorpej 
42944954feaSfvdl 	mii->mii_media_status = IFM_AVALID;
43044954feaSfvdl 	mii->mii_media_active = IFM_ETHER;
43144954feaSfvdl 
43272951728Smsaitoh 	PHY_READ(sc, IGPHY_PORT_STATUS, &pssr);
43344954feaSfvdl 
4344bdc7ccdSmsaitoh 	if (pssr & IGPHY_PSSR_LINK_UP)
43544954feaSfvdl 		mii->mii_media_status |= IFM_ACTIVE;
43644954feaSfvdl 
437e746222fSmsaitoh 	PHY_READ(sc, MII_BMCR, &bmcr);
43844954feaSfvdl 	if (bmcr & BMCR_ISO) {
43944954feaSfvdl 		mii->mii_media_active |= IFM_NONE;
44044954feaSfvdl 		return;
44144954feaSfvdl 	}
44244954feaSfvdl 
44344954feaSfvdl 	if (bmcr & BMCR_LOOP)
44444954feaSfvdl 		mii->mii_media_active |= IFM_LOOP;
44544954feaSfvdl 
446e746222fSmsaitoh 	PHY_READ(sc, MII_BMSR, &bmsr);
447e746222fSmsaitoh 	PHY_READ(sc, MII_BMSR, &bmsr);
44844954feaSfvdl 
4499846d3fdSmsaitoh 	/* XXX can't check if the info is valid, no 'negotiation done' bit? */
45044954feaSfvdl 	if (bmcr & BMCR_AUTOEN) {
45144954feaSfvdl 		if ((bmsr & BMSR_ACOMP) == 0) {
45244954feaSfvdl 			mii->mii_media_active |= IFM_NONE;
45344954feaSfvdl 			return;
45444954feaSfvdl 		}
4554bdc7ccdSmsaitoh 		switch (pssr & IGPHY_PSSR_SPEED_MASK) {
4564bdc7ccdSmsaitoh 		case IGPHY_PSSR_SPEED_1000MBPS:
45744954feaSfvdl 			mii->mii_media_active |= IFM_1000_T;
458e746222fSmsaitoh 			PHY_READ(sc, MII_100T2SR, &gtsr);
45944954feaSfvdl 			if (gtsr & GTSR_MS_RES)
46044954feaSfvdl 				mii->mii_media_active |= IFM_ETH_MASTER;
46144954feaSfvdl 			break;
46244954feaSfvdl 
4634bdc7ccdSmsaitoh 		case IGPHY_PSSR_SPEED_100MBPS:
46444954feaSfvdl 			mii->mii_media_active |= IFM_100_TX;
46544954feaSfvdl 			break;
46644954feaSfvdl 
4674bdc7ccdSmsaitoh 		case IGPHY_PSSR_SPEED_10MBPS:
46844954feaSfvdl 			mii->mii_media_active |= IFM_10_T;
46944954feaSfvdl 			break;
47044954feaSfvdl 
47144954feaSfvdl 		default:
47244954feaSfvdl 			mii->mii_media_active |= IFM_NONE;
47344954feaSfvdl 			mii->mii_media_status = 0;
47444954feaSfvdl 			return;
47544954feaSfvdl 		}
47644954feaSfvdl 
4774bdc7ccdSmsaitoh 		if (pssr & IGPHY_PSSR_FULL_DUPLEX)
47884694bb5Sthorpej 			mii->mii_media_active |=
47908645d15Sthorpej 			    IFM_FDX | mii_phy_flowstatus(sc);
480a7fbee7bSmsaitoh 		else
481a7fbee7bSmsaitoh 			mii->mii_media_active |= IFM_HDX;
48244954feaSfvdl 	} else
48344954feaSfvdl 		mii->mii_media_active = ife->ifm_media;
48444954feaSfvdl }
48544954feaSfvdl 
48644954feaSfvdl static void
igphy_smartspeed_workaround(struct mii_softc * sc)48744954feaSfvdl igphy_smartspeed_workaround(struct mii_softc *sc)
48844954feaSfvdl {
48929a0a6f5Sthorpej 	struct igphy_softc *igsc = (struct igphy_softc *)sc;
49029a0a6f5Sthorpej 	uint16_t reg, gtsr, gtcr;
49129a0a6f5Sthorpej 
492f03b8365Smsaitoh 	/* This workaround is only for 82541 and 82547 */
493f03b8365Smsaitoh 	switch (igsc->sc_mactype) {
494f03b8365Smsaitoh 	case WM_T_82541:
495f03b8365Smsaitoh 	case WM_T_82541_2:
496f03b8365Smsaitoh 	case WM_T_82547:
497f03b8365Smsaitoh 	case WM_T_82547_2:
498f03b8365Smsaitoh 		break;
499f03b8365Smsaitoh 	default:
500f03b8365Smsaitoh 		/* byebye */
501f03b8365Smsaitoh 		return;
502f03b8365Smsaitoh 	}
503f03b8365Smsaitoh 
504e746222fSmsaitoh 	PHY_READ(sc, MII_BMCR, &reg);
505e746222fSmsaitoh 	if ((reg & BMCR_AUTOEN) == 0)
50629a0a6f5Sthorpej 		return;
50729a0a6f5Sthorpej 
50829a0a6f5Sthorpej 	/* XXX Assume 1000TX-FDX is advertized if doing autonegotiation. */
50944954feaSfvdl 
510e746222fSmsaitoh 	PHY_READ(sc, MII_BMSR, &reg);
511e746222fSmsaitoh 	PHY_READ(sc, MII_BMSR, &reg);
51229a0a6f5Sthorpej 	if ((reg & BMSR_LINK) == 0) {
51329a0a6f5Sthorpej 		switch (igsc->sc_smartspeed) {
51444954feaSfvdl 		case 0:
515e746222fSmsaitoh 			PHY_READ(sc, MII_100T2SR, &gtsr);
51644954feaSfvdl 			if (!(gtsr & GTSR_MAN_MS_FLT))
51744954feaSfvdl 				break;
518e746222fSmsaitoh 			PHY_READ(sc, MII_100T2SR, &gtsr);
51944954feaSfvdl 			if (gtsr & GTSR_MAN_MS_FLT) {
520e746222fSmsaitoh 				PHY_READ(sc, MII_100T2CR, &gtcr);
52144954feaSfvdl 				if (gtcr & GTCR_MAN_MS) {
52244954feaSfvdl 					gtcr &= ~GTCR_MAN_MS;
523e746222fSmsaitoh 					PHY_WRITE(sc, MII_100T2CR, gtcr);
52444954feaSfvdl 				}
525e28eda5dSmsaitoh 				mii_phy_auto(sc);
52644954feaSfvdl 			}
52744954feaSfvdl 			break;
52844954feaSfvdl 		case IGPHY_TICK_DOWNSHIFT:
529e746222fSmsaitoh 			PHY_READ(sc, MII_100T2CR, &gtcr);
53044954feaSfvdl 			gtcr |= GTCR_MAN_MS;
53144954feaSfvdl 			PHY_WRITE(sc, MII_100T2CR, gtcr);
532e28eda5dSmsaitoh 			mii_phy_auto(sc);
53344954feaSfvdl 			break;
53444954feaSfvdl 		default:
53544954feaSfvdl 			break;
53644954feaSfvdl 		}
53729a0a6f5Sthorpej 		if (igsc->sc_smartspeed++ == IGPHY_TICK_MAX)
53829a0a6f5Sthorpej 			igsc->sc_smartspeed = 0;
53929a0a6f5Sthorpej 	} else
54029a0a6f5Sthorpej 		igsc->sc_smartspeed = 0;
54144954feaSfvdl }
542