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/11.0/sys/dev/utopia/suni.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/suni.h> 53#include <dev/utopia/utopia.h> 54#include <dev/utopia/utopia_priv.h> 55 56/* 57 * set SONET/SDH mode 58 */ 59static int 60suni_set_sdh(struct utopia *utp, int sdh) 61{ 62 int err; 63 64 if (sdh) 65 err = UTP_WRITEREG(utp, SUNI_REGO_TPOPAPTR + 1, 66 SUNI_REGM_TPOPAPTR_S, 67 SUNI_REGM_SDH << SUNI_REGS_TPOPAPTR_S); 68 else 69 err = UTP_WRITEREG(utp, SUNI_REGO_TPOPAPTR + 1, 70 SUNI_REGM_TPOPAPTR_S, 71 SUNI_REGM_SONET << SUNI_REGS_TPOPAPTR_S); 72 if (err != 0) 73 return (err); 74 75 utp->state &= ~UTP_ST_SDH; 76 if (sdh) 77 utp->state |= UTP_ST_SDH; 78 79 return (0); 80} 81 82/* 83 * set idle/unassigned cells 84 */ 85static int 86suni_set_unass(struct utopia *utp, int unass) 87{ 88 int err; 89 90 if (unass) 91 err = UTP_WRITEREG(utp, SUNI_REGO_TACPIDLEH, 92 0xff, (0 << SUNI_REGS_TACPIDLEH_CLP)); 93 else 94 err = UTP_WRITEREG(utp, SUNI_REGO_TACPIDLEH, 95 0xff, (1 << SUNI_REGS_TACPIDLEH_CLP)); 96 if (err != 0) 97 return (err); 98 99 utp->state &= ~UTP_ST_UNASS; 100 if (unass) 101 utp->state |= UTP_ST_UNASS; 102 103 return (0); 104} 105 106/* 107 * enable/disable scrambling 108 */ 109static int 110suni_set_noscramb(struct utopia *utp, int noscramb) 111{ 112 int err; 113 114 if (noscramb) { 115 err = UTP_WRITEREG(utp, SUNI_REGO_TACPCTRL, 116 SUNI_REGM_TACPCTRL_DSCR, SUNI_REGM_TACPCTRL_DSCR); 117 if (err) 118 return (err); 119 err = UTP_WRITEREG(utp, SUNI_REGO_RACPCTRL, 120 SUNI_REGM_RACPCTRL_DDSCR, SUNI_REGM_RACPCTRL_DDSCR); 121 if (err) 122 return (err); 123 utp->state |= UTP_ST_NOSCRAMB; 124 } else { 125 err = UTP_WRITEREG(utp, SUNI_REGO_TACPCTRL, 126 SUNI_REGM_TACPCTRL_DSCR, 0); 127 if (err) 128 return (err); 129 err = UTP_WRITEREG(utp, SUNI_REGO_RACPCTRL, 130 SUNI_REGM_RACPCTRL_DDSCR, 0); 131 if (err) 132 return (err); 133 utp->state &= ~UTP_ST_NOSCRAMB; 134 } 135 return (0); 136} 137 138/* 139 * Get current carrier state 140 */ 141static int 142suni_update_carrier(struct utopia *utp) 143{ 144 int err; 145 uint8_t reg; 146 u_int n = 1; 147 148 if ((err = UTP_READREGS(utp, SUNI_REGO_RSOPSIS, ®, &n)) != 0) { 149 utp->carrier = UTP_CARR_UNKNOWN; 150 return (err); 151 } 152 utopia_check_carrier(utp, !(reg & SUNI_REGM_RSOPSIS_LOSV)); 153 return (0); 154} 155 156/* 157 * Set the SUNI chip to reflect the current state in utopia. 158 * Assume, that the chip has been reset. 159 */ 160static int 161suni_set_chip(struct utopia *utp) 162{ 163 int err = 0; 164 165 /* set sonet/sdh */ 166 err |= utopia_set_sdh(utp, utp->state & UTP_ST_SDH); 167 168 /* unassigned or idle cells */ 169 err |= utopia_set_unass(utp, utp->state & UTP_ST_UNASS); 170 err |= UTP_WRITEREG(utp, SUNI_REGO_TACPIDLEP, 0xff, 0x6a); 171 172 /* set scrambling */ 173 err |= utopia_set_noscramb(utp, utp->state & UTP_ST_NOSCRAMB); 174 175 /* loopback */ 176 err |= utopia_set_loopback(utp, utp->loopback); 177 178 /* update carrier state */ 179 err |= utopia_update_carrier(utp); 180 181 /* enable interrupts on LOS */ 182 err |= UTP_WRITEREG(utp, SUNI_REGO_RSOPCIE, 183 SUNI_REGM_RSOPCIE_LOSE, SUNI_REGM_RSOPCIE_LOSE); 184 185 return (err ? EIO : 0); 186} 187 188/* 189 * Reset the SUNI chip to reflect the current state of utopia. 190 */ 191static int 192suni_reset_default(struct utopia *utp) 193{ 194 int err = 0; 195 196 if (!(utp->flags & UTP_FL_NORESET)) { 197 err |= UTP_WRITEREG(utp, SUNI_REGO_MRESET, 198 SUNI_REGM_MRESET_RESET, SUNI_REGM_MRESET_RESET); 199 err |= UTP_WRITEREG(utp, SUNI_REGO_MRESET, 200 SUNI_REGM_MRESET_RESET, 0); 201 } 202 203 /* disable test mode */ 204 err |= UTP_WRITEREG(utp, SUNI_REGO_MTEST, 0xff, 0x00); 205 206 err |= suni_set_chip(utp); 207 208 return (err ? EIO : 0); 209} 210 211/* 212 * Set loopback mode for the Lite 213 */ 214static int 215suni_set_loopback_lite(struct utopia *utp, u_int mode) 216{ 217 int err; 218 uint32_t val; 219 u_int nmode; 220 221 val = 0; 222 nmode = mode; 223 if (mode & UTP_LOOP_TIME) { 224 nmode &= ~UTP_LOOP_TIME; 225 val |= SUNI_REGM_MCTRL_LOOPT; 226 } 227 if (mode & UTP_LOOP_DIAG) { 228 nmode &= ~UTP_LOOP_DIAG; 229 val |= SUNI_REGM_MCTRL_DLE; 230 } 231 if (mode & UTP_LOOP_LINE) { 232 nmode &= ~UTP_LOOP_LINE; 233 if (val & SUNI_REGM_MCTRL_DLE) 234 return (EINVAL); 235 val |= SUNI_REGM_MCTRL_LLE; 236 } 237 if (nmode != 0) 238 return (EINVAL); 239 240 err = UTP_WRITEREG(utp, SUNI_REGO_MCTRL, 241 SUNI_REGM_MCTRL_LLE | SUNI_REGM_MCTRL_DLE | SUNI_REGM_MCTRL_LOOPT, 242 val); 243 if (err) 244 return (err); 245 utp->loopback = mode; 246 247 return (0); 248} 249 250/* 251 * Update statistics from a SUNI/LITE or SUNI/ULTRA 252 */ 253static void 254suni_lite_update_stats(struct utopia *utp) 255{ 256 int err; 257 258 /* write to the master if we can */ 259 if (!(utp->flags & UTP_FL_NORESET)) { 260 err = UTP_WRITEREG(utp, SUNI_REGO_MRESET, 0, 0); 261 } else { 262 err = UTP_WRITEREG(utp, SUNI_REGO_RSOP_BIP8, 0, 0); 263 err |= UTP_WRITEREG(utp, SUNI_REGO_RLOPBIP8_24, 0, 0); 264 err |= UTP_WRITEREG(utp, SUNI_REGO_RPOPBIP8, 0, 0); 265 err |= UTP_WRITEREG(utp, SUNI_REGO_RACPCHCS, 0, 0); 266 err |= UTP_WRITEREG(utp, SUNI_REGO_TACPCNT, 0, 0); 267 268 } 269 if (err) { 270#ifdef DIAGNOSTIC 271 printf("%s: register write error %s: %d\n", __func__, 272 utp->chip->name, err); 273#endif 274 return; 275 } 276 277 DELAY(8); 278 279 utp->stats.rx_sbip += utopia_update(utp, 280 SUNI_REGO_RSOP_BIP8, 2, 0xffff); 281 utp->stats.rx_lbip += utopia_update(utp, 282 SUNI_REGO_RLOPBIP8_24, 3, 0xfffff); 283 utp->stats.rx_lfebe += utopia_update(utp, 284 SUNI_REGO_RLOPFEBE, 3, 0xfffff); 285 utp->stats.rx_pbip += utopia_update(utp, 286 SUNI_REGO_RPOPBIP8, 2, 0xffff); 287 utp->stats.rx_pfebe += utopia_update(utp, 288 SUNI_REGO_RPOPFEBE, 2, 0xffff); 289 utp->stats.rx_corr += utopia_update(utp, 290 SUNI_REGO_RACPCHCS, 1, 0xff); 291 utp->stats.rx_uncorr += utopia_update(utp, 292 SUNI_REGO_RACPUHCS, 1, 0xff); 293 utp->stats.rx_cells += utopia_update(utp, 294 SUNI_REGO_RACPCNT, 3, 0x7ffff); 295 utp->stats.tx_cells += utopia_update(utp, 296 SUNI_REGO_TACPCNT, 3, 0x7ffff); 297} 298 299/* 300 * Handle interrupt on SUNI chip 301 */ 302static void 303suni_intr_default(struct utopia *utp) 304{ 305 uint8_t regs[SUNI_REGO_MTEST]; 306 u_int n = SUNI_REGO_MTEST; 307 int err; 308 309 /* Read all registers. This acks the interrupts */ 310 if ((err = UTP_READREGS(utp, SUNI_REGO_MRESET, regs, &n)) != 0) { 311 printf("SUNI read error %d\n", err); 312 return; 313 } 314 if (n <= SUNI_REGO_RSOPSIS) { 315 printf("%s: could not read RSOPSIS", __func__); 316 return; 317 } 318 /* check for LOSI (loss of signal) */ 319 if ((regs[SUNI_REGO_MISTATUS] & SUNI_REGM_MISTATUS_RSOPI) && 320 (regs[SUNI_REGO_RSOPSIS] & SUNI_REGM_RSOPSIS_LOSI)) 321 utopia_check_carrier(utp, !(regs[SUNI_REGO_RSOPSIS] 322 & SUNI_REGM_RSOPSIS_LOSV)); 323} 324 325const struct utopia_chip utopia_chip_lite = { 326 UTP_TYPE_SUNI_LITE, 327 "Suni/Lite (PMC-5346)", 328 256, 329 suni_reset_default, 330 suni_set_sdh, 331 suni_set_unass, 332 suni_set_noscramb, 333 suni_update_carrier, 334 suni_set_loopback_lite, 335 suni_intr_default, 336 suni_lite_update_stats, 337}; 338 339/* 340 * Set loopback mode for the Ultra 341 */ 342static int 343suni_set_loopback_ultra(struct utopia *utp, u_int mode) 344{ 345 int err; 346 uint32_t val; 347 u_int nmode; 348 349 val = 0; 350 nmode = mode; 351 if (mode & UTP_LOOP_TIME) { 352 nmode &= ~UTP_LOOP_TIME; 353 val |= SUNI_REGM_MCTRL_LOOPT; 354 } 355 if (mode & UTP_LOOP_DIAG) { 356 nmode &= ~UTP_LOOP_DIAG; 357 if (val & SUNI_REGM_MCTRL_LOOPT) 358 return (EINVAL); 359 val |= SUNI_REGM_MCTRL_SDLE; 360 } 361 if (mode & UTP_LOOP_LINE) { 362 nmode &= ~UTP_LOOP_LINE; 363 if (val & (SUNI_REGM_MCTRL_LOOPT | SUNI_REGM_MCTRL_SDLE)) 364 return (EINVAL); 365 val |= SUNI_REGM_MCTRL_LLE; 366 } 367 if (mode & UTP_LOOP_PARAL) { 368 nmode &= ~UTP_LOOP_PARAL; 369 val |= SUNI_REGM_MCTRL_PDLE; 370 } 371 if (mode & UTP_LOOP_TWIST) { 372 nmode &= ~UTP_LOOP_TWIST; 373 val |= SUNI_REGM_MCTRL_TPLE; 374 } 375 if (nmode != 0) 376 return (EINVAL); 377 378 err = UTP_WRITEREG(utp, SUNI_REGO_MCTRL, 379 SUNI_REGM_MCTRL_LLE | SUNI_REGM_MCTRL_SDLE | SUNI_REGM_MCTRL_LOOPT | 380 SUNI_REGM_MCTRL_PDLE | SUNI_REGM_MCTRL_TPLE, val); 381 if (err) 382 return (err); 383 utp->loopback = mode; 384 385 return (0); 386} 387 388const struct utopia_chip utopia_chip_ultra = { 389 UTP_TYPE_SUNI_ULTRA, 390 "Suni/Ultra (PMC-5350)", 391 256, 392 suni_reset_default, 393 suni_set_sdh, 394 suni_set_unass, 395 suni_set_noscramb, 396 suni_update_carrier, 397 suni_set_loopback_ultra, 398 suni_intr_default, 399 suni_lite_update_stats, 400}; 401 402/* 403 * Set loopback mode for the 622 404 */ 405static int 406suni_set_loopback_622(struct utopia *utp, u_int mode) 407{ 408 int err; 409 uint32_t val; 410 uint8_t config; 411 int smode; 412 u_int nmode; 413 u_int n = 1; 414 415 val = 0; 416 nmode = mode; 417 if (mode & UTP_LOOP_PATH) { 418 nmode &= ~UTP_LOOP_PATH; 419 val |= SUNI_REGM_MCTRLM_DPLE; 420 } 421 422 err = UTP_READREGS(utp, SUNI_REGO_MCONFIG, &config, &n); 423 if (err != 0) 424 return (err); 425 smode = ((config & SUNI_REGM_MCONFIG_TMODE_622) == 426 SUNI_REGM_MCONFIG_TMODE_STS1_BIT && 427 (config & SUNI_REGM_MCONFIG_RMODE_622) == 428 SUNI_REGM_MCONFIG_RMODE_STS1_BIT); 429 430 if (mode & UTP_LOOP_TIME) { 431 if (!smode) 432 return (EINVAL); 433 nmode &= ~UTP_LOOP_TIME; 434 val |= SUNI_REGM_MCTRLM_LOOPT; 435 } 436 if (mode & UTP_LOOP_DIAG) { 437 nmode &= ~UTP_LOOP_DIAG; 438 if (val & SUNI_REGM_MCTRLM_LOOPT) 439 return (EINVAL); 440 val |= SUNI_REGM_MCTRLM_DLE; 441 } 442 if (mode & UTP_LOOP_LINE) { 443 nmode &= ~UTP_LOOP_LINE; 444 if (val & (SUNI_REGM_MCTRLM_LOOPT | SUNI_REGM_MCTRLM_DLE)) 445 return (EINVAL); 446 val |= SUNI_REGM_MCTRLM_LLE; 447 } 448 if (nmode != 0) 449 return (EINVAL); 450 451 err = UTP_WRITEREG(utp, SUNI_REGO_MCTRLM, 452 SUNI_REGM_MCTRLM_LLE | SUNI_REGM_MCTRLM_DLE | 453 SUNI_REGM_MCTRLM_DPLE | SUNI_REGM_MCTRL_LOOPT, val); 454 if (err) 455 return (err); 456 utp->loopback = mode; 457 458 return (0); 459} 460 461/* 462 * Reset the SUNI chip to reflect the current state of utopia. 463 */ 464static int 465suni_reset_622(struct utopia *utp) 466{ 467 int err = 0; 468 469 if (!(utp->flags & UTP_FL_NORESET)) { 470 err |= UTP_WRITEREG(utp, SUNI_REGO_MRESET, 471 SUNI_REGM_MRESET_RESET, SUNI_REGM_MRESET_RESET); 472 err |= UTP_WRITEREG(utp, SUNI_REGO_MRESET, 473 SUNI_REGM_MRESET_RESET, 0); 474 } 475 476 /* disable test mode */ 477 err |= UTP_WRITEREG(utp, SUNI_REGO_MTEST, 0xff, 478 SUNI_REGM_MTEST_DS27_53_622); 479 480 err |= suni_set_chip(utp); 481 482 return (err ? EIO : 0); 483} 484 485/* 486 * Update statistics from a SUNI/622 487 */ 488static void 489suni_622_update_stats(struct utopia *utp) 490{ 491 int err; 492 493 /* write to the master if we can */ 494 if (!(utp->flags & UTP_FL_NORESET)) { 495 err = UTP_WRITEREG(utp, SUNI_REGO_MRESET, 0, 0); 496 } else { 497 err = UTP_WRITEREG(utp, SUNI_REGO_RSOP_BIP8, 0, 0); 498 err |= UTP_WRITEREG(utp, SUNI_REGO_RLOPBIP8_24, 0, 0); 499 err |= UTP_WRITEREG(utp, SUNI_REGO_RPOPBIP8, 0, 0); 500 err |= UTP_WRITEREG(utp, SUNI_REGO_RACPCHCS, 0, 0); 501 err |= UTP_WRITEREG(utp, SUNI_REGO_TACPCNT, 0, 0); 502 } 503 if (err) { 504#ifdef DIAGNOSTIC 505 printf("%s: register write error %s: %d\n", __func__, 506 utp->chip->name, err); 507#endif 508 return; 509 } 510 511 DELAY(8); 512 513 utp->stats.rx_sbip += utopia_update(utp, 514 SUNI_REGO_RSOP_BIP8, 2, 0xffff); 515 utp->stats.rx_lbip += utopia_update(utp, 516 SUNI_REGO_RLOPBIP8_24, 3, 0xfffff); 517 utp->stats.rx_lfebe += utopia_update(utp, 518 SUNI_REGO_RLOPFEBE, 3, 0xfffff); 519 utp->stats.rx_pbip += utopia_update(utp, 520 SUNI_REGO_RPOPBIP8, 2, 0xffff); 521 utp->stats.rx_pfebe += utopia_update(utp, 522 SUNI_REGO_RPOPFEBE, 2, 0xffff); 523 utp->stats.rx_corr += utopia_update(utp, 524 SUNI_REGO_RACPCHCS_622, 2, 0xfff); 525 utp->stats.rx_uncorr += utopia_update(utp, 526 SUNI_REGO_RACPUHCS_622, 2, 0xfff); 527 utp->stats.rx_cells += utopia_update(utp, 528 SUNI_REGO_RACPCNT_622, 3, 0x1fffff); 529 utp->stats.tx_cells += utopia_update(utp, 530 SUNI_REGO_TACPCNT, 3, 0x1fffff); 531} 532 533const struct utopia_chip utopia_chip_622 = { 534 UTP_TYPE_SUNI_622, 535 "Suni/622 (PMC-5355)", 536 256, 537 suni_reset_622, 538 suni_set_sdh, 539 suni_set_unass, 540 suni_set_noscramb, 541 suni_update_carrier, 542 suni_set_loopback_622, 543 suni_intr_default, 544 suni_622_update_stats, 545}; 546