xref: /illumos-gate/usr/src/uts/intel/io/amdzen/amdzen.c (revision 9b0429a1)
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.
14047043c2SRobert Mustacchi  * Copyright 2020 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.
122047043c2SRobert Mustacchi  */
123047043c2SRobert Mustacchi 
124047043c2SRobert Mustacchi #include <sys/modctl.h>
125047043c2SRobert Mustacchi #include <sys/conf.h>
126047043c2SRobert Mustacchi #include <sys/devops.h>
127047043c2SRobert Mustacchi #include <sys/ddi.h>
128047043c2SRobert Mustacchi #include <sys/sunddi.h>
129047043c2SRobert Mustacchi #include <sys/pci.h>
130047043c2SRobert Mustacchi #include <sys/sysmacros.h>
131047043c2SRobert Mustacchi #include <sys/sunndi.h>
132047043c2SRobert Mustacchi #include <sys/x86_archext.h>
133047043c2SRobert Mustacchi #include <sys/cpuvar.h>
134047043c2SRobert Mustacchi 
135047043c2SRobert Mustacchi #include "amdzen.h"
136047043c2SRobert Mustacchi 
137047043c2SRobert Mustacchi amdzen_t *amdzen_data;
138047043c2SRobert Mustacchi 
139047043c2SRobert Mustacchi /*
140047043c2SRobert Mustacchi  * Array of northbridge IDs that we care about.
141047043c2SRobert Mustacchi  */
142047043c2SRobert Mustacchi static const uint16_t amdzen_nb_ids[] = {
143047043c2SRobert Mustacchi 	/* Family 17h Ryzen, Epyc Models 00h-0fh (Zen uarch) */
144047043c2SRobert Mustacchi 	0x1450,
145becd642cSRobert Mustacchi 	/* Family 17h Raven Ridge, Kestrel, Dali Models 10h-2fh (Zen uarch) */
146047043c2SRobert Mustacchi 	0x15d0,
147e9abe9d6SRobert Mustacchi 	/* Family 17h/19h Rome, Milan, Matisse, Vermeer Zen 2/Zen 3 uarch */
148becd642cSRobert Mustacchi 	0x1480,
149becd642cSRobert Mustacchi 	/* Family 17h Renoir Models 60-6fh (Zen 2 uarch) */
150becd642cSRobert Mustacchi 	0x1630
151047043c2SRobert Mustacchi };
152047043c2SRobert Mustacchi 
153047043c2SRobert Mustacchi typedef struct {
154047043c2SRobert Mustacchi 	char *acd_name;
155047043c2SRobert Mustacchi 	amdzen_child_t acd_addr;
156047043c2SRobert Mustacchi } amdzen_child_data_t;
157047043c2SRobert Mustacchi 
158047043c2SRobert Mustacchi static const amdzen_child_data_t amdzen_children[] = {
159047043c2SRobert Mustacchi 	{ "smntemp", AMDZEN_C_SMNTEMP },
160549e0fd3SRobert Mustacchi 	{ "usmn", AMDZEN_C_USMN },
161549e0fd3SRobert Mustacchi 	{ "zen_udf", AMDZEN_C_ZEN_UDF }
162047043c2SRobert Mustacchi };
163047043c2SRobert Mustacchi 
164047043c2SRobert Mustacchi static uint32_t
165047043c2SRobert Mustacchi amdzen_stub_get32(amdzen_stub_t *stub, off_t reg)
166047043c2SRobert Mustacchi {
167047043c2SRobert Mustacchi 	return (pci_config_get32(stub->azns_cfgspace, reg));
168047043c2SRobert Mustacchi }
169047043c2SRobert Mustacchi 
170549e0fd3SRobert Mustacchi static uint64_t
171549e0fd3SRobert Mustacchi amdzen_stub_get64(amdzen_stub_t *stub, off_t reg)
172549e0fd3SRobert Mustacchi {
173549e0fd3SRobert Mustacchi 	return (pci_config_get64(stub->azns_cfgspace, reg));
174549e0fd3SRobert Mustacchi }
175549e0fd3SRobert Mustacchi 
176047043c2SRobert Mustacchi static void
177047043c2SRobert Mustacchi amdzen_stub_put32(amdzen_stub_t *stub, off_t reg, uint32_t val)
178047043c2SRobert Mustacchi {
179047043c2SRobert Mustacchi 	pci_config_put32(stub->azns_cfgspace, reg, val);
180047043c2SRobert Mustacchi }
181047043c2SRobert Mustacchi 
182047043c2SRobert Mustacchi /*
183047043c2SRobert Mustacchi  * Perform a targeted 32-bit indirect read to a specific instance and function.
184047043c2SRobert Mustacchi  */
185047043c2SRobert Mustacchi static uint32_t
186047043c2SRobert Mustacchi amdzen_df_read32(amdzen_t *azn, amdzen_df_t *df, uint8_t inst, uint8_t func,
187047043c2SRobert Mustacchi     uint16_t reg)
188047043c2SRobert Mustacchi {
189047043c2SRobert Mustacchi 	uint32_t val;
190047043c2SRobert Mustacchi 
191047043c2SRobert Mustacchi 	VERIFY(MUTEX_HELD(&azn->azn_mutex));
192047043c2SRobert Mustacchi 	val = AMDZEN_DF_F4_FICAA_TARG_INST | AMDZEN_DF_F4_FICAA_SET_REG(reg) |
193047043c2SRobert Mustacchi 	    AMDZEN_DF_F4_FICAA_SET_FUNC(func) |
194047043c2SRobert Mustacchi 	    AMDZEN_DF_F4_FICAA_SET_INST(inst);
195047043c2SRobert Mustacchi 	amdzen_stub_put32(df->adf_funcs[4], AMDZEN_DF_F4_FICAA, val);
196047043c2SRobert Mustacchi 	return (amdzen_stub_get32(df->adf_funcs[4], AMDZEN_DF_F4_FICAD_LO));
197047043c2SRobert Mustacchi }
198047043c2SRobert Mustacchi 
199549e0fd3SRobert Mustacchi /*
200549e0fd3SRobert Mustacchi  * Perform a targeted 64-bit indirect read to a specific instance and function.
201549e0fd3SRobert Mustacchi  */
202549e0fd3SRobert Mustacchi static uint64_t
203549e0fd3SRobert Mustacchi amdzen_df_read64(amdzen_t *azn, amdzen_df_t *df, uint8_t inst, uint8_t func,
204549e0fd3SRobert Mustacchi     uint16_t reg)
205549e0fd3SRobert Mustacchi {
206549e0fd3SRobert Mustacchi 	uint32_t val;
207549e0fd3SRobert Mustacchi 
208549e0fd3SRobert Mustacchi 	VERIFY(MUTEX_HELD(&azn->azn_mutex));
209549e0fd3SRobert Mustacchi 	val = AMDZEN_DF_F4_FICAA_TARG_INST | AMDZEN_DF_F4_FICAA_SET_REG(reg) |
210549e0fd3SRobert Mustacchi 	    AMDZEN_DF_F4_FICAA_SET_FUNC(func) |
211549e0fd3SRobert Mustacchi 	    AMDZEN_DF_F4_FICAA_SET_INST(inst) | AMDZEN_DF_F4_FICAA_SET_64B;
212549e0fd3SRobert Mustacchi 	amdzen_stub_put32(df->adf_funcs[4], AMDZEN_DF_F4_FICAA, val);
213549e0fd3SRobert Mustacchi 	return (amdzen_stub_get64(df->adf_funcs[4], AMDZEN_DF_F4_FICAD_LO));
214549e0fd3SRobert Mustacchi }
215549e0fd3SRobert Mustacchi 
216549e0fd3SRobert Mustacchi 
217047043c2SRobert Mustacchi static uint32_t
218047043c2SRobert Mustacchi amdzen_smn_read32(amdzen_t *azn, amdzen_df_t *df, uint32_t reg)
219047043c2SRobert Mustacchi {
220047043c2SRobert Mustacchi 	VERIFY(MUTEX_HELD(&azn->azn_mutex));
221047043c2SRobert Mustacchi 	amdzen_stub_put32(df->adf_nb, AMDZEN_NB_SMN_ADDR, reg);
222047043c2SRobert Mustacchi 	return (amdzen_stub_get32(df->adf_nb, AMDZEN_NB_SMN_DATA));
223047043c2SRobert Mustacchi }
224047043c2SRobert Mustacchi 
225047043c2SRobert Mustacchi static amdzen_df_t *
226047043c2SRobert Mustacchi amdzen_df_find(amdzen_t *azn, uint_t dfno)
227047043c2SRobert Mustacchi {
228047043c2SRobert Mustacchi 	uint_t i;
229047043c2SRobert Mustacchi 
230047043c2SRobert Mustacchi 	ASSERT(MUTEX_HELD(&azn->azn_mutex));
231047043c2SRobert Mustacchi 	if (dfno >= azn->azn_ndfs) {
232047043c2SRobert Mustacchi 		return (NULL);
233047043c2SRobert Mustacchi 	}
234047043c2SRobert Mustacchi 
235047043c2SRobert Mustacchi 	for (i = 0; i < azn->azn_ndfs; i++) {
236047043c2SRobert Mustacchi 		amdzen_df_t *df = &azn->azn_dfs[i];
237047043c2SRobert Mustacchi 		if ((df->adf_flags & AMDZEN_DF_F_VALID) == 0) {
238047043c2SRobert Mustacchi 			continue;
239047043c2SRobert Mustacchi 		}
240047043c2SRobert Mustacchi 
241047043c2SRobert Mustacchi 		if (dfno == 0) {
242047043c2SRobert Mustacchi 			return (df);
243047043c2SRobert Mustacchi 		}
244047043c2SRobert Mustacchi 		dfno--;
245047043c2SRobert Mustacchi 	}
246047043c2SRobert Mustacchi 
247047043c2SRobert Mustacchi 	return (NULL);
248047043c2SRobert Mustacchi }
249047043c2SRobert Mustacchi 
250047043c2SRobert Mustacchi /*
251047043c2SRobert Mustacchi  * Client functions that are used by nexus children.
252047043c2SRobert Mustacchi  */
253047043c2SRobert Mustacchi int
254047043c2SRobert Mustacchi amdzen_c_smn_read32(uint_t dfno, uint32_t reg, uint32_t *valp)
255047043c2SRobert Mustacchi {
256047043c2SRobert Mustacchi 	amdzen_df_t *df;
257047043c2SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
258047043c2SRobert Mustacchi 
259047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
260047043c2SRobert Mustacchi 	df = amdzen_df_find(azn, dfno);
261047043c2SRobert Mustacchi 	if (df == NULL) {
262047043c2SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
263047043c2SRobert Mustacchi 		return (ENOENT);
264047043c2SRobert Mustacchi 	}
265047043c2SRobert Mustacchi 
266047043c2SRobert Mustacchi 	if ((df->adf_flags & AMDZEN_DF_F_FOUND_NB) == 0) {
267047043c2SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
268047043c2SRobert Mustacchi 		return (ENXIO);
269047043c2SRobert Mustacchi 	}
270047043c2SRobert Mustacchi 
271047043c2SRobert Mustacchi 	*valp = amdzen_smn_read32(azn, df, reg);
272047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
273047043c2SRobert Mustacchi 	return (0);
274047043c2SRobert Mustacchi }
275047043c2SRobert Mustacchi 
276047043c2SRobert Mustacchi uint_t
277047043c2SRobert Mustacchi amdzen_c_df_count(void)
278047043c2SRobert Mustacchi {
279047043c2SRobert Mustacchi 	uint_t ret;
280047043c2SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
281047043c2SRobert Mustacchi 
282047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
283047043c2SRobert Mustacchi 	ret = azn->azn_ndfs;
284047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
285047043c2SRobert Mustacchi 	return (ret);
286047043c2SRobert Mustacchi }
287047043c2SRobert Mustacchi 
288549e0fd3SRobert Mustacchi int
289549e0fd3SRobert Mustacchi amdzen_c_df_read32(uint_t dfno, uint8_t inst, uint8_t func,
290549e0fd3SRobert Mustacchi     uint16_t reg, uint32_t *valp)
291549e0fd3SRobert Mustacchi {
292549e0fd3SRobert Mustacchi 	amdzen_df_t *df;
293549e0fd3SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
294549e0fd3SRobert Mustacchi 
295549e0fd3SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
296549e0fd3SRobert Mustacchi 	df = amdzen_df_find(azn, dfno);
297549e0fd3SRobert Mustacchi 	if (df == NULL) {
298549e0fd3SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
299549e0fd3SRobert Mustacchi 		return (ENOENT);
300549e0fd3SRobert Mustacchi 	}
301549e0fd3SRobert Mustacchi 
302549e0fd3SRobert Mustacchi 	*valp = amdzen_df_read32(azn, df, inst, func, reg);
303549e0fd3SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
304549e0fd3SRobert Mustacchi 
305549e0fd3SRobert Mustacchi 	return (0);
306549e0fd3SRobert Mustacchi }
307549e0fd3SRobert Mustacchi 
308549e0fd3SRobert Mustacchi int
309549e0fd3SRobert Mustacchi amdzen_c_df_read64(uint_t dfno, uint8_t inst, uint8_t func,
310549e0fd3SRobert Mustacchi     uint16_t reg, uint64_t *valp)
311549e0fd3SRobert Mustacchi {
312549e0fd3SRobert Mustacchi 	amdzen_df_t *df;
313549e0fd3SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
314549e0fd3SRobert Mustacchi 
315549e0fd3SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
316549e0fd3SRobert Mustacchi 	df = amdzen_df_find(azn, dfno);
317549e0fd3SRobert Mustacchi 	if (df == NULL) {
318549e0fd3SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
319549e0fd3SRobert Mustacchi 		return (ENOENT);
320549e0fd3SRobert Mustacchi 	}
321549e0fd3SRobert Mustacchi 
322549e0fd3SRobert Mustacchi 	*valp = amdzen_df_read64(azn, df, inst, func, reg);
323549e0fd3SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
324549e0fd3SRobert Mustacchi 
325549e0fd3SRobert Mustacchi 	return (0);
326549e0fd3SRobert Mustacchi }
327047043c2SRobert Mustacchi 
328047043c2SRobert Mustacchi static boolean_t
329047043c2SRobert Mustacchi amdzen_create_child(amdzen_t *azn, const amdzen_child_data_t *acd)
330047043c2SRobert Mustacchi {
331047043c2SRobert Mustacchi 	int ret;
332047043c2SRobert Mustacchi 	dev_info_t *child;
333047043c2SRobert Mustacchi 
334047043c2SRobert Mustacchi 	if (ndi_devi_alloc(azn->azn_dip, acd->acd_name,
335047043c2SRobert Mustacchi 	    (pnode_t)DEVI_SID_NODEID, &child) != NDI_SUCCESS) {
336549e0fd3SRobert Mustacchi 		dev_err(azn->azn_dip, CE_WARN, "!failed to allocate child "
337047043c2SRobert Mustacchi 		    "dip for %s", acd->acd_name);
338047043c2SRobert Mustacchi 		return (B_FALSE);
339047043c2SRobert Mustacchi 	}
340047043c2SRobert Mustacchi 
341047043c2SRobert Mustacchi 	ddi_set_parent_data(child, (void *)acd);
342047043c2SRobert Mustacchi 	if ((ret = ndi_devi_online(child, 0)) != NDI_SUCCESS) {
343549e0fd3SRobert Mustacchi 		dev_err(azn->azn_dip, CE_WARN, "!failed to online child "
344047043c2SRobert Mustacchi 		    "dip %s: %d", acd->acd_name, ret);
345047043c2SRobert Mustacchi 		return (B_FALSE);
346047043c2SRobert Mustacchi 	}
347047043c2SRobert Mustacchi 
348047043c2SRobert Mustacchi 	return (B_TRUE);
349047043c2SRobert Mustacchi }
350047043c2SRobert Mustacchi 
351047043c2SRobert Mustacchi static boolean_t
352047043c2SRobert Mustacchi amdzen_map_dfs(amdzen_t *azn)
353047043c2SRobert Mustacchi {
354047043c2SRobert Mustacchi 	amdzen_stub_t *stub;
355047043c2SRobert Mustacchi 
356047043c2SRobert Mustacchi 	ASSERT(MUTEX_HELD(&azn->azn_mutex));
357047043c2SRobert Mustacchi 
358047043c2SRobert Mustacchi 	for (stub = list_head(&azn->azn_df_stubs); stub != NULL;
359047043c2SRobert Mustacchi 	    stub = list_next(&azn->azn_df_stubs, stub)) {
360047043c2SRobert Mustacchi 		amdzen_df_t *df;
361047043c2SRobert Mustacchi 		uint_t dfno;
362047043c2SRobert Mustacchi 
363047043c2SRobert Mustacchi 		dfno = stub->azns_dev - AMDZEN_DF_FIRST_DEVICE;
364047043c2SRobert Mustacchi 		if (dfno > AMDZEN_MAX_DFS) {
365047043c2SRobert Mustacchi 			dev_err(stub->azns_dip, CE_WARN, "encountered df "
366047043c2SRobert Mustacchi 			    "device with illegal DF PCI b/d/f: 0x%x/%x/%x",
367047043c2SRobert Mustacchi 			    stub->azns_bus, stub->azns_dev, stub->azns_func);
368047043c2SRobert Mustacchi 			goto err;
369047043c2SRobert Mustacchi 		}
370047043c2SRobert Mustacchi 
371047043c2SRobert Mustacchi 		df = &azn->azn_dfs[dfno];
372047043c2SRobert Mustacchi 
373047043c2SRobert Mustacchi 		if (stub->azns_func >= AMDZEN_MAX_DF_FUNCS) {
374047043c2SRobert Mustacchi 			dev_err(stub->azns_dip, CE_WARN, "encountered df "
375047043c2SRobert Mustacchi 			    "device with illegal DF PCI b/d/f: 0x%x/%x/%x",
376047043c2SRobert Mustacchi 			    stub->azns_bus, stub->azns_dev, stub->azns_func);
377047043c2SRobert Mustacchi 			goto err;
378047043c2SRobert Mustacchi 		}
379047043c2SRobert Mustacchi 
380047043c2SRobert Mustacchi 		if (df->adf_funcs[stub->azns_func] != NULL) {
381047043c2SRobert Mustacchi 			dev_err(stub->azns_dip, CE_WARN, "encountered "
382047043c2SRobert Mustacchi 			    "duplicate df device with DF PCI b/d/f: 0x%x/%x/%x",
383047043c2SRobert Mustacchi 			    stub->azns_bus, stub->azns_dev, stub->azns_func);
384047043c2SRobert Mustacchi 			goto err;
385047043c2SRobert Mustacchi 		}
386047043c2SRobert Mustacchi 		df->adf_funcs[stub->azns_func] = stub;
387047043c2SRobert Mustacchi 	}
388047043c2SRobert Mustacchi 
389047043c2SRobert Mustacchi 	return (B_TRUE);
390047043c2SRobert Mustacchi 
391047043c2SRobert Mustacchi err:
392047043c2SRobert Mustacchi 	azn->azn_flags |= AMDZEN_F_DEVICE_ERROR;
393047043c2SRobert Mustacchi 	return (B_FALSE);
394047043c2SRobert Mustacchi }
395047043c2SRobert Mustacchi 
396047043c2SRobert Mustacchi static boolean_t
397047043c2SRobert Mustacchi amdzen_check_dfs(amdzen_t *azn)
398047043c2SRobert Mustacchi {
399047043c2SRobert Mustacchi 	uint_t i;
400047043c2SRobert Mustacchi 	boolean_t ret = B_TRUE;
401047043c2SRobert Mustacchi 
402047043c2SRobert Mustacchi 	for (i = 0; i < AMDZEN_MAX_DFS; i++) {
403047043c2SRobert Mustacchi 		amdzen_df_t *df = &azn->azn_dfs[i];
404047043c2SRobert Mustacchi 		uint_t count = 0;
405047043c2SRobert Mustacchi 
406047043c2SRobert Mustacchi 		/*
407047043c2SRobert Mustacchi 		 * We require all platforms to have DFs functions 0-6. Not all
408047043c2SRobert Mustacchi 		 * platforms have DF function 7.
409047043c2SRobert Mustacchi 		 */
410047043c2SRobert Mustacchi 		for (uint_t func = 0; func < AMDZEN_MAX_DF_FUNCS - 1; func++) {
411047043c2SRobert Mustacchi 			if (df->adf_funcs[func] != NULL) {
412047043c2SRobert Mustacchi 				count++;
413047043c2SRobert Mustacchi 			}
414047043c2SRobert Mustacchi 		}
415047043c2SRobert Mustacchi 
416047043c2SRobert Mustacchi 		if (count == 0)
417047043c2SRobert Mustacchi 			continue;
418047043c2SRobert Mustacchi 
419047043c2SRobert Mustacchi 		if (count != 7) {
420047043c2SRobert Mustacchi 			ret = B_FALSE;
421047043c2SRobert Mustacchi 			dev_err(azn->azn_dip, CE_WARN, "df %u devices "
422047043c2SRobert Mustacchi 			    "incomplete", i);
423047043c2SRobert Mustacchi 		} else {
424047043c2SRobert Mustacchi 			df->adf_flags |= AMDZEN_DF_F_VALID;
425047043c2SRobert Mustacchi 			azn->azn_ndfs++;
426047043c2SRobert Mustacchi 		}
427047043c2SRobert Mustacchi 	}
428047043c2SRobert Mustacchi 
429047043c2SRobert Mustacchi 	return (ret);
430047043c2SRobert Mustacchi }
431047043c2SRobert Mustacchi 
432047043c2SRobert Mustacchi static const uint8_t amdzen_df_rome_ids[0x2b] = {
433047043c2SRobert Mustacchi 	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20, 21, 22, 23,
434047043c2SRobert Mustacchi 	24, 25, 26, 27, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
435047043c2SRobert Mustacchi 	44, 45, 46, 47, 48
436047043c2SRobert Mustacchi };
437047043c2SRobert Mustacchi 
438047043c2SRobert Mustacchi /*
439e9abe9d6SRobert Mustacchi  * Check the first df entry to see if it belongs to Rome or Milan. If so, then
440e9abe9d6SRobert Mustacchi  * it uses the disjoint ID space.
441e9abe9d6SRobert Mustacchi  */
442e9abe9d6SRobert Mustacchi static boolean_t
443e9abe9d6SRobert Mustacchi amdzen_is_rome_style(uint_t id)
444e9abe9d6SRobert Mustacchi {
445e9abe9d6SRobert Mustacchi 	return (id == 0x1490 || id == 0x1650);
446e9abe9d6SRobert Mustacchi }
447e9abe9d6SRobert Mustacchi 
448e9abe9d6SRobert Mustacchi /*
449047043c2SRobert Mustacchi  * Initialize our knowledge about a given series of nodes on the data fabric.
450047043c2SRobert Mustacchi  */
451047043c2SRobert Mustacchi static void
452047043c2SRobert Mustacchi amdzen_setup_df(amdzen_t *azn, amdzen_df_t *df)
453047043c2SRobert Mustacchi {
454047043c2SRobert Mustacchi 	uint_t i;
455047043c2SRobert Mustacchi 	uint32_t val;
456047043c2SRobert Mustacchi 
457047043c2SRobert Mustacchi 	val = amdzen_stub_get32(df->adf_funcs[0], AMDZEN_DF_F0_CFG_ADDR_CTL);
458047043c2SRobert Mustacchi 	df->adf_nb_busno = AMDZEN_DF_F0_CFG_ADDR_CTL_BUS_NUM(val);
459047043c2SRobert Mustacchi 	val = amdzen_stub_get32(df->adf_funcs[0], AMDZEN_DF_F0_FBICNT);
460047043c2SRobert Mustacchi 	df->adf_nents = AMDZEN_DF_F0_FBICNT_COUNT(val);
461047043c2SRobert Mustacchi 	if (df->adf_nents == 0)
462047043c2SRobert Mustacchi 		return;
463047043c2SRobert Mustacchi 	df->adf_ents = kmem_zalloc(sizeof (amdzen_df_ent_t) * df->adf_nents,
464047043c2SRobert Mustacchi 	    KM_SLEEP);
465047043c2SRobert Mustacchi 
466047043c2SRobert Mustacchi 	for (i = 0; i < df->adf_nents; i++) {
467047043c2SRobert Mustacchi 		amdzen_df_ent_t *dfe = &df->adf_ents[i];
468047043c2SRobert Mustacchi 		uint8_t inst = i;
469047043c2SRobert Mustacchi 
470047043c2SRobert Mustacchi 		/*
471047043c2SRobert Mustacchi 		 * Unfortunately, Rome uses a discontinuous instance ID pattern
472047043c2SRobert Mustacchi 		 * while everything else we can find uses a contiguous instance
473047043c2SRobert Mustacchi 		 * ID pattern.  This means that for Rome, we need to adjust the
474047043c2SRobert Mustacchi 		 * indexes that we iterate over, though the total number of
475047043c2SRobert Mustacchi 		 * entries is right.
476047043c2SRobert Mustacchi 		 */
477e9abe9d6SRobert Mustacchi 		if (amdzen_is_rome_style(df->adf_funcs[0]->azns_did)) {
478047043c2SRobert Mustacchi 			if (inst > ARRAY_SIZE(amdzen_df_rome_ids)) {
479047043c2SRobert Mustacchi 				dev_err(azn->azn_dip, CE_WARN, "Rome family "
480047043c2SRobert Mustacchi 				    "processor reported more ids than the PPR, "
481e9abe9d6SRobert Mustacchi 				    "resetting %u to instance zero", inst);
482047043c2SRobert Mustacchi 				inst = 0;
483047043c2SRobert Mustacchi 			} else {
484047043c2SRobert Mustacchi 				inst = amdzen_df_rome_ids[inst];
485047043c2SRobert Mustacchi 			}
486047043c2SRobert Mustacchi 		}
487047043c2SRobert Mustacchi 
488047043c2SRobert Mustacchi 		dfe->adfe_drvid = inst;
489047043c2SRobert Mustacchi 		dfe->adfe_info0 = amdzen_df_read32(azn, df, inst, 0,
490047043c2SRobert Mustacchi 		    AMDZEN_DF_F0_FBIINFO0);
491047043c2SRobert Mustacchi 		dfe->adfe_info1 = amdzen_df_read32(azn, df, inst, 0,
492047043c2SRobert Mustacchi 		    AMDZEN_DF_F0_FBIINFO1);
493047043c2SRobert Mustacchi 		dfe->adfe_info2 = amdzen_df_read32(azn, df, inst, 0,
494047043c2SRobert Mustacchi 		    AMDZEN_DF_F0_FBIINFO2);
495047043c2SRobert Mustacchi 		dfe->adfe_info3 = amdzen_df_read32(azn, df, inst, 0,
496047043c2SRobert Mustacchi 		    AMDZEN_DF_F0_FBIINFO3);
497047043c2SRobert Mustacchi 		dfe->adfe_syscfg = amdzen_df_read32(azn, df, inst, 1,
498047043c2SRobert Mustacchi 		    AMDZEN_DF_F1_SYSCFG);
499047043c2SRobert Mustacchi 		dfe->adfe_mask0 = amdzen_df_read32(azn, df, inst, 1,
500047043c2SRobert Mustacchi 		    AMDZEN_DF_F1_FIDMASK0);
501047043c2SRobert Mustacchi 		dfe->adfe_mask1 = amdzen_df_read32(azn, df, inst, 1,
502047043c2SRobert Mustacchi 		    AMDZEN_DF_F1_FIDMASK1);
503047043c2SRobert Mustacchi 
504047043c2SRobert Mustacchi 		dfe->adfe_type = AMDZEN_DF_F0_FBIINFO0_TYPE(dfe->adfe_info0);
505047043c2SRobert Mustacchi 		dfe->adfe_sdp_width =
506047043c2SRobert Mustacchi 		    AMDZEN_DF_F0_FBIINFO0_SDP_WIDTH(dfe->adfe_info0);
507047043c2SRobert Mustacchi 		if (AMDZEN_DF_F0_FBIINFO0_ENABLED(dfe->adfe_info0)) {
508047043c2SRobert Mustacchi 			dfe->adfe_flags |= AMDZEN_DFE_F_ENABLED;
509047043c2SRobert Mustacchi 		}
510047043c2SRobert Mustacchi 		dfe->adfe_fti_width =
511047043c2SRobert Mustacchi 		    AMDZEN_DF_F0_FBIINFO0_FTI_WIDTH(dfe->adfe_info0);
512047043c2SRobert Mustacchi 		dfe->adfe_sdp_count =
513047043c2SRobert Mustacchi 		    AMDZEN_DF_F0_FBIINFO0_SDP_PCOUNT(dfe->adfe_info0);
514047043c2SRobert Mustacchi 		dfe->adfe_fti_count =
515047043c2SRobert Mustacchi 		    AMDZEN_DF_F0_FBIINFO0_FTI_PCOUNT(dfe->adfe_info0);
516047043c2SRobert Mustacchi 		if (AMDZEN_DF_F0_FBIINFO0_HAS_MCA(dfe->adfe_info0)) {
517047043c2SRobert Mustacchi 			dfe->adfe_flags |= AMDZEN_DFE_F_MCA;
518047043c2SRobert Mustacchi 		}
519047043c2SRobert Mustacchi 		dfe->adfe_subtype =
520047043c2SRobert Mustacchi 		    AMDZEN_DF_F0_FBIINFO0_SUBTYPE(dfe->adfe_info0);
521047043c2SRobert Mustacchi 
522047043c2SRobert Mustacchi 		dfe->adfe_inst_id =
523047043c2SRobert Mustacchi 		    AMDZEN_DF_F0_FBIINFO3_INSTID(dfe->adfe_info3);
524047043c2SRobert Mustacchi 		dfe->adfe_fabric_id =
525047043c2SRobert Mustacchi 		    AMDZEN_DF_F0_FBIINFO3_FABID(dfe->adfe_info3);
526047043c2SRobert Mustacchi 	}
527047043c2SRobert Mustacchi 
528047043c2SRobert Mustacchi 	df->adf_syscfg = amdzen_stub_get32(df->adf_funcs[1],
529047043c2SRobert Mustacchi 	    AMDZEN_DF_F1_SYSCFG);
530047043c2SRobert Mustacchi 	df->adf_nodeid = AMDZEN_DF_F1_SYSCFG_NODEID(df->adf_syscfg);
531047043c2SRobert Mustacchi 	df->adf_mask0 = amdzen_stub_get32(df->adf_funcs[1],
532047043c2SRobert Mustacchi 	    AMDZEN_DF_F1_FIDMASK0);
533047043c2SRobert Mustacchi 	df->adf_mask1 = amdzen_stub_get32(df->adf_funcs[1],
534047043c2SRobert Mustacchi 	    AMDZEN_DF_F1_FIDMASK1);
535047043c2SRobert Mustacchi }
536047043c2SRobert Mustacchi 
537047043c2SRobert Mustacchi static void
538047043c2SRobert Mustacchi amdzen_find_nb(amdzen_t *azn, amdzen_df_t *df)
539047043c2SRobert Mustacchi {
540047043c2SRobert Mustacchi 	amdzen_stub_t *stub;
541047043c2SRobert Mustacchi 
542047043c2SRobert Mustacchi 	for (stub = list_head(&azn->azn_nb_stubs); stub != NULL;
543047043c2SRobert Mustacchi 	    stub = list_next(&azn->azn_nb_stubs, stub)) {
544047043c2SRobert Mustacchi 		if (stub->azns_bus == df->adf_nb_busno) {
545047043c2SRobert Mustacchi 			df->adf_flags |= AMDZEN_DF_F_FOUND_NB;
546047043c2SRobert Mustacchi 			df->adf_nb = stub;
547047043c2SRobert Mustacchi 			return;
548047043c2SRobert Mustacchi 		}
549047043c2SRobert Mustacchi 	}
550047043c2SRobert Mustacchi }
551047043c2SRobert Mustacchi 
552047043c2SRobert Mustacchi static void
553047043c2SRobert Mustacchi amdzen_nexus_init(void *arg)
554047043c2SRobert Mustacchi {
555047043c2SRobert Mustacchi 	uint_t i;
556047043c2SRobert Mustacchi 	amdzen_t *azn = arg;
557047043c2SRobert Mustacchi 
558047043c2SRobert Mustacchi 	/*
559047043c2SRobert Mustacchi 	 * First go through all of the stubs and assign the DF entries.
560047043c2SRobert Mustacchi 	 */
561047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
562047043c2SRobert Mustacchi 	if (!amdzen_map_dfs(azn) || !amdzen_check_dfs(azn)) {
563047043c2SRobert Mustacchi 		azn->azn_flags |= AMDZEN_F_MAP_ERROR;
564047043c2SRobert Mustacchi 		goto done;
565047043c2SRobert Mustacchi 	}
566047043c2SRobert Mustacchi 
567047043c2SRobert Mustacchi 	for (i = 0; i < AMDZEN_MAX_DFS; i++) {
568047043c2SRobert Mustacchi 		amdzen_df_t *df = &azn->azn_dfs[i];
569047043c2SRobert Mustacchi 
570047043c2SRobert Mustacchi 		if ((df->adf_flags & AMDZEN_DF_F_VALID) == 0)
571047043c2SRobert Mustacchi 			continue;
572047043c2SRobert Mustacchi 		amdzen_setup_df(azn, df);
573047043c2SRobert Mustacchi 		amdzen_find_nb(azn, df);
574047043c2SRobert Mustacchi 	}
575047043c2SRobert Mustacchi 
576047043c2SRobert Mustacchi 	/*
577047043c2SRobert Mustacchi 	 * Not all children may be installed. As such, we do not treat the
578047043c2SRobert Mustacchi 	 * failure of a child as fatal to the driver.
579047043c2SRobert Mustacchi 	 */
580047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
581047043c2SRobert Mustacchi 	for (i = 0; i < ARRAY_SIZE(amdzen_children); i++) {
582047043c2SRobert Mustacchi 		(void) amdzen_create_child(azn, &amdzen_children[i]);
583047043c2SRobert Mustacchi 	}
584047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
585047043c2SRobert Mustacchi 
586047043c2SRobert Mustacchi done:
587047043c2SRobert Mustacchi 	azn->azn_flags &= ~AMDZEN_F_ATTACH_DISPATCHED;
588047043c2SRobert Mustacchi 	azn->azn_flags |= AMDZEN_F_ATTACH_COMPLETE;
589047043c2SRobert Mustacchi 	azn->azn_taskqid = TASKQID_INVALID;
590047043c2SRobert Mustacchi 	cv_broadcast(&azn->azn_cv);
591047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
592047043c2SRobert Mustacchi }
593047043c2SRobert Mustacchi 
594047043c2SRobert Mustacchi static int
595047043c2SRobert Mustacchi amdzen_stub_scan_cb(dev_info_t *dip, void *arg)
596047043c2SRobert Mustacchi {
597047043c2SRobert Mustacchi 	amdzen_t *azn = arg;
598047043c2SRobert Mustacchi 	uint16_t vid, did;
599047043c2SRobert Mustacchi 	int *regs;
600047043c2SRobert Mustacchi 	uint_t nregs, i;
601047043c2SRobert Mustacchi 	boolean_t match = B_FALSE;
602047043c2SRobert Mustacchi 
603047043c2SRobert Mustacchi 	if (dip == ddi_root_node()) {
604047043c2SRobert Mustacchi 		return (DDI_WALK_CONTINUE);
605047043c2SRobert Mustacchi 	}
606047043c2SRobert Mustacchi 
607047043c2SRobert Mustacchi 	/*
608047043c2SRobert Mustacchi 	 * If a node in question is not a pci node, then we have no interest in
609047043c2SRobert Mustacchi 	 * it as all the stubs that we care about are related to pci devices.
610047043c2SRobert Mustacchi 	 */
611047043c2SRobert Mustacchi 	if (strncmp("pci", ddi_get_name(dip), 3) != 0) {
612047043c2SRobert Mustacchi 		return (DDI_WALK_PRUNECHILD);
613047043c2SRobert Mustacchi 	}
614047043c2SRobert Mustacchi 
615047043c2SRobert Mustacchi 	/*
616047043c2SRobert Mustacchi 	 * If we can't get a device or vendor ID and prove that this is an AMD
617047043c2SRobert Mustacchi 	 * part, then we don't care about it.
618047043c2SRobert Mustacchi 	 */
619047043c2SRobert Mustacchi 	vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
620047043c2SRobert Mustacchi 	    "vendor-id", PCI_EINVAL16);
621047043c2SRobert Mustacchi 	did = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
622047043c2SRobert Mustacchi 	    "device-id", PCI_EINVAL16);
623047043c2SRobert Mustacchi 	if (vid == PCI_EINVAL16 || did == PCI_EINVAL16) {
624047043c2SRobert Mustacchi 		return (DDI_WALK_CONTINUE);
625047043c2SRobert Mustacchi 	}
626047043c2SRobert Mustacchi 
627*9b0429a1SPu Wen 	if (vid != AMDZEN_PCI_VID_AMD && vid != AMDZEN_PCI_VID_HYGON) {
628047043c2SRobert Mustacchi 		return (DDI_WALK_CONTINUE);
629047043c2SRobert Mustacchi 	}
630047043c2SRobert Mustacchi 
631047043c2SRobert Mustacchi 	for (i = 0; i < ARRAY_SIZE(amdzen_nb_ids); i++) {
632047043c2SRobert Mustacchi 		if (amdzen_nb_ids[i] == did) {
633047043c2SRobert Mustacchi 			match = B_TRUE;
634047043c2SRobert Mustacchi 		}
635047043c2SRobert Mustacchi 	}
636047043c2SRobert Mustacchi 
637047043c2SRobert Mustacchi 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
638047043c2SRobert Mustacchi 	    "reg", &regs, &nregs) != DDI_PROP_SUCCESS) {
639047043c2SRobert Mustacchi 		return (DDI_WALK_CONTINUE);
640047043c2SRobert Mustacchi 	}
641047043c2SRobert Mustacchi 
642047043c2SRobert Mustacchi 	if (nregs == 0) {
643047043c2SRobert Mustacchi 		ddi_prop_free(regs);
644047043c2SRobert Mustacchi 		return (DDI_WALK_CONTINUE);
645047043c2SRobert Mustacchi 	}
646047043c2SRobert Mustacchi 
647047043c2SRobert Mustacchi 	if (PCI_REG_BUS_G(regs[0]) == AMDZEN_DF_BUSNO &&
648047043c2SRobert Mustacchi 	    PCI_REG_DEV_G(regs[0]) >= AMDZEN_DF_FIRST_DEVICE) {
649047043c2SRobert Mustacchi 		match = B_TRUE;
650047043c2SRobert Mustacchi 	}
651047043c2SRobert Mustacchi 
652047043c2SRobert Mustacchi 	ddi_prop_free(regs);
653047043c2SRobert Mustacchi 	if (match) {
654047043c2SRobert Mustacchi 		mutex_enter(&azn->azn_mutex);
655047043c2SRobert Mustacchi 		azn->azn_nscanned++;
656047043c2SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
657047043c2SRobert Mustacchi 	}
658047043c2SRobert Mustacchi 
659047043c2SRobert Mustacchi 	return (DDI_WALK_CONTINUE);
660047043c2SRobert Mustacchi }
661047043c2SRobert Mustacchi 
662047043c2SRobert Mustacchi static void
663047043c2SRobert Mustacchi amdzen_stub_scan(void *arg)
664047043c2SRobert Mustacchi {
665047043c2SRobert Mustacchi 	amdzen_t *azn = arg;
666047043c2SRobert Mustacchi 
667047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
668047043c2SRobert Mustacchi 	azn->azn_nscanned = 0;
669047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
670047043c2SRobert Mustacchi 
671047043c2SRobert Mustacchi 	ddi_walk_devs(ddi_root_node(), amdzen_stub_scan_cb, azn);
672047043c2SRobert Mustacchi 
673047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
674047043c2SRobert Mustacchi 	azn->azn_flags &= ~AMDZEN_F_SCAN_DISPATCHED;
675047043c2SRobert Mustacchi 	azn->azn_flags |= AMDZEN_F_SCAN_COMPLETE;
676047043c2SRobert Mustacchi 
677047043c2SRobert Mustacchi 	if (azn->azn_nscanned == 0) {
678047043c2SRobert Mustacchi 		azn->azn_flags |= AMDZEN_F_UNSUPPORTED;
679047043c2SRobert Mustacchi 		azn->azn_taskqid = TASKQID_INVALID;
680047043c2SRobert Mustacchi 		cv_broadcast(&azn->azn_cv);
681047043c2SRobert Mustacchi 	} else if (azn->azn_npresent == azn->azn_nscanned) {
682047043c2SRobert Mustacchi 		azn->azn_flags |= AMDZEN_F_ATTACH_DISPATCHED;
683047043c2SRobert Mustacchi 		azn->azn_taskqid = taskq_dispatch(system_taskq,
684047043c2SRobert Mustacchi 		    amdzen_nexus_init, azn, TQ_SLEEP);
685047043c2SRobert Mustacchi 	}
686047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
687047043c2SRobert Mustacchi }
688047043c2SRobert Mustacchi 
689047043c2SRobert Mustacchi /*
690047043c2SRobert Mustacchi  * Unfortunately we can't really let the stubs detach as we may need them to be
691047043c2SRobert Mustacchi  * available for client operations. We may be able to improve this if we know
692047043c2SRobert Mustacchi  * that the actual nexus is going away. However, as long as it's active, we need
693047043c2SRobert Mustacchi  * all the stubs.
694047043c2SRobert Mustacchi  */
695047043c2SRobert Mustacchi int
696047043c2SRobert Mustacchi amdzen_detach_stub(dev_info_t *dip, ddi_detach_cmd_t cmd)
697047043c2SRobert Mustacchi {
698047043c2SRobert Mustacchi 	if (cmd == DDI_SUSPEND) {
699047043c2SRobert Mustacchi 		return (DDI_SUCCESS);
700047043c2SRobert Mustacchi 	}
701047043c2SRobert Mustacchi 
702047043c2SRobert Mustacchi 	return (DDI_FAILURE);
703047043c2SRobert Mustacchi }
704047043c2SRobert Mustacchi 
705047043c2SRobert Mustacchi int
706047043c2SRobert Mustacchi amdzen_attach_stub(dev_info_t *dip, ddi_attach_cmd_t cmd)
707047043c2SRobert Mustacchi {
708047043c2SRobert Mustacchi 	int *regs, reg;
709047043c2SRobert Mustacchi 	uint_t nregs, i;
710047043c2SRobert Mustacchi 	uint16_t vid, did;
711047043c2SRobert Mustacchi 	amdzen_stub_t *stub;
712047043c2SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
713047043c2SRobert Mustacchi 	boolean_t valid = B_FALSE;
714047043c2SRobert Mustacchi 	boolean_t nb = B_FALSE;
715047043c2SRobert Mustacchi 
716047043c2SRobert Mustacchi 	if (cmd == DDI_RESUME) {
717047043c2SRobert Mustacchi 		return (DDI_SUCCESS);
718047043c2SRobert Mustacchi 	} else if (cmd != DDI_ATTACH) {
719047043c2SRobert Mustacchi 		return (DDI_FAILURE);
720047043c2SRobert Mustacchi 	}
721047043c2SRobert Mustacchi 
722047043c2SRobert Mustacchi 	/*
723047043c2SRobert Mustacchi 	 * Make sure that the stub that we've been asked to attach is a pci type
724047043c2SRobert Mustacchi 	 * device. If not, then there is no reason for us to proceed.
725047043c2SRobert Mustacchi 	 */
726047043c2SRobert Mustacchi 	if (strncmp("pci", ddi_get_name(dip), 3) != 0) {
727047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "asked to attach a bad AMD Zen nexus "
728047043c2SRobert Mustacchi 		    "stub: %s", ddi_get_name(dip));
729047043c2SRobert Mustacchi 		return (DDI_FAILURE);
730047043c2SRobert Mustacchi 	}
731047043c2SRobert Mustacchi 	vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
732047043c2SRobert Mustacchi 	    "vendor-id", PCI_EINVAL16);
733047043c2SRobert Mustacchi 	did = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
734047043c2SRobert Mustacchi 	    "device-id", PCI_EINVAL16);
735047043c2SRobert Mustacchi 	if (vid == PCI_EINVAL16 || did == PCI_EINVAL16) {
736047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "failed to get PCI ID properties");
737047043c2SRobert Mustacchi 		return (DDI_FAILURE);
738047043c2SRobert Mustacchi 	}
739047043c2SRobert Mustacchi 
740*9b0429a1SPu Wen 	if (vid != AMDZEN_PCI_VID_AMD && vid != AMDZEN_PCI_VID_HYGON) {
741*9b0429a1SPu Wen 		dev_err(dip, CE_WARN, "expected vendor ID (0x%x), found 0x%x",
742*9b0429a1SPu Wen 		    cpuid_getvendor(CPU) == X86_VENDOR_HYGON ?
743*9b0429a1SPu Wen 		    AMDZEN_PCI_VID_HYGON : AMDZEN_PCI_VID_AMD, vid);
744047043c2SRobert Mustacchi 		return (DDI_FAILURE);
745047043c2SRobert Mustacchi 	}
746047043c2SRobert Mustacchi 
747047043c2SRobert Mustacchi 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
748047043c2SRobert Mustacchi 	    "reg", &regs, &nregs) != DDI_PROP_SUCCESS) {
749047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "failed to get 'reg' property");
750047043c2SRobert Mustacchi 		return (DDI_FAILURE);
751047043c2SRobert Mustacchi 	}
752047043c2SRobert Mustacchi 
753047043c2SRobert Mustacchi 	if (nregs == 0) {
754047043c2SRobert Mustacchi 		ddi_prop_free(regs);
755047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "missing 'reg' property values");
756047043c2SRobert Mustacchi 		return (DDI_FAILURE);
757047043c2SRobert Mustacchi 	}
758047043c2SRobert Mustacchi 	reg = *regs;
759047043c2SRobert Mustacchi 	ddi_prop_free(regs);
760047043c2SRobert Mustacchi 
761047043c2SRobert Mustacchi 	for (i = 0; i < ARRAY_SIZE(amdzen_nb_ids); i++) {
762047043c2SRobert Mustacchi 		if (amdzen_nb_ids[i] == did) {
763047043c2SRobert Mustacchi 			valid = B_TRUE;
764047043c2SRobert Mustacchi 			nb = B_TRUE;
765047043c2SRobert Mustacchi 		}
766047043c2SRobert Mustacchi 	}
767047043c2SRobert Mustacchi 
768047043c2SRobert Mustacchi 	if (!valid && PCI_REG_BUS_G(reg) == AMDZEN_DF_BUSNO &&
769047043c2SRobert Mustacchi 	    PCI_REG_DEV_G(reg) >= AMDZEN_DF_FIRST_DEVICE) {
770047043c2SRobert Mustacchi 		valid = B_TRUE;
771047043c2SRobert Mustacchi 		nb = B_FALSE;
772047043c2SRobert Mustacchi 	}
773047043c2SRobert Mustacchi 
774047043c2SRobert Mustacchi 	if (!valid) {
775047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "device %s didn't match the nexus list",
776047043c2SRobert Mustacchi 		    ddi_get_name(dip));
777047043c2SRobert Mustacchi 		return (DDI_FAILURE);
778047043c2SRobert Mustacchi 	}
779047043c2SRobert Mustacchi 
780047043c2SRobert Mustacchi 	stub = kmem_alloc(sizeof (amdzen_stub_t), KM_SLEEP);
781047043c2SRobert Mustacchi 	if (pci_config_setup(dip, &stub->azns_cfgspace) != DDI_SUCCESS) {
782047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "failed to set up config space");
783047043c2SRobert Mustacchi 		kmem_free(stub, sizeof (amdzen_stub_t));
784047043c2SRobert Mustacchi 		return (DDI_FAILURE);
785047043c2SRobert Mustacchi 	}
786047043c2SRobert Mustacchi 
787047043c2SRobert Mustacchi 	stub->azns_dip = dip;
788047043c2SRobert Mustacchi 	stub->azns_vid = vid;
789047043c2SRobert Mustacchi 	stub->azns_did = did;
790047043c2SRobert Mustacchi 	stub->azns_bus = PCI_REG_BUS_G(reg);
791047043c2SRobert Mustacchi 	stub->azns_dev = PCI_REG_DEV_G(reg);
792047043c2SRobert Mustacchi 	stub->azns_func = PCI_REG_FUNC_G(reg);
793047043c2SRobert Mustacchi 	ddi_set_driver_private(dip, stub);
794047043c2SRobert Mustacchi 
795047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
796047043c2SRobert Mustacchi 	azn->azn_npresent++;
797047043c2SRobert Mustacchi 	if (nb) {
798047043c2SRobert Mustacchi 		list_insert_tail(&azn->azn_nb_stubs, stub);
799047043c2SRobert Mustacchi 	} else {
800047043c2SRobert Mustacchi 		list_insert_tail(&azn->azn_df_stubs, stub);
801047043c2SRobert Mustacchi 	}
802047043c2SRobert Mustacchi 
803047043c2SRobert Mustacchi 	if ((azn->azn_flags & AMDZEN_F_TASKQ_MASK) == AMDZEN_F_SCAN_COMPLETE &&
804047043c2SRobert Mustacchi 	    azn->azn_nscanned == azn->azn_npresent) {
805047043c2SRobert Mustacchi 		azn->azn_flags |= AMDZEN_F_ATTACH_DISPATCHED;
806047043c2SRobert Mustacchi 		azn->azn_taskqid = taskq_dispatch(system_taskq,
807047043c2SRobert Mustacchi 		    amdzen_nexus_init, azn, TQ_SLEEP);
808047043c2SRobert Mustacchi 	}
809047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
810047043c2SRobert Mustacchi 
811047043c2SRobert Mustacchi 	return (DDI_SUCCESS);
812047043c2SRobert Mustacchi }
813047043c2SRobert Mustacchi 
814047043c2SRobert Mustacchi static int
815047043c2SRobert Mustacchi amdzen_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
816047043c2SRobert Mustacchi     void *arg, void *result)
817047043c2SRobert Mustacchi {
818047043c2SRobert Mustacchi 	char buf[32];
819047043c2SRobert Mustacchi 	dev_info_t *child;
820047043c2SRobert Mustacchi 	const amdzen_child_data_t *acd;
821047043c2SRobert Mustacchi 
822047043c2SRobert Mustacchi 	switch (ctlop) {
823047043c2SRobert Mustacchi 	case DDI_CTLOPS_REPORTDEV:
824047043c2SRobert Mustacchi 		if (rdip == NULL) {
825047043c2SRobert Mustacchi 			return (DDI_FAILURE);
826047043c2SRobert Mustacchi 		}
827047043c2SRobert Mustacchi 		cmn_err(CE_CONT, "amdzen nexus: %s@%s, %s%d\n",
828047043c2SRobert Mustacchi 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
829047043c2SRobert Mustacchi 		    ddi_driver_name(rdip), ddi_get_instance(rdip));
830047043c2SRobert Mustacchi 		break;
831047043c2SRobert Mustacchi 	case DDI_CTLOPS_INITCHILD:
832047043c2SRobert Mustacchi 		child = arg;
833047043c2SRobert Mustacchi 		if (child == NULL) {
834047043c2SRobert Mustacchi 			dev_err(dip, CE_WARN, "!no child passed for "
835047043c2SRobert Mustacchi 			    "DDI_CTLOPS_INITCHILD");
836047043c2SRobert Mustacchi 		}
837047043c2SRobert Mustacchi 
838047043c2SRobert Mustacchi 		acd = ddi_get_parent_data(child);
839047043c2SRobert Mustacchi 		if (acd == NULL) {
840047043c2SRobert Mustacchi 			dev_err(dip, CE_WARN, "!missing child parent data");
841047043c2SRobert Mustacchi 			return (DDI_FAILURE);
842047043c2SRobert Mustacchi 		}
843047043c2SRobert Mustacchi 
844047043c2SRobert Mustacchi 		if (snprintf(buf, sizeof (buf), "%d", acd->acd_addr) >=
845047043c2SRobert Mustacchi 		    sizeof (buf)) {
846047043c2SRobert Mustacchi 			dev_err(dip, CE_WARN, "!failed to construct device "
847047043c2SRobert Mustacchi 			    "addr due to overflow");
848047043c2SRobert Mustacchi 			return (DDI_FAILURE);
849047043c2SRobert Mustacchi 		}
850047043c2SRobert Mustacchi 
851047043c2SRobert Mustacchi 		ddi_set_name_addr(child, buf);
852047043c2SRobert Mustacchi 		break;
853047043c2SRobert Mustacchi 	case DDI_CTLOPS_UNINITCHILD:
854047043c2SRobert Mustacchi 		child = arg;
855047043c2SRobert Mustacchi 		if (child == NULL) {
856047043c2SRobert Mustacchi 			dev_err(dip, CE_WARN, "!no child passed for "
857047043c2SRobert Mustacchi 			    "DDI_CTLOPS_UNINITCHILD");
858047043c2SRobert Mustacchi 		}
859047043c2SRobert Mustacchi 
860047043c2SRobert Mustacchi 		ddi_set_name_addr(child, NULL);
861047043c2SRobert Mustacchi 		break;
862047043c2SRobert Mustacchi 	default:
863047043c2SRobert Mustacchi 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
864047043c2SRobert Mustacchi 	}
865047043c2SRobert Mustacchi 	return (DDI_SUCCESS);
866047043c2SRobert Mustacchi }
867047043c2SRobert Mustacchi 
868047043c2SRobert Mustacchi static int
869047043c2SRobert Mustacchi amdzen_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
870047043c2SRobert Mustacchi {
871047043c2SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
872047043c2SRobert Mustacchi 
873047043c2SRobert Mustacchi 	if (cmd == DDI_RESUME) {
874047043c2SRobert Mustacchi 		return (DDI_SUCCESS);
875047043c2SRobert Mustacchi 	} else if (cmd != DDI_ATTACH) {
876047043c2SRobert Mustacchi 		return (DDI_FAILURE);
877047043c2SRobert Mustacchi 	}
878047043c2SRobert Mustacchi 
879047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
880047043c2SRobert Mustacchi 	if (azn->azn_dip != NULL) {
881047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "driver is already attached!");
882047043c2SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
883047043c2SRobert Mustacchi 		return (DDI_FAILURE);
884047043c2SRobert Mustacchi 	}
885047043c2SRobert Mustacchi 
886047043c2SRobert Mustacchi 	azn->azn_dip = dip;
887047043c2SRobert Mustacchi 	azn->azn_taskqid = taskq_dispatch(system_taskq, amdzen_stub_scan,
888047043c2SRobert Mustacchi 	    azn, TQ_SLEEP);
889047043c2SRobert Mustacchi 	azn->azn_flags |= AMDZEN_F_SCAN_DISPATCHED;
890047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
891047043c2SRobert Mustacchi 
892047043c2SRobert Mustacchi 	return (DDI_SUCCESS);
893047043c2SRobert Mustacchi }
894047043c2SRobert Mustacchi 
895047043c2SRobert Mustacchi static int
896047043c2SRobert Mustacchi amdzen_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
897047043c2SRobert Mustacchi {
898047043c2SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
899047043c2SRobert Mustacchi 
900047043c2SRobert Mustacchi 	if (cmd == DDI_SUSPEND) {
901047043c2SRobert Mustacchi 		return (DDI_SUCCESS);
902047043c2SRobert Mustacchi 	} else if (cmd != DDI_DETACH) {
903047043c2SRobert Mustacchi 		return (DDI_FAILURE);
904047043c2SRobert Mustacchi 	}
905047043c2SRobert Mustacchi 
906047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
907047043c2SRobert Mustacchi 	while (azn->azn_taskqid != TASKQID_INVALID) {
908047043c2SRobert Mustacchi 		cv_wait(&azn->azn_cv, &azn->azn_mutex);
909047043c2SRobert Mustacchi 	}
910047043c2SRobert Mustacchi 
911047043c2SRobert Mustacchi 	/*
912047043c2SRobert Mustacchi 	 * If we've attached any stub drivers, e.g. this platform is important
913047043c2SRobert Mustacchi 	 * for us, then we fail detach.
914047043c2SRobert Mustacchi 	 */
915047043c2SRobert Mustacchi 	if (!list_is_empty(&azn->azn_df_stubs) ||
916047043c2SRobert Mustacchi 	    !list_is_empty(&azn->azn_nb_stubs)) {
917047043c2SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
918047043c2SRobert Mustacchi 		return (DDI_FAILURE);
919047043c2SRobert Mustacchi 	}
920047043c2SRobert Mustacchi 
921047043c2SRobert Mustacchi 	azn->azn_dip = NULL;
922047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
923047043c2SRobert Mustacchi 
924047043c2SRobert Mustacchi 	return (DDI_SUCCESS);
925047043c2SRobert Mustacchi }
926047043c2SRobert Mustacchi 
927047043c2SRobert Mustacchi static void
928047043c2SRobert Mustacchi amdzen_free(void)
929047043c2SRobert Mustacchi {
930047043c2SRobert Mustacchi 	if (amdzen_data == NULL) {
931047043c2SRobert Mustacchi 		return;
932047043c2SRobert Mustacchi 	}
933047043c2SRobert Mustacchi 
934047043c2SRobert Mustacchi 	VERIFY(list_is_empty(&amdzen_data->azn_df_stubs));
935047043c2SRobert Mustacchi 	list_destroy(&amdzen_data->azn_df_stubs);
936047043c2SRobert Mustacchi 	VERIFY(list_is_empty(&amdzen_data->azn_nb_stubs));
937047043c2SRobert Mustacchi 	list_destroy(&amdzen_data->azn_nb_stubs);
938047043c2SRobert Mustacchi 	cv_destroy(&amdzen_data->azn_cv);
939047043c2SRobert Mustacchi 	mutex_destroy(&amdzen_data->azn_mutex);
940047043c2SRobert Mustacchi 	kmem_free(amdzen_data, sizeof (amdzen_t));
941047043c2SRobert Mustacchi 	amdzen_data = NULL;
942047043c2SRobert Mustacchi }
943047043c2SRobert Mustacchi 
944047043c2SRobert Mustacchi static void
945047043c2SRobert Mustacchi amdzen_alloc(void)
946047043c2SRobert Mustacchi {
947047043c2SRobert Mustacchi 	amdzen_data = kmem_zalloc(sizeof (amdzen_t), KM_SLEEP);
948047043c2SRobert Mustacchi 	mutex_init(&amdzen_data->azn_mutex, NULL, MUTEX_DRIVER, NULL);
949047043c2SRobert Mustacchi 	list_create(&amdzen_data->azn_df_stubs, sizeof (amdzen_stub_t),
950047043c2SRobert Mustacchi 	    offsetof(amdzen_stub_t, azns_link));
951047043c2SRobert Mustacchi 	list_create(&amdzen_data->azn_nb_stubs, sizeof (amdzen_stub_t),
952047043c2SRobert Mustacchi 	    offsetof(amdzen_stub_t, azns_link));
953047043c2SRobert Mustacchi 	cv_init(&amdzen_data->azn_cv, NULL, CV_DRIVER, NULL);
954047043c2SRobert Mustacchi }
955047043c2SRobert Mustacchi 
956047043c2SRobert Mustacchi struct bus_ops amdzen_bus_ops = {
957047043c2SRobert Mustacchi 	.busops_rev = BUSO_REV,
958047043c2SRobert Mustacchi 	.bus_map = nullbusmap,
959047043c2SRobert Mustacchi 	.bus_dma_map = ddi_no_dma_map,
960047043c2SRobert Mustacchi 	.bus_dma_allochdl = ddi_no_dma_allochdl,
961047043c2SRobert Mustacchi 	.bus_dma_freehdl = ddi_no_dma_freehdl,
962047043c2SRobert Mustacchi 	.bus_dma_bindhdl = ddi_no_dma_bindhdl,
963047043c2SRobert Mustacchi 	.bus_dma_unbindhdl = ddi_no_dma_unbindhdl,
964047043c2SRobert Mustacchi 	.bus_dma_flush = ddi_no_dma_flush,
965047043c2SRobert Mustacchi 	.bus_dma_win = ddi_no_dma_win,
966047043c2SRobert Mustacchi 	.bus_dma_ctl = ddi_no_dma_mctl,
967047043c2SRobert Mustacchi 	.bus_prop_op = ddi_bus_prop_op,
968047043c2SRobert Mustacchi 	.bus_ctl = amdzen_bus_ctl
969047043c2SRobert Mustacchi };
970047043c2SRobert Mustacchi 
971047043c2SRobert Mustacchi static struct dev_ops amdzen_dev_ops = {
972047043c2SRobert Mustacchi 	.devo_rev = DEVO_REV,
973047043c2SRobert Mustacchi 	.devo_refcnt = 0,
974047043c2SRobert Mustacchi 	.devo_getinfo = nodev,
975047043c2SRobert Mustacchi 	.devo_identify = nulldev,
976047043c2SRobert Mustacchi 	.devo_probe = nulldev,
977047043c2SRobert Mustacchi 	.devo_attach = amdzen_attach,
978047043c2SRobert Mustacchi 	.devo_detach = amdzen_detach,
979047043c2SRobert Mustacchi 	.devo_reset = nodev,
980047043c2SRobert Mustacchi 	.devo_quiesce = ddi_quiesce_not_needed,
981047043c2SRobert Mustacchi 	.devo_bus_ops = &amdzen_bus_ops
982047043c2SRobert Mustacchi };
983047043c2SRobert Mustacchi 
984047043c2SRobert Mustacchi static struct modldrv amdzen_modldrv = {
985047043c2SRobert Mustacchi 	.drv_modops = &mod_driverops,
986047043c2SRobert Mustacchi 	.drv_linkinfo = "AMD Zen Nexus Driver",
987047043c2SRobert Mustacchi 	.drv_dev_ops = &amdzen_dev_ops
988047043c2SRobert Mustacchi };
989047043c2SRobert Mustacchi 
990047043c2SRobert Mustacchi static struct modlinkage amdzen_modlinkage = {
991047043c2SRobert Mustacchi 	.ml_rev = MODREV_1,
992047043c2SRobert Mustacchi 	.ml_linkage = { &amdzen_modldrv, NULL }
993047043c2SRobert Mustacchi };
994047043c2SRobert Mustacchi 
995047043c2SRobert Mustacchi int
996047043c2SRobert Mustacchi _init(void)
997047043c2SRobert Mustacchi {
998047043c2SRobert Mustacchi 	int ret;
999047043c2SRobert Mustacchi 
1000*9b0429a1SPu Wen 	if (cpuid_getvendor(CPU) != X86_VENDOR_AMD &&
1001*9b0429a1SPu Wen 	    cpuid_getvendor(CPU) != X86_VENDOR_HYGON) {
1002047043c2SRobert Mustacchi 		return (ENOTSUP);
1003047043c2SRobert Mustacchi 	}
1004047043c2SRobert Mustacchi 
1005047043c2SRobert Mustacchi 	if ((ret = mod_install(&amdzen_modlinkage)) == 0) {
1006047043c2SRobert Mustacchi 		amdzen_alloc();
1007047043c2SRobert Mustacchi 	}
1008047043c2SRobert Mustacchi 
1009047043c2SRobert Mustacchi 	return (ret);
1010047043c2SRobert Mustacchi }
1011047043c2SRobert Mustacchi 
1012047043c2SRobert Mustacchi int
1013047043c2SRobert Mustacchi _info(struct modinfo *modinfop)
1014047043c2SRobert Mustacchi {
1015047043c2SRobert Mustacchi 	return (mod_info(&amdzen_modlinkage, modinfop));
1016047043c2SRobert Mustacchi }
1017047043c2SRobert Mustacchi 
1018047043c2SRobert Mustacchi int
1019047043c2SRobert Mustacchi _fini(void)
1020047043c2SRobert Mustacchi {
1021047043c2SRobert Mustacchi 	int ret;
1022047043c2SRobert Mustacchi 
1023047043c2SRobert Mustacchi 	if ((ret = mod_remove(&amdzen_modlinkage)) == 0) {
1024047043c2SRobert Mustacchi 		amdzen_free();
1025047043c2SRobert Mustacchi 	}
1026047043c2SRobert Mustacchi 
1027047043c2SRobert Mustacchi 	return (ret);
1028047043c2SRobert Mustacchi }
1029