1487c407dSCristian Marussi // SPDX-License-Identifier: GPL-2.0
2487c407dSCristian Marussi /*
3487c407dSCristian Marussi * System Control and Management Interface (SCMI) Message Protocol Quirks
4487c407dSCristian Marussi *
5487c407dSCristian Marussi * Copyright (C) 2025 ARM Ltd.
6487c407dSCristian Marussi */
7487c407dSCristian Marussi
8487c407dSCristian Marussi /**
9487c407dSCristian Marussi * DOC: Theory of operation
10487c407dSCristian Marussi *
11487c407dSCristian Marussi * A framework to define SCMI quirks and their activation conditions based on
12487c407dSCristian Marussi * existing static_keys kernel facilities.
13487c407dSCristian Marussi *
14487c407dSCristian Marussi * Quirks are named and their activation conditions defined using the macro
15487c407dSCristian Marussi * DEFINE_SCMI_QUIRK() in this file.
16487c407dSCristian Marussi *
17487c407dSCristian Marussi * After a quirk is defined, a corresponding entry must also be added to the
18487c407dSCristian Marussi * global @scmi_quirks_table in this file using __DECLARE_SCMI_QUIRK_ENTRY().
19487c407dSCristian Marussi *
20487c407dSCristian Marussi * Additionally a corresponding quirk declaration must be added also to the
21487c407dSCristian Marussi * quirk.h file using DECLARE_SCMI_QUIRK().
22487c407dSCristian Marussi *
23487c407dSCristian Marussi * The needed quirk code-snippet itself will be defined local to the SCMI code
24487c407dSCristian Marussi * that is meant to fix and will be associated to the previously defined quirk
25487c407dSCristian Marussi * and related activation conditions using the macro SCMI_QUIRK().
26487c407dSCristian Marussi *
27487c407dSCristian Marussi * At runtime, during the SCMI stack probe sequence, once the SCMI Server had
28487c407dSCristian Marussi * advertised the running platform Vendor, SubVendor and Implementation Version
29487c407dSCristian Marussi * data, all the defined quirks matching the activation conditions will be
30487c407dSCristian Marussi * enabled.
31487c407dSCristian Marussi *
32487c407dSCristian Marussi * Example
33487c407dSCristian Marussi *
34487c407dSCristian Marussi * quirk.c
35487c407dSCristian Marussi * -------
36487c407dSCristian Marussi * DEFINE_SCMI_QUIRK(fix_me, "vendor", "subvend", "0x12000-0x30000",
37487c407dSCristian Marussi * "someone,plat_A", "another,plat_b", "vend,sku");
38487c407dSCristian Marussi *
39487c407dSCristian Marussi * static struct scmi_quirk *scmi_quirks_table[] = {
40487c407dSCristian Marussi * ...
41487c407dSCristian Marussi * __DECLARE_SCMI_QUIRK_ENTRY(fix_me),
42487c407dSCristian Marussi * NULL
43487c407dSCristian Marussi * };
44487c407dSCristian Marussi *
45487c407dSCristian Marussi * quirk.h
46487c407dSCristian Marussi * -------
47487c407dSCristian Marussi * DECLARE_SCMI_QUIRK(fix_me);
48487c407dSCristian Marussi *
49487c407dSCristian Marussi * <somewhere_in_the_scmi_stack.c>
50487c407dSCristian Marussi * ------------------------------
51487c407dSCristian Marussi *
52487c407dSCristian Marussi * #define QUIRK_CODE_SNIPPET_FIX_ME() \
53487c407dSCristian Marussi * ({ \
54487c407dSCristian Marussi * if (p->condition) \
55487c407dSCristian Marussi * a_ptr->calculated_val = 123; \
56487c407dSCristian Marussi * })
57487c407dSCristian Marussi *
58487c407dSCristian Marussi *
59487c407dSCristian Marussi * int some_function_to_fix(int param, struct something *p)
60487c407dSCristian Marussi * {
61487c407dSCristian Marussi * struct some_strut *a_ptr;
62487c407dSCristian Marussi *
63487c407dSCristian Marussi * a_ptr = some_load_func(p);
64487c407dSCristian Marussi * SCMI_QUIRK(fix_me, QUIRK_CODE_SNIPPET_FIX_ME);
65487c407dSCristian Marussi * some_more_func(a_ptr);
66487c407dSCristian Marussi * ...
67487c407dSCristian Marussi *
68487c407dSCristian Marussi * return 0;
69487c407dSCristian Marussi * }
70487c407dSCristian Marussi *
71487c407dSCristian Marussi */
72487c407dSCristian Marussi
73487c407dSCristian Marussi #include <linux/ctype.h>
74487c407dSCristian Marussi #include <linux/device.h>
75487c407dSCristian Marussi #include <linux/export.h>
76487c407dSCristian Marussi #include <linux/hashtable.h>
77487c407dSCristian Marussi #include <linux/kstrtox.h>
78487c407dSCristian Marussi #include <linux/of.h>
79487c407dSCristian Marussi #include <linux/slab.h>
80487c407dSCristian Marussi #include <linux/static_key.h>
81487c407dSCristian Marussi #include <linux/string.h>
82487c407dSCristian Marussi #include <linux/stringhash.h>
83487c407dSCristian Marussi #include <linux/types.h>
84487c407dSCristian Marussi
85487c407dSCristian Marussi #include "quirks.h"
86487c407dSCristian Marussi
87487c407dSCristian Marussi #define SCMI_QUIRKS_HT_SZ 4
88487c407dSCristian Marussi
89487c407dSCristian Marussi struct scmi_quirk {
90487c407dSCristian Marussi bool enabled;
91487c407dSCristian Marussi const char *name;
92487c407dSCristian Marussi char *vendor;
93487c407dSCristian Marussi char *sub_vendor_id;
94487c407dSCristian Marussi char *impl_ver_range;
95487c407dSCristian Marussi u32 start_range;
96487c407dSCristian Marussi u32 end_range;
97487c407dSCristian Marussi struct static_key_false *key;
98487c407dSCristian Marussi struct hlist_node hash;
99487c407dSCristian Marussi unsigned int hkey;
100487c407dSCristian Marussi const char *const compats[];
101487c407dSCristian Marussi };
102487c407dSCristian Marussi
103487c407dSCristian Marussi #define __DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ...) \
104487c407dSCristian Marussi static struct scmi_quirk scmi_quirk_entry_ ## _qn = { \
105487c407dSCristian Marussi .name = __stringify(quirk_ ## _qn), \
106487c407dSCristian Marussi .vendor = _ven, \
107487c407dSCristian Marussi .sub_vendor_id = _sub, \
108487c407dSCristian Marussi .impl_ver_range = _impl, \
109487c407dSCristian Marussi .key = &(scmi_quirk_ ## _qn), \
110487c407dSCristian Marussi .compats = { __VA_ARGS__ __VA_OPT__(,) NULL }, \
111487c407dSCristian Marussi }
112487c407dSCristian Marussi
113487c407dSCristian Marussi #define __DECLARE_SCMI_QUIRK_ENTRY(_qn) (&(scmi_quirk_entry_ ## _qn))
114487c407dSCristian Marussi
115487c407dSCristian Marussi /*
116487c407dSCristian Marussi * Define a quirk by name and provide the matching tokens where:
117487c407dSCristian Marussi *
118487c407dSCristian Marussi * _qn: A string which will be used to build the quirk and the global
119487c407dSCristian Marussi * static_key names.
120487c407dSCristian Marussi * _ven : SCMI Vendor ID string match, NULL means any.
121487c407dSCristian Marussi * _sub : SCMI SubVendor ID string match, NULL means any.
122487c407dSCristian Marussi * _impl : SCMI Implementation Version string match, NULL means any.
123487c407dSCristian Marussi * This string can be used to express version ranges which will be
124487c407dSCristian Marussi * interpreted as follows:
125487c407dSCristian Marussi *
126487c407dSCristian Marussi * NULL [0, 0xFFFFFFFF]
127487c407dSCristian Marussi * "X" [X, X]
128487c407dSCristian Marussi * "X-" [X, 0xFFFFFFFF]
129487c407dSCristian Marussi * "-X" [0, X]
130487c407dSCristian Marussi * "X-Y" [X, Y]
131487c407dSCristian Marussi *
132487c407dSCristian Marussi * with X <= Y and <v> in [X, Y] meaning X <= <v> <= Y
133487c407dSCristian Marussi *
134487c407dSCristian Marussi * ... : An optional variadic macros argument used to provide a comma-separated
135487c407dSCristian Marussi * list of compatible strings matches; when no variadic argument is
136487c407dSCristian Marussi * provided, ANY compatible will match this quirk.
137487c407dSCristian Marussi *
138487c407dSCristian Marussi * This implicitly define also a properly named global static-key that
139487c407dSCristian Marussi * will be used to dynamically enable the quirk at initialization time.
140487c407dSCristian Marussi *
141487c407dSCristian Marussi * Note that it is possible to associate multiple quirks to the same
142487c407dSCristian Marussi * matching pattern, if your firmware quality is really astounding :P
143487c407dSCristian Marussi *
144487c407dSCristian Marussi * Example:
145487c407dSCristian Marussi *
146487c407dSCristian Marussi * Compatibles list NOT provided, so ANY compatible will match:
147487c407dSCristian Marussi *
148487c407dSCristian Marussi * DEFINE_SCMI_QUIRK(my_new_issue, "Vend", "SVend", "0x12000-0x30000");
149487c407dSCristian Marussi *
150487c407dSCristian Marussi *
151487c407dSCristian Marussi * A few compatibles provided to match against:
152487c407dSCristian Marussi *
153487c407dSCristian Marussi * DEFINE_SCMI_QUIRK(my_new_issue, "Vend", "SVend", "0x12000-0x30000",
154487c407dSCristian Marussi * "xvend,plat_a", "xvend,plat_b", "xvend,sku_name");
155487c407dSCristian Marussi */
156487c407dSCristian Marussi #define DEFINE_SCMI_QUIRK(_qn, _ven, _sub, _impl, ...) \
157487c407dSCristian Marussi DEFINE_STATIC_KEY_FALSE(scmi_quirk_ ## _qn); \
158487c407dSCristian Marussi __DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ##__VA_ARGS__)
159487c407dSCristian Marussi
160487c407dSCristian Marussi /*
161487c407dSCristian Marussi * Same as DEFINE_SCMI_QUIRK but EXPORTED: this is meant to address quirks
162487c407dSCristian Marussi * that possibly reside in code that is included in loadable kernel modules
163487c407dSCristian Marussi * that needs to be able to access the global static keys at runtime to
164487c407dSCristian Marussi * determine if enabled or not. (see SCMI_QUIRK to understand usage)
165487c407dSCristian Marussi */
166487c407dSCristian Marussi #define DEFINE_SCMI_QUIRK_EXPORTED(_qn, _ven, _sub, _impl, ...) \
167487c407dSCristian Marussi DEFINE_STATIC_KEY_FALSE(scmi_quirk_ ## _qn); \
168487c407dSCristian Marussi EXPORT_SYMBOL_GPL(scmi_quirk_ ## _qn); \
169487c407dSCristian Marussi __DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ##__VA_ARGS__)
170487c407dSCristian Marussi
171487c407dSCristian Marussi /* Global Quirks Definitions */
1727b487beaSCristian Marussi DEFINE_SCMI_QUIRK(clock_rates_triplet_out_of_spec, NULL, NULL, NULL);
173*397f802dSJohan Hovold DEFINE_SCMI_QUIRK(perf_level_get_fc_force, "Qualcomm", NULL, "0x20000-");
174487c407dSCristian Marussi
175487c407dSCristian Marussi /*
176487c407dSCristian Marussi * Quirks Pointers Array
177487c407dSCristian Marussi *
178487c407dSCristian Marussi * This is filled at compile-time with the list of pointers to all the currently
179487c407dSCristian Marussi * defined quirks descriptors.
180487c407dSCristian Marussi */
181487c407dSCristian Marussi static struct scmi_quirk *scmi_quirks_table[] = {
1827b487beaSCristian Marussi __DECLARE_SCMI_QUIRK_ENTRY(clock_rates_triplet_out_of_spec),
183*397f802dSJohan Hovold __DECLARE_SCMI_QUIRK_ENTRY(perf_level_get_fc_force),
184487c407dSCristian Marussi NULL
185487c407dSCristian Marussi };
186487c407dSCristian Marussi
187487c407dSCristian Marussi /*
188487c407dSCristian Marussi * Quirks HashTable
189487c407dSCristian Marussi *
190487c407dSCristian Marussi * A run-time populated hashtable containing all the defined quirks descriptors
191487c407dSCristian Marussi * hashed by matching pattern.
192487c407dSCristian Marussi */
193487c407dSCristian Marussi static DEFINE_READ_MOSTLY_HASHTABLE(scmi_quirks_ht, SCMI_QUIRKS_HT_SZ);
194487c407dSCristian Marussi
scmi_quirk_signature(const char * vend,const char * sub_vend)195487c407dSCristian Marussi static unsigned int scmi_quirk_signature(const char *vend, const char *sub_vend)
196487c407dSCristian Marussi {
197487c407dSCristian Marussi char *signature, *p;
198487c407dSCristian Marussi unsigned int hash32;
199487c407dSCristian Marussi unsigned long hash = 0;
200487c407dSCristian Marussi
201487c407dSCristian Marussi /* vendor_id/sub_vendor_id guaranteed <= SCMI_SHORT_NAME_MAX_SIZE */
202487c407dSCristian Marussi signature = kasprintf(GFP_KERNEL, "|%s|%s|", vend ?: "", sub_vend ?: "");
203487c407dSCristian Marussi if (!signature)
204487c407dSCristian Marussi return 0;
205487c407dSCristian Marussi
206487c407dSCristian Marussi pr_debug("SCMI Quirk Signature >>>%s<<<\n", signature);
207487c407dSCristian Marussi
208487c407dSCristian Marussi p = signature;
209487c407dSCristian Marussi while (*p)
210487c407dSCristian Marussi hash = partial_name_hash(tolower(*p++), hash);
211487c407dSCristian Marussi hash32 = end_name_hash(hash);
212487c407dSCristian Marussi
213487c407dSCristian Marussi kfree(signature);
214487c407dSCristian Marussi
215487c407dSCristian Marussi return hash32;
216487c407dSCristian Marussi }
217487c407dSCristian Marussi
scmi_quirk_range_parse(struct scmi_quirk * quirk)218487c407dSCristian Marussi static int scmi_quirk_range_parse(struct scmi_quirk *quirk)
219487c407dSCristian Marussi {
220487c407dSCristian Marussi const char *last, *first = quirk->impl_ver_range;
221487c407dSCristian Marussi size_t len;
222487c407dSCristian Marussi char *sep;
223487c407dSCristian Marussi int ret;
224487c407dSCristian Marussi
225487c407dSCristian Marussi quirk->start_range = 0;
226487c407dSCristian Marussi quirk->end_range = 0xFFFFFFFF;
227487c407dSCristian Marussi len = quirk->impl_ver_range ? strlen(quirk->impl_ver_range) : 0;
228487c407dSCristian Marussi if (!len)
229487c407dSCristian Marussi return 0;
230487c407dSCristian Marussi
231487c407dSCristian Marussi last = first + len - 1;
232487c407dSCristian Marussi sep = strchr(quirk->impl_ver_range, '-');
233487c407dSCristian Marussi if (sep)
234487c407dSCristian Marussi *sep = '\0';
235487c407dSCristian Marussi
236487c407dSCristian Marussi if (sep == first) /* -X */
237487c407dSCristian Marussi ret = kstrtouint(first + 1, 0, &quirk->end_range);
238487c407dSCristian Marussi else /* X OR X- OR X-y */
239487c407dSCristian Marussi ret = kstrtouint(first, 0, &quirk->start_range);
240487c407dSCristian Marussi if (ret)
241487c407dSCristian Marussi return ret;
242487c407dSCristian Marussi
243487c407dSCristian Marussi if (!sep)
244487c407dSCristian Marussi quirk->end_range = quirk->start_range;
245487c407dSCristian Marussi else if (sep != last) /* x-Y */
246487c407dSCristian Marussi ret = kstrtouint(sep + 1, 0, &quirk->end_range);
247487c407dSCristian Marussi
248487c407dSCristian Marussi if (quirk->start_range > quirk->end_range)
249487c407dSCristian Marussi return -EINVAL;
250487c407dSCristian Marussi
251487c407dSCristian Marussi return ret;
252487c407dSCristian Marussi }
253487c407dSCristian Marussi
scmi_quirks_initialize(void)254487c407dSCristian Marussi void scmi_quirks_initialize(void)
255487c407dSCristian Marussi {
256487c407dSCristian Marussi struct scmi_quirk *quirk;
257487c407dSCristian Marussi int i;
258487c407dSCristian Marussi
259487c407dSCristian Marussi for (i = 0, quirk = scmi_quirks_table[0]; quirk;
260487c407dSCristian Marussi i++, quirk = scmi_quirks_table[i]) {
261487c407dSCristian Marussi int ret;
262487c407dSCristian Marussi
263487c407dSCristian Marussi ret = scmi_quirk_range_parse(quirk);
264487c407dSCristian Marussi if (ret) {
265487c407dSCristian Marussi pr_err("SCMI skip QUIRK [%s] - BAD RANGE - |%s|\n",
266487c407dSCristian Marussi quirk->name, quirk->impl_ver_range);
267487c407dSCristian Marussi continue;
268487c407dSCristian Marussi }
269487c407dSCristian Marussi quirk->hkey = scmi_quirk_signature(quirk->vendor,
270487c407dSCristian Marussi quirk->sub_vendor_id);
271487c407dSCristian Marussi
272487c407dSCristian Marussi hash_add(scmi_quirks_ht, &quirk->hash, quirk->hkey);
273487c407dSCristian Marussi
274487c407dSCristian Marussi pr_debug("Registered SCMI QUIRK [%s] -- %p - Key [0x%08X] - %s/%s/[0x%08X-0x%08X]\n",
275487c407dSCristian Marussi quirk->name, quirk, quirk->hkey,
276487c407dSCristian Marussi quirk->vendor, quirk->sub_vendor_id,
277487c407dSCristian Marussi quirk->start_range, quirk->end_range);
278487c407dSCristian Marussi }
279487c407dSCristian Marussi
280487c407dSCristian Marussi pr_debug("SCMI Quirks initialized\n");
281487c407dSCristian Marussi }
282487c407dSCristian Marussi
scmi_quirks_enable(struct device * dev,const char * vend,const char * subv,const u32 impl)283487c407dSCristian Marussi void scmi_quirks_enable(struct device *dev, const char *vend,
284487c407dSCristian Marussi const char *subv, const u32 impl)
285487c407dSCristian Marussi {
286487c407dSCristian Marussi for (int i = 3; i >= 0; i--) {
287487c407dSCristian Marussi struct scmi_quirk *quirk;
288487c407dSCristian Marussi unsigned int hkey;
289487c407dSCristian Marussi
290487c407dSCristian Marussi hkey = scmi_quirk_signature(i > 1 ? vend : NULL,
291487c407dSCristian Marussi i > 2 ? subv : NULL);
292487c407dSCristian Marussi
293487c407dSCristian Marussi /*
294487c407dSCristian Marussi * Note that there could be multiple matches so we
295487c407dSCristian Marussi * will enable multiple quirk part of a hash collision
296487c407dSCristian Marussi * domain...BUT we cannot assume that ALL quirks on the
297487c407dSCristian Marussi * same collision domain are a full match.
298487c407dSCristian Marussi */
299487c407dSCristian Marussi hash_for_each_possible(scmi_quirks_ht, quirk, hash, hkey) {
300487c407dSCristian Marussi if (quirk->enabled || quirk->hkey != hkey ||
301487c407dSCristian Marussi impl < quirk->start_range ||
302487c407dSCristian Marussi impl > quirk->end_range)
303487c407dSCristian Marussi continue;
304487c407dSCristian Marussi
305487c407dSCristian Marussi if (quirk->compats[0] &&
306487c407dSCristian Marussi !of_machine_compatible_match(quirk->compats))
307487c407dSCristian Marussi continue;
308487c407dSCristian Marussi
309487c407dSCristian Marussi dev_info(dev, "Enabling SCMI Quirk [%s]\n",
310487c407dSCristian Marussi quirk->name);
311487c407dSCristian Marussi
312487c407dSCristian Marussi dev_dbg(dev,
313487c407dSCristian Marussi "Quirk matched on: %s/%s/%s/[0x%08X-0x%08X]\n",
314487c407dSCristian Marussi quirk->compats[0], quirk->vendor,
315487c407dSCristian Marussi quirk->sub_vendor_id,
316487c407dSCristian Marussi quirk->start_range, quirk->end_range);
317487c407dSCristian Marussi
318487c407dSCristian Marussi static_branch_enable(quirk->key);
319487c407dSCristian Marussi quirk->enabled = true;
320487c407dSCristian Marussi }
321487c407dSCristian Marussi }
322487c407dSCristian Marussi }
323