linux/arch/arm/mach-bcm/bcm63xx_smp.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Broadcom BCM63138 DSL SoCs SMP support code
   4 *
   5 * Copyright (C) 2015, Broadcom Corporation
   6 */
   7
   8#include <linux/delay.h>
   9#include <linux/init.h>
  10#include <linux/smp.h>
  11#include <linux/io.h>
  12#include <linux/of.h>
  13#include <linux/of_address.h>
  14
  15#include <asm/cacheflush.h>
  16#include <asm/smp_scu.h>
  17#include <asm/smp_plat.h>
  18#include <asm/vfp.h>
  19
  20#include "bcm63xx_smp.h"
  21
  22/* Size of mapped Cortex A9 SCU address space */
  23#define CORTEX_A9_SCU_SIZE      0x58
  24
  25/*
  26 * Enable the Cortex A9 Snoop Control Unit
  27 *
  28 * By the time this is called we already know there are multiple
  29 * cores present.  We assume we're running on a Cortex A9 processor,
  30 * so any trouble getting the base address register or getting the
  31 * SCU base is a problem.
  32 *
  33 * Return 0 if successful or an error code otherwise.
  34 */
  35static int __init scu_a9_enable(void)
  36{
  37        unsigned long config_base;
  38        void __iomem *scu_base;
  39        unsigned int i, ncores;
  40
  41        if (!scu_a9_has_base()) {
  42                pr_err("no configuration base address register!\n");
  43                return -ENXIO;
  44        }
  45
  46        /* Config base address register value is zero for uniprocessor */
  47        config_base = scu_a9_get_base();
  48        if (!config_base) {
  49                pr_err("hardware reports only one core\n");
  50                return -ENOENT;
  51        }
  52
  53        scu_base = ioremap((phys_addr_t)config_base, CORTEX_A9_SCU_SIZE);
  54        if (!scu_base) {
  55                pr_err("failed to remap config base (%lu/%u) for SCU\n",
  56                        config_base, CORTEX_A9_SCU_SIZE);
  57                return -ENOMEM;
  58        }
  59
  60        scu_enable(scu_base);
  61
  62        ncores = scu_base ? scu_get_core_count(scu_base) : 1;
  63
  64        if (ncores > nr_cpu_ids) {
  65                pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
  66                                ncores, nr_cpu_ids);
  67                ncores = nr_cpu_ids;
  68        }
  69
  70        /* The BCM63138 SoC has two Cortex-A9 CPUs, CPU0 features a complete
  71         * and fully functional VFP unit that can be used, but CPU1 does not.
  72         * Since we will not be able to trap kernel-mode NEON to force
  73         * migration to CPU0, just do not advertise VFP support at all.
  74         *
  75         * This will make vfp_init bail out and do not attempt to use VFP at
  76         * all, for kernel-mode NEON, we do not want to introduce any
  77         * conditionals in hot-paths, so we just restrict the system to UP.
  78         */
  79#ifdef CONFIG_VFP
  80        if (ncores > 1) {
  81                pr_warn("SMP: secondary CPUs lack VFP unit, disabling VFP\n");
  82                vfp_disable();
  83
  84#ifdef CONFIG_KERNEL_MODE_NEON
  85                WARN(1, "SMP: kernel-mode NEON enabled, restricting to UP\n");
  86                ncores = 1;
  87#endif
  88        }
  89#endif
  90
  91        for (i = 0; i < ncores; i++)
  92                set_cpu_possible(i, true);
  93
  94        iounmap(scu_base);      /* That's the last we'll need of this */
  95
  96        return 0;
  97}
  98
  99static const struct of_device_id bcm63138_bootlut_ids[] = {
 100        { .compatible = "brcm,bcm63138-bootlut", },
 101        { /* sentinel */ },
 102};
 103
 104#define BOOTLUT_RESET_VECT      0x20
 105
 106static int bcm63138_smp_boot_secondary(unsigned int cpu,
 107                                       struct task_struct *idle)
 108{
 109        void __iomem *bootlut_base;
 110        struct device_node *dn;
 111        int ret = 0;
 112        u32 val;
 113
 114        dn = of_find_matching_node(NULL, bcm63138_bootlut_ids);
 115        if (!dn) {
 116                pr_err("SMP: unable to find bcm63138 boot LUT node\n");
 117                return -ENODEV;
 118        }
 119
 120        bootlut_base = of_iomap(dn, 0);
 121        of_node_put(dn);
 122
 123        if (!bootlut_base) {
 124                pr_err("SMP: unable to remap boot LUT base register\n");
 125                return -ENOMEM;
 126        }
 127
 128        /* Locate the secondary CPU node */
 129        dn = of_get_cpu_node(cpu, NULL);
 130        if (!dn) {
 131                pr_err("SMP: failed to locate secondary CPU%d node\n", cpu);
 132                ret = -ENODEV;
 133                goto out;
 134        }
 135
 136        /* Write the secondary init routine to the BootLUT reset vector */
 137        val = __pa_symbol(secondary_startup);
 138        writel_relaxed(val, bootlut_base + BOOTLUT_RESET_VECT);
 139
 140        /* Power up the core, will jump straight to its reset vector when we
 141         * return
 142         */
 143        ret = bcm63xx_pmb_power_on_cpu(dn);
 144        of_node_put(dn);
 145        if (ret)
 146                goto out;
 147out:
 148        iounmap(bootlut_base);
 149
 150        return ret;
 151}
 152
 153static void __init bcm63138_smp_prepare_cpus(unsigned int max_cpus)
 154{
 155        int ret;
 156
 157        ret = scu_a9_enable();
 158        if (ret) {
 159                pr_warn("SMP: Cortex-A9 SCU setup failed\n");
 160                return;
 161        }
 162}
 163
 164static const struct smp_operations bcm63138_smp_ops __initconst = {
 165        .smp_prepare_cpus       = bcm63138_smp_prepare_cpus,
 166        .smp_boot_secondary     = bcm63138_smp_boot_secondary,
 167};
 168
 169CPU_METHOD_OF_DECLARE(bcm63138_smp, "brcm,bcm63138", &bcm63138_smp_ops);
 170