aml8726_mp.c revision 281092
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: head/sys/arm/amlogic/aml8726/aml8726_mp.c 281092 2015-04-04 23:03:11Z andrew $");
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
56280905Sganbold#include <machine/bus.h>
57280905Sganbold#include <machine/smp.h>
58280905Sganbold#include <machine/fdt.h>
59280905Sganbold#include <machine/intr.h>
60280905Sganbold
61280905Sganbold#include <dev/fdt/fdt_common.h>
62280905Sganbold#include <dev/ofw/ofw_bus.h>
63280905Sganbold#include <dev/ofw/ofw_bus_subr.h>
64280905Sganbold
65280905Sganbold#include <arm/amlogic/aml8726/aml8726_soc.h>
66280905Sganbold
67280905Sganboldstatic const char *scu_compatible[] = {
68280905Sganbold	"arm,cortex-a5-scu",
69280905Sganbold	"arm,cortex-a9-scu",
70280905Sganbold	NULL
71280905Sganbold};
72280905Sganbold
73280905Sganboldstatic const char *scu_errata_764369[] = {
74280905Sganbold	"arm,cortex-a9-scu",
75280905Sganbold	NULL
76280905Sganbold};
77280905Sganbold
78280905Sganboldstatic const char *cpucfg_compatible[] = {
79280905Sganbold	"amlogic,aml8726-cpuconfig",
80280905Sganbold	NULL
81280905Sganbold};
82280905Sganbold
83280905Sganboldstatic struct {
84280905Sganbold	boolean_t errata_764369;
85280905Sganbold	u_long scu_size;
86280905Sganbold	struct resource scu_res;
87280905Sganbold	u_long cpucfg_size;
88280905Sganbold	struct resource cpucfg_res;
89280905Sganbold	struct resource aobus_res;
90280905Sganbold	struct resource cbus_res;
91280905Sganbold} aml8726_smp;
92280905Sganbold
93280905Sganbold#define	AML_SCU_CONTROL_REG			0
94280905Sganbold#define	AML_SCU_CONTROL_ENABLE			1
95280905Sganbold#define	AML_SCU_CONFIG_REG			4
96280905Sganbold#define	AML_SCU_CONFIG_NCPU_MASK		0x3
97280905Sganbold#define	AML_SCU_CPU_PWR_STATUS_REG		8
98280905Sganbold#define	AML_SCU_CPU_PWR_STATUS_CPU3_MASK	(3 << 24)
99280905Sganbold#define	AML_SCU_CPU_PWR_STATUS_CPU2_MASK	(3 << 16)
100280905Sganbold#define	AML_SCU_CPU_PWR_STATUS_CPU1_MASK	(3 << 8)
101280905Sganbold#define	AML_SCU_CPU_PWR_STATUS_CPU0_MASK	3
102280905Sganbold#define	AML_SCU_INV_TAGS_REG			12
103280905Sganbold#define	AML_SCU_DIAG_CONTROL_REG		48
104280905Sganbold#define	AML_SCU_DIAG_CONTROL_DISABLE_MIGBIT	1
105280905Sganbold
106280905Sganbold#define	AML_CPUCONF_CONTROL_REG			0
107280905Sganbold#define	AML_CPUCONF_CPU1_ADDR_REG		4
108280905Sganbold#define	AML_CPUCONF_CPU2_ADDR_REG		8
109280905Sganbold#define	AML_CPUCONF_CPU3_ADDR_REG		12
110280905Sganbold
111280905Sganbold/* aobus */
112280905Sganbold
113280905Sganbold#define	AML_M8_CPU_PWR_CNTL0_REG		0xe0
114280905Sganbold#define	AML_M8B_CPU_PWR_CNTL0_MODE_CPU3_MASK	(3 << 22)
115280905Sganbold#define	AML_M8B_CPU_PWR_CNTL0_MODE_CPU2_MASK	(3 << 20)
116280905Sganbold#define	AML_M8B_CPU_PWR_CNTL0_MODE_CPU1_MASK	(3 << 18)
117280905Sganbold
118280905Sganbold#define	AML_M8_CPU_PWR_CNTL0_ISO_CPU3		(1 << 3)
119280905Sganbold#define	AML_M8_CPU_PWR_CNTL0_ISO_CPU2		(1 << 2)
120280905Sganbold#define	AML_M8_CPU_PWR_CNTL0_ISO_CPU1		(1 << 1)
121280905Sganbold
122280905Sganbold#define	AML_M8_CPU_PWR_CNTL1_REG		0xe4
123280905Sganbold#define	AML_M8B_CPU_PWR_CNTL1_PWR_CPU3		(1 << 19)
124280905Sganbold#define	AML_M8B_CPU_PWR_CNTL1_PWR_CPU2		(1 << 18)
125280905Sganbold#define	AML_M8B_CPU_PWR_CNTL1_PWR_CPU1		(1 << 17)
126280905Sganbold
127280905Sganbold#define	AML_M8_CPU_PWR_CNTL1_MODE_CPU3_MASK	(3 << 8)
128280905Sganbold#define	AML_M8_CPU_PWR_CNTL1_MODE_CPU2_MASK	(3 << 6)
129280905Sganbold#define	AML_M8_CPU_PWR_CNTL1_MODE_CPU1_MASK	(3 << 4)
130280905Sganbold
131280905Sganbold#define	AML_M8B_CPU_PWR_MEM_PD0_REG		0xf4
132280905Sganbold#define	AML_M8B_CPU_PWR_MEM_PD0_CPU3		(0xf << 20)
133280905Sganbold#define	AML_M8B_CPU_PWR_MEM_PD0_CPU2		(0xf << 24)
134280905Sganbold#define	AML_M8B_CPU_PWR_MEM_PD0_CPU1		(0xf << 28)
135280905Sganbold
136280905Sganbold/* cbus */
137280905Sganbold
138280905Sganbold#define	AML_SOC_CPU_CLK_CNTL_REG		0x419c
139280905Sganbold#define	AML_M8_CPU_CLK_CNTL_RESET_CPU3		(1 << 27)
140280905Sganbold#define	AML_M8_CPU_CLK_CNTL_RESET_CPU2		(1 << 26)
141280905Sganbold#define	AML_M8_CPU_CLK_CNTL_RESET_CPU1		(1 << 25)
142280905Sganbold
143280905Sganbold#define	SCU_WRITE_4(reg, value)		bus_write_4(&aml8726_smp.scu_res,    \
144280905Sganbold    (reg), (value))
145280905Sganbold#define	SCU_READ_4(reg)			bus_read_4(&aml8726_smp.scu_res, (reg))
146280905Sganbold#define	SCU_BARRIER(reg)		bus_barrier(&aml8726_smp.scu_res,    \
147280905Sganbold    (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
148280905Sganbold
149280905Sganbold#define	CPUCONF_WRITE_4(reg, value)	bus_write_4(&aml8726_smp.cpucfg_res, \
150280905Sganbold    (reg), (value))
151280905Sganbold#define	CPUCONF_READ_4(reg)		bus_read_4(&aml8726_smp.cpucfg_res,  \
152280905Sganbold    (reg))
153280905Sganbold#define	CPUCONF_BARRIER(reg)		bus_barrier(&aml8726_smp.cpucfg_res, \
154280905Sganbold    (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
155280905Sganbold
156280905Sganbold#define	AOBUS_WRITE_4(reg, value)	bus_write_4(&aml8726_smp.aobus_res,  \
157280905Sganbold    (reg), (value))
158280905Sganbold#define	AOBUS_READ_4(reg)		bus_read_4(&aml8726_smp.aobus_res,   \
159280905Sganbold    (reg))
160280905Sganbold#define	AOBUS_BARRIER(reg)		bus_barrier(&aml8726_smp.aobus_res,  \
161280905Sganbold    (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
162280905Sganbold
163280905Sganbold#define	CBUS_WRITE_4(reg, value)	bus_write_4(&aml8726_smp.cbus_res,   \
164280905Sganbold    (reg), (value))
165280905Sganbold#define CBUS_READ_4(reg)		bus_read_4(&aml8726_smp.cbus_res,    \
166280905Sganbold    (reg))
167280905Sganbold#define	CBUS_BARRIER(reg)		bus_barrier(&aml8726_smp.cbus_res,   \
168280905Sganbold    (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
169280905Sganbold
170280905Sganboldstatic phandle_t
171280905Sganboldfind_node_for_device(const char *device, const char **compatible)
172280905Sganbold{
173280905Sganbold	int i;
174280905Sganbold	phandle_t node;
175280905Sganbold
176280905Sganbold	/*
177280905Sganbold	 * Try to access the node directly i.e. through /aliases/.
178280905Sganbold	 */
179280905Sganbold
180280905Sganbold	if ((node = OF_finddevice(device)) != 0)
181280905Sganbold		for (i = 0; compatible[i]; i++)
182280905Sganbold			if (fdt_is_compatible_strict(node, compatible[i]))
183280905Sganbold				return node;
184280905Sganbold
185280905Sganbold	/*
186280905Sganbold	 * Find the node the long way.
187280905Sganbold	 */
188280905Sganbold
189280905Sganbold	for (i = 0; compatible[i]; i++) {
190280905Sganbold		if ((node = OF_finddevice("/soc")) == 0)
191280905Sganbold			return (0);
192280905Sganbold
193280905Sganbold		if ((node = fdt_find_compatible(node, compatible[i], 1)) != 0)
194280905Sganbold			return node;
195280905Sganbold	}
196280905Sganbold
197280905Sganbold	return (0);
198280905Sganbold}
199280905Sganbold
200280905Sganbold
201280905Sganboldstatic int
202280905Sganboldalloc_resource_for_node(phandle_t node, struct resource *res, u_long *size)
203280905Sganbold{
204280905Sganbold	int err;
205280905Sganbold	u_long pbase, psize;
206280905Sganbold	u_long start;
207280905Sganbold
208280905Sganbold	if ((err = fdt_get_range(OF_parent(node), 0, &pbase, &psize)) != 0 ||
209280905Sganbold	    (err = fdt_regsize(node, &start, size)) != 0)
210280905Sganbold		return (err);
211280905Sganbold
212280905Sganbold	start += pbase;
213280905Sganbold
214280905Sganbold	memset(res, 0, sizeof(*res));
215280905Sganbold
216280905Sganbold	res->r_bustag = fdtbus_bs_tag;
217280905Sganbold
218280905Sganbold	err = bus_space_map(res->r_bustag, start, *size, 0, &res->r_bushandle);
219280905Sganbold
220280905Sganbold	return (err);
221280905Sganbold}
222280905Sganbold
223280905Sganbold
224280905Sganboldstatic void
225280905Sganboldpower_on_cpu(int cpu)
226280905Sganbold{
227280905Sganbold	uint32_t scpsr;
228280905Sganbold	uint32_t value;
229280905Sganbold
230280905Sganbold	if (cpu <= 0)
231280905Sganbold		return;
232280905Sganbold
233280905Sganbold	/*
234280905Sganbold	 * Power on the CPU if the intricate details are known, otherwise
235280905Sganbold	 * just hope for the best (it may have already be powered on by
236280905Sganbold	 * the hardware / firmware).
237280905Sganbold	 */
238280905Sganbold
239280905Sganbold	switch (aml8726_soc_hw_rev) {
240280905Sganbold	case AML_SOC_HW_REV_M8:
241280905Sganbold	case AML_SOC_HW_REV_M8B:
242280905Sganbold		/*
243280905Sganbold		 * Set the SCU power status for the CPU to normal mode.
244280905Sganbold		 */
245280905Sganbold		scpsr = SCU_READ_4(AML_SCU_CPU_PWR_STATUS_REG);
246280905Sganbold		scpsr &= ~(AML_SCU_CPU_PWR_STATUS_CPU1_MASK << ((cpu - 1) * 8));
247280905Sganbold		SCU_WRITE_4(AML_SCU_CPU_PWR_STATUS_REG, scpsr);
248280905Sganbold		SCU_BARRIER(AML_SCU_CPU_PWR_STATUS_REG);
249280905Sganbold
250280905Sganbold		if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
251280905Sganbold			/*
252280905Sganbold			 * Reset may cause the current power status from the
253280905Sganbold			 * actual CPU to be written to the SCU (over-writing
254280905Sganbold			 * the value  we've just written) so set it to normal
255280905Sganbold			 * mode as well.
256280905Sganbold			 */
257280905Sganbold			 value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL0_REG);
258280905Sganbold			 value &= ~(AML_M8B_CPU_PWR_CNTL0_MODE_CPU1_MASK <<
259280905Sganbold			    ((cpu - 1) * 2));
260280905Sganbold			 AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL0_REG, value);
261280905Sganbold			 AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL0_REG);
262280905Sganbold		 }
263280905Sganbold
264280905Sganbold		DELAY(5);
265280905Sganbold
266280905Sganbold		/*
267280905Sganbold		 * Assert reset.
268280905Sganbold		 */
269280905Sganbold		value = CBUS_READ_4(AML_SOC_CPU_CLK_CNTL_REG);
270280905Sganbold		value |= AML_M8_CPU_CLK_CNTL_RESET_CPU1 << (cpu - 1);
271280905Sganbold		CBUS_WRITE_4(AML_SOC_CPU_CLK_CNTL_REG, value);
272280905Sganbold		CBUS_BARRIER(AML_SOC_CPU_CLK_CNTL_REG);
273280905Sganbold
274280905Sganbold		if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
275280905Sganbold			/*
276280905Sganbold			 * Release RAM pull-down.
277280905Sganbold			 */
278280905Sganbold			 value = AOBUS_READ_4(AML_M8B_CPU_PWR_MEM_PD0_REG);
279280905Sganbold			 value &= ~((uint32_t)AML_M8B_CPU_PWR_MEM_PD0_CPU1 >>
280280905Sganbold			    ((cpu - 1) * 4));
281280905Sganbold			 AOBUS_WRITE_4(AML_M8B_CPU_PWR_MEM_PD0_REG, value);
282280905Sganbold			 AOBUS_BARRIER(AML_M8B_CPU_PWR_MEM_PD0_REG);
283280905Sganbold		 }
284280905Sganbold
285280905Sganbold		/*
286280905Sganbold		 * Power on CPU.
287280905Sganbold		 */
288280905Sganbold		value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL1_REG);
289280905Sganbold		value &= ~(AML_M8_CPU_PWR_CNTL1_MODE_CPU1_MASK <<
290280905Sganbold		    ((cpu - 1) * 2));
291280905Sganbold		AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL1_REG, value);
292280905Sganbold		AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL1_REG);
293280905Sganbold
294280905Sganbold		DELAY(10);
295280905Sganbold
296280905Sganbold		if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
297280905Sganbold			/*
298280905Sganbold			 * Wait for power on confirmation.
299280905Sganbold			 */
300280905Sganbold			for ( ; ; ) {
301280905Sganbold				value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL1_REG);
302280905Sganbold				value &= AML_M8B_CPU_PWR_CNTL1_PWR_CPU1 <<
303280905Sganbold				    (cpu - 1);
304280905Sganbold				if (value)
305280905Sganbold					break;
306280905Sganbold				DELAY(10);
307280905Sganbold			}
308280905Sganbold		}
309280905Sganbold
310280905Sganbold		/*
311280905Sganbold		 * Release peripheral clamp.
312280905Sganbold		 */
313280905Sganbold		value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL0_REG);
314280905Sganbold		value &= ~(AML_M8_CPU_PWR_CNTL0_ISO_CPU1 << (cpu - 1));
315280905Sganbold		AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL0_REG, value);
316280905Sganbold		AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL0_REG);
317280905Sganbold
318280905Sganbold		/*
319280905Sganbold		 * Release reset.
320280905Sganbold		 */
321280905Sganbold		value = CBUS_READ_4(AML_SOC_CPU_CLK_CNTL_REG);
322280905Sganbold		value &= ~(AML_M8_CPU_CLK_CNTL_RESET_CPU1 << (cpu - 1));
323280905Sganbold		CBUS_WRITE_4(AML_SOC_CPU_CLK_CNTL_REG, value);
324280905Sganbold		CBUS_BARRIER(AML_SOC_CPU_CLK_CNTL_REG);
325280905Sganbold
326280905Sganbold		if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
327280905Sganbold			/*
328280905Sganbold			 * The Amlogic Linux platform code sets the SCU power
329280905Sganbold			 * status for the CPU again for some reason so we
330280905Sganbold			 * follow suit (perhaps in case the reset caused
331280905Sganbold			 * a stale power status from the actual CPU to be
332280905Sganbold			 * written to the SCU).
333280905Sganbold			 */
334280905Sganbold			SCU_WRITE_4(AML_SCU_CPU_PWR_STATUS_REG, scpsr);
335280905Sganbold			SCU_BARRIER(AML_SCU_CPU_PWR_STATUS_REG);
336280905Sganbold		}
337280905Sganbold		break;
338280905Sganbold	default:
339280905Sganbold		break;
340280905Sganbold	}
341280905Sganbold}
342280905Sganbold
343280905Sganbold
344280905Sganboldvoid
345280905Sganboldplatform_mp_init_secondary(void)
346280905Sganbold{
347280905Sganbold
348280905Sganbold	/*
349280905Sganbold	 * Consider modifying the timer driver to support
350280905Sganbold	 * per-cpu timers and then enabling the timer for
351280905Sganbold	 * each AP.
352280905Sganbold	 */
353280905Sganbold
354280905Sganbold	 arm_init_secondary_ic();
355280905Sganbold}
356280905Sganbold
357280905Sganbold
358280905Sganboldvoid
359280905Sganboldplatform_mp_setmaxid(void)
360280905Sganbold{
361280905Sganbold	int err;
362280905Sganbold	int i;
363280905Sganbold	int ncpu;
364280905Sganbold	phandle_t cpucfg_node;
365280905Sganbold	phandle_t scu_node;
366280905Sganbold	uint32_t value;
367280905Sganbold
368280905Sganbold	if (mp_ncpus != 0)
369280905Sganbold		return;
370280905Sganbold
371280905Sganbold	ncpu = 1;
372280905Sganbold
373280905Sganbold	/*
374280905Sganbold	 * Is the hardware necessary for SMP present?
375280905Sganbold	 */
376280905Sganbold
377280905Sganbold	if ((scu_node = find_node_for_device("scu", scu_compatible)) == 0)
378280905Sganbold		goto moveon;
379280905Sganbold
380280905Sganbold	if ((cpucfg_node = find_node_for_device("cpuconfig",
381280905Sganbold	    cpucfg_compatible)) == 0)
382280905Sganbold		goto moveon;
383280905Sganbold
384280905Sganbold	if (alloc_resource_for_node(scu_node, &aml8726_smp.scu_res,
385280905Sganbold	    &aml8726_smp.scu_size) != 0)
386280905Sganbold		panic("Could not allocate resource for SCU");
387280905Sganbold
388280905Sganbold	if (alloc_resource_for_node(cpucfg_node, &aml8726_smp.cpucfg_res,
389280905Sganbold	    &aml8726_smp.cpucfg_size) != 0)
390280905Sganbold		panic("Could not allocate resource for CPUCONFIG");
391280905Sganbold
392280905Sganbold
393280905Sganbold	/*
394280905Sganbold	 * Strictly speaking the aobus and cbus may not be required in
395280905Sganbold	 * order to start an AP (it depends on the processor), however
396280905Sganbold	 * always mapping them in simplifies the code.
397280905Sganbold	 */
398280905Sganbold
399280905Sganbold	aml8726_smp.aobus_res.r_bustag = fdtbus_bs_tag;
400280905Sganbold
401280905Sganbold	err = bus_space_map(aml8726_smp.aobus_res.r_bustag,
402280905Sganbold	    AML_SOC_AOBUS_BASE_ADDR, 0x100000,
403280905Sganbold	    0, &aml8726_smp.aobus_res.r_bushandle);
404280905Sganbold
405280905Sganbold	if (err)
406280905Sganbold		panic("Could not allocate resource for AOBUS");
407280905Sganbold
408280905Sganbold	aml8726_smp.cbus_res.r_bustag = fdtbus_bs_tag;
409280905Sganbold
410280905Sganbold	err = bus_space_map(aml8726_smp.cbus_res.r_bustag,
411280905Sganbold	    AML_SOC_CBUS_BASE_ADDR, 0x100000,
412280905Sganbold	    0, &aml8726_smp.cbus_res.r_bushandle);
413280905Sganbold
414280905Sganbold	if (err)
415280905Sganbold		panic("Could not allocate resource for CBUS");
416280905Sganbold
417280905Sganbold	aml8726_smp.errata_764369 = false;
418280905Sganbold	for (i = 0; scu_errata_764369[i]; i++)
419280905Sganbold		if (fdt_is_compatible_strict(scu_node, scu_errata_764369[i])) {
420280905Sganbold			aml8726_smp.errata_764369 = true;
421280905Sganbold			break;
422280905Sganbold		}
423280905Sganbold
424280905Sganbold	/*
425280905Sganbold	 * Read the number of CPUs present.
426280905Sganbold	 */
427280905Sganbold	value = SCU_READ_4(AML_SCU_CONFIG_REG);
428280905Sganbold	ncpu = (value & AML_SCU_CONFIG_NCPU_MASK) + 1;
429280905Sganbold
430280905Sganboldmoveon:
431280905Sganbold	mp_ncpus = ncpu;
432280905Sganbold	mp_maxid = ncpu - 1;
433280905Sganbold}
434280905Sganbold
435280905Sganbold
436280905Sganboldint
437280905Sganboldplatform_mp_probe(void)
438280905Sganbold{
439280905Sganbold
440280905Sganbold	if (mp_ncpus == 0)
441280905Sganbold		platform_mp_setmaxid();
442280905Sganbold
443280905Sganbold	return (mp_ncpus > 1);
444280905Sganbold}
445280905Sganbold
446280905Sganbold
447280905Sganboldvoid
448280905Sganboldplatform_mp_start_ap(void)
449280905Sganbold{
450280905Sganbold	int i;
451280905Sganbold	uint32_t reg;
452280905Sganbold	uint32_t value;
453280905Sganbold	vm_paddr_t paddr;
454280905Sganbold
455280905Sganbold	if (mp_ncpus < 2)
456280905Sganbold		return;
457280905Sganbold
458280905Sganbold	/*
459280905Sganbold	 * Invalidate SCU cache tags.  The 0x0000ffff constant invalidates
460280905Sganbold	 * all ways on all cores 0-3.  Per the ARM docs, it's harmless to
461280905Sganbold	 * write to the bits for cores that are not present.
462280905Sganbold	 */
463280905Sganbold	SCU_WRITE_4(AML_SCU_INV_TAGS_REG, 0x0000ffff);
464280905Sganbold
465280905Sganbold	if (aml8726_smp.errata_764369) {
466280905Sganbold		/*
467280905Sganbold		 * Erratum ARM/MP: 764369 (problems with cache maintenance).
468280905Sganbold		 * Setting the "disable-migratory bit" in the undocumented SCU
469280905Sganbold		 * Diagnostic Control Register helps work around the problem.
470280905Sganbold		 */
471280905Sganbold		value = SCU_READ_4(AML_SCU_DIAG_CONTROL_REG);
472280905Sganbold		value |= AML_SCU_DIAG_CONTROL_DISABLE_MIGBIT;
473280905Sganbold		SCU_WRITE_4(AML_SCU_DIAG_CONTROL_REG, value);
474280905Sganbold	}
475280905Sganbold
476280905Sganbold	/*
477280905Sganbold	 * Enable the SCU, then clean the cache on this core.  After these
478280905Sganbold	 * two operations the cache tag ram in the SCU is coherent with
479280905Sganbold	 * the contents of the cache on this core.  The other cores aren't
480280905Sganbold	 * running yet so their caches can't contain valid data yet, however
481280905Sganbold	 * we've initialized their SCU tag ram above, so they will be
482280905Sganbold	 * coherent from startup.
483280905Sganbold	 */
484280905Sganbold	value = SCU_READ_4(AML_SCU_CONTROL_REG);
485280905Sganbold	value |= AML_SCU_CONTROL_ENABLE;
486280905Sganbold	SCU_WRITE_4(AML_SCU_CONTROL_REG, value);
487280905Sganbold	SCU_BARRIER(AML_SCU_CONTROL_REG);
488280905Sganbold	cpu_idcache_wbinv_all();
489280905Sganbold
490280905Sganbold	/* Set the boot address and power on each AP. */
491280905Sganbold	paddr = pmap_kextract((vm_offset_t)mpentry);
492280905Sganbold	for (i = 1; i < mp_ncpus; i++) {
493280905Sganbold		reg = AML_CPUCONF_CPU1_ADDR_REG + ((i - 1) * 4);
494280905Sganbold		CPUCONF_WRITE_4(reg, paddr);
495280905Sganbold		CPUCONF_BARRIER(reg);
496280905Sganbold
497280905Sganbold		power_on_cpu(i);
498280905Sganbold	}
499280905Sganbold
500280905Sganbold	/*
501280905Sganbold	 * Enable the APs.
502280905Sganbold	 *
503280905Sganbold	 * The Amlogic Linux platform code sets the lsb for some reason
504280905Sganbold	 * in addition to the enable bit for each AP so we follow suit
505280905Sganbold	 * (the lsb may be the enable bit for the BP, though in that case
506280905Sganbold	 * it should already be set since it's currently running).
507280905Sganbold	 */
508280905Sganbold	value = CPUCONF_READ_4(AML_CPUCONF_CONTROL_REG);
509280905Sganbold	value |= 1;
510280905Sganbold	for (i = 1; i < mp_ncpus; i++)
511280905Sganbold		value |= (1 << i);
512280905Sganbold	CPUCONF_WRITE_4(AML_CPUCONF_CONTROL_REG, value);
513280905Sganbold	CPUCONF_BARRIER(AML_CPUCONF_CONTROL_REG);
514280905Sganbold
515280905Sganbold	/* Wakeup the now enabled APs */
516280905Sganbold	armv7_sev();
517280905Sganbold
518280905Sganbold	/*
519280905Sganbold	 * Free the resources which are not needed after startup.
520280905Sganbold	 */
521280905Sganbold	bus_space_unmap(aml8726_smp.scu_res.r_bustag,
522280905Sganbold	    aml8726_smp.scu_res.r_bushandle,
523280905Sganbold	    aml8726_smp.scu_size);
524280905Sganbold	bus_space_unmap(aml8726_smp.cpucfg_res.r_bustag,
525280905Sganbold	    aml8726_smp.cpucfg_res.r_bushandle,
526280905Sganbold	    aml8726_smp.cpucfg_size);
527280905Sganbold	bus_space_unmap(aml8726_smp.aobus_res.r_bustag,
528280905Sganbold	    aml8726_smp.aobus_res.r_bushandle,
529280905Sganbold	    0x100000);
530280905Sganbold	bus_space_unmap(aml8726_smp.cbus_res.r_bustag,
531280905Sganbold	    aml8726_smp.cbus_res.r_bushandle,
532280905Sganbold	    0x100000);
533280905Sganbold	memset(&aml8726_smp, 0, sizeof(aml8726_smp));
534280905Sganbold}
535280905Sganbold
536280905Sganboldvoid
537280905Sganboldplatform_ipi_send(cpuset_t cpus, u_int ipi)
538280905Sganbold{
539280905Sganbold
540280905Sganbold	pic_ipi_send(cpus, ipi);
541280905Sganbold}
542280905Sganbold
543280905Sganbold/*
544280905Sganbold * Stub drivers for cosmetic purposes.
545280905Sganbold */
546280905Sganboldstruct aml8726_scu_softc {
547280905Sganbold	device_t	dev;
548280905Sganbold};
549280905Sganbold
550280905Sganboldstatic int
551280905Sganboldaml8726_scu_probe(device_t dev)
552280905Sganbold{
553280905Sganbold	int i;
554280905Sganbold
555280905Sganbold	for (i = 0; scu_compatible[i]; i++)
556280905Sganbold		if (ofw_bus_is_compatible(dev, scu_compatible[i]))
557280905Sganbold			break;
558280905Sganbold
559280905Sganbold	if (!scu_compatible[i])
560280905Sganbold		return (ENXIO);
561280905Sganbold
562280905Sganbold	device_set_desc(dev, "ARM Snoop Control Unit");
563280905Sganbold
564280905Sganbold	return (BUS_PROBE_DEFAULT);
565280905Sganbold}
566280905Sganbold
567280905Sganboldstatic int
568280905Sganboldaml8726_scu_attach(device_t dev)
569280905Sganbold{
570280905Sganbold	struct aml8726_scu_softc *sc = device_get_softc(dev);
571280905Sganbold
572280905Sganbold	sc->dev = dev;
573280905Sganbold
574280905Sganbold	return (0);
575280905Sganbold}
576280905Sganbold
577280905Sganboldstatic int
578280905Sganboldaml8726_scu_detach(device_t dev)
579280905Sganbold{
580280905Sganbold
581280905Sganbold	return (0);
582280905Sganbold}
583280905Sganbold
584280905Sganboldstatic device_method_t aml8726_scu_methods[] = {
585280905Sganbold	/* Device interface */
586280905Sganbold	DEVMETHOD(device_probe,		aml8726_scu_probe),
587280905Sganbold	DEVMETHOD(device_attach,	aml8726_scu_attach),
588280905Sganbold	DEVMETHOD(device_detach,	aml8726_scu_detach),
589280905Sganbold
590280905Sganbold	DEVMETHOD_END
591280905Sganbold};
592280905Sganbold
593280905Sganboldstatic driver_t aml8726_scu_driver = {
594280905Sganbold	"scu",
595280905Sganbold	aml8726_scu_methods,
596280905Sganbold	sizeof(struct aml8726_scu_softc),
597280905Sganbold};
598280905Sganbold
599280905Sganboldstatic devclass_t aml8726_scu_devclass;
600280905Sganbold
601280905SganboldEARLY_DRIVER_MODULE(scu, simplebus, aml8726_scu_driver, aml8726_scu_devclass,
602280905Sganbold    0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE);
603280905Sganbold
604280905Sganboldstruct aml8726_cpucfg_softc {
605280905Sganbold	device_t	dev;
606280905Sganbold};
607280905Sganbold
608280905Sganboldstatic int
609280905Sganboldaml8726_cpucfg_probe(device_t dev)
610280905Sganbold{
611280905Sganbold	int i;
612280905Sganbold
613280905Sganbold	for (i = 0; cpucfg_compatible[i]; i++)
614280905Sganbold		if (ofw_bus_is_compatible(dev, cpucfg_compatible[i]))
615280905Sganbold			break;
616280905Sganbold
617280905Sganbold	if (!cpucfg_compatible[i])
618280905Sganbold		return (ENXIO);
619280905Sganbold
620280905Sganbold	device_set_desc(dev, "Amlogic CPU Config");
621280905Sganbold
622280905Sganbold	return (BUS_PROBE_DEFAULT);
623280905Sganbold}
624280905Sganbold
625280905Sganboldstatic int
626280905Sganboldaml8726_cpucfg_attach(device_t dev)
627280905Sganbold{
628280905Sganbold	struct aml8726_cpucfg_softc *sc = device_get_softc(dev);
629280905Sganbold
630280905Sganbold	sc->dev = dev;
631280905Sganbold
632280905Sganbold	return (0);
633280905Sganbold}
634280905Sganbold
635280905Sganboldstatic int
636280905Sganboldaml8726_cpucfg_detach(device_t dev)
637280905Sganbold{
638280905Sganbold
639280905Sganbold	return (0);
640280905Sganbold}
641280905Sganbold
642280905Sganboldstatic device_method_t aml8726_cpucfg_methods[] = {
643280905Sganbold	/* Device interface */
644280905Sganbold	DEVMETHOD(device_probe,		aml8726_cpucfg_probe),
645280905Sganbold	DEVMETHOD(device_attach,	aml8726_cpucfg_attach),
646280905Sganbold	DEVMETHOD(device_detach,	aml8726_cpucfg_detach),
647280905Sganbold
648280905Sganbold	DEVMETHOD_END
649280905Sganbold};
650280905Sganbold
651280905Sganboldstatic driver_t aml8726_cpucfg_driver = {
652280905Sganbold	"cpuconfig",
653280905Sganbold	aml8726_cpucfg_methods,
654280905Sganbold	sizeof(struct aml8726_cpucfg_softc),
655280905Sganbold};
656280905Sganbold
657280905Sganboldstatic devclass_t aml8726_cpucfg_devclass;
658280905Sganbold
659280905SganboldEARLY_DRIVER_MODULE(cpuconfig, simplebus, aml8726_cpucfg_driver,
660280905Sganbold    aml8726_cpucfg_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE);
661