#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: sunxi_mc_smp.c,v 1.4 2019/03/03 17:00:22 jmcneill Exp $");
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/device.h>
#include <sys/systm.h>
#include <uvm/uvm_extern.h>
#include <dev/fdt/fdtvar.h>
#include <arm/armreg.h>
#include <arm/cpu.h>
#include <arm/cpufunc.h>
#include <arm/locore.h>
#include <arm/sunxi/sunxi_mc_smp.h>
#define A80_PRCM_BASE 0x08001400
#define A80_PRCM_SIZE 0x200
#define A83T_PRCM_BASE 0x01f01400
#define A83T_PRCM_SIZE 0x800
#define PRCM_CL_RST_CTRL(cluster) (0x4 + (cluster) * 0x4)
#define PRCM_CL_PWROFF(cluster) (0x100 + (cluster) * 0x4)
#define PRCM_CL_PWR_CLAMP(cluster, cpu) (0x140 + (cluster) * 0x10 + (cpu) * 0x4)
#define PRCM_CPU_SOFT_ENTRY 0x164
#define CPUCFG_BASE 0x01f01c00
#define CPUCFG_SIZE 0x400
#define CPUCFG_CL_RST(cluster) (0x30 + (cluster) * 0x4)
#define CPUCFG_P_REG0 0x1a4
#define CPUXCFG_BASE 0x01700000
#define CPUXCFG_SIZE 0x400
#define CPUXCFG_CL_RST(cluster) (0x80 + (cluster) * 0x4)
#define CPUXCFG_CL_RST_SOC_DBG_RST __BIT(24)
#define CPUXCFG_CL_RST_ETM_RST(cpu) __BIT(20 + (cpu))
#define CPUXCFG_CL_RST_DBG_RST(cpu) __BIT(16 + (cpu))
#define CPUXCFG_CL_RST_H_RST __BIT(12)
#define CPUXCFG_CL_RST_L2_RST __BIT(8)
#define CPUXCFG_CL_RST_CX_RST(cpu) __BIT(4 + (cpu))
#define CPUXCFG_CL_CTRL0(cluster) (0x0 + (cluster) * 0x10)
#define CPUXCFG_CL_CTRL1(cluster) (0x4 + (cluster) * 0x10)
#define CPUXCFG_CL_CTRL1_ACINACTM __BIT(0)
#define A80_CCI_BASE 0x01c90000
#define A83T_CCI_BASE 0x01790000
#define CCI_SLAVEIF3_OFFSET 0x4000
#define CCI_SLAVEIF4_OFFSET 0x5000
extern struct bus_space arm_generic_bs_tag;
enum sunxi_mc_soc {
MC_SOC_A80,
MC_SOC_A83T
};
enum sunxi_mc_cpu {
MC_CORE_CA7,
MC_CORE_CA15
};
uint32_t sunxi_mc_cci_port[MAXCPUS];
static uint32_t
sunxi_mc_smp_pa(void)
{
extern void sunxi_mc_mpstart(void);
bool ok __diagused;
paddr_t pa;
ok = pmap_extract(pmap_kernel(), (vaddr_t)sunxi_mc_mpstart, &pa);
KASSERT(ok);
return pa;
}
static int
sunxi_mc_smp_start(bus_space_tag_t bst, bus_space_handle_t prcm, bus_space_handle_t cpucfg,
bus_space_handle_t cpuxcfg, u_int cluster, u_int cpu, enum sunxi_mc_soc soc,
enum sunxi_mc_cpu core)
{
uint32_t val;
int i;
val = bus_space_read_4(bst, cpuxcfg, CPUXCFG_CL_RST(cluster));
val &= ~__BIT(cpu);
bus_space_write_4(bst, cpuxcfg, CPUXCFG_CL_RST(cluster), val);
if (soc == MC_SOC_A83T) {
val = bus_space_read_4(bst, cpucfg, CPUCFG_CL_RST(cluster));
val &= ~__BIT(cpu);
bus_space_write_4(bst, cpucfg, CPUCFG_CL_RST(cluster), val);
}
if (core == MC_CORE_CA7) {
val = bus_space_read_4(bst, cpuxcfg, CPUXCFG_CL_CTRL0(cluster));
val &= ~__BIT(cpu);
bus_space_write_4(bst, cpuxcfg, CPUXCFG_CL_CTRL0(cluster), val);
}
for (i = 0; i <= 8; i++) {
bus_space_write_4(bst, prcm, PRCM_CL_PWR_CLAMP(cluster, cpu), 0xff >> i);
delay(10);
}
for (i = 100000; i > 0; i--) {
if (bus_space_read_4(bst, prcm, PRCM_CL_PWR_CLAMP(cluster, cpu)) == 0)
break;
}
if (i == 0) {
printf("CPU %#llx failed to start\n", __SHIFTIN(cluster, MPIDR_AFF1) | __SHIFTIN(cpu, MPIDR_AFF0));
return ETIMEDOUT;
}
val = bus_space_read_4(bst, prcm, PRCM_CL_PWROFF(cluster));
if (soc == MC_SOC_A83T) {
if (cpu == 0)
val &= ~__BIT(4);
else
val &= ~__BIT(cpu);
val &= ~__BIT(0);
} else {
val &= ~__BIT(cpu);
val &= ~__BIT(4);
}
bus_space_write_4(bst, prcm, PRCM_CL_PWROFF(cluster), val);
val = bus_space_read_4(bst, prcm, PRCM_CL_RST_CTRL(cluster));
val |= __BIT(cpu);
bus_space_write_4(bst, prcm, PRCM_CL_RST_CTRL(cluster), val);
if (soc == MC_SOC_A83T) {
val = bus_space_read_4(bst, cpucfg, CPUCFG_CL_RST(cluster));
val |= __BIT(cpu);
bus_space_write_4(bst, cpucfg, CPUCFG_CL_RST(cluster), val);
delay(10);
}
val = bus_space_read_4(bst, cpuxcfg, CPUXCFG_CL_RST(cluster));
val |= __BIT(cpu);
val |= CPUXCFG_CL_RST_SOC_DBG_RST;
if (core == MC_CORE_CA7)
val |= CPUXCFG_CL_RST_ETM_RST(cpu);
else
val |= CPUXCFG_CL_RST_CX_RST(cpu);
val |= CPUXCFG_CL_RST_DBG_RST(cpu);
val |= CPUXCFG_CL_RST_L2_RST;
val |= CPUXCFG_CL_RST_H_RST;
bus_space_write_4(bst, cpuxcfg, CPUXCFG_CL_RST(cluster), val);
val = bus_space_read_4(bst, cpuxcfg, CPUXCFG_CL_CTRL1(cluster));
val &= ~CPUXCFG_CL_CTRL1_ACINACTM;
bus_space_write_4(bst, cpuxcfg, CPUXCFG_CL_CTRL1(cluster), val);
return 0;
}
int
sun8i_a83t_smp_enable(u_int mpidr)
{
bus_space_tag_t bst = &arm_generic_bs_tag;
bus_space_handle_t prcm, cpucfg, cpuxcfg;
int error;
const u_int cluster = __SHIFTOUT(mpidr, MPIDR_AFF1);
const u_int cpu = __SHIFTOUT(mpidr, MPIDR_AFF0);
if (bus_space_map(bst, A83T_PRCM_BASE, A83T_PRCM_SIZE, 0, &prcm) != 0 ||
bus_space_map(bst, CPUCFG_BASE, CPUCFG_SIZE, 0, &cpucfg) != 0 ||
bus_space_map(bst, CPUXCFG_BASE, CPUXCFG_SIZE, 0, &cpuxcfg) != 0)
return ENOMEM;
for (int i = 0; i < 4; i++)
sunxi_mc_cci_port[i] = A83T_CCI_BASE + CCI_SLAVEIF3_OFFSET;
for (int i = 4; i < 8; i++)
sunxi_mc_cci_port[i] = A83T_CCI_BASE + CCI_SLAVEIF4_OFFSET;
bus_space_write_4(bst, cpucfg, CPUCFG_P_REG0, sunxi_mc_smp_pa());
cpu_idcache_wbinv_all();
error = sunxi_mc_smp_start(bst, prcm, cpucfg, cpuxcfg, cluster, cpu,
MC_SOC_A83T, MC_CORE_CA7);
bus_space_unmap(bst, cpuxcfg, CPUXCFG_SIZE);
bus_space_unmap(bst, cpucfg, CPUCFG_SIZE);
bus_space_unmap(bst, prcm, A83T_PRCM_SIZE);
return error;
}
int
sun9i_a80_smp_enable(u_int mpidr)
{
bus_space_tag_t bst = &arm_generic_bs_tag;
bus_space_handle_t prcm, cpuxcfg;
int error;
const u_int cluster = __SHIFTOUT(mpidr, MPIDR_AFF1);
const u_int cpu = __SHIFTOUT(mpidr, MPIDR_AFF0);
if (bus_space_map(bst, A80_PRCM_BASE, A80_PRCM_SIZE, 0, &prcm) != 0 ||
bus_space_map(bst, CPUXCFG_BASE, CPUXCFG_SIZE, 0, &cpuxcfg) != 0)
return ENOMEM;
for (int i = 0; i < 4; i++)
sunxi_mc_cci_port[i] = A80_CCI_BASE + CCI_SLAVEIF3_OFFSET;
for (int i = 4; i < 8; i++)
sunxi_mc_cci_port[i] = A80_CCI_BASE + CCI_SLAVEIF4_OFFSET;
bus_space_write_4(bst, prcm, PRCM_CPU_SOFT_ENTRY, sunxi_mc_smp_pa());
cpu_idcache_wbinv_all();
error = sunxi_mc_smp_start(bst, prcm, 0, cpuxcfg, cluster, cpu,
MC_SOC_A80, cluster == 0 ? MC_CORE_CA7 : MC_CORE_CA15);
bus_space_unmap(bst, cpuxcfg, CPUXCFG_SIZE);
bus_space_unmap(bst, prcm, A80_PRCM_SIZE);
return error;
}