2019-05-24 01:52:36 +00:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2019 NXP .
*/
# include <dt-bindings/firmware/imx/rsrc.h>
# include <linux/firmware/imx/sci.h>
# include <linux/slab.h>
# include <linux/sys_soc.h>
# include <linux/platform_device.h>
# include <linux/of.h>
# define IMX_SCU_SOC_DRIVER_NAME "imx-scu-soc"
static struct imx_sc_ipc * soc_ipc_handle ;
struct imx_sc_msg_misc_get_soc_id {
struct imx_sc_rpc_msg hdr ;
union {
struct {
u32 control ;
u16 resource ;
} __packed req ;
struct {
u32 id ;
} resp ;
} data ;
} __packed ;
2019-07-02 15:45:45 +08:00
struct imx_sc_msg_misc_get_soc_uid {
struct imx_sc_rpc_msg hdr ;
u32 uid_low ;
u32 uid_high ;
} __packed ;
static ssize_t soc_uid_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct imx_sc_msg_misc_get_soc_uid msg ;
struct imx_sc_rpc_msg * hdr = & msg . hdr ;
u64 soc_uid ;
int ret ;
hdr - > ver = IMX_SC_RPC_VERSION ;
hdr - > svc = IMX_SC_RPC_SVC_MISC ;
hdr - > func = IMX_SC_MISC_FUNC_UNIQUE_ID ;
hdr - > size = 1 ;
ret = imx_scu_call_rpc ( soc_ipc_handle , & msg , false ) ;
if ( ret ) {
pr_err ( " %s: get soc uid failed, ret %d \n " , __func__ , ret ) ;
return ret ;
}
soc_uid = msg . uid_high ;
soc_uid < < = 32 ;
soc_uid | = msg . uid_low ;
return sprintf ( buf , " %016llX \n " , soc_uid ) ;
}
static DEVICE_ATTR_RO ( soc_uid ) ;
2019-05-24 01:52:36 +00:00
static int imx_scu_soc_id ( void )
{
struct imx_sc_msg_misc_get_soc_id msg ;
struct imx_sc_rpc_msg * hdr = & msg . hdr ;
int ret ;
hdr - > ver = IMX_SC_RPC_VERSION ;
hdr - > svc = IMX_SC_RPC_SVC_MISC ;
hdr - > func = IMX_SC_MISC_FUNC_GET_CONTROL ;
hdr - > size = 3 ;
msg . data . req . control = IMX_SC_C_ID ;
msg . data . req . resource = IMX_SC_R_SYSTEM ;
ret = imx_scu_call_rpc ( soc_ipc_handle , & msg , true ) ;
if ( ret ) {
pr_err ( " %s: get soc info failed, ret %d \n " , __func__ , ret ) ;
return ret ;
}
return msg . data . resp . id ;
}
static int imx_scu_soc_probe ( struct platform_device * pdev )
{
struct soc_device_attribute * soc_dev_attr ;
struct soc_device * soc_dev ;
int id , ret ;
u32 val ;
ret = imx_scu_get_handle ( & soc_ipc_handle ) ;
if ( ret )
return ret ;
soc_dev_attr = devm_kzalloc ( & pdev - > dev , sizeof ( * soc_dev_attr ) ,
GFP_KERNEL ) ;
if ( ! soc_dev_attr )
return - ENOMEM ;
soc_dev_attr - > family = " Freescale i.MX " ;
ret = of_property_read_string ( of_root ,
" model " ,
& soc_dev_attr - > machine ) ;
if ( ret )
return ret ;
id = imx_scu_soc_id ( ) ;
if ( id < 0 )
return - EINVAL ;
/* format soc_id value passed from SCU firmware */
val = id & 0x1f ;
soc_dev_attr - > soc_id = kasprintf ( GFP_KERNEL , " 0x%x " , val ) ;
if ( ! soc_dev_attr - > soc_id )
return - ENOMEM ;
/* format revision value passed from SCU firmware */
val = ( id > > 5 ) & 0xf ;
val = ( ( ( val > > 2 ) + 1 ) < < 4 ) | ( val & 0x3 ) ;
soc_dev_attr - > revision = kasprintf ( GFP_KERNEL ,
" %d.%d " ,
( val > > 4 ) & 0xf ,
val & 0xf ) ;
if ( ! soc_dev_attr - > revision ) {
ret = - ENOMEM ;
goto free_soc_id ;
}
soc_dev = soc_device_register ( soc_dev_attr ) ;
if ( IS_ERR ( soc_dev ) ) {
ret = PTR_ERR ( soc_dev ) ;
goto free_revision ;
}
2019-07-02 15:45:45 +08:00
ret = device_create_file ( soc_device_to_device ( soc_dev ) ,
& dev_attr_soc_uid ) ;
if ( ret )
goto free_revision ;
2019-05-24 01:52:36 +00:00
return 0 ;
free_revision :
kfree ( soc_dev_attr - > revision ) ;
free_soc_id :
kfree ( soc_dev_attr - > soc_id ) ;
return ret ;
}
static struct platform_driver imx_scu_soc_driver = {
. driver = {
. name = IMX_SCU_SOC_DRIVER_NAME ,
} ,
. probe = imx_scu_soc_probe ,
} ;
static int __init imx_scu_soc_init ( void )
{
struct platform_device * pdev ;
struct device_node * np ;
int ret ;
np = of_find_compatible_node ( NULL , NULL , " fsl,imx-scu " ) ;
if ( ! np )
return - ENODEV ;
of_node_put ( np ) ;
ret = platform_driver_register ( & imx_scu_soc_driver ) ;
if ( ret )
return ret ;
pdev = platform_device_register_simple ( IMX_SCU_SOC_DRIVER_NAME ,
- 1 , NULL , 0 ) ;
if ( IS_ERR ( pdev ) )
platform_driver_unregister ( & imx_scu_soc_driver ) ;
return PTR_ERR_OR_ZERO ( pdev ) ;
}
device_initcall ( imx_scu_soc_init ) ;