#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <sys/task.h>
#include <sys/device.h>
#include <uvm/uvm_extern.h>
#include <dev/ofw/openfirm.h>
#include <machine/autoconf.h>
#include <powerpc/bat.h>
#include <machine/cpu.h>
#include <machine/trap.h>
#include <machine/elf.h>
#include <powerpc/hid.h>
#define SCOM_PCR 0x0aa001
#define SCOM_PSR 0x408001
#define SCOMC_ADDR_SHIFT 8
#define SCOMC_ADDR_MASK 0xffff0000
#define SCOMC_READ 0x00008000
#define PSR_CMD_RECEIVED 0x2000000000000000LL
#define PSR_CMD_COMPLETED 0x1000000000000000LL
#define PSR_FREQ_MASK 0x0300000000000000LL
#define PSR_FREQ_HALF 0x0100000000000000LL
struct cpu_info cpu_info[PPC_MAXPROCS];
char cpu_model[80];
char machine[] = MACHINE;
int cpumatch(struct device *, void *, void *);
void cpuattach(struct device *, struct device *, void *);
const struct cfattach cpu_ca = {
sizeof(struct device), cpumatch, cpuattach
};
struct cfdriver cpu_cd = {
NULL, "cpu", DV_DULL
};
void ppc64_scale_frequency(u_int);
void (*ppc64_slew_voltage)(u_int);
void ppc64_setperf(int);
void config_l2cr(int);
int
cpumatch(struct device *parent, void *cfdata, void *aux)
{
struct confargs *ca = aux;
int *reg = ca->ca_reg;
if (strcmp(ca->ca_name, cpu_cd.cd_name) != 0)
return (0);
if (reg[0] >= PPC_MAXPROCS)
return (0);
return (1);
}
u_int32_t ppc_curfreq;
u_int32_t ppc_maxfreq;
int
ppc_cpuspeed(int *freq)
{
*freq = ppc_curfreq;
return (0);
}
static u_int32_t ppc_power_mode_data[2];
void
ppc64_scale_frequency(u_int freq_scale)
{
u_int64_t psr;
int s;
s = ppc_intr_disable();
ppc_mtscomd(0x00000000);
ppc_mtscomc(SCOM_PCR << SCOMC_ADDR_SHIFT);
ppc_mtscomd(0x80000000);
ppc_mtscomc(SCOM_PCR << SCOMC_ADDR_SHIFT);
ppc_mtscomd(ppc_power_mode_data[freq_scale] | 0x80000000);
ppc_mtscomc(SCOM_PCR << SCOMC_ADDR_SHIFT);
do {
ppc64_mtscomc((SCOM_PSR << SCOMC_ADDR_SHIFT) | SCOMC_READ);
psr = ppc64_mfscomd();
ppc64_mfscomc();
if (psr & PSR_CMD_COMPLETED)
break;
DELAY(100);
} while (psr & PSR_CMD_RECEIVED);
if ((psr & PSR_FREQ_MASK) == PSR_FREQ_HALF)
ppc_curfreq = ppc_maxfreq / 2;
else
ppc_curfreq = ppc_maxfreq;
ppc_intr_enable(s);
}
extern int perflevel;
struct task ppc64_setperf_task;
int ppc64_perflevel;
void
ppc64_do_setperf(void *arg)
{
if (ppc64_perflevel <= 50) {
if (ppc_curfreq == ppc_maxfreq / 2)
return;
ppc64_scale_frequency(FREQ_HALF);
if (ppc64_slew_voltage)
ppc64_slew_voltage(FREQ_HALF);
} else {
if (ppc_curfreq == ppc_maxfreq)
return;
if (ppc64_slew_voltage)
ppc64_slew_voltage(FREQ_FULL);
ppc64_scale_frequency(FREQ_FULL);
}
}
void
ppc64_setperf(int level)
{
ppc64_perflevel = level;
task_add(systq, &ppc64_setperf_task);
}
void
cpuattach(struct device *parent, struct device *dev, void *aux)
{
struct confargs *ca = aux;
int *reg = ca->ca_reg;
u_int32_t cpu, pvr, hid0;
char name[32];
int qhandle, phandle, len;
u_int32_t clock_freq = 0, timebase = 0;
struct cpu_info *ci;
ci = &cpu_info[reg[0]];
ci->ci_cpuid = reg[0];
ci->ci_dev = dev;
hwcap = PPC_FEATURE_32 | PPC_FEATURE_HAS_FPU | PPC_FEATURE_HAS_MMU;
pvr = ppc_mfpvr();
cpu = pvr >> 16;
switch (cpu) {
case PPC_CPU_MPC601:
snprintf(cpu_model, sizeof(cpu_model), "601");
break;
case PPC_CPU_MPC603:
snprintf(cpu_model, sizeof(cpu_model), "603");
break;
case PPC_CPU_MPC604:
snprintf(cpu_model, sizeof(cpu_model), "604");
break;
case PPC_CPU_MPC603e:
snprintf(cpu_model, sizeof(cpu_model), "603e");
break;
case PPC_CPU_MPC603ev:
snprintf(cpu_model, sizeof(cpu_model), "603ev");
break;
case PPC_CPU_MPC750:
snprintf(cpu_model, sizeof(cpu_model), "750");
break;
case PPC_CPU_MPC604ev:
snprintf(cpu_model, sizeof(cpu_model), "604ev");
break;
case PPC_CPU_MPC7400:
ppc_altivec = 1;
snprintf(cpu_model, sizeof(cpu_model), "7400");
break;
case PPC_CPU_MPC7447A:
ppc_altivec = 1;
snprintf(cpu_model, sizeof(cpu_model), "7447A");
break;
case PPC_CPU_MPC7448:
ppc_altivec = 1;
snprintf(cpu_model, sizeof(cpu_model), "7448");
break;
case PPC_CPU_IBM970:
ppc_altivec = 1;
snprintf(cpu_model, sizeof(cpu_model), "970");
break;
case PPC_CPU_IBM970FX:
ppc_altivec = 1;
snprintf(cpu_model, sizeof(cpu_model), "970FX");
break;
case PPC_CPU_IBM970MP:
ppc_altivec = 1;
snprintf(cpu_model, sizeof(cpu_model), "970MP");
break;
case PPC_CPU_IBM750FX:
snprintf(cpu_model, sizeof(cpu_model), "750FX");
break;
case PPC_CPU_MPC7410:
ppc_altivec = 1;
snprintf(cpu_model, sizeof(cpu_model), "7410");
break;
case PPC_CPU_MPC7450:
ppc_altivec = 1;
if ((pvr & 0xf) < 3)
snprintf(cpu_model, sizeof(cpu_model), "7450");
else
snprintf(cpu_model, sizeof(cpu_model), "7451");
break;
case PPC_CPU_MPC7455:
ppc_altivec = 1;
snprintf(cpu_model, sizeof(cpu_model), "7455");
break;
case PPC_CPU_MPC7457:
ppc_altivec = 1;
snprintf(cpu_model, sizeof(cpu_model), "7457");
break;
default:
snprintf(cpu_model, sizeof(cpu_model), "Version %x", cpu);
break;
}
#ifndef ALTIVEC
ppc_altivec = 0;
#endif
if (ppc_altivec)
hwcap |= PPC_FEATURE_HAS_ALTIVEC;
snprintf(cpu_model + strlen(cpu_model),
sizeof(cpu_model) - strlen(cpu_model),
" (Revision 0x%x)", pvr & 0xffff);
printf(": %s", cpu_model);
for (qhandle = OF_peer(0); qhandle; qhandle = phandle) {
len = OF_getprop(qhandle, "device_type", name, sizeof(name));
if (len >= 0 && strcmp(name, "cpu") == 0) {
OF_getprop(qhandle, "clock-frequency", &clock_freq,
sizeof(clock_freq));
OF_getprop(qhandle, "timebase-frequency", &timebase,
sizeof(timebase));
break;
}
if ((phandle = OF_child(qhandle)))
continue;
while (qhandle) {
if ((phandle = OF_peer(qhandle)))
break;
qhandle = OF_parent(qhandle);
}
}
if (timebase != 0) {
ticks_per_sec = timebase;
ns_per_tick = 1000000000 / ticks_per_sec;
}
if (clock_freq != 0) {
clock_freq /= 1000000;
printf(": %d MHz", clock_freq);
ppc_curfreq = ppc_maxfreq = clock_freq;
cpu_cpuspeed = ppc_cpuspeed;
}
if (cpu == PPC_CPU_IBM970FX) {
u_int64_t psr;
int s;
s = ppc_intr_disable();
ppc64_mtscomc((SCOM_PSR << SCOMC_ADDR_SHIFT) | SCOMC_READ);
psr = ppc64_mfscomd();
ppc64_mfscomc();
ppc_intr_enable(s);
if ((psr & PSR_FREQ_MASK) == PSR_FREQ_HALF) {
ppc_curfreq = ppc_maxfreq / 2;
perflevel = 50;
}
if (OF_getprop(qhandle, "power-mode-data",
&ppc_power_mode_data, sizeof ppc_power_mode_data) >= 8) {
task_set(&ppc64_setperf_task, ppc64_do_setperf, NULL);
cpu_setperf = ppc64_setperf;
}
}
hid0 = ppc_mfhid0();
switch (cpu) {
case PPC_CPU_MPC603:
case PPC_CPU_MPC603e:
case PPC_CPU_MPC750:
case PPC_CPU_MPC7400:
case PPC_CPU_IBM750FX:
case PPC_CPU_MPC7410:
hid0 &= ~(HID0_NAP | HID0_SLEEP);
hid0 |= HID0_DOZE | HID0_DPM;
ppc_cpuidle = 1;
break;
case PPC_CPU_MPC7447A:
case PPC_CPU_MPC7448:
case PPC_CPU_MPC7450:
case PPC_CPU_MPC7455:
case PPC_CPU_MPC7457:
hid0 &= ~(HID0_DOZE | HID0_SLEEP);
hid0 |= HID0_NAP | HID0_DPM;
hid0 |= HID0_SGE | HID0_BTIC;
hid0 |= HID0_LRSTK | HID0_FOLD | HID0_BHT;
if (cpu == PPC_CPU_MPC7450 && (pvr & 0xffff) < 0x0200)
hid0 &= ~HID0_BTIC;
ppc_cpuidle = 1;
break;
case PPC_CPU_IBM970:
case PPC_CPU_IBM970FX:
hid0 &= ~(HID0_DOZE | HID0_DEEPNAP);
hid0 |= HID0_NAP | HID0_DPM;
ppc_cpuidle = 1;
break;
case PPC_CPU_IBM970MP:
hid0 &= ~HID0_DOZE;
hid0 |= HID0_DEEPNAP | HID0_NAP | HID0_DPM;
ppc_cpuidle = 1;
break;
}
ppc_mthid0(hid0);
switch (cpu) {
case PPC_CPU_MPC750:
case PPC_CPU_MPC7400:
case PPC_CPU_IBM750FX:
case PPC_CPU_MPC7410:
case PPC_CPU_MPC7447A:
case PPC_CPU_MPC7448:
case PPC_CPU_MPC7450:
case PPC_CPU_MPC7455:
case PPC_CPU_MPC7457:
config_l2cr(cpu);
break;
}
printf("\n");
}
#define L2CR_L2E 0x80000000
#define L2CR_L2PE 0x40000000
#define L2CR_L2SIZ 0x30000000
#define L2SIZ_RESERVED 0x00000000
#define L2SIZ_256K 0x10000000
#define L2SIZ_512K 0x20000000
#define L2SIZ_1M 0x30000000
#define L2CR_L2CLK 0x0e000000
#define L2CLK_DIS 0x00000000
#define L2CLK_10 0x02000000
#define L2CLK_15 0x04000000
#define L2CLK_20 0x08000000
#define L2CLK_25 0x0a000000
#define L2CLK_30 0x0c000000
#define L2CR_L2RAM 0x01800000
#define L2RAM_FLOWTHRU_BURST 0x00000000
#define L2RAM_PIPELINE_BURST 0x01000000
#define L2RAM_PIPELINE_LATE 0x01800000
#define L2CR_L2DO 0x00400000
#define L2CR_L2I 0x00200000
#define L2CR_L2CTL 0x00100000
#define L2CR_L2WT 0x00080000
#define L2CR_L2TS 0x00040000
#define L2CR_L2OH 0x00030000
#define L2CR_L2SL 0x00008000
#define L2CR_L2DF 0x00004000
#define L2CR_L2BYP 0x00002000
#define L2CR_L2IP 0x00000001
#ifdef L2CR_CONFIG
u_int l2cr_config = L2CR_CONFIG;
#else
u_int l2cr_config = 0;
#endif
#define L3CR_L3E 0x80000000
#define L3CR_L3SIZ 0x10000000
void
config_l2cr(int cpu)
{
u_int l2cr, x;
l2cr = ppc_mfl2cr();
if ((l2cr & L2CR_L2E) == 0 && l2cr_config != 0) {
l2cr = l2cr_config;
ppc_mtl2cr(l2cr);
delay(100);
l2cr |= L2CR_L2I;
ppc_mtl2cr(l2cr);
do {
x = ppc_mfl2cr();
} while (x & L2CR_L2IP);
l2cr &= ~L2CR_L2I;
l2cr |= L2CR_L2E;
ppc_mtl2cr(l2cr);
}
if (l2cr & L2CR_L2E) {
if (cpu == PPC_CPU_MPC7450 || cpu == PPC_CPU_MPC7455) {
u_int l3cr;
printf(": 256KB L2 cache");
l3cr = ppc_mfl3cr();
if (l3cr & L3CR_L3E)
printf(", %cMB L3 cache",
l3cr & L3CR_L3SIZ ? '2' : '1');
} else if (cpu == PPC_CPU_IBM750FX ||
cpu == PPC_CPU_MPC7447A || cpu == PPC_CPU_MPC7457)
printf(": 512KB L2 cache");
else if (cpu == PPC_CPU_MPC7448)
printf(": 1MB L2 cache");
else {
switch (l2cr & L2CR_L2SIZ) {
case L2SIZ_256K:
printf(": 256KB");
break;
case L2SIZ_512K:
printf(": 512KB");
break;
case L2SIZ_1M:
printf(": 1MB");
break;
default:
printf(": unknown size");
}
printf(" backside cache");
}
#if 0
switch (l2cr & L2CR_L2RAM) {
case L2RAM_FLOWTHRU_BURST:
printf(" Flow-through synchronous burst SRAM");
break;
case L2RAM_PIPELINE_BURST:
printf(" Pipelined synchronous burst SRAM");
break;
case L2RAM_PIPELINE_LATE:
printf(" Pipelined synchronous late-write SRAM");
break;
default:
printf(" unknown type");
}
if (l2cr & L2CR_L2PE)
printf(" with parity");
#endif
} else
printf(": L2 cache not enabled");
}
#ifdef MULTIPROCESSOR
#define INTSTK (8*1024)
int cpu_spinup(struct device *, struct cpu_info *);
void cpu_hatch(void);
void cpu_spinup_trampoline(void);
struct cpu_hatch_data {
uint64_t tb;
struct cpu_info *ci;
uint32_t hid0;
uint64_t hid1;
uint64_t hid4;
uint64_t hid5;
int l2cr;
int running;
};
volatile struct cpu_hatch_data *cpu_hatch_data;
volatile void *cpu_hatch_stack;
char cpuname[64];
int
cpu_spinup(struct device *self, struct cpu_info *ci)
{
volatile struct cpu_hatch_data hatch_data, *h = &hatch_data;
int i;
struct pglist mlist;
struct vm_page *m;
int error;
int size = 0;
char *cp;
u_char *reset_cpu;
u_int node;
size += INTSTK;
size += 8192;
TAILQ_INIT(&mlist);
error = uvm_pglistalloc(size, 0x0, 0x10000000 - 1, 0, 0,
&mlist, 1, UVM_PLA_WAITOK);
if (error) {
printf(": unable to allocate idle stack\n");
return -1;
}
m = TAILQ_FIRST(&mlist);
cp = (char *)VM_PAGE_TO_PHYS(m);
bzero(cp, size);
ci->ci_intstk = cp + INTSTK;
cpu_hatch_stack = ci->ci_intstk - sizeof(struct trapframe);
h->ci = ci;
h->running = 0;
h->hid0 = ppc_mfhid0();
if (ppc_proc_is_64b) {
h->hid1 = ppc64_mfhid1();
h->hid4 = ppc64_mfhid4();
h->hid5 = ppc64_mfhid5();
} else {
h->l2cr = ppc_mfl2cr();
}
cpu_hatch_data = h;
__asm volatile ("sync; isync");
{
int off;
*(u_int *)EXC_RST = 0x48000002 | (u_int)cpu_spinup_trampoline;
syncicache((void *)EXC_RST, 0x100);
h->running = -1;
snprintf(cpuname, sizeof(cpuname), "/cpus/@%x", ci->ci_cpuid);
node = OF_finddevice(cpuname);
if (node == -1) {
printf(": unable to locate OF node %s\n", cpuname);
return -1;
}
if (OF_getprop(node, "soft-reset", &off, 4) == 4) {
reset_cpu = mapiodev(0x80000000 + off, 1);
*reset_cpu = 0x4;
__asm volatile ("eieio" ::: "memory");
*reset_cpu = 0x0;
__asm volatile ("eieio" ::: "memory");
} else {
reset_cpu = mapiodev(0x80000000 + 0x5c, 1);
*reset_cpu = 0x4;
__asm volatile ("eieio" ::: "memory");
*reset_cpu = 0x0;
__asm volatile ("eieio" ::: "memory");
}
h->tb = ppc_mftb() + 100000;
while (h->tb > ppc_mftb())
;
__asm volatile ("sync; isync");
h->running = 0;
delay(500000);
}
for (i = 0; i < 0x3fffffff; i++)
if (h->running) {
break;
}
return 0;
}
volatile static int start_secondary_cpu;
void
cpu_boot_secondary_processors(void)
{
struct cpu_info *ci;
int i;
for (i = 0; i < PPC_MAXPROCS; i++) {
ci = &cpu_info[i];
if (ci->ci_cpuid == 0)
continue;
ci->ci_randseed = (arc4random() & 0x7fffffff) + 1;
clockqueue_init(&ci->ci_queue);
sched_init_cpu(ci);
cpu_spinup(NULL, ci);
}
start_secondary_cpu = 1;
__asm volatile ("sync");
}
void cpu_startclock(void);
void
cpu_hatch(void)
{
volatile struct cpu_hatch_data *h = cpu_hatch_data;
int intrstate;
ppc_mttb(0);
ppc_mtsprg0((u_int)h->ci);
ppc_mtibat0u(0);
ppc_mtibat1u(0);
ppc_mtibat2u(0);
ppc_mtibat3u(0);
ppc_mtdbat0u(0);
ppc_mtdbat1u(0);
ppc_mtdbat2u(0);
ppc_mtdbat3u(0);
if (ppc_proc_is_64b) {
ppc_mthior(0);
__asm volatile ("sync");
ppc_mthid0(h->hid0);
ppc64_mthid1(h->hid1);
ppc64_mthid4(h->hid4);
ppc64_mthid5(h->hid5);
} else if (h->l2cr != 0) {
u_int x;
ppc_mthid0(h->hid0);
ppc_mtl2cr(h->l2cr & ~L2CR_L2E);
delay(100);
ppc_mtl2cr((h->l2cr & ~L2CR_L2E)|L2CR_L2I);
do {
x = ppc_mfl2cr();
} while (x & L2CR_L2IP);
ppc_mtl2cr(h->l2cr);
}
pmap_enable_mmu();
{
while (h->running == -1)
;
__asm volatile ("sync; isync");
ppc_mttb(h->tb);
}
ncpus++;
h->running = 1;
__asm volatile ("eieio" ::: "memory");
while (start_secondary_cpu == 0)
;
__asm volatile ("sync; isync");
curcpu()->ci_ipending = 0;
curcpu()->ci_cpl = 0;
intrstate = ppc_intr_disable();
cpu_startclock();
ppc_intr_enable(intrstate);
openpic_set_priority(curcpu()->ci_cpuid, 14);
sched_toidle();
}
#endif