Commit | Line | Data |
---|---|---|
1a59d1b8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
7019cc2d RA |
2 | /* |
3 | * BIOS run time interface routines. | |
4 | * | |
7a6d94f0 | 5 | * (C) Copyright 2020 Hewlett Packard Enterprise Development LP |
1e61f5a9 MT |
6 | * Copyright (C) 2007-2017 Silicon Graphics, Inc. All rights reserved. |
7 | * Copyright (c) Russ Anderson <rja@sgi.com> | |
7019cc2d RA |
8 | */ |
9 | ||
7f594232 | 10 | #include <linux/efi.h> |
69c60c88 | 11 | #include <linux/export.h> |
ef93bf80 | 12 | #include <linux/slab.h> |
7f594232 RA |
13 | #include <asm/efi.h> |
14 | #include <linux/io.h> | |
ca15ca40 | 15 | #include <asm/pgalloc.h> |
7019cc2d | 16 | #include <asm/uv/bios.h> |
922402f1 | 17 | #include <asm/uv/uv_hub.h> |
7019cc2d | 18 | |
ec7e1605 AB |
19 | unsigned long uv_systab_phys __ro_after_init = EFI_INVALID_TABLE_ADDR; |
20 | ||
ef93bf80 | 21 | struct uv_systab *uv_systab; |
7f594232 | 22 | |
f331e766 HB |
23 | static s64 __uv_bios_call(enum uv_bios_cmd which, u64 a1, u64 a2, u64 a3, |
24 | u64 a4, u64 a5) | |
7019cc2d | 25 | { |
ef93bf80 | 26 | struct uv_systab *tab = uv_systab; |
b76365a1 | 27 | s64 ret; |
7f594232 | 28 | |
ef93bf80 | 29 | if (!tab || !tab->function) |
7f594232 RA |
30 | /* |
31 | * BIOS does not support UV systab | |
32 | */ | |
33 | return BIOS_STATUS_UNIMPLEMENTED; | |
34 | ||
6aa3baab | 35 | ret = efi_call_virt_pointer(tab, function, (u64)which, a1, a2, a3, a4, a5); |
caef78b6 | 36 | |
b76365a1 | 37 | return ret; |
7019cc2d | 38 | } |
f331e766 | 39 | |
30ad8db3 CH |
40 | static s64 uv_bios_call(enum uv_bios_cmd which, u64 a1, u64 a2, u64 a3, u64 a4, |
41 | u64 a5) | |
f331e766 HB |
42 | { |
43 | s64 ret; | |
44 | ||
45 | if (down_interruptible(&__efi_uv_runtime_lock)) | |
46 | return BIOS_STATUS_ABORT; | |
47 | ||
48 | ret = __uv_bios_call(which, a1, a2, a3, a4, a5); | |
49 | up(&__efi_uv_runtime_lock); | |
50 | ||
51 | return ret; | |
52 | } | |
7019cc2d | 53 | |
30ad8db3 CH |
54 | static s64 uv_bios_call_irqsave(enum uv_bios_cmd which, u64 a1, u64 a2, u64 a3, |
55 | u64 a4, u64 a5) | |
7019cc2d | 56 | { |
7f594232 RA |
57 | unsigned long bios_flags; |
58 | s64 ret; | |
59 | ||
f331e766 HB |
60 | if (down_interruptible(&__efi_uv_runtime_lock)) |
61 | return BIOS_STATUS_ABORT; | |
62 | ||
7f594232 | 63 | local_irq_save(bios_flags); |
f331e766 | 64 | ret = __uv_bios_call(which, a1, a2, a3, a4, a5); |
7f594232 RA |
65 | local_irq_restore(bios_flags); |
66 | ||
f331e766 HB |
67 | up(&__efi_uv_runtime_lock); |
68 | ||
7f594232 RA |
69 | return ret; |
70 | } | |
71 | ||
922402f1 RA |
72 | long sn_partition_id; |
73 | EXPORT_SYMBOL_GPL(sn_partition_id); | |
b0f20989 | 74 | long sn_coherency_id; |
9a3c425c | 75 | EXPORT_SYMBOL_GPL(sn_coherency_id); |
b0f20989 RA |
76 | long sn_region_size; |
77 | EXPORT_SYMBOL_GPL(sn_region_size); | |
b76365a1 | 78 | long system_serial_number; |
922402f1 | 79 | int uv_type; |
922402f1 RA |
80 | |
81 | s64 uv_bios_get_sn_info(int fc, int *uvtype, long *partid, long *coher, | |
b76365a1 | 82 | long *region, long *ssn) |
922402f1 RA |
83 | { |
84 | s64 ret; | |
85 | u64 v0, v1; | |
86 | union partition_info_u part; | |
87 | ||
88 | ret = uv_bios_call_irqsave(UV_BIOS_GET_SN_INFO, fc, | |
89 | (u64)(&v0), (u64)(&v1), 0, 0); | |
90 | if (ret != BIOS_STATUS_SUCCESS) | |
91 | return ret; | |
92 | ||
93 | part.val = v0; | |
94 | if (uvtype) | |
95 | *uvtype = part.hub_version; | |
96 | if (partid) | |
97 | *partid = part.partition_id; | |
98 | if (coher) | |
99 | *coher = part.coherence_id; | |
100 | if (region) | |
101 | *region = part.region_size; | |
b76365a1 RA |
102 | if (ssn) |
103 | *ssn = v1; | |
922402f1 RA |
104 | return ret; |
105 | } | |
106 | ||
64ccf2f9 | 107 | int |
c2c9f115 | 108 | uv_bios_mq_watchlist_alloc(unsigned long addr, unsigned int mq_size, |
64ccf2f9 RA |
109 | unsigned long *intr_mmr_offset) |
110 | { | |
64ccf2f9 RA |
111 | u64 watchlist; |
112 | s64 ret; | |
113 | ||
64ccf2f9 RA |
114 | /* |
115 | * bios returns watchlist number or negative error number. | |
116 | */ | |
117 | ret = (int)uv_bios_call_irqsave(UV_BIOS_WATCHLIST_ALLOC, addr, | |
c2c9f115 | 118 | mq_size, (u64)intr_mmr_offset, |
64ccf2f9 RA |
119 | (u64)&watchlist, 0); |
120 | if (ret < BIOS_STATUS_SUCCESS) | |
121 | return ret; | |
122 | ||
123 | return watchlist; | |
124 | } | |
125 | EXPORT_SYMBOL_GPL(uv_bios_mq_watchlist_alloc); | |
126 | ||
127 | int | |
128 | uv_bios_mq_watchlist_free(int blade, int watchlist_num) | |
129 | { | |
130 | return (int)uv_bios_call_irqsave(UV_BIOS_WATCHLIST_FREE, | |
131 | blade, watchlist_num, 0, 0, 0); | |
132 | } | |
133 | EXPORT_SYMBOL_GPL(uv_bios_mq_watchlist_free); | |
922402f1 | 134 | |
e8929c8a RA |
135 | s64 |
136 | uv_bios_change_memprotect(u64 paddr, u64 len, enum uv_memprotect perms) | |
137 | { | |
138 | return uv_bios_call_irqsave(UV_BIOS_MEMPROTECT, paddr, len, | |
139 | perms, 0, 0); | |
140 | } | |
141 | EXPORT_SYMBOL_GPL(uv_bios_change_memprotect); | |
142 | ||
23c35700 RA |
143 | s64 |
144 | uv_bios_reserved_page_pa(u64 buf, u64 *cookie, u64 *addr, u64 *len) | |
145 | { | |
f148b41e MY |
146 | return uv_bios_call_irqsave(UV_BIOS_GET_PARTITION_ADDR, (u64)cookie, |
147 | (u64)addr, buf, (u64)len, 0); | |
23c35700 RA |
148 | } |
149 | EXPORT_SYMBOL_GPL(uv_bios_reserved_page_pa); | |
150 | ||
922402f1 | 151 | s64 uv_bios_freq_base(u64 clock_type, u64 *ticks_per_second) |
7f594232 RA |
152 | { |
153 | return uv_bios_call(UV_BIOS_FREQ_BASE, clock_type, | |
922402f1 | 154 | (u64)ticks_per_second, 0, 0, 0); |
7019cc2d | 155 | } |
7f594232 | 156 | |
841582ea MT |
157 | /* |
158 | * uv_bios_set_legacy_vga_target - Set Legacy VGA I/O Target | |
159 | * @decode: true to enable target, false to disable target | |
160 | * @domain: PCI domain number | |
161 | * @bus: PCI bus number | |
162 | * | |
163 | * Returns: | |
164 | * 0: Success | |
165 | * -EINVAL: Invalid domain or bus number | |
166 | * -ENOSYS: Capability not available | |
167 | * -EBUSY: Legacy VGA I/O cannot be retargeted at this time | |
168 | */ | |
169 | int uv_bios_set_legacy_vga_target(bool decode, int domain, int bus) | |
170 | { | |
171 | return uv_bios_call(UV_BIOS_SET_LEGACY_VGA_TARGET, | |
172 | (u64)decode, (u64)domain, (u64)bus, 0, 0); | |
173 | } | |
841582ea | 174 | |
9a3c425c JE |
175 | extern s64 uv_bios_get_master_nasid(u64 size, u64 *master_nasid) |
176 | { | |
177 | return uv_bios_call(UV_BIOS_EXTRA, 0, UV_BIOS_EXTRA_MASTER_NASID, 0, | |
178 | size, (u64)master_nasid); | |
179 | } | |
180 | EXPORT_SYMBOL_GPL(uv_bios_get_master_nasid); | |
181 | ||
182 | extern s64 uv_bios_get_heapsize(u64 nasid, u64 size, u64 *heap_size) | |
183 | { | |
184 | return uv_bios_call(UV_BIOS_EXTRA, nasid, UV_BIOS_EXTRA_GET_HEAPSIZE, | |
185 | 0, size, (u64)heap_size); | |
186 | } | |
187 | EXPORT_SYMBOL_GPL(uv_bios_get_heapsize); | |
188 | ||
189 | extern s64 uv_bios_install_heap(u64 nasid, u64 heap_size, u64 *bios_heap) | |
190 | { | |
191 | return uv_bios_call(UV_BIOS_EXTRA, nasid, UV_BIOS_EXTRA_INSTALL_HEAP, | |
192 | 0, heap_size, (u64)bios_heap); | |
193 | } | |
194 | EXPORT_SYMBOL_GPL(uv_bios_install_heap); | |
195 | ||
196 | extern s64 uv_bios_obj_count(u64 nasid, u64 size, u64 *objcnt) | |
197 | { | |
198 | return uv_bios_call(UV_BIOS_EXTRA, nasid, UV_BIOS_EXTRA_OBJECT_COUNT, | |
199 | 0, size, (u64)objcnt); | |
200 | } | |
201 | EXPORT_SYMBOL_GPL(uv_bios_obj_count); | |
202 | ||
203 | extern s64 uv_bios_enum_objs(u64 nasid, u64 size, u64 *objbuf) | |
204 | { | |
205 | return uv_bios_call(UV_BIOS_EXTRA, nasid, UV_BIOS_EXTRA_ENUM_OBJECTS, | |
206 | 0, size, (u64)objbuf); | |
207 | } | |
208 | EXPORT_SYMBOL_GPL(uv_bios_enum_objs); | |
209 | ||
210 | extern s64 uv_bios_enum_ports(u64 nasid, u64 obj_id, u64 size, u64 *portbuf) | |
211 | { | |
212 | return uv_bios_call(UV_BIOS_EXTRA, nasid, UV_BIOS_EXTRA_ENUM_PORTS, | |
213 | obj_id, size, (u64)portbuf); | |
214 | } | |
215 | EXPORT_SYMBOL_GPL(uv_bios_enum_ports); | |
216 | ||
217 | extern s64 uv_bios_get_geoinfo(u64 nasid, u64 size, u64 *buf) | |
218 | { | |
219 | return uv_bios_call(UV_BIOS_GET_GEOINFO, nasid, (u64)buf, size, 0, 0); | |
220 | } | |
221 | EXPORT_SYMBOL_GPL(uv_bios_get_geoinfo); | |
222 | ||
223 | extern s64 uv_bios_get_pci_topology(u64 size, u64 *buf) | |
224 | { | |
225 | return uv_bios_call(UV_BIOS_GET_PCI_TOPOLOGY, (u64)buf, size, 0, 0, 0); | |
226 | } | |
227 | EXPORT_SYMBOL_GPL(uv_bios_get_pci_topology); | |
228 | ||
1e61f5a9 | 229 | unsigned long get_uv_systab_phys(bool msg) |
7f594232 | 230 | { |
ec7e1605 AB |
231 | if ((uv_systab_phys == EFI_INVALID_TABLE_ADDR) || |
232 | !uv_systab_phys || efi_runtime_disabled()) { | |
1e61f5a9 MT |
233 | if (msg) |
234 | pr_crit("UV: UVsystab: missing\n"); | |
235 | return 0; | |
7f594232 | 236 | } |
1e61f5a9 MT |
237 | return uv_systab_phys; |
238 | } | |
239 | ||
240 | int uv_bios_init(void) | |
241 | { | |
242 | unsigned long uv_systab_phys_addr; | |
243 | ||
244 | uv_systab = NULL; | |
245 | uv_systab_phys_addr = get_uv_systab_phys(1); | |
246 | if (!uv_systab_phys_addr) | |
247 | return -EEXIST; | |
7f594232 | 248 | |
1e61f5a9 | 249 | uv_systab = ioremap(uv_systab_phys_addr, sizeof(struct uv_systab)); |
ef93bf80 MT |
250 | if (!uv_systab || strncmp(uv_systab->signature, UV_SYSTAB_SIG, 4)) { |
251 | pr_err("UV: UVsystab: bad signature!\n"); | |
252 | iounmap(uv_systab); | |
9743cb68 | 253 | return -EINVAL; |
ef93bf80 | 254 | } |
7f594232 | 255 | |
e363d24c | 256 | /* Starting with UV4 the UV systab size is variable */ |
ef93bf80 | 257 | if (uv_systab->revision >= UV_SYSTAB_VERSION_UV4) { |
e363d24c MT |
258 | int size = uv_systab->size; |
259 | ||
ef93bf80 | 260 | iounmap(uv_systab); |
1e61f5a9 | 261 | uv_systab = ioremap(uv_systab_phys_addr, size); |
ef93bf80 | 262 | if (!uv_systab) { |
e363d24c | 263 | pr_err("UV: UVsystab: ioremap(%d) failed!\n", size); |
9743cb68 | 264 | return -EFAULT; |
ef93bf80 MT |
265 | } |
266 | } | |
267 | pr_info("UV: UVsystab: Revision:%x\n", uv_systab->revision); | |
9743cb68 | 268 | return 0; |
7f594232 | 269 | } |