Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.17.
  1// SPDX-License-Identifier: GPL-2.0-only
  2
  3/*
  4 * Copyright (C) 2020 Advanced Micro Devices, Inc.
  5 */
  6#include <asm/cpu_device_id.h>
  7
  8#include <linux/bits.h>
  9#include <linux/cpu.h>
 10#include <linux/cpumask.h>
 11#include <linux/delay.h>
 12#include <linux/device.h>
 13#include <linux/hwmon.h>
 14#include <linux/kernel.h>
 15#include <linux/kthread.h>
 16#include <linux/list.h>
 17#include <linux/module.h>
 18#include <linux/mutex.h>
 19#include <linux/processor.h>
 20#include <linux/platform_device.h>
 21#include <linux/sched.h>
 22#include <linux/slab.h>
 23#include <linux/topology.h>
 24#include <linux/types.h>
 25
 26#define DRVNAME			"amd_energy"
 27
 28#define ENERGY_PWR_UNIT_MSR	0xC0010299
 29#define ENERGY_CORE_MSR		0xC001029A
 30#define ENERGY_PKG_MSR		0xC001029B
 31
 32#define AMD_ENERGY_UNIT_MASK	0x01F00
 33#define AMD_ENERGY_MASK		0xFFFFFFFF
 34
 35struct sensor_accumulator {
 36	u64 energy_ctr;
 37	u64 prev_value;
 38	char label[10];
 39};
 40
 41struct amd_energy_data {
 42	struct hwmon_channel_info energy_info;
 43	const struct hwmon_channel_info *info[2];
 44	struct hwmon_chip_info chip;
 45	struct task_struct *wrap_accumulate;
 46	/* Lock around the accumulator */
 47	struct mutex lock;
 48	/* An accumulator for each core and socket */
 49	struct sensor_accumulator *accums;
 50	/* Energy Status Units */
 51	u64 energy_units;
 52	int nr_cpus;
 53	int nr_socks;
 54	int core_id;
 55};
 56
 57static int amd_energy_read_labels(struct device *dev,
 58				  enum hwmon_sensor_types type,
 59				  u32 attr, int channel,
 60				  const char **str)
 61{
 62	struct amd_energy_data *data = dev_get_drvdata(dev);
 63
 64	*str = data->accums[channel].label;
 65	return 0;
 66}
 67
 68static void get_energy_units(struct amd_energy_data *data)
 69{
 70	u64 rapl_units;
 71
 72	rdmsrl_safe(ENERGY_PWR_UNIT_MSR, &rapl_units);
 73	data->energy_units = (rapl_units & AMD_ENERGY_UNIT_MASK) >> 8;
 74}
 75
 76static void accumulate_socket_delta(struct amd_energy_data *data,
 77				    int sock, int cpu)
 78{
 79	struct sensor_accumulator *s_accum;
 80	u64 input;
 81
 82	mutex_lock(&data->lock);
 83	rdmsrl_safe_on_cpu(cpu, ENERGY_PKG_MSR, &input);
 84	input &= AMD_ENERGY_MASK;
 85
 86	s_accum = &data->accums[data->nr_cpus + sock];
 87	if (input >= s_accum->prev_value)
 88		s_accum->energy_ctr +=
 89			input - s_accum->prev_value;
 90	else
 91		s_accum->energy_ctr += UINT_MAX -
 92			s_accum->prev_value + input;
 93
 94	s_accum->prev_value = input;
 95	mutex_unlock(&data->lock);
 96}
 97
 98static void accumulate_core_delta(struct amd_energy_data *data)
 99{
100	struct sensor_accumulator *c_accum;
101	u64 input;
102	int cpu;
103
104	mutex_lock(&data->lock);
105	if (data->core_id >= data->nr_cpus)
106		data->core_id = 0;
107
108	cpu = data->core_id;
109
110	if (!cpu_online(cpu))
111		goto out;
112
113	rdmsrl_safe_on_cpu(cpu, ENERGY_CORE_MSR, &input);
114	input &= AMD_ENERGY_MASK;
115
116	c_accum = &data->accums[cpu];
117
118	if (input >= c_accum->prev_value)
119		c_accum->energy_ctr +=
120			input - c_accum->prev_value;
121	else
122		c_accum->energy_ctr += UINT_MAX -
123			c_accum->prev_value + input;
124
125	c_accum->prev_value = input;
126
127out:
128	data->core_id++;
129	mutex_unlock(&data->lock);
130}
131
132static void read_accumulate(struct amd_energy_data *data)
133{
134	int sock;
135
136	for (sock = 0; sock < data->nr_socks; sock++) {
137		int cpu;
138
139		cpu = cpumask_first_and(cpu_online_mask,
140					cpumask_of_node(sock));
141
142		accumulate_socket_delta(data, sock, cpu);
143	}
144
145	accumulate_core_delta(data);
146}
147
148static void amd_add_delta(struct amd_energy_data *data, int ch,
149			  int cpu, long *val, bool is_core)
150{
151	struct sensor_accumulator *s_accum, *c_accum;
152	u64 input;
153
154	mutex_lock(&data->lock);
155	if (!is_core) {
156		rdmsrl_safe_on_cpu(cpu, ENERGY_PKG_MSR, &input);
157		input &= AMD_ENERGY_MASK;
158
159		s_accum = &data->accums[ch];
160		if (input >= s_accum->prev_value)
161			input += s_accum->energy_ctr -
162				  s_accum->prev_value;
163		else
164			input += UINT_MAX - s_accum->prev_value +
165				  s_accum->energy_ctr;
166	} else {
167		rdmsrl_safe_on_cpu(cpu, ENERGY_CORE_MSR, &input);
168		input &= AMD_ENERGY_MASK;
169
170		c_accum = &data->accums[ch];
171		if (input >= c_accum->prev_value)
172			input += c_accum->energy_ctr -
173				 c_accum->prev_value;
174		else
175			input += UINT_MAX - c_accum->prev_value +
176				 c_accum->energy_ctr;
177	}
178
179	/* Energy consumed = (1/(2^ESU) * RAW * 1000000UL) μJoules */
180	*val = div64_ul(input * 1000000UL, BIT(data->energy_units));
181
182	mutex_unlock(&data->lock);
183}
184
185static int amd_energy_read(struct device *dev,
186			   enum hwmon_sensor_types type,
187			   u32 attr, int channel, long *val)
188{
189	struct amd_energy_data *data = dev_get_drvdata(dev);
190	int cpu;
191
192	if (channel >= data->nr_cpus) {
193		cpu = cpumask_first_and(cpu_online_mask,
194					cpumask_of_node
195					(channel - data->nr_cpus));
196		amd_add_delta(data, channel, cpu, val, false);
197	} else {
198		cpu = channel;
199		if (!cpu_online(cpu))
200			return -ENODEV;
201
202		amd_add_delta(data, channel, cpu, val, true);
203	}
204
205	return 0;
206}
207
208static umode_t amd_energy_is_visible(const void *_data,
209				     enum hwmon_sensor_types type,
210				     u32 attr, int channel)
211{
212	return 0444;
213}
214
215static int energy_accumulator(void *p)
216{
217	struct amd_energy_data *data = (struct amd_energy_data *)p;
218
219	while (!kthread_should_stop()) {
220		/*
221		 * Ignoring the conditions such as
222		 * cpu being offline or rdmsr failure
223		 */
224		read_accumulate(data);
225
226		set_current_state(TASK_INTERRUPTIBLE);
227		if (kthread_should_stop())
228			break;
229
230		/*
231		 * On a 240W system, with default resolution the
232		 * Socket Energy status register may wrap around in
233		 * 2^32*15.3 e-6/240 = 273.8041 secs (~4.5 mins)
234		 *
235		 * let us accumulate for every 100secs
236		 */
237		schedule_timeout(msecs_to_jiffies(100000));
238	}
239	return 0;
240}
241
242static const struct hwmon_ops amd_energy_ops = {
243	.is_visible = amd_energy_is_visible,
244	.read = amd_energy_read,
245	.read_string = amd_energy_read_labels,
246};
247
248static int amd_create_sensor(struct device *dev,
249			     struct amd_energy_data *data,
250			     u8 type, u32 config)
251{
252	struct hwmon_channel_info *info = &data->energy_info;
253	struct sensor_accumulator *accums;
254	int i, num_siblings, cpus, sockets;
255	u32 *s_config;
256
257	/* Identify the number of siblings per core */
258	num_siblings = ((cpuid_ebx(0x8000001e) >> 8) & 0xff) + 1;
259
260	sockets = num_possible_nodes();
261
262	/*
263	 * Energy counter register is accessed at core level.
264	 * Hence, filterout the siblings.
265	 */
266	cpus = num_present_cpus() / num_siblings;
267
268	s_config = devm_kcalloc(dev, cpus + sockets,
269				sizeof(u32), GFP_KERNEL);
270	if (!s_config)
271		return -ENOMEM;
272
273	accums = devm_kcalloc(dev, cpus + sockets,
274			      sizeof(struct sensor_accumulator),
275			      GFP_KERNEL);
276	if (!accums)
277		return -ENOMEM;
278
279	info->type = type;
280	info->config = s_config;
281
282	data->nr_cpus = cpus;
283	data->nr_socks = sockets;
284	data->accums = accums;
285
286	for (i = 0; i < cpus + sockets; i++) {
287		s_config[i] = config;
288		if (i < cpus)
289			scnprintf(accums[i].label, 10,
290				  "Ecore%03u", i);
291		else
292			scnprintf(accums[i].label, 10,
293				  "Esocket%u", (i - cpus));
294	}
295
296	return 0;
297}
298
299static int amd_energy_probe(struct platform_device *pdev)
300{
301	struct device *hwmon_dev;
302	struct amd_energy_data *data;
303	struct device *dev = &pdev->dev;
304
305	data = devm_kzalloc(dev,
306			    sizeof(struct amd_energy_data), GFP_KERNEL);
307	if (!data)
308		return -ENOMEM;
309
310	data->chip.ops = &amd_energy_ops;
311	data->chip.info = data->info;
312
313	dev_set_drvdata(dev, data);
314	/* Populate per-core energy reporting */
315	data->info[0] = &data->energy_info;
316	amd_create_sensor(dev, data, hwmon_energy,
317			  HWMON_E_INPUT | HWMON_E_LABEL);
318
319	mutex_init(&data->lock);
320	get_energy_units(data);
321
322	hwmon_dev = devm_hwmon_device_register_with_info(dev, DRVNAME,
323							 data,
324							 &data->chip,
325							 NULL);
326	if (IS_ERR(hwmon_dev))
327		return PTR_ERR(hwmon_dev);
328
329	data->wrap_accumulate = kthread_run(energy_accumulator, data,
330					    "%s", dev_name(hwmon_dev));
331	if (IS_ERR(data->wrap_accumulate))
332		return PTR_ERR(data->wrap_accumulate);
333
334	return PTR_ERR_OR_ZERO(data->wrap_accumulate);
335}
336
337static int amd_energy_remove(struct platform_device *pdev)
338{
339	struct amd_energy_data *data = dev_get_drvdata(&pdev->dev);
340
341	if (data && data->wrap_accumulate)
342		kthread_stop(data->wrap_accumulate);
343
344	return 0;
345}
346
347static const struct platform_device_id amd_energy_ids[] = {
348	{ .name = DRVNAME, },
349	{}
350};
351MODULE_DEVICE_TABLE(platform, amd_energy_ids);
352
353static struct platform_driver amd_energy_driver = {
354	.probe = amd_energy_probe,
355	.remove	= amd_energy_remove,
356	.id_table = amd_energy_ids,
357	.driver = {
358		.name = DRVNAME,
359	},
360};
361
362static struct platform_device *amd_energy_platdev;
363
364static const struct x86_cpu_id cpu_ids[] __initconst = {
365	X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x17, 0x31, NULL),
366	{}
367};
368MODULE_DEVICE_TABLE(x86cpu, cpu_ids);
369
370static int __init amd_energy_init(void)
371{
372	int ret;
373
374	if (!x86_match_cpu(cpu_ids))
375		return -ENODEV;
376
377	ret = platform_driver_register(&amd_energy_driver);
378	if (ret)
379		return ret;
380
381	amd_energy_platdev = platform_device_alloc(DRVNAME, 0);
382	if (!amd_energy_platdev) {
383		platform_driver_unregister(&amd_energy_driver);
384		return -ENOMEM;
385	}
386
387	ret = platform_device_add(amd_energy_platdev);
388	if (ret) {
389		platform_device_put(amd_energy_platdev);
390		platform_driver_unregister(&amd_energy_driver);
391		return ret;
392	}
393
394	return ret;
395}
396
397static void __exit amd_energy_exit(void)
398{
399	platform_device_unregister(amd_energy_platdev);
400	platform_driver_unregister(&amd_energy_driver);
401}
402
403module_init(amd_energy_init);
404module_exit(amd_energy_exit);
405
406MODULE_DESCRIPTION("Driver for AMD Energy reporting from RAPL MSR via HWMON interface");
407MODULE_AUTHOR("Naveen Krishna Chatradhi <nchatrad@amd.com>");
408MODULE_LICENSE("GPL");