18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * SMP support for power macintosh.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * We support both the old "powersurge" SMP architecture
68c2ecf20Sopenharmony_ci * and the current Core99 (G4 PowerMac) machines.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Note that we don't support the very first rev. of
98c2ecf20Sopenharmony_ci * Apple/DayStar 2 CPUs board, the one with the funky
108c2ecf20Sopenharmony_ci * watchdog. Hopefully, none of these should be there except
118c2ecf20Sopenharmony_ci * maybe internally to Apple. I should probably still add some
128c2ecf20Sopenharmony_ci * code to detect this card though and disable SMP. --BenH.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * Support Macintosh G4 SMP by Troy Benjegerdes (hozer@drgw.net)
158c2ecf20Sopenharmony_ci * and Ben Herrenschmidt <benh@kernel.crashing.org>.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * Support for DayStar quad CPU cards
188c2ecf20Sopenharmony_ci * Copyright (C) XLR8, Inc. 1994-2000
198c2ecf20Sopenharmony_ci */
208c2ecf20Sopenharmony_ci#include <linux/kernel.h>
218c2ecf20Sopenharmony_ci#include <linux/sched.h>
228c2ecf20Sopenharmony_ci#include <linux/sched/hotplug.h>
238c2ecf20Sopenharmony_ci#include <linux/smp.h>
248c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
258c2ecf20Sopenharmony_ci#include <linux/kernel_stat.h>
268c2ecf20Sopenharmony_ci#include <linux/delay.h>
278c2ecf20Sopenharmony_ci#include <linux/init.h>
288c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
298c2ecf20Sopenharmony_ci#include <linux/errno.h>
308c2ecf20Sopenharmony_ci#include <linux/hardirq.h>
318c2ecf20Sopenharmony_ci#include <linux/cpu.h>
328c2ecf20Sopenharmony_ci#include <linux/compiler.h>
338c2ecf20Sopenharmony_ci#include <linux/pgtable.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include <asm/ptrace.h>
368c2ecf20Sopenharmony_ci#include <linux/atomic.h>
378c2ecf20Sopenharmony_ci#include <asm/code-patching.h>
388c2ecf20Sopenharmony_ci#include <asm/irq.h>
398c2ecf20Sopenharmony_ci#include <asm/page.h>
408c2ecf20Sopenharmony_ci#include <asm/sections.h>
418c2ecf20Sopenharmony_ci#include <asm/io.h>
428c2ecf20Sopenharmony_ci#include <asm/prom.h>
438c2ecf20Sopenharmony_ci#include <asm/smp.h>
448c2ecf20Sopenharmony_ci#include <asm/machdep.h>
458c2ecf20Sopenharmony_ci#include <asm/pmac_feature.h>
468c2ecf20Sopenharmony_ci#include <asm/time.h>
478c2ecf20Sopenharmony_ci#include <asm/mpic.h>
488c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
498c2ecf20Sopenharmony_ci#include <asm/keylargo.h>
508c2ecf20Sopenharmony_ci#include <asm/pmac_low_i2c.h>
518c2ecf20Sopenharmony_ci#include <asm/pmac_pfunc.h>
528c2ecf20Sopenharmony_ci#include <asm/inst.h>
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#include "pmac.h"
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#undef DEBUG
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci#ifdef DEBUG
598c2ecf20Sopenharmony_ci#define DBG(fmt...) udbg_printf(fmt)
608c2ecf20Sopenharmony_ci#else
618c2ecf20Sopenharmony_ci#define DBG(fmt...)
628c2ecf20Sopenharmony_ci#endif
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ciextern void __secondary_start_pmac_0(void);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic void (*pmac_tb_freeze)(int freeze);
678c2ecf20Sopenharmony_cistatic u64 timebase;
688c2ecf20Sopenharmony_cistatic int tb_req;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_PMAC32_PSURGE
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/*
738c2ecf20Sopenharmony_ci * Powersurge (old powermac SMP) support.
748c2ecf20Sopenharmony_ci */
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/* Addresses for powersurge registers */
778c2ecf20Sopenharmony_ci#define HAMMERHEAD_BASE		0xf8000000
788c2ecf20Sopenharmony_ci#define HHEAD_CONFIG		0x90
798c2ecf20Sopenharmony_ci#define HHEAD_SEC_INTR		0xc0
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/* register for interrupting the primary processor on the powersurge */
828c2ecf20Sopenharmony_ci/* N.B. this is actually the ethernet ROM! */
838c2ecf20Sopenharmony_ci#define PSURGE_PRI_INTR		0xf3019000
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/* register for storing the start address for the secondary processor */
868c2ecf20Sopenharmony_ci/* N.B. this is the PCI config space address register for the 1st bridge */
878c2ecf20Sopenharmony_ci#define PSURGE_START		0xf2800000
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci/* Daystar/XLR8 4-CPU card */
908c2ecf20Sopenharmony_ci#define PSURGE_QUAD_REG_ADDR	0xf8800000
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci#define PSURGE_QUAD_IRQ_SET	0
938c2ecf20Sopenharmony_ci#define PSURGE_QUAD_IRQ_CLR	1
948c2ecf20Sopenharmony_ci#define PSURGE_QUAD_IRQ_PRIMARY	2
958c2ecf20Sopenharmony_ci#define PSURGE_QUAD_CKSTOP_CTL	3
968c2ecf20Sopenharmony_ci#define PSURGE_QUAD_PRIMARY_ARB	4
978c2ecf20Sopenharmony_ci#define PSURGE_QUAD_BOARD_ID	6
988c2ecf20Sopenharmony_ci#define PSURGE_QUAD_WHICH_CPU	7
998c2ecf20Sopenharmony_ci#define PSURGE_QUAD_CKSTOP_RDBK	8
1008c2ecf20Sopenharmony_ci#define PSURGE_QUAD_RESET_CTL	11
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci#define PSURGE_QUAD_OUT(r, v)	(out_8(quad_base + ((r) << 4) + 4, (v)))
1038c2ecf20Sopenharmony_ci#define PSURGE_QUAD_IN(r)	(in_8(quad_base + ((r) << 4) + 4) & 0x0f)
1048c2ecf20Sopenharmony_ci#define PSURGE_QUAD_BIS(r, v)	(PSURGE_QUAD_OUT((r), PSURGE_QUAD_IN(r) | (v)))
1058c2ecf20Sopenharmony_ci#define PSURGE_QUAD_BIC(r, v)	(PSURGE_QUAD_OUT((r), PSURGE_QUAD_IN(r) & ~(v)))
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/* virtual addresses for the above */
1088c2ecf20Sopenharmony_cistatic volatile u8 __iomem *hhead_base;
1098c2ecf20Sopenharmony_cistatic volatile u8 __iomem *quad_base;
1108c2ecf20Sopenharmony_cistatic volatile u32 __iomem *psurge_pri_intr;
1118c2ecf20Sopenharmony_cistatic volatile u8 __iomem *psurge_sec_intr;
1128c2ecf20Sopenharmony_cistatic volatile u32 __iomem *psurge_start;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/* values for psurge_type */
1158c2ecf20Sopenharmony_ci#define PSURGE_NONE		-1
1168c2ecf20Sopenharmony_ci#define PSURGE_DUAL		0
1178c2ecf20Sopenharmony_ci#define PSURGE_QUAD_OKEE	1
1188c2ecf20Sopenharmony_ci#define PSURGE_QUAD_COTTON	2
1198c2ecf20Sopenharmony_ci#define PSURGE_QUAD_ICEGRASS	3
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci/* what sort of powersurge board we have */
1228c2ecf20Sopenharmony_cistatic int psurge_type = PSURGE_NONE;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci/* irq for secondary cpus to report */
1258c2ecf20Sopenharmony_cistatic struct irq_domain *psurge_host;
1268c2ecf20Sopenharmony_ciint psurge_secondary_virq;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci/*
1298c2ecf20Sopenharmony_ci * Set and clear IPIs for powersurge.
1308c2ecf20Sopenharmony_ci */
1318c2ecf20Sopenharmony_cistatic inline void psurge_set_ipi(int cpu)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	if (psurge_type == PSURGE_NONE)
1348c2ecf20Sopenharmony_ci		return;
1358c2ecf20Sopenharmony_ci	if (cpu == 0)
1368c2ecf20Sopenharmony_ci		in_be32(psurge_pri_intr);
1378c2ecf20Sopenharmony_ci	else if (psurge_type == PSURGE_DUAL)
1388c2ecf20Sopenharmony_ci		out_8(psurge_sec_intr, 0);
1398c2ecf20Sopenharmony_ci	else
1408c2ecf20Sopenharmony_ci		PSURGE_QUAD_OUT(PSURGE_QUAD_IRQ_SET, 1 << cpu);
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic inline void psurge_clr_ipi(int cpu)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	if (cpu > 0) {
1468c2ecf20Sopenharmony_ci		switch(psurge_type) {
1478c2ecf20Sopenharmony_ci		case PSURGE_DUAL:
1488c2ecf20Sopenharmony_ci			out_8(psurge_sec_intr, ~0);
1498c2ecf20Sopenharmony_ci		case PSURGE_NONE:
1508c2ecf20Sopenharmony_ci			break;
1518c2ecf20Sopenharmony_ci		default:
1528c2ecf20Sopenharmony_ci			PSURGE_QUAD_OUT(PSURGE_QUAD_IRQ_CLR, 1 << cpu);
1538c2ecf20Sopenharmony_ci		}
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci/*
1588c2ecf20Sopenharmony_ci * On powersurge (old SMP powermac architecture) we don't have
1598c2ecf20Sopenharmony_ci * separate IPIs for separate messages like openpic does.  Instead
1608c2ecf20Sopenharmony_ci * use the generic demux helpers
1618c2ecf20Sopenharmony_ci *  -- paulus.
1628c2ecf20Sopenharmony_ci */
1638c2ecf20Sopenharmony_cistatic irqreturn_t psurge_ipi_intr(int irq, void *d)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	psurge_clr_ipi(smp_processor_id());
1668c2ecf20Sopenharmony_ci	smp_ipi_demux();
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic void smp_psurge_cause_ipi(int cpu)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	psurge_set_ipi(cpu);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic int psurge_host_map(struct irq_domain *h, unsigned int virq,
1778c2ecf20Sopenharmony_ci			 irq_hw_number_t hw)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_percpu_irq);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	return 0;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic const struct irq_domain_ops psurge_host_ops = {
1858c2ecf20Sopenharmony_ci	.map	= psurge_host_map,
1868c2ecf20Sopenharmony_ci};
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic int psurge_secondary_ipi_init(void)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	int rc = -ENOMEM;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	psurge_host = irq_domain_add_nomap(NULL, ~0, &psurge_host_ops, NULL);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	if (psurge_host)
1958c2ecf20Sopenharmony_ci		psurge_secondary_virq = irq_create_direct_mapping(psurge_host);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (psurge_secondary_virq)
1988c2ecf20Sopenharmony_ci		rc = request_irq(psurge_secondary_virq, psurge_ipi_intr,
1998c2ecf20Sopenharmony_ci			IRQF_PERCPU | IRQF_NO_THREAD, "IPI", NULL);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (rc)
2028c2ecf20Sopenharmony_ci		pr_err("Failed to setup secondary cpu IPI\n");
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	return rc;
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci/*
2088c2ecf20Sopenharmony_ci * Determine a quad card presence. We read the board ID register, we
2098c2ecf20Sopenharmony_ci * force the data bus to change to something else, and we read it again.
2108c2ecf20Sopenharmony_ci * It it's stable, then the register probably exist (ugh !)
2118c2ecf20Sopenharmony_ci */
2128c2ecf20Sopenharmony_cistatic int __init psurge_quad_probe(void)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	int type;
2158c2ecf20Sopenharmony_ci	unsigned int i;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	type = PSURGE_QUAD_IN(PSURGE_QUAD_BOARD_ID);
2188c2ecf20Sopenharmony_ci	if (type < PSURGE_QUAD_OKEE || type > PSURGE_QUAD_ICEGRASS
2198c2ecf20Sopenharmony_ci	    || type != PSURGE_QUAD_IN(PSURGE_QUAD_BOARD_ID))
2208c2ecf20Sopenharmony_ci		return PSURGE_DUAL;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	/* looks OK, try a slightly more rigorous test */
2238c2ecf20Sopenharmony_ci	/* bogus is not necessarily cacheline-aligned,
2248c2ecf20Sopenharmony_ci	   though I don't suppose that really matters.  -- paulus */
2258c2ecf20Sopenharmony_ci	for (i = 0; i < 100; i++) {
2268c2ecf20Sopenharmony_ci		volatile u32 bogus[8];
2278c2ecf20Sopenharmony_ci		bogus[(0+i)%8] = 0x00000000;
2288c2ecf20Sopenharmony_ci		bogus[(1+i)%8] = 0x55555555;
2298c2ecf20Sopenharmony_ci		bogus[(2+i)%8] = 0xFFFFFFFF;
2308c2ecf20Sopenharmony_ci		bogus[(3+i)%8] = 0xAAAAAAAA;
2318c2ecf20Sopenharmony_ci		bogus[(4+i)%8] = 0x33333333;
2328c2ecf20Sopenharmony_ci		bogus[(5+i)%8] = 0xCCCCCCCC;
2338c2ecf20Sopenharmony_ci		bogus[(6+i)%8] = 0xCCCCCCCC;
2348c2ecf20Sopenharmony_ci		bogus[(7+i)%8] = 0x33333333;
2358c2ecf20Sopenharmony_ci		wmb();
2368c2ecf20Sopenharmony_ci		asm volatile("dcbf 0,%0" : : "r" (bogus) : "memory");
2378c2ecf20Sopenharmony_ci		mb();
2388c2ecf20Sopenharmony_ci		if (type != PSURGE_QUAD_IN(PSURGE_QUAD_BOARD_ID))
2398c2ecf20Sopenharmony_ci			return PSURGE_DUAL;
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci	return type;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic void __init psurge_quad_init(void)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	int procbits;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (ppc_md.progress) ppc_md.progress("psurge_quad_init", 0x351);
2498c2ecf20Sopenharmony_ci	procbits = ~PSURGE_QUAD_IN(PSURGE_QUAD_WHICH_CPU);
2508c2ecf20Sopenharmony_ci	if (psurge_type == PSURGE_QUAD_ICEGRASS)
2518c2ecf20Sopenharmony_ci		PSURGE_QUAD_BIS(PSURGE_QUAD_RESET_CTL, procbits);
2528c2ecf20Sopenharmony_ci	else
2538c2ecf20Sopenharmony_ci		PSURGE_QUAD_BIC(PSURGE_QUAD_CKSTOP_CTL, procbits);
2548c2ecf20Sopenharmony_ci	mdelay(33);
2558c2ecf20Sopenharmony_ci	out_8(psurge_sec_intr, ~0);
2568c2ecf20Sopenharmony_ci	PSURGE_QUAD_OUT(PSURGE_QUAD_IRQ_CLR, procbits);
2578c2ecf20Sopenharmony_ci	PSURGE_QUAD_BIS(PSURGE_QUAD_RESET_CTL, procbits);
2588c2ecf20Sopenharmony_ci	if (psurge_type != PSURGE_QUAD_ICEGRASS)
2598c2ecf20Sopenharmony_ci		PSURGE_QUAD_BIS(PSURGE_QUAD_CKSTOP_CTL, procbits);
2608c2ecf20Sopenharmony_ci	PSURGE_QUAD_BIC(PSURGE_QUAD_PRIMARY_ARB, procbits);
2618c2ecf20Sopenharmony_ci	mdelay(33);
2628c2ecf20Sopenharmony_ci	PSURGE_QUAD_BIC(PSURGE_QUAD_RESET_CTL, procbits);
2638c2ecf20Sopenharmony_ci	mdelay(33);
2648c2ecf20Sopenharmony_ci	PSURGE_QUAD_BIS(PSURGE_QUAD_PRIMARY_ARB, procbits);
2658c2ecf20Sopenharmony_ci	mdelay(33);
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic void __init smp_psurge_probe(void)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	int i, ncpus;
2718c2ecf20Sopenharmony_ci	struct device_node *dn;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/*
2748c2ecf20Sopenharmony_ci	 * The powersurge cpu board can be used in the generation
2758c2ecf20Sopenharmony_ci	 * of powermacs that have a socket for an upgradeable cpu card,
2768c2ecf20Sopenharmony_ci	 * including the 7500, 8500, 9500, 9600.
2778c2ecf20Sopenharmony_ci	 * The device tree doesn't tell you if you have 2 cpus because
2788c2ecf20Sopenharmony_ci	 * OF doesn't know anything about the 2nd processor.
2798c2ecf20Sopenharmony_ci	 * Instead we look for magic bits in magic registers,
2808c2ecf20Sopenharmony_ci	 * in the hammerhead memory controller in the case of the
2818c2ecf20Sopenharmony_ci	 * dual-cpu powersurge board.  -- paulus.
2828c2ecf20Sopenharmony_ci	 */
2838c2ecf20Sopenharmony_ci	dn = of_find_node_by_name(NULL, "hammerhead");
2848c2ecf20Sopenharmony_ci	if (dn == NULL)
2858c2ecf20Sopenharmony_ci		return;
2868c2ecf20Sopenharmony_ci	of_node_put(dn);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	hhead_base = ioremap(HAMMERHEAD_BASE, 0x800);
2898c2ecf20Sopenharmony_ci	quad_base = ioremap(PSURGE_QUAD_REG_ADDR, 1024);
2908c2ecf20Sopenharmony_ci	psurge_sec_intr = hhead_base + HHEAD_SEC_INTR;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	psurge_type = psurge_quad_probe();
2938c2ecf20Sopenharmony_ci	if (psurge_type != PSURGE_DUAL) {
2948c2ecf20Sopenharmony_ci		psurge_quad_init();
2958c2ecf20Sopenharmony_ci		/* All released cards using this HW design have 4 CPUs */
2968c2ecf20Sopenharmony_ci		ncpus = 4;
2978c2ecf20Sopenharmony_ci		/* No sure how timebase sync works on those, let's use SW */
2988c2ecf20Sopenharmony_ci		smp_ops->give_timebase = smp_generic_give_timebase;
2998c2ecf20Sopenharmony_ci		smp_ops->take_timebase = smp_generic_take_timebase;
3008c2ecf20Sopenharmony_ci	} else {
3018c2ecf20Sopenharmony_ci		iounmap(quad_base);
3028c2ecf20Sopenharmony_ci		if ((in_8(hhead_base + HHEAD_CONFIG) & 0x02) == 0) {
3038c2ecf20Sopenharmony_ci			/* not a dual-cpu card */
3048c2ecf20Sopenharmony_ci			iounmap(hhead_base);
3058c2ecf20Sopenharmony_ci			psurge_type = PSURGE_NONE;
3068c2ecf20Sopenharmony_ci			return;
3078c2ecf20Sopenharmony_ci		}
3088c2ecf20Sopenharmony_ci		ncpus = 2;
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (psurge_secondary_ipi_init())
3128c2ecf20Sopenharmony_ci		return;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	psurge_start = ioremap(PSURGE_START, 4);
3158c2ecf20Sopenharmony_ci	psurge_pri_intr = ioremap(PSURGE_PRI_INTR, 4);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	/* This is necessary because OF doesn't know about the
3188c2ecf20Sopenharmony_ci	 * secondary cpu(s), and thus there aren't nodes in the
3198c2ecf20Sopenharmony_ci	 * device tree for them, and smp_setup_cpu_maps hasn't
3208c2ecf20Sopenharmony_ci	 * set their bits in cpu_present_mask.
3218c2ecf20Sopenharmony_ci	 */
3228c2ecf20Sopenharmony_ci	if (ncpus > NR_CPUS)
3238c2ecf20Sopenharmony_ci		ncpus = NR_CPUS;
3248c2ecf20Sopenharmony_ci	for (i = 1; i < ncpus ; ++i)
3258c2ecf20Sopenharmony_ci		set_cpu_present(i, true);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if (ppc_md.progress) ppc_md.progress("smp_psurge_probe - done", 0x352);
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic int __init smp_psurge_kick_cpu(int nr)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	unsigned long start = __pa(__secondary_start_pmac_0) + nr * 8;
3338c2ecf20Sopenharmony_ci	unsigned long a, flags;
3348c2ecf20Sopenharmony_ci	int i, j;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/* Defining this here is evil ... but I prefer hiding that
3378c2ecf20Sopenharmony_ci	 * crap to avoid giving people ideas that they can do the
3388c2ecf20Sopenharmony_ci	 * same.
3398c2ecf20Sopenharmony_ci	 */
3408c2ecf20Sopenharmony_ci	extern volatile unsigned int cpu_callin_map[NR_CPUS];
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	/* may need to flush here if secondary bats aren't setup */
3438c2ecf20Sopenharmony_ci	for (a = KERNELBASE; a < KERNELBASE + 0x800000; a += 32)
3448c2ecf20Sopenharmony_ci		asm volatile("dcbf 0,%0" : : "r" (a) : "memory");
3458c2ecf20Sopenharmony_ci	asm volatile("sync");
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (ppc_md.progress) ppc_md.progress("smp_psurge_kick_cpu", 0x353);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	/* This is going to freeze the timeebase, we disable interrupts */
3508c2ecf20Sopenharmony_ci	local_irq_save(flags);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	out_be32(psurge_start, start);
3538c2ecf20Sopenharmony_ci	mb();
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	psurge_set_ipi(nr);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	/*
3588c2ecf20Sopenharmony_ci	 * We can't use udelay here because the timebase is now frozen.
3598c2ecf20Sopenharmony_ci	 */
3608c2ecf20Sopenharmony_ci	for (i = 0; i < 2000; ++i)
3618c2ecf20Sopenharmony_ci		asm volatile("nop" : : : "memory");
3628c2ecf20Sopenharmony_ci	psurge_clr_ipi(nr);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	/*
3658c2ecf20Sopenharmony_ci	 * Also, because the timebase is frozen, we must not return to the
3668c2ecf20Sopenharmony_ci	 * caller which will try to do udelay's etc... Instead, we wait -here-
3678c2ecf20Sopenharmony_ci	 * for the CPU to callin.
3688c2ecf20Sopenharmony_ci	 */
3698c2ecf20Sopenharmony_ci	for (i = 0; i < 100000 && !cpu_callin_map[nr]; ++i) {
3708c2ecf20Sopenharmony_ci		for (j = 1; j < 10000; j++)
3718c2ecf20Sopenharmony_ci			asm volatile("nop" : : : "memory");
3728c2ecf20Sopenharmony_ci		asm volatile("sync" : : : "memory");
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci	if (!cpu_callin_map[nr])
3758c2ecf20Sopenharmony_ci		goto stuck;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	/* And we do the TB sync here too for standard dual CPU cards */
3788c2ecf20Sopenharmony_ci	if (psurge_type == PSURGE_DUAL) {
3798c2ecf20Sopenharmony_ci		while(!tb_req)
3808c2ecf20Sopenharmony_ci			barrier();
3818c2ecf20Sopenharmony_ci		tb_req = 0;
3828c2ecf20Sopenharmony_ci		mb();
3838c2ecf20Sopenharmony_ci		timebase = get_tb();
3848c2ecf20Sopenharmony_ci		mb();
3858c2ecf20Sopenharmony_ci		while (timebase)
3868c2ecf20Sopenharmony_ci			barrier();
3878c2ecf20Sopenharmony_ci		mb();
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci stuck:
3908c2ecf20Sopenharmony_ci	/* now interrupt the secondary, restarting both TBs */
3918c2ecf20Sopenharmony_ci	if (psurge_type == PSURGE_DUAL)
3928c2ecf20Sopenharmony_ci		psurge_set_ipi(1);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	if (ppc_md.progress) ppc_md.progress("smp_psurge_kick_cpu - done", 0x354);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	return 0;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cistatic void __init smp_psurge_setup_cpu(int cpu_nr)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	unsigned long flags = IRQF_PERCPU | IRQF_NO_THREAD;
4028c2ecf20Sopenharmony_ci	int irq;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	if (cpu_nr != 0 || !psurge_start)
4058c2ecf20Sopenharmony_ci		return;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	/* reset the entry point so if we get another intr we won't
4088c2ecf20Sopenharmony_ci	 * try to startup again */
4098c2ecf20Sopenharmony_ci	out_be32(psurge_start, 0x100);
4108c2ecf20Sopenharmony_ci	irq = irq_create_mapping(NULL, 30);
4118c2ecf20Sopenharmony_ci	if (request_irq(irq, psurge_ipi_intr, flags, "primary IPI", NULL))
4128c2ecf20Sopenharmony_ci		printk(KERN_ERR "Couldn't get primary IPI interrupt");
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_civoid __init smp_psurge_take_timebase(void)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	if (psurge_type != PSURGE_DUAL)
4188c2ecf20Sopenharmony_ci		return;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	tb_req = 1;
4218c2ecf20Sopenharmony_ci	mb();
4228c2ecf20Sopenharmony_ci	while (!timebase)
4238c2ecf20Sopenharmony_ci		barrier();
4248c2ecf20Sopenharmony_ci	mb();
4258c2ecf20Sopenharmony_ci	set_tb(timebase >> 32, timebase & 0xffffffff);
4268c2ecf20Sopenharmony_ci	timebase = 0;
4278c2ecf20Sopenharmony_ci	mb();
4288c2ecf20Sopenharmony_ci	set_dec(tb_ticks_per_jiffy/2);
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_civoid __init smp_psurge_give_timebase(void)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	/* Nothing to do here */
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci/* PowerSurge-style Macs */
4378c2ecf20Sopenharmony_cistruct smp_ops_t psurge_smp_ops = {
4388c2ecf20Sopenharmony_ci	.message_pass	= NULL,	/* Use smp_muxed_ipi_message_pass */
4398c2ecf20Sopenharmony_ci	.cause_ipi	= smp_psurge_cause_ipi,
4408c2ecf20Sopenharmony_ci	.cause_nmi_ipi	= NULL,
4418c2ecf20Sopenharmony_ci	.probe		= smp_psurge_probe,
4428c2ecf20Sopenharmony_ci	.kick_cpu	= smp_psurge_kick_cpu,
4438c2ecf20Sopenharmony_ci	.setup_cpu	= smp_psurge_setup_cpu,
4448c2ecf20Sopenharmony_ci	.give_timebase	= smp_psurge_give_timebase,
4458c2ecf20Sopenharmony_ci	.take_timebase	= smp_psurge_take_timebase,
4468c2ecf20Sopenharmony_ci};
4478c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC_PMAC32_PSURGE */
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci/*
4508c2ecf20Sopenharmony_ci * Core 99 and later support
4518c2ecf20Sopenharmony_ci */
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_cistatic void smp_core99_give_timebase(void)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	unsigned long flags;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	local_irq_save(flags);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	while(!tb_req)
4618c2ecf20Sopenharmony_ci		barrier();
4628c2ecf20Sopenharmony_ci	tb_req = 0;
4638c2ecf20Sopenharmony_ci	(*pmac_tb_freeze)(1);
4648c2ecf20Sopenharmony_ci	mb();
4658c2ecf20Sopenharmony_ci	timebase = get_tb();
4668c2ecf20Sopenharmony_ci	mb();
4678c2ecf20Sopenharmony_ci	while (timebase)
4688c2ecf20Sopenharmony_ci		barrier();
4698c2ecf20Sopenharmony_ci	mb();
4708c2ecf20Sopenharmony_ci	(*pmac_tb_freeze)(0);
4718c2ecf20Sopenharmony_ci	mb();
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	local_irq_restore(flags);
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_cistatic void smp_core99_take_timebase(void)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	unsigned long flags;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	local_irq_save(flags);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	tb_req = 1;
4848c2ecf20Sopenharmony_ci	mb();
4858c2ecf20Sopenharmony_ci	while (!timebase)
4868c2ecf20Sopenharmony_ci		barrier();
4878c2ecf20Sopenharmony_ci	mb();
4888c2ecf20Sopenharmony_ci	set_tb(timebase >> 32, timebase & 0xffffffff);
4898c2ecf20Sopenharmony_ci	timebase = 0;
4908c2ecf20Sopenharmony_ci	mb();
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	local_irq_restore(flags);
4938c2ecf20Sopenharmony_ci}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64
4968c2ecf20Sopenharmony_ci/*
4978c2ecf20Sopenharmony_ci * G5s enable/disable the timebase via an i2c-connected clock chip.
4988c2ecf20Sopenharmony_ci */
4998c2ecf20Sopenharmony_cistatic struct pmac_i2c_bus *pmac_tb_clock_chip_host;
5008c2ecf20Sopenharmony_cistatic u8 pmac_tb_pulsar_addr;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic void smp_core99_cypress_tb_freeze(int freeze)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	u8 data;
5058c2ecf20Sopenharmony_ci	int rc;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	/* Strangely, the device-tree says address is 0xd2, but darwin
5088c2ecf20Sopenharmony_ci	 * accesses 0xd0 ...
5098c2ecf20Sopenharmony_ci	 */
5108c2ecf20Sopenharmony_ci	pmac_i2c_setmode(pmac_tb_clock_chip_host,
5118c2ecf20Sopenharmony_ci			 pmac_i2c_mode_combined);
5128c2ecf20Sopenharmony_ci	rc = pmac_i2c_xfer(pmac_tb_clock_chip_host,
5138c2ecf20Sopenharmony_ci			   0xd0 | pmac_i2c_read,
5148c2ecf20Sopenharmony_ci			   1, 0x81, &data, 1);
5158c2ecf20Sopenharmony_ci	if (rc != 0)
5168c2ecf20Sopenharmony_ci		goto bail;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	data = (data & 0xf3) | (freeze ? 0x00 : 0x0c);
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci       	pmac_i2c_setmode(pmac_tb_clock_chip_host, pmac_i2c_mode_stdsub);
5218c2ecf20Sopenharmony_ci	rc = pmac_i2c_xfer(pmac_tb_clock_chip_host,
5228c2ecf20Sopenharmony_ci			   0xd0 | pmac_i2c_write,
5238c2ecf20Sopenharmony_ci			   1, 0x81, &data, 1);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci bail:
5268c2ecf20Sopenharmony_ci	if (rc != 0) {
5278c2ecf20Sopenharmony_ci		printk("Cypress Timebase %s rc: %d\n",
5288c2ecf20Sopenharmony_ci		       freeze ? "freeze" : "unfreeze", rc);
5298c2ecf20Sopenharmony_ci		panic("Timebase freeze failed !\n");
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistatic void smp_core99_pulsar_tb_freeze(int freeze)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	u8 data;
5378c2ecf20Sopenharmony_ci	int rc;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	pmac_i2c_setmode(pmac_tb_clock_chip_host,
5408c2ecf20Sopenharmony_ci			 pmac_i2c_mode_combined);
5418c2ecf20Sopenharmony_ci	rc = pmac_i2c_xfer(pmac_tb_clock_chip_host,
5428c2ecf20Sopenharmony_ci			   pmac_tb_pulsar_addr | pmac_i2c_read,
5438c2ecf20Sopenharmony_ci			   1, 0x2e, &data, 1);
5448c2ecf20Sopenharmony_ci	if (rc != 0)
5458c2ecf20Sopenharmony_ci		goto bail;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	data = (data & 0x88) | (freeze ? 0x11 : 0x22);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	pmac_i2c_setmode(pmac_tb_clock_chip_host, pmac_i2c_mode_stdsub);
5508c2ecf20Sopenharmony_ci	rc = pmac_i2c_xfer(pmac_tb_clock_chip_host,
5518c2ecf20Sopenharmony_ci			   pmac_tb_pulsar_addr | pmac_i2c_write,
5528c2ecf20Sopenharmony_ci			   1, 0x2e, &data, 1);
5538c2ecf20Sopenharmony_ci bail:
5548c2ecf20Sopenharmony_ci	if (rc != 0) {
5558c2ecf20Sopenharmony_ci		printk(KERN_ERR "Pulsar Timebase %s rc: %d\n",
5568c2ecf20Sopenharmony_ci		       freeze ? "freeze" : "unfreeze", rc);
5578c2ecf20Sopenharmony_ci		panic("Timebase freeze failed !\n");
5588c2ecf20Sopenharmony_ci	}
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic void __init smp_core99_setup_i2c_hwsync(int ncpus)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	struct device_node *cc = NULL;
5648c2ecf20Sopenharmony_ci	struct device_node *p;
5658c2ecf20Sopenharmony_ci	const char *name = NULL;
5668c2ecf20Sopenharmony_ci	const u32 *reg;
5678c2ecf20Sopenharmony_ci	int ok;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	/* Look for the clock chip */
5708c2ecf20Sopenharmony_ci	for_each_node_by_name(cc, "i2c-hwclock") {
5718c2ecf20Sopenharmony_ci		p = of_get_parent(cc);
5728c2ecf20Sopenharmony_ci		ok = p && of_device_is_compatible(p, "uni-n-i2c");
5738c2ecf20Sopenharmony_ci		of_node_put(p);
5748c2ecf20Sopenharmony_ci		if (!ok)
5758c2ecf20Sopenharmony_ci			continue;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci		pmac_tb_clock_chip_host = pmac_i2c_find_bus(cc);
5788c2ecf20Sopenharmony_ci		if (pmac_tb_clock_chip_host == NULL)
5798c2ecf20Sopenharmony_ci			continue;
5808c2ecf20Sopenharmony_ci		reg = of_get_property(cc, "reg", NULL);
5818c2ecf20Sopenharmony_ci		if (reg == NULL)
5828c2ecf20Sopenharmony_ci			continue;
5838c2ecf20Sopenharmony_ci		switch (*reg) {
5848c2ecf20Sopenharmony_ci		case 0xd2:
5858c2ecf20Sopenharmony_ci			if (of_device_is_compatible(cc,"pulsar-legacy-slewing")) {
5868c2ecf20Sopenharmony_ci				pmac_tb_freeze = smp_core99_pulsar_tb_freeze;
5878c2ecf20Sopenharmony_ci				pmac_tb_pulsar_addr = 0xd2;
5888c2ecf20Sopenharmony_ci				name = "Pulsar";
5898c2ecf20Sopenharmony_ci			} else if (of_device_is_compatible(cc, "cy28508")) {
5908c2ecf20Sopenharmony_ci				pmac_tb_freeze = smp_core99_cypress_tb_freeze;
5918c2ecf20Sopenharmony_ci				name = "Cypress";
5928c2ecf20Sopenharmony_ci			}
5938c2ecf20Sopenharmony_ci			break;
5948c2ecf20Sopenharmony_ci		case 0xd4:
5958c2ecf20Sopenharmony_ci			pmac_tb_freeze = smp_core99_pulsar_tb_freeze;
5968c2ecf20Sopenharmony_ci			pmac_tb_pulsar_addr = 0xd4;
5978c2ecf20Sopenharmony_ci			name = "Pulsar";
5988c2ecf20Sopenharmony_ci			break;
5998c2ecf20Sopenharmony_ci		}
6008c2ecf20Sopenharmony_ci		if (pmac_tb_freeze != NULL)
6018c2ecf20Sopenharmony_ci			break;
6028c2ecf20Sopenharmony_ci	}
6038c2ecf20Sopenharmony_ci	if (pmac_tb_freeze != NULL) {
6048c2ecf20Sopenharmony_ci		/* Open i2c bus for synchronous access */
6058c2ecf20Sopenharmony_ci		if (pmac_i2c_open(pmac_tb_clock_chip_host, 1)) {
6068c2ecf20Sopenharmony_ci			printk(KERN_ERR "Failed top open i2c bus for clock"
6078c2ecf20Sopenharmony_ci			       " sync, fallback to software sync !\n");
6088c2ecf20Sopenharmony_ci			goto no_i2c_sync;
6098c2ecf20Sopenharmony_ci		}
6108c2ecf20Sopenharmony_ci		printk(KERN_INFO "Processor timebase sync using %s i2c clock\n",
6118c2ecf20Sopenharmony_ci		       name);
6128c2ecf20Sopenharmony_ci		return;
6138c2ecf20Sopenharmony_ci	}
6148c2ecf20Sopenharmony_ci no_i2c_sync:
6158c2ecf20Sopenharmony_ci	pmac_tb_freeze = NULL;
6168c2ecf20Sopenharmony_ci	pmac_tb_clock_chip_host = NULL;
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci/*
6228c2ecf20Sopenharmony_ci * Newer G5s uses a platform function
6238c2ecf20Sopenharmony_ci */
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_cistatic void smp_core99_pfunc_tb_freeze(int freeze)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	struct device_node *cpus;
6288c2ecf20Sopenharmony_ci	struct pmf_args args;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	cpus = of_find_node_by_path("/cpus");
6318c2ecf20Sopenharmony_ci	BUG_ON(cpus == NULL);
6328c2ecf20Sopenharmony_ci	args.count = 1;
6338c2ecf20Sopenharmony_ci	args.u[0].v = !freeze;
6348c2ecf20Sopenharmony_ci	pmf_call_function(cpus, "cpu-timebase", &args);
6358c2ecf20Sopenharmony_ci	of_node_put(cpus);
6368c2ecf20Sopenharmony_ci}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci#else /* CONFIG_PPC64 */
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci/*
6418c2ecf20Sopenharmony_ci * SMP G4 use a GPIO to enable/disable the timebase.
6428c2ecf20Sopenharmony_ci */
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_cistatic unsigned int core99_tb_gpio;	/* Timebase freeze GPIO */
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_cistatic void smp_core99_gpio_tb_freeze(int freeze)
6478c2ecf20Sopenharmony_ci{
6488c2ecf20Sopenharmony_ci	if (freeze)
6498c2ecf20Sopenharmony_ci		pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, core99_tb_gpio, 4);
6508c2ecf20Sopenharmony_ci	else
6518c2ecf20Sopenharmony_ci		pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, core99_tb_gpio, 0);
6528c2ecf20Sopenharmony_ci	pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, core99_tb_gpio, 0);
6538c2ecf20Sopenharmony_ci}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci#endif /* !CONFIG_PPC64 */
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_cistatic void core99_init_caches(int cpu)
6598c2ecf20Sopenharmony_ci{
6608c2ecf20Sopenharmony_ci#ifndef CONFIG_PPC64
6618c2ecf20Sopenharmony_ci	/* L2 and L3 cache settings to pass from CPU0 to CPU1 on G4 cpus */
6628c2ecf20Sopenharmony_ci	static long int core99_l2_cache;
6638c2ecf20Sopenharmony_ci	static long int core99_l3_cache;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	if (!cpu_has_feature(CPU_FTR_L2CR))
6668c2ecf20Sopenharmony_ci		return;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	if (cpu == 0) {
6698c2ecf20Sopenharmony_ci		core99_l2_cache = _get_L2CR();
6708c2ecf20Sopenharmony_ci		printk("CPU0: L2CR is %lx\n", core99_l2_cache);
6718c2ecf20Sopenharmony_ci	} else {
6728c2ecf20Sopenharmony_ci		printk("CPU%d: L2CR was %lx\n", cpu, _get_L2CR());
6738c2ecf20Sopenharmony_ci		_set_L2CR(0);
6748c2ecf20Sopenharmony_ci		_set_L2CR(core99_l2_cache);
6758c2ecf20Sopenharmony_ci		printk("CPU%d: L2CR set to %lx\n", cpu, core99_l2_cache);
6768c2ecf20Sopenharmony_ci	}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	if (!cpu_has_feature(CPU_FTR_L3CR))
6798c2ecf20Sopenharmony_ci		return;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	if (cpu == 0){
6828c2ecf20Sopenharmony_ci		core99_l3_cache = _get_L3CR();
6838c2ecf20Sopenharmony_ci		printk("CPU0: L3CR is %lx\n", core99_l3_cache);
6848c2ecf20Sopenharmony_ci	} else {
6858c2ecf20Sopenharmony_ci		printk("CPU%d: L3CR was %lx\n", cpu, _get_L3CR());
6868c2ecf20Sopenharmony_ci		_set_L3CR(0);
6878c2ecf20Sopenharmony_ci		_set_L3CR(core99_l3_cache);
6888c2ecf20Sopenharmony_ci		printk("CPU%d: L3CR set to %lx\n", cpu, core99_l3_cache);
6898c2ecf20Sopenharmony_ci	}
6908c2ecf20Sopenharmony_ci#endif /* !CONFIG_PPC64 */
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_cistatic void __init smp_core99_setup(int ncpus)
6948c2ecf20Sopenharmony_ci{
6958c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	/* i2c based HW sync on some G5s */
6988c2ecf20Sopenharmony_ci	if (of_machine_is_compatible("PowerMac7,2") ||
6998c2ecf20Sopenharmony_ci	    of_machine_is_compatible("PowerMac7,3") ||
7008c2ecf20Sopenharmony_ci	    of_machine_is_compatible("RackMac3,1"))
7018c2ecf20Sopenharmony_ci		smp_core99_setup_i2c_hwsync(ncpus);
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	/* pfunc based HW sync on recent G5s */
7048c2ecf20Sopenharmony_ci	if (pmac_tb_freeze == NULL) {
7058c2ecf20Sopenharmony_ci		struct device_node *cpus =
7068c2ecf20Sopenharmony_ci			of_find_node_by_path("/cpus");
7078c2ecf20Sopenharmony_ci		if (cpus &&
7088c2ecf20Sopenharmony_ci		    of_get_property(cpus, "platform-cpu-timebase", NULL)) {
7098c2ecf20Sopenharmony_ci			pmac_tb_freeze = smp_core99_pfunc_tb_freeze;
7108c2ecf20Sopenharmony_ci			printk(KERN_INFO "Processor timebase sync using"
7118c2ecf20Sopenharmony_ci			       " platform function\n");
7128c2ecf20Sopenharmony_ci		}
7138c2ecf20Sopenharmony_ci	}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci#else /* CONFIG_PPC64 */
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	/* GPIO based HW sync on ppc32 Core99 */
7188c2ecf20Sopenharmony_ci	if (pmac_tb_freeze == NULL && !of_machine_is_compatible("MacRISC4")) {
7198c2ecf20Sopenharmony_ci		struct device_node *cpu;
7208c2ecf20Sopenharmony_ci		const u32 *tbprop = NULL;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci		core99_tb_gpio = KL_GPIO_TB_ENABLE;	/* default value */
7238c2ecf20Sopenharmony_ci		cpu = of_find_node_by_type(NULL, "cpu");
7248c2ecf20Sopenharmony_ci		if (cpu != NULL) {
7258c2ecf20Sopenharmony_ci			tbprop = of_get_property(cpu, "timebase-enable", NULL);
7268c2ecf20Sopenharmony_ci			if (tbprop)
7278c2ecf20Sopenharmony_ci				core99_tb_gpio = *tbprop;
7288c2ecf20Sopenharmony_ci			of_node_put(cpu);
7298c2ecf20Sopenharmony_ci		}
7308c2ecf20Sopenharmony_ci		pmac_tb_freeze = smp_core99_gpio_tb_freeze;
7318c2ecf20Sopenharmony_ci		printk(KERN_INFO "Processor timebase sync using"
7328c2ecf20Sopenharmony_ci		       " GPIO 0x%02x\n", core99_tb_gpio);
7338c2ecf20Sopenharmony_ci	}
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC64 */
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	/* No timebase sync, fallback to software */
7388c2ecf20Sopenharmony_ci	if (pmac_tb_freeze == NULL) {
7398c2ecf20Sopenharmony_ci		smp_ops->give_timebase = smp_generic_give_timebase;
7408c2ecf20Sopenharmony_ci		smp_ops->take_timebase = smp_generic_take_timebase;
7418c2ecf20Sopenharmony_ci		printk(KERN_INFO "Processor timebase sync using software\n");
7428c2ecf20Sopenharmony_ci	}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci#ifndef CONFIG_PPC64
7458c2ecf20Sopenharmony_ci	{
7468c2ecf20Sopenharmony_ci		int i;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci		/* XXX should get this from reg properties */
7498c2ecf20Sopenharmony_ci		for (i = 1; i < ncpus; ++i)
7508c2ecf20Sopenharmony_ci			set_hard_smp_processor_id(i, i);
7518c2ecf20Sopenharmony_ci	}
7528c2ecf20Sopenharmony_ci#endif
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	/* 32 bits SMP can't NAP */
7558c2ecf20Sopenharmony_ci	if (!of_machine_is_compatible("MacRISC4"))
7568c2ecf20Sopenharmony_ci		powersave_nap = 0;
7578c2ecf20Sopenharmony_ci}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_cistatic void __init smp_core99_probe(void)
7608c2ecf20Sopenharmony_ci{
7618c2ecf20Sopenharmony_ci	struct device_node *cpus;
7628c2ecf20Sopenharmony_ci	int ncpus = 0;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	if (ppc_md.progress) ppc_md.progress("smp_core99_probe", 0x345);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	/* Count CPUs in the device-tree */
7678c2ecf20Sopenharmony_ci	for_each_node_by_type(cpus, "cpu")
7688c2ecf20Sopenharmony_ci		++ncpus;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	printk(KERN_INFO "PowerMac SMP probe found %d cpus\n", ncpus);
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	/* Nothing more to do if less than 2 of them */
7738c2ecf20Sopenharmony_ci	if (ncpus <= 1)
7748c2ecf20Sopenharmony_ci		return;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	/* We need to perform some early initialisations before we can start
7778c2ecf20Sopenharmony_ci	 * setting up SMP as we are running before initcalls
7788c2ecf20Sopenharmony_ci	 */
7798c2ecf20Sopenharmony_ci	pmac_pfunc_base_install();
7808c2ecf20Sopenharmony_ci	pmac_i2c_init();
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	/* Setup various bits like timebase sync method, ability to nap, ... */
7838c2ecf20Sopenharmony_ci	smp_core99_setup(ncpus);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	/* Install IPIs */
7868c2ecf20Sopenharmony_ci	mpic_request_ipis();
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	/* Collect l2cr and l3cr values from CPU 0 */
7898c2ecf20Sopenharmony_ci	core99_init_caches(0);
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_cistatic int smp_core99_kick_cpu(int nr)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	unsigned int save_vector;
7958c2ecf20Sopenharmony_ci	unsigned long target, flags;
7968c2ecf20Sopenharmony_ci	unsigned int *vector = (unsigned int *)(PAGE_OFFSET+0x100);
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	if (nr < 0 || nr > 3)
7998c2ecf20Sopenharmony_ci		return -ENOENT;
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	if (ppc_md.progress)
8028c2ecf20Sopenharmony_ci		ppc_md.progress("smp_core99_kick_cpu", 0x346);
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	local_irq_save(flags);
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	/* Save reset vector */
8078c2ecf20Sopenharmony_ci	save_vector = *vector;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	/* Setup fake reset vector that does
8108c2ecf20Sopenharmony_ci	 *   b __secondary_start_pmac_0 + nr*8
8118c2ecf20Sopenharmony_ci	 */
8128c2ecf20Sopenharmony_ci	target = (unsigned long) __secondary_start_pmac_0 + nr * 8;
8138c2ecf20Sopenharmony_ci	patch_branch((struct ppc_inst *)vector, target, BRANCH_SET_LINK);
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	/* Put some life in our friend */
8168c2ecf20Sopenharmony_ci	pmac_call_feature(PMAC_FTR_RESET_CPU, NULL, nr, 0);
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	/* FIXME: We wait a bit for the CPU to take the exception, I should
8198c2ecf20Sopenharmony_ci	 * instead wait for the entry code to set something for me. Well,
8208c2ecf20Sopenharmony_ci	 * ideally, all that crap will be done in prom.c and the CPU left
8218c2ecf20Sopenharmony_ci	 * in a RAM-based wait loop like CHRP.
8228c2ecf20Sopenharmony_ci	 */
8238c2ecf20Sopenharmony_ci	mdelay(1);
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	/* Restore our exception vector */
8268c2ecf20Sopenharmony_ci	patch_instruction((struct ppc_inst *)vector, ppc_inst(save_vector));
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	local_irq_restore(flags);
8298c2ecf20Sopenharmony_ci	if (ppc_md.progress) ppc_md.progress("smp_core99_kick_cpu done", 0x347);
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	return 0;
8328c2ecf20Sopenharmony_ci}
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_cistatic void smp_core99_setup_cpu(int cpu_nr)
8358c2ecf20Sopenharmony_ci{
8368c2ecf20Sopenharmony_ci	/* Setup L2/L3 */
8378c2ecf20Sopenharmony_ci	if (cpu_nr != 0)
8388c2ecf20Sopenharmony_ci		core99_init_caches(cpu_nr);
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	/* Setup openpic */
8418c2ecf20Sopenharmony_ci	mpic_setup_this_cpu();
8428c2ecf20Sopenharmony_ci}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64
8458c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
8468c2ecf20Sopenharmony_cistatic unsigned int smp_core99_host_open;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_cistatic int smp_core99_cpu_prepare(unsigned int cpu)
8498c2ecf20Sopenharmony_ci{
8508c2ecf20Sopenharmony_ci	int rc;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	/* Open i2c bus if it was used for tb sync */
8538c2ecf20Sopenharmony_ci	if (pmac_tb_clock_chip_host && !smp_core99_host_open) {
8548c2ecf20Sopenharmony_ci		rc = pmac_i2c_open(pmac_tb_clock_chip_host, 1);
8558c2ecf20Sopenharmony_ci		if (rc) {
8568c2ecf20Sopenharmony_ci			pr_err("Failed to open i2c bus for time sync\n");
8578c2ecf20Sopenharmony_ci			return notifier_from_errno(rc);
8588c2ecf20Sopenharmony_ci		}
8598c2ecf20Sopenharmony_ci		smp_core99_host_open = 1;
8608c2ecf20Sopenharmony_ci	}
8618c2ecf20Sopenharmony_ci	return 0;
8628c2ecf20Sopenharmony_ci}
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_cistatic int smp_core99_cpu_online(unsigned int cpu)
8658c2ecf20Sopenharmony_ci{
8668c2ecf20Sopenharmony_ci	/* Close i2c bus if it was used for tb sync */
8678c2ecf20Sopenharmony_ci	if (pmac_tb_clock_chip_host && smp_core99_host_open) {
8688c2ecf20Sopenharmony_ci		pmac_i2c_close(pmac_tb_clock_chip_host);
8698c2ecf20Sopenharmony_ci		smp_core99_host_open = 0;
8708c2ecf20Sopenharmony_ci	}
8718c2ecf20Sopenharmony_ci	return 0;
8728c2ecf20Sopenharmony_ci}
8738c2ecf20Sopenharmony_ci#endif /* CONFIG_HOTPLUG_CPU */
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_cistatic void __init smp_core99_bringup_done(void)
8768c2ecf20Sopenharmony_ci{
8778c2ecf20Sopenharmony_ci	extern void g5_phy_disable_cpu1(void);
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	/* Close i2c bus if it was used for tb sync */
8808c2ecf20Sopenharmony_ci	if (pmac_tb_clock_chip_host)
8818c2ecf20Sopenharmony_ci		pmac_i2c_close(pmac_tb_clock_chip_host);
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	/* If we didn't start the second CPU, we must take
8848c2ecf20Sopenharmony_ci	 * it off the bus.
8858c2ecf20Sopenharmony_ci	 */
8868c2ecf20Sopenharmony_ci	if (of_machine_is_compatible("MacRISC4") &&
8878c2ecf20Sopenharmony_ci	    num_online_cpus() < 2) {
8888c2ecf20Sopenharmony_ci		set_cpu_present(1, false);
8898c2ecf20Sopenharmony_ci		g5_phy_disable_cpu1();
8908c2ecf20Sopenharmony_ci	}
8918c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
8928c2ecf20Sopenharmony_ci	cpuhp_setup_state_nocalls(CPUHP_POWERPC_PMAC_PREPARE,
8938c2ecf20Sopenharmony_ci				  "powerpc/pmac:prepare", smp_core99_cpu_prepare,
8948c2ecf20Sopenharmony_ci				  NULL);
8958c2ecf20Sopenharmony_ci	cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "powerpc/pmac:online",
8968c2ecf20Sopenharmony_ci				  smp_core99_cpu_online, NULL);
8978c2ecf20Sopenharmony_ci#endif
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	if (ppc_md.progress)
9008c2ecf20Sopenharmony_ci		ppc_md.progress("smp_core99_bringup_done", 0x349);
9018c2ecf20Sopenharmony_ci}
9028c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC64 */
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_cistatic int smp_core99_cpu_disable(void)
9078c2ecf20Sopenharmony_ci{
9088c2ecf20Sopenharmony_ci	int rc = generic_cpu_disable();
9098c2ecf20Sopenharmony_ci	if (rc)
9108c2ecf20Sopenharmony_ci		return rc;
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	mpic_cpu_set_priority(0xf);
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	cleanup_cpu_mmu_context();
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	return 0;
9178c2ecf20Sopenharmony_ci}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC32
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_cistatic void pmac_cpu_offline_self(void)
9228c2ecf20Sopenharmony_ci{
9238c2ecf20Sopenharmony_ci	int cpu = smp_processor_id();
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	local_irq_disable();
9268c2ecf20Sopenharmony_ci	idle_task_exit();
9278c2ecf20Sopenharmony_ci	pr_debug("CPU%d offline\n", cpu);
9288c2ecf20Sopenharmony_ci	generic_set_cpu_dead(cpu);
9298c2ecf20Sopenharmony_ci	smp_wmb();
9308c2ecf20Sopenharmony_ci	mb();
9318c2ecf20Sopenharmony_ci	low_cpu_offline_self();
9328c2ecf20Sopenharmony_ci}
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci#else /* CONFIG_PPC32 */
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_cistatic void pmac_cpu_offline_self(void)
9378c2ecf20Sopenharmony_ci{
9388c2ecf20Sopenharmony_ci	int cpu = smp_processor_id();
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	local_irq_disable();
9418c2ecf20Sopenharmony_ci	idle_task_exit();
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	/*
9448c2ecf20Sopenharmony_ci	 * turn off as much as possible, we'll be
9458c2ecf20Sopenharmony_ci	 * kicked out as this will only be invoked
9468c2ecf20Sopenharmony_ci	 * on core99 platforms for now ...
9478c2ecf20Sopenharmony_ci	 */
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	printk(KERN_INFO "CPU#%d offline\n", cpu);
9508c2ecf20Sopenharmony_ci	generic_set_cpu_dead(cpu);
9518c2ecf20Sopenharmony_ci	smp_wmb();
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	/*
9548c2ecf20Sopenharmony_ci	 * Re-enable interrupts. The NAP code needs to enable them
9558c2ecf20Sopenharmony_ci	 * anyways, do it now so we deal with the case where one already
9568c2ecf20Sopenharmony_ci	 * happened while soft-disabled.
9578c2ecf20Sopenharmony_ci	 * We shouldn't get any external interrupts, only decrementer, and the
9588c2ecf20Sopenharmony_ci	 * decrementer handler is safe for use on offline CPUs
9598c2ecf20Sopenharmony_ci	 */
9608c2ecf20Sopenharmony_ci	local_irq_enable();
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	while (1) {
9638c2ecf20Sopenharmony_ci		/* let's not take timer interrupts too often ... */
9648c2ecf20Sopenharmony_ci		set_dec(0x7fffffff);
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci		/* Enter NAP mode */
9678c2ecf20Sopenharmony_ci		power4_idle();
9688c2ecf20Sopenharmony_ci	}
9698c2ecf20Sopenharmony_ci}
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci#endif /* else CONFIG_PPC32 */
9728c2ecf20Sopenharmony_ci#endif /* CONFIG_HOTPLUG_CPU */
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci/* Core99 Macs (dual G4s and G5s) */
9758c2ecf20Sopenharmony_cistatic struct smp_ops_t core99_smp_ops = {
9768c2ecf20Sopenharmony_ci	.message_pass	= smp_mpic_message_pass,
9778c2ecf20Sopenharmony_ci	.probe		= smp_core99_probe,
9788c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64
9798c2ecf20Sopenharmony_ci	.bringup_done	= smp_core99_bringup_done,
9808c2ecf20Sopenharmony_ci#endif
9818c2ecf20Sopenharmony_ci	.kick_cpu	= smp_core99_kick_cpu,
9828c2ecf20Sopenharmony_ci	.setup_cpu	= smp_core99_setup_cpu,
9838c2ecf20Sopenharmony_ci	.give_timebase	= smp_core99_give_timebase,
9848c2ecf20Sopenharmony_ci	.take_timebase	= smp_core99_take_timebase,
9858c2ecf20Sopenharmony_ci#if defined(CONFIG_HOTPLUG_CPU)
9868c2ecf20Sopenharmony_ci	.cpu_disable	= smp_core99_cpu_disable,
9878c2ecf20Sopenharmony_ci	.cpu_die	= generic_cpu_die,
9888c2ecf20Sopenharmony_ci#endif
9898c2ecf20Sopenharmony_ci};
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_civoid __init pmac_setup_smp(void)
9928c2ecf20Sopenharmony_ci{
9938c2ecf20Sopenharmony_ci	struct device_node *np;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	/* Check for Core99 */
9968c2ecf20Sopenharmony_ci	np = of_find_node_by_name(NULL, "uni-n");
9978c2ecf20Sopenharmony_ci	if (!np)
9988c2ecf20Sopenharmony_ci		np = of_find_node_by_name(NULL, "u3");
9998c2ecf20Sopenharmony_ci	if (!np)
10008c2ecf20Sopenharmony_ci		np = of_find_node_by_name(NULL, "u4");
10018c2ecf20Sopenharmony_ci	if (np) {
10028c2ecf20Sopenharmony_ci		of_node_put(np);
10038c2ecf20Sopenharmony_ci		smp_ops = &core99_smp_ops;
10048c2ecf20Sopenharmony_ci	}
10058c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_PMAC32_PSURGE
10068c2ecf20Sopenharmony_ci	else {
10078c2ecf20Sopenharmony_ci		/* We have to set bits in cpu_possible_mask here since the
10088c2ecf20Sopenharmony_ci		 * secondary CPU(s) aren't in the device tree. Various
10098c2ecf20Sopenharmony_ci		 * things won't be initialized for CPUs not in the possible
10108c2ecf20Sopenharmony_ci		 * map, so we really need to fix it up here.
10118c2ecf20Sopenharmony_ci		 */
10128c2ecf20Sopenharmony_ci		int cpu;
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci		for (cpu = 1; cpu < 4 && cpu < NR_CPUS; ++cpu)
10158c2ecf20Sopenharmony_ci			set_cpu_possible(cpu, true);
10168c2ecf20Sopenharmony_ci		smp_ops = &psurge_smp_ops;
10178c2ecf20Sopenharmony_ci	}
10188c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC_PMAC32_PSURGE */
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
10218c2ecf20Sopenharmony_ci	smp_ops->cpu_offline_self = pmac_cpu_offline_self;
10228c2ecf20Sopenharmony_ci#endif
10238c2ecf20Sopenharmony_ci}
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci
1026