xref: /illumos-gate/usr/src/uts/intel/io/amdzen/amdzen.c (revision 91061836e6d66992af6a50374729f8ef6a6263ae)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2019, Joyent, Inc.
14  * Copyright 2020 Oxide Computer Company
15  */
16 
17 /*
18  * Nexus Driver for AMD Zen family systems. The purpose of this driver is to
19  * provide access to the following resources in a single, centralized fashion:
20  *
21  *  - The per-chip Data Fabric
22  *  - The North Bridge
23  *  - The System Management Network (SMN)
24  *
25  * This is a nexus driver as once we have attached to all the requisite
26  * components, we will enumerate child devices which consume this functionality.
27  *
28  * ------------------------
29  * Mapping Devices Together
30  * ------------------------
31  *
32  * The operating system needs to expose things like temperature sensors and DRAM
33  * configuration registers in terms that are meaningful to the system such as
34  * logical CPUs, cores, etc. This driver attaches to the PCI IDs that represent
35  * the northbridge and data fabric; however, there are multiple PCI devices (one
36  * per die) that exist. This driver does manage to map all of these three things
37  * together; however, it requires some acrobatics. Unfortunately, there's no
38  * direct way to map a northbridge to its corresponding die. However, we can map
39  * a CPU die to a data fabric PCI device and a data fabric PCI device to a
40  * corresponding northbridge PCI device.
41  *
42  * In current Zen based products, there is a direct mapping between processor
43  * nodes and a data fabric PCI device. All of the devices are on PCI Bus 0 and
44  * start from Device 0x18. Device 0x18 maps to processor node 0, 0x19 to
45  * processor node 1, etc. This means that to map a logical CPU to a data fabric
46  * device, we take its processor node id, add it to 0x18 and find the PCI device
47  * that is on bus 0, device 0x18. As each data fabric device is attached based
48  * on its PCI ID, we add it to the global list, amd_nbdf_dfs that is in the
49  * amd_f17nbdf_t structure.
50  *
51  * The northbridge PCI device has a defined device and function, but the PCI bus
52  * that it's on can vary. Each die has its own series of PCI buses that are
53  * assigned to it and the northbridge PCI device is on the first of die-specific
54  * PCI bus for each die. This also means that the northbridge will not show up
55  * on PCI bus 0, which is the PCI bus that all of the data fabric devices are
56  * on. While conventionally the northbridge with the lowest PCI bus value
57  * would correspond to processor node zero, hardware does not guarantee that at
58  * all. Because we don't want to be at the mercy of firmware, we don't rely on
59  * this ordering, even though we have yet to find a system that deviates from
60  * this scheme.
61  *
62  * One of the registers in the data fabric device's function 0
63  * (AMDZEN_DF_F0_CFG_ADDR_CTL) happens to have the first PCI bus that is
64  * associated with the processor node. This means that we can map a data fabric
65  * device to a northbridge by finding the northbridge whose PCI bus matches the
66  * value in the corresponding data fabric's AMDZEN_DF_F0_CFG_ADDR_CTL.
67  *
68  * We can map a northbridge to a data fabric device and a data fabric device to
69  * a die. Because these are generally 1:1 mappings, there is a transitive
70  * relationship and therefore we know which northbridge is associated with which
71  * processor die. This is summarized in the following image:
72  *
73  *  +-------+    +-----------------------------------+        +--------------+
74  *  | Die 0 |--->| Data Fabric PCI BDF 0/18/0        |------->| Northbridge  |
75  *  +-------+    | AMDZEN_DF_F0_CFG_ADDR_CTL: bus 10 |        | PCI  10/0/0  |
76  *     ...       +-----------------------------------+        +--------------+
77  *  +-------+     +------------------------------------+        +--------------+
78  *  | Die n |---->| Data Fabric PCI BDF 0/18+n/0       |------->| Northbridge  |
79  *  +-------+     | AMDZEN_DF_F0_CFG_ADDR_CTL: bus 133 |        | PCI 133/0/0  |
80  *                +------------------------------------+        +--------------+
81  *
82  * Note, the PCI buses used by the northbridges here are arbitrary. They do not
83  * reflect the actual values by hardware; however, the bus/device/function (BDF)
84  * of the data fabric accurately models hardware. All of the BDF values are in
85  * hex.
86  *
87  * Starting with the Rome generation of processors (Family 17h Model 30-3Fh),
88  * AMD has multiple northbridges that exist on a given die. All of these
89  * northbridges share the same data fabric and system management network port.
90  * From our perspective this means that some of the northbridge devices will be
91  * redundant and that we will no longer have a 1:1 mapping between the
92  * northbridge and the data fabric devices. Every data fabric will have a
93  * northbridge, but not every northbridge will have a data fabric device mapped.
94  * Because we're always trying to map from a die to a northbridge and not the
95  * reverse, the fact that there are extra northbridge devices hanging around
96  * that we don't know about shouldn't be a problem.
97  *
98  * -------------------------------
99  * Attach and Detach Complications
100  * -------------------------------
101  *
102  * Because we need to map different PCI devices together, this means that we
103  * have multiple dev_info_t structures that we need to manage. Each of these is
104  * independently attached and detached. While this is easily managed for attach,
105  * it is not for detach. Each of these devices is a 'stub'.
106  *
107  * Once a device has been detached it will only come back if we have an active
108  * minor node that will be accessed. This means that if they are detached,
109  * nothing would ever cause them to be reattached. The system also doesn't
110  * provide us a way or any guarantees around making sure that we're attached to
111  * all such devices before we detach. As a result, unfortunately, it's easier to
112  * basically have detach always fail.
113  *
114  * ---------------
115  * Exposed Devices
116  * ---------------
117  *
118  * Rather than try and have all of the different functions that could be
119  * provided by one driver, we instead have created a nexus driver that will
120  * itself try and load children. Children are all pseudo-device drivers that
121  * provide different pieces of functionality that use this.
122  */
123 
124 #include <sys/modctl.h>
125 #include <sys/conf.h>
126 #include <sys/devops.h>
127 #include <sys/ddi.h>
128 #include <sys/sunddi.h>
129 #include <sys/pci.h>
130 #include <sys/sysmacros.h>
131 #include <sys/sunndi.h>
132 #include <sys/x86_archext.h>
133 #include <sys/cpuvar.h>
134 
135 #include "amdzen.h"
136 
137 amdzen_t *amdzen_data;
138 
139 /*
140  * Array of northbridge IDs that we care about.
141  */
142 static const uint16_t amdzen_nb_ids[] = {
143 	/* Family 17h Ryzen, Epyc Models 00h-0fh (Zen uarch) */
144 	0x1450,
145 	/* Family 17h Raven Ridge, Kestrel, Dali Models 10h-2fh (Zen uarch) */
146 	0x15d0,
147 	/* Family 17h Epyc Models 30h-3fh, Matisse 70-7fh (Zen 2 uarch) */
148 	0x1480,
149 	/* Family 17h Renoir Models 60-6fh (Zen 2 uarch) */
150 	0x1630
151 };
152 
153 typedef struct {
154 	char *acd_name;
155 	amdzen_child_t acd_addr;
156 } amdzen_child_data_t;
157 
158 static const amdzen_child_data_t amdzen_children[] = {
159 	{ "smntemp", AMDZEN_C_SMNTEMP },
160 	{ "usmn", AMDZEN_C_USMN },
161 	{ "zen_udf", AMDZEN_C_ZEN_UDF }
162 };
163 
164 static uint32_t
165 amdzen_stub_get32(amdzen_stub_t *stub, off_t reg)
166 {
167 	return (pci_config_get32(stub->azns_cfgspace, reg));
168 }
169 
170 static uint64_t
171 amdzen_stub_get64(amdzen_stub_t *stub, off_t reg)
172 {
173 	return (pci_config_get64(stub->azns_cfgspace, reg));
174 }
175 
176 static void
177 amdzen_stub_put32(amdzen_stub_t *stub, off_t reg, uint32_t val)
178 {
179 	pci_config_put32(stub->azns_cfgspace, reg, val);
180 }
181 
182 /*
183  * Perform a targeted 32-bit indirect read to a specific instance and function.
184  */
185 static uint32_t
186 amdzen_df_read32(amdzen_t *azn, amdzen_df_t *df, uint8_t inst, uint8_t func,
187     uint16_t reg)
188 {
189 	uint32_t val;
190 
191 	VERIFY(MUTEX_HELD(&azn->azn_mutex));
192 	val = AMDZEN_DF_F4_FICAA_TARG_INST | AMDZEN_DF_F4_FICAA_SET_REG(reg) |
193 	    AMDZEN_DF_F4_FICAA_SET_FUNC(func) |
194 	    AMDZEN_DF_F4_FICAA_SET_INST(inst);
195 	amdzen_stub_put32(df->adf_funcs[4], AMDZEN_DF_F4_FICAA, val);
196 	return (amdzen_stub_get32(df->adf_funcs[4], AMDZEN_DF_F4_FICAD_LO));
197 }
198 
199 /*
200  * Perform a targeted 64-bit indirect read to a specific instance and function.
201  */
202 static uint64_t
203 amdzen_df_read64(amdzen_t *azn, amdzen_df_t *df, uint8_t inst, uint8_t func,
204     uint16_t reg)
205 {
206 	uint32_t val;
207 
208 	VERIFY(MUTEX_HELD(&azn->azn_mutex));
209 	val = AMDZEN_DF_F4_FICAA_TARG_INST | AMDZEN_DF_F4_FICAA_SET_REG(reg) |
210 	    AMDZEN_DF_F4_FICAA_SET_FUNC(func) |
211 	    AMDZEN_DF_F4_FICAA_SET_INST(inst) | AMDZEN_DF_F4_FICAA_SET_64B;
212 	amdzen_stub_put32(df->adf_funcs[4], AMDZEN_DF_F4_FICAA, val);
213 	return (amdzen_stub_get64(df->adf_funcs[4], AMDZEN_DF_F4_FICAD_LO));
214 }
215 
216 
217 static uint32_t
218 amdzen_smn_read32(amdzen_t *azn, amdzen_df_t *df, uint32_t reg)
219 {
220 	VERIFY(MUTEX_HELD(&azn->azn_mutex));
221 	amdzen_stub_put32(df->adf_nb, AMDZEN_NB_SMN_ADDR, reg);
222 	return (amdzen_stub_get32(df->adf_nb, AMDZEN_NB_SMN_DATA));
223 }
224 
225 static amdzen_df_t *
226 amdzen_df_find(amdzen_t *azn, uint_t dfno)
227 {
228 	uint_t i;
229 
230 	ASSERT(MUTEX_HELD(&azn->azn_mutex));
231 	if (dfno >= azn->azn_ndfs) {
232 		return (NULL);
233 	}
234 
235 	for (i = 0; i < azn->azn_ndfs; i++) {
236 		amdzen_df_t *df = &azn->azn_dfs[i];
237 		if ((df->adf_flags & AMDZEN_DF_F_VALID) == 0) {
238 			continue;
239 		}
240 
241 		if (dfno == 0) {
242 			return (df);
243 		}
244 		dfno--;
245 	}
246 
247 	return (NULL);
248 }
249 
250 /*
251  * Client functions that are used by nexus children.
252  */
253 int
254 amdzen_c_smn_read32(uint_t dfno, uint32_t reg, uint32_t *valp)
255 {
256 	amdzen_df_t *df;
257 	amdzen_t *azn = amdzen_data;
258 
259 	mutex_enter(&azn->azn_mutex);
260 	df = amdzen_df_find(azn, dfno);
261 	if (df == NULL) {
262 		mutex_exit(&azn->azn_mutex);
263 		return (ENOENT);
264 	}
265 
266 	if ((df->adf_flags & AMDZEN_DF_F_FOUND_NB) == 0) {
267 		mutex_exit(&azn->azn_mutex);
268 		return (ENXIO);
269 	}
270 
271 	*valp = amdzen_smn_read32(azn, df, reg);
272 	mutex_exit(&azn->azn_mutex);
273 	return (0);
274 }
275 
276 uint_t
277 amdzen_c_df_count(void)
278 {
279 	uint_t ret;
280 	amdzen_t *azn = amdzen_data;
281 
282 	mutex_enter(&azn->azn_mutex);
283 	ret = azn->azn_ndfs;
284 	mutex_exit(&azn->azn_mutex);
285 	return (ret);
286 }
287 
288 int
289 amdzen_c_df_read32(uint_t dfno, uint8_t inst, uint8_t func,
290     uint16_t reg, uint32_t *valp)
291 {
292 	amdzen_df_t *df;
293 	amdzen_t *azn = amdzen_data;
294 
295 	mutex_enter(&azn->azn_mutex);
296 	df = amdzen_df_find(azn, dfno);
297 	if (df == NULL) {
298 		mutex_exit(&azn->azn_mutex);
299 		return (ENOENT);
300 	}
301 
302 	*valp = amdzen_df_read32(azn, df, inst, func, reg);
303 	mutex_exit(&azn->azn_mutex);
304 
305 	return (0);
306 }
307 
308 int
309 amdzen_c_df_read64(uint_t dfno, uint8_t inst, uint8_t func,
310     uint16_t reg, uint64_t *valp)
311 {
312 	amdzen_df_t *df;
313 	amdzen_t *azn = amdzen_data;
314 
315 	mutex_enter(&azn->azn_mutex);
316 	df = amdzen_df_find(azn, dfno);
317 	if (df == NULL) {
318 		mutex_exit(&azn->azn_mutex);
319 		return (ENOENT);
320 	}
321 
322 	*valp = amdzen_df_read64(azn, df, inst, func, reg);
323 	mutex_exit(&azn->azn_mutex);
324 
325 	return (0);
326 }
327 
328 static boolean_t
329 amdzen_create_child(amdzen_t *azn, const amdzen_child_data_t *acd)
330 {
331 	int ret;
332 	dev_info_t *child;
333 
334 	if (ndi_devi_alloc(azn->azn_dip, acd->acd_name,
335 	    (pnode_t)DEVI_SID_NODEID, &child) != NDI_SUCCESS) {
336 		dev_err(azn->azn_dip, CE_WARN, "!failed to allocate child "
337 		    "dip for %s", acd->acd_name);
338 		return (B_FALSE);
339 	}
340 
341 	ddi_set_parent_data(child, (void *)acd);
342 	if ((ret = ndi_devi_online(child, 0)) != NDI_SUCCESS) {
343 		dev_err(azn->azn_dip, CE_WARN, "!failed to online child "
344 		    "dip %s: %d", acd->acd_name, ret);
345 		return (B_FALSE);
346 	}
347 
348 	return (B_TRUE);
349 }
350 
351 static boolean_t
352 amdzen_map_dfs(amdzen_t *azn)
353 {
354 	amdzen_stub_t *stub;
355 
356 	ASSERT(MUTEX_HELD(&azn->azn_mutex));
357 
358 	for (stub = list_head(&azn->azn_df_stubs); stub != NULL;
359 	    stub = list_next(&azn->azn_df_stubs, stub)) {
360 		amdzen_df_t *df;
361 		uint_t dfno;
362 
363 		dfno = stub->azns_dev - AMDZEN_DF_FIRST_DEVICE;
364 		if (dfno > AMDZEN_MAX_DFS) {
365 			dev_err(stub->azns_dip, CE_WARN, "encountered df "
366 			    "device with illegal DF PCI b/d/f: 0x%x/%x/%x",
367 			    stub->azns_bus, stub->azns_dev, stub->azns_func);
368 			goto err;
369 		}
370 
371 		df = &azn->azn_dfs[dfno];
372 
373 		if (stub->azns_func >= AMDZEN_MAX_DF_FUNCS) {
374 			dev_err(stub->azns_dip, CE_WARN, "encountered df "
375 			    "device with illegal DF PCI b/d/f: 0x%x/%x/%x",
376 			    stub->azns_bus, stub->azns_dev, stub->azns_func);
377 			goto err;
378 		}
379 
380 		if (df->adf_funcs[stub->azns_func] != NULL) {
381 			dev_err(stub->azns_dip, CE_WARN, "encountered "
382 			    "duplicate df device with DF PCI b/d/f: 0x%x/%x/%x",
383 			    stub->azns_bus, stub->azns_dev, stub->azns_func);
384 			goto err;
385 		}
386 		df->adf_funcs[stub->azns_func] = stub;
387 	}
388 
389 	return (B_TRUE);
390 
391 err:
392 	azn->azn_flags |= AMDZEN_F_DEVICE_ERROR;
393 	return (B_FALSE);
394 }
395 
396 static boolean_t
397 amdzen_check_dfs(amdzen_t *azn)
398 {
399 	uint_t i;
400 	boolean_t ret = B_TRUE;
401 
402 	for (i = 0; i < AMDZEN_MAX_DFS; i++) {
403 		amdzen_df_t *df = &azn->azn_dfs[i];
404 		uint_t count = 0;
405 
406 		/*
407 		 * We require all platforms to have DFs functions 0-6. Not all
408 		 * platforms have DF function 7.
409 		 */
410 		for (uint_t func = 0; func < AMDZEN_MAX_DF_FUNCS - 1; func++) {
411 			if (df->adf_funcs[func] != NULL) {
412 				count++;
413 			}
414 		}
415 
416 		if (count == 0)
417 			continue;
418 
419 		if (count != 7) {
420 			ret = B_FALSE;
421 			dev_err(azn->azn_dip, CE_WARN, "df %u devices "
422 			    "incomplete", i);
423 		} else {
424 			df->adf_flags |= AMDZEN_DF_F_VALID;
425 			azn->azn_ndfs++;
426 		}
427 	}
428 
429 	return (ret);
430 }
431 
432 static const uint8_t amdzen_df_rome_ids[0x2b] = {
433 	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20, 21, 22, 23,
434 	24, 25, 26, 27, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
435 	44, 45, 46, 47, 48
436 };
437 
438 /*
439  * Initialize our knowledge about a given series of nodes on the data fabric.
440  */
441 static void
442 amdzen_setup_df(amdzen_t *azn, amdzen_df_t *df)
443 {
444 	uint_t i;
445 	uint32_t val;
446 
447 	val = amdzen_stub_get32(df->adf_funcs[0], AMDZEN_DF_F0_CFG_ADDR_CTL);
448 	df->adf_nb_busno = AMDZEN_DF_F0_CFG_ADDR_CTL_BUS_NUM(val);
449 	val = amdzen_stub_get32(df->adf_funcs[0], AMDZEN_DF_F0_FBICNT);
450 	df->adf_nents = AMDZEN_DF_F0_FBICNT_COUNT(val);
451 	if (df->adf_nents == 0)
452 		return;
453 	df->adf_ents = kmem_zalloc(sizeof (amdzen_df_ent_t) * df->adf_nents,
454 	    KM_SLEEP);
455 
456 	for (i = 0; i < df->adf_nents; i++) {
457 		amdzen_df_ent_t *dfe = &df->adf_ents[i];
458 		uint8_t inst = i;
459 
460 		/*
461 		 * Unfortunately, Rome uses a discontinuous instance ID pattern
462 		 * while everything else we can find uses a contiguous instance
463 		 * ID pattern.  This means that for Rome, we need to adjust the
464 		 * indexes that we iterate over, though the total number of
465 		 * entries is right.
466 		 */
467 		if (df->adf_funcs[0]->azns_did == 0x1490) {
468 			if (inst > ARRAY_SIZE(amdzen_df_rome_ids)) {
469 				dev_err(azn->azn_dip, CE_WARN, "Rome family "
470 				    "processor reported more ids than the PPR, "
471 				    "resting %u to instance zero", inst);
472 				inst = 0;
473 			} else {
474 				inst = amdzen_df_rome_ids[inst];
475 			}
476 		}
477 
478 		dfe->adfe_drvid = inst;
479 		dfe->adfe_info0 = amdzen_df_read32(azn, df, inst, 0,
480 		    AMDZEN_DF_F0_FBIINFO0);
481 		dfe->adfe_info1 = amdzen_df_read32(azn, df, inst, 0,
482 		    AMDZEN_DF_F0_FBIINFO1);
483 		dfe->adfe_info2 = amdzen_df_read32(azn, df, inst, 0,
484 		    AMDZEN_DF_F0_FBIINFO2);
485 		dfe->adfe_info3 = amdzen_df_read32(azn, df, inst, 0,
486 		    AMDZEN_DF_F0_FBIINFO3);
487 		dfe->adfe_syscfg = amdzen_df_read32(azn, df, inst, 1,
488 		    AMDZEN_DF_F1_SYSCFG);
489 		dfe->adfe_mask0 = amdzen_df_read32(azn, df, inst, 1,
490 		    AMDZEN_DF_F1_FIDMASK0);
491 		dfe->adfe_mask1 = amdzen_df_read32(azn, df, inst, 1,
492 		    AMDZEN_DF_F1_FIDMASK1);
493 
494 		dfe->adfe_type = AMDZEN_DF_F0_FBIINFO0_TYPE(dfe->adfe_info0);
495 		dfe->adfe_sdp_width =
496 		    AMDZEN_DF_F0_FBIINFO0_SDP_WIDTH(dfe->adfe_info0);
497 		if (AMDZEN_DF_F0_FBIINFO0_ENABLED(dfe->adfe_info0)) {
498 			dfe->adfe_flags |= AMDZEN_DFE_F_ENABLED;
499 		}
500 		dfe->adfe_fti_width =
501 		    AMDZEN_DF_F0_FBIINFO0_FTI_WIDTH(dfe->adfe_info0);
502 		dfe->adfe_sdp_count =
503 		    AMDZEN_DF_F0_FBIINFO0_SDP_PCOUNT(dfe->adfe_info0);
504 		dfe->adfe_fti_count =
505 		    AMDZEN_DF_F0_FBIINFO0_FTI_PCOUNT(dfe->adfe_info0);
506 		if (AMDZEN_DF_F0_FBIINFO0_HAS_MCA(dfe->adfe_info0)) {
507 			dfe->adfe_flags |= AMDZEN_DFE_F_MCA;
508 		}
509 		dfe->adfe_subtype =
510 		    AMDZEN_DF_F0_FBIINFO0_SUBTYPE(dfe->adfe_info0);
511 
512 		dfe->adfe_inst_id =
513 		    AMDZEN_DF_F0_FBIINFO3_INSTID(dfe->adfe_info3);
514 		dfe->adfe_fabric_id =
515 		    AMDZEN_DF_F0_FBIINFO3_FABID(dfe->adfe_info3);
516 	}
517 
518 	df->adf_syscfg = amdzen_stub_get32(df->adf_funcs[1],
519 	    AMDZEN_DF_F1_SYSCFG);
520 	df->adf_nodeid = AMDZEN_DF_F1_SYSCFG_NODEID(df->adf_syscfg);
521 	df->adf_mask0 = amdzen_stub_get32(df->adf_funcs[1],
522 	    AMDZEN_DF_F1_FIDMASK0);
523 	df->adf_mask1 = amdzen_stub_get32(df->adf_funcs[1],
524 	    AMDZEN_DF_F1_FIDMASK1);
525 }
526 
527 static void
528 amdzen_find_nb(amdzen_t *azn, amdzen_df_t *df)
529 {
530 	amdzen_stub_t *stub;
531 
532 	for (stub = list_head(&azn->azn_nb_stubs); stub != NULL;
533 	    stub = list_next(&azn->azn_nb_stubs, stub)) {
534 		if (stub->azns_bus == df->adf_nb_busno) {
535 			df->adf_flags |= AMDZEN_DF_F_FOUND_NB;
536 			df->adf_nb = stub;
537 			return;
538 		}
539 	}
540 }
541 
542 static void
543 amdzen_nexus_init(void *arg)
544 {
545 	uint_t i;
546 	amdzen_t *azn = arg;
547 
548 	/*
549 	 * First go through all of the stubs and assign the DF entries.
550 	 */
551 	mutex_enter(&azn->azn_mutex);
552 	if (!amdzen_map_dfs(azn) || !amdzen_check_dfs(azn)) {
553 		azn->azn_flags |= AMDZEN_F_MAP_ERROR;
554 		goto done;
555 	}
556 
557 	for (i = 0; i < AMDZEN_MAX_DFS; i++) {
558 		amdzen_df_t *df = &azn->azn_dfs[i];
559 
560 		if ((df->adf_flags & AMDZEN_DF_F_VALID) == 0)
561 			continue;
562 		amdzen_setup_df(azn, df);
563 		amdzen_find_nb(azn, df);
564 	}
565 
566 	/*
567 	 * Not all children may be installed. As such, we do not treat the
568 	 * failure of a child as fatal to the driver.
569 	 */
570 	mutex_exit(&azn->azn_mutex);
571 	for (i = 0; i < ARRAY_SIZE(amdzen_children); i++) {
572 		(void) amdzen_create_child(azn, &amdzen_children[i]);
573 	}
574 	mutex_enter(&azn->azn_mutex);
575 
576 done:
577 	azn->azn_flags &= ~AMDZEN_F_ATTACH_DISPATCHED;
578 	azn->azn_flags |= AMDZEN_F_ATTACH_COMPLETE;
579 	azn->azn_taskqid = TASKQID_INVALID;
580 	cv_broadcast(&azn->azn_cv);
581 	mutex_exit(&azn->azn_mutex);
582 }
583 
584 static int
585 amdzen_stub_scan_cb(dev_info_t *dip, void *arg)
586 {
587 	amdzen_t *azn = arg;
588 	uint16_t vid, did;
589 	int *regs;
590 	uint_t nregs, i;
591 	boolean_t match = B_FALSE;
592 
593 	if (dip == ddi_root_node()) {
594 		return (DDI_WALK_CONTINUE);
595 	}
596 
597 	/*
598 	 * If a node in question is not a pci node, then we have no interest in
599 	 * it as all the stubs that we care about are related to pci devices.
600 	 */
601 	if (strncmp("pci", ddi_get_name(dip), 3) != 0) {
602 		return (DDI_WALK_PRUNECHILD);
603 	}
604 
605 	/*
606 	 * If we can't get a device or vendor ID and prove that this is an AMD
607 	 * part, then we don't care about it.
608 	 */
609 	vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
610 	    "vendor-id", PCI_EINVAL16);
611 	did = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
612 	    "device-id", PCI_EINVAL16);
613 	if (vid == PCI_EINVAL16 || did == PCI_EINVAL16) {
614 		return (DDI_WALK_CONTINUE);
615 	}
616 
617 	if (vid != AMDZEN_PCI_VID_AMD) {
618 		return (DDI_WALK_CONTINUE);
619 	}
620 
621 	for (i = 0; i < ARRAY_SIZE(amdzen_nb_ids); i++) {
622 		if (amdzen_nb_ids[i] == did) {
623 			match = B_TRUE;
624 		}
625 	}
626 
627 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
628 	    "reg", &regs, &nregs) != DDI_PROP_SUCCESS) {
629 		return (DDI_WALK_CONTINUE);
630 	}
631 
632 	if (nregs == 0) {
633 		ddi_prop_free(regs);
634 		return (DDI_WALK_CONTINUE);
635 	}
636 
637 	if (PCI_REG_BUS_G(regs[0]) == AMDZEN_DF_BUSNO &&
638 	    PCI_REG_DEV_G(regs[0]) >= AMDZEN_DF_FIRST_DEVICE) {
639 		match = B_TRUE;
640 	}
641 
642 	ddi_prop_free(regs);
643 	if (match) {
644 		mutex_enter(&azn->azn_mutex);
645 		azn->azn_nscanned++;
646 		mutex_exit(&azn->azn_mutex);
647 	}
648 
649 	return (DDI_WALK_CONTINUE);
650 }
651 
652 static void
653 amdzen_stub_scan(void *arg)
654 {
655 	amdzen_t *azn = arg;
656 
657 	mutex_enter(&azn->azn_mutex);
658 	azn->azn_nscanned = 0;
659 	mutex_exit(&azn->azn_mutex);
660 
661 	ddi_walk_devs(ddi_root_node(), amdzen_stub_scan_cb, azn);
662 
663 	mutex_enter(&azn->azn_mutex);
664 	azn->azn_flags &= ~AMDZEN_F_SCAN_DISPATCHED;
665 	azn->azn_flags |= AMDZEN_F_SCAN_COMPLETE;
666 
667 	if (azn->azn_nscanned == 0) {
668 		azn->azn_flags |= AMDZEN_F_UNSUPPORTED;
669 		azn->azn_taskqid = TASKQID_INVALID;
670 		cv_broadcast(&azn->azn_cv);
671 	} else if (azn->azn_npresent == azn->azn_nscanned) {
672 		azn->azn_flags |= AMDZEN_F_ATTACH_DISPATCHED;
673 		azn->azn_taskqid = taskq_dispatch(system_taskq,
674 		    amdzen_nexus_init, azn, TQ_SLEEP);
675 	}
676 	mutex_exit(&azn->azn_mutex);
677 }
678 
679 /*
680  * Unfortunately we can't really let the stubs detach as we may need them to be
681  * available for client operations. We may be able to improve this if we know
682  * that the actual nexus is going away. However, as long as it's active, we need
683  * all the stubs.
684  */
685 int
686 amdzen_detach_stub(dev_info_t *dip, ddi_detach_cmd_t cmd)
687 {
688 	if (cmd == DDI_SUSPEND) {
689 		return (DDI_SUCCESS);
690 	}
691 
692 	return (DDI_FAILURE);
693 }
694 
695 int
696 amdzen_attach_stub(dev_info_t *dip, ddi_attach_cmd_t cmd)
697 {
698 	int *regs, reg;
699 	uint_t nregs, i;
700 	uint16_t vid, did;
701 	amdzen_stub_t *stub;
702 	amdzen_t *azn = amdzen_data;
703 	boolean_t valid = B_FALSE;
704 	boolean_t nb = B_FALSE;
705 
706 	if (cmd == DDI_RESUME) {
707 		return (DDI_SUCCESS);
708 	} else if (cmd != DDI_ATTACH) {
709 		return (DDI_FAILURE);
710 	}
711 
712 	/*
713 	 * Make sure that the stub that we've been asked to attach is a pci type
714 	 * device. If not, then there is no reason for us to proceed.
715 	 */
716 	if (strncmp("pci", ddi_get_name(dip), 3) != 0) {
717 		dev_err(dip, CE_WARN, "asked to attach a bad AMD Zen nexus "
718 		    "stub: %s", ddi_get_name(dip));
719 		return (DDI_FAILURE);
720 	}
721 	vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
722 	    "vendor-id", PCI_EINVAL16);
723 	did = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
724 	    "device-id", PCI_EINVAL16);
725 	if (vid == PCI_EINVAL16 || did == PCI_EINVAL16) {
726 		dev_err(dip, CE_WARN, "failed to get PCI ID properties");
727 		return (DDI_FAILURE);
728 	}
729 
730 	if (vid != AMDZEN_PCI_VID_AMD) {
731 		dev_err(dip, CE_WARN, "expected AMD vendor ID (0x%x), found "
732 		    "0x%x", AMDZEN_PCI_VID_AMD, vid);
733 		return (DDI_FAILURE);
734 	}
735 
736 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
737 	    "reg", &regs, &nregs) != DDI_PROP_SUCCESS) {
738 		dev_err(dip, CE_WARN, "failed to get 'reg' property");
739 		return (DDI_FAILURE);
740 	}
741 
742 	if (nregs == 0) {
743 		ddi_prop_free(regs);
744 		dev_err(dip, CE_WARN, "missing 'reg' property values");
745 		return (DDI_FAILURE);
746 	}
747 	reg = *regs;
748 	ddi_prop_free(regs);
749 
750 	for (i = 0; i < ARRAY_SIZE(amdzen_nb_ids); i++) {
751 		if (amdzen_nb_ids[i] == did) {
752 			valid = B_TRUE;
753 			nb = B_TRUE;
754 		}
755 	}
756 
757 	if (!valid && PCI_REG_BUS_G(reg) == AMDZEN_DF_BUSNO &&
758 	    PCI_REG_DEV_G(reg) >= AMDZEN_DF_FIRST_DEVICE) {
759 		valid = B_TRUE;
760 		nb = B_FALSE;
761 	}
762 
763 	if (!valid) {
764 		dev_err(dip, CE_WARN, "device %s didn't match the nexus list",
765 		    ddi_get_name(dip));
766 		return (DDI_FAILURE);
767 	}
768 
769 	stub = kmem_alloc(sizeof (amdzen_stub_t), KM_SLEEP);
770 	if (pci_config_setup(dip, &stub->azns_cfgspace) != DDI_SUCCESS) {
771 		dev_err(dip, CE_WARN, "failed to set up config space");
772 		kmem_free(stub, sizeof (amdzen_stub_t));
773 		return (DDI_FAILURE);
774 	}
775 
776 	stub->azns_dip = dip;
777 	stub->azns_vid = vid;
778 	stub->azns_did = did;
779 	stub->azns_bus = PCI_REG_BUS_G(reg);
780 	stub->azns_dev = PCI_REG_DEV_G(reg);
781 	stub->azns_func = PCI_REG_FUNC_G(reg);
782 	ddi_set_driver_private(dip, stub);
783 
784 	mutex_enter(&azn->azn_mutex);
785 	azn->azn_npresent++;
786 	if (nb) {
787 		list_insert_tail(&azn->azn_nb_stubs, stub);
788 	} else {
789 		list_insert_tail(&azn->azn_df_stubs, stub);
790 	}
791 
792 	if ((azn->azn_flags & AMDZEN_F_TASKQ_MASK) == AMDZEN_F_SCAN_COMPLETE &&
793 	    azn->azn_nscanned == azn->azn_npresent) {
794 		azn->azn_flags |= AMDZEN_F_ATTACH_DISPATCHED;
795 		azn->azn_taskqid = taskq_dispatch(system_taskq,
796 		    amdzen_nexus_init, azn, TQ_SLEEP);
797 	}
798 	mutex_exit(&azn->azn_mutex);
799 
800 	return (DDI_SUCCESS);
801 }
802 
803 static int
804 amdzen_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
805     void *arg, void *result)
806 {
807 	char buf[32];
808 	dev_info_t *child;
809 	const amdzen_child_data_t *acd;
810 
811 	switch (ctlop) {
812 	case DDI_CTLOPS_REPORTDEV:
813 		if (rdip == NULL) {
814 			return (DDI_FAILURE);
815 		}
816 		cmn_err(CE_CONT, "amdzen nexus: %s@%s, %s%d\n",
817 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
818 		    ddi_driver_name(rdip), ddi_get_instance(rdip));
819 		break;
820 	case DDI_CTLOPS_INITCHILD:
821 		child = arg;
822 		if (child == NULL) {
823 			dev_err(dip, CE_WARN, "!no child passed for "
824 			    "DDI_CTLOPS_INITCHILD");
825 		}
826 
827 		acd = ddi_get_parent_data(child);
828 		if (acd == NULL) {
829 			dev_err(dip, CE_WARN, "!missing child parent data");
830 			return (DDI_FAILURE);
831 		}
832 
833 		if (snprintf(buf, sizeof (buf), "%d", acd->acd_addr) >=
834 		    sizeof (buf)) {
835 			dev_err(dip, CE_WARN, "!failed to construct device "
836 			    "addr due to overflow");
837 			return (DDI_FAILURE);
838 		}
839 
840 		ddi_set_name_addr(child, buf);
841 		break;
842 	case DDI_CTLOPS_UNINITCHILD:
843 		child = arg;
844 		if (child == NULL) {
845 			dev_err(dip, CE_WARN, "!no child passed for "
846 			    "DDI_CTLOPS_UNINITCHILD");
847 		}
848 
849 		ddi_set_name_addr(child, NULL);
850 		break;
851 	default:
852 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
853 	}
854 	return (DDI_SUCCESS);
855 }
856 
857 static int
858 amdzen_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
859 {
860 	amdzen_t *azn = amdzen_data;
861 
862 	if (cmd == DDI_RESUME) {
863 		return (DDI_SUCCESS);
864 	} else if (cmd != DDI_ATTACH) {
865 		return (DDI_FAILURE);
866 	}
867 
868 	mutex_enter(&azn->azn_mutex);
869 	if (azn->azn_dip != NULL) {
870 		dev_err(dip, CE_WARN, "driver is already attached!");
871 		mutex_exit(&azn->azn_mutex);
872 		return (DDI_FAILURE);
873 	}
874 
875 	azn->azn_dip = dip;
876 	azn->azn_taskqid = taskq_dispatch(system_taskq, amdzen_stub_scan,
877 	    azn, TQ_SLEEP);
878 	azn->azn_flags |= AMDZEN_F_SCAN_DISPATCHED;
879 	mutex_exit(&azn->azn_mutex);
880 
881 	return (DDI_SUCCESS);
882 }
883 
884 static int
885 amdzen_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
886 {
887 	amdzen_t *azn = amdzen_data;
888 
889 	if (cmd == DDI_SUSPEND) {
890 		return (DDI_SUCCESS);
891 	} else if (cmd != DDI_DETACH) {
892 		return (DDI_FAILURE);
893 	}
894 
895 	mutex_enter(&azn->azn_mutex);
896 	while (azn->azn_taskqid != TASKQID_INVALID) {
897 		cv_wait(&azn->azn_cv, &azn->azn_mutex);
898 	}
899 
900 	/*
901 	 * If we've attached any stub drivers, e.g. this platform is important
902 	 * for us, then we fail detach.
903 	 */
904 	if (!list_is_empty(&azn->azn_df_stubs) ||
905 	    !list_is_empty(&azn->azn_nb_stubs)) {
906 		mutex_exit(&azn->azn_mutex);
907 		return (DDI_FAILURE);
908 	}
909 
910 	azn->azn_dip = NULL;
911 	mutex_exit(&azn->azn_mutex);
912 
913 	return (DDI_SUCCESS);
914 }
915 
916 static void
917 amdzen_free(void)
918 {
919 	if (amdzen_data == NULL) {
920 		return;
921 	}
922 
923 	VERIFY(list_is_empty(&amdzen_data->azn_df_stubs));
924 	list_destroy(&amdzen_data->azn_df_stubs);
925 	VERIFY(list_is_empty(&amdzen_data->azn_nb_stubs));
926 	list_destroy(&amdzen_data->azn_nb_stubs);
927 	cv_destroy(&amdzen_data->azn_cv);
928 	mutex_destroy(&amdzen_data->azn_mutex);
929 	kmem_free(amdzen_data, sizeof (amdzen_t));
930 	amdzen_data = NULL;
931 }
932 
933 static void
934 amdzen_alloc(void)
935 {
936 	amdzen_data = kmem_zalloc(sizeof (amdzen_t), KM_SLEEP);
937 	mutex_init(&amdzen_data->azn_mutex, NULL, MUTEX_DRIVER, NULL);
938 	list_create(&amdzen_data->azn_df_stubs, sizeof (amdzen_stub_t),
939 	    offsetof(amdzen_stub_t, azns_link));
940 	list_create(&amdzen_data->azn_nb_stubs, sizeof (amdzen_stub_t),
941 	    offsetof(amdzen_stub_t, azns_link));
942 	cv_init(&amdzen_data->azn_cv, NULL, CV_DRIVER, NULL);
943 }
944 
945 struct bus_ops amdzen_bus_ops = {
946 	.busops_rev = BUSO_REV,
947 	.bus_map = nullbusmap,
948 	.bus_dma_map = ddi_no_dma_map,
949 	.bus_dma_allochdl = ddi_no_dma_allochdl,
950 	.bus_dma_freehdl = ddi_no_dma_freehdl,
951 	.bus_dma_bindhdl = ddi_no_dma_bindhdl,
952 	.bus_dma_unbindhdl = ddi_no_dma_unbindhdl,
953 	.bus_dma_flush = ddi_no_dma_flush,
954 	.bus_dma_win = ddi_no_dma_win,
955 	.bus_dma_ctl = ddi_no_dma_mctl,
956 	.bus_prop_op = ddi_bus_prop_op,
957 	.bus_ctl = amdzen_bus_ctl
958 };
959 
960 static struct dev_ops amdzen_dev_ops = {
961 	.devo_rev = DEVO_REV,
962 	.devo_refcnt = 0,
963 	.devo_getinfo = nodev,
964 	.devo_identify = nulldev,
965 	.devo_probe = nulldev,
966 	.devo_attach = amdzen_attach,
967 	.devo_detach = amdzen_detach,
968 	.devo_reset = nodev,
969 	.devo_quiesce = ddi_quiesce_not_needed,
970 	.devo_bus_ops = &amdzen_bus_ops
971 };
972 
973 static struct modldrv amdzen_modldrv = {
974 	.drv_modops = &mod_driverops,
975 	.drv_linkinfo = "AMD Zen Nexus Driver",
976 	.drv_dev_ops = &amdzen_dev_ops
977 };
978 
979 static struct modlinkage amdzen_modlinkage = {
980 	.ml_rev = MODREV_1,
981 	.ml_linkage = { &amdzen_modldrv, NULL }
982 };
983 
984 int
985 _init(void)
986 {
987 	int ret;
988 
989 	if (cpuid_getvendor(CPU) != X86_VENDOR_AMD) {
990 		return (ENOTSUP);
991 	}
992 
993 	if ((ret = mod_install(&amdzen_modlinkage)) == 0) {
994 		amdzen_alloc();
995 	}
996 
997 	return (ret);
998 }
999 
1000 int
1001 _info(struct modinfo *modinfop)
1002 {
1003 	return (mod_info(&amdzen_modlinkage, modinfop));
1004 }
1005 
1006 int
1007 _fini(void)
1008 {
1009 	int ret;
1010 
1011 	if ((ret = mod_remove(&amdzen_modlinkage)) == 0) {
1012 		amdzen_free();
1013 	}
1014 
1015 	return (ret);
1016 }
1017