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 <kim...@codeaurora.org>
---
 .../devicetree/bindings/soc/qcom/qcom,socinfo.txt  |   18 +
 drivers/soc/qcom/socinfo.c                         | 1173 ++++++++++++++++++++
 include/linux/soc/qcom/socinfo.h                   |  198 ++++
 3 files changed, 1389 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,socinfo.txt
 create mode 100644 drivers/soc/qcom/socinfo.c
 create mode 100644 include/linux/soc/qcom/socinfo.h

diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,socinfo.txt 
b/Documentation/devicetree/bindings/soc/qcom/qcom,socinfo.txt
new file mode 100644
index 0000000..1f26299
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,socinfo.txt
@@ -0,0 +1,18 @@
+Qualcomm SoC Information (socinfo) Driver binding
+
+This binding describes the Qualcomm SoC Information Driver, which provides
+information such as chip id, chip family, serial number and other such
+details about Qualcomm SoCs.
+
+- compatible:
+       Usage: required
+       Value type: <stringlist>
+       Definition: must be "qcom,socinfo"
+
+= EXAMPLE
+
+The following example represents a socinfo node.
+
+       socinfo {
+               compatible = "qcom,socinfo";
+       };
diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c
new file mode 100644
index 0000000..dc26028
--- /dev/null
+++ b/drivers/soc/qcom/socinfo.c
@@ -0,0 +1,1173 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * SOC Info Routines
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#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/soc/qcom/socinfo.h>
+#include <linux/soc/qcom/smem.h>
+
+#include <asm/system_misc.h>
+
+
+#define BUILD_ID_LENGTH 32
+#define SMEM_IMAGE_VERSION_BLOCKS_COUNT 32
+#define SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE 128
+#define SMEM_IMAGE_VERSION_SIZE 4096
+#define SMEM_IMAGE_VERSION_NAME_SIZE 75
+#define SMEM_IMAGE_VERSION_VARIANT_SIZE 20
+#define SMEM_IMAGE_VERSION_VARIANT_OFFSET 75
+#define SMEM_IMAGE_VERSION_OEM_SIZE 32
+#define SMEM_IMAGE_VERSION_OEM_OFFSET 96
+#define SMEM_IMAGE_VERSION_PARTITION_APPS 10
+#define SMEM_ITEM_SIZE_ALIGN 8
+/*
+ * Shared memory identifiers, used to acquire handles to respective memory
+ * region.
+ */
+#define SMEM_IMAGE_VERSION_TABLE       469
+#define SMEM_HW_SW_BUILD_ID            137
+
+const char *hw_platform[] = {
+       [HW_PLATFORM_UNKNOWN] = "Unknown",
+       [HW_PLATFORM_SURF] = "Surf",
+       [HW_PLATFORM_FFA] = "FFA",
+       [HW_PLATFORM_FLUID] = "Fluid",
+       [HW_PLATFORM_SVLTE_FFA] = "SVLTE_FFA",
+       [HW_PLATFORM_SVLTE_SURF] = "SLVTE_SURF",
+       [HW_PLATFORM_MTP_MDM] = "MDM_MTP_NO_DISPLAY",
+       [HW_PLATFORM_MTP] = "MTP",
+       [HW_PLATFORM_RCM] = "RCM",
+       [HW_PLATFORM_LIQUID] = "Liquid",
+       [HW_PLATFORM_DRAGON] = "Dragon",
+       [HW_PLATFORM_QRD] = "QRD",
+       [HW_PLATFORM_HRD] = "HRD",
+       [HW_PLATFORM_DTV] = "DTV",
+       [HW_PLATFORM_STP] = "STP",
+       [HW_PLATFORM_SBC] = "SBC",
+};
+
+const char *qrd_hw_platform_subtype[] = {
+       [PLATFORM_SUBTYPE_QRD] = "QRD",
+       [PLATFORM_SUBTYPE_SKUAA] = "SKUAA",
+       [PLATFORM_SUBTYPE_SKUF] = "SKUF",
+       [PLATFORM_SUBTYPE_SKUAB] = "SKUAB",
+       [PLATFORM_SUBTYPE_SKUG] = "SKUG",
+       [PLATFORM_SUBTYPE_QRD_INVALID] = "INVALID",
+};
+
+const char *hw_platform_subtype[] = {
+       [PLATFORM_SUBTYPE_UNKNOWN] = "Unknown",
+       [PLATFORM_SUBTYPE_CHARM] = "charm",
+       [PLATFORM_SUBTYPE_STRANGE] = "strange",
+       [PLATFORM_SUBTYPE_STRANGE_2A] = "strange_2a",
+       [PLATFORM_SUBTYPE_INVALID] = "Invalid",
+};
+
+/* Used to parse shared memory. Must match the modem. */
+struct socinfo_v0_1 {
+       uint32_t format;
+       uint32_t id;
+       uint32_t version;
+       char build_id[BUILD_ID_LENGTH];
+};
+
+struct socinfo_v0_2 {
+       struct socinfo_v0_1 v0_1;
+       uint32_t raw_id;
+       uint32_t raw_version;
+};
+
+struct socinfo_v0_3 {
+       struct socinfo_v0_2 v0_2;
+       uint32_t hw_platform;
+};
+
+struct socinfo_v0_4 {
+       struct socinfo_v0_3 v0_3;
+       uint32_t platform_version;
+};
+
+struct socinfo_v0_5 {
+       struct socinfo_v0_4 v0_4;
+       uint32_t accessory_chip;
+};
+
+struct socinfo_v0_6 {
+       struct socinfo_v0_5 v0_5;
+       uint32_t hw_platform_subtype;
+};
+
+struct socinfo_v0_7 {
+       struct socinfo_v0_6 v0_6;
+       uint32_t pmic_model;
+       uint32_t pmic_die_revision;
+};
+
+struct socinfo_v0_8 {
+       struct socinfo_v0_7 v0_7;
+       uint32_t pmic_model_1;
+       uint32_t pmic_die_revision_1;
+       uint32_t pmic_model_2;
+       uint32_t pmic_die_revision_2;
+};
+
+struct socinfo_v0_9 {
+       struct socinfo_v0_8 v0_8;
+       uint32_t foundry_id;
+};
+
+struct socinfo_v0_10 {
+       struct socinfo_v0_9 v0_9;
+       uint32_t serial_number;
+};
+
+struct socinfo_v0_11 {
+       struct socinfo_v0_10 v0_10;
+       uint32_t num_pmics;
+       uint32_t pmic_array_offset;
+};
+
+struct socinfo_v0_12 {
+       struct socinfo_v0_11 v0_11;
+       uint32_t chip_family;
+       uint32_t raw_device_family;
+       uint32_t 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;
+
+/* max socinfo format version supported */
+#define MAX_SOCINFO_FORMAT SOCINFO_VERSION(0, 12)
+
+static struct qcom_soc_info cpu_of_id[] = {
+
+       [MSM_UNKNOWN_ID] = {MSM_CPU_UNKNOWN, "Unknown CPU"},
+
+       /* 8x60 IDs */
+       [MSM_8960_ID] = {MSM_CPU_8960, "MSM8960"},
+
+       /* 8x64 IDs */
+       [APQ_8064_ID] = {MSM_CPU_8064, "APQ8064"},
+       [MPQ_8064_ID] = {MSM_CPU_8064, "MPQ8064"},
+
+       /* 8x60A IDs */
+       [MSM_8660A_ID] = {MSM_CPU_8960, "MSM8660A"},
+       [MSM_8260A_ID] = {MSM_CPU_8960, "MSM8260A"},
+       [APQ_8060A_ID] = {MSM_CPU_8960, "APQ8060A"},
+
+       /* 8x74 IDs */
+       [MSM_8974_ID] = {MSM_CPU_8974, "MSM8974"},
+       [APQ_8074_ID] = {MSM_CPU_8974, "APQ8074"},
+       [MSM_8274_ID] = {MSM_CPU_8974, "MSM8274"},
+       [MSM_8674_ID] = {MSM_CPU_8974, "MSM8674"},
+
+       /* 8x74AA IDs */
+       [APQ_8074_AA_ID] = {MSM_CPU_8974PRO_AA, "APQ8074-AA"},
+       [MSM_8274_AA_ID] = {MSM_CPU_8974PRO_AA, "MSM8274-AA"},
+       [MSM_8674_AA_ID] = {MSM_CPU_8974PRO_AA, "MSM8674-AA"},
+       [MSM_8974_AA_ID] = {MSM_CPU_8974PRO_AA, "MSM8974-AA"},
+
+       /* 8x74AB IDs */
+       [APQ_8074_AB_ID] = {MSM_CPU_8974PRO_AB, "APQ8074-AB"},
+       [MSM_8274_AB_ID] = {MSM_CPU_8974PRO_AB, "MSM8274-AB"},
+       [MSM_8674_AB_ID] = {MSM_CPU_8974PRO_AB, "MSM8674-AB"},
+       [MSM_8974_AB_ID] = {MSM_CPU_8974PRO_AB, "MSM8974-AB"},
+
+       /* 8x74AC IDs */
+       [MSM_8974PRO_ID] = {MSM_CPU_8974PRO_AC, "MSM8974PRO"},
+       [APQ_8074PRO_ID] = {MSM_CPU_8974PRO_AC, "APQ8074PRO"},
+       [MSM_8274PRO_ID] = {MSM_CPU_8974PRO_AC, "MSM8274PRO"},
+       [MSM_8674PRO_ID] = {MSM_CPU_8974PRO_AC, "MSM8674PRO"},
+
+       /* 8x60AB IDs */
+       [MSM_8960AB_ID] = {MSM_CPU_8960AB, "MSM8960AB"},
+       [APQ_8060AB_ID] = {MSM_CPU_8960AB, "APQ8060AB"},
+       [MSM_8260AB_ID] = {MSM_CPU_8960AB, "MSM8260AB"},
+       [MSM_8660AB_ID] = {MSM_CPU_8960AB, "MSM8660AB"},
+
+       /* 8x84 IDs */
+       [APQ_8084_ID] = {MSM_CPU_8084, "APQ8084"},
+
+       /* 8x16 IDs */
+       [MSM_8916_ID] = {MSM_CPU_8916, "MSM8916"},
+       [APQ_8016_ID] = {MSM_CPU_8916, "APQ8016"},
+       [MSM_8216_ID] = {MSM_CPU_8916, "MSM8216"},
+       [MSM_8116_ID] = {MSM_CPU_8916, "MSM8116"},
+       [MSM_8616_ID] = {MSM_CPU_8916, "MSM8616"},
+
+       /* 8x96 IDs */
+       [MSM_8996_ID] = {MSM_CPU_8996, "MSM8996"},
+       [MSM_8996AU_ID] = {MSM_CPU_8996, "MSM8996AU"},
+       [APQ_8096AU_ID] = {MSM_CPU_8996, "APQ8096AU"},
+       [APQ8096_ID] = {MSM_CPU_8996, "APQ8096"},
+       [MSM_8996SG_ID] = {MSM_CPU_8996, "MSM8996SG"},
+       [APQ_8096SG_ID] = {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 enum qcom_cpu cur_cpu;
+static int current_image;
+static uint32_t socinfo_format;
+
+static struct socinfo_v0_1 dummy_socinfo = {
+       .format = SOCINFO_VERSION(0, 1),
+       .version = 1,
+};
+
+static char *socinfo_get_id_string(void)
+{
+       return (socinfo) ? cpu_of_id[socinfo->v0_1.id].soc_id_string : NULL;
+}
+
+static uint32_t socinfo_get_accessory_chip(void)
+{
+       return socinfo ?
+               (socinfo_format >= SOCINFO_VERSION(0, 5) ?
+                       socinfo->v0_5.accessory_chip : 0)
+               : 0;
+}
+
+static uint32_t socinfo_get_foundry_id(void)
+{
+       return socinfo ?
+               (socinfo_format >= SOCINFO_VERSION(0, 9) ?
+                       socinfo->v0_9.foundry_id : 0)
+               : 0;
+}
+
+static uint32_t socinfo_get_chip_family(void)
+{
+       return socinfo ?
+               (socinfo_format >= SOCINFO_VERSION(0, 12) ?
+                       socinfo->v0_12.chip_family : 0)
+               : 0;
+}
+
+static uint32_t socinfo_get_raw_device_family(void)
+{
+       return socinfo ?
+               (socinfo_format >= SOCINFO_VERSION(0, 12) ?
+                       socinfo->v0_12.raw_device_family : 0)
+               : 0;
+}
+
+static uint32_t socinfo_get_raw_device_number(void)
+{
+       return socinfo ?
+               (socinfo_format >= SOCINFO_VERSION(0, 12) ?
+                       socinfo->v0_12.raw_device_number : 0)
+               : 0;
+}
+
+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 (!ptr)
+               return 0;
+
+       size_in = ALIGN(SMEM_IMAGE_VERSION_SIZE, SMEM_ITEM_SIZE_ALIGN);
+       if (size_in != size) {
+               dev_err(dev, "Wrong size for smem item\n");
+               return 0;
+       }
+
+       return ptr;
+}
+
+uint32_t socinfo_get_id(void)
+{
+       return (socinfo) ? socinfo->v0_1.id : 0;
+}
+EXPORT_SYMBOL_GPL(socinfo_get_id);
+
+uint32_t socinfo_get_version(void)
+{
+       return (socinfo) ? socinfo->v0_1.version : 0;
+}
+EXPORT_SYMBOL_GPL(socinfo_get_version);
+
+char *socinfo_get_build_id(void)
+{
+       return (socinfo) ? socinfo->v0_1.build_id : NULL;
+}
+EXPORT_SYMBOL_GPL(socinfo_get_build_id);
+
+uint32_t socinfo_get_raw_id(void)
+{
+       return socinfo ?
+               (socinfo_format >= SOCINFO_VERSION(0, 2) ?
+                       socinfo->v0_2.raw_id : 0)
+               : 0;
+}
+EXPORT_SYMBOL_GPL(socinfo_get_raw_id);
+
+uint32_t socinfo_get_raw_version(void)
+{
+       return socinfo ?
+               (socinfo_format >= SOCINFO_VERSION(0, 2) ?
+                       socinfo->v0_2.raw_version : 0)
+               : 0;
+}
+EXPORT_SYMBOL_GPL(socinfo_get_raw_version);
+
+uint32_t socinfo_get_platform_type(void)
+{
+       return socinfo ?
+               (socinfo_format >= SOCINFO_VERSION(0, 3) ?
+                       socinfo->v0_3.hw_platform : 0)
+               : 0;
+}
+EXPORT_SYMBOL_GPL(socinfo_get_platform_type);
+
+uint32_t socinfo_get_platform_version(void)
+{
+       return socinfo ?
+               (socinfo_format >= SOCINFO_VERSION(0, 4) ?
+                       socinfo->v0_4.platform_version : 0)
+               : 0;
+}
+EXPORT_SYMBOL_GPL(socinfo_get_platform_version);
+
+uint32_t socinfo_get_platform_subtype(void)
+{
+       return socinfo ?
+               (socinfo_format >= SOCINFO_VERSION(0, 6) ?
+                       socinfo->v0_6.hw_platform_subtype : 0)
+               : 0;
+}
+EXPORT_SYMBOL_GPL(socinfo_get_platform_subtype);
+
+uint32_t socinfo_get_serial_number(void)
+{
+       return socinfo ?
+               (socinfo_format >= SOCINFO_VERSION(0, 10) ?
+                       socinfo->v0_10.serial_number : 0)
+               : 0;
+}
+EXPORT_SYMBOL_GPL(socinfo_get_serial_number);
+
+enum qcom_pmic_model socinfo_get_pmic_model(void)
+{
+       return socinfo ?
+               (socinfo_format >= SOCINFO_VERSION(0, 7) ?
+                       socinfo->v0_7.pmic_model : PMIC_MODEL_UNKNOWN)
+               : PMIC_MODEL_UNKNOWN;
+}
+
+uint32_t socinfo_get_pmic_die_revision(void)
+{
+       return socinfo ?
+               (socinfo_format >= SOCINFO_VERSION(0, 7) ?
+                       socinfo->v0_7.pmic_die_revision : 0)
+               : 0;
+}
+
+enum qcom_cpu socinfo_get_qcom_cpu(void)
+{
+       return cur_cpu;
+}
+EXPORT_SYMBOL_GPL(socinfo_get_qcom_cpu);
+
+/* socinfo: sysfs functions */
+
+static ssize_t
+qcom_get_vendor(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "Qualcomm\n");
+}
+
+static ssize_t
+qcom_get_raw_id(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%u\n",
+               socinfo_get_raw_id());
+}
+
+static ssize_t
+qcom_get_raw_version(struct device *dev,
+                    struct device_attribute *attr,
+                    char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%u\n",
+               socinfo_get_raw_version());
+}
+
+static ssize_t
+qcom_get_build_id(struct device *dev,
+                  struct device_attribute *attr,
+                  char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%-.32s\n",
+                       socinfo_get_build_id());
+}
+
+static ssize_t
+qcom_get_hw_platform(struct device *dev,
+                       struct device_attribute *attr,
+                       char *buf)
+{
+       uint32_t hw_type;
+
+       hw_type = socinfo_get_platform_type();
+
+       return snprintf(buf, PAGE_SIZE, "%-.32s\n",
+                       hw_platform[hw_type]);
+}
+
+static ssize_t
+qcom_get_platform_version(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%u\n",
+               socinfo_get_platform_version());
+}
+
+static ssize_t
+qcom_get_accessory_chip(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%u\n",
+               socinfo_get_accessory_chip());
+}
+
+static ssize_t
+qcom_get_platform_subtype(struct device *dev,
+                       struct device_attribute *attr,
+                       char *buf)
+{
+       uint32_t hw_subtype;
+
+       hw_subtype = socinfo_get_platform_subtype();
+       if (socinfo_get_platform_type() == HW_PLATFORM_QRD) {
+               if (hw_subtype >= PLATFORM_SUBTYPE_QRD_INVALID) {
+                       pr_err("Invalid hardware platform sub type for qrd 
found\n");
+                       hw_subtype = PLATFORM_SUBTYPE_QRD_INVALID;
+               }
+               return snprintf(buf, PAGE_SIZE, "%-.32s\n",
+                                       qrd_hw_platform_subtype[hw_subtype]);
+       } else {
+               if (hw_subtype >= PLATFORM_SUBTYPE_INVALID) {
+                       pr_err("Invalid hardware platform subtype\n");
+                       hw_subtype = PLATFORM_SUBTYPE_INVALID;
+               }
+               return snprintf(buf, PAGE_SIZE, "%-.32s\n",
+                       hw_platform_subtype[hw_subtype]);
+       }
+}
+
+static ssize_t
+qcom_get_platform_subtype_id(struct device *dev,
+                       struct device_attribute *attr,
+                       char *buf)
+{
+       uint32_t hw_subtype;
+
+       hw_subtype = socinfo_get_platform_subtype();
+       return snprintf(buf, PAGE_SIZE, "%u\n",
+               hw_subtype);
+}
+
+static ssize_t
+qcom_get_foundry_id(struct device *dev,
+                       struct device_attribute *attr,
+                       char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%u\n",
+               socinfo_get_foundry_id());
+}
+
+static ssize_t
+qcom_get_serial_number(struct device *dev,
+                       struct device_attribute *attr,
+                       char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%u\n",
+               socinfo_get_serial_number());
+}
+
+static ssize_t
+qcom_get_chip_family(struct device *dev,
+                       struct device_attribute *attr,
+                       char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "0x%x\n",
+               socinfo_get_chip_family());
+}
+
+static ssize_t
+qcom_get_raw_device_family(struct device *dev,
+                       struct device_attribute *attr,
+                       char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "0x%x\n",
+               socinfo_get_raw_device_family());
+}
+
+static ssize_t
+qcom_get_raw_device_number(struct device *dev,
+                       struct device_attribute *attr,
+                       char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "0x%x\n",
+               socinfo_get_raw_device_number());
+}
+
+static ssize_t
+qcom_get_pmic_model(struct device *dev,
+                       struct device_attribute *attr,
+                       char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%u\n",
+               socinfo_get_pmic_model());
+}
+
+static ssize_t
+qcom_get_pmic_die_revision(struct device *dev,
+                              struct device_attribute *attr,
+                              char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%u\n",
+                        socinfo_get_pmic_die_revision());
+}
+
+static ssize_t
+qcom_get_image_version(struct device *dev,
+                       struct device_attribute *attr,
+                       char *buf)
+{
+       char *string_address;
+
+       string_address = socinfo_get_image_version_base_address(dev);
+       if (IS_ERR_OR_NULL(string_address)) {
+               pr_err("Failed to get image version base address");
+               return snprintf(buf, SMEM_IMAGE_VERSION_NAME_SIZE, "Unknown");
+       }
+       string_address += current_image * SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE;
+       return snprintf(buf, SMEM_IMAGE_VERSION_NAME_SIZE, "%-.75s\n",
+                       string_address);
+}
+
+static ssize_t
+qcom_set_image_version(struct device *dev,
+                       struct device_attribute *attr,
+                       const char *buf,
+                       size_t count)
+{
+       char *store_address;
+
+       if (current_image != SMEM_IMAGE_VERSION_PARTITION_APPS)
+               return count;
+       store_address = socinfo_get_image_version_base_address(dev);
+       if (IS_ERR_OR_NULL(store_address)) {
+               pr_err("Failed to get image version base address");
+               return count;
+       }
+       store_address += current_image * SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE;
+       snprintf(store_address, SMEM_IMAGE_VERSION_NAME_SIZE, "%-.75s", buf);
+       return count;
+}
+
+static ssize_t
+qcom_get_image_variant(struct device *dev,
+                       struct device_attribute *attr,
+                       char *buf)
+{
+       char *string_address;
+
+       string_address = socinfo_get_image_version_base_address(dev);
+       if (IS_ERR_OR_NULL(string_address)) {
+               pr_err("Failed to get image version base address");
+               return snprintf(buf, SMEM_IMAGE_VERSION_VARIANT_SIZE,
+               "Unknown");
+       }
+       string_address += current_image * SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE;
+       string_address += SMEM_IMAGE_VERSION_VARIANT_OFFSET;
+       return snprintf(buf, SMEM_IMAGE_VERSION_VARIANT_SIZE, "%-.20s\n",
+                       string_address);
+}
+
+static ssize_t
+qcom_set_image_variant(struct device *dev,
+                       struct device_attribute *attr,
+                       const char *buf,
+                       size_t count)
+{
+       char *store_address;
+
+       if (current_image != SMEM_IMAGE_VERSION_PARTITION_APPS)
+               return count;
+       store_address = socinfo_get_image_version_base_address(dev);
+       if (IS_ERR_OR_NULL(store_address)) {
+               pr_err("Failed to get image version base address");
+               return count;
+       }
+       store_address += current_image * SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE;
+       store_address += SMEM_IMAGE_VERSION_VARIANT_OFFSET;
+       snprintf(store_address, SMEM_IMAGE_VERSION_VARIANT_SIZE, "%-.20s", buf);
+       return count;
+}
+
+static ssize_t
+qcom_get_image_crm_version(struct device *dev,
+                       struct device_attribute *attr,
+                       char *buf)
+{
+       char *string_address;
+
+       string_address = socinfo_get_image_version_base_address(dev);
+       if (IS_ERR_OR_NULL(string_address)) {
+               pr_err("Failed to get image version base address");
+               return snprintf(buf, SMEM_IMAGE_VERSION_OEM_SIZE, "Unknown");
+       }
+       string_address += current_image * SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE;
+       string_address += SMEM_IMAGE_VERSION_OEM_OFFSET;
+       return snprintf(buf, SMEM_IMAGE_VERSION_OEM_SIZE, "%-.32s\n",
+                       string_address);
+}
+
+static ssize_t
+qcom_set_image_crm_version(struct device *dev,
+                       struct device_attribute *attr,
+                       const char *buf,
+                       size_t count)
+{
+       char *store_address;
+
+       if (current_image != SMEM_IMAGE_VERSION_PARTITION_APPS)
+               return count;
+       store_address = socinfo_get_image_version_base_address(dev);
+       if (IS_ERR_OR_NULL(store_address)) {
+               pr_err("Failed to get image version base address");
+               return count;
+       }
+       store_address += current_image * SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE;
+       store_address += SMEM_IMAGE_VERSION_OEM_OFFSET;
+       snprintf(store_address, SMEM_IMAGE_VERSION_OEM_SIZE, "%-.32s", buf);
+       return count;
+}
+
+static ssize_t
+qcom_get_image_number(struct device *dev,
+                       struct device_attribute *attr,
+                       char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+                       current_image);
+}
+
+static ssize_t
+qcom_select_image(struct device *dev, struct device_attribute *attr,
+                       const char *buf, size_t count)
+{
+       int ret, digit;
+
+       ret = kstrtoint(buf, 10, &digit);
+       if (ret)
+               return ret;
+       if (digit >= 0 && digit < SMEM_IMAGE_VERSION_BLOCKS_COUNT)
+               current_image = digit;
+       else
+               current_image = 0;
+       return count;
+}
+
+static ssize_t
+qcom_get_images(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       int pos = 0;
+       int image;
+       char *image_address;
+
+       image_address = socinfo_get_image_version_base_address(dev);
+       if (IS_ERR_OR_NULL(image_address))
+               return snprintf(buf, PAGE_SIZE, "Unavailable\n");
+
+       *buf = '\0';
+       for (image = 0; image < SMEM_IMAGE_VERSION_BLOCKS_COUNT; image++) {
+               if (*image_address == '\0') {
+                       image_address += SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE;
+                       continue;
+               }
+
+               pos += snprintf(buf + pos, PAGE_SIZE - pos, "%d:\n",
+                               image);
+               pos += snprintf(buf + pos, PAGE_SIZE - pos,
+                               "\tCRM:\t\t%-.75s\n", image_address);
+               pos += snprintf(buf + pos, PAGE_SIZE - pos,
+                               "\tVariant:\t%-.20s\n", image_address +
+                               SMEM_IMAGE_VERSION_VARIANT_OFFSET);
+               pos += snprintf(buf + pos, PAGE_SIZE - pos,
+                               "\tVersion:\t%-.32s\n\n",
+                               image_address + SMEM_IMAGE_VERSION_OEM_OFFSET);
+
+               image_address += SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE;
+       }
+
+       return pos;
+}
+
+static struct device_attribute qcom_soc_attr_raw_version =
+       __ATTR(raw_version, S_IRUGO, qcom_get_raw_version,  NULL);
+
+static struct device_attribute qcom_soc_attr_raw_id =
+       __ATTR(raw_id, S_IRUGO, qcom_get_raw_id,  NULL);
+
+static struct device_attribute qcom_soc_attr_vendor =
+       __ATTR(vendor, S_IRUGO, qcom_get_vendor,  NULL);
+
+static struct device_attribute qcom_soc_attr_build_id =
+       __ATTR(build_id, S_IRUGO, qcom_get_build_id, NULL);
+
+static struct device_attribute qcom_soc_attr_hw_platform =
+       __ATTR(hw_platform, S_IRUGO, qcom_get_hw_platform, NULL);
+
+
+static struct device_attribute qcom_soc_attr_platform_version =
+       __ATTR(platform_version, S_IRUGO,
+                       qcom_get_platform_version, NULL);
+
+static struct device_attribute qcom_soc_attr_accessory_chip =
+       __ATTR(accessory_chip, S_IRUGO,
+                       qcom_get_accessory_chip, NULL);
+
+static struct device_attribute qcom_soc_attr_platform_subtype =
+       __ATTR(platform_subtype, S_IRUGO,
+                       qcom_get_platform_subtype, NULL);
+
+/* Platform Subtype String is being deprecated. Use Platform
+ * Subtype ID instead.
+ */
+static struct device_attribute qcom_soc_attr_platform_subtype_id =
+       __ATTR(platform_subtype_id, S_IRUGO,
+                       qcom_get_platform_subtype_id, NULL);
+
+static struct device_attribute qcom_soc_attr_foundry_id =
+       __ATTR(foundry_id, S_IRUGO,
+                       qcom_get_foundry_id, NULL);
+
+static struct device_attribute qcom_soc_attr_serial_number =
+       __ATTR(serial_number, S_IRUGO,
+                       qcom_get_serial_number, NULL);
+
+static struct device_attribute qcom_soc_attr_chip_family =
+       __ATTR(chip_family, S_IRUGO,
+                       qcom_get_chip_family, NULL);
+
+static struct device_attribute qcom_soc_attr_raw_device_family =
+       __ATTR(raw_device_family, S_IRUGO,
+                       qcom_get_raw_device_family, NULL);
+
+static struct device_attribute qcom_soc_attr_raw_device_number =
+       __ATTR(raw_device_number, S_IRUGO,
+                       qcom_get_raw_device_number, NULL);
+
+static struct device_attribute qcom_soc_attr_pmic_model =
+       __ATTR(pmic_model, S_IRUGO,
+                       qcom_get_pmic_model, NULL);
+
+static struct device_attribute qcom_soc_attr_pmic_die_revision =
+       __ATTR(pmic_die_revision, S_IRUGO,
+                       qcom_get_pmic_die_revision, NULL);
+
+static struct device_attribute image_version =
+       __ATTR(image_version, S_IRUGO | S_IWUSR,
+                       qcom_get_image_version, qcom_set_image_version);
+
+static struct device_attribute image_variant =
+       __ATTR(image_variant, S_IRUGO | S_IWUSR,
+                       qcom_get_image_variant, qcom_set_image_variant);
+
+static struct device_attribute image_crm_version =
+       __ATTR(image_crm_version, S_IRUGO | S_IWUSR,
+                       qcom_get_image_crm_version, qcom_set_image_crm_version);
+
+static struct device_attribute select_image =
+       __ATTR(select_image, S_IRUGO | S_IWUSR,
+                       qcom_get_image_number, qcom_select_image);
+
+static struct device_attribute images =
+       __ATTR(images, S_IRUGO, qcom_get_images, NULL);
+
+static void * __init setup_dummy_socinfo(void)
+{
+       if (early_machine_is_apq8064()) {
+               dummy_socinfo.id = APQ_8064_ID;
+               strlcpy(dummy_socinfo.build_id, "apq8064",
+                       sizeof(dummy_socinfo.build_id));
+       } else if (early_machine_is_apq8084()) {
+               dummy_socinfo.id = APQ_8084_ID;
+               strlcpy(dummy_socinfo.build_id, "apq8084",
+                       sizeof(dummy_socinfo.build_id));
+       } else if (early_machine_is_msm8916()) {
+               dummy_socinfo.id = MSM_8916_ID;
+               strlcpy(dummy_socinfo.build_id, "msm8916",
+                       sizeof(dummy_socinfo.build_id));
+       } else if (early_machine_is_msm8660()) {
+               dummy_socinfo.id = MSM_8660A_ID;
+               strlcpy(dummy_socinfo.build_id, "msm8660",
+                       sizeof(dummy_socinfo.build_id));
+       } else if (early_machine_is_msm8960()) {
+               dummy_socinfo.id = MSM_8960_ID;
+               strlcpy(dummy_socinfo.build_id, "msm8960",
+                       sizeof(dummy_socinfo.build_id));
+       } else if (early_machine_is_msm8974()) {
+               dummy_socinfo.id = MSM_8974_ID;
+               strlcpy(dummy_socinfo.build_id, "msm8974",
+                       sizeof(dummy_socinfo.build_id));
+       } else if (early_machine_is_msm8996()) {
+               dummy_socinfo.id = MSM_8996_ID;
+               strlcpy(dummy_socinfo.build_id, "msm8996",
+                       sizeof(dummy_socinfo.build_id));
+       }
+
+       strlcat(dummy_socinfo.build_id, "Dummy socinfo",
+               sizeof(dummy_socinfo.build_id));
+       return (void *) &dummy_socinfo;
+}
+
+static void socinfo_populate_sysfs_files(struct device *qcom_soc_device)
+{
+       device_create_file(qcom_soc_device, &qcom_soc_attr_vendor);
+       device_create_file(qcom_soc_device, &image_version);
+       device_create_file(qcom_soc_device, &image_variant);
+       device_create_file(qcom_soc_device, &image_crm_version);
+       device_create_file(qcom_soc_device, &select_image);
+       device_create_file(qcom_soc_device, &images);
+
+       switch (socinfo_format) {
+       case SOCINFO_VERSION(0, 12):
+               device_create_file(qcom_soc_device,
+                                       &qcom_soc_attr_chip_family);
+               device_create_file(qcom_soc_device,
+                                       &qcom_soc_attr_raw_device_family);
+               device_create_file(qcom_soc_device,
+                                       &qcom_soc_attr_raw_device_number);
+       case SOCINFO_VERSION(0, 11):
+       case SOCINFO_VERSION(0, 10):
+                device_create_file(qcom_soc_device,
+                                       &qcom_soc_attr_serial_number);
+       case SOCINFO_VERSION(0, 9):
+                device_create_file(qcom_soc_device,
+                                       &qcom_soc_attr_foundry_id);
+       case SOCINFO_VERSION(0, 8):
+       case SOCINFO_VERSION(0, 7):
+               device_create_file(qcom_soc_device,
+                                       &qcom_soc_attr_pmic_model);
+               device_create_file(qcom_soc_device,
+                                       &qcom_soc_attr_pmic_die_revision);
+       case SOCINFO_VERSION(0, 6):
+               device_create_file(qcom_soc_device,
+                                       &qcom_soc_attr_platform_subtype);
+               device_create_file(qcom_soc_device,
+                                       &qcom_soc_attr_platform_subtype_id);
+       case SOCINFO_VERSION(0, 5):
+               device_create_file(qcom_soc_device,
+                                       &qcom_soc_attr_accessory_chip);
+       case SOCINFO_VERSION(0, 4):
+               device_create_file(qcom_soc_device,
+                                       &qcom_soc_attr_platform_version);
+       case SOCINFO_VERSION(0, 3):
+               device_create_file(qcom_soc_device,
+                                       &qcom_soc_attr_hw_platform);
+       case SOCINFO_VERSION(0, 2):
+               device_create_file(qcom_soc_device,
+                                       &qcom_soc_attr_raw_id);
+               device_create_file(qcom_soc_device,
+                                       &qcom_soc_attr_raw_version);
+       case SOCINFO_VERSION(0, 1):
+               device_create_file(qcom_soc_device,
+                                       &qcom_soc_attr_build_id);
+               break;
+       default:
+               pr_err("Unknown socinfo format: v%u.%u\n",
+                               SOCINFO_VERSION_MAJOR(socinfo_format),
+                               SOCINFO_VERSION_MINOR(socinfo_format));
+               break;
+       }
+}
+
+static void socinfo_populate(struct soc_device_attribute *soc_dev_attr)
+{
+       uint32_t 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",
+                       SOCINFO_VERSION_MAJOR(soc_version),
+                       SOCINFO_VERSION_MINOR(soc_version));
+       return;
+
+}
+
+static int socinfo_init_sysfs(void)
+{
+       struct device *qcom_soc_device;
+       struct soc_device *soc_dev;
+       struct soc_device_attribute *soc_dev_attr;
+
+       soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
+       if (!soc_dev_attr) {
+               pr_err("Soc Device alloc failed!\n");
+               return -ENOMEM;
+       }
+
+       socinfo_populate(soc_dev_attr);
+       soc_dev = soc_device_register(soc_dev_attr);
+       if (IS_ERR_OR_NULL(soc_dev)) {
+               kfree(soc_dev_attr);
+               pr_err("Soc device register failed\n");
+               return -EIO;
+       }
+
+       qcom_soc_device = soc_device_to_device(soc_dev);
+       socinfo_populate_sysfs_files(qcom_soc_device);
+       return 0;
+}
+
+static void socinfo_print(void)
+{
+       uint32_t f_maj = SOCINFO_VERSION_MAJOR(socinfo_format);
+       uint32_t f_min = SOCINFO_VERSION_MINOR(socinfo_format);
+       uint32_t v_maj = SOCINFO_VERSION_MAJOR(socinfo->v0_1.version);
+       uint32_t v_min = SOCINFO_VERSION_MINOR(socinfo->v0_1.version);
+
+       switch (socinfo_format) {
+       case SOCINFO_VERSION(0, 1):
+               pr_info("v%u.%u, id=%u, ver=%u.%u\n",
+                       f_maj, f_min, socinfo->v0_1.id, v_maj, v_min);
+               break;
+       case SOCINFO_VERSION(0, 2):
+               pr_info("v%u.%u, id=%u, ver=%u.%u, raw_id=%u, raw_ver=%u\n",
+                       f_maj, f_min, socinfo->v0_1.id, v_maj, v_min,
+                       socinfo->v0_2.raw_id, socinfo->v0_2.raw_version);
+               break;
+       case SOCINFO_VERSION(0, 3):
+               pr_info("v%u.%u, id=%u, ver=%u.%u, raw_id=%u, raw_ver=%u, 
hw_plat=%u\n",
+                       f_maj, f_min, socinfo->v0_1.id,
+                       v_maj, v_min, socinfo->v0_2.raw_id,
+                       socinfo->v0_2.raw_version, socinfo->v0_3.hw_platform);
+               break;
+       case SOCINFO_VERSION(0, 4):
+               pr_info("v%u.%u, id=%u, ver=%u.%u, raw_id=%u, raw_ver=%u, 
hw_plat=%u, hw_plat_ver=%u\n",
+                       f_maj, f_min, socinfo->v0_1.id, v_maj, v_min,
+                       socinfo->v0_2.raw_id, socinfo->v0_2.raw_version,
+                       socinfo->v0_3.hw_platform,
+                       socinfo->v0_4.platform_version);
+               break;
+       case SOCINFO_VERSION(0, 5):
+               pr_info("v%u.%u, id=%u, ver=%u.%u, raw_id=%u, raw_ver=%u, 
hw_plat=%u, hw_plat_ver=%u accessory_chip=%u\n",
+                       f_maj, f_min, socinfo->v0_1.id, v_maj, v_min,
+                       socinfo->v0_2.raw_id, socinfo->v0_2.raw_version,
+                       socinfo->v0_3.hw_platform,
+                       socinfo->v0_4.platform_version,
+                       socinfo->v0_5.accessory_chip);
+               break;
+       case SOCINFO_VERSION(0, 6):
+               pr_info("v%u.%u, id=%u, ver=%u.%u, raw_id=%u, raw_ver=%u, 
hw_plat=%u, hw_plat_ver=%u accessory_chip=%u, hw_plat_subtype=%u\n",
+                       f_maj, f_min, socinfo->v0_1.id,
+                       v_maj, v_min, socinfo->v0_2.raw_id,
+                       socinfo->v0_2.raw_version,
+                       socinfo->v0_3.hw_platform,
+                       socinfo->v0_4.platform_version,
+                       socinfo->v0_5.accessory_chip,
+                       socinfo->v0_6.hw_platform_subtype);
+               break;
+       case SOCINFO_VERSION(0, 7):
+       case SOCINFO_VERSION(0, 8):
+               pr_info("v%u.%u, id=%u, ver=%u.%u, raw_id=%u, raw_ver=%u, 
hw_plat=%u, hw_plat_ver=%u accessory_chip=%u, hw_plat_subtype=%u, 
pmic_model=%u, pmic_die_revision=%u\n",
+                       f_maj, f_min, socinfo->v0_1.id, v_maj, v_min,
+                       socinfo->v0_2.raw_id, socinfo->v0_2.raw_version,
+                       socinfo->v0_3.hw_platform,
+                       socinfo->v0_4.platform_version,
+                       socinfo->v0_5.accessory_chip,
+                       socinfo->v0_6.hw_platform_subtype,
+                       socinfo->v0_7.pmic_model,
+                       socinfo->v0_7.pmic_die_revision);
+               break;
+       case SOCINFO_VERSION(0, 9):
+               pr_info("v%u.%u, id=%u, ver=%u.%u, raw_id=%u, raw_ver=%u, 
hw_plat=%u, hw_plat_ver=%u accessory_chip=%u, hw_plat_subtype=%u, 
pmic_model=%u, pmic_die_revision=%u foundry_id=%u\n",
+                       f_maj, f_min, socinfo->v0_1.id, v_maj, v_min,
+                       socinfo->v0_2.raw_id, socinfo->v0_2.raw_version,
+                       socinfo->v0_3.hw_platform,
+                       socinfo->v0_4.platform_version,
+                       socinfo->v0_5.accessory_chip,
+                       socinfo->v0_6.hw_platform_subtype,
+                       socinfo->v0_7.pmic_model,
+                       socinfo->v0_7.pmic_die_revision,
+                       socinfo->v0_9.foundry_id);
+               break;
+       case SOCINFO_VERSION(0, 10):
+               pr_info("v%u.%u, id=%u, ver=%u.%u, raw_id=%u, raw_ver=%u, 
hw_plat=%u, hw_plat_ver=%u accessory_chip=%u, hw_plat_subtype=%u, 
pmic_model=%u, pmic_die_revision=%u, foundry_id=%u, serial_number=%u\n",
+                       f_maj, f_min, socinfo->v0_1.id, v_maj, v_min,
+                       socinfo->v0_2.raw_id, socinfo->v0_2.raw_version,
+                       socinfo->v0_3.hw_platform,
+                       socinfo->v0_4.platform_version,
+                       socinfo->v0_5.accessory_chip,
+                       socinfo->v0_6.hw_platform_subtype,
+                       socinfo->v0_7.pmic_model,
+                       socinfo->v0_7.pmic_die_revision,
+                       socinfo->v0_9.foundry_id,
+                       socinfo->v0_10.serial_number);
+               break;
+       case SOCINFO_VERSION(0, 11):
+               pr_info("v%u.%u, id=%u, ver=%u.%u, raw_id=%u, raw_ver=%u, 
hw_plat=%u, hw_plat_ver=%u, accessory_chip=%u, hw_plat_subtype=%u, 
pmic_model=%u, pmic_die_revision=%u, foundry_id=%u, serial_number=%u 
num_pmics=%u\n",
+                       f_maj, f_min, socinfo->v0_1.id, v_maj, v_min,
+                       socinfo->v0_2.raw_id, socinfo->v0_2.raw_version,
+                       socinfo->v0_3.hw_platform,
+                       socinfo->v0_4.platform_version,
+                       socinfo->v0_5.accessory_chip,
+                       socinfo->v0_6.hw_platform_subtype,
+                       socinfo->v0_7.pmic_model,
+                       socinfo->v0_7.pmic_die_revision,
+                       socinfo->v0_9.foundry_id,
+                       socinfo->v0_10.serial_number,
+                       socinfo->v0_11.num_pmics);
+               break;
+       case SOCINFO_VERSION(0, 12):
+               pr_info("v%u.%u, id=%u, ver=%u.%u, raw_id=%u, raw_ver=%u, 
hw_plat=%u, hw_plat_ver=%u accessory_chip=%u, hw_plat_subtype=%u, 
pmic_model=%u, pmic_die_revision=%u, foundry_id=%u, serial_number=%u, 
num_pmics=%u, chip_family=0x%x, raw_device_family=0x%x, 
raw_device_number=0x%x\n",
+                       f_maj, f_min, socinfo->v0_1.id, v_maj, v_min,
+                       socinfo->v0_2.raw_id, socinfo->v0_2.raw_version,
+                       socinfo->v0_3.hw_platform,
+                       socinfo->v0_4.platform_version,
+                       socinfo->v0_5.accessory_chip,
+                       socinfo->v0_6.hw_platform_subtype,
+                       socinfo->v0_7.pmic_model,
+                       socinfo->v0_7.pmic_die_revision,
+                       socinfo->v0_9.foundry_id,
+                       socinfo->v0_10.serial_number,
+                       socinfo->v0_11.num_pmics,
+                       socinfo->v0_12.chip_family,
+                       socinfo->v0_12.raw_device_family,
+                       socinfo->v0_12.raw_device_number);
+               break;
+
+       default:
+               pr_err("Unknown format found: v%u.%u\n", f_maj, f_min);
+               break;
+       }
+}
+
+static void socinfo_select_format(void)
+{
+       uint32_t f_maj = SOCINFO_VERSION_MAJOR(socinfo->v0_1.format);
+       uint32_t f_min = SOCINFO_VERSION_MINOR(socinfo->v0_1.format);
+
+       if (f_maj != 0) {
+               pr_err("Unsupported format v%u.%u. Falling back to dummy 
values.\n",
+                       f_maj, f_min);
+               socinfo = setup_dummy_socinfo();
+       }
+
+       if (socinfo->v0_1.format > MAX_SOCINFO_FORMAT) {
+               pr_warn("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;
+       }
+}
+
+static int qcom_socinfo_probe(struct platform_device *pdev)
+{
+       size_t size;
+
+       socinfo = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID,
+                       &size);
+       if (IS_ERR_OR_NULL(socinfo)) {
+               if (PTR_ERR(socinfo) == -EPROBE_DEFER)
+                       return PTR_ERR(socinfo);
+               else {
+                       dev_warn(&pdev->dev,
+                               "Can't find SMEM_HW_SW_BUILD_ID; falling back 
on dummy values.\n");
+                       socinfo = setup_dummy_socinfo();
+               }
+       }
+
+       socinfo_select_format();
+
+       WARN(!socinfo_get_id(), "Unknown SOC ID!\n");
+
+       if (socinfo_get_id() >= ARRAY_SIZE(cpu_of_id))
+               BUG_ON("New IDs added! ID => CPU mapping needs an update.\n");
+       else
+               cur_cpu = cpu_of_id[socinfo->v0_1.id].generic_soc_type;
+
+       socinfo_print();
+
+       socinfo_init_sysfs();
+
+       return 0;
+}
+
+static const struct of_device_id qcom_socinfo_of_match[] = {
+       { .compatible = "qcom,socinfo" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, qcom_socinfo_of_match);
+
+static struct platform_driver qcom_socinfo_driver = {
+       .probe = qcom_socinfo_probe,
+       .driver  = {
+               .name  = "qcom-socinfo",
+               .of_match_table = qcom_socinfo_of_match,
+       },
+};
+module_platform_driver(qcom_socinfo_driver);
+
diff --git a/include/linux/soc/qcom/socinfo.h b/include/linux/soc/qcom/socinfo.h
new file mode 100644
index 0000000..17ca50a
--- /dev/null
+++ b/include/linux/soc/qcom/socinfo.h
@@ -0,0 +1,198 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_SOCINFO_H_
+#define _ARCH_ARM_MACH_MSM_SOCINFO_H_
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/of_fdt.h>
+#include <linux/of.h>
+
+#include <asm/cputype.h>
+/*
+ * 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 SOCINFO_VERSION_MAJOR(ver) (((ver) & 0xffff0000) >> 16)
+#define SOCINFO_VERSION_MINOR(ver) ((ver) & 0x0000ffff)
+#define SOCINFO_VERSION(maj, min)  ((((maj) & 0xffff) << 16)|((min) & 0xffff))
+
+#ifdef CONFIG_OF
+#define early_machine_is_apq8064()     \
+       of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,apq8064")
+#define early_machine_is_apq8084()     \
+       of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,apq8084")
+#define early_machine_is_msm8916()     \
+       of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8916")
+#define early_machine_is_msm8660()     \
+       of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8660")
+#define early_machine_is_msm8960()     \
+       of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8960")
+#define early_machine_is_msm8974()     \
+       of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8974")
+#define early_machine_is_msm8996()     \
+       of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8996")
+#else
+#define early_machine_is_apq8064()     0
+#define early_machine_is_apq8084()     0
+#define early_machine_is_msm8916()     0
+#define early_machine_is_msm8660()     0
+#define early_machine_is_msm8960()     0
+#define early_machine_is_msm8974()     0
+#define early_machine_is_msm8996()     0
+#endif
+
+#define PLATFORM_SUBTYPE_MDM   1
+#define PLATFORM_SUBTYPE_INTERPOSERV3 2
+#define PLATFORM_SUBTYPE_SGLTE 6
+
+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
+};
+
+enum qcom_cpu_id {
+       MSM_UNKNOWN_ID,
+       MSM_8960_ID = 87,
+       APQ_8064_ID = 109,
+       MSM_8660A_ID = 122,
+       MSM_8260A_ID,
+       APQ_8060A_ID,
+       MSM_8974_ID = 126,
+       MPQ_8064_ID = 130,
+       MSM_8960AB_ID = 138,
+       APQ_8060AB_ID,
+       MSM_8260AB_ID,
+       MSM_8660AB_ID,
+       APQ_8084_ID = 178,
+       APQ_8074_ID = 184,
+       MSM_8274_ID,
+       MSM_8674_ID,
+       MSM_8974PRO_ID = 194,
+       MSM_8916_ID = 206,
+       APQ_8074_AA_ID = 208,
+       APQ_8074_AB_ID,
+       APQ_8074PRO_ID,
+       MSM_8274_AA_ID,
+       MSM_8274_AB_ID,
+       MSM_8274PRO_ID,
+       MSM_8674_AA_ID,
+       MSM_8674_AB_ID,
+       MSM_8674PRO_ID,
+       MSM_8974_AA_ID,
+       MSM_8974_AB_ID,
+       MSM_8996_ID = 246,
+       APQ_8016_ID,
+       MSM_8216_ID,
+       MSM_8116_ID,
+       MSM_8616_ID,
+       APQ8096_ID = 291,
+       MSM_8996SG_ID = 305,
+       MSM_8996AU_ID = 310,
+       APQ_8096AU_ID,
+       APQ_8096SG_ID
+};
+
+struct qcom_soc_info {
+       enum qcom_cpu generic_soc_type;
+       char *soc_id_string;
+};
+
+enum qcom_pmic_model {
+       PMIC_MODEL_PM8058 = 13,
+       PMIC_MODEL_PM8028,
+       PMIC_MODEL_PM8901,
+       PMIC_MODEL_PM8027,
+       PMIC_MODEL_ISL_9519,
+       PMIC_MODEL_PM8921,
+       PMIC_MODEL_PM8018,
+       PMIC_MODEL_PM8015,
+       PMIC_MODEL_PM8014,
+       PMIC_MODEL_PM8821,
+       PMIC_MODEL_PM8038,
+       PMIC_MODEL_PM8922,
+       PMIC_MODEL_PM8917,
+       PMIC_MODEL_UNKNOWN = 0xFFFFFFFF
+};
+
+enum hw_platform_type {
+       HW_PLATFORM_UNKNOWN,
+       HW_PLATFORM_SURF,
+       HW_PLATFORM_FFA,
+       HW_PLATFORM_FLUID,
+       HW_PLATFORM_SVLTE_FFA,
+       HW_PLATFORM_SVLTE_SURF,
+       HW_PLATFORM_MTP_MDM = 7,
+       HW_PLATFORM_MTP,
+       HW_PLATFORM_LIQUID,
+       /* Dragonboard platform id is assigned as 10 in CDT */
+       HW_PLATFORM_DRAGON,
+       HW_PLATFORM_QRD,
+       HW_PLATFORM_HRD = 13,
+       HW_PLATFORM_DTV,
+       HW_PLATFORM_RCM = 21,
+       HW_PLATFORM_STP = 23,
+       HW_PLATFORM_SBC,
+       HW_PLATFORM_INVALID
+};
+
+enum accessory_chip_type {
+       ACCESSORY_CHIP_UNKNOWN = 0,
+       ACCESSORY_CHIP_CHARM = 58
+};
+
+enum qrd_platform_subtype {
+       PLATFORM_SUBTYPE_QRD,
+       PLATFORM_SUBTYPE_SKUAA,
+       PLATFORM_SUBTYPE_SKUF,
+       PLATFORM_SUBTYPE_SKUAB,
+       PLATFORM_SUBTYPE_SKUG = 0x5,
+       PLATFORM_SUBTYPE_QRD_INVALID
+};
+
+enum platform_subtype {
+       PLATFORM_SUBTYPE_UNKNOWN,
+       PLATFORM_SUBTYPE_CHARM,
+       PLATFORM_SUBTYPE_STRANGE,
+       PLATFORM_SUBTYPE_STRANGE_2A,
+       PLATFORM_SUBTYPE_INVALID
+};
+
+enum msm_cpu socinfo_get_msm_cpu(void);
+uint32_t socinfo_get_id(void);
+uint32_t socinfo_get_version(void);
+uint32_t socinfo_get_raw_id(void);
+char *socinfo_get_build_id(void);
+uint32_t socinfo_get_platform_type(void);
+uint32_t socinfo_get_platform_subtype(void);
+uint32_t socinfo_get_platform_version(void);
+uint32_t socinfo_get_serial_number(void);
+enum qcom_pmic_model socinfo_get_pmic_model(void);
+uint32_t socinfo_get_pmic_die_revision(void);
+int __init socinfo_init(void) __must_check;
+
+#endif
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

Reply via email to