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