2016-10-20 14:07:03

by Imran Khan

[permalink] [raw]
Subject: [PATCH] soc: qcom: Add SoC info driver

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 <[email protected]>
---
.../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


2016-10-20 14:09:45

by Mark Rutland

[permalink] [raw]
Subject: Re: [PATCH] soc: qcom: Add SoC info driver

On Thu, Oct 20, 2016 at 07:36:22PM +0530, 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 <[email protected]>
> ---
> .../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";
> + };

Binding should describe *hardware*, not *drivers*.

This clearly does not describe a paerticular piece of hardware, and as
such does not make sense.

If this is trying to describe some paritcular hardware, please describe
that in the binding explicitly.

Thanks,
Mark.

2016-10-20 15:20:23

by Andy Gross

[permalink] [raw]
Subject: Re: [PATCH] soc: qcom: Add SoC info driver

On Thu, Oct 20, 2016 at 07:36:22PM +0530, 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 <[email protected]>
> ---
> .../devicetree/bindings/soc/qcom/qcom,socinfo.txt | 18 +
> drivers/soc/qcom/socinfo.c | 1173 ++++++++++++++++++++

1173 lines!!!!!! To latch smem entry and read/process those contents?

> 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";
> + };

Let's drop adding a new binding. Let's roll this into the smem driver itself.

> 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

This needs to be more specific. SMEM_SOCINFO_XXX

> +#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
> +

<snip>

> +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,
> +};

Instead of having a dummy, can we just fail if we don't find a match? And by
fail, I mean having an 'unknown' cpu?

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

<snip>

> +uint32_t socinfo_get_id(void)
> +{
> + return (socinfo) ? socinfo->v0_1.id : 0;
> +}
> +EXPORT_SYMBOL_GPL(socinfo_get_id);

Why do we have EXPORTS at all? Drivers should be using compatibles to figure
out what they are.

<snip>

> +/* 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());
> +}
> +

<snip>

> +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;
> +}

Can we just remove this and just return whatever unknown values for the sysfs
entries?

> +
> +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;
> + }
> +}
> +

<snip>

> +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,
> + },
> +};

As mentioned above:
Perhaps we can forgo the separate platform driver and just roll this into the
smem driver that already exists. You can create a device there and get the
right attributes for the system and setup your sysfs entries.

Maybe splitting off the sysfs into a separate file would also be good. That
keeps it nice and clean and away from the main smem code.


> +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

You don't need this mess. You could just build a compat match and match against
the root compatible.

> +
> +#define PLATFORM_SUBTYPE_MDM 1
> +#define PLATFORM_SUBTYPE_INTERPOSERV3 2
> +#define PLATFORM_SUBTYPE_SGLTE 6
> +

<snip>

> +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;

We shouldn't have APIs for this. This was supposed to be userspace access only,
correct?


Andy

2016-10-21 10:34:03

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH] soc: qcom: Add SoC info driver

On Thursday, October 20, 2016 7:36:22 PM CEST Imran Khan wrote:
> +#include <linux/soc/qcom/socinfo.h>
> +#include <linux/soc/qcom/smem.h>
> +
> +#include <asm/system_misc.h>

I don't see anything here that needs asm/system_misc.h

> +const char *hw_platform[] = {
> + [HW_PLATFORM_UNKNOWN] = "Unknown",
> + [HW_PLATFORM_SURF] = "Surf",
> + [HW_PLATFORM_FFA] = "FFA",
> + [HW_PLATFORM_FLUID] = "Fluid",
> +};
> +
> +const char *qrd_hw_platform_subtype[] = {
> + [PLATFORM_SUBTYPE_QRD] = "QRD",
> + [PLATFORM_SUBTYPE_SKUAA] = "SKUAA",
> +};
> +
> +const char *hw_platform_subtype[] = {
> + [PLATFORM_SUBTYPE_UNKNOWN] = "Unknown",
> +};


Make these all static

> +
> +/* Used to parse shared memory. Must match the modem. */
> +struct socinfo_v0_1 {
> + uint32_t format;
> + uint32_t id;
> + uint32_t version;

s/uint32_t/u32/

> +
> +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);

Please remove all the exports and mark the functions static,
we don't want any drivers poking vendor-specific interfaces
for this.

> +/* socinfo: sysfs functions */

This seems overly verbose, having both raw and human-readable
IDs is generally not necessary, pick one of the two. If you
need any fields that we don't already support in soc_device,
let's talk about adding them to the generic structure.


> +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)) {

IS_ERR_OR_NULL() indicates that your interface is not well-designed.
Please return either NULL on all failures, or return an error pointer
on all failures, not both.

> +/* Platform Subtype String is being deprecated. Use Platform
> + * Subtype ID instead.
> + */

If it's deprecated, don't add it to the kernel!


> 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

Merge the header file into the driver.

Arnd

2016-10-25 09:38:33

by Imran Khan

[permalink] [raw]
Subject: Re: [PATCH] soc: qcom: Add SoC info driver

On 10/20/2016 8:50 PM, Andy Gross wrote:
> On Thu, Oct 20, 2016 at 07:36:22PM +0530, 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 <[email protected]>
>> ---
>> .../devicetree/bindings/soc/qcom/qcom,socinfo.txt | 18 +
>> drivers/soc/qcom/socinfo.c | 1173 ++++++++++++++++++++
>
> 1173 lines!!!!!! To latch smem entry and read/process those contents?
>
The number of code lines will be reduced once we remove the function to setup dummy socinfo.
Also I will try to remove redundant sysfs attributes.

>> 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";
>> + };
>
> Let's drop adding a new binding. Let's roll this into the smem driver itself.
>
Okay. I will move the socinfo setup part to smem and will keep only socinfo sysfs related
stuff in this file.
>> 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
>
> This needs to be more specific. SMEM_SOCINFO_XXX
>
>> +#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
>> +
>
> <snip>
>
>> +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,
>> +};
>
> Instead of having a dummy, can we just fail if we don't find a match? And by
> fail, I mean having an 'unknown' cpu?
>
>> +
>> +static char *socinfo_get_id_string(void)
>> +{
>> + return (socinfo) ? cpu_of_id[socinfo->v0_1.id].soc_id_string : NULL;
>> +}
>> +
>
> <snip>
>
>> +uint32_t socinfo_get_id(void)
>> +{
>> + return (socinfo) ? socinfo->v0_1.id : 0;
>> +}
>> +EXPORT_SYMBOL_GPL(socinfo_get_id);
>
> Why do we have EXPORTS at all? Drivers should be using compatibles to figure
> out what they are.
>
> <snip>
>
Okay.
>> +/* 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());
>> +}
>> +
>
> <snip>
>
>> +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;
>> +}
>
> Can we just remove this and just return whatever unknown values for the sysfs
> entries?
>
Will shorten this function. In case of error I will use 0 as socid and "unknown CPU"
as soc-id.
>> +
>> +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;
>> + }
>> +}
>> +
>
> <snip>
>
>> +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,
>> + },
>> +};
>
> As mentioned above:
> Perhaps we can forgo the separate platform driver and just roll this into the
> smem driver that already exists. You can create a device there and get the
> right attributes for the system and setup your sysfs entries.
>
> Maybe splitting off the sysfs into a separate file would also be good. That
> keeps it nice and clean and away from the main smem code.
>
>
Okay. I will move the socinfo setup part to smem and will keep only socinfo sysfs related
stuff in this file.
>> +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
>
> You don't need this mess. You could just build a compat match and match against
> the root compatible.
>
Okay. As we don't intend to use dummy soc information, this part will become
redundant and hence it will be removed.
>> +
>> +#define PLATFORM_SUBTYPE_MDM 1
>> +#define PLATFORM_SUBTYPE_INTERPOSERV3 2
>> +#define PLATFORM_SUBTYPE_SGLTE 6
>> +
>
> <snip>
>
>> +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;
>
> We shouldn't have APIs for this. This was supposed to be userspace access only,
> correct?
>
Yes.I will remove these. Although if some driver needs some data pertaining to soc
information, I will have to provide an alternate way for that but that can be done
in a generic way rather than using vendor specific interface
>
> Andy
>


--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a\nmember of the Code Aurora Forum, hosted by The Linux Foundation

2016-10-25 09:53:44

by Imran Khan

[permalink] [raw]
Subject: Re: [PATCH] soc: qcom: Add SoC info driver

On 10/21/2016 4:03 PM, Arnd Bergmann wrote:
> On Thursday, October 20, 2016 7:36:22 PM CEST Imran Khan wrote:
>> +#include <linux/soc/qcom/socinfo.h>
>> +#include <linux/soc/qcom/smem.h>
>> +
>> +#include <asm/system_misc.h>
>
> I don't see anything here that needs asm/system_misc.h
>

Okay. I will not use this header file here.

>> +const char *hw_platform[] = {
>> + [HW_PLATFORM_UNKNOWN] = "Unknown",
>> + [HW_PLATFORM_SURF] = "Surf",
>> + [HW_PLATFORM_FFA] = "FFA",
>> + [HW_PLATFORM_FLUID] = "Fluid",
>> +};
>> +
>> +const char *qrd_hw_platform_subtype[] = {
>> + [PLATFORM_SUBTYPE_QRD] = "QRD",
>> + [PLATFORM_SUBTYPE_SKUAA] = "SKUAA",
>> +};
>> +
>> +const char *hw_platform_subtype[] = {
>> + [PLATFORM_SUBTYPE_UNKNOWN] = "Unknown",
>> +};
>
>
> Make these all static
>

Okay. Will do this.

>> +
>> +/* Used to parse shared memory. Must match the modem. */
>> +struct socinfo_v0_1 {
>> + uint32_t format;
>> + uint32_t id;
>> + uint32_t version;
>
> s/uint32_t/u32/
>

Okay. Will do this.

>> +
>> +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);
>
> Please remove all the exports and mark the functions static,
> we don't want any drivers poking vendor-specific interfaces
> for this.
>

Yes. I will avoid exporting vendor specific interface.

>> +/* socinfo: sysfs functions */
>
> This seems overly verbose, having both raw and human-readable
> IDs is generally not necessary, pick one of the two. If you
> need any fields that we don't already support in soc_device,
> let's talk about adding them to the generic structure.
>
>

Okay. I will go for human readable IDs. Can we add 2 more fields
in the generic structure.
These 2 fields would be:

vendor: A string for vendor name
serial_number: A string containing serial number for the platform

>> +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)) {
>
> IS_ERR_OR_NULL() indicates that your interface is not well-designed.
> Please return either NULL on all failures, or return an error pointer
> on all failures, not both.
>
>> +/* Platform Subtype String is being deprecated. Use Platform
>> + * Subtype ID instead.
>> + */
>
> If it's deprecated, don't add it to the kernel!
>
>

Okay.

>> 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
>
> Merge the header file into the driver.
>

okay.

> Arnd
>


--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a\nmember of the Code Aurora Forum, hosted by The Linux Foundation

2016-10-25 20:49:43

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH] soc: qcom: Add SoC info driver

On Tuesday, October 25, 2016 3:23:34 PM CEST Imran Khan wrote:
> On 10/21/2016 4:03 PM, Arnd Bergmann wrote:
> >> +/* socinfo: sysfs functions */
> >
> > This seems overly verbose, having both raw and human-readable
> > IDs is generally not necessary, pick one of the two. If you
> > need any fields that we don't already support in soc_device,
> > let's talk about adding them to the generic structure.
> >
> >
>
> Okay. I will go for human readable IDs. Can we add 2 more fields
> in the generic structure.
> These 2 fields would be:
>
> vendor: A string for vendor name
> serial_number: A string containing serial number for the platform


serial_number seems straightforward, adding this seems like a good
idea. I don't understand yet what would go into the vendor field
though. For this particular driver, is it always "Qualcomm", or
would it be a third-party that makes a device based on that chip?

Arnd

2016-10-26 13:50:54

by Imran Khan

[permalink] [raw]
Subject: Re: [PATCH] soc: qcom: Add SoC info driver

On 10/26/2016 2:19 AM, Arnd Bergmann wrote:
> On Tuesday, October 25, 2016 3:23:34 PM CEST Imran Khan wrote:
>> On 10/21/2016 4:03 PM, Arnd Bergmann wrote:
>>>> +/* socinfo: sysfs functions */
>>>
>>> This seems overly verbose, having both raw and human-readable
>>> IDs is generally not necessary, pick one of the two. If you
>>> need any fields that we don't already support in soc_device,
>>> let's talk about adding them to the generic structure.
>>>
>>>
>>
>> Okay. I will go for human readable IDs. Can we add 2 more fields
>> in the generic structure.
>> These 2 fields would be:
>>
>> vendor: A string for vendor name
>> serial_number: A string containing serial number for the platform
>
>
> serial_number seems straightforward, adding this seems like a good
> idea. I don't understand yet what would go into the vendor field
> though. For this particular driver, is it always "Qualcomm", or
> would it be a third-party that makes a device based on that chip?
>

As we are talking about generic soc_device_attribute fields, I was hoping that
having a vendor field would be helpful as along with family it would provide
a more thorough information. Also as more than one foundries may be used for
a soc, can we have a field say foundry_id to provide this information.

> Arnd
>


--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a\nmember of the Code Aurora Forum, hosted by The Linux Foundation

2016-10-26 14:05:29

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH] soc: qcom: Add SoC info driver

On Wednesday, October 26, 2016 7:20:42 PM CEST Imran Khan wrote:
> On 10/26/2016 2:19 AM, Arnd Bergmann wrote:
> > On Tuesday, October 25, 2016 3:23:34 PM CEST Imran Khan wrote:
> >> On 10/21/2016 4:03 PM, Arnd Bergmann wrote:
> >>>> +/* socinfo: sysfs functions */
> >>>
> >>> This seems overly verbose, having both raw and human-readable
> >>> IDs is generally not necessary, pick one of the two. If you
> >>> need any fields that we don't already support in soc_device,
> >>> let's talk about adding them to the generic structure.
> >>>
> >>>
> >>
> >> Okay. I will go for human readable IDs. Can we add 2 more fields
> >> in the generic structure.
> >> These 2 fields would be:
> >>
> >> vendor: A string for vendor name
> >> serial_number: A string containing serial number for the platform
> >
> >
> > serial_number seems straightforward, adding this seems like a good
> > idea. I don't understand yet what would go into the vendor field
> > though. For this particular driver, is it always "Qualcomm", or
> > would it be a third-party that makes a device based on that chip?
> >
>
> As we are talking about generic soc_device_attribute fields, I was hoping that
> having a vendor field would be helpful as along with family it would provide
> a more thorough information. Also as more than one foundries may be used for
> a soc, can we have a field say foundry_id to provide this information.

My first feeling is that this 'vendor' information can should be
derived from the family. It's also not clear what would happen
to this when a company gets bought. E.g. the Oxnas product family
was subsequently owned by Oxford, PLX, Avago and Broadcom, and the
mxs family was Sigmatel, Freescale, now NXP and might soon be
Qualcomm. What would you put in there in this case?

Arnd

2016-10-26 14:12:59

by Imran Khan

[permalink] [raw]
Subject: Re: [PATCH] soc: qcom: Add SoC info driver

On 10/26/2016 7:35 PM, Arnd Bergmann wrote:
>> > As we are talking about generic soc_device_attribute fields, I was hoping that
>> > having a vendor field would be helpful as along with family it would provide
>> > a more thorough information. Also as more than one foundries may be used for
>> > a soc, can we have a field say foundry_id to provide this information.
> My first feeling is that this 'vendor' information can should be
> derived from the family. It's also not clear what would happen
> to this when a company gets bought. E.g. the Oxnas product family
> was subsequently owned by Oxford, PLX, Avago and Broadcom, and the
> mxs family was Sigmatel, Freescale, now NXP and might soon be
> Qualcomm. What would you put in there in this case?

Okay, not having vendor field is fine for me. Could you also suggest
something about the foundry_id field.

--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a\nmember of the Code Aurora Forum, hosted by The Linux Foundation

2016-10-26 14:46:57

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH] soc: qcom: Add SoC info driver

On Wednesday, October 26, 2016 7:42:08 PM CEST Imran Khan wrote:
> On 10/26/2016 7:35 PM, Arnd Bergmann wrote:
> >> > As we are talking about generic soc_device_attribute fields, I was hoping that
> >> > having a vendor field would be helpful as along with family it would provide
> >> > a more thorough information. Also as more than one foundries may be used for
> >> > a soc, can we have a field say foundry_id to provide this information.
> > My first feeling is that this 'vendor' information can should be
> > derived from the family. It's also not clear what would happen
> > to this when a company gets bought. E.g. the Oxnas product family
> > was subsequently owned by Oxford, PLX, Avago and Broadcom, and the
> > mxs family was Sigmatel, Freescale, now NXP and might soon be
> > Qualcomm. What would you put in there in this case?
>
> Okay, not having vendor field is fine for me. Could you also suggest
> something about the foundry_id field.

This one seems more well-defined, so it's probably ok to add. What
would be the use case of reading this? Would you want to read it
just from user space or also from the kernel?

Maybe this can be combined with a manufacturing process, which probably
falls into a similar category, so we could have something like
"TSMC 28ULP" as a string in there.

Arnd

2016-10-27 13:50:06

by Imran Khan

[permalink] [raw]
Subject: Re: [PATCH] soc: qcom: Add SoC info driver

On 10/26/2016 8:16 PM, Arnd Bergmann wrote:
> On Wednesday, October 26, 2016 7:42:08 PM CEST Imran Khan wrote:
>> On 10/26/2016 7:35 PM, Arnd Bergmann wrote:
>>>>> As we are talking about generic soc_device_attribute fields, I was hoping that
>>>>> having a vendor field would be helpful as along with family it would provide
>>>>> a more thorough information. Also as more than one foundries may be used for
>>>>> a soc, can we have a field say foundry_id to provide this information.
>>> My first feeling is that this 'vendor' information can should be
>>> derived from the family. It's also not clear what would happen
>>> to this when a company gets bought. E.g. the Oxnas product family
>>> was subsequently owned by Oxford, PLX, Avago and Broadcom, and the
>>> mxs family was Sigmatel, Freescale, now NXP and might soon be
>>> Qualcomm. What would you put in there in this case?
>>
>> Okay, not having vendor field is fine for me. Could you also suggest
>> something about the foundry_id field.
>
> This one seems more well-defined, so it's probably ok to add. What
> would be the use case of reading this? Would you want to read it
> just from user space or also from the kernel?
>

As of now the use case I can think of, only involve reading this from user
space. For example for the same soc, coming from different foundries with
different manufacturing process, we may have a situation where some inconsistent
h/w behavior is being observed only on parts received from a certain foundry
and in those cases this information may help in segregation of problematic socs
and may also be used in testing these socs under a different set of settings like
voltage, frequency etc.

> Maybe this can be combined with a manufacturing process, which probably
> falls into a similar category, so we could have something like
> "TSMC 28ULP" as a string in there.
>

Yes. Having a manufacturing process as part of foundry-id can provide a more
thorough information.

> Arnd
>


--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a\nmember of the Code Aurora Forum, hosted by The Linux Foundation

2016-10-27 13:55:04

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH] soc: qcom: Add SoC info driver

On Thursday, October 27, 2016 6:40:27 PM CEST Imran Khan wrote:
> On 10/26/2016 8:16 PM, Arnd Bergmann wrote:
> > On Wednesday, October 26, 2016 7:42:08 PM CEST Imran Khan wrote:
> >> On 10/26/2016 7:35 PM, Arnd Bergmann wrote:
> >>>>> As we are talking about generic soc_device_attribute fields, I was hoping that
> >>>>> having a vendor field would be helpful as along with family it would provide
> >>>>> a more thorough information. Also as more than one foundries may be used for
> >>>>> a soc, can we have a field say foundry_id to provide this information.
> >>> My first feeling is that this 'vendor' information can should be
> >>> derived from the family. It's also not clear what would happen
> >>> to this when a company gets bought. E.g. the Oxnas product family
> >>> was subsequently owned by Oxford, PLX, Avago and Broadcom, and the
> >>> mxs family was Sigmatel, Freescale, now NXP and might soon be
> >>> Qualcomm. What would you put in there in this case?
> >>
> >> Okay, not having vendor field is fine for me. Could you also suggest
> >> something about the foundry_id field.
> >
> > This one seems more well-defined, so it's probably ok to add. What
> > would be the use case of reading this? Would you want to read it
> > just from user space or also from the kernel?
> >
>
> As of now the use case I can think of, only involve reading this from user
> space. For example for the same soc, coming from different foundries with
> different manufacturing process, we may have a situation where some inconsistent
> h/w behavior is being observed only on parts received from a certain foundry
> and in those cases this information may help in segregation of problematic socs
> and may also be used in testing these socs under a different set of settings like
> voltage, frequency etc.
>
> > Maybe this can be combined with a manufacturing process, which probably
> > falls into a similar category, so we could have something like
> > "TSMC 28ULP" as a string in there.
> >
>
> Yes. Having a manufacturing process as part of foundry-id can provide a more
> thorough information.

Ok, sounds good. Let's do it like this. We can always add support for
in-kernel matching of this string if needed later.

Arnd

2016-11-02 07:49:57

by Imran Khan

[permalink] [raw]
Subject: Re: [PATCH] soc: qcom: Add SoC info driver

On 10/27/2016 7:11 PM, Arnd Bergmann wrote:
> On Thursday, October 27, 2016 6:40:27 PM CEST Imran Khan wrote:
>> On 10/26/2016 8:16 PM, Arnd Bergmann wrote:
>>> On Wednesday, October 26, 2016 7:42:08 PM CEST Imran Khan wrote:
>>>> On 10/26/2016 7:35 PM, Arnd Bergmann wrote:
>>>>>>> As we are talking about generic soc_device_attribute fields, I was hoping that
>>>>>>> having a vendor field would be helpful as along with family it would provide
>>>>>>> a more thorough information. Also as more than one foundries may be used for
>>>>>>> a soc, can we have a field say foundry_id to provide this information.
>>>>> My first feeling is that this 'vendor' information can should be
>>>>> derived from the family. It's also not clear what would happen
>>>>> to this when a company gets bought. E.g. the Oxnas product family
>>>>> was subsequently owned by Oxford, PLX, Avago and Broadcom, and the
>>>>> mxs family was Sigmatel, Freescale, now NXP and might soon be
>>>>> Qualcomm. What would you put in there in this case?
>>>>
>>>> Okay, not having vendor field is fine for me. Could you also suggest
>>>> something about the foundry_id field.
>>>
>>> This one seems more well-defined, so it's probably ok to add. What
>>> would be the use case of reading this? Would you want to read it
>>> just from user space or also from the kernel?
>>>
>>
>> As of now the use case I can think of, only involve reading this from user
>> space. For example for the same soc, coming from different foundries with
>> different manufacturing process, we may have a situation where some inconsistent
>> h/w behavior is being observed only on parts received from a certain foundry
>> and in those cases this information may help in segregation of problematic socs
>> and may also be used in testing these socs under a different set of settings like
>> voltage, frequency etc.
>>
>>> Maybe this can be combined with a manufacturing process, which probably
>>> falls into a similar category, so we could have something like
>>> "TSMC 28ULP" as a string in there.
>>>
>>
>> Yes. Having a manufacturing process as part of foundry-id can provide a more
>> thorough information.
>
> Ok, sounds good. Let's do it like this. We can always add support for
> in-kernel matching of this string if needed later.
>

Thanks for the feedback. So how should I proceed now, should I

i. send one patch first that adds the serial_number and foundry_id fields
in generic soc_dev_attribute structure and then send my modified socinfo
driver as per new soc_dev_attribute structure

or

ii. send both the changes as 2 separate patches of the same patch set.

or

iii. Continue with the current soc_dev_attribute structure and modify
the socinfo driver once soc_dev_attribute structure has serial_number
and foundry_id fields.


> Arnd
>


--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a\nmember of the Code Aurora Forum, hosted by The Linux Foundation

2016-11-02 13:12:17

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH] soc: qcom: Add SoC info driver

On Wednesday 02 November 2016, Imran Khan wrote:
> On 10/27/2016 7:11 PM, Arnd Bergmann wrote:
> > On Thursday, October 27, 2016 6:40:27 PM CEST Imran Khan wrote:
> >> On 10/26/2016 8:16 PM, Arnd Bergmann wrote:
> >>> On Wednesday, October 26, 2016 7:42:08 PM CEST Imran Khan wrote:
> >>
> >> Yes. Having a manufacturing process as part of foundry-id can provide a more
> >> thorough information.
> >
> > Ok, sounds good. Let's do it like this. We can always add support for
> > in-kernel matching of this string if needed later.
> >
>
> Thanks for the feedback. So how should I proceed now, should I
>
> i. send one patch first that adds the serial_number and foundry_id fields
> in generic soc_dev_attribute structure and then send my modified socinfo
> driver as per new soc_dev_attribute structure
>
> or
>
> ii. send both the changes as 2 separate patches of the same patch set.
>
> or
>
> iii. Continue with the current soc_dev_attribute structure and modify
> the socinfo driver once soc_dev_attribute structure has serial_number
> and foundry_id fields.

It's not overly important, but I'd prefer the third approach, the advantage
being that we can review the driver and merge it independently of
any possible further discussion on the added attributes. Sending
this as a series of three patches (1. add the driver, 2. add the
infrastructure, 3. use the infrastructure in your driver) is probably
best.

Arnd

2016-11-02 16:28:18

by Bjorn Andersson

[permalink] [raw]
Subject: Re: [PATCH] soc: qcom: Add SoC info driver

On Wed 26 Oct 07:05 PDT 2016, Arnd Bergmann wrote:

> On Wednesday, October 26, 2016 7:20:42 PM CEST Imran Khan wrote:
> > On 10/26/2016 2:19 AM, Arnd Bergmann wrote:
> > > On Tuesday, October 25, 2016 3:23:34 PM CEST Imran Khan wrote:
> > >> On 10/21/2016 4:03 PM, Arnd Bergmann wrote:
> > >>>> +/* socinfo: sysfs functions */
> > >>>
> > >>> This seems overly verbose, having both raw and human-readable
> > >>> IDs is generally not necessary, pick one of the two. If you
> > >>> need any fields that we don't already support in soc_device,
> > >>> let's talk about adding them to the generic structure.
> > >>>
> > >>>
> > >>
> > >> Okay. I will go for human readable IDs. Can we add 2 more fields
> > >> in the generic structure.
> > >> These 2 fields would be:
> > >>
> > >> vendor: A string for vendor name
> > >> serial_number: A string containing serial number for the platform
> > >
> > >
> > > serial_number seems straightforward, adding this seems like a good
> > > idea. I don't understand yet what would go into the vendor field
> > > though. For this particular driver, is it always "Qualcomm", or
> > > would it be a third-party that makes a device based on that chip?
> > >
> >
> > As we are talking about generic soc_device_attribute fields, I was hoping that
> > having a vendor field would be helpful as along with family it would provide
> > a more thorough information. Also as more than one foundries may be used for
> > a soc, can we have a field say foundry_id to provide this information.
>
> My first feeling is that this 'vendor' information can should be
> derived from the family.

In [1] Geert just put the vendor directly into "family", while Imran
uses "Snapdragon" (which I find reasonable in the Qualcomm case). But it
seems like Geert would like a "vendor" as well, rather than a "family".


And if "family" really is supposed to contain the "SoC family name" and
we're trying to provide user space with some useful information (for
some reason), should we just rely on the unlikeliness of two vendors
using the same family name?

[1] http://www.mail-archive.com/[email protected]/msg1261742.html

Regards,
Bjorn

2016-11-09 14:44:35

by Geert Uytterhoeven

[permalink] [raw]
Subject: Re: [PATCH] soc: qcom: Add SoC info driver

Hi Bjorn,

On Wed, Nov 2, 2016 at 5:28 PM, Bjorn Andersson
<[email protected]> wrote:
> On Wed 26 Oct 07:05 PDT 2016, Arnd Bergmann wrote:
>> On Wednesday, October 26, 2016 7:20:42 PM CEST Imran Khan wrote:
>> > On 10/26/2016 2:19 AM, Arnd Bergmann wrote:
>> > > On Tuesday, October 25, 2016 3:23:34 PM CEST Imran Khan wrote:
>> > >> On 10/21/2016 4:03 PM, Arnd Bergmann wrote:
>> > >> Okay. I will go for human readable IDs. Can we add 2 more fields
>> > >> in the generic structure.
>> > >> These 2 fields would be:
>> > >>
>> > >> vendor: A string for vendor name
>> > >> serial_number: A string containing serial number for the platform
>> > >
>> > >
>> > > serial_number seems straightforward, adding this seems like a good
>> > > idea. I don't understand yet what would go into the vendor field
>> > > though. For this particular driver, is it always "Qualcomm", or
>> > > would it be a third-party that makes a device based on that chip?
>> >
>> > As we are talking about generic soc_device_attribute fields, I was hoping that
>> > having a vendor field would be helpful as along with family it would provide
>> > a more thorough information. Also as more than one foundries may be used for
>> > a soc, can we have a field say foundry_id to provide this information.
>>
>> My first feeling is that this 'vendor' information can should be
>> derived from the family.
>
> In [1] Geert just put the vendor directly into "family", while Imran
> uses "Snapdragon" (which I find reasonable in the Qualcomm case). But it
> seems like Geert would like a "vendor" as well, rather than a "family".
>
> And if "family" really is supposed to contain the "SoC family name" and
> we're trying to provide user space with some useful information (for
> some reason), should we just rely on the unlikeliness of two vendors
> using the same family name?
>
> [1] http://www.mail-archive.com/[email protected]/msg1261742.html

I think vendor is slightly less volatile than family. Family may
change overnight
if marketing had a good dream. Well, vendor may change, too...

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds