utopia.c revision 147256
1/*- 2 * Copyright (c) 2003 3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4 * All rights reserved. 5 * 6 * Author: Hartmut Brandt <harti@freebsd.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: head/sys/dev/utopia/utopia.c 147256 2005-06-10 16:49:24Z brooks $"); 32 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/unistd.h> 36#include <sys/kernel.h> 37#include <sys/kthread.h> 38#include <sys/proc.h> 39#include <sys/bus.h> 40#include <sys/malloc.h> 41#include <sys/module.h> 42#include <sys/sysctl.h> 43#include <sys/lock.h> 44#include <sys/mutex.h> 45#include <sys/socket.h> 46 47#include <net/if.h> 48#include <net/if_var.h> 49#include <net/if_media.h> 50#include <net/if_atm.h> 51 52#include <dev/utopia/suni.h> 53#include <dev/utopia/idtphy.h> 54#include <dev/utopia/utopia.h> 55#include <dev/utopia/utopia_priv.h> 56 57/* known chips */ 58extern const struct utopia_chip utopia_chip_idt77155; 59extern const struct utopia_chip utopia_chip_idt77105; 60extern const struct utopia_chip utopia_chip_lite; 61extern const struct utopia_chip utopia_chip_ultra; 62extern const struct utopia_chip utopia_chip_622; 63 64/* 65 * Global list of all registered interfaces 66 */ 67static struct mtx utopia_list_mtx; 68static LIST_HEAD(, utopia) utopia_list = LIST_HEAD_INITIALIZER(utopia_list); 69 70#define UTP_RLOCK_LIST() mtx_lock(&utopia_list_mtx) 71#define UTP_RUNLOCK_LIST() mtx_unlock(&utopia_list_mtx) 72#define UTP_WLOCK_LIST() mtx_lock(&utopia_list_mtx) 73#define UTP_WUNLOCK_LIST() mtx_unlock(&utopia_list_mtx) 74 75#define UTP_LOCK(UTP) mtx_lock((UTP)->lock) 76#define UTP_UNLOCK(UTP) mtx_unlock((UTP)->lock) 77#define UTP_LOCK_ASSERT(UTP) mtx_assert((UTP)->lock, MA_OWNED) 78 79static struct proc *utopia_kproc; 80 81static void utopia_dump(struct utopia *) __unused; 82 83/* 84 * Read a multi-register value. 85 */ 86uint32_t 87utopia_update(struct utopia *utp, u_int reg, u_int nreg, uint32_t mask) 88{ 89 int err; 90 u_int n; 91 uint8_t regs[4]; 92 uint32_t val; 93 94 n = nreg; 95 if ((err = UTP_READREGS(utp, reg, regs, &n)) != 0) { 96#ifdef DIAGNOSTIC 97 printf("%s: register read error %s(%u,%u): %d\n", __func__, 98 utp->chip->name, reg, nreg, err); 99#endif 100 return (0); 101 } 102 if (n < nreg) { 103#ifdef DIAGNOSTIC 104 printf("%s: got only %u regs %s(%u,%u): %d\n", __func__, n, 105 utp->chip->name, reg, nreg, err); 106#endif 107 return (0); 108 } 109 val = 0; 110 for (n = nreg; n > 0; n--) { 111 val <<= 8; 112 val |= regs[n - 1]; 113 } 114 return (val & mask); 115} 116 117/* 118 * Debugging - dump all registers. 119 */ 120static void 121utopia_dump(struct utopia *utp) 122{ 123 uint8_t regs[256]; 124 u_int n = 256, i; 125 int err; 126 127 if ((err = UTP_READREGS(utp, 0, regs, &n)) != 0) { 128 printf("UTOPIA reg read error %d\n", err); 129 return; 130 } 131 for (i = 0; i < n; i++) { 132 if (i % 16 == 0) 133 printf("%02x:", i); 134 if (i % 16 == 8) 135 printf(" "); 136 printf(" %02x", regs[i]); 137 if (i % 16 == 15) 138 printf("\n"); 139 } 140 if (i % 16 != 0) 141 printf("\n"); 142} 143 144/* 145 * Update the carrier status 146 */ 147void 148utopia_check_carrier(struct utopia *utp, u_int carr_ok) 149{ 150 int old; 151 152 old = utp->carrier; 153 if (carr_ok) { 154 /* carrier */ 155 utp->carrier = UTP_CARR_OK; 156 if (old != UTP_CARR_OK) { 157 if_printf(utp->ifatm->ifp, "carrier detected\n"); 158 ATMEV_SEND_IFSTATE_CHANGED(utp->ifatm, 1); 159 } 160 } else { 161 /* no carrier */ 162 utp->carrier = UTP_CARR_LOST; 163 if (old == UTP_CARR_OK) { 164 if_printf(utp->ifatm->ifp, "carrier lost\n"); 165 ATMEV_SEND_IFSTATE_CHANGED(utp->ifatm, 0); 166 } 167 } 168} 169 170static int 171unknown_inval(struct utopia *utp, int what __unused) 172{ 173 174 return (EINVAL); 175} 176 177static int 178unknown_reset(struct utopia *utp __unused) 179{ 180 return (EIO); 181} 182 183static int 184unknown_update_carrier(struct utopia *utp) 185{ 186 utp->carrier = UTP_CARR_UNKNOWN; 187 return (0); 188} 189 190static int 191unknown_set_loopback(struct utopia *utp __unused, u_int mode __unused) 192{ 193 return (EINVAL); 194} 195 196static void 197unknown_intr(struct utopia *utp __unused) 198{ 199} 200 201static void 202unknown_update_stats(struct utopia *utp __unused) 203{ 204} 205 206static const struct utopia_chip utopia_chip_unknown = { 207 UTP_TYPE_UNKNOWN, 208 "unknown", 209 0, 210 unknown_reset, 211 unknown_inval, 212 unknown_inval, 213 unknown_inval, 214 unknown_update_carrier, 215 unknown_set_loopback, 216 unknown_intr, 217 unknown_update_stats, 218}; 219 220/* 221 * Callbacks for the ifmedia infrastructure. 222 */ 223static int 224utopia_media_change(struct ifnet *ifp) 225{ 226 struct ifatm *ifatm = (struct ifatm *)ifp->if_softc; 227 struct utopia *utp = ifatm->phy; 228 int error = 0; 229 230 UTP_LOCK(utp); 231 if (utp->chip->type != UTP_TYPE_UNKNOWN && utp->state & UTP_ST_ACTIVE) { 232 if (utp->media->ifm_media & IFM_ATM_SDH) { 233 if (!(utp->state & UTP_ST_SDH)) 234 error = utopia_set_sdh(utp, 1); 235 } else { 236 if (utp->state & UTP_ST_SDH) 237 error = utopia_set_sdh(utp, 0); 238 } 239 if (utp->media->ifm_media & IFM_ATM_UNASSIGNED) { 240 if (!(utp->state & UTP_ST_UNASS)) 241 error = utopia_set_unass(utp, 1); 242 } else { 243 if (utp->state & UTP_ST_UNASS) 244 error = utopia_set_unass(utp, 0); 245 } 246 if (utp->media->ifm_media & IFM_ATM_NOSCRAMB) { 247 if (!(utp->state & UTP_ST_NOSCRAMB)) 248 error = utopia_set_noscramb(utp, 1); 249 } else { 250 if (utp->state & UTP_ST_NOSCRAMB) 251 error = utopia_set_noscramb(utp, 0); 252 } 253 } else 254 error = EIO; 255 UTP_UNLOCK(utp); 256 return (error); 257} 258 259/* 260 * Look at the carrier status. 261 */ 262static void 263utopia_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) 264{ 265 struct utopia *utp = ((struct ifatm *)ifp->if_softc)->phy; 266 267 UTP_LOCK(utp); 268 if (utp->chip->type != UTP_TYPE_UNKNOWN && utp->state & UTP_ST_ACTIVE) { 269 ifmr->ifm_active = IFM_ATM | utp->ifatm->mib.media; 270 271 switch (utp->carrier) { 272 273 case UTP_CARR_OK: 274 ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE; 275 break; 276 277 case UTP_CARR_LOST: 278 ifmr->ifm_status = IFM_AVALID; 279 break; 280 281 default: 282 ifmr->ifm_status = 0; 283 break; 284 } 285 if (utp->state & UTP_ST_SDH) { 286 ifmr->ifm_active |= IFM_ATM_SDH; 287 ifmr->ifm_current |= IFM_ATM_SDH; 288 } 289 if (utp->state & UTP_ST_UNASS) { 290 ifmr->ifm_active |= IFM_ATM_UNASSIGNED; 291 ifmr->ifm_current |= IFM_ATM_UNASSIGNED; 292 } 293 if (utp->state & UTP_ST_NOSCRAMB) { 294 ifmr->ifm_active |= IFM_ATM_NOSCRAMB; 295 ifmr->ifm_current |= IFM_ATM_NOSCRAMB; 296 } 297 } else { 298 ifmr->ifm_active = 0; 299 ifmr->ifm_status = 0; 300 } 301 UTP_UNLOCK(utp); 302} 303 304/* 305 * Initialize media from the mib 306 */ 307void 308utopia_init_media(struct utopia *utp) 309{ 310 311 ifmedia_removeall(utp->media); 312 ifmedia_add(utp->media, IFM_ATM | utp->ifatm->mib.media, 0, NULL); 313 ifmedia_set(utp->media, IFM_ATM | utp->ifatm->mib.media); 314} 315 316/* 317 * Reset all media 318 */ 319void 320utopia_reset_media(struct utopia *utp) 321{ 322 323 ifmedia_removeall(utp->media); 324} 325 326/* 327 * This is called by the driver as soon as the SUNI registers are accessible. 328 * This may be either in the attach routine or the init routine of the driver. 329 */ 330int 331utopia_start(struct utopia *utp) 332{ 333 uint8_t reg; 334 int err; 335 u_int n = 1; 336 337 /* 338 * Try to find out what chip we have 339 */ 340 if ((err = UTP_READREGS(utp, SUNI_REGO_MRESET, ®, &n)) != 0) 341 return (err); 342 343 switch (reg & SUNI_REGM_MRESET_TYPE) { 344 345 case SUNI_REGM_MRESET_TYPE_622: 346 utp->chip = &utopia_chip_622; 347 break; 348 349 case SUNI_REGM_MRESET_TYPE_LITE: 350 /* this may be either a SUNI LITE or a IDT77155 * 351 * Read register 0x70. The SUNI doesn't have it */ 352 n = 1; 353 if ((err = UTP_READREGS(utp, IDTPHY_REGO_RBER, ®, &n)) != 0) 354 return (err); 355 if ((reg & ~IDTPHY_REGM_RBER_RESV) == 356 (IDTPHY_REGM_RBER_FAIL | IDTPHY_REGM_RBER_WARN)) 357 utp->chip = &utopia_chip_idt77155; 358 else 359 utp->chip = &utopia_chip_lite; 360 break; 361 362 case SUNI_REGM_MRESET_TYPE_ULTRA: 363 utp->chip = &utopia_chip_ultra; 364 break; 365 366 default: 367 if (reg == (IDTPHY_REGM_MCR_DRIC | IDTPHY_REGM_MCR_EI)) 368 utp->chip = &utopia_chip_idt77105; 369 else { 370 if_printf(utp->ifatm->ifp, 371 "unknown ATM-PHY chip %#x\n", reg); 372 utp->chip = &utopia_chip_unknown; 373 } 374 break; 375 } 376 utp->state |= UTP_ST_ACTIVE; 377 return (0); 378} 379 380/* 381 * Stop the chip 382 */ 383void 384utopia_stop(struct utopia *utp) 385{ 386 utp->state &= ~UTP_ST_ACTIVE; 387} 388 389/* 390 * Handle the sysctls 391 */ 392static int 393utopia_sysctl_regs(SYSCTL_HANDLER_ARGS) 394{ 395 struct utopia *utp = (struct utopia *)arg1; 396 int error; 397 u_int n; 398 uint8_t *val; 399 uint8_t new[3]; 400 401 if ((n = utp->chip->nregs) == 0) 402 return (EIO); 403 val = malloc(sizeof(uint8_t) * n, M_TEMP, M_WAITOK); 404 405 UTP_LOCK(utp); 406 error = UTP_READREGS(utp, 0, val, &n); 407 UTP_UNLOCK(utp); 408 409 if (error) { 410 free(val, M_TEMP); 411 return (error); 412 } 413 414 error = SYSCTL_OUT(req, val, sizeof(uint8_t) * n); 415 free(val, M_TEMP); 416 if (error != 0 || req->newptr == NULL) 417 return (error); 418 419 error = SYSCTL_IN(req, new, sizeof(new)); 420 if (error) 421 return (error); 422 423 UTP_LOCK(utp); 424 error = UTP_WRITEREG(utp, new[0], new[1], new[2]); 425 UTP_UNLOCK(utp); 426 427 return (error); 428} 429 430static int 431utopia_sysctl_stats(SYSCTL_HANDLER_ARGS) 432{ 433 struct utopia *utp = (struct utopia *)arg1; 434 void *val; 435 int error; 436 437 val = malloc(sizeof(utp->stats), M_TEMP, M_WAITOK); 438 439 UTP_LOCK(utp); 440 bcopy(&utp->stats, val, sizeof(utp->stats)); 441 if (req->newptr != NULL) 442 bzero((char *)&utp->stats + sizeof(utp->stats.version), 443 sizeof(utp->stats) - sizeof(utp->stats.version)); 444 UTP_UNLOCK(utp); 445 446 error = SYSCTL_OUT(req, val, sizeof(utp->stats)); 447 free(val, M_TEMP); 448 449 if (error && req->newptr != NULL) 450 bcopy(val, &utp->stats, sizeof(utp->stats)); 451 452 /* ignore actual new value */ 453 454 return (error); 455} 456 457/* 458 * Handle the loopback sysctl 459 */ 460static int 461utopia_sysctl_loopback(SYSCTL_HANDLER_ARGS) 462{ 463 struct utopia *utp = (struct utopia *)arg1; 464 int error; 465 u_int loopback; 466 467 error = SYSCTL_OUT(req, &utp->loopback, sizeof(u_int)); 468 if (error != 0 || req->newptr == NULL) 469 return (error); 470 471 error = SYSCTL_IN(req, &loopback, sizeof(u_int)); 472 if (error) 473 return (error); 474 475 UTP_LOCK(utp); 476 error = utopia_set_loopback(utp, loopback); 477 UTP_UNLOCK(utp); 478 479 return (error); 480} 481 482/* 483 * Handle the type sysctl 484 */ 485static int 486utopia_sysctl_type(SYSCTL_HANDLER_ARGS) 487{ 488 struct utopia *utp = (struct utopia *)arg1; 489 490 return (SYSCTL_OUT(req, &utp->chip->type, sizeof(utp->chip->type))); 491} 492 493/* 494 * Handle the name sysctl 495 */ 496static int 497utopia_sysctl_name(SYSCTL_HANDLER_ARGS) 498{ 499 struct utopia *utp = (struct utopia *)arg1; 500 501 return (SYSCTL_OUT(req, utp->chip->name, strlen(utp->chip->name) + 1)); 502} 503 504/* 505 * Initialize the state. This is called from the drivers attach 506 * function. The mutex must be already initialized. 507 */ 508int 509utopia_attach(struct utopia *utp, struct ifatm *ifatm, struct ifmedia *media, 510 struct mtx *lock, struct sysctl_ctx_list *ctx, 511 struct sysctl_oid_list *children, const struct utopia_methods *m) 512{ 513 514 bzero(utp, sizeof(*utp)); 515 utp->ifatm = ifatm; 516 utp->methods = m; 517 utp->media = media; 518 utp->lock = lock; 519 utp->chip = &utopia_chip_unknown; 520 utp->stats.version = 1; 521 522 ifmedia_init(media, 523 IFM_ATM_SDH | IFM_ATM_UNASSIGNED | IFM_ATM_NOSCRAMB, 524 utopia_media_change, utopia_media_status); 525 526 if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_regs", 527 CTLFLAG_RW | CTLTYPE_OPAQUE, utp, 0, utopia_sysctl_regs, "S", 528 "phy registers") == NULL) 529 return (-1); 530 531 if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_loopback", 532 CTLFLAG_RW | CTLTYPE_UINT, utp, 0, utopia_sysctl_loopback, "IU", 533 "phy loopback mode") == NULL) 534 return (-1); 535 536 if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_type", 537 CTLFLAG_RD | CTLTYPE_UINT, utp, 0, utopia_sysctl_type, "IU", 538 "phy type") == NULL) 539 return (-1); 540 541 if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_name", 542 CTLFLAG_RD | CTLTYPE_STRING, utp, 0, utopia_sysctl_name, "A", 543 "phy name") == NULL) 544 return (-1); 545 546 if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_stats", 547 CTLFLAG_RW | CTLTYPE_OPAQUE, utp, 0, utopia_sysctl_stats, "S", 548 "phy statistics") == NULL) 549 return (-1); 550 551 if (SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "phy_state", 552 CTLFLAG_RD, &utp->state, 0, "phy state") == NULL) 553 return (-1); 554 555 if (SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "phy_carrier", 556 CTLFLAG_RD, &utp->carrier, 0, "phy carrier") == NULL) 557 return (-1); 558 559 UTP_WLOCK_LIST(); 560 LIST_INSERT_HEAD(&utopia_list, utp, link); 561 UTP_WUNLOCK_LIST(); 562 563 utp->state |= UTP_ST_ATTACHED; 564 return (0); 565} 566 567/* 568 * Detach. We set a flag here, wakeup the daemon and let him do it. 569 * Here we need the lock for synchronisation with the daemon. 570 */ 571void 572utopia_detach(struct utopia *utp) 573{ 574 575 UTP_LOCK_ASSERT(utp); 576 if (utp->state & UTP_ST_ATTACHED) { 577 utp->state |= UTP_ST_DETACH; 578 while (utp->state & UTP_ST_DETACH) { 579 wakeup(&utopia_list); 580 msleep(utp, utp->lock, PZERO, "utopia_detach", hz); 581 } 582 } 583} 584 585/* 586 * The carrier state kernel proc for those adapters that do not interrupt. 587 * 588 * We assume, that utopia_attach can safely add a new utopia while we are going 589 * through the list without disturbing us (we lock the list while getting 590 * the address of the first element, adding is always done at the head). 591 * Removing is entirely handled here. 592 */ 593static void 594utopia_daemon(void *arg __unused) 595{ 596 struct utopia *utp, *next; 597 598 UTP_RLOCK_LIST(); 599 while (utopia_kproc != NULL) { 600 utp = LIST_FIRST(&utopia_list); 601 UTP_RUNLOCK_LIST(); 602 603 while (utp != NULL) { 604 mtx_lock(&Giant); /* XXX depend on MPSAFE */ 605 UTP_LOCK(utp); 606 next = LIST_NEXT(utp, link); 607 if (utp->state & UTP_ST_DETACH) { 608 LIST_REMOVE(utp, link); 609 utp->state &= ~UTP_ST_DETACH; 610 wakeup_one(utp); 611 } else if (utp->state & UTP_ST_ACTIVE) { 612 if (utp->flags & UTP_FL_POLL_CARRIER) 613 utopia_update_carrier(utp); 614 utopia_update_stats(utp); 615 } 616 UTP_UNLOCK(utp); 617 mtx_unlock(&Giant); /* XXX depend on MPSAFE */ 618 utp = next; 619 } 620 621 UTP_RLOCK_LIST(); 622 msleep(&utopia_list, &utopia_list_mtx, PZERO, "*idle*", hz); 623 } 624 wakeup_one(&utopia_list); 625 UTP_RUNLOCK_LIST(); 626 kthread_exit(0); 627} 628 629/* 630 * Module initialisation 631 */ 632static int 633utopia_mod_init(module_t mod, int what, void *arg) 634{ 635 int err; 636 struct proc *kp; 637 638 switch (what) { 639 640 case MOD_LOAD: 641 mtx_init(&utopia_list_mtx, "utopia list mutex", NULL, MTX_DEF); 642 err = kthread_create(utopia_daemon, NULL, &utopia_kproc, 643 RFHIGHPID, 0, "utopia"); 644 if (err != 0) { 645 printf("cannot created utopia thread %d\n", err); 646 return (err); 647 } 648 break; 649 650 case MOD_UNLOAD: 651 UTP_WLOCK_LIST(); 652 if ((kp = utopia_kproc) != NULL) { 653 utopia_kproc = NULL; 654 wakeup_one(&utopia_list); 655 PROC_LOCK(kp); 656 UTP_WUNLOCK_LIST(); 657 msleep(kp, &kp->p_mtx, PWAIT, "utopia_destroy", 0); 658 PROC_UNLOCK(kp); 659 } else 660 UTP_WUNLOCK_LIST(); 661 mtx_destroy(&utopia_list_mtx); 662 break; 663 default: 664 return (EOPNOTSUPP); 665 } 666 return (0); 667} 668 669static moduledata_t utopia_mod = { 670 "utopia", 671 utopia_mod_init, 672 0 673}; 674 675DECLARE_MODULE(utopia, utopia_mod, SI_SUB_INIT_IF, SI_ORDER_ANY); 676MODULE_VERSION(utopia, 1); 677