ARCv2: SLC: provide a line based flush routine for debugging
[linux-2.6-block.git] / arch / arc / mm / cache.c
index a867575a758b9845c09a84fb4515f17f08f1272b..ad498e58a1feb8ad53208c71b2d79df6d582715b 100644 (file)
@@ -652,7 +652,7 @@ static void __ic_line_inv_vaddr(phys_addr_t paddr, unsigned long vaddr,
 
 #endif /* CONFIG_ARC_HAS_ICACHE */
 
-noinline void slc_op(phys_addr_t paddr, unsigned long sz, const int op)
+noinline void slc_op_rgn(phys_addr_t paddr, unsigned long sz, const int op)
 {
 #ifdef CONFIG_ISA_ARCV2
        /*
@@ -665,6 +665,7 @@ noinline void slc_op(phys_addr_t paddr, unsigned long sz, const int op)
        static DEFINE_SPINLOCK(lock);
        unsigned long flags;
        unsigned int ctrl;
+       phys_addr_t end;
 
        spin_lock_irqsave(&lock, flags);
 
@@ -694,8 +695,69 @@ noinline void slc_op(phys_addr_t paddr, unsigned long sz, const int op)
         * END needs to be setup before START (latter triggers the operation)
         * END can't be same as START, so add (l2_line_sz - 1) to sz
         */
-       write_aux_reg(ARC_REG_SLC_RGN_END, (paddr + sz + l2_line_sz - 1));
-       write_aux_reg(ARC_REG_SLC_RGN_START, paddr);
+       end = paddr + sz + l2_line_sz - 1;
+       if (is_pae40_enabled())
+               write_aux_reg(ARC_REG_SLC_RGN_END1, upper_32_bits(end));
+
+       write_aux_reg(ARC_REG_SLC_RGN_END, lower_32_bits(end));
+
+       if (is_pae40_enabled())
+               write_aux_reg(ARC_REG_SLC_RGN_START1, upper_32_bits(paddr));
+
+       write_aux_reg(ARC_REG_SLC_RGN_START, lower_32_bits(paddr));
+
+       /* Make sure "busy" bit reports correct stataus, see STAR 9001165532 */
+       read_aux_reg(ARC_REG_SLC_CTRL);
+
+       while (read_aux_reg(ARC_REG_SLC_CTRL) & SLC_CTRL_BUSY);
+
+       spin_unlock_irqrestore(&lock, flags);
+#endif
+}
+
+noinline void slc_op_line(phys_addr_t paddr, unsigned long sz, const int op)
+{
+#ifdef CONFIG_ISA_ARCV2
+       /*
+        * SLC is shared between all cores and concurrent aux operations from
+        * multiple cores need to be serialized using a spinlock
+        * A concurrent operation can be silently ignored and/or the old/new
+        * operation can remain incomplete forever (lockup in SLC_CTRL_BUSY loop
+        * below)
+        */
+       static DEFINE_SPINLOCK(lock);
+
+       const unsigned long SLC_LINE_MASK = ~(l2_line_sz - 1);
+       unsigned int ctrl, cmd;
+       unsigned long flags;
+       int num_lines;
+
+       spin_lock_irqsave(&lock, flags);
+
+       ctrl = read_aux_reg(ARC_REG_SLC_CTRL);
+
+       /* Don't rely on default value of IM bit */
+       if (!(op & OP_FLUSH))           /* i.e. OP_INV */
+               ctrl &= ~SLC_CTRL_IM;   /* clear IM: Disable flush before Inv */
+       else
+               ctrl |= SLC_CTRL_IM;
+
+       write_aux_reg(ARC_REG_SLC_CTRL, ctrl);
+
+       cmd = op & OP_INV ? ARC_AUX_SLC_IVDL : ARC_AUX_SLC_FLDL;
+
+       sz += paddr & ~SLC_LINE_MASK;
+       paddr &= SLC_LINE_MASK;
+
+       num_lines = DIV_ROUND_UP(sz, l2_line_sz);
+
+       while (num_lines-- > 0) {
+               write_aux_reg(cmd, paddr);
+               paddr += l2_line_sz;
+       }
+
+       /* Make sure "busy" bit reports correct stataus, see STAR 9001165532 */
+       read_aux_reg(ARC_REG_SLC_CTRL);
 
        while (read_aux_reg(ARC_REG_SLC_CTRL) & SLC_CTRL_BUSY);
 
@@ -703,6 +765,8 @@ noinline void slc_op(phys_addr_t paddr, unsigned long sz, const int op)
 #endif
 }
 
+#define slc_op(paddr, sz, op)  slc_op_rgn(paddr, sz, op)
+
 noinline static void slc_entire_op(const int op)
 {
        unsigned int ctrl, r = ARC_REG_SLC_CTRL;
@@ -1111,6 +1175,13 @@ noinline void __init arc_ioc_setup(void)
        __dc_enable();
 }
 
+/*
+ * Cache related boot time checks/setups only needed on master CPU:
+ *  - Geometry checks (kernel build and hardware agree: e.g. L1_CACHE_BYTES)
+ *    Assume SMP only, so all cores will have same cache config. A check on
+ *    one core suffices for all
+ *  - IOC setup / dma callbacks only need to be done once
+ */
 void __init arc_cache_init_master(void)
 {
        unsigned int __maybe_unused cpu = smp_processor_id();
@@ -1188,14 +1259,29 @@ void __ref arc_cache_init(void)
        unsigned int __maybe_unused cpu = smp_processor_id();
        char str[256];
 
-       printk(arc_cache_mumbojumbo(0, str, sizeof(str)));
+       pr_info("%s", arc_cache_mumbojumbo(0, str, sizeof(str)));
 
-       /*
-        * Only master CPU needs to execute rest of function:
-        *  - Assume SMP so all cores will have same cache config so
-        *    any geomtry checks will be same for all
-        *  - IOC setup / dma callbacks only need to be setup once
-        */
        if (!cpu)
                arc_cache_init_master();
+
+       /*
+        * In PAE regime, TLB and cache maintenance ops take wider addresses
+        * And even if PAE is not enabled in kernel, the upper 32-bits still need
+        * to be zeroed to keep the ops sane.
+        * As an optimization for more common !PAE enabled case, zero them out
+        * once at init, rather than checking/setting to 0 for every runtime op
+        */
+       if (is_isa_arcv2() && pae40_exist_but_not_enab()) {
+
+               if (IS_ENABLED(CONFIG_ARC_HAS_ICACHE))
+                       write_aux_reg(ARC_REG_IC_PTAG_HI, 0);
+
+               if (IS_ENABLED(CONFIG_ARC_HAS_DCACHE))
+                       write_aux_reg(ARC_REG_DC_PTAG_HI, 0);
+
+               if (l2_line_sz) {
+                       write_aux_reg(ARC_REG_SLC_RGN_END1, 0);
+                       write_aux_reg(ARC_REG_SLC_RGN_START1, 0);
+               }
+       }
 }