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