These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / arch / arm / mach-bcm / bcm63xx_smp.c
diff --git a/kernel/arch/arm/mach-bcm/bcm63xx_smp.c b/kernel/arch/arm/mach-bcm/bcm63xx_smp.c
new file mode 100644 (file)
index 0000000..19be904
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * Broadcom BCM63138 DSL SoCs SMP support code
+ *
+ * Copyright (C) 2015, Broadcom Corporation
+ *
+ * Licensed under the terms of the GPLv2
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include <asm/cacheflush.h>
+#include <asm/smp_scu.h>
+#include <asm/smp_plat.h>
+#include <asm/vfp.h>
+
+#include "bcm63xx_smp.h"
+
+/* Size of mapped Cortex A9 SCU address space */
+#define CORTEX_A9_SCU_SIZE     0x58
+
+/*
+ * Enable the Cortex A9 Snoop Control Unit
+ *
+ * By the time this is called we already know there are multiple
+ * cores present.  We assume we're running on a Cortex A9 processor,
+ * so any trouble getting the base address register or getting the
+ * SCU base is a problem.
+ *
+ * Return 0 if successful or an error code otherwise.
+ */
+static int __init scu_a9_enable(void)
+{
+       unsigned long config_base;
+       void __iomem *scu_base;
+       unsigned int i, ncores;
+
+       if (!scu_a9_has_base()) {
+               pr_err("no configuration base address register!\n");
+               return -ENXIO;
+       }
+
+       /* Config base address register value is zero for uniprocessor */
+       config_base = scu_a9_get_base();
+       if (!config_base) {
+               pr_err("hardware reports only one core\n");
+               return -ENOENT;
+       }
+
+       scu_base = ioremap((phys_addr_t)config_base, CORTEX_A9_SCU_SIZE);
+       if (!scu_base) {
+               pr_err("failed to remap config base (%lu/%u) for SCU\n",
+                       config_base, CORTEX_A9_SCU_SIZE);
+               return -ENOMEM;
+       }
+
+       scu_enable(scu_base);
+
+       ncores = scu_base ? scu_get_core_count(scu_base) : 1;
+
+       if (ncores > nr_cpu_ids) {
+               pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
+                               ncores, nr_cpu_ids);
+               ncores = nr_cpu_ids;
+       }
+
+       /* The BCM63138 SoC has two Cortex-A9 CPUs, CPU0 features a complete
+        * and fully functional VFP unit that can be used, but CPU1 does not.
+        * Since we will not be able to trap kernel-mode NEON to force
+        * migration to CPU0, just do not advertise VFP support at all.
+        *
+        * This will make vfp_init bail out and do not attempt to use VFP at
+        * all, for kernel-mode NEON, we do not want to introduce any
+        * conditionals in hot-paths, so we just restrict the system to UP.
+        */
+#ifdef CONFIG_VFP
+       if (ncores > 1) {
+               pr_warn("SMP: secondary CPUs lack VFP unit, disabling VFP\n");
+               vfp_disable();
+
+#ifdef CONFIG_KERNEL_MODE_NEON
+               WARN(1, "SMP: kernel-mode NEON enabled, restricting to UP\n");
+               ncores = 1;
+#endif
+       }
+#endif
+
+       for (i = 0; i < ncores; i++)
+               set_cpu_possible(i, true);
+
+       iounmap(scu_base);      /* That's the last we'll need of this */
+
+       return 0;
+}
+
+static const struct of_device_id bcm63138_bootlut_ids[] = {
+       { .compatible = "brcm,bcm63138-bootlut", },
+       { /* sentinel */ },
+};
+
+#define BOOTLUT_RESET_VECT     0x20
+
+static int bcm63138_smp_boot_secondary(unsigned int cpu,
+                                      struct task_struct *idle)
+{
+       void __iomem *bootlut_base;
+       struct device_node *dn;
+       int ret = 0;
+       u32 val;
+
+       dn = of_find_matching_node(NULL, bcm63138_bootlut_ids);
+       if (!dn) {
+               pr_err("SMP: unable to find bcm63138 boot LUT node\n");
+               return -ENODEV;
+       }
+
+       bootlut_base = of_iomap(dn, 0);
+       of_node_put(dn);
+
+       if (!bootlut_base) {
+               pr_err("SMP: unable to remap boot LUT base register\n");
+               return -ENOMEM;
+       }
+
+       /* Locate the secondary CPU node */
+       dn = of_get_cpu_node(cpu, NULL);
+       if (!dn) {
+               pr_err("SMP: failed to locate secondary CPU%d node\n", cpu);
+               ret = -ENODEV;
+               goto out;
+       }
+
+       /* Write the secondary init routine to the BootLUT reset vector */
+       val = virt_to_phys(secondary_startup);
+       writel_relaxed(val, bootlut_base + BOOTLUT_RESET_VECT);
+
+       /* Power up the core, will jump straight to its reset vector when we
+        * return
+        */
+       ret = bcm63xx_pmb_power_on_cpu(dn);
+       if (ret)
+               goto out;
+out:
+       iounmap(bootlut_base);
+
+       return ret;
+}
+
+static void __init bcm63138_smp_prepare_cpus(unsigned int max_cpus)
+{
+       int ret;
+
+       ret = scu_a9_enable();
+       if (ret) {
+               pr_warn("SMP: Cortex-A9 SCU setup failed\n");
+               return;
+       }
+}
+
+struct smp_operations bcm63138_smp_ops __initdata = {
+       .smp_prepare_cpus       = bcm63138_smp_prepare_cpus,
+       .smp_boot_secondary     = bcm63138_smp_boot_secondary,
+};
+
+CPU_METHOD_OF_DECLARE(bcm63138_smp, "brcm,bcm63138", &bcm63138_smp_ops);