code:
/*	$OpenBSD: cpufunc.c,v 1.27 2015/05/29 05:48:07 jsg Exp $	*/
/*	$NetBSD: cpufunc.c,v 1.65 2003/11/05 12:53:15 scw Exp $	*/

/*
 * arm7tdmi support code Copyright (c) 2001 John Fremlin
 * arm8 support code Copyright (c) 1997 ARM Limited
 * arm8 support code Copyright (c) 1997 Causality Limited
 * arm9 support code Copyright (C) 2001 ARM Ltd
 * Copyright (c) 1997 Mark Brinicombe.
 * Copyright (c) 1997 Causality Limited
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Causality Limited.
 * 4. The name of Causality Limited may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY CAUSALITY LIMITED ``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 CAUSALITY LIMITED 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.
 *
 * RiscBSD kernel project
 *
 * cpufuncs.c
 *
 * C functions for supporting CPU / MMU / TLB specific operations.
 *
 * Created      : 30/01/97
 */

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

#include <uvm/uvm_extern.h>

#include <machine/pmap.h>
#include <arm/cpuconf.h>
#include <arm/cpufunc.h>

#ifdef CPU_XSCALE_80200
#include <arm/xscale/i80200reg.h>
#include <arm/xscale/i80200var.h>
#endif

#ifdef CPU_XSCALE_80321
#include <arm/xscale/i80321reg.h>
#include <arm/xscale/i80321var.h>
#endif

#ifdef CPU_XSCALE_IXP425
#include <arm/xscale/ixp425reg.h>
#include <arm/xscale/ixp425var.h>
#endif

#if defined(CPU_XSCALE_80200) || defined(CPU_XSCALE_80321)
#include <arm/xscale/xscalereg.h>
#endif

#if defined(PERFCTRS)
struct arm_pmc_funcs *arm_pmc;
#endif

/* PRIMARY CACHE VARIABLES */
int	arm_picache_size;
int	arm_picache_line_size;
int	arm_picache_ways;

int	arm_pdcache_size;	/* and unified */
int	arm_pdcache_line_size;
int	arm_pdcache_ways;

int	arm_pcache_type;
int	arm_pcache_unified;

int	arm_dcache_align;
int	arm_dcache_align_mask;

/* 1 == use cpu_sleep(), 0 == don't */
int cpu_do_powersave;

#ifdef CPU_ARM8
struct cpu_functions arm8_cpufuncs = {
	/* CPU functions */

	cpufunc_id,			/* id			*/
	cpufunc_nullop,			/* cpwait		*/

	/* MMU functions */

	cpufunc_control,		/* control		*/
	cpufunc_domains,		/* domain		*/
	arm8_setttb,			/* setttb		*/
	cpufunc_dfsr,			/* dfsr			*/
	cpufunc_dfar,			/* dfar			*/
	cpufunc_ifsr,			/* ifsr			*/
	cpufunc_ifar,			/* ifar			*/

	/* TLB functions */

	arm8_tlb_flushID,		/* tlb_flushID		*/
	arm8_tlb_flushID_SE,		/* tlb_flushID_SE	*/
	arm8_tlb_flushID,		/* tlb_flushI		*/
	arm8_tlb_flushID_SE,		/* tlb_flushI_SE	*/
	arm8_tlb_flushID,		/* tlb_flushD		*/
	arm8_tlb_flushID_SE,		/* tlb_flushD_SE	*/

	/* Cache operations */

	cpufunc_nullop,			/* icache_sync_all	*/
	(void *)cpufunc_nullop,		/* icache_sync_range	*/

	arm8_cache_purgeID,		/* dcache_wbinv_all	*/
	(void *)arm8_cache_purgeID,	/* dcache_wbinv_range	*/
/*XXX*/	(void *)arm8_cache_purgeID,	/* dcache_inv_range	*/
	(void *)arm8_cache_cleanID,	/* dcache_wb_range	*/

	arm8_cache_purgeID,		/* idcache_wbinv_all	*/
	(void *)arm8_cache_purgeID,	/* idcache_wbinv_range	*/

	cpufunc_nullop,			/* sdcache_wbinv_all	*/
	(void *)cpufunc_nullop,		/* sdcache_wbinv_range	*/
	(void *)cpufunc_nullop,		/* sdcache_inv_range	*/
	(void *)cpufunc_nullop,		/* sdcache_wb_range	*/

	/* Other functions */

	cpufunc_nullop,			/* flush_prefetchbuf	*/
	cpufunc_nullop,			/* drain_writebuf	*/

	(void *)cpufunc_nullop,		/* sleep		*/

	/* Soft functions */
	arm8_context_switch,		/* context_switch	*/
	arm8_setup			/* cpu setup		*/
};
#endif	/* CPU_ARM8 */

#ifdef CPU_ARM9
struct cpu_functions arm9_cpufuncs = {
	/* CPU functions */

	cpufunc_id,			/* id			*/
	cpufunc_nullop,			/* cpwait		*/

	/* MMU functions */

	cpufunc_control,		/* control		*/
	cpufunc_domains,		/* Domain		*/
	arm9_setttb,			/* Setttb		*/
	cpufunc_dfsr,			/* dfsr			*/
	cpufunc_dfar,			/* dfar			*/
	cpufunc_ifsr,			/* ifsr			*/
	cpufunc_ifar,			/* ifar			*/

	/* TLB functions */

	armv4_tlb_flushID,		/* tlb_flushID		*/
	arm9_tlb_flushID_SE,		/* tlb_flushID_SE	*/
	armv4_tlb_flushI,		/* tlb_flushI		*/
	(void *)armv4_tlb_flushI,	/* tlb_flushI_SE	*/
	armv4_tlb_flushD,		/* tlb_flushD		*/
	armv4_tlb_flushD_SE,		/* tlb_flushD_SE	*/

	/* Cache operations */

	arm9_icache_sync_all,		/* icache_sync_all	*/
	arm9_icache_sync_range,		/* icache_sync_range	*/

		/* ...cache in write-though mode... */
	arm9_dcache_wbinv_all,		/* dcache_wbinv_all	*/
	arm9_dcache_wbinv_range,	/* dcache_wbinv_range	*/
	arm9_dcache_wbinv_range,	/* dcache_inv_range	*/
	arm9_dcache_wb_range,		/* dcache_wb_range	*/

	arm9_idcache_wbinv_all,		/* idcache_wbinv_all	*/
	arm9_idcache_wbinv_range,	/* idcache_wbinv_range	*/

	cpufunc_nullop,			/* sdcache_wbinv_all	*/
	(void *)cpufunc_nullop,		/* sdcache_wbinv_range	*/
	(void *)cpufunc_nullop,		/* sdcache_inv_range	*/
	(void *)cpufunc_nullop,		/* sdcache_wb_range	*/

	/* Other functions */

	cpufunc_nullop,			/* flush_prefetchbuf	*/
	armv4_drain_writebuf,		/* drain_writebuf	*/

	(void *)cpufunc_nullop,		/* sleep		*/

	/* Soft functions */
	arm9_context_switch,		/* context_switch	*/
	arm9_setup			/* cpu setup		*/
};
#endif /* CPU_ARM9 */

#if defined(CPU_ARM9E) || defined(CPU_ARM10)
struct cpu_functions armv5_ec_cpufuncs = {
	/* CPU functions */

	cpufunc_id,			/* id			*/
	cpufunc_nullop,			/* cpwait		*/

	/* MMU functions */

	cpufunc_control,		/* control		*/
	cpufunc_domains,		/* Domain		*/
	armv5_ec_setttb,		/* Setttb		*/
	cpufunc_dfsr,			/* dfsr			*/
	cpufunc_dfar,			/* dfar			*/
	cpufunc_ifsr,			/* ifsr			*/
	cpufunc_ifar,			/* ifar			*/

	/* TLB functions */

	armv4_tlb_flushID,		/* tlb_flushID		*/
	arm10_tlb_flushID_SE,		/* tlb_flushID_SE	*/
	armv4_tlb_flushI,		/* tlb_flushI		*/
	arm10_tlb_flushI_SE,		/* tlb_flushI_SE	*/
	armv4_tlb_flushD,		/* tlb_flushD		*/
	armv4_tlb_flushD_SE,		/* tlb_flushD_SE	*/

	/* Cache operations */

	armv5_ec_icache_sync_all,	/* icache_sync_all	*/
	armv5_ec_icache_sync_range,	/* icache_sync_range	*/

		/* ...cache in write-though mode... */
	armv5_ec_dcache_wbinv_all,	/* dcache_wbinv_all	*/
	armv5_ec_dcache_wbinv_range,	/* dcache_wbinv_range	*/
	armv5_ec_dcache_wbinv_range,	/* dcache_inv_range	*/
	armv5_ec_dcache_wb_range,	/* dcache_wb_range	*/

	armv5_ec_idcache_wbinv_all,	/* idcache_wbinv_all	*/
	armv5_ec_idcache_wbinv_range,	/* idcache_wbinv_range	*/

	cpufunc_nullop,			/* sdcache_wbinv_all	*/
	(void *)cpufunc_nullop,		/* sdcache_wbinv_range	*/
	(void *)cpufunc_nullop,		/* sdcache_inv_range	*/
	(void *)cpufunc_nullop,		/* sdcache_wb_range	*/

	/* Other functions */

	cpufunc_nullop,			/* flush_prefetchbuf	*/
	armv4_drain_writebuf,		/* drain_writebuf	*/

	(void *)cpufunc_nullop,		/* sleep		*/

	/* Soft functions */
	arm10_context_switch,		/* context_switch	*/
	arm9e_setup			/* cpu setup		*/
};
#endif /* CPU_ARM9E || CPU_ARM10 */

#ifdef CPU_ARM10
struct cpu_functions arm10_cpufuncs = {
	/* CPU functions */

	cpufunc_id,			/* id			*/
	cpufunc_nullop,			/* cpwait		*/

	/* MMU functions */

	cpufunc_control,		/* control		*/
	cpufunc_domains,		/* Domain		*/
	armv5_setttb,			/* Setttb		*/
	cpufunc_dfsr,			/* dfsr			*/
	cpufunc_dfar,			/* dfar			*/
	cpufunc_ifsr,			/* ifsr			*/
	cpufunc_ifar,			/* ifar			*/

	/* TLB functions */

	armv4_tlb_flushID,		/* tlb_flushID		*/
	arm10_tlb_flushID_SE,		/* tlb_flushID_SE	*/
	armv4_tlb_flushI,		/* tlb_flushI		*/
	arm10_tlb_flushI_SE,		/* tlb_flushI_SE	*/
	armv4_tlb_flushD,		/* tlb_flushD		*/
	armv4_tlb_flushD_SE,		/* tlb_flushD_SE	*/

	/* Cache operations */

	armv5_icache_sync_all,		/* icache_sync_all	*/
	armv5_icache_sync_range,	/* icache_sync_range	*/

	armv5_dcache_wbinv_all,		/* dcache_wbinv_all	*/
	armv5_dcache_wbinv_range,	/* dcache_wbinv_range	*/
	armv5_dcache_inv_range,		/* dcache_inv_range	*/
	armv5_dcache_wb_range,		/* dcache_wb_range	*/

	armv5_idcache_wbinv_all,	/* idcache_wbinv_all	*/
	armv5_idcache_wbinv_range,	/* idcache_wbinv_range	*/

	cpufunc_nullop,			/* sdcache_wbinv_all	*/
	(void *)cpufunc_nullop,		/* sdcache_wbinv_range	*/
	(void *)cpufunc_nullop,		/* sdcache_inv_range	*/
	(void *)cpufunc_nullop,		/* sdcache_wb_range	*/

	/* Other functions */

	cpufunc_nullop,			/* flush_prefetchbuf	*/
	armv4_drain_writebuf,		/* drain_writebuf	*/

	(void *)cpufunc_nullop,		/* sleep		*/

	/* Soft functions */
	arm10_context_switch,		/* context_switch	*/
	arm10_setup			/* cpu setup		*/
};
#endif /* CPU_ARM10 */

#ifdef CPU_ARM11
struct cpu_functions arm11_cpufuncs = {
	/* CPU functions */

	cpufunc_id,			/* id				*/
	cpufunc_nullop,			/* cpwait			*/

	/* MMU functions */

	cpufunc_control,		/* control			*/
	cpufunc_domains,		/* Domain			*/
	arm11_setttb,			/* Setttb			*/
	cpufunc_dfsr,			/* dfsr			*/
	cpufunc_dfar,			/* dfar			*/
	cpufunc_ifsr,			/* ifsr			*/
	cpufunc_ifar,			/* ifar			*/

	/* TLB functions */

	arm11_tlb_flushID,		/* tlb_flushID			*/
	arm11_tlb_flushID_SE,		/* tlb_flushID_SE		*/
	arm11_tlb_flushI,		/* tlb_flushI			*/
	arm11_tlb_flushI_SE,		/* tlb_flushI_SE		*/
	arm11_tlb_flushD,		/* tlb_flushD			*/
	arm11_tlb_flushD_SE,		/* tlb_flushD_SE		*/

	/* Cache operations */

	armv5_icache_sync_all,		/* icache_sync_all	*/
	armv5_icache_sync_range,	/* icache_sync_range	*/

	armv5_dcache_wbinv_all,		/* dcache_wbinv_all	*/
	armv5_dcache_wbinv_range,	/* dcache_wbinv_range	*/
/*XXX*/	armv5_dcache_wbinv_range,	/* dcache_inv_range	*/
	armv5_dcache_wb_range,		/* dcache_wb_range	*/

	armv5_idcache_wbinv_all,	/* idcache_wbinv_all	*/
	armv5_idcache_wbinv_range,	/* idcache_wbinv_range	*/

	cpufunc_nullop,			/* sdcache_wbinv_all	*/
	(void *)cpufunc_nullop,		/* sdcache_wbinv_range	*/
	(void *)cpufunc_nullop,		/* sdcache_inv_range	*/
	(void *)cpufunc_nullop,		/* sdcache_wb_range	*/

	/* Other functions */

	cpufunc_nullop,			/* flush_prefetchbuf	*/
	arm11_drain_writebuf,		/* drain_writebuf	*/

	arm11_cpu_sleep,		/* sleep (wait for interrupt) */

	/* Soft functions */
	arm11_context_switch,		/* context_switch	*/
	arm11_setup			/* cpu setup		*/
};
#endif /* CPU_ARM11 */

#ifdef CPU_ARMv7
struct cpu_functions armv7_cpufuncs = {
	/* CPU functions */

	cpufunc_id,			/* id			*/
	cpufunc_nullop,			/* cpwait		*/

	/* MMU functions */

	cpufunc_control,		/* control		*/
	cpufunc_domains,		/* Domain		*/
	armv7_setttb,			/* Setttb		*/
	cpufunc_dfsr,			/* dfsr			*/
	cpufunc_dfar,			/* dfar			*/
	cpufunc_ifsr,			/* ifsr			*/
	cpufunc_ifar,			/* ifar			*/

	/* TLB functions */

	armv7_tlb_flushID,		/* tlb_flushID		*/
	armv7_tlb_flushID_SE,		/* tlb_flushID_SE	*/
	armv7_tlb_flushI,		/* tlb_flushI		*/
	armv7_tlb_flushI_SE,		/* tlb_flushI_SE	*/
	armv7_tlb_flushD,		/* tlb_flushD		*/
	armv7_tlb_flushD_SE,		/* tlb_flushD_SE	*/

	/* Cache operations */

	armv7_icache_sync_all,		/* icache_sync_all	*/
	armv7_icache_sync_range,	/* icache_sync_range	*/

	armv7_dcache_wbinv_all,		/* dcache_wbinv_all	*/
	armv7_dcache_wbinv_range,	/* dcache_wbinv_range	*/
	armv7_dcache_inv_range,		/* dcache_inv_range	*/
	armv7_dcache_wb_range,		/* dcache_wb_range	*/

	armv7_idcache_wbinv_all,	/* idcache_wbinv_all	*/
	armv7_idcache_wbinv_range,	/* idcache_wbinv_range	*/

	cpufunc_nullop,			/* sdcache_wbinv_all	*/
	(void *)cpufunc_nullop,		/* sdcache_wbinv_range	*/
	(void *)cpufunc_nullop,		/* sdcache_inv_range	*/
	(void *)cpufunc_nullop,		/* sdcache_wb_range	*/

	/* Other functions */

	cpufunc_nullop,			/* flush_prefetchbuf	*/
	armv7_drain_writebuf,		/* drain_writebuf	*/

	armv7_cpu_sleep,		/* sleep (wait for interrupt) */

	/* Soft functions */
	armv7_context_switch,		/* context_switch	*/
	armv7_setup			/* cpu setup		*/
};
#endif /* CPU_ARMv7 */

#if defined(CPU_SA1100) || defined(CPU_SA1110)
struct cpu_functions sa11x0_cpufuncs = {
	/* CPU functions */

	cpufunc_id,			/* id			*/
	cpufunc_nullop,			/* cpwait		*/

	/* MMU functions */

	cpufunc_control,		/* control		*/
	cpufunc_domains,		/* domain		*/
	sa1_setttb,			/* setttb		*/
	cpufunc_dfsr,			/* dfsr			*/
	cpufunc_dfar,			/* dfar			*/
	cpufunc_ifsr,			/* ifsr			*/
	cpufunc_ifar,			/* ifar			*/

	/* TLB functions */

	armv4_tlb_flushID,		/* tlb_flushID		*/
	sa1_tlb_flushID_SE,		/* tlb_flushID_SE	*/
	armv4_tlb_flushI,		/* tlb_flushI		*/
	(void *)armv4_tlb_flushI,	/* tlb_flushI_SE	*/
	armv4_tlb_flushD,		/* tlb_flushD		*/
	armv4_tlb_flushD_SE,		/* tlb_flushD_SE	*/

	/* Cache operations */

	sa1_cache_syncI,		/* icache_sync_all	*/
	sa1_cache_syncI_rng,		/* icache_sync_range	*/

	sa1_cache_purgeD,		/* dcache_wbinv_all	*/
	sa1_cache_purgeD_rng,		/* dcache_wbinv_range	*/
/*XXX*/	sa1_cache_purgeD_rng,		/* dcache_inv_range	*/
	sa1_cache_cleanD_rng,		/* dcache_wb_range	*/

	sa1_cache_purgeID,		/* idcache_wbinv_all	*/
	sa1_cache_purgeID_rng,		/* idcache_wbinv_range	*/

	cpufunc_nullop,			/* sdcache_wbinv_all	*/
	(void *)cpufunc_nullop,		/* sdcache_wbinv_range	*/
	(void *)cpufunc_nullop,		/* sdcache_inv_range	*/
	(void *)cpufunc_nullop,		/* sdcache_wb_range	*/

	/* Other functions */

	sa11x0_drain_readbuf,		/* flush_prefetchbuf	*/
	armv4_drain_writebuf,		/* drain_writebuf	*/

	sa11x0_cpu_sleep,		/* sleep		*/

	/* Soft functions */
	sa11x0_context_switch,		/* context_switch	*/
	sa11x0_setup			/* cpu setup		*/
};
#endif	/* CPU_SA1100 || CPU_SA1110 */

#ifdef CPU_IXP12X0
struct cpu_functions ixp12x0_cpufuncs = {
	/* CPU functions */

	cpufunc_id,			/* id			*/
	cpufunc_nullop,			/* cpwait		*/

	/* MMU functions */

	cpufunc_control,		/* control		*/
	cpufunc_domains,		/* domain		*/
	sa1_setttb,			/* setttb		*/
	cpufunc_dfsr,			/* dfsr			*/
	cpufunc_dfar,			/* dfar			*/
	cpufunc_ifsr,			/* ifsr			*/
	cpufunc_ifar,			/* ifar			*/

	/* TLB functions */

	armv4_tlb_flushID,		/* tlb_flushID		*/
	sa1_tlb_flushID_SE,		/* tlb_flushID_SE	*/
	armv4_tlb_flushI,		/* tlb_flushI		*/
	(void *)armv4_tlb_flushI,	/* tlb_flushI_SE	*/
	armv4_tlb_flushD,		/* tlb_flushD		*/
	armv4_tlb_flushD_SE,		/* tlb_flushD_SE	*/

	/* Cache operations */

	sa1_cache_syncI,		/* icache_sync_all	*/
	sa1_cache_syncI_rng,		/* icache_sync_range	*/

	sa1_cache_purgeD,		/* dcache_wbinv_all	*/
	sa1_cache_purgeD_rng,		/* dcache_wbinv_range	*/
/*XXX*/	sa1_cache_purgeD_rng,		/* dcache_inv_range	*/
	sa1_cache_cleanD_rng,		/* dcache_wb_range	*/

	sa1_cache_purgeID,		/* idcache_wbinv_all	*/
	sa1_cache_purgeID_rng,		/* idcache_wbinv_range	*/

	cpufunc_nullop,			/* sdcache_wbinv_all	*/
	(void *)cpufunc_nullop,		/* sdcache_wbinv_range	*/
	(void *)cpufunc_nullop,		/* sdcache_inv_range	*/
	(void *)cpufunc_nullop,		/* sdcache_wb_range	*/

	/* Other functions */

	ixp12x0_drain_readbuf,		/* flush_prefetchbuf	*/
	armv4_drain_writebuf,		/* drain_writebuf	*/

	(void *)cpufunc_nullop,		/* sleep		*/

	/* Soft functions */
	ixp12x0_context_switch,		/* context_switch	*/
	ixp12x0_setup			/* cpu setup		*/
};
#endif	/* CPU_IXP12X0 */

#if defined(CPU_XSCALE_80200) || defined(CPU_XSCALE_80321) || \
    defined(CPU_XSCALE_PXA2X0) || defined(CPU_XSCALE_IXP425)
struct cpu_functions xscale_cpufuncs = {
	/* CPU functions */

	cpufunc_id,			/* id			*/
	xscale_cpwait,			/* cpwait		*/

	/* MMU functions */

	xscale_control,			/* control		*/
	cpufunc_domains,		/* domain		*/
	xscale_setttb,			/* setttb		*/
	cpufunc_dfsr,			/* dfsr			*/
	cpufunc_dfar,			/* dfar			*/
	cpufunc_ifsr,			/* ifsr			*/
	cpufunc_ifar,			/* ifar			*/

	/* TLB functions */

	armv4_tlb_flushID,		/* tlb_flushID		*/
	xscale_tlb_flushID_SE,		/* tlb_flushID_SE	*/
	armv4_tlb_flushI,		/* tlb_flushI		*/
	(void *)armv4_tlb_flushI,	/* tlb_flushI_SE	*/
	armv4_tlb_flushD,		/* tlb_flushD		*/
	armv4_tlb_flushD_SE,		/* tlb_flushD_SE	*/

	/* Cache operations */

	xscale_cache_syncI,		/* icache_sync_all	*/
	xscale_cache_syncI_rng,		/* icache_sync_range	*/

	xscale_cache_purgeD,		/* dcache_wbinv_all	*/
	xscale_cache_purgeD_rng,	/* dcache_wbinv_range	*/
	xscale_cache_flushD_rng,	/* dcache_inv_range	*/
	xscale_cache_cleanD_rng,	/* dcache_wb_range	*/

	xscale_cache_purgeID,		/* idcache_wbinv_all	*/
	xscale_cache_purgeID_rng,	/* idcache_wbinv_range	*/

	cpufunc_nullop,			/* sdcache_wbinv_all	*/
	(void *)cpufunc_nullop,		/* sdcache_wbinv_range	*/
	(void *)cpufunc_nullop,		/* sdcache_inv_range	*/
	(void *)cpufunc_nullop,		/* sdcache_wb_range	*/

	/* Other functions */

	cpufunc_nullop,			/* flush_prefetchbuf	*/
	armv4_drain_writebuf,		/* drain_writebuf	*/

	xscale_cpu_sleep,		/* sleep		*/

	/* Soft functions */
	xscale_context_switch,		/* context_switch	*/
	xscale_setup			/* cpu setup		*/
};
#endif
/* CPU_XSCALE_80200 || CPU_XSCALE_80321 || CPU_XSCALE_PXA2X0 || CPU_XSCALE_IXP425 */

/*
 * Global constants also used by locore.s
 */

struct cpu_functions cpufuncs;
u_int cputype;
u_int cpu_reset_needs_v4_MMU_disable;	/* flag used in locore.s */

#if defined(CPU_ARM8) || defined(CPU_ARM9) || \
    defined(CPU_ARM9E) || defined(CPU_ARM10) || defined(CPU_ARM11) || \
    defined(CPU_XSCALE_80200) || defined(CPU_XSCALE_80321) || \
    defined(CPU_XSCALE_PXA2X0) || defined(CPU_XSCALE_IXP425)
static void get_cachetype_cp15 (void);

/* Additional cache information local to this file.  Log2 of some of the
   above numbers.  */
static int	arm_dcache_l2_nsets;
static int	arm_dcache_l2_assoc;
static int	arm_dcache_l2_linesize;

static void
get_cachetype_cp15()
{
	u_int ctype, isize, dsize;
	u_int multiplier;

	__asm volatile("mrc p15, 0, %0, c0, c0, 1"
		: "=r" (ctype));

	/*
	 * ...and thus spake the ARM ARM:
	 *
	 * If an <opcode2> value corresponding to an unimplemented or
	 * reserved ID register is encountered, the System Control
	 * processor returns the value of the main ID register.
	 */
	if (ctype == cpufunc_id())
		goto out;

	if ((ctype & CPU_CT_S) == 0)
		arm_pcache_unified = 1;

	/*
	 * If you want to know how this code works, go read the ARM ARM.
	 */

	arm_pcache_type = CPU_CT_CTYPE(ctype);

	if (arm_pcache_unified == 0) {
		isize = CPU_CT_ISIZE(ctype);
		multiplier = (isize & CPU_CT_xSIZE_M) ? 3 : 2;
		arm_picache_line_size = 1U << (CPU_CT_xSIZE_LEN(isize) + 3);
		if (CPU_CT_xSIZE_ASSOC(isize) == 0) {
			if (isize & CPU_CT_xSIZE_M)
				arm_picache_line_size = 0; /* not present */
			else
				arm_picache_ways = 1;
		} else {
			arm_picache_ways = multiplier <<
			    (CPU_CT_xSIZE_ASSOC(isize) - 1);
		}
		arm_picache_size = multiplier << (CPU_CT_xSIZE_SIZE(isize) + 8);
	}

	dsize = CPU_CT_DSIZE(ctype);
	multiplier = (dsize & CPU_CT_xSIZE_M) ? 3 : 2;
	arm_pdcache_line_size = 1U << (CPU_CT_xSIZE_LEN(dsize) + 3);
	if (CPU_CT_xSIZE_ASSOC(dsize) == 0) {
		if (dsize & CPU_CT_xSIZE_M)
			arm_pdcache_line_size = 0; /* not present */
		else
			arm_pdcache_ways = 1;
	} else {
		arm_pdcache_ways = multiplier <<
		    (CPU_CT_xSIZE_ASSOC(dsize) - 1);
	}
	arm_pdcache_size = multiplier << (CPU_CT_xSIZE_SIZE(dsize) + 8);

	arm_dcache_align = arm_pdcache_line_size;

	arm_dcache_l2_assoc = CPU_CT_xSIZE_ASSOC(dsize) + multiplier - 2;
	arm_dcache_l2_linesize = CPU_CT_xSIZE_LEN(dsize) + 3;
	arm_dcache_l2_nsets = 6 + CPU_CT_xSIZE_SIZE(dsize) -
	    CPU_CT_xSIZE_ASSOC(dsize) - CPU_CT_xSIZE_LEN(dsize);

 out:
	arm_dcache_align_mask = arm_dcache_align - 1;
}
#endif /* ARM7TDMI || ARM8 || ARM9 || XSCALE */

#if defined(CPU_SA1100) || defined(CPU_SA1110) || defined(CPU_IXP12X0)
/* Cache information for CPUs without cache type registers. */
struct cachetab {
	u_int32_t ct_cpuid;
	int	ct_pcache_type;
	int	ct_pcache_unified;
	int	ct_pdcache_size;
	int	ct_pdcache_line_size;
	int	ct_pdcache_ways;
	int	ct_picache_size;
	int	ct_picache_line_size;
	int	ct_picache_ways;
};

struct cachetab cachetab[] = {
    /* cpuid,		cache type,	  u,  dsiz, ls, wy,  isiz, ls, wy */
    /* XXX is this type right for SA-1? */
    { CPU_ID_SA1100,	CPU_CT_CTYPE_WB1, 0,  8192, 32, 32, 16384, 32, 32 },
    { CPU_ID_SA1110,	CPU_CT_CTYPE_WB1, 0,  8192, 32, 32, 16384, 32, 32 },
    { CPU_ID_IXP1200,	CPU_CT_CTYPE_WB1, 0, 16384, 32, 32, 16384, 32, 32 }, /* XXX */
    { 0, 0, 0, 0, 0, 0, 0, 0}
};

static void get_cachetype_table (void);

static void
get_cachetype_table()
{
	int i;
	u_int32_t cpuid = cpufunc_id();

	for (i = 0; cachetab[i].ct_cpuid != 0; i++) {
		if (cachetab[i].ct_cpuid == (cpuid & CPU_ID_CPU_MASK)) {
			arm_pcache_type = cachetab[i].ct_pcache_type;
			arm_pcache_unified = cachetab[i].ct_pcache_unified;
			arm_pdcache_size = cachetab[i].ct_pdcache_size;
			arm_pdcache_line_size =
			    cachetab[i].ct_pdcache_line_size;
			arm_pdcache_ways = cachetab[i].ct_pdcache_ways;
			arm_picache_size = cachetab[i].ct_picache_size;
			arm_picache_line_size =
			    cachetab[i].ct_picache_line_size;
			arm_picache_ways = cachetab[i].ct_picache_ways;
		}
	}
	arm_dcache_align = arm_pdcache_line_size;

	arm_dcache_align_mask = arm_dcache_align - 1;
}

#endif /* SA110 || SA1100 || SA1111 || IXP12X0 */

#ifdef CPU_ARMv7
void arm_get_cachetype_cp15v7 (void);
int	arm_dcache_l2_nsets;
int	arm_dcache_l2_assoc;
int	arm_dcache_l2_linesize;

/*
 * Base 2 logarithm of an int. returns 0 for 0 (yeye, I know).
 */
static int
log2(unsigned int i)
{
	int ret = 0;

	while (i >>= 1)
		ret++;

	return (ret);
}

void
arm_get_cachetype_cp15v7(void)
{
	uint32_t cachereg;
	uint32_t cache_level_id;
	uint32_t sets;
	uint32_t sel, level;

	/* CLIDR - Cache Level ID Register */
	__asm volatile("mrc p15, 1, %0, c0, c0, 1"
		: "=r" (cache_level_id) :);
	cpu_drain_writebuf();

	/* L1 Cache available. */
	level = 0;
	if (cache_level_id & (0x7 << level)) {
		/* Unified cache. */
		if (cache_level_id & (0x4 << level))
			arm_pcache_unified = 1;

		/* Unified or data cache separate. */
		if (cache_level_id & (0x4 << level) ||
		    cache_level_id & (0x2 << level)) {
			sel = level << 1 | 0 << 0; /* L1 | unified/data cache */
			/* CSSELR - Cache Size Selection Register */
			__asm volatile("mcr p15, 2, %0, c0, c0, 0"
				:: "r" (sel));
			cpu_drain_writebuf();
			/* CCSIDR - Cache Size Identification Register */
			__asm volatile("mrc p15, 1, %0, c0, c0, 0"
			: "=r" (cachereg) :);
			cpu_drain_writebuf();
			sets = ((cachereg >> 13) & 0x7fff) + 1;
			arm_pdcache_line_size = 1 << ((cachereg & 0x7) + 4);
			arm_pdcache_ways = ((cachereg >> 3) & 0x3ff) + 1;
			arm_pdcache_size = arm_pdcache_line_size * arm_pdcache_ways * sets;
			switch (cachereg & 0xc0000000) {
			case 0x00000000:
				arm_pcache_type = 0;
				break;
			case 0x40000000:
			case 0xc0000000:
				arm_pcache_type = CPU_CT_CTYPE_WB1;
				break;
			case 0x80000000:
				arm_pcache_type = CPU_CT_CTYPE_WT;
				break;
			}
		}

		/* Instruction cache separate. */
		if (cache_level_id & (0x1 << level)) {
			sel = level << 1 | 1 << 0; /* L1 | instruction cache */
			/* CSSELR - Cache Size Selection Register */
			__asm volatile("mcr p15, 2, %0, c0, c0, 0"
				:: "r" (sel));
			cpu_drain_writebuf();
			/* CCSIDR - Cache Size Identification Register */
			__asm volatile("mrc p15, 1, %0, c0, c0, 0"
			: "=r" (cachereg) :);
			cpu_drain_writebuf();
			sets = ((cachereg >> 13) & 0x7fff) + 1;
			arm_picache_line_size = 1 << ((cachereg & 0x7) + 4);
			arm_picache_ways = ((cachereg >> 3) & 0x3ff) + 1;
			arm_picache_size = arm_picache_line_size * arm_picache_ways * sets;
		}
	}

	arm_dcache_align = arm_pdcache_line_size;
	arm_dcache_align_mask = arm_dcache_align - 1;

	arm_dcache_l2_nsets = arm_pdcache_size/arm_pdcache_ways/arm_pdcache_line_size;
	arm_dcache_l2_assoc = log2(arm_pdcache_ways);
	arm_dcache_l2_linesize = log2(arm_pdcache_line_size);
}

/*
 */
void
armv7_idcache_wbinv_all()
{
	uint32_t arg;
	arg = 0;
	__asm volatile("mcr	p15, 0, r0, c7, c5, 0" :: "r" (arg));
	armv7_dcache_wbinv_all();
}
/* brute force cache flushing */
void
armv7_dcache_wbinv_all()
{
	int sets, ways, lvl;
	int nsets, nways;
	uint32_t wayincr, setincr;
	uint32_t wayval, setval;
	uint32_t word;

	nsets = arm_dcache_l2_nsets;
	nways = arm_pdcache_ways;

	setincr = armv7_dcache_sets_inc;
	wayincr = armv7_dcache_index_inc;

#if 0
	printf("l1 nsets %d nways %d wayincr %x setincr %x\n",
	    nsets, nways, wayincr, setincr);
#endif

	lvl = 0; /* L1 */
	setval = 0;
	for (sets = 0; sets < nsets; sets++)  {
		wayval = 0;
		for (ways = 0; ways < nways; ways++) {
			word = wayval | setval | lvl;

			/* Clean D cache SE with Set/Index */
			__asm volatile("mcr	p15, 0, %0, c7, c10, 2"
			    : : "r" (word));
			wayval += wayincr;
		}
		setval += setincr;
	}
	/* drain the write buffer */
	cpu_drain_writebuf();

	/* L2 cache flushing removed. Our current L2 caches are separate. */
}
#endif /* CPU_ARMv7 */

/*
 * Cannot panic here as we may not have a console yet ...
 */

int
set_cpufuncs()
{
	cputype = cpufunc_id();
	cputype &= CPU_ID_CPU_MASK;

	/*
	 * NOTE: cpu_do_powersave defaults to off.  If we encounter a
	 * CPU type where we want to use it by default, then we set it.
	 */

#ifdef CPU_ARM8
	if ((cputype & CPU_ID_IMPLEMENTOR_MASK) == CPU_ID_ARM_LTD &&
	    (cputype & 0x0000f000) == 0x00008000) {
		cpufuncs = arm8_cpufuncs;
		cpu_reset_needs_v4_MMU_disable = 0;	/* XXX correct? */
		get_cachetype_cp15();
		pmap_pte_init_arm8();
		return 0;
	}
#endif	/* CPU_ARM8 */
#ifdef CPU_ARM9
	if (((cputype & CPU_ID_IMPLEMENTOR_MASK) == CPU_ID_ARM_LTD ||
	     (cputype & CPU_ID_IMPLEMENTOR_MASK) == CPU_ID_TI) &&
	    (cputype & 0x0000f000) == 0x00009000) {
		cpufuncs = arm9_cpufuncs;
		cpu_reset_needs_v4_MMU_disable = 1;	/* V4 or higher */
		get_cachetype_cp15();
		arm9_dcache_sets_inc = 1U << arm_dcache_l2_linesize;
		arm9_dcache_sets_max =
		    (1U << (arm_dcache_l2_linesize + arm_dcache_l2_nsets)) -
		    arm9_dcache_sets_inc;
		arm9_dcache_index_inc = 1U << (32 - arm_dcache_l2_assoc);
		arm9_dcache_index_max = 0U - arm9_dcache_index_inc;
		pmap_pte_init_arm9();
		return 0;
	}
#endif /* CPU_ARM9 */
#if defined(CPU_ARM9E) || defined(CPU_ARM10)
	if (cputype == CPU_ID_ARM926EJS || cputype == CPU_ID_ARM1026EJS) {
		cpufuncs = armv5_ec_cpufuncs;
		cpu_reset_needs_v4_MMU_disable = 1;	/* V4 or higher */
		get_cachetype_cp15();
		pmap_pte_init_generic();
		return 0;
	}
#endif /* CPU_ARM9E || CPU_ARM10 */
#ifdef CPU_ARM10
	if (/* cputype == CPU_ID_ARM1020T || */
	    cputype == CPU_ID_ARM1020E) {
		/*
		 * Select write-through cacheing (this isn't really an
		 * option on ARM1020T).
		 */
		cpufuncs = arm10_cpufuncs;
		cpu_reset_needs_v4_MMU_disable = 1;	/* V4 or higher */
		get_cachetype_cp15();
		armv5_dcache_sets_inc = 1U << arm_dcache_l2_linesize;
		armv5_dcache_sets_max =
		    (1U << (arm_dcache_l2_linesize + arm_dcache_l2_nsets)) -
		    armv5_dcache_sets_inc;
		armv5_dcache_index_inc = 1U << (32 - arm_dcache_l2_assoc);
		armv5_dcache_index_max = 0U - armv5_dcache_index_inc;
		pmap_pte_init_generic();
		return 0;
	}
#endif /* CPU_ARM10 */
#ifdef CPU_ARM11
	if (cputype == CPU_ID_ARM1136JS ||
	    cputype == CPU_ID_ARM1136JSR1 || 1) {
		cpufuncs = arm11_cpufuncs;
		cpu_reset_needs_v4_MMU_disable = 1;	/* V4 or higher */
		get_cachetype_cp15();
		arm11_dcache_sets_inc = 1U << arm_dcache_l2_linesize;
		arm11_dcache_sets_max =
		    (1U << (arm_dcache_l2_linesize + arm_dcache_l2_nsets)) -
		    arm11_dcache_sets_inc;
		arm11_dcache_index_inc = 1U << (32 - arm_dcache_l2_assoc);
		arm11_dcache_index_max = 0U - arm11_dcache_index_inc;
		pmap_pte_init_arm11();

		/* Use powersave on this CPU. */
		cpu_do_powersave = 1;
		return 0;
	}
#endif /* CPU_ARM11 */
#ifdef CPU_ARMv7
	if ((cputype & CPU_ID_CORTEX_A5_MASK) == CPU_ID_CORTEX_A5 ||
	    (cputype & CPU_ID_CORTEX_A7_MASK) == CPU_ID_CORTEX_A7 ||
	    (cputype & CPU_ID_CORTEX_A8_MASK) == CPU_ID_CORTEX_A8 ||
	    (cputype & CPU_ID_CORTEX_A9_MASK) == CPU_ID_CORTEX_A9 ||
	    (cputype & CPU_ID_CORTEX_A15_MASK) == CPU_ID_CORTEX_A15 ||
	    (cputype & CPU_ID_CORTEX_A17_MASK) == CPU_ID_CORTEX_A17) {
		cpufuncs = armv7_cpufuncs;
		cpu_reset_needs_v4_MMU_disable = 1;	/* V4 or higher */
		arm_get_cachetype_cp15v7();
		armv7_dcache_sets_inc = 1U << arm_dcache_l2_linesize;
		armv7_dcache_sets_max =
		    (1U << (arm_dcache_l2_linesize + arm_dcache_l2_nsets)) -
		    armv7_dcache_sets_inc;
		armv7_dcache_index_inc = 1U << (32 - arm_dcache_l2_assoc);
		armv7_dcache_index_max = 0U - armv7_dcache_index_inc;
		pmap_pte_init_armv7();

		/* Use powersave on this CPU. */
		cpu_do_powersave = 1;
		return 0;
	}
#endif /* CPU_ARMv7 */
#ifdef CPU_SA1100
	if (cputype == CPU_ID_SA1100) {
		cpufuncs = sa11x0_cpufuncs;
		cpu_reset_needs_v4_MMU_disable = 1;	/* SA needs it	*/
		get_cachetype_table();
		pmap_pte_init_sa1();

		/* Use powersave on this CPU. */
		cpu_do_powersave = 1;

		return 0;
	}
#endif	/* CPU_SA1100 */
#ifdef CPU_SA1110
	if (cputype == CPU_ID_SA1110) {
		cpufuncs = sa11x0_cpufuncs;
		cpu_reset_needs_v4_MMU_disable = 1;	/* SA needs it	*/
		get_cachetype_table();
		pmap_pte_init_sa1();

		/* Use powersave on this CPU. */
		cpu_do_powersave = 1;

		return 0;
	}
#endif	/* CPU_SA1110 */
#ifdef CPU_IXP12X0
	if (cputype == CPU_ID_IXP1200) {
		cpufuncs = ixp12x0_cpufuncs;
		cpu_reset_needs_v4_MMU_disable = 1;
		get_cachetype_table();
		pmap_pte_init_sa1();
		return 0;
	}
#endif  /* CPU_IXP12X0 */
#ifdef CPU_XSCALE_80200
	if (cputype == CPU_ID_80200) {
		int rev = cpufunc_id() & CPU_ID_REVISION_MASK;

		i80200_icu_init();

#ifdef PERFCTRS
		/*
		 * Reset the Performance Monitoring Unit to a
		 * pristine state:
		 *	- CCNT, PMN0, PMN1 reset to 0
		 *	- overflow indications cleared
		 *	- all counters disabled
		 */
		__asm volatile("mcr p14, 0, %0, c0, c0, 0"
			:
			: "r" (PMNC_P|PMNC_C|PMNC_PMN0_IF|PMNC_PMN1_IF|
			       PMNC_CC_IF));
#endif /* PERFCTRS */

#if defined(XSCALE_CCLKCFG)
		/*
		 * Crank CCLKCFG to maximum legal value.
		 */
		__asm volatile ("mcr p14, 0, %0, c6, c0, 0"
			:
			: "r" (XSCALE_CCLKCFG));
#endif

		/*
		 * XXX Disable ECC in the Bus Controller Unit; we
		 * don't really support it, yet.  Clear any pending
		 * error indications.
		 */
		__asm volatile("mcr p13, 0, %0, c0, c1, 0"
			:
			: "r" (BCUCTL_E0|BCUCTL_E1|BCUCTL_EV));

		cpufuncs = xscale_cpufuncs;
#if defined(PERFCTRS)
		xscale_pmu_init();
#endif

		/*
		 * i80200 errata: Step-A0 and A1 have a bug where
		 * D$ dirty bits are not cleared on "invalidate by
		 * address".
		 *
		 * Workaround: Clean cache line before invalidating.
		 */
		if (rev == 0 || rev == 1)
			cpufuncs.cf_dcache_inv_range = xscale_cache_purgeD_rng;

		cpu_reset_needs_v4_MMU_disable = 1;	/* XScale needs it */
		get_cachetype_cp15();
		pmap_pte_init_xscale();
		return 0;
	}
#endif /* CPU_XSCALE_80200 */
#ifdef CPU_XSCALE_80321
	if (cputype == CPU_ID_80321_400 || cputype == CPU_ID_80321_600 ||
	    cputype == CPU_ID_80321_400_B0 || cputype == CPU_ID_80321_600_B0 ||
	    cputype == CPU_ID_80219_400 || cputype == CPU_ID_80219_600) {
		i80321intc_init();

#ifdef PERFCTRS
		/*
		 * Reset the Performance Monitoring Unit to a
		 * pristine state:
		 *	- CCNT, PMN0, PMN1 reset to 0
		 *	- overflow indications cleared
		 *	- all counters disabled
		 */
		__asm volatile("mcr p14, 0, %0, c0, c0, 0"
			:
			: "r" (PMNC_P|PMNC_C|PMNC_PMN0_IF|PMNC_PMN1_IF|
			       PMNC_CC_IF));
#endif /* PERFCTRS */

		cpufuncs = xscale_cpufuncs;
#if defined(PERFCTRS)
		xscale_pmu_init();
#endif

		cpu_reset_needs_v4_MMU_disable = 1;	/* XScale needs it */
		get_cachetype_cp15();
		pmap_pte_init_xscale();
		return 0;
	}
#endif /* CPU_XSCALE_80321 */
#ifdef CPU_XSCALE_PXA2X0
	/* ignore core revision to test PXA2xx CPUs */
	if ((cputype & ~CPU_ID_XSCALE_COREREV_MASK) == CPU_ID_PXA250 ||
	    (cputype & ~CPU_ID_XSCALE_COREREV_MASK) == CPU_ID_PXA27X ||
	    (cputype & ~CPU_ID_XSCALE_COREREV_MASK) == CPU_ID_PXA210) {

		cpufuncs = xscale_cpufuncs;
#if defined(PERFCTRS)
		xscale_pmu_init();
#endif

		cpu_reset_needs_v4_MMU_disable = 1;	/* XScale needs it */
		get_cachetype_cp15();
		pmap_pte_init_xscale();

		/* Use powersave on this CPU. */
		cpu_do_powersave = 1;

		return 0;
	}
#endif /* CPU_XSCALE_PXA2X0 */
#ifdef CPU_XSCALE_IXP425
	if (cputype == CPU_ID_IXP425_533 || cputype == CPU_ID_IXP425_400 ||
	    cputype == CPU_ID_IXP425_266) {
		ixp425_icu_init();

		cpufuncs = xscale_cpufuncs;
#if defined(PERFCTRS)
		xscale_pmu_init();
#endif

		cpu_reset_needs_v4_MMU_disable = 1;	/* XScale needs it */
		get_cachetype_cp15();
		pmap_pte_init_xscale();

		return 0;
	}
#endif /* CPU_XSCALE_IXP425 */
	/*
	 * Bzzzz. And the answer was ...
	 */
	panic("No support for this CPU type (%08x) in kernel", cputype);
	return(ARCHITECTURE_NOT_PRESENT);
}

/*
 * CPU Setup code
 */

#ifdef CPU_ARM8
void
arm8_setup()
{
	int integer;
	int cpuctrl, cpuctrlmask;
	int clocktest;
	int setclock = 0;

	cpuctrl = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_32BP_ENABLE
		 | CPU_CONTROL_32BD_ENABLE | CPU_CONTROL_SYST_ENABLE
		 | CPU_CONTROL_IDC_ENABLE | CPU_CONTROL_WBUF_ENABLE
		 | CPU_CONTROL_AFLT_ENABLE;
	cpuctrlmask = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_32BP_ENABLE
		 | CPU_CONTROL_32BD_ENABLE | CPU_CONTROL_SYST_ENABLE
		 | CPU_CONTROL_IDC_ENABLE | CPU_CONTROL_WBUF_ENABLE
		 | CPU_CONTROL_BPRD_ENABLE | CPU_CONTROL_ROM_ENABLE
		 | CPU_CONTROL_BEND_ENABLE | CPU_CONTROL_AFLT_ENABLE;

	/* Get clock configuration */
	clocktest = arm8_clock_config(0, 0) & 0x0f;

	/* Clear out the cache */
	cpu_idcache_wbinv_all();

	/* Set the control register */
	curcpu()->ci_ctrl = cpuctrl;
	cpu_control(0xffffffff, cpuctrl);

	/* Set the clock/test register */
	if (setclock)
		arm8_clock_config(0x7f, clocktest);
}
#endif	/* CPU_ARM8 */

#ifdef CPU_ARM9
void
arm9_setup()
{
	int cpuctrl, cpuctrlmask;

	cpuctrl = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_32BP_ENABLE
	    | CPU_CONTROL_32BD_ENABLE | CPU_CONTROL_SYST_ENABLE
	    | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE
	    | CPU_CONTROL_WBUF_ENABLE | CPU_CONTROL_AFLT_ENABLE;
	cpuctrlmask = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_32BP_ENABLE
		 | CPU_CONTROL_32BD_ENABLE | CPU_CONTROL_SYST_ENABLE
		 | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE
		 | CPU_CONTROL_WBUF_ENABLE | CPU_CONTROL_ROM_ENABLE
		 | CPU_CONTROL_BEND_ENABLE | CPU_CONTROL_AFLT_ENABLE
		 | CPU_CONTROL_LABT_ENABLE | CPU_CONTROL_VECRELOC
		 | CPU_CONTROL_ROUNDROBIN;

	if (vector_page == ARM_VECTORS_HIGH)
		cpuctrl |= CPU_CONTROL_VECRELOC;

	/* Clear out the cache */
	cpu_idcache_wbinv_all();

	/* Set the control register */
	curcpu()->ci_ctrl = cpuctrl;
	cpu_control(cpuctrlmask, cpuctrl);

}
#endif	/* CPU_ARM9 */

#if defined(CPU_ARM9E) || defined(CPU_ARM10)
void
arm9e_setup()
{
	int cpuctrl, cpuctrlmask;

	cpuctrl = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_SYST_ENABLE
	    | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE
	    | CPU_CONTROL_WBUF_ENABLE | CPU_CONTROL_BPRD_ENABLE
	    | CPU_CONTROL_AFLT_ENABLE;
	cpuctrlmask = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_SYST_ENABLE
	    | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE
	    | CPU_CONTROL_WBUF_ENABLE | CPU_CONTROL_ROM_ENABLE
	    | CPU_CONTROL_BEND_ENABLE | CPU_CONTROL_AFLT_ENABLE
	    | CPU_CONTROL_BPRD_ENABLE
	    | CPU_CONTROL_ROUNDROBIN | CPU_CONTROL_CPCLK;

	if (vector_page == ARM_VECTORS_HIGH)
		cpuctrl |= CPU_CONTROL_VECRELOC;

	/* Clear out the cache */
	cpu_idcache_wbinv_all();

	/* Now really make sure they are clean.  */
	__asm volatile ("mcr\tp15, 0, r0, c7, c7, 0" : : );

	/* Set the control register */
	curcpu()->ci_ctrl = cpuctrl;
	cpu_control(0xffffffff, cpuctrl);

	/* And again. */
	cpu_idcache_wbinv_all();
}
#endif	/* CPU_ARM9E || CPU_ARM10 */

#if defined(CPU_ARM10)
void
arm10_setup()
{
	arm9e_setup();

	/* Allow detection code to find the VFP if it's fitted.  */
	__asm volatile ("mcr\tp15, 0, %0, c1, c0, 2" : : "r" (0x0fffffff));
}
#endif	/* CPU_ARM10 */

#ifdef CPU_ARM11
void
arm11_setup()
{
	int cpuctrl, cpuctrlmask;

	cpuctrl = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_SYST_ENABLE
	    | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE
	    | CPU_CONTROL_AFLT_ENABLE /* | CPU_CONTROL_BPRD_ENABLE */;
	cpuctrlmask = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_SYST_ENABLE
	    | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE
	    | CPU_CONTROL_ROM_ENABLE | CPU_CONTROL_BPRD_ENABLE
	    | CPU_CONTROL_BEND_ENABLE | CPU_CONTROL_AFLT_ENABLE
	    | CPU_CONTROL_ROUNDROBIN | CPU_CONTROL_CPCLK;

	/* Clear out the cache */
	cpu_idcache_wbinv_all();

	/* Now really make sure they are clean.  */
	asm volatile ("mcr\tp15, 0, r0, c7, c7, 0" : : );

	/* Set the control register */
	curcpu()->ci_ctrl = cpuctrl;
	cpu_control(0xffffffff, cpuctrl);

	/* And again. */
	cpu_idcache_wbinv_all();
}
#endif	/* CPU_ARM11 */

#ifdef CPU_ARMv7
void
armv7_setup()
{
	int cpuctrl, cpuctrlmask;

	cpuctrl = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_SYST_ENABLE
	    | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE
	    | CPU_CONTROL_BPRD_ENABLE | CPU_CONTROL_AFLT_ENABLE;
	cpuctrlmask = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_SYST_ENABLE
	    | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE
	    | CPU_CONTROL_ROM_ENABLE | CPU_CONTROL_BPRD_ENABLE
	    | CPU_CONTROL_BEND_ENABLE | CPU_CONTROL_AFLT_ENABLE
	    | CPU_CONTROL_ROUNDROBIN | CPU_CONTROL_CPCLK
	    | CPU_CONTROL_VECRELOC | CPU_CONTROL_FI | CPU_CONTROL_VE
	    | CPU_CONTROL_TRE | CPU_CONTROL_AFE;

	if (vector_page == ARM_VECTORS_HIGH)
		cpuctrl |= CPU_CONTROL_VECRELOC;

	/* Clear out the cache */
	cpu_idcache_wbinv_all();

	/* Now really make sure they are clean.  */
	/* XXX */
	/*
	asm volatile ("mcr\tp15, 0, r0, c7, c7, 0" : : );
	*/

	/* Set the control register */
	curcpu()->ci_ctrl = cpuctrl;
	cpu_control(cpuctrlmask, cpuctrl);

	/* And again. */
	cpu_idcache_wbinv_all();
}
#endif	/* CPU_ARMv7 */

#if defined(CPU_SA1100) || defined(CPU_SA1110)
void
sa11x0_setup()
{
	int cpuctrl, cpuctrlmask;

	cpuctrl = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_32BP_ENABLE
		 | CPU_CONTROL_32BD_ENABLE | CPU_CONTROL_SYST_ENABLE
		 | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE
		 | CPU_CONTROL_WBUF_ENABLE | CPU_CONTROL_LABT_ENABLE
		 | CPU_CONTROL_AFLT_ENABLE;
	cpuctrlmask = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_32BP_ENABLE
		 | CPU_CONTROL_32BD_ENABLE | CPU_CONTROL_SYST_ENABLE
		 | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE
		 | CPU_CONTROL_WBUF_ENABLE | CPU_CONTROL_ROM_ENABLE
		 | CPU_CONTROL_BEND_ENABLE | CPU_CONTROL_AFLT_ENABLE
		 | CPU_CONTROL_LABT_ENABLE | CPU_CONTROL_BPRD_ENABLE
		 | CPU_CONTROL_CPCLK | CPU_CONTROL_VECRELOC;

	if (vector_page == ARM_VECTORS_HIGH)
		cpuctrl |= CPU_CONTROL_VECRELOC;

	/* Clear out the cache */
	cpu_idcache_wbinv_all();

	/* Set the control register */
	cpu_control(0xffffffff, cpuctrl);
}
#endif	/* CPU_SA1100 || CPU_SA1110 */

#if defined(CPU_IXP12X0)
void
ixp12x0_setup()
{
	int cpuctrl, cpuctrlmask;

	cpuctrl = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_DC_ENABLE
		 | CPU_CONTROL_WBUF_ENABLE | CPU_CONTROL_SYST_ENABLE
		 | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_AFLT_ENABLE;
	cpuctrlmask = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_AFLT_ENABLE
		 | CPU_CONTROL_DC_ENABLE | CPU_CONTROL_WBUF_ENABLE
		 | CPU_CONTROL_BEND_ENABLE | CPU_CONTROL_SYST_ENABLE
		 | CPU_CONTROL_ROM_ENABLE | CPU_CONTROL_IC_ENABLE
		 | CPU_CONTROL_VECRELOC;

	if (vector_page == ARM_VECTORS_HIGH)
		cpuctrl |= CPU_CONTROL_VECRELOC;

	/* Clear out the cache */
	cpu_idcache_wbinv_all();

	/* Set the control register */
	curcpu()->ci_ctrl = cpuctrl;
	/* cpu_control(0xffffffff, cpuctrl); */
	cpu_control(cpuctrlmask, cpuctrl);
}
#endif /* CPU_IXP12X0 */

#if defined(CPU_XSCALE_80200) || defined(CPU_XSCALE_80321) || \
    defined(CPU_XSCALE_PXA2X0) || defined(CPU_XSCALE_IXP425)
void
xscale_setup()
{
	uint32_t auxctl;
	int cpuctrl, cpuctrlmask;

	/*
	 * The XScale Write Buffer is always enabled.  Our option
	 * is to enable/disable coalescing.  Note that bits 6:3
	 * must always be enabled.
	 */

	cpuctrl = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_32BP_ENABLE
		 | CPU_CONTROL_32BD_ENABLE | CPU_CONTROL_SYST_ENABLE
		 | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE
		 | CPU_CONTROL_WBUF_ENABLE | CPU_CONTROL_LABT_ENABLE
		 | CPU_CONTROL_BPRD_ENABLE | CPU_CONTROL_AFLT_ENABLE;
	cpuctrlmask = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_32BP_ENABLE
		 | CPU_CONTROL_32BD_ENABLE | CPU_CONTROL_SYST_ENABLE
		 | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE
		 | CPU_CONTROL_WBUF_ENABLE | CPU_CONTROL_ROM_ENABLE
		 | CPU_CONTROL_BEND_ENABLE | CPU_CONTROL_AFLT_ENABLE
		 | CPU_CONTROL_LABT_ENABLE | CPU_CONTROL_BPRD_ENABLE
		 | CPU_CONTROL_CPCLK | CPU_CONTROL_VECRELOC;

	if (vector_page == ARM_VECTORS_HIGH)
		cpuctrl |= CPU_CONTROL_VECRELOC;

	/* Clear out the cache */
	cpu_idcache_wbinv_all();

	/*
	 * Set the control register.  Note that bits 6:3 must always
	 * be set to 1.
	 */
	curcpu()->ci_ctrl = cpuctrl;
/*	cpu_control(cpuctrlmask, cpuctrl);*/
	cpu_control(0xffffffff, cpuctrl);

	/* Make sure write coalescing is turned on */
	__asm volatile("mrc p15, 0, %0, c1, c0, 1"
		: "=r" (auxctl));
#ifdef XSCALE_NO_COALESCE_WRITES
	auxctl |= XSCALE_AUXCTL_K;
#else
	auxctl &= ~XSCALE_AUXCTL_K;
#endif
	__asm volatile("mcr p15, 0, %0, c1, c0, 1"
		: : "r" (auxctl));
}
#endif	/* CPU_XSCALE_80200 || CPU_XSCALE_80321 || CPU_XSCALE_PXA2X0 || CPU_XSCALE_IXP425 */


code:
/*	$OpenBSD: cpufunc_asm.S,v 1.2 2013/01/23 19:57:47 patrick Exp $	*/
/*	$NetBSD: cpufunc_asm.S,v 1.12 2003/09/06 09:14:52 rearnsha Exp $	*/

/*
 * Copyright (c) 1997,1998 Mark Brinicombe.
 * Copyright (c) 1997 Causality Limited
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Causality Limited.
 * 4. The name of Causality Limited may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY CAUSALITY LIMITED ``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 CAUSALITY LIMITED 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.
 *
 * RiscBSD kernel project
 *
 * cpufunc.S
 *
 * Assembly functions for CPU / MMU / TLB specific operations
 *
 * Created      : 30/01/97
 */

#include <machine/cpu.h>
#include <machine/asm.h>

	.text
	.align	0

ENTRY(cpufunc_nullop)
	mov	pc, lr

/*
 * Generic functions to read the internal coprocessor registers
 *
 * Currently these registers are :
 *  c0 - CPU ID
 *  c5 - Fault status
 *  c6 - Fault address
 *
 */

ENTRY(cpufunc_id)
	mrc	p15, 0, r0, c0, c0, 0
	mov	pc, lr

ENTRY(cpu_get_control)
	mrc	p15, 0, r0, c1, c0, 0
	mov	pc, lr

ENTRY(cpu_read_cache_config)
	mrc	p15, 0, r0, c0, c0, 1
	mov	pc, lr

ENTRY(cpufunc_dfsr)
	mrc	p15, 0, r0, c5, c0, 0
	mov	pc, lr

ENTRY(cpufunc_dfar)
	mrc	p15, 0, r0, c6, c0, 0
	mov	pc, lr

ENTRY(cpufunc_ifsr)
	mrc	p15, 0, r0, c5, c0, 1
	mov	pc, lr

ENTRY(cpufunc_ifar)
	mrc	p15, 0, r0, c6, c0, 2
	mov	pc, lr

/*
 * Generic functions to write the internal coprocessor registers
 *
 *
 * Currently these registers are
 *  c1 - CPU Control
 *  c3 - Domain Access Control
 *
 * All other registers are CPU architecture specific
 */

#if 0 /* See below. */
ENTRY(cpufunc_control)
	mcr	p15, 0, r0, c1, c0, 0
	mov	pc, lr
#endif

ENTRY(cpufunc_domains)
	mcr	p15, 0, r0, c3, c0, 0
	mov	pc, lr

/*
 * Generic functions to read/modify/write the internal coprocessor registers
 *
 *
 * Currently these registers are
 *  c1 - CPU Control
 *
 * All other registers are CPU architecture specific
 */

ENTRY(cpufunc_control)
	mrc	p15, 0, r3, c1, c0, 0	/* Read the control register */
	bic	r2, r3, r0		/* Clear bits */
	eor     r2, r2, r1		/* XOR bits */

	teq	r2, r3			/* Only write if there is a change */
	mcrne	p15, 0, r2, c1, c0, 0	/* Write new control register */
	mov	r0, r3			/* Return old value */
	mov	pc, lr

/*
 * other potentially useful software functions are:
 *  clean D cache entry and flush I cache entry
 *   for the moment use cache_purgeID_E
 */

/* Random odd functions */

/*
 * Function to get the offset of a stored program counter from the
 * instruction doing the store.  This offset is defined to be the same
 * for all STRs and STMs on a given implementation.  Code based on
 * section 2.4.3 of the ARM ARM (2nd Ed.), with modifications to work
 * in 26-bit modes as well.
 */
ENTRY(get_pc_str_offset)
	mov	ip, sp
	stmfd	sp!, {fp, ip, lr, pc}
	sub	fp, ip, #4
	sub	sp, sp, #4
	mov	r1, pc		/* R1 = addr of following STR */
	mov	r0, r0
	str	pc, [sp]	/* [SP] = . + offset */
	ldr	r0, [sp]
	sub	r0, r0, r1
	ldmdb	fp, {fp, sp, pc}

license: unknown


code:
/*	$OpenBSD: cpufunc_asm_arm10.S,v 1.1 2008/09/11 02:38:14 kevlo Exp $	*/
/*	$NetBSD: cpufunc_asm_arm10.S,v 1.8 2007/10/17 19:53:29 garbled Exp $	*/

/*
 * Copyright (c) 2002 ARM Limited
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the company may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * 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 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.
 *
 * ARM10 assembly functions for CPU / MMU / TLB specific operations
 */

#include <machine/cpu.h>
#include <machine/asm.h>

/*
 * TLB functions
 */
ENTRY(arm10_tlb_flushID_SE)
	mcr	p15, 0, r0, c8, c6, 1	/* flush D tlb single entry */
	mcr	p15, 0, r0, c8, c5, 1	/* flush I tlb single entry */
	mov	pc, lr

ENTRY(arm10_tlb_flushI_SE)
	mcr	p15, 0, r0, c8, c5, 1	/* flush I tlb single entry */
	mov	pc, lr

/*
 * Context switch.
 *
 * These is the CPU-specific parts of the context switcher cpu_switch()
 * These functions actually perform the TTB reload.
 */
ENTRY(arm10_context_switch)
	/*
	 * We can assume that the caches will only contain kernel addresses
	 * at this point.  So no need to flush them again.
	 */
	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
	mcr	p15, 0, r0, c2, c0, 0	/* set the new TTB */
	mcr	p15, 0, r0, c8, c7, 0	/* and flush the I+D tlbs */

	/* Paranoia -- make sure the pipeline is empty. */
	nop
	nop
	nop
	mov	pc, lr


code:
/*	$OpenBSD: cpufunc_asm_arm9.S,v 1.1 2008/09/11 02:38:14 kevlo Exp $	*/
/*	$NetBSD: cpufunc_asm_arm9.S,v 1.6 2007/10/17 19:53:29 garbled Exp $	*/

/*
 * Copyright (c) 2001, 2004 ARM Limited
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the company may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * 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 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.
 *
 * ARM9 assembly functions for CPU / MMU / TLB specific operations
 */

#include <machine/cpu.h>
#include <machine/asm.h>

/*
 * Functions to set the MMU Translation Table Base register
 *
 * We need to clean and flush the cache as it uses virtual
 * addresses that are about to change.
 */
ENTRY(arm9_setttb)
	stmfd	sp!, {r0, lr}
	bl	_C_LABEL(arm9_idcache_wbinv_all)
	ldmfd	sp!, {r0, lr}

	mcr	p15, 0, r0, c2, c0, 0	/* load new TTB */

	mcr	p15, 0, r0, c8, c7, 0	/* invalidate I+D TLBs */
	mov	pc, lr

/*
 * TLB functions
 */
ENTRY(arm9_tlb_flushID_SE)
	mcr	p15, 0, r0, c8, c6, 1	/* flush D tlb single entry */
	mcr	p15, 0, r0, c8, c5, 1	/* flush I tlb single entry */
	mov	pc, lr

/*
 * Cache operations.  For the entire cache we use the set/index
 * operations.
 */
	s_max	.req r0
	i_max	.req r1
	s_inc	.req r2
	i_inc	.req r3

ENTRY_NP(arm9_icache_sync_range)
	ldr	ip, .Larm9_line_size
	cmp	r1, #0x4000
	bcs	.Larm9_icache_sync_all
	ldr	ip, [ip]
	sub	r3, ip, #1
	and	r2, r0, r3
	add	r1, r1, r2
	bic	r0, r0, r3
.Larm9_sync_next:
	mcr	p15, 0, r0, c7, c5, 1	/* Invalidate I cache SE with VA */
	mcr	p15, 0, r0, c7, c10, 1	/* Clean D cache SE with VA */
	add	r0, r0, ip
	subs	r1, r1, ip
	bpl	.Larm9_sync_next
	mov	pc, lr

ENTRY_NP(arm9_icache_sync_all)
.Larm9_icache_sync_all:
	/*
	 * We assume that the code here can never be out of sync with the
	 * dcache, so that we can safely flush the Icache and fall through
	 * into the Dcache cleaning code.
	 */
	mcr	p15, 0, r0, c7, c5, 0	/* Flush I cache */
	/* Fall through to clean Dcache. */

.Larm9_dcache_wb:
	ldr	ip, .Larm9_cache_data
	ldmia	ip, {s_max, i_max, s_inc, i_inc}
.Lnext_set:
	orr	ip, s_max, i_max
.Lnext_index:
	mcr	p15, 0, ip, c7, c10, 2	/* Clean D cache SE with Set/Index */
	sub	ip, ip, i_inc
	tst	ip, i_max		/* Index 0 is last one */
	bne	.Lnext_index		/* Next index */
	mcr	p15, 0, ip, c7, c10, 2	/* Clean D cache SE with Set/Index */
	subs	s_max, s_max, s_inc
	bpl	.Lnext_set		/* Next set */
	mov	pc, lr

.Larm9_line_size:
	.word	_C_LABEL(arm_pdcache_line_size)

ENTRY(arm9_dcache_wb_range)
	ldr	ip, .Larm9_line_size
	cmp	r1, #0x4000
	bcs	.Larm9_dcache_wb
	ldr	ip, [ip]
	sub	r3, ip, #1
	and	r2, r0, r3
	add	r1, r1, r2
	bic	r0, r0, r3
.Larm9_wb_next:
	mcr	p15, 0, r0, c7, c10, 1	/* Clean D cache SE with VA */
	add	r0, r0, ip
	subs	r1, r1, ip
	bpl	.Larm9_wb_next
	mov	pc, lr

ENTRY(arm9_dcache_wbinv_range)
	ldr	ip, .Larm9_line_size
	cmp	r1, #0x4000
	bcs	.Larm9_dcache_wbinv_all
	ldr	ip, [ip]
	sub	r3, ip, #1
	and	r2, r0, r3
	add	r1, r1, r2
	bic	r0, r0, r3
.Larm9_wbinv_next:
	mcr	p15, 0, r0, c7, c14, 1	/* Purge D cache SE with VA */
	add	r0, r0, ip
	subs	r1, r1, ip
	bpl	.Larm9_wbinv_next
	mov	pc, lr

/*
 * Note, we must not invalidate everything.  If the range is too big we
 * must use wb-inv of the entire cache.
 */
ENTRY(arm9_dcache_inv_range)
	ldr	ip, .Larm9_line_size
	cmp	r1, #0x4000
	bcs	.Larm9_dcache_wbinv_all
	ldr	ip, [ip]
	sub	r3, ip, #1
	and	r2, r0, r3
	add	r1, r1, r2
	bic	r0, r0, r3
.Larm9_inv_next:
	mcr	p15, 0, r0, c7, c6, 1	/* Invalidate D cache SE with VA */
	add	r0, r0, ip
	subs	r1, r1, ip
	bpl	.Larm9_inv_next
	mov	pc, lr

ENTRY(arm9_idcache_wbinv_range)
	ldr	ip, .Larm9_line_size
	cmp	r1, #0x4000
	bcs	.Larm9_idcache_wbinv_all
	ldr	ip, [ip]
	sub	r3, ip, #1
	and	r2, r0, r3
	add	r1, r1, r2
	bic	r0, r0, r3
.Larm9_id_wbinv_next:
	mcr	p15, 0, r0, c7, c5, 1	/* Invalidate I cache SE with VA */
	mcr	p15, 0, r0, c7, c14, 1	/* Purge D cache SE with VA */
	add	r0, r0, ip
	subs	r1, r1, ip
	bpl	.Larm9_id_wbinv_next
	mov	pc, lr

ENTRY_NP(arm9_idcache_wbinv_all)
.Larm9_idcache_wbinv_all:
	/*
	 * We assume that the code here can never be out of sync with the
	 * dcache, so that we can safely flush the Icache and fall through
	 * into the Dcache purging code.
	 */
	mcr	p15, 0, r0, c7, c5, 0	/* Flush I cache */
	/* Fall through to purge Dcache. */

ENTRY(arm9_dcache_wbinv_all)
.Larm9_dcache_wbinv_all:
	ldr	ip, .Larm9_cache_data
	ldmia	ip, {s_max, i_max, s_inc, i_inc}
.Lnext_set_inv:
	orr	ip, s_max, i_max
.Lnext_index_inv:
	mcr	p15, 0, ip, c7, c14, 2	/* Purge D cache SE with Set/Index */
	sub	ip, ip, i_inc
	tst	ip, i_max		/* Index 0 is last one */
	bne	.Lnext_index_inv		/* Next index */
	mcr	p15, 0, ip, c7, c14, 2	/* Purge D cache SE with Set/Index */
	subs	s_max, s_max, s_inc
	bpl	.Lnext_set_inv		/* Next set */
	mov	pc, lr

.Larm9_cache_data:
	.word	_C_LABEL(arm9_dcache_sets_max)

/*
 * Context switch.
 *
 * These is the CPU-specific parts of the context switcher cpu_switch()
 * These functions actually perform the TTB reload.
 */
ENTRY(arm9_context_switch)
	/*
	 * We can assume that the caches will only contain kernel addresses
	 * at this point.  So no need to flush them again.
	 */
	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
	mcr	p15, 0, r0, c2, c0, 0	/* set the new TTB */
	mcr	p15, 0, r0, c8, c7, 0	/* and flush the I+D tlbs */

	/* Paranoia -- make sure the pipeline is empty. */
	nop
	nop
	nop
	mov	pc, lr

	.bss

/* XXX The following macros should probably be moved to asm.h */
#define _DATA_OBJECT(x) .globl x; .type x,_ASM_TYPE_OBJECT; x:
#define C_OBJECT(x)	_DATA_OBJECT(_C_LABEL(x))

/*
 * Parameters for the cache cleaning code.  Note that the order of these
 * four variables is assumed in the code above.  Hence the reason for
 * declaring them in the assembler file.
 */
	.align 0
C_OBJECT(arm9_dcache_sets_max)
	.space	4
C_OBJECT(arm9_dcache_index_max)
	.space	4
C_OBJECT(arm9_dcache_sets_inc)
	.space	4
C_OBJECT(arm9_dcache_index_inc)
	.space	4


code:
/*	$OpenBSD: cpufunc_asm_armv4.S,v 1.1 2004/02/01 05:09:48 drahn Exp $	*/
/*	$NetBSD: cpufunc_asm_armv4.S,v 1.1 2001/11/10 23:14:09 thorpej Exp $	*/

/*
 * Copyright (c) 2001 ARM Limited
 * Copyright (c) 1997,1998 Mark Brinicombe.
 * Copyright (c) 1997 Causality Limited
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Causality Limited.
 * 4. The name of Causality Limited may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY CAUSALITY LIMITED ``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 CAUSALITY LIMITED 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.
 *
 * ARM9 assembly functions for CPU / MMU / TLB specific operations
 */

#include <machine/cpu.h>
#include <machine/asm.h>

/*
 * TLB functions
 */
ENTRY(armv4_tlb_flushID)
	mcr	p15, 0, r0, c8, c7, 0	/* flush I+D tlb */
	mov	pc, lr

ENTRY(armv4_tlb_flushI)
	mcr	p15, 0, r0, c8, c5, 0	/* flush I tlb */
	mov	pc, lr

ENTRY(armv4_tlb_flushD)
	mcr	p15, 0, r0, c8, c6, 0	/* flush D tlb */
	mov	pc, lr

ENTRY(armv4_tlb_flushD_SE)
	mcr	p15, 0, r0, c8, c6, 1	/* flush D tlb single entry */
	mov	pc, lr

/*
 * Other functions
 */
ENTRY(armv4_drain_writebuf)
	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */
	mov	pc, lr


code:
/*	$OpenBSD: cpufunc_asm_armv5.S,v 1.2 2010/07/01 22:40:10 drahn Exp $	*/
/*	$NetBSD: cpufunc_asm_armv5.S,v 1.3 2007/01/06 00:50:54 christos Exp $	*/

/*
 * Copyright (c) 2002, 2005 ARM Limited
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the company may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * 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 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.
 *
 * ARMv5 assembly functions for manipulating caches.
 * These routines can be used by any core that supports the set/index
 * operations.
 */

#include <machine/cpu.h>
#include <machine/asm.h>

/*
 * Functions to set the MMU Translation Table Base register
 *
 * We need to clean and flush the cache as it uses virtual
 * addresses that are about to change.
 */
ENTRY(armv5_setttb)
	stmfd	sp!, {r0, lr}
	bl	_C_LABEL(armv5_idcache_wbinv_all)
	ldmfd	sp!, {r0, lr}

	mcr	p15, 0, r0, c2, c0, 0	/* load new TTB */

	mcr	p15, 0, r0, c8, c7, 0	/* invalidate I+D TLBs */
	mov	pc, lr

/*
 * Cache operations.  For the entire cache we use the set/index
 * operations.
 */
	s_max	.req r0
	i_max	.req r1
	s_inc	.req r2
	i_inc	.req r3

ENTRY_NP(armv5_icache_sync_range)
	ldr	ip, .Larmv5_line_size
	cmp	r1, #0x4000
	bcs	.Larmv5_icache_sync_all
	ldr	ip, [ip]
	sub	r1, r1, #1		/* Don't overrun */
	sub	r3, ip, #1
	and	r2, r0, r3
	add	r1, r1, r2
	bic	r0, r0, r3
1:
	mcr	p15, 0, r0, c7, c5, 1	/* Invalidate I cache SE with VA */
	mcr	p15, 0, r0, c7, c10, 1	/* Clean D cache SE with VA */
	add	r0, r0, ip
	subs	r1, r1, ip
	bpl	1b
	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
	mov	pc, lr

ENTRY_NP(armv5_icache_sync_all)
.Larmv5_icache_sync_all:
	/*
	 * We assume that the code here can never be out of sync with the
	 * dcache, so that we can safely flush the Icache and fall through
	 * into the Dcache cleaning code.
	 */
	mcr	p15, 0, r0, c7, c5, 0	/* Flush I cache */
	/* Fall through to clean Dcache. */

.Larmv5_dcache_wb:
	ldr	ip, .Larmv5_cache_data
	ldmia	ip, {s_max, i_max, s_inc, i_inc}
1:
	orr	ip, s_max, i_max
2:
	mcr	p15, 0, ip, c7, c10, 2	/* Clean D cache SE with Set/Index */
	sub	ip, ip, i_inc
	tst	ip, i_max		/* Index 0 is last one */
	bne	2b			/* Next index */
	mcr	p15, 0, ip, c7, c10, 2	/* Clean D cache SE with Set/Index */
	subs	s_max, s_max, s_inc
	bpl	1b			/* Next set */
	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
	mov	pc, lr

.Larmv5_line_size:
	.word	_C_LABEL(arm_pdcache_line_size)

ENTRY(armv5_dcache_wb_range)
	ldr	ip, .Larmv5_line_size
	cmp	r1, #0x4000
	bcs	.Larmv5_dcache_wb
	ldr	ip, [ip]
	sub	r1, r1, #1		/* Don't overrun */
	sub	r3, ip, #1
	and	r2, r0, r3
	add	r1, r1, r2
	bic	r0, r0, r3
1:
	mcr	p15, 0, r0, c7, c10, 1	/* Clean D cache SE with VA */
	add	r0, r0, ip
	subs	r1, r1, ip
	bpl	1b
	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
	mov	pc, lr

ENTRY(armv5_dcache_wbinv_range)
	ldr	ip, .Larmv5_line_size
	cmp	r1, #0x4000
	bcs	.Larmv5_dcache_wbinv_all
	ldr	ip, [ip]
	sub	r1, r1, #1		/* Don't overrun */
	sub	r3, ip, #1
	and	r2, r0, r3
	add	r1, r1, r2
	bic	r0, r0, r3
1:
	mcr	p15, 0, r0, c7, c14, 1	/* Purge D cache SE with VA */
	add	r0, r0, ip
	subs	r1, r1, ip
	bpl	1b
	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
	mov	pc, lr

/*
 * Note, we must not invalidate everything.  If the range is too big we
 * must use wb-inv of the entire cache.
 */
ENTRY(armv5_dcache_inv_range)
	ldr	ip, .Larmv5_line_size
	cmp	r1, #0x4000
	bcs	.Larmv5_dcache_wbinv_all
	ldr	ip, [ip]
	sub	r1, r1, #1		/* Don't overrun */
	sub	r3, ip, #1
	and	r2, r0, r3
	add	r1, r1, r2
	bic	r0, r0, r3
1:
	mcr	p15, 0, r0, c7, c6, 1	/* Invalidate D cache SE with VA */
	add	r0, r0, ip
	subs	r1, r1, ip
	bpl	1b
	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
	mov	pc, lr

ENTRY(armv5_idcache_wbinv_range)
	ldr	ip, .Larmv5_line_size
	cmp	r1, #0x4000
	bcs	.Larmv5_idcache_wbinv_all
	ldr	ip, [ip]
	sub	r1, r1, #1		/* Don't overrun */
	sub	r3, ip, #1
	and	r2, r0, r3
	add	r1, r1, r2
	bic	r0, r0, r3
1:
	mcr	p15, 0, r0, c7, c5, 1	/* Invalidate I cache SE with VA */
	mcr	p15, 0, r0, c7, c14, 1	/* Purge D cache SE with VA */
	add	r0, r0, ip
	subs	r1, r1, ip
	bpl	1b
	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
	mov	pc, lr

ENTRY_NP(armv5_idcache_wbinv_all)
.Larmv5_idcache_wbinv_all:
	/*
	 * We assume that the code here can never be out of sync with the
	 * dcache, so that we can safely flush the Icache and fall through
	 * into the Dcache purging code.
	 */
	mcr	p15, 0, r0, c7, c5, 0	/* Flush I cache */
	/* Fall through to purge Dcache. */

ENTRY(armv5_dcache_wbinv_all)
.Larmv5_dcache_wbinv_all:
	ldr	ip, .Larmv5_cache_data
	ldmia	ip, {s_max, i_max, s_inc, i_inc}
1:
	orr	ip, s_max, i_max
2:
	mcr	p15, 0, ip, c7, c14, 2	/* Purge D cache SE with Set/Index */
	sub	ip, ip, i_inc
	tst	ip, i_max		/* Index 0 is last one */
	bne	2b			/* Next index */
	mcr	p15, 0, ip, c7, c14, 2	/* Purge D cache SE with Set/Index */
	subs	s_max, s_max, s_inc
	bpl	1b			/* Next set */
	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
	mov	pc, lr

.Larmv5_cache_data:
	.word	_C_LABEL(armv5_dcache_sets_max)

	.bss

/* XXX The following macros should probably be moved to asm.h */
#define _DATA_OBJECT(x) .globl x; .type x,_ASM_TYPE_OBJECT; x:
#define C_OBJECT(x)	_DATA_OBJECT(_C_LABEL(x))

/*
 * Parameters for the cache cleaning code.  Note that the order of these
 * four variables is assumed in the code above.  Hence the reason for
 * declaring them in the assembler file.
 */
	.align 2
C_OBJECT(armv5_dcache_sets_max)
	.space	4
C_OBJECT(armv5_dcache_index_max)
	.space	4
C_OBJECT(armv5_dcache_sets_inc)
	.space	4
C_OBJECT(armv5_dcache_index_inc)
	.space	4


code:
/*	$OpenBSD: cpufunc_asm_armv5_ec.S,v 1.1 2008/09/11 02:38:14 kevlo Exp $	*/
/*	$NetBSD: cpufunc_asm_armv5_ec.S,v 1.1 2007/01/06 00:50:54 christos Exp $	*/

/*
 * Copyright (c) 2002, 2005 ARM Limited
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the company may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * 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 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.
 *
 * ARMv5 assembly functions for manipulating caches.
 * These routines can be used by any core that supports both the set/index
 * operations and the test and clean operations for efficiently cleaning the
 * entire DCache.  If a core does not have the test and clean operations, but
 * does have the set/index operations, use the routines in cpufunc_asm_armv5.S.
 * This source was derived from that file.
 */

#include <machine/cpu.h>
#include <machine/asm.h>

/*
 * Functions to set the MMU Translation Table Base register
 *
 * We need to clean and flush the cache as it uses virtual
 * addresses that are about to change.
 */
ENTRY(armv5_ec_setttb)
	/*
	 * Some other ARM ports save registers on the stack, call the
	 * idcache_wbinv_all function and then restore the registers from the
	 * stack before setting the TTB.  I observed that this caused a
	 * problem when the old and new translation table entries' buffering
	 * bits were different.  If I saved the registers in other registers
	 * or invalidated the caches when I returned from idcache_wbinv_all,
	 * it worked fine.  If not, I ended up executing at an invalid PC.
	 * For armv5_ec_settb, the idcache_wbinv_all is simple enough, I just
	 * do it directly and entirely avoid the problem.
	 */
	mcr	p15, 0, r0, c7, c5, 0	/* Invalidate ICache */
1:	mrc	p15, 0, r15, c7, c14, 3	/* Test, clean and invalidate DCache */
	bne	1b			/* More to do? */
	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */

	mcr	p15, 0, r0, c2, c0, 0	/* load new TTB */

	mcr	p15, 0, r0, c8, c7, 0	/* invalidate I+D TLBs */
	mov	pc, lr

/*
 * Cache operations.  For the entire cache we use the enhanced cache
 * operations.
 */

ENTRY_NP(armv5_ec_icache_sync_range)
	ldr	ip, .Larmv5_ec_line_size
	cmp	r1, #0x4000
	bcs	.Larmv5_ec_icache_sync_all
	ldr	ip, [ip]
	sub	r1, r1, #1		/* Don't overrun */
	sub	r3, ip, #1
	and	r2, r0, r3
	add	r1, r1, r2
	bic	r0, r0, r3
1:
	mcr	p15, 0, r0, c7, c5, 1	/* Invalidate I cache SE with VA */
	mcr	p15, 0, r0, c7, c10, 1	/* Clean D cache SE with VA */
	add	r0, r0, ip
	subs	r1, r1, ip
	bpl	1b
	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
	mov	pc, lr

ENTRY_NP(armv5_ec_icache_sync_all)
.Larmv5_ec_icache_sync_all:
	/*
	 * We assume that the code here can never be out of sync with the
	 * dcache, so that we can safely flush the Icache and fall through
	 * into the Dcache cleaning code.
	 */
	mcr	p15, 0, r0, c7, c5, 0	/* Flush I cache */
	/* Fall through to clean Dcache. */

.Larmv5_ec_dcache_wb:
1:
	mrc	p15, 0, r15, c7, c10, 3	/* Test and clean (don't invalidate) */
	bne	1b			/* More to do? */
	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
	mov	pc, lr

.Larmv5_ec_line_size:
	.word	_C_LABEL(arm_pdcache_line_size)

ENTRY(armv5_ec_dcache_wb_range)
	ldr	ip, .Larmv5_ec_line_size
	cmp	r1, #0x4000
	bcs	.Larmv5_ec_dcache_wb
	ldr	ip, [ip]
	sub	r1, r1, #1		/* Don't overrun */
	sub	r3, ip, #1
	and	r2, r0, r3
	add	r1, r1, r2
	bic	r0, r0, r3
1:
	mcr	p15, 0, r0, c7, c10, 1	/* Clean D cache SE with VA */
	add	r0, r0, ip
	subs	r1, r1, ip
	bpl	1b
	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
	mov	pc, lr

ENTRY(armv5_ec_dcache_wbinv_range)
	ldr	ip, .Larmv5_ec_line_size
	cmp	r1, #0x4000
	bcs	.Larmv5_ec_dcache_wbinv_all
	ldr	ip, [ip]
	sub	r1, r1, #1		/* Don't overrun */
	sub	r3, ip, #1
	and	r2, r0, r3
	add	r1, r1, r2
	bic	r0, r0, r3
1:
	mcr	p15, 0, r0, c7, c14, 1	/* Purge D cache SE with VA */
	add	r0, r0, ip
	subs	r1, r1, ip
	bpl	1b
	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
	mov	pc, lr

/*
 * Note, we must not invalidate everything.  If the range is too big we
 * must use wb-inv of the entire cache.
 */
ENTRY(armv5_ec_dcache_inv_range)
	ldr	ip, .Larmv5_ec_line_size
	cmp	r1, #0x4000
	bcs	.Larmv5_ec_dcache_wbinv_all
	ldr	ip, [ip]
	sub	r1, r1, #1		/* Don't overrun */
	sub	r3, ip, #1
	and	r2, r0, r3
	add	r1, r1, r2
	bic	r0, r0, r3
1:
	mcr	p15, 0, r0, c7, c6, 1	/* Invalidate D cache SE with VA */
	add	r0, r0, ip
	subs	r1, r1, ip
	bpl	1b
	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
	mov	pc, lr

ENTRY(armv5_ec_idcache_wbinv_range)
	ldr	ip, .Larmv5_ec_line_size
	cmp	r1, #0x4000
	bcs	.Larmv5_ec_idcache_wbinv_all
	ldr	ip, [ip]
	sub	r1, r1, #1		/* Don't overrun */
	sub	r3, ip, #1
	and	r2, r0, r3
	add	r1, r1, r2
	bic	r0, r0, r3
1:
	mcr	p15, 0, r0, c7, c5, 1	/* Invalidate I cache SE with VA */
	mcr	p15, 0, r0, c7, c14, 1	/* Purge D cache SE with VA */
	add	r0, r0, ip
	subs	r1, r1, ip
	bpl	1b
	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
	mov	pc, lr

ENTRY_NP(armv5_ec_idcache_wbinv_all)
.Larmv5_ec_idcache_wbinv_all:
	/*
	 * We assume that the code here can never be out of sync with the
	 * dcache, so that we can safely flush the Icache and fall through
	 * into the Dcache purging code.
	 */
	mcr	p15, 0, r0, c7, c5, 0	/* Invalidate ICache */
	/* Fall through to purge Dcache. */

ENTRY(armv5_ec_dcache_wbinv_all)
.Larmv5_ec_dcache_wbinv_all:
1:	mrc	p15, 0, r15, c7, c14, 3	/* Test, clean and invalidate DCache */
	bne	1b			/* More to do? */
	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
	mov	pc, lr


code:
/* $OpenBSD: cpufunc_asm_armv7.S,v 1.8 2015/06/02 02:30:16 jsg Exp $ */
/*
 * Copyright (c) 2008 Dale Rahn <drahn@openbsd.org>
 *
 * 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/cpu.h>
#include <machine/asm.h>

ENTRY(armv7_cpu_sleep)
	wfi
	mov	pc, lr

ENTRY(armv7_drain_writebuf)
	dsb	sy
	isb	sy
	mov	pc, lr

/*
 * Function to read the MPCore base address
 */
ENTRY(armv7_periphbase)
	mrc	p15, 4, r0, c15, c0, 0
	mov	pc, lr

/*
 * Functions to set the MMU Translation Table Base register
 */
ENTRY(armv7_setttb)
	mcr	p15, 0, r0, c7, c5, 0	/* Flush I cache */
	mcr	p15, 0, r0, c7, c5, 6	/* Flush BP cache */
	dsb	sy
	isb	sy

	mcr	p15, 0, r0, c2, c0, 0	/* load new TTB */
	mcr	p15, 0, r0, c8, c7, 0	/* invalidate I+D TLBs */
	dsb	sy
	isb	sy

	mov	pc, lr

/*
 * TLB functions
 */
ENTRY(armv7_tlb_flushID_SE)
	mcr	p15, 0, r0, c8, c6, 1	/* flush D tlb single entry */
	mcr	p15, 0, r0, c8, c5, 1	/* flush I tlb single entry */
	mcr	p15, 0, r0, c7, c5, 7	/* flush va from BP */
	dsb	sy
	isb	sy
	mov	pc, lr

ENTRY(armv7_tlb_flushI_SE)
	mcr	p15, 0, r0, c8, c5, 1	/* flush I tlb single entry */
	mcr	p15, 0, r0, c7, c5, 7	/* flush va from BP */
	dsb	sy
	isb	sy
	mov	pc, lr

/*
 * TLB functions
 */
ENTRY(armv7_tlb_flushID)
	mcr	p15, 0, r0, c8, c7, 0	/* flush I+D tlb */
	mcr	p15, 0, r0, c7, c5, 6	/* Flush BP cache */
	dsb	sy
	isb	sy
	mov	pc, lr

ENTRY(armv7_tlb_flushI)
	mcr	p15, 0, r0, c8, c5, 0	/* flush I tlb */
	mcr	p15, 0, r0, c7, c5, 6	/* Flush BP cache */
	dsb	sy
	isb	sy
	mov	pc, lr

ENTRY(armv7_tlb_flushD)
	mcr	p15, 0, r0, c8, c6, 0	/* flush D tlb */
	dsb	sy
	isb	sy
	mov	pc, lr

ENTRY(armv7_tlb_flushD_SE)
	mcr	p15, 0, r0, c8, c6, 1	/* flush D tlb single entry */
	dsb	sy
	isb	sy
	mov	pc, lr

/*
 * Cache operations.  For the entire cache we use the set/index
 * operations.
 */
	s_max	.req r0
	i_max	.req r1
	s_inc	.req r2
	i_inc	.req r3
ENTRY(armv7_icache_sync_range)
	ldr	ip, .Larmv7_line_size
	cmp	r1, #0x8000
	movcs	r1, #0x8000	/* XXX needs to match cache size... */
	ldr	ip, [ip]
	sub	r1, r1, #1		/* Don't overrun */
	sub	r3, ip, #1
	and	r2, r0, r3
	add	r1, r1, r2
	bic	r0, r0, r3
1:
	mcr	p15, 0, r0, c7, c11, 1	/* Clean D cache SE with VA to PoU */
	mcr	p15, 0, r0, c7, c5, 1	/* Invalidate I cache SE with VA */
	add	r0, r0, ip
	subs	r1, r1, ip
	bhi	1b
	mcr	p15, 0, r0, c7, c5, 6	/* Flush BP cache */
	dsb	sy
	isb	sy
	mov	pc, lr

ENTRY(armv7_icache_sync_all)
.Larmv7_icache_sync_all:
	/*
	 * We assume that the code here can never be out of sync with the
	 * dcache, so that we can safely flush the Icache and fall through
	 * into the Dcache cleaning code.
	 */
	mcr	p15, 0, r0, c7, c5, 0	/* Flush I cache */
	mcr	p15, 0, r0, c7, c5, 6	/* Flush BP cache */
	isb	sy
	mov	pc, lr

.Larmv7_line_size:
	.word	_C_LABEL(arm_pdcache_line_size)

ENTRY(armv7_dcache_wb_range)
	ldr	ip, .Larmv7_line_size
	cmp	r1, #0x8000
	movcs	r1, #0x8000	/* XXX needs to match cache size... */
	ldr	ip, [ip]
	sub	r1, r1, #1		/* Don't overrun */
	sub	r3, ip, #1
	and	r2, r0, r3
	add	r1, r1, r2
	bic	r0, r0, r3
1:
	mcr	p15, 0, r0, c7, c10, 1	/* Clean D cache SE with VA */
	add	r0, r0, ip
	subs	r1, r1, ip
	bhi	1b
	dsb	sy
	isb	sy
	mov	pc, lr

ENTRY(armv7_idcache_wbinv_range)
	ldr	ip, .Larmv7_line_size
	cmp	r1, #0x8000
	movcs	r1, #0x8000	/* XXX needs to match cache size... */
	ldr	ip, [ip]
	sub	r1, r1, #1		/* Don't overrun */
	sub	r3, ip, #1
	and	r2, r0, r3
	add	r1, r1, r2
	bic	r0, r0, r3
1:
	mcr	p15, 0, r0, c7, c11, 1	/* Clean D cache SE with VA to PoU */
	mcr	p15, 0, r0, c7, c5, 1	/* Invalidate I cache SE with VA */
	mcr	p15, 0, r0, c7, c14, 1	/* Purge D cache SE with VA */
	add	r0, r0, ip
	subs	r1, r1, ip
	bhi	1b
	mcr	p15, 0, r0, c7, c5, 6	/* Flush BP cache */
	dsb	sy
	isb	sy
	mov	pc, lr

ENTRY(armv7_dcache_wbinv_range)
	ldr	ip, .Larmv7_line_size
	cmp	r1, #0x8000
	movcs	r1, #0x8000	/* XXX needs to match cache size... */
	ldr	ip, [ip]
	sub	r1, r1, #1		/* Don't overrun */
	sub	r3, ip, #1
	and	r2, r0, r3
	add	r1, r1, r2
	bic	r0, r0, r3
1:
	mcr	p15, 0, r0, c7, c11, 1	/* Clean D cache SE with VA to PoU */
	mcr	p15, 0, r0, c7, c5, 1	/* Invalidate I cache SE with VA */
	mcr	p15, 0, r0, c7, c14, 1	/* Purge D cache SE with VA */
	add	r0, r0, ip
	subs	r1, r1, ip
	bhi	1b
	dsb	sy
	isb	sy
	mov	pc, lr

ENTRY(armv7_dcache_inv_range)
	ldr	ip, .Larmv7_line_size
	cmp	r1, #0x8000
	movcs	r1, #0x8000	/* XXX needs to match cache size... */
	ldr	ip, [ip]
	sub	r1, r1, #1		/* Don't overrun */
	sub	r3, ip, #1
	and	r2, r0, r3
	add	r1, r1, r2
	bic	r0, r0, r3
1:
	mcr	p15, 0, r0, c7, c11, 1	/* Clean D cache SE with VA to PoU */
	mcr	p15, 0, r0, c7, c5, 1	/* Invalidate I cache SE with VA */
	mcr	p15, 0, r0, c7, c6, 1	/* Invalidate D cache SE with VA */
	add	r0, r0, ip
	subs	r1, r1, ip
	bhi	1b
	dsb	sy
	isb	sy
	mov	pc, lr

/*
 * Context switch.
 *
 * These is the CPU-specific parts of the context switcher cpu_switch()
 * These functions actually perform the TTB reload.
 *
 * NOTE: Special calling convention
 *	r1, r4-r13 must be preserved
 */
ENTRY(armv7_context_switch)
	/*
	 * We can assume that the caches will only contain kernel addresses
	 * at this point.  So no need to flush them again.
	 */
	mcr	p15, 0, r0, c7, c5, 0	/* Flush I cache */
	mcr	p15, 0, r0, c7, c5, 6	/* Flush BP cache */
	dsb	sy
	isb	sy

	mcr	p15, 0, r0, c2, c0, 0	/* set the new TTB */
	mcr	p15, 0, r0, c8, c7, 0	/* and flush the I+D tlbs */
	dsb	sy
	isb	sy
	mov	pc, lr

/* XXX The following macros should probably be moved to asm.h */
#define _DATA_OBJECT(x) .globl x; .type x,_ASM_TYPE_OBJECT; x:
#define C_OBJECT(x)	_DATA_OBJECT(_C_LABEL(x))

	.align 2
C_OBJECT(armv7_dcache_sets_max)
	.word	0
C_OBJECT(armv7_dcache_index_max)
	.word	0
C_OBJECT(armv7_dcache_sets_inc)
	.word	0
C_OBJECT(armv7_dcache_index_inc)
	.word	0


code:
/*	$OpenBSD: cpufunc_asm_sa1.S,v 1.5 2015/01/18 14:55:02 jsg Exp $	*/
/*	$NetBSD: cpufunc_asm_sa1.S,v 1.8 2002/08/17 16:36:32 thorpej Exp $	*/

/*
 * Copyright (c) 1997,1998 Mark Brinicombe.
 * Copyright (c) 1997 Causality Limited
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Causality Limited.
 * 4. The name of Causality Limited may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY CAUSALITY LIMITED ``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 CAUSALITY LIMITED 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.
 *
 * SA-1 assembly functions for CPU / MMU / TLB specific operations
 */

#include <machine/cpu.h>
#include <machine/asm.h>

/*
 * Functions to set the MMU Translation Table Base register
 *
 * We need to clean and flush the cache as it uses virtual
 * addresses that are about to change.
 */
ENTRY(sa1_setttb)
	mrs	r3, cpsr
	orr	r1, r3, #(I32_bit | F32_bit)
	msr	cpsr_c, r1

	stmfd	sp!, {r0-r3, lr}
	bl	_C_LABEL(sa1_cache_cleanID)
	ldmfd	sp!, {r0-r3, lr}
	mcr	p15, 0, r0, c7, c5, 0	/* invalidate I$ and BTB */
	mcr	p15, 0, r0, c7, c10, 4	/* drain write and fill buffer */

	/* Write the TTB */
	mcr	p15, 0, r0, c2, c0, 0

	/* If we have updated the TTB we must flush the TLB */
	mcr	p15, 0, r0, c8, c7, 0	/* invalidate I+D TLB */

	/* The cleanID above means we only need to flush the I cache here */
	mcr	p15, 0, r0, c7, c5, 0	/* invalidate I$ and BTB */

	/* Make sure that pipeline is emptied */
	mov	r0, r0
	mov	r0, r0

	msr	cpsr_c, r3
	mov	pc, lr

/*
 * TLB functions
 */
ENTRY(sa1_tlb_flushID_SE)
	mcr	p15, 0, r0, c8, c6, 1	/* flush D tlb single entry */
	mcr	p15, 0, r0, c8, c5, 0	/* flush I tlb */
	mov	pc, lr

/*
 * Cache functions
 */
ENTRY(sa1_cache_flushID)
	mcr	p15, 0, r0, c7, c7, 0	/* flush I+D cache */
	mov	pc, lr

ENTRY(sa1_cache_flushI)
	mcr	p15, 0, r0, c7, c5, 0	/* flush I cache */
	mov	pc, lr

ENTRY(sa1_cache_flushD)
	mcr	p15, 0, r0, c7, c6, 0	/* flush D cache */
	mov	pc, lr

ENTRY(sa1_cache_flushD_SE)
	mcr	p15, 0, r0, c7, c6, 1	/* flush D cache single entry */
	mov	pc, lr

ENTRY(sa1_cache_cleanD_E)
	mcr	p15, 0, r0, c7, c10, 1	/* clean D cache entry */
	mov	pc, lr

/*
 * Information for the SA-1 cache clean/purge functions:
 *
 *	* Virtual address of the memory region to use
 *	* Size of memory region
 */
	.data

	.global	_C_LABEL(sa1_cache_clean_addr)
_C_LABEL(sa1_cache_clean_addr):
	.word	0xf0000000

	.global	_C_LABEL(sa1_cache_clean_size)
_C_LABEL(sa1_cache_clean_size):
#if defined(CPU_SA1100) || defined(CPU_SA1110)
	.word	0x00004000
#else
	.word	0x00008000
#endif

	.text

.Lsa1_cache_clean_addr:
	.word	_C_LABEL(sa1_cache_clean_addr)
.Lsa1_cache_clean_size:
	.word	_C_LABEL(sa1_cache_clean_size)

#define	SA1_CACHE_CLEAN_BLOCK						\
	mrs	r3, cpsr					;	\
	orr	r0, r3, #(I32_bit | F32_bit)			;	\
	msr	cpsr_c, r0

#define	SA1_CACHE_CLEAN_UNBLOCK						\
	msr	cpsr_c, r3

#ifdef DOUBLE_CACHE_CLEAN_BANK
#define	SA1_DOUBLE_CACHE_CLEAN_BANK					\
	eor	r0, r0, r1					;	\
	str	r0, [r2]
#else
#define	SA1_DOUBLE_CACHE_CLEAN_BANK	/* nothing */
#endif /* DOUBLE_CACHE_CLEAN_BANK */

#define	SA1_CACHE_CLEAN_PROLOGUE					\
	SA1_CACHE_CLEAN_BLOCK					;	\
	ldr	r2, .Lsa1_cache_clean_addr			;	\
	ldmia	r2, {r0, r1}					;	\
	SA1_DOUBLE_CACHE_CLEAN_BANK

#define	SA1_CACHE_CLEAN_EPILOGUE					\
	SA1_CACHE_CLEAN_UNBLOCK

ENTRY_NP(sa1_cache_syncI)
ENTRY_NP(sa1_cache_purgeID)
	mcr	p15, 0, r0, c7, c5, 0	/* flush I cache (D cleaned below) */
ENTRY_NP(sa1_cache_cleanID)
ENTRY_NP(sa1_cache_purgeD)
ENTRY(sa1_cache_cleanD)
	SA1_CACHE_CLEAN_PROLOGUE

1:	ldr	r2, [r0], #32
	subs	r1, r1, #32
	bne	1b

	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */

	SA1_CACHE_CLEAN_EPILOGUE
	mov	pc, lr

ENTRY(sa1_cache_purgeID_E)
	mcr	p15, 0, r0, c7, c10, 1	/* clean dcache entry */
	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */
	mcr	p15, 0, r0, c7, c5, 0	/* flush I cache */
	mcr	p15, 0, r0, c7, c6, 1	/* flush D cache single entry */
	mov	pc, lr

ENTRY(sa1_cache_purgeD_E)
	mcr	p15, 0, r0, c7, c10, 1	/* clean dcache entry */
	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */
	mcr	p15, 0, r0, c7, c6, 1	/* flush D cache single entry */
	mov	pc, lr

/*
 * Soft functions
 */
/* sa1_cache_syncI is identical to sa1_cache_purgeID */

ENTRY(sa1_cache_cleanID_rng)
ENTRY(sa1_cache_cleanD_rng)
	cmp	r1, #0x4000
	bcs	_C_LABEL(sa1_cache_cleanID)

	and	r2, r0, #0x1f
	add	r1, r1, r2
	bic	r0, r0, #0x1f

1:	mcr	p15, 0, r0, c7, c10, 1	/* clean D cache entry */
	add	r0, r0, #32
	subs	r1, r1, #32
	bhi	1b

	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */
	mov	pc, lr

ENTRY(sa1_cache_purgeID_rng)
	cmp	r1, #0x4000
	bcs	_C_LABEL(sa1_cache_purgeID)

	and	r2, r0, #0x1f
	add	r1, r1, r2
	bic	r0, r0, #0x1f

1:	mcr	p15, 0, r0, c7, c10, 1	/* clean D cache entry */
	mcr	p15, 0, r0, c7, c6, 1	/* flush D cache single entry */
	add	r0, r0, #32
	subs	r1, r1, #32
	bhi	1b

	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */
	mcr	p15, 0, r0, c7, c5, 0	/* flush I cache */
	mov	pc, lr

ENTRY(sa1_cache_purgeD_rng)
	cmp	r1, #0x4000
	bcs	_C_LABEL(sa1_cache_purgeD)

	and	r2, r0, #0x1f
	add	r1, r1, r2
	bic	r0, r0, #0x1f

1:	mcr	p15, 0, r0, c7, c10, 1	/* clean D cache entry */
	mcr	p15, 0, r0, c7, c6, 1	/* flush D cache single entry */
	add	r0, r0, #32
	subs	r1, r1, #32
	bhi	1b

	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */
	mov	pc, lr

ENTRY(sa1_cache_syncI_rng)
	cmp	r1, #0x4000
	bcs	_C_LABEL(sa1_cache_syncI)

	and	r2, r0, #0x1f
	add	r1, r1, r2
	bic	r0, r0, #0x1f

1:	mcr	p15, 0, r0, c7, c10, 1	/* clean D cache entry */
	add	r0, r0, #32
	subs	r1, r1, #32
	bhi	1b

	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */
	mcr	p15, 0, r0, c7, c5, 0	/* flush I cache */

	mov	pc, lr

/*
 * Context switch.
 *
 * These is the CPU-specific parts of the context switcher cpu_switch()
 * These functions actually perform the TTB reload.
 *
 * NOTE: Special calling convention
 *	r1, r4-r13 must be preserved
 */
#if defined(CPU_SA110)
ENTRY(sa110_context_switch)
	/*
	 * CF_CACHE_PURGE_ID will *ALWAYS* be called prior to this.
	 * Thus the data cache will contain only kernel data and the
	 * instruction cache will contain only kernel code, and all
	 * kernel mappings are shared by all processes.
	 */

	/* Write the TTB */
	mcr	p15, 0, r0, c2, c0, 0

	/* If we have updated the TTB we must flush the TLB */
	mcr	p15, 0, r0, c8, c7, 0	/* flush the I+D tlb */

	/* Make sure that pipeline is emptied */
	mov	r0, r0
	mov	r0, r0
	mov	pc, lr
#endif


code:
/*	$OpenBSD: cpufunc_asm_xscale.S,v 1.6 2015/01/18 14:55:02 jsg Exp $ */
/*	$NetBSD: cpufunc_asm_xscale.S,v 1.16 2002/08/17 16:36:32 thorpej Exp $	*/

/*
 * Copyright (c) 2001, 2002 Wasabi Systems, Inc.
 * All rights reserved.
 *
 * Written by Allen Briggs and Jason R. Thorpe for Wasabi Systems, Inc.
 *
 * 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.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed for the NetBSD Project by
 *	Wasabi Systems, Inc.
 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
 *    or promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
 * 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.
 */

/*
 * Copyright (c) 2001 Matt Thomas.
 * Copyright (c) 1997,1998 Mark Brinicombe.
 * Copyright (c) 1997 Causality Limited
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Causality Limited.
 * 4. The name of Causality Limited may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY CAUSALITY LIMITED ``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 CAUSALITY LIMITED 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.
 *
 * XScale assembly functions for CPU / MMU / TLB specific operations
 */

#include <machine/cpu.h>
#include <machine/asm.h>

/*
 * Size of the XScale core caches.
 */
#define	CACHE_SIZE		0x8000

/*
 * CPWAIT -- Canonical method to wait for CP15 update.
 * From: Intel 80200 manual, section 2.3.3.
 *
 * NOTE: Clobbers the specified temp reg.
 */
#define	CPWAIT_BRANCH							 \
	sub	pc, pc, #4

#define	CPWAIT(tmp)							 \
	mrc	p15, 0, tmp, c2, c0, 0	/* arbitrary read of CP15 */	;\
	mov	tmp, tmp		/* wait for it to complete */	;\
	CPWAIT_BRANCH			/* branch to next insn */

#define	CPWAIT_AND_RETURN_SHIFTER	lsr #32

#define	CPWAIT_AND_RETURN(tmp)						 \
	mrc	p15, 0, tmp, c2, c0, 0	/* arbitrary read of CP15 */	;\
	/* Wait for it to complete and branch to the return address */	 \
	sub	pc, lr, tmp, CPWAIT_AND_RETURN_SHIFTER

ENTRY(xscale_cpwait)
	CPWAIT_AND_RETURN(r0)

/*
 * We need a separate cpu_control() entry point, since we have to
 * invalidate the Branch Target Buffer in the event the BPRD bit
 * changes in the control register.
 */
ENTRY(xscale_control)
	mrc	p15, 0, r3, c1, c0, 0	/* Read the control register */
	bic	r2, r3, r0		/* Clear bits */
	eor	r2, r2, r1		/* XOR bits */

	teq	r2, r3			/* Only write if there was a change */
	mcrne	p15, 0, r0, c7, c5, 6	/* Invalidate the BTB */
	mcrne	p15, 0, r2, c1, c0, 0	/* Write new control register */
	mov	r0, r3			/* Return old value */

	CPWAIT_AND_RETURN(r1)

/*
 * Functions to set the MMU Translation Table Base register
 *
 * We need to clean and flush the cache as it uses virtual
 * addresses that are about to change.
 */
ENTRY(xscale_setttb)
	mrs	r3, cpsr
	orr	r1, r3, #(I32_bit | F32_bit)
	msr	cpsr_c, r1

	stmfd	sp!, {r0-r3, lr}
	bl	_C_LABEL(xscale_cache_cleanID)
	mcr	p15, 0, r0, c7, c5, 0	/* invalidate I$ and BTB */
	mcr	p15, 0, r0, c7, c10, 4	/* drain write and fill buffer */

	CPWAIT(r0)

	ldmfd	sp!, {r0-r3, lr}

	/* Write the TTB */
	mcr	p15, 0, r0, c2, c0, 0

	/* If we have updated the TTB we must flush the TLB */
	mcr	p15, 0, r0, c8, c7, 0	/* invalidate I+D TLB */

	/* The cleanID above means we only need to flush the I cache here */
	mcr	p15, 0, r0, c7, c5, 0	/* invalidate I$ and BTB */

	CPWAIT(r0)

	msr	cpsr_c, r3
	mov	pc, lr

/*
 * TLB functions
 *
 * Note: We don't need to worry about issuing a CPWAIT after
 * TLB operations, because we expect a pmap_update() to follow.
 */
ENTRY(xscale_tlb_flushID_SE)
	mcr	p15, 0, r0, c8, c6, 1	/* flush D tlb single entry */
	mcr	p15, 0, r0, c8, c5, 1	/* flush I tlb single entry */
	mov	pc, lr

/*
 * Cache functions
 */
ENTRY(xscale_cache_flushID)
	mcr	p15, 0, r0, c7, c7, 0	/* flush I+D cache */
	CPWAIT_AND_RETURN(r0)

ENTRY(xscale_cache_flushI)
	mcr	p15, 0, r0, c7, c5, 0	/* flush I cache */
	CPWAIT_AND_RETURN(r0)

ENTRY(xscale_cache_flushD)
	mcr	p15, 0, r0, c7, c6, 0	/* flush D cache */
	CPWAIT_AND_RETURN(r0)

ENTRY(xscale_cache_flushI_SE)
	mcr	p15, 0, r0, c7, c5, 1	/* flush I cache single entry */
	CPWAIT_AND_RETURN(r0)

ENTRY(xscale_cache_flushD_SE)
	/*
	 * Errata (rev < 2): Must clean-dcache-line to an address
	 * before invalidate-dcache-line to an address, or dirty
	 * bits will not be cleared in the dcache array.
	 */
	mcr	p15, 0, r0, c7, c10, 1
	mcr	p15, 0, r0, c7, c6, 1	/* flush D cache single entry */
	CPWAIT_AND_RETURN(r0)

ENTRY(xscale_cache_cleanD_E)
	mcr	p15, 0, r0, c7, c10, 1	/* clean D cache entry */
	CPWAIT_AND_RETURN(r0)

/*
 * Information for the XScale cache clean/purge functions:
 *
 *	* Virtual address of the memory region to use
 *	* Size of memory region
 *
 * Note the virtual address for the Data cache clean operation
 * does not need to be backed by physical memory, since no loads
 * will actually be performed by the allocate-line operation.
 *
 * Note that the Mini-Data cache MUST be cleaned by executing
 * loads from memory mapped into a region reserved exclusively
 * for cleaning of the Mini-Data cache.
 */
	.data

	.global	_C_LABEL(xscale_cache_clean_addr)
_C_LABEL(xscale_cache_clean_addr):
	.word	0x00000000

	.global	_C_LABEL(xscale_cache_clean_size)
_C_LABEL(xscale_cache_clean_size):
	.word	CACHE_SIZE

#	.global	_C_LABEL(xscale_minidata_clean_addr)
#_C_LABEL(xscale_minidata_clean_addr):
#	.word	0x00000000

#	.global	_C_LABEL(xscale_minidata_clean_size)
#_C_LABEL(xscale_minidata_clean_size):
#	.word	0x00000800

	.text

.Lxscale_cache_clean_addr:
	.word	_C_LABEL(xscale_cache_clean_addr)
.Lxscale_cache_clean_size:
	.word	_C_LABEL(xscale_cache_clean_size)

.Lxscale_minidata_clean_addr:
	.word	_C_LABEL(xscale_minidata_clean_addr)
.Lxscale_minidata_clean_size:
	.word	_C_LABEL(xscale_minidata_clean_size)

#define	XSCALE_CACHE_CLEAN_BLOCK					\
	mrs	r3, cpsr					;	\
	orr	r0, r3, #(I32_bit | F32_bit)			;	\
	msr	cpsr_c, r0

#define	XSCALE_CACHE_CLEAN_UNBLOCK					\
	msr	cpsr_c, r3

#define	XSCALE_CACHE_CLEAN_PROLOGUE					\
	XSCALE_CACHE_CLEAN_BLOCK				;	\
	ldr	r2, .Lxscale_cache_clean_addr			;	\
	ldmia	r2, {r0, r1}					;	\
	/*								\
	 * BUG ALERT!							\
	 *								\
	 * The XScale core has a strange cache eviction bug, which	\
	 * requires us to use 2x the cache size for the cache clean	\
	 * and for that area to be aligned to 2 * cache size.		\
	 *								\
	 * The work-around is to use 2 areas for cache clean, and to	\
	 * alternate between them whenever this is done.  No one knows	\
	 * why the work-around works (mmm!).				\
	 */								\
	eor	r0, r0, #(CACHE_SIZE)				;	\
	str	r0, [r2]					;	\
	add	r0, r0, r1

#define	XSCALE_CACHE_CLEAN_EPILOGUE					\
	XSCALE_CACHE_CLEAN_UNBLOCK

ENTRY_NP(xscale_cache_syncI)
ENTRY_NP(xscale_cache_purgeID)
	mcr	p15, 0, r0, c7, c5, 0	/* flush I cache (D cleaned below) */
ENTRY_NP(xscale_cache_cleanID)
ENTRY_NP(xscale_cache_purgeD)
ENTRY(xscale_cache_cleanD)
	XSCALE_CACHE_CLEAN_PROLOGUE

1:	subs	r0, r0, #32
	mcr	p15, 0, r0, c7, c2, 5	/* allocate cache line */
	subs	r1, r1, #32
	bne	1b

	CPWAIT(r0)

	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */

	CPWAIT(r0)

	XSCALE_CACHE_CLEAN_EPILOGUE
	mov	pc, lr

/*
 * Clean the mini-data cache.
 *
 * It's expected that we only use the mini-data cache for
 * kernel addresses, so there is no need to purge it on
 * context switch, and no need to prevent userspace access
 * while we clean it.
 */
ENTRY(xscale_cache_clean_minidata)
	ldr	r2, .Lxscale_minidata_clean_addr
	ldmia	r2, {r0, r1}
1:	ldr	r3, [r0], #32
	subs	r1, r1, #32
	bne	1b

	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */

	CPWAIT_AND_RETURN(r1)

ENTRY(xscale_cache_purgeID_E)
	mcr	p15, 0, r0, c7, c10, 1	/* clean D cache entry */
	CPWAIT(r1)
	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */
	mcr	p15, 0, r0, c7, c5, 1	/* flush I cache single entry */
	mcr	p15, 0, r0, c7, c6, 1	/* flush D cache single entry */
	CPWAIT_AND_RETURN(r1)

ENTRY(xscale_cache_purgeD_E)
	mcr	p15, 0, r0, c7, c10, 1	/* clean D cache entry */
	CPWAIT(r1)
	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */
	mcr	p15, 0, r0, c7, c6, 1	/* flush D cache single entry */
	CPWAIT_AND_RETURN(r1)

/*
 * Soft functions
 */
/* xscale_cache_syncI is identical to xscale_cache_purgeID */

ENTRY(xscale_cache_cleanID_rng)
ENTRY(xscale_cache_cleanD_rng)
	cmp	r1, #0x4000
	bcs	_C_LABEL(xscale_cache_cleanID)

	and	r2, r0, #0x1f
	add	r1, r1, r2
	bic	r0, r0, #0x1f

1:	mcr	p15, 0, r0, c7, c10, 1	/* clean D cache entry */
	add	r0, r0, #32
	subs	r1, r1, #32
	bhi	1b

	CPWAIT(r0)

	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */

	CPWAIT_AND_RETURN(r0)

ENTRY(xscale_cache_purgeID_rng)
	cmp	r1, #0x4000
	bcs	_C_LABEL(xscale_cache_purgeID)

	and	r2, r0, #0x1f
	add	r1, r1, r2
	bic	r0, r0, #0x1f

1:	mcr	p15, 0, r0, c7, c10, 1	/* clean D cache entry */
	mcr	p15, 0, r0, c7, c6, 1	/* flush D cache single entry */
	mcr	p15, 0, r0, c7, c5, 1	/* flush I cache single entry */
	add	r0, r0, #32
	subs	r1, r1, #32
	bhi	1b

	CPWAIT(r0)

	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */

	CPWAIT_AND_RETURN(r0)

ENTRY(xscale_cache_purgeD_rng)
	cmp	r1, #0x4000
	bcs	_C_LABEL(xscale_cache_purgeD)

	and	r2, r0, #0x1f
	add	r1, r1, r2
	bic	r0, r0, #0x1f

1:	mcr	p15, 0, r0, c7, c10, 1	/* clean D cache entry */
	mcr	p15, 0, r0, c7, c6, 1	/* flush D cache single entry */
	add	r0, r0, #32
	subs	r1, r1, #32
	bhi	1b

	CPWAIT(r0)

	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */

	CPWAIT_AND_RETURN(r0)

ENTRY(xscale_cache_syncI_rng)
	cmp	r1, #0x4000
	bcs	_C_LABEL(xscale_cache_syncI)

	and	r2, r0, #0x1f
	add	r1, r1, r2
	bic	r0, r0, #0x1f

1:	mcr	p15, 0, r0, c7, c10, 1	/* clean D cache entry */
	mcr	p15, 0, r0, c7, c5, 1	/* flush I cache single entry */
	add	r0, r0, #32
	subs	r1, r1, #32
	bhi	1b

	CPWAIT(r0)

	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */

	CPWAIT_AND_RETURN(r0)

ENTRY(xscale_cache_flushD_rng)
	cmp	r1, #(CACHE_SIZE)
	movcs	r1, #(CACHE_SIZE)

	and	r2, r0, #0x1f
	add	r1, r1, r2
	bic	r0, r0, #0x1f

1:	mcr	p15, 0, r0, c7, c6, 1	/* flush D cache single entry */
	add	r0, r0, #32
	subs	r1, r1, #32
	bhi	1b

	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */

	CPWAIT_AND_RETURN(r0)

/*
 * Context switch.
 *
 * These is the CPU-specific parts of the context switcher cpu_switch()
 * These functions actually perform the TTB reload.
 *
 * NOTE: Special calling convention
 *	r1, r4-r13 must be preserved
 */
ENTRY(xscale_context_switch)
	/*
	 * CF_CACHE_PURGE_ID will *ALWAYS* be called prior to this.
	 * Thus the data cache will contain only kernel data and the
	 * instruction cache will contain only kernel code, and all
	 * kernel mappings are shared by all processes.
	 */

	/* Write the TTB */
	mcr	p15, 0, r0, c2, c0, 0

	/* If we have updated the TTB we must flush the TLB */
	mcr	p15, 0, r0, c8, c7, 0	/* flush the I+D tlb */

	CPWAIT_AND_RETURN(r0)

/*
 * xscale_cpu_sleep
 *
 * This is called when there is nothing on any of the run queues.
 * We go into IDLE mode so that any IRQ or FIQ will awaken us.
 *
 * If this is called with anything other than ARM_SLEEP_MODE_IDLE,
 * ignore it.
 */
ENTRY(xscale_cpu_sleep)
	tst	r0, #0x00000000
	bne	1f
	mov	r0, #0x1
	mcr	p14, 0, r0, c7, c0, 0

1:
	mov	pc, lr