0001
0002
0003
0004
0005
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
0023 #define CORTEX_A9_SCU_SIZE 0x58
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
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
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
0071
0072
0073
0074
0075
0076
0077
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);
0095
0096 return 0;
0097 }
0098
0099 static const struct of_device_id bcm63138_bootlut_ids[] = {
0100 { .compatible = "brcm,bcm63138-bootlut", },
0101 { },
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
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
0137 val = __pa_symbol(secondary_startup);
0138 writel_relaxed(val, bootlut_base + BOOTLUT_RESET_VECT);
0139
0140
0141
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);