a3e51ff0e6
The ci20 port (by kan@) is going to reuse almost all of the intrng code since the SoC in question looks suspiciously like someone took an ARM SoC design and replaced the ARM core with a MIPS core. * migrate out the code; * rename ARM_ -> INTR_; * rename arm_ -> intr_; * move the interrupt flush routine from intr.c / intrng.c into arm/machdep_intr.c - removing the code duplication and removing the ARM specific bits from here. Thanks to the Star Wars: The Force Awakens premiere line for allowing me a couple hours of quiet time to finish the universe builds. Tested: * make universe TODO: * The structure definitions in subr_intr.c still includes machine/intr.h which requires one duplicates all of the intrng definitions in the platform code (which kan has done, and I think we don't have to.) Instead I should break out the generic things (function declarations, common intr structures, etc) into a separate header. * Kan has requested I make the PIC based IPI stuff optional.
661 lines
17 KiB
C
661 lines
17 KiB
C
/*-
|
|
* Copyright 2015 John Wehle <john@feith.com>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* Amlogic aml8726 multiprocessor support.
|
|
*
|
|
* Some processors require powering on which involves poking registers
|
|
* on the aobus and cbus ... it's expected that these locations are set
|
|
* in stone.
|
|
*
|
|
* Locking is not used as these routines should only be called by the BP
|
|
* during startup and should complete prior to the APs being released (the
|
|
* issue being to ensure that a register such as AML_SOC_CPU_CLK_CNTL_REG
|
|
* is not concurrently modified).
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/module.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/rman.h>
|
|
#include <sys/smp.h>
|
|
|
|
#include <vm/vm.h>
|
|
#include <vm/pmap.h>
|
|
|
|
#include <machine/bus.h>
|
|
#include <machine/smp.h>
|
|
#include <machine/fdt.h>
|
|
#include <machine/intr.h>
|
|
|
|
#include <dev/fdt/fdt_common.h>
|
|
#include <dev/ofw/ofw_bus.h>
|
|
#include <dev/ofw/ofw_bus_subr.h>
|
|
|
|
#include <arm/amlogic/aml8726/aml8726_soc.h>
|
|
|
|
static const char *scu_compatible[] = {
|
|
"arm,cortex-a5-scu",
|
|
"arm,cortex-a9-scu",
|
|
NULL
|
|
};
|
|
|
|
static const char *scu_errata_764369[] = {
|
|
"arm,cortex-a9-scu",
|
|
NULL
|
|
};
|
|
|
|
static const char *cpucfg_compatible[] = {
|
|
"amlogic,aml8726-cpuconfig",
|
|
NULL
|
|
};
|
|
|
|
static struct {
|
|
boolean_t errata_764369;
|
|
u_long scu_size;
|
|
struct resource scu_res;
|
|
u_long cpucfg_size;
|
|
struct resource cpucfg_res;
|
|
struct resource aobus_res;
|
|
struct resource cbus_res;
|
|
} aml8726_smp;
|
|
|
|
#define AML_SCU_CONTROL_REG 0
|
|
#define AML_SCU_CONTROL_ENABLE 1
|
|
#define AML_SCU_CONFIG_REG 4
|
|
#define AML_SCU_CONFIG_NCPU_MASK 0x3
|
|
#define AML_SCU_CPU_PWR_STATUS_REG 8
|
|
#define AML_SCU_CPU_PWR_STATUS_CPU3_MASK (3 << 24)
|
|
#define AML_SCU_CPU_PWR_STATUS_CPU2_MASK (3 << 16)
|
|
#define AML_SCU_CPU_PWR_STATUS_CPU1_MASK (3 << 8)
|
|
#define AML_SCU_CPU_PWR_STATUS_CPU0_MASK 3
|
|
#define AML_SCU_INV_TAGS_REG 12
|
|
#define AML_SCU_DIAG_CONTROL_REG 48
|
|
#define AML_SCU_DIAG_CONTROL_DISABLE_MIGBIT 1
|
|
|
|
#define AML_CPUCONF_CONTROL_REG 0
|
|
#define AML_CPUCONF_CPU1_ADDR_REG 4
|
|
#define AML_CPUCONF_CPU2_ADDR_REG 8
|
|
#define AML_CPUCONF_CPU3_ADDR_REG 12
|
|
|
|
/* aobus */
|
|
|
|
#define AML_M8_CPU_PWR_CNTL0_REG 0xe0
|
|
#define AML_M8B_CPU_PWR_CNTL0_MODE_CPU3_MASK (3 << 22)
|
|
#define AML_M8B_CPU_PWR_CNTL0_MODE_CPU2_MASK (3 << 20)
|
|
#define AML_M8B_CPU_PWR_CNTL0_MODE_CPU1_MASK (3 << 18)
|
|
|
|
#define AML_M8_CPU_PWR_CNTL0_ISO_CPU3 (1 << 3)
|
|
#define AML_M8_CPU_PWR_CNTL0_ISO_CPU2 (1 << 2)
|
|
#define AML_M8_CPU_PWR_CNTL0_ISO_CPU1 (1 << 1)
|
|
|
|
#define AML_M8_CPU_PWR_CNTL1_REG 0xe4
|
|
#define AML_M8B_CPU_PWR_CNTL1_PWR_CPU3 (1 << 19)
|
|
#define AML_M8B_CPU_PWR_CNTL1_PWR_CPU2 (1 << 18)
|
|
#define AML_M8B_CPU_PWR_CNTL1_PWR_CPU1 (1 << 17)
|
|
|
|
#define AML_M8_CPU_PWR_CNTL1_MODE_CPU3_MASK (3 << 8)
|
|
#define AML_M8_CPU_PWR_CNTL1_MODE_CPU2_MASK (3 << 6)
|
|
#define AML_M8_CPU_PWR_CNTL1_MODE_CPU1_MASK (3 << 4)
|
|
|
|
#define AML_M8B_CPU_PWR_MEM_PD0_REG 0xf4
|
|
#define AML_M8B_CPU_PWR_MEM_PD0_CPU3 (0xf << 20)
|
|
#define AML_M8B_CPU_PWR_MEM_PD0_CPU2 (0xf << 24)
|
|
#define AML_M8B_CPU_PWR_MEM_PD0_CPU1 (0xf << 28)
|
|
|
|
/* cbus */
|
|
|
|
#define AML_SOC_CPU_CLK_CNTL_REG 0x419c
|
|
#define AML_M8_CPU_CLK_CNTL_RESET_CPU3 (1 << 27)
|
|
#define AML_M8_CPU_CLK_CNTL_RESET_CPU2 (1 << 26)
|
|
#define AML_M8_CPU_CLK_CNTL_RESET_CPU1 (1 << 25)
|
|
|
|
#define SCU_WRITE_4(reg, value) bus_write_4(&aml8726_smp.scu_res, \
|
|
(reg), (value))
|
|
#define SCU_READ_4(reg) bus_read_4(&aml8726_smp.scu_res, (reg))
|
|
#define SCU_BARRIER(reg) bus_barrier(&aml8726_smp.scu_res, \
|
|
(reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
|
|
|
|
#define CPUCONF_WRITE_4(reg, value) bus_write_4(&aml8726_smp.cpucfg_res, \
|
|
(reg), (value))
|
|
#define CPUCONF_READ_4(reg) bus_read_4(&aml8726_smp.cpucfg_res, \
|
|
(reg))
|
|
#define CPUCONF_BARRIER(reg) bus_barrier(&aml8726_smp.cpucfg_res, \
|
|
(reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
|
|
|
|
#define AOBUS_WRITE_4(reg, value) bus_write_4(&aml8726_smp.aobus_res, \
|
|
(reg), (value))
|
|
#define AOBUS_READ_4(reg) bus_read_4(&aml8726_smp.aobus_res, \
|
|
(reg))
|
|
#define AOBUS_BARRIER(reg) bus_barrier(&aml8726_smp.aobus_res, \
|
|
(reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
|
|
|
|
#define CBUS_WRITE_4(reg, value) bus_write_4(&aml8726_smp.cbus_res, \
|
|
(reg), (value))
|
|
#define CBUS_READ_4(reg) bus_read_4(&aml8726_smp.cbus_res, \
|
|
(reg))
|
|
#define CBUS_BARRIER(reg) bus_barrier(&aml8726_smp.cbus_res, \
|
|
(reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
|
|
|
|
static phandle_t
|
|
find_node_for_device(const char *device, const char **compatible)
|
|
{
|
|
int i;
|
|
phandle_t node;
|
|
|
|
/*
|
|
* Try to access the node directly i.e. through /aliases/.
|
|
*/
|
|
|
|
if ((node = OF_finddevice(device)) != 0)
|
|
for (i = 0; compatible[i]; i++)
|
|
if (fdt_is_compatible_strict(node, compatible[i]))
|
|
return node;
|
|
|
|
/*
|
|
* Find the node the long way.
|
|
*/
|
|
|
|
for (i = 0; compatible[i]; i++) {
|
|
if ((node = OF_finddevice("/soc")) == 0)
|
|
return (0);
|
|
|
|
if ((node = fdt_find_compatible(node, compatible[i], 1)) != 0)
|
|
return node;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
static int
|
|
alloc_resource_for_node(phandle_t node, struct resource *res, u_long *size)
|
|
{
|
|
int err;
|
|
u_long pbase, psize;
|
|
u_long start;
|
|
|
|
if ((err = fdt_get_range(OF_parent(node), 0, &pbase, &psize)) != 0 ||
|
|
(err = fdt_regsize(node, &start, size)) != 0)
|
|
return (err);
|
|
|
|
start += pbase;
|
|
|
|
memset(res, 0, sizeof(*res));
|
|
|
|
res->r_bustag = fdtbus_bs_tag;
|
|
|
|
err = bus_space_map(res->r_bustag, start, *size, 0, &res->r_bushandle);
|
|
|
|
return (err);
|
|
}
|
|
|
|
|
|
static void
|
|
power_on_cpu(int cpu)
|
|
{
|
|
uint32_t scpsr;
|
|
uint32_t value;
|
|
|
|
if (cpu <= 0)
|
|
return;
|
|
|
|
/*
|
|
* Power on the CPU if the intricate details are known, otherwise
|
|
* just hope for the best (it may have already be powered on by
|
|
* the hardware / firmware).
|
|
*/
|
|
|
|
switch (aml8726_soc_hw_rev) {
|
|
case AML_SOC_HW_REV_M8:
|
|
case AML_SOC_HW_REV_M8B:
|
|
/*
|
|
* Set the SCU power status for the CPU to normal mode.
|
|
*/
|
|
scpsr = SCU_READ_4(AML_SCU_CPU_PWR_STATUS_REG);
|
|
scpsr &= ~(AML_SCU_CPU_PWR_STATUS_CPU1_MASK << ((cpu - 1) * 8));
|
|
SCU_WRITE_4(AML_SCU_CPU_PWR_STATUS_REG, scpsr);
|
|
SCU_BARRIER(AML_SCU_CPU_PWR_STATUS_REG);
|
|
|
|
if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
|
|
/*
|
|
* Reset may cause the current power status from the
|
|
* actual CPU to be written to the SCU (over-writing
|
|
* the value we've just written) so set it to normal
|
|
* mode as well.
|
|
*/
|
|
value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL0_REG);
|
|
value &= ~(AML_M8B_CPU_PWR_CNTL0_MODE_CPU1_MASK <<
|
|
((cpu - 1) * 2));
|
|
AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL0_REG, value);
|
|
AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL0_REG);
|
|
}
|
|
|
|
DELAY(5);
|
|
|
|
/*
|
|
* Assert reset.
|
|
*/
|
|
value = CBUS_READ_4(AML_SOC_CPU_CLK_CNTL_REG);
|
|
value |= AML_M8_CPU_CLK_CNTL_RESET_CPU1 << (cpu - 1);
|
|
CBUS_WRITE_4(AML_SOC_CPU_CLK_CNTL_REG, value);
|
|
CBUS_BARRIER(AML_SOC_CPU_CLK_CNTL_REG);
|
|
|
|
if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
|
|
/*
|
|
* Release RAM pull-down.
|
|
*/
|
|
value = AOBUS_READ_4(AML_M8B_CPU_PWR_MEM_PD0_REG);
|
|
value &= ~((uint32_t)AML_M8B_CPU_PWR_MEM_PD0_CPU1 >>
|
|
((cpu - 1) * 4));
|
|
AOBUS_WRITE_4(AML_M8B_CPU_PWR_MEM_PD0_REG, value);
|
|
AOBUS_BARRIER(AML_M8B_CPU_PWR_MEM_PD0_REG);
|
|
}
|
|
|
|
/*
|
|
* Power on CPU.
|
|
*/
|
|
value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL1_REG);
|
|
value &= ~(AML_M8_CPU_PWR_CNTL1_MODE_CPU1_MASK <<
|
|
((cpu - 1) * 2));
|
|
AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL1_REG, value);
|
|
AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL1_REG);
|
|
|
|
DELAY(10);
|
|
|
|
if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
|
|
/*
|
|
* Wait for power on confirmation.
|
|
*/
|
|
for ( ; ; ) {
|
|
value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL1_REG);
|
|
value &= AML_M8B_CPU_PWR_CNTL1_PWR_CPU1 <<
|
|
(cpu - 1);
|
|
if (value)
|
|
break;
|
|
DELAY(10);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Release peripheral clamp.
|
|
*/
|
|
value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL0_REG);
|
|
value &= ~(AML_M8_CPU_PWR_CNTL0_ISO_CPU1 << (cpu - 1));
|
|
AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL0_REG, value);
|
|
AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL0_REG);
|
|
|
|
/*
|
|
* Release reset.
|
|
*/
|
|
value = CBUS_READ_4(AML_SOC_CPU_CLK_CNTL_REG);
|
|
value &= ~(AML_M8_CPU_CLK_CNTL_RESET_CPU1 << (cpu - 1));
|
|
CBUS_WRITE_4(AML_SOC_CPU_CLK_CNTL_REG, value);
|
|
CBUS_BARRIER(AML_SOC_CPU_CLK_CNTL_REG);
|
|
|
|
if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
|
|
/*
|
|
* The Amlogic Linux platform code sets the SCU power
|
|
* status for the CPU again for some reason so we
|
|
* follow suit (perhaps in case the reset caused
|
|
* a stale power status from the actual CPU to be
|
|
* written to the SCU).
|
|
*/
|
|
SCU_WRITE_4(AML_SCU_CPU_PWR_STATUS_REG, scpsr);
|
|
SCU_BARRIER(AML_SCU_CPU_PWR_STATUS_REG);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
platform_mp_init_secondary(void)
|
|
{
|
|
|
|
/*
|
|
* Consider modifying the timer driver to support
|
|
* per-cpu timers and then enabling the timer for
|
|
* each AP.
|
|
*/
|
|
|
|
intr_pic_init_secondary();
|
|
}
|
|
|
|
|
|
void
|
|
platform_mp_setmaxid(void)
|
|
{
|
|
int err;
|
|
int i;
|
|
int ncpu;
|
|
phandle_t cpucfg_node;
|
|
phandle_t scu_node;
|
|
uint32_t value;
|
|
|
|
if (mp_ncpus != 0)
|
|
return;
|
|
|
|
ncpu = 1;
|
|
|
|
/*
|
|
* Is the hardware necessary for SMP present?
|
|
*/
|
|
|
|
if ((scu_node = find_node_for_device("scu", scu_compatible)) == 0)
|
|
goto moveon;
|
|
|
|
if ((cpucfg_node = find_node_for_device("cpuconfig",
|
|
cpucfg_compatible)) == 0)
|
|
goto moveon;
|
|
|
|
if (alloc_resource_for_node(scu_node, &aml8726_smp.scu_res,
|
|
&aml8726_smp.scu_size) != 0)
|
|
panic("Could not allocate resource for SCU");
|
|
|
|
if (alloc_resource_for_node(cpucfg_node, &aml8726_smp.cpucfg_res,
|
|
&aml8726_smp.cpucfg_size) != 0)
|
|
panic("Could not allocate resource for CPUCONFIG");
|
|
|
|
|
|
/*
|
|
* Strictly speaking the aobus and cbus may not be required in
|
|
* order to start an AP (it depends on the processor), however
|
|
* always mapping them in simplifies the code.
|
|
*/
|
|
|
|
aml8726_smp.aobus_res.r_bustag = fdtbus_bs_tag;
|
|
|
|
err = bus_space_map(aml8726_smp.aobus_res.r_bustag,
|
|
AML_SOC_AOBUS_BASE_ADDR, 0x100000,
|
|
0, &aml8726_smp.aobus_res.r_bushandle);
|
|
|
|
if (err)
|
|
panic("Could not allocate resource for AOBUS");
|
|
|
|
aml8726_smp.cbus_res.r_bustag = fdtbus_bs_tag;
|
|
|
|
err = bus_space_map(aml8726_smp.cbus_res.r_bustag,
|
|
AML_SOC_CBUS_BASE_ADDR, 0x100000,
|
|
0, &aml8726_smp.cbus_res.r_bushandle);
|
|
|
|
if (err)
|
|
panic("Could not allocate resource for CBUS");
|
|
|
|
aml8726_smp.errata_764369 = false;
|
|
for (i = 0; scu_errata_764369[i]; i++)
|
|
if (fdt_is_compatible_strict(scu_node, scu_errata_764369[i])) {
|
|
aml8726_smp.errata_764369 = true;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Read the number of CPUs present.
|
|
*/
|
|
value = SCU_READ_4(AML_SCU_CONFIG_REG);
|
|
ncpu = (value & AML_SCU_CONFIG_NCPU_MASK) + 1;
|
|
|
|
moveon:
|
|
mp_ncpus = ncpu;
|
|
mp_maxid = ncpu - 1;
|
|
}
|
|
|
|
|
|
int
|
|
platform_mp_probe(void)
|
|
{
|
|
|
|
if (mp_ncpus == 0)
|
|
platform_mp_setmaxid();
|
|
|
|
return (mp_ncpus > 1);
|
|
}
|
|
|
|
|
|
void
|
|
platform_mp_start_ap(void)
|
|
{
|
|
int i;
|
|
uint32_t reg;
|
|
uint32_t value;
|
|
vm_paddr_t paddr;
|
|
|
|
if (mp_ncpus < 2)
|
|
return;
|
|
|
|
/*
|
|
* Invalidate SCU cache tags. The 0x0000ffff constant invalidates
|
|
* all ways on all cores 0-3. Per the ARM docs, it's harmless to
|
|
* write to the bits for cores that are not present.
|
|
*/
|
|
SCU_WRITE_4(AML_SCU_INV_TAGS_REG, 0x0000ffff);
|
|
|
|
if (aml8726_smp.errata_764369) {
|
|
/*
|
|
* Erratum ARM/MP: 764369 (problems with cache maintenance).
|
|
* Setting the "disable-migratory bit" in the undocumented SCU
|
|
* Diagnostic Control Register helps work around the problem.
|
|
*/
|
|
value = SCU_READ_4(AML_SCU_DIAG_CONTROL_REG);
|
|
value |= AML_SCU_DIAG_CONTROL_DISABLE_MIGBIT;
|
|
SCU_WRITE_4(AML_SCU_DIAG_CONTROL_REG, value);
|
|
}
|
|
|
|
/*
|
|
* Enable the SCU, then clean the cache on this core. After these
|
|
* two operations the cache tag ram in the SCU is coherent with
|
|
* the contents of the cache on this core. The other cores aren't
|
|
* running yet so their caches can't contain valid data yet, however
|
|
* we've initialized their SCU tag ram above, so they will be
|
|
* coherent from startup.
|
|
*/
|
|
value = SCU_READ_4(AML_SCU_CONTROL_REG);
|
|
value |= AML_SCU_CONTROL_ENABLE;
|
|
SCU_WRITE_4(AML_SCU_CONTROL_REG, value);
|
|
SCU_BARRIER(AML_SCU_CONTROL_REG);
|
|
cpu_idcache_wbinv_all();
|
|
|
|
/* Set the boot address and power on each AP. */
|
|
paddr = pmap_kextract((vm_offset_t)mpentry);
|
|
for (i = 1; i < mp_ncpus; i++) {
|
|
reg = AML_CPUCONF_CPU1_ADDR_REG + ((i - 1) * 4);
|
|
CPUCONF_WRITE_4(reg, paddr);
|
|
CPUCONF_BARRIER(reg);
|
|
|
|
power_on_cpu(i);
|
|
}
|
|
|
|
/*
|
|
* Enable the APs.
|
|
*
|
|
* The Amlogic Linux platform code sets the lsb for some reason
|
|
* in addition to the enable bit for each AP so we follow suit
|
|
* (the lsb may be the enable bit for the BP, though in that case
|
|
* it should already be set since it's currently running).
|
|
*/
|
|
value = CPUCONF_READ_4(AML_CPUCONF_CONTROL_REG);
|
|
value |= 1;
|
|
for (i = 1; i < mp_ncpus; i++)
|
|
value |= (1 << i);
|
|
CPUCONF_WRITE_4(AML_CPUCONF_CONTROL_REG, value);
|
|
CPUCONF_BARRIER(AML_CPUCONF_CONTROL_REG);
|
|
|
|
/* Wakeup the now enabled APs */
|
|
armv7_sev();
|
|
|
|
/*
|
|
* Free the resources which are not needed after startup.
|
|
*/
|
|
bus_space_unmap(aml8726_smp.scu_res.r_bustag,
|
|
aml8726_smp.scu_res.r_bushandle,
|
|
aml8726_smp.scu_size);
|
|
bus_space_unmap(aml8726_smp.cpucfg_res.r_bustag,
|
|
aml8726_smp.cpucfg_res.r_bushandle,
|
|
aml8726_smp.cpucfg_size);
|
|
bus_space_unmap(aml8726_smp.aobus_res.r_bustag,
|
|
aml8726_smp.aobus_res.r_bushandle,
|
|
0x100000);
|
|
bus_space_unmap(aml8726_smp.cbus_res.r_bustag,
|
|
aml8726_smp.cbus_res.r_bushandle,
|
|
0x100000);
|
|
memset(&aml8726_smp, 0, sizeof(aml8726_smp));
|
|
}
|
|
|
|
void
|
|
platform_ipi_send(cpuset_t cpus, u_int ipi)
|
|
{
|
|
|
|
pic_ipi_send(cpus, ipi);
|
|
}
|
|
|
|
/*
|
|
* Stub drivers for cosmetic purposes.
|
|
*/
|
|
struct aml8726_scu_softc {
|
|
device_t dev;
|
|
};
|
|
|
|
static int
|
|
aml8726_scu_probe(device_t dev)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; scu_compatible[i]; i++)
|
|
if (ofw_bus_is_compatible(dev, scu_compatible[i]))
|
|
break;
|
|
|
|
if (!scu_compatible[i])
|
|
return (ENXIO);
|
|
|
|
device_set_desc(dev, "ARM Snoop Control Unit");
|
|
|
|
return (BUS_PROBE_DEFAULT);
|
|
}
|
|
|
|
static int
|
|
aml8726_scu_attach(device_t dev)
|
|
{
|
|
struct aml8726_scu_softc *sc = device_get_softc(dev);
|
|
|
|
sc->dev = dev;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
aml8726_scu_detach(device_t dev)
|
|
{
|
|
|
|
return (0);
|
|
}
|
|
|
|
static device_method_t aml8726_scu_methods[] = {
|
|
/* Device interface */
|
|
DEVMETHOD(device_probe, aml8726_scu_probe),
|
|
DEVMETHOD(device_attach, aml8726_scu_attach),
|
|
DEVMETHOD(device_detach, aml8726_scu_detach),
|
|
|
|
DEVMETHOD_END
|
|
};
|
|
|
|
static driver_t aml8726_scu_driver = {
|
|
"scu",
|
|
aml8726_scu_methods,
|
|
sizeof(struct aml8726_scu_softc),
|
|
};
|
|
|
|
static devclass_t aml8726_scu_devclass;
|
|
|
|
EARLY_DRIVER_MODULE(scu, simplebus, aml8726_scu_driver, aml8726_scu_devclass,
|
|
0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE);
|
|
|
|
struct aml8726_cpucfg_softc {
|
|
device_t dev;
|
|
};
|
|
|
|
static int
|
|
aml8726_cpucfg_probe(device_t dev)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; cpucfg_compatible[i]; i++)
|
|
if (ofw_bus_is_compatible(dev, cpucfg_compatible[i]))
|
|
break;
|
|
|
|
if (!cpucfg_compatible[i])
|
|
return (ENXIO);
|
|
|
|
device_set_desc(dev, "Amlogic CPU Config");
|
|
|
|
return (BUS_PROBE_DEFAULT);
|
|
}
|
|
|
|
static int
|
|
aml8726_cpucfg_attach(device_t dev)
|
|
{
|
|
struct aml8726_cpucfg_softc *sc = device_get_softc(dev);
|
|
|
|
sc->dev = dev;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
aml8726_cpucfg_detach(device_t dev)
|
|
{
|
|
|
|
return (0);
|
|
}
|
|
|
|
static device_method_t aml8726_cpucfg_methods[] = {
|
|
/* Device interface */
|
|
DEVMETHOD(device_probe, aml8726_cpucfg_probe),
|
|
DEVMETHOD(device_attach, aml8726_cpucfg_attach),
|
|
DEVMETHOD(device_detach, aml8726_cpucfg_detach),
|
|
|
|
DEVMETHOD_END
|
|
};
|
|
|
|
static driver_t aml8726_cpucfg_driver = {
|
|
"cpuconfig",
|
|
aml8726_cpucfg_methods,
|
|
sizeof(struct aml8726_cpucfg_softc),
|
|
};
|
|
|
|
static devclass_t aml8726_cpucfg_devclass;
|
|
|
|
EARLY_DRIVER_MODULE(cpuconfig, simplebus, aml8726_cpucfg_driver,
|
|
aml8726_cpucfg_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE);
|