Merge tag 'kvm-x86-misc-6.9' of https://github.com/kvm-x86/linux into HEAD
[linux-2.6-block.git] / drivers / gpu / drm / xe / xe_gt_idle.c
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2023 Intel Corporation
4  */
5
6 #include <drm/drm_managed.h>
7
8 #include "xe_device.h"
9 #include "xe_gt.h"
10 #include "xe_gt_idle.h"
11 #include "xe_gt_sysfs.h"
12 #include "xe_guc_pc.h"
13 #include "regs/xe_gt_regs.h"
14 #include "xe_mmio.h"
15
16 /**
17  * DOC: Xe GT Idle
18  *
19  * Contains functions that init GT idle features like C6
20  *
21  * device/gt#/gtidle/name - name of the state
22  * device/gt#/gtidle/idle_residency_ms - Provides residency of the idle state in ms
23  * device/gt#/gtidle/idle_status - Provides current idle state
24  */
25
26 static struct xe_gt_idle *dev_to_gtidle(struct device *dev)
27 {
28         struct kobject *kobj = &dev->kobj;
29
30         return &kobj_to_gt(kobj->parent)->gtidle;
31 }
32
33 static struct xe_gt *gtidle_to_gt(struct xe_gt_idle *gtidle)
34 {
35         return container_of(gtidle, struct xe_gt, gtidle);
36 }
37
38 static struct xe_guc_pc *gtidle_to_pc(struct xe_gt_idle *gtidle)
39 {
40         return &gtidle_to_gt(gtidle)->uc.guc.pc;
41 }
42
43 static const char *gt_idle_state_to_string(enum xe_gt_idle_state state)
44 {
45         switch (state) {
46         case GT_IDLE_C0:
47                 return "gt-c0";
48         case GT_IDLE_C6:
49                 return "gt-c6";
50         default:
51                 return "unknown";
52         }
53 }
54
55 static u64 get_residency_ms(struct xe_gt_idle *gtidle, u64 cur_residency)
56 {
57         u64 delta, overflow_residency, prev_residency;
58
59         overflow_residency = BIT_ULL(32);
60
61         /*
62          * Counter wrap handling
63          * Store previous hw counter values for counter wrap-around handling
64          * Relying on sufficient frequency of queries otherwise counters can still wrap.
65          */
66         prev_residency = gtidle->prev_residency;
67         gtidle->prev_residency = cur_residency;
68
69         /* delta */
70         if (cur_residency >= prev_residency)
71                 delta = cur_residency - prev_residency;
72         else
73                 delta = cur_residency + (overflow_residency - prev_residency);
74
75         /* Add delta to extended raw driver copy of idle residency */
76         cur_residency = gtidle->cur_residency + delta;
77         gtidle->cur_residency = cur_residency;
78
79         /* residency multiplier in ns, convert to ms */
80         cur_residency = mul_u64_u32_div(cur_residency, gtidle->residency_multiplier, 1e6);
81
82         return cur_residency;
83 }
84
85 static ssize_t name_show(struct device *dev,
86                          struct device_attribute *attr, char *buff)
87 {
88         struct xe_gt_idle *gtidle = dev_to_gtidle(dev);
89
90         return sysfs_emit(buff, "%s\n", gtidle->name);
91 }
92 static DEVICE_ATTR_RO(name);
93
94 static ssize_t idle_status_show(struct device *dev,
95                                 struct device_attribute *attr, char *buff)
96 {
97         struct xe_gt_idle *gtidle = dev_to_gtidle(dev);
98         struct xe_guc_pc *pc = gtidle_to_pc(gtidle);
99         enum xe_gt_idle_state state;
100
101         state = gtidle->idle_status(pc);
102
103         return sysfs_emit(buff, "%s\n", gt_idle_state_to_string(state));
104 }
105 static DEVICE_ATTR_RO(idle_status);
106
107 static ssize_t idle_residency_ms_show(struct device *dev,
108                                       struct device_attribute *attr, char *buff)
109 {
110         struct xe_gt_idle *gtidle = dev_to_gtidle(dev);
111         struct xe_guc_pc *pc = gtidle_to_pc(gtidle);
112         u64 residency;
113
114         residency = gtidle->idle_residency(pc);
115         return sysfs_emit(buff, "%llu\n", get_residency_ms(gtidle, residency));
116 }
117 static DEVICE_ATTR_RO(idle_residency_ms);
118
119 static const struct attribute *gt_idle_attrs[] = {
120         &dev_attr_name.attr,
121         &dev_attr_idle_status.attr,
122         &dev_attr_idle_residency_ms.attr,
123         NULL,
124 };
125
126 static void gt_idle_sysfs_fini(struct drm_device *drm, void *arg)
127 {
128         struct kobject *kobj = arg;
129
130         sysfs_remove_files(kobj, gt_idle_attrs);
131         kobject_put(kobj);
132 }
133
134 void xe_gt_idle_sysfs_init(struct xe_gt_idle *gtidle)
135 {
136         struct xe_gt *gt = gtidle_to_gt(gtidle);
137         struct xe_device *xe = gt_to_xe(gt);
138         struct kobject *kobj;
139         int err;
140
141         kobj = kobject_create_and_add("gtidle", gt->sysfs);
142         if (!kobj) {
143                 drm_warn(&xe->drm, "%s failed, err: %d\n", __func__, -ENOMEM);
144                 return;
145         }
146
147         if (xe_gt_is_media_type(gt)) {
148                 sprintf(gtidle->name, "gt%d-mc", gt->info.id);
149                 gtidle->idle_residency = xe_guc_pc_mc6_residency;
150         } else {
151                 sprintf(gtidle->name, "gt%d-rc", gt->info.id);
152                 gtidle->idle_residency = xe_guc_pc_rc6_residency;
153         }
154
155         /* Multiplier for Residency counter in units of 1.28us */
156         gtidle->residency_multiplier = 1280;
157         gtidle->idle_status = xe_guc_pc_c_status;
158
159         err = sysfs_create_files(kobj, gt_idle_attrs);
160         if (err) {
161                 kobject_put(kobj);
162                 drm_warn(&xe->drm, "failed to register gtidle sysfs, err: %d\n", err);
163                 return;
164         }
165
166         err = drmm_add_action_or_reset(&xe->drm, gt_idle_sysfs_fini, kobj);
167         if (err)
168                 drm_warn(&xe->drm, "%s: drmm_add_action_or_reset failed, err: %d\n",
169                          __func__, err);
170 }
171
172 void xe_gt_idle_enable_c6(struct xe_gt *gt)
173 {
174         xe_device_assert_mem_access(gt_to_xe(gt));
175         xe_force_wake_assert_held(gt_to_fw(gt), XE_FW_GT);
176
177         /* Units of 1280 ns for a total of 5s */
178         xe_mmio_write32(gt, RC_IDLE_HYSTERSIS, 0x3B9ACA);
179         /* Enable RC6 */
180         xe_mmio_write32(gt, RC_CONTROL,
181                         RC_CTL_HW_ENABLE | RC_CTL_TO_MODE | RC_CTL_RC6_ENABLE);
182 }
183
184 void xe_gt_idle_disable_c6(struct xe_gt *gt)
185 {
186         xe_device_assert_mem_access(gt_to_xe(gt));
187         xe_force_wake_assert_held(gt_to_fw(gt), XE_FORCEWAKE_ALL);
188
189         xe_mmio_write32(gt, PG_ENABLE, 0);
190         xe_mmio_write32(gt, RC_CONTROL, 0);
191         xe_mmio_write32(gt, RC_STATE, 0);
192 }