aml8726_mp.c revision 292426
1/*-
2 * Copyright 2015 John Wehle <john@feith.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27/*
28 * Amlogic aml8726 multiprocessor support.
29 *
30 * Some processors require powering on which involves poking registers
31 * on the aobus and cbus ... it's expected that these locations are set
32 * in stone.
33 *
34 * Locking is not used as these routines should only be called by the BP
35 * during startup and should complete prior to the APs being released (the
36 * issue being to ensure that a register such as AML_SOC_CPU_CLK_CNTL_REG
37 * is not concurrently modified).
38 */
39
40#include <sys/cdefs.h>
41__FBSDID("$FreeBSD: head/sys/arm/amlogic/aml8726/aml8726_mp.c 292426 2015-12-18 05:43:59Z adrian $");
42#include <sys/param.h>
43#include <sys/systm.h>
44#include <sys/bus.h>
45#include <sys/kernel.h>
46#include <sys/module.h>
47#include <sys/lock.h>
48#include <sys/mutex.h>
49#include <sys/resource.h>
50#include <sys/rman.h>
51#include <sys/smp.h>
52
53#include <vm/vm.h>
54#include <vm/pmap.h>
55
56#include <machine/bus.h>
57#include <machine/smp.h>
58#include <machine/fdt.h>
59#include <machine/intr.h>
60
61#include <dev/fdt/fdt_common.h>
62#include <dev/ofw/ofw_bus.h>
63#include <dev/ofw/ofw_bus_subr.h>
64
65#include <arm/amlogic/aml8726/aml8726_soc.h>
66
67static const char *scu_compatible[] = {
68	"arm,cortex-a5-scu",
69	"arm,cortex-a9-scu",
70	NULL
71};
72
73static const char *scu_errata_764369[] = {
74	"arm,cortex-a9-scu",
75	NULL
76};
77
78static const char *cpucfg_compatible[] = {
79	"amlogic,aml8726-cpuconfig",
80	NULL
81};
82
83static struct {
84	boolean_t errata_764369;
85	u_long scu_size;
86	struct resource scu_res;
87	u_long cpucfg_size;
88	struct resource cpucfg_res;
89	struct resource aobus_res;
90	struct resource cbus_res;
91} aml8726_smp;
92
93#define	AML_SCU_CONTROL_REG			0
94#define	AML_SCU_CONTROL_ENABLE			1
95#define	AML_SCU_CONFIG_REG			4
96#define	AML_SCU_CONFIG_NCPU_MASK		0x3
97#define	AML_SCU_CPU_PWR_STATUS_REG		8
98#define	AML_SCU_CPU_PWR_STATUS_CPU3_MASK	(3 << 24)
99#define	AML_SCU_CPU_PWR_STATUS_CPU2_MASK	(3 << 16)
100#define	AML_SCU_CPU_PWR_STATUS_CPU1_MASK	(3 << 8)
101#define	AML_SCU_CPU_PWR_STATUS_CPU0_MASK	3
102#define	AML_SCU_INV_TAGS_REG			12
103#define	AML_SCU_DIAG_CONTROL_REG		48
104#define	AML_SCU_DIAG_CONTROL_DISABLE_MIGBIT	1
105
106#define	AML_CPUCONF_CONTROL_REG			0
107#define	AML_CPUCONF_CPU1_ADDR_REG		4
108#define	AML_CPUCONF_CPU2_ADDR_REG		8
109#define	AML_CPUCONF_CPU3_ADDR_REG		12
110
111/* aobus */
112
113#define	AML_M8_CPU_PWR_CNTL0_REG		0xe0
114#define	AML_M8B_CPU_PWR_CNTL0_MODE_CPU3_MASK	(3 << 22)
115#define	AML_M8B_CPU_PWR_CNTL0_MODE_CPU2_MASK	(3 << 20)
116#define	AML_M8B_CPU_PWR_CNTL0_MODE_CPU1_MASK	(3 << 18)
117
118#define	AML_M8_CPU_PWR_CNTL0_ISO_CPU3		(1 << 3)
119#define	AML_M8_CPU_PWR_CNTL0_ISO_CPU2		(1 << 2)
120#define	AML_M8_CPU_PWR_CNTL0_ISO_CPU1		(1 << 1)
121
122#define	AML_M8_CPU_PWR_CNTL1_REG		0xe4
123#define	AML_M8B_CPU_PWR_CNTL1_PWR_CPU3		(1 << 19)
124#define	AML_M8B_CPU_PWR_CNTL1_PWR_CPU2		(1 << 18)
125#define	AML_M8B_CPU_PWR_CNTL1_PWR_CPU1		(1 << 17)
126
127#define	AML_M8_CPU_PWR_CNTL1_MODE_CPU3_MASK	(3 << 8)
128#define	AML_M8_CPU_PWR_CNTL1_MODE_CPU2_MASK	(3 << 6)
129#define	AML_M8_CPU_PWR_CNTL1_MODE_CPU1_MASK	(3 << 4)
130
131#define	AML_M8B_CPU_PWR_MEM_PD0_REG		0xf4
132#define	AML_M8B_CPU_PWR_MEM_PD0_CPU3		(0xf << 20)
133#define	AML_M8B_CPU_PWR_MEM_PD0_CPU2		(0xf << 24)
134#define	AML_M8B_CPU_PWR_MEM_PD0_CPU1		(0xf << 28)
135
136/* cbus */
137
138#define	AML_SOC_CPU_CLK_CNTL_REG		0x419c
139#define	AML_M8_CPU_CLK_CNTL_RESET_CPU3		(1 << 27)
140#define	AML_M8_CPU_CLK_CNTL_RESET_CPU2		(1 << 26)
141#define	AML_M8_CPU_CLK_CNTL_RESET_CPU1		(1 << 25)
142
143#define	SCU_WRITE_4(reg, value)		bus_write_4(&aml8726_smp.scu_res,    \
144    (reg), (value))
145#define	SCU_READ_4(reg)			bus_read_4(&aml8726_smp.scu_res, (reg))
146#define	SCU_BARRIER(reg)		bus_barrier(&aml8726_smp.scu_res,    \
147    (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
148
149#define	CPUCONF_WRITE_4(reg, value)	bus_write_4(&aml8726_smp.cpucfg_res, \
150    (reg), (value))
151#define	CPUCONF_READ_4(reg)		bus_read_4(&aml8726_smp.cpucfg_res,  \
152    (reg))
153#define	CPUCONF_BARRIER(reg)		bus_barrier(&aml8726_smp.cpucfg_res, \
154    (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
155
156#define	AOBUS_WRITE_4(reg, value)	bus_write_4(&aml8726_smp.aobus_res,  \
157    (reg), (value))
158#define	AOBUS_READ_4(reg)		bus_read_4(&aml8726_smp.aobus_res,   \
159    (reg))
160#define	AOBUS_BARRIER(reg)		bus_barrier(&aml8726_smp.aobus_res,  \
161    (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
162
163#define	CBUS_WRITE_4(reg, value)	bus_write_4(&aml8726_smp.cbus_res,   \
164    (reg), (value))
165#define CBUS_READ_4(reg)		bus_read_4(&aml8726_smp.cbus_res,    \
166    (reg))
167#define	CBUS_BARRIER(reg)		bus_barrier(&aml8726_smp.cbus_res,   \
168    (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
169
170static phandle_t
171find_node_for_device(const char *device, const char **compatible)
172{
173	int i;
174	phandle_t node;
175
176	/*
177	 * Try to access the node directly i.e. through /aliases/.
178	 */
179
180	if ((node = OF_finddevice(device)) != 0)
181		for (i = 0; compatible[i]; i++)
182			if (fdt_is_compatible_strict(node, compatible[i]))
183				return node;
184
185	/*
186	 * Find the node the long way.
187	 */
188
189	for (i = 0; compatible[i]; i++) {
190		if ((node = OF_finddevice("/soc")) == 0)
191			return (0);
192
193		if ((node = fdt_find_compatible(node, compatible[i], 1)) != 0)
194			return node;
195	}
196
197	return (0);
198}
199
200
201static int
202alloc_resource_for_node(phandle_t node, struct resource *res, u_long *size)
203{
204	int err;
205	u_long pbase, psize;
206	u_long start;
207
208	if ((err = fdt_get_range(OF_parent(node), 0, &pbase, &psize)) != 0 ||
209	    (err = fdt_regsize(node, &start, size)) != 0)
210		return (err);
211
212	start += pbase;
213
214	memset(res, 0, sizeof(*res));
215
216	res->r_bustag = fdtbus_bs_tag;
217
218	err = bus_space_map(res->r_bustag, start, *size, 0, &res->r_bushandle);
219
220	return (err);
221}
222
223
224static void
225power_on_cpu(int cpu)
226{
227	uint32_t scpsr;
228	uint32_t value;
229
230	if (cpu <= 0)
231		return;
232
233	/*
234	 * Power on the CPU if the intricate details are known, otherwise
235	 * just hope for the best (it may have already be powered on by
236	 * the hardware / firmware).
237	 */
238
239	switch (aml8726_soc_hw_rev) {
240	case AML_SOC_HW_REV_M8:
241	case AML_SOC_HW_REV_M8B:
242		/*
243		 * Set the SCU power status for the CPU to normal mode.
244		 */
245		scpsr = SCU_READ_4(AML_SCU_CPU_PWR_STATUS_REG);
246		scpsr &= ~(AML_SCU_CPU_PWR_STATUS_CPU1_MASK << ((cpu - 1) * 8));
247		SCU_WRITE_4(AML_SCU_CPU_PWR_STATUS_REG, scpsr);
248		SCU_BARRIER(AML_SCU_CPU_PWR_STATUS_REG);
249
250		if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
251			/*
252			 * Reset may cause the current power status from the
253			 * actual CPU to be written to the SCU (over-writing
254			 * the value  we've just written) so set it to normal
255			 * mode as well.
256			 */
257			 value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL0_REG);
258			 value &= ~(AML_M8B_CPU_PWR_CNTL0_MODE_CPU1_MASK <<
259			    ((cpu - 1) * 2));
260			 AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL0_REG, value);
261			 AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL0_REG);
262		 }
263
264		DELAY(5);
265
266		/*
267		 * Assert reset.
268		 */
269		value = CBUS_READ_4(AML_SOC_CPU_CLK_CNTL_REG);
270		value |= AML_M8_CPU_CLK_CNTL_RESET_CPU1 << (cpu - 1);
271		CBUS_WRITE_4(AML_SOC_CPU_CLK_CNTL_REG, value);
272		CBUS_BARRIER(AML_SOC_CPU_CLK_CNTL_REG);
273
274		if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
275			/*
276			 * Release RAM pull-down.
277			 */
278			 value = AOBUS_READ_4(AML_M8B_CPU_PWR_MEM_PD0_REG);
279			 value &= ~((uint32_t)AML_M8B_CPU_PWR_MEM_PD0_CPU1 >>
280			    ((cpu - 1) * 4));
281			 AOBUS_WRITE_4(AML_M8B_CPU_PWR_MEM_PD0_REG, value);
282			 AOBUS_BARRIER(AML_M8B_CPU_PWR_MEM_PD0_REG);
283		 }
284
285		/*
286		 * Power on CPU.
287		 */
288		value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL1_REG);
289		value &= ~(AML_M8_CPU_PWR_CNTL1_MODE_CPU1_MASK <<
290		    ((cpu - 1) * 2));
291		AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL1_REG, value);
292		AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL1_REG);
293
294		DELAY(10);
295
296		if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
297			/*
298			 * Wait for power on confirmation.
299			 */
300			for ( ; ; ) {
301				value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL1_REG);
302				value &= AML_M8B_CPU_PWR_CNTL1_PWR_CPU1 <<
303				    (cpu - 1);
304				if (value)
305					break;
306				DELAY(10);
307			}
308		}
309
310		/*
311		 * Release peripheral clamp.
312		 */
313		value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL0_REG);
314		value &= ~(AML_M8_CPU_PWR_CNTL0_ISO_CPU1 << (cpu - 1));
315		AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL0_REG, value);
316		AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL0_REG);
317
318		/*
319		 * Release reset.
320		 */
321		value = CBUS_READ_4(AML_SOC_CPU_CLK_CNTL_REG);
322		value &= ~(AML_M8_CPU_CLK_CNTL_RESET_CPU1 << (cpu - 1));
323		CBUS_WRITE_4(AML_SOC_CPU_CLK_CNTL_REG, value);
324		CBUS_BARRIER(AML_SOC_CPU_CLK_CNTL_REG);
325
326		if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
327			/*
328			 * The Amlogic Linux platform code sets the SCU power
329			 * status for the CPU again for some reason so we
330			 * follow suit (perhaps in case the reset caused
331			 * a stale power status from the actual CPU to be
332			 * written to the SCU).
333			 */
334			SCU_WRITE_4(AML_SCU_CPU_PWR_STATUS_REG, scpsr);
335			SCU_BARRIER(AML_SCU_CPU_PWR_STATUS_REG);
336		}
337		break;
338	default:
339		break;
340	}
341}
342
343
344void
345platform_mp_init_secondary(void)
346{
347
348	/*
349	 * Consider modifying the timer driver to support
350	 * per-cpu timers and then enabling the timer for
351	 * each AP.
352	 */
353
354	 intr_pic_init_secondary();
355}
356
357
358void
359platform_mp_setmaxid(void)
360{
361	int err;
362	int i;
363	int ncpu;
364	phandle_t cpucfg_node;
365	phandle_t scu_node;
366	uint32_t value;
367
368	if (mp_ncpus != 0)
369		return;
370
371	ncpu = 1;
372
373	/*
374	 * Is the hardware necessary for SMP present?
375	 */
376
377	if ((scu_node = find_node_for_device("scu", scu_compatible)) == 0)
378		goto moveon;
379
380	if ((cpucfg_node = find_node_for_device("cpuconfig",
381	    cpucfg_compatible)) == 0)
382		goto moveon;
383
384	if (alloc_resource_for_node(scu_node, &aml8726_smp.scu_res,
385	    &aml8726_smp.scu_size) != 0)
386		panic("Could not allocate resource for SCU");
387
388	if (alloc_resource_for_node(cpucfg_node, &aml8726_smp.cpucfg_res,
389	    &aml8726_smp.cpucfg_size) != 0)
390		panic("Could not allocate resource for CPUCONFIG");
391
392
393	/*
394	 * Strictly speaking the aobus and cbus may not be required in
395	 * order to start an AP (it depends on the processor), however
396	 * always mapping them in simplifies the code.
397	 */
398
399	aml8726_smp.aobus_res.r_bustag = fdtbus_bs_tag;
400
401	err = bus_space_map(aml8726_smp.aobus_res.r_bustag,
402	    AML_SOC_AOBUS_BASE_ADDR, 0x100000,
403	    0, &aml8726_smp.aobus_res.r_bushandle);
404
405	if (err)
406		panic("Could not allocate resource for AOBUS");
407
408	aml8726_smp.cbus_res.r_bustag = fdtbus_bs_tag;
409
410	err = bus_space_map(aml8726_smp.cbus_res.r_bustag,
411	    AML_SOC_CBUS_BASE_ADDR, 0x100000,
412	    0, &aml8726_smp.cbus_res.r_bushandle);
413
414	if (err)
415		panic("Could not allocate resource for CBUS");
416
417	aml8726_smp.errata_764369 = false;
418	for (i = 0; scu_errata_764369[i]; i++)
419		if (fdt_is_compatible_strict(scu_node, scu_errata_764369[i])) {
420			aml8726_smp.errata_764369 = true;
421			break;
422		}
423
424	/*
425	 * Read the number of CPUs present.
426	 */
427	value = SCU_READ_4(AML_SCU_CONFIG_REG);
428	ncpu = (value & AML_SCU_CONFIG_NCPU_MASK) + 1;
429
430moveon:
431	mp_ncpus = ncpu;
432	mp_maxid = ncpu - 1;
433}
434
435
436int
437platform_mp_probe(void)
438{
439
440	if (mp_ncpus == 0)
441		platform_mp_setmaxid();
442
443	return (mp_ncpus > 1);
444}
445
446
447void
448platform_mp_start_ap(void)
449{
450	int i;
451	uint32_t reg;
452	uint32_t value;
453	vm_paddr_t paddr;
454
455	if (mp_ncpus < 2)
456		return;
457
458	/*
459	 * Invalidate SCU cache tags.  The 0x0000ffff constant invalidates
460	 * all ways on all cores 0-3.  Per the ARM docs, it's harmless to
461	 * write to the bits for cores that are not present.
462	 */
463	SCU_WRITE_4(AML_SCU_INV_TAGS_REG, 0x0000ffff);
464
465	if (aml8726_smp.errata_764369) {
466		/*
467		 * Erratum ARM/MP: 764369 (problems with cache maintenance).
468		 * Setting the "disable-migratory bit" in the undocumented SCU
469		 * Diagnostic Control Register helps work around the problem.
470		 */
471		value = SCU_READ_4(AML_SCU_DIAG_CONTROL_REG);
472		value |= AML_SCU_DIAG_CONTROL_DISABLE_MIGBIT;
473		SCU_WRITE_4(AML_SCU_DIAG_CONTROL_REG, value);
474	}
475
476	/*
477	 * Enable the SCU, then clean the cache on this core.  After these
478	 * two operations the cache tag ram in the SCU is coherent with
479	 * the contents of the cache on this core.  The other cores aren't
480	 * running yet so their caches can't contain valid data yet, however
481	 * we've initialized their SCU tag ram above, so they will be
482	 * coherent from startup.
483	 */
484	value = SCU_READ_4(AML_SCU_CONTROL_REG);
485	value |= AML_SCU_CONTROL_ENABLE;
486	SCU_WRITE_4(AML_SCU_CONTROL_REG, value);
487	SCU_BARRIER(AML_SCU_CONTROL_REG);
488	cpu_idcache_wbinv_all();
489
490	/* Set the boot address and power on each AP. */
491	paddr = pmap_kextract((vm_offset_t)mpentry);
492	for (i = 1; i < mp_ncpus; i++) {
493		reg = AML_CPUCONF_CPU1_ADDR_REG + ((i - 1) * 4);
494		CPUCONF_WRITE_4(reg, paddr);
495		CPUCONF_BARRIER(reg);
496
497		power_on_cpu(i);
498	}
499
500	/*
501	 * Enable the APs.
502	 *
503	 * The Amlogic Linux platform code sets the lsb for some reason
504	 * in addition to the enable bit for each AP so we follow suit
505	 * (the lsb may be the enable bit for the BP, though in that case
506	 * it should already be set since it's currently running).
507	 */
508	value = CPUCONF_READ_4(AML_CPUCONF_CONTROL_REG);
509	value |= 1;
510	for (i = 1; i < mp_ncpus; i++)
511		value |= (1 << i);
512	CPUCONF_WRITE_4(AML_CPUCONF_CONTROL_REG, value);
513	CPUCONF_BARRIER(AML_CPUCONF_CONTROL_REG);
514
515	/* Wakeup the now enabled APs */
516	armv7_sev();
517
518	/*
519	 * Free the resources which are not needed after startup.
520	 */
521	bus_space_unmap(aml8726_smp.scu_res.r_bustag,
522	    aml8726_smp.scu_res.r_bushandle,
523	    aml8726_smp.scu_size);
524	bus_space_unmap(aml8726_smp.cpucfg_res.r_bustag,
525	    aml8726_smp.cpucfg_res.r_bushandle,
526	    aml8726_smp.cpucfg_size);
527	bus_space_unmap(aml8726_smp.aobus_res.r_bustag,
528	    aml8726_smp.aobus_res.r_bushandle,
529	    0x100000);
530	bus_space_unmap(aml8726_smp.cbus_res.r_bustag,
531	    aml8726_smp.cbus_res.r_bushandle,
532	    0x100000);
533	memset(&aml8726_smp, 0, sizeof(aml8726_smp));
534}
535
536void
537platform_ipi_send(cpuset_t cpus, u_int ipi)
538{
539
540	pic_ipi_send(cpus, ipi);
541}
542
543/*
544 * Stub drivers for cosmetic purposes.
545 */
546struct aml8726_scu_softc {
547	device_t	dev;
548};
549
550static int
551aml8726_scu_probe(device_t dev)
552{
553	int i;
554
555	for (i = 0; scu_compatible[i]; i++)
556		if (ofw_bus_is_compatible(dev, scu_compatible[i]))
557			break;
558
559	if (!scu_compatible[i])
560		return (ENXIO);
561
562	device_set_desc(dev, "ARM Snoop Control Unit");
563
564	return (BUS_PROBE_DEFAULT);
565}
566
567static int
568aml8726_scu_attach(device_t dev)
569{
570	struct aml8726_scu_softc *sc = device_get_softc(dev);
571
572	sc->dev = dev;
573
574	return (0);
575}
576
577static int
578aml8726_scu_detach(device_t dev)
579{
580
581	return (0);
582}
583
584static device_method_t aml8726_scu_methods[] = {
585	/* Device interface */
586	DEVMETHOD(device_probe,		aml8726_scu_probe),
587	DEVMETHOD(device_attach,	aml8726_scu_attach),
588	DEVMETHOD(device_detach,	aml8726_scu_detach),
589
590	DEVMETHOD_END
591};
592
593static driver_t aml8726_scu_driver = {
594	"scu",
595	aml8726_scu_methods,
596	sizeof(struct aml8726_scu_softc),
597};
598
599static devclass_t aml8726_scu_devclass;
600
601EARLY_DRIVER_MODULE(scu, simplebus, aml8726_scu_driver, aml8726_scu_devclass,
602    0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE);
603
604struct aml8726_cpucfg_softc {
605	device_t	dev;
606};
607
608static int
609aml8726_cpucfg_probe(device_t dev)
610{
611	int i;
612
613	for (i = 0; cpucfg_compatible[i]; i++)
614		if (ofw_bus_is_compatible(dev, cpucfg_compatible[i]))
615			break;
616
617	if (!cpucfg_compatible[i])
618		return (ENXIO);
619
620	device_set_desc(dev, "Amlogic CPU Config");
621
622	return (BUS_PROBE_DEFAULT);
623}
624
625static int
626aml8726_cpucfg_attach(device_t dev)
627{
628	struct aml8726_cpucfg_softc *sc = device_get_softc(dev);
629
630	sc->dev = dev;
631
632	return (0);
633}
634
635static int
636aml8726_cpucfg_detach(device_t dev)
637{
638
639	return (0);
640}
641
642static device_method_t aml8726_cpucfg_methods[] = {
643	/* Device interface */
644	DEVMETHOD(device_probe,		aml8726_cpucfg_probe),
645	DEVMETHOD(device_attach,	aml8726_cpucfg_attach),
646	DEVMETHOD(device_detach,	aml8726_cpucfg_detach),
647
648	DEVMETHOD_END
649};
650
651static driver_t aml8726_cpucfg_driver = {
652	"cpuconfig",
653	aml8726_cpucfg_methods,
654	sizeof(struct aml8726_cpucfg_softc),
655};
656
657static devclass_t aml8726_cpucfg_devclass;
658
659EARLY_DRIVER_MODULE(cpuconfig, simplebus, aml8726_cpucfg_driver,
660    aml8726_cpucfg_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE);
661