summaryrefslogtreecommitdiffstats
path: root/arch/mips/paravirt/paravirt-smp.c
blob: 42181c7105df70992892ead68933bcd5375ab74b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) 2013 Cavium, Inc.
 */

#include <linux/interrupt.h>
#include <linux/cpumask.h>
#include <linux/kernel.h>
#include <linux/sched.h>

#include <asm/mipsregs.h>
#include <asm/setup.h>
#include <asm/time.h>
#include <asm/smp.h>

/*
 * Writing the sp releases the CPU, so writes must be ordered, gp
 * first, then sp.
 */
unsigned long paravirt_smp_sp[NR_CPUS];
unsigned long paravirt_smp_gp[NR_CPUS];

static int numcpus = 1;

static int __init set_numcpus(char *str)
{
	int newval;

	if (get_option(&str, &newval)) {
		if (newval < 1 || newval >= NR_CPUS)
			goto bad;
		numcpus = newval;
		return 0;
	}
bad:
	return -EINVAL;
}
early_param("numcpus", set_numcpus);


static void paravirt_smp_setup(void)
{
	int id;
	unsigned int cpunum = get_ebase_cpunum();

	if (WARN_ON(cpunum >= NR_CPUS))
		return;

	/* The present CPUs are initially just the boot cpu (CPU 0). */
	for (id = 0; id < NR_CPUS; id++) {
		set_cpu_possible(id, id == 0);
		set_cpu_present(id, id == 0);
	}
	__cpu_number_map[cpunum] = 0;
	__cpu_logical_map[0] = cpunum;

	for (id = 0; id < numcpus; id++) {
		set_cpu_possible(id, true);
		set_cpu_present(id, true);
		__cpu_number_map[id] = id;
		__cpu_logical_map[id] = id;
	}
}

void irq_mbox_ipi(int cpu, unsigned int actions);
static void paravirt_send_ipi_single(int cpu, unsigned int action)
{
	irq_mbox_ipi(cpu, action);
}

static void paravirt_send_ipi_mask(const struct cpumask *mask, unsigned int action)
{
	unsigned int cpu;

	for_each_cpu(cpu, mask)
		paravirt_send_ipi_single(cpu, action);
}

static void paravirt_init_secondary(void)
{
	unsigned int sr;

	sr = set_c0_status(ST0_BEV);
	write_c0_ebase((u32)ebase);

	sr |= STATUSF_IP2; /* Interrupt controller on IP2 */
	write_c0_status(sr);

	irq_cpu_online();
}

static void paravirt_smp_finish(void)
{
	/* to generate the first CPU timer interrupt */
	write_c0_compare(read_c0_count() + mips_hpt_frequency / HZ);
	local_irq_enable();
}

static void paravirt_boot_secondary(int cpu, struct task_struct *idle)
{
	paravirt_smp_gp[cpu] = (unsigned long)task_thread_info(idle);
	smp_wmb();
	paravirt_smp_sp[cpu] = __KSTK_TOS(idle);
}

static irqreturn_t paravirt_reched_interrupt(int irq, void *dev_id)
{
	scheduler_ipi();
	return IRQ_HANDLED;
}

static irqreturn_t paravirt_function_interrupt(int irq, void *dev_id)
{
	smp_call_function_interrupt();
	return IRQ_HANDLED;
}

static void paravirt_prepare_cpus(unsigned int max_cpus)
{
	if (request_irq(MIPS_IRQ_MBOX0, paravirt_reched_interrupt,
			IRQF_PERCPU | IRQF_NO_THREAD, "Scheduler",
			paravirt_reched_interrupt)) {
		panic("Cannot request_irq for SchedulerIPI");
	}
	if (request_irq(MIPS_IRQ_MBOX1, paravirt_function_interrupt,
			IRQF_PERCPU | IRQF_NO_THREAD, "SMP-Call",
			paravirt_function_interrupt)) {
		panic("Cannot request_irq for SMP-Call");
	}
}

struct plat_smp_ops paravirt_smp_ops = {
	.send_ipi_single	= paravirt_send_ipi_single,
	.send_ipi_mask		= paravirt_send_ipi_mask,
	.init_secondary		= paravirt_init_secondary,
	.smp_finish		= paravirt_smp_finish,
	.boot_secondary		= paravirt_boot_secondary,
	.smp_setup		= paravirt_smp_setup,
	.prepare_cpus		= paravirt_prepare_cpus,
};