+----------------+-----------------+-----------------+-----------------------------+
 | Ampere         | AmpereOne AC04  | AC04_CPU_10     | AMPERE_ERRATUM_AC03_CPU_38  |
 +----------------+-----------------+-----------------+-----------------------------+
+| Ampere         | AmpereOne AC04  | AC04_CPU_23     | AMPERE_ERRATUM_AC04_CPU_23  |
++----------------+-----------------+-----------------+-----------------------------+
 +----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Cortex-A510     | #2457168        | ARM64_ERRATUM_2457168       |
 +----------------+-----------------+-----------------+-----------------------------+
 
 
          If unsure, say Y.
 
+config AMPERE_ERRATUM_AC04_CPU_23
+        bool "AmpereOne: AC04_CPU_23:  Failure to synchronize writes to HCR_EL2 may corrupt address translations."
+       default y
+       help
+         This option adds an alternative code sequence to work around Ampere
+         errata AC04_CPU_23 on AmpereOne.
+
+         Updates to HCR_EL2 can rarely corrupt simultaneous translations for
+         data addresses initiated by load/store instructions. Only
+         instruction initiated translations are vulnerable, not translations
+         from prefetches for example. A DSB before the store to HCR_EL2 is
+         sufficient to prevent older instructions from hitting the window
+         for corruption, and an ISB after is sufficient to prevent younger
+         instructions from hitting the window for corruption.
+
+         If unsure, say Y.
+
 config ARM64_WORKAROUND_CLEAN_CACHE
        bool
 
 
 
        orr     x0, x0, #HCR_E2H
 .LnVHE_\@:
-       msr     hcr_el2, x0
+       msr_hcr_el2 x0
        isb
 .endm
 
 
                                                                        \
        ___hcr = read_sysreg(hcr_el2);                                  \
        if (!(___hcr & HCR_TGE)) {                                      \
-               write_sysreg(___hcr | HCR_TGE, hcr_el2);                \
+               write_sysreg_hcr(___hcr | HCR_TGE);                     \
                isb();                                                  \
        }                                                               \
        /*                                                              \
         */                                                             \
        barrier();                                                      \
        if (!___ctx->cnt && !(___hcr & HCR_TGE))                        \
-               write_sysreg(___hcr, hcr_el2);                          \
+               write_sysreg_hcr(___hcr);                               \
 } while (0)
 
 static inline void ack_bad_irq(unsigned int irq)
 
        __emit_inst(0xd5000000|(\sreg)|(.L__gpr_num_\rt))
        .endm
 
+       .macro  msr_hcr_el2, reg
+#if IS_ENABLED(CONFIG_AMPERE_ERRATUM_AC04_CPU_23)
+       dsb     nsh
+       msr     hcr_el2, \reg
+       isb
+#else
+       msr     hcr_el2, \reg
+#endif
+       .endm
 #else
 
 #include <linux/bitfield.h>
                write_sysreg(__scs_new, sysreg);                        \
 } while (0)
 
+#define sysreg_clear_set_hcr(clear, set) do {                          \
+       u64 __scs_val = read_sysreg(hcr_el2);                           \
+       u64 __scs_new = (__scs_val & ~(u64)(clear)) | (set);            \
+       if (__scs_new != __scs_val)                                     \
+               write_sysreg_hcr(__scs_new);                    \
+} while (0)
+
 #define sysreg_clear_set_s(sysreg, clear, set) do {                    \
        u64 __scs_val = read_sysreg_s(sysreg);                          \
        u64 __scs_new = (__scs_val & ~(u64)(clear)) | (set);            \
                write_sysreg_s(__scs_new, sysreg);                      \
 } while (0)
 
+#define write_sysreg_hcr(__val) do {                                   \
+       if (IS_ENABLED(CONFIG_AMPERE_ERRATUM_AC04_CPU_23) &&            \
+          (!system_capabilities_finalized() ||                         \
+           alternative_has_cap_unlikely(ARM64_WORKAROUND_AMPERE_AC04_CPU_23))) \
+               asm volatile("dsb nsh; msr hcr_el2, %x0; isb"           \
+                            : : "rZ" (__val));                         \
+       else                                                            \
+               asm volatile("msr hcr_el2, %x0"                         \
+                            : : "rZ" (__val));                         \
+} while (0)
+
 #define read_sysreg_par() ({                                           \
        u64 par;                                                        \
        asm(ALTERNATIVE("nop", "dmb sy", ARM64_WORKAROUND_1508412));    \
 
 };
 #endif
 
+#ifdef CONFIG_AMPERE_ERRATUM_AC04_CPU_23
+static const struct midr_range erratum_ac04_cpu_23_list[] = {
+       MIDR_ALL_VERSIONS(MIDR_AMPERE1A),
+       {},
+};
+#endif
+
 const struct arm64_cpu_capabilities arm64_errata[] = {
 #ifdef CONFIG_ARM64_WORKAROUND_CLEAN_CACHE
        {
                .capability = ARM64_WORKAROUND_AMPERE_AC03_CPU_38,
                ERRATA_MIDR_RANGE_LIST(erratum_ac03_cpu_38_list),
        },
+#endif
+#ifdef CONFIG_AMPERE_ERRATUM_AC04_CPU_23
+       {
+               .desc = "AmpereOne erratum AC04_CPU_23",
+               .capability = ARM64_WORKAROUND_AMPERE_AC04_CPU_23,
+               ERRATA_MIDR_RANGE_LIST(erratum_ac04_cpu_23_list),
+       },
 #endif
        {
                .desc = "Broken CNTVOFF_EL2",
 
 2:
        // Engage the VHE magic!
        mov_q   x0, HCR_HOST_VHE_FLAGS
-       msr     hcr_el2, x0
+       msr_hcr_el2 x0
        isb
 
        // Use the EL1 allocated stack, per-cpu offset
 
 
 static void __mmu_config_restore(struct mmu_config *config)
 {
-       write_sysreg(config->hcr,       hcr_el2);
+       write_sysreg_hcr(config->hcr);
 
        /*
         * ARM errata 1165522 and 1530923 require TGE to be 1 before
 
 skip_mmu_switch:
        /* Clear TGE, enable S2 translation, we're rolling */
-       write_sysreg((config.hcr & ~HCR_TGE) | HCR_VM,  hcr_el2);
+       write_sysreg_hcr((config.hcr & ~HCR_TGE) | HCR_VM);
        isb();
 
        switch (op) {
                if (!vcpu_el2_e2h_is_set(vcpu))
                        val |= HCR_NV | HCR_NV1;
 
-               write_sysreg(val, hcr_el2);
+               write_sysreg_hcr(val);
                isb();
 
                par = SYS_PAR_EL1_F;
                if (!fail)
                        par = read_sysreg_par();
 
-               write_sysreg(hcr, hcr_el2);
+               write_sysreg_hcr(hcr);
                isb();
        }
 
 
        if (cpus_have_final_cap(ARM64_WORKAROUND_CAVIUM_TX2_219_TVM))
                hcr |= HCR_TVM;
 
-       write_sysreg(hcr, hcr_el2);
+       write_sysreg_hcr(hcr);
 
        if (cpus_have_final_cap(ARM64_HAS_RAS_EXTN) && (hcr & HCR_VSE))
                write_sysreg_s(vcpu->arch.vsesr_el2, SYS_VSESR_EL2);
 
        /* Ensure host stage-2 is disabled */
        mrs     x0, hcr_el2
        bic     x0, x0, #HCR_VM
-       msr     hcr_el2, x0
+       msr_hcr_el2 x0
        isb
        tlbi    vmalls12e1
        dsb     nsh
 
        msr     mair_el2, x1
 
        ldr     x1, [x0, #NVHE_INIT_HCR_EL2]
-       msr     hcr_el2, x1
+       msr_hcr_el2 x1
 
        mov     x2, #HCR_E2H
        and     x2, x1, x2
 
 alternative_if ARM64_KVM_PROTECTED_MODE
        mov_q   x5, HCR_HOST_NVHE_FLAGS
-       msr     hcr_el2, x5
+       msr_hcr_el2 x5
 alternative_else_nop_endif
 
        /* Install stub vectors */
 
         */
        kvm_flush_dcache_to_poc(params, sizeof(*params));
 
-       write_sysreg(params->hcr_el2, hcr_el2);
+       write_sysreg_hcr(params->hcr_el2);
        __load_stage2(&host_mmu.arch.mmu, &host_mmu.arch);
 
        /*
 
 
        __deactivate_traps_common(vcpu);
 
-       write_sysreg(this_cpu_ptr(&kvm_init_params)->hcr_el2, hcr_el2);
+       write_sysreg_hcr(this_cpu_ptr(&kvm_init_params)->hcr_el2);
 
        __deactivate_cptr_traps(vcpu);
        write_sysreg(__kvm_hyp_host_vector, vbar_el2);
 
        if (has_vhe()) {
                flags = local_daif_save();
        } else {
-               sysreg_clear_set(hcr_el2, 0, HCR_AMO | HCR_FMO | HCR_IMO);
+               sysreg_clear_set_hcr(0, HCR_AMO | HCR_FMO | HCR_IMO);
                isb();
        }
 
        if (has_vhe()) {
                local_daif_restore(flags);
        } else {
-               sysreg_clear_set(hcr_el2, HCR_AMO | HCR_FMO | HCR_IMO, 0);
+               sysreg_clear_set_hcr(HCR_AMO | HCR_FMO | HCR_IMO, 0);
                isb();
        }
 
 
 
        ___deactivate_traps(vcpu);
 
-       write_sysreg(HCR_HOST_VHE_FLAGS, hcr_el2);
+       write_sysreg_hcr(HCR_HOST_VHE_FLAGS);
 
        if (has_cntpoff()) {
                struct timer_map map;
 
        __load_stage2(mmu, mmu->arch);
        val = read_sysreg(hcr_el2);
        val &= ~HCR_TGE;
-       write_sysreg(val, hcr_el2);
+       write_sysreg_hcr(val);
        isb();
 }
 
         * We're done with the TLB operation, let's restore the host's
         * view of HCR_EL2.
         */
-       write_sysreg(HCR_HOST_VHE_FLAGS, hcr_el2);
+       write_sysreg_hcr(HCR_HOST_VHE_FLAGS);
        isb();
 
        /* ... and the stage-2 MMU context that we switched away from */
 
 WORKAROUND_2645198
 WORKAROUND_2658417
 WORKAROUND_AMPERE_AC03_CPU_38
+WORKAROUND_AMPERE_AC04_CPU_23
 WORKAROUND_TRBE_OVERWRITE_FILL_MODE
 WORKAROUND_TSB_FLUSH_FAILURE
 WORKAROUND_TRBE_WRITE_OUT_OF_RANGE