/*	$OpenBSD: cpu.c,v 1.82 2015/06/04 18:01:44 kettenis Exp $ */

/*
 * Copyright (c) 1997 Per Fogelstrom
 * Copyright (c) 1997 RTMX Inc
 *
 * 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.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed under OpenBSD for RTMX Inc
 *	North Carolina, USA, by Per Fogelstrom, Opsycon AB, Sweden.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
 *
 */

#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 <dev/rndvar.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 <powerpc/hid.h>

/* SCOM addresses (24-bit) */
#define SCOM_PCR	0x0aa001 /* Power Control Register */
#define SCOM_PSR	0x408001 /* Power Tuning Status Register */

/* SCOMC format */
#define SCOMC_ADDR_SHIFT	8
#define SCOMC_ADDR_MASK		0xffff0000
#define SCOMC_READ		0x00008000

/* Power (Tuning) Status Register */
#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;	/* cpu architecture */

/* Definition of the driver for autoconfig. */
int	cpumatch(struct device *, void *, void *);
void	cpuattach(struct device *, struct device *, void *);

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;

	/* make sure that we're looking for a CPU. */
	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();

	/* Clear PCRH and PCR. */
	ppc_mtscomd(0x00000000);
	ppc_mtscomc(SCOM_PCR << SCOMC_ADDR_SHIFT);
	ppc_mtscomd(0x80000000);
	ppc_mtscomc(SCOM_PCR << SCOMC_ADDR_SHIFT);

	/* Set PCR. */
	ppc_mtscomd(ppc_power_mode_data[freq_scale] | 0x80000000);
	ppc_mtscomc(SCOM_PCR << SCOMC_ADDR_SHIFT);

	/* Wait until frequency change is completed. */
	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_intrdepth = -1;
	ci->ci_dev = dev;

	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;
	}
	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) {
		/* Openfirmware stores clock in Hz, not MHz */
		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;
		}
	}

	/* power savings mode */
	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:
		/* select DOZE mode */
		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:
		/* select NAP mode */
		hid0 &= ~(HID0_DOZE | HID0_SLEEP);
		hid0 |= HID0_NAP | HID0_DPM;
		/* try some other flags */
		hid0 |= HID0_SGE | HID0_BTIC;
		hid0 |= HID0_LRSTK | HID0_FOLD | HID0_BHT;
		/* Disable BTIC on 7450 Rev 2.0 or earlier */
		if (cpu == PPC_CPU_MPC7450 && (pvr & 0xffff) < 0x0200)
			hid0 &= ~HID0_BTIC;
		ppc_cpuidle = 1;
		break;
	case PPC_CPU_IBM970:
	case PPC_CPU_IBM970FX:
		/* select NAP mode */
		hid0 &= ~(HID0_DOZE | HID0_DEEPNAP);
		hid0 |= HID0_NAP | HID0_DPM;
		ppc_cpuidle = 1;
		break;
	case PPC_CPU_IBM970MP:
		/* select DEEPNAP mode, which requires NAP */
		hid0 &= ~HID0_DOZE;
		hid0 |= HID0_DEEPNAP | HID0_NAP | HID0_DPM;
		ppc_cpuidle = 1;
		break;
	}
	ppc_mthid0(hid0);

	/* if processor is G3 or G4, configure L2 cache */
	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");
}

/* L2CR bit definitions */
#define L2CR_L2E        0x80000000 /* 0: L2 enable */
#define L2CR_L2PE       0x40000000 /* 1: L2 data parity enable */
#define L2CR_L2SIZ      0x30000000 /* 2-3: L2 size */
#define  L2SIZ_RESERVED         0x00000000
#define  L2SIZ_256K             0x10000000
#define  L2SIZ_512K             0x20000000
#define  L2SIZ_1M       0x30000000
#define L2CR_L2CLK      0x0e000000 /* 4-6: L2 clock ratio */
#define  L2CLK_DIS              0x00000000 /* disable L2 clock */
#define  L2CLK_10               0x02000000 /* core clock / 1   */
#define  L2CLK_15               0x04000000 /*            / 1.5 */
#define  L2CLK_20               0x08000000 /*            / 2   */
#define  L2CLK_25               0x0a000000 /*            / 2.5 */
#define  L2CLK_30               0x0c000000 /*            / 3   */
#define L2CR_L2RAM      0x01800000 /* 7-8: L2 RAM type */
#define  L2RAM_FLOWTHRU_BURST   0x00000000
#define  L2RAM_PIPELINE_BURST   0x01000000
#define  L2RAM_PIPELINE_LATE    0x01800000
#define L2CR_L2DO       0x00400000 /* 9: L2 data-only.
                                      Setting this bit disables instruction
                                      caching. */
#define L2CR_L2I        0x00200000 /* 10: L2 global invalidate. */
#define L2CR_L2CTL      0x00100000 /* 11: L2 RAM control (ZZ enable).
                                      Enables automatic operation of the
                                      L2ZZ (low-power mode) signal. */
#define L2CR_L2WT       0x00080000 /* 12: L2 write-through. */
#define L2CR_L2TS       0x00040000 /* 13: L2 test support. */
#define L2CR_L2OH       0x00030000 /* 14-15: L2 output hold. */
#define L2CR_L2SL       0x00008000 /* 16: L2 DLL slow. */
#define L2CR_L2DF       0x00004000 /* 17: L2 differential clock. */
#define L2CR_L2BYP      0x00002000 /* 18: L2 DLL bypass. */
#define L2CR_L2IP       0x00000001 /* 31: L2 global invalidate in progress
				       (read only). */
#ifdef L2CR_CONFIG
u_int l2cr_config = L2CR_CONFIG;
#else
u_int l2cr_config = 0;
#endif

/* L3CR bit definitions */
#define   L3CR_L3E                0x80000000 /*  0: L3 enable */
#define   L3CR_L3SIZ              0x10000000 /*  3: L3 size (0=1MB, 1=2MB) */

void
config_l2cr(int cpu)
{
	u_int l2cr, x;

	l2cr = ppc_mfl2cr();

	/*
	 * Configure L2 cache if not enabled.
	 */
	if ((l2cr & L2CR_L2E) == 0 && l2cr_config != 0) {
		l2cr = l2cr_config;
		ppc_mtl2cr(l2cr);

		/* Wait for L2 clock to be stable (640 L2 clocks). */
		delay(100);

		/* Invalidate all L2 contents. */
		l2cr |= L2CR_L2I;
		ppc_mtl2cr(l2cr);
		do {
			x = ppc_mfl2cr();
		} while (x & L2CR_L2IP);

		/* Enable L2 cache. */
		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)		/* 8K interrupt stack */

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;

/*
 * XXX Due to a bug in our OpenFirmware interface/memory mapping,
 * machines with 64bit CPUs hang in the OF_finddevice() call below
 * if this array is stored on the 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;

        /*
         * Allocate some contiguous pages for the interrupt stack
         * from the lowest 256MB (because bat0 always maps it va == pa).
         */
        size += INTSTK;
        size += 8192;   /* SPILLSTK(1k) + DDBSTK(7k) */

	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");

	/* XXX OpenPIC */
	{
		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 {
			/* Start secondary CPU. */
			reset_cpu = mapiodev(0x80000000 + 0x5c, 1);
			*reset_cpu = 0x4;
			__asm volatile ("eieio" ::: "memory");
			*reset_cpu = 0x0;
			__asm volatile ("eieio" ::: "memory");
		}

		/* Sync timebase. */
		h->tb = ppc_mftb() + 100000;	/* 3ms @ 33MHz  */

		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;

		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, s;

        /* Initialize timebase. */
	ppc_mttb(0);

	/* Initialize curcpu(). */
	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) {
		/*
		 * The Hardware Interrupt Offset Register should be
		 * cleared after initialization.
		 */
		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);

		/* Wait for L2 clock to be stable (640 L2 clocks). */
		delay(100);

		/* Invalidate all L2 contents. */
		ppc_mtl2cr((h->l2cr & ~L2CR_L2E)|L2CR_L2I);
		do {
			x = ppc_mfl2cr();
		} while (x & L2CR_L2IP);

		ppc_mtl2cr(h->l2cr);
	}

	/*
	 * Now enable translation (and machine checks/recoverable interrupts).
	 */
	pmap_enable_mmu();

	/* XXX OpenPIC */
	{
		/* Sync timebase. */
		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;

	s = splhigh();
	nanouptime(&curcpu()->ci_schedstate.spc_runtime);
	splx(s);

	intrstate = ppc_intr_disable();
	cpu_startclock();
	ppc_intr_enable(intrstate);

	/* Enable inter-processor interrupts. */
	openpic_set_priority(curcpu()->ci_cpuid, 14);

	SCHED_LOCK(s);
	cpu_switchto(NULL, sched_chooseproc());
}
#endif
