1077 lines
26 KiB
C
1077 lines
26 KiB
C
/******************************************************************************
|
|
* (c) COPYRIGHT 2015 by NetModule AG, Switzerland. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS for A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*****************************************************************************/
|
|
#undef DEBUG
|
|
#include <common.h>
|
|
#include <dm.h>
|
|
#include <dm/device.h>
|
|
#include <asm/gpio.h>
|
|
#include <asm/io.h>
|
|
#include <errno.h>
|
|
#include <image.h>
|
|
#include <mapmem.h>
|
|
#include "lattice/SSPIEm.h"
|
|
#include "lattice/debug.h"
|
|
|
|
#define DEBUG_TEST debug("%s: %d\n",__FILE__, __LINE__);
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
enum nbhw_fpga_type_num {
|
|
FPGA_XILINX,
|
|
FPGA_LATTICE,
|
|
FPGA_LATTICE_SSPI,
|
|
FPGA_MAX
|
|
};
|
|
|
|
struct nbhw_fpga_type {
|
|
enum nbhw_fpga_type_num type;
|
|
const char* name;
|
|
};
|
|
|
|
struct nbhw_fpga_type fpga_types[] = {
|
|
{ FPGA_XILINX, "xilinx"},
|
|
{ FPGA_LATTICE, "lattice"},
|
|
{ FPGA_LATTICE_SSPI, "lattice-sspi"}
|
|
};
|
|
|
|
struct nbhw_fpga_priv {
|
|
int signature;
|
|
struct gpio_desc ss;
|
|
struct gpio_desc sck;
|
|
struct gpio_desc sdi;
|
|
#ifdef CONFIG_FPGA_LATTICE_SSPI
|
|
struct gpio_desc sdo;
|
|
#endif
|
|
struct gpio_desc reset;
|
|
struct gpio_desc reset_logic;
|
|
struct gpio_desc cdone;
|
|
struct gpio_desc cinit;
|
|
enum nbhw_fpga_type_num fpga_type;
|
|
u32 fpga_id;
|
|
void *regs;
|
|
};
|
|
|
|
struct udevice *fpga_dev;
|
|
|
|
struct nbhw_fpga_priv* FPGA_PRIV = 0;
|
|
|
|
void tickdelay(void)
|
|
{
|
|
uint64_t curtick;
|
|
curtick = get_ticks(); /* get current timestamp */
|
|
while (get_ticks() < curtick+1); /* loop till event */
|
|
}
|
|
|
|
#ifdef CONFIG_FPGA_LATTICE_SSPI
|
|
|
|
static inline u8 spi_read_sspi(const struct nbhw_fpga_priv *priv)
|
|
{
|
|
int i;
|
|
u8 d;
|
|
u8 res = 0;
|
|
|
|
for (i=0; i<8; i++)
|
|
{
|
|
d = dm_gpio_get_value(&priv->sdo);
|
|
|
|
res = res << 1;
|
|
res |= d;
|
|
|
|
dm_gpio_set_value(&priv->sck, 1);
|
|
//ndelay(50);
|
|
tickdelay();
|
|
|
|
dm_gpio_set_value(&priv->sck, 0);
|
|
//ndelay(50);
|
|
tickdelay();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static inline void spi_write_sspi(const struct nbhw_fpga_priv *priv, u8 data)
|
|
{
|
|
int i;
|
|
u32 data_write = 0;
|
|
|
|
for (i=0; i<8; i++)
|
|
{
|
|
data_write = (data & 0x80) ? 1 : 0;
|
|
dm_gpio_set_value(&priv->sdi, data_write);
|
|
//ndelay(50);
|
|
tickdelay();
|
|
|
|
/* Read data on rising edge */
|
|
dm_gpio_set_value(&priv->sck, 1);
|
|
//ndelay(50);
|
|
tickdelay();
|
|
|
|
/* Clear clk bit and put data on the line */
|
|
dm_gpio_set_value(&priv->sck, 0);
|
|
data = data << 1;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
static inline void spi_write(const struct nbhw_fpga_priv *priv, u8 data)
|
|
{
|
|
int i;
|
|
u32 data_write = 0;
|
|
|
|
dm_gpio_set_value(&priv->sck, 1);
|
|
|
|
for (i=0; i<8; i++)
|
|
{
|
|
data_write = (data & 0x80) ? 1 : 0;
|
|
/* Clear clk bit and put data on the line */
|
|
dm_gpio_set_value(&priv->sck, 0);
|
|
dm_gpio_set_value(&priv->sdi, data_write);
|
|
tickdelay();
|
|
/* Read data on rising edge */
|
|
dm_gpio_set_value(&priv->sck, 1);
|
|
data = data << 1;
|
|
tickdelay();
|
|
}
|
|
}
|
|
|
|
static int fpga_verify(struct nbhw_fpga_priv *priv)
|
|
{
|
|
/* Check, if the FPGA is working */
|
|
char fpga_type[128];
|
|
u16 signature = readw(priv->regs);
|
|
u16 fpga_version = readw(priv->regs + 0x02);
|
|
u8 fpga_major = (fpga_version >> 8) & 0xFF;
|
|
u8 fpga_minor = (fpga_version >> 0) & 0xFF;
|
|
|
|
debug("Verify FPGA programming\n");
|
|
|
|
if (signature == 0xa501)
|
|
{
|
|
strcpy(fpga_type, "XC3S50A-4VQ100I top layer HW14");
|
|
} else if (signature == 0xa502) {
|
|
strcpy(fpga_type, "XC3S200A-4VQ100I top layer HW14");
|
|
} else if (signature == 0xa511) {
|
|
strcpy(fpga_type, "XC3S50A-4FTG256I bottom layer HW14");
|
|
} else if (signature == 0xa512) {
|
|
strcpy(fpga_type, "XC3S200A-4FTG256I bottom layer HW14");
|
|
} else if (signature == 0x4004) {
|
|
strcpy(fpga_type, "ICE40HX4K-CB132 HW17");
|
|
} else if (signature == 0x4184) {
|
|
strcpy(fpga_type, "ICE40HX4K-CB132 HW18 V1");
|
|
} else if (signature == 0x012f) {
|
|
strcpy(fpga_type, "LFE5U-12F-6BG381I HW18 V2");
|
|
} else if (signature == 0x4201) {
|
|
strcpy(fpga_type, "LFE5U-12F-6BG381I HW29");
|
|
} else {
|
|
priv->signature = signature;
|
|
goto abort;
|
|
}
|
|
|
|
printf(" Detected %s FPGA (Version: %d.%d)\n", fpga_type, fpga_major, fpga_minor);
|
|
|
|
priv->signature = signature;
|
|
|
|
debug("Local priv: %p\n",priv);
|
|
|
|
return 1;
|
|
|
|
abort:
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_FPGA_LATTICE_SSPI
|
|
|
|
#define print_out_string printf
|
|
extern unsigned int a_uiRowCount;
|
|
void printError(int code){
|
|
char Message[512];
|
|
sprintf(Message, "Error Code: %d\n\n", code);
|
|
print_out_string(Message);
|
|
switch(code){
|
|
case ERROR_INIT_ALGO:
|
|
print_out_string("Initialize algorithm file fail.\n");
|
|
break;
|
|
case ERROR_INIT_DATA:
|
|
print_out_string("Initialize data file fail.\n");
|
|
break;
|
|
case ERROR_INIT_VERSION:
|
|
print_out_string("Version not supported.\n");
|
|
break;
|
|
case ERROR_INIT_CHECKSUM:
|
|
print_out_string("Header checksum fail.\n");
|
|
break;
|
|
case ERROR_INIT_SPI:
|
|
print_out_string("Initialize SPI fail.\n");
|
|
break;
|
|
case ERROR_INIT:
|
|
print_out_string("Initialization fail.\n");
|
|
break;
|
|
case ERROR_PROC_ALGO:
|
|
print_out_string("Incorrect algorithm format.\n");
|
|
break;
|
|
case ERROR_PROC_DATA:
|
|
print_out_string("Invalid data.\n");
|
|
break;
|
|
case ERROR_PROC_HARDWARE:
|
|
print_out_string("Hardware fail.\n");
|
|
break;
|
|
case ERROR_VERIFICATION:
|
|
print_out_string("Verification fail.\n");
|
|
if(a_uiRowCount > 0)
|
|
{
|
|
sprintf(Message, "Failed on Frame %d\n",a_uiRowCount);
|
|
print_out_string(Message);
|
|
}
|
|
break;
|
|
case ERROR_IDCODE:
|
|
print_out_string("IDCODE verification fail.\n");
|
|
break;
|
|
case ERROR_USERCODE:
|
|
print_out_string("USERCODE verification fail.\n");
|
|
break;
|
|
case ERROR_SED:
|
|
print_out_string("SED CRC verification fail.\n");
|
|
break;
|
|
case ERROR_TAG:
|
|
print_out_string("TAG Memory verification fail.\n");
|
|
break;
|
|
case ERROR_LOOP_COND:
|
|
print_out_string("LOOP condition fail.\n");
|
|
break;
|
|
default:
|
|
print_out_string("Process fail.\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
static void put_in_prog_mode(const struct nbhw_fpga_priv *priv)
|
|
{
|
|
/* Change the muxing to GPIO */
|
|
writel(0x00055555, MVEBU_MPP_BASE + 0x10);
|
|
writel(0x06605500, MVEBU_MPP_BASE + 0x14);
|
|
|
|
printf("Put FPGA in programming mode\n");
|
|
dm_gpio_set_value(&priv->ss, 1);
|
|
udelay(10);
|
|
|
|
dm_gpio_set_value(&priv->ss, 0);
|
|
dm_gpio_set_value(&priv->sck, 1);
|
|
dm_gpio_set_value(&priv->reset, 1);
|
|
|
|
udelay(1);
|
|
|
|
/* Take FPGA out of reset */
|
|
dm_gpio_set_value(&priv->reset, 0);
|
|
|
|
/* Wait for at least 800 us according to lattice manual */
|
|
udelay(800);
|
|
}
|
|
|
|
#ifdef CONFIG_FPGA_LATTICE_SSPI
|
|
|
|
static void put_in_prog_mode_lattice_sspi(const struct nbhw_fpga_priv *priv)
|
|
{
|
|
/* Change the muxing to GPIO */
|
|
writel(0x00055555, MVEBU_MPP_BASE + 0x10);
|
|
writel(0x06605500, MVEBU_MPP_BASE + 0x14);
|
|
|
|
// Inital GPIO states
|
|
dm_gpio_set_value(&priv->reset,1); // PROGRAMn
|
|
dm_gpio_set_value(&priv->sck, 0); // SCLK
|
|
dm_gpio_set_value(&priv->ss, 1); // CSn
|
|
dm_gpio_set_value(&priv->sdi, 0); // Master Out Serial in
|
|
dm_gpio_set_value(&priv->reset_logic, 0); // TODO : m?ssen wir noch anschauen
|
|
|
|
// inputs
|
|
// dm_gpio_get_value(&priv->sdo ); // Master In Serial Out
|
|
// dm_gpio_get_value(&priv->cdone ); // FPGA Done
|
|
// dm_gpio_get_value(&priv->cinit ); // FPGG Init
|
|
udelay(1);
|
|
|
|
// Pull PROGRAMn for more than t_prgm = 110ns
|
|
dm_gpio_set_value(&priv->reset, 0); // PROGRAMn
|
|
udelay(1);
|
|
// release PROGRAMn
|
|
dm_gpio_set_value(&priv->reset, 1); // PROGRAMn
|
|
udelay(50000);
|
|
|
|
debug("Initialised Reset with pulling PROGRAM~\n");
|
|
}
|
|
|
|
#endif
|
|
|
|
static void clean_up_after_programming(const struct nbhw_fpga_priv *priv)
|
|
{
|
|
/* Change the muxing back to Devbus */
|
|
writel(0x55555555, MVEBU_MPP_BASE + 0x10);
|
|
writel(0x06605505, MVEBU_MPP_BASE + 0x14);
|
|
|
|
dm_gpio_set_value(&priv->reset_logic, 0);
|
|
udelay(100);
|
|
dm_gpio_set_value(&priv->reset_logic, 1);
|
|
udelay(100);
|
|
}
|
|
|
|
static int fpga_load_bitstream (const struct nbhw_fpga_priv *priv, const u8* data, int num_bytes)
|
|
{
|
|
int i;
|
|
|
|
printf(" Loading FPGA bitstream from 0x%p, %d bytes\n", data, num_bytes);
|
|
|
|
put_in_prog_mode(priv);
|
|
|
|
printf(" Sending configuration bitstream...\n");
|
|
|
|
for ( i=0; i<num_bytes; i++ )
|
|
{
|
|
spi_write(priv, *(data++));
|
|
}
|
|
|
|
debug("Configuration sent\n");
|
|
|
|
/* Send some extra clocks for startup */
|
|
for ( i=0; i<100; i++)
|
|
{
|
|
spi_write(priv, 0);
|
|
}
|
|
|
|
debug("Extra clocks sent\n");
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int fpga_check_bitstream_lattice(const struct nbhw_fpga_priv *priv,
|
|
const u8* fpgadata, int dataNumBytes,
|
|
const u8** pBitstreamAddr, int* pBitstreamSize)
|
|
{
|
|
printf ("Checking Lattice FPGA bitstream\n");
|
|
*pBitstreamAddr = (u8*)fpgadata;
|
|
*pBitstreamSize = dataNumBytes;
|
|
|
|
/* Do not touch pSize should send the whole file for now */
|
|
|
|
if ((priv->fpga_type != FPGA_LATTICE) && (priv->fpga_type != FPGA_LATTICE_SSPI)) {
|
|
return -1;
|
|
}
|
|
|
|
if ((priv->fpga_id != 0x00000000) && (priv->fpga_id != 0xFFFFFFFF)) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_FPGA_LATTICE_SSPI
|
|
|
|
static int fpga_load_bitstream_lattice_sspi (const struct nbhw_fpga_priv *priv,
|
|
const u8* pBitstreamAddr, int pBitstreamSize,
|
|
const u8* pAlgoAddr, int pAlgoSize)
|
|
{
|
|
int siRetCode;
|
|
siRetCode = SSPIEm_preset(pAlgoAddr, pAlgoSize, pBitstreamAddr, pBitstreamSize);
|
|
siRetCode = SSPIEm(0xFFFFFFFF);
|
|
if ( siRetCode != 2 ) {
|
|
printError(siRetCode);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
#define CHECKMARKER(index, a, b, c, d) ((fpgadata[index]==a) && (fpgadata[index+1]==b) \
|
|
&& (fpgadata[index+2]==c) && (fpgadata[index+3]==d))
|
|
|
|
#define GETSIZE(index) ((((int)fpgadata[index]) << 24) | \
|
|
(((int)fpgadata[index+1]) << 16) | \
|
|
(((int)fpgadata[index+2]) << 8) | \
|
|
(((int)fpgadata[index+3])))
|
|
|
|
static int fpga_check_bitstream_lattice_sspi(const struct nbhw_fpga_priv *priv,
|
|
const u8* fpgadata, int dataNumBytes,
|
|
const u8** pBitstreamAddr, int* pBitstreamSize,
|
|
const u8** pAlgoAddr, int* pAlgoSize)
|
|
{
|
|
int pos = 0;
|
|
|
|
printf ("Checking Lattice SSPI FPGA bitstream\n");
|
|
|
|
/* Check for SEA start tag */
|
|
if (!CHECKMARKER(pos, 0xae, 0x00, 0x00, 0x01)) goto abort;
|
|
pos += 4;
|
|
|
|
*pAlgoSize = GETSIZE(pos);
|
|
if (((*pAlgoSize)+pos) > dataNumBytes) goto abort;
|
|
pos += 4;
|
|
|
|
*pAlgoAddr = fpgadata + pos;
|
|
pos += *pAlgoSize;
|
|
|
|
/* Check for SED start tag */
|
|
if (!CHECKMARKER(pos, 0xae, 0x00, 0x00, 0x02)) goto abort;
|
|
pos += 4;
|
|
|
|
*pBitstreamSize = GETSIZE(pos);
|
|
if (((*pBitstreamSize)+pos) > dataNumBytes) goto abort;
|
|
pos += 4;
|
|
|
|
*pBitstreamAddr = fpgadata + pos;
|
|
pos += *pBitstreamSize;
|
|
|
|
/* Check for EOF tag */
|
|
if (!CHECKMARKER(pos, 0xae, 0xff, 0xff, 0xff)) goto abort;
|
|
|
|
return 0;
|
|
|
|
abort:
|
|
return -1;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* Legacy Xilinx check ported from PPC, seems to work */
|
|
static int fpga_check_bitstream_xilinx(const struct nbhw_fpga_priv *priv,
|
|
const u8* fpgadata, int dataNumBytes,
|
|
const u8** pBitstreamAddr, int* pBitstreamSize)
|
|
{
|
|
unsigned int length;
|
|
unsigned int codesize;
|
|
char buffer[80];
|
|
const u8* dataptr;
|
|
unsigned int i;
|
|
u32 fpgaid;
|
|
const char *p;
|
|
|
|
printf("Checking bitstream at 0x%p\n", fpgadata);
|
|
|
|
*pBitstreamSize = 0;
|
|
*pBitstreamAddr = NULL;
|
|
|
|
dataptr = fpgadata;
|
|
|
|
/* skip the first bytes of the bitsteam, their meaning is unknown */
|
|
length = (*dataptr << 8) + *(dataptr+1);
|
|
dataptr+=2;
|
|
dataptr+=length;
|
|
|
|
/* get design name (identifier, length, string) */
|
|
length = (*dataptr << 8) + *(dataptr+1);
|
|
dataptr+=2;
|
|
if (*dataptr++ != 0x61) {
|
|
printf("%s: Design name identifier not recognized in bitstream\n",
|
|
__FUNCTION__ );
|
|
return -1;
|
|
}
|
|
|
|
length = (*dataptr << 8) + *(dataptr+1);
|
|
dataptr+=2;
|
|
memset(buffer, 0, sizeof(buffer));
|
|
for(i=0;i<length && i<(sizeof(buffer)-1);i++)
|
|
buffer[i] = *dataptr++;
|
|
|
|
printf(" design filename = \"%s\"\n", buffer);
|
|
|
|
p = strstr(buffer, "UserID=0x");
|
|
if (p) {
|
|
p += 9;
|
|
fpgaid = 0;
|
|
for(i = 0;i < 8; i++) {
|
|
fpgaid <<= 4;
|
|
if ((p[i] >= '0') && (p[i] <= '9')) {
|
|
fpgaid |= (u32)(p[i]-'0');
|
|
} else if ((p[i] >= 'A') && (p[i] <= 'F')) {
|
|
fpgaid |= (u32)(p[i]-'A'+10);
|
|
} else if ((p[i] >= 'a') && (p[i] <= 'f')) {
|
|
fpgaid |= (u32)(p[i]-'a'+10);
|
|
} else {
|
|
printf("%s: User identifier not valid in bitstream\n",
|
|
__FUNCTION__ );
|
|
return -1;
|
|
}
|
|
}
|
|
/* verify fpga identifier */
|
|
if (priv->fpga_id != 0xFFFFFFFF) {
|
|
if (fpgaid != priv->fpga_id) {
|
|
printf("%s: User identifier not matched in bitstream\n",
|
|
__FUNCTION__ );
|
|
return -1;
|
|
}
|
|
}
|
|
} else {
|
|
printf("%s: User identifier not found in bitstream\n",
|
|
__FUNCTION__ );
|
|
return -1;
|
|
}
|
|
|
|
/* get part number (identifier, length, string) */
|
|
if (*dataptr++ != 0x62) {
|
|
printf("%s: Part number identifier not recognized in bitstream\n",
|
|
__FUNCTION__ );
|
|
return -1;
|
|
}
|
|
|
|
length = (*dataptr << 8) + *(dataptr+1);
|
|
dataptr+=2;
|
|
memset(buffer, 0, sizeof(buffer));
|
|
for(i=0;i<length && i<(sizeof(buffer)-1);i++)
|
|
buffer[i] = *dataptr++;
|
|
printf(" part number = \"%s\"\n", buffer);
|
|
|
|
/* get date (identifier, length, string) */
|
|
if (*dataptr++ != 0x63) {
|
|
printf("%s: Date identifier not recognized in bitstream\n",
|
|
__FUNCTION__);
|
|
return -1;
|
|
}
|
|
|
|
length = (*dataptr << 8) + *(dataptr+1);
|
|
dataptr+=2;
|
|
memset(buffer, 0, sizeof(buffer));
|
|
for(i=0;i<length && i<(sizeof(buffer)-1);i++)
|
|
buffer[i] = *dataptr++;
|
|
|
|
printf(" date = \"%s\"\n", buffer);
|
|
|
|
/* get time (identifier, length, string) */
|
|
if (*dataptr++ != 0x64) {
|
|
printf("%s: Time identifier not recognized in bitstream\n",__FUNCTION__);
|
|
return -1;
|
|
}
|
|
|
|
length = (*dataptr << 8) + *(dataptr+1);
|
|
dataptr+=2;
|
|
for(i=0;i<length;i++)
|
|
buffer[i] = *dataptr++;
|
|
|
|
printf(" time = \"%s\"\n", buffer);
|
|
|
|
/* get fpga data length (identifier, length) */
|
|
if (*dataptr++ != 0x65) {
|
|
printf("%s: Data length identifier not recognized in bitstream\n",
|
|
__FUNCTION__);
|
|
return -1;
|
|
}
|
|
|
|
codesize = ((unsigned int) *(dataptr+0) << 24) +
|
|
((unsigned int) *(dataptr+1) << 16) +
|
|
((unsigned int) *(dataptr+2) << 8) +
|
|
((unsigned int) *(dataptr+3) << 0);
|
|
dataptr+=4;
|
|
printf(" bytes in bitstream = %d\n", codesize);
|
|
printf(" headersize = %d\n", (u32)dataptr - (u32)fpgadata);
|
|
|
|
*pBitstreamAddr = (u8*)dataptr;
|
|
*pBitstreamSize = codesize;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fpga_check_bitstream(const struct nbhw_fpga_priv *priv,
|
|
const u8* fpgadata, int dataNumBytes,
|
|
const u8** pBitstreamAddr, int* pBitstreamSize,
|
|
const u8** pAlgoAddr, int* pAlgoSize)
|
|
{
|
|
if (pBitstreamSize) *pBitstreamSize = 0;
|
|
if (pAlgoSize) *pAlgoSize = 0;
|
|
|
|
debug("check bitstream %d, %0x, %0x\n", *pBitstreamSize, fpgadata[0],
|
|
fpgadata[1]);
|
|
|
|
/* Lattice header starts with 0xFF00 followed by comment */
|
|
if (fpgadata[0] == 0xFF && fpgadata[1] == 0x00) {
|
|
return fpga_check_bitstream_lattice(priv, fpgadata, dataNumBytes, pBitstreamAddr, pBitstreamSize);
|
|
}
|
|
#ifdef CONFIG_FPGA_LATTICE_SSPI
|
|
/* Lattice SSPI header starts with 0xae 0x00 0x00 0x01 (SEA tag) */
|
|
else if(fpgadata[0] == 0xae && fpgadata[1] == 0x00) {
|
|
return fpga_check_bitstream_lattice_sspi(priv, fpgadata, dataNumBytes,
|
|
pBitstreamAddr, pBitstreamSize,
|
|
pAlgoAddr, pAlgoSize);
|
|
}
|
|
#else
|
|
/* Xilinx header starts with 0x00 0x09 which means header with 0x09 length */
|
|
if(fpgadata[0] == 0x00 && fpgadata[1] == 0x09) {
|
|
return fpga_check_bitstream_xilinx(priv, fpgadata, dataNumBytes, pBitstreamAddr, pBitstreamSize);
|
|
}
|
|
#endif
|
|
printf("Unknown bitstream file format (not Xilinx nor Lattice)\n");
|
|
return -1;
|
|
}
|
|
|
|
static int fpga_boot_buffer(const struct nbhw_fpga_priv *priv, const u8* data, int dataNumBytes)
|
|
{
|
|
int res;
|
|
const u8* raw_bitstream = 0;
|
|
int raw_bitstream_num_bytes = 0;
|
|
const u8* raw_algo = 0;
|
|
int raw_algo_num_bytes = 0;
|
|
|
|
printf("Checking FPGA bitstream...\n");
|
|
if (fpga_check_bitstream(priv, data, dataNumBytes, &raw_bitstream, &raw_bitstream_num_bytes, &raw_algo, &raw_algo_num_bytes)) {
|
|
printf("Not a valid FPGA image at 0x%p\n", data);
|
|
goto abort;
|
|
}
|
|
|
|
/* Write bit stream */
|
|
switch (priv->fpga_type) {
|
|
#ifdef CONFIG_FPGA_LATTICE_SSPI
|
|
case FPGA_LATTICE_SSPI :
|
|
res = fpga_load_bitstream_lattice_sspi(priv, raw_bitstream, raw_bitstream_num_bytes, raw_algo, raw_algo_num_bytes);
|
|
break;
|
|
#endif
|
|
default :
|
|
res = fpga_load_bitstream(priv, raw_bitstream, raw_bitstream_num_bytes);
|
|
break;
|
|
}
|
|
if (!res) {
|
|
goto abort;
|
|
}
|
|
|
|
clean_up_after_programming(priv);
|
|
|
|
return 1;
|
|
|
|
abort:
|
|
printf(" FPGA programming failed\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int abort_fpga_boot (void)
|
|
{
|
|
int c, i;
|
|
int count = 0;
|
|
|
|
/* check for 3 stop keystrokes */
|
|
for (i = 0; i < 5; i++) {
|
|
if (tstc()) {
|
|
c = getc();
|
|
if (c == 'f') {
|
|
if (++count == 3) {
|
|
return 1;
|
|
}
|
|
} else {
|
|
return 0;
|
|
}
|
|
} else {
|
|
return 0;
|
|
}
|
|
udelay(200);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void setup_device_bus(void)
|
|
{
|
|
/* TODO: Use devicetree for this */
|
|
#define MV_DEV_BUS_REGS_OFFSET (0xF1010400)
|
|
/* Setup the device bus according to the FPGA implementation and
|
|
* for the polling SPI driver */
|
|
u32 val;
|
|
/* TODO: Implement a proper read write back function */
|
|
|
|
debug("%s\n", __func__);
|
|
|
|
/* Device Bus Control Interface Register */
|
|
val = readl(MV_DEV_BUS_REGS_OFFSET + 0xC0);
|
|
debug("Device Bus Control Interface Register (0x104C0): 0x%08X\n", val);
|
|
writel(0x5FFFF, MV_DEV_BUS_REGS_OFFSET + 0xC0);
|
|
/* Device Bus Synchronous Control Register */
|
|
val = readl(MV_DEV_BUS_REGS_OFFSET + 0xC8);
|
|
debug("Device Bus Synchronous Control Register (0x104C8): 0x%08X\n", val);
|
|
writel(0x00004204, MV_DEV_BUS_REGS_OFFSET + 0xC8);
|
|
/* Device Bus Misc Timing Parameters Register */
|
|
val = readl(MV_DEV_BUS_REGS_OFFSET + 0xD8);
|
|
debug("Device Bus Misc Timing Parameters Register (0x104D8): 0x%08X\n", val);
|
|
writel(0xFF80020, MV_DEV_BUS_REGS_OFFSET + 0xD8);
|
|
|
|
/* DEV_CS0 Read Parameters Register */
|
|
val = readl(MV_DEV_BUS_REGS_OFFSET + 0x08);
|
|
debug("DEV_CS0 Read Parameters Register(0x10408): 0x%08X\n", val);
|
|
writel(0x441884, MV_DEV_BUS_REGS_OFFSET + 0x08);
|
|
/* DEV_CS0 Write Parameters Register */
|
|
val = readl(MV_DEV_BUS_REGS_OFFSET + 0x0C);
|
|
debug("DEV_CS0 Write Parameters Register(0x1040C): 0x%08X\n", val);
|
|
writel(0x1020208, MV_DEV_BUS_REGS_OFFSET + 0x0C);
|
|
|
|
/* DEV_CS1 Read Parameters Register */
|
|
val = readl(MV_DEV_BUS_REGS_OFFSET + 0x10);
|
|
debug("DEV_CS1 Read Parameters Register (0x10410): 0x%08X\n", val);
|
|
writel(0x003E07C0, MV_DEV_BUS_REGS_OFFSET + 0x10);
|
|
/* DEV_CS1 Write Parameters Register */
|
|
val = readl(MV_DEV_BUS_REGS_OFFSET + 0x14);
|
|
debug("DEV_CS1 Write Parameters Register (0x10414): 0x%08X\n", val);
|
|
writel(0x00010114, MV_DEV_BUS_REGS_OFFSET + 0x14);
|
|
}
|
|
|
|
static int request_gpios(struct udevice *dev)
|
|
{
|
|
struct nbhw_fpga_priv *priv = dev_get_priv(dev);
|
|
|
|
FPGA_PRIV = priv;
|
|
|
|
debug("%s\n", __func__);
|
|
|
|
if (gpio_request_by_name(dev, "spi-ss", 0, &priv->ss, GPIOD_IS_OUT) != 0) {
|
|
printf("Could not request spi-gpio ss\n");
|
|
return -1;
|
|
}
|
|
|
|
if (gpio_request_by_name(dev, "spi-sck", 0, &priv->sck, GPIOD_IS_OUT) != 0) {
|
|
printf("Could not request spi-gpio sck\n");
|
|
return -1;
|
|
}
|
|
|
|
if (gpio_request_by_name(dev, "spi-sdi", 0, &priv->sdi, GPIOD_IS_OUT) != 0) {
|
|
printf("Could not request spi-gpio sdi\n");
|
|
return -1;
|
|
}
|
|
#ifdef CONFIG_FPGA_LATTICE_SSPI
|
|
if (gpio_request_by_name(dev, "spi-sdo", 0, &priv->sdo, GPIOD_IS_IN) != 0) {
|
|
printf("Could not request spi-gpio sdo\n");
|
|
return -1;
|
|
}
|
|
|
|
#endif
|
|
if (gpio_request_by_name(dev, "fpga-reset", 0, &priv->reset, GPIOD_IS_OUT) != 0) {
|
|
debug("Could not request fpga-reset\n");
|
|
}
|
|
|
|
if (gpio_request_by_name(dev, "fpga-cdone", 0, &priv->cdone, GPIOD_IS_IN) != 0) {
|
|
printf("Could not request fpga-cdone\n");
|
|
return -1;
|
|
}
|
|
|
|
if (gpio_request_by_name(dev, "fpga-cinit", 0, &priv->cinit, GPIOD_IS_IN) != 0) {
|
|
debug("Could not request fpga-cinit\n");
|
|
}
|
|
|
|
if (gpio_request_by_name(dev, "fpga-reset-logic", 0, &priv->reset_logic, GPIOD_IS_OUT) != 0) {
|
|
printf("Could not request fpga-reset-logic\n");
|
|
return -1;
|
|
}
|
|
|
|
debug("%s\n", __func__);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int fpga_program(struct udevice *dev, unsigned long load_addr,
|
|
unsigned long filesize)
|
|
{
|
|
int res, i;
|
|
struct nbhw_fpga_priv *priv = dev_get_priv(dev);
|
|
|
|
debug("%s\n", __func__);
|
|
if (abort_fpga_boot()) {
|
|
printf("Aborted FPGA boot\n");
|
|
udelay(1000000); /* 1s */
|
|
return 0;
|
|
}
|
|
|
|
if (priv->ss.dev == NULL) {
|
|
if (request_gpios(dev)) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Make sure device bus works, because HW14 uses its gpios to do SPI */
|
|
setup_device_bus();
|
|
|
|
/* NBHW_BASE_ADDRESS_FPGA points to base of FPGA registers */
|
|
if (!fpga_boot_buffer(priv, (const u8*)load_addr, filesize)) {
|
|
printf ("Loading FPGA over SPI failed.\n");
|
|
return -3;
|
|
}
|
|
|
|
/* Verify, if FPGA is loaded successfully */
|
|
i = 0;
|
|
do {
|
|
// try a few times, as Lattice SSPI seems to need some time to get ready
|
|
res = fpga_verify(priv);
|
|
udelay(100000);
|
|
i++;
|
|
} while ((i<20) && (!res));
|
|
|
|
if (!res)
|
|
{
|
|
printf(" No FPGA detected! (Signature:%x)\n", priv->signature);
|
|
return -4;
|
|
}
|
|
|
|
debug("Signature %d\n", priv->signature);
|
|
printf(" FPGA programming done\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int nbhw_fpga_bind(struct udevice *dev)
|
|
{
|
|
struct nbhw_fpga_priv *priv = malloc(sizeof(struct nbhw_fpga_priv));
|
|
|
|
if (priv == NULL) {
|
|
puts("Can't allocate memory for nbhw fpga driver\n");
|
|
return -1;
|
|
}
|
|
|
|
dev->priv = priv;
|
|
|
|
/* Make priv available in the whole c file */
|
|
fpga_dev = dev;
|
|
|
|
debug("Priv on bind: %p\n", priv);
|
|
|
|
/* Get memory region for fpga */
|
|
priv->regs = (struct mvebu_gpio_regs *)devfdt_get_addr(dev);
|
|
debug("FPGA Regs: %p\n", priv->regs);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int nbhw_fpga_probe(struct udevice *dev)
|
|
{
|
|
struct nbhw_fpga_priv *priv = dev_get_priv(dev);
|
|
/* Make priv available in the whole c file */
|
|
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
|
|
|
debug("dev: %p, local dev: %p\n", dev, fpga_dev);
|
|
debug("priv: %p", priv);
|
|
|
|
debug("%s signature %d\n", __func__, priv->signature);
|
|
|
|
uc_priv->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
|
|
"gpio-bank-name", NULL);
|
|
uc_priv->gpio_count = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
|
|
"ngpios", 1024);
|
|
debug("%s done\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static enum nbhw_fpga_type_num get_fpga_type(const char *name)
|
|
{
|
|
int i;
|
|
|
|
debug("%s\n", __func__);
|
|
|
|
for (i = 0;i < ARRAY_SIZE(fpga_types);i++) {
|
|
if (strcmp(fpga_types[i].name, name) == 0) {
|
|
return fpga_types[i].type;
|
|
}
|
|
}
|
|
return FPGA_MAX;
|
|
}
|
|
|
|
static int do_fpga_program (struct nbhw_fpga_priv *priv, int argc,
|
|
char * const argv[])
|
|
{
|
|
debug("%s\n", __func__);
|
|
ulong start_addr;
|
|
ulong fpga_size;
|
|
if (argc < 3)
|
|
return -1;
|
|
else if(argc == 3){ // Try to read fitImage
|
|
//get offset of fpga@1
|
|
const void * data;
|
|
const void *fit = map_sysmem((phys_addr_t) simple_strtoul(argv[2], NULL, 16), 0);
|
|
int offset = fit_image_get_node(fit, "fpga@1");
|
|
size_t size;
|
|
int ret = fit_image_get_data(fit, offset, &data, &size);
|
|
if(ret)
|
|
return ret;
|
|
start_addr = (ulong)map_to_sysmem(data);
|
|
unmap_sysmem(fit);
|
|
fpga_size = size;
|
|
}
|
|
else{ // Standard FPGA image
|
|
start_addr = simple_strtoul(argv[2], NULL, 16);
|
|
fpga_size = simple_strtoul(argv[3], NULL, 16);
|
|
}
|
|
|
|
priv->fpga_type = get_fpga_type(argv[0]);
|
|
priv->fpga_id = simple_strtoul(argv[1], NULL, 16);
|
|
|
|
return fpga_program(fpga_dev, start_addr, fpga_size);
|
|
}
|
|
|
|
static int do_fpga_configure (struct nbhw_fpga_priv *priv, int argc,
|
|
char * const argv[])
|
|
{
|
|
debug("%s\n", __func__);
|
|
if (argc > 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (priv->signature == 0) {
|
|
puts("Please program FPGA first\n");
|
|
return -1;
|
|
}
|
|
|
|
extern int nbhw_fpga_configure(void);
|
|
/* Call board specific nbhw_fpga_configure, must be implemented in
|
|
* the board file */
|
|
return nbhw_fpga_configure();
|
|
}
|
|
|
|
struct fpga_cmd {
|
|
const char *cmd;
|
|
int (*fpga_fun)(struct nbhw_fpga_priv *priv, int argc,
|
|
char * const argv[]);
|
|
};
|
|
|
|
struct fpga_cmd fpga_cmds[] = {
|
|
{"program", do_fpga_program},
|
|
{"configure", do_fpga_configure}
|
|
};
|
|
|
|
static int do_fpgacmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
struct nbhw_fpga_priv *priv = dev_get_priv(fpga_dev);
|
|
int i;
|
|
|
|
debug("Argv 1: %s\n", argv[1]);
|
|
for (i = 0;i < ARRAY_SIZE(fpga_cmds); i++) {
|
|
if (strcmp(fpga_cmds[i].cmd, argv[1]) == 0) {
|
|
return fpga_cmds[i].fpga_fun(priv, argc-2, &argv[2]);
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
U_BOOT_CMD(
|
|
nbhw_fpga, 6, 1, do_fpgacmd,
|
|
"NBHW FPGA control",
|
|
"program type id address filesize\n"
|
|
" type: fpga type lattice, lattice-sspi or xilinx\n"
|
|
" id: FPGA User ID (BD) in hex, 0xFFFFFFFF for any\n"
|
|
" address: memory address where to find the fpga image\n"
|
|
" filesize: filesize of the fpga image\n"
|
|
"configure\n"
|
|
" do initial fpga configuration\n"
|
|
);
|
|
|
|
int nbhw_fpga_get_value (struct udevice *dev, unsigned offset)
|
|
{
|
|
struct nbhw_fpga_priv *priv = dev_get_priv(dev);
|
|
u16 bit = offset%16;
|
|
u16 reg = (offset/8)&0xFFFE;
|
|
|
|
debug("%s\n", __func__);
|
|
|
|
if (priv->signature == 0) {
|
|
puts("FPGA not programmed yet\n");
|
|
return -1;
|
|
}
|
|
|
|
return ((readw(priv->regs + reg) & (1 << bit)) == 0) ? 0 : 1;
|
|
}
|
|
|
|
int nbhw_fpga_set_value (struct udevice *dev, unsigned offset, int value)
|
|
{
|
|
struct nbhw_fpga_priv *priv = dev_get_priv(dev);
|
|
u16 bit = offset%16;
|
|
u16 reg = (offset/8)&0xFFFE;
|
|
u16 val;
|
|
|
|
debug("%s\n", __func__);
|
|
|
|
if (priv->signature == 0) {
|
|
puts("FPGA not programmed yet\n");
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
debug("offset is %d\n", offset);
|
|
debug("reg is %d\n", reg);
|
|
debug("Read from %p\n", priv->regs + reg);
|
|
*/
|
|
|
|
val = readw(priv->regs + reg);
|
|
val &= ~(1 << bit);
|
|
val |= ((value > 0 ? 1 : 0) << bit);
|
|
|
|
// debug("Write 0x%p to reg 0x%x\n", priv->regs + reg, val);
|
|
|
|
return writew(val, priv->regs + reg);
|
|
}
|
|
|
|
static int nbhw_fpga_direction_input(struct udevice *dev, unsigned offset)
|
|
{
|
|
debug("%s\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
int nbhw_fpga_direction_output (struct udevice *dev, unsigned offset, int value)
|
|
{
|
|
debug("%s\n", __func__);
|
|
|
|
nbhw_fpga_set_value(dev, offset, value);
|
|
return 0;
|
|
}
|
|
|
|
static const struct dm_gpio_ops nbhw_fpga_ops = {
|
|
.direction_input = nbhw_fpga_direction_input,
|
|
.direction_output = nbhw_fpga_direction_output,
|
|
.get_value = nbhw_fpga_get_value,
|
|
.set_value = nbhw_fpga_set_value,
|
|
};
|
|
|
|
static const struct udevice_id nbhw_fpga_ids[] = {
|
|
{ .compatible = "nm,nbhw-fpga" },
|
|
{ }
|
|
};
|
|
|
|
U_BOOT_DRIVER(gpio_nbhw_fpga) = {
|
|
.name = "gpio_nbhw_fpga",
|
|
.id = UCLASS_GPIO,
|
|
.of_match = nbhw_fpga_ids,
|
|
.probe = nbhw_fpga_probe,
|
|
.bind = nbhw_fpga_bind,
|
|
.ops = &nbhw_fpga_ops,
|
|
};
|
|
|
|
#ifdef CONFIG_FPGA_LATTICE_SSPI
|
|
/* Lattice SSPI programming tool wrappers */
|
|
|
|
void FPGA_START(void) {
|
|
put_in_prog_mode_lattice_sspi(FPGA_PRIV);
|
|
}
|
|
|
|
void FPGA_FINAL(void) {
|
|
clean_up_after_programming(FPGA_PRIV);
|
|
}
|
|
|
|
void FPGA_OUT(u8 b) {
|
|
spi_write_sspi(FPGA_PRIV, b);
|
|
}
|
|
|
|
u8 FPGA_IN(void) {
|
|
return spi_read_sspi(FPGA_PRIV);
|
|
}
|
|
|
|
void FPGA_CS_LOW(void)
|
|
{
|
|
dm_gpio_set_value(&FPGA_PRIV->ss, 0);
|
|
}
|
|
|
|
void FPGA_CS_HIGH(void)
|
|
{
|
|
dm_gpio_set_value(&FPGA_PRIV->ss, 1);
|
|
}
|
|
|
|
#endif
|