1 /* 2 * Copyright (C) 2014 Marvell Technology Group Ltd. 3 * 4 * Antoine Ténart <antoine.tenart@free-electrons.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 */ 10 11 #include <linux/io.h> 12 #include <linux/delay.h> 13 #include <linux/of.h> 14 #include <linux/of_address.h> 15 16 #include <asm/cacheflush.h> 17 #include <asm/smp_plat.h> 18 #include <asm/smp_scu.h> 19 20 #define CPU_RESET 0x00 21 22 #define RESET_VECT 0x00 23 #define SW_RESET_ADDR 0x94 24 25 extern void berlin_secondary_startup(void); 26 extern u32 boot_inst; 27 28 static void __iomem *cpu_ctrl; 29 30 static inline void berlin_perform_reset_cpu(unsigned int cpu) 31 { 32 u32 val; 33 34 val = readl(cpu_ctrl + CPU_RESET); 35 val |= BIT(cpu_logical_map(cpu)); 36 writel(val, cpu_ctrl + CPU_RESET); 37 } 38 39 static int berlin_boot_secondary(unsigned int cpu, struct task_struct *idle) 40 { 41 if (!cpu_ctrl) 42 return -EFAULT; 43 44 /* 45 * Reset the CPU, making it to execute the instruction in the reset 46 * exception vector. 47 */ 48 berlin_perform_reset_cpu(cpu); 49 50 return 0; 51 } 52 53 static void __init berlin_smp_prepare_cpus(unsigned int max_cpus) 54 { 55 struct device_node *np; 56 void __iomem *scu_base; 57 void __iomem *vectors_base; 58 59 np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu"); 60 scu_base = of_iomap(np, 0); 61 of_node_put(np); 62 if (!scu_base) 63 return; 64 65 np = of_find_compatible_node(NULL, NULL, "marvell,berlin-cpu-ctrl"); 66 cpu_ctrl = of_iomap(np, 0); 67 of_node_put(np); 68 if (!cpu_ctrl) 69 goto unmap_scu; 70 71 vectors_base = ioremap(CONFIG_VECTORS_BASE, SZ_32K); 72 if (!vectors_base) 73 goto unmap_scu; 74 75 scu_enable(scu_base); 76 flush_cache_all(); 77 78 /* 79 * Write the first instruction the CPU will execute after being reset 80 * in the reset exception vector. 81 */ 82 writel(boot_inst, vectors_base + RESET_VECT); 83 84 /* 85 * Write the secondary startup address into the SW reset address 86 * vector. This is used by boot_inst. 87 */ 88 writel(virt_to_phys(berlin_secondary_startup), vectors_base + SW_RESET_ADDR); 89 90 iounmap(vectors_base); 91 unmap_scu: 92 iounmap(scu_base); 93 } 94 95 static struct smp_operations berlin_smp_ops __initdata = { 96 .smp_prepare_cpus = berlin_smp_prepare_cpus, 97 .smp_boot_secondary = berlin_boot_secondary, 98 }; 99 CPU_METHOD_OF_DECLARE(berlin_smp, "marvell,berlin-smp", &berlin_smp_ops); 100