Merge ../linus
[linux-2.6] / arch / x86_64 / mm / k8topology.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, nb; 
48         unsigned char nodeids[8];
49         int found = 0;
50         u32 reg;
51         unsigned numnodes;
52         nodemask_t nodes_parsed;
53         unsigned dualcore = 0;
54
55         nodes_clear(nodes_parsed);
56
57         if (!early_pci_allowed())
58                 return -1;
59
60         nb = find_northbridge(); 
61         if (nb < 0) 
62                 return nb;
63
64         printk(KERN_INFO "Scanning NUMA topology in Northbridge %d\n", nb); 
65
66         reg = read_pci_config(0, nb, 0, 0x60); 
67         numnodes = ((reg >> 4) & 0xF) + 1;
68
69         printk(KERN_INFO "Number of nodes %d\n", numnodes);
70
71         memset(&nodes,0,sizeof(nodes)); 
72         prevbase = 0;
73         for (i = 0; i < 8; i++) { 
74                 unsigned long base,limit; 
75                 u32 nodeid;
76                 
77                 /* Undefined before E stepping, but hopefully 0 */
78                 dualcore |= ((read_pci_config(0, nb, 3, 0xe8) >> 12) & 3) == 1;
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, nodes_parsed)) { 
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, nodes_parsed);
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                         apicid_to_node[nodeid << dualcore] = i;
175                         apicid_to_node[(nodeid << dualcore) + dualcore] = i;
176                         setup_node_bootmem(i, nodes[i].start, nodes[i].end); 
177                 } 
178         }
179
180         numa_init_array();
181         return 0;
182