1/* 2 * plat smp support for CSR Marco dual-core SMP SoCs 3 * 4 * Copyright (c) 2012 Cambridge Silicon Radio Limited, a CSR plc group company. 5 * 6 * Licensed under GPLv2 or later. 7 */ 8 9#include <linux/init.h> 10#include <linux/smp.h> 11#include <linux/delay.h> 12#include <linux/of.h> 13#include <linux/of_address.h> 14#include <asm/page.h> 15#include <asm/mach/map.h> 16#include <asm/smp_plat.h> 17#include <asm/smp_scu.h> 18#include <asm/cacheflush.h> 19#include <asm/cputype.h> 20 21#include "common.h" 22 23static void __iomem *clk_base; 24 25static DEFINE_SPINLOCK(boot_lock); 26 27static void sirfsoc_secondary_init(unsigned int cpu) 28{ 29 /* 30 * let the primary processor know we're out of the 31 * pen, then head off into the C entry point 32 */ 33 pen_release = -1; 34 smp_wmb(); 35 36 /* 37 * Synchronise with the boot thread. 38 */ 39 spin_lock(&boot_lock); 40 spin_unlock(&boot_lock); 41} 42 43static const struct of_device_id clk_ids[] = { 44 { .compatible = "sirf,atlas7-clkc" }, 45 {}, 46}; 47 48static int sirfsoc_boot_secondary(unsigned int cpu, struct task_struct *idle) 49{ 50 unsigned long timeout; 51 struct device_node *np; 52 53 np = of_find_matching_node(NULL, clk_ids); 54 if (!np) 55 return -ENODEV; 56 57 clk_base = of_iomap(np, 0); 58 if (!clk_base) 59 return -ENOMEM; 60 61 /* 62 * write the address of secondary startup into the clkc register 63 * at offset 0x2bC, then write the magic number 0x3CAF5D62 to the 64 * clkc register at offset 0x2b8, which is what boot rom code is 65 * waiting for. This would wake up the secondary core from WFE 66 */ 67#define SIRFSOC_CPU1_JUMPADDR_OFFSET 0x2bc 68 __raw_writel(virt_to_phys(sirfsoc_secondary_startup), 69 clk_base + SIRFSOC_CPU1_JUMPADDR_OFFSET); 70 71#define SIRFSOC_CPU1_WAKEMAGIC_OFFSET 0x2b8 72 __raw_writel(0x3CAF5D62, 73 clk_base + SIRFSOC_CPU1_WAKEMAGIC_OFFSET); 74 75 /* make sure write buffer is drained */ 76 mb(); 77 78 spin_lock(&boot_lock); 79 80 /* 81 * The secondary processor is waiting to be released from 82 * the holding pen - release it, then wait for it to flag 83 * that it has been released by resetting pen_release. 84 * 85 * Note that "pen_release" is the hardware CPU ID, whereas 86 * "cpu" is Linux's internal ID. 87 */ 88 pen_release = cpu_logical_map(cpu); 89 sync_cache_w(&pen_release); 90 91 /* 92 * Send the secondary CPU SEV, thereby causing the boot monitor to read 93 * the JUMPADDR and WAKEMAGIC, and branch to the address found there. 94 */ 95 dsb_sev(); 96 97 timeout = jiffies + (1 * HZ); 98 while (time_before(jiffies, timeout)) { 99 smp_rmb(); 100 if (pen_release == -1) 101 break; 102 103 udelay(10); 104 } 105 106 /* 107 * now the secondary core is starting up let it run its 108 * calibrations, then wait for it to finish 109 */ 110 spin_unlock(&boot_lock); 111 112 return pen_release != -1 ? -ENOSYS : 0; 113} 114 115struct smp_operations sirfsoc_smp_ops __initdata = { 116 .smp_secondary_init = sirfsoc_secondary_init, 117 .smp_boot_secondary = sirfsoc_boot_secondary, 118#ifdef CONFIG_HOTPLUG_CPU 119 .cpu_die = sirfsoc_cpu_die, 120#endif 121}; 122