File: | dev/pci/amas.c |
Warning: | line 255, column 2 Value stored to 'elimit' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: amas.c,v 1.7 2022/03/11 18:00:45 mpi 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)) (0x0040 + 0x08 * (node)) |
56 | #define AMAS_REG_LIMIT(node)(0x0044 + 0x08 * (node)) (0x0044 + 0x08 * (node)) |
57 | #define AMAS_REG_EXTBASE(node)(0x0140 + 0x08 * (node)) (0x0140 + 0x08 * (node)) |
58 | #define AMAS_REG_EXTLIMIT(node)(0x0144 + 0x08 * (node)) (0x0144 + 0x08 * (node)) |
59 | |
60 | #define AMAS_REG_BL_ADDR(reg)(((reg) >> 16) & 0xffff) (((reg) >> 16) & 0xffff) |
61 | #define AMAS_REG_EBL_ADDR(ereg)((ereg) & 0xff) ((ereg) & 0xff) |
62 | |
63 | #define AMAS_REG_BL_SHIFT(24) (24) |
64 | #define AMAS_REG_EBL_SHIFT(40) (40) |
65 | |
66 | #define AMAS_REG_BL_PGSHIFT((24) - 12) (AMAS_REG_BL_SHIFT(24) - PAGE_SHIFT12) |
67 | #define AMAS_REG_EBL_PGSHIFT((40) - 12) (AMAS_REG_EBL_SHIFT(40) - PAGE_SHIFT12) |
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)(((base) << ((24) - 12)) | ((ebase) << ((40) - 12 ))) \ |
78 | (((base) << AMAS_REG_BL_PGSHIFT((24) - 12)) | ((ebase) << AMAS_REG_EBL_PGSHIFT((40) - 12))) |
79 | #define AMAS_ADDR2PAGE_LIMIT(base, ebase)(((base + 1) << ((24) - 12)) | ((ebase) << ((40) - 12))) \ |
80 | (((base + 1) << AMAS_REG_BL_PGSHIFT((24) - 12)) | ((ebase) << AMAS_REG_EBL_PGSHIFT((40) - 12))) |
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) ((limit) & 0x07) |
89 | #define AMAS_INTL_ENABLE(base, limit)(((base) >> 8) & 0x07) (((base) >> 8) & 0x07) |
90 | #define AMAS_INTL_SELECTOR(base, limit)(((limit) >> 8) & 0x07) (((limit) >> 8) & 0x07) |
91 | |
92 | /* |
93 | * Defines for family. |
94 | * Corresponds to the amas_feature[] constant below. |
95 | */ |
96 | #define AMAS_FAM_0Fh(0) (0) |
97 | #define AMAS_FAM_10h(1) (1) |
98 | #define AMAS_FAM_11h(2) (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 | const struct cfattach amas_ca = { |
123 | sizeof(struct amas_softc), |
124 | amas_match, |
125 | amas_attach |
126 | }; |
127 | |
128 | struct cfdriver amas_cd = { |
129 | NULL((void *)0), |
130 | "amas", |
131 | DV_DULL |
132 | }; |
133 | |
134 | const struct pci_matchid amas_devices[] = { |
135 | { PCI_VENDOR_AMD0x1022, PCI_PRODUCT_AMD_0F_ADDR0x1101 }, |
136 | { PCI_VENDOR_AMD0x1022, PCI_PRODUCT_AMD_10_ADDR0x1201 }, |
137 | { PCI_VENDOR_AMD0x1022, PCI_PRODUCT_AMD_11_ADDR0x1301 }, |
138 | }; |
139 | |
140 | int |
141 | amas_match(struct device *parent, void *match, void *aux) |
142 | { |
143 | struct pci_attach_args* pa = aux; |
144 | |
145 | if (pci_matchbyid(pa, amas_devices, nitems(amas_devices)(sizeof((amas_devices)) / sizeof((amas_devices)[0])))) |
146 | return 2; /* override pchb */ |
147 | return 0; |
148 | } |
149 | |
150 | void |
151 | amas_attach(struct device *parent, struct device *self, void *aux) |
152 | { |
153 | struct pci_attach_args *pa = aux; |
154 | 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 | amas->pa_tag = pa->pa_tag; |
161 | amas->pa_pc = pa->pa_pc; |
162 | |
163 | switch (PCI_PRODUCT(pa->pa_id)(((pa->pa_id) >> 16) & 0xffff)) { |
164 | case PCI_PRODUCT_AMD_0F_ADDR0x1101: |
165 | amas->family = AMAS_FAM_0Fh(0); |
166 | break; |
167 | case PCI_PRODUCT_AMD_10_ADDR0x1201: |
168 | amas->family = AMAS_FAM_10h(1); |
169 | break; |
170 | case PCI_PRODUCT_AMD_11_ADDR0x1301: |
171 | amas->family = AMAS_FAM_11h(2); |
172 | 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(8); 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 | printf("\n"); |
191 | |
192 | return; |
193 | } |
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 | amas_intl_nodes(struct amas_softc *amas) |
201 | { |
202 | pcireg_t base_reg, limit_reg; |
203 | int mask; |
204 | |
205 | if (!amas_feature[amas->family].can_intl) |
206 | return 0; |
207 | |
208 | /* |
209 | * Use node 0 on amas device to find interleave information. |
210 | * Node 0 is always present. |
211 | */ |
212 | |
213 | base_reg = pci_conf_read(amas->pa_pc, amas->pa_tag, AMAS_REG_BASE(0)(0x0040 + 0x08 * (0))); |
214 | limit_reg = pci_conf_read(amas->pa_pc, amas->pa_tag, AMAS_REG_LIMIT(0)(0x0044 + 0x08 * (0))); |
215 | mask = AMAS_INTL_ENABLE(base_reg, limit_reg)(((base_reg) >> 8) & 0x07); |
216 | |
217 | return mask == 0 ? 0 : mask + 1; |
218 | } |
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 | 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 | KASSERT(node >= 0 && node < AMAS_MAX_NODES)((node >= 0 && node < (8)) ? (void)0 : __assert ("diagnostic ", "/usr/src/sys/dev/pci/amas.c", 238, "node >= 0 && node < AMAS_MAX_NODES" )); |
239 | |
240 | if (node >= amas_feature[amas->family].maxnodes) { |
241 | /* Unsupported node: bail out early. */ |
242 | *start_pg_idx = 0; |
243 | *end_pg_idx = 0; |
244 | return; |
245 | } |
246 | |
247 | base = pci_conf_read(amas->pa_pc, amas->pa_tag, |
248 | AMAS_REG_BASE(node)(0x0040 + 0x08 * (node))); |
249 | limit = pci_conf_read(amas->pa_pc, amas->pa_tag, |
250 | AMAS_REG_LIMIT(node)(0x0044 + 0x08 * (node))); |
251 | base_addr = AMAS_REG_BL_ADDR(base)(((base) >> 16) & 0xffff); |
252 | limit_addr = AMAS_REG_BL_ADDR(limit)(((limit) >> 16) & 0xffff); |
253 | |
254 | ebase = 0; |
255 | elimit = 0; |
Value stored to 'elimit' is never read | |
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)(0x0140 + 0x08 * (node))); |
262 | elimit = pci_conf_read(amas->pa_pc, amas->pa_tag, |
263 | AMAS_REG_EXTLIMIT(node)(0x0144 + 0x08 * (node))); |
264 | ebase_addr = AMAS_REG_EBL_ADDR(ebase)((ebase) & 0xff); |
265 | elimit_addr = AMAS_REG_EBL_ADDR(elimit)((elimit) & 0xff); |
266 | } |
267 | #endif /* 0 */ |
268 | |
269 | if (ebase_addr > elimit_addr || |
270 | (ebase_addr == elimit_addr && base_addr >= limit_addr)) { |
271 | /* no memory present */ |
272 | *start_pg_idx = 0; |
273 | *end_pg_idx = 0; |
274 | return; |
275 | } |
276 | |
277 | /* Guaranteed by spec. */ |
278 | KASSERT(node == AMAS_DST_NODE(base, limit))((node == ((limit) & 0x07)) ? (void)0 : __assert("diagnostic " , "/usr/src/sys/dev/pci/amas.c", 278, "node == AMAS_DST_NODE(base, limit)" )); |
279 | |
280 | *start_pg_idx = AMAS_ADDR2PAGE_BASE(base_addr, ebase_addr)(((base_addr) << ((24) - 12)) | ((ebase_addr) << ( (40) - 12))); |
281 | *end_pg_idx = AMAS_ADDR2PAGE_LIMIT(limit_addr, elimit_addr)(((limit_addr + 1) << ((24) - 12)) | ((elimit_addr) << ((40) - 12))); |
282 | return; |
283 | } |