summaryrefslogtreecommitdiffstats
path: root/drivers/accel/ivpu/ivpu_sysfs.c
blob: 268ab7744a8bbb04dd5f4d8e20608fc7786ac4cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2024-2025 Intel Corporation
 */

#include <linux/device.h>
#include <linux/err.h>
#include <linux/pm_runtime.h>
#include <linux/units.h>

#include "ivpu_drv.h"
#include "ivpu_gem.h"
#include "ivpu_fw.h"
#include "ivpu_hw.h"
#include "ivpu_sysfs.h"

/**
 * DOC: npu_busy_time_us
 *
 * npu_busy_time_us is the time that the device spent executing jobs.
 * The time is counted when and only when there are jobs submitted to firmware.
 *
 * This time can be used to measure the utilization of NPU, either by calculating
 * npu_busy_time_us difference between two timepoints (i.e. measuring the time
 * that the NPU was active during some workload) or monitoring utilization percentage
 * by reading npu_busy_time_us periodically.
 *
 * When reading the value periodically, it shouldn't be read too often as it may have
 * an impact on job submission performance. Recommended period is 1 second.
 */
static ssize_t
npu_busy_time_us_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct drm_device *drm = dev_get_drvdata(dev);
	struct ivpu_device *vdev = to_ivpu_device(drm);
	ktime_t total, now = 0;

	mutex_lock(&vdev->submitted_jobs_lock);

	total = vdev->busy_time;
	if (!xa_empty(&vdev->submitted_jobs_xa))
		now = ktime_sub(ktime_get(), vdev->busy_start_ts);
	mutex_unlock(&vdev->submitted_jobs_lock);

	return sysfs_emit(buf, "%lld\n", ktime_to_us(ktime_add(total, now)));
}

static DEVICE_ATTR_RO(npu_busy_time_us);

/**
 * DOC: npu_memory_utilization
 *
 * The npu_memory_utilization is used to report in bytes a current NPU memory utilization.
 *
 */
static ssize_t
npu_memory_utilization_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct drm_device *drm = dev_get_drvdata(dev);
	struct ivpu_device *vdev = to_ivpu_device(drm);
	struct ivpu_bo *bo;
	u64 total_npu_memory = 0;

	mutex_lock(&vdev->bo_list_lock);
	list_for_each_entry(bo, &vdev->bo_list, bo_list_node)
		total_npu_memory += bo->base.base.size;
	mutex_unlock(&vdev->bo_list_lock);

	return sysfs_emit(buf, "%lld\n", total_npu_memory);
}

static DEVICE_ATTR_RO(npu_memory_utilization);

/**
 * DOC: sched_mode
 *
 * The sched_mode is used to report current NPU scheduling mode.
 *
 * It returns following strings:
 * - "HW"		- Hardware Scheduler mode
 * - "OS"		- Operating System Scheduler mode
 *
 */
static ssize_t
sched_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct drm_device *drm = dev_get_drvdata(dev);
	struct ivpu_device *vdev = to_ivpu_device(drm);

	return sysfs_emit(buf, "%s\n", vdev->fw->sched_mode ? "HW" : "OS");
}

static DEVICE_ATTR_RO(sched_mode);

/**
 * DOC: npu_max_frequency
 *
 * The npu_max_frequency shows maximum frequency in MHz of the NPU's data
 * processing unit
 */
static ssize_t
npu_max_frequency_mhz_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct drm_device *drm = dev_get_drvdata(dev);
	struct ivpu_device *vdev = to_ivpu_device(drm);
	u32 freq = ivpu_hw_dpu_max_freq_get(vdev);

	return sysfs_emit(buf, "%lu\n", freq / HZ_PER_MHZ);
}

static DEVICE_ATTR_RO(npu_max_frequency_mhz);

/**
 * DOC: npu_current_frequency_mhz
 *
 * The npu_current_frequency_mhz shows current frequency in MHz of the NPU's
 * data processing unit
 */
static ssize_t
npu_current_frequency_mhz_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct drm_device *drm = dev_get_drvdata(dev);
	struct ivpu_device *vdev = to_ivpu_device(drm);
	u32 freq = 0;

	/* Read frequency only if device is active, otherwise frequency is 0 */
	if (pm_runtime_get_if_active(vdev->drm.dev) > 0) {
		freq = ivpu_hw_dpu_freq_get(vdev);

		pm_runtime_put_autosuspend(vdev->drm.dev);
	}

	return sysfs_emit(buf, "%lu\n", freq / HZ_PER_MHZ);
}

static DEVICE_ATTR_RO(npu_current_frequency_mhz);

static struct attribute *ivpu_dev_attrs[] = {
	&dev_attr_npu_busy_time_us.attr,
	&dev_attr_npu_memory_utilization.attr,
	&dev_attr_sched_mode.attr,
	&dev_attr_npu_max_frequency_mhz.attr,
	&dev_attr_npu_current_frequency_mhz.attr,
	NULL,
};

static struct attribute_group ivpu_dev_attr_group = {
	.attrs = ivpu_dev_attrs,
};

void ivpu_sysfs_init(struct ivpu_device *vdev)
{
	int ret;

	ret = devm_device_add_group(vdev->drm.dev, &ivpu_dev_attr_group);
	if (ret)
		ivpu_warn(vdev, "Failed to add group to device, ret %d", ret);
}