ft2232_spi.c: add frequency divisor parameter

This adds an optional argument when using the ft2232_spi programmer to set
the frequency divisor. The valid values for the divisor is any even integer
between 2 and 131072.

Corresponding to flashrom svn r1537.

Signed-off-by: Samir Ibradžić <sibradzic@gmail.com>
Signed-off-by: Stefan Tauner <stefan.tauner@alumni.tuwien.ac.at>
Acked-by: Stefan Tauner <stefan.tauner@alumni.tuwien.ac.at>
diff --git a/flashrom.8 b/flashrom.8
index c29da74..9fb2f55 100644
--- a/flashrom.8
+++ b/flashrom.8
@@ -549,6 +549,17 @@
 .B 4232H
 and the default interface is
 .BR B .
+.sp
+All models supported by the ft2232_spi driver can configure the SPI clock rate by setting a divisor. The
+expressible divisors are all even numbers between 2 and 2^17 (=131072) resulting in SPI clock frequencies of
+6 MHz down to about 92 Hz for 12 MHz inputs. The default divisor is set to 2, but you can use another one by
+specifying the optional
+.B divisor
+parameter with the
+.sp
+.B "  flashrom \-p ft2232_spi:divisor=div"
+.sp
+syntax.
 .SS
 .BR "serprog " programmer
 A mandatory parameter specifies either a serial
diff --git a/ft2232_spi.c b/ft2232_spi.c
index 122866f..d11867e 100644
--- a/ft2232_spi.c
+++ b/ft2232_spi.c
@@ -64,17 +64,8 @@
 	{},
 };
 
-/*
- * The 'H' chips can run internally at either 12MHz or 60MHz.
- * The non-H chips can only run at 12MHz.
- */
-static uint8_t clock_5x = 1;
 
-/*
- * In either case, the divisor is a simple integer clock divider.
- * If clock_5x is set, this divisor divides 30MHz, else it divides 6MHz.
- */
-#define DIVIDE_BY 3  /* e.g. '3' will give either 10MHz or 2MHz SPI clock. */
+#define DEFAULT_DIVISOR 2
 
 #define BITMODE_BITBANG_NORMAL	1
 #define BITMODE_BITBANG_SPI	2
@@ -162,12 +153,28 @@
 /* Returns 0 upon success, a negative number upon errors. */
 int ft2232_spi_init(void)
 {
-	int f, ret = 0;
+	int ret = 0;
 	struct ftdi_context *ftdic = &ftdic_context;
 	unsigned char buf[512];
 	int ft2232_vid = FTDI_VID;
 	int ft2232_type = FTDI_FT4232H_PID;
 	enum ftdi_interface ft2232_interface = INTERFACE_B;
+	/*
+	 * The 'H' chips can run with an internal clock of either 12 MHz or 60 MHz,
+	 * but the non-H chips can only run at 12 MHz. We enable the divide-by-5
+	 * prescaler on the former to run on the same speed.
+	 */
+	uint8_t clock_5x = 1;
+	/* In addition to the prescaler mentioned above there is also another
+	 * configurable one on all versions of the chips. Its divisor div can be
+	 * set by a 16 bit value x according to the following formula:
+	 * div = (1 + x) * 2 <-> x = div / 2 - 1
+	 * Hence the expressible divisors are all even numbers between 2 and
+	 * 2^17 (=131072) resulting in SCK frequencies of 6 MHz down to about
+	 * 92 Hz for 12 MHz inputs.
+	 */
+	uint32_t divisor = DEFAULT_DIVISOR;
+	int f;
 	char *arg;
 	double mpsse_clk;
 
@@ -243,6 +250,21 @@
 		}
 	}
 	free(arg);
+	arg = extract_programmer_param("divisor");
+	if (arg && strlen(arg)) {
+		unsigned int temp = 0;
+		char *endptr;
+		temp = strtoul(arg, &endptr, 10);
+		if (*endptr || temp < 2 || temp > 131072 || temp & 0x1) {
+			msg_perr("Error: Invalid SPI frequency divisor specified: \"%s\".\n"
+				 "Valid are even values between 2 and 131072.\n", arg);
+			free(arg);
+			return -2;
+		} else {
+			divisor = (uint32_t)temp;
+		}
+	}
+	free(arg);
 	msg_pdbg("Using device type %s %s ",
 		 get_ft2232_vendorname(ft2232_vid, ft2232_type),
 		 get_ft2232_devicename(ft2232_vid, ft2232_type));
@@ -303,17 +325,15 @@
 
 	msg_pdbg("Set clock divisor\n");
 	buf[0] = 0x86;		/* command "set divisor" */
-	/* valueL/valueH are (desired_divisor - 1) */
-	buf[1] = (DIVIDE_BY - 1) & 0xff;
-	buf[2] = ((DIVIDE_BY - 1) >> 8) & 0xff;
 	if (send_buf(ftdic, buf, 3)) {
 		ret = -6;
 		goto ftdi_err;
 	}
+	buf[1] = (divisor / 2 - 1) & 0xff;
+	buf[2] = ((divisor / 2 - 1) >> 8) & 0xff;
 
-	msg_pdbg("MPSSE clock: %f MHz divisor: %d "
-		 "SPI clock: %f MHz\n", mpsse_clk, DIVIDE_BY,
-		 (double)(mpsse_clk / (((DIVIDE_BY - 1) + 1) * 2)));
+	msg_pdbg("MPSSE clock: %f MHz, divisor: %u, SPI clock: %f MHz\n",
+		 mpsse_clk, divisor, (double)(mpsse_clk / divisor));
 
 	/* Disconnect TDI/DO to TDO/DI for loopback. */
 	msg_pdbg("No loopback of TDI/DO TDO/DI\n");