1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2019, Joyent, Inc. 14 * Copyright 2020 Oxide Computer Company 15 */ 16 17 /* 18 * Nexus Driver for AMD Zen family systems. The purpose of this driver is to 19 * provide access to the following resources in a single, centralized fashion: 20 * 21 * - The per-chip Data Fabric 22 * - The North Bridge 23 * - The System Management Network (SMN) 24 * 25 * This is a nexus driver as once we have attached to all the requisite 26 * components, we will enumerate child devices which consume this functionality. 27 * 28 * ------------------------ 29 * Mapping Devices Together 30 * ------------------------ 31 * 32 * The operating system needs to expose things like temperature sensors and DRAM 33 * configuration registers in terms that are meaningful to the system such as 34 * logical CPUs, cores, etc. This driver attaches to the PCI IDs that represent 35 * the northbridge and data fabric; however, there are multiple PCI devices (one 36 * per die) that exist. This driver does manage to map all of these three things 37 * together; however, it requires some acrobatics. Unfortunately, there's no 38 * direct way to map a northbridge to its corresponding die. However, we can map 39 * a CPU die to a data fabric PCI device and a data fabric PCI device to a 40 * corresponding northbridge PCI device. 41 * 42 * In current Zen based products, there is a direct mapping between processor 43 * nodes and a data fabric PCI device. All of the devices are on PCI Bus 0 and 44 * start from Device 0x18. Device 0x18 maps to processor node 0, 0x19 to 45 * processor node 1, etc. This means that to map a logical CPU to a data fabric 46 * device, we take its processor node id, add it to 0x18 and find the PCI device 47 * that is on bus 0, device 0x18. As each data fabric device is attached based 48 * on its PCI ID, we add it to the global list, amd_nbdf_dfs that is in the 49 * amd_f17nbdf_t structure. 50 * 51 * The northbridge PCI device has a defined device and function, but the PCI bus 52 * that it's on can vary. Each die has its own series of PCI buses that are 53 * assigned to it and the northbridge PCI device is on the first of die-specific 54 * PCI bus for each die. This also means that the northbridge will not show up 55 * on PCI bus 0, which is the PCI bus that all of the data fabric devices are 56 * on. While conventionally the northbridge with the lowest PCI bus value 57 * would correspond to processor node zero, hardware does not guarantee that at 58 * all. Because we don't want to be at the mercy of firmware, we don't rely on 59 * this ordering, even though we have yet to find a system that deviates from 60 * this scheme. 61 * 62 * One of the registers in the data fabric device's function 0 63 * (AMDZEN_DF_F0_CFG_ADDR_CTL) happens to have the first PCI bus that is 64 * associated with the processor node. This means that we can map a data fabric 65 * device to a northbridge by finding the northbridge whose PCI bus matches the 66 * value in the corresponding data fabric's AMDZEN_DF_F0_CFG_ADDR_CTL. 67 * 68 * We can map a northbridge to a data fabric device and a data fabric device to 69 * a die. Because these are generally 1:1 mappings, there is a transitive 70 * relationship and therefore we know which northbridge is associated with which 71 * processor die. This is summarized in the following image: 72 * 73 * +-------+ +-----------------------------------+ +--------------+ 74 * | Die 0 |--->| Data Fabric PCI BDF 0/18/0 |------->| Northbridge | 75 * +-------+ | AMDZEN_DF_F0_CFG_ADDR_CTL: bus 10 | | PCI 10/0/0 | 76 * ... +-----------------------------------+ +--------------+ 77 * +-------+ +------------------------------------+ +--------------+ 78 * | Die n |---->| Data Fabric PCI BDF 0/18+n/0 |------->| Northbridge | 79 * +-------+ | AMDZEN_DF_F0_CFG_ADDR_CTL: bus 133 | | PCI 133/0/0 | 80 * +------------------------------------+ +--------------+ 81 * 82 * Note, the PCI buses used by the northbridges here are arbitrary. They do not 83 * reflect the actual values by hardware; however, the bus/device/function (BDF) 84 * of the data fabric accurately models hardware. All of the BDF values are in 85 * hex. 86 * 87 * Starting with the Rome generation of processors (Family 17h Model 30-3Fh), 88 * AMD has multiple northbridges that exist on a given die. All of these 89 * northbridges share the same data fabric and system management network port. 90 * From our perspective this means that some of the northbridge devices will be 91 * redundant and that we will no longer have a 1:1 mapping between the 92 * northbridge and the data fabric devices. Every data fabric will have a 93 * northbridge, but not every northbridge will have a data fabric device mapped. 94 * Because we're always trying to map from a die to a northbridge and not the 95 * reverse, the fact that there are extra northbridge devices hanging around 96 * that we don't know about shouldn't be a problem. 97 * 98 * ------------------------------- 99 * Attach and Detach Complications 100 * ------------------------------- 101 * 102 * Because we need to map different PCI devices together, this means that we 103 * have multiple dev_info_t structures that we need to manage. Each of these is 104 * independently attached and detached. While this is easily managed for attach, 105 * it is not for detach. Each of these devices is a 'stub'. 106 * 107 * Once a device has been detached it will only come back if we have an active 108 * minor node that will be accessed. This means that if they are detached, 109 * nothing would ever cause them to be reattached. The system also doesn't 110 * provide us a way or any guarantees around making sure that we're attached to 111 * all such devices before we detach. As a result, unfortunately, it's easier to 112 * basically have detach always fail. 113 * 114 * --------------- 115 * Exposed Devices 116 * --------------- 117 * 118 * Rather than try and have all of the different functions that could be 119 * provided by one driver, we instead have created a nexus driver that will 120 * itself try and load children. Children are all pseudo-device drivers that 121 * provide different pieces of functionality that use this. 122 */ 123 124 #include <sys/modctl.h> 125 #include <sys/conf.h> 126 #include <sys/devops.h> 127 #include <sys/ddi.h> 128 #include <sys/sunddi.h> 129 #include <sys/pci.h> 130 #include <sys/sysmacros.h> 131 #include <sys/sunndi.h> 132 #include <sys/x86_archext.h> 133 #include <sys/cpuvar.h> 134 135 #include "amdzen.h" 136 137 amdzen_t *amdzen_data; 138 139 /* 140 * Array of northbridge IDs that we care about. 141 */ 142 static const uint16_t amdzen_nb_ids[] = { 143 /* Family 17h Ryzen, Epyc Models 00h-0fh (Zen uarch) */ 144 0x1450, 145 /* Family 17h Raven Ridge, Kestrel, Dali Models 10h-2fh (Zen uarch) */ 146 0x15d0, 147 /* Family 17h Epyc Models 30h-3fh, Matisse 70-7fh (Zen 2 uarch) */ 148 0x1480, 149 /* Family 17h Renoir Models 60-6fh (Zen 2 uarch) */ 150 0x1630 151 }; 152 153 typedef struct { 154 char *acd_name; 155 amdzen_child_t acd_addr; 156 } amdzen_child_data_t; 157 158 static const amdzen_child_data_t amdzen_children[] = { 159 { "smntemp", AMDZEN_C_SMNTEMP }, 160 { "usmn", AMDZEN_C_USMN }, 161 { "zen_udf", AMDZEN_C_ZEN_UDF } 162 }; 163 164 static uint32_t 165 amdzen_stub_get32(amdzen_stub_t *stub, off_t reg) 166 { 167 return (pci_config_get32(stub->azns_cfgspace, reg)); 168 } 169 170 static uint64_t 171 amdzen_stub_get64(amdzen_stub_t *stub, off_t reg) 172 { 173 return (pci_config_get64(stub->azns_cfgspace, reg)); 174 } 175 176 static void 177 amdzen_stub_put32(amdzen_stub_t *stub, off_t reg, uint32_t val) 178 { 179 pci_config_put32(stub->azns_cfgspace, reg, val); 180 } 181 182 /* 183 * Perform a targeted 32-bit indirect read to a specific instance and function. 184 */ 185 static uint32_t 186 amdzen_df_read32(amdzen_t *azn, amdzen_df_t *df, uint8_t inst, uint8_t func, 187 uint16_t reg) 188 { 189 uint32_t val; 190 191 VERIFY(MUTEX_HELD(&azn->azn_mutex)); 192 val = AMDZEN_DF_F4_FICAA_TARG_INST | AMDZEN_DF_F4_FICAA_SET_REG(reg) | 193 AMDZEN_DF_F4_FICAA_SET_FUNC(func) | 194 AMDZEN_DF_F4_FICAA_SET_INST(inst); 195 amdzen_stub_put32(df->adf_funcs[4], AMDZEN_DF_F4_FICAA, val); 196 return (amdzen_stub_get32(df->adf_funcs[4], AMDZEN_DF_F4_FICAD_LO)); 197 } 198 199 /* 200 * Perform a targeted 64-bit indirect read to a specific instance and function. 201 */ 202 static uint64_t 203 amdzen_df_read64(amdzen_t *azn, amdzen_df_t *df, uint8_t inst, uint8_t func, 204 uint16_t reg) 205 { 206 uint32_t val; 207 208 VERIFY(MUTEX_HELD(&azn->azn_mutex)); 209 val = AMDZEN_DF_F4_FICAA_TARG_INST | AMDZEN_DF_F4_FICAA_SET_REG(reg) | 210 AMDZEN_DF_F4_FICAA_SET_FUNC(func) | 211 AMDZEN_DF_F4_FICAA_SET_INST(inst) | AMDZEN_DF_F4_FICAA_SET_64B; 212 amdzen_stub_put32(df->adf_funcs[4], AMDZEN_DF_F4_FICAA, val); 213 return (amdzen_stub_get64(df->adf_funcs[4], AMDZEN_DF_F4_FICAD_LO)); 214 } 215 216 217 static uint32_t 218 amdzen_smn_read32(amdzen_t *azn, amdzen_df_t *df, uint32_t reg) 219 { 220 VERIFY(MUTEX_HELD(&azn->azn_mutex)); 221 amdzen_stub_put32(df->adf_nb, AMDZEN_NB_SMN_ADDR, reg); 222 return (amdzen_stub_get32(df->adf_nb, AMDZEN_NB_SMN_DATA)); 223 } 224 225 static amdzen_df_t * 226 amdzen_df_find(amdzen_t *azn, uint_t dfno) 227 { 228 uint_t i; 229 230 ASSERT(MUTEX_HELD(&azn->azn_mutex)); 231 if (dfno >= azn->azn_ndfs) { 232 return (NULL); 233 } 234 235 for (i = 0; i < azn->azn_ndfs; i++) { 236 amdzen_df_t *df = &azn->azn_dfs[i]; 237 if ((df->adf_flags & AMDZEN_DF_F_VALID) == 0) { 238 continue; 239 } 240 241 if (dfno == 0) { 242 return (df); 243 } 244 dfno--; 245 } 246 247 return (NULL); 248 } 249 250 /* 251 * Client functions that are used by nexus children. 252 */ 253 int 254 amdzen_c_smn_read32(uint_t dfno, uint32_t reg, uint32_t *valp) 255 { 256 amdzen_df_t *df; 257 amdzen_t *azn = amdzen_data; 258 259 mutex_enter(&azn->azn_mutex); 260 df = amdzen_df_find(azn, dfno); 261 if (df == NULL) { 262 mutex_exit(&azn->azn_mutex); 263 return (ENOENT); 264 } 265 266 if ((df->adf_flags & AMDZEN_DF_F_FOUND_NB) == 0) { 267 mutex_exit(&azn->azn_mutex); 268 return (ENXIO); 269 } 270 271 *valp = amdzen_smn_read32(azn, df, reg); 272 mutex_exit(&azn->azn_mutex); 273 return (0); 274 } 275 276 uint_t 277 amdzen_c_df_count(void) 278 { 279 uint_t ret; 280 amdzen_t *azn = amdzen_data; 281 282 mutex_enter(&azn->azn_mutex); 283 ret = azn->azn_ndfs; 284 mutex_exit(&azn->azn_mutex); 285 return (ret); 286 } 287 288 int 289 amdzen_c_df_read32(uint_t dfno, uint8_t inst, uint8_t func, 290 uint16_t reg, uint32_t *valp) 291 { 292 amdzen_df_t *df; 293 amdzen_t *azn = amdzen_data; 294 295 mutex_enter(&azn->azn_mutex); 296 df = amdzen_df_find(azn, dfno); 297 if (df == NULL) { 298 mutex_exit(&azn->azn_mutex); 299 return (ENOENT); 300 } 301 302 *valp = amdzen_df_read32(azn, df, inst, func, reg); 303 mutex_exit(&azn->azn_mutex); 304 305 return (0); 306 } 307 308 int 309 amdzen_c_df_read64(uint_t dfno, uint8_t inst, uint8_t func, 310 uint16_t reg, uint64_t *valp) 311 { 312 amdzen_df_t *df; 313 amdzen_t *azn = amdzen_data; 314 315 mutex_enter(&azn->azn_mutex); 316 df = amdzen_df_find(azn, dfno); 317 if (df == NULL) { 318 mutex_exit(&azn->azn_mutex); 319 return (ENOENT); 320 } 321 322 *valp = amdzen_df_read64(azn, df, inst, func, reg); 323 mutex_exit(&azn->azn_mutex); 324 325 return (0); 326 } 327 328 static boolean_t 329 amdzen_create_child(amdzen_t *azn, const amdzen_child_data_t *acd) 330 { 331 int ret; 332 dev_info_t *child; 333 334 if (ndi_devi_alloc(azn->azn_dip, acd->acd_name, 335 (pnode_t)DEVI_SID_NODEID, &child) != NDI_SUCCESS) { 336 dev_err(azn->azn_dip, CE_WARN, "!failed to allocate child " 337 "dip for %s", acd->acd_name); 338 return (B_FALSE); 339 } 340 341 ddi_set_parent_data(child, (void *)acd); 342 if ((ret = ndi_devi_online(child, 0)) != NDI_SUCCESS) { 343 dev_err(azn->azn_dip, CE_WARN, "!failed to online child " 344 "dip %s: %d", acd->acd_name, ret); 345 return (B_FALSE); 346 } 347 348 return (B_TRUE); 349 } 350 351 static boolean_t 352 amdzen_map_dfs(amdzen_t *azn) 353 { 354 amdzen_stub_t *stub; 355 356 ASSERT(MUTEX_HELD(&azn->azn_mutex)); 357 358 for (stub = list_head(&azn->azn_df_stubs); stub != NULL; 359 stub = list_next(&azn->azn_df_stubs, stub)) { 360 amdzen_df_t *df; 361 uint_t dfno; 362 363 dfno = stub->azns_dev - AMDZEN_DF_FIRST_DEVICE; 364 if (dfno > AMDZEN_MAX_DFS) { 365 dev_err(stub->azns_dip, CE_WARN, "encountered df " 366 "device with illegal DF PCI b/d/f: 0x%x/%x/%x", 367 stub->azns_bus, stub->azns_dev, stub->azns_func); 368 goto err; 369 } 370 371 df = &azn->azn_dfs[dfno]; 372 373 if (stub->azns_func >= AMDZEN_MAX_DF_FUNCS) { 374 dev_err(stub->azns_dip, CE_WARN, "encountered df " 375 "device with illegal DF PCI b/d/f: 0x%x/%x/%x", 376 stub->azns_bus, stub->azns_dev, stub->azns_func); 377 goto err; 378 } 379 380 if (df->adf_funcs[stub->azns_func] != NULL) { 381 dev_err(stub->azns_dip, CE_WARN, "encountered " 382 "duplicate df device with DF PCI b/d/f: 0x%x/%x/%x", 383 stub->azns_bus, stub->azns_dev, stub->azns_func); 384 goto err; 385 } 386 df->adf_funcs[stub->azns_func] = stub; 387 } 388 389 return (B_TRUE); 390 391 err: 392 azn->azn_flags |= AMDZEN_F_DEVICE_ERROR; 393 return (B_FALSE); 394 } 395 396 static boolean_t 397 amdzen_check_dfs(amdzen_t *azn) 398 { 399 uint_t i; 400 boolean_t ret = B_TRUE; 401 402 for (i = 0; i < AMDZEN_MAX_DFS; i++) { 403 amdzen_df_t *df = &azn->azn_dfs[i]; 404 uint_t count = 0; 405 406 /* 407 * We require all platforms to have DFs functions 0-6. Not all 408 * platforms have DF function 7. 409 */ 410 for (uint_t func = 0; func < AMDZEN_MAX_DF_FUNCS - 1; func++) { 411 if (df->adf_funcs[func] != NULL) { 412 count++; 413 } 414 } 415 416 if (count == 0) 417 continue; 418 419 if (count != 7) { 420 ret = B_FALSE; 421 dev_err(azn->azn_dip, CE_WARN, "df %u devices " 422 "incomplete", i); 423 } else { 424 df->adf_flags |= AMDZEN_DF_F_VALID; 425 azn->azn_ndfs++; 426 } 427 } 428 429 return (ret); 430 } 431 432 static const uint8_t amdzen_df_rome_ids[0x2b] = { 433 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20, 21, 22, 23, 434 24, 25, 26, 27, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 435 44, 45, 46, 47, 48 436 }; 437 438 /* 439 * Initialize our knowledge about a given series of nodes on the data fabric. 440 */ 441 static void 442 amdzen_setup_df(amdzen_t *azn, amdzen_df_t *df) 443 { 444 uint_t i; 445 uint32_t val; 446 447 val = amdzen_stub_get32(df->adf_funcs[0], AMDZEN_DF_F0_CFG_ADDR_CTL); 448 df->adf_nb_busno = AMDZEN_DF_F0_CFG_ADDR_CTL_BUS_NUM(val); 449 val = amdzen_stub_get32(df->adf_funcs[0], AMDZEN_DF_F0_FBICNT); 450 df->adf_nents = AMDZEN_DF_F0_FBICNT_COUNT(val); 451 if (df->adf_nents == 0) 452 return; 453 df->adf_ents = kmem_zalloc(sizeof (amdzen_df_ent_t) * df->adf_nents, 454 KM_SLEEP); 455 456 for (i = 0; i < df->adf_nents; i++) { 457 amdzen_df_ent_t *dfe = &df->adf_ents[i]; 458 uint8_t inst = i; 459 460 /* 461 * Unfortunately, Rome uses a discontinuous instance ID pattern 462 * while everything else we can find uses a contiguous instance 463 * ID pattern. This means that for Rome, we need to adjust the 464 * indexes that we iterate over, though the total number of 465 * entries is right. 466 */ 467 if (df->adf_funcs[0]->azns_did == 0x1490) { 468 if (inst > ARRAY_SIZE(amdzen_df_rome_ids)) { 469 dev_err(azn->azn_dip, CE_WARN, "Rome family " 470 "processor reported more ids than the PPR, " 471 "resting %u to instance zero", inst); 472 inst = 0; 473 } else { 474 inst = amdzen_df_rome_ids[inst]; 475 } 476 } 477 478 dfe->adfe_drvid = inst; 479 dfe->adfe_info0 = amdzen_df_read32(azn, df, inst, 0, 480 AMDZEN_DF_F0_FBIINFO0); 481 dfe->adfe_info1 = amdzen_df_read32(azn, df, inst, 0, 482 AMDZEN_DF_F0_FBIINFO1); 483 dfe->adfe_info2 = amdzen_df_read32(azn, df, inst, 0, 484 AMDZEN_DF_F0_FBIINFO2); 485 dfe->adfe_info3 = amdzen_df_read32(azn, df, inst, 0, 486 AMDZEN_DF_F0_FBIINFO3); 487 dfe->adfe_syscfg = amdzen_df_read32(azn, df, inst, 1, 488 AMDZEN_DF_F1_SYSCFG); 489 dfe->adfe_mask0 = amdzen_df_read32(azn, df, inst, 1, 490 AMDZEN_DF_F1_FIDMASK0); 491 dfe->adfe_mask1 = amdzen_df_read32(azn, df, inst, 1, 492 AMDZEN_DF_F1_FIDMASK1); 493 494 dfe->adfe_type = AMDZEN_DF_F0_FBIINFO0_TYPE(dfe->adfe_info0); 495 dfe->adfe_sdp_width = 496 AMDZEN_DF_F0_FBIINFO0_SDP_WIDTH(dfe->adfe_info0); 497 if (AMDZEN_DF_F0_FBIINFO0_ENABLED(dfe->adfe_info0)) { 498 dfe->adfe_flags |= AMDZEN_DFE_F_ENABLED; 499 } 500 dfe->adfe_fti_width = 501 AMDZEN_DF_F0_FBIINFO0_FTI_WIDTH(dfe->adfe_info0); 502 dfe->adfe_sdp_count = 503 AMDZEN_DF_F0_FBIINFO0_SDP_PCOUNT(dfe->adfe_info0); 504 dfe->adfe_fti_count = 505 AMDZEN_DF_F0_FBIINFO0_FTI_PCOUNT(dfe->adfe_info0); 506 if (AMDZEN_DF_F0_FBIINFO0_HAS_MCA(dfe->adfe_info0)) { 507 dfe->adfe_flags |= AMDZEN_DFE_F_MCA; 508 } 509 dfe->adfe_subtype = 510 AMDZEN_DF_F0_FBIINFO0_SUBTYPE(dfe->adfe_info0); 511 512 dfe->adfe_inst_id = 513 AMDZEN_DF_F0_FBIINFO3_INSTID(dfe->adfe_info3); 514 dfe->adfe_fabric_id = 515 AMDZEN_DF_F0_FBIINFO3_FABID(dfe->adfe_info3); 516 } 517 518 df->adf_syscfg = amdzen_stub_get32(df->adf_funcs[1], 519 AMDZEN_DF_F1_SYSCFG); 520 df->adf_nodeid = AMDZEN_DF_F1_SYSCFG_NODEID(df->adf_syscfg); 521 df->adf_mask0 = amdzen_stub_get32(df->adf_funcs[1], 522 AMDZEN_DF_F1_FIDMASK0); 523 df->adf_mask1 = amdzen_stub_get32(df->adf_funcs[1], 524 AMDZEN_DF_F1_FIDMASK1); 525 } 526 527 static void 528 amdzen_find_nb(amdzen_t *azn, amdzen_df_t *df) 529 { 530 amdzen_stub_t *stub; 531 532 for (stub = list_head(&azn->azn_nb_stubs); stub != NULL; 533 stub = list_next(&azn->azn_nb_stubs, stub)) { 534 if (stub->azns_bus == df->adf_nb_busno) { 535 df->adf_flags |= AMDZEN_DF_F_FOUND_NB; 536 df->adf_nb = stub; 537 return; 538 } 539 } 540 } 541 542 static void 543 amdzen_nexus_init(void *arg) 544 { 545 uint_t i; 546 amdzen_t *azn = arg; 547 548 /* 549 * First go through all of the stubs and assign the DF entries. 550 */ 551 mutex_enter(&azn->azn_mutex); 552 if (!amdzen_map_dfs(azn) || !amdzen_check_dfs(azn)) { 553 azn->azn_flags |= AMDZEN_F_MAP_ERROR; 554 goto done; 555 } 556 557 for (i = 0; i < AMDZEN_MAX_DFS; i++) { 558 amdzen_df_t *df = &azn->azn_dfs[i]; 559 560 if ((df->adf_flags & AMDZEN_DF_F_VALID) == 0) 561 continue; 562 amdzen_setup_df(azn, df); 563 amdzen_find_nb(azn, df); 564 } 565 566 /* 567 * Not all children may be installed. As such, we do not treat the 568 * failure of a child as fatal to the driver. 569 */ 570 mutex_exit(&azn->azn_mutex); 571 for (i = 0; i < ARRAY_SIZE(amdzen_children); i++) { 572 (void) amdzen_create_child(azn, &amdzen_children[i]); 573 } 574 mutex_enter(&azn->azn_mutex); 575 576 done: 577 azn->azn_flags &= ~AMDZEN_F_ATTACH_DISPATCHED; 578 azn->azn_flags |= AMDZEN_F_ATTACH_COMPLETE; 579 azn->azn_taskqid = TASKQID_INVALID; 580 cv_broadcast(&azn->azn_cv); 581 mutex_exit(&azn->azn_mutex); 582 } 583 584 static int 585 amdzen_stub_scan_cb(dev_info_t *dip, void *arg) 586 { 587 amdzen_t *azn = arg; 588 uint16_t vid, did; 589 int *regs; 590 uint_t nregs, i; 591 boolean_t match = B_FALSE; 592 593 if (dip == ddi_root_node()) { 594 return (DDI_WALK_CONTINUE); 595 } 596 597 /* 598 * If a node in question is not a pci node, then we have no interest in 599 * it as all the stubs that we care about are related to pci devices. 600 */ 601 if (strncmp("pci", ddi_get_name(dip), 3) != 0) { 602 return (DDI_WALK_PRUNECHILD); 603 } 604 605 /* 606 * If we can't get a device or vendor ID and prove that this is an AMD 607 * part, then we don't care about it. 608 */ 609 vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 610 "vendor-id", PCI_EINVAL16); 611 did = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 612 "device-id", PCI_EINVAL16); 613 if (vid == PCI_EINVAL16 || did == PCI_EINVAL16) { 614 return (DDI_WALK_CONTINUE); 615 } 616 617 if (vid != AMDZEN_PCI_VID_AMD) { 618 return (DDI_WALK_CONTINUE); 619 } 620 621 for (i = 0; i < ARRAY_SIZE(amdzen_nb_ids); i++) { 622 if (amdzen_nb_ids[i] == did) { 623 match = B_TRUE; 624 } 625 } 626 627 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 628 "reg", ®s, &nregs) != DDI_PROP_SUCCESS) { 629 return (DDI_WALK_CONTINUE); 630 } 631 632 if (nregs == 0) { 633 ddi_prop_free(regs); 634 return (DDI_WALK_CONTINUE); 635 } 636 637 if (PCI_REG_BUS_G(regs[0]) == AMDZEN_DF_BUSNO && 638 PCI_REG_DEV_G(regs[0]) >= AMDZEN_DF_FIRST_DEVICE) { 639 match = B_TRUE; 640 } 641 642 ddi_prop_free(regs); 643 if (match) { 644 mutex_enter(&azn->azn_mutex); 645 azn->azn_nscanned++; 646 mutex_exit(&azn->azn_mutex); 647 } 648 649 return (DDI_WALK_CONTINUE); 650 } 651 652 static void 653 amdzen_stub_scan(void *arg) 654 { 655 amdzen_t *azn = arg; 656 657 mutex_enter(&azn->azn_mutex); 658 azn->azn_nscanned = 0; 659 mutex_exit(&azn->azn_mutex); 660 661 ddi_walk_devs(ddi_root_node(), amdzen_stub_scan_cb, azn); 662 663 mutex_enter(&azn->azn_mutex); 664 azn->azn_flags &= ~AMDZEN_F_SCAN_DISPATCHED; 665 azn->azn_flags |= AMDZEN_F_SCAN_COMPLETE; 666 667 if (azn->azn_nscanned == 0) { 668 azn->azn_flags |= AMDZEN_F_UNSUPPORTED; 669 azn->azn_taskqid = TASKQID_INVALID; 670 cv_broadcast(&azn->azn_cv); 671 } else if (azn->azn_npresent == azn->azn_nscanned) { 672 azn->azn_flags |= AMDZEN_F_ATTACH_DISPATCHED; 673 azn->azn_taskqid = taskq_dispatch(system_taskq, 674 amdzen_nexus_init, azn, TQ_SLEEP); 675 } 676 mutex_exit(&azn->azn_mutex); 677 } 678 679 /* 680 * Unfortunately we can't really let the stubs detach as we may need them to be 681 * available for client operations. We may be able to improve this if we know 682 * that the actual nexus is going away. However, as long as it's active, we need 683 * all the stubs. 684 */ 685 int 686 amdzen_detach_stub(dev_info_t *dip, ddi_detach_cmd_t cmd) 687 { 688 if (cmd == DDI_SUSPEND) { 689 return (DDI_SUCCESS); 690 } 691 692 return (DDI_FAILURE); 693 } 694 695 int 696 amdzen_attach_stub(dev_info_t *dip, ddi_attach_cmd_t cmd) 697 { 698 int *regs, reg; 699 uint_t nregs, i; 700 uint16_t vid, did; 701 amdzen_stub_t *stub; 702 amdzen_t *azn = amdzen_data; 703 boolean_t valid = B_FALSE; 704 boolean_t nb = B_FALSE; 705 706 if (cmd == DDI_RESUME) { 707 return (DDI_SUCCESS); 708 } else if (cmd != DDI_ATTACH) { 709 return (DDI_FAILURE); 710 } 711 712 /* 713 * Make sure that the stub that we've been asked to attach is a pci type 714 * device. If not, then there is no reason for us to proceed. 715 */ 716 if (strncmp("pci", ddi_get_name(dip), 3) != 0) { 717 dev_err(dip, CE_WARN, "asked to attach a bad AMD Zen nexus " 718 "stub: %s", ddi_get_name(dip)); 719 return (DDI_FAILURE); 720 } 721 vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 722 "vendor-id", PCI_EINVAL16); 723 did = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 724 "device-id", PCI_EINVAL16); 725 if (vid == PCI_EINVAL16 || did == PCI_EINVAL16) { 726 dev_err(dip, CE_WARN, "failed to get PCI ID properties"); 727 return (DDI_FAILURE); 728 } 729 730 if (vid != AMDZEN_PCI_VID_AMD) { 731 dev_err(dip, CE_WARN, "expected AMD vendor ID (0x%x), found " 732 "0x%x", AMDZEN_PCI_VID_AMD, vid); 733 return (DDI_FAILURE); 734 } 735 736 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 737 "reg", ®s, &nregs) != DDI_PROP_SUCCESS) { 738 dev_err(dip, CE_WARN, "failed to get 'reg' property"); 739 return (DDI_FAILURE); 740 } 741 742 if (nregs == 0) { 743 ddi_prop_free(regs); 744 dev_err(dip, CE_WARN, "missing 'reg' property values"); 745 return (DDI_FAILURE); 746 } 747 reg = *regs; 748 ddi_prop_free(regs); 749 750 for (i = 0; i < ARRAY_SIZE(amdzen_nb_ids); i++) { 751 if (amdzen_nb_ids[i] == did) { 752 valid = B_TRUE; 753 nb = B_TRUE; 754 } 755 } 756 757 if (!valid && PCI_REG_BUS_G(reg) == AMDZEN_DF_BUSNO && 758 PCI_REG_DEV_G(reg) >= AMDZEN_DF_FIRST_DEVICE) { 759 valid = B_TRUE; 760 nb = B_FALSE; 761 } 762 763 if (!valid) { 764 dev_err(dip, CE_WARN, "device %s didn't match the nexus list", 765 ddi_get_name(dip)); 766 return (DDI_FAILURE); 767 } 768 769 stub = kmem_alloc(sizeof (amdzen_stub_t), KM_SLEEP); 770 if (pci_config_setup(dip, &stub->azns_cfgspace) != DDI_SUCCESS) { 771 dev_err(dip, CE_WARN, "failed to set up config space"); 772 kmem_free(stub, sizeof (amdzen_stub_t)); 773 return (DDI_FAILURE); 774 } 775 776 stub->azns_dip = dip; 777 stub->azns_vid = vid; 778 stub->azns_did = did; 779 stub->azns_bus = PCI_REG_BUS_G(reg); 780 stub->azns_dev = PCI_REG_DEV_G(reg); 781 stub->azns_func = PCI_REG_FUNC_G(reg); 782 ddi_set_driver_private(dip, stub); 783 784 mutex_enter(&azn->azn_mutex); 785 azn->azn_npresent++; 786 if (nb) { 787 list_insert_tail(&azn->azn_nb_stubs, stub); 788 } else { 789 list_insert_tail(&azn->azn_df_stubs, stub); 790 } 791 792 if ((azn->azn_flags & AMDZEN_F_TASKQ_MASK) == AMDZEN_F_SCAN_COMPLETE && 793 azn->azn_nscanned == azn->azn_npresent) { 794 azn->azn_flags |= AMDZEN_F_ATTACH_DISPATCHED; 795 azn->azn_taskqid = taskq_dispatch(system_taskq, 796 amdzen_nexus_init, azn, TQ_SLEEP); 797 } 798 mutex_exit(&azn->azn_mutex); 799 800 return (DDI_SUCCESS); 801 } 802 803 static int 804 amdzen_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop, 805 void *arg, void *result) 806 { 807 char buf[32]; 808 dev_info_t *child; 809 const amdzen_child_data_t *acd; 810 811 switch (ctlop) { 812 case DDI_CTLOPS_REPORTDEV: 813 if (rdip == NULL) { 814 return (DDI_FAILURE); 815 } 816 cmn_err(CE_CONT, "amdzen nexus: %s@%s, %s%d\n", 817 ddi_node_name(rdip), ddi_get_name_addr(rdip), 818 ddi_driver_name(rdip), ddi_get_instance(rdip)); 819 break; 820 case DDI_CTLOPS_INITCHILD: 821 child = arg; 822 if (child == NULL) { 823 dev_err(dip, CE_WARN, "!no child passed for " 824 "DDI_CTLOPS_INITCHILD"); 825 } 826 827 acd = ddi_get_parent_data(child); 828 if (acd == NULL) { 829 dev_err(dip, CE_WARN, "!missing child parent data"); 830 return (DDI_FAILURE); 831 } 832 833 if (snprintf(buf, sizeof (buf), "%d", acd->acd_addr) >= 834 sizeof (buf)) { 835 dev_err(dip, CE_WARN, "!failed to construct device " 836 "addr due to overflow"); 837 return (DDI_FAILURE); 838 } 839 840 ddi_set_name_addr(child, buf); 841 break; 842 case DDI_CTLOPS_UNINITCHILD: 843 child = arg; 844 if (child == NULL) { 845 dev_err(dip, CE_WARN, "!no child passed for " 846 "DDI_CTLOPS_UNINITCHILD"); 847 } 848 849 ddi_set_name_addr(child, NULL); 850 break; 851 default: 852 return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 853 } 854 return (DDI_SUCCESS); 855 } 856 857 static int 858 amdzen_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 859 { 860 amdzen_t *azn = amdzen_data; 861 862 if (cmd == DDI_RESUME) { 863 return (DDI_SUCCESS); 864 } else if (cmd != DDI_ATTACH) { 865 return (DDI_FAILURE); 866 } 867 868 mutex_enter(&azn->azn_mutex); 869 if (azn->azn_dip != NULL) { 870 dev_err(dip, CE_WARN, "driver is already attached!"); 871 mutex_exit(&azn->azn_mutex); 872 return (DDI_FAILURE); 873 } 874 875 azn->azn_dip = dip; 876 azn->azn_taskqid = taskq_dispatch(system_taskq, amdzen_stub_scan, 877 azn, TQ_SLEEP); 878 azn->azn_flags |= AMDZEN_F_SCAN_DISPATCHED; 879 mutex_exit(&azn->azn_mutex); 880 881 return (DDI_SUCCESS); 882 } 883 884 static int 885 amdzen_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 886 { 887 amdzen_t *azn = amdzen_data; 888 889 if (cmd == DDI_SUSPEND) { 890 return (DDI_SUCCESS); 891 } else if (cmd != DDI_DETACH) { 892 return (DDI_FAILURE); 893 } 894 895 mutex_enter(&azn->azn_mutex); 896 while (azn->azn_taskqid != TASKQID_INVALID) { 897 cv_wait(&azn->azn_cv, &azn->azn_mutex); 898 } 899 900 /* 901 * If we've attached any stub drivers, e.g. this platform is important 902 * for us, then we fail detach. 903 */ 904 if (!list_is_empty(&azn->azn_df_stubs) || 905 !list_is_empty(&azn->azn_nb_stubs)) { 906 mutex_exit(&azn->azn_mutex); 907 return (DDI_FAILURE); 908 } 909 910 azn->azn_dip = NULL; 911 mutex_exit(&azn->azn_mutex); 912 913 return (DDI_SUCCESS); 914 } 915 916 static void 917 amdzen_free(void) 918 { 919 if (amdzen_data == NULL) { 920 return; 921 } 922 923 VERIFY(list_is_empty(&amdzen_data->azn_df_stubs)); 924 list_destroy(&amdzen_data->azn_df_stubs); 925 VERIFY(list_is_empty(&amdzen_data->azn_nb_stubs)); 926 list_destroy(&amdzen_data->azn_nb_stubs); 927 cv_destroy(&amdzen_data->azn_cv); 928 mutex_destroy(&amdzen_data->azn_mutex); 929 kmem_free(amdzen_data, sizeof (amdzen_t)); 930 amdzen_data = NULL; 931 } 932 933 static void 934 amdzen_alloc(void) 935 { 936 amdzen_data = kmem_zalloc(sizeof (amdzen_t), KM_SLEEP); 937 mutex_init(&amdzen_data->azn_mutex, NULL, MUTEX_DRIVER, NULL); 938 list_create(&amdzen_data->azn_df_stubs, sizeof (amdzen_stub_t), 939 offsetof(amdzen_stub_t, azns_link)); 940 list_create(&amdzen_data->azn_nb_stubs, sizeof (amdzen_stub_t), 941 offsetof(amdzen_stub_t, azns_link)); 942 cv_init(&amdzen_data->azn_cv, NULL, CV_DRIVER, NULL); 943 } 944 945 struct bus_ops amdzen_bus_ops = { 946 .busops_rev = BUSO_REV, 947 .bus_map = nullbusmap, 948 .bus_dma_map = ddi_no_dma_map, 949 .bus_dma_allochdl = ddi_no_dma_allochdl, 950 .bus_dma_freehdl = ddi_no_dma_freehdl, 951 .bus_dma_bindhdl = ddi_no_dma_bindhdl, 952 .bus_dma_unbindhdl = ddi_no_dma_unbindhdl, 953 .bus_dma_flush = ddi_no_dma_flush, 954 .bus_dma_win = ddi_no_dma_win, 955 .bus_dma_ctl = ddi_no_dma_mctl, 956 .bus_prop_op = ddi_bus_prop_op, 957 .bus_ctl = amdzen_bus_ctl 958 }; 959 960 static struct dev_ops amdzen_dev_ops = { 961 .devo_rev = DEVO_REV, 962 .devo_refcnt = 0, 963 .devo_getinfo = nodev, 964 .devo_identify = nulldev, 965 .devo_probe = nulldev, 966 .devo_attach = amdzen_attach, 967 .devo_detach = amdzen_detach, 968 .devo_reset = nodev, 969 .devo_quiesce = ddi_quiesce_not_needed, 970 .devo_bus_ops = &amdzen_bus_ops 971 }; 972 973 static struct modldrv amdzen_modldrv = { 974 .drv_modops = &mod_driverops, 975 .drv_linkinfo = "AMD Zen Nexus Driver", 976 .drv_dev_ops = &amdzen_dev_ops 977 }; 978 979 static struct modlinkage amdzen_modlinkage = { 980 .ml_rev = MODREV_1, 981 .ml_linkage = { &amdzen_modldrv, NULL } 982 }; 983 984 int 985 _init(void) 986 { 987 int ret; 988 989 if (cpuid_getvendor(CPU) != X86_VENDOR_AMD) { 990 return (ENOTSUP); 991 } 992 993 if ((ret = mod_install(&amdzen_modlinkage)) == 0) { 994 amdzen_alloc(); 995 } 996 997 return (ret); 998 } 999 1000 int 1001 _info(struct modinfo *modinfop) 1002 { 1003 return (mod_info(&amdzen_modlinkage, modinfop)); 1004 } 1005 1006 int 1007 _fini(void) 1008 { 1009 int ret; 1010 1011 if ((ret = mod_remove(&amdzen_modlinkage)) == 0) { 1012 amdzen_free(); 1013 } 1014 1015 return (ret); 1016 } 1017