em100: Support 4 byte address mode

This change adds a command line option to set 3 or 4 byte address
mode for the em100 FPGA.  It automatically sets 4 byte address mode
if the selected chip is >16MB, unless 3 byte address mode is set
on the command line.

This does not seem to be necessary with the most recent FPGA version
2.014 but for devices with older versions (I have 2.010) this allows
emulating 32MB SPI flash parts like W25Q256.

Change-Id: Ibe955eb3b63bc51ae618d176a16d8a50f55c8c55
Signed-off-by: Duncan Laurie <dlaurie@google.com>
Reviewed-on: https://review.coreboot.org/c/em100/+/40418
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
diff --git a/em100.c b/em100.c
index 2d51bc9..e4f5e44 100644
--- a/em100.c
+++ b/em100.c
@@ -69,6 +69,24 @@
 		printf("EM100Pro state unknown\n");
 }
 
+static int set_address_mode(struct em100 *em100, int mode)
+{
+	int retval;
+
+	if (mode < 3 || mode > 4) {
+		printf("Invalid address mode: %d\n", mode);
+		return -1;
+	}
+
+	retval = write_fpga_register(em100, 0x4f, mode == 4);
+	if (retval)
+		printf("Enabled %d byte address mode\n", mode);
+	else
+		printf("Failed to enable %d byte address mode\n", mode);
+
+	return retval;
+}
+
 static const char *get_pin_string(int pin) {
 	switch (pin) {
 	case 0:
@@ -751,6 +769,7 @@
 	{"set", 1, 0, 'c'},
 	{"download", 1, 0, 'd'},
 	{"start-address", 1, 0, 'a'},
+	{"address-mode", 1, 0, 'm'},
 	{"start", 0, 0, 'r'},
 	{"stop", 0, 0, 's'},
 	{"verify", 0, 0, 'v'},
@@ -779,6 +798,7 @@
 		"  -c|--set CHIP:                  select chip emulation\n"
 		"  -d|--download FILE:             download FILE into EM100pro\n"
 		"  -a|--start address:             only works with -d (E.g. -d file.bin -a 0x300000)\n"
+	        "  -m|--address-mode MODE:         force 3 or 4 byte address mode\n"
 		"  -u|--upload FILE:               upload from EM100pro into FILE\n"
 		"  -r|--start:                     em100 shall run\n"
 		"  -s|--stop:                      em100 shall stop\n"
@@ -815,12 +835,13 @@
 	int compatibility = 0;
 	int bus = 0, device = 0;
 	int firmware_is_dpfw = 0;
+	int address_mode = 0;
 	unsigned int serial_number = 0;
 	unsigned long address_offset = 0;
 	unsigned int spi_start_address = 0;
 	const char *voltage = NULL;
 
-	while ((opt = getopt_long(argc, argv, "c:d:a:u:rsvtO:F:f:g:S:V:p:DCx:lUhT",
+	while ((opt = getopt_long(argc, argv, "c:d:a:m:u:rsvtO:F:f:g:S:V:p:DCx:lUhT",
 				  longopts, &idx)) != -1) {
 		switch (opt) {
 		case 'c':
@@ -834,6 +855,9 @@
 			sscanf(optarg, "%x", &spi_start_address);
 			printf("SPI address: 0x%08x\n", spi_start_address);
 			break;
+		case 'm':
+			sscanf(optarg, "%d", &address_mode);
+			break;
 		case 'u':
 			read_filename = optarg;
 			break;
@@ -991,6 +1015,19 @@
 			return 0;
 		}
 		printf("Chip set to %s %s.\n", chip->vendor, chip->name);
+
+		/* Automatically enable 4 byte address mode for chips >16MB
+		 * unless specified by the user on the command line.
+		 */
+		if (!address_mode && chip->size > (16 * 1024 * 1024)) {
+			set_address_mode(&em100, 4);
+		}
+	}
+
+	if (address_mode) {
+		if (set_address_mode(&em100, address_mode) < 0) {
+			return 1;
+		}
 	}
 
 	if (voltage) {