Commit 0b0ca959 authored by Djordje Todorovic's avatar Djordje Todorovic Committed by Paul Walmsley
Browse files

riscv: errata: Fix the PAUSE Opcode for MIPS P8700



Add ERRATA_MIPS and ERRATA_MIPS_P8700_PAUSE_OPCODE configs.
Handle errata for the MIPS PAUSE instruction.

Signed-off-by: default avatarDjordje Todorovic <djordje.todorovic@htecgroup.com>
Signed-off-by: default avatarAleksandar Rikalo <arikalo@gmail.com>
Signed-off-by: default avatarRaj Vishwanathan4 <rvishwanathan@mips.com>
Signed-off-by: default avatarAleksa Paunovic <aleksa.paunovic@htecgroup.com>
Reviewed-by: default avatarAlexandre Ghiti <alexghiti@rivosinc.com>
Link: https://lore.kernel.org/r/20250724-p8700-pause-v5-7-a6cbbe1c3412@htecgroup.com


[pjw@kernel.org: updated to apply and compile; fixed a checkpatch issue]
Signed-off-by: default avatarPaul Walmsley <pjw@kernel.org>
parent c9a9fc23
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -21,6 +21,29 @@ config ERRATA_ANDES_CMO

	  If you don't know what to do here, say "Y".

config ERRATA_MIPS
	bool "MIPS errata"
	depends on RISCV_ALTERNATIVE
	help
	  All MIPS errata Kconfig depend on this Kconfig. Disabling
	  this Kconfig will disable all MIPS errata. Please say "Y"
	  here if your platform uses MIPS CPU cores.

	  Otherwise, please say "N" here to avoid unnecessary overhead.

config ERRATA_MIPS_P8700_PAUSE_OPCODE
	bool "Fix the PAUSE Opcode for MIPS P8700"
	depends on ERRATA_MIPS && 64BIT
	default n
	help
	   The RISCV MIPS P8700 uses a different opcode for PAUSE.
	   It is a 'hint' encoding of the SLLI instruction,
	   with rd=0, rs1=0 and imm=5. It will behave as a NOP
	   instruction if no additional behavior beyond that of
	   SLLI is implemented.

	   If you are not using the P8700 processor, say n.

config ERRATA_SIFIVE
	bool "SiFive errata"
	depends on RISCV_ALTERNATIVE
+1 −0
Original line number Diff line number Diff line
@@ -13,5 +13,6 @@ endif
endif

obj-$(CONFIG_ERRATA_ANDES) += andes/
obj-$(CONFIG_ERRATA_MIPS) += mips/
obj-$(CONFIG_ERRATA_SIFIVE) += sifive/
obj-$(CONFIG_ERRATA_THEAD) += thead/
+5 −0
Original line number Diff line number Diff line
ifdef CONFIG_RISCV_ALTERNATIVE_EARLY
CFLAGS_errata.o := -mcmodel=medany
endif

obj-y += errata.o
+67 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2025 MIPS.
 */

#include <linux/memory.h>
#include <linux/module.h>
#include <asm/text-patching.h>
#include <asm/alternative.h>
#include <asm/errata_list.h>
#include <asm/vendorid_list.h>
#include <asm/vendor_extensions.h>
#include <asm/vendor_extensions/mips.h>

static inline bool errata_probe_pause(void)
{
	if (!IS_ENABLED(CONFIG_ERRATA_MIPS_P8700_PAUSE_OPCODE))
		return false;

	if (!riscv_isa_vendor_extension_available(MIPS_VENDOR_ID, XMIPSEXECTL))
		return false;

	return true;
}

static u32 mips_errata_probe(void)
{
	u32 cpu_req_errata = 0;

	if (errata_probe_pause())
		cpu_req_errata |= BIT(ERRATA_MIPS_P8700_PAUSE_OPCODE);

	return cpu_req_errata;
}

void mips_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
			    unsigned long archid, unsigned long impid,
			    unsigned int stage)
{
	struct alt_entry *alt;
	u32 cpu_req_errata = mips_errata_probe();
	u32 tmp;

	BUILD_BUG_ON(ERRATA_MIPS_NUMBER >= RISCV_VENDOR_EXT_ALTERNATIVES_BASE);

	if (stage == RISCV_ALTERNATIVES_EARLY_BOOT)
		return;

	for (alt = begin; alt < end; alt++) {
		if (alt->vendor_id != MIPS_VENDOR_ID)
			continue;

		if (alt->patch_id >= ERRATA_MIPS_NUMBER) {
			WARN(1, "MIPS errata id:%d not in kernel errata list\n",
			     alt->patch_id);
			continue;
		}

		tmp = (1U << alt->patch_id);
		if (cpu_req_errata && tmp) {
			mutex_lock(&text_mutex);
			patch_text_nosync(ALT_OLD_PTR(alt), ALT_ALT_PTR(alt),
					  alt->alt_len);
			mutex_unlock(&text_mutex);
		}
	}
}
+3 −0
Original line number Diff line number Diff line
@@ -48,6 +48,9 @@ struct alt_entry {
void andes_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
			     unsigned long archid, unsigned long impid,
			     unsigned int stage);
void mips_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
			    unsigned long archid, unsigned long impid,
			    unsigned int stage);
void sifive_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
			      unsigned long archid, unsigned long impid,
			      unsigned int stage);
Loading