2019-03-22 16:49:20 +00:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2019 NXP .
*/
# include <linux/init.h>
# include <linux/io.h>
# include <linux/of_address.h>
# include <linux/slab.h>
# include <linux/sys_soc.h>
# include <linux/platform_device.h>
2019-10-30 01:17:39 +02:00
# include <linux/arm-smccc.h>
2019-03-22 16:49:20 +00:00
# include <linux/of.h>
# define REV_B1 0x21
# define IMX8MQ_SW_INFO_B1 0x40
# define IMX8MQ_SW_MAGIC_B1 0xff0055aa
2019-10-30 01:17:39 +02:00
# define IMX_SIP_GET_SOC_INFO 0xc2000006
2019-07-18 15:13:11 +08:00
# define OCOTP_UID_LOW 0x410
# define OCOTP_UID_HIGH 0x420
2019-05-13 11:22:13 +00:00
/* Same as ANADIG_DIGPROG_IMX7D */
# define ANADIG_DIGPROG_IMX8MM 0x800
2019-03-22 16:49:20 +00:00
struct imx8_soc_data {
char * name ;
u32 ( * soc_revision ) ( void ) ;
} ;
2019-07-18 15:13:11 +08:00
static u64 soc_uid ;
2019-10-30 01:17:39 +02:00
# ifdef CONFIG_HAVE_ARM_SMCCC
static u32 imx8mq_soc_revision_from_atf ( void )
{
struct arm_smccc_res res ;
arm_smccc_smc ( IMX_SIP_GET_SOC_INFO , 0 , 0 , 0 , 0 , 0 , 0 , 0 , & res ) ;
if ( res . a0 = = SMCCC_RET_NOT_SUPPORTED )
return 0 ;
else
return res . a0 & 0xff ;
}
# else
static inline u32 imx8mq_soc_revision_from_atf ( void ) { return 0 ; } ;
# endif
2019-03-22 16:49:20 +00:00
static u32 __init imx8mq_soc_revision ( void )
{
struct device_node * np ;
void __iomem * ocotp_base ;
u32 magic ;
2020-03-17 09:37:33 +08:00
u32 rev ;
2019-03-22 16:49:20 +00:00
np = of_find_compatible_node ( NULL , NULL , " fsl,imx8mq-ocotp " ) ;
if ( ! np )
2020-03-17 09:37:33 +08:00
return 0 ;
2019-03-22 16:49:20 +00:00
ocotp_base = of_iomap ( np , 0 ) ;
WARN_ON ( ! ocotp_base ) ;
2019-10-30 01:17:39 +02:00
/*
* SOC revision on older imx8mq is not available in fuses so query
* the value from ATF instead .
*/
rev = imx8mq_soc_revision_from_atf ( ) ;
if ( ! rev ) {
magic = readl_relaxed ( ocotp_base + IMX8MQ_SW_INFO_B1 ) ;
if ( magic = = IMX8MQ_SW_MAGIC_B1 )
rev = REV_B1 ;
}
2019-03-22 16:49:20 +00:00
2019-07-18 15:13:11 +08:00
soc_uid = readl_relaxed ( ocotp_base + OCOTP_UID_HIGH ) ;
soc_uid < < = 32 ;
soc_uid | = readl_relaxed ( ocotp_base + OCOTP_UID_LOW ) ;
2019-03-22 16:49:20 +00:00
iounmap ( ocotp_base ) ;
of_node_put ( np ) ;
2020-03-17 09:37:33 +08:00
2019-03-22 16:49:20 +00:00
return rev ;
}
2019-06-26 15:44:15 +08:00
static void __init imx8mm_soc_uid ( void )
{
void __iomem * ocotp_base ;
struct device_node * np ;
np = of_find_compatible_node ( NULL , NULL , " fsl,imx8mm-ocotp " ) ;
if ( ! np )
return ;
ocotp_base = of_iomap ( np , 0 ) ;
WARN_ON ( ! ocotp_base ) ;
soc_uid = readl_relaxed ( ocotp_base + OCOTP_UID_HIGH ) ;
soc_uid < < = 32 ;
soc_uid | = readl_relaxed ( ocotp_base + OCOTP_UID_LOW ) ;
iounmap ( ocotp_base ) ;
of_node_put ( np ) ;
}
2019-05-13 11:22:13 +00:00
static u32 __init imx8mm_soc_revision ( void )
{
struct device_node * np ;
void __iomem * anatop_base ;
u32 rev ;
np = of_find_compatible_node ( NULL , NULL , " fsl,imx8mm-anatop " ) ;
if ( ! np )
return 0 ;
anatop_base = of_iomap ( np , 0 ) ;
WARN_ON ( ! anatop_base ) ;
rev = readl_relaxed ( anatop_base + ANADIG_DIGPROG_IMX8MM ) ;
iounmap ( anatop_base ) ;
of_node_put ( np ) ;
2019-06-26 15:44:15 +08:00
imx8mm_soc_uid ( ) ;
2019-05-13 11:22:13 +00:00
return rev ;
}
2019-03-22 16:49:20 +00:00
static const struct imx8_soc_data imx8mq_soc_data = {
. name = " i.MX8MQ " ,
. soc_revision = imx8mq_soc_revision ,
} ;
2019-05-13 11:22:13 +00:00
static const struct imx8_soc_data imx8mm_soc_data = {
. name = " i.MX8MM " ,
. soc_revision = imx8mm_soc_revision ,
} ;
2019-06-19 09:07:08 +08:00
static const struct imx8_soc_data imx8mn_soc_data = {
. name = " i.MX8MN " ,
. soc_revision = imx8mm_soc_revision ,
} ;
2019-12-26 14:40:02 +08:00
static const struct imx8_soc_data imx8mp_soc_data = {
. name = " i.MX8MP " ,
. soc_revision = imx8mm_soc_revision ,
} ;
2019-03-22 16:49:20 +00:00
static const struct of_device_id imx8_soc_match [ ] = {
{ . compatible = " fsl,imx8mq " , . data = & imx8mq_soc_data , } ,
2019-05-13 11:22:13 +00:00
{ . compatible = " fsl,imx8mm " , . data = & imx8mm_soc_data , } ,
2019-06-19 09:07:08 +08:00
{ . compatible = " fsl,imx8mn " , . data = & imx8mn_soc_data , } ,
2019-12-26 14:40:02 +08:00
{ . compatible = " fsl,imx8mp " , . data = & imx8mp_soc_data , } ,
2019-03-22 16:49:20 +00:00
{ }
} ;
# define imx8_revision(soc_rev) \
soc_rev ? \
kasprintf ( GFP_KERNEL , " %d.%d " , ( soc_rev > > 4 ) & 0xf , soc_rev & 0xf ) : \
" unknown "
static int __init imx8_soc_init ( void )
{
struct soc_device_attribute * soc_dev_attr ;
struct soc_device * soc_dev ;
const struct of_device_id * id ;
u32 soc_rev = 0 ;
const struct imx8_soc_data * data ;
int ret ;
soc_dev_attr = kzalloc ( sizeof ( * soc_dev_attr ) , GFP_KERNEL ) ;
if ( ! soc_dev_attr )
2019-05-24 13:51:01 +08:00
return - ENOMEM ;
2019-03-22 16:49:20 +00:00
soc_dev_attr - > family = " Freescale i.MX " ;
2019-06-18 17:43:38 +08:00
ret = of_property_read_string ( of_root , " model " , & soc_dev_attr - > machine ) ;
2019-03-22 16:49:20 +00:00
if ( ret )
goto free_soc ;
2019-06-18 17:43:38 +08:00
id = of_match_node ( imx8_soc_match , of_root ) ;
2019-05-24 13:51:01 +08:00
if ( ! id ) {
ret = - ENODEV ;
2019-03-22 16:49:20 +00:00
goto free_soc ;
2019-05-24 13:51:01 +08:00
}
2019-03-22 16:49:20 +00:00
data = id - > data ;
if ( data ) {
soc_dev_attr - > soc_id = data - > name ;
if ( data - > soc_revision )
soc_rev = data - > soc_revision ( ) ;
}
soc_dev_attr - > revision = imx8_revision ( soc_rev ) ;
2019-05-24 13:51:01 +08:00
if ( ! soc_dev_attr - > revision ) {
ret = - ENOMEM ;
2019-03-22 16:49:20 +00:00
goto free_soc ;
2019-05-24 13:51:01 +08:00
}
2019-03-22 16:49:20 +00:00
2019-10-25 14:56:22 +08:00
soc_dev_attr - > serial_number = kasprintf ( GFP_KERNEL , " %016llX " , soc_uid ) ;
if ( ! soc_dev_attr - > serial_number ) {
ret = - ENOMEM ;
goto free_rev ;
}
2019-03-22 16:49:20 +00:00
soc_dev = soc_device_register ( soc_dev_attr ) ;
2019-05-24 13:51:01 +08:00
if ( IS_ERR ( soc_dev ) ) {
ret = PTR_ERR ( soc_dev ) ;
2019-10-25 14:56:22 +08:00
goto free_serial_number ;
2019-05-24 13:51:01 +08:00
}
2019-03-22 16:49:20 +00:00
2019-12-09 15:20:55 +08:00
pr_info ( " SoC: %s revision %s \n " , soc_dev_attr - > soc_id ,
soc_dev_attr - > revision ) ;
2019-05-13 11:01:38 +00:00
if ( IS_ENABLED ( CONFIG_ARM_IMX_CPUFREQ_DT ) )
platform_device_register_simple ( " imx-cpufreq-dt " , - 1 , NULL , 0 ) ;
2019-03-22 16:49:20 +00:00
return 0 ;
2019-10-25 14:56:22 +08:00
free_serial_number :
kfree ( soc_dev_attr - > serial_number ) ;
2019-03-22 16:49:20 +00:00
free_rev :
2019-06-14 16:07:47 +08:00
if ( strcmp ( soc_dev_attr - > revision , " unknown " ) )
kfree ( soc_dev_attr - > revision ) ;
2019-03-22 16:49:20 +00:00
free_soc :
kfree ( soc_dev_attr ) ;
2019-05-24 13:51:01 +08:00
return ret ;
2019-03-22 16:49:20 +00:00
}
device_initcall ( imx8_soc_init ) ;