numaq_32.c 7.4 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 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
/*
 * Written by: Patricia Gaughen, IBM Corporation
 *
 * Copyright (C) 2002, IBM Corp.
 *
 * All rights reserved.          
 *
 * 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, GOOD TITLE or
 * NON INFRINGEMENT.  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Send feedback to <gone@us.ibm.com>
 */

#include <linux/mm.h>
#include <linux/bootmem.h>
#include <linux/mmzone.h>
#include <linux/module.h>
#include <linux/nodemask.h>
#include <asm/numaq.h>
#include <asm/topology.h>
D
Dave Hansen 已提交
33
#include <asm/processor.h>
34
#include <asm/genapic.h>
35
#include <asm/e820.h>
36
#include <asm/setup.h>
L
Linus Torvalds 已提交
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

#define	MB_TO_PAGES(addr) ((addr) << (20 - PAGE_SHIFT))

/*
 * Function: smp_dump_qct()
 *
 * Description: gets memory layout from the quad config table.  This
 * function also updates node_online_map with the nodes (quads) present.
 */
static void __init smp_dump_qct(void)
{
	int node;
	struct eachquadmem *eq;
	struct sys_cfg_data *scd =
		(struct sys_cfg_data *)__va(SYS_CFG_DATA_PRIV_ADDR);

	nodes_clear(node_online_map);
	for_each_node(node) {
		if (scd->quads_present31_0 & (1 << node)) {
			node_set_online(node);
			eq = &scd->eq[node];
			/* Convert to pages */
			node_start_pfn[node] = MB_TO_PAGES(
				eq->hi_shrd_mem_start - eq->priv_mem_size);
			node_end_pfn[node] = MB_TO_PAGES(
				eq->hi_shrd_mem_start + eq->hi_shrd_mem_size);

64 65
			e820_register_active_regions(node, node_start_pfn[node],
							node_end_pfn[node]);
L
Linus Torvalds 已提交
66 67 68 69 70 71 72 73 74
			memory_present(node,
				node_start_pfn[node], node_end_pfn[node]);
			node_remap_size[node] = node_memmap_size_bytes(node,
							node_start_pfn[node],
							node_end_pfn[node]);
		}
	}
}

75

76
void __cpuinit numaq_tsc_disable(void)
77 78 79 80 81 82 83 84 85 86
{
	if (!found_numaq)
		return;

	if (num_online_nodes() > 1) {
		printk(KERN_DEBUG "NUMAQ: disabling TSC\n");
		setup_clear_cpu_cap(X86_FEATURE_TSC);
	}
}

87 88 89 90 91 92
static int __init numaq_pre_time_init(void)
{
	numaq_tsc_disable();
	return 0;
}

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
int found_numaq;
/*
 * Have to match translation table entries to main table entries by counter
 * hence the mpc_record variable .... can't see a less disgusting way of
 * doing this ....
 */
struct mpc_config_translation {
	unsigned char mpc_type;
	unsigned char trans_len;
	unsigned char trans_type;
	unsigned char trans_quad;
	unsigned char trans_global;
	unsigned char trans_local;
	unsigned short trans_reserved;
};

/* x86_quirks member */
static int mpc_record;
static struct mpc_config_translation *translation_table[MAX_MPC_ENTRY]
    __cpuinitdata;

static inline int generate_logical_apicid(int quad, int phys_apicid)
{
	return (quad << 4) + (phys_apicid ? phys_apicid << 1 : 1);
}

/* x86_quirks member */
120
static int mpc_apic_id(struct mpc_cpu *m)
121 122
{
	int quad = translation_table[mpc_record]->trans_quad;
123
	int logical_apicid = generate_logical_apicid(quad, m->apicid);
124 125

	printk(KERN_DEBUG "Processor #%d %u:%u APIC version %d (quad %d, apic %d)\n",
126 127 128
	       m->apicid, (m->cpufeature & CPU_FAMILY_MASK) >> 8,
	       (m->cpufeature & CPU_MODEL_MASK) >> 4,
	       m->apicver, quad, logical_apicid);
129 130 131 132 133 134 135 136
	return logical_apicid;
}

int mp_bus_id_to_node[MAX_MP_BUSSES];

int mp_bus_id_to_local[MAX_MP_BUSSES];

/* x86_quirks member */
137
static void mpc_oem_bus_info(struct mpc_bus *m, char *name)
138 139 140 141
{
	int quad = translation_table[mpc_record]->trans_quad;
	int local = translation_table[mpc_record]->trans_local;

142 143
	mp_bus_id_to_node[m->busid] = quad;
	mp_bus_id_to_local[m->busid] = local;
144
	printk(KERN_INFO "Bus #%d is %s (node %d)\n",
145
	       m->busid, name, quad);
146 147 148 149 150
}

int quad_local_to_mp_bus_id [NR_CPUS/4][4];

/* x86_quirks member */
151
static void mpc_oem_pci_bus(struct mpc_bus *m)
152 153 154 155
{
	int quad = translation_table[mpc_record]->trans_quad;
	int local = translation_table[mpc_record]->trans_local;

156
	quad_local_to_mp_bus_id[quad][local] = m->busid;
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
}

static void __init MP_translation_info(struct mpc_config_translation *m)
{
	printk(KERN_INFO
	       "Translation: record %d, type %d, quad %d, global %d, local %d\n",
	       mpc_record, m->trans_type, m->trans_quad, m->trans_global,
	       m->trans_local);

	if (mpc_record >= MAX_MPC_ENTRY)
		printk(KERN_ERR "MAX_MPC_ENTRY exceeded!\n");
	else
		translation_table[mpc_record] = m;	/* stash this for later */
	if (m->trans_quad < MAX_NUMNODES && !node_online(m->trans_quad))
		node_set_online(m->trans_quad);
}

static int __init mpf_checksum(unsigned char *mp, int len)
{
	int sum = 0;

	while (len--)
		sum += *mp++;

	return sum & 0xFF;
}

/*
 * Read/parse the MPC oem tables
 */

188
static void __init smp_read_mpc_oem(struct mpc_oemtable *oemtable,
189 190 191 192 193 194 195 196
				    unsigned short oemsize)
{
	int count = sizeof(*oemtable);	/* the header size */
	unsigned char *oemptr = ((unsigned char *)oemtable) + count;

	mpc_record = 0;
	printk(KERN_INFO "Found an OEM MPC table at %8p - parsing it ... \n",
	       oemtable);
197
	if (memcmp(oemtable->signature, MPC_OEM_SIGNATURE, 4)) {
198 199
		printk(KERN_WARNING
		       "SMP mpc oemtable: bad signature [%c%c%c%c]!\n",
200 201
		       oemtable->signature[0], oemtable->signature[1],
		       oemtable->signature[2], oemtable->signature[3]);
202 203
		return;
	}
204
	if (mpf_checksum((unsigned char *)oemtable, oemtable->length)) {
205 206 207
		printk(KERN_WARNING "SMP oem mptable: checksum error!\n");
		return;
	}
208
	while (count < oemtable->length) {
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
		switch (*oemptr) {
		case MP_TRANSLATION:
			{
				struct mpc_config_translation *m =
				    (struct mpc_config_translation *)oemptr;
				MP_translation_info(m);
				oemptr += sizeof(*m);
				count += sizeof(*m);
				++mpc_record;
				break;
			}
		default:
			{
				printk(KERN_WARNING
				       "Unrecognised OEM table entry type! - %d\n",
				       (int)*oemptr);
				return;
			}
		}
	}
}

231 232 233 234 235 236
static int __init numaq_setup_ioapic_ids(void)
{
	/* so can skip it */
	return 1;
}

237 238 239 240 241 242 243
static int __init numaq_update_genapic(void)
{
	genapic->wakeup_cpu = wakeup_secondary_cpu_via_nmi;

	return 0;
}

244
static struct x86_quirks numaq_x86_quirks __initdata = {
245
	.arch_pre_time_init	= numaq_pre_time_init,
246 247 248 249 250 251 252 253 254 255 256 257
	.arch_time_init		= NULL,
	.arch_pre_intr_init	= NULL,
	.arch_memory_setup	= NULL,
	.arch_intr_init		= NULL,
	.arch_trap_init		= NULL,
	.mach_get_smp_config	= NULL,
	.mach_find_smp_config	= NULL,
	.mpc_record		= &mpc_record,
	.mpc_apic_id		= mpc_apic_id,
	.mpc_oem_bus_info	= mpc_oem_bus_info,
	.mpc_oem_pci_bus	= mpc_oem_pci_bus,
	.smp_read_mpc_oem	= smp_read_mpc_oem,
258
	.setup_ioapic_ids	= numaq_setup_ioapic_ids,
259
	.update_genapic		= numaq_update_genapic,
260 261
};

262
void numaq_mps_oem_check(struct mpc_table *mpc, char *oem, char *productid)
263 264 265 266 267 268 269
{
	if (strncmp(oem, "IBM NUMA", 8))
		printk("Warning!  Not a NUMA-Q system!\n");
	else
		found_numaq = 1;
}

270 271 272 273 274 275 276 277 278 279 280
static __init void early_check_numaq(void)
{
	/*
	 * Find possible boot-time SMP configuration:
	 */
	early_find_smp_config();
	/*
	 * get boot-time SMP configuration:
	 */
	if (smp_found_config)
		early_get_smp_config();
281 282 283

	if (found_numaq)
		x86_quirks = &numaq_x86_quirks;
284 285
}

L
Linus Torvalds 已提交
286 287
int __init get_memcfg_numaq(void)
{
288 289 290
	early_check_numaq();
	if (!found_numaq)
		return 0;
L
Linus Torvalds 已提交
291 292 293
	smp_dump_qct();
	return 1;
}