| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * (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. |
| */ |
| |
| /* |
| * Configuration support for Xilinx Virtex2 devices. Based |
| * on spartan2.c (Rich Ireland, rireland@enterasys.com). |
| */ |
| |
| #include <common.h> |
| #include <console.h> |
| #include <virtex2.h> |
| #include <linux/delay.h> |
| |
| #if 0 |
| #define FPGA_DEBUG |
| #endif |
| |
| #ifdef FPGA_DEBUG |
| #define PRINTF(fmt, args...) printf(fmt, ##args) |
| #else |
| #define PRINTF(fmt, args...) |
| #endif |
| |
| /* |
| * If the SelectMap interface can be overrun by the processor, define |
| * CONFIG_SYS_FPGA_CHECK_BUSY and/or CONFIG_FPGA_DELAY in the board |
| * configuration file and add board-specific support for checking BUSY status. |
| * By default, assume that the SelectMap interface cannot be overrun. |
| */ |
| #ifndef CONFIG_SYS_FPGA_CHECK_BUSY |
| #undef CONFIG_SYS_FPGA_CHECK_BUSY |
| #endif |
| |
| #ifndef CONFIG_FPGA_DELAY |
| #define CONFIG_FPGA_DELAY() |
| #endif |
| |
| /* |
| * Check for errors during configuration by default |
| */ |
| #ifndef CONFIG_SYS_FPGA_CHECK_ERROR |
| #define CONFIG_SYS_FPGA_CHECK_ERROR |
| #endif |
| |
| /* |
| * The default timeout in mS for INIT_B to deassert after PROG_B has |
| * been deasserted. Per the latest Virtex II Handbook (page 347), the |
| * max time from PORG_B deassertion to INIT_B deassertion is 4uS per |
| * data frame for the XC2V8000. The XC2V8000 has 2860 data frames |
| * which yields 11.44 mS. So let's make it bigger in order to handle |
| * an XC2V1000, if anyone can ever get ahold of one. |
| */ |
| #ifndef CONFIG_SYS_FPGA_WAIT_INIT |
| #define CONFIG_SYS_FPGA_WAIT_INIT CONFIG_SYS_HZ / 2 /* 500 ms */ |
| #endif |
| |
| /* |
| * The default timeout for waiting for BUSY to deassert during configuration. |
| * This is normally not necessary since for most reasonable configuration |
| * clock frequencies (i.e. 66 MHz or less), BUSY monitoring is unnecessary. |
| */ |
| #ifndef CONFIG_SYS_FPGA_WAIT_BUSY |
| #define CONFIG_SYS_FPGA_WAIT_BUSY CONFIG_SYS_HZ / 200 /* 5 ms*/ |
| #endif |
| |
| /* Default timeout for waiting for FPGA to enter operational mode after |
| * configuration data has been written. |
| */ |
| #ifndef CONFIG_SYS_FPGA_WAIT_CONFIG |
| #define CONFIG_SYS_FPGA_WAIT_CONFIG CONFIG_SYS_HZ / 5 /* 200 ms */ |
| #endif |
| |
| 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); |
| |
| static int virtex2_ss_load(xilinx_desc *desc, const void *buf, size_t bsize); |
| static int virtex2_ss_dump(xilinx_desc *desc, const void *buf, size_t bsize); |
| |
| static int virtex2_load(xilinx_desc *desc, const void *buf, size_t bsize, |
| bitstream_type bstype, int flags) |
| { |
| int ret_val = FPGA_FAIL; |
| |
| switch (desc->iface) { |
| case slave_serial: |
| PRINTF("%s: Launching Slave Serial Load\n", __func__); |
| ret_val = virtex2_ss_load(desc, buf, bsize); |
| break; |
| |
| case slave_selectmap: |
| PRINTF("%s: Launching Slave Parallel Load\n", __func__); |
| ret_val = virtex2_ssm_load(desc, buf, bsize); |
| break; |
| |
| default: |
| printf("%s: Unsupported interface type, %d\n", |
| __func__, desc->iface); |
| } |
| return ret_val; |
| } |
| |
| static int virtex2_dump(xilinx_desc *desc, const void *buf, size_t bsize) |
| { |
| int ret_val = FPGA_FAIL; |
| |
| switch (desc->iface) { |
| case slave_serial: |
| PRINTF("%s: Launching Slave Serial Dump\n", __func__); |
| ret_val = virtex2_ss_dump(desc, buf, bsize); |
| break; |
| |
| case slave_parallel: |
| PRINTF("%s: Launching Slave Parallel Dump\n", __func__); |
| ret_val = virtex2_ssm_dump(desc, buf, bsize); |
| break; |
| |
| default: |
| printf("%s: Unsupported interface type, %d\n", |
| __func__, desc->iface); |
| } |
| return ret_val; |
| } |
| |
| static int virtex2_info(xilinx_desc *desc) |
| { |
| return FPGA_SUCCESS; |
| } |
| |
| /* |
| * 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 |
| * this process, a configuration error (most likely CRC failure) has |
| * ocurred. At this point a status word may be read from the |
| * SelectMap interface to determine the source of the problem (You |
| * could, for instance, put this in your 'abort' function handler). |
| * 4. After all data has been written, test the state of the FPGA |
| * INIT_B and DONE lines. If both are high, configuration has |
| * succeeded. Congratulations! |
| */ |
| static int virtex2_slave_pre(xilinx_virtex2_slave_fns *fn, int cookie) |
| { |
| unsigned long ts; |
| |
| PRINTF("%s:%d: Start with interface functions @ 0x%p\n", |
| __func__, __LINE__, fn); |
| |
| if (!fn) { |
| printf("%s:%d: NULL Interface function table!\n", |
| __func__, __LINE__); |
| return FPGA_FAIL; |
| } |
| |
| /* Gotta split this one up (so the stack won't blow??) */ |
| PRINTF("%s:%d: Function Table:\n" |
| " base 0x%p\n" |
| " struct 0x%p\n" |
| " pre 0x%p\n" |
| " prog 0x%p\n" |
| " init 0x%p\n" |
| " error 0x%p\n", |
| __func__, __LINE__, |
| &fn, fn, fn->pre, fn->pgm, fn->init, fn->err); |
| PRINTF(" clock 0x%p\n" |
| " cs 0x%p\n" |
| " write 0x%p\n" |
| " rdata 0x%p\n" |
| " wdata 0x%p\n" |
| " busy 0x%p\n" |
| " abort 0x%p\n" |
| " post 0x%p\n\n", |
| fn->clk, fn->cs, fn->wr, fn->rdata, fn->wdata, |
| fn->busy, fn->abort, fn->post); |
| |
| #ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK |
| printf("Initializing FPGA Device %d...\n", cookie); |
| #endif |
| /* |
| * Run the pre configuration function if there is one. |
| */ |
| if (*fn->pre) |
| (*fn->pre)(cookie); |
| |
| /* |
| * Assert the program line. The minimum pulse width for |
| * Virtex II devices is 300 nS (Tprogram parameter in datasheet). |
| * There is no maximum value for the pulse width. Check to make |
| * sure that INIT_B goes low after assertion of PROG_B |
| */ |
| (*fn->pgm)(true, true, cookie); |
| udelay(10); |
| ts = get_timer(0); |
| do { |
| if (get_timer(ts) > CONFIG_SYS_FPGA_WAIT_INIT) { |
| printf("%s:%d: ** Timeout after %d ticks waiting for INIT to assert.\n", |
| __func__, __LINE__, CONFIG_SYS_FPGA_WAIT_INIT); |
| (*fn->abort)(cookie); |
| return FPGA_FAIL; |
| } |
| } while (!(*fn->init)(cookie)); |
| |
| (*fn->pgm)(false, true, cookie); |
| CONFIG_FPGA_DELAY(); |
| if (fn->clk) |
| (*fn->clk)(true, true, cookie); |
| |
| /* |
| * Start a timer and wait for INIT_B to go high |
| */ |
| ts = get_timer(0); |
| do { |
| CONFIG_FPGA_DELAY(); |
| if (get_timer(ts) > CONFIG_SYS_FPGA_WAIT_INIT) { |
| printf("%s:%d: ** Timeout after %d ticks waiting for INIT to deassert.\n", |
| __func__, __LINE__, CONFIG_SYS_FPGA_WAIT_INIT); |
| (*fn->abort)(cookie); |
| return FPGA_FAIL; |
| } |
| } while ((*fn->init)(cookie) && (*fn->busy)(cookie)); |
| |
| if (fn->wr) |
| (*fn->wr)(true, true, cookie); |
| if (fn->cs) |
| (*fn->cs)(true, true, cookie); |
| |
| mdelay(10); |
| return FPGA_SUCCESS; |
| } |
| |
| static int virtex2_slave_post(xilinx_virtex2_slave_fns *fn, |
| int cookie) |
| { |
| int ret_val = FPGA_SUCCESS; |
| int num_done = 0; |
| unsigned long ts; |
| |
| /* |
| * Finished writing the data; deassert FPGA CS_B and WRITE_B signals. |
| */ |
| CONFIG_FPGA_DELAY(); |
| if (fn->cs) |
| (*fn->cs)(false, true, cookie); |
| if (fn->wr) |
| (*fn->wr)(false, true, cookie); |
| |
| #ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK |
| putc('\n'); |
| #endif |
| |
| /* |
| * Check for successful configuration. FPGA INIT_B and DONE |
| * should both be high upon successful configuration. Continue pulsing |
| * clock with data set to all ones until DONE is asserted and for 8 |
| * clock cycles afterwards. |
| */ |
| ts = get_timer(0); |
| while (true) { |
| if ((*fn->done)(cookie) == FPGA_SUCCESS && |
| !((*fn->init)(cookie))) { |
| if (num_done++ >= 8) |
| break; |
| } |
| |
| if (get_timer(ts) > CONFIG_SYS_FPGA_WAIT_CONFIG) { |
| printf("%s:%d: ** Timeout after %d ticks waiting for DONE to assert and INIT to deassert\n", |
| __func__, __LINE__, CONFIG_SYS_FPGA_WAIT_CONFIG); |
| (*fn->abort)(cookie); |
| ret_val = FPGA_FAIL; |
| break; |
| } |
| 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) { |
| #ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK |
| printf("Initialization of FPGA device %d complete\n", cookie); |
| #endif |
| /* |
| * Run the post configuration function if there is one. |
| */ |
| if (*fn->post) |
| (*fn->post)(cookie); |
| } else { |
| #ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK |
| printf("** Initialization of FPGA device %d FAILED\n", |
| cookie); |
| #endif |
| } |
| return ret_val; |
| } |
| |
| static int virtex2_ssm_load(xilinx_desc *desc, const void *buf, size_t bsize) |
| { |
| int ret_val = FPGA_FAIL; |
| xilinx_virtex2_slave_fns *fn = desc->iface_fns; |
| size_t bytecount = 0; |
| 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; |
| |
| /* |
| * Load the data byte by byte |
| */ |
| while (bytecount < bsize) { |
| #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 |
| |
| (*fn->wdata)(data[bytecount++], true, cookie); |
| CONFIG_FPGA_DELAY(); |
| |
| /* |
| * Cycle the clock pin |
| */ |
| (*fn->clk)(false, true, cookie); |
| CONFIG_FPGA_DELAY(); |
| (*fn->clk)(true, true, cookie); |
| |
| #ifdef CONFIG_SYS_FPGA_CHECK_BUSY |
| ts = get_timer(0); |
| while ((*fn->busy)(cookie)) { |
| if (get_timer(ts) > CONFIG_SYS_FPGA_WAIT_BUSY) { |
| printf("%s:%d: ** Timeout after %d ticks waiting for BUSY to deassert\n", |
| __func__, __LINE__, |
| CONFIG_SYS_FPGA_WAIT_BUSY); |
| (*fn->abort)(cookie); |
| return FPGA_FAIL; |
| } |
| } |
| #endif |
| |
| #ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK |
| if (bytecount % (bsize / 40) == 0) |
| putc('.'); |
| #endif |
| } |
| |
| return virtex2_slave_post(fn, cookie); |
| } |
| |
| /* |
| * Read the FPGA configuration data |
| */ |
| static int virtex2_ssm_dump(xilinx_desc *desc, const void *buf, size_t bsize) |
| { |
| int ret_val = FPGA_FAIL; |
| xilinx_virtex2_slave_fns *fn = desc->iface_fns; |
| |
| if (fn) { |
| unsigned char *data = (unsigned char *)buf; |
| size_t bytecount = 0; |
| int cookie = desc->cookie; |
| |
| printf("Starting Dump of FPGA Device %d...\n", cookie); |
| |
| (*fn->cs)(true, true, cookie); |
| (*fn->clk)(true, true, cookie); |
| |
| while (bytecount < bsize) { |
| #ifdef CONFIG_SYS_FPGA_CHECK_CTRLC |
| if (ctrlc()) { |
| (*fn->abort)(cookie); |
| return FPGA_FAIL; |
| } |
| #endif |
| /* |
| * Cycle the clock and read the data |
| */ |
| (*fn->clk)(false, true, cookie); |
| (*fn->clk)(true, true, cookie); |
| (*fn->rdata)(&data[bytecount++], cookie); |
| #ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK |
| if (bytecount % (bsize / 40) == 0) |
| putc('.'); |
| #endif |
| } |
| |
| /* |
| * Deassert CS_B and cycle the clock to deselect the device. |
| */ |
| (*fn->cs)(false, false, cookie); |
| (*fn->clk)(false, true, cookie); |
| (*fn->clk)(true, true, cookie); |
| |
| #ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK |
| putc('\n'); |
| #endif |
| puts("Done.\n"); |
| } else { |
| printf("%s:%d: NULL Interface function table!\n", |
| __func__, __LINE__); |
| } |
| return ret_val; |
| } |
| |
| static int virtex2_ss_load(xilinx_desc *desc, const void *buf, size_t bsize) |
| { |
| 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) |
| { |
| printf("%s: Slave Serial Dumping is unsupported\n", __func__); |
| return FPGA_FAIL; |
| } |
| |
| /* vim: set ts=4 tw=78: */ |
| |
| struct xilinx_fpga_op virtex2_op = { |
| .load = virtex2_load, |
| .dump = virtex2_dump, |
| .info = virtex2_info, |
| }; |