Line data Source code
1 : /* $OpenBSD: amas.c,v 1.5 2014/06/15 11:43:24 sf Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2009 Ariane van der Steldt <ariane@stack.nl>
5 : *
6 : * Permission to use, copy, modify, and distribute this software for any
7 : * purpose with or without fee is hereby granted, provided that the above
8 : * copyright notice and this permission notice appear in all copies.
9 : *
10 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 : */
18 :
19 : /*
20 : * Device: amas (AMD memory access/address switch).
21 : *
22 : * Driver for the amd athlon/opteron 64 address map.
23 : * This device is integrated in 64-bit Athlon and Opteron cpus
24 : * and contains mappings for memory to processor nodes.
25 : */
26 :
27 : #include <dev/pci/amas.h>
28 :
29 : #include <sys/param.h>
30 : #include <sys/systm.h>
31 : #include <sys/device.h>
32 :
33 : #include <dev/pci/pcivar.h>
34 : #include <dev/pci/pcireg.h>
35 : #include <dev/pci/pcidevs.h>
36 :
37 : int amas_match(struct device*, void*, void*);
38 : void amas_attach(struct device*, struct device*, void*);
39 :
40 : /*
41 : * Amas device layout:
42 : *
43 : * - base/limit registers (on 0x0f, 0x10, 0x11)
44 : * - extended base/limit registers (on 0x10)
45 : *
46 : * 0x0f, 0x10 support up to 8 nodes
47 : * 0x11 supports up to 1 nodes
48 : *
49 : * base/limit registers use bits [31..16] to indicate address [39..24]
50 : * extended base/limit registers use bits [7..0] to indicate address [47..40]
51 : * base/limit addresses need to be shifted <<24 for memory address
52 : * extended base/limit addresses need to be shifted <<40 for memory address
53 : */
54 :
55 : #define AMAS_REG_BASE(node) (0x0040 + 0x08 * (node))
56 : #define AMAS_REG_LIMIT(node) (0x0044 + 0x08 * (node))
57 : #define AMAS_REG_EXTBASE(node) (0x0140 + 0x08 * (node))
58 : #define AMAS_REG_EXTLIMIT(node) (0x0144 + 0x08 * (node))
59 :
60 : #define AMAS_REG_BL_ADDR(reg) (((reg) >> 16) & 0xffff)
61 : #define AMAS_REG_EBL_ADDR(ereg) ((ereg) & 0xff)
62 :
63 : #define AMAS_REG_BL_SHIFT (24)
64 : #define AMAS_REG_EBL_SHIFT (40)
65 :
66 : #define AMAS_REG_BL_PGSHIFT (AMAS_REG_BL_SHIFT - PAGE_SHIFT)
67 : #define AMAS_REG_EBL_PGSHIFT (AMAS_REG_EBL_SHIFT - PAGE_SHIFT)
68 :
69 : /*
70 : * Convert an address in amas to a page number.
71 : *
72 : * The device uses an inclusive mapping, where the upper bound address
73 : * must be all 1's after shifting.
74 : * The device driver uses C-style array indices, hence the +1 in the _LIMIT
75 : * macro.
76 : */
77 : #define AMAS_ADDR2PAGE_BASE(base, ebase) \
78 : (((base) << AMAS_REG_BL_PGSHIFT) | ((ebase) << AMAS_REG_EBL_PGSHIFT))
79 : #define AMAS_ADDR2PAGE_LIMIT(base, ebase) \
80 : (((base + 1) << AMAS_REG_BL_PGSHIFT) | ((ebase) << AMAS_REG_EBL_PGSHIFT))
81 :
82 : /*
83 : * Node and interleave description.
84 : * - base contains node selection [10..8] (on 0x0f, 0x10)
85 : * - limit contains node selection bitmask [10..8] (on 0x0f, 0x10)
86 : * - limit contains destination node [2..0] (on 0x0f, 0x10)
87 : */
88 : #define AMAS_DST_NODE(base, limit) ((limit) & 0x07)
89 : #define AMAS_INTL_ENABLE(base, limit) (((base) >> 8) & 0x07)
90 : #define AMAS_INTL_SELECTOR(base, limit) (((limit) >> 8) & 0x07)
91 :
92 : /*
93 : * Defines for family.
94 : * Corresponds to the amas_feature[] constant below.
95 : */
96 : #define AMAS_FAM_0Fh (0)
97 : #define AMAS_FAM_10h (1)
98 : #define AMAS_FAM_11h (2)
99 :
100 : /*
101 : * Feature tests.
102 : *
103 : * 0x11 supports at max 1 node, 0x0f and 0x10 support up to 8 nodes.
104 : * 0x11 has extended address registers.
105 : * 0x0f, 0x10 can interleave memory.
106 : */
107 : struct amas_feature_t {
108 : int maxnodes;
109 : int can_intl;
110 : int has_extended_bl;
111 : };
112 : static const struct amas_feature_t amas_feature[] = {
113 : /* Family 0x0f */
114 : { 8, 1, 0 },
115 : /* Family 0x10 */
116 : { 8, 1, 1 },
117 : /* Family 0x11 */
118 : { 1, 0, 0 },
119 : };
120 :
121 : /* Probe code. */
122 : struct cfattach amas_ca = {
123 : sizeof(struct amas_softc),
124 : amas_match,
125 : amas_attach
126 : };
127 :
128 : struct cfdriver amas_cd = {
129 : NULL,
130 : "amas",
131 : DV_DULL
132 : };
133 :
134 : const struct pci_matchid amas_devices[] = {
135 : { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_AMD64_0F_ADDR },
136 : { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_AMD64_10_ADDR },
137 : { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_AMD64_11_ADDR },
138 : };
139 :
140 : int
141 0 : amas_match(struct device *parent, void *match, void *aux)
142 : {
143 0 : struct pci_attach_args* pa = aux;
144 :
145 0 : if (pci_matchbyid(pa, amas_devices, nitems(amas_devices)))
146 0 : return 2; /* override pchb */
147 0 : return 0;
148 0 : }
149 :
150 : void
151 0 : amas_attach(struct device *parent, struct device *self, void *aux)
152 : {
153 0 : struct pci_attach_args *pa = aux;
154 0 : struct amas_softc *amas = (struct amas_softc*)self;
155 : #ifdef DEBUG
156 : paddr_t start_pg, end_pg;
157 : int nodes, i;
158 : #endif /* DEBUG */
159 :
160 0 : amas->pa_tag = pa->pa_tag;
161 0 : amas->pa_pc = pa->pa_pc;
162 :
163 0 : switch (PCI_PRODUCT(pa->pa_id)) {
164 : case PCI_PRODUCT_AMD_AMD64_0F_ADDR:
165 0 : amas->family = AMAS_FAM_0Fh;
166 0 : break;
167 : case PCI_PRODUCT_AMD_AMD64_10_ADDR:
168 0 : amas->family = AMAS_FAM_10h;
169 0 : break;
170 : case PCI_PRODUCT_AMD_AMD64_11_ADDR:
171 0 : amas->family = AMAS_FAM_11h;
172 0 : break;
173 : }
174 :
175 : #ifdef DEBUG
176 : nodes = amas_intl_nodes(amas);
177 :
178 : printf(":");
179 : if (nodes != 0) {
180 : printf(" interleaved");
181 : } else {
182 : for (i = 0; i < AMAS_MAX_NODES; i++) {
183 : amas_get_pagerange(amas, i, &start_pg, &end_pg);
184 :
185 : if (!(start_pg == 0 && end_pg == 0))
186 : printf(" [%#lx, %#lx]", start_pg, end_pg);
187 : }
188 : }
189 : #endif /* DEBUG */
190 0 : printf("\n");
191 :
192 : return;
193 0 : }
194 :
195 : /*
196 : * Returns the number of nodes across which the memory is interleaved.
197 : * Returns 0 if the memory is not interleaved.
198 : */
199 : int
200 0 : amas_intl_nodes(struct amas_softc *amas)
201 : {
202 : pcireg_t base_reg, limit_reg;
203 : int mask;
204 :
205 0 : if (!amas_feature[amas->family].can_intl)
206 0 : return 0;
207 :
208 : /*
209 : * Use node 0 on amas device to find interleave information.
210 : * Node 0 is always present.
211 : */
212 :
213 0 : base_reg = pci_conf_read(amas->pa_pc, amas->pa_tag, AMAS_REG_BASE(0));
214 0 : limit_reg = pci_conf_read(amas->pa_pc, amas->pa_tag, AMAS_REG_LIMIT(0));
215 0 : mask = AMAS_INTL_ENABLE(base_reg, limit_reg);
216 :
217 0 : return mask == 0 ? 0 : mask + 1;
218 0 : }
219 :
220 : /*
221 : * Returns the range of memory that is contained on the given node.
222 : * If the memory is interleaved, the result is undefined.
223 : *
224 : * The range is written in {start,end}_pg_idx.
225 : * Note that these are page numbers and that these use array indices:
226 : * pages are in this range if start <= pg_no < end.
227 : *
228 : * This device supports at most 8 nodes.
229 : */
230 : void
231 0 : amas_get_pagerange(struct amas_softc *amas, int node,
232 : paddr_t *start_pg_idx, paddr_t *end_pg_idx)
233 : {
234 : pcireg_t base, ebase, limit, elimit;
235 : paddr_t base_addr, ebase_addr, limit_addr, elimit_addr;
236 :
237 : /* Sanity check: max AMAS_MAX_NODES supported. */
238 0 : KASSERT(node >= 0 && node < AMAS_MAX_NODES);
239 :
240 0 : if (node >= amas_feature[amas->family].maxnodes) {
241 : /* Unsupported node: bail out early. */
242 0 : *start_pg_idx = 0;
243 0 : *end_pg_idx = 0;
244 0 : return;
245 : }
246 :
247 0 : base = pci_conf_read(amas->pa_pc, amas->pa_tag,
248 0 : AMAS_REG_BASE(node));
249 0 : limit = pci_conf_read(amas->pa_pc, amas->pa_tag,
250 0 : AMAS_REG_LIMIT(node));
251 0 : base_addr = AMAS_REG_BL_ADDR(base);
252 0 : limit_addr = AMAS_REG_BL_ADDR(limit);
253 :
254 : ebase = 0;
255 : elimit = 0;
256 : ebase_addr = 0;
257 : elimit_addr = 0;
258 : #if 0 /* Needs extended pci registers. */
259 : if (amas_feature[amas->family].has_extended_bl) {
260 : ebase = pci_conf_read(amas->pa_pc, amas->pa_tag,
261 : AMAS_REG_EXTBASE(node));
262 : elimit = pci_conf_read(amas->pa_pc, amas->pa_tag,
263 : AMAS_REG_EXTLIMIT(node));
264 : ebase_addr = AMAS_REG_EBL_ADDR(ebase);
265 : elimit_addr = AMAS_REG_EBL_ADDR(elimit);
266 : }
267 : #endif /* 0 */
268 :
269 0 : if (ebase_addr > elimit_addr ||
270 0 : (ebase_addr == elimit_addr && base_addr >= limit_addr)) {
271 : /* no memory present */
272 0 : *start_pg_idx = 0;
273 0 : *end_pg_idx = 0;
274 0 : return;
275 : }
276 :
277 : /* Guaranteed by spec. */
278 0 : KASSERT(node == AMAS_DST_NODE(base, limit));
279 :
280 0 : *start_pg_idx = AMAS_ADDR2PAGE_BASE(base_addr, ebase_addr);
281 0 : *end_pg_idx = AMAS_ADDR2PAGE_LIMIT(limit_addr, elimit_addr);
282 0 : return;
283 0 : }
|