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