1 /* $NetbBSD$ */ 2 3 /* 4 * Copyright (c) 2007 Internet Initiative Japan, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: octeon_pow.c,v 1.2 2015/06/01 22:55:12 matt Exp $"); 31 32 #include "opt_octeon.h" /* OCTEON_ETH_DEBUG */ 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/types.h> 37 #include <sys/kernel.h> /* hz */ 38 #include <sys/malloc.h> 39 #include <sys/device.h> /* evcnt */ 40 #include <sys/syslog.h> /* evcnt */ 41 42 #include <sys/bus.h> 43 44 #include <mips/include/locore.h> 45 #include <mips/cavium/octeonvar.h> 46 #include <mips/cavium/include/iobusvar.h> 47 #include <mips/cavium/dev/octeon_ciureg.h> /* XXX */ 48 #include <mips/cavium/dev/octeon_powreg.h> 49 #include <mips/cavium/dev/octeon_powvar.h> 50 51 /* XXX ensure assertion */ 52 #if !defined(DIAGNOSTIC) 53 #define DIAGNOSTIC 54 #endif 55 56 extern int ipflow_fastforward_disable_flags; 57 58 struct octeon_pow_intr_handle { 59 void *pi_ih; 60 struct octeon_pow_softc *pi_sc; 61 int pi_group; 62 void (*pi_cb)(void *, uint64_t *); 63 void *pi_data; 64 65 #ifdef OCTEON_ETH_DEBUG 66 #define _EV_PER_N 32 /* XXX */ 67 #define _EV_IVAL_N 32 /* XXX */ 68 int pi_first; 69 struct timeval pi_last; 70 struct evcnt pi_ev_per[_EV_PER_N]; 71 struct evcnt pi_ev_ival[_EV_IVAL_N]; 72 struct evcnt pi_ev_stray_tc; 73 struct evcnt pi_ev_stray_ds; 74 struct evcnt pi_ev_stray_iq; 75 #endif 76 }; 77 78 void octeon_pow_bootstrap(struct octeon_config *); 79 80 #ifdef OCTEON_ETH_DEBUG 81 void octeon_pow_intr_evcnt_attach(struct octeon_pow_softc *); 82 void octeon_pow_intr_rml(void *); 83 84 static void octeon_pow_intr_debug_init( 85 struct octeon_pow_intr_handle *, int); 86 static inline void octeon_pow_intr_work_debug_ival(struct octeon_pow_softc *, 87 struct octeon_pow_intr_handle *); 88 static inline void octeon_pow_intr_work_debug_per(struct octeon_pow_softc *, 89 struct octeon_pow_intr_handle *, int); 90 #endif 91 static void octeon_pow_init(struct octeon_pow_softc *); 92 static void octeon_pow_init_regs(struct octeon_pow_softc *); 93 static inline int octeon_pow_tag_sw_poll(void) __unused; 94 static inline void octeon_pow_tag_sw_wait(void); 95 static inline void octeon_pow_config_int_pc(struct octeon_pow_softc *, int); 96 static inline void octeon_pow_config_int(struct octeon_pow_softc *, int, 97 uint64_t, uint64_t, uint64_t); 98 static inline void octeon_pow_intr_work(struct octeon_pow_softc *, 99 struct octeon_pow_intr_handle *, int); 100 static int octeon_pow_intr(void *); 101 102 #ifdef OCTEON_ETH_DEBUG 103 void octeon_pow_dump(void); 104 #endif 105 106 /* XXX */ 107 struct octeon_pow_softc octeon_pow_softc; 108 109 #ifdef OCTEON_ETH_DEBUG 110 struct octeon_pow_softc *__octeon_pow_softc; 111 #endif 112 113 /* 114 * XXX: parameter tuning is needed: see files.octeon 115 */ 116 #ifndef OCTEON_ETH_RING_MAX 117 #define OCTEON_ETH_RING_MAX 512 118 #endif 119 #ifndef OCTEON_ETH_RING_MIN 120 #define OCTEON_ETH_RING_MIN 1 121 #endif 122 123 #ifdef OCTEON_ETH_INTR_FEEDBACK_RING 124 int max_recv_cnt = OCTEON_ETH_RING_MAX; 125 int min_recv_cnt = OCTEON_ETH_RING_MIN; 126 int recv_cnt = OCTEON_ETH_RING_MIN; 127 int int_rate = 1; 128 #endif 129 130 /* -------------------------------------------------------------------------- */ 131 132 /* ---- operation primitive functions */ 133 134 /* Load Operations */ 135 136 /* IOBDMA Operations */ 137 138 /* Store Operations */ 139 140 /* -------------------------------------------------------------------------- */ 141 142 /* ---- utility functions */ 143 144 145 /* ---- status by coreid */ 146 147 static inline uint64_t 148 octeon_pow_status_by_coreid_pend_tag(int coreid) 149 { 150 return octeon_pow_ops_pow_status(coreid, 0, 0, 0); 151 } 152 153 static inline uint64_t 154 octeon_pow_status_by_coreid_pend_wqp(int coreid) 155 { 156 return octeon_pow_ops_pow_status(coreid, 0, 0, 1); 157 } 158 159 static inline uint64_t 160 octeon_pow_status_by_coreid_cur_tag_next(int coreid) 161 { 162 return octeon_pow_ops_pow_status(coreid, 0, 1, 0); 163 } 164 165 static inline uint64_t 166 octeon_pow_status_by_coreid_cur_tag_prev(int coreid) 167 { 168 return octeon_pow_ops_pow_status(coreid, 1, 1, 0); 169 } 170 171 static inline uint64_t 172 octeon_pow_status_by_coreid_cur_wqp_next(int coreid) 173 { 174 return octeon_pow_ops_pow_status(coreid, 0, 1, 1); 175 } 176 177 static inline uint64_t 178 octeon_pow_status_by_coreid_cur_wqp_prev(int coreid) 179 { 180 return octeon_pow_ops_pow_status(coreid, 1, 1, 1); 181 } 182 183 /* ---- status by index */ 184 185 static inline uint64_t 186 octeon_pow_status_by_index_tag(int index) 187 { 188 return octeon_pow_ops_pow_memory(index, 0, 0); 189 } 190 191 static inline uint64_t 192 octeon_pow_status_by_index_wqp(int index) 193 { 194 return octeon_pow_ops_pow_memory(index, 0, 1); 195 } 196 197 static inline uint64_t 198 octeon_pow_status_by_index_desched(int index) 199 { 200 return octeon_pow_ops_pow_memory(index, 1, 0); 201 } 202 203 /* ---- status by qos level */ 204 205 static inline uint64_t 206 octeon_pow_status_by_qos_free_loc(int qos) 207 { 208 return octeon_pow_ops_pow_idxptr(qos, 0, 0); 209 } 210 211 /* ---- status by desched group */ 212 213 static inline uint64_t 214 octeon_pow_status_by_grp_nosched_des(int grp) 215 { 216 return octeon_pow_ops_pow_idxptr(grp, 0, 1); 217 } 218 219 /* ---- status by memory input queue */ 220 221 static inline uint64_t 222 octeon_pow_status_by_queue_remote_head(int queue) 223 { 224 return octeon_pow_ops_pow_idxptr(queue, 1, 0); 225 } 226 227 static inline uint64_t 228 octeon_pow_status_by_queue_remote_tail(int queue) 229 { 230 return octeon_pow_ops_pow_idxptr(queue, 1, 0); 231 } 232 233 /* ---- tag switch */ 234 235 /* 236 * "RDHWR rt, $30" returns: 237 * 0 => pending bit is set 238 * 1 => pending bit is clear 239 */ 240 241 /* return 1 if pending bit is clear (ready) */ 242 static inline int 243 octeon_pow_tag_sw_poll(void) 244 { 245 uint64_t result; 246 247 /* XXX O32 */ 248 __asm __volatile ( 249 " .set push \n" 250 " .set noreorder \n" 251 " .set arch=octeon \n" 252 " rdhwr %[result], $30 \n" 253 " .set pop \n" 254 : [result]"=r"(result) 255 ); 256 /* XXX O32 */ 257 return (int)result; 258 } 259 260 /* -------------------------------------------------------------------------- */ 261 262 /* ---- initialization and configuration */ 263 264 void 265 octeon_pow_bootstrap(struct octeon_config *mcp) 266 { 267 struct octeon_pow_softc *sc = &octeon_pow_softc; 268 269 sc->sc_regt = &mcp->mc_iobus_bust; 270 /* XXX */ 271 272 octeon_pow_init(sc); 273 274 #ifdef OCTEON_ETH_DEBUG 275 __octeon_pow_softc = sc; 276 #endif 277 278 } 279 280 static inline void 281 octeon_pow_config_int(struct octeon_pow_softc *sc, int group, 282 uint64_t tc_thr, uint64_t ds_thr, uint64_t iq_thr) 283 { 284 uint64_t wq_int_thr; 285 286 wq_int_thr = 287 POW_WQ_INT_THRX_TC_EN | 288 (tc_thr << POW_WQ_INT_THRX_TC_THR_SHIFT) | 289 (ds_thr << POW_WQ_INT_THRX_DS_THR_SHIFT) | 290 (iq_thr << POW_WQ_INT_THRX_IQ_THR_SHIFT); 291 _POW_WR8(sc, POW_WQ_INT_THR0_OFFSET + (group * 8), wq_int_thr); 292 } 293 294 /* 295 * interrupt threshold configuration 296 * 297 * => DS / IQ 298 * => ... 299 * => time counter threshold 300 * => unit is 1msec 301 * => each group can set timeout 302 * => temporary disable bit 303 * => use CIU generic timer 304 */ 305 306 void 307 octeon_pow_config(struct octeon_pow_softc *sc, int group) 308 { 309 310 octeon_pow_config_int(sc, group, 311 0x0f, /* TC */ 312 0x00, /* DS */ 313 0x00); /* IQ */ 314 } 315 316 void * 317 octeon_pow_intr_establish(int group, int level, 318 void (*cb)(void *, uint64_t *), void (*fcb)(int*, int *, uint64_t, void *), 319 void *data) 320 { 321 struct octeon_pow_intr_handle *pow_ih; 322 323 KASSERT(group >= 0); 324 KASSERT(group < 16); 325 326 pow_ih = malloc(sizeof(*pow_ih), M_DEVBUF, M_NOWAIT); 327 KASSERT(pow_ih != NULL); 328 329 pow_ih->pi_ih = octeon_intr_establish( 330 ffs64(CIU_INTX_SUM0_WORKQ_0) - 1 + group, 331 level, 332 octeon_pow_intr, pow_ih); 333 KASSERT(pow_ih->pi_ih != NULL); 334 335 pow_ih->pi_sc = &octeon_pow_softc; /* XXX */ 336 pow_ih->pi_group = group; 337 pow_ih->pi_cb = cb; 338 pow_ih->pi_data = data; 339 340 #ifdef OCTEON_ETH_DEBUG 341 octeon_pow_intr_debug_init(pow_ih, group); 342 #endif 343 return pow_ih; 344 } 345 346 #ifdef OCTEON_ETH_DEBUG 347 #define _NAMELEN 8 348 #define _DESCRLEN 40 349 350 static void 351 octeon_pow_intr_debug_init(struct octeon_pow_intr_handle *pow_ih, int group) 352 { 353 pow_ih->pi_first = 1; 354 char *name, *descr; 355 int i; 356 357 name = malloc(_NAMELEN + 358 _DESCRLEN * __arraycount(pow_ih->pi_ev_per) + 359 _DESCRLEN * __arraycount(pow_ih->pi_ev_ival), 360 M_DEVBUF, M_NOWAIT); 361 descr = name + _NAMELEN; 362 snprintf(name, _NAMELEN, "pow%d", group); 363 for (i = 0; i < (int)__arraycount(pow_ih->pi_ev_per); i++) { 364 int n = 1 << (i - 1); 365 366 (void)snprintf(descr, _DESCRLEN, 367 "# of works per intr (%d-%d)", 368 (i == 0) ? 0 : n, 369 (i == 0) ? 0 : ((n << 1) - 1)); 370 evcnt_attach_dynamic(&pow_ih->pi_ev_per[i], 371 EVCNT_TYPE_MISC, NULL, name, descr); 372 descr += _DESCRLEN; 373 } 374 for (i = 0; i < (int)__arraycount(pow_ih->pi_ev_ival); i++) { 375 int n = 1 << (i - 1); 376 int p, q; 377 char unit; 378 379 p = n; 380 q = (n << 1) - 1; 381 unit = 'u'; 382 /* 383 * 0 is exceptional 384 */ 385 if (i == 0) 386 p = q = 0; 387 /* 388 * count 1024usec as 1msec 389 * 390 * XXX this is not exact 391 */ 392 if ((i - 1) >= 10) { 393 p /= 1000; 394 q /= 1000; 395 unit = 'm'; 396 } 397 (void)snprintf(descr, _DESCRLEN, "intr interval (%d-%d%csec)", 398 p, q, unit); 399 evcnt_attach_dynamic(&pow_ih->pi_ev_ival[i], 400 EVCNT_TYPE_MISC, NULL, name, descr); 401 descr += _DESCRLEN; 402 } 403 evcnt_attach_dynamic(&pow_ih->pi_ev_stray_tc, 404 EVCNT_TYPE_MISC, NULL, name, "stray intr (TC)"); 405 evcnt_attach_dynamic(&pow_ih->pi_ev_stray_ds, 406 EVCNT_TYPE_MISC, NULL, name, "stray intr (DS)"); 407 evcnt_attach_dynamic(&pow_ih->pi_ev_stray_iq, 408 EVCNT_TYPE_MISC, NULL, name, "stray intr (IQ)"); 409 } 410 #endif 411 412 void 413 octeon_pow_init(struct octeon_pow_softc *sc) 414 { 415 octeon_pow_init_regs(sc); 416 417 sc->sc_int_pc_base = 10000; 418 octeon_pow_config_int_pc(sc, sc->sc_int_pc_base); 419 420 #ifdef OCTEON_ETH_DEBUG 421 octeon_pow_error_int_enable(sc, 1); 422 #endif 423 } 424 425 void 426 octeon_pow_init_regs(struct octeon_pow_softc *sc) 427 { 428 int status; 429 430 status = bus_space_map(sc->sc_regt, POW_BASE, POW_SIZE, 0, 431 &sc->sc_regh); 432 if (status != 0) 433 panic("can't map %s space", "pow register"); 434 435 #ifdef OCTEON_ETH_DEBUG 436 _POW_WR8(sc, POW_ECC_ERR_OFFSET, 437 POW_ECC_ERR_IOP_IE | POW_ECC_ERR_RPE_IE | 438 POW_ECC_ERR_DBE_IE | POW_ECC_ERR_SBE_IE); 439 #endif 440 } 441 442 /* -------------------------------------------------------------------------- */ 443 444 /* ---- interrupt handling */ 445 446 #ifdef OCTEON_ETH_DEBUG 447 static inline void 448 octeon_pow_intr_work_debug_ival(struct octeon_pow_softc *sc, 449 struct octeon_pow_intr_handle *pow_ih) 450 { 451 struct timeval now; 452 struct timeval ival; 453 int n; 454 455 microtime(&now); 456 if (__predict_false(pow_ih->pi_first == 1)) { 457 pow_ih->pi_first = 0; 458 goto stat_done; 459 } 460 timersub(&now, &pow_ih->pi_last, &ival); 461 if (ival.tv_sec != 0) 462 goto stat_done; /* XXX */ 463 n = ffs64((uint64_t)ival.tv_usec); 464 if (n > (int)__arraycount(pow_ih->pi_ev_ival) - 1) 465 n = (int)__arraycount(pow_ih->pi_ev_ival) - 1; 466 pow_ih->pi_ev_ival[n].ev_count++; 467 468 stat_done: 469 pow_ih->pi_last = now; /* struct copy */ 470 } 471 472 static inline void 473 octeon_pow_intr_work_debug_per(struct octeon_pow_softc *sc, 474 struct octeon_pow_intr_handle *pow_ih, int count) 475 { 476 int n; 477 478 n = ffs64(count); 479 if (n > (int)__arraycount(pow_ih->pi_ev_per) - 1) 480 n = (int)__arraycount(pow_ih->pi_ev_per) - 1; 481 pow_ih->pi_ev_per[n].ev_count++; 482 #if 1 483 if (count == 0) { 484 uint64_t wq_int_cnt; 485 486 wq_int_cnt = _POW_GROUP_RD8(sc, pow_ih, POW_WQ_INT_CNT0_OFFSET); 487 if (wq_int_cnt & POW_WQ_INT_CNTX_TC_CNT) 488 pow_ih->pi_ev_stray_tc.ev_count++; 489 if (wq_int_cnt & POW_WQ_INT_CNTX_DS_CNT) 490 pow_ih->pi_ev_stray_ds.ev_count++; 491 if (wq_int_cnt & POW_WQ_INT_CNTX_IQ_CNT) 492 pow_ih->pi_ev_stray_iq.ev_count++; 493 } 494 #endif 495 } 496 #endif 497 498 #ifdef OCTEON_ETH_DEBUG 499 #define _POW_INTR_WORK_DEBUG_IVAL(sc, ih) \ 500 octeon_pow_intr_work_debug_ival((sc), (ih)) 501 #define _POW_INTR_WORK_DEBUG_PER(sc, ih, count) \ 502 octeon_pow_intr_work_debug_per((sc), (ih), (count)) 503 #else 504 #define _POW_INTR_WORK_DEBUG_IVAL(sc, ih) \ 505 do {} while (0) 506 #define _POW_INTR_WORK_DEBUG_PER(sc, ih, count) \ 507 do {} while (0) 508 #endif 509 510 /* 511 * Interrupt handling by fixed count, following Cavium's SDK code. 512 * 513 * XXX the fixed count (MAX_RX_CNT) could be changed dynamically? 514 * 515 * XXX this does not utilize "tag switch" very well 516 */ 517 /* 518 * usually all packet recieve 519 */ 520 #define MAX_RX_CNT 0x7fffffff 521 522 static inline void 523 octeon_pow_intr_work(struct octeon_pow_softc *sc, 524 struct octeon_pow_intr_handle *pow_ih, int recv_limit) 525 { 526 uint64_t *work; 527 uint64_t count = 0; 528 529 _POW_WR8(sc, POW_PP_GRP_MSK0_OFFSET, UINT64_C(1) << pow_ih->pi_group); 530 531 _POW_INTR_WORK_DEBUG_IVAL(sc, pow_ih); 532 533 for (count = 0; count < recv_limit; count++) { 534 octeon_pow_tag_sw_wait(); 535 octeon_pow_work_request_async( 536 OCTEON_CVMSEG_OFFSET(csm_pow_intr), POW_NO_WAIT); 537 work = (uint64_t *)octeon_pow_work_response_async( 538 OCTEON_CVMSEG_OFFSET(csm_pow_intr)); 539 if (work == NULL) 540 break; 541 (*pow_ih->pi_cb)(pow_ih->pi_data, work); 542 } 543 544 _POW_INTR_WORK_DEBUG_PER(sc, pow_ih, count); 545 } 546 547 static int 548 octeon_pow_intr(void *data) 549 { 550 struct octeon_pow_intr_handle *pow_ih = data; 551 struct octeon_pow_softc *sc = pow_ih->pi_sc; 552 uint64_t wq_int_mask = UINT64_C(0x1) << pow_ih->pi_group; 553 554 #ifdef OCTEON_ETH_INTR_FEEDBACK_RING 555 octeon_pow_intr_work(sc, pow_ih, recv_cnt); 556 #else 557 octeon_pow_intr_work(sc, pow_ih, INT_MAX); 558 #endif /* OCTEON_ETH_INTR_FEEDBACK_RING */ 559 560 _POW_WR8(sc, POW_WQ_INT_OFFSET, wq_int_mask << POW_WQ_INT_WQ_INT_SHIFT); 561 return 1; 562 } 563 564 #ifdef OCTEON_ETH_INTR_FEEDBACK_RING 565 int 566 octeon_pow_ring_reduce(void *arg) 567 { 568 struct octeon_pow_softc *sc = arg; 569 int new, newi; 570 int s; 571 572 #if 0 573 if (ipflow_fastforward_disable_flags == 0) { 574 newi = int_rate = 1; 575 octeon_pow_config_int_pc_rate(sc, int_rate); 576 return recv_cnt; 577 } 578 #endif 579 580 new = recv_cnt / 2; 581 if (new < min_recv_cnt) { 582 newi = int_rate << 1; 583 if (newi > 128) { 584 newi = 128; 585 #ifdef POW_DEBUG 586 log(LOG_DEBUG, 587 "Min intr rate.\n"); 588 #endif 589 new = min_recv_cnt; 590 } 591 else { 592 log(LOG_DEBUG, 593 "pow interrupt rate optimized %d->%d.\n", 594 int_rate, newi); 595 int_rate = newi; 596 octeon_pow_config_int_pc_rate(sc, int_rate); 597 new = max_recv_cnt; 598 } 599 } 600 601 s = splhigh(); /* XXX */ 602 recv_cnt = new; 603 splx(s); 604 605 return new; 606 } 607 608 int 609 octeon_pow_ring_grow(void *arg) 610 { 611 struct octeon_pow_softc *sc = arg; 612 int new, newi; 613 int s; 614 615 #if 0 616 if (ipflow_fastforward_disable_flags == 0) { 617 newi = int_rate = 1; 618 octeon_pow_config_int_pc_rate(sc, int_rate); 619 return recv_cnt; 620 } 621 #endif 622 623 new = recv_cnt + 1; 624 if (new > max_recv_cnt) { 625 newi = int_rate >> 1; 626 if (newi <= 0) { 627 newi = 1; 628 #ifdef POW_DEBUG 629 log(LOG_DEBUG, 630 "Max intr rate.\n"); 631 #endif 632 new = max_recv_cnt; 633 } 634 else { 635 log(LOG_DEBUG, 636 "pow interrupt rate optimized %d->%d.\n", 637 int_rate, newi); 638 int_rate = newi; 639 octeon_pow_config_int_pc_rate(sc, int_rate); 640 new = min_recv_cnt; 641 } 642 } 643 644 s = splhigh(); /* XXX */ 645 recv_cnt = new; 646 splx(s); 647 648 return new; 649 } 650 651 int 652 octeon_pow_ring_size(void) 653 { 654 return recv_cnt; 655 } 656 657 int 658 octeon_pow_ring_intr(void) 659 { 660 return int_rate; 661 } 662 #endif /* OCTEON_ETH_INTR_FEEDBACK_RING */ 663 664 /* -------------------------------------------------------------------------- */ 665 666 /* ---- debug configuration */ 667 668 #ifdef OCTEON_ETH_DEBUG 669 670 void 671 octeon_pow_error_int_enable(void *data, int enable) 672 { 673 struct octeon_pow_softc *sc = data; 674 uint64_t pow_error_int_xxx; 675 676 pow_error_int_xxx = 677 POW_ECC_ERR_IOP | POW_ECC_ERR_RPE | 678 POW_ECC_ERR_DBE | POW_ECC_ERR_SBE; 679 _POW_WR8(sc, POW_ECC_ERR_OFFSET, pow_error_int_xxx); 680 _POW_WR8(sc, POW_ECC_ERR_OFFSET, enable ? pow_error_int_xxx : 0); 681 } 682 683 uint64_t 684 octeon_pow_error_int_summary(void *data) 685 { 686 struct octeon_pow_softc *sc = data; 687 uint64_t summary; 688 689 summary = _POW_RD8(sc, POW_ECC_ERR_OFFSET); 690 _POW_WR8(sc, POW_ECC_ERR_OFFSET, summary); 691 return summary; 692 } 693 694 #endif 695 696 /* -------------------------------------------------------------------------- */ 697 698 /* ---- debug counter */ 699 700 #ifdef OCTEON_ETH_DEBUG 701 int octeon_pow_intr_rml_verbose; 702 struct evcnt octeon_pow_intr_evcnt; 703 704 static const struct octeon_evcnt_entry octeon_pow_intr_evcnt_entries[] = { 705 #define _ENTRY(name, type, parent, descr) \ 706 OCTEON_EVCNT_ENTRY(struct octeon_pow_softc, name, type, parent, descr) 707 _ENTRY(powecciopcsrpend, MISC, NULL, "pow csr load"), 708 _ENTRY(powecciopdbgpend, MISC, NULL, "pow dbg load"), 709 _ENTRY(powecciopaddwork, MISC, NULL, "pow addwork"), 710 _ENTRY(powecciopillop, MISC, NULL, "pow ill op"), 711 _ENTRY(poweccioppend24, MISC, NULL, "pow pend24"), 712 _ENTRY(poweccioppend23, MISC, NULL, "pow pend23"), 713 _ENTRY(poweccioppend22, MISC, NULL, "pow pend22"), 714 _ENTRY(poweccioppend21, MISC, NULL, "pow pend21"), 715 _ENTRY(poweccioptagnull, MISC, NULL, "pow tag null"), 716 _ENTRY(poweccioptagnullnull, MISC, NULL, "pow tag nullnull"), 717 _ENTRY(powecciopordatom, MISC, NULL, "pow ordered atomic"), 718 _ENTRY(powecciopnull, MISC, NULL, "pow core null"), 719 _ENTRY(powecciopnullnull, MISC, NULL, "pow core nullnull"), 720 _ENTRY(poweccrpe, MISC, NULL, "pow remote-pointer error"), 721 _ENTRY(poweccsyn, MISC, NULL, "pow syndrome value"), 722 _ENTRY(poweccdbe, MISC, NULL, "pow double bit"), 723 _ENTRY(poweccsbe, MISC, NULL, "pow single bit"), 724 #undef _ENTRY 725 }; 726 727 void 728 octeon_pow_intr_evcnt_attach(struct octeon_pow_softc *sc) 729 { 730 OCTEON_EVCNT_ATTACH_EVCNTS(sc, octeon_pow_intr_evcnt_entries, "pow0"); 731 } 732 733 void 734 octeon_pow_intr_rml(void *arg) 735 { 736 struct octeon_pow_softc *sc; 737 uint64_t reg; 738 739 octeon_pow_intr_evcnt.ev_count++; 740 sc = __octeon_pow_softc; 741 KASSERT(sc != NULL); 742 reg = octeon_pow_error_int_summary(sc); 743 if (octeon_pow_intr_rml_verbose) 744 printf("%s: POW_ECC_ERR=0x%016" PRIx64 "\n", __func__, reg); 745 switch (reg & POW_ECC_ERR_IOP) { 746 case POW_ECC_ERR_IOP_CSRPEND: 747 OCTEON_EVCNT_INC(sc, powecciopcsrpend); 748 break; 749 case POW_ECC_ERR_IOP_DBGPEND: 750 OCTEON_EVCNT_INC(sc, powecciopdbgpend); 751 break; 752 case POW_ECC_ERR_IOP_ADDWORK: 753 OCTEON_EVCNT_INC(sc, powecciopaddwork); 754 break; 755 case POW_ECC_ERR_IOP_ILLOP: 756 OCTEON_EVCNT_INC(sc, powecciopillop); 757 break; 758 case POW_ECC_ERR_IOP_PEND24: 759 OCTEON_EVCNT_INC(sc, poweccioppend24); 760 break; 761 case POW_ECC_ERR_IOP_PEND23: 762 OCTEON_EVCNT_INC(sc, poweccioppend23); 763 break; 764 case POW_ECC_ERR_IOP_PEND22: 765 OCTEON_EVCNT_INC(sc, poweccioppend22); 766 break; 767 case POW_ECC_ERR_IOP_PEND21: 768 OCTEON_EVCNT_INC(sc, poweccioppend21); 769 break; 770 case POW_ECC_ERR_IOP_TAGNULL: 771 OCTEON_EVCNT_INC(sc, poweccioptagnull); 772 break; 773 case POW_ECC_ERR_IOP_TAGNULLNULL: 774 OCTEON_EVCNT_INC(sc, poweccioptagnullnull); 775 break; 776 case POW_ECC_ERR_IOP_ORDATOM: 777 OCTEON_EVCNT_INC(sc, powecciopordatom); 778 break; 779 case POW_ECC_ERR_IOP_NULL: 780 OCTEON_EVCNT_INC(sc, powecciopnull); 781 break; 782 case POW_ECC_ERR_IOP_NULLNULL: 783 OCTEON_EVCNT_INC(sc, powecciopnullnull); 784 break; 785 default: 786 break; 787 } 788 if (reg & POW_ECC_ERR_RPE) 789 OCTEON_EVCNT_INC(sc, poweccrpe); 790 if (reg & POW_ECC_ERR_SYN) 791 OCTEON_EVCNT_INC(sc, poweccsyn); 792 if (reg & POW_ECC_ERR_DBE) 793 OCTEON_EVCNT_INC(sc, poweccdbe); 794 if (reg & POW_ECC_ERR_SBE) 795 OCTEON_EVCNT_INC(sc, poweccsbe); 796 } 797 #endif 798 799 /* -------------------------------------------------------------------------- */ 800 801 /* ---- debug dump */ 802 803 #ifdef OCTEON_ETH_DEBUG 804 805 void octeon_pow_dump_reg(void); 806 void octeon_pow_dump_ops(void); 807 808 void 809 octeon_pow_dump(void) 810 { 811 octeon_pow_dump_reg(); 812 octeon_pow_dump_ops(); 813 } 814 815 /* ---- register dump */ 816 817 struct octeon_pow_dump_reg_entry { 818 const char *name; 819 const char *format; 820 size_t offset; 821 }; 822 823 #define _ENTRY(x) { #x, x##_BITS, x##_OFFSET } 824 #define _ENTRY_0_7(x) \ 825 _ENTRY(x## 0), _ENTRY(x## 1), _ENTRY(x## 2), _ENTRY(x## 3), \ 826 _ENTRY(x## 4), _ENTRY(x## 5), _ENTRY(x## 6), _ENTRY(x## 7) 827 #define _ENTRY_0_15(x) \ 828 _ENTRY(x## 0), _ENTRY(x## 1), _ENTRY(x## 2), _ENTRY(x## 3), \ 829 _ENTRY(x## 4), _ENTRY(x## 5), _ENTRY(x## 6), _ENTRY(x## 7), \ 830 _ENTRY(x## 8), _ENTRY(x## 9), _ENTRY(x##10), _ENTRY(x##11), \ 831 _ENTRY(x##12), _ENTRY(x##13), _ENTRY(x##14), _ENTRY(x##15) 832 833 static const struct octeon_pow_dump_reg_entry octeon_pow_dump_reg_entries[] = { 834 _ENTRY (POW_PP_GRP_MSK0), 835 _ENTRY (POW_PP_GRP_MSK1), 836 _ENTRY_0_15 (POW_WQ_INT_THR), 837 _ENTRY_0_15 (POW_WQ_INT_CNT), 838 _ENTRY_0_7 (POW_QOS_THR), 839 _ENTRY_0_7 (POW_QOS_RND), 840 _ENTRY (POW_WQ_INT), 841 _ENTRY (POW_WQ_INT_PC), 842 _ENTRY (POW_NW_TIM), 843 _ENTRY (POW_ECC_ERR), 844 _ENTRY (POW_NOS_CNT), 845 _ENTRY_0_15 (POW_WS_PC), 846 _ENTRY_0_7 (POW_WA_PC), 847 _ENTRY_0_7 (POW_IQ_CNT), 848 _ENTRY (POW_WA_COM_PC), 849 _ENTRY (POW_IQ_COM_CNT), 850 _ENTRY (POW_TS_PC), 851 _ENTRY (POW_DS_PC), 852 _ENTRY (POW_BIST_STAT) 853 }; 854 855 #undef _ENTRY 856 857 void 858 octeon_pow_dump_reg(void) 859 { 860 struct octeon_pow_softc *sc = __octeon_pow_softc; 861 const struct octeon_pow_dump_reg_entry *entry; 862 uint64_t tmp; 863 char buf[512]; 864 int i; 865 866 for (i = 0; i < (int)__arraycount(octeon_pow_dump_reg_entries); i++) { 867 entry = &octeon_pow_dump_reg_entries[i]; 868 tmp = _POW_RD8(sc, entry->offset); 869 if (entry->format == NULL) 870 snprintf(buf, sizeof(buf), "%16" PRIx64, tmp); 871 else 872 snprintb(buf, sizeof(buf), entry->format, tmp); 873 printf("\t%-24s: %s\n", entry->name, buf); 874 } 875 } 876 877 /* ---- operations dump */ 878 879 struct octeon_pow_dump_ops_entry { 880 const char *name; 881 const char *format; 882 uint64_t (*func)(int); 883 }; 884 885 void octeon_pow_dump_ops_coreid(int); 886 void octeon_pow_dump_ops_index(int); 887 void octeon_pow_dump_ops_qos(int); 888 void octeon_pow_dump_ops_grp(int); 889 void octeon_pow_dump_ops_queue(int); 890 void octeon_pow_dump_ops_common(const struct 891 octeon_pow_dump_ops_entry *, size_t, const char *, 892 int); 893 894 #define _ENTRY_COMMON(name, prefix, x, y) \ 895 { #name "_" #x, prefix##_##y##_BITS, octeon_pow_status_by_##name##_##x } 896 897 const struct octeon_pow_dump_ops_entry octeon_pow_dump_ops_coreid_entries[] = { 898 #define _ENTRY(x, y) _ENTRY_COMMON(coreid, POW_STATUS_LOAD_RESULT, x, y) 899 _ENTRY(pend_tag, PEND_TAG), 900 _ENTRY(pend_wqp, PEND_WQP), 901 _ENTRY(cur_tag_next, CUR_TAG_NEXT), 902 _ENTRY(cur_tag_prev, CUR_TAG_PREV), 903 _ENTRY(cur_wqp_next, CUR_WQP_NEXT), 904 _ENTRY(cur_wqp_prev, CUR_WQP_PREV) 905 #undef _ENTRY 906 }; 907 908 const struct octeon_pow_dump_ops_entry octeon_pow_dump_ops_index_entries[] = { 909 #define _ENTRY(x, y) _ENTRY_COMMON(index, POW_MEMORY_LOAD_RESULT, x, y) 910 _ENTRY(tag, TAG), 911 _ENTRY(wqp, WQP), 912 _ENTRY(desched, DESCHED) 913 #undef _ENTRY 914 }; 915 916 const struct octeon_pow_dump_ops_entry octeon_pow_dump_ops_qos_entries[] = { 917 #define _ENTRY(x, y) _ENTRY_COMMON(qos, POW_IDXPTR_LOAD_RESULT_QOS, x, y) 918 _ENTRY(free_loc, FREE_LOC) 919 #undef _ENTRY 920 }; 921 922 const struct octeon_pow_dump_ops_entry octeon_pow_dump_ops_grp_entries[] = { 923 #define _ENTRY(x, y) _ENTRY_COMMON(grp, POW_IDXPTR_LOAD_RESULT_GRP, x, y) 924 _ENTRY(nosched_des, NOSCHED_DES) 925 #undef _ENTRY 926 }; 927 928 const struct octeon_pow_dump_ops_entry octeon_pow_dump_ops_queue_entries[] = { 929 #define _ENTRY(x, y) _ENTRY_COMMON(queue, POW_IDXPTR_LOAD_RESULT_QUEUE, x, y) 930 _ENTRY(remote_head, REMOTE_HEAD), 931 _ENTRY(remote_tail, REMOTE_TAIL) 932 #undef _ENTRY 933 }; 934 935 void 936 octeon_pow_dump_ops(void) 937 { 938 int i; 939 940 /* XXX */ 941 for (i = 0; i < 2/* XXX */; i++) 942 octeon_pow_dump_ops_coreid(i); 943 944 /* XXX */ 945 octeon_pow_dump_ops_index(0); 946 947 for (i = 0; i < 8; i++) 948 octeon_pow_dump_ops_qos(i); 949 950 for (i = 0; i < 16; i++) 951 octeon_pow_dump_ops_grp(i); 952 953 for (i = 0; i < 16; i++) 954 octeon_pow_dump_ops_queue(i); 955 } 956 957 void 958 octeon_pow_dump_ops_coreid(int coreid) 959 { 960 octeon_pow_dump_ops_common(octeon_pow_dump_ops_coreid_entries, 961 __arraycount(octeon_pow_dump_ops_coreid_entries), "coreid", coreid); 962 } 963 964 void 965 octeon_pow_dump_ops_index(int index) 966 { 967 octeon_pow_dump_ops_common(octeon_pow_dump_ops_index_entries, 968 __arraycount(octeon_pow_dump_ops_index_entries), "index", index); 969 } 970 971 void 972 octeon_pow_dump_ops_qos(int qos) 973 { 974 octeon_pow_dump_ops_common(octeon_pow_dump_ops_qos_entries, 975 __arraycount(octeon_pow_dump_ops_qos_entries), "qos", qos); 976 } 977 978 void 979 octeon_pow_dump_ops_grp(int grp) 980 { 981 octeon_pow_dump_ops_common(octeon_pow_dump_ops_grp_entries, 982 __arraycount(octeon_pow_dump_ops_grp_entries), "grp", grp); 983 } 984 985 void 986 octeon_pow_dump_ops_queue(int queue) 987 { 988 octeon_pow_dump_ops_common(octeon_pow_dump_ops_queue_entries, 989 __arraycount(octeon_pow_dump_ops_queue_entries), "queue", queue); 990 } 991 992 void 993 octeon_pow_dump_ops_common(const struct octeon_pow_dump_ops_entry *entries, 994 size_t nentries, const char *by_what, int arg) 995 { 996 const struct octeon_pow_dump_ops_entry *entry; 997 uint64_t tmp; 998 char buf[512]; 999 int i; 1000 1001 printf("%s=%d\n", by_what, arg); 1002 for (i = 0; i < (int)nentries; i++) { 1003 entry = &entries[i]; 1004 tmp = (*entry->func)(arg); 1005 if (entry->format == NULL) 1006 snprintf(buf, sizeof(buf), "%16" PRIx64, tmp); 1007 else 1008 snprintb(buf, sizeof(buf), entry->format, tmp); 1009 printf("\t%-24s: %s\n", entry->name, buf); 1010 } 1011 } 1012 1013 #endif 1014 1015 /* -------------------------------------------------------------------------- */ 1016 1017 /* ---- test */ 1018 1019 #ifdef OCTEON_POW_TEST 1020 /* 1021 * Standalone test entries; meant to be called from ddb. 1022 */ 1023 1024 void octeon_pow_test(void); 1025 void octeon_pow_test_dump_wqe(paddr_t); 1026 1027 static void octeon_pow_test_1(void); 1028 1029 struct test_wqe { 1030 uint64_t word0; 1031 uint64_t word1; 1032 uint64_t word2; 1033 uint64_t word3; 1034 } __packed; 1035 struct test_wqe test_wqe; 1036 1037 void 1038 octeon_pow_test(void) 1039 { 1040 octeon_pow_test_1(); 1041 } 1042 1043 static void 1044 octeon_pow_test_1(void) 1045 { 1046 struct test_wqe *wqe = &test_wqe; 1047 int qos, grp, queue, tt; 1048 uint32_t tag; 1049 paddr_t ptr; 1050 1051 qos = 7; /* XXX */ 1052 grp = queue = 15; /* XXX */ 1053 tt = POW_TAG_TYPE_ORDERED; /* XXX */ 1054 tag = UINT32_C(0x01234567); /* XXX */ 1055 1056 /* => make sure that the queue is empty */ 1057 1058 octeon_pow_dump_ops_qos(qos); 1059 octeon_pow_dump_ops_grp(grp); 1060 printf("\n"); 1061 1062 /* 1063 * Initialize WQE. 1064 * 1065 * word0:next is used by hardware. 1066 * 1067 * word1:qos, word1:grp, word1:tt, word1:tag must match with arguments 1068 * of the following ADDWQ transaction. 1069 */ 1070 1071 (void)memset(wqe, 0, sizeof(*wqe)); 1072 wqe->word0 = 1073 __BITS64_SET(POW_WQE_WORD0_NEXT, 0); 1074 wqe->word1 = 1075 __BITS64_SET(POW_WQE_WORD1_QOS, qos) | 1076 __BITS64_SET(POW_WQE_WORD1_GRP, grp) | 1077 __BITS64_SET(POW_WQE_WORD1_TT, tt) | 1078 __BITS64_SET(POW_WQE_WORD1_TAG, tag); 1079 1080 printf("calling ADDWQ\n"); 1081 octeon_pow_ops_addwq(MIPS_KSEG0_TO_PHYS(wqe), qos, grp, tt, tag); 1082 1083 octeon_pow_dump_ops_qos(qos); 1084 octeon_pow_dump_ops_grp(grp); 1085 printf("\n"); 1086 1087 /* => make sure that a WQE is added to the queue */ 1088 1089 printf("calling GET_WORK_LOAD\n"); 1090 ptr = octeon_pow_ops_get_work_load(0); 1091 1092 octeon_pow_dump_ops_qos(qos); 1093 octeon_pow_dump_ops_grp(grp); 1094 printf("\n"); 1095 1096 octeon_pow_test_dump_wqe(ptr); 1097 1098 /* => make sure that the WQE is in-flight (and scheduled) */ 1099 1100 printf("calling SWTAG(NULL)\n"); 1101 octeon_pow_ops_swtag(POW_TAG_TYPE_NULL, tag); 1102 1103 octeon_pow_dump_ops_qos(qos); 1104 octeon_pow_dump_ops_grp(grp); 1105 printf("\n"); 1106 1107 /* => make sure that the WQE is un-scheduled (completed) */ 1108 } 1109 1110 void 1111 octeon_pow_test_dump_wqe(paddr_t ptr) 1112 { 1113 uint64_t word0, word1; 1114 char buf[128]; 1115 1116 printf("wqe\n"); 1117 1118 word0 = *(uint64_t *)MIPS_PHYS_TO_XKPHYS_CACHED(ptr); 1119 snprintb(buf, sizeof(buf), POW_WQE_WORD0_BITS, word0); 1120 printf("\t%-24s: %s\n", "word0", buf); 1121 1122 word1 = *(uint64_t *)MIPS_PHYS_TO_XKPHYS_CACHED(ptr + 8); 1123 snprintb(buf, sizeof(buf), POW_WQE_WORD1_BITS, word1); 1124 printf("\t%-24s: %s\n", "word1", buf); 1125 } 1126 #endif 1127