Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Broadcom BCM63138 DSL SoCs SMP support code
0004  *
0005  * Copyright (C) 2015, Broadcom Corporation
0006  */
0007 
0008 #include <linux/delay.h>
0009 #include <linux/init.h>
0010 #include <linux/smp.h>
0011 #include <linux/io.h>
0012 #include <linux/of.h>
0013 #include <linux/of_address.h>
0014 
0015 #include <asm/cacheflush.h>
0016 #include <asm/smp_scu.h>
0017 #include <asm/smp_plat.h>
0018 #include <asm/vfp.h>
0019 
0020 #include "bcm63xx_smp.h"
0021 
0022 /* Size of mapped Cortex A9 SCU address space */
0023 #define CORTEX_A9_SCU_SIZE  0x58
0024 
0025 /*
0026  * Enable the Cortex A9 Snoop Control Unit
0027  *
0028  * By the time this is called we already know there are multiple
0029  * cores present.  We assume we're running on a Cortex A9 processor,
0030  * so any trouble getting the base address register or getting the
0031  * SCU base is a problem.
0032  *
0033  * Return 0 if successful or an error code otherwise.
0034  */
0035 static int __init scu_a9_enable(void)
0036 {
0037     unsigned long config_base;
0038     void __iomem *scu_base;
0039     unsigned int i, ncores;
0040 
0041     if (!scu_a9_has_base()) {
0042         pr_err("no configuration base address register!\n");
0043         return -ENXIO;
0044     }
0045 
0046     /* Config base address register value is zero for uniprocessor */
0047     config_base = scu_a9_get_base();
0048     if (!config_base) {
0049         pr_err("hardware reports only one core\n");
0050         return -ENOENT;
0051     }
0052 
0053     scu_base = ioremap((phys_addr_t)config_base, CORTEX_A9_SCU_SIZE);
0054     if (!scu_base) {
0055         pr_err("failed to remap config base (%lu/%u) for SCU\n",
0056             config_base, CORTEX_A9_SCU_SIZE);
0057         return -ENOMEM;
0058     }
0059 
0060     scu_enable(scu_base);
0061 
0062     ncores = scu_base ? scu_get_core_count(scu_base) : 1;
0063 
0064     if (ncores > nr_cpu_ids) {
0065         pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
0066                 ncores, nr_cpu_ids);
0067         ncores = nr_cpu_ids;
0068     }
0069 
0070     /* The BCM63138 SoC has two Cortex-A9 CPUs, CPU0 features a complete
0071      * and fully functional VFP unit that can be used, but CPU1 does not.
0072      * Since we will not be able to trap kernel-mode NEON to force
0073      * migration to CPU0, just do not advertise VFP support at all.
0074      *
0075      * This will make vfp_init bail out and do not attempt to use VFP at
0076      * all, for kernel-mode NEON, we do not want to introduce any
0077      * conditionals in hot-paths, so we just restrict the system to UP.
0078      */
0079 #ifdef CONFIG_VFP
0080     if (ncores > 1) {
0081         pr_warn("SMP: secondary CPUs lack VFP unit, disabling VFP\n");
0082         vfp_disable();
0083 
0084 #ifdef CONFIG_KERNEL_MODE_NEON
0085         WARN(1, "SMP: kernel-mode NEON enabled, restricting to UP\n");
0086         ncores = 1;
0087 #endif
0088     }
0089 #endif
0090 
0091     for (i = 0; i < ncores; i++)
0092         set_cpu_possible(i, true);
0093 
0094     iounmap(scu_base);  /* That's the last we'll need of this */
0095 
0096     return 0;
0097 }
0098 
0099 static const struct of_device_id bcm63138_bootlut_ids[] = {
0100     { .compatible = "brcm,bcm63138-bootlut", },
0101     { /* sentinel */ },
0102 };
0103 
0104 #define BOOTLUT_RESET_VECT  0x20
0105 
0106 static int bcm63138_smp_boot_secondary(unsigned int cpu,
0107                        struct task_struct *idle)
0108 {
0109     void __iomem *bootlut_base;
0110     struct device_node *dn;
0111     int ret = 0;
0112     u32 val;
0113 
0114     dn = of_find_matching_node(NULL, bcm63138_bootlut_ids);
0115     if (!dn) {
0116         pr_err("SMP: unable to find bcm63138 boot LUT node\n");
0117         return -ENODEV;
0118     }
0119 
0120     bootlut_base = of_iomap(dn, 0);
0121     of_node_put(dn);
0122 
0123     if (!bootlut_base) {
0124         pr_err("SMP: unable to remap boot LUT base register\n");
0125         return -ENOMEM;
0126     }
0127 
0128     /* Locate the secondary CPU node */
0129     dn = of_get_cpu_node(cpu, NULL);
0130     if (!dn) {
0131         pr_err("SMP: failed to locate secondary CPU%d node\n", cpu);
0132         ret = -ENODEV;
0133         goto out;
0134     }
0135 
0136     /* Write the secondary init routine to the BootLUT reset vector */
0137     val = __pa_symbol(secondary_startup);
0138     writel_relaxed(val, bootlut_base + BOOTLUT_RESET_VECT);
0139 
0140     /* Power up the core, will jump straight to its reset vector when we
0141      * return
0142      */
0143     ret = bcm63xx_pmb_power_on_cpu(dn);
0144     of_node_put(dn);
0145     if (ret)
0146         goto out;
0147 out:
0148     iounmap(bootlut_base);
0149 
0150     return ret;
0151 }
0152 
0153 static void __init bcm63138_smp_prepare_cpus(unsigned int max_cpus)
0154 {
0155     int ret;
0156 
0157     ret = scu_a9_enable();
0158     if (ret) {
0159         pr_warn("SMP: Cortex-A9 SCU setup failed\n");
0160         return;
0161     }
0162 }
0163 
0164 static const struct smp_operations bcm63138_smp_ops __initconst = {
0165     .smp_prepare_cpus   = bcm63138_smp_prepare_cpus,
0166     .smp_boot_secondary = bcm63138_smp_boot_secondary,
0167 };
0168 
0169 CPU_METHOD_OF_DECLARE(bcm63138_smp, "brcm,bcm63138", &bcm63138_smp_ops);