ARCv2: MMUv4: cache programming model changes
[cascardo/linux.git] / arch / arc / mm / cache.c
index 322e11b..7a898f5 100644 (file)
@@ -1,64 +1,12 @@
 /*
- * ARC700 VIPT Cache Management
+ * ARC Cache Management
  *
+ * Copyright (C) 2014-15 Synopsys, Inc. (www.synopsys.com)
  * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
- *
- *  vineetg: May 2011: for Non-aliasing VIPT D-cache following can be NOPs
- *   -flush_cache_dup_mm (fork)
- *   -likewise for flush_cache_mm (exit/execve)
- *   -likewise for flush_cache_range,flush_cache_page (munmap, exit, COW-break)
- *
- * vineetg: Apr 2011
- *  -Now that MMU can support larger pg sz (16K), the determiniation of
- *   aliasing shd not be based on assumption of 8k pg
- *
- * vineetg: Mar 2011
- *  -optimised version of flush_icache_range( ) for making I/D coherent
- *   when vaddr is available (agnostic of num of aliases)
- *
- * vineetg: Mar 2011
- *  -Added documentation about I-cache aliasing on ARC700 and the way it
- *   was handled up until MMU V2.
- *  -Spotted a three year old bug when killing the 4 aliases, which needs
- *   bottom 2 bits, so we need to do paddr | {0x00, 0x01, 0x02, 0x03}
- *                        instead of paddr | {0x00, 0x01, 0x10, 0x11}
- *   (Rajesh you owe me one now)
- *
- * vineetg: Dec 2010
- *  -Off-by-one error when computing num_of_lines to flush
- *   This broke signal handling with bionic which uses synthetic sigret stub
- *
- * vineetg: Mar 2010
- *  -GCC can't generate ZOL for core cache flush loops.
- *   Conv them into iterations based as opposed to while (start < end) types
- *
- * Vineetg: July 2009
- *  -In I-cache flush routine we used to chk for aliasing for every line INV.
- *   Instead now we setup routines per cache geometry and invoke them
- *   via function pointers.
- *
- * Vineetg: Jan 2009
- *  -Cache Line flush routines used to flush an extra line beyond end addr
- *   because check was while (end >= start) instead of (end > start)
- *     =Some call sites had to work around by doing -1, -4 etc to end param
- *     =Some callers didnt care. This was spec bad in case of INV routines
- *      which would discard valid data (cause of the horrible ext2 bug
- *      in ARC IDE driver)
- *
- * vineetg: June 11th 2008: Fixed flush_icache_range( )
- *  -Since ARC700 caches are not coherent (I$ doesnt snoop D$) both need
- *   to be flushed, which it was not doing.
- *  -load_module( ) passes vmalloc addr (Kernel Virtual Addr) to the API,
- *   however ARC cache maintenance OPs require PHY addr. Thus need to do
- *   vmalloc_to_phy.
- *  -Also added optimisation there, that for range > PAGE SIZE we flush the
- *   entire cache in one shot rather than line by line. For e.g. a module
- *   with Code sz 600k, old code flushed 600k worth of cache (line-by-line),
- *   while cache is only 16 or 32k.
  */
 
 #include <linux/module.h>
@@ -76,6 +24,7 @@
 char *arc_cache_mumbojumbo(int c, char *buf, int len)
 {
        int n = 0;
+       struct cpuinfo_arc_cache *p;
 
 #define PR_CACHE(p, cfg, str)                                          \
        if (!(p)->ver)                                                  \
@@ -91,6 +40,11 @@ char *arc_cache_mumbojumbo(int c, char *buf, int len)
        PR_CACHE(&cpuinfo_arc700[c].icache, CONFIG_ARC_HAS_ICACHE, "I-Cache");
        PR_CACHE(&cpuinfo_arc700[c].dcache, CONFIG_ARC_HAS_DCACHE, "D-Cache");
 
+       p = &cpuinfo_arc700[c].slc;
+       if (p->ver)
+               n += scnprintf(buf + n, len - n,
+                       "SLC\t\t: %uK, %uB Line\n", p->sz_k, p->line_len);
+
        return buf;
 }
 
@@ -101,7 +55,7 @@ char *arc_cache_mumbojumbo(int c, char *buf, int len)
  */
 void read_decode_cache_bcr(void)
 {
-       struct cpuinfo_arc_cache *p_ic, *p_dc;
+       struct cpuinfo_arc_cache *p_ic, *p_dc, *p_slc;
        unsigned int cpu = smp_processor_id();
        struct bcr_cache {
 #ifdef CONFIG_CPU_BIG_ENDIAN
@@ -111,14 +65,29 @@ void read_decode_cache_bcr(void)
 #endif
        } ibcr, dbcr;
 
+       struct bcr_generic sbcr;
+
+       struct bcr_slc_cfg {
+#ifdef CONFIG_CPU_BIG_ENDIAN
+               unsigned int pad:24, way:2, lsz:2, sz:4;
+#else
+               unsigned int sz:4, lsz:2, way:2, pad:24;
+#endif
+       } slc_cfg;
+
        p_ic = &cpuinfo_arc700[cpu].icache;
        READ_BCR(ARC_REG_IC_BCR, ibcr);
 
        if (!ibcr.ver)
                goto dc_chk;
 
-       BUG_ON(ibcr.config != 3);
-       p_ic->assoc = 2;                /* Fixed to 2w set assoc */
+       if (ibcr.ver <= 3) {
+               BUG_ON(ibcr.config != 3);
+               p_ic->assoc = 2;                /* Fixed to 2w set assoc */
+       } else if (ibcr.ver >= 4) {
+               p_ic->assoc = 1 << ibcr.config; /* 1,2,4,8 */
+       }
+
        p_ic->line_len = 8 << ibcr.line_len;
        p_ic->sz_k = 1 << (ibcr.sz - 1);
        p_ic->ver = ibcr.ver;
@@ -130,94 +99,137 @@ dc_chk:
        READ_BCR(ARC_REG_DC_BCR, dbcr);
 
        if (!dbcr.ver)
-               return;
+               goto slc_chk;
+
+       if (dbcr.ver <= 3) {
+               BUG_ON(dbcr.config != 2);
+               p_dc->assoc = 4;                /* Fixed to 4w set assoc */
+               p_dc->vipt = 1;
+               p_dc->alias = p_dc->sz_k/p_dc->assoc/TO_KB(PAGE_SIZE) > 1;
+       } else if (dbcr.ver >= 4) {
+               p_dc->assoc = 1 << dbcr.config; /* 1,2,4,8 */
+               p_dc->vipt = 0;
+               p_dc->alias = 0;                /* PIPT so can't VIPT alias */
+       }
 
-       BUG_ON(dbcr.config != 2);
-       p_dc->assoc = 4;                /* Fixed to 4w set assoc */
        p_dc->line_len = 16 << dbcr.line_len;
        p_dc->sz_k = 1 << (dbcr.sz - 1);
        p_dc->ver = dbcr.ver;
-       p_dc->vipt = 1;
-       p_dc->alias = p_dc->sz_k/p_dc->assoc/TO_KB(PAGE_SIZE) > 1;
+
+slc_chk:
+       p_slc = &cpuinfo_arc700[cpu].slc;
+       READ_BCR(ARC_REG_SLC_BCR, sbcr);
+       if (sbcr.ver) {
+               READ_BCR(ARC_REG_SLC_CFG, slc_cfg);
+               p_slc->ver = sbcr.ver;
+               p_slc->sz_k = 128 << slc_cfg.sz;
+               p_slc->line_len = (slc_cfg.lsz == 0) ? 128 : 64;
+       }
 }
 
 /*
- * 1. Validate the Cache Geomtery (compile time config matches hardware)
- * 2. If I-cache suffers from aliasing, setup work arounds (difft flush rtn)
- *    (aliasing D-cache configurations are not supported YET)
- * 3. Enable the Caches, setup default flush mode for D-Cache
- * 3. Calculate the SHMLBA used by user space
+ * Line Operation on {I,D}-Cache
  */
-void arc_cache_init(void)
-{
-       unsigned int __maybe_unused cpu = smp_processor_id();
-       char str[256];
-
-       printk(arc_cache_mumbojumbo(0, str, sizeof(str)));
 
-       if (IS_ENABLED(CONFIG_ARC_HAS_ICACHE)) {
-               struct cpuinfo_arc_cache *ic = &cpuinfo_arc700[cpu].icache;
+#define OP_INV         0x1
+#define OP_FLUSH       0x2
+#define OP_FLUSH_N_INV 0x3
+#define OP_INV_IC      0x4
 
-               if (!ic->ver)
-                       panic("cache support enabled but non-existent cache\n");
+/*
+ *             I-Cache Aliasing in ARC700 VIPT caches (MMU v1-v3)
+ *
+ * ARC VIPT I-cache uses vaddr to index into cache and paddr to match the tag.
+ * The orig Cache Management Module "CDU" only required paddr to invalidate a
+ * certain line since it sufficed as index in Non-Aliasing VIPT cache-geometry.
+ * Infact for distinct V1,V2,P: all of {V1-P},{V2-P},{P-P} would end up fetching
+ * the exact same line.
+ *
+ * However for larger Caches (way-size > page-size) - i.e. in Aliasing config,
+ * paddr alone could not be used to correctly index the cache.
+ *
+ * ------------------
+ * MMU v1/v2 (Fixed Page Size 8k)
+ * ------------------
+ * The solution was to provide CDU with these additonal vaddr bits. These
+ * would be bits [x:13], x would depend on cache-geometry, 13 comes from
+ * standard page size of 8k.
+ * H/w folks chose [17:13] to be a future safe range, and moreso these 5 bits
+ * of vaddr could easily be "stuffed" in the paddr as bits [4:0] since the
+ * orig 5 bits of paddr were anyways ignored by CDU line ops, as they
+ * represent the offset within cache-line. The adv of using this "clumsy"
+ * interface for additional info was no new reg was needed in CDU programming
+ * model.
+ *
+ * 17:13 represented the max num of bits passable, actual bits needed were
+ * fewer, based on the num-of-aliases possible.
+ * -for 2 alias possibility, only bit 13 needed (32K cache)
+ * -for 4 alias possibility, bits 14:13 needed (64K cache)
+ *
+ * ------------------
+ * MMU v3
+ * ------------------
+ * This ver of MMU supports variable page sizes (1k-16k): although Linux will
+ * only support 8k (default), 16k and 4k.
+ * However from hardware perspective, smaller page sizes aggrevate aliasing
+ * meaning more vaddr bits needed to disambiguate the cache-line-op ;
+ * the existing scheme of piggybacking won't work for certain configurations.
+ * Two new registers IC_PTAG and DC_PTAG inttoduced.
+ * "tag" bits are provided in PTAG, index bits in existing IVIL/IVDL/FLDL regs
+ */
 
-               if (ic->line_len != L1_CACHE_BYTES)
-                       panic("ICache line [%d] != kernel Config [%d]",
-                             ic->line_len, L1_CACHE_BYTES);
+static inline
+void __cache_line_loop_v2(unsigned long paddr, unsigned long vaddr,
+                         unsigned long sz, const int op)
+{
+       unsigned int aux_cmd;
+       int num_lines;
+       const int full_page = __builtin_constant_p(sz) && sz == PAGE_SIZE;
 
-               if (ic->ver != CONFIG_ARC_MMU_VER)
-                       panic("Cache ver [%d] doesn't match MMU ver [%d]\n",
-                             ic->ver, CONFIG_ARC_MMU_VER);
+       if (op == OP_INV_IC) {
+               aux_cmd = ARC_REG_IC_IVIL;
+       } else {
+               /* d$ cmd: INV (discard or wback-n-discard) OR FLUSH (wback) */
+               aux_cmd = op & OP_INV ? ARC_REG_DC_IVDL : ARC_REG_DC_FLDL;
        }
 
-       if (IS_ENABLED(CONFIG_ARC_HAS_DCACHE)) {
-               struct cpuinfo_arc_cache *dc = &cpuinfo_arc700[cpu].dcache;
-               int handled;
-
-               if (!dc->ver)
-                       panic("cache support enabled but non-existent cache\n");
+       /* Ensure we properly floor/ceil the non-line aligned/sized requests
+        * and have @paddr - aligned to cache line and integral @num_lines.
+        * This however can be avoided for page sized since:
+        *  -@paddr will be cache-line aligned already (being page aligned)
+        *  -@sz will be integral multiple of line size (being page sized).
+        */
+       if (!full_page) {
+               sz += paddr & ~CACHE_LINE_MASK;
+               paddr &= CACHE_LINE_MASK;
+               vaddr &= CACHE_LINE_MASK;
+       }
 
-               if (dc->line_len != L1_CACHE_BYTES)
-                       panic("DCache line [%d] != kernel Config [%d]",
-                             dc->line_len, L1_CACHE_BYTES);
+       num_lines = DIV_ROUND_UP(sz, L1_CACHE_BYTES);
 
-               /* check for D-Cache aliasing */
-               handled = IS_ENABLED(CONFIG_ARC_CACHE_VIPT_ALIASING);
+       /* MMUv2 and before: paddr contains stuffed vaddrs bits */
+       paddr |= (vaddr >> PAGE_SHIFT) & 0x1F;
 
-               if (dc->alias && !handled)
-                       panic("Enable CONFIG_ARC_CACHE_VIPT_ALIASING\n");
-               else if (!dc->alias && handled)
-                       panic("Don't need CONFIG_ARC_CACHE_VIPT_ALIASING\n");
+       while (num_lines-- > 0) {
+               write_aux_reg(aux_cmd, paddr);
+               paddr += L1_CACHE_BYTES;
        }
 }
 
-#define OP_INV         0x1
-#define OP_FLUSH       0x2
-#define OP_FLUSH_N_INV 0x3
-#define OP_INV_IC      0x4
-
-/*
- * Common Helper for Line Operations on {I,D}-Cache
- */
-static inline void __cache_line_loop(unsigned long paddr, unsigned long vaddr,
-                                    unsigned long sz, const int cacheop)
+static inline
+void __cache_line_loop_v3(unsigned long paddr, unsigned long vaddr,
+                         unsigned long sz, const int op)
 {
        unsigned int aux_cmd, aux_tag;
        int num_lines;
-       const int full_page_op = __builtin_constant_p(sz) && sz == PAGE_SIZE;
+       const int full_page = __builtin_constant_p(sz) && sz == PAGE_SIZE;
 
-       if (cacheop == OP_INV_IC) {
+       if (op == OP_INV_IC) {
                aux_cmd = ARC_REG_IC_IVIL;
-#if (CONFIG_ARC_MMU_VER > 2)
                aux_tag = ARC_REG_IC_PTAG;
-#endif
-       }
-       else {
-               /* d$ cmd: INV (discard or wback-n-discard) OR FLUSH (wback) */
-               aux_cmd = cacheop & OP_INV ? ARC_REG_DC_IVDL : ARC_REG_DC_FLDL;
-#if (CONFIG_ARC_MMU_VER > 2)
+       } else {
+               aux_cmd = op & OP_INV ? ARC_REG_DC_IVDL : ARC_REG_DC_FLDL;
                aux_tag = ARC_REG_DC_PTAG;
-#endif
        }
 
        /* Ensure we properly floor/ceil the non-line aligned/sized requests
@@ -226,177 +238,169 @@ static inline void __cache_line_loop(unsigned long paddr, unsigned long vaddr,
         *  -@paddr will be cache-line aligned already (being page aligned)
         *  -@sz will be integral multiple of line size (being page sized).
         */
-       if (!full_page_op) {
+       if (!full_page) {
                sz += paddr & ~CACHE_LINE_MASK;
                paddr &= CACHE_LINE_MASK;
                vaddr &= CACHE_LINE_MASK;
        }
-
        num_lines = DIV_ROUND_UP(sz, L1_CACHE_BYTES);
 
-#if (CONFIG_ARC_MMU_VER <= 2)
-       /* MMUv2 and before: paddr contains stuffed vaddrs bits */
-       paddr |= (vaddr >> PAGE_SHIFT) & 0x1F;
-#else
-       /* if V-P const for loop, PTAG can be written once outside loop */
-       if (full_page_op)
+       /*
+        * MMUv3, cache ops require paddr in PTAG reg
+        * if V-P const for loop, PTAG can be written once outside loop
+        */
+       if (full_page)
                write_aux_reg(aux_tag, paddr);
-#endif
 
        while (num_lines-- > 0) {
-#if (CONFIG_ARC_MMU_VER > 2)
-               /* MMUv3, cache ops require paddr seperately */
-               if (!full_page_op) {
+               if (!full_page) {
                        write_aux_reg(aux_tag, paddr);
                        paddr += L1_CACHE_BYTES;
                }
 
                write_aux_reg(aux_cmd, vaddr);
                vaddr += L1_CACHE_BYTES;
-#else
+       }
+}
+
+/*
+ * In HS38x (MMU v4), although icache is VIPT, only paddr is needed for cache
+ * maintenance ops (in IVIL reg), as long as icache doesn't alias.
+ *
+ * For Aliasing icache, vaddr is also needed (in IVIL), while paddr is
+ * specified in PTAG (similar to MMU v3)
+ */
+static inline
+void __cache_line_loop_v4(unsigned long paddr, unsigned long vaddr,
+                         unsigned long sz, const int cacheop)
+{
+       unsigned int aux_cmd;
+       int num_lines;
+       const int full_page_op = __builtin_constant_p(sz) && sz == PAGE_SIZE;
+
+       if (cacheop == OP_INV_IC) {
+               aux_cmd = ARC_REG_IC_IVIL;
+       } else {
+               /* d$ cmd: INV (discard or wback-n-discard) OR FLUSH (wback) */
+               aux_cmd = cacheop & OP_INV ? ARC_REG_DC_IVDL : ARC_REG_DC_FLDL;
+       }
+
+       /* Ensure we properly floor/ceil the non-line aligned/sized requests
+        * and have @paddr - aligned to cache line and integral @num_lines.
+        * This however can be avoided for page sized since:
+        *  -@paddr will be cache-line aligned already (being page aligned)
+        *  -@sz will be integral multiple of line size (being page sized).
+        */
+       if (!full_page_op) {
+               sz += paddr & ~CACHE_LINE_MASK;
+               paddr &= CACHE_LINE_MASK;
+       }
+
+       num_lines = DIV_ROUND_UP(sz, L1_CACHE_BYTES);
+
+       while (num_lines-- > 0) {
                write_aux_reg(aux_cmd, paddr);
                paddr += L1_CACHE_BYTES;
-#endif
        }
 }
 
+#if (CONFIG_ARC_MMU_VER < 3)
+#define __cache_line_loop      __cache_line_loop_v2
+#elif (CONFIG_ARC_MMU_VER == 3)
+#define __cache_line_loop      __cache_line_loop_v3
+#elif (CONFIG_ARC_MMU_VER > 3)
+#define __cache_line_loop      __cache_line_loop_v4
+#endif
+
 #ifdef CONFIG_ARC_HAS_DCACHE
 
 /***************************************************************
  * Machine specific helpers for Entire D-Cache or Per Line ops
  */
 
-static inline unsigned int __before_dc_op(const int op)
+static inline void __before_dc_op(const int op)
 {
-       unsigned int reg = reg;
-
        if (op == OP_FLUSH_N_INV) {
                /* Dcache provides 2 cmd: FLUSH or INV
                 * INV inturn has sub-modes: DISCARD or FLUSH-BEFORE
                 * flush-n-inv is achieved by INV cmd but with IM=1
                 * So toggle INV sub-mode depending on op request and default
                 */
-               reg = read_aux_reg(ARC_REG_DC_CTRL);
-               write_aux_reg(ARC_REG_DC_CTRL, reg | DC_CTRL_INV_MODE_FLUSH)
-                       ;
+               const unsigned int ctl = ARC_REG_DC_CTRL;
+               write_aux_reg(ctl, read_aux_reg(ctl) | DC_CTRL_INV_MODE_FLUSH);
        }
-
-       return reg;
 }
 
-static inline void __after_dc_op(const int op, unsigned int reg)
+static inline void __after_dc_op(const int op)
 {
-       if (op & OP_FLUSH)      /* flush / flush-n-inv both wait */
-               while (read_aux_reg(ARC_REG_DC_CTRL) & DC_CTRL_FLUSH_STATUS);
+       if (op & OP_FLUSH) {
+               const unsigned int ctl = ARC_REG_DC_CTRL;
+               unsigned int reg;
+
+               /* flush / flush-n-inv both wait */
+               while ((reg = read_aux_reg(ctl)) & DC_CTRL_FLUSH_STATUS)
+                       ;
 
-       /* Switch back to default Invalidate mode */
-       if (op == OP_FLUSH_N_INV)
-               write_aux_reg(ARC_REG_DC_CTRL, reg & ~DC_CTRL_INV_MODE_FLUSH);
+               /* Switch back to default Invalidate mode */
+               if (op == OP_FLUSH_N_INV)
+                       write_aux_reg(ctl, reg & ~DC_CTRL_INV_MODE_FLUSH);
+       }
 }
 
 /*
  * Operation on Entire D-Cache
- * @cacheop = {OP_INV, OP_FLUSH, OP_FLUSH_N_INV}
+ * @op = {OP_INV, OP_FLUSH, OP_FLUSH_N_INV}
  * Note that constant propagation ensures all the checks are gone
  * in generated code
  */
-static inline void __dc_entire_op(const int cacheop)
+static inline void __dc_entire_op(const int op)
 {
-       unsigned int ctrl_reg;
        int aux;
 
-       ctrl_reg = __before_dc_op(cacheop);
+       __before_dc_op(op);
 
-       if (cacheop & OP_INV)   /* Inv or flush-n-inv use same cmd reg */
+       if (op & OP_INV)        /* Inv or flush-n-inv use same cmd reg */
                aux = ARC_REG_DC_IVDC;
        else
                aux = ARC_REG_DC_FLSH;
 
        write_aux_reg(aux, 0x1);
 
-       __after_dc_op(cacheop, ctrl_reg);
+       __after_dc_op(op);
 }
 
 /* For kernel mappings cache operation: index is same as paddr */
 #define __dc_line_op_k(p, sz, op)      __dc_line_op(p, p, sz, op)
 
 /*
- * D-Cache : Per Line INV (discard or wback+discard) or FLUSH (wback)
+ * D-Cache Line ops: Per Line INV (discard or wback+discard) or FLUSH (wback)
  */
 static inline void __dc_line_op(unsigned long paddr, unsigned long vaddr,
-                               unsigned long sz, const int cacheop)
+                               unsigned long sz, const int op)
 {
        unsigned long flags;
-       unsigned int ctrl_reg;
 
        local_irq_save(flags);
 
-       ctrl_reg = __before_dc_op(cacheop);
+       __before_dc_op(op);
 
-       __cache_line_loop(paddr, vaddr, sz, cacheop);
+       __cache_line_loop(paddr, vaddr, sz, op);
 
-       __after_dc_op(cacheop, ctrl_reg);
+       __after_dc_op(op);
 
        local_irq_restore(flags);
 }
 
 #else
 
-#define __dc_entire_op(cacheop)
-#define __dc_line_op(paddr, vaddr, sz, cacheop)
-#define __dc_line_op_k(paddr, sz, cacheop)
+#define __dc_entire_op(op)
+#define __dc_line_op(paddr, vaddr, sz, op)
+#define __dc_line_op_k(paddr, sz, op)
 
 #endif /* CONFIG_ARC_HAS_DCACHE */
 
-
 #ifdef CONFIG_ARC_HAS_ICACHE
 
-/*
- *             I-Cache Aliasing in ARC700 VIPT caches
- *
- * ARC VIPT I-cache uses vaddr to index into cache and paddr to match the tag.
- * The orig Cache Management Module "CDU" only required paddr to invalidate a
- * certain line since it sufficed as index in Non-Aliasing VIPT cache-geometry.
- * Infact for distinct V1,V2,P: all of {V1-P},{V2-P},{P-P} would end up fetching
- * the exact same line.
- *
- * However for larger Caches (way-size > page-size) - i.e. in Aliasing config,
- * paddr alone could not be used to correctly index the cache.
- *
- * ------------------
- * MMU v1/v2 (Fixed Page Size 8k)
- * ------------------
- * The solution was to provide CDU with these additonal vaddr bits. These
- * would be bits [x:13], x would depend on cache-geometry, 13 comes from
- * standard page size of 8k.
- * H/w folks chose [17:13] to be a future safe range, and moreso these 5 bits
- * of vaddr could easily be "stuffed" in the paddr as bits [4:0] since the
- * orig 5 bits of paddr were anyways ignored by CDU line ops, as they
- * represent the offset within cache-line. The adv of using this "clumsy"
- * interface for additional info was no new reg was needed in CDU programming
- * model.
- *
- * 17:13 represented the max num of bits passable, actual bits needed were
- * fewer, based on the num-of-aliases possible.
- * -for 2 alias possibility, only bit 13 needed (32K cache)
- * -for 4 alias possibility, bits 14:13 needed (64K cache)
- *
- * ------------------
- * MMU v3
- * ------------------
- * This ver of MMU supports variable page sizes (1k-16k): although Linux will
- * only support 8k (default), 16k and 4k.
- * However from hardware perspective, smaller page sizes aggrevate aliasing
- * meaning more vaddr bits needed to disambiguate the cache-line-op ;
- * the existing scheme of piggybacking won't work for certain configurations.
- * Two new registers IC_PTAG and DC_PTAG inttoduced.
- * "tag" bits are provided in PTAG, index bits in existing IVIL/IVDL/FLDL regs
- */
-
-/***********************************************************
- * Machine specific helper for per line I-Cache invalidate.
- */
-
 static inline void __ic_entire_inv(void)
 {
        write_aux_reg(ARC_REG_IC_IVIC, 1);
@@ -721,3 +725,47 @@ SYSCALL_DEFINE3(cacheflush, uint32_t, start, uint32_t, sz, uint32_t, flags)
        flush_cache_all();
        return 0;
 }
+
+void arc_cache_init(void)
+{
+       unsigned int __maybe_unused cpu = smp_processor_id();
+       char str[256];
+
+       printk(arc_cache_mumbojumbo(0, str, sizeof(str)));
+
+       if (IS_ENABLED(CONFIG_ARC_HAS_ICACHE)) {
+               struct cpuinfo_arc_cache *ic = &cpuinfo_arc700[cpu].icache;
+
+               if (!ic->ver)
+                       panic("cache support enabled but non-existent cache\n");
+
+               if (ic->line_len != L1_CACHE_BYTES)
+                       panic("ICache line [%d] != kernel Config [%d]",
+                             ic->line_len, L1_CACHE_BYTES);
+
+               if (ic->ver != CONFIG_ARC_MMU_VER)
+                       panic("Cache ver [%d] doesn't match MMU ver [%d]\n",
+                             ic->ver, CONFIG_ARC_MMU_VER);
+       }
+
+       if (IS_ENABLED(CONFIG_ARC_HAS_DCACHE)) {
+               struct cpuinfo_arc_cache *dc = &cpuinfo_arc700[cpu].dcache;
+
+               if (!dc->ver)
+                       panic("cache support enabled but non-existent cache\n");
+
+               if (dc->line_len != L1_CACHE_BYTES)
+                       panic("DCache line [%d] != kernel Config [%d]",
+                             dc->line_len, L1_CACHE_BYTES);
+
+               /* check for D-Cache aliasing on ARCompact: ARCv2 has PIPT */
+               if (is_isa_arcompact()) {
+                       int handled = IS_ENABLED(CONFIG_ARC_CACHE_VIPT_ALIASING);
+
+                       if (dc->alias && !handled)
+                               panic("Enable CONFIG_ARC_CACHE_VIPT_ALIASING\n");
+                       else if (!dc->alias && handled)
+                               panic("Disable CONFIG_ARC_CACHE_VIPT_ALIASING\n");
+               }
+       }
+}