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, ®val); 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], ®val); 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], ®val); 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), ®val); 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, ®val); 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, ®val); 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 ®); 37040626a1bSGuenter Roeck if (!(reg & NB_CAP_HTC)) 37140626a1bSGuenter Roeck return 0; 37240626a1bSGuenter Roeck 37340626a1bSGuenter Roeck data->read_htcreg(data->pdev, ®); 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, ®_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), ®val); 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