xref: /linux/arch/loongarch/kernel/smp.c (revision d4b6f1562a3c3284adcef81d6e4f183d7d34b8a9)
146859ac8SHuacai Chen // SPDX-License-Identifier: GPL-2.0-or-later
246859ac8SHuacai Chen /*
346859ac8SHuacai Chen  * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
446859ac8SHuacai Chen  *
546859ac8SHuacai Chen  * Derived from MIPS:
646859ac8SHuacai Chen  * Copyright (C) 2000, 2001 Kanoj Sarcar
746859ac8SHuacai Chen  * Copyright (C) 2000, 2001 Ralf Baechle
846859ac8SHuacai Chen  * Copyright (C) 2000, 2001 Silicon Graphics, Inc.
946859ac8SHuacai Chen  * Copyright (C) 2000, 2001, 2003 Broadcom Corporation
1046859ac8SHuacai Chen  */
1146859ac8SHuacai Chen #include <linux/cpu.h>
1246859ac8SHuacai Chen #include <linux/cpumask.h>
1346859ac8SHuacai Chen #include <linux/init.h>
1446859ac8SHuacai Chen #include <linux/interrupt.h>
1546859ac8SHuacai Chen #include <linux/seq_file.h>
1646859ac8SHuacai Chen #include <linux/smp.h>
1746859ac8SHuacai Chen #include <linux/threads.h>
1846859ac8SHuacai Chen #include <linux/export.h>
1946859ac8SHuacai Chen #include <linux/time.h>
2046859ac8SHuacai Chen #include <linux/tracepoint.h>
2146859ac8SHuacai Chen #include <linux/sched/hotplug.h>
2246859ac8SHuacai Chen #include <linux/sched/task_stack.h>
2346859ac8SHuacai Chen 
2446859ac8SHuacai Chen #include <asm/cpu.h>
2546859ac8SHuacai Chen #include <asm/idle.h>
2646859ac8SHuacai Chen #include <asm/loongson.h>
2746859ac8SHuacai Chen #include <asm/mmu_context.h>
28*d4b6f156SHuacai Chen #include <asm/numa.h>
2946859ac8SHuacai Chen #include <asm/processor.h>
3046859ac8SHuacai Chen #include <asm/setup.h>
3146859ac8SHuacai Chen #include <asm/time.h>
3246859ac8SHuacai Chen 
3346859ac8SHuacai Chen int __cpu_number_map[NR_CPUS];   /* Map physical to logical */
3446859ac8SHuacai Chen EXPORT_SYMBOL(__cpu_number_map);
3546859ac8SHuacai Chen 
3646859ac8SHuacai Chen int __cpu_logical_map[NR_CPUS];		/* Map logical to physical */
3746859ac8SHuacai Chen EXPORT_SYMBOL(__cpu_logical_map);
3846859ac8SHuacai Chen 
3946859ac8SHuacai Chen /* Number of threads (siblings) per CPU core */
4046859ac8SHuacai Chen int smp_num_siblings = 1;
4146859ac8SHuacai Chen EXPORT_SYMBOL(smp_num_siblings);
4246859ac8SHuacai Chen 
4346859ac8SHuacai Chen /* Representing the threads (siblings) of each logical CPU */
4446859ac8SHuacai Chen cpumask_t cpu_sibling_map[NR_CPUS] __read_mostly;
4546859ac8SHuacai Chen EXPORT_SYMBOL(cpu_sibling_map);
4646859ac8SHuacai Chen 
4746859ac8SHuacai Chen /* Representing the core map of multi-core chips of each logical CPU */
4846859ac8SHuacai Chen cpumask_t cpu_core_map[NR_CPUS] __read_mostly;
4946859ac8SHuacai Chen EXPORT_SYMBOL(cpu_core_map);
5046859ac8SHuacai Chen 
5146859ac8SHuacai Chen static DECLARE_COMPLETION(cpu_starting);
5246859ac8SHuacai Chen static DECLARE_COMPLETION(cpu_running);
5346859ac8SHuacai Chen 
5446859ac8SHuacai Chen /*
5546859ac8SHuacai Chen  * A logcal cpu mask containing only one VPE per core to
5646859ac8SHuacai Chen  * reduce the number of IPIs on large MT systems.
5746859ac8SHuacai Chen  */
5846859ac8SHuacai Chen cpumask_t cpu_foreign_map[NR_CPUS] __read_mostly;
5946859ac8SHuacai Chen EXPORT_SYMBOL(cpu_foreign_map);
6046859ac8SHuacai Chen 
6146859ac8SHuacai Chen /* representing cpus for which sibling maps can be computed */
6246859ac8SHuacai Chen static cpumask_t cpu_sibling_setup_map;
6346859ac8SHuacai Chen 
6446859ac8SHuacai Chen /* representing cpus for which core maps can be computed */
6546859ac8SHuacai Chen static cpumask_t cpu_core_setup_map;
6646859ac8SHuacai Chen 
6746859ac8SHuacai Chen struct secondary_data cpuboot_data;
6846859ac8SHuacai Chen static DEFINE_PER_CPU(int, cpu_state);
6946859ac8SHuacai Chen DEFINE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);
7046859ac8SHuacai Chen EXPORT_PER_CPU_SYMBOL(irq_stat);
7146859ac8SHuacai Chen 
7246859ac8SHuacai Chen enum ipi_msg_type {
7346859ac8SHuacai Chen 	IPI_RESCHEDULE,
7446859ac8SHuacai Chen 	IPI_CALL_FUNCTION,
7546859ac8SHuacai Chen };
7646859ac8SHuacai Chen 
7746859ac8SHuacai Chen static const char *ipi_types[NR_IPI] __tracepoint_string = {
7846859ac8SHuacai Chen 	[IPI_RESCHEDULE] = "Rescheduling interrupts",
7946859ac8SHuacai Chen 	[IPI_CALL_FUNCTION] = "Function call interrupts",
8046859ac8SHuacai Chen };
8146859ac8SHuacai Chen 
8246859ac8SHuacai Chen void show_ipi_list(struct seq_file *p, int prec)
8346859ac8SHuacai Chen {
8446859ac8SHuacai Chen 	unsigned int cpu, i;
8546859ac8SHuacai Chen 
8646859ac8SHuacai Chen 	for (i = 0; i < NR_IPI; i++) {
8746859ac8SHuacai Chen 		seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i, prec >= 4 ? " " : "");
8846859ac8SHuacai Chen 		for_each_online_cpu(cpu)
8946859ac8SHuacai Chen 			seq_printf(p, "%10u ", per_cpu(irq_stat, cpu).ipi_irqs[i]);
9046859ac8SHuacai Chen 		seq_printf(p, " LoongArch  %d  %s\n", i + 1, ipi_types[i]);
9146859ac8SHuacai Chen 	}
9246859ac8SHuacai Chen }
9346859ac8SHuacai Chen 
9446859ac8SHuacai Chen /* Send mailbox buffer via Mail_Send */
9546859ac8SHuacai Chen static void csr_mail_send(uint64_t data, int cpu, int mailbox)
9646859ac8SHuacai Chen {
9746859ac8SHuacai Chen 	uint64_t val;
9846859ac8SHuacai Chen 
9946859ac8SHuacai Chen 	/* Send high 32 bits */
10046859ac8SHuacai Chen 	val = IOCSR_MBUF_SEND_BLOCKING;
10146859ac8SHuacai Chen 	val |= (IOCSR_MBUF_SEND_BOX_HI(mailbox) << IOCSR_MBUF_SEND_BOX_SHIFT);
10246859ac8SHuacai Chen 	val |= (cpu << IOCSR_MBUF_SEND_CPU_SHIFT);
10346859ac8SHuacai Chen 	val |= (data & IOCSR_MBUF_SEND_H32_MASK);
10446859ac8SHuacai Chen 	iocsr_write64(val, LOONGARCH_IOCSR_MBUF_SEND);
10546859ac8SHuacai Chen 
10646859ac8SHuacai Chen 	/* Send low 32 bits */
10746859ac8SHuacai Chen 	val = IOCSR_MBUF_SEND_BLOCKING;
10846859ac8SHuacai Chen 	val |= (IOCSR_MBUF_SEND_BOX_LO(mailbox) << IOCSR_MBUF_SEND_BOX_SHIFT);
10946859ac8SHuacai Chen 	val |= (cpu << IOCSR_MBUF_SEND_CPU_SHIFT);
11046859ac8SHuacai Chen 	val |= (data << IOCSR_MBUF_SEND_BUF_SHIFT);
11146859ac8SHuacai Chen 	iocsr_write64(val, LOONGARCH_IOCSR_MBUF_SEND);
11246859ac8SHuacai Chen };
11346859ac8SHuacai Chen 
11446859ac8SHuacai Chen static u32 ipi_read_clear(int cpu)
11546859ac8SHuacai Chen {
11646859ac8SHuacai Chen 	u32 action;
11746859ac8SHuacai Chen 
11846859ac8SHuacai Chen 	/* Load the ipi register to figure out what we're supposed to do */
11946859ac8SHuacai Chen 	action = iocsr_read32(LOONGARCH_IOCSR_IPI_STATUS);
12046859ac8SHuacai Chen 	/* Clear the ipi register to clear the interrupt */
12146859ac8SHuacai Chen 	iocsr_write32(action, LOONGARCH_IOCSR_IPI_CLEAR);
12246859ac8SHuacai Chen 	smp_mb();
12346859ac8SHuacai Chen 
12446859ac8SHuacai Chen 	return action;
12546859ac8SHuacai Chen }
12646859ac8SHuacai Chen 
12746859ac8SHuacai Chen static void ipi_write_action(int cpu, u32 action)
12846859ac8SHuacai Chen {
12946859ac8SHuacai Chen 	unsigned int irq = 0;
13046859ac8SHuacai Chen 
13146859ac8SHuacai Chen 	while ((irq = ffs(action))) {
13246859ac8SHuacai Chen 		uint32_t val = IOCSR_IPI_SEND_BLOCKING;
13346859ac8SHuacai Chen 
13446859ac8SHuacai Chen 		val |= (irq - 1);
13546859ac8SHuacai Chen 		val |= (cpu << IOCSR_IPI_SEND_CPU_SHIFT);
13646859ac8SHuacai Chen 		iocsr_write32(val, LOONGARCH_IOCSR_IPI_SEND);
13746859ac8SHuacai Chen 		action &= ~BIT(irq - 1);
13846859ac8SHuacai Chen 	}
13946859ac8SHuacai Chen }
14046859ac8SHuacai Chen 
14146859ac8SHuacai Chen void loongson3_send_ipi_single(int cpu, unsigned int action)
14246859ac8SHuacai Chen {
14346859ac8SHuacai Chen 	ipi_write_action(cpu_logical_map(cpu), (u32)action);
14446859ac8SHuacai Chen }
14546859ac8SHuacai Chen 
14646859ac8SHuacai Chen void loongson3_send_ipi_mask(const struct cpumask *mask, unsigned int action)
14746859ac8SHuacai Chen {
14846859ac8SHuacai Chen 	unsigned int i;
14946859ac8SHuacai Chen 
15046859ac8SHuacai Chen 	for_each_cpu(i, mask)
15146859ac8SHuacai Chen 		ipi_write_action(cpu_logical_map(i), (u32)action);
15246859ac8SHuacai Chen }
15346859ac8SHuacai Chen 
15446859ac8SHuacai Chen irqreturn_t loongson3_ipi_interrupt(int irq, void *dev)
15546859ac8SHuacai Chen {
15646859ac8SHuacai Chen 	unsigned int action;
15746859ac8SHuacai Chen 	unsigned int cpu = smp_processor_id();
15846859ac8SHuacai Chen 
15946859ac8SHuacai Chen 	action = ipi_read_clear(cpu_logical_map(cpu));
16046859ac8SHuacai Chen 
16146859ac8SHuacai Chen 	if (action & SMP_RESCHEDULE) {
16246859ac8SHuacai Chen 		scheduler_ipi();
16346859ac8SHuacai Chen 		per_cpu(irq_stat, cpu).ipi_irqs[IPI_RESCHEDULE]++;
16446859ac8SHuacai Chen 	}
16546859ac8SHuacai Chen 
16646859ac8SHuacai Chen 	if (action & SMP_CALL_FUNCTION) {
16746859ac8SHuacai Chen 		generic_smp_call_function_interrupt();
16846859ac8SHuacai Chen 		per_cpu(irq_stat, cpu).ipi_irqs[IPI_CALL_FUNCTION]++;
16946859ac8SHuacai Chen 	}
17046859ac8SHuacai Chen 
17146859ac8SHuacai Chen 	return IRQ_HANDLED;
17246859ac8SHuacai Chen }
17346859ac8SHuacai Chen 
17446859ac8SHuacai Chen void __init loongson3_smp_setup(void)
17546859ac8SHuacai Chen {
17646859ac8SHuacai Chen 	cpu_data[0].core = cpu_logical_map(0) % loongson_sysconf.cores_per_package;
17746859ac8SHuacai Chen 	cpu_data[0].package = cpu_logical_map(0) / loongson_sysconf.cores_per_package;
17846859ac8SHuacai Chen 
17946859ac8SHuacai Chen 	iocsr_write32(0xffffffff, LOONGARCH_IOCSR_IPI_EN);
18046859ac8SHuacai Chen 	pr_info("Detected %i available CPU(s)\n", loongson_sysconf.nr_cpus);
18146859ac8SHuacai Chen }
18246859ac8SHuacai Chen 
18346859ac8SHuacai Chen void __init loongson3_prepare_cpus(unsigned int max_cpus)
18446859ac8SHuacai Chen {
18546859ac8SHuacai Chen 	int i = 0;
18646859ac8SHuacai Chen 
18746859ac8SHuacai Chen 	for (i = 0; i < loongson_sysconf.nr_cpus; i++) {
18846859ac8SHuacai Chen 		set_cpu_present(i, true);
18946859ac8SHuacai Chen 		csr_mail_send(0, __cpu_logical_map[i], 0);
19046859ac8SHuacai Chen 	}
19146859ac8SHuacai Chen 
19246859ac8SHuacai Chen 	per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE;
19346859ac8SHuacai Chen }
19446859ac8SHuacai Chen 
19546859ac8SHuacai Chen /*
19646859ac8SHuacai Chen  * Setup the PC, SP, and TP of a secondary processor and start it running!
19746859ac8SHuacai Chen  */
19846859ac8SHuacai Chen void loongson3_boot_secondary(int cpu, struct task_struct *idle)
19946859ac8SHuacai Chen {
20046859ac8SHuacai Chen 	unsigned long entry;
20146859ac8SHuacai Chen 
20246859ac8SHuacai Chen 	pr_info("Booting CPU#%d...\n", cpu);
20346859ac8SHuacai Chen 
20446859ac8SHuacai Chen 	entry = __pa_symbol((unsigned long)&smpboot_entry);
20546859ac8SHuacai Chen 	cpuboot_data.stack = (unsigned long)__KSTK_TOS(idle);
20646859ac8SHuacai Chen 	cpuboot_data.thread_info = (unsigned long)task_thread_info(idle);
20746859ac8SHuacai Chen 
20846859ac8SHuacai Chen 	csr_mail_send(entry, cpu_logical_map(cpu), 0);
20946859ac8SHuacai Chen 
21046859ac8SHuacai Chen 	loongson3_send_ipi_single(cpu, SMP_BOOT_CPU);
21146859ac8SHuacai Chen }
21246859ac8SHuacai Chen 
21346859ac8SHuacai Chen /*
21446859ac8SHuacai Chen  * SMP init and finish on secondary CPUs
21546859ac8SHuacai Chen  */
21646859ac8SHuacai Chen void loongson3_init_secondary(void)
21746859ac8SHuacai Chen {
21846859ac8SHuacai Chen 	unsigned int cpu = smp_processor_id();
21946859ac8SHuacai Chen 	unsigned int imask = ECFGF_IP0 | ECFGF_IP1 | ECFGF_IP2 |
22046859ac8SHuacai Chen 			     ECFGF_IPI | ECFGF_PMC | ECFGF_TIMER;
22146859ac8SHuacai Chen 
22246859ac8SHuacai Chen 	change_csr_ecfg(ECFG0_IM, imask);
22346859ac8SHuacai Chen 
22446859ac8SHuacai Chen 	iocsr_write32(0xffffffff, LOONGARCH_IOCSR_IPI_EN);
22546859ac8SHuacai Chen 
226*d4b6f156SHuacai Chen #ifdef CONFIG_NUMA
227*d4b6f156SHuacai Chen 	numa_add_cpu(cpu);
228*d4b6f156SHuacai Chen #endif
22946859ac8SHuacai Chen 	per_cpu(cpu_state, cpu) = CPU_ONLINE;
23046859ac8SHuacai Chen 	cpu_data[cpu].core =
23146859ac8SHuacai Chen 		     cpu_logical_map(cpu) % loongson_sysconf.cores_per_package;
23246859ac8SHuacai Chen 	cpu_data[cpu].package =
23346859ac8SHuacai Chen 		     cpu_logical_map(cpu) / loongson_sysconf.cores_per_package;
23446859ac8SHuacai Chen }
23546859ac8SHuacai Chen 
23646859ac8SHuacai Chen void loongson3_smp_finish(void)
23746859ac8SHuacai Chen {
23846859ac8SHuacai Chen 	local_irq_enable();
23946859ac8SHuacai Chen 	iocsr_write64(0, LOONGARCH_IOCSR_MBUF0);
24046859ac8SHuacai Chen 	pr_info("CPU#%d finished\n", smp_processor_id());
24146859ac8SHuacai Chen }
24246859ac8SHuacai Chen 
24346859ac8SHuacai Chen #ifdef CONFIG_HOTPLUG_CPU
24446859ac8SHuacai Chen 
24546859ac8SHuacai Chen static bool io_master(int cpu)
24646859ac8SHuacai Chen {
24746859ac8SHuacai Chen 	if (cpu == 0)
24846859ac8SHuacai Chen 		return true;
24946859ac8SHuacai Chen 
25046859ac8SHuacai Chen 	return false;
25146859ac8SHuacai Chen }
25246859ac8SHuacai Chen 
25346859ac8SHuacai Chen int loongson3_cpu_disable(void)
25446859ac8SHuacai Chen {
25546859ac8SHuacai Chen 	unsigned long flags;
25646859ac8SHuacai Chen 	unsigned int cpu = smp_processor_id();
25746859ac8SHuacai Chen 
25846859ac8SHuacai Chen 	if (io_master(cpu))
25946859ac8SHuacai Chen 		return -EBUSY;
26046859ac8SHuacai Chen 
261*d4b6f156SHuacai Chen #ifdef CONFIG_NUMA
262*d4b6f156SHuacai Chen 	numa_remove_cpu(cpu);
263*d4b6f156SHuacai Chen #endif
26446859ac8SHuacai Chen 	set_cpu_online(cpu, false);
26546859ac8SHuacai Chen 	calculate_cpu_foreign_map();
26646859ac8SHuacai Chen 	local_irq_save(flags);
26746859ac8SHuacai Chen 	irq_migrate_all_off_this_cpu();
26846859ac8SHuacai Chen 	clear_csr_ecfg(ECFG0_IM);
26946859ac8SHuacai Chen 	local_irq_restore(flags);
27046859ac8SHuacai Chen 	local_flush_tlb_all();
27146859ac8SHuacai Chen 
27246859ac8SHuacai Chen 	return 0;
27346859ac8SHuacai Chen }
27446859ac8SHuacai Chen 
27546859ac8SHuacai Chen void loongson3_cpu_die(unsigned int cpu)
27646859ac8SHuacai Chen {
27746859ac8SHuacai Chen 	while (per_cpu(cpu_state, cpu) != CPU_DEAD)
27846859ac8SHuacai Chen 		cpu_relax();
27946859ac8SHuacai Chen 
28046859ac8SHuacai Chen 	mb();
28146859ac8SHuacai Chen }
28246859ac8SHuacai Chen 
28346859ac8SHuacai Chen /*
28446859ac8SHuacai Chen  * The target CPU should go to XKPRANGE (uncached area) and flush
28546859ac8SHuacai Chen  * ICache/DCache/VCache before the control CPU can safely disable its clock.
28646859ac8SHuacai Chen  */
28746859ac8SHuacai Chen static void loongson3_play_dead(int *state_addr)
28846859ac8SHuacai Chen {
28946859ac8SHuacai Chen 	register int val;
29046859ac8SHuacai Chen 	register void *addr;
29146859ac8SHuacai Chen 	register void (*init_fn)(void);
29246859ac8SHuacai Chen 
29346859ac8SHuacai Chen 	__asm__ __volatile__(
29446859ac8SHuacai Chen 		"   li.d %[addr], 0x8000000000000000\n"
29546859ac8SHuacai Chen 		"1: cacop 0x8, %[addr], 0           \n" /* flush ICache */
29646859ac8SHuacai Chen 		"   cacop 0x8, %[addr], 1           \n"
29746859ac8SHuacai Chen 		"   cacop 0x8, %[addr], 2           \n"
29846859ac8SHuacai Chen 		"   cacop 0x8, %[addr], 3           \n"
29946859ac8SHuacai Chen 		"   cacop 0x9, %[addr], 0           \n" /* flush DCache */
30046859ac8SHuacai Chen 		"   cacop 0x9, %[addr], 1           \n"
30146859ac8SHuacai Chen 		"   cacop 0x9, %[addr], 2           \n"
30246859ac8SHuacai Chen 		"   cacop 0x9, %[addr], 3           \n"
30346859ac8SHuacai Chen 		"   addi.w %[sets], %[sets], -1     \n"
30446859ac8SHuacai Chen 		"   addi.d %[addr], %[addr], 0x40   \n"
30546859ac8SHuacai Chen 		"   bnez %[sets], 1b                \n"
30646859ac8SHuacai Chen 		"   li.d %[addr], 0x8000000000000000\n"
30746859ac8SHuacai Chen 		"2: cacop 0xa, %[addr], 0           \n" /* flush VCache */
30846859ac8SHuacai Chen 		"   cacop 0xa, %[addr], 1           \n"
30946859ac8SHuacai Chen 		"   cacop 0xa, %[addr], 2           \n"
31046859ac8SHuacai Chen 		"   cacop 0xa, %[addr], 3           \n"
31146859ac8SHuacai Chen 		"   cacop 0xa, %[addr], 4           \n"
31246859ac8SHuacai Chen 		"   cacop 0xa, %[addr], 5           \n"
31346859ac8SHuacai Chen 		"   cacop 0xa, %[addr], 6           \n"
31446859ac8SHuacai Chen 		"   cacop 0xa, %[addr], 7           \n"
31546859ac8SHuacai Chen 		"   cacop 0xa, %[addr], 8           \n"
31646859ac8SHuacai Chen 		"   cacop 0xa, %[addr], 9           \n"
31746859ac8SHuacai Chen 		"   cacop 0xa, %[addr], 10          \n"
31846859ac8SHuacai Chen 		"   cacop 0xa, %[addr], 11          \n"
31946859ac8SHuacai Chen 		"   cacop 0xa, %[addr], 12          \n"
32046859ac8SHuacai Chen 		"   cacop 0xa, %[addr], 13          \n"
32146859ac8SHuacai Chen 		"   cacop 0xa, %[addr], 14          \n"
32246859ac8SHuacai Chen 		"   cacop 0xa, %[addr], 15          \n"
32346859ac8SHuacai Chen 		"   addi.w %[vsets], %[vsets], -1   \n"
32446859ac8SHuacai Chen 		"   addi.d %[addr], %[addr], 0x40   \n"
32546859ac8SHuacai Chen 		"   bnez   %[vsets], 2b             \n"
32646859ac8SHuacai Chen 		"   li.w   %[val], 0x7              \n" /* *state_addr = CPU_DEAD; */
32746859ac8SHuacai Chen 		"   st.w   %[val], %[state_addr], 0 \n"
32846859ac8SHuacai Chen 		"   dbar 0                          \n"
32946859ac8SHuacai Chen 		"   cacop 0x11, %[state_addr], 0    \n" /* flush entry of *state_addr */
33046859ac8SHuacai Chen 		: [addr] "=&r" (addr), [val] "=&r" (val)
33146859ac8SHuacai Chen 		: [state_addr] "r" (state_addr),
33246859ac8SHuacai Chen 		  [sets] "r" (cpu_data[smp_processor_id()].dcache.sets),
33346859ac8SHuacai Chen 		  [vsets] "r" (cpu_data[smp_processor_id()].vcache.sets));
33446859ac8SHuacai Chen 
33546859ac8SHuacai Chen 	local_irq_enable();
33646859ac8SHuacai Chen 	change_csr_ecfg(ECFG0_IM, ECFGF_IPI);
33746859ac8SHuacai Chen 
33846859ac8SHuacai Chen 	__asm__ __volatile__(
33946859ac8SHuacai Chen 		"   idle      0			    \n"
34046859ac8SHuacai Chen 		"   li.w      $t0, 0x1020	    \n"
34146859ac8SHuacai Chen 		"   iocsrrd.d %[init_fn], $t0	    \n" /* Get init PC */
34246859ac8SHuacai Chen 		: [init_fn] "=&r" (addr)
34346859ac8SHuacai Chen 		: /* No Input */
34446859ac8SHuacai Chen 		: "a0");
34546859ac8SHuacai Chen 	init_fn = __va(addr);
34646859ac8SHuacai Chen 
34746859ac8SHuacai Chen 	init_fn();
34846859ac8SHuacai Chen 	unreachable();
34946859ac8SHuacai Chen }
35046859ac8SHuacai Chen 
35146859ac8SHuacai Chen void play_dead(void)
35246859ac8SHuacai Chen {
35346859ac8SHuacai Chen 	int *state_addr;
35446859ac8SHuacai Chen 	unsigned int cpu = smp_processor_id();
35546859ac8SHuacai Chen 	void (*play_dead_uncached)(int *s);
35646859ac8SHuacai Chen 
35746859ac8SHuacai Chen 	idle_task_exit();
35846859ac8SHuacai Chen 	play_dead_uncached = (void *)TO_UNCACHE(__pa((unsigned long)loongson3_play_dead));
35946859ac8SHuacai Chen 	state_addr = &per_cpu(cpu_state, cpu);
36046859ac8SHuacai Chen 	mb();
36146859ac8SHuacai Chen 	play_dead_uncached(state_addr);
36246859ac8SHuacai Chen }
36346859ac8SHuacai Chen 
36446859ac8SHuacai Chen static int loongson3_enable_clock(unsigned int cpu)
36546859ac8SHuacai Chen {
36646859ac8SHuacai Chen 	uint64_t core_id = cpu_data[cpu].core;
36746859ac8SHuacai Chen 	uint64_t package_id = cpu_data[cpu].package;
36846859ac8SHuacai Chen 
36946859ac8SHuacai Chen 	LOONGSON_FREQCTRL(package_id) |= 1 << (core_id * 4 + 3);
37046859ac8SHuacai Chen 
37146859ac8SHuacai Chen 	return 0;
37246859ac8SHuacai Chen }
37346859ac8SHuacai Chen 
37446859ac8SHuacai Chen static int loongson3_disable_clock(unsigned int cpu)
37546859ac8SHuacai Chen {
37646859ac8SHuacai Chen 	uint64_t core_id = cpu_data[cpu].core;
37746859ac8SHuacai Chen 	uint64_t package_id = cpu_data[cpu].package;
37846859ac8SHuacai Chen 
37946859ac8SHuacai Chen 	LOONGSON_FREQCTRL(package_id) &= ~(1 << (core_id * 4 + 3));
38046859ac8SHuacai Chen 
38146859ac8SHuacai Chen 	return 0;
38246859ac8SHuacai Chen }
38346859ac8SHuacai Chen 
38446859ac8SHuacai Chen static int register_loongson3_notifier(void)
38546859ac8SHuacai Chen {
38646859ac8SHuacai Chen 	return cpuhp_setup_state_nocalls(CPUHP_LOONGARCH_SOC_PREPARE,
38746859ac8SHuacai Chen 					 "loongarch/loongson:prepare",
38846859ac8SHuacai Chen 					 loongson3_enable_clock,
38946859ac8SHuacai Chen 					 loongson3_disable_clock);
39046859ac8SHuacai Chen }
39146859ac8SHuacai Chen early_initcall(register_loongson3_notifier);
39246859ac8SHuacai Chen 
39346859ac8SHuacai Chen #endif
39446859ac8SHuacai Chen 
39546859ac8SHuacai Chen /*
39646859ac8SHuacai Chen  * Power management
39746859ac8SHuacai Chen  */
39846859ac8SHuacai Chen #ifdef CONFIG_PM
39946859ac8SHuacai Chen 
40046859ac8SHuacai Chen static int loongson3_ipi_suspend(void)
40146859ac8SHuacai Chen {
40246859ac8SHuacai Chen 	return 0;
40346859ac8SHuacai Chen }
40446859ac8SHuacai Chen 
40546859ac8SHuacai Chen static void loongson3_ipi_resume(void)
40646859ac8SHuacai Chen {
40746859ac8SHuacai Chen 	iocsr_write32(0xffffffff, LOONGARCH_IOCSR_IPI_EN);
40846859ac8SHuacai Chen }
40946859ac8SHuacai Chen 
41046859ac8SHuacai Chen static struct syscore_ops loongson3_ipi_syscore_ops = {
41146859ac8SHuacai Chen 	.resume         = loongson3_ipi_resume,
41246859ac8SHuacai Chen 	.suspend        = loongson3_ipi_suspend,
41346859ac8SHuacai Chen };
41446859ac8SHuacai Chen 
41546859ac8SHuacai Chen /*
41646859ac8SHuacai Chen  * Enable boot cpu ipi before enabling nonboot cpus
41746859ac8SHuacai Chen  * during syscore_resume.
41846859ac8SHuacai Chen  */
41946859ac8SHuacai Chen static int __init ipi_pm_init(void)
42046859ac8SHuacai Chen {
42146859ac8SHuacai Chen 	register_syscore_ops(&loongson3_ipi_syscore_ops);
42246859ac8SHuacai Chen 	return 0;
42346859ac8SHuacai Chen }
42446859ac8SHuacai Chen 
42546859ac8SHuacai Chen core_initcall(ipi_pm_init);
42646859ac8SHuacai Chen #endif
42746859ac8SHuacai Chen 
42846859ac8SHuacai Chen static inline void set_cpu_sibling_map(int cpu)
42946859ac8SHuacai Chen {
43046859ac8SHuacai Chen 	int i;
43146859ac8SHuacai Chen 
43246859ac8SHuacai Chen 	cpumask_set_cpu(cpu, &cpu_sibling_setup_map);
43346859ac8SHuacai Chen 
43446859ac8SHuacai Chen 	if (smp_num_siblings <= 1)
43546859ac8SHuacai Chen 		cpumask_set_cpu(cpu, &cpu_sibling_map[cpu]);
43646859ac8SHuacai Chen 	else {
43746859ac8SHuacai Chen 		for_each_cpu(i, &cpu_sibling_setup_map) {
43846859ac8SHuacai Chen 			if (cpus_are_siblings(cpu, i)) {
43946859ac8SHuacai Chen 				cpumask_set_cpu(i, &cpu_sibling_map[cpu]);
44046859ac8SHuacai Chen 				cpumask_set_cpu(cpu, &cpu_sibling_map[i]);
44146859ac8SHuacai Chen 			}
44246859ac8SHuacai Chen 		}
44346859ac8SHuacai Chen 	}
44446859ac8SHuacai Chen }
44546859ac8SHuacai Chen 
44646859ac8SHuacai Chen static inline void set_cpu_core_map(int cpu)
44746859ac8SHuacai Chen {
44846859ac8SHuacai Chen 	int i;
44946859ac8SHuacai Chen 
45046859ac8SHuacai Chen 	cpumask_set_cpu(cpu, &cpu_core_setup_map);
45146859ac8SHuacai Chen 
45246859ac8SHuacai Chen 	for_each_cpu(i, &cpu_core_setup_map) {
45346859ac8SHuacai Chen 		if (cpu_data[cpu].package == cpu_data[i].package) {
45446859ac8SHuacai Chen 			cpumask_set_cpu(i, &cpu_core_map[cpu]);
45546859ac8SHuacai Chen 			cpumask_set_cpu(cpu, &cpu_core_map[i]);
45646859ac8SHuacai Chen 		}
45746859ac8SHuacai Chen 	}
45846859ac8SHuacai Chen }
45946859ac8SHuacai Chen 
46046859ac8SHuacai Chen /*
46146859ac8SHuacai Chen  * Calculate a new cpu_foreign_map mask whenever a
46246859ac8SHuacai Chen  * new cpu appears or disappears.
46346859ac8SHuacai Chen  */
46446859ac8SHuacai Chen void calculate_cpu_foreign_map(void)
46546859ac8SHuacai Chen {
46646859ac8SHuacai Chen 	int i, k, core_present;
46746859ac8SHuacai Chen 	cpumask_t temp_foreign_map;
46846859ac8SHuacai Chen 
46946859ac8SHuacai Chen 	/* Re-calculate the mask */
47046859ac8SHuacai Chen 	cpumask_clear(&temp_foreign_map);
47146859ac8SHuacai Chen 	for_each_online_cpu(i) {
47246859ac8SHuacai Chen 		core_present = 0;
47346859ac8SHuacai Chen 		for_each_cpu(k, &temp_foreign_map)
47446859ac8SHuacai Chen 			if (cpus_are_siblings(i, k))
47546859ac8SHuacai Chen 				core_present = 1;
47646859ac8SHuacai Chen 		if (!core_present)
47746859ac8SHuacai Chen 			cpumask_set_cpu(i, &temp_foreign_map);
47846859ac8SHuacai Chen 	}
47946859ac8SHuacai Chen 
48046859ac8SHuacai Chen 	for_each_online_cpu(i)
48146859ac8SHuacai Chen 		cpumask_andnot(&cpu_foreign_map[i],
48246859ac8SHuacai Chen 			       &temp_foreign_map, &cpu_sibling_map[i]);
48346859ac8SHuacai Chen }
48446859ac8SHuacai Chen 
48546859ac8SHuacai Chen /* Preload SMP state for boot cpu */
48646859ac8SHuacai Chen void smp_prepare_boot_cpu(void)
48746859ac8SHuacai Chen {
488*d4b6f156SHuacai Chen 	unsigned int cpu, node, rr_node;
48946859ac8SHuacai Chen 
49046859ac8SHuacai Chen 	set_cpu_possible(0, true);
49146859ac8SHuacai Chen 	set_cpu_online(0, true);
49246859ac8SHuacai Chen 	set_my_cpu_offset(per_cpu_offset(0));
49346859ac8SHuacai Chen 
494*d4b6f156SHuacai Chen 	rr_node = first_node(node_online_map);
495*d4b6f156SHuacai Chen 	for_each_possible_cpu(cpu) {
496*d4b6f156SHuacai Chen 		node = early_cpu_to_node(cpu);
497*d4b6f156SHuacai Chen 
498*d4b6f156SHuacai Chen 		/*
499*d4b6f156SHuacai Chen 		 * The mapping between present cpus and nodes has been
500*d4b6f156SHuacai Chen 		 * built during MADT and SRAT parsing.
501*d4b6f156SHuacai Chen 		 *
502*d4b6f156SHuacai Chen 		 * If possible cpus = present cpus here, early_cpu_to_node
503*d4b6f156SHuacai Chen 		 * will return valid node.
504*d4b6f156SHuacai Chen 		 *
505*d4b6f156SHuacai Chen 		 * If possible cpus > present cpus here (e.g. some possible
506*d4b6f156SHuacai Chen 		 * cpus will be added by cpu-hotplug later), for possible but
507*d4b6f156SHuacai Chen 		 * not present cpus, early_cpu_to_node will return NUMA_NO_NODE,
508*d4b6f156SHuacai Chen 		 * and we just map them to online nodes in round-robin way.
509*d4b6f156SHuacai Chen 		 * Once hotplugged, new correct mapping will be built for them.
510*d4b6f156SHuacai Chen 		 */
511*d4b6f156SHuacai Chen 		if (node != NUMA_NO_NODE)
512*d4b6f156SHuacai Chen 			set_cpu_numa_node(cpu, node);
513*d4b6f156SHuacai Chen 		else {
514*d4b6f156SHuacai Chen 			set_cpu_numa_node(cpu, rr_node);
515*d4b6f156SHuacai Chen 			rr_node = next_node_in(rr_node, node_online_map);
516*d4b6f156SHuacai Chen 		}
517*d4b6f156SHuacai Chen 	}
51846859ac8SHuacai Chen }
51946859ac8SHuacai Chen 
52046859ac8SHuacai Chen /* called from main before smp_init() */
52146859ac8SHuacai Chen void __init smp_prepare_cpus(unsigned int max_cpus)
52246859ac8SHuacai Chen {
52346859ac8SHuacai Chen 	init_new_context(current, &init_mm);
52446859ac8SHuacai Chen 	current_thread_info()->cpu = 0;
52546859ac8SHuacai Chen 	loongson3_prepare_cpus(max_cpus);
52646859ac8SHuacai Chen 	set_cpu_sibling_map(0);
52746859ac8SHuacai Chen 	set_cpu_core_map(0);
52846859ac8SHuacai Chen 	calculate_cpu_foreign_map();
52946859ac8SHuacai Chen #ifndef CONFIG_HOTPLUG_CPU
53046859ac8SHuacai Chen 	init_cpu_present(cpu_possible_mask);
53146859ac8SHuacai Chen #endif
53246859ac8SHuacai Chen }
53346859ac8SHuacai Chen 
53446859ac8SHuacai Chen int __cpu_up(unsigned int cpu, struct task_struct *tidle)
53546859ac8SHuacai Chen {
53646859ac8SHuacai Chen 	loongson3_boot_secondary(cpu, tidle);
53746859ac8SHuacai Chen 
53846859ac8SHuacai Chen 	/* Wait for CPU to start and be ready to sync counters */
53946859ac8SHuacai Chen 	if (!wait_for_completion_timeout(&cpu_starting,
54046859ac8SHuacai Chen 					 msecs_to_jiffies(5000))) {
54146859ac8SHuacai Chen 		pr_crit("CPU%u: failed to start\n", cpu);
54246859ac8SHuacai Chen 		return -EIO;
54346859ac8SHuacai Chen 	}
54446859ac8SHuacai Chen 
54546859ac8SHuacai Chen 	/* Wait for CPU to finish startup & mark itself online before return */
54646859ac8SHuacai Chen 	wait_for_completion(&cpu_running);
54746859ac8SHuacai Chen 
54846859ac8SHuacai Chen 	return 0;
54946859ac8SHuacai Chen }
55046859ac8SHuacai Chen 
55146859ac8SHuacai Chen /*
55246859ac8SHuacai Chen  * First C code run on the secondary CPUs after being started up by
55346859ac8SHuacai Chen  * the master.
55446859ac8SHuacai Chen  */
55546859ac8SHuacai Chen asmlinkage void start_secondary(void)
55646859ac8SHuacai Chen {
55746859ac8SHuacai Chen 	unsigned int cpu;
55846859ac8SHuacai Chen 
55946859ac8SHuacai Chen 	sync_counter();
56046859ac8SHuacai Chen 	cpu = smp_processor_id();
56146859ac8SHuacai Chen 	set_my_cpu_offset(per_cpu_offset(cpu));
56246859ac8SHuacai Chen 
56346859ac8SHuacai Chen 	cpu_probe();
56446859ac8SHuacai Chen 	constant_clockevent_init();
56546859ac8SHuacai Chen 	loongson3_init_secondary();
56646859ac8SHuacai Chen 
56746859ac8SHuacai Chen 	set_cpu_sibling_map(cpu);
56846859ac8SHuacai Chen 	set_cpu_core_map(cpu);
56946859ac8SHuacai Chen 
57046859ac8SHuacai Chen 	notify_cpu_starting(cpu);
57146859ac8SHuacai Chen 
57246859ac8SHuacai Chen 	/* Notify boot CPU that we're starting */
57346859ac8SHuacai Chen 	complete(&cpu_starting);
57446859ac8SHuacai Chen 
57546859ac8SHuacai Chen 	/* The CPU is running, now mark it online */
57646859ac8SHuacai Chen 	set_cpu_online(cpu, true);
57746859ac8SHuacai Chen 
57846859ac8SHuacai Chen 	calculate_cpu_foreign_map();
57946859ac8SHuacai Chen 
58046859ac8SHuacai Chen 	/*
58146859ac8SHuacai Chen 	 * Notify boot CPU that we're up & online and it can safely return
58246859ac8SHuacai Chen 	 * from __cpu_up()
58346859ac8SHuacai Chen 	 */
58446859ac8SHuacai Chen 	complete(&cpu_running);
58546859ac8SHuacai Chen 
58646859ac8SHuacai Chen 	/*
58746859ac8SHuacai Chen 	 * irq will be enabled in loongson3_smp_finish(), enabling it too
58846859ac8SHuacai Chen 	 * early is dangerous.
58946859ac8SHuacai Chen 	 */
59046859ac8SHuacai Chen 	WARN_ON_ONCE(!irqs_disabled());
59146859ac8SHuacai Chen 	loongson3_smp_finish();
59246859ac8SHuacai Chen 
59346859ac8SHuacai Chen 	cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
59446859ac8SHuacai Chen }
59546859ac8SHuacai Chen 
59646859ac8SHuacai Chen void __init smp_cpus_done(unsigned int max_cpus)
59746859ac8SHuacai Chen {
59846859ac8SHuacai Chen }
59946859ac8SHuacai Chen 
60046859ac8SHuacai Chen static void stop_this_cpu(void *dummy)
60146859ac8SHuacai Chen {
60246859ac8SHuacai Chen 	set_cpu_online(smp_processor_id(), false);
60346859ac8SHuacai Chen 	calculate_cpu_foreign_map();
60446859ac8SHuacai Chen 	local_irq_disable();
60546859ac8SHuacai Chen 	while (true);
60646859ac8SHuacai Chen }
60746859ac8SHuacai Chen 
60846859ac8SHuacai Chen void smp_send_stop(void)
60946859ac8SHuacai Chen {
61046859ac8SHuacai Chen 	smp_call_function(stop_this_cpu, NULL, 0);
61146859ac8SHuacai Chen }
61246859ac8SHuacai Chen 
61346859ac8SHuacai Chen int setup_profiling_timer(unsigned int multiplier)
61446859ac8SHuacai Chen {
61546859ac8SHuacai Chen 	return 0;
61646859ac8SHuacai Chen }
61746859ac8SHuacai Chen 
61846859ac8SHuacai Chen static void flush_tlb_all_ipi(void *info)
61946859ac8SHuacai Chen {
62046859ac8SHuacai Chen 	local_flush_tlb_all();
62146859ac8SHuacai Chen }
62246859ac8SHuacai Chen 
62346859ac8SHuacai Chen void flush_tlb_all(void)
62446859ac8SHuacai Chen {
62546859ac8SHuacai Chen 	on_each_cpu(flush_tlb_all_ipi, NULL, 1);
62646859ac8SHuacai Chen }
62746859ac8SHuacai Chen 
62846859ac8SHuacai Chen static void flush_tlb_mm_ipi(void *mm)
62946859ac8SHuacai Chen {
63046859ac8SHuacai Chen 	local_flush_tlb_mm((struct mm_struct *)mm);
63146859ac8SHuacai Chen }
63246859ac8SHuacai Chen 
63346859ac8SHuacai Chen void flush_tlb_mm(struct mm_struct *mm)
63446859ac8SHuacai Chen {
63546859ac8SHuacai Chen 	if (atomic_read(&mm->mm_users) == 0)
63646859ac8SHuacai Chen 		return;		/* happens as a result of exit_mmap() */
63746859ac8SHuacai Chen 
63846859ac8SHuacai Chen 	preempt_disable();
63946859ac8SHuacai Chen 
64046859ac8SHuacai Chen 	if ((atomic_read(&mm->mm_users) != 1) || (current->mm != mm)) {
64146859ac8SHuacai Chen 		on_each_cpu_mask(mm_cpumask(mm), flush_tlb_mm_ipi, mm, 1);
64246859ac8SHuacai Chen 	} else {
64346859ac8SHuacai Chen 		unsigned int cpu;
64446859ac8SHuacai Chen 
64546859ac8SHuacai Chen 		for_each_online_cpu(cpu) {
64646859ac8SHuacai Chen 			if (cpu != smp_processor_id() && cpu_context(cpu, mm))
64746859ac8SHuacai Chen 				cpu_context(cpu, mm) = 0;
64846859ac8SHuacai Chen 		}
64946859ac8SHuacai Chen 		local_flush_tlb_mm(mm);
65046859ac8SHuacai Chen 	}
65146859ac8SHuacai Chen 
65246859ac8SHuacai Chen 	preempt_enable();
65346859ac8SHuacai Chen }
65446859ac8SHuacai Chen 
65546859ac8SHuacai Chen struct flush_tlb_data {
65646859ac8SHuacai Chen 	struct vm_area_struct *vma;
65746859ac8SHuacai Chen 	unsigned long addr1;
65846859ac8SHuacai Chen 	unsigned long addr2;
65946859ac8SHuacai Chen };
66046859ac8SHuacai Chen 
66146859ac8SHuacai Chen static void flush_tlb_range_ipi(void *info)
66246859ac8SHuacai Chen {
66346859ac8SHuacai Chen 	struct flush_tlb_data *fd = info;
66446859ac8SHuacai Chen 
66546859ac8SHuacai Chen 	local_flush_tlb_range(fd->vma, fd->addr1, fd->addr2);
66646859ac8SHuacai Chen }
66746859ac8SHuacai Chen 
66846859ac8SHuacai Chen void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
66946859ac8SHuacai Chen {
67046859ac8SHuacai Chen 	struct mm_struct *mm = vma->vm_mm;
67146859ac8SHuacai Chen 
67246859ac8SHuacai Chen 	preempt_disable();
67346859ac8SHuacai Chen 	if ((atomic_read(&mm->mm_users) != 1) || (current->mm != mm)) {
67446859ac8SHuacai Chen 		struct flush_tlb_data fd = {
67546859ac8SHuacai Chen 			.vma = vma,
67646859ac8SHuacai Chen 			.addr1 = start,
67746859ac8SHuacai Chen 			.addr2 = end,
67846859ac8SHuacai Chen 		};
67946859ac8SHuacai Chen 
68046859ac8SHuacai Chen 		on_each_cpu_mask(mm_cpumask(mm), flush_tlb_range_ipi, &fd, 1);
68146859ac8SHuacai Chen 	} else {
68246859ac8SHuacai Chen 		unsigned int cpu;
68346859ac8SHuacai Chen 
68446859ac8SHuacai Chen 		for_each_online_cpu(cpu) {
68546859ac8SHuacai Chen 			if (cpu != smp_processor_id() && cpu_context(cpu, mm))
686*d4b6f156SHuacai Chen 				cpu_context(cpu, mm) = 0;
68746859ac8SHuacai Chen 		}
68846859ac8SHuacai Chen 		local_flush_tlb_range(vma, start, end);
68946859ac8SHuacai Chen 	}
69046859ac8SHuacai Chen 	preempt_enable();
69146859ac8SHuacai Chen }
69246859ac8SHuacai Chen 
69346859ac8SHuacai Chen static void flush_tlb_kernel_range_ipi(void *info)
69446859ac8SHuacai Chen {
69546859ac8SHuacai Chen 	struct flush_tlb_data *fd = info;
69646859ac8SHuacai Chen 
69746859ac8SHuacai Chen 	local_flush_tlb_kernel_range(fd->addr1, fd->addr2);
69846859ac8SHuacai Chen }
69946859ac8SHuacai Chen 
70046859ac8SHuacai Chen void flush_tlb_kernel_range(unsigned long start, unsigned long end)
70146859ac8SHuacai Chen {
70246859ac8SHuacai Chen 	struct flush_tlb_data fd = {
70346859ac8SHuacai Chen 		.addr1 = start,
70446859ac8SHuacai Chen 		.addr2 = end,
70546859ac8SHuacai Chen 	};
70646859ac8SHuacai Chen 
70746859ac8SHuacai Chen 	on_each_cpu(flush_tlb_kernel_range_ipi, &fd, 1);
70846859ac8SHuacai Chen }
70946859ac8SHuacai Chen 
71046859ac8SHuacai Chen static void flush_tlb_page_ipi(void *info)
71146859ac8SHuacai Chen {
71246859ac8SHuacai Chen 	struct flush_tlb_data *fd = info;
71346859ac8SHuacai Chen 
71446859ac8SHuacai Chen 	local_flush_tlb_page(fd->vma, fd->addr1);
71546859ac8SHuacai Chen }
71646859ac8SHuacai Chen 
71746859ac8SHuacai Chen void flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
71846859ac8SHuacai Chen {
71946859ac8SHuacai Chen 	preempt_disable();
72046859ac8SHuacai Chen 	if ((atomic_read(&vma->vm_mm->mm_users) != 1) || (current->mm != vma->vm_mm)) {
72146859ac8SHuacai Chen 		struct flush_tlb_data fd = {
72246859ac8SHuacai Chen 			.vma = vma,
72346859ac8SHuacai Chen 			.addr1 = page,
72446859ac8SHuacai Chen 		};
72546859ac8SHuacai Chen 
72646859ac8SHuacai Chen 		on_each_cpu_mask(mm_cpumask(vma->vm_mm), flush_tlb_page_ipi, &fd, 1);
72746859ac8SHuacai Chen 	} else {
72846859ac8SHuacai Chen 		unsigned int cpu;
72946859ac8SHuacai Chen 
73046859ac8SHuacai Chen 		for_each_online_cpu(cpu) {
73146859ac8SHuacai Chen 			if (cpu != smp_processor_id() && cpu_context(cpu, vma->vm_mm))
732*d4b6f156SHuacai Chen 				cpu_context(cpu, vma->vm_mm) = 0;
73346859ac8SHuacai Chen 		}
73446859ac8SHuacai Chen 		local_flush_tlb_page(vma, page);
73546859ac8SHuacai Chen 	}
73646859ac8SHuacai Chen 	preempt_enable();
73746859ac8SHuacai Chen }
73846859ac8SHuacai Chen EXPORT_SYMBOL(flush_tlb_page);
73946859ac8SHuacai Chen 
74046859ac8SHuacai Chen static void flush_tlb_one_ipi(void *info)
74146859ac8SHuacai Chen {
74246859ac8SHuacai Chen 	unsigned long vaddr = (unsigned long) info;
74346859ac8SHuacai Chen 
74446859ac8SHuacai Chen 	local_flush_tlb_one(vaddr);
74546859ac8SHuacai Chen }
74646859ac8SHuacai Chen 
74746859ac8SHuacai Chen void flush_tlb_one(unsigned long vaddr)
74846859ac8SHuacai Chen {
74946859ac8SHuacai Chen 	on_each_cpu(flush_tlb_one_ipi, (void *)vaddr, 1);
75046859ac8SHuacai Chen }
75146859ac8SHuacai Chen EXPORT_SYMBOL(flush_tlb_one);
752