diff mbox

[v5] soc: qcom: Add SoC info driver

Message ID 1479302540-16690-1-git-send-email-kimran@codeaurora.org (mailing list archive)
State Superseded, archived
Delegated to: Andy Gross
Headers show

Commit Message

Khan, Imran Nov. 16, 2016, 1:22 p.m. UTC
The SoC info driver provides information such as Chip ID,
Chip family, serial number and other such details about
Qualcomm SoCs.

Signed-off-by: Imran Khan <kimran@codeaurora.org>
---
v4 --> v5:
 - Removed redundant function socinfo_print

v3 --> v4:
 - Corrected makefile so that smem and socinfo are treated as one module
 - Moved the code snippet to get socinfo smem item into socinfo.c
 - Removed redundant use of socinfo major version as it is always zero
 - Removed unused enums
 - Removed redundant indirections
 - Use image_version object to store information about each entry in the
   smem image table
 - Replaced usage of snprintf with sprintf and scnprintf
 - Get the address of image version table at the beginning and setup
   image version attributes only if image version table is available
 - Do the same for platform_subtype
 - Make different type of image version objects read only or readable/
   writable depending on their types. For example apps image attributes
   can be modified via sysfs but the same can't be done for modem image
 - Show PMIC model in a human readable form rather than a numeric number
 - Avoid using table in single sysfs entry
 - Removed checkpatch warnings about S_IRUGO

v2 --> v3:
 - Add support to toss soc information data into entropy pool
 - Since socinfo is rolled into smem driver, compile the
   relevant portion of socinfo driver with smem driver
 
v1 --> v2:
 - Removed inclusion of system_misc.h
 - merged socinfo.h into socinfo.c
 - made platform type and subtype arrays static
 - replaced uint32_t with u32
 - made functions static to avoid exposing vendor specific interface
 - Replaced usage of IS_ERR_OR_NULL with IS_ERR.
 - Remove raw-id attribute usage as human readable soc-id will suffice here
 - Avoid using a separate platform driver for socinfo by rolling it into smem driver itself.
   The sysfs setup is being done in a separate file (socinfo.c)
 - Replaced macro BUILD_ID_LENGTH with  SMEM_SOCINFO_BUILD_ID_LENGTH.
 - For failure cases where socinfo can not be read use a single dummy socinfo with error values.
 - Removed usage of early_machine_is_xxx.


 drivers/soc/qcom/Kconfig   |   1 +
 drivers/soc/qcom/Makefile  |   3 +-
 drivers/soc/qcom/smem.c    |   5 +
 drivers/soc/qcom/socinfo.c | 989 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 997 insertions(+), 1 deletion(-)
 create mode 100644 drivers/soc/qcom/socinfo.c

Comments

Bjorn Andersson Dec. 5, 2016, 6:05 p.m. UTC | #1
On Wed 16 Nov 05:22 PST 2016, Imran Khan wrote:

> The SoC info driver provides information such as Chip ID,
> Chip family, serial number and other such details about
> Qualcomm SoCs.
> 
> Signed-off-by: Imran Khan <kimran@codeaurora.org>
> ---
> v4 --> v5:
>  - Removed redundant function socinfo_print
> 
> v3 --> v4:
>  - Corrected makefile so that smem and socinfo are treated as one module
>  - Moved the code snippet to get socinfo smem item into socinfo.c
>  - Removed redundant use of socinfo major version as it is always zero
>  - Removed unused enums
>  - Removed redundant indirections
>  - Use image_version object to store information about each entry in the
>    smem image table
>  - Replaced usage of snprintf with sprintf and scnprintf
>  - Get the address of image version table at the beginning and setup
>    image version attributes only if image version table is available
>  - Do the same for platform_subtype
>  - Make different type of image version objects read only or readable/
>    writable depending on their types. For example apps image attributes
>    can be modified via sysfs but the same can't be done for modem image
>  - Show PMIC model in a human readable form rather than a numeric number
>  - Avoid using table in single sysfs entry
>  - Removed checkpatch warnings about S_IRUGO
> 
> v2 --> v3:
>  - Add support to toss soc information data into entropy pool
>  - Since socinfo is rolled into smem driver, compile the
>    relevant portion of socinfo driver with smem driver
>  
> v1 --> v2:
>  - Removed inclusion of system_misc.h
>  - merged socinfo.h into socinfo.c
>  - made platform type and subtype arrays static
>  - replaced uint32_t with u32
>  - made functions static to avoid exposing vendor specific interface
>  - Replaced usage of IS_ERR_OR_NULL with IS_ERR.
>  - Remove raw-id attribute usage as human readable soc-id will suffice here
>  - Avoid using a separate platform driver for socinfo by rolling it into smem driver itself.
>    The sysfs setup is being done in a separate file (socinfo.c)
>  - Replaced macro BUILD_ID_LENGTH with  SMEM_SOCINFO_BUILD_ID_LENGTH.
>  - For failure cases where socinfo can not be read use a single dummy socinfo with error values.
>  - Removed usage of early_machine_is_xxx.
> 
> 
>  drivers/soc/qcom/Kconfig   |   1 +
>  drivers/soc/qcom/Makefile  |   3 +-
>  drivers/soc/qcom/smem.c    |   5 +
>  drivers/soc/qcom/socinfo.c | 989 +++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 997 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/soc/qcom/socinfo.c
> 
> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
> index 461b387..f89d34d 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -24,6 +24,7 @@ config QCOM_SMEM
>  	tristate "Qualcomm Shared Memory Manager (SMEM)"
>  	depends on ARCH_QCOM
>  	depends on HWSPINLOCK
> +	select SOC_BUS
>  	help
>  	  Say y here to enable support for the Qualcomm Shared Memory Manager.
>  	  The driver provides an interface to items in a heap shared among all
> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
> index fdd664e..438efec 100644
> --- a/drivers/soc/qcom/Makefile
> +++ b/drivers/soc/qcom/Makefile
> @@ -2,7 +2,8 @@ obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
>  obj-$(CONFIG_QCOM_PM)	+=	spm.o
>  obj-$(CONFIG_QCOM_SMD) +=	smd.o
>  obj-$(CONFIG_QCOM_SMD_RPM)	+= smd-rpm.o
> -obj-$(CONFIG_QCOM_SMEM) +=	smem.o
> +obj-$(CONFIG_QCOM_SMEM) +=	qcom_smem.o
> +qcom_smem-y := smem.o socinfo.o
>  obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
>  obj-$(CONFIG_QCOM_SMP2P)	+= smp2p.o
>  obj-$(CONFIG_QCOM_SMSM)	+= smsm.o
> diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
> index 18ec52f..a57acfb0 100644
> --- a/drivers/soc/qcom/smem.c
> +++ b/drivers/soc/qcom/smem.c
> @@ -85,6 +85,9 @@
>  /* Max number of processors/hosts in a system */
>  #define SMEM_HOST_COUNT		9
>  
> +
> +extern int qcom_socinfo_init(struct platform_device *pdev);
> +
>  /**
>    * struct smem_proc_comm - proc_comm communication struct (legacy)
>    * @command:	current command to be executed
> @@ -751,6 +754,8 @@ static int qcom_smem_probe(struct platform_device *pdev)
>  
>  	__smem = smem;
>  
> +	qcom_socinfo_init(pdev);
> +
>  	return 0;
>  }
>  
> diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c
> new file mode 100644
> index 0000000..eca46df
> --- /dev/null
> +++ b/drivers/soc/qcom/socinfo.c
> @@ -0,0 +1,989 @@
> +/*
> + * Copyright (c) 2009-2016, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/export.h>
> +#include <linux/module.h>
> +#include <linux/err.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/sys_soc.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +#include <linux/string.h>
> +#include <linux/types.h>
> +#include <linux/random.h>
> +#include <linux/soc/qcom/smem.h>
> +
> +#define PMIC_MODEL_UNKNOWN 0
> +#define HW_PLATFORM_QRD 11
> +#define PLATFORM_SUBTYPE_QRD_INVALID 6
> +#define PLATFORM_SUBTYPE_INVALID 4
> +/*
> + * SOC version type with major number in the upper 16 bits and minor
> + * number in the lower 16 bits.  For example:
> + *   1.0 -> 0x00010000
> + *   2.3 -> 0x00020003
> + */
> +#define SOC_VERSION_MAJOR(ver) (((ver) & 0xffff0000) >> 16)
> +#define SOC_VERSION_MINOR(ver) ((ver) & 0x0000ffff)
> +#define SOCINFO_FORMAT(x)  (x)
> +#define SOCINFO_VERSION_MAJOR	SOC_VERSION_MAJOR
> +#define SOCINFO_VERSION_MINOR	SOC_VERSION_MINOR
> +
> +#define SMEM_SOCINFO_BUILD_ID_LENGTH 32
> +#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
> +#define SMEM_IMAGE_VERSION_PARTITION_APPS 10

Please indent the values evenly, making this slightly easier to read.

> +
> +/*
> + * Shared memory identifiers, used to acquire handles to respective memory
> + * region.
> + */
> +#define SMEM_IMAGE_VERSION_TABLE	469
> +
> +/*
> + * Shared memory identifiers, used to acquire handles to respective memory
> + * region.
> + */

Duplicating this comment doesn't add value, please just drop this second
copy and replace the above comment with something like "SMEM item ids"

> +#define SMEM_HW_SW_BUILD_ID		137
> +
> +/*
> + * SMEM Image table indices
> + */
> +enum smem_image_table_index {
> +	SMEM_IMAGE_TABLE_BOOT_INDEX = 0,
> +	SMEM_IMAGE_TABLE_TZ_INDEX,
> +	SMEM_IMAGE_TABLE_RPM_INDEX = 3,
> +	SMEM_IMAGE_TABLE_APPS_INDEX = 10,
> +	SMEM_IMAGE_TABLE_MPSS_INDEX,
> +	SMEM_IMAGE_TABLE_ADSP_INDEX,
> +	SMEM_IMAGE_TABLE_CNSS_INDEX,
> +	SMEM_IMAGE_TABLE_VIDEO_INDEX
> +};
> +
> +#define SMEM_IMAGE_TABLE_ATTR(type, index)				\
> +static ssize_t								\
> +qcom_get_##type##_image_version(struct device *dev,			\
> +			struct device_attribute *attr,			\
> +			char *buf)					\
> +{									\
> +	return	qcom_get_image_version(index, buf);			\
> +}									\
> +static ssize_t								\
> +qcom_set_##type##_image_version(struct device *dev,			\
> +			struct device_attribute *attr,			\
> +			const char *buf,				\
> +			size_t count)					\
> +{									\
> +	return	qcom_set_image_version(index, buf);			\
> +}									\
> +static ssize_t								\
> +qcom_get_##type##_image_variant(struct device *dev,			\
> +			struct device_attribute *attr,			\
> +			char *buf)					\
> +{									\
> +	return	qcom_get_image_variant(index, buf);			\
> +}									\
> +static ssize_t								\
> +qcom_set_##type##_image_variant(struct device *dev,			\
> +			struct device_attribute *attr,			\
> +			const char *buf,				\
> +			size_t count)					\
> +{									\
> +	return	qcom_set_image_variant(index, buf);			\
> +}									\
> +static ssize_t								\
> +qcom_get_##type##_image_crm_version(struct device *dev,			\
> +			struct device_attribute *attr,			\
> +			char *buf)					\
> +{									\
> +	return	qcom_get_image_crm_version(index, buf);			\
> +}									\
> +static ssize_t								\
> +qcom_set_##type##_image_crm_version(struct device *dev,			\
> +			struct device_attribute *attr,			\
> +			const char *buf,				\
> +			size_t count)					\
> +{									\
> +	return	qcom_set_image_crm_version(index, buf);			\
> +}									\
> +DEVICE_ATTR(type##_image_version, 0644, qcom_get_##type##_image_version,\
> +				qcom_set_##type##_image_version);	\
> +DEVICE_ATTR(type##_image_variant, 0644, qcom_get_##type##_image_variant,\
> +				qcom_set_##type##_image_variant);	\
> +DEVICE_ATTR(type##_image_crm_version, 0644,				\
> +				qcom_get_##type##_image_crm_version,	\
> +				qcom_set_##type##_image_crm_version);	\
> +static struct attribute *type##_image_attrs[] = {			\
> +	&dev_attr_##type##_image_version.attr,				\
> +	&dev_attr_##type##_image_variant.attr,				\
> +	&dev_attr_##type##_image_crm_version.attr,			\
> +	NULL,								\
> +};									\
> +static struct attribute_group type##_image_attr_group = {		\
> +	.attrs = type##_image_attrs,					\
> +}
> +
> +#define SMEM_IMAGE_TABLE_RO_ATTR(type, index)				\
> +static ssize_t								\
> +qcom_get_##type##_image_version(struct device *dev,			\
> +			struct device_attribute *attr,			\
> +			char *buf)					\
> +{									\
> +	return	qcom_get_image_version(index, buf);			\
> +}									\
> +static ssize_t								\
> +qcom_get_##type##_image_variant(struct device *dev,			\
> +			struct device_attribute *attr,			\
> +			char *buf)					\
> +{									\
> +	return	qcom_get_image_variant(index, buf);			\
> +}									\
> +static ssize_t								\
> +qcom_get_##type##_image_crm_version(struct device *dev,			\
> +			struct device_attribute *attr,			\
> +			char *buf)					\
> +{									\
> +	return	qcom_get_image_crm_version(index, buf);			\
> +}									\
> +DEVICE_ATTR(type##_image_version, 0444, qcom_get_##type##_image_version,\
> +					NULL);				\
> +DEVICE_ATTR(type##_image_variant, 0444, qcom_get_##type##_image_variant,\
> +					NULL);				\
> +DEVICE_ATTR(type##_image_crm_version, 0444,				\
> +				qcom_get_##type##_image_crm_version,	\
> +					NULL);				\
> +static struct attribute *type##_image_attrs[] = {			\
> +	&dev_attr_##type##_image_version.attr,				\
> +	&dev_attr_##type##_image_variant.attr,				\
> +	&dev_attr_##type##_image_crm_version.attr,			\
> +	NULL,								\
> +	};								\
> +static struct attribute_group type##_image_attr_group = {		\
> +	.attrs = type##_image_attrs,					\
> +}

Rather than having this based on two huge macros take a look at
DEVICE_INT_ATTR() and struct dev_ext_attribute in
include/linux/device.h. It looks like if you just put the index in a
struct and use container_of to reach that you can replace these with
direct functions.

Also, it's perfectly fine to always specify a store operation and use
0444 vs 0644 to control it's availability. So you don't need two sets if
you just expose the mode in your macro.

> +
> +/*
> + * Qcom SoC types
> + */
> +enum qcom_cpu {
> +	MSM_CPU_UNKNOWN = 0,
> +	MSM_CPU_8960,
> +	MSM_CPU_8960AB,
> +	MSM_CPU_8064,
> +	MSM_CPU_8974,
> +	MSM_CPU_8974PRO_AA,
> +	MSM_CPU_8974PRO_AB,
> +	MSM_CPU_8974PRO_AC,
> +	MSM_CPU_8916,
> +	MSM_CPU_8084,
> +	MSM_CPU_8996
> +};

These are now "unused" (per below comment).

> +
> +struct qcom_soc_info {
> +	enum qcom_cpu generic_soc_type;

This enum is no longer used, seems like you can turn cpu_of_id into a
simple char *[] array with with soc_id_string indexed by id.

> +	char *soc_id_string;
> +};
> +
> +/* Hardware platform types */
> +static const char *hw_platform[] = {
> +	[0] = "Unknown",
> +	[1] = "Surf",
> +	[2] = "FFA",
> +	[3] = "Fluid",
> +	[4] = "SVLTE_FFA",
> +	[5] = "SLVTE_SURF",
> +	[7] = "MDM_MTP_NO_DISPLAY",
> +	[8] = "MTP",
> +	[9] = "Liquid",
> +	[10] = "Dragon",
> +	[11] = "QRD",
> +	[13] = "HRD",
> +	[14] = "DTV",
> +	[21] = "RCM",
> +	[23] = "STP",
> +	[24] = "SBC",
> +};
> +
> +static const char *qrd_hw_platform_subtype[] = {
> +	[0] = "QRD",
> +	[1] = "SKUAA",
> +	[2] = "SKUF",
> +	[3] = "SKUAB",
> +	[5] = "SKUG",
> +	[6] = "INVALID",
> +};
> +
> +static const char *hw_platform_subtype[] = {
> +	"Unknown", "charm", "strange", "strange_2a", "Invalid",
> +};
> +
> +static const char *pmic_model[] = {
> +	[0]  = "Unknown PMIC model",
> +	[13] = "PMIC model: PM8058",
> +	[14] = "PMIC model: PM8028",
> +	[15] = "PMIC model: PM8901",
> +	[16] = "PMIC model: PM8027",
> +	[17] = "PMIC model: ISL9519",
> +	[18] = "PMIC model: PM8921",
> +	[19] = "PMIC model: PM8018",
> +	[20] = "PMIC model: PM8015",
> +	[21] = "PMIC model: PM8014",
> +	[22] = "PMIC model: PM8821",
> +	[23] = "PMIC model: PM8038",
> +	[24] = "PMIC model: PM8922",
> +	[25] = "PMIC model: PM8917",
> +};
> +
> +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];
> +};
> +
> +/* Used to parse shared memory. Must match the modem. */
> +struct socinfo_v0_1 {
> +	u32 format;
> +	u32 id;
> +	u32 version;
> +	char build_id[SMEM_SOCINFO_BUILD_ID_LENGTH];
> +};
> +
> +struct socinfo_v0_2 {
> +	struct socinfo_v0_1 v0_1;
> +	u32 raw_version;
> +};
> +
> +struct socinfo_v0_3 {
> +	struct socinfo_v0_2 v0_2;
> +	u32 hw_platform;
> +};
> +
> +struct socinfo_v0_4 {
> +	struct socinfo_v0_3 v0_3;
> +	u32 platform_version;
> +};
> +
> +struct socinfo_v0_5 {
> +	struct socinfo_v0_4 v0_4;
> +	u32 accessory_chip;
> +};
> +
> +struct socinfo_v0_6 {
> +	struct socinfo_v0_5 v0_5;
> +	u32 hw_platform_subtype;
> +};
> +
> +struct socinfo_v0_7 {
> +	struct socinfo_v0_6 v0_6;
> +	u32 pmic_model;
> +	u32 pmic_die_revision;
> +};
> +
> +struct socinfo_v0_8 {
> +	struct socinfo_v0_7 v0_7;
> +	u32 pmic_model_1;
> +	u32 pmic_die_revision_1;
> +	u32 pmic_model_2;
> +	u32 pmic_die_revision_2;
> +};
> +
> +struct socinfo_v0_9 {
> +	struct socinfo_v0_8 v0_8;
> +	u32 foundry_id;
> +};
> +
> +struct socinfo_v0_10 {
> +	struct socinfo_v0_9 v0_9;
> +	u32 serial_number;
> +};
> +
> +struct socinfo_v0_11 {
> +	struct socinfo_v0_10 v0_10;
> +	u32 num_pmics;
> +	u32 pmic_array_offset;
> +};
> +
> +struct socinfo_v0_12 {
> +	struct socinfo_v0_11 v0_11;
> +	u32 chip_family;
> +	u32 raw_device_family;
> +	u32 raw_device_number;
> +};
> +
> +static union {
> +	struct socinfo_v0_1 v0_1;
> +	struct socinfo_v0_2 v0_2;
> +	struct socinfo_v0_3 v0_3;
> +	struct socinfo_v0_4 v0_4;
> +	struct socinfo_v0_5 v0_5;
> +	struct socinfo_v0_6 v0_6;
> +	struct socinfo_v0_7 v0_7;
> +	struct socinfo_v0_8 v0_8;
> +	struct socinfo_v0_9 v0_9;
> +	struct socinfo_v0_10 v0_10;
> +	struct socinfo_v0_11 v0_11;
> +	struct socinfo_v0_12 v0_12;
> +} *socinfo;
> +
> +static struct smem_image_version *smem_image_version;
> +static bool smem_img_tbl_avlbl = true;

You don't need to keep track of this separately, just test if
smem_image_version is NULL or not (reset it to NULL if qcom_smem_get()
fails).

> +static bool hw_subtype_valid = true;
> +static u32 hw_subtype;

Don't keep track of this in a global variable, it's perfectly fine to
figure it out in qcom_get_platform_subtype()

> +
> +
> +/* max socinfo format version supported */
> +#define MAX_SOCINFO_FORMAT SOCINFO_FORMAT(12)

It's fine to drop SOCINFO_FORMAT and just put 12 here.

> +
> +static struct qcom_soc_info cpu_of_id[] = {
> +
> +	[0] = {MSM_CPU_UNKNOWN, "Unknown CPU"},
> +
> +	/* 8x60 IDs */
> +	[87] = {MSM_CPU_8960, "MSM8960"},
> +
> +	/* 8x64 IDs */
> +	[109] = {MSM_CPU_8064, "APQ8064"},
> +	[130] = {MSM_CPU_8064, "MPQ8064"},
> +
> +	/* 8x60A IDs */
> +	[122] = {MSM_CPU_8960, "MSM8660A"},
> +	[123] = {MSM_CPU_8960, "MSM8260A"},
> +	[124] = {MSM_CPU_8960, "APQ8060A"},
> +
> +	/* 8x74 IDs */
> +	[126] = {MSM_CPU_8974, "MSM8974"},
> +	[184] = {MSM_CPU_8974, "APQ8074"},
> +	[185] = {MSM_CPU_8974, "MSM8274"},
> +	[186] = {MSM_CPU_8974, "MSM8674"},
> +
> +	/* 8x74AA IDs */
> +	[208] = {MSM_CPU_8974PRO_AA, "APQ8074-AA"},
> +	[211] = {MSM_CPU_8974PRO_AA, "MSM8274-AA"},
> +	[214] = {MSM_CPU_8974PRO_AA, "MSM8674-AA"},
> +	[217] = {MSM_CPU_8974PRO_AA, "MSM8974-AA"},
> +
> +	/* 8x74AB IDs */
> +	[209] = {MSM_CPU_8974PRO_AB, "APQ8074-AB"},
> +	[212] = {MSM_CPU_8974PRO_AB, "MSM8274-AB"},
> +	[215] = {MSM_CPU_8974PRO_AB, "MSM8674-AB"},
> +	[218] = {MSM_CPU_8974PRO_AB, "MSM8974-AB"},
> +
> +	/* 8x74AC IDs */
> +	[194] = {MSM_CPU_8974PRO_AC, "MSM8974PRO"},
> +	[210] = {MSM_CPU_8974PRO_AC, "APQ8074PRO"},
> +	[213] = {MSM_CPU_8974PRO_AC, "MSM8274PRO"},
> +	[216] = {MSM_CPU_8974PRO_AC, "MSM8674PRO"},
> +
> +	/* 8x60AB IDs */
> +	[138] = {MSM_CPU_8960AB, "MSM8960AB"},
> +	[139] = {MSM_CPU_8960AB, "APQ8060AB"},
> +	[140] = {MSM_CPU_8960AB, "MSM8260AB"},
> +	[141] = {MSM_CPU_8960AB, "MSM8660AB"},
> +
> +	/* 8x84 IDs */
> +	[178] = {MSM_CPU_8084, "APQ8084"},
> +
> +	/* 8x16 IDs */
> +	[206] = {MSM_CPU_8916, "MSM8916"},
> +	[247] = {MSM_CPU_8916, "APQ8016"},
> +	[248] = {MSM_CPU_8916, "MSM8216"},
> +	[249] = {MSM_CPU_8916, "MSM8116"},
> +	[250] = {MSM_CPU_8916, "MSM8616"},
> +
> +	/* 8x96 IDs */
> +	[246] = {MSM_CPU_8996, "MSM8996"},
> +	[310] = {MSM_CPU_8996, "MSM8996AU"},
> +	[311] = {MSM_CPU_8996, "APQ8096AU"},
> +	[291] = {MSM_CPU_8996, "APQ8096"},
> +	[305] = {MSM_CPU_8996, "MSM8996SG"},
> +	[312] = {MSM_CPU_8996, "APQ8096SG"},
> +
> +	/*
> +	 * Uninitialized IDs are not known to run Linux.
> +	 * MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are
> +	 * considered as unknown CPU.
> +	 */
> +};
> +
> +static u32 socinfo_format;
> +
> +static u32 socinfo_get_id(void);
> +

Merge socinfo_init_sysfs() and qcom_socinfo_init() into one function and
you don't need to declare this here.

> +/* socinfo: sysfs functions */
> +
> +static char *socinfo_get_id_string(void)
> +{
> +	return (socinfo) ? cpu_of_id[socinfo->v0_1.id].soc_id_string : NULL;

socinfo can't be NULL anymore.

> +}
> +
> +static char *socinfo_get_image_version_base_address(struct device *dev)

Inline this into qcom_socinfo_init() as described below.

> +{
> +	size_t size, size_in;
> +	void *ptr;
> +
> +	ptr = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_IMAGE_VERSION_TABLE,
> +			&size);
> +	if (IS_ERR(ptr))
> +		return ptr;
> +
> +	size_in = SMEM_IMAGE_VERSION_SIZE;
> +	if (size_in != size) {
> +		dev_err(dev, "Wrong size for smem item\n");
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	return ptr;
> +}
> +
> +
> +static u32 socinfo_get_version(void)
> +{
> +	return (socinfo) ? socinfo->v0_1.version : 0;

socinfo can't be NULL, so you can just inline "socinfo->v0_1.version" in
the one place calling this.

> +}
> +
> +static char *socinfo_get_build_id(void)
> +{
> +	return (socinfo) ? socinfo->v0_1.build_id : NULL;

socinfo can't be NULL, and this is only called from one place, so inline
"socinfo->v0_1.build_id" there.

> +}
> +
> +static u32 socinfo_get_raw_version(void)
> +{
> +	return socinfo ?

socinfo can't be NULL.

> +		(socinfo_format >= SOCINFO_FORMAT(2) ?

qcom_soc_attr_raw_version will not be created unless socinfo_format >=
2, so this can't be called if socinfo_format < 2, so you don't need this
extra check.

> +			socinfo->v0_2.raw_version : 0)
> +		: 0;

And as this is then simplified to "return socinfo->v0_2.raw_version;" I
think you should just inline it in the one place calling this function.

> +}
> +
> +static u32 socinfo_get_platform_type(void)
> +{
> +	return socinfo ?
> +		(socinfo_format >= SOCINFO_FORMAT(3) ?
> +			socinfo->v0_3.hw_platform : 0)
> +		: 0;
> +}
> +
> +static u32 socinfo_get_platform_version(void)
> +{
> +	return socinfo ?
> +		(socinfo_format >= SOCINFO_FORMAT(4) ?
> +			socinfo->v0_4.platform_version : 0)
> +		: 0;
> +}
> +
> +static u32 socinfo_get_platform_subtype(void)
> +{
> +	return socinfo ?
> +		(socinfo_format >= SOCINFO_FORMAT(6) ?
> +			socinfo->v0_6.hw_platform_subtype : 0)
> +		: 0;
> +}
> +
> +static u32 socinfo_get_serial_number(void)
> +{
> +	return socinfo ?
> +		(socinfo_format >= SOCINFO_FORMAT(10) ?
> +			socinfo->v0_10.serial_number : 0)
> +		: 0;
> +}
> +
> +static u32 socinfo_get_pmic_model(void)
> +{
> +	return socinfo ?
> +		(socinfo_format >= SOCINFO_FORMAT(7) ?
> +			socinfo->v0_7.pmic_model : PMIC_MODEL_UNKNOWN)
> +		: PMIC_MODEL_UNKNOWN;
> +}
> +
> +static u32 socinfo_get_pmic_die_revision(void)
> +{
> +	return socinfo ?
> +		(socinfo_format >= SOCINFO_FORMAT(7) ?
> +			socinfo->v0_7.pmic_die_revision : 0)
> +		: 0;
> +}
> +
> +
> +static ssize_t
> +qcom_get_vendor(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	return sprintf(buf, "%s", "Qualcomm\n");
> +}
> +
> +static ssize_t
> +qcom_get_raw_version(struct device *dev,
> +		     struct device_attribute *attr,
> +		     char *buf)

This is the "show" function of an attr, so please drop the "get" and add
"_show".

> +{
> +	return sprintf(buf, "%u\n",
> +		socinfo_get_raw_version());

And please inline as much as you can of these, there's little point in
having a helper function to wrap a single line accessing the element in
the socinfo struct.

> +}
> +
> +static ssize_t
> +qcom_get_build_id(struct device *dev,
> +		   struct device_attribute *attr,
> +		   char *buf)
> +{
> +	return scnprintf(buf, SMEM_SOCINFO_BUILD_ID_LENGTH, "%s\n",
> +				socinfo_get_build_id());
> +}
> +
> +static ssize_t
> +qcom_get_hw_platform(struct device *dev,
> +			struct device_attribute *attr,
> +			char *buf)
> +{
> +	u32 hw_type;
> +
> +	hw_type = socinfo_get_platform_type();
> +
> +	return sprintf(buf, "%s\n", hw_platform[hw_type]);
> +}
> +
> +static ssize_t
> +qcom_get_platform_version(struct device *dev,
> +				struct device_attribute *attr,
> +				char *buf)
> +{
> +	return sprintf(buf, "%u\n", socinfo_get_platform_version());
> +}
> +
> +static ssize_t
> +qcom_get_accessory_chip(struct device *dev,
> +				struct device_attribute *attr,
> +				char *buf)
> +{
> +	u32 acc_chip = socinfo ? (socinfo_format >= SOCINFO_FORMAT(5) ?
> +		socinfo->v0_5.accessory_chip : 0) : 0;
> +	return sprintf(buf, "%u\n", acc_chip);
> +}
> +
> +static ssize_t
> +qcom_get_platform_subtype(struct device *dev,
> +			struct device_attribute *attr,
> +			char *buf)
> +{
> +	if (socinfo_get_platform_type() == HW_PLATFORM_QRD) {
> +		return sprintf(buf, "%s\n",
> +			qrd_hw_platform_subtype[hw_subtype]);
> +	} else {
> +		return sprintf(buf, "%s\n",
> +			hw_platform_subtype[hw_subtype]);
> +	}
> +}
> +
> +static ssize_t
> +qcom_get_platform_subtype_id(struct device *dev,
> +			struct device_attribute *attr,
> +			char *buf)
> +{
> +	return sprintf(buf, "%u\n", socinfo_get_platform_subtype());
> +}
> +
> +static ssize_t
> +qcom_get_foundry_id(struct device *dev,
> +			struct device_attribute *attr,
> +			char *buf)
> +{
> +	u32 foundry_id = socinfo ? (socinfo_format >= SOCINFO_FORMAT(9) ?
> +		socinfo->v0_9.foundry_id : 0) : 0;
> +	return sprintf(buf, "%u\n", foundry_id);
> +}
> +
> +static ssize_t
> +qcom_get_serial_number(struct device *dev,
> +			struct device_attribute *attr,
> +			char *buf)
> +{
> +	return sprintf(buf, "%u\n", socinfo_get_serial_number());
> +}
> +
> +static ssize_t
> +qcom_get_chip_family(struct device *dev,
> +			struct device_attribute *attr,
> +			char *buf)
> +{
> +	u32 chip_family = socinfo ? (socinfo_format >= SOCINFO_FORMAT(12) ?
> +		socinfo->v0_12.chip_family : 0) : 0;
> +	return sprintf(buf, "0x%x\n", chip_family);
> +}
> +
> +static ssize_t
> +qcom_get_raw_device_family(struct device *dev,
> +			struct device_attribute *attr,
> +			char *buf)
> +{
> +	u32 raw_dev_fam = socinfo ? (socinfo_format >= SOCINFO_FORMAT(12) ?
> +		socinfo->v0_12.raw_device_family : 0) : 0;
> +	return sprintf(buf, "0x%x\n", raw_dev_fam);
> +}
> +
> +static ssize_t
> +qcom_get_raw_device_number(struct device *dev,
> +			struct device_attribute *attr,
> +			char *buf)
> +{
> +	u32 raw_dev_num = socinfo ? (socinfo_format >= SOCINFO_FORMAT(12) ?
> +		socinfo->v0_12.raw_device_number : 0) : 0;
> +	return sprintf(buf, "0x%x\n", raw_dev_num);
> +}
> +
> +static ssize_t
> +qcom_get_pmic_model(struct device *dev,
> +			struct device_attribute *attr,
> +			char *buf)
> +{
> +	u32 pmic_id = socinfo_get_pmic_model();
> +
> +	return sprintf(buf, "%s\n", pmic_model[pmic_id]);
> +}
> +
> +static ssize_t
> +qcom_get_pmic_die_revision(struct device *dev,
> +			       struct device_attribute *attr,
> +			       char *buf)
> +{
> +	return sprintf(buf, "%u\n", socinfo_get_pmic_die_revision());
> +}
> +
> +static ssize_t
> +qcom_get_image_version(int index, char *buf)
> +{

Just inline these into the version show() and store() functions.  And
whenever you copy to the sysfs buffer, it's of size PAGE_SIZE.

> +	return scnprintf(buf, SMEM_IMAGE_VERSION_NAME_SIZE, "%s\n",
> +			smem_image_version[index].name);
> +}
> +
> +static ssize_t
> +qcom_set_image_version(int index, const char *buf)
> +{
> +	return strlcpy(smem_image_version[index].name, buf,
> +			SMEM_IMAGE_VERSION_NAME_SIZE);
> +}
> +
> +static ssize_t
> +qcom_get_image_variant(int index, char *buf)
> +{
> +	return scnprintf(buf, SMEM_IMAGE_VERSION_VARIANT_SIZE, "%s\n",
> +			smem_image_version[index].variant);
> +}
> +
> +static ssize_t
> +qcom_set_image_variant(int index, const char *buf)
> +{
> +	return strlcpy(smem_image_version[index].variant, buf,
> +			SMEM_IMAGE_VERSION_VARIANT_SIZE);
> +}
> +
> +static ssize_t
> +qcom_get_image_crm_version(int index, char *buf)
> +{
> +	return scnprintf(buf, SMEM_IMAGE_VERSION_OEM_SIZE, "%s\n",
> +			smem_image_version[index].oem);
> +}
> +
> +static ssize_t
> +qcom_set_image_crm_version(int index, const char *buf)
> +{
> +	return strlcpy(smem_image_version[index].oem, buf,
> +			SMEM_IMAGE_VERSION_OEM_SIZE);
> +}
> +
> +static struct device_attribute qcom_soc_attr_raw_version =
> +	__ATTR(raw_version, 0444, qcom_get_raw_version,  NULL);

Rather than writing all these out here you can put them in an array of:

struct qcom_socinfo_attr {
	struct device_attribute attr;
	int min_version;
};

#define QCOM_SOCINFO_ATTR(_name, _show, _min_version) \
	{ __ATTR(_name, 0444, _show, NULL), _min_version) }

static const struct qcom_socinfo_attr qcom_socinfo_attrs[] = {
	QCOM_SOCINFO_ATTR(raw_version, qcom_get_raw_version, 2),
};

And then socinfo_populate_sysfs_files() will be a matter of looping over
this array and comparing min_version and registering the attr.

> +
> +static struct device_attribute qcom_soc_attr_vendor =
> +	__ATTR(vendor, 0444, qcom_get_vendor,  NULL);
> +
> +static struct device_attribute qcom_soc_attr_build_id =
> +	__ATTR(build_id, 0444, qcom_get_build_id, NULL);
> +
> +static struct device_attribute qcom_soc_attr_hw_platform =
> +	__ATTR(hw_platform, 0444, qcom_get_hw_platform, NULL);
> +
> +
> +static struct device_attribute qcom_soc_attr_platform_version =
> +	__ATTR(platform_version, 0444,
> +			qcom_get_platform_version, NULL);
> +
> +static struct device_attribute qcom_soc_attr_accessory_chip =
> +	__ATTR(accessory_chip, 0444,
> +			qcom_get_accessory_chip, NULL);
> +
> +static struct device_attribute qcom_soc_attr_platform_subtype =
> +	__ATTR(platform_subtype, 0444,
> +			qcom_get_platform_subtype, NULL);
> +
> +static struct device_attribute qcom_soc_attr_platform_subtype_id =
> +	__ATTR(platform_subtype_id, 0444,
> +			qcom_get_platform_subtype_id, NULL);
> +
> +static struct device_attribute qcom_soc_attr_foundry_id =
> +	__ATTR(foundry_id, 0444,
> +			qcom_get_foundry_id, NULL);
> +
> +static struct device_attribute qcom_soc_attr_serial_number =
> +	__ATTR(serial_number, 0444,
> +			qcom_get_serial_number, NULL);
> +
> +static struct device_attribute qcom_soc_attr_chip_family =
> +	__ATTR(chip_family, 0444,
> +			qcom_get_chip_family, NULL);
> +
> +static struct device_attribute qcom_soc_attr_raw_device_family =
> +	__ATTR(raw_device_family, 0444,
> +			qcom_get_raw_device_family, NULL);
> +
> +static struct device_attribute qcom_soc_attr_raw_device_number =
> +	__ATTR(raw_device_number, 0444,
> +			qcom_get_raw_device_number, NULL);
> +
> +static struct device_attribute qcom_soc_attr_pmic_model =
> +	__ATTR(pmic_model, 0444,
> +			qcom_get_pmic_model, NULL);
> +
> +static struct device_attribute qcom_soc_attr_pmic_die_revision =
> +	__ATTR(pmic_die_revision, 0444,
> +			qcom_get_pmic_die_revision, NULL);
> +
> +SMEM_IMAGE_TABLE_RO_ATTR(boot, SMEM_IMAGE_TABLE_BOOT_INDEX);
> +SMEM_IMAGE_TABLE_RO_ATTR(tz, SMEM_IMAGE_TABLE_TZ_INDEX);
> +SMEM_IMAGE_TABLE_RO_ATTR(rpm, SMEM_IMAGE_TABLE_RPM_INDEX);
> +SMEM_IMAGE_TABLE_ATTR(apps, SMEM_IMAGE_TABLE_APPS_INDEX);
> +SMEM_IMAGE_TABLE_RO_ATTR(mpss, SMEM_IMAGE_TABLE_MPSS_INDEX);
> +SMEM_IMAGE_TABLE_RO_ATTR(adsp, SMEM_IMAGE_TABLE_ADSP_INDEX);
> +SMEM_IMAGE_TABLE_RO_ATTR(cnss, SMEM_IMAGE_TABLE_CNSS_INDEX);
> +SMEM_IMAGE_TABLE_RO_ATTR(video, SMEM_IMAGE_TABLE_VIDEO_INDEX);
> +
> +static struct attribute_group
> +	*smem_image_table[SMEM_IMAGE_VERSION_BLOCKS_COUNT] = {
> +		[SMEM_IMAGE_TABLE_BOOT_INDEX] = &boot_image_attr_group,
> +		[SMEM_IMAGE_TABLE_TZ_INDEX] = &tz_image_attr_group,
> +		[SMEM_IMAGE_TABLE_RPM_INDEX] = &rpm_image_attr_group,
> +		[SMEM_IMAGE_TABLE_APPS_INDEX] = &apps_image_attr_group,
> +		[SMEM_IMAGE_TABLE_MPSS_INDEX] = &mpss_image_attr_group,
> +		[SMEM_IMAGE_TABLE_ADSP_INDEX] = &adsp_image_attr_group,
> +		[SMEM_IMAGE_TABLE_CNSS_INDEX] = &cnss_image_attr_group,
> +		[SMEM_IMAGE_TABLE_VIDEO_INDEX] = &video_image_attr_group,
> +};
> +
> +static int socinfo_populate_sysfs_files(struct device *qcom_soc_device)
> +{
> +	int err, img_idx;
> +
> +	/*
> +	 * Expose SMEM_IMAGE_TABLE to sysfs only when we have IMAGE_TABLE
> +	 * available in SMEM. As IMAGE_TABLE and SOCINFO are two separate
> +	 * items within SMEM, we expose the remaining soc information(i.e
> +	 * only the SOCINFO item available in SMEM) to sysfs even in the
> +	 * absence of an IMAGE_TABLE.
> +	 */
> +	if (smem_img_tbl_avlbl) {
> +		for (img_idx = 0; img_idx < SMEM_IMAGE_VERSION_BLOCKS_COUNT;
> +				img_idx++) {
> +			if (smem_image_table[img_idx]) {
> +				err = sysfs_create_group(&qcom_soc_device->kobj,
> +					smem_image_table[img_idx]);
> +				if (err)
> +					break;

Should we just ignore this error?

> +			}
> +		}
> +	}
> +	err = device_create_file(qcom_soc_device, &qcom_soc_attr_vendor);
> +	if (!err) {

Flip this conditional around and handle the error here, it saves you one
indentation level for the rest of this function.

> +		switch (socinfo_format) {
> +		case SOCINFO_FORMAT(12):
> +			err = device_create_file(qcom_soc_device,
> +					&qcom_soc_attr_chip_family);
> +			if (!err)
> +				err = device_create_file(qcom_soc_device,
> +					&qcom_soc_attr_raw_device_family);
> +			if (!err)
> +				err = device_create_file(qcom_soc_device,
> +					&qcom_soc_attr_raw_device_number);
> +		case SOCINFO_FORMAT(11):
> +		case SOCINFO_FORMAT(10):
> +			if (!err)
> +				err = device_create_file(qcom_soc_device,
> +					&qcom_soc_attr_serial_number);
> +		case SOCINFO_FORMAT(9):
> +			if (!err)
> +				err = device_create_file(qcom_soc_device,
> +					&qcom_soc_attr_foundry_id);
> +		case SOCINFO_FORMAT(8):
> +		case SOCINFO_FORMAT(7):
> +			if (!err)
> +				err = device_create_file(qcom_soc_device,
> +					&qcom_soc_attr_pmic_model);
> +			if (!err)
> +				err = device_create_file(qcom_soc_device,
> +					&qcom_soc_attr_pmic_die_revision);
> +		case SOCINFO_FORMAT(6):
> +			if (hw_subtype_valid)
> +				if (!err)
> +					err = device_create_file(
> +						qcom_soc_device,
> +					&qcom_soc_attr_platform_subtype);
> +			if (!err)
> +				err = device_create_file(qcom_soc_device,
> +				&qcom_soc_attr_platform_subtype_id);
> +		case SOCINFO_FORMAT(5):
> +			if (!err)
> +				err = device_create_file(qcom_soc_device,
> +					&qcom_soc_attr_accessory_chip);
> +		case SOCINFO_FORMAT(4):
> +			if (!err)
> +				err = device_create_file(qcom_soc_device,
> +					&qcom_soc_attr_platform_version);
> +		case SOCINFO_FORMAT(3):
> +			if (!err)
> +				err = device_create_file(qcom_soc_device,
> +					&qcom_soc_attr_hw_platform);
> +		case SOCINFO_FORMAT(2):
> +			if (!err)
> +				err = device_create_file(qcom_soc_device,
> +						&qcom_soc_attr_raw_version);
> +		case SOCINFO_FORMAT(1):
> +			if (!err)
> +				err = device_create_file(qcom_soc_device,
> +						&qcom_soc_attr_build_id);
> +			if (err) {
> +				dev_err(qcom_soc_device,
> +					"Could not create sysfs entry\n");
> +				return err;
> +			}
> +			return 0;
> +		default:
> +			dev_err(qcom_soc_device, "Unknown socinfo format: v0.%u\n",
> +					SOCINFO_VERSION_MINOR(socinfo_format));
> +			return -EINVAL;
> +		}
> +	} else {
> +		dev_err(qcom_soc_device, "Could not create sysfs entry\n");
> +		return err;
> +	}
> +}
> +
> +static int socinfo_init_sysfs(struct device *dev)

This device * is specifically the smem device, so if you name it
"smem_dev"...

> +{
> +	struct device *qcom_soc_device;

...then you can name this "dev" to shorten things.

> +	struct soc_device *soc_dev;
> +	struct soc_device_attribute *soc_dev_attr;

"attr" would be long enough.

> +	u32 soc_version;
> +
> +	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
> +	if (!soc_dev_attr)
> +		return -ENOMEM;
> +
> +	soc_version = socinfo_get_version();
> +
> +	soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%d", socinfo_get_id());
> +	soc_dev_attr->family = "Snapdragon";
> +	soc_dev_attr->machine = socinfo_get_id_string();

Single use of socinfo_get_id_string() so you should just go:

unsigned int id = socinfo->v0_1.id;

and then use that for your soc_id print and use soc_id_string[id] here.

> +	soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%u.%u",
> +				SOC_VERSION_MAJOR(soc_version),
> +				SOC_VERSION_MINOR(soc_version));
> +
> +	soc_dev = soc_device_register(soc_dev_attr);
> +	if (IS_ERR(soc_dev)) {
> +		kfree(soc_dev_attr);
> +		return PTR_ERR(soc_dev);
> +	}
> +
> +	qcom_soc_device = soc_device_to_device(soc_dev);
> +	socinfo_populate_sysfs_files(qcom_soc_device);
> +	return 0;
> +}
> +
> +static u32 socinfo_get_id(void)
> +{
> +	return (socinfo) ? socinfo->v0_1.id : 0;

As we no longer expose the socinfo functions to external callers this
will only ever be called if socinfo_init() succeeded, so no need to
check for socinfo being NULL.

> +}
> +
> +static int socinfo_select_format(struct device *dev)
> +{
> +	u32 f_maj = SOCINFO_VERSION_MAJOR(socinfo->v0_1.format);
> +	u32 f_min = SOCINFO_VERSION_MINOR(socinfo->v0_1.format);
> +
> +	if (f_maj != 0) {
> +		dev_err(dev, "Unsupported format v%u.%u.\n",
> +			f_maj, f_min);
> +		return -EINVAL;
> +	}
> +
> +	if (socinfo->v0_1.format > MAX_SOCINFO_FORMAT) {
> +		dev_warn(dev, "Unsupported format v%u.%u. Falling back to v%u.%u.\n",
> +			f_maj, f_min, SOCINFO_VERSION_MAJOR(MAX_SOCINFO_FORMAT),
> +			SOCINFO_VERSION_MINOR(MAX_SOCINFO_FORMAT));
> +		socinfo_format = MAX_SOCINFO_FORMAT;

Don't fall back, just print an error and fail.

> +	} else {
> +		socinfo_format = socinfo->v0_1.format;
> +	}
> +	return 0;
> +}
> +
> +int qcom_socinfo_init(struct platform_device *pdev)

As smem doesn't care if we're failing here you can just have the return
type void.

> +{
> +	size_t size;
> +
> +	socinfo = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID,
> +			&size);
> +	if (IS_ERR(socinfo) || (socinfo_select_format(&pdev->dev) < 0)) {
> +		dev_warn(&pdev->dev,
> +			"Coudn't find soc information; Unable to setup socinfo.\n");
> +		return -ENOMEM;

"Couldn't find socinfo" and return -ENOENT if IS_ERR(socinfo).

> +	}

And then just move the rest of socinfo_select_format() here:

	if (socinfo->v0.1.format > MAX_SOCINFO_FORMAT) {
		dev_err(&pdev->dev, "Invalid socinfo version %d.%d\n",
			MAJOR(), MINOR());
		return -EINVAL;
	}

> +
> +	if (!socinfo_get_id()) {
> +		dev_err(&pdev->dev, "socinfo: Unknown SoC ID!\n");
> +		return -EINVAL;
> +	}
> +
> +	WARN(socinfo_get_id() >= ARRAY_SIZE(cpu_of_id),
> +		"New IDs added! ID => CPU mapping needs an update.\n");
> +
> +	smem_image_version = (struct smem_image_version *)
> +			socinfo_get_image_version_base_address(&pdev->dev);

You could make socinfo_get_image_version_base_address() return void *
and you don't have to do the type cast. But you can make this more
compact by just inlining the qcom_smem_get() here directly.

	smem_image_version = qcom_smem_get(QCOM_SMEM_HOST_ANY,
					   SMEM_IMAGE_VERSION_TABLE,
					   &size);
	if (IS_ERR(smem_image_version) || size != SMEM_IMAGE_VERSION_SIZE) {
		...
	}

> +	if (IS_ERR(smem_image_version)) {
> +		dev_warn(&pdev->dev, "Unable to get address for image version table.\n");

"Image version table missing" should keep you < 80 characters.

> +		smem_img_tbl_avlbl = false;
> +	}
> +
> +	hw_subtype = socinfo_get_platform_subtype();
> +	if (socinfo_get_platform_type() == HW_PLATFORM_QRD) {
> +		if (hw_subtype >= PLATFORM_SUBTYPE_QRD_INVALID) {
> +			dev_err(&pdev->dev, "Invalid hardware platform sub type for qrd found\n");
> +			hw_subtype_valid = false;
> +		}
> +	} else {
> +		if (hw_subtype >= PLATFORM_SUBTYPE_INVALID) {
> +			dev_err(&pdev->dev, "Invalid hardware platform subtype\n");
> +			hw_subtype_valid = false;
> +		}
> +	}

This will only matter if format >= 6 and hw_subtype is outside the array
of strings, I think you should just be optimistic about this not being
an issue and then in all cases where you dereference a lookup table
check that you're within bounds, or fail with -EINVAL; to userspace.

> +
> +	socinfo_init_sysfs(&pdev->dev);

socinfo_init_sysfs() is just the second half of this function, so inline
it here.

> +
> +	/* Feed the soc specific unique data into entropy pool */
> +	add_device_randomness(socinfo, size);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(qcom_socinfo_init);
> +

No empty lines at the end of the file, please.

Regards,
Bjorn
--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Khan, Imran Dec. 12, 2016, 3:14 p.m. UTC | #2
On 12/5/2016 11:35 PM, Bjorn Andersson wrote:
> On Wed 16 Nov 05:22 PST 2016, Imran Khan wrote:
> 
<snip>
>>  
>> diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c
>> new file mode 100644
>> index 0000000..eca46df
>> --- /dev/null
>> +++ b/drivers/soc/qcom/socinfo.c
>> @@ -0,0 +1,989 @@
>> +/*
>> + * Copyright (c) 2009-2016, The Linux Foundation. All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 and
>> + * only version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + */
>> +
>> +#include <linux/export.h>
>> +#include <linux/module.h>
>> +#include <linux/err.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/sys_soc.h>
>> +#include <linux/slab.h>
>> +#include <linux/stat.h>
>> +#include <linux/string.h>
>> +#include <linux/types.h>
>> +#include <linux/random.h>
>> +#include <linux/soc/qcom/smem.h>
>> +
>> +#define PMIC_MODEL_UNKNOWN 0
>> +#define HW_PLATFORM_QRD 11
>> +#define PLATFORM_SUBTYPE_QRD_INVALID 6
>> +#define PLATFORM_SUBTYPE_INVALID 4
>> +/*
>> + * SOC version type with major number in the upper 16 bits and minor
>> + * number in the lower 16 bits.  For example:
>> + *   1.0 -> 0x00010000
>> + *   2.3 -> 0x00020003
>> + */
>> +#define SOC_VERSION_MAJOR(ver) (((ver) & 0xffff0000) >> 16)
>> +#define SOC_VERSION_MINOR(ver) ((ver) & 0x0000ffff)
>> +#define SOCINFO_FORMAT(x)  (x)
>> +#define SOCINFO_VERSION_MAJOR	SOC_VERSION_MAJOR
>> +#define SOCINFO_VERSION_MINOR	SOC_VERSION_MINOR
>> +
>> +#define SMEM_SOCINFO_BUILD_ID_LENGTH 32
>> +#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
>> +#define SMEM_IMAGE_VERSION_PARTITION_APPS 10
> 
> Please indent the values evenly, making this slightly easier to read.
> 
Done.

>> +
>> +/*
>> + * Shared memory identifiers, used to acquire handles to respective memory
>> + * region.
>> + */
>> +#define SMEM_IMAGE_VERSION_TABLE	469
>> +
>> +/*
>> + * Shared memory identifiers, used to acquire handles to respective memory
>> + * region.
>> + */
> 
> Duplicating this comment doesn't add value, please just drop this second
> copy and replace the above comment with something like "SMEM item ids"
> 
Done.
>> +#define SMEM_HW_SW_BUILD_ID		137
>> +
>> +/*
>> + * SMEM Image table indices
>> + */
<snip>
> 
> Rather than having this based on two huge macros take a look at
> DEVICE_INT_ATTR() and struct dev_ext_attribute in
> include/linux/device.h. It looks like if you just put the index in a
> struct and use container_of to reach that you can replace these with
> direct functions.
> 
> Also, it's perfectly fine to always specify a store operation and use
> 0444 vs 0644 to control it's availability. So you don't need two sets if
> you just expose the mode in your macro.
>

If I understood this correct, the main idea here is to use dev_ext_attribute
and avoid wrapper macros. Have tried to implement this suggestion in the 
subsequent patch set. Please let me know if it looks okay or can be improved
further. 
 
>> +
>> +/*
>> + * Qcom SoC types
>> + */
>> +enum qcom_cpu {
>> +	MSM_CPU_UNKNOWN = 0,
>> +	MSM_CPU_8960,
>> +	MSM_CPU_8960AB,
>> +	MSM_CPU_8064,
>> +	MSM_CPU_8974,
>> +	MSM_CPU_8974PRO_AA,
>> +	MSM_CPU_8974PRO_AB,
>> +	MSM_CPU_8974PRO_AC,
>> +	MSM_CPU_8916,
>> +	MSM_CPU_8084,
>> +	MSM_CPU_8996
>> +};
> 
> These are now "unused" (per below comment).
> 

Done. As these are no more needed.
>> +
>> +struct qcom_soc_info {
>> +	enum qcom_cpu generic_soc_type;
> 
> This enum is no longer used, seems like you can turn cpu_of_id into a
> simple char *[] array with with soc_id_string indexed by id.
> 
True. Have implemented cpu_of_id as per this suggestion.

>> +	char *soc_id_string;
>> +};
>> +
<snip>
>> +
>> +static struct smem_image_version *smem_image_version;
>> +static bool smem_img_tbl_avlbl = true;
> 
> You don't need to keep track of this separately, just test if
> smem_image_version is NULL or not (reset it to NULL if qcom_smem_get()
> fails).
> 
Done.

>> +static bool hw_subtype_valid = true;
>> +static u32 hw_subtype;
> 
> Don't keep track of this in a global variable, it's perfectly fine to
> figure it out in qcom_get_platform_subtype()
> 
Done.
>> +
>> +
>> +/* max socinfo format version supported */
>> +#define MAX_SOCINFO_FORMAT SOCINFO_FORMAT(12)
> 
> It's fine to drop SOCINFO_FORMAT and just put 12 here.
> 
Done.
>> +
>> +static struct qcom_soc_info cpu_of_id[] = {
>> +
>> +	[0] = {MSM_CPU_UNKNOWN, "Unknown CPU"},
>> +
<snip>
>> +static u32 socinfo_format;
>> +
>> +static u32 socinfo_get_id(void);
>> +
> 
> Merge socinfo_init_sysfs() and qcom_socinfo_init() into one function and
> you don't need to declare this here.
>
Done.
 
>> +/* socinfo: sysfs functions */
>> +
>> +static char *socinfo_get_id_string(void)
>> +{
>> +	return (socinfo) ? cpu_of_id[socinfo->v0_1.id].soc_id_string : NULL;
> 
> socinfo can't be NULL anymore.
> 
True. This checking is redundant indeed. In the subsequent patch set I
have removed redundant checking of socinfo being NULL or not.

>> +}
>> +
>> +static char *socinfo_get_image_version_base_address(struct device *dev)
> 
> Inline this into qcom_socinfo_init() as described below.
> 
Done.

>> +{
>> +	size_t size, size_in;
>> +	void *ptr;
>> +
>> +	ptr = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_IMAGE_VERSION_TABLE,
>> +			&size);
>> +	if (IS_ERR(ptr))
>> +		return ptr;
>> +
>> +	size_in = SMEM_IMAGE_VERSION_SIZE;
>> +	if (size_in != size) {
>> +		dev_err(dev, "Wrong size for smem item\n");
>> +		return ERR_PTR(-EINVAL);
>> +	}
>> +
>> +	return ptr;
>> +}
>> +
>> +
>> +static u32 socinfo_get_version(void)
>> +{
>> +	return (socinfo) ? socinfo->v0_1.version : 0;
> 
> socinfo can't be NULL, so you can just inline "socinfo->v0_1.version" in
> the one place calling this.
> 
Done.

>> +}
>> +
>> +static char *socinfo_get_build_id(void)
>> +{
>> +	return (socinfo) ? socinfo->v0_1.build_id : NULL;
> 
> socinfo can't be NULL, and this is only called from one place, so inline
> "socinfo->v0_1.build_id" there.
> 
Done.
>> +}
>> +
>> +static u32 socinfo_get_raw_version(void)
>> +{
>> +	return socinfo ?
> 
> socinfo can't be NULL.
> 
>> +		(socinfo_format >= SOCINFO_FORMAT(2) ?
> 
As mentioned above, have taken care of this.

> qcom_soc_attr_raw_version will not be created unless socinfo_format >=
> 2, so this can't be called if socinfo_format < 2, so you don't need this
> extra check.
> 
>> +			socinfo->v0_2.raw_version : 0)
>> +		: 0;
> 
> And as this is then simplified to "return socinfo->v0_2.raw_version;" I
> think you should just inline it in the one place calling this function.
> 
Done. Have removed redundant checking of socinfo format version from 
this and other functions and also have made these functions as much inline
as possible.

>> +}
>> +
>> +static u32 socinfo_get_platform_type(void)
>> +{
>> +	return socinfo ?
>> +		(socinfo_format >= SOCINFO_FORMAT(3) ?
>> +			socinfo->v0_3.hw_platform : 0)
>> +		: 0;
>> +}
<snip>

>> +
>> +static ssize_t
>> +qcom_get_raw_version(struct device *dev,
>> +		     struct device_attribute *attr,
>> +		     char *buf)
> 
> This is the "show" function of an attr, so please drop the "get" and add
> "_show".
> 
Done. Have renamed attr show and store functions properly.
>> +{
>> +	return sprintf(buf, "%u\n",
>> +		socinfo_get_raw_version());
> 
> And please inline as much as you can of these, there's little point in
> having a helper function to wrap a single line accessing the element in
> the socinfo struct.
> 

Done. Have tried this but please let me know if you see any further scope
in this regard.

>> +}
>> +
>> +static ssize_t
>> +qcom_get_build_id(struct device *dev,
>> +		   struct device_attribute *attr,
>> +		   char *buf)
>> +{
>> +	return scnprintf(buf, SMEM_SOCINFO_BUILD_ID_LENGTH, "%s\n",
>> +				socinfo_get_build_id());
>> +}
>> +
<snip>
>> +
>> +static ssize_t
>> +qcom_get_pmic_die_revision(struct device *dev,
>> +			       struct device_attribute *attr,
>> +			       char *buf)
>> +{
>> +	return sprintf(buf, "%u\n", socinfo_get_pmic_die_revision());
>> +}
>> +
>> +static ssize_t
>> +qcom_get_image_version(int index, char *buf)
>> +{
> 
> Just inline these into the version show() and store() functions.  And
> whenever you copy to the sysfs buffer, it's of size PAGE_SIZE.
> 
Done.
>> +	return scnprintf(buf, SMEM_IMAGE_VERSION_NAME_SIZE, "%s\n",
>> +			smem_image_version[index].name);
>> +}
>> +
<snip>
>> +static ssize_t
>> +qcom_set_image_crm_version(int index, const char *buf)
>> +{
>> +	return strlcpy(smem_image_version[index].oem, buf,
>> +			SMEM_IMAGE_VERSION_OEM_SIZE);
>> +}
>> +
>> +static struct device_attribute qcom_soc_attr_raw_version =
>> +	__ATTR(raw_version, 0444, qcom_get_raw_version,  NULL);
> 
> Rather than writing all these out here you can put them in an array of:
> 
> struct qcom_socinfo_attr {
> 	struct device_attribute attr;
> 	int min_version;
> };
> 
> #define QCOM_SOCINFO_ATTR(_name, _show, _min_version) \
> 	{ __ATTR(_name, 0444, _show, NULL), _min_version) }
> 
> static const struct qcom_socinfo_attr qcom_socinfo_attrs[] = {
> 	QCOM_SOCINFO_ATTR(raw_version, qcom_get_raw_version, 2),
> };
> 
> And then socinfo_populate_sysfs_files() will be a matter of looping over
> this array and comparing min_version and registering the attr.
>
Done. This makes the implementation really neat and concise. Thanks for
this suggestion.
 
>> +
>> +static struct device_attribute qcom_soc_attr_vendor =
>> +	__ATTR(vendor, 0444, qcom_get_vendor,  NULL);
>> +
>> +static struct device_attribute qcom_soc_attr_build_id =
>> +	__ATTR(build_id, 0444, qcom_get_build_id, NULL);
<snip>
>> +	/*
>> +	 * Expose SMEM_IMAGE_TABLE to sysfs only when we have IMAGE_TABLE
>> +	 * available in SMEM. As IMAGE_TABLE and SOCINFO are two separate
>> +	 * items within SMEM, we expose the remaining soc information(i.e
>> +	 * only the SOCINFO item available in SMEM) to sysfs even in the
>> +	 * absence of an IMAGE_TABLE.
>> +	 */
>> +	if (smem_img_tbl_avlbl) {
>> +		for (img_idx = 0; img_idx < SMEM_IMAGE_VERSION_BLOCKS_COUNT;
>> +				img_idx++) {
>> +			if (smem_image_table[img_idx]) {
>> +				err = sysfs_create_group(&qcom_soc_device->kobj,
>> +					smem_image_table[img_idx]);
>> +				if (err)
>> +					break;
> 
> Should we just ignore this error?
> 
Yeah. I think we can and should ignore this error. Failing to create sysfs entry
for one item in image table should not deprive other SMEM image items.
>> +			}
>> +		}
>> +	}
>> +	err = device_create_file(qcom_soc_device, &qcom_soc_attr_vendor);
>> +	if (!err) {
> 
> Flip this conditional around and handle the error here, it saves you one
> indentation level for the rest of this function.
> 
Done. No more needed in new implementation.

>> +		switch (socinfo_format) {
>> +		case SOCINFO_FORMAT(12):
>> +			err = device_create_file(qcom_soc_device,
>> +					&qcom_soc_attr_chip_family);
>> +			if (!err)
>> +				err = device_create_file(qcom_soc_device,
>> +					&qcom_soc_attr_raw_device_family);
>> +			if (!err)
>> +				err = device_create_file(qcom_soc_device,
>> +					&qcom_soc_attr_raw_device_number);
<snip>
>> +			return 0;
>> +		default:
>> +			dev_err(qcom_soc_device, "Unknown socinfo format: v0.%u\n",
>> +					SOCINFO_VERSION_MINOR(socinfo_format));
>> +			return -EINVAL;
>> +		}
>> +	} else {
>> +		dev_err(qcom_soc_device, "Could not create sysfs entry\n");
>> +		return err;
>> +	}
>> +}
>> +
>> +static int socinfo_init_sysfs(struct device *dev)
> 
> This device * is specifically the smem device, so if you name it
> "smem_dev"...
> 
Done.
>> +{
>> +	struct device *qcom_soc_device;
> 
> ...then you can name this "dev" to shorten things.
>
Done.
 
>> +	struct soc_device *soc_dev;
>> +	struct soc_device_attribute *soc_dev_attr;
> 
> "attr" would be long enough.
>
Done.
 
>> +	u32 soc_version;
>> +
>> +	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
>> +	if (!soc_dev_attr)
>> +		return -ENOMEM;
>> +
>> +	soc_version = socinfo_get_version();
>> +
>> +	soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%d", socinfo_get_id());
>> +	soc_dev_attr->family = "Snapdragon";
>> +	soc_dev_attr->machine = socinfo_get_id_string();
> 
> Single use of socinfo_get_id_string() so you should just go:
> 
> unsigned int id = socinfo->v0_1.id;
> 
> and then use that for your soc_id print and use soc_id_string[id] here.
> 
Done.
>> +	soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%u.%u",
>> +				SOC_VERSION_MAJOR(soc_version),
>> +				SOC_VERSION_MINOR(soc_version));
>> +
>> +	soc_dev = soc_device_register(soc_dev_attr);
>> +	if (IS_ERR(soc_dev)) {
>> +		kfree(soc_dev_attr);
>> +		return PTR_ERR(soc_dev);
>> +	}
>> +
>> +	qcom_soc_device = soc_device_to_device(soc_dev);
>> +	socinfo_populate_sysfs_files(qcom_soc_device);
>> +	return 0;
>> +}
>> +
>> +static u32 socinfo_get_id(void)
>> +{
>> +	return (socinfo) ? socinfo->v0_1.id : 0;
> 
> As we no longer expose the socinfo functions to external callers this
> will only ever be called if socinfo_init() succeeded, so no need to
> check for socinfo being NULL.
> 
Done.
>> +}
>> +
>> +static int socinfo_select_format(struct device *dev)
>> +{
>> +	u32 f_maj = SOCINFO_VERSION_MAJOR(socinfo->v0_1.format);
>> +	u32 f_min = SOCINFO_VERSION_MINOR(socinfo->v0_1.format);
>> +
>> +	if (f_maj != 0) {
>> +		dev_err(dev, "Unsupported format v%u.%u.\n",
>> +			f_maj, f_min);
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (socinfo->v0_1.format > MAX_SOCINFO_FORMAT) {
>> +		dev_warn(dev, "Unsupported format v%u.%u. Falling back to v%u.%u.\n",
>> +			f_maj, f_min, SOCINFO_VERSION_MAJOR(MAX_SOCINFO_FORMAT),
>> +			SOCINFO_VERSION_MINOR(MAX_SOCINFO_FORMAT));
>> +		socinfo_format = MAX_SOCINFO_FORMAT;
> 
> Don't fall back, just print an error and fail.
> 
Done.
>> +	} else {
>> +		socinfo_format = socinfo->v0_1.format;
>> +	}
>> +	return 0;
>> +}
>> +
>> +int qcom_socinfo_init(struct platform_device *pdev)
> 
> As smem doesn't care if we're failing here you can just have the return
> type void.
> 
Done.
>> +{
>> +	size_t size;
>> +
>> +	socinfo = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID,
>> +			&size);
>> +	if (IS_ERR(socinfo) || (socinfo_select_format(&pdev->dev) < 0)) {
>> +		dev_warn(&pdev->dev,
>> +			"Coudn't find soc information; Unable to setup socinfo.\n");
>> +		return -ENOMEM;
> 
> "Couldn't find socinfo" and return -ENOENT if IS_ERR(socinfo).
> 
Done
>> +	}
> 
> And then just move the rest of socinfo_select_format() here:
> 
> 	if (socinfo->v0.1.format > MAX_SOCINFO_FORMAT) {
> 		dev_err(&pdev->dev, "Invalid socinfo version %d.%d\n",
> 			MAJOR(), MINOR());
> 		return -EINVAL;
> 	}
> 
Done
>> +
>> +	if (!socinfo_get_id()) {
>> +		dev_err(&pdev->dev, "socinfo: Unknown SoC ID!\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	WARN(socinfo_get_id() >= ARRAY_SIZE(cpu_of_id),
>> +		"New IDs added! ID => CPU mapping needs an update.\n");
>> +
>> +	smem_image_version = (struct smem_image_version *)
>> +			socinfo_get_image_version_base_address(&pdev->dev);
> 
> You could make socinfo_get_image_version_base_address() return void *
> and you don't have to do the type cast. But you can make this more
> compact by just inlining the qcom_smem_get() here directly.
> 
> 	smem_image_version = qcom_smem_get(QCOM_SMEM_HOST_ANY,
> 					   SMEM_IMAGE_VERSION_TABLE,
> 					   &size);
> 	if (IS_ERR(smem_image_version) || size != SMEM_IMAGE_VERSION_SIZE) {
> 		...
> 	}
> 

Done
>> +	if (IS_ERR(smem_image_version)) {
>> +		dev_warn(&pdev->dev, "Unable to get address for image version table.\n");
> 
> "Image version table missing" should keep you < 80 characters.
> 
Done.
>> +		smem_img_tbl_avlbl = false;
>> +	}
>> +
>> +	hw_subtype = socinfo_get_platform_subtype();
>> +	if (socinfo_get_platform_type() == HW_PLATFORM_QRD) {
>> +		if (hw_subtype >= PLATFORM_SUBTYPE_QRD_INVALID) {
>> +			dev_err(&pdev->dev, "Invalid hardware platform sub type for qrd found\n");
>> +			hw_subtype_valid = false;
>> +		}
>> +	} else {
>> +		if (hw_subtype >= PLATFORM_SUBTYPE_INVALID) {
>> +			dev_err(&pdev->dev, "Invalid hardware platform subtype\n");
>> +			hw_subtype_valid = false;
>> +		}
>> +	}
> 
> This will only matter if format >= 6 and hw_subtype is outside the array
> of strings, I think you should just be optimistic about this not being
> an issue and then in all cases where you dereference a lookup table
> check that you're within bounds, or fail with -EINVAL; to userspace.
> 
True. Have taken care of this. No more tracking this with a global variable.
>> +
>> +	socinfo_init_sysfs(&pdev->dev);
> 
> socinfo_init_sysfs() is just the second half of this function, so inline
> it here.
> 
Done. now this is inline.
>> +
>> +	/* Feed the soc specific unique data into entropy pool */
>> +	add_device_randomness(socinfo, size);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(qcom_socinfo_init);
>> +
> 
> No empty lines at the end of the file, please.
> 
Done.
> Regards,
> Bjorn
> 
Thanks Bjorn for review comments.
Regards,
Imran
Bjorn Andersson Dec. 12, 2016, 7:04 p.m. UTC | #3
On Mon 12 Dec 07:14 PST 2016, Imran Khan wrote:

> On 12/5/2016 11:35 PM, Bjorn Andersson wrote:
[..]
> > 
> > Rather than having this based on two huge macros take a look at
> > DEVICE_INT_ATTR() and struct dev_ext_attribute in
> > include/linux/device.h. It looks like if you just put the index in a
> > struct and use container_of to reach that you can replace these with
> > direct functions.
> > 
> > Also, it's perfectly fine to always specify a store operation and use
> > 0444 vs 0644 to control it's availability. So you don't need two sets if
> > you just expose the mode in your macro.
> >
> 
> If I understood this correct, the main idea here is to use dev_ext_attribute
> and avoid wrapper macros. Have tried to implement this suggestion in the 
> subsequent patch set. Please let me know if it looks okay or can be improved
> further. 
>  

Not necessarily using dev_ext_attribute directly, but using the same
design (a struct and container_of) allows you do parameterize these
functions.

Looking forward to v6.

Regards,
Bjorn
--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 461b387..f89d34d 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -24,6 +24,7 @@  config QCOM_SMEM
 	tristate "Qualcomm Shared Memory Manager (SMEM)"
 	depends on ARCH_QCOM
 	depends on HWSPINLOCK
+	select SOC_BUS
 	help
 	  Say y here to enable support for the Qualcomm Shared Memory Manager.
 	  The driver provides an interface to items in a heap shared among all
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index fdd664e..438efec 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -2,7 +2,8 @@  obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
 obj-$(CONFIG_QCOM_PM)	+=	spm.o
 obj-$(CONFIG_QCOM_SMD) +=	smd.o
 obj-$(CONFIG_QCOM_SMD_RPM)	+= smd-rpm.o
-obj-$(CONFIG_QCOM_SMEM) +=	smem.o
+obj-$(CONFIG_QCOM_SMEM) +=	qcom_smem.o
+qcom_smem-y := smem.o socinfo.o
 obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
 obj-$(CONFIG_QCOM_SMP2P)	+= smp2p.o
 obj-$(CONFIG_QCOM_SMSM)	+= smsm.o
diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
index 18ec52f..a57acfb0 100644
--- a/drivers/soc/qcom/smem.c
+++ b/drivers/soc/qcom/smem.c
@@ -85,6 +85,9 @@ 
 /* Max number of processors/hosts in a system */
 #define SMEM_HOST_COUNT		9
 
+
+extern int qcom_socinfo_init(struct platform_device *pdev);
+
 /**
   * struct smem_proc_comm - proc_comm communication struct (legacy)
   * @command:	current command to be executed
@@ -751,6 +754,8 @@  static int qcom_smem_probe(struct platform_device *pdev)
 
 	__smem = smem;
 
+	qcom_socinfo_init(pdev);
+
 	return 0;
 }
 
diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c
new file mode 100644
index 0000000..eca46df
--- /dev/null
+++ b/drivers/soc/qcom/socinfo.c
@@ -0,0 +1,989 @@ 
+/*
+ * Copyright (c) 2009-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/sys_soc.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/random.h>
+#include <linux/soc/qcom/smem.h>
+
+#define PMIC_MODEL_UNKNOWN 0
+#define HW_PLATFORM_QRD 11
+#define PLATFORM_SUBTYPE_QRD_INVALID 6
+#define PLATFORM_SUBTYPE_INVALID 4
+/*
+ * SOC version type with major number in the upper 16 bits and minor
+ * number in the lower 16 bits.  For example:
+ *   1.0 -> 0x00010000
+ *   2.3 -> 0x00020003
+ */
+#define SOC_VERSION_MAJOR(ver) (((ver) & 0xffff0000) >> 16)
+#define SOC_VERSION_MINOR(ver) ((ver) & 0x0000ffff)
+#define SOCINFO_FORMAT(x)  (x)
+#define SOCINFO_VERSION_MAJOR	SOC_VERSION_MAJOR
+#define SOCINFO_VERSION_MINOR	SOC_VERSION_MINOR
+
+#define SMEM_SOCINFO_BUILD_ID_LENGTH 32
+#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
+#define SMEM_IMAGE_VERSION_PARTITION_APPS 10
+
+/*
+ * Shared memory identifiers, used to acquire handles to respective memory
+ * region.
+ */
+#define SMEM_IMAGE_VERSION_TABLE	469
+
+/*
+ * Shared memory identifiers, used to acquire handles to respective memory
+ * region.
+ */
+#define SMEM_HW_SW_BUILD_ID		137
+
+/*
+ * SMEM Image table indices
+ */
+enum smem_image_table_index {
+	SMEM_IMAGE_TABLE_BOOT_INDEX = 0,
+	SMEM_IMAGE_TABLE_TZ_INDEX,
+	SMEM_IMAGE_TABLE_RPM_INDEX = 3,
+	SMEM_IMAGE_TABLE_APPS_INDEX = 10,
+	SMEM_IMAGE_TABLE_MPSS_INDEX,
+	SMEM_IMAGE_TABLE_ADSP_INDEX,
+	SMEM_IMAGE_TABLE_CNSS_INDEX,
+	SMEM_IMAGE_TABLE_VIDEO_INDEX
+};
+
+#define SMEM_IMAGE_TABLE_ATTR(type, index)				\
+static ssize_t								\
+qcom_get_##type##_image_version(struct device *dev,			\
+			struct device_attribute *attr,			\
+			char *buf)					\
+{									\
+	return	qcom_get_image_version(index, buf);			\
+}									\
+static ssize_t								\
+qcom_set_##type##_image_version(struct device *dev,			\
+			struct device_attribute *attr,			\
+			const char *buf,				\
+			size_t count)					\
+{									\
+	return	qcom_set_image_version(index, buf);			\
+}									\
+static ssize_t								\
+qcom_get_##type##_image_variant(struct device *dev,			\
+			struct device_attribute *attr,			\
+			char *buf)					\
+{									\
+	return	qcom_get_image_variant(index, buf);			\
+}									\
+static ssize_t								\
+qcom_set_##type##_image_variant(struct device *dev,			\
+			struct device_attribute *attr,			\
+			const char *buf,				\
+			size_t count)					\
+{									\
+	return	qcom_set_image_variant(index, buf);			\
+}									\
+static ssize_t								\
+qcom_get_##type##_image_crm_version(struct device *dev,			\
+			struct device_attribute *attr,			\
+			char *buf)					\
+{									\
+	return	qcom_get_image_crm_version(index, buf);			\
+}									\
+static ssize_t								\
+qcom_set_##type##_image_crm_version(struct device *dev,			\
+			struct device_attribute *attr,			\
+			const char *buf,				\
+			size_t count)					\
+{									\
+	return	qcom_set_image_crm_version(index, buf);			\
+}									\
+DEVICE_ATTR(type##_image_version, 0644, qcom_get_##type##_image_version,\
+				qcom_set_##type##_image_version);	\
+DEVICE_ATTR(type##_image_variant, 0644, qcom_get_##type##_image_variant,\
+				qcom_set_##type##_image_variant);	\
+DEVICE_ATTR(type##_image_crm_version, 0644,				\
+				qcom_get_##type##_image_crm_version,	\
+				qcom_set_##type##_image_crm_version);	\
+static struct attribute *type##_image_attrs[] = {			\
+	&dev_attr_##type##_image_version.attr,				\
+	&dev_attr_##type##_image_variant.attr,				\
+	&dev_attr_##type##_image_crm_version.attr,			\
+	NULL,								\
+};									\
+static struct attribute_group type##_image_attr_group = {		\
+	.attrs = type##_image_attrs,					\
+}
+
+#define SMEM_IMAGE_TABLE_RO_ATTR(type, index)				\
+static ssize_t								\
+qcom_get_##type##_image_version(struct device *dev,			\
+			struct device_attribute *attr,			\
+			char *buf)					\
+{									\
+	return	qcom_get_image_version(index, buf);			\
+}									\
+static ssize_t								\
+qcom_get_##type##_image_variant(struct device *dev,			\
+			struct device_attribute *attr,			\
+			char *buf)					\
+{									\
+	return	qcom_get_image_variant(index, buf);			\
+}									\
+static ssize_t								\
+qcom_get_##type##_image_crm_version(struct device *dev,			\
+			struct device_attribute *attr,			\
+			char *buf)					\
+{									\
+	return	qcom_get_image_crm_version(index, buf);			\
+}									\
+DEVICE_ATTR(type##_image_version, 0444, qcom_get_##type##_image_version,\
+					NULL);				\
+DEVICE_ATTR(type##_image_variant, 0444, qcom_get_##type##_image_variant,\
+					NULL);				\
+DEVICE_ATTR(type##_image_crm_version, 0444,				\
+				qcom_get_##type##_image_crm_version,	\
+					NULL);				\
+static struct attribute *type##_image_attrs[] = {			\
+	&dev_attr_##type##_image_version.attr,				\
+	&dev_attr_##type##_image_variant.attr,				\
+	&dev_attr_##type##_image_crm_version.attr,			\
+	NULL,								\
+	};								\
+static struct attribute_group type##_image_attr_group = {		\
+	.attrs = type##_image_attrs,					\
+}
+
+/*
+ * Qcom SoC types
+ */
+enum qcom_cpu {
+	MSM_CPU_UNKNOWN = 0,
+	MSM_CPU_8960,
+	MSM_CPU_8960AB,
+	MSM_CPU_8064,
+	MSM_CPU_8974,
+	MSM_CPU_8974PRO_AA,
+	MSM_CPU_8974PRO_AB,
+	MSM_CPU_8974PRO_AC,
+	MSM_CPU_8916,
+	MSM_CPU_8084,
+	MSM_CPU_8996
+};
+
+struct qcom_soc_info {
+	enum qcom_cpu generic_soc_type;
+	char *soc_id_string;
+};
+
+/* Hardware platform types */
+static const char *hw_platform[] = {
+	[0] = "Unknown",
+	[1] = "Surf",
+	[2] = "FFA",
+	[3] = "Fluid",
+	[4] = "SVLTE_FFA",
+	[5] = "SLVTE_SURF",
+	[7] = "MDM_MTP_NO_DISPLAY",
+	[8] = "MTP",
+	[9] = "Liquid",
+	[10] = "Dragon",
+	[11] = "QRD",
+	[13] = "HRD",
+	[14] = "DTV",
+	[21] = "RCM",
+	[23] = "STP",
+	[24] = "SBC",
+};
+
+static const char *qrd_hw_platform_subtype[] = {
+	[0] = "QRD",
+	[1] = "SKUAA",
+	[2] = "SKUF",
+	[3] = "SKUAB",
+	[5] = "SKUG",
+	[6] = "INVALID",
+};
+
+static const char *hw_platform_subtype[] = {
+	"Unknown", "charm", "strange", "strange_2a", "Invalid",
+};
+
+static const char *pmic_model[] = {
+	[0]  = "Unknown PMIC model",
+	[13] = "PMIC model: PM8058",
+	[14] = "PMIC model: PM8028",
+	[15] = "PMIC model: PM8901",
+	[16] = "PMIC model: PM8027",
+	[17] = "PMIC model: ISL9519",
+	[18] = "PMIC model: PM8921",
+	[19] = "PMIC model: PM8018",
+	[20] = "PMIC model: PM8015",
+	[21] = "PMIC model: PM8014",
+	[22] = "PMIC model: PM8821",
+	[23] = "PMIC model: PM8038",
+	[24] = "PMIC model: PM8922",
+	[25] = "PMIC model: PM8917",
+};
+
+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];
+};
+
+/* Used to parse shared memory. Must match the modem. */
+struct socinfo_v0_1 {
+	u32 format;
+	u32 id;
+	u32 version;
+	char build_id[SMEM_SOCINFO_BUILD_ID_LENGTH];
+};
+
+struct socinfo_v0_2 {
+	struct socinfo_v0_1 v0_1;
+	u32 raw_version;
+};
+
+struct socinfo_v0_3 {
+	struct socinfo_v0_2 v0_2;
+	u32 hw_platform;
+};
+
+struct socinfo_v0_4 {
+	struct socinfo_v0_3 v0_3;
+	u32 platform_version;
+};
+
+struct socinfo_v0_5 {
+	struct socinfo_v0_4 v0_4;
+	u32 accessory_chip;
+};
+
+struct socinfo_v0_6 {
+	struct socinfo_v0_5 v0_5;
+	u32 hw_platform_subtype;
+};
+
+struct socinfo_v0_7 {
+	struct socinfo_v0_6 v0_6;
+	u32 pmic_model;
+	u32 pmic_die_revision;
+};
+
+struct socinfo_v0_8 {
+	struct socinfo_v0_7 v0_7;
+	u32 pmic_model_1;
+	u32 pmic_die_revision_1;
+	u32 pmic_model_2;
+	u32 pmic_die_revision_2;
+};
+
+struct socinfo_v0_9 {
+	struct socinfo_v0_8 v0_8;
+	u32 foundry_id;
+};
+
+struct socinfo_v0_10 {
+	struct socinfo_v0_9 v0_9;
+	u32 serial_number;
+};
+
+struct socinfo_v0_11 {
+	struct socinfo_v0_10 v0_10;
+	u32 num_pmics;
+	u32 pmic_array_offset;
+};
+
+struct socinfo_v0_12 {
+	struct socinfo_v0_11 v0_11;
+	u32 chip_family;
+	u32 raw_device_family;
+	u32 raw_device_number;
+};
+
+static union {
+	struct socinfo_v0_1 v0_1;
+	struct socinfo_v0_2 v0_2;
+	struct socinfo_v0_3 v0_3;
+	struct socinfo_v0_4 v0_4;
+	struct socinfo_v0_5 v0_5;
+	struct socinfo_v0_6 v0_6;
+	struct socinfo_v0_7 v0_7;
+	struct socinfo_v0_8 v0_8;
+	struct socinfo_v0_9 v0_9;
+	struct socinfo_v0_10 v0_10;
+	struct socinfo_v0_11 v0_11;
+	struct socinfo_v0_12 v0_12;
+} *socinfo;
+
+static struct smem_image_version *smem_image_version;
+static bool smem_img_tbl_avlbl = true;
+static bool hw_subtype_valid = true;
+static u32 hw_subtype;
+
+
+/* max socinfo format version supported */
+#define MAX_SOCINFO_FORMAT SOCINFO_FORMAT(12)
+
+static struct qcom_soc_info cpu_of_id[] = {
+
+	[0] = {MSM_CPU_UNKNOWN, "Unknown CPU"},
+
+	/* 8x60 IDs */
+	[87] = {MSM_CPU_8960, "MSM8960"},
+
+	/* 8x64 IDs */
+	[109] = {MSM_CPU_8064, "APQ8064"},
+	[130] = {MSM_CPU_8064, "MPQ8064"},
+
+	/* 8x60A IDs */
+	[122] = {MSM_CPU_8960, "MSM8660A"},
+	[123] = {MSM_CPU_8960, "MSM8260A"},
+	[124] = {MSM_CPU_8960, "APQ8060A"},
+
+	/* 8x74 IDs */
+	[126] = {MSM_CPU_8974, "MSM8974"},
+	[184] = {MSM_CPU_8974, "APQ8074"},
+	[185] = {MSM_CPU_8974, "MSM8274"},
+	[186] = {MSM_CPU_8974, "MSM8674"},
+
+	/* 8x74AA IDs */
+	[208] = {MSM_CPU_8974PRO_AA, "APQ8074-AA"},
+	[211] = {MSM_CPU_8974PRO_AA, "MSM8274-AA"},
+	[214] = {MSM_CPU_8974PRO_AA, "MSM8674-AA"},
+	[217] = {MSM_CPU_8974PRO_AA, "MSM8974-AA"},
+
+	/* 8x74AB IDs */
+	[209] = {MSM_CPU_8974PRO_AB, "APQ8074-AB"},
+	[212] = {MSM_CPU_8974PRO_AB, "MSM8274-AB"},
+	[215] = {MSM_CPU_8974PRO_AB, "MSM8674-AB"},
+	[218] = {MSM_CPU_8974PRO_AB, "MSM8974-AB"},
+
+	/* 8x74AC IDs */
+	[194] = {MSM_CPU_8974PRO_AC, "MSM8974PRO"},
+	[210] = {MSM_CPU_8974PRO_AC, "APQ8074PRO"},
+	[213] = {MSM_CPU_8974PRO_AC, "MSM8274PRO"},
+	[216] = {MSM_CPU_8974PRO_AC, "MSM8674PRO"},
+
+	/* 8x60AB IDs */
+	[138] = {MSM_CPU_8960AB, "MSM8960AB"},
+	[139] = {MSM_CPU_8960AB, "APQ8060AB"},
+	[140] = {MSM_CPU_8960AB, "MSM8260AB"},
+	[141] = {MSM_CPU_8960AB, "MSM8660AB"},
+
+	/* 8x84 IDs */
+	[178] = {MSM_CPU_8084, "APQ8084"},
+
+	/* 8x16 IDs */
+	[206] = {MSM_CPU_8916, "MSM8916"},
+	[247] = {MSM_CPU_8916, "APQ8016"},
+	[248] = {MSM_CPU_8916, "MSM8216"},
+	[249] = {MSM_CPU_8916, "MSM8116"},
+	[250] = {MSM_CPU_8916, "MSM8616"},
+
+	/* 8x96 IDs */
+	[246] = {MSM_CPU_8996, "MSM8996"},
+	[310] = {MSM_CPU_8996, "MSM8996AU"},
+	[311] = {MSM_CPU_8996, "APQ8096AU"},
+	[291] = {MSM_CPU_8996, "APQ8096"},
+	[305] = {MSM_CPU_8996, "MSM8996SG"},
+	[312] = {MSM_CPU_8996, "APQ8096SG"},
+
+	/*
+	 * Uninitialized IDs are not known to run Linux.
+	 * MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are
+	 * considered as unknown CPU.
+	 */
+};
+
+static u32 socinfo_format;
+
+static u32 socinfo_get_id(void);
+
+/* socinfo: sysfs functions */
+
+static char *socinfo_get_id_string(void)
+{
+	return (socinfo) ? cpu_of_id[socinfo->v0_1.id].soc_id_string : NULL;
+}
+
+static char *socinfo_get_image_version_base_address(struct device *dev)
+{
+	size_t size, size_in;
+	void *ptr;
+
+	ptr = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_IMAGE_VERSION_TABLE,
+			&size);
+	if (IS_ERR(ptr))
+		return ptr;
+
+	size_in = SMEM_IMAGE_VERSION_SIZE;
+	if (size_in != size) {
+		dev_err(dev, "Wrong size for smem item\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	return ptr;
+}
+
+
+static u32 socinfo_get_version(void)
+{
+	return (socinfo) ? socinfo->v0_1.version : 0;
+}
+
+static char *socinfo_get_build_id(void)
+{
+	return (socinfo) ? socinfo->v0_1.build_id : NULL;
+}
+
+static u32 socinfo_get_raw_version(void)
+{
+	return socinfo ?
+		(socinfo_format >= SOCINFO_FORMAT(2) ?
+			socinfo->v0_2.raw_version : 0)
+		: 0;
+}
+
+static u32 socinfo_get_platform_type(void)
+{
+	return socinfo ?
+		(socinfo_format >= SOCINFO_FORMAT(3) ?
+			socinfo->v0_3.hw_platform : 0)
+		: 0;
+}
+
+static u32 socinfo_get_platform_version(void)
+{
+	return socinfo ?
+		(socinfo_format >= SOCINFO_FORMAT(4) ?
+			socinfo->v0_4.platform_version : 0)
+		: 0;
+}
+
+static u32 socinfo_get_platform_subtype(void)
+{
+	return socinfo ?
+		(socinfo_format >= SOCINFO_FORMAT(6) ?
+			socinfo->v0_6.hw_platform_subtype : 0)
+		: 0;
+}
+
+static u32 socinfo_get_serial_number(void)
+{
+	return socinfo ?
+		(socinfo_format >= SOCINFO_FORMAT(10) ?
+			socinfo->v0_10.serial_number : 0)
+		: 0;
+}
+
+static u32 socinfo_get_pmic_model(void)
+{
+	return socinfo ?
+		(socinfo_format >= SOCINFO_FORMAT(7) ?
+			socinfo->v0_7.pmic_model : PMIC_MODEL_UNKNOWN)
+		: PMIC_MODEL_UNKNOWN;
+}
+
+static u32 socinfo_get_pmic_die_revision(void)
+{
+	return socinfo ?
+		(socinfo_format >= SOCINFO_FORMAT(7) ?
+			socinfo->v0_7.pmic_die_revision : 0)
+		: 0;
+}
+
+
+static ssize_t
+qcom_get_vendor(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "%s", "Qualcomm\n");
+}
+
+static ssize_t
+qcom_get_raw_version(struct device *dev,
+		     struct device_attribute *attr,
+		     char *buf)
+{
+	return sprintf(buf, "%u\n",
+		socinfo_get_raw_version());
+}
+
+static ssize_t
+qcom_get_build_id(struct device *dev,
+		   struct device_attribute *attr,
+		   char *buf)
+{
+	return scnprintf(buf, SMEM_SOCINFO_BUILD_ID_LENGTH, "%s\n",
+				socinfo_get_build_id());
+}
+
+static ssize_t
+qcom_get_hw_platform(struct device *dev,
+			struct device_attribute *attr,
+			char *buf)
+{
+	u32 hw_type;
+
+	hw_type = socinfo_get_platform_type();
+
+	return sprintf(buf, "%s\n", hw_platform[hw_type]);
+}
+
+static ssize_t
+qcom_get_platform_version(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	return sprintf(buf, "%u\n", socinfo_get_platform_version());
+}
+
+static ssize_t
+qcom_get_accessory_chip(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	u32 acc_chip = socinfo ? (socinfo_format >= SOCINFO_FORMAT(5) ?
+		socinfo->v0_5.accessory_chip : 0) : 0;
+	return sprintf(buf, "%u\n", acc_chip);
+}
+
+static ssize_t
+qcom_get_platform_subtype(struct device *dev,
+			struct device_attribute *attr,
+			char *buf)
+{
+	if (socinfo_get_platform_type() == HW_PLATFORM_QRD) {
+		return sprintf(buf, "%s\n",
+			qrd_hw_platform_subtype[hw_subtype]);
+	} else {
+		return sprintf(buf, "%s\n",
+			hw_platform_subtype[hw_subtype]);
+	}
+}
+
+static ssize_t
+qcom_get_platform_subtype_id(struct device *dev,
+			struct device_attribute *attr,
+			char *buf)
+{
+	return sprintf(buf, "%u\n", socinfo_get_platform_subtype());
+}
+
+static ssize_t
+qcom_get_foundry_id(struct device *dev,
+			struct device_attribute *attr,
+			char *buf)
+{
+	u32 foundry_id = socinfo ? (socinfo_format >= SOCINFO_FORMAT(9) ?
+		socinfo->v0_9.foundry_id : 0) : 0;
+	return sprintf(buf, "%u\n", foundry_id);
+}
+
+static ssize_t
+qcom_get_serial_number(struct device *dev,
+			struct device_attribute *attr,
+			char *buf)
+{
+	return sprintf(buf, "%u\n", socinfo_get_serial_number());
+}
+
+static ssize_t
+qcom_get_chip_family(struct device *dev,
+			struct device_attribute *attr,
+			char *buf)
+{
+	u32 chip_family = socinfo ? (socinfo_format >= SOCINFO_FORMAT(12) ?
+		socinfo->v0_12.chip_family : 0) : 0;
+	return sprintf(buf, "0x%x\n", chip_family);
+}
+
+static ssize_t
+qcom_get_raw_device_family(struct device *dev,
+			struct device_attribute *attr,
+			char *buf)
+{
+	u32 raw_dev_fam = socinfo ? (socinfo_format >= SOCINFO_FORMAT(12) ?
+		socinfo->v0_12.raw_device_family : 0) : 0;
+	return sprintf(buf, "0x%x\n", raw_dev_fam);
+}
+
+static ssize_t
+qcom_get_raw_device_number(struct device *dev,
+			struct device_attribute *attr,
+			char *buf)
+{
+	u32 raw_dev_num = socinfo ? (socinfo_format >= SOCINFO_FORMAT(12) ?
+		socinfo->v0_12.raw_device_number : 0) : 0;
+	return sprintf(buf, "0x%x\n", raw_dev_num);
+}
+
+static ssize_t
+qcom_get_pmic_model(struct device *dev,
+			struct device_attribute *attr,
+			char *buf)
+{
+	u32 pmic_id = socinfo_get_pmic_model();
+
+	return sprintf(buf, "%s\n", pmic_model[pmic_id]);
+}
+
+static ssize_t
+qcom_get_pmic_die_revision(struct device *dev,
+			       struct device_attribute *attr,
+			       char *buf)
+{
+	return sprintf(buf, "%u\n", socinfo_get_pmic_die_revision());
+}
+
+static ssize_t
+qcom_get_image_version(int index, char *buf)
+{
+	return scnprintf(buf, SMEM_IMAGE_VERSION_NAME_SIZE, "%s\n",
+			smem_image_version[index].name);
+}
+
+static ssize_t
+qcom_set_image_version(int index, const char *buf)
+{
+	return strlcpy(smem_image_version[index].name, buf,
+			SMEM_IMAGE_VERSION_NAME_SIZE);
+}
+
+static ssize_t
+qcom_get_image_variant(int index, char *buf)
+{
+	return scnprintf(buf, SMEM_IMAGE_VERSION_VARIANT_SIZE, "%s\n",
+			smem_image_version[index].variant);
+}
+
+static ssize_t
+qcom_set_image_variant(int index, const char *buf)
+{
+	return strlcpy(smem_image_version[index].variant, buf,
+			SMEM_IMAGE_VERSION_VARIANT_SIZE);
+}
+
+static ssize_t
+qcom_get_image_crm_version(int index, char *buf)
+{
+	return scnprintf(buf, SMEM_IMAGE_VERSION_OEM_SIZE, "%s\n",
+			smem_image_version[index].oem);
+}
+
+static ssize_t
+qcom_set_image_crm_version(int index, const char *buf)
+{
+	return strlcpy(smem_image_version[index].oem, buf,
+			SMEM_IMAGE_VERSION_OEM_SIZE);
+}
+
+static struct device_attribute qcom_soc_attr_raw_version =
+	__ATTR(raw_version, 0444, qcom_get_raw_version,  NULL);
+
+static struct device_attribute qcom_soc_attr_vendor =
+	__ATTR(vendor, 0444, qcom_get_vendor,  NULL);
+
+static struct device_attribute qcom_soc_attr_build_id =
+	__ATTR(build_id, 0444, qcom_get_build_id, NULL);
+
+static struct device_attribute qcom_soc_attr_hw_platform =
+	__ATTR(hw_platform, 0444, qcom_get_hw_platform, NULL);
+
+
+static struct device_attribute qcom_soc_attr_platform_version =
+	__ATTR(platform_version, 0444,
+			qcom_get_platform_version, NULL);
+
+static struct device_attribute qcom_soc_attr_accessory_chip =
+	__ATTR(accessory_chip, 0444,
+			qcom_get_accessory_chip, NULL);
+
+static struct device_attribute qcom_soc_attr_platform_subtype =
+	__ATTR(platform_subtype, 0444,
+			qcom_get_platform_subtype, NULL);
+
+static struct device_attribute qcom_soc_attr_platform_subtype_id =
+	__ATTR(platform_subtype_id, 0444,
+			qcom_get_platform_subtype_id, NULL);
+
+static struct device_attribute qcom_soc_attr_foundry_id =
+	__ATTR(foundry_id, 0444,
+			qcom_get_foundry_id, NULL);
+
+static struct device_attribute qcom_soc_attr_serial_number =
+	__ATTR(serial_number, 0444,
+			qcom_get_serial_number, NULL);
+
+static struct device_attribute qcom_soc_attr_chip_family =
+	__ATTR(chip_family, 0444,
+			qcom_get_chip_family, NULL);
+
+static struct device_attribute qcom_soc_attr_raw_device_family =
+	__ATTR(raw_device_family, 0444,
+			qcom_get_raw_device_family, NULL);
+
+static struct device_attribute qcom_soc_attr_raw_device_number =
+	__ATTR(raw_device_number, 0444,
+			qcom_get_raw_device_number, NULL);
+
+static struct device_attribute qcom_soc_attr_pmic_model =
+	__ATTR(pmic_model, 0444,
+			qcom_get_pmic_model, NULL);
+
+static struct device_attribute qcom_soc_attr_pmic_die_revision =
+	__ATTR(pmic_die_revision, 0444,
+			qcom_get_pmic_die_revision, NULL);
+
+SMEM_IMAGE_TABLE_RO_ATTR(boot, SMEM_IMAGE_TABLE_BOOT_INDEX);
+SMEM_IMAGE_TABLE_RO_ATTR(tz, SMEM_IMAGE_TABLE_TZ_INDEX);
+SMEM_IMAGE_TABLE_RO_ATTR(rpm, SMEM_IMAGE_TABLE_RPM_INDEX);
+SMEM_IMAGE_TABLE_ATTR(apps, SMEM_IMAGE_TABLE_APPS_INDEX);
+SMEM_IMAGE_TABLE_RO_ATTR(mpss, SMEM_IMAGE_TABLE_MPSS_INDEX);
+SMEM_IMAGE_TABLE_RO_ATTR(adsp, SMEM_IMAGE_TABLE_ADSP_INDEX);
+SMEM_IMAGE_TABLE_RO_ATTR(cnss, SMEM_IMAGE_TABLE_CNSS_INDEX);
+SMEM_IMAGE_TABLE_RO_ATTR(video, SMEM_IMAGE_TABLE_VIDEO_INDEX);
+
+static struct attribute_group
+	*smem_image_table[SMEM_IMAGE_VERSION_BLOCKS_COUNT] = {
+		[SMEM_IMAGE_TABLE_BOOT_INDEX] = &boot_image_attr_group,
+		[SMEM_IMAGE_TABLE_TZ_INDEX] = &tz_image_attr_group,
+		[SMEM_IMAGE_TABLE_RPM_INDEX] = &rpm_image_attr_group,
+		[SMEM_IMAGE_TABLE_APPS_INDEX] = &apps_image_attr_group,
+		[SMEM_IMAGE_TABLE_MPSS_INDEX] = &mpss_image_attr_group,
+		[SMEM_IMAGE_TABLE_ADSP_INDEX] = &adsp_image_attr_group,
+		[SMEM_IMAGE_TABLE_CNSS_INDEX] = &cnss_image_attr_group,
+		[SMEM_IMAGE_TABLE_VIDEO_INDEX] = &video_image_attr_group,
+};
+
+static int socinfo_populate_sysfs_files(struct device *qcom_soc_device)
+{
+	int err, img_idx;
+
+	/*
+	 * Expose SMEM_IMAGE_TABLE to sysfs only when we have IMAGE_TABLE
+	 * available in SMEM. As IMAGE_TABLE and SOCINFO are two separate
+	 * items within SMEM, we expose the remaining soc information(i.e
+	 * only the SOCINFO item available in SMEM) to sysfs even in the
+	 * absence of an IMAGE_TABLE.
+	 */
+	if (smem_img_tbl_avlbl) {
+		for (img_idx = 0; img_idx < SMEM_IMAGE_VERSION_BLOCKS_COUNT;
+				img_idx++) {
+			if (smem_image_table[img_idx]) {
+				err = sysfs_create_group(&qcom_soc_device->kobj,
+					smem_image_table[img_idx]);
+				if (err)
+					break;
+			}
+		}
+	}
+	err = device_create_file(qcom_soc_device, &qcom_soc_attr_vendor);
+	if (!err) {
+		switch (socinfo_format) {
+		case SOCINFO_FORMAT(12):
+			err = device_create_file(qcom_soc_device,
+					&qcom_soc_attr_chip_family);
+			if (!err)
+				err = device_create_file(qcom_soc_device,
+					&qcom_soc_attr_raw_device_family);
+			if (!err)
+				err = device_create_file(qcom_soc_device,
+					&qcom_soc_attr_raw_device_number);
+		case SOCINFO_FORMAT(11):
+		case SOCINFO_FORMAT(10):
+			if (!err)
+				err = device_create_file(qcom_soc_device,
+					&qcom_soc_attr_serial_number);
+		case SOCINFO_FORMAT(9):
+			if (!err)
+				err = device_create_file(qcom_soc_device,
+					&qcom_soc_attr_foundry_id);
+		case SOCINFO_FORMAT(8):
+		case SOCINFO_FORMAT(7):
+			if (!err)
+				err = device_create_file(qcom_soc_device,
+					&qcom_soc_attr_pmic_model);
+			if (!err)
+				err = device_create_file(qcom_soc_device,
+					&qcom_soc_attr_pmic_die_revision);
+		case SOCINFO_FORMAT(6):
+			if (hw_subtype_valid)
+				if (!err)
+					err = device_create_file(
+						qcom_soc_device,
+					&qcom_soc_attr_platform_subtype);
+			if (!err)
+				err = device_create_file(qcom_soc_device,
+				&qcom_soc_attr_platform_subtype_id);
+		case SOCINFO_FORMAT(5):
+			if (!err)
+				err = device_create_file(qcom_soc_device,
+					&qcom_soc_attr_accessory_chip);
+		case SOCINFO_FORMAT(4):
+			if (!err)
+				err = device_create_file(qcom_soc_device,
+					&qcom_soc_attr_platform_version);
+		case SOCINFO_FORMAT(3):
+			if (!err)
+				err = device_create_file(qcom_soc_device,
+					&qcom_soc_attr_hw_platform);
+		case SOCINFO_FORMAT(2):
+			if (!err)
+				err = device_create_file(qcom_soc_device,
+						&qcom_soc_attr_raw_version);
+		case SOCINFO_FORMAT(1):
+			if (!err)
+				err = device_create_file(qcom_soc_device,
+						&qcom_soc_attr_build_id);
+			if (err) {
+				dev_err(qcom_soc_device,
+					"Could not create sysfs entry\n");
+				return err;
+			}
+			return 0;
+		default:
+			dev_err(qcom_soc_device, "Unknown socinfo format: v0.%u\n",
+					SOCINFO_VERSION_MINOR(socinfo_format));
+			return -EINVAL;
+		}
+	} else {
+		dev_err(qcom_soc_device, "Could not create sysfs entry\n");
+		return err;
+	}
+}
+
+static int socinfo_init_sysfs(struct device *dev)
+{
+	struct device *qcom_soc_device;
+	struct soc_device *soc_dev;
+	struct soc_device_attribute *soc_dev_attr;
+	u32 soc_version;
+
+	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
+	if (!soc_dev_attr)
+		return -ENOMEM;
+
+	soc_version = socinfo_get_version();
+
+	soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%d", socinfo_get_id());
+	soc_dev_attr->family = "Snapdragon";
+	soc_dev_attr->machine = socinfo_get_id_string();
+	soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%u.%u",
+				SOC_VERSION_MAJOR(soc_version),
+				SOC_VERSION_MINOR(soc_version));
+
+	soc_dev = soc_device_register(soc_dev_attr);
+	if (IS_ERR(soc_dev)) {
+		kfree(soc_dev_attr);
+		return PTR_ERR(soc_dev);
+	}
+
+	qcom_soc_device = soc_device_to_device(soc_dev);
+	socinfo_populate_sysfs_files(qcom_soc_device);
+	return 0;
+}
+
+static u32 socinfo_get_id(void)
+{
+	return (socinfo) ? socinfo->v0_1.id : 0;
+}
+
+static int socinfo_select_format(struct device *dev)
+{
+	u32 f_maj = SOCINFO_VERSION_MAJOR(socinfo->v0_1.format);
+	u32 f_min = SOCINFO_VERSION_MINOR(socinfo->v0_1.format);
+
+	if (f_maj != 0) {
+		dev_err(dev, "Unsupported format v%u.%u.\n",
+			f_maj, f_min);
+		return -EINVAL;
+	}
+
+	if (socinfo->v0_1.format > MAX_SOCINFO_FORMAT) {
+		dev_warn(dev, "Unsupported format v%u.%u. Falling back to v%u.%u.\n",
+			f_maj, f_min, SOCINFO_VERSION_MAJOR(MAX_SOCINFO_FORMAT),
+			SOCINFO_VERSION_MINOR(MAX_SOCINFO_FORMAT));
+		socinfo_format = MAX_SOCINFO_FORMAT;
+	} else {
+		socinfo_format = socinfo->v0_1.format;
+	}
+	return 0;
+}
+
+int qcom_socinfo_init(struct platform_device *pdev)
+{
+	size_t size;
+
+	socinfo = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID,
+			&size);
+	if (IS_ERR(socinfo) || (socinfo_select_format(&pdev->dev) < 0)) {
+		dev_warn(&pdev->dev,
+			"Coudn't find soc information; Unable to setup socinfo.\n");
+		return -ENOMEM;
+	}
+
+	if (!socinfo_get_id()) {
+		dev_err(&pdev->dev, "socinfo: Unknown SoC ID!\n");
+		return -EINVAL;
+	}
+
+	WARN(socinfo_get_id() >= ARRAY_SIZE(cpu_of_id),
+		"New IDs added! ID => CPU mapping needs an update.\n");
+
+	smem_image_version = (struct smem_image_version *)
+			socinfo_get_image_version_base_address(&pdev->dev);
+	if (IS_ERR(smem_image_version)) {
+		dev_warn(&pdev->dev, "Unable to get address for image version table.\n");
+		smem_img_tbl_avlbl = false;
+	}
+
+	hw_subtype = socinfo_get_platform_subtype();
+	if (socinfo_get_platform_type() == HW_PLATFORM_QRD) {
+		if (hw_subtype >= PLATFORM_SUBTYPE_QRD_INVALID) {
+			dev_err(&pdev->dev, "Invalid hardware platform sub type for qrd found\n");
+			hw_subtype_valid = false;
+		}
+	} else {
+		if (hw_subtype >= PLATFORM_SUBTYPE_INVALID) {
+			dev_err(&pdev->dev, "Invalid hardware platform subtype\n");
+			hw_subtype_valid = false;
+		}
+	}
+
+	socinfo_init_sysfs(&pdev->dev);
+
+	/* Feed the soc specific unique data into entropy pool */
+	add_device_randomness(socinfo, size);
+
+	return 0;
+}
+EXPORT_SYMBOL(qcom_socinfo_init);
+