// SPDX-License-Identifier: GPL-2.01/*2* System Control and Management Interface (SCMI) Message Protocol Quirks3*4* Copyright (C) 2025 ARM Ltd.5*/67/**8* DOC: Theory of operation9*10* A framework to define SCMI quirks and their activation conditions based on11* existing static_keys kernel facilities.12*13* Quirks are named and their activation conditions defined using the macro14* DEFINE_SCMI_QUIRK() in this file.15*16* After a quirk is defined, a corresponding entry must also be added to the17* global @scmi_quirks_table in this file using __DECLARE_SCMI_QUIRK_ENTRY().18*19* Additionally a corresponding quirk declaration must be added also to the20* quirk.h file using DECLARE_SCMI_QUIRK().21*22* The needed quirk code-snippet itself will be defined local to the SCMI code23* that is meant to fix and will be associated to the previously defined quirk24* and related activation conditions using the macro SCMI_QUIRK().25*26* At runtime, during the SCMI stack probe sequence, once the SCMI Server had27* advertised the running platform Vendor, SubVendor and Implementation Version28* data, all the defined quirks matching the activation conditions will be29* enabled.30*31* Example32*33* quirk.c34* -------35* DEFINE_SCMI_QUIRK(fix_me, "vendor", "subvend", "0x12000-0x30000",36* "someone,plat_A", "another,plat_b", "vend,sku");37*38* static struct scmi_quirk *scmi_quirks_table[] = {39* ...40* __DECLARE_SCMI_QUIRK_ENTRY(fix_me),41* NULL42* };43*44* quirk.h45* -------46* DECLARE_SCMI_QUIRK(fix_me);47*48* <somewhere_in_the_scmi_stack.c>49* ------------------------------50*51* #define QUIRK_CODE_SNIPPET_FIX_ME() \52* ({ \53* if (p->condition) \54* a_ptr->calculated_val = 123; \55* })56*57*58* int some_function_to_fix(int param, struct something *p)59* {60* struct some_strut *a_ptr;61*62* a_ptr = some_load_func(p);63* SCMI_QUIRK(fix_me, QUIRK_CODE_SNIPPET_FIX_ME);64* some_more_func(a_ptr);65* ...66*67* return 0;68* }69*70*/7172#include <linux/ctype.h>73#include <linux/cleanup.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>8485#include "quirks.h"8687#define SCMI_QUIRKS_HT_SZ 48889struct scmi_quirk {90bool enabled;91const char *name;92const char *vendor;93const char *sub_vendor_id;94const char *impl_ver_range;95u32 start_range;96u32 end_range;97struct static_key_false *key;98struct hlist_node hash;99unsigned int hkey;100const char *const compats[];101};102103#define __DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ...) \104static 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}112113#define __DECLARE_SCMI_QUIRK_ENTRY(_qn) (&(scmi_quirk_entry_ ## _qn))114115/*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 global119* 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 be124* 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> <= Y133*134* ... : An optional variadic macros argument used to provide a comma-separated135* list of compatible strings matches; when no variadic argument is136* provided, ANY compatible will match this quirk.137*138* This implicitly define also a properly named global static-key that139* will be used to dynamically enable the quirk at initialization time.140*141* Note that it is possible to associate multiple quirks to the same142* matching pattern, if your firmware quality is really astounding :P143*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, ...) \157DEFINE_STATIC_KEY_FALSE(scmi_quirk_ ## _qn); \158__DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ##__VA_ARGS__)159160/*161* Same as DEFINE_SCMI_QUIRK but EXPORTED: this is meant to address quirks162* that possibly reside in code that is included in loadable kernel modules163* that needs to be able to access the global static keys at runtime to164* determine if enabled or not. (see SCMI_QUIRK to understand usage)165*/166#define DEFINE_SCMI_QUIRK_EXPORTED(_qn, _ven, _sub, _impl, ...) \167DEFINE_STATIC_KEY_FALSE(scmi_quirk_ ## _qn); \168EXPORT_SYMBOL_GPL(scmi_quirk_ ## _qn); \169__DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ##__VA_ARGS__)170171/* Global Quirks Definitions */172DEFINE_SCMI_QUIRK(clock_rates_triplet_out_of_spec, NULL, NULL, NULL);173DEFINE_SCMI_QUIRK(perf_level_get_fc_force, "Qualcomm", NULL, "0x20000-");174175/*176* Quirks Pointers Array177*178* This is filled at compile-time with the list of pointers to all the currently179* defined quirks descriptors.180*/181static 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),184NULL185};186187/*188* Quirks HashTable189*190* A run-time populated hashtable containing all the defined quirks descriptors191* hashed by matching pattern.192*/193static DEFINE_READ_MOSTLY_HASHTABLE(scmi_quirks_ht, SCMI_QUIRKS_HT_SZ);194195static unsigned int scmi_quirk_signature(const char *vend, const char *sub_vend)196{197char *signature, *p;198unsigned int hash32;199unsigned long hash = 0;200201/* vendor_id/sub_vendor_id guaranteed <= SCMI_SHORT_NAME_MAX_SIZE */202signature = kasprintf(GFP_KERNEL, "|%s|%s|", vend ?: "", sub_vend ?: "");203if (!signature)204return 0;205206pr_debug("SCMI Quirk Signature >>>%s<<<\n", signature);207208p = signature;209while (*p)210hash = partial_name_hash(tolower(*p++), hash);211hash32 = end_name_hash(hash);212213kfree(signature);214215return hash32;216}217218static int scmi_quirk_range_parse(struct scmi_quirk *quirk)219{220const char *last, *first __free(kfree) = NULL;221size_t len;222char *sep;223int ret;224225quirk->start_range = 0;226quirk->end_range = 0xFFFFFFFF;227len = quirk->impl_ver_range ? strlen(quirk->impl_ver_range) : 0;228if (!len)229return 0;230231first = kmemdup(quirk->impl_ver_range, len + 1, GFP_KERNEL);232if (!first)233return -ENOMEM;234235last = first + len - 1;236sep = strchr(first, '-');237if (sep)238*sep = '\0';239240if (sep == first) /* -X */241ret = kstrtouint(first + 1, 0, &quirk->end_range);242else /* X OR X- OR X-y */243ret = kstrtouint(first, 0, &quirk->start_range);244if (ret)245return ret;246247if (!sep)248quirk->end_range = quirk->start_range;249else if (sep != last) /* x-Y */250ret = kstrtouint(sep + 1, 0, &quirk->end_range);251252if (quirk->start_range > quirk->end_range)253return -EINVAL;254255return ret;256}257258void scmi_quirks_initialize(void)259{260struct scmi_quirk *quirk;261int i;262263for (i = 0, quirk = scmi_quirks_table[0]; quirk;264i++, quirk = scmi_quirks_table[i]) {265int ret;266267ret = scmi_quirk_range_parse(quirk);268if (ret) {269pr_err("SCMI skip QUIRK [%s] - BAD RANGE - |%s|\n",270quirk->name, quirk->impl_ver_range);271continue;272}273quirk->hkey = scmi_quirk_signature(quirk->vendor,274quirk->sub_vendor_id);275276hash_add(scmi_quirks_ht, &quirk->hash, quirk->hkey);277278pr_debug("Registered SCMI QUIRK [%s] -- %p - Key [0x%08X] - %s/%s/[0x%08X-0x%08X]\n",279quirk->name, quirk, quirk->hkey,280quirk->vendor, quirk->sub_vendor_id,281quirk->start_range, quirk->end_range);282}283284pr_debug("SCMI Quirks initialized\n");285}286287void scmi_quirks_enable(struct device *dev, const char *vend,288const char *subv, const u32 impl)289{290for (int i = 3; i >= 0; i--) {291struct scmi_quirk *quirk;292unsigned int hkey;293294hkey = scmi_quirk_signature(i > 1 ? vend : NULL,295i > 2 ? subv : NULL);296297/*298* Note that there could be multiple matches so we299* will enable multiple quirk part of a hash collision300* domain...BUT we cannot assume that ALL quirks on the301* same collision domain are a full match.302*/303hash_for_each_possible(scmi_quirks_ht, quirk, hash, hkey) {304if (quirk->enabled || quirk->hkey != hkey ||305impl < quirk->start_range ||306impl > quirk->end_range)307continue;308309if (quirk->compats[0] &&310!of_machine_compatible_match(quirk->compats))311continue;312313dev_info(dev, "Enabling SCMI Quirk [%s]\n",314quirk->name);315316dev_dbg(dev,317"Quirk matched on: %s/%s/%s/[0x%08X-0x%08X]\n",318quirk->compats[0], quirk->vendor,319quirk->sub_vendor_id,320quirk->start_range, quirk->end_range);321322static_branch_enable(quirk->key);323quirk->enabled = true;324}325}326}327328329