18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * fam15h_power.c - AMD Family 15h processor power monitoring 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2011-2016 Advanced Micro Devices, Inc. 68c2ecf20Sopenharmony_ci * Author: Andreas Herrmann <herrmann.der.user@googlemail.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/err.h> 108c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 118c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/pci.h> 158c2ecf20Sopenharmony_ci#include <linux/bitops.h> 168c2ecf20Sopenharmony_ci#include <linux/cpu.h> 178c2ecf20Sopenharmony_ci#include <linux/cpumask.h> 188c2ecf20Sopenharmony_ci#include <linux/time.h> 198c2ecf20Sopenharmony_ci#include <linux/sched.h> 208c2ecf20Sopenharmony_ci#include <asm/processor.h> 218c2ecf20Sopenharmony_ci#include <asm/msr.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor"); 248c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andreas Herrmann <herrmann.der.user@googlemail.com>"); 258c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* D18F3 */ 288c2ecf20Sopenharmony_ci#define REG_NORTHBRIDGE_CAP 0xe8 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* D18F4 */ 318c2ecf20Sopenharmony_ci#define REG_PROCESSOR_TDP 0x1b8 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* D18F5 */ 348c2ecf20Sopenharmony_ci#define REG_TDP_RUNNING_AVERAGE 0xe0 358c2ecf20Sopenharmony_ci#define REG_TDP_LIMIT3 0xe8 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define FAM15H_MIN_NUM_ATTRS 2 388c2ecf20Sopenharmony_ci#define FAM15H_NUM_GROUPS 2 398c2ecf20Sopenharmony_ci#define MAX_CUS 8 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* set maximum interval as 1 second */ 428c2ecf20Sopenharmony_ci#define MAX_INTERVAL 1000 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_AMD_15H_M70H_NB_F4 0x15b4 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct fam15h_power_data { 478c2ecf20Sopenharmony_ci struct pci_dev *pdev; 488c2ecf20Sopenharmony_ci unsigned int tdp_to_watts; 498c2ecf20Sopenharmony_ci unsigned int base_tdp; 508c2ecf20Sopenharmony_ci unsigned int processor_pwr_watts; 518c2ecf20Sopenharmony_ci unsigned int cpu_pwr_sample_ratio; 528c2ecf20Sopenharmony_ci const struct attribute_group *groups[FAM15H_NUM_GROUPS]; 538c2ecf20Sopenharmony_ci struct attribute_group group; 548c2ecf20Sopenharmony_ci /* maximum accumulated power of a compute unit */ 558c2ecf20Sopenharmony_ci u64 max_cu_acc_power; 568c2ecf20Sopenharmony_ci /* accumulated power of the compute units */ 578c2ecf20Sopenharmony_ci u64 cu_acc_power[MAX_CUS]; 588c2ecf20Sopenharmony_ci /* performance timestamp counter */ 598c2ecf20Sopenharmony_ci u64 cpu_sw_pwr_ptsc[MAX_CUS]; 608c2ecf20Sopenharmony_ci /* online/offline status of current compute unit */ 618c2ecf20Sopenharmony_ci int cu_on[MAX_CUS]; 628c2ecf20Sopenharmony_ci unsigned long power_period; 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic bool is_carrizo_or_later(void) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci return boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model >= 0x60; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic ssize_t power1_input_show(struct device *dev, 718c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci u32 val, tdp_limit, running_avg_range; 748c2ecf20Sopenharmony_ci s32 running_avg_capture; 758c2ecf20Sopenharmony_ci u64 curr_pwr_watts; 768c2ecf20Sopenharmony_ci struct fam15h_power_data *data = dev_get_drvdata(dev); 778c2ecf20Sopenharmony_ci struct pci_dev *f4 = data->pdev; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5), 808c2ecf20Sopenharmony_ci REG_TDP_RUNNING_AVERAGE, &val); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* 838c2ecf20Sopenharmony_ci * On Carrizo and later platforms, TdpRunAvgAccCap bit field 848c2ecf20Sopenharmony_ci * is extended to 4:31 from 4:25. 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_ci if (is_carrizo_or_later()) { 878c2ecf20Sopenharmony_ci running_avg_capture = val >> 4; 888c2ecf20Sopenharmony_ci running_avg_capture = sign_extend32(running_avg_capture, 27); 898c2ecf20Sopenharmony_ci } else { 908c2ecf20Sopenharmony_ci running_avg_capture = (val >> 4) & 0x3fffff; 918c2ecf20Sopenharmony_ci running_avg_capture = sign_extend32(running_avg_capture, 21); 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci running_avg_range = (val & 0xf) + 1; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5), 978c2ecf20Sopenharmony_ci REG_TDP_LIMIT3, &val); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* 1008c2ecf20Sopenharmony_ci * On Carrizo and later platforms, ApmTdpLimit bit field 1018c2ecf20Sopenharmony_ci * is extended to 16:31 from 16:28. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci if (is_carrizo_or_later()) 1048c2ecf20Sopenharmony_ci tdp_limit = val >> 16; 1058c2ecf20Sopenharmony_ci else 1068c2ecf20Sopenharmony_ci tdp_limit = (val >> 16) & 0x1fff; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci curr_pwr_watts = ((u64)(tdp_limit + 1098c2ecf20Sopenharmony_ci data->base_tdp)) << running_avg_range; 1108c2ecf20Sopenharmony_ci curr_pwr_watts -= running_avg_capture; 1118c2ecf20Sopenharmony_ci curr_pwr_watts *= data->tdp_to_watts; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* 1148c2ecf20Sopenharmony_ci * Convert to microWatt 1158c2ecf20Sopenharmony_ci * 1168c2ecf20Sopenharmony_ci * power is in Watt provided as fixed point integer with 1178c2ecf20Sopenharmony_ci * scaling factor 1/(2^16). For conversion we use 1188c2ecf20Sopenharmony_ci * (10^6)/(2^16) = 15625/(2^10) 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_ci curr_pwr_watts = (curr_pwr_watts * 15625) >> (10 + running_avg_range); 1218c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", (unsigned int) curr_pwr_watts); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(power1_input); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic ssize_t power1_crit_show(struct device *dev, 1268c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct fam15h_power_data *data = dev_get_drvdata(dev); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", data->processor_pwr_watts); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(power1_crit); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic void do_read_registers_on_cu(void *_data) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct fam15h_power_data *data = _data; 1378c2ecf20Sopenharmony_ci int cpu, cu; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci cpu = smp_processor_id(); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* 1428c2ecf20Sopenharmony_ci * With the new x86 topology modelling, cpu core id actually 1438c2ecf20Sopenharmony_ci * is compute unit id. 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_ci cu = cpu_data(cpu).cpu_core_id; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci rdmsrl_safe(MSR_F15H_CU_PWR_ACCUMULATOR, &data->cu_acc_power[cu]); 1488c2ecf20Sopenharmony_ci rdmsrl_safe(MSR_F15H_PTSC, &data->cpu_sw_pwr_ptsc[cu]); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci data->cu_on[cu] = 1; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* 1548c2ecf20Sopenharmony_ci * This function is only able to be called when CPUID 1558c2ecf20Sopenharmony_ci * Fn8000_0007:EDX[12] is set. 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_cistatic int read_registers(struct fam15h_power_data *data) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci int core, this_core; 1608c2ecf20Sopenharmony_ci cpumask_var_t mask; 1618c2ecf20Sopenharmony_ci int ret, cpu; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci ret = zalloc_cpumask_var(&mask, GFP_KERNEL); 1648c2ecf20Sopenharmony_ci if (!ret) 1658c2ecf20Sopenharmony_ci return -ENOMEM; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci memset(data->cu_on, 0, sizeof(int) * MAX_CUS); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci get_online_cpus(); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* 1728c2ecf20Sopenharmony_ci * Choose the first online core of each compute unit, and then 1738c2ecf20Sopenharmony_ci * read their MSR value of power and ptsc in a single IPI, 1748c2ecf20Sopenharmony_ci * because the MSR value of CPU core represent the compute 1758c2ecf20Sopenharmony_ci * unit's. 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_ci core = -1; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci for_each_online_cpu(cpu) { 1808c2ecf20Sopenharmony_ci this_core = topology_core_id(cpu); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (this_core == core) 1838c2ecf20Sopenharmony_ci continue; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci core = this_core; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* get any CPU on this compute unit */ 1888c2ecf20Sopenharmony_ci cpumask_set_cpu(cpumask_any(topology_sibling_cpumask(cpu)), mask); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci on_each_cpu_mask(mask, do_read_registers_on_cu, data, true); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci put_online_cpus(); 1948c2ecf20Sopenharmony_ci free_cpumask_var(mask); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return 0; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic ssize_t power1_average_show(struct device *dev, 2008c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct fam15h_power_data *data = dev_get_drvdata(dev); 2038c2ecf20Sopenharmony_ci u64 prev_cu_acc_power[MAX_CUS], prev_ptsc[MAX_CUS], 2048c2ecf20Sopenharmony_ci jdelta[MAX_CUS]; 2058c2ecf20Sopenharmony_ci u64 tdelta, avg_acc; 2068c2ecf20Sopenharmony_ci int cu, cu_num, ret; 2078c2ecf20Sopenharmony_ci signed long leftover; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* 2108c2ecf20Sopenharmony_ci * With the new x86 topology modelling, x86_max_cores is the 2118c2ecf20Sopenharmony_ci * compute unit number. 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_ci cu_num = boot_cpu_data.x86_max_cores; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci ret = read_registers(data); 2168c2ecf20Sopenharmony_ci if (ret) 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci for (cu = 0; cu < cu_num; cu++) { 2208c2ecf20Sopenharmony_ci prev_cu_acc_power[cu] = data->cu_acc_power[cu]; 2218c2ecf20Sopenharmony_ci prev_ptsc[cu] = data->cpu_sw_pwr_ptsc[cu]; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci leftover = schedule_timeout_interruptible(msecs_to_jiffies(data->power_period)); 2258c2ecf20Sopenharmony_ci if (leftover) 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci ret = read_registers(data); 2298c2ecf20Sopenharmony_ci if (ret) 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci for (cu = 0, avg_acc = 0; cu < cu_num; cu++) { 2338c2ecf20Sopenharmony_ci /* check if current compute unit is online */ 2348c2ecf20Sopenharmony_ci if (data->cu_on[cu] == 0) 2358c2ecf20Sopenharmony_ci continue; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (data->cu_acc_power[cu] < prev_cu_acc_power[cu]) { 2388c2ecf20Sopenharmony_ci jdelta[cu] = data->max_cu_acc_power + data->cu_acc_power[cu]; 2398c2ecf20Sopenharmony_ci jdelta[cu] -= prev_cu_acc_power[cu]; 2408c2ecf20Sopenharmony_ci } else { 2418c2ecf20Sopenharmony_ci jdelta[cu] = data->cu_acc_power[cu] - prev_cu_acc_power[cu]; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci tdelta = data->cpu_sw_pwr_ptsc[cu] - prev_ptsc[cu]; 2448c2ecf20Sopenharmony_ci jdelta[cu] *= data->cpu_pwr_sample_ratio * 1000; 2458c2ecf20Sopenharmony_ci do_div(jdelta[cu], tdelta); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* the unit is microWatt */ 2488c2ecf20Sopenharmony_ci avg_acc += jdelta[cu]; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return sprintf(buf, "%llu\n", (unsigned long long)avg_acc); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(power1_average); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic ssize_t power1_average_interval_show(struct device *dev, 2568c2ecf20Sopenharmony_ci struct device_attribute *attr, 2578c2ecf20Sopenharmony_ci char *buf) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci struct fam15h_power_data *data = dev_get_drvdata(dev); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci return sprintf(buf, "%lu\n", data->power_period); 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic ssize_t power1_average_interval_store(struct device *dev, 2658c2ecf20Sopenharmony_ci struct device_attribute *attr, 2668c2ecf20Sopenharmony_ci const char *buf, size_t count) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct fam15h_power_data *data = dev_get_drvdata(dev); 2698c2ecf20Sopenharmony_ci unsigned long temp; 2708c2ecf20Sopenharmony_ci int ret; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci ret = kstrtoul(buf, 10, &temp); 2738c2ecf20Sopenharmony_ci if (ret) 2748c2ecf20Sopenharmony_ci return ret; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (temp > MAX_INTERVAL) 2778c2ecf20Sopenharmony_ci return -EINVAL; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* the interval value should be greater than 0 */ 2808c2ecf20Sopenharmony_ci if (temp <= 0) 2818c2ecf20Sopenharmony_ci return -EINVAL; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci data->power_period = temp; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return count; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(power1_average_interval); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic int fam15h_power_init_attrs(struct pci_dev *pdev, 2908c2ecf20Sopenharmony_ci struct fam15h_power_data *data) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci int n = FAM15H_MIN_NUM_ATTRS; 2938c2ecf20Sopenharmony_ci struct attribute **fam15h_power_attrs; 2948c2ecf20Sopenharmony_ci struct cpuinfo_x86 *c = &boot_cpu_data; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (c->x86 == 0x15 && 2978c2ecf20Sopenharmony_ci (c->x86_model <= 0xf || 2988c2ecf20Sopenharmony_ci (c->x86_model >= 0x60 && c->x86_model <= 0x7f))) 2998c2ecf20Sopenharmony_ci n += 1; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* check if processor supports accumulated power */ 3028c2ecf20Sopenharmony_ci if (boot_cpu_has(X86_FEATURE_ACC_POWER)) 3038c2ecf20Sopenharmony_ci n += 2; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci fam15h_power_attrs = devm_kcalloc(&pdev->dev, n, 3068c2ecf20Sopenharmony_ci sizeof(*fam15h_power_attrs), 3078c2ecf20Sopenharmony_ci GFP_KERNEL); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (!fam15h_power_attrs) 3108c2ecf20Sopenharmony_ci return -ENOMEM; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci n = 0; 3138c2ecf20Sopenharmony_ci fam15h_power_attrs[n++] = &dev_attr_power1_crit.attr; 3148c2ecf20Sopenharmony_ci if (c->x86 == 0x15 && 3158c2ecf20Sopenharmony_ci (c->x86_model <= 0xf || 3168c2ecf20Sopenharmony_ci (c->x86_model >= 0x60 && c->x86_model <= 0x7f))) 3178c2ecf20Sopenharmony_ci fam15h_power_attrs[n++] = &dev_attr_power1_input.attr; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (boot_cpu_has(X86_FEATURE_ACC_POWER)) { 3208c2ecf20Sopenharmony_ci fam15h_power_attrs[n++] = &dev_attr_power1_average.attr; 3218c2ecf20Sopenharmony_ci fam15h_power_attrs[n++] = &dev_attr_power1_average_interval.attr; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci data->group.attrs = fam15h_power_attrs; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic bool should_load_on_this_node(struct pci_dev *f4) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci u32 val; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3), 3348c2ecf20Sopenharmony_ci REG_NORTHBRIDGE_CAP, &val); 3358c2ecf20Sopenharmony_ci if ((val & BIT(29)) && ((val >> 30) & 3)) 3368c2ecf20Sopenharmony_ci return false; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci return true; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci/* 3428c2ecf20Sopenharmony_ci * Newer BKDG versions have an updated recommendation on how to properly 3438c2ecf20Sopenharmony_ci * initialize the running average range (was: 0xE, now: 0x9). This avoids 3448c2ecf20Sopenharmony_ci * counter saturations resulting in bogus power readings. 3458c2ecf20Sopenharmony_ci * We correct this value ourselves to cope with older BIOSes. 3468c2ecf20Sopenharmony_ci */ 3478c2ecf20Sopenharmony_cistatic const struct pci_device_id affected_device[] = { 3488c2ecf20Sopenharmony_ci { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) }, 3498c2ecf20Sopenharmony_ci { 0 } 3508c2ecf20Sopenharmony_ci}; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic void tweak_runavg_range(struct pci_dev *pdev) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci u32 val; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci /* 3578c2ecf20Sopenharmony_ci * let this quirk apply only to the current version of the 3588c2ecf20Sopenharmony_ci * northbridge, since future versions may change the behavior 3598c2ecf20Sopenharmony_ci */ 3608c2ecf20Sopenharmony_ci if (!pci_match_id(affected_device, pdev)) 3618c2ecf20Sopenharmony_ci return; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci pci_bus_read_config_dword(pdev->bus, 3648c2ecf20Sopenharmony_ci PCI_DEVFN(PCI_SLOT(pdev->devfn), 5), 3658c2ecf20Sopenharmony_ci REG_TDP_RUNNING_AVERAGE, &val); 3668c2ecf20Sopenharmony_ci if ((val & 0xf) != 0xe) 3678c2ecf20Sopenharmony_ci return; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci val &= ~0xf; 3708c2ecf20Sopenharmony_ci val |= 0x9; 3718c2ecf20Sopenharmony_ci pci_bus_write_config_dword(pdev->bus, 3728c2ecf20Sopenharmony_ci PCI_DEVFN(PCI_SLOT(pdev->devfn), 5), 3738c2ecf20Sopenharmony_ci REG_TDP_RUNNING_AVERAGE, val); 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 3778c2ecf20Sopenharmony_cistatic int fam15h_power_resume(struct pci_dev *pdev) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci tweak_runavg_range(pdev); 3808c2ecf20Sopenharmony_ci return 0; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci#else 3838c2ecf20Sopenharmony_ci#define fam15h_power_resume NULL 3848c2ecf20Sopenharmony_ci#endif 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic int fam15h_power_init_data(struct pci_dev *f4, 3878c2ecf20Sopenharmony_ci struct fam15h_power_data *data) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci u32 val; 3908c2ecf20Sopenharmony_ci u64 tmp; 3918c2ecf20Sopenharmony_ci int ret; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val); 3948c2ecf20Sopenharmony_ci data->base_tdp = val >> 16; 3958c2ecf20Sopenharmony_ci tmp = val & 0xffff; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5), 3988c2ecf20Sopenharmony_ci REG_TDP_LIMIT3, &val); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci data->tdp_to_watts = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f); 4018c2ecf20Sopenharmony_ci tmp *= data->tdp_to_watts; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* result not allowed to be >= 256W */ 4048c2ecf20Sopenharmony_ci if ((tmp >> 16) >= 256) 4058c2ecf20Sopenharmony_ci dev_warn(&f4->dev, 4068c2ecf20Sopenharmony_ci "Bogus value for ProcessorPwrWatts (processor_pwr_watts>=%u)\n", 4078c2ecf20Sopenharmony_ci (unsigned int) (tmp >> 16)); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* convert to microWatt */ 4108c2ecf20Sopenharmony_ci data->processor_pwr_watts = (tmp * 15625) >> 10; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci ret = fam15h_power_init_attrs(f4, data); 4138c2ecf20Sopenharmony_ci if (ret) 4148c2ecf20Sopenharmony_ci return ret; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* CPUID Fn8000_0007:EDX[12] indicates to support accumulated power */ 4188c2ecf20Sopenharmony_ci if (!boot_cpu_has(X86_FEATURE_ACC_POWER)) 4198c2ecf20Sopenharmony_ci return 0; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* 4228c2ecf20Sopenharmony_ci * determine the ratio of the compute unit power accumulator 4238c2ecf20Sopenharmony_ci * sample period to the PTSC counter period by executing CPUID 4248c2ecf20Sopenharmony_ci * Fn8000_0007:ECX 4258c2ecf20Sopenharmony_ci */ 4268c2ecf20Sopenharmony_ci data->cpu_pwr_sample_ratio = cpuid_ecx(0x80000007); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (rdmsrl_safe(MSR_F15H_CU_MAX_PWR_ACCUMULATOR, &tmp)) { 4298c2ecf20Sopenharmony_ci pr_err("Failed to read max compute unit power accumulator MSR\n"); 4308c2ecf20Sopenharmony_ci return -ENODEV; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci data->max_cu_acc_power = tmp; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* 4368c2ecf20Sopenharmony_ci * Milliseconds are a reasonable interval for the measurement. 4378c2ecf20Sopenharmony_ci * But it shouldn't set too long here, because several seconds 4388c2ecf20Sopenharmony_ci * would cause the read function to hang. So set default 4398c2ecf20Sopenharmony_ci * interval as 10 ms. 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci data->power_period = 10; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci return read_registers(data); 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic int fam15h_power_probe(struct pci_dev *pdev, 4478c2ecf20Sopenharmony_ci const struct pci_device_id *id) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci struct fam15h_power_data *data; 4508c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 4518c2ecf20Sopenharmony_ci struct device *hwmon_dev; 4528c2ecf20Sopenharmony_ci int ret; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* 4558c2ecf20Sopenharmony_ci * though we ignore every other northbridge, we still have to 4568c2ecf20Sopenharmony_ci * do the tweaking on _each_ node in MCM processors as the counters 4578c2ecf20Sopenharmony_ci * are working hand-in-hand 4588c2ecf20Sopenharmony_ci */ 4598c2ecf20Sopenharmony_ci tweak_runavg_range(pdev); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (!should_load_on_this_node(pdev)) 4628c2ecf20Sopenharmony_ci return -ENODEV; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci data = devm_kzalloc(dev, sizeof(struct fam15h_power_data), GFP_KERNEL); 4658c2ecf20Sopenharmony_ci if (!data) 4668c2ecf20Sopenharmony_ci return -ENOMEM; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci ret = fam15h_power_init_data(pdev, data); 4698c2ecf20Sopenharmony_ci if (ret) 4708c2ecf20Sopenharmony_ci return ret; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci data->pdev = pdev; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci data->groups[0] = &data->group; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_groups(dev, "fam15h_power", 4778c2ecf20Sopenharmony_ci data, 4788c2ecf20Sopenharmony_ci &data->groups[0]); 4798c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(hwmon_dev); 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic const struct pci_device_id fam15h_power_id_table[] = { 4838c2ecf20Sopenharmony_ci { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) }, 4848c2ecf20Sopenharmony_ci { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F4) }, 4858c2ecf20Sopenharmony_ci { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F4) }, 4868c2ecf20Sopenharmony_ci { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M70H_NB_F4) }, 4878c2ecf20Sopenharmony_ci { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F4) }, 4888c2ecf20Sopenharmony_ci { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F4) }, 4898c2ecf20Sopenharmony_ci {} 4908c2ecf20Sopenharmony_ci}; 4918c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, fam15h_power_id_table); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic struct pci_driver fam15h_power_driver = { 4948c2ecf20Sopenharmony_ci .name = "fam15h_power", 4958c2ecf20Sopenharmony_ci .id_table = fam15h_power_id_table, 4968c2ecf20Sopenharmony_ci .probe = fam15h_power_probe, 4978c2ecf20Sopenharmony_ci .resume = fam15h_power_resume, 4988c2ecf20Sopenharmony_ci}; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cimodule_pci_driver(fam15h_power_driver); 501