aml8726_mp.c revision 295319
1228753Smm/*-
2228753Smm * Copyright 2015 John Wehle <john@feith.com>
3228753Smm * All rights reserved.
4228753Smm *
5228753Smm * Redistribution and use in source and binary forms, with or without
6228753Smm * modification, are permitted provided that the following conditions
7228753Smm * are met:
8228753Smm * 1. Redistributions of source code must retain the above copyright
9228753Smm *    notice, this list of conditions and the following disclaimer.
10228753Smm * 2. Redistributions in binary form must reproduce the above copyright
11228753Smm *    notice, this list of conditions and the following disclaimer in the
12228753Smm *    documentation and/or other materials provided with the distribution.
13228753Smm *
14228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15228753Smm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16228753Smm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17228753Smm * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18228753Smm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19228753Smm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20228753Smm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21228753Smm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22228753Smm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23228753Smm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24228753Smm * SUCH DAMAGE.
25228753Smm */
26228753Smm
27228753Smm/*
28228753Smm * Amlogic aml8726 multiprocessor support.
29229592Smm *
30228753Smm * Some processors require powering on which involves poking registers
31228753Smm * on the aobus and cbus ... it's expected that these locations are set
32228753Smm * in stone.
33228753Smm *
34228753Smm * Locking is not used as these routines should only be called by the BP
35228753Smm * during startup and should complete prior to the APs being released (the
36228753Smm * issue being to ensure that a register such as AML_SOC_CPU_CLK_CNTL_REG
37228753Smm * is not concurrently modified).
38228753Smm */
39228753Smm
40228753Smm#include <sys/cdefs.h>
41228753Smm__FBSDID("$FreeBSD: head/sys/arm/amlogic/aml8726/aml8726_mp.c 295319 2016-02-05 14:57:41Z mmel $");
42228753Smm#include <sys/param.h>
43228753Smm#include <sys/systm.h>
44228753Smm#include <sys/bus.h>
45228753Smm#include <sys/kernel.h>
46228753Smm#include <sys/module.h>
47228753Smm#include <sys/lock.h>
48228753Smm#include <sys/mutex.h>
49228753Smm#include <sys/resource.h>
50228753Smm#include <sys/rman.h>
51228753Smm#include <sys/smp.h>
52228753Smm
53228753Smm#include <vm/vm.h>
54228753Smm#include <vm/pmap.h>
55228753Smm
56228753Smm#include <machine/cpu.h>
57228753Smm#include <machine/bus.h>
58228753Smm#include <machine/smp.h>
59228753Smm#include <machine/fdt.h>
60228753Smm#include <machine/intr.h>
61228753Smm
62228753Smm#include <dev/fdt/fdt_common.h>
63228753Smm#include <dev/ofw/ofw_bus.h>
64228753Smm#include <dev/ofw/ofw_bus_subr.h>
65228753Smm
66228753Smm#include <arm/amlogic/aml8726/aml8726_soc.h>
67228753Smm
68228753Smmstatic const char *scu_compatible[] = {
69228753Smm	"arm,cortex-a5-scu",
70228753Smm	"arm,cortex-a9-scu",
71228753Smm	NULL
72228753Smm};
73228753Smm
74228753Smmstatic const char *scu_errata_764369[] = {
75228753Smm	"arm,cortex-a9-scu",
76228753Smm	NULL
77228753Smm};
78228753Smm
79228753Smmstatic const char *cpucfg_compatible[] = {
80228753Smm	"amlogic,aml8726-cpuconfig",
81228753Smm	NULL
82228753Smm};
83228753Smm
84228753Smmstatic struct {
85228753Smm	boolean_t errata_764369;
86228753Smm	u_long scu_size;
87228753Smm	struct resource scu_res;
88228753Smm	u_long cpucfg_size;
89228753Smm	struct resource cpucfg_res;
90228753Smm	struct resource aobus_res;
91228753Smm	struct resource cbus_res;
92228753Smm} aml8726_smp;
93228753Smm
94228753Smm#define	AML_SCU_CONTROL_REG			0
95228753Smm#define	AML_SCU_CONTROL_ENABLE			1
96228753Smm#define	AML_SCU_CONFIG_REG			4
97228753Smm#define	AML_SCU_CONFIG_NCPU_MASK		0x3
98228753Smm#define	AML_SCU_CPU_PWR_STATUS_REG		8
99228753Smm#define	AML_SCU_CPU_PWR_STATUS_CPU3_MASK	(3 << 24)
100228753Smm#define	AML_SCU_CPU_PWR_STATUS_CPU2_MASK	(3 << 16)
101228753Smm#define	AML_SCU_CPU_PWR_STATUS_CPU1_MASK	(3 << 8)
102228753Smm#define	AML_SCU_CPU_PWR_STATUS_CPU0_MASK	3
103228753Smm#define	AML_SCU_INV_TAGS_REG			12
104228753Smm#define	AML_SCU_DIAG_CONTROL_REG		48
105228753Smm#define	AML_SCU_DIAG_CONTROL_DISABLE_MIGBIT	1
106228753Smm
107228753Smm#define	AML_CPUCONF_CONTROL_REG			0
108228753Smm#define	AML_CPUCONF_CPU1_ADDR_REG		4
109228753Smm#define	AML_CPUCONF_CPU2_ADDR_REG		8
110228753Smm#define	AML_CPUCONF_CPU3_ADDR_REG		12
111228753Smm
112228753Smm/* aobus */
113228753Smm
114228753Smm#define	AML_M8_CPU_PWR_CNTL0_REG		0xe0
115228753Smm#define	AML_M8B_CPU_PWR_CNTL0_MODE_CPU3_MASK	(3 << 22)
116228753Smm#define	AML_M8B_CPU_PWR_CNTL0_MODE_CPU2_MASK	(3 << 20)
117228753Smm#define	AML_M8B_CPU_PWR_CNTL0_MODE_CPU1_MASK	(3 << 18)
118228753Smm
119228753Smm#define	AML_M8_CPU_PWR_CNTL0_ISO_CPU3		(1 << 3)
120228753Smm#define	AML_M8_CPU_PWR_CNTL0_ISO_CPU2		(1 << 2)
121228753Smm#define	AML_M8_CPU_PWR_CNTL0_ISO_CPU1		(1 << 1)
122228753Smm
123228753Smm#define	AML_M8_CPU_PWR_CNTL1_REG		0xe4
124228753Smm#define	AML_M8B_CPU_PWR_CNTL1_PWR_CPU3		(1 << 19)
125228753Smm#define	AML_M8B_CPU_PWR_CNTL1_PWR_CPU2		(1 << 18)
126228753Smm#define	AML_M8B_CPU_PWR_CNTL1_PWR_CPU1		(1 << 17)
127228753Smm
128228753Smm#define	AML_M8_CPU_PWR_CNTL1_MODE_CPU3_MASK	(3 << 8)
129228753Smm#define	AML_M8_CPU_PWR_CNTL1_MODE_CPU2_MASK	(3 << 6)
130228753Smm#define	AML_M8_CPU_PWR_CNTL1_MODE_CPU1_MASK	(3 << 4)
131228753Smm
132228753Smm#define	AML_M8B_CPU_PWR_MEM_PD0_REG		0xf4
133228753Smm#define	AML_M8B_CPU_PWR_MEM_PD0_CPU3		(0xf << 20)
134228753Smm#define	AML_M8B_CPU_PWR_MEM_PD0_CPU2		(0xf << 24)
135228753Smm#define	AML_M8B_CPU_PWR_MEM_PD0_CPU1		(0xf << 28)
136228753Smm
137228753Smm/* cbus */
138228753Smm
139228753Smm#define	AML_SOC_CPU_CLK_CNTL_REG		0x419c
140228753Smm#define	AML_M8_CPU_CLK_CNTL_RESET_CPU3		(1 << 27)
141228753Smm#define	AML_M8_CPU_CLK_CNTL_RESET_CPU2		(1 << 26)
142228753Smm#define	AML_M8_CPU_CLK_CNTL_RESET_CPU1		(1 << 25)
143228753Smm
144228753Smm#define	SCU_WRITE_4(reg, value)		bus_write_4(&aml8726_smp.scu_res,    \
145228753Smm    (reg), (value))
146228753Smm#define	SCU_READ_4(reg)			bus_read_4(&aml8726_smp.scu_res, (reg))
147228753Smm#define	SCU_BARRIER(reg)		bus_barrier(&aml8726_smp.scu_res,    \
148228753Smm    (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
149228753Smm
150228753Smm#define	CPUCONF_WRITE_4(reg, value)	bus_write_4(&aml8726_smp.cpucfg_res, \
151228753Smm    (reg), (value))
152228753Smm#define	CPUCONF_READ_4(reg)		bus_read_4(&aml8726_smp.cpucfg_res,  \
153228753Smm    (reg))
154228753Smm#define	CPUCONF_BARRIER(reg)		bus_barrier(&aml8726_smp.cpucfg_res, \
155228753Smm    (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
156228753Smm
157228753Smm#define	AOBUS_WRITE_4(reg, value)	bus_write_4(&aml8726_smp.aobus_res,  \
158228753Smm    (reg), (value))
159228753Smm#define	AOBUS_READ_4(reg)		bus_read_4(&aml8726_smp.aobus_res,   \
160228753Smm    (reg))
161228753Smm#define	AOBUS_BARRIER(reg)		bus_barrier(&aml8726_smp.aobus_res,  \
162228753Smm    (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
163228753Smm
164228753Smm#define	CBUS_WRITE_4(reg, value)	bus_write_4(&aml8726_smp.cbus_res,   \
165228753Smm    (reg), (value))
166228753Smm#define CBUS_READ_4(reg)		bus_read_4(&aml8726_smp.cbus_res,    \
167228753Smm    (reg))
168228753Smm#define	CBUS_BARRIER(reg)		bus_barrier(&aml8726_smp.cbus_res,   \
169228753Smm    (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
170228753Smm
171228753Smmstatic phandle_t
172228753Smmfind_node_for_device(const char *device, const char **compatible)
173228753Smm{
174228753Smm	int i;
175228753Smm	phandle_t node;
176228753Smm
177228753Smm	/*
178228753Smm	 * Try to access the node directly i.e. through /aliases/.
179228753Smm	 */
180228753Smm
181228753Smm	if ((node = OF_finddevice(device)) != 0)
182228753Smm		for (i = 0; compatible[i]; i++)
183228753Smm			if (fdt_is_compatible_strict(node, compatible[i]))
184228753Smm				return node;
185228753Smm
186228753Smm	/*
187228753Smm	 * Find the node the long way.
188228753Smm	 */
189228753Smm
190228753Smm	for (i = 0; compatible[i]; i++) {
191228753Smm		if ((node = OF_finddevice("/soc")) == 0)
192228753Smm			return (0);
193228753Smm
194228753Smm		if ((node = fdt_find_compatible(node, compatible[i], 1)) != 0)
195228753Smm			return node;
196228753Smm	}
197228753Smm
198228753Smm	return (0);
199228753Smm}
200228753Smm
201228753Smm
202228753Smmstatic int
203228753Smmalloc_resource_for_node(phandle_t node, struct resource *res, u_long *size)
204228753Smm{
205228753Smm	int err;
206228753Smm	u_long pbase, psize;
207228753Smm	u_long start;
208228753Smm
209228753Smm	if ((err = fdt_get_range(OF_parent(node), 0, &pbase, &psize)) != 0 ||
210228753Smm	    (err = fdt_regsize(node, &start, size)) != 0)
211228753Smm		return (err);
212228753Smm
213228753Smm	start += pbase;
214228753Smm
215228753Smm	memset(res, 0, sizeof(*res));
216228753Smm
217228753Smm	res->r_bustag = fdtbus_bs_tag;
218228753Smm
219228753Smm	err = bus_space_map(res->r_bustag, start, *size, 0, &res->r_bushandle);
220228753Smm
221228753Smm	return (err);
222228753Smm}
223228753Smm
224228753Smm
225228753Smmstatic void
226228753Smmpower_on_cpu(int cpu)
227228753Smm{
228228753Smm	uint32_t scpsr;
229228753Smm	uint32_t value;
230228753Smm
231228753Smm	if (cpu <= 0)
232228753Smm		return;
233228753Smm
234228753Smm	/*
235228753Smm	 * Power on the CPU if the intricate details are known, otherwise
236228753Smm	 * just hope for the best (it may have already be powered on by
237228753Smm	 * the hardware / firmware).
238228753Smm	 */
239228753Smm
240228753Smm	switch (aml8726_soc_hw_rev) {
241228753Smm	case AML_SOC_HW_REV_M8:
242228753Smm	case AML_SOC_HW_REV_M8B:
243228753Smm		/*
244228753Smm		 * Set the SCU power status for the CPU to normal mode.
245228753Smm		 */
246228753Smm		scpsr = SCU_READ_4(AML_SCU_CPU_PWR_STATUS_REG);
247228753Smm		scpsr &= ~(AML_SCU_CPU_PWR_STATUS_CPU1_MASK << ((cpu - 1) * 8));
248228753Smm		SCU_WRITE_4(AML_SCU_CPU_PWR_STATUS_REG, scpsr);
249228753Smm		SCU_BARRIER(AML_SCU_CPU_PWR_STATUS_REG);
250228753Smm
251228753Smm		if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
252228753Smm			/*
253228753Smm			 * Reset may cause the current power status from the
254228753Smm			 * actual CPU to be written to the SCU (over-writing
255228753Smm			 * the value  we've just written) so set it to normal
256228753Smm			 * mode as well.
257228753Smm			 */
258228753Smm			 value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL0_REG);
259228753Smm			 value &= ~(AML_M8B_CPU_PWR_CNTL0_MODE_CPU1_MASK <<
260228753Smm			    ((cpu - 1) * 2));
261228753Smm			 AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL0_REG, value);
262228753Smm			 AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL0_REG);
263228753Smm		 }
264228753Smm
265228753Smm		DELAY(5);
266228753Smm
267228753Smm		/*
268228753Smm		 * Assert reset.
269228753Smm		 */
270228753Smm		value = CBUS_READ_4(AML_SOC_CPU_CLK_CNTL_REG);
271228753Smm		value |= AML_M8_CPU_CLK_CNTL_RESET_CPU1 << (cpu - 1);
272228753Smm		CBUS_WRITE_4(AML_SOC_CPU_CLK_CNTL_REG, value);
273228753Smm		CBUS_BARRIER(AML_SOC_CPU_CLK_CNTL_REG);
274228753Smm
275228753Smm		if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
276228753Smm			/*
277228753Smm			 * Release RAM pull-down.
278228753Smm			 */
279228753Smm			 value = AOBUS_READ_4(AML_M8B_CPU_PWR_MEM_PD0_REG);
280228753Smm			 value &= ~((uint32_t)AML_M8B_CPU_PWR_MEM_PD0_CPU1 >>
281228753Smm			    ((cpu - 1) * 4));
282228753Smm			 AOBUS_WRITE_4(AML_M8B_CPU_PWR_MEM_PD0_REG, value);
283228753Smm			 AOBUS_BARRIER(AML_M8B_CPU_PWR_MEM_PD0_REG);
284228753Smm		 }
285228753Smm
286228753Smm		/*
287228753Smm		 * Power on CPU.
288228753Smm		 */
289228753Smm		value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL1_REG);
290228753Smm		value &= ~(AML_M8_CPU_PWR_CNTL1_MODE_CPU1_MASK <<
291228753Smm		    ((cpu - 1) * 2));
292228753Smm		AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL1_REG, value);
293228753Smm		AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL1_REG);
294228753Smm
295228753Smm		DELAY(10);
296228753Smm
297228753Smm		if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
298228753Smm			/*
299228753Smm			 * Wait for power on confirmation.
300228753Smm			 */
301228753Smm			for ( ; ; ) {
302228753Smm				value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL1_REG);
303228753Smm				value &= AML_M8B_CPU_PWR_CNTL1_PWR_CPU1 <<
304228753Smm				    (cpu - 1);
305228753Smm				if (value)
306228753Smm					break;
307228753Smm				DELAY(10);
308228753Smm			}
309228753Smm		}
310228753Smm
311228753Smm		/*
312228753Smm		 * Release peripheral clamp.
313228753Smm		 */
314228753Smm		value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL0_REG);
315228753Smm		value &= ~(AML_M8_CPU_PWR_CNTL0_ISO_CPU1 << (cpu - 1));
316228753Smm		AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL0_REG, value);
317228753Smm		AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL0_REG);
318228753Smm
319228753Smm		/*
320228753Smm		 * Release reset.
321228753Smm		 */
322228753Smm		value = CBUS_READ_4(AML_SOC_CPU_CLK_CNTL_REG);
323228753Smm		value &= ~(AML_M8_CPU_CLK_CNTL_RESET_CPU1 << (cpu - 1));
324228753Smm		CBUS_WRITE_4(AML_SOC_CPU_CLK_CNTL_REG, value);
325228753Smm		CBUS_BARRIER(AML_SOC_CPU_CLK_CNTL_REG);
326228753Smm
327228753Smm		if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
328228753Smm			/*
329228753Smm			 * The Amlogic Linux platform code sets the SCU power
330228753Smm			 * status for the CPU again for some reason so we
331228753Smm			 * follow suit (perhaps in case the reset caused
332228753Smm			 * a stale power status from the actual CPU to be
333228753Smm			 * written to the SCU).
334228753Smm			 */
335228753Smm			SCU_WRITE_4(AML_SCU_CPU_PWR_STATUS_REG, scpsr);
336228753Smm			SCU_BARRIER(AML_SCU_CPU_PWR_STATUS_REG);
337228753Smm		}
338228753Smm		break;
339228753Smm	default:
340228753Smm		break;
341228753Smm	}
342228753Smm}
343228753Smm
344228753Smm
345228753Smmvoid
346228753Smmplatform_mp_init_secondary(void)
347228753Smm{
348228753Smm
349228753Smm	/*
350228753Smm	 * Consider modifying the timer driver to support
351228753Smm	 * per-cpu timers and then enabling the timer for
352228753Smm	 * each AP.
353228753Smm	 */
354228753Smm
355228753Smm	 intr_pic_init_secondary();
356228753Smm}
357228753Smm
358228753Smm
359228753Smmvoid
360228753Smmplatform_mp_setmaxid(void)
361228753Smm{
362228753Smm	int err;
363228753Smm	int i;
364228753Smm	int ncpu;
365228753Smm	phandle_t cpucfg_node;
366228753Smm	phandle_t scu_node;
367228753Smm	uint32_t value;
368228753Smm
369228753Smm	if (mp_ncpus != 0)
370228753Smm		return;
371228753Smm
372228753Smm	ncpu = 1;
373228753Smm
374228753Smm	/*
375228753Smm	 * Is the hardware necessary for SMP present?
376228753Smm	 */
377228753Smm
378228753Smm	if ((scu_node = find_node_for_device("scu", scu_compatible)) == 0)
379228753Smm		goto moveon;
380228753Smm
381228753Smm	if ((cpucfg_node = find_node_for_device("cpuconfig",
382228753Smm	    cpucfg_compatible)) == 0)
383228753Smm		goto moveon;
384228753Smm
385228753Smm	if (alloc_resource_for_node(scu_node, &aml8726_smp.scu_res,
386228753Smm	    &aml8726_smp.scu_size) != 0)
387228753Smm		panic("Could not allocate resource for SCU");
388228753Smm
389228753Smm	if (alloc_resource_for_node(cpucfg_node, &aml8726_smp.cpucfg_res,
390228753Smm	    &aml8726_smp.cpucfg_size) != 0)
391228753Smm		panic("Could not allocate resource for CPUCONFIG");
392228753Smm
393228753Smm
394228753Smm	/*
395228753Smm	 * Strictly speaking the aobus and cbus may not be required in
396228753Smm	 * order to start an AP (it depends on the processor), however
397228753Smm	 * always mapping them in simplifies the code.
398228753Smm	 */
399228753Smm
400228753Smm	aml8726_smp.aobus_res.r_bustag = fdtbus_bs_tag;
401228753Smm
402228753Smm	err = bus_space_map(aml8726_smp.aobus_res.r_bustag,
403228753Smm	    AML_SOC_AOBUS_BASE_ADDR, 0x100000,
404228753Smm	    0, &aml8726_smp.aobus_res.r_bushandle);
405228753Smm
406228753Smm	if (err)
407228753Smm		panic("Could not allocate resource for AOBUS");
408228753Smm
409228753Smm	aml8726_smp.cbus_res.r_bustag = fdtbus_bs_tag;
410228753Smm
411228753Smm	err = bus_space_map(aml8726_smp.cbus_res.r_bustag,
412228753Smm	    AML_SOC_CBUS_BASE_ADDR, 0x100000,
413228753Smm	    0, &aml8726_smp.cbus_res.r_bushandle);
414228753Smm
415228753Smm	if (err)
416228753Smm		panic("Could not allocate resource for CBUS");
417228753Smm
418228753Smm	aml8726_smp.errata_764369 = false;
419228753Smm	for (i = 0; scu_errata_764369[i]; i++)
420228753Smm		if (fdt_is_compatible_strict(scu_node, scu_errata_764369[i])) {
421228753Smm			aml8726_smp.errata_764369 = true;
422228753Smm			break;
423228753Smm		}
424228753Smm
425228753Smm	/*
426228753Smm	 * Read the number of CPUs present.
427228753Smm	 */
428228753Smm	value = SCU_READ_4(AML_SCU_CONFIG_REG);
429228753Smm	ncpu = (value & AML_SCU_CONFIG_NCPU_MASK) + 1;
430228753Smm
431228753Smmmoveon:
432228753Smm	mp_ncpus = ncpu;
433228753Smm	mp_maxid = ncpu - 1;
434228753Smm}
435228753Smm
436228753Smm
437228753Smmint
438228753Smmplatform_mp_probe(void)
439228753Smm{
440228753Smm
441228753Smm	if (mp_ncpus == 0)
442228753Smm		platform_mp_setmaxid();
443228753Smm
444228753Smm	return (mp_ncpus > 1);
445228753Smm}
446228753Smm
447228753Smm
448228753Smmvoid
449228753Smmplatform_mp_start_ap(void)
450228753Smm{
451228753Smm	int i;
452228753Smm	uint32_t reg;
453228753Smm	uint32_t value;
454228753Smm	vm_paddr_t paddr;
455228753Smm
456228753Smm	if (mp_ncpus < 2)
457228753Smm		return;
458228753Smm
459228753Smm	/*
460228753Smm	 * Invalidate SCU cache tags.  The 0x0000ffff constant invalidates
461228753Smm	 * all ways on all cores 0-3.  Per the ARM docs, it's harmless to
462228753Smm	 * write to the bits for cores that are not present.
463228753Smm	 */
464228753Smm	SCU_WRITE_4(AML_SCU_INV_TAGS_REG, 0x0000ffff);
465228753Smm
466228753Smm	if (aml8726_smp.errata_764369) {
467228753Smm		/*
468228753Smm		 * Erratum ARM/MP: 764369 (problems with cache maintenance).
469228753Smm		 * Setting the "disable-migratory bit" in the undocumented SCU
470228753Smm		 * Diagnostic Control Register helps work around the problem.
471228753Smm		 */
472228753Smm		value = SCU_READ_4(AML_SCU_DIAG_CONTROL_REG);
473228753Smm		value |= AML_SCU_DIAG_CONTROL_DISABLE_MIGBIT;
474228753Smm		SCU_WRITE_4(AML_SCU_DIAG_CONTROL_REG, value);
475228753Smm	}
476228753Smm
477228753Smm	/*
478228753Smm	 * Enable the SCU, then clean the cache on this core.  After these
479228753Smm	 * two operations the cache tag ram in the SCU is coherent with
480228753Smm	 * the contents of the cache on this core.  The other cores aren't
481228753Smm	 * running yet so their caches can't contain valid data yet, however
482228753Smm	 * we've initialized their SCU tag ram above, so they will be
483228753Smm	 * coherent from startup.
484228753Smm	 */
485228753Smm	value = SCU_READ_4(AML_SCU_CONTROL_REG);
486228753Smm	value |= AML_SCU_CONTROL_ENABLE;
487228753Smm	SCU_WRITE_4(AML_SCU_CONTROL_REG, value);
488228753Smm	SCU_BARRIER(AML_SCU_CONTROL_REG);
489228753Smm	dcache_wbinv_poc_all();
490228753Smm
491228753Smm	/* Set the boot address and power on each AP. */
492228753Smm	paddr = pmap_kextract((vm_offset_t)mpentry);
493228753Smm	for (i = 1; i < mp_ncpus; i++) {
494228753Smm		reg = AML_CPUCONF_CPU1_ADDR_REG + ((i - 1) * 4);
495228753Smm		CPUCONF_WRITE_4(reg, paddr);
496228753Smm		CPUCONF_BARRIER(reg);
497228753Smm
498228753Smm		power_on_cpu(i);
499228753Smm	}
500228753Smm
501228753Smm	/*
502228753Smm	 * Enable the APs.
503228753Smm	 *
504228753Smm	 * The Amlogic Linux platform code sets the lsb for some reason
505228753Smm	 * in addition to the enable bit for each AP so we follow suit
506228753Smm	 * (the lsb may be the enable bit for the BP, though in that case
507228753Smm	 * it should already be set since it's currently running).
508228753Smm	 */
509228753Smm	value = CPUCONF_READ_4(AML_CPUCONF_CONTROL_REG);
510228753Smm	value |= 1;
511228753Smm	for (i = 1; i < mp_ncpus; i++)
512228753Smm		value |= (1 << i);
513228753Smm	CPUCONF_WRITE_4(AML_CPUCONF_CONTROL_REG, value);
514228753Smm	CPUCONF_BARRIER(AML_CPUCONF_CONTROL_REG);
515228753Smm
516228753Smm	/* Wakeup the now enabled APs */
517228753Smm	armv7_sev();
518228753Smm
519228753Smm	/*
520228753Smm	 * Free the resources which are not needed after startup.
521228753Smm	 */
522228753Smm	bus_space_unmap(aml8726_smp.scu_res.r_bustag,
523228753Smm	    aml8726_smp.scu_res.r_bushandle,
524228753Smm	    aml8726_smp.scu_size);
525228753Smm	bus_space_unmap(aml8726_smp.cpucfg_res.r_bustag,
526228753Smm	    aml8726_smp.cpucfg_res.r_bushandle,
527228753Smm	    aml8726_smp.cpucfg_size);
528228753Smm	bus_space_unmap(aml8726_smp.aobus_res.r_bustag,
529228753Smm	    aml8726_smp.aobus_res.r_bushandle,
530228753Smm	    0x100000);
531228753Smm	bus_space_unmap(aml8726_smp.cbus_res.r_bustag,
532228753Smm	    aml8726_smp.cbus_res.r_bushandle,
533228753Smm	    0x100000);
534228753Smm	memset(&aml8726_smp, 0, sizeof(aml8726_smp));
535228753Smm}
536228753Smm
537228753Smmvoid
538228753Smmplatform_ipi_send(cpuset_t cpus, u_int ipi)
539228753Smm{
540228753Smm
541228753Smm	pic_ipi_send(cpus, ipi);
542228753Smm}
543228753Smm
544228753Smm/*
545228753Smm * Stub drivers for cosmetic purposes.
546228753Smm */
547228753Smmstruct aml8726_scu_softc {
548228753Smm	device_t	dev;
549228753Smm};
550228753Smm
551228753Smmstatic int
552228753Smmaml8726_scu_probe(device_t dev)
553228753Smm{
554228753Smm	int i;
555228753Smm
556228753Smm	for (i = 0; scu_compatible[i]; i++)
557228753Smm		if (ofw_bus_is_compatible(dev, scu_compatible[i]))
558228753Smm			break;
559228753Smm
560228753Smm	if (!scu_compatible[i])
561228753Smm		return (ENXIO);
562228753Smm
563228753Smm	device_set_desc(dev, "ARM Snoop Control Unit");
564228753Smm
565228753Smm	return (BUS_PROBE_DEFAULT);
566228753Smm}
567228753Smm
568228753Smmstatic int
569228753Smmaml8726_scu_attach(device_t dev)
570228753Smm{
571228753Smm	struct aml8726_scu_softc *sc = device_get_softc(dev);
572228753Smm
573228753Smm	sc->dev = dev;
574228753Smm
575228753Smm	return (0);
576228753Smm}
577228753Smm
578228753Smmstatic int
579228753Smmaml8726_scu_detach(device_t dev)
580228753Smm{
581228753Smm
582228753Smm	return (0);
583228753Smm}
584228753Smm
585228753Smmstatic device_method_t aml8726_scu_methods[] = {
586228753Smm	/* Device interface */
587228753Smm	DEVMETHOD(device_probe,		aml8726_scu_probe),
588228753Smm	DEVMETHOD(device_attach,	aml8726_scu_attach),
589228753Smm	DEVMETHOD(device_detach,	aml8726_scu_detach),
590228753Smm
591228753Smm	DEVMETHOD_END
592228753Smm};
593228753Smm
594228753Smmstatic driver_t aml8726_scu_driver = {
595228753Smm	"scu",
596228753Smm	aml8726_scu_methods,
597228753Smm	sizeof(struct aml8726_scu_softc),
598228753Smm};
599228753Smm
600228753Smmstatic devclass_t aml8726_scu_devclass;
601228753Smm
602228753SmmEARLY_DRIVER_MODULE(scu, simplebus, aml8726_scu_driver, aml8726_scu_devclass,
603228753Smm    0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE);
604228753Smm
605228753Smmstruct aml8726_cpucfg_softc {
606228753Smm	device_t	dev;
607228753Smm};
608228753Smm
609228753Smmstatic int
610228753Smmaml8726_cpucfg_probe(device_t dev)
611228753Smm{
612228753Smm	int i;
613228753Smm
614228753Smm	for (i = 0; cpucfg_compatible[i]; i++)
615228753Smm		if (ofw_bus_is_compatible(dev, cpucfg_compatible[i]))
616228753Smm			break;
617228753Smm
618228753Smm	if (!cpucfg_compatible[i])
619228753Smm		return (ENXIO);
620228753Smm
621228753Smm	device_set_desc(dev, "Amlogic CPU Config");
622228753Smm
623228753Smm	return (BUS_PROBE_DEFAULT);
624228753Smm}
625228753Smm
626228753Smmstatic int
627228753Smmaml8726_cpucfg_attach(device_t dev)
628228753Smm{
629228753Smm	struct aml8726_cpucfg_softc *sc = device_get_softc(dev);
630228753Smm
631228753Smm	sc->dev = dev;
632228753Smm
633228753Smm	return (0);
634228753Smm}
635228753Smm
636228753Smmstatic int
637228753Smmaml8726_cpucfg_detach(device_t dev)
638228753Smm{
639228753Smm
640228753Smm	return (0);
641228753Smm}
642228753Smm
643228753Smmstatic device_method_t aml8726_cpucfg_methods[] = {
644228753Smm	/* Device interface */
645228753Smm	DEVMETHOD(device_probe,		aml8726_cpucfg_probe),
646228753Smm	DEVMETHOD(device_attach,	aml8726_cpucfg_attach),
647228753Smm	DEVMETHOD(device_detach,	aml8726_cpucfg_detach),
648228753Smm
649228753Smm	DEVMETHOD_END
650228753Smm};
651228753Smm
652228753Smmstatic driver_t aml8726_cpucfg_driver = {
653228753Smm	"cpuconfig",
654228753Smm	aml8726_cpucfg_methods,
655228753Smm	sizeof(struct aml8726_cpucfg_softc),
656228753Smm};
657228753Smm
658228753Smmstatic devclass_t aml8726_cpucfg_devclass;
659228753Smm
660228753SmmEARLY_DRIVER_MODULE(cpuconfig, simplebus, aml8726_cpucfg_driver,
661228753Smm    aml8726_cpucfg_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE);
662228753Smm