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