Merge git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc
[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         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         reg = read_pci_config(0, nb, 0, 0x60); 
64         numnodes = ((reg >> 4) & 0xF) + 1;
65
66         printk(KERN_INFO "Number of nodes %d\n", numnodes);
67
68         memset(&nodes,0,sizeof(nodes)); 
69         prevbase = 0;
70         for (i = 0; i < 8; i++) { 
71                 unsigned long base,limit; 
72                 u32 nodeid;
73                 
74                 /* Undefined before E stepping, but hopefully 0 */
75                 dualcore |= ((read_pci_config(0, nb, 3, 0xe8) >> 12) & 3) == 1;
76                 base = read_pci_config(0, nb, 1, 0x40 + i*8);
77                 limit = read_pci_config(0, nb, 1, 0x44 + i*8);
78
79                 nodeid = limit & 7; 
80                 nodeids[i] = nodeid;
81                 if ((base & 3) == 0) { 
82                         if (i < numnodes)
83                                 printk("Skipping disabled node %d\n", i); 
84                         continue;
85                 } 
86                 if (nodeid >= numnodes) {
87                         printk("Ignoring excess node %d (%lx:%lx)\n", nodeid,
88                                base, limit); 
89                         continue;
90                 } 
91
92                 if (!limit) { 
93                         printk(KERN_INFO "Skipping node entry %d (base %lx)\n", i,
94                                base);
95                         continue;
96                 }
97                 if ((base >> 8) & 3 || (limit >> 8) & 3) {
98                         printk(KERN_ERR "Node %d using interleaving mode %lx/%lx\n", 
99                                nodeid, (base>>8)&3, (limit>>8) & 3); 
100                         return -1; 
101                 }       
102                 if (node_isset(nodeid, nodes_parsed)) { 
103                         printk(KERN_INFO "Node %d already present. Skipping\n", 
104                                nodeid);
105                         continue;
106                 }
107
108                 limit >>= 16; 
109                 limit <<= 24; 
110                 limit |= (1<<24)-1;
111                 limit++;
112
113                 if (limit > end_pfn << PAGE_SHIFT)
114                         limit = end_pfn << PAGE_SHIFT;
115                 if (limit <= base)
116                         continue; 
117                         
118                 base >>= 16;
119                 base <<= 24; 
120
121                 if (base < start) 
122                         base = start; 
123                 if (limit > end) 
124                         limit = end; 
125                 if (limit == base) { 
126                         printk(KERN_ERR "Empty node %d\n", nodeid); 
127                         continue; 
128                 }
129                 if (limit < base) { 
130                         printk(KERN_ERR "Node %d bogus settings %lx-%lx.\n",
131                                nodeid, base, limit);                           
132                         continue;
133                 } 
134                 
135                 /* Could sort here, but pun for now. Should not happen anyroads. */
136                 if (prevbase > base) { 
137                         printk(KERN_ERR "Node map not sorted %lx,%lx\n",
138                                prevbase,base);
139                         return -1;
140                 }
141                         
142                 printk(KERN_INFO "Node %d MemBase %016lx Limit %016lx\n", 
143                        nodeid, base, limit); 
144                 
145                 found++;
146                 
147                 nodes[nodeid].start = base; 
148                 nodes[nodeid].end = limit;
149
150                 prevbase = base;
151
152                 node_set(nodeid, nodes_parsed);
153         } 
154
155         if (!found)
156                 return -1; 
157
158         memnode_shift = compute_hash_shift(nodes, 8);
159         if (memnode_shift < 0) { 
160                 printk(KERN_ERR "No NUMA node hash function found. Contact maintainer\n"); 
161                 return -1; 
162         } 
163         printk(KERN_INFO "Using node hash shift of %d\n", memnode_shift); 
164
165         for (i = 0; i < 8; i++) {
166                 if (nodes[i].start != nodes[i].end) { 
167                         nodeid = nodeids[i];
168                         apicid_to_node[nodeid << dualcore] = i;
169                         apicid_to_node[(nodeid << dualcore) + dualcore] = i;
170                         setup_node_bootmem(i, nodes[i].start, nodes[i].end); 
171                 } 
172         }
173
174         numa_init_array();
175         return 0;
176