1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright © 2022 Intel Corporation 4 */ 5 6 #include "xe_guc_hwconfig.h" 7 8 #include <drm/drm_managed.h> 9 #include <drm/drm_print.h> 10 11 #include "abi/guc_actions_abi.h" 12 #include "xe_bo.h" 13 #include "xe_device.h" 14 #include "xe_gt.h" 15 #include "xe_guc.h" 16 #include "xe_map.h" 17 18 static int send_get_hwconfig(struct xe_guc *guc, u64 ggtt_addr, u32 size) 19 { 20 u32 action[] = { 21 XE_GUC_ACTION_GET_HWCONFIG, 22 lower_32_bits(ggtt_addr), 23 upper_32_bits(ggtt_addr), 24 size, 25 }; 26 27 return xe_guc_mmio_send(guc, action, ARRAY_SIZE(action)); 28 } 29 30 static int guc_hwconfig_size(struct xe_guc *guc, u32 *size) 31 { 32 int ret = send_get_hwconfig(guc, 0, 0); 33 34 if (ret < 0) 35 return ret; 36 37 *size = ret; 38 return 0; 39 } 40 41 static int guc_hwconfig_copy(struct xe_guc *guc) 42 { 43 int ret = send_get_hwconfig(guc, xe_bo_ggtt_addr(guc->hwconfig.bo), 44 guc->hwconfig.size); 45 46 if (ret < 0) 47 return ret; 48 49 return 0; 50 } 51 52 int xe_guc_hwconfig_init(struct xe_guc *guc) 53 { 54 struct xe_device *xe = guc_to_xe(guc); 55 struct xe_gt *gt = guc_to_gt(guc); 56 struct xe_tile *tile = gt_to_tile(gt); 57 struct xe_bo *bo; 58 u32 size; 59 int err; 60 61 /* Initialization already done */ 62 if (guc->hwconfig.bo) 63 return 0; 64 65 /* 66 * All hwconfig the same across GTs so only GT0 needs to be configured 67 */ 68 if (gt->info.id != XE_GT0) 69 return 0; 70 71 /* ADL_P, DG2+ supports hwconfig table */ 72 if (GRAPHICS_VERx100(xe) < 1255 && xe->info.platform != XE_ALDERLAKE_P) 73 return 0; 74 75 err = guc_hwconfig_size(guc, &size); 76 if (err) 77 return err; 78 if (!size) 79 return -EINVAL; 80 81 bo = xe_managed_bo_create_pin_map(xe, tile, PAGE_ALIGN(size), 82 XE_BO_FLAG_SYSTEM | 83 XE_BO_FLAG_GGTT | 84 XE_BO_FLAG_GGTT_INVALIDATE); 85 if (IS_ERR(bo)) 86 return PTR_ERR(bo); 87 guc->hwconfig.bo = bo; 88 guc->hwconfig.size = size; 89 90 return guc_hwconfig_copy(guc); 91 } 92 93 u32 xe_guc_hwconfig_size(struct xe_guc *guc) 94 { 95 return !guc->hwconfig.bo ? 0 : guc->hwconfig.size; 96 } 97 98 void xe_guc_hwconfig_copy(struct xe_guc *guc, void *dst) 99 { 100 struct xe_device *xe = guc_to_xe(guc); 101 102 XE_WARN_ON(!guc->hwconfig.bo); 103 104 xe_map_memcpy_from(xe, dst, &guc->hwconfig.bo->vmap, 0, 105 guc->hwconfig.size); 106 } 107 108 void xe_guc_hwconfig_dump(struct xe_guc *guc, struct drm_printer *p) 109 { 110 size_t size = xe_guc_hwconfig_size(guc); 111 u32 *hwconfig; 112 u64 num_dw; 113 u32 extra_bytes; 114 int i = 0; 115 116 if (size == 0) { 117 drm_printf(p, "No hwconfig available\n"); 118 return; 119 } 120 121 num_dw = div_u64_rem(size, sizeof(u32), &extra_bytes); 122 123 hwconfig = kzalloc(size, GFP_KERNEL); 124 if (!hwconfig) { 125 drm_printf(p, "Error: could not allocate hwconfig memory\n"); 126 return; 127 } 128 129 xe_guc_hwconfig_copy(guc, hwconfig); 130 131 /* An entry requires at least three dwords for key, length, value */ 132 while (i + 3 <= num_dw) { 133 u32 attribute = hwconfig[i++]; 134 u32 len_dw = hwconfig[i++]; 135 136 if (i + len_dw > num_dw) { 137 drm_printf(p, "Error: Attribute %u is %u dwords, but only %llu remain\n", 138 attribute, len_dw, num_dw - i); 139 len_dw = num_dw - i; 140 } 141 142 /* 143 * If it's a single dword (as most hwconfig attributes are), 144 * then it's probably a number that makes sense to display 145 * in decimal form. In the rare cases where it's more than 146 * one dword, just print it in hex form and let the user 147 * figure out how to interpret it. 148 */ 149 if (len_dw == 1) 150 drm_printf(p, "[%2u] = %u\n", attribute, hwconfig[i]); 151 else 152 drm_printf(p, "[%2u] = { %*ph }\n", attribute, 153 (int)(len_dw * sizeof(u32)), &hwconfig[i]); 154 i += len_dw; 155 } 156 157 if (i < num_dw || extra_bytes) 158 drm_printf(p, "Error: %llu extra bytes at end of hwconfig\n", 159 (num_dw - i) * sizeof(u32) + extra_bytes); 160 161 kfree(hwconfig); 162 } 163 164 /* 165 * Lookup a specific 32-bit attribute value in the GuC's hwconfig table. 166 */ 167 int xe_guc_hwconfig_lookup_u32(struct xe_guc *guc, u32 attribute, u32 *val) 168 { 169 size_t size = xe_guc_hwconfig_size(guc); 170 u64 num_dw = div_u64(size, sizeof(u32)); 171 u32 *hwconfig; 172 bool found = false; 173 int i = 0; 174 175 if (num_dw == 0) 176 return -EINVAL; 177 178 hwconfig = kzalloc(size, GFP_KERNEL); 179 if (!hwconfig) 180 return -ENOMEM; 181 182 xe_guc_hwconfig_copy(guc, hwconfig); 183 184 /* An entry requires at least three dwords for key, length, value */ 185 while (i + 3 <= num_dw) { 186 u32 key = hwconfig[i++]; 187 u32 len_dw = hwconfig[i++]; 188 189 if (key != attribute) { 190 i += len_dw; 191 continue; 192 } 193 194 *val = hwconfig[i]; 195 found = true; 196 break; 197 } 198 199 kfree(hwconfig); 200 201 return found ? 0 : -ENOENT; 202 } 203