xref: /linux/arch/hexagon/kernel/smp.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
108dbd0f8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
243afdf50SRichard Kuo /*
343afdf50SRichard Kuo  * SMP support for Hexagon
443afdf50SRichard Kuo  *
5e1858b2aSRichard Kuo  * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
643afdf50SRichard Kuo  */
743afdf50SRichard Kuo 
843afdf50SRichard Kuo #include <linux/err.h>
943afdf50SRichard Kuo #include <linux/errno.h>
1043afdf50SRichard Kuo #include <linux/kernel.h>
1143afdf50SRichard Kuo #include <linux/init.h>
1243afdf50SRichard Kuo #include <linux/interrupt.h>
1343afdf50SRichard Kuo #include <linux/module.h>
1443afdf50SRichard Kuo #include <linux/percpu.h>
1568e21be2SIngo Molnar #include <linux/sched/mm.h>
1643afdf50SRichard Kuo #include <linux/smp.h>
1743afdf50SRichard Kuo #include <linux/spinlock.h>
18e00574b7SRichard Kuo #include <linux/cpu.h>
19589ee628SIngo Molnar #include <linux/mm_types.h>
2043afdf50SRichard Kuo 
2143afdf50SRichard Kuo #include <asm/time.h>    /*  timer_interrupt  */
2243afdf50SRichard Kuo #include <asm/hexagon_vm.h>
2343afdf50SRichard Kuo 
2443afdf50SRichard Kuo #define BASE_IPI_IRQ 26
2543afdf50SRichard Kuo 
2643afdf50SRichard Kuo /*
270b5f9c00SRusty Russell  * cpu_possible_mask needs to be filled out prior to setup_per_cpu_areas
2843afdf50SRichard Kuo  * (which is prior to any of our smp_prepare_cpu crap), in order to set
2943afdf50SRichard Kuo  * up the...  per_cpu areas.
3043afdf50SRichard Kuo  */
3143afdf50SRichard Kuo 
3243afdf50SRichard Kuo struct ipi_data {
3343afdf50SRichard Kuo 	unsigned long bits;
3443afdf50SRichard Kuo };
3543afdf50SRichard Kuo 
3643afdf50SRichard Kuo static DEFINE_PER_CPU(struct ipi_data, ipi_data);
3743afdf50SRichard Kuo 
__handle_ipi(unsigned long * ops,struct ipi_data * ipi,int cpu)3843afdf50SRichard Kuo static inline void __handle_ipi(unsigned long *ops, struct ipi_data *ipi,
3943afdf50SRichard Kuo 				int cpu)
4043afdf50SRichard Kuo {
4143afdf50SRichard Kuo 	unsigned long msg = 0;
4243afdf50SRichard Kuo 	do {
4343afdf50SRichard Kuo 		msg = find_next_bit(ops, BITS_PER_LONG, msg+1);
4443afdf50SRichard Kuo 
4543afdf50SRichard Kuo 		switch (msg) {
4643afdf50SRichard Kuo 
4743afdf50SRichard Kuo 		case IPI_TIMER:
4843afdf50SRichard Kuo 			ipi_timer();
4943afdf50SRichard Kuo 			break;
5043afdf50SRichard Kuo 
5143afdf50SRichard Kuo 		case IPI_CALL_FUNC:
5243afdf50SRichard Kuo 			generic_smp_call_function_interrupt();
5343afdf50SRichard Kuo 			break;
5443afdf50SRichard Kuo 
5543afdf50SRichard Kuo 		case IPI_CPU_STOP:
5643afdf50SRichard Kuo 			/*
5743afdf50SRichard Kuo 			 * call vmstop()
5843afdf50SRichard Kuo 			 */
5943afdf50SRichard Kuo 			__vmstop();
6043afdf50SRichard Kuo 			break;
6143afdf50SRichard Kuo 
6243afdf50SRichard Kuo 		case IPI_RESCHEDULE:
6343afdf50SRichard Kuo 			scheduler_ipi();
6443afdf50SRichard Kuo 			break;
6543afdf50SRichard Kuo 		}
6643afdf50SRichard Kuo 	} while (msg < BITS_PER_LONG);
6743afdf50SRichard Kuo }
6843afdf50SRichard Kuo 
6943afdf50SRichard Kuo /*  Used for IPI call from other CPU's to unmask int  */
smp_vm_unmask_irq(void * info)7043afdf50SRichard Kuo void smp_vm_unmask_irq(void *info)
7143afdf50SRichard Kuo {
7243afdf50SRichard Kuo 	__vmintop_locen((long) info);
7343afdf50SRichard Kuo }
7443afdf50SRichard Kuo 
7543afdf50SRichard Kuo 
7643afdf50SRichard Kuo /*
7743afdf50SRichard Kuo  * This is based on Alpha's IPI stuff.
7843afdf50SRichard Kuo  * Supposed to take (int, void*) as args now.
7943afdf50SRichard Kuo  * Specifically, first arg is irq, second is the irq_desc.
8043afdf50SRichard Kuo  */
8143afdf50SRichard Kuo 
handle_ipi(int irq,void * desc)82*ef14250eSNathan Chancellor static irqreturn_t handle_ipi(int irq, void *desc)
8343afdf50SRichard Kuo {
8443afdf50SRichard Kuo 	int cpu = smp_processor_id();
8543afdf50SRichard Kuo 	struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
8643afdf50SRichard Kuo 	unsigned long ops;
8743afdf50SRichard Kuo 
8843afdf50SRichard Kuo 	while ((ops = xchg(&ipi->bits, 0)) != 0)
8943afdf50SRichard Kuo 		__handle_ipi(&ops, ipi, cpu);
9043afdf50SRichard Kuo 	return IRQ_HANDLED;
9143afdf50SRichard Kuo }
9243afdf50SRichard Kuo 
send_ipi(const struct cpumask * cpumask,enum ipi_message_type msg)9343afdf50SRichard Kuo void send_ipi(const struct cpumask *cpumask, enum ipi_message_type msg)
9443afdf50SRichard Kuo {
9543afdf50SRichard Kuo 	unsigned long flags;
9643afdf50SRichard Kuo 	unsigned long cpu;
9743afdf50SRichard Kuo 	unsigned long retval;
9843afdf50SRichard Kuo 
9943afdf50SRichard Kuo 	local_irq_save(flags);
10043afdf50SRichard Kuo 
10143afdf50SRichard Kuo 	for_each_cpu(cpu, cpumask) {
10243afdf50SRichard Kuo 		struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
10343afdf50SRichard Kuo 
10443afdf50SRichard Kuo 		set_bit(msg, &ipi->bits);
10543afdf50SRichard Kuo 		/*  Possible barrier here  */
10643afdf50SRichard Kuo 		retval = __vmintop_post(BASE_IPI_IRQ+cpu);
10743afdf50SRichard Kuo 
10843afdf50SRichard Kuo 		if (retval != 0) {
10943afdf50SRichard Kuo 			printk(KERN_ERR "interrupt %ld not configured?\n",
11043afdf50SRichard Kuo 				BASE_IPI_IRQ+cpu);
11143afdf50SRichard Kuo 		}
11243afdf50SRichard Kuo 	}
11343afdf50SRichard Kuo 
11443afdf50SRichard Kuo 	local_irq_restore(flags);
11543afdf50SRichard Kuo }
11643afdf50SRichard Kuo 
11743afdf50SRichard Kuo /*
11843afdf50SRichard Kuo  * interrupts should already be disabled from the VM
11943afdf50SRichard Kuo  * SP should already be correct; need to set THREADINFO_REG
12043afdf50SRichard Kuo  * to point to current thread info
12143afdf50SRichard Kuo  */
12243afdf50SRichard Kuo 
start_secondary(void)123*ef14250eSNathan Chancellor static void start_secondary(void)
12443afdf50SRichard Kuo {
12543afdf50SRichard Kuo 	unsigned long thread_ptr;
12645b26ddeSafzal mohammed 	unsigned int cpu, irq;
12743afdf50SRichard Kuo 
12843afdf50SRichard Kuo 	/*  Calculate thread_info pointer from stack pointer  */
12943afdf50SRichard Kuo 	__asm__ __volatile__(
13043afdf50SRichard Kuo 		"%0 = SP;\n"
13143afdf50SRichard Kuo 		: "=r" (thread_ptr)
13243afdf50SRichard Kuo 	);
13343afdf50SRichard Kuo 
13443afdf50SRichard Kuo 	thread_ptr = thread_ptr & ~(THREAD_SIZE-1);
13543afdf50SRichard Kuo 
13643afdf50SRichard Kuo 	__asm__ __volatile__(
13743afdf50SRichard Kuo 		QUOTED_THREADINFO_REG " = %0;\n"
13843afdf50SRichard Kuo 		:
13943afdf50SRichard Kuo 		: "r" (thread_ptr)
14043afdf50SRichard Kuo 	);
14143afdf50SRichard Kuo 
14243afdf50SRichard Kuo 	/*  Set the memory struct  */
143f1f10076SVegard Nossum 	mmgrab(&init_mm);
14443afdf50SRichard Kuo 	current->active_mm = &init_mm;
14543afdf50SRichard Kuo 
14643afdf50SRichard Kuo 	cpu = smp_processor_id();
14743afdf50SRichard Kuo 
14845b26ddeSafzal mohammed 	irq = BASE_IPI_IRQ + cpu;
14945b26ddeSafzal mohammed 	if (request_irq(irq, handle_ipi, IRQF_TRIGGER_RISING, "ipi_handler",
15045b26ddeSafzal mohammed 			NULL))
15145b26ddeSafzal mohammed 		pr_err("Failed to request irq %u (ipi_handler)\n", irq);
15243afdf50SRichard Kuo 
15343afdf50SRichard Kuo 	/*  Register the clock_event dummy  */
15443afdf50SRichard Kuo 	setup_percpu_clockdev();
15543afdf50SRichard Kuo 
15643afdf50SRichard Kuo 	printk(KERN_INFO "%s cpu %d\n", __func__, current_thread_info()->cpu);
15743afdf50SRichard Kuo 
15857f27ccaSSrivatsa S. Bhat 	notify_cpu_starting(cpu);
15957f27ccaSSrivatsa S. Bhat 
16043afdf50SRichard Kuo 	set_cpu_online(cpu, true);
16157f27ccaSSrivatsa S. Bhat 
16243afdf50SRichard Kuo 	local_irq_enable();
16343afdf50SRichard Kuo 
164fc6d73d6SThomas Gleixner 	cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
16543afdf50SRichard Kuo }
16643afdf50SRichard Kuo 
16743afdf50SRichard Kuo 
16843afdf50SRichard Kuo /*
16943afdf50SRichard Kuo  * called once for each present cpu
17043afdf50SRichard Kuo  * apparently starts up the CPU and then
17143afdf50SRichard Kuo  * maintains control until "cpu_online(cpu)" is set.
17243afdf50SRichard Kuo  */
17343afdf50SRichard Kuo 
__cpu_up(unsigned int cpu,struct task_struct * idle)1747ddc8399SPaul Gortmaker int __cpu_up(unsigned int cpu, struct task_struct *idle)
17543afdf50SRichard Kuo {
1763b0132ceSThomas Gleixner 	struct thread_info *thread = (struct thread_info *)idle->stack;
17743afdf50SRichard Kuo 	void *stack_start;
17843afdf50SRichard Kuo 
17943afdf50SRichard Kuo 	thread->cpu = cpu;
18043afdf50SRichard Kuo 
18143afdf50SRichard Kuo 	/*  Boot to the head.  */
18243afdf50SRichard Kuo 	stack_start =  ((void *) thread) + THREAD_SIZE;
18343afdf50SRichard Kuo 	__vmstart(start_secondary, stack_start);
18443afdf50SRichard Kuo 
1850b5f9c00SRusty Russell 	while (!cpu_online(cpu))
18643afdf50SRichard Kuo 		barrier();
18743afdf50SRichard Kuo 
18843afdf50SRichard Kuo 	return 0;
18943afdf50SRichard Kuo }
19043afdf50SRichard Kuo 
smp_cpus_done(unsigned int max_cpus)19143afdf50SRichard Kuo void __init smp_cpus_done(unsigned int max_cpus)
19243afdf50SRichard Kuo {
19343afdf50SRichard Kuo }
19443afdf50SRichard Kuo 
smp_prepare_cpus(unsigned int max_cpus)19543afdf50SRichard Kuo void __init smp_prepare_cpus(unsigned int max_cpus)
19643afdf50SRichard Kuo {
19745b26ddeSafzal mohammed 	int i, irq = BASE_IPI_IRQ;
19843afdf50SRichard Kuo 
19943afdf50SRichard Kuo 	/*
20043afdf50SRichard Kuo 	 * should eventually have some sort of machine
20143afdf50SRichard Kuo 	 * descriptor that has this stuff
20243afdf50SRichard Kuo 	 */
20343afdf50SRichard Kuo 
20443afdf50SRichard Kuo 	/*  Right now, let's just fake it. */
20543afdf50SRichard Kuo 	for (i = 0; i < max_cpus; i++)
2060b5f9c00SRusty Russell 		set_cpu_present(i, true);
20743afdf50SRichard Kuo 
20843afdf50SRichard Kuo 	/*  Also need to register the interrupts for IPI  */
20945b26ddeSafzal mohammed 	if (max_cpus > 1) {
21045b26ddeSafzal mohammed 		if (request_irq(irq, handle_ipi, IRQF_TRIGGER_RISING,
21145b26ddeSafzal mohammed 				"ipi_handler", NULL))
21245b26ddeSafzal mohammed 			pr_err("Failed to request irq %d (ipi_handler)\n", irq);
21345b26ddeSafzal mohammed 	}
21443afdf50SRichard Kuo }
21543afdf50SRichard Kuo 
arch_smp_send_reschedule(int cpu)2164c8c3c7fSValentin Schneider void arch_smp_send_reschedule(int cpu)
21743afdf50SRichard Kuo {
21843afdf50SRichard Kuo 	send_ipi(cpumask_of(cpu), IPI_RESCHEDULE);
21943afdf50SRichard Kuo }
22043afdf50SRichard Kuo 
smp_send_stop(void)22143afdf50SRichard Kuo void smp_send_stop(void)
22243afdf50SRichard Kuo {
22343afdf50SRichard Kuo 	struct cpumask targets;
22443afdf50SRichard Kuo 	cpumask_copy(&targets, cpu_online_mask);
22543afdf50SRichard Kuo 	cpumask_clear_cpu(smp_processor_id(), &targets);
22643afdf50SRichard Kuo 	send_ipi(&targets, IPI_CPU_STOP);
22743afdf50SRichard Kuo }
22843afdf50SRichard Kuo 
arch_send_call_function_single_ipi(int cpu)22943afdf50SRichard Kuo void arch_send_call_function_single_ipi(int cpu)
23043afdf50SRichard Kuo {
231bd09f606SJiang Liu 	send_ipi(cpumask_of(cpu), IPI_CALL_FUNC);
23243afdf50SRichard Kuo }
23343afdf50SRichard Kuo 
arch_send_call_function_ipi_mask(const struct cpumask * mask)23443afdf50SRichard Kuo void arch_send_call_function_ipi_mask(const struct cpumask *mask)
23543afdf50SRichard Kuo {
23643afdf50SRichard Kuo 	send_ipi(mask, IPI_CALL_FUNC);
23743afdf50SRichard Kuo }
23843afdf50SRichard Kuo 
smp_start_cpus(void)23943afdf50SRichard Kuo void smp_start_cpus(void)
24043afdf50SRichard Kuo {
24143afdf50SRichard Kuo 	int i;
24243afdf50SRichard Kuo 
24343afdf50SRichard Kuo 	for (i = 0; i < NR_CPUS; i++)
2440b5f9c00SRusty Russell 		set_cpu_possible(i, true);
24543afdf50SRichard Kuo }
246