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 6817822417SWei Huang /* Common for Zen CPU families (Family 17h and 18h) */ 6917822417SWei Huang #define ZEN_REPORTED_TEMP_CTRL_OFFSET 0x00059800 70fd8bdb23SGuenter Roeck 7117822417SWei Huang #define ZEN_CCD_TEMP(x) (0x00059954 + ((x) * 4)) 7217822417SWei Huang #define ZEN_CCD_TEMP_VALID BIT(11) 7317822417SWei Huang #define ZEN_CCD_TEMP_MASK GENMASK(10, 0) 749af0a9aeSGuenter Roeck 7517822417SWei Huang #define ZEN_CUR_TEMP_SHIFT 21 7617822417SWei Huang #define ZEN_CUR_TEMP_RANGE_SEL_MASK BIT(19) 77b00647c4SGuenter Roeck 7817822417SWei Huang #define ZEN_SVI_BASE 0x0005A000 79a6d210daSGuenter Roeck 8017822417SWei Huang /* F17h thermal registers through SMN */ 8117822417SWei Huang #define F17H_M01H_SVI_TEL_PLANE0 (ZEN_SVI_BASE + 0xc) 8217822417SWei Huang #define F17H_M01H_SVI_TEL_PLANE1 (ZEN_SVI_BASE + 0x10) 83d6144a40SWei Huang #define F17H_M31H_SVI_TEL_PLANE0 (ZEN_SVI_BASE + 0x14) 84d6144a40SWei Huang #define F17H_M31H_SVI_TEL_PLANE1 (ZEN_SVI_BASE + 0x10) 8517822417SWei Huang 86d6144a40SWei Huang #define F17H_M01H_CFACTOR_ICORE 1000000 /* 1A / LSB */ 87d6144a40SWei Huang #define F17H_M01H_CFACTOR_ISOC 250000 /* 0.25A / LSB */ 88d6144a40SWei Huang #define F17H_M31H_CFACTOR_ICORE 1000000 /* 1A / LSB */ 89d6144a40SWei Huang #define F17H_M31H_CFACTOR_ISOC 310000 /* 0.31A / LSB */ 90b00647c4SGuenter Roeck 9155163a1cSWei Huang /* F19h thermal registers through SMN */ 9255163a1cSWei Huang #define F19H_M01_SVI_TEL_PLANE0 (ZEN_SVI_BASE + 0x14) 9355163a1cSWei Huang #define F19H_M01_SVI_TEL_PLANE1 (ZEN_SVI_BASE + 0x10) 9455163a1cSWei Huang 9555163a1cSWei Huang #define F19H_M01H_CFACTOR_ICORE 1000000 /* 1A / LSB */ 9655163a1cSWei Huang #define F19H_M01H_CFACTOR_ISOC 310000 /* 0.31A / LSB */ 9755163a1cSWei Huang 9868546abfSGuenter Roeck struct k10temp_data { 9968546abfSGuenter Roeck struct pci_dev *pdev; 10040626a1bSGuenter Roeck void (*read_htcreg)(struct pci_dev *pdev, u32 *regval); 10168546abfSGuenter Roeck void (*read_tempreg)(struct pci_dev *pdev, u32 *regval); 1021b50b776SGuenter Roeck int temp_offset; 1031b597889SGuenter Roeck u32 temp_adjust_mask; 10460465245SGuenter Roeck u32 show_temp; 10560465245SGuenter Roeck bool is_zen; 1061b50b776SGuenter Roeck }; 1071b50b776SGuenter Roeck 10860465245SGuenter Roeck #define TCTL_BIT 0 10960465245SGuenter Roeck #define TDIE_BIT 1 11060465245SGuenter Roeck #define TCCD_BIT(x) ((x) + 2) 11160465245SGuenter Roeck 11260465245SGuenter Roeck #define HAVE_TEMP(d, channel) ((d)->show_temp & BIT(channel)) 11360465245SGuenter Roeck #define HAVE_TDIE(d) HAVE_TEMP(d, TDIE_BIT) 11460465245SGuenter Roeck 1151b50b776SGuenter Roeck struct tctl_offset { 1161b50b776SGuenter Roeck u8 model; 1171b50b776SGuenter Roeck char const *id; 1181b50b776SGuenter Roeck int offset; 1191b50b776SGuenter Roeck }; 1201b50b776SGuenter Roeck 1211b50b776SGuenter Roeck static const struct tctl_offset tctl_offset_table[] = { 122ab5ee246SGuenter Roeck { 0x17, "AMD Ryzen 5 1600X", 20000 }, 1231b50b776SGuenter Roeck { 0x17, "AMD Ryzen 7 1700X", 20000 }, 1241b50b776SGuenter Roeck { 0x17, "AMD Ryzen 7 1800X", 20000 }, 1251b597889SGuenter Roeck { 0x17, "AMD Ryzen 7 2700X", 10000 }, 126cd6a2064SGuenter Roeck { 0x17, "AMD Ryzen Threadripper 19", 27000 }, /* 19{00,20,50}X */ 127cd6a2064SGuenter Roeck { 0x17, "AMD Ryzen Threadripper 29", 27000 }, /* 29{20,50,70,90}[W]X */ 12868546abfSGuenter Roeck }; 12968546abfSGuenter Roeck 13040626a1bSGuenter Roeck static void read_htcreg_pci(struct pci_dev *pdev, u32 *regval) 13140626a1bSGuenter Roeck { 13240626a1bSGuenter Roeck pci_read_config_dword(pdev, REG_HARDWARE_THERMAL_CONTROL, regval); 13340626a1bSGuenter Roeck } 13440626a1bSGuenter Roeck 13568546abfSGuenter Roeck static void read_tempreg_pci(struct pci_dev *pdev, u32 *regval) 13668546abfSGuenter Roeck { 13768546abfSGuenter Roeck pci_read_config_dword(pdev, REG_REPORTED_TEMPERATURE, regval); 13868546abfSGuenter Roeck } 13968546abfSGuenter Roeck 14068546abfSGuenter Roeck static void amd_nb_index_read(struct pci_dev *pdev, unsigned int devfn, 14168546abfSGuenter Roeck unsigned int base, int offset, u32 *val) 142f89ce270SAravind Gopalakrishnan { 143f89ce270SAravind Gopalakrishnan mutex_lock(&nb_smu_ind_mutex); 144f89ce270SAravind Gopalakrishnan pci_bus_write_config_dword(pdev->bus, devfn, 14568546abfSGuenter Roeck base, offset); 146f89ce270SAravind Gopalakrishnan pci_bus_read_config_dword(pdev->bus, devfn, 14768546abfSGuenter Roeck base + 4, val); 148f89ce270SAravind Gopalakrishnan mutex_unlock(&nb_smu_ind_mutex); 149f89ce270SAravind Gopalakrishnan } 150f89ce270SAravind Gopalakrishnan 15140626a1bSGuenter Roeck static void read_htcreg_nb_f15(struct pci_dev *pdev, u32 *regval) 15240626a1bSGuenter Roeck { 15340626a1bSGuenter Roeck amd_nb_index_read(pdev, PCI_DEVFN(0, 0), 0xb8, 15440626a1bSGuenter Roeck F15H_M60H_HARDWARE_TEMP_CTRL_OFFSET, regval); 15540626a1bSGuenter Roeck } 15640626a1bSGuenter Roeck 15768546abfSGuenter Roeck static void read_tempreg_nb_f15(struct pci_dev *pdev, u32 *regval) 15868546abfSGuenter Roeck { 15968546abfSGuenter Roeck amd_nb_index_read(pdev, PCI_DEVFN(0, 0), 0xb8, 16068546abfSGuenter Roeck F15H_M60H_REPORTED_TEMP_CTRL_OFFSET, regval); 16168546abfSGuenter Roeck } 16268546abfSGuenter Roeck 16317822417SWei Huang static void read_tempreg_nb_zen(struct pci_dev *pdev, u32 *regval) 1649af0a9aeSGuenter Roeck { 1653b031622SGuenter Roeck amd_smn_read(amd_pci_dev_to_node_id(pdev), 16617822417SWei Huang ZEN_REPORTED_TEMP_CTRL_OFFSET, regval); 1679af0a9aeSGuenter Roeck } 1689af0a9aeSGuenter Roeck 169d547552aSGuenter Roeck static long get_raw_temp(struct k10temp_data *data) 1703c57e89bSClemens Ladisch { 171f934c059SGuenter Roeck u32 regval; 172d547552aSGuenter Roeck long temp; 1733c57e89bSClemens Ladisch 17468546abfSGuenter Roeck data->read_tempreg(data->pdev, ®val); 17517822417SWei Huang temp = (regval >> ZEN_CUR_TEMP_SHIFT) * 125; 1761b597889SGuenter Roeck if (regval & data->temp_adjust_mask) 1771b597889SGuenter Roeck temp -= 49000; 178f934c059SGuenter Roeck return temp; 179f934c059SGuenter Roeck } 180f934c059SGuenter Roeck 1810e786f32SJason Yan static const char *k10temp_temp_label[] = { 182d547552aSGuenter Roeck "Tctl", 183b02c6857SGuenter Roeck "Tdie", 184c7579389SGuenter Roeck "Tccd1", 185c7579389SGuenter Roeck "Tccd2", 186fd8bdb23SGuenter Roeck "Tccd3", 187fd8bdb23SGuenter Roeck "Tccd4", 188fd8bdb23SGuenter Roeck "Tccd5", 189fd8bdb23SGuenter Roeck "Tccd6", 190fd8bdb23SGuenter Roeck "Tccd7", 191fd8bdb23SGuenter Roeck "Tccd8", 192d547552aSGuenter Roeck }; 193d547552aSGuenter Roeck 194d547552aSGuenter Roeck static int k10temp_read_labels(struct device *dev, 195d547552aSGuenter Roeck enum hwmon_sensor_types type, 196d547552aSGuenter Roeck u32 attr, int channel, const char **str) 197d547552aSGuenter Roeck { 198b00647c4SGuenter Roeck switch (type) { 199b00647c4SGuenter Roeck case hwmon_temp: 200d547552aSGuenter Roeck *str = k10temp_temp_label[channel]; 201b00647c4SGuenter Roeck break; 202b00647c4SGuenter Roeck default: 203b00647c4SGuenter Roeck return -EOPNOTSUPP; 204b00647c4SGuenter Roeck } 205b00647c4SGuenter Roeck return 0; 206b00647c4SGuenter Roeck } 207b00647c4SGuenter Roeck 208b00647c4SGuenter Roeck static int k10temp_read_temp(struct device *dev, u32 attr, int channel, 209b00647c4SGuenter Roeck long *val) 210f934c059SGuenter Roeck { 211f934c059SGuenter Roeck struct k10temp_data *data = dev_get_drvdata(dev); 2123c57e89bSClemens Ladisch u32 regval; 2133c57e89bSClemens Ladisch 214d547552aSGuenter Roeck switch (attr) { 215d547552aSGuenter Roeck case hwmon_temp_input: 216d547552aSGuenter Roeck switch (channel) { 217b02c6857SGuenter Roeck case 0: /* Tctl */ 218b02c6857SGuenter Roeck *val = get_raw_temp(data); 219d547552aSGuenter Roeck if (*val < 0) 220d547552aSGuenter Roeck *val = 0; 221d547552aSGuenter Roeck break; 222b02c6857SGuenter Roeck case 1: /* Tdie */ 223b02c6857SGuenter Roeck *val = get_raw_temp(data) - data->temp_offset; 224d547552aSGuenter Roeck if (*val < 0) 225d547552aSGuenter Roeck *val = 0; 226d547552aSGuenter Roeck break; 227fd8bdb23SGuenter Roeck case 2 ... 9: /* Tccd{1-8} */ 228c7579389SGuenter Roeck amd_smn_read(amd_pci_dev_to_node_id(data->pdev), 22917822417SWei Huang ZEN_CCD_TEMP(channel - 2), ®val); 23017822417SWei Huang *val = (regval & ZEN_CCD_TEMP_MASK) * 125 - 49000; 231c7579389SGuenter Roeck break; 232d547552aSGuenter Roeck default: 233d547552aSGuenter Roeck return -EOPNOTSUPP; 234d547552aSGuenter Roeck } 235d547552aSGuenter Roeck break; 236d547552aSGuenter Roeck case hwmon_temp_max: 237d547552aSGuenter Roeck *val = 70 * 1000; 238d547552aSGuenter Roeck break; 239d547552aSGuenter Roeck case hwmon_temp_crit: 24040626a1bSGuenter Roeck data->read_htcreg(data->pdev, ®val); 241d547552aSGuenter Roeck *val = ((regval >> 16) & 0x7f) * 500 + 52000; 242d547552aSGuenter Roeck break; 243d547552aSGuenter Roeck case hwmon_temp_crit_hyst: 244d547552aSGuenter Roeck data->read_htcreg(data->pdev, ®val); 245d547552aSGuenter Roeck *val = (((regval >> 16) & 0x7f) 246d547552aSGuenter Roeck - ((regval >> 24) & 0xf)) * 500 + 52000; 247d547552aSGuenter Roeck break; 248d547552aSGuenter Roeck default: 249d547552aSGuenter Roeck return -EOPNOTSUPP; 250d547552aSGuenter Roeck } 251d547552aSGuenter Roeck return 0; 2523c57e89bSClemens Ladisch } 2533c57e89bSClemens Ladisch 254b00647c4SGuenter Roeck static int k10temp_read(struct device *dev, enum hwmon_sensor_types type, 255b00647c4SGuenter Roeck u32 attr, int channel, long *val) 256b00647c4SGuenter Roeck { 257b00647c4SGuenter Roeck switch (type) { 258b00647c4SGuenter Roeck case hwmon_temp: 259b00647c4SGuenter Roeck return k10temp_read_temp(dev, attr, channel, val); 260b00647c4SGuenter Roeck default: 261b00647c4SGuenter Roeck return -EOPNOTSUPP; 262b00647c4SGuenter Roeck } 263b00647c4SGuenter Roeck } 264b00647c4SGuenter Roeck 265d547552aSGuenter Roeck static umode_t k10temp_is_visible(const void *_data, 266d547552aSGuenter Roeck enum hwmon_sensor_types type, 267d547552aSGuenter Roeck u32 attr, int channel) 2683e3e1022SGuenter Roeck { 269d547552aSGuenter Roeck const struct k10temp_data *data = _data; 27068546abfSGuenter Roeck struct pci_dev *pdev = data->pdev; 27140626a1bSGuenter Roeck u32 reg; 27240626a1bSGuenter Roeck 273d547552aSGuenter Roeck switch (type) { 274d547552aSGuenter Roeck case hwmon_temp: 275d547552aSGuenter Roeck switch (attr) { 276d547552aSGuenter Roeck case hwmon_temp_input: 27760465245SGuenter Roeck if (!HAVE_TEMP(data, channel)) 278d547552aSGuenter Roeck return 0; 279f934c059SGuenter Roeck break; 280d547552aSGuenter Roeck case hwmon_temp_max: 28160465245SGuenter Roeck if (channel || data->is_zen) 282d547552aSGuenter Roeck return 0; 283d547552aSGuenter Roeck break; 284d547552aSGuenter Roeck case hwmon_temp_crit: 285d547552aSGuenter Roeck case hwmon_temp_crit_hyst: 286d547552aSGuenter Roeck if (channel || !data->read_htcreg) 28740626a1bSGuenter Roeck return 0; 2883e3e1022SGuenter Roeck 289d547552aSGuenter Roeck pci_read_config_dword(pdev, 290d547552aSGuenter Roeck REG_NORTHBRIDGE_CAPABILITIES, 29140626a1bSGuenter Roeck ®); 29240626a1bSGuenter Roeck if (!(reg & NB_CAP_HTC)) 29340626a1bSGuenter Roeck return 0; 29440626a1bSGuenter Roeck 29540626a1bSGuenter Roeck data->read_htcreg(data->pdev, ®); 29640626a1bSGuenter Roeck if (!(reg & HTC_ENABLE)) 2973e3e1022SGuenter Roeck return 0; 298f934c059SGuenter Roeck break; 299d547552aSGuenter Roeck case hwmon_temp_label: 30060465245SGuenter Roeck /* Show temperature labels only on Zen CPUs */ 30160465245SGuenter Roeck if (!data->is_zen || !HAVE_TEMP(data, channel)) 302f934c059SGuenter Roeck return 0; 303f934c059SGuenter Roeck break; 304d547552aSGuenter Roeck default: 305d547552aSGuenter Roeck return 0; 3063e3e1022SGuenter Roeck } 307d547552aSGuenter Roeck break; 308d547552aSGuenter Roeck default: 309d547552aSGuenter Roeck return 0; 3103e3e1022SGuenter Roeck } 311d547552aSGuenter Roeck return 0444; 312d547552aSGuenter Roeck } 3133c57e89bSClemens Ladisch 3146c931ae1SBill Pemberton static bool has_erratum_319(struct pci_dev *pdev) 3153c57e89bSClemens Ladisch { 316c5114a1cSClemens Ladisch u32 pkg_type, reg_dram_cfg; 317c5114a1cSClemens Ladisch 318c5114a1cSClemens Ladisch if (boot_cpu_data.x86 != 0x10) 319c5114a1cSClemens Ladisch return false; 320c5114a1cSClemens Ladisch 3213c57e89bSClemens Ladisch /* 322c5114a1cSClemens Ladisch * Erratum 319: The thermal sensor of Socket F/AM2+ processors 323c5114a1cSClemens Ladisch * may be unreliable. 3243c57e89bSClemens Ladisch */ 325c5114a1cSClemens Ladisch pkg_type = cpuid_ebx(0x80000001) & CPUID_PKGTYPE_MASK; 326c5114a1cSClemens Ladisch if (pkg_type == CPUID_PKGTYPE_F) 327c5114a1cSClemens Ladisch return true; 328c5114a1cSClemens Ladisch if (pkg_type != CPUID_PKGTYPE_AM2R2_AM3) 329c5114a1cSClemens Ladisch return false; 330c5114a1cSClemens Ladisch 331eefc2d9eSJean Delvare /* DDR3 memory implies socket AM3, which is good */ 332c5114a1cSClemens Ladisch pci_bus_read_config_dword(pdev->bus, 333c5114a1cSClemens Ladisch PCI_DEVFN(PCI_SLOT(pdev->devfn), 2), 334c5114a1cSClemens Ladisch REG_DCT0_CONFIG_HIGH, ®_dram_cfg); 335eefc2d9eSJean Delvare if (reg_dram_cfg & DDR3_MODE) 336eefc2d9eSJean Delvare return false; 337eefc2d9eSJean Delvare 338eefc2d9eSJean Delvare /* 339eefc2d9eSJean Delvare * Unfortunately it is possible to run a socket AM3 CPU with DDR2 340eefc2d9eSJean Delvare * memory. We blacklist all the cores which do exist in socket AM2+ 341eefc2d9eSJean Delvare * format. It still isn't perfect, as RB-C2 cores exist in both AM2+ 342eefc2d9eSJean Delvare * and AM3 formats, but that's the best we can do. 343eefc2d9eSJean Delvare */ 344eefc2d9eSJean Delvare return boot_cpu_data.x86_model < 4 || 345b399151cSJia Zhang (boot_cpu_data.x86_model == 4 && boot_cpu_data.x86_stepping <= 2); 3463c57e89bSClemens Ladisch } 3473c57e89bSClemens Ladisch 348d547552aSGuenter Roeck static const struct hwmon_channel_info *k10temp_info[] = { 349d547552aSGuenter Roeck HWMON_CHANNEL_INFO(temp, 350d547552aSGuenter Roeck HWMON_T_INPUT | HWMON_T_MAX | 351d547552aSGuenter Roeck HWMON_T_CRIT | HWMON_T_CRIT_HYST | 352d547552aSGuenter Roeck HWMON_T_LABEL, 353c7579389SGuenter Roeck HWMON_T_INPUT | HWMON_T_LABEL, 354c7579389SGuenter 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, 358fd8bdb23SGuenter Roeck HWMON_T_INPUT | HWMON_T_LABEL, 359fd8bdb23SGuenter Roeck HWMON_T_INPUT | HWMON_T_LABEL, 360fd8bdb23SGuenter Roeck HWMON_T_INPUT | HWMON_T_LABEL, 361d547552aSGuenter Roeck HWMON_T_INPUT | HWMON_T_LABEL), 362b00647c4SGuenter Roeck HWMON_CHANNEL_INFO(in, 363b00647c4SGuenter Roeck HWMON_I_INPUT | HWMON_I_LABEL, 364b00647c4SGuenter Roeck HWMON_I_INPUT | HWMON_I_LABEL), 365b00647c4SGuenter Roeck HWMON_CHANNEL_INFO(curr, 366b00647c4SGuenter Roeck HWMON_C_INPUT | HWMON_C_LABEL, 367b00647c4SGuenter Roeck HWMON_C_INPUT | HWMON_C_LABEL), 368d547552aSGuenter Roeck NULL 369d547552aSGuenter Roeck }; 370d547552aSGuenter Roeck 371d547552aSGuenter Roeck static const struct hwmon_ops k10temp_hwmon_ops = { 372d547552aSGuenter Roeck .is_visible = k10temp_is_visible, 373d547552aSGuenter Roeck .read = k10temp_read, 374d547552aSGuenter Roeck .read_string = k10temp_read_labels, 375d547552aSGuenter Roeck }; 376d547552aSGuenter Roeck 377d547552aSGuenter Roeck static const struct hwmon_chip_info k10temp_chip_info = { 378d547552aSGuenter Roeck .ops = &k10temp_hwmon_ops, 379d547552aSGuenter Roeck .info = k10temp_info, 380d547552aSGuenter Roeck }; 381d547552aSGuenter Roeck 382fd8bdb23SGuenter Roeck static void k10temp_get_ccd_support(struct pci_dev *pdev, 383fd8bdb23SGuenter Roeck struct k10temp_data *data, int limit) 384fd8bdb23SGuenter Roeck { 385fd8bdb23SGuenter Roeck u32 regval; 386fd8bdb23SGuenter Roeck int i; 387fd8bdb23SGuenter Roeck 388fd8bdb23SGuenter Roeck for (i = 0; i < limit; i++) { 389fd8bdb23SGuenter Roeck amd_smn_read(amd_pci_dev_to_node_id(pdev), 39017822417SWei Huang ZEN_CCD_TEMP(i), ®val); 39117822417SWei Huang if (regval & ZEN_CCD_TEMP_VALID) 39260465245SGuenter Roeck data->show_temp |= BIT(TCCD_BIT(i)); 393fd8bdb23SGuenter Roeck } 394fd8bdb23SGuenter Roeck } 395fd8bdb23SGuenter Roeck 396d547552aSGuenter Roeck static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) 3973c57e89bSClemens Ladisch { 398c5114a1cSClemens Ladisch int unreliable = has_erratum_319(pdev); 3993e3e1022SGuenter Roeck struct device *dev = &pdev->dev; 40068546abfSGuenter Roeck struct k10temp_data *data; 4013e3e1022SGuenter Roeck struct device *hwmon_dev; 4021b50b776SGuenter Roeck int i; 4033c57e89bSClemens Ladisch 4043e3e1022SGuenter Roeck if (unreliable) { 4053e3e1022SGuenter Roeck if (!force) { 4063e3e1022SGuenter Roeck dev_err(dev, 4073c57e89bSClemens Ladisch "unreliable CPU thermal sensor; monitoring disabled\n"); 4083e3e1022SGuenter Roeck return -ENODEV; 4093c57e89bSClemens Ladisch } 4103e3e1022SGuenter Roeck dev_warn(dev, 4113c57e89bSClemens Ladisch "unreliable CPU thermal sensor; check erratum 319\n"); 4123c57e89bSClemens Ladisch } 4133c57e89bSClemens Ladisch 41468546abfSGuenter Roeck data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 41568546abfSGuenter Roeck if (!data) 41668546abfSGuenter Roeck return -ENOMEM; 41768546abfSGuenter Roeck 41868546abfSGuenter Roeck data->pdev = pdev; 41960465245SGuenter Roeck data->show_temp |= BIT(TCTL_BIT); /* Always show Tctl */ 42068546abfSGuenter Roeck 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->show_temp |= BIT(TDIE_BIT); /* show Tdie */ 43060465245SGuenter Roeck data->is_zen = true; 431c7579389SGuenter Roeck 432c7579389SGuenter Roeck switch (boot_cpu_data.x86_model) { 433c7579389SGuenter Roeck case 0x1: /* Zen */ 434c7579389SGuenter Roeck case 0x8: /* Zen+ */ 435c7579389SGuenter Roeck case 0x11: /* Zen APU */ 436c7579389SGuenter Roeck case 0x18: /* Zen+ APU */ 437fd8bdb23SGuenter Roeck k10temp_get_ccd_support(pdev, data, 4); 438c7579389SGuenter Roeck break; 439c7579389SGuenter Roeck case 0x31: /* Zen2 Threadripper */ 440*128066c8SMario Limonciello case 0x60: /* Renoir */ 441*128066c8SMario Limonciello case 0x68: /* Lucienne */ 442c7579389SGuenter Roeck case 0x71: /* Zen2 */ 443fd8bdb23SGuenter Roeck k10temp_get_ccd_support(pdev, data, 8); 444c7579389SGuenter Roeck break; 445c7579389SGuenter Roeck } 44655163a1cSWei Huang } else if (boot_cpu_data.x86 == 0x19) { 44755163a1cSWei Huang data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK; 44855163a1cSWei Huang data->read_tempreg = read_tempreg_nb_zen; 44955163a1cSWei Huang data->show_temp |= BIT(TDIE_BIT); 45055163a1cSWei Huang data->is_zen = true; 45155163a1cSWei Huang 45255163a1cSWei Huang switch (boot_cpu_data.x86_model) { 453c8d0d3faSGabriel Craciunescu case 0x0 ... 0x1: /* Zen3 SP3/TR */ 454c8d0d3faSGabriel Craciunescu case 0x21: /* Zen3 Ryzen Desktop */ 455*128066c8SMario Limonciello case 0x50 ... 0x5f: /* Green Sardine */ 45655163a1cSWei Huang k10temp_get_ccd_support(pdev, data, 8); 45755163a1cSWei Huang break; 45855163a1cSWei Huang } 4591b597889SGuenter Roeck } else { 46040626a1bSGuenter Roeck data->read_htcreg = read_htcreg_pci; 46168546abfSGuenter Roeck data->read_tempreg = read_tempreg_pci; 4621b597889SGuenter Roeck } 46368546abfSGuenter Roeck 4641b50b776SGuenter Roeck for (i = 0; i < ARRAY_SIZE(tctl_offset_table); i++) { 4651b50b776SGuenter Roeck const struct tctl_offset *entry = &tctl_offset_table[i]; 4661b50b776SGuenter Roeck 4671b50b776SGuenter Roeck if (boot_cpu_data.x86 == entry->model && 4681b50b776SGuenter Roeck strstr(boot_cpu_data.x86_model_id, entry->id)) { 4691b50b776SGuenter Roeck data->temp_offset = entry->offset; 4701b50b776SGuenter Roeck break; 4711b50b776SGuenter Roeck } 4721b50b776SGuenter Roeck } 4731b50b776SGuenter Roeck 474d547552aSGuenter Roeck hwmon_dev = devm_hwmon_device_register_with_info(dev, "k10temp", data, 475d547552aSGuenter Roeck &k10temp_chip_info, 476d547552aSGuenter Roeck NULL); 4778999eabfSGuenter Roeck return PTR_ERR_OR_ZERO(hwmon_dev); 4783c57e89bSClemens Ladisch } 4793c57e89bSClemens Ladisch 480cd9bb056SJingoo Han static const struct pci_device_id k10temp_id_table[] = { 4813c57e89bSClemens Ladisch { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) }, 4823c57e89bSClemens Ladisch { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) }, 483aa4790a6SClemens Ladisch { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) }, 4849e581311SAndre Przywara { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) }, 48524214449SBorislav Petkov { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) }, 486d303b1b5SPhil Pokorny { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F3) }, 487f89ce270SAravind Gopalakrishnan { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F3) }, 488ccaf63b4SGuenter Roeck { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M70H_NB_F3) }, 48930b146d1SWei Hu { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) }, 490ec015950SAravind Gopalakrishnan { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) }, 4919af0a9aeSGuenter Roeck { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_DF_F3) }, 4923b031622SGuenter Roeck { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F3) }, 493210ba120SWoods, Brian { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M30H_DF_F3) }, 494279f0b3aSAlexander Monakov { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M60H_DF_F3) }, 49512163cfbSMarcel Bocu { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F3) }, 49655163a1cSWei Huang { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_DF_F3) }, 49702c9dce4SDavid Bartley { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M50H_DF_F3) }, 498d93217d8SPu Wen { PCI_VDEVICE(HYGON, PCI_DEVICE_ID_AMD_17H_DF_F3) }, 4993c57e89bSClemens Ladisch {} 5003c57e89bSClemens Ladisch }; 5013c57e89bSClemens Ladisch MODULE_DEVICE_TABLE(pci, k10temp_id_table); 5023c57e89bSClemens Ladisch 5033c57e89bSClemens Ladisch static struct pci_driver k10temp_driver = { 5043c57e89bSClemens Ladisch .name = "k10temp", 5053c57e89bSClemens Ladisch .id_table = k10temp_id_table, 5063c57e89bSClemens Ladisch .probe = k10temp_probe, 5073c57e89bSClemens Ladisch }; 5083c57e89bSClemens Ladisch 509f71f5a55SAxel Lin module_pci_driver(k10temp_driver); 510