1fc1f75e5SRui Paulo /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni *
4454e82d7SRui Paulo * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org>
5454e82d7SRui Paulo * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org>
6074d80acSJung-uk Kim * Copyright (c) 2009-2012 Jung-uk Kim <jkim@FreeBSD.org>
7fc1f75e5SRui Paulo * All rights reserved.
8c59b9a4fSConrad Meyer * Copyright (c) 2017-2020 Conrad Meyer <cem@FreeBSD.org>. All rights reserved.
9fc1f75e5SRui Paulo *
10fc1f75e5SRui Paulo * Redistribution and use in source and binary forms, with or without
11fc1f75e5SRui Paulo * modification, are permitted provided that the following conditions
12fc1f75e5SRui Paulo * are met:
13fc1f75e5SRui Paulo * 1. Redistributions of source code must retain the above copyright
14fc1f75e5SRui Paulo * notice, this list of conditions and the following disclaimer.
15fc1f75e5SRui Paulo * 2. Redistributions in binary form must reproduce the above copyright
16fc1f75e5SRui Paulo * notice, this list of conditions and the following disclaimer in the
17fc1f75e5SRui Paulo * documentation and/or other materials provided with the distribution.
18fc1f75e5SRui Paulo *
19fc1f75e5SRui Paulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20fc1f75e5SRui Paulo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21fc1f75e5SRui Paulo * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22fc1f75e5SRui Paulo * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
23fc1f75e5SRui Paulo * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24fc1f75e5SRui Paulo * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25fc1f75e5SRui Paulo * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26fc1f75e5SRui Paulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27fc1f75e5SRui Paulo * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28fc1f75e5SRui Paulo * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29fc1f75e5SRui Paulo * POSSIBILITY OF SUCH DAMAGE.
30fc1f75e5SRui Paulo */
31fc1f75e5SRui Paulo
32fc1f75e5SRui Paulo /*
33074d80acSJung-uk Kim * Driver for the AMD CPU on-die thermal sensors.
34a4165bbaSJung-uk Kim * Initially based on the k8temp Linux driver.
35fc1f75e5SRui Paulo */
36fc1f75e5SRui Paulo
37fc1f75e5SRui Paulo #include <sys/param.h>
38fc1f75e5SRui Paulo #include <sys/bus.h>
39fc1f75e5SRui Paulo #include <sys/conf.h>
40fc1f75e5SRui Paulo #include <sys/kernel.h>
416c101ed7SAlexander Motin #include <sys/lock.h>
42a4165bbaSJung-uk Kim #include <sys/module.h>
436c101ed7SAlexander Motin #include <sys/mutex.h>
44fc1f75e5SRui Paulo #include <sys/sysctl.h>
45a4165bbaSJung-uk Kim #include <sys/systm.h>
46fc1f75e5SRui Paulo
47fdfa6079SJung-uk Kim #include <machine/cpufunc.h>
48fc1f75e5SRui Paulo #include <machine/md_var.h>
49a4165bbaSJung-uk Kim #include <machine/specialreg.h>
50fc1f75e5SRui Paulo
51fc1f75e5SRui Paulo #include <dev/pci/pcivar.h>
52074d80acSJung-uk Kim #include <x86/pci_cfgreg.h>
53fc1f75e5SRui Paulo
54a03d621bSConrad Meyer #include <dev/amdsmn/amdsmn.h>
55a03d621bSConrad Meyer
56fc1f75e5SRui Paulo typedef enum {
57074d80acSJung-uk Kim CORE0_SENSOR0,
58074d80acSJung-uk Kim CORE0_SENSOR1,
59074d80acSJung-uk Kim CORE1_SENSOR0,
60074d80acSJung-uk Kim CORE1_SENSOR1,
61fc1f75e5SRui Paulo CORE0,
62c59b9a4fSConrad Meyer CORE1,
63c59b9a4fSConrad Meyer CCD1,
64c59b9a4fSConrad Meyer CCD_BASE = CCD1,
65c59b9a4fSConrad Meyer CCD2,
66c59b9a4fSConrad Meyer CCD3,
67c59b9a4fSConrad Meyer CCD4,
68c59b9a4fSConrad Meyer CCD5,
69c59b9a4fSConrad Meyer CCD6,
70c59b9a4fSConrad Meyer CCD7,
71c59b9a4fSConrad Meyer CCD8,
7251c69c86SXin LI CCD9,
7351c69c86SXin LI CCD10,
7451c69c86SXin LI CCD11,
7551c69c86SXin LI CCD12,
7651c69c86SXin LI CCD_MAX = CCD12,
77c59b9a4fSConrad Meyer NUM_CCDS = CCD_MAX - CCD_BASE + 1,
78454e82d7SRui Paulo } amdsensor_t;
79454e82d7SRui Paulo
80454e82d7SRui Paulo struct amdtemp_softc {
81a4165bbaSJung-uk Kim int sc_ncores;
82454e82d7SRui Paulo int sc_ntemps;
83fdfa6079SJung-uk Kim int sc_flags;
84074d80acSJung-uk Kim #define AMDTEMP_FLAG_CS_SWAP 0x01 /* ThermSenseCoreSel is inverted. */
85074d80acSJung-uk Kim #define AMDTEMP_FLAG_CT_10BIT 0x02 /* CurTmp is 10-bit wide. */
86074d80acSJung-uk Kim #define AMDTEMP_FLAG_ALT_OFFSET 0x04 /* CurTmp starts at -28C. */
87074d80acSJung-uk Kim int32_t sc_offset;
88323a94afSAkio Morita int32_t sc_temp_base;
89454e82d7SRui Paulo int32_t (*sc_gettemp)(device_t, amdsensor_t);
90a4165bbaSJung-uk Kim struct sysctl_oid *sc_sysctl_cpu[MAXCPU];
91a4165bbaSJung-uk Kim struct intr_config_hook sc_ich;
92a03d621bSConrad Meyer device_t sc_smn;
936c101ed7SAlexander Motin struct mtx sc_lock;
94454e82d7SRui Paulo };
95454e82d7SRui Paulo
96e49ec461SConrad Meyer /*
97e49ec461SConrad Meyer * N.B. The numbers in macro names below are significant and represent CPU
98e49ec461SConrad Meyer * family and model numbers. Do not make up fictitious family or model numbers
99e49ec461SConrad Meyer * when adding support for new devices.
100e49ec461SConrad Meyer */
101454e82d7SRui Paulo #define VENDORID_AMD 0x1022
102454e82d7SRui Paulo #define DEVICEID_AMD_MISC0F 0x1103
103454e82d7SRui Paulo #define DEVICEID_AMD_MISC10 0x1203
104454e82d7SRui Paulo #define DEVICEID_AMD_MISC11 0x1303
105074d80acSJung-uk Kim #define DEVICEID_AMD_MISC14 0x1703
106074d80acSJung-uk Kim #define DEVICEID_AMD_MISC15 0x1603
107e49ec461SConrad Meyer #define DEVICEID_AMD_MISC15_M10H 0x1403
108e49ec461SConrad Meyer #define DEVICEID_AMD_MISC15_M30H 0x141d
109e49ec461SConrad Meyer #define DEVICEID_AMD_MISC15_M60H_ROOT 0x1576
1102b56f12bSChristian Brueffer #define DEVICEID_AMD_MISC16 0x1533
111df20515dSLuiz Otavio O Souza #define DEVICEID_AMD_MISC16_M30H 0x1583
1129d49c422SConrad Meyer #define DEVICEID_AMD_HOSTB17H_ROOT 0x1450
1139d49c422SConrad Meyer #define DEVICEID_AMD_HOSTB17H_M10H_ROOT 0x15d0
114ea6189d3SConrad Meyer #define DEVICEID_AMD_HOSTB17H_M30H_ROOT 0x1480 /* Also M70H, F19H M00H/M20H */
1157aa6eeb2SSimon Wells #define DEVICEID_AMD_HOSTB17H_M60H_ROOT 0x1630 /* Also F19H M50H */
11651c69c86SXin LI #define DEVICEID_AMD_HOSTB19H_M10H_ROOT 0x14a4
117ecbe99e1SMatthias Lanter #define DEVICEID_AMD_HOSTB19H_M40H_ROOT 0x14b5
118*a9a71513SSimon Wells #define DEVICEID_AMD_HOSTB19H_M60H_ROOT 0x14d8 /* Also F1AH M40H */
119ef3f8aa0SOliver Fromme #define DEVICEID_AMD_HOSTB19H_M70H_ROOT 0x14e8
120454e82d7SRui Paulo
121e49ec461SConrad Meyer static const struct amdtemp_product {
122454e82d7SRui Paulo uint16_t amdtemp_vendorid;
123454e82d7SRui Paulo uint16_t amdtemp_deviceid;
124e49ec461SConrad Meyer /*
125e49ec461SConrad Meyer * 0xFC register is only valid on the D18F3 PCI device; SMN temp
126e49ec461SConrad Meyer * drivers do not attach to that device.
127e49ec461SConrad Meyer */
128e49ec461SConrad Meyer bool amdtemp_has_cpuid;
129454e82d7SRui Paulo } amdtemp_products[] = {
130e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC0F, true },
131e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC10, true },
132e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC11, true },
133e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC14, true },
134e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC15, true },
135e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC15_M10H, true },
136e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC15_M30H, true },
137e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC15_M60H_ROOT, false },
138e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC16, true },
139e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC16_M30H, true },
140e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_HOSTB17H_ROOT, false },
141e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_HOSTB17H_M10H_ROOT, false },
14285dbddbeSConrad Meyer { VENDORID_AMD, DEVICEID_AMD_HOSTB17H_M30H_ROOT, false },
1435b505170SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_HOSTB17H_M60H_ROOT, false },
14451c69c86SXin LI { VENDORID_AMD, DEVICEID_AMD_HOSTB19H_M10H_ROOT, false },
145ecbe99e1SMatthias Lanter { VENDORID_AMD, DEVICEID_AMD_HOSTB19H_M40H_ROOT, false },
146323a94afSAkio Morita { VENDORID_AMD, DEVICEID_AMD_HOSTB19H_M60H_ROOT, false },
147ef3f8aa0SOliver Fromme { VENDORID_AMD, DEVICEID_AMD_HOSTB19H_M70H_ROOT, false },
148454e82d7SRui Paulo };
149454e82d7SRui Paulo
150454e82d7SRui Paulo /*
151e49ec461SConrad Meyer * Reported Temperature Control Register, family 0Fh-15h (some models), 16h.
152454e82d7SRui Paulo */
153a4165bbaSJung-uk Kim #define AMDTEMP_REPTMP_CTRL 0xa4
154454e82d7SRui Paulo
155e49ec461SConrad Meyer #define AMDTEMP_REPTMP10H_CURTMP_MASK 0x7ff
156e49ec461SConrad Meyer #define AMDTEMP_REPTMP10H_CURTMP_SHIFT 21
157e49ec461SConrad Meyer #define AMDTEMP_REPTMP10H_TJSEL_MASK 0x3
158e49ec461SConrad Meyer #define AMDTEMP_REPTMP10H_TJSEL_SHIFT 16
159e49ec461SConrad Meyer
160e49ec461SConrad Meyer /*
161e49ec461SConrad Meyer * Reported Temperature, Family 15h, M60+
162e49ec461SConrad Meyer *
163e49ec461SConrad Meyer * Same register bit definitions as other Family 15h CPUs, but access is
164e49ec461SConrad Meyer * indirect via SMN, like Family 17h.
165e49ec461SConrad Meyer */
166e49ec461SConrad Meyer #define AMDTEMP_15H_M60H_REPTMP_CTRL 0xd8200ca4
167e49ec461SConrad Meyer
168454e82d7SRui Paulo /*
169a03d621bSConrad Meyer * Reported Temperature, Family 17h
170fbd5d782SConrad Meyer *
171fbd5d782SConrad Meyer * According to AMD OSRR for 17H, section 4.2.1, bits 31-21 of this register
172fbd5d782SConrad Meyer * provide the current temp. bit 19, when clear, means the temp is reported in
173fbd5d782SConrad Meyer * a range 0.."225C" (probable typo for 255C), and when set changes the range
174fbd5d782SConrad Meyer * to -49..206C.
175a03d621bSConrad Meyer */
176a03d621bSConrad Meyer #define AMDTEMP_17H_CUR_TMP 0x59800
177c59b9a4fSConrad Meyer #define AMDTEMP_17H_CUR_TMP_RANGE_SEL (1u << 19)
178c59b9a4fSConrad Meyer /*
179c1cbabe8SVal Packett * Bits 16-17, when set, mean that CUR_TMP is read-write. When it is, the
180c1cbabe8SVal Packett * 49 degree offset should apply as well. This was revealed in a Linux
181c1cbabe8SVal Packett * patch from an AMD employee.
182c1cbabe8SVal Packett */
183c1cbabe8SVal Packett #define AMDTEMP_17H_CUR_TMP_TJ_SEL ((1u << 17) | (1u << 16))
184c1cbabe8SVal Packett /*
185c59b9a4fSConrad Meyer * The following register set was discovered experimentally by Ondrej Čerman
186c59b9a4fSConrad Meyer * and collaborators, but is not (yet) documented in a PPR/OSRR (other than
187c59b9a4fSConrad Meyer * the M70H PPR SMN memory map showing [0x59800, +0x314] as allocated to
188c59b9a4fSConrad Meyer * SMU::THM). It seems plausible and the Linux sensor folks have adopted it.
189c59b9a4fSConrad Meyer */
190c59b9a4fSConrad Meyer #define AMDTEMP_17H_CCD_TMP_BASE 0x59954
191c59b9a4fSConrad Meyer #define AMDTEMP_17H_CCD_TMP_VALID (1u << 11)
192e49ec461SConrad Meyer
19351c69c86SXin LI #define AMDTEMP_ZEN4_10H_CCD_TMP_BASE 0x59b00
194323a94afSAkio Morita #define AMDTEMP_ZEN4_CCD_TMP_BASE 0x59b08
195323a94afSAkio Morita
196e49ec461SConrad Meyer /*
197e49ec461SConrad Meyer * AMD temperature range adjustment, in deciKelvins (i.e., 49.0 Celsius).
198e49ec461SConrad Meyer */
199e49ec461SConrad Meyer #define AMDTEMP_CURTMP_RANGE_ADJUST 490
200a03d621bSConrad Meyer
201a03d621bSConrad Meyer /*
202074d80acSJung-uk Kim * Thermaltrip Status Register (Family 0Fh only)
203454e82d7SRui Paulo */
204a4165bbaSJung-uk Kim #define AMDTEMP_THERMTP_STAT 0xe4
205074d80acSJung-uk Kim #define AMDTEMP_TTSR_SELCORE 0x04
206074d80acSJung-uk Kim #define AMDTEMP_TTSR_SELSENSOR 0x40
207074d80acSJung-uk Kim
208074d80acSJung-uk Kim /*
209074d80acSJung-uk Kim * DRAM Configuration High Register
210074d80acSJung-uk Kim */
211074d80acSJung-uk Kim #define AMDTEMP_DRAM_CONF_HIGH 0x94 /* Function 2 */
212074d80acSJung-uk Kim #define AMDTEMP_DRAM_MODE_DDR3 0x0100
213454e82d7SRui Paulo
214a4165bbaSJung-uk Kim /*
215a4165bbaSJung-uk Kim * CPU Family/Model Register
216a4165bbaSJung-uk Kim */
217a4165bbaSJung-uk Kim #define AMDTEMP_CPUID 0xfc
218fc1f75e5SRui Paulo
219fc1f75e5SRui Paulo /*
220fc1f75e5SRui Paulo * Device methods.
221fc1f75e5SRui Paulo */
222454e82d7SRui Paulo static void amdtemp_identify(driver_t *driver, device_t parent);
223454e82d7SRui Paulo static int amdtemp_probe(device_t dev);
224454e82d7SRui Paulo static int amdtemp_attach(device_t dev);
225454e82d7SRui Paulo static void amdtemp_intrhook(void *arg);
226454e82d7SRui Paulo static int amdtemp_detach(device_t dev);
227454e82d7SRui Paulo static int32_t amdtemp_gettemp0f(device_t dev, amdsensor_t sensor);
228454e82d7SRui Paulo static int32_t amdtemp_gettemp(device_t dev, amdsensor_t sensor);
229e49ec461SConrad Meyer static int32_t amdtemp_gettemp15hm60h(device_t dev, amdsensor_t sensor);
230a03d621bSConrad Meyer static int32_t amdtemp_gettemp17h(device_t dev, amdsensor_t sensor);
231c59b9a4fSConrad Meyer static void amdtemp_probe_ccd_sensors17h(device_t dev, uint32_t model);
232ea6189d3SConrad Meyer static void amdtemp_probe_ccd_sensors19h(device_t dev, uint32_t model);
233*a9a71513SSimon Wells static void amdtemp_probe_ccd_sensors1ah(device_t dev, uint32_t model);
234454e82d7SRui Paulo static int amdtemp_sysctl(SYSCTL_HANDLER_ARGS);
235fc1f75e5SRui Paulo
236454e82d7SRui Paulo static device_method_t amdtemp_methods[] = {
237fc1f75e5SRui Paulo /* Device interface */
238454e82d7SRui Paulo DEVMETHOD(device_identify, amdtemp_identify),
239454e82d7SRui Paulo DEVMETHOD(device_probe, amdtemp_probe),
240454e82d7SRui Paulo DEVMETHOD(device_attach, amdtemp_attach),
241454e82d7SRui Paulo DEVMETHOD(device_detach, amdtemp_detach),
242fc1f75e5SRui Paulo
24361bfd867SSofian Brabez DEVMETHOD_END
244fc1f75e5SRui Paulo };
245fc1f75e5SRui Paulo
246454e82d7SRui Paulo static driver_t amdtemp_driver = {
247454e82d7SRui Paulo "amdtemp",
248454e82d7SRui Paulo amdtemp_methods,
249454e82d7SRui Paulo sizeof(struct amdtemp_softc),
250fc1f75e5SRui Paulo };
251fc1f75e5SRui Paulo
25283a273efSJohn Baldwin DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, NULL, NULL);
253a03d621bSConrad Meyer MODULE_VERSION(amdtemp, 1);
254a03d621bSConrad Meyer MODULE_DEPEND(amdtemp, amdsmn, 1, 1, 1);
255a64bf59cSConrad Meyer MODULE_PNP_INFO("U16:vendor;U16:device", pci, amdtemp, amdtemp_products,
256329e817fSWarner Losh nitems(amdtemp_products));
257fc1f75e5SRui Paulo
258e49ec461SConrad Meyer static bool
amdtemp_match(device_t dev,const struct amdtemp_product ** product_out)259e49ec461SConrad Meyer amdtemp_match(device_t dev, const struct amdtemp_product **product_out)
260fc1f75e5SRui Paulo {
261fc1f75e5SRui Paulo int i;
262fc1f75e5SRui Paulo uint16_t vendor, devid;
263fc1f75e5SRui Paulo
264fc1f75e5SRui Paulo vendor = pci_get_vendor(dev);
265fc1f75e5SRui Paulo devid = pci_get_device(dev);
266fc1f75e5SRui Paulo
267a64bf59cSConrad Meyer for (i = 0; i < nitems(amdtemp_products); i++) {
268454e82d7SRui Paulo if (vendor == amdtemp_products[i].amdtemp_vendorid &&
269e49ec461SConrad Meyer devid == amdtemp_products[i].amdtemp_deviceid) {
270e49ec461SConrad Meyer if (product_out != NULL)
271e49ec461SConrad Meyer *product_out = &amdtemp_products[i];
272e49ec461SConrad Meyer return (true);
273fc1f75e5SRui Paulo }
274e49ec461SConrad Meyer }
275e49ec461SConrad Meyer return (false);
276fc1f75e5SRui Paulo }
277fc1f75e5SRui Paulo
278fc1f75e5SRui Paulo static void
amdtemp_identify(driver_t * driver,device_t parent)279454e82d7SRui Paulo amdtemp_identify(driver_t *driver, device_t parent)
280fc1f75e5SRui Paulo {
281fc1f75e5SRui Paulo device_t child;
282fc1f75e5SRui Paulo
283fc1f75e5SRui Paulo /* Make sure we're not being doubly invoked. */
284454e82d7SRui Paulo if (device_find_child(parent, "amdtemp", -1) != NULL)
285fc1f75e5SRui Paulo return;
286fc1f75e5SRui Paulo
287e49ec461SConrad Meyer if (amdtemp_match(parent, NULL)) {
2885b56413dSWarner Losh child = device_add_child(parent, "amdtemp", DEVICE_UNIT_ANY);
289fc1f75e5SRui Paulo if (child == NULL)
290454e82d7SRui Paulo device_printf(parent, "add amdtemp child failed\n");
291fc1f75e5SRui Paulo }
292fc1f75e5SRui Paulo }
293fc1f75e5SRui Paulo
294fc1f75e5SRui Paulo static int
amdtemp_probe(device_t dev)295454e82d7SRui Paulo amdtemp_probe(device_t dev)
296fc1f75e5SRui Paulo {
297fdfa6079SJung-uk Kim uint32_t family, model;
298fc1f75e5SRui Paulo
299a8de37b0SEitan Adler if (resource_disabled("amdtemp", 0))
300a8de37b0SEitan Adler return (ENXIO);
301e49ec461SConrad Meyer if (!amdtemp_match(device_get_parent(dev), NULL))
30240f7bccbSConrad Meyer return (ENXIO);
303a8de37b0SEitan Adler
304fdfa6079SJung-uk Kim family = CPUID_TO_FAMILY(cpu_id);
305fdfa6079SJung-uk Kim model = CPUID_TO_MODEL(cpu_id);
306a4165bbaSJung-uk Kim
307a4165bbaSJung-uk Kim switch (family) {
308a4165bbaSJung-uk Kim case 0x0f:
309fdfa6079SJung-uk Kim if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) ||
310fdfa6079SJung-uk Kim (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1))
311a4165bbaSJung-uk Kim return (ENXIO);
312a4165bbaSJung-uk Kim break;
313a4165bbaSJung-uk Kim case 0x10:
314a4165bbaSJung-uk Kim case 0x11:
315074d80acSJung-uk Kim case 0x12:
316074d80acSJung-uk Kim case 0x14:
317074d80acSJung-uk Kim case 0x15:
3182b56f12bSChristian Brueffer case 0x16:
319a03d621bSConrad Meyer case 0x17:
320ea6189d3SConrad Meyer case 0x19:
321*a9a71513SSimon Wells case 0x1a:
322a4165bbaSJung-uk Kim break;
323a4165bbaSJung-uk Kim default:
324fc1f75e5SRui Paulo return (ENXIO);
325fc1f75e5SRui Paulo }
326a4165bbaSJung-uk Kim device_set_desc(dev, "AMD CPU On-Die Thermal Sensors");
327fc1f75e5SRui Paulo
328fc1f75e5SRui Paulo return (BUS_PROBE_GENERIC);
329fc1f75e5SRui Paulo }
330fc1f75e5SRui Paulo
331fc1f75e5SRui Paulo static int
amdtemp_attach(device_t dev)332454e82d7SRui Paulo amdtemp_attach(device_t dev)
333fc1f75e5SRui Paulo {
334074d80acSJung-uk Kim char tn[32];
335074d80acSJung-uk Kim u_int regs[4];
336e49ec461SConrad Meyer const struct amdtemp_product *product;
337e49ec461SConrad Meyer struct amdtemp_softc *sc;
338fc1f75e5SRui Paulo struct sysctl_ctx_list *sysctlctx;
339fc1f75e5SRui Paulo struct sysctl_oid *sysctlnode;
340a4165bbaSJung-uk Kim uint32_t cpuid, family, model;
341074d80acSJung-uk Kim u_int bid;
342074d80acSJung-uk Kim int erratum319, unit;
343e49ec461SConrad Meyer bool needsmn;
344fc1f75e5SRui Paulo
345e49ec461SConrad Meyer sc = device_get_softc(dev);
346074d80acSJung-uk Kim erratum319 = 0;
347e49ec461SConrad Meyer needsmn = false;
348fdfa6079SJung-uk Kim
349e49ec461SConrad Meyer if (!amdtemp_match(device_get_parent(dev), &product))
350e49ec461SConrad Meyer return (ENXIO);
351e49ec461SConrad Meyer
352074d80acSJung-uk Kim cpuid = cpu_id;
353074d80acSJung-uk Kim family = CPUID_TO_FAMILY(cpuid);
354074d80acSJung-uk Kim model = CPUID_TO_MODEL(cpuid);
355e49ec461SConrad Meyer
356e49ec461SConrad Meyer /*
357e49ec461SConrad Meyer * This checks for the byzantine condition of running a heterogenous
358e49ec461SConrad Meyer * revision multi-socket system where the attach thread is potentially
359e49ec461SConrad Meyer * probing a remote socket's PCI device.
360e49ec461SConrad Meyer *
361e49ec461SConrad Meyer * Currently, such scenarios are unsupported on models using the SMN
362e49ec461SConrad Meyer * (because on those models, amdtemp(4) attaches to a different PCI
363e49ec461SConrad Meyer * device than the one that contains AMDTEMP_CPUID).
364e49ec461SConrad Meyer *
365e49ec461SConrad Meyer * The ancient 0x0F family of devices only supports this register from
366e49ec461SConrad Meyer * models 40h+.
367e49ec461SConrad Meyer */
368e49ec461SConrad Meyer if (product->amdtemp_has_cpuid && (family > 0x0f ||
369e49ec461SConrad Meyer (family == 0x0f && model >= 0x40))) {
370e49ec461SConrad Meyer cpuid = pci_read_config(device_get_parent(dev), AMDTEMP_CPUID,
371e49ec461SConrad Meyer 4);
372a4165bbaSJung-uk Kim family = CPUID_TO_FAMILY(cpuid);
373a4165bbaSJung-uk Kim model = CPUID_TO_MODEL(cpuid);
374fdfa6079SJung-uk Kim }
375a4165bbaSJung-uk Kim
376a4165bbaSJung-uk Kim switch (family) {
377a4165bbaSJung-uk Kim case 0x0f:
378a4165bbaSJung-uk Kim /*
379fdfa6079SJung-uk Kim * Thermaltrip Status Register
380fdfa6079SJung-uk Kim *
381fdfa6079SJung-uk Kim * - ThermSenseCoreSel
382fdfa6079SJung-uk Kim *
383fdfa6079SJung-uk Kim * Revision F & G: 0 - Core1, 1 - Core0
384fdfa6079SJung-uk Kim * Other: 0 - Core0, 1 - Core1
385fdfa6079SJung-uk Kim *
386fdfa6079SJung-uk Kim * - CurTmp
387a4165bbaSJung-uk Kim *
388a4165bbaSJung-uk Kim * Revision G: bits 23-14
389fdfa6079SJung-uk Kim * Other: bits 23-16
390a4165bbaSJung-uk Kim *
391fdfa6079SJung-uk Kim * XXX According to the BKDG, CurTmp, ThermSenseSel and
392fdfa6079SJung-uk Kim * ThermSenseCoreSel bits were introduced in Revision F
393fdfa6079SJung-uk Kim * but CurTmp seems working fine as early as Revision C.
394fdfa6079SJung-uk Kim * However, it is not clear whether ThermSenseSel and/or
395fdfa6079SJung-uk Kim * ThermSenseCoreSel work in undocumented cases as well.
396fdfa6079SJung-uk Kim * In fact, the Linux driver suggests it may not work but
397fdfa6079SJung-uk Kim * we just assume it does until we find otherwise.
398074d80acSJung-uk Kim *
399074d80acSJung-uk Kim * XXX According to Linux, CurTmp starts at -28C on
400074d80acSJung-uk Kim * Socket AM2 Revision G processors, which is not
401074d80acSJung-uk Kim * documented anywhere.
402fc1f75e5SRui Paulo */
403074d80acSJung-uk Kim if (model >= 0x40)
404fdfa6079SJung-uk Kim sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP;
405074d80acSJung-uk Kim if (model >= 0x60 && model != 0xc1) {
406074d80acSJung-uk Kim do_cpuid(0x80000001, regs);
407074d80acSJung-uk Kim bid = (regs[1] >> 9) & 0x1f;
408074d80acSJung-uk Kim switch (model) {
409074d80acSJung-uk Kim case 0x68: /* Socket S1g1 */
410074d80acSJung-uk Kim case 0x6c:
411074d80acSJung-uk Kim case 0x7c:
412074d80acSJung-uk Kim break;
413074d80acSJung-uk Kim case 0x6b: /* Socket AM2 and ASB1 (2 cores) */
414074d80acSJung-uk Kim if (bid != 0x0b && bid != 0x0c)
415074d80acSJung-uk Kim sc->sc_flags |=
416074d80acSJung-uk Kim AMDTEMP_FLAG_ALT_OFFSET;
417074d80acSJung-uk Kim break;
418074d80acSJung-uk Kim case 0x6f: /* Socket AM2 and ASB1 (1 core) */
419074d80acSJung-uk Kim case 0x7f:
420074d80acSJung-uk Kim if (bid != 0x07 && bid != 0x09 &&
421074d80acSJung-uk Kim bid != 0x0c)
422074d80acSJung-uk Kim sc->sc_flags |=
423074d80acSJung-uk Kim AMDTEMP_FLAG_ALT_OFFSET;
424074d80acSJung-uk Kim break;
425074d80acSJung-uk Kim default:
426074d80acSJung-uk Kim sc->sc_flags |= AMDTEMP_FLAG_ALT_OFFSET;
427074d80acSJung-uk Kim }
428fdfa6079SJung-uk Kim sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT;
429fdfa6079SJung-uk Kim }
430a4165bbaSJung-uk Kim
431a4165bbaSJung-uk Kim /*
432a4165bbaSJung-uk Kim * There are two sensors per core.
433a4165bbaSJung-uk Kim */
434a4165bbaSJung-uk Kim sc->sc_ntemps = 2;
435a4165bbaSJung-uk Kim
436a4165bbaSJung-uk Kim sc->sc_gettemp = amdtemp_gettemp0f;
437a4165bbaSJung-uk Kim break;
438a4165bbaSJung-uk Kim case 0x10:
439074d80acSJung-uk Kim /*
440074d80acSJung-uk Kim * Erratum 319 Inaccurate Temperature Measurement
441074d80acSJung-uk Kim *
442074d80acSJung-uk Kim * http://support.amd.com/us/Processor_TechDocs/41322.pdf
443074d80acSJung-uk Kim */
444074d80acSJung-uk Kim do_cpuid(0x80000001, regs);
445074d80acSJung-uk Kim switch ((regs[1] >> 28) & 0xf) {
446074d80acSJung-uk Kim case 0: /* Socket F */
447074d80acSJung-uk Kim erratum319 = 1;
448074d80acSJung-uk Kim break;
449074d80acSJung-uk Kim case 1: /* Socket AM2+ or AM3 */
4501587a9dbSJohn Baldwin if ((pci_cfgregread(pci_get_domain(dev),
4511587a9dbSJohn Baldwin pci_get_bus(dev), pci_get_slot(dev), 2,
4521587a9dbSJohn Baldwin AMDTEMP_DRAM_CONF_HIGH, 2) &
453074d80acSJung-uk Kim AMDTEMP_DRAM_MODE_DDR3) != 0 || model > 0x04 ||
454074d80acSJung-uk Kim (model == 0x04 && (cpuid & CPUID_STEPPING) >= 3))
455074d80acSJung-uk Kim break;
456074d80acSJung-uk Kim /* XXX 00100F42h (RB-C2) exists in both formats. */
457074d80acSJung-uk Kim erratum319 = 1;
458074d80acSJung-uk Kim break;
459074d80acSJung-uk Kim }
460074d80acSJung-uk Kim /* FALLTHROUGH */
461a4165bbaSJung-uk Kim case 0x11:
462074d80acSJung-uk Kim case 0x12:
463074d80acSJung-uk Kim case 0x14:
464074d80acSJung-uk Kim case 0x15:
4652b56f12bSChristian Brueffer case 0x16:
466a4165bbaSJung-uk Kim sc->sc_ntemps = 1;
467e49ec461SConrad Meyer /*
468e49ec461SConrad Meyer * Some later (60h+) models of family 15h use a similar SMN
469e49ec461SConrad Meyer * network as family 17h. (However, the register index differs
470e49ec461SConrad Meyer * from 17h and the decoding matches other 10h-15h models,
471e49ec461SConrad Meyer * which differ from 17h.)
472e49ec461SConrad Meyer */
473e49ec461SConrad Meyer if (family == 0x15 && model >= 0x60) {
474e49ec461SConrad Meyer sc->sc_gettemp = amdtemp_gettemp15hm60h;
475e49ec461SConrad Meyer needsmn = true;
476e49ec461SConrad Meyer } else
477a4165bbaSJung-uk Kim sc->sc_gettemp = amdtemp_gettemp;
478a4165bbaSJung-uk Kim break;
479a03d621bSConrad Meyer case 0x17:
480ea6189d3SConrad Meyer case 0x19:
481*a9a71513SSimon Wells case 0x1a:
482a03d621bSConrad Meyer sc->sc_ntemps = 1;
483a03d621bSConrad Meyer sc->sc_gettemp = amdtemp_gettemp17h;
484e49ec461SConrad Meyer needsmn = true;
485e49ec461SConrad Meyer break;
486e49ec461SConrad Meyer default:
487e49ec461SConrad Meyer device_printf(dev, "Bogus family 0x%x\n", family);
488e49ec461SConrad Meyer return (ENXIO);
489e49ec461SConrad Meyer }
490e49ec461SConrad Meyer
491e49ec461SConrad Meyer if (needsmn) {
492a03d621bSConrad Meyer sc->sc_smn = device_find_child(
493a03d621bSConrad Meyer device_get_parent(dev), "amdsmn", -1);
494a03d621bSConrad Meyer if (sc->sc_smn == NULL) {
495a03d621bSConrad Meyer if (bootverbose)
496a03d621bSConrad Meyer device_printf(dev, "No SMN device found\n");
497a03d621bSConrad Meyer return (ENXIO);
498a03d621bSConrad Meyer }
499fc1f75e5SRui Paulo }
500fc1f75e5SRui Paulo
501a4165bbaSJung-uk Kim /* Find number of cores per package. */
502a4165bbaSJung-uk Kim sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ?
503a4165bbaSJung-uk Kim (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1;
504a4165bbaSJung-uk Kim if (sc->sc_ncores > MAXCPU)
505a4165bbaSJung-uk Kim return (ENXIO);
506a4165bbaSJung-uk Kim
5076c101ed7SAlexander Motin mtx_init(&sc->sc_lock, "amdtemp", NULL, MTX_DEF);
508074d80acSJung-uk Kim if (erratum319)
509074d80acSJung-uk Kim device_printf(dev,
510074d80acSJung-uk Kim "Erratum 319: temperature measurement may be inaccurate\n");
511a4165bbaSJung-uk Kim if (bootverbose)
512a4165bbaSJung-uk Kim device_printf(dev, "Found %d cores and %d sensors.\n",
513a4165bbaSJung-uk Kim sc->sc_ncores,
514a4165bbaSJung-uk Kim sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1);
515454e82d7SRui Paulo
516fc1f75e5SRui Paulo /*
517454e82d7SRui Paulo * dev.amdtemp.N tree.
518fc1f75e5SRui Paulo */
519074d80acSJung-uk Kim unit = device_get_unit(dev);
520074d80acSJung-uk Kim snprintf(tn, sizeof(tn), "dev.amdtemp.%d.sensor_offset", unit);
521074d80acSJung-uk Kim TUNABLE_INT_FETCH(tn, &sc->sc_offset);
522074d80acSJung-uk Kim
523fc1f75e5SRui Paulo sysctlctx = device_get_sysctl_ctx(dev);
524074d80acSJung-uk Kim SYSCTL_ADD_INT(sysctlctx,
525074d80acSJung-uk Kim SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
526074d80acSJung-uk Kim "sensor_offset", CTLFLAG_RW, &sc->sc_offset, 0,
527074d80acSJung-uk Kim "Temperature sensor offset");
528fc1f75e5SRui Paulo sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
529a4165bbaSJung-uk Kim SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
5307029da5cSPawel Biernacki "core0", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Core 0");
531fc1f75e5SRui Paulo
532fc1f75e5SRui Paulo SYSCTL_ADD_PROC(sysctlctx,
533fc1f75e5SRui Paulo SYSCTL_CHILDREN(sysctlnode),
5347029da5cSPawel Biernacki OID_AUTO, "sensor0",
5356c101ed7SAlexander Motin CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
536074d80acSJung-uk Kim dev, CORE0_SENSOR0, amdtemp_sysctl, "IK",
537074d80acSJung-uk Kim "Core 0 / Sensor 0 temperature");
538fc1f75e5SRui Paulo
539323a94afSAkio Morita sc->sc_temp_base = AMDTEMP_17H_CCD_TMP_BASE;
540323a94afSAkio Morita
541c59b9a4fSConrad Meyer if (family == 0x17)
542c59b9a4fSConrad Meyer amdtemp_probe_ccd_sensors17h(dev, model);
543ea6189d3SConrad Meyer else if (family == 0x19)
544ea6189d3SConrad Meyer amdtemp_probe_ccd_sensors19h(dev, model);
545*a9a71513SSimon Wells else if (family == 0x1a)
546*a9a71513SSimon Wells amdtemp_probe_ccd_sensors1ah(dev, model);
547c59b9a4fSConrad Meyer else if (sc->sc_ntemps > 1) {
548fc1f75e5SRui Paulo SYSCTL_ADD_PROC(sysctlctx,
549fc1f75e5SRui Paulo SYSCTL_CHILDREN(sysctlnode),
5507029da5cSPawel Biernacki OID_AUTO, "sensor1",
5516c101ed7SAlexander Motin CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
552074d80acSJung-uk Kim dev, CORE0_SENSOR1, amdtemp_sysctl, "IK",
553074d80acSJung-uk Kim "Core 0 / Sensor 1 temperature");
554fc1f75e5SRui Paulo
555074d80acSJung-uk Kim if (sc->sc_ncores > 1) {
556fc1f75e5SRui Paulo sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
557074d80acSJung-uk Kim SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
5587029da5cSPawel Biernacki OID_AUTO, "core1", CTLFLAG_RD | CTLFLAG_MPSAFE,
5597029da5cSPawel Biernacki 0, "Core 1");
560fc1f75e5SRui Paulo
561fc1f75e5SRui Paulo SYSCTL_ADD_PROC(sysctlctx,
562fc1f75e5SRui Paulo SYSCTL_CHILDREN(sysctlnode),
5637029da5cSPawel Biernacki OID_AUTO, "sensor0",
5646c101ed7SAlexander Motin CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
565074d80acSJung-uk Kim dev, CORE1_SENSOR0, amdtemp_sysctl, "IK",
566074d80acSJung-uk Kim "Core 1 / Sensor 0 temperature");
567fc1f75e5SRui Paulo
568fc1f75e5SRui Paulo SYSCTL_ADD_PROC(sysctlctx,
569fc1f75e5SRui Paulo SYSCTL_CHILDREN(sysctlnode),
5707029da5cSPawel Biernacki OID_AUTO, "sensor1",
5716c101ed7SAlexander Motin CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
572074d80acSJung-uk Kim dev, CORE1_SENSOR1, amdtemp_sysctl, "IK",
573074d80acSJung-uk Kim "Core 1 / Sensor 1 temperature");
574074d80acSJung-uk Kim }
575a4165bbaSJung-uk Kim }
576a4165bbaSJung-uk Kim
577a4165bbaSJung-uk Kim /*
578a4165bbaSJung-uk Kim * Try to create dev.cpu sysctl entries and setup intrhook function.
579a4165bbaSJung-uk Kim * This is needed because the cpu driver may be loaded late on boot,
580a4165bbaSJung-uk Kim * after us.
581a4165bbaSJung-uk Kim */
582a4165bbaSJung-uk Kim amdtemp_intrhook(dev);
583a4165bbaSJung-uk Kim sc->sc_ich.ich_func = amdtemp_intrhook;
584a4165bbaSJung-uk Kim sc->sc_ich.ich_arg = dev;
585a4165bbaSJung-uk Kim if (config_intrhook_establish(&sc->sc_ich) != 0) {
586a4165bbaSJung-uk Kim device_printf(dev, "config_intrhook_establish failed!\n");
587a4165bbaSJung-uk Kim return (ENXIO);
588a4165bbaSJung-uk Kim }
589fc1f75e5SRui Paulo
590fc1f75e5SRui Paulo return (0);
591fc1f75e5SRui Paulo }
592fc1f75e5SRui Paulo
593fc1f75e5SRui Paulo void
amdtemp_intrhook(void * arg)594454e82d7SRui Paulo amdtemp_intrhook(void *arg)
595fc1f75e5SRui Paulo {
596454e82d7SRui Paulo struct amdtemp_softc *sc;
597fc1f75e5SRui Paulo struct sysctl_ctx_list *sysctlctx;
598a4165bbaSJung-uk Kim device_t dev = (device_t)arg;
599a4165bbaSJung-uk Kim device_t acpi, cpu, nexus;
600a4165bbaSJung-uk Kim amdsensor_t sensor;
601a4165bbaSJung-uk Kim int i;
602fc1f75e5SRui Paulo
603fc1f75e5SRui Paulo sc = device_get_softc(dev);
604fc1f75e5SRui Paulo
605fc1f75e5SRui Paulo /*
606fc1f75e5SRui Paulo * dev.cpu.N.temperature.
607fc1f75e5SRui Paulo */
608fc1f75e5SRui Paulo nexus = device_find_child(root_bus, "nexus", 0);
609fc1f75e5SRui Paulo acpi = device_find_child(nexus, "acpi", 0);
610fc1f75e5SRui Paulo
611a4165bbaSJung-uk Kim for (i = 0; i < sc->sc_ncores; i++) {
612a4165bbaSJung-uk Kim if (sc->sc_sysctl_cpu[i] != NULL)
613a4165bbaSJung-uk Kim continue;
614fc1f75e5SRui Paulo cpu = device_find_child(acpi, "cpu",
615a4165bbaSJung-uk Kim device_get_unit(dev) * sc->sc_ncores + i);
616a4165bbaSJung-uk Kim if (cpu != NULL) {
617fc1f75e5SRui Paulo sysctlctx = device_get_sysctl_ctx(cpu);
618fc1f75e5SRui Paulo
619a4165bbaSJung-uk Kim sensor = sc->sc_ntemps > 1 ?
620074d80acSJung-uk Kim (i == 0 ? CORE0 : CORE1) : CORE0_SENSOR0;
621fc1f75e5SRui Paulo sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
622fc1f75e5SRui Paulo SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
6237029da5cSPawel Biernacki OID_AUTO, "temperature",
6246c101ed7SAlexander Motin CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
625a4165bbaSJung-uk Kim dev, sensor, amdtemp_sysctl, "IK",
626a4165bbaSJung-uk Kim "Current temparature");
627fc1f75e5SRui Paulo }
628fc1f75e5SRui Paulo }
629a4165bbaSJung-uk Kim if (sc->sc_ich.ich_arg != NULL)
630fc1f75e5SRui Paulo config_intrhook_disestablish(&sc->sc_ich);
631fc1f75e5SRui Paulo }
632fc1f75e5SRui Paulo
633fc1f75e5SRui Paulo int
amdtemp_detach(device_t dev)634454e82d7SRui Paulo amdtemp_detach(device_t dev)
635fc1f75e5SRui Paulo {
636454e82d7SRui Paulo struct amdtemp_softc *sc = device_get_softc(dev);
637a4165bbaSJung-uk Kim int i;
638fc1f75e5SRui Paulo
639a4165bbaSJung-uk Kim for (i = 0; i < sc->sc_ncores; i++)
640a4165bbaSJung-uk Kim if (sc->sc_sysctl_cpu[i] != NULL)
641fc1f75e5SRui Paulo sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
642fc1f75e5SRui Paulo
643454e82d7SRui Paulo /* NewBus removes the dev.amdtemp.N tree by itself. */
644fc1f75e5SRui Paulo
6456c101ed7SAlexander Motin mtx_destroy(&sc->sc_lock);
646fc1f75e5SRui Paulo return (0);
647fc1f75e5SRui Paulo }
648fc1f75e5SRui Paulo
649fc1f75e5SRui Paulo static int
amdtemp_sysctl(SYSCTL_HANDLER_ARGS)650454e82d7SRui Paulo amdtemp_sysctl(SYSCTL_HANDLER_ARGS)
651fc1f75e5SRui Paulo {
652fc1f75e5SRui Paulo device_t dev = (device_t)arg1;
653454e82d7SRui Paulo struct amdtemp_softc *sc = device_get_softc(dev);
654a4165bbaSJung-uk Kim amdsensor_t sensor = (amdsensor_t)arg2;
655a4165bbaSJung-uk Kim int32_t auxtemp[2], temp;
656fc1f75e5SRui Paulo int error;
657fc1f75e5SRui Paulo
658a4165bbaSJung-uk Kim switch (sensor) {
659fc1f75e5SRui Paulo case CORE0:
660074d80acSJung-uk Kim auxtemp[0] = sc->sc_gettemp(dev, CORE0_SENSOR0);
661074d80acSJung-uk Kim auxtemp[1] = sc->sc_gettemp(dev, CORE0_SENSOR1);
662fc1f75e5SRui Paulo temp = imax(auxtemp[0], auxtemp[1]);
663fc1f75e5SRui Paulo break;
664fc1f75e5SRui Paulo case CORE1:
665074d80acSJung-uk Kim auxtemp[0] = sc->sc_gettemp(dev, CORE1_SENSOR0);
666074d80acSJung-uk Kim auxtemp[1] = sc->sc_gettemp(dev, CORE1_SENSOR1);
667fc1f75e5SRui Paulo temp = imax(auxtemp[0], auxtemp[1]);
668fc1f75e5SRui Paulo break;
669fc1f75e5SRui Paulo default:
670a4165bbaSJung-uk Kim temp = sc->sc_gettemp(dev, sensor);
671fc1f75e5SRui Paulo break;
672fc1f75e5SRui Paulo }
673fc1f75e5SRui Paulo error = sysctl_handle_int(oidp, &temp, 0, req);
674fc1f75e5SRui Paulo
675fc1f75e5SRui Paulo return (error);
676fc1f75e5SRui Paulo }
677fc1f75e5SRui Paulo
6789d6672e1SLuiz Otavio O Souza #define AMDTEMP_ZERO_C_TO_K 2731
679a4165bbaSJung-uk Kim
680fc1f75e5SRui Paulo static int32_t
amdtemp_gettemp0f(device_t dev,amdsensor_t sensor)681454e82d7SRui Paulo amdtemp_gettemp0f(device_t dev, amdsensor_t sensor)
682fc1f75e5SRui Paulo {
683a4165bbaSJung-uk Kim struct amdtemp_softc *sc = device_get_softc(dev);
684074d80acSJung-uk Kim uint32_t mask, offset, temp;
685fc1f75e5SRui Paulo
6866c101ed7SAlexander Motin mtx_lock(&sc->sc_lock);
6876c101ed7SAlexander Motin
688a4165bbaSJung-uk Kim /* Set Sensor/Core selector. */
689074d80acSJung-uk Kim temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1);
690074d80acSJung-uk Kim temp &= ~(AMDTEMP_TTSR_SELCORE | AMDTEMP_TTSR_SELSENSOR);
691fc1f75e5SRui Paulo switch (sensor) {
692074d80acSJung-uk Kim case CORE0_SENSOR1:
693074d80acSJung-uk Kim temp |= AMDTEMP_TTSR_SELSENSOR;
6947ca2d97bSJung-uk Kim /* FALLTHROUGH */
695074d80acSJung-uk Kim case CORE0_SENSOR0:
696a4165bbaSJung-uk Kim case CORE0:
697fdfa6079SJung-uk Kim if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0)
698074d80acSJung-uk Kim temp |= AMDTEMP_TTSR_SELCORE;
699fc1f75e5SRui Paulo break;
700074d80acSJung-uk Kim case CORE1_SENSOR1:
701074d80acSJung-uk Kim temp |= AMDTEMP_TTSR_SELSENSOR;
7027ca2d97bSJung-uk Kim /* FALLTHROUGH */
703074d80acSJung-uk Kim case CORE1_SENSOR0:
704a4165bbaSJung-uk Kim case CORE1:
705fdfa6079SJung-uk Kim if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0)
706074d80acSJung-uk Kim temp |= AMDTEMP_TTSR_SELCORE;
707fc1f75e5SRui Paulo break;
708c59b9a4fSConrad Meyer default:
709c79cee71SKyle Evans __assert_unreachable();
710fc1f75e5SRui Paulo }
711074d80acSJung-uk Kim pci_write_config(dev, AMDTEMP_THERMTP_STAT, temp, 1);
712a4165bbaSJung-uk Kim
713fdfa6079SJung-uk Kim mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc;
714074d80acSJung-uk Kim offset = (sc->sc_flags & AMDTEMP_FLAG_ALT_OFFSET) != 0 ? 28 : 49;
715074d80acSJung-uk Kim temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
716074d80acSJung-uk Kim temp = ((temp >> 14) & mask) * 5 / 2;
717074d80acSJung-uk Kim temp += AMDTEMP_ZERO_C_TO_K + (sc->sc_offset - offset) * 10;
718454e82d7SRui Paulo
7196c101ed7SAlexander Motin mtx_unlock(&sc->sc_lock);
720454e82d7SRui Paulo return (temp);
721454e82d7SRui Paulo }
722454e82d7SRui Paulo
723e49ec461SConrad Meyer static uint32_t
amdtemp_decode_fam10h_to_17h(int32_t sc_offset,uint32_t val,bool minus49)72402f70002SConrad Meyer amdtemp_decode_fam10h_to_17h(int32_t sc_offset, uint32_t val, bool minus49)
725e49ec461SConrad Meyer {
726e49ec461SConrad Meyer uint32_t temp;
727e49ec461SConrad Meyer
728e49ec461SConrad Meyer /* Convert raw register subfield units (0.125C) to units of 0.1C. */
72902f70002SConrad Meyer temp = (val & AMDTEMP_REPTMP10H_CURTMP_MASK) * 5 / 4;
73002f70002SConrad Meyer
73102f70002SConrad Meyer if (minus49)
73202f70002SConrad Meyer temp -= AMDTEMP_CURTMP_RANGE_ADJUST;
73302f70002SConrad Meyer
73402f70002SConrad Meyer temp += AMDTEMP_ZERO_C_TO_K + sc_offset * 10;
73502f70002SConrad Meyer return (temp);
73602f70002SConrad Meyer }
73702f70002SConrad Meyer
73802f70002SConrad Meyer static uint32_t
amdtemp_decode_fam10h_to_16h(int32_t sc_offset,uint32_t val)73902f70002SConrad Meyer amdtemp_decode_fam10h_to_16h(int32_t sc_offset, uint32_t val)
74002f70002SConrad Meyer {
74102f70002SConrad Meyer bool minus49;
742e49ec461SConrad Meyer
743e49ec461SConrad Meyer /*
744e49ec461SConrad Meyer * On Family 15h and higher, if CurTmpTjSel is 11b, the range is
745e49ec461SConrad Meyer * adjusted down by 49.0 degrees Celsius. (This adjustment is not
746e49ec461SConrad Meyer * documented in BKDGs prior to family 15h model 00h.)
747e49ec461SConrad Meyer */
74802f70002SConrad Meyer minus49 = (CPUID_TO_FAMILY(cpu_id) >= 0x15 &&
749e49ec461SConrad Meyer ((val >> AMDTEMP_REPTMP10H_TJSEL_SHIFT) &
75002f70002SConrad Meyer AMDTEMP_REPTMP10H_TJSEL_MASK) == 0x3);
751e49ec461SConrad Meyer
75202f70002SConrad Meyer return (amdtemp_decode_fam10h_to_17h(sc_offset,
75302f70002SConrad Meyer val >> AMDTEMP_REPTMP10H_CURTMP_SHIFT, minus49));
75402f70002SConrad Meyer }
75502f70002SConrad Meyer
75602f70002SConrad Meyer static uint32_t
amdtemp_decode_fam17h_tctl(int32_t sc_offset,uint32_t val)75702f70002SConrad Meyer amdtemp_decode_fam17h_tctl(int32_t sc_offset, uint32_t val)
75802f70002SConrad Meyer {
75902f70002SConrad Meyer bool minus49;
76002f70002SConrad Meyer
761c1cbabe8SVal Packett minus49 = ((val & AMDTEMP_17H_CUR_TMP_RANGE_SEL) != 0)
762c1cbabe8SVal Packett || ((val & AMDTEMP_17H_CUR_TMP_TJ_SEL) == AMDTEMP_17H_CUR_TMP_TJ_SEL);
76302f70002SConrad Meyer return (amdtemp_decode_fam10h_to_17h(sc_offset,
76402f70002SConrad Meyer val >> AMDTEMP_REPTMP10H_CURTMP_SHIFT, minus49));
765e49ec461SConrad Meyer }
766e49ec461SConrad Meyer
767454e82d7SRui Paulo static int32_t
amdtemp_gettemp(device_t dev,amdsensor_t sensor)768454e82d7SRui Paulo amdtemp_gettemp(device_t dev, amdsensor_t sensor)
769454e82d7SRui Paulo {
770074d80acSJung-uk Kim struct amdtemp_softc *sc = device_get_softc(dev);
771454e82d7SRui Paulo uint32_t temp;
772a4165bbaSJung-uk Kim
773a4165bbaSJung-uk Kim temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4);
774e49ec461SConrad Meyer return (amdtemp_decode_fam10h_to_16h(sc->sc_offset, temp));
775e49ec461SConrad Meyer }
776fc1f75e5SRui Paulo
777e49ec461SConrad Meyer static int32_t
amdtemp_gettemp15hm60h(device_t dev,amdsensor_t sensor)778e49ec461SConrad Meyer amdtemp_gettemp15hm60h(device_t dev, amdsensor_t sensor)
779e49ec461SConrad Meyer {
780e49ec461SConrad Meyer struct amdtemp_softc *sc = device_get_softc(dev);
781e49ec461SConrad Meyer uint32_t val;
782b9723c5bSMateusz Guzik int error __diagused;
783e49ec461SConrad Meyer
784e49ec461SConrad Meyer error = amdsmn_read(sc->sc_smn, AMDTEMP_15H_M60H_REPTMP_CTRL, &val);
785e49ec461SConrad Meyer KASSERT(error == 0, ("amdsmn_read"));
786e49ec461SConrad Meyer return (amdtemp_decode_fam10h_to_16h(sc->sc_offset, val));
787fc1f75e5SRui Paulo }
788a03d621bSConrad Meyer
789a03d621bSConrad Meyer static int32_t
amdtemp_gettemp17h(device_t dev,amdsensor_t sensor)790a03d621bSConrad Meyer amdtemp_gettemp17h(device_t dev, amdsensor_t sensor)
791a03d621bSConrad Meyer {
792a03d621bSConrad Meyer struct amdtemp_softc *sc = device_get_softc(dev);
79302f70002SConrad Meyer uint32_t val;
794b9723c5bSMateusz Guzik int error __diagused;
795a03d621bSConrad Meyer
796c59b9a4fSConrad Meyer switch (sensor) {
797c59b9a4fSConrad Meyer case CORE0_SENSOR0:
798c59b9a4fSConrad Meyer /* Tctl */
799fbd5d782SConrad Meyer error = amdsmn_read(sc->sc_smn, AMDTEMP_17H_CUR_TMP, &val);
800a03d621bSConrad Meyer KASSERT(error == 0, ("amdsmn_read"));
80102f70002SConrad Meyer return (amdtemp_decode_fam17h_tctl(sc->sc_offset, val));
802c59b9a4fSConrad Meyer case CCD_BASE ... CCD_MAX:
803c59b9a4fSConrad Meyer /* Tccd<N> */
804323a94afSAkio Morita error = amdsmn_read(sc->sc_smn, sc->sc_temp_base +
805c59b9a4fSConrad Meyer (((int)sensor - CCD_BASE) * sizeof(val)), &val);
806c59b9a4fSConrad Meyer KASSERT(error == 0, ("amdsmn_read2"));
807c59b9a4fSConrad Meyer KASSERT((val & AMDTEMP_17H_CCD_TMP_VALID) != 0,
808c59b9a4fSConrad Meyer ("sensor %d: not valid", (int)sensor));
809c59b9a4fSConrad Meyer return (amdtemp_decode_fam10h_to_17h(sc->sc_offset, val, true));
810c59b9a4fSConrad Meyer default:
811c79cee71SKyle Evans __assert_unreachable();
812c59b9a4fSConrad Meyer }
813c59b9a4fSConrad Meyer }
814c59b9a4fSConrad Meyer
815c59b9a4fSConrad Meyer static void
amdtemp_probe_ccd_sensors(device_t dev,uint32_t maxreg)816ea6189d3SConrad Meyer amdtemp_probe_ccd_sensors(device_t dev, uint32_t maxreg)
817c59b9a4fSConrad Meyer {
818c59b9a4fSConrad Meyer char sensor_name[16], sensor_descr[32];
819c59b9a4fSConrad Meyer struct amdtemp_softc *sc;
820ea6189d3SConrad Meyer uint32_t i, val;
821c59b9a4fSConrad Meyer int error;
822c59b9a4fSConrad Meyer
823c59b9a4fSConrad Meyer sc = device_get_softc(dev);
824c59b9a4fSConrad Meyer for (i = 0; i < maxreg; i++) {
825323a94afSAkio Morita error = amdsmn_read(sc->sc_smn, sc->sc_temp_base +
826c59b9a4fSConrad Meyer (i * sizeof(val)), &val);
827c59b9a4fSConrad Meyer if (error != 0)
828c59b9a4fSConrad Meyer continue;
829c59b9a4fSConrad Meyer if ((val & AMDTEMP_17H_CCD_TMP_VALID) == 0)
830c59b9a4fSConrad Meyer continue;
831c59b9a4fSConrad Meyer
832c59b9a4fSConrad Meyer snprintf(sensor_name, sizeof(sensor_name), "ccd%u", i);
833c59b9a4fSConrad Meyer snprintf(sensor_descr, sizeof(sensor_descr),
834c59b9a4fSConrad Meyer "CCD %u temperature (Tccd%u)", i, i);
835c59b9a4fSConrad Meyer
836c59b9a4fSConrad Meyer SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
837c59b9a4fSConrad Meyer SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
838c59b9a4fSConrad Meyer sensor_name, CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
839c59b9a4fSConrad Meyer dev, CCD_BASE + i, amdtemp_sysctl, "IK", sensor_descr);
840c59b9a4fSConrad Meyer }
841a03d621bSConrad Meyer }
842ea6189d3SConrad Meyer
843ea6189d3SConrad Meyer static void
amdtemp_probe_ccd_sensors17h(device_t dev,uint32_t model)844ea6189d3SConrad Meyer amdtemp_probe_ccd_sensors17h(device_t dev, uint32_t model)
845ea6189d3SConrad Meyer {
846ea6189d3SConrad Meyer uint32_t maxreg;
847ea6189d3SConrad Meyer
848ea6189d3SConrad Meyer switch (model) {
849b499ab87SConrad Meyer case 0x00 ... 0x2f: /* Zen1, Zen+ */
850ea6189d3SConrad Meyer maxreg = 4;
851ea6189d3SConrad Meyer break;
852b499ab87SConrad Meyer case 0x30 ... 0x3f: /* Zen2 TR (Castle Peak)/EPYC (Rome) */
853b499ab87SConrad Meyer case 0x60 ... 0x7f: /* Zen2 Ryzen (Renoir APU, Matisse) */
854b499ab87SConrad Meyer case 0x90 ... 0x9f: /* Zen2 Ryzen (Van Gogh APU) */
855ea6189d3SConrad Meyer maxreg = 8;
856ea6189d3SConrad Meyer _Static_assert((int)NUM_CCDS >= 8, "");
857ea6189d3SConrad Meyer break;
858ea6189d3SConrad Meyer default:
859ea6189d3SConrad Meyer device_printf(dev,
860ea6189d3SConrad Meyer "Unrecognized Family 17h Model: %02xh\n", model);
861ea6189d3SConrad Meyer return;
862ea6189d3SConrad Meyer }
863ea6189d3SConrad Meyer
864ea6189d3SConrad Meyer amdtemp_probe_ccd_sensors(dev, maxreg);
865ea6189d3SConrad Meyer }
866ea6189d3SConrad Meyer
867ea6189d3SConrad Meyer static void
amdtemp_probe_ccd_sensors19h(device_t dev,uint32_t model)868ea6189d3SConrad Meyer amdtemp_probe_ccd_sensors19h(device_t dev, uint32_t model)
869ea6189d3SConrad Meyer {
870323a94afSAkio Morita struct amdtemp_softc *sc = device_get_softc(dev);
871ea6189d3SConrad Meyer uint32_t maxreg;
872ea6189d3SConrad Meyer
873ea6189d3SConrad Meyer switch (model) {
874ea6189d3SConrad Meyer case 0x00 ... 0x0f: /* Zen3 EPYC "Milan" */
875ea6189d3SConrad Meyer case 0x20 ... 0x2f: /* Zen3 Ryzen "Vermeer" */
8767aa6eeb2SSimon Wells case 0x50 ... 0x5f: /* Zen3 Ryzen "Cezanne" */
877ea6189d3SConrad Meyer maxreg = 8;
878ea6189d3SConrad Meyer _Static_assert((int)NUM_CCDS >= 8, "");
879ea6189d3SConrad Meyer break;
88051c69c86SXin LI case 0x10 ... 0x1f:
88151c69c86SXin LI sc->sc_temp_base = AMDTEMP_ZEN4_10H_CCD_TMP_BASE;
88251c69c86SXin LI maxreg = 12;
88351c69c86SXin LI _Static_assert((int)NUM_CCDS >= 12, "");
88451c69c86SXin LI break;
885ecbe99e1SMatthias Lanter case 0x40 ... 0x4f: /* Zen3+ Ryzen "Rembrandt" */
886323a94afSAkio Morita case 0x60 ... 0x6f: /* Zen4 Ryzen "Raphael" */
887ef3f8aa0SOliver Fromme case 0x70 ... 0x7f: /* Zen4 Ryzen "Phoenix" */
888323a94afSAkio Morita sc->sc_temp_base = AMDTEMP_ZEN4_CCD_TMP_BASE;
889323a94afSAkio Morita maxreg = 8;
890323a94afSAkio Morita _Static_assert((int)NUM_CCDS >= 8, "");
891323a94afSAkio Morita break;
892ea6189d3SConrad Meyer default:
893ea6189d3SConrad Meyer device_printf(dev,
894ea6189d3SConrad Meyer "Unrecognized Family 19h Model: %02xh\n", model);
895ea6189d3SConrad Meyer return;
896ea6189d3SConrad Meyer }
897ea6189d3SConrad Meyer
898ea6189d3SConrad Meyer amdtemp_probe_ccd_sensors(dev, maxreg);
899ea6189d3SConrad Meyer }
900*a9a71513SSimon Wells
901*a9a71513SSimon Wells static void
amdtemp_probe_ccd_sensors1ah(device_t dev,uint32_t model)902*a9a71513SSimon Wells amdtemp_probe_ccd_sensors1ah(device_t dev, uint32_t model)
903*a9a71513SSimon Wells {
904*a9a71513SSimon Wells struct amdtemp_softc *sc = device_get_softc(dev);
905*a9a71513SSimon Wells uint32_t maxreg;
906*a9a71513SSimon Wells
907*a9a71513SSimon Wells switch (model) {
908*a9a71513SSimon Wells case 0x40 ... 0x4f: /* Zen5 Ryzen "Granite Ridge" */
909*a9a71513SSimon Wells sc->sc_temp_base = AMDTEMP_ZEN4_CCD_TMP_BASE;
910*a9a71513SSimon Wells maxreg = 8;
911*a9a71513SSimon Wells _Static_assert((int)NUM_CCDS >= 8, "");
912*a9a71513SSimon Wells break;
913*a9a71513SSimon Wells default:
914*a9a71513SSimon Wells device_printf(dev,
915*a9a71513SSimon Wells "Unrecognized Family 1ah Model: %02xh\n", model);
916*a9a71513SSimon Wells return;
917*a9a71513SSimon Wells }
918*a9a71513SSimon Wells
919*a9a71513SSimon Wells amdtemp_probe_ccd_sensors(dev, maxreg);
920*a9a71513SSimon Wells }
921