162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * PPC476 board specific routines
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2010 Torez Smith, IBM Corporation.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Based on earlier code:
862306a36Sopenharmony_ci *    Matt Porter <mporter@kernel.crashing.org>
962306a36Sopenharmony_ci *    Copyright 2002-2005 MontaVista Software Inc.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *    Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
1262306a36Sopenharmony_ci *    Copyright (c) 2003-2005 Zultys Technologies
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *    Rewritten and ported to the merged powerpc tree:
1562306a36Sopenharmony_ci *    Copyright 2007 David Gibson <dwg@au1.ibm.com>, IBM Corporation.
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/init.h>
1962306a36Sopenharmony_ci#include <linux/of_platform.h>
2062306a36Sopenharmony_ci#include <linux/rtc.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <asm/machdep.h>
2362306a36Sopenharmony_ci#include <asm/prom.h>
2462306a36Sopenharmony_ci#include <asm/udbg.h>
2562306a36Sopenharmony_ci#include <asm/time.h>
2662306a36Sopenharmony_ci#include <asm/uic.h>
2762306a36Sopenharmony_ci#include <asm/ppc4xx.h>
2862306a36Sopenharmony_ci#include <asm/mpic.h>
2962306a36Sopenharmony_ci#include <asm/mmu.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic const struct of_device_id iss4xx_of_bus[] __initconst = {
3262306a36Sopenharmony_ci	{ .compatible = "ibm,plb4", },
3362306a36Sopenharmony_ci	{ .compatible = "ibm,plb6", },
3462306a36Sopenharmony_ci	{ .compatible = "ibm,opb", },
3562306a36Sopenharmony_ci	{ .compatible = "ibm,ebc", },
3662306a36Sopenharmony_ci	{},
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic int __init iss4xx_device_probe(void)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	of_platform_bus_probe(NULL, iss4xx_of_bus, NULL);
4262306a36Sopenharmony_ci	of_instantiate_rtc();
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	return 0;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_cimachine_device_initcall(iss4xx, iss4xx_device_probe);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* We can have either UICs or MPICs */
4962306a36Sopenharmony_cistatic void __init iss4xx_init_irq(void)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct device_node *np;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	/* Find top level interrupt controller */
5462306a36Sopenharmony_ci	for_each_node_with_property(np, "interrupt-controller") {
5562306a36Sopenharmony_ci		if (!of_property_present(np, "interrupts"))
5662306a36Sopenharmony_ci			break;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci	if (np == NULL)
5962306a36Sopenharmony_ci		panic("Can't find top level interrupt controller");
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	/* Check type and do appropriate initialization */
6262306a36Sopenharmony_ci	if (of_device_is_compatible(np, "ibm,uic")) {
6362306a36Sopenharmony_ci		uic_init_tree();
6462306a36Sopenharmony_ci		ppc_md.get_irq = uic_get_irq;
6562306a36Sopenharmony_ci#ifdef CONFIG_MPIC
6662306a36Sopenharmony_ci	} else if (of_device_is_compatible(np, "chrp,open-pic")) {
6762306a36Sopenharmony_ci		/* The MPIC driver will get everything it needs from the
6862306a36Sopenharmony_ci		 * device-tree, just pass 0 to all arguments
6962306a36Sopenharmony_ci		 */
7062306a36Sopenharmony_ci		struct mpic *mpic = mpic_alloc(np, 0, MPIC_NO_RESET, 0, 0, " MPIC     ");
7162306a36Sopenharmony_ci		BUG_ON(mpic == NULL);
7262306a36Sopenharmony_ci		mpic_init(mpic);
7362306a36Sopenharmony_ci		ppc_md.get_irq = mpic_get_irq;
7462306a36Sopenharmony_ci#endif
7562306a36Sopenharmony_ci	} else
7662306a36Sopenharmony_ci		panic("Unrecognized top level interrupt controller");
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci#ifdef CONFIG_SMP
8062306a36Sopenharmony_cistatic void smp_iss4xx_setup_cpu(int cpu)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	mpic_setup_this_cpu();
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic int smp_iss4xx_kick_cpu(int cpu)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct device_node *cpunode = of_get_cpu_node(cpu, NULL);
8862306a36Sopenharmony_ci	const u64 *spin_table_addr_prop;
8962306a36Sopenharmony_ci	u32 *spin_table;
9062306a36Sopenharmony_ci	extern void start_secondary_47x(void);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	BUG_ON(cpunode == NULL);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* Assume spin table. We could test for the enable-method in
9562306a36Sopenharmony_ci	 * the device-tree but currently there's little point as it's
9662306a36Sopenharmony_ci	 * our only supported method
9762306a36Sopenharmony_ci	 */
9862306a36Sopenharmony_ci	spin_table_addr_prop = of_get_property(cpunode, "cpu-release-addr",
9962306a36Sopenharmony_ci					       NULL);
10062306a36Sopenharmony_ci	if (spin_table_addr_prop == NULL) {
10162306a36Sopenharmony_ci		pr_err("CPU%d: Can't start, missing cpu-release-addr !\n", cpu);
10262306a36Sopenharmony_ci		return -ENOENT;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	/* Assume it's mapped as part of the linear mapping. This is a bit
10662306a36Sopenharmony_ci	 * fishy but will work fine for now
10762306a36Sopenharmony_ci	 */
10862306a36Sopenharmony_ci	spin_table = (u32 *)__va(*spin_table_addr_prop);
10962306a36Sopenharmony_ci	pr_debug("CPU%d: Spin table mapped at %p\n", cpu, spin_table);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	spin_table[3] = cpu;
11262306a36Sopenharmony_ci	smp_wmb();
11362306a36Sopenharmony_ci	spin_table[1] = __pa(start_secondary_47x);
11462306a36Sopenharmony_ci	mb();
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return 0;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic struct smp_ops_t iss_smp_ops = {
12062306a36Sopenharmony_ci	.probe		= smp_mpic_probe,
12162306a36Sopenharmony_ci	.message_pass	= smp_mpic_message_pass,
12262306a36Sopenharmony_ci	.setup_cpu	= smp_iss4xx_setup_cpu,
12362306a36Sopenharmony_ci	.kick_cpu	= smp_iss4xx_kick_cpu,
12462306a36Sopenharmony_ci	.give_timebase	= smp_generic_give_timebase,
12562306a36Sopenharmony_ci	.take_timebase	= smp_generic_take_timebase,
12662306a36Sopenharmony_ci};
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic void __init iss4xx_smp_init(void)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	if (mmu_has_feature(MMU_FTR_TYPE_47x))
13162306a36Sopenharmony_ci		smp_ops = &iss_smp_ops;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci#else /* CONFIG_SMP */
13562306a36Sopenharmony_cistatic void __init iss4xx_smp_init(void) { }
13662306a36Sopenharmony_ci#endif /* CONFIG_SMP */
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic void __init iss4xx_setup_arch(void)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	iss4xx_smp_init();
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cidefine_machine(iss4xx) {
14462306a36Sopenharmony_ci	.name			= "ISS-4xx",
14562306a36Sopenharmony_ci	.compatible		= "ibm,iss-4xx",
14662306a36Sopenharmony_ci	.progress		= udbg_progress,
14762306a36Sopenharmony_ci	.init_IRQ		= iss4xx_init_irq,
14862306a36Sopenharmony_ci	.setup_arch		= iss4xx_setup_arch,
14962306a36Sopenharmony_ci	.restart		= ppc4xx_reset_system,
15062306a36Sopenharmony_ci};
151