1280905Sganbold/*-
2280905Sganbold * Copyright 2015 John Wehle <john@feith.com>
3280905Sganbold * All rights reserved.
4280905Sganbold *
5280905Sganbold * Redistribution and use in source and binary forms, with or without
6280905Sganbold * modification, are permitted provided that the following conditions
7280905Sganbold * are met:
8280905Sganbold * 1. Redistributions of source code must retain the above copyright
9280905Sganbold *    notice, this list of conditions and the following disclaimer.
10280905Sganbold * 2. Redistributions in binary form must reproduce the above copyright
11280905Sganbold *    notice, this list of conditions and the following disclaimer in the
12280905Sganbold *    documentation and/or other materials provided with the distribution.
13280905Sganbold *
14280905Sganbold * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15280905Sganbold * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16280905Sganbold * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17280905Sganbold * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18280905Sganbold * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19280905Sganbold * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20280905Sganbold * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21280905Sganbold * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22280905Sganbold * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23280905Sganbold * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24280905Sganbold * SUCH DAMAGE.
25280905Sganbold */
26280905Sganbold
27280905Sganbold/*
28280905Sganbold * Amlogic aml8726 multiprocessor support.
29280905Sganbold *
30280905Sganbold * Some processors require powering on which involves poking registers
31280905Sganbold * on the aobus and cbus ... it's expected that these locations are set
32280905Sganbold * in stone.
33280905Sganbold *
34280905Sganbold * Locking is not used as these routines should only be called by the BP
35280905Sganbold * during startup and should complete prior to the APs being released (the
36280905Sganbold * issue being to ensure that a register such as AML_SOC_CPU_CLK_CNTL_REG
37280905Sganbold * is not concurrently modified).
38280905Sganbold */
39280905Sganbold
40280905Sganbold#include <sys/cdefs.h>
41280905Sganbold__FBSDID("$FreeBSD: stable/11/sys/arm/amlogic/aml8726/aml8726_mp.c 307344 2016-10-15 08:27:54Z mmel $");
42280905Sganbold#include <sys/param.h>
43280905Sganbold#include <sys/systm.h>
44280905Sganbold#include <sys/bus.h>
45280905Sganbold#include <sys/kernel.h>
46280905Sganbold#include <sys/module.h>
47280905Sganbold#include <sys/lock.h>
48280905Sganbold#include <sys/mutex.h>
49280905Sganbold#include <sys/resource.h>
50280905Sganbold#include <sys/rman.h>
51280905Sganbold#include <sys/smp.h>
52280905Sganbold
53281092Sandrew#include <vm/vm.h>
54281092Sandrew#include <vm/pmap.h>
55281092Sandrew
56295319Smmel#include <machine/cpu.h>
57280905Sganbold#include <machine/bus.h>
58280905Sganbold#include <machine/smp.h>
59280905Sganbold#include <machine/fdt.h>
60280905Sganbold#include <machine/intr.h>
61280905Sganbold
62280905Sganbold#include <dev/fdt/fdt_common.h>
63280905Sganbold#include <dev/ofw/ofw_bus.h>
64280905Sganbold#include <dev/ofw/ofw_bus_subr.h>
65280905Sganbold
66280905Sganbold#include <arm/amlogic/aml8726/aml8726_soc.h>
67280905Sganbold
68280905Sganboldstatic const char *scu_compatible[] = {
69280905Sganbold	"arm,cortex-a5-scu",
70280905Sganbold	"arm,cortex-a9-scu",
71280905Sganbold	NULL
72280905Sganbold};
73280905Sganbold
74280905Sganboldstatic const char *scu_errata_764369[] = {
75280905Sganbold	"arm,cortex-a9-scu",
76280905Sganbold	NULL
77280905Sganbold};
78280905Sganbold
79280905Sganboldstatic const char *cpucfg_compatible[] = {
80280905Sganbold	"amlogic,aml8726-cpuconfig",
81280905Sganbold	NULL
82280905Sganbold};
83280905Sganbold
84280905Sganboldstatic struct {
85280905Sganbold	boolean_t errata_764369;
86280905Sganbold	u_long scu_size;
87280905Sganbold	struct resource scu_res;
88280905Sganbold	u_long cpucfg_size;
89280905Sganbold	struct resource cpucfg_res;
90280905Sganbold	struct resource aobus_res;
91280905Sganbold	struct resource cbus_res;
92280905Sganbold} aml8726_smp;
93280905Sganbold
94280905Sganbold#define	AML_SCU_CONTROL_REG			0
95280905Sganbold#define	AML_SCU_CONTROL_ENABLE			1
96280905Sganbold#define	AML_SCU_CONFIG_REG			4
97280905Sganbold#define	AML_SCU_CONFIG_NCPU_MASK		0x3
98280905Sganbold#define	AML_SCU_CPU_PWR_STATUS_REG		8
99280905Sganbold#define	AML_SCU_CPU_PWR_STATUS_CPU3_MASK	(3 << 24)
100280905Sganbold#define	AML_SCU_CPU_PWR_STATUS_CPU2_MASK	(3 << 16)
101280905Sganbold#define	AML_SCU_CPU_PWR_STATUS_CPU1_MASK	(3 << 8)
102280905Sganbold#define	AML_SCU_CPU_PWR_STATUS_CPU0_MASK	3
103280905Sganbold#define	AML_SCU_INV_TAGS_REG			12
104280905Sganbold#define	AML_SCU_DIAG_CONTROL_REG		48
105280905Sganbold#define	AML_SCU_DIAG_CONTROL_DISABLE_MIGBIT	1
106280905Sganbold
107280905Sganbold#define	AML_CPUCONF_CONTROL_REG			0
108280905Sganbold#define	AML_CPUCONF_CPU1_ADDR_REG		4
109280905Sganbold#define	AML_CPUCONF_CPU2_ADDR_REG		8
110280905Sganbold#define	AML_CPUCONF_CPU3_ADDR_REG		12
111280905Sganbold
112280905Sganbold/* aobus */
113280905Sganbold
114280905Sganbold#define	AML_M8_CPU_PWR_CNTL0_REG		0xe0
115280905Sganbold#define	AML_M8B_CPU_PWR_CNTL0_MODE_CPU3_MASK	(3 << 22)
116280905Sganbold#define	AML_M8B_CPU_PWR_CNTL0_MODE_CPU2_MASK	(3 << 20)
117280905Sganbold#define	AML_M8B_CPU_PWR_CNTL0_MODE_CPU1_MASK	(3 << 18)
118280905Sganbold
119280905Sganbold#define	AML_M8_CPU_PWR_CNTL0_ISO_CPU3		(1 << 3)
120280905Sganbold#define	AML_M8_CPU_PWR_CNTL0_ISO_CPU2		(1 << 2)
121280905Sganbold#define	AML_M8_CPU_PWR_CNTL0_ISO_CPU1		(1 << 1)
122280905Sganbold
123280905Sganbold#define	AML_M8_CPU_PWR_CNTL1_REG		0xe4
124280905Sganbold#define	AML_M8B_CPU_PWR_CNTL1_PWR_CPU3		(1 << 19)
125280905Sganbold#define	AML_M8B_CPU_PWR_CNTL1_PWR_CPU2		(1 << 18)
126280905Sganbold#define	AML_M8B_CPU_PWR_CNTL1_PWR_CPU1		(1 << 17)
127280905Sganbold
128280905Sganbold#define	AML_M8_CPU_PWR_CNTL1_MODE_CPU3_MASK	(3 << 8)
129280905Sganbold#define	AML_M8_CPU_PWR_CNTL1_MODE_CPU2_MASK	(3 << 6)
130280905Sganbold#define	AML_M8_CPU_PWR_CNTL1_MODE_CPU1_MASK	(3 << 4)
131280905Sganbold
132280905Sganbold#define	AML_M8B_CPU_PWR_MEM_PD0_REG		0xf4
133280905Sganbold#define	AML_M8B_CPU_PWR_MEM_PD0_CPU3		(0xf << 20)
134280905Sganbold#define	AML_M8B_CPU_PWR_MEM_PD0_CPU2		(0xf << 24)
135280905Sganbold#define	AML_M8B_CPU_PWR_MEM_PD0_CPU1		(0xf << 28)
136280905Sganbold
137280905Sganbold/* cbus */
138280905Sganbold
139280905Sganbold#define	AML_SOC_CPU_CLK_CNTL_REG		0x419c
140280905Sganbold#define	AML_M8_CPU_CLK_CNTL_RESET_CPU3		(1 << 27)
141280905Sganbold#define	AML_M8_CPU_CLK_CNTL_RESET_CPU2		(1 << 26)
142280905Sganbold#define	AML_M8_CPU_CLK_CNTL_RESET_CPU1		(1 << 25)
143280905Sganbold
144280905Sganbold#define	SCU_WRITE_4(reg, value)		bus_write_4(&aml8726_smp.scu_res,    \
145280905Sganbold    (reg), (value))
146280905Sganbold#define	SCU_READ_4(reg)			bus_read_4(&aml8726_smp.scu_res, (reg))
147280905Sganbold#define	SCU_BARRIER(reg)		bus_barrier(&aml8726_smp.scu_res,    \
148280905Sganbold    (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
149280905Sganbold
150280905Sganbold#define	CPUCONF_WRITE_4(reg, value)	bus_write_4(&aml8726_smp.cpucfg_res, \
151280905Sganbold    (reg), (value))
152280905Sganbold#define	CPUCONF_READ_4(reg)		bus_read_4(&aml8726_smp.cpucfg_res,  \
153280905Sganbold    (reg))
154280905Sganbold#define	CPUCONF_BARRIER(reg)		bus_barrier(&aml8726_smp.cpucfg_res, \
155280905Sganbold    (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
156280905Sganbold
157280905Sganbold#define	AOBUS_WRITE_4(reg, value)	bus_write_4(&aml8726_smp.aobus_res,  \
158280905Sganbold    (reg), (value))
159280905Sganbold#define	AOBUS_READ_4(reg)		bus_read_4(&aml8726_smp.aobus_res,   \
160280905Sganbold    (reg))
161280905Sganbold#define	AOBUS_BARRIER(reg)		bus_barrier(&aml8726_smp.aobus_res,  \
162280905Sganbold    (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
163280905Sganbold
164280905Sganbold#define	CBUS_WRITE_4(reg, value)	bus_write_4(&aml8726_smp.cbus_res,   \
165280905Sganbold    (reg), (value))
166280905Sganbold#define CBUS_READ_4(reg)		bus_read_4(&aml8726_smp.cbus_res,    \
167280905Sganbold    (reg))
168280905Sganbold#define	CBUS_BARRIER(reg)		bus_barrier(&aml8726_smp.cbus_res,   \
169280905Sganbold    (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
170280905Sganbold
171280905Sganboldstatic phandle_t
172280905Sganboldfind_node_for_device(const char *device, const char **compatible)
173280905Sganbold{
174280905Sganbold	int i;
175280905Sganbold	phandle_t node;
176280905Sganbold
177280905Sganbold	/*
178280905Sganbold	 * Try to access the node directly i.e. through /aliases/.
179280905Sganbold	 */
180280905Sganbold
181280905Sganbold	if ((node = OF_finddevice(device)) != 0)
182280905Sganbold		for (i = 0; compatible[i]; i++)
183280905Sganbold			if (fdt_is_compatible_strict(node, compatible[i]))
184280905Sganbold				return node;
185280905Sganbold
186280905Sganbold	/*
187280905Sganbold	 * Find the node the long way.
188280905Sganbold	 */
189280905Sganbold
190280905Sganbold	for (i = 0; compatible[i]; i++) {
191280905Sganbold		if ((node = OF_finddevice("/soc")) == 0)
192280905Sganbold			return (0);
193280905Sganbold
194280905Sganbold		if ((node = fdt_find_compatible(node, compatible[i], 1)) != 0)
195280905Sganbold			return node;
196280905Sganbold	}
197280905Sganbold
198280905Sganbold	return (0);
199280905Sganbold}
200280905Sganbold
201280905Sganbold
202280905Sganboldstatic int
203280905Sganboldalloc_resource_for_node(phandle_t node, struct resource *res, u_long *size)
204280905Sganbold{
205280905Sganbold	int err;
206280905Sganbold	u_long pbase, psize;
207280905Sganbold	u_long start;
208280905Sganbold
209280905Sganbold	if ((err = fdt_get_range(OF_parent(node), 0, &pbase, &psize)) != 0 ||
210280905Sganbold	    (err = fdt_regsize(node, &start, size)) != 0)
211280905Sganbold		return (err);
212280905Sganbold
213280905Sganbold	start += pbase;
214280905Sganbold
215280905Sganbold	memset(res, 0, sizeof(*res));
216280905Sganbold
217280905Sganbold	res->r_bustag = fdtbus_bs_tag;
218280905Sganbold
219280905Sganbold	err = bus_space_map(res->r_bustag, start, *size, 0, &res->r_bushandle);
220280905Sganbold
221280905Sganbold	return (err);
222280905Sganbold}
223280905Sganbold
224280905Sganbold
225280905Sganboldstatic void
226280905Sganboldpower_on_cpu(int cpu)
227280905Sganbold{
228280905Sganbold	uint32_t scpsr;
229280905Sganbold	uint32_t value;
230280905Sganbold
231280905Sganbold	if (cpu <= 0)
232280905Sganbold		return;
233280905Sganbold
234280905Sganbold	/*
235280905Sganbold	 * Power on the CPU if the intricate details are known, otherwise
236280905Sganbold	 * just hope for the best (it may have already be powered on by
237280905Sganbold	 * the hardware / firmware).
238280905Sganbold	 */
239280905Sganbold
240280905Sganbold	switch (aml8726_soc_hw_rev) {
241280905Sganbold	case AML_SOC_HW_REV_M8:
242280905Sganbold	case AML_SOC_HW_REV_M8B:
243280905Sganbold		/*
244280905Sganbold		 * Set the SCU power status for the CPU to normal mode.
245280905Sganbold		 */
246280905Sganbold		scpsr = SCU_READ_4(AML_SCU_CPU_PWR_STATUS_REG);
247280905Sganbold		scpsr &= ~(AML_SCU_CPU_PWR_STATUS_CPU1_MASK << ((cpu - 1) * 8));
248280905Sganbold		SCU_WRITE_4(AML_SCU_CPU_PWR_STATUS_REG, scpsr);
249280905Sganbold		SCU_BARRIER(AML_SCU_CPU_PWR_STATUS_REG);
250280905Sganbold
251280905Sganbold		if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
252280905Sganbold			/*
253280905Sganbold			 * Reset may cause the current power status from the
254280905Sganbold			 * actual CPU to be written to the SCU (over-writing
255280905Sganbold			 * the value  we've just written) so set it to normal
256280905Sganbold			 * mode as well.
257280905Sganbold			 */
258280905Sganbold			 value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL0_REG);
259280905Sganbold			 value &= ~(AML_M8B_CPU_PWR_CNTL0_MODE_CPU1_MASK <<
260280905Sganbold			    ((cpu - 1) * 2));
261280905Sganbold			 AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL0_REG, value);
262280905Sganbold			 AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL0_REG);
263280905Sganbold		 }
264280905Sganbold
265280905Sganbold		DELAY(5);
266280905Sganbold
267280905Sganbold		/*
268280905Sganbold		 * Assert reset.
269280905Sganbold		 */
270280905Sganbold		value = CBUS_READ_4(AML_SOC_CPU_CLK_CNTL_REG);
271280905Sganbold		value |= AML_M8_CPU_CLK_CNTL_RESET_CPU1 << (cpu - 1);
272280905Sganbold		CBUS_WRITE_4(AML_SOC_CPU_CLK_CNTL_REG, value);
273280905Sganbold		CBUS_BARRIER(AML_SOC_CPU_CLK_CNTL_REG);
274280905Sganbold
275280905Sganbold		if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
276280905Sganbold			/*
277280905Sganbold			 * Release RAM pull-down.
278280905Sganbold			 */
279280905Sganbold			 value = AOBUS_READ_4(AML_M8B_CPU_PWR_MEM_PD0_REG);
280280905Sganbold			 value &= ~((uint32_t)AML_M8B_CPU_PWR_MEM_PD0_CPU1 >>
281280905Sganbold			    ((cpu - 1) * 4));
282280905Sganbold			 AOBUS_WRITE_4(AML_M8B_CPU_PWR_MEM_PD0_REG, value);
283280905Sganbold			 AOBUS_BARRIER(AML_M8B_CPU_PWR_MEM_PD0_REG);
284280905Sganbold		 }
285280905Sganbold
286280905Sganbold		/*
287280905Sganbold		 * Power on CPU.
288280905Sganbold		 */
289280905Sganbold		value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL1_REG);
290280905Sganbold		value &= ~(AML_M8_CPU_PWR_CNTL1_MODE_CPU1_MASK <<
291280905Sganbold		    ((cpu - 1) * 2));
292280905Sganbold		AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL1_REG, value);
293280905Sganbold		AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL1_REG);
294280905Sganbold
295280905Sganbold		DELAY(10);
296280905Sganbold
297280905Sganbold		if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
298280905Sganbold			/*
299280905Sganbold			 * Wait for power on confirmation.
300280905Sganbold			 */
301280905Sganbold			for ( ; ; ) {
302280905Sganbold				value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL1_REG);
303280905Sganbold				value &= AML_M8B_CPU_PWR_CNTL1_PWR_CPU1 <<
304280905Sganbold				    (cpu - 1);
305280905Sganbold				if (value)
306280905Sganbold					break;
307280905Sganbold				DELAY(10);
308280905Sganbold			}
309280905Sganbold		}
310280905Sganbold
311280905Sganbold		/*
312280905Sganbold		 * Release peripheral clamp.
313280905Sganbold		 */
314280905Sganbold		value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL0_REG);
315280905Sganbold		value &= ~(AML_M8_CPU_PWR_CNTL0_ISO_CPU1 << (cpu - 1));
316280905Sganbold		AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL0_REG, value);
317280905Sganbold		AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL0_REG);
318280905Sganbold
319280905Sganbold		/*
320280905Sganbold		 * Release reset.
321280905Sganbold		 */
322280905Sganbold		value = CBUS_READ_4(AML_SOC_CPU_CLK_CNTL_REG);
323280905Sganbold		value &= ~(AML_M8_CPU_CLK_CNTL_RESET_CPU1 << (cpu - 1));
324280905Sganbold		CBUS_WRITE_4(AML_SOC_CPU_CLK_CNTL_REG, value);
325280905Sganbold		CBUS_BARRIER(AML_SOC_CPU_CLK_CNTL_REG);
326280905Sganbold
327280905Sganbold		if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
328280905Sganbold			/*
329280905Sganbold			 * The Amlogic Linux platform code sets the SCU power
330280905Sganbold			 * status for the CPU again for some reason so we
331280905Sganbold			 * follow suit (perhaps in case the reset caused
332280905Sganbold			 * a stale power status from the actual CPU to be
333280905Sganbold			 * written to the SCU).
334280905Sganbold			 */
335280905Sganbold			SCU_WRITE_4(AML_SCU_CPU_PWR_STATUS_REG, scpsr);
336280905Sganbold			SCU_BARRIER(AML_SCU_CPU_PWR_STATUS_REG);
337280905Sganbold		}
338280905Sganbold		break;
339280905Sganbold	default:
340280905Sganbold		break;
341280905Sganbold	}
342280905Sganbold}
343280905Sganbold
344280905Sganboldvoid
345280905Sganboldplatform_mp_setmaxid(void)
346280905Sganbold{
347280905Sganbold	int err;
348280905Sganbold	int i;
349280905Sganbold	int ncpu;
350280905Sganbold	phandle_t cpucfg_node;
351280905Sganbold	phandle_t scu_node;
352280905Sganbold	uint32_t value;
353280905Sganbold
354280905Sganbold	if (mp_ncpus != 0)
355280905Sganbold		return;
356280905Sganbold
357280905Sganbold	ncpu = 1;
358280905Sganbold
359280905Sganbold	/*
360280905Sganbold	 * Is the hardware necessary for SMP present?
361280905Sganbold	 */
362280905Sganbold
363280905Sganbold	if ((scu_node = find_node_for_device("scu", scu_compatible)) == 0)
364280905Sganbold		goto moveon;
365280905Sganbold
366280905Sganbold	if ((cpucfg_node = find_node_for_device("cpuconfig",
367280905Sganbold	    cpucfg_compatible)) == 0)
368280905Sganbold		goto moveon;
369280905Sganbold
370280905Sganbold	if (alloc_resource_for_node(scu_node, &aml8726_smp.scu_res,
371280905Sganbold	    &aml8726_smp.scu_size) != 0)
372280905Sganbold		panic("Could not allocate resource for SCU");
373280905Sganbold
374280905Sganbold	if (alloc_resource_for_node(cpucfg_node, &aml8726_smp.cpucfg_res,
375280905Sganbold	    &aml8726_smp.cpucfg_size) != 0)
376280905Sganbold		panic("Could not allocate resource for CPUCONFIG");
377280905Sganbold
378280905Sganbold
379280905Sganbold	/*
380280905Sganbold	 * Strictly speaking the aobus and cbus may not be required in
381280905Sganbold	 * order to start an AP (it depends on the processor), however
382280905Sganbold	 * always mapping them in simplifies the code.
383280905Sganbold	 */
384280905Sganbold
385280905Sganbold	aml8726_smp.aobus_res.r_bustag = fdtbus_bs_tag;
386280905Sganbold
387280905Sganbold	err = bus_space_map(aml8726_smp.aobus_res.r_bustag,
388280905Sganbold	    AML_SOC_AOBUS_BASE_ADDR, 0x100000,
389280905Sganbold	    0, &aml8726_smp.aobus_res.r_bushandle);
390280905Sganbold
391280905Sganbold	if (err)
392280905Sganbold		panic("Could not allocate resource for AOBUS");
393280905Sganbold
394280905Sganbold	aml8726_smp.cbus_res.r_bustag = fdtbus_bs_tag;
395280905Sganbold
396280905Sganbold	err = bus_space_map(aml8726_smp.cbus_res.r_bustag,
397280905Sganbold	    AML_SOC_CBUS_BASE_ADDR, 0x100000,
398280905Sganbold	    0, &aml8726_smp.cbus_res.r_bushandle);
399280905Sganbold
400280905Sganbold	if (err)
401280905Sganbold		panic("Could not allocate resource for CBUS");
402280905Sganbold
403280905Sganbold	aml8726_smp.errata_764369 = false;
404280905Sganbold	for (i = 0; scu_errata_764369[i]; i++)
405280905Sganbold		if (fdt_is_compatible_strict(scu_node, scu_errata_764369[i])) {
406280905Sganbold			aml8726_smp.errata_764369 = true;
407280905Sganbold			break;
408280905Sganbold		}
409280905Sganbold
410280905Sganbold	/*
411280905Sganbold	 * Read the number of CPUs present.
412280905Sganbold	 */
413280905Sganbold	value = SCU_READ_4(AML_SCU_CONFIG_REG);
414280905Sganbold	ncpu = (value & AML_SCU_CONFIG_NCPU_MASK) + 1;
415280905Sganbold
416280905Sganboldmoveon:
417280905Sganbold	mp_ncpus = ncpu;
418280905Sganbold	mp_maxid = ncpu - 1;
419280905Sganbold}
420280905Sganbold
421280905Sganboldvoid
422280905Sganboldplatform_mp_start_ap(void)
423280905Sganbold{
424280905Sganbold	int i;
425280905Sganbold	uint32_t reg;
426280905Sganbold	uint32_t value;
427280905Sganbold	vm_paddr_t paddr;
428280905Sganbold
429280905Sganbold	if (mp_ncpus < 2)
430280905Sganbold		return;
431280905Sganbold
432280905Sganbold	/*
433280905Sganbold	 * Invalidate SCU cache tags.  The 0x0000ffff constant invalidates
434280905Sganbold	 * all ways on all cores 0-3.  Per the ARM docs, it's harmless to
435280905Sganbold	 * write to the bits for cores that are not present.
436280905Sganbold	 */
437280905Sganbold	SCU_WRITE_4(AML_SCU_INV_TAGS_REG, 0x0000ffff);
438280905Sganbold
439280905Sganbold	if (aml8726_smp.errata_764369) {
440280905Sganbold		/*
441280905Sganbold		 * Erratum ARM/MP: 764369 (problems with cache maintenance).
442280905Sganbold		 * Setting the "disable-migratory bit" in the undocumented SCU
443280905Sganbold		 * Diagnostic Control Register helps work around the problem.
444280905Sganbold		 */
445280905Sganbold		value = SCU_READ_4(AML_SCU_DIAG_CONTROL_REG);
446280905Sganbold		value |= AML_SCU_DIAG_CONTROL_DISABLE_MIGBIT;
447280905Sganbold		SCU_WRITE_4(AML_SCU_DIAG_CONTROL_REG, value);
448280905Sganbold	}
449280905Sganbold
450280905Sganbold	/*
451280905Sganbold	 * Enable the SCU, then clean the cache on this core.  After these
452280905Sganbold	 * two operations the cache tag ram in the SCU is coherent with
453280905Sganbold	 * the contents of the cache on this core.  The other cores aren't
454280905Sganbold	 * running yet so their caches can't contain valid data yet, however
455280905Sganbold	 * we've initialized their SCU tag ram above, so they will be
456280905Sganbold	 * coherent from startup.
457280905Sganbold	 */
458280905Sganbold	value = SCU_READ_4(AML_SCU_CONTROL_REG);
459280905Sganbold	value |= AML_SCU_CONTROL_ENABLE;
460280905Sganbold	SCU_WRITE_4(AML_SCU_CONTROL_REG, value);
461280905Sganbold	SCU_BARRIER(AML_SCU_CONTROL_REG);
462295319Smmel	dcache_wbinv_poc_all();
463280905Sganbold
464280905Sganbold	/* Set the boot address and power on each AP. */
465280905Sganbold	paddr = pmap_kextract((vm_offset_t)mpentry);
466280905Sganbold	for (i = 1; i < mp_ncpus; i++) {
467280905Sganbold		reg = AML_CPUCONF_CPU1_ADDR_REG + ((i - 1) * 4);
468280905Sganbold		CPUCONF_WRITE_4(reg, paddr);
469280905Sganbold		CPUCONF_BARRIER(reg);
470280905Sganbold
471280905Sganbold		power_on_cpu(i);
472280905Sganbold	}
473280905Sganbold
474280905Sganbold	/*
475280905Sganbold	 * Enable the APs.
476280905Sganbold	 *
477280905Sganbold	 * The Amlogic Linux platform code sets the lsb for some reason
478280905Sganbold	 * in addition to the enable bit for each AP so we follow suit
479280905Sganbold	 * (the lsb may be the enable bit for the BP, though in that case
480280905Sganbold	 * it should already be set since it's currently running).
481280905Sganbold	 */
482280905Sganbold	value = CPUCONF_READ_4(AML_CPUCONF_CONTROL_REG);
483280905Sganbold	value |= 1;
484280905Sganbold	for (i = 1; i < mp_ncpus; i++)
485280905Sganbold		value |= (1 << i);
486280905Sganbold	CPUCONF_WRITE_4(AML_CPUCONF_CONTROL_REG, value);
487280905Sganbold	CPUCONF_BARRIER(AML_CPUCONF_CONTROL_REG);
488280905Sganbold
489280905Sganbold	/* Wakeup the now enabled APs */
490307344Smmel	dsb();
491307344Smmel	sev();
492280905Sganbold
493280905Sganbold	/*
494280905Sganbold	 * Free the resources which are not needed after startup.
495280905Sganbold	 */
496280905Sganbold	bus_space_unmap(aml8726_smp.scu_res.r_bustag,
497280905Sganbold	    aml8726_smp.scu_res.r_bushandle,
498280905Sganbold	    aml8726_smp.scu_size);
499280905Sganbold	bus_space_unmap(aml8726_smp.cpucfg_res.r_bustag,
500280905Sganbold	    aml8726_smp.cpucfg_res.r_bushandle,
501280905Sganbold	    aml8726_smp.cpucfg_size);
502280905Sganbold	bus_space_unmap(aml8726_smp.aobus_res.r_bustag,
503280905Sganbold	    aml8726_smp.aobus_res.r_bushandle,
504280905Sganbold	    0x100000);
505280905Sganbold	bus_space_unmap(aml8726_smp.cbus_res.r_bustag,
506280905Sganbold	    aml8726_smp.cbus_res.r_bushandle,
507280905Sganbold	    0x100000);
508280905Sganbold	memset(&aml8726_smp, 0, sizeof(aml8726_smp));
509280905Sganbold}
510280905Sganbold
511280905Sganbold/*
512280905Sganbold * Stub drivers for cosmetic purposes.
513280905Sganbold */
514280905Sganboldstruct aml8726_scu_softc {
515280905Sganbold	device_t	dev;
516280905Sganbold};
517280905Sganbold
518280905Sganboldstatic int
519280905Sganboldaml8726_scu_probe(device_t dev)
520280905Sganbold{
521280905Sganbold	int i;
522280905Sganbold
523280905Sganbold	for (i = 0; scu_compatible[i]; i++)
524280905Sganbold		if (ofw_bus_is_compatible(dev, scu_compatible[i]))
525280905Sganbold			break;
526280905Sganbold
527280905Sganbold	if (!scu_compatible[i])
528280905Sganbold		return (ENXIO);
529280905Sganbold
530280905Sganbold	device_set_desc(dev, "ARM Snoop Control Unit");
531280905Sganbold
532280905Sganbold	return (BUS_PROBE_DEFAULT);
533280905Sganbold}
534280905Sganbold
535280905Sganboldstatic int
536280905Sganboldaml8726_scu_attach(device_t dev)
537280905Sganbold{
538280905Sganbold	struct aml8726_scu_softc *sc = device_get_softc(dev);
539280905Sganbold
540280905Sganbold	sc->dev = dev;
541280905Sganbold
542280905Sganbold	return (0);
543280905Sganbold}
544280905Sganbold
545280905Sganboldstatic int
546280905Sganboldaml8726_scu_detach(device_t dev)
547280905Sganbold{
548280905Sganbold
549280905Sganbold	return (0);
550280905Sganbold}
551280905Sganbold
552280905Sganboldstatic device_method_t aml8726_scu_methods[] = {
553280905Sganbold	/* Device interface */
554280905Sganbold	DEVMETHOD(device_probe,		aml8726_scu_probe),
555280905Sganbold	DEVMETHOD(device_attach,	aml8726_scu_attach),
556280905Sganbold	DEVMETHOD(device_detach,	aml8726_scu_detach),
557280905Sganbold
558280905Sganbold	DEVMETHOD_END
559280905Sganbold};
560280905Sganbold
561280905Sganboldstatic driver_t aml8726_scu_driver = {
562280905Sganbold	"scu",
563280905Sganbold	aml8726_scu_methods,
564280905Sganbold	sizeof(struct aml8726_scu_softc),
565280905Sganbold};
566280905Sganbold
567280905Sganboldstatic devclass_t aml8726_scu_devclass;
568280905Sganbold
569280905SganboldEARLY_DRIVER_MODULE(scu, simplebus, aml8726_scu_driver, aml8726_scu_devclass,
570280905Sganbold    0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE);
571280905Sganbold
572280905Sganboldstruct aml8726_cpucfg_softc {
573280905Sganbold	device_t	dev;
574280905Sganbold};
575280905Sganbold
576280905Sganboldstatic int
577280905Sganboldaml8726_cpucfg_probe(device_t dev)
578280905Sganbold{
579280905Sganbold	int i;
580280905Sganbold
581280905Sganbold	for (i = 0; cpucfg_compatible[i]; i++)
582280905Sganbold		if (ofw_bus_is_compatible(dev, cpucfg_compatible[i]))
583280905Sganbold			break;
584280905Sganbold
585280905Sganbold	if (!cpucfg_compatible[i])
586280905Sganbold		return (ENXIO);
587280905Sganbold
588280905Sganbold	device_set_desc(dev, "Amlogic CPU Config");
589280905Sganbold
590280905Sganbold	return (BUS_PROBE_DEFAULT);
591280905Sganbold}
592280905Sganbold
593280905Sganboldstatic int
594280905Sganboldaml8726_cpucfg_attach(device_t dev)
595280905Sganbold{
596280905Sganbold	struct aml8726_cpucfg_softc *sc = device_get_softc(dev);
597280905Sganbold
598280905Sganbold	sc->dev = dev;
599280905Sganbold
600280905Sganbold	return (0);
601280905Sganbold}
602280905Sganbold
603280905Sganboldstatic int
604280905Sganboldaml8726_cpucfg_detach(device_t dev)
605280905Sganbold{
606280905Sganbold
607280905Sganbold	return (0);
608280905Sganbold}
609280905Sganbold
610280905Sganboldstatic device_method_t aml8726_cpucfg_methods[] = {
611280905Sganbold	/* Device interface */
612280905Sganbold	DEVMETHOD(device_probe,		aml8726_cpucfg_probe),
613280905Sganbold	DEVMETHOD(device_attach,	aml8726_cpucfg_attach),
614280905Sganbold	DEVMETHOD(device_detach,	aml8726_cpucfg_detach),
615280905Sganbold
616280905Sganbold	DEVMETHOD_END
617280905Sganbold};
618280905Sganbold
619280905Sganboldstatic driver_t aml8726_cpucfg_driver = {
620280905Sganbold	"cpuconfig",
621280905Sganbold	aml8726_cpucfg_methods,
622280905Sganbold	sizeof(struct aml8726_cpucfg_softc),
623280905Sganbold};
624280905Sganbold
625280905Sganboldstatic devclass_t aml8726_cpucfg_devclass;
626280905Sganbold
627280905SganboldEARLY_DRIVER_MODULE(cpuconfig, simplebus, aml8726_cpucfg_driver,
628280905Sganbold    aml8726_cpucfg_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE);
629