1 // SPDX-License-Identifier: MIT
2 /*
3 * Copyright © 2022 Intel Corporation
4 */
5
6 #include "gt/intel_gt.h"
7 #include "gt/intel_hwconfig.h"
8 #include "i915_drv.h"
9 #include "i915_memcpy.h"
10 #include "intel_guc_print.h"
11
12 /*
13 * GuC has a blob containing hardware configuration information (HWConfig).
14 * This is formatted as a simple and flexible KLV (Key/Length/Value) table.
15 *
16 * For example, a minimal version could be:
17 * enum device_attr {
18 * ATTR_SOME_VALUE = 0,
19 * ATTR_SOME_MASK = 1,
20 * };
21 *
22 * static const u32 hwconfig[] = {
23 * ATTR_SOME_VALUE,
24 * 1, // Value Length in DWords
25 * 8, // Value
26 *
27 * ATTR_SOME_MASK,
28 * 3,
29 * 0x00FFFFFFFF, 0xFFFFFFFF, 0xFF000000,
30 * };
31 *
32 * The attribute ids are defined in a hardware spec.
33 */
34
__guc_action_get_hwconfig(struct intel_guc * guc,u32 ggtt_offset,u32 ggtt_size)35 static int __guc_action_get_hwconfig(struct intel_guc *guc,
36 u32 ggtt_offset, u32 ggtt_size)
37 {
38 u32 action[] = {
39 INTEL_GUC_ACTION_GET_HWCONFIG,
40 lower_32_bits(ggtt_offset),
41 upper_32_bits(ggtt_offset),
42 ggtt_size,
43 };
44 int ret;
45
46 guc_dbg(guc, "Querying HW config table: size = %d, offset = 0x%08X\n",
47 ggtt_size, ggtt_offset);
48 ret = intel_guc_send_mmio(guc, action, ARRAY_SIZE(action), NULL, 0);
49 if (ret == -ENXIO)
50 return -ENOENT;
51
52 return ret;
53 }
54
guc_hwconfig_discover_size(struct intel_guc * guc,struct intel_hwconfig * hwconfig)55 static int guc_hwconfig_discover_size(struct intel_guc *guc, struct intel_hwconfig *hwconfig)
56 {
57 int ret;
58
59 /*
60 * Sending a query with zero offset and size will return the
61 * size of the blob.
62 */
63 ret = __guc_action_get_hwconfig(guc, 0, 0);
64 if (ret < 0)
65 return ret;
66
67 if (ret == 0)
68 return -EINVAL;
69
70 hwconfig->size = ret;
71 return 0;
72 }
73
guc_hwconfig_fill_buffer(struct intel_guc * guc,struct intel_hwconfig * hwconfig)74 static int guc_hwconfig_fill_buffer(struct intel_guc *guc, struct intel_hwconfig *hwconfig)
75 {
76 struct i915_vma *vma;
77 u32 ggtt_offset;
78 void *vaddr;
79 int ret;
80
81 GEM_BUG_ON(!hwconfig->size);
82
83 ret = intel_guc_allocate_and_map_vma(guc, hwconfig->size, &vma, &vaddr);
84 if (ret)
85 return ret;
86
87 ggtt_offset = intel_guc_ggtt_offset(guc, vma);
88
89 ret = __guc_action_get_hwconfig(guc, ggtt_offset, hwconfig->size);
90 if (ret >= 0)
91 memcpy(hwconfig->ptr, vaddr, hwconfig->size);
92
93 i915_vma_unpin_and_release(&vma, I915_VMA_RELEASE_MAP);
94
95 return ret;
96 }
97
has_table(struct drm_i915_private * i915)98 static bool has_table(struct drm_i915_private *i915)
99 {
100 if (IS_ALDERLAKE_P(i915) && !IS_ALDERLAKE_P_N(i915))
101 return true;
102 if (GRAPHICS_VER_FULL(i915) >= IP_VER(12, 55))
103 return true;
104
105 return false;
106 }
107
108 /*
109 * intel_guc_hwconfig_init - Initialize the HWConfig
110 *
111 * Retrieve the HWConfig table from the GuC and save it locally.
112 * It can then be queried on demand by other users later on.
113 */
guc_hwconfig_init(struct intel_gt * gt)114 static int guc_hwconfig_init(struct intel_gt *gt)
115 {
116 struct intel_hwconfig *hwconfig = >->info.hwconfig;
117 struct intel_guc *guc = gt_to_guc(gt);
118 int ret;
119
120 if (!has_table(gt->i915))
121 return 0;
122
123 ret = guc_hwconfig_discover_size(guc, hwconfig);
124 if (ret)
125 return ret;
126
127 hwconfig->ptr = kmalloc(hwconfig->size, GFP_KERNEL);
128 if (!hwconfig->ptr) {
129 hwconfig->size = 0;
130 return -ENOMEM;
131 }
132
133 ret = guc_hwconfig_fill_buffer(guc, hwconfig);
134 if (ret < 0) {
135 intel_gt_fini_hwconfig(gt);
136 return ret;
137 }
138
139 return 0;
140 }
141
142 /*
143 * intel_gt_init_hwconfig - Initialize the HWConfig if available
144 *
145 * Retrieve the HWConfig table if available on the current platform.
146 */
intel_gt_init_hwconfig(struct intel_gt * gt)147 int intel_gt_init_hwconfig(struct intel_gt *gt)
148 {
149 if (!intel_uc_uses_guc(>->uc))
150 return 0;
151
152 return guc_hwconfig_init(gt);
153 }
154
155 /*
156 * intel_gt_fini_hwconfig - Finalize the HWConfig
157 *
158 * Free up the memory allocation holding the table.
159 */
intel_gt_fini_hwconfig(struct intel_gt * gt)160 void intel_gt_fini_hwconfig(struct intel_gt *gt)
161 {
162 struct intel_hwconfig *hwconfig = >->info.hwconfig;
163
164 kfree(hwconfig->ptr);
165 hwconfig->size = 0;
166 hwconfig->ptr = NULL;
167 }
168