xref: /illumos-gate/usr/src/uts/intel/io/amdzen/amdzen.c (revision 019df03d)
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.
141e0464b8SRobert Mustacchi  * Copyright 2024 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>
173dd23d762SRobert Mustacchi #include <sys/policy.h>
174dd23d762SRobert Mustacchi #include <sys/stat.h>
175dd23d762SRobert Mustacchi #include <sys/sunddi.h>
176dd23d762SRobert Mustacchi #include <sys/bitmap.h>
1771e0464b8SRobert Mustacchi #include <sys/stdbool.h>
178047043c2SRobert Mustacchi 
17971815ce7SRobert Mustacchi #include <sys/amdzen/df.h>
180dd23d762SRobert Mustacchi #include <sys/amdzen/ccd.h>
181047043c2SRobert Mustacchi #include "amdzen.h"
182dd23d762SRobert Mustacchi #include "amdzen_client.h"
183dd23d762SRobert Mustacchi #include "amdzen_topo.h"
184047043c2SRobert Mustacchi 
185047043c2SRobert Mustacchi amdzen_t *amdzen_data;
186047043c2SRobert Mustacchi 
187047043c2SRobert Mustacchi /*
188dd23d762SRobert Mustacchi  * Internal minor nodes for devices that the nexus provides itself.
189dd23d762SRobert Mustacchi  */
190dd23d762SRobert Mustacchi #define	AMDZEN_MINOR_TOPO	0
191dd23d762SRobert Mustacchi 
192dd23d762SRobert Mustacchi /*
193047043c2SRobert Mustacchi  * Array of northbridge IDs that we care about.
194047043c2SRobert Mustacchi  */
195047043c2SRobert Mustacchi static const uint16_t amdzen_nb_ids[] = {
196047043c2SRobert Mustacchi 	/* Family 17h Ryzen, Epyc Models 00h-0fh (Zen uarch) */
197047043c2SRobert Mustacchi 	0x1450,
198becd642cSRobert Mustacchi 	/* Family 17h Raven Ridge, Kestrel, Dali Models 10h-2fh (Zen uarch) */
199047043c2SRobert Mustacchi 	0x15d0,
200e9abe9d6SRobert Mustacchi 	/* Family 17h/19h Rome, Milan, Matisse, Vermeer Zen 2/Zen 3 uarch */
201becd642cSRobert Mustacchi 	0x1480,
202f8e9c7b3SRobert Mustacchi 	/* Family 17h/19h Renoir, Cezanne, Van Gogh Zen 2/3 uarch */
203f8e9c7b3SRobert Mustacchi 	0x1630,
204e6f89c3aSRobert Mustacchi 	/* Family 19h Genoa and Bergamo */
205f8e9c7b3SRobert Mustacchi 	0x14a4,
206f8e9c7b3SRobert Mustacchi 	/* Family 17h Mendocino, Family 19h Rembrandt */
207f8e9c7b3SRobert Mustacchi 	0x14b5,
208*019df03dSRobert Mustacchi 	/* Family 19h Raphael, Family 1Ah 40-4fh */
209e6f89c3aSRobert Mustacchi 	0x14d8,
210e6f89c3aSRobert Mustacchi 	/* Family 19h Phoenix */
211*019df03dSRobert Mustacchi 	0x14e8,
212*019df03dSRobert Mustacchi 	/* Family 1Ah Turin */
213*019df03dSRobert Mustacchi 	0x153a,
214*019df03dSRobert Mustacchi 	/* Family 1Ah 20-2fh */
215*019df03dSRobert Mustacchi 	0x1507
216047043c2SRobert Mustacchi };
217047043c2SRobert Mustacchi 
218047043c2SRobert Mustacchi typedef struct {
219047043c2SRobert Mustacchi 	char *acd_name;
220047043c2SRobert Mustacchi 	amdzen_child_t acd_addr;
2211e0464b8SRobert Mustacchi 	/*
2221e0464b8SRobert Mustacchi 	 * This indicates whether or not we should issue warnings to users when
2231e0464b8SRobert Mustacchi 	 * something happens specific to this instance. The main reason we don't
2241e0464b8SRobert Mustacchi 	 * want to is for optional devices that may not be installed as they are
2251e0464b8SRobert Mustacchi 	 * for development purposes (e.g. usmn, zen_udf); however, if there is
2261e0464b8SRobert Mustacchi 	 * an issue with the others we still want to know.
2271e0464b8SRobert Mustacchi 	 */
2281e0464b8SRobert Mustacchi 	bool acd_warn;
229047043c2SRobert Mustacchi } amdzen_child_data_t;
230047043c2SRobert Mustacchi 
231047043c2SRobert Mustacchi static const amdzen_child_data_t amdzen_children[] = {
2321e0464b8SRobert Mustacchi 	{ "smntemp", AMDZEN_C_SMNTEMP, true },
2331e0464b8SRobert Mustacchi 	{ "usmn", AMDZEN_C_USMN, false },
2341e0464b8SRobert Mustacchi 	{ "zen_udf", AMDZEN_C_ZEN_UDF, false },
2351e0464b8SRobert Mustacchi 	{ "zen_umc", AMDZEN_C_ZEN_UMC, true }
236047043c2SRobert Mustacchi };
237047043c2SRobert Mustacchi 
2384adf43b0SKeith M Wesolowski static uint8_t
amdzen_stub_get8(amdzen_stub_t * stub,off_t reg)2394adf43b0SKeith M Wesolowski amdzen_stub_get8(amdzen_stub_t *stub, off_t reg)
2404adf43b0SKeith M Wesolowski {
2414adf43b0SKeith M Wesolowski 	return (pci_config_get8(stub->azns_cfgspace, reg));
2424adf43b0SKeith M Wesolowski }
2434adf43b0SKeith M Wesolowski 
2444adf43b0SKeith M Wesolowski static uint16_t
amdzen_stub_get16(amdzen_stub_t * stub,off_t reg)2454adf43b0SKeith M Wesolowski amdzen_stub_get16(amdzen_stub_t *stub, off_t reg)
2464adf43b0SKeith M Wesolowski {
2474adf43b0SKeith M Wesolowski 	return (pci_config_get16(stub->azns_cfgspace, reg));
2484adf43b0SKeith M Wesolowski }
2494adf43b0SKeith M Wesolowski 
250047043c2SRobert Mustacchi static uint32_t
amdzen_stub_get32(amdzen_stub_t * stub,off_t reg)251047043c2SRobert Mustacchi amdzen_stub_get32(amdzen_stub_t *stub, off_t reg)
252047043c2SRobert Mustacchi {
253047043c2SRobert Mustacchi 	return (pci_config_get32(stub->azns_cfgspace, reg));
254047043c2SRobert Mustacchi }
255047043c2SRobert Mustacchi 
256549e0fd3SRobert Mustacchi static uint64_t
amdzen_stub_get64(amdzen_stub_t * stub,off_t reg)257549e0fd3SRobert Mustacchi amdzen_stub_get64(amdzen_stub_t *stub, off_t reg)
258549e0fd3SRobert Mustacchi {
259549e0fd3SRobert Mustacchi 	return (pci_config_get64(stub->azns_cfgspace, reg));
260549e0fd3SRobert Mustacchi }
261549e0fd3SRobert Mustacchi 
262047043c2SRobert Mustacchi static void
amdzen_stub_put8(amdzen_stub_t * stub,off_t reg,uint8_t val)2634adf43b0SKeith M Wesolowski amdzen_stub_put8(amdzen_stub_t *stub, off_t reg, uint8_t val)
2644adf43b0SKeith M Wesolowski {
2654adf43b0SKeith M Wesolowski 	pci_config_put8(stub->azns_cfgspace, reg, val);
2664adf43b0SKeith M Wesolowski }
2674adf43b0SKeith M Wesolowski 
2684adf43b0SKeith M Wesolowski static void
amdzen_stub_put16(amdzen_stub_t * stub,off_t reg,uint16_t val)2694adf43b0SKeith M Wesolowski amdzen_stub_put16(amdzen_stub_t *stub, off_t reg, uint16_t val)
2704adf43b0SKeith M Wesolowski {
2714adf43b0SKeith M Wesolowski 	pci_config_put16(stub->azns_cfgspace, reg, val);
2724adf43b0SKeith M Wesolowski }
2734adf43b0SKeith M Wesolowski 
2744adf43b0SKeith M Wesolowski static void
amdzen_stub_put32(amdzen_stub_t * stub,off_t reg,uint32_t val)275047043c2SRobert Mustacchi amdzen_stub_put32(amdzen_stub_t *stub, off_t reg, uint32_t val)
276047043c2SRobert Mustacchi {
277047043c2SRobert Mustacchi 	pci_config_put32(stub->azns_cfgspace, reg, val);
278047043c2SRobert Mustacchi }
279047043c2SRobert Mustacchi 
28071815ce7SRobert Mustacchi static uint64_t
amdzen_df_read_regdef(amdzen_t * azn,amdzen_df_t * df,const df_reg_def_t def,uint8_t inst,boolean_t do_64)28171815ce7SRobert Mustacchi amdzen_df_read_regdef(amdzen_t *azn, amdzen_df_t *df, const df_reg_def_t def,
28271815ce7SRobert Mustacchi     uint8_t inst, boolean_t do_64)
28371815ce7SRobert Mustacchi {
28471815ce7SRobert Mustacchi 	df_reg_def_t ficaa;
28571815ce7SRobert Mustacchi 	df_reg_def_t ficad;
28671815ce7SRobert Mustacchi 	uint32_t val = 0;
28771815ce7SRobert Mustacchi 	df_rev_t df_rev = azn->azn_dfs[0].adf_rev;
28871815ce7SRobert Mustacchi 
28971815ce7SRobert Mustacchi 	VERIFY(MUTEX_HELD(&azn->azn_mutex));
29071815ce7SRobert Mustacchi 	ASSERT3U(def.drd_gens & df_rev, ==, df_rev);
29171815ce7SRobert Mustacchi 	val = DF_FICAA_V2_SET_TARG_INST(val, 1);
29271815ce7SRobert Mustacchi 	val = DF_FICAA_V2_SET_FUNC(val, def.drd_func);
29371815ce7SRobert Mustacchi 	val = DF_FICAA_V2_SET_INST(val, inst);
29471815ce7SRobert Mustacchi 	val = DF_FICAA_V2_SET_64B(val, do_64 ? 1 : 0);
29571815ce7SRobert Mustacchi 
29671815ce7SRobert Mustacchi 	switch (df_rev) {
29771815ce7SRobert Mustacchi 	case DF_REV_2:
29871815ce7SRobert Mustacchi 	case DF_REV_3:
29971815ce7SRobert Mustacchi 	case DF_REV_3P5:
30071815ce7SRobert Mustacchi 		ficaa = DF_FICAA_V2;
30171815ce7SRobert Mustacchi 		ficad = DF_FICAD_LO_V2;
30271815ce7SRobert Mustacchi 		/*
30371815ce7SRobert Mustacchi 		 * Both here and in the DFv4 case, the register ignores the
30471815ce7SRobert Mustacchi 		 * lower 2 bits. That is we can only address and encode things
30571815ce7SRobert Mustacchi 		 * in units of 4 bytes.
30671815ce7SRobert Mustacchi 		 */
30771815ce7SRobert Mustacchi 		val = DF_FICAA_V2_SET_REG(val, def.drd_reg >> 2);
30871815ce7SRobert Mustacchi 		break;
30971815ce7SRobert Mustacchi 	case DF_REV_4:
310*019df03dSRobert Mustacchi 	case DF_REV_4D2:
31171815ce7SRobert Mustacchi 		ficaa = DF_FICAA_V4;
31271815ce7SRobert Mustacchi 		ficad = DF_FICAD_LO_V4;
31371815ce7SRobert Mustacchi 		val = DF_FICAA_V4_SET_REG(val, def.drd_reg >> 2);
31471815ce7SRobert Mustacchi 		break;
31571815ce7SRobert Mustacchi 	default:
31671815ce7SRobert Mustacchi 		panic("encountered unexpected DF rev: %u", df_rev);
31771815ce7SRobert Mustacchi 	}
31871815ce7SRobert Mustacchi 
31971815ce7SRobert Mustacchi 	amdzen_stub_put32(df->adf_funcs[ficaa.drd_func], ficaa.drd_reg, val);
32071815ce7SRobert Mustacchi 	if (do_64) {
32171815ce7SRobert Mustacchi 		return (amdzen_stub_get64(df->adf_funcs[ficad.drd_func],
32271815ce7SRobert Mustacchi 		    ficad.drd_reg));
32371815ce7SRobert Mustacchi 	} else {
32471815ce7SRobert Mustacchi 		return (amdzen_stub_get32(df->adf_funcs[ficad.drd_func],
32571815ce7SRobert Mustacchi 		    ficad.drd_reg));
32671815ce7SRobert Mustacchi 	}
32771815ce7SRobert Mustacchi }
32871815ce7SRobert Mustacchi 
329047043c2SRobert Mustacchi /*
330047043c2SRobert Mustacchi  * Perform a targeted 32-bit indirect read to a specific instance and function.
331047043c2SRobert Mustacchi  */
332047043c2SRobert Mustacchi static uint32_t
amdzen_df_read32(amdzen_t * azn,amdzen_df_t * df,uint8_t inst,const df_reg_def_t def)33371815ce7SRobert Mustacchi amdzen_df_read32(amdzen_t *azn, amdzen_df_t *df, uint8_t inst,
33471815ce7SRobert Mustacchi     const df_reg_def_t def)
335047043c2SRobert Mustacchi {
33671815ce7SRobert Mustacchi 	return (amdzen_df_read_regdef(azn, df, def, inst, B_FALSE));
337047043c2SRobert Mustacchi }
338047043c2SRobert Mustacchi 
339549e0fd3SRobert Mustacchi /*
34071815ce7SRobert Mustacchi  * For a broadcast read, just go to the underlying PCI function and perform a
34171815ce7SRobert Mustacchi  * read. At this point in time, we don't believe we need to use the FICAA/FICAD
34271815ce7SRobert Mustacchi  * to access it (though it does have a broadcast mode).
343549e0fd3SRobert Mustacchi  */
34471815ce7SRobert Mustacchi static uint32_t
amdzen_df_read32_bcast(amdzen_t * azn,amdzen_df_t * df,const df_reg_def_t def)34571815ce7SRobert Mustacchi amdzen_df_read32_bcast(amdzen_t *azn, amdzen_df_t *df, const df_reg_def_t def)
346549e0fd3SRobert Mustacchi {
347549e0fd3SRobert Mustacchi 	VERIFY(MUTEX_HELD(&azn->azn_mutex));
34871815ce7SRobert Mustacchi 	return (amdzen_stub_get32(df->adf_funcs[def.drd_func], def.drd_reg));
349549e0fd3SRobert Mustacchi }
350549e0fd3SRobert Mustacchi 
351047043c2SRobert Mustacchi static uint32_t
amdzen_smn_read(amdzen_t * azn,amdzen_df_t * df,const smn_reg_t reg)3524adf43b0SKeith M Wesolowski amdzen_smn_read(amdzen_t *azn, amdzen_df_t *df, const smn_reg_t reg)
353047043c2SRobert Mustacchi {
3544adf43b0SKeith M Wesolowski 	const uint32_t base_addr = SMN_REG_ADDR_BASE(reg);
3554adf43b0SKeith M Wesolowski 	const uint32_t addr_off = SMN_REG_ADDR_OFF(reg);
3564adf43b0SKeith M Wesolowski 
3574adf43b0SKeith M Wesolowski 	VERIFY(SMN_REG_IS_NATURALLY_ALIGNED(reg));
358047043c2SRobert Mustacchi 	VERIFY(MUTEX_HELD(&azn->azn_mutex));
3594adf43b0SKeith M Wesolowski 	amdzen_stub_put32(df->adf_nb, AMDZEN_NB_SMN_ADDR, base_addr);
3604adf43b0SKeith M Wesolowski 
3614adf43b0SKeith M Wesolowski 	switch (SMN_REG_SIZE(reg)) {
3624adf43b0SKeith M Wesolowski 	case 1:
3634adf43b0SKeith M Wesolowski 		return ((uint32_t)amdzen_stub_get8(df->adf_nb,
3644adf43b0SKeith M Wesolowski 		    AMDZEN_NB_SMN_DATA + addr_off));
3654adf43b0SKeith M Wesolowski 	case 2:
3664adf43b0SKeith M Wesolowski 		return ((uint32_t)amdzen_stub_get16(df->adf_nb,
3674adf43b0SKeith M Wesolowski 		    AMDZEN_NB_SMN_DATA + addr_off));
3684adf43b0SKeith M Wesolowski 	case 4:
369047043c2SRobert Mustacchi 		return (amdzen_stub_get32(df->adf_nb, AMDZEN_NB_SMN_DATA));
3704adf43b0SKeith M Wesolowski 	default:
3714adf43b0SKeith M Wesolowski 		panic("unreachable invalid SMN register size %u",
3724adf43b0SKeith M Wesolowski 		    SMN_REG_SIZE(reg));
3734adf43b0SKeith M Wesolowski 	}
374047043c2SRobert Mustacchi }
375047043c2SRobert Mustacchi 
376f198607dSRobert Mustacchi static void
amdzen_smn_write(amdzen_t * azn,amdzen_df_t * df,const smn_reg_t reg,const uint32_t val)3774adf43b0SKeith M Wesolowski amdzen_smn_write(amdzen_t *azn, amdzen_df_t *df, const smn_reg_t reg,
378ba215efeSKeith M Wesolowski     const uint32_t val)
379f198607dSRobert Mustacchi {
3804adf43b0SKeith M Wesolowski 	const uint32_t base_addr = SMN_REG_ADDR_BASE(reg);
3814adf43b0SKeith M Wesolowski 	const uint32_t addr_off = SMN_REG_ADDR_OFF(reg);
3824adf43b0SKeith M Wesolowski 
3834adf43b0SKeith M Wesolowski 	VERIFY(SMN_REG_IS_NATURALLY_ALIGNED(reg));
3844adf43b0SKeith M Wesolowski 	VERIFY(SMN_REG_VALUE_FITS(reg, val));
385f198607dSRobert Mustacchi 	VERIFY(MUTEX_HELD(&azn->azn_mutex));
3864adf43b0SKeith M Wesolowski 	amdzen_stub_put32(df->adf_nb, AMDZEN_NB_SMN_ADDR, base_addr);
3874adf43b0SKeith M Wesolowski 
3884adf43b0SKeith M Wesolowski 	switch (SMN_REG_SIZE(reg)) {
3894adf43b0SKeith M Wesolowski 	case 1:
3904adf43b0SKeith M Wesolowski 		amdzen_stub_put8(df->adf_nb, AMDZEN_NB_SMN_DATA + addr_off,
3914adf43b0SKeith M Wesolowski 		    (uint8_t)val);
3924adf43b0SKeith M Wesolowski 		break;
3934adf43b0SKeith M Wesolowski 	case 2:
3944adf43b0SKeith M Wesolowski 		amdzen_stub_put16(df->adf_nb, AMDZEN_NB_SMN_DATA + addr_off,
3954adf43b0SKeith M Wesolowski 		    (uint16_t)val);
3964adf43b0SKeith M Wesolowski 		break;
3974adf43b0SKeith M Wesolowski 	case 4:
398f198607dSRobert Mustacchi 		amdzen_stub_put32(df->adf_nb, AMDZEN_NB_SMN_DATA, val);
3994adf43b0SKeith M Wesolowski 		break;
4004adf43b0SKeith M Wesolowski 	default:
4014adf43b0SKeith M Wesolowski 		panic("unreachable invalid SMN register size %u",
4024adf43b0SKeith M Wesolowski 		    SMN_REG_SIZE(reg));
4034adf43b0SKeith M Wesolowski 	}
404f198607dSRobert Mustacchi }
405f198607dSRobert Mustacchi 
406*019df03dSRobert Mustacchi /*
407*019df03dSRobert Mustacchi  * This is an unfortunate necessity due to the evolution of the CCM DF values.
408*019df03dSRobert Mustacchi  */
409*019df03dSRobert Mustacchi static inline boolean_t
amdzen_df_at_least(const amdzen_df_t * df,uint8_t major,uint8_t minor)410*019df03dSRobert Mustacchi amdzen_df_at_least(const amdzen_df_t *df, uint8_t major, uint8_t minor)
411*019df03dSRobert Mustacchi {
412*019df03dSRobert Mustacchi 	return (df->adf_major > major || (df->adf_major == major &&
413*019df03dSRobert Mustacchi 	    df->adf_minor >= minor));
414*019df03dSRobert Mustacchi }
415*019df03dSRobert Mustacchi 
416047043c2SRobert Mustacchi static amdzen_df_t *
amdzen_df_find(amdzen_t * azn,uint_t dfno)417047043c2SRobert Mustacchi amdzen_df_find(amdzen_t *azn, uint_t dfno)
418047043c2SRobert Mustacchi {
419047043c2SRobert Mustacchi 	uint_t i;
420047043c2SRobert Mustacchi 
421047043c2SRobert Mustacchi 	ASSERT(MUTEX_HELD(&azn->azn_mutex));
422047043c2SRobert Mustacchi 	if (dfno >= azn->azn_ndfs) {
423047043c2SRobert Mustacchi 		return (NULL);
424047043c2SRobert Mustacchi 	}
425047043c2SRobert Mustacchi 
426047043c2SRobert Mustacchi 	for (i = 0; i < azn->azn_ndfs; i++) {
427047043c2SRobert Mustacchi 		amdzen_df_t *df = &azn->azn_dfs[i];
428047043c2SRobert Mustacchi 		if ((df->adf_flags & AMDZEN_DF_F_VALID) == 0) {
429047043c2SRobert Mustacchi 			continue;
430047043c2SRobert Mustacchi 		}
431047043c2SRobert Mustacchi 
432047043c2SRobert Mustacchi 		if (dfno == 0) {
433047043c2SRobert Mustacchi 			return (df);
434047043c2SRobert Mustacchi 		}
435047043c2SRobert Mustacchi 		dfno--;
436047043c2SRobert Mustacchi 	}
437047043c2SRobert Mustacchi 
438047043c2SRobert Mustacchi 	return (NULL);
439047043c2SRobert Mustacchi }
440047043c2SRobert Mustacchi 
441dd23d762SRobert Mustacchi static amdzen_df_ent_t *
amdzen_df_ent_find_by_instid(amdzen_df_t * df,uint8_t instid)442dd23d762SRobert Mustacchi amdzen_df_ent_find_by_instid(amdzen_df_t *df, uint8_t instid)
443dd23d762SRobert Mustacchi {
444dd23d762SRobert Mustacchi 	for (uint_t i = 0; i < df->adf_nents; i++) {
445dd23d762SRobert Mustacchi 		amdzen_df_ent_t *ent = &df->adf_ents[i];
446dd23d762SRobert Mustacchi 
447dd23d762SRobert Mustacchi 		if ((ent->adfe_flags & AMDZEN_DFE_F_ENABLED) == 0) {
448dd23d762SRobert Mustacchi 			continue;
449dd23d762SRobert Mustacchi 		}
450dd23d762SRobert Mustacchi 
451dd23d762SRobert Mustacchi 		if (ent->adfe_inst_id == instid) {
452dd23d762SRobert Mustacchi 			return (ent);
453dd23d762SRobert Mustacchi 		}
454dd23d762SRobert Mustacchi 	}
455dd23d762SRobert Mustacchi 
456dd23d762SRobert Mustacchi 	return (NULL);
457dd23d762SRobert Mustacchi }
458dd23d762SRobert Mustacchi 
459047043c2SRobert Mustacchi /*
460047043c2SRobert Mustacchi  * Client functions that are used by nexus children.
461047043c2SRobert Mustacchi  */
462047043c2SRobert Mustacchi int
amdzen_c_smn_read(uint_t dfno,const smn_reg_t reg,uint32_t * valp)4634adf43b0SKeith M Wesolowski amdzen_c_smn_read(uint_t dfno, const smn_reg_t reg, uint32_t *valp)
464047043c2SRobert Mustacchi {
465047043c2SRobert Mustacchi 	amdzen_df_t *df;
466047043c2SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
467047043c2SRobert Mustacchi 
4684adf43b0SKeith M Wesolowski 	if (!SMN_REG_SIZE_IS_VALID(reg))
4694adf43b0SKeith M Wesolowski 		return (EINVAL);
4704adf43b0SKeith M Wesolowski 	if (!SMN_REG_IS_NATURALLY_ALIGNED(reg))
4714adf43b0SKeith M Wesolowski 		return (EINVAL);
4724adf43b0SKeith M Wesolowski 
473047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
474047043c2SRobert Mustacchi 	df = amdzen_df_find(azn, dfno);
475047043c2SRobert Mustacchi 	if (df == NULL) {
476047043c2SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
477047043c2SRobert Mustacchi 		return (ENOENT);
478047043c2SRobert Mustacchi 	}
479047043c2SRobert Mustacchi 
480047043c2SRobert Mustacchi 	if ((df->adf_flags & AMDZEN_DF_F_FOUND_NB) == 0) {
481047043c2SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
482047043c2SRobert Mustacchi 		return (ENXIO);
483047043c2SRobert Mustacchi 	}
484047043c2SRobert Mustacchi 
4854adf43b0SKeith M Wesolowski 	*valp = amdzen_smn_read(azn, df, reg);
486047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
487047043c2SRobert Mustacchi 	return (0);
488047043c2SRobert Mustacchi }
489047043c2SRobert Mustacchi 
490f198607dSRobert Mustacchi int
amdzen_c_smn_write(uint_t dfno,const smn_reg_t reg,const uint32_t val)4914adf43b0SKeith M Wesolowski amdzen_c_smn_write(uint_t dfno, const smn_reg_t reg, const uint32_t val)
492f198607dSRobert Mustacchi {
493f198607dSRobert Mustacchi 	amdzen_df_t *df;
494f198607dSRobert Mustacchi 	amdzen_t *azn = amdzen_data;
495f198607dSRobert Mustacchi 
4964adf43b0SKeith M Wesolowski 	if (!SMN_REG_SIZE_IS_VALID(reg))
4974adf43b0SKeith M Wesolowski 		return (EINVAL);
4984adf43b0SKeith M Wesolowski 	if (!SMN_REG_IS_NATURALLY_ALIGNED(reg))
4994adf43b0SKeith M Wesolowski 		return (EINVAL);
5004adf43b0SKeith M Wesolowski 	if (!SMN_REG_VALUE_FITS(reg, val))
5014adf43b0SKeith M Wesolowski 		return (EOVERFLOW);
5024adf43b0SKeith M Wesolowski 
503f198607dSRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
504f198607dSRobert Mustacchi 	df = amdzen_df_find(azn, dfno);
505f198607dSRobert Mustacchi 	if (df == NULL) {
506f198607dSRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
507f198607dSRobert Mustacchi 		return (ENOENT);
508f198607dSRobert Mustacchi 	}
509f198607dSRobert Mustacchi 
510f198607dSRobert Mustacchi 	if ((df->adf_flags & AMDZEN_DF_F_FOUND_NB) == 0) {
511f198607dSRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
512f198607dSRobert Mustacchi 		return (ENXIO);
513f198607dSRobert Mustacchi 	}
514f198607dSRobert Mustacchi 
5154adf43b0SKeith M Wesolowski 	amdzen_smn_write(azn, df, reg, val);
516f198607dSRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
517f198607dSRobert Mustacchi 	return (0);
518f198607dSRobert Mustacchi }
519f198607dSRobert Mustacchi 
520047043c2SRobert Mustacchi uint_t
amdzen_c_df_count(void)521047043c2SRobert Mustacchi amdzen_c_df_count(void)
522047043c2SRobert Mustacchi {
523047043c2SRobert Mustacchi 	uint_t ret;
524047043c2SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
525047043c2SRobert Mustacchi 
526047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
527047043c2SRobert Mustacchi 	ret = azn->azn_ndfs;
528047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
529047043c2SRobert Mustacchi 	return (ret);
530047043c2SRobert Mustacchi }
531047043c2SRobert Mustacchi 
53271815ce7SRobert Mustacchi df_rev_t
amdzen_c_df_rev(void)53371815ce7SRobert Mustacchi amdzen_c_df_rev(void)
53471815ce7SRobert Mustacchi {
53571815ce7SRobert Mustacchi 	amdzen_df_t *df;
53671815ce7SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
53771815ce7SRobert Mustacchi 	df_rev_t rev;
53871815ce7SRobert Mustacchi 
53971815ce7SRobert Mustacchi 	/*
54071815ce7SRobert Mustacchi 	 * Always use the first DF instance to determine what we're using. Our
54171815ce7SRobert Mustacchi 	 * current assumption, which seems to generally be true, is that the
54271815ce7SRobert Mustacchi 	 * given DF revisions are the same in a given system when the DFs are
54371815ce7SRobert Mustacchi 	 * directly connected.
54471815ce7SRobert Mustacchi 	 */
54571815ce7SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
54671815ce7SRobert Mustacchi 	df = amdzen_df_find(azn, 0);
54771815ce7SRobert Mustacchi 	if (df == NULL) {
54871815ce7SRobert Mustacchi 		rev = DF_REV_UNKNOWN;
54971815ce7SRobert Mustacchi 	} else {
55071815ce7SRobert Mustacchi 		rev = df->adf_rev;
55171815ce7SRobert Mustacchi 	}
55271815ce7SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
55371815ce7SRobert Mustacchi 
55471815ce7SRobert Mustacchi 	return (rev);
55571815ce7SRobert Mustacchi }
55671815ce7SRobert Mustacchi 
557549e0fd3SRobert Mustacchi int
amdzen_c_df_read32(uint_t dfno,uint8_t inst,const df_reg_def_t def,uint32_t * valp)55871815ce7SRobert Mustacchi amdzen_c_df_read32(uint_t dfno, uint8_t inst, const df_reg_def_t def,
55971815ce7SRobert Mustacchi     uint32_t *valp)
560549e0fd3SRobert Mustacchi {
561549e0fd3SRobert Mustacchi 	amdzen_df_t *df;
562549e0fd3SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
563549e0fd3SRobert Mustacchi 
564549e0fd3SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
565549e0fd3SRobert Mustacchi 	df = amdzen_df_find(azn, dfno);
566549e0fd3SRobert Mustacchi 	if (df == NULL) {
567549e0fd3SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
568549e0fd3SRobert Mustacchi 		return (ENOENT);
569549e0fd3SRobert Mustacchi 	}
570549e0fd3SRobert Mustacchi 
57109dcaaa3SRobert Mustacchi 	if (df->adf_rev == DF_REV_UNKNOWN) {
57209dcaaa3SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
57309dcaaa3SRobert Mustacchi 		return (ENOTSUP);
57409dcaaa3SRobert Mustacchi 	}
57509dcaaa3SRobert Mustacchi 
57671815ce7SRobert Mustacchi 	*valp = amdzen_df_read_regdef(azn, df, def, inst, B_FALSE);
577549e0fd3SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
578549e0fd3SRobert Mustacchi 
579549e0fd3SRobert Mustacchi 	return (0);
580549e0fd3SRobert Mustacchi }
581549e0fd3SRobert Mustacchi 
582549e0fd3SRobert Mustacchi int
amdzen_c_df_read64(uint_t dfno,uint8_t inst,const df_reg_def_t def,uint64_t * valp)58371815ce7SRobert Mustacchi amdzen_c_df_read64(uint_t dfno, uint8_t inst, const df_reg_def_t def,
58471815ce7SRobert Mustacchi     uint64_t *valp)
585549e0fd3SRobert Mustacchi {
586549e0fd3SRobert Mustacchi 	amdzen_df_t *df;
587549e0fd3SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
588549e0fd3SRobert Mustacchi 
589549e0fd3SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
590549e0fd3SRobert Mustacchi 	df = amdzen_df_find(azn, dfno);
591549e0fd3SRobert Mustacchi 	if (df == NULL) {
592549e0fd3SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
593549e0fd3SRobert Mustacchi 		return (ENOENT);
594549e0fd3SRobert Mustacchi 	}
595549e0fd3SRobert Mustacchi 
59609dcaaa3SRobert Mustacchi 	if (df->adf_rev == DF_REV_UNKNOWN) {
59709dcaaa3SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
59809dcaaa3SRobert Mustacchi 		return (ENOTSUP);
59909dcaaa3SRobert Mustacchi 	}
60009dcaaa3SRobert Mustacchi 
60171815ce7SRobert Mustacchi 	*valp = amdzen_df_read_regdef(azn, df, def, inst, B_TRUE);
602549e0fd3SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
603549e0fd3SRobert Mustacchi 
604549e0fd3SRobert Mustacchi 	return (0);
605549e0fd3SRobert Mustacchi }
606047043c2SRobert Mustacchi 
60771815ce7SRobert Mustacchi int
amdzen_c_df_iter(uint_t dfno,zen_df_type_t type,amdzen_c_iter_f func,void * arg)60871815ce7SRobert Mustacchi amdzen_c_df_iter(uint_t dfno, zen_df_type_t type, amdzen_c_iter_f func,
60971815ce7SRobert Mustacchi     void *arg)
61071815ce7SRobert Mustacchi {
61171815ce7SRobert Mustacchi 	amdzen_df_t *df;
61271815ce7SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
61371815ce7SRobert Mustacchi 	df_type_t df_type;
61471815ce7SRobert Mustacchi 	uint8_t df_subtype;
61571815ce7SRobert Mustacchi 
61671815ce7SRobert Mustacchi 	/*
61771815ce7SRobert Mustacchi 	 * Unlike other calls here, we hold our lock only to find the DF here.
61871815ce7SRobert Mustacchi 	 * The main reason for this is the nature of the callback function.
61971815ce7SRobert Mustacchi 	 * Folks are iterating over instances so they can call back into us. If
62071815ce7SRobert Mustacchi 	 * you look at the locking statement, the thing that is most volatile
62171815ce7SRobert Mustacchi 	 * right here and what we need to protect is the DF itself and
62271815ce7SRobert Mustacchi 	 * subsequent register accesses to it. The actual data about which
62371815ce7SRobert Mustacchi 	 * entities exist is static and so once we have found a DF we should
62471815ce7SRobert Mustacchi 	 * hopefully be in good shape as they only come, but don't go.
62571815ce7SRobert Mustacchi 	 */
62671815ce7SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
62771815ce7SRobert Mustacchi 	df = amdzen_df_find(azn, dfno);
62871815ce7SRobert Mustacchi 	if (df == NULL) {
62971815ce7SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
63071815ce7SRobert Mustacchi 		return (ENOENT);
63171815ce7SRobert Mustacchi 	}
63271815ce7SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
63371815ce7SRobert Mustacchi 
63471815ce7SRobert Mustacchi 	switch (type) {
63571815ce7SRobert Mustacchi 	case ZEN_DF_TYPE_CS_UMC:
63671815ce7SRobert Mustacchi 		df_type = DF_TYPE_CS;
63771815ce7SRobert Mustacchi 		/*
63871815ce7SRobert Mustacchi 		 * In the original Zeppelin DFv2 die there was no subtype field
63971815ce7SRobert Mustacchi 		 * used for the CS. The UMC is the only type and has a subtype
64071815ce7SRobert Mustacchi 		 * of zero.
64171815ce7SRobert Mustacchi 		 */
64271815ce7SRobert Mustacchi 		if (df->adf_rev != DF_REV_2) {
64371815ce7SRobert Mustacchi 			df_subtype = DF_CS_SUBTYPE_UMC;
64471815ce7SRobert Mustacchi 		} else {
64571815ce7SRobert Mustacchi 			df_subtype = 0;
64671815ce7SRobert Mustacchi 		}
64771815ce7SRobert Mustacchi 		break;
64871815ce7SRobert Mustacchi 	case ZEN_DF_TYPE_CCM_CPU:
649f8e9c7b3SRobert Mustacchi 		df_type = DF_TYPE_CCM;
650*019df03dSRobert Mustacchi 
651*019df03dSRobert Mustacchi 		if (df->adf_rev >= DF_REV_4 && amdzen_df_at_least(df, 4, 1)) {
652*019df03dSRobert Mustacchi 			df_subtype = DF_CCM_SUBTYPE_CPU_V4P1;
653*019df03dSRobert Mustacchi 		} else {
654*019df03dSRobert Mustacchi 			df_subtype = DF_CCM_SUBTYPE_CPU_V2;
655*019df03dSRobert Mustacchi 		}
65671815ce7SRobert Mustacchi 		break;
65771815ce7SRobert Mustacchi 	default:
65871815ce7SRobert Mustacchi 		return (EINVAL);
65971815ce7SRobert Mustacchi 	}
66071815ce7SRobert Mustacchi 
66171815ce7SRobert Mustacchi 	for (uint_t i = 0; i < df->adf_nents; i++) {
66271815ce7SRobert Mustacchi 		amdzen_df_ent_t *ent = &df->adf_ents[i];
66371815ce7SRobert Mustacchi 
66471815ce7SRobert Mustacchi 		/*
66571815ce7SRobert Mustacchi 		 * Some DF components are not considered enabled and therefore
66671815ce7SRobert Mustacchi 		 * will end up having bogus values in their ID fields. If we do
66771815ce7SRobert Mustacchi 		 * not have an enable flag set, we must skip this node.
66871815ce7SRobert Mustacchi 		 */
66971815ce7SRobert Mustacchi 		if ((ent->adfe_flags & AMDZEN_DFE_F_ENABLED) == 0)
67071815ce7SRobert Mustacchi 			continue;
67171815ce7SRobert Mustacchi 
67271815ce7SRobert Mustacchi 		if (ent->adfe_type == df_type &&
67371815ce7SRobert Mustacchi 		    ent->adfe_subtype == df_subtype) {
67471815ce7SRobert Mustacchi 			int ret = func(dfno, ent->adfe_fabric_id,
67571815ce7SRobert Mustacchi 			    ent->adfe_inst_id, arg);
67671815ce7SRobert Mustacchi 			if (ret != 0) {
67771815ce7SRobert Mustacchi 				return (ret);
67871815ce7SRobert Mustacchi 			}
67971815ce7SRobert Mustacchi 		}
68071815ce7SRobert Mustacchi 	}
68171815ce7SRobert Mustacchi 
68271815ce7SRobert Mustacchi 	return (0);
68371815ce7SRobert Mustacchi }
68471815ce7SRobert Mustacchi 
68571815ce7SRobert Mustacchi int
amdzen_c_df_fabric_decomp(df_fabric_decomp_t * decomp)68671815ce7SRobert Mustacchi amdzen_c_df_fabric_decomp(df_fabric_decomp_t *decomp)
68771815ce7SRobert Mustacchi {
68871815ce7SRobert Mustacchi 	const amdzen_df_t *df;
68971815ce7SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
69071815ce7SRobert Mustacchi 
69171815ce7SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
69271815ce7SRobert Mustacchi 	df = amdzen_df_find(azn, 0);
69371815ce7SRobert Mustacchi 	if (df == NULL) {
69471815ce7SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
69571815ce7SRobert Mustacchi 		return (ENOENT);
69671815ce7SRobert Mustacchi 	}
69771815ce7SRobert Mustacchi 
69871815ce7SRobert Mustacchi 	*decomp = df->adf_decomp;
69971815ce7SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
70071815ce7SRobert Mustacchi 	return (0);
70171815ce7SRobert Mustacchi }
70271815ce7SRobert Mustacchi 
703047043c2SRobert Mustacchi static boolean_t
amdzen_create_child(amdzen_t * azn,const amdzen_child_data_t * acd)704047043c2SRobert Mustacchi amdzen_create_child(amdzen_t *azn, const amdzen_child_data_t *acd)
705047043c2SRobert Mustacchi {
706047043c2SRobert Mustacchi 	int ret;
707047043c2SRobert Mustacchi 	dev_info_t *child;
708047043c2SRobert Mustacchi 
709047043c2SRobert Mustacchi 	if (ndi_devi_alloc(azn->azn_dip, acd->acd_name,
710047043c2SRobert Mustacchi 	    (pnode_t)DEVI_SID_NODEID, &child) != NDI_SUCCESS) {
711549e0fd3SRobert Mustacchi 		dev_err(azn->azn_dip, CE_WARN, "!failed to allocate child "
712047043c2SRobert Mustacchi 		    "dip for %s", acd->acd_name);
713047043c2SRobert Mustacchi 		return (B_FALSE);
714047043c2SRobert Mustacchi 	}
715047043c2SRobert Mustacchi 
716047043c2SRobert Mustacchi 	ddi_set_parent_data(child, (void *)acd);
717047043c2SRobert Mustacchi 	if ((ret = ndi_devi_online(child, 0)) != NDI_SUCCESS) {
7181e0464b8SRobert Mustacchi 		if (acd->acd_warn) {
7191e0464b8SRobert Mustacchi 			dev_err(azn->azn_dip, CE_WARN, "!failed to online "
7201e0464b8SRobert Mustacchi 			    "child dip %s: %d", acd->acd_name, ret);
7211e0464b8SRobert Mustacchi 		}
722047043c2SRobert Mustacchi 		return (B_FALSE);
723047043c2SRobert Mustacchi 	}
724047043c2SRobert Mustacchi 
725047043c2SRobert Mustacchi 	return (B_TRUE);
726047043c2SRobert Mustacchi }
727047043c2SRobert Mustacchi 
728047043c2SRobert Mustacchi static boolean_t
amdzen_map_dfs(amdzen_t * azn)729047043c2SRobert Mustacchi amdzen_map_dfs(amdzen_t *azn)
730047043c2SRobert Mustacchi {
731047043c2SRobert Mustacchi 	amdzen_stub_t *stub;
732047043c2SRobert Mustacchi 
733047043c2SRobert Mustacchi 	ASSERT(MUTEX_HELD(&azn->azn_mutex));
734047043c2SRobert Mustacchi 
735047043c2SRobert Mustacchi 	for (stub = list_head(&azn->azn_df_stubs); stub != NULL;
736047043c2SRobert Mustacchi 	    stub = list_next(&azn->azn_df_stubs, stub)) {
737047043c2SRobert Mustacchi 		amdzen_df_t *df;
738047043c2SRobert Mustacchi 		uint_t dfno;
739047043c2SRobert Mustacchi 
740047043c2SRobert Mustacchi 		dfno = stub->azns_dev - AMDZEN_DF_FIRST_DEVICE;
741047043c2SRobert Mustacchi 		if (dfno > AMDZEN_MAX_DFS) {
742047043c2SRobert Mustacchi 			dev_err(stub->azns_dip, CE_WARN, "encountered df "
743047043c2SRobert Mustacchi 			    "device with illegal DF PCI b/d/f: 0x%x/%x/%x",
744047043c2SRobert Mustacchi 			    stub->azns_bus, stub->azns_dev, stub->azns_func);
745047043c2SRobert Mustacchi 			goto err;
746047043c2SRobert Mustacchi 		}
747047043c2SRobert Mustacchi 
748047043c2SRobert Mustacchi 		df = &azn->azn_dfs[dfno];
749047043c2SRobert Mustacchi 
750047043c2SRobert Mustacchi 		if (stub->azns_func >= AMDZEN_MAX_DF_FUNCS) {
751047043c2SRobert Mustacchi 			dev_err(stub->azns_dip, CE_WARN, "encountered df "
752047043c2SRobert Mustacchi 			    "device with illegal DF PCI b/d/f: 0x%x/%x/%x",
753047043c2SRobert Mustacchi 			    stub->azns_bus, stub->azns_dev, stub->azns_func);
754047043c2SRobert Mustacchi 			goto err;
755047043c2SRobert Mustacchi 		}
756047043c2SRobert Mustacchi 
757047043c2SRobert Mustacchi 		if (df->adf_funcs[stub->azns_func] != NULL) {
758047043c2SRobert Mustacchi 			dev_err(stub->azns_dip, CE_WARN, "encountered "
759047043c2SRobert Mustacchi 			    "duplicate df device with DF PCI b/d/f: 0x%x/%x/%x",
760047043c2SRobert Mustacchi 			    stub->azns_bus, stub->azns_dev, stub->azns_func);
761047043c2SRobert Mustacchi 			goto err;
762047043c2SRobert Mustacchi 		}
763047043c2SRobert Mustacchi 		df->adf_funcs[stub->azns_func] = stub;
764047043c2SRobert Mustacchi 	}
765047043c2SRobert Mustacchi 
766047043c2SRobert Mustacchi 	return (B_TRUE);
767047043c2SRobert Mustacchi 
768047043c2SRobert Mustacchi err:
769047043c2SRobert Mustacchi 	azn->azn_flags |= AMDZEN_F_DEVICE_ERROR;
770047043c2SRobert Mustacchi 	return (B_FALSE);
771047043c2SRobert Mustacchi }
772047043c2SRobert Mustacchi 
773047043c2SRobert Mustacchi static boolean_t
amdzen_check_dfs(amdzen_t * azn)774047043c2SRobert Mustacchi amdzen_check_dfs(amdzen_t *azn)
775047043c2SRobert Mustacchi {
776047043c2SRobert Mustacchi 	uint_t i;
777047043c2SRobert Mustacchi 	boolean_t ret = B_TRUE;
778047043c2SRobert Mustacchi 
779047043c2SRobert Mustacchi 	for (i = 0; i < AMDZEN_MAX_DFS; i++) {
780047043c2SRobert Mustacchi 		amdzen_df_t *df = &azn->azn_dfs[i];
781047043c2SRobert Mustacchi 		uint_t count = 0;
782047043c2SRobert Mustacchi 
783047043c2SRobert Mustacchi 		/*
784047043c2SRobert Mustacchi 		 * We require all platforms to have DFs functions 0-6. Not all
785047043c2SRobert Mustacchi 		 * platforms have DF function 7.
786047043c2SRobert Mustacchi 		 */
787047043c2SRobert Mustacchi 		for (uint_t func = 0; func < AMDZEN_MAX_DF_FUNCS - 1; func++) {
788047043c2SRobert Mustacchi 			if (df->adf_funcs[func] != NULL) {
789047043c2SRobert Mustacchi 				count++;
790047043c2SRobert Mustacchi 			}
791047043c2SRobert Mustacchi 		}
792047043c2SRobert Mustacchi 
793047043c2SRobert Mustacchi 		if (count == 0)
794047043c2SRobert Mustacchi 			continue;
795047043c2SRobert Mustacchi 
796047043c2SRobert Mustacchi 		if (count != 7) {
797047043c2SRobert Mustacchi 			ret = B_FALSE;
798047043c2SRobert Mustacchi 			dev_err(azn->azn_dip, CE_WARN, "df %u devices "
799047043c2SRobert Mustacchi 			    "incomplete", i);
800047043c2SRobert Mustacchi 		} else {
801047043c2SRobert Mustacchi 			df->adf_flags |= AMDZEN_DF_F_VALID;
802047043c2SRobert Mustacchi 			azn->azn_ndfs++;
803047043c2SRobert Mustacchi 		}
804047043c2SRobert Mustacchi 	}
805047043c2SRobert Mustacchi 
806047043c2SRobert Mustacchi 	return (ret);
807047043c2SRobert Mustacchi }
808047043c2SRobert Mustacchi 
809047043c2SRobert Mustacchi static const uint8_t amdzen_df_rome_ids[0x2b] = {
810047043c2SRobert Mustacchi 	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20, 21, 22, 23,
811047043c2SRobert Mustacchi 	24, 25, 26, 27, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
812047043c2SRobert Mustacchi 	44, 45, 46, 47, 48
813047043c2SRobert Mustacchi };
814047043c2SRobert Mustacchi 
815047043c2SRobert Mustacchi /*
816e9abe9d6SRobert Mustacchi  * Check the first df entry to see if it belongs to Rome or Milan. If so, then
817e9abe9d6SRobert Mustacchi  * it uses the disjoint ID space.
818e9abe9d6SRobert Mustacchi  */
819e9abe9d6SRobert Mustacchi static boolean_t
amdzen_is_rome_style(uint_t id)820e9abe9d6SRobert Mustacchi amdzen_is_rome_style(uint_t id)
821e9abe9d6SRobert Mustacchi {
822e9abe9d6SRobert Mustacchi 	return (id == 0x1490 || id == 0x1650);
823e9abe9d6SRobert Mustacchi }
824e9abe9d6SRobert Mustacchi 
825e9abe9d6SRobert Mustacchi /*
826*019df03dSRobert Mustacchi  * Deal with the differences between between how a CCM subtype is indicated
827*019df03dSRobert Mustacchi  * across CPU generations.
828*019df03dSRobert Mustacchi  */
829*019df03dSRobert Mustacchi static boolean_t
amdzen_dfe_is_ccm(const amdzen_df_t * df,const amdzen_df_ent_t * ent)830*019df03dSRobert Mustacchi amdzen_dfe_is_ccm(const amdzen_df_t *df, const amdzen_df_ent_t *ent)
831*019df03dSRobert Mustacchi {
832*019df03dSRobert Mustacchi 	if (ent->adfe_type != DF_TYPE_CCM) {
833*019df03dSRobert Mustacchi 		return (B_FALSE);
834*019df03dSRobert Mustacchi 	}
835*019df03dSRobert Mustacchi 
836*019df03dSRobert Mustacchi 	if (df->adf_rev >= DF_REV_4 && amdzen_df_at_least(df, 4, 1)) {
837*019df03dSRobert Mustacchi 		return (ent->adfe_subtype == DF_CCM_SUBTYPE_CPU_V4P1);
838*019df03dSRobert Mustacchi 	} else {
839*019df03dSRobert Mustacchi 		return (ent->adfe_subtype == DF_CCM_SUBTYPE_CPU_V2);
840*019df03dSRobert Mustacchi 	}
841*019df03dSRobert Mustacchi }
842*019df03dSRobert Mustacchi 
843*019df03dSRobert Mustacchi /*
84471815ce7SRobert Mustacchi  * To be able to do most other things we want to do, we must first determine
84571815ce7SRobert Mustacchi  * what revision of the DF (data fabric) that we're using.
84671815ce7SRobert Mustacchi  *
84771815ce7SRobert Mustacchi  * Snapshot the df version. This was added explicitly in DFv4.0, around the Zen
84871815ce7SRobert Mustacchi  * 4 timeframe and allows us to tell apart different version of the DF register
84971815ce7SRobert Mustacchi  * set, most usefully when various subtypes were added.
85071815ce7SRobert Mustacchi  *
85171815ce7SRobert Mustacchi  * Older versions can theoretically be told apart based on usage of reserved
85271815ce7SRobert Mustacchi  * registers. We walk these in the following order, starting with the newest rev
85371815ce7SRobert Mustacchi  * and walking backwards to tell things apart:
85471815ce7SRobert Mustacchi  *
85571815ce7SRobert Mustacchi  *   o v3.5 -> Check function 1, register 0x150. This was reserved prior
85671815ce7SRobert Mustacchi  *             to this point. This is actually DF_FIDMASK0_V3P5. We are supposed
85771815ce7SRobert Mustacchi  *             to check bits [7:0].
85871815ce7SRobert Mustacchi  *
85971815ce7SRobert Mustacchi  *   o v3.0 -> Check function 1, register 0x208. The low byte (7:0) was
86071815ce7SRobert Mustacchi  *             changed to indicate a component mask. This is non-zero
86171815ce7SRobert Mustacchi  *             in the 3.0 generation. This is actually DF_FIDMASK_V2.
86271815ce7SRobert Mustacchi  *
86371815ce7SRobert Mustacchi  *   o v2.0 -> This is just the not that case. Presumably v1 wasn't part
86471815ce7SRobert Mustacchi  *             of the Zen generation.
86571815ce7SRobert Mustacchi  *
86671815ce7SRobert Mustacchi  * Because we don't know what version we are yet, we do not use the normal
86771815ce7SRobert Mustacchi  * versioned register accesses which would check what DF version we are and
86871815ce7SRobert Mustacchi  * would want to use the normal indirect register accesses (which also require
86971815ce7SRobert Mustacchi  * us to know the version). We instead do direct broadcast reads.
87071815ce7SRobert Mustacchi  */
87171815ce7SRobert Mustacchi static void
amdzen_determine_df_vers(amdzen_t * azn,amdzen_df_t * df)87271815ce7SRobert Mustacchi amdzen_determine_df_vers(amdzen_t *azn, amdzen_df_t *df)
87371815ce7SRobert Mustacchi {
87471815ce7SRobert Mustacchi 	uint32_t val;
87571815ce7SRobert Mustacchi 	df_reg_def_t rd = DF_FBICNT;
87671815ce7SRobert Mustacchi 
87771815ce7SRobert Mustacchi 	val = amdzen_stub_get32(df->adf_funcs[rd.drd_func], rd.drd_reg);
87871815ce7SRobert Mustacchi 	df->adf_major = DF_FBICNT_V4_GET_MAJOR(val);
87971815ce7SRobert Mustacchi 	df->adf_minor = DF_FBICNT_V4_GET_MINOR(val);
88071815ce7SRobert Mustacchi 	if (df->adf_major == 0 && df->adf_minor == 0) {
88171815ce7SRobert Mustacchi 		rd = DF_FIDMASK0_V3P5;
88271815ce7SRobert Mustacchi 		val = amdzen_stub_get32(df->adf_funcs[rd.drd_func], rd.drd_reg);
88371815ce7SRobert Mustacchi 		if (bitx32(val, 7, 0) != 0) {
88471815ce7SRobert Mustacchi 			df->adf_major = 3;
88571815ce7SRobert Mustacchi 			df->adf_minor = 5;
88671815ce7SRobert Mustacchi 			df->adf_rev = DF_REV_3P5;
88771815ce7SRobert Mustacchi 		} else {
88871815ce7SRobert Mustacchi 			rd = DF_FIDMASK_V2;
88971815ce7SRobert Mustacchi 			val = amdzen_stub_get32(df->adf_funcs[rd.drd_func],
89071815ce7SRobert Mustacchi 			    rd.drd_reg);
89171815ce7SRobert Mustacchi 			if (bitx32(val, 7, 0) != 0) {
89271815ce7SRobert Mustacchi 				df->adf_major = 3;
89371815ce7SRobert Mustacchi 				df->adf_minor = 0;
89471815ce7SRobert Mustacchi 				df->adf_rev = DF_REV_3;
89571815ce7SRobert Mustacchi 			} else {
89671815ce7SRobert Mustacchi 				df->adf_major = 2;
89771815ce7SRobert Mustacchi 				df->adf_minor = 0;
89871815ce7SRobert Mustacchi 				df->adf_rev = DF_REV_2;
89971815ce7SRobert Mustacchi 			}
90071815ce7SRobert Mustacchi 		}
901*019df03dSRobert Mustacchi 	} else if (df->adf_major == 4 && df->adf_minor >= 2) {
902*019df03dSRobert Mustacchi 		/*
903*019df03dSRobert Mustacchi 		 * These are devices that have the newer memory layout that
904*019df03dSRobert Mustacchi 		 * moves the DF::DramBaseAddress to 0x200. Please see the df.h
905*019df03dSRobert Mustacchi 		 * theory statement for more information.
906*019df03dSRobert Mustacchi 		 */
907*019df03dSRobert Mustacchi 		df->adf_rev = DF_REV_4D2;
908*019df03dSRobert Mustacchi 	} else if (df->adf_major == 4) {
90971815ce7SRobert Mustacchi 		df->adf_rev = DF_REV_4;
91071815ce7SRobert Mustacchi 	} else {
91171815ce7SRobert Mustacchi 		df->adf_rev = DF_REV_UNKNOWN;
91271815ce7SRobert Mustacchi 	}
91371815ce7SRobert Mustacchi }
91471815ce7SRobert Mustacchi 
91571815ce7SRobert Mustacchi /*
91671815ce7SRobert Mustacchi  * All of the different versions of the DF have different ways of getting at and
91771815ce7SRobert Mustacchi  * answering the question of how do I break a fabric ID into a corresponding
91871815ce7SRobert Mustacchi  * socket, die, and component. Importantly the goal here is to obtain, cache,
91971815ce7SRobert Mustacchi  * and normalize:
92071815ce7SRobert Mustacchi  *
92171815ce7SRobert Mustacchi  *  o The DF System Configuration
92271815ce7SRobert Mustacchi  *  o The various Mask registers
92371815ce7SRobert Mustacchi  *  o The Node ID
92471815ce7SRobert Mustacchi  */
92571815ce7SRobert Mustacchi static void
amdzen_determine_fabric_decomp(amdzen_t * azn,amdzen_df_t * df)92671815ce7SRobert Mustacchi amdzen_determine_fabric_decomp(amdzen_t *azn, amdzen_df_t *df)
92771815ce7SRobert Mustacchi {
92871815ce7SRobert Mustacchi 	uint32_t mask;
92971815ce7SRobert Mustacchi 	df_fabric_decomp_t *decomp = &df->adf_decomp;
93071815ce7SRobert Mustacchi 
93171815ce7SRobert Mustacchi 	switch (df->adf_rev) {
93271815ce7SRobert Mustacchi 	case DF_REV_2:
93371815ce7SRobert Mustacchi 		df->adf_syscfg = amdzen_df_read32_bcast(azn, df, DF_SYSCFG_V2);
93471815ce7SRobert Mustacchi 		switch (DF_SYSCFG_V2_GET_MY_TYPE(df->adf_syscfg)) {
93571815ce7SRobert Mustacchi 		case DF_DIE_TYPE_CPU:
93671815ce7SRobert Mustacchi 			mask = amdzen_df_read32_bcast(azn, df,
93771815ce7SRobert Mustacchi 			    DF_DIEMASK_CPU_V2);
93871815ce7SRobert Mustacchi 			break;
93971815ce7SRobert Mustacchi 		case DF_DIE_TYPE_APU:
94071815ce7SRobert Mustacchi 			mask = amdzen_df_read32_bcast(azn, df,
94171815ce7SRobert Mustacchi 			    DF_DIEMASK_APU_V2);
94271815ce7SRobert Mustacchi 			break;
94371815ce7SRobert Mustacchi 		default:
94471815ce7SRobert Mustacchi 			panic("DF thinks we're not on a CPU!");
94571815ce7SRobert Mustacchi 		}
94671815ce7SRobert Mustacchi 		df->adf_mask0 = mask;
94771815ce7SRobert Mustacchi 
94871815ce7SRobert Mustacchi 		/*
94971815ce7SRobert Mustacchi 		 * DFv2 is a bit different in how the fabric mask register is
95071815ce7SRobert Mustacchi 		 * phrased. Logically a fabric ID is broken into something that
95171815ce7SRobert Mustacchi 		 * uniquely identifies a "node" (a particular die on a socket)
95271815ce7SRobert Mustacchi 		 * and something that identifies a "component", e.g. a memory
95371815ce7SRobert Mustacchi 		 * controller.
95471815ce7SRobert Mustacchi 		 *
95571815ce7SRobert Mustacchi 		 * Starting with DFv3, these registers logically called out how
95671815ce7SRobert Mustacchi 		 * to separate the fabric ID first into a node and a component.
95771815ce7SRobert Mustacchi 		 * Then the node was then broken down into a socket and die. In
95871815ce7SRobert Mustacchi 		 * DFv2, there is no separate mask and shift of a node. Instead
95971815ce7SRobert Mustacchi 		 * the socket and die are absolute offsets into the fabric ID
96071815ce7SRobert Mustacchi 		 * rather than relative offsets into the node ID. As such, when
96171815ce7SRobert Mustacchi 		 * we encounter DFv2, we fake up a node mask and shift and make
96271815ce7SRobert Mustacchi 		 * it look like DFv3+.
96371815ce7SRobert Mustacchi 		 */
96471815ce7SRobert Mustacchi 		decomp->dfd_node_mask = DF_DIEMASK_V2_GET_SOCK_MASK(mask) |
96571815ce7SRobert Mustacchi 		    DF_DIEMASK_V2_GET_DIE_MASK(mask);
96671815ce7SRobert Mustacchi 		decomp->dfd_node_shift = DF_DIEMASK_V2_GET_DIE_SHIFT(mask);
96771815ce7SRobert Mustacchi 		decomp->dfd_comp_mask = DF_DIEMASK_V2_GET_COMP_MASK(mask);
96871815ce7SRobert Mustacchi 		decomp->dfd_comp_shift = 0;
96971815ce7SRobert Mustacchi 
97071815ce7SRobert Mustacchi 		decomp->dfd_sock_mask = DF_DIEMASK_V2_GET_SOCK_MASK(mask) >>
97171815ce7SRobert Mustacchi 		    decomp->dfd_node_shift;
97271815ce7SRobert Mustacchi 		decomp->dfd_die_mask = DF_DIEMASK_V2_GET_DIE_MASK(mask) >>
97371815ce7SRobert Mustacchi 		    decomp->dfd_node_shift;
97471815ce7SRobert Mustacchi 		decomp->dfd_sock_shift = DF_DIEMASK_V2_GET_SOCK_SHIFT(mask) -
97571815ce7SRobert Mustacchi 		    decomp->dfd_node_shift;
97671815ce7SRobert Mustacchi 		decomp->dfd_die_shift = DF_DIEMASK_V2_GET_DIE_SHIFT(mask) -
97771815ce7SRobert Mustacchi 		    decomp->dfd_node_shift;
97871815ce7SRobert Mustacchi 		ASSERT3U(decomp->dfd_die_shift, ==, 0);
979dd23d762SRobert Mustacchi 
980dd23d762SRobert Mustacchi 		/*
981dd23d762SRobert Mustacchi 		 * There is no register in the actual data fabric with the node
982dd23d762SRobert Mustacchi 		 * ID in DFv2 that we have found. Instead we take the first
983dd23d762SRobert Mustacchi 		 * entity's fabric ID and transform it into the node id.
984dd23d762SRobert Mustacchi 		 */
985dd23d762SRobert Mustacchi 		df->adf_nodeid = (df->adf_ents[0].adfe_fabric_id &
986dd23d762SRobert Mustacchi 		    decomp->dfd_node_mask) >> decomp->dfd_node_shift;
98771815ce7SRobert Mustacchi 		break;
98871815ce7SRobert Mustacchi 	case DF_REV_3:
98971815ce7SRobert Mustacchi 		df->adf_syscfg = amdzen_df_read32_bcast(azn, df, DF_SYSCFG_V3);
99071815ce7SRobert Mustacchi 		df->adf_mask0 =  amdzen_df_read32_bcast(azn, df,
99171815ce7SRobert Mustacchi 		    DF_FIDMASK0_V3);
99271815ce7SRobert Mustacchi 		df->adf_mask1 =  amdzen_df_read32_bcast(azn, df,
99371815ce7SRobert Mustacchi 		    DF_FIDMASK1_V3);
99471815ce7SRobert Mustacchi 
99571815ce7SRobert Mustacchi 		decomp->dfd_sock_mask =
99671815ce7SRobert Mustacchi 		    DF_FIDMASK1_V3_GET_SOCK_MASK(df->adf_mask1);
99771815ce7SRobert Mustacchi 		decomp->dfd_sock_shift =
99871815ce7SRobert Mustacchi 		    DF_FIDMASK1_V3_GET_SOCK_SHIFT(df->adf_mask1);
99971815ce7SRobert Mustacchi 		decomp->dfd_die_mask =
100071815ce7SRobert Mustacchi 		    DF_FIDMASK1_V3_GET_DIE_MASK(df->adf_mask1);
100171815ce7SRobert Mustacchi 		decomp->dfd_die_shift = 0;
100271815ce7SRobert Mustacchi 		decomp->dfd_node_mask =
100371815ce7SRobert Mustacchi 		    DF_FIDMASK0_V3_GET_NODE_MASK(df->adf_mask0);
100471815ce7SRobert Mustacchi 		decomp->dfd_node_shift =
100571815ce7SRobert Mustacchi 		    DF_FIDMASK1_V3_GET_NODE_SHIFT(df->adf_mask1);
100671815ce7SRobert Mustacchi 		decomp->dfd_comp_mask =
100771815ce7SRobert Mustacchi 		    DF_FIDMASK0_V3_GET_COMP_MASK(df->adf_mask0);
100871815ce7SRobert Mustacchi 		decomp->dfd_comp_shift = 0;
1009dd23d762SRobert Mustacchi 
1010dd23d762SRobert Mustacchi 		df->adf_nodeid = DF_SYSCFG_V3_GET_NODE_ID(df->adf_syscfg);
101171815ce7SRobert Mustacchi 		break;
101271815ce7SRobert Mustacchi 	case DF_REV_3P5:
101371815ce7SRobert Mustacchi 		df->adf_syscfg = amdzen_df_read32_bcast(azn, df,
101471815ce7SRobert Mustacchi 		    DF_SYSCFG_V3P5);
101571815ce7SRobert Mustacchi 		df->adf_mask0 =  amdzen_df_read32_bcast(azn, df,
101671815ce7SRobert Mustacchi 		    DF_FIDMASK0_V3P5);
101771815ce7SRobert Mustacchi 		df->adf_mask1 =  amdzen_df_read32_bcast(azn, df,
101871815ce7SRobert Mustacchi 		    DF_FIDMASK1_V3P5);
101971815ce7SRobert Mustacchi 		df->adf_mask2 =  amdzen_df_read32_bcast(azn, df,
102071815ce7SRobert Mustacchi 		    DF_FIDMASK2_V3P5);
102171815ce7SRobert Mustacchi 
102271815ce7SRobert Mustacchi 		decomp->dfd_sock_mask =
102371815ce7SRobert Mustacchi 		    DF_FIDMASK2_V3P5_GET_SOCK_MASK(df->adf_mask2);
102471815ce7SRobert Mustacchi 		decomp->dfd_sock_shift =
102571815ce7SRobert Mustacchi 		    DF_FIDMASK1_V3P5_GET_SOCK_SHIFT(df->adf_mask1);
102671815ce7SRobert Mustacchi 		decomp->dfd_die_mask =
102771815ce7SRobert Mustacchi 		    DF_FIDMASK2_V3P5_GET_DIE_MASK(df->adf_mask2);
102871815ce7SRobert Mustacchi 		decomp->dfd_die_shift = 0;
102971815ce7SRobert Mustacchi 		decomp->dfd_node_mask =
103071815ce7SRobert Mustacchi 		    DF_FIDMASK0_V3P5_GET_NODE_MASK(df->adf_mask0);
103171815ce7SRobert Mustacchi 		decomp->dfd_node_shift =
103271815ce7SRobert Mustacchi 		    DF_FIDMASK1_V3P5_GET_NODE_SHIFT(df->adf_mask1);
103371815ce7SRobert Mustacchi 		decomp->dfd_comp_mask =
103471815ce7SRobert Mustacchi 		    DF_FIDMASK0_V3P5_GET_COMP_MASK(df->adf_mask0);
103571815ce7SRobert Mustacchi 		decomp->dfd_comp_shift = 0;
1036dd23d762SRobert Mustacchi 
1037dd23d762SRobert Mustacchi 		df->adf_nodeid = DF_SYSCFG_V3P5_GET_NODE_ID(df->adf_syscfg);
103871815ce7SRobert Mustacchi 		break;
103971815ce7SRobert Mustacchi 	case DF_REV_4:
1040*019df03dSRobert Mustacchi 	case DF_REV_4D2:
104171815ce7SRobert Mustacchi 		df->adf_syscfg = amdzen_df_read32_bcast(azn, df, DF_SYSCFG_V4);
104271815ce7SRobert Mustacchi 		df->adf_mask0 =  amdzen_df_read32_bcast(azn, df,
104371815ce7SRobert Mustacchi 		    DF_FIDMASK0_V4);
104471815ce7SRobert Mustacchi 		df->adf_mask1 =  amdzen_df_read32_bcast(azn, df,
104571815ce7SRobert Mustacchi 		    DF_FIDMASK1_V4);
104671815ce7SRobert Mustacchi 		df->adf_mask2 =  amdzen_df_read32_bcast(azn, df,
104771815ce7SRobert Mustacchi 		    DF_FIDMASK2_V4);
104871815ce7SRobert Mustacchi 
104971815ce7SRobert Mustacchi 		/*
105071815ce7SRobert Mustacchi 		 * The DFv4 registers are at a different location in the DF;
105171815ce7SRobert Mustacchi 		 * however, the actual layout of fields is the same as DFv3.5.
105271815ce7SRobert Mustacchi 		 * This is why you see V3P5 below.
105371815ce7SRobert Mustacchi 		 */
105471815ce7SRobert Mustacchi 		decomp->dfd_sock_mask =
105571815ce7SRobert Mustacchi 		    DF_FIDMASK2_V3P5_GET_SOCK_MASK(df->adf_mask2);
105671815ce7SRobert Mustacchi 		decomp->dfd_sock_shift =
105771815ce7SRobert Mustacchi 		    DF_FIDMASK1_V3P5_GET_SOCK_SHIFT(df->adf_mask1);
105871815ce7SRobert Mustacchi 		decomp->dfd_die_mask =
105971815ce7SRobert Mustacchi 		    DF_FIDMASK2_V3P5_GET_DIE_MASK(df->adf_mask2);
106071815ce7SRobert Mustacchi 		decomp->dfd_die_shift = 0;
106171815ce7SRobert Mustacchi 		decomp->dfd_node_mask =
106271815ce7SRobert Mustacchi 		    DF_FIDMASK0_V3P5_GET_NODE_MASK(df->adf_mask0);
106371815ce7SRobert Mustacchi 		decomp->dfd_node_shift =
106471815ce7SRobert Mustacchi 		    DF_FIDMASK1_V3P5_GET_NODE_SHIFT(df->adf_mask1);
106571815ce7SRobert Mustacchi 		decomp->dfd_comp_mask =
106671815ce7SRobert Mustacchi 		    DF_FIDMASK0_V3P5_GET_COMP_MASK(df->adf_mask0);
106771815ce7SRobert Mustacchi 		decomp->dfd_comp_shift = 0;
1068dd23d762SRobert Mustacchi 
1069dd23d762SRobert Mustacchi 		df->adf_nodeid = DF_SYSCFG_V4_GET_NODE_ID(df->adf_syscfg);
107071815ce7SRobert Mustacchi 		break;
107171815ce7SRobert Mustacchi 	default:
107271815ce7SRobert Mustacchi 		panic("encountered suspicious, previously rejected DF "
107371815ce7SRobert Mustacchi 		    "rev: 0x%x", df->adf_rev);
107471815ce7SRobert Mustacchi 	}
107571815ce7SRobert Mustacchi }
107671815ce7SRobert Mustacchi 
107771815ce7SRobert Mustacchi /*
1078dd23d762SRobert Mustacchi  * The purpose of this function is to map CCMs to the corresponding CCDs that
1079dd23d762SRobert Mustacchi  * exist. This is not an obvious thing as there is no direct mapping in the data
1080dd23d762SRobert Mustacchi  * fabric between these IDs.
1081dd23d762SRobert Mustacchi  *
1082dd23d762SRobert Mustacchi  * Prior to DFv4, a given CCM was only ever connected to at most one CCD.
1083dd23d762SRobert Mustacchi  * Starting in DFv4 a given CCM may have one or two SDP (scalable data ports)
1084dd23d762SRobert Mustacchi  * that connect to CCDs. These may be connected to the same CCD or a different
1085dd23d762SRobert Mustacchi  * one. When both ports are enabled we must check whether or not the port is
1086dd23d762SRobert Mustacchi  * considered to be in wide mode. When wide mode is enabled then the two ports
1087dd23d762SRobert Mustacchi  * are connected to a single CCD. If wide mode is disabled then the two ports
1088dd23d762SRobert Mustacchi  * are connected to separate CCDs.
1089dd23d762SRobert Mustacchi  *
1090dd23d762SRobert Mustacchi  * The physical number of a CCD, which is how we determine the SMN aperture to
1091dd23d762SRobert Mustacchi  * use, is based on the CCM ID. In most sockets we have seen up to a maximum of
1092dd23d762SRobert Mustacchi  * 8 CCMs. When a CCM is connected to more than one CCD we have determined based
1093dd23d762SRobert Mustacchi  * on some hints from AMD's ACPI information that the numbering is assumed to be
1094dd23d762SRobert Mustacchi  * that CCM's number plus the total number of CCMs.
1095dd23d762SRobert Mustacchi  *
1096dd23d762SRobert Mustacchi  * More concretely, the SP5 Genoa/Bergamo Zen 4 platform has 8 CCMs. When there
1097dd23d762SRobert Mustacchi  * are more than 8 CCDs installed then CCM 0 maps to CCDs 0 and 8. CCM 1 to CCDs
1098dd23d762SRobert Mustacchi  * 1 and 9, etc. CCMs 4-7 map 1:1 to CCDs 4-7. However, the placement of CCDs
1099dd23d762SRobert Mustacchi  * within the package has changed across generations.
1100dd23d762SRobert Mustacchi  *
1101dd23d762SRobert Mustacchi  * Notably in Rome and Milan (Zen 2/3) it appears that each quadrant had an
1102dd23d762SRobert Mustacchi  * increasing number of CCDs. So CCDs 0/1 were together, 2/3, 4/5, and 6/7. This
1103dd23d762SRobert Mustacchi  * meant that in cases where only a subset of CCDs were populated it'd forcibly
1104dd23d762SRobert Mustacchi  * disable the higher CCD in a group (but with DFv3 the CCM would still be
1105dd23d762SRobert Mustacchi  * enabled). So a 4 CCD config would generally enable CCDs 0, 2, 4, and 6 say.
1106dd23d762SRobert Mustacchi  * This was almost certainly done to balance the NUMA config.
1107dd23d762SRobert Mustacchi  *
1108dd23d762SRobert Mustacchi  * Instead, starting in Genoa (Zen 4) the CCMs are round-robined around the
1109dd23d762SRobert Mustacchi  * quadrants so CCMs (CCDs) 0 (0/8) and 4 (4) are together, 1 (1/9) and 5 (5),
1110dd23d762SRobert Mustacchi  * etc. This is also why we more often see disabled CCMs in Genoa, but not in
1111dd23d762SRobert Mustacchi  * Rome/Milan.
1112dd23d762SRobert Mustacchi  *
1113dd23d762SRobert Mustacchi  * When we're operating in wide mode and therefore both SDPs are connected to a
1114dd23d762SRobert Mustacchi  * single CCD, we've always found that the lower CCD index will be used by the
1115dd23d762SRobert Mustacchi  * system and the higher one is not considered present. Therefore, when
1116dd23d762SRobert Mustacchi  * operating in wide mode, we need to make sure that whenever we have a non-zero
1117dd23d762SRobert Mustacchi  * value for SDPs being connected that we rewrite this to only appear as a
1118dd23d762SRobert Mustacchi  * single CCD is present. It's conceivable (though hard to imagine) that we
1119dd23d762SRobert Mustacchi  * could get a value of 0b10 indicating that only the upper SDP link is active
1120dd23d762SRobert Mustacchi  * for some reason.
1121dd23d762SRobert Mustacchi  */
1122dd23d762SRobert Mustacchi static void
amdzen_setup_df_ccm(amdzen_t * azn,amdzen_df_t * df,amdzen_df_ent_t * dfe,uint32_t ccmno)1123dd23d762SRobert Mustacchi amdzen_setup_df_ccm(amdzen_t *azn, amdzen_df_t *df, amdzen_df_ent_t *dfe,
1124dd23d762SRobert Mustacchi     uint32_t ccmno)
1125dd23d762SRobert Mustacchi {
1126dd23d762SRobert Mustacchi 	amdzen_ccm_data_t *ccm = &dfe->adfe_data.aded_ccm;
1127dd23d762SRobert Mustacchi 	uint32_t ccd_en;
1128dd23d762SRobert Mustacchi 
1129dd23d762SRobert Mustacchi 	if (df->adf_rev >= DF_REV_4) {
1130dd23d762SRobert Mustacchi 		uint32_t val = amdzen_df_read32(azn, df, dfe->adfe_inst_id,
1131dd23d762SRobert Mustacchi 		    DF_CCD_EN_V4);
1132dd23d762SRobert Mustacchi 		ccd_en = DF_CCD_EN_V4_GET_CCD_EN(val);
1133dd23d762SRobert Mustacchi 
1134dd23d762SRobert Mustacchi 		val = amdzen_df_read32(azn, df, dfe->adfe_inst_id,
1135dd23d762SRobert Mustacchi 		    DF_CCMCFG4_V4);
1136dd23d762SRobert Mustacchi 		if (DF_CCMCFG4_V4_GET_WIDE_EN(val) != 0 && ccd_en != 0) {
1137dd23d762SRobert Mustacchi 			ccd_en = 0x1;
1138dd23d762SRobert Mustacchi 		}
1139dd23d762SRobert Mustacchi 	} else {
1140dd23d762SRobert Mustacchi 		ccd_en = 0x1;
1141dd23d762SRobert Mustacchi 	}
1142dd23d762SRobert Mustacchi 
1143dd23d762SRobert Mustacchi 	for (uint32_t i = 0; i < DF_MAX_CCDS_PER_CCM; i++) {
1144dd23d762SRobert Mustacchi 		ccm->acd_ccd_en[i] = (ccd_en & (1 << i)) != 0;
1145dd23d762SRobert Mustacchi 		if (ccm->acd_ccd_en[i] == 0)
1146dd23d762SRobert Mustacchi 			continue;
1147dd23d762SRobert Mustacchi 		ccm->acd_ccd_id[i] = ccmno + i * df->adf_nccm;
1148dd23d762SRobert Mustacchi 		ccm->acd_nccds++;
1149dd23d762SRobert Mustacchi 	}
1150dd23d762SRobert Mustacchi }
1151dd23d762SRobert Mustacchi 
1152dd23d762SRobert Mustacchi /*
1153047043c2SRobert Mustacchi  * Initialize our knowledge about a given series of nodes on the data fabric.
1154047043c2SRobert Mustacchi  */
1155047043c2SRobert Mustacchi static void
amdzen_setup_df(amdzen_t * azn,amdzen_df_t * df)1156047043c2SRobert Mustacchi amdzen_setup_df(amdzen_t *azn, amdzen_df_t *df)
1157047043c2SRobert Mustacchi {
1158047043c2SRobert Mustacchi 	uint_t i;
1159dd23d762SRobert Mustacchi 	uint32_t val, ccmno;
1160047043c2SRobert Mustacchi 
116171815ce7SRobert Mustacchi 	amdzen_determine_df_vers(azn, df);
116271815ce7SRobert Mustacchi 
116371815ce7SRobert Mustacchi 	switch (df->adf_rev) {
116471815ce7SRobert Mustacchi 	case DF_REV_2:
116571815ce7SRobert Mustacchi 	case DF_REV_3:
116671815ce7SRobert Mustacchi 	case DF_REV_3P5:
116771815ce7SRobert Mustacchi 		val = amdzen_df_read32_bcast(azn, df, DF_CFG_ADDR_CTL_V2);
116871815ce7SRobert Mustacchi 		break;
116971815ce7SRobert Mustacchi 	case DF_REV_4:
1170*019df03dSRobert Mustacchi 	case DF_REV_4D2:
117171815ce7SRobert Mustacchi 		val = amdzen_df_read32_bcast(azn, df, DF_CFG_ADDR_CTL_V4);
117271815ce7SRobert Mustacchi 		break;
117371815ce7SRobert Mustacchi 	default:
117471815ce7SRobert Mustacchi 		dev_err(azn->azn_dip, CE_WARN, "encountered unsupported DF "
117571815ce7SRobert Mustacchi 		    "revision: 0x%x", df->adf_rev);
117671815ce7SRobert Mustacchi 		return;
117771815ce7SRobert Mustacchi 	}
117871815ce7SRobert Mustacchi 	df->adf_nb_busno = DF_CFG_ADDR_CTL_GET_BUS_NUM(val);
117971815ce7SRobert Mustacchi 	val = amdzen_df_read32_bcast(azn, df, DF_FBICNT);
118071815ce7SRobert Mustacchi 	df->adf_nents = DF_FBICNT_GET_COUNT(val);
1181047043c2SRobert Mustacchi 	if (df->adf_nents == 0)
1182047043c2SRobert Mustacchi 		return;
1183047043c2SRobert Mustacchi 	df->adf_ents = kmem_zalloc(sizeof (amdzen_df_ent_t) * df->adf_nents,
1184047043c2SRobert Mustacchi 	    KM_SLEEP);
1185047043c2SRobert Mustacchi 
1186047043c2SRobert Mustacchi 	for (i = 0; i < df->adf_nents; i++) {
1187047043c2SRobert Mustacchi 		amdzen_df_ent_t *dfe = &df->adf_ents[i];
1188047043c2SRobert Mustacchi 		uint8_t inst = i;
1189047043c2SRobert Mustacchi 
1190047043c2SRobert Mustacchi 		/*
1191047043c2SRobert Mustacchi 		 * Unfortunately, Rome uses a discontinuous instance ID pattern
1192047043c2SRobert Mustacchi 		 * while everything else we can find uses a contiguous instance
1193047043c2SRobert Mustacchi 		 * ID pattern. This means that for Rome, we need to adjust the
1194047043c2SRobert Mustacchi 		 * indexes that we iterate over, though the total number of
119571815ce7SRobert Mustacchi 		 * entries is right. This was carried over into Milan, but not
119671815ce7SRobert Mustacchi 		 * Genoa.
1197047043c2SRobert Mustacchi 		 */
1198e9abe9d6SRobert Mustacchi 		if (amdzen_is_rome_style(df->adf_funcs[0]->azns_did)) {
11997c3513e0SRobert Mustacchi 			if (inst >= ARRAY_SIZE(amdzen_df_rome_ids)) {
1200047043c2SRobert Mustacchi 				dev_err(azn->azn_dip, CE_WARN, "Rome family "
1201047043c2SRobert Mustacchi 				    "processor reported more ids than the PPR, "
1202e9abe9d6SRobert Mustacchi 				    "resetting %u to instance zero", inst);
1203047043c2SRobert Mustacchi 				inst = 0;
1204047043c2SRobert Mustacchi 			} else {
1205047043c2SRobert Mustacchi 				inst = amdzen_df_rome_ids[inst];
1206047043c2SRobert Mustacchi 			}
1207047043c2SRobert Mustacchi 		}
1208047043c2SRobert Mustacchi 
1209047043c2SRobert Mustacchi 		dfe->adfe_drvid = inst;
121071815ce7SRobert Mustacchi 		dfe->adfe_info0 = amdzen_df_read32(azn, df, inst, DF_FBIINFO0);
1211*019df03dSRobert Mustacchi 		if (df->adf_rev <= DF_REV_4) {
1212*019df03dSRobert Mustacchi 			dfe->adfe_info1 = amdzen_df_read32(azn, df, inst,
1213*019df03dSRobert Mustacchi 			    DF_FBIINFO1);
1214*019df03dSRobert Mustacchi 			dfe->adfe_info2 = amdzen_df_read32(azn, df, inst,
1215*019df03dSRobert Mustacchi 			    DF_FBIINFO2);
1216*019df03dSRobert Mustacchi 		}
121771815ce7SRobert Mustacchi 		dfe->adfe_info3 = amdzen_df_read32(azn, df, inst, DF_FBIINFO3);
1218047043c2SRobert Mustacchi 
121971815ce7SRobert Mustacchi 		dfe->adfe_type = DF_FBIINFO0_GET_TYPE(dfe->adfe_info0);
122071815ce7SRobert Mustacchi 		dfe->adfe_subtype = DF_FBIINFO0_GET_SUBTYPE(dfe->adfe_info0);
122171815ce7SRobert Mustacchi 
122271815ce7SRobert Mustacchi 		/*
122371815ce7SRobert Mustacchi 		 * The enabled flag was not present in Zen 1. Simulate it by
122471815ce7SRobert Mustacchi 		 * checking for a non-zero register instead.
122571815ce7SRobert Mustacchi 		 */
122671815ce7SRobert Mustacchi 		if (DF_FBIINFO0_V3_GET_ENABLED(dfe->adfe_info0) ||
122771815ce7SRobert Mustacchi 		    (df->adf_rev == DF_REV_2 && dfe->adfe_info0 != 0)) {
1228047043c2SRobert Mustacchi 			dfe->adfe_flags |= AMDZEN_DFE_F_ENABLED;
1229047043c2SRobert Mustacchi 		}
123071815ce7SRobert Mustacchi 		if (DF_FBIINFO0_GET_HAS_MCA(dfe->adfe_info0)) {
1231047043c2SRobert Mustacchi 			dfe->adfe_flags |= AMDZEN_DFE_F_MCA;
1232047043c2SRobert Mustacchi 		}
1233*019df03dSRobert Mustacchi 
1234*019df03dSRobert Mustacchi 		/*
1235*019df03dSRobert Mustacchi 		 * Starting with DFv4 there is no instance ID in the fabric info
1236*019df03dSRobert Mustacchi 		 * 3 register, so we instead grab it out of the driver ID which
1237*019df03dSRobert Mustacchi 		 * is what it should be anyways.
1238*019df03dSRobert Mustacchi 		 */
1239*019df03dSRobert Mustacchi 		if (df->adf_rev >= DF_REV_4) {
1240*019df03dSRobert Mustacchi 			dfe->adfe_inst_id = dfe->adfe_drvid;
1241*019df03dSRobert Mustacchi 		} else {
1242*019df03dSRobert Mustacchi 			dfe->adfe_inst_id =
1243*019df03dSRobert Mustacchi 			    DF_FBIINFO3_GET_INSTID(dfe->adfe_info3);
1244*019df03dSRobert Mustacchi 		}
1245*019df03dSRobert Mustacchi 
124671815ce7SRobert Mustacchi 		switch (df->adf_rev) {
124771815ce7SRobert Mustacchi 		case DF_REV_2:
1248047043c2SRobert Mustacchi 			dfe->adfe_fabric_id =
124971815ce7SRobert Mustacchi 			    DF_FBIINFO3_V2_GET_BLOCKID(dfe->adfe_info3);
125071815ce7SRobert Mustacchi 			break;
125171815ce7SRobert Mustacchi 		case DF_REV_3:
125271815ce7SRobert Mustacchi 			dfe->adfe_fabric_id =
125371815ce7SRobert Mustacchi 			    DF_FBIINFO3_V3_GET_BLOCKID(dfe->adfe_info3);
125471815ce7SRobert Mustacchi 			break;
125571815ce7SRobert Mustacchi 		case DF_REV_3P5:
125671815ce7SRobert Mustacchi 			dfe->adfe_fabric_id =
125771815ce7SRobert Mustacchi 			    DF_FBIINFO3_V3P5_GET_BLOCKID(dfe->adfe_info3);
125871815ce7SRobert Mustacchi 			break;
125971815ce7SRobert Mustacchi 		case DF_REV_4:
1260*019df03dSRobert Mustacchi 		case DF_REV_4D2:
126171815ce7SRobert Mustacchi 			dfe->adfe_fabric_id =
126271815ce7SRobert Mustacchi 			    DF_FBIINFO3_V4_GET_BLOCKID(dfe->adfe_info3);
126371815ce7SRobert Mustacchi 			break;
126471815ce7SRobert Mustacchi 		default:
126571815ce7SRobert Mustacchi 			panic("encountered suspicious, previously rejected DF "
126671815ce7SRobert Mustacchi 			    "rev: 0x%x", df->adf_rev);
126771815ce7SRobert Mustacchi 		}
1268dd23d762SRobert Mustacchi 
1269dd23d762SRobert Mustacchi 		/*
1270dd23d762SRobert Mustacchi 		 * Record information about a subset of DF entities that we've
1271dd23d762SRobert Mustacchi 		 * found. Currently we're tracking this only for CCMs.
1272dd23d762SRobert Mustacchi 		 */
1273dd23d762SRobert Mustacchi 		if ((dfe->adfe_flags & AMDZEN_DFE_F_ENABLED) == 0)
1274dd23d762SRobert Mustacchi 			continue;
1275dd23d762SRobert Mustacchi 
1276*019df03dSRobert Mustacchi 		if (amdzen_dfe_is_ccm(df, dfe)) {
1277dd23d762SRobert Mustacchi 			df->adf_nccm++;
1278dd23d762SRobert Mustacchi 		}
1279dd23d762SRobert Mustacchi 	}
1280dd23d762SRobert Mustacchi 
1281dd23d762SRobert Mustacchi 	/*
1282dd23d762SRobert Mustacchi 	 * Now that we have filled in all of our info, attempt to fill in
1283dd23d762SRobert Mustacchi 	 * specific information about different types of instances.
1284dd23d762SRobert Mustacchi 	 */
1285dd23d762SRobert Mustacchi 	ccmno = 0;
1286dd23d762SRobert Mustacchi 	for (uint_t i = 0; i < df->adf_nents; i++) {
1287dd23d762SRobert Mustacchi 		amdzen_df_ent_t *dfe = &df->adf_ents[i];
1288dd23d762SRobert Mustacchi 
1289dd23d762SRobert Mustacchi 		if ((dfe->adfe_flags & AMDZEN_DFE_F_ENABLED) == 0)
1290dd23d762SRobert Mustacchi 			continue;
1291dd23d762SRobert Mustacchi 
1292dd23d762SRobert Mustacchi 		/*
1293dd23d762SRobert Mustacchi 		 * Perform type and sub-type specific initialization. Currently
1294dd23d762SRobert Mustacchi 		 * limited to CCMs.
1295dd23d762SRobert Mustacchi 		 */
1296dd23d762SRobert Mustacchi 		switch (dfe->adfe_type) {
1297dd23d762SRobert Mustacchi 		case DF_TYPE_CCM:
1298dd23d762SRobert Mustacchi 			amdzen_setup_df_ccm(azn, df, dfe, ccmno);
1299dd23d762SRobert Mustacchi 			ccmno++;
1300dd23d762SRobert Mustacchi 			break;
1301dd23d762SRobert Mustacchi 		default:
1302dd23d762SRobert Mustacchi 			break;
1303dd23d762SRobert Mustacchi 		}
1304047043c2SRobert Mustacchi 	}
1305047043c2SRobert Mustacchi 
130671815ce7SRobert Mustacchi 	amdzen_determine_fabric_decomp(azn, df);
1307047043c2SRobert Mustacchi }
1308047043c2SRobert Mustacchi 
1309047043c2SRobert Mustacchi static void
amdzen_find_nb(amdzen_t * azn,amdzen_df_t * df)1310047043c2SRobert Mustacchi amdzen_find_nb(amdzen_t *azn, amdzen_df_t *df)
1311047043c2SRobert Mustacchi {
1312047043c2SRobert Mustacchi 	amdzen_stub_t *stub;
1313047043c2SRobert Mustacchi 
1314047043c2SRobert Mustacchi 	for (stub = list_head(&azn->azn_nb_stubs); stub != NULL;
1315047043c2SRobert Mustacchi 	    stub = list_next(&azn->azn_nb_stubs, stub)) {
1316047043c2SRobert Mustacchi 		if (stub->azns_bus == df->adf_nb_busno) {
1317047043c2SRobert Mustacchi 			df->adf_flags |= AMDZEN_DF_F_FOUND_NB;
1318047043c2SRobert Mustacchi 			df->adf_nb = stub;
1319047043c2SRobert Mustacchi 			return;
1320047043c2SRobert Mustacchi 		}
1321047043c2SRobert Mustacchi 	}
1322047043c2SRobert Mustacchi }
1323047043c2SRobert Mustacchi 
1324*019df03dSRobert Mustacchi /*
1325*019df03dSRobert Mustacchi  * We need to be careful using this function as different AMD generations have
1326*019df03dSRobert Mustacchi  * acted in different ways when there is a missing CCD. We've found that in
1327*019df03dSRobert Mustacchi  * hardware where the CCM is enabled but there is no CCD attached, it generally
1328*019df03dSRobert Mustacchi  * is safe (i.e. DFv3 on Rome), but on DFv4 if we ask for a CCD that would
1329*019df03dSRobert Mustacchi  * correspond to a disabled CCM then the firmware may inject a fatal error
1330*019df03dSRobert Mustacchi  * (which is hopefully something missing in our RAS/MCA-X enablement).
1331*019df03dSRobert Mustacchi  *
1332*019df03dSRobert Mustacchi  * Put differently if this doesn't correspond to an Enabled CCM and you know the
1333*019df03dSRobert Mustacchi  * number of valid CCDs on this, don't use it.
1334*019df03dSRobert Mustacchi  */
1335*019df03dSRobert Mustacchi static boolean_t
amdzen_ccd_present(amdzen_t * azn,amdzen_df_t * df,uint32_t ccdno)1336*019df03dSRobert Mustacchi amdzen_ccd_present(amdzen_t *azn, amdzen_df_t *df, uint32_t ccdno)
1337*019df03dSRobert Mustacchi {
1338*019df03dSRobert Mustacchi 	smn_reg_t die_reg = SMUPWR_CCD_DIE_ID(ccdno);
1339*019df03dSRobert Mustacchi 	uint32_t val = amdzen_smn_read(azn, df, die_reg);
1340*019df03dSRobert Mustacchi 	if (val == SMN_EINVAL32) {
1341*019df03dSRobert Mustacchi 		return (B_FALSE);
1342*019df03dSRobert Mustacchi 	}
1343*019df03dSRobert Mustacchi 
1344*019df03dSRobert Mustacchi 	ASSERT3U(ccdno, ==, SMUPWR_CCD_DIE_ID_GET(val));
1345*019df03dSRobert Mustacchi 	return (B_TRUE);
1346*019df03dSRobert Mustacchi }
1347*019df03dSRobert Mustacchi 
1348*019df03dSRobert Mustacchi static uint32_t
amdzen_ccd_thread_en(amdzen_t * azn,amdzen_df_t * df,uint32_t ccdno)1349*019df03dSRobert Mustacchi amdzen_ccd_thread_en(amdzen_t *azn, amdzen_df_t *df, uint32_t ccdno)
1350*019df03dSRobert Mustacchi {
1351*019df03dSRobert Mustacchi 	smn_reg_t reg;
1352*019df03dSRobert Mustacchi 
1353*019df03dSRobert Mustacchi 	if (uarchrev_uarch(azn->azn_uarchrev) >= X86_UARCH_AMD_ZEN5) {
1354*019df03dSRobert Mustacchi 		reg = L3SOC_THREAD_EN(ccdno);
1355*019df03dSRobert Mustacchi 	} else {
1356*019df03dSRobert Mustacchi 		reg = SMUPWR_THREAD_EN(ccdno);
1357*019df03dSRobert Mustacchi 	}
1358*019df03dSRobert Mustacchi 
1359*019df03dSRobert Mustacchi 	return (amdzen_smn_read(azn, df, reg));
1360*019df03dSRobert Mustacchi }
1361*019df03dSRobert Mustacchi 
1362*019df03dSRobert Mustacchi static uint32_t
amdzen_ccd_core_en(amdzen_t * azn,amdzen_df_t * df,uint32_t ccdno)1363*019df03dSRobert Mustacchi amdzen_ccd_core_en(amdzen_t *azn, amdzen_df_t *df, uint32_t ccdno)
1364*019df03dSRobert Mustacchi {
1365*019df03dSRobert Mustacchi 	smn_reg_t reg;
1366*019df03dSRobert Mustacchi 
1367*019df03dSRobert Mustacchi 	if (uarchrev_uarch(azn->azn_uarchrev) >= X86_UARCH_AMD_ZEN5) {
1368*019df03dSRobert Mustacchi 		reg = L3SOC_CORE_EN(ccdno);
1369*019df03dSRobert Mustacchi 	} else {
1370*019df03dSRobert Mustacchi 		reg = SMUPWR_CORE_EN(ccdno);
1371*019df03dSRobert Mustacchi 	}
1372*019df03dSRobert Mustacchi 
1373*019df03dSRobert Mustacchi 	return (amdzen_smn_read(azn, df, reg));
1374*019df03dSRobert Mustacchi }
1375*019df03dSRobert Mustacchi 
1376*019df03dSRobert Mustacchi static void
amdzen_ccd_info(amdzen_t * azn,amdzen_df_t * df,uint32_t ccdno,uint32_t * nccxp,uint32_t * nlcorep,uint32_t * nthrp)1377*019df03dSRobert Mustacchi amdzen_ccd_info(amdzen_t *azn, amdzen_df_t *df, uint32_t ccdno, uint32_t *nccxp,
1378*019df03dSRobert Mustacchi     uint32_t *nlcorep, uint32_t *nthrp)
1379*019df03dSRobert Mustacchi {
1380*019df03dSRobert Mustacchi 	uint32_t nccx, nlcore, smt;
1381*019df03dSRobert Mustacchi 
1382*019df03dSRobert Mustacchi 	if (uarchrev_uarch(azn->azn_uarchrev) >= X86_UARCH_AMD_ZEN5) {
1383*019df03dSRobert Mustacchi 		smn_reg_t reg = L3SOC_THREAD_CFG(ccdno);
1384*019df03dSRobert Mustacchi 		uint32_t val = amdzen_smn_read(azn, df, reg);
1385*019df03dSRobert Mustacchi 		nccx = L3SOC_THREAD_CFG_GET_COMPLEX_COUNT(val) + 1;
1386*019df03dSRobert Mustacchi 		nlcore = L3SOC_THREAD_CFG_GET_CORE_COUNT(val) + 1;
1387*019df03dSRobert Mustacchi 		smt = L3SOC_THREAD_CFG_GET_SMT_MODE(val);
1388*019df03dSRobert Mustacchi 	} else {
1389*019df03dSRobert Mustacchi 		smn_reg_t reg = SMUPWR_THREAD_CFG(ccdno);
1390*019df03dSRobert Mustacchi 		uint32_t val = amdzen_smn_read(azn, df, reg);
1391*019df03dSRobert Mustacchi 		nccx = SMUPWR_THREAD_CFG_GET_COMPLEX_COUNT(val) + 1;
1392*019df03dSRobert Mustacchi 		nlcore = SMUPWR_THREAD_CFG_GET_CORE_COUNT(val) + 1;
1393*019df03dSRobert Mustacchi 		smt = SMUPWR_THREAD_CFG_GET_SMT_MODE(val);
1394*019df03dSRobert Mustacchi 	}
1395*019df03dSRobert Mustacchi 
1396*019df03dSRobert Mustacchi 	if (nccxp != NULL) {
1397*019df03dSRobert Mustacchi 		*nccxp = nccx;
1398*019df03dSRobert Mustacchi 	}
1399*019df03dSRobert Mustacchi 
1400*019df03dSRobert Mustacchi 	if (nlcorep != NULL) {
1401*019df03dSRobert Mustacchi 		*nlcorep = nlcore;
1402*019df03dSRobert Mustacchi 	}
1403*019df03dSRobert Mustacchi 
1404*019df03dSRobert Mustacchi 	if (nthrp != NULL) {
1405*019df03dSRobert Mustacchi 		/* The L3::L3SOC and SMU::PWR values are the same here */
1406*019df03dSRobert Mustacchi 		if (smt == SMUPWR_THREAD_CFG_SMT_MODE_SMT) {
1407*019df03dSRobert Mustacchi 			*nthrp = 2;
1408*019df03dSRobert Mustacchi 		} else {
1409*019df03dSRobert Mustacchi 			*nthrp = 1;
1410*019df03dSRobert Mustacchi 		}
1411*019df03dSRobert Mustacchi 	}
1412*019df03dSRobert Mustacchi }
1413*019df03dSRobert Mustacchi 
1414047043c2SRobert Mustacchi static void
amdzen_initpkg_to_apic(amdzen_t * azn,const uint32_t pkg0,const uint32_t pkg7)1415dd23d762SRobert Mustacchi amdzen_initpkg_to_apic(amdzen_t *azn, const uint32_t pkg0, const uint32_t pkg7)
1416dd23d762SRobert Mustacchi {
1417dd23d762SRobert Mustacchi 	uint32_t nsock, nccd, nccx, ncore, nthr, extccx;
1418dd23d762SRobert Mustacchi 	uint32_t nsock_bits, nccd_bits, nccx_bits, ncore_bits, nthr_bits;
1419dd23d762SRobert Mustacchi 	amdzen_apic_decomp_t *apic = &azn->azn_apic_decomp;
1420dd23d762SRobert Mustacchi 
1421dd23d762SRobert Mustacchi 	/*
1422dd23d762SRobert Mustacchi 	 * These are all 0 based values, meaning that we need to add one to each
1423dd23d762SRobert Mustacchi 	 * of them. However, we skip this because to calculate the number of
1424dd23d762SRobert Mustacchi 	 * bits to cover an entity we would subtract one.
1425dd23d762SRobert Mustacchi 	 */
1426dd23d762SRobert Mustacchi 	nthr = SCFCTP_PMREG_INITPKG0_GET_SMTEN(pkg0);
1427dd23d762SRobert Mustacchi 	ncore = SCFCTP_PMREG_INITPKG7_GET_N_CORES(pkg7);
1428dd23d762SRobert Mustacchi 	nccx = SCFCTP_PMREG_INITPKG7_GET_N_CCXS(pkg7);
1429dd23d762SRobert Mustacchi 	nccd = SCFCTP_PMREG_INITPKG7_GET_N_DIES(pkg7);
1430dd23d762SRobert Mustacchi 	nsock = SCFCTP_PMREG_INITPKG7_GET_N_SOCKETS(pkg7);
1431dd23d762SRobert Mustacchi 
1432*019df03dSRobert Mustacchi 	if (uarchrev_uarch(azn->azn_uarchrev) >= X86_UARCH_AMD_ZEN4) {
1433dd23d762SRobert Mustacchi 		extccx = SCFCTP_PMREG_INITPKG7_ZEN4_GET_16TAPIC(pkg7);
1434dd23d762SRobert Mustacchi 	} else {
1435dd23d762SRobert Mustacchi 		extccx = 0;
1436dd23d762SRobert Mustacchi 	}
1437dd23d762SRobert Mustacchi 
1438dd23d762SRobert Mustacchi 	nthr_bits = highbit(nthr);
1439dd23d762SRobert Mustacchi 	ncore_bits = highbit(ncore);
1440dd23d762SRobert Mustacchi 	nccx_bits = highbit(nccx);
1441dd23d762SRobert Mustacchi 	nccd_bits = highbit(nccd);
1442dd23d762SRobert Mustacchi 	nsock_bits = highbit(nsock);
1443dd23d762SRobert Mustacchi 
1444dd23d762SRobert Mustacchi 	apic->aad_thread_shift = 0;
1445dd23d762SRobert Mustacchi 	apic->aad_thread_mask = (1 << nthr_bits) - 1;
1446dd23d762SRobert Mustacchi 
1447dd23d762SRobert Mustacchi 	apic->aad_core_shift = nthr_bits;
1448dd23d762SRobert Mustacchi 	if (ncore_bits > 0) {
1449dd23d762SRobert Mustacchi 		apic->aad_core_mask = (1 << ncore_bits) - 1;
1450dd23d762SRobert Mustacchi 		apic->aad_core_mask <<= apic->aad_core_shift;
1451dd23d762SRobert Mustacchi 	} else {
1452dd23d762SRobert Mustacchi 		apic->aad_core_mask = 0;
1453dd23d762SRobert Mustacchi 	}
1454dd23d762SRobert Mustacchi 
1455dd23d762SRobert Mustacchi 	/*
1456dd23d762SRobert Mustacchi 	 * The APIC_16T_MODE bit indicates that the total shift to start the CCX
1457dd23d762SRobert Mustacchi 	 * should be at 4 bits if it's not. It doesn't mean that the CCX portion
1458dd23d762SRobert Mustacchi 	 * of the value should take up four bits. In the common Genoa case,
1459dd23d762SRobert Mustacchi 	 * nccx_bits will be zero.
1460dd23d762SRobert Mustacchi 	 */
1461dd23d762SRobert Mustacchi 	apic->aad_ccx_shift = apic->aad_core_shift + ncore_bits;
1462dd23d762SRobert Mustacchi 	if (extccx != 0 && apic->aad_ccx_shift < 4) {
1463dd23d762SRobert Mustacchi 		apic->aad_ccx_shift = 4;
1464dd23d762SRobert Mustacchi 	}
1465dd23d762SRobert Mustacchi 	if (nccx_bits > 0) {
1466dd23d762SRobert Mustacchi 		apic->aad_ccx_mask = (1 << nccx_bits) - 1;
1467dd23d762SRobert Mustacchi 		apic->aad_ccx_mask <<= apic->aad_ccx_shift;
1468dd23d762SRobert Mustacchi 	} else {
1469dd23d762SRobert Mustacchi 		apic->aad_ccx_mask = 0;
1470dd23d762SRobert Mustacchi 	}
1471dd23d762SRobert Mustacchi 
1472dd23d762SRobert Mustacchi 	apic->aad_ccd_shift = apic->aad_ccx_shift + nccx_bits;
1473dd23d762SRobert Mustacchi 	if (nccd_bits > 0) {
1474dd23d762SRobert Mustacchi 		apic->aad_ccd_mask = (1 << nccd_bits) - 1;
1475dd23d762SRobert Mustacchi 		apic->aad_ccd_mask <<= apic->aad_ccd_shift;
1476dd23d762SRobert Mustacchi 	} else {
1477dd23d762SRobert Mustacchi 		apic->aad_ccd_mask = 0;
1478dd23d762SRobert Mustacchi 	}
1479dd23d762SRobert Mustacchi 
1480dd23d762SRobert Mustacchi 	apic->aad_sock_shift = apic->aad_ccd_shift + nccd_bits;
1481dd23d762SRobert Mustacchi 	if (nsock_bits > 0) {
1482dd23d762SRobert Mustacchi 		apic->aad_sock_mask = (1 << nsock_bits) - 1;
1483dd23d762SRobert Mustacchi 		apic->aad_sock_mask <<= apic->aad_sock_shift;
1484dd23d762SRobert Mustacchi 	} else {
1485dd23d762SRobert Mustacchi 		apic->aad_sock_mask = 0;
1486dd23d762SRobert Mustacchi 	}
1487dd23d762SRobert Mustacchi 
1488dd23d762SRobert Mustacchi 	/*
1489dd23d762SRobert Mustacchi 	 * Currently all supported Zen 2+ platforms only have a single die per
1490dd23d762SRobert Mustacchi 	 * socket as compared to Zen 1. So this is always kept at zero.
1491dd23d762SRobert Mustacchi 	 */
1492dd23d762SRobert Mustacchi 	apic->aad_die_mask = 0;
1493dd23d762SRobert Mustacchi 	apic->aad_die_shift = 0;
1494dd23d762SRobert Mustacchi }
1495dd23d762SRobert Mustacchi 
1496dd23d762SRobert Mustacchi /*
1497dd23d762SRobert Mustacchi  * We would like to determine what the logical APIC decomposition is on Zen 3
1498dd23d762SRobert Mustacchi  * and newer family parts. While there is information added to CPUID in the form
1499dd23d762SRobert Mustacchi  * of leaf 8X26, that isn't present in Zen 3, so instead we go to what we
1500dd23d762SRobert Mustacchi  * believe is the underlying source of the CPUID data.
1501dd23d762SRobert Mustacchi  *
1502dd23d762SRobert Mustacchi  * Fundamentally there are a series of registers in SMN space that relate to the
1503dd23d762SRobert Mustacchi  * SCFCTP. Coincidentally, there is one of these for each core and there are a
1504dd23d762SRobert Mustacchi  * pair of related SMN registers. L3::SCFCTP::PMREG_INITPKG0 contains
1505dd23d762SRobert Mustacchi  * information about a given's core logical and physical IDs. More interestingly
1506dd23d762SRobert Mustacchi  * for this particular case, L3::SCFCTP::PMREG_INITPKG7, contains the overall
1507dd23d762SRobert Mustacchi  * total number of logical entities. We've been promised that this has to be
1508dd23d762SRobert Mustacchi  * the same across the fabric. That's all well and good, but this begs the
1509dd23d762SRobert Mustacchi  * question of how do we actually get there. The above is a core-specific
1510dd23d762SRobert Mustacchi  * register and requires that we understand information about which CCDs and
1511dd23d762SRobert Mustacchi  * CCXs are actually present.
1512dd23d762SRobert Mustacchi  *
1513dd23d762SRobert Mustacchi  * So we are starting with a data fabric that has some CCM present. The CCM
1514dd23d762SRobert Mustacchi  * entries in the data fabric may be tagged with our ENABLED flag.
1515dd23d762SRobert Mustacchi  * Unfortunately, that can be true regardless of whether or not it's actually
1516dd23d762SRobert Mustacchi  * present or not. As a result, we go to another chunk of SMN space registers,
1517dd23d762SRobert Mustacchi  * SMU::PWR. These contain information about the CCDs, the physical cores that
1518dd23d762SRobert Mustacchi  * are enabled, and related. So we will first walk the DF entities and see if we
1519dd23d762SRobert Mustacchi  * can read its SMN::PWR::CCD_DIE_ID. If we get back a value of all 1s then
1520dd23d762SRobert Mustacchi  * there is nothing present. Otherwise, we should get back something that
1521dd23d762SRobert Mustacchi  * matches information in the data fabric.
1522dd23d762SRobert Mustacchi  *
1523dd23d762SRobert Mustacchi  * With that in hand, we can read the SMU::PWR::CORE_ENABLE register to
1524dd23d762SRobert Mustacchi  * determine which physical cores are enabled in the CCD/CCX. That will finally
1525dd23d762SRobert Mustacchi  * give us an index to get to our friend INITPKG7.
1526dd23d762SRobert Mustacchi  */
1527dd23d762SRobert Mustacchi static boolean_t
amdzen_determine_apic_decomp_initpkg(amdzen_t * azn)1528dd23d762SRobert Mustacchi amdzen_determine_apic_decomp_initpkg(amdzen_t *azn)
1529dd23d762SRobert Mustacchi {
1530dd23d762SRobert Mustacchi 	amdzen_df_t *df = &azn->azn_dfs[0];
1531dd23d762SRobert Mustacchi 	uint32_t ccdno = 0;
1532dd23d762SRobert Mustacchi 
1533dd23d762SRobert Mustacchi 	for (uint_t i = 0; i < df->adf_nents; i++) {
1534dd23d762SRobert Mustacchi 		const amdzen_df_ent_t *ent = &df->adf_ents[i];
1535dd23d762SRobert Mustacchi 		if ((ent->adfe_flags & AMDZEN_DFE_F_ENABLED) == 0)
1536dd23d762SRobert Mustacchi 			continue;
1537dd23d762SRobert Mustacchi 
1538*019df03dSRobert Mustacchi 		if (amdzen_dfe_is_ccm(df, ent)) {
1539dd23d762SRobert Mustacchi 			uint32_t val, nccx, pkg7, pkg0;
1540dd23d762SRobert Mustacchi 			smn_reg_t pkg7_reg, pkg0_reg;
1541dd23d762SRobert Mustacchi 			int core_bit;
1542dd23d762SRobert Mustacchi 			uint8_t pccxno, pcoreno;
1543dd23d762SRobert Mustacchi 
1544*019df03dSRobert Mustacchi 			if (!amdzen_ccd_present(azn, df, ccdno)) {
1545dd23d762SRobert Mustacchi 				ccdno++;
1546dd23d762SRobert Mustacchi 				continue;
1547dd23d762SRobert Mustacchi 			}
1548dd23d762SRobert Mustacchi 
1549dd23d762SRobert Mustacchi 			/*
1550dd23d762SRobert Mustacchi 			 * This die actually exists. Switch over to the core
1551dd23d762SRobert Mustacchi 			 * enable register to find one to ask about physically.
1552dd23d762SRobert Mustacchi 			 */
1553*019df03dSRobert Mustacchi 			amdzen_ccd_info(azn, df, ccdno, &nccx, NULL, NULL);
1554*019df03dSRobert Mustacchi 			val = amdzen_ccd_core_en(azn, df, ccdno);
1555dd23d762SRobert Mustacchi 			if (val == 0) {
1556dd23d762SRobert Mustacchi 				ccdno++;
1557dd23d762SRobert Mustacchi 				continue;
1558dd23d762SRobert Mustacchi 			}
1559dd23d762SRobert Mustacchi 
1560dd23d762SRobert Mustacchi 			/*
1561dd23d762SRobert Mustacchi 			 * There exists an enabled physical core. Find the first
1562dd23d762SRobert Mustacchi 			 * index of it and map it to the corresponding CCD and
1563dd23d762SRobert Mustacchi 			 * CCX. ddi_ffs is the bit index, but we want the
1564dd23d762SRobert Mustacchi 			 * physical core number, hence the -1.
1565dd23d762SRobert Mustacchi 			 */
1566dd23d762SRobert Mustacchi 			core_bit = ddi_ffs(val);
1567dd23d762SRobert Mustacchi 			ASSERT3S(core_bit, !=, 0);
1568dd23d762SRobert Mustacchi 			pcoreno = core_bit - 1;
1569dd23d762SRobert Mustacchi 
1570dd23d762SRobert Mustacchi 			/*
1571dd23d762SRobert Mustacchi 			 * Unfortunately SMU::PWR::THREAD_CONFIGURATION gives us
1572dd23d762SRobert Mustacchi 			 * the Number of logical cores that are present in the
1573*019df03dSRobert Mustacchi 			 * complex, not the total number of physical cores.
1574*019df03dSRobert Mustacchi 			 * Right now we do assume that the physical and logical
1575*019df03dSRobert Mustacchi 			 * ccx numbering is equivalent (we have no other way of
1576*019df03dSRobert Mustacchi 			 * knowing if it is or isn't right now) and that we'd
1577*019df03dSRobert Mustacchi 			 * always have CCX0 before CCX1. AMD seems to suggest we
1578*019df03dSRobert Mustacchi 			 * can assume this, though it is a worrisome assumption.
1579dd23d762SRobert Mustacchi 			 */
1580*019df03dSRobert Mustacchi 			pccxno = pcoreno / azn->azn_ncore_per_ccx;
1581dd23d762SRobert Mustacchi 			ASSERT3U(pccxno, <, nccx);
1582dd23d762SRobert Mustacchi 			pkg7_reg = SCFCTP_PMREG_INITPKG7(ccdno, pccxno,
1583dd23d762SRobert Mustacchi 			    pcoreno);
1584dd23d762SRobert Mustacchi 			pkg7 = amdzen_smn_read(azn, df, pkg7_reg);
1585dd23d762SRobert Mustacchi 			pkg0_reg = SCFCTP_PMREG_INITPKG0(ccdno, pccxno,
1586dd23d762SRobert Mustacchi 			    pcoreno);
1587dd23d762SRobert Mustacchi 			pkg0 = amdzen_smn_read(azn, df, pkg0_reg);
1588dd23d762SRobert Mustacchi 			amdzen_initpkg_to_apic(azn, pkg0, pkg7);
1589dd23d762SRobert Mustacchi 			return (B_TRUE);
1590dd23d762SRobert Mustacchi 		}
1591dd23d762SRobert Mustacchi 	}
1592dd23d762SRobert Mustacchi 
1593dd23d762SRobert Mustacchi 	return (B_FALSE);
1594dd23d762SRobert Mustacchi }
1595dd23d762SRobert Mustacchi 
1596dd23d762SRobert Mustacchi /*
1597dd23d762SRobert Mustacchi  * We have the fun job of trying to figure out what the correct form of the APIC
1598dd23d762SRobert Mustacchi  * decomposition should be and how to break that into its logical components.
1599dd23d762SRobert Mustacchi  * The way that we get at this is generation-specific unfortunately. Here's how
1600dd23d762SRobert Mustacchi  * it works out:
1601dd23d762SRobert Mustacchi  *
1602dd23d762SRobert Mustacchi  * Zen 1-2	This era of CPUs are deceptively simple. The PPR for a given
1603dd23d762SRobert Mustacchi  *		family defines exactly how the APIC ID is broken into logical
1604dd23d762SRobert Mustacchi  *		components and it's fixed. That is, depending on whether or
1605dd23d762SRobert Mustacchi  *		not SMT is enabled. Zen 1 and Zen 2 use different schemes for
1606dd23d762SRobert Mustacchi  *		constructing this. The way that we're supposed to check if SMT
1607dd23d762SRobert Mustacchi  *		is enabled is to use AMD leaf 8X1E and ask how many threads per
1608dd23d762SRobert Mustacchi  *		core there are. We use the x86 feature set to determine that
1609dd23d762SRobert Mustacchi  *		instead.
1610dd23d762SRobert Mustacchi  *
1611dd23d762SRobert Mustacchi  *		More specifically the Zen 1 scheme is 7 bits long. The bits have
1612dd23d762SRobert Mustacchi  *		the following meanings.
1613dd23d762SRobert Mustacchi  *
1614dd23d762SRobert Mustacchi  *		[6]   Socket ID
1615dd23d762SRobert Mustacchi  *		[5:4] Node ID
1616dd23d762SRobert Mustacchi  *		[3]   Logical CCX ID
1617dd23d762SRobert Mustacchi  *		With SMT		Without SMT
1618dd23d762SRobert Mustacchi  *		[2:1] Logical Core ID	[2]   hardcoded to zero
1619dd23d762SRobert Mustacchi  *		[0] Thread ID		[1:0] Logical Core ID
1620dd23d762SRobert Mustacchi  *
1621dd23d762SRobert Mustacchi  *		The following is the Zen 2 scheme assuming SMT. The Zen 2 scheme
1622dd23d762SRobert Mustacchi  *		without SMT shifts everything to the right by one bit.
1623dd23d762SRobert Mustacchi  *
1624dd23d762SRobert Mustacchi  *		[7]   Socket ID
1625dd23d762SRobert Mustacchi  *		[6:4] Logical CCD ID
1626dd23d762SRobert Mustacchi  *		[3]   Logical CCX ID
1627dd23d762SRobert Mustacchi  *		[2:1] Logical Core ID
1628dd23d762SRobert Mustacchi  *		[0]   Thread ID
1629dd23d762SRobert Mustacchi  *
1630dd23d762SRobert Mustacchi  * Zen 3	Zen 3 CPUs moved past the fixed APIC ID format that Zen 1 and
1631dd23d762SRobert Mustacchi  *		Zen 2 had, but also don't give us the nice way of discovering
1632dd23d762SRobert Mustacchi  *		this via CPUID that Zen 4 did. The APIC ID id uses a given
1633dd23d762SRobert Mustacchi  *		number of bits for each logical component that exists, but the
1634dd23d762SRobert Mustacchi  *		exact number varies based on what's actually present. To get at
1635dd23d762SRobert Mustacchi  *		this we use a piece of data that is embedded in the SCFCTP
1636dd23d762SRobert Mustacchi  *		(Scalable Control Fabric, Clocks, Test, Power Gating). This can
1637dd23d762SRobert Mustacchi  *		be used to determine how many logical entities of each kind the
1638dd23d762SRobert Mustacchi  *		system thinks exist. While we could use the various CPUID
1639dd23d762SRobert Mustacchi  *		topology items to try to speed this up, they don't tell us the
1640dd23d762SRobert Mustacchi  *		die information that we need to do this.
1641dd23d762SRobert Mustacchi  *
1642dd23d762SRobert Mustacchi  * Zen 4+	Zen 4 introduced CPUID leaf 8000_0026h which gives us a means
1643dd23d762SRobert Mustacchi  *		for determining how to extract the CCD, CCX, and related pieces
1644dd23d762SRobert Mustacchi  *		out of the device. One thing we have to be aware of is that when
1645dd23d762SRobert Mustacchi  *		the CCD and CCX shift are the same, that means that there is
1646dd23d762SRobert Mustacchi  *		only a single CCX and therefore have to take that into account
1647dd23d762SRobert Mustacchi  *		appropriately. This is the case generally on Zen 4 platforms,
1648dd23d762SRobert Mustacchi  *		but not on Bergamo. Until we can confirm the actual CPUID leaf
1649dd23d762SRobert Mustacchi  *		values that we receive in the cases of Bergamo and others, we
1650dd23d762SRobert Mustacchi  *		opt instead to use the same SCFCTP scheme as Zen 3.
1651dd23d762SRobert Mustacchi  */
1652dd23d762SRobert Mustacchi static boolean_t
amdzen_determine_apic_decomp(amdzen_t * azn)1653dd23d762SRobert Mustacchi amdzen_determine_apic_decomp(amdzen_t *azn)
1654dd23d762SRobert Mustacchi {
1655dd23d762SRobert Mustacchi 	amdzen_apic_decomp_t *apic = &azn->azn_apic_decomp;
1656dd23d762SRobert Mustacchi 	boolean_t smt = is_x86_feature(x86_featureset, X86FSET_HTT);
1657dd23d762SRobert Mustacchi 
1658*019df03dSRobert Mustacchi 	switch (uarchrev_uarch(azn->azn_uarchrev)) {
1659dd23d762SRobert Mustacchi 	case X86_UARCH_AMD_ZEN1:
1660dd23d762SRobert Mustacchi 	case X86_UARCH_AMD_ZENPLUS:
1661dd23d762SRobert Mustacchi 		apic->aad_sock_mask = 0x40;
1662dd23d762SRobert Mustacchi 		apic->aad_sock_shift = 6;
1663dd23d762SRobert Mustacchi 		apic->aad_die_mask = 0x30;
1664dd23d762SRobert Mustacchi 		apic->aad_die_shift = 4;
1665dd23d762SRobert Mustacchi 		apic->aad_ccd_mask = 0;
1666dd23d762SRobert Mustacchi 		apic->aad_ccd_shift = 0;
1667dd23d762SRobert Mustacchi 		apic->aad_ccx_mask = 0x08;
1668dd23d762SRobert Mustacchi 		apic->aad_ccx_shift = 3;
1669dd23d762SRobert Mustacchi 
1670dd23d762SRobert Mustacchi 		if (smt) {
1671dd23d762SRobert Mustacchi 			apic->aad_core_mask = 0x06;
1672dd23d762SRobert Mustacchi 			apic->aad_core_shift = 1;
1673dd23d762SRobert Mustacchi 			apic->aad_thread_mask = 0x1;
1674dd23d762SRobert Mustacchi 			apic->aad_thread_shift = 0;
1675dd23d762SRobert Mustacchi 		} else {
1676dd23d762SRobert Mustacchi 			apic->aad_core_mask = 0x03;
1677dd23d762SRobert Mustacchi 			apic->aad_core_shift = 0;
1678dd23d762SRobert Mustacchi 			apic->aad_thread_mask = 0;
1679dd23d762SRobert Mustacchi 			apic->aad_thread_shift = 0;
1680dd23d762SRobert Mustacchi 		}
1681dd23d762SRobert Mustacchi 		break;
1682dd23d762SRobert Mustacchi 	case X86_UARCH_AMD_ZEN2:
1683dd23d762SRobert Mustacchi 		if (smt) {
1684dd23d762SRobert Mustacchi 			apic->aad_sock_mask = 0x80;
1685dd23d762SRobert Mustacchi 			apic->aad_sock_shift = 7;
1686dd23d762SRobert Mustacchi 			apic->aad_die_mask = 0;
1687dd23d762SRobert Mustacchi 			apic->aad_die_shift = 0;
1688dd23d762SRobert Mustacchi 			apic->aad_ccd_mask = 0x70;
1689dd23d762SRobert Mustacchi 			apic->aad_ccd_shift = 4;
1690dd23d762SRobert Mustacchi 			apic->aad_ccx_mask = 0x08;
1691dd23d762SRobert Mustacchi 			apic->aad_ccx_shift = 3;
1692dd23d762SRobert Mustacchi 			apic->aad_core_mask = 0x06;
1693dd23d762SRobert Mustacchi 			apic->aad_core_shift = 1;
1694dd23d762SRobert Mustacchi 			apic->aad_thread_mask = 0x01;
1695dd23d762SRobert Mustacchi 			apic->aad_thread_shift = 0;
1696dd23d762SRobert Mustacchi 		} else {
1697dd23d762SRobert Mustacchi 			apic->aad_sock_mask = 0x40;
1698dd23d762SRobert Mustacchi 			apic->aad_sock_shift = 6;
1699dd23d762SRobert Mustacchi 			apic->aad_die_mask = 0;
1700dd23d762SRobert Mustacchi 			apic->aad_die_shift = 0;
1701dd23d762SRobert Mustacchi 			apic->aad_ccd_mask = 0x38;
1702dd23d762SRobert Mustacchi 			apic->aad_ccd_shift = 3;
1703dd23d762SRobert Mustacchi 			apic->aad_ccx_mask = 0x04;
1704dd23d762SRobert Mustacchi 			apic->aad_ccx_shift = 2;
1705dd23d762SRobert Mustacchi 			apic->aad_core_mask = 0x3;
1706dd23d762SRobert Mustacchi 			apic->aad_core_shift = 0;
1707dd23d762SRobert Mustacchi 			apic->aad_thread_mask = 0;
1708dd23d762SRobert Mustacchi 			apic->aad_thread_shift = 0;
1709dd23d762SRobert Mustacchi 		}
1710dd23d762SRobert Mustacchi 		break;
1711dd23d762SRobert Mustacchi 	case X86_UARCH_AMD_ZEN3:
1712dd23d762SRobert Mustacchi 	case X86_UARCH_AMD_ZEN4:
1713*019df03dSRobert Mustacchi 	case X86_UARCH_AMD_ZEN5:
1714dd23d762SRobert Mustacchi 		return (amdzen_determine_apic_decomp_initpkg(azn));
1715dd23d762SRobert Mustacchi 	default:
1716dd23d762SRobert Mustacchi 		return (B_FALSE);
1717dd23d762SRobert Mustacchi 	}
1718dd23d762SRobert Mustacchi 	return (B_TRUE);
1719dd23d762SRobert Mustacchi }
1720dd23d762SRobert Mustacchi 
1721dd23d762SRobert Mustacchi /*
1722dd23d762SRobert Mustacchi  * Snapshot the number of cores that can exist in a CCX based on the Zen
1723dd23d762SRobert Mustacchi  * microarchitecture revision. In Zen 1-4 this has been a constant number
1724*019df03dSRobert Mustacchi  * regardless of the actual CPU Family. In Zen 5 this varies based upon whether
1725*019df03dSRobert Mustacchi  * or not dense dies are being used.
1726dd23d762SRobert Mustacchi  */
1727dd23d762SRobert Mustacchi static void
amdzen_determine_ncore_per_ccx(amdzen_t * azn)1728dd23d762SRobert Mustacchi amdzen_determine_ncore_per_ccx(amdzen_t *azn)
1729dd23d762SRobert Mustacchi {
1730*019df03dSRobert Mustacchi 	switch (uarchrev_uarch(azn->azn_uarchrev)) {
1731dd23d762SRobert Mustacchi 	case X86_UARCH_AMD_ZEN1:
1732dd23d762SRobert Mustacchi 	case X86_UARCH_AMD_ZENPLUS:
1733dd23d762SRobert Mustacchi 	case X86_UARCH_AMD_ZEN2:
1734dd23d762SRobert Mustacchi 		azn->azn_ncore_per_ccx = 4;
1735dd23d762SRobert Mustacchi 		break;
1736dd23d762SRobert Mustacchi 	case X86_UARCH_AMD_ZEN3:
1737dd23d762SRobert Mustacchi 	case X86_UARCH_AMD_ZEN4:
1738dd23d762SRobert Mustacchi 		azn->azn_ncore_per_ccx = 8;
1739dd23d762SRobert Mustacchi 		break;
1740*019df03dSRobert Mustacchi 	case X86_UARCH_AMD_ZEN5:
1741*019df03dSRobert Mustacchi 		if (chiprev_family(azn->azn_chiprev) ==
1742*019df03dSRobert Mustacchi 		    X86_PF_AMD_DENSE_TURIN) {
1743*019df03dSRobert Mustacchi 			azn->azn_ncore_per_ccx = 16;
1744*019df03dSRobert Mustacchi 		} else {
1745*019df03dSRobert Mustacchi 			azn->azn_ncore_per_ccx = 8;
1746*019df03dSRobert Mustacchi 		}
1747*019df03dSRobert Mustacchi 		break;
1748dd23d762SRobert Mustacchi 	default:
1749*019df03dSRobert Mustacchi 		panic("asked about non-Zen or unknown uarch");
1750dd23d762SRobert Mustacchi 	}
1751dd23d762SRobert Mustacchi }
1752dd23d762SRobert Mustacchi 
1753dd23d762SRobert Mustacchi /*
1754dd23d762SRobert Mustacchi  * Attempt to determine a logical CCD number of a given CCD where we don't have
1755dd23d762SRobert Mustacchi  * hardware support for L3::SCFCTP::PMREG_INITPKG* (e.g. pre-Zen 3 systems).
1756*019df03dSRobert Mustacchi  * The CCD numbers that we have are the in the physical space. Likely because of
1757dd23d762SRobert Mustacchi  * how the orientation of CCM numbers map to physical locations and the layout
1758*019df03dSRobert Mustacchi  * of them within the package, we haven't found a good way using the core DFv3
1759dd23d762SRobert Mustacchi  * registers to determine if a given CCD is actually present or not as generally
1760dd23d762SRobert Mustacchi  * all the CCMs are left enabled. Instead we use SMU::PWR::DIE_ID as a proxy to
1761dd23d762SRobert Mustacchi  * determine CCD presence.
1762dd23d762SRobert Mustacchi  */
1763dd23d762SRobert Mustacchi static uint32_t
amdzen_ccd_log_id_zen2(amdzen_t * azn,amdzen_df_t * df,const amdzen_df_ent_t * targ)1764dd23d762SRobert Mustacchi amdzen_ccd_log_id_zen2(amdzen_t *azn, amdzen_df_t *df,
1765dd23d762SRobert Mustacchi     const amdzen_df_ent_t *targ)
1766dd23d762SRobert Mustacchi {
1767dd23d762SRobert Mustacchi 	uint32_t smnid = 0;
1768dd23d762SRobert Mustacchi 	uint32_t logid = 0;
1769dd23d762SRobert Mustacchi 
1770dd23d762SRobert Mustacchi 	for (uint_t i = 0; i < df->adf_nents; i++) {
1771dd23d762SRobert Mustacchi 		const amdzen_df_ent_t *ent = &df->adf_ents[i];
1772dd23d762SRobert Mustacchi 
1773dd23d762SRobert Mustacchi 		if ((ent->adfe_flags & AMDZEN_DFE_F_ENABLED) == 0) {
1774dd23d762SRobert Mustacchi 			continue;
1775dd23d762SRobert Mustacchi 		}
1776dd23d762SRobert Mustacchi 
1777dd23d762SRobert Mustacchi 		if (ent->adfe_inst_id == targ->adfe_inst_id) {
1778dd23d762SRobert Mustacchi 			return (logid);
1779dd23d762SRobert Mustacchi 		}
1780dd23d762SRobert Mustacchi 
1781dd23d762SRobert Mustacchi 		if (ent->adfe_type == targ->adfe_type &&
1782dd23d762SRobert Mustacchi 		    ent->adfe_subtype == targ->adfe_subtype) {
1783dd23d762SRobert Mustacchi 			boolean_t present = amdzen_ccd_present(azn, df, smnid);
1784dd23d762SRobert Mustacchi 			smnid++;
1785dd23d762SRobert Mustacchi 			if (present) {
1786dd23d762SRobert Mustacchi 				logid++;
1787dd23d762SRobert Mustacchi 			}
1788dd23d762SRobert Mustacchi 		}
1789dd23d762SRobert Mustacchi 	}
1790dd23d762SRobert Mustacchi 
1791dd23d762SRobert Mustacchi 	panic("asked to match against invalid DF entity %p in df %p", targ, df);
1792dd23d762SRobert Mustacchi }
1793dd23d762SRobert Mustacchi 
1794dd23d762SRobert Mustacchi static void
amdzen_ccd_fill_core_initpkg0(amdzen_t * azn,amdzen_df_t * df,amdzen_topo_ccd_t * ccd,amdzen_topo_ccx_t * ccx,amdzen_topo_core_t * core,boolean_t * ccd_set,boolean_t * ccx_set)1795dd23d762SRobert Mustacchi amdzen_ccd_fill_core_initpkg0(amdzen_t *azn, amdzen_df_t *df,
1796dd23d762SRobert Mustacchi     amdzen_topo_ccd_t *ccd, amdzen_topo_ccx_t *ccx, amdzen_topo_core_t *core,
1797dd23d762SRobert Mustacchi     boolean_t *ccd_set, boolean_t *ccx_set)
1798dd23d762SRobert Mustacchi {
1799dd23d762SRobert Mustacchi 	smn_reg_t pkg0_reg;
1800dd23d762SRobert Mustacchi 	uint32_t pkg0;
1801dd23d762SRobert Mustacchi 
1802dd23d762SRobert Mustacchi 	pkg0_reg = SCFCTP_PMREG_INITPKG0(ccd->atccd_phys_no, ccx->atccx_phys_no,
1803dd23d762SRobert Mustacchi 	    core->atcore_phys_no);
1804dd23d762SRobert Mustacchi 	pkg0 = amdzen_smn_read(azn, df, pkg0_reg);
1805dd23d762SRobert Mustacchi 	core->atcore_log_no = SCFCTP_PMREG_INITPKG0_GET_LOG_CORE(pkg0);
1806dd23d762SRobert Mustacchi 
1807dd23d762SRobert Mustacchi 	if (!*ccx_set) {
1808dd23d762SRobert Mustacchi 		ccx->atccx_log_no = SCFCTP_PMREG_INITPKG0_GET_LOG_CCX(pkg0);
1809dd23d762SRobert Mustacchi 		*ccx_set = B_TRUE;
1810dd23d762SRobert Mustacchi 	}
1811dd23d762SRobert Mustacchi 
1812dd23d762SRobert Mustacchi 	if (!*ccd_set) {
1813dd23d762SRobert Mustacchi 		ccd->atccd_log_no = SCFCTP_PMREG_INITPKG0_GET_LOG_DIE(pkg0);
1814dd23d762SRobert Mustacchi 		*ccd_set = B_TRUE;
1815dd23d762SRobert Mustacchi 	}
1816dd23d762SRobert Mustacchi }
1817dd23d762SRobert Mustacchi 
1818dd23d762SRobert Mustacchi /*
1819dd23d762SRobert Mustacchi  * Attempt to fill in the physical topology information for this given CCD.
1820dd23d762SRobert Mustacchi  * There are a few steps to this that we undertake to perform this as follows:
1821dd23d762SRobert Mustacchi  *
1822dd23d762SRobert Mustacchi  * 1) First we determine whether the CCD is actually present or not by reading
1823dd23d762SRobert Mustacchi  * SMU::PWR::DIE_ID. CCDs that are not installed will still have an enabled DF
1824dd23d762SRobert Mustacchi  * entry it appears, but the request for the die ID will returns an invalid
1825dd23d762SRobert Mustacchi  * read (all 1s). This die ID should match what we think of as the SMN number
1826dd23d762SRobert Mustacchi  * below. If not, we're in trouble and the rest of this is in question.
1827dd23d762SRobert Mustacchi  *
1828dd23d762SRobert Mustacchi  * 2) We use the SMU::PWR registers to determine how many logical and physical
1829dd23d762SRobert Mustacchi  * cores are present in this CCD and how they are split amongst the CCX. Here we
1830dd23d762SRobert Mustacchi  * need to encode the CPU to CCX core size rankings. Through this process we
1831dd23d762SRobert Mustacchi  * determine and fill out which threads and cores are enabled.
1832dd23d762SRobert Mustacchi  *
1833dd23d762SRobert Mustacchi  * 3) In Zen 3+ we then will read each core's INITPK0 values to ensure that we
1834dd23d762SRobert Mustacchi  * have a proper physical to logical mapping, at which point we can fill in the
1835dd23d762SRobert Mustacchi  * APIC IDs. For Zen 2, we will set the AMDZEN_TOPO_CCD_F_CORE_PHYS_UNKNOWN to
1836dd23d762SRobert Mustacchi  * indicate that we just mapped the first logical processor to the first enabled
1837dd23d762SRobert Mustacchi  * core.
1838dd23d762SRobert Mustacchi  *
1839dd23d762SRobert Mustacchi  * 4) Once we have the logical IDs determined we will construct the APIC ID that
1840dd23d762SRobert Mustacchi  * we expect this to have.
1841dd23d762SRobert Mustacchi  *
1842dd23d762SRobert Mustacchi  * Steps (2) - (4) are intertwined and done together.
1843dd23d762SRobert Mustacchi  */
1844dd23d762SRobert Mustacchi static void
amdzen_ccd_fill_topo(amdzen_t * azn,amdzen_df_t * df,amdzen_df_ent_t * ent,amdzen_topo_ccd_t * ccd)1845dd23d762SRobert Mustacchi amdzen_ccd_fill_topo(amdzen_t *azn, amdzen_df_t *df, amdzen_df_ent_t *ent,
1846dd23d762SRobert Mustacchi     amdzen_topo_ccd_t *ccd)
1847dd23d762SRobert Mustacchi {
1848*019df03dSRobert Mustacchi 	uint32_t nccx, core_en, thread_en;
1849dd23d762SRobert Mustacchi 	uint32_t nlcore_per_ccx, nthreads_per_core;
1850dd23d762SRobert Mustacchi 	uint32_t sockid, dieid, compid;
1851dd23d762SRobert Mustacchi 	const uint32_t ccdno = ccd->atccd_phys_no;
1852*019df03dSRobert Mustacchi 	const x86_uarch_t uarch = uarchrev_uarch(azn->azn_uarchrev);
1853*019df03dSRobert Mustacchi 	boolean_t pkg0_ids, logccd_set = B_FALSE;
1854dd23d762SRobert Mustacchi 
1855dd23d762SRobert Mustacchi 	ASSERT(MUTEX_HELD(&azn->azn_mutex));
1856dd23d762SRobert Mustacchi 	if (!amdzen_ccd_present(azn, df, ccdno)) {
1857dd23d762SRobert Mustacchi 		ccd->atccd_err = AMDZEN_TOPO_CCD_E_CCD_MISSING;
1858dd23d762SRobert Mustacchi 		return;
1859dd23d762SRobert Mustacchi 	}
1860dd23d762SRobert Mustacchi 
1861*019df03dSRobert Mustacchi 	amdzen_ccd_info(azn, df, ccdno, &nccx, &nlcore_per_ccx,
1862*019df03dSRobert Mustacchi 	    &nthreads_per_core);
1863dd23d762SRobert Mustacchi 	ASSERT3U(nccx, <=, AMDZEN_TOPO_CCD_MAX_CCX);
1864dd23d762SRobert Mustacchi 
1865*019df03dSRobert Mustacchi 	core_en = amdzen_ccd_core_en(azn, df, ccdno);
1866*019df03dSRobert Mustacchi 	thread_en = amdzen_ccd_thread_en(azn, df, ccdno);
1867dd23d762SRobert Mustacchi 
1868dd23d762SRobert Mustacchi 	/*
1869dd23d762SRobert Mustacchi 	 * The BSP is never enabled in a conventional sense and therefore the
1870dd23d762SRobert Mustacchi 	 * bit is reserved and left as 0. As the BSP should be in the first CCD,
1871dd23d762SRobert Mustacchi 	 * we go through and OR back in the bit lest we think the thread isn't
1872dd23d762SRobert Mustacchi 	 * enabled.
1873dd23d762SRobert Mustacchi 	 */
1874dd23d762SRobert Mustacchi 	if (ccdno == 0) {
1875dd23d762SRobert Mustacchi 		thread_en |= 1;
1876dd23d762SRobert Mustacchi 	}
1877dd23d762SRobert Mustacchi 
1878dd23d762SRobert Mustacchi 	ccd->atccd_phys_no = ccdno;
1879dd23d762SRobert Mustacchi 	if (uarch >= X86_UARCH_AMD_ZEN3) {
1880dd23d762SRobert Mustacchi 		pkg0_ids = B_TRUE;
1881dd23d762SRobert Mustacchi 	} else {
1882dd23d762SRobert Mustacchi 		ccd->atccd_flags |= AMDZEN_TOPO_CCD_F_CORE_PHYS_UNKNOWN;
1883dd23d762SRobert Mustacchi 		pkg0_ids = B_FALSE;
1884dd23d762SRobert Mustacchi 
1885dd23d762SRobert Mustacchi 		/*
1886dd23d762SRobert Mustacchi 		 * Determine the CCD logical ID for Zen 2 now since this doesn't
1887dd23d762SRobert Mustacchi 		 * rely upon needing a valid physical core.
1888dd23d762SRobert Mustacchi 		 */
1889dd23d762SRobert Mustacchi 		ccd->atccd_log_no = amdzen_ccd_log_id_zen2(azn, df, ent);
1890dd23d762SRobert Mustacchi 		logccd_set = B_TRUE;
1891dd23d762SRobert Mustacchi 	}
1892dd23d762SRobert Mustacchi 
1893dd23d762SRobert Mustacchi 	/*
1894dd23d762SRobert Mustacchi 	 * To construct the APIC ID we need to know the socket and die (not CCD)
1895dd23d762SRobert Mustacchi 	 * this is on. We deconstruct the CCD's fabric ID to determine that.
1896dd23d762SRobert Mustacchi 	 */
1897dd23d762SRobert Mustacchi 	zen_fabric_id_decompose(&df->adf_decomp, ent->adfe_fabric_id, &sockid,
1898dd23d762SRobert Mustacchi 	    &dieid, &compid);
1899dd23d762SRobert Mustacchi 
1900dd23d762SRobert Mustacchi 	/*
1901dd23d762SRobert Mustacchi 	 * At this point we have all the information about the CCD, the number
1902dd23d762SRobert Mustacchi 	 * of CCX instances, and which physical cores and threads are enabled.
1903dd23d762SRobert Mustacchi 	 * Currently we assume that if we have one CCX enabled, then it is
1904dd23d762SRobert Mustacchi 	 * always CCX0. We cannot find evidence of a two CCX supporting part
1905dd23d762SRobert Mustacchi 	 * that doesn't always ship with both CCXs present and enabled.
1906dd23d762SRobert Mustacchi 	 */
1907dd23d762SRobert Mustacchi 	ccd->atccd_nlog_ccx = ccd->atccd_nphys_ccx = nccx;
1908dd23d762SRobert Mustacchi 	for (uint32_t ccxno = 0; ccxno < nccx; ccxno++) {
1909dd23d762SRobert Mustacchi 		amdzen_topo_ccx_t *ccx = &ccd->atccd_ccx[ccxno];
1910dd23d762SRobert Mustacchi 		const uint32_t core_mask = (1 << azn->azn_ncore_per_ccx) - 1;
1911dd23d762SRobert Mustacchi 		const uint32_t core_shift = ccxno * azn->azn_ncore_per_ccx;
1912dd23d762SRobert Mustacchi 		const uint32_t ccx_core_en = (core_en >> core_shift) &
1913dd23d762SRobert Mustacchi 		    core_mask;
1914dd23d762SRobert Mustacchi 		boolean_t logccx_set = B_FALSE;
1915dd23d762SRobert Mustacchi 
1916dd23d762SRobert Mustacchi 		ccd->atccd_ccx_en[ccxno] = 1;
1917dd23d762SRobert Mustacchi 		ccx->atccx_phys_no = ccxno;
1918dd23d762SRobert Mustacchi 		ccx->atccx_nphys_cores = azn->azn_ncore_per_ccx;
1919dd23d762SRobert Mustacchi 		ccx->atccx_nlog_cores = nlcore_per_ccx;
1920dd23d762SRobert Mustacchi 
1921dd23d762SRobert Mustacchi 		if (!pkg0_ids) {
1922dd23d762SRobert Mustacchi 			ccx->atccx_log_no = ccx->atccx_phys_no;
1923dd23d762SRobert Mustacchi 			logccx_set = B_TRUE;
1924dd23d762SRobert Mustacchi 		}
1925dd23d762SRobert Mustacchi 
1926dd23d762SRobert Mustacchi 		for (uint32_t coreno = 0, logcorezen2 = 0;
1927dd23d762SRobert Mustacchi 		    coreno < azn->azn_ncore_per_ccx; coreno++) {
1928dd23d762SRobert Mustacchi 			amdzen_topo_core_t *core = &ccx->atccx_cores[coreno];
1929dd23d762SRobert Mustacchi 
1930dd23d762SRobert Mustacchi 			if ((ccx_core_en & (1 << coreno)) == 0) {
1931dd23d762SRobert Mustacchi 				continue;
1932dd23d762SRobert Mustacchi 			}
1933dd23d762SRobert Mustacchi 
1934dd23d762SRobert Mustacchi 			ccx->atccx_core_en[coreno] = 1;
1935dd23d762SRobert Mustacchi 			core->atcore_phys_no = coreno;
1936dd23d762SRobert Mustacchi 
1937dd23d762SRobert Mustacchi 			/*
1938dd23d762SRobert Mustacchi 			 * Now that we have the physical core number present, we
1939dd23d762SRobert Mustacchi 			 * must determine the logical core number and fill out
1940dd23d762SRobert Mustacchi 			 * the logical CCX/CCD if it has not been set. We must
1941dd23d762SRobert Mustacchi 			 * do this before we attempt to look at which threads
1942dd23d762SRobert Mustacchi 			 * are enabled, because that operates based upon logical
1943dd23d762SRobert Mustacchi 			 * core number.
1944dd23d762SRobert Mustacchi 			 *
1945dd23d762SRobert Mustacchi 			 * For Zen 2 we do not have INITPKG0 at our disposal. We
1946dd23d762SRobert Mustacchi 			 * currently assume (and tag for userland with the
1947dd23d762SRobert Mustacchi 			 * AMDZEN_TOPO_CCD_F_CORE_PHYS_UNKNOWN flag) that we are
1948dd23d762SRobert Mustacchi 			 * mapping logical cores to physicals in the order of
1949dd23d762SRobert Mustacchi 			 * appearance.
1950dd23d762SRobert Mustacchi 			 */
1951dd23d762SRobert Mustacchi 			if (pkg0_ids) {
1952dd23d762SRobert Mustacchi 				amdzen_ccd_fill_core_initpkg0(azn, df, ccd, ccx,
1953dd23d762SRobert Mustacchi 				    core, &logccd_set, &logccx_set);
1954dd23d762SRobert Mustacchi 			} else {
1955dd23d762SRobert Mustacchi 				core->atcore_log_no = logcorezen2;
1956dd23d762SRobert Mustacchi 				logcorezen2++;
1957dd23d762SRobert Mustacchi 			}
1958dd23d762SRobert Mustacchi 
1959dd23d762SRobert Mustacchi 			/*
1960dd23d762SRobert Mustacchi 			 * Determining which bits to use for the thread is a bit
1961dd23d762SRobert Mustacchi 			 * weird here. Thread IDs within a CCX are logical, but
1962dd23d762SRobert Mustacchi 			 * there are always physically spaced CCX sizes. See the
1963dd23d762SRobert Mustacchi 			 * comment at the definition for SMU::PWR::THREAD_ENABLE
1964dd23d762SRobert Mustacchi 			 * for more information.
1965dd23d762SRobert Mustacchi 			 */
1966dd23d762SRobert Mustacchi 			const uint32_t thread_shift = (ccx->atccx_nphys_cores *
1967dd23d762SRobert Mustacchi 			    ccx->atccx_log_no + core->atcore_log_no) *
1968dd23d762SRobert Mustacchi 			    nthreads_per_core;
1969dd23d762SRobert Mustacchi 			const uint32_t thread_mask = (nthreads_per_core << 1) -
1970dd23d762SRobert Mustacchi 			    1;
1971dd23d762SRobert Mustacchi 			const uint32_t core_thread_en = (thread_en >>
1972dd23d762SRobert Mustacchi 			    thread_shift) & thread_mask;
1973dd23d762SRobert Mustacchi 			core->atcore_nthreads = nthreads_per_core;
1974dd23d762SRobert Mustacchi 			core->atcore_thr_en[0] = core_thread_en & 0x01;
1975dd23d762SRobert Mustacchi 			core->atcore_thr_en[1] = core_thread_en & 0x02;
1976dd23d762SRobert Mustacchi #ifdef	DEBUG
1977dd23d762SRobert Mustacchi 			if (nthreads_per_core == 1) {
1978dd23d762SRobert Mustacchi 				VERIFY0(core->atcore_thr_en[1]);
1979dd23d762SRobert Mustacchi 			}
1980dd23d762SRobert Mustacchi #endif
1981dd23d762SRobert Mustacchi 			for (uint32_t thrno = 0; thrno < core->atcore_nthreads;
1982dd23d762SRobert Mustacchi 			    thrno++) {
1983dd23d762SRobert Mustacchi 				ASSERT3U(core->atcore_thr_en[thrno], !=, 0);
1984dd23d762SRobert Mustacchi 
1985dd23d762SRobert Mustacchi 				zen_apic_id_compose(&azn->azn_apic_decomp,
1986dd23d762SRobert Mustacchi 				    sockid, dieid, ccd->atccd_log_no,
1987dd23d762SRobert Mustacchi 				    ccx->atccx_log_no, core->atcore_log_no,
1988dd23d762SRobert Mustacchi 				    thrno, &core->atcore_apicids[thrno]);
1989dd23d762SRobert Mustacchi 
1990dd23d762SRobert Mustacchi 			}
1991dd23d762SRobert Mustacchi 		}
1992dd23d762SRobert Mustacchi 
1993dd23d762SRobert Mustacchi 		ASSERT3U(logccx_set, ==, B_TRUE);
1994dd23d762SRobert Mustacchi 		ASSERT3U(logccd_set, ==, B_TRUE);
1995dd23d762SRobert Mustacchi 	}
1996dd23d762SRobert Mustacchi }
1997dd23d762SRobert Mustacchi 
1998dd23d762SRobert Mustacchi static void
amdzen_nexus_init(void * arg)1999047043c2SRobert Mustacchi amdzen_nexus_init(void *arg)
2000047043c2SRobert Mustacchi {
2001047043c2SRobert Mustacchi 	uint_t i;
2002047043c2SRobert Mustacchi 	amdzen_t *azn = arg;
2003047043c2SRobert Mustacchi 
2004047043c2SRobert Mustacchi 	/*
2005*019df03dSRobert Mustacchi 	 * Assign the requisite identifying information for this CPU.
2006*019df03dSRobert Mustacchi 	 */
2007*019df03dSRobert Mustacchi 	azn->azn_uarchrev = cpuid_getuarchrev(CPU);
2008*019df03dSRobert Mustacchi 	azn->azn_chiprev = cpuid_getchiprev(CPU);
2009*019df03dSRobert Mustacchi 
2010*019df03dSRobert Mustacchi 	/*
2011*019df03dSRobert Mustacchi 	 * Go through all of the stubs and assign the DF entries.
2012047043c2SRobert Mustacchi 	 */
2013047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
2014047043c2SRobert Mustacchi 	if (!amdzen_map_dfs(azn) || !amdzen_check_dfs(azn)) {
2015047043c2SRobert Mustacchi 		azn->azn_flags |= AMDZEN_F_MAP_ERROR;
2016047043c2SRobert Mustacchi 		goto done;
2017047043c2SRobert Mustacchi 	}
2018047043c2SRobert Mustacchi 
2019047043c2SRobert Mustacchi 	for (i = 0; i < AMDZEN_MAX_DFS; i++) {
2020047043c2SRobert Mustacchi 		amdzen_df_t *df = &azn->azn_dfs[i];
2021047043c2SRobert Mustacchi 
2022047043c2SRobert Mustacchi 		if ((df->adf_flags & AMDZEN_DF_F_VALID) == 0)
2023047043c2SRobert Mustacchi 			continue;
2024047043c2SRobert Mustacchi 		amdzen_setup_df(azn, df);
2025047043c2SRobert Mustacchi 		amdzen_find_nb(azn, df);
2026047043c2SRobert Mustacchi 	}
2027047043c2SRobert Mustacchi 
2028*019df03dSRobert Mustacchi 	amdzen_determine_ncore_per_ccx(azn);
2029*019df03dSRobert Mustacchi 
2030dd23d762SRobert Mustacchi 	if (amdzen_determine_apic_decomp(azn)) {
2031dd23d762SRobert Mustacchi 		azn->azn_flags |= AMDZEN_F_APIC_DECOMP_VALID;
2032dd23d762SRobert Mustacchi 	}
2033dd23d762SRobert Mustacchi 
2034047043c2SRobert Mustacchi 	/*
2035047043c2SRobert Mustacchi 	 * Not all children may be installed. As such, we do not treat the
2036047043c2SRobert Mustacchi 	 * failure of a child as fatal to the driver.
2037047043c2SRobert Mustacchi 	 */
2038047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
2039047043c2SRobert Mustacchi 	for (i = 0; i < ARRAY_SIZE(amdzen_children); i++) {
2040047043c2SRobert Mustacchi 		(void) amdzen_create_child(azn, &amdzen_children[i]);
2041047043c2SRobert Mustacchi 	}
2042047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
2043047043c2SRobert Mustacchi 
2044047043c2SRobert Mustacchi done:
2045047043c2SRobert Mustacchi 	azn->azn_flags &= ~AMDZEN_F_ATTACH_DISPATCHED;
2046047043c2SRobert Mustacchi 	azn->azn_flags |= AMDZEN_F_ATTACH_COMPLETE;
2047047043c2SRobert Mustacchi 	azn->azn_taskqid = TASKQID_INVALID;
2048047043c2SRobert Mustacchi 	cv_broadcast(&azn->azn_cv);
2049047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
2050047043c2SRobert Mustacchi }
2051047043c2SRobert Mustacchi 
2052047043c2SRobert Mustacchi static int
amdzen_stub_scan_cb(dev_info_t * dip,void * arg)2053047043c2SRobert Mustacchi amdzen_stub_scan_cb(dev_info_t *dip, void *arg)
2054047043c2SRobert Mustacchi {
2055047043c2SRobert Mustacchi 	amdzen_t *azn = arg;
2056047043c2SRobert Mustacchi 	uint16_t vid, did;
2057047043c2SRobert Mustacchi 	int *regs;
2058047043c2SRobert Mustacchi 	uint_t nregs, i;
2059047043c2SRobert Mustacchi 	boolean_t match = B_FALSE;
2060047043c2SRobert Mustacchi 
2061047043c2SRobert Mustacchi 	if (dip == ddi_root_node()) {
2062047043c2SRobert Mustacchi 		return (DDI_WALK_CONTINUE);
2063047043c2SRobert Mustacchi 	}
2064047043c2SRobert Mustacchi 
2065047043c2SRobert Mustacchi 	/*
2066047043c2SRobert Mustacchi 	 * If a node in question is not a pci node, then we have no interest in
2067047043c2SRobert Mustacchi 	 * it as all the stubs that we care about are related to pci devices.
2068047043c2SRobert Mustacchi 	 */
2069047043c2SRobert Mustacchi 	if (strncmp("pci", ddi_get_name(dip), 3) != 0) {
2070047043c2SRobert Mustacchi 		return (DDI_WALK_PRUNECHILD);
2071047043c2SRobert Mustacchi 	}
2072047043c2SRobert Mustacchi 
2073047043c2SRobert Mustacchi 	/*
2074047043c2SRobert Mustacchi 	 * If we can't get a device or vendor ID and prove that this is an AMD
2075047043c2SRobert Mustacchi 	 * part, then we don't care about it.
2076047043c2SRobert Mustacchi 	 */
2077047043c2SRobert Mustacchi 	vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
2078047043c2SRobert Mustacchi 	    "vendor-id", PCI_EINVAL16);
2079047043c2SRobert Mustacchi 	did = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
2080047043c2SRobert Mustacchi 	    "device-id", PCI_EINVAL16);
2081047043c2SRobert Mustacchi 	if (vid == PCI_EINVAL16 || did == PCI_EINVAL16) {
2082047043c2SRobert Mustacchi 		return (DDI_WALK_CONTINUE);
2083047043c2SRobert Mustacchi 	}
2084047043c2SRobert Mustacchi 
20859b0429a1SPu Wen 	if (vid != AMDZEN_PCI_VID_AMD && vid != AMDZEN_PCI_VID_HYGON) {
2086047043c2SRobert Mustacchi 		return (DDI_WALK_CONTINUE);
2087047043c2SRobert Mustacchi 	}
2088047043c2SRobert Mustacchi 
2089047043c2SRobert Mustacchi 	for (i = 0; i < ARRAY_SIZE(amdzen_nb_ids); i++) {
2090047043c2SRobert Mustacchi 		if (amdzen_nb_ids[i] == did) {
2091047043c2SRobert Mustacchi 			match = B_TRUE;
2092047043c2SRobert Mustacchi 		}
2093047043c2SRobert Mustacchi 	}
2094047043c2SRobert Mustacchi 
2095047043c2SRobert Mustacchi 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
2096047043c2SRobert Mustacchi 	    "reg", &regs, &nregs) != DDI_PROP_SUCCESS) {
2097047043c2SRobert Mustacchi 		return (DDI_WALK_CONTINUE);
2098047043c2SRobert Mustacchi 	}
2099047043c2SRobert Mustacchi 
2100047043c2SRobert Mustacchi 	if (nregs == 0) {
2101047043c2SRobert Mustacchi 		ddi_prop_free(regs);
2102047043c2SRobert Mustacchi 		return (DDI_WALK_CONTINUE);
2103047043c2SRobert Mustacchi 	}
2104047043c2SRobert Mustacchi 
2105047043c2SRobert Mustacchi 	if (PCI_REG_BUS_G(regs[0]) == AMDZEN_DF_BUSNO &&
2106047043c2SRobert Mustacchi 	    PCI_REG_DEV_G(regs[0]) >= AMDZEN_DF_FIRST_DEVICE) {
2107047043c2SRobert Mustacchi 		match = B_TRUE;
2108047043c2SRobert Mustacchi 	}
2109047043c2SRobert Mustacchi 
2110047043c2SRobert Mustacchi 	ddi_prop_free(regs);
2111047043c2SRobert Mustacchi 	if (match) {
2112047043c2SRobert Mustacchi 		mutex_enter(&azn->azn_mutex);
2113047043c2SRobert Mustacchi 		azn->azn_nscanned++;
2114047043c2SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
2115047043c2SRobert Mustacchi 	}
2116047043c2SRobert Mustacchi 
2117047043c2SRobert Mustacchi 	return (DDI_WALK_CONTINUE);
2118047043c2SRobert Mustacchi }
2119047043c2SRobert Mustacchi 
2120047043c2SRobert Mustacchi static void
amdzen_stub_scan(void * arg)2121047043c2SRobert Mustacchi amdzen_stub_scan(void *arg)
2122047043c2SRobert Mustacchi {
2123047043c2SRobert Mustacchi 	amdzen_t *azn = arg;
2124047043c2SRobert Mustacchi 
2125047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
2126047043c2SRobert Mustacchi 	azn->azn_nscanned = 0;
2127047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
2128047043c2SRobert Mustacchi 
2129047043c2SRobert Mustacchi 	ddi_walk_devs(ddi_root_node(), amdzen_stub_scan_cb, azn);
2130047043c2SRobert Mustacchi 
2131047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
2132047043c2SRobert Mustacchi 	azn->azn_flags &= ~AMDZEN_F_SCAN_DISPATCHED;
2133047043c2SRobert Mustacchi 	azn->azn_flags |= AMDZEN_F_SCAN_COMPLETE;
2134047043c2SRobert Mustacchi 
2135047043c2SRobert Mustacchi 	if (azn->azn_nscanned == 0) {
2136047043c2SRobert Mustacchi 		azn->azn_flags |= AMDZEN_F_UNSUPPORTED;
2137047043c2SRobert Mustacchi 		azn->azn_taskqid = TASKQID_INVALID;
2138047043c2SRobert Mustacchi 		cv_broadcast(&azn->azn_cv);
2139047043c2SRobert Mustacchi 	} else if (azn->azn_npresent == azn->azn_nscanned) {
2140047043c2SRobert Mustacchi 		azn->azn_flags |= AMDZEN_F_ATTACH_DISPATCHED;
2141047043c2SRobert Mustacchi 		azn->azn_taskqid = taskq_dispatch(system_taskq,
2142047043c2SRobert Mustacchi 		    amdzen_nexus_init, azn, TQ_SLEEP);
2143047043c2SRobert Mustacchi 	}
2144047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
2145047043c2SRobert Mustacchi }
2146047043c2SRobert Mustacchi 
2147047043c2SRobert Mustacchi /*
2148047043c2SRobert Mustacchi  * Unfortunately we can't really let the stubs detach as we may need them to be
2149047043c2SRobert Mustacchi  * available for client operations. We may be able to improve this if we know
2150047043c2SRobert Mustacchi  * that the actual nexus is going away. However, as long as it's active, we need
2151047043c2SRobert Mustacchi  * all the stubs.
2152047043c2SRobert Mustacchi  */
2153047043c2SRobert Mustacchi int
amdzen_detach_stub(dev_info_t * dip,ddi_detach_cmd_t cmd)2154047043c2SRobert Mustacchi amdzen_detach_stub(dev_info_t *dip, ddi_detach_cmd_t cmd)
2155047043c2SRobert Mustacchi {
2156047043c2SRobert Mustacchi 	if (cmd == DDI_SUSPEND) {
2157047043c2SRobert Mustacchi 		return (DDI_SUCCESS);
2158047043c2SRobert Mustacchi 	}
2159047043c2SRobert Mustacchi 
2160047043c2SRobert Mustacchi 	return (DDI_FAILURE);
2161047043c2SRobert Mustacchi }
2162047043c2SRobert Mustacchi 
2163047043c2SRobert Mustacchi int
amdzen_attach_stub(dev_info_t * dip,ddi_attach_cmd_t cmd)2164047043c2SRobert Mustacchi amdzen_attach_stub(dev_info_t *dip, ddi_attach_cmd_t cmd)
2165047043c2SRobert Mustacchi {
2166047043c2SRobert Mustacchi 	int *regs, reg;
2167047043c2SRobert Mustacchi 	uint_t nregs, i;
2168047043c2SRobert Mustacchi 	uint16_t vid, did;
2169047043c2SRobert Mustacchi 	amdzen_stub_t *stub;
2170047043c2SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
2171047043c2SRobert Mustacchi 	boolean_t valid = B_FALSE;
2172047043c2SRobert Mustacchi 	boolean_t nb = B_FALSE;
2173047043c2SRobert Mustacchi 
2174047043c2SRobert Mustacchi 	if (cmd == DDI_RESUME) {
2175047043c2SRobert Mustacchi 		return (DDI_SUCCESS);
2176047043c2SRobert Mustacchi 	} else if (cmd != DDI_ATTACH) {
2177047043c2SRobert Mustacchi 		return (DDI_FAILURE);
2178047043c2SRobert Mustacchi 	}
2179047043c2SRobert Mustacchi 
2180047043c2SRobert Mustacchi 	/*
2181047043c2SRobert Mustacchi 	 * Make sure that the stub that we've been asked to attach is a pci type
2182047043c2SRobert Mustacchi 	 * device. If not, then there is no reason for us to proceed.
2183047043c2SRobert Mustacchi 	 */
2184047043c2SRobert Mustacchi 	if (strncmp("pci", ddi_get_name(dip), 3) != 0) {
2185047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "asked to attach a bad AMD Zen nexus "
2186047043c2SRobert Mustacchi 		    "stub: %s", ddi_get_name(dip));
2187047043c2SRobert Mustacchi 		return (DDI_FAILURE);
2188047043c2SRobert Mustacchi 	}
2189047043c2SRobert Mustacchi 	vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
2190047043c2SRobert Mustacchi 	    "vendor-id", PCI_EINVAL16);
2191047043c2SRobert Mustacchi 	did = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
2192047043c2SRobert Mustacchi 	    "device-id", PCI_EINVAL16);
2193047043c2SRobert Mustacchi 	if (vid == PCI_EINVAL16 || did == PCI_EINVAL16) {
2194047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "failed to get PCI ID properties");
2195047043c2SRobert Mustacchi 		return (DDI_FAILURE);
2196047043c2SRobert Mustacchi 	}
2197047043c2SRobert Mustacchi 
21989b0429a1SPu Wen 	if (vid != AMDZEN_PCI_VID_AMD && vid != AMDZEN_PCI_VID_HYGON) {
21999b0429a1SPu Wen 		dev_err(dip, CE_WARN, "expected vendor ID (0x%x), found 0x%x",
22009b0429a1SPu Wen 		    cpuid_getvendor(CPU) == X86_VENDOR_HYGON ?
22019b0429a1SPu Wen 		    AMDZEN_PCI_VID_HYGON : AMDZEN_PCI_VID_AMD, vid);
2202047043c2SRobert Mustacchi 		return (DDI_FAILURE);
2203047043c2SRobert Mustacchi 	}
2204047043c2SRobert Mustacchi 
2205047043c2SRobert Mustacchi 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
2206047043c2SRobert Mustacchi 	    "reg", &regs, &nregs) != DDI_PROP_SUCCESS) {
2207047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "failed to get 'reg' property");
2208047043c2SRobert Mustacchi 		return (DDI_FAILURE);
2209047043c2SRobert Mustacchi 	}
2210047043c2SRobert Mustacchi 
2211047043c2SRobert Mustacchi 	if (nregs == 0) {
2212047043c2SRobert Mustacchi 		ddi_prop_free(regs);
2213047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "missing 'reg' property values");
2214047043c2SRobert Mustacchi 		return (DDI_FAILURE);
2215047043c2SRobert Mustacchi 	}
2216047043c2SRobert Mustacchi 	reg = *regs;
2217047043c2SRobert Mustacchi 	ddi_prop_free(regs);
2218047043c2SRobert Mustacchi 
2219047043c2SRobert Mustacchi 	for (i = 0; i < ARRAY_SIZE(amdzen_nb_ids); i++) {
2220047043c2SRobert Mustacchi 		if (amdzen_nb_ids[i] == did) {
2221047043c2SRobert Mustacchi 			valid = B_TRUE;
2222047043c2SRobert Mustacchi 			nb = B_TRUE;
2223047043c2SRobert Mustacchi 		}
2224047043c2SRobert Mustacchi 	}
2225047043c2SRobert Mustacchi 
2226047043c2SRobert Mustacchi 	if (!valid && PCI_REG_BUS_G(reg) == AMDZEN_DF_BUSNO &&
2227047043c2SRobert Mustacchi 	    PCI_REG_DEV_G(reg) >= AMDZEN_DF_FIRST_DEVICE) {
2228047043c2SRobert Mustacchi 		valid = B_TRUE;
2229047043c2SRobert Mustacchi 		nb = B_FALSE;
2230047043c2SRobert Mustacchi 	}
2231047043c2SRobert Mustacchi 
2232047043c2SRobert Mustacchi 	if (!valid) {
2233047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "device %s didn't match the nexus list",
2234047043c2SRobert Mustacchi 		    ddi_get_name(dip));
2235047043c2SRobert Mustacchi 		return (DDI_FAILURE);
2236047043c2SRobert Mustacchi 	}
2237047043c2SRobert Mustacchi 
2238047043c2SRobert Mustacchi 	stub = kmem_alloc(sizeof (amdzen_stub_t), KM_SLEEP);
2239047043c2SRobert Mustacchi 	if (pci_config_setup(dip, &stub->azns_cfgspace) != DDI_SUCCESS) {
2240047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "failed to set up config space");
2241047043c2SRobert Mustacchi 		kmem_free(stub, sizeof (amdzen_stub_t));
2242047043c2SRobert Mustacchi 		return (DDI_FAILURE);
2243047043c2SRobert Mustacchi 	}
2244047043c2SRobert Mustacchi 
2245047043c2SRobert Mustacchi 	stub->azns_dip = dip;
2246047043c2SRobert Mustacchi 	stub->azns_vid = vid;
2247047043c2SRobert Mustacchi 	stub->azns_did = did;
2248047043c2SRobert Mustacchi 	stub->azns_bus = PCI_REG_BUS_G(reg);
2249047043c2SRobert Mustacchi 	stub->azns_dev = PCI_REG_DEV_G(reg);
2250047043c2SRobert Mustacchi 	stub->azns_func = PCI_REG_FUNC_G(reg);
2251047043c2SRobert Mustacchi 	ddi_set_driver_private(dip, stub);
2252047043c2SRobert Mustacchi 
2253047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
2254047043c2SRobert Mustacchi 	azn->azn_npresent++;
2255047043c2SRobert Mustacchi 	if (nb) {
2256047043c2SRobert Mustacchi 		list_insert_tail(&azn->azn_nb_stubs, stub);
2257047043c2SRobert Mustacchi 	} else {
2258047043c2SRobert Mustacchi 		list_insert_tail(&azn->azn_df_stubs, stub);
2259047043c2SRobert Mustacchi 	}
2260047043c2SRobert Mustacchi 
2261047043c2SRobert Mustacchi 	if ((azn->azn_flags & AMDZEN_F_TASKQ_MASK) == AMDZEN_F_SCAN_COMPLETE &&
2262047043c2SRobert Mustacchi 	    azn->azn_nscanned == azn->azn_npresent) {
2263047043c2SRobert Mustacchi 		azn->azn_flags |= AMDZEN_F_ATTACH_DISPATCHED;
2264047043c2SRobert Mustacchi 		azn->azn_taskqid = taskq_dispatch(system_taskq,
2265047043c2SRobert Mustacchi 		    amdzen_nexus_init, azn, TQ_SLEEP);
2266047043c2SRobert Mustacchi 	}
2267047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
2268047043c2SRobert Mustacchi 
2269047043c2SRobert Mustacchi 	return (DDI_SUCCESS);
2270047043c2SRobert Mustacchi }
2271047043c2SRobert Mustacchi 
2272047043c2SRobert Mustacchi static int
amdzen_bus_ctl(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)2273047043c2SRobert Mustacchi amdzen_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
2274047043c2SRobert Mustacchi     void *arg, void *result)
2275047043c2SRobert Mustacchi {
2276047043c2SRobert Mustacchi 	char buf[32];
2277047043c2SRobert Mustacchi 	dev_info_t *child;
2278047043c2SRobert Mustacchi 	const amdzen_child_data_t *acd;
2279047043c2SRobert Mustacchi 
2280047043c2SRobert Mustacchi 	switch (ctlop) {
2281047043c2SRobert Mustacchi 	case DDI_CTLOPS_REPORTDEV:
2282047043c2SRobert Mustacchi 		if (rdip == NULL) {
2283047043c2SRobert Mustacchi 			return (DDI_FAILURE);
2284047043c2SRobert Mustacchi 		}
2285047043c2SRobert Mustacchi 		cmn_err(CE_CONT, "amdzen nexus: %s@%s, %s%d\n",
2286047043c2SRobert Mustacchi 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
2287047043c2SRobert Mustacchi 		    ddi_driver_name(rdip), ddi_get_instance(rdip));
2288047043c2SRobert Mustacchi 		break;
2289047043c2SRobert Mustacchi 	case DDI_CTLOPS_INITCHILD:
2290047043c2SRobert Mustacchi 		child = arg;
2291047043c2SRobert Mustacchi 		if (child == NULL) {
2292047043c2SRobert Mustacchi 			dev_err(dip, CE_WARN, "!no child passed for "
2293047043c2SRobert Mustacchi 			    "DDI_CTLOPS_INITCHILD");
2294047043c2SRobert Mustacchi 		}
2295047043c2SRobert Mustacchi 
2296047043c2SRobert Mustacchi 		acd = ddi_get_parent_data(child);
2297047043c2SRobert Mustacchi 		if (acd == NULL) {
2298047043c2SRobert Mustacchi 			dev_err(dip, CE_WARN, "!missing child parent data");
2299047043c2SRobert Mustacchi 			return (DDI_FAILURE);
2300047043c2SRobert Mustacchi 		}
2301047043c2SRobert Mustacchi 
2302047043c2SRobert Mustacchi 		if (snprintf(buf, sizeof (buf), "%d", acd->acd_addr) >=
2303047043c2SRobert Mustacchi 		    sizeof (buf)) {
2304047043c2SRobert Mustacchi 			dev_err(dip, CE_WARN, "!failed to construct device "
2305047043c2SRobert Mustacchi 			    "addr due to overflow");
2306047043c2SRobert Mustacchi 			return (DDI_FAILURE);
2307047043c2SRobert Mustacchi 		}
2308047043c2SRobert Mustacchi 
2309047043c2SRobert Mustacchi 		ddi_set_name_addr(child, buf);
2310047043c2SRobert Mustacchi 		break;
2311047043c2SRobert Mustacchi 	case DDI_CTLOPS_UNINITCHILD:
2312047043c2SRobert Mustacchi 		child = arg;
2313047043c2SRobert Mustacchi 		if (child == NULL) {
2314047043c2SRobert Mustacchi 			dev_err(dip, CE_WARN, "!no child passed for "
2315047043c2SRobert Mustacchi 			    "DDI_CTLOPS_UNINITCHILD");
2316047043c2SRobert Mustacchi 		}
2317047043c2SRobert Mustacchi 
2318047043c2SRobert Mustacchi 		ddi_set_name_addr(child, NULL);
2319047043c2SRobert Mustacchi 		break;
2320047043c2SRobert Mustacchi 	default:
2321047043c2SRobert Mustacchi 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
2322047043c2SRobert Mustacchi 	}
2323047043c2SRobert Mustacchi 	return (DDI_SUCCESS);
2324047043c2SRobert Mustacchi }
2325047043c2SRobert Mustacchi 
2326047043c2SRobert Mustacchi static int
amdzen_topo_open(dev_t * devp,int flag,int otyp,cred_t * credp)2327dd23d762SRobert Mustacchi amdzen_topo_open(dev_t *devp, int flag, int otyp, cred_t *credp)
2328dd23d762SRobert Mustacchi {
2329dd23d762SRobert Mustacchi 	minor_t m;
2330dd23d762SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
2331dd23d762SRobert Mustacchi 
2332dd23d762SRobert Mustacchi 	if (crgetzoneid(credp) != GLOBAL_ZONEID ||
2333dd23d762SRobert Mustacchi 	    secpolicy_sys_config(credp, B_FALSE) != 0) {
2334dd23d762SRobert Mustacchi 		return (EPERM);
2335dd23d762SRobert Mustacchi 	}
2336dd23d762SRobert Mustacchi 
2337dd23d762SRobert Mustacchi 	if ((flag & (FEXCL | FNDELAY | FNONBLOCK)) != 0) {
2338dd23d762SRobert Mustacchi 		return (EINVAL);
2339dd23d762SRobert Mustacchi 	}
2340dd23d762SRobert Mustacchi 
2341dd23d762SRobert Mustacchi 	if (otyp != OTYP_CHR) {
2342dd23d762SRobert Mustacchi 		return (EINVAL);
2343dd23d762SRobert Mustacchi 	}
2344dd23d762SRobert Mustacchi 
2345dd23d762SRobert Mustacchi 	m = getminor(*devp);
2346dd23d762SRobert Mustacchi 	if (m != AMDZEN_MINOR_TOPO) {
2347dd23d762SRobert Mustacchi 		return (ENXIO);
2348dd23d762SRobert Mustacchi 	}
2349dd23d762SRobert Mustacchi 
2350dd23d762SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
2351dd23d762SRobert Mustacchi 	if ((azn->azn_flags & AMDZEN_F_IOCTL_MASK) !=
2352dd23d762SRobert Mustacchi 	    AMDZEN_F_ATTACH_COMPLETE) {
2353dd23d762SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
2354dd23d762SRobert Mustacchi 		return (ENOTSUP);
2355dd23d762SRobert Mustacchi 	}
2356dd23d762SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
2357dd23d762SRobert Mustacchi 
2358dd23d762SRobert Mustacchi 	return (0);
2359dd23d762SRobert Mustacchi }
2360dd23d762SRobert Mustacchi 
2361dd23d762SRobert Mustacchi static int
amdzen_topo_ioctl_base(amdzen_t * azn,intptr_t arg,int mode)2362dd23d762SRobert Mustacchi amdzen_topo_ioctl_base(amdzen_t *azn, intptr_t arg, int mode)
2363dd23d762SRobert Mustacchi {
2364dd23d762SRobert Mustacchi 	amdzen_topo_base_t base;
2365dd23d762SRobert Mustacchi 
2366dd23d762SRobert Mustacchi 	bzero(&base, sizeof (base));
2367dd23d762SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
2368dd23d762SRobert Mustacchi 	base.atb_ndf = azn->azn_ndfs;
2369dd23d762SRobert Mustacchi 
2370dd23d762SRobert Mustacchi 	if ((azn->azn_flags & AMDZEN_F_APIC_DECOMP_VALID) == 0) {
2371dd23d762SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
2372dd23d762SRobert Mustacchi 		return (ENOTSUP);
2373dd23d762SRobert Mustacchi 	}
2374dd23d762SRobert Mustacchi 
2375dd23d762SRobert Mustacchi 	base.atb_apic_decomp = azn->azn_apic_decomp;
2376dd23d762SRobert Mustacchi 	for (uint_t i = 0; i < azn->azn_ndfs; i++) {
2377dd23d762SRobert Mustacchi 		const amdzen_df_t *df = &azn->azn_dfs[i];
2378dd23d762SRobert Mustacchi 
2379dd23d762SRobert Mustacchi 		base.atb_maxdfent = MAX(base.atb_maxdfent, df->adf_nents);
2380dd23d762SRobert Mustacchi 		if (i == 0) {
2381dd23d762SRobert Mustacchi 			base.atb_rev = df->adf_rev;
2382dd23d762SRobert Mustacchi 			base.atb_df_decomp = df->adf_decomp;
2383dd23d762SRobert Mustacchi 		}
2384dd23d762SRobert Mustacchi 	}
2385dd23d762SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
2386dd23d762SRobert Mustacchi 
2387dd23d762SRobert Mustacchi 	if (ddi_copyout(&base, (void *)(uintptr_t)arg, sizeof (base),
2388dd23d762SRobert Mustacchi 	    mode & FKIOCTL) != 0) {
2389dd23d762SRobert Mustacchi 		return (EFAULT);
2390dd23d762SRobert Mustacchi 	}
2391dd23d762SRobert Mustacchi 
2392dd23d762SRobert Mustacchi 	return (0);
2393dd23d762SRobert Mustacchi }
2394dd23d762SRobert Mustacchi 
2395dd23d762SRobert Mustacchi /*
2396*019df03dSRobert Mustacchi  * Fill in the peers. We only have this information prior to DF 4D2.  The way we
2397*019df03dSRobert Mustacchi  * do is this is to just fill in all the entries and then zero out the ones that
2398*019df03dSRobert Mustacchi  * aren't valid.
2399dd23d762SRobert Mustacchi  */
2400dd23d762SRobert Mustacchi static void
amdzen_topo_ioctl_df_fill_peers(const amdzen_df_t * df,const amdzen_df_ent_t * ent,amdzen_topo_df_ent_t * topo_ent)2401*019df03dSRobert Mustacchi amdzen_topo_ioctl_df_fill_peers(const amdzen_df_t *df,
2402*019df03dSRobert Mustacchi     const amdzen_df_ent_t *ent, amdzen_topo_df_ent_t *topo_ent)
2403dd23d762SRobert Mustacchi {
2404dd23d762SRobert Mustacchi 	topo_ent->atde_npeers = DF_FBIINFO0_GET_FTI_PCNT(ent->adfe_info0);
2405*019df03dSRobert Mustacchi 
2406*019df03dSRobert Mustacchi 	if (df->adf_rev >= DF_REV_4D2) {
2407*019df03dSRobert Mustacchi 		bzero(topo_ent->atde_peers, sizeof (topo_ent->atde_npeers));
2408*019df03dSRobert Mustacchi 		return;
2409*019df03dSRobert Mustacchi 	}
2410*019df03dSRobert Mustacchi 
2411dd23d762SRobert Mustacchi 	topo_ent->atde_peers[0] = DF_FBINFO1_GET_FTI0_NINSTID(ent->adfe_info1);
2412dd23d762SRobert Mustacchi 	topo_ent->atde_peers[1] = DF_FBINFO1_GET_FTI1_NINSTID(ent->adfe_info1);
2413dd23d762SRobert Mustacchi 	topo_ent->atde_peers[2] = DF_FBINFO1_GET_FTI2_NINSTID(ent->adfe_info1);
2414dd23d762SRobert Mustacchi 	topo_ent->atde_peers[3] = DF_FBINFO1_GET_FTI3_NINSTID(ent->adfe_info1);
2415dd23d762SRobert Mustacchi 	topo_ent->atde_peers[4] = DF_FBINFO2_GET_FTI4_NINSTID(ent->adfe_info2);
2416dd23d762SRobert Mustacchi 	topo_ent->atde_peers[5] = DF_FBINFO2_GET_FTI5_NINSTID(ent->adfe_info2);
2417dd23d762SRobert Mustacchi 
2418dd23d762SRobert Mustacchi 	for (uint32_t i = topo_ent->atde_npeers; i < AMDZEN_TOPO_DF_MAX_PEERS;
2419dd23d762SRobert Mustacchi 	    i++) {
2420dd23d762SRobert Mustacchi 		topo_ent->atde_peers[i] = 0;
2421dd23d762SRobert Mustacchi 	}
2422dd23d762SRobert Mustacchi }
2423dd23d762SRobert Mustacchi 
2424dd23d762SRobert Mustacchi static void
amdzen_topo_ioctl_df_fill_ccm(const amdzen_df_ent_t * ent,amdzen_topo_df_ent_t * topo_ent)2425dd23d762SRobert Mustacchi amdzen_topo_ioctl_df_fill_ccm(const amdzen_df_ent_t *ent,
2426dd23d762SRobert Mustacchi     amdzen_topo_df_ent_t *topo_ent)
2427dd23d762SRobert Mustacchi {
2428dd23d762SRobert Mustacchi 	const amdzen_ccm_data_t *ccm = &ent->adfe_data.aded_ccm;
2429dd23d762SRobert Mustacchi 	amdzen_topo_ccm_data_t *topo_ccm = &topo_ent->atde_data.atded_ccm;
2430dd23d762SRobert Mustacchi 
2431dd23d762SRobert Mustacchi 	topo_ccm->atcd_nccds = ccm->acd_nccds;
2432dd23d762SRobert Mustacchi 	for (uint32_t i = 0; i < DF_MAX_CCDS_PER_CCM; i++) {
2433dd23d762SRobert Mustacchi 		topo_ccm->atcd_ccd_en[i] = ccm->acd_ccd_en[i];
2434dd23d762SRobert Mustacchi 		topo_ccm->atcd_ccd_ids[i] = ccm->acd_ccd_id[i];
2435dd23d762SRobert Mustacchi 	}
2436dd23d762SRobert Mustacchi }
2437dd23d762SRobert Mustacchi 
2438dd23d762SRobert Mustacchi static int
amdzen_topo_ioctl_df(amdzen_t * azn,intptr_t arg,int mode)2439dd23d762SRobert Mustacchi amdzen_topo_ioctl_df(amdzen_t *azn, intptr_t arg, int mode)
2440dd23d762SRobert Mustacchi {
2441dd23d762SRobert Mustacchi 	uint_t model;
2442dd23d762SRobert Mustacchi 	uint32_t max_ents, nwritten;
2443dd23d762SRobert Mustacchi 	const amdzen_df_t *df;
2444dd23d762SRobert Mustacchi 	amdzen_topo_df_t topo_df;
2445dd23d762SRobert Mustacchi #ifdef	_MULTI_DATAMODEL
2446dd23d762SRobert Mustacchi 	amdzen_topo_df32_t topo_df32;
2447dd23d762SRobert Mustacchi #endif
2448dd23d762SRobert Mustacchi 
2449dd23d762SRobert Mustacchi 	model = ddi_model_convert_from(mode);
2450dd23d762SRobert Mustacchi 	switch (model) {
2451dd23d762SRobert Mustacchi #ifdef	_MULTI_DATAMODEL
2452dd23d762SRobert Mustacchi 	case DDI_MODEL_ILP32:
2453dd23d762SRobert Mustacchi 		if (ddi_copyin((void *)(uintptr_t)arg, &topo_df32,
2454dd23d762SRobert Mustacchi 		    sizeof (topo_df32), mode & FKIOCTL) != 0) {
2455dd23d762SRobert Mustacchi 			return (EFAULT);
2456dd23d762SRobert Mustacchi 		}
2457dd23d762SRobert Mustacchi 		bzero(&topo_df, sizeof (topo_df));
2458dd23d762SRobert Mustacchi 		topo_df.atd_dfno = topo_df32.atd_dfno;
2459dd23d762SRobert Mustacchi 		topo_df.atd_df_buf_nents = topo_df32.atd_df_buf_nents;
2460dd23d762SRobert Mustacchi 		topo_df.atd_df_ents = (void *)(uintptr_t)topo_df32.atd_df_ents;
2461dd23d762SRobert Mustacchi 		break;
2462dd23d762SRobert Mustacchi #endif
2463dd23d762SRobert Mustacchi 	case DDI_MODEL_NONE:
2464dd23d762SRobert Mustacchi 		if (ddi_copyin((void *)(uintptr_t)arg, &topo_df,
2465dd23d762SRobert Mustacchi 		    sizeof (topo_df), mode & FKIOCTL) != 0) {
2466dd23d762SRobert Mustacchi 			return (EFAULT);
2467dd23d762SRobert Mustacchi 		}
2468dd23d762SRobert Mustacchi 		break;
2469dd23d762SRobert Mustacchi 	default:
2470dd23d762SRobert Mustacchi 		return (ENOTSUP);
2471dd23d762SRobert Mustacchi 	}
2472dd23d762SRobert Mustacchi 
2473dd23d762SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
2474dd23d762SRobert Mustacchi 	if (topo_df.atd_dfno >= azn->azn_ndfs) {
2475dd23d762SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
2476dd23d762SRobert Mustacchi 		return (EINVAL);
2477dd23d762SRobert Mustacchi 	}
2478dd23d762SRobert Mustacchi 
2479dd23d762SRobert Mustacchi 	df = &azn->azn_dfs[topo_df.atd_dfno];
2480dd23d762SRobert Mustacchi 	topo_df.atd_nodeid = df->adf_nodeid;
2481dd23d762SRobert Mustacchi 	topo_df.atd_sockid = (df->adf_nodeid & df->adf_decomp.dfd_sock_mask) >>
2482dd23d762SRobert Mustacchi 	    df->adf_decomp.dfd_sock_shift;
2483dd23d762SRobert Mustacchi 	topo_df.atd_dieid = (df->adf_nodeid & df->adf_decomp.dfd_die_mask) >>
2484dd23d762SRobert Mustacchi 	    df->adf_decomp.dfd_die_shift;
2485dd23d762SRobert Mustacchi 	topo_df.atd_rev = df->adf_rev;
2486*019df03dSRobert Mustacchi 	topo_df.atd_major = df->adf_major;
2487*019df03dSRobert Mustacchi 	topo_df.atd_minor = df->adf_minor;
2488dd23d762SRobert Mustacchi 	topo_df.atd_df_act_nents = df->adf_nents;
2489dd23d762SRobert Mustacchi 	max_ents = MIN(topo_df.atd_df_buf_nents, df->adf_nents);
2490dd23d762SRobert Mustacchi 
2491dd23d762SRobert Mustacchi 	if (topo_df.atd_df_ents == NULL) {
2492dd23d762SRobert Mustacchi 		topo_df.atd_df_buf_nvalid = 0;
2493dd23d762SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
2494dd23d762SRobert Mustacchi 		goto copyout;
2495dd23d762SRobert Mustacchi 	}
2496dd23d762SRobert Mustacchi 
2497dd23d762SRobert Mustacchi 	nwritten = 0;
2498dd23d762SRobert Mustacchi 	for (uint32_t i = 0; i < max_ents; i++) {
2499dd23d762SRobert Mustacchi 		amdzen_topo_df_ent_t topo_ent;
2500dd23d762SRobert Mustacchi 		const amdzen_df_ent_t *ent = &df->adf_ents[i];
2501dd23d762SRobert Mustacchi 
2502dd23d762SRobert Mustacchi 		/*
2503dd23d762SRobert Mustacchi 		 * We opt not to include disabled elements right now. They
2504dd23d762SRobert Mustacchi 		 * generally don't have a valid type and there isn't much useful
2505dd23d762SRobert Mustacchi 		 * information we can get from them. This can be changed if we
2506dd23d762SRobert Mustacchi 		 * find a use case for them for userland topo.
2507dd23d762SRobert Mustacchi 		 */
2508dd23d762SRobert Mustacchi 		if ((ent->adfe_flags & AMDZEN_DFE_F_ENABLED) == 0)
2509dd23d762SRobert Mustacchi 			continue;
2510dd23d762SRobert Mustacchi 
2511dd23d762SRobert Mustacchi 		bzero(&topo_ent, sizeof (topo_ent));
2512dd23d762SRobert Mustacchi 		topo_ent.atde_type = ent->adfe_type;
2513dd23d762SRobert Mustacchi 		topo_ent.atde_subtype = ent->adfe_subtype;
2514dd23d762SRobert Mustacchi 		topo_ent.atde_fabric_id = ent->adfe_fabric_id;
2515dd23d762SRobert Mustacchi 		topo_ent.atde_inst_id = ent->adfe_inst_id;
2516*019df03dSRobert Mustacchi 		amdzen_topo_ioctl_df_fill_peers(df, ent, &topo_ent);
2517dd23d762SRobert Mustacchi 
2518*019df03dSRobert Mustacchi 		if (amdzen_dfe_is_ccm(df, ent)) {
2519dd23d762SRobert Mustacchi 			amdzen_topo_ioctl_df_fill_ccm(ent, &topo_ent);
2520dd23d762SRobert Mustacchi 		}
2521dd23d762SRobert Mustacchi 
2522dd23d762SRobert Mustacchi 		if (ddi_copyout(&topo_ent, &topo_df.atd_df_ents[nwritten],
2523dd23d762SRobert Mustacchi 		    sizeof (topo_ent), mode & FKIOCTL) != 0) {
2524dd23d762SRobert Mustacchi 			mutex_exit(&azn->azn_mutex);
2525dd23d762SRobert Mustacchi 			return (EFAULT);
2526dd23d762SRobert Mustacchi 		}
2527dd23d762SRobert Mustacchi 		nwritten++;
2528dd23d762SRobert Mustacchi 	}
2529dd23d762SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
2530dd23d762SRobert Mustacchi 
2531dd23d762SRobert Mustacchi 	topo_df.atd_df_buf_nvalid = nwritten;
2532dd23d762SRobert Mustacchi copyout:
2533dd23d762SRobert Mustacchi 	switch (model) {
2534dd23d762SRobert Mustacchi #ifdef	_MULTI_DATAMODEL
2535dd23d762SRobert Mustacchi 	case DDI_MODEL_ILP32:
2536dd23d762SRobert Mustacchi 		topo_df32.atd_nodeid = topo_df.atd_nodeid;
2537dd23d762SRobert Mustacchi 		topo_df32.atd_sockid = topo_df.atd_sockid;
2538dd23d762SRobert Mustacchi 		topo_df32.atd_dieid = topo_df.atd_dieid;
2539dd23d762SRobert Mustacchi 		topo_df32.atd_rev = topo_df.atd_rev;
2540*019df03dSRobert Mustacchi 		topo_df32.atd_major = topo_df.atd_major;
2541*019df03dSRobert Mustacchi 		topo_df32.atd_minor = topo_df.atd_minor;
2542dd23d762SRobert Mustacchi 		topo_df32.atd_df_buf_nvalid = topo_df.atd_df_buf_nvalid;
2543dd23d762SRobert Mustacchi 		topo_df32.atd_df_act_nents = topo_df.atd_df_act_nents;
2544dd23d762SRobert Mustacchi 
2545dd23d762SRobert Mustacchi 		if (ddi_copyout(&topo_df32, (void *)(uintptr_t)arg,
2546dd23d762SRobert Mustacchi 		    sizeof (topo_df32), mode & FKIOCTL) != 0) {
2547dd23d762SRobert Mustacchi 			return (EFAULT);
2548dd23d762SRobert Mustacchi 		}
2549dd23d762SRobert Mustacchi 		break;
2550dd23d762SRobert Mustacchi #endif
2551dd23d762SRobert Mustacchi 	case DDI_MODEL_NONE:
2552dd23d762SRobert Mustacchi 		if (ddi_copyout(&topo_df, (void *)(uintptr_t)arg,
2553dd23d762SRobert Mustacchi 		    sizeof (topo_df), mode & FKIOCTL) != 0) {
2554dd23d762SRobert Mustacchi 			return (EFAULT);
2555dd23d762SRobert Mustacchi 		}
2556dd23d762SRobert Mustacchi 		break;
2557dd23d762SRobert Mustacchi 	default:
2558dd23d762SRobert Mustacchi 		break;
2559dd23d762SRobert Mustacchi 	}
2560dd23d762SRobert Mustacchi 
2561dd23d762SRobert Mustacchi 
2562dd23d762SRobert Mustacchi 	return (0);
2563dd23d762SRobert Mustacchi }
2564dd23d762SRobert Mustacchi 
2565dd23d762SRobert Mustacchi static int
amdzen_topo_ioctl_ccd(amdzen_t * azn,intptr_t arg,int mode)2566dd23d762SRobert Mustacchi amdzen_topo_ioctl_ccd(amdzen_t *azn, intptr_t arg, int mode)
2567dd23d762SRobert Mustacchi {
2568dd23d762SRobert Mustacchi 	amdzen_topo_ccd_t ccd, *ccdp;
2569dd23d762SRobert Mustacchi 	amdzen_df_t *df;
2570dd23d762SRobert Mustacchi 	amdzen_df_ent_t *ent;
2571dd23d762SRobert Mustacchi 	amdzen_ccm_data_t *ccm;
2572dd23d762SRobert Mustacchi 	uint32_t ccdno;
2573dd23d762SRobert Mustacchi 	size_t copyin_size = offsetof(amdzen_topo_ccd_t, atccd_err);
2574dd23d762SRobert Mustacchi 
2575dd23d762SRobert Mustacchi 	/*
2576dd23d762SRobert Mustacchi 	 * Only copy in the identifying information so that way we can ensure
2577dd23d762SRobert Mustacchi 	 * the rest of the structure we return to the user doesn't contain
2578dd23d762SRobert Mustacchi 	 * anything unexpected in it.
2579dd23d762SRobert Mustacchi 	 */
2580dd23d762SRobert Mustacchi 	bzero(&ccd, sizeof (ccd));
2581dd23d762SRobert Mustacchi 	if (ddi_copyin((void *)(uintptr_t)arg, &ccd, copyin_size,
2582dd23d762SRobert Mustacchi 	    mode & FKIOCTL) != 0) {
2583dd23d762SRobert Mustacchi 		return (EFAULT);
2584dd23d762SRobert Mustacchi 	}
2585dd23d762SRobert Mustacchi 
2586dd23d762SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
2587dd23d762SRobert Mustacchi 	if ((azn->azn_flags & AMDZEN_F_APIC_DECOMP_VALID) == 0) {
2588dd23d762SRobert Mustacchi 		ccd.atccd_err = AMDZEN_TOPO_CCD_E_NO_APIC_DECOMP;
2589dd23d762SRobert Mustacchi 		goto copyout;
2590dd23d762SRobert Mustacchi 	}
2591dd23d762SRobert Mustacchi 
2592dd23d762SRobert Mustacchi 	df = amdzen_df_find(azn, ccd.atccd_dfno);
2593dd23d762SRobert Mustacchi 	if (df == NULL) {
2594dd23d762SRobert Mustacchi 		ccd.atccd_err = AMDZEN_TOPO_CCD_E_BAD_DFNO;
2595dd23d762SRobert Mustacchi 		goto copyout;
2596dd23d762SRobert Mustacchi 	}
2597dd23d762SRobert Mustacchi 
2598dd23d762SRobert Mustacchi 	/*
2599dd23d762SRobert Mustacchi 	 * We don't have enough information to know how to construct this
2600dd23d762SRobert Mustacchi 	 * information in Zen 1 at this time, so refuse.
2601dd23d762SRobert Mustacchi 	 */
2602dd23d762SRobert Mustacchi 	if (df->adf_rev <= DF_REV_2) {
2603dd23d762SRobert Mustacchi 		ccd.atccd_err = AMDZEN_TOPO_CCD_E_SOC_UNSUPPORTED;
2604dd23d762SRobert Mustacchi 		goto copyout;
2605dd23d762SRobert Mustacchi 	}
2606dd23d762SRobert Mustacchi 
2607dd23d762SRobert Mustacchi 	ent = amdzen_df_ent_find_by_instid(df, ccd.atccd_instid);
2608dd23d762SRobert Mustacchi 	if (ent == NULL) {
2609dd23d762SRobert Mustacchi 		ccd.atccd_err = AMDZEN_TOPO_CCD_E_BAD_INSTID;
2610dd23d762SRobert Mustacchi 		goto copyout;
2611dd23d762SRobert Mustacchi 	}
2612dd23d762SRobert Mustacchi 
2613*019df03dSRobert Mustacchi 	if (!amdzen_dfe_is_ccm(df, ent)) {
2614dd23d762SRobert Mustacchi 		ccd.atccd_err = AMDZEN_TOPO_CCD_E_NOT_A_CCD;
2615dd23d762SRobert Mustacchi 		goto copyout;
2616dd23d762SRobert Mustacchi 	}
2617dd23d762SRobert Mustacchi 
2618dd23d762SRobert Mustacchi 	ccm = &ent->adfe_data.aded_ccm;
2619dd23d762SRobert Mustacchi 	for (ccdno = 0; ccdno < DF_MAX_CCDS_PER_CCM; ccdno++) {
2620dd23d762SRobert Mustacchi 		if (ccm->acd_ccd_en[ccdno] != 0 &&
2621dd23d762SRobert Mustacchi 		    ccm->acd_ccd_id[ccdno] == ccd.atccd_phys_no) {
2622dd23d762SRobert Mustacchi 			break;
2623dd23d762SRobert Mustacchi 		}
2624dd23d762SRobert Mustacchi 	}
2625dd23d762SRobert Mustacchi 
2626dd23d762SRobert Mustacchi 	if (ccdno == DF_MAX_CCDS_PER_CCM) {
2627dd23d762SRobert Mustacchi 		ccd.atccd_err = AMDZEN_TOPO_CCD_E_NOT_A_CCD;
2628dd23d762SRobert Mustacchi 		goto copyout;
2629dd23d762SRobert Mustacchi 	}
2630dd23d762SRobert Mustacchi 
2631dd23d762SRobert Mustacchi 	if (ccm->acd_ccd_data[ccdno] == NULL) {
2632dd23d762SRobert Mustacchi 		/*
2633dd23d762SRobert Mustacchi 		 * We don't actually have this data. Go fill it out and save it
2634dd23d762SRobert Mustacchi 		 * for future use.
2635dd23d762SRobert Mustacchi 		 */
2636dd23d762SRobert Mustacchi 		ccdp = kmem_zalloc(sizeof (amdzen_topo_ccd_t), KM_NOSLEEP_LAZY);
2637dd23d762SRobert Mustacchi 		if (ccdp == NULL) {
2638dd23d762SRobert Mustacchi 			mutex_exit(&azn->azn_mutex);
2639dd23d762SRobert Mustacchi 			return (ENOMEM);
2640dd23d762SRobert Mustacchi 		}
2641dd23d762SRobert Mustacchi 
2642dd23d762SRobert Mustacchi 		ccdp->atccd_dfno = ccd.atccd_dfno;
2643dd23d762SRobert Mustacchi 		ccdp->atccd_instid = ccd.atccd_instid;
2644dd23d762SRobert Mustacchi 		ccdp->atccd_phys_no = ccd.atccd_phys_no;
2645dd23d762SRobert Mustacchi 		amdzen_ccd_fill_topo(azn, df, ent, ccdp);
2646dd23d762SRobert Mustacchi 		ccm->acd_ccd_data[ccdno] = ccdp;
2647dd23d762SRobert Mustacchi 	}
2648dd23d762SRobert Mustacchi 	ASSERT3P(ccm->acd_ccd_data[ccdno], !=, NULL);
2649dd23d762SRobert Mustacchi 	bcopy(ccm->acd_ccd_data[ccdno], &ccd, sizeof (ccd));
2650dd23d762SRobert Mustacchi 
2651dd23d762SRobert Mustacchi copyout:
2652dd23d762SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
2653dd23d762SRobert Mustacchi 	if (ddi_copyout(&ccd, (void *)(uintptr_t)arg, sizeof (ccd),
2654dd23d762SRobert Mustacchi 	    mode & FKIOCTL) != 0) {
2655dd23d762SRobert Mustacchi 		return (EFAULT);
2656dd23d762SRobert Mustacchi 	}
2657dd23d762SRobert Mustacchi 
2658dd23d762SRobert Mustacchi 	return (0);
2659dd23d762SRobert Mustacchi }
2660dd23d762SRobert Mustacchi 
2661dd23d762SRobert Mustacchi static int
amdzen_topo_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)2662dd23d762SRobert Mustacchi amdzen_topo_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
2663dd23d762SRobert Mustacchi     cred_t *credp, int *rvalp)
2664dd23d762SRobert Mustacchi {
2665dd23d762SRobert Mustacchi 	int ret;
2666dd23d762SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
2667dd23d762SRobert Mustacchi 
2668dd23d762SRobert Mustacchi 	if (getminor(dev) != AMDZEN_MINOR_TOPO) {
2669dd23d762SRobert Mustacchi 		return (ENXIO);
2670dd23d762SRobert Mustacchi 	}
2671dd23d762SRobert Mustacchi 
2672dd23d762SRobert Mustacchi 	if ((mode & FREAD) == 0) {
2673dd23d762SRobert Mustacchi 		return (EBADF);
2674dd23d762SRobert Mustacchi 	}
2675dd23d762SRobert Mustacchi 
2676dd23d762SRobert Mustacchi 	switch (cmd) {
2677dd23d762SRobert Mustacchi 	case AMDZEN_TOPO_IOCTL_BASE:
2678dd23d762SRobert Mustacchi 		ret = amdzen_topo_ioctl_base(azn, arg, mode);
2679dd23d762SRobert Mustacchi 		break;
2680dd23d762SRobert Mustacchi 	case AMDZEN_TOPO_IOCTL_DF:
2681dd23d762SRobert Mustacchi 		ret = amdzen_topo_ioctl_df(azn, arg, mode);
2682dd23d762SRobert Mustacchi 		break;
2683dd23d762SRobert Mustacchi 	case AMDZEN_TOPO_IOCTL_CCD:
2684dd23d762SRobert Mustacchi 		ret = amdzen_topo_ioctl_ccd(azn, arg, mode);
2685dd23d762SRobert Mustacchi 		break;
2686dd23d762SRobert Mustacchi 	default:
2687dd23d762SRobert Mustacchi 		ret = ENOTTY;
2688dd23d762SRobert Mustacchi 		break;
2689dd23d762SRobert Mustacchi 	}
2690dd23d762SRobert Mustacchi 
2691dd23d762SRobert Mustacchi 	return (ret);
2692dd23d762SRobert Mustacchi }
2693dd23d762SRobert Mustacchi 
2694dd23d762SRobert Mustacchi static int
amdzen_topo_close(dev_t dev,int flag,int otyp,cred_t * credp)2695dd23d762SRobert Mustacchi amdzen_topo_close(dev_t dev, int flag, int otyp, cred_t *credp)
2696dd23d762SRobert Mustacchi {
2697dd23d762SRobert Mustacchi 	if (otyp != OTYP_CHR) {
2698dd23d762SRobert Mustacchi 		return (EINVAL);
2699dd23d762SRobert Mustacchi 	}
2700dd23d762SRobert Mustacchi 
2701dd23d762SRobert Mustacchi 	if (getminor(dev) != AMDZEN_MINOR_TOPO) {
2702dd23d762SRobert Mustacchi 		return (ENXIO);
2703dd23d762SRobert Mustacchi 	}
2704dd23d762SRobert Mustacchi 
2705dd23d762SRobert Mustacchi 	return (0);
2706dd23d762SRobert Mustacchi }
2707dd23d762SRobert Mustacchi 
2708dd23d762SRobert Mustacchi static int
amdzen_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2709047043c2SRobert Mustacchi amdzen_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2710047043c2SRobert Mustacchi {
2711047043c2SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
2712047043c2SRobert Mustacchi 
2713047043c2SRobert Mustacchi 	if (cmd == DDI_RESUME) {
2714047043c2SRobert Mustacchi 		return (DDI_SUCCESS);
2715047043c2SRobert Mustacchi 	} else if (cmd != DDI_ATTACH) {
2716047043c2SRobert Mustacchi 		return (DDI_FAILURE);
2717047043c2SRobert Mustacchi 	}
2718047043c2SRobert Mustacchi 
2719047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
2720047043c2SRobert Mustacchi 	if (azn->azn_dip != NULL) {
2721047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "driver is already attached!");
2722047043c2SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
2723047043c2SRobert Mustacchi 		return (DDI_FAILURE);
2724047043c2SRobert Mustacchi 	}
2725047043c2SRobert Mustacchi 
2726dd23d762SRobert Mustacchi 	if (ddi_create_minor_node(dip, "topo", S_IFCHR, AMDZEN_MINOR_TOPO,
2727dd23d762SRobert Mustacchi 	    DDI_PSEUDO, 0) != 0) {
2728dd23d762SRobert Mustacchi 		dev_err(dip, CE_WARN, "failed to create topo minor node!");
2729dd23d762SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
2730dd23d762SRobert Mustacchi 		return (DDI_FAILURE);
2731dd23d762SRobert Mustacchi 	}
2732dd23d762SRobert Mustacchi 
2733047043c2SRobert Mustacchi 	azn->azn_dip = dip;
2734047043c2SRobert Mustacchi 	azn->azn_taskqid = taskq_dispatch(system_taskq, amdzen_stub_scan,
2735047043c2SRobert Mustacchi 	    azn, TQ_SLEEP);
2736047043c2SRobert Mustacchi 	azn->azn_flags |= AMDZEN_F_SCAN_DISPATCHED;
2737047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
2738047043c2SRobert Mustacchi 
2739047043c2SRobert Mustacchi 	return (DDI_SUCCESS);
2740047043c2SRobert Mustacchi }
2741047043c2SRobert Mustacchi 
2742047043c2SRobert Mustacchi static int
amdzen_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)2743047043c2SRobert Mustacchi amdzen_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2744047043c2SRobert Mustacchi {
2745047043c2SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
2746047043c2SRobert Mustacchi 
2747047043c2SRobert Mustacchi 	if (cmd == DDI_SUSPEND) {
2748047043c2SRobert Mustacchi 		return (DDI_SUCCESS);
2749047043c2SRobert Mustacchi 	} else if (cmd != DDI_DETACH) {
2750047043c2SRobert Mustacchi 		return (DDI_FAILURE);
2751047043c2SRobert Mustacchi 	}
2752047043c2SRobert Mustacchi 
2753047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
2754047043c2SRobert Mustacchi 	while (azn->azn_taskqid != TASKQID_INVALID) {
2755047043c2SRobert Mustacchi 		cv_wait(&azn->azn_cv, &azn->azn_mutex);
2756047043c2SRobert Mustacchi 	}
2757047043c2SRobert Mustacchi 
2758047043c2SRobert Mustacchi 	/*
2759047043c2SRobert Mustacchi 	 * If we've attached any stub drivers, e.g. this platform is important
2760047043c2SRobert Mustacchi 	 * for us, then we fail detach.
2761047043c2SRobert Mustacchi 	 */
2762047043c2SRobert Mustacchi 	if (!list_is_empty(&azn->azn_df_stubs) ||
2763047043c2SRobert Mustacchi 	    !list_is_empty(&azn->azn_nb_stubs)) {
2764047043c2SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
2765047043c2SRobert Mustacchi 		return (DDI_FAILURE);
2766047043c2SRobert Mustacchi 	}
2767047043c2SRobert Mustacchi 
2768dd23d762SRobert Mustacchi 	ddi_remove_minor_node(azn->azn_dip, NULL);
2769047043c2SRobert Mustacchi 	azn->azn_dip = NULL;
2770047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
2771047043c2SRobert Mustacchi 
2772047043c2SRobert Mustacchi 	return (DDI_SUCCESS);
2773047043c2SRobert Mustacchi }
2774047043c2SRobert Mustacchi 
2775047043c2SRobert Mustacchi static void
amdzen_free(void)2776047043c2SRobert Mustacchi amdzen_free(void)
2777047043c2SRobert Mustacchi {
2778047043c2SRobert Mustacchi 	if (amdzen_data == NULL) {
2779047043c2SRobert Mustacchi 		return;
2780047043c2SRobert Mustacchi 	}
2781047043c2SRobert Mustacchi 
2782047043c2SRobert Mustacchi 	VERIFY(list_is_empty(&amdzen_data->azn_df_stubs));
2783047043c2SRobert Mustacchi 	list_destroy(&amdzen_data->azn_df_stubs);
2784047043c2SRobert Mustacchi 	VERIFY(list_is_empty(&amdzen_data->azn_nb_stubs));
2785047043c2SRobert Mustacchi 	list_destroy(&amdzen_data->azn_nb_stubs);
2786047043c2SRobert Mustacchi 	cv_destroy(&amdzen_data->azn_cv);
2787047043c2SRobert Mustacchi 	mutex_destroy(&amdzen_data->azn_mutex);
2788047043c2SRobert Mustacchi 	kmem_free(amdzen_data, sizeof (amdzen_t));
2789047043c2SRobert Mustacchi 	amdzen_data = NULL;
2790047043c2SRobert Mustacchi }
2791047043c2SRobert Mustacchi 
2792047043c2SRobert Mustacchi static void
amdzen_alloc(void)2793047043c2SRobert Mustacchi amdzen_alloc(void)
2794047043c2SRobert Mustacchi {
2795047043c2SRobert Mustacchi 	amdzen_data = kmem_zalloc(sizeof (amdzen_t), KM_SLEEP);
2796047043c2SRobert Mustacchi 	mutex_init(&amdzen_data->azn_mutex, NULL, MUTEX_DRIVER, NULL);
2797047043c2SRobert Mustacchi 	list_create(&amdzen_data->azn_df_stubs, sizeof (amdzen_stub_t),
2798047043c2SRobert Mustacchi 	    offsetof(amdzen_stub_t, azns_link));
2799047043c2SRobert Mustacchi 	list_create(&amdzen_data->azn_nb_stubs, sizeof (amdzen_stub_t),
2800047043c2SRobert Mustacchi 	    offsetof(amdzen_stub_t, azns_link));
2801047043c2SRobert Mustacchi 	cv_init(&amdzen_data->azn_cv, NULL, CV_DRIVER, NULL);
2802047043c2SRobert Mustacchi }
2803047043c2SRobert Mustacchi 
2804dd23d762SRobert Mustacchi static struct cb_ops amdzen_topo_cb_ops = {
2805dd23d762SRobert Mustacchi 	.cb_open = amdzen_topo_open,
2806dd23d762SRobert Mustacchi 	.cb_close = amdzen_topo_close,
2807dd23d762SRobert Mustacchi 	.cb_strategy = nodev,
2808dd23d762SRobert Mustacchi 	.cb_print = nodev,
2809dd23d762SRobert Mustacchi 	.cb_dump = nodev,
2810dd23d762SRobert Mustacchi 	.cb_read = nodev,
2811dd23d762SRobert Mustacchi 	.cb_write = nodev,
2812dd23d762SRobert Mustacchi 	.cb_ioctl = amdzen_topo_ioctl,
2813dd23d762SRobert Mustacchi 	.cb_devmap = nodev,
2814dd23d762SRobert Mustacchi 	.cb_mmap = nodev,
2815dd23d762SRobert Mustacchi 	.cb_segmap = nodev,
2816dd23d762SRobert Mustacchi 	.cb_chpoll = nochpoll,
2817dd23d762SRobert Mustacchi 	.cb_prop_op = ddi_prop_op,
2818dd23d762SRobert Mustacchi 	.cb_flag = D_MP,
2819dd23d762SRobert Mustacchi 	.cb_rev = CB_REV,
2820dd23d762SRobert Mustacchi 	.cb_aread = nodev,
2821dd23d762SRobert Mustacchi 	.cb_awrite = nodev
2822dd23d762SRobert Mustacchi };
2823dd23d762SRobert Mustacchi 
2824047043c2SRobert Mustacchi struct bus_ops amdzen_bus_ops = {
2825047043c2SRobert Mustacchi 	.busops_rev = BUSO_REV,
2826047043c2SRobert Mustacchi 	.bus_map = nullbusmap,
2827047043c2SRobert Mustacchi 	.bus_dma_map = ddi_no_dma_map,
2828047043c2SRobert Mustacchi 	.bus_dma_allochdl = ddi_no_dma_allochdl,
2829047043c2SRobert Mustacchi 	.bus_dma_freehdl = ddi_no_dma_freehdl,
2830047043c2SRobert Mustacchi 	.bus_dma_bindhdl = ddi_no_dma_bindhdl,
2831047043c2SRobert Mustacchi 	.bus_dma_unbindhdl = ddi_no_dma_unbindhdl,
2832047043c2SRobert Mustacchi 	.bus_dma_flush = ddi_no_dma_flush,
2833047043c2SRobert Mustacchi 	.bus_dma_win = ddi_no_dma_win,
2834047043c2SRobert Mustacchi 	.bus_dma_ctl = ddi_no_dma_mctl,
2835047043c2SRobert Mustacchi 	.bus_prop_op = ddi_bus_prop_op,
2836047043c2SRobert Mustacchi 	.bus_ctl = amdzen_bus_ctl
2837047043c2SRobert Mustacchi };
2838047043c2SRobert Mustacchi 
2839047043c2SRobert Mustacchi static struct dev_ops amdzen_dev_ops = {
2840047043c2SRobert Mustacchi 	.devo_rev = DEVO_REV,
2841047043c2SRobert Mustacchi 	.devo_refcnt = 0,
2842047043c2SRobert Mustacchi 	.devo_getinfo = nodev,
2843047043c2SRobert Mustacchi 	.devo_identify = nulldev,
2844047043c2SRobert Mustacchi 	.devo_probe = nulldev,
2845047043c2SRobert Mustacchi 	.devo_attach = amdzen_attach,
2846047043c2SRobert Mustacchi 	.devo_detach = amdzen_detach,
2847047043c2SRobert Mustacchi 	.devo_reset = nodev,
2848047043c2SRobert Mustacchi 	.devo_quiesce = ddi_quiesce_not_needed,
2849dd23d762SRobert Mustacchi 	.devo_bus_ops = &amdzen_bus_ops,
2850dd23d762SRobert Mustacchi 	.devo_cb_ops = &amdzen_topo_cb_ops
2851047043c2SRobert Mustacchi };
2852047043c2SRobert Mustacchi 
2853047043c2SRobert Mustacchi static struct modldrv amdzen_modldrv = {
2854047043c2SRobert Mustacchi 	.drv_modops = &mod_driverops,
2855047043c2SRobert Mustacchi 	.drv_linkinfo = "AMD Zen Nexus Driver",
2856047043c2SRobert Mustacchi 	.drv_dev_ops = &amdzen_dev_ops
2857047043c2SRobert Mustacchi };
2858047043c2SRobert Mustacchi 
2859047043c2SRobert Mustacchi static struct modlinkage amdzen_modlinkage = {
2860047043c2SRobert Mustacchi 	.ml_rev = MODREV_1,
2861047043c2SRobert Mustacchi 	.ml_linkage = { &amdzen_modldrv, NULL }
2862047043c2SRobert Mustacchi };
2863047043c2SRobert Mustacchi 
2864047043c2SRobert Mustacchi int
_init(void)2865047043c2SRobert Mustacchi _init(void)
2866047043c2SRobert Mustacchi {
2867047043c2SRobert Mustacchi 	int ret;
2868047043c2SRobert Mustacchi 
28699b0429a1SPu Wen 	if (cpuid_getvendor(CPU) != X86_VENDOR_AMD &&
28709b0429a1SPu Wen 	    cpuid_getvendor(CPU) != X86_VENDOR_HYGON) {
2871047043c2SRobert Mustacchi 		return (ENOTSUP);
2872047043c2SRobert Mustacchi 	}
2873047043c2SRobert Mustacchi 
2874047043c2SRobert Mustacchi 	if ((ret = mod_install(&amdzen_modlinkage)) == 0) {
2875047043c2SRobert Mustacchi 		amdzen_alloc();
2876047043c2SRobert Mustacchi 	}
2877047043c2SRobert Mustacchi 
2878047043c2SRobert Mustacchi 	return (ret);
2879047043c2SRobert Mustacchi }
2880047043c2SRobert Mustacchi 
2881047043c2SRobert Mustacchi int
_info(struct modinfo * modinfop)2882047043c2SRobert Mustacchi _info(struct modinfo *modinfop)
2883047043c2SRobert Mustacchi {
2884047043c2SRobert Mustacchi 	return (mod_info(&amdzen_modlinkage, modinfop));
2885047043c2SRobert Mustacchi }
2886047043c2SRobert Mustacchi 
2887047043c2SRobert Mustacchi int
_fini(void)2888047043c2SRobert Mustacchi _fini(void)
2889047043c2SRobert Mustacchi {
2890047043c2SRobert Mustacchi 	int ret;
2891047043c2SRobert Mustacchi 
2892047043c2SRobert Mustacchi 	if ((ret = mod_remove(&amdzen_modlinkage)) == 0) {
2893047043c2SRobert Mustacchi 		amdzen_free();
2894047043c2SRobert Mustacchi 	}
2895047043c2SRobert Mustacchi 
2896047043c2SRobert Mustacchi 	return (ret);
2897047043c2SRobert Mustacchi }
2898