Expose the DDR vendor name through a sysctl entry

This patch requires a modified SBL1 that stores the installed DDR vendor
identifier in SMEM. The new sysctl entry located at
/proc/sys/dev/ddr/vendor exposes the DDR vendor name (e.g. "Samsung").

FPIIM-2246

Depends-On: I35e2ea83716b4e1b1d49a734001233c5528bde4d
Change-Id: I4b78c8efa4a8b84cbdbf2dcc2d77f3e2e80f3df0
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index c8f46a6..ce9dd23 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -419,3 +419,5 @@
 obj-$(CONFIG_WALL_CLK_SYSFS) += wallclk_sysfs.o
 obj-$(CONFIG_ARCH_RANDOM) += early_random.o
 obj-$(CONFIG_PERFMAP) += perfmap.o
+
+obj-y += msm_ddr_sysctl.o
diff --git a/arch/arm/mach-msm/include/mach/msm_smem.h b/arch/arm/mach-msm/include/mach/msm_smem.h
index 670efe6..683a359 100644
--- a/arch/arm/mach-msm/include/mach/msm_smem.h
+++ b/arch/arm/mach-msm/include/mach/msm_smem.h
@@ -108,6 +108,7 @@
 	SMEM_ID_VENDOR0,
 	SMEM_ID_VENDOR1,
 	SMEM_ID_VENDOR2,
+	SMEM_DDR_VENDOR_ID = SMEM_ID_VENDOR2, /* DDR vendor identifier written by SBL1 */
 	SMEM_HW_SW_BUILD_ID,
 	SMEM_SMD_BASE_ID_2,
 	SMEM_SMD_FIFO_BASE_ID_2 = SMEM_SMD_BASE_ID_2 +
diff --git a/arch/arm/mach-msm/msm_ddr_sysctl.c b/arch/arm/mach-msm/msm_ddr_sysctl.c
new file mode 100644
index 0000000..284118a
--- /dev/null
+++ b/arch/arm/mach-msm/msm_ddr_sysctl.c
@@ -0,0 +1,113 @@
+/* linux/arch/arm/mach-msm/msm_ddr_sysctl.c
+ *
+ * Copyright 2017-2018 Fairphone B.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/sysctl.h>
+#include <mach/msm_smem.h>
+#include "smem_private.h"
+
+
+/* DDR vendor identifiers (64-bits unsigned integer) */
+enum {
+    DDR_VENDOR_ID_INVALID   = -1,
+    DDR_VENDOR_ID_SAMSUNG   = 1,
+    DDR_VENDOR_ID_MICRON    = 255,
+};
+
+/* Maximum DDR vendor name length */
+#define DDR_VENDOR_NAME_MAX_LEN 32
+
+/* DDR vendor names (shorter than DDR_VENDOR_NAME_MAX_LEN) */
+char *DDR_VENDOR_NAME_MICRON    = "Micron";
+char *DDR_VENDOR_NAME_SAMSUNG   = "Samsung";
+char *DDR_VENDOR_NAME_UNKNOWN   = "Unknown";
+
+static char ddr_vendor[DDR_VENDOR_NAME_MAX_LEN] = "";
+
+static struct ctl_table_header *ddr_table_header;
+
+static ctl_table ddr_vendor_table[] = {
+    {
+        .procname       = "vendor",
+        .data           = ddr_vendor,
+        .maxlen         = DDR_VENDOR_NAME_MAX_LEN,
+        .mode           = 0444,
+        .proc_handler   = proc_dostring,
+    },
+    {}
+};
+
+static ctl_table ddr_dir_table[] = {
+    {
+        .procname       = "ddr",
+        .mode           = 0555,
+        .child          = ddr_vendor_table,
+    },
+    {}
+};
+
+static ctl_table dev_root_table[] = {
+    {
+        .procname       = "dev",
+        .mode           = 0555,
+        .child          = ddr_dir_table,
+    },
+    {}
+};
+
+
+static int get_ddr_vendor_id_from_smem(void) {
+    unsigned int smem_size;
+    unsigned int *smem_ddr_vendor_id;
+
+    smem_ddr_vendor_id = smem_get_entry(SMEM_DDR_VENDOR_ID, &smem_size);
+    if (smem_ddr_vendor_id == NULL) {
+        printk("Could not get the DDR vendor identifier from SMEM\n");
+        return DDR_VENDOR_ID_INVALID;
+    }
+
+    return (int) *smem_ddr_vendor_id;
+}
+
+static int __init msm_ddr_init(void) {
+    switch (get_ddr_vendor_id_from_smem()) {
+        case DDR_VENDOR_ID_MICRON:
+            strncpy(ddr_vendor, DDR_VENDOR_NAME_MICRON, DDR_VENDOR_NAME_MAX_LEN);
+            break;
+        case DDR_VENDOR_ID_SAMSUNG:
+            strncpy(ddr_vendor, DDR_VENDOR_NAME_SAMSUNG, DDR_VENDOR_NAME_MAX_LEN);
+            break;
+        case DDR_VENDOR_ID_INVALID:
+        default:
+            strncpy(ddr_vendor, DDR_VENDOR_NAME_UNKNOWN, DDR_VENDOR_NAME_MAX_LEN);
+            break;
+    }
+    ddr_vendor[DDR_VENDOR_NAME_MAX_LEN-1] = '\0';
+
+    ddr_table_header = register_sysctl_table(dev_root_table);
+    if (!ddr_table_header)
+        return -ENOMEM;
+
+    return 0;
+}
+
+static void __exit msm_ddr_exit(void) {
+    unregister_sysctl_table(ddr_table_header);
+}
+
+module_init(msm_ddr_init);
+module_exit(msm_ddr_exit);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DDR information");