code:
/*	$OpenBSD: cache_loongson2.c,v 1.6 2014/03/31 20:21:19 miod Exp $	*/

/*
 * Copyright (c) 2009, 2012 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * Cache handling code for Loongson 2E and 2F processors.
 * This code could be made to work on 2C by not hardcoding the number of
 * cache ways.
 *
 * 2E and 2F caches are :
 * - L1 I$ is 4-way, VIPT, 32 bytes/line, 64KB total
 * - L1 D$ is 4-way, VIPT, write-back, 32 bytes/line, 64KB total
 * - L2 is 4-way, PIPT, write-back, 32 bytes/line, 512KB total
 */

#include <sys/param.h>
#include <sys/systm.h>

#include <mips64/cache.h>
#include <machine/cpu.h>

#include <uvm/uvm_extern.h>

/* L1 cache operations */
#define	IndexInvalidate_I	0x00
#define	IndexWBInvalidate_D	0x01
#define	IndexLoadTag_D		0x05
#define	IndexStoreTag_D		0x09
#define	HitInvalidate_D		0x11
#define	HitWBInvalidate_D	0x15
#define	IndexLoadData_D		0x19
#define	IndexStoreData_D	0x1d

/* L2 cache operations */
#define	IndexWBInvalidate_S	0x03
#define	IndexLoadTag_S		0x07
#define	IndexStoreTag_S		0x0b
#define	HitInvalidate_S		0x13
#define	HitWBInvalidate_S	0x17
#define	IndexLoadData_S		0x1b
#define	IndexStoreData_S	0x1f

#define	cache(op,set,addr) \
    __asm__ volatile \
      ("cache %0, %1(%2)" :: "i"(op), "i"(set), "r"(addr) : "memory")

static __inline__ void	ls2f_hitinv_primary(vaddr_t, vsize_t);
static __inline__ void	ls2f_hitinv_secondary(vaddr_t, vsize_t);
static __inline__ void	ls2f_hitwbinv_primary(vaddr_t, vsize_t);
static __inline__ void	ls2f_hitwbinv_secondary(vaddr_t, vsize_t);

#define	LS2F_CACHE_LINE	32UL
#define	LS2F_CACHE_WAYS	4UL
#define	LS2F_L1_SIZE		(64UL * 1024UL)
#define	LS2F_L2_SIZE		(512UL * 1024UL)

void
Loongson2_ConfigCache(struct cpu_info *ci)
{
	ci->ci_l1inst.size = LS2F_L1_SIZE;
	ci->ci_l1inst.linesize = LS2F_CACHE_LINE;
	ci->ci_l1inst.setsize = LS2F_L1_SIZE / LS2F_CACHE_WAYS;
	ci->ci_l1inst.sets = LS2F_CACHE_WAYS;

	ci->ci_l1data.size = LS2F_L1_SIZE;
	ci->ci_l1data.linesize = LS2F_CACHE_LINE;
	ci->ci_l1data.setsize = LS2F_L1_SIZE / LS2F_CACHE_WAYS;
	ci->ci_l1data.sets = LS2F_CACHE_WAYS;

	ci->ci_l2.size = LS2F_L2_SIZE;
	ci->ci_l2.linesize = LS2F_CACHE_LINE;
	ci->ci_l2.setsize = LS2F_L2_SIZE / LS2F_CACHE_WAYS;
	ci->ci_l2.sets = LS2F_CACHE_WAYS;

	memset(&ci->ci_l3, 0, sizeof(struct cache_info));

	cache_valias_mask = ci->ci_l1inst.setsize & ~PAGE_MASK;

	/* should not happen as we use 16KB pages */
	if (cache_valias_mask != 0) {
		cache_valias_mask |= PAGE_MASK;
		pmap_prefer_mask |= cache_valias_mask;
	}

	ci->ci_SyncCache = Loongson2_SyncCache;
	ci->ci_InvalidateICache = Loongson2_InvalidateICache;
	ci->ci_InvalidateICachePage = Loongson2_InvalidateICachePage;
	ci->ci_SyncICache = Loongson2_SyncICache;
	ci->ci_SyncDCachePage = Loongson2_SyncDCachePage;
	ci->ci_HitSyncDCache = Loongson2_HitSyncDCache;
	ci->ci_HitInvalidateDCache = Loongson2_HitInvalidateDCache;
	ci->ci_IOSyncDCache = Loongson2_IOSyncDCache;
}

/*
 * Writeback and invalidate all caches.
 */
void
Loongson2_SyncCache(struct cpu_info *ci)
{
	vaddr_t sva, eva;

	mips_sync();

	sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
	eva = sva + LS2F_L1_SIZE / LS2F_CACHE_WAYS;
	while (sva != eva) {
		cache(IndexInvalidate_I, 0, sva);
		sva += LS2F_CACHE_LINE;
	}

	sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
	eva = sva + LS2F_L1_SIZE / LS2F_CACHE_WAYS;
	while (sva != eva) {
		cache(IndexWBInvalidate_D, 0, sva);
		cache(IndexWBInvalidate_D, 1, sva);
		cache(IndexWBInvalidate_D, 2, sva);
		cache(IndexWBInvalidate_D, 3, sva);
		sva += LS2F_CACHE_LINE;
	}

	sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
	eva = sva + LS2F_L2_SIZE / LS2F_CACHE_WAYS;
	while (sva != eva) {
		cache(IndexWBInvalidate_S, 0, sva);
		cache(IndexWBInvalidate_S, 1, sva);
		cache(IndexWBInvalidate_S, 2, sva);
		cache(IndexWBInvalidate_S, 3, sva);
		sva += LS2F_CACHE_LINE;
	}
}

/*
 * Invalidate I$ for the given range.
 */
void
Loongson2_InvalidateICache(struct cpu_info *ci, vaddr_t _va, size_t _sz)
{
	vaddr_t va, sva, eva;
	vsize_t sz;

	/* extend the range to integral cache lines */
	va = _va & ~(LS2F_CACHE_LINE - 1);
	sz = ((_va + _sz + LS2F_CACHE_LINE - 1) & ~(LS2F_CACHE_LINE - 1)) - va;

	sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
	/* keep only the index bits */
	sva |= va & ((1UL << 14) - 1);
	eva = sva + sz;
	while (sva != eva) {
		cache(IndexInvalidate_I, 0, sva);
		sva += LS2F_CACHE_LINE;
	}
}

/*
 * Register a given page for I$ invalidation.
 */
void
Loongson2_InvalidateICachePage(struct cpu_info *ci, vaddr_t va)
{
	/*
	 * Since the page size matches the I$ set size, and I$ maintainance
	 * operations always operate on all the sets, all we need to do here
	 * is remember there are postponed flushes.
	 */
	ci->ci_cachepending_l1i = 1;
}

/*
 * Perform postponed I$ invalidation.
 */
void
Loongson2_SyncICache(struct cpu_info *ci)
{
	vaddr_t sva, eva;

	if (ci->ci_cachepending_l1i != 0) {
		/* inline Loongson2_InvalidateICache(ci, 0, PAGE_SIZE); */
		sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
		eva = sva + PAGE_SIZE;
		while (sva != eva) {
			cache(IndexInvalidate_I, 0, sva);
			sva += LS2F_CACHE_LINE;
		}

		ci->ci_cachepending_l1i = 0;
	}
}

/*
 * Writeback D$ for the given page.
 *
 * The index for L1 is the low 14 bits of the virtual address. Since the
 * page size is 2**14 bytes, it is possible to access the page through
 * any valid address.
 */
void
Loongson2_SyncDCachePage(struct cpu_info *ci, vaddr_t va, paddr_t pa)
{
	vaddr_t sva, eva;

	mips_sync();

	sva = PHYS_TO_XKPHYS(pa, CCA_CACHED);
	eva = sva + PAGE_SIZE;
	for (va = sva; va != eva; va += LS2F_CACHE_LINE)
		cache(HitWBInvalidate_D, 0, va);
	for (va = sva; va != eva; va += LS2F_CACHE_LINE)
		cache(HitWBInvalidate_S, 0, va);
}

/*
 * Writeback D$ for the given range. Range is expected to be currently
 * mapped, allowing the use of `Hit' operations. This is less aggressive
 * than using `Index' operations.
 */

static __inline__ void
ls2f_hitwbinv_primary(vaddr_t va, vsize_t sz)
{
	vaddr_t eva;

	eva = va + sz;
	while (va != eva) {
		cache(HitWBInvalidate_D, 0, va);
		va += LS2F_CACHE_LINE;
	}
}

static __inline__ void
ls2f_hitwbinv_secondary(vaddr_t va, vsize_t sz)
{
	vaddr_t eva;

	eva = va + sz;
	while (va != eva) {
		cache(HitWBInvalidate_S, 0, va);
		va += LS2F_CACHE_LINE;
	}
}

void
Loongson2_HitSyncDCache(struct cpu_info *ci, vaddr_t _va, size_t _sz)
{
	vaddr_t va;
	vsize_t sz;

	mips_sync();

	/* extend the range to integral cache lines */
	va = _va & ~(LS2F_CACHE_LINE - 1);
	sz = ((_va + _sz + LS2F_CACHE_LINE - 1) & ~(LS2F_CACHE_LINE - 1)) - va;

	ls2f_hitwbinv_primary(va, sz);
	ls2f_hitwbinv_secondary(va, sz);
}

/*
 * Invalidate D$ for the given range. Range is expected to be currently
 * mapped, allowing the use of `Hit' operations. This is less aggressive
 * than using `Index' operations.
 */

static __inline__ void
ls2f_hitinv_primary(vaddr_t va, vsize_t sz)
{
	vaddr_t eva;

	eva = va + sz;
	while (va != eva) {
		cache(HitInvalidate_D, 0, va);
		va += LS2F_CACHE_LINE;
	}
}

static __inline__ void
ls2f_hitinv_secondary(vaddr_t va, vsize_t sz)
{
	vaddr_t eva;

	eva = va + sz;
	while (va != eva) {
		cache(HitInvalidate_S, 0, va);
		va += LS2F_CACHE_LINE;
	}
}

void
Loongson2_HitInvalidateDCache(struct cpu_info *ci, vaddr_t _va, size_t _sz)
{
	vaddr_t va;
	vsize_t sz;

	/* extend the range to integral cache lines */
	va = _va & ~(LS2F_CACHE_LINE - 1);
	sz = ((_va + _sz + LS2F_CACHE_LINE - 1) & ~(LS2F_CACHE_LINE - 1)) - va;

	ls2f_hitinv_primary(va, sz);
	ls2f_hitinv_secondary(va, sz);

	mips_sync();
}

/*
 * Backend for bus_dmamap_sync(). Enforce coherency of the given range
 * by performing the necessary cache writeback and/or invalidate
 * operations.
 */
void
Loongson2_IOSyncDCache(struct cpu_info *ci, vaddr_t _va, size_t _sz, int how)
{
	vaddr_t va;
	vsize_t sz;
	int partial_start, partial_end;

	/* extend the range to integral cache lines */
	va = _va & ~(LS2F_CACHE_LINE - 1);
	sz = ((_va + _sz + LS2F_CACHE_LINE - 1) & ~(LS2F_CACHE_LINE - 1)) - va;

	switch (how) {
	case CACHE_SYNC_R:
		/* writeback partial cachelines */
		if (((_va | _sz) & (LS2F_CACHE_LINE - 1)) != 0) {
			partial_start = va != _va;
			partial_end = va + sz != _va + _sz;
		} else {
			partial_start = partial_end = 0;
		}
		if (partial_start) {
			cache(HitWBInvalidate_D, 0, va);
			cache(HitWBInvalidate_S, 0, va);
			va += LS2F_CACHE_LINE;
			sz -= LS2F_CACHE_LINE;
		}
		if (sz != 0 && partial_end) {
			cache(HitWBInvalidate_D, 0, va + sz - LS2F_CACHE_LINE);
			cache(HitWBInvalidate_S, 0, va + sz - LS2F_CACHE_LINE);
			sz -= LS2F_CACHE_LINE;
		}
		ls2f_hitinv_primary(va, sz);
		ls2f_hitinv_secondary(va, sz);
		break;
	case CACHE_SYNC_X:
	case CACHE_SYNC_W:
		ls2f_hitwbinv_primary(va, sz);
		ls2f_hitwbinv_secondary(va, sz);
		break;
	}
}


code:
/*	$OpenBSD: cache_octeon.c,v 1.9 2014/08/12 04:30:21 miod Exp $	*/
/*
 * Copyright (c) 2010 Takuya ASADA.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/*
 * Copyright (c) 1998-2004 Opsycon AB (www.opsycon.se)
 *
 * 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 AUTHOR ``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 AUTHOR 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.
 *
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>

#include <uvm/uvm_extern.h>

#include <mips64/cache.h>
#include <machine/cpu.h>

#define SYNCI() \
	asm volatile( \
		".set push\n" \
		".set mips64r2\n" \
		".word 0x041f0000\n" \
		"nop\n" \
		".set pop")

void
Octeon_ConfigCache(struct cpu_info *ci)
{
	uint32_t cfg;
	uint32_t s, l, a;

	/* minimalist default values */
	ci->ci_l1inst.size = 16 * 1024;
	ci->ci_l1inst.linesize = 128;
	ci->ci_l1inst.sets = 2;
	ci->ci_l1inst.setsize = ci->ci_l1inst.size / ci->ci_l1inst.sets;

	ci->ci_l1data.size = 8 * 1024;
	ci->ci_l1data.linesize = 128;
	ci->ci_l1data.sets = 64;
	ci->ci_l1data.setsize = ci->ci_l1data.size / ci->ci_l1data.sets;

	ci->ci_l2.size = 128 * 1024;
	ci->ci_l2.linesize = 128;
	ci->ci_l2.sets = 4;
	ci->ci_l2.setsize = ci->ci_l2.size / ci->ci_l2.sets;

	cfg = cp0_get_config();
	if ((cfg & 0x80000000) != 0) {
		cfg = cp0_get_config_1();

		/*
		 * Octeon L1 cache information does not follow the mips64
		 * standard encoding.
		 */

	        a = (cfg >> 16) & 0x07;
		l = (cfg >> 19) & 0x07;
		s = (cfg >> 22) & 0x07;
		ci->ci_l1inst.linesize = 2 << l;
		ci->ci_l1inst.setsize = (64 << s) * ci->ci_l1inst.linesize;
		if (a >= 1)
	        	ci->ci_l1inst.sets = 1 << (a - 1);
		else
			ci->ci_l1inst.sets = 1;
		ci->ci_l1inst.size = ci->ci_l1inst.sets * ci->ci_l1inst.setsize;

		if ((cfg & 0x80000000) != 0) {
			cfg = cp0_get_config_2();

			a = 1 + ((cfg >> 0) & 0x0f);
			l = (cfg >> 4) & 0x0f;
			s = (cfg >> 8) & 0x0f;

			ci->ci_l2.linesize = 2 << l;
			ci->ci_l2.sets = a;
			ci->ci_l2.setsize = (64 << s) * ci->ci_l2.linesize;
			ci->ci_l2.size = ci->ci_l2.sets * ci->ci_l2.setsize;

		}
	}

	memset(&ci->ci_l3, 0, sizeof(struct cache_info));

	ci->ci_SyncCache = Octeon_SyncCache;
	ci->ci_InvalidateICache = Octeon_InvalidateICache;
	ci->ci_InvalidateICachePage = Octeon_InvalidateICachePage;
	ci->ci_SyncICache = Octeon_SyncICache;
	ci->ci_SyncDCachePage = Octeon_SyncDCachePage;
	ci->ci_HitSyncDCache = Octeon_HitSyncDCache;
	ci->ci_HitInvalidateDCache = Octeon_HitInvalidateDCache;
	ci->ci_IOSyncDCache = Octeon_IOSyncDCache;
}

void
Octeon_SyncCache(struct cpu_info *ci)
{
	mips_sync();
}

void
Octeon_InvalidateICache(struct cpu_info *ci, vaddr_t va, size_t len)
{
	/* A SYNCI flushes the entire icache on OCTEON */
	SYNCI();
}

/*
 * Register a given page for I$ invalidation.
 */
void
Octeon_InvalidateICachePage(struct cpu_info *ci, vaddr_t va)
{
	/*
	 * Since there is apparently no way to operate on a subset of I$,
	 * all we need to do here is remember there are postponed flushes.
	 */
	ci->ci_cachepending_l1i = 1;
}

/*
 * Perform postponed I$ invalidation.
 */
void
Octeon_SyncICache(struct cpu_info *ci)
{
	if (ci->ci_cachepending_l1i != 0) {
		SYNCI(); /* Octeon_InvalidateICache(ci, 0, PAGE_SIZE); */
		ci->ci_cachepending_l1i = 0;
	}
}

void
Octeon_SyncDCachePage(struct cpu_info *ci, vaddr_t va, paddr_t pa)
{
}

void
Octeon_HitSyncDCache(struct cpu_info *ci, vaddr_t va, size_t len)
{
}

void
Octeon_HitInvalidateDCache(struct cpu_info *ci, vaddr_t va, size_t len)
{
}

void
Octeon_IOSyncDCache(struct cpu_info *ci, vaddr_t va, size_t len, int how)
{
	switch (how) {
	default:
	case CACHE_SYNC_R:
		break;
	case CACHE_SYNC_W: /* writeback */
	case CACHE_SYNC_X: /* writeback and invalidate */
		mips_sync();
		break;
	}
}

license: unknown


code:
/*	$OpenBSD: cache_r10k.c,v 1.6 2014/03/31 20:21:19 miod Exp $	*/

/*
 * Copyright (c) 2012 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * Cache handling code for R10000 processor family (R10000, R12000,
 * R14000, R16000).
 *
 * R10000 caches are :
 * - L1 I$ is 2-way, VIPT, 64 bytes/line, 32KB total
 * - L1 D$ is 2-way, VIPT, write-back, 32 bytes/line, 32KB total
 * - L2 is 2-way, PIPT, write-back, 64 or 128 bytes/line, 512KB up to 16MB
 */

#include <sys/param.h>
#include <sys/systm.h>

#include <mips64/cache.h>
#include <machine/cpu.h>

#include <uvm/uvm_extern.h>

#define	IndexInvalidate_I	0x00
#define	IndexWBInvalidate_D	0x01
#define	IndexWBInvalidate_S	0x03

#define	HitInvalidate_D		0x11
#define	HitInvalidate_S		0x13

#define	CacheBarrier		0x14

#define	HitWBInvalidate_D	0x15
#define	HitWBInvalidate_S	0x17

#define	cache(op,set,addr) \
    __asm__ volatile \
      ("cache %0, %1(%2)" :: "i"(op), "i"(set), "r"(addr) : "memory")

static __inline__ void	mips10k_hitinv_primary(vaddr_t, vsize_t);
static __inline__ void	mips10k_hitinv_secondary(vaddr_t, vsize_t, vsize_t);
static __inline__ void	mips10k_hitwbinv_primary(vaddr_t, vsize_t);
static __inline__ void	mips10k_hitwbinv_secondary(vaddr_t, vsize_t, vsize_t);

#define	R10K_L1I_LINE	64UL
#define	R10K_L1D_LINE	32UL

void
Mips10k_ConfigCache(struct cpu_info *ci)
{
	uint32_t cfg, valias_mask;

	cfg = cp0_get_config();

	ci->ci_l1inst.size = (1 << 12) << ((cfg >> 29) & 0x07);	/* IC */
	ci->ci_l1inst.linesize = R10K_L1I_LINE;
	ci->ci_l1inst.setsize = ci->ci_l1inst.size / 2;
	ci->ci_l1inst.sets = 2;

	ci->ci_l1data.size = (1 << 12) << ((cfg >> 26) & 0x07);	/* DC */
	ci->ci_l1data.linesize = R10K_L1D_LINE;
	ci->ci_l1data.setsize = ci->ci_l1data.size / 2;
	ci->ci_l1data.sets = 2;

	ci->ci_l2.size = (1 << 19) << ((cfg >> 16) & 0x07);
	ci->ci_l2.linesize = (cfg & (1 << 13)) ? 128 : 64;
	ci->ci_l2.setsize = ci->ci_l2.size / 2;
	ci->ci_l2.sets = 2;

	memset(&ci->ci_l3, 0, sizeof(struct cache_info));

	valias_mask = (max(ci->ci_l1inst.setsize, ci->ci_l1data.setsize) - 1) &
	    ~PAGE_MASK;

	if (valias_mask != 0) {
		valias_mask |= PAGE_MASK;
#ifdef MULTIPROCESSOR
		if (valias_mask > cache_valias_mask) {
#endif
			cache_valias_mask = valias_mask;
			pmap_prefer_mask = valias_mask;
#ifdef MULTIPROCESSOR
		}
#endif
	}

	ci->ci_SyncCache = Mips10k_SyncCache;
	ci->ci_InvalidateICache = Mips10k_InvalidateICache;
	ci->ci_InvalidateICachePage = Mips10k_InvalidateICachePage;
	ci->ci_SyncICache = Mips10k_SyncICache;
	ci->ci_SyncDCachePage = Mips10k_SyncDCachePage;
	ci->ci_HitSyncDCache = Mips10k_HitSyncDCache;
	ci->ci_HitInvalidateDCache = Mips10k_HitInvalidateDCache;
	ci->ci_IOSyncDCache = Mips10k_IOSyncDCache;
}

static __inline__ void
mips10k_hitwbinv_primary(vaddr_t va, vsize_t sz)
{
	vaddr_t eva;

	eva = va + sz;
	while (va != eva) {
		cache(HitWBInvalidate_D, 0, va);
		va += R10K_L1D_LINE;
	}
}

static __inline__ void
mips10k_hitwbinv_secondary(vaddr_t va, vsize_t sz, vsize_t line)
{
	vaddr_t eva;

	eva = va + sz;
	while (va != eva) {
		cache(HitWBInvalidate_S, 0, va);
		va += line;
	}
}

static __inline__ void
mips10k_hitinv_primary(vaddr_t va, vsize_t sz)
{
	vaddr_t eva;

	eva = va + sz;
	while (va != eva) {
		cache(HitInvalidate_D, 0, va);
		va += R10K_L1D_LINE;
	}
}

static __inline__ void
mips10k_hitinv_secondary(vaddr_t va, vsize_t sz, vsize_t line)
{
	vaddr_t eva;

	eva = va + sz;
	while (va != eva) {
		cache(HitInvalidate_S, 0, va);
		va += line;
	}
}

/*
 * Writeback and invalidate all caches.
 */
void
Mips10k_SyncCache(struct cpu_info *ci)
{
	vaddr_t sva, eva;

	sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
	eva = sva + ci->ci_l1inst.setsize;
	while (sva != eva) {
		cache(IndexInvalidate_I, 0, sva);
		cache(IndexInvalidate_I, 1, sva);
		sva += R10K_L1I_LINE;
	}

	sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
	eva = sva + ci->ci_l1data.setsize;
	while (sva != eva) {
		cache(IndexWBInvalidate_D, 0, sva);
		cache(IndexWBInvalidate_D, 1, sva);
		sva += R10K_L1D_LINE;
	}

	sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
	eva = sva + ci->ci_l2.setsize;
	while (sva != eva) {
		cache(IndexWBInvalidate_S, 0, sva);
		cache(IndexWBInvalidate_S, 1, sva);
		sva += ci->ci_l2.linesize;
	}
}

/*
 * Invalidate I$ for the given range.
 */
void
Mips10k_InvalidateICache(struct cpu_info *ci, vaddr_t _va, size_t _sz)
{
	vaddr_t va, sva, eva;
	vsize_t sz;

	/* extend the range to integral cache lines */
	va = _va & ~(R10K_L1I_LINE - 1);
	sz = ((_va + _sz + R10K_L1I_LINE - 1) & ~(R10K_L1I_LINE - 1)) - va;

	sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
	/* keep only the index bits */
	sva |= va & ((1UL << 14) - 1);
	eva = sva + sz;
	while (sva != eva) {
		cache(IndexInvalidate_I, 0, sva);
		cache(IndexInvalidate_I, 1, sva);
		sva += R10K_L1I_LINE;
	}
}

/*
 * Register a given page for I$ invalidation.
 */
void
Mips10k_InvalidateICachePage(struct cpu_info *ci, vaddr_t va)
{
#if PAGE_SHIFT < 14
	/*
	 * Mark the particular page index bits as needing to be flushed.
	 */
	ci->ci_cachepending_l1i |=
	    1ULL << ((va & ((1UL << 14) - 1)) >> PAGE_SHIFT);
#else
	/*
	 * Since the page size matches the I$ set size, all we need to do
	 * here is remember there are postponed flushes.
	 */
	ci->ci_cachepending_l1i = 1;
#endif
}

/*
 * Perform postponed I$ invalidation.
 */
void
Mips10k_SyncICache(struct cpu_info *ci)
{
	vaddr_t sva, eva;

	if (ci->ci_cachepending_l1i != 0) {
#if PAGE_SHIFT < 14
		/*
		 * Iterate on all pending page index bits.
		 */
		uint64_t idx;

		for (idx = 0; ci->ci_cachepending_l1i != 0; idx++) {
			if ((ci->ci_cachepending_l1i & (1ULL << idx)) == 0)
				continue;

			sva = PHYS_TO_XKPHYS(idx << PAGE_SHIFT, CCA_CACHED);
			eva = sva + PAGE_SIZE;
			while (sva != eva) {
				cache(IndexInvalidate_I, 0, sva);
				cache(IndexInvalidate_I, 1, sva);
				sva += R10K_L1I_LINE;
			}

			ci->ci_cachepending_l1i &= ~(1ULL << idx);
		}
#else
		/* inline Mips10k_InvalidateICache(ci, 0, PAGE_SIZE); */
		sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
		eva = sva + PAGE_SIZE;
		while (sva != eva) {
			cache(IndexInvalidate_I, 0, sva);
			cache(IndexInvalidate_I, 1, sva);
			sva += R10K_L1I_LINE;
		}
		ci->ci_cachepending_l1i = 0;
#endif
	}
}

/*
 * Writeback D$ for the given page.
 */
void
Mips10k_SyncDCachePage(struct cpu_info *ci, vaddr_t va, paddr_t pa)
{
#if PAGE_SHIFT < 14
	vaddr_t sva, eva;

	sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
	/* keep only the index bits */
	sva += va & ((1UL << 14) - 1);
	eva = sva + PAGE_SIZE;
	while (sva != eva) {
		cache(IndexWBInvalidate_D, 0 + 0 * R10K_L1D_LINE, sva);
		cache(IndexWBInvalidate_D, 0 + 1 * R10K_L1D_LINE, sva);
		cache(IndexWBInvalidate_D, 0 + 2 * R10K_L1D_LINE, sva);
		cache(IndexWBInvalidate_D, 0 + 3 * R10K_L1D_LINE, sva);
		cache(IndexWBInvalidate_D, 1 + 0 * R10K_L1D_LINE, sva);
		cache(IndexWBInvalidate_D, 1 + 1 * R10K_L1D_LINE, sva);
		cache(IndexWBInvalidate_D, 1 + 2 * R10K_L1D_LINE, sva);
		cache(IndexWBInvalidate_D, 1 + 3 * R10K_L1D_LINE, sva);
		sva += 4 * R10K_L1D_LINE;
	}
#else
	/*
	 * Since the page size is not smaller than the L1 virtual index space,
	 * it is cheaper to use Hit operations to make sure we do not affect
	 * innocent cache lines.
	 */
	mips10k_hitwbinv_primary(PHYS_TO_XKPHYS(pa, CCA_CACHED), PAGE_SIZE);
#endif
}

/*
 * Writeback D$ for the given range. Range is expected to be currently
 * mapped, allowing the use of `Hit' operations. This is less aggressive
 * than using `Index' operations.
 */

void
Mips10k_HitSyncDCache(struct cpu_info *ci, vaddr_t _va, size_t _sz)
{
	vaddr_t va;
	vsize_t sz;

	/* extend the range to integral cache lines */
	va = _va & ~(R10K_L1D_LINE - 1);
	sz = ((_va + _sz + R10K_L1D_LINE - 1) & ~(R10K_L1D_LINE - 1)) - va;
	mips10k_hitwbinv_primary(va, sz);
}

/*
 * Invalidate D$ for the given range. Range is expected to be currently
 * mapped, allowing the use of `Hit' operations. This is less aggressive
 * than using `Index' operations.
 */

void
Mips10k_HitInvalidateDCache(struct cpu_info *ci, vaddr_t _va, size_t _sz)
{
	vaddr_t va;
	vsize_t sz;

	/* extend the range to integral cache lines */
	va = _va & ~(R10K_L1D_LINE - 1);
	sz = ((_va + _sz + R10K_L1D_LINE - 1) & ~(R10K_L1D_LINE - 1)) - va;
	mips10k_hitinv_primary(va, sz);
}

/*
 * Backend for bus_dmamap_sync(). Enforce coherency of the given range
 * by performing the necessary cache writeback and/or invalidate
 * operations.
 */
void
Mips10k_IOSyncDCache(struct cpu_info *ci, vaddr_t _va, size_t _sz, int how)
{
	vaddr_t va;
	vsize_t sz;
	vsize_t line;
	int partial_start, partial_end;

	line = ci->ci_l2.linesize;
	/* extend the range to integral cache lines */
	if (line == 64) {
		va = _va & ~(64UL - 1);
		sz = ((_va + _sz + 64UL - 1) & ~(64UL - 1)) - va;
	} else {
		va = _va & ~(128UL - 1);
		sz = ((_va + _sz + 128UL - 1) & ~(128UL - 1)) - va;
	}

	switch (how) {
	case CACHE_SYNC_R:
		/* writeback partial cachelines */
		if (((_va | _sz) & (line - 1)) != 0) {
			partial_start = va != _va;
			partial_end = va + sz != _va + _sz;
		} else {
			partial_start = partial_end = 0;
		}
		if (partial_start) {
			cache(HitWBInvalidate_S, 0, va);
			va += line;
			sz -= line;
		}
		if (sz != 0 && partial_end) {
			cache(HitWBInvalidate_S, 0, va + sz - line);
			sz -= line;
		}
		if (sz != 0) {
			if (line == 64)
				mips10k_hitinv_secondary(va, sz, 64UL);
			else
				mips10k_hitinv_secondary(va, sz, 128UL);
		}
		break;
	case CACHE_SYNC_X:
	case CACHE_SYNC_W:
		if (line == 64)
			mips10k_hitwbinv_secondary(va, sz, 64UL);
		else
			mips10k_hitwbinv_secondary(va, sz, 128UL);
		break;
	}
}


code:
/*	$OpenBSD: cache_r4k.c,v 1.14 2014/03/31 20:21:19 miod Exp $	*/

/*
 * Copyright (c) 2012 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>

#include <mips64/cache.h>
#include <machine/cpu.h>
#include <mips64/mips_cpu.h>

#include <uvm/uvm_extern.h>

#define	IndexInvalidate_I	0x00
#define	IndexWBInvalidate_D	0x01
#define	IndexWBInvalidate_S	0x03

#define	HitInvalidate_D		0x11
#define	HitInvalidate_S		0x13

#define	HitWBInvalidate_D	0x15
#define	HitWBInvalidate_S	0x17

#define	cache(op,addr) \
    __asm__ volatile ("cache %0, 0(%1)" :: "i"(op), "r"(addr) : "memory")

static __inline__ void	mips4k_hitinv_primary(vaddr_t, vsize_t, vsize_t);
static __inline__ void	mips4k_hitinv_secondary(vaddr_t, vsize_t, vsize_t);
static __inline__ void	mips4k_hitwbinv_primary(vaddr_t, vsize_t, vsize_t);
static __inline__ void	mips4k_hitwbinv_secondary(vaddr_t, vsize_t, vsize_t);

/*
 * Invoke a simple routine from uncached space (either CKSEG1 or uncached
 * XKPHYS).
 */

static void
run_uncached(void (*fn)(register_t), register_t arg)
{
	vaddr_t va;
	paddr_t pa;

	va = (vaddr_t)fn;
	if (IS_XKPHYS(va)) {
		pa = XKPHYS_TO_PHYS(va);
		va = PHYS_TO_XKPHYS(pa, CCA_NC);
	} else {
		pa = CKSEG0_TO_PHYS(va);
		va = PHYS_TO_CKSEG1(pa);
	}
	fn = (void (*)(register_t))va;

	(*fn)(arg);
}

void
Mips4k_ConfigCache(struct cpu_info *ci)
{
	register_t cfg, ncfg;

	cfg = cp0_get_config();

	ci->ci_l1inst.size = (1 << 12) << ((cfg >> 9) & 0x07); /* IC */
	if (cfg & (1 << 5))	/* IB */
		ci->ci_l1inst.linesize = 32;
	else
		ci->ci_l1inst.linesize = 16;

	ci->ci_l1data.size = (1 << 12) << ((cfg >> 6) & 0x07); /* DC */
	if (cfg & (1 << 4))	/* DB */
		ci->ci_l1data.linesize = 32;
	else
		ci->ci_l1data.linesize = 16;

	/* R4000 and R4400 L1 caches are direct */
	ci->ci_l1inst.setsize = ci->ci_l1inst.size;
	ci->ci_l1inst.sets = 1;
	ci->ci_l1data.setsize = ci->ci_l1data.size;
	ci->ci_l1data.sets = 1;

	cache_valias_mask =
	    (max(ci->ci_l1inst.size, ci->ci_l1data.size) - 1) &
	    ~PAGE_MASK;

	if ((cfg & (1 << 17)) == 0) {	/* SC */
		/*
		 * We expect the setup code to have set up ci->ci_l2 for us.
		 * Unfortunately we aren't allowed to panic() there if it
		 * didn't, because the console is not available.
		 */

		/* fixed 32KB aliasing to avoid VCE */
		pmap_prefer_mask = ((1 << 15) - 1);
	} else {
		memset(&ci->ci_l2, 0, sizeof(struct cache_info));
	}
	memset(&ci->ci_l3, 0, sizeof(struct cache_info));

	if (cache_valias_mask != 0) {
		cache_valias_mask |= PAGE_MASK;
		pmap_prefer_mask |= cache_valias_mask;
	}

	ci->ci_SyncCache = Mips4k_SyncCache;
	ci->ci_InvalidateICache = Mips4k_InvalidateICache;
	ci->ci_InvalidateICachePage = Mips4k_InvalidateICachePage;
	ci->ci_SyncICache = Mips4k_SyncICache;
	ci->ci_SyncDCachePage = Mips4k_SyncDCachePage;
	ci->ci_HitSyncDCache = Mips4k_HitSyncDCache;
	ci->ci_HitInvalidateDCache = Mips4k_HitInvalidateDCache;
	ci->ci_IOSyncDCache = Mips4k_IOSyncDCache;

	ncfg = (cfg & ~CFGR_CCA_MASK) | CCA_CACHED;
	ncfg &= ~CFGR_CU;
	if (cfg != ncfg)
		run_uncached(cp0_set_config, ncfg);
}

/*
 * Writeback and invalidate all caches.
 */
void
Mips4k_SyncCache(struct cpu_info *ci)
{
	vaddr_t sva, eva;
	vsize_t line;

	sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
	eva = sva + ci->ci_l1inst.size;
	line = ci->ci_l1inst.linesize;
	while (sva != eva) {
		cache(IndexInvalidate_I, sva);
		sva += line;
	}

	sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
	eva = sva + ci->ci_l1data.size;
	line = ci->ci_l1data.linesize;
	while (sva != eva) {
		cache(IndexWBInvalidate_D, sva);
		sva += line;
	}

	if (ci->ci_l2.size != 0) {
		sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
		eva = sva + ci->ci_l2.size;
		line = ci->ci_l2.linesize;
		while (sva != eva) {
			cache(IndexWBInvalidate_S, sva);
			sva += line;
		}
	}

	mips_sync();
}

/*
 * Invalidate I$ for the given range.
 */
void
Mips4k_InvalidateICache(struct cpu_info *ci, vaddr_t _va, size_t _sz)
{
	vaddr_t va, sva, eva;
	vsize_t sz;
	vsize_t line;

	line = ci->ci_l1inst.linesize;
	/* extend the range to integral cache lines */
	if (line == 16) {
		va = _va & ~(16UL - 1);
		sz = ((_va + _sz + 16 - 1) & ~(16UL - 1)) - va;
	} else {
		va = _va & ~(32UL - 1);
		sz = ((_va + _sz + 32 - 1) & ~(32UL - 1)) - va;
	}

	sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
	/* keep only the index bits */
	sva += va & ((1UL << 15) - 1);
	if (sz > ci->ci_l1inst.size)
		sz = ci->ci_l1inst.size;
	eva = sva + sz;
	while (sva != eva) {
		cache(IndexInvalidate_I, sva);
		sva += line;
	}

	mips_sync();
}

/*
 * Register a given page for I$ invalidation.
 */
void
Mips4k_InvalidateICachePage(struct cpu_info *ci, vaddr_t va)
{
	/*
	 * Mark the particular page index bits as needing to be flushed.
	 */
	ci->ci_cachepending_l1i |=
	    1ULL << ((va & ((1UL << 15) - 1)) >> PAGE_SHIFT);
}

/*
 * Perform postponed I$ invalidation.
 */
void
Mips4k_SyncICache(struct cpu_info *ci)
{
	vaddr_t sva, eva;
	vsize_t line;
	uint64_t idx;

	if (ci->ci_cachepending_l1i != 0) {
		line = ci->ci_l1inst.linesize;

		if (ci->ci_l1inst.size <= PAGE_SIZE) {
			ci->ci_cachepending_l1i = 0;
			sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
			eva = sva + ci->ci_l1inst.size;
			while (sva != eva) {
				cache(IndexInvalidate_I, sva);
				sva += line;
			}
			return;
		}

		/*
		 * Iterate on all pending page index bits.
		 */
		for (idx = 0; ci->ci_cachepending_l1i != 0; idx++) {
			if ((ci->ci_cachepending_l1i & (1ULL << idx)) == 0)
				continue;

			sva = PHYS_TO_XKPHYS(idx << PAGE_SHIFT, CCA_CACHED);
			eva = sva + PAGE_SIZE;
			while (sva != eva) {
				cache(IndexInvalidate_I, sva);
				sva += line;
			}

			ci->ci_cachepending_l1i &= ~(1ULL << idx);
		}
	}
}

/*
 * Writeback D$ for the given page.
 */
void
Mips4k_SyncDCachePage(struct cpu_info *ci, vaddr_t va, paddr_t pa)
{
	vaddr_t sva, eva;
	vsize_t line;

	line = ci->ci_l1data.linesize;
	sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
	/* keep only the index bits */
	sva += va & ((1UL << 15) - 1);
	if (PAGE_SIZE > ci->ci_l1data.size)
		eva = sva + ci->ci_l1data.size;
	else
		eva = sva + PAGE_SIZE;
	while (sva != eva) {
		cache(IndexWBInvalidate_D, sva);
		sva += line;
	}

	if (ci->ci_l2.size != 0) {
		line = ci->ci_l2.linesize;
		sva = PHYS_TO_XKPHYS(pa, CCA_CACHED);
		eva = sva + PAGE_SIZE;
		while (sva != eva) {
			cache(IndexWBInvalidate_S, sva);
			sva += line;
		}
	}

	mips_sync();
}

/*
 * Writeback D$ for the given range. Range is expected to be currently
 * mapped, allowing the use of `Hit' operations. This is less aggressive
 * than using `Index' operations.
 */

static __inline__ void
mips4k_hitwbinv_primary(vaddr_t va, vsize_t sz, vsize_t line)
{
	vaddr_t eva;

	eva = va + sz;
	while (va != eva) {
		cache(HitWBInvalidate_D, va);
		va += line;
	}
}

static __inline__ void
mips4k_hitwbinv_secondary(vaddr_t va, vsize_t sz, vsize_t line)
{
	vaddr_t eva;

	eva = va + sz;
	while (va != eva) {
		cache(HitWBInvalidate_S, va);
		va += line;
	}
}

void
Mips4k_HitSyncDCache(struct cpu_info *ci, vaddr_t _va, size_t _sz)
{
	vaddr_t va;
	vsize_t sz;
	vsize_t line;

	line = ci->ci_l1data.linesize;
	/* extend the range to integral cache lines */
	if (line == 16) {
		va = _va & ~(16UL - 1);
		sz = ((_va + _sz + 16 - 1) & ~(16UL - 1)) - va;
	} else {
		va = _va & ~(32UL - 1);
		sz = ((_va + _sz + 32 - 1) & ~(32UL - 1)) - va;
	}
	mips4k_hitwbinv_primary(va, sz, line);

	if (ci->ci_l2.size != 0) {
		line = ci->ci_l2.linesize;
		/* extend the range to integral cache lines */
		va = _va & ~(line - 1);
		sz = ((_va + _sz + line - 1) & ~(line - 1)) - va;
		mips4k_hitwbinv_secondary(va, sz, line);
	}

	mips_sync();
}

/*
 * Invalidate D$ for the given range. Range is expected to be currently
 * mapped, allowing the use of `Hit' operations. This is less aggressive
 * than using `Index' operations.
 */

static __inline__ void
mips4k_hitinv_primary(vaddr_t va, vsize_t sz, vsize_t line)
{
	vaddr_t eva;

	eva = va + sz;
	while (va != eva) {
		cache(HitInvalidate_D, va);
		va += line;
	}
}

static __inline__ void
mips4k_hitinv_secondary(vaddr_t va, vsize_t sz, vsize_t line)
{
	vaddr_t eva;

	eva = va + sz;
	while (va != eva) {
		cache(HitInvalidate_S, va);
		va += line;
	}
}

void
Mips4k_HitInvalidateDCache(struct cpu_info *ci, vaddr_t _va, size_t _sz)
{
	vaddr_t va;
	vsize_t sz;
	vsize_t line;

	line = ci->ci_l1data.linesize;
	/* extend the range to integral cache lines */
	if (line == 16) {
		va = _va & ~(16UL - 1);
		sz = ((_va + _sz + 16 - 1) & ~(16UL - 1)) - va;
	} else {
		va = _va & ~(32UL - 1);
		sz = ((_va + _sz + 32 - 1) & ~(32UL - 1)) - va;
	}
	mips4k_hitinv_primary(va, sz, line);

	if (ci->ci_l2.size != 0) {
		line = ci->ci_l2.linesize;
		/* extend the range to integral cache lines */
		va = _va & ~(line - 1);
		sz = ((_va + _sz + line - 1) & ~(line - 1)) - va;
		mips4k_hitinv_secondary(va, sz, line);
	}

	mips_sync();
}

/*
 * Backend for bus_dmamap_sync(). Enforce coherency of the given range
 * by performing the necessary cache writeback and/or invalidate
 * operations.
 */
void
Mips4k_IOSyncDCache(struct cpu_info *ci, vaddr_t _va, size_t _sz, int how)
{
	vaddr_t va;
	vsize_t sz;
	vsize_t line;
	int partial_start, partial_end;

	/*
	 * L1
	 */

	line = ci->ci_l1data.linesize;
	/* extend the range to integral cache lines */
	if (line == 16) {
		va = _va & ~(16UL - 1);
		sz = ((_va + _sz + 16 - 1) & ~(16UL - 1)) - va;
	} else {
		va = _va & ~(32UL - 1);
		sz = ((_va + _sz + 32 - 1) & ~(32UL - 1)) - va;
	}

	switch (how) {
	case CACHE_SYNC_R:
		/* writeback partial cachelines */
		if (((_va | _sz) & (line - 1)) != 0) {
			partial_start = va != _va;
			partial_end = va + sz != _va + _sz;
		} else {
			partial_start = partial_end = 0;
		}
		if (partial_start) {
			cache(HitWBInvalidate_D, va);
			va += line;
			sz -= line;
		}
		if (sz != 0 && partial_end) {
			sz -= line;
			cache(HitWBInvalidate_D, va + sz);
		}
		if (sz != 0)
			mips4k_hitinv_primary(va, sz, line);
		break;
	case CACHE_SYNC_X:
	case CACHE_SYNC_W:
		mips4k_hitwbinv_primary(va, sz, line);
		break;
	}

	/*
	 * L2
	 */

	if (ci->ci_l2.size != 0) {
		line = ci->ci_l2.linesize;
		/* extend the range to integral cache lines */
		va = _va & ~(line - 1);
		sz = ((_va + _sz + line - 1) & ~(line - 1)) - va;

		switch (how) {
		case CACHE_SYNC_R:
			/* writeback partial cachelines */
			if (((_va | _sz) & (line - 1)) != 0) {
				partial_start = va != _va;
				partial_end = va + sz != _va + _sz;
			} else {
				partial_start = partial_end = 0;
			}
			if (partial_start) {
				cache(HitWBInvalidate_S, va);
				va += line;
				sz -= line;
			}
			if (sz != 0 && partial_end) {
				sz -= line;
				cache(HitWBInvalidate_S, va + sz);
			}
			if (sz != 0)
				mips4k_hitinv_secondary(va, sz, line);
			break;
		case CACHE_SYNC_X:
		case CACHE_SYNC_W:
			mips4k_hitwbinv_secondary(va, sz, line);
			break;
		}
	}
}


code:
/*	$OpenBSD: cache_r5k.c,v 1.14 2014/06/17 18:58:35 miod Exp $	*/

/*
 * Copyright (c) 2012 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/*
 * Copyright (c) 1998-2004 Opsycon AB (www.opsycon.se)
 *
 * 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 AUTHOR ``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 AUTHOR 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.
 *
 */

/*
 * Cache handling for R5000 processor and close relatives:
 * R4600/R4700, R5000, RM52xx, RM7xxx, RM9xxx.
 *
 * The following assumptions are made:
 * - L1 I$ is 2-way or 4-way (RM7k/RM9k), VIPT, 32 bytes/line, up to 32KB
 * - L1 D$ is 2-way or 4-way (RM7k/RM9k), VIPT, write-back, 32 bytes/line,
 *   up to 32KB
 * - `internal' L2 on RM7k/RM9k is 4-way, PIPT, write-back, 32 bytes/line,
 *   256KB total
 * - L3 (on RM7k/RM9k) or `external' L2 (all others) may not exist. If it
 *   does, it is direct-mapped, PIPT, write-through
 */

#include <sys/param.h>
#include <sys/systm.h>

#include <mips64/cache.h>
#include <machine/cpu.h>
#include <mips64/mips_cpu.h>

#include <uvm/uvm_extern.h>

#define	IndexInvalidate_I	0x00
#define	IndexWBInvalidate_D	0x01
#define	IndexWBInvalidate_S	0x03

#define	IndexStoreTag_S		0x0b

#define	HitInvalidate_D		0x11
#define	HitInvalidate_S		0x13

#define	HitWBInvalidate_D	0x15
#define	InvalidatePage_T	0x16	/* Only RM7k/RM9k */
#define	HitWBInvalidate_S	0x17
#define	InvalidatePage_S	0x17	/* Only RM527[0-1] */

#define	HitWriteback_D		0x19
#define	HitWriteback_S		0x1b

/*
 *  R5000 and RM52xx config register bits.
 */
#define	CF_5_SE		(1U << 12)	/* Secondary cache enable */
#define	CF_5_SC		(1U << 17)	/* Secondary cache not present */
#define	CF_5_SS		(3U << 20)	/* Secondary cache size */
#define	CF_5_SS_AL	20		/* Shift to align */

/*
 *  RM7000 config register bits.
 */
#define	CF_7_SE		(1U << 3)	/* Secondary cache enable */
#define	CF_7_SC		(1U << 31)	/* Secondary cache not present */
#define	CF_7_TE		(1U << 12)	/* Tertiary cache enable */
#define	CF_7_TC		(1U << 17)	/* Tertiary cache not present */
#define	CF_7_TS		(3U << 20)	/* Tertiary cache size */
#define	CF_7_TS_AL	20		/* Shift to align */

#define	R5K_LINE	32UL		/* internal cache line */
#define	R5K_PAGE	4096UL		/* external cache page */

/*
 * Cache configuration
 */
#define	CTYPE_HAS_IL2		0x01	/* Internal L2 Cache present */
#define	CTYPE_HAS_XL2		0x02	/* External L2 Cache present */
#define	CTYPE_HAS_XL3		0x04	/* External L3 Cache present */

#define	nop4()			__asm__ volatile \
	("nop; nop; nop; nop")
#define	nop10()			__asm__ volatile \
	("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop")

#define	cache(op,offs,addr)	__asm__ volatile \
	("cache %0, %1(%2)" :: "i"(op), "i"(offs), "r"(addr) : "memory")

#define	reset_taglo()		__asm__ volatile \
	("mtc0 $zero, $28")	/* COP_0_TAG_LO */
#define	reset_taghi()		__asm__ volatile \
	("mtc0 $zero, $29")	/* COP_0_TAG_HI */

static __inline__ register_t
get_config(void)
{
	register_t cfg;
	__asm__ volatile ("mfc0 %0, $16" : "=r"(cfg)); /* COP_0_CONFIG */
	return cfg;
}

static __inline__ void
set_config(register_t cfg)
{
	__asm__ volatile ("mtc0 %0, $16" :: "r"(cfg)); /* COP_0_CONFIG */
	/* MTC0_HAZARD */
#ifdef CPU_RM7000
	nop10();
#else
	nop4();
#endif
}

static __inline__ void	mips5k_hitinv_primary(vaddr_t, vsize_t);
static __inline__ void	mips5k_hitinv_secondary(vaddr_t, vsize_t);
static __inline__ void	mips5k_hitwbinv_primary(vaddr_t, vsize_t);
static __inline__ void	mips5k_hitwbinv_secondary(vaddr_t, vsize_t);
static __inline__ void	mips5k_hitwb_primary(vaddr_t, vsize_t);
#if 0
static __inline__ void	mips5k_hitwb_secondary(vaddr_t, vsize_t);
#endif

void mips5k_l2_init(register_t);
void mips7k_l2_init(register_t);
void mips7k_l3_init(register_t);
void mips5k_c0_cca_update(register_t);
static void run_uncached(void (*)(register_t), register_t);

/*
 * Invoke a simple routine from uncached space (either CKSEG1 or uncached
 * XKPHYS).
 */

static void
run_uncached(void (*fn)(register_t), register_t arg)
{
	vaddr_t va;
	paddr_t pa;

	va = (vaddr_t)fn;
	if (IS_XKPHYS(va)) {
		pa = XKPHYS_TO_PHYS(va);
		va = PHYS_TO_XKPHYS(pa, CCA_NC);
	} else {
		pa = CKSEG0_TO_PHYS(va);
		va = PHYS_TO_CKSEG1(pa);
	}
	fn = (void (*)(register_t))va;

	(*fn)(arg);
}

/*
 * Initialize the external L2 cache of an R5000 (or close relative) processor.
 * INTENDED TO BE RUN UNCACHED - BE SURE TO CHECK THAT IT WON'T STORE ANYTHING
 * ON THE STACK IN THE ASSEMBLY OUTPUT EVERYTIME YOU CHANGE IT.
 */
void
mips5k_l2_init(register_t l2size)
{
	register vaddr_t va, eva;
	register register_t cfg;

	cfg = get_config();
	cfg |= CF_5_SE;
	set_config(cfg);

	reset_taglo();
	reset_taghi();

	va = PHYS_TO_XKPHYS(0, CCA_CACHED);
	eva = va + l2size;
	while (va != eva) {
		cache(InvalidatePage_S, 0, va);
		va += R5K_PAGE;
	}
}

/*
 * Initialize the internal L2 cache of an RM7000 (or close relative) processor.
 * INTENDED TO BE RUN UNCACHED - BE SURE TO CHECK THAT IT WON'T STORE ANYTHING
 * ON THE STACK IN THE ASSEMBLY OUTPUT EVERYTIME YOU CHANGE IT.
 */
void
mips7k_l2_init(register_t l2size)
{
	register vaddr_t va, eva;
	register register_t cfg;

	cfg = get_config();
	cfg |= CF_7_SE;
	set_config(cfg);

	reset_taglo();
	reset_taghi();

	va = PHYS_TO_XKPHYS(0, CCA_CACHED);
	eva = va + l2size;
	while (va != eva) {
		cache(IndexStoreTag_S, 0, va);
		va += R5K_LINE;
	}
	mips_sync();

	va = PHYS_TO_XKPHYS(0, CCA_CACHED);
	eva = va + l2size;
	while (va != eva) {
		__asm__ volatile
		    ("lw $zero, 0(%0)" :: "r"(va));
		va += R5K_LINE;
	}
	mips_sync();

	va = PHYS_TO_XKPHYS(0, CCA_CACHED);
	eva = va + l2size;
	while (va != eva) {
		cache(IndexStoreTag_S, 0, va);
		va += R5K_LINE;
	}
	mips_sync();
}

/*
 * Initialize the external L3 cache of an RM7000 (or close relative) processor.
 * INTENDED TO BE RUN UNCACHED - BE SURE TO CHECK THAT IT WON'T STORE ANYTHING
 * ON THE STACK IN THE ASSEMBLY OUTPUT EVERYTIME YOU CHANGE IT.
 */
void
mips7k_l3_init(register_t l3size)
{
	register vaddr_t va, eva;
	register register_t cfg;

	cfg = get_config();
	cfg |= CF_7_TE;
	set_config(cfg);

	reset_taglo();
	reset_taghi();

	va = PHYS_TO_XKPHYS(0, CCA_CACHED);
	eva = va + l3size;
	while (va != eva) {
		cache(InvalidatePage_T, 0, va);
		va += R5K_PAGE;
	}
}

/*
 * Update the coherency of KSEG0.
 * INTENDED TO BE RUN UNCACHED - BE SURE TO CHECK THAT IT WON'T STORE ANYTHING
 * ON THE STACK IN THE ASSEMBLY OUTPUT EVERYTIME YOU CHANGE IT.
 */
void
mips5k_c0_cca_update(register_t cfg)
{
	set_config(cfg);

#if defined(CPU_R5000) || defined(CPU_RM7000)
	/*
	 * RM52xx and RM7000 hazard: after updating the K0 field of the Config
	 * register, the KSEG0 and CKSEG0 address segments should not be used
	 * for 5 cycles. The register modification and the use of these address
	 * segments should be separated by at least five (RM52xx) or
	 * ten (RM7000) integer instructions.
	 */
	nop10();
#endif
}

/*
 * Discover cache configuration and update cpu_info accordingly.
 * Initialize L2 and L3 caches found if necessary.
 */
void
Mips5k_ConfigCache(struct cpu_info *ci)
{
	register_t cfg, ncfg;
	uint setshift;

	cfg = cp0_get_config();

	/* L1 cache */
	ci->ci_l1inst.size = (1 << 12) << ((cfg >> 9) & 0x07); /* IC */
	ci->ci_l1inst.linesize = R5K_LINE;
	ci->ci_l1data.size = (1 << 12) << ((cfg >> 6) & 0x07); /* DC */
	ci->ci_l1data.linesize = R5K_LINE;

	/* sane defaults */
	setshift = 1;
	memset(&ci->ci_l2, 0, sizeof(struct cache_info));
	memset(&ci->ci_l3, 0, sizeof(struct cache_info));
	ci->ci_cacheconfiguration = 0;

	switch ((cp0_get_prid() >> 8) & 0xff) {
	default:
		/* shouldn't happen, really; but we can't panic here */
		break;
#ifdef CPU_R4600
	case MIPS_R4600:
	case MIPS_R4700:
		/* no external L2 interface */
		break;
#endif
#ifdef CPU_R5000
	case MIPS_R5000:
	case MIPS_RM52X0:
		/* optional external direct L2 cache */
		if ((cfg & CF_5_SC) == 0) {
			ci->ci_l2.size = (1 << 19) <<
			    ((cfg & CF_5_SS) >> CF_5_SS_AL);
			ci->ci_l2.linesize = R5K_LINE;
			ci->ci_l2.setsize = ci->ci_l2.size;
			ci->ci_l2.sets = 1;
		}
		if (ci->ci_l2.size != 0) {
			ci->ci_cacheconfiguration |= CTYPE_HAS_XL2;
			cfg |= CF_5_SE;
			run_uncached(mips5k_l2_init, ci->ci_l2.size);
		}
		break;
#endif	/* CPU_R5000 */
#ifdef CPU_RM7000
	case MIPS_RM7000:
	case MIPS_RM9000:
		setshift = 2;
		/* optional external direct L3 cache */
		if ((cfg & CF_7_TC) == 0) {
#ifndef L3SZEXT
			/*
			 * Assume L3 size is provided in the system information
			 * field of the Config register. This is usually the
			 * case on systems where the RM7k/RM9k processor is
			 * an upgrade from an R5000/RM52xx processor, such as
			 * the SGI O2.
			 */
			ci->ci_l3.size = (1 << 19) <<
			    ((cfg & CF_7_TS) >> CF_7_TS_AL);
			ci->ci_l3.linesize = R5K_LINE;
			ci->ci_l3.setsize = ci->ci_l3.size;
			ci->ci_l3.sets = 1;
#else
			/*
			 * Assume machdep has initialized ci_l3 for us.
			 */
#endif
		}
		if (ci->ci_l3.size != 0) {
			ci->ci_cacheconfiguration |= CTYPE_HAS_XL3;
			cfg |= CF_7_TE;
			run_uncached(mips7k_l3_init, ci->ci_l3.size);

		}
		/* internal 4-way L2 cache */
		if ((cfg & CF_7_SC) == 0) {
			ci->ci_l2.size = 256 * 1024;	/* fixed size */
			ci->ci_l2.linesize = R5K_LINE;
			ci->ci_l2.setsize = ci->ci_l2.size / 4;
			ci->ci_l2.sets = 4;
		}
		if (ci->ci_l2.size != 0) {
			ci->ci_cacheconfiguration |= CTYPE_HAS_IL2;
			if ((cfg & CF_7_SE) == 0) {
				cfg |= CF_7_SE;
				run_uncached(mips7k_l2_init, ci->ci_l2.size);
			}
		}
		break;
#endif	/* CPU_RM7000 */
	}

	ci->ci_l1inst.setsize = ci->ci_l1inst.size >> setshift;
	ci->ci_l1inst.sets = setshift == 2 ? 4 : 2;
	ci->ci_l1data.setsize = ci->ci_l1data.size >> setshift;
	ci->ci_l1data.sets = setshift == 2 ? 4 : 2;

	cache_valias_mask =
	    (max(ci->ci_l1inst.setsize, ci->ci_l1data.setsize) - 1) &
	    ~PAGE_MASK;

	if (cache_valias_mask != 0) {
		cache_valias_mask |= PAGE_MASK;
		pmap_prefer_mask = cache_valias_mask;
	}

	ci->ci_SyncCache = Mips5k_SyncCache;
	ci->ci_InvalidateICache = Mips5k_InvalidateICache;
	ci->ci_InvalidateICachePage = Mips5k_InvalidateICachePage;
	ci->ci_SyncICache = Mips5k_SyncICache;
	ci->ci_SyncDCachePage = Mips5k_SyncDCachePage;
	ci->ci_HitSyncDCache = Mips5k_HitSyncDCache;
	ci->ci_HitInvalidateDCache = Mips5k_HitInvalidateDCache;
	ci->ci_IOSyncDCache = Mips5k_IOSyncDCache;

	ncfg = (cfg & ~CFGR_CCA_MASK) | CCA_CACHED;
	if (cfg != ncfg)
		run_uncached(mips5k_c0_cca_update, ncfg);
}

/*
 * Writeback and invalidate all caches.
 */
void
Mips5k_SyncCache(struct cpu_info *ci)
{
	vaddr_t sva, eva;
#ifdef CPU_R4600
	/*
	 * Revision 1 R4600 need to perform `Index' cache operations with
	 * interrupt disabled, to make sure both ways are correctly updated.
	 */
	register_t sr = disableintr();
#endif

	sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
	eva = sva + ci->ci_l1inst.size;
	while (sva != eva) {
		cache(IndexInvalidate_I, 0, sva);
		sva += R5K_LINE;
	}

	sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
	eva = sva + ci->ci_l1data.size;
	while (sva != eva) {
		cache(IndexWBInvalidate_D, 0, sva);
		sva += R5K_LINE;
	}

#ifdef CPU_R4600
	setsr(sr);
#endif

#ifdef CPU_RM7000
	if (ci->ci_cacheconfiguration & CTYPE_HAS_IL2) {
		sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
		eva = sva + ci->ci_l2.size;
		while (sva != eva) {
			cache(IndexWBInvalidate_S, 0, sva);
			sva += R5K_LINE;
		}
	} else
#endif
#ifdef CPU_R5000
	if (ci->ci_cacheconfiguration & CTYPE_HAS_XL2) {
		sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
		eva = sva + ci->ci_l2.size;
		reset_taglo();
		while (sva != eva) {
			cache(InvalidatePage_S, 0, sva);
			sva += R5K_PAGE;
		}
	} else
#endif
	{
	}

#ifdef CPU_RM7000
	if (ci->ci_cacheconfiguration & CTYPE_HAS_XL3) {
		sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
		eva = sva + ci->ci_l3.size;
		reset_taglo();
		while (sva != eva) {
			cache(InvalidatePage_T, 0, sva);
			sva += R5K_PAGE;
		}
	}
#endif

	mips_sync();
}

/*
 * Invalidate I$ for the given range.
 */
void
Mips5k_InvalidateICache(struct cpu_info *ci, vaddr_t _va, size_t _sz)
{
	vaddr_t va, sva, eva, iva;
	vsize_t sz, offs;
#ifdef CPU_R4600
	/*
	 * Revision 1 R4600 need to perform `Index' cache operations with
	 * interrupt disabled, to make sure both ways are correctly updated.
	 */
	register_t sr = disableintr();
#endif

	/* extend the range to integral cache lines */
	va = _va & ~(R5K_LINE - 1);
	sz = ((_va + _sz + R5K_LINE - 1) & ~(R5K_LINE - 1)) - va;

	sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
	offs = ci->ci_l1inst.setsize;
	/* keep only the index bits */
	sva |= va & (offs - 1);
	eva = sva + sz;

	switch (ci->ci_l1inst.sets) {
	default:
#ifdef CPU_RM7000
	case 4:
		while (sva != eva) {
			iva = sva;
			cache(IndexInvalidate_I, 0, iva);
			iva += offs;
			cache(IndexInvalidate_I, 0, iva);
			iva += offs;
			cache(IndexInvalidate_I, 0, iva);
			iva += offs;
			cache(IndexInvalidate_I, 0, iva);
			sva += R5K_LINE;
		}
		break;
#endif
#if defined(CPU_R5000) || defined(CPU_R4600)
	case 2:
		iva = sva + offs;
		while (sva != eva) {
			cache(IndexInvalidate_I, 0, iva);
			cache(IndexInvalidate_I, 0, sva);
			iva += R5K_LINE;
			sva += R5K_LINE;
		}
		break;
#endif
	}

#ifdef CPU_R4600
	setsr(sr);
#endif
	mips_sync();
}

/*
 * Register a given page for I$ invalidation.
 */
void
Mips5k_InvalidateICachePage(struct cpu_info *ci, vaddr_t va)
{
	/*
	 * Mark the particular page index bits as needing to be flushed.
	 */
	ci->ci_cachepending_l1i |=
	    1ULL << ((va & (ci->ci_l1inst.setsize - 1)) >> PAGE_SHIFT);
}

/*
 * Perform postponed I$ invalidation.
 */
void
Mips5k_SyncICache(struct cpu_info *ci)
{
	vaddr_t sva, eva, iva;
	vsize_t offs;
	uint64_t idx;
#ifdef CPU_R4600
	register_t sr;
#endif

	if (ci->ci_cachepending_l1i == 0)
		return;

#ifdef CPU_R4600
	/*
	 * Revision 1 R4600 need to perform `Index' cache operations with
	 * interrupt disabled, to make sure both ways are correctly updated.
	 */
	sr = disableintr();
#endif

	offs = ci->ci_l1inst.setsize;

	if (ci->ci_l1inst.setsize <= PAGE_SIZE) {
		ci->ci_cachepending_l1i = 0;
		sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
		eva = sva + offs;
		switch (ci->ci_l1inst.sets) {
		default:
#ifdef CPU_RM7000
		case 4:
			while (sva != eva) {
				iva = sva;
				cache(IndexInvalidate_I, 0, iva);
				iva += offs;
				cache(IndexInvalidate_I, 0, iva);
				iva += offs;
				cache(IndexInvalidate_I, 0, iva);
				iva += offs;
				cache(IndexInvalidate_I, 0, iva);
				sva += R5K_LINE;
			}
			break;
#endif
#if defined(CPU_R5000) || defined(CPU_R4600)
		case 2:
			iva = sva + offs;
			while (sva != eva) {
				cache(IndexInvalidate_I, 0, iva);
				cache(IndexInvalidate_I, 0, sva);
				iva += R5K_LINE;
				sva += R5K_LINE;
			}
			break;
#endif
		}
	} else {
		/*
		 * Iterate on all pending page index bits.
		 */
		for (idx = 0; ci->ci_cachepending_l1i != 0; idx++) {
			if ((ci->ci_cachepending_l1i & (1ULL << idx)) == 0)
				continue;

			sva = PHYS_TO_XKPHYS(idx << PAGE_SHIFT, CCA_CACHED);
			eva = sva + offs;
			switch (ci->ci_l1inst.sets) {
			default:
#ifdef CPU_RM7000
			case 4:
				while (sva != eva) {
					iva = sva;
					cache(IndexInvalidate_I, 0, iva);
					iva += offs;
					cache(IndexInvalidate_I, 0, iva);
					iva += offs;
					cache(IndexInvalidate_I, 0, iva);
					iva += offs;
					cache(IndexInvalidate_I, 0, iva);
					sva += R5K_LINE;
				}
				break;
#endif
#if defined(CPU_R5000) || defined(CPU_R4600)
			case 2:
				iva = sva + offs;
				while (sva != eva) {
					cache(IndexInvalidate_I, 0, iva);
					cache(IndexInvalidate_I, 0, sva);
					iva += R5K_LINE;
					sva += R5K_LINE;
				}
				break;
#endif
			}

			ci->ci_cachepending_l1i &= ~(1ULL << idx);
		}
	}

#ifdef CPU_R4600
	setsr(sr);
#endif
}

static __inline__ void
mips5k_hitwbinv_primary(vaddr_t va, vsize_t sz)
{
	vaddr_t eva;

	eva = va + sz;
	while (va != eva) {
		cache(HitWBInvalidate_D, 0, va);
		va += R5K_LINE;
	}
}

static __inline__ void
mips5k_hitwbinv_secondary(vaddr_t va, vsize_t sz)
{
	vaddr_t eva;

	eva = va + sz;
	while (va != eva) {
		cache(HitWBInvalidate_S, 0, va);
		cache(HitWBInvalidate_D, 0, va);	/* orphans in L1 */
		va += R5K_LINE;
	}
}

/*
 * Writeback D$ for the given page.
 */
void
Mips5k_SyncDCachePage(struct cpu_info *ci, vaddr_t va, paddr_t pa)
{
#ifdef CPU_R4600
	/*
	 * Revision 1 R4600 need to perform `Index' cache operations with
	 * interrupt disabled, to make sure both ways are correctly updated.
	 */
	register_t sr = disableintr();
#endif

	switch (ci->ci_l1data.sets) {
	default:
#ifdef CPU_RM7000
	case 4:
		/*
		 * On RM7000 and RM9000, the D$ cache set is never larger than
		 * the page size, causing it to behave as a physically-indexed
		 * cache. We can thus use Hit operations on the physical
		 * address.
		 */
		if ((ci->ci_cacheconfiguration & CTYPE_HAS_IL2) == 0) {
			mips5k_hitwbinv_primary(PHYS_TO_XKPHYS(pa, CCA_CACHED),
			    PAGE_SIZE);
		} /* else
			done below */
		break;
#endif
#if defined(CPU_R5000) || defined(CPU_R4600)
	case 2:
	    {
		vaddr_t sva, eva, iva;
		vsize_t offs;

		sva = PHYS_TO_XKPHYS(0, CCA_CACHED);
		offs = ci->ci_l1data.setsize;
		/* keep only the index bits */
		sva |= va & (offs - 1);
		eva = sva + PAGE_SIZE;

		iva = sva + offs;
		while (sva != eva) {
			cache(IndexWBInvalidate_D, 0, iva);
			cache(IndexWBInvalidate_D, 0, sva);
			iva += R5K_LINE;
			sva += R5K_LINE;
		}
	    }
		break;
#endif
	}

#ifdef CPU_R4600
	setsr(sr);
#endif

#ifdef CPU_RM7000
	if (ci->ci_cacheconfiguration & CTYPE_HAS_IL2) {
		mips5k_hitwbinv_secondary(PHYS_TO_XKPHYS(pa, CCA_CACHED),
		    PAGE_SIZE);
	}
#endif

	mips_sync();
}

/*
 * Writeback D$ for the given range. Range is expected to be currently
 * mapped, allowing the use of `Hit' operations. This is less aggressive
 * than using `Index' operations.
 */

void
Mips5k_HitSyncDCache(struct cpu_info *ci, vaddr_t _va, size_t _sz)
{
	vaddr_t va;
	vsize_t sz;

	/* extend the range to integral cache lines */
	va = _va & ~(R5K_LINE - 1);
	sz = ((_va + _sz + R5K_LINE - 1) & ~(R5K_LINE - 1)) - va;

	if (ci->ci_cacheconfiguration & CTYPE_HAS_IL2)
		mips5k_hitwbinv_secondary(va, sz);
	else {
#ifdef CPU_R4600
		/*
		 * R4600 revision 2 needs to load from an uncached address
		 * before any Hit or CreateDEX operation. Alternatively, 12
		 * nop (cycles) will empty the cache load buffer.
		 * We are only putting 10 here, and hope the overhead of the
		 * code around will provide the rest.
		 */
		nop10();
#endif
		mips5k_hitwbinv_primary(va, sz);
	}

	mips_sync();
}

/*
 * Invalidate D$ for the given range. Range is expected to be currently
 * mapped, allowing the use of `Hit' operations. This is less aggressive
 * than using `Index' operations.
 */

static __inline__ void
mips5k_hitinv_primary(vaddr_t va, vsize_t sz)
{
	vaddr_t eva;

	eva = va + sz;
	while (va != eva) {
		cache(HitInvalidate_D, 0, va);
		va += R5K_LINE;
	}
}

static __inline__ void
mips5k_hitinv_secondary(vaddr_t va, vsize_t sz)
{
	vaddr_t eva;

	eva = va + sz;
	while (va != eva) {
		cache(HitInvalidate_S, 0, va);
		cache(HitInvalidate_D, 0, va);	/* orphans in L1 */
		va += R5K_LINE;
	}
}

void
Mips5k_HitInvalidateDCache(struct cpu_info *ci, vaddr_t _va, size_t _sz)
{
	vaddr_t va;
	vsize_t sz;

	/* extend the range to integral cache lines */
	va = _va & ~(R5K_LINE - 1);
	sz = ((_va + _sz + R5K_LINE - 1) & ~(R5K_LINE - 1)) - va;

	if (ci->ci_cacheconfiguration & CTYPE_HAS_IL2)
		mips5k_hitinv_secondary(va, sz);
	else {
#ifdef CPU_R4600
		/*
		 * R4600 revision 2 needs to load from an uncached address
		 * before any Hit or CreateDEX operation. Alternatively, 12
		 * nop (cycles) will empty the cache load buffer.
		 * We are only putting 10 here, and hope the overhead of the
		 * code around will provide the rest.
		 */
		nop10();
#endif
		mips5k_hitinv_primary(va, sz);
	}

	mips_sync();
}

static __inline__ void
mips5k_hitwb_primary(vaddr_t va, vsize_t sz)
{
	vaddr_t eva;

	eva = va + sz;
	while (va != eva) {
		cache(HitWriteback_D, 0, va);
		va += R5K_LINE;
	}
}

#if 0
static __inline__ void
mips5k_hitwb_secondary(vaddr_t va, vsize_t sz)
{
	vaddr_t eva;

	eva = va + sz;
	while (va != eva) {
		cache(HitWriteback_S, 0, va);
		cache(HitWriteback_D, 0, va);	/* orphans in L1 */
		va += R5K_LINE;
	}
}
#endif

/*
 * Backend for bus_dmamap_sync(). Enforce coherency of the given range
 * by performing the necessary cache writeback and/or invalidate
 * operations.
 */
void
Mips5k_IOSyncDCache(struct cpu_info *ci, vaddr_t _va, size_t _sz, int how)
{
	vaddr_t va;
	vsize_t sz;
	int partial_start, partial_end;

	/*
	 * internal cache
	 */

	/* extend the range to integral cache lines */
	va = _va & ~(R5K_LINE - 1);
	sz = ((_va + _sz + R5K_LINE - 1) & ~(R5K_LINE - 1)) - va;

#ifdef CPU_R4600
	/*
	 * R4600 revision 2 needs to load from an uncached address
	 * before any Hit or CreateDEX operation. Alternatively, 12
	 * nop (cycles) will empty the cache load buffer.
	 * We are only putting 10 here, and hope the overhead of the
	 * code around will provide the rest.
	 */
	nop10();
#endif

	switch (how) {
	case CACHE_SYNC_R:
		/* writeback partial cachelines */
		if (((_va | _sz) & (R5K_LINE - 1)) != 0) {
			partial_start = va != _va;
			partial_end = va + sz != _va + _sz;
		} else {
			partial_start = partial_end = 0;
		}
		if (partial_start) {
#ifdef CPU_RM7000
			if (ci->ci_cacheconfiguration & CTYPE_HAS_IL2)
				cache(HitWBInvalidate_S, 0, va);
#endif
			cache(HitWBInvalidate_D, 0, va);
			va += R5K_LINE;
			sz -= R5K_LINE;
		}
		if (sz != 0 && partial_end) {
			sz -= R5K_LINE;
#ifdef CPU_RM7000
			if (ci->ci_cacheconfiguration & CTYPE_HAS_IL2)
				cache(HitWBInvalidate_S, 0, va + sz);
#endif
			cache(HitWBInvalidate_D, 0, va + sz);
		}

		if (sz != 0) {
#ifdef CPU_RM7000
			if (ci->ci_cacheconfiguration & CTYPE_HAS_IL2)
				mips5k_hitinv_secondary(va, sz);
			else
#endif
				mips5k_hitinv_primary(va, sz);
		}
		break;

	case CACHE_SYNC_W:
#ifdef CPU_RM7000
		if (ci->ci_cacheconfiguration & CTYPE_HAS_IL2)
			mips5k_hitwbinv_secondary(va, sz);	/* XXX */
		else
#endif
			mips5k_hitwb_primary(va, sz);
		break;

	case CACHE_SYNC_X:
#ifdef CPU_RM7000
		if (ci->ci_cacheconfiguration & CTYPE_HAS_IL2)
			mips5k_hitwbinv_secondary(va, sz);
		else
#endif
			mips5k_hitwbinv_primary(va, sz);
		break;
	}

	/*
	 * external cache
	 */

	switch (how) {
	case CACHE_SYNC_W:
		break;
	case CACHE_SYNC_X:
	case CACHE_SYNC_R:
#ifdef CPU_R5000
		if (ci->ci_cacheconfiguration & CTYPE_HAS_XL2) {
			/* align on external page size */
			va = _va & ~(R5K_PAGE - 1);
			sz = ((_va + _sz + R5K_PAGE - 1) - va) / R5K_PAGE;
			reset_taglo();
			while (sz != 0) {
				cache(InvalidatePage_S, 0, va);
				va += R5K_PAGE;
				sz--;
			}
		} else
#endif
#ifdef CPU_RM7000
		if (ci->ci_cacheconfiguration & CTYPE_HAS_XL3) {
			/* align on external page size */
			va = _va & ~(R5K_PAGE - 1);
			sz = ((_va + _sz + R5K_PAGE - 1) - va) / R5K_PAGE;
			reset_taglo();
			while (sz != 0) {
				cache(InvalidatePage_T, 0, va);
				va += R5K_PAGE;
				sz--;
			}
		} else
#endif
		{
		}
		break;
	}

	mips_sync();
}


code:
/*	$OpenBSD: cache_tfp.c,v 1.4 2014/03/31 20:21:19 miod Exp $	*/

/*
 * Copyright (c) 2012 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * R8000 cache routines.
 *
 * These routines only handle the L1 cache found onboard the R8000.
 * The L2 (Streaming Cache) cache handling is apparently quite different
 * accross R8000-based designs (well... the two of them: IP21 and IP26),
 * and is handled on a per-platform basis.
 */

#include <sys/param.h>
#include <sys/systm.h>

#include <mips64/cache.h>
#include <machine/cpu.h>

#include <uvm/uvm_extern.h>

void	tfp_dctw_zero(vaddr_t);
void	tfp_inval_icache(vsize_t);

#define	TFP_DCTW_STEP	16UL			/* 4 words per tag */

void
tfp_ConfigCache(struct cpu_info *ci)
{
	register_t cfg;

	cfg = cp0_get_config();

	/*
	 * XXX It would make sense to trust the configuration register,
	 * XXX but at least on my system it would report a 32KB I$, while
	 * XXX ARCS reports the architected 16KB.
	 * XXX Better not trust anything from the configuration register,
	 * XXX then.
	 */
#if 0
	ci->ci_l1inst.size = (1 << 11) << ((cfg >> 9) & 0x07); /* IC */
	if (cfg & (1 << 5))	/* IB */
		ci->ci_l1inst.linesize = 32;
	else
		ci->ci_l1inst.linesize = 16;

	ci->ci_l1data.size = (1 << 12) << ((cfg >> 6) & 0x07); /* DC */
	if (cfg & (1 << 4))	/* DB */
		ci->ci_l1data.linesize = 32;
	else
		ci->ci_l1data.linesize = 16;
#else
	ci->ci_l1inst.size = 16384;
	ci->ci_l1inst.linesize = 32;
	ci->ci_l1data.size = 16384;
	ci->ci_l1data.linesize = 32;
#endif

	/* R8000 L1 caches are direct */
	ci->ci_l1inst.setsize = ci->ci_l1inst.size;
	ci->ci_l1inst.sets = 1;
	ci->ci_l1data.setsize = ci->ci_l1data.size;
	ci->ci_l1data.sets = 1;

	cache_valias_mask =
	    (max(ci->ci_l1inst.size, ci->ci_l1data.size) - 1) &
	    ~PAGE_MASK;

	/* R8000 L2 cache are platform-specific, and not covered here */
	memset(&ci->ci_l2, 0, sizeof(struct cache_info));
	memset(&ci->ci_l3, 0, sizeof(struct cache_info));

	ci->ci_SyncCache = tfp_SyncCache;
	ci->ci_InvalidateICache = tfp_InvalidateICache;
	ci->ci_InvalidateICachePage = tfp_InvalidateICachePage;
	ci->ci_SyncICache = tfp_SyncICache;
	ci->ci_SyncDCachePage = tfp_SyncDCachePage;
	ci->ci_HitSyncDCache = tfp_HitSyncDCache;
	ci->ci_HitInvalidateDCache = tfp_HitInvalidateDCache;
	ci->ci_IOSyncDCache = tfp_IOSyncDCache;
}

/*
 * Writeback and invalidate all caches.
 */
void
tfp_SyncCache(struct cpu_info *ci)
{
	vaddr_t va, eva;
	register_t sr;

	tfp_InvalidateICache(ci, 0, ci->ci_l1inst.size);

	sr = disableintr();
	eva = ci->ci_l1data.size;
	for (va = 0; va < eva; va += TFP_DCTW_STEP)
		tfp_dctw_zero(va);
	setsr(sr);
}

/*
 * Invalidate I$ for the given range.
 */
void
tfp_InvalidateICache(struct cpu_info *ci, vaddr_t _va, size_t _sz)
{
	vaddr_t va;
	vsize_t sz;
	void (*inval_subr)(vsize_t);

	if (_sz >= ci->ci_l1inst.size) {
		tfp_inval_icache(ci->ci_l1inst.size);
	} else {
		/* extend the range to multiple of 32 bytes */
		va = _va & ~(32UL - 1);
		sz = ((_va + _sz + 32 - 1) & ~(32UL - 1)) - va;

		/* compute cache offset */
		va &= (ci->ci_l1inst.size - 1);
		inval_subr = (void (*)(vsize_t))
		    ((vaddr_t)tfp_inval_icache + va);
		(*inval_subr)(sz);
	}
}

/*
 * Register a given page for I$ invalidation.
 */
void
tfp_InvalidateICachePage(struct cpu_info *ci, vaddr_t va)
{
	/*
	 * Since the page size matches the size of the instruction cache,
	 * all we need to do here is remember there are postponed flushes.
	 */
	ci->ci_cachepending_l1i = 1;
}

/*
 * Perform postponed I$ invalidation.
 */
void
tfp_SyncICache(struct cpu_info *ci)
{
	if (ci->ci_cachepending_l1i != 0) {
		tfp_inval_icache(ci->ci_l1inst.size);
		ci->ci_cachepending_l1i = 0;
	}
}

/*
 * Writeback D$ for the given page.
 */
void
tfp_SyncDCachePage(struct cpu_info *ci, vaddr_t va, paddr_t pa)
{
	/* nothing to do, D$ is write-through */
}

/*
 * Writeback D$ for the given range.
 */
void
tfp_HitSyncDCache(struct cpu_info *ci, vaddr_t _va, size_t _sz)
{
	/* nothing to do, D$ is write-through */
}

/*
 * Invalidate D$ for the given range.
 */
void
tfp_HitInvalidateDCache(struct cpu_info *ci, vaddr_t _va, size_t _sz)
{
	vaddr_t va, eva;
	vsize_t sz;
	register_t sr;

	/* extend the range to multiples of the D$ tag span */
	va = _va & ~(TFP_DCTW_STEP - 1);
	sz = ((_va + _sz + TFP_DCTW_STEP - 1) & ~(TFP_DCTW_STEP - 1)) - va;

	sr = disableintr();
	for (eva = va + sz; va < eva; va += TFP_DCTW_STEP)
		tfp_dctw_zero(va);
	setsr(sr);
}

/*
 * Backend for bus_dmamap_sync(). Enforce coherency of the given range
 * by performing the necessary cache writeback and/or invalidate
 * operations.
 */
void
tfp_IOSyncDCache(struct cpu_info *ci, vaddr_t _va, size_t _sz, int how)
{
	switch (how) {
	case CACHE_SYNC_R:
	case CACHE_SYNC_X:
		tfp_HitInvalidateDCache(ci, _va, _sz);
		break;
	case CACHE_SYNC_W:
		/* nothing to do */
		break;
	}
}


code:
/*	$OpenBSD: cache_tfp_subr.S,v 1.2 2012/10/03 11:18:23 miod Exp $	*/

/*
 * Copyright (c) 2012 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <machine/param.h>
#include <machine/asm.h>
#include <machine/cpu.h>
#include <mips64/mips_cpu.h>

#include "assym.h"

	.set	mips4
	.set	noreorder

/*
 * The R8000 processor does not have documented cache maintainance
 * instructions. While this is not really a problem for D$, which is
 * write-through and physically tagged, this is a problem for I$,
 * which is virtually tagged.
 *
 * Since there is no way to invalidate I$, the best we can do is
 * override existing cache lines to evict them from the cache.
 *
 * The following routine will attempt to do so. It spans the whole I$
 * cache size, and may be invoked from any offset multiple of 32 bytes
 * (in case a smaller portion of the cache needs to be invalidated).
 *
 * void tfp_inval_icache(register_t size)
 */
	.align	14
LEAF(tfp_inval_icache, 0)						/* { */

#define	ICACHE_CHUNK_INVALIDATE \
	beqz	a0, 9f; \
	 NOP; \
	NOP; \
	NOP; \
	subu	a0, 32; \
	NOP; \
	NOP; \
	NOP

#define	ICACHE_256B_INVALIDATE \
	ICACHE_CHUNK_INVALIDATE; ICACHE_CHUNK_INVALIDATE; \
	ICACHE_CHUNK_INVALIDATE; ICACHE_CHUNK_INVALIDATE; \
	ICACHE_CHUNK_INVALIDATE; ICACHE_CHUNK_INVALIDATE; \
	ICACHE_CHUNK_INVALIDATE; ICACHE_CHUNK_INVALIDATE;

#define	ICACHE_2KB_INVALIDATE \
	ICACHE_256B_INVALIDATE; ICACHE_256B_INVALIDATE; \
	ICACHE_256B_INVALIDATE; ICACHE_256B_INVALIDATE; \
	ICACHE_256B_INVALIDATE; ICACHE_256B_INVALIDATE; \
	ICACHE_256B_INVALIDATE; ICACHE_256B_INVALIDATE

#define	ICACHE_16KB_INVALIDATE \
	ICACHE_2KB_INVALIDATE; ICACHE_2KB_INVALIDATE; \
	ICACHE_2KB_INVALIDATE; ICACHE_2KB_INVALIDATE; \
	ICACHE_2KB_INVALIDATE; ICACHE_2KB_INVALIDATE; \
	ICACHE_2KB_INVALIDATE; ICACHE_2KB_INVALIDATE

	ICACHE_16KB_INVALIDATE

	bnez	a0, tfp_inval_icache	/* wrap around */
	 NOP
9:
	j	ra
	 NOP
END(tfp_inval_icache)							/* } */

/*
 * Clear the D$ cache tag for the given address.
 * Interrupts must be disabled (to be free to alter the Vaddr register).
 */

#define	DCTW	.align 4; .word 0x4300000a; SSNOP; SSNOP; SSNOP

LEAF(tfp_dctw_zero, 0)							/* { */
	DMTC0	a0, COP_0_VADDR
	MTC0_HAZARD
	DMTC0	zero, COP_0_DCACHE
	MTC0_HAZARD
	DCTW
	j	ra
	 NOP
END(tfp_dctw_zero)							/* } */


code:
/*	$OpenBSD: clock.c,v 1.40 2015/05/01 11:17:22 miod Exp $ */

/*
 * Copyright (c) 2001-2004 Opsycon AB  (www.opsycon.se / www.opsycon.com)
 *
 * 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 AUTHOR ``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 AUTHOR 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.
 *
 */

/*
 * Clock code for systems using the on-cpu counter register, when both the
 * counter and comparator registers are available (i.e. everything MIPS-III
 * or MIPS-IV capable but the R8000).
 *
 * On most processors, this register counts at half the pipeline frequency.
 */

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/evcount.h>

#include <machine/autoconf.h>
#include <machine/cpu.h>
#include <mips64/mips_cpu.h>

#include <mips64/dev/clockvar.h>

static struct evcount cp0_clock_count;
static int cp0_clock_irq = 5;

int	clockmatch(struct device *, void *, void *);
void	clockattach(struct device *, struct device *, void *);

struct cfdriver clock_cd = {
	NULL, "clock", DV_DULL
};

struct cfattach clock_ca = {
	sizeof(struct device), clockmatch, clockattach
};

void	cp0_startclock(struct cpu_info *);
uint32_t cp0_int5(uint32_t, struct trap_frame *);

int
clockmatch(struct device *parent, void *vcf, void *aux)
{
	struct mainbus_attach_args *maa = aux;

#ifdef CPU_R8000
	return 0;	/* shouldn't be in the kernel configuration anyway */
#endif
	return strcmp(maa->maa_name, clock_cd.cd_name) == 0;
}

void
clockattach(struct device *parent, struct device *self, void *aux)
{
	printf(": int 5\n");

	/*
	 * We need to register the interrupt now, for idle_mask to
	 * be computed correctly.
	 */
	set_intr(INTPRI_CLOCK, CR_INT_5, cp0_int5);
	evcount_attach(&cp0_clock_count, "clock", &cp0_clock_irq);

	/* try to avoid getting clock interrupts early */
	cp0_set_compare(cp0_get_count() - 1);

	md_startclock = cp0_startclock;
}

/*
 *  Interrupt handler for targets using the internal count register
 *  as interval clock. Normally the system is run with the clock
 *  interrupt always enabled. Masking is done here and if the clock
 *  can not be run the tick is just counted and handled later when
 *  the clock is logically unmasked again.
 */
uint32_t
cp0_int5(uint32_t mask, struct trap_frame *tf)
{
	u_int32_t clkdiff;
	struct cpu_info *ci = curcpu();

	/*
	 * If we got an interrupt before we got ready to process it,
	 * retrigger it as far as possible. cpu_initclocks() will
	 * take care of retriggering it correctly.
	 */
	if (ci->ci_clock_started == 0) {
		cp0_set_compare(cp0_get_count() - 1);

		return CR_INT_5;
	}

	/*
	 * Count how many ticks have passed since the last clock interrupt...
	 */
	clkdiff = cp0_get_count() - ci->ci_cpu_counter_last;
	while (clkdiff >= ci->ci_cpu_counter_interval) {
		ci->ci_cpu_counter_last += ci->ci_cpu_counter_interval;
		clkdiff = cp0_get_count() - ci->ci_cpu_counter_last;
		ci->ci_pendingticks++;
	}
	ci->ci_pendingticks++;
	ci->ci_cpu_counter_last += ci->ci_cpu_counter_interval;

	/*
	 * Set up next tick, and check if it has just been hit; in this
	 * case count it and schedule one tick ahead.
	 */
	cp0_set_compare(ci->ci_cpu_counter_last);
	clkdiff = cp0_get_count() - ci->ci_cpu_counter_last;
	if ((int)clkdiff >= 0) {
		ci->ci_cpu_counter_last += ci->ci_cpu_counter_interval;
		ci->ci_pendingticks++;
		cp0_set_compare(ci->ci_cpu_counter_last);
	}

	/*
	 * Process clock interrupt unless it is currently masked.
	 */
	if (tf->ipl < IPL_CLOCK) {
#ifdef MULTIPROCESSOR
		register_t sr;

		sr = getsr();
		ENABLEIPI();
#endif
		while (ci->ci_pendingticks) {
			cp0_clock_count.ec_count++;
			hardclock(tf);
			ci->ci_pendingticks--;
		}
#ifdef MULTIPROCESSOR
		setsr(sr);
#endif
	}

	return CR_INT_5;	/* Clock is always on 5 */
}

/*
 * Start the real-time and statistics clocks. Leave stathz 0 since there
 * are no other timers available.
 */
void
cp0_startclock(struct cpu_info *ci)
{
	int s;

#ifdef MULTIPROCESSOR
	if (!CPU_IS_PRIMARY(ci)) {
		s = splhigh();
		nanouptime(&ci->ci_schedstate.spc_runtime);
		splx(s);

		/* try to avoid getting clock interrupts early */
		cp0_set_compare(cp0_get_count() - 1);

		cp0_calibrate(ci);
	}
#endif

	/* Start the clock. */
	s = splclock();
	ci->ci_cpu_counter_interval =
	    (ci->ci_hw.clock / CP0_CYCLE_DIVIDER) / hz;
	ci->ci_cpu_counter_last = cp0_get_count() + ci->ci_cpu_counter_interval;
	cp0_set_compare(ci->ci_cpu_counter_last);
	ci->ci_clock_started++;
	splx(s);
}


code:
/*	$OpenBSD: context.S,v 1.53 2014/03/22 00:01:04 miod Exp $ */

/*
 * Copyright (c) 2002-2003 Opsycon AB  (www.opsycon.se / www.opsycon.com)
 *
 * 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 AUTHOR ``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 AUTHOR 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.
 *
 */
#include <sys/errno.h>
#include <sys/syscall.h>

#include <machine/param.h>
#include <machine/asm.h>
#include <machine/cpu.h>
#include <machine/pte.h>
#include <machine/regnum.h>
#include <mips64/mips_cpu.h>
#include <machine/cpustate.h>
#ifdef CPU_LOONGSON2
#include <machine/loongson2.h>
#endif

#include "assym.h"

#ifdef MIPS_PTE64
#define	PTE_LOG		3
#define	PTE_LOAD	ld
#define	PTE_OFFS	8
#else
#define	PTE_LOG		2
#define	PTE_LOAD	lw
#define	PTE_OFFS	4
#endif

	.set	mips3
	.set	noreorder		# Noreorder is default style!

/*
 * Save registers and state used by reboot to take snapshot.
 */
LEAF(savectx, 0)
	REG_S	s0, PCB_CONTEXT+0*REGSZ(a0)
	REG_S	s1, PCB_CONTEXT+1*REGSZ(a0)
	REG_S	s2, PCB_CONTEXT+2*REGSZ(a0)
	REG_S	s3, PCB_CONTEXT+3*REGSZ(a0)
	MFC0	v0, COP_0_STATUS_REG
	MFC0_HAZARD
	REG_S	s4, PCB_CONTEXT+4*REGSZ(a0)
	REG_S	s5, PCB_CONTEXT+5*REGSZ(a0)
	REG_S	s6, PCB_CONTEXT+6*REGSZ(a0)
	REG_S	s7, PCB_CONTEXT+7*REGSZ(a0)
	REG_S	sp, PCB_CONTEXT+8*REGSZ(a0)
	REG_S	s8, PCB_CONTEXT+9*REGSZ(a0)
	REG_S	ra, PCB_CONTEXT+10*REGSZ(a0)
	REG_S	v0, PCB_CONTEXT+11*REGSZ(a0)
#ifdef RM7000_ICR
	cfc0	t1, COP_0_ICR
	REG_S	t1, PCB_CONTEXT+12*REGSZ(a0)	# save status register
#endif
	j	ra
	 move	v0, zero
END(savectx)

LEAF(cpu_idle_enter, 0)
	j	ra
	 NOP
END(cpu_idle_enter)

LEAF(cpu_idle_leave, 0)
	j	ra
	 NOP
END(cpu_idle_leave)

LEAF(cpu_idle_cycle, 0)
	j	ra
	 NOP
END(cpu_idle_cycle)

/*
 * cpu_switchto_asm(struct proc *oldproc, struct proc *newproc)
 */
NON_LEAF(cpu_switchto_asm, FRAMESZ(CF_SZ), ra)
	GET_CPU_INFO(t1, t3)
	PTR_L	t3, CI_CURPROCPADDR(t1)
	REG_S	sp, PCB_CONTEXT+8*REGSZ(t3)	# save old sp

	PTR_SUBU sp, sp, FRAMESZ(CF_SZ)
	REG_S	ra, CF_RA_OFFS(sp)
	.mask	0x80000000, (CF_RA_OFFS - FRAMESZ(CF_SZ))

#ifdef CPU_R8000	/* XXX everywhere where MFC0_HAZARD is not empty */
	MFC0	v0, COP_0_STATUS_REG
	MFC0_HAZARD
	beqz	a0, 1f
	 NOP
#else
	beqz	a0, 1f
	 MFC0	v0, COP_0_STATUS_REG
#endif

	REG_S	s0, PCB_CONTEXT+0*REGSZ(t3)	# do a 'savectx()'
	REG_S	s1, PCB_CONTEXT+1*REGSZ(t3)
	REG_S	s2, PCB_CONTEXT+2*REGSZ(t3)
	REG_S	s3, PCB_CONTEXT+3*REGSZ(t3)
	REG_S	s4, PCB_CONTEXT+4*REGSZ(t3)
	REG_S	s5, PCB_CONTEXT+5*REGSZ(t3)
	REG_S	s6, PCB_CONTEXT+6*REGSZ(t3)
	REG_S	s7, PCB_CONTEXT+7*REGSZ(t3)
	REG_S	s8, PCB_CONTEXT+9*REGSZ(t3)
	REG_S	ra, PCB_CONTEXT+10*REGSZ(t3)
	REG_S	v0, PCB_CONTEXT+11*REGSZ(t3)
#ifdef RM7000_ICR
	cfc0	t1, COP_0_ICR
	REG_S	t1, PCB_CONTEXT+12*REGSZ(t3)
#endif

1:
	/*
	 * Disable interrupts
	 */
	ori	v0, SR_INT_ENAB
	xori	v0, SR_INT_ENAB
	MTC0	v0, COP_0_STATUS_REG
	MTC0_SR_IE_HAZARD

	/*
	 * Switch to new context
 	*/
	move	s0, a1				# save p
	jal	pmap_activate
	move	a0, s0

	PTR_L	t3, P_ADDR(s0)			# get uarea pointer.
	GET_CPU_INFO(t1, t0)
	PTR_S	s0, CI_CURPROC(t1)		# set curproc
	PTR_S	t3, CI_CURPROCPADDR(t1)

#ifdef MULTIPROCESSOR
	PTR_S	t1, P_CPU(s0)
#endif
	li	t1, SONPROC
	sb	t1, P_STAT(s0)			# set to onproc.

	/* get process ASID */
	PTR_L	t0, P_VMSPACE(s0)		# p->p_vmspace
	PTR_L	t1, VMSPACE_PMAP(t0)		# ->vm_map.pmap
#ifdef MULTIPROCESSOR
	GET_CPU_INFO(v0, t2)
	PTR_L	v0, CI_CPUID(v0)
	PTR_SLL	v0, v0, 0x3			# size of pmap_asid_info
	PTR_ADDU t1, t1, v0
#endif
	lw	v0, PM_ASID(t1)			# ->pm_asid[cpuid].pma_asid
#ifdef CPU_R8000
	PTR_L	t0, PCB_SEGTAB(t3)		# save pcb_segtab for
	DMTC0	t0, COP_0_UBASE			# Utlbmiss handler
	MTC0_HAZARD
#endif

#ifdef CPU_R4000
	/*
	 * Restore the number of wired TLB entries to the minimal possible
	 * value, in case the EOP errata workaround has caused more wired
	 * entries to be created.
	 */
	lw	a0, PCB_NWIRED(t3)
	beqz	a0, 1f
	sw	zero, PCB_NWIRED(t3)

	li	a1, UPAGES / 2
	move	s0, v0				# save asid
	mtc0	a1, COP_0_TLB_WIRED
	jal	tlb_flush			# clear formerly wired entries
	addu	a0, a1, a0
	move	v0, s0
1:
#endif

#if UPAGES > 1	/* { */
#ifdef CPU_R8000
#error Your processor has just been eaten by a grue.
#endif
	or	v0, t3
	dmtc0	v0, COP_0_TLB_HI		# init high entry (tlbid)

	/*
	 * We need to wire the process kernel stack mapping so there
	 * will be no tlb misses in exception handlers. This is done
	 * by invalidating any tlb entries mapping the U-area and
	 * put valid mappings in tlb entries 0 and 1.
	 */

	LA	t1, CKSEG0_BASE
	PTR_SUBU t2, t3, t1
	bgez	t2, ctx3			# in CKSEG0
	LA	t1, VM_MIN_KERNEL_ADDRESS	# (safe if expands to > 1 insn)
	PTR_SUBU t2, t3, t1
	bltz	t2, ctx3			# not mapped.
	PTR_SRL	t2, PGSHIFT+1
	PTR_L	t1, Sysmap
	TLB_HAZARD
	tlbp
	TLB_HAZARD			# necessary?
	PTR_SLL	t2, PTE_LOG + 1
	PTR_ADDU t1, t2				# t1 now points at ptes.
	mfc0	t0, COP_0_TLB_INDEX
	nop
	bltz	t0, ctx1			# not in tlb
	LA	t2, CKSEG0_BASE			# safe if expands to > 1 insn

	dmtc0	t2, COP_0_TLB_HI		# invalidate it.
	dmtc0	zero, COP_0_TLB_LO0
	dmtc0	zero, COP_0_TLB_LO1
	TLB_HAZARD
	tlbwi
	TLB_HAZARD

ctx1:
	mtc0	zero, COP_0_TLB_INDEX
	dmtc0	v0, COP_0_TLB_HI
	PTE_LOAD ta0, 0(t1)
	PTE_LOAD ta1, PTE_OFFS(t1)
	dsll	ta0, ta0, (64 - PG_FRAMEBITS)	# clear bits left of PG_FRAME
	dsrl	ta0, ta0, (64 - PG_FRAMEBITS)
	dsll	ta1, ta1, (64 - PG_FRAMEBITS)
	dsrl	ta1, ta1, (64 - PG_FRAMEBITS)
	dmtc0	ta0, COP_0_TLB_LO0
	dmtc0	ta1, COP_0_TLB_LO1
	PTR_ADDU v0, 2*PAGE_SIZE
	TLB_HAZARD
	tlbwi
	TLB_HAZARD

#if UPAGES > 2	/* { */
	dmtc0	v0, COP_0_TLB_HI		# init high entry (tlbid)
	PTE_LOAD ta0, (2*PTE_OFFS)(t1)
	PTE_LOAD ta1, (3*PTE_OFFS)(t1)
	dsll	ta0, ta0, (64 - PG_FRAMEBITS)	# clear bits left of PG_FRAME
	dsrl	ta0, ta0, (64 - PG_FRAMEBITS)
	TLB_HAZARD
	tlbp
	TLB_HAZARD			# necessary?
	dsll	ta1, ta1, (64 - PG_FRAMEBITS)
	dsrl	ta1, ta1, (64 - PG_FRAMEBITS)
	mfc0	t0, COP_0_TLB_INDEX
	nop
	bltz	t0, ctx2			# not in tlb
	li	t2, 1

	dmtc0	t2, COP_0_TLB_HI		# invalidate it.
	dmtc0	zero, COP_0_TLB_LO0
	dmtc0	zero, COP_0_TLB_LO1
	TLB_HAZARD
	tlbwi
	TLB_HAZARD

ctx2:
	mtc0	t2, COP_0_TLB_INDEX
	dmtc0	v0, COP_0_TLB_HI
	dmtc0	ta0, COP_0_TLB_LO0
	dmtc0	ta1, COP_0_TLB_LO1
	TLB_HAZARD
	tlbwi
	TLB_HAZARD
#endif	/* } UPAGES > 2 */
ctx3:
#else	/* } UPAGES > 1 { */
#if PG_ASID_SHIFT != 0
	dsll	v0, PG_ASID_SHIFT
#endif
	dmtc0	v0, COP_0_TLB_HI		# init high entry (tlbid)
#endif	/* } UPAGES > 1 */

#ifdef CPU_LOONGSON2
	li	v0, COP_0_DIAG_ITLB_CLEAR | COP_0_DIAG_BTB_CLEAR | COP_0_DIAG_RAS_DISABLE
	dmtc0	v0, COP_0_DIAG
#endif

	/*
	 * Restore registers and return.
	 */

	REG_L	s0, PCB_CONTEXT+0*REGSZ(t3)
	REG_L	s1, PCB_CONTEXT+1*REGSZ(t3)
	REG_L	s2, PCB_CONTEXT+2*REGSZ(t3)
	REG_L	s3, PCB_CONTEXT+3*REGSZ(t3)
	REG_L	s4, PCB_CONTEXT+4*REGSZ(t3)
	REG_L	s5, PCB_CONTEXT+5*REGSZ(t3)
	REG_L	s6, PCB_CONTEXT+6*REGSZ(t3)
	REG_L	s7, PCB_CONTEXT+7*REGSZ(t3)
	REG_L	sp, PCB_CONTEXT+8*REGSZ(t3)
	REG_L	s8, PCB_CONTEXT+9*REGSZ(t3)
	REG_L	ra, PCB_CONTEXT+10*REGSZ(t3)
	REG_L	v0, PCB_CONTEXT+11*REGSZ(t3)
#ifdef RM7000_ICR
	REG_L	v1, PCB_CONTEXT+12*REGSZ(t3)
	ctc0	v1, COP_0_ICR		# XXX RM7000
#endif
	ori	v0, v0, SR_INT_ENAB
	MTC0	v0, COP_0_STATUS_REG
	MTC0_SR_IE_HAZARD
	j	ra
	 NOP
END(cpu_switchto_asm)

/*-------------------------------------------------------------- proc_trampoline
 *	Setup for and return to user.
 */
LEAF(proc_trampoline, 0)
#ifdef DDB
	move	zero, ra
#endif
#ifdef MULTIPROCESSOR
	jal	_C_LABEL(proc_trampoline_mp)
	 NOP
#endif
	/*
	 * Enable interrupts, since we want kernel threads to
	 * start at spl0 and with interrupts enabled, and these
	 * won't ``return to userland''.
	 */
#ifndef MULTIPROCESSOR	/* done by proc_trampoline_mp() */
	jal	splx
	 xor	a0, a0
#endif
	jal	updateimask		# Make sure SR imask is updated
	 xor	a0, a0			# and interrupts enabled

	jal	s0
	 move	a0,s1			# invoke callback.

#if 0	/* && defined(RM7000_ICR) */
	lw	t0, cpu_is_rm7k
	beqz	t0, 1f			# not an RM7K. Don't do IC reg.
	 NOP

	GET_CPU_INFO(t1, t0)
	PTR_L	t0, CI_CURPROC(t1)	# set up rm7k.
	ld	v0, P_WATCH_1(t0)
	dmtc0	v0, COP_0_WATCH_1
	ld	v0, P_WATCH_2(t0)
	dmtc0	v0, COP_0_WATCH_2
	lw	v0, P_WATCH_M(t0)
	mtc0	v0, COP_0_WATCH_M
	lw	v0, P_PC_CTRL(t0)
	lw	v1, P_PC_COUNT(t0)
	nop;nop
	mtc0	v0, COP_0_PC_CTRL
	nop;nop;nop;nop
	mtc0	v1, COP_0_PC_COUNT
	nop;nop;nop;nop
	li	v0, IC_INT_PERF
	ctc0	v0, COP_0_ICR		# enable perfcntr interrupt.
1:
#endif

	MFC0	t0, COP_0_STATUS_REG
	MFC0_HAZARD
	LI	t1, ~SR_INT_ENAB
	and	t0, t0, t1
	MTC0	t0, COP_0_STATUS_REG
	MTC0_SR_IE_HAZARD

	ori	t0, SR_EXL		# restoring to user mode.
	MTC0	t0, COP_0_STATUS_REG	# must set exception level bit.
	MTC0_SR_IE_HAZARD

	.set	noat
	GET_CPU_INFO(k1, k0)
	PTR_L	k0, CI_CURPROCPADDR(k1)
	RESTORE_CPU_SREG(k0, 0)
	RESTORE_REG(a0, PC, k0, 0)
#ifdef RM7000_ICR
	RESTORE_REG(t0, IC, k0, 0)
	ctc0	t0, COP_0_ICR
#endif
	RESTORE_CPU(k0, 0)
	RESTORE_REG(sp, SP, k0, 0)
	LI	k0, 0
	LI	k1, 0
	ERET
	.set	at
END(proc_trampoline)


code:
/*	$OpenBSD: cp0access.S,v 1.19 2012/10/03 11:18:23 miod Exp $ */

/*
 * Copyright (c) 2001-2003 Opsycon AB  (www.opsycon.se / www.opsycon.com)
 *
 * 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 AUTHOR ``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 AUTHOR 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.
 *
 */

/*
 *  Low level code to manage processor specific registers.
 */

#include <sys/errno.h>
#include <sys/syscall.h>

#include <machine/param.h>
#include <machine/asm.h>
#include <machine/cpu.h>
#include <mips64/mips_cpu.h>
#include <machine/regnum.h>

#include "assym.h"

	.set	mips3
	.set	noreorder		# Noreorder is default style!

/*
 * Set/clear software interrupt.
 */

LEAF(setsoftintr0, 0)
	MFC0	v0, COP_0_CAUSE_REG	# read cause register
	MFC0_HAZARD
	NOP
	or	v0, v0, SOFT_INT_MASK_0	# set soft clock interrupt
	MTC0	v0, COP_0_CAUSE_REG	# save it
	MTC0_HAZARD
	j	ra
	 NOP
END(setsoftintr0)

LEAF(clearsoftintr0, 0)
	MFC0	v0, COP_0_CAUSE_REG	# read cause register
	MFC0_HAZARD
	NOP
	and	v0, v0, ~SOFT_INT_MASK_0	# clear soft clock interrupt
	MTC0	v0, COP_0_CAUSE_REG	# save it
	MTC0_HAZARD
	j	ra
	 NOP
END(clearsoftintr0)

LEAF(setsoftintr1, 0)
	MFC0	v0, COP_0_CAUSE_REG	# read cause register
	MFC0_HAZARD
	NOP
	or	v0, v0, SOFT_INT_MASK_1	# set soft net interrupt
	MTC0	v0, COP_0_CAUSE_REG	# save it
	MTC0_HAZARD
	j	ra
	 NOP
END(setsoftintr1)

LEAF(clearsoftintr1, 0)
	MFC0	v0, COP_0_CAUSE_REG	# read cause register
	MFC0_HAZARD
	NOP
	and	v0, v0, ~SOFT_INT_MASK_1	# clear soft net interrupt
	MTC0	v0, COP_0_CAUSE_REG	# save it
	MTC0_HAZARD
	j	ra
	 NOP
END(clearsoftintr1)

/*
 * Set/change interrupt priority routines.
 * These routines return the previous state.
 */

LEAF(enableintr, 0)
	MFC0	v0, COP_0_STATUS_REG	# read status register
	MFC0_HAZARD
	NOP
	or	v1, v0, SR_INT_ENAB
	MTC0	v1, COP_0_STATUS_REG	# enable all interrupts
	MTC0_SR_IE_HAZARD
	j	ra
	 NOP
END(enableintr)

LEAF(disableintr, 0)
	MFC0	v0, COP_0_STATUS_REG	# read status register
	MFC0_HAZARD
	NOP
	and	v1, v0, ~SR_INT_ENAB
	MTC0	v1, COP_0_STATUS_REG	# disable all interrupts
	MTC0_SR_IE_HAZARD
	j	ra
	 NOP
END(disableintr)

LEAF(updateimask, 0)
	lw	t0, idle_mask
	not	a0, a0			# 1 means masked so invert.
	and	a0, t0			# never upgrade to higher than max
#ifdef RM7000_ICR
	cfc0	v0, COP_0_ICR
	li	v1, ~IC_INT_MASK
	and	v1, v0
	sll	v0, a0, 8
	and	v0, IC_INT_MASK
	and	v0, a0, IC_INT_MASK
	or	v1, v0
	ctc0	v1, COP_0_ICR
#endif
	MFC0	v0, COP_0_STATUS_REG
	MFC0_HAZARD
	LI	v1, ~SR_INT_MASK
	and	v1, v0
	and	v0, a0, SR_INT_MASK
	or	v1, v0
	MTC0	v1, COP_0_STATUS_REG
	MTC0_SR_IE_HAZARD
	ori	v1, SR_INT_ENAB		# enable interrupts
	MTC0	v1, COP_0_STATUS_REG
	MTC0_SR_IE_HAZARD
	jr	ra
	 move	v0, v1
END(updateimask)

LEAF(setsr, 0)
	MTC0	a0, COP_0_STATUS_REG
	MTC0_SR_IE_HAZARD
	jr	ra
	 move	v0, a0
END(setsr)

LEAF(getsr, 0)
	MFC0	v0, COP_0_STATUS_REG
	MFC0_HAZARD
	j	ra
	 NOP
END(getsr)

LEAF(cp0_get_config, 0)
	MFC0	v0, COP_0_CONFIG
	MFC0_HAZARD
	j	ra
	 NOP
END(cp0_get_config)

/* WARNING!
   Needs to be invoked from uncached address if changing cache settings. */
LEAF(cp0_set_config, 0)
	MTC0	a0, COP_0_CONFIG
	MTC0_HAZARD
	j	ra
	 NOP
END(cp0_set_config)

LEAF(cp0_get_prid, 0)
	MFC0	v0, COP_0_PRID
	MFC0_HAZARD
	j	ra
	NOP
END(cp0_get_prid)

LEAF(cp0_get_count, 0)
	MFC0	v0, COP_0_COUNT
	MFC0_HAZARD
	j	ra
	NOP
END(cp0_get_count)

#ifndef CPU_R8000
LEAF(cp0_set_compare, 0)
	MTC0	a0, COP_0_COMPARE
	MTC0_HAZARD
	j	ra
	NOP
END(cp0_set_compare)
#endif

#ifdef CPU_R8000
LEAF(cp0_reset_cause, 0)
	DMFC0	v0, COP_0_CAUSE_REG
	MFC0_HAZARD
	or	v0, a0
	xor	v0, a0
	DMTC0	v0, COP_0_CAUSE_REG
	MTC0_HAZARD
	j	ra
	 NOP
END(cp0_reset_cause)

LEAF(cp0_set_trapbase, 0)
	DMTC0	a0, COP_0_TRAPBASE
	MTC0_HAZARD
	j	ra
	 NOP
END(cp0_set_trapbase)
#endif

#ifdef notused
LEAF(cp0_getperfcount, 0)
	MFC0	v0, COP_0_PC_COUNT
	MFC0_HAZARD
	j	ra
	NOP
END(cp0_getperfcount)

LEAF(cp0_setperfcount, 0)
	MTC0	a0, COP_0_PC_COUNT
	MTC0_HAZARD
	j	ra
	NOP
END(cp0_setperfcount)

LEAF(cp0_setperfctrl, 0)
	MTC0	a0, COP_0_PC_CTRL
	MTC0_HAZARD
	j	ra
	NOP
END(cp0_setperfctrl)
#endif