patch-2.4.15 linux/arch/ia64/kernel/acpi.c

Next file: linux/arch/ia64/kernel/efi.c
Previous file: linux/arch/ia64/kernel/Makefile
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.14/linux/arch/ia64/kernel/acpi.c linux/arch/ia64/kernel/acpi.c
@@ -9,7 +9,7 @@
  * Copyright (C) 2000 Hewlett-Packard Co.
  * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com>
  * Copyright (C) 2000 Intel Corp.
- * Copyright (C) 2000 J.I. Lee <jung-ik.lee@intel.com>
+ * Copyright (C) 2000,2001 J.I. Lee <jung-ik.lee@intel.com>
  *      ACPI based kernel configuration manager.
  *      ACPI 2.0 & IA64 ext 0.71
  */
@@ -23,6 +23,9 @@
 #include <linux/string.h>
 #include <linux/types.h>
 #include <linux/irq.h>
+#ifdef CONFIG_SERIAL_ACPI
+#include <linux/acpi_serial.h>
+#endif
 
 #include <asm/acpi-ext.h>
 #include <asm/acpikcfg.h>
@@ -34,6 +37,9 @@
 
 #undef ACPI_DEBUG		/* Guess what this does? */
 
+/* global array to record platform interrupt vectors for generic int routing */
+int platform_irq_list[ACPI_MAX_PLATFORM_IRQS];
+
 /* These are ugly but will be reclaimed by the kernel */
 int __initdata available_cpus;
 int __initdata total_cpus;
@@ -41,8 +47,11 @@
 void (*pm_idle) (void);
 void (*pm_power_off) (void);
 
+asm (".weak iosapic_register_irq");
 asm (".weak iosapic_register_legacy_irq");
+asm (".weak iosapic_register_platform_irq");
 asm (".weak iosapic_init");
+asm (".weak iosapic_version");
 
 const char *
 acpi_get_sysname (void)
@@ -55,6 +64,8 @@
 	return "hpsim";
 # elif defined (CONFIG_IA64_SGI_SN1)
 	return "sn1";
+# elif defined (CONFIG_IA64_SGI_SN2)
+	return "sn2";
 # elif defined (CONFIG_IA64_DIG)
 	return "dig";
 # else
@@ -65,6 +76,25 @@
 }
 
 /*
+ * Interrupt routing API for device drivers.
+ * Provides the interrupt vector for a generic platform event
+ * (currently only CPEI implemented)
+ */
+int
+acpi_request_vector(u32 int_type)
+{
+	int vector = -1;
+
+	if (int_type < ACPI_MAX_PLATFORM_IRQS) {
+		/* correctable platform error interrupt */
+		vector = platform_irq_list[int_type];
+	} else
+		printk("acpi_request_vector(): invalid interrupt type\n");
+
+	return vector;
+}
+
+/*
  * Configure legacy IRQ information.
  */
 static void __init
@@ -139,15 +169,93 @@
 }
 
 /*
- * Info on platform interrupt sources: NMI. PMI, INIT, etc.
+ * Extract iosapic info from madt (again) to determine which iosapic
+ * this platform interrupt resides in
+ */
+static int __init
+acpi20_which_iosapic (int global_vector, acpi_madt_t *madt, u32 *irq_base, char **iosapic_address)
+{
+	acpi_entry_iosapic_t *iosapic;
+	char *p, *end;
+	int ver, max_pin;
+
+	p = (char *) (madt + 1);
+	end = p + (madt->header.length - sizeof(acpi_madt_t));
+
+	while (p < end) {
+		switch (*p) {
+		      case ACPI20_ENTRY_IO_SAPIC:
+			/* collect IOSAPIC info for platform int use later */
+			iosapic = (acpi_entry_iosapic_t *)p;
+			*irq_base = iosapic->irq_base;
+			*iosapic_address = ioremap(iosapic->address, 0);
+			/* is this the iosapic we're looking for? */
+			ver = iosapic_version(*iosapic_address);
+			max_pin = (ver >> 16) & 0xff;
+			if ((global_vector - *irq_base) <= max_pin)
+				return 0;		/* found it! */
+			break;
+		      default:
+			break;
+		}
+		p += p[1];
+	}
+	return 1;
+}
+
+/*
+ * Info on platform interrupt sources: NMI, PMI, INIT, etc.
  */
 static void __init
-acpi20_platform (char *p)
+acpi20_platform (char *p, acpi_madt_t *madt)
 {
+	int vector;
+	u32 irq_base;
+	char *iosapic_address;
+	unsigned long polarity = 0, trigger = 0;
 	acpi20_entry_platform_src_t *plat = (acpi20_entry_platform_src_t *) p;
 
 	printk("PLATFORM: IOSAPIC %x -> Vector %x on CPU %.04u:%.04u\n",
 	       plat->iosapic_vector, plat->global_vector, plat->eid, plat->id);
+
+	/* record platform interrupt vectors for generic int routing code */
+
+	if (!iosapic_register_platform_irq) {
+		printk("acpi20_platform(): no ACPI platform IRQ support\n");
+		return;
+	}
+
+	/* extract polarity and trigger info from flags */
+	switch (plat->flags) {
+	      case 0x5: polarity = 1; trigger = 1; break;
+	      case 0x7: polarity = 0; trigger = 1; break;
+	      case 0xd: polarity = 1; trigger = 0; break;
+	      case 0xf: polarity = 0; trigger = 0; break;
+	      default:
+		printk("acpi20_platform(): unknown flags 0x%x\n", plat->flags);
+		break;
+	}
+
+	/* which iosapic does this IRQ belong to? */
+	if (acpi20_which_iosapic(plat->global_vector, madt, &irq_base, &iosapic_address)) {
+		printk("acpi20_platform(): I/O SAPIC not found!\n");
+		return;
+	}
+
+	/*
+	 * get vector assignment for this IRQ, set attributes, and program the IOSAPIC
+	 * routing table
+	 */
+	vector = iosapic_register_platform_irq(plat->int_type,
+					       plat->global_vector,
+					       plat->iosapic_vector,
+					       plat->eid,
+					       plat->id,
+					       polarity,
+					       trigger,
+					       irq_base,
+					       iosapic_address);
+	platform_irq_list[plat->int_type] = vector;
 }
 
 /*
@@ -173,8 +281,10 @@
 static void __init
 acpi20_parse_madt (acpi_madt_t *madt)
 {
-	acpi_entry_iosapic_t *iosapic;
+	acpi_entry_iosapic_t *iosapic = NULL;
+	acpi20_entry_lsapic_t *lsapic = NULL;
 	char *p, *end;
+	int i;
 
 	/* Base address of IPI Message Block */
 	if (madt->lapic_address) {
@@ -186,23 +296,27 @@
 	p = (char *) (madt + 1);
 	end = p + (madt->header.length - sizeof(acpi_madt_t));
 
+	/* Initialize platform interrupt vector array */
+	for (i = 0; i < ACPI_MAX_PLATFORM_IRQS; i++)
+		platform_irq_list[i] = -1;
+
 	/*
-	 * Splitted entry parsing to ensure ordering.
+	 * Split-up entry parsing to ensure ordering.
 	 */
-
 	while (p < end) {
 		switch (*p) {
-		case ACPI20_ENTRY_LOCAL_APIC_ADDR_OVERRIDE:
+		      case ACPI20_ENTRY_LOCAL_APIC_ADDR_OVERRIDE:
 			printk("ACPI 2.0 MADT: LOCAL APIC Override\n");
 			acpi20_lapic_addr_override(p);
 			break;
 
-		case ACPI20_ENTRY_LOCAL_SAPIC:
+		      case ACPI20_ENTRY_LOCAL_SAPIC:
 			printk("ACPI 2.0 MADT: LOCAL SAPIC\n");
+			lsapic = (acpi20_entry_lsapic_t *) p;
 			acpi20_lsapic(p);
 			break;
 
-		case ACPI20_ENTRY_IO_SAPIC:
+		      case ACPI20_ENTRY_IO_SAPIC:
 			iosapic = (acpi_entry_iosapic_t *) p;
 			if (iosapic_init)
 				/*
@@ -218,26 +332,25 @@
 					);
 			break;
 
-		case ACPI20_ENTRY_PLATFORM_INT_SOURCE:
+		      case ACPI20_ENTRY_PLATFORM_INT_SOURCE:
 			printk("ACPI 2.0 MADT: PLATFORM INT SOURCE\n");
-			acpi20_platform(p);
+			acpi20_platform(p, madt);
 			break;
 
-		case ACPI20_ENTRY_LOCAL_APIC:
+		      case ACPI20_ENTRY_LOCAL_APIC:
 			printk("ACPI 2.0 MADT: LOCAL APIC entry\n"); break;
-		case ACPI20_ENTRY_IO_APIC:
+		      case ACPI20_ENTRY_IO_APIC:
 			printk("ACPI 2.0 MADT: IO APIC entry\n"); break;
-		case ACPI20_ENTRY_NMI_SOURCE:
+		      case ACPI20_ENTRY_NMI_SOURCE:
 			printk("ACPI 2.0 MADT: NMI SOURCE entry\n"); break;
-		case ACPI20_ENTRY_LOCAL_APIC_NMI:
+		      case ACPI20_ENTRY_LOCAL_APIC_NMI:
 			printk("ACPI 2.0 MADT: LOCAL APIC NMI entry\n"); break;
-		case ACPI20_ENTRY_INT_SRC_OVERRIDE:
+		      case ACPI20_ENTRY_INT_SRC_OVERRIDE:
 			break;
-		default:
+		      default:
 			printk("ACPI 2.0 MADT: unknown entry skip\n"); break;
 			break;
 		}
-
 		p += p[1];
 	}
 
@@ -245,16 +358,35 @@
 	end = p + (madt->header.length - sizeof(acpi_madt_t));
 
 	while (p < end) {
+		switch (*p) {
+		      case ACPI20_ENTRY_LOCAL_APIC:
+			if (lsapic) break;
+			printk("ACPI 2.0 MADT: LOCAL APIC entry\n");
+			/* parse local apic if there's no local Sapic */
+			break;
+		      case ACPI20_ENTRY_IO_APIC:
+			if (iosapic) break;
+			printk("ACPI 2.0 MADT: IO APIC entry\n");
+			/* parse ioapic if there's no ioSapic */
+			break;
+		      default:
+			break;
+		}
+		p += p[1];
+	}
 
+	p = (char *) (madt + 1);
+	end = p + (madt->header.length - sizeof(acpi_madt_t));
+
+	while (p < end) {
 		switch (*p) {
-		case ACPI20_ENTRY_INT_SRC_OVERRIDE:
+		      case ACPI20_ENTRY_INT_SRC_OVERRIDE:
 			printk("ACPI 2.0 MADT: INT SOURCE Override\n");
 			acpi_legacy_irq(p);
 			break;
-		default:
+		      default:
 			break;
 		}
-
 		p += p[1];
 	}
 
@@ -269,6 +401,7 @@
 # ifdef CONFIG_ACPI
 	acpi_xsdt_t *xsdt;
 	acpi_desc_table_hdr_t *hdrp;
+	acpi_madt_t *madt;
 	int tables, i;
 
 	if (strncmp(rsdp20->signature, ACPI_RSDP_SIG, ACPI_RSDP_SIG_LEN)) {
@@ -310,9 +443,76 @@
 			ACPI_MADT_SIG, ACPI_MADT_SIG_LEN) != 0)
 			continue;
 
-		acpi20_parse_madt((acpi_madt_t *) hdrp);
+		/* Save MADT pointer for later */
+		madt = (acpi_madt_t *) hdrp;
+		acpi20_parse_madt(madt);
 	}
 
+#ifdef CONFIG_SERIAL_ACPI
+	/*
+	 * Now we're interested in other tables.  We want the iosapics already
+	 * initialized, so we do it in a separate loop.
+	 */
+	for (i = 0; i < tables; i++) {
+		hdrp = (acpi_desc_table_hdr_t *) __va(readl_unaligned(&xsdt->entry_ptrs[i]));
+		/*
+		 * search for SPCR and DBGP table entries so we can enable
+		 * non-pci interrupts to IO-SAPICs.
+		 */
+		if (!strncmp(hdrp->signature, ACPI_SPCRT_SIG, ACPI_SPCRT_SIG_LEN) ||
+		    !strncmp(hdrp->signature, ACPI_DBGPT_SIG, ACPI_DBGPT_SIG_LEN))
+		{
+			acpi_ser_t *spcr = (void *)hdrp;
+			unsigned long global_int;
+
+			setup_serial_acpi(hdrp);
+
+			/*
+			 * ACPI is able to describe serial ports that live at non-standard
+			 * memory space addresses and use SAPIC interrupts.  If not also
+			 * PCI devices, there would be no interrupt vector information for
+			 * them.  This checks for and fixes that situation.
+			 */
+			if (spcr->length < sizeof(acpi_ser_t))
+				/* table is not long enough for full info, thus no int */
+				break;
+
+			/*
+			 * If the device is not in PCI space, but uses a SAPIC interrupt,
+			 * we need to program the SAPIC so that serial can autoprobe for
+			 * the IA64 interrupt vector later on.  If the device is in PCI
+			 * space, it should already be setup via the PCI vectors
+			 */
+			if (spcr->base_addr.space_id != ACPI_SERIAL_PCICONF_SPACE &&
+			    spcr->int_type == ACPI_SERIAL_INT_SAPIC)
+			{
+				u32 irq_base;
+				char *iosapic_address;
+				int vector;
+
+				/* We have a UART in memory space with a SAPIC interrupt */
+				global_int = (  (spcr->global_int[3] << 24)
+					      | (spcr->global_int[2] << 16)
+					      | (spcr->global_int[1] << 8)
+					      | spcr->global_int[0]);
+
+				if (!iosapic_register_irq)
+					continue;
+
+				/* which iosapic does this IRQ belong to? */
+				if (acpi20_which_iosapic(global_int, madt, &irq_base,
+							 &iosapic_address) == 0)
+				{
+					vector = iosapic_register_irq(global_int,
+					                              1, /* active high polarity */
+					                              1, /* edge triggered */
+					                              irq_base,
+					                              iosapic_address);
+				}
+			}
+		}
+	}
+#endif
 	acpi_cf_terminate();
 
 #  ifdef CONFIG_SMP

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)