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 = &gt->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(&gt->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 = &gt->info.hwconfig;
163 
164 	kfree(hwconfig->ptr);
165 	hwconfig->size = 0;
166 	hwconfig->ptr = NULL;
167 }
168