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