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