mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-11 15:37:23 +00:00
175dccd710
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>
527 lines
13 KiB
C
527 lines
13 KiB
C
// 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>
|
|
|
|
#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
|
|
|
|
#ifndef CONFIG_SYS_FPGA_PROG_FEEDBACK
|
|
#define CONFIG_SYS_FPGA_PROG_FEEDBACK
|
|
#endif
|
|
|
|
/*
|
|
* Don't allow config cycle to be interrupted
|
|
*/
|
|
#ifndef CONFIG_SYS_FPGA_CHECK_CTRLC
|
|
#undef CONFIG_SYS_FPGA_CHECK_CTRLC
|
|
#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 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,
|
|
};
|