ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / x86_64 / mm / k8topology.c
1 /* 
2  * AMD K8 NUMA support.
3  * Discover the memory map and associated nodes.
4  * 
5  * Doesn't use the ACPI SRAT table because it has a questionable license.
6  * Instead the northbridge registers are read directly. 
7  * XXX in 2.5 we could use the generic SRAT code
8  * 
9  * Copyright 2002,2003 Andi Kleen, SuSE Labs.
10  */
11 #include <linux/kernel.h>
12 #include <linux/init.h>
13 #include <linux/string.h>
14 #include <linux/module.h>
15 #include <asm/io.h>
16 #include <linux/pci_ids.h>
17 #include <asm/types.h>
18 #include <asm/mmzone.h>
19 #include <asm/proto.h>
20 #include <asm/e820.h>
21 #include <asm/pci-direct.h>
22 #include <asm/numa.h>
23
24 static __init int find_northbridge(void)
25 {
26         int num; 
27
28         for (num = 0; num < 32; num++) { 
29                 u32 header;
30                 
31                 header = read_pci_config(0, num, 0, 0x00);  
32                 if (header != (PCI_VENDOR_ID_AMD | (0x1100<<16)))
33                         continue;       
34
35                 header = read_pci_config(0, num, 1, 0x00); 
36                 if (header != (PCI_VENDOR_ID_AMD | (0x1101<<16)))
37                         continue;       
38                 return num; 
39         } 
40
41         return -1;      
42 }
43
44 int __init k8_scan_nodes(unsigned long start, unsigned long end)
45
46         unsigned long prevbase;
47         struct node nodes[MAXNODE];
48         int nodeid, i, nb; 
49         int found = 0;
50         u32 reg;
51
52         nb = find_northbridge(); 
53         if (nb < 0) 
54                 return nb;
55
56         printk(KERN_INFO "Scanning NUMA topology in Northbridge %d\n", nb); 
57
58         reg = read_pci_config(0, nb, 0, 0x60); 
59         numnodes =  ((reg >> 4) & 7) + 1; 
60
61         printk(KERN_INFO "Number of nodes %d (%x)\n", numnodes, reg);
62
63         memset(&nodes,0,sizeof(nodes)); 
64         prevbase = 0;
65         for (i = 0; i < 8; i++) { 
66                 unsigned long base,limit; 
67
68                 base = read_pci_config(0, nb, 1, 0x40 + i*8);
69                 limit = read_pci_config(0, nb, 1, 0x44 + i*8);
70
71                 nodeid = limit & 7; 
72                 if ((base & 3) == 0) { 
73                         if (i < numnodes) 
74                                 printk("Skipping disabled node %d\n", i); 
75                         continue;
76                 } 
77                 if (nodeid >= numnodes) { 
78                         printk("Ignoring excess node %d (%lx:%lx)\n", nodeid,
79                                base, limit); 
80                         continue;
81                 } 
82
83                 if (!limit) { 
84                         printk(KERN_INFO "Skipping node entry %d (base %lx)\n", i,
85                                base);
86                         continue;
87                 }
88                 if ((base >> 8) & 3 || (limit >> 8) & 3) {
89                         printk(KERN_ERR "Node %d using interleaving mode %lx/%lx\n", 
90                                nodeid, (base>>8)&3, (limit>>8) & 3); 
91                         return -1; 
92                 }       
93                 if (node_online(nodeid)) { 
94                         printk(KERN_INFO "Node %d already present. Skipping\n", 
95                                nodeid);
96                         continue;
97                 }
98
99                 limit >>= 16; 
100                 limit <<= 24; 
101                 limit |= (1<<24)-1;
102
103                 if (limit > end_pfn_map << PAGE_SHIFT) 
104                         limit = end_pfn_map << PAGE_SHIFT; 
105                 if (limit <= base)
106                         continue; 
107                         
108                 base >>= 16;
109                 base <<= 24; 
110
111                 if (base < start) 
112                         base = start; 
113                 if (limit > end) 
114                         limit = end; 
115                 if (limit == base) { 
116                         printk(KERN_ERR "Empty node %d\n", nodeid); 
117                         continue; 
118                 }
119                 if (limit < base) { 
120                         printk(KERN_ERR "Node %d bogus settings %lx-%lx.\n",
121                                nodeid, base, limit);                           
122                         continue;
123                 } 
124                 
125                 /* Could sort here, but pun for now. Should not happen anyroads. */
126                 if (prevbase > base) { 
127                         printk(KERN_ERR "Node map not sorted %lx,%lx\n",
128                                prevbase,base);
129                         return -1;
130                 }
131                         
132                 printk(KERN_INFO "Node %d MemBase %016lx Limit %016lx\n", 
133                        nodeid, base, limit); 
134                 
135                 found++;
136                 
137                 nodes[nodeid].start = base; 
138                 nodes[nodeid].end = limit;
139
140                 prevbase = base;
141         } 
142
143         if (!found)
144                 return -1; 
145
146         memnode_shift = compute_hash_shift(nodes);
147         if (memnode_shift < 0) { 
148                 printk(KERN_ERR "No NUMA node hash function found. Contact maintainer\n"); 
149                 return -1; 
150         } 
151         printk(KERN_INFO "Using node hash shift of %d\n", memnode_shift); 
152
153         for (i = 0; i < MAXNODE; i++) { 
154                 if (nodes[i].start != nodes[i].end) { 
155                         /* assume 1:1 NODE:CPU */
156                         cpu_to_node[i] = i; 
157                 setup_node_bootmem(i, nodes[i].start, nodes[i].end); 
158         } 
159         }
160
161         numa_init_array();
162         return 0;
163