mosys: Add herobrine memory support

BUG=b:177917361
TEST=mosys -v -v -v -v memory spd print all

Cq-Depend: chrome-internal:4710395
Change-Id: I936bf8fc66a2cb5853cd206a1d82d80bdc846d95
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/mosys/+/3422166
Reviewed-by: Julius Werner <jwerner@chromium.org>
Reviewed-by: Jack Rosenthal <jrosenth@chromium.org>
Reviewed-by: Jian-Jia Su <jjsu@chromium.org>
Tested-by: Bob Moragues <moragues@chromium.org>
Commit-Queue: Jian-Jia Su <jjsu@chromium.org>
diff --git a/include/lib/fdt.h b/include/lib/fdt.h
index 6bde94d..52e6677 100644
--- a/include/lib/fdt.h
+++ b/include/lib/fdt.h
@@ -44,6 +44,20 @@
 extern int fdt_get_ram_code(uint32_t *ram_code);
 
 /*
+ * fdt_get_ram_manufacturer_id - Get RAM manufacturer id from device tree
+ *
+ * returns 0 to indicate success, <0 to indicate failure.
+ */
+extern int fdt_get_ram_manufacturer_id(uint8_t *manufacturer_id);
+
+/*
+ * fdt_get_ram_revision_id - Get the two byte RAM revision id from device tree
+ *
+ * returns 0 to indicate success, <0 to indicate failure.
+ */
+extern int fdt_get_ram_revision_id(uint8_t *revision_id1, uint8_t *revision_id2);
+
+/*
  * fdt_get_board_id - Obtain board ID code from FDT board-id node
  *
  * returns 0 to indicate success, <0 to indicate failure.
diff --git a/include/lib/lpddr_ids.h b/include/lib/lpddr_ids.h
new file mode 100644
index 0000000..33ae4ce
--- /dev/null
+++ b/include/lib/lpddr_ids.h
@@ -0,0 +1,13 @@
+/* Copyright 2022 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef LPDDR_IDS_H
+#define LPDDR_IDS_H
+
+#include "lib/nonspd.h"
+
+extern const struct nonspd_mem_info *lpddr_info_from_fdt_ids(void);
+
+#endif /* LPDDR_IDS_H */
diff --git a/include/lib/nonspd_modules.h b/include/lib/nonspd_modules.h
index d3cbb1e..66c0e62 100644
--- a/include/lib/nonspd_modules.h
+++ b/include/lib/nonspd_modules.h
@@ -37,6 +37,7 @@
 extern const struct nonspd_mem_info micron_lpddr4x_mt53e1g32d2np_046wta;
 extern const struct nonspd_mem_info micron_lpddr4x_mt53e1g32d2np_046wtb;
 extern const struct nonspd_mem_info micron_lpddr4x_mt53e2g32d4nq_046wta;
+extern const struct nonspd_mem_info micron_lpddr4x_mt53e2g32d4nq_046wtc;
 extern const struct nonspd_mem_info micron_lpddr4x_mt53e512m32d1np_046wtb;
 extern const struct nonspd_mem_info micron_lpddr4x_mt53e512m32d2np_046;
 extern const struct nonspd_mem_info micron_lpddr4x_mt53d1g32d4dt_046wtd;
@@ -56,6 +57,7 @@
 extern const struct nonspd_mem_info samsung_lpddr4x_k4ube3d4ab_mgcl;
 extern const struct nonspd_mem_info samsung_lpddr4x_k4ube3d4aa_mgcr;
 extern const struct nonspd_mem_info samsung_lpddr4x_k4uce3q4aa_mgcr;
+extern const struct nonspd_mem_info samsung_lpddr4x_k4uce3q4ab_mgcl;
 extern const struct nonspd_mem_info sandisk_lpddr4x_sdada4cr_128g;
 extern const struct nonspd_mem_info foresee_lpddr4x_feprf6432_58a1930;
 extern const struct nonspd_mem_info foresee_lpddr4x_flxc2004g_n1;
diff --git a/lib/file/fdt.c b/lib/file/fdt.c
index 82f9f4f..468dea8 100644
--- a/lib/file/fdt.c
+++ b/lib/file/fdt.c
@@ -52,6 +52,10 @@
 #define FDT_BOARD_ID_PATH	"firmware/coreboot/board-id"
 #define FDT_SKU_ID_PATH		"firmware/coreboot/sku-id"
 
+#define LPDDR3_MFG_ID_PATH	"lpddr3/manufacturer_id"
+#define LPDDR3_REV_ID_PATH	"lpddr3/revision_id"
+
+
 /* returns number of bytes read or -1 to indicate error */
 static ssize_t fdt_read_node(const char *path, char *buf, size_t buf_sz)
 {
@@ -96,6 +100,60 @@
 	return 0;
 }
 
+/*
+ * get_ram_manufacturer_id - Get RAM manufacturer id from device tree
+ *
+ * returns 0 to indicate success, <0 to indicate failure.
+ */
+
+int fdt_get_ram_manufacturer_id(uint8_t *manufacturer_id)
+{
+	uint32_t buf[2];
+
+	if (fdt_read_node(LPDDR3_MFG_ID_PATH, (char *)buf, sizeof(buf)) < sizeof(uint32_t)) {
+		lprintf(LOG_ERR,
+			"%s: Error when reading Manufacturer ID\n",
+			__func__);
+		return -1;
+	}
+
+	*manufacturer_id = ntohl(buf[0]);
+
+	lprintf(LOG_DEBUG,
+		"%s: manufacturer_id: %#02x\n",
+		__func__,
+		*manufacturer_id);
+	return 0;
+}
+
+/*
+ * get_ram_revision_id - Get RAM revision id from device tree
+ *
+ * returns 0 to indicate success, <0 to indicate failure.
+ */
+
+int fdt_get_ram_revision_id(uint8_t *revision_id1, uint8_t *revision_id2)
+{
+	uint32_t buf[3];
+
+	if (fdt_read_node(LPDDR3_REV_ID_PATH, (char *)buf, sizeof(buf)) < sizeof(uint32_t) * 2) {
+		lprintf(LOG_ERR,
+			"%s: Error when reading Revision ID\n",
+			__func__);
+		return -1;
+	}
+
+	*revision_id1 = ntohl(buf[0]);
+	*revision_id2 = ntohl(buf[1]);
+
+	lprintf(LOG_DEBUG,
+		"%s: revision_id: %#02x %#02x\n",
+		__func__,
+		*revision_id1,
+		*revision_id2);
+	return 0;
+}
+
 int fdt_get_board_id(uint32_t *board_id)
 {
 	if (fdt_get_uint32_val(FDT_BOARD_ID_PATH, board_id) < 0) {
diff --git a/lib/spd/lpddr_ids.c b/lib/spd/lpddr_ids.c
new file mode 100644
index 0000000..44cfc1e
--- /dev/null
+++ b/lib/spd/lpddr_ids.c
@@ -0,0 +1,74 @@
+/* Copyright 2022 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "lib/lpddr_ids.h"
+
+#include "lib/fdt.h"
+#include <stdbool.h>
+#include "lib/nonspd.h"
+#include "lib/nonspd_modules.h"
+
+enum herobrine_ram_manufacturer {
+	SAMSUNG = 0x01,
+	HYNIX = 0x06,
+	MICRON = 0xff,
+};
+
+const static struct {
+	uint8_t manufacturer;
+	uint8_t revision1;
+	uint8_t revision2;
+	const struct nonspd_mem_info *mem_info;
+} ddr_table[] = {
+	/* Current used components */
+	{ HYNIX, 0x06, 0x06, &hynix_lpddr4x_h9hcnnnfammlxr_nee },
+	{ MICRON, 0x07, 0x07, &micron_lpddr4x_mt53e2g32d4nq_046wtc},
+
+	/* Placeholder for other AVL components in DLM */
+	{ HYNIX, 0x00, 0x00, &hynix_lpddr4x_h54g68cyrbx248 },
+	{ SAMSUNG, 0x00, 0x00,  &samsung_lpddr4x_k4uce3q4ab_mgcl },
+};
+
+const struct nonspd_mem_info *lpddr_info_from_fdt_ids(void)
+{
+	static const struct nonspd_mem_info *lpddr_mem_info;
+
+	static bool ran_once;
+
+	uint8_t ram_manufacturer;
+	uint8_t ram_revision1;
+	uint8_t ram_revision2;
+
+	if (ran_once)
+		return lpddr_mem_info;
+
+	ran_once = true;
+
+	if (fdt_get_ram_manufacturer_id(&ram_manufacturer) < 0) {
+		lprintf(LOG_ERR,
+			"%s: Unable to obtain RAM manufacturer_id.\n",
+			__func__);
+		return lpddr_mem_info;
+	}
+	if (fdt_get_ram_revision_id(&ram_revision1, &ram_revision2) < 0) {
+		lprintf(LOG_ERR, "%s: Unable to obtain RAM revision_id.\n",
+			__func__);
+		return lpddr_mem_info;
+	}
+
+	for (int i = 0; i < ARRAY_SIZE(ddr_table); i++) {
+		if ((ddr_table[i].manufacturer == ram_manufacturer) &&
+			(ddr_table[i].revision1 == ram_revision1) &&
+			(ddr_table[i].revision2 == ram_revision2)) {
+			lpddr_mem_info = ddr_table[i].mem_info;
+			return lpddr_mem_info;
+		}
+	}
+	lprintf(LOG_ERR,
+		"%s: Unrecognized configuration: Manufacturer ID: %#02x Revision ID: %#02x %#02x\n",
+		__func__, ram_manufacturer, ram_revision1, ram_revision2);
+
+	return lpddr_mem_info;
+}
diff --git a/lib/spd/meson.build b/lib/spd/meson.build
index be77d09..9a32726 100644
--- a/lib/spd/meson.build
+++ b/lib/spd/meson.build
@@ -2,4 +2,5 @@
   'nonspd_modules.c',
   'nonspd.c',
   'jedec_id.c',
+  'lpddr_ids.c',
 )
diff --git a/lib/spd/nonspd_modules.c b/lib/spd/nonspd_modules.c
index bbd7cad..d833f4b 100644
--- a/lib/spd/nonspd_modules.c
+++ b/lib/spd/nonspd_modules.c
@@ -377,6 +377,19 @@
 	.part_num		= "MT53E2G32D4NQ-46WT:A",
 };
 
+const struct nonspd_mem_info micron_lpddr4x_mt53e2g32d4nq_046wtc = {
+	.dram_type		= SPD_DRAM_TYPE_LPDDR4X,
+
+	.module_size_mbits	= 65536,
+	.num_ranks		= 2,
+	.device_width		= 32,
+
+	.module_mfg_id		= MFGID_MICRON,
+	.dram_mfg_id		= MFGID_MICRON,
+
+	.part_num		= "MT53E2G32D4NQ-46WT:C",
+};
+
 const struct nonspd_mem_info micron_lpddr4x_mt53e512m32d1np_046wtb = {
 	.dram_type              = SPD_DRAM_TYPE_LPDDR4X,
 
@@ -598,6 +611,19 @@
 	.part_num		= "K4UCE3Q4AA-MGCR",
 };
 
+const struct nonspd_mem_info samsung_lpddr4x_k4uce3q4ab_mgcl = {
+	.dram_type		= SPD_DRAM_TYPE_LPDDR4X,
+
+	.module_size_mbits	= 65536,
+	.num_ranks		= 2,
+	.device_width		= 32,
+
+	.module_mfg_id		= MFGID_SAMSUNG,
+	.dram_mfg_id		= MFGID_SAMSUNG,
+
+	.part_num		= "K4UCE3Q4AB-MGCL",
+};
+
 const struct nonspd_mem_info sandisk_lpddr4x_sdada4cr_128g = {
 	.dram_type		= SPD_DRAM_TYPE_LPDDR4X,
 
diff --git a/platform/arm/herobrine/herobrine.c b/platform/arm/herobrine/herobrine.c
index a240531..ae00818 100644
--- a/platform/arm/herobrine/herobrine.c
+++ b/platform/arm/herobrine/herobrine.c
@@ -8,10 +8,12 @@
 #include "lib/sku.h"
 #include "mosys/command_list.h"
 #include "mosys/platform.h"
+#include "herobrine.h"
 
 /* TODO(b:177917361): Add &cmd_memory */
 static struct platform_cmd *herobrine_sub[] = {
 	&cmd_ec,
+	&cmd_memory,
 	&cmd_pd,
 	&cmd_platform,
 	NULL
@@ -19,13 +21,14 @@
 
 /* TODO(b:177917361): add .memory */
 static struct platform_cb herobrine_cb = {
-	.ec		= &cros_ec_cb,
-	.sys		= &fdt_sysinfo_cb,
+	.ec	= &cros_ec_cb,
+	.memory = &herobrine_memory_cb,
+	.sys	= &fdt_sysinfo_cb,
 };
 
 static struct platform_intf platform_herobrine = {
-	.type		= PLATFORM_ARMV8,
-	.sub		= herobrine_sub,
-	.cb		= &herobrine_cb,
+	.type	= PLATFORM_ARMV8,
+	.sub	= herobrine_sub,
+	.cb	= &herobrine_cb,
 };
 REGISTER_PLATFORM(platform_herobrine, "Herobrine");
diff --git a/platform/arm/herobrine/herobrine.h b/platform/arm/herobrine/herobrine.h
new file mode 100644
index 0000000..c12f6c4
--- /dev/null
+++ b/platform/arm/herobrine/herobrine.h
@@ -0,0 +1,14 @@
+/* Copyright 2022 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GOOGLE_HEROBRINE_H
+#define GOOGLE_HEROBRINE_H
+
+#include "mosys/platform.h"
+
+/* platform callbacks */
+extern struct memory_cb herobrine_memory_cb;
+
+#endif /* GOOGLE_HEROBRIBNE_H */
diff --git a/platform/arm/herobrine/memory.c b/platform/arm/herobrine/memory.c
new file mode 100644
index 0000000..e04e572
--- /dev/null
+++ b/platform/arm/herobrine/memory.c
@@ -0,0 +1,38 @@
+/* Copyright 2022 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "lib/fdt.h"
+#include "lib/lpddr_ids.h"
+#include "lib/nonspd.h"
+#include "mosys/platform.h"
+#include "herobrine.h"
+
+/*
+ * dimm_count  -  return total number of dimm slots
+ *
+ * @intf:       platform interface
+ *
+ * returns dimm slot count
+ */
+static int dimm_count(struct platform_intf *intf)
+{
+	return 1;
+}
+
+static int get_mem_info(struct platform_intf *intf, int dimm,
+			const struct nonspd_mem_info **info)
+{
+	*info = lpddr_info_from_fdt_ids();
+
+	if (*info)
+		return 0;
+	else
+		return -1;
+}
+
+struct memory_cb herobrine_memory_cb = {
+	.dimm_count = &dimm_count,
+	.nonspd_mem_info = &get_mem_info,
+};
diff --git a/platform/arm/herobrine/meson.build b/platform/arm/herobrine/meson.build
index 5021365..e532a57 100644
--- a/platform/arm/herobrine/meson.build
+++ b/platform/arm/herobrine/meson.build
@@ -1,3 +1,4 @@
 platform_support_src += files(
+  'memory.c',
   'herobrine.c',
 )