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