1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2012 Regents of the University of California 4 */ 5 6 #include <linux/init.h> 7 #include <linux/seq_file.h> 8 #include <linux/of.h> 9 #include <asm/smp.h> 10 #include <asm/pgtable.h> 11 12 /* 13 * Returns the hart ID of the given device tree node, or -ENODEV if the node 14 * isn't an enabled and valid RISC-V hart node. 15 */ 16 int riscv_of_processor_hartid(struct device_node *node) 17 { 18 const char *isa; 19 u32 hart; 20 21 if (!of_device_is_compatible(node, "riscv")) { 22 pr_warn("Found incompatible CPU\n"); 23 return -ENODEV; 24 } 25 26 hart = of_get_cpu_hwid(node, 0); 27 if (hart == ~0U) { 28 pr_warn("Found CPU without hart ID\n"); 29 return -ENODEV; 30 } 31 32 if (!of_device_is_available(node)) { 33 pr_info("CPU with hartid=%d is not available\n", hart); 34 return -ENODEV; 35 } 36 37 if (of_property_read_string(node, "riscv,isa", &isa)) { 38 pr_warn("CPU with hartid=%d has no \"riscv,isa\" property\n", hart); 39 return -ENODEV; 40 } 41 if (isa[0] != 'r' || isa[1] != 'v') { 42 pr_warn("CPU with hartid=%d has an invalid ISA of \"%s\"\n", hart, isa); 43 return -ENODEV; 44 } 45 46 return hart; 47 } 48 49 /* 50 * Find hart ID of the CPU DT node under which given DT node falls. 51 * 52 * To achieve this, we walk up the DT tree until we find an active 53 * RISC-V core (HART) node and extract the cpuid from it. 54 */ 55 int riscv_of_parent_hartid(struct device_node *node) 56 { 57 for (; node; node = node->parent) { 58 if (of_device_is_compatible(node, "riscv")) 59 return riscv_of_processor_hartid(node); 60 } 61 62 return -1; 63 } 64 65 #ifdef CONFIG_PROC_FS 66 67 static void print_isa(struct seq_file *f, const char *isa) 68 { 69 /* Print the entire ISA as it is */ 70 seq_puts(f, "isa\t\t: "); 71 seq_write(f, isa, strlen(isa)); 72 seq_puts(f, "\n"); 73 } 74 75 static void print_mmu(struct seq_file *f) 76 { 77 char sv_type[16]; 78 79 #if defined(CONFIG_32BIT) 80 strncpy(sv_type, "sv32", 5); 81 #elif defined(CONFIG_64BIT) 82 if (pgtable_l4_enabled) 83 strncpy(sv_type, "sv48", 5); 84 else 85 strncpy(sv_type, "sv39", 5); 86 #endif 87 seq_printf(f, "mmu\t\t: %s\n", sv_type); 88 } 89 90 static void *c_start(struct seq_file *m, loff_t *pos) 91 { 92 *pos = cpumask_next(*pos - 1, cpu_online_mask); 93 if ((*pos) < nr_cpu_ids) 94 return (void *)(uintptr_t)(1 + *pos); 95 return NULL; 96 } 97 98 static void *c_next(struct seq_file *m, void *v, loff_t *pos) 99 { 100 (*pos)++; 101 return c_start(m, pos); 102 } 103 104 static void c_stop(struct seq_file *m, void *v) 105 { 106 } 107 108 static int c_show(struct seq_file *m, void *v) 109 { 110 unsigned long cpu_id = (unsigned long)v - 1; 111 struct device_node *node = of_get_cpu_node(cpu_id, NULL); 112 const char *compat, *isa; 113 114 seq_printf(m, "processor\t: %lu\n", cpu_id); 115 seq_printf(m, "hart\t\t: %lu\n", cpuid_to_hartid_map(cpu_id)); 116 if (!of_property_read_string(node, "riscv,isa", &isa)) 117 print_isa(m, isa); 118 print_mmu(m); 119 if (!of_property_read_string(node, "compatible", &compat) 120 && strcmp(compat, "riscv")) 121 seq_printf(m, "uarch\t\t: %s\n", compat); 122 seq_puts(m, "\n"); 123 of_node_put(node); 124 125 return 0; 126 } 127 128 const struct seq_operations cpuinfo_op = { 129 .start = c_start, 130 .next = c_next, 131 .stop = c_stop, 132 .show = c_show 133 }; 134 135 #endif /* CONFIG_PROC_FS */ 136