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, ®val); 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], ®val); 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], ®val); 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), ®val); 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, ®val); 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, ®val); 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 ®); 36440626a1bSGuenter Roeck if (!(reg & NB_CAP_HTC)) 36540626a1bSGuenter Roeck return 0; 36640626a1bSGuenter Roeck 36740626a1bSGuenter Roeck data->read_htcreg(data->pdev, ®); 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, ®_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, ®); 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), ®val); 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