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