summaryrefslogtreecommitdiffstats
path: root/util
diff options
context:
space:
mode:
authorReka Norman <rekanorman@google.com>2021-09-08 18:50:58 +1000
committerFelix Held <felix-coreboot@felixheld.de>2021-09-17 14:49:22 +0000
commit0e79274d33ed701d320bd4cc3b7c26b97120557d (patch)
treeaca22f39037a9f973ff1b437ab03a8f19228bfae /util
parent20795899999bab47ba875b0d2a2c7e0a6ff4effa (diff)
downloadcoreboot-0e79274d33ed701d320bd4cc3b7c26b97120557d.tar.gz
coreboot-0e79274d33ed701d320bd4cc3b7c26b97120557d.tar.bz2
coreboot-0e79274d33ed701d320bd4cc3b7c26b97120557d.zip
util/spd_tools: Implement a unified version of the spd_gen tool
Currently there are two versions of spd_tools: one for LP4x and one for DDR4. This change is the first step in unifying these into a single tool. This change implements a unified version of the spd_gen tool, by combining the functionality currently in lp4x/gen_spd.go and ddr4/gen_spd.go. The unified version takes the memory technology as an argument, and generates SPD files for all platforms supporting that technology. BUG=b:191776301 TEST=Compare the SPDs generated by the old and new versions of the tool for all supported platforms. For reference, the test script used is here: https://review.coreboot.org/c/coreboot/+/57511 Signed-off-by: Reka Norman <rekanorman@google.com> Change-Id: I7fc036996dbafbb54e075da0c3ac2ea0886a6db2 Reviewed-on: https://review.coreboot.org/c/coreboot/+/57512 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Furquan Shaikh <furquan@google.com> Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org> Reviewed-by: Karthik Ramasubramanian <kramasub@google.com> Reviewed-by: Michael Niewöhner <foss@mniewoehner.de>
Diffstat (limited to 'util')
-rw-r--r--util/spd_tools/.gitignore1
-rw-r--r--util/spd_tools/Makefile11
-rw-r--r--util/spd_tools/src/spd_gen/ddr4.go1284
-rw-r--r--util/spd_tools/src/spd_gen/lp4x.go906
-rw-r--r--util/spd_tools/src/spd_gen/spd_gen.go281
5 files changed, 2483 insertions, 0 deletions
diff --git a/util/spd_tools/.gitignore b/util/spd_tools/.gitignore
index cafb8530d635..e0d159218458 100644
--- a/util/spd_tools/.gitignore
+++ b/util/spd_tools/.gitignore
@@ -1,2 +1,3 @@
*/gen_spd
*/gen_part_id
+bin
diff --git a/util/spd_tools/Makefile b/util/spd_tools/Makefile
new file mode 100644
index 000000000000..8a9d3924a11f
--- /dev/null
+++ b/util/spd_tools/Makefile
@@ -0,0 +1,11 @@
+SPD_GEN = bin/spd_gen
+
+all: $(SPD_GEN)
+
+$(SPD_GEN):
+ go build -o $(SPD_GEN) src/spd_gen/*.go
+
+clean:
+ rm -rf bin/
+
+.PHONY: all
diff --git a/util/spd_tools/src/spd_gen/ddr4.go b/util/spd_tools/src/spd_gen/ddr4.go
new file mode 100644
index 000000000000..ce60038b77fd
--- /dev/null
+++ b/util/spd_tools/src/spd_gen/ddr4.go
@@ -0,0 +1,1284 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+/* ------------------------------------------------------------------------------------------ */
+/* DDR4-defined types */
+/* ------------------------------------------------------------------------------------------ */
+
+type ddr4 struct {
+}
+
+type DDR4MemAttributes struct {
+ /* Primary attributes - must be provided by JSON file for each part */
+ SpeedMTps int
+ CL_nRCD_nRP int
+ CapacityPerDieGb int
+ DiesPerPackage int
+ PackageBusWidth int
+ RanksPerPackage int
+
+ /*
+ * All the following parameters are optional and required only if the part requires
+ * special parameters as per the datasheet.
+ */
+ /* Timing parameters */
+ TAAMinPs int
+ TRCDMinPs int
+ TRPMinPs int
+ TRASMinPs int
+ TRCMinPs int
+ TCKMinPs int
+ TCKMaxPs int
+ TRFC1MinPs int
+ TRFC2MinPs int
+ TRFC4MinPs int
+ TFAWMinPs int
+ TRRDLMinPs int
+ TRRDSMinPs int
+ TCCDLMinPs int
+ TWRMinPs int
+ TWTRLMinPs int
+ TWTRSMinPs int
+
+ /* CAS */
+ CASLatencies string
+ CASFirstByte byte
+ CASSecondByte byte
+ CASThirdByte byte
+ CASFourthByte byte
+
+ /* The following is for internal-use only and is not overridable */
+ dieBusWidth int
+}
+
+type DDR4SpeedBinAttributes struct {
+ TRASMinPs int
+ TCKMaxPs int
+}
+
+type DDR4SPDMemAttribFunc func(*DDR4MemAttributes) byte
+
+type DDR4SPDAttribTableEntry struct {
+ constVal byte
+ getVal DDR4SPDMemAttribFunc
+}
+
+/* ------------------------------------------------------------------------------------------ */
+/* Constants */
+/* ------------------------------------------------------------------------------------------ */
+
+const (
+ /* SPD Byte Index */
+ DDR4SPDIndexSize = 0
+ DDR4SPDIndexRevision = 1
+ DDR4SPDIndexMemoryType = 2
+ DDR4SPDIndexModuleType = 3
+ DDR4SPDIndexDensityBanks = 4
+ DDR4SPDIndexAddressing = 5
+ DDR4SPDIndexPackageType = 6
+ DDR4SPDIndexOptionalFeatures = 7
+ DDR4SPDIndexModuleOrganization = 12
+ DDR4SPDIndexBusWidth = 13
+ DDR4SPDIndexTimebases = 17
+ DDR4SPDIndexTCKMin = 18
+ DDR4SPDIndexTCKMax = 19
+ DDR4SPDIndexCASFirstByte = 20
+ DDR4SPDIndexCASSecondByte = 21
+ DDR4SPDIndexCASThirdByte = 22
+ DDR4SPDIndexCASFourthByte = 23
+ DDR4SPDIndexTAAMin = 24
+ DDR4SPDIndexTRCDMin = 25
+ DDR4SPDIndexTRPMin = 26
+ DDR4SPDIndexTRASRCMinMSNs = 27
+ DDR4SPDIndexTRASMinLsb = 28
+ DDR4SPDIndexTRCMinLsb = 29
+ DDR4SPDIndexTRFC1MinLsb = 30
+ DDR4SPDIndexTRFC1MinMsb = 31
+ DDR4SPDIndexTRFC2MinLsb = 32
+ DDR4SPDIndexTRFC2MinMsb = 33
+ DDR4SPDIndexTRFC4MinLsb = 34
+ DDR4SPDIndexTRFC4MinMsb = 35
+ DDR4SPDIndexTFAWMinMSN = 36
+ DDR4SPDIndexTFAWMinLsb = 37
+ DDR4SPDIndexTRRDSMin = 38
+ DDR4SPDIndexTRRDLMin = 39
+ DDR4SPDIndexTCCDLMin = 40
+ DDR4SPDIndexTWRMinMSN = 41
+ DDR4SPDIndexTWRMinLsb = 42
+ DDR4SPDIndexTWTRMinMSNs = 43
+ DDR4SPDIndexWTRSMinLsb = 44
+ DDR4SPDIndexWTRLMinLsb = 45
+ DDR4SPDIndexTCCDLMinFineOffset = 117
+ DDR4SPDIndexTRRDLMinFineOffset = 118
+ DDR4SPDIndexTRRDSMinFineOffset = 119
+ DDR4SPDIndexTRCMinFineOffset = 120
+ DDR4SPDIndexTRPMinFineOffset = 121
+ DDR4SPDIndexTRCDMinFineOffset = 122
+ DDR4SPDIndexTAAMinFineOffset = 123
+ DDR4SPDIndexTCKMaxFineOffset = 124
+ DDR4SPDIndexTCKMinFineOffset = 125
+ DDR4SPDIndexManufacturerPartNumberStartByte = 329
+ DDR4SPDIndexManufacturerPartNumberEndByte = 348
+
+ /* SPD Byte Value */
+
+ /*
+ * From JEDEC spec:
+ * 6:4 (Bytes total) = 2 (512 bytes)
+ * 3:0 (Bytes used) = 3 (384 bytes)
+ * Set to 0x23 for DDR4.
+ */
+ DDR4SPDValueSize = 0x23
+
+ /*
+ * From JEDEC spec: Revision 1.1
+ * Set to 0x11.
+ */
+ DDR4SPDValueRevision = 0x11
+
+ /* DDR4 memory type = 0x0C */
+ DDR4SPDValueMemoryType = 0x0C
+
+ /*
+ * From JEDEC spec:
+ * Module Type [0:3] :
+ * 0 = Undefined
+ * 1 = RDIMM (width = 133.35 mm nom)
+ * 2 = UDIMM (width = 133.35 mm nom)
+ * 3 = SO-DIMM (width = 68.60 mm nom)
+ * 4 = LRDIMM (width = 133.35 mm nom)
+ *
+ * DDR4 on TGL uses SO-DIMM type for for both memory down and DIMM config.
+ * Set to 0x03.
+ */
+ DDR4SPDValueModuleType = 0x03
+
+ /*
+ * From JEDEC spec:
+ * 5:4 (Maximum Activate Window) = 00 (8192 * tREFI)
+ * 3:0 (Maximum Activate Count) = 1000 (Unlimited MAC)
+ *
+ * Needs to come from datasheet, but most parts seem to support unlimited MAC.
+ * MR#24 OP3
+ */
+ DDR4SPDValueOptionalFeatures = 0x08
+
+ /*
+ * From JEDEC spec:
+ * 2:0 Primary Bus Width in Bits = 011 (x64 always)
+ * Set to 0x03.
+ */
+ DDR4SPDValueModuleBusWidth = 0x03
+
+ /*
+ * From JEDEC spec:
+ * 3:2 (MTB) = 00 (0.125ns)
+ * 1:0 (FTB) = 00 (1ps)
+ * Set to 0x00.
+ */
+ DDR4SPDValueTimebases = 0x00
+
+ /* CAS fourth byte: All bits are reserved */
+ DDR4SPDValueCASFourthByte = 0x00
+
+ /* As per JEDEC spec, unused digits of manufacturer part number are left as blank. */
+ DDR4SPDValueManufacturerPartNumberBlank = 0x20
+)
+
+const (
+ /* First Byte */
+ DDR4CAS9 = 1 << 2
+ DDR4CAS10 = 1 << 3
+ DDR4CAS11 = 1 << 4
+ DDR4CAS12 = 1 << 5
+ DDR4CAS13 = 1 << 6
+ DDR4CAS14 = 1 << 7
+ /* Second Byte */
+ DDR4CAS15 = 1 << 0
+ DDR4CAS16 = 1 << 1
+ DDR4CAS17 = 1 << 2
+ DDR4CAS18 = 1 << 3
+ DDR4CAS19 = 1 << 4
+ DDR4CAS20 = 1 << 5
+ DDR4CAS21 = 1 << 6
+ DDR4CAS22 = 1 << 7
+ /* Third Byte */
+ DDR4CAS24 = 1 << 1
+)
+
+const (
+ /*
+ * As per Table 75 of Jedec spec 4.1.20-L-5 R29 v103:
+ * tWRMin = 15nS for all DDR4 Speed Bins
+ * Set to 15000 pS
+ */
+ DDR4TimingValueTWRMinPs = 15000
+
+ /*
+ * As per Table 78 of Jedec spec 4.1.20-L-5 R29 v103:
+ * tWTR_SMin = 2.5nS for all DDR4 Speed Bins
+ * Set to 2500 pS
+ */
+ DDR4TimingValueTWTRSMinPs = 2500
+
+ /*
+ * As per Table 80 of Jedec spec 4.1.20-L-5 R29 v103:
+ * tWTR_LMin = 7.5 nS for all DDR4 Speed Bins
+ * Set to 7500 pS
+ */
+ DDR4TimingValueTWTRLMinPs = 7500
+)
+
+/* ------------------------------------------------------------------------------------------ */
+/* Global variables */
+/* ------------------------------------------------------------------------------------------ */
+
+var DDR4PlatformSetMap = map[int][]int{
+ 0: {PlatformTGL, PlatformPCO, PlatformPLK},
+}
+
+var DDR4PartAttributeMap = map[string]DDR4MemAttributes{}
+var DDR4CurrSet int
+
+/* This encodes the density in Gb to SPD low nibble value as per JESD 4.1.2.L-5 R29 */
+var DDR4DensityGbToSPDEncoding = map[int]byte{
+ 2: 0x3,
+ 4: 0x4,
+ 8: 0x5,
+ 16: 0x6,
+}
+
+/*
+ * Tables 4 thru Table 7 from JESD79-4C.
+ * Maps density per die to row-column encoding for a device with x8/x16
+ * physical channel.
+ */
+var DDR4DensityGbx8x16DieCapacityToRowColumnEncoding = map[int]byte{
+ 2: 0x11, /* 14 rows, 10 columns */
+ 4: 0x19, /* 15 rows, 10 columns */
+ 8: 0x21, /* 16 rows, 10 columns */
+ 16: 0x29, /* 17 rows, 10 columns */
+}
+
+/*
+ * Tables 169 & 170 in the JESD79-4C spec
+ * Maps die density to refresh timings. This is the same for x8 and x16
+ * devices.
+ */
+
+/* maps die density to rcf1 timing in pico seconds */
+var DDR4TRFC1Encoding = map[int]int{
+ 2: 160000,
+ 4: 260000,
+ 8: 350000,
+ 16: 550000,
+}
+
+/* maps die density to rcf2 timing in pico seconds */
+var DDR4TRFC2Encoding = map[int]int{
+ 2: 110000,
+ 4: 160000,
+ 8: 260000,
+ 16: 350000,
+}
+
+/* maps die density to rcf4 timing in pico seconds */
+var DDR4TRFC4Encoding = map[int]int{
+ 2: 90000,
+ 4: 110000,
+ 8: 160000,
+ 16: 260000,
+}
+
+var DDR4SpeedBinToSPDEncoding = map[int]DDR4SpeedBinAttributes{
+ 1600: {
+ TRASMinPs: 35000,
+ TCKMaxPs: 1500,
+ },
+ 1866: {
+ TRASMinPs: 34000,
+ TCKMaxPs: 1250,
+ },
+ 2133: {
+ TRASMinPs: 33000,
+ TCKMaxPs: 1071,
+ },
+ 2400: {
+ TRASMinPs: 32000,
+ TCKMaxPs: 937,
+ },
+ 2666: {
+ TRASMinPs: 32000,
+ TCKMaxPs: 833,
+ },
+ 2933: {
+ TRASMinPs: 32000,
+ TCKMaxPs: 750,
+ },
+ 3200: {
+ TRASMinPs: 32000,
+ TCKMaxPs: 682,
+ },
+}
+
+/* This takes memAttribs.PackageBusWidth as an index */
+var DDR4PageSizefromBusWidthEncoding = map[int]int{
+ 8: 1,
+ 16: 2,
+}
+
+var DDR4SpeedToTRRDSMinPsOneKPageSize = map[int]int{
+ 1600: 5000,
+ 1866: 4200,
+ 2133: 3700,
+ 2400: 3300,
+ 2666: 3000,
+ 2933: 2700,
+ 3200: 2500,
+}
+
+var DDR4SpeedToTRRDSMinPsTwoKPageSize = map[int]int{
+ 1600: 6000,
+ 1866: 5300,
+ 2133: 5300,
+ 2400: 5300,
+ 2666: 5300,
+ 2933: 5300,
+ 3200: 5300,
+}
+
+var DDR4SPDAttribTable = map[int]DDR4SPDAttribTableEntry{
+ DDR4SPDIndexSize: {constVal: DDR4SPDValueSize},
+ DDR4SPDIndexRevision: {constVal: DDR4SPDValueRevision},
+ DDR4SPDIndexMemoryType: {constVal: DDR4SPDValueMemoryType},
+ DDR4SPDIndexModuleType: {constVal: DDR4SPDValueModuleType},
+ DDR4SPDIndexDensityBanks: {getVal: DDR4EncodeDensityBanks},
+ DDR4SPDIndexAddressing: {getVal: DDR4EncodeSdramAddressing},
+ DDR4SPDIndexPackageType: {getVal: DDR4EncodePackageType},
+ DDR4SPDIndexOptionalFeatures: {constVal: DDR4SPDValueOptionalFeatures},
+ DDR4SPDIndexModuleOrganization: {getVal: DDR4EncodeModuleOrganization},
+ DDR4SPDIndexBusWidth: {constVal: DDR4SPDValueModuleBusWidth},
+ DDR4SPDIndexTimebases: {constVal: DDR4SPDValueTimebases},
+ DDR4SPDIndexTCKMin: {getVal: DDR4EncodeTCKMin},
+ DDR4SPDIndexTCKMinFineOffset: {getVal: DDR4EncodeTCKMinFineOffset},
+ DDR4SPDIndexTCKMax: {getVal: DDR4EncodeTCKMax},
+ DDR4SPDIndexTCKMaxFineOffset: {getVal: DDR4EncodeTCKMaxFineOffset},
+ DDR4SPDIndexCASFirstByte: {getVal: DDR4EncodeCASFirstByte},
+ DDR4SPDIndexCASSecondByte: {getVal: DDR4EncodeCASSecondByte},
+ DDR4SPDIndexCASThirdByte: {getVal: DDR4EncodeCASThirdByte},
+ DDR4SPDIndexCASFourthByte: {getVal: DDR4EncodeCASFourthByte},
+ DDR4SPDIndexTAAMin: {getVal: DDR4EncodeTAAMin},
+ DDR4SPDIndexTAAMinFineOffset: {getVal: DDR4EncodeTAAMinFineOffset},
+ DDR4SPDIndexTRCDMin: {getVal: DDR4EncodeTRCDMin},
+ DDR4SPDIndexTRCDMinFineOffset: {getVal: DDR4EncodeTRCDMinFineOffset},
+ DDR4SPDIndexTRPMin: {getVal: DDR4EncodeTRPMin},
+ DDR4SPDIndexTRPMinFineOffset: {getVal: DDR4EncodeTRPMinFineOffset},
+ DDR4SPDIndexTRASRCMinMSNs: {getVal: DDR4EncodeTRASRCMinMSNs},
+ DDR4SPDIndexTRASMinLsb: {getVal: DDR4EncodeTRASMinLsb},
+ DDR4SPDIndexTRCMinLsb: {getVal: DDR4EncodeTRCMinLsb},
+ DDR4SPDIndexTRCMinFineOffset: {getVal: DDR4EncodeTRCMinFineOffset},
+ DDR4SPDIndexTRFC1MinLsb: {getVal: DDR4EncodeTRFC1MinLsb},
+ DDR4SPDIndexTRFC1MinMsb: {getVal: DDR4EncodeTRFC1MinMsb},
+ DDR4SPDIndexTRFC2MinLsb: {getVal: DDR4EncodeTRFC2MinLsb},
+ DDR4SPDIndexTRFC2MinMsb: {getVal: DDR4EncodeTRFC2MinMsb},
+ DDR4SPDIndexTRFC4MinLsb: {getVal: DDR4EncodeTRFC4MinLsb},
+ DDR4SPDIndexTRFC4MinMsb: {getVal: DDR4EncodeTRFC4MinMsb},
+ DDR4SPDIndexTFAWMinMSN: {getVal: DDR4EncodeTFAWMinMSN},
+ DDR4SPDIndexTFAWMinLsb: {getVal: DDR4EncodeTFAWMinLsb},
+ DDR4SPDIndexTRRDSMin: {getVal: DDR4EncodeTRRDSMin},
+ DDR4SPDIndexTRRDSMinFineOffset: {getVal: DDR4EncodeTRRDSMinFineOffset},
+ DDR4SPDIndexTRRDLMin: {getVal: DDR4EncodeTRRDLMin},
+ DDR4SPDIndexTRRDLMinFineOffset: {getVal: DDR4EncodeTRRDLMinFineOffset},
+ DDR4SPDIndexTCCDLMin: {getVal: DDR4EncodeTCCDLMin},
+ DDR4SPDIndexTCCDLMinFineOffset: {getVal: DDR4EncodeTCCDLMinFineOffset},
+ DDR4SPDIndexTWRMinMSN: {getVal: DDR4EncodeTWRMinMSN},
+ DDR4SPDIndexTWRMinLsb: {getVal: DDR4EncodeTWRMinLsb},
+ DDR4SPDIndexTWTRMinMSNs: {getVal: DDR4EncodeTWTRMinMSNs},
+ DDR4SPDIndexWTRSMinLsb: {getVal: DDR4EncodeTWTRSMinLsb},
+ DDR4SPDIndexWTRLMinLsb: {getVal: DDR4EncodeTWTRLMinLsb},
+}
+
+/* ------------------------------------------------------------------------------------------ */
+/* Functions */
+/* ------------------------------------------------------------------------------------------ */
+
+func DDR4GetBankGroups(memAttribs *DDR4MemAttributes) byte {
+ var bg byte
+
+ switch memAttribs.PackageBusWidth {
+ case 8:
+ bg = 4
+ case 16:
+ if memAttribs.DiesPerPackage == 1 {
+ bg = 2 /* x16 SDP has 2 bank groups */
+ } else {
+ bg = 4 /* x16 DDP has 4 bank groups */
+ }
+ }
+
+ return bg
+}
+
+func DDR4EncodeBankGroups(bg byte) byte {
+ var val byte
+
+ switch bg {
+ case 2:
+ val = 1
+ case 4:
+ val = 2
+ }
+
+ return val << 6
+}
+
+func DDR4EncodeDensityBanks(memAttribs *DDR4MemAttributes) byte {
+ var b byte
+
+ b = DDR4DensityGbToSPDEncoding[memAttribs.CapacityPerDieGb]
+ b |= DDR4EncodeBankGroups(DDR4GetBankGroups(memAttribs))
+ /* No need to encode banksPerGroup.it's always 4 ([4:5] = 0) */
+
+ return b
+}
+
+func DDR4EncodeSdramAddressing(memAttribs *DDR4MemAttributes) byte {
+ var b byte
+
+ b = DDR4DensityGbx8x16DieCapacityToRowColumnEncoding[memAttribs.CapacityPerDieGb]
+
+ return b
+}
+
+func DDR4EncodePackageDeviceType(dies int) byte {
+ var b byte
+
+ if dies > 1 {
+ /* If more than one die, then this is a non-monolithic device. */
+ b = 1
+ } else {
+ /* If only single die, then this is a monolithic device. */
+ b = 0
+ }
+
+ return b << 7
+}
+
+func DDR4EncodeSignalLoadingFromDieCount(dies int) byte {
+ var loading byte
+
+ /*
+ * If die count = 1, signal loading = "not specified" = 0
+ * If die count > 1, signal loading = "multi" = 2
+ */
+ if dies == 1 {
+ loading = 0
+ } else {
+ loading = 1
+ }
+
+ return loading
+}
+
+func DDR4EncodeDiesPerPackage(dies int) byte {
+ var b byte
+
+ b = DDR4EncodePackageDeviceType(dies) /* Monolithic / Non-monolithic device */
+ b |= (byte(dies) - 1) << 4
+
+ return b
+}
+
+func DDR4EncodePackageType(memAttribs *DDR4MemAttributes) byte {
+ var b byte
+
+ b = DDR4EncodeDiesPerPackage(memAttribs.DiesPerPackage)
+ b |= DDR4EncodeSignalLoadingFromDieCount(memAttribs.DiesPerPackage)
+
+ return b
+}
+
+func DDR4EncodeDataWidth(bitWidthPerDevice int) byte {
+ var width byte
+
+ switch bitWidthPerDevice {
+ case 8:
+ width = 1
+ case 16:
+ width = 2
+ }
+
+ return width
+}
+
+func DDR4EncodeRanks(ranks int) byte {
+ var b byte
+
+ b = byte(ranks - 1)
+
+ return b << 3
+}
+
+func DDR4EncodeModuleOrganization(memAttribs *DDR4MemAttributes) byte {
+ var b byte
+
+ b = DDR4EncodeDataWidth(memAttribs.dieBusWidth)
+ b |= DDR4EncodeRanks(memAttribs.RanksPerPackage)
+
+ return b
+}
+
+func DDR4EncodeTCKMin(memAttribs *DDR4MemAttributes) byte {
+ return convPsToMtbByte(memAttribs.TCKMinPs)
+}
+
+func DDR4EncodeTCKMinFineOffset(memAttribs *DDR4MemAttributes) byte {
+ return convPsToFtbByte(memAttribs.TCKMinPs)
+}
+
+func DDR4EncodeTCKMax(memAttribs *DDR4MemAttributes) byte {
+ return convPsToMtbByte(memAttribs.TCKMaxPs)
+}
+
+func DDR4EncodeTCKMaxFineOffset(memAttribs *DDR4MemAttributes) byte {
+ return convPsToFtbByte(memAttribs.TCKMaxPs)
+}
+
+func DDR4EncodeTAAMin(memAttribs *DDR4MemAttributes) byte {
+ return convPsToMtbByte(memAttribs.TAAMinPs)
+}
+
+func DDR4EncodeTAAMinFineOffset(memAttribs *DDR4MemAttributes) byte {
+ return convPsToFtbByte(memAttribs.TAAMinPs)
+}
+
+func DDR4EncodeTRCDMin(memAttribs *DDR4MemAttributes) byte {
+ return convPsToMtbByte(memAttribs.TRCDMinPs)
+}
+
+func DDR4EncodeTRCDMinFineOffset(memAttribs *DDR4MemAttributes) byte {
+ return convPsToFtbByte(memAttribs.TRCDMinPs)
+}
+
+func DDR4EncodeTRPMin(memAttribs *DDR4MemAttributes) byte {
+ return convPsToMtbByte(memAttribs.TRPMinPs)
+}
+
+func DDR4EncodeTRCMinFineOffset(memAttribs *DDR4MemAttributes) byte {
+ return convPsToFtbByte(memAttribs.TRCMinPs)
+}
+
+func DDR4EncodeTRPMinFineOffset(memAttribs *DDR4MemAttributes) byte {
+ return convPsToFtbByte(memAttribs.TRPMinPs)
+}
+
+func DDR4EncodeTRASRCMinMSNs(memAttribs *DDR4MemAttributes) byte {
+ var b byte
+
+ b = byte((convPsToMtb(memAttribs.TRASMinPs) >> 4) & 0xf0)
+ b |= byte((convPsToMtb(memAttribs.TRCMinPs) >> 8) & 0x0f)
+
+ return b
+}
+
+func DDR4EncodeTRASMinLsb(memAttribs *DDR4MemAttributes) byte {
+ return byte(convPsToMtb(memAttribs.TRASMinPs) & 0xff)
+}
+
+func DDR4EncodeTRCMinLsb(memAttribs *DDR4MemAttributes) byte {
+ return byte(convPsToMtb(memAttribs.TRCMinPs) & 0xff)
+}
+
+func DDR4EncodeTRFC1MinLsb(memAttribs *DDR4MemAttributes) byte {
+ var mtb int
+
+ mtb = convPsToMtb(memAttribs.TRFC1MinPs)
+
+ return byte(mtb & 0xff)
+}
+
+func DDR4EncodeTRFC1MinMsb(memAttribs *DDR4MemAttributes) byte {
+ var mtb int
+
+ mtb = convPsToMtb(memAttribs.TRFC1MinPs)
+
+ return byte((mtb >> 8) & 0xff)
+}
+
+func DDR4EncodeTRFC2MinLsb(memAttribs *DDR4MemAttributes) byte {
+ var mtb int
+
+ mtb = convPsToMtb(memAttribs.TRFC2MinPs)
+
+ return byte(mtb & 0xff)
+}
+
+func DDR4EncodeTRFC2MinMsb(memAttribs *DDR4MemAttributes) byte {
+ var mtb int
+
+ mtb = convPsToMtb(memAttribs.TRFC2MinPs)
+
+ return byte((mtb >> 8) & 0xff)
+}
+
+func DDR4EncodeTRFC4MinLsb(memAttribs *DDR4MemAttributes) byte {
+ var mtb int
+
+ mtb = convPsToMtb(memAttribs.TRFC4MinPs)
+
+ return byte(mtb & 0xff)
+}
+
+func DDR4EncodeTRFC4MinMsb(memAttribs *DDR4MemAttributes) byte {
+ var mtb int
+
+ mtb = convPsToMtb(memAttribs.TRFC4MinPs)
+
+ return byte((mtb >> 8) & 0xff)
+}
+
+func DDR4EncodeTFAWMinMSN(memAttribs *DDR4MemAttributes) byte {
+ var mtb int
+
+ mtb = convPsToMtb(memAttribs.TFAWMinPs)
+
+ return byte((mtb >> 8) & 0x0f)
+}
+
+func DDR4EncodeTFAWMinLsb(memAttribs *DDR4MemAttributes) byte {
+ var mtb int
+
+ mtb = convPsToMtb(memAttribs.TFAWMinPs)
+
+ return byte(mtb & 0xff)
+}
+
+func DDR4EncodeCASFirstByte(memAttribs *DDR4MemAttributes) byte {
+ return memAttribs.CASFirstByte
+}
+
+func DDR4EncodeCASSecondByte(memAttribs *DDR4MemAttributes) byte {
+ return memAttribs.CASSecondByte
+}
+
+func DDR4EncodeCASThirdByte(memAttribs *DDR4MemAttributes) byte {
+ return memAttribs.CASThirdByte
+}
+
+func DDR4EncodeCASFourthByte(memAttribs *DDR4MemAttributes) byte {
+ return memAttribs.CASFourthByte
+}
+
+func DDR4EncodeTRRDSMin(memAttribs *DDR4MemAttributes) byte {
+ return convPsToMtbByte(memAttribs.TRRDSMinPs)
+}
+
+func DDR4EncodeTRRDSMinFineOffset(memAttribs *DDR4MemAttributes) byte {
+ return convPsToFtbByte(memAttribs.TRRDSMinPs)
+}
+
+func DDR4EncodeTRRDLMin(memAttribs *DDR4MemAttributes) byte {
+ return convPsToMtbByte(memAttribs.TRRDLMinPs)
+}
+
+func DDR4EncodeTRRDLMinFineOffset(memAttribs *DDR4MemAttributes) byte {
+ return convPsToFtbByte(memAttribs.TRRDLMinPs)
+}
+
+func DDR4EncodeTCCDLMin(memAttribs *DDR4MemAttributes) byte {
+ return convPsToMtbByte(memAttribs.TCCDLMinPs)
+}
+
+func DDR4EncodeTCCDLMinFineOffset(memAttribs *DDR4MemAttributes) byte {
+ return convPsToFtbByte(memAttribs.TCCDLMinPs)
+}
+
+func DDR4EncodeTWRMinMSN(memAttribs *DDR4MemAttributes) byte {
+ return byte((convPsToMtb(DDR4TimingValueTWRMinPs) >> 8) & 0x0f)
+}
+
+func DDR4EncodeTWRMinLsb(memAttribs *DDR4MemAttributes) byte {
+ return byte(convPsToMtb(DDR4TimingValueTWRMinPs) & 0xff)
+}
+
+func DDR4EncodeTWTRMinMSNs(memAttribs *DDR4MemAttributes) byte {
+ var b byte
+
+ b = byte((convPsToMtb(memAttribs.TWTRLMinPs) >> 4) & 0xf0)
+ b |= byte((convPsToMtb(memAttribs.TWTRSMinPs) >> 8) & 0x0f)
+
+ return b
+}
+
+func DDR4EncodeTWTRSMinLsb(memAttribs *DDR4MemAttributes) byte {
+ return byte(convPsToMtb(memAttribs.TWTRSMinPs) & 0xff)
+}
+
+func DDR4EncodeTWTRLMinLsb(memAttribs *DDR4MemAttributes) byte {
+ return byte(convPsToMtb(memAttribs.TWTRLMinPs) & 0xff)
+}
+
+func DDR4EncodeLatencies(latency int, memAttribs *DDR4MemAttributes) error {
+ switch latency {
+ case 9:
+ memAttribs.CASFirstByte |= DDR4CAS9
+ case 10:
+ memAttribs.CASFirstByte |= DDR4CAS10
+ case 11:
+ memAttribs.CASFirstByte |= DDR4CAS11
+ case 12:
+ memAttribs.CASFirstByte |= DDR4CAS12
+ case 13:
+ memAttribs.CASFirstByte |= DDR4CAS13
+ case 14:
+ memAttribs.CASFirstByte |= DDR4CAS14
+ case 15:
+ memAttribs.CASSecondByte |= DDR4CAS15
+ case 16:
+ memAttribs.CASSecondByte |= DDR4CAS16
+ case 17:
+ memAttribs.CASSecondByte |= DDR4CAS17
+ case 18:
+ memAttribs.CASSecondByte |= DDR4CAS18
+ case 19:
+ memAttribs.CASSecondByte |= DDR4CAS19
+ case 20:
+ memAttribs.CASSecondByte |= DDR4CAS20
+ case 21:
+ memAttribs.CASSecondByte |= DDR4CAS21
+ case 22:
+ memAttribs.CASSecondByte |= DDR4CAS22
+ case 24:
+ memAttribs.CASThirdByte |= DDR4CAS24
+ default:
+ fmt.Errorf("Incorrect CAS Latency: ", latency)
+ }
+
+ return nil
+}
+
+/* Default CAS Latencies from Speed Bin tables in JEDS79-4C */
+func DDR4GetDefaultCASLatencies(memAttribs *DDR4MemAttributes) string {
+ var str string
+
+ switch memAttribs.SpeedMTps {
+ case 1600:
+ switch memAttribs.CL_nRCD_nRP {
+ case 10:
+ str = "9 10 11 12"
+ case 11:
+ str = "9 11 12"
+ case 12:
+ str = "10 12"
+ }
+ case 1866:
+ switch memAttribs.CL_nRCD_nRP {
+ case 12:
+ str = "9 10 12 13 14"
+ case 13:
+ str = "9 11 12 13 14"
+ case 14:
+ str = "10 12 14"
+ }
+ case 2133:
+ switch memAttribs.CL_nRCD_nRP {
+ case 14:
+ str = "9 10 12 14 15 16"
+ case 15:
+ str = "9 11 12 13 14 15 16"
+ case 16:
+ str = "10 12 14 16"
+ }
+ case 2400:
+ switch memAttribs.CL_nRCD_nRP {
+ case 15:
+ str = "9 10 12 14 15 16 17 18"
+ case 16:
+ str = "9 11 12 13 14 15 16 17 18"
+ case 17:
+ str = "10 11 12 13 14 15 16 17 18"
+ case 18:
+ str = "10 12 14 16 18"
+ }
+ case 2666:
+ switch memAttribs.CL_nRCD_nRP {
+ case 17:
+ str = "9 10 11 12 13 14 15 16 17 18 19 20"
+ case 18:
+ str = "9 10 11 12 13 14 15 16 17 18 19 20"
+ case 19:
+ str = "10 11 12 13 14 15 16 17 18 19 20"
+ case 20:
+ str = "10 12 14 16 18 20"
+ }
+ case 2933:
+ switch memAttribs.CL_nRCD_nRP {
+ case 19:
+ str = "9 10 11 12 13 14 15 16 17 18 19 20 21 22"
+ case 20:
+ str = "10 11 12 13 14 15 16 17 18 19 20 21 22"
+ case 21:
+ str = "10 11 12 13 14 15 16 17 18 19 20 21 22"
+ case 22:
+ str = "10 12 14 16 18 20 22"
+ }
+ case 3200:
+ switch memAttribs.CL_nRCD_nRP {
+ case 20:
+ str = "9 10 11 12 13 14 15 16 17 18 19 20 21 22 24"
+ case 22:
+ str = "10 11 12 13 14 15 16 17 18 19 20 21 22 24"
+ case 24:
+ str = "10 12 14 16 18 20 22 24"
+ }
+ }
+
+ return str
+}
+
+func DDR4UpdateDieBusWidth(memAttribs *DDR4MemAttributes) {
+ if memAttribs.PackageBusWidth == 16 && memAttribs.RanksPerPackage == 1 &&
+ memAttribs.DiesPerPackage == 2 {
+ /*
+ * If a x16 part has 2 die with single rank, PackageBusWidth
+ * needs to be converted to match die bus width.
+ */
+ memAttribs.dieBusWidth = 8
+ } else {
+ memAttribs.dieBusWidth = memAttribs.PackageBusWidth
+ }
+}
+
+func DDR4UpdateCAS(memAttribs *DDR4MemAttributes) error {
+ if len(memAttribs.CASLatencies) == 0 {
+ memAttribs.CASLatencies = DDR4GetDefaultCASLatencies(memAttribs)
+ }
+
+ latencies := strings.Fields(memAttribs.CASLatencies)
+ for i := 0; i < len(latencies); i++ {
+ latency, err := strconv.Atoi(latencies[i])
+ if err != nil {
+ return fmt.Errorf("Unable to convert latency ", latencies[i])
+ }
+ if err := DDR4EncodeLatencies(latency, memAttribs); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func DDR4GetTAAMinPs(memAttribs *DDR4MemAttributes) int {
+ return (memAttribs.CL_nRCD_nRP * 2000000) / memAttribs.SpeedMTps
+}
+
+func DDR4UpdateTAAMin(memAttribs *DDR4MemAttributes) {
+ if memAttribs.TAAMinPs == 0 {
+ memAttribs.TAAMinPs = DDR4GetTAAMinPs(memAttribs)
+ }
+}
+
+func DDR4UpdateTRCDMin(memAttribs *DDR4MemAttributes) {
+ /* tRCDmin is same as tAAmin for all cases */
+ if memAttribs.TRCDMinPs == 0 {
+ memAttribs.TRCDMinPs = DDR4GetTAAMinPs(memAttribs)
+ }
+}
+
+func DDR4UpdateTRPMin(memAttribs *DDR4MemAttributes) {
+ /* tRPmin is same as tAAmin for all cases */
+ if memAttribs.TRPMinPs == 0 {
+ memAttribs.TRPMinPs = DDR4GetTAAMinPs(memAttribs)
+ }
+}
+
+func DDR4UpdateTRASMin(memAttribs *DDR4MemAttributes) {
+ if memAttribs.TRASMinPs == 0 {
+ memAttribs.TRASMinPs = DDR4SpeedBinToSPDEncoding[memAttribs.SpeedMTps].TRASMinPs
+ }
+}
+
+func DDR4GetTRCMinPs(memAttribs *DDR4MemAttributes) int {
+ return memAttribs.TAAMinPs + memAttribs.TRASMinPs
+}
+
+func DDR4UpdateTRCMin(memAttribs *DDR4MemAttributes) {
+ if memAttribs.TRCMinPs == 0 {
+ memAttribs.TRCMinPs = DDR4GetTRCMinPs(memAttribs)
+ }
+}
+
+func DDR4GetDefaultTCKMinPs(memAttribs *DDR4MemAttributes) int {
+ /* value 2000000 = 2 * 1000000, where 1000000 is to convert mS to pS */
+ return 2000000 / memAttribs.SpeedMTps
+}
+
+func DDR4UpdateTCK(memAttribs *DDR4MemAttributes) {
+ if memAttribs.TCKMinPs == 0 {
+ memAttribs.TCKMinPs = DDR4GetDefaultTCKMinPs(memAttribs)
+ }
+ if memAttribs.TCKMaxPs == 0 {
+ memAttribs.TCKMaxPs = DDR4SpeedBinToSPDEncoding[memAttribs.SpeedMTps].TCKMaxPs
+ }
+}
+
+func DDR4UpdateTWRMin(memAttribs *DDR4MemAttributes) {
+ if memAttribs.TWRMinPs == 0 {
+ memAttribs.TWRMinPs = DDR4TimingValueTWRMinPs
+ }
+}
+
+func DDR4UpdateTWTRMin(memAttribs *DDR4MemAttributes) {
+ if memAttribs.TWTRLMinPs == 0 {
+ memAttribs.TWTRLMinPs = DDR4TimingValueTWTRLMinPs
+ }
+ if memAttribs.TWTRSMinPs == 0 {
+ memAttribs.TWTRSMinPs = DDR4TimingValueTWTRSMinPs
+ }
+}
+
+/*
+ * Per Table 169 & Table 170 of Jedec JESD79-4C
+ * tFAW timing is based on :
+ * Speed bin and page size
+ */
+func DDR4GetTFAWMinPs(memAttribs *DDR4MemAttributes) int {
+ var tFAWFixed int
+
+ if DDR4PageSizefromBusWidthEncoding[memAttribs.PackageBusWidth] == 1 {
+ switch memAttribs.SpeedMTps {
+ case 1600:
+ tFAWFixed = 25000
+ case 1866:
+ tFAWFixed = 23000
+ default:
+ tFAWFixed = 21000
+ }
+ } else if DDR4PageSizefromBusWidthEncoding[memAttribs.PackageBusWidth] == 2 {
+ switch memAttribs.SpeedMTps {
+ case 1600:
+ tFAWFixed = 35000
+ default:
+ tFAWFixed = 30000
+ }
+ }
+
+ return tFAWFixed
+}
+
+/* Update settings based on data sheet (json) supplied memory attributes */
+
+func DDR4UpdateTFAWMin(memAttribs *DDR4MemAttributes) {
+ var tFAWFromTck int
+
+ if memAttribs.TFAWMinPs == 0 {
+ memAttribs.TFAWMinPs = DDR4GetTFAWMinPs(memAttribs)
+ }
+
+ switch DDR4PageSizefromBusWidthEncoding[memAttribs.PackageBusWidth] {
+ case 1:
+ tFAWFromTck = 20 * memAttribs.TCKMinPs
+ case 2:
+ tFAWFromTck = 28 * memAttribs.TCKMinPs
+ }
+
+ if memAttribs.TFAWMinPs < tFAWFromTck {
+ memAttribs.TFAWMinPs = tFAWFromTck
+ }
+}
+
+func DDR4UpdateTRFC1Min(memAttribs *DDR4MemAttributes) {
+ if memAttribs.TRFC1MinPs == 0 {
+ memAttribs.TRFC1MinPs = DDR4TRFC1Encoding[memAttribs.CapacityPerDieGb]
+ }
+}
+
+func DDR4UpdateTRFC2Min(memAttribs *DDR4MemAttributes) {
+ if memAttribs.TRFC2MinPs == 0 {
+ memAttribs.TRFC2MinPs = DDR4TRFC2Encoding[memAttribs.CapacityPerDieGb]
+ }
+}
+
+func DDR4UpdateTRFC4Min(memAttribs *DDR4MemAttributes) {
+ if memAttribs.TRFC4MinPs == 0 {
+ memAttribs.TRFC4MinPs = DDR4TRFC4Encoding[memAttribs.CapacityPerDieGb]
+ }
+}
+
+func DDR4GetTRRDLMinPs(memAttribs *DDR4MemAttributes) int {
+ var tRRDLFixed int
+
+ /*
+ * Per JESD79-4C Tables 169 & 170, tRRD_L is based on :
+ * Speed bin and page size
+ */
+ switch DDR4PageSizefromBusWidthEncoding[memAttribs.PackageBusWidth] {
+ case 1:
+ switch memAttribs.SpeedMTps {
+ case 1600:
+ tRRDLFixed = 6000
+ default:
+ tRRDLFixed = 5300
+ }
+ case 2:
+ switch memAttribs.SpeedMTps {
+ case 1600:
+ tRRDLFixed = 7500
+ default:
+ tRRDLFixed = 6400
+ }
+ }
+
+ return tRRDLFixed
+}
+
+func DDR4UpdateTRRDLMin(memAttribs *DDR4MemAttributes) {
+ var tRRDLFromTck int
+
+ if memAttribs.TRRDLMinPs == 0 {
+ memAttribs.TRRDLMinPs = DDR4GetTRRDLMinPs(memAttribs)
+ }
+
+ tRRDLFromTck = 4 * memAttribs.TCKMinPs
+
+ if memAttribs.TRRDLMinPs < tRRDLFromTck {
+ memAttribs.TRRDLMinPs = tRRDLFromTck
+ }
+}
+
+func DDR4GetTRRDSMinPs(memAttribs *DDR4MemAttributes) int {
+ var tRRDFixed int
+
+ switch DDR4PageSizefromBusWidthEncoding[memAttribs.PackageBusWidth] {
+ case 1:
+ tRRDFixed = DDR4SpeedToTRRDSMinPsOneKPageSize[memAttribs.SpeedMTps]
+ case 2:
+ tRRDFixed = DDR4SpeedToTRRDSMinPsTwoKPageSize[memAttribs.SpeedMTps]
+ }
+
+ return tRRDFixed
+}
+
+func DDR4UpdateTRRDSMin(memAttribs *DDR4MemAttributes) {
+ var tRRDFromTck int
+
+ if memAttribs.TRRDSMinPs == 0 {
+ memAttribs.TRRDSMinPs = DDR4GetTRRDSMinPs(memAttribs)
+ }
+
+ tRRDFromTck = 4 * memAttribs.TCKMinPs
+
+ if memAttribs.TRRDSMinPs < tRRDFromTck {
+ memAttribs.TRRDSMinPs = tRRDFromTck
+ }
+}
+
+/*
+ * Per JESD79-4C Tables 169 and 170,
+ * tCCD_L is based on :
+ * Speed Bin
+ */
+func DDR4GetTCCDLMinPs(memAttribs *DDR4MemAttributes) int {
+ var tCCDLFixed int
+
+ switch memAttribs.SpeedMTps {
+ case 1600:
+ tCCDLFixed = 6250
+ case 1866:
+ tCCDLFixed = 5355
+ case 2133:
+ tCCDLFixed = 5355
+ default:
+ tCCDLFixed = 5000
+ }
+
+ return tCCDLFixed
+}
+
+func DDR4UpdateTCCDLMin(memAttribs *DDR4MemAttributes) {
+ var tCCDLFromTck int
+
+ if memAttribs.TCCDLMinPs == 0 {
+ memAttribs.TCCDLMinPs = DDR4GetTCCDLMinPs(memAttribs)
+ }
+
+ tCCDLFromTck = 5 * memAttribs.TCKMinPs
+
+ if memAttribs.TCCDLMinPs < tCCDLFromTck {
+ memAttribs.TCCDLMinPs = tCCDLFromTck
+ }
+}
+
+func DDR4UpdateMemoryAttributes(memAttribs *DDR4MemAttributes) {
+ DDR4UpdateDieBusWidth(memAttribs)
+ DDR4UpdateTCK(memAttribs)
+ DDR4UpdateTAAMin(memAttribs)
+ DDR4UpdateTRCDMin(memAttribs)
+ DDR4UpdateTRPMin(memAttribs)
+ DDR4UpdateTRASMin(memAttribs)
+ DDR4UpdateTRCMin(memAttribs)
+ DDR4UpdateTWRMin(memAttribs)
+ DDR4UpdateTWTRMin(memAttribs)
+ DDR4UpdateCAS(memAttribs)
+ DDR4UpdateTRFC1Min(memAttribs)
+ DDR4UpdateTRFC2Min(memAttribs)
+ DDR4UpdateTRFC4Min(memAttribs)
+ DDR4UpdateTCCDLMin(memAttribs)
+ DDR4UpdateTRRDSMin(memAttribs)
+ DDR4UpdateTRRDLMin(memAttribs)
+ DDR4UpdateTFAWMin(memAttribs)
+}
+
+func DDR4ValidateSpeedMTps(speedBin int) error {
+ if _, ok := DDR4SpeedBinToSPDEncoding[speedBin]; ok == false {
+ return fmt.Errorf("Incorrect speed bin: DDR4-%d", speedBin)
+ }
+ return nil
+}
+
+func DDR4ValidateCapacityPerDie(capacityPerDieGb int) error {
+ if _, ok := DDR4DensityGbToSPDEncoding[capacityPerDieGb]; ok == false {
+ return fmt.Errorf("Incorrect capacity per die: %d", capacityPerDieGb)
+ }
+ return nil
+}
+
+func DDR4ValidateDiesPerPackage(dieCount int) error {
+ if dieCount >= 1 && dieCount <= 2 {
+ return nil
+ }
+ return fmt.Errorf("Incorrect dies per package count: %d", dieCount)
+}
+
+func DDR4ValidatePackageBusWidth(width int) error {
+ if width != 8 && width != 16 {
+ return fmt.Errorf("Incorrect device bus width: %d", width)
+ }
+ return nil
+}
+
+func DDR4ValidateRanksPerPackage(ranks int) error {
+ if ranks >= 1 && ranks <= 2 {
+ return nil
+ }
+ return fmt.Errorf("Incorrect package ranks: %d", ranks)
+}
+
+func DDR4ValidateCASLatency(CL int) error {
+ if CL >= 10 && CL <= 24 && CL != 23 {
+ return nil
+ }
+ return fmt.Errorf("Incorrect CAS latency: %d", CL)
+}
+
+func DDR4VerifySupportedCASLatencies(name string, memAttribs *DDR4MemAttributes) error {
+ if memAttribs.CASLatencies == DDR4GetDefaultCASLatencies(memAttribs) {
+ return fmt.Errorf("CASLatencies for %s already matches default,\nPlease remove CASLatencies override line from the %s part attributes in the global part list and regenerate SPD Manifest", name, name)
+ }
+
+ return nil
+}
+
+func DDR4ValidateMemPartAttributes(name string, memAttribs *DDR4MemAttributes) error {
+ if err := DDR4ValidateSpeedMTps(memAttribs.SpeedMTps); err != nil {
+ return err
+ }
+ if err := DDR4ValidateCapacityPerDie(memAttribs.CapacityPerDieGb); err != nil {
+ return err
+ }
+ if err := DDR4ValidateDiesPerPackage(memAttribs.DiesPerPackage); err != nil {
+ return err
+ }
+ if err := DDR4ValidatePackageBusWidth(memAttribs.PackageBusWidth); err != nil {
+ return err
+ }
+ if err := DDR4ValidateRanksPerPackage(memAttribs.RanksPerPackage); err != nil {
+ return err
+ }
+ if err := DDR4ValidateCASLatency(memAttribs.CL_nRCD_nRP); err != nil {
+ return err
+ }
+
+ /* If CAS Latency was supplied, make sure it doesn't match default value */
+ if len(memAttribs.CASLatencies) != 0 {
+ if err := DDR4VerifySupportedCASLatencies(name, memAttribs); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func DDR4IsManufacturerPartNumberByte(index int) bool {
+ if index >= DDR4SPDIndexManufacturerPartNumberStartByte && index <= DDR4SPDIndexManufacturerPartNumberEndByte {
+ return true
+ }
+ return false
+}
+
+/* ------------------------------------------------------------------------------------------ */
+/* Interface Functions */
+/* ------------------------------------------------------------------------------------------ */
+
+func (ddr4) getSetMap() map[int][]int {
+ return DDR4PlatformSetMap
+}
+
+func (ddr4) addNewPart(name string, attribs interface{}) error {
+ var ddr4MemAttribs DDR4MemAttributes
+ eByte, err := json.Marshal(attribs)
+ if err != nil {
+ return err
+ }
+
+ if err := json.Unmarshal(eByte, &ddr4MemAttribs); err != nil {
+ return err
+ }
+
+ if err := DDR4ValidateMemPartAttributes(name, &ddr4MemAttribs); err != nil {
+ return err
+ }
+
+ DDR4PartAttributeMap[name] = ddr4MemAttribs
+ return nil
+}
+
+func (ddr4) getSPDAttribs(name string, set int) (interface{}, error) {
+ ddr4Attributes := DDR4PartAttributeMap[name]
+
+ DDR4CurrSet = set
+
+ DDR4UpdateMemoryAttributes(&ddr4Attributes)
+
+ return ddr4Attributes, nil
+}
+
+func (ddr4) getSPDLen() int {
+ return 512
+}
+
+func (ddr4) getSPDByte(index int, attribs interface{}) byte {
+ e, ok := DDR4SPDAttribTable[index]
+ if ok == false {
+ if DDR4IsManufacturerPartNumberByte(index) {
+ return DDR4SPDValueManufacturerPartNumberBlank
+ }
+ return 0x00
+ }
+
+ if e.getVal != nil {
+ var ddr4Attribs DDR4MemAttributes
+ ddr4Attribs = attribs.(DDR4MemAttributes)
+ return e.getVal(&ddr4Attribs)
+
+ }
+
+ return e.constVal
+}
diff --git a/util/spd_tools/src/spd_gen/lp4x.go b/util/spd_tools/src/spd_gen/lp4x.go
new file mode 100644
index 000000000000..5257bcb92a9e
--- /dev/null
+++ b/util/spd_tools/src/spd_gen/lp4x.go
@@ -0,0 +1,906 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+/* ------------------------------------------------------------------------------------------ */
+/* LP4x-defined types */
+/* ------------------------------------------------------------------------------------------ */
+
+type lp4x struct {
+}
+
+type LP4xMemAttributes struct {
+ /* Primary attributes - must be provided by JSON file for each part */
+ DensityPerChannelGb int
+ Banks int
+ ChannelsPerDie int
+ DiesPerPackage int
+ BitWidthPerChannel int
+ RanksPerChannel int
+ SpeedMbps int
+
+ /*
+ * All the following parameters are optional and required only if the part requires
+ * special parameters as per the datasheet.
+ */
+ /* Timing parameters */
+ TRFCABNs int
+ TRFCPBNs int
+ TRPABMinNs int
+ TRPPBMinNs int
+ TCKMinPs int
+ TCKMaxPs int
+ TAAMinPs int
+ TRCDMinNs int
+
+ /* CAS */
+ CASLatencies string
+ CASFirstByte byte
+ CASSecondByte byte
+ CASThirdByte byte
+}
+
+type LP4xSpeedParams struct {
+ TCKMinPs int
+ TCKMaxPs int
+ CASLatenciesx16Channel string
+ CASLatenciesx8Channel string
+}
+
+type LP4xRefreshTimings struct {
+ TRFCABNs int
+ TRFCPBNs int
+}
+
+type LP4xSPDAttribFunc func(*LP4xMemAttributes) byte
+
+type LP4xSPDAttribTableEntry struct {
+ constVal byte
+ getVal LP4xSPDAttribFunc
+}
+
+type LP4xSetFunc func(*LP4xMemAttributes) int
+
+type LP4xSet struct {
+ getMRCDensity LP4xSetFunc
+ getDiesPerPackage LP4xSetFunc
+ busWidthEncoding byte
+ normalizeAttribs LP4xSetFunc
+}
+
+/* ------------------------------------------------------------------------------------------ */
+/* Constants */
+/* ------------------------------------------------------------------------------------------ */
+
+const (
+ /* SPD Byte Index */
+ LP4xSPDIndexSize = 0
+ LP4xSPDIndexRevision = 1
+ LP4xSPDIndexMemoryType = 2
+ LP4xSPDIndexModuleType = 3
+ LP4xSPDIndexDensityBanks = 4
+ LP4xSPDIndexAddressing = 5
+ LP4xSPDIndexPackageType = 6
+ LP4xSPDIndexOptionalFeatures = 7
+ LP4xSPDIndexModuleOrganization = 12
+ LP4xSPDIndexBusWidth = 13
+ LP4xSPDIndexTimebases = 17
+ LP4xSPDIndexTCKMin = 18
+ LP4xSPDIndexTCKMax = 19
+ LP4xSPDIndexCASFirstByte = 20
+ LP4xSPDIndexCASSecondByte = 21
+ LP4xSPDIndexCASThirdByte = 22
+ LP4xSPDIndexCASFourthByte = 23
+ LP4xSPDIndexTAAMin = 24
+ LP4xSPDIndexReadWriteLatency = 25
+ LP4xSPDIndexTRCDMin = 26
+ LP4xSPDIndexTRPABMin = 27
+ LP4xSPDIndexTRPPBMin = 28
+ LP4xSPDIndexTRFCABMinLSB = 29
+ LP4xSPDIndexTRFCABMinMSB = 30
+ LP4xSPDIndexTRFCPBMinLSB = 31
+ LP4xSPDIndexTRFCPBMinMSB = 32
+ LP4xSPDIndexTRPPBMinFineOffset = 120
+ LP4xSPDIndexTRPABMinFineOffset = 121
+ LP4xSPDIndexTRCDMinFineOffset = 122
+ LP4xSPDIndexTAAMinFineOffset = 123
+ LP4xSPDIndexTCKMaxFineOffset = 124
+ LP4xSPDIndexTCKMinFineOffset = 125
+ LP4xSPDIndexManufacturerPartNumberStartByte = 329
+ LP4xSPDIndexManufacturerPartNumberEndByte = 348
+
+ /* SPD Byte Value */
+
+ /*
+ * From JEDEC spec:
+ * 6:4 (Bytes total) = 2 (512 bytes)
+ * 3:0 (Bytes used) = 3 (384 bytes)
+ * Set to 0x23 for LPDDR4x.
+ */
+ LP4xSPDValueSize = 0x23
+
+ /*
+ * From JEDEC spec: Revision 1.1
+ * Set to 0x11.
+ */
+ LP4xSPDValueRevision = 0x11
+
+ /* LPDDR4x memory type = 0x11 */
+ LP4xSPDValueMemoryType = 0x11
+
+ /*
+ * From JEDEC spec:
+ * 7:7 (Hybrid) = 0 (Not hybrid)
+ * 6:4 (Hybrid media) = 000 (Not hybrid)
+ * 3:0 (Base Module Type) = 1110 (Non-DIMM solution)
+ *
+ * This is dependent on hardware design. LPDDR4x only has memory down solution.
+ * Hence this is not hybrid non-DIMM solution.
+ * Set to 0x0E.
+ */
+ LP4xSPDValueModuleType = 0x0e
+
+ /*
+ * From JEDEC spec:
+ * 5:4 (Maximum Activate Window) = 00 (8192 * tREFI)
+ * 3:0 (Maximum Activate Count) = 1000 (Unlimited MAC)
+ *
+ * Needs to come from datasheet, but most parts seem to support unlimited MAC.
+ * MR#24 OP3
+ */
+ LP4xSPDValueOptionalFeatures = 0x08
+
+ /*
+ * From JEDEC spec:
+ * 3:2 (MTB) = 00 (0.125ns)
+ * 1:0 (FTB) = 00 (1ps)
+ * Set to 0x00.
+ */
+ LP4xSPDValueTimebases = 0x00
+
+ /* CAS fourth byte: All bits are reserved */
+ LP4xSPDValueCASFourthByte = 0x00
+
+ /* Write Latency Set A and Read Latency DBI-RD disabled. */
+ LP4xSPDValueReadWriteLatency = 0x00
+
+ /* As per JEDEC spec, unused digits of manufacturer part number are left as blank. */
+ LP4xSPDValueManufacturerPartNumberBlank = 0x20
+)
+
+const (
+ /* First Byte */
+ LP4xCAS6 = 1 << 1
+ LP4xCAS10 = 1 << 4
+ LP4xCAS14 = 1 << 7
+ /* Second Byte */
+ LP4xCAS16 = 1 << 0
+ LP4xCAS20 = 1 << 2
+ LP4xCAS22 = 1 << 3
+ LP4xCAS24 = 1 << 4
+ LP4xCAS26 = 1 << 5
+ LP4xCAS28 = 1 << 6
+ /* Third Byte */
+ LP4xCAS32 = 1 << 0
+ LP4xCAS36 = 1 << 2
+ LP4xCAS40 = 1 << 4
+)
+
+const (
+ /*
+ * JEDEC spec says that TCKmax should be 100ns for all speed grades.
+ * 100ns in MTB units comes out to be 0x320. But since this is a byte field, set it to
+ * 0xFF i.e. 31.875ns.
+ */
+ LP4xTCKMaxPsDefault = 31875
+)
+
+/* ------------------------------------------------------------------------------------------ */
+/* Global variables */
+/* ------------------------------------------------------------------------------------------ */
+
+var LP4xPlatformSetMap = map[int][]int{
+ 0: {PlatformTGL, PlatformADL},
+ 1: {PlatformJSL, PlatformCZN},
+}
+
+var LP4xSetInfo = map[int]LP4xSet{
+ 0: {
+ getMRCDensity: LP4xGetMRCDensitySet0,
+ getDiesPerPackage: LP4xGetDiesPerPackageSet0,
+ /*
+ * As per advisory 616599:
+ * 7:5 (Number of system channels) = 000 (1 channel always)
+ * 2:0 (Bus width) = 001 (x16 always)
+ * Set to 0x01.
+ */
+ busWidthEncoding: 0x01,
+ normalizeAttribs: LP4xNormalizeAttribsSet0,
+ },
+ 1: {
+ getMRCDensity: LP4xGetMRCDensitySet1,
+ getDiesPerPackage: LP4xGetDiesPerPackageSet1,
+ /*
+ * As per advisory 610202:
+ * 7:5 (Number of system channels) = 001 (2 channel always)
+ * 2:0 (Bus width) = 010 (x32 always)
+ * Set to 0x22.
+ */
+ busWidthEncoding: 0x22,
+ },
+}
+
+var LP4xPartAttributeMap = map[string]LP4xMemAttributes{}
+var LP4xCurrSet int
+
+/* This encodes the density in Gb to SPD values as per JESD 21-C */
+var LP4xDensityGbToSPDEncoding = map[int]byte{
+ 4: 0x4,
+ 6: 0xb,
+ 8: 0x5,
+ 12: 0x8,
+ 16: 0x6,
+ 24: 0x9,
+ 32: 0x7,
+}
+
+/*
+ * Table 5 from JESD209-4C.
+ * Maps density per physical channel to row-column encoding as per JESD 21-C for a device with
+ * x8 physical channel.
+ */
+var LP4xDensityGbx8ChannelToRowColumnEncoding = map[int]byte{
+ 3: 0x21, /* 16 rows, 10 columns */
+ 4: 0x21, /* 16 rows, 10 columns */
+ 6: 0x29, /* 17 rows, 10 columns */
+ 8: 0x29, /* 17 rows, 10 columns */
+ 12: 0x31, /* 18 rows, 10 columns */
+ 16: 0x31, /* 18 rows, 10 columns */
+}
+
+/*
+ * Table 3 from JESD209-4C.
+ * Maps density per physical channel to row-column encoding as per JESD 21-C for a device with
+ * x16 physical channel.
+ */
+var LP4xDensityGbx16ChannelToRowColumnEncoding = map[int]byte{
+ 4: 0x19, /* 15 rows, 10 columns */
+ 6: 0x21, /* 16 rows, 10 columns */
+ 8: 0x21, /* 16 rows, 10 columns */
+ 12: 0x29, /* 17 rows, 10 columns */
+ 16: 0x29, /* 17 rows, 10 columns */
+}
+
+/*
+ * Table 112 from JESD209-4C
+ * Maps density per physical channel to refresh timings. This is the same for x8 and x16
+ * devices.
+ */
+var LP4xDensityGbPhysicalChannelToRefreshEncoding = map[int]LP4xRefreshTimings{
+ 3: {
+ TRFCABNs: 180,
+ TRFCPBNs: 90,
+ },
+ 4: {
+ TRFCABNs: 180,
+ TRFCPBNs: 90,
+ },
+ 6: {
+ TRFCABNs: 280,
+ TRFCPBNs: 140,
+ },
+ 8: {
+ TRFCABNs: 280,
+ TRFCPBNs: 140,
+ },
+ 12: {
+ TRFCABNs: 380,
+ TRFCPBNs: 190,
+ },
+ 16: {
+ TRFCABNs: 380,
+ TRFCPBNs: 190,
+ },
+}
+
+var LP4xBankEncoding = map[int]byte{
+ 4: 0 << 4,
+ 8: 1 << 4,
+}
+
+var LP4xSpeedMbpsToSPDEncoding = map[int]LP4xSpeedParams{
+ 4267: {
+ TCKMinPs: 468, /* 1/4267 * 2 */
+ TCKMaxPs: LP4xTCKMaxPsDefault,
+ CASLatenciesx16Channel: "6 10 14 20 24 28 32 36",
+ CASLatenciesx8Channel: "6 10 16 22 26 32 36 40",
+ },
+ 3733: {
+ TCKMinPs: 535, /* 1/3733 * 2 */
+ TCKMaxPs: LP4xTCKMaxPsDefault,
+ CASLatenciesx16Channel: "6 10 14 20 24 28 32",
+ CASLatenciesx8Channel: "6 10 16 22 26 32 36",
+ },
+ 3200: {
+ TCKMinPs: 625, /* 1/3200 * 2 */
+ TCKMaxPs: LP4xTCKMaxPsDefault,
+ CASLatenciesx16Channel: "6 10 14 20 24 28",
+ CASLatenciesx8Channel: "6 10 16 22 26 32",
+ },
+}
+
+var LP4xSPDAttribTable = map[int]LP4xSPDAttribTableEntry{
+ LP4xSPDIndexSize: {constVal: LP4xSPDValueSize},
+ LP4xSPDIndexRevision: {constVal: LP4xSPDValueRevision},
+ LP4xSPDIndexMemoryType: {constVal: LP4xSPDValueMemoryType},
+ LP4xSPDIndexModuleType: {constVal: LP4xSPDValueModuleType},
+ LP4xSPDIndexDensityBanks: {getVal: LP4xEncodeDensityBanks},
+ LP4xSPDIndexAddressing: {getVal: LP4xEncodeSdramAddressing},
+ LP4xSPDIndexPackageType: {getVal: LP4xEncodePackageType},
+ LP4xSPDIndexOptionalFeatures: {constVal: LP4xSPDValueOptionalFeatures},
+ LP4xSPDIndexModuleOrganization: {getVal: LP4xEncodeModuleOrganization},
+ LP4xSPDIndexBusWidth: {getVal: LP4xEncodeBusWidth},
+ LP4xSPDIndexTimebases: {constVal: LP4xSPDValueTimebases},
+ LP4xSPDIndexTCKMin: {getVal: LP4xEncodeTCKMin},
+ LP4xSPDIndexTCKMax: {getVal: LP4xEncodeTCKMax},
+ LP4xSPDIndexTCKMaxFineOffset: {getVal: LP4xEncodeTCKMaxFineOffset},
+ LP4xSPDIndexTCKMinFineOffset: {getVal: LP4xEncodeTCKMinFineOffset},
+ LP4xSPDIndexCASFirstByte: {getVal: LP4xEncodeCASFirstByte},
+ LP4xSPDIndexCASSecondByte: {getVal: LP4xEncodeCASSecondByte},
+ LP4xSPDIndexCASThirdByte: {getVal: LP4xEncodeCASThirdByte},
+ LP4xSPDIndexCASFourthByte: {constVal: LP4xSPDValueCASFourthByte},
+ LP4xSPDIndexTAAMin: {getVal: LP4xEncodeTAAMin},
+ LP4xSPDIndexTAAMinFineOffset: {getVal: LP4xEncodeTAAMinFineOffset},
+ LP4xSPDIndexReadWriteLatency: {constVal: LP4xSPDValueReadWriteLatency},
+ LP4xSPDIndexTRCDMin: {getVal: LP4xEncodeTRCDMin},
+ LP4xSPDIndexTRCDMinFineOffset: {getVal: LP4xEncodeTRCDMinFineOffset},
+ LP4xSPDIndexTRPABMin: {getVal: LP4xEncodeTRPABMin},
+ LP4xSPDIndexTRPABMinFineOffset: {getVal: LP4xEncodeTRPABMinFineOffset},
+ LP4xSPDIndexTRPPBMin: {getVal: LP4xEncodeTRPPBMin},
+ LP4xSPDIndexTRPPBMinFineOffset: {getVal: LP4xEncodeTRPPBMinFineOffset},
+ LP4xSPDIndexTRFCABMinLSB: {getVal: LP4xEncodeTRFCABMinLsb},
+ LP4xSPDIndexTRFCABMinMSB: {getVal: LP4xEncodeTRFCABMinMsb},
+ LP4xSPDIndexTRFCPBMinLSB: {getVal: LP4xEncodeTRFCPBMinLsb},
+ LP4xSPDIndexTRFCPBMinMSB: {getVal: LP4xEncodeTRFCPBMinMsb},
+}
+
+/* ------------------------------------------------------------------------------------------ */
+/* Functions */
+/* ------------------------------------------------------------------------------------------ */
+
+func LP4xGetMRCDensitySet0(memAttribs *LP4xMemAttributes) int {
+ /*
+ * Intel MRC on TGL/ADL expects density per logical channel to be encoded in
+ * SPDIndexDensityBanks. Logical channel on TGL/ADL is an x16 channel.
+ */
+ return memAttribs.DensityPerChannelGb * 16 / memAttribs.BitWidthPerChannel
+}
+
+func LP4xGetMRCDensitySet1(memAttribs *LP4xMemAttributes) int {
+ /*
+ * Intel MRC on JSL expects density per die to be encoded in
+ * SPDIndexDensityBanks.
+ */
+ return memAttribs.DensityPerChannelGb * memAttribs.ChannelsPerDie
+}
+
+func LP4xGetMRCDensity(memAttribs *LP4xMemAttributes) int {
+ f, ok := LP4xSetInfo[LP4xCurrSet]
+
+ if ok == false || f.getMRCDensity == nil {
+ return 0
+ }
+
+ return f.getMRCDensity(memAttribs)
+}
+
+func LP4xEncodeDensityBanks(memAttribs *LP4xMemAttributes) byte {
+ var b byte
+
+ b = LP4xDensityGbToSPDEncoding[LP4xGetMRCDensity(memAttribs)]
+ b |= LP4xBankEncoding[memAttribs.Banks]
+
+ return b
+}
+
+func LP4xEncodeSdramAddressing(memAttribs *LP4xMemAttributes) byte {
+ densityPerChannelGb := memAttribs.DensityPerChannelGb
+ if memAttribs.BitWidthPerChannel == 8 {
+ return LP4xDensityGbx8ChannelToRowColumnEncoding[densityPerChannelGb]
+ } else {
+ return LP4xDensityGbx16ChannelToRowColumnEncoding[densityPerChannelGb]
+ }
+ return 0
+}
+
+func LP4xEncodePackage(dies int) byte {
+ var temp byte
+
+ if dies > 1 {
+ /* If more than one die, then this is a non-monolithic device. */
+ temp = 1
+ } else {
+ /* If only single die, then this is a monolithic device. */
+ temp = 0
+ }
+
+ return temp << 7
+}
+
+func LP4xEncodeChannelsPerDie(channels int) byte {
+ var temp byte
+
+ temp = byte(channels >> 1)
+
+ return temp << 2
+}
+
+func LP4xGetDiesPerPackageSet0(memAttribs *LP4xMemAttributes) int {
+ /* Intel MRC expects logical dies to be encoded for TGL/ADL. */
+ return memAttribs.ChannelsPerDie * memAttribs.RanksPerChannel * memAttribs.BitWidthPerChannel / 16
+}
+
+func LP4xGetDiesPerPackageSet1(memAttribs *LP4xMemAttributes) int {
+ /* Intel MRC expects physical dies to be encoded for JSL. */
+ /* AMD PSP expects physical dies (ZQ balls) */
+ return memAttribs.DiesPerPackage
+}
+
+/* Per JESD209-4C Dies = ZQ balls on the package */
+/* Note that this can be different than the part's die count */
+func LP4xEncodeDiesPerPackage(memAttribs *LP4xMemAttributes) byte {
+ var dies int = 0
+
+ f, ok := LP4xSetInfo[LP4xCurrSet]
+ if ok != false && f.getDiesPerPackage != nil {
+ dies = f.getDiesPerPackage(memAttribs)
+ }
+
+ b := LP4xEncodePackage(dies) /* Monolithic / Non-monolithic device */
+ b |= (byte(dies) - 1) << 4
+
+ return b
+}
+
+func LP4xEncodePackageType(memAttribs *LP4xMemAttributes) byte {
+ var b byte
+
+ b |= LP4xEncodeChannelsPerDie(memAttribs.ChannelsPerDie)
+ b |= LP4xEncodeDiesPerPackage(memAttribs)
+
+ return b
+}
+
+func LP4xEncodeDataWidth(bitWidthPerChannel int) byte {
+ return byte(bitWidthPerChannel / 8)
+}
+
+func LP4xEncodeRanks(ranks int) byte {
+ var b byte
+ b = byte(ranks - 1)
+ return b << 3
+}
+
+func LP4xEncodeModuleOrganization(memAttribs *LP4xMemAttributes) byte {
+ var b byte
+
+ b = LP4xEncodeDataWidth(memAttribs.BitWidthPerChannel)
+ b |= LP4xEncodeRanks(memAttribs.RanksPerChannel)
+
+ return b
+}
+
+func LP4xEncodeBusWidth(memAttribs *LP4xMemAttributes) byte {
+ f, ok := LP4xSetInfo[LP4xCurrSet]
+
+ if ok == false {
+ return 0
+ }
+
+ return f.busWidthEncoding
+}
+
+func LP4xEncodeTCKMin(memAttribs *LP4xMemAttributes) byte {
+ return convPsToMtbByte(memAttribs.TCKMinPs)
+}
+
+func LP4xEncodeTCKMinFineOffset(memAttribs *LP4xMemAttributes) byte {
+ return convPsToFtbByte(memAttribs.TCKMinPs)
+}
+
+func LP4xEncodeTCKMax(memAttribs *LP4xMemAttributes) byte {
+ return convPsToMtbByte(memAttribs.TCKMaxPs)
+}
+
+func LP4xEncodeTCKMaxFineOffset(memAttribs *LP4xMemAttributes) byte {
+ return convPsToFtbByte(memAttribs.TCKMaxPs)
+}
+
+func LP4xEncodeCASFirstByte(memAttribs *LP4xMemAttributes) byte {
+ return memAttribs.CASFirstByte
+}
+
+func LP4xEncodeCASSecondByte(memAttribs *LP4xMemAttributes) byte {
+ return memAttribs.CASSecondByte
+}
+
+func LP4xEncodeCASThirdByte(memAttribs *LP4xMemAttributes) byte {
+ return memAttribs.CASThirdByte
+}
+
+func LP4xEncodeTAAMin(memAttribs *LP4xMemAttributes) byte {
+ return convPsToMtbByte(memAttribs.TAAMinPs)
+}
+
+func LP4xEncodeTAAMinFineOffset(memAttribs *LP4xMemAttributes) byte {
+ return convPsToFtbByte(memAttribs.TAAMinPs)
+}
+
+func LP4xEncodeTRCDMin(memAttribs *LP4xMemAttributes) byte {
+ return convNsToMtbByte(memAttribs.TRCDMinNs)
+}
+
+func LP4xEncodeTRCDMinFineOffset(memAttribs *LP4xMemAttributes) byte {
+ return convNsToFtbByte(memAttribs.TRCDMinNs)
+}
+
+func LP4xEncodeTRPABMin(memAttribs *LP4xMemAttributes) byte {
+ return convNsToMtbByte(memAttribs.TRPABMinNs)
+}
+
+func LP4xEncodeTRPABMinFineOffset(memAttribs *LP4xMemAttributes) byte {
+ return convNsToFtbByte(memAttribs.TRPABMinNs)
+}
+
+func LP4xEncodeTRPPBMin(memAttribs *LP4xMemAttributes) byte {
+ return convNsToMtbByte(memAttribs.TRPPBMinNs)
+}
+
+func LP4xEncodeTRPPBMinFineOffset(memAttribs *LP4xMemAttributes) byte {
+ return convNsToFtbByte(memAttribs.TRPPBMinNs)
+}
+
+func LP4xEncodeTRFCABMinMsb(memAttribs *LP4xMemAttributes) byte {
+ return byte((convNsToMtb(memAttribs.TRFCABNs) >> 8) & 0xff)
+}
+
+func LP4xEncodeTRFCABMinLsb(memAttribs *LP4xMemAttributes) byte {
+ return byte(convNsToMtb(memAttribs.TRFCABNs) & 0xff)
+}
+
+func LP4xEncodeTRFCPBMinMsb(memAttribs *LP4xMemAttributes) byte {
+ return byte((convNsToMtb(memAttribs.TRFCPBNs) >> 8) & 0xff)
+}
+
+func LP4xEncodeTRFCPBMinLsb(memAttribs *LP4xMemAttributes) byte {
+ return byte(convNsToMtb(memAttribs.TRFCPBNs) & 0xff)
+}
+
+func LP4xEncodeLatencies(latency int, memAttribs *LP4xMemAttributes) error {
+ switch latency {
+ case 6:
+ memAttribs.CASFirstByte |= LP4xCAS6
+ case 10:
+ memAttribs.CASFirstByte |= LP4xCAS10
+ case 14:
+ memAttribs.CASFirstByte |= LP4xCAS14
+ case 16:
+ memAttribs.CASSecondByte |= LP4xCAS16
+ case 20:
+ memAttribs.CASSecondByte |= LP4xCAS20
+ case 22:
+ memAttribs.CASSecondByte |= LP4xCAS22
+ case 24:
+ memAttribs.CASSecondByte |= LP4xCAS24
+ case 26:
+ memAttribs.CASSecondByte |= LP4xCAS26
+ case 28:
+ memAttribs.CASSecondByte |= LP4xCAS28
+ case 32:
+ memAttribs.CASThirdByte |= LP4xCAS32
+ case 36:
+ memAttribs.CASThirdByte |= LP4xCAS36
+ case 40:
+ memAttribs.CASThirdByte |= LP4xCAS40
+ default:
+ fmt.Errorf("Incorrect CAS Latency: ", latency)
+ }
+
+ return nil
+}
+
+func LP4xUpdateTCK(memAttribs *LP4xMemAttributes) {
+ if memAttribs.TCKMinPs == 0 {
+ memAttribs.TCKMinPs = LP4xSpeedMbpsToSPDEncoding[memAttribs.SpeedMbps].TCKMinPs
+ }
+ if memAttribs.TCKMaxPs == 0 {
+ memAttribs.TCKMaxPs = LP4xSpeedMbpsToSPDEncoding[memAttribs.SpeedMbps].TCKMaxPs
+ }
+}
+
+func LP4xGetCASLatencies(memAttribs *LP4xMemAttributes) string {
+ if memAttribs.BitWidthPerChannel == 16 {
+ return LP4xSpeedMbpsToSPDEncoding[memAttribs.SpeedMbps].CASLatenciesx16Channel
+ } else if memAttribs.BitWidthPerChannel == 8 {
+ return LP4xSpeedMbpsToSPDEncoding[memAttribs.SpeedMbps].CASLatenciesx8Channel
+ }
+
+ return ""
+}
+
+func LP4xUpdateCAS(memAttribs *LP4xMemAttributes) error {
+ if len(memAttribs.CASLatencies) == 0 {
+ memAttribs.CASLatencies = LP4xGetCASLatencies(memAttribs)
+ }
+
+ latencies := strings.Fields(memAttribs.CASLatencies)
+ for i := 0; i < len(latencies); i++ {
+ latency, err := strconv.Atoi(latencies[i])
+ if err != nil {
+ return fmt.Errorf("Unable to convert latency ", latencies[i])
+ }
+ if err := LP4xEncodeLatencies(latency, memAttribs); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func LP4xGetMinCAS(memAttribs *LP4xMemAttributes) (int, error) {
+ if (memAttribs.CASThirdByte & LP4xCAS40) != 0 {
+ return 40, nil
+ }
+ if (memAttribs.CASThirdByte & LP4xCAS36) != 0 {
+ return 36, nil
+ }
+ if (memAttribs.CASThirdByte & LP4xCAS32) != 0 {
+ return 32, nil
+ }
+ if (memAttribs.CASSecondByte & LP4xCAS28) != 0 {
+ return 28, nil
+ }
+
+ return 0, fmt.Errorf("Unexpected min CAS")
+}
+
+func LP4xUpdateTAAMin(memAttribs *LP4xMemAttributes) error {
+ if memAttribs.TAAMinPs == 0 {
+ minCAS, err := LP4xGetMinCAS(memAttribs)
+ if err != nil {
+ return err
+ }
+ memAttribs.TAAMinPs = memAttribs.TCKMinPs * minCAS
+ }
+
+ return nil
+}
+
+func LP4xUpdateTRFCAB(memAttribs *LP4xMemAttributes) {
+ if memAttribs.TRFCABNs == 0 {
+ memAttribs.TRFCABNs = LP4xDensityGbPhysicalChannelToRefreshEncoding[memAttribs.DensityPerChannelGb].TRFCABNs
+ }
+}
+
+func LP4xUpdateTRFCPB(memAttribs *LP4xMemAttributes) {
+ if memAttribs.TRFCPBNs == 0 {
+ memAttribs.TRFCPBNs = LP4xDensityGbPhysicalChannelToRefreshEncoding[memAttribs.DensityPerChannelGb].TRFCPBNs
+ }
+}
+
+func LP4xUpdateTRCD(memAttribs *LP4xMemAttributes) {
+ if memAttribs.TRCDMinNs == 0 {
+ /* JEDEC spec says max of 18ns */
+ memAttribs.TRCDMinNs = 18
+ }
+}
+
+func LP4xUpdateTRPAB(memAttribs *LP4xMemAttributes) {
+ if memAttribs.TRPABMinNs == 0 {
+ /* JEDEC spec says max of 21ns */
+ memAttribs.TRPABMinNs = 21
+ }
+}
+
+func LP4xUpdateTRPPB(memAttribs *LP4xMemAttributes) {
+ if memAttribs.TRPPBMinNs == 0 {
+ /* JEDEC spec says max of 18ns */
+ memAttribs.TRPPBMinNs = 18
+ }
+}
+
+func LP4xNormalizeAttribsSet0(memAttribs *LP4xMemAttributes) int {
+ /*
+ * TGL does not really use physical organization of dies per package when
+ * generating the SPD. So, set it to 0 here so that deduplication ignores
+ * that field.
+ */
+ memAttribs.DiesPerPackage = 0
+
+ return 0
+}
+
+func LP4xNormalizeMemoryAttributes(memAttribs *LP4xMemAttributes) {
+ f, ok := LP4xSetInfo[LP4xCurrSet]
+
+ if ok == false || f.normalizeAttribs == nil {
+ return
+ }
+
+ f.normalizeAttribs(memAttribs)
+}
+
+func Lp4xUpdateMemoryAttributes(memAttribs *LP4xMemAttributes) error {
+ LP4xUpdateTCK(memAttribs)
+ if err := LP4xUpdateCAS(memAttribs); err != nil {
+ return err
+ }
+ if err := LP4xUpdateTAAMin(memAttribs); err != nil {
+ return err
+ }
+ LP4xUpdateTRFCAB(memAttribs)
+ LP4xUpdateTRFCPB(memAttribs)
+ LP4xUpdateTRCD(memAttribs)
+ LP4xUpdateTRPAB(memAttribs)
+ LP4xUpdateTRPPB(memAttribs)
+
+ LP4xNormalizeMemoryAttributes(memAttribs)
+
+ return nil
+}
+
+func LP4xValidateDensityx8Channel(densityPerChannelGb int) error {
+ if _, ok := LP4xDensityGbx8ChannelToRowColumnEncoding[densityPerChannelGb]; ok == false {
+ return fmt.Errorf("Incorrect x8 density: %d Gb", densityPerChannelGb)
+ }
+ return nil
+}
+
+func LP4xValidateDensityx16Channel(densityPerChannelGb int) error {
+ if _, ok := LP4xDensityGbx16ChannelToRowColumnEncoding[densityPerChannelGb]; ok == false {
+ return fmt.Errorf("Incorrect x16 density: %d Gb", densityPerChannelGb)
+ }
+ return nil
+}
+
+func LP4xValidateDensity(memAttribs *LP4xMemAttributes) error {
+ if memAttribs.BitWidthPerChannel == 8 {
+ return LP4xValidateDensityx8Channel(memAttribs.DensityPerChannelGb)
+ } else if memAttribs.BitWidthPerChannel == 16 {
+ return LP4xValidateDensityx16Channel(memAttribs.DensityPerChannelGb)
+ }
+
+ return fmt.Errorf("No density table for this bit width: %d", memAttribs.BitWidthPerChannel)
+}
+
+func LP4xValidateBanks(banks int) error {
+ if banks != 4 && banks != 8 {
+ return fmt.Errorf("Incorrect banks: %d", banks)
+ }
+ return nil
+}
+
+func LP4xValidateChannels(channels int) error {
+ if channels != 1 && channels != 2 && channels != 4 {
+ return fmt.Errorf("Incorrect channels per die: %d ", channels)
+ }
+ return nil
+}
+
+func LP4xValidateDataWidth(width int) error {
+ if width != 8 && width != 16 {
+ return fmt.Errorf("Incorrect bit width: %d", width)
+ }
+ return nil
+}
+
+func LP4xValidateRanks(ranks int) error {
+ if ranks != 1 && ranks != 2 {
+ return fmt.Errorf("Incorrect ranks: %d", ranks)
+ }
+ return nil
+}
+
+func LP4xValidateSpeed(speed int) error {
+ if _, ok := LP4xSpeedMbpsToSPDEncoding[speed]; ok == false {
+ return fmt.Errorf("Incorrect speed: %d Mbps", speed)
+ }
+ return nil
+}
+
+func Lp4xValidateMemPartAttributes(memAttribs *LP4xMemAttributes) error {
+ if err := LP4xValidateBanks(memAttribs.Banks); err != nil {
+ return err
+ }
+ if err := LP4xValidateChannels(memAttribs.ChannelsPerDie); err != nil {
+ return err
+ }
+ if err := LP4xValidateDataWidth(memAttribs.BitWidthPerChannel); err != nil {
+ return err
+ }
+ if err := LP4xValidateDensity(memAttribs); err != nil {
+ return err
+ }
+ if err := LP4xValidateRanks(memAttribs.RanksPerChannel); err != nil {
+ return err
+ }
+ if err := LP4xValidateSpeed(memAttribs.SpeedMbps); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func LP4xIsManufacturerPartNumberByte(index int) bool {
+ if index >= LP4xSPDIndexManufacturerPartNumberStartByte &&
+ index <= LP4xSPDIndexManufacturerPartNumberEndByte {
+ return true
+ }
+ return false
+}
+
+/* ------------------------------------------------------------------------------------------ */
+/* Interface Functions */
+/* ------------------------------------------------------------------------------------------ */
+
+func (lp4x) getSetMap() map[int][]int {
+ return LP4xPlatformSetMap
+}
+
+func (lp4x) addNewPart(name string, attribs interface{}) error {
+ var lp4xAttributes LP4xMemAttributes
+ eByte, err := json.Marshal(attribs)
+ if err != nil {
+ return err
+ }
+
+ if err := json.Unmarshal(eByte, &lp4xAttributes); err != nil {
+ return err
+ }
+
+ if err := Lp4xValidateMemPartAttributes(&lp4xAttributes); err != nil {
+ return err
+ }
+
+ LP4xPartAttributeMap[name] = lp4xAttributes
+ return nil
+}
+
+func (lp4x) getSPDAttribs(name string, set int) (interface{}, error) {
+ lp4xAttributes := LP4xPartAttributeMap[name]
+
+ LP4xCurrSet = set
+
+ if err := Lp4xUpdateMemoryAttributes(&lp4xAttributes); err != nil {
+ return lp4xAttributes, err
+ }
+
+ return lp4xAttributes, nil
+}
+
+func (lp4x) getSPDLen() int {
+ return 512
+}
+
+func (lp4x) getSPDByte(index int, attribs interface{}) byte {
+ e, ok := LP4xSPDAttribTable[index]
+ if ok == false {
+ if LP4xIsManufacturerPartNumberByte(index) {
+ return LP4xSPDValueManufacturerPartNumberBlank
+ }
+ return 0x00
+ }
+
+ if e.getVal != nil {
+ var lp4xAttribs LP4xMemAttributes
+ lp4xAttribs = attribs.(LP4xMemAttributes)
+ return e.getVal(&lp4xAttribs)
+ }
+
+ return e.constVal
+}
diff --git a/util/spd_tools/src/spd_gen/spd_gen.go b/util/spd_tools/src/spd_gen/spd_gen.go
new file mode 100644
index 000000000000..38d706d309a7
--- /dev/null
+++ b/util/spd_tools/src/spd_gen/spd_gen.go
@@ -0,0 +1,281 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+ "reflect"
+ "regexp"
+ "strings"
+)
+
+/* ------------------------------------------------------------------------------------------ */
+/* Program-defined types */
+/* ------------------------------------------------------------------------------------------ */
+type memParts struct {
+ MemParts []memPart `json:"parts"`
+}
+
+type memPart struct {
+ Name string
+ Attribs interface{}
+ SPDId int
+}
+
+type memTech interface {
+ getSetMap() map[int][]int
+ addNewPart(string, interface{}) error
+ getSPDAttribs(string, int) (interface{}, error)
+ getSPDLen() int
+ getSPDByte(int, interface{}) byte
+}
+
+/* ------------------------------------------------------------------------------------------ */
+/* Constants */
+/* ------------------------------------------------------------------------------------------ */
+
+const (
+ PlatformTGL = iota
+ PlatformADL
+ PlatformJSL
+ PlatformPCO
+ PlatformPLK
+ PlatformCZN
+ PlatformMax
+)
+
+const (
+ SPDManifestFileName = "parts_spd_manifest.generated.txt"
+ PlatformManifestFileName = "platforms_manifest.generated.txt"
+)
+
+/* ------------------------------------------------------------------------------------------ */
+/* Global variables */
+/* ------------------------------------------------------------------------------------------ */
+
+var platformNames = map[int]string{
+ PlatformTGL: "TGL",
+ PlatformADL: "ADL",
+ PlatformJSL: "JSL",
+ PlatformPCO: "PCO",
+ PlatformPLK: "PLK",
+ PlatformCZN: "CZN",
+}
+
+/* ------------------------------------------------------------------------------------------ */
+/* Conversion Helper Functions */
+/* ------------------------------------------------------------------------------------------ */
+
+func convNsToPs(timeNs int) int {
+ return timeNs * 1000
+}
+
+func convMtbToPs(mtb int) int {
+ return mtb * 125
+}
+
+func convPsToMtb(timePs int) int {
+ return divRoundUp(timePs, 125)
+}
+
+func convPsToMtbByte(timePs int) byte {
+ return byte(convPsToMtb(timePs) & 0xff)
+}
+
+func convPsToFtbByte(timePs int) byte {
+ mtb := convPsToMtb(timePs)
+ ftb := timePs - convMtbToPs(mtb)
+
+ return byte(ftb)
+}
+
+func convNsToMtb(timeNs int) int {
+ return convPsToMtb(convNsToPs(timeNs))
+}
+
+func convNsToMtbByte(timeNs int) byte {
+ return convPsToMtbByte(convNsToPs(timeNs))
+}
+
+func convNsToFtbByte(timeNs int) byte {
+ return convPsToFtbByte(convNsToPs(timeNs))
+}
+
+func divRoundUp(dividend int, divisor int) int {
+ return (dividend + divisor - 1) / divisor
+}
+
+/* ------------------------------------------------------------------------------------------ */
+/* Functions */
+/* ------------------------------------------------------------------------------------------ */
+
+func findIndex(dedupedAttribs []interface{}, newSPDAttribs interface{}) int {
+ for i := 0; i < len(dedupedAttribs); i++ {
+ if reflect.DeepEqual(dedupedAttribs[i], newSPDAttribs) {
+ return i
+ }
+ }
+
+ return -1
+}
+
+func readMemParts(memPartsFilePath string) (memParts, error) {
+ var memParts memParts
+
+ dataBytes, err := ioutil.ReadFile(memPartsFilePath)
+ if err != nil {
+ return memParts, err
+ }
+
+ // Strip comments from json file
+ re := regexp.MustCompile(`(?m)^\s*//.*`)
+ dataBytes = re.ReplaceAll(dataBytes, []byte(""))
+
+ if err := json.Unmarshal(dataBytes, &memParts); err != nil {
+ return memParts, err
+ }
+
+ return memParts, nil
+}
+
+func createSPD(memAttribs interface{}, t memTech) string {
+ var s string
+
+ for i := 0; i < t.getSPDLen(); i++ {
+ var b byte = 0
+ if memAttribs != nil {
+ b = t.getSPDByte(i, memAttribs)
+ }
+
+ if (i+1)%16 == 0 {
+ s += fmt.Sprintf("%02X\n", b)
+ } else {
+ s += fmt.Sprintf("%02X ", b)
+ }
+ }
+
+ return s
+}
+
+func writeSPD(memAttribs interface{}, SPDId int, SPDSetDirName string, t memTech) {
+ s := createSPD(memAttribs, t)
+ SPDFileName := fmt.Sprintf("spd-%d.hex", SPDId)
+ ioutil.WriteFile(filepath.Join(SPDSetDirName, SPDFileName), []byte(s), 0644)
+}
+
+func writeEmptySPD(SPDSetDirName string, t memTech) {
+ s := createSPD(nil, t)
+ SPDFileName := "spd-empty.hex"
+ ioutil.WriteFile(filepath.Join(SPDSetDirName, SPDFileName), []byte(s), 0644)
+}
+
+func getGeneratedString() string {
+ return fmt.Sprintf("# Generated by:\n# %s\n\n", strings.Join(os.Args[0:], " "))
+}
+
+func writeSPDManifest(memPartArray []memPart, SPDSetDirName string) {
+ var s string
+
+ s += getGeneratedString()
+ for i := 0; i < len(memPartArray); i++ {
+ s += fmt.Sprintf("%s,spd-%d.hex\n", memPartArray[i].Name, memPartArray[i].SPDId)
+ }
+
+ ioutil.WriteFile(filepath.Join(SPDSetDirName, SPDManifestFileName), []byte(s), 0644)
+}
+
+func writeSetMap(setMap map[int][]int, SPDDirName string) {
+ var s string
+
+ s += getGeneratedString()
+
+ for index, arr := range setMap {
+ for _, item := range arr {
+ s += fmt.Sprintf("%s,set-%d\n", platformNames[item], index)
+ }
+ }
+
+ ioutil.WriteFile(filepath.Join(SPDDirName, PlatformManifestFileName), []byte(s), 0644)
+}
+
+func usage() {
+ fmt.Printf("\nUsage: %s <mem_parts_list_json> <mem_technology>\n\n", os.Args[0])
+ fmt.Printf(" where,\n")
+ fmt.Printf(" mem_parts_list_json = JSON File containing list of memory parts and attributes\n")
+ fmt.Printf(" mem_technology = Memory technology -- one of lp4x, ddr4\n\n\n")
+}
+
+func main() {
+ if len(os.Args) != 3 {
+ usage()
+ log.Fatal("Incorrect number of arguments")
+ }
+
+ var t memTech
+ memPartsFilePath, memTechnology := os.Args[1], os.Args[2]
+
+ if strings.ToUpper(memTechnology) == "LP4X" {
+ t = lp4x{}
+ } else if strings.ToUpper(memTechnology) == "DDR4" {
+ t = ddr4{}
+ } else {
+ log.Fatal("Unsupported memory technology ", memTechnology)
+ }
+
+ SPDDir, err := filepath.Abs(filepath.Dir(memPartsFilePath))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ memParts, err := readMemParts(memPartsFilePath)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ memPartExists := make(map[string]bool)
+ for i := 0; i < len(memParts.MemParts); i++ {
+ if memPartExists[memParts.MemParts[i].Name] {
+ log.Fatalf("%s is duplicated in mem_parts_list_json", memParts.MemParts[i].Name)
+ }
+ memPartExists[memParts.MemParts[i].Name] = true
+
+ if err := t.addNewPart(memParts.MemParts[i].Name, memParts.MemParts[i].Attribs); err != nil {
+ log.Fatal(err)
+ }
+ }
+
+ setMap := t.getSetMap()
+
+ for i := 0; i < len(setMap); i++ {
+ var dedupedAttribs []interface{}
+
+ for j := 0; j < len(memParts.MemParts); j++ {
+ spdAttribs, _ := t.getSPDAttribs(memParts.MemParts[j].Name, i)
+ index := -1
+
+ if index = findIndex(dedupedAttribs, spdAttribs); index == -1 {
+ dedupedAttribs = append(dedupedAttribs, spdAttribs)
+ index = len(dedupedAttribs) - 1
+ }
+
+ memParts.MemParts[j].SPDId = index + 1
+ }
+
+ SPDSetDir := fmt.Sprintf("%s/set-%d", SPDDir, i)
+ os.MkdirAll(SPDSetDir, os.ModePerm)
+
+ for j := 0; j < len(dedupedAttribs); j++ {
+ writeSPD(dedupedAttribs[j], j+1, SPDSetDir, t)
+ }
+
+ writeEmptySPD(SPDSetDir, t)
+
+ writeSPDManifest(memParts.MemParts, SPDSetDir)
+ }
+
+ writeSetMap(setMap, SPDDir)
+}