linux/drivers/firmware/smccc/soc_id.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright 2020 Arm Limited
   4 */
   5
   6#define pr_fmt(fmt) "SMCCC: SOC_ID: " fmt
   7
   8#include <linux/arm-smccc.h>
   9#include <linux/bitfield.h>
  10#include <linux/device.h>
  11#include <linux/module.h>
  12#include <linux/kernel.h>
  13#include <linux/slab.h>
  14#include <linux/sys_soc.h>
  15
  16#define SMCCC_SOC_ID_JEP106_BANK_IDX_MASK       GENMASK(30, 24)
  17/*
  18 * As per the SMC Calling Convention specification v1.2 (ARM DEN 0028C)
  19 * Section 7.4 SMCCC_ARCH_SOC_ID bits[23:16] are JEP-106 identification
  20 * code with parity bit for the SiP. We can drop the parity bit.
  21 */
  22#define SMCCC_SOC_ID_JEP106_ID_CODE_MASK        GENMASK(22, 16)
  23#define SMCCC_SOC_ID_IMP_DEF_SOC_ID_MASK        GENMASK(15, 0)
  24
  25#define JEP106_BANK_CONT_CODE(x)        \
  26        (u8)(FIELD_GET(SMCCC_SOC_ID_JEP106_BANK_IDX_MASK, (x)))
  27#define JEP106_ID_CODE(x)       \
  28        (u8)(FIELD_GET(SMCCC_SOC_ID_JEP106_ID_CODE_MASK, (x)))
  29#define IMP_DEF_SOC_ID(x)       \
  30        (u16)(FIELD_GET(SMCCC_SOC_ID_IMP_DEF_SOC_ID_MASK, (x)))
  31
  32static struct soc_device *soc_dev;
  33static struct soc_device_attribute *soc_dev_attr;
  34
  35static int __init smccc_soc_init(void)
  36{
  37        struct arm_smccc_res res;
  38        int soc_id_rev, soc_id_version;
  39        static char soc_id_str[20], soc_id_rev_str[12];
  40        static char soc_id_jep106_id_str[12];
  41
  42        if (arm_smccc_get_version() < ARM_SMCCC_VERSION_1_2)
  43                return 0;
  44
  45        if (arm_smccc_1_1_get_conduit() == SMCCC_CONDUIT_NONE) {
  46                pr_err("%s: invalid SMCCC conduit\n", __func__);
  47                return -EOPNOTSUPP;
  48        }
  49
  50        arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
  51                             ARM_SMCCC_ARCH_SOC_ID, &res);
  52
  53        if (res.a0 == SMCCC_RET_NOT_SUPPORTED) {
  54                pr_info("ARCH_SOC_ID not implemented, skipping ....\n");
  55                return 0;
  56        }
  57
  58        if ((int)res.a0 < 0) {
  59                pr_info("ARCH_FEATURES(ARCH_SOC_ID) returned error: %lx\n",
  60                        res.a0);
  61                return -EINVAL;
  62        }
  63
  64        arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_SOC_ID, 0, &res);
  65        if ((int)res.a0 < 0) {
  66                pr_err("ARCH_SOC_ID(0) returned error: %lx\n", res.a0);
  67                return -EINVAL;
  68        }
  69
  70        soc_id_version = res.a0;
  71
  72        arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_SOC_ID, 1, &res);
  73        if ((int)res.a0 < 0) {
  74                pr_err("ARCH_SOC_ID(1) returned error: %lx\n", res.a0);
  75                return -EINVAL;
  76        }
  77
  78        soc_id_rev = res.a0;
  79
  80        soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
  81        if (!soc_dev_attr)
  82                return -ENOMEM;
  83
  84        sprintf(soc_id_rev_str, "0x%08x", soc_id_rev);
  85        sprintf(soc_id_jep106_id_str, "jep106:%02x%02x",
  86                JEP106_BANK_CONT_CODE(soc_id_version),
  87                JEP106_ID_CODE(soc_id_version));
  88        sprintf(soc_id_str, "%s:%04x", soc_id_jep106_id_str,
  89                IMP_DEF_SOC_ID(soc_id_version));
  90
  91        soc_dev_attr->soc_id = soc_id_str;
  92        soc_dev_attr->revision = soc_id_rev_str;
  93        soc_dev_attr->family = soc_id_jep106_id_str;
  94
  95        soc_dev = soc_device_register(soc_dev_attr);
  96        if (IS_ERR(soc_dev)) {
  97                kfree(soc_dev_attr);
  98                return PTR_ERR(soc_dev);
  99        }
 100
 101        pr_info("ID = %s Revision = %s\n", soc_dev_attr->soc_id,
 102                soc_dev_attr->revision);
 103
 104        return 0;
 105}
 106module_init(smccc_soc_init);
 107
 108static void __exit smccc_soc_exit(void)
 109{
 110        if (soc_dev)
 111                soc_device_unregister(soc_dev);
 112        kfree(soc_dev_attr);
 113}
 114module_exit(smccc_soc_exit);
 115