Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux...
[sfrench/cifs-2.6.git] / arch / x86 / kernel / apic / x2apic_uv_x.c
index e6230af1986468d2f0a03464988dcd576e7ca081..d5b51a740524d6759a1a3c88a0b022a6335abe48 100644 (file)
@@ -14,6 +14,8 @@
 #include <linux/memory.h>
 #include <linux/export.h>
 #include <linux/pci.h>
+#include <linux/acpi.h>
+#include <linux/efi.h>
 
 #include <asm/e820/api.h>
 #include <asm/uv/uv_mmrs.h>
 static DEFINE_PER_CPU(int, x2apic_extra_bits);
 
 static enum uv_system_type     uv_system_type;
-static bool                    uv_hubless_system;
+static int                     uv_hubbed_system;
+static int                     uv_hubless_system;
 static u64                     gru_start_paddr, gru_end_paddr;
 static u64                     gru_dist_base, gru_first_node_paddr = -1LL, gru_last_node_paddr;
 static u64                     gru_dist_lmask, gru_dist_umask;
 static union uvh_apicid                uvh_apicid;
 
+/* Unpack OEM/TABLE ID's to be NULL terminated strings */
+static u8 oem_id[ACPI_OEM_ID_SIZE + 1];
+static u8 oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1];
+
 /* Information derived from CPUID: */
 static struct {
        unsigned int apicid_shift;
@@ -248,17 +255,35 @@ static void __init uv_set_apicid_hibit(void)
        }
 }
 
-static int __init uv_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
+static void __init uv_stringify(int len, char *to, char *from)
+{
+       /* Relies on 'to' being NULL chars so result will be NULL terminated */
+       strncpy(to, from, len-1);
+}
+
+static int __init uv_acpi_madt_oem_check(char *_oem_id, char *_oem_table_id)
 {
        int pnodeid;
        int uv_apic;
 
+       uv_stringify(sizeof(oem_id), oem_id, _oem_id);
+       uv_stringify(sizeof(oem_table_id), oem_table_id, _oem_table_id);
+
        if (strncmp(oem_id, "SGI", 3) != 0) {
-               if (strncmp(oem_id, "NSGI", 4) == 0) {
-                       uv_hubless_system = true;
-                       pr_info("UV: OEM IDs %s/%s, HUBLESS\n",
-                               oem_id, oem_table_id);
-               }
+               if (strncmp(oem_id, "NSGI", 4) != 0)
+                       return 0;
+
+               /* UV4 Hubless, CH, (0x11:UV4+Any) */
+               if (strncmp(oem_id, "NSGI4", 5) == 0)
+                       uv_hubless_system = 0x11;
+
+               /* UV3 Hubless, UV300/MC990X w/o hub (0x9:UV3+Any) */
+               else
+                       uv_hubless_system = 0x9;
+
+               pr_info("UV: OEM IDs %s/%s, HUBLESS(0x%x)\n",
+                       oem_id, oem_table_id, uv_hubless_system);
+
                return 0;
        }
 
@@ -286,6 +311,24 @@ static int __init uv_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
        if (uv_hub_info->hub_revision == 0)
                goto badbios;
 
+       switch (uv_hub_info->hub_revision) {
+       case UV4_HUB_REVISION_BASE:
+               uv_hubbed_system = 0x11;
+               break;
+
+       case UV3_HUB_REVISION_BASE:
+               uv_hubbed_system = 0x9;
+               break;
+
+       case UV2_HUB_REVISION_BASE:
+               uv_hubbed_system = 0x5;
+               break;
+
+       case UV1_HUB_REVISION_BASE:
+               uv_hubbed_system = 0x3;
+               break;
+       }
+
        pnodeid = early_get_pnodeid();
        early_get_apic_socketid_shift();
 
@@ -336,9 +379,15 @@ int is_uv_system(void)
 }
 EXPORT_SYMBOL_GPL(is_uv_system);
 
-int is_uv_hubless(void)
+int is_uv_hubbed(int uvtype)
+{
+       return (uv_hubbed_system & uvtype);
+}
+EXPORT_SYMBOL_GPL(is_uv_hubbed);
+
+int is_uv_hubless(int uvtype)
 {
-       return uv_hubless_system;
+       return (uv_hubless_system & uvtype);
 }
 EXPORT_SYMBOL_GPL(is_uv_hubless);
 
@@ -1255,7 +1304,8 @@ static int __init decode_uv_systab(void)
        struct uv_systab *st;
        int i;
 
-       if (uv_hub_info->hub_revision < UV4_HUB_REVISION_BASE)
+       /* If system is uv3 or lower, there is no extended UVsystab */
+       if (is_uv_hubbed(0xfffffe) < uv(4) && is_uv_hubless(0xfffffe) < uv(4))
                return 0;       /* No extended UVsystab required */
 
        st = uv_systab;
@@ -1434,6 +1484,103 @@ static void __init build_socket_tables(void)
        }
 }
 
+/* Check which reboot to use */
+static void check_efi_reboot(void)
+{
+       /* If EFI reboot not available, use ACPI reboot */
+       if (!efi_enabled(EFI_BOOT))
+               reboot_type = BOOT_ACPI;
+}
+
+/* Setup user proc fs files */
+static int proc_hubbed_show(struct seq_file *file, void *data)
+{
+       seq_printf(file, "0x%x\n", uv_hubbed_system);
+       return 0;
+}
+
+static int proc_hubless_show(struct seq_file *file, void *data)
+{
+       seq_printf(file, "0x%x\n", uv_hubless_system);
+       return 0;
+}
+
+static int proc_oemid_show(struct seq_file *file, void *data)
+{
+       seq_printf(file, "%s/%s\n", oem_id, oem_table_id);
+       return 0;
+}
+
+static int proc_hubbed_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, proc_hubbed_show, (void *)NULL);
+}
+
+static int proc_hubless_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, proc_hubless_show, (void *)NULL);
+}
+
+static int proc_oemid_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, proc_oemid_show, (void *)NULL);
+}
+
+/* (struct is "non-const" as open function is set at runtime) */
+static struct file_operations proc_version_fops = {
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static const struct file_operations proc_oemid_fops = {
+       .open           = proc_oemid_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static __init void uv_setup_proc_files(int hubless)
+{
+       struct proc_dir_entry *pde;
+       char *name = hubless ? "hubless" : "hubbed";
+
+       pde = proc_mkdir(UV_PROC_NODE, NULL);
+       proc_create("oemid", 0, pde, &proc_oemid_fops);
+       proc_create(name, 0, pde, &proc_version_fops);
+       if (hubless)
+               proc_version_fops.open = proc_hubless_open;
+       else
+               proc_version_fops.open = proc_hubbed_open;
+}
+
+/* Initialize UV hubless systems */
+static __init int uv_system_init_hubless(void)
+{
+       int rc;
+
+       /* Setup PCH NMI handler */
+       uv_nmi_setup_hubless();
+
+       /* Init kernel/BIOS interface */
+       rc = uv_bios_init();
+       if (rc < 0)
+               return rc;
+
+       /* Process UVsystab */
+       rc = decode_uv_systab();
+       if (rc < 0)
+               return rc;
+
+       /* Create user access node */
+       if (rc >= 0)
+               uv_setup_proc_files(1);
+
+       check_efi_reboot();
+
+       return rc;
+}
+
 static void __init uv_system_init_hub(void)
 {
        struct uv_hub_info_s hub_info = {0};
@@ -1559,32 +1706,27 @@ static void __init uv_system_init_hub(void)
        uv_nmi_setup();
        uv_cpu_init();
        uv_scir_register_cpu_notifier();
-       proc_mkdir("sgi_uv", NULL);
+       uv_setup_proc_files(0);
 
        /* Register Legacy VGA I/O redirection handler: */
        pci_register_set_vga_state(uv_set_vga_state);
 
-       /*
-        * For a kdump kernel the reset must be BOOT_ACPI, not BOOT_EFI, as
-        * EFI is not enabled in the kdump kernel:
-        */
-       if (is_kdump_kernel())
-               reboot_type = BOOT_ACPI;
+       check_efi_reboot();
 }
 
 /*
- * There is a small amount of UV specific code needed to initialize a
- * UV system that does not have a "UV HUB" (referred to as "hubless").
+ * There is a different code path needed to initialize a UV system that does
+ * not have a "UV HUB" (referred to as "hubless").
  */
 void __init uv_system_init(void)
 {
-       if (likely(!is_uv_system() && !is_uv_hubless()))
+       if (likely(!is_uv_system() && !is_uv_hubless(1)))
                return;
 
        if (is_uv_system())
                uv_system_init_hub();
        else
-               uv_nmi_setup_hubless();
+               uv_system_init_hubless();
 }
 
 apic_driver(apic_x2apic_uv_x);