18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci/* 48c2ecf20Sopenharmony_ci * Copyright (C) 2020 Advanced Micro Devices, Inc. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include <asm/cpu_device_id.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/bits.h> 98c2ecf20Sopenharmony_ci#include <linux/cpu.h> 108c2ecf20Sopenharmony_ci#include <linux/cpumask.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/kthread.h> 168c2ecf20Sopenharmony_ci#include <linux/list.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/mutex.h> 198c2ecf20Sopenharmony_ci#include <linux/processor.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 218c2ecf20Sopenharmony_ci#include <linux/sched.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/topology.h> 248c2ecf20Sopenharmony_ci#include <linux/types.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define DRVNAME "amd_energy" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define ENERGY_PWR_UNIT_MSR 0xC0010299 298c2ecf20Sopenharmony_ci#define ENERGY_CORE_MSR 0xC001029A 308c2ecf20Sopenharmony_ci#define ENERGY_PKG_MSR 0xC001029B 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define AMD_ENERGY_UNIT_MASK 0x01F00 338c2ecf20Sopenharmony_ci#define AMD_ENERGY_MASK 0xFFFFFFFF 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct sensor_accumulator { 368c2ecf20Sopenharmony_ci u64 energy_ctr; 378c2ecf20Sopenharmony_ci u64 prev_value; 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct amd_energy_data { 418c2ecf20Sopenharmony_ci struct hwmon_channel_info energy_info; 428c2ecf20Sopenharmony_ci const struct hwmon_channel_info *info[2]; 438c2ecf20Sopenharmony_ci struct hwmon_chip_info chip; 448c2ecf20Sopenharmony_ci struct task_struct *wrap_accumulate; 458c2ecf20Sopenharmony_ci /* Lock around the accumulator */ 468c2ecf20Sopenharmony_ci struct mutex lock; 478c2ecf20Sopenharmony_ci /* An accumulator for each core and socket */ 488c2ecf20Sopenharmony_ci struct sensor_accumulator *accums; 498c2ecf20Sopenharmony_ci unsigned int timeout_ms; 508c2ecf20Sopenharmony_ci /* Energy Status Units */ 518c2ecf20Sopenharmony_ci int energy_units; 528c2ecf20Sopenharmony_ci int nr_cpus; 538c2ecf20Sopenharmony_ci int nr_socks; 548c2ecf20Sopenharmony_ci int core_id; 558c2ecf20Sopenharmony_ci char (*label)[10]; 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic int amd_energy_read_labels(struct device *dev, 598c2ecf20Sopenharmony_ci enum hwmon_sensor_types type, 608c2ecf20Sopenharmony_ci u32 attr, int channel, 618c2ecf20Sopenharmony_ci const char **str) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct amd_energy_data *data = dev_get_drvdata(dev); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci *str = data->label[channel]; 668c2ecf20Sopenharmony_ci return 0; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic void get_energy_units(struct amd_energy_data *data) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci u64 rapl_units; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci rdmsrl_safe(ENERGY_PWR_UNIT_MSR, &rapl_units); 748c2ecf20Sopenharmony_ci data->energy_units = (rapl_units & AMD_ENERGY_UNIT_MASK) >> 8; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic void accumulate_delta(struct amd_energy_data *data, 788c2ecf20Sopenharmony_ci int channel, int cpu, u32 reg) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct sensor_accumulator *accum; 818c2ecf20Sopenharmony_ci u64 input; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 848c2ecf20Sopenharmony_ci rdmsrl_safe_on_cpu(cpu, reg, &input); 858c2ecf20Sopenharmony_ci input &= AMD_ENERGY_MASK; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci accum = &data->accums[channel]; 888c2ecf20Sopenharmony_ci if (input >= accum->prev_value) 898c2ecf20Sopenharmony_ci accum->energy_ctr += 908c2ecf20Sopenharmony_ci input - accum->prev_value; 918c2ecf20Sopenharmony_ci else 928c2ecf20Sopenharmony_ci accum->energy_ctr += UINT_MAX - 938c2ecf20Sopenharmony_ci accum->prev_value + input; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci accum->prev_value = input; 968c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic void read_accumulate(struct amd_energy_data *data) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci int sock, scpu, cpu; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci for (sock = 0; sock < data->nr_socks; sock++) { 1048c2ecf20Sopenharmony_ci scpu = cpumask_first_and(cpu_online_mask, 1058c2ecf20Sopenharmony_ci cpumask_of_node(sock)); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci accumulate_delta(data, data->nr_cpus + sock, 1088c2ecf20Sopenharmony_ci scpu, ENERGY_PKG_MSR); 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (data->core_id >= data->nr_cpus) 1128c2ecf20Sopenharmony_ci data->core_id = 0; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci cpu = data->core_id; 1158c2ecf20Sopenharmony_ci if (cpu_online(cpu)) 1168c2ecf20Sopenharmony_ci accumulate_delta(data, cpu, cpu, ENERGY_CORE_MSR); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci data->core_id++; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic void amd_add_delta(struct amd_energy_data *data, int ch, 1228c2ecf20Sopenharmony_ci int cpu, long *val, u32 reg) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct sensor_accumulator *accum; 1258c2ecf20Sopenharmony_ci u64 input; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 1288c2ecf20Sopenharmony_ci rdmsrl_safe_on_cpu(cpu, reg, &input); 1298c2ecf20Sopenharmony_ci input &= AMD_ENERGY_MASK; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci accum = &data->accums[ch]; 1328c2ecf20Sopenharmony_ci if (input >= accum->prev_value) 1338c2ecf20Sopenharmony_ci input += accum->energy_ctr - 1348c2ecf20Sopenharmony_ci accum->prev_value; 1358c2ecf20Sopenharmony_ci else 1368c2ecf20Sopenharmony_ci input += UINT_MAX - accum->prev_value + 1378c2ecf20Sopenharmony_ci accum->energy_ctr; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* Energy consumed = (1/(2^ESU) * RAW * 1000000UL) μJoules */ 1408c2ecf20Sopenharmony_ci *val = div64_ul(input * 1000000UL, BIT(data->energy_units)); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int amd_energy_read(struct device *dev, 1468c2ecf20Sopenharmony_ci enum hwmon_sensor_types type, 1478c2ecf20Sopenharmony_ci u32 attr, int channel, long *val) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct amd_energy_data *data = dev_get_drvdata(dev); 1508c2ecf20Sopenharmony_ci u32 reg; 1518c2ecf20Sopenharmony_ci int cpu; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (channel >= data->nr_cpus) { 1548c2ecf20Sopenharmony_ci cpu = cpumask_first_and(cpu_online_mask, 1558c2ecf20Sopenharmony_ci cpumask_of_node 1568c2ecf20Sopenharmony_ci (channel - data->nr_cpus)); 1578c2ecf20Sopenharmony_ci reg = ENERGY_PKG_MSR; 1588c2ecf20Sopenharmony_ci } else { 1598c2ecf20Sopenharmony_ci cpu = channel; 1608c2ecf20Sopenharmony_ci if (!cpu_online(cpu)) 1618c2ecf20Sopenharmony_ci return -ENODEV; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci reg = ENERGY_CORE_MSR; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci amd_add_delta(data, channel, cpu, val, reg); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return 0; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic umode_t amd_energy_is_visible(const void *_data, 1718c2ecf20Sopenharmony_ci enum hwmon_sensor_types type, 1728c2ecf20Sopenharmony_ci u32 attr, int channel) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci return 0440; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic int energy_accumulator(void *p) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct amd_energy_data *data = (struct amd_energy_data *)p; 1808c2ecf20Sopenharmony_ci unsigned int timeout = data->timeout_ms; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci while (!kthread_should_stop()) { 1838c2ecf20Sopenharmony_ci /* 1848c2ecf20Sopenharmony_ci * Ignoring the conditions such as 1858c2ecf20Sopenharmony_ci * cpu being offline or rdmsr failure 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_ci read_accumulate(data); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 1908c2ecf20Sopenharmony_ci if (kthread_should_stop()) 1918c2ecf20Sopenharmony_ci break; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci schedule_timeout(msecs_to_jiffies(timeout)); 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic const struct hwmon_ops amd_energy_ops = { 1998c2ecf20Sopenharmony_ci .is_visible = amd_energy_is_visible, 2008c2ecf20Sopenharmony_ci .read = amd_energy_read, 2018c2ecf20Sopenharmony_ci .read_string = amd_energy_read_labels, 2028c2ecf20Sopenharmony_ci}; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int amd_create_sensor(struct device *dev, 2058c2ecf20Sopenharmony_ci struct amd_energy_data *data, 2068c2ecf20Sopenharmony_ci enum hwmon_sensor_types type, u32 config) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct hwmon_channel_info *info = &data->energy_info; 2098c2ecf20Sopenharmony_ci struct sensor_accumulator *accums; 2108c2ecf20Sopenharmony_ci int i, num_siblings, cpus, sockets; 2118c2ecf20Sopenharmony_ci u32 *s_config; 2128c2ecf20Sopenharmony_ci char (*label_l)[10]; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* Identify the number of siblings per core */ 2158c2ecf20Sopenharmony_ci num_siblings = ((cpuid_ebx(0x8000001e) >> 8) & 0xff) + 1; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci sockets = num_possible_nodes(); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* 2208c2ecf20Sopenharmony_ci * Energy counter register is accessed at core level. 2218c2ecf20Sopenharmony_ci * Hence, filterout the siblings. 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_ci cpus = num_present_cpus() / num_siblings; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci s_config = devm_kcalloc(dev, cpus + sockets + 1, 2268c2ecf20Sopenharmony_ci sizeof(u32), GFP_KERNEL); 2278c2ecf20Sopenharmony_ci if (!s_config) 2288c2ecf20Sopenharmony_ci return -ENOMEM; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci accums = devm_kcalloc(dev, cpus + sockets, 2318c2ecf20Sopenharmony_ci sizeof(struct sensor_accumulator), 2328c2ecf20Sopenharmony_ci GFP_KERNEL); 2338c2ecf20Sopenharmony_ci if (!accums) 2348c2ecf20Sopenharmony_ci return -ENOMEM; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci label_l = devm_kcalloc(dev, cpus + sockets, 2378c2ecf20Sopenharmony_ci sizeof(*label_l), GFP_KERNEL); 2388c2ecf20Sopenharmony_ci if (!label_l) 2398c2ecf20Sopenharmony_ci return -ENOMEM; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci info->type = type; 2428c2ecf20Sopenharmony_ci info->config = s_config; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci data->nr_cpus = cpus; 2458c2ecf20Sopenharmony_ci data->nr_socks = sockets; 2468c2ecf20Sopenharmony_ci data->accums = accums; 2478c2ecf20Sopenharmony_ci data->label = label_l; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci for (i = 0; i < cpus + sockets; i++) { 2508c2ecf20Sopenharmony_ci s_config[i] = config; 2518c2ecf20Sopenharmony_ci if (i < cpus) 2528c2ecf20Sopenharmony_ci scnprintf(label_l[i], 10, "Ecore%03u", i); 2538c2ecf20Sopenharmony_ci else 2548c2ecf20Sopenharmony_ci scnprintf(label_l[i], 10, "Esocket%u", (i - cpus)); 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci s_config[i] = 0; 2588c2ecf20Sopenharmony_ci return 0; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic int amd_energy_probe(struct platform_device *pdev) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct device *hwmon_dev; 2648c2ecf20Sopenharmony_ci struct amd_energy_data *data; 2658c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 2668c2ecf20Sopenharmony_ci int ret; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci data = devm_kzalloc(dev, 2698c2ecf20Sopenharmony_ci sizeof(struct amd_energy_data), GFP_KERNEL); 2708c2ecf20Sopenharmony_ci if (!data) 2718c2ecf20Sopenharmony_ci return -ENOMEM; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci data->chip.ops = &amd_energy_ops; 2748c2ecf20Sopenharmony_ci data->chip.info = data->info; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci dev_set_drvdata(dev, data); 2778c2ecf20Sopenharmony_ci /* Populate per-core energy reporting */ 2788c2ecf20Sopenharmony_ci data->info[0] = &data->energy_info; 2798c2ecf20Sopenharmony_ci ret = amd_create_sensor(dev, data, hwmon_energy, 2808c2ecf20Sopenharmony_ci HWMON_E_INPUT | HWMON_E_LABEL); 2818c2ecf20Sopenharmony_ci if (ret) 2828c2ecf20Sopenharmony_ci return ret; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci mutex_init(&data->lock); 2858c2ecf20Sopenharmony_ci get_energy_units(data); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_info(dev, DRVNAME, 2888c2ecf20Sopenharmony_ci data, 2898c2ecf20Sopenharmony_ci &data->chip, 2908c2ecf20Sopenharmony_ci NULL); 2918c2ecf20Sopenharmony_ci if (IS_ERR(hwmon_dev)) 2928c2ecf20Sopenharmony_ci return PTR_ERR(hwmon_dev); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* 2958c2ecf20Sopenharmony_ci * On a system with peak wattage of 250W 2968c2ecf20Sopenharmony_ci * timeout = 2 ^ 32 / 2 ^ energy_units / 250 secs 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_ci data->timeout_ms = 1000 * 2998c2ecf20Sopenharmony_ci BIT(min(28, 31 - data->energy_units)) / 250; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci data->wrap_accumulate = kthread_run(energy_accumulator, data, 3028c2ecf20Sopenharmony_ci "%s", dev_name(hwmon_dev)); 3038c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(data->wrap_accumulate); 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic int amd_energy_remove(struct platform_device *pdev) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct amd_energy_data *data = dev_get_drvdata(&pdev->dev); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (data && data->wrap_accumulate) 3118c2ecf20Sopenharmony_ci kthread_stop(data->wrap_accumulate); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci return 0; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic const struct platform_device_id amd_energy_ids[] = { 3178c2ecf20Sopenharmony_ci { .name = DRVNAME, }, 3188c2ecf20Sopenharmony_ci {} 3198c2ecf20Sopenharmony_ci}; 3208c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, amd_energy_ids); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic struct platform_driver amd_energy_driver = { 3238c2ecf20Sopenharmony_ci .probe = amd_energy_probe, 3248c2ecf20Sopenharmony_ci .remove = amd_energy_remove, 3258c2ecf20Sopenharmony_ci .id_table = amd_energy_ids, 3268c2ecf20Sopenharmony_ci .driver = { 3278c2ecf20Sopenharmony_ci .name = DRVNAME, 3288c2ecf20Sopenharmony_ci }, 3298c2ecf20Sopenharmony_ci}; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic struct platform_device *amd_energy_platdev; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic const struct x86_cpu_id cpu_ids[] __initconst = { 3348c2ecf20Sopenharmony_ci X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x17, 0x31, NULL), 3358c2ecf20Sopenharmony_ci {} 3368c2ecf20Sopenharmony_ci}; 3378c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(x86cpu, cpu_ids); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic int __init amd_energy_init(void) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci int ret; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (!x86_match_cpu(cpu_ids)) 3448c2ecf20Sopenharmony_ci return -ENODEV; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci ret = platform_driver_register(&amd_energy_driver); 3478c2ecf20Sopenharmony_ci if (ret) 3488c2ecf20Sopenharmony_ci return ret; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci amd_energy_platdev = platform_device_alloc(DRVNAME, 0); 3518c2ecf20Sopenharmony_ci if (!amd_energy_platdev) { 3528c2ecf20Sopenharmony_ci platform_driver_unregister(&amd_energy_driver); 3538c2ecf20Sopenharmony_ci return -ENOMEM; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci ret = platform_device_add(amd_energy_platdev); 3578c2ecf20Sopenharmony_ci if (ret) { 3588c2ecf20Sopenharmony_ci platform_device_put(amd_energy_platdev); 3598c2ecf20Sopenharmony_ci platform_driver_unregister(&amd_energy_driver); 3608c2ecf20Sopenharmony_ci return ret; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return ret; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic void __exit amd_energy_exit(void) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci platform_device_unregister(amd_energy_platdev); 3698c2ecf20Sopenharmony_ci platform_driver_unregister(&amd_energy_driver); 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cimodule_init(amd_energy_init); 3738c2ecf20Sopenharmony_cimodule_exit(amd_energy_exit); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for AMD Energy reporting from RAPL MSR via HWMON interface"); 3768c2ecf20Sopenharmony_ciMODULE_AUTHOR("Naveen Krishna Chatradhi <nchatrad@amd.com>"); 3778c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 378