Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc
[linux-2.6] / arch / x86 / mm / k8topology_64.c
1 /* 
2  * AMD K8 NUMA support.
3  * Discover the memory map and associated nodes.
4  * 
5  * This version reads it directly from the K8 northbridge.
6  * 
7  * Copyright 2002,2003 Andi Kleen, SuSE Labs.
8  */
9 #include <linux/kernel.h>
10 #include <linux/init.h>
11 #include <linux/string.h>
12 #include <linux/module.h>
13 #include <linux/nodemask.h>
14 #include <asm/io.h>
15 #include <linux/pci_ids.h>
16 #include <asm/types.h>
17 #include <asm/mmzone.h>
18 #include <asm/proto.h>
19 #include <asm/e820.h>
20 #include <asm/pci-direct.h>
21 #include <asm/numa.h>
22
23 static __init int find_northbridge(void)
24 {
25         int num; 
26
27         for (num = 0; num < 32; num++) { 
28                 u32 header;
29                 
30                 header = read_pci_config(0, num, 0, 0x00);  
31                 if (header != (PCI_VENDOR_ID_AMD | (0x1100<<16)))
32                         continue;       
33
34                 header = read_pci_config(0, num, 1, 0x00); 
35                 if (header != (PCI_VENDOR_ID_AMD | (0x1101<<16)))
36                         continue;       
37                 return num; 
38         } 
39
40         return -1;      
41 }
42
43 int __init k8_scan_nodes(unsigned long start, unsigned long end)
44
45         unsigned long prevbase;
46         struct bootnode nodes[8];
47         int nodeid, i, j, nb;
48         unsigned char nodeids[8];
49         int found = 0;
50         u32 reg;
51         unsigned numnodes;
52         unsigned num_cores;
53
54         if (!early_pci_allowed())
55                 return -1;
56
57         nb = find_northbridge(); 
58         if (nb < 0) 
59                 return nb;
60
61         printk(KERN_INFO "Scanning NUMA topology in Northbridge %d\n", nb); 
62
63         num_cores = (cpuid_ecx(0x80000008) & 0xff) + 1;
64         printk(KERN_INFO "CPU has %d num_cores\n", num_cores);
65
66         reg = read_pci_config(0, nb, 0, 0x60); 
67         numnodes = ((reg >> 4) & 0xF) + 1;
68         if (numnodes <= 1)
69                 return -1;
70
71         printk(KERN_INFO "Number of nodes %d\n", numnodes);
72
73         memset(&nodes,0,sizeof(nodes)); 
74         prevbase = 0;
75         for (i = 0; i < 8; i++) { 
76                 unsigned long base,limit; 
77                 u32 nodeid;
78                 
79                 base = read_pci_config(0, nb, 1, 0x40 + i*8);
80                 limit = read_pci_config(0, nb, 1, 0x44 + i*8);
81
82                 nodeid = limit & 7; 
83                 nodeids[i] = nodeid;
84                 if ((base & 3) == 0) { 
85                         if (i < numnodes)
86                                 printk("Skipping disabled node %d\n", i); 
87                         continue;
88                 } 
89                 if (nodeid >= numnodes) {
90                         printk("Ignoring excess node %d (%lx:%lx)\n", nodeid,
91                                base, limit); 
92                         continue;
93                 } 
94
95                 if (!limit) { 
96                         printk(KERN_INFO "Skipping node entry %d (base %lx)\n", i,
97                                base);
98                         continue;
99                 }
100                 if ((base >> 8) & 3 || (limit >> 8) & 3) {
101                         printk(KERN_ERR "Node %d using interleaving mode %lx/%lx\n", 
102                                nodeid, (base>>8)&3, (limit>>8) & 3); 
103                         return -1; 
104                 }       
105                 if (node_isset(nodeid, node_possible_map)) {
106                         printk(KERN_INFO "Node %d already present. Skipping\n", 
107                                nodeid);
108                         continue;
109                 }
110
111                 limit >>= 16; 
112                 limit <<= 24; 
113                 limit |= (1<<24)-1;
114                 limit++;
115
116                 if (limit > end_pfn << PAGE_SHIFT)
117                         limit = end_pfn << PAGE_SHIFT;
118                 if (limit <= base)
119                         continue; 
120                         
121                 base >>= 16;
122                 base <<= 24; 
123
124                 if (base < start) 
125                         base = start; 
126                 if (limit > end) 
127                         limit = end; 
128                 if (limit == base) { 
129                         printk(KERN_ERR "Empty node %d\n", nodeid); 
130                         continue; 
131                 }
132                 if (limit < base) { 
133                         printk(KERN_ERR "Node %d bogus settings %lx-%lx.\n",
134                                nodeid, base, limit);                           
135                         continue;
136                 } 
137                 
138                 /* Could sort here, but pun for now. Should not happen anyroads. */
139                 if (prevbase > base) { 
140                         printk(KERN_ERR "Node map not sorted %lx,%lx\n",
141                                prevbase,base);
142                         return -1;
143                 }
144                         
145                 printk(KERN_INFO "Node %d MemBase %016lx Limit %016lx\n", 
146                        nodeid, base, limit); 
147                 
148                 found++;
149                 
150                 nodes[nodeid].start = base; 
151                 nodes[nodeid].end = limit;
152                 e820_register_active_regions(nodeid,
153                                 nodes[nodeid].start >> PAGE_SHIFT,
154                                 nodes[nodeid].end >> PAGE_SHIFT);
155
156                 prevbase = base;
157
158                 node_set(nodeid, node_possible_map);
159         } 
160
161         if (!found)
162                 return -1; 
163
164         memnode_shift = compute_hash_shift(nodes, 8);
165         if (memnode_shift < 0) { 
166                 printk(KERN_ERR "No NUMA node hash function found. Contact maintainer\n"); 
167                 return -1; 
168         } 
169         printk(KERN_INFO "Using node hash shift of %d\n", memnode_shift); 
170
171         for (i = 0; i < 8; i++) {
172                 if (nodes[i].start != nodes[i].end) { 
173                         nodeid = nodeids[i];
174                         for (j = 0; j < num_cores; j++)
175                                 apicid_to_node[(nodeid * num_cores) + j] = i;
176                         setup_node_bootmem(i, nodes[i].start, nodes[i].end); 
177                 } 
178         }
179
180         numa_init_array();
181         return 0;
182