aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/sgi-ip30/ip30-smp.c
blob: 4bfe654602b1190f9e59f89a08a83d346603e568 (plain) (blame)
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
144
145
146
147
148
149
// 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,
};