blob: 53699c931ad41ebdaee8594cdb17f4464fcc986f [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>
Yinghai Lub5fe3632008-11-18 08:14:14 -080041#include <asm/atomic.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include <asm/apicdef.h>
Vivek Goyal071922c2007-07-06 02:39:55 -070043#include <mach_mpparse.h>
Yinghai Lu54ac14a2008-11-17 15:19:53 -080044#include <asm/genapic.h>
Yinghai Lu569712b2008-11-16 03:12:49 -080045#include <asm/setup.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070046
47/*
Yinghai Lu16253242008-08-27 23:01:16 -070048 * ES7000 chipsets
49 */
50
51#define NON_UNISYS 0
52#define ES7000_CLASSIC 1
53#define ES7000_ZORRO 2
54
55
56#define MIP_REG 1
57#define MIP_PSAI_REG 4
58
59#define MIP_BUSY 1
60#define MIP_SPIN 0xf0000
61#define MIP_VALID 0x0100000000000000ULL
62#define MIP_PORT(VALUE) ((VALUE >> 32) & 0xffff)
63
64#define MIP_RD_LO(VALUE) (VALUE & 0xffffffff)
65
66struct mip_reg_info {
67 unsigned long long mip_info;
68 unsigned long long delivery_info;
69 unsigned long long host_reg;
70 unsigned long long mip_reg;
71};
72
73struct part_info {
74 unsigned char type;
75 unsigned char length;
76 unsigned char part_id;
77 unsigned char apic_mode;
78 unsigned long snum;
79 char ptype[16];
80 char sname[64];
81 char pname[64];
82};
83
84struct psai {
85 unsigned long long entry_type;
86 unsigned long long addr;
87 unsigned long long bep_addr;
88};
89
90struct es7000_mem_info {
91 unsigned char type;
92 unsigned char length;
93 unsigned char resv[6];
94 unsigned long long start;
95 unsigned long long size;
96};
97
98struct es7000_oem_table {
99 unsigned long long hdr;
100 struct mip_reg_info mip;
101 struct part_info pif;
102 struct es7000_mem_info shm;
103 struct psai psai;
104};
105
106#ifdef CONFIG_ACPI
107
108struct oem_table {
109 struct acpi_table_header Header;
110 u32 OEMTableAddr;
111 u32 OEMTableSize;
112};
113
114extern int find_unisys_acpi_oem_table(unsigned long *oem_addr);
Yinghai Lua73aaed2008-09-14 02:33:14 -0700115extern void unmap_unisys_acpi_oem_table(unsigned long oem_addr);
Yinghai Lu16253242008-08-27 23:01:16 -0700116#endif
117
118struct mip_reg {
119 unsigned long long off_0;
120 unsigned long long off_8;
121 unsigned long long off_10;
122 unsigned long long off_18;
123 unsigned long long off_20;
124 unsigned long long off_28;
125 unsigned long long off_30;
126 unsigned long long off_38;
127};
128
129#define MIP_SW_APIC 0x1020b
130#define MIP_FUNC(VALUE) (VALUE & 0xff)
131
132/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 * ES7000 Globals
134 */
135
Adrian Bunkfb7ae262007-10-17 18:04:37 +0200136static volatile unsigned long *psai = NULL;
137static struct mip_reg *mip_reg;
138static struct mip_reg *host_reg;
139static int mip_port;
140static unsigned long mip_addr, host_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141
Alexey Starikovskiy32c50612008-05-14 19:02:51 +0400142int es7000_plat;
143
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144/*
145 * GSI override for ES7000 platforms.
146 */
147
148static unsigned int base;
149
150static int
151es7000_rename_gsi(int ioapic, int gsi)
152{
Natalie.Protasevich@unisys.com93383162005-10-30 14:59:38 -0800153 if (es7000_plat == ES7000_ZORRO)
154 return gsi;
155
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 if (!base) {
157 int i;
158 for (i = 0; i < nr_ioapics; i++)
159 base += nr_ioapic_registers[i];
160 }
161
Yinghai Luc7e79642008-07-25 02:17:33 -0700162 if (!ioapic && (gsi < 16))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 gsi += base;
164 return gsi;
165}
166
Yinghai Lu569712b2008-11-16 03:12:49 -0800167static int wakeup_secondary_cpu_via_mip(int cpu, unsigned long eip)
168{
169 unsigned long vect = 0, psaival = 0;
170
171 if (psai == NULL)
172 return -1;
173
174 vect = ((unsigned long)__pa(eip)/0x1000) << 16;
175 psaival = (0x1000000 | vect | cpu);
176
177 while (*psai & 0x1000000)
178 ;
179
180 *psai = psaival;
181
182 return 0;
183}
Yinghai Lu54ac14a2008-11-17 15:19:53 -0800184
Yinghai Lub5fe3632008-11-18 08:14:14 -0800185static void noop_wait_for_deassert(atomic_t *deassert_not_used)
186{
187}
188
Yinghai Lu54ac14a2008-11-17 15:19:53 -0800189static int __init es7000_update_genapic(void)
190{
191 genapic->wakeup_cpu = wakeup_secondary_cpu_via_mip;
192
Yinghai Lub5fe3632008-11-18 08:14:14 -0800193 /* MPENTIUMIII */
194 if (boot_cpu_data.x86 == 6 &&
195 (boot_cpu_data.x86_model >= 7 || boot_cpu_data.x86_model <= 11)) {
196 es7000_update_genapic_to_cluster();
197 genapic->wait_for_init_deassert = noop_wait_for_deassert;
198 genapic->wakeup_cpu = wakeup_secondary_cpu_via_mip;
199 }
200
Yinghai Lu54ac14a2008-11-17 15:19:53 -0800201 return 0;
202}
Yinghai Lu569712b2008-11-16 03:12:49 -0800203
Natalie.Protasevich@unisys.com56f1d5d2005-09-03 15:56:34 -0700204void __init
Natalie.Protasevich@unisys.com93383162005-10-30 14:59:38 -0800205setup_unisys(void)
Natalie.Protasevich@unisys.com56f1d5d2005-09-03 15:56:34 -0700206{
207 /*
208 * Determine the generation of the ES7000 currently running.
209 *
210 * es7000_plat = 1 if the machine is a 5xx ES7000 box
211 * es7000_plat = 2 if the machine is a x86_64 ES7000 box
212 *
213 */
214 if (!(boot_cpu_data.x86 <= 15 && boot_cpu_data.x86_model <= 2))
Natalie.Protasevich@unisys.com93383162005-10-30 14:59:38 -0800215 es7000_plat = ES7000_ZORRO;
Natalie.Protasevich@unisys.com56f1d5d2005-09-03 15:56:34 -0700216 else
Natalie.Protasevich@unisys.com93383162005-10-30 14:59:38 -0800217 es7000_plat = ES7000_CLASSIC;
Natalie.Protasevich@unisys.com56f1d5d2005-09-03 15:56:34 -0700218 ioapic_renumber_irq = es7000_rename_gsi;
Yinghai Lu54ac14a2008-11-17 15:19:53 -0800219
Yinghai Lu54ac14a2008-11-17 15:19:53 -0800220 x86_quirks->update_genapic = es7000_update_genapic;
Natalie.Protasevich@unisys.com56f1d5d2005-09-03 15:56:34 -0700221}
222
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223/*
224 * Parse the OEM Table
225 */
226
227int __init
Natalie.Protasevich@unisys.com56f1d5d2005-09-03 15:56:34 -0700228parse_unisys_oem (char *oemptr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229{
230 int i;
231 int success = 0;
232 unsigned char type, size;
233 unsigned long val;
234 char *tp = NULL;
235 struct psai *psaip = NULL;
236 struct mip_reg_info *mi;
237 struct mip_reg *host, *mip;
238
239 tp = oemptr;
240
241 tp += 8;
242
Natalie.Protasevich@unisys.com56f1d5d2005-09-03 15:56:34 -0700243 for (i=0; i <= 6; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 type = *tp++;
245 size = *tp++;
246 tp -= 2;
247 switch (type) {
248 case MIP_REG:
249 mi = (struct mip_reg_info *)tp;
250 val = MIP_RD_LO(mi->host_reg);
251 host_addr = val;
252 host = (struct mip_reg *)val;
253 host_reg = __va(host);
254 val = MIP_RD_LO(mi->mip_reg);
255 mip_port = MIP_PORT(mi->mip_info);
256 mip_addr = val;
257 mip = (struct mip_reg *)val;
258 mip_reg = __va(mip);
Thomas Gleixner5171c302008-07-21 21:58:34 +0200259 pr_debug("es7000_mipcfg: host_reg = 0x%lx \n",
260 (unsigned long)host_reg);
261 pr_debug("es7000_mipcfg: mip_reg = 0x%lx \n",
262 (unsigned long)mip_reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 success++;
264 break;
265 case MIP_PSAI_REG:
266 psaip = (struct psai *)tp;
267 if (tp != NULL) {
268 if (psaip->addr)
269 psai = __va(psaip->addr);
270 else
271 psai = NULL;
272 success++;
273 }
274 break;
275 default:
276 break;
277 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 tp += size;
279 }
280
281 if (success < 2) {
Natalie.Protasevich@unisys.com93383162005-10-30 14:59:38 -0800282 es7000_plat = NON_UNISYS;
Natalie.Protasevich@unisys.com56f1d5d2005-09-03 15:56:34 -0700283 } else
284 setup_unisys();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 return es7000_plat;
286}
287
Natalie.Protasevich@unisys.come5428ed2006-03-23 02:59:36 -0800288#ifdef CONFIG_ACPI
Yinghai Lua73aaed2008-09-14 02:33:14 -0700289static unsigned long oem_addrX;
290static unsigned long oem_size;
291int __init find_unisys_acpi_oem_table(unsigned long *oem_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292{
Alexey Starikovskiyceb6c462007-02-02 19:48:22 +0300293 struct acpi_table_header *header = NULL;
294 int i = 0;
Yinghai Lua73aaed2008-09-14 02:33:14 -0700295
Yinghai Lud3c6aa12008-11-16 00:49:31 -0800296 while (ACPI_SUCCESS(acpi_get_table("OEM1", i++, &header))) {
Alexey Starikovskiyceb6c462007-02-02 19:48:22 +0300297 if (!memcmp((char *) &header->oem_id, "UNISYS", 6)) {
298 struct oem_table *t = (struct oem_table *)header;
Yinghai Lua73aaed2008-09-14 02:33:14 -0700299
300 oem_addrX = t->OEMTableAddr;
301 oem_size = t->OEMTableSize;
Yinghai Lua73aaed2008-09-14 02:33:14 -0700302
303 *oem_addr = (unsigned long)__acpi_map_table(oem_addrX,
304 oem_size);
Alexey Starikovskiyceb6c462007-02-02 19:48:22 +0300305 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 }
307 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 return -1;
309}
Yinghai Lua73aaed2008-09-14 02:33:14 -0700310
311void __init unmap_unisys_acpi_oem_table(unsigned long oem_addr)
312{
Yinghai Lua73aaed2008-09-14 02:33:14 -0700313}
Natalie.Protasevich@unisys.come5428ed2006-03-23 02:59:36 -0800314#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316static void
317es7000_spin(int n)
318{
319 int i = 0;
320
321 while (i++ < n)
322 rep_nop();
323}
324
325static int __init
326es7000_mip_write(struct mip_reg *mip_reg)
327{
328 int status = 0;
329 int spin;
330
331 spin = MIP_SPIN;
332 while (((unsigned long long)host_reg->off_38 &
333 (unsigned long long)MIP_VALID) != 0) {
334 if (--spin <= 0) {
335 printk("es7000_mip_write: Timeout waiting for Host Valid Flag");
336 return -1;
337 }
338 es7000_spin(MIP_SPIN);
339 }
340
341 memcpy(host_reg, mip_reg, sizeof(struct mip_reg));
342 outb(1, mip_port);
343
344 spin = MIP_SPIN;
345
346 while (((unsigned long long)mip_reg->off_38 &
347 (unsigned long long)MIP_VALID) == 0) {
348 if (--spin <= 0) {
349 printk("es7000_mip_write: Timeout waiting for MIP Valid Flag");
350 return -1;
351 }
352 es7000_spin(MIP_SPIN);
353 }
354
355 status = ((unsigned long long)mip_reg->off_0 &
356 (unsigned long long)0xffff0000000000ULL) >> 48;
357 mip_reg->off_38 = ((unsigned long long)mip_reg->off_38 &
358 (unsigned long long)~MIP_VALID);
359 return status;
360}
361
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362void __init
Adrian Bunkfb7ae262007-10-17 18:04:37 +0200363es7000_sw_apic(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364{
365 if (es7000_plat) {
366 int mip_status;
367 struct mip_reg es7000_mip_reg;
368
369 printk("ES7000: Enabling APIC mode.\n");
370 memset(&es7000_mip_reg, 0, sizeof(struct mip_reg));
371 es7000_mip_reg.off_0 = MIP_SW_APIC;
372 es7000_mip_reg.off_38 = (MIP_VALID);
373 while ((mip_status = es7000_mip_write(&es7000_mip_reg)) != 0)
374 printk("es7000_sw_apic: command failed, status = %x\n",
375 mip_status);
376 return;
377 }
378}