octeon_pow.c revision 1.9 1 /* $NetBSD: octeon_pow.c,v 1.9 2020/06/22 02:26:20 simonb Exp $ */
2
3 /*
4 * Copyright (c) 2007 Internet Initiative Japan, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: octeon_pow.c,v 1.9 2020/06/22 02:26:20 simonb Exp $");
31
32 #include "opt_octeon.h" /* CNMAC_DEBUG */
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/types.h>
37 #include <sys/kernel.h> /* hz */
38 #include <sys/malloc.h>
39 #include <sys/device.h> /* evcnt */
40 #include <sys/syslog.h> /* evcnt */
41
42 #include <sys/bus.h>
43
44 #include <mips/include/locore.h>
45 #include <mips/cavium/octeonvar.h>
46 #include <mips/cavium/include/iobusvar.h>
47 #include <mips/cavium/dev/octeon_ciureg.h> /* XXX */
48 #include <mips/cavium/dev/octeon_powreg.h>
49 #include <mips/cavium/dev/octeon_powvar.h>
50
51 /* XXX ensure assertion */
52 #if !defined(DIAGNOSTIC)
53 #define DIAGNOSTIC
54 #endif
55
56 extern int ipflow_fastforward_disable_flags;
57
58 struct octpow_intr_handle {
59 void *pi_ih;
60 struct octpow_softc *pi_sc;
61 int pi_group;
62 void (*pi_cb)(void *, uint64_t *);
63 void *pi_data;
64 };
65
66 void octpow_bootstrap(struct octeon_config *);
67
68 static void octpow_init(struct octpow_softc *);
69 static void octpow_init_regs(struct octpow_softc *);
70 static inline int octpow_tag_sw_poll(void) __unused;
71 static inline void octpow_tag_sw_wait(void);
72 static inline void octpow_config_int_pc(struct octpow_softc *, int);
73 static inline void octpow_config_int(struct octpow_softc *, int, uint64_t,
74 uint64_t, uint64_t);
75 static inline void octpow_intr_work(struct octpow_softc *,
76 struct octpow_intr_handle *, int);
77 static int octpow_intr(void *);
78
79 /* XXX */
80 struct octpow_softc octpow_softc;
81
82 /*
83 * XXX: parameter tuning is needed: see files.octeon
84 */
85 #ifndef CNMAC_RING_MAX
86 #define CNMAC_RING_MAX 512
87 #endif
88 #ifndef CNMAC_RING_MIN
89 #define CNMAC_RING_MIN 1
90 #endif
91
92 #ifdef CNMAC_INTR_FEEDBACK_RING
93 int max_recv_cnt = CNMAC_RING_MAX;
94 int min_recv_cnt = CNMAC_RING_MIN;
95 int recv_cnt = CNMAC_RING_MIN;
96 int int_rate = 1;
97 #endif
98
99 /* -------------------------------------------------------------------------- */
100
101 /* ---- utility functions */
102
103
104 /* ---- status by coreid */
105
106 static inline uint64_t
107 octpow_status_by_coreid_pend_tag(int coreid)
108 {
109 return octpow_ops_pow_status(coreid, 0, 0, 0);
110 }
111
112 static inline uint64_t
113 octpow_status_by_coreid_pend_wqp(int coreid)
114 {
115 return octpow_ops_pow_status(coreid, 0, 0, 1);
116 }
117
118 static inline uint64_t
119 octpow_status_by_coreid_cur_tag_next(int coreid)
120 {
121 return octpow_ops_pow_status(coreid, 0, 1, 0);
122 }
123
124 static inline uint64_t
125 octpow_status_by_coreid_cur_tag_prev(int coreid)
126 {
127 return octpow_ops_pow_status(coreid, 1, 1, 0);
128 }
129
130 static inline uint64_t
131 octpow_status_by_coreid_cur_wqp_next(int coreid)
132 {
133 return octpow_ops_pow_status(coreid, 0, 1, 1);
134 }
135
136 static inline uint64_t
137 octpow_status_by_coreid_cur_wqp_prev(int coreid)
138 {
139 return octpow_ops_pow_status(coreid, 1, 1, 1);
140 }
141
142 /* ---- status by index */
143
144 static inline uint64_t
145 octpow_status_by_index_tag(int index)
146 {
147 return octpow_ops_pow_memory(index, 0, 0);
148 }
149
150 static inline uint64_t
151 octpow_status_by_index_wqp(int index)
152 {
153 return octpow_ops_pow_memory(index, 0, 1);
154 }
155
156 static inline uint64_t
157 octpow_status_by_index_desched(int index)
158 {
159 return octpow_ops_pow_memory(index, 1, 0);
160 }
161
162 /* ---- status by qos level */
163
164 static inline uint64_t
165 octpow_status_by_qos_free_loc(int qos)
166 {
167 return octpow_ops_pow_idxptr(qos, 0, 0);
168 }
169
170 /* ---- status by desched group */
171
172 static inline uint64_t
173 octpow_status_by_grp_nosched_des(int grp)
174 {
175 return octpow_ops_pow_idxptr(grp, 0, 1);
176 }
177
178 /* ---- status by memory input queue */
179
180 static inline uint64_t
181 octpow_status_by_queue_remote_head(int queue)
182 {
183 return octpow_ops_pow_idxptr(queue, 1, 0);
184 }
185
186 static inline uint64_t
187 octpow_status_by_queue_remote_tail(int queue)
188 {
189 return octpow_ops_pow_idxptr(queue, 1, 0);
190 }
191
192 /* ---- tag switch */
193
194 /*
195 * "RDHWR rt, $30" returns:
196 * 0 => pending bit is set
197 * 1 => pending bit is clear
198 */
199
200 /* return 1 if pending bit is clear (ready) */
201 static inline int
202 octpow_tag_sw_poll(void)
203 {
204 uint64_t result;
205
206 /* XXX O32 */
207 __asm __volatile (
208 " .set push \n"
209 " .set noreorder \n"
210 " .set arch=octeon \n"
211 " rdhwr %[result], $30 \n"
212 " .set pop \n"
213 : [result]"=r"(result)
214 );
215 /* XXX O32 */
216 return (int)result;
217 }
218
219 /* -------------------------------------------------------------------------- */
220
221 /* ---- initialization and configuration */
222
223 void
224 octpow_bootstrap(struct octeon_config *mcp)
225 {
226 struct octpow_softc *sc = &octpow_softc;
227
228 sc->sc_regt = &mcp->mc_iobus_bust;
229 /* XXX */
230
231 octpow_init(sc);
232 }
233
234 static inline void
235 octpow_config_int(struct octpow_softc *sc, int group, uint64_t tc_thr,
236 uint64_t ds_thr, uint64_t iq_thr)
237 {
238 uint64_t wq_int_thr =
239 POW_WQ_INT_THRX_TC_EN |
240 __SHIFTIN(tc_thr, POW_WQ_INT_THRX_TC_THR) |
241 __SHIFTIN(ds_thr, POW_WQ_INT_THRX_DS_THR) |
242 __SHIFTIN(iq_thr, POW_WQ_INT_THRX_IQ_THR);
243
244 _POW_WR8(sc, POW_WQ_INT_THR0_OFFSET + (group * 8), wq_int_thr);
245 }
246
247 /*
248 * interrupt threshold configuration
249 *
250 * => DS / IQ
251 * => ...
252 * => time counter threshold
253 * => unit is 1msec
254 * => each group can set timeout
255 * => temporary disable bit
256 * => use CIU generic timer
257 */
258
259 void
260 octpow_config(struct octpow_softc *sc, int group)
261 {
262
263 octpow_config_int(sc, group,
264 0x0f, /* TC */
265 0x00, /* DS */
266 0x00); /* IQ */
267 }
268
269 void *
270 octpow_intr_establish(int group, int level, void (*cb)(void *, uint64_t *),
271 void (*fcb)(int*, int *, uint64_t, void *), void *data)
272 {
273 struct octpow_intr_handle *pow_ih;
274
275 KASSERT(group >= 0);
276 KASSERT(group < 16);
277
278 pow_ih = malloc(sizeof(*pow_ih), M_DEVBUF, M_WAITOK);
279 pow_ih->pi_ih = octeon_intr_establish(
280 CIU_INT_WORKQ_0 + group,
281 level,
282 octpow_intr, pow_ih);
283 KASSERT(pow_ih->pi_ih != NULL);
284
285 pow_ih->pi_sc = &octpow_softc; /* XXX */
286 pow_ih->pi_group = group;
287 pow_ih->pi_cb = cb;
288 pow_ih->pi_data = data;
289
290 return pow_ih;
291 }
292
293 void
294 octpow_init(struct octpow_softc *sc)
295 {
296 octpow_init_regs(sc);
297
298 sc->sc_int_pc_base = 10000;
299 octpow_config_int_pc(sc, sc->sc_int_pc_base);
300 }
301
302 void
303 octpow_init_regs(struct octpow_softc *sc)
304 {
305 int status;
306
307 status = bus_space_map(sc->sc_regt, POW_BASE, POW_SIZE, 0,
308 &sc->sc_regh);
309 if (status != 0)
310 panic("can't map %s space", "pow register");
311 }
312
313 /* -------------------------------------------------------------------------- */
314
315 /* ---- interrupt handling */
316
317 /*
318 * Interrupt handling by fixed count, following Cavium's SDK code.
319 *
320 * XXX the fixed count (MAX_RX_CNT) could be changed dynamically?
321 *
322 * XXX this does not utilize "tag switch" very well
323 */
324 /*
325 * usually all packet receive
326 */
327 #define MAX_RX_CNT 0x7fffffff
328
329 static inline void
330 octpow_intr_work(struct octpow_softc *sc, struct octpow_intr_handle *pow_ih,
331 int recv_limit)
332 {
333 uint64_t *work;
334 uint64_t count = 0;
335
336 _POW_WR8(sc, POW_PP_GRP_MSK0_OFFSET, __BIT(pow_ih->pi_group));
337
338 for (count = 0; count < recv_limit; count++) {
339 octpow_tag_sw_wait();
340 octpow_work_request_async(
341 OCTEON_CVMSEG_OFFSET(csm_pow_intr), POW_NO_WAIT);
342 work = (uint64_t *)octpow_work_response_async(
343 OCTEON_CVMSEG_OFFSET(csm_pow_intr));
344 if (work == NULL)
345 break;
346 (*pow_ih->pi_cb)(pow_ih->pi_data, work);
347 }
348 }
349
350 static int
351 octpow_intr(void *data)
352 {
353 struct octpow_intr_handle *pow_ih = data;
354 struct octpow_softc *sc = pow_ih->pi_sc;
355 uint64_t wq_int_mask = __BIT(pow_ih->pi_group);
356
357 #ifdef CNMAC_INTR_FEEDBACK_RING
358 octpow_intr_work(sc, pow_ih, recv_cnt);
359 #else
360 octpow_intr_work(sc, pow_ih, INT_MAX);
361 #endif /* CNMAC_INTR_FEEDBACK_RING */
362
363 _POW_WR8(sc, POW_WQ_INT_OFFSET,
364 __SHIFTIN(wq_int_mask, POW_WQ_INT_WQ_INT));
365 return 1;
366 }
367
368 #ifdef CNMAC_INTR_FEEDBACK_RING
369 int
370 octpow_ring_reduce(void *arg)
371 {
372 struct octpow_softc *sc = arg;
373 int new, newi;
374 int s;
375
376 #if 0
377 if (ipflow_fastforward_disable_flags == 0) {
378 newi = int_rate = 1;
379 octpow_config_int_pc_rate(sc, int_rate);
380 return recv_cnt;
381 }
382 #endif
383
384 new = recv_cnt / 2;
385 if (new < min_recv_cnt) {
386 newi = int_rate << 1;
387 if (newi > 128) {
388 newi = 128;
389 #ifdef POW_DEBUG
390 log(LOG_DEBUG,
391 "Min intr rate.\n");
392 #endif
393 new = min_recv_cnt;
394 }
395 else {
396 log(LOG_DEBUG,
397 "pow interrupt rate optimized %d->%d.\n",
398 int_rate, newi);
399 int_rate = newi;
400 octpow_config_int_pc_rate(sc, int_rate);
401 new = max_recv_cnt;
402 }
403 }
404
405 s = splhigh(); /* XXX */
406 recv_cnt = new;
407 splx(s);
408
409 return new;
410 }
411
412 int
413 octpow_ring_grow(void *arg)
414 {
415 struct octpow_softc *sc = arg;
416 int new, newi;
417 int s;
418
419 #if 0
420 if (ipflow_fastforward_disable_flags == 0) {
421 newi = int_rate = 1;
422 octpow_config_int_pc_rate(sc, int_rate);
423 return recv_cnt;
424 }
425 #endif
426
427 new = recv_cnt + 1;
428 if (new > max_recv_cnt) {
429 newi = int_rate >> 1;
430 if (newi <= 0) {
431 newi = 1;
432 #ifdef POW_DEBUG
433 log(LOG_DEBUG,
434 "Max intr rate.\n");
435 #endif
436 new = max_recv_cnt;
437 }
438 else {
439 log(LOG_DEBUG,
440 "pow interrupt rate optimized %d->%d.\n",
441 int_rate, newi);
442 int_rate = newi;
443 octpow_config_int_pc_rate(sc, int_rate);
444 new = min_recv_cnt;
445 }
446 }
447
448 s = splhigh(); /* XXX */
449 recv_cnt = new;
450 splx(s);
451
452 return new;
453 }
454
455 int
456 octpow_ring_size(void)
457 {
458 return recv_cnt;
459 }
460
461 int
462 octpow_ring_intr(void)
463 {
464 return int_rate;
465 }
466 #endif /* CNMAC_INTR_FEEDBACK_RING */
467