From: Matt Dobson, via Martin Bligh This parses the machine's BIOS tables to populate the mp_bus_id_to_node[bus] array. Only affects Summit machines, safe, boring. Has been in -mjb tree for ages, and works fine. arch/i386/kernel/Makefile | 1 arch/i386/kernel/setup.c | 3 arch/i386/kernel/summit.c | 162 ++++++++++++++++++++++++++++ include/asm-i386/mach-summit/mach_mpparse.h | 69 +++++++++++ include/asm-i386/mpspec.h | 4 5 files changed, 239 insertions(+) diff -puN arch/i386/kernel/Makefile~summit-bus-to-node-mapping arch/i386/kernel/Makefile --- 25/arch/i386/kernel/Makefile~summit-bus-to-node-mapping 2003-06-07 14:20:49.000000000 -0700 +++ 25-akpm/arch/i386/kernel/Makefile 2003-06-07 14:20:49.000000000 -0700 @@ -27,6 +27,7 @@ obj-$(CONFIG_X86_LOCAL_APIC) += apic.o n obj-$(CONFIG_X86_IO_APIC) += io_apic.o obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend.o suspend_asm.o obj-$(CONFIG_X86_NUMAQ) += numaq.o +obj-$(CONFIG_X86_SUMMIT) += summit.o obj-$(CONFIG_EDD) += edd.o obj-$(CONFIG_MODULES) += module.o obj-y += sysenter.o vsyscall.o diff -puN arch/i386/kernel/setup.c~summit-bus-to-node-mapping arch/i386/kernel/setup.c --- 25/arch/i386/kernel/setup.c~summit-bus-to-node-mapping 2003-06-07 14:20:49.000000000 -0700 +++ 25-akpm/arch/i386/kernel/setup.c 2003-06-07 14:20:49.000000000 -0700 @@ -980,6 +980,9 @@ void __init setup_arch(char **cmdline_p) if (smp_found_config) get_smp_config(); #endif +#ifdef CONFIG_X86_SUMMIT + setup_summit(); +#endif register_memory(max_low_pfn); diff -puN /dev/null arch/i386/kernel/summit.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/arch/i386/kernel/summit.c 2003-06-07 14:20:49.000000000 -0700 @@ -0,0 +1,162 @@ +/* + * arch/i386/kernel/summit.c - IBM Summit-Specific Code + * + * Written By: Matthew Dobson, IBM Corporation + * + * Copyright (c) 2003 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 + * + */ + +#include +#include +#include +#include + +static void __init setup_pci_node_map_for_wpeg(int wpeg_num, struct rio_table_hdr *rth, + struct scal_detail **scal_nodes, struct rio_detail **rio_nodes){ + int twst_num = 0, node = 0, first_bus = 0; + int i, bus, num_busses; + + for(i = 0; i < rth->num_rio_dev; i++){ + if (rio_nodes[i]->node_id == rio_nodes[wpeg_num]->owner_id){ + twst_num = rio_nodes[i]->owner_id; + break; + } + } + if (i == rth->num_rio_dev){ + printk("%s: Couldn't find owner Cyclone for Winnipeg!\n", __FUNCTION__); + return; + } + + for(i = 0; i < rth->num_scal_dev; i++){ + if (scal_nodes[i]->node_id == twst_num){ + node = scal_nodes[i]->node_id; + break; + } + } + if (i == rth->num_scal_dev){ + printk("%s: Couldn't find owner Twister for Cyclone!\n", __FUNCTION__); + return; + } + + switch (rio_nodes[wpeg_num]->type){ + case CompatWPEG: + /* The Compatability Winnipeg controls the legacy busses + (busses 0 & 1), the 66MHz PCI bus [2 slots] (bus 2), + and the "extra" busses in case a PCI-PCI bridge card is + used in either slot (busses 3 & 4): total 5 busses. */ + num_busses = 5; + /* The BIOS numbers the busses starting at 1, and in a + slightly wierd manner. You'll have to trust that + the math used below to determine the number of the + first bus works. */ + first_bus = (rio_nodes[wpeg_num]->first_slot - 1) * 2; + break; + case AltWPEG: + /* The Alternate/Secondary Winnipeg controls the 1st 133MHz + bus [1 slot] & its "extra" bus (busses 0 & 1), the 2nd + 133MHz bus [1 slot] & its "extra" bus (busses 2 & 3), the + 100MHz bus [2 slots] (bus 4), and the "extra" busses for + the 2 100MHz slots (busses 5 & 6): total 7 busses. */ + num_busses = 7; + first_bus = (rio_nodes[wpeg_num]->first_slot * 2) - 1; + break; + case LookOutAWPEG: + case LookOutBWPEG: + printk("%s: LookOut Winnipegs not supported yet!\n", __FUNCTION__); + return; + default: + printk("%s: Unsupported Winnipeg type!\n", __FUNCTION__); + return; + } + + for(bus = first_bus; bus < first_bus + num_busses; bus++) + mp_bus_id_to_node[bus] = node; +} + +static void __init build_detail_arrays(struct rio_table_hdr *rth, + struct scal_detail **sd, struct rio_detail **rd){ + unsigned long ptr; + int i, scal_detail_size, rio_detail_size; + + switch (rth->version){ + default: + printk("%s: Bad Rio Grande Table Version: %d\n", __FUNCTION__, rth->version); + /* Fall through to default to version 2 spec */ + case 2: + scal_detail_size = 11; + rio_detail_size = 13; + break; + case 3: + scal_detail_size = 12; + rio_detail_size = 15; + break; + } + + ptr = (unsigned long)rth + 3; + for(i = 0; i < rth->num_scal_dev; i++) + sd[i] = (struct scal_detail *)(ptr + (scal_detail_size * i)); + + ptr += scal_detail_size * rth->num_scal_dev; + for(i = 0; i < rth->num_rio_dev; i++) + rd[i] = (struct rio_detail *)(ptr + (rio_detail_size * i)); +} + +void __init setup_summit(void) +{ + struct rio_table_hdr *rio_table_hdr = NULL; + struct scal_detail *scal_devs[MAX_NUMNODES]; + struct rio_detail *rio_devs[MAX_NUMNODES*2]; + unsigned long ptr; + unsigned short offset; + int i; + + memset(mp_bus_id_to_node, -1, sizeof(mp_bus_id_to_node)); + + /* The pointer to the EBDA is stored in the word @ phys 0x40E(40:0E) */ + ptr = *(unsigned short *)phys_to_virt(0x40Eul); + ptr = (unsigned long)phys_to_virt(ptr << 4); + + offset = 0x180; + while (offset){ + /* The block id is stored in the 2nd word */ + if (*((unsigned short *)(ptr + offset + 2)) == 0x4752){ + /* set the pointer past the offset & block id */ + rio_table_hdr = (struct rio_table_hdr *)(ptr + offset + 4); + break; + } + /* The next offset is stored in the 1st word. 0 means no more */ + offset = *((unsigned short *)(ptr + offset)); + } + if (!rio_table_hdr){ + printk("%s: Unable to locate Rio Grande Table in EBDA - bailing!\n", __FUNCTION__); + return; + } + + /* Deal with the ugly version 2/3 pointer arithmetic */ + build_detail_arrays(rio_table_hdr, scal_devs, rio_devs); + + for(i = 0; i < rio_table_hdr->num_rio_dev; i++) + if (is_WPEG(rio_devs[i]->type)) + /* It's a Winnipeg, it's got PCI Busses */ + setup_pci_node_map_for_wpeg(i, rio_table_hdr, scal_devs, rio_devs); +} diff -puN include/asm-i386/mach-summit/mach_mpparse.h~summit-bus-to-node-mapping include/asm-i386/mach-summit/mach_mpparse.h --- 25/include/asm-i386/mach-summit/mach_mpparse.h~summit-bus-to-node-mapping 2003-06-07 14:20:49.000000000 -0700 +++ 25-akpm/include/asm-i386/mach-summit/mach_mpparse.h 2003-06-07 14:20:49.000000000 -0700 @@ -1,6 +1,8 @@ #ifndef __ASM_MACH_MPPARSE_H #define __ASM_MACH_MPPARSE_H +#include + extern int use_cyclone; static inline void mpc_oem_bus_info(struct mpc_config_bus *m, char *name, @@ -38,4 +40,71 @@ static inline int acpi_madt_oem_check(ch } return 0; } + +struct rio_table_hdr { + unsigned char version; /* Version number of this data structure */ + /* Version 3 adds chassis_num & WP_index */ + unsigned char num_scal_dev; /* # of Scalability devices (Twisters for Vigil) */ + unsigned char num_rio_dev; /* # of RIO I/O devices (Cyclones and Winnipegs) */ +} __attribute__((packed)); + +struct scal_detail { + unsigned char node_id; /* Scalability Node ID */ + unsigned long CBAR; /* Address of 1MB register space */ + unsigned char port0node; /* Node ID port connected to: 0xFF=None */ + unsigned char port0port; /* Port num port connected to: 0,1,2, or 0xFF=None */ + unsigned char port1node; /* Node ID port connected to: 0xFF = None */ + unsigned char port1port; /* Port num port connected to: 0,1,2, or 0xFF=None */ + unsigned char port2node; /* Node ID port connected to: 0xFF = None */ + unsigned char port2port; /* Port num port connected to: 0,1,2, or 0xFF=None */ + unsigned char chassis_num; /* 1 based Chassis number (1 = boot node) */ +} __attribute__((packed)); + +struct rio_detail { + unsigned char node_id; /* RIO Node ID */ + unsigned long BBAR; /* Address of 1MB register space */ + unsigned char type; /* Type of device */ + unsigned char owner_id; /* For WPEG: Node ID of Cyclone that owns this WPEG*/ + /* For CYC: Node ID of Twister that owns this CYC */ + unsigned char port0node; /* Node ID port connected to: 0xFF=None */ + unsigned char port0port; /* Port num port connected to: 0,1,2, or 0xFF=None */ + unsigned char port1node; /* Node ID port connected to: 0xFF=None */ + unsigned char port1port; /* Port num port connected to: 0,1,2, or 0xFF=None */ + unsigned char first_slot; /* For WPEG: Lowest slot number below this WPEG */ + /* For CYC: 0 */ + unsigned char status; /* For WPEG: Bit 0 = 1 : the XAPIC is used */ + /* = 0 : the XAPIC is not used, ie:*/ + /* ints fwded to another XAPIC */ + /* Bits1:7 Reserved */ + /* For CYC: Bits0:7 Reserved */ + unsigned char WP_index; /* For WPEG: WPEG instance index - lower ones have */ + /* lower slot numbers/PCI bus numbers */ + /* For CYC: No meaning */ + unsigned char chassis_num; /* 1 based Chassis number */ + /* For LookOut WPEGs this field indicates the */ + /* Expansion Chassis #, enumerated from Boot */ + /* Node WPEG external port, then Boot Node CYC */ + /* external port, then Next Vigil chassis WPEG */ + /* external port, etc. */ + /* Shared Lookouts have only 1 chassis number (the */ + /* first one assigned) */ +} __attribute__((packed)); + + +typedef enum { + CompatTwister = 0, /* Compatibility Twister */ + AltTwister = 1, /* Alternate Twister of internal 8-way */ + CompatCyclone = 2, /* Compatibility Cyclone */ + AltCyclone = 3, /* Alternate Cyclone of internal 8-way */ + CompatWPEG = 4, /* Compatibility WPEG */ + AltWPEG = 5, /* Second Planar WPEG */ + LookOutAWPEG = 6, /* LookOut WPEG */ + LookOutBWPEG = 7, /* LookOut WPEG */ +} node_type; + +static inline int is_WPEG(node_type type){ + return (type == CompatWPEG || type == AltWPEG || + type == LookOutAWPEG || type == LookOutBWPEG); +} + #endif /* __ASM_MACH_MPPARSE_H */ diff -puN include/asm-i386/mpspec.h~summit-bus-to-node-mapping include/asm-i386/mpspec.h --- 25/include/asm-i386/mpspec.h~summit-bus-to-node-mapping 2003-06-07 14:20:49.000000000 -0700 +++ 25-akpm/include/asm-i386/mpspec.h 2003-06-07 14:20:49.000000000 -0700 @@ -222,6 +222,10 @@ extern unsigned long mp_lapic_addr; extern int pic_mode; extern int using_apic_timer; +#ifdef CONFIG_X86_SUMMIT +extern void setup_summit (void); +#endif + #ifdef CONFIG_ACPI_BOOT extern void mp_register_lapic (u8 id, u8 enabled); extern void mp_register_lapic_address (u64 address); _