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