/*	$NetBSD: octeonvar.h,v 1.5 2016/07/11 16:15:35 matt Exp $	*/

/*-
 * Copyright (c) 2001 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jason R. Thorpe.
 *
 * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 _MIPS_OCTEON_OCTEONVAR_H_
#define _MIPS_OCTEON_OCTEONVAR_H_

#include <sys/bus.h>
#include <sys/evcnt.h>
#include <sys/kcpuset.h>
#include <mips/locore.h>
#include <dev/pci/pcivar.h>

/* XXX elsewhere */
#define	_ASM_PROLOGUE \
		"	.set push			\n" \
		"	.set noreorder			\n"
#define	_ASM_PROLOGUE_MIPS64 \
		_ASM_PROLOGUE				\
		"	.set mips64			\n"
#define	_ASM_PROLOGUE_OCTEON \
		_ASM_PROLOGUE				\
		"	.set arch=octeon		\n"
#define	_ASM_EPILOGUE \
		"	.set pop			\n"
/*
 * subbits = __BITS64_GET(XXX, bits);
 * bits = __BITS64_SET(XXX, subbits);
 */
#ifndef	__BITS64_GET
#define	__BITS64_GET(name, bits)	\
	    (((uint64_t)(bits) & name) >> name##_SHIFT)
#endif
#ifndef	__BITS64_SET
#define	__BITS64_SET(name, subbits)	\
	    (((uint64_t)(subbits) << name##_SHIFT) & name)
#endif

struct octeon_config {
	struct mips_bus_space mc_iobus_bust;
	struct mips_bus_space mc_bootbus_bust;
	struct mips_pci_chipset mc_pc;

	struct mips_bus_dma_tag mc_iobus_dmat;
	struct mips_bus_dma_tag mc_bootbus_dmat;
	struct mips_bus_dma_tag mc_core1_dmat;
	struct mips_bus_dma_tag mc_fpa_dmat;

	struct extent *mc_io_ex;
	struct extent *mc_mem_ex;

	int	mc_mallocsafe;
};

#define NIRQS	64

struct cpu_softc {
	struct cpu_info *cpu_ci;

	uint64_t cpu_int0_sum0;
	uint64_t cpu_int1_sum0;
	uint64_t cpu_int2_sum0;

	uint64_t cpu_int0_en0;
	uint64_t cpu_int1_en0;
	uint64_t cpu_int2_en0;

	uint64_t cpu_int0_en1;
	uint64_t cpu_int1_en1;
	uint64_t cpu_int2_en1;

	uint64_t cpu_int32_en;

	struct evcnt cpu_intr_evs[NIRQS];

	uint64_t cpu_int0_enable0;
	uint64_t cpu_int1_enable0;
	uint64_t cpu_int2_enable0;

	void *cpu_wdog_sih;		// wdog softint handler
	uint64_t cpu_wdog;
	uint64_t cpu_pp_poke;

#ifdef MULTIPROCESSOR
	uint64_t cpu_mbox_set;
	uint64_t cpu_mbox_clr;
#endif
};

/*
 * FPA map
 */

#define	OCTEON_POOL_NO_PKT	0
#define	OCTEON_POOL_NO_WQE	1
#define	OCTEON_POOL_NO_CMD	2
#define	OCTEON_POOL_NO_SG	3
#define	OCTEON_POOL_NO_XXX_4	4
#define	OCTEON_POOL_NO_XXX_5	5
#define	OCTEON_POOL_NO_XXX_6	6
#define	OCTEON_POOL_NO_DUMP	7	/* FPA debug dump */

#define	OCTEON_POOL_SIZE_PKT	2048	/* 128 x 16 */
#define	OCTEON_POOL_SIZE_WQE	128	/* 128 x 1 */
#define	OCTEON_POOL_SIZE_CMD	1024	/* 128 x 8 */
#define	OCTEON_POOL_SIZE_SG	512	/* 128 x 4 */
#define	OCTEON_POOL_SIZE_XXX_4	0
#define	OCTEON_POOL_SIZE_XXX_5	0
#define	OCTEON_POOL_SIZE_XXX_6	0
#define	OCTEON_POOL_SIZE_XXX_7	0

#define	OCTEON_POOL_NELEMS_PKT		4096
#define	OCTEON_POOL_NELEMS_WQE		4096
#define	OCTEON_POOL_NELEMS_CMD		32
#define	OCTEON_POOL_NELEMS_SG		1024
#define	OCTEON_POOL_NELEMS_XXX_4	0
#define	OCTEON_POOL_NELEMS_XXX_5	0
#define	OCTEON_POOL_NELEMS_XXX_6	0
#define	OCTEON_POOL_NELEMS_XXX_7	0

/*
 * CVMSEG (``scratch'') memory map
 */
struct octeon_cvmseg_map {
	/* 0-3 */
	uint64_t		csm_xxx_0;
	uint64_t		csm_xxx_1;
	uint64_t		csm_xxx_2;
	uint64_t		csm_pow_intr;

	/* 4-19 */
	struct octeon_cvmseg_ether_map {
		uint64_t	csm_ether_fau_req;
		uint64_t	csm_ether_fau_done;
		uint64_t	csm_ether_fau_cmdptr;
		uint64_t	csm_ether_xxx_3;
	} csm_ether[4/* XXX */];

	/* 20-32 */
	uint64_t	xxx_20_32[32 - 20];
} __packed;
#define	OCTEON_CVMSEG_OFFSET(entry) \
	offsetof(struct octeon_cvmseg_map, entry)
#define	OCTEON_CVMSEG_ETHER_OFFSET(n, entry) \
	(offsetof(struct octeon_cvmseg_map, csm_ether) + \
	 sizeof(struct octeon_cvmseg_ether_map) * (n) + \
	 offsetof(struct octeon_cvmseg_ether_map, entry))

/*
 * FAU register map
 *
 * => FAU registers exist in FAU unit
 * => devices (PKO) can access these registers
 * => CPU can read those values after loading them into CVMSEG
 */
struct octeon_fau_map {
	struct {
		/* PKO command index */
		uint64_t	_fau_map_port_pkocmdidx;
		/* send requested */
		uint64_t	_fau_map_port_txreq;
		/* send completed */
		uint64_t	_fau_map_port_txdone;
		/* XXX */
		uint64_t	_fau_map_port_pad;
	} __packed _fau_map_port[3];
};

/*
 * POW qos/group map
 */

#define	OCTEON_POW_QOS_PIP		0
#define	OCTEON_POW_QOS_CORE1		1
#define	OCTEON_POW_QOS_XXX_2		2
#define	OCTEON_POW_QOS_XXX_3		3
#define	OCTEON_POW_QOS_XXX_4		4
#define	OCTEON_POW_QOS_XXX_5		5
#define	OCTEON_POW_QOS_XXX_6		6
#define	OCTEON_POW_QOS_XXX_7		7

#define	OCTEON_POW_GROUP_PIP		0
#define	OCTEON_POW_GROUP_XXX_1		1
#define	OCTEON_POW_GROUP_XXX_2		2
#define	OCTEON_POW_GROUP_XXX_3		3
#define	OCTEON_POW_GROUP_XXX_4		4
#define	OCTEON_POW_GROUP_XXX_5		5
#define	OCTEON_POW_GROUP_XXX_6		6
#define	OCTEON_POW_GROUP_CORE1_SEND	7
#define	OCTEON_POW_GROUP_CORE1_TASK_0	8
#define	OCTEON_POW_GROUP_CORE1_TASK_1	9
#define	OCTEON_POW_GROUP_CORE1_TASK_2	10
#define	OCTEON_POW_GROUP_CORE1_TASK_3	11
#define	OCTEON_POW_GROUP_CORE1_TASK_4	12
#define	OCTEON_POW_GROUP_CORE1_TASK_5	13
#define	OCTEON_POW_GROUP_CORE1_TASK_6	14
#define	OCTEON_POW_GROUP_CORE1_TASK_7	15

#ifdef _KERNEL
extern struct octeon_config	octeon_configuration;
#ifdef MULTIPROCESSOR
extern kcpuset_t *cpus_booted;
extern struct cpu_softc		octeon_cpu1_softc;
#endif

void	octeon_bus_io_init(bus_space_tag_t, void *);
void	octeon_bus_mem_init(bus_space_tag_t, void *);
void	octeon_cal_timer(int);
void	octeon_dma_init(struct octeon_config *);
void	octeon_intr_init(struct cpu_info *);
void	octeon_iointr(int, vaddr_t, uint32_t);
void	octeon_pci_init(pci_chipset_tag_t, struct octeon_config *);
void	*octeon_intr_establish(int, int, int (*)(void *), void *);
void	octeon_intr_disestablish(void *cookie);

void	octeon_reset_vector(void);
uint64_t mips_cp0_cvmctl_read(void);
void	 mips_cp0_cvmctl_write(uint64_t);

#endif /* _KERNEL */

#if defined(__mips_n32)
#define ffs64	__builtin_ffsll
#elif defined(_LP64)
#define ffs64	__builtin_ffsl
#else
#error unknown ABI
#endif

/* 
 * Prefetch
 *
 *	OCTEON_PREF		normal (L1 and L2)
 *	OCTEON_PREF_L1		L1 only
 *	OCTEON_PREF_L2		L2 only
 *	OCTEON_PREF_DWB		don't write back
 *	OCTEON_PREF_PFS		prepare for store
 */
#define __OCTEON_PREF_N(n, base, offset)			\
	__asm __volatile (					\
		"	.set	push				\
		"	.set	arch=octeon			\n" \
		"	pref	"#n", "#offset"(%[base])	\n" \
		"	.set	pop				\
		: : [base] "d" (base)				\
	)
#define __OCTEON_PREF_0(base, offset)	__OCTEON_PREF_N(0, base, offset)
#define __OCTEON_PREF_4(base, offset)	__OCTEON_PREF_N(4, base, offset)
#define __OCTEON_PREF_28(base, offset)	__OCTEON_PREF_N(28, base, offset)
#define __OCTEON_PREF_29(base, offset)	__OCTEON_PREF_N(29, base, offset)
#define __OCTEON_PREF_30(base, offset)	__OCTEON_PREF_N(30, base, offset)
#define OCTEON_PREF(base, offset)	__OCTEON_PREF_0(base, offset)
#define OCTEON_PREF_L1(base, offset)	__OCTEON_PREF_4(base, offset)
#define OCTEON_PREF_L2(base, offset)	__OCTEON_PREF_28(base, offset)
#define OCTEON_PREF_DWB(base, offset)	__OCTEON_PREF_29(base, offset)
#define OCTEON_PREF_PFS(base, offset)	__OCTEON_PREF_30(base, offset)

/*
 * Sync
 */
#define OCTEON_SYNCCOMMON(name) \
	__asm __volatile ( \
		_ASM_PROLOGUE_OCTEON			\
		"	"#name"				\n" \
		_ASM_EPILOGUE				\
		::: "memory")
#define OCTEON_SYNCIOBDMA	OCTEON_SYNCCOMMON(synciobdma)
#define OCTEON_SYNCW		OCTEON_SYNCCOMMON(syncw)
#define OCTEON_SYNC		OCTEON_SYNCCOMMON(sync)
#define OCTEON_SYNCWS		OCTEON_SYNCCOMMON(syncws)
#define OCTEON_SYNCS		OCTEON_SYNCCOMMON(syncs)
/* XXX backward compatibility */
#if 1
#define	OCT_SYNCIOBDMA		OCTEON_SYNCIOBDMA
#define	OCT_SYNCW		OCTEON_SYNCW
#define	OCT_SYNC		OCTEON_SYNC
#define	OCT_SYNCWS		OCTEON_SYNCWS
#define	OCT_SYNCS		OCTEON_SYNCS
#endif

/* octeon core does not use cca to determine cacheability */
#define OCTEON_CCA_NONE UINT64_C(0)

static inline uint64_t
octeon_xkphys_read_8(paddr_t address)
{
	return mips3_ld(MIPS_PHYS_TO_XKPHYS(OCTEON_CCA_NONE, address));
}

static inline void
octeon_xkphys_write_8(paddr_t address, uint64_t value)
{
	mips3_sd(MIPS_PHYS_TO_XKPHYS(OCTEON_CCA_NONE, address), value);
}

/* XXX backward compatibility */
#if 1
#define octeon_read_csr(address) \
	octeon_xkphys_read_8(address)
#define octeon_write_csr(address, value) \
	octeon_xkphys_write_8(address, value)
#endif

static inline void
octeon_iobdma_write_8(uint64_t value)
{
	uint64_t addr = UINT64_C(0xffffffffffffa200);

	octeon_xkphys_write_8(addr, value);
}

static inline uint64_t
octeon_cvmseg_read_8(size_t offset)
{
	return octeon_xkphys_read_8(UINT64_C(0xffffffffffff8000) + offset);
}

static inline void
octeon_cvmseg_write_8(size_t offset, uint64_t value)
{
	octeon_xkphys_write_8(UINT64_C(0xffffffffffff8000) + offset, value);
}

/* XXX */
static inline uint32_t
octeon_disable_interrupt(uint32_t *new)
{
	uint32_t s, tmp;
        
	__asm __volatile (
		_ASM_PROLOGUE
		"	mfc0	%[s], $12		\n"
		"	and	%[tmp], %[s], ~1	\n"
		"	mtc0	%[tmp], $12		\n"
		_ASM_EPILOGUE
		: [s]"=&r"(s), [tmp]"=&r"(tmp));
	if (new)
		*new = tmp;
	return s;
}

/* XXX */
static inline void
octeon_restore_status(uint32_t s)
{
	__asm __volatile (
		_ASM_PROLOGUE
		"	mtc0	%[s], $12		\n"
		_ASM_EPILOGUE
		:: [s]"r"(s));
}

static inline uint64_t
octeon_get_cycles(void)
{ 
#if defined(__mips_o32)
	uint32_t s, lo, hi;
  
	s = octeon_disable_interrupt((void *)0);
	__asm __volatile (
		_ASM_PROLOGUE_MIPS64
		"	dmfc0	%[lo], $9, 6		\n"
		"	add	%[hi], %[lo], $0	\n"
		"	srl	%[hi], 32		\n"
		"	sll	%[lo], 32		\n"
		"	srl	%[lo], 32		\n"
		_ASM_EPILOGUE
		: [lo]"=&r"(lo), [hi]"=&r"(hi));
	octeon_restore_status(s);
	return ((uint64_t)hi << 32) + (uint64_t)lo;
#else
	uint64_t tmp;

	__asm __volatile (
		_ASM_PROLOGUE_MIPS64
		"	dmfc0	%[tmp], $9, 6		\n"
		_ASM_EPILOGUE
		: [tmp]"=&r"(tmp));
	return tmp;
#endif
}

/* -------------------------------------------------------------------------- */

/* ---- event counter */

#if defined(OCTEON_ETH_DEBUG)
#define	OCTEON_EVCNT_INC(sc, name) \
	do { (sc)->sc_ev_##name.ev_count++; } while (0)
#define	OCTEON_EVCNT_ADD(sc, name, n) \
	do { (sc)->sc_ev_##name.ev_count += (n); } while (0)
#define	OCTEON_EVCNT_ATTACH_EVCNTS(sc, entries, devname) \
do {								\
	int i;							\
	const struct octeon_evcnt_entry *ee;			\
								\
	for (i = 0; i < (int)__arraycount(entries); i++) {	\
		ee = &(entries)[i];				\
		evcnt_attach_dynamic(				\
		    (struct evcnt *)((uintptr_t)(sc) + ee->ee_offset), \
		    ee->ee_type, ee->ee_parent, devname,	\
		    ee->ee_name);				\
	}							\
} while (0)
#else
#define	OCTEON_EVCNT_INC(sc, name)
#define	OCTEON_EVCNT_ADD(sc, name, n)
#define	OCTEON_EVCNT_ATTACH_EVCNTS(sc, entries, devname)
#endif

struct octeon_evcnt_entry {
	size_t		ee_offset;
	int		ee_type;
	struct evcnt	*ee_parent;
	const char	*ee_name;
};

#define	OCTEON_EVCNT_ENTRY(_sc_type, _var, _ev_type, _parent, _name) \
	{							\
		.ee_offset = offsetof(_sc_type, sc_ev_##_var),	\
		.ee_type = EVCNT_TYPE_##_ev_type,		\
		.ee_parent = _parent,				\
		.ee_name = _name				\
	}

#endif	/* _MIPS_OCTEON_OCTEONVAR_H_ */
