acpi.c 12.1 KB
Newer Older
Andy Grover's avatar
Andy Grover committed
1 2 3
/*
 *  acpi.c - Architecture-Specific Low-Level ACPI Support
 *
Andy Grover's avatar
Andy Grover committed
4
 *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
Andy Grover's avatar
Andy Grover committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
 *  Copyright (C) 2001 Jun Nakajima <jun.nakajima@intel.com>
 *  Copyright (C) 2001 Patrick Mochel <mochel@osdl.org>
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */

#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/bootmem.h>
#include <linux/irq.h>
#include <linux/acpi.h>
#include <asm/mpspec.h>
#include <asm/io.h>
#include <asm/apic.h>
#include <asm/apicdef.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
#include <asm/io_apic.h>
45
#include <asm/tlbflush.h>
Andy Grover's avatar
Andy Grover committed
46 47 48 49 50 51 52 53 54


#define PREFIX			"ACPI: "


/* --------------------------------------------------------------------------
                              Boot-time Configuration
   -------------------------------------------------------------------------- */

Andy Grover's avatar
Andy Grover committed
55 56
enum acpi_irq_model_id		acpi_irq_model;

57
#ifdef CONFIG_ACPI_BOOT
Andy Grover's avatar
Andy Grover committed
58 59 60 61 62 63 64 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
/*
 * Use reserved fixmap pages for physical-to-virtual mappings of ACPI tables.
 * Note that the same range is used for each table, so tables that need to
 * persist should be memcpy'd.
 */
char *
__acpi_map_table (
	unsigned long	phys_addr,
	unsigned long	size)
{
	unsigned long	base = 0;
	unsigned long	mapped_phys = phys_addr;
	unsigned long	offset = phys_addr & (PAGE_SIZE - 1);
	unsigned long	mapped_size = PAGE_SIZE - offset;
	unsigned long	avail_size = mapped_size + (PAGE_SIZE * FIX_ACPI_PAGES);
	int		idx = FIX_ACPI_BEGIN;

	if (!phys_addr || !size)
		return NULL;

	base = fix_to_virt(FIX_ACPI_BEGIN);

	set_fixmap(idx, mapped_phys);

	if (size > avail_size)
		return NULL;

	/* If the table doesn't map completely into the fist page... */
	if (size > mapped_size) {
		do {
			/* Make sure we don't go past our range */
			if (idx++ == FIX_ACPI_END)
				return NULL;
			mapped_phys = mapped_phys + PAGE_SIZE;
			set_fixmap(idx, mapped_phys);
			mapped_size = mapped_size + PAGE_SIZE;
		} while (mapped_size < size);
	}

	return ((unsigned char *) base + offset);
}


#ifdef CONFIG_X86_LOCAL_APIC

Andy Grover's avatar
Andy Grover committed
103
int acpi_lapic;
Andy Grover's avatar
Andy Grover committed
104

Andy Grover's avatar
Andy Grover committed
105
static u64 acpi_lapic_addr __initdata = APIC_DEFAULT_PHYS_BASE;
Andy Grover's avatar
Andy Grover committed
106

Andy Grover's avatar
Andy Grover committed
107 108 109 110 111

static int __init
acpi_parse_madt (
	unsigned long		phys_addr,
	unsigned long		size)
Andy Grover's avatar
Andy Grover committed
112
{
Andy Grover's avatar
Andy Grover committed
113
	struct acpi_table_madt	*madt = NULL;
Andy Grover's avatar
Andy Grover committed
114

Andy Grover's avatar
Andy Grover committed
115
	if (!phys_addr || !size)
Andy Grover's avatar
Andy Grover committed
116 117
		return -EINVAL;

Andy Grover's avatar
Andy Grover committed
118 119 120
	madt = (struct acpi_table_madt *) __acpi_map_table(phys_addr, size);
	if (!madt) {
		printk(KERN_WARNING PREFIX "Unable to map MADT\n");
Andy Grover's avatar
Andy Grover committed
121 122 123
		return -ENODEV;
	}

Andy Grover's avatar
Andy Grover committed
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
	if (madt->lapic_address)
		acpi_lapic_addr = (u64) madt->lapic_address;

	printk(KERN_INFO PREFIX "Local APIC address 0x%08x\n",
		madt->lapic_address);

	return 0;
}


static int __init
acpi_parse_lapic (
	acpi_table_entry_header *header)
{
	struct acpi_table_lapic	*processor = NULL;
Andy Grover's avatar
Andy Grover committed
139

Andy Grover's avatar
Andy Grover committed
140 141 142 143 144
	processor = (struct acpi_table_lapic*) header;
	if (!processor)
		return -EINVAL;

	acpi_table_print_madt_entry(header);
Andy Grover's avatar
Andy Grover committed
145

Andy Grover's avatar
Andy Grover committed
146 147 148
	mp_register_lapic (
		processor->id,					   /* APIC ID */
		processor->flags.enabled);			  /* Enabled? */
Andy Grover's avatar
Andy Grover committed
149 150 151 152 153

	return 0;
}


Andy Grover's avatar
Andy Grover committed
154
static int __init
Andy Grover's avatar
Andy Grover committed
155 156 157 158 159 160 161 162 163
acpi_parse_lapic_addr_ovr (
	acpi_table_entry_header *header)
{
	struct acpi_table_lapic_addr_ovr *lapic_addr_ovr = NULL;

	lapic_addr_ovr = (struct acpi_table_lapic_addr_ovr*) header;
	if (!lapic_addr_ovr)
		return -EINVAL;

Andy Grover's avatar
Andy Grover committed
164
	acpi_lapic_addr = lapic_addr_ovr->address;
Andy Grover's avatar
Andy Grover committed
165 166 167 168 169

	return 0;
}


Andy Grover's avatar
Andy Grover committed
170
static int __init
Andy Grover's avatar
Andy Grover committed
171 172 173
acpi_parse_lapic_nmi (
	acpi_table_entry_header *header)
{
Andy Grover's avatar
Andy Grover committed
174
	struct acpi_table_lapic_nmi *lapic_nmi = NULL;
Andy Grover's avatar
Andy Grover committed
175

Andy Grover's avatar
Andy Grover committed
176 177
	lapic_nmi = (struct acpi_table_lapic_nmi*) header;
	if (!lapic_nmi)
Andy Grover's avatar
Andy Grover committed
178 179 180 181
		return -EINVAL;

	acpi_table_print_madt_entry(header);

Andy Grover's avatar
Andy Grover committed
182 183
	if (lapic_nmi->lint != 1)
		printk(KERN_WARNING PREFIX "NMI not connected to LINT 1!\n");
Andy Grover's avatar
Andy Grover committed
184 185 186 187 188 189 190 191

	return 0;
}

#endif /*CONFIG_X86_LOCAL_APIC*/

#ifdef CONFIG_X86_IO_APIC

Andy Grover's avatar
Andy Grover committed
192 193 194
int acpi_ioapic;

static int __init
Andy Grover's avatar
Andy Grover committed
195 196 197 198
acpi_parse_ioapic (
	acpi_table_entry_header *header)
{
	struct acpi_table_ioapic *ioapic = NULL;
199

Andy Grover's avatar
Andy Grover committed
200 201 202
	ioapic = (struct acpi_table_ioapic*) header;
	if (!ioapic)
		return -EINVAL;
203 204 205
 
	acpi_table_print_madt_entry(header);

Andy Grover's avatar
Andy Grover committed
206 207 208 209 210
	mp_register_ioapic (
		ioapic->id,
		ioapic->address,
		ioapic->global_irq_base);
 
211 212 213 214
	return 0;
}


Andy Grover's avatar
Andy Grover committed
215
static int __init
216 217 218
acpi_parse_int_src_ovr (
	acpi_table_entry_header *header)
{
Andy Grover's avatar
Andy Grover committed
219
	struct acpi_table_int_src_ovr *intsrc = NULL;
220

Andy Grover's avatar
Andy Grover committed
221 222
	intsrc = (struct acpi_table_int_src_ovr*) header;
	if (!intsrc)
223
		return -EINVAL;
Andy Grover's avatar
Andy Grover committed
224 225 226

	acpi_table_print_madt_entry(header);

Andy Grover's avatar
Andy Grover committed
227 228 229 230 231
	mp_override_legacy_irq (
		intsrc->bus_irq,
		intsrc->flags.polarity,
		intsrc->flags.trigger,
		intsrc->global_irq);
232 233 234 235 236

	return 0;
}


Andy Grover's avatar
Andy Grover committed
237
static int __init
238 239 240 241 242 243 244 245 246 247 248
acpi_parse_nmi_src (
	acpi_table_entry_header *header)
{
	struct acpi_table_nmi_src *nmi_src = NULL;

	nmi_src = (struct acpi_table_nmi_src*) header;
	if (!nmi_src)
		return -EINVAL;

	acpi_table_print_madt_entry(header);

Andy Grover's avatar
Andy Grover committed
249
	/* TBD: Support nimsrc entries? */
Andy Grover's avatar
Andy Grover committed
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269

	return 0;
}

#endif /*CONFIG_X86_IO_APIC*/


static unsigned long __init
acpi_scan_rsdp (
	unsigned long		start,
	unsigned long		length)
{
	unsigned long		offset = 0;
	unsigned long		sig_len = sizeof("RSD PTR ") - 1;

	/*
	 * Scan all 16-byte boundaries of the physical memory region for the
	 * RSDP signature.
	 */
	for (offset = 0; offset < length; offset += 16) {
Andy Grover's avatar
Andy Grover committed
270
		if (strncmp((char *) (start + offset), "RSD PTR ", sig_len))
Andy Grover's avatar
Andy Grover committed
271 272 273 274 275 276 277 278
			continue;
		return (start + offset);
	}

	return 0;
}


Andy Grover's avatar
Andy Grover committed
279 280
unsigned long __init
acpi_find_rsdp (void)
Andy Grover's avatar
Andy Grover committed
281
{
Andy Grover's avatar
Andy Grover committed
282
	unsigned long		rsdp_phys = 0;
Andy Grover's avatar
Andy Grover committed
283 284 285 286 287

	/*
	 * Scan memory looking for the RSDP signature. First search EBDA (low
	 * memory) paragraphs and then search upper memory (E0000-FFFFF).
	 */
Andy Grover's avatar
Andy Grover committed
288 289 290
	rsdp_phys = acpi_scan_rsdp (0, 0x400);
	if (!rsdp_phys)
		rsdp_phys = acpi_scan_rsdp (0xE0000, 0xFFFFF);
Andy Grover's avatar
Andy Grover committed
291

Andy Grover's avatar
Andy Grover committed
292
	return rsdp_phys;
Andy Grover's avatar
Andy Grover committed
293 294 295
}


296 297 298 299 300
int __init
acpi_boot_init (
	char			*cmdline)
{
	int			result = 0;
Andy Grover's avatar
Andy Grover committed
301

Andy Grover's avatar
Andy Grover committed
302 303 304 305 306 307 308 309 310
	/*
	 * The default interrupt routing model is PIC (8259).  This gets
	 * overriden if IOAPICs are enumerated (below).
	 */
	acpi_irq_model = ACPI_IRQ_MODEL_PIC;

	/* 
	 * Initialize the ACPI boot-time table parser.
	 */
311
	result = acpi_table_init(cmdline);
Andy Grover's avatar
Andy Grover committed
312
	if (result)
313 314 315 316 317 318 319 320 321 322 323 324 325
		return result;

#ifdef CONFIG_X86_LOCAL_APIC

	/* 
	 * MADT
	 * ----
	 * Parse the Multiple APIC Description Table (MADT), if exists.
	 * Note that this table provides platform SMP configuration 
	 * information -- the successor to MPS tables.
	 */

	result = acpi_table_parse(ACPI_APIC, acpi_parse_madt);
Andy Grover's avatar
Andy Grover committed
326
	if (!result) {
327 328 329
		printk(KERN_WARNING PREFIX "MADT not present\n");
		return 0;
	}
Andy Grover's avatar
Andy Grover committed
330
	else if (result < 0) {
331 332 333
		printk(KERN_ERR PREFIX "Error parsing MADT\n");
		return result;
	}
Andy Grover's avatar
Andy Grover committed
334
	else if (result > 1) 
335 336
		printk(KERN_WARNING PREFIX "Multiple MADT tables exist\n");

Andy Grover's avatar
Andy Grover committed
337 338 339 340 341 342
	/* 
	 * Local APIC
	 * ----------
	 * Note that the LAPIC address is obtained from the MADT (32-bit value)
	 * and (optionally) overriden by a LAPIC_ADDR_OVR entry (64-bit value).
	 */
343 344

	result = acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr);
Andy Grover's avatar
Andy Grover committed
345
	if (result < 0) {
346 347 348 349
		printk(KERN_ERR PREFIX "Error parsing LAPIC address override entry\n");
		return result;
	}

Andy Grover's avatar
Andy Grover committed
350 351
	mp_register_lapic_address(acpi_lapic_addr);

352
	result = acpi_table_parse_madt(ACPI_MADT_LAPIC, acpi_parse_lapic);
Andy Grover's avatar
Andy Grover committed
353 354 355
	if (!result) { 
		printk(KERN_ERR PREFIX "No LAPIC entries present\n");
		/* TBD: Cleanup to allow fallback to MPS */
356 357
		return -ENODEV;
	}
Andy Grover's avatar
Andy Grover committed
358 359 360 361 362
	else if (result < 0) {
		printk(KERN_ERR PREFIX "Error parsing LAPIC entry\n");
		/* TBD: Cleanup to allow fallback to MPS */
		return result;
	}
363

364
#ifndef CONFIG_ACPI_HT_ONLY
365
	result = acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi);
Andy Grover's avatar
Andy Grover committed
366
	if (result < 0) {
367
		printk(KERN_ERR PREFIX "Error parsing LAPIC NMI entry\n");
Andy Grover's avatar
Andy Grover committed
368
		/* TBD: Cleanup to allow fallback to MPS */
369 370
		return result;
	}
371
#endif /*!CONFIG_ACPI_HT_ONLY*/
372

Andy Grover's avatar
Andy Grover committed
373 374 375 376 377
	acpi_lapic = 1;

#endif /*CONFIG_X86_LOCAL_APIC*/

#ifdef CONFIG_X86_IO_APIC
378
#ifndef CONFIG_ACPI_HT_ONLY
Andy Grover's avatar
Andy Grover committed
379 380 381 382 383

	/* 
	 * I/O APIC 
	 * --------
	 */
384 385

	result = acpi_table_parse_madt(ACPI_MADT_IOAPIC, acpi_parse_ioapic);
Andy Grover's avatar
Andy Grover committed
386 387
	if (!result) { 
		printk(KERN_ERR PREFIX "No IOAPIC entries present\n");
388 389
		return -ENODEV;
	}
Andy Grover's avatar
Andy Grover committed
390 391 392 393
	else if (result < 0) {
		printk(KERN_ERR PREFIX "Error parsing IOAPIC entry\n");
		return result;
	}
394

Andy Grover's avatar
Andy Grover committed
395 396
	/* Build a default routing table for legacy (ISA) interrupts. */
	mp_config_acpi_legacy_irqs();
397 398

	result = acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr);
Andy Grover's avatar
Andy Grover committed
399
	if (result < 0) {
400
		printk(KERN_ERR PREFIX "Error parsing interrupt source overrides entry\n");
Andy Grover's avatar
Andy Grover committed
401
		/* TBD: Cleanup to allow fallback to MPS */
402 403 404 405
		return result;
	}

	result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src);
Andy Grover's avatar
Andy Grover committed
406
	if (result < 0) {
407
		printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n");
Andy Grover's avatar
Andy Grover committed
408
		/* TBD: Cleanup to allow fallback to MPS */
409 410 411
		return result;
	}

Andy Grover's avatar
Andy Grover committed
412 413 414
	acpi_irq_model = ACPI_IRQ_MODEL_IOAPIC;

	acpi_ioapic = 1;
415

416
#endif /*!CONFIG_ACPI_HT_ONLY*/
417 418
#endif /*CONFIG_X86_IO_APIC*/

Andy Grover's avatar
Andy Grover committed
419 420 421 422
#ifdef CONFIG_X86_LOCAL_APIC
	if (acpi_lapic && acpi_ioapic)
		smp_found_config = 1;
#endif
423 424 425 426 427

	return 0;
}

#endif /*CONFIG_ACPI_BOOT*/
Andy Grover's avatar
Andy Grover committed
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475


/* --------------------------------------------------------------------------
                              Low-Level Sleep Support
   -------------------------------------------------------------------------- */

#ifdef CONFIG_ACPI_SLEEP

#define DEBUG

#ifdef DEBUG
#include <linux/serial.h>
#endif

/* address in low memory of the wakeup routine. */
unsigned long acpi_wakeup_address = 0;

/* new page directory that we will be using */
static pmd_t *pmd;

/* saved page directory */
static pmd_t saved_pmd;

/* page which we'll use for the new page directory */
static pte_t *ptep;

extern unsigned long FASTCALL(acpi_copy_wakeup_routine(unsigned long));

/*
 * acpi_create_identity_pmd
 *
 * Create a new, identity mapped pmd.
 *
 * Do this by creating new page directory, and marking all the pages as R/W
 * Then set it as the new Page Middle Directory.
 * And, of course, flush the TLB so it takes effect.
 *
 * We save the address of the old one, for later restoration.
 */
static void acpi_create_identity_pmd (void)
{
	pgd_t *pgd;
	int i;

	ptep = (pte_t*)__get_free_page(GFP_KERNEL);

	/* fill page with low mapping */
	for (i = 0; i < PTRS_PER_PTE; i++)
476
		set_pte(ptep + i, pfn_pte(i, PAGE_SHARED));
Andy Grover's avatar
Andy Grover committed
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549

	pgd = pgd_offset(current->active_mm, 0);
	pmd = pmd_alloc(current->mm,pgd, 0);

	/* save the old pmd */
	saved_pmd = *pmd;

	/* set the new one */
	set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(ptep)));

	/* flush the TLB */
	local_flush_tlb();
}

/*
 * acpi_restore_pmd
 *
 * Restore the old pmd saved by acpi_create_identity_pmd and
 * free the page that said function alloc'd
 */
static void acpi_restore_pmd (void)
{
	set_pmd(pmd, saved_pmd);
	local_flush_tlb();
	free_page((unsigned long)ptep);
}

/**
 * acpi_save_state_mem - save kernel state
 *
 * Create an identity mapped page table and copy the wakeup routine to
 * low memory.
 */
int acpi_save_state_mem (void)
{
	acpi_create_identity_pmd();
	acpi_copy_wakeup_routine(acpi_wakeup_address);

	return 0;
}

/**
 * acpi_save_state_disk - save kernel state to disk
 *
 */
int acpi_save_state_disk (void)
{
	return 1;
}

/*
 * acpi_restore_state
 */
void acpi_restore_state_mem (void)
{
	acpi_restore_pmd();
}

/**
 * acpi_reserve_bootmem - do _very_ early ACPI initialisation
 *
 * We allocate a page in low memory for the wakeup
 * routine for when we come back from a sleep state. The
 * runtime allocator allows specification of <16M pages, but not
 * <1M pages.
 */
void __init acpi_reserve_bootmem(void)
{
	acpi_wakeup_address = (unsigned long)alloc_bootmem_low(PAGE_SIZE);
	printk(KERN_DEBUG "ACPI: have wakeup address 0x%8.8lx\n", acpi_wakeup_address);
}

#endif /*CONFIG_ACPI_SLEEP*/