1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2016 Imagination Technologies 4 * Author: Paul Burton <paul.burton@mips.com> 5 */ 6 7 #include <linux/clk.h> 8 #include <linux/clk-provider.h> 9 #include <linux/clocksource.h> 10 #include <linux/init.h> 11 #include <linux/irqchip.h> 12 #include <linux/of_fdt.h> 13 14 #include <asm/bootinfo.h> 15 #include <asm/fw/fw.h> 16 #include <asm/irq_cpu.h> 17 #include <asm/machine.h> 18 #include <asm/mips-cps.h> 19 #include <asm/prom.h> 20 #include <asm/smp-ops.h> 21 #include <asm/time.h> 22 23 static __initconst const void *fdt; 24 static __initconst const struct mips_machine *mach; 25 static __initconst const void *mach_match_data; 26 27 void __init prom_init(void) 28 { 29 plat_get_fdt(); 30 BUG_ON(!fdt); 31 } 32 33 void __init *plat_get_fdt(void) 34 { 35 const struct mips_machine *check_mach; 36 const struct of_device_id *match; 37 38 if (fdt) 39 /* Already set up */ 40 return (void *)fdt; 41 42 if ((fw_arg0 == -2) && !fdt_check_header((void *)fw_passed_dtb)) { 43 /* 44 * We booted using the UHI boot protocol, so we have been 45 * provided with the appropriate device tree for the board. 46 * Make use of it & search for any machine struct based upon 47 * the root compatible string. 48 */ 49 fdt = (void *)fw_passed_dtb; 50 51 for_each_mips_machine(check_mach) { 52 match = mips_machine_is_compatible(check_mach, fdt); 53 if (match) { 54 mach = check_mach; 55 mach_match_data = match->data; 56 break; 57 } 58 } 59 } else if (IS_ENABLED(CONFIG_LEGACY_BOARDS)) { 60 /* 61 * We weren't booted using the UHI boot protocol, but do 62 * support some number of boards with legacy boot protocols. 63 * Attempt to find the right one. 64 */ 65 for_each_mips_machine(check_mach) { 66 if (!check_mach->detect) 67 continue; 68 69 if (!check_mach->detect()) 70 continue; 71 72 mach = check_mach; 73 } 74 75 /* 76 * If we don't recognise the machine then we can't continue, so 77 * die here. 78 */ 79 BUG_ON(!mach); 80 81 /* Retrieve the machine's FDT */ 82 fdt = mach->fdt; 83 } 84 return (void *)fdt; 85 } 86 87 #ifdef CONFIG_RELOCATABLE 88 89 void __init plat_fdt_relocated(void *new_location) 90 { 91 /* 92 * reset fdt as the cached value would point to the location 93 * before relocations happened and update the location argument 94 * if it was passed using UHI 95 */ 96 fdt = NULL; 97 98 if (fw_arg0 == -2) 99 fw_arg1 = (unsigned long)new_location; 100 } 101 102 #endif /* CONFIG_RELOCATABLE */ 103 104 void __init plat_mem_setup(void) 105 { 106 if (mach && mach->fixup_fdt) 107 fdt = mach->fixup_fdt(fdt, mach_match_data); 108 109 strlcpy(arcs_cmdline, boot_command_line, COMMAND_LINE_SIZE); 110 __dt_setup_arch((void *)fdt); 111 } 112 113 void __init device_tree_init(void) 114 { 115 int err; 116 117 unflatten_and_copy_device_tree(); 118 mips_cpc_probe(); 119 120 err = register_cps_smp_ops(); 121 if (err) 122 err = register_up_smp_ops(); 123 } 124 125 int __init apply_mips_fdt_fixups(void *fdt_out, size_t fdt_out_size, 126 const void *fdt_in, 127 const struct mips_fdt_fixup *fixups) 128 { 129 int err; 130 131 err = fdt_open_into(fdt_in, fdt_out, fdt_out_size); 132 if (err) { 133 pr_err("Failed to open FDT\n"); 134 return err; 135 } 136 137 for (; fixups->apply; fixups++) { 138 err = fixups->apply(fdt_out); 139 if (err) { 140 pr_err("Failed to apply FDT fixup \"%s\"\n", 141 fixups->description); 142 return err; 143 } 144 } 145 146 err = fdt_pack(fdt_out); 147 if (err) 148 pr_err("Failed to pack FDT\n"); 149 return err; 150 } 151 152 void __init plat_time_init(void) 153 { 154 struct device_node *np; 155 struct clk *clk; 156 157 of_clk_init(NULL); 158 159 if (!cpu_has_counter) { 160 mips_hpt_frequency = 0; 161 } else if (mach && mach->measure_hpt_freq) { 162 mips_hpt_frequency = mach->measure_hpt_freq(); 163 } else { 164 np = of_get_cpu_node(0, NULL); 165 if (!np) { 166 pr_err("Failed to get CPU node\n"); 167 return; 168 } 169 170 clk = of_clk_get(np, 0); 171 if (IS_ERR(clk)) { 172 pr_err("Failed to get CPU clock: %ld\n", PTR_ERR(clk)); 173 return; 174 } 175 176 mips_hpt_frequency = clk_get_rate(clk); 177 clk_put(clk); 178 179 switch (boot_cpu_type()) { 180 case CPU_20KC: 181 case CPU_25KF: 182 /* The counter runs at the CPU clock rate */ 183 break; 184 default: 185 /* The counter runs at half the CPU clock rate */ 186 mips_hpt_frequency /= 2; 187 break; 188 } 189 } 190 191 timer_probe(); 192 } 193 194 void __init arch_init_irq(void) 195 { 196 struct device_node *intc_node; 197 198 intc_node = of_find_compatible_node(NULL, NULL, 199 "mti,cpu-interrupt-controller"); 200 if (!cpu_has_veic && !intc_node) 201 mips_cpu_irq_init(); 202 of_node_put(intc_node); 203 204 irqchip_init(); 205 } 206 207 void __init prom_free_prom_memory(void) 208 { 209 } 210