Bin Meng | 055700e | 2018-09-26 06:55:14 -0700 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* |
| 3 | * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com> |
| 4 | */ |
| 5 | |
Simon Glass | 34ee3ed | 2023-12-15 20:14:09 -0700 | [diff] [blame] | 6 | #include <command.h> |
Bin Meng | 7a3bbfb | 2018-12-12 06:12:34 -0800 | [diff] [blame] | 7 | #include <cpu.h> |
Simon Glass | 34ee3ed | 2023-12-15 20:14:09 -0700 | [diff] [blame] | 8 | #include <cpu_func.h> |
Bin Meng | edfe9a9 | 2018-12-12 06:12:38 -0800 | [diff] [blame] | 9 | #include <dm.h> |
Heinrich Schuchardt | cc382ff | 2021-09-12 21:11:46 +0200 | [diff] [blame] | 10 | #include <dm/lists.h> |
Simon Glass | fc55736 | 2022-03-04 08:43:05 -0700 | [diff] [blame] | 11 | #include <event.h> |
Simon Glass | 34ee3ed | 2023-12-15 20:14:09 -0700 | [diff] [blame] | 12 | #include <hang.h> |
Simon Glass | 9758973 | 2020-05-10 11:40:02 -0600 | [diff] [blame] | 13 | #include <init.h> |
Yao Zi | 4a9b1b0 | 2025-01-23 09:11:33 +0000 | [diff] [blame] | 14 | #include <irq_func.h> |
Bin Meng | 7a3bbfb | 2018-12-12 06:12:34 -0800 | [diff] [blame] | 15 | #include <log.h> |
Bin Meng | a7544ed | 2018-12-12 06:12:40 -0800 | [diff] [blame] | 16 | #include <asm/encoding.h> |
Simon Glass | fc55736 | 2022-03-04 08:43:05 -0700 | [diff] [blame] | 17 | #include <asm/system.h> |
Mayuresh Chitale | 7df8a0c | 2025-01-06 13:04:04 +0000 | [diff] [blame] | 18 | #include <asm/hwcap.h> |
| 19 | #include <asm/cpufeature.h> |
Yao Zi | 4a9b1b0 | 2025-01-23 09:11:33 +0000 | [diff] [blame] | 20 | #include <asm/cache.h> |
Bin Meng | edfe9a9 | 2018-12-12 06:12:38 -0800 | [diff] [blame] | 21 | #include <dm/uclass-internal.h> |
Simon Glass | 4dcacfc | 2020-05-10 11:40:13 -0600 | [diff] [blame] | 22 | #include <linux/bitops.h> |
Mayuresh Chitale | 7df8a0c | 2025-01-06 13:04:04 +0000 | [diff] [blame] | 23 | #include <linux/log2.h> |
| 24 | #include <linux/ctype.h> |
Bin Meng | 055700e | 2018-09-26 06:55:14 -0700 | [diff] [blame] | 25 | |
Lukas Auer | 39a652b | 2018-11-22 11:26:29 +0100 | [diff] [blame] | 26 | /* |
Lukas Auer | a359665 | 2019-03-17 19:28:37 +0100 | [diff] [blame] | 27 | * The variables here must be stored in the data section since they are used |
Lukas Auer | 39a652b | 2018-11-22 11:26:29 +0100 | [diff] [blame] | 28 | * before the bss section is available. |
| 29 | */ |
Nikita Shubin | 7e5e029 | 2022-09-02 11:47:39 +0300 | [diff] [blame] | 30 | #if !CONFIG_IS_ENABLED(XIP) |
Marek BehĂșn | 4bebdd3 | 2021-05-20 13:23:52 +0200 | [diff] [blame] | 31 | u32 hart_lottery __section(".data") = 0; |
Lukas Auer | a359665 | 2019-03-17 19:28:37 +0100 | [diff] [blame] | 32 | |
Rick Chen | 9c4d5c1 | 2022-09-21 14:34:54 +0800 | [diff] [blame] | 33 | #ifdef CONFIG_AVAILABLE_HARTS |
Lukas Auer | a359665 | 2019-03-17 19:28:37 +0100 | [diff] [blame] | 34 | /* |
| 35 | * The main hart running U-Boot has acquired available_harts_lock until it has |
| 36 | * finished initialization of global data. |
| 37 | */ |
| 38 | u32 available_harts_lock = 1; |
Rick Chen | e5e6c36 | 2019-04-30 13:49:33 +0800 | [diff] [blame] | 39 | #endif |
Rick Chen | 9c4d5c1 | 2022-09-21 14:34:54 +0800 | [diff] [blame] | 40 | #endif |
Lukas Auer | 39a652b | 2018-11-22 11:26:29 +0100 | [diff] [blame] | 41 | |
Mayuresh Chitale | 7df8a0c | 2025-01-06 13:04:04 +0000 | [diff] [blame] | 42 | /* Host ISA bitmap */ |
| 43 | static DECLARE_BITMAP(riscv_isa, RISCV_ISA_EXT_MAX) __section(".data"); |
| 44 | |
| 45 | static unsigned int riscv_cbom_block_size __section(".data"); |
| 46 | static unsigned int riscv_cboz_block_size __section(".data"); |
| 47 | /** |
| 48 | * __riscv_isa_extension_available() - Check whether given extension |
| 49 | * is available or not |
| 50 | * |
| 51 | * @bit: bit position of the desired extension |
| 52 | * Return: true or false |
| 53 | * |
| 54 | */ |
| 55 | static bool __riscv_isa_extension_available(unsigned int bit) |
| 56 | { |
| 57 | if (bit >= RISCV_ISA_EXT_MAX) |
| 58 | return false; |
| 59 | |
| 60 | return test_bit(bit, riscv_isa) ? true : false; |
| 61 | } |
| 62 | |
| 63 | inline unsigned int riscv_get_cbom_block_size(void) |
| 64 | { |
| 65 | return riscv_cbom_block_size; |
| 66 | } |
| 67 | |
| 68 | inline unsigned int riscv_get_cboz_block_size(void) |
| 69 | { |
| 70 | return riscv_cboz_block_size; |
| 71 | } |
| 72 | |
| 73 | static int riscv_ext_zicbom_validate(const struct riscv_isa_ext_data *data, |
| 74 | const unsigned long *isa_bitmap) |
| 75 | { |
| 76 | struct udevice *dev; |
| 77 | |
| 78 | if (!CONFIG_IS_ENABLED(RISCV_ISA_ZICBOM) || riscv_cbom_block_size) |
| 79 | return 0; |
| 80 | |
| 81 | uclass_first_device(UCLASS_CPU, &dev); |
| 82 | if (!dev) { |
| 83 | log_info("Failed to get cpu device!\n"); |
| 84 | return -ENXIO; |
| 85 | } |
| 86 | |
| 87 | if (!dev_read_u32(dev, "riscv,cbom-block-size", |
| 88 | &riscv_cbom_block_size)) { |
| 89 | if (!riscv_cbom_block_size) { |
| 90 | log_err("Zicbom detected in ISA string, disabling as no cbom-block-size found\n"); |
| 91 | return -EINVAL; |
| 92 | } |
| 93 | if (!is_power_of_2(riscv_cbom_block_size)) { |
| 94 | log_err("Zicbom disabled as cbom-block-size present, but is not a power-of-2\n"); |
| 95 | return -EINVAL; |
| 96 | } |
| 97 | return 0; |
| 98 | } else { |
| 99 | return -EINVAL; |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | static int riscv_ext_zicboz_validate(const struct riscv_isa_ext_data *data, |
| 104 | const unsigned long *isa_bitmap) |
| 105 | { |
| 106 | struct udevice *dev; |
| 107 | |
| 108 | if (!CONFIG_IS_ENABLED(RISCV_ISA_ZICBOM) || riscv_cboz_block_size) |
| 109 | return 0; |
| 110 | |
| 111 | uclass_first_device(UCLASS_CPU, &dev); |
| 112 | if (!dev) { |
| 113 | log_debug("Failed to get cpu device!\n"); |
| 114 | return -ENXIO; |
| 115 | } |
| 116 | |
| 117 | if (!dev_read_u32(dev, "riscv,cboz-block-size", |
| 118 | &riscv_cboz_block_size)) { |
| 119 | if (!riscv_cboz_block_size) { |
| 120 | log_err("Zicboz detected in ISA string, disabling as no cboz-block-size found\n"); |
| 121 | return -EINVAL; |
| 122 | } |
| 123 | if (!is_power_of_2(riscv_cboz_block_size)) { |
| 124 | log_err("Zicboz disabled as cboz-block-size present, but is not a power-of-2\n"); |
| 125 | return -EINVAL; |
| 126 | } |
| 127 | return 0; |
| 128 | } else { |
| 129 | return -EINVAL; |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | static int riscv_ext_zca_depends(const struct riscv_isa_ext_data *data, |
| 134 | const unsigned long *isa_bitmap) |
| 135 | { |
| 136 | if (__riscv_isa_extension_available(RISCV_ISA_EXT_ZCA)) |
| 137 | return 0; |
| 138 | |
| 139 | return -EINVAL; |
| 140 | } |
| 141 | |
| 142 | static int riscv_ext_zcd_validate(const struct riscv_isa_ext_data *data, |
| 143 | const unsigned long *isa_bitmap) |
| 144 | { |
| 145 | if (__riscv_isa_extension_available(RISCV_ISA_EXT_ZCA) && |
| 146 | __riscv_isa_extension_available(RISCV_ISA_EXT_d)) |
| 147 | return 0; |
| 148 | |
| 149 | return -EINVAL; |
| 150 | } |
| 151 | |
| 152 | static int riscv_ext_zcf_validate(const struct riscv_isa_ext_data *data, |
| 153 | const unsigned long *isa_bitmap) |
| 154 | { |
| 155 | if (IS_ENABLED(CONFIG_64BIT)) |
| 156 | return -EINVAL; |
| 157 | |
| 158 | if (__riscv_isa_extension_available(RISCV_ISA_EXT_ZCA) && |
| 159 | __riscv_isa_extension_available(RISCV_ISA_EXT_f)) |
| 160 | return 0; |
| 161 | |
| 162 | return -EINVAL; |
| 163 | } |
| 164 | |
| 165 | static const unsigned int riscv_zk_bundled_exts[] = { |
| 166 | RISCV_ISA_EXT_ZBKB, |
| 167 | RISCV_ISA_EXT_ZBKC, |
| 168 | RISCV_ISA_EXT_ZBKX, |
| 169 | RISCV_ISA_EXT_ZKND, |
| 170 | RISCV_ISA_EXT_ZKNE, |
| 171 | RISCV_ISA_EXT_ZKR, |
| 172 | RISCV_ISA_EXT_ZKT, |
| 173 | }; |
| 174 | |
| 175 | static const unsigned int riscv_zkn_bundled_exts[] = { |
| 176 | RISCV_ISA_EXT_ZBKB, |
| 177 | RISCV_ISA_EXT_ZBKC, |
| 178 | RISCV_ISA_EXT_ZBKX, |
| 179 | RISCV_ISA_EXT_ZKND, |
| 180 | RISCV_ISA_EXT_ZKNE, |
| 181 | RISCV_ISA_EXT_ZKNH, |
| 182 | }; |
| 183 | |
| 184 | static const unsigned int riscv_zks_bundled_exts[] = { |
| 185 | RISCV_ISA_EXT_ZBKB, |
| 186 | RISCV_ISA_EXT_ZBKC, |
| 187 | RISCV_ISA_EXT_ZKSED, |
| 188 | RISCV_ISA_EXT_ZKSH |
| 189 | }; |
| 190 | |
| 191 | #define RISCV_ISA_EXT_ZVKN \ |
| 192 | RISCV_ISA_EXT_ZVKNED, \ |
| 193 | RISCV_ISA_EXT_ZVKNHB, \ |
| 194 | RISCV_ISA_EXT_ZVKB, \ |
| 195 | RISCV_ISA_EXT_ZVKT |
| 196 | |
| 197 | static const unsigned int riscv_zvkn_bundled_exts[] = { |
| 198 | RISCV_ISA_EXT_ZVKN |
| 199 | }; |
| 200 | |
| 201 | static const unsigned int riscv_zvknc_bundled_exts[] = { |
| 202 | RISCV_ISA_EXT_ZVKN, |
| 203 | RISCV_ISA_EXT_ZVBC |
| 204 | }; |
| 205 | |
| 206 | static const unsigned int riscv_zvkng_bundled_exts[] = { |
| 207 | RISCV_ISA_EXT_ZVKN, |
| 208 | RISCV_ISA_EXT_ZVKG |
| 209 | }; |
| 210 | |
| 211 | #define RISCV_ISA_EXT_ZVKS \ |
| 212 | RISCV_ISA_EXT_ZVKSED, \ |
| 213 | RISCV_ISA_EXT_ZVKSH, \ |
| 214 | RISCV_ISA_EXT_ZVKB, \ |
| 215 | RISCV_ISA_EXT_ZVKT |
| 216 | |
| 217 | static const unsigned int riscv_zvks_bundled_exts[] = { |
| 218 | RISCV_ISA_EXT_ZVKS |
| 219 | }; |
| 220 | |
| 221 | static const unsigned int riscv_zvksc_bundled_exts[] = { |
| 222 | RISCV_ISA_EXT_ZVKS, |
| 223 | RISCV_ISA_EXT_ZVBC |
| 224 | }; |
| 225 | |
| 226 | static const unsigned int riscv_zvksg_bundled_exts[] = { |
| 227 | RISCV_ISA_EXT_ZVKS, |
| 228 | RISCV_ISA_EXT_ZVKG |
| 229 | }; |
| 230 | |
| 231 | static const unsigned int riscv_zvbb_exts[] = { |
| 232 | RISCV_ISA_EXT_ZVKB |
| 233 | }; |
| 234 | |
| 235 | #define RISCV_ISA_EXT_ZVE64F_IMPLY_LIST \ |
| 236 | RISCV_ISA_EXT_ZVE64X, \ |
| 237 | RISCV_ISA_EXT_ZVE32F, \ |
| 238 | RISCV_ISA_EXT_ZVE32X |
| 239 | |
| 240 | #define RISCV_ISA_EXT_ZVE64D_IMPLY_LIST \ |
| 241 | RISCV_ISA_EXT_ZVE64F, \ |
| 242 | RISCV_ISA_EXT_ZVE64F_IMPLY_LIST |
| 243 | |
| 244 | #define RISCV_ISA_EXT_V_IMPLY_LIST \ |
| 245 | RISCV_ISA_EXT_ZVE64D, \ |
| 246 | RISCV_ISA_EXT_ZVE64D_IMPLY_LIST |
| 247 | |
| 248 | static const unsigned int riscv_zve32f_exts[] = { |
| 249 | RISCV_ISA_EXT_ZVE32X |
| 250 | }; |
| 251 | |
| 252 | static const unsigned int riscv_zve64f_exts[] = { |
| 253 | RISCV_ISA_EXT_ZVE64F_IMPLY_LIST |
| 254 | }; |
| 255 | |
| 256 | static const unsigned int riscv_zve64d_exts[] = { |
| 257 | RISCV_ISA_EXT_ZVE64D_IMPLY_LIST |
| 258 | }; |
| 259 | |
| 260 | static const unsigned int riscv_v_exts[] = { |
| 261 | RISCV_ISA_EXT_V_IMPLY_LIST |
| 262 | }; |
| 263 | |
| 264 | static const unsigned int riscv_zve64x_exts[] = { |
| 265 | RISCV_ISA_EXT_ZVE32X, |
| 266 | RISCV_ISA_EXT_ZVE64X |
| 267 | }; |
| 268 | |
| 269 | /* |
| 270 | * While the [ms]envcfg CSRs were not defined until version 1.12 of the RISC-V |
| 271 | * privileged ISA, the existence of the CSRs is implied by any extension which |
| 272 | * specifies [ms]envcfg bit(s). Hence, we define a custom ISA extension for the |
| 273 | * existence of the CSR, and treat it as a subset of those other extensions. |
| 274 | */ |
| 275 | static const unsigned int riscv_xlinuxenvcfg_exts[] = { |
| 276 | RISCV_ISA_EXT_XLINUXENVCFG |
| 277 | }; |
| 278 | |
| 279 | /* |
| 280 | * Zc* spec states that: |
| 281 | * - C always implies Zca |
| 282 | * - C+F implies Zcf (RV32 only) |
| 283 | * - C+D implies Zcd |
| 284 | * |
| 285 | * These extensions will be enabled and then validated depending on the |
| 286 | * availability of F/D RV32. |
| 287 | */ |
| 288 | static const unsigned int riscv_c_exts[] = { |
| 289 | RISCV_ISA_EXT_ZCA, |
| 290 | RISCV_ISA_EXT_ZCF, |
| 291 | RISCV_ISA_EXT_ZCD, |
| 292 | }; |
| 293 | |
| 294 | /* |
| 295 | * The canonical order of ISA extension names in the ISA string is defined in |
| 296 | * chapter 27 of the unprivileged specification. |
| 297 | * |
| 298 | * Ordinarily, for in-kernel data structures, this order is unimportant but |
| 299 | * isa_ext_arr defines the order of the ISA string in /proc/cpuinfo. |
| 300 | * |
| 301 | * The specification uses vague wording, such as should, when it comes to |
| 302 | * ordering, so for our purposes the following rules apply: |
| 303 | * |
| 304 | * 1. All multi-letter extensions must be separated from other extensions by an |
| 305 | * underscore. |
| 306 | * |
| 307 | * 2. Additional standard extensions (starting with 'Z') must be sorted after |
| 308 | * single-letter extensions and before any higher-privileged extensions. |
| 309 | * |
| 310 | * 3. The first letter following the 'Z' conventionally indicates the most |
| 311 | * closely related alphabetical extension category, IMAFDQLCBKJTPVH. |
| 312 | * If multiple 'Z' extensions are named, they must be ordered first by |
| 313 | * category, then alphabetically within a category. |
| 314 | * |
| 315 | * 3. Standard supervisor-level extensions (starting with 'S') must be listed |
| 316 | * after standard unprivileged extensions. If multiple supervisor-level |
| 317 | * extensions are listed, they must be ordered alphabetically. |
| 318 | * |
| 319 | * 4. Standard machine-level extensions (starting with 'Zxm') must be listed |
| 320 | * after any lower-privileged, standard extensions. If multiple |
| 321 | * machine-level extensions are listed, they must be ordered |
| 322 | * alphabetically. |
| 323 | * |
| 324 | * 5. Non-standard extensions (starting with 'X') must be listed after all |
| 325 | * standard extensions. If multiple non-standard extensions are listed, they |
| 326 | * must be ordered alphabetically. |
| 327 | * |
| 328 | * An example string following the order is: |
| 329 | * rv64imadc_zifoo_zigoo_zafoo_sbar_scar_zxmbaz_xqux_xrux |
| 330 | * |
| 331 | * New entries to this struct should follow the ordering rules described above. |
| 332 | */ |
| 333 | const struct riscv_isa_ext_data riscv_isa_ext[] = { |
| 334 | __RISCV_ISA_EXT_DATA(i, RISCV_ISA_EXT_i), |
| 335 | __RISCV_ISA_EXT_DATA(m, RISCV_ISA_EXT_m), |
| 336 | __RISCV_ISA_EXT_DATA(a, RISCV_ISA_EXT_a), |
| 337 | __RISCV_ISA_EXT_DATA(f, RISCV_ISA_EXT_f), |
| 338 | __RISCV_ISA_EXT_DATA(d, RISCV_ISA_EXT_d), |
| 339 | __RISCV_ISA_EXT_DATA(q, RISCV_ISA_EXT_q), |
| 340 | __RISCV_ISA_EXT_SUPERSET(c, RISCV_ISA_EXT_c, riscv_c_exts), |
| 341 | __RISCV_ISA_EXT_SUPERSET(v, RISCV_ISA_EXT_v, riscv_v_exts), |
| 342 | __RISCV_ISA_EXT_DATA(h, RISCV_ISA_EXT_h), |
| 343 | __RISCV_ISA_EXT_SUPERSET_VALIDATE(zicbom, RISCV_ISA_EXT_ZICBOM, riscv_xlinuxenvcfg_exts, |
| 344 | riscv_ext_zicbom_validate), |
| 345 | __RISCV_ISA_EXT_SUPERSET_VALIDATE(zicboz, RISCV_ISA_EXT_ZICBOZ, riscv_xlinuxenvcfg_exts, |
| 346 | riscv_ext_zicboz_validate), |
| 347 | __RISCV_ISA_EXT_DATA(zicntr, RISCV_ISA_EXT_ZICNTR), |
| 348 | __RISCV_ISA_EXT_DATA(zicond, RISCV_ISA_EXT_ZICOND), |
| 349 | __RISCV_ISA_EXT_DATA(zicsr, RISCV_ISA_EXT_ZICSR), |
| 350 | __RISCV_ISA_EXT_DATA(zifencei, RISCV_ISA_EXT_ZIFENCEI), |
| 351 | __RISCV_ISA_EXT_DATA(zihintntl, RISCV_ISA_EXT_ZIHINTNTL), |
| 352 | __RISCV_ISA_EXT_DATA(zihintpause, RISCV_ISA_EXT_ZIHINTPAUSE), |
| 353 | __RISCV_ISA_EXT_DATA(zihpm, RISCV_ISA_EXT_ZIHPM), |
| 354 | __RISCV_ISA_EXT_DATA(zimop, RISCV_ISA_EXT_ZIMOP), |
| 355 | __RISCV_ISA_EXT_DATA(zacas, RISCV_ISA_EXT_ZACAS), |
| 356 | __RISCV_ISA_EXT_DATA(zawrs, RISCV_ISA_EXT_ZAWRS), |
| 357 | __RISCV_ISA_EXT_DATA(zfa, RISCV_ISA_EXT_ZFA), |
| 358 | __RISCV_ISA_EXT_DATA(zfh, RISCV_ISA_EXT_ZFH), |
| 359 | __RISCV_ISA_EXT_DATA(zfhmin, RISCV_ISA_EXT_ZFHMIN), |
| 360 | __RISCV_ISA_EXT_DATA(zca, RISCV_ISA_EXT_ZCA), |
| 361 | __RISCV_ISA_EXT_DATA_VALIDATE(zcb, RISCV_ISA_EXT_ZCB, riscv_ext_zca_depends), |
| 362 | __RISCV_ISA_EXT_DATA_VALIDATE(zcd, RISCV_ISA_EXT_ZCD, riscv_ext_zcd_validate), |
| 363 | __RISCV_ISA_EXT_DATA_VALIDATE(zcf, RISCV_ISA_EXT_ZCF, riscv_ext_zcf_validate), |
| 364 | __RISCV_ISA_EXT_DATA_VALIDATE(zcmop, RISCV_ISA_EXT_ZCMOP, riscv_ext_zca_depends), |
| 365 | __RISCV_ISA_EXT_DATA(zba, RISCV_ISA_EXT_ZBA), |
| 366 | __RISCV_ISA_EXT_DATA(zbb, RISCV_ISA_EXT_ZBB), |
| 367 | __RISCV_ISA_EXT_DATA(zbc, RISCV_ISA_EXT_ZBC), |
| 368 | __RISCV_ISA_EXT_DATA(zbkb, RISCV_ISA_EXT_ZBKB), |
| 369 | __RISCV_ISA_EXT_DATA(zbkc, RISCV_ISA_EXT_ZBKC), |
| 370 | __RISCV_ISA_EXT_DATA(zbkx, RISCV_ISA_EXT_ZBKX), |
| 371 | __RISCV_ISA_EXT_DATA(zbs, RISCV_ISA_EXT_ZBS), |
| 372 | __RISCV_ISA_EXT_BUNDLE(zk, riscv_zk_bundled_exts), |
| 373 | __RISCV_ISA_EXT_BUNDLE(zkn, riscv_zkn_bundled_exts), |
| 374 | __RISCV_ISA_EXT_DATA(zknd, RISCV_ISA_EXT_ZKND), |
| 375 | __RISCV_ISA_EXT_DATA(zkne, RISCV_ISA_EXT_ZKNE), |
| 376 | __RISCV_ISA_EXT_DATA(zknh, RISCV_ISA_EXT_ZKNH), |
| 377 | __RISCV_ISA_EXT_DATA(zkr, RISCV_ISA_EXT_ZKR), |
| 378 | __RISCV_ISA_EXT_BUNDLE(zks, riscv_zks_bundled_exts), |
| 379 | __RISCV_ISA_EXT_DATA(zkt, RISCV_ISA_EXT_ZKT), |
| 380 | __RISCV_ISA_EXT_DATA(zksed, RISCV_ISA_EXT_ZKSED), |
| 381 | __RISCV_ISA_EXT_DATA(zksh, RISCV_ISA_EXT_ZKSH), |
| 382 | __RISCV_ISA_EXT_DATA(ztso, RISCV_ISA_EXT_ZTSO), |
| 383 | __RISCV_ISA_EXT_SUPERSET(zvbb, RISCV_ISA_EXT_ZVBB, riscv_zvbb_exts), |
| 384 | __RISCV_ISA_EXT_DATA(zvbc, RISCV_ISA_EXT_ZVBC), |
| 385 | __RISCV_ISA_EXT_SUPERSET(zve32f, RISCV_ISA_EXT_ZVE32F, riscv_zve32f_exts), |
| 386 | __RISCV_ISA_EXT_DATA(zve32x, RISCV_ISA_EXT_ZVE32X), |
| 387 | __RISCV_ISA_EXT_SUPERSET(zve64d, RISCV_ISA_EXT_ZVE64D, riscv_zve64d_exts), |
| 388 | __RISCV_ISA_EXT_SUPERSET(zve64f, RISCV_ISA_EXT_ZVE64F, riscv_zve64f_exts), |
| 389 | __RISCV_ISA_EXT_SUPERSET(zve64x, RISCV_ISA_EXT_ZVE64X, riscv_zve64x_exts), |
| 390 | __RISCV_ISA_EXT_DATA(zvfh, RISCV_ISA_EXT_ZVFH), |
| 391 | __RISCV_ISA_EXT_DATA(zvfhmin, RISCV_ISA_EXT_ZVFHMIN), |
| 392 | __RISCV_ISA_EXT_DATA(zvkb, RISCV_ISA_EXT_ZVKB), |
| 393 | __RISCV_ISA_EXT_DATA(zvkg, RISCV_ISA_EXT_ZVKG), |
| 394 | __RISCV_ISA_EXT_BUNDLE(zvkn, riscv_zvkn_bundled_exts), |
| 395 | __RISCV_ISA_EXT_BUNDLE(zvknc, riscv_zvknc_bundled_exts), |
| 396 | __RISCV_ISA_EXT_DATA(zvkned, RISCV_ISA_EXT_ZVKNED), |
| 397 | __RISCV_ISA_EXT_BUNDLE(zvkng, riscv_zvkng_bundled_exts), |
| 398 | __RISCV_ISA_EXT_DATA(zvknha, RISCV_ISA_EXT_ZVKNHA), |
| 399 | __RISCV_ISA_EXT_DATA(zvknhb, RISCV_ISA_EXT_ZVKNHB), |
| 400 | __RISCV_ISA_EXT_BUNDLE(zvks, riscv_zvks_bundled_exts), |
| 401 | __RISCV_ISA_EXT_BUNDLE(zvksc, riscv_zvksc_bundled_exts), |
| 402 | __RISCV_ISA_EXT_DATA(zvksed, RISCV_ISA_EXT_ZVKSED), |
| 403 | __RISCV_ISA_EXT_DATA(zvksh, RISCV_ISA_EXT_ZVKSH), |
| 404 | __RISCV_ISA_EXT_BUNDLE(zvksg, riscv_zvksg_bundled_exts), |
| 405 | __RISCV_ISA_EXT_DATA(zvkt, RISCV_ISA_EXT_ZVKT), |
| 406 | __RISCV_ISA_EXT_DATA(smaia, RISCV_ISA_EXT_SMAIA), |
| 407 | __RISCV_ISA_EXT_DATA(smmpm, RISCV_ISA_EXT_SMMPM), |
| 408 | __RISCV_ISA_EXT_SUPERSET(smnpm, RISCV_ISA_EXT_SMNPM, riscv_xlinuxenvcfg_exts), |
| 409 | __RISCV_ISA_EXT_DATA(smstateen, RISCV_ISA_EXT_SMSTATEEN), |
| 410 | __RISCV_ISA_EXT_DATA(ssaia, RISCV_ISA_EXT_SSAIA), |
| 411 | __RISCV_ISA_EXT_DATA(sscofpmf, RISCV_ISA_EXT_SSCOFPMF), |
| 412 | __RISCV_ISA_EXT_SUPERSET(ssnpm, RISCV_ISA_EXT_SSNPM, riscv_xlinuxenvcfg_exts), |
| 413 | __RISCV_ISA_EXT_DATA(sstc, RISCV_ISA_EXT_SSTC), |
| 414 | __RISCV_ISA_EXT_DATA(svinval, RISCV_ISA_EXT_SVINVAL), |
| 415 | __RISCV_ISA_EXT_DATA(svnapot, RISCV_ISA_EXT_SVNAPOT), |
| 416 | __RISCV_ISA_EXT_DATA(svpbmt, RISCV_ISA_EXT_SVPBMT), |
| 417 | __RISCV_ISA_EXT_DATA(svvptc, RISCV_ISA_EXT_SVVPTC), |
| 418 | }; |
| 419 | |
| 420 | const size_t riscv_isa_ext_count = ARRAY_SIZE(riscv_isa_ext); |
| 421 | |
| 422 | static void riscv_isa_set_ext(const struct riscv_isa_ext_data *ext, unsigned long *bitmap) |
| 423 | { |
| 424 | if (ext->id != RISCV_ISA_EXT_INVALID) |
| 425 | __set_bit(ext->id, bitmap); |
| 426 | |
| 427 | for (int i = 0; i < ext->subset_ext_size; i++) { |
| 428 | if (ext->subset_ext_ids[i] != RISCV_ISA_EXT_INVALID) |
| 429 | __set_bit(ext->subset_ext_ids[i], bitmap); |
| 430 | } |
| 431 | } |
| 432 | |
| 433 | static void match_isa_ext(const char *name, const char *name_end) |
| 434 | { |
| 435 | for (int i = 0; i < riscv_isa_ext_count; i++) { |
| 436 | const struct riscv_isa_ext_data *ext = &riscv_isa_ext[i]; |
| 437 | |
| 438 | if ((name_end - name == strlen(ext->name)) && |
| 439 | !strncasecmp(name, ext->name, name_end - name)) { |
| 440 | if (ext->validate && !ext->validate(ext, riscv_isa)) |
| 441 | riscv_isa_set_ext(ext, riscv_isa); |
| 442 | break; |
| 443 | } |
| 444 | } |
| 445 | } |
| 446 | |
| 447 | static void riscv_parse_isa_string(const char *isa) |
| 448 | { |
| 449 | /* |
| 450 | * For all possible cpus, we have already validated in |
| 451 | * the boot process that they at least contain "rv" and |
| 452 | * whichever of "32"/"64" this kernel supports, and so this |
| 453 | * section can be skipped. |
| 454 | */ |
| 455 | isa += 4; |
| 456 | |
| 457 | while (*isa) { |
| 458 | const char *ext = isa++; |
| 459 | const char *ext_end = isa; |
| 460 | bool ext_err = false; |
| 461 | |
| 462 | switch (*ext) { |
| 463 | case 'x': |
| 464 | case 'X': |
| 465 | log_warning("Vendor extensions are ignored in riscv,isa. Use riscv,isa-extensions instead."); |
| 466 | /* |
| 467 | * To skip an extension, we find its end. |
| 468 | * As multi-letter extensions must be split from other multi-letter |
| 469 | * extensions with an "_", the end of a multi-letter extension will |
| 470 | * either be the null character or the "_" at the start of the next |
| 471 | * multi-letter extension. |
| 472 | */ |
| 473 | for (; *isa && *isa != '_'; ++isa) |
| 474 | ; |
| 475 | ext_err = true; |
| 476 | break; |
| 477 | case 's': |
| 478 | /* |
| 479 | * Workaround for invalid single-letter 's' & 'u' (QEMU). |
| 480 | * No need to set the bit in riscv_isa as 's' & 'u' are |
| 481 | * not valid ISA extensions. It works unless the first |
| 482 | * multi-letter extension in the ISA string begins with |
| 483 | * "Su" and is not prefixed with an underscore. |
| 484 | */ |
| 485 | if (ext[-1] != '_' && ext[1] == 'u') { |
| 486 | ++isa; |
| 487 | ext_err = true; |
| 488 | break; |
| 489 | } |
| 490 | fallthrough; |
| 491 | case 'S': |
| 492 | case 'z': |
| 493 | case 'Z': |
| 494 | /* |
| 495 | * Before attempting to parse the extension itself, we find its end. |
| 496 | * As multi-letter extensions must be split from other multi-letter |
| 497 | * extensions with an "_", the end of a multi-letter extension will |
| 498 | * either be the null character or the "_" at the start of the next |
| 499 | * multi-letter extension. |
| 500 | * |
| 501 | * Next, as the extensions version is currently ignored, we |
| 502 | * eliminate that portion. This is done by parsing backwards from |
| 503 | * the end of the extension, removing any numbers. This may be a |
| 504 | * major or minor number however, so the process is repeated if a |
| 505 | * minor number was found. |
| 506 | * |
| 507 | * ext_end is intended to represent the first character *after* the |
| 508 | * name portion of an extension, but will be decremented to the last |
| 509 | * character itself while eliminating the extensions version number. |
| 510 | * A simple re-increment solves this problem. |
| 511 | */ |
| 512 | for (; *isa && *isa != '_'; ++isa) |
| 513 | if (unlikely(!isalnum(*isa))) |
| 514 | ext_err = true; |
| 515 | |
| 516 | ext_end = isa; |
| 517 | if (unlikely(ext_err)) |
| 518 | break; |
| 519 | |
| 520 | if (!isdigit(ext_end[-1])) |
| 521 | break; |
| 522 | |
| 523 | while (isdigit(*--ext_end)) |
| 524 | ; |
| 525 | |
| 526 | if (tolower(ext_end[0]) != 'p' || !isdigit(ext_end[-1])) { |
| 527 | ++ext_end; |
| 528 | break; |
| 529 | } |
| 530 | |
| 531 | while (isdigit(*--ext_end)) |
| 532 | ; |
| 533 | |
| 534 | ++ext_end; |
| 535 | break; |
| 536 | default: |
| 537 | /* |
| 538 | * Things are a little easier for single-letter extensions, as they |
| 539 | * are parsed forwards. |
| 540 | * |
| 541 | * After checking that our starting position is valid, we need to |
| 542 | * ensure that, when isa was incremented at the start of the loop, |
| 543 | * that it arrived at the start of the next extension. |
| 544 | * |
| 545 | * If we are already on a non-digit, there is nothing to do. Either |
| 546 | * we have a multi-letter extension's _, or the start of an |
| 547 | * extension. |
| 548 | * |
| 549 | * Otherwise we have found the current extension's major version |
| 550 | * number. Parse past it, and a subsequent p/minor version number |
| 551 | * if present. The `p` extension must not appear immediately after |
| 552 | * a number, so there is no fear of missing it. |
| 553 | * |
| 554 | */ |
| 555 | if (unlikely(!isalpha(*ext))) { |
| 556 | ext_err = true; |
| 557 | break; |
| 558 | } |
| 559 | |
| 560 | if (!isdigit(*isa)) |
| 561 | break; |
| 562 | |
| 563 | while (isdigit(*++isa)) |
| 564 | ; |
| 565 | |
| 566 | if (tolower(*isa) != 'p') |
| 567 | break; |
| 568 | |
| 569 | if (!isdigit(*++isa)) { |
| 570 | --isa; |
| 571 | break; |
| 572 | } |
| 573 | |
| 574 | while (isdigit(*++isa)) |
| 575 | ; |
| 576 | |
| 577 | break; |
| 578 | } |
| 579 | |
| 580 | /* |
| 581 | * The parser expects that at the start of an iteration isa points to the |
| 582 | * first character of the next extension. As we stop parsing an extension |
| 583 | * on meeting a non-alphanumeric character, an extra increment is needed |
| 584 | * where the succeeding extension is a multi-letter prefixed with an "_". |
| 585 | */ |
| 586 | if (*isa == '_') |
| 587 | ++isa; |
| 588 | |
| 589 | if (unlikely(ext_err)) |
| 590 | continue; |
| 591 | match_isa_ext(ext, ext_end); |
| 592 | } |
| 593 | } |
| 594 | |
Bin Meng | 055700e | 2018-09-26 06:55:14 -0700 | [diff] [blame] | 595 | static inline bool supports_extension(char ext) |
| 596 | { |
Nikita Shubin | c9382b1 | 2022-12-14 08:58:43 +0300 | [diff] [blame] | 597 | #if CONFIG_IS_ENABLED(RISCV_MMODE) |
| 598 | return csr_read(CSR_MISA) & (1 << (ext - 'a')); |
| 599 | #elif CONFIG_CPU |
Mayuresh Chitale | b710853 | 2025-01-06 13:04:05 +0000 | [diff] [blame] | 600 | return __riscv_isa_extension_available(ext); |
Bin Meng | edfe9a9 | 2018-12-12 06:12:38 -0800 | [diff] [blame] | 601 | #else /* !CONFIG_CPU */ |
Bin Meng | edfe9a9 | 2018-12-12 06:12:38 -0800 | [diff] [blame] | 602 | #warning "There is no way to determine the available extensions in S-mode." |
| 603 | #warning "Please convert your board to use the RISC-V CPU driver." |
| 604 | return false; |
Bin Meng | edfe9a9 | 2018-12-12 06:12:38 -0800 | [diff] [blame] | 605 | #endif /* CONFIG_CPU */ |
Bin Meng | 055700e | 2018-09-26 06:55:14 -0700 | [diff] [blame] | 606 | } |
| 607 | |
Tom Rini | f4d52f6 | 2023-09-04 15:06:34 -0400 | [diff] [blame] | 608 | static int riscv_cpu_probe(void) |
Bin Meng | 7a3bbfb | 2018-12-12 06:12:34 -0800 | [diff] [blame] | 609 | { |
| 610 | #ifdef CONFIG_CPU |
| 611 | int ret; |
| 612 | |
| 613 | /* probe cpus so that RISC-V timer can be bound */ |
| 614 | ret = cpu_probe_all(); |
| 615 | if (ret) |
| 616 | return log_msg_ret("RISC-V cpus probe failed\n", ret); |
| 617 | #endif |
| 618 | |
| 619 | return 0; |
| 620 | } |
Tom Rini | f4d52f6 | 2023-09-04 15:06:34 -0400 | [diff] [blame] | 621 | EVENT_SPY_SIMPLE(EVT_DM_POST_INIT_R, riscv_cpu_probe); |
Bin Meng | 7a3bbfb | 2018-12-12 06:12:34 -0800 | [diff] [blame] | 622 | |
Sean Anderson | dd1cd70 | 2020-09-21 07:51:38 -0400 | [diff] [blame] | 623 | /* |
| 624 | * This is called on secondary harts just after the IPI is init'd. Currently |
| 625 | * there's nothing to do, since we just need to clear any existing IPIs, and |
| 626 | * that is handled by the sending of an ipi itself. |
| 627 | */ |
| 628 | #if CONFIG_IS_ENABLED(SMP) |
| 629 | static void dummy_pending_ipi_clear(ulong hart, ulong arg0, ulong arg1) |
| 630 | { |
| 631 | } |
| 632 | #endif |
| 633 | |
Simon Glass | b8357c1 | 2023-08-21 21:16:56 -0600 | [diff] [blame] | 634 | int riscv_cpu_setup(void) |
Bin Meng | 7a3bbfb | 2018-12-12 06:12:34 -0800 | [diff] [blame] | 635 | { |
Mayuresh Chitale | b710853 | 2025-01-06 13:04:05 +0000 | [diff] [blame] | 636 | int ret = -ENODEV, ext_count, i; |
| 637 | const char *isa, **exts; |
| 638 | struct udevice *dev; |
| 639 | |
| 640 | uclass_find_first_device(UCLASS_CPU, &dev); |
| 641 | if (!dev) { |
| 642 | debug("unable to find the RISC-V cpu device\n"); |
| 643 | return ret; |
| 644 | } |
| 645 | |
| 646 | ext_count = dev_read_string_list(dev, "riscv,isa-extensions", &exts); |
| 647 | if (ext_count > 0) { |
| 648 | for (i = 0; i < ext_count; i++) |
| 649 | match_isa_ext(exts[i], exts[i] + strlen(exts[i])); |
| 650 | } else { |
| 651 | isa = dev_read_string(dev, "riscv,isa"); |
| 652 | if (!isa) |
| 653 | return ret; |
| 654 | riscv_parse_isa_string(isa); |
| 655 | } |
Bin Meng | a7544ed | 2018-12-12 06:12:40 -0800 | [diff] [blame] | 656 | |
| 657 | /* Enable FPU */ |
| 658 | if (supports_extension('d') || supports_extension('f')) { |
| 659 | csr_set(MODE_PREFIX(status), MSTATUS_FS); |
Bin Meng | f942636 | 2019-07-10 23:43:13 -0700 | [diff] [blame] | 660 | csr_write(CSR_FCSR, 0); |
Bin Meng | a7544ed | 2018-12-12 06:12:40 -0800 | [diff] [blame] | 661 | } |
| 662 | |
| 663 | if (CONFIG_IS_ENABLED(RISCV_MMODE)) { |
| 664 | /* |
| 665 | * Enable perf counters for cycle, time, |
| 666 | * and instret counters only |
| 667 | */ |
Nikita Shubin | c9382b1 | 2022-12-14 08:58:43 +0300 | [diff] [blame] | 668 | if (supports_extension('u')) { |
Sean Anderson | 7f4b666 | 2020-06-24 06:41:19 -0400 | [diff] [blame] | 669 | #ifdef CONFIG_RISCV_PRIV_1_9 |
Nikita Shubin | c9382b1 | 2022-12-14 08:58:43 +0300 | [diff] [blame] | 670 | csr_write(CSR_MSCOUNTEREN, GENMASK(2, 0)); |
| 671 | csr_write(CSR_MUCOUNTEREN, GENMASK(2, 0)); |
Sean Anderson | 7f4b666 | 2020-06-24 06:41:19 -0400 | [diff] [blame] | 672 | #else |
Nikita Shubin | c9382b1 | 2022-12-14 08:58:43 +0300 | [diff] [blame] | 673 | csr_write(CSR_MCOUNTEREN, GENMASK(2, 0)); |
Sean Anderson | 7f4b666 | 2020-06-24 06:41:19 -0400 | [diff] [blame] | 674 | #endif |
Nikita Shubin | c9382b1 | 2022-12-14 08:58:43 +0300 | [diff] [blame] | 675 | } |
Bin Meng | a7544ed | 2018-12-12 06:12:40 -0800 | [diff] [blame] | 676 | |
| 677 | /* Disable paging */ |
| 678 | if (supports_extension('s')) |
Sean Anderson | 7f4b666 | 2020-06-24 06:41:19 -0400 | [diff] [blame] | 679 | #ifdef CONFIG_RISCV_PRIV_1_9 |
| 680 | csr_read_clear(CSR_MSTATUS, SR_VM); |
| 681 | #else |
Bin Meng | f942636 | 2019-07-10 23:43:13 -0700 | [diff] [blame] | 682 | csr_write(CSR_SATP, 0); |
Sean Anderson | 7f4b666 | 2020-06-24 06:41:19 -0400 | [diff] [blame] | 683 | #endif |
Bin Meng | a7544ed | 2018-12-12 06:12:40 -0800 | [diff] [blame] | 684 | } |
| 685 | |
Bin Meng | 257875d | 2020-07-19 23:17:07 -0700 | [diff] [blame] | 686 | #if CONFIG_IS_ENABLED(SMP) |
Sean Anderson | b1d0cb3 | 2020-06-24 06:41:18 -0400 | [diff] [blame] | 687 | ret = riscv_init_ipi(); |
| 688 | if (ret) |
| 689 | return ret; |
Sean Anderson | dd1cd70 | 2020-09-21 07:51:38 -0400 | [diff] [blame] | 690 | |
| 691 | /* |
| 692 | * Clear all pending IPIs on secondary harts. We don't do anything on |
| 693 | * the boot hart, since we never send an IPI to ourselves, and no |
| 694 | * interrupts are enabled |
| 695 | */ |
| 696 | ret = smp_call_function((ulong)dummy_pending_ipi_clear, 0, 0, 0); |
| 697 | if (ret) |
| 698 | return ret; |
Sean Anderson | b1d0cb3 | 2020-06-24 06:41:18 -0400 | [diff] [blame] | 699 | #endif |
| 700 | |
Bin Meng | a7544ed | 2018-12-12 06:12:40 -0800 | [diff] [blame] | 701 | return 0; |
Bin Meng | 7a3bbfb | 2018-12-12 06:12:34 -0800 | [diff] [blame] | 702 | } |
Simon Glass | b8357c1 | 2023-08-21 21:16:56 -0600 | [diff] [blame] | 703 | EVENT_SPY_SIMPLE(EVT_DM_POST_INIT_F, riscv_cpu_setup); |
Bin Meng | 7a3bbfb | 2018-12-12 06:12:34 -0800 | [diff] [blame] | 704 | |
| 705 | int arch_early_init_r(void) |
| 706 | { |
Heinrich Schuchardt | cc382ff | 2021-09-12 21:11:46 +0200 | [diff] [blame] | 707 | if (IS_ENABLED(CONFIG_SYSRESET_SBI)) |
| 708 | device_bind_driver(gd->dm_root, "sbi-sysreset", |
| 709 | "sbi-sysreset", NULL); |
| 710 | |
| 711 | return 0; |
Bin Meng | 7a3bbfb | 2018-12-12 06:12:34 -0800 | [diff] [blame] | 712 | } |
Green Wan | 2612080 | 2021-05-02 23:23:04 -0700 | [diff] [blame] | 713 | |
| 714 | /** |
| 715 | * harts_early_init() - A callback function called by start.S to configure |
| 716 | * feature settings of each hart. |
| 717 | * |
| 718 | * In a multi-core system, memory access shall be careful here, it shall |
| 719 | * take care of race conditions. |
| 720 | */ |
| 721 | __weak void harts_early_init(void) |
| 722 | { |
| 723 | } |
Simon Glass | 34ee3ed | 2023-12-15 20:14:09 -0700 | [diff] [blame] | 724 | |
| 725 | #if !CONFIG_IS_ENABLED(SYSRESET) |
| 726 | void reset_cpu(void) |
| 727 | { |
| 728 | printf("resetting ...\n"); |
| 729 | |
| 730 | printf("reset not supported yet\n"); |
| 731 | hang(); |
| 732 | } |
| 733 | #endif |
Yao Zi | 4a9b1b0 | 2025-01-23 09:11:33 +0000 | [diff] [blame] | 734 | |
| 735 | /* |
| 736 | * cleanup_before_linux() is called just before we call linux, which prepares |
| 737 | * the processor for linux. |
| 738 | * this weak implementation is used by default. we disable interrupts and flush |
| 739 | * the cache. |
| 740 | */ |
| 741 | __weak int cleanup_before_linux(void) |
| 742 | { |
| 743 | disable_interrupts(); |
| 744 | |
| 745 | cache_flush(); |
| 746 | |
| 747 | return 0; |
| 748 | } |