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