mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
169 lines
4.1 KiB
169 lines
4.1 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* Broadcom BCM63138 DSL SoCs SMP support code |
|
* |
|
* Copyright (C) 2015, Broadcom Corporation |
|
*/ |
|
|
|
#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); |
|
of_node_put(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);
|
|
|