13c57e89bSClemens Ladisch /* 23c57e89bSClemens Ladisch * k10temp.c - AMD Family 10h/11h 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 283c57e89bSClemens Ladisch MODULE_DESCRIPTION("AMD Family 10h/11h 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 36c5114a1cSClemens Ladisch /* CPUID function 0x80000001, ebx */ 37c5114a1cSClemens Ladisch #define CPUID_PKGTYPE_MASK 0xf0000000 38c5114a1cSClemens Ladisch #define CPUID_PKGTYPE_F 0x00000000 39c5114a1cSClemens Ladisch #define CPUID_PKGTYPE_AM2R2_AM3 0x10000000 40c5114a1cSClemens Ladisch 41c5114a1cSClemens Ladisch /* DRAM controller (PCI function 2) */ 42c5114a1cSClemens Ladisch #define REG_DCT0_CONFIG_HIGH 0x094 43c5114a1cSClemens Ladisch #define DDR3_MODE 0x00000100 44c5114a1cSClemens Ladisch 45c5114a1cSClemens Ladisch /* miscellaneous (PCI function 3) */ 463c57e89bSClemens Ladisch #define REG_HARDWARE_THERMAL_CONTROL 0x64 473c57e89bSClemens Ladisch #define HTC_ENABLE 0x00000001 483c57e89bSClemens Ladisch 493c57e89bSClemens Ladisch #define REG_REPORTED_TEMPERATURE 0xa4 503c57e89bSClemens Ladisch 513c57e89bSClemens Ladisch #define REG_NORTHBRIDGE_CAPABILITIES 0xe8 523c57e89bSClemens Ladisch #define NB_CAP_HTC 0x00000400 533c57e89bSClemens Ladisch 543c57e89bSClemens Ladisch static ssize_t show_temp(struct device *dev, 553c57e89bSClemens Ladisch struct device_attribute *attr, char *buf) 563c57e89bSClemens Ladisch { 573c57e89bSClemens Ladisch u32 regval; 583c57e89bSClemens Ladisch 593c57e89bSClemens Ladisch pci_read_config_dword(to_pci_dev(dev), 603c57e89bSClemens Ladisch REG_REPORTED_TEMPERATURE, ®val); 613c57e89bSClemens Ladisch return sprintf(buf, "%u\n", (regval >> 21) * 125); 623c57e89bSClemens Ladisch } 633c57e89bSClemens Ladisch 643c57e89bSClemens Ladisch static ssize_t show_temp_max(struct device *dev, 653c57e89bSClemens Ladisch struct device_attribute *attr, char *buf) 663c57e89bSClemens Ladisch { 673c57e89bSClemens Ladisch return sprintf(buf, "%d\n", 70 * 1000); 683c57e89bSClemens Ladisch } 693c57e89bSClemens Ladisch 703c57e89bSClemens Ladisch static ssize_t show_temp_crit(struct device *dev, 713c57e89bSClemens Ladisch struct device_attribute *devattr, char *buf) 723c57e89bSClemens Ladisch { 733c57e89bSClemens Ladisch struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 743c57e89bSClemens Ladisch int show_hyst = attr->index; 753c57e89bSClemens Ladisch u32 regval; 763c57e89bSClemens Ladisch int value; 773c57e89bSClemens Ladisch 783c57e89bSClemens Ladisch pci_read_config_dword(to_pci_dev(dev), 793c57e89bSClemens Ladisch REG_HARDWARE_THERMAL_CONTROL, ®val); 803c57e89bSClemens Ladisch value = ((regval >> 16) & 0x7f) * 500 + 52000; 813c57e89bSClemens Ladisch if (show_hyst) 823c57e89bSClemens Ladisch value -= ((regval >> 24) & 0xf) * 500; 833c57e89bSClemens Ladisch return sprintf(buf, "%d\n", value); 843c57e89bSClemens Ladisch } 853c57e89bSClemens Ladisch 863c57e89bSClemens Ladisch static ssize_t show_name(struct device *dev, 873c57e89bSClemens Ladisch struct device_attribute *attr, char *buf) 883c57e89bSClemens Ladisch { 893c57e89bSClemens Ladisch return sprintf(buf, "k10temp\n"); 903c57e89bSClemens Ladisch } 913c57e89bSClemens Ladisch 923c57e89bSClemens Ladisch static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL); 933c57e89bSClemens Ladisch static DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, NULL); 943c57e89bSClemens Ladisch static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit, NULL, 0); 953c57e89bSClemens Ladisch static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_crit, NULL, 1); 963c57e89bSClemens Ladisch static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); 973c57e89bSClemens Ladisch 98c5114a1cSClemens Ladisch static bool __devinit has_erratum_319(struct pci_dev *pdev) 993c57e89bSClemens Ladisch { 100c5114a1cSClemens Ladisch u32 pkg_type, reg_dram_cfg; 101c5114a1cSClemens Ladisch 102c5114a1cSClemens Ladisch if (boot_cpu_data.x86 != 0x10) 103c5114a1cSClemens Ladisch return false; 104c5114a1cSClemens Ladisch 1053c57e89bSClemens Ladisch /* 106c5114a1cSClemens Ladisch * Erratum 319: The thermal sensor of Socket F/AM2+ processors 107c5114a1cSClemens Ladisch * may be unreliable. 1083c57e89bSClemens Ladisch */ 109c5114a1cSClemens Ladisch pkg_type = cpuid_ebx(0x80000001) & CPUID_PKGTYPE_MASK; 110c5114a1cSClemens Ladisch if (pkg_type == CPUID_PKGTYPE_F) 111c5114a1cSClemens Ladisch return true; 112c5114a1cSClemens Ladisch if (pkg_type != CPUID_PKGTYPE_AM2R2_AM3) 113c5114a1cSClemens Ladisch return false; 114c5114a1cSClemens Ladisch 115c5114a1cSClemens Ladisch /* Differentiate between AM2+ (bad) and AM3 (good) */ 116c5114a1cSClemens Ladisch pci_bus_read_config_dword(pdev->bus, 117c5114a1cSClemens Ladisch PCI_DEVFN(PCI_SLOT(pdev->devfn), 2), 118c5114a1cSClemens Ladisch REG_DCT0_CONFIG_HIGH, ®_dram_cfg); 119c5114a1cSClemens Ladisch return !(reg_dram_cfg & DDR3_MODE); 1203c57e89bSClemens Ladisch } 1213c57e89bSClemens Ladisch 1223c57e89bSClemens Ladisch static int __devinit k10temp_probe(struct pci_dev *pdev, 1233c57e89bSClemens Ladisch const struct pci_device_id *id) 1243c57e89bSClemens Ladisch { 1253c57e89bSClemens Ladisch struct device *hwmon_dev; 1263c57e89bSClemens Ladisch u32 reg_caps, reg_htc; 127c5114a1cSClemens Ladisch int unreliable = has_erratum_319(pdev); 1283c57e89bSClemens Ladisch int err; 1293c57e89bSClemens Ladisch 130c5114a1cSClemens Ladisch if (unreliable && !force) { 1313c57e89bSClemens Ladisch dev_err(&pdev->dev, 1323c57e89bSClemens Ladisch "unreliable CPU thermal sensor; monitoring disabled\n"); 1333c57e89bSClemens Ladisch err = -ENODEV; 1343c57e89bSClemens Ladisch goto exit; 1353c57e89bSClemens Ladisch } 1363c57e89bSClemens Ladisch 1373c57e89bSClemens Ladisch err = device_create_file(&pdev->dev, &dev_attr_temp1_input); 1383c57e89bSClemens Ladisch if (err) 1393c57e89bSClemens Ladisch goto exit; 1403c57e89bSClemens Ladisch err = device_create_file(&pdev->dev, &dev_attr_temp1_max); 1413c57e89bSClemens Ladisch if (err) 1423c57e89bSClemens Ladisch goto exit_remove; 1433c57e89bSClemens Ladisch 1443c57e89bSClemens Ladisch pci_read_config_dword(pdev, REG_NORTHBRIDGE_CAPABILITIES, ®_caps); 1453c57e89bSClemens Ladisch pci_read_config_dword(pdev, REG_HARDWARE_THERMAL_CONTROL, ®_htc); 1463c57e89bSClemens Ladisch if ((reg_caps & NB_CAP_HTC) && (reg_htc & HTC_ENABLE)) { 1473c57e89bSClemens Ladisch err = device_create_file(&pdev->dev, 1483c57e89bSClemens Ladisch &sensor_dev_attr_temp1_crit.dev_attr); 1493c57e89bSClemens Ladisch if (err) 1503c57e89bSClemens Ladisch goto exit_remove; 1513c57e89bSClemens Ladisch err = device_create_file(&pdev->dev, 1523c57e89bSClemens Ladisch &sensor_dev_attr_temp1_crit_hyst.dev_attr); 1533c57e89bSClemens Ladisch if (err) 1543c57e89bSClemens Ladisch goto exit_remove; 1553c57e89bSClemens Ladisch } 1563c57e89bSClemens Ladisch 1573c57e89bSClemens Ladisch err = device_create_file(&pdev->dev, &dev_attr_name); 1583c57e89bSClemens Ladisch if (err) 1593c57e89bSClemens Ladisch goto exit_remove; 1603c57e89bSClemens Ladisch 1613c57e89bSClemens Ladisch hwmon_dev = hwmon_device_register(&pdev->dev); 1623c57e89bSClemens Ladisch if (IS_ERR(hwmon_dev)) { 1633c57e89bSClemens Ladisch err = PTR_ERR(hwmon_dev); 1643c57e89bSClemens Ladisch goto exit_remove; 1653c57e89bSClemens Ladisch } 1663c57e89bSClemens Ladisch dev_set_drvdata(&pdev->dev, hwmon_dev); 1673c57e89bSClemens Ladisch 168c5114a1cSClemens Ladisch if (unreliable && force) 1693c57e89bSClemens Ladisch dev_warn(&pdev->dev, 1703c57e89bSClemens Ladisch "unreliable CPU thermal sensor; check erratum 319\n"); 1713c57e89bSClemens Ladisch return 0; 1723c57e89bSClemens Ladisch 1733c57e89bSClemens Ladisch exit_remove: 1743c57e89bSClemens Ladisch device_remove_file(&pdev->dev, &dev_attr_name); 1753c57e89bSClemens Ladisch device_remove_file(&pdev->dev, &dev_attr_temp1_input); 1763c57e89bSClemens Ladisch device_remove_file(&pdev->dev, &dev_attr_temp1_max); 1773c57e89bSClemens Ladisch device_remove_file(&pdev->dev, 1783c57e89bSClemens Ladisch &sensor_dev_attr_temp1_crit.dev_attr); 1793c57e89bSClemens Ladisch device_remove_file(&pdev->dev, 1803c57e89bSClemens Ladisch &sensor_dev_attr_temp1_crit_hyst.dev_attr); 1813c57e89bSClemens Ladisch exit: 1823c57e89bSClemens Ladisch return err; 1833c57e89bSClemens Ladisch } 1843c57e89bSClemens Ladisch 1853c57e89bSClemens Ladisch static void __devexit k10temp_remove(struct pci_dev *pdev) 1863c57e89bSClemens Ladisch { 1873c57e89bSClemens Ladisch hwmon_device_unregister(dev_get_drvdata(&pdev->dev)); 1883c57e89bSClemens Ladisch device_remove_file(&pdev->dev, &dev_attr_name); 1893c57e89bSClemens Ladisch device_remove_file(&pdev->dev, &dev_attr_temp1_input); 1903c57e89bSClemens Ladisch device_remove_file(&pdev->dev, &dev_attr_temp1_max); 1913c57e89bSClemens Ladisch device_remove_file(&pdev->dev, 1923c57e89bSClemens Ladisch &sensor_dev_attr_temp1_crit.dev_attr); 1933c57e89bSClemens Ladisch device_remove_file(&pdev->dev, 1943c57e89bSClemens Ladisch &sensor_dev_attr_temp1_crit_hyst.dev_attr); 1953c57e89bSClemens Ladisch dev_set_drvdata(&pdev->dev, NULL); 1963c57e89bSClemens Ladisch } 1973c57e89bSClemens Ladisch 1983dd3a156SMárton Németh static const struct pci_device_id k10temp_id_table[] = { 1993c57e89bSClemens Ladisch { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) }, 2003c57e89bSClemens Ladisch { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) }, 2013c57e89bSClemens Ladisch {} 2023c57e89bSClemens Ladisch }; 2033c57e89bSClemens Ladisch MODULE_DEVICE_TABLE(pci, k10temp_id_table); 2043c57e89bSClemens Ladisch 2053c57e89bSClemens Ladisch static struct pci_driver k10temp_driver = { 2063c57e89bSClemens Ladisch .name = "k10temp", 2073c57e89bSClemens Ladisch .id_table = k10temp_id_table, 2083c57e89bSClemens Ladisch .probe = k10temp_probe, 2093c57e89bSClemens Ladisch .remove = __devexit_p(k10temp_remove), 2103c57e89bSClemens Ladisch }; 2113c57e89bSClemens Ladisch 2123c57e89bSClemens Ladisch static int __init k10temp_init(void) 2133c57e89bSClemens Ladisch { 2143c57e89bSClemens Ladisch return pci_register_driver(&k10temp_driver); 2153c57e89bSClemens Ladisch } 2163c57e89bSClemens Ladisch 2173c57e89bSClemens Ladisch static void __exit k10temp_exit(void) 2183c57e89bSClemens Ladisch { 2193c57e89bSClemens Ladisch pci_unregister_driver(&k10temp_driver); 2203c57e89bSClemens Ladisch } 2213c57e89bSClemens Ladisch 2223c57e89bSClemens Ladisch module_init(k10temp_init) 2233c57e89bSClemens Ladisch module_exit(k10temp_exit) 224