fpga: virtex2: Add slave serial programming support
authorRobert Hancock <hancock@sedsystems.ca>
Tue, 18 Jun 2019 15:47:16 +0000 (09:47 -0600)
committerMichal Simek <michal.simek@xilinx.com>
Tue, 30 Jul 2019 08:20:06 +0000 (10:20 +0200)
This adds support for slave serial programming, in addition to the
previously supported slave SelectMAP mode. There are two ways that this
can be used:

-Using the clk and wdata callbacks in order to write image data one bit
at a time using pure bit-banging. This works, but is rather painfully
slow with typical image sizes.

-By specifying the wbulkdata callback instead, the image loading process
can be offloaded to SPI hardware. In this mode the clk and wdata
callbacks do not need to be specified. This allows the image to be
loaded much faster, taking only a few seconds with even relatively large
images.

Slave serial programming has been tested on the Kintex-7 series of
FPGAs.

Signed-off-by: Robert Hancock <hancock@sedsystems.ca>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
drivers/fpga/virtex2.c
include/virtex2.h

index 502824424cd7327552966c46ff9643538e139ff1..395736875f60106506033d65dc7f1768137964d8 100644 (file)
@@ -3,6 +3,8 @@
  * (C) Copyright 2002
  * Rich Ireland, Enterasys Networks, rireland@enterasys.com.
  * Keith Outwater, keith_outwater@mvis.com
+ *
+ * Copyright (c) 2019 SED Systems, a division of Calian Ltd.
  */
 
 /*
@@ -141,8 +143,8 @@ static int virtex2_info(xilinx_desc *desc)
 }
 
 /*
- * Virtex-II Slave SelectMap configuration loader. Configuration via
- * SelectMap is as follows:
+ * Virtex-II Slave SelectMap or Serial configuration loader. Configuration
+ * is as follows:
  * 1. Set the FPGA's PROG_B line low.
  * 2. Set the FPGA's PROG_B line high.  Wait for INIT_B to go high.
  * 3. Write data to the SelectMap port.  If INIT_B goes low at any time
@@ -154,7 +156,7 @@ static int virtex2_info(xilinx_desc *desc)
  *    INIT_B and DONE lines.  If both are high, configuration has
  *    succeeded. Congratulations!
  */
-static int virtex2_slave_pre(xilinx_virtex2_slave_selectmap_fns *fn, int cookie)
+static int virtex2_slave_pre(xilinx_virtex2_slave_fns *fn, int cookie)
 {
        unsigned long ts;
 
@@ -243,7 +245,7 @@ static int virtex2_slave_pre(xilinx_virtex2_slave_selectmap_fns *fn, int cookie)
        return FPGA_SUCCESS;
 }
 
-static int virtex2_slave_post(xilinx_virtex2_slave_selectmap_fns *fn,
+static int virtex2_slave_post(xilinx_virtex2_slave_fns *fn,
                              int cookie)
 {
        int ret_val = FPGA_SUCCESS;
@@ -284,11 +286,16 @@ static int virtex2_slave_post(xilinx_virtex2_slave_selectmap_fns *fn,
                        ret_val = FPGA_FAIL;
                        break;
                }
-               (*fn->wdata) (0xff, true, cookie);
-               CONFIG_FPGA_DELAY();
-               (*fn->clk) (false, true, cookie);
-               CONFIG_FPGA_DELAY();
-               (*fn->clk) (true, true, cookie);
+               if (fn->wbulkdata) {
+                       unsigned char dummy = 0xff;
+                       (*fn->wbulkdata)(&dummy, 1, true, cookie);
+               } else {
+                       (*fn->wdata)(0xff, true, cookie);
+                       CONFIG_FPGA_DELAY();
+                       (*fn->clk)(false, true, cookie);
+                       CONFIG_FPGA_DELAY();
+                       (*fn->clk)(true, true, cookie);
+               }
        }
 
        if (ret_val == FPGA_SUCCESS) {
@@ -312,7 +319,7 @@ static int virtex2_slave_post(xilinx_virtex2_slave_selectmap_fns *fn,
 static int virtex2_ssm_load(xilinx_desc *desc, const void *buf, size_t bsize)
 {
        int ret_val = FPGA_FAIL;
-       xilinx_virtex2_slave_selectmap_fns *fn = desc->iface_fns;
+       xilinx_virtex2_slave_fns *fn = desc->iface_fns;
        size_t bytecount = 0;
        unsigned char *data = (unsigned char *)buf;
        int cookie = desc->cookie;
@@ -387,7 +394,7 @@ static int virtex2_ssm_load(xilinx_desc *desc, const void *buf, size_t bsize)
 static int virtex2_ssm_dump(xilinx_desc *desc, const void *buf, size_t bsize)
 {
        int ret_val = FPGA_FAIL;
-       xilinx_virtex2_slave_selectmap_fns *fn = desc->iface_fns;
+       xilinx_virtex2_slave_fns *fn = desc->iface_fns;
 
        if (fn) {
                unsigned char *data = (unsigned char *)buf;
@@ -438,8 +445,71 @@ static int virtex2_ssm_dump(xilinx_desc *desc, const void *buf, size_t bsize)
 
 static int virtex2_ss_load(xilinx_desc *desc, const void *buf, size_t bsize)
 {
-       printf("%s: Slave Serial Loading is unsupported\n", __func__);
-       return FPGA_FAIL;
+       int ret_val = FPGA_FAIL;
+       xilinx_virtex2_slave_fns *fn = desc->iface_fns;
+       unsigned char *data = (unsigned char *)buf;
+       int cookie = desc->cookie;
+
+       ret_val = virtex2_slave_pre(fn, cookie);
+       if (ret_val != FPGA_SUCCESS)
+               return ret_val;
+
+       if (fn->wbulkdata) {
+               /* Load the data in a single chunk */
+               (*fn->wbulkdata)(data, bsize, true, cookie);
+       } else {
+               size_t bytecount = 0;
+
+               /*
+                * Load the data bit by bit
+                */
+               while (bytecount < bsize) {
+                       unsigned char curr_data = data[bytecount++];
+                       int bit;
+
+#ifdef CONFIG_SYS_FPGA_CHECK_CTRLC
+                       if (ctrlc()) {
+                               (*fn->abort) (cookie);
+                               return FPGA_FAIL;
+                       }
+#endif
+
+                       if ((*fn->done)(cookie) == FPGA_SUCCESS) {
+                               PRINTF("%s:%d:done went active early, bytecount = %d\n",
+                                      __func__, __LINE__, bytecount);
+                               break;
+                       }
+
+#ifdef CONFIG_SYS_FPGA_CHECK_ERROR
+                       if ((*fn->init)(cookie)) {
+                               printf("\n%s:%d:  ** Error: INIT asserted during configuration\n",
+                                      __func__, __LINE__);
+                               printf("%zu = buffer offset, %zu = buffer size\n",
+                                      bytecount, bsize);
+                               (*fn->abort)(cookie);
+                               return FPGA_FAIL;
+                       }
+#endif
+
+                       for (bit = 7; bit >= 0; --bit) {
+                               unsigned char curr_bit = (curr_data >> bit) & 1;
+                               (*fn->wdata)(curr_bit, true, cookie);
+                               CONFIG_FPGA_DELAY();
+                               (*fn->clk)(false, true, cookie);
+                               CONFIG_FPGA_DELAY();
+                               (*fn->clk)(true, true, cookie);
+                       }
+
+                       /* Slave serial never uses a busy pin */
+
+#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
+                       if (bytecount % (bsize / 40) == 0)
+                               putc('.');
+#endif
+               }
+       }
+
+       return virtex2_slave_post(fn, cookie);
 }
 
 static int virtex2_ss_dump(xilinx_desc *desc, const void *buf, size_t bsize)
index a48113060e731e465191082750b1fcd9e5229e47..7e8d93f24da45d094ef379f04ae948021a35f6d1 100644 (file)
@@ -11,7 +11,7 @@
 #include <xilinx.h>
 
 /*
- * Slave SelectMap Implementation function table.
+ * Slave SelectMap or Serial Implementation function table.
  */
 typedef struct {
        xilinx_pre_fn   pre;
@@ -24,18 +24,11 @@ typedef struct {
        xilinx_wr_fn    wr;
        xilinx_rdata_fn rdata;
        xilinx_wdata_fn wdata;
+       xilinx_bwr_fn   wbulkdata;
        xilinx_busy_fn  busy;
        xilinx_abort_fn abort;
        xilinx_post_fn  post;
-} xilinx_virtex2_slave_selectmap_fns;
-
-/* Slave Serial Implementation function table */
-typedef struct {
-       xilinx_pgm_fn   pgm;
-       xilinx_clk_fn   clk;
-       xilinx_rdata_fn rdata;
-       xilinx_wdata_fn wdata;
-} xilinx_virtex2_slave_serial_fns;
+} xilinx_virtex2_slave_fns;
 
 #if defined(CONFIG_FPGA_VIRTEX2)
 extern struct xilinx_fpga_op virtex2_op;