// SPDX-License-Identifier: GPL-2.0 /* * System Control and Management Interface (SCMI) Message Protocol Quirks * * Copyright (C) 2025 ARM Ltd.
*/
/** * DOC: Theory of operation * * A framework to define SCMI quirks and their activation conditions based on * existing static_keys kernel facilities. * * Quirks are named and their activation conditions defined using the macro * DEFINE_SCMI_QUIRK() in this file. * * After a quirk is defined, a corresponding entry must also be added to the * global @scmi_quirks_table in this file using __DECLARE_SCMI_QUIRK_ENTRY(). * * Additionally a corresponding quirk declaration must be added also to the * quirk.h file using DECLARE_SCMI_QUIRK(). * * The needed quirk code-snippet itself will be defined local to the SCMI code * that is meant to fix and will be associated to the previously defined quirk * and related activation conditions using the macro SCMI_QUIRK(). * * At runtime, during the SCMI stack probe sequence, once the SCMI Server had * advertised the running platform Vendor, SubVendor and Implementation Version * data, all the defined quirks matching the activation conditions will be * enabled. * * Example * * quirk.c * ------- * DEFINE_SCMI_QUIRK(fix_me, "vendor", "subvend", "0x12000-0x30000", * "someone,plat_A", "another,plat_b", "vend,sku"); * * static struct scmi_quirk *scmi_quirks_table[] = { * ... * __DECLARE_SCMI_QUIRK_ENTRY(fix_me), * NULL * }; * * quirk.h * ------- * DECLARE_SCMI_QUIRK(fix_me); * * <somewhere_in_the_scmi_stack.c> * ------------------------------ * * #define QUIRK_CODE_SNIPPET_FIX_ME() \ * ({ \ * if (p->condition) \ * a_ptr->calculated_val = 123; \ * }) * * * int some_function_to_fix(int param, struct something *p) * { * struct some_strut *a_ptr; * * a_ptr = some_load_func(p); * SCMI_QUIRK(fix_me, QUIRK_CODE_SNIPPET_FIX_ME); * some_more_func(a_ptr); * ... * * return 0; * } *
*/
/* * Define a quirk by name and provide the matching tokens where: * * _qn: A string which will be used to build the quirk and the global * static_key names. * _ven : SCMI Vendor ID string match, NULL means any. * _sub : SCMI SubVendor ID string match, NULL means any. * _impl : SCMI Implementation Version string match, NULL means any. * This string can be used to express version ranges which will be * interpreted as follows: * * NULL [0, 0xFFFFFFFF] * "X" [X, X] * "X-" [X, 0xFFFFFFFF] * "-X" [0, X] * "X-Y" [X, Y] * * with X <= Y and <v> in [X, Y] meaning X <= <v> <= Y * * ... : An optional variadic macros argument used to provide a comma-separated * list of compatible strings matches; when no variadic argument is * provided, ANY compatible will match this quirk. * * This implicitly define also a properly named global static-key that * will be used to dynamically enable the quirk at initialization time. * * Note that it is possible to associate multiple quirks to the same * matching pattern, if your firmware quality is really astounding :P * * Example: * * Compatibles list NOT provided, so ANY compatible will match: * * DEFINE_SCMI_QUIRK(my_new_issue, "Vend", "SVend", "0x12000-0x30000"); * * * A few compatibles provided to match against: * * DEFINE_SCMI_QUIRK(my_new_issue, "Vend", "SVend", "0x12000-0x30000", * "xvend,plat_a", "xvend,plat_b", "xvend,sku_name");
*/ #define DEFINE_SCMI_QUIRK(_qn, _ven, _sub, _impl, ...) \
DEFINE_STATIC_KEY_FALSE(scmi_quirk_ ## _qn); \
__DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ##__VA_ARGS__)
/* * Same as DEFINE_SCMI_QUIRK but EXPORTED: this is meant to address quirks * that possibly reside in code that is included in loadable kernel modules * that needs to be able to access the global static keys at runtime to * determine if enabled or not. (see SCMI_QUIRK to understand usage)
*/ #define DEFINE_SCMI_QUIRK_EXPORTED(_qn, _ven, _sub, _impl, ...) \
DEFINE_STATIC_KEY_FALSE(scmi_quirk_ ## _qn); \
EXPORT_SYMBOL_GPL(scmi_quirk_ ## _qn); \
__DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ##__VA_ARGS__)
/* * Quirks Pointers Array * * This is filled at compile-time with the list of pointers to all the currently * defined quirks descriptors.
*/ staticstruct scmi_quirk *scmi_quirks_table[] = {
__DECLARE_SCMI_QUIRK_ENTRY(clock_rates_triplet_out_of_spec),
__DECLARE_SCMI_QUIRK_ENTRY(perf_level_get_fc_force),
NULL
};
/* * Quirks HashTable * * A run-time populated hashtable containing all the defined quirks descriptors * hashed by matching pattern.
*/ static DEFINE_READ_MOSTLY_HASHTABLE(scmi_quirks_ht, SCMI_QUIRKS_HT_SZ);
quirk->start_range = 0;
quirk->end_range = 0xFFFFFFFF;
len = quirk->impl_ver_range ? strlen(quirk->impl_ver_range) : 0; if (!len) return 0;
first = kmemdup(quirk->impl_ver_range, len + 1, GFP_KERNEL); if (!first) return -ENOMEM;
last = first + len - 1;
sep = strchr(first, '-'); if (sep)
*sep = '\0';
if (sep == first) /* -X */
ret = kstrtouint(first + 1, 0, &quirk->end_range); else/* X OR X- OR X-y */
ret = kstrtouint(first, 0, &quirk->start_range); if (ret) return ret;
if (!sep)
quirk->end_range = quirk->start_range; elseif (sep != last) /* x-Y */
ret = kstrtouint(sep + 1, 0, &quirk->end_range);
if (quirk->start_range > quirk->end_range) return -EINVAL;
return ret;
}
void scmi_quirks_initialize(void)
{ struct scmi_quirk *quirk; int i;
for (i = 0, quirk = scmi_quirks_table[0]; quirk;
i++, quirk = scmi_quirks_table[i]) { int ret;
ret = scmi_quirk_range_parse(quirk); if (ret) {
pr_err("SCMI skip QUIRK [%s] - BAD RANGE - |%s|\n",
quirk->name, quirk->impl_ver_range); continue;
}
quirk->hkey = scmi_quirk_signature(quirk->vendor,
quirk->sub_vendor_id);
void scmi_quirks_enable(struct device *dev, constchar *vend, constchar *subv, const u32 impl)
{ for (int i = 3; i >= 0; i--) { struct scmi_quirk *quirk; unsignedint hkey;
hkey = scmi_quirk_signature(i > 1 ? vend : NULL,
i > 2 ? subv : NULL);
/* * Note that there could be multiple matches so we * will enable multiple quirk part of a hash collision * domain...BUT we cannot assume that ALL quirks on the * same collision domain are a full match.
*/
hash_for_each_possible(scmi_quirks_ht, quirk, hash, hkey) { if (quirk->enabled || quirk->hkey != hkey ||
impl < quirk->start_range ||
impl > quirk->end_range) continue;
if (quirk->compats[0] &&
!of_machine_compatible_match(quirk->compats)) continue;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.