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)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 21 Nov 2017 08:23:28 +0000 (09:23 +0100)
[ Upstream commit 35e6de38858f59b6b65dcfeaf700b5d06fc2b93d ]

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>
Signed-off-by: Sasha Levin <alexander.levin@verizon.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/mips/include/asm/mips-cm.h
arch/mips/kernel/traps.c

index b6845db453a3c36bffd0d07600f582d5524fb5ed..163317fd3d7e9f58feb5070883831267dd1de439 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 b0b29cb6f3d84b30757a7ed4e3287ffb90c074e8..bb1d9ff1be5ccc48f0950114d509753b88f511db 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>
@@ -1646,6 +1647,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:
@@ -1656,11 +1716,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;