xref: /openbmc/linux/arch/xtensa/kernel/smp.c (revision 0f95df62)
1f615136cSMax Filippov /*
2f615136cSMax Filippov  * Xtensa SMP support functions.
3f615136cSMax Filippov  *
4f615136cSMax Filippov  * This file is subject to the terms and conditions of the GNU General Public
5f615136cSMax Filippov  * License.  See the file "COPYING" in the main directory of this archive
6f615136cSMax Filippov  * for more details.
7f615136cSMax Filippov  *
8f615136cSMax Filippov  * Copyright (C) 2008 - 2013 Tensilica Inc.
9f615136cSMax Filippov  *
10f615136cSMax Filippov  * Chris Zankel <chris@zankel.net>
11f615136cSMax Filippov  * Joe Taylor <joe@tensilica.com>
12f615136cSMax Filippov  * Pete Delaney <piet@tensilica.com
13f615136cSMax Filippov  */
14f615136cSMax Filippov 
15f615136cSMax Filippov #include <linux/cpu.h>
16f615136cSMax Filippov #include <linux/cpumask.h>
17f615136cSMax Filippov #include <linux/delay.h>
18f615136cSMax Filippov #include <linux/init.h>
19f615136cSMax Filippov #include <linux/interrupt.h>
20f615136cSMax Filippov #include <linux/irqdomain.h>
21f615136cSMax Filippov #include <linux/irq.h>
22f615136cSMax Filippov #include <linux/kdebug.h>
23f615136cSMax Filippov #include <linux/module.h>
24*0f95df62SRandy Dunlap #include <linux/profile.h>
2568e21be2SIngo Molnar #include <linux/sched/mm.h>
26ef8bd77fSIngo Molnar #include <linux/sched/hotplug.h>
2768db0cf1SIngo Molnar #include <linux/sched/task_stack.h>
28f615136cSMax Filippov #include <linux/reboot.h>
29f615136cSMax Filippov #include <linux/seq_file.h>
30f615136cSMax Filippov #include <linux/smp.h>
31f615136cSMax Filippov #include <linux/thread_info.h>
32f615136cSMax Filippov 
33f615136cSMax Filippov #include <asm/cacheflush.h>
3411e969bcSMax Filippov #include <asm/coprocessor.h>
35f615136cSMax Filippov #include <asm/kdebug.h>
36f615136cSMax Filippov #include <asm/mmu_context.h>
37f615136cSMax Filippov #include <asm/mxregs.h>
38f615136cSMax Filippov #include <asm/platform.h>
39f615136cSMax Filippov #include <asm/tlbflush.h>
40f615136cSMax Filippov #include <asm/traps.h>
41f615136cSMax Filippov 
42f615136cSMax Filippov #ifdef CONFIG_SMP
43f615136cSMax Filippov # if XCHAL_HAVE_S32C1I == 0
44f615136cSMax Filippov #  error "The S32C1I option is required for SMP."
45f615136cSMax Filippov # endif
46f615136cSMax Filippov #endif
47f615136cSMax Filippov 
4849b424feSMax Filippov static void system_invalidate_dcache_range(unsigned long start,
4949b424feSMax Filippov 		unsigned long size);
5049b424feSMax Filippov static void system_flush_invalidate_dcache_range(unsigned long start,
5149b424feSMax Filippov 		unsigned long size);
5249b424feSMax Filippov 
53f615136cSMax Filippov /* IPI (Inter Process Interrupt) */
54f615136cSMax Filippov 
55f615136cSMax Filippov #define IPI_IRQ	0
56f615136cSMax Filippov 
57f615136cSMax Filippov static irqreturn_t ipi_interrupt(int irq, void *dev_id);
58f615136cSMax Filippov 
ipi_init(void)59f615136cSMax Filippov void ipi_init(void)
60f615136cSMax Filippov {
61f615136cSMax Filippov 	unsigned irq = irq_create_mapping(NULL, IPI_IRQ);
6298060484Safzal mohammed 	if (request_irq(irq, ipi_interrupt, IRQF_PERCPU, "ipi", NULL))
6398060484Safzal mohammed 		pr_err("Failed to request irq %u (ipi)\n", irq);
64f615136cSMax Filippov }
65f615136cSMax Filippov 
get_core_count(void)66f615136cSMax Filippov static inline unsigned int get_core_count(void)
67f615136cSMax Filippov {
68f615136cSMax Filippov 	/* Bits 18..21 of SYSCFGID contain the core count minus 1. */
69f615136cSMax Filippov 	unsigned int syscfgid = get_er(SYSCFGID);
70f615136cSMax Filippov 	return ((syscfgid >> 18) & 0xf) + 1;
71f615136cSMax Filippov }
72f615136cSMax Filippov 
get_core_id(void)73f615136cSMax Filippov static inline int get_core_id(void)
74f615136cSMax Filippov {
75f615136cSMax Filippov 	/* Bits 0...18 of SYSCFGID contain the core id  */
76f615136cSMax Filippov 	unsigned int core_id = get_er(SYSCFGID);
77f615136cSMax Filippov 	return core_id & 0x3fff;
78f615136cSMax Filippov }
79f615136cSMax Filippov 
smp_prepare_cpus(unsigned int max_cpus)80f615136cSMax Filippov void __init smp_prepare_cpus(unsigned int max_cpus)
81f615136cSMax Filippov {
82f615136cSMax Filippov 	unsigned i;
83f615136cSMax Filippov 
848b1c42cdSMax Filippov 	for_each_possible_cpu(i)
85f615136cSMax Filippov 		set_cpu_present(i, true);
86f615136cSMax Filippov }
87f615136cSMax Filippov 
smp_init_cpus(void)88f615136cSMax Filippov void __init smp_init_cpus(void)
89f615136cSMax Filippov {
90f615136cSMax Filippov 	unsigned i;
91f615136cSMax Filippov 	unsigned int ncpus = get_core_count();
92f615136cSMax Filippov 	unsigned int core_id = get_core_id();
93f615136cSMax Filippov 
94f615136cSMax Filippov 	pr_info("%s: Core Count = %d\n", __func__, ncpus);
95f615136cSMax Filippov 	pr_info("%s: Core Id = %d\n", __func__, core_id);
96f615136cSMax Filippov 
9725384ce5SMax Filippov 	if (ncpus > NR_CPUS) {
9825384ce5SMax Filippov 		ncpus = NR_CPUS;
9925384ce5SMax Filippov 		pr_info("%s: limiting core count by %d\n", __func__, ncpus);
10025384ce5SMax Filippov 	}
10125384ce5SMax Filippov 
102f615136cSMax Filippov 	for (i = 0; i < ncpus; ++i)
103f615136cSMax Filippov 		set_cpu_possible(i, true);
104f615136cSMax Filippov }
105f615136cSMax Filippov 
smp_prepare_boot_cpu(void)106f615136cSMax Filippov void __init smp_prepare_boot_cpu(void)
107f615136cSMax Filippov {
108f615136cSMax Filippov 	unsigned int cpu = smp_processor_id();
109f615136cSMax Filippov 	BUG_ON(cpu != 0);
110f615136cSMax Filippov 	cpu_asid_cache(cpu) = ASID_USER_FIRST;
111f615136cSMax Filippov }
112f615136cSMax Filippov 
smp_cpus_done(unsigned int max_cpus)113f615136cSMax Filippov void __init smp_cpus_done(unsigned int max_cpus)
114f615136cSMax Filippov {
115f615136cSMax Filippov }
116f615136cSMax Filippov 
117f615136cSMax Filippov static int boot_secondary_processors = 1; /* Set with xt-gdb via .xt-gdb */
118f615136cSMax Filippov static DECLARE_COMPLETION(cpu_running);
119f615136cSMax Filippov 
secondary_start_kernel(void)12049b424feSMax Filippov void secondary_start_kernel(void)
121f615136cSMax Filippov {
122f615136cSMax Filippov 	struct mm_struct *mm = &init_mm;
123f615136cSMax Filippov 	unsigned int cpu = smp_processor_id();
124f615136cSMax Filippov 
125f615136cSMax Filippov 	init_mmu();
126f615136cSMax Filippov 
127900f4928SSinan Kaya #ifdef CONFIG_DEBUG_MISC
128f615136cSMax Filippov 	if (boot_secondary_processors == 0) {
129f615136cSMax Filippov 		pr_debug("%s: boot_secondary_processors:%d; Hanging cpu:%d\n",
130f615136cSMax Filippov 			__func__, boot_secondary_processors, cpu);
131f615136cSMax Filippov 		for (;;)
132f615136cSMax Filippov 			__asm__ __volatile__ ("waiti " __stringify(LOCKLEVEL));
133f615136cSMax Filippov 	}
134f615136cSMax Filippov 
135f615136cSMax Filippov 	pr_debug("%s: boot_secondary_processors:%d; Booting cpu:%d\n",
136f615136cSMax Filippov 		__func__, boot_secondary_processors, cpu);
137f615136cSMax Filippov #endif
138f615136cSMax Filippov 	/* Init EXCSAVE1 */
139f615136cSMax Filippov 
140f615136cSMax Filippov 	secondary_trap_init();
141f615136cSMax Filippov 
142f615136cSMax Filippov 	/* All kernel threads share the same mm context. */
143f615136cSMax Filippov 
1443fce371bSVegard Nossum 	mmget(mm);
145f1f10076SVegard Nossum 	mmgrab(mm);
146f615136cSMax Filippov 	current->active_mm = mm;
147f615136cSMax Filippov 	cpumask_set_cpu(cpu, mm_cpumask(mm));
148f615136cSMax Filippov 	enter_lazy_tlb(mm, current);
149f615136cSMax Filippov 
150f615136cSMax Filippov 	trace_hardirqs_off();
151f615136cSMax Filippov 
152f615136cSMax Filippov 	calibrate_delay();
153f615136cSMax Filippov 
154f615136cSMax Filippov 	notify_cpu_starting(cpu);
155f615136cSMax Filippov 
156f615136cSMax Filippov 	secondary_init_irq();
157f615136cSMax Filippov 	local_timer_setup(cpu);
158f615136cSMax Filippov 
159abf0ea65SKirill Tkhai 	set_cpu_online(cpu, true);
160abf0ea65SKirill Tkhai 
161f615136cSMax Filippov 	local_irq_enable();
162f615136cSMax Filippov 
163f615136cSMax Filippov 	complete(&cpu_running);
164f615136cSMax Filippov 
165fc6d73d6SThomas Gleixner 	cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
166f615136cSMax Filippov }
167f615136cSMax Filippov 
mx_cpu_start(void * p)168f615136cSMax Filippov static void mx_cpu_start(void *p)
169f615136cSMax Filippov {
170f615136cSMax Filippov 	unsigned cpu = (unsigned)p;
171f615136cSMax Filippov 	unsigned long run_stall_mask = get_er(MPSCORE);
172f615136cSMax Filippov 
173f615136cSMax Filippov 	set_er(run_stall_mask & ~(1u << cpu), MPSCORE);
174f615136cSMax Filippov 	pr_debug("%s: cpu: %d, run_stall_mask: %lx ---> %lx\n",
175f615136cSMax Filippov 			__func__, cpu, run_stall_mask, get_er(MPSCORE));
176f615136cSMax Filippov }
177f615136cSMax Filippov 
mx_cpu_stop(void * p)178f615136cSMax Filippov static void mx_cpu_stop(void *p)
179f615136cSMax Filippov {
180f615136cSMax Filippov 	unsigned cpu = (unsigned)p;
181f615136cSMax Filippov 	unsigned long run_stall_mask = get_er(MPSCORE);
182f615136cSMax Filippov 
183f615136cSMax Filippov 	set_er(run_stall_mask | (1u << cpu), MPSCORE);
184f615136cSMax Filippov 	pr_debug("%s: cpu: %d, run_stall_mask: %lx ---> %lx\n",
185f615136cSMax Filippov 			__func__, cpu, run_stall_mask, get_er(MPSCORE));
186f615136cSMax Filippov }
187f615136cSMax Filippov 
18849b424feSMax Filippov #ifdef CONFIG_HOTPLUG_CPU
18949b424feSMax Filippov unsigned long cpu_start_id __cacheline_aligned;
19049b424feSMax Filippov #endif
191f615136cSMax Filippov unsigned long cpu_start_ccount;
192f615136cSMax Filippov 
boot_secondary(unsigned int cpu,struct task_struct * ts)193f615136cSMax Filippov static int boot_secondary(unsigned int cpu, struct task_struct *ts)
194f615136cSMax Filippov {
195f615136cSMax Filippov 	unsigned long timeout = jiffies + msecs_to_jiffies(1000);
196f615136cSMax Filippov 	unsigned long ccount;
197f615136cSMax Filippov 	int i;
198f615136cSMax Filippov 
19949b424feSMax Filippov #ifdef CONFIG_HOTPLUG_CPU
20032a7726cSMax Filippov 	WRITE_ONCE(cpu_start_id, cpu);
20132a7726cSMax Filippov 	/* Pairs with the third memw in the cpu_restart */
20232a7726cSMax Filippov 	mb();
20332a7726cSMax Filippov 	system_flush_invalidate_dcache_range((unsigned long)&cpu_start_id,
20432a7726cSMax Filippov 					     sizeof(cpu_start_id));
20549b424feSMax Filippov #endif
206f615136cSMax Filippov 	smp_call_function_single(0, mx_cpu_start, (void *)cpu, 1);
207f615136cSMax Filippov 
208f615136cSMax Filippov 	for (i = 0; i < 2; ++i) {
209f615136cSMax Filippov 		do
210f615136cSMax Filippov 			ccount = get_ccount();
211f615136cSMax Filippov 		while (!ccount);
212f615136cSMax Filippov 
21332a7726cSMax Filippov 		WRITE_ONCE(cpu_start_ccount, ccount);
214f615136cSMax Filippov 
21532a7726cSMax Filippov 		do {
21632a7726cSMax Filippov 			/*
21732a7726cSMax Filippov 			 * Pairs with the first two memws in the
21832a7726cSMax Filippov 			 * .Lboot_secondary.
21932a7726cSMax Filippov 			 */
220f615136cSMax Filippov 			mb();
22132a7726cSMax Filippov 			ccount = READ_ONCE(cpu_start_ccount);
22232a7726cSMax Filippov 		} while (ccount && time_before(jiffies, timeout));
223f615136cSMax Filippov 
22432a7726cSMax Filippov 		if (ccount) {
225f615136cSMax Filippov 			smp_call_function_single(0, mx_cpu_stop,
226f615136cSMax Filippov 						 (void *)cpu, 1);
22732a7726cSMax Filippov 			WRITE_ONCE(cpu_start_ccount, 0);
228f615136cSMax Filippov 			return -EIO;
229f615136cSMax Filippov 		}
230f615136cSMax Filippov 	}
231f615136cSMax Filippov 	return 0;
232f615136cSMax Filippov }
233f615136cSMax Filippov 
__cpu_up(unsigned int cpu,struct task_struct * idle)234f615136cSMax Filippov int __cpu_up(unsigned int cpu, struct task_struct *idle)
235f615136cSMax Filippov {
236f615136cSMax Filippov 	int ret = 0;
237f615136cSMax Filippov 
238f615136cSMax Filippov 	if (cpu_asid_cache(cpu) == 0)
239f615136cSMax Filippov 		cpu_asid_cache(cpu) = ASID_USER_FIRST;
240f615136cSMax Filippov 
241f615136cSMax Filippov 	start_info.stack = (unsigned long)task_pt_regs(idle);
242f615136cSMax Filippov 	wmb();
243f615136cSMax Filippov 
244f615136cSMax Filippov 	pr_debug("%s: Calling wakeup_secondary(cpu:%d, idle:%p, sp: %08lx)\n",
245f615136cSMax Filippov 			__func__, cpu, idle, start_info.stack);
246f615136cSMax Filippov 
24732a7726cSMax Filippov 	init_completion(&cpu_running);
248f615136cSMax Filippov 	ret = boot_secondary(cpu, idle);
249f615136cSMax Filippov 	if (ret == 0) {
250f615136cSMax Filippov 		wait_for_completion_timeout(&cpu_running,
251f615136cSMax Filippov 				msecs_to_jiffies(1000));
252f615136cSMax Filippov 		if (!cpu_online(cpu))
253f615136cSMax Filippov 			ret = -EIO;
254f615136cSMax Filippov 	}
255f615136cSMax Filippov 
256f615136cSMax Filippov 	if (ret)
257f615136cSMax Filippov 		pr_err("CPU %u failed to boot\n", cpu);
258f615136cSMax Filippov 
259f615136cSMax Filippov 	return ret;
260f615136cSMax Filippov }
261f615136cSMax Filippov 
26249b424feSMax Filippov #ifdef CONFIG_HOTPLUG_CPU
26349b424feSMax Filippov 
26449b424feSMax Filippov /*
26549b424feSMax Filippov  * __cpu_disable runs on the processor to be shutdown.
26649b424feSMax Filippov  */
__cpu_disable(void)26749b424feSMax Filippov int __cpu_disable(void)
26849b424feSMax Filippov {
26949b424feSMax Filippov 	unsigned int cpu = smp_processor_id();
27049b424feSMax Filippov 
27149b424feSMax Filippov 	/*
27249b424feSMax Filippov 	 * Take this CPU offline.  Once we clear this, we can't return,
27349b424feSMax Filippov 	 * and we must not schedule until we're ready to give up the cpu.
27449b424feSMax Filippov 	 */
27549b424feSMax Filippov 	set_cpu_online(cpu, false);
27649b424feSMax Filippov 
27711e969bcSMax Filippov #if XTENSA_HAVE_COPROCESSORS
27811e969bcSMax Filippov 	/*
27911e969bcSMax Filippov 	 * Flush coprocessor contexts that are active on the current CPU.
28011e969bcSMax Filippov 	 */
28111e969bcSMax Filippov 	local_coprocessors_flush_release_all();
28211e969bcSMax Filippov #endif
28349b424feSMax Filippov 	/*
28449b424feSMax Filippov 	 * OK - migrate IRQs away from this CPU
28549b424feSMax Filippov 	 */
28649b424feSMax Filippov 	migrate_irqs();
28749b424feSMax Filippov 
28849b424feSMax Filippov 	/*
28949b424feSMax Filippov 	 * Flush user cache and TLB mappings, and then remove this CPU
29049b424feSMax Filippov 	 * from the vm mask set of all processes.
29149b424feSMax Filippov 	 */
29249b424feSMax Filippov 	local_flush_cache_all();
29349b424feSMax Filippov 	local_flush_tlb_all();
29449b424feSMax Filippov 	invalidate_page_directory();
29549b424feSMax Filippov 
29649b424feSMax Filippov 	clear_tasks_mm_cpumask(cpu);
29749b424feSMax Filippov 
29849b424feSMax Filippov 	return 0;
29949b424feSMax Filippov }
30049b424feSMax Filippov 
platform_cpu_kill(unsigned int cpu)30149b424feSMax Filippov static void platform_cpu_kill(unsigned int cpu)
30249b424feSMax Filippov {
30349b424feSMax Filippov 	smp_call_function_single(0, mx_cpu_stop, (void *)cpu, true);
30449b424feSMax Filippov }
30549b424feSMax Filippov 
30649b424feSMax Filippov /*
30749b424feSMax Filippov  * called on the thread which is asking for a CPU to be shutdown -
30849b424feSMax Filippov  * waits until shutdown has completed, or it is timed out.
30949b424feSMax Filippov  */
__cpu_die(unsigned int cpu)31049b424feSMax Filippov void __cpu_die(unsigned int cpu)
31149b424feSMax Filippov {
31249b424feSMax Filippov 	unsigned long timeout = jiffies + msecs_to_jiffies(1000);
31349b424feSMax Filippov 	while (time_before(jiffies, timeout)) {
31449b424feSMax Filippov 		system_invalidate_dcache_range((unsigned long)&cpu_start_id,
31549b424feSMax Filippov 					       sizeof(cpu_start_id));
31632a7726cSMax Filippov 		/* Pairs with the second memw in the cpu_restart */
31732a7726cSMax Filippov 		mb();
31832a7726cSMax Filippov 		if (READ_ONCE(cpu_start_id) == -cpu) {
31949b424feSMax Filippov 			platform_cpu_kill(cpu);
32049b424feSMax Filippov 			return;
32149b424feSMax Filippov 		}
32249b424feSMax Filippov 	}
32349b424feSMax Filippov 	pr_err("CPU%u: unable to kill\n", cpu);
32449b424feSMax Filippov }
32549b424feSMax Filippov 
arch_cpu_idle_dead(void)326071c44e4SJosh Poimboeuf void __noreturn arch_cpu_idle_dead(void)
32749b424feSMax Filippov {
32849b424feSMax Filippov 	cpu_die();
32949b424feSMax Filippov }
33049b424feSMax Filippov /*
33149b424feSMax Filippov  * Called from the idle thread for the CPU which has been shutdown.
33249b424feSMax Filippov  *
33349b424feSMax Filippov  * Note that we disable IRQs here, but do not re-enable them
33449b424feSMax Filippov  * before returning to the caller. This is also the behaviour
33549b424feSMax Filippov  * of the other hotplug-cpu capable cores, so presumably coming
33649b424feSMax Filippov  * out of idle fixes this.
33749b424feSMax Filippov  */
cpu_die(void)33849b424feSMax Filippov void __ref cpu_die(void)
33949b424feSMax Filippov {
34049b424feSMax Filippov 	idle_task_exit();
34149b424feSMax Filippov 	local_irq_disable();
34249b424feSMax Filippov 	__asm__ __volatile__(
34349b424feSMax Filippov 			"	movi	a2, cpu_restart\n"
34449b424feSMax Filippov 			"	jx	a2\n");
345d08e12e8SJosh Poimboeuf 
346d08e12e8SJosh Poimboeuf 	BUG();
34749b424feSMax Filippov }
34849b424feSMax Filippov 
34949b424feSMax Filippov #endif /* CONFIG_HOTPLUG_CPU */
35049b424feSMax Filippov 
351f615136cSMax Filippov enum ipi_msg_type {
352f615136cSMax Filippov 	IPI_RESCHEDULE = 0,
353f615136cSMax Filippov 	IPI_CALL_FUNC,
354f615136cSMax Filippov 	IPI_CPU_STOP,
355f615136cSMax Filippov 	IPI_MAX
356f615136cSMax Filippov };
357f615136cSMax Filippov 
358f615136cSMax Filippov static const struct {
359f615136cSMax Filippov 	const char *short_text;
360f615136cSMax Filippov 	const char *long_text;
361f615136cSMax Filippov } ipi_text[] = {
362f615136cSMax Filippov 	{ .short_text = "RES", .long_text = "Rescheduling interrupts" },
363f615136cSMax Filippov 	{ .short_text = "CAL", .long_text = "Function call interrupts" },
364f615136cSMax Filippov 	{ .short_text = "DIE", .long_text = "CPU shutdown interrupts" },
365f615136cSMax Filippov };
366f615136cSMax Filippov 
367f615136cSMax Filippov struct ipi_data {
368f615136cSMax Filippov 	unsigned long ipi_count[IPI_MAX];
369f615136cSMax Filippov };
370f615136cSMax Filippov 
371f615136cSMax Filippov static DEFINE_PER_CPU(struct ipi_data, ipi_data);
372f615136cSMax Filippov 
send_ipi_message(const struct cpumask * callmask,enum ipi_msg_type msg_id)373f615136cSMax Filippov static void send_ipi_message(const struct cpumask *callmask,
374f615136cSMax Filippov 		enum ipi_msg_type msg_id)
375f615136cSMax Filippov {
376f615136cSMax Filippov 	int index;
377f615136cSMax Filippov 	unsigned long mask = 0;
378f615136cSMax Filippov 
379f615136cSMax Filippov 	for_each_cpu(index, callmask)
380f615136cSMax Filippov 		mask |= 1 << index;
381f615136cSMax Filippov 
382f615136cSMax Filippov 	set_er(mask, MIPISET(msg_id));
383f615136cSMax Filippov }
384f615136cSMax Filippov 
arch_send_call_function_ipi_mask(const struct cpumask * mask)385f615136cSMax Filippov void arch_send_call_function_ipi_mask(const struct cpumask *mask)
386f615136cSMax Filippov {
387f615136cSMax Filippov 	send_ipi_message(mask, IPI_CALL_FUNC);
388f615136cSMax Filippov }
389f615136cSMax Filippov 
arch_send_call_function_single_ipi(int cpu)390f615136cSMax Filippov void arch_send_call_function_single_ipi(int cpu)
391f615136cSMax Filippov {
392f615136cSMax Filippov 	send_ipi_message(cpumask_of(cpu), IPI_CALL_FUNC);
393f615136cSMax Filippov }
394f615136cSMax Filippov 
arch_smp_send_reschedule(int cpu)3954c8c3c7fSValentin Schneider void arch_smp_send_reschedule(int cpu)
396f615136cSMax Filippov {
397f615136cSMax Filippov 	send_ipi_message(cpumask_of(cpu), IPI_RESCHEDULE);
398f615136cSMax Filippov }
399f615136cSMax Filippov 
smp_send_stop(void)400f615136cSMax Filippov void smp_send_stop(void)
401f615136cSMax Filippov {
402f615136cSMax Filippov 	struct cpumask targets;
403f615136cSMax Filippov 
404f615136cSMax Filippov 	cpumask_copy(&targets, cpu_online_mask);
405f615136cSMax Filippov 	cpumask_clear_cpu(smp_processor_id(), &targets);
406f615136cSMax Filippov 	send_ipi_message(&targets, IPI_CPU_STOP);
407f615136cSMax Filippov }
408f615136cSMax Filippov 
ipi_cpu_stop(unsigned int cpu)409f615136cSMax Filippov static void ipi_cpu_stop(unsigned int cpu)
410f615136cSMax Filippov {
411f615136cSMax Filippov 	set_cpu_online(cpu, false);
412f615136cSMax Filippov 	machine_halt();
413f615136cSMax Filippov }
414f615136cSMax Filippov 
ipi_interrupt(int irq,void * dev_id)415f615136cSMax Filippov irqreturn_t ipi_interrupt(int irq, void *dev_id)
416f615136cSMax Filippov {
417f615136cSMax Filippov 	unsigned int cpu = smp_processor_id();
418f615136cSMax Filippov 	struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
419815af8ffSMax Filippov 
420815af8ffSMax Filippov 	for (;;) {
421f615136cSMax Filippov 		unsigned int msg;
422f615136cSMax Filippov 
423f615136cSMax Filippov 		msg = get_er(MIPICAUSE(cpu));
424815af8ffSMax Filippov 		set_er(msg, MIPICAUSE(cpu));
425815af8ffSMax Filippov 
426815af8ffSMax Filippov 		if (!msg)
427815af8ffSMax Filippov 			break;
428815af8ffSMax Filippov 
429815af8ffSMax Filippov 		if (msg & (1 << IPI_CALL_FUNC)) {
430815af8ffSMax Filippov 			++ipi->ipi_count[IPI_CALL_FUNC];
431815af8ffSMax Filippov 			generic_smp_call_function_interrupt();
432f615136cSMax Filippov 		}
433f615136cSMax Filippov 
434815af8ffSMax Filippov 		if (msg & (1 << IPI_RESCHEDULE)) {
435815af8ffSMax Filippov 			++ipi->ipi_count[IPI_RESCHEDULE];
436f615136cSMax Filippov 			scheduler_ipi();
437815af8ffSMax Filippov 		}
438815af8ffSMax Filippov 
439815af8ffSMax Filippov 		if (msg & (1 << IPI_CPU_STOP)) {
440815af8ffSMax Filippov 			++ipi->ipi_count[IPI_CPU_STOP];
441f615136cSMax Filippov 			ipi_cpu_stop(cpu);
442815af8ffSMax Filippov 		}
443815af8ffSMax Filippov 	}
444f615136cSMax Filippov 
445f615136cSMax Filippov 	return IRQ_HANDLED;
446f615136cSMax Filippov }
447f615136cSMax Filippov 
show_ipi_list(struct seq_file * p,int prec)448f615136cSMax Filippov void show_ipi_list(struct seq_file *p, int prec)
449f615136cSMax Filippov {
450f615136cSMax Filippov 	unsigned int cpu;
451f615136cSMax Filippov 	unsigned i;
452f615136cSMax Filippov 
453f615136cSMax Filippov 	for (i = 0; i < IPI_MAX; ++i) {
454f615136cSMax Filippov 		seq_printf(p, "%*s:", prec, ipi_text[i].short_text);
455f615136cSMax Filippov 		for_each_online_cpu(cpu)
456f615136cSMax Filippov 			seq_printf(p, " %10lu",
457f615136cSMax Filippov 					per_cpu(ipi_data, cpu).ipi_count[i]);
458f615136cSMax Filippov 		seq_printf(p, "   %s\n", ipi_text[i].long_text);
459f615136cSMax Filippov 	}
460f615136cSMax Filippov }
461f615136cSMax Filippov 
setup_profiling_timer(unsigned int multiplier)462f615136cSMax Filippov int setup_profiling_timer(unsigned int multiplier)
463f615136cSMax Filippov {
464f615136cSMax Filippov 	pr_debug("setup_profiling_timer %d\n", multiplier);
465f615136cSMax Filippov 	return 0;
466f615136cSMax Filippov }
467f615136cSMax Filippov 
468f615136cSMax Filippov /* TLB flush functions */
469f615136cSMax Filippov 
470f615136cSMax Filippov struct flush_data {
471f615136cSMax Filippov 	struct vm_area_struct *vma;
472f615136cSMax Filippov 	unsigned long addr1;
473f615136cSMax Filippov 	unsigned long addr2;
474f615136cSMax Filippov };
475f615136cSMax Filippov 
ipi_flush_tlb_all(void * arg)476f615136cSMax Filippov static void ipi_flush_tlb_all(void *arg)
477f615136cSMax Filippov {
478f615136cSMax Filippov 	local_flush_tlb_all();
479f615136cSMax Filippov }
480f615136cSMax Filippov 
flush_tlb_all(void)481f615136cSMax Filippov void flush_tlb_all(void)
482f615136cSMax Filippov {
483f615136cSMax Filippov 	on_each_cpu(ipi_flush_tlb_all, NULL, 1);
484f615136cSMax Filippov }
485f615136cSMax Filippov 
ipi_flush_tlb_mm(void * arg)486f615136cSMax Filippov static void ipi_flush_tlb_mm(void *arg)
487f615136cSMax Filippov {
488f615136cSMax Filippov 	local_flush_tlb_mm(arg);
489f615136cSMax Filippov }
490f615136cSMax Filippov 
flush_tlb_mm(struct mm_struct * mm)491f615136cSMax Filippov void flush_tlb_mm(struct mm_struct *mm)
492f615136cSMax Filippov {
493f615136cSMax Filippov 	on_each_cpu(ipi_flush_tlb_mm, mm, 1);
494f615136cSMax Filippov }
495f615136cSMax Filippov 
ipi_flush_tlb_page(void * arg)496f615136cSMax Filippov static void ipi_flush_tlb_page(void *arg)
497f615136cSMax Filippov {
498f615136cSMax Filippov 	struct flush_data *fd = arg;
499f615136cSMax Filippov 	local_flush_tlb_page(fd->vma, fd->addr1);
500f615136cSMax Filippov }
501f615136cSMax Filippov 
flush_tlb_page(struct vm_area_struct * vma,unsigned long addr)502f615136cSMax Filippov void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
503f615136cSMax Filippov {
504f615136cSMax Filippov 	struct flush_data fd = {
505f615136cSMax Filippov 		.vma = vma,
506f615136cSMax Filippov 		.addr1 = addr,
507f615136cSMax Filippov 	};
508f615136cSMax Filippov 	on_each_cpu(ipi_flush_tlb_page, &fd, 1);
509f615136cSMax Filippov }
510f615136cSMax Filippov 
ipi_flush_tlb_range(void * arg)511f615136cSMax Filippov static void ipi_flush_tlb_range(void *arg)
512f615136cSMax Filippov {
513f615136cSMax Filippov 	struct flush_data *fd = arg;
514f615136cSMax Filippov 	local_flush_tlb_range(fd->vma, fd->addr1, fd->addr2);
515f615136cSMax Filippov }
516f615136cSMax Filippov 
flush_tlb_range(struct vm_area_struct * vma,unsigned long start,unsigned long end)517f615136cSMax Filippov void flush_tlb_range(struct vm_area_struct *vma,
518f615136cSMax Filippov 		     unsigned long start, unsigned long end)
519f615136cSMax Filippov {
520f615136cSMax Filippov 	struct flush_data fd = {
521f615136cSMax Filippov 		.vma = vma,
522f615136cSMax Filippov 		.addr1 = start,
523f615136cSMax Filippov 		.addr2 = end,
524f615136cSMax Filippov 	};
525f615136cSMax Filippov 	on_each_cpu(ipi_flush_tlb_range, &fd, 1);
526f615136cSMax Filippov }
527f615136cSMax Filippov 
ipi_flush_tlb_kernel_range(void * arg)52804c6b3e2SMax Filippov static void ipi_flush_tlb_kernel_range(void *arg)
52904c6b3e2SMax Filippov {
53004c6b3e2SMax Filippov 	struct flush_data *fd = arg;
53104c6b3e2SMax Filippov 	local_flush_tlb_kernel_range(fd->addr1, fd->addr2);
53204c6b3e2SMax Filippov }
53304c6b3e2SMax Filippov 
flush_tlb_kernel_range(unsigned long start,unsigned long end)53404c6b3e2SMax Filippov void flush_tlb_kernel_range(unsigned long start, unsigned long end)
53504c6b3e2SMax Filippov {
53604c6b3e2SMax Filippov 	struct flush_data fd = {
53704c6b3e2SMax Filippov 		.addr1 = start,
53804c6b3e2SMax Filippov 		.addr2 = end,
53904c6b3e2SMax Filippov 	};
54004c6b3e2SMax Filippov 	on_each_cpu(ipi_flush_tlb_kernel_range, &fd, 1);
54104c6b3e2SMax Filippov }
54204c6b3e2SMax Filippov 
543f615136cSMax Filippov /* Cache flush functions */
544f615136cSMax Filippov 
ipi_flush_cache_all(void * arg)545f615136cSMax Filippov static void ipi_flush_cache_all(void *arg)
546f615136cSMax Filippov {
547f615136cSMax Filippov 	local_flush_cache_all();
548f615136cSMax Filippov }
549f615136cSMax Filippov 
flush_cache_all(void)550f615136cSMax Filippov void flush_cache_all(void)
551f615136cSMax Filippov {
552f615136cSMax Filippov 	on_each_cpu(ipi_flush_cache_all, NULL, 1);
553f615136cSMax Filippov }
554f615136cSMax Filippov 
ipi_flush_cache_page(void * arg)555f615136cSMax Filippov static void ipi_flush_cache_page(void *arg)
556f615136cSMax Filippov {
557f615136cSMax Filippov 	struct flush_data *fd = arg;
558f615136cSMax Filippov 	local_flush_cache_page(fd->vma, fd->addr1, fd->addr2);
559f615136cSMax Filippov }
560f615136cSMax Filippov 
flush_cache_page(struct vm_area_struct * vma,unsigned long address,unsigned long pfn)561f615136cSMax Filippov void flush_cache_page(struct vm_area_struct *vma,
562f615136cSMax Filippov 		     unsigned long address, unsigned long pfn)
563f615136cSMax Filippov {
564f615136cSMax Filippov 	struct flush_data fd = {
565f615136cSMax Filippov 		.vma = vma,
566f615136cSMax Filippov 		.addr1 = address,
567f615136cSMax Filippov 		.addr2 = pfn,
568f615136cSMax Filippov 	};
569f615136cSMax Filippov 	on_each_cpu(ipi_flush_cache_page, &fd, 1);
570f615136cSMax Filippov }
571f615136cSMax Filippov 
ipi_flush_cache_range(void * arg)572f615136cSMax Filippov static void ipi_flush_cache_range(void *arg)
573f615136cSMax Filippov {
574f615136cSMax Filippov 	struct flush_data *fd = arg;
575f615136cSMax Filippov 	local_flush_cache_range(fd->vma, fd->addr1, fd->addr2);
576f615136cSMax Filippov }
577f615136cSMax Filippov 
flush_cache_range(struct vm_area_struct * vma,unsigned long start,unsigned long end)578f615136cSMax Filippov void flush_cache_range(struct vm_area_struct *vma,
579f615136cSMax Filippov 		     unsigned long start, unsigned long end)
580f615136cSMax Filippov {
581f615136cSMax Filippov 	struct flush_data fd = {
582f615136cSMax Filippov 		.vma = vma,
583f615136cSMax Filippov 		.addr1 = start,
584f615136cSMax Filippov 		.addr2 = end,
585f615136cSMax Filippov 	};
586f615136cSMax Filippov 	on_each_cpu(ipi_flush_cache_range, &fd, 1);
587f615136cSMax Filippov }
588f615136cSMax Filippov 
ipi_flush_icache_range(void * arg)589f615136cSMax Filippov static void ipi_flush_icache_range(void *arg)
590f615136cSMax Filippov {
591f615136cSMax Filippov 	struct flush_data *fd = arg;
592f615136cSMax Filippov 	local_flush_icache_range(fd->addr1, fd->addr2);
593f615136cSMax Filippov }
594f615136cSMax Filippov 
flush_icache_range(unsigned long start,unsigned long end)595f615136cSMax Filippov void flush_icache_range(unsigned long start, unsigned long end)
596f615136cSMax Filippov {
597f615136cSMax Filippov 	struct flush_data fd = {
598f615136cSMax Filippov 		.addr1 = start,
599f615136cSMax Filippov 		.addr2 = end,
600f615136cSMax Filippov 	};
601f615136cSMax Filippov 	on_each_cpu(ipi_flush_icache_range, &fd, 1);
602f615136cSMax Filippov }
603e3560305SPranith Kumar EXPORT_SYMBOL(flush_icache_range);
60449b424feSMax Filippov 
60549b424feSMax Filippov /* ------------------------------------------------------------------------- */
60649b424feSMax Filippov 
ipi_invalidate_dcache_range(void * arg)60749b424feSMax Filippov static void ipi_invalidate_dcache_range(void *arg)
60849b424feSMax Filippov {
60949b424feSMax Filippov 	struct flush_data *fd = arg;
61049b424feSMax Filippov 	__invalidate_dcache_range(fd->addr1, fd->addr2);
61149b424feSMax Filippov }
61249b424feSMax Filippov 
system_invalidate_dcache_range(unsigned long start,unsigned long size)61349b424feSMax Filippov static void system_invalidate_dcache_range(unsigned long start,
61449b424feSMax Filippov 		unsigned long size)
61549b424feSMax Filippov {
61649b424feSMax Filippov 	struct flush_data fd = {
61749b424feSMax Filippov 		.addr1 = start,
61849b424feSMax Filippov 		.addr2 = size,
61949b424feSMax Filippov 	};
62049b424feSMax Filippov 	on_each_cpu(ipi_invalidate_dcache_range, &fd, 1);
62149b424feSMax Filippov }
62249b424feSMax Filippov 
ipi_flush_invalidate_dcache_range(void * arg)62349b424feSMax Filippov static void ipi_flush_invalidate_dcache_range(void *arg)
62449b424feSMax Filippov {
62549b424feSMax Filippov 	struct flush_data *fd = arg;
62649b424feSMax Filippov 	__flush_invalidate_dcache_range(fd->addr1, fd->addr2);
62749b424feSMax Filippov }
62849b424feSMax Filippov 
system_flush_invalidate_dcache_range(unsigned long start,unsigned long size)62949b424feSMax Filippov static void system_flush_invalidate_dcache_range(unsigned long start,
63049b424feSMax Filippov 		unsigned long size)
63149b424feSMax Filippov {
63249b424feSMax Filippov 	struct flush_data fd = {
63349b424feSMax Filippov 		.addr1 = start,
63449b424feSMax Filippov 		.addr2 = size,
63549b424feSMax Filippov 	};
63649b424feSMax Filippov 	on_each_cpu(ipi_flush_invalidate_dcache_range, &fd, 1);
63749b424feSMax Filippov }
638