xref: /openbmc/linux/drivers/hwmon/k10temp.c (revision 0e3f52bb)
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.
143c57e89bSClemens Ladisch  */
153c57e89bSClemens Ladisch 
16a6d210daSGuenter Roeck #include <linux/bitops.h>
173c57e89bSClemens Ladisch #include <linux/err.h>
183c57e89bSClemens Ladisch #include <linux/hwmon.h>
193c57e89bSClemens Ladisch #include <linux/init.h>
203c57e89bSClemens Ladisch #include <linux/module.h>
213c57e89bSClemens Ladisch #include <linux/pci.h>
22dedf7dceSWoods, Brian #include <linux/pci_ids.h>
233b031622SGuenter Roeck #include <asm/amd_nb.h>
243c57e89bSClemens Ladisch #include <asm/processor.h>
253c57e89bSClemens Ladisch 
269e581311SAndre Przywara MODULE_DESCRIPTION("AMD Family 10h+ CPU core temperature monitor");
273c57e89bSClemens Ladisch MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
283c57e89bSClemens Ladisch MODULE_LICENSE("GPL");
293c57e89bSClemens Ladisch 
303c57e89bSClemens Ladisch static bool force;
313c57e89bSClemens Ladisch module_param(force, bool, 0444);
323c57e89bSClemens Ladisch MODULE_PARM_DESC(force, "force loading on processors with erratum 319");
333c57e89bSClemens Ladisch 
34f89ce270SAravind Gopalakrishnan /* Provide lock for writing to NB_SMU_IND_ADDR */
35f89ce270SAravind Gopalakrishnan static DEFINE_MUTEX(nb_smu_ind_mutex);
36f89ce270SAravind Gopalakrishnan 
37ccaf63b4SGuenter Roeck #ifndef PCI_DEVICE_ID_AMD_15H_M70H_NB_F3
38ccaf63b4SGuenter Roeck #define PCI_DEVICE_ID_AMD_15H_M70H_NB_F3	0x15b3
39ccaf63b4SGuenter Roeck #endif
40ccaf63b4SGuenter Roeck 
41c5114a1cSClemens Ladisch /* CPUID function 0x80000001, ebx */
42a6d210daSGuenter Roeck #define CPUID_PKGTYPE_MASK	GENMASK(31, 28)
43c5114a1cSClemens Ladisch #define CPUID_PKGTYPE_F		0x00000000
44c5114a1cSClemens Ladisch #define CPUID_PKGTYPE_AM2R2_AM3	0x10000000
45c5114a1cSClemens Ladisch 
46c5114a1cSClemens Ladisch /* DRAM controller (PCI function 2) */
47c5114a1cSClemens Ladisch #define REG_DCT0_CONFIG_HIGH		0x094
48a6d210daSGuenter Roeck #define  DDR3_MODE			BIT(8)
49c5114a1cSClemens Ladisch 
50c5114a1cSClemens Ladisch /* miscellaneous (PCI function 3) */
513c57e89bSClemens Ladisch #define REG_HARDWARE_THERMAL_CONTROL	0x64
52a6d210daSGuenter Roeck #define  HTC_ENABLE			BIT(0)
533c57e89bSClemens Ladisch 
543c57e89bSClemens Ladisch #define REG_REPORTED_TEMPERATURE	0xa4
553c57e89bSClemens Ladisch 
563c57e89bSClemens Ladisch #define REG_NORTHBRIDGE_CAPABILITIES	0xe8
57a6d210daSGuenter Roeck #define  NB_CAP_HTC			BIT(10)
583c57e89bSClemens Ladisch 
59f89ce270SAravind Gopalakrishnan /*
6040626a1bSGuenter Roeck  * For F15h M60h and M70h, REG_HARDWARE_THERMAL_CONTROL
6140626a1bSGuenter Roeck  * and REG_REPORTED_TEMPERATURE have been moved to
6240626a1bSGuenter Roeck  * D0F0xBC_xD820_0C64 [Hardware Temperature Control]
6340626a1bSGuenter Roeck  * D0F0xBC_xD820_0CA4 [Reported Temperature Control]
64f89ce270SAravind Gopalakrishnan  */
6540626a1bSGuenter Roeck #define F15H_M60H_HARDWARE_TEMP_CTRL_OFFSET	0xd8200c64
66f89ce270SAravind Gopalakrishnan #define F15H_M60H_REPORTED_TEMP_CTRL_OFFSET	0xd8200ca4
67f89ce270SAravind Gopalakrishnan 
68*0e3f52bbSMario Limonciello /* Common for Zen CPU families (Family 17h and 18h and 19h) */
69*0e3f52bbSMario Limonciello #define ZEN_REPORTED_TEMP_CTRL_BASE		0x00059800
70fd8bdb23SGuenter Roeck 
71*0e3f52bbSMario Limonciello #define ZEN_CCD_TEMP(offset, x)			(ZEN_REPORTED_TEMP_CTRL_BASE + \
72*0e3f52bbSMario Limonciello 						 (offset) + ((x) * 4))
7317822417SWei Huang #define ZEN_CCD_TEMP_VALID			BIT(11)
7417822417SWei Huang #define ZEN_CCD_TEMP_MASK			GENMASK(10, 0)
759af0a9aeSGuenter Roeck 
7617822417SWei Huang #define ZEN_CUR_TEMP_SHIFT			21
7717822417SWei Huang #define ZEN_CUR_TEMP_RANGE_SEL_MASK		BIT(19)
78b00647c4SGuenter Roeck 
7917822417SWei Huang #define ZEN_SVI_BASE				0x0005A000
80a6d210daSGuenter Roeck 
8117822417SWei Huang /* F17h thermal registers through SMN */
8217822417SWei Huang #define F17H_M01H_SVI_TEL_PLANE0		(ZEN_SVI_BASE + 0xc)
8317822417SWei Huang #define F17H_M01H_SVI_TEL_PLANE1		(ZEN_SVI_BASE + 0x10)
84d6144a40SWei Huang #define F17H_M31H_SVI_TEL_PLANE0		(ZEN_SVI_BASE + 0x14)
85d6144a40SWei Huang #define F17H_M31H_SVI_TEL_PLANE1		(ZEN_SVI_BASE + 0x10)
8617822417SWei Huang 
87d6144a40SWei Huang #define F17H_M01H_CFACTOR_ICORE			1000000	/* 1A / LSB	*/
88d6144a40SWei Huang #define F17H_M01H_CFACTOR_ISOC			250000	/* 0.25A / LSB	*/
89d6144a40SWei Huang #define F17H_M31H_CFACTOR_ICORE			1000000	/* 1A / LSB	*/
90d6144a40SWei Huang #define F17H_M31H_CFACTOR_ISOC			310000	/* 0.31A / LSB	*/
91b00647c4SGuenter Roeck 
9255163a1cSWei Huang /* F19h thermal registers through SMN */
9355163a1cSWei Huang #define F19H_M01_SVI_TEL_PLANE0			(ZEN_SVI_BASE + 0x14)
9455163a1cSWei Huang #define F19H_M01_SVI_TEL_PLANE1			(ZEN_SVI_BASE + 0x10)
9555163a1cSWei Huang 
9655163a1cSWei Huang #define F19H_M01H_CFACTOR_ICORE			1000000	/* 1A / LSB	*/
9755163a1cSWei Huang #define F19H_M01H_CFACTOR_ISOC			310000	/* 0.31A / LSB	*/
9855163a1cSWei Huang 
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;
10660465245SGuenter Roeck 	bool is_zen;
107*0e3f52bbSMario Limonciello 	u32 ccd_offset;
1081b50b776SGuenter Roeck };
1091b50b776SGuenter Roeck 
11060465245SGuenter Roeck #define TCTL_BIT	0
11160465245SGuenter Roeck #define TDIE_BIT	1
11260465245SGuenter Roeck #define TCCD_BIT(x)	((x) + 2)
11360465245SGuenter Roeck 
11460465245SGuenter Roeck #define HAVE_TEMP(d, channel)	((d)->show_temp & BIT(channel))
11560465245SGuenter Roeck #define HAVE_TDIE(d)		HAVE_TEMP(d, TDIE_BIT)
11660465245SGuenter Roeck 
1171b50b776SGuenter Roeck struct tctl_offset {
1181b50b776SGuenter Roeck 	u8 model;
1191b50b776SGuenter Roeck 	char const *id;
1201b50b776SGuenter Roeck 	int offset;
1211b50b776SGuenter Roeck };
1221b50b776SGuenter Roeck 
1231b50b776SGuenter Roeck static const struct tctl_offset tctl_offset_table[] = {
124ab5ee246SGuenter Roeck 	{ 0x17, "AMD Ryzen 5 1600X", 20000 },
1251b50b776SGuenter Roeck 	{ 0x17, "AMD Ryzen 7 1700X", 20000 },
1261b50b776SGuenter Roeck 	{ 0x17, "AMD Ryzen 7 1800X", 20000 },
1271b597889SGuenter Roeck 	{ 0x17, "AMD Ryzen 7 2700X", 10000 },
128cd6a2064SGuenter Roeck 	{ 0x17, "AMD Ryzen Threadripper 19", 27000 }, /* 19{00,20,50}X */
129cd6a2064SGuenter Roeck 	{ 0x17, "AMD Ryzen Threadripper 29", 27000 }, /* 29{20,50,70,90}[W]X */
13068546abfSGuenter Roeck };
13168546abfSGuenter Roeck 
13240626a1bSGuenter Roeck static void read_htcreg_pci(struct pci_dev *pdev, u32 *regval)
13340626a1bSGuenter Roeck {
13440626a1bSGuenter Roeck 	pci_read_config_dword(pdev, REG_HARDWARE_THERMAL_CONTROL, regval);
13540626a1bSGuenter Roeck }
13640626a1bSGuenter Roeck 
13768546abfSGuenter Roeck static void read_tempreg_pci(struct pci_dev *pdev, u32 *regval)
13868546abfSGuenter Roeck {
13968546abfSGuenter Roeck 	pci_read_config_dword(pdev, REG_REPORTED_TEMPERATURE, regval);
14068546abfSGuenter Roeck }
14168546abfSGuenter Roeck 
14268546abfSGuenter Roeck static void amd_nb_index_read(struct pci_dev *pdev, unsigned int devfn,
14368546abfSGuenter Roeck 			      unsigned int base, int offset, u32 *val)
144f89ce270SAravind Gopalakrishnan {
145f89ce270SAravind Gopalakrishnan 	mutex_lock(&nb_smu_ind_mutex);
146f89ce270SAravind Gopalakrishnan 	pci_bus_write_config_dword(pdev->bus, devfn,
14768546abfSGuenter Roeck 				   base, offset);
148f89ce270SAravind Gopalakrishnan 	pci_bus_read_config_dword(pdev->bus, devfn,
14968546abfSGuenter Roeck 				  base + 4, val);
150f89ce270SAravind Gopalakrishnan 	mutex_unlock(&nb_smu_ind_mutex);
151f89ce270SAravind Gopalakrishnan }
152f89ce270SAravind Gopalakrishnan 
15340626a1bSGuenter Roeck static void read_htcreg_nb_f15(struct pci_dev *pdev, u32 *regval)
15440626a1bSGuenter Roeck {
15540626a1bSGuenter Roeck 	amd_nb_index_read(pdev, PCI_DEVFN(0, 0), 0xb8,
15640626a1bSGuenter Roeck 			  F15H_M60H_HARDWARE_TEMP_CTRL_OFFSET, regval);
15740626a1bSGuenter Roeck }
15840626a1bSGuenter Roeck 
15968546abfSGuenter Roeck static void read_tempreg_nb_f15(struct pci_dev *pdev, u32 *regval)
16068546abfSGuenter Roeck {
16168546abfSGuenter Roeck 	amd_nb_index_read(pdev, PCI_DEVFN(0, 0), 0xb8,
16268546abfSGuenter Roeck 			  F15H_M60H_REPORTED_TEMP_CTRL_OFFSET, regval);
16368546abfSGuenter Roeck }
16468546abfSGuenter Roeck 
16517822417SWei Huang static void read_tempreg_nb_zen(struct pci_dev *pdev, u32 *regval)
1669af0a9aeSGuenter Roeck {
1673b031622SGuenter Roeck 	amd_smn_read(amd_pci_dev_to_node_id(pdev),
168*0e3f52bbSMario Limonciello 		     ZEN_REPORTED_TEMP_CTRL_BASE, regval);
1699af0a9aeSGuenter Roeck }
1709af0a9aeSGuenter Roeck 
171d547552aSGuenter Roeck static long get_raw_temp(struct k10temp_data *data)
1723c57e89bSClemens Ladisch {
173f934c059SGuenter Roeck 	u32 regval;
174d547552aSGuenter Roeck 	long temp;
1753c57e89bSClemens Ladisch 
17668546abfSGuenter Roeck 	data->read_tempreg(data->pdev, &regval);
17717822417SWei Huang 	temp = (regval >> ZEN_CUR_TEMP_SHIFT) * 125;
1781b597889SGuenter Roeck 	if (regval & data->temp_adjust_mask)
1791b597889SGuenter Roeck 		temp -= 49000;
180f934c059SGuenter Roeck 	return temp;
181f934c059SGuenter Roeck }
182f934c059SGuenter Roeck 
1830e786f32SJason Yan static const char *k10temp_temp_label[] = {
184d547552aSGuenter Roeck 	"Tctl",
185b02c6857SGuenter Roeck 	"Tdie",
186c7579389SGuenter Roeck 	"Tccd1",
187c7579389SGuenter Roeck 	"Tccd2",
188fd8bdb23SGuenter Roeck 	"Tccd3",
189fd8bdb23SGuenter Roeck 	"Tccd4",
190fd8bdb23SGuenter Roeck 	"Tccd5",
191fd8bdb23SGuenter Roeck 	"Tccd6",
192fd8bdb23SGuenter Roeck 	"Tccd7",
193fd8bdb23SGuenter Roeck 	"Tccd8",
194d547552aSGuenter Roeck };
195d547552aSGuenter Roeck 
196d547552aSGuenter Roeck static int k10temp_read_labels(struct device *dev,
197d547552aSGuenter Roeck 			       enum hwmon_sensor_types type,
198d547552aSGuenter Roeck 			       u32 attr, int channel, const char **str)
199d547552aSGuenter Roeck {
200b00647c4SGuenter Roeck 	switch (type) {
201b00647c4SGuenter Roeck 	case hwmon_temp:
202d547552aSGuenter Roeck 		*str = k10temp_temp_label[channel];
203b00647c4SGuenter Roeck 		break;
204b00647c4SGuenter Roeck 	default:
205b00647c4SGuenter Roeck 		return -EOPNOTSUPP;
206b00647c4SGuenter Roeck 	}
207b00647c4SGuenter Roeck 	return 0;
208b00647c4SGuenter Roeck }
209b00647c4SGuenter Roeck 
210b00647c4SGuenter Roeck static int k10temp_read_temp(struct device *dev, u32 attr, int channel,
211b00647c4SGuenter Roeck 			     long *val)
212f934c059SGuenter Roeck {
213f934c059SGuenter Roeck 	struct k10temp_data *data = dev_get_drvdata(dev);
2143c57e89bSClemens Ladisch 	u32 regval;
2153c57e89bSClemens Ladisch 
216d547552aSGuenter Roeck 	switch (attr) {
217d547552aSGuenter Roeck 	case hwmon_temp_input:
218d547552aSGuenter Roeck 		switch (channel) {
219b02c6857SGuenter Roeck 		case 0:		/* Tctl */
220b02c6857SGuenter Roeck 			*val = get_raw_temp(data);
221d547552aSGuenter Roeck 			if (*val < 0)
222d547552aSGuenter Roeck 				*val = 0;
223d547552aSGuenter Roeck 			break;
224b02c6857SGuenter Roeck 		case 1:		/* Tdie */
225b02c6857SGuenter Roeck 			*val = get_raw_temp(data) - data->temp_offset;
226d547552aSGuenter Roeck 			if (*val < 0)
227d547552aSGuenter Roeck 				*val = 0;
228d547552aSGuenter Roeck 			break;
229fd8bdb23SGuenter Roeck 		case 2 ... 9:		/* Tccd{1-8} */
230c7579389SGuenter Roeck 			amd_smn_read(amd_pci_dev_to_node_id(data->pdev),
231*0e3f52bbSMario Limonciello 				     ZEN_CCD_TEMP(data->ccd_offset, channel - 2),
232*0e3f52bbSMario Limonciello 						  &regval);
23317822417SWei Huang 			*val = (regval & ZEN_CCD_TEMP_MASK) * 125 - 49000;
234c7579389SGuenter Roeck 			break;
235d547552aSGuenter Roeck 		default:
236d547552aSGuenter Roeck 			return -EOPNOTSUPP;
237d547552aSGuenter Roeck 		}
238d547552aSGuenter Roeck 		break;
239d547552aSGuenter Roeck 	case hwmon_temp_max:
240d547552aSGuenter Roeck 		*val = 70 * 1000;
241d547552aSGuenter Roeck 		break;
242d547552aSGuenter Roeck 	case hwmon_temp_crit:
24340626a1bSGuenter Roeck 		data->read_htcreg(data->pdev, &regval);
244d547552aSGuenter Roeck 		*val = ((regval >> 16) & 0x7f) * 500 + 52000;
245d547552aSGuenter Roeck 		break;
246d547552aSGuenter Roeck 	case hwmon_temp_crit_hyst:
247d547552aSGuenter Roeck 		data->read_htcreg(data->pdev, &regval);
248d547552aSGuenter Roeck 		*val = (((regval >> 16) & 0x7f)
249d547552aSGuenter Roeck 			- ((regval >> 24) & 0xf)) * 500 + 52000;
250d547552aSGuenter Roeck 		break;
251d547552aSGuenter Roeck 	default:
252d547552aSGuenter Roeck 		return -EOPNOTSUPP;
253d547552aSGuenter Roeck 	}
254d547552aSGuenter Roeck 	return 0;
2553c57e89bSClemens Ladisch }
2563c57e89bSClemens Ladisch 
257b00647c4SGuenter Roeck static int k10temp_read(struct device *dev, enum hwmon_sensor_types type,
258b00647c4SGuenter Roeck 			u32 attr, int channel, long *val)
259b00647c4SGuenter Roeck {
260b00647c4SGuenter Roeck 	switch (type) {
261b00647c4SGuenter Roeck 	case hwmon_temp:
262b00647c4SGuenter Roeck 		return k10temp_read_temp(dev, attr, channel, val);
263b00647c4SGuenter Roeck 	default:
264b00647c4SGuenter Roeck 		return -EOPNOTSUPP;
265b00647c4SGuenter Roeck 	}
266b00647c4SGuenter Roeck }
267b00647c4SGuenter Roeck 
268d547552aSGuenter Roeck static umode_t k10temp_is_visible(const void *_data,
269d547552aSGuenter Roeck 				  enum hwmon_sensor_types type,
270d547552aSGuenter Roeck 				  u32 attr, int channel)
2713e3e1022SGuenter Roeck {
272d547552aSGuenter Roeck 	const struct k10temp_data *data = _data;
27368546abfSGuenter Roeck 	struct pci_dev *pdev = data->pdev;
27440626a1bSGuenter Roeck 	u32 reg;
27540626a1bSGuenter Roeck 
276d547552aSGuenter Roeck 	switch (type) {
277d547552aSGuenter Roeck 	case hwmon_temp:
278d547552aSGuenter Roeck 		switch (attr) {
279d547552aSGuenter Roeck 		case hwmon_temp_input:
28060465245SGuenter Roeck 			if (!HAVE_TEMP(data, channel))
281d547552aSGuenter Roeck 				return 0;
282f934c059SGuenter Roeck 			break;
283d547552aSGuenter Roeck 		case hwmon_temp_max:
28460465245SGuenter Roeck 			if (channel || data->is_zen)
285d547552aSGuenter Roeck 				return 0;
286d547552aSGuenter Roeck 			break;
287d547552aSGuenter Roeck 		case hwmon_temp_crit:
288d547552aSGuenter Roeck 		case hwmon_temp_crit_hyst:
289d547552aSGuenter Roeck 			if (channel || !data->read_htcreg)
29040626a1bSGuenter Roeck 				return 0;
2913e3e1022SGuenter Roeck 
292d547552aSGuenter Roeck 			pci_read_config_dword(pdev,
293d547552aSGuenter Roeck 					      REG_NORTHBRIDGE_CAPABILITIES,
29440626a1bSGuenter Roeck 					      &reg);
29540626a1bSGuenter Roeck 			if (!(reg & NB_CAP_HTC))
29640626a1bSGuenter Roeck 				return 0;
29740626a1bSGuenter Roeck 
29840626a1bSGuenter Roeck 			data->read_htcreg(data->pdev, &reg);
29940626a1bSGuenter Roeck 			if (!(reg & HTC_ENABLE))
3003e3e1022SGuenter Roeck 				return 0;
301f934c059SGuenter Roeck 			break;
302d547552aSGuenter Roeck 		case hwmon_temp_label:
30360465245SGuenter Roeck 			/* Show temperature labels only on Zen CPUs */
30460465245SGuenter Roeck 			if (!data->is_zen || !HAVE_TEMP(data, channel))
305f934c059SGuenter Roeck 				return 0;
306f934c059SGuenter Roeck 			break;
307d547552aSGuenter Roeck 		default:
308d547552aSGuenter Roeck 			return 0;
3093e3e1022SGuenter Roeck 		}
310d547552aSGuenter Roeck 		break;
311d547552aSGuenter Roeck 	default:
312d547552aSGuenter Roeck 		return 0;
3133e3e1022SGuenter Roeck 	}
314d547552aSGuenter Roeck 	return 0444;
315d547552aSGuenter Roeck }
3163c57e89bSClemens Ladisch 
3176c931ae1SBill Pemberton static bool has_erratum_319(struct pci_dev *pdev)
3183c57e89bSClemens Ladisch {
319c5114a1cSClemens Ladisch 	u32 pkg_type, reg_dram_cfg;
320c5114a1cSClemens Ladisch 
321c5114a1cSClemens Ladisch 	if (boot_cpu_data.x86 != 0x10)
322c5114a1cSClemens Ladisch 		return false;
323c5114a1cSClemens Ladisch 
3243c57e89bSClemens Ladisch 	/*
325c5114a1cSClemens Ladisch 	 * Erratum 319: The thermal sensor of Socket F/AM2+ processors
326c5114a1cSClemens Ladisch 	 *              may be unreliable.
3273c57e89bSClemens Ladisch 	 */
328c5114a1cSClemens Ladisch 	pkg_type = cpuid_ebx(0x80000001) & CPUID_PKGTYPE_MASK;
329c5114a1cSClemens Ladisch 	if (pkg_type == CPUID_PKGTYPE_F)
330c5114a1cSClemens Ladisch 		return true;
331c5114a1cSClemens Ladisch 	if (pkg_type != CPUID_PKGTYPE_AM2R2_AM3)
332c5114a1cSClemens Ladisch 		return false;
333c5114a1cSClemens Ladisch 
334eefc2d9eSJean Delvare 	/* DDR3 memory implies socket AM3, which is good */
335c5114a1cSClemens Ladisch 	pci_bus_read_config_dword(pdev->bus,
336c5114a1cSClemens Ladisch 				  PCI_DEVFN(PCI_SLOT(pdev->devfn), 2),
337c5114a1cSClemens Ladisch 				  REG_DCT0_CONFIG_HIGH, &reg_dram_cfg);
338eefc2d9eSJean Delvare 	if (reg_dram_cfg & DDR3_MODE)
339eefc2d9eSJean Delvare 		return false;
340eefc2d9eSJean Delvare 
341eefc2d9eSJean Delvare 	/*
342eefc2d9eSJean Delvare 	 * Unfortunately it is possible to run a socket AM3 CPU with DDR2
343eefc2d9eSJean Delvare 	 * memory. We blacklist all the cores which do exist in socket AM2+
344eefc2d9eSJean Delvare 	 * format. It still isn't perfect, as RB-C2 cores exist in both AM2+
345eefc2d9eSJean Delvare 	 * and AM3 formats, but that's the best we can do.
346eefc2d9eSJean Delvare 	 */
347eefc2d9eSJean Delvare 	return boot_cpu_data.x86_model < 4 ||
348b399151cSJia Zhang 	       (boot_cpu_data.x86_model == 4 && boot_cpu_data.x86_stepping <= 2);
3493c57e89bSClemens Ladisch }
3503c57e89bSClemens Ladisch 
351d547552aSGuenter Roeck static const struct hwmon_channel_info *k10temp_info[] = {
352d547552aSGuenter Roeck 	HWMON_CHANNEL_INFO(temp,
353d547552aSGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_MAX |
354d547552aSGuenter Roeck 			   HWMON_T_CRIT | HWMON_T_CRIT_HYST |
355d547552aSGuenter Roeck 			   HWMON_T_LABEL,
356c7579389SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
357c7579389SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
358fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
359fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
360fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
361fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
362fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
363fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
364d547552aSGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL),
365b00647c4SGuenter Roeck 	HWMON_CHANNEL_INFO(in,
366b00647c4SGuenter Roeck 			   HWMON_I_INPUT | HWMON_I_LABEL,
367b00647c4SGuenter Roeck 			   HWMON_I_INPUT | HWMON_I_LABEL),
368b00647c4SGuenter Roeck 	HWMON_CHANNEL_INFO(curr,
369b00647c4SGuenter Roeck 			   HWMON_C_INPUT | HWMON_C_LABEL,
370b00647c4SGuenter Roeck 			   HWMON_C_INPUT | HWMON_C_LABEL),
371d547552aSGuenter Roeck 	NULL
372d547552aSGuenter Roeck };
373d547552aSGuenter Roeck 
374d547552aSGuenter Roeck static const struct hwmon_ops k10temp_hwmon_ops = {
375d547552aSGuenter Roeck 	.is_visible = k10temp_is_visible,
376d547552aSGuenter Roeck 	.read = k10temp_read,
377d547552aSGuenter Roeck 	.read_string = k10temp_read_labels,
378d547552aSGuenter Roeck };
379d547552aSGuenter Roeck 
380d547552aSGuenter Roeck static const struct hwmon_chip_info k10temp_chip_info = {
381d547552aSGuenter Roeck 	.ops = &k10temp_hwmon_ops,
382d547552aSGuenter Roeck 	.info = k10temp_info,
383d547552aSGuenter Roeck };
384d547552aSGuenter Roeck 
385fd8bdb23SGuenter Roeck static void k10temp_get_ccd_support(struct pci_dev *pdev,
386fd8bdb23SGuenter Roeck 				    struct k10temp_data *data, int limit)
387fd8bdb23SGuenter Roeck {
388fd8bdb23SGuenter Roeck 	u32 regval;
389fd8bdb23SGuenter Roeck 	int i;
390fd8bdb23SGuenter Roeck 
391fd8bdb23SGuenter Roeck 	for (i = 0; i < limit; i++) {
392fd8bdb23SGuenter Roeck 		amd_smn_read(amd_pci_dev_to_node_id(pdev),
393*0e3f52bbSMario Limonciello 			     ZEN_CCD_TEMP(data->ccd_offset, i), &regval);
39417822417SWei Huang 		if (regval & ZEN_CCD_TEMP_VALID)
39560465245SGuenter Roeck 			data->show_temp |= BIT(TCCD_BIT(i));
396fd8bdb23SGuenter Roeck 	}
397fd8bdb23SGuenter Roeck }
398fd8bdb23SGuenter Roeck 
399d547552aSGuenter Roeck static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
4003c57e89bSClemens Ladisch {
401c5114a1cSClemens Ladisch 	int unreliable = has_erratum_319(pdev);
4023e3e1022SGuenter Roeck 	struct device *dev = &pdev->dev;
40368546abfSGuenter Roeck 	struct k10temp_data *data;
4043e3e1022SGuenter Roeck 	struct device *hwmon_dev;
4051b50b776SGuenter Roeck 	int i;
4063c57e89bSClemens Ladisch 
4073e3e1022SGuenter Roeck 	if (unreliable) {
4083e3e1022SGuenter Roeck 		if (!force) {
4093e3e1022SGuenter Roeck 			dev_err(dev,
4103c57e89bSClemens Ladisch 				"unreliable CPU thermal sensor; monitoring disabled\n");
4113e3e1022SGuenter Roeck 			return -ENODEV;
4123c57e89bSClemens Ladisch 		}
4133e3e1022SGuenter Roeck 		dev_warn(dev,
4143c57e89bSClemens Ladisch 			 "unreliable CPU thermal sensor; check erratum 319\n");
4153c57e89bSClemens Ladisch 	}
4163c57e89bSClemens Ladisch 
41768546abfSGuenter Roeck 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
41868546abfSGuenter Roeck 	if (!data)
41968546abfSGuenter Roeck 		return -ENOMEM;
42068546abfSGuenter Roeck 
42168546abfSGuenter Roeck 	data->pdev = pdev;
42260465245SGuenter Roeck 	data->show_temp |= BIT(TCTL_BIT);	/* Always show Tctl */
42368546abfSGuenter Roeck 
42453dfa008SGuenter Roeck 	if (boot_cpu_data.x86 == 0x15 &&
42553dfa008SGuenter Roeck 	    ((boot_cpu_data.x86_model & 0xf0) == 0x60 ||
42653dfa008SGuenter Roeck 	     (boot_cpu_data.x86_model & 0xf0) == 0x70)) {
42740626a1bSGuenter Roeck 		data->read_htcreg = read_htcreg_nb_f15;
42868546abfSGuenter Roeck 		data->read_tempreg = read_tempreg_nb_f15;
429d93217d8SPu Wen 	} else if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) {
43017822417SWei Huang 		data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK;
43117822417SWei Huang 		data->read_tempreg = read_tempreg_nb_zen;
43260465245SGuenter Roeck 		data->is_zen = true;
433c7579389SGuenter Roeck 
434c7579389SGuenter Roeck 		switch (boot_cpu_data.x86_model) {
435c7579389SGuenter Roeck 		case 0x1:	/* Zen */
436c7579389SGuenter Roeck 		case 0x8:	/* Zen+ */
437c7579389SGuenter Roeck 		case 0x11:	/* Zen APU */
438c7579389SGuenter Roeck 		case 0x18:	/* Zen+ APU */
439*0e3f52bbSMario Limonciello 			data->ccd_offset = 0x154;
440fd8bdb23SGuenter Roeck 			k10temp_get_ccd_support(pdev, data, 4);
441c7579389SGuenter Roeck 			break;
442c7579389SGuenter Roeck 		case 0x31:	/* Zen2 Threadripper */
443128066c8SMario Limonciello 		case 0x60:	/* Renoir */
444128066c8SMario Limonciello 		case 0x68:	/* Lucienne */
445c7579389SGuenter Roeck 		case 0x71:	/* Zen2 */
446*0e3f52bbSMario Limonciello 			data->ccd_offset = 0x154;
447fd8bdb23SGuenter Roeck 			k10temp_get_ccd_support(pdev, data, 8);
448c7579389SGuenter Roeck 			break;
449c7579389SGuenter Roeck 		}
45055163a1cSWei Huang 	} else if (boot_cpu_data.x86 == 0x19) {
45155163a1cSWei Huang 		data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK;
45255163a1cSWei Huang 		data->read_tempreg = read_tempreg_nb_zen;
45355163a1cSWei Huang 		data->is_zen = true;
45455163a1cSWei Huang 
45555163a1cSWei Huang 		switch (boot_cpu_data.x86_model) {
456c8d0d3faSGabriel Craciunescu 		case 0x0 ... 0x1:	/* Zen3 SP3/TR */
457c8d0d3faSGabriel Craciunescu 		case 0x21:		/* Zen3 Ryzen Desktop */
458128066c8SMario Limonciello 		case 0x50 ... 0x5f:	/* Green Sardine */
459*0e3f52bbSMario Limonciello 			data->ccd_offset = 0x154;
46055163a1cSWei Huang 			k10temp_get_ccd_support(pdev, data, 8);
46155163a1cSWei Huang 			break;
46255163a1cSWei Huang 		}
4631b597889SGuenter Roeck 	} else {
46440626a1bSGuenter Roeck 		data->read_htcreg = read_htcreg_pci;
46568546abfSGuenter Roeck 		data->read_tempreg = read_tempreg_pci;
4661b597889SGuenter Roeck 	}
46768546abfSGuenter Roeck 
4681b50b776SGuenter Roeck 	for (i = 0; i < ARRAY_SIZE(tctl_offset_table); i++) {
4691b50b776SGuenter Roeck 		const struct tctl_offset *entry = &tctl_offset_table[i];
4701b50b776SGuenter Roeck 
4711b50b776SGuenter Roeck 		if (boot_cpu_data.x86 == entry->model &&
4721b50b776SGuenter Roeck 		    strstr(boot_cpu_data.x86_model_id, entry->id)) {
47302a2484cSMario Limonciello 			data->show_temp |= BIT(TDIE_BIT);	/* show Tdie */
4741b50b776SGuenter Roeck 			data->temp_offset = entry->offset;
4751b50b776SGuenter Roeck 			break;
4761b50b776SGuenter Roeck 		}
4771b50b776SGuenter Roeck 	}
4781b50b776SGuenter Roeck 
479d547552aSGuenter Roeck 	hwmon_dev = devm_hwmon_device_register_with_info(dev, "k10temp", data,
480d547552aSGuenter Roeck 							 &k10temp_chip_info,
481d547552aSGuenter Roeck 							 NULL);
4828999eabfSGuenter Roeck 	return PTR_ERR_OR_ZERO(hwmon_dev);
4833c57e89bSClemens Ladisch }
4843c57e89bSClemens Ladisch 
485cd9bb056SJingoo Han static const struct pci_device_id k10temp_id_table[] = {
4863c57e89bSClemens Ladisch 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) },
4873c57e89bSClemens Ladisch 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) },
488aa4790a6SClemens Ladisch 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) },
4899e581311SAndre Przywara 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) },
49024214449SBorislav Petkov 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) },
491d303b1b5SPhil Pokorny 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F3) },
492f89ce270SAravind Gopalakrishnan 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F3) },
493ccaf63b4SGuenter Roeck 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M70H_NB_F3) },
49430b146d1SWei Hu 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) },
495ec015950SAravind Gopalakrishnan 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) },
4969af0a9aeSGuenter Roeck 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_DF_F3) },
4973b031622SGuenter Roeck 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F3) },
498210ba120SWoods, Brian 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M30H_DF_F3) },
499279f0b3aSAlexander Monakov 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M60H_DF_F3) },
50012163cfbSMarcel Bocu 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F3) },
50155163a1cSWei Huang 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_DF_F3) },
50202c9dce4SDavid Bartley 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M50H_DF_F3) },
503d93217d8SPu Wen 	{ PCI_VDEVICE(HYGON, PCI_DEVICE_ID_AMD_17H_DF_F3) },
5043c57e89bSClemens Ladisch 	{}
5053c57e89bSClemens Ladisch };
5063c57e89bSClemens Ladisch MODULE_DEVICE_TABLE(pci, k10temp_id_table);
5073c57e89bSClemens Ladisch 
5083c57e89bSClemens Ladisch static struct pci_driver k10temp_driver = {
5093c57e89bSClemens Ladisch 	.name = "k10temp",
5103c57e89bSClemens Ladisch 	.id_table = k10temp_id_table,
5113c57e89bSClemens Ladisch 	.probe = k10temp_probe,
5123c57e89bSClemens Ladisch };
5133c57e89bSClemens Ladisch 
514f71f5a55SAxel Lin module_pci_driver(k10temp_driver);
515