MIPS: traps: Ensure L1 & L2 ECC checking match for CM3 systems
authorPaul Burton <paul.burton@imgtec.com>
Mon, 17 Oct 2016 15:01:07 +0000 (16:01 +0100)
committerRalf Baechle <ralf@linux-mips.org>
Tue, 3 Jan 2017 15:34:41 +0000 (16:34 +0100)
On systems with CM3, we must ensure that the L1 & L2 ECC enables are set
to the same value. This is presumed by the hardware & cache corruption
can occur when it is not the case. Support enabling & disabling the L2
ECC checking on CM3 systems where this is controlled via a GCR, and
ensure that it matches the state of L1 ECC checking. Remove I6400 from
the switch statement it will no longer hit, and which was incorrect
since the L2 ECC enable bit isn't in the CP0 ErrCtl register.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/14413/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
arch/mips/include/asm/mips-cm.h
arch/mips/kernel/traps.c

index 2e4180797b21828c3bc93a76d214b51f94f1392e..cfdbab0157697f74cf0eedf048d2791f2b4dd643 100644 (file)
@@ -187,6 +187,7 @@ BUILD_CM_R_(config,         MIPS_CM_GCB_OFS + 0x00)
 BUILD_CM_RW(base,              MIPS_CM_GCB_OFS + 0x08)
 BUILD_CM_RW(access,            MIPS_CM_GCB_OFS + 0x20)
 BUILD_CM_R_(rev,               MIPS_CM_GCB_OFS + 0x30)
+BUILD_CM_RW(err_control,       MIPS_CM_GCB_OFS + 0x38)
 BUILD_CM_RW(error_mask,                MIPS_CM_GCB_OFS + 0x40)
 BUILD_CM_RW(error_cause,       MIPS_CM_GCB_OFS + 0x48)
 BUILD_CM_RW(error_addr,                MIPS_CM_GCB_OFS + 0x50)
@@ -266,6 +267,12 @@ BUILD_CM_Cx_R_(tcid_8_priority,    0x80)
 #define CM_REV_CM2_5                           CM_ENCODE_REV(7, 0)
 #define CM_REV_CM3                             CM_ENCODE_REV(8, 0)
 
+/* GCR_ERR_CONTROL register fields */
+#define CM_GCR_ERR_CONTROL_L2_ECC_EN_SHF       1
+#define CM_GCR_ERR_CONTROL_L2_ECC_EN_MSK       (_ULCAST_(0x1) << 1)
+#define CM_GCR_ERR_CONTROL_L2_ECC_SUPPORT_SHF  0
+#define CM_GCR_ERR_CONTROL_L2_ECC_SUPPORT_MSK  (_ULCAST_(0x1) << 0)
+
 /* GCR_ERROR_CAUSE register fields */
 #define CM_GCR_ERROR_CAUSE_ERRTYPE_SHF         27
 #define CM_GCR_ERROR_CAUSE_ERRTYPE_MSK         (_ULCAST_(0x1f) << 27)
index 6c7f9d7e92b3e0dace551122460ff0df8ae89698..9ea6959cd5bb652d217521de57b95485549fcd0f 100644 (file)
@@ -51,6 +51,7 @@
 #include <asm/idle.h>
 #include <asm/mips-cm.h>
 #include <asm/mips-r2-to-r6-emul.h>
+#include <asm/mips-cm.h>
 #include <asm/mipsregs.h>
 #include <asm/mipsmtregs.h>
 #include <asm/module.h>
@@ -1644,6 +1645,65 @@ __setup("nol2par", nol2parity);
  */
 static inline void parity_protection_init(void)
 {
+#define ERRCTL_PE      0x80000000
+#define ERRCTL_L2P     0x00800000
+
+       if (mips_cm_revision() >= CM_REV_CM3) {
+               ulong gcr_ectl, cp0_ectl;
+
+               /*
+                * With CM3 systems we need to ensure that the L1 & L2
+                * parity enables are set to the same value, since this
+                * is presumed by the hardware engineers.
+                *
+                * If the user disabled either of L1 or L2 ECC checking,
+                * disable both.
+                */
+               l1parity &= l2parity;
+               l2parity &= l1parity;
+
+               /* Probe L1 ECC support */
+               cp0_ectl = read_c0_ecc();
+               write_c0_ecc(cp0_ectl | ERRCTL_PE);
+               back_to_back_c0_hazard();
+               cp0_ectl = read_c0_ecc();
+
+               /* Probe L2 ECC support */
+               gcr_ectl = read_gcr_err_control();
+
+               if (!(gcr_ectl & CM_GCR_ERR_CONTROL_L2_ECC_SUPPORT_MSK) ||
+                   !(cp0_ectl & ERRCTL_PE)) {
+                       /*
+                        * One of L1 or L2 ECC checking isn't supported,
+                        * so we cannot enable either.
+                        */
+                       l1parity = l2parity = 0;
+               }
+
+               /* Configure L1 ECC checking */
+               if (l1parity)
+                       cp0_ectl |= ERRCTL_PE;
+               else
+                       cp0_ectl &= ~ERRCTL_PE;
+               write_c0_ecc(cp0_ectl);
+               back_to_back_c0_hazard();
+               WARN_ON(!!(read_c0_ecc() & ERRCTL_PE) != l1parity);
+
+               /* Configure L2 ECC checking */
+               if (l2parity)
+                       gcr_ectl |= CM_GCR_ERR_CONTROL_L2_ECC_EN_MSK;
+               else
+                       gcr_ectl &= ~CM_GCR_ERR_CONTROL_L2_ECC_EN_MSK;
+               write_gcr_err_control(gcr_ectl);
+               gcr_ectl = read_gcr_err_control();
+               gcr_ectl &= CM_GCR_ERR_CONTROL_L2_ECC_EN_MSK;
+               WARN_ON(!!gcr_ectl != l2parity);
+
+               pr_info("Cache parity protection %sabled\n",
+                       l1parity ? "en" : "dis");
+               return;
+       }
+
        switch (current_cpu_type()) {
        case CPU_24K:
        case CPU_34K:
@@ -1654,11 +1714,8 @@ static inline void parity_protection_init(void)
        case CPU_PROAPTIV:
        case CPU_P5600:
        case CPU_QEMU_GENERIC:
-       case CPU_I6400:
        case CPU_P6600:
                {
-#define ERRCTL_PE      0x80000000
-#define ERRCTL_L2P     0x00800000
                        unsigned long errctl;
                        unsigned int l1parity_present, l2parity_present;