13c57e89bSClemens Ladisch /* 230b146d1SWei Hu * k10temp.c - AMD Family 10h/11h/12h/14h/15h/16h processor hardware monitoring 33c57e89bSClemens Ladisch * 43c57e89bSClemens Ladisch * Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de> 53c57e89bSClemens Ladisch * 63c57e89bSClemens Ladisch * 73c57e89bSClemens Ladisch * This driver is free software; you can redistribute it and/or 83c57e89bSClemens Ladisch * modify it under the terms of the GNU General Public License; either 93c57e89bSClemens Ladisch * version 2 of the License, or (at your option) any later version. 103c57e89bSClemens Ladisch * 113c57e89bSClemens Ladisch * This driver is distributed in the hope that it will be useful, 123c57e89bSClemens Ladisch * but WITHOUT ANY WARRANTY; without even the implied warranty of 133c57e89bSClemens Ladisch * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 143c57e89bSClemens Ladisch * See the GNU General Public License for more details. 153c57e89bSClemens Ladisch * 163c57e89bSClemens Ladisch * You should have received a copy of the GNU General Public License 173c57e89bSClemens Ladisch * along with this driver; if not, see <http://www.gnu.org/licenses/>. 183c57e89bSClemens Ladisch */ 193c57e89bSClemens Ladisch 203c57e89bSClemens Ladisch #include <linux/err.h> 213c57e89bSClemens Ladisch #include <linux/hwmon.h> 223c57e89bSClemens Ladisch #include <linux/hwmon-sysfs.h> 233c57e89bSClemens Ladisch #include <linux/init.h> 243c57e89bSClemens Ladisch #include <linux/module.h> 253c57e89bSClemens Ladisch #include <linux/pci.h> 263c57e89bSClemens Ladisch #include <asm/processor.h> 273c57e89bSClemens Ladisch 289e581311SAndre Przywara MODULE_DESCRIPTION("AMD Family 10h+ CPU core temperature monitor"); 293c57e89bSClemens Ladisch MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); 303c57e89bSClemens Ladisch MODULE_LICENSE("GPL"); 313c57e89bSClemens Ladisch 323c57e89bSClemens Ladisch static bool force; 333c57e89bSClemens Ladisch module_param(force, bool, 0444); 343c57e89bSClemens Ladisch MODULE_PARM_DESC(force, "force loading on processors with erratum 319"); 353c57e89bSClemens Ladisch 36f89ce270SAravind Gopalakrishnan /* Provide lock for writing to NB_SMU_IND_ADDR */ 37f89ce270SAravind Gopalakrishnan static DEFINE_MUTEX(nb_smu_ind_mutex); 38f89ce270SAravind Gopalakrishnan 39c5114a1cSClemens Ladisch /* CPUID function 0x80000001, ebx */ 40c5114a1cSClemens Ladisch #define CPUID_PKGTYPE_MASK 0xf0000000 41c5114a1cSClemens Ladisch #define CPUID_PKGTYPE_F 0x00000000 42c5114a1cSClemens Ladisch #define CPUID_PKGTYPE_AM2R2_AM3 0x10000000 43c5114a1cSClemens Ladisch 44c5114a1cSClemens Ladisch /* DRAM controller (PCI function 2) */ 45c5114a1cSClemens Ladisch #define REG_DCT0_CONFIG_HIGH 0x094 46c5114a1cSClemens Ladisch #define DDR3_MODE 0x00000100 47c5114a1cSClemens Ladisch 48c5114a1cSClemens Ladisch /* miscellaneous (PCI function 3) */ 493c57e89bSClemens Ladisch #define REG_HARDWARE_THERMAL_CONTROL 0x64 503c57e89bSClemens Ladisch #define HTC_ENABLE 0x00000001 513c57e89bSClemens Ladisch 523c57e89bSClemens Ladisch #define REG_REPORTED_TEMPERATURE 0xa4 533c57e89bSClemens Ladisch 543c57e89bSClemens Ladisch #define REG_NORTHBRIDGE_CAPABILITIES 0xe8 553c57e89bSClemens Ladisch #define NB_CAP_HTC 0x00000400 563c57e89bSClemens Ladisch 57f89ce270SAravind Gopalakrishnan /* 58f89ce270SAravind Gopalakrishnan * For F15h M60h, functionality of REG_REPORTED_TEMPERATURE 59f89ce270SAravind Gopalakrishnan * has been moved to D0F0xBC_xD820_0CA4 [Reported Temperature 60f89ce270SAravind Gopalakrishnan * Control] 61f89ce270SAravind Gopalakrishnan */ 62f89ce270SAravind Gopalakrishnan #define F15H_M60H_REPORTED_TEMP_CTRL_OFFSET 0xd8200ca4 63f89ce270SAravind Gopalakrishnan #define PCI_DEVICE_ID_AMD_15H_M60H_NB_F3 0x1573 64f89ce270SAravind Gopalakrishnan 65f89ce270SAravind Gopalakrishnan static void amd_nb_smu_index_read(struct pci_dev *pdev, unsigned int devfn, 66f89ce270SAravind Gopalakrishnan int offset, u32 *val) 67f89ce270SAravind Gopalakrishnan { 68f89ce270SAravind Gopalakrishnan mutex_lock(&nb_smu_ind_mutex); 69f89ce270SAravind Gopalakrishnan pci_bus_write_config_dword(pdev->bus, devfn, 70f89ce270SAravind Gopalakrishnan 0xb8, offset); 71f89ce270SAravind Gopalakrishnan pci_bus_read_config_dword(pdev->bus, devfn, 72f89ce270SAravind Gopalakrishnan 0xbc, val); 73f89ce270SAravind Gopalakrishnan mutex_unlock(&nb_smu_ind_mutex); 74f89ce270SAravind Gopalakrishnan } 75f89ce270SAravind Gopalakrishnan 763c57e89bSClemens Ladisch static ssize_t show_temp(struct device *dev, 773c57e89bSClemens Ladisch struct device_attribute *attr, char *buf) 783c57e89bSClemens Ladisch { 793c57e89bSClemens Ladisch u32 regval; 80f89ce270SAravind Gopalakrishnan struct pci_dev *pdev = to_pci_dev(dev); 813c57e89bSClemens Ladisch 82f89ce270SAravind Gopalakrishnan if (boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model == 0x60) { 83f89ce270SAravind Gopalakrishnan amd_nb_smu_index_read(pdev, PCI_DEVFN(0, 0), 84f89ce270SAravind Gopalakrishnan F15H_M60H_REPORTED_TEMP_CTRL_OFFSET, 85f89ce270SAravind Gopalakrishnan ®val); 86f89ce270SAravind Gopalakrishnan } else { 87f89ce270SAravind Gopalakrishnan pci_read_config_dword(pdev, REG_REPORTED_TEMPERATURE, ®val); 88f89ce270SAravind Gopalakrishnan } 893c57e89bSClemens Ladisch return sprintf(buf, "%u\n", (regval >> 21) * 125); 903c57e89bSClemens Ladisch } 913c57e89bSClemens Ladisch 923c57e89bSClemens Ladisch static ssize_t show_temp_max(struct device *dev, 933c57e89bSClemens Ladisch struct device_attribute *attr, char *buf) 943c57e89bSClemens Ladisch { 953c57e89bSClemens Ladisch return sprintf(buf, "%d\n", 70 * 1000); 963c57e89bSClemens Ladisch } 973c57e89bSClemens Ladisch 983c57e89bSClemens Ladisch static ssize_t show_temp_crit(struct device *dev, 993c57e89bSClemens Ladisch struct device_attribute *devattr, char *buf) 1003c57e89bSClemens Ladisch { 1013c57e89bSClemens Ladisch struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 1023c57e89bSClemens Ladisch int show_hyst = attr->index; 1033c57e89bSClemens Ladisch u32 regval; 1043c57e89bSClemens Ladisch int value; 1053c57e89bSClemens Ladisch 1063c57e89bSClemens Ladisch pci_read_config_dword(to_pci_dev(dev), 1073c57e89bSClemens Ladisch REG_HARDWARE_THERMAL_CONTROL, ®val); 1083c57e89bSClemens Ladisch value = ((regval >> 16) & 0x7f) * 500 + 52000; 1093c57e89bSClemens Ladisch if (show_hyst) 1103c57e89bSClemens Ladisch value -= ((regval >> 24) & 0xf) * 500; 1113c57e89bSClemens Ladisch return sprintf(buf, "%d\n", value); 1123c57e89bSClemens Ladisch } 1133c57e89bSClemens Ladisch 1143c57e89bSClemens Ladisch static ssize_t show_name(struct device *dev, 1153c57e89bSClemens Ladisch struct device_attribute *attr, char *buf) 1163c57e89bSClemens Ladisch { 1173c57e89bSClemens Ladisch return sprintf(buf, "k10temp\n"); 1183c57e89bSClemens Ladisch } 1193c57e89bSClemens Ladisch 1203c57e89bSClemens Ladisch static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL); 1213c57e89bSClemens Ladisch static DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, NULL); 1223c57e89bSClemens Ladisch static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit, NULL, 0); 1233c57e89bSClemens Ladisch static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_crit, NULL, 1); 1243c57e89bSClemens Ladisch static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); 1253c57e89bSClemens Ladisch 1266c931ae1SBill Pemberton static bool has_erratum_319(struct pci_dev *pdev) 1273c57e89bSClemens Ladisch { 128c5114a1cSClemens Ladisch u32 pkg_type, reg_dram_cfg; 129c5114a1cSClemens Ladisch 130c5114a1cSClemens Ladisch if (boot_cpu_data.x86 != 0x10) 131c5114a1cSClemens Ladisch return false; 132c5114a1cSClemens Ladisch 1333c57e89bSClemens Ladisch /* 134c5114a1cSClemens Ladisch * Erratum 319: The thermal sensor of Socket F/AM2+ processors 135c5114a1cSClemens Ladisch * may be unreliable. 1363c57e89bSClemens Ladisch */ 137c5114a1cSClemens Ladisch pkg_type = cpuid_ebx(0x80000001) & CPUID_PKGTYPE_MASK; 138c5114a1cSClemens Ladisch if (pkg_type == CPUID_PKGTYPE_F) 139c5114a1cSClemens Ladisch return true; 140c5114a1cSClemens Ladisch if (pkg_type != CPUID_PKGTYPE_AM2R2_AM3) 141c5114a1cSClemens Ladisch return false; 142c5114a1cSClemens Ladisch 143eefc2d9eSJean Delvare /* DDR3 memory implies socket AM3, which is good */ 144c5114a1cSClemens Ladisch pci_bus_read_config_dword(pdev->bus, 145c5114a1cSClemens Ladisch PCI_DEVFN(PCI_SLOT(pdev->devfn), 2), 146c5114a1cSClemens Ladisch REG_DCT0_CONFIG_HIGH, ®_dram_cfg); 147eefc2d9eSJean Delvare if (reg_dram_cfg & DDR3_MODE) 148eefc2d9eSJean Delvare return false; 149eefc2d9eSJean Delvare 150eefc2d9eSJean Delvare /* 151eefc2d9eSJean Delvare * Unfortunately it is possible to run a socket AM3 CPU with DDR2 152eefc2d9eSJean Delvare * memory. We blacklist all the cores which do exist in socket AM2+ 153eefc2d9eSJean Delvare * format. It still isn't perfect, as RB-C2 cores exist in both AM2+ 154eefc2d9eSJean Delvare * and AM3 formats, but that's the best we can do. 155eefc2d9eSJean Delvare */ 156eefc2d9eSJean Delvare return boot_cpu_data.x86_model < 4 || 157eefc2d9eSJean Delvare (boot_cpu_data.x86_model == 4 && boot_cpu_data.x86_mask <= 2); 1583c57e89bSClemens Ladisch } 1593c57e89bSClemens Ladisch 1606c931ae1SBill Pemberton static int k10temp_probe(struct pci_dev *pdev, 1613c57e89bSClemens Ladisch const struct pci_device_id *id) 1623c57e89bSClemens Ladisch { 1633c57e89bSClemens Ladisch struct device *hwmon_dev; 1643c57e89bSClemens Ladisch u32 reg_caps, reg_htc; 165c5114a1cSClemens Ladisch int unreliable = has_erratum_319(pdev); 1663c57e89bSClemens Ladisch int err; 1673c57e89bSClemens Ladisch 168c5114a1cSClemens Ladisch if (unreliable && !force) { 1693c57e89bSClemens Ladisch dev_err(&pdev->dev, 1703c57e89bSClemens Ladisch "unreliable CPU thermal sensor; monitoring disabled\n"); 1713c57e89bSClemens Ladisch err = -ENODEV; 1723c57e89bSClemens Ladisch goto exit; 1733c57e89bSClemens Ladisch } 1743c57e89bSClemens Ladisch 1753c57e89bSClemens Ladisch err = device_create_file(&pdev->dev, &dev_attr_temp1_input); 1763c57e89bSClemens Ladisch if (err) 1773c57e89bSClemens Ladisch goto exit; 1783c57e89bSClemens Ladisch err = device_create_file(&pdev->dev, &dev_attr_temp1_max); 1793c57e89bSClemens Ladisch if (err) 1803c57e89bSClemens Ladisch goto exit_remove; 1813c57e89bSClemens Ladisch 1823c57e89bSClemens Ladisch pci_read_config_dword(pdev, REG_NORTHBRIDGE_CAPABILITIES, ®_caps); 1833c57e89bSClemens Ladisch pci_read_config_dword(pdev, REG_HARDWARE_THERMAL_CONTROL, ®_htc); 1843c57e89bSClemens Ladisch if ((reg_caps & NB_CAP_HTC) && (reg_htc & HTC_ENABLE)) { 1853c57e89bSClemens Ladisch err = device_create_file(&pdev->dev, 1863c57e89bSClemens Ladisch &sensor_dev_attr_temp1_crit.dev_attr); 1873c57e89bSClemens Ladisch if (err) 1883c57e89bSClemens Ladisch goto exit_remove; 1893c57e89bSClemens Ladisch err = device_create_file(&pdev->dev, 1903c57e89bSClemens Ladisch &sensor_dev_attr_temp1_crit_hyst.dev_attr); 1913c57e89bSClemens Ladisch if (err) 1923c57e89bSClemens Ladisch goto exit_remove; 1933c57e89bSClemens Ladisch } 1943c57e89bSClemens Ladisch 1953c57e89bSClemens Ladisch err = device_create_file(&pdev->dev, &dev_attr_name); 1963c57e89bSClemens Ladisch if (err) 1973c57e89bSClemens Ladisch goto exit_remove; 1983c57e89bSClemens Ladisch 1993c57e89bSClemens Ladisch hwmon_dev = hwmon_device_register(&pdev->dev); 2003c57e89bSClemens Ladisch if (IS_ERR(hwmon_dev)) { 2013c57e89bSClemens Ladisch err = PTR_ERR(hwmon_dev); 2023c57e89bSClemens Ladisch goto exit_remove; 2033c57e89bSClemens Ladisch } 20495de3b25SJean Delvare pci_set_drvdata(pdev, hwmon_dev); 2053c57e89bSClemens Ladisch 206c5114a1cSClemens Ladisch if (unreliable && force) 2073c57e89bSClemens Ladisch dev_warn(&pdev->dev, 2083c57e89bSClemens Ladisch "unreliable CPU thermal sensor; check erratum 319\n"); 2093c57e89bSClemens Ladisch return 0; 2103c57e89bSClemens Ladisch 2113c57e89bSClemens Ladisch exit_remove: 2123c57e89bSClemens Ladisch device_remove_file(&pdev->dev, &dev_attr_name); 2133c57e89bSClemens Ladisch device_remove_file(&pdev->dev, &dev_attr_temp1_input); 2143c57e89bSClemens Ladisch device_remove_file(&pdev->dev, &dev_attr_temp1_max); 2153c57e89bSClemens Ladisch device_remove_file(&pdev->dev, 2163c57e89bSClemens Ladisch &sensor_dev_attr_temp1_crit.dev_attr); 2173c57e89bSClemens Ladisch device_remove_file(&pdev->dev, 2183c57e89bSClemens Ladisch &sensor_dev_attr_temp1_crit_hyst.dev_attr); 2193c57e89bSClemens Ladisch exit: 2203c57e89bSClemens Ladisch return err; 2213c57e89bSClemens Ladisch } 2223c57e89bSClemens Ladisch 223281dfd0bSBill Pemberton static void k10temp_remove(struct pci_dev *pdev) 2243c57e89bSClemens Ladisch { 22595de3b25SJean Delvare hwmon_device_unregister(pci_get_drvdata(pdev)); 2263c57e89bSClemens Ladisch device_remove_file(&pdev->dev, &dev_attr_name); 2273c57e89bSClemens Ladisch device_remove_file(&pdev->dev, &dev_attr_temp1_input); 2283c57e89bSClemens Ladisch device_remove_file(&pdev->dev, &dev_attr_temp1_max); 2293c57e89bSClemens Ladisch device_remove_file(&pdev->dev, 2303c57e89bSClemens Ladisch &sensor_dev_attr_temp1_crit.dev_attr); 2313c57e89bSClemens Ladisch device_remove_file(&pdev->dev, 2323c57e89bSClemens Ladisch &sensor_dev_attr_temp1_crit_hyst.dev_attr); 2333c57e89bSClemens Ladisch } 2343c57e89bSClemens Ladisch 235cd9bb056SJingoo Han static const struct pci_device_id k10temp_id_table[] = { 2363c57e89bSClemens Ladisch { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) }, 2373c57e89bSClemens Ladisch { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) }, 238aa4790a6SClemens Ladisch { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) }, 2399e581311SAndre Przywara { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) }, 24024214449SBorislav Petkov { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) }, 241d303b1b5SPhil Pokorny { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F3) }, 242f89ce270SAravind Gopalakrishnan { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F3) }, 24330b146d1SWei Hu { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) }, 244ec015950SAravind Gopalakrishnan { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) }, 2453c57e89bSClemens Ladisch {} 2463c57e89bSClemens Ladisch }; 2473c57e89bSClemens Ladisch MODULE_DEVICE_TABLE(pci, k10temp_id_table); 2483c57e89bSClemens Ladisch 2493c57e89bSClemens Ladisch static struct pci_driver k10temp_driver = { 2503c57e89bSClemens Ladisch .name = "k10temp", 2513c57e89bSClemens Ladisch .id_table = k10temp_id_table, 2523c57e89bSClemens Ladisch .probe = k10temp_probe, 2539e5e9b7aSBill Pemberton .remove = k10temp_remove, 2543c57e89bSClemens Ladisch }; 2553c57e89bSClemens Ladisch 256f71f5a55SAxel Lin module_pci_driver(k10temp_driver); 257