1 /* $NetBSD: octeon_pow.c,v 1.5 2019/11/10 21:16:30 chs Exp $ */ 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.5 2019/11/10 21:16:30 chs 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_WAITOK); 327 pow_ih->pi_ih = octeon_intr_establish( 328 ffs64(CIU_INTX_SUM0_WORKQ_0) - 1 + group, 329 level, 330 octeon_pow_intr, pow_ih); 331 KASSERT(pow_ih->pi_ih != NULL); 332 333 pow_ih->pi_sc = &octeon_pow_softc; /* XXX */ 334 pow_ih->pi_group = group; 335 pow_ih->pi_cb = cb; 336 pow_ih->pi_data = data; 337 338 #ifdef OCTEON_ETH_DEBUG 339 octeon_pow_intr_debug_init(pow_ih, group); 340 #endif 341 return pow_ih; 342 } 343 344 #ifdef OCTEON_ETH_DEBUG 345 #define _NAMELEN 8 346 #define _DESCRLEN 40 347 348 static void 349 octeon_pow_intr_debug_init(struct octeon_pow_intr_handle *pow_ih, int group) 350 { 351 pow_ih->pi_first = 1; 352 char *name, *descr; 353 int i; 354 355 name = malloc(_NAMELEN + 356 _DESCRLEN * __arraycount(pow_ih->pi_ev_per) + 357 _DESCRLEN * __arraycount(pow_ih->pi_ev_ival), 358 M_DEVBUF, M_WAITOK); 359 descr = name + _NAMELEN; 360 snprintf(name, _NAMELEN, "pow%d", group); 361 for (i = 0; i < (int)__arraycount(pow_ih->pi_ev_per); i++) { 362 int n = 1 << (i - 1); 363 364 (void)snprintf(descr, _DESCRLEN, 365 "# of works per intr (%d-%d)", 366 (i == 0) ? 0 : n, 367 (i == 0) ? 0 : ((n << 1) - 1)); 368 evcnt_attach_dynamic(&pow_ih->pi_ev_per[i], 369 EVCNT_TYPE_MISC, NULL, name, descr); 370 descr += _DESCRLEN; 371 } 372 for (i = 0; i < (int)__arraycount(pow_ih->pi_ev_ival); i++) { 373 int n = 1 << (i - 1); 374 int p, q; 375 char unit; 376 377 p = n; 378 q = (n << 1) - 1; 379 unit = 'u'; 380 /* 381 * 0 is exceptional 382 */ 383 if (i == 0) 384 p = q = 0; 385 /* 386 * count 1024usec as 1msec 387 * 388 * XXX this is not exact 389 */ 390 if ((i - 1) >= 10) { 391 p /= 1000; 392 q /= 1000; 393 unit = 'm'; 394 } 395 (void)snprintf(descr, _DESCRLEN, "intr interval (%d-%d%csec)", 396 p, q, unit); 397 evcnt_attach_dynamic(&pow_ih->pi_ev_ival[i], 398 EVCNT_TYPE_MISC, NULL, name, descr); 399 descr += _DESCRLEN; 400 } 401 evcnt_attach_dynamic(&pow_ih->pi_ev_stray_tc, 402 EVCNT_TYPE_MISC, NULL, name, "stray intr (TC)"); 403 evcnt_attach_dynamic(&pow_ih->pi_ev_stray_ds, 404 EVCNT_TYPE_MISC, NULL, name, "stray intr (DS)"); 405 evcnt_attach_dynamic(&pow_ih->pi_ev_stray_iq, 406 EVCNT_TYPE_MISC, NULL, name, "stray intr (IQ)"); 407 } 408 #endif 409 410 void 411 octeon_pow_init(struct octeon_pow_softc *sc) 412 { 413 octeon_pow_init_regs(sc); 414 415 sc->sc_int_pc_base = 10000; 416 octeon_pow_config_int_pc(sc, sc->sc_int_pc_base); 417 418 #ifdef OCTEON_ETH_DEBUG 419 octeon_pow_error_int_enable(sc, 1); 420 #endif 421 } 422 423 void 424 octeon_pow_init_regs(struct octeon_pow_softc *sc) 425 { 426 int status; 427 428 status = bus_space_map(sc->sc_regt, POW_BASE, POW_SIZE, 0, 429 &sc->sc_regh); 430 if (status != 0) 431 panic("can't map %s space", "pow register"); 432 433 #ifdef OCTEON_ETH_DEBUG 434 _POW_WR8(sc, POW_ECC_ERR_OFFSET, 435 POW_ECC_ERR_IOP_IE | POW_ECC_ERR_RPE_IE | 436 POW_ECC_ERR_DBE_IE | POW_ECC_ERR_SBE_IE); 437 #endif 438 } 439 440 /* -------------------------------------------------------------------------- */ 441 442 /* ---- interrupt handling */ 443 444 #ifdef OCTEON_ETH_DEBUG 445 static inline void 446 octeon_pow_intr_work_debug_ival(struct octeon_pow_softc *sc, 447 struct octeon_pow_intr_handle *pow_ih) 448 { 449 struct timeval now; 450 struct timeval ival; 451 int n; 452 453 microtime(&now); 454 if (__predict_false(pow_ih->pi_first == 1)) { 455 pow_ih->pi_first = 0; 456 goto stat_done; 457 } 458 timersub(&now, &pow_ih->pi_last, &ival); 459 if (ival.tv_sec != 0) 460 goto stat_done; /* XXX */ 461 n = ffs64((uint64_t)ival.tv_usec); 462 if (n > (int)__arraycount(pow_ih->pi_ev_ival) - 1) 463 n = (int)__arraycount(pow_ih->pi_ev_ival) - 1; 464 pow_ih->pi_ev_ival[n].ev_count++; 465 466 stat_done: 467 pow_ih->pi_last = now; /* struct copy */ 468 } 469 470 static inline void 471 octeon_pow_intr_work_debug_per(struct octeon_pow_softc *sc, 472 struct octeon_pow_intr_handle *pow_ih, int count) 473 { 474 int n; 475 476 n = ffs64(count); 477 if (n > (int)__arraycount(pow_ih->pi_ev_per) - 1) 478 n = (int)__arraycount(pow_ih->pi_ev_per) - 1; 479 pow_ih->pi_ev_per[n].ev_count++; 480 #if 1 481 if (count == 0) { 482 uint64_t wq_int_cnt; 483 484 wq_int_cnt = _POW_GROUP_RD8(sc, pow_ih, POW_WQ_INT_CNT0_OFFSET); 485 if (wq_int_cnt & POW_WQ_INT_CNTX_TC_CNT) 486 pow_ih->pi_ev_stray_tc.ev_count++; 487 if (wq_int_cnt & POW_WQ_INT_CNTX_DS_CNT) 488 pow_ih->pi_ev_stray_ds.ev_count++; 489 if (wq_int_cnt & POW_WQ_INT_CNTX_IQ_CNT) 490 pow_ih->pi_ev_stray_iq.ev_count++; 491 } 492 #endif 493 } 494 #endif 495 496 #ifdef OCTEON_ETH_DEBUG 497 #define _POW_INTR_WORK_DEBUG_IVAL(sc, ih) \ 498 octeon_pow_intr_work_debug_ival((sc), (ih)) 499 #define _POW_INTR_WORK_DEBUG_PER(sc, ih, count) \ 500 octeon_pow_intr_work_debug_per((sc), (ih), (count)) 501 #else 502 #define _POW_INTR_WORK_DEBUG_IVAL(sc, ih) \ 503 do {} while (0) 504 #define _POW_INTR_WORK_DEBUG_PER(sc, ih, count) \ 505 do {} while (0) 506 #endif 507 508 /* 509 * Interrupt handling by fixed count, following Cavium's SDK code. 510 * 511 * XXX the fixed count (MAX_RX_CNT) could be changed dynamically? 512 * 513 * XXX this does not utilize "tag switch" very well 514 */ 515 /* 516 * usually all packet receive 517 */ 518 #define MAX_RX_CNT 0x7fffffff 519 520 static inline void 521 octeon_pow_intr_work(struct octeon_pow_softc *sc, 522 struct octeon_pow_intr_handle *pow_ih, int recv_limit) 523 { 524 uint64_t *work; 525 uint64_t count = 0; 526 527 _POW_WR8(sc, POW_PP_GRP_MSK0_OFFSET, UINT64_C(1) << pow_ih->pi_group); 528 529 _POW_INTR_WORK_DEBUG_IVAL(sc, pow_ih); 530 531 for (count = 0; count < recv_limit; count++) { 532 octeon_pow_tag_sw_wait(); 533 octeon_pow_work_request_async( 534 OCTEON_CVMSEG_OFFSET(csm_pow_intr), POW_NO_WAIT); 535 work = (uint64_t *)octeon_pow_work_response_async( 536 OCTEON_CVMSEG_OFFSET(csm_pow_intr)); 537 if (work == NULL) 538 break; 539 (*pow_ih->pi_cb)(pow_ih->pi_data, work); 540 } 541 542 _POW_INTR_WORK_DEBUG_PER(sc, pow_ih, count); 543 } 544 545 static int 546 octeon_pow_intr(void *data) 547 { 548 struct octeon_pow_intr_handle *pow_ih = data; 549 struct octeon_pow_softc *sc = pow_ih->pi_sc; 550 uint64_t wq_int_mask = UINT64_C(0x1) << pow_ih->pi_group; 551 552 #ifdef OCTEON_ETH_INTR_FEEDBACK_RING 553 octeon_pow_intr_work(sc, pow_ih, recv_cnt); 554 #else 555 octeon_pow_intr_work(sc, pow_ih, INT_MAX); 556 #endif /* OCTEON_ETH_INTR_FEEDBACK_RING */ 557 558 _POW_WR8(sc, POW_WQ_INT_OFFSET, wq_int_mask << POW_WQ_INT_WQ_INT_SHIFT); 559 return 1; 560 } 561 562 #ifdef OCTEON_ETH_INTR_FEEDBACK_RING 563 int 564 octeon_pow_ring_reduce(void *arg) 565 { 566 struct octeon_pow_softc *sc = arg; 567 int new, newi; 568 int s; 569 570 #if 0 571 if (ipflow_fastforward_disable_flags == 0) { 572 newi = int_rate = 1; 573 octeon_pow_config_int_pc_rate(sc, int_rate); 574 return recv_cnt; 575 } 576 #endif 577 578 new = recv_cnt / 2; 579 if (new < min_recv_cnt) { 580 newi = int_rate << 1; 581 if (newi > 128) { 582 newi = 128; 583 #ifdef POW_DEBUG 584 log(LOG_DEBUG, 585 "Min intr rate.\n"); 586 #endif 587 new = min_recv_cnt; 588 } 589 else { 590 log(LOG_DEBUG, 591 "pow interrupt rate optimized %d->%d.\n", 592 int_rate, newi); 593 int_rate = newi; 594 octeon_pow_config_int_pc_rate(sc, int_rate); 595 new = max_recv_cnt; 596 } 597 } 598 599 s = splhigh(); /* XXX */ 600 recv_cnt = new; 601 splx(s); 602 603 return new; 604 } 605 606 int 607 octeon_pow_ring_grow(void *arg) 608 { 609 struct octeon_pow_softc *sc = arg; 610 int new, newi; 611 int s; 612 613 #if 0 614 if (ipflow_fastforward_disable_flags == 0) { 615 newi = int_rate = 1; 616 octeon_pow_config_int_pc_rate(sc, int_rate); 617 return recv_cnt; 618 } 619 #endif 620 621 new = recv_cnt + 1; 622 if (new > max_recv_cnt) { 623 newi = int_rate >> 1; 624 if (newi <= 0) { 625 newi = 1; 626 #ifdef POW_DEBUG 627 log(LOG_DEBUG, 628 "Max intr rate.\n"); 629 #endif 630 new = max_recv_cnt; 631 } 632 else { 633 log(LOG_DEBUG, 634 "pow interrupt rate optimized %d->%d.\n", 635 int_rate, newi); 636 int_rate = newi; 637 octeon_pow_config_int_pc_rate(sc, int_rate); 638 new = min_recv_cnt; 639 } 640 } 641 642 s = splhigh(); /* XXX */ 643 recv_cnt = new; 644 splx(s); 645 646 return new; 647 } 648 649 int 650 octeon_pow_ring_size(void) 651 { 652 return recv_cnt; 653 } 654 655 int 656 octeon_pow_ring_intr(void) 657 { 658 return int_rate; 659 } 660 #endif /* OCTEON_ETH_INTR_FEEDBACK_RING */ 661 662 /* -------------------------------------------------------------------------- */ 663 664 /* ---- debug configuration */ 665 666 #ifdef OCTEON_ETH_DEBUG 667 668 void 669 octeon_pow_error_int_enable(void *data, int enable) 670 { 671 struct octeon_pow_softc *sc = data; 672 uint64_t pow_error_int_xxx; 673 674 pow_error_int_xxx = 675 POW_ECC_ERR_IOP | POW_ECC_ERR_RPE | 676 POW_ECC_ERR_DBE | POW_ECC_ERR_SBE; 677 _POW_WR8(sc, POW_ECC_ERR_OFFSET, pow_error_int_xxx); 678 _POW_WR8(sc, POW_ECC_ERR_OFFSET, enable ? pow_error_int_xxx : 0); 679 } 680 681 uint64_t 682 octeon_pow_error_int_summary(void *data) 683 { 684 struct octeon_pow_softc *sc = data; 685 uint64_t summary; 686 687 summary = _POW_RD8(sc, POW_ECC_ERR_OFFSET); 688 _POW_WR8(sc, POW_ECC_ERR_OFFSET, summary); 689 return summary; 690 } 691 692 #endif 693 694 /* -------------------------------------------------------------------------- */ 695 696 /* ---- debug counter */ 697 698 #ifdef OCTEON_ETH_DEBUG 699 int octeon_pow_intr_rml_verbose; 700 struct evcnt octeon_pow_intr_evcnt; 701 702 static const struct octeon_evcnt_entry octeon_pow_intr_evcnt_entries[] = { 703 #define _ENTRY(name, type, parent, descr) \ 704 OCTEON_EVCNT_ENTRY(struct octeon_pow_softc, name, type, parent, descr) 705 _ENTRY(powecciopcsrpend, MISC, NULL, "pow csr load"), 706 _ENTRY(powecciopdbgpend, MISC, NULL, "pow dbg load"), 707 _ENTRY(powecciopaddwork, MISC, NULL, "pow addwork"), 708 _ENTRY(powecciopillop, MISC, NULL, "pow ill op"), 709 _ENTRY(poweccioppend24, MISC, NULL, "pow pend24"), 710 _ENTRY(poweccioppend23, MISC, NULL, "pow pend23"), 711 _ENTRY(poweccioppend22, MISC, NULL, "pow pend22"), 712 _ENTRY(poweccioppend21, MISC, NULL, "pow pend21"), 713 _ENTRY(poweccioptagnull, MISC, NULL, "pow tag null"), 714 _ENTRY(poweccioptagnullnull, MISC, NULL, "pow tag nullnull"), 715 _ENTRY(powecciopordatom, MISC, NULL, "pow ordered atomic"), 716 _ENTRY(powecciopnull, MISC, NULL, "pow core null"), 717 _ENTRY(powecciopnullnull, MISC, NULL, "pow core nullnull"), 718 _ENTRY(poweccrpe, MISC, NULL, "pow remote-pointer error"), 719 _ENTRY(poweccsyn, MISC, NULL, "pow syndrome value"), 720 _ENTRY(poweccdbe, MISC, NULL, "pow double bit"), 721 _ENTRY(poweccsbe, MISC, NULL, "pow single bit"), 722 #undef _ENTRY 723 }; 724 725 void 726 octeon_pow_intr_evcnt_attach(struct octeon_pow_softc *sc) 727 { 728 OCTEON_EVCNT_ATTACH_EVCNTS(sc, octeon_pow_intr_evcnt_entries, "pow0"); 729 } 730 731 void 732 octeon_pow_intr_rml(void *arg) 733 { 734 struct octeon_pow_softc *sc; 735 uint64_t reg; 736 737 octeon_pow_intr_evcnt.ev_count++; 738 sc = __octeon_pow_softc; 739 KASSERT(sc != NULL); 740 reg = octeon_pow_error_int_summary(sc); 741 if (octeon_pow_intr_rml_verbose) 742 printf("%s: POW_ECC_ERR=0x%016" PRIx64 "\n", __func__, reg); 743 switch (reg & POW_ECC_ERR_IOP) { 744 case POW_ECC_ERR_IOP_CSRPEND: 745 OCTEON_EVCNT_INC(sc, powecciopcsrpend); 746 break; 747 case POW_ECC_ERR_IOP_DBGPEND: 748 OCTEON_EVCNT_INC(sc, powecciopdbgpend); 749 break; 750 case POW_ECC_ERR_IOP_ADDWORK: 751 OCTEON_EVCNT_INC(sc, powecciopaddwork); 752 break; 753 case POW_ECC_ERR_IOP_ILLOP: 754 OCTEON_EVCNT_INC(sc, powecciopillop); 755 break; 756 case POW_ECC_ERR_IOP_PEND24: 757 OCTEON_EVCNT_INC(sc, poweccioppend24); 758 break; 759 case POW_ECC_ERR_IOP_PEND23: 760 OCTEON_EVCNT_INC(sc, poweccioppend23); 761 break; 762 case POW_ECC_ERR_IOP_PEND22: 763 OCTEON_EVCNT_INC(sc, poweccioppend22); 764 break; 765 case POW_ECC_ERR_IOP_PEND21: 766 OCTEON_EVCNT_INC(sc, poweccioppend21); 767 break; 768 case POW_ECC_ERR_IOP_TAGNULL: 769 OCTEON_EVCNT_INC(sc, poweccioptagnull); 770 break; 771 case POW_ECC_ERR_IOP_TAGNULLNULL: 772 OCTEON_EVCNT_INC(sc, poweccioptagnullnull); 773 break; 774 case POW_ECC_ERR_IOP_ORDATOM: 775 OCTEON_EVCNT_INC(sc, powecciopordatom); 776 break; 777 case POW_ECC_ERR_IOP_NULL: 778 OCTEON_EVCNT_INC(sc, powecciopnull); 779 break; 780 case POW_ECC_ERR_IOP_NULLNULL: 781 OCTEON_EVCNT_INC(sc, powecciopnullnull); 782 break; 783 default: 784 break; 785 } 786 if (reg & POW_ECC_ERR_RPE) 787 OCTEON_EVCNT_INC(sc, poweccrpe); 788 if (reg & POW_ECC_ERR_SYN) 789 OCTEON_EVCNT_INC(sc, poweccsyn); 790 if (reg & POW_ECC_ERR_DBE) 791 OCTEON_EVCNT_INC(sc, poweccdbe); 792 if (reg & POW_ECC_ERR_SBE) 793 OCTEON_EVCNT_INC(sc, poweccsbe); 794 } 795 #endif 796 797 /* -------------------------------------------------------------------------- */ 798 799 /* ---- debug dump */ 800 801 #ifdef OCTEON_ETH_DEBUG 802 803 void octeon_pow_dump_reg(void); 804 void octeon_pow_dump_ops(void); 805 806 void 807 octeon_pow_dump(void) 808 { 809 octeon_pow_dump_reg(); 810 octeon_pow_dump_ops(); 811 } 812 813 /* ---- register dump */ 814 815 struct octeon_pow_dump_reg_entry { 816 const char *name; 817 const char *format; 818 size_t offset; 819 }; 820 821 #define _ENTRY(x) { #x, x##_BITS, x##_OFFSET } 822 #define _ENTRY_0_7(x) \ 823 _ENTRY(x## 0), _ENTRY(x## 1), _ENTRY(x## 2), _ENTRY(x## 3), \ 824 _ENTRY(x## 4), _ENTRY(x## 5), _ENTRY(x## 6), _ENTRY(x## 7) 825 #define _ENTRY_0_15(x) \ 826 _ENTRY(x## 0), _ENTRY(x## 1), _ENTRY(x## 2), _ENTRY(x## 3), \ 827 _ENTRY(x## 4), _ENTRY(x## 5), _ENTRY(x## 6), _ENTRY(x## 7), \ 828 _ENTRY(x## 8), _ENTRY(x## 9), _ENTRY(x##10), _ENTRY(x##11), \ 829 _ENTRY(x##12), _ENTRY(x##13), _ENTRY(x##14), _ENTRY(x##15) 830 831 static const struct octeon_pow_dump_reg_entry octeon_pow_dump_reg_entries[] = { 832 _ENTRY (POW_PP_GRP_MSK0), 833 _ENTRY (POW_PP_GRP_MSK1), 834 _ENTRY_0_15 (POW_WQ_INT_THR), 835 _ENTRY_0_15 (POW_WQ_INT_CNT), 836 _ENTRY_0_7 (POW_QOS_THR), 837 _ENTRY_0_7 (POW_QOS_RND), 838 _ENTRY (POW_WQ_INT), 839 _ENTRY (POW_WQ_INT_PC), 840 _ENTRY (POW_NW_TIM), 841 _ENTRY (POW_ECC_ERR), 842 _ENTRY (POW_NOS_CNT), 843 _ENTRY_0_15 (POW_WS_PC), 844 _ENTRY_0_7 (POW_WA_PC), 845 _ENTRY_0_7 (POW_IQ_CNT), 846 _ENTRY (POW_WA_COM_PC), 847 _ENTRY (POW_IQ_COM_CNT), 848 _ENTRY (POW_TS_PC), 849 _ENTRY (POW_DS_PC), 850 _ENTRY (POW_BIST_STAT) 851 }; 852 853 #undef _ENTRY 854 855 void 856 octeon_pow_dump_reg(void) 857 { 858 struct octeon_pow_softc *sc = __octeon_pow_softc; 859 const struct octeon_pow_dump_reg_entry *entry; 860 uint64_t tmp; 861 char buf[512]; 862 int i; 863 864 for (i = 0; i < (int)__arraycount(octeon_pow_dump_reg_entries); i++) { 865 entry = &octeon_pow_dump_reg_entries[i]; 866 tmp = _POW_RD8(sc, entry->offset); 867 if (entry->format == NULL) 868 snprintf(buf, sizeof(buf), "%16" PRIx64, tmp); 869 else 870 snprintb(buf, sizeof(buf), entry->format, tmp); 871 printf("\t%-24s: %s\n", entry->name, buf); 872 } 873 } 874 875 /* ---- operations dump */ 876 877 struct octeon_pow_dump_ops_entry { 878 const char *name; 879 const char *format; 880 uint64_t (*func)(int); 881 }; 882 883 void octeon_pow_dump_ops_coreid(int); 884 void octeon_pow_dump_ops_index(int); 885 void octeon_pow_dump_ops_qos(int); 886 void octeon_pow_dump_ops_grp(int); 887 void octeon_pow_dump_ops_queue(int); 888 void octeon_pow_dump_ops_common(const struct 889 octeon_pow_dump_ops_entry *, size_t, const char *, 890 int); 891 892 #define _ENTRY_COMMON(name, prefix, x, y) \ 893 { #name "_" #x, prefix##_##y##_BITS, octeon_pow_status_by_##name##_##x } 894 895 const struct octeon_pow_dump_ops_entry octeon_pow_dump_ops_coreid_entries[] = { 896 #define _ENTRY(x, y) _ENTRY_COMMON(coreid, POW_STATUS_LOAD_RESULT, x, y) 897 _ENTRY(pend_tag, PEND_TAG), 898 _ENTRY(pend_wqp, PEND_WQP), 899 _ENTRY(cur_tag_next, CUR_TAG_NEXT), 900 _ENTRY(cur_tag_prev, CUR_TAG_PREV), 901 _ENTRY(cur_wqp_next, CUR_WQP_NEXT), 902 _ENTRY(cur_wqp_prev, CUR_WQP_PREV) 903 #undef _ENTRY 904 }; 905 906 const struct octeon_pow_dump_ops_entry octeon_pow_dump_ops_index_entries[] = { 907 #define _ENTRY(x, y) _ENTRY_COMMON(index, POW_MEMORY_LOAD_RESULT, x, y) 908 _ENTRY(tag, TAG), 909 _ENTRY(wqp, WQP), 910 _ENTRY(desched, DESCHED) 911 #undef _ENTRY 912 }; 913 914 const struct octeon_pow_dump_ops_entry octeon_pow_dump_ops_qos_entries[] = { 915 #define _ENTRY(x, y) _ENTRY_COMMON(qos, POW_IDXPTR_LOAD_RESULT_QOS, x, y) 916 _ENTRY(free_loc, FREE_LOC) 917 #undef _ENTRY 918 }; 919 920 const struct octeon_pow_dump_ops_entry octeon_pow_dump_ops_grp_entries[] = { 921 #define _ENTRY(x, y) _ENTRY_COMMON(grp, POW_IDXPTR_LOAD_RESULT_GRP, x, y) 922 _ENTRY(nosched_des, NOSCHED_DES) 923 #undef _ENTRY 924 }; 925 926 const struct octeon_pow_dump_ops_entry octeon_pow_dump_ops_queue_entries[] = { 927 #define _ENTRY(x, y) _ENTRY_COMMON(queue, POW_IDXPTR_LOAD_RESULT_QUEUE, x, y) 928 _ENTRY(remote_head, REMOTE_HEAD), 929 _ENTRY(remote_tail, REMOTE_TAIL) 930 #undef _ENTRY 931 }; 932 933 void 934 octeon_pow_dump_ops(void) 935 { 936 int i; 937 938 /* XXX */ 939 for (i = 0; i < 2/* XXX */; i++) 940 octeon_pow_dump_ops_coreid(i); 941 942 /* XXX */ 943 octeon_pow_dump_ops_index(0); 944 945 for (i = 0; i < 8; i++) 946 octeon_pow_dump_ops_qos(i); 947 948 for (i = 0; i < 16; i++) 949 octeon_pow_dump_ops_grp(i); 950 951 for (i = 0; i < 16; i++) 952 octeon_pow_dump_ops_queue(i); 953 } 954 955 void 956 octeon_pow_dump_ops_coreid(int coreid) 957 { 958 octeon_pow_dump_ops_common(octeon_pow_dump_ops_coreid_entries, 959 __arraycount(octeon_pow_dump_ops_coreid_entries), "coreid", coreid); 960 } 961 962 void 963 octeon_pow_dump_ops_index(int index) 964 { 965 octeon_pow_dump_ops_common(octeon_pow_dump_ops_index_entries, 966 __arraycount(octeon_pow_dump_ops_index_entries), "index", index); 967 } 968 969 void 970 octeon_pow_dump_ops_qos(int qos) 971 { 972 octeon_pow_dump_ops_common(octeon_pow_dump_ops_qos_entries, 973 __arraycount(octeon_pow_dump_ops_qos_entries), "qos", qos); 974 } 975 976 void 977 octeon_pow_dump_ops_grp(int grp) 978 { 979 octeon_pow_dump_ops_common(octeon_pow_dump_ops_grp_entries, 980 __arraycount(octeon_pow_dump_ops_grp_entries), "grp", grp); 981 } 982 983 void 984 octeon_pow_dump_ops_queue(int queue) 985 { 986 octeon_pow_dump_ops_common(octeon_pow_dump_ops_queue_entries, 987 __arraycount(octeon_pow_dump_ops_queue_entries), "queue", queue); 988 } 989 990 void 991 octeon_pow_dump_ops_common(const struct octeon_pow_dump_ops_entry *entries, 992 size_t nentries, const char *by_what, int arg) 993 { 994 const struct octeon_pow_dump_ops_entry *entry; 995 uint64_t tmp; 996 char buf[512]; 997 int i; 998 999 printf("%s=%d\n", by_what, arg); 1000 for (i = 0; i < (int)nentries; i++) { 1001 entry = &entries[i]; 1002 tmp = (*entry->func)(arg); 1003 if (entry->format == NULL) 1004 snprintf(buf, sizeof(buf), "%16" PRIx64, tmp); 1005 else 1006 snprintb(buf, sizeof(buf), entry->format, tmp); 1007 printf("\t%-24s: %s\n", entry->name, buf); 1008 } 1009 } 1010 1011 #endif 1012 1013 /* -------------------------------------------------------------------------- */ 1014 1015 /* ---- test */ 1016 1017 #ifdef OCTEON_POW_TEST 1018 /* 1019 * Standalone test entries; meant to be called from ddb. 1020 */ 1021 1022 void octeon_pow_test(void); 1023 void octeon_pow_test_dump_wqe(paddr_t); 1024 1025 static void octeon_pow_test_1(void); 1026 1027 struct test_wqe { 1028 uint64_t word0; 1029 uint64_t word1; 1030 uint64_t word2; 1031 uint64_t word3; 1032 } __packed; 1033 struct test_wqe test_wqe; 1034 1035 void 1036 octeon_pow_test(void) 1037 { 1038 octeon_pow_test_1(); 1039 } 1040 1041 static void 1042 octeon_pow_test_1(void) 1043 { 1044 struct test_wqe *wqe = &test_wqe; 1045 int qos, grp, queue, tt; 1046 uint32_t tag; 1047 paddr_t ptr; 1048 1049 qos = 7; /* XXX */ 1050 grp = queue = 15; /* XXX */ 1051 tt = POW_TAG_TYPE_ORDERED; /* XXX */ 1052 tag = UINT32_C(0x01234567); /* XXX */ 1053 1054 /* => make sure that the queue is empty */ 1055 1056 octeon_pow_dump_ops_qos(qos); 1057 octeon_pow_dump_ops_grp(grp); 1058 printf("\n"); 1059 1060 /* 1061 * Initialize WQE. 1062 * 1063 * word0:next is used by hardware. 1064 * 1065 * word1:qos, word1:grp, word1:tt, word1:tag must match with arguments 1066 * of the following ADDWQ transaction. 1067 */ 1068 1069 (void)memset(wqe, 0, sizeof(*wqe)); 1070 wqe->word0 = 1071 __BITS64_SET(POW_WQE_WORD0_NEXT, 0); 1072 wqe->word1 = 1073 __BITS64_SET(POW_WQE_WORD1_QOS, qos) | 1074 __BITS64_SET(POW_WQE_WORD1_GRP, grp) | 1075 __BITS64_SET(POW_WQE_WORD1_TT, tt) | 1076 __BITS64_SET(POW_WQE_WORD1_TAG, tag); 1077 1078 printf("calling ADDWQ\n"); 1079 octeon_pow_ops_addwq(MIPS_KSEG0_TO_PHYS(wqe), qos, grp, tt, tag); 1080 1081 octeon_pow_dump_ops_qos(qos); 1082 octeon_pow_dump_ops_grp(grp); 1083 printf("\n"); 1084 1085 /* => make sure that a WQE is added to the queue */ 1086 1087 printf("calling GET_WORK_LOAD\n"); 1088 ptr = octeon_pow_ops_get_work_load(0); 1089 1090 octeon_pow_dump_ops_qos(qos); 1091 octeon_pow_dump_ops_grp(grp); 1092 printf("\n"); 1093 1094 octeon_pow_test_dump_wqe(ptr); 1095 1096 /* => make sure that the WQE is in-flight (and scheduled) */ 1097 1098 printf("calling SWTAG(NULL)\n"); 1099 octeon_pow_ops_swtag(POW_TAG_TYPE_NULL, tag); 1100 1101 octeon_pow_dump_ops_qos(qos); 1102 octeon_pow_dump_ops_grp(grp); 1103 printf("\n"); 1104 1105 /* => make sure that the WQE is un-scheduled (completed) */ 1106 } 1107 1108 void 1109 octeon_pow_test_dump_wqe(paddr_t ptr) 1110 { 1111 uint64_t word0, word1; 1112 char buf[128]; 1113 1114 printf("wqe\n"); 1115 1116 word0 = *(uint64_t *)MIPS_PHYS_TO_XKPHYS_CACHED(ptr); 1117 snprintb(buf, sizeof(buf), POW_WQE_WORD0_BITS, word0); 1118 printf("\t%-24s: %s\n", "word0", buf); 1119 1120 word1 = *(uint64_t *)MIPS_PHYS_TO_XKPHYS_CACHED(ptr + 8); 1121 snprintb(buf, sizeof(buf), POW_WQE_WORD1_BITS, word1); 1122 printf("\t%-24s: %s\n", "word1", buf); 1123 } 1124 #endif 1125