Home | History | Annotate | Line # | Download | only in dev
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