13c57e89bSClemens Ladisch /* 29e581311SAndre Przywara * k10temp.c - AMD Family 10h/11h/12h/14h/15h 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 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 115eefc2d9eSJean Delvare /* DDR3 memory implies socket AM3, which is 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); 119eefc2d9eSJean Delvare if (reg_dram_cfg & DDR3_MODE) 120eefc2d9eSJean Delvare return false; 121eefc2d9eSJean Delvare 122eefc2d9eSJean Delvare /* 123eefc2d9eSJean Delvare * Unfortunately it is possible to run a socket AM3 CPU with DDR2 124eefc2d9eSJean Delvare * memory. We blacklist all the cores which do exist in socket AM2+ 125eefc2d9eSJean Delvare * format. It still isn't perfect, as RB-C2 cores exist in both AM2+ 126eefc2d9eSJean Delvare * and AM3 formats, but that's the best we can do. 127eefc2d9eSJean Delvare */ 128eefc2d9eSJean Delvare return boot_cpu_data.x86_model < 4 || 129eefc2d9eSJean Delvare (boot_cpu_data.x86_model == 4 && boot_cpu_data.x86_mask <= 2); 1303c57e89bSClemens Ladisch } 1313c57e89bSClemens Ladisch 1323c57e89bSClemens Ladisch static int __devinit k10temp_probe(struct pci_dev *pdev, 1333c57e89bSClemens Ladisch const struct pci_device_id *id) 1343c57e89bSClemens Ladisch { 1353c57e89bSClemens Ladisch struct device *hwmon_dev; 1363c57e89bSClemens Ladisch u32 reg_caps, reg_htc; 137c5114a1cSClemens Ladisch int unreliable = has_erratum_319(pdev); 1383c57e89bSClemens Ladisch int err; 1393c57e89bSClemens Ladisch 140c5114a1cSClemens Ladisch if (unreliable && !force) { 1413c57e89bSClemens Ladisch dev_err(&pdev->dev, 1423c57e89bSClemens Ladisch "unreliable CPU thermal sensor; monitoring disabled\n"); 1433c57e89bSClemens Ladisch err = -ENODEV; 1443c57e89bSClemens Ladisch goto exit; 1453c57e89bSClemens Ladisch } 1463c57e89bSClemens Ladisch 1473c57e89bSClemens Ladisch err = device_create_file(&pdev->dev, &dev_attr_temp1_input); 1483c57e89bSClemens Ladisch if (err) 1493c57e89bSClemens Ladisch goto exit; 1503c57e89bSClemens Ladisch err = device_create_file(&pdev->dev, &dev_attr_temp1_max); 1513c57e89bSClemens Ladisch if (err) 1523c57e89bSClemens Ladisch goto exit_remove; 1533c57e89bSClemens Ladisch 1543c57e89bSClemens Ladisch pci_read_config_dword(pdev, REG_NORTHBRIDGE_CAPABILITIES, ®_caps); 1553c57e89bSClemens Ladisch pci_read_config_dword(pdev, REG_HARDWARE_THERMAL_CONTROL, ®_htc); 1563c57e89bSClemens Ladisch if ((reg_caps & NB_CAP_HTC) && (reg_htc & HTC_ENABLE)) { 1573c57e89bSClemens Ladisch err = device_create_file(&pdev->dev, 1583c57e89bSClemens Ladisch &sensor_dev_attr_temp1_crit.dev_attr); 1593c57e89bSClemens Ladisch if (err) 1603c57e89bSClemens Ladisch goto exit_remove; 1613c57e89bSClemens Ladisch err = device_create_file(&pdev->dev, 1623c57e89bSClemens Ladisch &sensor_dev_attr_temp1_crit_hyst.dev_attr); 1633c57e89bSClemens Ladisch if (err) 1643c57e89bSClemens Ladisch goto exit_remove; 1653c57e89bSClemens Ladisch } 1663c57e89bSClemens Ladisch 1673c57e89bSClemens Ladisch err = device_create_file(&pdev->dev, &dev_attr_name); 1683c57e89bSClemens Ladisch if (err) 1693c57e89bSClemens Ladisch goto exit_remove; 1703c57e89bSClemens Ladisch 1713c57e89bSClemens Ladisch hwmon_dev = hwmon_device_register(&pdev->dev); 1723c57e89bSClemens Ladisch if (IS_ERR(hwmon_dev)) { 1733c57e89bSClemens Ladisch err = PTR_ERR(hwmon_dev); 1743c57e89bSClemens Ladisch goto exit_remove; 1753c57e89bSClemens Ladisch } 17695de3b25SJean Delvare pci_set_drvdata(pdev, hwmon_dev); 1773c57e89bSClemens Ladisch 178c5114a1cSClemens Ladisch if (unreliable && force) 1793c57e89bSClemens Ladisch dev_warn(&pdev->dev, 1803c57e89bSClemens Ladisch "unreliable CPU thermal sensor; check erratum 319\n"); 1813c57e89bSClemens Ladisch return 0; 1823c57e89bSClemens Ladisch 1833c57e89bSClemens Ladisch exit_remove: 1843c57e89bSClemens Ladisch device_remove_file(&pdev->dev, &dev_attr_name); 1853c57e89bSClemens Ladisch device_remove_file(&pdev->dev, &dev_attr_temp1_input); 1863c57e89bSClemens Ladisch device_remove_file(&pdev->dev, &dev_attr_temp1_max); 1873c57e89bSClemens Ladisch device_remove_file(&pdev->dev, 1883c57e89bSClemens Ladisch &sensor_dev_attr_temp1_crit.dev_attr); 1893c57e89bSClemens Ladisch device_remove_file(&pdev->dev, 1903c57e89bSClemens Ladisch &sensor_dev_attr_temp1_crit_hyst.dev_attr); 1913c57e89bSClemens Ladisch exit: 1923c57e89bSClemens Ladisch return err; 1933c57e89bSClemens Ladisch } 1943c57e89bSClemens Ladisch 1953c57e89bSClemens Ladisch static void __devexit k10temp_remove(struct pci_dev *pdev) 1963c57e89bSClemens Ladisch { 19795de3b25SJean Delvare hwmon_device_unregister(pci_get_drvdata(pdev)); 1983c57e89bSClemens Ladisch device_remove_file(&pdev->dev, &dev_attr_name); 1993c57e89bSClemens Ladisch device_remove_file(&pdev->dev, &dev_attr_temp1_input); 2003c57e89bSClemens Ladisch device_remove_file(&pdev->dev, &dev_attr_temp1_max); 2013c57e89bSClemens Ladisch device_remove_file(&pdev->dev, 2023c57e89bSClemens Ladisch &sensor_dev_attr_temp1_crit.dev_attr); 2033c57e89bSClemens Ladisch device_remove_file(&pdev->dev, 2043c57e89bSClemens Ladisch &sensor_dev_attr_temp1_crit_hyst.dev_attr); 20595de3b25SJean Delvare pci_set_drvdata(pdev, NULL); 2063c57e89bSClemens Ladisch } 2073c57e89bSClemens Ladisch 208600151b9SFrans Meulenbroeks static DEFINE_PCI_DEVICE_TABLE(k10temp_id_table) = { 2093c57e89bSClemens Ladisch { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) }, 2103c57e89bSClemens Ladisch { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) }, 211aa4790a6SClemens Ladisch { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) }, 2129e581311SAndre Przywara { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) }, 21324214449SBorislav Petkov { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) }, 2143c57e89bSClemens Ladisch {} 2153c57e89bSClemens Ladisch }; 2163c57e89bSClemens Ladisch MODULE_DEVICE_TABLE(pci, k10temp_id_table); 2173c57e89bSClemens Ladisch 2183c57e89bSClemens Ladisch static struct pci_driver k10temp_driver = { 2193c57e89bSClemens Ladisch .name = "k10temp", 2203c57e89bSClemens Ladisch .id_table = k10temp_id_table, 2213c57e89bSClemens Ladisch .probe = k10temp_probe, 2223c57e89bSClemens Ladisch .remove = __devexit_p(k10temp_remove), 2233c57e89bSClemens Ladisch }; 2243c57e89bSClemens Ladisch 225f71f5a55SAxel Lin module_pci_driver(k10temp_driver); 226