| 1 | // SPDX-License-Identifier: GPL-2.0 | 
|---|
| 2 | #include <linux/init.h> | 
|---|
| 3 | #include <linux/pci.h> | 
|---|
| 4 | #include <linux/topology.h> | 
|---|
| 5 | #include <linux/cpu.h> | 
|---|
| 6 | #include <linux/range.h> | 
|---|
| 7 |  | 
|---|
| 8 | #include <asm/amd/nb.h> | 
|---|
| 9 | #include <asm/pci_x86.h> | 
|---|
| 10 |  | 
|---|
| 11 | #include <asm/pci-direct.h> | 
|---|
| 12 |  | 
|---|
| 13 | #include "bus_numa.h" | 
|---|
| 14 |  | 
|---|
| 15 | #define AMD_NB_F0_NODE_ID			0x60 | 
|---|
| 16 | #define AMD_NB_F0_UNIT_ID			0x64 | 
|---|
| 17 | #define AMD_NB_F1_CONFIG_MAP_REG		0xe0 | 
|---|
| 18 |  | 
|---|
| 19 | #define RANGE_NUM				16 | 
|---|
| 20 | #define AMD_NB_F1_CONFIG_MAP_RANGES		4 | 
|---|
| 21 |  | 
|---|
| 22 | struct amd_hostbridge { | 
|---|
| 23 | u32 bus; | 
|---|
| 24 | u32 slot; | 
|---|
| 25 | u32 device; | 
|---|
| 26 | }; | 
|---|
| 27 |  | 
|---|
| 28 | /* | 
|---|
| 29 | * IMPORTANT NOTE: | 
|---|
| 30 | * hb_probes[] and early_root_info_init() is in maintenance mode. | 
|---|
| 31 | * It only supports K8, Fam10h, Fam11h, and Fam15h_00h-0fh . | 
|---|
| 32 | * Future processor will rely on information in ACPI. | 
|---|
| 33 | */ | 
|---|
| 34 | static struct amd_hostbridge hb_probes[] __initdata = { | 
|---|
| 35 | { 0, 0x18, 0x1100 }, /* K8 */ | 
|---|
| 36 | { 0, 0x18, 0x1200 }, /* Family10h */ | 
|---|
| 37 | { 0xff, 0, 0x1200 }, /* Family10h */ | 
|---|
| 38 | { 0, 0x18, 0x1300 }, /* Family11h */ | 
|---|
| 39 | { 0, 0x18, 0x1600 }, /* Family15h */ | 
|---|
| 40 | }; | 
|---|
| 41 |  | 
|---|
| 42 | static struct pci_root_info __init *find_pci_root_info(int node, int link) | 
|---|
| 43 | { | 
|---|
| 44 | struct pci_root_info *info; | 
|---|
| 45 |  | 
|---|
| 46 | /* find the position */ | 
|---|
| 47 | list_for_each_entry(info, &pci_root_infos, list) | 
|---|
| 48 | if (info->node == node && info->link == link) | 
|---|
| 49 | return info; | 
|---|
| 50 |  | 
|---|
| 51 | return NULL; | 
|---|
| 52 | } | 
|---|
| 53 |  | 
|---|
| 54 | static inline resource_size_t cap_resource(u64 val) | 
|---|
| 55 | { | 
|---|
| 56 | if (val > RESOURCE_SIZE_MAX) | 
|---|
| 57 | return RESOURCE_SIZE_MAX; | 
|---|
| 58 |  | 
|---|
| 59 | return val; | 
|---|
| 60 | } | 
|---|
| 61 |  | 
|---|
| 62 | /** | 
|---|
| 63 | * early_root_info_init() | 
|---|
| 64 | * called before pcibios_scan_root and pci_scan_bus | 
|---|
| 65 | * fills the mp_bus_to_cpumask array based according | 
|---|
| 66 | * to the LDT Bus Number Registers found in the northbridge. | 
|---|
| 67 | */ | 
|---|
| 68 | static int __init early_root_info_init(void) | 
|---|
| 69 | { | 
|---|
| 70 | int i; | 
|---|
| 71 | unsigned bus; | 
|---|
| 72 | unsigned slot; | 
|---|
| 73 | int node; | 
|---|
| 74 | int link; | 
|---|
| 75 | int def_node; | 
|---|
| 76 | int def_link; | 
|---|
| 77 | struct pci_root_info *info; | 
|---|
| 78 | u32 reg; | 
|---|
| 79 | u64 start; | 
|---|
| 80 | u64 end; | 
|---|
| 81 | struct range range[RANGE_NUM]; | 
|---|
| 82 | u64 val; | 
|---|
| 83 | u32 address; | 
|---|
| 84 | bool found; | 
|---|
| 85 | struct resource fam10h_mmconf_res, *fam10h_mmconf; | 
|---|
| 86 | u64 fam10h_mmconf_start; | 
|---|
| 87 | u64 fam10h_mmconf_end; | 
|---|
| 88 |  | 
|---|
| 89 | if (!early_pci_allowed()) | 
|---|
| 90 | return -1; | 
|---|
| 91 |  | 
|---|
| 92 | found = false; | 
|---|
| 93 | for (i = 0; i < ARRAY_SIZE(hb_probes); i++) { | 
|---|
| 94 | u32 id; | 
|---|
| 95 | u16 device; | 
|---|
| 96 | u16 vendor; | 
|---|
| 97 |  | 
|---|
| 98 | bus = hb_probes[i].bus; | 
|---|
| 99 | slot = hb_probes[i].slot; | 
|---|
| 100 | id = read_pci_config(bus, slot, func: 0, PCI_VENDOR_ID); | 
|---|
| 101 | vendor = id & 0xffff; | 
|---|
| 102 | device = (id>>16) & 0xffff; | 
|---|
| 103 |  | 
|---|
| 104 | if (vendor != PCI_VENDOR_ID_AMD && | 
|---|
| 105 | vendor != PCI_VENDOR_ID_HYGON) | 
|---|
| 106 | continue; | 
|---|
| 107 |  | 
|---|
| 108 | if (hb_probes[i].device == device) { | 
|---|
| 109 | found = true; | 
|---|
| 110 | break; | 
|---|
| 111 | } | 
|---|
| 112 | } | 
|---|
| 113 |  | 
|---|
| 114 | if (!found) | 
|---|
| 115 | return 0; | 
|---|
| 116 |  | 
|---|
| 117 | /* | 
|---|
| 118 | * We should learn topology and routing information from _PXM and | 
|---|
| 119 | * _CRS methods in the ACPI namespace.  We extract node numbers | 
|---|
| 120 | * here to work around BIOSes that don't supply _PXM. | 
|---|
| 121 | */ | 
|---|
| 122 | for (i = 0; i < AMD_NB_F1_CONFIG_MAP_RANGES; i++) { | 
|---|
| 123 | int min_bus; | 
|---|
| 124 | int max_bus; | 
|---|
| 125 | reg = read_pci_config(bus, slot, func: 1, | 
|---|
| 126 | AMD_NB_F1_CONFIG_MAP_REG + (i << 2)); | 
|---|
| 127 |  | 
|---|
| 128 | /* Check if that register is enabled for bus range */ | 
|---|
| 129 | if ((reg & 7) != 3) | 
|---|
| 130 | continue; | 
|---|
| 131 |  | 
|---|
| 132 | min_bus = (reg >> 16) & 0xff; | 
|---|
| 133 | max_bus = (reg >> 24) & 0xff; | 
|---|
| 134 | node = (reg >> 4) & 0x07; | 
|---|
| 135 | link = (reg >> 8) & 0x03; | 
|---|
| 136 |  | 
|---|
| 137 | alloc_pci_root_info(bus_min: min_bus, bus_max: max_bus, node, link); | 
|---|
| 138 | } | 
|---|
| 139 |  | 
|---|
| 140 | /* | 
|---|
| 141 | * The following code extracts routing information for use on old | 
|---|
| 142 | * systems where Linux doesn't automatically use host bridge _CRS | 
|---|
| 143 | * methods (or when the user specifies "pci=nocrs"). | 
|---|
| 144 | * | 
|---|
| 145 | * We only do this through Fam11h, because _CRS should be enough on | 
|---|
| 146 | * newer systems. | 
|---|
| 147 | */ | 
|---|
| 148 | if (boot_cpu_data.x86 > 0x11) | 
|---|
| 149 | return 0; | 
|---|
| 150 |  | 
|---|
| 151 | /* get the default node and link for left over res */ | 
|---|
| 152 | reg = read_pci_config(bus, slot, func: 0, AMD_NB_F0_NODE_ID); | 
|---|
| 153 | def_node = (reg >> 8) & 0x07; | 
|---|
| 154 | reg = read_pci_config(bus, slot, func: 0, AMD_NB_F0_UNIT_ID); | 
|---|
| 155 | def_link = (reg >> 8) & 0x03; | 
|---|
| 156 |  | 
|---|
| 157 | memset(s: range, c: 0, n: sizeof(range)); | 
|---|
| 158 | add_range(range, RANGE_NUM, nr_range: 0, start: 0, end: 0xffff + 1); | 
|---|
| 159 | /* io port resource */ | 
|---|
| 160 | for (i = 0; i < 4; i++) { | 
|---|
| 161 | reg = read_pci_config(bus, slot, func: 1, offset: 0xc0 + (i << 3)); | 
|---|
| 162 | if (!(reg & 3)) | 
|---|
| 163 | continue; | 
|---|
| 164 |  | 
|---|
| 165 | start = reg & 0xfff000; | 
|---|
| 166 | reg = read_pci_config(bus, slot, func: 1, offset: 0xc4 + (i << 3)); | 
|---|
| 167 | node = reg & 0x07; | 
|---|
| 168 | link = (reg >> 4) & 0x03; | 
|---|
| 169 | end = (reg & 0xfff000) | 0xfff; | 
|---|
| 170 |  | 
|---|
| 171 | info = find_pci_root_info(node, link); | 
|---|
| 172 | if (!info) | 
|---|
| 173 | continue; /* not found */ | 
|---|
| 174 |  | 
|---|
| 175 | printk(KERN_DEBUG "node %d link %d: io port [%llx, %llx]\n", | 
|---|
| 176 | node, link, start, end); | 
|---|
| 177 |  | 
|---|
| 178 | /* kernel only handle 16 bit only */ | 
|---|
| 179 | if (end > 0xffff) | 
|---|
| 180 | end = 0xffff; | 
|---|
| 181 | update_res(info, start, end, IORESOURCE_IO, merge: 1); | 
|---|
| 182 | subtract_range(range, RANGE_NUM, start, end: end + 1); | 
|---|
| 183 | } | 
|---|
| 184 | /* add left over io port range to def node/link, [0, 0xffff] */ | 
|---|
| 185 | /* find the position */ | 
|---|
| 186 | info = find_pci_root_info(node: def_node, link: def_link); | 
|---|
| 187 | if (info) { | 
|---|
| 188 | for (i = 0; i < RANGE_NUM; i++) { | 
|---|
| 189 | if (!range[i].end) | 
|---|
| 190 | continue; | 
|---|
| 191 |  | 
|---|
| 192 | update_res(info, start: range[i].start, end: range[i].end - 1, | 
|---|
| 193 | IORESOURCE_IO, merge: 1); | 
|---|
| 194 | } | 
|---|
| 195 | } | 
|---|
| 196 |  | 
|---|
| 197 | memset(s: range, c: 0, n: sizeof(range)); | 
|---|
| 198 | /* 0xfd00000000-0xffffffffff for HT */ | 
|---|
| 199 | end = cap_resource(val: (0xfdULL<<32) - 1); | 
|---|
| 200 | end++; | 
|---|
| 201 | add_range(range, RANGE_NUM, nr_range: 0, start: 0, end); | 
|---|
| 202 |  | 
|---|
| 203 | /* need to take out [0, TOM) for RAM*/ | 
|---|
| 204 | address = MSR_K8_TOP_MEM1; | 
|---|
| 205 | rdmsrq(address, val); | 
|---|
| 206 | end = (val & 0xffffff800000ULL); | 
|---|
| 207 | printk(KERN_INFO "TOM: %016llx aka %lldM\n", end, end>>20); | 
|---|
| 208 | if (end < (1ULL<<32)) | 
|---|
| 209 | subtract_range(range, RANGE_NUM, start: 0, end); | 
|---|
| 210 |  | 
|---|
| 211 | /* get mmconfig */ | 
|---|
| 212 | fam10h_mmconf = amd_get_mmconfig_range(res: &fam10h_mmconf_res); | 
|---|
| 213 | /* need to take out mmconf range */ | 
|---|
| 214 | if (fam10h_mmconf) { | 
|---|
| 215 | printk(KERN_DEBUG "Fam 10h mmconf %pR\n", fam10h_mmconf); | 
|---|
| 216 | fam10h_mmconf_start = fam10h_mmconf->start; | 
|---|
| 217 | fam10h_mmconf_end = fam10h_mmconf->end; | 
|---|
| 218 | subtract_range(range, RANGE_NUM, start: fam10h_mmconf_start, | 
|---|
| 219 | end: fam10h_mmconf_end + 1); | 
|---|
| 220 | } else { | 
|---|
| 221 | fam10h_mmconf_start = 0; | 
|---|
| 222 | fam10h_mmconf_end = 0; | 
|---|
| 223 | } | 
|---|
| 224 |  | 
|---|
| 225 | /* mmio resource */ | 
|---|
| 226 | for (i = 0; i < 8; i++) { | 
|---|
| 227 | reg = read_pci_config(bus, slot, func: 1, offset: 0x80 + (i << 3)); | 
|---|
| 228 | if (!(reg & 3)) | 
|---|
| 229 | continue; | 
|---|
| 230 |  | 
|---|
| 231 | start = reg & 0xffffff00; /* 39:16 on 31:8*/ | 
|---|
| 232 | start <<= 8; | 
|---|
| 233 | reg = read_pci_config(bus, slot, func: 1, offset: 0x84 + (i << 3)); | 
|---|
| 234 | node = reg & 0x07; | 
|---|
| 235 | link = (reg >> 4) & 0x03; | 
|---|
| 236 | end = (reg & 0xffffff00); | 
|---|
| 237 | end <<= 8; | 
|---|
| 238 | end |= 0xffff; | 
|---|
| 239 |  | 
|---|
| 240 | info = find_pci_root_info(node, link); | 
|---|
| 241 |  | 
|---|
| 242 | if (!info) | 
|---|
| 243 | continue; | 
|---|
| 244 |  | 
|---|
| 245 | printk(KERN_DEBUG "node %d link %d: mmio [%llx, %llx]", | 
|---|
| 246 | node, link, start, end); | 
|---|
| 247 | /* | 
|---|
| 248 | * some sick allocation would have range overlap with fam10h | 
|---|
| 249 | * mmconf range, so need to update start and end. | 
|---|
| 250 | */ | 
|---|
| 251 | if (fam10h_mmconf_end) { | 
|---|
| 252 | int changed = 0; | 
|---|
| 253 | u64 endx = 0; | 
|---|
| 254 | if (start >= fam10h_mmconf_start && | 
|---|
| 255 | start <= fam10h_mmconf_end) { | 
|---|
| 256 | start = fam10h_mmconf_end + 1; | 
|---|
| 257 | changed = 1; | 
|---|
| 258 | } | 
|---|
| 259 |  | 
|---|
| 260 | if (end >= fam10h_mmconf_start && | 
|---|
| 261 | end <= fam10h_mmconf_end) { | 
|---|
| 262 | end = fam10h_mmconf_start - 1; | 
|---|
| 263 | changed = 1; | 
|---|
| 264 | } | 
|---|
| 265 |  | 
|---|
| 266 | if (start < fam10h_mmconf_start && | 
|---|
| 267 | end > fam10h_mmconf_end) { | 
|---|
| 268 | /* we got a hole */ | 
|---|
| 269 | endx = fam10h_mmconf_start - 1; | 
|---|
| 270 | update_res(info, start, end: endx, IORESOURCE_MEM, merge: 0); | 
|---|
| 271 | subtract_range(range, RANGE_NUM, start, | 
|---|
| 272 | end: endx + 1); | 
|---|
| 273 | printk(KERN_CONT " ==> [%llx, %llx]", start, endx); | 
|---|
| 274 | start = fam10h_mmconf_end + 1; | 
|---|
| 275 | changed = 1; | 
|---|
| 276 | } | 
|---|
| 277 | if (changed) { | 
|---|
| 278 | if (start <= end) { | 
|---|
| 279 | printk(KERN_CONT " %s [%llx, %llx]", endx ? "and": "==>", start, end); | 
|---|
| 280 | } else { | 
|---|
| 281 | printk(KERN_CONT "%s\n", endx? "": " ==> none"); | 
|---|
| 282 | continue; | 
|---|
| 283 | } | 
|---|
| 284 | } | 
|---|
| 285 | } | 
|---|
| 286 |  | 
|---|
| 287 | update_res(info, start: cap_resource(val: start), end: cap_resource(val: end), | 
|---|
| 288 | IORESOURCE_MEM, merge: 1); | 
|---|
| 289 | subtract_range(range, RANGE_NUM, start, end: end + 1); | 
|---|
| 290 | printk(KERN_CONT "\n"); | 
|---|
| 291 | } | 
|---|
| 292 |  | 
|---|
| 293 | /* need to take out [4G, TOM2) for RAM*/ | 
|---|
| 294 | /* SYS_CFG */ | 
|---|
| 295 | address = MSR_AMD64_SYSCFG; | 
|---|
| 296 | rdmsrq(address, val); | 
|---|
| 297 | /* TOP_MEM2 is enabled? */ | 
|---|
| 298 | if (val & (1<<21)) { | 
|---|
| 299 | /* TOP_MEM2 */ | 
|---|
| 300 | address = MSR_K8_TOP_MEM2; | 
|---|
| 301 | rdmsrq(address, val); | 
|---|
| 302 | end = (val & 0xffffff800000ULL); | 
|---|
| 303 | printk(KERN_INFO "TOM2: %016llx aka %lldM\n", end, end>>20); | 
|---|
| 304 | subtract_range(range, RANGE_NUM, start: 1ULL<<32, end); | 
|---|
| 305 | } | 
|---|
| 306 |  | 
|---|
| 307 | /* | 
|---|
| 308 | * add left over mmio range to def node/link ? | 
|---|
| 309 | * that is tricky, just record range in from start_min to 4G | 
|---|
| 310 | */ | 
|---|
| 311 | info = find_pci_root_info(node: def_node, link: def_link); | 
|---|
| 312 | if (info) { | 
|---|
| 313 | for (i = 0; i < RANGE_NUM; i++) { | 
|---|
| 314 | if (!range[i].end) | 
|---|
| 315 | continue; | 
|---|
| 316 |  | 
|---|
| 317 | update_res(info, start: cap_resource(val: range[i].start), | 
|---|
| 318 | end: cap_resource(val: range[i].end - 1), | 
|---|
| 319 | IORESOURCE_MEM, merge: 1); | 
|---|
| 320 | } | 
|---|
| 321 | } | 
|---|
| 322 |  | 
|---|
| 323 | list_for_each_entry(info, &pci_root_infos, list) { | 
|---|
| 324 | int busnum; | 
|---|
| 325 | struct pci_root_res *root_res; | 
|---|
| 326 |  | 
|---|
| 327 | busnum = info->busn.start; | 
|---|
| 328 | printk(KERN_DEBUG "bus: %pR on node %x link %x\n", | 
|---|
| 329 | &info->busn, info->node, info->link); | 
|---|
| 330 | list_for_each_entry(root_res, &info->resources, list) | 
|---|
| 331 | printk(KERN_DEBUG "bus: %02x %pR\n", | 
|---|
| 332 | busnum, &root_res->res); | 
|---|
| 333 | } | 
|---|
| 334 |  | 
|---|
| 335 | return 0; | 
|---|
| 336 | } | 
|---|
| 337 |  | 
|---|
| 338 | #define ENABLE_CF8_EXT_CFG      (1ULL << 46) | 
|---|
| 339 |  | 
|---|
| 340 | static int amd_bus_cpu_online(unsigned int cpu) | 
|---|
| 341 | { | 
|---|
| 342 | u64 reg; | 
|---|
| 343 |  | 
|---|
| 344 | rdmsrq(MSR_AMD64_NB_CFG, reg); | 
|---|
| 345 | if (!(reg & ENABLE_CF8_EXT_CFG)) { | 
|---|
| 346 | reg |= ENABLE_CF8_EXT_CFG; | 
|---|
| 347 | wrmsrq(MSR_AMD64_NB_CFG, val: reg); | 
|---|
| 348 | } | 
|---|
| 349 | return 0; | 
|---|
| 350 | } | 
|---|
| 351 |  | 
|---|
| 352 | static void __init pci_enable_pci_io_ecs(void) | 
|---|
| 353 | { | 
|---|
| 354 | #ifdef CONFIG_AMD_NB | 
|---|
| 355 | unsigned int i, n; | 
|---|
| 356 |  | 
|---|
| 357 | for (n = i = 0; !n && amd_nb_bus_dev_ranges[i].dev_limit; ++i) { | 
|---|
| 358 | u8 bus = amd_nb_bus_dev_ranges[i].bus; | 
|---|
| 359 | u8 slot = amd_nb_bus_dev_ranges[i].dev_base; | 
|---|
| 360 | u8 limit = amd_nb_bus_dev_ranges[i].dev_limit; | 
|---|
| 361 |  | 
|---|
| 362 | for (; slot < limit; ++slot) { | 
|---|
| 363 | u32 val = read_pci_config(bus, slot, func: 3, offset: 0); | 
|---|
| 364 |  | 
|---|
| 365 | if (!early_is_amd_nb(value: val)) | 
|---|
| 366 | continue; | 
|---|
| 367 |  | 
|---|
| 368 | val = read_pci_config(bus, slot, func: 3, offset: 0x8c); | 
|---|
| 369 | if (!(val & (ENABLE_CF8_EXT_CFG >> 32))) { | 
|---|
| 370 | val |= ENABLE_CF8_EXT_CFG >> 32; | 
|---|
| 371 | write_pci_config(bus, slot, func: 3, offset: 0x8c, val); | 
|---|
| 372 | } | 
|---|
| 373 | ++n; | 
|---|
| 374 | } | 
|---|
| 375 | } | 
|---|
| 376 | #endif | 
|---|
| 377 | } | 
|---|
| 378 |  | 
|---|
| 379 | static int __init pci_io_ecs_init(void) | 
|---|
| 380 | { | 
|---|
| 381 | int ret; | 
|---|
| 382 |  | 
|---|
| 383 | /* assume all cpus from fam10h have IO ECS */ | 
|---|
| 384 | if (boot_cpu_data.x86 < 0x10) | 
|---|
| 385 | return 0; | 
|---|
| 386 |  | 
|---|
| 387 | /* Try the PCI method first. */ | 
|---|
| 388 | if (early_pci_allowed()) | 
|---|
| 389 | pci_enable_pci_io_ecs(); | 
|---|
| 390 |  | 
|---|
| 391 | ret = cpuhp_setup_state(state: CPUHP_AP_ONLINE_DYN, name: "pci/amd_bus:online", | 
|---|
| 392 | startup: amd_bus_cpu_online, NULL); | 
|---|
| 393 | WARN_ON(ret < 0); | 
|---|
| 394 |  | 
|---|
| 395 | pci_probe |= PCI_HAS_IO_ECS; | 
|---|
| 396 |  | 
|---|
| 397 | return 0; | 
|---|
| 398 | } | 
|---|
| 399 |  | 
|---|
| 400 | static int __init amd_postcore_init(void) | 
|---|
| 401 | { | 
|---|
| 402 | if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD && | 
|---|
| 403 | boot_cpu_data.x86_vendor != X86_VENDOR_HYGON) | 
|---|
| 404 | return 0; | 
|---|
| 405 |  | 
|---|
| 406 | early_root_info_init(); | 
|---|
| 407 | pci_io_ecs_init(); | 
|---|
| 408 |  | 
|---|
| 409 | return 0; | 
|---|
| 410 | } | 
|---|
| 411 |  | 
|---|
| 412 | postcore_initcall(amd_postcore_init); | 
|---|
| 413 |  | 
|---|