Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jgarzi...
[pandora-kernel.git] / arch / x86 / mm / amdtopology_64.c
1 /*
2  * AMD NUMA support.
3  * Discover the memory map and associated nodes.
4  *
5  * This version reads it directly from the AMD 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 <linux/memblock.h>
15
16 #include <asm/io.h>
17 #include <linux/pci_ids.h>
18 #include <linux/acpi.h>
19 #include <asm/types.h>
20 #include <asm/mmzone.h>
21 #include <asm/proto.h>
22 #include <asm/e820.h>
23 #include <asm/pci-direct.h>
24 #include <asm/numa.h>
25 #include <asm/mpspec.h>
26 #include <asm/apic.h>
27 #include <asm/amd_nb.h>
28
29 static struct bootnode __initdata nodes[8];
30 static nodemask_t __initdata nodes_parsed = NODE_MASK_NONE;
31
32 static __init int find_northbridge(void)
33 {
34         int num;
35
36         for (num = 0; num < 32; num++) {
37                 u32 header;
38
39                 header = read_pci_config(0, num, 0, 0x00);
40                 if (header != (PCI_VENDOR_ID_AMD | (0x1100<<16)) &&
41                         header != (PCI_VENDOR_ID_AMD | (0x1200<<16)) &&
42                         header != (PCI_VENDOR_ID_AMD | (0x1300<<16)))
43                         continue;
44
45                 header = read_pci_config(0, num, 1, 0x00);
46                 if (header != (PCI_VENDOR_ID_AMD | (0x1101<<16)) &&
47                         header != (PCI_VENDOR_ID_AMD | (0x1201<<16)) &&
48                         header != (PCI_VENDOR_ID_AMD | (0x1301<<16)))
49                         continue;
50                 return num;
51         }
52
53         return -1;
54 }
55
56 static __init void early_get_boot_cpu_id(void)
57 {
58         /*
59          * need to get the APIC ID of the BSP so can use that to
60          * create apicid_to_node in amd_scan_nodes()
61          */
62 #ifdef CONFIG_X86_MPPARSE
63         /*
64          * get boot-time SMP configuration:
65          */
66         if (smp_found_config)
67                 early_get_smp_config();
68 #endif
69 }
70
71 int __init amd_get_nodes(struct bootnode *physnodes)
72 {
73         int i;
74         int ret = 0;
75
76         for_each_node_mask(i, nodes_parsed) {
77                 physnodes[ret].start = nodes[i].start;
78                 physnodes[ret].end = nodes[i].end;
79                 ret++;
80         }
81         return ret;
82 }
83
84 int __init amd_numa_init(unsigned long start_pfn, unsigned long end_pfn)
85 {
86         unsigned long start = PFN_PHYS(start_pfn);
87         unsigned long end = PFN_PHYS(end_pfn);
88         unsigned numnodes;
89         unsigned long prevbase;
90         int i, nb, found = 0;
91         u32 nodeid, reg;
92
93         if (!early_pci_allowed())
94                 return -1;
95
96         nb = find_northbridge();
97         if (nb < 0)
98                 return nb;
99
100         pr_info("Scanning NUMA topology in Northbridge %d\n", nb);
101
102         reg = read_pci_config(0, nb, 0, 0x60);
103         numnodes = ((reg >> 4) & 0xF) + 1;
104         if (numnodes <= 1)
105                 return -1;
106
107         pr_info("Number of physical nodes %d\n", numnodes);
108
109         prevbase = 0;
110         for (i = 0; i < 8; i++) {
111                 unsigned long base, limit;
112
113                 base = read_pci_config(0, nb, 1, 0x40 + i*8);
114                 limit = read_pci_config(0, nb, 1, 0x44 + i*8);
115
116                 nodeid = limit & 7;
117                 if ((base & 3) == 0) {
118                         if (i < numnodes)
119                                 pr_info("Skipping disabled node %d\n", i);
120                         continue;
121                 }
122                 if (nodeid >= numnodes) {
123                         pr_info("Ignoring excess node %d (%lx:%lx)\n", nodeid,
124                                 base, limit);
125                         continue;
126                 }
127
128                 if (!limit) {
129                         pr_info("Skipping node entry %d (base %lx)\n",
130                                 i, base);
131                         continue;
132                 }
133                 if ((base >> 8) & 3 || (limit >> 8) & 3) {
134                         pr_err("Node %d using interleaving mode %lx/%lx\n",
135                                nodeid, (base >> 8) & 3, (limit >> 8) & 3);
136                         return -1;
137                 }
138                 if (node_isset(nodeid, nodes_parsed)) {
139                         pr_info("Node %d already present, skipping\n",
140                                 nodeid);
141                         continue;
142                 }
143
144                 limit >>= 16;
145                 limit <<= 24;
146                 limit |= (1<<24)-1;
147                 limit++;
148
149                 if (limit > end)
150                         limit = end;
151                 if (limit <= base)
152                         continue;
153
154                 base >>= 16;
155                 base <<= 24;
156
157                 if (base < start)
158                         base = start;
159                 if (limit > end)
160                         limit = end;
161                 if (limit == base) {
162                         pr_err("Empty node %d\n", nodeid);
163                         continue;
164                 }
165                 if (limit < base) {
166                         pr_err("Node %d bogus settings %lx-%lx.\n",
167                                nodeid, base, limit);
168                         continue;
169                 }
170
171                 /* Could sort here, but pun for now. Should not happen anyroads. */
172                 if (prevbase > base) {
173                         pr_err("Node map not sorted %lx,%lx\n",
174                                prevbase, base);
175                         return -1;
176                 }
177
178                 pr_info("Node %d MemBase %016lx Limit %016lx\n",
179                         nodeid, base, limit);
180
181                 found++;
182
183                 nodes[nodeid].start = base;
184                 nodes[nodeid].end = limit;
185
186                 prevbase = base;
187
188                 node_set(nodeid, nodes_parsed);
189         }
190
191         if (!found)
192                 return -1;
193         return 0;
194 }
195
196 int __init amd_scan_nodes(void)
197 {
198         unsigned int bits;
199         unsigned int cores;
200         unsigned int apicid_base;
201         int i;
202
203         BUG_ON(nodes_empty(nodes_parsed));
204         node_possible_map = nodes_parsed;
205         memnode_shift = compute_hash_shift(nodes, 8, NULL);
206         if (memnode_shift < 0) {
207                 pr_err("No NUMA node hash function found. Contact maintainer\n");
208                 return -1;
209         }
210         pr_info("Using node hash shift of %d\n", memnode_shift);
211
212         /* use the coreid bits from early_identify_cpu */
213         bits = boot_cpu_data.x86_coreid_bits;
214         cores = (1<<bits);
215         apicid_base = 0;
216         /* get the APIC ID of the BSP early for systems with apicid lifting */
217         early_get_boot_cpu_id();
218         if (boot_cpu_physical_apicid > 0) {
219                 pr_info("BSP APIC ID: %02x\n", boot_cpu_physical_apicid);
220                 apicid_base = boot_cpu_physical_apicid;
221         }
222
223         for_each_node_mask(i, node_possible_map) {
224                 int j;
225
226                 memblock_x86_register_active_regions(i,
227                                 nodes[i].start >> PAGE_SHIFT,
228                                 nodes[i].end >> PAGE_SHIFT);
229                 for (j = apicid_base; j < cores + apicid_base; j++)
230                         apicid_to_node[(i << bits) + j] = i;
231                 setup_node_bootmem(i, nodes[i].start, nodes[i].end);
232         }
233
234         numa_init_array();
235         return 0;
236 }