linux/drivers/firmware/imx/imx-scu-soc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright 2019 NXP.
   4 */
   5
   6#include <dt-bindings/firmware/imx/rsrc.h>
   7#include <linux/firmware/imx/sci.h>
   8#include <linux/slab.h>
   9#include <linux/sys_soc.h>
  10#include <linux/platform_device.h>
  11#include <linux/of.h>
  12
  13static struct imx_sc_ipc *imx_sc_soc_ipc_handle;
  14
  15struct imx_sc_msg_misc_get_soc_id {
  16        struct imx_sc_rpc_msg hdr;
  17        union {
  18                struct {
  19                        u32 control;
  20                        u16 resource;
  21                } __packed req;
  22                struct {
  23                        u32 id;
  24                } resp;
  25        } data;
  26} __packed __aligned(4);
  27
  28struct imx_sc_msg_misc_get_soc_uid {
  29        struct imx_sc_rpc_msg hdr;
  30        u32 uid_low;
  31        u32 uid_high;
  32} __packed;
  33
  34static int imx_scu_soc_uid(u64 *soc_uid)
  35{
  36        struct imx_sc_msg_misc_get_soc_uid msg;
  37        struct imx_sc_rpc_msg *hdr = &msg.hdr;
  38        int ret;
  39
  40        hdr->ver = IMX_SC_RPC_VERSION;
  41        hdr->svc = IMX_SC_RPC_SVC_MISC;
  42        hdr->func = IMX_SC_MISC_FUNC_UNIQUE_ID;
  43        hdr->size = 1;
  44
  45        ret = imx_scu_call_rpc(imx_sc_soc_ipc_handle, &msg, true);
  46        if (ret) {
  47                pr_err("%s: get soc uid failed, ret %d\n", __func__, ret);
  48                return ret;
  49        }
  50
  51        *soc_uid = msg.uid_high;
  52        *soc_uid <<= 32;
  53        *soc_uid |= msg.uid_low;
  54
  55        return 0;
  56}
  57
  58static int imx_scu_soc_id(void)
  59{
  60        struct imx_sc_msg_misc_get_soc_id msg;
  61        struct imx_sc_rpc_msg *hdr = &msg.hdr;
  62        int ret;
  63
  64        hdr->ver = IMX_SC_RPC_VERSION;
  65        hdr->svc = IMX_SC_RPC_SVC_MISC;
  66        hdr->func = IMX_SC_MISC_FUNC_GET_CONTROL;
  67        hdr->size = 3;
  68
  69        msg.data.req.control = IMX_SC_C_ID;
  70        msg.data.req.resource = IMX_SC_R_SYSTEM;
  71
  72        ret = imx_scu_call_rpc(imx_sc_soc_ipc_handle, &msg, true);
  73        if (ret) {
  74                pr_err("%s: get soc info failed, ret %d\n", __func__, ret);
  75                return ret;
  76        }
  77
  78        return msg.data.resp.id;
  79}
  80
  81int imx_scu_soc_init(struct device *dev)
  82{
  83        struct soc_device_attribute *soc_dev_attr;
  84        struct soc_device *soc_dev;
  85        int id, ret;
  86        u64 uid = 0;
  87        u32 val;
  88
  89        ret = imx_scu_get_handle(&imx_sc_soc_ipc_handle);
  90        if (ret)
  91                return ret;
  92
  93        soc_dev_attr = devm_kzalloc(dev, sizeof(*soc_dev_attr),
  94                                    GFP_KERNEL);
  95        if (!soc_dev_attr)
  96                return -ENOMEM;
  97
  98        soc_dev_attr->family = "Freescale i.MX";
  99
 100        ret = of_property_read_string(of_root,
 101                                      "model",
 102                                      &soc_dev_attr->machine);
 103        if (ret)
 104                return ret;
 105
 106        id = imx_scu_soc_id();
 107        if (id < 0)
 108                return -EINVAL;
 109
 110        ret = imx_scu_soc_uid(&uid);
 111        if (ret < 0)
 112                return -EINVAL;
 113
 114        /* format soc_id value passed from SCU firmware */
 115        val = id & 0x1f;
 116        soc_dev_attr->soc_id = devm_kasprintf(dev, GFP_KERNEL, "0x%x", val);
 117        if (!soc_dev_attr->soc_id)
 118                return -ENOMEM;
 119
 120        /* format revision value passed from SCU firmware */
 121        val = (id >> 5) & 0xf;
 122        val = (((val >> 2) + 1) << 4) | (val & 0x3);
 123        soc_dev_attr->revision = devm_kasprintf(dev, GFP_KERNEL, "%d.%d",
 124                                                (val >> 4) & 0xf, val & 0xf);
 125        if (!soc_dev_attr->revision)
 126                return -ENOMEM;
 127
 128        soc_dev_attr->serial_number = devm_kasprintf(dev, GFP_KERNEL,
 129                                                     "%016llX", uid);
 130        if (!soc_dev_attr->serial_number)
 131                return -ENOMEM;
 132
 133        soc_dev = soc_device_register(soc_dev_attr);
 134        if (IS_ERR(soc_dev))
 135                return PTR_ERR(soc_dev);
 136
 137        return 0;
 138}
 139