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