1 /* 2 * Copyright (C) 2012 Regents of the University of California 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation, version 2. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 */ 13 14 #include <linux/init.h> 15 #include <linux/seq_file.h> 16 #include <linux/of.h> 17 #include <asm/smp.h> 18 19 /* 20 * Returns the hart ID of the given device tree node, or -ENODEV if the node 21 * isn't an enabled and valid RISC-V hart node. 22 */ 23 int riscv_of_processor_hartid(struct device_node *node) 24 { 25 const char *isa; 26 u32 hart; 27 28 if (!of_device_is_compatible(node, "riscv")) { 29 pr_warn("Found incompatible CPU\n"); 30 return -ENODEV; 31 } 32 33 if (of_property_read_u32(node, "reg", &hart)) { 34 pr_warn("Found CPU without hart ID\n"); 35 return -ENODEV; 36 } 37 38 if (!of_device_is_available(node)) { 39 pr_info("CPU with hartid=%d is not available\n", hart); 40 return -ENODEV; 41 } 42 43 if (of_property_read_string(node, "riscv,isa", &isa)) { 44 pr_warn("CPU with hartid=%d has no \"riscv,isa\" property\n", hart); 45 return -ENODEV; 46 } 47 if (isa[0] != 'r' || isa[1] != 'v') { 48 pr_warn("CPU with hartid=%d has an invalid ISA of \"%s\"\n", hart, isa); 49 return -ENODEV; 50 } 51 52 return hart; 53 } 54 55 #ifdef CONFIG_PROC_FS 56 57 static void print_isa(struct seq_file *f, const char *orig_isa) 58 { 59 static const char *ext = "mafdcsu"; 60 const char *isa = orig_isa; 61 const char *e; 62 63 /* 64 * Linux doesn't support rv32e or rv128i, and we only support booting 65 * kernels on harts with the same ISA that the kernel is compiled for. 66 */ 67 #if defined(CONFIG_32BIT) 68 if (strncmp(isa, "rv32i", 5) != 0) 69 return; 70 #elif defined(CONFIG_64BIT) 71 if (strncmp(isa, "rv64i", 5) != 0) 72 return; 73 #endif 74 75 /* Print the base ISA, as we already know it's legal. */ 76 seq_puts(f, "isa\t\t: "); 77 seq_write(f, isa, 5); 78 isa += 5; 79 80 /* 81 * Check the rest of the ISA string for valid extensions, printing those 82 * we find. RISC-V ISA strings define an order, so we only print the 83 * extension bits when they're in order. Hide the supervisor (S) 84 * extension from userspace as it's not accessible from there. 85 */ 86 for (e = ext; *e != '\0'; ++e) { 87 if (isa[0] == e[0]) { 88 if (isa[0] != 's') 89 seq_write(f, isa, 1); 90 91 isa++; 92 } 93 } 94 seq_puts(f, "\n"); 95 96 /* 97 * If we were given an unsupported ISA in the device tree then print 98 * a bit of info describing what went wrong. 99 */ 100 if (isa[0] != '\0') 101 pr_info("unsupported ISA \"%s\" in device tree\n", orig_isa); 102 } 103 104 static void print_mmu(struct seq_file *f, const char *mmu_type) 105 { 106 #if defined(CONFIG_32BIT) 107 if (strcmp(mmu_type, "riscv,sv32") != 0) 108 return; 109 #elif defined(CONFIG_64BIT) 110 if (strcmp(mmu_type, "riscv,sv39") != 0 && 111 strcmp(mmu_type, "riscv,sv48") != 0) 112 return; 113 #endif 114 115 seq_printf(f, "mmu\t\t: %s\n", mmu_type+6); 116 } 117 118 static void *c_start(struct seq_file *m, loff_t *pos) 119 { 120 *pos = cpumask_next(*pos - 1, cpu_online_mask); 121 if ((*pos) < nr_cpu_ids) 122 return (void *)(uintptr_t)(1 + *pos); 123 return NULL; 124 } 125 126 static void *c_next(struct seq_file *m, void *v, loff_t *pos) 127 { 128 (*pos)++; 129 return c_start(m, pos); 130 } 131 132 static void c_stop(struct seq_file *m, void *v) 133 { 134 } 135 136 static int c_show(struct seq_file *m, void *v) 137 { 138 unsigned long cpu_id = (unsigned long)v - 1; 139 struct device_node *node = of_get_cpu_node(cpuid_to_hartid_map(cpu_id), 140 NULL); 141 const char *compat, *isa, *mmu; 142 143 seq_printf(m, "processor\t: %lu\n", cpu_id); 144 seq_printf(m, "hart\t\t: %lu\n", cpuid_to_hartid_map(cpu_id)); 145 if (!of_property_read_string(node, "riscv,isa", &isa)) 146 print_isa(m, isa); 147 if (!of_property_read_string(node, "mmu-type", &mmu)) 148 print_mmu(m, mmu); 149 if (!of_property_read_string(node, "compatible", &compat) 150 && strcmp(compat, "riscv")) 151 seq_printf(m, "uarch\t\t: %s\n", compat); 152 seq_puts(m, "\n"); 153 of_node_put(node); 154 155 return 0; 156 } 157 158 const struct seq_operations cpuinfo_op = { 159 .start = c_start, 160 .next = c_next, 161 .stop = c_stop, 162 .show = c_show 163 }; 164 165 #endif /* CONFIG_PROC_FS */ 166