// 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/device.h>74#include <linux/export.h>75#include <linux/hashtable.h>76#include <linux/kstrtox.h>77#include <linux/of.h>78#include <linux/slab.h>79#include <linux/static_key.h>80#include <linux/string.h>81#include <linux/stringhash.h>82#include <linux/types.h>8384#include "quirks.h"8586#define SCMI_QUIRKS_HT_SZ 48788struct scmi_quirk {89bool enabled;90const char *name;91char *vendor;92char *sub_vendor_id;93char *impl_ver_range;94u32 start_range;95u32 end_range;96struct static_key_false *key;97struct hlist_node hash;98unsigned int hkey;99const char *const compats[];100};101102#define __DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ...) \103static struct scmi_quirk scmi_quirk_entry_ ## _qn = { \104.name = __stringify(quirk_ ## _qn), \105.vendor = _ven, \106.sub_vendor_id = _sub, \107.impl_ver_range = _impl, \108.key = &(scmi_quirk_ ## _qn), \109.compats = { __VA_ARGS__ __VA_OPT__(,) NULL }, \110}111112#define __DECLARE_SCMI_QUIRK_ENTRY(_qn) (&(scmi_quirk_entry_ ## _qn))113114/*115* Define a quirk by name and provide the matching tokens where:116*117* _qn: A string which will be used to build the quirk and the global118* static_key names.119* _ven : SCMI Vendor ID string match, NULL means any.120* _sub : SCMI SubVendor ID string match, NULL means any.121* _impl : SCMI Implementation Version string match, NULL means any.122* This string can be used to express version ranges which will be123* interpreted as follows:124*125* NULL [0, 0xFFFFFFFF]126* "X" [X, X]127* "X-" [X, 0xFFFFFFFF]128* "-X" [0, X]129* "X-Y" [X, Y]130*131* with X <= Y and <v> in [X, Y] meaning X <= <v> <= Y132*133* ... : An optional variadic macros argument used to provide a comma-separated134* list of compatible strings matches; when no variadic argument is135* provided, ANY compatible will match this quirk.136*137* This implicitly define also a properly named global static-key that138* will be used to dynamically enable the quirk at initialization time.139*140* Note that it is possible to associate multiple quirks to the same141* matching pattern, if your firmware quality is really astounding :P142*143* Example:144*145* Compatibles list NOT provided, so ANY compatible will match:146*147* DEFINE_SCMI_QUIRK(my_new_issue, "Vend", "SVend", "0x12000-0x30000");148*149*150* A few compatibles provided to match against:151*152* DEFINE_SCMI_QUIRK(my_new_issue, "Vend", "SVend", "0x12000-0x30000",153* "xvend,plat_a", "xvend,plat_b", "xvend,sku_name");154*/155#define DEFINE_SCMI_QUIRK(_qn, _ven, _sub, _impl, ...) \156DEFINE_STATIC_KEY_FALSE(scmi_quirk_ ## _qn); \157__DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ##__VA_ARGS__)158159/*160* Same as DEFINE_SCMI_QUIRK but EXPORTED: this is meant to address quirks161* that possibly reside in code that is included in loadable kernel modules162* that needs to be able to access the global static keys at runtime to163* determine if enabled or not. (see SCMI_QUIRK to understand usage)164*/165#define DEFINE_SCMI_QUIRK_EXPORTED(_qn, _ven, _sub, _impl, ...) \166DEFINE_STATIC_KEY_FALSE(scmi_quirk_ ## _qn); \167EXPORT_SYMBOL_GPL(scmi_quirk_ ## _qn); \168__DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ##__VA_ARGS__)169170/* Global Quirks Definitions */171DEFINE_SCMI_QUIRK(clock_rates_triplet_out_of_spec, NULL, NULL, NULL);172DEFINE_SCMI_QUIRK(perf_level_get_fc_force, "Qualcomm", NULL, "0x20000-");173174/*175* Quirks Pointers Array176*177* This is filled at compile-time with the list of pointers to all the currently178* defined quirks descriptors.179*/180static struct scmi_quirk *scmi_quirks_table[] = {181__DECLARE_SCMI_QUIRK_ENTRY(clock_rates_triplet_out_of_spec),182__DECLARE_SCMI_QUIRK_ENTRY(perf_level_get_fc_force),183NULL184};185186/*187* Quirks HashTable188*189* A run-time populated hashtable containing all the defined quirks descriptors190* hashed by matching pattern.191*/192static DEFINE_READ_MOSTLY_HASHTABLE(scmi_quirks_ht, SCMI_QUIRKS_HT_SZ);193194static unsigned int scmi_quirk_signature(const char *vend, const char *sub_vend)195{196char *signature, *p;197unsigned int hash32;198unsigned long hash = 0;199200/* vendor_id/sub_vendor_id guaranteed <= SCMI_SHORT_NAME_MAX_SIZE */201signature = kasprintf(GFP_KERNEL, "|%s|%s|", vend ?: "", sub_vend ?: "");202if (!signature)203return 0;204205pr_debug("SCMI Quirk Signature >>>%s<<<\n", signature);206207p = signature;208while (*p)209hash = partial_name_hash(tolower(*p++), hash);210hash32 = end_name_hash(hash);211212kfree(signature);213214return hash32;215}216217static int scmi_quirk_range_parse(struct scmi_quirk *quirk)218{219const char *last, *first = quirk->impl_ver_range;220size_t len;221char *sep;222int ret;223224quirk->start_range = 0;225quirk->end_range = 0xFFFFFFFF;226len = quirk->impl_ver_range ? strlen(quirk->impl_ver_range) : 0;227if (!len)228return 0;229230last = first + len - 1;231sep = strchr(quirk->impl_ver_range, '-');232if (sep)233*sep = '\0';234235if (sep == first) /* -X */236ret = kstrtouint(first + 1, 0, &quirk->end_range);237else /* X OR X- OR X-y */238ret = kstrtouint(first, 0, &quirk->start_range);239if (ret)240return ret;241242if (!sep)243quirk->end_range = quirk->start_range;244else if (sep != last) /* x-Y */245ret = kstrtouint(sep + 1, 0, &quirk->end_range);246247if (quirk->start_range > quirk->end_range)248return -EINVAL;249250return ret;251}252253void scmi_quirks_initialize(void)254{255struct scmi_quirk *quirk;256int i;257258for (i = 0, quirk = scmi_quirks_table[0]; quirk;259i++, quirk = scmi_quirks_table[i]) {260int ret;261262ret = scmi_quirk_range_parse(quirk);263if (ret) {264pr_err("SCMI skip QUIRK [%s] - BAD RANGE - |%s|\n",265quirk->name, quirk->impl_ver_range);266continue;267}268quirk->hkey = scmi_quirk_signature(quirk->vendor,269quirk->sub_vendor_id);270271hash_add(scmi_quirks_ht, &quirk->hash, quirk->hkey);272273pr_debug("Registered SCMI QUIRK [%s] -- %p - Key [0x%08X] - %s/%s/[0x%08X-0x%08X]\n",274quirk->name, quirk, quirk->hkey,275quirk->vendor, quirk->sub_vendor_id,276quirk->start_range, quirk->end_range);277}278279pr_debug("SCMI Quirks initialized\n");280}281282void scmi_quirks_enable(struct device *dev, const char *vend,283const char *subv, const u32 impl)284{285for (int i = 3; i >= 0; i--) {286struct scmi_quirk *quirk;287unsigned int hkey;288289hkey = scmi_quirk_signature(i > 1 ? vend : NULL,290i > 2 ? subv : NULL);291292/*293* Note that there could be multiple matches so we294* will enable multiple quirk part of a hash collision295* domain...BUT we cannot assume that ALL quirks on the296* same collision domain are a full match.297*/298hash_for_each_possible(scmi_quirks_ht, quirk, hash, hkey) {299if (quirk->enabled || quirk->hkey != hkey ||300impl < quirk->start_range ||301impl > quirk->end_range)302continue;303304if (quirk->compats[0] &&305!of_machine_compatible_match(quirk->compats))306continue;307308dev_info(dev, "Enabling SCMI Quirk [%s]\n",309quirk->name);310311dev_dbg(dev,312"Quirk matched on: %s/%s/%s/[0x%08X-0x%08X]\n",313quirk->compats[0], quirk->vendor,314quirk->sub_vendor_id,315quirk->start_range, quirk->end_range);316317static_branch_enable(quirk->key);318quirk->enabled = true;319}320}321}322323324