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