blob: 51b8d3120567925f6b163ace0b2ee29501806e76 [file] [log] [blame]
// 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,
};