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
35static 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, response_buf_size: 0);
49 if (ret == -ENXIO)
50 return -ENOENT;
51
52 return ret;
53}
54
55static 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, ggtt_offset: 0, ggtt_size: 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
74static 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, size: hwconfig->size, out_vma: &vma, out_vaddr: &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, ggtt_size: hwconfig->size);
90 if (ret >= 0)
91 memcpy(to: hwconfig->ptr, from: vaddr, len: hwconfig->size);
92
93 i915_vma_unpin_and_release(p_vma: &vma, I915_VMA_RELEASE_MAP);
94
95 return ret;
96}
97
98static 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 */
114static 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(i915: 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 */
147int intel_gt_init_hwconfig(struct intel_gt *gt)
148{
149 if (!intel_uc_uses_guc(uc: &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 */
160void intel_gt_fini_hwconfig(struct intel_gt *gt)
161{
162 struct intel_hwconfig *hwconfig = &gt->info.hwconfig;
163
164 kfree(objp: hwconfig->ptr);
165 hwconfig->size = 0;
166 hwconfig->ptr = NULL;
167}
168