linux/arch/x86/kernel/numaq_32.c
<<
>>
Prefs
   1/*
   2 * Written by: Patricia Gaughen, IBM Corporation
   3 *
   4 * Copyright (C) 2002, IBM Corp.
   5 *
   6 * All rights reserved.          
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful, but
  14 * WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
  16 * NON INFRINGEMENT.  See the GNU General Public License for more
  17 * details.
  18 *
  19 * You should have received a copy of the GNU General Public License
  20 * along with this program; if not, write to the Free Software
  21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22 *
  23 * Send feedback to <gone@us.ibm.com>
  24 */
  25
  26#include <linux/mm.h>
  27#include <linux/bootmem.h>
  28#include <linux/mmzone.h>
  29#include <linux/module.h>
  30#include <linux/nodemask.h>
  31#include <asm/numaq.h>
  32#include <asm/topology.h>
  33#include <asm/processor.h>
  34#include <asm/mpspec.h>
  35#include <asm/e820.h>
  36#include <asm/setup.h>
  37
  38#define MB_TO_PAGES(addr) ((addr) << (20 - PAGE_SHIFT))
  39
  40/*
  41 * Function: smp_dump_qct()
  42 *
  43 * Description: gets memory layout from the quad config table.  This
  44 * function also updates node_online_map with the nodes (quads) present.
  45 */
  46static void __init smp_dump_qct(void)
  47{
  48        int node;
  49        struct eachquadmem *eq;
  50        struct sys_cfg_data *scd =
  51                (struct sys_cfg_data *)__va(SYS_CFG_DATA_PRIV_ADDR);
  52
  53        nodes_clear(node_online_map);
  54        for_each_node(node) {
  55                if (scd->quads_present31_0 & (1 << node)) {
  56                        node_set_online(node);
  57                        eq = &scd->eq[node];
  58                        /* Convert to pages */
  59                        node_start_pfn[node] = MB_TO_PAGES(
  60                                eq->hi_shrd_mem_start - eq->priv_mem_size);
  61                        node_end_pfn[node] = MB_TO_PAGES(
  62                                eq->hi_shrd_mem_start + eq->hi_shrd_mem_size);
  63
  64                        e820_register_active_regions(node, node_start_pfn[node],
  65                                                        node_end_pfn[node]);
  66                        memory_present(node,
  67                                node_start_pfn[node], node_end_pfn[node]);
  68                        node_remap_size[node] = node_memmap_size_bytes(node,
  69                                                        node_start_pfn[node],
  70                                                        node_end_pfn[node]);
  71                }
  72        }
  73}
  74
  75
  76void __cpuinit numaq_tsc_disable(void)
  77{
  78        if (!found_numaq)
  79                return;
  80
  81        if (num_online_nodes() > 1) {
  82                printk(KERN_DEBUG "NUMAQ: disabling TSC\n");
  83                setup_clear_cpu_cap(X86_FEATURE_TSC);
  84        }
  85}
  86
  87static int __init numaq_pre_time_init(void)
  88{
  89        numaq_tsc_disable();
  90        return 0;
  91}
  92
  93int found_numaq;
  94/*
  95 * Have to match translation table entries to main table entries by counter
  96 * hence the mpc_record variable .... can't see a less disgusting way of
  97 * doing this ....
  98 */
  99struct mpc_config_translation {
 100        unsigned char mpc_type;
 101        unsigned char trans_len;
 102        unsigned char trans_type;
 103        unsigned char trans_quad;
 104        unsigned char trans_global;
 105        unsigned char trans_local;
 106        unsigned short trans_reserved;
 107};
 108
 109/* x86_quirks member */
 110static int mpc_record;
 111static struct mpc_config_translation *translation_table[MAX_MPC_ENTRY]
 112    __cpuinitdata;
 113
 114static inline int generate_logical_apicid(int quad, int phys_apicid)
 115{
 116        return (quad << 4) + (phys_apicid ? phys_apicid << 1 : 1);
 117}
 118
 119/* x86_quirks member */
 120static int mpc_apic_id(struct mpc_config_processor *m)
 121{
 122        int quad = translation_table[mpc_record]->trans_quad;
 123        int logical_apicid = generate_logical_apicid(quad, m->mpc_apicid);
 124
 125        printk(KERN_DEBUG "Processor #%d %u:%u APIC version %d (quad %d, apic %d)\n",
 126               m->mpc_apicid,
 127               (m->mpc_cpufeature & CPU_FAMILY_MASK) >> 8,
 128               (m->mpc_cpufeature & CPU_MODEL_MASK) >> 4,
 129               m->mpc_apicver, quad, logical_apicid);
 130        return logical_apicid;
 131}
 132
 133int mp_bus_id_to_node[MAX_MP_BUSSES];
 134
 135int mp_bus_id_to_local[MAX_MP_BUSSES];
 136
 137/* x86_quirks member */
 138static void mpc_oem_bus_info(struct mpc_config_bus *m, char *name)
 139{
 140        int quad = translation_table[mpc_record]->trans_quad;
 141        int local = translation_table[mpc_record]->trans_local;
 142
 143        mp_bus_id_to_node[m->mpc_busid] = quad;
 144        mp_bus_id_to_local[m->mpc_busid] = local;
 145        printk(KERN_INFO "Bus #%d is %s (node %d)\n",
 146               m->mpc_busid, name, quad);
 147}
 148
 149int quad_local_to_mp_bus_id [NR_CPUS/4][4];
 150
 151/* x86_quirks member */
 152static void mpc_oem_pci_bus(struct mpc_config_bus *m)
 153{
 154        int quad = translation_table[mpc_record]->trans_quad;
 155        int local = translation_table[mpc_record]->trans_local;
 156
 157        quad_local_to_mp_bus_id[quad][local] = m->mpc_busid;
 158}
 159
 160static void __init MP_translation_info(struct mpc_config_translation *m)
 161{
 162        printk(KERN_INFO
 163               "Translation: record %d, type %d, quad %d, global %d, local %d\n",
 164               mpc_record, m->trans_type, m->trans_quad, m->trans_global,
 165               m->trans_local);
 166
 167        if (mpc_record >= MAX_MPC_ENTRY)
 168                printk(KERN_ERR "MAX_MPC_ENTRY exceeded!\n");
 169        else
 170                translation_table[mpc_record] = m;      /* stash this for later */
 171        if (m->trans_quad < MAX_NUMNODES && !node_online(m->trans_quad))
 172                node_set_online(m->trans_quad);
 173}
 174
 175static int __init mpf_checksum(unsigned char *mp, int len)
 176{
 177        int sum = 0;
 178
 179        while (len--)
 180                sum += *mp++;
 181
 182        return sum & 0xFF;
 183}
 184
 185/*
 186 * Read/parse the MPC oem tables
 187 */
 188
 189static void __init smp_read_mpc_oem(struct mp_config_oemtable *oemtable,
 190                                    unsigned short oemsize)
 191{
 192        int count = sizeof(*oemtable);  /* the header size */
 193        unsigned char *oemptr = ((unsigned char *)oemtable) + count;
 194
 195        mpc_record = 0;
 196        printk(KERN_INFO "Found an OEM MPC table at %8p - parsing it ... \n",
 197               oemtable);
 198        if (memcmp(oemtable->oem_signature, MPC_OEM_SIGNATURE, 4)) {
 199                printk(KERN_WARNING
 200                       "SMP mpc oemtable: bad signature [%c%c%c%c]!\n",
 201                       oemtable->oem_signature[0], oemtable->oem_signature[1],
 202                       oemtable->oem_signature[2], oemtable->oem_signature[3]);
 203                return;
 204        }
 205        if (mpf_checksum((unsigned char *)oemtable, oemtable->oem_length)) {
 206                printk(KERN_WARNING "SMP oem mptable: checksum error!\n");
 207                return;
 208        }
 209        while (count < oemtable->oem_length) {
 210                switch (*oemptr) {
 211                case MP_TRANSLATION:
 212                        {
 213                                struct mpc_config_translation *m =
 214                                    (struct mpc_config_translation *)oemptr;
 215                                MP_translation_info(m);
 216                                oemptr += sizeof(*m);
 217                                count += sizeof(*m);
 218                                ++mpc_record;
 219                                break;
 220                        }
 221                default:
 222                        {
 223                                printk(KERN_WARNING
 224                                       "Unrecognised OEM table entry type! - %d\n",
 225                                       (int)*oemptr);
 226                                return;
 227                        }
 228                }
 229        }
 230}
 231
 232static int __init numaq_setup_ioapic_ids(void)
 233{
 234        /* so can skip it */
 235        return 1;
 236}
 237
 238static struct x86_quirks numaq_x86_quirks __initdata = {
 239        .arch_pre_time_init     = numaq_pre_time_init,
 240        .arch_time_init         = NULL,
 241        .arch_pre_intr_init     = NULL,
 242        .arch_memory_setup      = NULL,
 243        .arch_intr_init         = NULL,
 244        .arch_trap_init         = NULL,
 245        .mach_get_smp_config    = NULL,
 246        .mach_find_smp_config   = NULL,
 247        .mpc_record             = &mpc_record,
 248        .mpc_apic_id            = mpc_apic_id,
 249        .mpc_oem_bus_info       = mpc_oem_bus_info,
 250        .mpc_oem_pci_bus        = mpc_oem_pci_bus,
 251        .smp_read_mpc_oem       = smp_read_mpc_oem,
 252        .setup_ioapic_ids       = numaq_setup_ioapic_ids,
 253};
 254
 255void numaq_mps_oem_check(struct mp_config_table *mpc, char *oem,
 256                                 char *productid)
 257{
 258        if (strncmp(oem, "IBM NUMA", 8))
 259                printk("Warning!  Not a NUMA-Q system!\n");
 260        else
 261                found_numaq = 1;
 262}
 263
 264static __init void early_check_numaq(void)
 265{
 266        /*
 267         * Find possible boot-time SMP configuration:
 268         */
 269        early_find_smp_config();
 270        /*
 271         * get boot-time SMP configuration:
 272         */
 273        if (smp_found_config)
 274                early_get_smp_config();
 275
 276        if (found_numaq)
 277                x86_quirks = &numaq_x86_quirks;
 278}
 279
 280int __init get_memcfg_numaq(void)
 281{
 282        early_check_numaq();
 283        if (!found_numaq)
 284                return 0;
 285        smp_dump_qct();
 286        return 1;
 287}
 288
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.