1f2dbfd3Robert Mustacchi/*
2f2dbfd3Robert Mustacchi * This file and its contents are supplied under the terms of the
3f2dbfd3Robert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4f2dbfd3Robert Mustacchi * You may only use this file in accordance with the terms of version
5f2dbfd3Robert Mustacchi * 1.0 of the CDDL.
6f2dbfd3Robert Mustacchi *
7f2dbfd3Robert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8f2dbfd3Robert Mustacchi * source.  A copy of the CDDL is also available via the Internet at
9f2dbfd3Robert Mustacchi * http://www.illumos.org/license/CDDL.
10f2dbfd3Robert Mustacchi */
11f2dbfd3Robert Mustacchi
12f2dbfd3Robert Mustacchi/*
13f2dbfd3Robert Mustacchi * Copyright 2019, Joyent, Inc.
14f2dbfd3Robert Mustacchi */
15f2dbfd3Robert Mustacchi
16f2dbfd3Robert Mustacchi/*
17f2dbfd3Robert Mustacchi * AMD Family 17 Northbridge and Data Fabric Driver
18f2dbfd3Robert Mustacchi *
19f2dbfd3Robert Mustacchi * This driver attaches to the AMD Family 17h northbridge and data fabric bus.
20f2dbfd3Robert Mustacchi * Each Zeppelin die ('processor node' in cpuid.c parlance) has its own
21f2dbfd3Robert Mustacchi * northbridge and access to the data fabric bus. The northbridge and data
22f2dbfd3Robert Mustacchi * fabric both provide access to various features such as:
23f2dbfd3Robert Mustacchi *
24f2dbfd3Robert Mustacchi *  - The System Management Network (SMN)
25f2dbfd3Robert Mustacchi *  - Data Fabric via Fabric Indirect Config Access (FICAA)
26f2dbfd3Robert Mustacchi *
27f2dbfd3Robert Mustacchi * These are required to access things such as temperature sensors or memory
28f2dbfd3Robert Mustacchi * controller configuration registers.
29f2dbfd3Robert Mustacchi *
30f2dbfd3Robert Mustacchi * In AMD Family 17h systems, the 'northbridge' is an ASIC that is part of the
31f2dbfd3Robert Mustacchi * package that contains many I/O capabilities related to things like PCI
32f2dbfd3Robert Mustacchi * express, etc. The 'data fabric' is the means by which different components
33f2dbfd3Robert Mustacchi * both inside the socket and multiple sockets are connected together. Both the
34f2dbfd3Robert Mustacchi * northbridge and the data fabric have dedicated PCI devices which the
35f2dbfd3Robert Mustacchi * operating system can use to interact with them.
36f2dbfd3Robert Mustacchi *
37f2dbfd3Robert Mustacchi * ------------------------
38f2dbfd3Robert Mustacchi * Mapping Devices Together
39f2dbfd3Robert Mustacchi * ------------------------
40f2dbfd3Robert Mustacchi *
41f2dbfd3Robert Mustacchi * The operating system needs to expose things like temperature sensors and DRAM
42f2dbfd3Robert Mustacchi * configuration registers in terms that are meaningful to the system such as
43f2dbfd3Robert Mustacchi * logical CPUs, cores, etc. This driver attaches to the PCI IDs that represent
44f2dbfd3Robert Mustacchi * the northbridge and data fabric; however, there are multiple PCI devices (one
45f2dbfd3Robert Mustacchi * per die) that exist. This driver does manage to map all of these three things
46f2dbfd3Robert Mustacchi * together; however, it requires some acrobatics. Unfortunately, there's no
47f2dbfd3Robert Mustacchi * direct way to map a northbridge to its corresponding die. However, we can map
48f2dbfd3Robert Mustacchi * a CPU die to a data fabric PCI device and a data fabric PCI device to a
49f2dbfd3Robert Mustacchi * corresponding northbridge PCI device.
50f2dbfd3Robert Mustacchi *
51f2dbfd3Robert Mustacchi * In current Zen based products, there is a direct mapping between processor
52f2dbfd3Robert Mustacchi * nodes and a data fabric PCI device. All of the devices are on PCI Bus 0 and
53f2dbfd3Robert Mustacchi * start from Device 0x18. Device 0x18 maps to processor node 0, 0x19 to
54f2dbfd3Robert Mustacchi * processor node 1, etc. This means that to map a logical CPU to a data fabric
55f2dbfd3Robert Mustacchi * device, we take its processor node id, add it to 0x18 and find the PCI device
56f2dbfd3Robert Mustacchi * that is on bus 0, device 0x18. As each data fabric device is attached based
57f2dbfd3Robert Mustacchi * on its PCI ID, we add it to the global list, amd_nbdf_dfs that is in the
58f2dbfd3Robert Mustacchi * amd_f17nbdf_t structure.
59f2dbfd3Robert Mustacchi *
60f2dbfd3Robert Mustacchi * The northbridge PCI device has a defined device and function, but the PCI bus
61f2dbfd3Robert Mustacchi * that it's on can vary. Each die has its own series of PCI buses that are
62f2dbfd3Robert Mustacchi * assigned to it and the northbridge PCI device is on the first of die-specific
63f2dbfd3Robert Mustacchi * PCI bus for each die. This also means that the northbridge will not show up
64f2dbfd3Robert Mustacchi * on PCI bus 0, which is the PCI bus that all of the data fabric devices are
65f2dbfd3Robert Mustacchi * on. While conventionally the northbridge with the lowest PCI bus value
66f2dbfd3Robert Mustacchi * would correspond to processor node zero, hardware does not guarantee that at
67f2dbfd3Robert Mustacchi * all. Because we don't want to be at the mercy of firmware, we don't rely on
68f2dbfd3Robert Mustacchi * this ordering, even though we have yet to find a system that deviates from
69f2dbfd3Robert Mustacchi * this scheme.
70f2dbfd3Robert Mustacchi *
71f2dbfd3Robert Mustacchi * One of the registers in the data fabric device's function 0
72f2dbfd3Robert Mustacchi * (AMDF17_DF_CFG_ADDR_CTL), happens to have the first PCI bus that is
73f2dbfd3Robert Mustacchi * associated with the processor node. This means, that we can map a data fabric
74f2dbfd3Robert Mustacchi * device to a northbridge by finding the northbridge whose PCI bus matches the
75f2dbfd3Robert Mustacchi * value in the corresponding data fabric's AMDF17_DF_CFG_ADDR_CTL.
76f2dbfd3Robert Mustacchi *
77f2dbfd3Robert Mustacchi * This means that we can map a northbridge to a data fabric device and a data
78f2dbfd3Robert Mustacchi * fabric device to a die. Because these are 1:1 mappings, there is a transitive
79f2dbfd3Robert Mustacchi * relationship and therefore we know which northbridge is associated with which
80f2dbfd3Robert Mustacchi * processor die. This is summarized in the following image:
81f2dbfd3Robert Mustacchi *
82f2dbfd3Robert Mustacchi *  +-------+      +----------------------------+         +--------------+
83f2dbfd3Robert Mustacchi *  | Die 0 | ---> | Data Fabric PCI BDF 0/18/0 |-------> | Northbridge  |
84f2dbfd3Robert Mustacchi *  +-------+      | AMDF17_DF_CFG_ADDR: bus 10 |         | PCI  10/0/0  |
85f2dbfd3Robert Mustacchi *     ...         +----------------------------+         +--------------+
86f2dbfd3Robert Mustacchi *  +-------+      +------------------------------+         +--------------+
87f2dbfd3Robert Mustacchi *  | Die n | ---> | Data Fabric PCI BDF 0/18+n/0 |-------> | Northbridge  |
88f2dbfd3Robert Mustacchi *  +-------+      | AMDF17_DF_CFG_ADDR: bus 133  |         | PCI 133/0/0  |
89f2dbfd3Robert Mustacchi *                 +------------------------------+         +--------------+
90f2dbfd3Robert Mustacchi *
91f2dbfd3Robert Mustacchi * Note, the PCI buses used by the northbridges here are arbitrary. They do not
92f2dbfd3Robert Mustacchi * reflect the actual values by hardware; however, the bus/device/function (BDF)
93f2dbfd3Robert Mustacchi * of the data fabric accurately models hardware. All of the BDF values are in
94f2dbfd3Robert Mustacchi * hex.
95f2dbfd3Robert Mustacchi *
96f2dbfd3Robert Mustacchi * -------------------------------
97f2dbfd3Robert Mustacchi * Attach and Detach Complications
98f2dbfd3Robert Mustacchi * -------------------------------
99f2dbfd3Robert Mustacchi *
100f2dbfd3Robert Mustacchi * Because we need to map different PCI devices together, this means that we
101f2dbfd3Robert Mustacchi * have multiple dev_info_t structures that we need to manage. Each of these is
102f2dbfd3Robert Mustacchi * independently attached and detached. While this is easily managed for attach,
103f2dbfd3Robert Mustacchi * it is not for detach.
104f2dbfd3Robert Mustacchi *
105f2dbfd3Robert Mustacchi * Once a device has been detached it will only come back if we have an active
106f2dbfd3Robert Mustacchi * minor node that will be accessed. While we have minor nodes associated with
107f2dbfd3Robert Mustacchi * the northbridges, we don't with the data fabric devices. This means that if
108f2dbfd3Robert Mustacchi * they are detached, nothing would ever cause them to be reattached. The system
109f2dbfd3Robert Mustacchi * also doesn't provide us a way or any guarantees around making sure that we're
110f2dbfd3Robert Mustacchi * attached to all such devices before we detach. As a result, unfortunately,
111f2dbfd3Robert Mustacchi * it's easier to basically have detach always fail.
112f2dbfd3Robert Mustacchi *
113f2dbfd3Robert Mustacchi * To deal with both development and if issues arise in the field, there is a
114f2dbfd3Robert Mustacchi * knob, amdf17df_allow_detach, which if set to a non-zero value, will allow
115f2dbfd3Robert Mustacchi * instances to detach.
116f2dbfd3Robert Mustacchi *
117f2dbfd3Robert Mustacchi * ---------------
118f2dbfd3Robert Mustacchi * Exposed Devices
119f2dbfd3Robert Mustacchi * ---------------
120f2dbfd3Robert Mustacchi *
121f2dbfd3Robert Mustacchi * Currently we expose a single set of character devices which represent
122f2dbfd3Robert Mustacchi * temperature sensors for this family of processors. Because temperature
123f2dbfd3Robert Mustacchi * sensors exist on a per-processor node basis, we create a single minor node
124f2dbfd3Robert Mustacchi * for each one. Because our naming matches the cpuid naming, FMA can match that
125f2dbfd3Robert Mustacchi * up to logical CPUs and take care of matching the sensors appropriately. We
126f2dbfd3Robert Mustacchi * internally rate limit the sensor updates to 100ms, which is controlled by the
127f2dbfd3Robert Mustacchi * global amdf17nbdf_cache_ms.
128f2dbfd3Robert Mustacchi */
129f2dbfd3Robert Mustacchi
130f2dbfd3Robert Mustacchi#include <sys/modctl.h>
131f2dbfd3Robert Mustacchi#include <sys/conf.h>
132f2dbfd3Robert Mustacchi#include <sys/devops.h>
133f2dbfd3Robert Mustacchi#include <sys/types.h>
134f2dbfd3Robert Mustacchi#include <sys/file.h>
135f2dbfd3Robert Mustacchi#include <sys/open.h>
136f2dbfd3Robert Mustacchi#include <sys/cred.h>
137f2dbfd3Robert Mustacchi#include <sys/ddi.h>
138f2dbfd3Robert Mustacchi#include <sys/sunddi.h>
139f2dbfd3Robert Mustacchi#include <sys/cmn_err.h>
140f2dbfd3Robert Mustacchi#include <sys/list.h>
141f2dbfd3Robert Mustacchi#include <sys/pci.h>
142f2dbfd3Robert Mustacchi#include <sys/stddef.h>
143f2dbfd3Robert Mustacchi#include <sys/stat.h>
144f2dbfd3Robert Mustacchi#include <sys/x86_archext.h>
145f2dbfd3Robert Mustacchi#include <sys/cpuvar.h>
146f2dbfd3Robert Mustacchi#include <sys/sensors.h>
147f2dbfd3Robert Mustacchi
148f2dbfd3Robert Mustacchi/*
149f2dbfd3Robert Mustacchi * The range of minors that we'll allow.
150f2dbfd3Robert Mustacchi */
151f2dbfd3Robert Mustacchi#define	AMDF17_MINOR_LOW	1
152f2dbfd3Robert Mustacchi#define	AMDF17_MINOR_HIGH	INT32_MAX
153f2dbfd3Robert Mustacchi
154f2dbfd3Robert Mustacchi/*
155f2dbfd3Robert Mustacchi * This is the value of the first PCI data fabric device that globally exists.
156f2dbfd3Robert Mustacchi * It always maps to AMD's first nodeid (what we call cpi_procnodeid).
157f2dbfd3Robert Mustacchi */
158f2dbfd3Robert Mustacchi#define	AMDF17_DF_FIRST_DEVICE	0x18
159f2dbfd3Robert Mustacchi
160f2dbfd3Robert Mustacchi/*
161f2dbfd3Robert Mustacchi * The data fabric devices are defined to always be on PCI bus zero.
162f2dbfd3Robert Mustacchi */
163f2dbfd3Robert Mustacchi#define	AMDF17_DF_BUSNO		0x00
164f2dbfd3Robert Mustacchi
165f2dbfd3Robert Mustacchi/*
166f2dbfd3Robert Mustacchi * This register contains the BUS A of the the processor node that corresponds
167f2dbfd3Robert Mustacchi * to the data fabric device.
168f2dbfd3Robert Mustacchi */
169f2dbfd3Robert Mustacchi#define	AMDF17_DF_CFG_ADDR_CTL		0x84
170f2dbfd3Robert Mustacchi#define	AMDF17_DF_CFG_ADDR_CTL_MASK	0xff
171f2dbfd3Robert Mustacchi
172f2dbfd3Robert Mustacchi/*
173f2dbfd3Robert Mustacchi * Northbridge registers that are related to accessing the SMN. One writes to
174f2dbfd3Robert Mustacchi * the SMN address register and then can read from the SMN data register.
175f2dbfd3Robert Mustacchi */
176f2dbfd3Robert Mustacchi#define	AMDF17_NB_SMN_ADDR	0x60
177f2dbfd3Robert Mustacchi#define	AMDF17_NB_SMN_DATA	0x64
178f2dbfd3Robert Mustacchi
179f2dbfd3Robert Mustacchi/*
180f2dbfd3Robert Mustacchi * The following are register offsets and the meaning of their bits related to
181f2dbfd3Robert Mustacchi * temperature. These addresses are addresses in the System Management Network
182f2dbfd3Robert Mustacchi * which is accessed through the northbridge.  They are not addresses in PCI
183f2dbfd3Robert Mustacchi * configuration space.
184f2dbfd3Robert Mustacchi */
185f2dbfd3Robert Mustacchi#define	AMDF17_SMU_THERMAL_CURTEMP			0x00059800
186f2dbfd3Robert Mustacchi#define	AMDF17_SMU_THERMAL_CURTEMP_TEMPERATURE(x)	((x) >> 21)
187f2dbfd3Robert Mustacchi#define	AMDF17_SMU_THERMAL_CURTEMP_RANGE_SEL		(1 << 19)
188f2dbfd3Robert Mustacchi
189f2dbfd3Robert Mustacchi#define	AMDF17_SMU_THERMAL_CURTEMP_RANGE_ADJ		(-49)
190f2dbfd3Robert Mustacchi#define	AMDF17_SMU_THERMAL_CURTEMP_DECIMAL_BITS		3
191f2dbfd3Robert Mustacchi#define	AMDF17_SMU_THERMAL_CURTEMP_BITS_MASK		0x7
192f2dbfd3Robert Mustacchi
193f2dbfd3Robert Mustacchi/*
194f2dbfd3Robert Mustacchi * The temperature sensor in family 17 is measured in terms of 0.125 C steps.
195f2dbfd3Robert Mustacchi */
196f2dbfd3Robert Mustacchi#define	AMDF17_THERMAL_GRANULARITY	8
197f2dbfd3Robert Mustacchi
198f2dbfd3Robert Mustacchistruct amdf17nb;
199f2dbfd3Robert Mustacchistruct amdf17df;
200f2dbfd3Robert Mustacchi
201f2dbfd3Robert Mustacchitypedef struct amdf17nb {
202f2dbfd3Robert Mustacchi	list_node_t		amd_nb_link;
203f2dbfd3Robert Mustacchi	dev_info_t		*amd_nb_dip;
204f2dbfd3Robert Mustacchi	ddi_acc_handle_t	amd_nb_cfgspace;
205f2dbfd3Robert Mustacchi	uint_t			amd_nb_bus;
206f2dbfd3Robert Mustacchi	uint_t			amd_nb_dev;
207f2dbfd3Robert Mustacchi	uint_t			amd_nb_func;
208f2dbfd3Robert Mustacchi	struct amdf17df		*amd_nb_df;
209f2dbfd3Robert Mustacchi	uint_t			amd_nb_procnodeid;
210f2dbfd3Robert Mustacchi	id_t			amd_nb_temp_minor;
211f2dbfd3Robert Mustacchi	hrtime_t		amd_nb_temp_last_read;
212f2dbfd3Robert Mustacchi	int			amd_nb_temp_off;
213f2dbfd3Robert Mustacchi	uint32_t		amd_nb_temp_reg;
214f2dbfd3Robert Mustacchi	/* Values derived from the above */
215f2dbfd3Robert Mustacchi	int64_t			amd_nb_temp;
216f2dbfd3Robert Mustacchi} amdf17nb_t;
217f2dbfd3Robert Mustacchi
218f2dbfd3Robert Mustacchitypedef struct amdf17df {
219f2dbfd3Robert Mustacchi	list_node_t		amd_df_link;
220f2dbfd3Robert Mustacchi	dev_info_t		*amd_df_f0_dip;
221f2dbfd3Robert Mustacchi	ddi_acc_handle_t	amd_df_f0_cfgspace;
222f2dbfd3Robert Mustacchi	uint_t			amd_df_procnodeid;
223f2dbfd3Robert Mustacchi	uint_t			amd_df_iobus;
224f2dbfd3Robert Mustacchi	amdf17nb_t		*amd_df_nb;
225f2dbfd3Robert Mustacchi} amdf17df_t;
226f2dbfd3Robert Mustacchi
227f2dbfd3Robert Mustacchitypedef struct amdf17nbdf {
228f2dbfd3Robert Mustacchi	kmutex_t	amd_nbdf_lock;
229f2dbfd3Robert Mustacchi	id_space_t	*amd_nbdf_minors;
230f2dbfd3Robert Mustacchi	list_t		amd_nbdf_nbs;
231f2dbfd3Robert Mustacchi	list_t		amd_nbdf_dfs;
232f2dbfd3Robert Mustacchi} amdf17nbdf_t;
233f2dbfd3Robert Mustacchi
234f2dbfd3Robert Mustacchitypedef enum {
235f2dbfd3Robert Mustacchi	AMD_NBDF_TYPE_UNKNOWN,
236f2dbfd3Robert Mustacchi	AMD_NBDF_TYPE_NORTHBRIDGE,
237f2dbfd3Robert Mustacchi	AMD_NBDF_TYPE_DATA_FABRIC
238f2dbfd3Robert Mustacchi} amdf17nbdf_type_t;
239f2dbfd3Robert Mustacchi
240f2dbfd3Robert Mustacchitypedef struct {
241f2dbfd3Robert Mustacchi	uint16_t		amd_nbdft_pci_did;
242f2dbfd3Robert Mustacchi	amdf17nbdf_type_t	amd_nbdft_type;
243f2dbfd3Robert Mustacchi} amdf17nbdf_table_t;
244f2dbfd3Robert Mustacchi
245f2dbfd3Robert Mustacchistatic const amdf17nbdf_table_t amdf17nbdf_dev_map[] = {
246f2dbfd3Robert Mustacchi	/* Family 17h Ryzen, Epyc Models 00h-0fh (Zen uarch) */
247f2dbfd3Robert Mustacchi	{ 0x1450, AMD_NBDF_TYPE_NORTHBRIDGE },
248f2dbfd3Robert Mustacchi	{ 0x1460, AMD_NBDF_TYPE_DATA_FABRIC },
249f2dbfd3Robert Mustacchi	{ PCI_EINVAL16 }
250f2dbfd3Robert Mustacchi};
251f2dbfd3Robert Mustacchi
252f2dbfd3Robert Mustacchitypedef struct {
253f2dbfd3Robert Mustacchi	const char	*amd_nbdfo_brand;
254f2dbfd3Robert Mustacchi	uint_t		amd_nbdfo_family;
255f2dbfd3Robert Mustacchi	int		amd_nbdfo_off;
256f2dbfd3Robert Mustacchi} amdf17nbdf_offset_t;
257f2dbfd3Robert Mustacchi
258f2dbfd3Robert Mustacchi/*
259f2dbfd3Robert Mustacchi * AMD processors report a control temperature (called Tctl) which may be
260f2dbfd3Robert Mustacchi * different from the junction temperature, which is the value that is actually
261f2dbfd3Robert Mustacchi * measured from the die (sometimes called Tdie or Tjct). This is done so that
262f2dbfd3Robert Mustacchi * socket-based environmental monitoring can be consistent from a platform
263f2dbfd3Robert Mustacchi * perspective, but doesn't help us. Unfortunately, these values aren't in
264f2dbfd3Robert Mustacchi * datasheets that we can find, but have been documented partially in a series
265f2dbfd3Robert Mustacchi * of blog posts by AMD when discussing their 'Ryzen Master' monitoring software
266f2dbfd3Robert Mustacchi * for Windows.
267f2dbfd3Robert Mustacchi *
268f2dbfd3Robert Mustacchi * The brand strings below may contain partial matches such in the Threadripper
269f2dbfd3Robert Mustacchi * cases so we can match the entire family of processors. The offset value is
270f2dbfd3Robert Mustacchi * the quantity in degrees that we should adjust Tctl to reach Tdie.
271f2dbfd3Robert Mustacchi */
272f2dbfd3Robert Mustacchistatic const amdf17nbdf_offset_t amdf17nbdf_offsets[] = {
273f2dbfd3Robert Mustacchi	{ "AMD Ryzen 5 1600X", 0x17, -20 },
274f2dbfd3Robert Mustacchi	{ "AMD Ryzen 7 1700X", 0x17, -20 },
275f2dbfd3Robert Mustacchi	{ "AMD Ryzen 7 1800X", 0x17, -20 },
276f2dbfd3Robert Mustacchi	{ "AMD Ryzen 7 2700X", 0x17, -10 },
277f2dbfd3Robert Mustacchi	{ "AMD Ryzen Threadripper 19", 0x17, -27 },
278f2dbfd3Robert Mustacchi	{ "AMD Ryzen Threadripper 29", 0x17, -27 },
279f2dbfd3Robert Mustacchi	{ NULL }
280f2dbfd3Robert Mustacchi};
281f2dbfd3Robert Mustacchi
282f2dbfd3Robert Mustacchi/*
283f2dbfd3Robert Mustacchi * This indicates a number of milliseconds that we should wait between reads.
284f2dbfd3Robert Mustacchi * This is somewhat arbitrary, but the goal is to reduce cross call activity
285f2dbfd3Robert Mustacchi * and reflect that the sensor may not update all the time.
286f2dbfd3Robert Mustacchi */
287f2dbfd3Robert Mustacchiuint_t amdf17nbdf_cache_ms = 100;
288f2dbfd3Robert Mustacchi
289f2dbfd3Robert Mustacchi/*
290f2dbfd3Robert Mustacchi * This indicates whether detach is allowed. It is not by default. See the
291f2dbfd3Robert Mustacchi * theory statement section 'Attach and Detach Complications' for more
292f2dbfd3Robert Mustacchi * information.
293f2dbfd3Robert Mustacchi */
294f2dbfd3Robert Mustacchiuint_t amdf17nbdf_allow_detach = 0;
295f2dbfd3Robert Mustacchi
296f2dbfd3Robert Mustacchi/*
297f2dbfd3Robert Mustacchi * Global data that we keep regarding the device.
298f2dbfd3Robert Mustacchi */
299f2dbfd3Robert Mustacchiamdf17nbdf_t *amdf17nbdf;
300f2dbfd3Robert Mustacchi
301f2dbfd3Robert Mustacchistatic amdf17nb_t *
302f2dbfd3Robert Mustacchiamdf17nbdf_lookup_nb(amdf17nbdf_t *nbdf, minor_t minor)
303f2dbfd3Robert Mustacchi{
304f2dbfd3Robert Mustacchi	ASSERT(MUTEX_HELD(&nbdf->amd_nbdf_lock));
305f2dbfd3Robert Mustacchi
306f2dbfd3Robert Mustacchi	if (minor < AMDF17_MINOR_LOW || minor > AMDF17_MINOR_HIGH) {
307f2dbfd3Robert Mustacchi		return (NULL);
308f2dbfd3Robert Mustacchi	}
309f2dbfd3Robert Mustacchi
310f2dbfd3Robert Mustacchi	for (amdf17nb_t *nb = list_head(&nbdf->amd_nbdf_nbs); nb != NULL;
311f2dbfd3Robert Mustacchi	    nb = list_next(&nbdf->amd_nbdf_nbs, nb)) {
312f2dbfd3Robert Mustacchi		if ((id_t)minor == nb->amd_nb_temp_minor) {
313f2dbfd3Robert Mustacchi			return (nb);
314f2dbfd3Robert Mustacchi		}
315f2dbfd3Robert Mustacchi	}
316f2dbfd3Robert Mustacchi
317f2dbfd3Robert Mustacchi	return (NULL);
318f2dbfd3Robert Mustacchi}
319f2dbfd3Robert Mustacchi
320f2dbfd3Robert Mustacchistatic void
321f2dbfd3Robert Mustacchiamdf17nbdf_cleanup_nb(amdf17nbdf_t *nbdf, amdf17nb_t *nb)
322f2dbfd3Robert Mustacchi{
323f2dbfd3Robert Mustacchi	if (nb == NULL)
324f2dbfd3Robert Mustacchi		return;
325f2dbfd3Robert Mustacchi
326f2dbfd3Robert Mustacchi	ddi_remove_minor_node(nb->amd_nb_dip, NULL);
327f2dbfd3Robert Mustacchi	if (nb->amd_nb_temp_minor > 0) {
328f2dbfd3Robert Mustacchi		id_free(nbdf->amd_nbdf_minors, nb->amd_nb_temp_minor);
329f2dbfd3Robert Mustacchi	}
330f2dbfd3Robert Mustacchi	if (nb->amd_nb_cfgspace != NULL) {
331f2dbfd3Robert Mustacchi		pci_config_teardown(&nb->amd_nb_cfgspace);
332f2dbfd3Robert Mustacchi	}
333f2dbfd3Robert Mustacchi	kmem_free(nb, sizeof (amdf17nb_t));
334f2dbfd3Robert Mustacchi}
335f2dbfd3Robert Mustacchi
336f2dbfd3Robert Mustacchistatic void
337f2dbfd3Robert Mustacchiamdf17nbdf_cleanup_df(amdf17df_t *df)
338f2dbfd3Robert Mustacchi{
339f2dbfd3Robert Mustacchi	if (df == NULL)
340f2dbfd3Robert Mustacchi		return;
341f2dbfd3Robert Mustacchi
342f2dbfd3Robert Mustacchi	if (df->amd_df_f0_cfgspace != NULL) {
343f2dbfd3Robert Mustacchi		pci_config_teardown(&df->amd_df_f0_cfgspace);
344f2dbfd3Robert Mustacchi	}
345f2dbfd3Robert Mustacchi	kmem_free(df, sizeof (amdf17df_t));
346f2dbfd3Robert Mustacchi}
347f2dbfd3Robert Mustacchi
348f2dbfd3Robert Mustacchistatic int
349f2dbfd3Robert Mustacchiamdf17nbdf_smn_read(amdf17nbdf_t *nbdf, amdf17nb_t *nb, uint32_t addr,
350f2dbfd3Robert Mustacchi    uint32_t *valp)
351f2dbfd3Robert Mustacchi{
352f2dbfd3Robert Mustacchi	VERIFY(MUTEX_HELD(&nbdf->amd_nbdf_lock));
353f2dbfd3Robert Mustacchi
354f2dbfd3Robert Mustacchi	pci_config_put32(nb->amd_nb_cfgspace, AMDF17_NB_SMN_ADDR, addr);
355f2dbfd3Robert Mustacchi	*valp = pci_config_get32(nb->amd_nb_cfgspace, AMDF17_NB_SMN_DATA);
356f2dbfd3Robert Mustacchi
357f2dbfd3Robert Mustacchi	return (0);
358f2dbfd3Robert Mustacchi}
359f2dbfd3Robert Mustacchi
360f2dbfd3Robert Mustacchistatic int
361f2dbfd3Robert Mustacchiamdf17nbdf_temp_read(amdf17nbdf_t *nbdf, amdf17nb_t *nb)
362f2dbfd3Robert Mustacchi{
363f2dbfd3Robert Mustacchi	int ret;
364f2dbfd3Robert Mustacchi	uint32_t reg, rawtemp, decimal;
365f2dbfd3Robert Mustacchi
366f2dbfd3Robert Mustacchi	ASSERT(MUTEX_HELD(&nbdf->amd_nbdf_lock));
367f2dbfd3Robert Mustacchi
368f2dbfd3Robert Mustacchi	/*
369f2dbfd3Robert Mustacchi	 * Update the last read time first. Even if this fails, we want to make
370f2dbfd3Robert Mustacchi	 * sure that we latch the fact that we tried.
371f2dbfd3Robert Mustacchi	 */
372f2dbfd3Robert Mustacchi	nb->amd_nb_temp_last_read = gethrtime();
373f2dbfd3Robert Mustacchi	if ((ret = amdf17nbdf_smn_read(nbdf, nb, AMDF17_SMU_THERMAL_CURTEMP,
374f2dbfd3Robert Mustacchi	    &reg)) != 0) {
375f2dbfd3Robert Mustacchi		return (ret);
376f2dbfd3Robert Mustacchi	}
377f2dbfd3Robert Mustacchi
378f2dbfd3Robert Mustacchi	nb->amd_nb_temp_reg = reg;
379f2dbfd3Robert Mustacchi
380f2dbfd3Robert Mustacchi	/*
381f2dbfd3Robert Mustacchi	 * Take the primary temperature value and break apart its decimal value
382f2dbfd3Robert Mustacchi	 * from its main value.
383f2dbfd3Robert Mustacchi	 */
384f2dbfd3Robert Mustacchi	rawtemp = AMDF17_SMU_THERMAL_CURTEMP_TEMPERATURE(reg);
385f2dbfd3Robert Mustacchi	decimal = rawtemp & AMDF17_SMU_THERMAL_CURTEMP_BITS_MASK;
386f2dbfd3Robert Mustacchi	rawtemp = rawtemp >> AMDF17_SMU_THERMAL_CURTEMP_DECIMAL_BITS;
387f2dbfd3Robert Mustacchi
388f2dbfd3Robert Mustacchi	if ((reg & AMDF17_SMU_THERMAL_CURTEMP_RANGE_SEL) != 0) {
389f2dbfd3Robert Mustacchi		rawtemp += AMDF17_SMU_THERMAL_CURTEMP_RANGE_ADJ;
390f2dbfd3Robert Mustacchi	}
391f2dbfd3Robert Mustacchi	rawtemp += nb->amd_nb_temp_off;
392f2dbfd3Robert Mustacchi	nb->amd_nb_temp = rawtemp << AMDF17_SMU_THERMAL_CURTEMP_DECIMAL_BITS;
393f2dbfd3Robert Mustacchi	nb->amd_nb_temp += decimal;
394f2dbfd3Robert Mustacchi
395f2dbfd3Robert Mustacchi	return (0);
396f2dbfd3Robert Mustacchi}
397f2dbfd3Robert Mustacchi
398f2dbfd3Robert Mustacchistatic int
399f2dbfd3Robert Mustacchiamdf17nbdf_temp_init(amdf17nbdf_t *nbdf, amdf17nb_t *nb)
400f2dbfd3Robert Mustacchi{
401f2dbfd3Robert Mustacchi	uint_t i, family;
402f2dbfd3Robert Mustacchi	char buf[256];
403f2dbfd3Robert Mustacchi
404f2dbfd3Robert Mustacchi	if (cpuid_getbrandstr(CPU, buf, sizeof (buf)) >= sizeof (buf)) {
405f2dbfd3Robert Mustacchi		dev_err(nb->amd_nb_dip, CE_WARN, "!failed to read processor "
406f2dbfd3Robert Mustacchi		    "brand string, brand larger than internal buffer");
407f2dbfd3Robert Mustacchi		return (EOVERFLOW);
408f2dbfd3Robert Mustacchi	}
409f2dbfd3Robert Mustacchi
410f2dbfd3Robert Mustacchi	family = cpuid_getfamily(CPU);
411f2dbfd3Robert Mustacchi
412f2dbfd3Robert Mustacchi	for (i = 0; amdf17nbdf_offsets[i].amd_nbdfo_brand != NULL; i++) {
413f2dbfd3Robert Mustacchi		if (family != amdf17nbdf_offsets[i].amd_nbdfo_family)
414f2dbfd3Robert Mustacchi			continue;
415f2dbfd3Robert Mustacchi		if (strncmp(buf, amdf17nbdf_offsets[i].amd_nbdfo_brand,
416f2dbfd3Robert Mustacchi		    strlen(amdf17nbdf_offsets[i].amd_nbdfo_brand)) == 0) {
417f2dbfd3Robert Mustacchi			nb->amd_nb_temp_off =
418f2dbfd3Robert Mustacchi			    amdf17nbdf_offsets[i].amd_nbdfo_off;
419f2dbfd3Robert Mustacchi			break;
420f2dbfd3Robert Mustacchi		}
421f2dbfd3Robert Mustacchi	}
422f2dbfd3Robert Mustacchi
423f2dbfd3Robert Mustacchi	return (amdf17nbdf_temp_read(nbdf, nb));
424f2dbfd3Robert Mustacchi}
425f2dbfd3Robert Mustacchi
426f2dbfd3Robert Mustacchistatic amdf17nbdf_type_t
427f2dbfd3Robert Mustacchiamdf17nbdf_dip_type(uint16_t dev)
428f2dbfd3Robert Mustacchi{
429f2dbfd3Robert Mustacchi	uint_t i;
430f2dbfd3Robert Mustacchi	const amdf17nbdf_table_t *tp = amdf17nbdf_dev_map;
431f2dbfd3Robert Mustacchi
432f2dbfd3Robert Mustacchi	for (i = 0; tp[i].amd_nbdft_pci_did != PCI_EINVAL16; i++) {
433f2dbfd3Robert Mustacchi		if (tp[i].amd_nbdft_pci_did == dev) {
434f2dbfd3Robert Mustacchi			return (tp[i].amd_nbdft_type);
435f2dbfd3Robert Mustacchi		}
436f2dbfd3Robert Mustacchi	}
437f2dbfd3Robert Mustacchi
438f2dbfd3Robert Mustacchi	return (AMD_NBDF_TYPE_UNKNOWN);
439f2dbfd3Robert Mustacchi}
440f2dbfd3Robert Mustacchi
441f2dbfd3Robert Mustacchistatic boolean_t
442f2dbfd3Robert Mustacchiamdf17nbdf_map(amdf17nbdf_t *nbdf, amdf17nb_t *nb, amdf17df_t *df)
443f2dbfd3Robert Mustacchi{
444f2dbfd3Robert Mustacchi	int ret;
445f2dbfd3Robert Mustacchi	char buf[128];
446f2dbfd3Robert Mustacchi
447f2dbfd3Robert Mustacchi	ASSERT(MUTEX_HELD(&nbdf->amd_nbdf_lock));
448f2dbfd3Robert Mustacchi
449f2dbfd3Robert Mustacchi	/*
450f2dbfd3Robert Mustacchi	 * This means that we encountered a duplicate. We're going to stop
451f2dbfd3Robert Mustacchi	 * processing, but we're not going to fail its attach at this point.
452f2dbfd3Robert Mustacchi	 */
453f2dbfd3Robert Mustacchi	if (nb->amd_nb_df != NULL) {
454f2dbfd3Robert Mustacchi		dev_err(nb->amd_nb_dip, CE_WARN, "!trying to map NB %u/%u/%u "
455f2dbfd3Robert Mustacchi		    "to DF procnode %u, but NB is already mapped to DF "
456f2dbfd3Robert Mustacchi		    "procnode %u!",
457f2dbfd3Robert Mustacchi		    nb->amd_nb_bus, nb->amd_nb_dev, nb->amd_nb_func,
458f2dbfd3Robert Mustacchi		    df->amd_df_procnodeid, nb->amd_nb_df->amd_df_procnodeid);
459f2dbfd3Robert Mustacchi		return (B_TRUE);
460f2dbfd3Robert Mustacchi	}
461f2dbfd3Robert Mustacchi
462f2dbfd3Robert Mustacchi	/*
463f2dbfd3Robert Mustacchi	 * Now that we have found a mapping, initialize our temperature
464f2dbfd3Robert Mustacchi	 * information and create the minor node.
465f2dbfd3Robert Mustacchi	 */
466f2dbfd3Robert Mustacchi	nb->amd_nb_procnodeid = df->amd_df_procnodeid;
467f2dbfd3Robert Mustacchi	nb->amd_nb_temp_minor = id_alloc(nbdf->amd_nbdf_minors);
468f2dbfd3Robert Mustacchi
469f2dbfd3Robert Mustacchi	if ((ret = amdf17nbdf_temp_init(nbdf, nb)) != 0) {
470f2dbfd3Robert Mustacchi		dev_err(nb->amd_nb_dip, CE_WARN, "!failed to init SMN "
471f2dbfd3Robert Mustacchi		    "temperature data on node %u: %d", nb->amd_nb_procnodeid,
472f2dbfd3Robert Mustacchi		    ret);
473f2dbfd3Robert Mustacchi		return (B_FALSE);
474f2dbfd3Robert Mustacchi	}
475f2dbfd3Robert Mustacchi
476f2dbfd3Robert Mustacchi	if (snprintf(buf, sizeof (buf), "procnode.%u", nb->amd_nb_procnodeid) >=
477f2dbfd3Robert Mustacchi	    sizeof (buf)) {
478f2dbfd3Robert Mustacchi		dev_err(nb->amd_nb_dip, CE_WARN, "!unexpected buffer name "
479f2dbfd3Robert Mustacchi		    "overrun assembling temperature minor %u",
480f2dbfd3Robert Mustacchi		    nb->amd_nb_procnodeid);
481f2dbfd3Robert Mustacchi		return (B_FALSE);
482f2dbfd3Robert Mustacchi	}
483f2dbfd3Robert Mustacchi
484f2dbfd3Robert Mustacchi	if (ddi_create_minor_node(nb->amd_nb_dip, buf, S_IFCHR,
485f2dbfd3Robert Mustacchi	    nb->amd_nb_temp_minor, DDI_NT_SENSOR_TEMP_CPU, 0) != DDI_SUCCESS) {
486f2dbfd3Robert Mustacchi		dev_err(nb->amd_nb_dip, CE_WARN, "!failed to create minor node "
487f2dbfd3Robert Mustacchi		    "%s", buf);
488f2dbfd3Robert Mustacchi		return (B_FALSE);
489f2dbfd3Robert Mustacchi	}
490f2dbfd3Robert Mustacchi
491f2dbfd3Robert Mustacchi	/*
492f2dbfd3Robert Mustacchi	 * Now that's it's all done, note that they're mapped to each other.
493f2dbfd3Robert Mustacchi	 */
494f2dbfd3Robert Mustacchi	nb->amd_nb_df = df;
495f2dbfd3Robert Mustacchi	df->amd_df_nb = nb;
496f2dbfd3Robert Mustacchi
497f2dbfd3Robert Mustacchi	return (B_TRUE);
498f2dbfd3Robert Mustacchi}
499f2dbfd3Robert Mustacchi
500f2dbfd3Robert Mustacchistatic boolean_t
501f2dbfd3Robert Mustacchiamdf17nbdf_add_nb(amdf17nbdf_t *nbdf, amdf17nb_t *nb)
502f2dbfd3Robert Mustacchi{
503f2dbfd3Robert Mustacchi	amdf17df_t *df;
504f2dbfd3Robert Mustacchi	boolean_t ret = B_TRUE;
505f2dbfd3Robert Mustacchi
506f2dbfd3Robert Mustacchi	mutex_enter(&nbdf->amd_nbdf_lock);
507f2dbfd3Robert Mustacchi	list_insert_tail(&nbdf->amd_nbdf_nbs, nb);
508f2dbfd3Robert Mustacchi	for (df = list_head(&nbdf->amd_nbdf_dfs); df != NULL;
509f2dbfd3Robert Mustacchi	    df = list_next(&nbdf->amd_nbdf_dfs, df)) {
510f2dbfd3Robert Mustacchi		if (nb->amd_nb_bus == df->amd_df_iobus) {
511f2dbfd3Robert Mustacchi			ret = amdf17nbdf_map(nbdf, nb, df);
512f2dbfd3Robert Mustacchi			break;
513f2dbfd3Robert Mustacchi		}
514f2dbfd3Robert Mustacchi	}
515f2dbfd3Robert Mustacchi	mutex_exit(&nbdf->amd_nbdf_lock);
516f2dbfd3Robert Mustacchi
517f2dbfd3Robert Mustacchi	return (ret);
518f2dbfd3Robert Mustacchi}
519f2dbfd3Robert Mustacchi
520f2dbfd3Robert Mustacchistatic boolean_t
521f2dbfd3Robert Mustacchiamdf17nbdf_add_df(amdf17nbdf_t *nbdf, amdf17df_t *df)
522f2dbfd3Robert Mustacchi{
523f2dbfd3Robert Mustacchi	amdf17nb_t *nb;
524f2dbfd3Robert Mustacchi	boolean_t ret = B_TRUE;
525f2dbfd3Robert Mustacchi
526f2dbfd3Robert Mustacchi	mutex_enter(&nbdf->amd_nbdf_lock);
527f2dbfd3Robert Mustacchi	list_insert_tail(&nbdf->amd_nbdf_dfs, df);
528f2dbfd3Robert Mustacchi	for (nb = list_head(&nbdf->amd_nbdf_nbs); nb != NULL;
529f2dbfd3Robert Mustacchi	    nb = list_next(&nbdf->amd_nbdf_nbs, nb)) {
530f2dbfd3Robert Mustacchi		if (nb->amd_nb_bus == df->amd_df_iobus) {
531f2dbfd3Robert Mustacchi			ret = amdf17nbdf_map(nbdf, nb, df);
532f2dbfd3Robert Mustacchi		}
533f2dbfd3Robert Mustacchi	}
534f2dbfd3Robert Mustacchi	mutex_exit(&nbdf->amd_nbdf_lock);
535f2dbfd3Robert Mustacchi
536f2dbfd3Robert Mustacchi	return (ret);
537f2dbfd3Robert Mustacchi}
538f2dbfd3Robert Mustacchi
539f2dbfd3Robert Mustacchistatic boolean_t
540f2dbfd3Robert Mustacchiamdf17nbdf_attach_nb(amdf17nbdf_t *nbdf, dev_info_t *dip, ddi_acc_handle_t hdl,
541f2dbfd3Robert Mustacchi    uint_t bus, uint_t dev, uint_t func)
542f2dbfd3Robert Mustacchi{
543f2dbfd3Robert Mustacchi	amdf17nb_t *nb;
544f2dbfd3Robert Mustacchi
545f2dbfd3Robert Mustacchi	nb = kmem_zalloc(sizeof (amdf17nb_t), KM_SLEEP);
546f2dbfd3Robert Mustacchi	nb->amd_nb_dip = dip;
547f2dbfd3Robert Mustacchi	nb->amd_nb_cfgspace = hdl;
548f2dbfd3Robert Mustacchi	nb->amd_nb_bus = bus;
549f2dbfd3Robert Mustacchi	nb->amd_nb_dev = dev;
550f2dbfd3Robert Mustacchi	nb->amd_nb_func = func;
551f2dbfd3Robert Mustacchi	/*
552f2dbfd3Robert Mustacchi	 * Set this to a value we won't get from the processor.
553f2dbfd3Robert Mustacchi	 */
554f2dbfd3Robert Mustacchi	nb->amd_nb_procnodeid = UINT_MAX;
555f2dbfd3Robert Mustacchi
556f2dbfd3Robert Mustacchi	if (!amdf17nbdf_add_nb(nbdf, nb)) {
557f2dbfd3Robert Mustacchi		amdf17nbdf_cleanup_nb(nbdf, nb);
558f2dbfd3Robert Mustacchi		return (B_FALSE);
559f2dbfd3Robert Mustacchi	}
560f2dbfd3Robert Mustacchi
561f2dbfd3Robert Mustacchi	return (B_TRUE);
562f2dbfd3Robert Mustacchi}
563f2dbfd3Robert Mustacchi
564f2dbfd3Robert Mustacchistatic boolean_t
565f2dbfd3Robert Mustacchiamdf17nbdf_attach_df(amdf17nbdf_t *nbdf, dev_info_t *dip, ddi_acc_handle_t hdl,
566f2dbfd3Robert Mustacchi    uint_t bus, uint_t dev, uint_t func)
567f2dbfd3Robert Mustacchi{
568f2dbfd3Robert Mustacchi	amdf17df_t *df;
569f2dbfd3Robert Mustacchi
570f2dbfd3Robert Mustacchi	if (bus != AMDF17_DF_BUSNO) {
571f2dbfd3Robert Mustacchi		dev_err(dip, CE_WARN, "!encountered data fabric device with "
572f2dbfd3Robert Mustacchi		    "unexpected PCI bus assignment, found 0x%x, expected 0x%x",
573f2dbfd3Robert Mustacchi		    bus, AMDF17_DF_BUSNO);
574f2dbfd3Robert Mustacchi		return (B_FALSE);
575f2dbfd3Robert Mustacchi	}
576f2dbfd3Robert Mustacchi
577f2dbfd3Robert Mustacchi	if (dev < AMDF17_DF_FIRST_DEVICE) {
578f2dbfd3Robert Mustacchi		dev_err(dip, CE_WARN, "!encountered data fabric device with "
579f2dbfd3Robert Mustacchi		    "PCI device assignment below the first minimum device "
580f2dbfd3Robert Mustacchi		    "(0x%x): 0x%x", AMDF17_DF_FIRST_DEVICE, dev);
581f2dbfd3Robert Mustacchi		return (B_FALSE);
582f2dbfd3Robert Mustacchi	}
583f2dbfd3Robert Mustacchi
584f2dbfd3Robert Mustacchi	/*
585f2dbfd3Robert Mustacchi	 * At the moment we only care about function 0. However, we may care
586f2dbfd3Robert Mustacchi	 * about Function 4 in the future which has access to the FICAA.
587f2dbfd3Robert Mustacchi	 * However, only function zero should ever be attached, so this is just
588f2dbfd3Robert Mustacchi	 * an extra precaution.
589f2dbfd3Robert Mustacchi	 */
590f2dbfd3Robert Mustacchi	if (func != 0) {
591f2dbfd3Robert Mustacchi		dev_err(dip, CE_WARN, "!encountered data fabric device with "
592f2dbfd3Robert Mustacchi		    "unxpected PCI function assignment, found 0x%x, expected "
593f2dbfd3Robert Mustacchi		    "0x0", func);
594f2dbfd3Robert Mustacchi		return (B_FALSE);
595f2dbfd3Robert Mustacchi	}
596f2dbfd3Robert Mustacchi
597f2dbfd3Robert Mustacchi	df = kmem_zalloc(sizeof (amdf17df_t), KM_SLEEP);
598f2dbfd3Robert Mustacchi	df->amd_df_f0_dip = dip;
599f2dbfd3Robert Mustacchi	df->amd_df_f0_cfgspace = hdl;
600f2dbfd3Robert Mustacchi	df->amd_df_procnodeid = dev - AMDF17_DF_FIRST_DEVICE;
601f2dbfd3Robert Mustacchi	df->amd_df_iobus = pci_config_get32(hdl, AMDF17_DF_CFG_ADDR_CTL) &
602f2dbfd3Robert Mustacchi	    AMDF17_DF_CFG_ADDR_CTL_MASK;
603f2dbfd3Robert Mustacchi
604f2dbfd3Robert Mustacchi	if (!amdf17nbdf_add_df(nbdf, df)) {
605f2dbfd3Robert Mustacchi		amdf17nbdf_cleanup_df(df);
606f2dbfd3Robert Mustacchi		return (B_FALSE);
607f2dbfd3Robert Mustacchi	}
608f2dbfd3Robert Mustacchi
609f2dbfd3Robert Mustacchi	return (B_TRUE);
610f2dbfd3Robert Mustacchi}
611f2dbfd3Robert Mustacchi
612f2dbfd3Robert Mustacchistatic int
613f2dbfd3Robert Mustacchiamdf17nbdf_open(dev_t *devp, int flags, int otype, cred_t *credp)
614f2dbfd3Robert Mustacchi{
615f2dbfd3Robert Mustacchi	amdf17nbdf_t *nbdf = amdf17nbdf;
616f2dbfd3Robert Mustacchi	minor_t m;
617f2dbfd3Robert Mustacchi
618f2dbfd3Robert Mustacchi	if (crgetzoneid(credp) != GLOBAL_ZONEID || drv_priv(credp)) {
619f2dbfd3Robert Mustacchi		return (EPERM);
620f2dbfd3Robert Mustacchi	}
621f2dbfd3Robert Mustacchi
622f2dbfd3Robert Mustacchi	if ((flags & (FEXCL | FNDELAY | FWRITE)) != 0) {
623f2dbfd3Robert Mustacchi		return (EINVAL);
624f2dbfd3Robert Mustacchi	}
625f2dbfd3Robert Mustacchi
626f2dbfd3Robert Mustacchi	if (otype != OTYP_CHR) {
627f2dbfd3Robert Mustacchi		return (EINVAL);
628f2dbfd3Robert Mustacchi	}
629f2dbfd3Robert Mustacchi
630f2dbfd3Robert Mustacchi	m = getminor(*devp);
631f2dbfd3Robert Mustacchi
632f2dbfd3Robert Mustacchi	/*
633f2dbfd3Robert Mustacchi	 * Sanity check the minor
634f2dbfd3Robert Mustacchi	 */
635f2dbfd3Robert Mustacchi	mutex_enter(&nbdf->amd_nbdf_lock);
636f2dbfd3Robert Mustacchi	if (amdf17nbdf_lookup_nb(nbdf, m) == NULL) {
637f2dbfd3Robert Mustacchi		mutex_exit(&nbdf->amd_nbdf_lock);
638f2dbfd3Robert Mustacchi		return (ENXIO);
639f2dbfd3Robert Mustacchi	}
640f2dbfd3Robert Mustacchi	mutex_exit(&nbdf->amd_nbdf_lock);
641f2dbfd3Robert Mustacchi
642f2dbfd3Robert Mustacchi	return (0);
643f2dbfd3Robert Mustacchi}
644f2dbfd3Robert Mustacchi
645f2dbfd3Robert Mustacchistatic int
646f2dbfd3Robert Mustacchiamdf17nbdf_ioctl_kind(intptr_t arg, int mode)
647f2dbfd3Robert Mustacchi{
648f2dbfd3Robert Mustacchi	sensor_ioctl_kind_t kind;
649f2dbfd3Robert Mustacchi
650f2dbfd3Robert Mustacchi	bzero(&kind, sizeof (sensor_ioctl_kind_t));
651f2dbfd3Robert Mustacchi	kind.sik_kind = SENSOR_KIND_TEMPERATURE;
652f2dbfd3Robert Mustacchi
653f2dbfd3Robert Mustacchi	if (ddi_copyout((void *)&kind, (void *)arg,
654f2dbfd3Robert Mustacchi	    sizeof (sensor_ioctl_kind_t), mode & FKIOCTL) != 0) {
655f2dbfd3Robert Mustacchi		return (EFAULT);
656f2dbfd3Robert Mustacchi	}
657f2dbfd3Robert Mustacchi
658f2dbfd3Robert Mustacchi	return (0);
659f2dbfd3Robert Mustacchi}
660f2dbfd3Robert Mustacchi
661f2dbfd3Robert Mustacchistatic int
662f2dbfd3Robert Mustacchiamdf17nbdf_ioctl_temp(amdf17nbdf_t *nbdf, minor_t minor, intptr_t arg, int mode)
663f2dbfd3Robert Mustacchi{
664f2dbfd3Robert Mustacchi	amdf17nb_t *nb;
665f2dbfd3Robert Mustacchi	hrtime_t diff;
666f2dbfd3Robert Mustacchi	sensor_ioctl_temperature_t temp;
667f2dbfd3Robert Mustacchi
668f2dbfd3Robert Mustacchi	bzero(&temp, sizeof (temp));
669f2dbfd3Robert Mustacchi
670f2dbfd3Robert Mustacchi	mutex_enter(&nbdf->amd_nbdf_lock);
671f2dbfd3Robert Mustacchi	nb = amdf17nbdf_lookup_nb(nbdf, minor);
672f2dbfd3Robert Mustacchi	if (nb == NULL) {
673f2dbfd3Robert Mustacchi		mutex_exit(&nbdf->amd_nbdf_lock);
674f2dbfd3Robert Mustacchi		return (ENXIO);
675f2dbfd3Robert Mustacchi	}
676f2dbfd3Robert Mustacchi
677f2dbfd3Robert Mustacchi	diff = NSEC2MSEC(gethrtime() - nb->amd_nb_temp_last_read);
678f2dbfd3Robert Mustacchi	if (diff > 0 && diff > (hrtime_t)amdf17nbdf_cache_ms) {
679f2dbfd3Robert Mustacchi		int ret;
680f2dbfd3Robert Mustacchi
681f2dbfd3Robert Mustacchi		ret = amdf17nbdf_temp_read(nbdf, nb);
682f2dbfd3Robert Mustacchi		if (ret != 0) {
683f2dbfd3Robert Mustacchi			mutex_exit(&nbdf->amd_nbdf_lock);
684f2dbfd3Robert Mustacchi			return (ret);
685f2dbfd3Robert Mustacchi		}
686f2dbfd3Robert Mustacchi	}
687f2dbfd3Robert Mustacchi
688f2dbfd3Robert Mustacchi	temp.sit_unit = SENSOR_UNIT_CELSIUS;
689f2dbfd3Robert Mustacchi	temp.sit_temp = nb->amd_nb_temp;
690f2dbfd3Robert Mustacchi	temp.sit_gran = AMDF17_THERMAL_GRANULARITY;
691f2dbfd3Robert Mustacchi	mutex_exit(&nbdf->amd_nbdf_lock);
692f2dbfd3Robert Mustacchi
693f2dbfd3Robert Mustacchi	if (ddi_copyout(&temp, (void *)arg, sizeof (temp),
694f2dbfd3Robert Mustacchi	    mode & FKIOCTL) != 0) {
695f2dbfd3Robert Mustacchi		return (EFAULT);
696f2dbfd3Robert Mustacchi	}
697f2dbfd3Robert Mustacchi
698f2dbfd3Robert Mustacchi	return (0);
699f2dbfd3Robert Mustacchi}
700f2dbfd3Robert Mustacchi
701f2dbfd3Robert Mustacchistatic int
702f2dbfd3Robert Mustacchiamdf17nbdf_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
703f2dbfd3Robert Mustacchi    int *rvalp)
704f2dbfd3Robert Mustacchi{
705f2dbfd3Robert Mustacchi	minor_t m;
706f2dbfd3Robert Mustacchi	amdf17nbdf_t *nbdf = amdf17nbdf;
707f2dbfd3Robert Mustacchi
708f2dbfd3Robert Mustacchi	if ((mode & FREAD) == 0) {
709f2dbfd3Robert Mustacchi		return (EINVAL);
710f2dbfd3Robert Mustacchi	}
711f2dbfd3Robert Mustacchi
712f2dbfd3Robert Mustacchi	m = getminor(dev);
713f2dbfd3Robert Mustacchi
714f2dbfd3Robert Mustacchi	switch (cmd) {
715f2dbfd3Robert Mustacchi	case SENSOR_IOCTL_TYPE:
716f2dbfd3Robert Mustacchi		return (amdf17nbdf_ioctl_kind(arg, mode));
717f2dbfd3Robert Mustacchi	case SENSOR_IOCTL_TEMPERATURE:
718f2dbfd3Robert Mustacchi		return (amdf17nbdf_ioctl_temp(nbdf, m, arg, mode));
719f2dbfd3Robert Mustacchi	default:
720f2dbfd3Robert Mustacchi		return (ENOTTY);
721f2dbfd3Robert Mustacchi	}
722f2dbfd3Robert Mustacchi}
723f2dbfd3Robert Mustacchi
724f2dbfd3Robert Mustacchi/*
725f2dbfd3Robert Mustacchi * We don't really do any state tracking on close, so for now, just allow it to
726f2dbfd3Robert Mustacchi * always succeed.
727f2dbfd3Robert Mustacchi */
728f2dbfd3Robert Mustacchistatic int
729f2dbfd3Robert Mustacchiamdf17nbdf_close(dev_t dev, int flags, int otype, cred_t *credp)
730f2dbfd3Robert Mustacchi{
731f2dbfd3Robert Mustacchi	return (0);
732f2dbfd3Robert Mustacchi}
733f2dbfd3Robert Mustacchi
734f2dbfd3Robert Mustacchistatic int
735f2dbfd3Robert Mustacchiamdf17nbdf_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
736f2dbfd3Robert Mustacchi{
737f2dbfd3Robert Mustacchi	uint_t nregs;
738f2dbfd3Robert Mustacchi	int *regs;
739f2dbfd3Robert Mustacchi	uint_t bus, dev, func;
740f2dbfd3Robert Mustacchi	uint16_t pci_did;
741f2dbfd3Robert Mustacchi	ddi_acc_handle_t pci_hdl;
742f2dbfd3Robert Mustacchi	amdf17nbdf_type_t type;
743f2dbfd3Robert Mustacchi	amdf17nbdf_t *nbdf = amdf17nbdf;
744f2dbfd3Robert Mustacchi
745f2dbfd3Robert Mustacchi	if (cmd == DDI_RESUME)
746f2dbfd3Robert Mustacchi		return (DDI_SUCCESS);
747f2dbfd3Robert Mustacchi	if (cmd != DDI_ATTACH)
748f2dbfd3Robert Mustacchi		return (DDI_FAILURE);
749f2dbfd3Robert Mustacchi
750f2dbfd3Robert Mustacchi	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 0, "reg",
751f2dbfd3Robert Mustacchi	    &regs, &nregs) != DDI_PROP_SUCCESS) {
752f2dbfd3Robert Mustacchi		dev_err(dip, CE_WARN, "!failed to find pci 'reg' property");
753f2dbfd3Robert Mustacchi		return (DDI_FAILURE);
754f2dbfd3Robert Mustacchi	}
755f2dbfd3Robert Mustacchi
756f2dbfd3Robert Mustacchi	if (nregs < 1) {
757f2dbfd3Robert Mustacchi		ddi_prop_free(regs);
758f2dbfd3Robert Mustacchi		return (DDI_FAILURE);
759f2dbfd3Robert Mustacchi	}
760f2dbfd3Robert Mustacchi
761f2dbfd3Robert Mustacchi	bus = PCI_REG_BUS_G(regs[0]);
762f2dbfd3Robert Mustacchi	dev = PCI_REG_DEV_G(regs[0]);
763f2dbfd3Robert Mustacchi	func = PCI_REG_FUNC_G(regs[0]);
764f2dbfd3Robert Mustacchi
765f2dbfd3Robert Mustacchi	ddi_prop_free(regs);
766f2dbfd3Robert Mustacchi
767f2dbfd3Robert Mustacchi	if (pci_config_setup(dip, &pci_hdl) != DDI_SUCCESS) {
768f2dbfd3Robert Mustacchi		dev_err(dip, CE_WARN, "!failed to map pci devices");
769f2dbfd3Robert Mustacchi		return (DDI_FAILURE);
770f2dbfd3Robert Mustacchi	}
771f2dbfd3Robert Mustacchi
772