Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux-2.6
[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                         header != (PCI_VENDOR_ID_AMD | (0x1200<<16)) &&
33                         header != (PCI_VENDOR_ID_AMD | (0x1300<<16)))
34                         continue;
35
36                 header = read_pci_config(0, num, 1, 0x00);
37                 if (header != (PCI_VENDOR_ID_AMD | (0x1101<<16)) &&
38                         header != (PCI_VENDOR_ID_AMD | (0x1201<<16)) &&
39                         header != (PCI_VENDOR_ID_AMD | (0x1301<<16)))
40                         continue;
41                 return num;
42         }
43
44         return -1;
45 }
46
47 int __init k8_scan_nodes(unsigned long start, unsigned long end)
48 {
49         unsigned long prevbase;
50         struct bootnode nodes[8];
51         int nodeid, i, nb;
52         unsigned char nodeids[8];
53         int found = 0;
54         u32 reg;
55         unsigned numnodes;
56         unsigned cores;
57         unsigned bits;
58         int j;
59
60         if (!early_pci_allowed())
61                 return -1;
62
63         nb = find_northbridge();
64         if (nb < 0)
65                 return nb;
66
67         printk(KERN_INFO "Scanning NUMA topology in Northbridge %d\n", nb);
68
69         reg = read_pci_config(0, nb, 0, 0x60);
70         numnodes = ((reg >> 4) & 0xF) + 1;
71         if (numnodes <= 1)
72                 return -1;
73
74         printk(KERN_INFO "Number of nodes %d\n", numnodes);
75
76         memset(&nodes, 0, sizeof(nodes));
77         prevbase = 0;
78         for (i = 0; i < 8; i++) {
79                 unsigned long base, limit;
80                 u32 nodeid;
81
82                 base = read_pci_config(0, nb, 1, 0x40 + i*8);
83                 limit = read_pci_config(0, nb, 1, 0x44 + i*8);
84
85                 nodeid = limit & 7;
86                 nodeids[i] = nodeid;
87                 if ((base & 3) == 0) {
88                         if (i < numnodes)
89                                 printk("Skipping disabled node %d\n", i);
90                         continue;
91                 }
92                 if (nodeid >= numnodes) {
93                         printk("Ignoring excess node %d (%lx:%lx)\n", nodeid,
94                                base, limit);
95                         continue;
96                 }
97
98                 if (!limit) {
99                         printk(KERN_INFO "Skipping node entry %d (base %lx)\n",
100                                i, base);
101                         continue;
102                 }
103                 if ((base >> 8) & 3 || (limit >> 8) & 3) {
104                         printk(KERN_ERR "Node %d using interleaving mode %lx/%lx\n",
105                                nodeid, (base>>8)&3, (limit>>8) & 3);
106                         return -1;
107                 }
108                 if (node_isset(nodeid, node_possible_map)) {
109                         printk(KERN_INFO "Node %d already present. Skipping\n",
110                                nodeid);
111                         continue;
112                 }
113
114                 limit >>= 16;
115                 limit <<= 24;
116                 limit |= (1<<24)-1;
117                 limit++;
118
119                 if (limit > end_pfn << PAGE_SHIFT)
120                         limit = end_pfn << PAGE_SHIFT;
121                 if (limit <= base)
122                         continue;
123
124                 base >>= 16;
125                 base <<= 24;
126
127                 if (base < start)
128                         base = start;
129                 if (limit > end)
130                         limit = end;
131                 if (limit == base) {
132                         printk(KERN_ERR "Empty node %d\n", nodeid);
133                         continue;
134                 }
135                 if (limit < base) {
136                         printk(KERN_ERR "Node %d bogus settings %lx-%lx.\n",
137                                nodeid, base, limit);
138                         continue;
139                 }
140
141                 /* Could sort here, but pun for now. Should not happen anyroads. */
142                 if (prevbase > base) {
143                         printk(KERN_ERR "Node map not sorted %lx,%lx\n",
144                                prevbase, base);
145                         return -1;
146                 }
147
148                 printk(KERN_INFO "Node %d MemBase %016lx Limit %016lx\n",
149                        nodeid, base, limit);
150
151                 found++;
152
153                 nodes[nodeid].start = base;
154                 nodes[nodeid].end = limit;
155                 e820_register_active_regions(nodeid,
156                                 nodes[nodeid].start >> PAGE_SHIFT,
157                                 nodes[nodeid].end >> PAGE_SHIFT);
158
159                 prevbase = base;
160
161                 node_set(nodeid, node_possible_map);
162         }
163
164         if (!found)
165                 return -1;
166
167         memnode_shift = compute_hash_shift(nodes, 8);
168         if (memnode_shift < 0) {
169                 printk(KERN_ERR "No NUMA node hash function found. Contact maintainer\n");
170                 return -1;
171         }
172         printk(KERN_INFO "Using node hash shift of %d\n", memnode_shift);
173
174         /* use the coreid bits from early_identify_cpu */
175         bits = boot_cpu_data.x86_coreid_bits;
176         cores = (1<<bits);
177
178         for (i = 0; i < 8; i++) {
179                 if (nodes[i].start != nodes[i].end) {
180                         nodeid = nodeids[i];
181                         for (j = 0; j < cores; j++)
182                                 apicid_to_node[(nodeid << bits) + j] = i;
183                         setup_node_bootmem(i, nodes[i].start, nodes[i].end);
184                 }
185         }
186
187         numa_init_array();
188         return 0;
189 }