xref: /illumos-gate/usr/src/uts/intel/io/dnet/dnet_mii.c (revision bdb9230ac765cb7af3fc1f4119caf2c5720dceb3)
1*bdb9230aSGarrett D'Amore /*
2*bdb9230aSGarrett D'Amore  * CDDL HEADER START
3*bdb9230aSGarrett D'Amore  *
4*bdb9230aSGarrett D'Amore  * The contents of this file are subject to the terms of the
5*bdb9230aSGarrett D'Amore  * Common Development and Distribution License (the "License").
6*bdb9230aSGarrett D'Amore  * You may not use this file except in compliance with the License.
7*bdb9230aSGarrett D'Amore  *
8*bdb9230aSGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*bdb9230aSGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
10*bdb9230aSGarrett D'Amore  * See the License for the specific language governing permissions
11*bdb9230aSGarrett D'Amore  * and limitations under the License.
12*bdb9230aSGarrett D'Amore  *
13*bdb9230aSGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
14*bdb9230aSGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*bdb9230aSGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
16*bdb9230aSGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
17*bdb9230aSGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
18*bdb9230aSGarrett D'Amore  *
19*bdb9230aSGarrett D'Amore  * CDDL HEADER END
20*bdb9230aSGarrett D'Amore  */
21*bdb9230aSGarrett D'Amore /*
22*bdb9230aSGarrett D'Amore  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23*bdb9230aSGarrett D'Amore  * Use is subject to license terms.
24*bdb9230aSGarrett D'Amore  */
25*bdb9230aSGarrett D'Amore 
26*bdb9230aSGarrett D'Amore /*
27*bdb9230aSGarrett D'Amore  * mii - MII/PHY support for MAC drivers
28*bdb9230aSGarrett D'Amore  *
29*bdb9230aSGarrett D'Amore  * Utility module to provide a consistent interface to a MAC driver accross
30*bdb9230aSGarrett D'Amore  * different implementations of PHY devices
31*bdb9230aSGarrett D'Amore  */
32*bdb9230aSGarrett D'Amore 
33*bdb9230aSGarrett D'Amore #include <sys/types.h>
34*bdb9230aSGarrett D'Amore #include <sys/debug.h>
35*bdb9230aSGarrett D'Amore #include <sys/errno.h>
36*bdb9230aSGarrett D'Amore #include <sys/param.h>
37*bdb9230aSGarrett D'Amore #include <sys/sysmacros.h>
38*bdb9230aSGarrett D'Amore #include <sys/stropts.h>
39*bdb9230aSGarrett D'Amore #include <sys/stream.h>
40*bdb9230aSGarrett D'Amore #include <sys/kmem.h>
41*bdb9230aSGarrett D'Amore #include <sys/conf.h>
42*bdb9230aSGarrett D'Amore #include <sys/ddi.h>
43*bdb9230aSGarrett D'Amore #include <sys/sunddi.h>
44*bdb9230aSGarrett D'Amore #include <sys/devops.h>
45*bdb9230aSGarrett D'Amore #include <sys/modctl.h>
46*bdb9230aSGarrett D'Amore #include <sys/cmn_err.h>
47*bdb9230aSGarrett D'Amore #include <sys/miiregs.h>
48*bdb9230aSGarrett D'Amore #include "dnet_mii.h"
49*bdb9230aSGarrett D'Amore 
50*bdb9230aSGarrett D'Amore 
51*bdb9230aSGarrett D'Amore #ifdef DEBUG
52*bdb9230aSGarrett D'Amore #define	MIIDEBUG
53*bdb9230aSGarrett D'Amore int miidebug = 0;
54*bdb9230aSGarrett D'Amore #define	MIITRACE 1
55*bdb9230aSGarrett D'Amore #define	MIIDUMP 2
56*bdb9230aSGarrett D'Amore #define	MIIPROBE 4
57*bdb9230aSGarrett D'Amore #define	MIICOMPAT 8
58*bdb9230aSGarrett D'Amore #endif
59*bdb9230aSGarrett D'Amore 
60*bdb9230aSGarrett D'Amore /* Local functions */
61*bdb9230aSGarrett D'Amore static struct phydata *mii_get_valid_phydata(mii_handle_t mac, int phy);
62*bdb9230aSGarrett D'Amore static void mii_portmon(mii_handle_t mac);
63*bdb9230aSGarrett D'Amore 
64*bdb9230aSGarrett D'Amore /* Vendor specific callback function prototypes */
65*bdb9230aSGarrett D'Amore static void dump_NS83840(mii_handle_t, int);
66*bdb9230aSGarrett D'Amore static void dump_ICS1890(struct mii_info *, int);
67*bdb9230aSGarrett D'Amore static int getspeed_NS83840(mii_handle_t, int, int *, int *);
68*bdb9230aSGarrett D'Amore static int getspeed_82553(mii_handle_t, int, int *, int *);
69*bdb9230aSGarrett D'Amore static int getspeed_ICS1890(mii_handle_t, int, int *, int *);
70*bdb9230aSGarrett D'Amore static int getspeed_generic(mii_handle_t, int, int *, int *);
71*bdb9230aSGarrett D'Amore static void postreset_ICS1890(mii_handle_t mac, int phy);
72*bdb9230aSGarrett D'Amore static void postreset_NS83840(mii_handle_t mac, int phy);
73*bdb9230aSGarrett D'Amore 
74*bdb9230aSGarrett D'Amore /*
75*bdb9230aSGarrett D'Amore  * MII Interface functions
76*bdb9230aSGarrett D'Amore  */
77*bdb9230aSGarrett D'Amore 
78*bdb9230aSGarrett D'Amore /*
79*bdb9230aSGarrett D'Amore  * Register an instance of an MII interface user
80*bdb9230aSGarrett D'Amore  */
81*bdb9230aSGarrett D'Amore 
82*bdb9230aSGarrett D'Amore int
mii_create(dev_info_t * dip,mii_writefunc_t writefunc,mii_readfunc_t readfunc,mii_handle_t * macp)83*bdb9230aSGarrett D'Amore mii_create(dev_info_t *dip,		/* Passed to read/write functions */
84*bdb9230aSGarrett D'Amore 	    mii_writefunc_t writefunc, 	/* How to write to a MII register */
85*bdb9230aSGarrett D'Amore 	    mii_readfunc_t readfunc,	/* How to read from a MII regster */
86*bdb9230aSGarrett D'Amore 	    mii_handle_t *macp)
87*bdb9230aSGarrett D'Amore {
88*bdb9230aSGarrett D'Amore 	mii_handle_t mac;
89*bdb9230aSGarrett D'Amore 
90*bdb9230aSGarrett D'Amore 	/*  Allocate space for the mii structure */
91*bdb9230aSGarrett D'Amore 	if ((mac = (mii_handle_t)
92*bdb9230aSGarrett D'Amore 	    kmem_zalloc(sizeof (struct mii_info), KM_NOSLEEP)) == NULL)
93*bdb9230aSGarrett D'Amore 		return (MII_NOMEM);
94*bdb9230aSGarrett D'Amore 
95*bdb9230aSGarrett D'Amore 	mac->mii_write = writefunc;
96*bdb9230aSGarrett D'Amore 	mac->mii_read = readfunc;
97*bdb9230aSGarrett D'Amore 	mac->mii_dip = dip;
98*bdb9230aSGarrett D'Amore 	*macp = mac;
99*bdb9230aSGarrett D'Amore 	return (MII_SUCCESS);
100*bdb9230aSGarrett D'Amore }
101*bdb9230aSGarrett D'Amore 
102*bdb9230aSGarrett D'Amore /*
103*bdb9230aSGarrett D'Amore  * Returns true if PHY at address phy is accessible. This should be
104*bdb9230aSGarrett D'Amore  * considered the only function that takes a PHY address that can be called
105*bdb9230aSGarrett D'Amore  * before mii_init_phy. There should be at least one bit set in the status
106*bdb9230aSGarrett D'Amore  * register, and at least one clear
107*bdb9230aSGarrett D'Amore  */
108*bdb9230aSGarrett D'Amore int
mii_probe_phy(mii_handle_t mac,int phy)109*bdb9230aSGarrett D'Amore mii_probe_phy(mii_handle_t mac, int phy)
110*bdb9230aSGarrett D'Amore {
111*bdb9230aSGarrett D'Amore 	ushort_t status;
112*bdb9230aSGarrett D'Amore 	dev_info_t *dip;
113*bdb9230aSGarrett D'Amore 
114*bdb9230aSGarrett D'Amore 	if (!mac || phy < 0 || phy > 31)
115*bdb9230aSGarrett D'Amore 		return (MII_PARAM);
116*bdb9230aSGarrett D'Amore 
117*bdb9230aSGarrett D'Amore 	dip = mac->mii_dip;
118*bdb9230aSGarrett D'Amore 
119*bdb9230aSGarrett D'Amore 	/* Clear any latched bits by reading twice */
120*bdb9230aSGarrett D'Amore 	mac->mii_read(dip, phy, MII_STATUS);
121*bdb9230aSGarrett D'Amore 	status = mac->mii_read(dip, phy, MII_STATUS);
122*bdb9230aSGarrett D'Amore 
123*bdb9230aSGarrett D'Amore #ifdef MIIDEBUG
124*bdb9230aSGarrett D'Amore 	mac->mii_read(dip, phy, MII_CONTROL);
125*bdb9230aSGarrett D'Amore 	if (miidebug & MIIPROBE)
126*bdb9230aSGarrett D'Amore 		cmn_err(CE_NOTE, "PHY Probe: Control=%x, Status=%x",
127*bdb9230aSGarrett D'Amore 		    mac->mii_read(dip, phy, MII_CONTROL), status);
128*bdb9230aSGarrett D'Amore #endif
129*bdb9230aSGarrett D'Amore 	/*
130*bdb9230aSGarrett D'Amore 	 * At least one bit in status should be clear (one of the error
131*bdb9230aSGarrett D'Amore 	 * bits), and there must be at least one bit set for the device
132*bdb9230aSGarrett D'Amore 	 * capabilities. Unconnected devices tend to show 0xffff, but 0x0000
133*bdb9230aSGarrett D'Amore 	 * has been seen.
134*bdb9230aSGarrett D'Amore 	 */
135*bdb9230aSGarrett D'Amore 
136*bdb9230aSGarrett D'Amore 	if (status == 0xffff || status == 0x0000)
137*bdb9230aSGarrett D'Amore 		return (MII_PHYNOTPRESENT);
138*bdb9230aSGarrett D'Amore 	return (MII_SUCCESS);
139*bdb9230aSGarrett D'Amore }
140*bdb9230aSGarrett D'Amore 
141*bdb9230aSGarrett D'Amore /*
142*bdb9230aSGarrett D'Amore  * Initialise PHY, and store info about it in the handle for future
143*bdb9230aSGarrett D'Amore  * reference when the MAC calls us. PHY Vendor-specific code here isolates
144*bdb9230aSGarrett D'Amore  * the LAN driver from worrying about different PHY implementations
145*bdb9230aSGarrett D'Amore  */
146*bdb9230aSGarrett D'Amore 
147*bdb9230aSGarrett D'Amore int
mii_init_phy(mii_handle_t mac,int phy)148*bdb9230aSGarrett D'Amore mii_init_phy(mii_handle_t mac, int phy)
149*bdb9230aSGarrett D'Amore {
150*bdb9230aSGarrett D'Amore 	ushort_t status;
151*bdb9230aSGarrett D'Amore 	void *dip;
152*bdb9230aSGarrett D'Amore 	struct phydata *phydata;
153*bdb9230aSGarrett D'Amore 
154*bdb9230aSGarrett D'Amore 	if ((mac == (mii_handle_t)NULL) || phy < 0 || phy > 31)
155*bdb9230aSGarrett D'Amore 		return (MII_PARAM);
156*bdb9230aSGarrett D'Amore 
157*bdb9230aSGarrett D'Amore 	dip = mac->mii_dip;
158*bdb9230aSGarrett D'Amore 
159*bdb9230aSGarrett D'Amore 	/* Create a phydata structure for this new phy */
160*bdb9230aSGarrett D'Amore 	if (mac->phys[phy])
161*bdb9230aSGarrett D'Amore 		return (MII_PHYPRESENT);
162*bdb9230aSGarrett D'Amore 
163*bdb9230aSGarrett D'Amore 	mac->phys[phy] = phydata = (struct phydata *)
164*bdb9230aSGarrett D'Amore 	    kmem_zalloc(sizeof (struct phydata), KM_NOSLEEP);
165*bdb9230aSGarrett D'Amore 
166*bdb9230aSGarrett D'Amore 	if (!phydata)
167*bdb9230aSGarrett D'Amore 		return (MII_NOMEM);
168*bdb9230aSGarrett D'Amore 
169*bdb9230aSGarrett D'Amore 	phydata->id = (ulong_t)mac->mii_read(dip, phy, MII_PHYIDH) << 16;
170*bdb9230aSGarrett D'Amore 	phydata->id |= (ulong_t)mac->mii_read(dip, phy, MII_PHYIDL);
171*bdb9230aSGarrett D'Amore 	phydata->state = phy_state_unknown;
172*bdb9230aSGarrett D'Amore 
173*bdb9230aSGarrett D'Amore 	/* Override speed and duplex mode from conf-file if present */
174*bdb9230aSGarrett D'Amore 	phydata->fix_duplex =
175*bdb9230aSGarrett D'Amore 	    ddi_getprop(DDI_DEV_T_NONE,
176*bdb9230aSGarrett D'Amore 	    mac->mii_dip, DDI_PROP_DONTPASS, "full-duplex", 0);
177*bdb9230aSGarrett D'Amore 
178*bdb9230aSGarrett D'Amore 	phydata->fix_speed =
179*bdb9230aSGarrett D'Amore 	    ddi_getprop(DDI_DEV_T_NONE,
180*bdb9230aSGarrett D'Amore 	    mac->mii_dip, DDI_PROP_DONTPASS, "speed", 0);
181*bdb9230aSGarrett D'Amore 
182*bdb9230aSGarrett D'Amore 	status = mac->mii_read(dip, phy, MII_STATUS);
183*bdb9230aSGarrett D'Amore 
184*bdb9230aSGarrett D'Amore 	/*
185*bdb9230aSGarrett D'Amore 	 * when explicitly setting speed or duplex, we must
186*bdb9230aSGarrett D'Amore 	 * disable autonegotiation
187*bdb9230aSGarrett D'Amore 	 */
188*bdb9230aSGarrett D'Amore 	if (!(status & MII_STATUS_CANAUTONEG) ||
189*bdb9230aSGarrett D'Amore 	    phydata->fix_speed || phydata->fix_duplex) {
190*bdb9230aSGarrett D'Amore 		/*
191*bdb9230aSGarrett D'Amore 		 * If local side cannot autonegotiate, we can't try to enable
192*bdb9230aSGarrett D'Amore 		 * full duplex without the user's consent, because we cannot
193*bdb9230aSGarrett D'Amore 		 * tell without AN if the partner can support it
194*bdb9230aSGarrett D'Amore 		 */
195*bdb9230aSGarrett D'Amore 		if ((status & (MII_STATUS_100_BASEX | MII_STATUS_100_BASEX_FD |
196*bdb9230aSGarrett D'Amore 		    MII_STATUS_100_BASE_T4)) && phydata->fix_speed == 0) {
197*bdb9230aSGarrett D'Amore 			phydata->fix_speed = 100;
198*bdb9230aSGarrett D'Amore 		} else if ((status & (MII_STATUS_10 | MII_STATUS_10_FD)) &&
199*bdb9230aSGarrett D'Amore 		    phydata->fix_speed == 0) {
200*bdb9230aSGarrett D'Amore 			phydata->fix_speed = 10;
201*bdb9230aSGarrett D'Amore 		} else if (phydata->fix_speed == 0) {
202*bdb9230aSGarrett D'Amore 			/* A very stupid PHY would not be supported */
203*bdb9230aSGarrett D'Amore 			kmem_free(mac->phys[phy], sizeof (struct phydata));
204*bdb9230aSGarrett D'Amore 			mac->phys[phy] = NULL;
205*bdb9230aSGarrett D'Amore 			return (MII_NOTSUPPORTED);
206*bdb9230aSGarrett D'Amore 		}
207*bdb9230aSGarrett D'Amore 		/* mii_sync will sort out the speed selection on the PHY */
208*bdb9230aSGarrett D'Amore 	} else
209*bdb9230aSGarrett D'Amore 		phydata->control = MII_CONTROL_ANE;
210*bdb9230aSGarrett D'Amore 
211*bdb9230aSGarrett D'Amore 	switch (MII_PHY_MFG(phydata->id)) {
212*bdb9230aSGarrett D'Amore 	case OUI_NATIONAL_SEMICONDUCTOR:
213*bdb9230aSGarrett D'Amore 		switch (MII_PHY_MODEL(phydata->id)) {
214*bdb9230aSGarrett D'Amore 		case NS_DP83840:
215*bdb9230aSGarrett D'Amore 			phydata->phy_postreset = postreset_NS83840;
216*bdb9230aSGarrett D'Amore 			phydata->phy_dump = dump_NS83840;
217*bdb9230aSGarrett D'Amore 			phydata->description =
218*bdb9230aSGarrett D'Amore 			    "National Semiconductor DP-83840";
219*bdb9230aSGarrett D'Amore 			phydata->phy_getspeed = getspeed_NS83840;
220*bdb9230aSGarrett D'Amore 			break;
221*bdb9230aSGarrett D'Amore 		default:
222*bdb9230aSGarrett D'Amore 			phydata->description = "Unknown NS";
223*bdb9230aSGarrett D'Amore 			break;
224*bdb9230aSGarrett D'Amore 		}
225*bdb9230aSGarrett D'Amore 		break;
226*bdb9230aSGarrett D'Amore 
227*bdb9230aSGarrett D'Amore 	case OUI_INTEL:
228*bdb9230aSGarrett D'Amore 		switch (MII_PHY_MODEL(phydata->id)) {
229*bdb9230aSGarrett D'Amore 		case INTEL_82553_CSTEP:
230*bdb9230aSGarrett D'Amore 			phydata->description = "Intel 82553 C-step";
231*bdb9230aSGarrett D'Amore 			phydata->phy_getspeed = getspeed_82553;
232*bdb9230aSGarrett D'Amore 			break;
233*bdb9230aSGarrett D'Amore 		case INTEL_82555:
234*bdb9230aSGarrett D'Amore 			phydata->description = "Intel 82555";
235*bdb9230aSGarrett D'Amore 			phydata->phy_getspeed = getspeed_82553;
236*bdb9230aSGarrett D'Amore 			break;
237*bdb9230aSGarrett D'Amore 		case INTEL_82562_EH:
238*bdb9230aSGarrett D'Amore 			phydata->description = "Intel 82562 EH";
239*bdb9230aSGarrett D'Amore 			phydata->phy_getspeed = getspeed_82553;
240*bdb9230aSGarrett D'Amore 			break;
241*bdb9230aSGarrett D'Amore 		case INTEL_82562_ET:
242*bdb9230aSGarrett D'Amore 			phydata->description = "Intel 82562 ET";
243*bdb9230aSGarrett D'Amore 			phydata->phy_getspeed = getspeed_82553;
244*bdb9230aSGarrett D'Amore 			break;
245*bdb9230aSGarrett D'Amore 		case INTEL_82562_EM:
246*bdb9230aSGarrett D'Amore 			phydata->description = "Intel 82562 EM";
247*bdb9230aSGarrett D'Amore 			phydata->phy_getspeed = getspeed_82553;
248*bdb9230aSGarrett D'Amore 			break;
249*bdb9230aSGarrett D'Amore 		default:
250*bdb9230aSGarrett D'Amore 			phydata->description = "Unknown INTEL";
251*bdb9230aSGarrett D'Amore 			break;
252*bdb9230aSGarrett D'Amore 		}
253*bdb9230aSGarrett D'Amore 		break;
254*bdb9230aSGarrett D'Amore 
255*bdb9230aSGarrett D'Amore 	case OUI_ICS:
256*bdb9230aSGarrett D'Amore 		switch (MII_PHY_MODEL(phydata->id)) {
257*bdb9230aSGarrett D'Amore 		case ICS_1890:
258*bdb9230aSGarrett D'Amore 		case ICS_1889:
259*bdb9230aSGarrett D'Amore 			phydata->phy_postreset = postreset_ICS1890;
260*bdb9230aSGarrett D'Amore 			phydata->description = "ICS 1890/1889 PHY";
261*bdb9230aSGarrett D'Amore 			phydata->phy_getspeed = getspeed_ICS1890;
262*bdb9230aSGarrett D'Amore 			phydata->phy_dump = dump_ICS1890;
263*bdb9230aSGarrett D'Amore 			break;
264*bdb9230aSGarrett D'Amore 		default:
265*bdb9230aSGarrett D'Amore 			phydata->description = "ICS Unknown PHY";
266*bdb9230aSGarrett D'Amore 			break;
267*bdb9230aSGarrett D'Amore 		}
268*bdb9230aSGarrett D'Amore 		break;
269*bdb9230aSGarrett D'Amore 
270*bdb9230aSGarrett D'Amore 	default: /* Non-standard PHYs, that encode weird IDs */
271*bdb9230aSGarrett D'Amore 		phydata->description = "Unknown PHY";
272*bdb9230aSGarrett D'Amore 		phydata->phy_dump = NULL;
273*bdb9230aSGarrett D'Amore 		phydata->phy_getspeed = getspeed_generic;
274*bdb9230aSGarrett D'Amore 		break;
275*bdb9230aSGarrett D'Amore 	}
276*bdb9230aSGarrett D'Amore 
277*bdb9230aSGarrett D'Amore 	/* Do all post-reset hacks and user settings */
278*bdb9230aSGarrett D'Amore 	(void) mii_sync(mac, phy);
279*bdb9230aSGarrett D'Amore 
280*bdb9230aSGarrett D'Amore 	if (ddi_getprop(DDI_DEV_T_NONE, mac->mii_dip, DDI_PROP_DONTPASS,
281*bdb9230aSGarrett D'Amore 	    "dump-phy", 0))
282*bdb9230aSGarrett D'Amore 		(void) mii_dump_phy(mac, phy);
283*bdb9230aSGarrett D'Amore 
284*bdb9230aSGarrett D'Amore 	return (MII_SUCCESS);
285*bdb9230aSGarrett D'Amore }
286*bdb9230aSGarrett D'Amore 
287*bdb9230aSGarrett D'Amore /*
288*bdb9230aSGarrett D'Amore  * Cause a reset on a PHY
289*bdb9230aSGarrett D'Amore  */
290*bdb9230aSGarrett D'Amore 
291*bdb9230aSGarrett D'Amore int
mii_reset_phy(mii_handle_t mac,int phy,enum mii_wait_type wait)292*bdb9230aSGarrett D'Amore mii_reset_phy(mii_handle_t mac, int phy, enum mii_wait_type wait)
293*bdb9230aSGarrett D'Amore {
294*bdb9230aSGarrett D'Amore 	int i;
295*bdb9230aSGarrett D'Amore 	struct phydata *phyd;
296*bdb9230aSGarrett D'Amore 	ushort_t control;
297*bdb9230aSGarrett D'Amore 	if (!(phyd = mii_get_valid_phydata(mac, phy)))
298*bdb9230aSGarrett D'Amore 		return (MII_PARAM);
299*bdb9230aSGarrett D'Amore 
300*bdb9230aSGarrett D'Amore 	/* Strobe the reset bit in the control register */
301*bdb9230aSGarrett D'Amore 	mac->mii_write(mac->mii_dip, phy, MII_CONTROL,
302*bdb9230aSGarrett D'Amore 	    phyd->control | MII_CONTROL_RESET);
303*bdb9230aSGarrett D'Amore 
304*bdb9230aSGarrett D'Amore 	phyd->state = phy_state_unknown;
305*bdb9230aSGarrett D'Amore 
306*bdb9230aSGarrett D'Amore 	/*
307*bdb9230aSGarrett D'Amore 	 * This is likely to be very fast (ie, by the time we read the
308*bdb9230aSGarrett D'Amore 	 * control register once, the devices we have seen can have already
309*bdb9230aSGarrett D'Amore 	 * reset), but according to 802.3u 22.2.4.1.1, it could be up to .5 sec.
310*bdb9230aSGarrett D'Amore 	 */
311*bdb9230aSGarrett D'Amore 	if (wait == mii_wait_interrupt || wait == mii_wait_user) {
312*bdb9230aSGarrett D'Amore 		for (i = 100; i--; ) {
313*bdb9230aSGarrett D'Amore 			control = mac->mii_read(mac->mii_dip, phy, MII_CONTROL);
314*bdb9230aSGarrett D'Amore 			if (!(control & MII_CONTROL_RESET))
315*bdb9230aSGarrett D'Amore 				break;
316*bdb9230aSGarrett D'Amore 			drv_usecwait(10);
317*bdb9230aSGarrett D'Amore 		}
318*bdb9230aSGarrett D'Amore 		if (i)
319*bdb9230aSGarrett D'Amore 			goto reset_completed;
320*bdb9230aSGarrett D'Amore 	}
321*bdb9230aSGarrett D'Amore 
322*bdb9230aSGarrett D'Amore 	if (wait == mii_wait_user) {
323*bdb9230aSGarrett D'Amore 		for (i = 50; i--; ) {
324*bdb9230aSGarrett D'Amore 			control = mac->mii_read(mac->mii_dip, phy, MII_CONTROL);
325*bdb9230aSGarrett D'Amore 			if (!(control & MII_CONTROL_RESET))
326*bdb9230aSGarrett D'Amore 				break;
327*bdb9230aSGarrett D'Amore 			delay(drv_usectohz(10000));
328*bdb9230aSGarrett D'Amore 		}
329*bdb9230aSGarrett D'Amore 		if (i)
330*bdb9230aSGarrett D'Amore 			goto reset_completed;
331*bdb9230aSGarrett D'Amore 		return (MII_HARDFAIL);	/* It MUST reset within this time */
332*bdb9230aSGarrett D'Amore 
333*bdb9230aSGarrett D'Amore 	}
334*bdb9230aSGarrett D'Amore 	return (MII_TIMEOUT);
335*bdb9230aSGarrett D'Amore 
336*bdb9230aSGarrett D'Amore reset_completed:
337*bdb9230aSGarrett D'Amore 	(void) mii_sync(mac, phy);
338*bdb9230aSGarrett D'Amore 	return (MII_SUCCESS);
339*bdb9230aSGarrett D'Amore }
340*bdb9230aSGarrett D'Amore 
341*bdb9230aSGarrett D'Amore /*
342*bdb9230aSGarrett D'Amore  * This routine is called to synchronise the software and the PHY. It should
343*bdb9230aSGarrett D'Amore  * be called after the PHY is reset, and after initialising the PHY. This
344*bdb9230aSGarrett D'Amore  * routine is external because devices (DNET) can reset the PHY in ways beyond
345*bdb9230aSGarrett D'Amore  * the control of the mii interface. Should this happen, the driver is
346*bdb9230aSGarrett D'Amore  * required to call mii_sync().
347*bdb9230aSGarrett D'Amore  * If the PHY is resetting still when this is called, it will do nothing,
348*bdb9230aSGarrett D'Amore  * but, it will be retriggered when the portmon timer expires.
349*bdb9230aSGarrett D'Amore  */
350*bdb9230aSGarrett D'Amore 
351*bdb9230aSGarrett D'Amore int
mii_sync(mii_handle_t mac,int phy)352*bdb9230aSGarrett D'Amore mii_sync(mii_handle_t mac, int phy)
353*bdb9230aSGarrett D'Amore {
354*bdb9230aSGarrett D'Amore 	struct phydata *phyd = mac->phys[phy];
355*bdb9230aSGarrett D'Amore 	int len, i, numprop;
356*bdb9230aSGarrett D'Amore 	struct regprop {
357*bdb9230aSGarrett D'Amore 		int reg;
358*bdb9230aSGarrett D'Amore 		int value;
359*bdb9230aSGarrett D'Amore 	} *regprop;
360*bdb9230aSGarrett D'Amore 
361*bdb9230aSGarrett D'Amore #ifdef MIIDEBUG
362*bdb9230aSGarrett D'Amore 	if (miidebug & MIITRACE)
363*bdb9230aSGarrett D'Amore 		cmn_err(CE_NOTE, "mii_sync (phy addr %d)", phy);
364*bdb9230aSGarrett D'Amore #endif
365*bdb9230aSGarrett D'Amore 
366*bdb9230aSGarrett D'Amore 	len = 0;
367*bdb9230aSGarrett D'Amore 	/*
368*bdb9230aSGarrett D'Amore 	 * Conf file can specify a sequence of values to write to
369*bdb9230aSGarrett D'Amore 	 * the PHY registers if required
370*bdb9230aSGarrett D'Amore 	 */
371*bdb9230aSGarrett D'Amore 	if (ddi_getlongprop(DDI_DEV_T_ANY, mac->mii_dip,
372*bdb9230aSGarrett D'Amore 	    DDI_PROP_DONTPASS, "phy-registers", (caddr_t)&regprop,
373*bdb9230aSGarrett D'Amore 	    &len) == DDI_PROP_SUCCESS) {
374*bdb9230aSGarrett D'Amore 		numprop = len / sizeof (struct regprop);
375*bdb9230aSGarrett D'Amore 		for (i = 0; i < numprop; i++) {
376*bdb9230aSGarrett D'Amore 			mac->mii_write(mac->mii_dip, phy,
377*bdb9230aSGarrett D'Amore 			    regprop[i].reg, regprop[i].value);
378*bdb9230aSGarrett D'Amore #ifdef MIIDEBUG
379*bdb9230aSGarrett D'Amore 			if (miidebug & MIITRACE)
380*bdb9230aSGarrett D'Amore 				cmn_err(CE_NOTE, "PHY Write reg %d=%x",
381*bdb9230aSGarrett D'Amore 				    regprop[i].reg, regprop[i].value);
382*bdb9230aSGarrett D'Amore #endif
383*bdb9230aSGarrett D'Amore 		}
384*bdb9230aSGarrett D'Amore 		kmem_free(regprop, len);
385*bdb9230aSGarrett D'Amore 	} else {
386*bdb9230aSGarrett D'Amore 		mac->mii_write(mac->mii_dip, phy, MII_CONTROL, phyd->control);
387*bdb9230aSGarrett D'Amore 		if (phyd->phy_postreset)
388*bdb9230aSGarrett D'Amore 			phyd->phy_postreset(mac, phy);
389*bdb9230aSGarrett D'Amore 		if (phyd->fix_speed || phyd->fix_duplex) {
390*bdb9230aSGarrett D'Amore 			/* XXX function return value ignored */
391*bdb9230aSGarrett D'Amore 			(void) mii_fixspeed(mac, phy, phyd->fix_speed,
392*bdb9230aSGarrett D'Amore 			    phyd->fix_duplex);
393*bdb9230aSGarrett D'Amore 		}
394*bdb9230aSGarrett D'Amore 	}
395*bdb9230aSGarrett D'Amore 	return (MII_SUCCESS);
396*bdb9230aSGarrett D'Amore }
397*bdb9230aSGarrett D'Amore 
398*bdb9230aSGarrett D'Amore /*
399*bdb9230aSGarrett D'Amore  * Disable full-duplex negotiation on the PHY. This is useful if the
400*bdb9230aSGarrett D'Amore  * driver or link-partner is advertising full duplex, but does not support
401*bdb9230aSGarrett D'Amore  * it properly (as some previous solaris drivers didn't)
402*bdb9230aSGarrett D'Amore  */
403*bdb9230aSGarrett D'Amore 
404*bdb9230aSGarrett D'Amore int
mii_disable_fullduplex(mii_handle_t mac,int phy)405*bdb9230aSGarrett D'Amore mii_disable_fullduplex(mii_handle_t mac, int phy)
406*bdb9230aSGarrett D'Amore {
407*bdb9230aSGarrett D'Amore 	void *dip = mac->mii_dip;
408*bdb9230aSGarrett D'Amore 	ushort_t expansion,  miiadvert;
409*bdb9230aSGarrett D'Amore 	/* dont advertise full duplex capabilites */
410*bdb9230aSGarrett D'Amore 	const int fullduplex = MII_ABILITY_10BASE_T_FD
411*bdb9230aSGarrett D'Amore 	    | MII_ABILITY_100BASE_TX_FD;
412*bdb9230aSGarrett D'Amore 
413*bdb9230aSGarrett D'Amore 	if (!(mac->mii_read(dip, phy, MII_STATUS) & MII_STATUS_CANAUTONEG)) {
414*bdb9230aSGarrett D'Amore 		/*
415*bdb9230aSGarrett D'Amore 		 * Local side cannot autonegotiate, so full duplex should
416*bdb9230aSGarrett D'Amore 		 * never be negotiated. Consider it as a success
417*bdb9230aSGarrett D'Amore 		 */
418*bdb9230aSGarrett D'Amore 		return (MII_SUCCESS);
419*bdb9230aSGarrett D'Amore 	}
420*bdb9230aSGarrett D'Amore 
421*bdb9230aSGarrett D'Amore 	/* Change what we advertise if it includes full duplex */
422*bdb9230aSGarrett D'Amore 
423*bdb9230aSGarrett D'Amore 	miiadvert = mac->mii_read(dip, phy, MII_AN_ADVERT);
424*bdb9230aSGarrett D'Amore 	if (miiadvert & fullduplex)
425*bdb9230aSGarrett D'Amore 		mac->mii_write(dip, phy, MII_AN_ADVERT,
426*bdb9230aSGarrett D'Amore 		    miiadvert & ~fullduplex);
427*bdb9230aSGarrett D'Amore 
428*bdb9230aSGarrett D'Amore 	/* See what other end is able to do.  */
429*bdb9230aSGarrett D'Amore 
430*bdb9230aSGarrett D'Amore 	expansion = mac->mii_read(dip, phy, MII_AN_EXPANSION);
431*bdb9230aSGarrett D'Amore 
432*bdb9230aSGarrett D'Amore 	/*
433*bdb9230aSGarrett D'Amore 	 * Renegotiate if the link partner supports autonegotiation
434*bdb9230aSGarrett D'Amore 	 * If it doesn't, we will never have auto-negotiated full duplex
435*bdb9230aSGarrett D'Amore 	 * anyway
436*bdb9230aSGarrett D'Amore 	 */
437*bdb9230aSGarrett D'Amore 
438*bdb9230aSGarrett D'Amore 	if (expansion & MII_AN_EXP_LPCANAN)
439*bdb9230aSGarrett D'Amore 		return (mii_rsan(mac, phy, mii_wait_none));
440*bdb9230aSGarrett D'Amore 	else
441*bdb9230aSGarrett D'Amore 		return (MII_SUCCESS);
442*bdb9230aSGarrett D'Amore }
443*bdb9230aSGarrett D'Amore 
444*bdb9230aSGarrett D'Amore /*
445*bdb9230aSGarrett D'Amore  * (re)enable autonegotiation on a PHY.
446*bdb9230aSGarrett D'Amore  */
447*bdb9230aSGarrett D'Amore 
448*bdb9230aSGarrett D'Amore int
mii_autoneg_enab(mii_handle_t mac,int phy)449*bdb9230aSGarrett D'Amore mii_autoneg_enab(mii_handle_t mac, int phy)
450*bdb9230aSGarrett D'Amore {
451*bdb9230aSGarrett D'Amore 	struct phydata *phyd;
452*bdb9230aSGarrett D'Amore 	if (!(phyd = mii_get_valid_phydata(mac, phy)))
453*bdb9230aSGarrett D'Amore 		return (MII_PARAM);
454*bdb9230aSGarrett D'Amore 	phyd->control |= MII_CONTROL_ANE;
455*bdb9230aSGarrett D'Amore 	mac->mii_write(mac->mii_dip, phy, MII_CONTROL, phyd->control);
456*bdb9230aSGarrett D'Amore 	return (MII_SUCCESS);
457*bdb9230aSGarrett D'Amore }
458*bdb9230aSGarrett D'Amore 
459*bdb9230aSGarrett D'Amore /*
460*bdb9230aSGarrett D'Amore  * Check the link status of a PHY connection
461*bdb9230aSGarrett D'Amore  */
462*bdb9230aSGarrett D'Amore int
mii_linkup(mii_handle_t mac,int phy)463*bdb9230aSGarrett D'Amore mii_linkup(mii_handle_t mac, int phy)
464*bdb9230aSGarrett D'Amore {
465*bdb9230aSGarrett D'Amore 	ushort_t status;
466*bdb9230aSGarrett D'Amore 
467*bdb9230aSGarrett D'Amore 	/*
468*bdb9230aSGarrett D'Amore 	 * Link status latches, so we need to read it twice, to make sure we
469*bdb9230aSGarrett D'Amore 	 * get its current status
470*bdb9230aSGarrett D'Amore 	 */
471*bdb9230aSGarrett D'Amore 	mac->mii_read(mac->mii_dip, phy, MII_STATUS);
472*bdb9230aSGarrett D'Amore 	status = mac->mii_read(mac->mii_dip, phy, MII_STATUS);
473*bdb9230aSGarrett D'Amore 
474*bdb9230aSGarrett D'Amore 	if (status != 0xffff && (status & MII_STATUS_LINKUP))
475*bdb9230aSGarrett D'Amore 		return (1);
476*bdb9230aSGarrett D'Amore 	else
477*bdb9230aSGarrett D'Amore 		return (0);
478*bdb9230aSGarrett D'Amore }
479*bdb9230aSGarrett D'Amore 
480*bdb9230aSGarrett D'Amore /*
481*bdb9230aSGarrett D'Amore  * Discover what speed the PHY is running at, irrespective of wheather it
482*bdb9230aSGarrett D'Amore  * autonegotiated this, or was fixed at that rate.
483*bdb9230aSGarrett D'Amore  */
484*bdb9230aSGarrett D'Amore 
485*bdb9230aSGarrett D'Amore int
mii_getspeed(mii_handle_t mac,int phy,int * speed,int * fulld)486*bdb9230aSGarrett D'Amore mii_getspeed(mii_handle_t mac, int phy, int *speed, int *fulld)
487*bdb9230aSGarrett D'Amore {
488*bdb9230aSGarrett D'Amore 	struct phydata *phyd;
489*bdb9230aSGarrett D'Amore 
490*bdb9230aSGarrett D'Amore 	if (!(phyd = mii_get_valid_phydata(mac, phy)))
491*bdb9230aSGarrett D'Amore 		return (MII_PARAM);
492*bdb9230aSGarrett D'Amore 	if (!(phyd->control & MII_CONTROL_ANE)) {
493*bdb9230aSGarrett D'Amore 		/*
494*bdb9230aSGarrett D'Amore 		 * user has requested fixed speed operation, return what we
495*bdb9230aSGarrett D'Amore 		 * wrote to the control registerfrom control register
496*bdb9230aSGarrett D'Amore 		 */
497*bdb9230aSGarrett D'Amore 
498*bdb9230aSGarrett D'Amore 		*speed = phyd->control & MII_CONTROL_100MB ? 100:10;
499*bdb9230aSGarrett D'Amore 		*fulld = phyd->control & MII_CONTROL_FDUPLEX ? 1:0;
500*bdb9230aSGarrett D'Amore 		return (MII_SUCCESS);
501*bdb9230aSGarrett D'Amore 	}
502*bdb9230aSGarrett D'Amore 
503*bdb9230aSGarrett D'Amore 	if (!phyd->phy_getspeed) /* No standard way to do this(!) */
504*bdb9230aSGarrett D'Amore 		return (MII_NOTSUPPORTED);
505*bdb9230aSGarrett D'Amore 
506*bdb9230aSGarrett D'Amore 	return (phyd->phy_getspeed(mac, phy, speed, fulld));
507*bdb9230aSGarrett D'Amore }
508*bdb9230aSGarrett D'Amore 
509*bdb9230aSGarrett D'Amore /*
510*bdb9230aSGarrett D'Amore  * Fix the speed and duplex mode of a PHY
511*bdb9230aSGarrett D'Amore  */
512*bdb9230aSGarrett D'Amore 
513*bdb9230aSGarrett D'Amore int
mii_fixspeed(mii_handle_t mac,int phy,int speed,int fullduplex)514*bdb9230aSGarrett D'Amore mii_fixspeed(mii_handle_t mac, int phy, int speed, int fullduplex)
515*bdb9230aSGarrett D'Amore {
516*bdb9230aSGarrett D'Amore 	struct phydata *phyd;
517*bdb9230aSGarrett D'Amore 
518*bdb9230aSGarrett D'Amore #ifdef MIIDEBUG
519*bdb9230aSGarrett D'Amore 	cmn_err(CE_CONT, "!%s: setting speed to %d, %s duplex",
520*bdb9230aSGarrett D'Amore 	    ddi_get_name(mac->mii_dip), speed,
521*bdb9230aSGarrett D'Amore 	    fullduplex ? "full" : "half");
522*bdb9230aSGarrett D'Amore #endif
523*bdb9230aSGarrett D'Amore 
524*bdb9230aSGarrett D'Amore 	if (!(phyd = mii_get_valid_phydata(mac, phy)))
525*bdb9230aSGarrett D'Amore 		return (MII_PARAM);
526*bdb9230aSGarrett D'Amore 	phyd->control &= ~MII_CONTROL_ANE;
527*bdb9230aSGarrett D'Amore 
528*bdb9230aSGarrett D'Amore 	if (speed == 100)
529*bdb9230aSGarrett D'Amore 		phyd->control |= MII_CONTROL_100MB;
530*bdb9230aSGarrett D'Amore 	else if (speed == 10)
531*bdb9230aSGarrett D'Amore 		phyd->control &= ~MII_CONTROL_100MB;
532*bdb9230aSGarrett D'Amore 	else
533*bdb9230aSGarrett D'Amore 		cmn_err(CE_NOTE, "%s: mii does not support %d Mb/s speed",
534*bdb9230aSGarrett D'Amore 		    ddi_get_name(mac->mii_dip), speed);
535*bdb9230aSGarrett D'Amore 
536*bdb9230aSGarrett D'Amore 	if (fullduplex)
537*bdb9230aSGarrett D'Amore 		phyd->control |= MII_CONTROL_FDUPLEX;
538*bdb9230aSGarrett D'Amore 	else
539*bdb9230aSGarrett D'Amore 		phyd->control &= ~MII_CONTROL_FDUPLEX;
540*bdb9230aSGarrett D'Amore 
541*bdb9230aSGarrett D'Amore 	mac->mii_write(mac->mii_dip, phy, MII_CONTROL, phyd->control);
542*bdb9230aSGarrett D'Amore 	phyd->fix_speed = speed;
543*bdb9230aSGarrett D'Amore 	phyd->fix_duplex = fullduplex;
544*bdb9230aSGarrett D'Amore 	return (MII_SUCCESS);
545*bdb9230aSGarrett D'Amore }
546*bdb9230aSGarrett D'Amore /*
547*bdb9230aSGarrett D'Amore  * Electrically isolate/unisolate the PHY
548*bdb9230aSGarrett D'Amore  */
549*bdb9230aSGarrett D'Amore 
550*bdb9230aSGarrett D'Amore int
mii_isolate(mii_handle_t mac,int phy)551*bdb9230aSGarrett D'Amore mii_isolate(mii_handle_t mac, int phy)
552*bdb9230aSGarrett D'Amore {
553*bdb9230aSGarrett D'Amore 	struct phydata *phyd;
554*bdb9230aSGarrett D'Amore 
555*bdb9230aSGarrett D'Amore 	if (!(phyd = mii_get_valid_phydata(mac, phy)))
556*bdb9230aSGarrett D'Amore 		return (MII_PARAM);
557*bdb9230aSGarrett D'Amore 
558*bdb9230aSGarrett D'Amore 	phyd->control |= MII_CONTROL_ISOLATE;
559*bdb9230aSGarrett D'Amore 	mac->mii_write(mac->mii_dip, phy, MII_CONTROL, phyd->control);
560*bdb9230aSGarrett D'Amore 
561*bdb9230aSGarrett D'Amore 	/* Wait for device to settle */
562*bdb9230aSGarrett D'Amore 	drv_usecwait(50);
563*bdb9230aSGarrett D'Amore 	return (MII_SUCCESS);
564*bdb9230aSGarrett D'Amore }
565*bdb9230aSGarrett D'Amore 
566*bdb9230aSGarrett D'Amore int
mii_unisolate(mii_handle_t mac,int phy)567*bdb9230aSGarrett D'Amore mii_unisolate(mii_handle_t mac, int phy)
568*bdb9230aSGarrett D'Amore {
569*bdb9230aSGarrett D'Amore 	struct phydata *phyd;
570*bdb9230aSGarrett D'Amore 
571*bdb9230aSGarrett D'Amore 	if (!(phyd = mii_get_valid_phydata(mac, phy)))
572*bdb9230aSGarrett D'Amore 		return (MII_PARAM);
573*bdb9230aSGarrett D'Amore 
574*bdb9230aSGarrett D'Amore 	phyd->control &= ~MII_CONTROL_ISOLATE;
575*bdb9230aSGarrett D'Amore 	mac->mii_write(mac->mii_dip, phy, MII_CONTROL, phyd->control);
576*bdb9230aSGarrett D'Amore 	return (MII_SUCCESS);
577*bdb9230aSGarrett D'Amore }
578*bdb9230aSGarrett D'Amore 
579*bdb9230aSGarrett D'Amore /*
580*bdb9230aSGarrett D'Amore  * Restart autonegotiation on a PHY
581*bdb9230aSGarrett D'Amore  */
582*bdb9230aSGarrett D'Amore 
583*bdb9230aSGarrett D'Amore int
mii_rsan(mii_handle_t mac,int phy,enum mii_wait_type wait)584*bdb9230aSGarrett D'Amore mii_rsan(mii_handle_t mac, int phy, enum mii_wait_type wait)
585*bdb9230aSGarrett D'Amore {
586*bdb9230aSGarrett D'Amore 	int i;
587*bdb9230aSGarrett D'Amore 	void *dip;
588*bdb9230aSGarrett D'Amore 	struct phydata *phyd;
589*bdb9230aSGarrett D'Amore 
590*bdb9230aSGarrett D'Amore 	if (wait == mii_wait_interrupt ||
591*bdb9230aSGarrett D'Amore 	    !(phyd = mii_get_valid_phydata(mac, phy)))
592*bdb9230aSGarrett D'Amore 		return (MII_PARAM);
593*bdb9230aSGarrett D'Amore 
594*bdb9230aSGarrett D'Amore 	if (phyd->fix_speed)
595*bdb9230aSGarrett D'Amore 		return (MII_STATE);
596*bdb9230aSGarrett D'Amore 
597*bdb9230aSGarrett D'Amore 	dip = mac->mii_dip;
598*bdb9230aSGarrett D'Amore 
599*bdb9230aSGarrett D'Amore 	phyd->control |= MII_CONTROL_ANE;
600*bdb9230aSGarrett D'Amore 	mac->mii_write(dip, phy, MII_CONTROL, phyd->control|MII_CONTROL_RSAN);
601*bdb9230aSGarrett D'Amore 
602*bdb9230aSGarrett D'Amore 	/*
603*bdb9230aSGarrett D'Amore 	 * This can take ages (a second or so). It makes more sense to use
604*bdb9230aSGarrett D'Amore 	 * the port monitor rather than waiting for completion of this on the
605*bdb9230aSGarrett D'Amore 	 * PHY. It is pointless doing a busy wait here
606*bdb9230aSGarrett D'Amore 	 */
607*bdb9230aSGarrett D'Amore 
608*bdb9230aSGarrett D'Amore 	if (wait == mii_wait_user) {
609*bdb9230aSGarrett D'Amore 		for (i = 200; i--; ) {
610*bdb9230aSGarrett D'Amore 			delay(drv_usectohz(10000));
611*bdb9230aSGarrett D'Amore 			if (mac->mii_read(dip, phy, MII_STATUS) &
612*bdb9230aSGarrett D'Amore 			    MII_STATUS_ANDONE)
613*bdb9230aSGarrett D'Amore 				return (MII_SUCCESS);
614*bdb9230aSGarrett D'Amore 		}
615*bdb9230aSGarrett D'Amore 		cmn_err(CE_NOTE,
616*bdb9230aSGarrett D'Amore 		    "!%s:Timed out waiting for autonegotiation",
617*bdb9230aSGarrett D'Amore 		    ddi_get_name(mac->mii_dip));
618*bdb9230aSGarrett D'Amore 		return (MII_TIMEOUT);
619*bdb9230aSGarrett D'Amore 	}
620*bdb9230aSGarrett D'Amore 	return (MII_TIMEOUT);
621*bdb9230aSGarrett D'Amore }
622*bdb9230aSGarrett D'Amore 
623*bdb9230aSGarrett D'Amore /*
624*bdb9230aSGarrett D'Amore  * Debuging function to dump contents of PHY registers
625*bdb9230aSGarrett D'Amore  */
626*bdb9230aSGarrett D'Amore int
mii_dump_phy(mii_handle_t mac,int phy)627*bdb9230aSGarrett D'Amore mii_dump_phy(mii_handle_t mac, int phy)
628*bdb9230aSGarrett D'Amore {
629*bdb9230aSGarrett D'Amore 	struct phydata *phydat;
630*bdb9230aSGarrett D'Amore 
631*bdb9230aSGarrett D'Amore 	char *miiregs[] = {
632*bdb9230aSGarrett D'Amore 		"Control             ",
633*bdb9230aSGarrett D'Amore 		"Status              ",
634*bdb9230aSGarrett D'Amore 		"PHY Id(H)           ",
635*bdb9230aSGarrett D'Amore 		"PHY Id(L)           ",
636*bdb9230aSGarrett D'Amore 		"Advertisement       ",
637*bdb9230aSGarrett D'Amore 		"Link Partner Ability",
638*bdb9230aSGarrett D'Amore 		"Expansion           ",
639*bdb9230aSGarrett D'Amore 		"Next Page Transmit  ",
640*bdb9230aSGarrett D'Amore 		0
641*bdb9230aSGarrett D'Amore 	};
642*bdb9230aSGarrett D'Amore 	int i;
643*bdb9230aSGarrett D'Amore 
644*bdb9230aSGarrett D'Amore 	if (!(phydat = mii_get_valid_phydata(mac, phy)))
645*bdb9230aSGarrett D'Amore 		return (MII_PARAM);
646*bdb9230aSGarrett D'Amore 
647*bdb9230aSGarrett D'Amore 	cmn_err(CE_NOTE, "%s: PHY %d, type %s", ddi_get_name(mac->mii_dip), phy,
648*bdb9230aSGarrett D'Amore 	    phydat->description ? phydat->description: "Unknown");
649*bdb9230aSGarrett D'Amore 
650*bdb9230aSGarrett D'Amore 	for (i = 0; miiregs[i]; i++)
651*bdb9230aSGarrett D'Amore 		cmn_err(CE_NOTE, "%s:\t%x",
652*bdb9230aSGarrett D'Amore 		    miiregs[i], mac->mii_read(mac->mii_dip, phy, i));
653*bdb9230aSGarrett D'Amore 
654*bdb9230aSGarrett D'Amore 	if (phydat->phy_dump)
655*bdb9230aSGarrett D'Amore 		phydat->phy_dump((struct mii_info *)mac, phy);
656*bdb9230aSGarrett D'Amore 
657*bdb9230aSGarrett D'Amore 	return (MII_SUCCESS);
658*bdb9230aSGarrett D'Amore }
659*bdb9230aSGarrett D'Amore 
660*bdb9230aSGarrett D'Amore /*
661*bdb9230aSGarrett D'Amore  * Start a periodic check to monitor the MII devices attached, and callback
662*bdb9230aSGarrett D'Amore  * to the MAC driver when the state on a device changes
663*bdb9230aSGarrett D'Amore  */
664*bdb9230aSGarrett D'Amore 
665*bdb9230aSGarrett D'Amore int
mii_start_portmon(mii_handle_t mac,mii_linkfunc_t notify,kmutex_t * lock)666*bdb9230aSGarrett D'Amore mii_start_portmon(mii_handle_t mac, mii_linkfunc_t notify, kmutex_t *lock)
667*bdb9230aSGarrett D'Amore {
668*bdb9230aSGarrett D'Amore 	if (mac->mii_linknotify || mac->portmon_timer)
669*bdb9230aSGarrett D'Amore 		return (MII_STATE);
670*bdb9230aSGarrett D'Amore 	mac->mii_linknotify = notify;
671*bdb9230aSGarrett D'Amore 	/*
672*bdb9230aSGarrett D'Amore 	 * NOTE: Portmon is normally called through a timeout. In the case
673*bdb9230aSGarrett D'Amore 	 * of starting off, we assume that the lock is already held
674*bdb9230aSGarrett D'Amore 	 */
675*bdb9230aSGarrett D'Amore 	mac->lock = NULL; /* portmon wont try to aquire any lock this time */
676*bdb9230aSGarrett D'Amore 	mii_portmon(mac);
677*bdb9230aSGarrett D'Amore 	mac->lock = lock;
678*bdb9230aSGarrett D'Amore 	return (MII_SUCCESS);
679*bdb9230aSGarrett D'Amore }
680*bdb9230aSGarrett D'Amore 
681*bdb9230aSGarrett D'Amore int
mii_stop_portmon(mii_handle_t mac)682*bdb9230aSGarrett D'Amore mii_stop_portmon(mii_handle_t mac)
683*bdb9230aSGarrett D'Amore {
684*bdb9230aSGarrett D'Amore 	if (!mac->mii_linknotify || !mac->portmon_timer)
685*bdb9230aSGarrett D'Amore 		return (MII_STATE);
686*bdb9230aSGarrett D'Amore 
687*bdb9230aSGarrett D'Amore 	mac->mii_linknotify = NULL;
688*bdb9230aSGarrett D'Amore 	mac->lock = NULL;
689*bdb9230aSGarrett D'Amore 	(void) untimeout(mac->portmon_timer);
690*bdb9230aSGarrett D'Amore 	mac->portmon_timer = 0;
691*bdb9230aSGarrett D'Amore 	return (MII_SUCCESS);
692*bdb9230aSGarrett D'Amore }
693*bdb9230aSGarrett D'Amore 
694*bdb9230aSGarrett D'Amore static void
mii_portmon(mii_handle_t mac)695*bdb9230aSGarrett D'Amore mii_portmon(mii_handle_t mac)
696*bdb9230aSGarrett D'Amore {
697*bdb9230aSGarrett D'Amore 	int i;
698*bdb9230aSGarrett D'Amore 	enum mii_phy_state state;
699*bdb9230aSGarrett D'Amore 	struct phydata *phydata;
700*bdb9230aSGarrett D'Amore 
701*bdb9230aSGarrett D'Amore 	/*
702*bdb9230aSGarrett D'Amore 	 * There is a potential deadlock between this test and the
703*bdb9230aSGarrett D'Amore 	 * mutex_enter
704*bdb9230aSGarrett D'Amore 	 */
705*bdb9230aSGarrett D'Amore 	if (!mac->mii_linknotify) /* Exiting */
706*bdb9230aSGarrett D'Amore 		return;
707*bdb9230aSGarrett D'Amore 
708*bdb9230aSGarrett D'Amore 	if (mac->lock)
709*bdb9230aSGarrett D'Amore 		mutex_enter(mac->lock);
710*bdb9230aSGarrett D'Amore 
711*bdb9230aSGarrett D'Amore 	/*
712*bdb9230aSGarrett D'Amore 	 * For each initialised phy, see if the link state has changed, and
713*bdb9230aSGarrett D'Amore 	 * callback to the mac driver if it has
714*bdb9230aSGarrett D'Amore 	 */
715*bdb9230aSGarrett D'Amore 	for (i = 0; i < 32; i++) {
716*bdb9230aSGarrett D'Amore 		if ((phydata = mac->phys[i]) != 0) {
717*bdb9230aSGarrett D'Amore 			state = mii_linkup(mac, i) ?
718*bdb9230aSGarrett D'Amore 			    phy_state_linkup : phy_state_linkdown;
719*bdb9230aSGarrett D'Amore 			if (state != phydata->state) {
720*bdb9230aSGarrett D'Amore #ifdef MIIDEBUG
721*bdb9230aSGarrett D'Amore 				if (miidebug)
722*bdb9230aSGarrett D'Amore 					cmn_err(CE_NOTE, "%s: PHY %d link %s",
723*bdb9230aSGarrett D'Amore 					    ddi_get_name(mac->mii_dip), i,
724*bdb9230aSGarrett D'Amore 					    state == phy_state_linkup ?
725*bdb9230aSGarrett D'Amore 					    "up" : "down");
726*bdb9230aSGarrett D'Amore #endif
727*bdb9230aSGarrett D'Amore 				phydata->state = state;
728*bdb9230aSGarrett D'Amore 				mac->mii_linknotify(mac->mii_dip, i, state);
729*bdb9230aSGarrett D'Amore 			}
730*bdb9230aSGarrett D'Amore 		}
731*bdb9230aSGarrett D'Amore 	}
732*bdb9230aSGarrett D'Amore 	/* Check the ports every 5 seconds */
733*bdb9230aSGarrett D'Amore 	mac->portmon_timer = timeout((void (*)(void*))mii_portmon, (void *)mac,
734*bdb9230aSGarrett D'Amore 	    (clock_t)(5 * drv_usectohz(1000000)));
735*bdb9230aSGarrett D'Amore 	if (mac->lock)
736*bdb9230aSGarrett D'Amore 		mutex_exit(mac->lock);
737*bdb9230aSGarrett D'Amore }
738*bdb9230aSGarrett D'Amore 
739*bdb9230aSGarrett D'Amore /*
740*bdb9230aSGarrett D'Amore  * Close a handle to the MII interface from a registered user
741*bdb9230aSGarrett D'Amore  */
742*bdb9230aSGarrett D'Amore 
743*bdb9230aSGarrett D'Amore void
mii_destroy(mii_handle_t mac)744*bdb9230aSGarrett D'Amore mii_destroy(mii_handle_t mac)
745*bdb9230aSGarrett D'Amore {
746*bdb9230aSGarrett D'Amore 	/* Free per-PHY information */
747*bdb9230aSGarrett D'Amore 	int i;
748*bdb9230aSGarrett D'Amore 
749*bdb9230aSGarrett D'Amore 	(void) mii_stop_portmon(mac);
750*bdb9230aSGarrett D'Amore 
751*bdb9230aSGarrett D'Amore 	for (i = 0; i < 32; i++)
752*bdb9230aSGarrett D'Amore 		if (mac->phys[i])
753*bdb9230aSGarrett D'Amore 			kmem_free(mac->phys[i], sizeof (struct phydata));
754*bdb9230aSGarrett D'Amore 
755*bdb9230aSGarrett D'Amore 	kmem_free(mac, sizeof (*mac));
756*bdb9230aSGarrett D'Amore }
757*bdb9230aSGarrett D'Amore 
758*bdb9230aSGarrett D'Amore /*
759*bdb9230aSGarrett D'Amore  * Get a PHY data structure from an MII handle, and validate the common
760*bdb9230aSGarrett D'Amore  * parameters to the MII functions. Used to verify parameters in most MII
761*bdb9230aSGarrett D'Amore  * functions
762*bdb9230aSGarrett D'Amore  */
763*bdb9230aSGarrett D'Amore static struct phydata *
mii_get_valid_phydata(mii_handle_t mac,int phy)764*bdb9230aSGarrett D'Amore mii_get_valid_phydata(mii_handle_t mac, int phy)
765*bdb9230aSGarrett D'Amore {
766*bdb9230aSGarrett D'Amore 	if (!mac || phy > 31 || phy < 0 || !mac->phys[phy]) {
767*bdb9230aSGarrett D'Amore 		ASSERT(!"MII: Bad invocation");
768*bdb9230aSGarrett D'Amore 		return (NULL);
769*bdb9230aSGarrett D'Amore 	}
770*bdb9230aSGarrett D'Amore 	return (mac->phys[phy]);
771*bdb9230aSGarrett D'Amore }
772*bdb9230aSGarrett D'Amore /*
773*bdb9230aSGarrett D'Amore  * Device-specific routines - National Semiconductor
774*bdb9230aSGarrett D'Amore  */
775*bdb9230aSGarrett D'Amore 
776*bdb9230aSGarrett D'Amore #define	BIT(bit, value) ((value) & (1<<(bit)))
777*bdb9230aSGarrett D'Amore static void
dump_NS83840(mii_handle_t mac,int phy)778*bdb9230aSGarrett D'Amore dump_NS83840(mii_handle_t mac, int phy)
779*bdb9230aSGarrett D'Amore {
780*bdb9230aSGarrett D'Amore 	ushort_t reg;
781*bdb9230aSGarrett D'Amore 	void *dip;
782*bdb9230aSGarrett D'Amore 
783*bdb9230aSGarrett D'Amore 	dip = mac->mii_dip;
784*bdb9230aSGarrett D'Amore 	cmn_err(CE_NOTE, "Disconnect count: %x",
785*bdb9230aSGarrett D'Amore 	    mac->mii_read(dip, phy, 0x12));
786*bdb9230aSGarrett D'Amore 	cmn_err(CE_NOTE, "False Carrier detect count: %x",
787*bdb9230aSGarrett D'Amore 	    mac->mii_read(dip, phy, 0x13));
788*bdb9230aSGarrett D'Amore 	cmn_err(CE_NOTE, "Receive error count: %x",
789*bdb9230aSGarrett D'Amore 	    mac->mii_read(dip, phy, 0x15));
790*bdb9230aSGarrett D'Amore 	cmn_err(CE_NOTE, "Silicon revision: %x",
791*bdb9230aSGarrett D'Amore 	    mac->mii_read(dip, phy, 0x16));
792*bdb9230aSGarrett D'Amore 	cmn_err(CE_NOTE, "PCS Configuration : %x",
793*bdb9230aSGarrett D'Amore 	    mac->mii_read(dip, phy, 0x17));
794*bdb9230aSGarrett D'Amore 
795*bdb9230aSGarrett D'Amore 	cmn_err(CE_NOTE, "Loopback, Bypass and Receiver error mask: %x",
796*bdb9230aSGarrett D'Amore 	    mac->mii_read(dip, phy, 0x18));
797*bdb9230aSGarrett D'Amore 	cmn_err(CE_NOTE, "Wired phy address: %x",
798*bdb9230aSGarrett D'Amore 	    mac->mii_read(dip, phy, 0x19)&0xf);
799*bdb9230aSGarrett D'Amore 
800*bdb9230aSGarrett D'Amore 	reg = mac->mii_read(dip, phy, 0x1b);
801*bdb9230aSGarrett D'Amore 	cmn_err(CE_NOTE, "10 Base T in %s mode",
802*bdb9230aSGarrett D'Amore 	    BIT(9, reg) ? "serial":"nibble");
803*bdb9230aSGarrett D'Amore 
804*bdb9230aSGarrett D'Amore 	cmn_err(CE_NOTE, "%slink pulses, %sheartbeat, %s,%s squelch,jabber %s",
805*bdb9230aSGarrett D'Amore 	    BIT(reg, 5) ? "" : "no ",
806*bdb9230aSGarrett D'Amore 	    BIT(reg, 4) ? "" : "no ",
807*bdb9230aSGarrett D'Amore 	    BIT(reg, 3) ? "UTP" : "STP",
808*bdb9230aSGarrett D'Amore 	    BIT(reg, 2) ? "low" : "normal",
809*bdb9230aSGarrett D'Amore 	    BIT(reg, 0) ? "enabled" : "disabled");
810*bdb9230aSGarrett D'Amore }
811*bdb9230aSGarrett D'Amore 
812*bdb9230aSGarrett D'Amore static int
getspeed_NS83840(mii_handle_t mac,int phy,int * speed,int * fulld)813*bdb9230aSGarrett D'Amore getspeed_NS83840(mii_handle_t mac, int phy, int *speed, int *fulld)
814*bdb9230aSGarrett D'Amore {
815*bdb9230aSGarrett D'Amore 	int exten =  mac->mii_read(mac->mii_dip, phy, MII_AN_EXPANSION);
816*bdb9230aSGarrett D'Amore 	if (exten & MII_AN_EXP_LPCANAN) {
817*bdb9230aSGarrett D'Amore 		/*
818*bdb9230aSGarrett D'Amore 		 * Link partner can auto-neg, take speed from LP Ability
819*bdb9230aSGarrett D'Amore 		 * register
820*bdb9230aSGarrett D'Amore 		 */
821*bdb9230aSGarrett D'Amore 		int lpable, anadv, mask;
822*bdb9230aSGarrett D'Amore 
823*bdb9230aSGarrett D'Amore 		lpable = mac->mii_read(mac->mii_dip, phy, MII_AN_LPABLE);
824*bdb9230aSGarrett D'Amore 		anadv = mac->mii_read(mac->mii_dip, phy, MII_AN_ADVERT);
825*bdb9230aSGarrett D'Amore 		mask = anadv & lpable;
826*bdb9230aSGarrett D'Amore 
827*bdb9230aSGarrett D'Amore 		if (mask & MII_ABILITY_100BASE_TX_FD) {
828*bdb9230aSGarrett D'Amore 			*speed = 100;
829*bdb9230aSGarrett D'Amore 			*fulld = 1;
830*bdb9230aSGarrett D'Amore 		} else if (mask & MII_ABILITY_100BASE_T4) {
831*bdb9230aSGarrett D'Amore 			*speed = 100;
832*bdb9230aSGarrett D'Amore 			*fulld = 0;
833*bdb9230aSGarrett D'Amore 		} else if (mask & MII_ABILITY_100BASE_TX) {
834*bdb9230aSGarrett D'Amore 			*speed = 100;
835*bdb9230aSGarrett D'Amore 			*fulld = 0;
836*bdb9230aSGarrett D'Amore 		} else if (mask & MII_ABILITY_10BASE_T_FD) {
837*bdb9230aSGarrett D'Amore 			*speed = 10;
838*bdb9230aSGarrett D'Amore 			*fulld = 1;
839*bdb9230aSGarrett D'Amore 		} else if (mask & MII_ABILITY_10BASE_T) {
840*bdb9230aSGarrett D'Amore 			*speed = 10;
841*bdb9230aSGarrett D'Amore 			*fulld = 0;
842*bdb9230aSGarrett D'Amore 		}
843*bdb9230aSGarrett D'Amore 	} else {
844*bdb9230aSGarrett D'Amore 		int addr = mac->mii_read(mac->mii_dip, phy, MII_83840_ADDR);
845*bdb9230aSGarrett D'Amore 		*speed = (addr & NS83840_ADDR_SPEED10) ? 10:100;
846*bdb9230aSGarrett D'Amore 		/* No fullduplex without autonegotiation on link partner */
847*bdb9230aSGarrett D'Amore 		*fulld = 0;
848*bdb9230aSGarrett D'Amore 	}
849*bdb9230aSGarrett D'Amore 	return (0);
850*bdb9230aSGarrett D'Amore }
851*bdb9230aSGarrett D'Amore 
852*bdb9230aSGarrett D'Amore /*
853*bdb9230aSGarrett D'Amore  * Device-specific routines - INTEL
854*bdb9230aSGarrett D'Amore  */
855*bdb9230aSGarrett D'Amore 
856*bdb9230aSGarrett D'Amore static int
getspeed_82553(mii_handle_t mac,int phy,int * speed,int * fulld)857*bdb9230aSGarrett D'Amore getspeed_82553(mii_handle_t mac, int phy, int *speed, int *fulld)
858*bdb9230aSGarrett D'Amore {
859*bdb9230aSGarrett D'Amore 	int ex0 = mac->mii_read(mac->mii_dip, phy, MII_82553_EX0);
860*bdb9230aSGarrett D'Amore 	*fulld = (ex0 & I82553_EX0_FDUPLEX) ? 1:0;
861*bdb9230aSGarrett D'Amore 	*speed = (ex0 & I82553_EX0_100MB) ? 100:10;
862*bdb9230aSGarrett D'Amore 	return (0);
863*bdb9230aSGarrett D'Amore }
864*bdb9230aSGarrett D'Amore 
865*bdb9230aSGarrett D'Amore /*
866*bdb9230aSGarrett D'Amore  * Device-specific routines - ICS
867*bdb9230aSGarrett D'Amore  */
868*bdb9230aSGarrett D'Amore 
869*bdb9230aSGarrett D'Amore static int
getspeed_ICS1890(mii_handle_t mac,int phy,int * speed,int * fulld)870*bdb9230aSGarrett D'Amore getspeed_ICS1890(mii_handle_t mac, int phy, int *speed, int *fulld)
871*bdb9230aSGarrett D'Amore {
872*bdb9230aSGarrett D'Amore 	ushort_t quickpoll = mac->mii_read(mac->mii_dip, phy, ICS_QUICKPOLL);
873*bdb9230aSGarrett D'Amore 	*speed = (quickpoll & ICS_QUICKPOLL_100MB) ? 100 : 10;
874*bdb9230aSGarrett D'Amore 	*fulld = (quickpoll & ICS_QUICKPOLL_FDUPLEX) ? 1 : 0;
875*bdb9230aSGarrett D'Amore 	return (0);
876*bdb9230aSGarrett D'Amore }
877*bdb9230aSGarrett D'Amore 
878*bdb9230aSGarrett D'Amore static void
dump_ICS1890(mii_handle_t mac,int phy)879*bdb9230aSGarrett D'Amore dump_ICS1890(mii_handle_t mac, int phy)
880*bdb9230aSGarrett D'Amore {
881*bdb9230aSGarrett D'Amore 	ushort_t quickpoll = mac->mii_read(mac->mii_dip, phy, ICS_QUICKPOLL);
882*bdb9230aSGarrett D'Amore 	cmn_err(CE_NOTE, "QuickPoll:%x (Speed:%d FullDuplex:%c) ",
883*bdb9230aSGarrett D'Amore 	    quickpoll,
884*bdb9230aSGarrett D'Amore 	    quickpoll & ICS_QUICKPOLL_100MB ? 100:10,
885*bdb9230aSGarrett D'Amore 	    quickpoll & ICS_QUICKPOLL_FDUPLEX ? 'Y' : 'N');
886*bdb9230aSGarrett D'Amore }
887*bdb9230aSGarrett D'Amore 
888*bdb9230aSGarrett D'Amore static void
postreset_NS83840(mii_handle_t mac,int phy)889*bdb9230aSGarrett D'Amore postreset_NS83840(mii_handle_t mac, int phy)
890*bdb9230aSGarrett D'Amore {
891*bdb9230aSGarrett D'Amore 	ushort_t reg;
892*bdb9230aSGarrett D'Amore 	struct phydata *phyd = mac->phys[phy];
893*bdb9230aSGarrett D'Amore 	/*
894*bdb9230aSGarrett D'Amore 	 * As per INTEL "PRO/100B Adapter Software Technical
895*bdb9230aSGarrett D'Amore 	 * Reference Manual", set bit 10 of MII register 23.
896*bdb9230aSGarrett D'Amore 	 * National Semiconductor documentation shows this as
897*bdb9230aSGarrett D'Amore 	 * "reserved, write to as zero". We also set the
898*bdb9230aSGarrett D'Amore 	 * "f_connect" bit, also as requested by the PRO/100B
899*bdb9230aSGarrett D'Amore 	 * doc
900*bdb9230aSGarrett D'Amore 	 */
901*bdb9230aSGarrett D'Amore 
902*bdb9230aSGarrett D'Amore 	reg = mac->mii_read(mac->mii_dip, phy, 23) | (1<<10) | (1<<5);
903*bdb9230aSGarrett D'Amore 	mac->mii_write(mac->mii_dip, phy, 23, reg);
904*bdb9230aSGarrett D'Amore 
905*bdb9230aSGarrett D'Amore 	/*
906*bdb9230aSGarrett D'Amore 	 * Some of thses PHYs seem to reset with the wrong value in the
907*bdb9230aSGarrett D'Amore 	 * AN advertisment register. It should containt 1e1, indicating that
908*bdb9230aSGarrett D'Amore 	 * the device can do 802.3 10BASE-T, 10BASE-T Full duplex, 100BASE-TX,
909*bdb9230aSGarrett D'Amore 	 * and 100 BASE-TX full duplex. Instead it seems to advertise only
910*bdb9230aSGarrett D'Amore 	 * 100BASE-TX Full duplex. The result of this is that the device will
911*bdb9230aSGarrett D'Amore 	 * NOT autonegotiate at all against a 10MB only or 100MB/Half duplex
912*bdb9230aSGarrett D'Amore 	 * autonegotiating hub
913*bdb9230aSGarrett D'Amore 	 * NEEDSWORK:
914*bdb9230aSGarrett D'Amore 	 * There is possibly a time-dependancy here.
915*bdb9230aSGarrett D'Amore 	 * If the autonegotiation has completed BEFORE we get to here
916*bdb9230aSGarrett D'Amore 	 * (after the reset) then this could possibly have not effect
917*bdb9230aSGarrett D'Amore 	 */
918*bdb9230aSGarrett D'Amore 	if (!phyd->fix_speed) {
919*bdb9230aSGarrett D'Amore #ifdef MIIDEBUG
920*bdb9230aSGarrett D'Amore 		if (miidebug & MIICOMPAT)
921*bdb9230aSGarrett D'Amore 			cmn_err(CE_NOTE, "Reset value of AN_ADV reg:%x",
922*bdb9230aSGarrett D'Amore 			    mac->mii_read(mac->mii_dip, phy, MII_AN_ADVERT));
923*bdb9230aSGarrett D'Amore #endif
924*bdb9230aSGarrett D'Amore 		mac->mii_write(mac->mii_dip, phy, MII_AN_ADVERT, 0x1e1);
925*bdb9230aSGarrett D'Amore 	}
926*bdb9230aSGarrett D'Amore }
927*bdb9230aSGarrett D'Amore 
928*bdb9230aSGarrett D'Amore void
postreset_ICS1890(mii_handle_t mac,int phy)929*bdb9230aSGarrett D'Amore postreset_ICS1890(mii_handle_t mac, int phy)
930*bdb9230aSGarrett D'Amore {
931*bdb9230aSGarrett D'Amore 	/* This device comes up isolated if no link is found */
932*bdb9230aSGarrett D'Amore 	(void) mii_unisolate(mac, phy);
933*bdb9230aSGarrett D'Amore }
934*bdb9230aSGarrett D'Amore 
935*bdb9230aSGarrett D'Amore /*
936*bdb9230aSGarrett D'Amore  * generic getspeed routine
937*bdb9230aSGarrett D'Amore  */
938*bdb9230aSGarrett D'Amore static int
getspeed_generic(mii_handle_t mac,int phy,int * speed,int * fulld)939*bdb9230aSGarrett D'Amore getspeed_generic(mii_handle_t mac, int phy, int *speed, int *fulld)
940*bdb9230aSGarrett D'Amore {
941*bdb9230aSGarrett D'Amore 	int exten =  mac->mii_read(mac->mii_dip, phy, MII_AN_EXPANSION);
942*bdb9230aSGarrett D'Amore 	if (exten & MII_AN_EXP_LPCANAN) {
943*bdb9230aSGarrett D'Amore 		/*
944*bdb9230aSGarrett D'Amore 		 * Link partner can auto-neg, take speed from LP Ability
945*bdb9230aSGarrett D'Amore 		 * register
946*bdb9230aSGarrett D'Amore 		 */
947*bdb9230aSGarrett D'Amore 		int lpable, anadv, mask;
948*bdb9230aSGarrett D'Amore 
949*bdb9230aSGarrett D'Amore 		lpable = mac->mii_read(mac->mii_dip, phy, MII_AN_LPABLE);
950*bdb9230aSGarrett D'Amore 		anadv = mac->mii_read(mac->mii_dip, phy, MII_AN_ADVERT);
951*bdb9230aSGarrett D'Amore 		mask = anadv & lpable;
952*bdb9230aSGarrett D'Amore 
953*bdb9230aSGarrett D'Amore 		if (mask & MII_ABILITY_100BASE_TX_FD) {
954*bdb9230aSGarrett D'Amore 			*speed = 100;
955*bdb9230aSGarrett D'Amore 			*fulld = 1;
956*bdb9230aSGarrett D'Amore 		} else if (mask & MII_ABILITY_100BASE_T4) {
957*bdb9230aSGarrett D'Amore 			*speed = 100;
958*bdb9230aSGarrett D'Amore 			*fulld = 0;
959*bdb9230aSGarrett D'Amore 		} else if (mask & MII_ABILITY_100BASE_TX) {
960*bdb9230aSGarrett D'Amore 			*speed = 100;
961*bdb9230aSGarrett D'Amore 			*fulld = 0;
962*bdb9230aSGarrett D'Amore 		} else if (mask & MII_ABILITY_10BASE_T_FD) {
963*bdb9230aSGarrett D'Amore 			*speed = 10;
964*bdb9230aSGarrett D'Amore 			*fulld = 1;
965*bdb9230aSGarrett D'Amore 		} else if (mask & MII_ABILITY_10BASE_T) {
966*bdb9230aSGarrett D'Amore 			*speed = 10;
967*bdb9230aSGarrett D'Amore 			*fulld = 0;
968*bdb9230aSGarrett D'Amore 		}
969*bdb9230aSGarrett D'Amore 	} else {
970*bdb9230aSGarrett D'Amore 		/*
971*bdb9230aSGarrett D'Amore 		 * Link partner cannot auto-neg, it would be nice if we
972*bdb9230aSGarrett D'Amore 		 * could figure out what the device selected.  (NWay?)
973*bdb9230aSGarrett D'Amore 		 */
974*bdb9230aSGarrett D'Amore 		*speed = 0;
975*bdb9230aSGarrett D'Amore 		*fulld = 0;
976*bdb9230aSGarrett D'Amore 	}
977*bdb9230aSGarrett D'Amore 	return (MII_SUCCESS);
978*bdb9230aSGarrett D'Amore }
979