#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: cache.c,v 1.102 2021/01/24 07:36:54 mrg Exp $");
#include "opt_multiprocessor.h"
#include "opt_sparc_arch.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <uvm/uvm_extern.h>
#include <machine/ctlreg.h>
#include <machine/pte.h>
#include <machine/locore.h>
#include <sparc/sparc/asm.h>
#include <sparc/sparc/cache.h>
#include <sparc/sparc/cpuvar.h>
struct evcnt vcache_flush_pg =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC,0,"vcfl","pg");
EVCNT_ATTACH_STATIC(vcache_flush_pg);
struct evcnt vcache_flush_seg =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC,0,"vcfl","seg");
EVCNT_ATTACH_STATIC(vcache_flush_seg);
struct evcnt vcache_flush_reg =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC,0,"vcfl","reg");
EVCNT_ATTACH_STATIC(vcache_flush_reg);
struct evcnt vcache_flush_ctx =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC,0,"vcfl","ctx");
EVCNT_ATTACH_STATIC(vcache_flush_ctx);
struct evcnt vcache_flush_range =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC,0,"vcfl","rng");
EVCNT_ATTACH_STATIC(vcache_flush_range);
int cache_alias_dist;
int cache_alias_bits;
u_long dvma_cachealign;
void
sun4_cache_enable(void)
{
u_int i, lim, ls, ts;
cache_alias_bits = CPU_ISSUN4
? CACHE_ALIAS_BITS_SUN4
: CACHE_ALIAS_BITS_SUN4C;
cache_alias_dist = CPU_ISSUN4
? CACHE_ALIAS_DIST_SUN4
: CACHE_ALIAS_DIST_SUN4C;
ls = CACHEINFO.c_linesize;
ts = CACHEINFO.c_totalsize;
for (i = AC_CACHETAGS, lim = i + ts; i < lim; i += ls)
sta(i, ASI_CONTROL, 0);
stba(AC_SYSENABLE, ASI_CONTROL,
lduba(AC_SYSENABLE, ASI_CONTROL) | SYSEN_CACHE);
CACHEINFO.c_enabled = 1;
#ifdef notyet
if (cpuinfo.flags & SUN4_IOCACHE) {
stba(AC_SYSENABLE, ASI_CONTROL,
lduba(AC_SYSENABLE, ASI_CONTROL) | SYSEN_IOCACHE);
printf("iocache enabled\n");
}
#endif
}
#if defined(SUN4M) || defined(SUN4D)
void
ms1_cache_enable(void)
{
u_int pcr;
cache_alias_dist = uimax(
CACHEINFO.ic_totalsize / CACHEINFO.ic_associativity,
CACHEINFO.dc_totalsize / CACHEINFO.dc_associativity);
cache_alias_bits = (cache_alias_dist - 1) & ~PGOFSET;
pcr = lda(SRMMU_PCR, ASI_SRMMU);
if ((pcr & MS1_PCR_ICE) == 0)
sta(0, ASI_ICACHECLR, 0);
if ((pcr & MS1_PCR_DCE) == 0)
sta(0, ASI_DCACHECLR, 0);
sta(SRMMU_PCR, ASI_SRMMU, pcr | MS1_PCR_DCE | MS1_PCR_ICE);
CACHEINFO.c_enabled = CACHEINFO.dc_enabled = 1;
CACHEINFO.c_flags |= CACHE_MANDATORY;
}
void
viking_cache_enable(void)
{
u_int pcr;
pcr = lda(SRMMU_PCR, ASI_SRMMU);
if ((pcr & VIKING_PCR_ICE) == 0) {
sta(0x80000000, ASI_ICACHECLR, 0);
sta(0, ASI_ICACHECLR, 0);
}
if ((pcr & VIKING_PCR_DCE) == 0) {
sta(0x80000000, ASI_DCACHECLR, 0);
sta(0, ASI_DCACHECLR, 0);
}
sta(SRMMU_PCR, ASI_SRMMU, pcr | VIKING_PCR_DCE | VIKING_PCR_ICE);
CACHEINFO.c_enabled = CACHEINFO.dc_enabled = 1;
if (cpuinfo.mxcc && CACHEINFO.ec_totalsize > 0) {
stda(MXCC_CTRLREG, ASI_CONTROL,
ldda(MXCC_CTRLREG, ASI_CONTROL) | MXCC_CTRLREG_CE);
CACHEINFO.c_flags |= CACHE_PAGETABLES;
CACHEINFO.ec_enabled = 1;
}
}
void
hypersparc_cache_enable(void)
{
int i, ls, ts;
u_int pcr, v;
int alias_dist;
alias_dist = CACHEINFO.c_totalsize;
if (alias_dist > cache_alias_dist) {
cache_alias_dist = alias_dist;
cache_alias_bits = (alias_dist - 1) & ~PGOFSET;
dvma_cachealign = cache_alias_dist;
}
ls = CACHEINFO.c_linesize;
ts = CACHEINFO.c_totalsize;
pcr = lda(SRMMU_PCR, ASI_SRMMU);
if ((pcr & HYPERSPARC_PCR_CE) == 0)
for (i = 0; i < ts; i += ls)
sta(i, ASI_DCACHETAG, 0);
pcr &= ~(HYPERSPARC_PCR_CE | HYPERSPARC_PCR_CM);
hypersparc_cache_flush_all();
pcr |= HYPERSPARC_PCR_CE;
if (CACHEINFO.c_vactype == VAC_WRITEBACK)
pcr |= HYPERSPARC_PCR_CM;
sta(SRMMU_PCR, ASI_SRMMU, pcr);
CACHEINFO.c_enabled = 1;
if (CACHEINFO.c_hwflush)
panic("cache_enable: can't handle 4M with hw-flush cache");
v = HYPERSPARC_ICCR_ICE | (sparc_ncpus <= 1 ? HYPERSPARC_ICCR_FTD : 0);
wrasr(v, HYPERSPARC_ASRNUM_ICCR);
}
void
swift_cache_enable(void)
{
int i, ls, ts;
u_int pcr;
cache_alias_dist = uimax(
CACHEINFO.ic_totalsize / CACHEINFO.ic_associativity,
CACHEINFO.dc_totalsize / CACHEINFO.dc_associativity);
cache_alias_bits = (cache_alias_dist - 1) & ~PGOFSET;
pcr = lda(SRMMU_PCR, ASI_SRMMU);
ls = CACHEINFO.ic_linesize;
ts = CACHEINFO.ic_totalsize;
if ((pcr & SWIFT_PCR_ICE) == 0)
for (i = 0; i < ts; i += ls)
sta(i, ASI_ICACHETAG, 0);
ls = CACHEINFO.dc_linesize;
ts = CACHEINFO.dc_totalsize;
if ((pcr & SWIFT_PCR_DCE) == 0)
for (i = 0; i < ts; i += ls)
sta(i, ASI_DCACHETAG, 0);
pcr |= (SWIFT_PCR_ICE | SWIFT_PCR_DCE);
sta(SRMMU_PCR, ASI_SRMMU, pcr);
CACHEINFO.c_enabled = 1;
}
void
cypress_cache_enable(void)
{
int i, ls, ts;
u_int pcr;
int alias_dist;
alias_dist = CACHEINFO.c_totalsize;
if (alias_dist > cache_alias_dist) {
cache_alias_dist = alias_dist;
cache_alias_bits = (alias_dist - 1) & ~PGOFSET;
dvma_cachealign = alias_dist;
}
pcr = lda(SRMMU_PCR, ASI_SRMMU);
pcr &= ~CYPRESS_PCR_CM;
ls = CACHEINFO.c_linesize;
ts = CACHEINFO.c_totalsize;
if ((pcr & CYPRESS_PCR_CE) == 0)
for (i = 0; i < ts; i += ls)
sta(i, ASI_DCACHETAG, 0);
pcr |= CYPRESS_PCR_CE;
if (CACHEINFO.c_vactype == VAC_WRITEBACK)
pcr |= CYPRESS_PCR_CM;
sta(SRMMU_PCR, ASI_SRMMU, pcr);
CACHEINFO.c_enabled = 1;
}
void
turbosparc_cache_enable(void)
{
int i, ls, ts;
u_int pcr, pcf;
static const int ts_ecache_table[8] = {0,256,512,1024,512,1024,1024,0};
cache_alias_dist = uimax(
CACHEINFO.ic_totalsize / CACHEINFO.ic_associativity,
CACHEINFO.dc_totalsize / CACHEINFO.dc_associativity);
cache_alias_bits = (cache_alias_dist - 1) & ~PGOFSET;
pcr = lda(SRMMU_PCR, ASI_SRMMU);
ls = CACHEINFO.ic_linesize;
ts = CACHEINFO.ic_totalsize;
if ((pcr & TURBOSPARC_PCR_ICE) == 0)
for (i = 0; i < ts; i += ls)
sta(i, ASI_ICACHETAG, 0);
ls = CACHEINFO.dc_linesize;
ts = CACHEINFO.dc_totalsize;
if ((pcr & TURBOSPARC_PCR_DCE) == 0)
for (i = 0; i < ts; i += ls)
sta(i, ASI_DCACHETAG, 0);
pcr |= (TURBOSPARC_PCR_ICE | TURBOSPARC_PCR_DCE);
sta(SRMMU_PCR, ASI_SRMMU, pcr);
pcf = lda(SRMMU_PCFG, ASI_SRMMU);
if (pcf & TURBOSPARC_PCFG_SE) {
CACHEINFO.ec_totalsize = 1024 *
ts_ecache_table[(pcf & TURBOSPARC_PCFG_SCC)];
CACHEINFO.ec_linesize = 32;
}
if (pcf & TURBOSPARC_PCFG_SNP)
printf(": DVMA coherent ");
CACHEINFO.c_enabled = 1;
}
#endif
void
sun4_vcache_flush_context(int ctx)
{
char *p;
int i, ls;
vcache_flush_ctx.ev_count++;
p = (char *)0;
if (CACHEINFO.c_hwflush) {
ls = PAGE_SIZE;
i = CACHEINFO.c_totalsize >> PGSHIFT;
for (; --i >= 0; p += ls)
sta(p, ASI_HWFLUSHCTX, 0);
} else {
ls = CACHEINFO.c_linesize;
i = CACHEINFO.c_nlines;
for (; --i >= 0; p += ls)
sta(p, ASI_FLUSHCTX, 0);
}
}
void
sun4_vcache_flush_region(int vreg, int ctx)
{
int i, ls;
char *p;
vcache_flush_reg.ev_count++;
p = (char *)VRTOVA(vreg);
ls = CACHEINFO.c_linesize;
i = CACHEINFO.c_nlines;
for (; --i >= 0; p += ls)
sta(p, ASI_FLUSHREG, 0);
}
void
sun4_vcache_flush_segment(int vreg, int vseg, int ctx)
{
int i, ls;
char *p;
vcache_flush_seg.ev_count++;
p = (char *)VSTOVA(vreg, vseg);
if (CACHEINFO.c_hwflush) {
ls = PAGE_SIZE;
i = CACHEINFO.c_totalsize >> PGSHIFT;
for (; --i >= 0; p += ls)
sta(p, ASI_HWFLUSHSEG, 0);
} else {
ls = CACHEINFO.c_linesize;
i = CACHEINFO.c_nlines;
for (; --i >= 0; p += ls)
sta(p, ASI_FLUSHSEG, 0);
}
}
void
sun4_vcache_flush_page(int va, int ctx)
{
int i, ls;
char *p;
#ifdef DEBUG
if (va & PGOFSET)
panic("cache_flush_page: asked to flush misaligned va 0x%x",va);
#endif
vcache_flush_pg.ev_count++;
p = (char *)va;
ls = CACHEINFO.c_linesize;
i = PAGE_SIZE >> CACHEINFO.c_l2linesize;
for (; --i >= 0; p += ls)
sta(p, ASI_FLUSHPG, 0);
}
void
sun4_vcache_flush_page_hw(int va, int ctx)
{
char *p;
#ifdef DEBUG
if (va & PGOFSET)
panic("cache_flush_page: asked to flush misaligned va 0x%x",va);
#endif
vcache_flush_pg.ev_count++;
p = (char *)va;
sta(p, ASI_HWFLUSHPG, 0);
}
#define CACHE_FLUSH_MAGIC (CACHEINFO.c_totalsize / PAGE_SIZE)
void
sun4_cache_flush(void *base, u_int len)
{
int i, ls, baseoff;
char *p;
if (CACHEINFO.c_vactype == VAC_NONE)
return;
baseoff = (int)base & PGOFSET;
i = (baseoff + len + PGOFSET) >> PGSHIFT;
vcache_flush_range.ev_count++;
if (__predict_true(i < CACHE_FLUSH_MAGIC)) {
p = (char *)((int)base & ~baseoff);
if (CACHEINFO.c_hwflush) {
for (; --i >= 0; p += PAGE_SIZE)
sta(p, ASI_HWFLUSHPG, 0);
} else {
ls = CACHEINFO.c_linesize;
i <<= PGSHIFT - CACHEINFO.c_l2linesize;
for (; --i >= 0; p += ls)
sta(p, ASI_FLUSHPG, 0);
}
return;
}
baseoff = (u_int)base & SGOFSET;
i = (baseoff + len + SGOFSET) >> SGSHIFT;
if (__predict_true(i == 1)) {
sun4_vcache_flush_segment(VA_VREG(base), VA_VSEG(base), 0);
return;
}
if (HASSUN4_MMU3L) {
baseoff = (u_int)base & RGOFSET;
i = (baseoff + len + RGOFSET) >> RGSHIFT;
if (i == 1)
sun4_vcache_flush_region(VA_VREG(base), 0);
else
sun4_vcache_flush_context(0);
} else
sun4_vcache_flush_context(0);
}
#if defined(SUN4M) || defined(SUN4D)
#define trapoff() do { setpsr(getpsr() & ~PSR_ET); } while(0)
#define trapon() do { setpsr(getpsr() | PSR_ET); } while(0)
void
srmmu_vcache_flush_context(int ctx)
{
int i, ls, octx;
char *p;
vcache_flush_ctx.ev_count++;
p = (char *)0;
ls = CACHEINFO.c_linesize;
i = CACHEINFO.c_nlines;
octx = getcontext4m();
trapoff();
setcontext4m(ctx);
for (; --i >= 0; p += ls)
sta(p, ASI_IDCACHELFC, 0);
setcontext4m(octx);
trapon();
}
void
srmmu_vcache_flush_region(int vreg, int ctx)
{
int i, ls, octx;
char *p;
vcache_flush_reg.ev_count++;
p = (char *)VRTOVA(vreg);
ls = CACHEINFO.c_linesize;
i = CACHEINFO.c_nlines;
octx = getcontext4m();
trapoff();
setcontext4m(ctx);
for (; --i >= 0; p += ls)
sta(p, ASI_IDCACHELFR, 0);
setcontext4m(octx);
trapon();
}
void
srmmu_vcache_flush_segment(int vreg, int vseg, int ctx)
{
int i, ls, octx;
char *p;
vcache_flush_seg.ev_count++;
p = (char *)VSTOVA(vreg, vseg);
ls = CACHEINFO.c_linesize;
i = CACHEINFO.c_nlines;
octx = getcontext4m();
trapoff();
setcontext4m(ctx);
for (; --i >= 0; p += ls)
sta(p, ASI_IDCACHELFS, 0);
setcontext4m(octx);
trapon();
}
void
srmmu_vcache_flush_page(int va, int ctx)
{
int i, ls, octx;
char *p;
#ifdef DEBUG
if (va & PGOFSET)
panic("cache_flush_page: asked to flush misaligned va 0x%x",va);
#endif
vcache_flush_pg.ev_count++;
p = (char *)va;
if ((ls = CACHEINFO.c_linesize) == 0)
ls = 8;
i = PAGE_SIZE;
octx = getcontext4m();
trapoff();
setcontext4m(ctx);
for (; i > 0; p += ls, i -= ls)
sta(p, ASI_IDCACHELFP, 0);
#if defined(MULTIPROCESSOR)
sta(va | ASI_SRMMUFP_L3, ASI_SRMMUFP, 0);
#endif
setcontext4m(octx);
trapon();
}
void
srmmu_cache_flush_all(void)
{
srmmu_vcache_flush_context(0);
}
void
srmmu_vcache_flush_range(int va, int len, int ctx)
{
int i, ls, offset;
char *p;
int octx;
if ((ls = CACHEINFO.c_linesize) == 0)
ls = 8;
vcache_flush_range.ev_count++;
offset = va & (ls - 1);
i = len + offset;
p = (char *)(va & ~(ls - 1));
octx = getcontext4m();
trapoff();
setcontext4m(ctx);
for (; i > 0; p += ls, i -= ls)
sta(p, ASI_IDCACHELFP, 0);
#if defined(MULTIPROCESSOR)
if (cpuinfo.cpu_type == CPUTYP_HS_MBUS) {
offset = va & PGOFSET;
i = (offset + len + PGOFSET) >> PGSHIFT;
va = va & ~PGOFSET;
for (; --i >= 0; va += PAGE_SIZE)
sta(va | ASI_SRMMUFP_L3, ASI_SRMMUFP, 0);
}
#endif
setcontext4m(octx);
trapon();
return;
}
void
srmmu_cache_flush(void *base, u_int len)
{
int ctx = getcontext4m();
int i, baseoff;
if (__predict_true(len < CACHEINFO.c_totalsize)) {
#if defined(MULTIPROCESSOR)
FXCALL3(cpuinfo.sp_vcache_flush_range,
cpuinfo.ft_vcache_flush_range,
(int)base, len, ctx, CPUSET_ALL);
#else
cpuinfo.sp_vcache_flush_range((int)base, len, ctx);
#endif
return;
}
baseoff = (u_int)base & SGOFSET;
i = (baseoff + len + SGOFSET) >> SGSHIFT;
if (__predict_true(i == 1)) {
#if defined(MULTIPROCESSOR)
FXCALL3(cpuinfo.sp_vcache_flush_segment,
cpuinfo.ft_vcache_flush_segment,
VA_VREG(base), VA_VSEG(base), ctx, CPUSET_ALL);
#else
srmmu_vcache_flush_segment(VA_VREG(base), VA_VSEG(base), ctx);
#endif
return;
}
baseoff = (u_int)base & RGOFSET;
i = (baseoff + len + RGOFSET) >> RGSHIFT;
while (i--) {
#if defined(MULTIPROCESSOR)
FXCALL2(cpuinfo.sp_vcache_flush_region,
cpuinfo.ft_vcache_flush_region,
VA_VREG(base), ctx, CPUSET_ALL);
#else
srmmu_vcache_flush_region(VA_VREG(base), ctx);
#endif
base = ((char *)base + NBPRG);
}
}
int ms1_cacheflush_magic = 0;
#define MS1_CACHEFLUSH_MAGIC ms1_cacheflush_magic
void
ms1_cache_flush(void *base, u_int len)
{
#if MS1_CACHEFLUSH_MAGIC
if (len <= MS1_CACHEFLUSH_MAGIC) {
int tagaddr = ((u_int)base & 0x7f0);
len = roundup(len, 16);
while (len != 0) {
int tag = lda(tagaddr, ASI_DCACHETAG);
if ((tag & 1) == 1) {
sta(tagaddr, ASI_DCACHETAG, 0);
}
len -= 16;
tagaddr = (tagaddr + 16) & 0x7f0;
}
} else
#endif
sta(0, ASI_DCACHECLR, 0);
}
void
ms1_cache_flush_all(void)
{
sta(0, ASI_ICACHECLR, 0);
sta(0, ASI_DCACHECLR, 0);
}
void
hypersparc_cache_flush_all(void)
{
srmmu_vcache_flush_context(getcontext4m());
hypersparc_pure_vcache_flush();
}
void
cypress_cache_flush_all(void)
{
char *p;
int i, ls;
p = (char *)kernel_text;
ls = CACHEINFO.c_linesize;
i = CACHEINFO.c_nlines;
for (; --i >= 0; p += ls)
(*(volatile char *)p);
}
void
viking_cache_flush(void *base, u_int len)
{
}
void
viking_pcache_flush_page(paddr_t pa, int invalidate_only)
{
int set, i;
if (invalidate_only) {
u_int pa_tag = (pa >> 12);
u_int tagaddr;
uint64_t tag;
#define VIKING_DCACHETAG_S 0x0000010000000000ULL
#define VIKING_DCACHETAG_D 0x0001000000000000ULL
#define VIKING_DCACHETAG_V 0x0100000000000000ULL
#define VIKING_DCACHETAG_PAMASK 0x0000000000ffffffULL
for (set = 0; set < 128; set++) {
tagaddr = (set << 5) | (2 << 30);
for (i = 0 ; i < 4; i++) {
tag = ldda(tagaddr | (i << 26), ASI_DCACHETAG);
if ((tag & VIKING_DCACHETAG_PAMASK) == pa_tag &&
(tag & VIKING_DCACHETAG_V) != 0)
stda(tagaddr | (i << 26),
ASI_DCACHETAG, 0);
}
}
} else {
for (set = 0; set < 128; set++) {
int *v = (int *)(kernel_text + (set << 5));
i = 2 * 4 - 1;
while (i--) {
(*(volatile int *)v);
v += 4096;
}
}
}
}
#endif
#if defined(MULTIPROCESSOR)
void
smp_vcache_flush_page(int va, int ctx)
{
FXCALL2(cpuinfo.sp_vcache_flush_page, cpuinfo.ft_vcache_flush_page,
va, ctx, CPUSET_ALL);
}
void
smp_vcache_flush_segment(int vr, int vs, int ctx)
{
FXCALL3(cpuinfo.sp_vcache_flush_segment, cpuinfo.ft_vcache_flush_segment,
vr, vs, ctx, CPUSET_ALL);
}
void
smp_vcache_flush_region(int vr, int ctx)
{
FXCALL2(cpuinfo.sp_vcache_flush_region, cpuinfo.ft_vcache_flush_region,
vr, ctx, CPUSET_ALL);
}
void
smp_vcache_flush_context(int ctx)
{
FXCALL1(cpuinfo.sp_vcache_flush_context, cpuinfo.ft_vcache_flush_context,
ctx, CPUSET_ALL);
}
#endif