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