mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-28 22:44:08 -04:00
Commit5f9c23abc4("firmware: smccc: Support optional Arm SMCCC SOC_ID name") introduced the SOC_ID name string call, which reports a human readable string describing the SoC, as returned by firmware. The SMCCC spec v1.6 describes this feature as AArch64 only, since we rely on 8 characters to be transmitted per register. Consequently the SMCCC call must use the AArch64 calling convention, which requires bit 30 of the FID to be set. The spec is a bit confusing here, since it mentions that in the parameter description ("2: SoC name (optionally implemented for SMC64 calls, ..."), but still prints the FID explicitly as 0x80000002. But as this FID is using the SMC32 calling convention (correct for the other two calls), it will not match what any SMCCC conformant firmware is expecting, so any call would return NOT_SUPPORTED. Add a 64-bit version of the ARCH_SOC_ID FID macro, and use that for the SoC name version of the call to fix the issue. Fixes:5f9c23abc4("firmware: smccc: Support optional Arm SMCCC SOC_ID name") Signed-off-by: Andre Przywara <andre.przywara@arm.com> Link: https://patch.msgid.link/20250902172053.304911-1-andre.przywara@arm.com Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
176 lines
5.0 KiB
C
176 lines
5.0 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright 2020 Arm Limited
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "SMCCC: SOC_ID: " fmt
|
|
|
|
#include <linux/arm-smccc.h>
|
|
#include <linux/bitfield.h>
|
|
#include <linux/device.h>
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/sys_soc.h>
|
|
|
|
#define SMCCC_SOC_ID_JEP106_BANK_IDX_MASK GENMASK(30, 24)
|
|
/*
|
|
* As per the SMC Calling Convention specification v1.2 (ARM DEN 0028C)
|
|
* Section 7.4 SMCCC_ARCH_SOC_ID bits[23:16] are JEP-106 identification
|
|
* code with parity bit for the SiP. We can drop the parity bit.
|
|
*/
|
|
#define SMCCC_SOC_ID_JEP106_ID_CODE_MASK GENMASK(22, 16)
|
|
#define SMCCC_SOC_ID_IMP_DEF_SOC_ID_MASK GENMASK(15, 0)
|
|
|
|
#define JEP106_BANK_CONT_CODE(x) \
|
|
(u8)(FIELD_GET(SMCCC_SOC_ID_JEP106_BANK_IDX_MASK, (x)))
|
|
#define JEP106_ID_CODE(x) \
|
|
(u8)(FIELD_GET(SMCCC_SOC_ID_JEP106_ID_CODE_MASK, (x)))
|
|
#define IMP_DEF_SOC_ID(x) \
|
|
(u16)(FIELD_GET(SMCCC_SOC_ID_IMP_DEF_SOC_ID_MASK, (x)))
|
|
|
|
static struct soc_device *soc_dev;
|
|
static struct soc_device_attribute *soc_dev_attr;
|
|
|
|
#ifdef CONFIG_ARM64
|
|
|
|
static char __ro_after_init smccc_soc_id_name[136] = "";
|
|
|
|
static inline void str_fragment_from_reg(char *dst, unsigned long reg)
|
|
{
|
|
dst[0] = (reg >> 0) & 0xff;
|
|
dst[1] = (reg >> 8) & 0xff;
|
|
dst[2] = (reg >> 16) & 0xff;
|
|
dst[3] = (reg >> 24) & 0xff;
|
|
dst[4] = (reg >> 32) & 0xff;
|
|
dst[5] = (reg >> 40) & 0xff;
|
|
dst[6] = (reg >> 48) & 0xff;
|
|
dst[7] = (reg >> 56) & 0xff;
|
|
}
|
|
|
|
static char __init *smccc_soc_name_init(void)
|
|
{
|
|
struct arm_smccc_1_2_regs args;
|
|
struct arm_smccc_1_2_regs res;
|
|
size_t len;
|
|
|
|
/*
|
|
* Issue Number 1.6 of the Arm SMC Calling Convention
|
|
* specification introduces an optional "name" string
|
|
* to the ARM_SMCCC_ARCH_SOC_ID function. Fetch it if
|
|
* available.
|
|
*/
|
|
args.a0 = ARM_SMCCC_ARCH_SOC_ID64;
|
|
args.a1 = 2; /* SOC_ID name */
|
|
arm_smccc_1_2_invoke(&args, &res);
|
|
|
|
if ((u32)res.a0 == 0) {
|
|
/*
|
|
* Copy res.a1..res.a17 to the smccc_soc_id_name string
|
|
* 8 bytes at a time. As per Issue 1.6 of the Arm SMC
|
|
* Calling Convention, the string will be NUL terminated
|
|
* and padded, from the end of the string to the end of the
|
|
* 136 byte buffer, with NULs.
|
|
*/
|
|
str_fragment_from_reg(smccc_soc_id_name + 8 * 0, res.a1);
|
|
str_fragment_from_reg(smccc_soc_id_name + 8 * 1, res.a2);
|
|
str_fragment_from_reg(smccc_soc_id_name + 8 * 2, res.a3);
|
|
str_fragment_from_reg(smccc_soc_id_name + 8 * 3, res.a4);
|
|
str_fragment_from_reg(smccc_soc_id_name + 8 * 4, res.a5);
|
|
str_fragment_from_reg(smccc_soc_id_name + 8 * 5, res.a6);
|
|
str_fragment_from_reg(smccc_soc_id_name + 8 * 6, res.a7);
|
|
str_fragment_from_reg(smccc_soc_id_name + 8 * 7, res.a8);
|
|
str_fragment_from_reg(smccc_soc_id_name + 8 * 8, res.a9);
|
|
str_fragment_from_reg(smccc_soc_id_name + 8 * 9, res.a10);
|
|
str_fragment_from_reg(smccc_soc_id_name + 8 * 10, res.a11);
|
|
str_fragment_from_reg(smccc_soc_id_name + 8 * 11, res.a12);
|
|
str_fragment_from_reg(smccc_soc_id_name + 8 * 12, res.a13);
|
|
str_fragment_from_reg(smccc_soc_id_name + 8 * 13, res.a14);
|
|
str_fragment_from_reg(smccc_soc_id_name + 8 * 14, res.a15);
|
|
str_fragment_from_reg(smccc_soc_id_name + 8 * 15, res.a16);
|
|
str_fragment_from_reg(smccc_soc_id_name + 8 * 16, res.a17);
|
|
|
|
len = strnlen(smccc_soc_id_name, sizeof(smccc_soc_id_name));
|
|
if (len) {
|
|
if (len == sizeof(smccc_soc_id_name))
|
|
pr_warn(FW_BUG "Ignoring improperly formatted name\n");
|
|
else
|
|
return smccc_soc_id_name;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#else
|
|
|
|
static char __init *smccc_soc_name_init(void)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
#endif
|
|
|
|
static int __init smccc_soc_init(void)
|
|
{
|
|
int soc_id_rev, soc_id_version;
|
|
static char soc_id_str[20], soc_id_rev_str[12];
|
|
static char soc_id_jep106_id_str[12];
|
|
|
|
if (arm_smccc_get_version() < ARM_SMCCC_VERSION_1_2)
|
|
return 0;
|
|
|
|
soc_id_version = arm_smccc_get_soc_id_version();
|
|
if (soc_id_version == SMCCC_RET_NOT_SUPPORTED) {
|
|
pr_info("ARCH_SOC_ID not implemented, skipping ....\n");
|
|
return 0;
|
|
}
|
|
|
|
if (soc_id_version < 0) {
|
|
pr_err("Invalid SoC Version: %x\n", soc_id_version);
|
|
return -EINVAL;
|
|
}
|
|
|
|
soc_id_rev = arm_smccc_get_soc_id_revision();
|
|
if (soc_id_rev < 0) {
|
|
pr_err("Invalid SoC Revision: %x\n", soc_id_rev);
|
|
return -EINVAL;
|
|
}
|
|
|
|
soc_dev_attr = kzalloc_obj(*soc_dev_attr);
|
|
if (!soc_dev_attr)
|
|
return -ENOMEM;
|
|
|
|
sprintf(soc_id_rev_str, "0x%08x", soc_id_rev);
|
|
sprintf(soc_id_jep106_id_str, "jep106:%02x%02x",
|
|
JEP106_BANK_CONT_CODE(soc_id_version),
|
|
JEP106_ID_CODE(soc_id_version));
|
|
sprintf(soc_id_str, "%s:%04x", soc_id_jep106_id_str,
|
|
IMP_DEF_SOC_ID(soc_id_version));
|
|
|
|
soc_dev_attr->soc_id = soc_id_str;
|
|
soc_dev_attr->revision = soc_id_rev_str;
|
|
soc_dev_attr->family = soc_id_jep106_id_str;
|
|
soc_dev_attr->machine = smccc_soc_name_init();
|
|
|
|
soc_dev = soc_device_register(soc_dev_attr);
|
|
if (IS_ERR(soc_dev)) {
|
|
kfree(soc_dev_attr);
|
|
return PTR_ERR(soc_dev);
|
|
}
|
|
|
|
pr_info("ID = %s Revision = %s\n", soc_dev_attr->soc_id,
|
|
soc_dev_attr->revision);
|
|
|
|
return 0;
|
|
}
|
|
module_init(smccc_soc_init);
|
|
|
|
static void __exit smccc_soc_exit(void)
|
|
{
|
|
if (soc_dev)
|
|
soc_device_unregister(soc_dev);
|
|
kfree(soc_dev_attr);
|
|
}
|
|
module_exit(smccc_soc_exit);
|