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
34static 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
52static 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
71static 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
95static 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 */
111static 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 */
144int 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 */
157void 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