xref: /openbmc/linux/drivers/hwmon/k10temp.c (revision 55163a1c)
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>
243c57e89bSClemens Ladisch #include <linux/err.h>
253c57e89bSClemens Ladisch #include <linux/hwmon.h>
263c57e89bSClemens Ladisch #include <linux/init.h>
273c57e89bSClemens Ladisch #include <linux/module.h>
283c57e89bSClemens Ladisch #include <linux/pci.h>
29dedf7dceSWoods, Brian #include <linux/pci_ids.h>
303b031622SGuenter Roeck #include <asm/amd_nb.h>
313c57e89bSClemens Ladisch #include <asm/processor.h>
323c57e89bSClemens Ladisch 
339e581311SAndre Przywara MODULE_DESCRIPTION("AMD Family 10h+ CPU core temperature monitor");
343c57e89bSClemens Ladisch MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
353c57e89bSClemens Ladisch MODULE_LICENSE("GPL");
363c57e89bSClemens Ladisch 
373c57e89bSClemens Ladisch static bool force;
383c57e89bSClemens Ladisch module_param(force, bool, 0444);
393c57e89bSClemens Ladisch MODULE_PARM_DESC(force, "force loading on processors with erratum 319");
403c57e89bSClemens Ladisch 
41f89ce270SAravind Gopalakrishnan /* Provide lock for writing to NB_SMU_IND_ADDR */
42f89ce270SAravind Gopalakrishnan static DEFINE_MUTEX(nb_smu_ind_mutex);
43f89ce270SAravind Gopalakrishnan 
44ccaf63b4SGuenter Roeck #ifndef PCI_DEVICE_ID_AMD_15H_M70H_NB_F3
45ccaf63b4SGuenter Roeck #define PCI_DEVICE_ID_AMD_15H_M70H_NB_F3	0x15b3
46ccaf63b4SGuenter Roeck #endif
47ccaf63b4SGuenter Roeck 
48c5114a1cSClemens Ladisch /* CPUID function 0x80000001, ebx */
49a6d210daSGuenter Roeck #define CPUID_PKGTYPE_MASK	GENMASK(31, 28)
50c5114a1cSClemens Ladisch #define CPUID_PKGTYPE_F		0x00000000
51c5114a1cSClemens Ladisch #define CPUID_PKGTYPE_AM2R2_AM3	0x10000000
52c5114a1cSClemens Ladisch 
53c5114a1cSClemens Ladisch /* DRAM controller (PCI function 2) */
54c5114a1cSClemens Ladisch #define REG_DCT0_CONFIG_HIGH		0x094
55a6d210daSGuenter Roeck #define  DDR3_MODE			BIT(8)
56c5114a1cSClemens Ladisch 
57c5114a1cSClemens Ladisch /* miscellaneous (PCI function 3) */
583c57e89bSClemens Ladisch #define REG_HARDWARE_THERMAL_CONTROL	0x64
59a6d210daSGuenter Roeck #define  HTC_ENABLE			BIT(0)
603c57e89bSClemens Ladisch 
613c57e89bSClemens Ladisch #define REG_REPORTED_TEMPERATURE	0xa4
623c57e89bSClemens Ladisch 
633c57e89bSClemens Ladisch #define REG_NORTHBRIDGE_CAPABILITIES	0xe8
64a6d210daSGuenter Roeck #define  NB_CAP_HTC			BIT(10)
653c57e89bSClemens Ladisch 
66f89ce270SAravind Gopalakrishnan /*
6740626a1bSGuenter Roeck  * For F15h M60h and M70h, REG_HARDWARE_THERMAL_CONTROL
6840626a1bSGuenter Roeck  * and REG_REPORTED_TEMPERATURE have been moved to
6940626a1bSGuenter Roeck  * D0F0xBC_xD820_0C64 [Hardware Temperature Control]
7040626a1bSGuenter Roeck  * D0F0xBC_xD820_0CA4 [Reported Temperature Control]
71f89ce270SAravind Gopalakrishnan  */
7240626a1bSGuenter Roeck #define F15H_M60H_HARDWARE_TEMP_CTRL_OFFSET	0xd8200c64
73f89ce270SAravind Gopalakrishnan #define F15H_M60H_REPORTED_TEMP_CTRL_OFFSET	0xd8200ca4
74f89ce270SAravind Gopalakrishnan 
7517822417SWei Huang /* Common for Zen CPU families (Family 17h and 18h) */
7617822417SWei Huang #define ZEN_REPORTED_TEMP_CTRL_OFFSET		0x00059800
77fd8bdb23SGuenter Roeck 
7817822417SWei Huang #define ZEN_CCD_TEMP(x)				(0x00059954 + ((x) * 4))
7917822417SWei Huang #define ZEN_CCD_TEMP_VALID			BIT(11)
8017822417SWei Huang #define ZEN_CCD_TEMP_MASK			GENMASK(10, 0)
819af0a9aeSGuenter Roeck 
8217822417SWei Huang #define ZEN_CUR_TEMP_SHIFT			21
8317822417SWei Huang #define ZEN_CUR_TEMP_RANGE_SEL_MASK		BIT(19)
84b00647c4SGuenter Roeck 
8517822417SWei Huang #define ZEN_SVI_BASE				0x0005A000
86a6d210daSGuenter Roeck 
8717822417SWei Huang /* F17h thermal registers through SMN */
8817822417SWei Huang #define F17H_M01H_SVI_TEL_PLANE0		(ZEN_SVI_BASE + 0xc)
8917822417SWei Huang #define F17H_M01H_SVI_TEL_PLANE1		(ZEN_SVI_BASE + 0x10)
90d6144a40SWei Huang #define F17H_M31H_SVI_TEL_PLANE0		(ZEN_SVI_BASE + 0x14)
91d6144a40SWei Huang #define F17H_M31H_SVI_TEL_PLANE1		(ZEN_SVI_BASE + 0x10)
9217822417SWei Huang 
93d6144a40SWei Huang #define F17H_M01H_CFACTOR_ICORE			1000000	/* 1A / LSB	*/
94d6144a40SWei Huang #define F17H_M01H_CFACTOR_ISOC			250000	/* 0.25A / LSB	*/
95d6144a40SWei Huang #define F17H_M31H_CFACTOR_ICORE			1000000	/* 1A / LSB	*/
96d6144a40SWei Huang #define F17H_M31H_CFACTOR_ISOC			310000	/* 0.31A / LSB	*/
97b00647c4SGuenter Roeck 
9855163a1cSWei Huang /* F19h thermal registers through SMN */
9955163a1cSWei Huang #define F19H_M01_SVI_TEL_PLANE0			(ZEN_SVI_BASE + 0x14)
10055163a1cSWei Huang #define F19H_M01_SVI_TEL_PLANE1			(ZEN_SVI_BASE + 0x10)
10155163a1cSWei Huang 
10255163a1cSWei Huang #define F19H_M01H_CFACTOR_ICORE			1000000	/* 1A / LSB	*/
10355163a1cSWei Huang #define F19H_M01H_CFACTOR_ISOC			310000	/* 0.31A / LSB	*/
10455163a1cSWei Huang 
10568546abfSGuenter Roeck struct k10temp_data {
10668546abfSGuenter Roeck 	struct pci_dev *pdev;
10740626a1bSGuenter Roeck 	void (*read_htcreg)(struct pci_dev *pdev, u32 *regval);
10868546abfSGuenter Roeck 	void (*read_tempreg)(struct pci_dev *pdev, u32 *regval);
1091b50b776SGuenter Roeck 	int temp_offset;
1101b597889SGuenter Roeck 	u32 temp_adjust_mask;
11160465245SGuenter Roeck 	u32 show_temp;
112b00647c4SGuenter Roeck 	u32 svi_addr[2];
11360465245SGuenter Roeck 	bool is_zen;
114b00647c4SGuenter Roeck 	bool show_current;
115b00647c4SGuenter Roeck 	int cfactor[2];
1161b50b776SGuenter Roeck };
1171b50b776SGuenter Roeck 
11860465245SGuenter Roeck #define TCTL_BIT	0
11960465245SGuenter Roeck #define TDIE_BIT	1
12060465245SGuenter Roeck #define TCCD_BIT(x)	((x) + 2)
12160465245SGuenter Roeck 
12260465245SGuenter Roeck #define HAVE_TEMP(d, channel)	((d)->show_temp & BIT(channel))
12360465245SGuenter Roeck #define HAVE_TDIE(d)		HAVE_TEMP(d, TDIE_BIT)
12460465245SGuenter Roeck 
1251b50b776SGuenter Roeck struct tctl_offset {
1261b50b776SGuenter Roeck 	u8 model;
1271b50b776SGuenter Roeck 	char const *id;
1281b50b776SGuenter Roeck 	int offset;
1291b50b776SGuenter Roeck };
1301b50b776SGuenter Roeck 
1311b50b776SGuenter Roeck static const struct tctl_offset tctl_offset_table[] = {
132ab5ee246SGuenter Roeck 	{ 0x17, "AMD Ryzen 5 1600X", 20000 },
1331b50b776SGuenter Roeck 	{ 0x17, "AMD Ryzen 7 1700X", 20000 },
1341b50b776SGuenter Roeck 	{ 0x17, "AMD Ryzen 7 1800X", 20000 },
1351b597889SGuenter Roeck 	{ 0x17, "AMD Ryzen 7 2700X", 10000 },
136cd6a2064SGuenter Roeck 	{ 0x17, "AMD Ryzen Threadripper 19", 27000 }, /* 19{00,20,50}X */
137cd6a2064SGuenter Roeck 	{ 0x17, "AMD Ryzen Threadripper 29", 27000 }, /* 29{20,50,70,90}[W]X */
13868546abfSGuenter Roeck };
13968546abfSGuenter Roeck 
140b00647c4SGuenter Roeck static bool is_threadripper(void)
141b00647c4SGuenter Roeck {
142b00647c4SGuenter Roeck 	return strstr(boot_cpu_data.x86_model_id, "Threadripper");
143b00647c4SGuenter Roeck }
144b00647c4SGuenter Roeck 
145b00647c4SGuenter Roeck static bool is_epyc(void)
146b00647c4SGuenter Roeck {
147b00647c4SGuenter Roeck 	return strstr(boot_cpu_data.x86_model_id, "EPYC");
148b00647c4SGuenter Roeck }
149b00647c4SGuenter Roeck 
15040626a1bSGuenter Roeck static void read_htcreg_pci(struct pci_dev *pdev, u32 *regval)
15140626a1bSGuenter Roeck {
15240626a1bSGuenter Roeck 	pci_read_config_dword(pdev, REG_HARDWARE_THERMAL_CONTROL, regval);
15340626a1bSGuenter Roeck }
15440626a1bSGuenter Roeck 
15568546abfSGuenter Roeck static void read_tempreg_pci(struct pci_dev *pdev, u32 *regval)
15668546abfSGuenter Roeck {
15768546abfSGuenter Roeck 	pci_read_config_dword(pdev, REG_REPORTED_TEMPERATURE, regval);
15868546abfSGuenter Roeck }
15968546abfSGuenter Roeck 
16068546abfSGuenter Roeck static void amd_nb_index_read(struct pci_dev *pdev, unsigned int devfn,
16168546abfSGuenter Roeck 			      unsigned int base, int offset, u32 *val)
162f89ce270SAravind Gopalakrishnan {
163f89ce270SAravind Gopalakrishnan 	mutex_lock(&nb_smu_ind_mutex);
164f89ce270SAravind Gopalakrishnan 	pci_bus_write_config_dword(pdev->bus, devfn,
16568546abfSGuenter Roeck 				   base, offset);
166f89ce270SAravind Gopalakrishnan 	pci_bus_read_config_dword(pdev->bus, devfn,
16768546abfSGuenter Roeck 				  base + 4, val);
168f89ce270SAravind Gopalakrishnan 	mutex_unlock(&nb_smu_ind_mutex);
169f89ce270SAravind Gopalakrishnan }
170f89ce270SAravind Gopalakrishnan 
17140626a1bSGuenter Roeck static void read_htcreg_nb_f15(struct pci_dev *pdev, u32 *regval)
17240626a1bSGuenter Roeck {
17340626a1bSGuenter Roeck 	amd_nb_index_read(pdev, PCI_DEVFN(0, 0), 0xb8,
17440626a1bSGuenter Roeck 			  F15H_M60H_HARDWARE_TEMP_CTRL_OFFSET, regval);
17540626a1bSGuenter Roeck }
17640626a1bSGuenter Roeck 
17768546abfSGuenter Roeck static void read_tempreg_nb_f15(struct pci_dev *pdev, u32 *regval)
17868546abfSGuenter Roeck {
17968546abfSGuenter Roeck 	amd_nb_index_read(pdev, PCI_DEVFN(0, 0), 0xb8,
18068546abfSGuenter Roeck 			  F15H_M60H_REPORTED_TEMP_CTRL_OFFSET, regval);
18168546abfSGuenter Roeck }
18268546abfSGuenter Roeck 
18317822417SWei Huang static void read_tempreg_nb_zen(struct pci_dev *pdev, u32 *regval)
1849af0a9aeSGuenter Roeck {
1853b031622SGuenter Roeck 	amd_smn_read(amd_pci_dev_to_node_id(pdev),
18617822417SWei Huang 		     ZEN_REPORTED_TEMP_CTRL_OFFSET, regval);
1879af0a9aeSGuenter Roeck }
1889af0a9aeSGuenter Roeck 
189d547552aSGuenter Roeck static long get_raw_temp(struct k10temp_data *data)
1903c57e89bSClemens Ladisch {
191f934c059SGuenter Roeck 	u32 regval;
192d547552aSGuenter Roeck 	long temp;
1933c57e89bSClemens Ladisch 
19468546abfSGuenter Roeck 	data->read_tempreg(data->pdev, &regval);
19517822417SWei Huang 	temp = (regval >> ZEN_CUR_TEMP_SHIFT) * 125;
1961b597889SGuenter Roeck 	if (regval & data->temp_adjust_mask)
1971b597889SGuenter Roeck 		temp -= 49000;
198f934c059SGuenter Roeck 	return temp;
199f934c059SGuenter Roeck }
200f934c059SGuenter Roeck 
2010e786f32SJason Yan static const char *k10temp_temp_label[] = {
202d547552aSGuenter Roeck 	"Tctl",
203b02c6857SGuenter Roeck 	"Tdie",
204c7579389SGuenter Roeck 	"Tccd1",
205c7579389SGuenter Roeck 	"Tccd2",
206fd8bdb23SGuenter Roeck 	"Tccd3",
207fd8bdb23SGuenter Roeck 	"Tccd4",
208fd8bdb23SGuenter Roeck 	"Tccd5",
209fd8bdb23SGuenter Roeck 	"Tccd6",
210fd8bdb23SGuenter Roeck 	"Tccd7",
211fd8bdb23SGuenter Roeck 	"Tccd8",
212d547552aSGuenter Roeck };
213d547552aSGuenter Roeck 
2140e786f32SJason Yan static const char *k10temp_in_label[] = {
215b00647c4SGuenter Roeck 	"Vcore",
216b00647c4SGuenter Roeck 	"Vsoc",
217b00647c4SGuenter Roeck };
218b00647c4SGuenter Roeck 
2190e786f32SJason Yan static const char *k10temp_curr_label[] = {
220b00647c4SGuenter Roeck 	"Icore",
221b00647c4SGuenter Roeck 	"Isoc",
222b00647c4SGuenter Roeck };
223b00647c4SGuenter Roeck 
224d547552aSGuenter Roeck static int k10temp_read_labels(struct device *dev,
225d547552aSGuenter Roeck 			       enum hwmon_sensor_types type,
226d547552aSGuenter Roeck 			       u32 attr, int channel, const char **str)
227d547552aSGuenter Roeck {
228b00647c4SGuenter Roeck 	switch (type) {
229b00647c4SGuenter Roeck 	case hwmon_temp:
230d547552aSGuenter Roeck 		*str = k10temp_temp_label[channel];
231b00647c4SGuenter Roeck 		break;
232b00647c4SGuenter Roeck 	case hwmon_in:
233b00647c4SGuenter Roeck 		*str = k10temp_in_label[channel];
234b00647c4SGuenter Roeck 		break;
235b00647c4SGuenter Roeck 	case hwmon_curr:
236b00647c4SGuenter Roeck 		*str = k10temp_curr_label[channel];
237b00647c4SGuenter Roeck 		break;
238b00647c4SGuenter Roeck 	default:
239b00647c4SGuenter Roeck 		return -EOPNOTSUPP;
240b00647c4SGuenter Roeck 	}
241d547552aSGuenter Roeck 	return 0;
242d547552aSGuenter Roeck }
243d547552aSGuenter Roeck 
244b00647c4SGuenter Roeck static int k10temp_read_curr(struct device *dev, u32 attr, int channel,
245b00647c4SGuenter Roeck 			     long *val)
246b00647c4SGuenter Roeck {
247b00647c4SGuenter Roeck 	struct k10temp_data *data = dev_get_drvdata(dev);
248b00647c4SGuenter Roeck 	u32 regval;
249b00647c4SGuenter Roeck 
250b00647c4SGuenter Roeck 	switch (attr) {
251b00647c4SGuenter Roeck 	case hwmon_curr_input:
252b00647c4SGuenter Roeck 		amd_smn_read(amd_pci_dev_to_node_id(data->pdev),
253b00647c4SGuenter Roeck 			     data->svi_addr[channel], &regval);
254b00647c4SGuenter Roeck 		*val = DIV_ROUND_CLOSEST(data->cfactor[channel] *
255b00647c4SGuenter Roeck 					 (regval & 0xff),
256b00647c4SGuenter Roeck 					 1000);
257b00647c4SGuenter Roeck 		break;
258b00647c4SGuenter Roeck 	default:
259b00647c4SGuenter Roeck 		return -EOPNOTSUPP;
260b00647c4SGuenter Roeck 	}
261b00647c4SGuenter Roeck 	return 0;
262b00647c4SGuenter Roeck }
263b00647c4SGuenter Roeck 
264b00647c4SGuenter Roeck static int k10temp_read_in(struct device *dev, u32 attr, int channel, long *val)
265b00647c4SGuenter Roeck {
266b00647c4SGuenter Roeck 	struct k10temp_data *data = dev_get_drvdata(dev);
267b00647c4SGuenter Roeck 	u32 regval;
268b00647c4SGuenter Roeck 
269b00647c4SGuenter Roeck 	switch (attr) {
270b00647c4SGuenter Roeck 	case hwmon_in_input:
271b00647c4SGuenter Roeck 		amd_smn_read(amd_pci_dev_to_node_id(data->pdev),
272b00647c4SGuenter Roeck 			     data->svi_addr[channel], &regval);
273b00647c4SGuenter Roeck 		regval = (regval >> 16) & 0xff;
274b00647c4SGuenter Roeck 		*val = DIV_ROUND_CLOSEST(155000 - regval * 625, 100);
275b00647c4SGuenter Roeck 		break;
276b00647c4SGuenter Roeck 	default:
277b00647c4SGuenter Roeck 		return -EOPNOTSUPP;
278b00647c4SGuenter Roeck 	}
279b00647c4SGuenter Roeck 	return 0;
280b00647c4SGuenter Roeck }
281b00647c4SGuenter Roeck 
282b00647c4SGuenter Roeck static int k10temp_read_temp(struct device *dev, u32 attr, int channel,
283b00647c4SGuenter Roeck 			     long *val)
284f934c059SGuenter Roeck {
285f934c059SGuenter Roeck 	struct k10temp_data *data = dev_get_drvdata(dev);
2863c57e89bSClemens Ladisch 	u32 regval;
2873c57e89bSClemens Ladisch 
288d547552aSGuenter Roeck 	switch (attr) {
289d547552aSGuenter Roeck 	case hwmon_temp_input:
290d547552aSGuenter Roeck 		switch (channel) {
291b02c6857SGuenter Roeck 		case 0:		/* Tctl */
292b02c6857SGuenter Roeck 			*val = get_raw_temp(data);
293d547552aSGuenter Roeck 			if (*val < 0)
294d547552aSGuenter Roeck 				*val = 0;
295d547552aSGuenter Roeck 			break;
296b02c6857SGuenter Roeck 		case 1:		/* Tdie */
297b02c6857SGuenter Roeck 			*val = get_raw_temp(data) - data->temp_offset;
298d547552aSGuenter Roeck 			if (*val < 0)
299d547552aSGuenter Roeck 				*val = 0;
300d547552aSGuenter Roeck 			break;
301fd8bdb23SGuenter Roeck 		case 2 ... 9:		/* Tccd{1-8} */
302c7579389SGuenter Roeck 			amd_smn_read(amd_pci_dev_to_node_id(data->pdev),
30317822417SWei Huang 				     ZEN_CCD_TEMP(channel - 2), &regval);
30417822417SWei Huang 			*val = (regval & ZEN_CCD_TEMP_MASK) * 125 - 49000;
305c7579389SGuenter Roeck 			break;
306d547552aSGuenter Roeck 		default:
307d547552aSGuenter Roeck 			return -EOPNOTSUPP;
308d547552aSGuenter Roeck 		}
309d547552aSGuenter Roeck 		break;
310d547552aSGuenter Roeck 	case hwmon_temp_max:
311d547552aSGuenter Roeck 		*val = 70 * 1000;
312d547552aSGuenter Roeck 		break;
313d547552aSGuenter Roeck 	case hwmon_temp_crit:
31440626a1bSGuenter Roeck 		data->read_htcreg(data->pdev, &regval);
315d547552aSGuenter Roeck 		*val = ((regval >> 16) & 0x7f) * 500 + 52000;
316d547552aSGuenter Roeck 		break;
317d547552aSGuenter Roeck 	case hwmon_temp_crit_hyst:
318d547552aSGuenter Roeck 		data->read_htcreg(data->pdev, &regval);
319d547552aSGuenter Roeck 		*val = (((regval >> 16) & 0x7f)
320d547552aSGuenter Roeck 			- ((regval >> 24) & 0xf)) * 500 + 52000;
321d547552aSGuenter Roeck 		break;
322d547552aSGuenter Roeck 	default:
323d547552aSGuenter Roeck 		return -EOPNOTSUPP;
324d547552aSGuenter Roeck 	}
325d547552aSGuenter Roeck 	return 0;
3263c57e89bSClemens Ladisch }
3273c57e89bSClemens Ladisch 
328b00647c4SGuenter Roeck static int k10temp_read(struct device *dev, enum hwmon_sensor_types type,
329b00647c4SGuenter Roeck 			u32 attr, int channel, long *val)
330b00647c4SGuenter Roeck {
331b00647c4SGuenter Roeck 	switch (type) {
332b00647c4SGuenter Roeck 	case hwmon_temp:
333b00647c4SGuenter Roeck 		return k10temp_read_temp(dev, attr, channel, val);
334b00647c4SGuenter Roeck 	case hwmon_in:
335b00647c4SGuenter Roeck 		return k10temp_read_in(dev, attr, channel, val);
336b00647c4SGuenter Roeck 	case hwmon_curr:
337b00647c4SGuenter Roeck 		return k10temp_read_curr(dev, attr, channel, val);
338b00647c4SGuenter Roeck 	default:
339b00647c4SGuenter Roeck 		return -EOPNOTSUPP;
340b00647c4SGuenter Roeck 	}
341b00647c4SGuenter Roeck }
342b00647c4SGuenter Roeck 
343d547552aSGuenter Roeck static umode_t k10temp_is_visible(const void *_data,
344d547552aSGuenter Roeck 				  enum hwmon_sensor_types type,
345d547552aSGuenter Roeck 				  u32 attr, int channel)
3463e3e1022SGuenter Roeck {
347d547552aSGuenter Roeck 	const struct k10temp_data *data = _data;
34868546abfSGuenter Roeck 	struct pci_dev *pdev = data->pdev;
34940626a1bSGuenter Roeck 	u32 reg;
35040626a1bSGuenter Roeck 
351d547552aSGuenter Roeck 	switch (type) {
352d547552aSGuenter Roeck 	case hwmon_temp:
353d547552aSGuenter Roeck 		switch (attr) {
354d547552aSGuenter Roeck 		case hwmon_temp_input:
35560465245SGuenter Roeck 			if (!HAVE_TEMP(data, channel))
356d547552aSGuenter Roeck 				return 0;
357f934c059SGuenter Roeck 			break;
358d547552aSGuenter Roeck 		case hwmon_temp_max:
35960465245SGuenter Roeck 			if (channel || data->is_zen)
360d547552aSGuenter Roeck 				return 0;
361d547552aSGuenter Roeck 			break;
362d547552aSGuenter Roeck 		case hwmon_temp_crit:
363d547552aSGuenter Roeck 		case hwmon_temp_crit_hyst:
364d547552aSGuenter Roeck 			if (channel || !data->read_htcreg)
36540626a1bSGuenter Roeck 				return 0;
3663e3e1022SGuenter Roeck 
367d547552aSGuenter Roeck 			pci_read_config_dword(pdev,
368d547552aSGuenter Roeck 					      REG_NORTHBRIDGE_CAPABILITIES,
36940626a1bSGuenter Roeck 					      &reg);
37040626a1bSGuenter Roeck 			if (!(reg & NB_CAP_HTC))
37140626a1bSGuenter Roeck 				return 0;
37240626a1bSGuenter Roeck 
37340626a1bSGuenter Roeck 			data->read_htcreg(data->pdev, &reg);
37440626a1bSGuenter Roeck 			if (!(reg & HTC_ENABLE))
3753e3e1022SGuenter Roeck 				return 0;
376f934c059SGuenter Roeck 			break;
377d547552aSGuenter Roeck 		case hwmon_temp_label:
37860465245SGuenter Roeck 			/* Show temperature labels only on Zen CPUs */
37960465245SGuenter Roeck 			if (!data->is_zen || !HAVE_TEMP(data, channel))
380f934c059SGuenter Roeck 				return 0;
381f934c059SGuenter Roeck 			break;
382d547552aSGuenter Roeck 		default:
383d547552aSGuenter Roeck 			return 0;
3843e3e1022SGuenter Roeck 		}
385d547552aSGuenter Roeck 		break;
386b00647c4SGuenter Roeck 	case hwmon_in:
387b00647c4SGuenter Roeck 	case hwmon_curr:
388b00647c4SGuenter Roeck 		if (!data->show_current)
389b00647c4SGuenter Roeck 			return 0;
390b00647c4SGuenter Roeck 		break;
391d547552aSGuenter Roeck 	default:
392d547552aSGuenter Roeck 		return 0;
3933e3e1022SGuenter Roeck 	}
394d547552aSGuenter Roeck 	return 0444;
395d547552aSGuenter Roeck }
3963c57e89bSClemens Ladisch 
3976c931ae1SBill Pemberton static bool has_erratum_319(struct pci_dev *pdev)
3983c57e89bSClemens Ladisch {
399c5114a1cSClemens Ladisch 	u32 pkg_type, reg_dram_cfg;
400c5114a1cSClemens Ladisch 
401c5114a1cSClemens Ladisch 	if (boot_cpu_data.x86 != 0x10)
402c5114a1cSClemens Ladisch 		return false;
403c5114a1cSClemens Ladisch 
4043c57e89bSClemens Ladisch 	/*
405c5114a1cSClemens Ladisch 	 * Erratum 319: The thermal sensor of Socket F/AM2+ processors
406c5114a1cSClemens Ladisch 	 *              may be unreliable.
4073c57e89bSClemens Ladisch 	 */
408c5114a1cSClemens Ladisch 	pkg_type = cpuid_ebx(0x80000001) & CPUID_PKGTYPE_MASK;
409c5114a1cSClemens Ladisch 	if (pkg_type == CPUID_PKGTYPE_F)
410c5114a1cSClemens Ladisch 		return true;
411c5114a1cSClemens Ladisch 	if (pkg_type != CPUID_PKGTYPE_AM2R2_AM3)
412c5114a1cSClemens Ladisch 		return false;
413c5114a1cSClemens Ladisch 
414eefc2d9eSJean Delvare 	/* DDR3 memory implies socket AM3, which is good */
415c5114a1cSClemens Ladisch 	pci_bus_read_config_dword(pdev->bus,
416c5114a1cSClemens Ladisch 				  PCI_DEVFN(PCI_SLOT(pdev->devfn), 2),
417c5114a1cSClemens Ladisch 				  REG_DCT0_CONFIG_HIGH, &reg_dram_cfg);
418eefc2d9eSJean Delvare 	if (reg_dram_cfg & DDR3_MODE)
419eefc2d9eSJean Delvare 		return false;
420eefc2d9eSJean Delvare 
421eefc2d9eSJean Delvare 	/*
422eefc2d9eSJean Delvare 	 * Unfortunately it is possible to run a socket AM3 CPU with DDR2
423eefc2d9eSJean Delvare 	 * memory. We blacklist all the cores which do exist in socket AM2+
424eefc2d9eSJean Delvare 	 * format. It still isn't perfect, as RB-C2 cores exist in both AM2+
425eefc2d9eSJean Delvare 	 * and AM3 formats, but that's the best we can do.
426eefc2d9eSJean Delvare 	 */
427eefc2d9eSJean Delvare 	return boot_cpu_data.x86_model < 4 ||
428b399151cSJia Zhang 	       (boot_cpu_data.x86_model == 4 && boot_cpu_data.x86_stepping <= 2);
4293c57e89bSClemens Ladisch }
4303c57e89bSClemens Ladisch 
431d547552aSGuenter Roeck static const struct hwmon_channel_info *k10temp_info[] = {
432d547552aSGuenter Roeck 	HWMON_CHANNEL_INFO(temp,
433d547552aSGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_MAX |
434d547552aSGuenter Roeck 			   HWMON_T_CRIT | HWMON_T_CRIT_HYST |
435d547552aSGuenter Roeck 			   HWMON_T_LABEL,
436c7579389SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
437c7579389SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
438fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
439fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
440fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
441fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
442fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
443fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
444d547552aSGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL),
445b00647c4SGuenter Roeck 	HWMON_CHANNEL_INFO(in,
446b00647c4SGuenter Roeck 			   HWMON_I_INPUT | HWMON_I_LABEL,
447b00647c4SGuenter Roeck 			   HWMON_I_INPUT | HWMON_I_LABEL),
448b00647c4SGuenter Roeck 	HWMON_CHANNEL_INFO(curr,
449b00647c4SGuenter Roeck 			   HWMON_C_INPUT | HWMON_C_LABEL,
450b00647c4SGuenter Roeck 			   HWMON_C_INPUT | HWMON_C_LABEL),
451d547552aSGuenter Roeck 	NULL
452d547552aSGuenter Roeck };
453d547552aSGuenter Roeck 
454d547552aSGuenter Roeck static const struct hwmon_ops k10temp_hwmon_ops = {
455d547552aSGuenter Roeck 	.is_visible = k10temp_is_visible,
456d547552aSGuenter Roeck 	.read = k10temp_read,
457d547552aSGuenter Roeck 	.read_string = k10temp_read_labels,
458d547552aSGuenter Roeck };
459d547552aSGuenter Roeck 
460d547552aSGuenter Roeck static const struct hwmon_chip_info k10temp_chip_info = {
461d547552aSGuenter Roeck 	.ops = &k10temp_hwmon_ops,
462d547552aSGuenter Roeck 	.info = k10temp_info,
463d547552aSGuenter Roeck };
464d547552aSGuenter Roeck 
465fd8bdb23SGuenter Roeck static void k10temp_get_ccd_support(struct pci_dev *pdev,
466fd8bdb23SGuenter Roeck 				    struct k10temp_data *data, int limit)
467fd8bdb23SGuenter Roeck {
468fd8bdb23SGuenter Roeck 	u32 regval;
469fd8bdb23SGuenter Roeck 	int i;
470fd8bdb23SGuenter Roeck 
471fd8bdb23SGuenter Roeck 	for (i = 0; i < limit; i++) {
472fd8bdb23SGuenter Roeck 		amd_smn_read(amd_pci_dev_to_node_id(pdev),
47317822417SWei Huang 			     ZEN_CCD_TEMP(i), &regval);
47417822417SWei Huang 		if (regval & ZEN_CCD_TEMP_VALID)
47560465245SGuenter Roeck 			data->show_temp |= BIT(TCCD_BIT(i));
476fd8bdb23SGuenter Roeck 	}
477fd8bdb23SGuenter Roeck }
478fd8bdb23SGuenter Roeck 
479d547552aSGuenter Roeck static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
4803c57e89bSClemens Ladisch {
481c5114a1cSClemens Ladisch 	int unreliable = has_erratum_319(pdev);
4823e3e1022SGuenter Roeck 	struct device *dev = &pdev->dev;
48368546abfSGuenter Roeck 	struct k10temp_data *data;
4843e3e1022SGuenter Roeck 	struct device *hwmon_dev;
4851b50b776SGuenter Roeck 	int i;
4863c57e89bSClemens Ladisch 
4873e3e1022SGuenter Roeck 	if (unreliable) {
4883e3e1022SGuenter Roeck 		if (!force) {
4893e3e1022SGuenter Roeck 			dev_err(dev,
4903c57e89bSClemens Ladisch 				"unreliable CPU thermal sensor; monitoring disabled\n");
4913e3e1022SGuenter Roeck 			return -ENODEV;
4923c57e89bSClemens Ladisch 		}
4933e3e1022SGuenter Roeck 		dev_warn(dev,
4943c57e89bSClemens Ladisch 			 "unreliable CPU thermal sensor; check erratum 319\n");
4953c57e89bSClemens Ladisch 	}
4963c57e89bSClemens Ladisch 
49768546abfSGuenter Roeck 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
49868546abfSGuenter Roeck 	if (!data)
49968546abfSGuenter Roeck 		return -ENOMEM;
50068546abfSGuenter Roeck 
50168546abfSGuenter Roeck 	data->pdev = pdev;
50260465245SGuenter Roeck 	data->show_temp |= BIT(TCTL_BIT);	/* Always show Tctl */
50368546abfSGuenter Roeck 
50453dfa008SGuenter Roeck 	if (boot_cpu_data.x86 == 0x15 &&
50553dfa008SGuenter Roeck 	    ((boot_cpu_data.x86_model & 0xf0) == 0x60 ||
50653dfa008SGuenter Roeck 	     (boot_cpu_data.x86_model & 0xf0) == 0x70)) {
50740626a1bSGuenter Roeck 		data->read_htcreg = read_htcreg_nb_f15;
50868546abfSGuenter Roeck 		data->read_tempreg = read_tempreg_nb_f15;
509d93217d8SPu Wen 	} else if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) {
51017822417SWei Huang 		data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK;
51117822417SWei Huang 		data->read_tempreg = read_tempreg_nb_zen;
51260465245SGuenter Roeck 		data->show_temp |= BIT(TDIE_BIT);	/* show Tdie */
51360465245SGuenter Roeck 		data->is_zen = true;
514c7579389SGuenter Roeck 
515c7579389SGuenter Roeck 		switch (boot_cpu_data.x86_model) {
516c7579389SGuenter Roeck 		case 0x1:	/* Zen */
517c7579389SGuenter Roeck 		case 0x8:	/* Zen+ */
518c7579389SGuenter Roeck 		case 0x11:	/* Zen APU */
519c7579389SGuenter Roeck 		case 0x18:	/* Zen+ APU */
520b00647c4SGuenter Roeck 			data->show_current = !is_threadripper() && !is_epyc();
521b00647c4SGuenter Roeck 			data->svi_addr[0] = F17H_M01H_SVI_TEL_PLANE0;
522b00647c4SGuenter Roeck 			data->svi_addr[1] = F17H_M01H_SVI_TEL_PLANE1;
523d6144a40SWei Huang 			data->cfactor[0] = F17H_M01H_CFACTOR_ICORE;
524d6144a40SWei Huang 			data->cfactor[1] = F17H_M01H_CFACTOR_ISOC;
525fd8bdb23SGuenter Roeck 			k10temp_get_ccd_support(pdev, data, 4);
526c7579389SGuenter Roeck 			break;
527c7579389SGuenter Roeck 		case 0x31:	/* Zen2 Threadripper */
528c7579389SGuenter Roeck 		case 0x71:	/* Zen2 */
529b00647c4SGuenter Roeck 			data->show_current = !is_threadripper() && !is_epyc();
530d6144a40SWei Huang 			data->cfactor[0] = F17H_M31H_CFACTOR_ICORE;
531d6144a40SWei Huang 			data->cfactor[1] = F17H_M31H_CFACTOR_ISOC;
532d6144a40SWei Huang 			data->svi_addr[0] = F17H_M31H_SVI_TEL_PLANE0;
533d6144a40SWei Huang 			data->svi_addr[1] = F17H_M31H_SVI_TEL_PLANE1;
534fd8bdb23SGuenter Roeck 			k10temp_get_ccd_support(pdev, data, 8);
535c7579389SGuenter Roeck 			break;
536c7579389SGuenter Roeck 		}
53755163a1cSWei Huang 	} else if (boot_cpu_data.x86 == 0x19) {
53855163a1cSWei Huang 		data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK;
53955163a1cSWei Huang 		data->read_tempreg = read_tempreg_nb_zen;
54055163a1cSWei Huang 		data->show_temp |= BIT(TDIE_BIT);
54155163a1cSWei Huang 		data->is_zen = true;
54255163a1cSWei Huang 
54355163a1cSWei Huang 		switch (boot_cpu_data.x86_model) {
54455163a1cSWei Huang 		case 0x0 ... 0x1:	/* Zen3 */
54555163a1cSWei Huang 			data->show_current = true;
54655163a1cSWei Huang 			data->svi_addr[0] = F19H_M01_SVI_TEL_PLANE0;
54755163a1cSWei Huang 			data->svi_addr[1] = F19H_M01_SVI_TEL_PLANE1;
54855163a1cSWei Huang 			data->cfactor[0] = F19H_M01H_CFACTOR_ICORE;
54955163a1cSWei Huang 			data->cfactor[1] = F19H_M01H_CFACTOR_ISOC;
55055163a1cSWei Huang 			k10temp_get_ccd_support(pdev, data, 8);
55155163a1cSWei Huang 			break;
55255163a1cSWei Huang 		}
5531b597889SGuenter Roeck 	} else {
55440626a1bSGuenter Roeck 		data->read_htcreg = read_htcreg_pci;
55568546abfSGuenter Roeck 		data->read_tempreg = read_tempreg_pci;
5561b597889SGuenter Roeck 	}
55768546abfSGuenter Roeck 
5581b50b776SGuenter Roeck 	for (i = 0; i < ARRAY_SIZE(tctl_offset_table); i++) {
5591b50b776SGuenter Roeck 		const struct tctl_offset *entry = &tctl_offset_table[i];
5601b50b776SGuenter Roeck 
5611b50b776SGuenter Roeck 		if (boot_cpu_data.x86 == entry->model &&
5621b50b776SGuenter Roeck 		    strstr(boot_cpu_data.x86_model_id, entry->id)) {
5631b50b776SGuenter Roeck 			data->temp_offset = entry->offset;
5641b50b776SGuenter Roeck 			break;
5651b50b776SGuenter Roeck 		}
5661b50b776SGuenter Roeck 	}
5671b50b776SGuenter Roeck 
568d547552aSGuenter Roeck 	hwmon_dev = devm_hwmon_device_register_with_info(dev, "k10temp", data,
569d547552aSGuenter Roeck 							 &k10temp_chip_info,
570d547552aSGuenter Roeck 							 NULL);
5718999eabfSGuenter Roeck 	return PTR_ERR_OR_ZERO(hwmon_dev);
5723c57e89bSClemens Ladisch }
5733c57e89bSClemens Ladisch 
574cd9bb056SJingoo Han static const struct pci_device_id k10temp_id_table[] = {
5753c57e89bSClemens Ladisch 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) },
5763c57e89bSClemens Ladisch 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) },
577aa4790a6SClemens Ladisch 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) },
5789e581311SAndre Przywara 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) },
57924214449SBorislav Petkov 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) },
580d303b1b5SPhil Pokorny 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F3) },
581f89ce270SAravind Gopalakrishnan 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F3) },
582ccaf63b4SGuenter Roeck 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M70H_NB_F3) },
58330b146d1SWei Hu 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) },
584ec015950SAravind Gopalakrishnan 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) },
5859af0a9aeSGuenter Roeck 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_DF_F3) },
5863b031622SGuenter Roeck 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F3) },
587210ba120SWoods, Brian 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M30H_DF_F3) },
588279f0b3aSAlexander Monakov 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M60H_DF_F3) },
58912163cfbSMarcel Bocu 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F3) },
59055163a1cSWei Huang 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_DF_F3) },
591d93217d8SPu Wen 	{ PCI_VDEVICE(HYGON, PCI_DEVICE_ID_AMD_17H_DF_F3) },
5923c57e89bSClemens Ladisch 	{}
5933c57e89bSClemens Ladisch };
5943c57e89bSClemens Ladisch MODULE_DEVICE_TABLE(pci, k10temp_id_table);
5953c57e89bSClemens Ladisch 
5963c57e89bSClemens Ladisch static struct pci_driver k10temp_driver = {
5973c57e89bSClemens Ladisch 	.name = "k10temp",
5983c57e89bSClemens Ladisch 	.id_table = k10temp_id_table,
5993c57e89bSClemens Ladisch 	.probe = k10temp_probe,
6003c57e89bSClemens Ladisch };
6013c57e89bSClemens Ladisch 
602f71f5a55SAxel Lin module_pci_driver(k10temp_driver);
603