123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- /*
- * 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 = __pa_symbol(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;
- }
- }
- static const struct smp_operations bcm63138_smp_ops __initconst = {
- .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);
|