xref: /openbsd/sys/arch/octeon/dev/cn30xxpow.c (revision 891d7ab6)
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