xref: /illumos-gate/usr/src/uts/intel/io/amdzen/amdzen.c (revision ba215efe)
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.
1471815ce7SRobert Mustacchi  * Copyright 2022 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
33047043c2SRobert Mustacchi  * configuration registers in terms that are meaningful to the system such as
34047043c2SRobert Mustacchi  * logical CPUs, cores, etc. This driver attaches to the PCI IDs that represent
35047043c2SRobert Mustacchi  * the northbridge and data fabric; however, there are multiple PCI devices (one
36047043c2SRobert Mustacchi  * per die) that exist. This driver does manage to map all of these three things
37047043c2SRobert Mustacchi  * together; however, it requires some acrobatics. Unfortunately, there's no
38047043c2SRobert Mustacchi  * direct way to map a northbridge to its corresponding die. However, we can map
39047043c2SRobert Mustacchi  * a CPU die to a data fabric PCI device and a data fabric PCI device to a
40047043c2SRobert Mustacchi  * corresponding northbridge PCI device.
41047043c2SRobert Mustacchi  *
42047043c2SRobert Mustacchi  * In current Zen based products, there is a direct mapping between processor
43047043c2SRobert Mustacchi  * nodes and a data fabric PCI device. All of the devices are on PCI Bus 0 and
44047043c2SRobert Mustacchi  * start from Device 0x18. Device 0x18 maps to processor node 0, 0x19 to
45047043c2SRobert Mustacchi  * processor node 1, etc. This means that to map a logical CPU to a data fabric
46047043c2SRobert Mustacchi  * device, we take its processor node id, add it to 0x18 and find the PCI device
47047043c2SRobert Mustacchi  * that is on bus 0, device 0x18. As each data fabric device is attached based
48047043c2SRobert Mustacchi  * on its PCI ID, we add it to the global list, amd_nbdf_dfs that is in the
49047043c2SRobert Mustacchi  * amd_f17nbdf_t structure.
50047043c2SRobert Mustacchi  *
51047043c2SRobert Mustacchi  * The northbridge PCI device has a defined device and function, but the PCI bus
52047043c2SRobert Mustacchi  * that it's on can vary. Each die has its own series of PCI buses that are
53047043c2SRobert Mustacchi  * assigned to it and the northbridge PCI device is on the first of die-specific
54047043c2SRobert Mustacchi  * PCI bus for each die. This also means that the northbridge will not show up
55047043c2SRobert Mustacchi  * on PCI bus 0, which is the PCI bus that all of the data fabric devices are
56047043c2SRobert Mustacchi  * on. While conventionally the northbridge with the lowest PCI bus value
57047043c2SRobert Mustacchi  * would correspond to processor node zero, hardware does not guarantee that at
58047043c2SRobert Mustacchi  * all. Because we don't want to be at the mercy of firmware, we don't rely on
59047043c2SRobert Mustacchi  * this ordering, even though we have yet to find a system that deviates from
60047043c2SRobert Mustacchi  * this scheme.
61047043c2SRobert Mustacchi  *
62047043c2SRobert Mustacchi  * One of the registers in the data fabric device's function 0
63047043c2SRobert Mustacchi  * (AMDZEN_DF_F0_CFG_ADDR_CTL) happens to have the first PCI bus that is
64047043c2SRobert Mustacchi  * associated with the processor node. This means that we can map a data fabric
65047043c2SRobert Mustacchi  * device to a northbridge by finding the northbridge whose PCI bus matches the
66047043c2SRobert Mustacchi  * value in the corresponding data fabric's AMDZEN_DF_F0_CFG_ADDR_CTL.
67047043c2SRobert Mustacchi  *
68047043c2SRobert Mustacchi  * We can map a northbridge to a data fabric device and a data fabric device to
69047043c2SRobert Mustacchi  * a die. Because these are generally 1:1 mappings, there is a transitive
70047043c2SRobert Mustacchi  * relationship and therefore we know which northbridge is associated with which
71047043c2SRobert Mustacchi  * processor die. This is summarized in the following image:
72047043c2SRobert Mustacchi  *
73047043c2SRobert Mustacchi  *  +-------+    +-----------------------------------+        +--------------+
74047043c2SRobert Mustacchi  *  | Die 0 |--->| Data Fabric PCI BDF 0/18/0        |------->| Northbridge  |
75047043c2SRobert Mustacchi  *  +-------+    | AMDZEN_DF_F0_CFG_ADDR_CTL: bus 10 |        | PCI  10/0/0  |
76047043c2SRobert Mustacchi  *     ...       +-----------------------------------+        +--------------+
77047043c2SRobert Mustacchi  *  +-------+     +------------------------------------+        +--------------+
78047043c2SRobert Mustacchi  *  | Die n |---->| Data Fabric PCI BDF 0/18+n/0       |------->| Northbridge  |
79047043c2SRobert Mustacchi  *  +-------+     | AMDZEN_DF_F0_CFG_ADDR_CTL: bus 133 |        | PCI 133/0/0  |
80047043c2SRobert Mustacchi  *                +------------------------------------+        +--------------+
81047043c2SRobert Mustacchi  *
82047043c2SRobert Mustacchi  * Note, the PCI buses used by the northbridges here are arbitrary. They do not
83047043c2SRobert Mustacchi  * reflect the actual values by hardware; however, the bus/device/function (BDF)
84047043c2SRobert Mustacchi  * of the data fabric accurately models hardware. All of the BDF values are in
85047043c2SRobert Mustacchi  * hex.
86047043c2SRobert Mustacchi  *
87047043c2SRobert Mustacchi  * Starting with the Rome generation of processors (Family 17h Model 30-3Fh),
88047043c2SRobert Mustacchi  * AMD has multiple northbridges that exist on a given die. All of these
89047043c2SRobert Mustacchi  * northbridges share the same data fabric and system management network port.
90047043c2SRobert Mustacchi  * From our perspective this means that some of the northbridge devices will be
91047043c2SRobert Mustacchi  * redundant and that we will no longer have a 1:1 mapping between the
92047043c2SRobert Mustacchi  * northbridge and the data fabric devices. Every data fabric will have a
93047043c2SRobert Mustacchi  * northbridge, but not every northbridge will have a data fabric device mapped.
94047043c2SRobert Mustacchi  * Because we're always trying to map from a die to a northbridge and not the
95047043c2SRobert Mustacchi  * reverse, the fact that there are extra northbridge devices hanging around
96047043c2SRobert Mustacchi  * that we don't know about shouldn't be a problem.
97047043c2SRobert Mustacchi  *
98047043c2SRobert Mustacchi  * -------------------------------
99047043c2SRobert Mustacchi  * Attach and Detach Complications
100047043c2SRobert Mustacchi  * -------------------------------
101047043c2SRobert Mustacchi  *
102047043c2SRobert Mustacchi  * Because we need to map different PCI devices together, this means that we
103047043c2SRobert Mustacchi  * have multiple dev_info_t structures that we need to manage. Each of these is
104047043c2SRobert Mustacchi  * independently attached and detached. While this is easily managed for attach,
105047043c2SRobert Mustacchi  * it is not for detach. Each of these devices is a 'stub'.
106047043c2SRobert Mustacchi  *
107047043c2SRobert Mustacchi  * Once a device has been detached it will only come back if we have an active
108047043c2SRobert Mustacchi  * minor node that will be accessed. This means that if they are detached,
109047043c2SRobert Mustacchi  * nothing would ever cause them to be reattached. The system also doesn't
110047043c2SRobert Mustacchi  * provide us a way or any guarantees around making sure that we're attached to
111047043c2SRobert Mustacchi  * all such devices before we detach. As a result, unfortunately, it's easier to
112047043c2SRobert Mustacchi  * basically have detach always fail.
113047043c2SRobert Mustacchi  *
114047043c2SRobert Mustacchi  * ---------------
115047043c2SRobert Mustacchi  * Exposed Devices
116047043c2SRobert Mustacchi  * ---------------
117047043c2SRobert Mustacchi  *
118047043c2SRobert Mustacchi  * Rather than try and have all of the different functions that could be
119047043c2SRobert Mustacchi  * provided by one driver, we instead have created a nexus driver that will
120047043c2SRobert Mustacchi  * itself try and load children. Children are all pseudo-device drivers that
121047043c2SRobert Mustacchi  * provide different pieces of functionality that use this.
12271815ce7SRobert Mustacchi  *
12371815ce7SRobert Mustacchi  * -------
12471815ce7SRobert Mustacchi  * Locking
12571815ce7SRobert Mustacchi  * -------
12671815ce7SRobert Mustacchi  *
12771815ce7SRobert Mustacchi  * The amdzen_data structure contains a single lock, azn_mutex. The various
12871815ce7SRobert Mustacchi  * client functions are intended for direct children of our nexus, but have been
12971815ce7SRobert Mustacchi  * designed in case someone else depends on this driver despite not being a
13071815ce7SRobert Mustacchi  * child. Once a DF has been discovered, the set of entities inside of it
13171815ce7SRobert Mustacchi  * (adf_nents, adf_ents[]) is considered static, constant data. This means that
13271815ce7SRobert Mustacchi  * iterating over it in and of itself does not require locking; however, the
13371815ce7SRobert Mustacchi  * discovery of the amd_df_t does. In addition, whenever performing register
13471815ce7SRobert Mustacchi  * accesses to the DF or SMN, those require locking. This means that one must
13571815ce7SRobert Mustacchi  * hold the lock in the following circumstances:
13671815ce7SRobert Mustacchi  *
13771815ce7SRobert Mustacchi  *   o Looking up DF structures
13871815ce7SRobert Mustacchi  *   o Reading or writing to DF registers
13971815ce7SRobert Mustacchi  *   o Reading or writing to SMN registers
14071815ce7SRobert Mustacchi  *
14171815ce7SRobert Mustacchi  * In general, it is preferred that the lock be held across an entire client
14271815ce7SRobert Mustacchi  * operation if possible. The only time this becomes an issue are when we have
14371815ce7SRobert Mustacchi  * callbacks into our callers (ala amdzen_c_df_iter()) as they will likely
14471815ce7SRobert Mustacchi  * recursively call into us.
145047043c2SRobert Mustacchi  */
146047043c2SRobert Mustacchi 
147047043c2SRobert Mustacchi #include <sys/modctl.h>
148047043c2SRobert Mustacchi #include <sys/conf.h>
149047043c2SRobert Mustacchi #include <sys/devops.h>
150047043c2SRobert Mustacchi #include <sys/ddi.h>
151047043c2SRobert Mustacchi #include <sys/sunddi.h>
152047043c2SRobert Mustacchi #include <sys/pci.h>
153047043c2SRobert Mustacchi #include <sys/sysmacros.h>
154047043c2SRobert Mustacchi #include <sys/sunndi.h>
155047043c2SRobert Mustacchi #include <sys/x86_archext.h>
156047043c2SRobert Mustacchi #include <sys/cpuvar.h>
157047043c2SRobert Mustacchi 
15871815ce7SRobert Mustacchi #include <sys/amdzen/df.h>
15971815ce7SRobert Mustacchi #include "amdzen_client.h"
160047043c2SRobert Mustacchi #include "amdzen.h"
161047043c2SRobert Mustacchi 
162047043c2SRobert Mustacchi amdzen_t *amdzen_data;
163047043c2SRobert Mustacchi 
164047043c2SRobert Mustacchi /*
165047043c2SRobert Mustacchi  * Array of northbridge IDs that we care about.
166047043c2SRobert Mustacchi  */
167047043c2SRobert Mustacchi static const uint16_t amdzen_nb_ids[] = {
168047043c2SRobert Mustacchi 	/* Family 17h Ryzen, Epyc Models 00h-0fh (Zen uarch) */
169047043c2SRobert Mustacchi 	0x1450,
170becd642cSRobert Mustacchi 	/* Family 17h Raven Ridge, Kestrel, Dali Models 10h-2fh (Zen uarch) */
171047043c2SRobert Mustacchi 	0x15d0,
172e9abe9d6SRobert Mustacchi 	/* Family 17h/19h Rome, Milan, Matisse, Vermeer Zen 2/Zen 3 uarch */
173becd642cSRobert Mustacchi 	0x1480,
17464168f2bSRobert Mustacchi 	/* Family 17h/19h Renoir, Cezanne Zen 2/3 uarch) */
175becd642cSRobert Mustacchi 	0x1630
176047043c2SRobert Mustacchi };
177047043c2SRobert Mustacchi 
178047043c2SRobert Mustacchi typedef struct {
179047043c2SRobert Mustacchi 	char *acd_name;
180047043c2SRobert Mustacchi 	amdzen_child_t acd_addr;
181047043c2SRobert Mustacchi } amdzen_child_data_t;
182047043c2SRobert Mustacchi 
183047043c2SRobert Mustacchi static const amdzen_child_data_t amdzen_children[] = {
184047043c2SRobert Mustacchi 	{ "smntemp", AMDZEN_C_SMNTEMP },
185549e0fd3SRobert Mustacchi 	{ "usmn", AMDZEN_C_USMN },
18671815ce7SRobert Mustacchi 	{ "zen_udf", AMDZEN_C_ZEN_UDF },
18771815ce7SRobert Mustacchi 	{ "zen_umc", AMDZEN_C_ZEN_UMC }
188047043c2SRobert Mustacchi };
189047043c2SRobert Mustacchi 
190047043c2SRobert Mustacchi static uint32_t
191047043c2SRobert Mustacchi amdzen_stub_get32(amdzen_stub_t *stub, off_t reg)
192047043c2SRobert Mustacchi {
193047043c2SRobert Mustacchi 	return (pci_config_get32(stub->azns_cfgspace, reg));
194047043c2SRobert Mustacchi }
195047043c2SRobert Mustacchi 
196549e0fd3SRobert Mustacchi static uint64_t
197549e0fd3SRobert Mustacchi amdzen_stub_get64(amdzen_stub_t *stub, off_t reg)
198549e0fd3SRobert Mustacchi {
199549e0fd3SRobert Mustacchi 	return (pci_config_get64(stub->azns_cfgspace, reg));
200549e0fd3SRobert Mustacchi }
201549e0fd3SRobert Mustacchi 
202047043c2SRobert Mustacchi static void
203047043c2SRobert Mustacchi amdzen_stub_put32(amdzen_stub_t *stub, off_t reg, uint32_t val)
204047043c2SRobert Mustacchi {
205047043c2SRobert Mustacchi 	pci_config_put32(stub->azns_cfgspace, reg, val);
206047043c2SRobert Mustacchi }
207047043c2SRobert Mustacchi 
20871815ce7SRobert Mustacchi static uint64_t
20971815ce7SRobert Mustacchi amdzen_df_read_regdef(amdzen_t *azn, amdzen_df_t *df, const df_reg_def_t def,
21071815ce7SRobert Mustacchi     uint8_t inst, boolean_t do_64)
21171815ce7SRobert Mustacchi {
21271815ce7SRobert Mustacchi 	df_reg_def_t ficaa;
21371815ce7SRobert Mustacchi 	df_reg_def_t ficad;
21471815ce7SRobert Mustacchi 	uint32_t val = 0;
21571815ce7SRobert Mustacchi 	df_rev_t df_rev = azn->azn_dfs[0].adf_rev;
21671815ce7SRobert Mustacchi 
21771815ce7SRobert Mustacchi 	VERIFY(MUTEX_HELD(&azn->azn_mutex));
21871815ce7SRobert Mustacchi 	ASSERT3U(def.drd_gens & df_rev, ==, df_rev);
21971815ce7SRobert Mustacchi 	val = DF_FICAA_V2_SET_TARG_INST(val, 1);
22071815ce7SRobert Mustacchi 	val = DF_FICAA_V2_SET_FUNC(val, def.drd_func);
22171815ce7SRobert Mustacchi 	val = DF_FICAA_V2_SET_INST(val, inst);
22271815ce7SRobert Mustacchi 	val = DF_FICAA_V2_SET_64B(val, do_64 ? 1 : 0);
22371815ce7SRobert Mustacchi 
22471815ce7SRobert Mustacchi 	switch (df_rev) {
22571815ce7SRobert Mustacchi 	case DF_REV_2:
22671815ce7SRobert Mustacchi 	case DF_REV_3:
22771815ce7SRobert Mustacchi 	case DF_REV_3P5:
22871815ce7SRobert Mustacchi 		ficaa = DF_FICAA_V2;
22971815ce7SRobert Mustacchi 		ficad = DF_FICAD_LO_V2;
23071815ce7SRobert Mustacchi 		/*
23171815ce7SRobert Mustacchi 		 * Both here and in the DFv4 case, the register ignores the
23271815ce7SRobert Mustacchi 		 * lower 2 bits. That is we can only address and encode things
23371815ce7SRobert Mustacchi 		 * in units of 4 bytes.
23471815ce7SRobert Mustacchi 		 */
23571815ce7SRobert Mustacchi 		val = DF_FICAA_V2_SET_REG(val, def.drd_reg >> 2);
23671815ce7SRobert Mustacchi 		break;
23771815ce7SRobert Mustacchi 	case DF_REV_4:
23871815ce7SRobert Mustacchi 		ficaa = DF_FICAA_V4;
23971815ce7SRobert Mustacchi 		ficad = DF_FICAD_LO_V4;
24071815ce7SRobert Mustacchi 		val = DF_FICAA_V4_SET_REG(val, def.drd_reg >> 2);
24171815ce7SRobert Mustacchi 		break;
24271815ce7SRobert Mustacchi 	default:
24371815ce7SRobert Mustacchi 		panic("encountered unexpected DF rev: %u", df_rev);
24471815ce7SRobert Mustacchi 	}
24571815ce7SRobert Mustacchi 
24671815ce7SRobert Mustacchi 	amdzen_stub_put32(df->adf_funcs[ficaa.drd_func], ficaa.drd_reg, val);
24771815ce7SRobert Mustacchi 	if (do_64) {
24871815ce7SRobert Mustacchi 		return (amdzen_stub_get64(df->adf_funcs[ficad.drd_func],
24971815ce7SRobert Mustacchi 		    ficad.drd_reg));
25071815ce7SRobert Mustacchi 	} else {
25171815ce7SRobert Mustacchi 		return (amdzen_stub_get32(df->adf_funcs[ficad.drd_func],
25271815ce7SRobert Mustacchi 		    ficad.drd_reg));
25371815ce7SRobert Mustacchi 	}
25471815ce7SRobert Mustacchi }
25571815ce7SRobert Mustacchi 
256047043c2SRobert Mustacchi /*
257047043c2SRobert Mustacchi  * Perform a targeted 32-bit indirect read to a specific instance and function.
258047043c2SRobert Mustacchi  */
259047043c2SRobert Mustacchi static uint32_t
26071815ce7SRobert Mustacchi amdzen_df_read32(amdzen_t *azn, amdzen_df_t *df, uint8_t inst,
26171815ce7SRobert Mustacchi     const df_reg_def_t def)
262047043c2SRobert Mustacchi {
26371815ce7SRobert Mustacchi 	return (amdzen_df_read_regdef(azn, df, def, inst, B_FALSE));
264047043c2SRobert Mustacchi }
265047043c2SRobert Mustacchi 
266549e0fd3SRobert Mustacchi /*
26771815ce7SRobert Mustacchi  * For a broadcast read, just go to the underlying PCI function and perform a
26871815ce7SRobert Mustacchi  * read. At this point in time, we don't believe we need to use the FICAA/FICAD
26971815ce7SRobert Mustacchi  * to access it (though it does have a broadcast mode).
270549e0fd3SRobert Mustacchi  */
27171815ce7SRobert Mustacchi static uint32_t
27271815ce7SRobert Mustacchi amdzen_df_read32_bcast(amdzen_t *azn, amdzen_df_t *df, const df_reg_def_t def)
273549e0fd3SRobert Mustacchi {
274549e0fd3SRobert Mustacchi 	VERIFY(MUTEX_HELD(&azn->azn_mutex));
27571815ce7SRobert Mustacchi 	return (amdzen_stub_get32(df->adf_funcs[def.drd_func], def.drd_reg));
276549e0fd3SRobert Mustacchi }
277549e0fd3SRobert Mustacchi 
27871815ce7SRobert Mustacchi 
279047043c2SRobert Mustacchi static uint32_t
280*ba215efeSKeith M Wesolowski amdzen_smn_read32(amdzen_t *azn, amdzen_df_t *df, const smn_reg_t reg)
281047043c2SRobert Mustacchi {
282047043c2SRobert Mustacchi 	VERIFY(MUTEX_HELD(&azn->azn_mutex));
283*ba215efeSKeith M Wesolowski 	amdzen_stub_put32(df->adf_nb, AMDZEN_NB_SMN_ADDR, SMN_REG_ADDR(reg));
284047043c2SRobert Mustacchi 	return (amdzen_stub_get32(df->adf_nb, AMDZEN_NB_SMN_DATA));
285047043c2SRobert Mustacchi }
286047043c2SRobert Mustacchi 
287f198607dSRobert Mustacchi static void
288*ba215efeSKeith M Wesolowski amdzen_smn_write32(amdzen_t *azn, amdzen_df_t *df, const smn_reg_t reg,
289*ba215efeSKeith M Wesolowski     const uint32_t val)
290f198607dSRobert Mustacchi {
291f198607dSRobert Mustacchi 	VERIFY(MUTEX_HELD(&azn->azn_mutex));
292*ba215efeSKeith M Wesolowski 	amdzen_stub_put32(df->adf_nb, AMDZEN_NB_SMN_ADDR, SMN_REG_ADDR(reg));
293f198607dSRobert Mustacchi 	amdzen_stub_put32(df->adf_nb, AMDZEN_NB_SMN_DATA, val);
294f198607dSRobert Mustacchi }
295f198607dSRobert Mustacchi 
296047043c2SRobert Mustacchi static amdzen_df_t *
297047043c2SRobert Mustacchi amdzen_df_find(amdzen_t *azn, uint_t dfno)
298047043c2SRobert Mustacchi {
299047043c2SRobert Mustacchi 	uint_t i;
300047043c2SRobert Mustacchi 
301047043c2SRobert Mustacchi 	ASSERT(MUTEX_HELD(&azn->azn_mutex));
302047043c2SRobert Mustacchi 	if (dfno >= azn->azn_ndfs) {
303047043c2SRobert Mustacchi 		return (NULL);
304047043c2SRobert Mustacchi 	}
305047043c2SRobert Mustacchi 
306047043c2SRobert Mustacchi 	for (i = 0; i < azn->azn_ndfs; i++) {
307047043c2SRobert Mustacchi 		amdzen_df_t *df = &azn->azn_dfs[i];
308047043c2SRobert Mustacchi 		if ((df->adf_flags & AMDZEN_DF_F_VALID) == 0) {
309047043c2SRobert Mustacchi 			continue;
310047043c2SRobert Mustacchi 		}
311047043c2SRobert Mustacchi 
312047043c2SRobert Mustacchi 		if (dfno == 0) {
313047043c2SRobert Mustacchi 			return (df);
314047043c2SRobert Mustacchi 		}
315047043c2SRobert Mustacchi 		dfno--;
316047043c2SRobert Mustacchi 	}
317047043c2SRobert Mustacchi 
318047043c2SRobert Mustacchi 	return (NULL);
319047043c2SRobert Mustacchi }
320047043c2SRobert Mustacchi 
321047043c2SRobert Mustacchi /*
322047043c2SRobert Mustacchi  * Client functions that are used by nexus children.
323047043c2SRobert Mustacchi  */
324047043c2SRobert Mustacchi int
325*ba215efeSKeith M Wesolowski amdzen_c_smn_read32(uint_t dfno, const smn_reg_t reg, uint32_t *valp)
326047043c2SRobert Mustacchi {
327047043c2SRobert Mustacchi 	amdzen_df_t *df;
328047043c2SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
329047043c2SRobert Mustacchi 
330047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
331047043c2SRobert Mustacchi 	df = amdzen_df_find(azn, dfno);
332047043c2SRobert Mustacchi 	if (df == NULL) {
333047043c2SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
334047043c2SRobert Mustacchi 		return (ENOENT);
335047043c2SRobert Mustacchi 	}
336047043c2SRobert Mustacchi 
337047043c2SRobert Mustacchi 	if ((df->adf_flags & AMDZEN_DF_F_FOUND_NB) == 0) {
338047043c2SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
339047043c2SRobert Mustacchi 		return (ENXIO);
340047043c2SRobert Mustacchi 	}
341047043c2SRobert Mustacchi 
342047043c2SRobert Mustacchi 	*valp = amdzen_smn_read32(azn, df, reg);
343047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
344047043c2SRobert Mustacchi 	return (0);
345047043c2SRobert Mustacchi }
346047043c2SRobert Mustacchi 
347f198607dSRobert Mustacchi int
348*ba215efeSKeith M Wesolowski amdzen_c_smn_write32(uint_t dfno, const smn_reg_t reg, const uint32_t val)
349f198607dSRobert Mustacchi {
350f198607dSRobert Mustacchi 	amdzen_df_t *df;
351f198607dSRobert Mustacchi 	amdzen_t *azn = amdzen_data;
352f198607dSRobert Mustacchi 
353f198607dSRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
354f198607dSRobert Mustacchi 	df = amdzen_df_find(azn, dfno);
355f198607dSRobert Mustacchi 	if (df == NULL) {
356f198607dSRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
357f198607dSRobert Mustacchi 		return (ENOENT);
358f198607dSRobert Mustacchi 	}
359f198607dSRobert Mustacchi 
360f198607dSRobert Mustacchi 	if ((df->adf_flags & AMDZEN_DF_F_FOUND_NB) == 0) {
361f198607dSRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
362f198607dSRobert Mustacchi 		return (ENXIO);
363f198607dSRobert Mustacchi 	}
364f198607dSRobert Mustacchi 
365f198607dSRobert Mustacchi 	amdzen_smn_write32(azn, df, reg, val);
366f198607dSRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
367f198607dSRobert Mustacchi 	return (0);
368f198607dSRobert Mustacchi }
369f198607dSRobert Mustacchi 
370f198607dSRobert Mustacchi 
371047043c2SRobert Mustacchi uint_t
372047043c2SRobert Mustacchi amdzen_c_df_count(void)
373047043c2SRobert Mustacchi {
374047043c2SRobert Mustacchi 	uint_t ret;
375047043c2SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
376047043c2SRobert Mustacchi 
377047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
378047043c2SRobert Mustacchi 	ret = azn->azn_ndfs;
379047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
380047043c2SRobert Mustacchi 	return (ret);
381047043c2SRobert Mustacchi }
382047043c2SRobert Mustacchi 
38371815ce7SRobert Mustacchi df_rev_t
38471815ce7SRobert Mustacchi amdzen_c_df_rev(void)
38571815ce7SRobert Mustacchi {
38671815ce7SRobert Mustacchi 	amdzen_df_t *df;
38771815ce7SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
38871815ce7SRobert Mustacchi 	df_rev_t rev;
38971815ce7SRobert Mustacchi 
39071815ce7SRobert Mustacchi 	/*
39171815ce7SRobert Mustacchi 	 * Always use the first DF instance to determine what we're using. Our
39271815ce7SRobert Mustacchi 	 * current assumption, which seems to generally be true, is that the
39371815ce7SRobert Mustacchi 	 * given DF revisions are the same in a given system when the DFs are
39471815ce7SRobert Mustacchi 	 * directly connected.
39571815ce7SRobert Mustacchi 	 */
39671815ce7SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
39771815ce7SRobert Mustacchi 	df = amdzen_df_find(azn, 0);
39871815ce7SRobert Mustacchi 	if (df == NULL) {
39971815ce7SRobert Mustacchi 		rev = DF_REV_UNKNOWN;
40071815ce7SRobert Mustacchi 	} else {
40171815ce7SRobert Mustacchi 		rev = df->adf_rev;
40271815ce7SRobert Mustacchi 	}
40371815ce7SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
40471815ce7SRobert Mustacchi 
40571815ce7SRobert Mustacchi 	return (rev);
40671815ce7SRobert Mustacchi }
40771815ce7SRobert Mustacchi 
408549e0fd3SRobert Mustacchi int
40971815ce7SRobert Mustacchi amdzen_c_df_read32(uint_t dfno, uint8_t inst, const df_reg_def_t def,
41071815ce7SRobert Mustacchi     uint32_t *valp)
411549e0fd3SRobert Mustacchi {
412549e0fd3SRobert Mustacchi 	amdzen_df_t *df;
413549e0fd3SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
414549e0fd3SRobert Mustacchi 
415549e0fd3SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
416549e0fd3SRobert Mustacchi 	df = amdzen_df_find(azn, dfno);
417549e0fd3SRobert Mustacchi 	if (df == NULL) {
418549e0fd3SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
419549e0fd3SRobert Mustacchi 		return (ENOENT);
420549e0fd3SRobert Mustacchi 	}
421549e0fd3SRobert Mustacchi 
42271815ce7SRobert Mustacchi 	*valp = amdzen_df_read_regdef(azn, df, def, inst, B_FALSE);
423549e0fd3SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
424549e0fd3SRobert Mustacchi 
425549e0fd3SRobert Mustacchi 	return (0);
426549e0fd3SRobert Mustacchi }
427549e0fd3SRobert Mustacchi 
428549e0fd3SRobert Mustacchi int
42971815ce7SRobert Mustacchi amdzen_c_df_read64(uint_t dfno, uint8_t inst, const df_reg_def_t def,
43071815ce7SRobert Mustacchi     uint64_t *valp)
431549e0fd3SRobert Mustacchi {
432549e0fd3SRobert Mustacchi 	amdzen_df_t *df;
433549e0fd3SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
434549e0fd3SRobert Mustacchi 
435549e0fd3SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
436549e0fd3SRobert Mustacchi 	df = amdzen_df_find(azn, dfno);
437549e0fd3SRobert Mustacchi 	if (df == NULL) {
438549e0fd3SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
439549e0fd3SRobert Mustacchi 		return (ENOENT);
440549e0fd3SRobert Mustacchi 	}
441549e0fd3SRobert Mustacchi 
44271815ce7SRobert Mustacchi 	*valp = amdzen_df_read_regdef(azn, df, def, inst, B_TRUE);
443549e0fd3SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
444549e0fd3SRobert Mustacchi 
445549e0fd3SRobert Mustacchi 	return (0);
446549e0fd3SRobert Mustacchi }
447047043c2SRobert Mustacchi 
44871815ce7SRobert Mustacchi int
44971815ce7SRobert Mustacchi amdzen_c_df_iter(uint_t dfno, zen_df_type_t type, amdzen_c_iter_f func,
45071815ce7SRobert Mustacchi     void *arg)
45171815ce7SRobert Mustacchi {
45271815ce7SRobert Mustacchi 	amdzen_df_t *df;
45371815ce7SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
45471815ce7SRobert Mustacchi 	df_type_t df_type;
45571815ce7SRobert Mustacchi 	uint8_t df_subtype;
45671815ce7SRobert Mustacchi 
45771815ce7SRobert Mustacchi 	/*
45871815ce7SRobert Mustacchi 	 * Unlike other calls here, we hold our lock only to find the DF here.
45971815ce7SRobert Mustacchi 	 * The main reason for this is the nature of the callback function.
46071815ce7SRobert Mustacchi 	 * Folks are iterating over instances so they can call back into us. If
46171815ce7SRobert Mustacchi 	 * you look at the locking statement, the thing that is most volatile
46271815ce7SRobert Mustacchi 	 * right here and what we need to protect is the DF itself and
46371815ce7SRobert Mustacchi 	 * subsequent register accesses to it. The actual data about which
46471815ce7SRobert Mustacchi 	 * entities exist is static and so once we have found a DF we should
46571815ce7SRobert Mustacchi 	 * hopefully be in good shape as they only come, but don't go.
46671815ce7SRobert Mustacchi 	 */
46771815ce7SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
46871815ce7SRobert Mustacchi 	df = amdzen_df_find(azn, dfno);
46971815ce7SRobert Mustacchi 	if (df == NULL) {
47071815ce7SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
47171815ce7SRobert Mustacchi 		return (ENOENT);
47271815ce7SRobert Mustacchi 	}
47371815ce7SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
47471815ce7SRobert Mustacchi 
47571815ce7SRobert Mustacchi 	switch (type) {
47671815ce7SRobert Mustacchi 	case ZEN_DF_TYPE_CS_UMC:
47771815ce7SRobert Mustacchi 		df_type = DF_TYPE_CS;
47871815ce7SRobert Mustacchi 		/*
47971815ce7SRobert Mustacchi 		 * In the original Zeppelin DFv2 die there was no subtype field
48071815ce7SRobert Mustacchi 		 * used for the CS. The UMC is the only type and has a subtype
48171815ce7SRobert Mustacchi 		 * of zero.
48271815ce7SRobert Mustacchi 		 */
48371815ce7SRobert Mustacchi 		if (df->adf_rev != DF_REV_2) {
48471815ce7SRobert Mustacchi 			df_subtype = DF_CS_SUBTYPE_UMC;
48571815ce7SRobert Mustacchi 		} else {
48671815ce7SRobert Mustacchi 			df_subtype = 0;
48771815ce7SRobert Mustacchi 		}
48871815ce7SRobert Mustacchi 		break;
48971815ce7SRobert Mustacchi 	case ZEN_DF_TYPE_CCM_CPU:
49071815ce7SRobert Mustacchi 		df_type = DF_TYPE_CCM;
49171815ce7SRobert Mustacchi 		/*
49271815ce7SRobert Mustacchi 		 * In the Genoa/DFv4 timeframe, with the introduction of CXL and
49371815ce7SRobert Mustacchi 		 * related, a subtype was added here where as previously it was
49471815ce7SRobert Mustacchi 		 * always zero.
49571815ce7SRobert Mustacchi 		 */
49671815ce7SRobert Mustacchi 		if (df->adf_major >= 4) {
49771815ce7SRobert Mustacchi 			df_subtype = DF_CCM_SUBTYPE_CPU;
49871815ce7SRobert Mustacchi 		} else {
49971815ce7SRobert Mustacchi 			df_subtype = 0;
50071815ce7SRobert Mustacchi 		}
50171815ce7SRobert Mustacchi 		break;
50271815ce7SRobert Mustacchi 	default:
50371815ce7SRobert Mustacchi 		return (EINVAL);
50471815ce7SRobert Mustacchi 	}
50571815ce7SRobert Mustacchi 
50671815ce7SRobert Mustacchi 	for (uint_t i = 0; i < df->adf_nents; i++) {
50771815ce7SRobert Mustacchi 		amdzen_df_ent_t *ent = &df->adf_ents[i];
50871815ce7SRobert Mustacchi 
50971815ce7SRobert Mustacchi 		/*
51071815ce7SRobert Mustacchi 		 * Some DF components are not considered enabled and therefore
51171815ce7SRobert Mustacchi 		 * will end up having bogus values in their ID fields. If we do
51271815ce7SRobert Mustacchi 		 * not have an enable flag set, we must skip this node.
51371815ce7SRobert Mustacchi 		 */
51471815ce7SRobert Mustacchi 		if ((ent->adfe_flags & AMDZEN_DFE_F_ENABLED) == 0)
51571815ce7SRobert Mustacchi 			continue;
51671815ce7SRobert Mustacchi 
51771815ce7SRobert Mustacchi 		if (ent->adfe_type == df_type &&
51871815ce7SRobert Mustacchi 		    ent->adfe_subtype == df_subtype) {
51971815ce7SRobert Mustacchi 			int ret = func(dfno, ent->adfe_fabric_id,
52071815ce7SRobert Mustacchi 			    ent->adfe_inst_id, arg);
52171815ce7SRobert Mustacchi 			if (ret != 0) {
52271815ce7SRobert Mustacchi 				return (ret);
52371815ce7SRobert Mustacchi 			}
52471815ce7SRobert Mustacchi 		}
52571815ce7SRobert Mustacchi 	}
52671815ce7SRobert Mustacchi 
52771815ce7SRobert Mustacchi 	return (0);
52871815ce7SRobert Mustacchi }
52971815ce7SRobert Mustacchi 
53071815ce7SRobert Mustacchi int
53171815ce7SRobert Mustacchi amdzen_c_df_fabric_decomp(df_fabric_decomp_t *decomp)
53271815ce7SRobert Mustacchi {
53371815ce7SRobert Mustacchi 	const amdzen_df_t *df;
53471815ce7SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
53571815ce7SRobert Mustacchi 
53671815ce7SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
53771815ce7SRobert Mustacchi 	df = amdzen_df_find(azn, 0);
53871815ce7SRobert Mustacchi 	if (df == NULL) {
53971815ce7SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
54071815ce7SRobert Mustacchi 		return (ENOENT);
54171815ce7SRobert Mustacchi 	}
54271815ce7SRobert Mustacchi 
54371815ce7SRobert Mustacchi 	*decomp = df->adf_decomp;
54471815ce7SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
54571815ce7SRobert Mustacchi 	return (0);
54671815ce7SRobert Mustacchi }
54771815ce7SRobert Mustacchi 
548047043c2SRobert Mustacchi static boolean_t
549047043c2SRobert Mustacchi amdzen_create_child(amdzen_t *azn, const amdzen_child_data_t *acd)
550047043c2SRobert Mustacchi {
551047043c2SRobert Mustacchi 	int ret;
552047043c2SRobert Mustacchi 	dev_info_t *child;
553047043c2SRobert Mustacchi 
554047043c2SRobert Mustacchi 	if (ndi_devi_alloc(azn->azn_dip, acd->acd_name,
555047043c2SRobert Mustacchi 	    (pnode_t)DEVI_SID_NODEID, &child) != NDI_SUCCESS) {
556549e0fd3SRobert Mustacchi 		dev_err(azn->azn_dip, CE_WARN, "!failed to allocate child "
557047043c2SRobert Mustacchi 		    "dip for %s", acd->acd_name);
558047043c2SRobert Mustacchi 		return (B_FALSE);
559047043c2SRobert Mustacchi 	}
560047043c2SRobert Mustacchi 
561047043c2SRobert Mustacchi 	ddi_set_parent_data(child, (void *)acd);
562047043c2SRobert Mustacchi 	if ((ret = ndi_devi_online(child, 0)) != NDI_SUCCESS) {
563549e0fd3SRobert Mustacchi 		dev_err(azn->azn_dip, CE_WARN, "!failed to online child "
564047043c2SRobert Mustacchi 		    "dip %s: %d", acd->acd_name, ret);
565047043c2SRobert Mustacchi 		return (B_FALSE);
566047043c2SRobert Mustacchi 	}
567047043c2SRobert Mustacchi 
568047043c2SRobert Mustacchi 	return (B_TRUE);
569047043c2SRobert Mustacchi }
570047043c2SRobert Mustacchi 
571047043c2SRobert Mustacchi static boolean_t
572047043c2SRobert Mustacchi amdzen_map_dfs(amdzen_t *azn)
573047043c2SRobert Mustacchi {
574047043c2SRobert Mustacchi 	amdzen_stub_t *stub;
575047043c2SRobert Mustacchi 
576047043c2SRobert Mustacchi 	ASSERT(MUTEX_HELD(&azn->azn_mutex));
577047043c2SRobert Mustacchi 
578047043c2SRobert Mustacchi 	for (stub = list_head(&azn->azn_df_stubs); stub != NULL;
579047043c2SRobert Mustacchi 	    stub = list_next(&azn->azn_df_stubs, stub)) {
580047043c2SRobert Mustacchi 		amdzen_df_t *df;
581047043c2SRobert Mustacchi 		uint_t dfno;
582047043c2SRobert Mustacchi 
583047043c2SRobert Mustacchi 		dfno = stub->azns_dev - AMDZEN_DF_FIRST_DEVICE;
584047043c2SRobert Mustacchi 		if (dfno > AMDZEN_MAX_DFS) {
585047043c2SRobert Mustacchi 			dev_err(stub->azns_dip, CE_WARN, "encountered df "
586047043c2SRobert Mustacchi 			    "device with illegal DF PCI b/d/f: 0x%x/%x/%x",
587047043c2SRobert Mustacchi 			    stub->azns_bus, stub->azns_dev, stub->azns_func);
588047043c2SRobert Mustacchi 			goto err;
589047043c2SRobert Mustacchi 		}
590047043c2SRobert Mustacchi 
591047043c2SRobert Mustacchi 		df = &azn->azn_dfs[dfno];
592047043c2SRobert Mustacchi 
593047043c2SRobert Mustacchi 		if (stub->azns_func >= AMDZEN_MAX_DF_FUNCS) {
594047043c2SRobert Mustacchi 			dev_err(stub->azns_dip, CE_WARN, "encountered df "
595047043c2SRobert Mustacchi 			    "device with illegal DF PCI b/d/f: 0x%x/%x/%x",
596047043c2SRobert Mustacchi 			    stub->azns_bus, stub->azns_dev, stub->azns_func);
597047043c2SRobert Mustacchi 			goto err;
598047043c2SRobert Mustacchi 		}
599047043c2SRobert Mustacchi 
600047043c2SRobert Mustacchi 		if (df->adf_funcs[stub->azns_func] != NULL) {
601047043c2SRobert Mustacchi 			dev_err(stub->azns_dip, CE_WARN, "encountered "
602047043c2SRobert Mustacchi 			    "duplicate df device with DF PCI b/d/f: 0x%x/%x/%x",
603047043c2SRobert Mustacchi 			    stub->azns_bus, stub->azns_dev, stub->azns_func);
604047043c2SRobert Mustacchi 			goto err;
605047043c2SRobert Mustacchi 		}
606047043c2SRobert Mustacchi 		df->adf_funcs[stub->azns_func] = stub;
607047043c2SRobert Mustacchi 	}
608047043c2SRobert Mustacchi 
609047043c2SRobert Mustacchi 	return (B_TRUE);
610047043c2SRobert Mustacchi 
611047043c2SRobert Mustacchi err:
612047043c2SRobert Mustacchi 	azn->azn_flags |= AMDZEN_F_DEVICE_ERROR;
613047043c2SRobert Mustacchi 	return (B_FALSE);
614047043c2SRobert Mustacchi }
615047043c2SRobert Mustacchi 
616047043c2SRobert Mustacchi static boolean_t
617047043c2SRobert Mustacchi amdzen_check_dfs(amdzen_t *azn)
618047043c2SRobert Mustacchi {
619047043c2SRobert Mustacchi 	uint_t i;
620047043c2SRobert Mustacchi 	boolean_t ret = B_TRUE;
621047043c2SRobert Mustacchi 
622047043c2SRobert Mustacchi 	for (i = 0; i < AMDZEN_MAX_DFS; i++) {
623047043c2SRobert Mustacchi 		amdzen_df_t *df = &azn->azn_dfs[i];
624047043c2SRobert Mustacchi 		uint_t count = 0;
625047043c2SRobert Mustacchi 
626047043c2SRobert Mustacchi 		/*
627047043c2SRobert Mustacchi 		 * We require all platforms to have DFs functions 0-6. Not all
628047043c2SRobert Mustacchi 		 * platforms have DF function 7.
629047043c2SRobert Mustacchi 		 */
630047043c2SRobert Mustacchi 		for (uint_t func = 0; func < AMDZEN_MAX_DF_FUNCS - 1; func++) {
631047043c2SRobert Mustacchi 			if (df->adf_funcs[func] != NULL) {
632047043c2SRobert Mustacchi 				count++;
633047043c2SRobert Mustacchi 			}
634047043c2SRobert Mustacchi 		}
635047043c2SRobert Mustacchi 
636047043c2SRobert Mustacchi 		if (count == 0)
637047043c2SRobert Mustacchi 			continue;
638047043c2SRobert Mustacchi 
639047043c2SRobert Mustacchi 		if (count != 7) {
640047043c2SRobert Mustacchi 			ret = B_FALSE;
641047043c2SRobert Mustacchi 			dev_err(azn->azn_dip, CE_WARN, "df %u devices "
642047043c2SRobert Mustacchi 			    "incomplete", i);
643047043c2SRobert Mustacchi 		} else {
644047043c2SRobert Mustacchi 			df->adf_flags |= AMDZEN_DF_F_VALID;
645047043c2SRobert Mustacchi 			azn->azn_ndfs++;
646047043c2SRobert Mustacchi 		}
647047043c2SRobert Mustacchi 	}
648047043c2SRobert Mustacchi 
649047043c2SRobert Mustacchi 	return (ret);
650047043c2SRobert Mustacchi }
651047043c2SRobert Mustacchi 
652047043c2SRobert Mustacchi static const uint8_t amdzen_df_rome_ids[0x2b] = {
653047043c2SRobert Mustacchi 	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20, 21, 22, 23,
654047043c2SRobert Mustacchi 	24, 25, 26, 27, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
655047043c2SRobert Mustacchi 	44, 45, 46, 47, 48
656047043c2SRobert Mustacchi };
657047043c2SRobert Mustacchi 
658047043c2SRobert Mustacchi /*
659e9abe9d6SRobert Mustacchi  * Check the first df entry to see if it belongs to Rome or Milan. If so, then
660e9abe9d6SRobert Mustacchi  * it uses the disjoint ID space.
661e9abe9d6SRobert Mustacchi  */
662e9abe9d6SRobert Mustacchi static boolean_t
663e9abe9d6SRobert Mustacchi amdzen_is_rome_style(uint_t id)
664e9abe9d6SRobert Mustacchi {
665e9abe9d6SRobert Mustacchi 	return (id == 0x1490 || id == 0x1650);
666e9abe9d6SRobert Mustacchi }
667e9abe9d6SRobert Mustacchi 
668e9abe9d6SRobert Mustacchi /*
66971815ce7SRobert Mustacchi  * To be able to do most other things we want to do, we must first determine
67071815ce7SRobert Mustacchi  * what revision of the DF (data fabric) that we're using.
67171815ce7SRobert Mustacchi  *
67271815ce7SRobert Mustacchi  * Snapshot the df version. This was added explicitly in DFv4.0, around the Zen
67371815ce7SRobert Mustacchi  * 4 timeframe and allows us to tell apart different version of the DF register
67471815ce7SRobert Mustacchi  * set, most usefully when various subtypes were added.
67571815ce7SRobert Mustacchi  *
67671815ce7SRobert Mustacchi  * Older versions can theoretically be told apart based on usage of reserved
67771815ce7SRobert Mustacchi  * registers. We walk these in the following order, starting with the newest rev
67871815ce7SRobert Mustacchi  * and walking backwards to tell things apart:
67971815ce7SRobert Mustacchi  *
68071815ce7SRobert Mustacchi  *   o v3.5 -> Check function 1, register 0x150. This was reserved prior
68171815ce7SRobert Mustacchi  *             to this point. This is actually DF_FIDMASK0_V3P5. We are supposed
68271815ce7SRobert Mustacchi  *             to check bits [7:0].
68371815ce7SRobert Mustacchi  *
68471815ce7SRobert Mustacchi  *   o v3.0 -> Check function 1, register 0x208. The low byte (7:0) was
68571815ce7SRobert Mustacchi  *             changed to indicate a component mask. This is non-zero
68671815ce7SRobert Mustacchi  *             in the 3.0 generation. This is actually DF_FIDMASK_V2.
68771815ce7SRobert Mustacchi  *
68871815ce7SRobert Mustacchi  *   o v2.0 -> This is just the not that case. Presumably v1 wasn't part
68971815ce7SRobert Mustacchi  *             of the Zen generation.
69071815ce7SRobert Mustacchi  *
69171815ce7SRobert Mustacchi  * Because we don't know what version we are yet, we do not use the normal
69271815ce7SRobert Mustacchi  * versioned register accesses which would check what DF version we are and
69371815ce7SRobert Mustacchi  * would want to use the normal indirect register accesses (which also require
69471815ce7SRobert Mustacchi  * us to know the version). We instead do direct broadcast reads.
69571815ce7SRobert Mustacchi  */
69671815ce7SRobert Mustacchi static void
69771815ce7SRobert Mustacchi amdzen_determine_df_vers(amdzen_t *azn, amdzen_df_t *df)
69871815ce7SRobert Mustacchi {
69971815ce7SRobert Mustacchi 	uint32_t val;
70071815ce7SRobert Mustacchi 	df_reg_def_t rd = DF_FBICNT;
70171815ce7SRobert Mustacchi 
70271815ce7SRobert Mustacchi 	val = amdzen_stub_get32(df->adf_funcs[rd.drd_func], rd.drd_reg);
70371815ce7SRobert Mustacchi 	df->adf_major = DF_FBICNT_V4_GET_MAJOR(val);
70471815ce7SRobert Mustacchi 	df->adf_minor = DF_FBICNT_V4_GET_MINOR(val);
70571815ce7SRobert Mustacchi 	if (df->adf_major == 0 && df->adf_minor == 0) {
70671815ce7SRobert Mustacchi 		rd = DF_FIDMASK0_V3P5;
70771815ce7SRobert Mustacchi 		val = amdzen_stub_get32(df->adf_funcs[rd.drd_func], rd.drd_reg);
70871815ce7SRobert Mustacchi 		if (bitx32(val, 7, 0) != 0) {
70971815ce7SRobert Mustacchi 			df->adf_major = 3;
71071815ce7SRobert Mustacchi 			df->adf_minor = 5;
71171815ce7SRobert Mustacchi 			df->adf_rev = DF_REV_3P5;
71271815ce7SRobert Mustacchi 		} else {
71371815ce7SRobert Mustacchi 			rd = DF_FIDMASK_V2;
71471815ce7SRobert Mustacchi 			val = amdzen_stub_get32(df->adf_funcs[rd.drd_func],
71571815ce7SRobert Mustacchi 			    rd.drd_reg);
71671815ce7SRobert Mustacchi 			if (bitx32(val, 7, 0) != 0) {
71771815ce7SRobert Mustacchi 				df->adf_major = 3;
71871815ce7SRobert Mustacchi 				df->adf_minor = 0;
71971815ce7SRobert Mustacchi 				df->adf_rev = DF_REV_3;
72071815ce7SRobert Mustacchi 			} else {
72171815ce7SRobert Mustacchi 				df->adf_major = 2;
72271815ce7SRobert Mustacchi 				df->adf_minor = 0;
72371815ce7SRobert Mustacchi 				df->adf_rev = DF_REV_2;
72471815ce7SRobert Mustacchi 			}
72571815ce7SRobert Mustacchi 		}
72671815ce7SRobert Mustacchi 	} else if (df->adf_major == 4 && df->adf_minor == 0) {
72771815ce7SRobert Mustacchi 		df->adf_rev = DF_REV_4;
72871815ce7SRobert Mustacchi 	} else {
72971815ce7SRobert Mustacchi 		df->adf_rev = DF_REV_UNKNOWN;
73071815ce7SRobert Mustacchi 	}
73171815ce7SRobert Mustacchi }
73271815ce7SRobert Mustacchi 
73371815ce7SRobert Mustacchi /*
73471815ce7SRobert Mustacchi  * All of the different versions of the DF have different ways of getting at and
73571815ce7SRobert Mustacchi  * answering the question of how do I break a fabric ID into a corresponding
73671815ce7SRobert Mustacchi  * socket, die, and component. Importantly the goal here is to obtain, cache,
73771815ce7SRobert Mustacchi  * and normalize:
73871815ce7SRobert Mustacchi  *
73971815ce7SRobert Mustacchi  *  o The DF System Configuration
74071815ce7SRobert Mustacchi  *  o The various Mask registers
74171815ce7SRobert Mustacchi  *  o The Node ID
74271815ce7SRobert Mustacchi  */
74371815ce7SRobert Mustacchi static void
74471815ce7SRobert Mustacchi amdzen_determine_fabric_decomp(amdzen_t *azn, amdzen_df_t *df)
74571815ce7SRobert Mustacchi {
74671815ce7SRobert Mustacchi 	uint32_t mask;
74771815ce7SRobert Mustacchi 	df_fabric_decomp_t *decomp = &df->adf_decomp;
74871815ce7SRobert Mustacchi 
74971815ce7SRobert Mustacchi 	switch (df->adf_rev) {
75071815ce7SRobert Mustacchi 	case DF_REV_2:
75171815ce7SRobert Mustacchi 		df->adf_syscfg = amdzen_df_read32_bcast(azn, df, DF_SYSCFG_V2);
75271815ce7SRobert Mustacchi 		switch (DF_SYSCFG_V2_GET_MY_TYPE(df->adf_syscfg)) {
75371815ce7SRobert Mustacchi 		case DF_DIE_TYPE_CPU:
75471815ce7SRobert Mustacchi 			mask = amdzen_df_read32_bcast(azn, df,
75571815ce7SRobert Mustacchi 			    DF_DIEMASK_CPU_V2);
75671815ce7SRobert Mustacchi 			break;
75771815ce7SRobert Mustacchi 		case DF_DIE_TYPE_APU:
75871815ce7SRobert Mustacchi 			mask = amdzen_df_read32_bcast(azn, df,
75971815ce7SRobert Mustacchi 			    DF_DIEMASK_APU_V2);
76071815ce7SRobert Mustacchi 			break;
76171815ce7SRobert Mustacchi 		default:
76271815ce7SRobert Mustacchi 			panic("DF thinks we're not on a CPU!");
76371815ce7SRobert Mustacchi 		}
76471815ce7SRobert Mustacchi 		df->adf_mask0 = mask;
76571815ce7SRobert Mustacchi 
76671815ce7SRobert Mustacchi 		/*
76771815ce7SRobert Mustacchi 		 * DFv2 is a bit different in how the fabric mask register is
76871815ce7SRobert Mustacchi 		 * phrased. Logically a fabric ID is broken into something that
76971815ce7SRobert Mustacchi 		 * uniquely identifies a "node" (a particular die on a socket)
77071815ce7SRobert Mustacchi 		 * and something that identifies a "component", e.g. a memory
77171815ce7SRobert Mustacchi 		 * controller.
77271815ce7SRobert Mustacchi 		 *
77371815ce7SRobert Mustacchi 		 * Starting with DFv3, these registers logically called out how
77471815ce7SRobert Mustacchi 		 * to separate the fabric ID first into a node and a component.
77571815ce7SRobert Mustacchi 		 * Then the node was then broken down into a socket and die. In
77671815ce7SRobert Mustacchi 		 * DFv2, there is no separate mask and shift of a node. Instead
77771815ce7SRobert Mustacchi 		 * the socket and die are absolute offsets into the fabric ID
77871815ce7SRobert Mustacchi 		 * rather than relative offsets into the node ID. As such, when
77971815ce7SRobert Mustacchi 		 * we encounter DFv2, we fake up a node mask and shift and make
78071815ce7SRobert Mustacchi 		 * it look like DFv3+.
78171815ce7SRobert Mustacchi 		 */
78271815ce7SRobert Mustacchi 		decomp->dfd_node_mask = DF_DIEMASK_V2_GET_SOCK_MASK(mask) |
78371815ce7SRobert Mustacchi 		    DF_DIEMASK_V2_GET_DIE_MASK(mask);
78471815ce7SRobert Mustacchi 		decomp->dfd_node_shift = DF_DIEMASK_V2_GET_DIE_SHIFT(mask);
78571815ce7SRobert Mustacchi 		decomp->dfd_comp_mask = DF_DIEMASK_V2_GET_COMP_MASK(mask);
78671815ce7SRobert Mustacchi 		decomp->dfd_comp_shift = 0;
78771815ce7SRobert Mustacchi 
78871815ce7SRobert Mustacchi 		decomp->dfd_sock_mask = DF_DIEMASK_V2_GET_SOCK_MASK(mask) >>
78971815ce7SRobert Mustacchi 		    decomp->dfd_node_shift;
79071815ce7SRobert Mustacchi 		decomp->dfd_die_mask = DF_DIEMASK_V2_GET_DIE_MASK(mask) >>
79171815ce7SRobert Mustacchi 		    decomp->dfd_node_shift;
79271815ce7SRobert Mustacchi 		decomp->dfd_sock_shift = DF_DIEMASK_V2_GET_SOCK_SHIFT(mask) -
79371815ce7SRobert Mustacchi 		    decomp->dfd_node_shift;
79471815ce7SRobert Mustacchi 		decomp->dfd_die_shift = DF_DIEMASK_V2_GET_DIE_SHIFT(mask) -
79571815ce7SRobert Mustacchi 		    decomp->dfd_node_shift;
79671815ce7SRobert Mustacchi 		ASSERT3U(decomp->dfd_die_shift, ==, 0);
79771815ce7SRobert Mustacchi 		break;
79871815ce7SRobert Mustacchi 	case DF_REV_3:
79971815ce7SRobert Mustacchi 		df->adf_syscfg = amdzen_df_read32_bcast(azn, df, DF_SYSCFG_V3);
80071815ce7SRobert Mustacchi 		df->adf_mask0 =  amdzen_df_read32_bcast(azn, df,
80171815ce7SRobert Mustacchi 		    DF_FIDMASK0_V3);
80271815ce7SRobert Mustacchi 		df->adf_mask1 =  amdzen_df_read32_bcast(azn, df,
80371815ce7SRobert Mustacchi 		    DF_FIDMASK1_V3);
80471815ce7SRobert Mustacchi 
80571815ce7SRobert Mustacchi 		decomp->dfd_sock_mask =
80671815ce7SRobert Mustacchi 		    DF_FIDMASK1_V3_GET_SOCK_MASK(df->adf_mask1);
80771815ce7SRobert Mustacchi 		decomp->dfd_sock_shift =
80871815ce7SRobert Mustacchi 		    DF_FIDMASK1_V3_GET_SOCK_SHIFT(df->adf_mask1);
80971815ce7SRobert Mustacchi 		decomp->dfd_die_mask =
81071815ce7SRobert Mustacchi 		    DF_FIDMASK1_V3_GET_DIE_MASK(df->adf_mask1);
81171815ce7SRobert Mustacchi 		decomp->dfd_die_shift = 0;
81271815ce7SRobert Mustacchi 		decomp->dfd_node_mask =
81371815ce7SRobert Mustacchi 		    DF_FIDMASK0_V3_GET_NODE_MASK(df->adf_mask0);
81471815ce7SRobert Mustacchi 		decomp->dfd_node_shift =
81571815ce7SRobert Mustacchi 		    DF_FIDMASK1_V3_GET_NODE_SHIFT(df->adf_mask1);
81671815ce7SRobert Mustacchi 		decomp->dfd_comp_mask =
81771815ce7SRobert Mustacchi 		    DF_FIDMASK0_V3_GET_COMP_MASK(df->adf_mask0);
81871815ce7SRobert Mustacchi 		decomp->dfd_comp_shift = 0;
81971815ce7SRobert Mustacchi 		break;
82071815ce7SRobert Mustacchi 	case DF_REV_3P5:
82171815ce7SRobert Mustacchi 		df->adf_syscfg = amdzen_df_read32_bcast(azn, df,
82271815ce7SRobert Mustacchi 		    DF_SYSCFG_V3P5);
82371815ce7SRobert Mustacchi 		df->adf_mask0 =  amdzen_df_read32_bcast(azn, df,
82471815ce7SRobert Mustacchi 		    DF_FIDMASK0_V3P5);
82571815ce7SRobert Mustacchi 		df->adf_mask1 =  amdzen_df_read32_bcast(azn, df,
82671815ce7SRobert Mustacchi 		    DF_FIDMASK1_V3P5);
82771815ce7SRobert Mustacchi 		df->adf_mask2 =  amdzen_df_read32_bcast(azn, df,
82871815ce7SRobert Mustacchi 		    DF_FIDMASK2_V3P5);
82971815ce7SRobert Mustacchi 
83071815ce7SRobert Mustacchi 		decomp->dfd_sock_mask =
83171815ce7SRobert Mustacchi 		    DF_FIDMASK2_V3P5_GET_SOCK_MASK(df->adf_mask2);
83271815ce7SRobert Mustacchi 		decomp->dfd_sock_shift =
83371815ce7SRobert Mustacchi 		    DF_FIDMASK1_V3P5_GET_SOCK_SHIFT(df->adf_mask1);
83471815ce7SRobert Mustacchi 		decomp->dfd_die_mask =
83571815ce7SRobert Mustacchi 		    DF_FIDMASK2_V3P5_GET_DIE_MASK(df->adf_mask2);
83671815ce7SRobert Mustacchi 		decomp->dfd_die_shift = 0;
83771815ce7SRobert Mustacchi 		decomp->dfd_node_mask =
83871815ce7SRobert Mustacchi 		    DF_FIDMASK0_V3P5_GET_NODE_MASK(df->adf_mask0);
83971815ce7SRobert Mustacchi 		decomp->dfd_node_shift =
84071815ce7SRobert Mustacchi 		    DF_FIDMASK1_V3P5_GET_NODE_SHIFT(df->adf_mask1);
84171815ce7SRobert Mustacchi 		decomp->dfd_comp_mask =
84271815ce7SRobert Mustacchi 		    DF_FIDMASK0_V3P5_GET_COMP_MASK(df->adf_mask0);
84371815ce7SRobert Mustacchi 		decomp->dfd_comp_shift = 0;
84471815ce7SRobert Mustacchi 		break;
84571815ce7SRobert Mustacchi 	case DF_REV_4:
84671815ce7SRobert Mustacchi 		df->adf_syscfg = amdzen_df_read32_bcast(azn, df, DF_SYSCFG_V4);
84771815ce7SRobert Mustacchi 		df->adf_mask0 =  amdzen_df_read32_bcast(azn, df,
84871815ce7SRobert Mustacchi 		    DF_FIDMASK0_V4);
84971815ce7SRobert Mustacchi 		df->adf_mask1 =  amdzen_df_read32_bcast(azn, df,
85071815ce7SRobert Mustacchi 		    DF_FIDMASK1_V4);
85171815ce7SRobert Mustacchi 		df->adf_mask2 =  amdzen_df_read32_bcast(azn, df,
85271815ce7SRobert Mustacchi 		    DF_FIDMASK2_V4);
85371815ce7SRobert Mustacchi 
85471815ce7SRobert Mustacchi 		/*
85571815ce7SRobert Mustacchi 		 * The DFv4 registers are at a different location in the DF;
85671815ce7SRobert Mustacchi 		 * however, the actual layout of fields is the same as DFv3.5.
85771815ce7SRobert Mustacchi 		 * This is why you see V3P5 below.
85871815ce7SRobert Mustacchi 		 */
85971815ce7SRobert Mustacchi 		decomp->dfd_sock_mask =
86071815ce7SRobert Mustacchi 		    DF_FIDMASK2_V3P5_GET_SOCK_MASK(df->adf_mask2);
86171815ce7SRobert Mustacchi 		decomp->dfd_sock_shift =
86271815ce7SRobert Mustacchi 		    DF_FIDMASK1_V3P5_GET_SOCK_SHIFT(df->adf_mask1);
86371815ce7SRobert Mustacchi 		decomp->dfd_die_mask =
86471815ce7SRobert Mustacchi 		    DF_FIDMASK2_V3P5_GET_DIE_MASK(df->adf_mask2);
86571815ce7SRobert Mustacchi 		decomp->dfd_die_shift = 0;
86671815ce7SRobert Mustacchi 		decomp->dfd_node_mask =
86771815ce7SRobert Mustacchi 		    DF_FIDMASK0_V3P5_GET_NODE_MASK(df->adf_mask0);
86871815ce7SRobert Mustacchi 		decomp->dfd_node_shift =
86971815ce7SRobert Mustacchi 		    DF_FIDMASK1_V3P5_GET_NODE_SHIFT(df->adf_mask1);
87071815ce7SRobert Mustacchi 		decomp->dfd_comp_mask =
87171815ce7SRobert Mustacchi 		    DF_FIDMASK0_V3P5_GET_COMP_MASK(df->adf_mask0);
87271815ce7SRobert Mustacchi 		decomp->dfd_comp_shift = 0;
87371815ce7SRobert Mustacchi 		break;
87471815ce7SRobert Mustacchi 	default:
87571815ce7SRobert Mustacchi 		panic("encountered suspicious, previously rejected DF "
87671815ce7SRobert Mustacchi 		    "rev: 0x%x", df->adf_rev);
87771815ce7SRobert Mustacchi 	}
87871815ce7SRobert Mustacchi }
87971815ce7SRobert Mustacchi 
88071815ce7SRobert Mustacchi /*
881047043c2SRobert Mustacchi  * Initialize our knowledge about a given series of nodes on the data fabric.
882047043c2SRobert Mustacchi  */
883047043c2SRobert Mustacchi static void
884047043c2SRobert Mustacchi amdzen_setup_df(amdzen_t *azn, amdzen_df_t *df)
885047043c2SRobert Mustacchi {
886047043c2SRobert Mustacchi 	uint_t i;
887047043c2SRobert Mustacchi 	uint32_t val;
888047043c2SRobert Mustacchi 
88971815ce7SRobert Mustacchi 	amdzen_determine_df_vers(azn, df);
89071815ce7SRobert Mustacchi 
89171815ce7SRobert Mustacchi 	switch (df->adf_rev) {
89271815ce7SRobert Mustacchi 	case DF_REV_2:
89371815ce7SRobert Mustacchi 	case DF_REV_3:
89471815ce7SRobert Mustacchi 	case DF_REV_3P5:
89571815ce7SRobert Mustacchi 		val = amdzen_df_read32_bcast(azn, df, DF_CFG_ADDR_CTL_V2);
89671815ce7SRobert Mustacchi 		break;
89771815ce7SRobert Mustacchi 	case DF_REV_4:
89871815ce7SRobert Mustacchi 		val = amdzen_df_read32_bcast(azn, df, DF_CFG_ADDR_CTL_V4);
89971815ce7SRobert Mustacchi 		break;
90071815ce7SRobert Mustacchi 	default:
90171815ce7SRobert Mustacchi 		dev_err(azn->azn_dip, CE_WARN, "encountered unsupported DF "
90271815ce7SRobert Mustacchi 		    "revision: 0x%x", df->adf_rev);
90371815ce7SRobert Mustacchi 		return;
90471815ce7SRobert Mustacchi 	}
90571815ce7SRobert Mustacchi 	df->adf_nb_busno = DF_CFG_ADDR_CTL_GET_BUS_NUM(val);
90671815ce7SRobert Mustacchi 	val = amdzen_df_read32_bcast(azn, df, DF_FBICNT);
90771815ce7SRobert Mustacchi 	df->adf_nents = DF_FBICNT_GET_COUNT(val);
908047043c2SRobert Mustacchi 	if (df->adf_nents == 0)
909047043c2SRobert Mustacchi 		return;
910047043c2SRobert Mustacchi 	df->adf_ents = kmem_zalloc(sizeof (amdzen_df_ent_t) * df->adf_nents,
911047043c2SRobert Mustacchi 	    KM_SLEEP);
912047043c2SRobert Mustacchi 
913047043c2SRobert Mustacchi 	for (i = 0; i < df->adf_nents; i++) {
914047043c2SRobert Mustacchi 		amdzen_df_ent_t *dfe = &df->adf_ents[i];
915047043c2SRobert Mustacchi 		uint8_t inst = i;
916047043c2SRobert Mustacchi 
917047043c2SRobert Mustacchi 		/*
918047043c2SRobert Mustacchi 		 * Unfortunately, Rome uses a discontinuous instance ID pattern
919047043c2SRobert Mustacchi 		 * while everything else we can find uses a contiguous instance
920047043c2SRobert Mustacchi 		 * ID pattern.  This means that for Rome, we need to adjust the
921047043c2SRobert Mustacchi 		 * indexes that we iterate over, though the total number of
92271815ce7SRobert Mustacchi 		 * entries is right. This was carried over into Milan, but not
92371815ce7SRobert Mustacchi 		 * Genoa.
924047043c2SRobert Mustacchi 		 */
925e9abe9d6SRobert Mustacchi 		if (amdzen_is_rome_style(df->adf_funcs[0]->azns_did)) {
926047043c2SRobert Mustacchi 			if (inst > ARRAY_SIZE(amdzen_df_rome_ids)) {
927047043c2SRobert Mustacchi 				dev_err(azn->azn_dip, CE_WARN, "Rome family "
928047043c2SRobert Mustacchi 				    "processor reported more ids than the PPR, "
929e9abe9d6SRobert Mustacchi 				    "resetting %u to instance zero", inst);
930047043c2SRobert Mustacchi 				inst = 0;
931047043c2SRobert Mustacchi 			} else {
932047043c2SRobert Mustacchi 				inst = amdzen_df_rome_ids[inst];
933047043c2SRobert Mustacchi 			}
934047043c2SRobert Mustacchi 		}
935047043c2SRobert Mustacchi 
936047043c2SRobert Mustacchi 		dfe->adfe_drvid = inst;
93771815ce7SRobert Mustacchi 		dfe->adfe_info0 = amdzen_df_read32(azn, df, inst, DF_FBIINFO0);
93871815ce7SRobert Mustacchi 		dfe->adfe_info1 = amdzen_df_read32(azn, df, inst, DF_FBIINFO1);
93971815ce7SRobert Mustacchi 		dfe->adfe_info2 = amdzen_df_read32(azn, df, inst, DF_FBIINFO2);
94071815ce7SRobert Mustacchi 		dfe->adfe_info3 = amdzen_df_read32(azn, df, inst, DF_FBIINFO3);
941047043c2SRobert Mustacchi 
94271815ce7SRobert Mustacchi 		dfe->adfe_type = DF_FBIINFO0_GET_TYPE(dfe->adfe_info0);
94371815ce7SRobert Mustacchi 		dfe->adfe_subtype = DF_FBIINFO0_GET_SUBTYPE(dfe->adfe_info0);
94471815ce7SRobert Mustacchi 
94571815ce7SRobert Mustacchi 		/*
94671815ce7SRobert Mustacchi 		 * The enabled flag was not present in Zen 1. Simulate it by
94771815ce7SRobert Mustacchi 		 * checking for a non-zero register instead.
94871815ce7SRobert Mustacchi 		 */
94971815ce7SRobert Mustacchi 		if (DF_FBIINFO0_V3_GET_ENABLED(dfe->adfe_info0) ||
95071815ce7SRobert Mustacchi 		    (df->adf_rev == DF_REV_2 && dfe->adfe_info0 != 0)) {
951047043c2SRobert Mustacchi 			dfe->adfe_flags |= AMDZEN_DFE_F_ENABLED;
952047043c2SRobert Mustacchi 		}
95371815ce7SRobert Mustacchi 		if (DF_FBIINFO0_GET_HAS_MCA(dfe->adfe_info0)) {
954047043c2SRobert Mustacchi 			dfe->adfe_flags |= AMDZEN_DFE_F_MCA;
955047043c2SRobert Mustacchi 		}
95671815ce7SRobert Mustacchi 		dfe->adfe_inst_id = DF_FBIINFO3_GET_INSTID(dfe->adfe_info3);
95771815ce7SRobert Mustacchi 		switch (df->adf_rev) {
95871815ce7SRobert Mustacchi 		case DF_REV_2:
959047043c2SRobert Mustacchi 			dfe->adfe_fabric_id =
96071815ce7SRobert Mustacchi 			    DF_FBIINFO3_V2_GET_BLOCKID(dfe->adfe_info3);
96171815ce7SRobert Mustacchi 			break;
96271815ce7SRobert Mustacchi 		case DF_REV_3:
96371815ce7SRobert Mustacchi 			dfe->adfe_fabric_id =
96471815ce7SRobert Mustacchi 			    DF_FBIINFO3_V3_GET_BLOCKID(dfe->adfe_info3);
96571815ce7SRobert Mustacchi 			break;
96671815ce7SRobert Mustacchi 		case DF_REV_3P5:
96771815ce7SRobert Mustacchi 			dfe->adfe_fabric_id =
96871815ce7SRobert Mustacchi 			    DF_FBIINFO3_V3P5_GET_BLOCKID(dfe->adfe_info3);
96971815ce7SRobert Mustacchi 			break;
97071815ce7SRobert Mustacchi 		case DF_REV_4:
97171815ce7SRobert Mustacchi 			dfe->adfe_fabric_id =
97271815ce7SRobert Mustacchi 			    DF_FBIINFO3_V4_GET_BLOCKID(dfe->adfe_info3);
97371815ce7SRobert Mustacchi 			break;
97471815ce7SRobert Mustacchi 		default:
97571815ce7SRobert Mustacchi 			panic("encountered suspicious, previously rejected DF "
97671815ce7SRobert Mustacchi 			    "rev: 0x%x", df->adf_rev);
97771815ce7SRobert Mustacchi 		}
978047043c2SRobert Mustacchi 	}
979047043c2SRobert Mustacchi 
98071815ce7SRobert Mustacchi 	amdzen_determine_fabric_decomp(azn, df);
981047043c2SRobert Mustacchi }
982047043c2SRobert Mustacchi 
983047043c2SRobert Mustacchi static void
984047043c2SRobert Mustacchi amdzen_find_nb(amdzen_t *azn, amdzen_df_t *df)
985047043c2SRobert Mustacchi {
986047043c2SRobert Mustacchi 	amdzen_stub_t *stub;
987047043c2SRobert Mustacchi 
988047043c2SRobert Mustacchi 	for (stub = list_head(&azn->azn_nb_stubs); stub != NULL;
989047043c2SRobert Mustacchi 	    stub = list_next(&azn->azn_nb_stubs, stub)) {
990047043c2SRobert Mustacchi 		if (stub->azns_bus == df->adf_nb_busno) {
991047043c2SRobert Mustacchi 			df->adf_flags |= AMDZEN_DF_F_FOUND_NB;
992047043c2SRobert Mustacchi 			df->adf_nb = stub;
993047043c2SRobert Mustacchi 			return;
994047043c2SRobert Mustacchi 		}
995047043c2SRobert Mustacchi 	}
996047043c2SRobert Mustacchi }
997047043c2SRobert Mustacchi 
998047043c2SRobert Mustacchi static void
999047043c2SRobert Mustacchi amdzen_nexus_init(void *arg)
1000047043c2SRobert Mustacchi {
1001047043c2SRobert Mustacchi 	uint_t i;
1002047043c2SRobert Mustacchi 	amdzen_t *azn = arg;
1003047043c2SRobert Mustacchi 
1004047043c2SRobert Mustacchi 	/*
1005047043c2SRobert Mustacchi 	 * First go through all of the stubs and assign the DF entries.
1006047043c2SRobert Mustacchi 	 */
1007047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
1008047043c2SRobert Mustacchi 	if (!amdzen_map_dfs(azn) || !amdzen_check_dfs(azn)) {
1009047043c2SRobert Mustacchi 		azn->azn_flags |= AMDZEN_F_MAP_ERROR;
1010047043c2SRobert Mustacchi 		goto done;
1011047043c2SRobert Mustacchi 	}
1012047043c2SRobert Mustacchi 
1013047043c2SRobert Mustacchi 	for (i = 0; i < AMDZEN_MAX_DFS; i++) {
1014047043c2SRobert Mustacchi 		amdzen_df_t *df = &azn->azn_dfs[i];
1015047043c2SRobert Mustacchi 
1016047043c2SRobert Mustacchi 		if ((df->adf_flags & AMDZEN_DF_F_VALID) == 0)
1017047043c2SRobert Mustacchi 			continue;
1018047043c2SRobert Mustacchi 		amdzen_setup_df(azn, df);
1019047043c2SRobert Mustacchi 		amdzen_find_nb(azn, df);
1020047043c2SRobert Mustacchi 	}
1021047043c2SRobert Mustacchi 
1022047043c2SRobert Mustacchi 	/*
1023047043c2SRobert Mustacchi 	 * Not all children may be installed. As such, we do not treat the
1024047043c2SRobert Mustacchi 	 * failure of a child as fatal to the driver.
1025047043c2SRobert Mustacchi 	 */
1026047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
1027047043c2SRobert Mustacchi 	for (i = 0; i < ARRAY_SIZE(amdzen_children); i++) {
1028047043c2SRobert Mustacchi 		(void) amdzen_create_child(azn, &amdzen_children[i]);
1029047043c2SRobert Mustacchi 	}
1030047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
1031047043c2SRobert Mustacchi 
1032047043c2SRobert Mustacchi done:
1033047043c2SRobert Mustacchi 	azn->azn_flags &= ~AMDZEN_F_ATTACH_DISPATCHED;
1034047043c2SRobert Mustacchi 	azn->azn_flags |= AMDZEN_F_ATTACH_COMPLETE;
1035047043c2SRobert Mustacchi 	azn->azn_taskqid = TASKQID_INVALID;
1036047043c2SRobert Mustacchi 	cv_broadcast(&azn->azn_cv);
1037047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
1038047043c2SRobert Mustacchi }
1039047043c2SRobert Mustacchi 
1040047043c2SRobert Mustacchi static int
1041047043c2SRobert Mustacchi amdzen_stub_scan_cb(dev_info_t *dip, void *arg)
1042047043c2SRobert Mustacchi {
1043047043c2SRobert Mustacchi 	amdzen_t *azn = arg;
1044047043c2SRobert Mustacchi 	uint16_t vid, did;
1045047043c2SRobert Mustacchi 	int *regs;
1046047043c2SRobert Mustacchi 	uint_t nregs, i;
1047047043c2SRobert Mustacchi 	boolean_t match = B_FALSE;
1048047043c2SRobert Mustacchi 
1049047043c2SRobert Mustacchi 	if (dip == ddi_root_node()) {
1050047043c2SRobert Mustacchi 		return (DDI_WALK_CONTINUE);
1051047043c2SRobert Mustacchi 	}
1052047043c2SRobert Mustacchi 
1053047043c2SRobert Mustacchi 	/*
1054047043c2SRobert Mustacchi 	 * If a node in question is not a pci node, then we have no interest in
1055047043c2SRobert Mustacchi 	 * it as all the stubs that we care about are related to pci devices.
1056047043c2SRobert Mustacchi 	 */
1057047043c2SRobert Mustacchi 	if (strncmp("pci", ddi_get_name(dip), 3) != 0) {
1058047043c2SRobert Mustacchi 		return (DDI_WALK_PRUNECHILD);
1059047043c2SRobert Mustacchi 	}
1060047043c2SRobert Mustacchi 
1061047043c2SRobert Mustacchi 	/*
1062047043c2SRobert Mustacchi 	 * If we can't get a device or vendor ID and prove that this is an AMD
1063047043c2SRobert Mustacchi 	 * part, then we don't care about it.
1064047043c2SRobert Mustacchi 	 */
1065047043c2SRobert Mustacchi 	vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1066047043c2SRobert Mustacchi 	    "vendor-id", PCI_EINVAL16);
1067047043c2SRobert Mustacchi 	did = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1068047043c2SRobert Mustacchi 	    "device-id", PCI_EINVAL16);
1069047043c2SRobert Mustacchi 	if (vid == PCI_EINVAL16 || did == PCI_EINVAL16) {
1070047043c2SRobert Mustacchi 		return (DDI_WALK_CONTINUE);
1071047043c2SRobert Mustacchi 	}
1072047043c2SRobert Mustacchi 
10739b0429a1SPu Wen 	if (vid != AMDZEN_PCI_VID_AMD && vid != AMDZEN_PCI_VID_HYGON) {
1074047043c2SRobert Mustacchi 		return (DDI_WALK_CONTINUE);
1075047043c2SRobert Mustacchi 	}
1076047043c2SRobert Mustacchi 
1077047043c2SRobert Mustacchi 	for (i = 0; i < ARRAY_SIZE(amdzen_nb_ids); i++) {
1078047043c2SRobert Mustacchi 		if (amdzen_nb_ids[i] == did) {
1079047043c2SRobert Mustacchi 			match = B_TRUE;
1080047043c2SRobert Mustacchi 		}
1081047043c2SRobert Mustacchi 	}
1082047043c2SRobert Mustacchi 
1083047043c2SRobert Mustacchi 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1084047043c2SRobert Mustacchi 	    "reg", &regs, &nregs) != DDI_PROP_SUCCESS) {
1085047043c2SRobert Mustacchi 		return (DDI_WALK_CONTINUE);
1086047043c2SRobert Mustacchi 	}
1087047043c2SRobert Mustacchi 
1088047043c2SRobert Mustacchi 	if (nregs == 0) {
1089047043c2SRobert Mustacchi 		ddi_prop_free(regs);
1090047043c2SRobert Mustacchi 		return (DDI_WALK_CONTINUE);
1091047043c2SRobert Mustacchi 	}
1092047043c2SRobert Mustacchi 
1093047043c2SRobert Mustacchi 	if (PCI_REG_BUS_G(regs[0]) == AMDZEN_DF_BUSNO &&
1094047043c2SRobert Mustacchi 	    PCI_REG_DEV_G(regs[0]) >= AMDZEN_DF_FIRST_DEVICE) {
1095047043c2SRobert Mustacchi 		match = B_TRUE;
1096047043c2SRobert Mustacchi 	}
1097047043c2SRobert Mustacchi 
1098047043c2SRobert Mustacchi 	ddi_prop_free(regs);
1099047043c2SRobert Mustacchi 	if (match) {
1100047043c2SRobert Mustacchi 		mutex_enter(&azn->azn_mutex);
1101047043c2SRobert Mustacchi 		azn->azn_nscanned++;
1102047043c2SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
1103047043c2SRobert Mustacchi 	}
1104047043c2SRobert Mustacchi 
1105047043c2SRobert Mustacchi 	return (DDI_WALK_CONTINUE);
1106047043c2SRobert Mustacchi }
1107047043c2SRobert Mustacchi 
1108047043c2SRobert Mustacchi static void
1109047043c2SRobert Mustacchi amdzen_stub_scan(void *arg)
1110047043c2SRobert Mustacchi {
1111047043c2SRobert Mustacchi 	amdzen_t *azn = arg;
1112047043c2SRobert Mustacchi 
1113047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
1114047043c2SRobert Mustacchi 	azn->azn_nscanned = 0;
1115047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
1116047043c2SRobert Mustacchi 
1117047043c2SRobert Mustacchi 	ddi_walk_devs(ddi_root_node(), amdzen_stub_scan_cb, azn);
1118047043c2SRobert Mustacchi 
1119047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
1120047043c2SRobert Mustacchi 	azn->azn_flags &= ~AMDZEN_F_SCAN_DISPATCHED;
1121047043c2SRobert Mustacchi 	azn->azn_flags |= AMDZEN_F_SCAN_COMPLETE;
1122047043c2SRobert Mustacchi 
1123047043c2SRobert Mustacchi 	if (azn->azn_nscanned == 0) {
1124047043c2SRobert Mustacchi 		azn->azn_flags |= AMDZEN_F_UNSUPPORTED;
1125047043c2SRobert Mustacchi 		azn->azn_taskqid = TASKQID_INVALID;
1126047043c2SRobert Mustacchi 		cv_broadcast(&azn->azn_cv);
1127047043c2SRobert Mustacchi 	} else if (azn->azn_npresent == azn->azn_nscanned) {
1128047043c2SRobert Mustacchi 		azn->azn_flags |= AMDZEN_F_ATTACH_DISPATCHED;
1129047043c2SRobert Mustacchi 		azn->azn_taskqid = taskq_dispatch(system_taskq,
1130047043c2SRobert Mustacchi 		    amdzen_nexus_init, azn, TQ_SLEEP);
1131047043c2SRobert Mustacchi 	}
1132047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
1133047043c2SRobert Mustacchi }
1134047043c2SRobert Mustacchi 
1135047043c2SRobert Mustacchi /*
1136047043c2SRobert Mustacchi  * Unfortunately we can't really let the stubs detach as we may need them to be
1137047043c2SRobert Mustacchi  * available for client operations. We may be able to improve this if we know
1138047043c2SRobert Mustacchi  * that the actual nexus is going away. However, as long as it's active, we need
1139047043c2SRobert Mustacchi  * all the stubs.
1140047043c2SRobert Mustacchi  */
1141047043c2SRobert Mustacchi int
1142047043c2SRobert Mustacchi amdzen_detach_stub(dev_info_t *dip, ddi_detach_cmd_t cmd)
1143047043c2SRobert Mustacchi {
1144047043c2SRobert Mustacchi 	if (cmd == DDI_SUSPEND) {
1145047043c2SRobert Mustacchi 		return (DDI_SUCCESS);
1146047043c2SRobert Mustacchi 	}
1147047043c2SRobert Mustacchi 
1148047043c2SRobert Mustacchi 	return (DDI_FAILURE);
1149047043c2SRobert Mustacchi }
1150047043c2SRobert Mustacchi 
1151047043c2SRobert Mustacchi int
1152047043c2SRobert Mustacchi amdzen_attach_stub(dev_info_t *dip, ddi_attach_cmd_t cmd)
1153047043c2SRobert Mustacchi {
1154047043c2SRobert Mustacchi 	int *regs, reg;
1155047043c2SRobert Mustacchi 	uint_t nregs, i;
1156047043c2SRobert Mustacchi 	uint16_t vid, did;
1157047043c2SRobert Mustacchi 	amdzen_stub_t *stub;
1158047043c2SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
1159047043c2SRobert Mustacchi 	boolean_t valid = B_FALSE;
1160047043c2SRobert Mustacchi 	boolean_t nb = B_FALSE;
1161047043c2SRobert Mustacchi 
1162047043c2SRobert Mustacchi 	if (cmd == DDI_RESUME) {
1163047043c2SRobert Mustacchi 		return (DDI_SUCCESS);
1164047043c2SRobert Mustacchi 	} else if (cmd != DDI_ATTACH) {
1165047043c2SRobert Mustacchi 		return (DDI_FAILURE);
1166047043c2SRobert Mustacchi 	}
1167047043c2SRobert Mustacchi 
1168047043c2SRobert Mustacchi 	/*
1169047043c2SRobert Mustacchi 	 * Make sure that the stub that we've been asked to attach is a pci type
1170047043c2SRobert Mustacchi 	 * device. If not, then there is no reason for us to proceed.
1171047043c2SRobert Mustacchi 	 */
1172047043c2SRobert Mustacchi 	if (strncmp("pci", ddi_get_name(dip), 3) != 0) {
1173047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "asked to attach a bad AMD Zen nexus "
1174047043c2SRobert Mustacchi 		    "stub: %s", ddi_get_name(dip));
1175047043c2SRobert Mustacchi 		return (DDI_FAILURE);
1176047043c2SRobert Mustacchi 	}
1177047043c2SRobert Mustacchi 	vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1178047043c2SRobert Mustacchi 	    "vendor-id", PCI_EINVAL16);
1179047043c2SRobert Mustacchi 	did = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1180047043c2SRobert Mustacchi 	    "device-id", PCI_EINVAL16);
1181047043c2SRobert Mustacchi 	if (vid == PCI_EINVAL16 || did == PCI_EINVAL16) {
1182047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "failed to get PCI ID properties");
1183047043c2SRobert Mustacchi 		return (DDI_FAILURE);
1184047043c2SRobert Mustacchi 	}
1185047043c2SRobert Mustacchi 
11869b0429a1SPu Wen 	if (vid != AMDZEN_PCI_VID_AMD && vid != AMDZEN_PCI_VID_HYGON) {
11879b0429a1SPu Wen 		dev_err(dip, CE_WARN, "expected vendor ID (0x%x), found 0x%x",
11889b0429a1SPu Wen 		    cpuid_getvendor(CPU) == X86_VENDOR_HYGON ?
11899b0429a1SPu Wen 		    AMDZEN_PCI_VID_HYGON : AMDZEN_PCI_VID_AMD, vid);
1190047043c2SRobert Mustacchi 		return (DDI_FAILURE);
1191047043c2SRobert Mustacchi 	}
1192047043c2SRobert Mustacchi 
1193047043c2SRobert Mustacchi 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1194047043c2SRobert Mustacchi 	    "reg", &regs, &nregs) != DDI_PROP_SUCCESS) {
1195047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "failed to get 'reg' property");
1196047043c2SRobert Mustacchi 		return (DDI_FAILURE);
1197047043c2SRobert Mustacchi 	}
1198047043c2SRobert Mustacchi 
1199047043c2SRobert Mustacchi 	if (nregs == 0) {
1200047043c2SRobert Mustacchi 		ddi_prop_free(regs);
1201047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "missing 'reg' property values");
1202047043c2SRobert Mustacchi 		return (DDI_FAILURE);
1203047043c2SRobert Mustacchi 	}
1204047043c2SRobert Mustacchi 	reg = *regs;
1205047043c2SRobert Mustacchi 	ddi_prop_free(regs);
1206047043c2SRobert Mustacchi 
1207047043c2SRobert Mustacchi 	for (i = 0; i < ARRAY_SIZE(amdzen_nb_ids); i++) {
1208047043c2SRobert Mustacchi 		if (amdzen_nb_ids[i] == did) {
1209047043c2SRobert Mustacchi 			valid = B_TRUE;
1210047043c2SRobert Mustacchi 			nb = B_TRUE;
1211047043c2SRobert Mustacchi 		}
1212047043c2SRobert Mustacchi 	}
1213047043c2SRobert Mustacchi 
1214047043c2SRobert Mustacchi 	if (!valid && PCI_REG_BUS_G(reg) == AMDZEN_DF_BUSNO &&
1215047043c2SRobert Mustacchi 	    PCI_REG_DEV_G(reg) >= AMDZEN_DF_FIRST_DEVICE) {
1216047043c2SRobert Mustacchi 		valid = B_TRUE;
1217047043c2SRobert Mustacchi 		nb = B_FALSE;
1218047043c2SRobert Mustacchi 	}
1219047043c2SRobert Mustacchi 
1220047043c2SRobert Mustacchi 	if (!valid) {
1221047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "device %s didn't match the nexus list",
1222047043c2SRobert Mustacchi 		    ddi_get_name(dip));
1223047043c2SRobert Mustacchi 		return (DDI_FAILURE);
1224047043c2SRobert Mustacchi 	}
1225047043c2SRobert Mustacchi 
1226047043c2SRobert Mustacchi 	stub = kmem_alloc(sizeof (amdzen_stub_t), KM_SLEEP);
1227047043c2SRobert Mustacchi 	if (pci_config_setup(dip, &stub->azns_cfgspace) != DDI_SUCCESS) {
1228047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "failed to set up config space");
1229047043c2SRobert Mustacchi 		kmem_free(stub, sizeof (amdzen_stub_t));
1230047043c2SRobert Mustacchi 		return (DDI_FAILURE);
1231047043c2SRobert Mustacchi 	}
1232047043c2SRobert Mustacchi 
1233047043c2SRobert Mustacchi 	stub->azns_dip = dip;
1234047043c2SRobert Mustacchi 	stub->azns_vid = vid;
1235047043c2SRobert Mustacchi 	stub->azns_did = did;
1236047043c2SRobert Mustacchi 	stub->azns_bus = PCI_REG_BUS_G(reg);
1237047043c2SRobert Mustacchi 	stub->azns_dev = PCI_REG_DEV_G(reg);
1238047043c2SRobert Mustacchi 	stub->azns_func = PCI_REG_FUNC_G(reg);
1239047043c2SRobert Mustacchi 	ddi_set_driver_private(dip, stub);
1240047043c2SRobert Mustacchi 
1241047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
1242047043c2SRobert Mustacchi 	azn->azn_npresent++;
1243047043c2SRobert Mustacchi 	if (nb) {
1244047043c2SRobert Mustacchi 		list_insert_tail(&azn->azn_nb_stubs, stub);
1245047043c2SRobert Mustacchi 	} else {
1246047043c2SRobert Mustacchi 		list_insert_tail(&azn->azn_df_stubs, stub);
1247047043c2SRobert Mustacchi 	}
1248047043c2SRobert Mustacchi 
1249047043c2SRobert Mustacchi 	if ((azn->azn_flags & AMDZEN_F_TASKQ_MASK) == AMDZEN_F_SCAN_COMPLETE &&
1250047043c2SRobert Mustacchi 	    azn->azn_nscanned == azn->azn_npresent) {
1251047043c2SRobert Mustacchi 		azn->azn_flags |= AMDZEN_F_ATTACH_DISPATCHED;
1252047043c2SRobert Mustacchi 		azn->azn_taskqid = taskq_dispatch(system_taskq,
1253047043c2SRobert Mustacchi 		    amdzen_nexus_init, azn, TQ_SLEEP);
1254047043c2SRobert Mustacchi 	}
1255047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
1256047043c2SRobert Mustacchi 
1257047043c2SRobert Mustacchi 	return (DDI_SUCCESS);
1258047043c2SRobert Mustacchi }
1259047043c2SRobert Mustacchi 
1260047043c2SRobert Mustacchi static int
1261047043c2SRobert Mustacchi amdzen_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
1262047043c2SRobert Mustacchi     void *arg, void *result)
1263047043c2SRobert Mustacchi {
1264047043c2SRobert Mustacchi 	char buf[32];
1265047043c2SRobert Mustacchi 	dev_info_t *child;
1266047043c2SRobert Mustacchi 	const amdzen_child_data_t *acd;
1267047043c2SRobert Mustacchi 
1268047043c2SRobert Mustacchi 	switch (ctlop) {
1269047043c2SRobert Mustacchi 	case DDI_CTLOPS_REPORTDEV:
1270047043c2SRobert Mustacchi 		if (rdip == NULL) {
1271047043c2SRobert Mustacchi 			return (DDI_FAILURE);
1272047043c2SRobert Mustacchi 		}
1273047043c2SRobert Mustacchi 		cmn_err(CE_CONT, "amdzen nexus: %s@%s, %s%d\n",
1274047043c2SRobert Mustacchi 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
1275047043c2SRobert Mustacchi 		    ddi_driver_name(rdip), ddi_get_instance(rdip));
1276047043c2SRobert Mustacchi 		break;
1277047043c2SRobert Mustacchi 	case DDI_CTLOPS_INITCHILD:
1278047043c2SRobert Mustacchi 		child = arg;
1279047043c2SRobert Mustacchi 		if (child == NULL) {
1280047043c2SRobert Mustacchi 			dev_err(dip, CE_WARN, "!no child passed for "
1281047043c2SRobert Mustacchi 			    "DDI_CTLOPS_INITCHILD");
1282047043c2SRobert Mustacchi 		}
1283047043c2SRobert Mustacchi 
1284047043c2SRobert Mustacchi 		acd = ddi_get_parent_data(child);
1285047043c2SRobert Mustacchi 		if (acd == NULL) {
1286047043c2SRobert Mustacchi 			dev_err(dip, CE_WARN, "!missing child parent data");
1287047043c2SRobert Mustacchi 			return (DDI_FAILURE);
1288047043c2SRobert Mustacchi 		}
1289047043c2SRobert Mustacchi 
1290047043c2SRobert Mustacchi 		if (snprintf(buf, sizeof (buf), "%d", acd->acd_addr) >=
1291047043c2SRobert Mustacchi 		    sizeof (buf)) {
1292047043c2SRobert Mustacchi 			dev_err(dip, CE_WARN, "!failed to construct device "
1293047043c2SRobert Mustacchi 			    "addr due to overflow");
1294047043c2SRobert Mustacchi 			return (DDI_FAILURE);
1295047043c2SRobert Mustacchi 		}
1296047043c2SRobert Mustacchi 
1297047043c2SRobert Mustacchi 		ddi_set_name_addr(child, buf);
1298047043c2SRobert Mustacchi 		break;
1299047043c2SRobert Mustacchi 	case DDI_CTLOPS_UNINITCHILD:
1300047043c2SRobert Mustacchi 		child = arg;
1301047043c2SRobert Mustacchi 		if (child == NULL) {
1302047043c2SRobert Mustacchi 			dev_err(dip, CE_WARN, "!no child passed for "
1303047043c2SRobert Mustacchi 			    "DDI_CTLOPS_UNINITCHILD");
1304047043c2SRobert Mustacchi 		}
1305047043c2SRobert Mustacchi 
1306047043c2SRobert Mustacchi 		ddi_set_name_addr(child, NULL);
1307047043c2SRobert Mustacchi 		break;
1308047043c2SRobert Mustacchi 	default:
1309047043c2SRobert Mustacchi 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
1310047043c2SRobert Mustacchi 	}
1311047043c2SRobert Mustacchi 	return (DDI_SUCCESS);
1312047043c2SRobert Mustacchi }
1313047043c2SRobert Mustacchi 
1314047043c2SRobert Mustacchi static int
1315047043c2SRobert Mustacchi amdzen_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1316047043c2SRobert Mustacchi {
1317047043c2SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
1318047043c2SRobert Mustacchi 
1319047043c2SRobert Mustacchi 	if (cmd == DDI_RESUME) {
1320047043c2SRobert Mustacchi 		return (DDI_SUCCESS);
1321047043c2SRobert Mustacchi 	} else if (cmd != DDI_ATTACH) {
1322047043c2SRobert Mustacchi 		return (DDI_FAILURE);
1323047043c2SRobert Mustacchi 	}
1324047043c2SRobert Mustacchi 
1325047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
1326047043c2SRobert Mustacchi 	if (azn->azn_dip != NULL) {
1327047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "driver is already attached!");
1328047043c2SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
1329047043c2SRobert Mustacchi 		return (DDI_FAILURE);
1330047043c2SRobert Mustacchi 	}
1331047043c2SRobert Mustacchi 
1332047043c2SRobert Mustacchi 	azn->azn_dip = dip;
1333047043c2SRobert Mustacchi 	azn->azn_taskqid = taskq_dispatch(system_taskq, amdzen_stub_scan,
1334047043c2SRobert Mustacchi 	    azn, TQ_SLEEP);
1335047043c2SRobert Mustacchi 	azn->azn_flags |= AMDZEN_F_SCAN_DISPATCHED;
1336047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
1337047043c2SRobert Mustacchi 
1338047043c2SRobert Mustacchi 	return (DDI_SUCCESS);
1339047043c2SRobert Mustacchi }
1340047043c2SRobert Mustacchi 
1341047043c2SRobert Mustacchi static int
1342047043c2SRobert Mustacchi amdzen_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1343047043c2SRobert Mustacchi {
1344047043c2SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
1345047043c2SRobert Mustacchi 
1346047043c2SRobert Mustacchi 	if (cmd == DDI_SUSPEND) {
1347047043c2SRobert Mustacchi 		return (DDI_SUCCESS);
1348047043c2SRobert Mustacchi 	} else if (cmd != DDI_DETACH) {
1349047043c2SRobert Mustacchi 		return (DDI_FAILURE);
1350047043c2SRobert Mustacchi 	}
1351047043c2SRobert Mustacchi 
1352047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
1353047043c2SRobert Mustacchi 	while (azn->azn_taskqid != TASKQID_INVALID) {
1354047043c2SRobert Mustacchi 		cv_wait(&azn->azn_cv, &azn->azn_mutex);
1355047043c2SRobert Mustacchi 	}
1356047043c2SRobert Mustacchi 
1357047043c2SRobert Mustacchi 	/*
1358047043c2SRobert Mustacchi 	 * If we've attached any stub drivers, e.g. this platform is important
1359047043c2SRobert Mustacchi 	 * for us, then we fail detach.
1360047043c2SRobert Mustacchi 	 */
1361047043c2SRobert Mustacchi 	if (!list_is_empty(&azn->azn_df_stubs) ||
1362047043c2SRobert Mustacchi 	    !list_is_empty(&azn->azn_nb_stubs)) {
1363047043c2SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
1364047043c2SRobert Mustacchi 		return (DDI_FAILURE);
1365047043c2SRobert Mustacchi 	}
1366047043c2SRobert Mustacchi 
1367047043c2SRobert Mustacchi 	azn->azn_dip = NULL;
1368047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
1369047043c2SRobert Mustacchi 
1370047043c2SRobert Mustacchi 	return (DDI_SUCCESS);
1371047043c2SRobert Mustacchi }
1372047043c2SRobert Mustacchi 
1373047043c2SRobert Mustacchi static void
1374047043c2SRobert Mustacchi amdzen_free(void)
1375047043c2SRobert Mustacchi {
1376047043c2SRobert Mustacchi 	if (amdzen_data == NULL) {
1377047043c2SRobert Mustacchi 		return;
1378047043c2SRobert Mustacchi 	}
1379047043c2SRobert Mustacchi 
1380047043c2SRobert Mustacchi 	VERIFY(list_is_empty(&amdzen_data->azn_df_stubs));
1381047043c2SRobert Mustacchi 	list_destroy(&amdzen_data->azn_df_stubs);
1382047043c2SRobert Mustacchi 	VERIFY(list_is_empty(&amdzen_data->azn_nb_stubs));
1383047043c2SRobert Mustacchi 	list_destroy(&amdzen_data->azn_nb_stubs);
1384047043c2SRobert Mustacchi 	cv_destroy(&amdzen_data->azn_cv);
1385047043c2SRobert Mustacchi 	mutex_destroy(&amdzen_data->azn_mutex);
1386047043c2SRobert Mustacchi 	kmem_free(amdzen_data, sizeof (amdzen_t));
1387047043c2SRobert Mustacchi 	amdzen_data = NULL;
1388047043c2SRobert Mustacchi }
1389047043c2SRobert Mustacchi 
1390047043c2SRobert Mustacchi static void
1391047043c2SRobert Mustacchi amdzen_alloc(void)
1392047043c2SRobert Mustacchi {
1393047043c2SRobert Mustacchi 	amdzen_data = kmem_zalloc(sizeof (amdzen_t), KM_SLEEP);
1394047043c2SRobert Mustacchi 	mutex_init(&amdzen_data->azn_mutex, NULL, MUTEX_DRIVER, NULL);
1395047043c2SRobert Mustacchi 	list_create(&amdzen_data->azn_df_stubs, sizeof (amdzen_stub_t),
1396047043c2SRobert Mustacchi 	    offsetof(amdzen_stub_t, azns_link));
1397047043c2SRobert Mustacchi 	list_create(&amdzen_data->azn_nb_stubs, sizeof (amdzen_stub_t),
1398047043c2SRobert Mustacchi 	    offsetof(amdzen_stub_t, azns_link));
1399047043c2SRobert Mustacchi 	cv_init(&amdzen_data->azn_cv, NULL, CV_DRIVER, NULL);
1400047043c2SRobert Mustacchi }
1401047043c2SRobert Mustacchi 
1402047043c2SRobert Mustacchi struct bus_ops amdzen_bus_ops = {
1403047043c2SRobert Mustacchi 	.busops_rev = BUSO_REV,
1404047043c2SRobert Mustacchi 	.bus_map = nullbusmap,
1405047043c2SRobert Mustacchi 	.bus_dma_map = ddi_no_dma_map,
1406047043c2SRobert Mustacchi 	.bus_dma_allochdl = ddi_no_dma_allochdl,
1407047043c2SRobert Mustacchi 	.bus_dma_freehdl = ddi_no_dma_freehdl,
1408047043c2SRobert Mustacchi 	.bus_dma_bindhdl = ddi_no_dma_bindhdl,
1409047043c2SRobert Mustacchi 	.bus_dma_unbindhdl = ddi_no_dma_unbindhdl,
1410047043c2SRobert Mustacchi 	.bus_dma_flush = ddi_no_dma_flush,
1411047043c2SRobert Mustacchi 	.bus_dma_win = ddi_no_dma_win,
1412047043c2SRobert Mustacchi 	.bus_dma_ctl = ddi_no_dma_mctl,
1413047043c2SRobert Mustacchi 	.bus_prop_op = ddi_bus_prop_op,
1414047043c2SRobert Mustacchi 	.bus_ctl = amdzen_bus_ctl
1415047043c2SRobert Mustacchi };
1416047043c2SRobert Mustacchi 
1417047043c2SRobert Mustacchi static struct dev_ops amdzen_dev_ops = {
1418047043c2SRobert Mustacchi 	.devo_rev = DEVO_REV,
1419047043c2SRobert Mustacchi 	.devo_refcnt = 0,
1420047043c2SRobert Mustacchi 	.devo_getinfo = nodev,
1421047043c2SRobert Mustacchi 	.devo_identify = nulldev,
1422047043c2SRobert Mustacchi 	.devo_probe = nulldev,
1423047043c2SRobert Mustacchi 	.devo_attach = amdzen_attach,
1424047043c2SRobert Mustacchi 	.devo_detach = amdzen_detach,
1425047043c2SRobert Mustacchi 	.devo_reset = nodev,
1426047043c2SRobert Mustacchi 	.devo_quiesce = ddi_quiesce_not_needed,
1427047043c2SRobert Mustacchi 	.devo_bus_ops = &amdzen_bus_ops
1428047043c2SRobert Mustacchi };
1429047043c2SRobert Mustacchi 
1430047043c2SRobert Mustacchi static struct modldrv amdzen_modldrv = {
1431047043c2SRobert Mustacchi 	.drv_modops = &mod_driverops,
1432047043c2SRobert Mustacchi 	.drv_linkinfo = "AMD Zen Nexus Driver",
1433047043c2SRobert Mustacchi 	.drv_dev_ops = &amdzen_dev_ops
1434047043c2SRobert Mustacchi };
1435047043c2SRobert Mustacchi 
1436047043c2SRobert Mustacchi static struct modlinkage amdzen_modlinkage = {
1437047043c2SRobert Mustacchi 	.ml_rev = MODREV_1,
1438047043c2SRobert Mustacchi 	.ml_linkage = { &amdzen_modldrv, NULL }
1439047043c2SRobert Mustacchi };
1440047043c2SRobert Mustacchi 
1441047043c2SRobert Mustacchi int
1442047043c2SRobert Mustacchi _init(void)
1443047043c2SRobert Mustacchi {
1444047043c2SRobert Mustacchi 	int ret;
1445047043c2SRobert Mustacchi 
14469b0429a1SPu Wen 	if (cpuid_getvendor(CPU) != X86_VENDOR_AMD &&
14479b0429a1SPu Wen 	    cpuid_getvendor(CPU) != X86_VENDOR_HYGON) {
1448047043c2SRobert Mustacchi 		return (ENOTSUP);
1449047043c2SRobert Mustacchi 	}
1450047043c2SRobert Mustacchi 
1451047043c2SRobert Mustacchi 	if ((ret = mod_install(&amdzen_modlinkage)) == 0) {
1452047043c2SRobert Mustacchi 		amdzen_alloc();
1453047043c2SRobert Mustacchi 	}
1454047043c2SRobert Mustacchi 
1455047043c2SRobert Mustacchi 	return (ret);
1456047043c2SRobert Mustacchi }
1457047043c2SRobert Mustacchi 
1458047043c2SRobert Mustacchi int
1459047043c2SRobert Mustacchi _info(struct modinfo *modinfop)
1460047043c2SRobert Mustacchi {
1461047043c2SRobert Mustacchi 	return (mod_info(&amdzen_modlinkage, modinfop));
1462047043c2SRobert Mustacchi }
1463047043c2SRobert Mustacchi 
1464047043c2SRobert Mustacchi int
1465047043c2SRobert Mustacchi _fini(void)
1466047043c2SRobert Mustacchi {
1467047043c2SRobert Mustacchi 	int ret;
1468047043c2SRobert Mustacchi 
1469047043c2SRobert Mustacchi 	if ((ret = mod_remove(&amdzen_modlinkage)) == 0) {
1470047043c2SRobert Mustacchi 		amdzen_free();
1471047043c2SRobert Mustacchi 	}
1472047043c2SRobert Mustacchi 
1473047043c2SRobert Mustacchi 	return (ret);
1474047043c2SRobert Mustacchi }
1475