| // SPDX-License-Identifier: GPL-2.0 | 
 | /* | 
 |  * ip30-smp.c: SMP on IP30 architecture. | 
 |  * Based off of the original IP30 SMP code, with inspiration from ip27-smp.c | 
 |  * and smp-bmips.c. | 
 |  * | 
 |  * Copyright (C) 2005-2007 Stanislaw Skowronek <skylark@unaligned.org> | 
 |  *               2006-2007, 2014-2015 Joshua Kinard <kumba@gentoo.org> | 
 |  *               2009 Johannes Dickgreber <tanzy@gmx.de> | 
 |  */ | 
 |  | 
 | #include <linux/init.h> | 
 | #include <linux/sched.h> | 
 | #include <linux/sched/task_stack.h> | 
 |  | 
 | #include <asm/time.h> | 
 | #include <asm/sgi/heart.h> | 
 |  | 
 | #include "ip30-common.h" | 
 |  | 
 | #define MPCONF_MAGIC	0xbaddeed2 | 
 | #define	MPCONF_ADDR	0xa800000000000600L | 
 | #define MPCONF_SIZE	0x80 | 
 | #define MPCONF(x)	(MPCONF_ADDR + (x) * MPCONF_SIZE) | 
 |  | 
 | /* HEART can theoretically do 4 CPUs, but only 2 are physically possible */ | 
 | #define MP_NCPU		2 | 
 |  | 
 | struct mpconf { | 
 | 	u32 magic; | 
 | 	u32 prid; | 
 | 	u32 physid; | 
 | 	u32 virtid; | 
 | 	u32 scachesz; | 
 | 	u16 fanloads; | 
 | 	u16 res; | 
 | 	void *launch; | 
 | 	void *rendezvous; | 
 | 	u64 res2[3]; | 
 | 	void *stackaddr; | 
 | 	void *lnch_parm; | 
 | 	void *rndv_parm; | 
 | 	u32 idleflag; | 
 | }; | 
 |  | 
 | static void ip30_smp_send_ipi_single(int cpu, u32 action) | 
 | { | 
 | 	int irq; | 
 |  | 
 | 	switch (action) { | 
 | 	case SMP_RESCHEDULE_YOURSELF: | 
 | 		irq = HEART_L2_INT_RESCHED_CPU_0; | 
 | 		break; | 
 | 	case SMP_CALL_FUNCTION: | 
 | 		irq = HEART_L2_INT_CALL_CPU_0; | 
 | 		break; | 
 | 	default: | 
 | 		panic("IP30: Unknown action value in %s!\n", __func__); | 
 | 	} | 
 |  | 
 | 	irq += cpu; | 
 |  | 
 | 	/* Poke the other CPU -- it's got mail! */ | 
 | 	heart_write(BIT_ULL(irq), &heart_regs->set_isr); | 
 | } | 
 |  | 
 | static void ip30_smp_send_ipi_mask(const struct cpumask *mask, u32 action) | 
 | { | 
 | 	u32 i; | 
 |  | 
 | 	for_each_cpu(i, mask) | 
 | 		ip30_smp_send_ipi_single(i, action); | 
 | } | 
 |  | 
 | static void __init ip30_smp_setup(void) | 
 | { | 
 | 	int i; | 
 | 	int ncpu = 0; | 
 | 	struct mpconf *mpc; | 
 |  | 
 | 	init_cpu_possible(cpumask_of(0)); | 
 |  | 
 | 	/* Scan the MPCONF structure and enumerate available CPUs. */ | 
 | 	for (i = 0; i < MP_NCPU; i++) { | 
 | 		mpc = (struct mpconf *)MPCONF(i); | 
 | 		if (mpc->magic == MPCONF_MAGIC) { | 
 | 			set_cpu_possible(i, true); | 
 | 			__cpu_number_map[i] = ++ncpu; | 
 | 			__cpu_logical_map[ncpu] = i; | 
 | 			pr_info("IP30: Slot: %d, PrID: %.8x, PhyID: %d, VirtID: %d\n", | 
 | 				i, mpc->prid, mpc->physid, mpc->virtid); | 
 | 		} | 
 | 	} | 
 | 	pr_info("IP30: Detected %d CPU(s) present.\n", ncpu); | 
 |  | 
 | 	/* | 
 | 	 * Set the coherency algorithm to '5' (cacheable coherent | 
 | 	 * exclusive on write).  This is needed on IP30 SMP, especially | 
 | 	 * for R14000 CPUs, otherwise, instruction bus errors will | 
 | 	 * occur upon reaching userland. | 
 | 	 */ | 
 | 	change_c0_config(CONF_CM_CMASK, CONF_CM_CACHABLE_COW); | 
 | } | 
 |  | 
 | static void __init ip30_smp_prepare_cpus(unsigned int max_cpus) | 
 | { | 
 | 	/* nothing to do here */ | 
 | } | 
 |  | 
 | static int __init ip30_smp_boot_secondary(int cpu, struct task_struct *idle) | 
 | { | 
 | 	struct mpconf *mpc = (struct mpconf *)MPCONF(cpu); | 
 |  | 
 | 	/* Stack pointer (sp). */ | 
 | 	mpc->stackaddr = (void *)__KSTK_TOS(idle); | 
 |  | 
 | 	/* Global pointer (gp). */ | 
 | 	mpc->lnch_parm = task_thread_info(idle); | 
 |  | 
 | 	mb(); /* make sure stack and lparm are written */ | 
 |  | 
 | 	/* Boot CPUx. */ | 
 | 	mpc->launch = smp_bootstrap; | 
 |  | 
 | 	/* CPUx now executes smp_bootstrap, then ip30_smp_finish */ | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void __init ip30_smp_init_cpu(void) | 
 | { | 
 | 	ip30_per_cpu_init(); | 
 | } | 
 |  | 
 | static void __init ip30_smp_finish(void) | 
 | { | 
 | 	enable_percpu_irq(get_c0_compare_int(), IRQ_TYPE_NONE); | 
 | 	local_irq_enable(); | 
 | } | 
 |  | 
 | struct plat_smp_ops __read_mostly ip30_smp_ops = { | 
 | 	.send_ipi_single	= ip30_smp_send_ipi_single, | 
 | 	.send_ipi_mask		= ip30_smp_send_ipi_mask, | 
 | 	.smp_setup		= ip30_smp_setup, | 
 | 	.prepare_cpus		= ip30_smp_prepare_cpus, | 
 | 	.boot_secondary		= ip30_smp_boot_secondary, | 
 | 	.init_secondary		= ip30_smp_init_cpu, | 
 | 	.smp_finish		= ip30_smp_finish, | 
 | 	.prepare_boot_cpu	= ip30_smp_init_cpu, | 
 | }; |