diff options
Diffstat (limited to 'arch/ia64/kernel/sal.c')
-rw-r--r-- | arch/ia64/kernel/sal.c | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/arch/ia64/kernel/sal.c b/arch/ia64/kernel/sal.c new file mode 100644 index 000000000000..acc0f132f86c --- /dev/null +++ b/arch/ia64/kernel/sal.c | |||
@@ -0,0 +1,302 @@ | |||
1 | /* | ||
2 | * System Abstraction Layer (SAL) interface routines. | ||
3 | * | ||
4 | * Copyright (C) 1998, 1999, 2001, 2003 Hewlett-Packard Co | ||
5 | * David Mosberger-Tang <davidm@hpl.hp.com> | ||
6 | * Copyright (C) 1999 VA Linux Systems | ||
7 | * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> | ||
8 | */ | ||
9 | #include <linux/config.h> | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/spinlock.h> | ||
15 | #include <linux/string.h> | ||
16 | |||
17 | #include <asm/page.h> | ||
18 | #include <asm/sal.h> | ||
19 | #include <asm/pal.h> | ||
20 | |||
21 | __cacheline_aligned DEFINE_SPINLOCK(sal_lock); | ||
22 | unsigned long sal_platform_features; | ||
23 | |||
24 | unsigned short sal_revision; | ||
25 | unsigned short sal_version; | ||
26 | |||
27 | #define SAL_MAJOR(x) ((x) >> 8) | ||
28 | #define SAL_MINOR(x) ((x) & 0xff) | ||
29 | |||
30 | static struct { | ||
31 | void *addr; /* function entry point */ | ||
32 | void *gpval; /* gp value to use */ | ||
33 | } pdesc; | ||
34 | |||
35 | static long | ||
36 | default_handler (void) | ||
37 | { | ||
38 | return -1; | ||
39 | } | ||
40 | |||
41 | ia64_sal_handler ia64_sal = (ia64_sal_handler) default_handler; | ||
42 | ia64_sal_desc_ptc_t *ia64_ptc_domain_info; | ||
43 | |||
44 | const char * | ||
45 | ia64_sal_strerror (long status) | ||
46 | { | ||
47 | const char *str; | ||
48 | switch (status) { | ||
49 | case 0: str = "Call completed without error"; break; | ||
50 | case 1: str = "Effect a warm boot of the system to complete " | ||
51 | "the update"; break; | ||
52 | case -1: str = "Not implemented"; break; | ||
53 | case -2: str = "Invalid argument"; break; | ||
54 | case -3: str = "Call completed with error"; break; | ||
55 | case -4: str = "Virtual address not registered"; break; | ||
56 | case -5: str = "No information available"; break; | ||
57 | case -6: str = "Insufficient space to add the entry"; break; | ||
58 | case -7: str = "Invalid entry_addr value"; break; | ||
59 | case -8: str = "Invalid interrupt vector"; break; | ||
60 | case -9: str = "Requested memory not available"; break; | ||
61 | case -10: str = "Unable to write to the NVM device"; break; | ||
62 | case -11: str = "Invalid partition type specified"; break; | ||
63 | case -12: str = "Invalid NVM_Object id specified"; break; | ||
64 | case -13: str = "NVM_Object already has the maximum number " | ||
65 | "of partitions"; break; | ||
66 | case -14: str = "Insufficient space in partition for the " | ||
67 | "requested write sub-function"; break; | ||
68 | case -15: str = "Insufficient data buffer space for the " | ||
69 | "requested read record sub-function"; break; | ||
70 | case -16: str = "Scratch buffer required for the write/delete " | ||
71 | "sub-function"; break; | ||
72 | case -17: str = "Insufficient space in the NVM_Object for the " | ||
73 | "requested create sub-function"; break; | ||
74 | case -18: str = "Invalid value specified in the partition_rec " | ||
75 | "argument"; break; | ||
76 | case -19: str = "Record oriented I/O not supported for this " | ||
77 | "partition"; break; | ||
78 | case -20: str = "Bad format of record to be written or " | ||
79 | "required keyword variable not " | ||
80 | "specified"; break; | ||
81 | default: str = "Unknown SAL status code"; break; | ||
82 | } | ||
83 | return str; | ||
84 | } | ||
85 | |||
86 | void __init | ||
87 | ia64_sal_handler_init (void *entry_point, void *gpval) | ||
88 | { | ||
89 | /* fill in the SAL procedure descriptor and point ia64_sal to it: */ | ||
90 | pdesc.addr = entry_point; | ||
91 | pdesc.gpval = gpval; | ||
92 | ia64_sal = (ia64_sal_handler) &pdesc; | ||
93 | } | ||
94 | |||
95 | static void __init | ||
96 | check_versions (struct ia64_sal_systab *systab) | ||
97 | { | ||
98 | sal_revision = (systab->sal_rev_major << 8) | systab->sal_rev_minor; | ||
99 | sal_version = (systab->sal_b_rev_major << 8) | systab->sal_b_rev_minor; | ||
100 | |||
101 | /* Check for broken firmware */ | ||
102 | if ((sal_revision == SAL_VERSION_CODE(49, 29)) | ||
103 | && (sal_version == SAL_VERSION_CODE(49, 29))) | ||
104 | { | ||
105 | /* | ||
106 | * Old firmware for zx2000 prototypes have this weird version number, | ||
107 | * reset it to something sane. | ||
108 | */ | ||
109 | sal_revision = SAL_VERSION_CODE(2, 8); | ||
110 | sal_version = SAL_VERSION_CODE(0, 0); | ||
111 | } | ||
112 | } | ||
113 | |||
114 | static void __init | ||
115 | sal_desc_entry_point (void *p) | ||
116 | { | ||
117 | struct ia64_sal_desc_entry_point *ep = p; | ||
118 | ia64_pal_handler_init(__va(ep->pal_proc)); | ||
119 | ia64_sal_handler_init(__va(ep->sal_proc), __va(ep->gp)); | ||
120 | } | ||
121 | |||
122 | #ifdef CONFIG_SMP | ||
123 | static void __init | ||
124 | set_smp_redirect (int flag) | ||
125 | { | ||
126 | #ifndef CONFIG_HOTPLUG_CPU | ||
127 | if (no_int_routing) | ||
128 | smp_int_redirect &= ~flag; | ||
129 | else | ||
130 | smp_int_redirect |= flag; | ||
131 | #else | ||
132 | /* | ||
133 | * For CPU Hotplug we dont want to do any chipset supported | ||
134 | * interrupt redirection. The reason is this would require that | ||
135 | * All interrupts be stopped and hard bind the irq to a cpu. | ||
136 | * Later when the interrupt is fired we need to set the redir hint | ||
137 | * on again in the vector. This is combersome for something that the | ||
138 | * user mode irq balancer will solve anyways. | ||
139 | */ | ||
140 | no_int_routing=1; | ||
141 | smp_int_redirect &= ~flag; | ||
142 | #endif | ||
143 | } | ||
144 | #else | ||
145 | #define set_smp_redirect(flag) do { } while (0) | ||
146 | #endif | ||
147 | |||
148 | static void __init | ||
149 | sal_desc_platform_feature (void *p) | ||
150 | { | ||
151 | struct ia64_sal_desc_platform_feature *pf = p; | ||
152 | sal_platform_features = pf->feature_mask; | ||
153 | |||
154 | printk(KERN_INFO "SAL Platform features:"); | ||
155 | if (!sal_platform_features) { | ||
156 | printk(" None\n"); | ||
157 | return; | ||
158 | } | ||
159 | |||
160 | if (sal_platform_features & IA64_SAL_PLATFORM_FEATURE_BUS_LOCK) | ||
161 | printk(" BusLock"); | ||
162 | if (sal_platform_features & IA64_SAL_PLATFORM_FEATURE_IRQ_REDIR_HINT) { | ||
163 | printk(" IRQ_Redirection"); | ||
164 | set_smp_redirect(SMP_IRQ_REDIRECTION); | ||
165 | } | ||
166 | if (sal_platform_features & IA64_SAL_PLATFORM_FEATURE_IPI_REDIR_HINT) { | ||
167 | printk(" IPI_Redirection"); | ||
168 | set_smp_redirect(SMP_IPI_REDIRECTION); | ||
169 | } | ||
170 | if (sal_platform_features & IA64_SAL_PLATFORM_FEATURE_ITC_DRIFT) | ||
171 | printk(" ITC_Drift"); | ||
172 | printk("\n"); | ||
173 | } | ||
174 | |||
175 | #ifdef CONFIG_SMP | ||
176 | static void __init | ||
177 | sal_desc_ap_wakeup (void *p) | ||
178 | { | ||
179 | struct ia64_sal_desc_ap_wakeup *ap = p; | ||
180 | |||
181 | switch (ap->mechanism) { | ||
182 | case IA64_SAL_AP_EXTERNAL_INT: | ||
183 | ap_wakeup_vector = ap->vector; | ||
184 | printk(KERN_INFO "SAL: AP wakeup using external interrupt " | ||
185 | "vector 0x%lx\n", ap_wakeup_vector); | ||
186 | break; | ||
187 | default: | ||
188 | printk(KERN_ERR "SAL: AP wakeup mechanism unsupported!\n"); | ||
189 | break; | ||
190 | } | ||
191 | } | ||
192 | |||
193 | static void __init | ||
194 | chk_nointroute_opt(void) | ||
195 | { | ||
196 | char *cp; | ||
197 | extern char saved_command_line[]; | ||
198 | |||
199 | for (cp = saved_command_line; *cp; ) { | ||
200 | if (memcmp(cp, "nointroute", 10) == 0) { | ||
201 | no_int_routing = 1; | ||
202 | printk ("no_int_routing on\n"); | ||
203 | break; | ||
204 | } else { | ||
205 | while (*cp != ' ' && *cp) | ||
206 | ++cp; | ||
207 | while (*cp == ' ') | ||
208 | ++cp; | ||
209 | } | ||
210 | } | ||
211 | } | ||
212 | |||
213 | #else | ||
214 | static void __init sal_desc_ap_wakeup(void *p) { } | ||
215 | #endif | ||
216 | |||
217 | void __init | ||
218 | ia64_sal_init (struct ia64_sal_systab *systab) | ||
219 | { | ||
220 | char *p; | ||
221 | int i; | ||
222 | |||
223 | if (!systab) { | ||
224 | printk(KERN_WARNING "Hmm, no SAL System Table.\n"); | ||
225 | return; | ||
226 | } | ||
227 | |||
228 | if (strncmp(systab->signature, "SST_", 4) != 0) | ||
229 | printk(KERN_ERR "bad signature in system table!"); | ||
230 | |||
231 | check_versions(systab); | ||
232 | #ifdef CONFIG_SMP | ||
233 | chk_nointroute_opt(); | ||
234 | #endif | ||
235 | |||
236 | /* revisions are coded in BCD, so %x does the job for us */ | ||
237 | printk(KERN_INFO "SAL %x.%x: %.32s %.32s%sversion %x.%x\n", | ||
238 | SAL_MAJOR(sal_revision), SAL_MINOR(sal_revision), | ||
239 | systab->oem_id, systab->product_id, | ||
240 | systab->product_id[0] ? " " : "", | ||
241 | SAL_MAJOR(sal_version), SAL_MINOR(sal_version)); | ||
242 | |||
243 | p = (char *) (systab + 1); | ||
244 | for (i = 0; i < systab->entry_count; i++) { | ||
245 | /* | ||
246 | * The first byte of each entry type contains the type | ||
247 | * descriptor. | ||
248 | */ | ||
249 | switch (*p) { | ||
250 | case SAL_DESC_ENTRY_POINT: | ||
251 | sal_desc_entry_point(p); | ||
252 | break; | ||
253 | case SAL_DESC_PLATFORM_FEATURE: | ||
254 | sal_desc_platform_feature(p); | ||
255 | break; | ||
256 | case SAL_DESC_PTC: | ||
257 | ia64_ptc_domain_info = (ia64_sal_desc_ptc_t *)p; | ||
258 | break; | ||
259 | case SAL_DESC_AP_WAKEUP: | ||
260 | sal_desc_ap_wakeup(p); | ||
261 | break; | ||
262 | } | ||
263 | p += SAL_DESC_SIZE(*p); | ||
264 | } | ||
265 | } | ||
266 | |||
267 | int | ||
268 | ia64_sal_oemcall(struct ia64_sal_retval *isrvp, u64 oemfunc, u64 arg1, | ||
269 | u64 arg2, u64 arg3, u64 arg4, u64 arg5, u64 arg6, u64 arg7) | ||
270 | { | ||
271 | if (oemfunc < IA64_SAL_OEMFUNC_MIN || oemfunc > IA64_SAL_OEMFUNC_MAX) | ||
272 | return -1; | ||
273 | SAL_CALL(*isrvp, oemfunc, arg1, arg2, arg3, arg4, arg5, arg6, arg7); | ||
274 | return 0; | ||
275 | } | ||
276 | EXPORT_SYMBOL(ia64_sal_oemcall); | ||
277 | |||
278 | int | ||
279 | ia64_sal_oemcall_nolock(struct ia64_sal_retval *isrvp, u64 oemfunc, u64 arg1, | ||
280 | u64 arg2, u64 arg3, u64 arg4, u64 arg5, u64 arg6, | ||
281 | u64 arg7) | ||
282 | { | ||
283 | if (oemfunc < IA64_SAL_OEMFUNC_MIN || oemfunc > IA64_SAL_OEMFUNC_MAX) | ||
284 | return -1; | ||
285 | SAL_CALL_NOLOCK(*isrvp, oemfunc, arg1, arg2, arg3, arg4, arg5, arg6, | ||
286 | arg7); | ||
287 | return 0; | ||
288 | } | ||
289 | EXPORT_SYMBOL(ia64_sal_oemcall_nolock); | ||
290 | |||
291 | int | ||
292 | ia64_sal_oemcall_reentrant(struct ia64_sal_retval *isrvp, u64 oemfunc, | ||
293 | u64 arg1, u64 arg2, u64 arg3, u64 arg4, u64 arg5, | ||
294 | u64 arg6, u64 arg7) | ||
295 | { | ||
296 | if (oemfunc < IA64_SAL_OEMFUNC_MIN || oemfunc > IA64_SAL_OEMFUNC_MAX) | ||
297 | return -1; | ||
298 | SAL_CALL_REENTRANT(*isrvp, oemfunc, arg1, arg2, arg3, arg4, arg5, arg6, | ||
299 | arg7); | ||
300 | return 0; | ||
301 | } | ||
302 | EXPORT_SYMBOL(ia64_sal_oemcall_reentrant); | ||