1 /* 2 * Copyright (c) 2013 Linaro Ltd. 3 * Copyright (c) 2013 Hisilicon Limited. 4 * Based on arch/arm/mach-vexpress/platsmp.c, Copyright (C) 2002 ARM Ltd. 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms and conditions of the GNU General Public License, 8 * version 2, as published by the Free Software Foundation. 9 */ 10 #include <linux/smp.h> 11 #include <linux/io.h> 12 #include <linux/of_address.h> 13 14 #include <asm/cacheflush.h> 15 #include <asm/smp_plat.h> 16 #include <asm/smp_scu.h> 17 18 #include "core.h" 19 20 #define HIX5HD2_BOOT_ADDRESS 0xffff0000 21 22 static void __iomem *ctrl_base; 23 24 void hi3xxx_set_cpu_jump(int cpu, void *jump_addr) 25 { 26 cpu = cpu_logical_map(cpu); 27 if (!cpu || !ctrl_base) 28 return; 29 writel_relaxed(virt_to_phys(jump_addr), ctrl_base + ((cpu - 1) << 2)); 30 } 31 32 int hi3xxx_get_cpu_jump(int cpu) 33 { 34 cpu = cpu_logical_map(cpu); 35 if (!cpu || !ctrl_base) 36 return 0; 37 return readl_relaxed(ctrl_base + ((cpu - 1) << 2)); 38 } 39 40 static void __init hisi_enable_scu_a9(void) 41 { 42 unsigned long base = 0; 43 void __iomem *scu_base = NULL; 44 45 if (scu_a9_has_base()) { 46 base = scu_a9_get_base(); 47 scu_base = ioremap(base, SZ_4K); 48 if (!scu_base) { 49 pr_err("ioremap(scu_base) failed\n"); 50 return; 51 } 52 scu_enable(scu_base); 53 iounmap(scu_base); 54 } 55 } 56 57 static void __init hi3xxx_smp_prepare_cpus(unsigned int max_cpus) 58 { 59 struct device_node *np = NULL; 60 u32 offset = 0; 61 62 hisi_enable_scu_a9(); 63 if (!ctrl_base) { 64 np = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl"); 65 if (!np) { 66 pr_err("failed to find hisilicon,sysctrl node\n"); 67 return; 68 } 69 ctrl_base = of_iomap(np, 0); 70 if (!ctrl_base) { 71 pr_err("failed to map address\n"); 72 return; 73 } 74 if (of_property_read_u32(np, "smp-offset", &offset) < 0) { 75 pr_err("failed to find smp-offset property\n"); 76 return; 77 } 78 ctrl_base += offset; 79 } 80 } 81 82 static int hi3xxx_boot_secondary(unsigned int cpu, struct task_struct *idle) 83 { 84 hi3xxx_set_cpu(cpu, true); 85 hi3xxx_set_cpu_jump(cpu, secondary_startup); 86 arch_send_wakeup_ipi_mask(cpumask_of(cpu)); 87 return 0; 88 } 89 90 struct smp_operations hi3xxx_smp_ops __initdata = { 91 .smp_prepare_cpus = hi3xxx_smp_prepare_cpus, 92 .smp_boot_secondary = hi3xxx_boot_secondary, 93 #ifdef CONFIG_HOTPLUG_CPU 94 .cpu_die = hi3xxx_cpu_die, 95 .cpu_kill = hi3xxx_cpu_kill, 96 #endif 97 }; 98 99 static void __init hix5hd2_smp_prepare_cpus(unsigned int max_cpus) 100 { 101 hisi_enable_scu_a9(); 102 } 103 104 void hix5hd2_set_scu_boot_addr(phys_addr_t start_addr, phys_addr_t jump_addr) 105 { 106 void __iomem *virt; 107 108 virt = ioremap(start_addr, PAGE_SIZE); 109 110 writel_relaxed(0xe51ff004, virt); /* ldr pc, [rc, #-4] */ 111 writel_relaxed(jump_addr, virt + 4); /* pc jump phy address */ 112 iounmap(virt); 113 } 114 115 static int hix5hd2_boot_secondary(unsigned int cpu, struct task_struct *idle) 116 { 117 phys_addr_t jumpaddr; 118 119 jumpaddr = virt_to_phys(hix5hd2_secondary_startup); 120 hix5hd2_set_scu_boot_addr(HIX5HD2_BOOT_ADDRESS, jumpaddr); 121 hix5hd2_set_cpu(cpu, true); 122 arch_send_wakeup_ipi_mask(cpumask_of(cpu)); 123 return 0; 124 } 125 126 127 struct smp_operations hix5hd2_smp_ops __initdata = { 128 .smp_prepare_cpus = hix5hd2_smp_prepare_cpus, 129 .smp_boot_secondary = hix5hd2_boot_secondary, 130 #ifdef CONFIG_HOTPLUG_CPU 131 .cpu_die = hix5hd2_cpu_die, 132 #endif 133 }; 134 135 CPU_METHOD_OF_DECLARE(hi3xxx_smp, "hisilicon,hi3620-smp", &hi3xxx_smp_ops); 136 CPU_METHOD_OF_DECLARE(hix5hd2_smp, "hisilicon,hix5hd2-smp", &hix5hd2_smp_ops); 137