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