aml8726_mp.c revision 280905
11556Srgrimes/*-
21556Srgrimes * Copyright 2015 John Wehle <john@feith.com>
31556Srgrimes * All rights reserved.
41556Srgrimes *
51556Srgrimes * Redistribution and use in source and binary forms, with or without
61556Srgrimes * modification, are permitted provided that the following conditions
71556Srgrimes * are met:
81556Srgrimes * 1. Redistributions of source code must retain the above copyright
91556Srgrimes *    notice, this list of conditions and the following disclaimer.
101556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111556Srgrimes *    notice, this list of conditions and the following disclaimer in the
121556Srgrimes *    documentation and/or other materials provided with the distribution.
131556Srgrimes *
141556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
151556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
181556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
201556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241556Srgrimes * SUCH DAMAGE.
251556Srgrimes */
261556Srgrimes
271556Srgrimes/*
281556Srgrimes * Amlogic aml8726 multiprocessor support.
291556Srgrimes *
301556Srgrimes * Some processors require powering on which involves poking registers
311556Srgrimes * on the aobus and cbus ... it's expected that these locations are set
321556Srgrimes * in stone.
331556Srgrimes *
3436150Scharnier * Locking is not used as these routines should only be called by the BP
3536150Scharnier * during startup and should complete prior to the APs being released (the
3636150Scharnier * issue being to ensure that a register such as AML_SOC_CPU_CLK_CNTL_REG
371556Srgrimes * is not concurrently modified).
3899110Sobrien */
3999110Sobrien
401556Srgrimes#include <sys/cdefs.h>
411556Srgrimes__FBSDID("$FreeBSD: head/sys/arm/amlogic/aml8726/aml8726_mp.c 280905 2015-03-31 11:50:46Z ganbold $");
421556Srgrimes#include <sys/param.h>
431556Srgrimes#include <sys/systm.h>
441556Srgrimes#include <sys/bus.h>
451556Srgrimes#include <sys/kernel.h>
461556Srgrimes#include <sys/module.h>
471556Srgrimes#include <sys/lock.h>
481556Srgrimes#include <sys/mutex.h>
491556Srgrimes#include <sys/resource.h>
501556Srgrimes#include <sys/rman.h>
511556Srgrimes#include <sys/smp.h>
5217987Speter
5317987Speter#include <machine/bus.h>
541556Srgrimes#include <machine/smp.h>
5517987Speter#include <machine/fdt.h>
5617987Speter#include <machine/intr.h>
571556Srgrimes
5817987Speter#include <dev/fdt/fdt_common.h>
5917987Speter#include <dev/ofw/ofw_bus.h>
6017987Speter#include <dev/ofw/ofw_bus_subr.h>
6117987Speter
6217987Speter#include <arm/amlogic/aml8726/aml8726_soc.h>
6397909Stjr
641556Srgrimesstatic const char *scu_compatible[] = {
6517987Speter	"arm,cortex-a5-scu",
661556Srgrimes	"arm,cortex-a9-scu",
671556Srgrimes	NULL
681556Srgrimes};
691556Srgrimes
701556Srgrimesstatic const char *scu_errata_764369[] = {
71104286Stjr	"arm,cortex-a9-scu",
721556Srgrimes	NULL
731556Srgrimes};
74199629Sjilles
751556Srgrimesstatic const char *cpucfg_compatible[] = {
761556Srgrimes	"amlogic,aml8726-cpuconfig",
771556Srgrimes	NULL
781556Srgrimes};
791556Srgrimes
801556Srgrimesstatic struct {
811556Srgrimes	boolean_t errata_764369;
821556Srgrimes	u_long scu_size;
831556Srgrimes	struct resource scu_res;
841556Srgrimes	u_long cpucfg_size;
851556Srgrimes	struct resource cpucfg_res;
861556Srgrimes	struct resource aobus_res;
871556Srgrimes	struct resource cbus_res;
881556Srgrimes} aml8726_smp;
891556Srgrimes
901556Srgrimes#define	AML_SCU_CONTROL_REG			0
911556Srgrimes#define	AML_SCU_CONTROL_ENABLE			1
921556Srgrimes#define	AML_SCU_CONFIG_REG			4
931556Srgrimes#define	AML_SCU_CONFIG_NCPU_MASK		0x3
941556Srgrimes#define	AML_SCU_CPU_PWR_STATUS_REG		8
951556Srgrimes#define	AML_SCU_CPU_PWR_STATUS_CPU3_MASK	(3 << 24)
961556Srgrimes#define	AML_SCU_CPU_PWR_STATUS_CPU2_MASK	(3 << 16)
971556Srgrimes#define	AML_SCU_CPU_PWR_STATUS_CPU1_MASK	(3 << 8)
981556Srgrimes#define	AML_SCU_CPU_PWR_STATUS_CPU0_MASK	3
9990111Simp#define	AML_SCU_INV_TAGS_REG			12
10090111Simp#define	AML_SCU_DIAG_CONTROL_REG		48
1011556Srgrimes#define	AML_SCU_DIAG_CONTROL_DISABLE_MIGBIT	1
1021556Srgrimes
1031556Srgrimes#define	AML_CPUCONF_CONTROL_REG			0
10497815Stjr#define	AML_CPUCONF_CPU1_ADDR_REG		4
10597815Stjr#define	AML_CPUCONF_CPU2_ADDR_REG		8
10697815Stjr#define	AML_CPUCONF_CPU3_ADDR_REG		12
10797815Stjr
10897815Stjr/* aobus */
1091556Srgrimes
1101556Srgrimes#define	AML_M8_CPU_PWR_CNTL0_REG		0xe0
11190111Simp#define	AML_M8B_CPU_PWR_CNTL0_MODE_CPU3_MASK	(3 << 22)
11290111Simp#define	AML_M8B_CPU_PWR_CNTL0_MODE_CPU2_MASK	(3 << 20)
1131556Srgrimes#define	AML_M8B_CPU_PWR_CNTL0_MODE_CPU1_MASK	(3 << 18)
1141556Srgrimes
1151556Srgrimes#define	AML_M8_CPU_PWR_CNTL0_ISO_CPU3		(1 << 3)
11697815Stjr#define	AML_M8_CPU_PWR_CNTL0_ISO_CPU2		(1 << 2)
11797815Stjr#define	AML_M8_CPU_PWR_CNTL0_ISO_CPU1		(1 << 1)
11897815Stjr
11997815Stjr#define	AML_M8_CPU_PWR_CNTL1_REG		0xe4
12097815Stjr#define	AML_M8B_CPU_PWR_CNTL1_PWR_CPU3		(1 << 19)
1211556Srgrimes#define	AML_M8B_CPU_PWR_CNTL1_PWR_CPU2		(1 << 18)
1221556Srgrimes#define	AML_M8B_CPU_PWR_CNTL1_PWR_CPU1		(1 << 17)
12390111Simp
12490111Simp#define	AML_M8_CPU_PWR_CNTL1_MODE_CPU3_MASK	(3 << 8)
1251556Srgrimes#define	AML_M8_CPU_PWR_CNTL1_MODE_CPU2_MASK	(3 << 6)
1261556Srgrimes#define	AML_M8_CPU_PWR_CNTL1_MODE_CPU1_MASK	(3 << 4)
1271556Srgrimes
1281556Srgrimes#define	AML_M8B_CPU_PWR_MEM_PD0_REG		0xf4
12997815Stjr#define	AML_M8B_CPU_PWR_MEM_PD0_CPU3		(0xf << 20)
13097815Stjr#define	AML_M8B_CPU_PWR_MEM_PD0_CPU2		(0xf << 24)
13197815Stjr#define	AML_M8B_CPU_PWR_MEM_PD0_CPU1		(0xf << 28)
13297815Stjr
13397815Stjr/* cbus */
134194516Sjilles
1351556Srgrimes#define	AML_SOC_CPU_CLK_CNTL_REG		0x419c
136153245Sstefanf#define	AML_M8_CPU_CLK_CNTL_RESET_CPU3		(1 << 27)
137153245Sstefanf#define	AML_M8_CPU_CLK_CNTL_RESET_CPU2		(1 << 26)
138153245Sstefanf#define	AML_M8_CPU_CLK_CNTL_RESET_CPU1		(1 << 25)
139153245Sstefanf
140194516Sjilles#define	SCU_WRITE_4(reg, value)		bus_write_4(&aml8726_smp.scu_res,    \
141194516Sjilles    (reg), (value))
142194516Sjilles#define	SCU_READ_4(reg)			bus_read_4(&aml8726_smp.scu_res, (reg))
14397909Stjr#define	SCU_BARRIER(reg)		bus_barrier(&aml8726_smp.scu_res,    \
14497909Stjr    (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
14597909Stjr
14697909Stjr#define	CPUCONF_WRITE_4(reg, value)	bus_write_4(&aml8726_smp.cpucfg_res, \
147194516Sjilles    (reg), (value))
14897815Stjr#define	CPUCONF_READ_4(reg)		bus_read_4(&aml8726_smp.cpucfg_res,  \
14997815Stjr    (reg))
15097815Stjr#define	CPUCONF_BARRIER(reg)		bus_barrier(&aml8726_smp.cpucfg_res, \
151194516Sjilles    (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
152194516Sjilles
153194516Sjilles#define	AOBUS_WRITE_4(reg, value)	bus_write_4(&aml8726_smp.aobus_res,  \
154194516Sjilles    (reg), (value))
155194516Sjilles#define	AOBUS_READ_4(reg)		bus_read_4(&aml8726_smp.aobus_res,   \
15697815Stjr    (reg))
15797815Stjr#define	AOBUS_BARRIER(reg)		bus_barrier(&aml8726_smp.aobus_res,  \
158194516Sjilles    (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
159194516Sjilles
160194516Sjilles#define	CBUS_WRITE_4(reg, value)	bus_write_4(&aml8726_smp.cbus_res,   \
16197815Stjr    (reg), (value))
16297815Stjr#define CBUS_READ_4(reg)		bus_read_4(&aml8726_smp.cbus_res,    \
16397815Stjr    (reg))
164194516Sjilles#define	CBUS_BARRIER(reg)		bus_barrier(&aml8726_smp.cbus_res,   \
165194516Sjilles    (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
16697815Stjr
16797815Stjrstatic phandle_t
168117261Sddsfind_node_for_device(const char *device, const char **compatible)
1691556Srgrimes{
1701556Srgrimes	int i;
17190111Simp	phandle_t node;
17290111Simp
1731556Srgrimes	/*
1741556Srgrimes	 * Try to access the node directly i.e. through /aliases/.
1751556Srgrimes	 */
1761556Srgrimes
1771556Srgrimes	if ((node = OF_finddevice(device)) != 0)
1781556Srgrimes		for (i = 0; compatible[i]; i++)
1791556Srgrimes			if (fdt_is_compatible_strict(node, compatible[i]))
1801556Srgrimes				return node;
1811556Srgrimes
1821556Srgrimes	/*
1831556Srgrimes	 * Find the node the long way.
1841556Srgrimes	 */
1851556Srgrimes
1861556Srgrimes	for (i = 0; compatible[i]; i++) {
1871556Srgrimes		if ((node = OF_finddevice("/soc")) == 0)
1881556Srgrimes			return (0);
1891556Srgrimes
1901556Srgrimes		if ((node = fdt_find_compatible(node, compatible[i], 1)) != 0)
1911556Srgrimes			return node;
1921556Srgrimes	}
1931556Srgrimes
1941556Srgrimes	return (0);
1951556Srgrimes}
1961556Srgrimes
1971556Srgrimes
1981556Srgrimesstatic int
1991556Srgrimesalloc_resource_for_node(phandle_t node, struct resource *res, u_long *size)
2001556Srgrimes{
20190111Simp	int err;
20290111Simp	u_long pbase, psize;
2031556Srgrimes	u_long start;
2041556Srgrimes
2051556Srgrimes	if ((err = fdt_get_range(OF_parent(node), 0, &pbase, &psize)) != 0 ||
2061556Srgrimes	    (err = fdt_regsize(node, &start, size)) != 0)
2071556Srgrimes		return (err);
2081556Srgrimes
20990111Simp	start += pbase;
21090111Simp
2111556Srgrimes	memset(res, 0, sizeof(*res));
2121556Srgrimes
2131556Srgrimes	res->r_bustag = fdtbus_bs_tag;
2141556Srgrimes
2151556Srgrimes	err = bus_space_map(res->r_bustag, start, *size, 0, &res->r_bushandle);
2161556Srgrimes
2171556Srgrimes	return (err);
2181556Srgrimes}
2191556Srgrimes
2201556Srgrimes
2211556Srgrimesstatic void
22290111Simppower_on_cpu(int cpu)
22390111Simp{
2241556Srgrimes	uint32_t scpsr;
2251556Srgrimes	uint32_t value;
2261556Srgrimes
2271556Srgrimes	if (cpu <= 0)
2281556Srgrimes		return;
2291556Srgrimes
2301556Srgrimes	/*
2311556Srgrimes	 * Power on the CPU if the intricate details are known, otherwise
2321556Srgrimes	 * just hope for the best (it may have already be powered on by
2331556Srgrimes	 * the hardware / firmware).
2341556Srgrimes	 */
23590111Simp
23690111Simp	switch (aml8726_soc_hw_rev) {
2371556Srgrimes	case AML_SOC_HW_REV_M8:
2381556Srgrimes	case AML_SOC_HW_REV_M8B:
2391556Srgrimes		/*
2401556Srgrimes		 * Set the SCU power status for the CPU to normal mode.
2411556Srgrimes		 */
2421556Srgrimes		scpsr = SCU_READ_4(AML_SCU_CPU_PWR_STATUS_REG);
2431556Srgrimes		scpsr &= ~(AML_SCU_CPU_PWR_STATUS_CPU1_MASK << ((cpu - 1) * 8));
2441556Srgrimes		SCU_WRITE_4(AML_SCU_CPU_PWR_STATUS_REG, scpsr);
2451556Srgrimes		SCU_BARRIER(AML_SCU_CPU_PWR_STATUS_REG);
24690111Simp
24790111Simp		if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
2481556Srgrimes			/*
2491556Srgrimes			 * Reset may cause the current power status from the
2501556Srgrimes			 * actual CPU to be written to the SCU (over-writing
2511556Srgrimes			 * the value  we've just written) so set it to normal
2521556Srgrimes			 * mode as well.
2531556Srgrimes			 */
2541556Srgrimes			 value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL0_REG);
2551556Srgrimes			 value &= ~(AML_M8B_CPU_PWR_CNTL0_MODE_CPU1_MASK <<
256199629Sjilles			    ((cpu - 1) * 2));
25790111Simp			 AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL0_REG, value);
2581556Srgrimes			 AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL0_REG);
2591556Srgrimes		 }
2601556Srgrimes
2611556Srgrimes		DELAY(5);
2621556Srgrimes
2631556Srgrimes		/*
2641556Srgrimes		 * Assert reset.
2651556Srgrimes		 */
2661556Srgrimes		value = CBUS_READ_4(AML_SOC_CPU_CLK_CNTL_REG);
26790111Simp		value |= AML_M8_CPU_CLK_CNTL_RESET_CPU1 << (cpu - 1);
26890111Simp		CBUS_WRITE_4(AML_SOC_CPU_CLK_CNTL_REG, value);
2691556Srgrimes		CBUS_BARRIER(AML_SOC_CPU_CLK_CNTL_REG);
270104289Stjr
2711556Srgrimes		if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
272104289Stjr			/*
273104289Stjr			 * Release RAM pull-down.
274104289Stjr			 */
275104289Stjr			 value = AOBUS_READ_4(AML_M8B_CPU_PWR_MEM_PD0_REG);
2761556Srgrimes			 value &= ~((uint32_t)AML_M8B_CPU_PWR_MEM_PD0_CPU1 >>
277104289Stjr			    ((cpu - 1) * 4));
278104286Stjr			 AOBUS_WRITE_4(AML_M8B_CPU_PWR_MEM_PD0_REG, value);
279104289Stjr			 AOBUS_BARRIER(AML_M8B_CPU_PWR_MEM_PD0_REG);
280104289Stjr		 }
281104289Stjr
2821556Srgrimes		/*
2831556Srgrimes		 * Power on CPU.
284104286Stjr		 */
285104286Stjr		value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL1_REG);
286104286Stjr		value &= ~(AML_M8_CPU_PWR_CNTL1_MODE_CPU1_MASK <<
287104286Stjr		    ((cpu - 1) * 2));
288104286Stjr		AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL1_REG, value);
289104286Stjr		AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL1_REG);
2901556Srgrimes
291104286Stjr		DELAY(10);
292104286Stjr
293104286Stjr		if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
294104286Stjr			/*
295104286Stjr			 * Wait for power on confirmation.
296104286Stjr			 */
2971556Srgrimes			for ( ; ; ) {
298104286Stjr				value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL1_REG);
299104286Stjr				value &= AML_M8B_CPU_PWR_CNTL1_PWR_CPU1 <<
3001556Srgrimes				    (cpu - 1);
3011556Srgrimes				if (value)
30290111Simp					break;
30390111Simp				DELAY(10);
304104286Stjr			}
3051556Srgrimes		}
306104286Stjr
307104286Stjr		/*
308104286Stjr		 * Release peripheral clamp.
3091556Srgrimes		 */
3101556Srgrimes		value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL0_REG);
3111556Srgrimes		value &= ~(AML_M8_CPU_PWR_CNTL0_ISO_CPU1 << (cpu - 1));
3121556Srgrimes		AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL0_REG, value);
3131556Srgrimes		AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL0_REG);
3141556Srgrimes
3151556Srgrimes		/*
3161556Srgrimes		 * Release reset.
31790111Simp		 */
31890111Simp		value = CBUS_READ_4(AML_SOC_CPU_CLK_CNTL_REG);
3191556Srgrimes		value &= ~(AML_M8_CPU_CLK_CNTL_RESET_CPU1 << (cpu - 1));
3201556Srgrimes		CBUS_WRITE_4(AML_SOC_CPU_CLK_CNTL_REG, value);
3211556Srgrimes		CBUS_BARRIER(AML_SOC_CPU_CLK_CNTL_REG);
3221556Srgrimes
3231556Srgrimes		if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
3241556Srgrimes			/*
3251556Srgrimes			 * The Amlogic Linux platform code sets the SCU power
3261556Srgrimes			 * status for the CPU again for some reason so we
3271556Srgrimes			 * follow suit (perhaps in case the reset caused
3281556Srgrimes			 * a stale power status from the actual CPU to be
3291556Srgrimes			 * written to the SCU).
3301556Srgrimes			 */
3311556Srgrimes			SCU_WRITE_4(AML_SCU_CPU_PWR_STATUS_REG, scpsr);
3321556Srgrimes			SCU_BARRIER(AML_SCU_CPU_PWR_STATUS_REG);
3331556Srgrimes		}
3341556Srgrimes		break;
3351556Srgrimes	default:
3361556Srgrimes		break;
3371556Srgrimes	}
3381556Srgrimes}
3391556Srgrimes
340
341void
342platform_mp_init_secondary(void)
343{
344
345	/*
346	 * Consider modifying the timer driver to support
347	 * per-cpu timers and then enabling the timer for
348	 * each AP.
349	 */
350
351	 arm_init_secondary_ic();
352}
353
354
355void
356platform_mp_setmaxid(void)
357{
358	int err;
359	int i;
360	int ncpu;
361	phandle_t cpucfg_node;
362	phandle_t scu_node;
363	uint32_t value;
364
365	if (mp_ncpus != 0)
366		return;
367
368	ncpu = 1;
369
370	/*
371	 * Is the hardware necessary for SMP present?
372	 */
373
374	if ((scu_node = find_node_for_device("scu", scu_compatible)) == 0)
375		goto moveon;
376
377	if ((cpucfg_node = find_node_for_device("cpuconfig",
378	    cpucfg_compatible)) == 0)
379		goto moveon;
380
381	if (alloc_resource_for_node(scu_node, &aml8726_smp.scu_res,
382	    &aml8726_smp.scu_size) != 0)
383		panic("Could not allocate resource for SCU");
384
385	if (alloc_resource_for_node(cpucfg_node, &aml8726_smp.cpucfg_res,
386	    &aml8726_smp.cpucfg_size) != 0)
387		panic("Could not allocate resource for CPUCONFIG");
388
389
390	/*
391	 * Strictly speaking the aobus and cbus may not be required in
392	 * order to start an AP (it depends on the processor), however
393	 * always mapping them in simplifies the code.
394	 */
395
396	aml8726_smp.aobus_res.r_bustag = fdtbus_bs_tag;
397
398	err = bus_space_map(aml8726_smp.aobus_res.r_bustag,
399	    AML_SOC_AOBUS_BASE_ADDR, 0x100000,
400	    0, &aml8726_smp.aobus_res.r_bushandle);
401
402	if (err)
403		panic("Could not allocate resource for AOBUS");
404
405	aml8726_smp.cbus_res.r_bustag = fdtbus_bs_tag;
406
407	err = bus_space_map(aml8726_smp.cbus_res.r_bustag,
408	    AML_SOC_CBUS_BASE_ADDR, 0x100000,
409	    0, &aml8726_smp.cbus_res.r_bushandle);
410
411	if (err)
412		panic("Could not allocate resource for CBUS");
413
414	aml8726_smp.errata_764369 = false;
415	for (i = 0; scu_errata_764369[i]; i++)
416		if (fdt_is_compatible_strict(scu_node, scu_errata_764369[i])) {
417			aml8726_smp.errata_764369 = true;
418			break;
419		}
420
421	/*
422	 * Read the number of CPUs present.
423	 */
424	value = SCU_READ_4(AML_SCU_CONFIG_REG);
425	ncpu = (value & AML_SCU_CONFIG_NCPU_MASK) + 1;
426
427moveon:
428	mp_ncpus = ncpu;
429	mp_maxid = ncpu - 1;
430}
431
432
433int
434platform_mp_probe(void)
435{
436
437	if (mp_ncpus == 0)
438		platform_mp_setmaxid();
439
440	return (mp_ncpus > 1);
441}
442
443
444void
445platform_mp_start_ap(void)
446{
447	int i;
448	uint32_t reg;
449	uint32_t value;
450	vm_paddr_t paddr;
451
452	if (mp_ncpus < 2)
453		return;
454
455	/*
456	 * Invalidate SCU cache tags.  The 0x0000ffff constant invalidates
457	 * all ways on all cores 0-3.  Per the ARM docs, it's harmless to
458	 * write to the bits for cores that are not present.
459	 */
460	SCU_WRITE_4(AML_SCU_INV_TAGS_REG, 0x0000ffff);
461
462	if (aml8726_smp.errata_764369) {
463		/*
464		 * Erratum ARM/MP: 764369 (problems with cache maintenance).
465		 * Setting the "disable-migratory bit" in the undocumented SCU
466		 * Diagnostic Control Register helps work around the problem.
467		 */
468		value = SCU_READ_4(AML_SCU_DIAG_CONTROL_REG);
469		value |= AML_SCU_DIAG_CONTROL_DISABLE_MIGBIT;
470		SCU_WRITE_4(AML_SCU_DIAG_CONTROL_REG, value);
471	}
472
473	/*
474	 * Enable the SCU, then clean the cache on this core.  After these
475	 * two operations the cache tag ram in the SCU is coherent with
476	 * the contents of the cache on this core.  The other cores aren't
477	 * running yet so their caches can't contain valid data yet, however
478	 * we've initialized their SCU tag ram above, so they will be
479	 * coherent from startup.
480	 */
481	value = SCU_READ_4(AML_SCU_CONTROL_REG);
482	value |= AML_SCU_CONTROL_ENABLE;
483	SCU_WRITE_4(AML_SCU_CONTROL_REG, value);
484	SCU_BARRIER(AML_SCU_CONTROL_REG);
485	cpu_idcache_wbinv_all();
486
487	/* Set the boot address and power on each AP. */
488	paddr = pmap_kextract((vm_offset_t)mpentry);
489	for (i = 1; i < mp_ncpus; i++) {
490		reg = AML_CPUCONF_CPU1_ADDR_REG + ((i - 1) * 4);
491		CPUCONF_WRITE_4(reg, paddr);
492		CPUCONF_BARRIER(reg);
493
494		power_on_cpu(i);
495	}
496
497	/*
498	 * Enable the APs.
499	 *
500	 * The Amlogic Linux platform code sets the lsb for some reason
501	 * in addition to the enable bit for each AP so we follow suit
502	 * (the lsb may be the enable bit for the BP, though in that case
503	 * it should already be set since it's currently running).
504	 */
505	value = CPUCONF_READ_4(AML_CPUCONF_CONTROL_REG);
506	value |= 1;
507	for (i = 1; i < mp_ncpus; i++)
508		value |= (1 << i);
509	CPUCONF_WRITE_4(AML_CPUCONF_CONTROL_REG, value);
510	CPUCONF_BARRIER(AML_CPUCONF_CONTROL_REG);
511
512	/* Wakeup the now enabled APs */
513	armv7_sev();
514
515	/*
516	 * Free the resources which are not needed after startup.
517	 */
518	bus_space_unmap(aml8726_smp.scu_res.r_bustag,
519	    aml8726_smp.scu_res.r_bushandle,
520	    aml8726_smp.scu_size);
521	bus_space_unmap(aml8726_smp.cpucfg_res.r_bustag,
522	    aml8726_smp.cpucfg_res.r_bushandle,
523	    aml8726_smp.cpucfg_size);
524	bus_space_unmap(aml8726_smp.aobus_res.r_bustag,
525	    aml8726_smp.aobus_res.r_bushandle,
526	    0x100000);
527	bus_space_unmap(aml8726_smp.cbus_res.r_bustag,
528	    aml8726_smp.cbus_res.r_bushandle,
529	    0x100000);
530	memset(&aml8726_smp, 0, sizeof(aml8726_smp));
531}
532
533void
534platform_ipi_send(cpuset_t cpus, u_int ipi)
535{
536
537	pic_ipi_send(cpus, ipi);
538}
539
540/*
541 * Stub drivers for cosmetic purposes.
542 */
543struct aml8726_scu_softc {
544	device_t	dev;
545};
546
547static int
548aml8726_scu_probe(device_t dev)
549{
550	int i;
551
552	for (i = 0; scu_compatible[i]; i++)
553		if (ofw_bus_is_compatible(dev, scu_compatible[i]))
554			break;
555
556	if (!scu_compatible[i])
557		return (ENXIO);
558
559	device_set_desc(dev, "ARM Snoop Control Unit");
560
561	return (BUS_PROBE_DEFAULT);
562}
563
564static int
565aml8726_scu_attach(device_t dev)
566{
567	struct aml8726_scu_softc *sc = device_get_softc(dev);
568
569	sc->dev = dev;
570
571	return (0);
572}
573
574static int
575aml8726_scu_detach(device_t dev)
576{
577
578	return (0);
579}
580
581static device_method_t aml8726_scu_methods[] = {
582	/* Device interface */
583	DEVMETHOD(device_probe,		aml8726_scu_probe),
584	DEVMETHOD(device_attach,	aml8726_scu_attach),
585	DEVMETHOD(device_detach,	aml8726_scu_detach),
586
587	DEVMETHOD_END
588};
589
590static driver_t aml8726_scu_driver = {
591	"scu",
592	aml8726_scu_methods,
593	sizeof(struct aml8726_scu_softc),
594};
595
596static devclass_t aml8726_scu_devclass;
597
598EARLY_DRIVER_MODULE(scu, simplebus, aml8726_scu_driver, aml8726_scu_devclass,
599    0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE);
600
601struct aml8726_cpucfg_softc {
602	device_t	dev;
603};
604
605static int
606aml8726_cpucfg_probe(device_t dev)
607{
608	int i;
609
610	for (i = 0; cpucfg_compatible[i]; i++)
611		if (ofw_bus_is_compatible(dev, cpucfg_compatible[i]))
612			break;
613
614	if (!cpucfg_compatible[i])
615		return (ENXIO);
616
617	device_set_desc(dev, "Amlogic CPU Config");
618
619	return (BUS_PROBE_DEFAULT);
620}
621
622static int
623aml8726_cpucfg_attach(device_t dev)
624{
625	struct aml8726_cpucfg_softc *sc = device_get_softc(dev);
626
627	sc->dev = dev;
628
629	return (0);
630}
631
632static int
633aml8726_cpucfg_detach(device_t dev)
634{
635
636	return (0);
637}
638
639static device_method_t aml8726_cpucfg_methods[] = {
640	/* Device interface */
641	DEVMETHOD(device_probe,		aml8726_cpucfg_probe),
642	DEVMETHOD(device_attach,	aml8726_cpucfg_attach),
643	DEVMETHOD(device_detach,	aml8726_cpucfg_detach),
644
645	DEVMETHOD_END
646};
647
648static driver_t aml8726_cpucfg_driver = {
649	"cpuconfig",
650	aml8726_cpucfg_methods,
651	sizeof(struct aml8726_cpucfg_softc),
652};
653
654static devclass_t aml8726_cpucfg_devclass;
655
656EARLY_DRIVER_MODULE(cpuconfig, simplebus, aml8726_cpucfg_driver,
657    aml8726_cpucfg_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE);
658