summaryrefslogtreecommitdiff
path: root/drivers/misc/sgi-xp/xpc_sn2.c
blob: 5a37348715c7b5b0a424335c2d80bee65ed2fb9c (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
/*
 * 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) 2008 Silicon Graphics, Inc.  All Rights Reserved.
 */

/*
 * Cross Partition Communication (XPC) sn2-based functions.
 *
 *     Architecture specific implementation of common functions.
 *
 */

#include <linux/kernel.h>
#include <asm/uncached.h>
#include <asm/sn/sn_sal.h>
#include "xpc.h"

struct xpc_vars *xpc_vars;
struct xpc_vars_part *xpc_vars_part;

static enum xp_retval
xpc_rsvd_page_init_sn2(struct xpc_rsvd_page *rp)
{
	AMO_t *amos_page;
	u64 nasid_array = 0;
	int i;
	int ret;

	xpc_vars = XPC_RP_VARS(rp);

	rp->sn.vars_pa = __pa(xpc_vars);

	xpc_vars_part = XPC_RP_VARS_PART(rp);

	/*
	 * Before clearing xpc_vars, see if a page of AMOs had been previously
	 * allocated. If not we'll need to allocate one and set permissions
	 * so that cross-partition AMOs are allowed.
	 *
	 * The allocated AMO page needs MCA reporting to remain disabled after
	 * XPC has unloaded.  To make this work, we keep a copy of the pointer
	 * to this page (i.e., amos_page) in the struct xpc_vars structure,
	 * which is pointed to by the reserved page, and re-use that saved copy
	 * on subsequent loads of XPC. This AMO page is never freed, and its
	 * memory protections are never restricted.
	 */
	amos_page = xpc_vars->amos_page;
	if (amos_page == NULL) {
		amos_page = (AMO_t *)TO_AMO(uncached_alloc_page(0, 1));
		if (amos_page == NULL) {
			dev_err(xpc_part, "can't allocate page of AMOs\n");
			return xpNoMemory;
		}

		/*
		 * Open up AMO-R/W to cpu.  This is done for Shub 1.1 systems
		 * when xpc_allow_IPI_ops() is called via xpc_hb_init().
		 */
		if (!enable_shub_wars_1_1()) {
			ret = sn_change_memprotect(ia64_tpa((u64)amos_page),
						   PAGE_SIZE,
						   SN_MEMPROT_ACCESS_CLASS_1,
						   &nasid_array);
			if (ret != 0) {
				dev_err(xpc_part, "can't change memory "
					"protections\n");
				uncached_free_page(__IA64_UNCACHED_OFFSET |
						   TO_PHYS((u64)amos_page), 1);
				return xpSalError;
			}
		}
	}

	/* clear xpc_vars */
	memset(xpc_vars, 0, sizeof(struct xpc_vars));

	xpc_vars->version = XPC_V_VERSION;
	xpc_vars->act_nasid = cpuid_to_nasid(0);
	xpc_vars->act_phys_cpuid = cpu_physical_id(0);
	xpc_vars->vars_part_pa = __pa(xpc_vars_part);
	xpc_vars->amos_page_pa = ia64_tpa((u64)amos_page);
	xpc_vars->amos_page = amos_page;	/* save for next load of XPC */

	/* clear xpc_vars_part */
	memset((u64 *)xpc_vars_part, 0, sizeof(struct xpc_vars_part) *
	       xp_max_npartitions);

	/* initialize the activate IRQ related AMO variables */
	for (i = 0; i < xp_nasid_mask_words; i++)
		(void)xpc_IPI_init(XPC_ACTIVATE_IRQ_AMOS + i);

	/* initialize the engaged remote partitions related AMO variables */
	(void)xpc_IPI_init(XPC_ENGAGED_PARTITIONS_AMO);
	(void)xpc_IPI_init(XPC_DISENGAGE_REQUEST_AMO);

	return xpSuccess;
}

void
xpc_init_sn2(void)
{
	xpc_rsvd_page_init = xpc_rsvd_page_init_sn2;
}

void
xpc_exit_sn2(void)
{
}