1047043c2SRobert Mustacchi /* 2047043c2SRobert Mustacchi * This file and its contents are supplied under the terms of the 3047043c2SRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0. 4047043c2SRobert Mustacchi * You may only use this file in accordance with the terms of version 5047043c2SRobert Mustacchi * 1.0 of the CDDL. 6047043c2SRobert Mustacchi * 7047043c2SRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this 8047043c2SRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at 9047043c2SRobert Mustacchi * http://www.illumos.org/license/CDDL. 10047043c2SRobert Mustacchi */ 11047043c2SRobert Mustacchi 12047043c2SRobert Mustacchi /* 13047043c2SRobert Mustacchi * Copyright 2019, Joyent, Inc. 1419040b41SDan Cross * Copyright 2023 Oxide Computer Company 15047043c2SRobert Mustacchi */ 16047043c2SRobert Mustacchi 17047043c2SRobert Mustacchi /* 18047043c2SRobert Mustacchi * Nexus Driver for AMD Zen family systems. The purpose of this driver is to 19047043c2SRobert Mustacchi * provide access to the following resources in a single, centralized fashion: 20047043c2SRobert Mustacchi * 21047043c2SRobert Mustacchi * - The per-chip Data Fabric 22047043c2SRobert Mustacchi * - The North Bridge 23047043c2SRobert Mustacchi * - The System Management Network (SMN) 24047043c2SRobert Mustacchi * 25047043c2SRobert Mustacchi * This is a nexus driver as once we have attached to all the requisite 26047043c2SRobert Mustacchi * components, we will enumerate child devices which consume this functionality. 27047043c2SRobert Mustacchi * 28047043c2SRobert Mustacchi * ------------------------ 29047043c2SRobert Mustacchi * Mapping Devices Together 30047043c2SRobert Mustacchi * ------------------------ 31047043c2SRobert Mustacchi * 32047043c2SRobert Mustacchi * The operating system needs to expose things like temperature sensors and DRAM 3319040b41SDan Cross * configuration registers in terms of things that are meaningful to the system 3419040b41SDan Cross * such as logical CPUs, cores, etc. This driver attaches to the PCI devices 3519040b41SDan Cross * that represent the northbridge, data fabrics, and dies. Note that there are 3619040b41SDan Cross * multiple northbridge and DF devices (one each per die) and this driver maps 3719040b41SDan Cross * all of these three things together. Unfortunately, this requires some 3819040b41SDan Cross * acrobatics as there is no direct way to map a northbridge to its 3919040b41SDan Cross * corresponding die. Instead, we map a CPU die to a data fabric PCI device and 4019040b41SDan Cross * a data fabric PCI device to a corresponding northbridge PCI device. This 4119040b41SDan Cross * transitive relationship allows us to map from between northbridge and die. 4219040b41SDan Cross * 4319040b41SDan Cross * As each data fabric device is attached, based on vendor and device portions 4419040b41SDan Cross * of the PCI ID, we add it to the DF stubs list in the global amdzen_t 4519040b41SDan Cross * structure, amdzen_data->azn_df_stubs. We must now map these to logical CPUs. 46047043c2SRobert Mustacchi * 47047043c2SRobert Mustacchi * In current Zen based products, there is a direct mapping between processor 4819040b41SDan Cross * nodes and a data fabric PCI device: all of the devices are on PCI Bus 0 and 4919040b41SDan Cross * start from Device 0x18, so device 0x18 maps to processor node 0, 0x19 to 50047043c2SRobert Mustacchi * processor node 1, etc. This means that to map a logical CPU to a data fabric 51047043c2SRobert Mustacchi * device, we take its processor node id, add it to 0x18 and find the PCI device 5219040b41SDan Cross * that is on bus 0 with that ID number. We already discovered the DF devices as 5319040b41SDan Cross * described above. 54047043c2SRobert Mustacchi * 5519040b41SDan Cross * The northbridge PCI device has a well-defined device and function, but the 5619040b41SDan Cross * bus that it is on varies. Each die has its own set of assigned PCI buses and 5719040b41SDan Cross * its northbridge device is on the first die-specific bus. This implies that 5819040b41SDan Cross * the northbridges do not show up on PCI bus 0, as that is the PCI bus that all 5919040b41SDan Cross * of the data fabric devices are on and is not assigned to any particular die. 6019040b41SDan Cross * Additionally, while the northbridge on the lowest-numbered PCI bus 6119040b41SDan Cross * intuitively corresponds to processor node zero, hardware does not guarantee 6219040b41SDan Cross * this. Because we don't want to be at the mercy of firmware, we don't rely on 6319040b41SDan Cross * this ordering assumption, though we have yet to find a system that deviates 6419040b41SDan Cross * from it, either. 65047043c2SRobert Mustacchi * 66047043c2SRobert Mustacchi * One of the registers in the data fabric device's function 0 6719040b41SDan Cross * (AMDZEN_DF_F0_CFG_ADDR_CTL) happens to identify the first PCI bus that is 68047043c2SRobert Mustacchi * associated with the processor node. This means that we can map a data fabric 6919040b41SDan Cross * device to a northbridge by finding the northbridge whose PCI bus ID matches 7019040b41SDan Cross * the value in the corresponding data fabric's AMDZEN_DF_F0_CFG_ADDR_CTL. 71047043c2SRobert Mustacchi * 7219040b41SDan Cross * Given all of the above, we can map a northbridge to a data fabric device and 7319040b41SDan Cross * a die to a data fabric device. Because these are 1:1 mappings, there is a 7419040b41SDan Cross * transitive relationship from northbridge to die. and therefore we know which 7519040b41SDan Cross * northbridge is associated with which processor die. This is summarized in the 7619040b41SDan Cross * following image: 77047043c2SRobert Mustacchi * 78047043c2SRobert Mustacchi * +-------+ +------------------------------------+ +--------------+ 7919040b41SDan Cross * | Die 0 |---->| Data Fabric PCI BDF 0/18/0 |---->| Northbridge | 8019040b41SDan Cross * +-------+ | AMDZEN_DF_F0_CFG_ADDR_CTL: bus 10 | | PCI 10/0/0 | 8119040b41SDan Cross * ... +------------------------------------+ +--------------+ 8219040b41SDan Cross * +-------+ +------------------------------------+ +--------------+ 8319040b41SDan Cross * | Die n |---->| Data Fabric PCI BDF 0/18+n/0 |---->| Northbridge | 84047043c2SRobert Mustacchi * +-------+ | AMDZEN_DF_F0_CFG_ADDR_CTL: bus 133 | | PCI 133/0/0 | 85047043c2SRobert Mustacchi * +------------------------------------+ +--------------+ 86047043c2SRobert Mustacchi * 8719040b41SDan Cross * Note, the PCI buses used by the northbridges here are arbitrary examples that 8819040b41SDan Cross * do not necessarily reflect actual hardware values; however, the 8919040b41SDan Cross * bus/device/function (BDF) of the data fabric accurately models hardware. All 9019040b41SDan Cross * BDF values are in hex. 91047043c2SRobert Mustacchi * 92047043c2SRobert Mustacchi * Starting with the Rome generation of processors (Family 17h Model 30-3Fh), 9319040b41SDan Cross * AMD has multiple northbridges on a given die. All of these northbridges share 9419040b41SDan Cross * the same data fabric and system management network port. From our perspective 9519040b41SDan Cross * this means that some of the northbridge devices will be redundant and that we 9619040b41SDan Cross * no longer have a 1:1 mapping between the northbridge and the data fabric 9719040b41SDan Cross * devices. Every data fabric will have a northbridge, but not every northbridge 9819040b41SDan Cross * will have a data fabric device mapped. Because we're always trying to map 9919040b41SDan Cross * from a die to a northbridge and not the reverse, the fact that there are 10019040b41SDan Cross * extra northbridge devices hanging around that we don't know about shouldn't 10119040b41SDan Cross * be a problem. 102047043c2SRobert Mustacchi * 103047043c2SRobert Mustacchi * ------------------------------- 104047043c2SRobert Mustacchi * Attach and Detach Complications 105047043c2SRobert Mustacchi * ------------------------------- 106047043c2SRobert Mustacchi * 10719040b41SDan Cross * We need to map different PCI devices together. Each device is attached to a 10819040b41SDan Cross * amdzen_stub driver to facilitate integration with the rest of the kernel PCI 10919040b41SDan Cross * machinery and so we have to manage multiple dev_info_t structures, each of 11019040b41SDan Cross * which may be independently attached and detached. 111047043c2SRobert Mustacchi * 11219040b41SDan Cross * This is not particularly complex for attach: our _init routine allocates the 11319040b41SDan Cross * necessary mutex and list structures at module load time, and as each stub is 11419040b41SDan Cross * attached, it calls into this code to be added to the appropriate list. When 11519040b41SDan Cross * the nexus itself is attached, we walk the PCI device tree accumulating a 11619040b41SDan Cross * counter for all devices we expect to be attached. Once the scan is complete 11719040b41SDan Cross * and all such devices are accounted for (stub registration may be happening 11819040b41SDan Cross * asynchronously with respect to nexus attach), we initialize the nexus device 11919040b41SDan Cross * and the attach is complete. 12019040b41SDan Cross * 12119040b41SDan Cross * Most other device drivers support instances that can be brought back after 12219040b41SDan Cross * detach, provided they are associated with an active minor node in the 12319040b41SDan Cross * /devices file system. This driver is different. Once a stub device has been 12419040b41SDan Cross * attached, we do not permit detaching the nexus driver instance, as the kernel 12519040b41SDan Cross * does not give us interlocking guarantees between nexus and stub driver attach 12619040b41SDan Cross * and detach. It is simplest to just unconditionally fail detach once a stub 12719040b41SDan Cross * has attached. 128047043c2SRobert Mustacchi * 129047043c2SRobert Mustacchi * --------------- 130047043c2SRobert Mustacchi * Exposed Devices 131047043c2SRobert Mustacchi * --------------- 132047043c2SRobert Mustacchi * 133047043c2SRobert Mustacchi * Rather than try and have all of the different functions that could be 13419040b41SDan Cross * provided in one driver, we have a nexus driver that tries to load child 13519040b41SDan Cross * pseudo-device drivers that provide specific pieces of functionality. 13671815ce7SRobert Mustacchi * 13771815ce7SRobert Mustacchi * ------- 13871815ce7SRobert Mustacchi * Locking 13971815ce7SRobert Mustacchi * ------- 14071815ce7SRobert Mustacchi * 14119040b41SDan Cross * The amdzen_data structure contains a single lock, azn_mutex. 14271815ce7SRobert Mustacchi * 14319040b41SDan Cross * The various client functions here are intended for our nexus's direct 14419040b41SDan Cross * children, but have been designed in case someone else should depends on this 14519040b41SDan Cross * driver. Once a DF has been discovered, the set of entities inside of it 14619040b41SDan Cross * (adf_nents, adf_ents[]) is considered static, constant data, and iteration 14719040b41SDan Cross * over them does not require locking. However, the discovery of the amd_df_t 14819040b41SDan Cross * does. In addition, locking is required whenever performing register accesses 14919040b41SDan Cross * to the DF or SMN. 15019040b41SDan Cross * 15119040b41SDan Cross * To summarize, one must hold the lock in the following circumstances: 15219040b41SDan Cross * 15319040b41SDan Cross * - Looking up DF structures 15419040b41SDan Cross * - Reading or writing to DF registers 15519040b41SDan Cross * - Reading or writing to SMN registers 15671815ce7SRobert Mustacchi * 15771815ce7SRobert Mustacchi * In general, it is preferred that the lock be held across an entire client 15871815ce7SRobert Mustacchi * operation if possible. The only time this becomes an issue are when we have 15919040b41SDan Cross * callbacks into our callers (ala amdzen_c_df_iter()) as they may recursively 16019040b41SDan Cross * call into us. 161047043c2SRobert Mustacchi */ 162047043c2SRobert Mustacchi 163047043c2SRobert Mustacchi #include <sys/modctl.h> 164047043c2SRobert Mustacchi #include <sys/conf.h> 165047043c2SRobert Mustacchi #include <sys/devops.h> 166047043c2SRobert Mustacchi #include <sys/ddi.h> 167047043c2SRobert Mustacchi #include <sys/sunddi.h> 168047043c2SRobert Mustacchi #include <sys/pci.h> 169047043c2SRobert Mustacchi #include <sys/sysmacros.h> 170047043c2SRobert Mustacchi #include <sys/sunndi.h> 171047043c2SRobert Mustacchi #include <sys/x86_archext.h> 172047043c2SRobert Mustacchi #include <sys/cpuvar.h> 173047043c2SRobert Mustacchi 17471815ce7SRobert Mustacchi #include <sys/amdzen/df.h> 17571815ce7SRobert Mustacchi #include "amdzen_client.h" 176047043c2SRobert Mustacchi #include "amdzen.h" 177047043c2SRobert Mustacchi 178047043c2SRobert Mustacchi amdzen_t *amdzen_data; 179047043c2SRobert Mustacchi 180047043c2SRobert Mustacchi /* 181047043c2SRobert Mustacchi * Array of northbridge IDs that we care about. 182047043c2SRobert Mustacchi */ 183047043c2SRobert Mustacchi static const uint16_t amdzen_nb_ids[] = { 184047043c2SRobert Mustacchi /* Family 17h Ryzen, Epyc Models 00h-0fh (Zen uarch) */ 185047043c2SRobert Mustacchi 0x1450, 186becd642cSRobert Mustacchi /* Family 17h Raven Ridge, Kestrel, Dali Models 10h-2fh (Zen uarch) */ 187047043c2SRobert Mustacchi 0x15d0, 188e9abe9d6SRobert Mustacchi /* Family 17h/19h Rome, Milan, Matisse, Vermeer Zen 2/Zen 3 uarch */ 189becd642cSRobert Mustacchi 0x1480, 190f8e9c7b3SRobert Mustacchi /* Family 17h/19h Renoir, Cezanne, Van Gogh Zen 2/3 uarch */ 191f8e9c7b3SRobert Mustacchi 0x1630, 192*e6f89c3aSRobert Mustacchi /* Family 19h Genoa and Bergamo */ 193f8e9c7b3SRobert Mustacchi 0x14a4, 194f8e9c7b3SRobert Mustacchi /* Family 17h Mendocino, Family 19h Rembrandt */ 195f8e9c7b3SRobert Mustacchi 0x14b5, 196f8e9c7b3SRobert Mustacchi /* Family 19h Raphael */ 197*e6f89c3aSRobert Mustacchi 0x14d8, 198*e6f89c3aSRobert Mustacchi /* Family 19h Phoenix */ 199*e6f89c3aSRobert Mustacchi 0x14e8 200047043c2SRobert Mustacchi }; 201047043c2SRobert Mustacchi 202047043c2SRobert Mustacchi typedef struct { 203047043c2SRobert Mustacchi char *acd_name; 204047043c2SRobert Mustacchi amdzen_child_t acd_addr; 205047043c2SRobert Mustacchi } amdzen_child_data_t; 206047043c2SRobert Mustacchi 207047043c2SRobert Mustacchi static const amdzen_child_data_t amdzen_children[] = { 208047043c2SRobert Mustacchi { "smntemp", AMDZEN_C_SMNTEMP }, 209549e0fd3SRobert Mustacchi { "usmn", AMDZEN_C_USMN }, 21071815ce7SRobert Mustacchi { "zen_udf", AMDZEN_C_ZEN_UDF }, 21171815ce7SRobert Mustacchi { "zen_umc", AMDZEN_C_ZEN_UMC } 212047043c2SRobert Mustacchi }; 213047043c2SRobert Mustacchi 2144adf43b0SKeith M Wesolowski static uint8_t 2154adf43b0SKeith M Wesolowski amdzen_stub_get8(amdzen_stub_t *stub, off_t reg) 2164adf43b0SKeith M Wesolowski { 2174adf43b0SKeith M Wesolowski return (pci_config_get8(stub->azns_cfgspace, reg)); 2184adf43b0SKeith M Wesolowski } 2194adf43b0SKeith M Wesolowski 2204adf43b0SKeith M Wesolowski static uint16_t 2214adf43b0SKeith M Wesolowski amdzen_stub_get16(amdzen_stub_t *stub, off_t reg) 2224adf43b0SKeith M Wesolowski { 2234adf43b0SKeith M Wesolowski return (pci_config_get16(stub->azns_cfgspace, reg)); 2244adf43b0SKeith M Wesolowski } 2254adf43b0SKeith M Wesolowski 226047043c2SRobert Mustacchi static uint32_t 227047043c2SRobert Mustacchi amdzen_stub_get32(amdzen_stub_t *stub, off_t reg) 228047043c2SRobert Mustacchi { 229047043c2SRobert Mustacchi return (pci_config_get32(stub->azns_cfgspace, reg)); 230047043c2SRobert Mustacchi } 231047043c2SRobert Mustacchi 232549e0fd3SRobert Mustacchi static uint64_t 233549e0fd3SRobert Mustacchi amdzen_stub_get64(amdzen_stub_t *stub, off_t reg) 234549e0fd3SRobert Mustacchi { 235549e0fd3SRobert Mustacchi return (pci_config_get64(stub->azns_cfgspace, reg)); 236549e0fd3SRobert Mustacchi } 237549e0fd3SRobert Mustacchi 238047043c2SRobert Mustacchi static void 2394adf43b0SKeith M Wesolowski amdzen_stub_put8(amdzen_stub_t *stub, off_t reg, uint8_t val) 2404adf43b0SKeith M Wesolowski { 2414adf43b0SKeith M Wesolowski pci_config_put8(stub->azns_cfgspace, reg, val); 2424adf43b0SKeith M Wesolowski } 2434adf43b0SKeith M Wesolowski 2444adf43b0SKeith M Wesolowski static void 2454adf43b0SKeith M Wesolowski amdzen_stub_put16(amdzen_stub_t *stub, off_t reg, uint16_t val) 2464adf43b0SKeith M Wesolowski { 2474adf43b0SKeith M Wesolowski pci_config_put16(stub->azns_cfgspace, reg, val); 2484adf43b0SKeith M Wesolowski } 2494adf43b0SKeith M Wesolowski 2504adf43b0SKeith M Wesolowski static void 251047043c2SRobert Mustacchi amdzen_stub_put32(amdzen_stub_t *stub, off_t reg, uint32_t val) 252047043c2SRobert Mustacchi { 253047043c2SRobert Mustacchi pci_config_put32(stub->azns_cfgspace, reg, val); 254047043c2SRobert Mustacchi } 255047043c2SRobert Mustacchi 25671815ce7SRobert Mustacchi static uint64_t 25771815ce7SRobert Mustacchi amdzen_df_read_regdef(amdzen_t *azn, amdzen_df_t *df, const df_reg_def_t def, 25871815ce7SRobert Mustacchi uint8_t inst, boolean_t do_64) 25971815ce7SRobert Mustacchi { 26071815ce7SRobert Mustacchi df_reg_def_t ficaa; 26171815ce7SRobert Mustacchi df_reg_def_t ficad; 26271815ce7SRobert Mustacchi uint32_t val = 0; 26371815ce7SRobert Mustacchi df_rev_t df_rev = azn->azn_dfs[0].adf_rev; 26471815ce7SRobert Mustacchi 26571815ce7SRobert Mustacchi VERIFY(MUTEX_HELD(&azn->azn_mutex)); 26671815ce7SRobert Mustacchi ASSERT3U(def.drd_gens & df_rev, ==, df_rev); 26771815ce7SRobert Mustacchi val = DF_FICAA_V2_SET_TARG_INST(val, 1); 26871815ce7SRobert Mustacchi val = DF_FICAA_V2_SET_FUNC(val, def.drd_func); 26971815ce7SRobert Mustacchi val = DF_FICAA_V2_SET_INST(val, inst); 27071815ce7SRobert Mustacchi val = DF_FICAA_V2_SET_64B(val, do_64 ? 1 : 0); 27171815ce7SRobert Mustacchi 27271815ce7SRobert Mustacchi switch (df_rev) { 27371815ce7SRobert Mustacchi case DF_REV_2: 27471815ce7SRobert Mustacchi case DF_REV_3: 27571815ce7SRobert Mustacchi case DF_REV_3P5: 27671815ce7SRobert Mustacchi ficaa = DF_FICAA_V2; 27771815ce7SRobert Mustacchi ficad = DF_FICAD_LO_V2; 27871815ce7SRobert Mustacchi /* 27971815ce7SRobert Mustacchi * Both here and in the DFv4 case, the register ignores the 28071815ce7SRobert Mustacchi * lower 2 bits. That is we can only address and encode things 28171815ce7SRobert Mustacchi * in units of 4 bytes. 28271815ce7SRobert Mustacchi */ 28371815ce7SRobert Mustacchi val = DF_FICAA_V2_SET_REG(val, def.drd_reg >> 2); 28471815ce7SRobert Mustacchi break; 28571815ce7SRobert Mustacchi case DF_REV_4: 28671815ce7SRobert Mustacchi ficaa = DF_FICAA_V4; 28771815ce7SRobert Mustacchi ficad = DF_FICAD_LO_V4; 28871815ce7SRobert Mustacchi val = DF_FICAA_V4_SET_REG(val, def.drd_reg >> 2); 28971815ce7SRobert Mustacchi break; 29071815ce7SRobert Mustacchi default: 29171815ce7SRobert Mustacchi panic("encountered unexpected DF rev: %u", df_rev); 29271815ce7SRobert Mustacchi } 29371815ce7SRobert Mustacchi 29471815ce7SRobert Mustacchi amdzen_stub_put32(df->adf_funcs[ficaa.drd_func], ficaa.drd_reg, val); 29571815ce7SRobert Mustacchi if (do_64) { 29671815ce7SRobert Mustacchi return (amdzen_stub_get64(df->adf_funcs[ficad.drd_func], 29771815ce7SRobert Mustacchi ficad.drd_reg)); 29871815ce7SRobert Mustacchi } else { 29971815ce7SRobert Mustacchi return (amdzen_stub_get32(df->adf_funcs[ficad.drd_func], 30071815ce7SRobert Mustacchi ficad.drd_reg)); 30171815ce7SRobert Mustacchi } 30271815ce7SRobert Mustacchi } 30371815ce7SRobert Mustacchi 304047043c2SRobert Mustacchi /* 305047043c2SRobert Mustacchi * Perform a targeted 32-bit indirect read to a specific instance and function. 306047043c2SRobert Mustacchi */ 307047043c2SRobert Mustacchi static uint32_t 30871815ce7SRobert Mustacchi amdzen_df_read32(amdzen_t *azn, amdzen_df_t *df, uint8_t inst, 30971815ce7SRobert Mustacchi const df_reg_def_t def) 310047043c2SRobert Mustacchi { 31171815ce7SRobert Mustacchi return (amdzen_df_read_regdef(azn, df, def, inst, B_FALSE)); 312047043c2SRobert Mustacchi } 313047043c2SRobert Mustacchi 314549e0fd3SRobert Mustacchi /* 31571815ce7SRobert Mustacchi * For a broadcast read, just go to the underlying PCI function and perform a 31671815ce7SRobert Mustacchi * read. At this point in time, we don't believe we need to use the FICAA/FICAD 31771815ce7SRobert Mustacchi * to access it (though it does have a broadcast mode). 318549e0fd3SRobert Mustacchi */ 31971815ce7SRobert Mustacchi static uint32_t 32071815ce7SRobert Mustacchi amdzen_df_read32_bcast(amdzen_t *azn, amdzen_df_t *df, const df_reg_def_t def) 321549e0fd3SRobert Mustacchi { 322549e0fd3SRobert Mustacchi VERIFY(MUTEX_HELD(&azn->azn_mutex)); 32371815ce7SRobert Mustacchi return (amdzen_stub_get32(df->adf_funcs[def.drd_func], def.drd_reg)); 324549e0fd3SRobert Mustacchi } 325549e0fd3SRobert Mustacchi 326047043c2SRobert Mustacchi static uint32_t 3274adf43b0SKeith M Wesolowski amdzen_smn_read(amdzen_t *azn, amdzen_df_t *df, const smn_reg_t reg) 328047043c2SRobert Mustacchi { 3294adf43b0SKeith M Wesolowski const uint32_t base_addr = SMN_REG_ADDR_BASE(reg); 3304adf43b0SKeith M Wesolowski const uint32_t addr_off = SMN_REG_ADDR_OFF(reg); 3314adf43b0SKeith M Wesolowski 3324adf43b0SKeith M Wesolowski VERIFY(SMN_REG_IS_NATURALLY_ALIGNED(reg)); 333047043c2SRobert Mustacchi VERIFY(MUTEX_HELD(&azn->azn_mutex)); 3344adf43b0SKeith M Wesolowski amdzen_stub_put32(df->adf_nb, AMDZEN_NB_SMN_ADDR, base_addr); 3354adf43b0SKeith M Wesolowski 3364adf43b0SKeith M Wesolowski switch (SMN_REG_SIZE(reg)) { 3374adf43b0SKeith M Wesolowski case 1: 3384adf43b0SKeith M Wesolowski return ((uint32_t)amdzen_stub_get8(df->adf_nb, 3394adf43b0SKeith M Wesolowski AMDZEN_NB_SMN_DATA + addr_off)); 3404adf43b0SKeith M Wesolowski case 2: 3414adf43b0SKeith M Wesolowski return ((uint32_t)amdzen_stub_get16(df->adf_nb, 3424adf43b0SKeith M Wesolowski AMDZEN_NB_SMN_DATA + addr_off)); 3434adf43b0SKeith M Wesolowski case 4: 344047043c2SRobert Mustacchi return (amdzen_stub_get32(df->adf_nb, AMDZEN_NB_SMN_DATA)); 3454adf43b0SKeith M Wesolowski default: 3464adf43b0SKeith M Wesolowski panic("unreachable invalid SMN register size %u", 3474adf43b0SKeith M Wesolowski SMN_REG_SIZE(reg)); 3484adf43b0SKeith M Wesolowski } 349047043c2SRobert Mustacchi } 350047043c2SRobert Mustacchi 351f198607dSRobert Mustacchi static void 3524adf43b0SKeith M Wesolowski amdzen_smn_write(amdzen_t *azn, amdzen_df_t *df, const smn_reg_t reg, 353ba215efeSKeith M Wesolowski const uint32_t val) 354f198607dSRobert Mustacchi { 3554adf43b0SKeith M Wesolowski const uint32_t base_addr = SMN_REG_ADDR_BASE(reg); 3564adf43b0SKeith M Wesolowski const uint32_t addr_off = SMN_REG_ADDR_OFF(reg); 3574adf43b0SKeith M Wesolowski 3584adf43b0SKeith M Wesolowski VERIFY(SMN_REG_IS_NATURALLY_ALIGNED(reg)); 3594adf43b0SKeith M Wesolowski VERIFY(SMN_REG_VALUE_FITS(reg, val)); 360f198607dSRobert Mustacchi VERIFY(MUTEX_HELD(&azn->azn_mutex)); 3614adf43b0SKeith M Wesolowski amdzen_stub_put32(df->adf_nb, AMDZEN_NB_SMN_ADDR, base_addr); 3624adf43b0SKeith M Wesolowski 3634adf43b0SKeith M Wesolowski switch (SMN_REG_SIZE(reg)) { 3644adf43b0SKeith M Wesolowski case 1: 3654adf43b0SKeith M Wesolowski amdzen_stub_put8(df->adf_nb, AMDZEN_NB_SMN_DATA + addr_off, 3664adf43b0SKeith M Wesolowski (uint8_t)val); 3674adf43b0SKeith M Wesolowski break; 3684adf43b0SKeith M Wesolowski case 2: 3694adf43b0SKeith M Wesolowski amdzen_stub_put16(df->adf_nb, AMDZEN_NB_SMN_DATA + addr_off, 3704adf43b0SKeith M Wesolowski (uint16_t)val); 3714adf43b0SKeith M Wesolowski break; 3724adf43b0SKeith M Wesolowski case 4: 373f198607dSRobert Mustacchi amdzen_stub_put32(df->adf_nb, AMDZEN_NB_SMN_DATA, val); 3744adf43b0SKeith M Wesolowski break; 3754adf43b0SKeith M Wesolowski default: 3764adf43b0SKeith M Wesolowski panic("unreachable invalid SMN register size %u", 3774adf43b0SKeith M Wesolowski SMN_REG_SIZE(reg)); 3784adf43b0SKeith M Wesolowski } 379f198607dSRobert Mustacchi } 380f198607dSRobert Mustacchi 381047043c2SRobert Mustacchi static amdzen_df_t * 382047043c2SRobert Mustacchi amdzen_df_find(amdzen_t *azn, uint_t dfno) 383047043c2SRobert Mustacchi { 384047043c2SRobert Mustacchi uint_t i; 385047043c2SRobert Mustacchi 386047043c2SRobert Mustacchi ASSERT(MUTEX_HELD(&azn->azn_mutex)); 387047043c2SRobert Mustacchi if (dfno >= azn->azn_ndfs) { 388047043c2SRobert Mustacchi return (NULL); 389047043c2SRobert Mustacchi } 390047043c2SRobert Mustacchi 391047043c2SRobert Mustacchi for (i = 0; i < azn->azn_ndfs; i++) { 392047043c2SRobert Mustacchi amdzen_df_t *df = &azn->azn_dfs[i]; 393047043c2SRobert Mustacchi if ((df->adf_flags & AMDZEN_DF_F_VALID) == 0) { 394047043c2SRobert Mustacchi continue; 395047043c2SRobert Mustacchi } 396047043c2SRobert Mustacchi 397047043c2SRobert Mustacchi if (dfno == 0) { 398047043c2SRobert Mustacchi return (df); 399047043c2SRobert Mustacchi } 400047043c2SRobert Mustacchi dfno--; 401047043c2SRobert Mustacchi } 402047043c2SRobert Mustacchi 403047043c2SRobert Mustacchi return (NULL); 404047043c2SRobert Mustacchi } 405047043c2SRobert Mustacchi 406047043c2SRobert Mustacchi /* 407047043c2SRobert Mustacchi * Client functions that are used by nexus children. 408047043c2SRobert Mustacchi */ 409047043c2SRobert Mustacchi int 4104adf43b0SKeith M Wesolowski amdzen_c_smn_read(uint_t dfno, const smn_reg_t reg, uint32_t *valp) 411047043c2SRobert Mustacchi { 412047043c2SRobert Mustacchi amdzen_df_t *df; 413047043c2SRobert Mustacchi amdzen_t *azn = amdzen_data; 414047043c2SRobert Mustacchi 4154adf43b0SKeith M Wesolowski if (!SMN_REG_SIZE_IS_VALID(reg)) 4164adf43b0SKeith M Wesolowski return (EINVAL); 4174adf43b0SKeith M Wesolowski if (!SMN_REG_IS_NATURALLY_ALIGNED(reg)) 4184adf43b0SKeith M Wesolowski return (EINVAL); 4194adf43b0SKeith M Wesolowski 420047043c2SRobert Mustacchi mutex_enter(&azn->azn_mutex); 421047043c2SRobert Mustacchi df = amdzen_df_find(azn, dfno); 422047043c2SRobert Mustacchi if (df == NULL) { 423047043c2SRobert Mustacchi mutex_exit(&azn->azn_mutex); 424047043c2SRobert Mustacchi return (ENOENT); 425047043c2SRobert Mustacchi } 426047043c2SRobert Mustacchi 427047043c2SRobert Mustacchi if ((df->adf_flags & AMDZEN_DF_F_FOUND_NB) == 0) { 428047043c2SRobert Mustacchi mutex_exit(&azn->azn_mutex); 429047043c2SRobert Mustacchi return (ENXIO); 430047043c2SRobert Mustacchi } 431047043c2SRobert Mustacchi 4324adf43b0SKeith M Wesolowski *valp = amdzen_smn_read(azn, df, reg); 433047043c2SRobert Mustacchi mutex_exit(&azn->azn_mutex); 434047043c2SRobert Mustacchi return (0); 435047043c2SRobert Mustacchi } 436047043c2SRobert Mustacchi 437f198607dSRobert Mustacchi int 4384adf43b0SKeith M Wesolowski amdzen_c_smn_write(uint_t dfno, const smn_reg_t reg, const uint32_t val) 439f198607dSRobert Mustacchi { 440f198607dSRobert Mustacchi amdzen_df_t *df; 441f198607dSRobert Mustacchi amdzen_t *azn = amdzen_data; 442f198607dSRobert Mustacchi 4434adf43b0SKeith M Wesolowski if (!SMN_REG_SIZE_IS_VALID(reg)) 4444adf43b0SKeith M Wesolowski return (EINVAL); 4454adf43b0SKeith M Wesolowski if (!SMN_REG_IS_NATURALLY_ALIGNED(reg)) 4464adf43b0SKeith M Wesolowski return (EINVAL); 4474adf43b0SKeith M Wesolowski if (!SMN_REG_VALUE_FITS(reg, val)) 4484adf43b0SKeith M Wesolowski return (EOVERFLOW); 4494adf43b0SKeith M Wesolowski 450f198607dSRobert Mustacchi mutex_enter(&azn->azn_mutex); 451f198607dSRobert Mustacchi df = amdzen_df_find(azn, dfno); 452f198607dSRobert Mustacchi if (df == NULL) { 453f198607dSRobert Mustacchi mutex_exit(&azn->azn_mutex); 454f198607dSRobert Mustacchi return (ENOENT); 455f198607dSRobert Mustacchi } 456f198607dSRobert Mustacchi 457f198607dSRobert Mustacchi if ((df->adf_flags & AMDZEN_DF_F_FOUND_NB) == 0) { 458f198607dSRobert Mustacchi mutex_exit(&azn->azn_mutex); 459f198607dSRobert Mustacchi return (ENXIO); 460f198607dSRobert Mustacchi } 461f198607dSRobert Mustacchi 4624adf43b0SKeith M Wesolowski amdzen_smn_write(azn, df, reg, val); 463f198607dSRobert Mustacchi mutex_exit(&azn->azn_mutex); 464f198607dSRobert Mustacchi return (0); 465f198607dSRobert Mustacchi } 466f198607dSRobert Mustacchi 467047043c2SRobert Mustacchi uint_t 468047043c2SRobert Mustacchi amdzen_c_df_count(void) 469047043c2SRobert Mustacchi { 470047043c2SRobert Mustacchi uint_t ret; 471047043c2SRobert Mustacchi amdzen_t *azn = amdzen_data; 472047043c2SRobert Mustacchi 473047043c2SRobert Mustacchi mutex_enter(&azn->azn_mutex); 474047043c2SRobert Mustacchi ret = azn->azn_ndfs; 475047043c2SRobert Mustacchi mutex_exit(&azn->azn_mutex); 476047043c2SRobert Mustacchi return (ret); 477047043c2SRobert Mustacchi } 478047043c2SRobert Mustacchi 47971815ce7SRobert Mustacchi df_rev_t 48071815ce7SRobert Mustacchi amdzen_c_df_rev(void) 48171815ce7SRobert Mustacchi { 48271815ce7SRobert Mustacchi amdzen_df_t *df; 48371815ce7SRobert Mustacchi amdzen_t *azn = amdzen_data; 48471815ce7SRobert Mustacchi df_rev_t rev; 48571815ce7SRobert Mustacchi 48671815ce7SRobert Mustacchi /* 48771815ce7SRobert Mustacchi * Always use the first DF instance to determine what we're using. Our 48871815ce7SRobert Mustacchi * current assumption, which seems to generally be true, is that the 48971815ce7SRobert Mustacchi * given DF revisions are the same in a given system when the DFs are 49071815ce7SRobert Mustacchi * directly connected. 49171815ce7SRobert Mustacchi */ 49271815ce7SRobert Mustacchi mutex_enter(&azn->azn_mutex); 49371815ce7SRobert Mustacchi df = amdzen_df_find(azn, 0); 49471815ce7SRobert Mustacchi if (df == NULL) { 49571815ce7SRobert Mustacchi rev = DF_REV_UNKNOWN; 49671815ce7SRobert Mustacchi } else { 49771815ce7SRobert Mustacchi rev = df->adf_rev; 49871815ce7SRobert Mustacchi } 49971815ce7SRobert Mustacchi mutex_exit(&azn->azn_mutex); 50071815ce7SRobert Mustacchi 50171815ce7SRobert Mustacchi return (rev); 50271815ce7SRobert Mustacchi } 50371815ce7SRobert Mustacchi 504549e0fd3SRobert Mustacchi int 50571815ce7SRobert Mustacchi amdzen_c_df_read32(uint_t dfno, uint8_t inst, const df_reg_def_t def, 50671815ce7SRobert Mustacchi uint32_t *valp) 507549e0fd3SRobert Mustacchi { 508549e0fd3SRobert Mustacchi amdzen_df_t *df; 509549e0fd3SRobert Mustacchi amdzen_t *azn = amdzen_data; 510549e0fd3SRobert Mustacchi 511549e0fd3SRobert Mustacchi mutex_enter(&azn->azn_mutex); 512549e0fd3SRobert Mustacchi df = amdzen_df_find(azn, dfno); 513549e0fd3SRobert Mustacchi if (df == NULL) { 514549e0fd3SRobert Mustacchi mutex_exit(&azn->azn_mutex); 515549e0fd3SRobert Mustacchi return (ENOENT); 516549e0fd3SRobert Mustacchi } 517549e0fd3SRobert Mustacchi 51871815ce7SRobert Mustacchi *valp = amdzen_df_read_regdef(azn, df, def, inst, B_FALSE); 519549e0fd3SRobert Mustacchi mutex_exit(&azn->azn_mutex); 520549e0fd3SRobert Mustacchi 521549e0fd3SRobert Mustacchi return (0); 522549e0fd3SRobert Mustacchi } 523549e0fd3SRobert Mustacchi 524549e0fd3SRobert Mustacchi int 52571815ce7SRobert Mustacchi amdzen_c_df_read64(uint_t dfno, uint8_t inst, const df_reg_def_t def, 52671815ce7SRobert Mustacchi uint64_t *valp) 527549e0fd3SRobert Mustacchi { 528549e0fd3SRobert Mustacchi amdzen_df_t *df; 529549e0fd3SRobert Mustacchi amdzen_t *azn = amdzen_data; 530549e0fd3SRobert Mustacchi 531549e0fd3SRobert Mustacchi mutex_enter(&azn->azn_mutex); 532549e0fd3SRobert Mustacchi df = amdzen_df_find(azn, dfno); 533549e0fd3SRobert Mustacchi if (df == NULL) { 534549e0fd3SRobert Mustacchi mutex_exit(&azn->azn_mutex); 535549e0fd3SRobert Mustacchi return (ENOENT); 536549e0fd3SRobert Mustacchi } 537549e0fd3SRobert Mustacchi 53871815ce7SRobert Mustacchi *valp = amdzen_df_read_regdef(azn, df, def, inst, B_TRUE); 539549e0fd3SRobert Mustacchi mutex_exit(&azn->azn_mutex); 540549e0fd3SRobert Mustacchi 541549e0fd3SRobert Mustacchi return (0); 542549e0fd3SRobert Mustacchi } 543047043c2SRobert Mustacchi 54471815ce7SRobert Mustacchi int 54571815ce7SRobert Mustacchi amdzen_c_df_iter(uint_t dfno, zen_df_type_t type, amdzen_c_iter_f func, 54671815ce7SRobert Mustacchi void *arg) 54771815ce7SRobert Mustacchi { 54871815ce7SRobert Mustacchi amdzen_df_t *df; 54971815ce7SRobert Mustacchi amdzen_t *azn = amdzen_data; 55071815ce7SRobert Mustacchi df_type_t df_type; 55171815ce7SRobert Mustacchi uint8_t df_subtype; 55271815ce7SRobert Mustacchi 55371815ce7SRobert Mustacchi /* 55471815ce7SRobert Mustacchi * Unlike other calls here, we hold our lock only to find the DF here. 55571815ce7SRobert Mustacchi * The main reason for this is the nature of the callback function. 55671815ce7SRobert Mustacchi * Folks are iterating over instances so they can call back into us. If 55771815ce7SRobert Mustacchi * you look at the locking statement, the thing that is most volatile 55871815ce7SRobert Mustacchi * right here and what we need to protect is the DF itself and 55971815ce7SRobert Mustacchi * subsequent register accesses to it. The actual data about which 56071815ce7SRobert Mustacchi * entities exist is static and so once we have found a DF we should 56171815ce7SRobert Mustacchi * hopefully be in good shape as they only come, but don't go. 56271815ce7SRobert Mustacchi */ 56371815ce7SRobert Mustacchi mutex_enter(&azn->azn_mutex); 56471815ce7SRobert Mustacchi df = amdzen_df_find(azn, dfno); 56571815ce7SRobert Mustacchi if (df == NULL) { 56671815ce7SRobert Mustacchi mutex_exit(&azn->azn_mutex); 56771815ce7SRobert Mustacchi return (ENOENT); 56871815ce7SRobert Mustacchi } 56971815ce7SRobert Mustacchi mutex_exit(&azn->azn_mutex); 57071815ce7SRobert Mustacchi 57171815ce7SRobert Mustacchi switch (type) { 57271815ce7SRobert Mustacchi case ZEN_DF_TYPE_CS_UMC: 57371815ce7SRobert Mustacchi df_type = DF_TYPE_CS; 57471815ce7SRobert Mustacchi /* 57571815ce7SRobert Mustacchi * In the original Zeppelin DFv2 die there was no subtype field 57671815ce7SRobert Mustacchi * used for the CS. The UMC is the only type and has a subtype 57771815ce7SRobert Mustacchi * of zero. 57871815ce7SRobert Mustacchi */ 57971815ce7SRobert Mustacchi if (df->adf_rev != DF_REV_2) { 58071815ce7SRobert Mustacchi df_subtype = DF_CS_SUBTYPE_UMC; 58171815ce7SRobert Mustacchi } else { 58271815ce7SRobert Mustacchi df_subtype = 0; 58371815ce7SRobert Mustacchi } 58471815ce7SRobert Mustacchi break; 58571815ce7SRobert Mustacchi case ZEN_DF_TYPE_CCM_CPU: 58671815ce7SRobert Mustacchi /* 587f8e9c7b3SRobert Mustacchi * While the wording of the PPR is a little weird, the CCM still 588f8e9c7b3SRobert Mustacchi * has subtype 0 in DFv4 systems; however, what's said to be for 589f8e9c7b3SRobert Mustacchi * the CPU appears to apply to the ACM. 59071815ce7SRobert Mustacchi */ 591f8e9c7b3SRobert Mustacchi df_type = DF_TYPE_CCM; 59271815ce7SRobert Mustacchi df_subtype = 0; 59371815ce7SRobert Mustacchi break; 59471815ce7SRobert Mustacchi default: 59571815ce7SRobert Mustacchi return (EINVAL); 59671815ce7SRobert Mustacchi } 59771815ce7SRobert Mustacchi 59871815ce7SRobert Mustacchi for (uint_t i = 0; i < df->adf_nents; i++) { 59971815ce7SRobert Mustacchi amdzen_df_ent_t *ent = &df->adf_ents[i]; 60071815ce7SRobert Mustacchi 60171815ce7SRobert Mustacchi /* 60271815ce7SRobert Mustacchi * Some DF components are not considered enabled and therefore 60371815ce7SRobert Mustacchi * will end up having bogus values in their ID fields. If we do 60471815ce7SRobert Mustacchi * not have an enable flag set, we must skip this node. 60571815ce7SRobert Mustacchi */ 60671815ce7SRobert Mustacchi if ((ent->adfe_flags & AMDZEN_DFE_F_ENABLED) == 0) 60771815ce7SRobert Mustacchi continue; 60871815ce7SRobert Mustacchi 60971815ce7SRobert Mustacchi if (ent->adfe_type == df_type && 61071815ce7SRobert Mustacchi ent->adfe_subtype == df_subtype) { 61171815ce7SRobert Mustacchi int ret = func(dfno, ent->adfe_fabric_id, 61271815ce7SRobert Mustacchi ent->adfe_inst_id, arg); 61371815ce7SRobert Mustacchi if (ret != 0) { 61471815ce7SRobert Mustacchi return (ret); 61571815ce7SRobert Mustacchi } 61671815ce7SRobert Mustacchi } 61771815ce7SRobert Mustacchi } 61871815ce7SRobert Mustacchi 61971815ce7SRobert Mustacchi return (0); 62071815ce7SRobert Mustacchi } 62171815ce7SRobert Mustacchi 62271815ce7SRobert Mustacchi int 62371815ce7SRobert Mustacchi amdzen_c_df_fabric_decomp(df_fabric_decomp_t *decomp) 62471815ce7SRobert Mustacchi { 62571815ce7SRobert Mustacchi const amdzen_df_t *df; 62671815ce7SRobert Mustacchi amdzen_t *azn = amdzen_data; 62771815ce7SRobert Mustacchi 62871815ce7SRobert Mustacchi mutex_enter(&azn->azn_mutex); 62971815ce7SRobert Mustacchi df = amdzen_df_find(azn, 0); 63071815ce7SRobert Mustacchi if (df == NULL) { 63171815ce7SRobert Mustacchi mutex_exit(&azn->azn_mutex); 63271815ce7SRobert Mustacchi return (ENOENT); 63371815ce7SRobert Mustacchi } 63471815ce7SRobert Mustacchi 63571815ce7SRobert Mustacchi *decomp = df->adf_decomp; 63671815ce7SRobert Mustacchi mutex_exit(&azn->azn_mutex); 63771815ce7SRobert Mustacchi return (0); 63871815ce7SRobert Mustacchi } 63971815ce7SRobert Mustacchi 640047043c2SRobert Mustacchi static boolean_t 641047043c2SRobert Mustacchi amdzen_create_child(amdzen_t *azn, const amdzen_child_data_t *acd) 642047043c2SRobert Mustacchi { 643047043c2SRobert Mustacchi int ret; 644047043c2SRobert Mustacchi dev_info_t *child; 645047043c2SRobert Mustacchi 646047043c2SRobert Mustacchi if (ndi_devi_alloc(azn->azn_dip, acd->acd_name, 647047043c2SRobert Mustacchi (pnode_t)DEVI_SID_NODEID, &child) != NDI_SUCCESS) { 648549e0fd3SRobert Mustacchi dev_err(azn->azn_dip, CE_WARN, "!failed to allocate child " 649047043c2SRobert Mustacchi "dip for %s", acd->acd_name); 650047043c2SRobert Mustacchi return (B_FALSE); 651047043c2SRobert Mustacchi } 652047043c2SRobert Mustacchi 653047043c2SRobert Mustacchi ddi_set_parent_data(child, (void *)acd); 654047043c2SRobert Mustacchi if ((ret = ndi_devi_online(child, 0)) != NDI_SUCCESS) { 655549e0fd3SRobert Mustacchi dev_err(azn->azn_dip, CE_WARN, "!failed to online child " 656047043c2SRobert Mustacchi "dip %s: %d", acd->acd_name, ret); 657047043c2SRobert Mustacchi return (B_FALSE); 658047043c2SRobert Mustacchi } 659047043c2SRobert Mustacchi 660047043c2SRobert Mustacchi return (B_TRUE); 661047043c2SRobert Mustacchi } 662047043c2SRobert Mustacchi 663047043c2SRobert Mustacchi static boolean_t 664047043c2SRobert Mustacchi amdzen_map_dfs(amdzen_t *azn) 665047043c2SRobert Mustacchi { 666047043c2SRobert Mustacchi amdzen_stub_t *stub; 667047043c2SRobert Mustacchi 668047043c2SRobert Mustacchi ASSERT(MUTEX_HELD(&azn->azn_mutex)); 669047043c2SRobert Mustacchi 670047043c2SRobert Mustacchi for (stub = list_head(&azn->azn_df_stubs); stub != NULL; 671047043c2SRobert Mustacchi stub = list_next(&azn->azn_df_stubs, stub)) { 672047043c2SRobert Mustacchi amdzen_df_t *df; 673047043c2SRobert Mustacchi uint_t dfno; 674047043c2SRobert Mustacchi 675047043c2SRobert Mustacchi dfno = stub->azns_dev - AMDZEN_DF_FIRST_DEVICE; 676047043c2SRobert Mustacchi if (dfno > AMDZEN_MAX_DFS) { 677047043c2SRobert Mustacchi dev_err(stub->azns_dip, CE_WARN, "encountered df " 678047043c2SRobert Mustacchi "device with illegal DF PCI b/d/f: 0x%x/%x/%x", 679047043c2SRobert Mustacchi stub->azns_bus, stub->azns_dev, stub->azns_func); 680047043c2SRobert Mustacchi goto err; 681047043c2SRobert Mustacchi } 682047043c2SRobert Mustacchi 683047043c2SRobert Mustacchi df = &azn->azn_dfs[dfno]; 684047043c2SRobert Mustacchi 685047043c2SRobert Mustacchi if (stub->azns_func >= AMDZEN_MAX_DF_FUNCS) { 686047043c2SRobert Mustacchi dev_err(stub->azns_dip, CE_WARN, "encountered df " 687047043c2SRobert Mustacchi "device with illegal DF PCI b/d/f: 0x%x/%x/%x", 688047043c2SRobert Mustacchi stub->azns_bus, stub->azns_dev, stub->azns_func); 689047043c2SRobert Mustacchi goto err; 690047043c2SRobert Mustacchi } 691047043c2SRobert Mustacchi 692047043c2SRobert Mustacchi if (df->adf_funcs[stub->azns_func] != NULL) { 693047043c2SRobert Mustacchi dev_err(stub->azns_dip, CE_WARN, "encountered " 694047043c2SRobert Mustacchi "duplicate df device with DF PCI b/d/f: 0x%x/%x/%x", 695047043c2SRobert Mustacchi stub->azns_bus, stub->azns_dev, stub->azns_func); 696047043c2SRobert Mustacchi goto err; 697047043c2SRobert Mustacchi } 698047043c2SRobert Mustacchi df->adf_funcs[stub->azns_func] = stub; 699047043c2SRobert Mustacchi } 700047043c2SRobert Mustacchi 701047043c2SRobert Mustacchi return (B_TRUE); 702047043c2SRobert Mustacchi 703047043c2SRobert Mustacchi err: 704047043c2SRobert Mustacchi azn->azn_flags |= AMDZEN_F_DEVICE_ERROR; 705047043c2SRobert Mustacchi return (B_FALSE); 706047043c2SRobert Mustacchi } 707047043c2SRobert Mustacchi 708047043c2SRobert Mustacchi static boolean_t 709047043c2SRobert Mustacchi amdzen_check_dfs(amdzen_t *azn) 710047043c2SRobert Mustacchi { 711047043c2SRobert Mustacchi uint_t i; 712047043c2SRobert Mustacchi boolean_t ret = B_TRUE; 713047043c2SRobert Mustacchi 714047043c2SRobert Mustacchi for (i = 0; i < AMDZEN_MAX_DFS; i++) { 715047043c2SRobert Mustacchi amdzen_df_t *df = &azn->azn_dfs[i]; 716047043c2SRobert Mustacchi uint_t count = 0; 717047043c2SRobert Mustacchi 718047043c2SRobert Mustacchi /* 719047043c2SRobert Mustacchi * We require all platforms to have DFs functions 0-6. Not all 720047043c2SRobert Mustacchi * platforms have DF function 7. 721047043c2SRobert Mustacchi */ 722047043c2SRobert Mustacchi for (uint_t func = 0; func < AMDZEN_MAX_DF_FUNCS - 1; func++) { 723047043c2SRobert Mustacchi if (df->adf_funcs[func] != NULL) { 724047043c2SRobert Mustacchi count++; 725047043c2SRobert Mustacchi } 726047043c2SRobert Mustacchi } 727047043c2SRobert Mustacchi 728047043c2SRobert Mustacchi if (count == 0) 729047043c2SRobert Mustacchi continue; 730047043c2SRobert Mustacchi 731047043c2SRobert Mustacchi if (count != 7) { 732047043c2SRobert Mustacchi ret = B_FALSE; 733047043c2SRobert Mustacchi dev_err(azn->azn_dip, CE_WARN, "df %u devices " 734047043c2SRobert Mustacchi "incomplete", i); 735047043c2SRobert Mustacchi } else { 736047043c2SRobert Mustacchi df->adf_flags |= AMDZEN_DF_F_VALID; 737047043c2SRobert Mustacchi azn->azn_ndfs++; 738047043c2SRobert Mustacchi } 739047043c2SRobert Mustacchi } 740047043c2SRobert Mustacchi 741047043c2SRobert Mustacchi return (ret); 742047043c2SRobert Mustacchi } 743047043c2SRobert Mustacchi 744047043c2SRobert Mustacchi static const uint8_t amdzen_df_rome_ids[0x2b] = { 745047043c2SRobert Mustacchi 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20, 21, 22, 23, 746047043c2SRobert Mustacchi 24, 25, 26, 27, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 747047043c2SRobert Mustacchi 44, 45, 46, 47, 48 748047043c2SRobert Mustacchi }; 749047043c2SRobert Mustacchi 750047043c2SRobert Mustacchi /* 751e9abe9d6SRobert Mustacchi * Check the first df entry to see if it belongs to Rome or Milan. If so, then 752e9abe9d6SRobert Mustacchi * it uses the disjoint ID space. 753e9abe9d6SRobert Mustacchi */ 754e9abe9d6SRobert Mustacchi static boolean_t 755e9abe9d6SRobert Mustacchi amdzen_is_rome_style(uint_t id) 756e9abe9d6SRobert Mustacchi { 757e9abe9d6SRobert Mustacchi return (id == 0x1490 || id == 0x1650); 758e9abe9d6SRobert Mustacchi } 759e9abe9d6SRobert Mustacchi 760e9abe9d6SRobert Mustacchi /* 76171815ce7SRobert Mustacchi * To be able to do most other things we want to do, we must first determine 76271815ce7SRobert Mustacchi * what revision of the DF (data fabric) that we're using. 76371815ce7SRobert Mustacchi * 76471815ce7SRobert Mustacchi * Snapshot the df version. This was added explicitly in DFv4.0, around the Zen 76571815ce7SRobert Mustacchi * 4 timeframe and allows us to tell apart different version of the DF register 76671815ce7SRobert Mustacchi * set, most usefully when various subtypes were added. 76771815ce7SRobert Mustacchi * 76871815ce7SRobert Mustacchi * Older versions can theoretically be told apart based on usage of reserved 76971815ce7SRobert Mustacchi * registers. We walk these in the following order, starting with the newest rev 77071815ce7SRobert Mustacchi * and walking backwards to tell things apart: 77171815ce7SRobert Mustacchi * 77271815ce7SRobert Mustacchi * o v3.5 -> Check function 1, register 0x150. This was reserved prior 77371815ce7SRobert Mustacchi * to this point. This is actually DF_FIDMASK0_V3P5. We are supposed 77471815ce7SRobert Mustacchi * to check bits [7:0]. 77571815ce7SRobert Mustacchi * 77671815ce7SRobert Mustacchi * o v3.0 -> Check function 1, register 0x208. The low byte (7:0) was 77771815ce7SRobert Mustacchi * changed to indicate a component mask. This is non-zero 77871815ce7SRobert Mustacchi * in the 3.0 generation. This is actually DF_FIDMASK_V2. 77971815ce7SRobert Mustacchi * 78071815ce7SRobert Mustacchi * o v2.0 -> This is just the not that case. Presumably v1 wasn't part 78171815ce7SRobert Mustacchi * of the Zen generation. 78271815ce7SRobert Mustacchi * 78371815ce7SRobert Mustacchi * Because we don't know what version we are yet, we do not use the normal 78471815ce7SRobert Mustacchi * versioned register accesses which would check what DF version we are and 78571815ce7SRobert Mustacchi * would want to use the normal indirect register accesses (which also require 78671815ce7SRobert Mustacchi * us to know the version). We instead do direct broadcast reads. 78771815ce7SRobert Mustacchi */ 78871815ce7SRobert Mustacchi static void 78971815ce7SRobert Mustacchi amdzen_determine_df_vers(amdzen_t *azn, amdzen_df_t *df) 79071815ce7SRobert Mustacchi { 79171815ce7SRobert Mustacchi uint32_t val; 79271815ce7SRobert Mustacchi df_reg_def_t rd = DF_FBICNT; 79371815ce7SRobert Mustacchi 79471815ce7SRobert Mustacchi val = amdzen_stub_get32(df->adf_funcs[rd.drd_func], rd.drd_reg); 79571815ce7SRobert Mustacchi df->adf_major = DF_FBICNT_V4_GET_MAJOR(val); 79671815ce7SRobert Mustacchi df->adf_minor = DF_FBICNT_V4_GET_MINOR(val); 79771815ce7SRobert Mustacchi if (df->adf_major == 0 && df->adf_minor == 0) { 79871815ce7SRobert Mustacchi rd = DF_FIDMASK0_V3P5; 79971815ce7SRobert Mustacchi val = amdzen_stub_get32(df->adf_funcs[rd.drd_func], rd.drd_reg); 80071815ce7SRobert Mustacchi if (bitx32(val, 7, 0) != 0) { 80171815ce7SRobert Mustacchi df->adf_major = 3; 80271815ce7SRobert Mustacchi df->adf_minor = 5; 80371815ce7SRobert Mustacchi df->adf_rev = DF_REV_3P5; 80471815ce7SRobert Mustacchi } else { 80571815ce7SRobert Mustacchi rd = DF_FIDMASK_V2; 80671815ce7SRobert Mustacchi val = amdzen_stub_get32(df->adf_funcs[rd.drd_func], 80771815ce7SRobert Mustacchi rd.drd_reg); 80871815ce7SRobert Mustacchi if (bitx32(val, 7, 0) != 0) { 80971815ce7SRobert Mustacchi df->adf_major = 3; 81071815ce7SRobert Mustacchi df->adf_minor = 0; 81171815ce7SRobert Mustacchi df->adf_rev = DF_REV_3; 81271815ce7SRobert Mustacchi } else { 81371815ce7SRobert Mustacchi df->adf_major = 2; 81471815ce7SRobert Mustacchi df->adf_minor = 0; 81571815ce7SRobert Mustacchi df->adf_rev = DF_REV_2; 81671815ce7SRobert Mustacchi } 81771815ce7SRobert Mustacchi } 81871815ce7SRobert Mustacchi } else if (df->adf_major == 4 && df->adf_minor == 0) { 81971815ce7SRobert Mustacchi df->adf_rev = DF_REV_4; 82071815ce7SRobert Mustacchi } else { 82171815ce7SRobert Mustacchi df->adf_rev = DF_REV_UNKNOWN; 82271815ce7SRobert Mustacchi } 82371815ce7SRobert Mustacchi } 82471815ce7SRobert Mustacchi 82571815ce7SRobert Mustacchi /* 82671815ce7SRobert Mustacchi * All of the different versions of the DF have different ways of getting at and 82771815ce7SRobert Mustacchi * answering the question of how do I break a fabric ID into a corresponding 82871815ce7SRobert Mustacchi * socket, die, and component. Importantly the goal here is to obtain, cache, 82971815ce7SRobert Mustacchi * and normalize: 83071815ce7SRobert Mustacchi * 83171815ce7SRobert Mustacchi * o The DF System Configuration 83271815ce7SRobert Mustacchi * o The various Mask registers 83371815ce7SRobert Mustacchi * o The Node ID 83471815ce7SRobert Mustacchi */ 83571815ce7SRobert Mustacchi static void 83671815ce7SRobert Mustacchi amdzen_determine_fabric_decomp(amdzen_t *azn, amdzen_df_t *df) 83771815ce7SRobert Mustacchi { 83871815ce7SRobert Mustacchi uint32_t mask; 83971815ce7SRobert Mustacchi df_fabric_decomp_t *decomp = &df->adf_decomp; 84071815ce7SRobert Mustacchi 84171815ce7SRobert Mustacchi switch (df->adf_rev) { 84271815ce7SRobert Mustacchi case DF_REV_2: 84371815ce7SRobert Mustacchi df->adf_syscfg = amdzen_df_read32_bcast(azn, df, DF_SYSCFG_V2); 84471815ce7SRobert Mustacchi switch (DF_SYSCFG_V2_GET_MY_TYPE(df->adf_syscfg)) { 84571815ce7SRobert Mustacchi case DF_DIE_TYPE_CPU: 84671815ce7SRobert Mustacchi mask = amdzen_df_read32_bcast(azn, df, 84771815ce7SRobert Mustacchi DF_DIEMASK_CPU_V2); 84871815ce7SRobert Mustacchi break; 84971815ce7SRobert Mustacchi case DF_DIE_TYPE_APU: 85071815ce7SRobert Mustacchi mask = amdzen_df_read32_bcast(azn, df, 85171815ce7SRobert Mustacchi DF_DIEMASK_APU_V2); 85271815ce7SRobert Mustacchi break; 85371815ce7SRobert Mustacchi default: 85471815ce7SRobert Mustacchi panic("DF thinks we're not on a CPU!"); 85571815ce7SRobert Mustacchi } 85671815ce7SRobert Mustacchi df->adf_mask0 = mask; 85771815ce7SRobert Mustacchi 85871815ce7SRobert Mustacchi /* 85971815ce7SRobert Mustacchi * DFv2 is a bit different in how the fabric mask register is 86071815ce7SRobert Mustacchi * phrased. Logically a fabric ID is broken into something that 86171815ce7SRobert Mustacchi * uniquely identifies a "node" (a particular die on a socket) 86271815ce7SRobert Mustacchi * and something that identifies a "component", e.g. a memory 86371815ce7SRobert Mustacchi * controller. 86471815ce7SRobert Mustacchi * 86571815ce7SRobert Mustacchi * Starting with DFv3, these registers logically called out how 86671815ce7SRobert Mustacchi * to separate the fabric ID first into a node and a component. 86771815ce7SRobert Mustacchi * Then the node was then broken down into a socket and die. In 86871815ce7SRobert Mustacchi * DFv2, there is no separate mask and shift of a node. Instead 86971815ce7SRobert Mustacchi * the socket and die are absolute offsets into the fabric ID 87071815ce7SRobert Mustacchi * rather than relative offsets into the node ID. As such, when 87171815ce7SRobert Mustacchi * we encounter DFv2, we fake up a node mask and shift and make 87271815ce7SRobert Mustacchi * it look like DFv3+. 87371815ce7SRobert Mustacchi */ 87471815ce7SRobert Mustacchi decomp->dfd_node_mask = DF_DIEMASK_V2_GET_SOCK_MASK(mask) | 87571815ce7SRobert Mustacchi DF_DIEMASK_V2_GET_DIE_MASK(mask); 87671815ce7SRobert Mustacchi decomp->dfd_node_shift = DF_DIEMASK_V2_GET_DIE_SHIFT(mask); 87771815ce7SRobert Mustacchi decomp->dfd_comp_mask = DF_DIEMASK_V2_GET_COMP_MASK(mask); 87871815ce7SRobert Mustacchi decomp->dfd_comp_shift = 0; 87971815ce7SRobert Mustacchi 88071815ce7SRobert Mustacchi decomp->dfd_sock_mask = DF_DIEMASK_V2_GET_SOCK_MASK(mask) >> 88171815ce7SRobert Mustacchi decomp->dfd_node_shift; 88271815ce7SRobert Mustacchi decomp->dfd_die_mask = DF_DIEMASK_V2_GET_DIE_MASK(mask) >> 88371815ce7SRobert Mustacchi decomp->dfd_node_shift; 88471815ce7SRobert Mustacchi decomp->dfd_sock_shift = DF_DIEMASK_V2_GET_SOCK_SHIFT(mask) - 88571815ce7SRobert Mustacchi decomp->dfd_node_shift; 88671815ce7SRobert Mustacchi decomp->dfd_die_shift = DF_DIEMASK_V2_GET_DIE_SHIFT(mask) - 88771815ce7SRobert Mustacchi decomp->dfd_node_shift; 88871815ce7SRobert Mustacchi ASSERT3U(decomp->dfd_die_shift, ==, 0); 88971815ce7SRobert Mustacchi break; 89071815ce7SRobert Mustacchi case DF_REV_3: 89171815ce7SRobert Mustacchi df->adf_syscfg = amdzen_df_read32_bcast(azn, df, DF_SYSCFG_V3); 89271815ce7SRobert Mustacchi df->adf_mask0 = amdzen_df_read32_bcast(azn, df, 89371815ce7SRobert Mustacchi DF_FIDMASK0_V3); 89471815ce7SRobert Mustacchi df->adf_mask1 = amdzen_df_read32_bcast(azn, df, 89571815ce7SRobert Mustacchi DF_FIDMASK1_V3); 89671815ce7SRobert Mustacchi 89771815ce7SRobert Mustacchi decomp->dfd_sock_mask = 89871815ce7SRobert Mustacchi DF_FIDMASK1_V3_GET_SOCK_MASK(df->adf_mask1); 89971815ce7SRobert Mustacchi decomp->dfd_sock_shift = 90071815ce7SRobert Mustacchi DF_FIDMASK1_V3_GET_SOCK_SHIFT(df->adf_mask1); 90171815ce7SRobert Mustacchi decomp->dfd_die_mask = 90271815ce7SRobert Mustacchi DF_FIDMASK1_V3_GET_DIE_MASK(df->adf_mask1); 90371815ce7SRobert Mustacchi decomp->dfd_die_shift = 0; 90471815ce7SRobert Mustacchi decomp->dfd_node_mask = 90571815ce7SRobert Mustacchi DF_FIDMASK0_V3_GET_NODE_MASK(df->adf_mask0); 90671815ce7SRobert Mustacchi decomp->dfd_node_shift = 90771815ce7SRobert Mustacchi DF_FIDMASK1_V3_GET_NODE_SHIFT(df->adf_mask1); 90871815ce7SRobert Mustacchi decomp->dfd_comp_mask = 90971815ce7SRobert Mustacchi DF_FIDMASK0_V3_GET_COMP_MASK(df->adf_mask0); 91071815ce7SRobert Mustacchi decomp->dfd_comp_shift = 0; 91171815ce7SRobert Mustacchi break; 91271815ce7SRobert Mustacchi case DF_REV_3P5: 91371815ce7SRobert Mustacchi df->adf_syscfg = amdzen_df_read32_bcast(azn, df, 91471815ce7SRobert Mustacchi DF_SYSCFG_V3P5); 91571815ce7SRobert Mustacchi df->adf_mask0 = amdzen_df_read32_bcast(azn, df, 91671815ce7SRobert Mustacchi DF_FIDMASK0_V3P5); 91771815ce7SRobert Mustacchi df->adf_mask1 = amdzen_df_read32_bcast(azn, df, 91871815ce7SRobert Mustacchi DF_FIDMASK1_V3P5); 91971815ce7SRobert Mustacchi df->adf_mask2 = amdzen_df_read32_bcast(azn, df, 92071815ce7SRobert Mustacchi DF_FIDMASK2_V3P5); 92171815ce7SRobert Mustacchi 92271815ce7SRobert Mustacchi decomp->dfd_sock_mask = 92371815ce7SRobert Mustacchi DF_FIDMASK2_V3P5_GET_SOCK_MASK(df->adf_mask2); 92471815ce7SRobert Mustacchi decomp->dfd_sock_shift = 92571815ce7SRobert Mustacchi DF_FIDMASK1_V3P5_GET_SOCK_SHIFT(df->adf_mask1); 92671815ce7SRobert Mustacchi decomp->dfd_die_mask = 92771815ce7SRobert Mustacchi DF_FIDMASK2_V3P5_GET_DIE_MASK(df->adf_mask2); 92871815ce7SRobert Mustacchi decomp->dfd_die_shift = 0; 92971815ce7SRobert Mustacchi decomp->dfd_node_mask = 93071815ce7SRobert Mustacchi DF_FIDMASK0_V3P5_GET_NODE_MASK(df->adf_mask0); 93171815ce7SRobert Mustacchi decomp->dfd_node_shift = 93271815ce7SRobert Mustacchi DF_FIDMASK1_V3P5_GET_NODE_SHIFT(df->adf_mask1); 93371815ce7SRobert Mustacchi decomp->dfd_comp_mask = 93471815ce7SRobert Mustacchi DF_FIDMASK0_V3P5_GET_COMP_MASK(df->adf_mask0); 93571815ce7SRobert Mustacchi decomp->dfd_comp_shift = 0; 93671815ce7SRobert Mustacchi break; 93771815ce7SRobert Mustacchi case DF_REV_4: 93871815ce7SRobert Mustacchi df->adf_syscfg = amdzen_df_read32_bcast(azn, df, DF_SYSCFG_V4); 93971815ce7SRobert Mustacchi df->adf_mask0 = amdzen_df_read32_bcast(azn, df, 94071815ce7SRobert Mustacchi DF_FIDMASK0_V4); 94171815ce7SRobert Mustacchi df->adf_mask1 = amdzen_df_read32_bcast(azn, df, 94271815ce7SRobert Mustacchi DF_FIDMASK1_V4); 94371815ce7SRobert Mustacchi df->adf_mask2 = amdzen_df_read32_bcast(azn, df, 94471815ce7SRobert Mustacchi DF_FIDMASK2_V4); 94571815ce7SRobert Mustacchi 94671815ce7SRobert Mustacchi /* 94771815ce7SRobert Mustacchi * The DFv4 registers are at a different location in the DF; 94871815ce7SRobert Mustacchi * however, the actual layout of fields is the same as DFv3.5. 94971815ce7SRobert Mustacchi * This is why you see V3P5 below. 95071815ce7SRobert Mustacchi */ 95171815ce7SRobert Mustacchi decomp->dfd_sock_mask = 95271815ce7SRobert Mustacchi DF_FIDMASK2_V3P5_GET_SOCK_MASK(df->adf_mask2); 95371815ce7SRobert Mustacchi decomp->dfd_sock_shift = 95471815ce7SRobert Mustacchi DF_FIDMASK1_V3P5_GET_SOCK_SHIFT(df->adf_mask1); 95571815ce7SRobert Mustacchi decomp->dfd_die_mask = 95671815ce7SRobert Mustacchi DF_FIDMASK2_V3P5_GET_DIE_MASK(df->adf_mask2); 95771815ce7SRobert Mustacchi decomp->dfd_die_shift = 0; 95871815ce7SRobert Mustacchi decomp->dfd_node_mask = 95971815ce7SRobert Mustacchi DF_FIDMASK0_V3P5_GET_NODE_MASK(df->adf_mask0); 96071815ce7SRobert Mustacchi decomp->dfd_node_shift = 96171815ce7SRobert Mustacchi DF_FIDMASK1_V3P5_GET_NODE_SHIFT(df->adf_mask1); 96271815ce7SRobert Mustacchi decomp->dfd_comp_mask = 96371815ce7SRobert Mustacchi DF_FIDMASK0_V3P5_GET_COMP_MASK(df->adf_mask0); 96471815ce7SRobert Mustacchi decomp->dfd_comp_shift = 0; 96571815ce7SRobert Mustacchi break; 96671815ce7SRobert Mustacchi default: 96771815ce7SRobert Mustacchi panic("encountered suspicious, previously rejected DF " 96871815ce7SRobert Mustacchi "rev: 0x%x", df->adf_rev); 96971815ce7SRobert Mustacchi } 97071815ce7SRobert Mustacchi } 97171815ce7SRobert Mustacchi 97271815ce7SRobert Mustacchi /* 973047043c2SRobert Mustacchi * Initialize our knowledge about a given series of nodes on the data fabric. 974047043c2SRobert Mustacchi */ 975047043c2SRobert Mustacchi static void 976047043c2SRobert Mustacchi amdzen_setup_df(amdzen_t *azn, amdzen_df_t *df) 977047043c2SRobert Mustacchi { 978047043c2SRobert Mustacchi uint_t i; 979047043c2SRobert Mustacchi uint32_t val; 980047043c2SRobert Mustacchi 98171815ce7SRobert Mustacchi amdzen_determine_df_vers(azn, df); 98271815ce7SRobert Mustacchi 98371815ce7SRobert Mustacchi switch (df->adf_rev) { 98471815ce7SRobert Mustacchi case DF_REV_2: 98571815ce7SRobert Mustacchi case DF_REV_3: 98671815ce7SRobert Mustacchi case DF_REV_3P5: 98771815ce7SRobert Mustacchi val = amdzen_df_read32_bcast(azn, df, DF_CFG_ADDR_CTL_V2); 98871815ce7SRobert Mustacchi break; 98971815ce7SRobert Mustacchi case DF_REV_4: 99071815ce7SRobert Mustacchi val = amdzen_df_read32_bcast(azn, df, DF_CFG_ADDR_CTL_V4); 99171815ce7SRobert Mustacchi break; 99271815ce7SRobert Mustacchi default: 99371815ce7SRobert Mustacchi dev_err(azn->azn_dip, CE_WARN, "encountered unsupported DF " 99471815ce7SRobert Mustacchi "revision: 0x%x", df->adf_rev); 99571815ce7SRobert Mustacchi return; 99671815ce7SRobert Mustacchi } 99771815ce7SRobert Mustacchi df->adf_nb_busno = DF_CFG_ADDR_CTL_GET_BUS_NUM(val); 99871815ce7SRobert Mustacchi val = amdzen_df_read32_bcast(azn, df, DF_FBICNT); 99971815ce7SRobert Mustacchi df->adf_nents = DF_FBICNT_GET_COUNT(val); 1000047043c2SRobert Mustacchi if (df->adf_nents == 0) 1001047043c2SRobert Mustacchi return; 1002047043c2SRobert Mustacchi df->adf_ents = kmem_zalloc(sizeof (amdzen_df_ent_t) * df->adf_nents, 1003047043c2SRobert Mustacchi KM_SLEEP); 1004047043c2SRobert Mustacchi 1005047043c2SRobert Mustacchi for (i = 0; i < df->adf_nents; i++) { 1006047043c2SRobert Mustacchi amdzen_df_ent_t *dfe = &df->adf_ents[i]; 1007047043c2SRobert Mustacchi uint8_t inst = i; 1008047043c2SRobert Mustacchi 1009047043c2SRobert Mustacchi /* 1010047043c2SRobert Mustacchi * Unfortunately, Rome uses a discontinuous instance ID pattern 1011047043c2SRobert Mustacchi * while everything else we can find uses a contiguous instance 1012047043c2SRobert Mustacchi * ID pattern. This means that for Rome, we need to adjust the 1013047043c2SRobert Mustacchi * indexes that we iterate over, though the total number of 101471815ce7SRobert Mustacchi * entries is right. This was carried over into Milan, but not 101571815ce7SRobert Mustacchi * Genoa. 1016047043c2SRobert Mustacchi */ 1017e9abe9d6SRobert Mustacchi if (amdzen_is_rome_style(df->adf_funcs[0]->azns_did)) { 1018047043c2SRobert Mustacchi if (inst > ARRAY_SIZE(amdzen_df_rome_ids)) { 1019047043c2SRobert Mustacchi dev_err(azn->azn_dip, CE_WARN, "Rome family " 1020047043c2SRobert Mustacchi "processor reported more ids than the PPR, " 1021e9abe9d6SRobert Mustacchi "resetting %u to instance zero", inst); 1022047043c2SRobert Mustacchi inst = 0; 1023047043c2SRobert Mustacchi } else { 1024047043c2SRobert Mustacchi inst = amdzen_df_rome_ids[inst]; 1025047043c2SRobert Mustacchi } 1026047043c2SRobert Mustacchi } 1027047043c2SRobert Mustacchi 1028047043c2SRobert Mustacchi dfe->adfe_drvid = inst; 102971815ce7SRobert Mustacchi dfe->adfe_info0 = amdzen_df_read32(azn, df, inst, DF_FBIINFO0); 103071815ce7SRobert Mustacchi dfe->adfe_info1 = amdzen_df_read32(azn, df, inst, DF_FBIINFO1); 103171815ce7SRobert Mustacchi dfe->adfe_info2 = amdzen_df_read32(azn, df, inst, DF_FBIINFO2); 103271815ce7SRobert Mustacchi dfe->adfe_info3 = amdzen_df_read32(azn, df, inst, DF_FBIINFO3); 1033047043c2SRobert Mustacchi 103471815ce7SRobert Mustacchi dfe->adfe_type = DF_FBIINFO0_GET_TYPE(dfe->adfe_info0); 103571815ce7SRobert Mustacchi dfe->adfe_subtype = DF_FBIINFO0_GET_SUBTYPE(dfe->adfe_info0); 103671815ce7SRobert Mustacchi 103771815ce7SRobert Mustacchi /* 103871815ce7SRobert Mustacchi * The enabled flag was not present in Zen 1. Simulate it by 103971815ce7SRobert Mustacchi * checking for a non-zero register instead. 104071815ce7SRobert Mustacchi */ 104171815ce7SRobert Mustacchi if (DF_FBIINFO0_V3_GET_ENABLED(dfe->adfe_info0) || 104271815ce7SRobert Mustacchi (df->adf_rev == DF_REV_2 && dfe->adfe_info0 != 0)) { 1043047043c2SRobert Mustacchi dfe->adfe_flags |= AMDZEN_DFE_F_ENABLED; 1044047043c2SRobert Mustacchi } 104571815ce7SRobert Mustacchi if (DF_FBIINFO0_GET_HAS_MCA(dfe->adfe_info0)) { 1046047043c2SRobert Mustacchi dfe->adfe_flags |= AMDZEN_DFE_F_MCA; 1047047043c2SRobert Mustacchi } 104871815ce7SRobert Mustacchi dfe->adfe_inst_id = DF_FBIINFO3_GET_INSTID(dfe->adfe_info3); 104971815ce7SRobert Mustacchi switch (df->adf_rev) { 105071815ce7SRobert Mustacchi case DF_REV_2: 1051047043c2SRobert Mustacchi dfe->adfe_fabric_id = 105271815ce7SRobert Mustacchi DF_FBIINFO3_V2_GET_BLOCKID(dfe->adfe_info3); 105371815ce7SRobert Mustacchi break; 105471815ce7SRobert Mustacchi case DF_REV_3: 105571815ce7SRobert Mustacchi dfe->adfe_fabric_id = 105671815ce7SRobert Mustacchi DF_FBIINFO3_V3_GET_BLOCKID(dfe->adfe_info3); 105771815ce7SRobert Mustacchi break; 105871815ce7SRobert Mustacchi case DF_REV_3P5: 105971815ce7SRobert Mustacchi dfe->adfe_fabric_id = 106071815ce7SRobert Mustacchi DF_FBIINFO3_V3P5_GET_BLOCKID(dfe->adfe_info3); 106171815ce7SRobert Mustacchi break; 106271815ce7SRobert Mustacchi case DF_REV_4: 106371815ce7SRobert Mustacchi dfe->adfe_fabric_id = 106471815ce7SRobert Mustacchi DF_FBIINFO3_V4_GET_BLOCKID(dfe->adfe_info3); 106571815ce7SRobert Mustacchi break; 106671815ce7SRobert Mustacchi default: 106771815ce7SRobert Mustacchi panic("encountered suspicious, previously rejected DF " 106871815ce7SRobert Mustacchi "rev: 0x%x", df->adf_rev); 106971815ce7SRobert Mustacchi } 1070047043c2SRobert Mustacchi } 1071047043c2SRobert Mustacchi 107271815ce7SRobert Mustacchi amdzen_determine_fabric_decomp(azn, df); 1073047043c2SRobert Mustacchi } 1074047043c2SRobert Mustacchi 1075047043c2SRobert Mustacchi static void 1076047043c2SRobert Mustacchi amdzen_find_nb(amdzen_t *azn, amdzen_df_t *df) 1077047043c2SRobert Mustacchi { 1078047043c2SRobert Mustacchi amdzen_stub_t *stub; 1079047043c2SRobert Mustacchi 1080047043c2SRobert Mustacchi for (stub = list_head(&azn->azn_nb_stubs); stub != NULL; 1081047043c2SRobert Mustacchi stub = list_next(&azn->azn_nb_stubs, stub)) { 1082047043c2SRobert Mustacchi if (stub->azns_bus == df->adf_nb_busno) { 1083047043c2SRobert Mustacchi df->adf_flags |= AMDZEN_DF_F_FOUND_NB; 1084047043c2SRobert Mustacchi df->adf_nb = stub; 1085047043c2SRobert Mustacchi return; 1086047043c2SRobert Mustacchi } 1087047043c2SRobert Mustacchi } 1088047043c2SRobert Mustacchi } 1089047043c2SRobert Mustacchi 1090047043c2SRobert Mustacchi static void 1091047043c2SRobert Mustacchi amdzen_nexus_init(void *arg) 1092047043c2SRobert Mustacchi { 1093047043c2SRobert Mustacchi uint_t i; 1094047043c2SRobert Mustacchi amdzen_t *azn = arg; 1095047043c2SRobert Mustacchi 1096047043c2SRobert Mustacchi /* 1097047043c2SRobert Mustacchi * First go through all of the stubs and assign the DF entries. 1098047043c2SRobert Mustacchi */ 1099047043c2SRobert Mustacchi mutex_enter(&azn->azn_mutex); 1100047043c2SRobert Mustacchi if (!amdzen_map_dfs(azn) || !amdzen_check_dfs(azn)) { 1101047043c2SRobert Mustacchi azn->azn_flags |= AMDZEN_F_MAP_ERROR; 1102047043c2SRobert Mustacchi goto done; 1103047043c2SRobert Mustacchi } 1104047043c2SRobert Mustacchi 1105047043c2SRobert Mustacchi for (i = 0; i < AMDZEN_MAX_DFS; i++) { 1106047043c2SRobert Mustacchi amdzen_df_t *df = &azn->azn_dfs[i]; 1107047043c2SRobert Mustacchi 1108047043c2SRobert Mustacchi if ((df->adf_flags & AMDZEN_DF_F_VALID) == 0) 1109047043c2SRobert Mustacchi continue; 1110047043c2SRobert Mustacchi amdzen_setup_df(azn, df); 1111047043c2SRobert Mustacchi amdzen_find_nb(azn, df); 1112047043c2SRobert Mustacchi } 1113047043c2SRobert Mustacchi 1114047043c2SRobert Mustacchi /* 1115047043c2SRobert Mustacchi * Not all children may be installed. As such, we do not treat the 1116047043c2SRobert Mustacchi * failure of a child as fatal to the driver. 1117047043c2SRobert Mustacchi */ 1118047043c2SRobert Mustacchi mutex_exit(&azn->azn_mutex); 1119047043c2SRobert Mustacchi for (i = 0; i < ARRAY_SIZE(amdzen_children); i++) { 1120047043c2SRobert Mustacchi (void) amdzen_create_child(azn, &amdzen_children[i]); 1121047043c2SRobert Mustacchi } 1122047043c2SRobert Mustacchi mutex_enter(&azn->azn_mutex); 1123047043c2SRobert Mustacchi 1124047043c2SRobert Mustacchi done: 1125047043c2SRobert Mustacchi azn->azn_flags &= ~AMDZEN_F_ATTACH_DISPATCHED; 1126047043c2SRobert Mustacchi azn->azn_flags |= AMDZEN_F_ATTACH_COMPLETE; 1127047043c2SRobert Mustacchi azn->azn_taskqid = TASKQID_INVALID; 1128047043c2SRobert Mustacchi cv_broadcast(&azn->azn_cv); 1129047043c2SRobert Mustacchi mutex_exit(&azn->azn_mutex); 1130047043c2SRobert Mustacchi } 1131047043c2SRobert Mustacchi 1132047043c2SRobert Mustacchi static int 1133047043c2SRobert Mustacchi amdzen_stub_scan_cb(dev_info_t *dip, void *arg) 1134047043c2SRobert Mustacchi { 1135047043c2SRobert Mustacchi amdzen_t *azn = arg; 1136047043c2SRobert Mustacchi uint16_t vid, did; 1137047043c2SRobert Mustacchi int *regs; 1138047043c2SRobert Mustacchi uint_t nregs, i; 1139047043c2SRobert Mustacchi boolean_t match = B_FALSE; 1140047043c2SRobert Mustacchi 1141047043c2SRobert Mustacchi if (dip == ddi_root_node()) { 1142047043c2SRobert Mustacchi return (DDI_WALK_CONTINUE); 1143047043c2SRobert Mustacchi } 1144047043c2SRobert Mustacchi 1145047043c2SRobert Mustacchi /* 1146047043c2SRobert Mustacchi * If a node in question is not a pci node, then we have no interest in 1147047043c2SRobert Mustacchi * it as all the stubs that we care about are related to pci devices. 1148047043c2SRobert Mustacchi */ 1149047043c2SRobert Mustacchi if (strncmp("pci", ddi_get_name(dip), 3) != 0) { 1150047043c2SRobert Mustacchi return (DDI_WALK_PRUNECHILD); 1151047043c2SRobert Mustacchi } 1152047043c2SRobert Mustacchi 1153047043c2SRobert Mustacchi /* 1154047043c2SRobert Mustacchi * If we can't get a device or vendor ID and prove that this is an AMD 1155047043c2SRobert Mustacchi * part, then we don't care about it. 1156047043c2SRobert Mustacchi */ 1157047043c2SRobert Mustacchi vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1158047043c2SRobert Mustacchi "vendor-id", PCI_EINVAL16); 1159047043c2SRobert Mustacchi did = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1160047043c2SRobert Mustacchi "device-id", PCI_EINVAL16); 1161047043c2SRobert Mustacchi if (vid == PCI_EINVAL16 || did == PCI_EINVAL16) { 1162047043c2SRobert Mustacchi return (DDI_WALK_CONTINUE); 1163047043c2SRobert Mustacchi } 1164047043c2SRobert Mustacchi 11659b0429a1SPu Wen if (vid != AMDZEN_PCI_VID_AMD && vid != AMDZEN_PCI_VID_HYGON) { 1166047043c2SRobert Mustacchi return (DDI_WALK_CONTINUE); 1167047043c2SRobert Mustacchi } 1168047043c2SRobert Mustacchi 1169047043c2SRobert Mustacchi for (i = 0; i < ARRAY_SIZE(amdzen_nb_ids); i++) { 1170047043c2SRobert Mustacchi if (amdzen_nb_ids[i] == did) { 1171047043c2SRobert Mustacchi match = B_TRUE; 1172047043c2SRobert Mustacchi } 1173047043c2SRobert Mustacchi } 1174047043c2SRobert Mustacchi 1175047043c2SRobert Mustacchi if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1176047043c2SRobert Mustacchi "reg", ®s, &nregs) != DDI_PROP_SUCCESS) { 1177047043c2SRobert Mustacchi return (DDI_WALK_CONTINUE); 1178047043c2SRobert Mustacchi } 1179047043c2SRobert Mustacchi 1180047043c2SRobert Mustacchi if (nregs == 0) { 1181047043c2SRobert Mustacchi ddi_prop_free(regs); 1182047043c2SRobert Mustacchi return (DDI_WALK_CONTINUE); 1183047043c2SRobert Mustacchi } 1184047043c2SRobert Mustacchi 1185047043c2SRobert Mustacchi if (PCI_REG_BUS_G(regs[0]) == AMDZEN_DF_BUSNO && 1186047043c2SRobert Mustacchi PCI_REG_DEV_G(regs[0]) >= AMDZEN_DF_FIRST_DEVICE) { 1187047043c2SRobert Mustacchi match = B_TRUE; 1188047043c2SRobert Mustacchi } 1189047043c2SRobert Mustacchi 1190047043c2SRobert Mustacchi ddi_prop_free(regs); 1191047043c2SRobert Mustacchi if (match) { 1192047043c2SRobert Mustacchi mutex_enter(&azn->azn_mutex); 1193047043c2SRobert Mustacchi azn->azn_nscanned++; 1194047043c2SRobert Mustacchi mutex_exit(&azn->azn_mutex); 1195047043c2SRobert Mustacchi } 1196047043c2SRobert Mustacchi 1197047043c2SRobert Mustacchi return (DDI_WALK_CONTINUE); 1198047043c2SRobert Mustacchi } 1199047043c2SRobert Mustacchi 1200047043c2SRobert Mustacchi static void 1201047043c2SRobert Mustacchi amdzen_stub_scan(void *arg) 1202047043c2SRobert Mustacchi { 1203047043c2SRobert Mustacchi amdzen_t *azn = arg; 1204047043c2SRobert Mustacchi 1205047043c2SRobert Mustacchi mutex_enter(&azn->azn_mutex); 1206047043c2SRobert Mustacchi azn->azn_nscanned = 0; 1207047043c2SRobert Mustacchi mutex_exit(&azn->azn_mutex); 1208047043c2SRobert Mustacchi 1209047043c2SRobert Mustacchi ddi_walk_devs(ddi_root_node(), amdzen_stub_scan_cb, azn); 1210047043c2SRobert Mustacchi 1211047043c2SRobert Mustacchi mutex_enter(&azn->azn_mutex); 1212047043c2SRobert Mustacchi azn->azn_flags &= ~AMDZEN_F_SCAN_DISPATCHED; 1213047043c2SRobert Mustacchi azn->azn_flags |= AMDZEN_F_SCAN_COMPLETE; 1214047043c2SRobert Mustacchi 1215047043c2SRobert Mustacchi if (azn->azn_nscanned == 0) { 1216047043c2SRobert Mustacchi azn->azn_flags |= AMDZEN_F_UNSUPPORTED; 1217047043c2SRobert Mustacchi azn->azn_taskqid = TASKQID_INVALID; 1218047043c2SRobert Mustacchi cv_broadcast(&azn->azn_cv); 1219047043c2SRobert Mustacchi } else if (azn->azn_npresent == azn->azn_nscanned) { 1220047043c2SRobert Mustacchi azn->azn_flags |= AMDZEN_F_ATTACH_DISPATCHED; 1221047043c2SRobert Mustacchi azn->azn_taskqid = taskq_dispatch(system_taskq, 1222047043c2SRobert Mustacchi amdzen_nexus_init, azn, TQ_SLEEP); 1223047043c2SRobert Mustacchi } 1224047043c2SRobert Mustacchi mutex_exit(&azn->azn_mutex); 1225047043c2SRobert Mustacchi } 1226047043c2SRobert Mustacchi 1227047043c2SRobert Mustacchi /* 1228047043c2SRobert Mustacchi * Unfortunately we can't really let the stubs detach as we may need them to be 1229047043c2SRobert Mustacchi * available for client operations. We may be able to improve this if we know 1230047043c2SRobert Mustacchi * that the actual nexus is going away. However, as long as it's active, we need 1231047043c2SRobert Mustacchi * all the stubs. 1232047043c2SRobert Mustacchi */ 1233047043c2SRobert Mustacchi int 1234047043c2SRobert Mustacchi amdzen_detach_stub(dev_info_t *dip, ddi_detach_cmd_t cmd) 1235047043c2SRobert Mustacchi { 1236047043c2SRobert Mustacchi if (cmd == DDI_SUSPEND) { 1237047043c2SRobert Mustacchi return (DDI_SUCCESS); 1238047043c2SRobert Mustacchi } 1239047043c2SRobert Mustacchi 1240047043c2SRobert Mustacchi return (DDI_FAILURE); 1241047043c2SRobert Mustacchi } 1242047043c2SRobert Mustacchi 1243047043c2SRobert Mustacchi int 1244047043c2SRobert Mustacchi amdzen_attach_stub(dev_info_t *dip, ddi_attach_cmd_t cmd) 1245047043c2SRobert Mustacchi { 1246047043c2SRobert Mustacchi int *regs, reg; 1247047043c2SRobert Mustacchi uint_t nregs, i; 1248047043c2SRobert Mustacchi uint16_t vid, did; 1249047043c2SRobert Mustacchi amdzen_stub_t *stub; 1250047043c2SRobert Mustacchi amdzen_t *azn = amdzen_data; 1251047043c2SRobert Mustacchi boolean_t valid = B_FALSE; 1252047043c2SRobert Mustacchi boolean_t nb = B_FALSE; 1253047043c2SRobert Mustacchi 1254047043c2SRobert Mustacchi if (cmd == DDI_RESUME) { 1255047043c2SRobert Mustacchi return (DDI_SUCCESS); 1256047043c2SRobert Mustacchi } else if (cmd != DDI_ATTACH) { 1257047043c2SRobert Mustacchi return (DDI_FAILURE); 1258047043c2SRobert Mustacchi } 1259047043c2SRobert Mustacchi 1260047043c2SRobert Mustacchi /* 1261047043c2SRobert Mustacchi * Make sure that the stub that we've been asked to attach is a pci type 1262047043c2SRobert Mustacchi * device. If not, then there is no reason for us to proceed. 1263047043c2SRobert Mustacchi */ 1264047043c2SRobert Mustacchi if (strncmp("pci", ddi_get_name(dip), 3) != 0) { 1265047043c2SRobert Mustacchi dev_err(dip, CE_WARN, "asked to attach a bad AMD Zen nexus " 1266047043c2SRobert Mustacchi "stub: %s", ddi_get_name(dip)); 1267047043c2SRobert Mustacchi return (DDI_FAILURE); 1268047043c2SRobert Mustacchi } 1269047043c2SRobert Mustacchi vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1270047043c2SRobert Mustacchi "vendor-id", PCI_EINVAL16); 1271047043c2SRobert Mustacchi did = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1272047043c2SRobert Mustacchi "device-id", PCI_EINVAL16); 1273047043c2SRobert Mustacchi if (vid == PCI_EINVAL16 || did == PCI_EINVAL16) { 1274047043c2SRobert Mustacchi dev_err(dip, CE_WARN, "failed to get PCI ID properties"); 1275047043c2SRobert Mustacchi return (DDI_FAILURE); 1276047043c2SRobert Mustacchi } 1277047043c2SRobert Mustacchi 12789b0429a1SPu Wen if (vid != AMDZEN_PCI_VID_AMD && vid != AMDZEN_PCI_VID_HYGON) { 12799b0429a1SPu Wen dev_err(dip, CE_WARN, "expected vendor ID (0x%x), found 0x%x", 12809b0429a1SPu Wen cpuid_getvendor(CPU) == X86_VENDOR_HYGON ? 12819b0429a1SPu Wen AMDZEN_PCI_VID_HYGON : AMDZEN_PCI_VID_AMD, vid); 1282047043c2SRobert Mustacchi return (DDI_FAILURE); 1283047043c2SRobert Mustacchi } 1284047043c2SRobert Mustacchi 1285047043c2SRobert Mustacchi if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1286047043c2SRobert Mustacchi "reg", ®s, &nregs) != DDI_PROP_SUCCESS) { 1287047043c2SRobert Mustacchi dev_err(dip, CE_WARN, "failed to get 'reg' property"); 1288047043c2SRobert Mustacchi return (DDI_FAILURE); 1289047043c2SRobert Mustacchi } 1290047043c2SRobert Mustacchi 1291047043c2SRobert Mustacchi if (nregs == 0) { 1292047043c2SRobert Mustacchi ddi_prop_free(regs); 1293047043c2SRobert Mustacchi dev_err(dip, CE_WARN, "missing 'reg' property values"); 1294047043c2SRobert Mustacchi return (DDI_FAILURE); 1295047043c2SRobert Mustacchi } 1296047043c2SRobert Mustacchi reg = *regs; 1297047043c2SRobert Mustacchi ddi_prop_free(regs); 1298047043c2SRobert Mustacchi 1299047043c2SRobert Mustacchi for (i = 0; i < ARRAY_SIZE(amdzen_nb_ids); i++) { 1300047043c2SRobert Mustacchi if (amdzen_nb_ids[i] == did) { 1301047043c2SRobert Mustacchi valid = B_TRUE; 1302047043c2SRobert Mustacchi nb = B_TRUE; 1303047043c2SRobert Mustacchi } 1304047043c2SRobert Mustacchi } 1305047043c2SRobert Mustacchi 1306047043c2SRobert Mustacchi if (!valid && PCI_REG_BUS_G(reg) == AMDZEN_DF_BUSNO && 1307047043c2SRobert Mustacchi PCI_REG_DEV_G(reg) >= AMDZEN_DF_FIRST_DEVICE) { 1308047043c2SRobert Mustacchi valid = B_TRUE; 1309047043c2SRobert Mustacchi nb = B_FALSE; 1310047043c2SRobert Mustacchi } 1311047043c2SRobert Mustacchi 1312047043c2SRobert Mustacchi if (!valid) { 1313047043c2SRobert Mustacchi dev_err(dip, CE_WARN, "device %s didn't match the nexus list", 1314047043c2SRobert Mustacchi ddi_get_name(dip)); 1315047043c2SRobert Mustacchi return (DDI_FAILURE); 1316047043c2SRobert Mustacchi } 1317047043c2SRobert Mustacchi 1318047043c2SRobert Mustacchi stub = kmem_alloc(sizeof (amdzen_stub_t), KM_SLEEP); 1319047043c2SRobert Mustacchi if (pci_config_setup(dip, &stub->azns_cfgspace) != DDI_SUCCESS) { 1320047043c2SRobert Mustacchi dev_err(dip, CE_WARN, "failed to set up config space"); 1321047043c2SRobert Mustacchi kmem_free(stub, sizeof (amdzen_stub_t)); 1322047043c2SRobert Mustacchi return (DDI_FAILURE); 1323047043c2SRobert Mustacchi } 1324047043c2SRobert Mustacchi 1325047043c2SRobert Mustacchi stub->azns_dip = dip; 1326047043c2SRobert Mustacchi stub->azns_vid = vid; 1327047043c2SRobert Mustacchi stub->azns_did = did; 1328047043c2SRobert Mustacchi stub->azns_bus = PCI_REG_BUS_G(reg); 1329047043c2SRobert Mustacchi stub->azns_dev = PCI_REG_DEV_G(reg); 1330047043c2SRobert Mustacchi stub->azns_func = PCI_REG_FUNC_G(reg); 1331047043c2SRobert Mustacchi ddi_set_driver_private(dip, stub); 1332047043c2SRobert Mustacchi 1333047043c2SRobert Mustacchi mutex_enter(&azn->azn_mutex); 1334047043c2SRobert Mustacchi azn->azn_npresent++; 1335047043c2SRobert Mustacchi if (nb) { 1336047043c2SRobert Mustacchi list_insert_tail(&azn->azn_nb_stubs, stub); 1337047043c2SRobert Mustacchi } else { 1338047043c2SRobert Mustacchi list_insert_tail(&azn->azn_df_stubs, stub); 1339047043c2SRobert Mustacchi } 1340047043c2SRobert Mustacchi 1341047043c2SRobert Mustacchi if ((azn->azn_flags & AMDZEN_F_TASKQ_MASK) == AMDZEN_F_SCAN_COMPLETE && 1342047043c2SRobert Mustacchi azn->azn_nscanned == azn->azn_npresent) { 1343047043c2SRobert Mustacchi azn->azn_flags |= AMDZEN_F_ATTACH_DISPATCHED; 1344047043c2SRobert Mustacchi azn->azn_taskqid = taskq_dispatch(system_taskq, 1345047043c2SRobert Mustacchi amdzen_nexus_init, azn, TQ_SLEEP); 1346047043c2SRobert Mustacchi } 1347047043c2SRobert Mustacchi mutex_exit(&azn->azn_mutex); 1348047043c2SRobert Mustacchi 1349047043c2SRobert Mustacchi return (DDI_SUCCESS); 1350047043c2SRobert Mustacchi } 1351047043c2SRobert Mustacchi 1352047043c2SRobert Mustacchi static int 1353047043c2SRobert Mustacchi amdzen_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop, 1354047043c2SRobert Mustacchi void *arg, void *result) 1355047043c2SRobert Mustacchi { 1356047043c2SRobert Mustacchi char buf[32]; 1357047043c2SRobert Mustacchi dev_info_t *child; 1358047043c2SRobert Mustacchi const amdzen_child_data_t *acd; 1359047043c2SRobert Mustacchi 1360047043c2SRobert Mustacchi switch (ctlop) { 1361047043c2SRobert Mustacchi case DDI_CTLOPS_REPORTDEV: 1362047043c2SRobert Mustacchi if (rdip == NULL) { 1363047043c2SRobert Mustacchi return (DDI_FAILURE); 1364047043c2SRobert Mustacchi } 1365047043c2SRobert Mustacchi cmn_err(CE_CONT, "amdzen nexus: %s@%s, %s%d\n", 1366047043c2SRobert Mustacchi ddi_node_name(rdip), ddi_get_name_addr(rdip), 1367047043c2SRobert Mustacchi ddi_driver_name(rdip), ddi_get_instance(rdip)); 1368047043c2SRobert Mustacchi break; 1369047043c2SRobert Mustacchi case DDI_CTLOPS_INITCHILD: 1370047043c2SRobert Mustacchi child = arg; 1371047043c2SRobert Mustacchi if (child == NULL) { 1372047043c2SRobert Mustacchi dev_err(dip, CE_WARN, "!no child passed for " 1373047043c2SRobert Mustacchi "DDI_CTLOPS_INITCHILD"); 1374047043c2SRobert Mustacchi } 1375047043c2SRobert Mustacchi 1376047043c2SRobert Mustacchi acd = ddi_get_parent_data(child); 1377047043c2SRobert Mustacchi if (acd == NULL) { 1378047043c2SRobert Mustacchi dev_err(dip, CE_WARN, "!missing child parent data"); 1379047043c2SRobert Mustacchi return (DDI_FAILURE); 1380047043c2SRobert Mustacchi } 1381047043c2SRobert Mustacchi 1382047043c2SRobert Mustacchi if (snprintf(buf, sizeof (buf), "%d", acd->acd_addr) >= 1383047043c2SRobert Mustacchi sizeof (buf)) { 1384047043c2SRobert Mustacchi dev_err(dip, CE_WARN, "!failed to construct device " 1385047043c2SRobert Mustacchi "addr due to overflow"); 1386047043c2SRobert Mustacchi return (DDI_FAILURE); 1387047043c2SRobert Mustacchi } 1388047043c2SRobert Mustacchi 1389047043c2SRobert Mustacchi ddi_set_name_addr(child, buf); 1390047043c2SRobert Mustacchi break; 1391047043c2SRobert Mustacchi case DDI_CTLOPS_UNINITCHILD: 1392047043c2SRobert Mustacchi child = arg; 1393047043c2SRobert Mustacchi if (child == NULL) { 1394047043c2SRobert Mustacchi dev_err(dip, CE_WARN, "!no child passed for " 1395047043c2SRobert Mustacchi "DDI_CTLOPS_UNINITCHILD"); 1396047043c2SRobert Mustacchi } 1397047043c2SRobert Mustacchi 1398047043c2SRobert Mustacchi ddi_set_name_addr(child, NULL); 1399047043c2SRobert Mustacchi break; 1400047043c2SRobert Mustacchi default: 1401047043c2SRobert Mustacchi return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 1402047043c2SRobert Mustacchi } 1403047043c2SRobert Mustacchi return (DDI_SUCCESS); 1404047043c2SRobert Mustacchi } 1405047043c2SRobert Mustacchi 1406047043c2SRobert Mustacchi static int 1407047043c2SRobert Mustacchi amdzen_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 1408047043c2SRobert Mustacchi { 1409047043c2SRobert Mustacchi amdzen_t *azn = amdzen_data; 1410047043c2SRobert Mustacchi 1411047043c2SRobert Mustacchi if (cmd == DDI_RESUME) { 1412047043c2SRobert Mustacchi return (DDI_SUCCESS); 1413047043c2SRobert Mustacchi } else if (cmd != DDI_ATTACH) { 1414047043c2SRobert Mustacchi return (DDI_FAILURE); 1415047043c2SRobert Mustacchi } 1416047043c2SRobert Mustacchi 1417047043c2SRobert Mustacchi mutex_enter(&azn->azn_mutex); 1418047043c2SRobert Mustacchi if (azn->azn_dip != NULL) { 1419047043c2SRobert Mustacchi dev_err(dip, CE_WARN, "driver is already attached!"); 1420047043c2SRobert Mustacchi mutex_exit(&azn->azn_mutex); 1421047043c2SRobert Mustacchi return (DDI_FAILURE); 1422047043c2SRobert Mustacchi } 1423047043c2SRobert Mustacchi 1424047043c2SRobert Mustacchi azn->azn_dip = dip; 1425047043c2SRobert Mustacchi azn->azn_taskqid = taskq_dispatch(system_taskq, amdzen_stub_scan, 1426047043c2SRobert Mustacchi azn, TQ_SLEEP); 1427047043c2SRobert Mustacchi azn->azn_flags |= AMDZEN_F_SCAN_DISPATCHED; 1428047043c2SRobert Mustacchi mutex_exit(&azn->azn_mutex); 1429047043c2SRobert Mustacchi 1430047043c2SRobert Mustacchi return (DDI_SUCCESS); 1431047043c2SRobert Mustacchi } 1432047043c2SRobert Mustacchi 1433047043c2SRobert Mustacchi static int 1434047043c2SRobert Mustacchi amdzen_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 1435047043c2SRobert Mustacchi { 1436047043c2SRobert Mustacchi amdzen_t *azn = amdzen_data; 1437047043c2SRobert Mustacchi 1438047043c2SRobert Mustacchi if (cmd == DDI_SUSPEND) { 1439047043c2SRobert Mustacchi return (DDI_SUCCESS); 1440047043c2SRobert Mustacchi } else if (cmd != DDI_DETACH) { 1441047043c2SRobert Mustacchi return (DDI_FAILURE); 1442047043c2SRobert Mustacchi } 1443047043c2SRobert Mustacchi 1444047043c2SRobert Mustacchi mutex_enter(&azn->azn_mutex); 1445047043c2SRobert Mustacchi while (azn->azn_taskqid != TASKQID_INVALID) { 1446047043c2SRobert Mustacchi cv_wait(&azn->azn_cv, &azn->azn_mutex); 1447047043c2SRobert Mustacchi } 1448047043c2SRobert Mustacchi 1449047043c2SRobert Mustacchi /* 1450047043c2SRobert Mustacchi * If we've attached any stub drivers, e.g. this platform is important 1451047043c2SRobert Mustacchi * for us, then we fail detach. 1452047043c2SRobert Mustacchi */ 1453047043c2SRobert Mustacchi if (!list_is_empty(&azn->azn_df_stubs) || 1454047043c2SRobert Mustacchi !list_is_empty(&azn->azn_nb_stubs)) { 1455047043c2SRobert Mustacchi mutex_exit(&azn->azn_mutex); 1456047043c2SRobert Mustacchi return (DDI_FAILURE); 1457047043c2SRobert Mustacchi } 1458047043c2SRobert Mustacchi 1459047043c2SRobert Mustacchi azn->azn_dip = NULL; 1460047043c2SRobert Mustacchi mutex_exit(&azn->azn_mutex); 1461047043c2SRobert Mustacchi 1462047043c2SRobert Mustacchi return (DDI_SUCCESS); 1463047043c2SRobert Mustacchi } 1464047043c2SRobert Mustacchi 1465047043c2SRobert Mustacchi static void 1466047043c2SRobert Mustacchi amdzen_free(void) 1467047043c2SRobert Mustacchi { 1468047043c2SRobert Mustacchi if (amdzen_data == NULL) { 1469047043c2SRobert Mustacchi return; 1470047043c2SRobert Mustacchi } 1471047043c2SRobert Mustacchi 1472047043c2SRobert Mustacchi VERIFY(list_is_empty(&amdzen_data->azn_df_stubs)); 1473047043c2SRobert Mustacchi list_destroy(&amdzen_data->azn_df_stubs); 1474047043c2SRobert Mustacchi VERIFY(list_is_empty(&amdzen_data->azn_nb_stubs)); 1475047043c2SRobert Mustacchi list_destroy(&amdzen_data->azn_nb_stubs); 1476047043c2SRobert Mustacchi cv_destroy(&amdzen_data->azn_cv); 1477047043c2SRobert Mustacchi mutex_destroy(&amdzen_data->azn_mutex); 1478047043c2SRobert Mustacchi kmem_free(amdzen_data, sizeof (amdzen_t)); 1479047043c2SRobert Mustacchi amdzen_data = NULL; 1480047043c2SRobert Mustacchi } 1481047043c2SRobert Mustacchi 1482047043c2SRobert Mustacchi static void 1483047043c2SRobert Mustacchi amdzen_alloc(void) 1484047043c2SRobert Mustacchi { 1485047043c2SRobert Mustacchi amdzen_data = kmem_zalloc(sizeof (amdzen_t), KM_SLEEP); 1486047043c2SRobert Mustacchi mutex_init(&amdzen_data->azn_mutex, NULL, MUTEX_DRIVER, NULL); 1487047043c2SRobert Mustacchi list_create(&amdzen_data->azn_df_stubs, sizeof (amdzen_stub_t), 1488047043c2SRobert Mustacchi offsetof(amdzen_stub_t, azns_link)); 1489047043c2SRobert Mustacchi list_create(&amdzen_data->azn_nb_stubs, sizeof (amdzen_stub_t), 1490047043c2SRobert Mustacchi offsetof(amdzen_stub_t, azns_link)); 1491047043c2SRobert Mustacchi cv_init(&amdzen_data->azn_cv, NULL, CV_DRIVER, NULL); 1492047043c2SRobert Mustacchi } 1493047043c2SRobert Mustacchi 1494047043c2SRobert Mustacchi struct bus_ops amdzen_bus_ops = { 1495047043c2SRobert Mustacchi .busops_rev = BUSO_REV, 1496047043c2SRobert Mustacchi .bus_map = nullbusmap, 1497047043c2SRobert Mustacchi .bus_dma_map = ddi_no_dma_map, 1498047043c2SRobert Mustacchi .bus_dma_allochdl = ddi_no_dma_allochdl, 1499047043c2SRobert Mustacchi .bus_dma_freehdl = ddi_no_dma_freehdl, 1500047043c2SRobert Mustacchi .bus_dma_bindhdl = ddi_no_dma_bindhdl, 1501047043c2SRobert Mustacchi .bus_dma_unbindhdl = ddi_no_dma_unbindhdl, 1502047043c2SRobert Mustacchi .bus_dma_flush = ddi_no_dma_flush, 1503047043c2SRobert Mustacchi .bus_dma_win = ddi_no_dma_win, 1504047043c2SRobert Mustacchi .bus_dma_ctl = ddi_no_dma_mctl, 1505047043c2SRobert Mustacchi .bus_prop_op = ddi_bus_prop_op, 1506047043c2SRobert Mustacchi .bus_ctl = amdzen_bus_ctl 1507047043c2SRobert Mustacchi }; 1508047043c2SRobert Mustacchi 1509047043c2SRobert Mustacchi static struct dev_ops amdzen_dev_ops = { 1510047043c2SRobert Mustacchi .devo_rev = DEVO_REV, 1511047043c2SRobert Mustacchi .devo_refcnt = 0, 1512047043c2SRobert Mustacchi .devo_getinfo = nodev, 1513047043c2SRobert Mustacchi .devo_identify = nulldev, 1514047043c2SRobert Mustacchi .devo_probe = nulldev, 1515047043c2SRobert Mustacchi .devo_attach = amdzen_attach, 1516047043c2SRobert Mustacchi .devo_detach = amdzen_detach, 1517047043c2SRobert Mustacchi .devo_reset = nodev, 1518047043c2SRobert Mustacchi .devo_quiesce = ddi_quiesce_not_needed, 1519047043c2SRobert Mustacchi .devo_bus_ops = &amdzen_bus_ops 1520047043c2SRobert Mustacchi }; 1521047043c2SRobert Mustacchi 1522047043c2SRobert Mustacchi static struct modldrv amdzen_modldrv = { 1523047043c2SRobert Mustacchi .drv_modops = &mod_driverops, 1524047043c2SRobert Mustacchi .drv_linkinfo = "AMD Zen Nexus Driver", 1525047043c2SRobert Mustacchi .drv_dev_ops = &amdzen_dev_ops 1526047043c2SRobert Mustacchi }; 1527047043c2SRobert Mustacchi 1528047043c2SRobert Mustacchi static struct modlinkage amdzen_modlinkage = { 1529047043c2SRobert Mustacchi .ml_rev = MODREV_1, 1530047043c2SRobert Mustacchi .ml_linkage = { &amdzen_modldrv, NULL } 1531047043c2SRobert Mustacchi }; 1532047043c2SRobert Mustacchi 1533047043c2SRobert Mustacchi int 1534047043c2SRobert Mustacchi _init(void) 1535047043c2SRobert Mustacchi { 1536047043c2SRobert Mustacchi int ret; 1537047043c2SRobert Mustacchi 15389b0429a1SPu Wen if (cpuid_getvendor(CPU) != X86_VENDOR_AMD && 15399b0429a1SPu Wen cpuid_getvendor(CPU) != X86_VENDOR_HYGON) { 1540047043c2SRobert Mustacchi return (ENOTSUP); 1541047043c2SRobert Mustacchi } 1542047043c2SRobert Mustacchi 1543047043c2SRobert Mustacchi if ((ret = mod_install(&amdzen_modlinkage)) == 0) { 1544047043c2SRobert Mustacchi amdzen_alloc(); 1545047043c2SRobert Mustacchi } 1546047043c2SRobert Mustacchi 1547047043c2SRobert Mustacchi return (ret); 1548047043c2SRobert Mustacchi } 1549047043c2SRobert Mustacchi 1550047043c2SRobert Mustacchi int 1551047043c2SRobert Mustacchi _info(struct modinfo *modinfop) 1552047043c2SRobert Mustacchi { 1553047043c2SRobert Mustacchi return (mod_info(&amdzen_modlinkage, modinfop)); 1554047043c2SRobert Mustacchi } 1555047043c2SRobert Mustacchi 1556047043c2SRobert Mustacchi int 1557047043c2SRobert Mustacchi _fini(void) 1558047043c2SRobert Mustacchi { 1559047043c2SRobert Mustacchi int ret; 1560047043c2SRobert Mustacchi 1561047043c2SRobert Mustacchi if ((ret = mod_remove(&amdzen_modlinkage)) == 0) { 1562047043c2SRobert Mustacchi amdzen_free(); 1563047043c2SRobert Mustacchi } 1564047043c2SRobert Mustacchi 1565047043c2SRobert Mustacchi return (ret); 1566047043c2SRobert Mustacchi } 1567