/* $NetBSD: octeon_powvar.h,v 1.7 2020/06/23 05:15:33 simonb Exp $ */
/*
* Copyright (c) 2007 Internet Initiative Japan, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _OCTEON_POWVAR_H_
#define _OCTEON_POWVAR_H_
#include <sys/cpu.h>
#include <mips/cavium/octeonreg.h>
#define POW_TAG_TYPE_ORDERED 0
#define POW_TAG_TYPE_ATOMIC 1
#define POW_TAG_TYPE_NULL 2
#define POW_TAG_TYPE_NULL_NULL 3
#define POW_TAG_OP_SWTAG 0
#define POW_TAG_OP_SWTAG_FULL 1
#define POW_TAG_OP_SWTAG_DESCHED 2
#define POW_TAG_OP_DESCHED 3
#define POW_TAG_OP_ADDWQ 4
#define POW_TAG_OP_UPD_WQP_GRP 5
#define POW_TAG_OP_CLR_NSCHED 7
#define POW_TAG_OP_NOP 15
#define POW_WAIT 1
#define POW_NO_WAIT 0
#define POW_WORKQ_IRQ(group) (group)
/* XXX */
struct octpow_softc {
device_t sc_dev;
bus_space_tag_t sc_regt;
bus_space_handle_t sc_regh;
int sc_port;
int sc_int_pc_base;
};
/* XXX */
struct octpow_attach_args {
int aa_port;
bus_space_tag_t aa_regt;
};
void octpow_config(struct octpow_softc *, int);
void octpow_error_int_enable(void *, int);
uint64_t octpow_error_int_summary(void *);
int octpow_ring_reduce(void *);
int octpow_ring_grow(void *);
int octpow_ring_size(void);
int octpow_ring_intr(void);
#define _POW_RD8(sc, off) \
bus_space_read_8((sc)->sc_regt, (sc)->sc_regh, (off))
#define _POW_WR8(sc, off, v) \
bus_space_write_8((sc)->sc_regt, (sc)->sc_regh, (off), (v))
#define _POW_GROUP_RD8(sc, pi, off) \
bus_space_read_8((sc)->sc_regt, (sc)->sc_regh, \
(off) + sizeof(uint64_t) * (pi)->pi_group)
#define _POW_GROUP_WR8(sc, pi, off, v) \
bus_space_write_8((sc)->sc_regt, (sc)->sc_regh, \
(off) + sizeof(uint64_t) * (pi)->pi_group, (v))
extern struct octpow_softc octpow_softc;
/* -------------------------------------------------------------------------- */
/* Load Operations */
/* GET_WORK Loads */
static __inline uint64_t
octpow_ops_get_work_load(
int wait) /* 0-1 */
{
uint64_t ptr =
OCTEON_ADDR_IO_DID(POW_MAJOR_DID, POW_OP_SUBDID_GET_WORK) |
(wait ? POW_GET_WORK_LOAD_WAIT : 0);
return octeon_xkphys_read_8(ptr);
}
/* IOBDMA Operations */
/* ``subdid'' values are inverted between ``get_work_addr'' and ``null_read_id'' */
/* The ``scraddr'' part is index in 8 byte words, not address. */
/* GET_WORK IOBDMAs */
static __inline void
octpow_ops_get_work_iobdma(
int scraddr, /* 0-2047 */
int wait) /* 0-1 */
{
/* ``scraddr'' part is index in 64-bit words, not address */
const int scrindex = scraddr / sizeof(uint64_t);
uint64_t value = IOBDMA_CREATE(POW_MAJOR_DID,
POW_IOBDMA_SUBDID_GET_WORK, scrindex, POW_IOBDMA_LEN,
wait ? POW_IOBDMA_GET_WORK_WAIT : 0);
octeon_iobdma_write_8(value);
}
/* NULL_RD IOBDMAs */
static __inline void
octpow_ops_null_rd_iobdma(
int scraddr) /* 0-2047 */
{
/* ``scraddr'' part is index in 64-bit words, not address */
const int scrindex = scraddr / sizeof(uint64_t);
uint64_t value = IOBDMA_CREATE(POW_MAJOR_DID,
POW_IOBDMA_SUBDID_NULL_RD, scrindex, POW_IOBDMA_LEN, 0);
octeon_iobdma_write_8(value);
}
/* Store Operations */
static __inline void
octpow_store(
int subdid, /* 0, 1, 3 */
uint64_t addr, /* 0-0x0000.000f.ffff.ffff */
int no_sched, /* 0, 1 */
int index, /* 0-8191 */
int op, /* 0-15 */
int qos, /* 0-7 */
int grp, /* 0-7 */
int type, /* 0-7 */
uint32_t tag) /* 0-0xffff.ffff */
{
/* Physical Address to Store to POW */
uint64_t ptr = OCTEON_ADDR_IO_DID(POW_MAJOR_DID, subdid) |
__SHIFTIN(addr, POW_PHY_ADDR_STORE_ADDR);
/* Store Data on Store to POW */
uint64_t args =
__SHIFTIN(no_sched, POW_STORE_DATA_NO_SCHED) |
__SHIFTIN(index, POW_STORE_DATA_INDEX) |
__SHIFTIN(op, POW_STORE_DATA_OP) |
__SHIFTIN(qos, POW_STORE_DATA_QOS) |
__SHIFTIN(grp, POW_STORE_DATA_GRP) |
__SHIFTIN(type, POW_STORE_DATA_TYPE) |
__SHIFTIN(tag, POW_STORE_DATA_TAG);
octeon_xkphys_write_8(ptr, args);
}
/* SWTAG */
static __inline void
octpow_ops_swtag(int type, uint32_t tag)
{
octpow_store(
POW_STORE_SUBDID_OTHER,
0, /* addr (not used for SWTAG) */
0, /* no_sched (not used for SWTAG) */
0, /* index (not used for SWTAG) */
POW_TAG_OP_SWTAG, /* op == SWTAG */
0, /* qos (not used for SWTAG) */
0, /* grp (not used for SWTAG) */
type,
tag);
/* switch to NULL completes immediately */
}
/* SWTAG_FULL */
static __inline void
octpow_ops_swtag_full(paddr_t addr, int grp, int type, uint32_t tag)
{
octpow_store(
POW_STORE_SUBDID_SWTAG_FULL,
addr,
0, /* no_sched (not used for SWTAG_FULL) */
0, /* index (not used for SWTAG_FULL) */
POW_TAG_OP_SWTAG_FULL, /* op == SWTAG_FULL */
0, /* qos (not used for SWTAG_FULL) */
grp,
type,
tag);
}
/* SWTAG_DESCHED */
static __inline void
octpow_ops_swtag_desched(int no_sched, int grp, int type, uint32_t tag)
{
octpow_store(
POW_STORE_SUBDID_DESCHED,
0, /* addr (not used for SWTAG_DESCHED) */
no_sched,
0, /* index (not used for SWTAG_DESCHED) */
POW_TAG_OP_SWTAG_DESCHED, /* op == SWTAG_DESCHED */
0, /* qos (not used for SWTAG_DESCHED) */
grp,
type,
tag);
}
/* DESCHED */
static __inline void
octpow_ops_desched(int no_sched)
{
octpow_store(
POW_STORE_SUBDID_DESCHED,
0, /* addr (not used for DESCHED) */
no_sched,
0, /* index (not used for DESCHED) */
POW_TAG_OP_DESCHED, /* op == DESCHED */
0, /* qos (not used for DESCHED) */
0, /* grp (not used for DESCHED) */
0, /* type (not used for DESCHED) */
0); /* tag (not used for DESCHED) */
}
/* ADDWQ */
static __inline void
octpow_ops_addwq(paddr_t addr, int qos, int grp, int type, uint32_t tag)
{
octpow_store(
POW_STORE_SUBDID_OTHER,
addr,
0, /* no_sched (not used for ADDWQ) */
0, /* index (not used for ADDWQ) */
POW_TAG_OP_ADDWQ, /* op == ADDWQ */
qos,
grp,
type,
tag);
}
/* UPD_WQP_GRP */
static __inline void
octpow_ops_upd_wqp_grp(paddr_t addr, int grp)
{
octpow_store(
POW_STORE_SUBDID_OTHER,
addr,
0, /* no_sched (not used for UPD_WQP_GRP) */
0, /* index (not used for UPD_WQP_GRP) */
POW_TAG_OP_UPD_WQP_GRP, /* op == UPD_WQP_GRP */
0, /* qos (not used for UPD_WQP_GRP) */
grp,
0, /* type (not used for UPD_WQP_GRP) */
0); /* tag (not used for UPD_WQP_GRP) */
}
/* CLR_NSCHED */
static __inline void
octpow_ops_clr_nsched(paddr_t addr, int index)
{
octpow_store(
POW_STORE_SUBDID_OTHER,
addr,
0, /* no_sched (not used for CLR_NSCHED) */
index,
POW_TAG_OP_CLR_NSCHED, /* op == CLR_NSCHED */
0, /* qos (not used for CLR_NSCHED) */
0, /* grp (not used for CLR_NSCHED) */
0, /* type (not used for CLR_NSCHED) */
0); /* tag (not used for CLR_NSCHED) */
}
/* NOP */
static __inline void
octpow_ops_nop(void)
{
octpow_store(
POW_STORE_SUBDID_OTHER,
0, /* addr (not used for NOP) */
0, /* no_sched (not used for NOP) */
0, /* index (not used for NOP) */
POW_TAG_OP_NOP, /* op == NOP */
0, /* qos (not used for NOP) */
0, /* grp (not used for NOP) */
0, /* type (not used for NOP) */
0); /* tag (not used for NOP) */
}
/*
* Check if there is a pending POW tag switch.
*/
static __inline int
octpow_tag_sw_pending(void)
{
int result;
/*
* "RDHWR rt, $30" returns:
* 0 => pending bit is set
* 1 => pending bit is clear
*/
__asm volatile (
" .set push\n"
" .set noreorder\n"
" .set arch=mips64r2\n"
" rdhwr %0, $30\n"
" .set pop\n"
: "=r" (result));
return result == 0;
}
/*
* Wait until there is no pending POW tag switch.
*/
static inline void
octpow_tag_sw_wait(void)
{
while (octpow_tag_sw_pending())
continue;
}
/* -------------------------------------------------------------------------- */
/*
* global functions
*/
static __inline void
octpow_work_request_async(uint64_t scraddr, uint64_t wait)
{
octpow_ops_get_work_iobdma(scraddr, wait);
}
static __inline uint64_t *
octpow_work_response_async(uint64_t scraddr)
{
uint64_t result;
OCTEON_SYNCIOBDMA;
result = octeon_cvmseg_read_8(scraddr);
paddr_t addr = result & POW_IOBDMA_GET_WORK_RESULT_ADDR;
if (result & POW_IOBDMA_GET_WORK_RESULT_NO_WORK)
return NULL;
#ifdef __mips_n32
KASSERT(addr < MIPS_PHYS_MASK);
return (uint64_t *)MIPS_PHYS_TO_KSEG0(addr);
#else
return (uint64_t *)MIPS_PHYS_TO_XKPHYS_CACHED(addr);
#endif
}
static __inline void
octpow_config_int_pc(struct octpow_softc *sc, int unit)
{
uint64_t wq_int_pc;
uint64_t pc_thr;
static uint64_t cpu_clock_hz;
if (cpu_clock_hz == 0)
cpu_clock_hz = curcpu()->ci_cpu_freq;
/* from SDK */
pc_thr = (cpu_clock_hz) / (unit * 16 * 256);
wq_int_pc = __SHIFTIN(pc_thr, POW_WQ_INT_PC_PC_THR);
_POW_WR8(sc, POW_WQ_INT_PC_OFFSET, wq_int_pc);
}
static __inline void
octpow_config_int_pc_rate(struct octpow_softc *sc, int rate)
{
octpow_config_int_pc(sc, sc->sc_int_pc_base / rate);
}
#endif /* !_OCTEON_POWVAR_H_ */