1*512d1027SAndreas Herrmann /* 2*512d1027SAndreas Herrmann * fam15h_power.c - AMD Family 15h processor power monitoring 3*512d1027SAndreas Herrmann * 4*512d1027SAndreas Herrmann * Copyright (c) 2011 Advanced Micro Devices, Inc. 5*512d1027SAndreas Herrmann * Author: Andreas Herrmann <andreas.herrmann3@amd.com> 6*512d1027SAndreas Herrmann * 7*512d1027SAndreas Herrmann * 8*512d1027SAndreas Herrmann * This driver is free software; you can redistribute it and/or 9*512d1027SAndreas Herrmann * modify it under the terms of the GNU General Public License; either 10*512d1027SAndreas Herrmann * version 2 of the License, or (at your option) any later version. 11*512d1027SAndreas Herrmann * 12*512d1027SAndreas Herrmann * This driver is distributed in the hope that it will be useful, 13*512d1027SAndreas Herrmann * but WITHOUT ANY WARRANTY; without even the implied warranty of 14*512d1027SAndreas Herrmann * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 15*512d1027SAndreas Herrmann * See the GNU General Public License for more details. 16*512d1027SAndreas Herrmann * 17*512d1027SAndreas Herrmann * You should have received a copy of the GNU General Public License 18*512d1027SAndreas Herrmann * along with this driver; if not, see <http://www.gnu.org/licenses/>. 19*512d1027SAndreas Herrmann */ 20*512d1027SAndreas Herrmann 21*512d1027SAndreas Herrmann #include <linux/err.h> 22*512d1027SAndreas Herrmann #include <linux/hwmon.h> 23*512d1027SAndreas Herrmann #include <linux/hwmon-sysfs.h> 24*512d1027SAndreas Herrmann #include <linux/init.h> 25*512d1027SAndreas Herrmann #include <linux/module.h> 26*512d1027SAndreas Herrmann #include <linux/pci.h> 27*512d1027SAndreas Herrmann #include <linux/bitops.h> 28*512d1027SAndreas Herrmann #include <asm/processor.h> 29*512d1027SAndreas Herrmann 30*512d1027SAndreas Herrmann MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor"); 31*512d1027SAndreas Herrmann MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>"); 32*512d1027SAndreas Herrmann MODULE_LICENSE("GPL"); 33*512d1027SAndreas Herrmann 34*512d1027SAndreas Herrmann /* D18F3 */ 35*512d1027SAndreas Herrmann #define REG_NORTHBRIDGE_CAP 0xe8 36*512d1027SAndreas Herrmann 37*512d1027SAndreas Herrmann /* D18F4 */ 38*512d1027SAndreas Herrmann #define REG_PROCESSOR_TDP 0x1b8 39*512d1027SAndreas Herrmann 40*512d1027SAndreas Herrmann /* D18F5 */ 41*512d1027SAndreas Herrmann #define REG_TDP_RUNNING_AVERAGE 0xe0 42*512d1027SAndreas Herrmann #define REG_TDP_LIMIT3 0xe8 43*512d1027SAndreas Herrmann 44*512d1027SAndreas Herrmann struct fam15h_power_data { 45*512d1027SAndreas Herrmann struct device *hwmon_dev; 46*512d1027SAndreas Herrmann unsigned int tdp_to_watts; 47*512d1027SAndreas Herrmann unsigned int base_tdp; 48*512d1027SAndreas Herrmann unsigned int processor_pwr_watts; 49*512d1027SAndreas Herrmann }; 50*512d1027SAndreas Herrmann 51*512d1027SAndreas Herrmann static ssize_t show_power(struct device *dev, 52*512d1027SAndreas Herrmann struct device_attribute *attr, char *buf) 53*512d1027SAndreas Herrmann { 54*512d1027SAndreas Herrmann u32 val, tdp_limit, running_avg_range; 55*512d1027SAndreas Herrmann s32 running_avg_capture; 56*512d1027SAndreas Herrmann u64 curr_pwr_watts; 57*512d1027SAndreas Herrmann struct pci_dev *f4 = to_pci_dev(dev); 58*512d1027SAndreas Herrmann struct fam15h_power_data *data = dev_get_drvdata(dev); 59*512d1027SAndreas Herrmann 60*512d1027SAndreas Herrmann pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5), 61*512d1027SAndreas Herrmann REG_TDP_RUNNING_AVERAGE, &val); 62*512d1027SAndreas Herrmann running_avg_capture = (val >> 4) & 0x3fffff; 63*512d1027SAndreas Herrmann running_avg_capture = sign_extend32(running_avg_capture, 22); 64*512d1027SAndreas Herrmann running_avg_range = val & 0xf; 65*512d1027SAndreas Herrmann 66*512d1027SAndreas Herrmann pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5), 67*512d1027SAndreas Herrmann REG_TDP_LIMIT3, &val); 68*512d1027SAndreas Herrmann 69*512d1027SAndreas Herrmann tdp_limit = val >> 16; 70*512d1027SAndreas Herrmann curr_pwr_watts = tdp_limit + data->base_tdp - 71*512d1027SAndreas Herrmann (s32)(running_avg_capture >> (running_avg_range + 1)); 72*512d1027SAndreas Herrmann curr_pwr_watts *= data->tdp_to_watts; 73*512d1027SAndreas Herrmann 74*512d1027SAndreas Herrmann /* 75*512d1027SAndreas Herrmann * Convert to microWatt 76*512d1027SAndreas Herrmann * 77*512d1027SAndreas Herrmann * power is in Watt provided as fixed point integer with 78*512d1027SAndreas Herrmann * scaling factor 1/(2^16). For conversion we use 79*512d1027SAndreas Herrmann * (10^6)/(2^16) = 15625/(2^10) 80*512d1027SAndreas Herrmann */ 81*512d1027SAndreas Herrmann curr_pwr_watts = (curr_pwr_watts * 15625) >> 10; 82*512d1027SAndreas Herrmann return sprintf(buf, "%u\n", (unsigned int) curr_pwr_watts); 83*512d1027SAndreas Herrmann } 84*512d1027SAndreas Herrmann static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL); 85*512d1027SAndreas Herrmann 86*512d1027SAndreas Herrmann static ssize_t show_power_crit(struct device *dev, 87*512d1027SAndreas Herrmann struct device_attribute *attr, char *buf) 88*512d1027SAndreas Herrmann { 89*512d1027SAndreas Herrmann struct fam15h_power_data *data = dev_get_drvdata(dev); 90*512d1027SAndreas Herrmann 91*512d1027SAndreas Herrmann return sprintf(buf, "%u\n", data->processor_pwr_watts); 92*512d1027SAndreas Herrmann } 93*512d1027SAndreas Herrmann static DEVICE_ATTR(power1_crit, S_IRUGO, show_power_crit, NULL); 94*512d1027SAndreas Herrmann 95*512d1027SAndreas Herrmann static ssize_t show_name(struct device *dev, 96*512d1027SAndreas Herrmann struct device_attribute *attr, char *buf) 97*512d1027SAndreas Herrmann { 98*512d1027SAndreas Herrmann return sprintf(buf, "fam15h_power\n"); 99*512d1027SAndreas Herrmann } 100*512d1027SAndreas Herrmann static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); 101*512d1027SAndreas Herrmann 102*512d1027SAndreas Herrmann static struct attribute *fam15h_power_attrs[] = { 103*512d1027SAndreas Herrmann &dev_attr_power1_input.attr, 104*512d1027SAndreas Herrmann &dev_attr_power1_crit.attr, 105*512d1027SAndreas Herrmann &dev_attr_name.attr, 106*512d1027SAndreas Herrmann NULL 107*512d1027SAndreas Herrmann }; 108*512d1027SAndreas Herrmann 109*512d1027SAndreas Herrmann static const struct attribute_group fam15h_power_attr_group = { 110*512d1027SAndreas Herrmann .attrs = fam15h_power_attrs, 111*512d1027SAndreas Herrmann }; 112*512d1027SAndreas Herrmann 113*512d1027SAndreas Herrmann static bool __devinit fam15h_power_is_internal_node0(struct pci_dev *f4) 114*512d1027SAndreas Herrmann { 115*512d1027SAndreas Herrmann u32 val; 116*512d1027SAndreas Herrmann 117*512d1027SAndreas Herrmann pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3), 118*512d1027SAndreas Herrmann REG_NORTHBRIDGE_CAP, &val); 119*512d1027SAndreas Herrmann if ((val & BIT(29)) && ((val >> 30) & 3)) 120*512d1027SAndreas Herrmann return false; 121*512d1027SAndreas Herrmann 122*512d1027SAndreas Herrmann return true; 123*512d1027SAndreas Herrmann } 124*512d1027SAndreas Herrmann 125*512d1027SAndreas Herrmann static void __devinit fam15h_power_init_data(struct pci_dev *f4, 126*512d1027SAndreas Herrmann struct fam15h_power_data *data) 127*512d1027SAndreas Herrmann { 128*512d1027SAndreas Herrmann u32 val; 129*512d1027SAndreas Herrmann u64 tmp; 130*512d1027SAndreas Herrmann 131*512d1027SAndreas Herrmann pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val); 132*512d1027SAndreas Herrmann data->base_tdp = val >> 16; 133*512d1027SAndreas Herrmann tmp = val & 0xffff; 134*512d1027SAndreas Herrmann 135*512d1027SAndreas Herrmann pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5), 136*512d1027SAndreas Herrmann REG_TDP_LIMIT3, &val); 137*512d1027SAndreas Herrmann 138*512d1027SAndreas Herrmann data->tdp_to_watts = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f); 139*512d1027SAndreas Herrmann tmp *= data->tdp_to_watts; 140*512d1027SAndreas Herrmann 141*512d1027SAndreas Herrmann /* result not allowed to be >= 256W */ 142*512d1027SAndreas Herrmann if ((tmp >> 16) >= 256) 143*512d1027SAndreas Herrmann dev_warn(&f4->dev, "Bogus value for ProcessorPwrWatts " 144*512d1027SAndreas Herrmann "(processor_pwr_watts>=%u)\n", 145*512d1027SAndreas Herrmann (unsigned int) (tmp >> 16)); 146*512d1027SAndreas Herrmann 147*512d1027SAndreas Herrmann /* convert to microWatt */ 148*512d1027SAndreas Herrmann data->processor_pwr_watts = (tmp * 15625) >> 10; 149*512d1027SAndreas Herrmann } 150*512d1027SAndreas Herrmann 151*512d1027SAndreas Herrmann static int __devinit fam15h_power_probe(struct pci_dev *pdev, 152*512d1027SAndreas Herrmann const struct pci_device_id *id) 153*512d1027SAndreas Herrmann { 154*512d1027SAndreas Herrmann struct fam15h_power_data *data; 155*512d1027SAndreas Herrmann struct device *dev; 156*512d1027SAndreas Herrmann int err; 157*512d1027SAndreas Herrmann 158*512d1027SAndreas Herrmann if (!fam15h_power_is_internal_node0(pdev)) { 159*512d1027SAndreas Herrmann err = -ENODEV; 160*512d1027SAndreas Herrmann goto exit; 161*512d1027SAndreas Herrmann } 162*512d1027SAndreas Herrmann 163*512d1027SAndreas Herrmann data = kzalloc(sizeof(struct fam15h_power_data), GFP_KERNEL); 164*512d1027SAndreas Herrmann if (!data) { 165*512d1027SAndreas Herrmann err = -ENOMEM; 166*512d1027SAndreas Herrmann goto exit; 167*512d1027SAndreas Herrmann } 168*512d1027SAndreas Herrmann fam15h_power_init_data(pdev, data); 169*512d1027SAndreas Herrmann dev = &pdev->dev; 170*512d1027SAndreas Herrmann 171*512d1027SAndreas Herrmann dev_set_drvdata(dev, data); 172*512d1027SAndreas Herrmann err = sysfs_create_group(&dev->kobj, &fam15h_power_attr_group); 173*512d1027SAndreas Herrmann if (err) 174*512d1027SAndreas Herrmann goto exit_free_data; 175*512d1027SAndreas Herrmann 176*512d1027SAndreas Herrmann data->hwmon_dev = hwmon_device_register(dev); 177*512d1027SAndreas Herrmann if (IS_ERR(data->hwmon_dev)) { 178*512d1027SAndreas Herrmann err = PTR_ERR(data->hwmon_dev); 179*512d1027SAndreas Herrmann goto exit_remove_group; 180*512d1027SAndreas Herrmann } 181*512d1027SAndreas Herrmann 182*512d1027SAndreas Herrmann return 0; 183*512d1027SAndreas Herrmann 184*512d1027SAndreas Herrmann exit_remove_group: 185*512d1027SAndreas Herrmann sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group); 186*512d1027SAndreas Herrmann exit_free_data: 187*512d1027SAndreas Herrmann kfree(data); 188*512d1027SAndreas Herrmann exit: 189*512d1027SAndreas Herrmann return err; 190*512d1027SAndreas Herrmann } 191*512d1027SAndreas Herrmann 192*512d1027SAndreas Herrmann static void __devexit fam15h_power_remove(struct pci_dev *pdev) 193*512d1027SAndreas Herrmann { 194*512d1027SAndreas Herrmann struct device *dev; 195*512d1027SAndreas Herrmann struct fam15h_power_data *data; 196*512d1027SAndreas Herrmann 197*512d1027SAndreas Herrmann dev = &pdev->dev; 198*512d1027SAndreas Herrmann data = dev_get_drvdata(dev); 199*512d1027SAndreas Herrmann hwmon_device_unregister(data->hwmon_dev); 200*512d1027SAndreas Herrmann sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group); 201*512d1027SAndreas Herrmann dev_set_drvdata(dev, NULL); 202*512d1027SAndreas Herrmann kfree(data); 203*512d1027SAndreas Herrmann } 204*512d1027SAndreas Herrmann 205*512d1027SAndreas Herrmann static DEFINE_PCI_DEVICE_TABLE(fam15h_power_id_table) = { 206*512d1027SAndreas Herrmann { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) }, 207*512d1027SAndreas Herrmann {} 208*512d1027SAndreas Herrmann }; 209*512d1027SAndreas Herrmann MODULE_DEVICE_TABLE(pci, fam15h_power_id_table); 210*512d1027SAndreas Herrmann 211*512d1027SAndreas Herrmann static struct pci_driver fam15h_power_driver = { 212*512d1027SAndreas Herrmann .name = "fam15h_power", 213*512d1027SAndreas Herrmann .id_table = fam15h_power_id_table, 214*512d1027SAndreas Herrmann .probe = fam15h_power_probe, 215*512d1027SAndreas Herrmann .remove = __devexit_p(fam15h_power_remove), 216*512d1027SAndreas Herrmann }; 217*512d1027SAndreas Herrmann 218*512d1027SAndreas Herrmann static int __init fam15h_power_init(void) 219*512d1027SAndreas Herrmann { 220*512d1027SAndreas Herrmann return pci_register_driver(&fam15h_power_driver); 221*512d1027SAndreas Herrmann } 222*512d1027SAndreas Herrmann 223*512d1027SAndreas Herrmann static void __exit fam15h_power_exit(void) 224*512d1027SAndreas Herrmann { 225*512d1027SAndreas Herrmann pci_unregister_driver(&fam15h_power_driver); 226*512d1027SAndreas Herrmann } 227*512d1027SAndreas Herrmann 228*512d1027SAndreas Herrmann module_init(fam15h_power_init) 229*512d1027SAndreas Herrmann module_exit(fam15h_power_exit) 230