util/spd_tools: Automatically determine the SPD dir in part_id_gen

Currently, one of the arguments to part_id_gen is the directory
containing the SPD files, e.g. spd/lp4x/set-0. This requires the user of
the tool to understand the spd/ directory structure, and manually look
up the set number corresponding to their platform.

Change part_id_gen to take the platform and memory technology as
arguments instead of the SPD directory, and automatically determine the
SPD directory by reading the platforms manifest file generated by
spd_gen.go.

BUG=b:191776301
TEST=Run part_id_gen and check that the generated Makefile.inc and
dram_id.generated.txt are the same as before. Example:
util/spd_tools/bin/part_id_gen \
  ADL \
  lp4x \
  src/mainboard/google/brya/variants/kano/memory \
  src/mainboard/google/brya/variants/kano/memory/mem_parts_used.txt

Change-Id: I7cd7243d76b5769e8a15daa56b8438274bdd8e96
Signed-off-by: Reka Norman <rekanorman@google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/57586
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Karthik Ramasubramanian <kramasub@google.com>
Reviewed-by: Furquan Shaikh <furquan@google.com>
Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
diff --git a/util/spd_tools/src/part_id_gen/part_id_gen.go b/util/spd_tools/src/part_id_gen/part_id_gen.go
index 7803361..10388bf 100644
--- a/util/spd_tools/src/part_id_gen/part_id_gen.go
+++ b/util/spd_tools/src/part_id_gen/part_id_gen.go
@@ -11,42 +11,84 @@
 	"os"
 	"path/filepath"
 	"strconv"
+	"strings"
 )
 
 /*
  * This program allocates DRAM strap IDs for different parts that are being used by the variant.
  *
  * It expects the following inputs:
- *  Path to SPD directory. This is the location where SPD files and SPD Manifest generated by
- *  gen_spd.go are placed.
+ *  Name of the SoC platform, e.g. TGL.
+ *  Memory technology used by the variant, e.g. lp4x.
  *  Path to Makefile directory. Makefile.inc generated by this program is placed in this
  *  location.
  *  Text file containing a list of memory part names used by the board. Each line in the file
  *  is expected to have one memory part name.
  */
 const (
-	SPDManifestFileName = "parts_spd_manifest.generated.txt"
-	SPDEmptyFileName    = "spd-empty.hex"
-	MakefileName        = "Makefile.inc"
-	DRAMIdFileName      = "dram_id.generated.txt"
-	MaxMemoryId         = 15
+	SPDManifestFileName       = "parts_spd_manifest.generated.txt"
+	PlatformsManifestFileName = "platforms_manifest.generated.txt"
+	SPDEmptyFileName          = "spd-empty.hex"
+	MakefileName              = "Makefile.inc"
+	DRAMIdFileName            = "dram_id.generated.txt"
+	MaxMemoryId               = 15
 )
 
+var supportedPlatforms = [...]string{
+	"TGL",
+	"ADL",
+	"JSL",
+	"PCO",
+	"PLK",
+	"CZN",
+}
+
+var supportedMemTechs = [...]string{
+	"lp4x",
+	"ddr4",
+}
+
 func usage() {
-	fmt.Printf("\nUsage: %s <spd_dir> <makefile_dir> <mem_parts_used_file>\n\n", os.Args[0])
+	fmt.Printf("\nUsage: %s <platform> <mem_technology> <makefile_dir> <mem_parts_used_file>\n\n", os.Args[0])
 	fmt.Printf("   where,\n")
-	fmt.Printf("   spd_dir = Directory path containing SPD files and manifest generated by gen_spd.go\n")
+	fmt.Printf("   platform = SoC platform which the board is based on\n")
+	fmt.Printf("              supported platforms: %v\n", supportedPlatforms)
+	fmt.Printf("   mem_technology = Memory technology used by the board\n")
+	fmt.Printf("                    supported technologies: %v\n", supportedMemTechs)
 	fmt.Printf("   makefile_dir = Directory path where generated Makefile.inc should be placed\n")
 	fmt.Printf("   mem_parts_used_file = CSV file containing list of memory parts used by the board and optional fixed ids\n\n\n")
 }
 
-func checkArgs() error {
-
-	for _, arg := range os.Args[1:] {
-		if _, err := os.Stat(arg); err != nil {
-			return err
+func checkArgs(platform string, memTech string, makefileDir string, memPartsUsedFile string) error {
+	supported := false
+	for _, p := range supportedPlatforms {
+		if strings.ToUpper(platform) == p {
+			supported = true
+			break
 		}
 	}
+	if !supported {
+		return fmt.Errorf("Platform %s is not supported", platform)
+	}
+
+	supported = false
+	for _, m := range supportedMemTechs {
+		if strings.ToLower(memTech) == m {
+			supported = true
+			break
+		}
+	}
+	if !supported {
+		return fmt.Errorf("Memory technology %s is not supported", memTech)
+	}
+
+	if _, err := os.Stat(makefileDir); err != nil {
+		return fmt.Errorf("Invalid makefile_dir %s: %v", makefileDir, err)
+	}
+
+	if _, err := os.Stat(memPartsUsedFile); err != nil {
+		return fmt.Errorf("Invalid mem_parts_used_file %s: %v", memPartsUsedFile, err)
+	}
 
 	return nil
 }
@@ -56,12 +98,58 @@
 	index    int
 }
 
+func readPlatformsManifest(memTech string) (map[string]string, error) {
+	manifestFilePath := filepath.Join("spd", strings.ToLower(memTech), PlatformsManifestFileName)
+	f, err := os.Open(manifestFilePath)
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	r := csv.NewReader(f)
+	r.Comment = '#'
+
+	platformToSetMap := make(map[string]string)
+
+	for {
+		fields, err := r.Read()
+
+		if err == io.EOF {
+			break
+		}
+
+		if err != nil {
+			return nil, err
+		}
+
+		if len(fields) != 2 {
+			return nil, fmt.Errorf("Platforms manifest file is incorrectly formatted: %s", manifestFilePath)
+		}
+
+		platformToSetMap[fields[0]] = fields[1]
+	}
+
+	return platformToSetMap, nil
+}
+
+func getSPDDir(platform string, memTech string) (string, error) {
+	platformToSetMap, err := readPlatformsManifest(memTech)
+	if err != nil {
+		return "", err
+	}
+
+	setName, ok := platformToSetMap[strings.ToUpper(platform)]
+	if !ok {
+		return "", fmt.Errorf("Platform %s does not support memory technology %s", platform, memTech)
+	}
+
+	return filepath.Join("spd", strings.ToLower(memTech), setName), nil
+}
+
 /*
  * Read input file CSV that contains list of memory part names used by the variant
  * and an optional assigned id.
  */
 func readParts(memPartsUsedFileName string) ([]usedPart, error) {
-
 	f, err := os.Open(memPartsUsedFileName)
 	if err != nil {
 		return nil, err
@@ -166,7 +254,6 @@
  * assigned ID.
  */
 func genPartIdInfo(parts []usedPart, partToSPDMap map[string]string, SPDToIndexMap map[string]int, makefileDirName string) ([]partIds, error) {
-
 	partIdList := []partIds{}
 	var s string
 
@@ -285,29 +372,39 @@
 }
 
 func main() {
-	if len(os.Args) != 4 {
+	if len(os.Args) != 5 {
 		usage()
 		log.Fatal("Incorrect number of arguments")
 	}
 
-	SPDDir, MakefileDir, MemPartsUsedFile := os.Args[1], os.Args[2], os.Args[3]
+	platform, memTech, makefileDir, memPartsUsedFile := os.Args[1], os.Args[2], os.Args[3], os.Args[4]
+
+	err := checkArgs(platform, memTech, makefileDir, memPartsUsedFile)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	SPDDir, err := getSPDDir(platform, memTech)
+	if err != nil {
+		log.Fatal(err)
+	}
 
 	partToSPDMap, SPDToIndexMap, err := readSPDManifest(SPDDir)
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	parts, err := readParts(MemPartsUsedFile)
+	parts, err := readParts(memPartsUsedFile)
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	partIdList, err := genPartIdInfo(parts, partToSPDMap, SPDToIndexMap, MakefileDir)
+	partIdList, err := genPartIdInfo(parts, partToSPDMap, SPDToIndexMap, makefileDir)
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	if err := genMakefile(partIdList, MakefileDir, SPDDir); err != nil {
+	if err := genMakefile(partIdList, makefileDir, SPDDir); err != nil {
 		log.Fatal(err)
 	}
 }