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