Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2013 Linaro Ltd. * Copyright (c) 2013 Hisilicon Limited. * Based on arch/arm/mach-vexpress/platsmp.c, Copyright (C) 2002 ARM Ltd. */ #include <linux/smp.h> #include <linux/io.h> #include <linux/of_address.h> #include <linux/delay.h> #include <asm/cacheflush.h> #include <asm/smp_plat.h> #include <asm/smp_scu.h> #include <asm/mach/map.h> #include "core.h" #define HIX5HD2_BOOT_ADDRESS 0xffff0000 static void __iomem *ctrl_base; void hi3xxx_set_cpu_jump(int cpu, void *jump_addr) { cpu = cpu_logical_map(cpu); if (!cpu || !ctrl_base) return; writel_relaxed(__pa_symbol(jump_addr), ctrl_base + ((cpu - 1) << 2)); } int hi3xxx_get_cpu_jump(int cpu) { cpu = cpu_logical_map(cpu); if (!cpu || !ctrl_base) return 0; return readl_relaxed(ctrl_base + ((cpu - 1) << 2)); } static void __init hisi_enable_scu_a9(void) { unsigned long base = 0; void __iomem *scu_base = NULL; if (scu_a9_has_base()) { base = scu_a9_get_base(); scu_base = ioremap(base, SZ_4K); if (!scu_base) { pr_err("ioremap(scu_base) failed\n"); return; } scu_enable(scu_base); iounmap(scu_base); } } static void __init hi3xxx_smp_prepare_cpus(unsigned int max_cpus) { struct device_node *np = NULL; u32 offset = 0; hisi_enable_scu_a9(); if (!ctrl_base) { np = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl"); if (!np) { pr_err("failed to find hisilicon,sysctrl node\n"); return; } ctrl_base = of_iomap(np, 0); if (!ctrl_base) { pr_err("failed to map address\n"); return; } if (of_property_read_u32(np, "smp-offset", &offset) < 0) { pr_err("failed to find smp-offset property\n"); return; } ctrl_base += offset; } } static int hi3xxx_boot_secondary(unsigned int cpu, struct task_struct *idle) { hi3xxx_set_cpu(cpu, true); hi3xxx_set_cpu_jump(cpu, secondary_startup); arch_send_wakeup_ipi_mask(cpumask_of(cpu)); return 0; } static const struct smp_operations hi3xxx_smp_ops __initconst = { .smp_prepare_cpus = hi3xxx_smp_prepare_cpus, .smp_boot_secondary = hi3xxx_boot_secondary, #ifdef CONFIG_HOTPLUG_CPU .cpu_die = hi3xxx_cpu_die, .cpu_kill = hi3xxx_cpu_kill, #endif }; static void __init hisi_common_smp_prepare_cpus(unsigned int max_cpus) { hisi_enable_scu_a9(); } static void hix5hd2_set_scu_boot_addr(phys_addr_t start_addr, phys_addr_t jump_addr) { void __iomem *virt; virt = ioremap(start_addr, PAGE_SIZE); writel_relaxed(0xe51ff004, virt); /* ldr pc, [pc, #-4] */ writel_relaxed(jump_addr, virt + 4); /* pc jump phy address */ iounmap(virt); } static int hix5hd2_boot_secondary(unsigned int cpu, struct task_struct *idle) { phys_addr_t jumpaddr; jumpaddr = __pa_symbol(secondary_startup); hix5hd2_set_scu_boot_addr(HIX5HD2_BOOT_ADDRESS, jumpaddr); hix5hd2_set_cpu(cpu, true); arch_send_wakeup_ipi_mask(cpumask_of(cpu)); return 0; } static const struct smp_operations hix5hd2_smp_ops __initconst = { .smp_prepare_cpus = hisi_common_smp_prepare_cpus, .smp_boot_secondary = hix5hd2_boot_secondary, #ifdef CONFIG_HOTPLUG_CPU .cpu_die = hix5hd2_cpu_die, #endif }; #define SC_SCTL_REMAP_CLR 0x00000100 #define HIP01_BOOT_ADDRESS 0x80000000 #define REG_SC_CTRL 0x000 static void hip01_set_boot_addr(phys_addr_t start_addr, phys_addr_t jump_addr) { void __iomem *virt; virt = phys_to_virt(start_addr); writel_relaxed(0xe51ff004, virt); writel_relaxed(jump_addr, virt + 4); } static int hip01_boot_secondary(unsigned int cpu, struct task_struct *idle) { phys_addr_t jumpaddr; unsigned int remap_reg_value = 0; struct device_node *node; jumpaddr = __pa_symbol(secondary_startup); hip01_set_boot_addr(HIP01_BOOT_ADDRESS, jumpaddr); node = of_find_compatible_node(NULL, NULL, "hisilicon,hip01-sysctrl"); if (WARN_ON(!node)) return -1; ctrl_base = of_iomap(node, 0); /* set the secondary core boot from DDR */ remap_reg_value = readl_relaxed(ctrl_base + REG_SC_CTRL); barrier(); remap_reg_value |= SC_SCTL_REMAP_CLR; barrier(); writel_relaxed(remap_reg_value, ctrl_base + REG_SC_CTRL); hip01_set_cpu(cpu, true); return 0; } static const struct smp_operations hip01_smp_ops __initconst = { .smp_prepare_cpus = hisi_common_smp_prepare_cpus, .smp_boot_secondary = hip01_boot_secondary, }; CPU_METHOD_OF_DECLARE(hi3xxx_smp, "hisilicon,hi3620-smp", &hi3xxx_smp_ops); CPU_METHOD_OF_DECLARE(hix5hd2_smp, "hisilicon,hix5hd2-smp", &hix5hd2_smp_ops); CPU_METHOD_OF_DECLARE(hip01_smp, "hisilicon,hip01-smp", &hip01_smp_ops); |