1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * System Control and Management Interface (SCMI) Message Protocol Quirks
4 *
5 * Copyright (C) 2025 ARM Ltd.
6 */
7
8 /**
9 * DOC: Theory of operation
10 *
11 * A framework to define SCMI quirks and their activation conditions based on
12 * existing static_keys kernel facilities.
13 *
14 * Quirks are named and their activation conditions defined using the macro
15 * DEFINE_SCMI_QUIRK() in this file.
16 *
17 * After a quirk is defined, a corresponding entry must also be added to the
18 * global @scmi_quirks_table in this file using __DECLARE_SCMI_QUIRK_ENTRY().
19 *
20 * Additionally a corresponding quirk declaration must be added also to the
21 * quirk.h file using DECLARE_SCMI_QUIRK().
22 *
23 * The needed quirk code-snippet itself will be defined local to the SCMI code
24 * that is meant to fix and will be associated to the previously defined quirk
25 * and related activation conditions using the macro SCMI_QUIRK().
26 *
27 * At runtime, during the SCMI stack probe sequence, once the SCMI Server had
28 * advertised the running platform Vendor, SubVendor and Implementation Version
29 * data, all the defined quirks matching the activation conditions will be
30 * enabled.
31 *
32 * Example
33 *
34 * quirk.c
35 * -------
36 * DEFINE_SCMI_QUIRK(fix_me, "vendor", "subvend", "0x12000-0x30000",
37 * "someone,plat_A", "another,plat_b", "vend,sku");
38 *
39 * static struct scmi_quirk *scmi_quirks_table[] = {
40 * ...
41 * __DECLARE_SCMI_QUIRK_ENTRY(fix_me),
42 * NULL
43 * };
44 *
45 * quirk.h
46 * -------
47 * DECLARE_SCMI_QUIRK(fix_me);
48 *
49 * <somewhere_in_the_scmi_stack.c>
50 * ------------------------------
51 *
52 * #define QUIRK_CODE_SNIPPET_FIX_ME() \
53 * ({ \
54 * if (p->condition) \
55 * a_ptr->calculated_val = 123; \
56 * })
57 *
58 *
59 * int some_function_to_fix(int param, struct something *p)
60 * {
61 * struct some_strut *a_ptr;
62 *
63 * a_ptr = some_load_func(p);
64 * SCMI_QUIRK(fix_me, QUIRK_CODE_SNIPPET_FIX_ME);
65 * some_more_func(a_ptr);
66 * ...
67 *
68 * return 0;
69 * }
70 *
71 */
72
73 #include <linux/ctype.h>
74 #include <linux/device.h>
75 #include <linux/export.h>
76 #include <linux/hashtable.h>
77 #include <linux/kstrtox.h>
78 #include <linux/of.h>
79 #include <linux/slab.h>
80 #include <linux/static_key.h>
81 #include <linux/string.h>
82 #include <linux/stringhash.h>
83 #include <linux/types.h>
84
85 #include "quirks.h"
86
87 #define SCMI_QUIRKS_HT_SZ 4
88
89 struct scmi_quirk {
90 bool enabled;
91 const char *name;
92 char *vendor;
93 char *sub_vendor_id;
94 char *impl_ver_range;
95 u32 start_range;
96 u32 end_range;
97 struct static_key_false *key;
98 struct hlist_node hash;
99 unsigned int hkey;
100 const char *const compats[];
101 };
102
103 #define __DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ...) \
104 static struct scmi_quirk scmi_quirk_entry_ ## _qn = { \
105 .name = __stringify(quirk_ ## _qn), \
106 .vendor = _ven, \
107 .sub_vendor_id = _sub, \
108 .impl_ver_range = _impl, \
109 .key = &(scmi_quirk_ ## _qn), \
110 .compats = { __VA_ARGS__ __VA_OPT__(,) NULL }, \
111 }
112
113 #define __DECLARE_SCMI_QUIRK_ENTRY(_qn) (&(scmi_quirk_entry_ ## _qn))
114
115 /*
116 * Define a quirk by name and provide the matching tokens where:
117 *
118 * _qn: A string which will be used to build the quirk and the global
119 * static_key names.
120 * _ven : SCMI Vendor ID string match, NULL means any.
121 * _sub : SCMI SubVendor ID string match, NULL means any.
122 * _impl : SCMI Implementation Version string match, NULL means any.
123 * This string can be used to express version ranges which will be
124 * interpreted as follows:
125 *
126 * NULL [0, 0xFFFFFFFF]
127 * "X" [X, X]
128 * "X-" [X, 0xFFFFFFFF]
129 * "-X" [0, X]
130 * "X-Y" [X, Y]
131 *
132 * with X <= Y and <v> in [X, Y] meaning X <= <v> <= Y
133 *
134 * ... : An optional variadic macros argument used to provide a comma-separated
135 * list of compatible strings matches; when no variadic argument is
136 * provided, ANY compatible will match this quirk.
137 *
138 * This implicitly define also a properly named global static-key that
139 * will be used to dynamically enable the quirk at initialization time.
140 *
141 * Note that it is possible to associate multiple quirks to the same
142 * matching pattern, if your firmware quality is really astounding :P
143 *
144 * Example:
145 *
146 * Compatibles list NOT provided, so ANY compatible will match:
147 *
148 * DEFINE_SCMI_QUIRK(my_new_issue, "Vend", "SVend", "0x12000-0x30000");
149 *
150 *
151 * A few compatibles provided to match against:
152 *
153 * DEFINE_SCMI_QUIRK(my_new_issue, "Vend", "SVend", "0x12000-0x30000",
154 * "xvend,plat_a", "xvend,plat_b", "xvend,sku_name");
155 */
156 #define DEFINE_SCMI_QUIRK(_qn, _ven, _sub, _impl, ...) \
157 DEFINE_STATIC_KEY_FALSE(scmi_quirk_ ## _qn); \
158 __DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ##__VA_ARGS__)
159
160 /*
161 * Same as DEFINE_SCMI_QUIRK but EXPORTED: this is meant to address quirks
162 * that possibly reside in code that is included in loadable kernel modules
163 * that needs to be able to access the global static keys at runtime to
164 * determine if enabled or not. (see SCMI_QUIRK to understand usage)
165 */
166 #define DEFINE_SCMI_QUIRK_EXPORTED(_qn, _ven, _sub, _impl, ...) \
167 DEFINE_STATIC_KEY_FALSE(scmi_quirk_ ## _qn); \
168 EXPORT_SYMBOL_GPL(scmi_quirk_ ## _qn); \
169 __DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ##__VA_ARGS__)
170
171 /* Global Quirks Definitions */
172 DEFINE_SCMI_QUIRK(clock_rates_triplet_out_of_spec, NULL, NULL, NULL);
173 DEFINE_SCMI_QUIRK(perf_level_get_fc_force, "Qualcomm", NULL, "0x20000-");
174
175 /*
176 * Quirks Pointers Array
177 *
178 * This is filled at compile-time with the list of pointers to all the currently
179 * defined quirks descriptors.
180 */
181 static struct scmi_quirk *scmi_quirks_table[] = {
182 __DECLARE_SCMI_QUIRK_ENTRY(clock_rates_triplet_out_of_spec),
183 __DECLARE_SCMI_QUIRK_ENTRY(perf_level_get_fc_force),
184 NULL
185 };
186
187 /*
188 * Quirks HashTable
189 *
190 * A run-time populated hashtable containing all the defined quirks descriptors
191 * hashed by matching pattern.
192 */
193 static DEFINE_READ_MOSTLY_HASHTABLE(scmi_quirks_ht, SCMI_QUIRKS_HT_SZ);
194
scmi_quirk_signature(const char * vend,const char * sub_vend)195 static unsigned int scmi_quirk_signature(const char *vend, const char *sub_vend)
196 {
197 char *signature, *p;
198 unsigned int hash32;
199 unsigned long hash = 0;
200
201 /* vendor_id/sub_vendor_id guaranteed <= SCMI_SHORT_NAME_MAX_SIZE */
202 signature = kasprintf(GFP_KERNEL, "|%s|%s|", vend ?: "", sub_vend ?: "");
203 if (!signature)
204 return 0;
205
206 pr_debug("SCMI Quirk Signature >>>%s<<<\n", signature);
207
208 p = signature;
209 while (*p)
210 hash = partial_name_hash(tolower(*p++), hash);
211 hash32 = end_name_hash(hash);
212
213 kfree(signature);
214
215 return hash32;
216 }
217
scmi_quirk_range_parse(struct scmi_quirk * quirk)218 static int scmi_quirk_range_parse(struct scmi_quirk *quirk)
219 {
220 const char *last, *first = quirk->impl_ver_range;
221 size_t len;
222 char *sep;
223 int ret;
224
225 quirk->start_range = 0;
226 quirk->end_range = 0xFFFFFFFF;
227 len = quirk->impl_ver_range ? strlen(quirk->impl_ver_range) : 0;
228 if (!len)
229 return 0;
230
231 last = first + len - 1;
232 sep = strchr(quirk->impl_ver_range, '-');
233 if (sep)
234 *sep = '\0';
235
236 if (sep == first) /* -X */
237 ret = kstrtouint(first + 1, 0, &quirk->end_range);
238 else /* X OR X- OR X-y */
239 ret = kstrtouint(first, 0, &quirk->start_range);
240 if (ret)
241 return ret;
242
243 if (!sep)
244 quirk->end_range = quirk->start_range;
245 else if (sep != last) /* x-Y */
246 ret = kstrtouint(sep + 1, 0, &quirk->end_range);
247
248 if (quirk->start_range > quirk->end_range)
249 return -EINVAL;
250
251 return ret;
252 }
253
scmi_quirks_initialize(void)254 void scmi_quirks_initialize(void)
255 {
256 struct scmi_quirk *quirk;
257 int i;
258
259 for (i = 0, quirk = scmi_quirks_table[0]; quirk;
260 i++, quirk = scmi_quirks_table[i]) {
261 int ret;
262
263 ret = scmi_quirk_range_parse(quirk);
264 if (ret) {
265 pr_err("SCMI skip QUIRK [%s] - BAD RANGE - |%s|\n",
266 quirk->name, quirk->impl_ver_range);
267 continue;
268 }
269 quirk->hkey = scmi_quirk_signature(quirk->vendor,
270 quirk->sub_vendor_id);
271
272 hash_add(scmi_quirks_ht, &quirk->hash, quirk->hkey);
273
274 pr_debug("Registered SCMI QUIRK [%s] -- %p - Key [0x%08X] - %s/%s/[0x%08X-0x%08X]\n",
275 quirk->name, quirk, quirk->hkey,
276 quirk->vendor, quirk->sub_vendor_id,
277 quirk->start_range, quirk->end_range);
278 }
279
280 pr_debug("SCMI Quirks initialized\n");
281 }
282
scmi_quirks_enable(struct device * dev,const char * vend,const char * subv,const u32 impl)283 void scmi_quirks_enable(struct device *dev, const char *vend,
284 const char *subv, const u32 impl)
285 {
286 for (int i = 3; i >= 0; i--) {
287 struct scmi_quirk *quirk;
288 unsigned int hkey;
289
290 hkey = scmi_quirk_signature(i > 1 ? vend : NULL,
291 i > 2 ? subv : NULL);
292
293 /*
294 * Note that there could be multiple matches so we
295 * will enable multiple quirk part of a hash collision
296 * domain...BUT we cannot assume that ALL quirks on the
297 * same collision domain are a full match.
298 */
299 hash_for_each_possible(scmi_quirks_ht, quirk, hash, hkey) {
300 if (quirk->enabled || quirk->hkey != hkey ||
301 impl < quirk->start_range ||
302 impl > quirk->end_range)
303 continue;
304
305 if (quirk->compats[0] &&
306 !of_machine_compatible_match(quirk->compats))
307 continue;
308
309 dev_info(dev, "Enabling SCMI Quirk [%s]\n",
310 quirk->name);
311
312 dev_dbg(dev,
313 "Quirk matched on: %s/%s/%s/[0x%08X-0x%08X]\n",
314 quirk->compats[0], quirk->vendor,
315 quirk->sub_vendor_id,
316 quirk->start_range, quirk->end_range);
317
318 static_branch_enable(quirk->key);
319 quirk->enabled = true;
320 }
321 }
322 }
323