File Index
Files and directories
Sign in
FILE

quirks.c

File preview with syntax highlighting.

linux-6.19.14/drivers/firmware/arm_scmi/quirks.c C
  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/cleanup.h>
 75#include <linux/device.h>
 76#include <linux/export.h>
 77#include <linux/hashtable.h>
 78#include <linux/kstrtox.h>
 79#include <linux/of.h>
 80#include <linux/slab.h>
 81#include <linux/static_key.h>
 82#include <linux/string.h>
 83#include <linux/stringhash.h>
 84#include <linux/types.h>
 85
 86#include "quirks.h"
 87
 88#define SCMI_QUIRKS_HT_SZ	4
 89
 90struct scmi_quirk {
 91	bool enabled;
 92	const char *name;
 93	const char *vendor;
 94	const char *sub_vendor_id;
 95	const char *impl_ver_range;
 96	u32 start_range;
 97	u32 end_range;
 98	struct static_key_false *key;
 99	struct hlist_node hash;
100	unsigned int hkey;
101	const char *const compats[];
102};
103
104#define __DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ...)	\
105	static struct scmi_quirk scmi_quirk_entry_ ## _qn = {		\
106		.name = __stringify(quirk_ ## _qn),			\
107		.vendor = _ven,						\
108		.sub_vendor_id = _sub,					\
109		.impl_ver_range = _impl,				\
110		.key = &(scmi_quirk_ ## _qn),				\
111		.compats = { __VA_ARGS__ __VA_OPT__(,) NULL },		\
112	}
113
114#define __DECLARE_SCMI_QUIRK_ENTRY(_qn)		(&(scmi_quirk_entry_ ## _qn))
115
116/*
117 * Define a quirk by name and provide the matching tokens where:
118 *
119 *  _qn: A string which will be used to build the quirk and the global
120 *	 static_key names.
121 *  _ven : SCMI Vendor ID string match, NULL means any.
122 *  _sub : SCMI SubVendor ID string match, NULL means any.
123 *  _impl : SCMI Implementation Version string match, NULL means any.
124 *          This string can be used to express version ranges which will be
125 *          interpreted as follows:
126 *
127 *			NULL		[0, 0xFFFFFFFF]
128 *			"X"		[X, X]
129 *			"X-"		[X, 0xFFFFFFFF]
130 *			"-X"		[0, X]
131 *			"X-Y"		[X, Y]
132 *
133 *          with X <= Y and <v> in [X, Y] meaning X <= <v> <= Y
134 *
135 *  ... : An optional variadic macros argument used to provide a comma-separated
136 *	  list of compatible strings matches; when no variadic argument is
137 *	  provided, ANY compatible will match this quirk.
138 *
139 *  This implicitly define also a properly named global static-key that
140 *  will be used to dynamically enable the quirk at initialization time.
141 *
142 *  Note that it is possible to associate multiple quirks to the same
143 *  matching pattern, if your firmware quality is really astounding :P
144 *
145 * Example:
146 *
147 * Compatibles list NOT provided, so ANY compatible will match:
148 *
149 *  DEFINE_SCMI_QUIRK(my_new_issue, "Vend", "SVend", "0x12000-0x30000");
150 *
151 *
152 * A few compatibles provided to match against:
153 *
154 *  DEFINE_SCMI_QUIRK(my_new_issue, "Vend", "SVend", "0x12000-0x30000",
155 *		      "xvend,plat_a", "xvend,plat_b", "xvend,sku_name");
156 */
157#define DEFINE_SCMI_QUIRK(_qn, _ven, _sub, _impl, ...)			\
158	DEFINE_STATIC_KEY_FALSE(scmi_quirk_ ## _qn);			\
159	__DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ##__VA_ARGS__)
160
161/*
162 * Same as DEFINE_SCMI_QUIRK but EXPORTED: this is meant to address quirks
163 * that possibly reside in code that is included in loadable kernel modules
164 * that needs to be able to access the global static keys at runtime to
165 * determine if enabled or not. (see SCMI_QUIRK to understand usage)
166 */
167#define DEFINE_SCMI_QUIRK_EXPORTED(_qn, _ven, _sub, _impl, ...)		\
168	DEFINE_STATIC_KEY_FALSE(scmi_quirk_ ## _qn);			\
169	EXPORT_SYMBOL_GPL(scmi_quirk_ ## _qn);				\
170	__DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ##__VA_ARGS__)
171
172/* Global Quirks Definitions */
173DEFINE_SCMI_QUIRK(clock_rates_triplet_out_of_spec, NULL, NULL, NULL);
174DEFINE_SCMI_QUIRK(perf_level_get_fc_force, "Qualcomm", NULL, "0x20000-");
175
176/*
177 * Quirks Pointers Array
178 *
179 * This is filled at compile-time with the list of pointers to all the currently
180 * defined quirks descriptors.
181 */
182static struct scmi_quirk *scmi_quirks_table[] = {
183	__DECLARE_SCMI_QUIRK_ENTRY(clock_rates_triplet_out_of_spec),
184	__DECLARE_SCMI_QUIRK_ENTRY(perf_level_get_fc_force),
185	NULL
186};
187
188/*
189 * Quirks HashTable
190 *
191 * A run-time populated hashtable containing all the defined quirks descriptors
192 * hashed by matching pattern.
193 */
194static DEFINE_READ_MOSTLY_HASHTABLE(scmi_quirks_ht, SCMI_QUIRKS_HT_SZ);
195
196static unsigned int scmi_quirk_signature(const char *vend, const char *sub_vend)
197{
198	char *signature, *p;
199	unsigned int hash32;
200	unsigned long hash = 0;
201
202	/* vendor_id/sub_vendor_id guaranteed <= SCMI_SHORT_NAME_MAX_SIZE */
203	signature = kasprintf(GFP_KERNEL, "|%s|%s|", vend ?: "", sub_vend ?: "");
204	if (!signature)
205		return 0;
206
207	pr_debug("SCMI Quirk Signature >>>%s<<<\n", signature);
208
209	p = signature;
210	while (*p)
211		hash = partial_name_hash(tolower(*p++), hash);
212	hash32 = end_name_hash(hash);
213
214	kfree(signature);
215
216	return hash32;
217}
218
219static int scmi_quirk_range_parse(struct scmi_quirk *quirk)
220{
221	const char *last, *first __free(kfree) = NULL;
222	size_t len;
223	char *sep;
224	int ret;
225
226	quirk->start_range = 0;
227	quirk->end_range = 0xFFFFFFFF;
228	len = quirk->impl_ver_range ? strlen(quirk->impl_ver_range) : 0;
229	if (!len)
230		return 0;
231
232	first = kmemdup(quirk->impl_ver_range, len + 1, GFP_KERNEL);
233	if (!first)
234		return -ENOMEM;
235
236	last = first + len - 1;
237	sep = strchr(first, '-');
238	if (sep)
239		*sep = '\0';
240
241	if (sep == first) /* -X */
242		ret = kstrtouint(first + 1, 0, &quirk->end_range);
243	else /* X OR X- OR X-y */
244		ret = kstrtouint(first, 0, &quirk->start_range);
245	if (ret)
246		return ret;
247
248	if (!sep)
249		quirk->end_range = quirk->start_range;
250	else if (sep != last) /* x-Y */
251		ret = kstrtouint(sep + 1, 0, &quirk->end_range);
252
253	if (quirk->start_range > quirk->end_range)
254		return -EINVAL;
255
256	return ret;
257}
258
259void scmi_quirks_initialize(void)
260{
261	struct scmi_quirk *quirk;
262	int i;
263
264	for (i = 0, quirk = scmi_quirks_table[0]; quirk;
265	     i++, quirk = scmi_quirks_table[i]) {
266		int ret;
267
268		ret = scmi_quirk_range_parse(quirk);
269		if (ret) {
270			pr_err("SCMI skip QUIRK [%s] - BAD RANGE - |%s|\n",
271			       quirk->name, quirk->impl_ver_range);
272			continue;
273		}
274		quirk->hkey = scmi_quirk_signature(quirk->vendor,
275						   quirk->sub_vendor_id);
276
277		hash_add(scmi_quirks_ht, &quirk->hash, quirk->hkey);
278
279		pr_debug("Registered SCMI QUIRK [%s] -- %p - Key [0x%08X] - %s/%s/[0x%08X-0x%08X]\n",
280			 quirk->name, quirk, quirk->hkey,
281			 quirk->vendor, quirk->sub_vendor_id,
282			 quirk->start_range, quirk->end_range);
283	}
284
285	pr_debug("SCMI Quirks initialized\n");
286}
287
288void scmi_quirks_enable(struct device *dev, const char *vend,
289			const char *subv, const u32 impl)
290{
291	for (int i = 3; i >= 0; i--) {
292		struct scmi_quirk *quirk;
293		unsigned int hkey;
294
295		hkey = scmi_quirk_signature(i > 1 ? vend : NULL,
296					    i > 2 ? subv : NULL);
297
298		/*
299		 * Note that there could be multiple matches so we
300		 * will enable multiple quirk part of a hash collision
301		 * domain...BUT we cannot assume that ALL quirks on the
302		 * same collision domain are a full match.
303		 */
304		hash_for_each_possible(scmi_quirks_ht, quirk, hash, hkey) {
305			if (quirk->enabled || quirk->hkey != hkey ||
306			    impl < quirk->start_range ||
307			    impl > quirk->end_range)
308				continue;
309
310			if (quirk->compats[0] &&
311			    !of_machine_compatible_match(quirk->compats))
312				continue;
313
314			dev_info(dev, "Enabling SCMI Quirk [%s]\n",
315				 quirk->name);
316
317			dev_dbg(dev,
318				"Quirk matched on: %s/%s/%s/[0x%08X-0x%08X]\n",
319				quirk->compats[0], quirk->vendor,
320				quirk->sub_vendor_id,
321				quirk->start_range, quirk->end_range);
322
323			static_branch_enable(quirk->key);
324			quirk->enabled = true;
325		}
326	}
327}
~/ Read-only file preview.