1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2013 Altera Corporation |
4 | * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch> |
5 | * |
6 | * Based on cpuinfo.c from microblaze |
7 | */ |
8 | |
9 | #include <linux/kernel.h> |
10 | #include <linux/init.h> |
11 | #include <linux/delay.h> |
12 | #include <linux/seq_file.h> |
13 | #include <linux/string.h> |
14 | #include <linux/of.h> |
15 | #include <asm/cpuinfo.h> |
16 | |
17 | struct cpuinfo cpuinfo; |
18 | |
19 | #define err_cpu(x) \ |
20 | pr_err("ERROR: Nios II " x " different for kernel and DTS\n") |
21 | |
22 | static inline u32 fcpu(struct device_node *cpu, const char *n) |
23 | { |
24 | u32 val = 0; |
25 | |
26 | of_property_read_u32(np: cpu, propname: n, out_value: &val); |
27 | |
28 | return val; |
29 | } |
30 | |
31 | void __init setup_cpuinfo(void) |
32 | { |
33 | struct device_node *cpu; |
34 | const char *str; |
35 | int len; |
36 | |
37 | cpu = of_get_cpu_node(cpu: 0, NULL); |
38 | if (!cpu) |
39 | panic(fmt: "%s: No CPU found in devicetree!\n" , __func__); |
40 | |
41 | if (!of_property_read_bool(np: cpu, propname: "altr,has-initda" )) |
42 | panic(fmt: "initda instruction is unimplemented. Please update your " |
43 | "hardware system to have more than 4-byte line data " |
44 | "cache\n" ); |
45 | |
46 | cpuinfo.cpu_clock_freq = fcpu(cpu, n: "clock-frequency" ); |
47 | |
48 | str = of_get_property(node: cpu, name: "altr,implementation" , lenp: &len); |
49 | if (str) |
50 | strscpy(cpuinfo.cpu_impl, str, sizeof(cpuinfo.cpu_impl)); |
51 | else |
52 | strcpy(cpuinfo.cpu_impl, "<unknown>" ); |
53 | |
54 | cpuinfo.has_div = of_property_read_bool(np: cpu, propname: "altr,has-div" ); |
55 | cpuinfo.has_mul = of_property_read_bool(np: cpu, propname: "altr,has-mul" ); |
56 | cpuinfo.has_mulx = of_property_read_bool(np: cpu, propname: "altr,has-mulx" ); |
57 | cpuinfo.has_bmx = of_property_read_bool(np: cpu, propname: "altr,has-bmx" ); |
58 | cpuinfo.has_cdx = of_property_read_bool(np: cpu, propname: "altr,has-cdx" ); |
59 | cpuinfo.mmu = of_property_read_bool(np: cpu, propname: "altr,has-mmu" ); |
60 | |
61 | if (IS_ENABLED(CONFIG_NIOS2_HW_DIV_SUPPORT) && !cpuinfo.has_div) |
62 | err_cpu("DIV" ); |
63 | |
64 | if (IS_ENABLED(CONFIG_NIOS2_HW_MUL_SUPPORT) && !cpuinfo.has_mul) |
65 | err_cpu("MUL" ); |
66 | |
67 | if (IS_ENABLED(CONFIG_NIOS2_HW_MULX_SUPPORT) && !cpuinfo.has_mulx) |
68 | err_cpu("MULX" ); |
69 | |
70 | if (IS_ENABLED(CONFIG_NIOS2_BMX_SUPPORT) && !cpuinfo.has_bmx) |
71 | err_cpu("BMX" ); |
72 | |
73 | if (IS_ENABLED(CONFIG_NIOS2_CDX_SUPPORT) && !cpuinfo.has_cdx) |
74 | err_cpu("CDX" ); |
75 | |
76 | cpuinfo.tlb_num_ways = fcpu(cpu, n: "altr,tlb-num-ways" ); |
77 | if (!cpuinfo.tlb_num_ways) |
78 | panic(fmt: "altr,tlb-num-ways can't be 0. Please check your hardware " |
79 | "system\n" ); |
80 | cpuinfo.icache_line_size = fcpu(cpu, n: "icache-line-size" ); |
81 | cpuinfo.icache_size = fcpu(cpu, n: "icache-size" ); |
82 | if (CONFIG_NIOS2_ICACHE_SIZE != cpuinfo.icache_size) |
83 | pr_warn("Warning: icache size configuration mismatch " |
84 | "(0x%x vs 0x%x) of CONFIG_NIOS2_ICACHE_SIZE vs " |
85 | "device tree icache-size\n" , |
86 | CONFIG_NIOS2_ICACHE_SIZE, cpuinfo.icache_size); |
87 | |
88 | cpuinfo.dcache_line_size = fcpu(cpu, n: "dcache-line-size" ); |
89 | if (CONFIG_NIOS2_DCACHE_LINE_SIZE != cpuinfo.dcache_line_size) |
90 | pr_warn("Warning: dcache line size configuration mismatch " |
91 | "(0x%x vs 0x%x) of CONFIG_NIOS2_DCACHE_LINE_SIZE vs " |
92 | "device tree dcache-line-size\n" , |
93 | CONFIG_NIOS2_DCACHE_LINE_SIZE, cpuinfo.dcache_line_size); |
94 | cpuinfo.dcache_size = fcpu(cpu, n: "dcache-size" ); |
95 | if (CONFIG_NIOS2_DCACHE_SIZE != cpuinfo.dcache_size) |
96 | pr_warn("Warning: dcache size configuration mismatch " |
97 | "(0x%x vs 0x%x) of CONFIG_NIOS2_DCACHE_SIZE vs " |
98 | "device tree dcache-size\n" , |
99 | CONFIG_NIOS2_DCACHE_SIZE, cpuinfo.dcache_size); |
100 | |
101 | cpuinfo.tlb_pid_num_bits = fcpu(cpu, n: "altr,pid-num-bits" ); |
102 | cpuinfo.tlb_num_ways_log2 = ilog2(cpuinfo.tlb_num_ways); |
103 | cpuinfo.tlb_num_entries = fcpu(cpu, n: "altr,tlb-num-entries" ); |
104 | cpuinfo.tlb_num_lines = cpuinfo.tlb_num_entries / cpuinfo.tlb_num_ways; |
105 | cpuinfo.tlb_ptr_sz = fcpu(cpu, n: "altr,tlb-ptr-sz" ); |
106 | |
107 | cpuinfo.reset_addr = fcpu(cpu, n: "altr,reset-addr" ); |
108 | cpuinfo.exception_addr = fcpu(cpu, n: "altr,exception-addr" ); |
109 | cpuinfo.fast_tlb_miss_exc_addr = fcpu(cpu, n: "altr,fast-tlb-miss-addr" ); |
110 | |
111 | of_node_put(node: cpu); |
112 | } |
113 | |
114 | #ifdef CONFIG_PROC_FS |
115 | |
116 | /* |
117 | * Get CPU information for use by the procfs. |
118 | */ |
119 | static int show_cpuinfo(struct seq_file *m, void *v) |
120 | { |
121 | const u32 clockfreq = cpuinfo.cpu_clock_freq; |
122 | |
123 | seq_printf(m, |
124 | fmt: "CPU:\t\tNios II/%s\n" |
125 | "REV:\t\t%i\n" |
126 | "MMU:\t\t%s\n" |
127 | "FPU:\t\tnone\n" |
128 | "Clocking:\t%u.%02u MHz\n" |
129 | "BogoMips:\t%lu.%02lu\n" |
130 | "Calibration:\t%lu loops\n" , |
131 | cpuinfo.cpu_impl, |
132 | CONFIG_NIOS2_ARCH_REVISION, |
133 | cpuinfo.mmu ? "present" : "none" , |
134 | clockfreq / 1000000, (clockfreq / 100000) % 10, |
135 | (loops_per_jiffy * HZ) / 500000, |
136 | ((loops_per_jiffy * HZ) / 5000) % 100, |
137 | (loops_per_jiffy * HZ)); |
138 | |
139 | seq_printf(m, |
140 | fmt: "HW:\n" |
141 | " MUL:\t\t%s\n" |
142 | " MULX:\t\t%s\n" |
143 | " DIV:\t\t%s\n" |
144 | " BMX:\t\t%s\n" |
145 | " CDX:\t\t%s\n" , |
146 | cpuinfo.has_mul ? "yes" : "no" , |
147 | cpuinfo.has_mulx ? "yes" : "no" , |
148 | cpuinfo.has_div ? "yes" : "no" , |
149 | cpuinfo.has_bmx ? "yes" : "no" , |
150 | cpuinfo.has_cdx ? "yes" : "no" ); |
151 | |
152 | seq_printf(m, |
153 | fmt: "Icache:\t\t%ukB, line length: %u\n" , |
154 | cpuinfo.icache_size >> 10, |
155 | cpuinfo.icache_line_size); |
156 | |
157 | seq_printf(m, |
158 | fmt: "Dcache:\t\t%ukB, line length: %u\n" , |
159 | cpuinfo.dcache_size >> 10, |
160 | cpuinfo.dcache_line_size); |
161 | |
162 | seq_printf(m, |
163 | fmt: "TLB:\t\t%u ways, %u entries, %u PID bits\n" , |
164 | cpuinfo.tlb_num_ways, |
165 | cpuinfo.tlb_num_entries, |
166 | cpuinfo.tlb_pid_num_bits); |
167 | |
168 | return 0; |
169 | } |
170 | |
171 | static void *cpuinfo_start(struct seq_file *m, loff_t *pos) |
172 | { |
173 | unsigned long i = *pos; |
174 | |
175 | return i < num_possible_cpus() ? (void *) (i + 1) : NULL; |
176 | } |
177 | |
178 | static void *cpuinfo_next(struct seq_file *m, void *v, loff_t *pos) |
179 | { |
180 | ++*pos; |
181 | return cpuinfo_start(m, pos); |
182 | } |
183 | |
184 | static void cpuinfo_stop(struct seq_file *m, void *v) |
185 | { |
186 | } |
187 | |
188 | const struct seq_operations cpuinfo_op = { |
189 | .start = cpuinfo_start, |
190 | .next = cpuinfo_next, |
191 | .stop = cpuinfo_stop, |
192 | .show = show_cpuinfo |
193 | }; |
194 | |
195 | #endif /* CONFIG_PROC_FS */ |
196 | |