xref: /linux/arch/riscv/errata/sifive/errata.c (revision d14ca1f8d3039970e812fec1f01e7b46b62cc5fc)
11a0e5dbdSVincent Chen // SPDX-License-Identifier: GPL-2.0-only
21a0e5dbdSVincent Chen /*
31a0e5dbdSVincent Chen  * Copyright (C) 2021 Sifive.
41a0e5dbdSVincent Chen  */
51a0e5dbdSVincent Chen 
61a0e5dbdSVincent Chen #include <linux/kernel.h>
71a0e5dbdSVincent Chen #include <linux/string.h>
81a0e5dbdSVincent Chen #include <linux/bug.h>
91a0e5dbdSVincent Chen #include <asm/patch.h>
101a0e5dbdSVincent Chen #include <asm/alternative.h>
111a0e5dbdSVincent Chen #include <asm/vendorid_list.h>
121a0e5dbdSVincent Chen #include <asm/errata_list.h>
131a0e5dbdSVincent Chen 
141a0e5dbdSVincent Chen struct errata_info_t {
151a0e5dbdSVincent Chen 	char name[ERRATA_STRING_LENGTH_MAX];
161a0e5dbdSVincent Chen 	bool (*check_func)(unsigned long  arch_id, unsigned long impid);
171a0e5dbdSVincent Chen };
181a0e5dbdSVincent Chen 
19800149a7SVincent Chen static bool errata_cip_453_check_func(unsigned long  arch_id, unsigned long impid)
20800149a7SVincent Chen {
21800149a7SVincent Chen 	/*
22800149a7SVincent Chen 	 * Affected cores:
23800149a7SVincent Chen 	 * Architecture ID: 0x8000000000000007
24800149a7SVincent Chen 	 * Implement ID: 0x20181004 <= impid <= 0x20191105
25800149a7SVincent Chen 	 */
26800149a7SVincent Chen 	if (arch_id != 0x8000000000000007 ||
27800149a7SVincent Chen 	    (impid < 0x20181004 || impid > 0x20191105))
28800149a7SVincent Chen 		return false;
29800149a7SVincent Chen 	return true;
30800149a7SVincent Chen }
31800149a7SVincent Chen 
32bff3ff52SVincent Chen static bool errata_cip_1200_check_func(unsigned long  arch_id, unsigned long impid)
33bff3ff52SVincent Chen {
34bff3ff52SVincent Chen 	/*
35bff3ff52SVincent Chen 	 * Affected cores:
36bff3ff52SVincent Chen 	 * Architecture ID: 0x8000000000000007 or 0x1
37bff3ff52SVincent Chen 	 * Implement ID: mimpid[23:0] <= 0x200630 and mimpid != 0x01200626
38bff3ff52SVincent Chen 	 */
39bff3ff52SVincent Chen 	if (arch_id != 0x8000000000000007 && arch_id != 0x1)
40bff3ff52SVincent Chen 		return false;
41bff3ff52SVincent Chen 	if ((impid & 0xffffff) > 0x200630 || impid == 0x1200626)
42bff3ff52SVincent Chen 		return false;
43bff3ff52SVincent Chen 	return true;
44bff3ff52SVincent Chen }
45bff3ff52SVincent Chen 
46800149a7SVincent Chen static struct errata_info_t errata_list[ERRATA_SIFIVE_NUMBER] = {
47800149a7SVincent Chen 	{
48800149a7SVincent Chen 		.name = "cip-453",
49800149a7SVincent Chen 		.check_func = errata_cip_453_check_func
50800149a7SVincent Chen 	},
51bff3ff52SVincent Chen 	{
52bff3ff52SVincent Chen 		.name = "cip-1200",
53bff3ff52SVincent Chen 		.check_func = errata_cip_1200_check_func
54bff3ff52SVincent Chen 	},
55800149a7SVincent Chen };
56800149a7SVincent Chen 
571a0e5dbdSVincent Chen static u32 __init sifive_errata_probe(unsigned long archid, unsigned long impid)
581a0e5dbdSVincent Chen {
591a0e5dbdSVincent Chen 	int idx;
601a0e5dbdSVincent Chen 	u32 cpu_req_errata = 0;
611a0e5dbdSVincent Chen 
621a0e5dbdSVincent Chen 	for (idx = 0; idx < ERRATA_SIFIVE_NUMBER; idx++)
631a0e5dbdSVincent Chen 		if (errata_list[idx].check_func(archid, impid))
641a0e5dbdSVincent Chen 			cpu_req_errata |= (1U << idx);
651a0e5dbdSVincent Chen 
661a0e5dbdSVincent Chen 	return cpu_req_errata;
671a0e5dbdSVincent Chen }
681a0e5dbdSVincent Chen 
691a0e5dbdSVincent Chen static void __init warn_miss_errata(u32 miss_errata)
701a0e5dbdSVincent Chen {
711a0e5dbdSVincent Chen 	int i;
721a0e5dbdSVincent Chen 
731a0e5dbdSVincent Chen 	pr_warn("----------------------------------------------------------------\n");
741a0e5dbdSVincent Chen 	pr_warn("WARNING: Missing the following errata may cause potential issues\n");
751a0e5dbdSVincent Chen 	for (i = 0; i < ERRATA_SIFIVE_NUMBER; i++)
761a0e5dbdSVincent Chen 		if (miss_errata & 0x1 << i)
771a0e5dbdSVincent Chen 			pr_warn("\tSiFive Errata[%d]:%s\n", i, errata_list[i].name);
781a0e5dbdSVincent Chen 	pr_warn("Please enable the corresponding Kconfig to apply them\n");
791a0e5dbdSVincent Chen 	pr_warn("----------------------------------------------------------------\n");
801a0e5dbdSVincent Chen }
811a0e5dbdSVincent Chen 
821a0e5dbdSVincent Chen void __init sifive_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
83*d14ca1f8SHeiko Stuebner 				     unsigned long archid, unsigned long impid,
84*d14ca1f8SHeiko Stuebner 				     unsigned int stage)
851a0e5dbdSVincent Chen {
861a0e5dbdSVincent Chen 	struct alt_entry *alt;
871a0e5dbdSVincent Chen 	u32 cpu_req_errata = sifive_errata_probe(archid, impid);
881a0e5dbdSVincent Chen 	u32 cpu_apply_errata = 0;
891a0e5dbdSVincent Chen 	u32 tmp;
901a0e5dbdSVincent Chen 
911a0e5dbdSVincent Chen 	for (alt = begin; alt < end; alt++) {
921a0e5dbdSVincent Chen 		if (alt->vendor_id != SIFIVE_VENDOR_ID)
931a0e5dbdSVincent Chen 			continue;
941a0e5dbdSVincent Chen 		if (alt->errata_id >= ERRATA_SIFIVE_NUMBER) {
951a0e5dbdSVincent Chen 			WARN(1, "This errata id:%d is not in kernel errata list", alt->errata_id);
961a0e5dbdSVincent Chen 			continue;
971a0e5dbdSVincent Chen 		}
981a0e5dbdSVincent Chen 
991a0e5dbdSVincent Chen 		tmp = (1U << alt->errata_id);
1001a0e5dbdSVincent Chen 		if (cpu_req_errata & tmp) {
1011a0e5dbdSVincent Chen 			patch_text_nosync(alt->old_ptr, alt->alt_ptr, alt->alt_len);
1021a0e5dbdSVincent Chen 			cpu_apply_errata |= tmp;
1031a0e5dbdSVincent Chen 		}
1041a0e5dbdSVincent Chen 	}
1051a0e5dbdSVincent Chen 	if (cpu_apply_errata != cpu_req_errata)
1061a0e5dbdSVincent Chen 		warn_miss_errata(cpu_req_errata - cpu_apply_errata);
1071a0e5dbdSVincent Chen }
108