xref: /openbmc/linux/drivers/hwmon/k10temp.c (revision 3cd9da41)
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*3cd9da41SAvadhut Naik /* Common for Zen CPU families (Family 17h and 18h and 19h and 1Ah) */
690e3f52bbSMario Limonciello #define ZEN_REPORTED_TEMP_CTRL_BASE		0x00059800
70fd8bdb23SGuenter Roeck 
710e3f52bbSMario Limonciello #define ZEN_CCD_TEMP(offset, x)			(ZEN_REPORTED_TEMP_CTRL_BASE + \
720e3f52bbSMario 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)
780c072385SBabu Moger #define ZEN_CUR_TEMP_TJ_SEL_MASK		GENMASK(17, 16)
79b00647c4SGuenter Roeck 
80e146503aSBaskaran Kannan /*
81e146503aSBaskaran Kannan  * AMD's Industrial processor 3255 supports temperature from -40 deg to 105 deg Celsius.
82e146503aSBaskaran Kannan  * Use the model name to identify 3255 CPUs and set a flag to display negative temperature.
83e146503aSBaskaran Kannan  * Do not round off to zero for negative Tctl or Tdie values if the flag is set
84e146503aSBaskaran Kannan  */
85e146503aSBaskaran Kannan #define AMD_I3255_STR				"3255"
86e146503aSBaskaran Kannan 
8768546abfSGuenter Roeck struct k10temp_data {
8868546abfSGuenter Roeck 	struct pci_dev *pdev;
8940626a1bSGuenter Roeck 	void (*read_htcreg)(struct pci_dev *pdev, u32 *regval);
9068546abfSGuenter Roeck 	void (*read_tempreg)(struct pci_dev *pdev, u32 *regval);
911b50b776SGuenter Roeck 	int temp_offset;
921b597889SGuenter Roeck 	u32 temp_adjust_mask;
9360465245SGuenter Roeck 	u32 show_temp;
9460465245SGuenter Roeck 	bool is_zen;
950e3f52bbSMario Limonciello 	u32 ccd_offset;
96e146503aSBaskaran Kannan 	bool disp_negative;
971b50b776SGuenter Roeck };
981b50b776SGuenter Roeck 
9960465245SGuenter Roeck #define TCTL_BIT	0
10060465245SGuenter Roeck #define TDIE_BIT	1
10160465245SGuenter Roeck #define TCCD_BIT(x)	((x) + 2)
10260465245SGuenter Roeck 
10360465245SGuenter Roeck #define HAVE_TEMP(d, channel)	((d)->show_temp & BIT(channel))
10460465245SGuenter Roeck #define HAVE_TDIE(d)		HAVE_TEMP(d, TDIE_BIT)
10560465245SGuenter Roeck 
1061b50b776SGuenter Roeck struct tctl_offset {
1071b50b776SGuenter Roeck 	u8 model;
1081b50b776SGuenter Roeck 	char const *id;
1091b50b776SGuenter Roeck 	int offset;
1101b50b776SGuenter Roeck };
1111b50b776SGuenter Roeck 
1121b50b776SGuenter Roeck static const struct tctl_offset tctl_offset_table[] = {
113ab5ee246SGuenter Roeck 	{ 0x17, "AMD Ryzen 5 1600X", 20000 },
1141b50b776SGuenter Roeck 	{ 0x17, "AMD Ryzen 7 1700X", 20000 },
1151b50b776SGuenter Roeck 	{ 0x17, "AMD Ryzen 7 1800X", 20000 },
1161b597889SGuenter Roeck 	{ 0x17, "AMD Ryzen 7 2700X", 10000 },
117cd6a2064SGuenter Roeck 	{ 0x17, "AMD Ryzen Threadripper 19", 27000 }, /* 19{00,20,50}X */
118cd6a2064SGuenter Roeck 	{ 0x17, "AMD Ryzen Threadripper 29", 27000 }, /* 29{20,50,70,90}[W]X */
11968546abfSGuenter Roeck };
12068546abfSGuenter Roeck 
read_htcreg_pci(struct pci_dev * pdev,u32 * regval)12140626a1bSGuenter Roeck static void read_htcreg_pci(struct pci_dev *pdev, u32 *regval)
12240626a1bSGuenter Roeck {
12340626a1bSGuenter Roeck 	pci_read_config_dword(pdev, REG_HARDWARE_THERMAL_CONTROL, regval);
12440626a1bSGuenter Roeck }
12540626a1bSGuenter Roeck 
read_tempreg_pci(struct pci_dev * pdev,u32 * regval)12668546abfSGuenter Roeck static void read_tempreg_pci(struct pci_dev *pdev, u32 *regval)
12768546abfSGuenter Roeck {
12868546abfSGuenter Roeck 	pci_read_config_dword(pdev, REG_REPORTED_TEMPERATURE, regval);
12968546abfSGuenter Roeck }
13068546abfSGuenter Roeck 
amd_nb_index_read(struct pci_dev * pdev,unsigned int devfn,unsigned int base,int offset,u32 * val)13168546abfSGuenter Roeck static void amd_nb_index_read(struct pci_dev *pdev, unsigned int devfn,
13268546abfSGuenter Roeck 			      unsigned int base, int offset, u32 *val)
133f89ce270SAravind Gopalakrishnan {
134f89ce270SAravind Gopalakrishnan 	mutex_lock(&nb_smu_ind_mutex);
135f89ce270SAravind Gopalakrishnan 	pci_bus_write_config_dword(pdev->bus, devfn,
13668546abfSGuenter Roeck 				   base, offset);
137f89ce270SAravind Gopalakrishnan 	pci_bus_read_config_dword(pdev->bus, devfn,
13868546abfSGuenter Roeck 				  base + 4, val);
139f89ce270SAravind Gopalakrishnan 	mutex_unlock(&nb_smu_ind_mutex);
140f89ce270SAravind Gopalakrishnan }
141f89ce270SAravind Gopalakrishnan 
read_htcreg_nb_f15(struct pci_dev * pdev,u32 * regval)14240626a1bSGuenter Roeck static void read_htcreg_nb_f15(struct pci_dev *pdev, u32 *regval)
14340626a1bSGuenter Roeck {
14440626a1bSGuenter Roeck 	amd_nb_index_read(pdev, PCI_DEVFN(0, 0), 0xb8,
14540626a1bSGuenter Roeck 			  F15H_M60H_HARDWARE_TEMP_CTRL_OFFSET, regval);
14640626a1bSGuenter Roeck }
14740626a1bSGuenter Roeck 
read_tempreg_nb_f15(struct pci_dev * pdev,u32 * regval)14868546abfSGuenter Roeck static void read_tempreg_nb_f15(struct pci_dev *pdev, u32 *regval)
14968546abfSGuenter Roeck {
15068546abfSGuenter Roeck 	amd_nb_index_read(pdev, PCI_DEVFN(0, 0), 0xb8,
15168546abfSGuenter Roeck 			  F15H_M60H_REPORTED_TEMP_CTRL_OFFSET, regval);
15268546abfSGuenter Roeck }
15368546abfSGuenter Roeck 
read_tempreg_nb_zen(struct pci_dev * pdev,u32 * regval)15417822417SWei Huang static void read_tempreg_nb_zen(struct pci_dev *pdev, u32 *regval)
1559af0a9aeSGuenter Roeck {
1563b031622SGuenter Roeck 	amd_smn_read(amd_pci_dev_to_node_id(pdev),
1570e3f52bbSMario Limonciello 		     ZEN_REPORTED_TEMP_CTRL_BASE, regval);
1589af0a9aeSGuenter Roeck }
1599af0a9aeSGuenter Roeck 
get_raw_temp(struct k10temp_data * data)160d547552aSGuenter Roeck static long get_raw_temp(struct k10temp_data *data)
1613c57e89bSClemens Ladisch {
162f934c059SGuenter Roeck 	u32 regval;
163d547552aSGuenter Roeck 	long temp;
1643c57e89bSClemens Ladisch 
16568546abfSGuenter Roeck 	data->read_tempreg(data->pdev, &regval);
16617822417SWei Huang 	temp = (regval >> ZEN_CUR_TEMP_SHIFT) * 125;
1670c072385SBabu Moger 	if ((regval & data->temp_adjust_mask) ||
1680c072385SBabu Moger 	    (regval & ZEN_CUR_TEMP_TJ_SEL_MASK) == ZEN_CUR_TEMP_TJ_SEL_MASK)
1691b597889SGuenter Roeck 		temp -= 49000;
170f934c059SGuenter Roeck 	return temp;
171f934c059SGuenter Roeck }
172f934c059SGuenter Roeck 
1730e786f32SJason Yan static const char *k10temp_temp_label[] = {
174d547552aSGuenter Roeck 	"Tctl",
175b02c6857SGuenter Roeck 	"Tdie",
176c7579389SGuenter Roeck 	"Tccd1",
177c7579389SGuenter Roeck 	"Tccd2",
178fd8bdb23SGuenter Roeck 	"Tccd3",
179fd8bdb23SGuenter Roeck 	"Tccd4",
180fd8bdb23SGuenter Roeck 	"Tccd5",
181fd8bdb23SGuenter Roeck 	"Tccd6",
182fd8bdb23SGuenter Roeck 	"Tccd7",
183fd8bdb23SGuenter Roeck 	"Tccd8",
1848bb050cdSBabu Moger 	"Tccd9",
1858bb050cdSBabu Moger 	"Tccd10",
1868bb050cdSBabu Moger 	"Tccd11",
1878bb050cdSBabu Moger 	"Tccd12",
188d547552aSGuenter Roeck };
189d547552aSGuenter Roeck 
k10temp_read_labels(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,const char ** str)190d547552aSGuenter Roeck static int k10temp_read_labels(struct device *dev,
191d547552aSGuenter Roeck 			       enum hwmon_sensor_types type,
192d547552aSGuenter Roeck 			       u32 attr, int channel, const char **str)
193d547552aSGuenter Roeck {
194b00647c4SGuenter Roeck 	switch (type) {
195b00647c4SGuenter Roeck 	case hwmon_temp:
196d547552aSGuenter Roeck 		*str = k10temp_temp_label[channel];
197b00647c4SGuenter Roeck 		break;
198b00647c4SGuenter Roeck 	default:
199b00647c4SGuenter Roeck 		return -EOPNOTSUPP;
200b00647c4SGuenter Roeck 	}
201b00647c4SGuenter Roeck 	return 0;
202b00647c4SGuenter Roeck }
203b00647c4SGuenter Roeck 
k10temp_read_temp(struct device * dev,u32 attr,int channel,long * val)204b00647c4SGuenter Roeck static int k10temp_read_temp(struct device *dev, u32 attr, int channel,
205b00647c4SGuenter Roeck 			     long *val)
206f934c059SGuenter Roeck {
207f934c059SGuenter Roeck 	struct k10temp_data *data = dev_get_drvdata(dev);
2083c57e89bSClemens Ladisch 	u32 regval;
2093c57e89bSClemens Ladisch 
210d547552aSGuenter Roeck 	switch (attr) {
211d547552aSGuenter Roeck 	case hwmon_temp_input:
212d547552aSGuenter Roeck 		switch (channel) {
213b02c6857SGuenter Roeck 		case 0:		/* Tctl */
214b02c6857SGuenter Roeck 			*val = get_raw_temp(data);
215e146503aSBaskaran Kannan 			if (*val < 0 && !data->disp_negative)
216d547552aSGuenter Roeck 				*val = 0;
217d547552aSGuenter Roeck 			break;
218b02c6857SGuenter Roeck 		case 1:		/* Tdie */
219b02c6857SGuenter Roeck 			*val = get_raw_temp(data) - data->temp_offset;
220e146503aSBaskaran Kannan 			if (*val < 0 && !data->disp_negative)
221d547552aSGuenter Roeck 				*val = 0;
222d547552aSGuenter Roeck 			break;
2238bb050cdSBabu Moger 		case 2 ... 13:		/* Tccd{1-12} */
224c7579389SGuenter Roeck 			amd_smn_read(amd_pci_dev_to_node_id(data->pdev),
2250e3f52bbSMario Limonciello 				     ZEN_CCD_TEMP(data->ccd_offset, channel - 2),
2260e3f52bbSMario Limonciello 						  &regval);
22717822417SWei Huang 			*val = (regval & ZEN_CCD_TEMP_MASK) * 125 - 49000;
228c7579389SGuenter Roeck 			break;
229d547552aSGuenter Roeck 		default:
230d547552aSGuenter Roeck 			return -EOPNOTSUPP;
231d547552aSGuenter Roeck 		}
232d547552aSGuenter Roeck 		break;
233d547552aSGuenter Roeck 	case hwmon_temp_max:
234d547552aSGuenter Roeck 		*val = 70 * 1000;
235d547552aSGuenter Roeck 		break;
236d547552aSGuenter Roeck 	case hwmon_temp_crit:
23740626a1bSGuenter Roeck 		data->read_htcreg(data->pdev, &regval);
238d547552aSGuenter Roeck 		*val = ((regval >> 16) & 0x7f) * 500 + 52000;
239d547552aSGuenter Roeck 		break;
240d547552aSGuenter Roeck 	case hwmon_temp_crit_hyst:
241d547552aSGuenter Roeck 		data->read_htcreg(data->pdev, &regval);
242d547552aSGuenter Roeck 		*val = (((regval >> 16) & 0x7f)
243d547552aSGuenter Roeck 			- ((regval >> 24) & 0xf)) * 500 + 52000;
244d547552aSGuenter Roeck 		break;
245d547552aSGuenter Roeck 	default:
246d547552aSGuenter Roeck 		return -EOPNOTSUPP;
247d547552aSGuenter Roeck 	}
248d547552aSGuenter Roeck 	return 0;
2493c57e89bSClemens Ladisch }
2503c57e89bSClemens Ladisch 
k10temp_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)251b00647c4SGuenter Roeck static int k10temp_read(struct device *dev, enum hwmon_sensor_types type,
252b00647c4SGuenter Roeck 			u32 attr, int channel, long *val)
253b00647c4SGuenter Roeck {
254b00647c4SGuenter Roeck 	switch (type) {
255b00647c4SGuenter Roeck 	case hwmon_temp:
256b00647c4SGuenter Roeck 		return k10temp_read_temp(dev, attr, channel, val);
257b00647c4SGuenter Roeck 	default:
258b00647c4SGuenter Roeck 		return -EOPNOTSUPP;
259b00647c4SGuenter Roeck 	}
260b00647c4SGuenter Roeck }
261b00647c4SGuenter Roeck 
k10temp_is_visible(const void * _data,enum hwmon_sensor_types type,u32 attr,int channel)262d547552aSGuenter Roeck static umode_t k10temp_is_visible(const void *_data,
263d547552aSGuenter Roeck 				  enum hwmon_sensor_types type,
264d547552aSGuenter Roeck 				  u32 attr, int channel)
2653e3e1022SGuenter Roeck {
266d547552aSGuenter Roeck 	const struct k10temp_data *data = _data;
26768546abfSGuenter Roeck 	struct pci_dev *pdev = data->pdev;
26840626a1bSGuenter Roeck 	u32 reg;
26940626a1bSGuenter Roeck 
270d547552aSGuenter Roeck 	switch (type) {
271d547552aSGuenter Roeck 	case hwmon_temp:
272d547552aSGuenter Roeck 		switch (attr) {
273d547552aSGuenter Roeck 		case hwmon_temp_input:
27460465245SGuenter Roeck 			if (!HAVE_TEMP(data, channel))
275d547552aSGuenter Roeck 				return 0;
276f934c059SGuenter Roeck 			break;
277d547552aSGuenter Roeck 		case hwmon_temp_max:
27860465245SGuenter Roeck 			if (channel || data->is_zen)
279d547552aSGuenter Roeck 				return 0;
280d547552aSGuenter Roeck 			break;
281d547552aSGuenter Roeck 		case hwmon_temp_crit:
282d547552aSGuenter Roeck 		case hwmon_temp_crit_hyst:
283d547552aSGuenter Roeck 			if (channel || !data->read_htcreg)
28440626a1bSGuenter Roeck 				return 0;
2853e3e1022SGuenter Roeck 
286d547552aSGuenter Roeck 			pci_read_config_dword(pdev,
287d547552aSGuenter Roeck 					      REG_NORTHBRIDGE_CAPABILITIES,
28840626a1bSGuenter Roeck 					      &reg);
28940626a1bSGuenter Roeck 			if (!(reg & NB_CAP_HTC))
29040626a1bSGuenter Roeck 				return 0;
29140626a1bSGuenter Roeck 
29240626a1bSGuenter Roeck 			data->read_htcreg(data->pdev, &reg);
29340626a1bSGuenter Roeck 			if (!(reg & HTC_ENABLE))
2943e3e1022SGuenter Roeck 				return 0;
295f934c059SGuenter Roeck 			break;
296d547552aSGuenter Roeck 		case hwmon_temp_label:
29760465245SGuenter Roeck 			/* Show temperature labels only on Zen CPUs */
29860465245SGuenter Roeck 			if (!data->is_zen || !HAVE_TEMP(data, channel))
299f934c059SGuenter Roeck 				return 0;
300f934c059SGuenter Roeck 			break;
301d547552aSGuenter Roeck 		default:
302d547552aSGuenter Roeck 			return 0;
3033e3e1022SGuenter Roeck 		}
304d547552aSGuenter Roeck 		break;
305d547552aSGuenter Roeck 	default:
306d547552aSGuenter Roeck 		return 0;
3073e3e1022SGuenter Roeck 	}
308d547552aSGuenter Roeck 	return 0444;
309d547552aSGuenter Roeck }
3103c57e89bSClemens Ladisch 
has_erratum_319(struct pci_dev * pdev)3116c931ae1SBill Pemberton static bool has_erratum_319(struct pci_dev *pdev)
3123c57e89bSClemens Ladisch {
313c5114a1cSClemens Ladisch 	u32 pkg_type, reg_dram_cfg;
314c5114a1cSClemens Ladisch 
315c5114a1cSClemens Ladisch 	if (boot_cpu_data.x86 != 0x10)
316c5114a1cSClemens Ladisch 		return false;
317c5114a1cSClemens Ladisch 
3183c57e89bSClemens Ladisch 	/*
319c5114a1cSClemens Ladisch 	 * Erratum 319: The thermal sensor of Socket F/AM2+ processors
320c5114a1cSClemens Ladisch 	 *              may be unreliable.
3213c57e89bSClemens Ladisch 	 */
322c5114a1cSClemens Ladisch 	pkg_type = cpuid_ebx(0x80000001) & CPUID_PKGTYPE_MASK;
323c5114a1cSClemens Ladisch 	if (pkg_type == CPUID_PKGTYPE_F)
324c5114a1cSClemens Ladisch 		return true;
325c5114a1cSClemens Ladisch 	if (pkg_type != CPUID_PKGTYPE_AM2R2_AM3)
326c5114a1cSClemens Ladisch 		return false;
327c5114a1cSClemens Ladisch 
328eefc2d9eSJean Delvare 	/* DDR3 memory implies socket AM3, which is good */
329c5114a1cSClemens Ladisch 	pci_bus_read_config_dword(pdev->bus,
330c5114a1cSClemens Ladisch 				  PCI_DEVFN(PCI_SLOT(pdev->devfn), 2),
331c5114a1cSClemens Ladisch 				  REG_DCT0_CONFIG_HIGH, &reg_dram_cfg);
332eefc2d9eSJean Delvare 	if (reg_dram_cfg & DDR3_MODE)
333eefc2d9eSJean Delvare 		return false;
334eefc2d9eSJean Delvare 
335eefc2d9eSJean Delvare 	/*
336eefc2d9eSJean Delvare 	 * Unfortunately it is possible to run a socket AM3 CPU with DDR2
337eefc2d9eSJean Delvare 	 * memory. We blacklist all the cores which do exist in socket AM2+
338eefc2d9eSJean Delvare 	 * format. It still isn't perfect, as RB-C2 cores exist in both AM2+
339eefc2d9eSJean Delvare 	 * and AM3 formats, but that's the best we can do.
340eefc2d9eSJean Delvare 	 */
341eefc2d9eSJean Delvare 	return boot_cpu_data.x86_model < 4 ||
342b399151cSJia Zhang 	       (boot_cpu_data.x86_model == 4 && boot_cpu_data.x86_stepping <= 2);
3433c57e89bSClemens Ladisch }
3443c57e89bSClemens Ladisch 
3454dd50f3cSKrzysztof Kozlowski static const struct hwmon_channel_info * const k10temp_info[] = {
346d547552aSGuenter Roeck 	HWMON_CHANNEL_INFO(temp,
347d547552aSGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_MAX |
348d547552aSGuenter Roeck 			   HWMON_T_CRIT | HWMON_T_CRIT_HYST |
349d547552aSGuenter Roeck 			   HWMON_T_LABEL,
350c7579389SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
351c7579389SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
352fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
353fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
354fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
355fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
356fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
357fd8bdb23SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL,
3588bb050cdSBabu Moger 			   HWMON_T_INPUT | HWMON_T_LABEL,
3598bb050cdSBabu Moger 			   HWMON_T_INPUT | HWMON_T_LABEL,
3608bb050cdSBabu Moger 			   HWMON_T_INPUT | HWMON_T_LABEL,
3618bb050cdSBabu Moger 			   HWMON_T_INPUT | HWMON_T_LABEL,
362d547552aSGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_LABEL),
363d547552aSGuenter Roeck 	NULL
364d547552aSGuenter Roeck };
365d547552aSGuenter Roeck 
366d547552aSGuenter Roeck static const struct hwmon_ops k10temp_hwmon_ops = {
367d547552aSGuenter Roeck 	.is_visible = k10temp_is_visible,
368d547552aSGuenter Roeck 	.read = k10temp_read,
369d547552aSGuenter Roeck 	.read_string = k10temp_read_labels,
370d547552aSGuenter Roeck };
371d547552aSGuenter Roeck 
372d547552aSGuenter Roeck static const struct hwmon_chip_info k10temp_chip_info = {
373d547552aSGuenter Roeck 	.ops = &k10temp_hwmon_ops,
374d547552aSGuenter Roeck 	.info = k10temp_info,
375d547552aSGuenter Roeck };
376d547552aSGuenter Roeck 
k10temp_get_ccd_support(struct pci_dev * pdev,struct k10temp_data * data,int limit)377fd8bdb23SGuenter Roeck static void k10temp_get_ccd_support(struct pci_dev *pdev,
378fd8bdb23SGuenter Roeck 				    struct k10temp_data *data, int limit)
379fd8bdb23SGuenter Roeck {
380fd8bdb23SGuenter Roeck 	u32 regval;
381fd8bdb23SGuenter Roeck 	int i;
382fd8bdb23SGuenter Roeck 
383fd8bdb23SGuenter Roeck 	for (i = 0; i < limit; i++) {
384fd8bdb23SGuenter Roeck 		amd_smn_read(amd_pci_dev_to_node_id(pdev),
3850e3f52bbSMario Limonciello 			     ZEN_CCD_TEMP(data->ccd_offset, i), &regval);
38617822417SWei Huang 		if (regval & ZEN_CCD_TEMP_VALID)
38760465245SGuenter Roeck 			data->show_temp |= BIT(TCCD_BIT(i));
388fd8bdb23SGuenter Roeck 	}
389fd8bdb23SGuenter Roeck }
390fd8bdb23SGuenter Roeck 
k10temp_probe(struct pci_dev * pdev,const struct pci_device_id * id)391d547552aSGuenter Roeck static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
3923c57e89bSClemens Ladisch {
393c5114a1cSClemens Ladisch 	int unreliable = has_erratum_319(pdev);
3943e3e1022SGuenter Roeck 	struct device *dev = &pdev->dev;
39568546abfSGuenter Roeck 	struct k10temp_data *data;
3963e3e1022SGuenter Roeck 	struct device *hwmon_dev;
3971b50b776SGuenter Roeck 	int i;
3983c57e89bSClemens Ladisch 
3993e3e1022SGuenter Roeck 	if (unreliable) {
4003e3e1022SGuenter Roeck 		if (!force) {
4013e3e1022SGuenter Roeck 			dev_err(dev,
4023c57e89bSClemens Ladisch 				"unreliable CPU thermal sensor; monitoring disabled\n");
4033e3e1022SGuenter Roeck 			return -ENODEV;
4043c57e89bSClemens Ladisch 		}
4053e3e1022SGuenter Roeck 		dev_warn(dev,
4063c57e89bSClemens Ladisch 			 "unreliable CPU thermal sensor; check erratum 319\n");
4073c57e89bSClemens Ladisch 	}
4083c57e89bSClemens Ladisch 
40968546abfSGuenter Roeck 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
41068546abfSGuenter Roeck 	if (!data)
41168546abfSGuenter Roeck 		return -ENOMEM;
41268546abfSGuenter Roeck 
41368546abfSGuenter Roeck 	data->pdev = pdev;
41460465245SGuenter Roeck 	data->show_temp |= BIT(TCTL_BIT);	/* Always show Tctl */
41568546abfSGuenter Roeck 
416e146503aSBaskaran Kannan 	if (boot_cpu_data.x86 == 0x17 &&
417e146503aSBaskaran Kannan 	    strstr(boot_cpu_data.x86_model_id, AMD_I3255_STR)) {
418e146503aSBaskaran Kannan 		data->disp_negative = true;
419e146503aSBaskaran Kannan 	}
420e146503aSBaskaran Kannan 
42153dfa008SGuenter Roeck 	if (boot_cpu_data.x86 == 0x15 &&
42253dfa008SGuenter Roeck 	    ((boot_cpu_data.x86_model & 0xf0) == 0x60 ||
42353dfa008SGuenter Roeck 	     (boot_cpu_data.x86_model & 0xf0) == 0x70)) {
42440626a1bSGuenter Roeck 		data->read_htcreg = read_htcreg_nb_f15;
42568546abfSGuenter Roeck 		data->read_tempreg = read_tempreg_nb_f15;
426d93217d8SPu Wen 	} else if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) {
42717822417SWei Huang 		data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK;
42817822417SWei Huang 		data->read_tempreg = read_tempreg_nb_zen;
42960465245SGuenter Roeck 		data->is_zen = true;
430c7579389SGuenter Roeck 
431c7579389SGuenter Roeck 		switch (boot_cpu_data.x86_model) {
432c7579389SGuenter Roeck 		case 0x1:	/* Zen */
433c7579389SGuenter Roeck 		case 0x8:	/* Zen+ */
434c7579389SGuenter Roeck 		case 0x11:	/* Zen APU */
435c7579389SGuenter Roeck 		case 0x18:	/* Zen+ APU */
4360e3f52bbSMario Limonciello 			data->ccd_offset = 0x154;
437fd8bdb23SGuenter Roeck 			k10temp_get_ccd_support(pdev, data, 4);
438c7579389SGuenter Roeck 			break;
439c7579389SGuenter Roeck 		case 0x31:	/* Zen2 Threadripper */
440128066c8SMario Limonciello 		case 0x60:	/* Renoir */
441128066c8SMario Limonciello 		case 0x68:	/* Lucienne */
442c7579389SGuenter Roeck 		case 0x71:	/* Zen2 */
4430e3f52bbSMario Limonciello 			data->ccd_offset = 0x154;
444fd8bdb23SGuenter Roeck 			k10temp_get_ccd_support(pdev, data, 8);
445c7579389SGuenter Roeck 			break;
446d906fa73SMario Limonciello 		case 0xa0 ... 0xaf:
447d906fa73SMario Limonciello 			data->ccd_offset = 0x300;
448d906fa73SMario Limonciello 			k10temp_get_ccd_support(pdev, data, 8);
449d906fa73SMario Limonciello 			break;
450c7579389SGuenter Roeck 		}
45155163a1cSWei Huang 	} else if (boot_cpu_data.x86 == 0x19) {
45255163a1cSWei Huang 		data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK;
45355163a1cSWei Huang 		data->read_tempreg = read_tempreg_nb_zen;
45455163a1cSWei Huang 		data->is_zen = true;
45555163a1cSWei Huang 
45655163a1cSWei Huang 		switch (boot_cpu_data.x86_model) {
457c8d0d3faSGabriel Craciunescu 		case 0x0 ... 0x1:	/* Zen3 SP3/TR */
458c8d0d3faSGabriel Craciunescu 		case 0x21:		/* Zen3 Ryzen Desktop */
459128066c8SMario Limonciello 		case 0x50 ... 0x5f:	/* Green Sardine */
4600e3f52bbSMario Limonciello 			data->ccd_offset = 0x154;
46155163a1cSWei Huang 			k10temp_get_ccd_support(pdev, data, 8);
46255163a1cSWei Huang 			break;
46325572c81SMario Limonciello 		case 0x40 ... 0x4f:	/* Yellow Carp */
46425572c81SMario Limonciello 			data->ccd_offset = 0x300;
46525572c81SMario Limonciello 			k10temp_get_ccd_support(pdev, data, 8);
46625572c81SMario Limonciello 			break;
467d906fa73SMario Limonciello 		case 0x60 ... 0x6f:
468d906fa73SMario Limonciello 		case 0x70 ... 0x7f:
469d906fa73SMario Limonciello 			data->ccd_offset = 0x308;
470d906fa73SMario Limonciello 			k10temp_get_ccd_support(pdev, data, 8);
471d906fa73SMario Limonciello 			break;
4728bb050cdSBabu Moger 		case 0x10 ... 0x1f:
4738bb050cdSBabu Moger 		case 0xa0 ... 0xaf:
4748bb050cdSBabu Moger 			data->ccd_offset = 0x300;
4758bb050cdSBabu Moger 			k10temp_get_ccd_support(pdev, data, 12);
4768bb050cdSBabu Moger 			break;
47755163a1cSWei Huang 		}
478*3cd9da41SAvadhut Naik 	} else if (boot_cpu_data.x86 == 0x1a) {
479*3cd9da41SAvadhut Naik 		data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK;
480*3cd9da41SAvadhut Naik 		data->read_tempreg = read_tempreg_nb_zen;
481*3cd9da41SAvadhut Naik 		data->is_zen = true;
4821b597889SGuenter Roeck 	} else {
48340626a1bSGuenter Roeck 		data->read_htcreg = read_htcreg_pci;
48468546abfSGuenter Roeck 		data->read_tempreg = read_tempreg_pci;
4851b597889SGuenter Roeck 	}
48668546abfSGuenter Roeck 
4871b50b776SGuenter Roeck 	for (i = 0; i < ARRAY_SIZE(tctl_offset_table); i++) {
4881b50b776SGuenter Roeck 		const struct tctl_offset *entry = &tctl_offset_table[i];
4891b50b776SGuenter Roeck 
4901b50b776SGuenter Roeck 		if (boot_cpu_data.x86 == entry->model &&
4911b50b776SGuenter Roeck 		    strstr(boot_cpu_data.x86_model_id, entry->id)) {
49202a2484cSMario Limonciello 			data->show_temp |= BIT(TDIE_BIT);	/* show Tdie */
4931b50b776SGuenter Roeck 			data->temp_offset = entry->offset;
4941b50b776SGuenter Roeck 			break;
4951b50b776SGuenter Roeck 		}
4961b50b776SGuenter Roeck 	}
4971b50b776SGuenter Roeck 
498d547552aSGuenter Roeck 	hwmon_dev = devm_hwmon_device_register_with_info(dev, "k10temp", data,
499d547552aSGuenter Roeck 							 &k10temp_chip_info,
500d547552aSGuenter Roeck 							 NULL);
5018999eabfSGuenter Roeck 	return PTR_ERR_OR_ZERO(hwmon_dev);
5023c57e89bSClemens Ladisch }
5033c57e89bSClemens Ladisch 
504cd9bb056SJingoo Han static const struct pci_device_id k10temp_id_table[] = {
5053c57e89bSClemens Ladisch 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) },
5063c57e89bSClemens Ladisch 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) },
507aa4790a6SClemens Ladisch 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) },
5089e581311SAndre Przywara 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) },
50924214449SBorislav Petkov 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) },
510d303b1b5SPhil Pokorny 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F3) },
511f89ce270SAravind Gopalakrishnan 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F3) },
512ccaf63b4SGuenter Roeck 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M70H_NB_F3) },
51330b146d1SWei Hu 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) },
514ec015950SAravind Gopalakrishnan 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) },
5159af0a9aeSGuenter Roeck 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_DF_F3) },
5163b031622SGuenter Roeck 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F3) },
517210ba120SWoods, Brian 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M30H_DF_F3) },
518279f0b3aSAlexander Monakov 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M60H_DF_F3) },
51912163cfbSMarcel Bocu 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F3) },
520d906fa73SMario Limonciello 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_MA0H_DF_F3) },
52155163a1cSWei Huang 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_DF_F3) },
5223cf90efaSBabu Moger 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M10H_DF_F3) },
52325572c81SMario Limonciello 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M40H_DF_F3) },
52402c9dce4SDavid Bartley 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M50H_DF_F3) },
525d906fa73SMario Limonciello 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M60H_DF_F3) },
526d906fa73SMario Limonciello 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M70H_DF_F3) },
5277d8accfaSMario Limonciello 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M78H_DF_F3) },
528*3cd9da41SAvadhut Naik 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_1AH_M00H_DF_F3) },
529*3cd9da41SAvadhut Naik 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_1AH_M20H_DF_F3) },
530d93217d8SPu Wen 	{ PCI_VDEVICE(HYGON, PCI_DEVICE_ID_AMD_17H_DF_F3) },
5313c57e89bSClemens Ladisch 	{}
5323c57e89bSClemens Ladisch };
5333c57e89bSClemens Ladisch MODULE_DEVICE_TABLE(pci, k10temp_id_table);
5343c57e89bSClemens Ladisch 
5353c57e89bSClemens Ladisch static struct pci_driver k10temp_driver = {
5363c57e89bSClemens Ladisch 	.name = "k10temp",
5373c57e89bSClemens Ladisch 	.id_table = k10temp_id_table,
5383c57e89bSClemens Ladisch 	.probe = k10temp_probe,
5393c57e89bSClemens Ladisch };
5403c57e89bSClemens Ladisch 
541f71f5a55SAxel Lin module_pci_driver(k10temp_driver);
542