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  *
8  * Copyright 2002 Andi Kleen, SuSE Labs.
9  * $Id: k8topology.c,v 1.12 2004/01/29 00:51:01 ak Exp $
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 
23 #define Dprintk(x...)
24 
25 int memnode_shift;
26 u8  memnodemap[NODEMAPSIZE];
27 
find_northbridge(void)28 static __init int find_northbridge(void)
29 {
30 	int num;
31 
32 	for (num = 0; num < 32; num++) {
33 		u32 header;
34 
35 		header = read_pci_config(0, num, 0, 0x00);
36 		if (header != (PCI_VENDOR_ID_AMD | (0x1100<<16)))
37 			continue;
38 
39 		header = read_pci_config(0, num, 1, 0x00);
40 		if (header != (PCI_VENDOR_ID_AMD | (0x1101<<16)))
41 			continue;
42 		return num;
43 	}
44 
45 	return -1;
46 }
47 
48 #define MAXNODE 8
49 #define NODEMASK 0xff
50 
51 struct node {
52 	u64 start,end;
53 };
54 
55 #define for_all_nodes(n) \
56 	for (n=0; n<MAXNODE;n++) if (nodes[n].start!=nodes[n].end)
57 
compute_hash_shift(struct node * nodes,int numnodes,u64 maxmem)58 static __init int compute_hash_shift(struct node *nodes, int numnodes, u64 maxmem)
59 {
60 	int i;
61 	int shift = 24;
62 	u64 addr;
63 
64 	/* When in doubt use brute force. */
65 	while (shift < 48) {
66 		memset(memnodemap,0xff,sizeof(*memnodemap) * NODEMAPSIZE);
67 		for_all_nodes (i) {
68 			for (addr = nodes[i].start;
69 			     addr < nodes[i].end;
70 			     addr += (1UL << shift)) {
71 				if (memnodemap[addr >> shift] != 0xff &&
72 				    memnodemap[addr >> shift] != i) {
73 					printk("node %d shift %d addr %Lx conflict %d\n",
74 					       i, shift, addr, memnodemap[addr>>shift]);
75 					goto next;
76 				}
77 				memnodemap[addr >> shift] = i;
78 			}
79 		}
80 		return shift;
81 	next:
82 		shift++;
83 	}
84 	memset(memnodemap,0,sizeof(*memnodemap) * NODEMAPSIZE);
85 	return -1;
86 }
87 
88 extern unsigned long nodes_present;
89 extern unsigned long end_pfn;
90 
k8_scan_nodes(unsigned long start,unsigned long end)91 int __init k8_scan_nodes(unsigned long start, unsigned long end)
92 {
93 	unsigned long prevbase;
94 	struct node nodes[MAXNODE];
95 	int nodeid, numnodes, maxnode, i, nb;
96 
97 	nb = find_northbridge();
98 	if (nb < 0)
99 		return nb;
100 
101 	printk(KERN_INFO "Scanning NUMA topology in Northbridge %d\n", nb);
102 
103 	numnodes = (read_pci_config(0, nb, 0, 0x60 ) >> 4) & 7;
104 
105 	memset(&nodes,0,sizeof(nodes));
106 	prevbase = 0;
107 	maxnode = -1;
108 	for (i = 0; i < MAXNODE; i++) {
109 		unsigned long base,limit;
110 
111 		base = read_pci_config(0, nb, 1, 0x40 + i*8);
112 		limit = read_pci_config(0, nb, 1, 0x44 + i*8);
113 
114 		nodeid = limit & 7;
115 		if ((base & 3) == 0) {
116 			continue;
117 		}
118 		if (!limit) {
119 			printk(KERN_INFO "Skipping node entry %d (base %lx)\n", i,			       base);
120 			continue;
121 		}
122 		if ((base >> 8) & 3 || (limit >> 8) & 3) {
123 			printk(KERN_ERR "Node %d using interleaving mode %lx/%lx\n",
124 			       nodeid, (base>>8)&3, (limit>>8) & 3);
125 			return -1;
126 		}
127 		if (nodeid > maxnode)
128 			maxnode = nodeid;
129 		if ((1UL << nodeid) & nodes_present) {
130 			printk("Node %d already present. Skipping\n", nodeid);
131 			continue;
132 		}
133 
134 		limit >>= 16;
135 		limit <<= 24;
136 		limit |= (1<<24)-1;
137 
138 		if (limit > end_pfn << PAGE_SHIFT)
139 			limit = end_pfn << PAGE_SHIFT;
140 		if (limit <= base)
141 			continue;
142 
143 		base >>= 16;
144 		base <<= 24;
145 
146 		if (base < start)
147 			base = start;
148 		if (limit > end)
149 			limit = end;
150 		if (limit == base)
151 			continue;
152 		if (limit < base) {
153 			Dprintk(KERN_INFO"Node %d bogus settings %lx-%lx. Ignored.\n",
154 			       nodeid, base, limit);
155 			continue;
156 		}
157 
158 		/* Could sort here, but pun for now. Should not happen anyroads. */
159 		if (prevbase > base) {
160 			printk(KERN_INFO "Node map not sorted %lx,%lx\n",
161 			       prevbase,base);
162 			return -1;
163 		}
164 
165 		printk(KERN_INFO "Node %d MemBase %016lx Limit %016lx\n",
166 		       nodeid, base, limit);
167 
168 		nodes[nodeid].start = base;
169 		nodes[nodeid].end = limit;
170 
171 		prevbase = base;
172 	}
173 
174 	if (maxnode <= 0)
175 		return -1;
176 
177 	memnode_shift = compute_hash_shift(nodes,maxnode,end);
178 	if (memnode_shift < 0) {
179 		printk(KERN_ERR "No NUMA node hash function found. Contact maintainer\n");
180 		return -1;
181 	}
182 	printk(KERN_INFO "Using node hash shift of %d\n", memnode_shift);
183 
184 	for (i = 0; i < MAXNODE; i++) {
185 		if (nodes[i].start != nodes[i].end)
186 		setup_node_bootmem(i, nodes[i].start, nodes[i].end);
187 	}
188 
189 	/* There are unfortunately some poorly designed mainboards around
190 	   that only connect memory to a single CPU. This breaks the 1:1 cpu->node
191 	   mapping. To avoid this fill in the mapping for all possible
192 	   CPUs, as the number of CPUs is not known yet.
193 	   We round robin the existing nodes. */
194 	int rr = 0;
195 	for (i = 0; i < MAXNODE; i++) {
196 		if (nodes[i].start != nodes[i].end)
197 			continue;
198 		if ((nodes_present >> rr) == 0)
199 			rr = 0;
200 		rr += ffz(~nodes_present >> rr);
201 		PLAT_NODE_DATA(i) = PLAT_NODE_DATA(rr);
202 		rr++;
203 	}
204 
205 	return 0;
206 }
207 
208