probe_32.c 10.3 KB
Newer Older
1
/*
2 3
 * Default generic APIC driver. This handles up to 8 CPUs.
 *
4 5 6
 * Copyright 2003 Andi Kleen, SuSE Labs.
 * Subject to the GNU Public License, v.2
 *
L
Linus Torvalds 已提交
7
 * Generic x86 APIC driver probe layer.
8
 */
L
Linus Torvalds 已提交
9 10
#include <linux/threads.h>
#include <linux/cpumask.h>
11
#include <linux/module.h>
L
Linus Torvalds 已提交
12 13 14 15
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/ctype.h>
#include <linux/init.h>
16
#include <linux/errno.h>
L
Linus Torvalds 已提交
17 18 19
#include <asm/fixmap.h>
#include <asm/mpspec.h>
#include <asm/apicdef.h>
I
Ingo Molnar 已提交
20
#include <asm/apic.h>
21
#include <asm/setup.h>
L
Linus Torvalds 已提交
22

23 24 25
#include <linux/threads.h>
#include <linux/cpumask.h>
#include <asm/mpspec.h>
I
Ingo Molnar 已提交
26
#include <asm/apic.h>
27 28 29 30 31 32
#include <asm/fixmap.h>
#include <asm/apicdef.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/smp.h>
#include <linux/init.h>
I
Ingo Molnar 已提交
33
#include <asm/apic.h>
34 35
#include <asm/ipi.h>

36 37 38 39 40 41 42 43
#include <linux/smp.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <asm/acpi.h>
#include <asm/arch_hooks.h>
#include <asm/e820.h>
#include <asm/setup.h>

I
Ingo Molnar 已提交
44
#include <asm/apic.h>
45 46 47 48 49 50 51 52 53 54 55

#ifdef CONFIG_HOTPLUG_CPU
#define DEFAULT_SEND_IPI	(1)
#else
#define DEFAULT_SEND_IPI	(0)
#endif

int no_broadcast = DEFAULT_SEND_IPI;

#ifdef CONFIG_X86_LOCAL_APIC

I
Ingo Molnar 已提交
56 57 58 59 60 61 62 63 64
void default_setup_apic_routing(void)
{
#ifdef CONFIG_X86_IO_APIC
	printk(KERN_INFO
		"Enabling APIC mode:  Flat.  Using %d I/O APICs\n",
		nr_ioapics);
#endif
}

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 112 113 114 115 116 117 118 119 120 121 122 123 124
static void default_vector_allocation_domain(int cpu, struct cpumask *retmask)
{
	/*
	 * Careful. Some cpus do not strictly honor the set of cpus
	 * specified in the interrupt destination when using lowest
	 * priority interrupt delivery mode.
	 *
	 * In particular there was a hyperthreading cpu observed to
	 * deliver interrupts to the wrong hyperthread when only one
	 * hyperthread was specified in the interrupt desitination.
	 */
	*retmask = (cpumask_t) { { [0] = APIC_ALL_CPUS } };
}

/* should be called last. */
static int probe_default(void)
{
	return 1;
}

struct genapic apic_default = {

	.name				= "default",
	.probe				= probe_default,
	.acpi_madt_oem_check		= NULL,
	.apic_id_registered		= default_apic_id_registered,

	.irq_delivery_mode		= dest_LowestPrio,
	/* logical delivery broadcast to all CPUs: */
	.irq_dest_mode			= 1,

	.target_cpus			= default_target_cpus,
	.disable_esr			= 0,
	.dest_logical			= APIC_DEST_LOGICAL,
	.check_apicid_used		= default_check_apicid_used,
	.check_apicid_present		= default_check_apicid_present,

	.vector_allocation_domain	= default_vector_allocation_domain,
	.init_apic_ldr			= default_init_apic_ldr,

	.ioapic_phys_id_map		= default_ioapic_phys_id_map,
	.setup_apic_routing		= default_setup_apic_routing,
	.multi_timer_check		= NULL,
	.apicid_to_node			= default_apicid_to_node,
	.cpu_to_logical_apicid		= default_cpu_to_logical_apicid,
	.cpu_present_to_apicid		= default_cpu_present_to_apicid,
	.apicid_to_cpu_present		= default_apicid_to_cpu_present,
	.setup_portio_remap		= NULL,
	.check_phys_apicid_present	= default_check_phys_apicid_present,
	.enable_apic_mode		= NULL,
	.phys_pkg_id			= default_phys_pkg_id,
	.mps_oem_check			= NULL,

	.get_apic_id			= default_get_apic_id,
	.set_apic_id			= NULL,
	.apic_id_mask			= 0x0F << 24,

	.cpu_mask_to_apicid		= default_cpu_mask_to_apicid,
	.cpu_mask_to_apicid_and		= default_cpu_mask_to_apicid_and,

125 126
	.send_IPI_mask			= default_send_IPI_mask_logical,
	.send_IPI_mask_allbutself	= default_send_IPI_mask_allbutself_logical,
127 128
	.send_IPI_allbutself		= default_send_IPI_allbutself,
	.send_IPI_all			= default_send_IPI_all,
129
	.send_IPI_self			= default_send_IPI_self,
130 131 132 133 134 135 136 137 138 139

	.wakeup_cpu			= NULL,
	.trampoline_phys_low		= DEFAULT_TRAMPOLINE_PHYS_LOW,
	.trampoline_phys_high		= DEFAULT_TRAMPOLINE_PHYS_HIGH,

	.wait_for_init_deassert		= default_wait_for_init_deassert,

	.smp_callin_clear_local_apic	= NULL,
	.store_NMI_vector		= NULL,
	.inquire_remote_apic		= default_inquire_remote_apic,
Y
Yinghai Lu 已提交
140 141 142 143 144 145 146

	.read				= native_apic_mem_read,
	.write				= native_apic_mem_write,
	.icr_read			= native_apic_icr_read,
	.icr_write			= native_apic_icr_write,
	.wait_icr_idle			= native_apic_wait_icr_idle,
	.safe_wait_icr_idle		= native_safe_apic_wait_icr_idle,
147 148
};

149
extern struct genapic apic_numaq;
L
Linus Torvalds 已提交
150 151 152 153 154
extern struct genapic apic_summit;
extern struct genapic apic_bigsmp;
extern struct genapic apic_es7000;
extern struct genapic apic_default;

I
Ingo Molnar 已提交
155
struct genapic *apic = &apic_default;
156
EXPORT_SYMBOL_GPL(apic);
L
Linus Torvalds 已提交
157

A
Adrian Bunk 已提交
158
static struct genapic *apic_probe[] __initdata = {
159 160 161 162
#ifdef CONFIG_X86_NUMAQ
	&apic_numaq,
#endif
#ifdef CONFIG_X86_SUMMIT
L
Linus Torvalds 已提交
163
	&apic_summit,
164 165
#endif
#ifdef CONFIG_X86_BIGSMP
166
	&apic_bigsmp,
167 168
#endif
#ifdef CONFIG_X86_ES7000
L
Linus Torvalds 已提交
169
	&apic_es7000,
170
#endif
L
Linus Torvalds 已提交
171 172 173 174
	&apic_default,	/* must be last */
	NULL,
};

175 176 177 178 179 180 181 182 183 184
static int cmdline_apic __initdata;
static int __init parse_apic(char *arg)
{
	int i;

	if (!arg)
		return -EINVAL;

	for (i = 0; apic_probe[i]; i++) {
		if (!strcmp(apic_probe[i]->name, arg)) {
I
Ingo Molnar 已提交
185
			apic = apic_probe[i];
186 187 188 189
			cmdline_apic = 1;
			return 0;
		}
	}
190

191 192 193
	if (x86_quirks->update_genapic)
		x86_quirks->update_genapic();

194 195
	/* Parsed again by __setup for debug/verbose */
	return 0;
196 197
}
early_param("apic", parse_apic);
198 199 200

void __init generic_bigsmp_probe(void)
{
201
#ifdef CONFIG_X86_BIGSMP
202 203 204
	/*
	 * This routine is used to switch to bigsmp mode when
	 * - There is no apic= option specified by the user
S
Simon Arlott 已提交
205
	 * - generic_apic_probe() has chosen apic_default as the sub_arch
206 207 208
	 * - we find more than 8 CPUs in acpi LAPIC listing with xAPIC support
	 */

I
Ingo Molnar 已提交
209
	if (!cmdline_apic && apic == &apic_default) {
210
		if (apic_bigsmp.probe()) {
I
Ingo Molnar 已提交
211
			apic = &apic_bigsmp;
212 213
			if (x86_quirks->update_genapic)
				x86_quirks->update_genapic();
214
			printk(KERN_INFO "Overriding APIC driver with %s\n",
I
Ingo Molnar 已提交
215
			       apic->name);
216
		}
217
	}
218
#endif
219 220
}

221
void __init generic_apic_probe(void)
222
{
223 224 225 226
	if (!cmdline_apic) {
		int i;
		for (i = 0; apic_probe[i]; i++) {
			if (apic_probe[i]->probe()) {
I
Ingo Molnar 已提交
227
				apic = apic_probe[i];
228
				break;
L
Linus Torvalds 已提交
229 230
			}
		}
231 232 233
		/* Not visible without early console */
		if (!apic_probe[i])
			panic("Didn't find an APIC driver");
234 235 236

		if (x86_quirks->update_genapic)
			x86_quirks->update_genapic();
L
Linus Torvalds 已提交
237
	}
I
Ingo Molnar 已提交
238
	printk(KERN_INFO "Using APIC driver %s\n", apic->name);
239
}
L
Linus Torvalds 已提交
240 241 242

/* These functions can switch the APIC even after the initial ->probe() */

243 244
int __init
generic_mps_oem_check(struct mpc_table *mpc, char *oem, char *productid)
245
{
L
Linus Torvalds 已提交
246
	int i;
I
Ingo Molnar 已提交
247

248
	for (i = 0; apic_probe[i]; ++i) {
I
Ingo Molnar 已提交
249 250 251 252 253 254 255 256 257 258 259
		if (!apic_probe[i]->mps_oem_check)
			continue;
		if (!apic_probe[i]->mps_oem_check(mpc, oem, productid))
			continue;

		if (!cmdline_apic) {
			apic = apic_probe[i];
			if (x86_quirks->update_genapic)
				x86_quirks->update_genapic();
			printk(KERN_INFO "Switched to APIC driver `%s'.\n",
			       apic->name);
260
		}
I
Ingo Molnar 已提交
261
		return 1;
262
	}
L
Linus Torvalds 已提交
263
	return 0;
264
}
L
Linus Torvalds 已提交
265

266
int __init default_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
L
Linus Torvalds 已提交
267 268
{
	int i;
269

270
	for (i = 0; apic_probe[i]; ++i) {
271 272 273 274 275 276 277 278 279 280 281
		if (!apic_probe[i]->acpi_madt_oem_check)
			continue;
		if (!apic_probe[i]->acpi_madt_oem_check(oem_id, oem_table_id))
			continue;

		if (!cmdline_apic) {
			apic = apic_probe[i];
			if (x86_quirks->update_genapic)
				x86_quirks->update_genapic();
			printk(KERN_INFO "Switched to APIC driver `%s'.\n",
			       apic->name);
282
		}
283
		return 1;
284 285
	}
	return 0;
L
Linus Torvalds 已提交
286
}
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429

#endif /* CONFIG_X86_LOCAL_APIC */

/**
 * pre_intr_init_hook - initialisation prior to setting up interrupt vectors
 *
 * Description:
 *	Perform any necessary interrupt initialisation prior to setting up
 *	the "ordinary" interrupt call gates.  For legacy reasons, the ISA
 *	interrupts should be initialised here if the machine emulates a PC
 *	in any way.
 **/
void __init pre_intr_init_hook(void)
{
	if (x86_quirks->arch_pre_intr_init) {
		if (x86_quirks->arch_pre_intr_init())
			return;
	}
	init_ISA_irqs();
}

/**
 * intr_init_hook - post gate setup interrupt initialisation
 *
 * Description:
 *	Fill in any interrupts that may have been left out by the general
 *	init_IRQ() routine.  interrupts having to do with the machine rather
 *	than the devices on the I/O bus (like APIC interrupts in intel MP
 *	systems) are started here.
 **/
void __init intr_init_hook(void)
{
	if (x86_quirks->arch_intr_init) {
		if (x86_quirks->arch_intr_init())
			return;
	}
}

/**
 * pre_setup_arch_hook - hook called prior to any setup_arch() execution
 *
 * Description:
 *	generally used to activate any machine specific identification
 *	routines that may be needed before setup_arch() runs.  On Voyager
 *	this is used to get the board revision and type.
 **/
void __init pre_setup_arch_hook(void)
{
}

/**
 * trap_init_hook - initialise system specific traps
 *
 * Description:
 *	Called as the final act of trap_init().  Used in VISWS to initialise
 *	the various board specific APIC traps.
 **/
void __init trap_init_hook(void)
{
	if (x86_quirks->arch_trap_init) {
		if (x86_quirks->arch_trap_init())
			return;
	}
}

static struct irqaction irq0  = {
	.handler = timer_interrupt,
	.flags = IRQF_DISABLED | IRQF_NOBALANCING | IRQF_IRQPOLL,
	.mask = CPU_MASK_NONE,
	.name = "timer"
};

/**
 * pre_time_init_hook - do any specific initialisations before.
 *
 **/
void __init pre_time_init_hook(void)
{
	if (x86_quirks->arch_pre_time_init)
		x86_quirks->arch_pre_time_init();
}

/**
 * time_init_hook - do any specific initialisations for the system timer.
 *
 * Description:
 *	Must plug the system timer interrupt source at HZ into the IRQ listed
 *	in irq_vectors.h:TIMER_IRQ
 **/
void __init time_init_hook(void)
{
	if (x86_quirks->arch_time_init) {
		/*
		 * A nonzero return code does not mean failure, it means
		 * that the architecture quirk does not want any
		 * generic (timer) setup to be performed after this:
		 */
		if (x86_quirks->arch_time_init())
			return;
	}

	irq0.mask = cpumask_of_cpu(0);
	setup_irq(0, &irq0);
}

#ifdef CONFIG_MCA
/**
 * mca_nmi_hook - hook into MCA specific NMI chain
 *
 * Description:
 *	The MCA (Microchannel Architecture) has an NMI chain for NMI sources
 *	along the MCA bus.  Use this to hook into that chain if you will need
 *	it.
 **/
void mca_nmi_hook(void)
{
	/*
	 * If I recall correctly, there's a whole bunch of other things that
	 * we can do to check for NMI problems, but that's all I know about
	 * at the moment.
	 */
	pr_warning("NMI generated from unknown source!\n");
}
#endif

static __init int no_ipi_broadcast(char *str)
{
	get_option(&str, &no_broadcast);
	pr_info("Using %s mode\n",
		no_broadcast ? "No IPI Broadcast" : "IPI Broadcast");
	return 1;
}
__setup("no_ipi_broadcast=", no_ipi_broadcast);

static int __init print_ipi_mode(void)
{
	pr_info("Using IPI %s mode\n",
		no_broadcast ? "No-Shortcut" : "Shortcut");
	return 0;
}

late_initcall(print_ipi_mode);