Merge branch 'linus' into x86/urgent, to pick up dependent changes
[linux-2.6-block.git] / arch / arm / mach-prima2 / platsmp.c
CommitLineData
4898de3d
BS
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>
4898de3d
BS
20
21#include "common.h"
22
a7ae982f 23static void __iomem *clk_base;
4898de3d
BS
24
25static DEFINE_SPINLOCK(boot_lock);
26
6213f70e
RK
27/* XXX prima2_pen_release is cargo culted code - DO NOT COPY XXX */
28volatile int prima2_pen_release = -1;
29
8bd26e3a 30static void sirfsoc_secondary_init(unsigned int cpu)
4898de3d 31{
4898de3d
BS
32 /*
33 * let the primary processor know we're out of the
34 * pen, then head off into the C entry point
35 */
6213f70e 36 prima2_pen_release = -1;
4898de3d
BS
37 smp_wmb();
38
39 /*
40 * Synchronise with the boot thread.
41 */
42 spin_lock(&boot_lock);
43 spin_unlock(&boot_lock);
44}
45
444d2d33 46static const struct of_device_id clk_ids[] = {
a7ae982f 47 { .compatible = "sirf,atlas7-clkc" },
4898de3d
BS
48 {},
49};
50
8bd26e3a 51static int sirfsoc_boot_secondary(unsigned int cpu, struct task_struct *idle)
4898de3d
BS
52{
53 unsigned long timeout;
54 struct device_node *np;
55
a7ae982f 56 np = of_find_matching_node(NULL, clk_ids);
4898de3d
BS
57 if (!np)
58 return -ENODEV;
59
a7ae982f
ZS
60 clk_base = of_iomap(np, 0);
61 if (!clk_base)
4898de3d
BS
62 return -ENOMEM;
63
64 /*
a7ae982f
ZS
65 * write the address of secondary startup into the clkc register
66 * at offset 0x2bC, then write the magic number 0x3CAF5D62 to the
67 * clkc register at offset 0x2b8, which is what boot rom code is
4898de3d
BS
68 * waiting for. This would wake up the secondary core from WFE
69 */
a7ae982f 70#define SIRFSOC_CPU1_JUMPADDR_OFFSET 0x2bc
64fc2a94 71 __raw_writel(__pa_symbol(sirfsoc_secondary_startup),
a7ae982f 72 clk_base + SIRFSOC_CPU1_JUMPADDR_OFFSET);
4898de3d 73
a7ae982f 74#define SIRFSOC_CPU1_WAKEMAGIC_OFFSET 0x2b8
4898de3d 75 __raw_writel(0x3CAF5D62,
a7ae982f 76 clk_base + SIRFSOC_CPU1_WAKEMAGIC_OFFSET);
4898de3d
BS
77
78 /* make sure write buffer is drained */
79 mb();
80
81 spin_lock(&boot_lock);
82
83 /*
84 * The secondary processor is waiting to be released from
85 * the holding pen - release it, then wait for it to flag
6213f70e 86 * that it has been released by resetting prima2_pen_release.
4898de3d 87 *
6213f70e 88 * Note that "prima2_pen_release" is the hardware CPU ID, whereas
4898de3d
BS
89 * "cpu" is Linux's internal ID.
90 */
6213f70e
RK
91 prima2_pen_release = cpu_logical_map(cpu);
92 sync_cache_w(&prima2_pen_release);
4898de3d
BS
93
94 /*
95 * Send the secondary CPU SEV, thereby causing the boot monitor to read
96 * the JUMPADDR and WAKEMAGIC, and branch to the address found there.
97 */
98 dsb_sev();
99
100 timeout = jiffies + (1 * HZ);
101 while (time_before(jiffies, timeout)) {
102 smp_rmb();
6213f70e 103 if (prima2_pen_release == -1)
4898de3d
BS
104 break;
105
106 udelay(10);
107 }
108
109 /*
110 * now the secondary core is starting up let it run its
111 * calibrations, then wait for it to finish
112 */
113 spin_unlock(&boot_lock);
114
6213f70e 115 return prima2_pen_release != -1 ? -ENOSYS : 0;
4898de3d
BS
116}
117
75305275 118const struct smp_operations sirfsoc_smp_ops __initconst = {
661bfe23
BS
119 .smp_secondary_init = sirfsoc_secondary_init,
120 .smp_boot_secondary = sirfsoc_boot_secondary,
4898de3d
BS
121#ifdef CONFIG_HOTPLUG_CPU
122 .cpu_die = sirfsoc_cpu_die,
123#endif
124};