1 /* $OpenBSD: amas.c,v 1.6 2020/01/04 01:34:24 jsg 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_0F_ADDR }, 136 { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_10_ADDR }, 137 { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_11_ADDR }, 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))) 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)) { 164 case PCI_PRODUCT_AMD_0F_ADDR: 165 amas->family = AMAS_FAM_0Fh; 166 break; 167 case PCI_PRODUCT_AMD_10_ADDR: 168 amas->family = AMAS_FAM_10h; 169 break; 170 case PCI_PRODUCT_AMD_11_ADDR: 171 amas->family = AMAS_FAM_11h; 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; 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)); 214 limit_reg = pci_conf_read(amas->pa_pc, amas->pa_tag, AMAS_REG_LIMIT(0)); 215 mask = AMAS_INTL_ENABLE(base_reg, limit_reg); 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); 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)); 249 limit = pci_conf_read(amas->pa_pc, amas->pa_tag, 250 AMAS_REG_LIMIT(node)); 251 base_addr = AMAS_REG_BL_ADDR(base); 252 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 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)); 279 280 *start_pg_idx = AMAS_ADDR2PAGE_BASE(base_addr, ebase_addr); 281 *end_pg_idx = AMAS_ADDR2PAGE_LIMIT(limit_addr, elimit_addr); 282 return; 283 } 284