summaryrefslogtreecommitdiff
path: root/arch/arm/mach-prima2/platsmp.c
diff options
context:
space:
mode:
authorOlof Johansson <olof@lixom.net>2013-01-27 23:03:42 -0800
committerOlof Johansson <olof@lixom.net>2013-01-27 23:03:42 -0800
commitaf70fdc947dbe835acc26c6ee9e8e930f38935f8 (patch)
tree9924e9f2732e3cf3c7a882cb11377247729891ab /arch/arm/mach-prima2/platsmp.c
parent66eae035dc59755c51ad3b3c718a5ed7535322e3 (diff)
parent7f46a10724e0d31c83029436e54298be2cc558fa (diff)
downloadlinux-rt-af70fdc947dbe835acc26c6ee9e8e930f38935f8.tar.gz
Merge branch 'marco-timer-cleanup-rebase' of git://gitorious.org/sirfprima2-kernel/sirfprima2-kernel into next/soc
From Barry Song, this adds support for a new SoC from CSR; marco. It's SMP, uses GIC instead of VIC and in general needs a bit of rework of the platform code for setup, which this branch contains. * 'marco-timer-cleanup-rebase' of git://gitorious.org/sirfprima2-kernel/sirfprima2-kernel: ARM: PRIMA2: provide two DEBUG_LL ports for prima2 and marco ARM: PRIMA2: add new SiRFmarco SMP SoC infrastructures ARM: PRIMA2: irq: make prima2 irq can work even we enable GIC for Marco ARM: PRIMA2: rtciobg: it is also compatible with marco ARM: PRIMA2: rstc: enable the support for Marco ARM: PRIMA2: mv timer to timer-prima2 as we will add timer-marco ARM: PRIMA2: initialize l2x0 according to mach from DT ARM: PRIMA2: enable AUTO_ZRELADDR for SIRF in Kconfig ARM: PRIMA2: add CSR SiRFmarco device tree .dts Signed-off-by: Olof Johansson <olof@lixom.net>
Diffstat (limited to 'arch/arm/mach-prima2/platsmp.c')
-rw-r--r--arch/arm/mach-prima2/platsmp.c163
1 files changed, 163 insertions, 0 deletions
diff --git a/arch/arm/mach-prima2/platsmp.c b/arch/arm/mach-prima2/platsmp.c
new file mode 100644
index 000000000000..2395022bc733
--- /dev/null
+++ b/arch/arm/mach-prima2/platsmp.c
@@ -0,0 +1,163 @@
+/*
+ * plat smp support for CSR Marco dual-core SMP SoCs
+ *
+ * Copyright (c) 2012 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <asm/page.h>
+#include <asm/mach/map.h>
+#include <asm/smp_plat.h>
+#include <asm/smp_scu.h>
+#include <asm/cacheflush.h>
+#include <asm/cputype.h>
+#include <asm/hardware/gic.h>
+#include <mach/map.h>
+
+#include "common.h"
+
+static void __iomem *scu_base;
+static void __iomem *rsc_base;
+
+static DEFINE_SPINLOCK(boot_lock);
+
+static struct map_desc scu_io_desc __initdata = {
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+};
+
+void __init sirfsoc_map_scu(void)
+{
+ unsigned long base;
+
+ /* Get SCU base */
+ asm("mrc p15, 4, %0, c15, c0, 0" : "=r" (base));
+
+ scu_io_desc.virtual = SIRFSOC_VA(base);
+ scu_io_desc.pfn = __phys_to_pfn(base);
+ iotable_init(&scu_io_desc, 1);
+
+ scu_base = (void __iomem *)SIRFSOC_VA(base);
+}
+
+static void __cpuinit sirfsoc_secondary_init(unsigned int cpu)
+{
+ /*
+ * if any interrupts are already enabled for the primary
+ * core (e.g. timer irq), then they will not have been enabled
+ * for us: do so
+ */
+ gic_secondary_init(0);
+
+ /*
+ * let the primary processor know we're out of the
+ * pen, then head off into the C entry point
+ */
+ pen_release = -1;
+ smp_wmb();
+
+ /*
+ * Synchronise with the boot thread.
+ */
+ spin_lock(&boot_lock);
+ spin_unlock(&boot_lock);
+}
+
+static struct of_device_id rsc_ids[] = {
+ { .compatible = "sirf,marco-rsc" },
+ {},
+};
+
+static int __cpuinit sirfsoc_boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ unsigned long timeout;
+ struct device_node *np;
+
+ np = of_find_matching_node(NULL, rsc_ids);
+ if (!np)
+ return -ENODEV;
+
+ rsc_base = of_iomap(np, 0);
+ if (!rsc_base)
+ return -ENOMEM;
+
+ /*
+ * write the address of secondary startup into the sram register
+ * at offset 0x2C, then write the magic number 0x3CAF5D62 to the
+ * RSC register at offset 0x28, which is what boot rom code is
+ * waiting for. This would wake up the secondary core from WFE
+ */
+#define SIRFSOC_CPU1_JUMPADDR_OFFSET 0x2C
+ __raw_writel(virt_to_phys(sirfsoc_secondary_startup),
+ rsc_base + SIRFSOC_CPU1_JUMPADDR_OFFSET);
+
+#define SIRFSOC_CPU1_WAKEMAGIC_OFFSET 0x28
+ __raw_writel(0x3CAF5D62,
+ rsc_base + SIRFSOC_CPU1_WAKEMAGIC_OFFSET);
+
+ /* make sure write buffer is drained */
+ mb();
+
+ spin_lock(&boot_lock);
+
+ /*
+ * The secondary processor is waiting to be released from
+ * the holding pen - release it, then wait for it to flag
+ * that it has been released by resetting pen_release.
+ *
+ * Note that "pen_release" is the hardware CPU ID, whereas
+ * "cpu" is Linux's internal ID.
+ */
+ pen_release = cpu_logical_map(cpu);
+ __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
+ outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
+
+ /*
+ * Send the secondary CPU SEV, thereby causing the boot monitor to read
+ * the JUMPADDR and WAKEMAGIC, and branch to the address found there.
+ */
+ dsb_sev();
+
+ timeout = jiffies + (1 * HZ);
+ while (time_before(jiffies, timeout)) {
+ smp_rmb();
+ if (pen_release == -1)
+ break;
+
+ udelay(10);
+ }
+
+ /*
+ * now the secondary core is starting up let it run its
+ * calibrations, then wait for it to finish
+ */
+ spin_unlock(&boot_lock);
+
+ return pen_release != -1 ? -ENOSYS : 0;
+}
+
+static void __init sirfsoc_smp_init_cpus(void)
+{
+ set_smp_cross_call(gic_raise_softirq);
+}
+
+static void __init sirfsoc_smp_prepare_cpus(unsigned int max_cpus)
+{
+ scu_enable(scu_base);
+}
+
+struct smp_operations sirfsoc_smp_ops __initdata = {
+ .smp_init_cpus = sirfsoc_smp_init_cpus,
+ .smp_prepare_cpus = sirfsoc_smp_prepare_cpus,
+ .smp_secondary_init = sirfsoc_secondary_init,
+ .smp_boot_secondary = sirfsoc_boot_secondary,
+#ifdef CONFIG_HOTPLUG_CPU
+ .cpu_die = sirfsoc_cpu_die,
+#endif
+};