riscv: cpu: improve multi-letter extension detection in supports_extension()
[pandora-u-boot.git] / arch / riscv / cpu / cpu.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
4  */
5
6 #include <command.h>
7 #include <cpu.h>
8 #include <cpu_func.h>
9 #include <dm.h>
10 #include <dm/lists.h>
11 #include <event.h>
12 #include <hang.h>
13 #include <init.h>
14 #include <log.h>
15 #include <asm/encoding.h>
16 #include <asm/system.h>
17 #include <dm/uclass-internal.h>
18 #include <linux/bitops.h>
19
20 /*
21  * The variables here must be stored in the data section since they are used
22  * before the bss section is available.
23  */
24 #if !CONFIG_IS_ENABLED(XIP)
25 u32 hart_lottery __section(".data") = 0;
26
27 #ifdef CONFIG_AVAILABLE_HARTS
28 /*
29  * The main hart running U-Boot has acquired available_harts_lock until it has
30  * finished initialization of global data.
31  */
32 u32 available_harts_lock = 1;
33 #endif
34 #endif
35
36 static inline bool supports_extension(char ext)
37 {
38 #if CONFIG_IS_ENABLED(RISCV_MMODE)
39         return csr_read(CSR_MISA) & (1 << (ext - 'a'));
40 #elif CONFIG_CPU
41         struct udevice *dev;
42         char desc[32];
43         int i;
44
45         uclass_find_first_device(UCLASS_CPU, &dev);
46         if (!dev) {
47                 debug("unable to find the RISC-V cpu device\n");
48                 return false;
49         }
50         if (!cpu_get_desc(dev, desc, sizeof(desc))) {
51                 /*
52                  * skip the first 4 characters (rv32|rv64)
53                  */
54                 for (i = 4; i < sizeof(desc); i++) {
55                         switch (desc[i]) {
56                         case 's':
57                         case 'x':
58                         case 'z':
59                         case '_':
60                         case '\0':
61                                 /*
62                                  * Any of these characters mean the single
63                                  * letter extensions have all been consumed.
64                                  */
65                                 return false;
66                         default:
67                                 if (desc[i] == ext)
68                                         return true;
69                         }
70                 }
71         }
72
73         return false;
74 #else  /* !CONFIG_CPU */
75 #warning "There is no way to determine the available extensions in S-mode."
76 #warning "Please convert your board to use the RISC-V CPU driver."
77         return false;
78 #endif /* CONFIG_CPU */
79 }
80
81 static int riscv_cpu_probe(void)
82 {
83 #ifdef CONFIG_CPU
84         int ret;
85
86         /* probe cpus so that RISC-V timer can be bound */
87         ret = cpu_probe_all();
88         if (ret)
89                 return log_msg_ret("RISC-V cpus probe failed\n", ret);
90 #endif
91
92         return 0;
93 }
94 EVENT_SPY_SIMPLE(EVT_DM_POST_INIT_R, riscv_cpu_probe);
95
96 /*
97  * This is called on secondary harts just after the IPI is init'd. Currently
98  * there's nothing to do, since we just need to clear any existing IPIs, and
99  * that is handled by the sending of an ipi itself.
100  */
101 #if CONFIG_IS_ENABLED(SMP)
102 static void dummy_pending_ipi_clear(ulong hart, ulong arg0, ulong arg1)
103 {
104 }
105 #endif
106
107 int riscv_cpu_setup(void)
108 {
109         int __maybe_unused ret;
110
111         /* Enable FPU */
112         if (supports_extension('d') || supports_extension('f')) {
113                 csr_set(MODE_PREFIX(status), MSTATUS_FS);
114                 csr_write(CSR_FCSR, 0);
115         }
116
117         if (CONFIG_IS_ENABLED(RISCV_MMODE)) {
118                 /*
119                  * Enable perf counters for cycle, time,
120                  * and instret counters only
121                  */
122                 if (supports_extension('u')) {
123 #ifdef CONFIG_RISCV_PRIV_1_9
124                         csr_write(CSR_MSCOUNTEREN, GENMASK(2, 0));
125                         csr_write(CSR_MUCOUNTEREN, GENMASK(2, 0));
126 #else
127                         csr_write(CSR_MCOUNTEREN, GENMASK(2, 0));
128 #endif
129                 }
130
131                 /* Disable paging */
132                 if (supports_extension('s'))
133 #ifdef CONFIG_RISCV_PRIV_1_9
134                         csr_read_clear(CSR_MSTATUS, SR_VM);
135 #else
136                         csr_write(CSR_SATP, 0);
137 #endif
138         }
139
140 #if CONFIG_IS_ENABLED(SMP)
141         ret = riscv_init_ipi();
142         if (ret)
143                 return ret;
144
145         /*
146          * Clear all pending IPIs on secondary harts. We don't do anything on
147          * the boot hart, since we never send an IPI to ourselves, and no
148          * interrupts are enabled
149          */
150         ret = smp_call_function((ulong)dummy_pending_ipi_clear, 0, 0, 0);
151         if (ret)
152                 return ret;
153 #endif
154
155         return 0;
156 }
157 EVENT_SPY_SIMPLE(EVT_DM_POST_INIT_F, riscv_cpu_setup);
158
159 int arch_early_init_r(void)
160 {
161         if (IS_ENABLED(CONFIG_SYSRESET_SBI))
162                 device_bind_driver(gd->dm_root, "sbi-sysreset",
163                                    "sbi-sysreset", NULL);
164
165         return 0;
166 }
167
168 /**
169  * harts_early_init() - A callback function called by start.S to configure
170  * feature settings of each hart.
171  *
172  * In a multi-core system, memory access shall be careful here, it shall
173  * take care of race conditions.
174  */
175 __weak void harts_early_init(void)
176 {
177 }
178
179 #if !CONFIG_IS_ENABLED(SYSRESET)
180 void reset_cpu(void)
181 {
182         printf("resetting ...\n");
183
184         printf("reset not supported yet\n");
185         hang();
186 }
187 #endif