xref: /openbmc/linux/drivers/hwmon/k10temp.c (revision d6144a40)
16e7c1094SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
23c57e89bSClemens Ladisch /*
3d547552aSGuenter Roeck  * k10temp.c - AMD Family 10h/11h/12h/14h/15h/16h/17h
4d547552aSGuenter Roeck  *		processor hardware monitoring
53c57e89bSClemens Ladisch  *
63c57e89bSClemens Ladisch  * Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de>
7d547552aSGuenter Roeck  * Copyright (c) 2020 Guenter Roeck <linux@roeck-us.net>
8c7579389SGuenter Roeck  *
9c7579389SGuenter Roeck  * Implementation notes:
10fd8bdb23SGuenter Roeck  * - CCD register address information as well as the calculation to
11c7579389SGuenter Roeck  *   convert raw register values is from https://github.com/ocerman/zenpower.
12c7579389SGuenter Roeck  *   The information is not confirmed from chip datasheets, but experiments
13c7579389SGuenter Roeck  *   suggest that it provides reasonable temperature values.
14b00647c4SGuenter Roeck  * - Register addresses to read chip voltage and current are also from
15b00647c4SGuenter Roeck  *   https://github.com/ocerman/zenpower, and not confirmed from chip
16b00647c4SGuenter Roeck  *   datasheets. Current calibration is board specific and not typically
17b00647c4SGuenter Roeck  *   shared by board vendors. For this reason, current values are
18b00647c4SGuenter Roeck  *   normalized to report 1A/LSB for core current and and 0.25A/LSB for SoC
19b00647c4SGuenter Roeck  *   current. Reported values can be adjusted using the sensors configuration
20b00647c4SGuenter Roeck  *   file.
213c57e89bSClemens Ladisch  */
223c57e89bSClemens Ladisch 
23a6d210daSGuenter Roeck #include <linux/bitops.h>
249c4a38f1SGuenter Roeck #include <linux/debugfs.h>
253c57e89bSClemens Ladisch #include <linux/err.h>
263c57e89bSClemens Ladisch #include <linux/hwmon.h>
273c57e89bSClemens Ladisch #include <linux/init.h>
283c57e89bSClemens Ladisch #include <linux/module.h>
293c57e89bSClemens Ladisch #include <linux/pci.h>
30dedf7dceSWoods, Brian #include <linux/pci_ids.h>
313b031622SGuenter Roeck #include <asm/amd_nb.h>
323c57e89bSClemens Ladisch #include <asm/processor.h>
333c57e89bSClemens Ladisch 
349e581311SAndre Przywara MODULE_DESCRIPTION("AMD Family 10h+ CPU core temperature monitor");
353c57e89bSClemens Ladisch MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
363c57e89bSClemens Ladisch MODULE_LICENSE("GPL");
373c57e89bSClemens Ladisch 
383c57e89bSClemens Ladisch static bool force;
393c57e89bSClemens Ladisch module_param(force, bool, 0444);
403c57e89bSClemens Ladisch MODULE_PARM_DESC(force, "force loading on processors with erratum 319");
413c57e89bSClemens Ladisch 
42f89ce270SAravind Gopalakrishnan /* Provide lock for writing to NB_SMU_IND_ADDR */
43f89ce270SAravind Gopalakrishnan static DEFINE_MUTEX(nb_smu_ind_mutex);
44f89ce270SAravind Gopalakrishnan 
45ccaf63b4SGuenter Roeck #ifndef PCI_DEVICE_ID_AMD_15H_M70H_NB_F3
46ccaf63b4SGuenter Roeck #define PCI_DEVICE_ID_AMD_15H_M70H_NB_F3	0x15b3
47ccaf63b4SGuenter Roeck #endif
48ccaf63b4SGuenter Roeck 
49c5114a1cSClemens Ladisch /* CPUID function 0x80000001, ebx */
50a6d210daSGuenter Roeck #define CPUID_PKGTYPE_MASK	GENMASK(31, 28)
51c5114a1cSClemens Ladisch #define CPUID_PKGTYPE_F		0x00000000
52c5114a1cSClemens Ladisch #define CPUID_PKGTYPE_AM2R2_AM3	0x10000000
53c5114a1cSClemens Ladisch 
54c5114a1cSClemens Ladisch /* DRAM controller (PCI function 2) */
55c5114a1cSClemens Ladisch #define REG_DCT0_CONFIG_HIGH		0x094
56a6d210daSGuenter Roeck #define  DDR3_MODE			BIT(8)
57c5114a1cSClemens Ladisch 
58c5114a1cSClemens Ladisch /* miscellaneous (PCI function 3) */
593c57e89bSClemens Ladisch #define REG_HARDWARE_THERMAL_CONTROL	0x64
60a6d210daSGuenter Roeck #define  HTC_ENABLE			BIT(0)
613c57e89bSClemens Ladisch 
623c57e89bSClemens Ladisch #define REG_REPORTED_TEMPERATURE	0xa4
633c57e89bSClemens Ladisch 
643c57e89bSClemens Ladisch #define REG_NORTHBRIDGE_CAPABILITIES	0xe8
65a6d210daSGuenter Roeck #define  NB_CAP_HTC			BIT(10)
663c57e89bSClemens Ladisch 
67f89ce270SAravind Gopalakrishnan /*
6840626a1bSGuenter Roeck  * For F15h M60h and M70h, REG_HARDWARE_THERMAL_CONTROL
6940626a1bSGuenter Roeck  * and REG_REPORTED_TEMPERATURE have been moved to
7040626a1bSGuenter Roeck  * D0F0xBC_xD820_0C64 [Hardware Temperature Control]
7140626a1bSGuenter Roeck  * D0F0xBC_xD820_0CA4 [Reported Temperature Control]
72f89ce270SAravind Gopalakrishnan  */
7340626a1bSGuenter Roeck #define F15H_M60H_HARDWARE_TEMP_CTRL_OFFSET	0xd8200c64
74f89ce270SAravind Gopalakrishnan #define F15H_M60H_REPORTED_TEMP_CTRL_OFFSET	0xd8200ca4
75f89ce270SAravind Gopalakrishnan 
7617822417SWei Huang /* Common for Zen CPU families (Family 17h and 18h) */
7717822417SWei Huang #define ZEN_REPORTED_TEMP_CTRL_OFFSET		0x00059800
78fd8bdb23SGuenter Roeck 
7917822417SWei Huang #define ZEN_CCD_TEMP(x)				(0x00059954 + ((x) * 4))
8017822417SWei Huang #define ZEN_CCD_TEMP_VALID			BIT(11)
8117822417SWei Huang #define ZEN_CCD_TEMP_MASK			GENMASK(10, 0)
829af0a9aeSGuenter Roeck 
8317822417SWei Huang #define ZEN_CUR_TEMP_SHIFT			21
8417822417SWei Huang #define ZEN_CUR_TEMP_RANGE_SEL_MASK		BIT(19)
85b00647c4SGuenter Roeck 
8617822417SWei Huang #define ZEN_SVI_BASE				0x0005A000
87a6d210daSGuenter Roeck 
8817822417SWei Huang /* F17h thermal registers through SMN */
8917822417SWei Huang #define F17H_M01H_SVI_TEL_PLANE0		(ZEN_SVI_BASE + 0xc)
9017822417SWei Huang #define F17H_M01H_SVI_TEL_PLANE1		(ZEN_SVI_BASE + 0x10)
91d6144a40SWei Huang #define F17H_M31H_SVI_TEL_PLANE0		(ZEN_SVI_BASE + 0x14)
92d6144a40SWei Huang #define F17H_M31H_SVI_TEL_PLANE1		(ZEN_SVI_BASE + 0x10)
9317822417SWei Huang 
94d6144a40SWei Huang #define F17H_M01H_CFACTOR_ICORE			1000000	/* 1A / LSB	*/
95d6144a40SWei Huang #define F17H_M01H_CFACTOR_ISOC			250000	/* 0.25A / LSB	*/
96d6144a40SWei Huang #define F17H_M31H_CFACTOR_ICORE			1000000	/* 1A / LSB	*/
97d6144a40SWei Huang #define F17H_M31H_CFACTOR_ISOC			310000	/* 0.31A / LSB	*/
98b00647c4SGuenter Roeck 
9968546abfSGuenter Roeck struct k10temp_data {
10068546abfSGuenter Roeck 	struct pci_dev *pdev;
10140626a1bSGuenter Roeck 	void (*read_htcreg)(struct pci_dev *pdev, u32 *regval);
10268546abfSGuenter Roeck 	void (*read_tempreg)(struct pci_dev *pdev, u32 *regval);
1031b50b776SGuenter Roeck 	int temp_offset;
1041b597889SGuenter Roeck 	u32 temp_adjust_mask;
10560465245SGuenter Roeck 	u32 show_temp;
106b00647c4SGuenter Roeck 	u32 svi_addr[2];
10760465245SGuenter Roeck 	bool is_zen;
108b00647c4SGuenter Roeck 	bool show_current;
109b00647c4SGuenter Roeck 	int cfactor[2];
1101b50b776SGuenter Roeck };
1111b50b776SGuenter Roeck 
11260465245SGuenter Roeck #define TCTL_BIT	0
11360465245SGuenter Roeck #define TDIE_BIT	1
11460465245SGuenter Roeck #define TCCD_BIT(x)	((x) + 2)
11560465245SGuenter Roeck 
11660465245SGuenter Roeck #define HAVE_TEMP(d, channel)	((d)->show_temp & BIT(channel))
11760465245SGuenter Roeck #define HAVE_TDIE(d)		HAVE_TEMP(d, TDIE_BIT)
11860465245SGuenter Roeck 
1191b50b776SGuenter Roeck struct tctl_offset {
1201b50b776SGuenter Roeck 	u8 model;
1211b50b776SGuenter Roeck 	char const *id;
1221b50b776SGuenter Roeck 	int offset;
1231b50b776SGuenter Roeck };
1241b50b776SGuenter Roeck 
1251b50b776SGuenter Roeck static const struct tctl_offset tctl_offset_table[] = {
126ab5ee246SGuenter Roeck 	{ 0x17, "AMD Ryzen 5 1600X", 20000 },
1271b50b776SGuenter Roeck 	{ 0x17, "AMD Ryzen 7 1700X", 20000 },
1281b50b776SGuenter Roeck 	{ 0x17, "AMD Ryzen 7 1800X", 20000 },
1291b597889SGuenter Roeck 	{ 0x17, "AMD Ryzen 7 2700X", 10000 },
130cd6a2064SGuenter Roeck 	{ 0x17, "AMD Ryzen Threadripper 19", 27000 }, /* 19{00,20,50}X */
131cd6a2064SGuenter Roeck 	{ 0x17, "AMD Ryzen Threadripper 29", 27000 }, /* 29{20,50,70,90}[W]X */
13268546abfSGuenter Roeck };
13368546abfSGuenter Roeck 
134b00647c4SGuenter Roeck static bool is_threadripper(void)
135b00647c4SGuenter Roeck {
136b00647c4SGuenter Roeck 	return strstr(boot_cpu_data.x86_model_id, "Threadripper");
137b00647c4SGuenter Roeck }
138b00647c4SGuenter Roeck 
139b00647c4SGuenter Roeck static bool is_epyc(void)
140b00647c4SGuenter Roeck {
141b00647c4SGuenter Roeck 	return strstr(boot_cpu_data.x86_model_id, "EPYC");
142b00647c4SGuenter Roeck }
143b00647c4SGuenter Roeck 
14440626a1bSGuenter Roeck static void read_htcreg_pci(struct pci_dev *pdev, u32 *regval)
14540626a1bSGuenter Roeck {
14640626a1bSGuenter Roeck 	pci_read_config_dword(pdev, REG_HARDWARE_THERMAL_CONTROL, regval);
14740626a1bSGuenter Roeck }
14840626a1bSGuenter Roeck 
14968546abfSGuenter Roeck static void read_tempreg_pci(struct pci_dev *pdev, u32 *regval)
15068546abfSGuenter Roeck {
15168546abfSGuenter Roeck 	pci_read_config_dword(pdev, REG_REPORTED_TEMPERATURE, regval);
15268546abfSGuenter Roeck }
15368546abfSGuenter Roeck 
15468546abfSGuenter Roeck static void amd_nb_index_read(struct pci_dev *pdev, unsigned int devfn,
15568546abfSGuenter Roeck 			      unsigned int base, int offset, u32 *val)
156f89ce270SAravind Gopalakrishnan {
157f89ce270SAravind Gopalakrishnan 	mutex_lock(&nb_smu_ind_mutex);
158f89ce270SAravind Gopalakrishnan 	pci_bus_write_config_dword(pdev->bus, devfn,
15968546abfSGuenter Roeck 				   base, offset);
160f89ce270SAravind Gopalakrishnan 	pci_bus_read_config_dword(pdev->bus, devfn,
16168546abfSGuenter Roeck 				  base + 4, val);
162f89ce270SAravind Gopalakrishnan 	mutex_unlock(&nb_smu_ind_mutex);
163f89ce270SAravind Gopalakrishnan }
164f89ce270SAravind Gopalakrishnan 
16540626a1bSGuenter Roeck static void read_htcreg_nb_f15(struct pci_dev *pdev, u32 *regval)
16640626a1bSGuenter Roeck {
16740626a1bSGuenter Roeck 	amd_nb_index_read(pdev, PCI_DEVFN(0, 0), 0xb8,
16840626a1bSGuenter Roeck 			  F15H_M60H_HARDWARE_TEMP_CTRL_OFFSET, regval);
16940626a1bSGuenter Roeck }
17040626a1bSGuenter Roeck 
17168546abfSGuenter Roeck static void read_tempreg_nb_f15(struct pci_dev *pdev, u32 *regval)
17268546abfSGuenter Roeck {
17368546abfSGuenter Roeck 	amd_nb_index_read(pdev, PCI_DEVFN(0, 0), 0xb8,
17468546abfSGuenter Roeck 			  F15H_M60H_REPORTED_TEMP_CTRL_OFFSET, regval);
17568546abfSGuenter Roeck }
17668546abfSGuenter Roeck 
17717822417SWei Huang static void read_tempreg_nb_zen(struct pci_dev *pdev, u32 *regval)
1789af0a9aeSGuenter Roeck {
1793b031622SGuenter Roeck 	amd_smn_read(amd_pci_dev_to_node_id(pdev),
18017822417SWei Huang 		     ZEN_REPORTED_TEMP_CTRL_OFFSET, regval);
1819af0a9aeSGuenter Roeck }
1829af0a9aeSGuenter Roeck 
183d547552aSGuenter Roeck static long get_raw_temp(struct k10temp_data *data)
1843c57e89bSClemens Ladisch {
185f934c059SGuenter Roeck 	u32 regval;
186d547552aSGuenter Roeck 	long temp;
1873c57e89bSClemens Ladisch 
18868546abfSGuenter Roeck 	data->read_tempreg(data->pdev, &regval);
18917822417SWei Huang 	temp = (regval >> ZEN_CUR_TEMP_SHIFT) * 125;
1901b597889SGuenter Roeck 	if (regval & data->temp_adjust_mask)
1911b597889SGuenter Roeck 		temp -= 49000;
192f934c059SGuenter Roeck 	return temp;
193f934c059SGuenter Roeck }
194f934c059SGuenter Roeck 
1950e786f32SJason Yan static const char *k10temp_temp_label[] = {
196d547552aSGuenter Roeck 	"Tctl",
197b02c6857SGuenter Roeck 	"Tdie",
198c7579389SGuenter Roeck 	"Tccd1",
199c7579389SGuenter Roeck 	"Tccd2",
200fd8bdb23SGuenter Roeck 	"Tccd3",
201fd8bdb23SGuenter Roeck 	"Tccd4",
202fd8bdb23SGuenter Roeck 	"Tccd5",
203fd8bdb23SGuenter Roeck 	"Tccd6",
204fd8bdb23SGuenter Roeck 	"Tccd7",
205fd8bdb23SGuenter Roeck 	"Tccd8",
206d547552aSGuenter Roeck };
207d547552aSGuenter Roeck 
2080e786f32SJason Yan static const char *k10temp_in_label[] = {
209b00647c4SGuenter Roeck 	"Vcore",
210b00647c4SGuenter Roeck 	"Vsoc",
211b00647c4SGuenter Roeck };
212b00647c4SGuenter Roeck 
2130e786f32SJason Yan static const char *k10temp_curr_label[] = {
214b00647c4SGuenter Roeck 	"Icore",
215b00647c4SGuenter Roeck 	"Isoc",
216b00647c4SGuenter Roeck };
217b00647c4SGuenter Roeck 
218d547552aSGuenter Roeck static int k10temp_read_labels(struct device *dev,
219d547552aSGuenter Roeck 			       enum hwmon_sensor_types type,
220d547552aSGuenter Roeck 			       u32 attr, int channel, const char **str)
221d547552aSGuenter Roeck {
222b00647c4SGuenter Roeck 	switch (type) {
223b00647c4SGuenter Roeck 	case hwmon_temp:
224d547552aSGuenter Roeck 		*str = k10temp_temp_label[channel];
225b00647c4SGuenter Roeck 		break;
226b00647c4SGuenter Roeck 	case hwmon_in:
227b00647c4SGuenter Roeck 		*str = k10temp_in_label[channel];
228b00647c4SGuenter Roeck 		break;
229b00647c4SGuenter Roeck 	case hwmon_curr:
230b00647c4SGuenter Roeck 		*str = k10temp_curr_label[channel];
231b00647c4SGuenter Roeck 		break;
232b00647c4SGuenter Roeck 	default:
233b00647c4SGuenter Roeck 		return -EOPNOTSUPP;
234b00647c4SGuenter Roeck 	}
235d547552aSGuenter Roeck 	return 0;
236d547552aSGuenter Roeck }
237d547552aSGuenter Roeck 
238b00647c4SGuenter Roeck static int k10temp_read_curr(struct device *dev, u32 attr, int channel,
239b00647c4SGuenter Roeck 			     long *val)
240b00647c4SGuenter Roeck {
241b00647c4SGuenter Roeck 	struct k10temp_data *data = dev_get_drvdata(dev);
242b00647c4SGuenter Roeck 	u32 regval;
243b00647c4SGuenter Roeck 
244b00647c4SGuenter Roeck 	switch (attr) {
245b00647c4SGuenter Roeck 	case hwmon_curr_input:
246b00647c4SGuenter Roeck 		amd_smn_read(amd_pci_dev_to_node_id(data->pdev),
247b00647c4SGuenter Roeck 			     data->svi_addr[channel], &regval);
248b00647c4SGuenter Roeck 		*val = DIV_ROUND_CLOSEST(data->cfactor[channel] *
249b00647c4SGuenter Roeck 					 (regval & 0xff),
250b00647c4SGuenter Roeck 					 1000);
251b00647c4SGuenter Roeck 		break;
252b00647c4SGuenter Roeck 	default:
253b00647c4SGuenter Roeck 		return -EOPNOTSUPP;
254b00647c4SGuenter Roeck 	}
255b00647c4SGuenter Roeck 	return 0;
256b00647c4SGuenter Roeck }
257b00647c4SGuenter Roeck 
258b00647c4SGuenter Roeck static int k10temp_read_in(struct device *dev, u32 attr, int channel, long *val)
259b00647c4SGuenter Roeck {
260b00647c4SGuenter Roeck 	struct k10temp_data *data = dev_get_drvdata(dev);
261b00647c4SGuenter Roeck 	u32 regval;
262b00647c4SGuenter Roeck 
263b00647c4SGuenter Roeck 	switch (attr) {
264b00647c4SGuenter Roeck 	case hwmon_in_input:
265b00647c4SGuenter Roeck 		amd_smn_read(amd_pci_dev_to_node_id(data->pdev),
266b00647c4SGuenter Roeck 			     data->svi_addr[channel], &regval);
267b00647c4SGuenter Roeck 		regval = (regval >> 16) & 0xff;
268b00647c4SGuenter Roeck 		*val = DIV_ROUND_CLOSEST(155000 - regval * 625, 100);
269b00647c4SGuenter Roeck 		break;
270b00647c4SGuenter Roeck 	default:
271b00647c4SGuenter Roeck 		return -EOPNOTSUPP;
272b00647c4SGuenter Roeck 	}
273b00647c4SGuenter Roeck 	return 0;
274b00647c4SGuenter Roeck }
275b00647c4SGuenter Roeck 
276b00647c4SGuenter Roeck static int k10temp_read_temp(struct device *dev, u32 attr, int channel,
277b00647c4SGuenter Roeck 			     long *val)
278f934c059SGuenter Roeck {
279f934c059SGuenter Roeck 	struct k10temp_data *data = dev_get_drvdata(dev);
2803c57e89bSClemens Ladisch 	u32 regval;
2813c57e89bSClemens Ladisch 
282d547552aSGuenter Roeck 	switch (attr) {
283d547552aSGuenter Roeck 	case hwmon_temp_input:
284d547552aSGuenter Roeck 		switch (channel) {
285b02c6857SGuenter Roeck 		case 0:		/* Tctl */
286b02c6857SGuenter Roeck 			*val = get_raw_temp(data);
287d547552aSGuenter Roeck 			if (*val < 0)
288d547552aSGuenter Roeck 				*val = 0;
289d547552aSGuenter Roeck 			break;
290b02c6857SGuenter Roeck 		case 1:		/* Tdie */
291b02c6857SGuenter Roeck 			*val = get_raw_temp(data) - data->temp_offset;
292d547552aSGuenter Roeck 			if (*val < 0)
293d547552aSGuenter Roeck 				*val = 0;
294d547552aSGuenter Roeck 			break;
295fd8bdb23SGuenter Roeck 		case 2 ... 9:		/* Tccd{1-8} */
296c7579389SGuenter Roeck 			amd_smn_read(amd_pci_dev_to_node_id(data->pdev),
29717822417SWei Huang 				     ZEN_CCD_TEMP(channel - 2), &regval);
29817822417SWei Huang 			*val = (regval & ZEN_CCD_TEMP_MASK) * 125 - 49000;
299c7579389SGuenter Roeck 			break;
300d547552aSGuenter Roeck 		default:
301d547552aSGuenter Roeck 			return -EOPNOTSUPP;
302d547552aSGuenter Roeck 		}
303d547552aSGuenter Roeck 		break;
304d547552aSGuenter Roeck 	case hwmon_temp_max:
305d547552aSGuenter Roeck 		*val = 70 * 1000;
306d547552aSGuenter Roeck 		break;
307d547552aSGuenter Roeck 	case hwmon_temp_crit:
30840626a1bSGuenter Roeck 		data->read_htcreg(data->pdev, &regval);
309d547552aSGuenter Roeck 		*val = ((regval >> 16) & 0x7f) * 500 + 52000;
310d547552aSGuenter Roeck 		break;
311d547552aSGuenter Roeck 	case hwmon_temp_crit_hyst:
312d547552aSGuenter Roeck 		data->read_htcreg(data->pdev, &regval);
313d547552aSGuenter Roeck 		*val = (((regval >> 16) & 0x7f)
314d547552aSGuenter Roeck 			- ((regval >> 24) & 0xf)) * 500 + 52000;
315d547552aSGuenter Roeck 		break;
316d547552aSGuenter Roeck 	default:
317d547552aSGuenter Roeck 		return -EOPNOTSUPP;
318d547552aSGuenter Roeck 	}
319d547552aSGuenter Roeck 	return 0;
3203c57e89bSClemens Ladisch }
3213c57e89bSClemens Ladisch 
322b00647c4SGuenter Roeck static int k10temp_read(struct device *dev, enum hwmon_sensor_types type,
323b00647c4SGuenter Roeck 			u32 attr, int channel, long *val)
324b00647c4SGuenter Roeck {
325b00647c4SGuenter Roeck 	switch (type) {
326b00647c4SGuenter Roeck 	case hwmon_temp:
327b00647c4SGuenter Roeck 		return k10temp_read_temp(dev, attr, channel, val);
328b00647c4SGuenter Roeck 	case hwmon_in:
329b00647c4SGuenter Roeck 		return k10temp_read_in(dev, attr, channel, val);
330b00647c4SGuenter Roeck 	case hwmon_curr:
331b00647c4SGuenter Roeck 		return k10temp_read_curr(dev, attr, channel, val);
332b00647c4SGuenter Roeck 	default:
333b00647c4SGuenter Roeck 		return -EOPNOTSUPP;
334b00647c4SGuenter Roeck 	}
335b00647c4SGuenter Roeck }
336b00647c4SGuenter Roeck 
337d547552aSGuenter Roeck static umode_t k10temp_is_visible(const void *_data,
338d547552aSGuenter Roeck 				  enum hwmon_sensor_types type,
339d547552aSGuenter Roeck 				  u32 attr, int channel)
3403e3e1022SGuenter Roeck {
341d547552aSGuenter Roeck 	const struct k10temp_data *data = _data;
34268546abfSGuenter Roeck 	struct pci_dev *pdev = data->pdev;
34340626a1bSGuenter Roeck 	u32 reg;
34440626a1bSGuenter Roeck 
345d547552aSGuenter Roeck 	switch (type) {
346d547552aSGuenter Roeck 	case hwmon_temp:
347d547552aSGuenter Roeck 		switch (attr) {
348d547552aSGuenter Roeck 		case hwmon_temp_input:
34960465245SGuenter Roeck 			if (!HAVE_TEMP(data, channel))
350d547552aSGuenter Roeck 				return 0;
351f934c059SGuenter Roeck 			break;
352d547552aSGuenter Roeck 		case hwmon_temp_max:
35360465245SGuenter Roeck 			if (channel || data->is_zen)
354d547552aSGuenter Roeck 				return 0;
355d547552aSGuenter Roeck 			break;
356d547552aSGuenter Roeck 		case hwmon_temp_crit:
357d547552aSGuenter Roeck 		case hwmon_temp_crit_hyst:
358d547552aSGuenter Roeck 			if (channel || !data->read_htcreg)
35940626a1bSGuenter Roeck 				return 0;
3603e3e1022SGuenter Roeck 
361d547552aSGuenter Roeck 			pci_read_config_dword(pdev,
362d547552aSGuenter Roeck 					      REG_NORTHBRIDGE_CAPABILITIES,
36340626a1bSGuenter Roeck 					      &reg);
36440626a1bSGuenter Roeck 			if (!(reg & NB_CAP_HTC))
36540626a1bSGuenter Roeck 				return 0;
36640626a1bSGuenter Roeck 
36740626a1bSGuenter Roeck 			data->read_htcreg(data->pdev, &reg);
36840626a1bSGuenter Roeck 			if (!(reg & HTC_ENABLE))
3693e3e1022SGuenter Roeck 				return 0;
370f934c059SGuenter Roeck 			break;
371d547552aSGuenter Roeck 		case hwmon_temp_label:
37260465245SGuenter Roeck 			/* Show temperature labels only on Zen CPUs */
37360465245SGuenter Roeck 			if (!data->is_zen || !HAVE_TEMP(data, channel))
374f934c059SGuenter Roeck 				return 0;
375f934c059SGuenter Roeck 			break;
376d547552aSGuenter Roeck 		default:
377d547552aSGuenter Roeck 			return 0;
3783e3e1022SGuenter Roeck 		}
379d547552aSGuenter Roeck 		break;
380b00647c4SGuenter Roeck 	case hwmon_in:
381b00647c4SGuenter Roeck 	case hwmon_curr:
382b00647c4SGuenter Roeck 		if (!data->show_current)
383b00647c4SGuenter Roeck 			return 0;
384b00647c4SGuenter Roeck 		break;
385d547552aSGuenter Roeck 	default:
386d547552aSGuenter Roeck 		return 0;
3873e3e1022SGuenter Roeck 	}
388d547552aSGuenter Roeck 	return 0444;
389d547552aSGuenter Roeck }
3903c57e89bSClemens Ladisch 
3916c931ae1SBill Pemberton static bool has_erratum_319(struct pci_dev *pdev)
3923c57e89bSClemens Ladisch {
393c5114a1cSClemens Ladisch 	u32 pkg_type, reg_dram_cfg;
394c5114a1cSClemens Ladisch 
395c5114a1cSClemens Ladisch 	if (boot_cpu_data.x86 != 0x10)
396c5114a1cSClemens Ladisch 		return false;
397c5114a1cSClemens Ladisch 
3983c57e89bSClemens Ladisch 	/*
399c5114a1cSClemens Ladisch 	 * Erratum 319: The thermal sensor of Socket F/AM2+ processors
400c5114a1cSClemens Ladisch 	 *              may be unreliable.
4013c57e89bSClemens Ladisch 	 */
402c5114a1cSClemens Ladisch 	pkg_type = cpuid_ebx(0x80000001) & CPUID_PKGTYPE_MASK;
403c5114a1cSClemens Ladisch 	if (pkg_type == CPUID_PKGTYPE_F)
404c5114a1cSClemens Ladisch 		return true;
405c5114a1cSClemens Ladisch 	if (pkg_type != CPUID_PKGTYPE_AM2R2_AM3)
406c5114a1cSClemens Ladisch 		return false;
407c5114a1cSClemens Ladisch 
408eefc2d9eSJean Delvare 	/* DDR3 memory implies socket AM3, which is good */
409c5114a1cSClemens Ladisch 	pci_bus_read_config_dword(pdev->bus,
410c5114a1cSClemens Ladisch 				  PCI_DEVFN(PCI_SLOT(pdev->devfn), 2),
411c5114a1cSClemens Ladisch 				  REG_DCT0_CONFIG_HIGH, &reg_dram_cfg);
412eefc2d9eSJean Delvare 	if (reg_dram_cfg & DDR3_MODE)
413eefc2d9eSJean Delvare 		return false;
414eefc2d9eSJean Delvare 
415eefc2d9eSJean Delvare 	/*
416eefc2d9eSJean Delvare 	 * Unfortunately it is possible to run a socket AM3 CPU with DDR2
417eefc2d9eSJean Delvare 	 * memory. We blacklist all the cores which do exist in socket AM2+
418eefc2d9eSJean Delvare 	 * format. It still isn't perfect, as RB-C2 cores exist in both AM2+
419eefc2d9eSJean Delvare 	 * and AM3 formats, but that's the best we can do.
420eefc2d9eSJean Delvare 	 */
421eefc2d9eSJean Delvare 	return boot_cpu_data.x86_model < 4 ||
422b399151cSJia Zhang 	       (boot_cpu_data.x86_model == 4 && boot_cpu_data.x86_stepping <= 2);
4233c57e89bSClemens Ladisch }
4243c57e89bSClemens Ladisch 
4259c4a38f1SGuenter Roeck #ifdef CONFIG_DEBUG_FS
4269c4a38f1SGuenter Roeck 
4279c4a38f1SGuenter Roeck static void k10temp_smn_regs_show(struct seq_file *s, struct pci_dev *pdev,
4289c4a38f1SGuenter Roeck 				  u32 addr, int count)
4299c4a38f1SGuenter Roeck {
4309c4a38f1SGuenter Roeck 	u32 reg;
4319c4a38f1SGuenter Roeck 	int i;
4329c4a38f1SGuenter Roeck 
4339c4a38f1SGuenter Roeck 	for (i = 0; i < count; i++) {
4349c4a38f1SGuenter Roeck 		if (!(i & 3))
4359c4a38f1SGuenter Roeck 			seq_printf(s, "0x%06x: ", addr + i * 4);
4369c4a38f1SGuenter Roeck 		amd_smn_read(amd_pci_dev_to_node_id(pdev), addr + i * 4, &reg);
4379c4a38f1SGuenter Roeck 		seq_printf(s, "%08x ", reg);
4389c4a38f1SGuenter Roeck 		if ((i & 3) == 3)
4399c4a38f1SGuenter Roeck 			seq_puts(s, "\n");
4409c4a38f1SGuenter Roeck 	}
4419c4a38f1SGuenter Roeck }
4429c4a38f1SGuenter Roeck 
4439c4a38f1SGuenter Roeck static int svi_show(struct seq_file *s, void *unused)
4449c4a38f1SGuenter Roeck {
4459c4a38f1SGuenter Roeck 	struct k10temp_data *data = s->private;
4469c4a38f1SGuenter Roeck 
44717822417SWei Huang 	k10temp_smn_regs_show(s, data->pdev, ZEN_SVI_BASE, 32);
4489c4a38f1SGuenter Roeck 	return 0;
4499c4a38f1SGuenter Roeck }
4509c4a38f1SGuenter Roeck DEFINE_SHOW_ATTRIBUTE(svi);
4519c4a38f1SGuenter Roeck 
4529c4a38f1SGuenter Roeck static int thm_show(struct seq_file *s, void *unused)
4539c4a38f1SGuenter Roeck {
4549c4a38f1SGuenter Roeck 	struct k10temp_data *data = s->private;
4559c4a38f1SGuenter Roeck 
4569c4a38f1SGuenter Roeck 	k10temp_smn_regs_show(s, data->pdev,
45717822417SWei Huang 			      ZEN_REPORTED_TEMP_CTRL_OFFSET, 256);
4589c4a38f1SGuenter Roeck 	return 0;
4599c4a38f1SGuenter Roeck }
4609c4a38f1SGuenter Roeck DEFINE_SHOW_ATTRIBUTE(thm);
4619c4a38f1SGuenter Roeck 
4629c4a38f1SGuenter Roeck static void k10temp_debugfs_cleanup(void *ddir)
4639c4a38f1SGuenter Roeck {
4649c4a38f1SGuenter Roeck 	debugfs_remove_recursive(ddir);
4659c4a38f1SGuenter Roeck }
4669c4a38f1SGuenter Roeck 
4679c4a38f1SGuenter Roeck static void k10temp_init_debugfs(struct k10temp_data *data)
4689c4a38f1SGuenter Roeck {
4699c4a38f1SGuenter Roeck 	struct dentry *debugfs;
4709c4a38f1SGuenter Roeck 	char name[32];
4719c4a38f1SGuenter Roeck 
4729c4a38f1SGuenter Roeck 	/* Only show debugfs data for Family 17h/18h CPUs */
47360465245SGuenter Roeck 	if (!data->is_zen)
4749c4a38f1SGuenter Roeck 		return;
4759c4a38f1SGuenter Roeck 
4769c4a38f1SGuenter Roeck 	scnprintf(name, sizeof(name), "k10temp-%s", pci_name(data->pdev));
4779c4a38f1SGuenter Roeck 
4789c4a38f1SGuenter Roeck 	debugfs = debugfs_create_dir(name, NULL);
4799c4a38f1SGuenter Roeck 	if (debugfs) {
4809c4a38f1SGuenter Roeck 		debugfs_create_file("svi", 0444, debugfs, data, &svi_fops);
4819c4a38f1SGuenter Roeck 		debugfs_create_file("thm", 0444, debugfs, data, &thm_fops);
4829c4a38f1SGuenter Roeck 		devm_add_action_or_reset(&data->pdev->dev,
4839c4a38f1SGuenter Roeck 					 k10temp_debugfs_cleanup, debugfs);
4849c4a38f1SGuenter Roeck 	}
4859c4a38f1SGuenter Roeck }
4869c4a38f1SGuenter Roeck 
4879c4a38f1SGuenter Roeck #else
4889c4a38f1SGuenter Roeck 
4899c4a38f1SGuenter Roeck static void k10temp_init_debugfs(struct k10temp_data *data)
4909c4a38f1SGuenter Roeck {
4919c4a38f1SGuenter Roeck }
4929c4a38f1SGuenter Roeck 
4939c4a38f1SGuenter Roeck #endif
4949c4a38f1SGuenter Roeck 
495d547552aSGuenter Roeck static const struct hwmon_channel_info *k10temp_info[] = {
496d547552aSGuenter Roeck 	HWMON_CHANNEL_INFO(temp,
497d547552aSGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_MAX |
498d547552aSGuenter Roeck 			   HWMON_T_CRIT | HWMON_T_CRIT_HYST |
499d547552aSGuenter Roeck 			   HWMON_T_LABEL,
500c7579389SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
501c7579389SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
502fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
503fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
504fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
505fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
506fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
507fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
508d547552aSGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL),
509b00647c4SGuenter Roeck 	HWMON_CHANNEL_INFO(in,
510b00647c4SGuenter Roeck 			   HWMON_I_INPUT | HWMON_I_LABEL,
511b00647c4SGuenter Roeck 			   HWMON_I_INPUT | HWMON_I_LABEL),
512b00647c4SGuenter Roeck 	HWMON_CHANNEL_INFO(curr,
513b00647c4SGuenter Roeck 			   HWMON_C_INPUT | HWMON_C_LABEL,
514b00647c4SGuenter Roeck 			   HWMON_C_INPUT | HWMON_C_LABEL),
515d547552aSGuenter Roeck 	NULL
516d547552aSGuenter Roeck };
517d547552aSGuenter Roeck 
518d547552aSGuenter Roeck static const struct hwmon_ops k10temp_hwmon_ops = {
519d547552aSGuenter Roeck 	.is_visible = k10temp_is_visible,
520d547552aSGuenter Roeck 	.read = k10temp_read,
521d547552aSGuenter Roeck 	.read_string = k10temp_read_labels,
522d547552aSGuenter Roeck };
523d547552aSGuenter Roeck 
524d547552aSGuenter Roeck static const struct hwmon_chip_info k10temp_chip_info = {
525d547552aSGuenter Roeck 	.ops = &k10temp_hwmon_ops,
526d547552aSGuenter Roeck 	.info = k10temp_info,
527d547552aSGuenter Roeck };
528d547552aSGuenter Roeck 
529fd8bdb23SGuenter Roeck static void k10temp_get_ccd_support(struct pci_dev *pdev,
530fd8bdb23SGuenter Roeck 				    struct k10temp_data *data, int limit)
531fd8bdb23SGuenter Roeck {
532fd8bdb23SGuenter Roeck 	u32 regval;
533fd8bdb23SGuenter Roeck 	int i;
534fd8bdb23SGuenter Roeck 
535fd8bdb23SGuenter Roeck 	for (i = 0; i < limit; i++) {
536fd8bdb23SGuenter Roeck 		amd_smn_read(amd_pci_dev_to_node_id(pdev),
53717822417SWei Huang 			     ZEN_CCD_TEMP(i), &regval);
53817822417SWei Huang 		if (regval & ZEN_CCD_TEMP_VALID)
53960465245SGuenter Roeck 			data->show_temp |= BIT(TCCD_BIT(i));
540fd8bdb23SGuenter Roeck 	}
541fd8bdb23SGuenter Roeck }
542fd8bdb23SGuenter Roeck 
543d547552aSGuenter Roeck static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
5443c57e89bSClemens Ladisch {
545c5114a1cSClemens Ladisch 	int unreliable = has_erratum_319(pdev);
5463e3e1022SGuenter Roeck 	struct device *dev = &pdev->dev;
54768546abfSGuenter Roeck 	struct k10temp_data *data;
5483e3e1022SGuenter Roeck 	struct device *hwmon_dev;
5491b50b776SGuenter Roeck 	int i;
5503c57e89bSClemens Ladisch 
5513e3e1022SGuenter Roeck 	if (unreliable) {
5523e3e1022SGuenter Roeck 		if (!force) {
5533e3e1022SGuenter Roeck 			dev_err(dev,
5543c57e89bSClemens Ladisch 				"unreliable CPU thermal sensor; monitoring disabled\n");
5553e3e1022SGuenter Roeck 			return -ENODEV;
5563c57e89bSClemens Ladisch 		}
5573e3e1022SGuenter Roeck 		dev_warn(dev,
5583c57e89bSClemens Ladisch 			 "unreliable CPU thermal sensor; check erratum 319\n");
5593c57e89bSClemens Ladisch 	}
5603c57e89bSClemens Ladisch 
56168546abfSGuenter Roeck 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
56268546abfSGuenter Roeck 	if (!data)
56368546abfSGuenter Roeck 		return -ENOMEM;
56468546abfSGuenter Roeck 
56568546abfSGuenter Roeck 	data->pdev = pdev;
56660465245SGuenter Roeck 	data->show_temp |= BIT(TCTL_BIT);	/* Always show Tctl */
56768546abfSGuenter Roeck 
56853dfa008SGuenter Roeck 	if (boot_cpu_data.x86 == 0x15 &&
56953dfa008SGuenter Roeck 	    ((boot_cpu_data.x86_model & 0xf0) == 0x60 ||
57053dfa008SGuenter Roeck 	     (boot_cpu_data.x86_model & 0xf0) == 0x70)) {
57140626a1bSGuenter Roeck 		data->read_htcreg = read_htcreg_nb_f15;
57268546abfSGuenter Roeck 		data->read_tempreg = read_tempreg_nb_f15;
573d93217d8SPu Wen 	} else if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) {
57417822417SWei Huang 		data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK;
57517822417SWei Huang 		data->read_tempreg = read_tempreg_nb_zen;
57660465245SGuenter Roeck 		data->show_temp |= BIT(TDIE_BIT);	/* show Tdie */
57760465245SGuenter Roeck 		data->is_zen = true;
578c7579389SGuenter Roeck 
579c7579389SGuenter Roeck 		switch (boot_cpu_data.x86_model) {
580c7579389SGuenter Roeck 		case 0x1:	/* Zen */
581c7579389SGuenter Roeck 		case 0x8:	/* Zen+ */
582c7579389SGuenter Roeck 		case 0x11:	/* Zen APU */
583c7579389SGuenter Roeck 		case 0x18:	/* Zen+ APU */
584b00647c4SGuenter Roeck 			data->show_current = !is_threadripper() && !is_epyc();
585b00647c4SGuenter Roeck 			data->svi_addr[0] = F17H_M01H_SVI_TEL_PLANE0;
586b00647c4SGuenter Roeck 			data->svi_addr[1] = F17H_M01H_SVI_TEL_PLANE1;
587d6144a40SWei Huang 			data->cfactor[0] = F17H_M01H_CFACTOR_ICORE;
588d6144a40SWei Huang 			data->cfactor[1] = F17H_M01H_CFACTOR_ISOC;
589fd8bdb23SGuenter Roeck 			k10temp_get_ccd_support(pdev, data, 4);
590c7579389SGuenter Roeck 			break;
591c7579389SGuenter Roeck 		case 0x31:	/* Zen2 Threadripper */
592c7579389SGuenter Roeck 		case 0x71:	/* Zen2 */
593b00647c4SGuenter Roeck 			data->show_current = !is_threadripper() && !is_epyc();
594d6144a40SWei Huang 			data->cfactor[0] = F17H_M31H_CFACTOR_ICORE;
595d6144a40SWei Huang 			data->cfactor[1] = F17H_M31H_CFACTOR_ISOC;
596d6144a40SWei Huang 			data->svi_addr[0] = F17H_M31H_SVI_TEL_PLANE0;
597d6144a40SWei Huang 			data->svi_addr[1] = F17H_M31H_SVI_TEL_PLANE1;
598fd8bdb23SGuenter Roeck 			k10temp_get_ccd_support(pdev, data, 8);
599c7579389SGuenter Roeck 			break;
600c7579389SGuenter Roeck 		}
6011b597889SGuenter Roeck 	} else {
60240626a1bSGuenter Roeck 		data->read_htcreg = read_htcreg_pci;
60368546abfSGuenter Roeck 		data->read_tempreg = read_tempreg_pci;
6041b597889SGuenter Roeck 	}
60568546abfSGuenter Roeck 
6061b50b776SGuenter Roeck 	for (i = 0; i < ARRAY_SIZE(tctl_offset_table); i++) {
6071b50b776SGuenter Roeck 		const struct tctl_offset *entry = &tctl_offset_table[i];
6081b50b776SGuenter Roeck 
6091b50b776SGuenter Roeck 		if (boot_cpu_data.x86 == entry->model &&
6101b50b776SGuenter Roeck 		    strstr(boot_cpu_data.x86_model_id, entry->id)) {
6111b50b776SGuenter Roeck 			data->temp_offset = entry->offset;
6121b50b776SGuenter Roeck 			break;
6131b50b776SGuenter Roeck 		}
6141b50b776SGuenter Roeck 	}
6151b50b776SGuenter Roeck 
616d547552aSGuenter Roeck 	hwmon_dev = devm_hwmon_device_register_with_info(dev, "k10temp", data,
617d547552aSGuenter Roeck 							 &k10temp_chip_info,
618d547552aSGuenter Roeck 							 NULL);
6199c4a38f1SGuenter Roeck 	if (IS_ERR(hwmon_dev))
6209c4a38f1SGuenter Roeck 		return PTR_ERR(hwmon_dev);
6219c4a38f1SGuenter Roeck 
6229c4a38f1SGuenter Roeck 	k10temp_init_debugfs(data);
6239c4a38f1SGuenter Roeck 
6249c4a38f1SGuenter Roeck 	return 0;
6253c57e89bSClemens Ladisch }
6263c57e89bSClemens Ladisch 
627cd9bb056SJingoo Han static const struct pci_device_id k10temp_id_table[] = {
6283c57e89bSClemens Ladisch 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) },
6293c57e89bSClemens Ladisch 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) },
630aa4790a6SClemens Ladisch 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) },
6319e581311SAndre Przywara 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) },
63224214449SBorislav Petkov 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) },
633d303b1b5SPhil Pokorny 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F3) },
634f89ce270SAravind Gopalakrishnan 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F3) },
635ccaf63b4SGuenter Roeck 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M70H_NB_F3) },
63630b146d1SWei Hu 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) },
637ec015950SAravind Gopalakrishnan 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) },
6389af0a9aeSGuenter Roeck 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_DF_F3) },
6393b031622SGuenter Roeck 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F3) },
640210ba120SWoods, Brian 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M30H_DF_F3) },
641279f0b3aSAlexander Monakov 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M60H_DF_F3) },
64212163cfbSMarcel Bocu 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F3) },
643d93217d8SPu Wen 	{ PCI_VDEVICE(HYGON, PCI_DEVICE_ID_AMD_17H_DF_F3) },
6443c57e89bSClemens Ladisch 	{}
6453c57e89bSClemens Ladisch };
6463c57e89bSClemens Ladisch MODULE_DEVICE_TABLE(pci, k10temp_id_table);
6473c57e89bSClemens Ladisch 
6483c57e89bSClemens Ladisch static struct pci_driver k10temp_driver = {
6493c57e89bSClemens Ladisch 	.name = "k10temp",
6503c57e89bSClemens Ladisch 	.id_table = k10temp_id_table,
6513c57e89bSClemens Ladisch 	.probe = k10temp_probe,
6523c57e89bSClemens Ladisch };
6533c57e89bSClemens Ladisch 
654f71f5a55SAxel Lin module_pci_driver(k10temp_driver);
655