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: releng/10.2/sys/dev/utopia/idtphy.c 142384 2005-02-24 16:56:36Z harti $"); 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/idtphy.h> 53#include <dev/utopia/utopia.h> 54#include <dev/utopia/utopia_priv.h> 55 56/* 57 * Reset IDT77105. There is really no way to reset this thing by acessing 58 * the registers. Load the registers with default values. 59 */ 60static int 61idt77105_reset(struct utopia *utp) 62{ 63 int err = 0; 64 u_int n; 65 uint8_t val[2]; 66 67 err |= UTP_WRITEREG(utp, IDTPHY_REGO_MCR, 0xff, 68 IDTPHY_REGM_MCR_DRIC | IDTPHY_REGM_MCR_EI); 69 n = 1; 70 err |= UTP_READREGS(utp, IDTPHY_REGO_ISTAT, val, &n); 71 err |= UTP_WRITEREG(utp, IDTPHY_REGO_DIAG, 0xff, 0); 72 err |= UTP_WRITEREG(utp, IDTPHY_REGO_LHEC, 0xff, 0); 73 74 err |= UTP_WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_SEC); 75 n = 2; 76 err |= UTP_READREGS(utp, IDTPHY_REGO_CNT, val, &n); 77 78 err |= UTP_WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_TX); 79 n = 2; 80 err |= UTP_READREGS(utp, IDTPHY_REGO_CNT, val, &n); 81 82 err |= UTP_WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_RX); 83 n = 2; 84 err |= UTP_READREGS(utp, IDTPHY_REGO_CNT, val, &n); 85 86 err |= UTP_WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_HECE); 87 n = 2; 88 err |= UTP_READREGS(utp, IDTPHY_REGO_CNT, val, &n); 89 90 err |= UTP_WRITEREG(utp, IDTPHY_REGO_MCR, IDTPHY_REGM_MCR_DREC, 91 IDTPHY_REGM_MCR_DREC); 92 err |= UTP_WRITEREG(utp, IDTPHY_REGO_DIAG, IDTPHY_REGM_DIAG_RFLUSH, 93 IDTPHY_REGM_DIAG_RFLUSH); 94 95 /* loopback */ 96 err |= utopia_set_loopback(utp, utp->loopback); 97 98 /* update carrier state */ 99 err |= utopia_update_carrier(utp); 100 101 return (err ? EIO : 0); 102} 103 104static int 105idt77105_inval(struct utopia *utp, int what __unused) 106{ 107 108 return (EINVAL); 109} 110 111static int 112idt77105_update_carrier(struct utopia *utp) 113{ 114 int err; 115 uint8_t reg; 116 u_int n = 1; 117 118 if ((err = UTP_READREGS(utp, IDTPHY_REGO_ISTAT, ®, &n)) != 0) { 119 utp->carrier = UTP_CARR_UNKNOWN; 120 return (err); 121 } 122 utopia_check_carrier(utp, reg & IDTPHY_REGM_ISTAT_GOOD); 123 return (0); 124} 125 126static int 127idt77105_set_loopback(struct utopia *utp, u_int mode) 128{ 129 int err; 130 131 switch (mode) { 132 case UTP_LOOP_NONE: 133 err = UTP_WRITEREG(utp, IDTPHY_REGO_DIAG, 134 IDTPHY_REGM_DIAG_LOOP, IDTPHY_REGM_DIAG_LOOP_NONE); 135 break; 136 137 case UTP_LOOP_DIAG: 138 err = UTP_WRITEREG(utp, IDTPHY_REGO_DIAG, 139 IDTPHY_REGM_DIAG_LOOP, IDTPHY_REGM_DIAG_LOOP_PHY); 140 break; 141 142 case UTP_LOOP_LINE: 143 err = UTP_WRITEREG(utp, IDTPHY_REGO_DIAG, 144 IDTPHY_REGM_DIAG_LOOP, IDTPHY_REGM_DIAG_LOOP_LINE); 145 break; 146 147 default: 148 return (EINVAL); 149 } 150 if (err) 151 return (err); 152 utp->loopback = mode; 153 return (0); 154} 155 156/* 157 * Handle interrupt on IDT77105 chip 158 */ 159static void 160idt77105_intr(struct utopia *utp) 161{ 162 uint8_t reg; 163 u_int n = 1; 164 int err; 165 166 /* Interrupt status and ack the interrupt */ 167 if ((err = UTP_READREGS(utp, IDTPHY_REGO_ISTAT, ®, &n)) != 0) { 168 printf("IDT77105 read error %d\n", err); 169 return; 170 } 171 /* check for signal condition */ 172 utopia_check_carrier(utp, reg & IDTPHY_REGM_ISTAT_GOOD); 173} 174 175static void 176idt77105_update_stats(struct utopia *utp) 177{ 178 int err = 0; 179 uint8_t regs[2]; 180 u_int n; 181 182#ifdef DIAGNOSTIC 183#define UDIAG(F,A,B) printf(F, A, B) 184#else 185#define UDIAG(F,A,B) do { } while (0) 186#endif 187 188#define UPD(FIELD, CODE, N, MASK) \ 189 err = UTP_WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, CODE); \ 190 if (err != 0) { \ 191 UDIAG("%s: cannot write CNTS: %d\n", __func__, err); \ 192 return; \ 193 } \ 194 n = N; \ 195 err = UTP_READREGS(utp, IDTPHY_REGO_CNT, regs, &n); \ 196 if (err != 0) { \ 197 UDIAG("%s: cannot read CNT: %d\n", __func__, err); \ 198 return; \ 199 } \ 200 if (n != N) { \ 201 UDIAG("%s: got only %u registers\n", __func__, n); \ 202 return; \ 203 } \ 204 if (N == 1) \ 205 utp->stats.FIELD += (regs[0] & MASK); \ 206 else \ 207 utp->stats.FIELD += (regs[0] | (regs[1] << 8)) & MASK; 208 209 UPD(rx_symerr, IDTPHY_REGM_CNTS_SEC, 1, 0xff); 210 UPD(tx_cells, IDTPHY_REGM_CNTS_TX, 2, 0xffff); 211 UPD(rx_cells, IDTPHY_REGM_CNTS_RX, 2, 0xffff); 212 UPD(rx_uncorr, IDTPHY_REGM_CNTS_HECE, 1, 0x1f); 213 214#undef UDIAG 215#undef UPD 216} 217 218struct utopia_chip utopia_chip_idt77105 = { 219 UTP_TYPE_IDT77105, 220 "IDT77105", 221 7, 222 idt77105_reset, 223 idt77105_inval, 224 idt77105_inval, 225 idt77105_inval, 226 idt77105_update_carrier, 227 idt77105_set_loopback, 228 idt77105_intr, 229 idt77105_update_stats, 230}; 231 232/* 233 * Update the carrier status 234 */ 235static int 236idt77155_update_carrier(struct utopia *utp) 237{ 238 int err; 239 uint8_t reg; 240 u_int n = 1; 241 242 if ((err = UTP_READREGS(utp, IDTPHY_REGO_RSOS, ®, &n)) != 0) { 243 utp->carrier = UTP_CARR_UNKNOWN; 244 return (err); 245 } 246 utopia_check_carrier(utp, !(reg & IDTPHY_REGM_RSOS_LOS)); 247 return (0); 248} 249 250/* 251 * Handle interrupt on IDT77155 chip 252 */ 253static void 254idt77155_intr(struct utopia *utp) 255{ 256 uint8_t reg; 257 u_int n = 1; 258 int err; 259 260 if ((err = UTP_READREGS(utp, IDTPHY_REGO_RSOS, ®, &n)) != 0) { 261 printf("IDT77105 read error %d\n", err); 262 return; 263 } 264 utopia_check_carrier(utp, !(reg & IDTPHY_REGM_RSOS_LOS)); 265} 266 267/* 268 * set SONET/SDH mode 269 */ 270static int 271idt77155_set_sdh(struct utopia *utp, int sdh) 272{ 273 int err; 274 275 if (sdh) 276 err = UTP_WRITEREG(utp, IDTPHY_REGO_PTRM, 277 IDTPHY_REGM_PTRM_SS, IDTPHY_REGM_PTRM_SDH); 278 else 279 err = UTP_WRITEREG(utp, IDTPHY_REGO_PTRM, 280 IDTPHY_REGM_PTRM_SS, IDTPHY_REGM_PTRM_SONET); 281 if (err != 0) 282 return (err); 283 284 utp->state &= ~UTP_ST_SDH; 285 if (sdh) 286 utp->state |= UTP_ST_SDH; 287 288 return (0); 289} 290 291/* 292 * set idle/unassigned cells 293 */ 294static int 295idt77155_set_unass(struct utopia *utp, int unass) 296{ 297 int err; 298 299 if (unass) 300 err = UTP_WRITEREG(utp, IDTPHY_REGO_TCHP, 0xff, 0); 301 else 302 err = UTP_WRITEREG(utp, IDTPHY_REGO_TCHP, 0xff, 1); 303 if (err != 0) 304 return (err); 305 306 utp->state &= ~UTP_ST_UNASS; 307 if (unass) 308 utp->state |= UTP_ST_UNASS; 309 310 return (0); 311} 312 313/* 314 * enable/disable scrambling 315 */ 316static int 317idt77155_set_noscramb(struct utopia *utp, int noscramb) 318{ 319 int err; 320 321 if (noscramb) { 322 err = UTP_WRITEREG(utp, IDTPHY_REGO_TCC, 323 IDTPHY_REGM_TCC_DSCR, IDTPHY_REGM_TCC_DSCR); 324 if (err) 325 return (err); 326 err = UTP_WRITEREG(utp, IDTPHY_REGO_RCC, 327 IDTPHY_REGM_RCC_DSCR, IDTPHY_REGM_RCC_DSCR); 328 if (err) 329 return (err); 330 utp->state |= UTP_ST_NOSCRAMB; 331 } else { 332 err = UTP_WRITEREG(utp, IDTPHY_REGO_TCC, 333 IDTPHY_REGM_TCC_DSCR, 0); 334 if (err) 335 return (err); 336 err = UTP_WRITEREG(utp, IDTPHY_REGO_RCC, 337 IDTPHY_REGM_RCC_DSCR, 0); 338 if (err) 339 return (err); 340 utp->state &= ~UTP_ST_NOSCRAMB; 341 } 342 return (0); 343} 344 345/* 346 * Set loopback mode for the 77155 347 */ 348static int 349idt77155_set_loopback(struct utopia *utp, u_int mode) 350{ 351 int err; 352 uint32_t val; 353 u_int nmode; 354 355 val = 0; 356 nmode = mode; 357 if (mode & UTP_LOOP_TIME) { 358 nmode &= ~UTP_LOOP_TIME; 359 val |= IDTPHY_REGM_MCTL_TLOOP; 360 } 361 if (mode & UTP_LOOP_DIAG) { 362 nmode &= ~UTP_LOOP_DIAG; 363 val |= IDTPHY_REGM_MCTL_DLOOP; 364 } 365 if (mode & UTP_LOOP_LINE) { 366 nmode &= ~UTP_LOOP_LINE; 367 val |= IDTPHY_REGM_MCTL_LLOOP; 368 } 369 if (nmode != 0) 370 return (EINVAL); 371 372 err = UTP_WRITEREG(utp, IDTPHY_REGO_MCTL, IDTPHY_REGM_MCTL_TLOOP | 373 IDTPHY_REGM_MCTL_DLOOP | IDTPHY_REGM_MCTL_LLOOP, val); 374 if (err) 375 return (err); 376 utp->loopback = mode; 377 378 return (0); 379} 380 381/* 382 * Set the chip to reflect the current state in utopia. 383 * Assume, that the chip has been reset. 384 */ 385static int 386idt77155_set_chip(struct utopia *utp) 387{ 388 int err = 0; 389 390 /* set sonet/sdh */ 391 err |= idt77155_set_sdh(utp, utp->state & UTP_ST_SDH); 392 393 /* unassigned or idle cells */ 394 err |= idt77155_set_unass(utp, utp->state & UTP_ST_UNASS); 395 396 /* loopback */ 397 err |= idt77155_set_loopback(utp, utp->loopback); 398 399 /* update carrier state */ 400 err |= idt77155_update_carrier(utp); 401 402 /* enable interrupts on LOS */ 403 err |= UTP_WRITEREG(utp, IDTPHY_REGO_INT, 404 IDTPHY_REGM_INT_RXSOHI, IDTPHY_REGM_INT_RXSOHI); 405 err |= UTP_WRITEREG(utp, IDTPHY_REGO_RSOC, 406 IDTPHY_REGM_RSOC_LOSI, IDTPHY_REGM_RSOC_LOSI); 407 408 return (err ? EIO : 0); 409} 410 411/* 412 * Reset the chip to reflect the current state of utopia. 413 */ 414static int 415idt77155_reset(struct utopia *utp) 416{ 417 int err = 0; 418 419 if (!(utp->flags & UTP_FL_NORESET)) { 420 err |= UTP_WRITEREG(utp, IDTPHY_REGO_MRID, 421 IDTPHY_REGM_MRID_RESET, IDTPHY_REGM_MRID_RESET); 422 err |= UTP_WRITEREG(utp, IDTPHY_REGO_MRID, 423 IDTPHY_REGM_MRID_RESET, 0); 424 } 425 426 err |= idt77155_set_chip(utp); 427 428 return (err ? EIO : 0); 429} 430 431/* 432 * Update statistics from a IDT77155 433 * This appears to be the same as for the Suni/Lite and Ultra. IDT however 434 * makes no assessment about the transfer time. Assume 7us. 435 */ 436static void 437idt77155_update_stats(struct utopia *utp) 438{ 439 int err; 440 441 /* write to the master if we can */ 442 if (!(utp->flags & UTP_FL_NORESET)) { 443 err = UTP_WRITEREG(utp, IDTPHY_REGO_MRID, 0, 0); 444 } else { 445 err = UTP_WRITEREG(utp, IDTPHY_REGO_BIPC, 0, 0); 446 err |= UTP_WRITEREG(utp, IDTPHY_REGO_B2EC, 0, 0); 447 err |= UTP_WRITEREG(utp, IDTPHY_REGO_B3EC, 0, 0); 448 err |= UTP_WRITEREG(utp, IDTPHY_REGO_CEC, 0, 0); 449 err |= UTP_WRITEREG(utp, IDTPHY_REGO_TXCNT, 0, 0); 450 451 } 452 if (err) { 453#ifdef DIAGNOSTIC 454 printf("%s: register write error %s: %d\n", __func__, 455 utp->chip->name, err); 456#endif 457 return; 458 } 459 460 DELAY(8); 461 462 utp->stats.rx_sbip += utopia_update(utp, 463 IDTPHY_REGO_BIPC, 2, 0xffff); 464 utp->stats.rx_lbip += utopia_update(utp, 465 IDTPHY_REGO_B2EC, 3, 0xfffff); 466 utp->stats.rx_lfebe += utopia_update(utp, 467 IDTPHY_REGO_FEBEC, 3, 0xfffff); 468 utp->stats.rx_pbip += utopia_update(utp, 469 IDTPHY_REGO_B3EC, 2, 0xffff); 470 utp->stats.rx_pfebe += utopia_update(utp, 471 IDTPHY_REGO_PFEBEC, 2, 0xffff); 472 utp->stats.rx_corr += utopia_update(utp, 473 IDTPHY_REGO_CEC, 1, 0xff); 474 utp->stats.rx_uncorr += utopia_update(utp, 475 IDTPHY_REGO_UEC, 1, 0xff); 476 utp->stats.rx_cells += utopia_update(utp, 477 IDTPHY_REGO_RCCNT, 3, 0x7ffff); 478 utp->stats.tx_cells += utopia_update(utp, 479 IDTPHY_REGO_TXCNT, 3, 0x7ffff); 480} 481 482const struct utopia_chip utopia_chip_idt77155 = { 483 UTP_TYPE_IDT77155, 484 "IDT77155", 485 0x80, 486 idt77155_reset, 487 idt77155_set_sdh, 488 idt77155_set_unass, 489 idt77155_set_noscramb, 490 idt77155_update_carrier, 491 idt77155_set_loopback, 492 idt77155_intr, 493 idt77155_update_stats, 494}; 495