summaryrefslogtreecommitdiffstats
path: root/arch/mips64/sgi-ip27/ip27-nmi.c
blob: 8b03918f817a336c524be1fb4ec8093d17cc625b (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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#include <linux/kernel.h>
#include <linux/mmzone.h>
#include <linux/spinlock.h>
#include <linux/smp.h>
#include <asm/atomic.h>
#include <asm/sn/types.h>
#include <asm/sn/addrs.h>
#include <asm/sn/nmi.h>
#include <asm/sn/arch.h>
#include <asm/sn/sn0/hub.h>

#if 0
#define NODE_NUM_CPUS(n)	CNODE_NUM_CPUS(n)
#else
#define NODE_NUM_CPUS(n)	CPUS_PER_NODE
#endif

#define CNODEID_NONE (cnodeid_t)-1
#define enter_panic_mode()	spin_lock(&nmi_lock)

typedef unsigned long machreg_t;

spinlock_t nmi_lock = SPIN_LOCK_UNLOCKED;

/*
 * Lets see what else we need to do here. Set up sp, gp?
 */
void nmi_dump(void)
{
	void cont_nmi_dump(void);

	cont_nmi_dump();
}

void install_cpu_nmi_handler(int slice)
{
	nmi_t *nmi_addr;

	nmi_addr = (nmi_t *)NMI_ADDR(get_nasid(), slice);
	if (nmi_addr->call_addr)
		return;
	nmi_addr->magic = NMI_MAGIC;
	nmi_addr->call_addr = (void *)nmi_dump;
	nmi_addr->call_addr_c =
		(void *)(~((unsigned long)(nmi_addr->call_addr)));
	nmi_addr->call_parm = 0;
}

/*
 * Copy the cpu registers which have been saved in the IP27prom format
 * into the eframe format for the node under consideration.
 */

void
nmi_cpu_eframe_save(nasid_t nasid,
		    int	    slice)
{
	int 		i, numberof_nmi_cpu_regs;
	machreg_t	*prom_format;

	/* Get the total number of registers being saved by the prom */
	numberof_nmi_cpu_regs = sizeof(struct reg_struct) / sizeof(machreg_t);

	/* Get the pointer to the current cpu's register set. */
	prom_format = 
	    (machreg_t *)(TO_UNCAC(TO_NODE(nasid, IP27_NMI_KREGS_OFFSET)) +
			  slice * IP27_NMI_KREGS_CPU_SIZE);

	printk("NMI nasid %d: slice %d\n", nasid, slice);
	for (i = 0; i < numberof_nmi_cpu_regs; i++)
		printk("0x%lx  ", prom_format[i]);
	printk("\n\n");
}	

/*
 * Copy the cpu registers which have been saved in the IP27prom format
 * into the eframe format for the node under consideration.
 */
void
nmi_node_eframe_save(cnodeid_t  cnode)
{
	int		cpu;
	nasid_t		nasid;

	/* Make sure that we have a valid node */
	if (cnode == CNODEID_NONE)
		return;

	nasid = COMPACT_TO_NASID_NODEID(cnode);
	if (nasid == INVALID_NASID)
		return;

	/* Save the registers into eframe for each cpu */
	for(cpu = 0; cpu < NODE_NUM_CPUS(cnode); cpu++) 
		nmi_cpu_eframe_save(nasid, cpu);
}

/*
 * Save the nmi cpu registers for all cpus in the system.
 */
void
nmi_eframes_save(void)
{
	cnodeid_t	cnode;

	for(cnode = 0 ; cnode < numnodes; cnode++) 
		nmi_node_eframe_save(cnode);
}

void
cont_nmi_dump(void)
{
#ifndef REAL_NMI_SIGNAL
	static atomic_t nmied_cpus = ATOMIC_INIT(0);

	atomic_inc(&nmied_cpus);
#endif
	/* 
	 * Use enter_panic_mode to allow only 1 cpu to proceed
	 */
	enter_panic_mode();

#ifdef REAL_NMI_SIGNAL
	/*
	 * Wait up to 15 seconds for the other cpus to respond to the NMI.
	 * If a cpu has not responded after 10 sec, send it 1 additional NMI.
	 * This is for 2 reasons:
	 *	- sometimes a MMSC fail to NMI all cpus.
	 *	- on 512p SN0 system, the MMSC will only send NMIs to
	 *	  half the cpus. Unfortunately, we dont know which cpus may be
	 *	  NMIed - it depends on how the site chooses to configure.
	 * 
	 * Note: it has been measure that it takes the MMSC up to 2.3 secs to
	 * send NMIs to all cpus on a 256p system.
	 */
	for (i=0; i < 1500; i++) {
		for (node=0; node < numnodes; node++)
			if (NODEPDA(node)->dump_count == 0)
				break;
		if (node == numnodes)
			break;
		if (i == 1000) {
			for (node=0; node < numnodes; node++)
				if (NODEPDA(node)->dump_count == 0) {
					cpu = CNODE_TO_CPU_BASE(node);
					for (n=0; n < CNODE_NUM_CPUS(node); cpu++, n++) {
						CPUMASK_SETB(nmied_cpus, cpu);
						/* 
						 * cputonasid, cputoslice
						 * needs kernel cpuid
						 */
						SEND_NMI((cputonasid(cpu)), (cputoslice(cpu)));
					}
				}
					
		}
		udelay(10000);
	}
#else
	while (atomic_read(&nmied_cpus) != smp_num_cpus);
#endif

	/*
	 * Save the nmi cpu registers for all cpu in the eframe format.
	 */
	nmi_eframes_save();
	LOCAL_HUB_S(NI_PORT_RESET, NPR_PORTRESET | NPR_LOCALRESET);
}