ec35747aacd764b0aedf1824e604b704e5a164e9
[pandora-kernel.git] / 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 node nodes[8];
47         int nodeid, i, nb; 
48         int found = 0;
49         u32 reg;
50         unsigned numnodes;
51         nodemask_t nodes_parsed;
52
53         nodes_clear(nodes_parsed);
54
55         nb = find_northbridge(); 
56         if (nb < 0) 
57                 return nb;
58
59         printk(KERN_INFO "Scanning NUMA topology in Northbridge %d\n", nb); 
60
61         reg = read_pci_config(0, nb, 0, 0x60); 
62         numnodes = ((reg >> 4) & 0xF) + 1;
63
64         printk(KERN_INFO "Number of nodes %d\n", numnodes);
65
66         memset(&nodes,0,sizeof(nodes)); 
67         prevbase = 0;
68         for (i = 0; i < 8; i++) { 
69                 unsigned long base,limit; 
70
71                 base = read_pci_config(0, nb, 1, 0x40 + i*8);
72                 limit = read_pci_config(0, nb, 1, 0x44 + i*8);
73
74                 nodeid = limit & 7; 
75                 if ((base & 3) == 0) { 
76                         if (i < numnodes)
77                                 printk("Skipping disabled node %d\n", i); 
78                         continue;
79                 } 
80                 if (nodeid >= numnodes) {
81                         printk("Ignoring excess node %d (%lx:%lx)\n", nodeid,
82                                base, limit); 
83                         continue;
84                 } 
85
86                 if (!limit) { 
87                         printk(KERN_INFO "Skipping node entry %d (base %lx)\n", i,
88                                base);
89                         continue;
90                 }
91                 if ((base >> 8) & 3 || (limit >> 8) & 3) {
92                         printk(KERN_ERR "Node %d using interleaving mode %lx/%lx\n", 
93                                nodeid, (base>>8)&3, (limit>>8) & 3); 
94                         return -1; 
95                 }       
96                 if (node_isset(nodeid, nodes_parsed)) { 
97                         printk(KERN_INFO "Node %d already present. Skipping\n", 
98                                nodeid);
99                         continue;
100                 }
101
102                 limit >>= 16; 
103                 limit <<= 24; 
104                 limit |= (1<<24)-1;
105
106                 if (limit > end_pfn << PAGE_SHIFT)
107                         limit = end_pfn << PAGE_SHIFT;
108                 if (limit <= base)
109                         continue; 
110                         
111                 base >>= 16;
112                 base <<= 24; 
113
114                 if (base < start) 
115                         base = start; 
116                 if (limit > end) 
117                         limit = end; 
118                 if (limit == base) { 
119                         printk(KERN_ERR "Empty node %d\n", nodeid); 
120                         continue; 
121                 }
122                 if (limit < base) { 
123                         printk(KERN_ERR "Node %d bogus settings %lx-%lx.\n",
124                                nodeid, base, limit);                           
125                         continue;
126                 } 
127                 
128                 /* Could sort here, but pun for now. Should not happen anyroads. */
129                 if (prevbase > base) { 
130                         printk(KERN_ERR "Node map not sorted %lx,%lx\n",
131                                prevbase,base);
132                         return -1;
133                 }
134                         
135                 printk(KERN_INFO "Node %d MemBase %016lx Limit %016lx\n", 
136                        nodeid, base, limit); 
137                 
138                 found++;
139                 
140                 nodes[nodeid].start = base; 
141                 nodes[nodeid].end = limit;
142
143                 prevbase = base;
144
145                 node_set(nodeid, nodes_parsed);
146         } 
147
148         if (!found)
149                 return -1; 
150
151         memnode_shift = compute_hash_shift(nodes, numnodes);
152         if (memnode_shift < 0) { 
153                 printk(KERN_ERR "No NUMA node hash function found. Contact maintainer\n"); 
154                 return -1; 
155         } 
156         printk(KERN_INFO "Using node hash shift of %d\n", memnode_shift); 
157
158         for (i = 0; i < 8; i++) {
159                 if (nodes[i].start != nodes[i].end) { 
160                         /* assume 1:1 NODE:CPU */
161                         cpu_to_node[i] = i; 
162                         setup_node_bootmem(i, nodes[i].start, nodes[i].end); 
163                 } 
164         }
165
166         numa_init_array();
167         return 0;
168