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