xref: /linux/arch/riscv/errata/thead/errata.c (revision bb3f89487fd936df7cc5165bae37ca2669056c5c)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2021 Heiko Stuebner <heiko@sntech.de>
4  */
5 
6 #include <linux/bug.h>
7 #include <linux/kernel.h>
8 #include <linux/memory.h>
9 #include <linux/module.h>
10 #include <linux/string.h>
11 #include <linux/uaccess.h>
12 #include <asm/alternative.h>
13 #include <asm/cacheflush.h>
14 #include <asm/cpufeature.h>
15 #include <asm/errata_list.h>
16 #include <asm/hwprobe.h>
17 #include <asm/patch.h>
18 #include <asm/vendorid_list.h>
19 
20 static bool errata_probe_pbmt(unsigned int stage,
21 			      unsigned long arch_id, unsigned long impid)
22 {
23 	if (!IS_ENABLED(CONFIG_ERRATA_THEAD_PBMT))
24 		return false;
25 
26 	if (arch_id != 0 || impid != 0)
27 		return false;
28 
29 	if (stage == RISCV_ALTERNATIVES_EARLY_BOOT ||
30 	    stage == RISCV_ALTERNATIVES_MODULE)
31 		return true;
32 
33 	return false;
34 }
35 
36 static bool errata_probe_cmo(unsigned int stage,
37 			     unsigned long arch_id, unsigned long impid)
38 {
39 	if (!IS_ENABLED(CONFIG_ERRATA_THEAD_CMO))
40 		return false;
41 
42 	if (arch_id != 0 || impid != 0)
43 		return false;
44 
45 	if (stage == RISCV_ALTERNATIVES_EARLY_BOOT)
46 		return false;
47 
48 	riscv_cbom_block_size = L1_CACHE_BYTES;
49 	riscv_noncoherent_supported();
50 	return true;
51 }
52 
53 static bool errata_probe_pmu(unsigned int stage,
54 			     unsigned long arch_id, unsigned long impid)
55 {
56 	if (!IS_ENABLED(CONFIG_ERRATA_THEAD_PMU))
57 		return false;
58 
59 	/* target-c9xx cores report arch_id and impid as 0 */
60 	if (arch_id != 0 || impid != 0)
61 		return false;
62 
63 	if (stage == RISCV_ALTERNATIVES_EARLY_BOOT)
64 		return false;
65 
66 	return true;
67 }
68 
69 static u32 thead_errata_probe(unsigned int stage,
70 			      unsigned long archid, unsigned long impid)
71 {
72 	u32 cpu_req_errata = 0;
73 
74 	if (errata_probe_pbmt(stage, archid, impid))
75 		cpu_req_errata |= BIT(ERRATA_THEAD_PBMT);
76 
77 	if (errata_probe_cmo(stage, archid, impid))
78 		cpu_req_errata |= BIT(ERRATA_THEAD_CMO);
79 
80 	if (errata_probe_pmu(stage, archid, impid))
81 		cpu_req_errata |= BIT(ERRATA_THEAD_PMU);
82 
83 	return cpu_req_errata;
84 }
85 
86 void __init_or_module thead_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
87 					      unsigned long archid, unsigned long impid,
88 					      unsigned int stage)
89 {
90 	struct alt_entry *alt;
91 	u32 cpu_req_errata = thead_errata_probe(stage, archid, impid);
92 	u32 tmp;
93 	void *oldptr, *altptr;
94 
95 	for (alt = begin; alt < end; alt++) {
96 		if (alt->vendor_id != THEAD_VENDOR_ID)
97 			continue;
98 		if (alt->patch_id >= ERRATA_THEAD_NUMBER)
99 			continue;
100 
101 		tmp = (1U << alt->patch_id);
102 		if (cpu_req_errata & tmp) {
103 			oldptr = ALT_OLD_PTR(alt);
104 			altptr = ALT_ALT_PTR(alt);
105 
106 			/* On vm-alternatives, the mmu isn't running yet */
107 			if (stage == RISCV_ALTERNATIVES_EARLY_BOOT) {
108 				memcpy(oldptr, altptr, alt->alt_len);
109 			} else {
110 				mutex_lock(&text_mutex);
111 				patch_text_nosync(oldptr, altptr, alt->alt_len);
112 				mutex_unlock(&text_mutex);
113 			}
114 		}
115 	}
116 
117 	if (stage == RISCV_ALTERNATIVES_EARLY_BOOT)
118 		local_flush_icache_all();
119 }
120 
121 void thead_feature_probe_func(unsigned int cpu,
122 			      unsigned long archid,
123 			      unsigned long impid)
124 {
125 	if ((archid == 0) && (impid == 0))
126 		per_cpu(misaligned_access_speed, cpu) = RISCV_HWPROBE_MISALIGNED_FAST;
127 }
128