xref: /openbsd/sys/arch/octeon/dev/cn30xxpow.c (revision 9b7c3dbb)
1 /*	$OpenBSD: cn30xxpow.c,v 1.8 2016/07/10 10:18:58 visa 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 
35 #include <machine/bus.h>
36 #include <machine/octeonvar.h>
37 
38 #include <octeon/dev/iobusvar.h>
39 #include <octeon/dev/cn30xxciureg.h>	/* XXX */
40 #include <octeon/dev/cn30xxpowreg.h>
41 #include <octeon/dev/cn30xxpowvar.h>
42 
43 extern int ipflow_fastforward_disable_flags;
44 
45 struct cn30xxpow_intr_handle {
46 	void				*pi_ih;
47 	struct cn30xxpow_softc		*pi_sc;
48 	int				pi_group;
49 	void				(*pi_cb)(void *, uint64_t *);
50 	void				*pi_data;
51 
52 #ifdef OCTEON_ETH_DEBUG
53 #define	_EV_PER_N	32	/* XXX */
54 #define	_EV_IVAL_N	32	/* XXX */
55 	int				pi_first;
56 	struct timeval			pi_last;
57 #endif
58 };
59 
60 void	cn30xxpow_bootstrap(struct octeon_config *);
61 
62 #ifdef OCTEON_ETH_DEBUG
63 void	cn30xxpow_intr_rml(void *);
64 
65 void	cn30xxpow_intr_debug_init(struct cn30xxpow_intr_handle *, int);
66 void	cn30xxpow_intr_work_debug_ival(struct cn30xxpow_softc *,
67 	    struct cn30xxpow_intr_handle *);
68 #endif
69 void	cn30xxpow_init(struct cn30xxpow_softc *);
70 void	cn30xxpow_init_regs(struct cn30xxpow_softc *);
71 int	cn30xxpow_tag_sw_poll(void);
72 void	cn30xxpow_tag_sw_wait(void);
73 void	cn30xxpow_config_int_pc(struct cn30xxpow_softc *, int);
74 void	cn30xxpow_config_int(struct cn30xxpow_softc *, int,
75 	    uint64_t, uint64_t, uint64_t);
76 void	cn30xxpow_intr_work(struct cn30xxpow_softc *,
77 	    struct cn30xxpow_intr_handle *, int);
78 int	cn30xxpow_intr(void *);
79 
80 #ifdef OCTEON_ETH_DEBUG
81 void	cn30xxpow_dump(void);
82 #endif
83 
84 /* XXX */
85 struct cn30xxpow_softc	cn30xxpow_softc;
86 
87 #ifdef OCTEON_ETH_DEBUG
88 struct cn30xxpow_softc *__cn30xxpow_softc;
89 #endif
90 
91 /*
92  * XXX: parameter tuning is needed: see files.octeon
93  */
94 #ifndef OCTEON_ETH_RING_MAX
95 #define OCTEON_ETH_RING_MAX 512
96 #endif
97 #ifndef OCTEON_ETH_RING_MIN
98 #define OCTEON_ETH_RING_MIN 1
99 #endif
100 
101 #ifdef OCTEON_ETH_INTR_FEEDBACK_RING
102 int max_recv_cnt = OCTEON_ETH_RING_MAX;
103 int min_recv_cnt = OCTEON_ETH_RING_MIN;
104 int recv_cnt = OCTEON_ETH_RING_MIN;
105 int int_rate = 1;
106 #else
107 /* infinity */
108 int max_recv_cnt = 0;
109 int min_recv_cnt = 0;
110 int recv_cnt = 0;
111 #endif
112 
113 /* -------------------------------------------------------------------------- */
114 
115 /* ---- operation primitive functions */
116 
117 /* 5.11.1 Load Operations */
118 
119 /* 5.11.2 IOBDMA Operations */
120 
121 /* 5.11.3 Store Operations */
122 
123 /* -------------------------------------------------------------------------- */
124 
125 /* ---- utility functions */
126 
127 void
128 cn30xxpow_work_request_async(uint64_t scraddr, uint64_t wait)
129 {
130         cn30xxpow_ops_get_work_iobdma(scraddr, wait);
131 }
132 
133 uint64_t *
134 cn30xxpow_work_response_async(uint64_t scraddr)
135 {
136 	uint64_t result;
137 
138 	octeon_synciobdma();
139 	result = octeon_cvmseg_read_8(scraddr);
140 
141 	return (result & POW_IOBDMA_GET_WORK_RESULT_NO_WORK) ?
142 	    NULL :
143 	    (uint64_t *)PHYS_TO_XKPHYS(
144 		result & POW_IOBDMA_GET_WORK_RESULT_ADDR, CCA_CACHED);
145 }
146 
147 /* ---- status by coreid */
148 
149 static inline uint64_t
150 cn30xxpow_status_by_coreid_pend_tag(int coreid)
151 {
152 	return cn30xxpow_ops_pow_status(coreid, 0, 0, 0);
153 }
154 
155 static inline uint64_t
156 cn30xxpow_status_by_coreid_pend_wqp(int coreid)
157 {
158 	return cn30xxpow_ops_pow_status(coreid, 0, 0, 1);
159 }
160 
161 static inline uint64_t
162 cn30xxpow_status_by_coreid_cur_tag_next(int coreid)
163 {
164 	return cn30xxpow_ops_pow_status(coreid, 0, 1, 0);
165 }
166 
167 static inline uint64_t
168 cn30xxpow_status_by_coreid_cur_tag_prev(int coreid)
169 {
170 	return cn30xxpow_ops_pow_status(coreid, 1, 1, 0);
171 }
172 
173 static inline uint64_t
174 cn30xxpow_status_by_coreid_cur_wqp_next(int coreid)
175 {
176 	return cn30xxpow_ops_pow_status(coreid, 0, 1, 1);
177 }
178 
179 static inline uint64_t
180 cn30xxpow_status_by_coreid_cur_wqp_prev(int coreid)
181 {
182 	return cn30xxpow_ops_pow_status(coreid, 1, 1, 1);
183 }
184 
185 /* ---- status by index */
186 
187 static inline uint64_t
188 cn30xxpow_status_by_index_tag(int index)
189 {
190 	return cn30xxpow_ops_pow_memory(index, 0, 0);
191 }
192 
193 static inline uint64_t
194 cn30xxpow_status_by_index_wqp(int index)
195 {
196 	return cn30xxpow_ops_pow_memory(index, 0, 1);
197 }
198 
199 static inline uint64_t
200 cn30xxpow_status_by_index_desched(int index)
201 {
202 	return cn30xxpow_ops_pow_memory(index, 1, 0);
203 }
204 
205 /* ---- status by qos level */
206 
207 static inline uint64_t
208 cn30xxpow_status_by_qos_free_loc(int qos)
209 {
210 	return cn30xxpow_ops_pow_idxptr(qos, 0, 0);
211 }
212 
213 /* ---- status by desched group */
214 
215 static inline uint64_t
216 cn30xxpow_status_by_grp_nosched_des(int grp)
217 {
218 	return cn30xxpow_ops_pow_idxptr(grp, 0, 1);
219 }
220 
221 /* ---- status by memory input queue */
222 
223 static inline uint64_t
224 cn30xxpow_status_by_queue_remote_head(int queue)
225 {
226 	return cn30xxpow_ops_pow_idxptr(queue, 1, 0);
227 }
228 
229 static inline uint64_t
230 cn30xxpow_status_by_queue_remote_tail(int queue)
231 {
232 	return cn30xxpow_ops_pow_idxptr(queue, 1, 0);
233 }
234 
235 /* ---- tag switch */
236 
237 /*
238  * "RDHWR rt, $30" returns:
239  *	0 => pending bit is set
240  *	1 => pending bit is clear
241  */
242 
243 /* return 1 if pending bit is clear (ready) */
244 int
245 cn30xxpow_tag_sw_poll(void)
246 {
247 	uint64_t result;
248 
249 	__asm volatile (
250 		"	.set	push		\n"
251 		"	.set	noreorder	\n"
252 		"	.set	arch=mips64r2	\n"
253 		"	rdhwr	%[result], $30	\n"
254 		"	.set	pop		\n"
255 		: [result]"=r"(result)
256 	);
257 	return (int)result;
258 }
259 
260 void
261 cn30xxpow_tag_sw_wait(void)
262 {
263 
264 	while (cn30xxpow_tag_sw_poll() == 0)
265 		continue;
266 }
267 
268 /* -------------------------------------------------------------------------- */
269 
270 /* ---- initialization and configuration */
271 
272 void
273 cn30xxpow_bootstrap(struct octeon_config *mcp)
274 {
275 	struct cn30xxpow_softc *sc = &cn30xxpow_softc;
276 
277 	sc->sc_regt = mcp->mc_iobus_bust;
278 	/* XXX */
279 
280 	cn30xxpow_init(sc);
281 
282 #ifdef OCTEON_ETH_DEBUG
283 	__cn30xxpow_softc = sc;
284 #endif
285 
286 }
287 
288 void
289 cn30xxpow_config_int(struct cn30xxpow_softc *sc, int group,
290    uint64_t tc_thr, uint64_t ds_thr, uint64_t iq_thr)
291 {
292 	uint64_t wq_int_thr;
293 
294 	wq_int_thr =
295 	    POW_WQ_INT_THRX_TC_EN |
296 	    (tc_thr << POW_WQ_INT_THRX_TC_THR_SHIFT) |
297 	    (ds_thr << POW_WQ_INT_THRX_DS_THR_SHIFT) |
298 	    (iq_thr << POW_WQ_INT_THRX_IQ_THR_SHIFT);
299 	_POW_WR8(sc, POW_WQ_INT_THR0_OFFSET + (group * 8), wq_int_thr);
300 }
301 
302 /*
303  * interrupt threshold configuration
304  *
305  * => DS / IQ
306  *    => ...
307  * => time counter threshold
308  *    => unit is 1msec
309  *    => each group can set timeout
310  * => temporary disable bit
311  *    => use CIU generic timer
312  */
313 
314 void
315 cn30xxpow_config(struct cn30xxpow_softc *sc, int group)
316 {
317 
318 	cn30xxpow_config_int(sc, group,
319 	    0x0f,		/* TC */
320 	    0x00,		/* DS */
321 	    0x00);		/* IQ */
322 }
323 
324 void *
325 cn30xxpow_intr_establish(int group, int level,
326     void (*cb)(void *, uint64_t *), void (*fcb)(int*, int *, uint64_t, void *),
327     void *data, char *what)
328 {
329 	struct cn30xxpow_intr_handle *pow_ih;
330 
331 	KASSERT(group >= 0);
332 	KASSERT(group < 16);
333 
334 	pow_ih = malloc(sizeof(*pow_ih), M_DEVBUF, M_NOWAIT);
335 	KASSERT(pow_ih != NULL);	/* XXX handle failure */
336 
337 	pow_ih->pi_ih = octeon_intr_establish(
338 	    ffs64(CIU_INTX_SUM0_WORKQ_0) - 1 + group,
339 	    level,
340 	    cn30xxpow_intr, pow_ih, what);
341 	KASSERT(pow_ih->pi_ih != NULL);
342 
343 	pow_ih->pi_sc = &cn30xxpow_softc;	/* XXX */
344 	pow_ih->pi_group = group;
345 	pow_ih->pi_cb = cb;
346 	pow_ih->pi_data = data;
347 
348 #ifdef OCTEON_ETH_DEBUG
349 	cn30xxpow_intr_debug_init(pow_ih, group);
350 #endif
351 	return pow_ih;
352 }
353 
354 #ifdef OCTEON_ETH_DEBUG
355 
356 void
357 cn30xxpow_intr_debug_init(struct cn30xxpow_intr_handle *pow_ih, int group)
358 {
359 	pow_ih->pi_first = 1;
360 
361 }
362 #endif
363 
364 void
365 cn30xxpow_init(struct cn30xxpow_softc *sc)
366 {
367 	cn30xxpow_init_regs(sc);
368 
369 	sc->sc_int_pc_base = 10000;
370 	cn30xxpow_config_int_pc(sc, sc->sc_int_pc_base);
371 
372 #ifdef OCTEON_ETH_DEBUG
373 	cn30xxpow_error_int_enable(sc, 1);
374 #endif
375 }
376 
377 void
378 cn30xxpow_init_regs(struct cn30xxpow_softc *sc)
379 {
380 	int status;
381 
382 	status = bus_space_map(sc->sc_regt, POW_BASE, POW_SIZE, 0,
383 	    &sc->sc_regh);
384 	if (status != 0)
385 		panic("can't map %s space", "pow register");
386 
387 #ifdef OCTEON_ETH_DEBUG
388 	_POW_WR8(sc, POW_ECC_ERR_OFFSET,
389 	    POW_ECC_ERR_IOP_IE | POW_ECC_ERR_RPE_IE |
390 	    POW_ECC_ERR_DBE_IE | POW_ECC_ERR_SBE_IE);
391 #endif
392 }
393 
394 /* -------------------------------------------------------------------------- */
395 
396 /* ---- interrupt handling */
397 
398 #ifdef OCTEON_ETH_DEBUG
399 void
400 cn30xxpow_intr_work_debug_ival(struct cn30xxpow_softc *sc,
401     struct cn30xxpow_intr_handle *pow_ih)
402 {
403 	struct timeval now;
404 	struct timeval ival;
405 
406 	microtime(&now);
407 	if (__predict_false(pow_ih->pi_first == 1)) {
408 		pow_ih->pi_first = 0;
409 		goto stat_done;
410 	}
411 	timersub(&now, &pow_ih->pi_last, &ival);
412 	if (ival.tv_sec != 0)
413 		goto stat_done;	/* XXX */
414 
415 stat_done:
416 	pow_ih->pi_last = now;	/* struct copy */
417 }
418 #endif
419 
420 #ifdef OCTEON_ETH_DEBUG
421 #define _POW_INTR_WORK_DEBUG_IVAL(sc, ih) \
422 	    cn30xxpow_intr_work_debug_ival((sc), (ih))
423 #else
424 #define _POW_INTR_WORK_DEBUG_IVAL(sc, ih) \
425 	    do {} while (0)
426 #endif
427 
428 /*
429  * Interrupt handling by fixed count.
430  *
431  * XXX the fixed count (MAX_RX_CNT) could be changed dynamically?
432  *
433  * XXX this does not utilize "tag switch" very well
434  */
435 /*
436  * usually all packet recieve
437  */
438 #define MAX_RX_CNT 0x7fffffff
439 
440 void
441 cn30xxpow_intr_work(struct cn30xxpow_softc *sc,
442     struct cn30xxpow_intr_handle *pow_ih, int max_recv_cnt)
443 {
444 	uint64_t *work;
445 	uint64_t count = 0;
446 	int recv_cnt = MAX_RX_CNT;
447 
448 	/* s = splhigh(); */
449 	_POW_WR8(sc, POW_PP_GRP_MSK0_OFFSET, 1ULL << pow_ih->pi_group);
450 
451 	if (max_recv_cnt > 0)
452 		recv_cnt = max_recv_cnt - 1;
453 
454 	_POW_INTR_WORK_DEBUG_IVAL(sc, pow_ih);
455 
456 	cn30xxpow_tag_sw_wait();
457 	cn30xxpow_work_request_async(OCTEON_CVMSEG_OFFSET(csm_pow_intr),
458 	    POW_NO_WAIT);
459 
460 	for (count = 0; count < recv_cnt; count++) {
461 		work = (uint64_t *)cn30xxpow_work_response_async(
462 		    OCTEON_CVMSEG_OFFSET(csm_pow_intr));
463 		if (work == NULL)
464 			goto done;
465 		cn30xxpow_tag_sw_wait();
466 		cn30xxpow_work_request_async(
467 		    OCTEON_CVMSEG_OFFSET(csm_pow_intr), POW_NO_WAIT);
468 		(*pow_ih->pi_cb)(pow_ih->pi_data, work);
469 	}
470 
471 	work = (uint64_t *)cn30xxpow_work_response_async(
472 	    OCTEON_CVMSEG_OFFSET(csm_pow_intr));
473 	if (work == NULL)
474 		goto done;
475 
476 	(*pow_ih->pi_cb)(pow_ih->pi_data, work);
477 	count++;
478 
479 done:
480 	;
481 	/* KASSERT(work == NULL); */
482 	/* KASSERT(count > 0); */
483 
484 	/* _POW_WR8(sc, POW_PP_GRP, 0)ULL; */
485 	/* splx(s); */
486 }
487 
488 int
489 cn30xxpow_intr(void *data)
490 {
491 	struct cn30xxpow_intr_handle *pow_ih = data;
492 	struct cn30xxpow_softc *sc = pow_ih->pi_sc;
493 	uint64_t wq_int_mask = 0x1ULL << pow_ih->pi_group;
494 
495 #if 0
496 	if (ipflow_fastforward_disable_flags == 0)
497 		cn30xxpow_intr_work(sc, pow_ih, -1);
498 	else
499 		cn30xxpow_intr_work(sc, pow_ih, recv_cnt);
500 #else
501 	cn30xxpow_intr_work(sc, pow_ih, recv_cnt);
502 #endif
503 
504 	_POW_WR8(sc, POW_WQ_INT_OFFSET, wq_int_mask << POW_WQ_INT_WQ_INT_SHIFT);
505 	return 1;
506 }
507 
508 /* -------------------------------------------------------------------------- */
509 
510 /* ---- debug configuration */
511 
512 #ifdef OCTEON_ETH_DEBUG
513 
514 void
515 cn30xxpow_error_int_enable(void *data, int enable)
516 {
517 	struct cn30xxpow_softc *sc = data;
518 	uint64_t pow_error_int_xxx;
519 
520 	pow_error_int_xxx =
521 	    POW_ECC_ERR_IOP | POW_ECC_ERR_RPE |
522 	    POW_ECC_ERR_DBE | POW_ECC_ERR_SBE;
523 	_POW_WR8(sc, POW_ECC_ERR_OFFSET, pow_error_int_xxx);
524 	_POW_WR8(sc, POW_ECC_ERR_OFFSET, enable ? pow_error_int_xxx : 0);
525 }
526 
527 uint64_t
528 cn30xxpow_error_int_summary(void *data)
529 {
530 	struct cn30xxpow_softc *sc = data;
531 	uint64_t summary;
532 
533 	summary = _POW_RD8(sc, POW_ECC_ERR_OFFSET);
534 	_POW_WR8(sc, POW_ECC_ERR_OFFSET, summary);
535 	return summary;
536 }
537 
538 #endif
539 
540 /* -------------------------------------------------------------------------- */
541 
542 /* ---- debug counter */
543 
544 #ifdef OCTEON_ETH_DEBUG
545 int	cn30xxpow_intr_rml_verbose;
546 
547 void
548 cn30xxpow_intr_rml(void *arg)
549 {
550 	struct cn30xxpow_softc *sc;
551 	uint64_t reg;
552 
553 	sc = __cn30xxpow_softc;
554 	KASSERT(sc != NULL);
555 	reg = cn30xxpow_error_int_summary(sc);
556 	if (cn30xxpow_intr_rml_verbose)
557 		printf("%s: POW_ECC_ERR=0x%016llx\n", __func__, reg);
558 }
559 #endif
560 
561 /* -------------------------------------------------------------------------- */
562 
563 /* ---- debug dump */
564 
565 #ifdef OCTEON_ETH_DEBUG
566 
567 void	cn30xxpow_dump_reg(void);
568 void	cn30xxpow_dump_ops(void);
569 
570 void
571 cn30xxpow_dump(void)
572 {
573 	cn30xxpow_dump_reg();
574 	cn30xxpow_dump_ops();
575 }
576 
577 /* ---- register dump */
578 
579 struct cn30xxpow_dump_reg_entry {
580 	const char *name;
581 	size_t offset;
582 };
583 
584 #define	_ENTRY(x)	{ #x, x##_OFFSET }
585 #define	_ENTRY_0_7(x) \
586 	_ENTRY(x## 0), _ENTRY(x## 1), _ENTRY(x## 2), _ENTRY(x## 3), \
587 	_ENTRY(x## 4), _ENTRY(x## 5), _ENTRY(x## 6), _ENTRY(x## 7)
588 #define	_ENTRY_0_15(x) \
589 	_ENTRY(x## 0), _ENTRY(x## 1), _ENTRY(x## 2), _ENTRY(x## 3), \
590 	_ENTRY(x## 4), _ENTRY(x## 5), _ENTRY(x## 6), _ENTRY(x## 7), \
591 	_ENTRY(x## 8), _ENTRY(x## 9), _ENTRY(x##10), _ENTRY(x##11), \
592 	_ENTRY(x##12), _ENTRY(x##13), _ENTRY(x##14), _ENTRY(x##15)
593 
594 const struct cn30xxpow_dump_reg_entry cn30xxpow_dump_reg_entries[] = {
595 	_ENTRY		(POW_PP_GRP_MSK0),
596 	_ENTRY		(POW_PP_GRP_MSK1),
597 	_ENTRY_0_15	(POW_WQ_INT_THR),
598 	_ENTRY_0_15	(POW_WQ_INT_CNT),
599 	_ENTRY_0_7	(POW_QOS_THR),
600 	_ENTRY_0_7	(POW_QOS_RND),
601 	_ENTRY		(POW_WQ_INT),
602 	_ENTRY		(POW_WQ_INT_PC),
603 	_ENTRY		(POW_NW_TIM),
604 	_ENTRY		(POW_ECC_ERR),
605 	_ENTRY		(POW_NOS_CNT),
606 	_ENTRY_0_15	(POW_WS_PC),
607 	_ENTRY_0_7	(POW_WA_PC),
608 	_ENTRY_0_7	(POW_IQ_CNT),
609 	_ENTRY		(POW_WA_COM_PC),
610 	_ENTRY		(POW_IQ_COM_CNT),
611 	_ENTRY		(POW_TS_PC),
612 	_ENTRY		(POW_DS_PC),
613 	_ENTRY		(POW_BIST_STAT)
614 };
615 
616 #undef _ENTRY
617 
618 void
619 cn30xxpow_dump_reg(void)
620 {
621 	struct cn30xxpow_softc *sc = __cn30xxpow_softc;
622 	const struct cn30xxpow_dump_reg_entry *entry;
623 	uint64_t tmp;
624 	int i;
625 
626 	for (i = 0; i < (int)nitems(cn30xxpow_dump_reg_entries); i++) {
627 		entry = &cn30xxpow_dump_reg_entries[i];
628 		tmp = _POW_RD8(sc, entry->offset);
629 		printf("\t%-24s: %16llx\n", entry->name, tmp);
630 	}
631 }
632 
633 /* ---- operations dump */
634 
635 struct cn30xxpow_dump_ops_entry {
636 	const char *name;
637 	uint64_t (*func)(int);
638 };
639 
640 void	cn30xxpow_dump_ops_coreid(int);
641 void	cn30xxpow_dump_ops_index(int);
642 void	cn30xxpow_dump_ops_qos(int);
643 void	cn30xxpow_dump_ops_grp(int);
644 void	cn30xxpow_dump_ops_queue(int);
645 void	cn30xxpow_dump_ops_common(const struct cn30xxpow_dump_ops_entry *,
646 	    size_t, const char *, int);
647 
648 #define	_ENTRY_COMMON(name, prefix, x, y) \
649 	{ #name "_" #x, cn30xxpow_status_by_##name##_##x }
650 
651 const struct cn30xxpow_dump_ops_entry cn30xxpow_dump_ops_coreid_entries[] = {
652 #define	_ENTRY(x, y)	_ENTRY_COMMON(coreid, POW_STATUS_LOAD_RESULT, x, y)
653 	_ENTRY(pend_tag, PEND_TAG),
654 	_ENTRY(pend_wqp, PEND_WQP),
655 	_ENTRY(cur_tag_next, CUR_TAG_NEXT),
656 	_ENTRY(cur_tag_prev, CUR_TAG_PREV),
657 	_ENTRY(cur_wqp_next, CUR_WQP_NEXT),
658 	_ENTRY(cur_wqp_prev, CUR_WQP_PREV)
659 #undef _ENTRY
660 };
661 
662 const struct cn30xxpow_dump_ops_entry cn30xxpow_dump_ops_index_entries[] = {
663 #define	_ENTRY(x, y)	_ENTRY_COMMON(index, POW_MEMORY_LOAD_RESULT, x, y)
664 	_ENTRY(tag, TAG),
665 	_ENTRY(wqp, WQP),
666 	_ENTRY(desched, DESCHED)
667 #undef _ENTRY
668 };
669 
670 const struct cn30xxpow_dump_ops_entry cn30xxpow_dump_ops_qos_entries[] = {
671 #define	_ENTRY(x, y)	_ENTRY_COMMON(qos, POW_IDXPTR_LOAD_RESULT_QOS, x, y)
672 	_ENTRY(free_loc, FREE_LOC)
673 #undef _ENTRY
674 };
675 
676 const struct cn30xxpow_dump_ops_entry cn30xxpow_dump_ops_grp_entries[] = {
677 #define	_ENTRY(x, y)	_ENTRY_COMMON(grp, POW_IDXPTR_LOAD_RESULT_GRP, x, y)
678 	_ENTRY(nosched_des, NOSCHED_DES)
679 #undef _ENTRY
680 };
681 
682 const struct cn30xxpow_dump_ops_entry cn30xxpow_dump_ops_queue_entries[] = {
683 #define	_ENTRY(x, y)	_ENTRY_COMMON(queue, POW_IDXPTR_LOAD_RESULT_QUEUE, x, y)
684 	_ENTRY(remote_head, REMOTE_HEAD),
685 	_ENTRY(remote_tail, REMOTE_TAIL)
686 #undef _ENTRY
687 };
688 
689 void
690 cn30xxpow_dump_ops(void)
691 {
692 	int i;
693 
694 	/* XXX */
695 	for (i = 0; i < 2/* XXX */; i++)
696 		cn30xxpow_dump_ops_coreid(i);
697 
698 	/* XXX */
699 	cn30xxpow_dump_ops_index(0);
700 
701 	for (i = 0; i < 8; i++)
702 		cn30xxpow_dump_ops_qos(i);
703 
704 	for (i = 0; i < 16; i++)
705 		cn30xxpow_dump_ops_grp(i);
706 
707 	for (i = 0; i < 16; i++)
708 		cn30xxpow_dump_ops_queue(i);
709 }
710 
711 void
712 cn30xxpow_dump_ops_coreid(int coreid)
713 {
714 	cn30xxpow_dump_ops_common(cn30xxpow_dump_ops_coreid_entries,
715 	    nitems(cn30xxpow_dump_ops_coreid_entries), "coreid", coreid);
716 }
717 
718 void
719 cn30xxpow_dump_ops_index(int index)
720 {
721 	cn30xxpow_dump_ops_common(cn30xxpow_dump_ops_index_entries,
722 	    nitems(cn30xxpow_dump_ops_index_entries), "index", index);
723 }
724 
725 void
726 cn30xxpow_dump_ops_qos(int qos)
727 {
728 	cn30xxpow_dump_ops_common(cn30xxpow_dump_ops_qos_entries,
729 	    nitems(cn30xxpow_dump_ops_qos_entries), "qos", qos);
730 }
731 
732 void
733 cn30xxpow_dump_ops_grp(int grp)
734 {
735 	cn30xxpow_dump_ops_common(cn30xxpow_dump_ops_grp_entries,
736 	    nitems(cn30xxpow_dump_ops_grp_entries), "grp", grp);
737 }
738 
739 void
740 cn30xxpow_dump_ops_queue(int queue)
741 {
742 	cn30xxpow_dump_ops_common(cn30xxpow_dump_ops_queue_entries,
743 	    nitems(cn30xxpow_dump_ops_queue_entries), "queue", queue);
744 }
745 
746 void
747 cn30xxpow_dump_ops_common(const struct cn30xxpow_dump_ops_entry *entries,
748     size_t nentries, const char *by_what, int arg)
749 {
750 	const struct cn30xxpow_dump_ops_entry *entry;
751 	uint64_t tmp;
752 	int i;
753 
754 	printf("%s=%d\n", by_what, arg);
755 	for (i = 0; i < (int)nentries; i++) {
756 		entry = &entries[i];
757 		tmp = (*entry->func)(arg);
758 		printf("\t%-24s: %16llx\n", entry->name, tmp);
759 	}
760 }
761 
762 #endif
763 
764 /* -------------------------------------------------------------------------- */
765 
766 /* ---- test */
767 
768 #ifdef OCTEON_POW_TEST
769 /*
770  * Standalone test entries; meant to be called from ddb.
771  */
772 
773 void		cn30xxpow_test(void);
774 void		cn30xxpow_test_dump_wqe(paddr_t);
775 
776 void		cn30xxpow_test_1(void);
777 
778 struct test_wqe {
779 	uint64_t word0;
780 	uint64_t word1;
781 	uint64_t word2;
782 	uint64_t word3;
783 } __packed;
784 struct test_wqe test_wqe;
785 
786 void
787 cn30xxpow_test(void)
788 {
789 	cn30xxpow_test_1();
790 }
791 
792 void
793 cn30xxpow_test_1(void)
794 {
795 	struct test_wqe *wqe = &test_wqe;
796 	int qos, grp, queue, tt;
797 	uint32_t tag;
798 	paddr_t ptr;
799 
800 	qos = 7;			/* XXX */
801 	grp = queue = 15;		/* XXX */
802 	tt = POW_TAG_TYPE_ORDERED;	/* XXX */
803 	tag = UINT32_C(0x01234567);	/* XXX */
804 
805 	/* => make sure that the queue is empty */
806 
807 	cn30xxpow_dump_ops_qos(qos);
808 	cn30xxpow_dump_ops_grp(grp);
809 	printf("\n");
810 
811 	/*
812 	 * Initialize WQE.
813 	 *
814 	 * word0:next is used by hardware.
815 	 *
816 	 * word1:qos, word1:grp, word1:tt, word1:tag must match with arguments
817 	 * of the following ADDWQ transaction.
818 	 */
819 
820 	(void)memset(wqe, 0, sizeof(*wqe));
821 	wqe->word0 =
822 	    __BITS64_SET(POW_WQE_WORD0_NEXT, 0);
823 	wqe->word1 =
824 	    __BITS64_SET(POW_WQE_WORD1_QOS, qos) |
825 	    __BITS64_SET(POW_WQE_WORD1_GRP, grp) |
826 	    __BITS64_SET(POW_WQE_WORD1_TT, tt) |
827 	    __BITS64_SET(POW_WQE_WORD1_TAG, tag);
828 
829 	printf("calling ADDWQ\n");
830 	cn30xxpow_ops_addwq(MIPS_KSEG0_TO_PHYS(wqe), qos, grp, tt, tag);
831 
832 	cn30xxpow_dump_ops_qos(qos);
833 	cn30xxpow_dump_ops_grp(grp);
834 	printf("\n");
835 
836 	/* => make sure that a WQE is added to the queue */
837 
838 	printf("calling GET_WORK_LOAD\n");
839 	ptr = cn30xxpow_ops_get_work_load(0);
840 
841 	cn30xxpow_dump_ops_qos(qos);
842 	cn30xxpow_dump_ops_grp(grp);
843 	printf("\n");
844 
845 	cn30xxpow_test_dump_wqe(ptr);
846 
847 	/* => make sure that the WQE is in-flight (and scheduled) */
848 
849 	printf("calling SWTAG(NULL)\n");
850 	cn30xxpow_ops_swtag(POW_TAG_TYPE_NULL, tag);
851 
852 	cn30xxpow_dump_ops_qos(qos);
853 	cn30xxpow_dump_ops_grp(grp);
854 	printf("\n");
855 
856 	/* => make sure that the WQE is un-scheduled (completed) */
857 }
858 
859 void
860 cn30xxpow_test_dump_wqe(paddr_t ptr)
861 {
862 	uint64_t word0, word1;
863 
864 	printf("wqe\n");
865 
866 	word0 = *(uint64_t *)PHYS_TO_XKPHYS(ptr, CCA_CACHED);
867 	printf("\t%-24s: %16llx\n", "word0", word0);
868 
869 	word1 = *(uint64_t *)PHYS_TO_XKPHYS(ptr + 8, CCA_CACHED);
870 	printf("\t%-24s: %16llx\n", "word1", word1);
871 }
872 #endif
873