u-boot/board/nm/common/nbhw_fpga_gpio.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