xref: /illumos-gate/usr/src/uts/intel/io/amdzen/amdzen.c (revision e9abe9d6)
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,
147*e9abe9d6SRobert 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 /*
439*e9abe9d6SRobert Mustacchi  * Check the first df entry to see if it belongs to Rome or Milan. If so, then
440*e9abe9d6SRobert Mustacchi  * it uses the disjoint ID space.
441*e9abe9d6SRobert Mustacchi  */
442*e9abe9d6SRobert Mustacchi static boolean_t
443*e9abe9d6SRobert Mustacchi amdzen_is_rome_style(uint_t id)
444*e9abe9d6SRobert Mustacchi {
445*e9abe9d6SRobert Mustacchi 	return (id == 0x1490 || id == 0x1650);
446*e9abe9d6SRobert Mustacchi }
447*e9abe9d6SRobert Mustacchi 
448*e9abe9d6SRobert 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 		 */
477*e9abe9d6SRobert 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, "
481*e9abe9d6SRobert 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 
627047043c2SRobert Mustacchi 	if (vid != AMDZEN_PCI_VID_AMD) {
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 
740047043c2SRobert Mustacchi 	if (vid != AMDZEN_PCI_VID_AMD) {
741047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "expected AMD vendor ID (0x%x), found "
742047043c2SRobert Mustacchi 		    "0x%x", AMDZEN_PCI_VID_AMD, vid);
743047043c2SRobert Mustacchi 		return (DDI_FAILURE);
744047043c2SRobert Mustacchi 	}
745047043c2SRobert Mustacchi 
746047043c2SRobert Mustacchi 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
747047043c2SRobert Mustacchi 	    "reg", &regs, &nregs) != DDI_PROP_SUCCESS) {
748047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "failed to get 'reg' property");
749047043c2SRobert Mustacchi 		return (DDI_FAILURE);
750047043c2SRobert Mustacchi 	}
751047043c2SRobert Mustacchi 
752047043c2SRobert Mustacchi 	if (nregs == 0) {
753047043c2SRobert Mustacchi 		ddi_prop_free(regs);
754047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "missing 'reg' property values");
755047043c2SRobert Mustacchi 		return (DDI_FAILURE);
756047043c2SRobert Mustacchi 	}
757047043c2SRobert Mustacchi 	reg = *regs;
758047043c2SRobert Mustacchi 	ddi_prop_free(regs);
759047043c2SRobert Mustacchi 
760047043c2SRobert Mustacchi 	for (i = 0; i < ARRAY_SIZE(amdzen_nb_ids); i++) {
761047043c2SRobert Mustacchi 		if (amdzen_nb_ids[i] == did) {
762047043c2SRobert Mustacchi 			valid = B_TRUE;
763047043c2SRobert Mustacchi 			nb = B_TRUE;
764047043c2SRobert Mustacchi 		}
765047043c2SRobert Mustacchi 	}
766047043c2SRobert Mustacchi 
767047043c2SRobert Mustacchi 	if (!valid && PCI_REG_BUS_G(reg) == AMDZEN_DF_BUSNO &&
768047043c2SRobert Mustacchi 	    PCI_REG_DEV_G(reg) >= AMDZEN_DF_FIRST_DEVICE) {
769047043c2SRobert Mustacchi 		valid = B_TRUE;
770047043c2SRobert Mustacchi 		nb = B_FALSE;
771047043c2SRobert Mustacchi 	}
772047043c2SRobert Mustacchi 
773047043c2SRobert Mustacchi 	if (!valid) {
774047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "device %s didn't match the nexus list",
775047043c2SRobert Mustacchi 		    ddi_get_name(dip));
776047043c2SRobert Mustacchi 		return (DDI_FAILURE);
777047043c2SRobert Mustacchi 	}
778047043c2SRobert Mustacchi 
779047043c2SRobert Mustacchi 	stub = kmem_alloc(sizeof (amdzen_stub_t), KM_SLEEP);
780047043c2SRobert Mustacchi 	if (pci_config_setup(dip, &stub->azns_cfgspace) != DDI_SUCCESS) {
781047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "failed to set up config space");
782047043c2SRobert Mustacchi 		kmem_free(stub, sizeof (amdzen_stub_t));
783047043c2SRobert Mustacchi 		return (DDI_FAILURE);
784047043c2SRobert Mustacchi 	}
785047043c2SRobert Mustacchi 
786047043c2SRobert Mustacchi 	stub->azns_dip = dip;
787047043c2SRobert Mustacchi 	stub->azns_vid = vid;
788047043c2SRobert Mustacchi 	stub->azns_did = did;
789047043c2SRobert Mustacchi 	stub->azns_bus = PCI_REG_BUS_G(reg);
790047043c2SRobert Mustacchi 	stub->azns_dev = PCI_REG_DEV_G(reg);
791047043c2SRobert Mustacchi 	stub->azns_func = PCI_REG_FUNC_G(reg);
792047043c2SRobert Mustacchi 	ddi_set_driver_private(dip, stub);
793047043c2SRobert Mustacchi 
794047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
795047043c2SRobert Mustacchi 	azn->azn_npresent++;
796047043c2SRobert Mustacchi 	if (nb) {
797047043c2SRobert Mustacchi 		list_insert_tail(&azn->azn_nb_stubs, stub);
798047043c2SRobert Mustacchi 	} else {
799047043c2SRobert Mustacchi 		list_insert_tail(&azn->azn_df_stubs, stub);
800047043c2SRobert Mustacchi 	}
801047043c2SRobert Mustacchi 
802047043c2SRobert Mustacchi 	if ((azn->azn_flags & AMDZEN_F_TASKQ_MASK) == AMDZEN_F_SCAN_COMPLETE &&
803047043c2SRobert Mustacchi 	    azn->azn_nscanned == azn->azn_npresent) {
804047043c2SRobert Mustacchi 		azn->azn_flags |= AMDZEN_F_ATTACH_DISPATCHED;
805047043c2SRobert Mustacchi 		azn->azn_taskqid = taskq_dispatch(system_taskq,
806047043c2SRobert Mustacchi 		    amdzen_nexus_init, azn, TQ_SLEEP);
807047043c2SRobert Mustacchi 	}
808047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
809047043c2SRobert Mustacchi 
810047043c2SRobert Mustacchi 	return (DDI_SUCCESS);
811047043c2SRobert Mustacchi }
812047043c2SRobert Mustacchi 
813047043c2SRobert Mustacchi static int
814047043c2SRobert Mustacchi amdzen_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
815047043c2SRobert Mustacchi     void *arg, void *result)
816047043c2SRobert Mustacchi {
817047043c2SRobert Mustacchi 	char buf[32];
818047043c2SRobert Mustacchi 	dev_info_t *child;
819047043c2SRobert Mustacchi 	const amdzen_child_data_t *acd;
820047043c2SRobert Mustacchi 
821047043c2SRobert Mustacchi 	switch (ctlop) {
822047043c2SRobert Mustacchi 	case DDI_CTLOPS_REPORTDEV:
823047043c2SRobert Mustacchi 		if (rdip == NULL) {
824047043c2SRobert Mustacchi 			return (DDI_FAILURE);
825047043c2SRobert Mustacchi 		}
826047043c2SRobert Mustacchi 		cmn_err(CE_CONT, "amdzen nexus: %s@%s, %s%d\n",
827047043c2SRobert Mustacchi 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
828047043c2SRobert Mustacchi 		    ddi_driver_name(rdip), ddi_get_instance(rdip));
829047043c2SRobert Mustacchi 		break;
830047043c2SRobert Mustacchi 	case DDI_CTLOPS_INITCHILD:
831047043c2SRobert Mustacchi 		child = arg;
832047043c2SRobert Mustacchi 		if (child == NULL) {
833047043c2SRobert Mustacchi 			dev_err(dip, CE_WARN, "!no child passed for "
834047043c2SRobert Mustacchi 			    "DDI_CTLOPS_INITCHILD");
835047043c2SRobert Mustacchi 		}
836047043c2SRobert Mustacchi 
837047043c2SRobert Mustacchi 		acd = ddi_get_parent_data(child);
838047043c2SRobert Mustacchi 		if (acd == NULL) {
839047043c2SRobert Mustacchi 			dev_err(dip, CE_WARN, "!missing child parent data");
840047043c2SRobert Mustacchi 			return (DDI_FAILURE);
841047043c2SRobert Mustacchi 		}
842047043c2SRobert Mustacchi 
843047043c2SRobert Mustacchi 		if (snprintf(buf, sizeof (buf), "%d", acd->acd_addr) >=
844047043c2SRobert Mustacchi 		    sizeof (buf)) {
845047043c2SRobert Mustacchi 			dev_err(dip, CE_WARN, "!failed to construct device "
846047043c2SRobert Mustacchi 			    "addr due to overflow");
847047043c2SRobert Mustacchi 			return (DDI_FAILURE);
848047043c2SRobert Mustacchi 		}
849047043c2SRobert Mustacchi 
850047043c2SRobert Mustacchi 		ddi_set_name_addr(child, buf);
851047043c2SRobert Mustacchi 		break;
852047043c2SRobert Mustacchi 	case DDI_CTLOPS_UNINITCHILD:
853047043c2SRobert Mustacchi 		child = arg;
854047043c2SRobert Mustacchi 		if (child == NULL) {
855047043c2SRobert Mustacchi 			dev_err(dip, CE_WARN, "!no child passed for "
856047043c2SRobert Mustacchi 			    "DDI_CTLOPS_UNINITCHILD");
857047043c2SRobert Mustacchi 		}
858047043c2SRobert Mustacchi 
859047043c2SRobert Mustacchi 		ddi_set_name_addr(child, NULL);
860047043c2SRobert Mustacchi 		break;
861047043c2SRobert Mustacchi 	default:
862047043c2SRobert Mustacchi 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
863047043c2SRobert Mustacchi 	}
864047043c2SRobert Mustacchi 	return (DDI_SUCCESS);
865047043c2SRobert Mustacchi }
866047043c2SRobert Mustacchi 
867047043c2SRobert Mustacchi static int
868047043c2SRobert Mustacchi amdzen_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
869047043c2SRobert Mustacchi {
870047043c2SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
871047043c2SRobert Mustacchi 
872047043c2SRobert Mustacchi 	if (cmd == DDI_RESUME) {
873047043c2SRobert Mustacchi 		return (DDI_SUCCESS);
874047043c2SRobert Mustacchi 	} else if (cmd != DDI_ATTACH) {
875047043c2SRobert Mustacchi 		return (DDI_FAILURE);
876047043c2SRobert Mustacchi 	}
877047043c2SRobert Mustacchi 
878047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
879047043c2SRobert Mustacchi 	if (azn->azn_dip != NULL) {
880047043c2SRobert Mustacchi 		dev_err(dip, CE_WARN, "driver is already attached!");
881047043c2SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
882047043c2SRobert Mustacchi 		return (DDI_FAILURE);
883047043c2SRobert Mustacchi 	}
884047043c2SRobert Mustacchi 
885047043c2SRobert Mustacchi 	azn->azn_dip = dip;
886047043c2SRobert Mustacchi 	azn->azn_taskqid = taskq_dispatch(system_taskq, amdzen_stub_scan,
887047043c2SRobert Mustacchi 	    azn, TQ_SLEEP);
888047043c2SRobert Mustacchi 	azn->azn_flags |= AMDZEN_F_SCAN_DISPATCHED;
889047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
890047043c2SRobert Mustacchi 
891047043c2SRobert Mustacchi 	return (DDI_SUCCESS);
892047043c2SRobert Mustacchi }
893047043c2SRobert Mustacchi 
894047043c2SRobert Mustacchi static int
895047043c2SRobert Mustacchi amdzen_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
896047043c2SRobert Mustacchi {
897047043c2SRobert Mustacchi 	amdzen_t *azn = amdzen_data;
898047043c2SRobert Mustacchi 
899047043c2SRobert Mustacchi 	if (cmd == DDI_SUSPEND) {
900047043c2SRobert Mustacchi 		return (DDI_SUCCESS);
901047043c2SRobert Mustacchi 	} else if (cmd != DDI_DETACH) {
902047043c2SRobert Mustacchi 		return (DDI_FAILURE);
903047043c2SRobert Mustacchi 	}
904047043c2SRobert Mustacchi 
905047043c2SRobert Mustacchi 	mutex_enter(&azn->azn_mutex);
906047043c2SRobert Mustacchi 	while (azn->azn_taskqid != TASKQID_INVALID) {
907047043c2SRobert Mustacchi 		cv_wait(&azn->azn_cv, &azn->azn_mutex);
908047043c2SRobert Mustacchi 	}
909047043c2SRobert Mustacchi 
910047043c2SRobert Mustacchi 	/*
911047043c2SRobert Mustacchi 	 * If we've attached any stub drivers, e.g. this platform is important
912047043c2SRobert Mustacchi 	 * for us, then we fail detach.
913047043c2SRobert Mustacchi 	 */
914047043c2SRobert Mustacchi 	if (!list_is_empty(&azn->azn_df_stubs) ||
915047043c2SRobert Mustacchi 	    !list_is_empty(&azn->azn_nb_stubs)) {
916047043c2SRobert Mustacchi 		mutex_exit(&azn->azn_mutex);
917047043c2SRobert Mustacchi 		return (DDI_FAILURE);
918047043c2SRobert Mustacchi 	}
919047043c2SRobert Mustacchi 
920047043c2SRobert Mustacchi 	azn->azn_dip = NULL;
921047043c2SRobert Mustacchi 	mutex_exit(&azn->azn_mutex);
922047043c2SRobert Mustacchi 
923047043c2SRobert Mustacchi 	return (DDI_SUCCESS);
924047043c2SRobert Mustacchi }
925047043c2SRobert Mustacchi 
926047043c2SRobert Mustacchi static void
927047043c2SRobert Mustacchi amdzen_free(void)
928047043c2SRobert Mustacchi {
929047043c2SRobert Mustacchi 	if (amdzen_data == NULL) {
930047043c2SRobert Mustacchi 		return;
931047043c2SRobert Mustacchi 	}
932047043c2SRobert Mustacchi 
933047043c2SRobert Mustacchi 	VERIFY(list_is_empty(&amdzen_data->azn_df_stubs));
934047043c2SRobert Mustacchi 	list_destroy(&amdzen_data->azn_df_stubs);
935047043c2SRobert Mustacchi 	VERIFY(list_is_empty(&amdzen_data->azn_nb_stubs));
936047043c2SRobert Mustacchi 	list_destroy(&amdzen_data->azn_nb_stubs);
937047043c2SRobert Mustacchi 	cv_destroy(&amdzen_data->azn_cv);
938047043c2SRobert Mustacchi 	mutex_destroy(&amdzen_data->azn_mutex);
939047043c2SRobert Mustacchi 	kmem_free(amdzen_data, sizeof (amdzen_t));
940047043c2SRobert Mustacchi 	amdzen_data = NULL;
941047043c2SRobert Mustacchi }
942047043c2SRobert Mustacchi 
943047043c2SRobert Mustacchi static void
944047043c2SRobert Mustacchi amdzen_alloc(void)
945047043c2SRobert Mustacchi {
946047043c2SRobert Mustacchi 	amdzen_data = kmem_zalloc(sizeof (amdzen_t), KM_SLEEP);
947047043c2SRobert Mustacchi 	mutex_init(&amdzen_data->azn_mutex, NULL, MUTEX_DRIVER, NULL);
948047043c2SRobert Mustacchi 	list_create(&amdzen_data->azn_df_stubs, sizeof (amdzen_stub_t),
949047043c2SRobert Mustacchi 	    offsetof(amdzen_stub_t, azns_link));
950047043c2SRobert Mustacchi 	list_create(&amdzen_data->azn_nb_stubs, sizeof (amdzen_stub_t),
951047043c2SRobert Mustacchi 	    offsetof(amdzen_stub_t, azns_link));
952047043c2SRobert Mustacchi 	cv_init(&amdzen_data->azn_cv, NULL, CV_DRIVER, NULL);
953047043c2SRobert Mustacchi }
954047043c2SRobert Mustacchi 
955047043c2SRobert Mustacchi struct bus_ops amdzen_bus_ops = {
956047043c2SRobert Mustacchi 	.busops_rev = BUSO_REV,
957047043c2SRobert Mustacchi 	.bus_map = nullbusmap,
958047043c2SRobert Mustacchi 	.bus_dma_map = ddi_no_dma_map,
959047043c2SRobert Mustacchi 	.bus_dma_allochdl = ddi_no_dma_allochdl,
960047043c2SRobert Mustacchi 	.bus_dma_freehdl = ddi_no_dma_freehdl,
961047043c2SRobert Mustacchi 	.bus_dma_bindhdl = ddi_no_dma_bindhdl,
962047043c2SRobert Mustacchi 	.bus_dma_unbindhdl = ddi_no_dma_unbindhdl,
963047043c2SRobert Mustacchi 	.bus_dma_flush = ddi_no_dma_flush,
964047043c2SRobert Mustacchi 	.bus_dma_win = ddi_no_dma_win,
965047043c2SRobert Mustacchi 	.bus_dma_ctl = ddi_no_dma_mctl,
966047043c2SRobert Mustacchi 	.bus_prop_op = ddi_bus_prop_op,
967047043c2SRobert Mustacchi 	.bus_ctl = amdzen_bus_ctl
968047043c2SRobert Mustacchi };
969047043c2SRobert Mustacchi 
970047043c2SRobert Mustacchi static struct dev_ops amdzen_dev_ops = {
971047043c2SRobert Mustacchi 	.devo_rev = DEVO_REV,
972047043c2SRobert Mustacchi 	.devo_refcnt = 0,
973047043c2SRobert Mustacchi 	.devo_getinfo = nodev,
974047043c2SRobert Mustacchi 	.devo_identify = nulldev,
975047043c2SRobert Mustacchi 	.devo_probe = nulldev,
976047043c2SRobert Mustacchi 	.devo_attach = amdzen_attach,
977047043c2SRobert Mustacchi 	.devo_detach = amdzen_detach,
978047043c2SRobert Mustacchi 	.devo_reset = nodev,
979047043c2SRobert Mustacchi 	.devo_quiesce = ddi_quiesce_not_needed,
980047043c2SRobert Mustacchi 	.devo_bus_ops = &amdzen_bus_ops
981047043c2SRobert Mustacchi };
982047043c2SRobert Mustacchi 
983047043c2SRobert Mustacchi static struct modldrv amdzen_modldrv = {
984047043c2SRobert Mustacchi 	.drv_modops = &mod_driverops,
985047043c2SRobert Mustacchi 	.drv_linkinfo = "AMD Zen Nexus Driver",
986047043c2SRobert Mustacchi 	.drv_dev_ops = &amdzen_dev_ops
987047043c2SRobert Mustacchi };
988047043c2SRobert Mustacchi 
989047043c2SRobert Mustacchi static struct modlinkage amdzen_modlinkage = {
990047043c2SRobert Mustacchi 	.ml_rev = MODREV_1,
991047043c2SRobert Mustacchi 	.ml_linkage = { &amdzen_modldrv, NULL }
992047043c2SRobert Mustacchi };
993047043c2SRobert Mustacchi 
994047043c2SRobert Mustacchi int
995047043c2SRobert Mustacchi _init(void)
996047043c2SRobert Mustacchi {
997047043c2SRobert Mustacchi 	int ret;
998047043c2SRobert Mustacchi 
999047043c2SRobert Mustacchi 	if (cpuid_getvendor(CPU) != X86_VENDOR_AMD) {
1000047043c2SRobert Mustacchi 		return (ENOTSUP);
1001047043c2SRobert Mustacchi 	}
1002047043c2SRobert Mustacchi 
1003047043c2SRobert Mustacchi 	if ((ret = mod_install(&amdzen_modlinkage)) == 0) {
1004047043c2SRobert Mustacchi 		amdzen_alloc();
1005047043c2SRobert Mustacchi 	}
1006047043c2SRobert Mustacchi 
1007047043c2SRobert Mustacchi 	return (ret);
1008047043c2SRobert Mustacchi }
1009047043c2SRobert Mustacchi 
1010047043c2SRobert Mustacchi int
1011047043c2SRobert Mustacchi _info(struct modinfo *modinfop)
1012047043c2SRobert Mustacchi {
1013047043c2SRobert Mustacchi 	return (mod_info(&amdzen_modlinkage, modinfop));
1014047043c2SRobert Mustacchi }
1015047043c2SRobert Mustacchi 
1016047043c2SRobert Mustacchi int
1017047043c2SRobert Mustacchi _fini(void)
1018047043c2SRobert Mustacchi {
1019047043c2SRobert Mustacchi 	int ret;
1020047043c2SRobert Mustacchi 
1021047043c2SRobert Mustacchi 	if ((ret = mod_remove(&amdzen_modlinkage)) == 0) {
1022047043c2SRobert Mustacchi 		amdzen_free();
1023047043c2SRobert Mustacchi 	}
1024047043c2SRobert Mustacchi 
1025047043c2SRobert Mustacchi 	return (ret);
1026047043c2SRobert Mustacchi }
1027