xref: /linux/drivers/firmware/arm_scmi/quirks.c (revision 297d9111e9fcf47dd1dcc6f79bba915f35378d01)
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