2019-07-24 04:05:13 +05:30
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( c ) 2009 - 2017 , The Linux Foundation . All rights reserved .
* Copyright ( c ) 2017 - 2019 , Linaro Ltd .
*/
2019-07-24 04:05:14 +05:30
# include <linux/debugfs.h>
2019-07-24 04:05:13 +05:30
# include <linux/err.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/random.h>
# include <linux/slab.h>
# include <linux/soc/qcom/smem.h>
# include <linux/string.h>
# include <linux/sys_soc.h>
# include <linux/types.h>
/*
* SoC version type with major number in the upper 16 bits and minor
* number in the lower 16 bits .
*/
# define SOCINFO_MAJOR(ver) (((ver) >> 16) & 0xffff)
# define SOCINFO_MINOR(ver) ((ver) & 0xffff)
2019-07-24 04:05:14 +05:30
# define SOCINFO_VERSION(maj, min) ((((maj) & 0xffff) << 16)|((min) & 0xffff))
2019-07-24 04:05:13 +05:30
# define SMEM_SOCINFO_BUILD_ID_LENGTH 32
/*
* SMEM item id , used to acquire handles to respective
* SMEM region .
*/
# define SMEM_HW_SW_BUILD_ID 137
2019-07-24 04:05:14 +05:30
# ifdef CONFIG_DEBUG_FS
2019-07-24 04:05:15 +05:30
# define SMEM_IMAGE_VERSION_BLOCKS_COUNT 32
# define SMEM_IMAGE_VERSION_SIZE 4096
# define SMEM_IMAGE_VERSION_NAME_SIZE 75
# define SMEM_IMAGE_VERSION_VARIANT_SIZE 20
# define SMEM_IMAGE_VERSION_OEM_SIZE 32
/*
* SMEM Image table indices
*/
# define SMEM_IMAGE_TABLE_BOOT_INDEX 0
# define SMEM_IMAGE_TABLE_TZ_INDEX 1
# define SMEM_IMAGE_TABLE_RPM_INDEX 3
# define SMEM_IMAGE_TABLE_APPS_INDEX 10
# define SMEM_IMAGE_TABLE_MPSS_INDEX 11
# define SMEM_IMAGE_TABLE_ADSP_INDEX 12
# define SMEM_IMAGE_TABLE_CNSS_INDEX 13
# define SMEM_IMAGE_TABLE_VIDEO_INDEX 14
# define SMEM_IMAGE_VERSION_TABLE 469
/*
* SMEM Image table names
*/
static const char * const socinfo_image_names [ ] = {
[ SMEM_IMAGE_TABLE_ADSP_INDEX ] = " adsp " ,
[ SMEM_IMAGE_TABLE_APPS_INDEX ] = " apps " ,
[ SMEM_IMAGE_TABLE_BOOT_INDEX ] = " boot " ,
[ SMEM_IMAGE_TABLE_CNSS_INDEX ] = " cnss " ,
[ SMEM_IMAGE_TABLE_MPSS_INDEX ] = " mpss " ,
[ SMEM_IMAGE_TABLE_RPM_INDEX ] = " rpm " ,
[ SMEM_IMAGE_TABLE_TZ_INDEX ] = " tz " ,
[ SMEM_IMAGE_TABLE_VIDEO_INDEX ] = " video " ,
} ;
2019-07-24 04:05:14 +05:30
static const char * const pmic_models [ ] = {
[ 0 ] = " Unknown PMIC model " ,
[ 9 ] = " PM8994 " ,
[ 11 ] = " PM8916 " ,
[ 13 ] = " PM8058 " ,
[ 14 ] = " PM8028 " ,
[ 15 ] = " PM8901 " ,
[ 16 ] = " PM8027 " ,
[ 17 ] = " ISL9519 " ,
[ 18 ] = " PM8921 " ,
[ 19 ] = " PM8018 " ,
[ 20 ] = " PM8015 " ,
[ 21 ] = " PM8014 " ,
[ 22 ] = " PM8821 " ,
[ 23 ] = " PM8038 " ,
[ 24 ] = " PM8922 " ,
[ 25 ] = " PM8917 " ,
} ;
# endif /* CONFIG_DEBUG_FS */
2019-07-24 04:05:13 +05:30
/* Socinfo SMEM item structure */
struct socinfo {
__le32 fmt ;
__le32 id ;
__le32 ver ;
char build_id [ SMEM_SOCINFO_BUILD_ID_LENGTH ] ;
/* Version 2 */
__le32 raw_id ;
__le32 raw_ver ;
/* Version 3 */
__le32 hw_plat ;
/* Version 4 */
__le32 plat_ver ;
/* Version 5 */
__le32 accessory_chip ;
/* Version 6 */
__le32 hw_plat_subtype ;
/* Version 7 */
__le32 pmic_model ;
__le32 pmic_die_rev ;
/* Version 8 */
__le32 pmic_model_1 ;
__le32 pmic_die_rev_1 ;
__le32 pmic_model_2 ;
__le32 pmic_die_rev_2 ;
/* Version 9 */
__le32 foundry_id ;
/* Version 10 */
__le32 serial_num ;
/* Version 11 */
__le32 num_pmics ;
__le32 pmic_array_offset ;
/* Version 12 */
__le32 chip_family ;
__le32 raw_device_family ;
__le32 raw_device_num ;
} ;
2019-07-24 04:05:14 +05:30
# ifdef CONFIG_DEBUG_FS
struct socinfo_params {
u32 raw_device_family ;
u32 hw_plat_subtype ;
u32 accessory_chip ;
u32 raw_device_num ;
u32 chip_family ;
u32 foundry_id ;
u32 plat_ver ;
u32 raw_ver ;
u32 hw_plat ;
u32 fmt ;
} ;
2019-07-24 04:05:15 +05:30
struct smem_image_version {
char name [ SMEM_IMAGE_VERSION_NAME_SIZE ] ;
char variant [ SMEM_IMAGE_VERSION_VARIANT_SIZE ] ;
char pad ;
char oem [ SMEM_IMAGE_VERSION_OEM_SIZE ] ;
} ;
2019-07-24 04:05:14 +05:30
# endif /* CONFIG_DEBUG_FS */
2019-07-24 04:05:13 +05:30
struct qcom_socinfo {
struct soc_device * soc_dev ;
struct soc_device_attribute attr ;
2019-07-24 04:05:14 +05:30
# ifdef CONFIG_DEBUG_FS
struct dentry * dbg_root ;
struct socinfo_params info ;
# endif /* CONFIG_DEBUG_FS */
2019-07-24 04:05:13 +05:30
} ;
struct soc_id {
unsigned int id ;
const char * name ;
} ;
static const struct soc_id soc_id [ ] = {
{ 87 , " MSM8960 " } ,
{ 109 , " APQ8064 " } ,
{ 122 , " MSM8660A " } ,
{ 123 , " MSM8260A " } ,
{ 124 , " APQ8060A " } ,
{ 126 , " MSM8974 " } ,
{ 130 , " MPQ8064 " } ,
{ 138 , " MSM8960AB " } ,
{ 139 , " APQ8060AB " } ,
{ 140 , " MSM8260AB " } ,
{ 141 , " MSM8660AB " } ,
{ 178 , " APQ8084 " } ,
{ 184 , " APQ8074 " } ,
{ 185 , " MSM8274 " } ,
{ 186 , " MSM8674 " } ,
{ 194 , " MSM8974PRO " } ,
{ 206 , " MSM8916 " } ,
{ 208 , " APQ8074-AA " } ,
{ 209 , " APQ8074-AB " } ,
{ 210 , " APQ8074PRO " } ,
{ 211 , " MSM8274-AA " } ,
{ 212 , " MSM8274-AB " } ,
{ 213 , " MSM8274PRO " } ,
{ 214 , " MSM8674-AA " } ,
{ 215 , " MSM8674-AB " } ,
{ 216 , " MSM8674PRO " } ,
{ 217 , " MSM8974-AA " } ,
{ 218 , " MSM8974-AB " } ,
{ 246 , " MSM8996 " } ,
{ 247 , " APQ8016 " } ,
{ 248 , " MSM8216 " } ,
{ 249 , " MSM8116 " } ,
{ 250 , " MSM8616 " } ,
{ 291 , " APQ8096 " } ,
{ 305 , " MSM8996SG " } ,
{ 310 , " MSM8996AU " } ,
{ 311 , " APQ8096AU " } ,
{ 312 , " APQ8096SG " } ,
} ;
static const char * socinfo_machine ( struct device * dev , unsigned int id )
{
int idx ;
for ( idx = 0 ; idx < ARRAY_SIZE ( soc_id ) ; idx + + ) {
if ( soc_id [ idx ] . id = = id )
return soc_id [ idx ] . name ;
}
return NULL ;
}
2019-07-24 04:05:14 +05:30
# ifdef CONFIG_DEBUG_FS
# define QCOM_OPEN(name, _func) \
static int qcom_open_ # # name ( struct inode * inode , struct file * file ) \
{ \
return single_open ( file , _func , inode - > i_private ) ; \
} \
\
static const struct file_operations qcom_ # # name # # _ops = { \
. open = qcom_open_ # # name , \
. read = seq_read , \
. llseek = seq_lseek , \
. release = single_release , \
}
# define DEBUGFS_ADD(info, name) \
debugfs_create_file ( __stringify ( name ) , 0400 , \
qcom_socinfo - > dbg_root , \
info , & qcom_ # # name # # _ops )
static int qcom_show_build_id ( struct seq_file * seq , void * p )
{
struct socinfo * socinfo = seq - > private ;
seq_printf ( seq , " %s \n " , socinfo - > build_id ) ;
return 0 ;
}
static int qcom_show_pmic_model ( struct seq_file * seq , void * p )
{
struct socinfo * socinfo = seq - > private ;
int model = SOCINFO_MINOR ( le32_to_cpu ( socinfo - > pmic_model ) ) ;
if ( model < 0 )
return - EINVAL ;
seq_printf ( seq , " %s \n " , pmic_models [ model ] ) ;
return 0 ;
}
static int qcom_show_pmic_die_revision ( struct seq_file * seq , void * p )
{
struct socinfo * socinfo = seq - > private ;
seq_printf ( seq , " %u.%u \n " ,
SOCINFO_MAJOR ( le32_to_cpu ( socinfo - > pmic_die_rev ) ) ,
SOCINFO_MINOR ( le32_to_cpu ( socinfo - > pmic_die_rev ) ) ) ;
return 0 ;
}
QCOM_OPEN ( build_id , qcom_show_build_id ) ;
QCOM_OPEN ( pmic_model , qcom_show_pmic_model ) ;
QCOM_OPEN ( pmic_die_rev , qcom_show_pmic_die_revision ) ;
2019-07-24 04:05:15 +05:30
# define DEFINE_IMAGE_OPS(type) \
static int show_image_ # # type ( struct seq_file * seq , void * p ) \
{ \
struct smem_image_version * image_version = seq - > private ; \
seq_puts ( seq , image_version - > type ) ; \
seq_puts ( seq , " \n " ) ; \
return 0 ; \
} \
static int open_image_ # # type ( struct inode * inode , struct file * file ) \
{ \
return single_open ( file , show_image_ # # type , inode - > i_private ) ; \
} \
\
static const struct file_operations qcom_image_ # # type # # _ops = { \
. open = open_image_ # # type , \
. read = seq_read , \
. llseek = seq_lseek , \
. release = single_release , \
}
DEFINE_IMAGE_OPS ( name ) ;
DEFINE_IMAGE_OPS ( variant ) ;
DEFINE_IMAGE_OPS ( oem ) ;
2019-07-24 04:05:14 +05:30
static void socinfo_debugfs_init ( struct qcom_socinfo * qcom_socinfo ,
struct socinfo * info )
{
2019-07-24 04:05:15 +05:30
struct smem_image_version * versions ;
struct dentry * dentry ;
2019-07-24 04:05:14 +05:30
size_t size ;
2019-07-24 04:05:15 +05:30
int i ;
2019-07-24 04:05:14 +05:30
qcom_socinfo - > dbg_root = debugfs_create_dir ( " qcom_socinfo " , NULL ) ;
qcom_socinfo - > info . fmt = __le32_to_cpu ( info - > fmt ) ;
switch ( qcom_socinfo - > info . fmt ) {
case SOCINFO_VERSION ( 0 , 12 ) :
qcom_socinfo - > info . chip_family =
__le32_to_cpu ( info - > chip_family ) ;
qcom_socinfo - > info . raw_device_family =
__le32_to_cpu ( info - > raw_device_family ) ;
qcom_socinfo - > info . raw_device_num =
__le32_to_cpu ( info - > raw_device_num ) ;
debugfs_create_x32 ( " chip_family " , 0400 , qcom_socinfo - > dbg_root ,
& qcom_socinfo - > info . chip_family ) ;
debugfs_create_x32 ( " raw_device_family " , 0400 ,
qcom_socinfo - > dbg_root ,
& qcom_socinfo - > info . raw_device_family ) ;
debugfs_create_x32 ( " raw_device_number " , 0400 ,
qcom_socinfo - > dbg_root ,
& qcom_socinfo - > info . raw_device_num ) ;
2019-08-06 19:23:56 -07:00
/* Fall through */
2019-07-24 04:05:14 +05:30
case SOCINFO_VERSION ( 0 , 11 ) :
case SOCINFO_VERSION ( 0 , 10 ) :
case SOCINFO_VERSION ( 0 , 9 ) :
qcom_socinfo - > info . foundry_id = __le32_to_cpu ( info - > foundry_id ) ;
debugfs_create_u32 ( " foundry_id " , 0400 , qcom_socinfo - > dbg_root ,
& qcom_socinfo - > info . foundry_id ) ;
2019-08-06 19:23:56 -07:00
/* Fall through */
2019-07-24 04:05:14 +05:30
case SOCINFO_VERSION ( 0 , 8 ) :
case SOCINFO_VERSION ( 0 , 7 ) :
DEBUGFS_ADD ( info , pmic_model ) ;
DEBUGFS_ADD ( info , pmic_die_rev ) ;
2019-08-06 19:23:56 -07:00
/* Fall through */
2019-07-24 04:05:14 +05:30
case SOCINFO_VERSION ( 0 , 6 ) :
qcom_socinfo - > info . hw_plat_subtype =
__le32_to_cpu ( info - > hw_plat_subtype ) ;
debugfs_create_u32 ( " hardware_platform_subtype " , 0400 ,
qcom_socinfo - > dbg_root ,
& qcom_socinfo - > info . hw_plat_subtype ) ;
2019-08-06 19:23:56 -07:00
/* Fall through */
2019-07-24 04:05:14 +05:30
case SOCINFO_VERSION ( 0 , 5 ) :
qcom_socinfo - > info . accessory_chip =
__le32_to_cpu ( info - > accessory_chip ) ;
debugfs_create_u32 ( " accessory_chip " , 0400 ,
qcom_socinfo - > dbg_root ,
& qcom_socinfo - > info . accessory_chip ) ;
2019-08-06 19:23:56 -07:00
/* Fall through */
2019-07-24 04:05:14 +05:30
case SOCINFO_VERSION ( 0 , 4 ) :
qcom_socinfo - > info . plat_ver = __le32_to_cpu ( info - > plat_ver ) ;
debugfs_create_u32 ( " platform_version " , 0400 ,
qcom_socinfo - > dbg_root ,
& qcom_socinfo - > info . plat_ver ) ;
2019-08-06 19:23:56 -07:00
/* Fall through */
2019-07-24 04:05:14 +05:30
case SOCINFO_VERSION ( 0 , 3 ) :
qcom_socinfo - > info . hw_plat = __le32_to_cpu ( info - > hw_plat ) ;
debugfs_create_u32 ( " hardware_platform " , 0400 ,
qcom_socinfo - > dbg_root ,
& qcom_socinfo - > info . hw_plat ) ;
2019-08-06 19:23:56 -07:00
/* Fall through */
2019-07-24 04:05:14 +05:30
case SOCINFO_VERSION ( 0 , 2 ) :
qcom_socinfo - > info . raw_ver = __le32_to_cpu ( info - > raw_ver ) ;
debugfs_create_u32 ( " raw_version " , 0400 , qcom_socinfo - > dbg_root ,
& qcom_socinfo - > info . raw_ver ) ;
2019-08-06 19:23:56 -07:00
/* Fall through */
2019-07-24 04:05:14 +05:30
case SOCINFO_VERSION ( 0 , 1 ) :
DEBUGFS_ADD ( info , build_id ) ;
break ;
}
2019-07-24 04:05:15 +05:30
versions = qcom_smem_get ( QCOM_SMEM_HOST_ANY , SMEM_IMAGE_VERSION_TABLE ,
& size ) ;
for ( i = 0 ; i < ARRAY_SIZE ( socinfo_image_names ) ; i + + ) {
if ( ! socinfo_image_names [ i ] )
continue ;
dentry = debugfs_create_dir ( socinfo_image_names [ i ] ,
qcom_socinfo - > dbg_root ) ;
debugfs_create_file ( " name " , 0400 , dentry , & versions [ i ] ,
& qcom_image_name_ops ) ;
debugfs_create_file ( " variant " , 0400 , dentry , & versions [ i ] ,
& qcom_image_variant_ops ) ;
debugfs_create_file ( " oem " , 0400 , dentry , & versions [ i ] ,
& qcom_image_oem_ops ) ;
}
2019-07-24 04:05:14 +05:30
}
static void socinfo_debugfs_exit ( struct qcom_socinfo * qcom_socinfo )
{
debugfs_remove_recursive ( qcom_socinfo - > dbg_root ) ;
}
# else
static void socinfo_debugfs_init ( struct qcom_socinfo * qcom_socinfo ,
struct socinfo * info )
{
}
static void socinfo_debugfs_exit ( struct qcom_socinfo * qcom_socinfo ) { }
# endif /* CONFIG_DEBUG_FS */
2019-07-24 04:05:13 +05:30
static int qcom_socinfo_probe ( struct platform_device * pdev )
{
struct qcom_socinfo * qs ;
struct socinfo * info ;
size_t item_size ;
info = qcom_smem_get ( QCOM_SMEM_HOST_ANY , SMEM_HW_SW_BUILD_ID ,
& item_size ) ;
if ( IS_ERR ( info ) ) {
dev_err ( & pdev - > dev , " Couldn't find socinfo \n " ) ;
return PTR_ERR ( info ) ;
}
qs = devm_kzalloc ( & pdev - > dev , sizeof ( * qs ) , GFP_KERNEL ) ;
if ( ! qs )
return - ENOMEM ;
qs - > attr . family = " Snapdragon " ;
qs - > attr . machine = socinfo_machine ( & pdev - > dev ,
le32_to_cpu ( info - > id ) ) ;
qs - > attr . revision = devm_kasprintf ( & pdev - > dev , GFP_KERNEL , " %u.%u " ,
SOCINFO_MAJOR ( le32_to_cpu ( info - > ver ) ) ,
SOCINFO_MINOR ( le32_to_cpu ( info - > ver ) ) ) ;
if ( offsetof ( struct socinfo , serial_num ) < = item_size )
qs - > attr . serial_number = devm_kasprintf ( & pdev - > dev , GFP_KERNEL ,
" %u " ,
le32_to_cpu ( info - > serial_num ) ) ;
qs - > soc_dev = soc_device_register ( & qs - > attr ) ;
if ( IS_ERR ( qs - > soc_dev ) )
return PTR_ERR ( qs - > soc_dev ) ;
2019-07-24 04:05:14 +05:30
socinfo_debugfs_init ( qs , info ) ;
2019-07-24 04:05:13 +05:30
/* Feed the soc specific unique data into entropy pool */
add_device_randomness ( info , item_size ) ;
platform_set_drvdata ( pdev , qs - > soc_dev ) ;
return 0 ;
}
static int qcom_socinfo_remove ( struct platform_device * pdev )
{
struct qcom_socinfo * qs = platform_get_drvdata ( pdev ) ;
soc_device_unregister ( qs - > soc_dev ) ;
2019-07-24 04:05:14 +05:30
socinfo_debugfs_exit ( qs ) ;
2019-07-24 04:05:13 +05:30
return 0 ;
}
static struct platform_driver qcom_socinfo_driver = {
. probe = qcom_socinfo_probe ,
. remove = qcom_socinfo_remove ,
. driver = {
. name = " qcom-socinfo " ,
} ,
} ;
module_platform_driver ( qcom_socinfo_driver ) ;
MODULE_DESCRIPTION ( " Qualcomm SoCinfo driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform:qcom-socinfo " ) ;