blob: bed10dddf099a8e2a670f8c8a899cf2f6b979119 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Written by: Garry Forsgren, Unisys Corporation
3 * Natalie Protasevich, Unisys Corporation
4 * This file contains the code to configure and interface
5 * with Unisys ES7000 series hardware system manager.
6 *
7 * Copyright (c) 2003 Unisys Corporation. All Rights Reserved.
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of version 2 of the GNU General Public License as
11 * published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it would be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write the Free Software Foundation, Inc., 59
19 * Temple Place - Suite 330, Boston MA 02111-1307, USA.
20 *
21 * Contact information: Unisys Corporation, Township Line & Union Meeting
22 * Roads-A, Unisys Way, Blue Bell, Pennsylvania, 19424, or:
23 *
24 * http://www.unisys.com
25 */
26
27#include <linux/module.h>
28#include <linux/types.h>
29#include <linux/kernel.h>
30#include <linux/smp.h>
31#include <linux/string.h>
32#include <linux/spinlock.h>
33#include <linux/errno.h>
34#include <linux/notifier.h>
35#include <linux/reboot.h>
36#include <linux/init.h>
37#include <linux/acpi.h>
38#include <asm/io.h>
39#include <asm/nmi.h>
40#include <asm/smp.h>
41#include <asm/apicdef.h>
Vivek Goyal071922c2007-07-06 02:39:55 -070042#include <mach_mpparse.h>
Yinghai Lu569712b2008-11-16 03:12:49 -080043#include <asm/setup.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044
45/*
Yinghai Lu16253242008-08-27 23:01:16 -070046 * ES7000 chipsets
47 */
48
49#define NON_UNISYS 0
50#define ES7000_CLASSIC 1
51#define ES7000_ZORRO 2
52
53
54#define MIP_REG 1
55#define MIP_PSAI_REG 4
56
57#define MIP_BUSY 1
58#define MIP_SPIN 0xf0000
59#define MIP_VALID 0x0100000000000000ULL
60#define MIP_PORT(VALUE) ((VALUE >> 32) & 0xffff)
61
62#define MIP_RD_LO(VALUE) (VALUE & 0xffffffff)
63
64struct mip_reg_info {
65 unsigned long long mip_info;
66 unsigned long long delivery_info;
67 unsigned long long host_reg;
68 unsigned long long mip_reg;
69};
70
71struct part_info {
72 unsigned char type;
73 unsigned char length;
74 unsigned char part_id;
75 unsigned char apic_mode;
76 unsigned long snum;
77 char ptype[16];
78 char sname[64];
79 char pname[64];
80};
81
82struct psai {
83 unsigned long long entry_type;
84 unsigned long long addr;
85 unsigned long long bep_addr;
86};
87
88struct es7000_mem_info {
89 unsigned char type;
90 unsigned char length;
91 unsigned char resv[6];
92 unsigned long long start;
93 unsigned long long size;
94};
95
96struct es7000_oem_table {
97 unsigned long long hdr;
98 struct mip_reg_info mip;
99 struct part_info pif;
100 struct es7000_mem_info shm;
101 struct psai psai;
102};
103
104#ifdef CONFIG_ACPI
105
106struct oem_table {
107 struct acpi_table_header Header;
108 u32 OEMTableAddr;
109 u32 OEMTableSize;
110};
111
112extern int find_unisys_acpi_oem_table(unsigned long *oem_addr);
Yinghai Lua73aaed2008-09-14 02:33:14 -0700113extern void unmap_unisys_acpi_oem_table(unsigned long oem_addr);
Yinghai Lu16253242008-08-27 23:01:16 -0700114#endif
115
116struct mip_reg {
117 unsigned long long off_0;
118 unsigned long long off_8;
119 unsigned long long off_10;
120 unsigned long long off_18;
121 unsigned long long off_20;
122 unsigned long long off_28;
123 unsigned long long off_30;
124 unsigned long long off_38;
125};
126
127#define MIP_SW_APIC 0x1020b
128#define MIP_FUNC(VALUE) (VALUE & 0xff)
129
130/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 * ES7000 Globals
132 */
133
Adrian Bunkfb7ae262007-10-17 18:04:37 +0200134static volatile unsigned long *psai = NULL;
135static struct mip_reg *mip_reg;
136static struct mip_reg *host_reg;
137static int mip_port;
138static unsigned long mip_addr, host_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139
Alexey Starikovskiy32c50612008-05-14 19:02:51 +0400140int es7000_plat;
141
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142/*
143 * GSI override for ES7000 platforms.
144 */
145
146static unsigned int base;
147
148static int
149es7000_rename_gsi(int ioapic, int gsi)
150{
Natalie.Protasevich@unisys.com93383162005-10-30 14:59:38 -0800151 if (es7000_plat == ES7000_ZORRO)
152 return gsi;
153
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 if (!base) {
155 int i;
156 for (i = 0; i < nr_ioapics; i++)
157 base += nr_ioapic_registers[i];
158 }
159
Yinghai Luc7e79642008-07-25 02:17:33 -0700160 if (!ioapic && (gsi < 16))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 gsi += base;
162 return gsi;
163}
164
Yinghai Lu569712b2008-11-16 03:12:49 -0800165#ifdef CONFIG_ES7000_CLUSTERED_APIC
166static int wakeup_secondary_cpu_via_mip(int cpu, unsigned long eip)
167{
168 unsigned long vect = 0, psaival = 0;
169
170 if (psai == NULL)
171 return -1;
172
173 vect = ((unsigned long)__pa(eip)/0x1000) << 16;
174 psaival = (0x1000000 | vect | cpu);
175
176 while (*psai & 0x1000000)
177 ;
178
179 *psai = psaival;
180
181 return 0;
182}
183#endif
184
Natalie.Protasevich@unisys.com56f1d5d2005-09-03 15:56:34 -0700185void __init
Natalie.Protasevich@unisys.com93383162005-10-30 14:59:38 -0800186setup_unisys(void)
Natalie.Protasevich@unisys.com56f1d5d2005-09-03 15:56:34 -0700187{
188 /*
189 * Determine the generation of the ES7000 currently running.
190 *
191 * es7000_plat = 1 if the machine is a 5xx ES7000 box
192 * es7000_plat = 2 if the machine is a x86_64 ES7000 box
193 *
194 */
195 if (!(boot_cpu_data.x86 <= 15 && boot_cpu_data.x86_model <= 2))
Natalie.Protasevich@unisys.com93383162005-10-30 14:59:38 -0800196 es7000_plat = ES7000_ZORRO;
Natalie.Protasevich@unisys.com56f1d5d2005-09-03 15:56:34 -0700197 else
Natalie.Protasevich@unisys.com93383162005-10-30 14:59:38 -0800198 es7000_plat = ES7000_CLASSIC;
Natalie.Protasevich@unisys.com56f1d5d2005-09-03 15:56:34 -0700199 ioapic_renumber_irq = es7000_rename_gsi;
Yinghai Lu569712b2008-11-16 03:12:49 -0800200#ifdef CONFIG_ES7000_CLUSTERED_APIC
201 x86_quirks->wakeup_secondary_cpu = wakeup_secondary_cpu_via_mip;
202#endif
Natalie.Protasevich@unisys.com56f1d5d2005-09-03 15:56:34 -0700203}
204
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205/*
206 * Parse the OEM Table
207 */
208
209int __init
Natalie.Protasevich@unisys.com56f1d5d2005-09-03 15:56:34 -0700210parse_unisys_oem (char *oemptr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211{
212 int i;
213 int success = 0;
214 unsigned char type, size;
215 unsigned long val;
216 char *tp = NULL;
217 struct psai *psaip = NULL;
218 struct mip_reg_info *mi;
219 struct mip_reg *host, *mip;
220
221 tp = oemptr;
222
223 tp += 8;
224
Natalie.Protasevich@unisys.com56f1d5d2005-09-03 15:56:34 -0700225 for (i=0; i <= 6; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 type = *tp++;
227 size = *tp++;
228 tp -= 2;
229 switch (type) {
230 case MIP_REG:
231 mi = (struct mip_reg_info *)tp;
232 val = MIP_RD_LO(mi->host_reg);
233 host_addr = val;
234 host = (struct mip_reg *)val;
235 host_reg = __va(host);
236 val = MIP_RD_LO(mi->mip_reg);
237 mip_port = MIP_PORT(mi->mip_info);
238 mip_addr = val;
239 mip = (struct mip_reg *)val;
240 mip_reg = __va(mip);
Thomas Gleixner5171c302008-07-21 21:58:34 +0200241 pr_debug("es7000_mipcfg: host_reg = 0x%lx \n",
242 (unsigned long)host_reg);
243 pr_debug("es7000_mipcfg: mip_reg = 0x%lx \n",
244 (unsigned long)mip_reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 success++;
246 break;
247 case MIP_PSAI_REG:
248 psaip = (struct psai *)tp;
249 if (tp != NULL) {
250 if (psaip->addr)
251 psai = __va(psaip->addr);
252 else
253 psai = NULL;
254 success++;
255 }
256 break;
257 default:
258 break;
259 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 tp += size;
261 }
262
263 if (success < 2) {
Natalie.Protasevich@unisys.com93383162005-10-30 14:59:38 -0800264 es7000_plat = NON_UNISYS;
Natalie.Protasevich@unisys.com56f1d5d2005-09-03 15:56:34 -0700265 } else
266 setup_unisys();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 return es7000_plat;
268}
269
Natalie.Protasevich@unisys.come5428ed2006-03-23 02:59:36 -0800270#ifdef CONFIG_ACPI
Yinghai Lua73aaed2008-09-14 02:33:14 -0700271static unsigned long oem_addrX;
272static unsigned long oem_size;
273int __init find_unisys_acpi_oem_table(unsigned long *oem_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274{
Alexey Starikovskiyceb6c462007-02-02 19:48:22 +0300275 struct acpi_table_header *header = NULL;
276 int i = 0;
Yinghai Lua73aaed2008-09-14 02:33:14 -0700277 acpi_size tbl_size;
278
279 while (ACPI_SUCCESS(acpi_get_table_with_size("OEM1", i++, &header, &tbl_size))) {
Alexey Starikovskiyceb6c462007-02-02 19:48:22 +0300280 if (!memcmp((char *) &header->oem_id, "UNISYS", 6)) {
281 struct oem_table *t = (struct oem_table *)header;
Yinghai Lua73aaed2008-09-14 02:33:14 -0700282
283 oem_addrX = t->OEMTableAddr;
284 oem_size = t->OEMTableSize;
285 early_acpi_os_unmap_memory(header, tbl_size);
286
287 *oem_addr = (unsigned long)__acpi_map_table(oem_addrX,
288 oem_size);
Alexey Starikovskiyceb6c462007-02-02 19:48:22 +0300289 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 }
Yinghai Lua73aaed2008-09-14 02:33:14 -0700291 early_acpi_os_unmap_memory(header, tbl_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 return -1;
294}
Yinghai Lua73aaed2008-09-14 02:33:14 -0700295
296void __init unmap_unisys_acpi_oem_table(unsigned long oem_addr)
297{
298 if (!oem_addr)
299 return;
300
301 __acpi_unmap_table((char *)oem_addr, oem_size);
302}
Natalie.Protasevich@unisys.come5428ed2006-03-23 02:59:36 -0800303#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305static void
306es7000_spin(int n)
307{
308 int i = 0;
309
310 while (i++ < n)
311 rep_nop();
312}
313
314static int __init
315es7000_mip_write(struct mip_reg *mip_reg)
316{
317 int status = 0;
318 int spin;
319
320 spin = MIP_SPIN;
321 while (((unsigned long long)host_reg->off_38 &
322 (unsigned long long)MIP_VALID) != 0) {
323 if (--spin <= 0) {
324 printk("es7000_mip_write: Timeout waiting for Host Valid Flag");
325 return -1;
326 }
327 es7000_spin(MIP_SPIN);
328 }
329
330 memcpy(host_reg, mip_reg, sizeof(struct mip_reg));
331 outb(1, mip_port);
332
333 spin = MIP_SPIN;
334
335 while (((unsigned long long)mip_reg->off_38 &
336 (unsigned long long)MIP_VALID) == 0) {
337 if (--spin <= 0) {
338 printk("es7000_mip_write: Timeout waiting for MIP Valid Flag");
339 return -1;
340 }
341 es7000_spin(MIP_SPIN);
342 }
343
344 status = ((unsigned long long)mip_reg->off_0 &
345 (unsigned long long)0xffff0000000000ULL) >> 48;
346 mip_reg->off_38 = ((unsigned long long)mip_reg->off_38 &
347 (unsigned long long)~MIP_VALID);
348 return status;
349}
350
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351void __init
Adrian Bunkfb7ae262007-10-17 18:04:37 +0200352es7000_sw_apic(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353{
354 if (es7000_plat) {
355 int mip_status;
356 struct mip_reg es7000_mip_reg;
357
358 printk("ES7000: Enabling APIC mode.\n");
359 memset(&es7000_mip_reg, 0, sizeof(struct mip_reg));
360 es7000_mip_reg.off_0 = MIP_SW_APIC;
361 es7000_mip_reg.off_38 = (MIP_VALID);
362 while ((mip_status = es7000_mip_write(&es7000_mip_reg)) != 0)
363 printk("es7000_sw_apic: command failed, status = %x\n",
364 mip_status);
365 return;
366 }
367}