nbhw17: add support for fpga loading
This commit is contained in:
parent
51120348fb
commit
c2035d6033
|
|
@ -37,12 +37,75 @@
|
|||
MBUS_ID(0x01, 0x1d) 0 0xfff00000 0x100000
|
||||
MBUS_ID(0x09, 0x19) 0 0xf1100000 0x10000 /* CESA 0 SRAM */
|
||||
MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000 /* CESA 1 SRAM */
|
||||
MBUS_ID(0x01, 0x3e) 0 0xfd000000 0x20000>; /* Devbus CS0 */
|
||||
MBUS_ID(0x01, 0x3e) 0 0xfd000000 0x20000>; /* FPGA */
|
||||
|
||||
devbus-cs0 {
|
||||
status = "okay";
|
||||
ranges = <0 MBUS_ID(0x01, 0x3e) 0 0x20000>;
|
||||
devbus,keep-config;
|
||||
gpiofpga: gpio@fd0000000 {
|
||||
compatible = "nm,nbhw-fpga";
|
||||
reg = <MBUS_ID(0x01, 0x3e) 0 0x20000>; /* This translates to 0xfd000000 */
|
||||
ngpios = <8500>;
|
||||
gpio-controller;
|
||||
gpio-bank-name = "FPGA";
|
||||
#gpio-cells = <2>;
|
||||
spi-ss = <&gpio1 5 0>; /* SS */
|
||||
spi-sdo = <&gpio1 6 0>; /* SDO slave data out */
|
||||
spi-sck = <&gpio1 7 0>; /* SCK */
|
||||
spi-sdi = <&gpio1 8 0>; /* SDI slave data in */
|
||||
fpga-reset = <&gpio0 26 0>; /* FPGA reset */
|
||||
fpga-cdone = <&gpio0 29 0>; /* FPGA cdone */
|
||||
fpga-reset-logic = <&gpio1 12 0>; /* FPGA reset logic (after load)*/
|
||||
};
|
||||
|
||||
fpga_conf: fpgaconf@0 {
|
||||
compatible = "nm,nbhw17-fpga-config";
|
||||
leds = <&gpiofpga 256 0>, <&gpiofpga 257 0>, <&gpiofpga 258 0>,
|
||||
<&gpiofpga 259 0>, <&gpiofpga 260 0>, <&gpiofpga 261 0>,
|
||||
<&gpiofpga 262 0>, <&gpiofpga 263 0>, <&gpiofpga 264 0>,
|
||||
<&gpiofpga 265 0>, <&gpiofpga 266 0>, <&gpiofpga 267 0>,
|
||||
<&gpiofpga 268 0>, <&gpiofpga 269 0>, <&gpiofpga 270 0>,
|
||||
<&gpiofpga 271 0>;
|
||||
|
||||
misc@0 {
|
||||
#gpio-cells = <4>;
|
||||
// gpio controller, gpio number, low/high_active, default value
|
||||
hold-pwr-on = <&gpiofpga 64 0 1>;
|
||||
en-gps-ant = <&gpiofpga 65 0 0>;
|
||||
en-mdio-phy = <&gpiofpga 66 GPIO_ACTIVE_LOW 1>;
|
||||
en-mdio-ext = <&gpiofpga 67 GPIO_ACTIVE_LOW 0>;
|
||||
en-sata-pwr = <&gpiofpga 68 GPIO_ACTIVE_HIGH 1>;
|
||||
serdes-sel = <&gpiofpga 69 GPIO_ACTIVE_HIGH 1>;
|
||||
serdes-en = <&gpiofpga 70 GPIO_ACTIVE_LOW 0>;
|
||||
rst-ext = <&gpiofpga 71 GPIO_ACTIVE_LOW 1>;
|
||||
rst-ext-eth = <&gpiofpga 72 GPIO_ACTIVE_LOW 1>;
|
||||
rst-ext-en = <&gpiofpga 73 GPIO_ACTIVE_HIGH 0>;
|
||||
};
|
||||
|
||||
pcieslot@0 {
|
||||
reset = <&gpiofpga 384 GPIO_ACTIVE_LOW 1>;
|
||||
power = <&gpiofpga 400 GPIO_ACTIVE_HIGH 0>;
|
||||
wdis-out = <&gpiofpga 2053 GPIO_ACTIVE_HIGH 1>;
|
||||
wdis = <&gpiofpga 2068 GPIO_ACTIVE_LOW 1>;
|
||||
};
|
||||
|
||||
pcieslot@1 {
|
||||
reset = <&gpiofpga 385 GPIO_ACTIVE_LOW 1>;
|
||||
power = <&gpiofpga 401 GPIO_ACTIVE_HIGH 0>;
|
||||
wdis-out = <&gpiofpga 4101 GPIO_ACTIVE_HIGH 1>;
|
||||
wdis = <&gpiofpga 4117 GPIO_ACTIVE_LOW 1>;
|
||||
};
|
||||
|
||||
pcieslot@2 {
|
||||
reset = <&gpiofpga 386 GPIO_ACTIVE_LOW 1>;
|
||||
power = <&gpiofpga 402 GPIO_ACTIVE_HIGH 0>;
|
||||
wdis-out = <&gpiofpga 6149 GPIO_ACTIVE_HIGH 1>;
|
||||
wdis = <&gpiofpga 6165 GPIO_ACTIVE_LOW 1>;
|
||||
};
|
||||
|
||||
pcieslot@3 {
|
||||
reset = <&gpiofpga 387 GPIO_ACTIVE_LOW 1>;
|
||||
power = <&gpiofpga 403 GPIO_ACTIVE_HIGH 0>;
|
||||
wdis-out = <&gpiofpga 8197 GPIO_ACTIVE_HIGH 1>;
|
||||
wdis = <&gpiofpga 8213 GPIO_ACTIVE_LOW 1>;
|
||||
};
|
||||
};
|
||||
|
||||
internal-regs {
|
||||
|
|
@ -69,7 +132,6 @@
|
|||
broken-cd;
|
||||
wp-inverted;
|
||||
no-1-8-v;
|
||||
clock-freq-min-max=<0 50000000>;
|
||||
bus-width = <8>;
|
||||
status = "okay";
|
||||
};
|
||||
|
|
@ -85,6 +147,7 @@
|
|||
pinctrl-0 = <&uart1_pins>;
|
||||
u-boot,dm-pre-reloc;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
pcie-controller {
|
||||
|
|
@ -104,14 +167,6 @@
|
|||
};
|
||||
};
|
||||
|
||||
// nbhw-fpga {
|
||||
// compatible = "nm,nbhw-fpga";
|
||||
// status = "disabled";
|
||||
// spi-ss = <&gpio1 5 0> /* SS */
|
||||
// spi-sck = <&gpio1 6 0>; /* SCK */
|
||||
// spi-sdi = <&gpio1 7 0>; /* SDI slave data in */
|
||||
// spi-sdo = <&gpio1 8 0>; /* SDO slave data out */
|
||||
// };
|
||||
};
|
||||
|
||||
ð0 {
|
||||
|
|
|
|||
|
|
@ -17,20 +17,15 @@
|
|||
model = "NetModule Router NBHW17 with Armada A385 (NB2800)";
|
||||
};
|
||||
|
||||
ð0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
// SFP-Port
|
||||
ð1 {
|
||||
status = "okay";
|
||||
fixed-link {
|
||||
speed = <1000>;
|
||||
full-duplex;
|
||||
};
|
||||
};
|
||||
|
||||
ð2 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpiofpga {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ int bd_get_context(BD_Context *bdctx, uint32_t i2caddress, uint32_t offset)
|
|||
/* Check whether this is a valid board descriptor (or empty EEPROM) */
|
||||
rc = BD_CheckHeader( bdctx, bdHeader );
|
||||
if (!rc) {
|
||||
printf("%s() No valid board descriptor found\n", __func__);
|
||||
debug("%s() No valid board descriptor found\n", __func__);
|
||||
goto exit1;
|
||||
}
|
||||
|
||||
|
|
@ -271,3 +271,8 @@ int bd_get_boot_partition(void)
|
|||
/* If we not have a Bootpartition entry, perhaps we have a partition table */
|
||||
return try_partition_read();
|
||||
}
|
||||
|
||||
enum serdes_type bd_get_serdes_type(int serdes)
|
||||
{
|
||||
return DEFAULT_SERDES;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,12 @@
|
|||
#include "bdparser.h" /* tlv parser */
|
||||
#include <linux/types.h>
|
||||
|
||||
#ifdef _CONFIG_DB_88F6820_GP_H
|
||||
#include <../serdes/a38x/high_speed_env_spec.h>
|
||||
#else
|
||||
enum serdes_type {DEFAULT_SERDES, LAST_SERDES_TYPE};
|
||||
#endif
|
||||
|
||||
void nbhw_settings_init(void);
|
||||
void bd_register_context_list(const BD_Context *list, size_t count);
|
||||
int bd_get_context(BD_Context *bdctx, uint32_t i2caddress, uint32_t offset);
|
||||
|
|
@ -16,5 +22,7 @@ int bd_get_sim_config(char* simconfig, size_t len);
|
|||
void bd_get_hw_version(int* ver, int* rev);
|
||||
int bd_get_devicetree(char* devicetreename, size_t len);
|
||||
int bd_get_boot_partition(void);
|
||||
enum serdes_type bd_get_serdes_type(int serdes);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ void find_and_set_active_partition(void)
|
|||
|
||||
void set_console(void)
|
||||
{
|
||||
int len;
|
||||
char buf[50] = "\n";
|
||||
char *defaultconsole = getenv("defaultconsole");
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,753 @@
|
|||
/******************************************************************************
|
||||
* (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>
|
||||
|
||||
#define DEBUG_TEST debug("%s: %d\n",__FILE__, __LINE__);
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
enum nbhw_fpga_type_num {
|
||||
FPGA_XILINX,
|
||||
FPGA_LATTICE,
|
||||
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"}
|
||||
};
|
||||
|
||||
struct nbhw_fpga_priv {
|
||||
int signature;
|
||||
struct gpio_desc ss;
|
||||
struct gpio_desc sck;
|
||||
struct gpio_desc sdi;
|
||||
struct gpio_desc sdo;
|
||||
struct gpio_desc reset;
|
||||
struct gpio_desc reset_logic;
|
||||
struct gpio_desc cdone;
|
||||
enum nbhw_fpga_type_num fpga_type;
|
||||
u32 fpga_id;
|
||||
void *regs;
|
||||
};
|
||||
|
||||
struct udevice *fpga_dev;
|
||||
|
||||
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);
|
||||
udelay(1);
|
||||
/* Read data on rising edge */
|
||||
dm_gpio_set_value(&priv->sck, 1);
|
||||
data = data << 1;
|
||||
udelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
static int fpga_verify(struct nbhw_fpga_priv *priv)
|
||||
{
|
||||
/* Check, if the FPGA is working */
|
||||
char fpga_type[30];
|
||||
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");
|
||||
} else if (signature == 0xa502) {
|
||||
strcpy(fpga_type, "XC3S200A-4VQ100I top layer");
|
||||
} else if (signature == 0xa511) {
|
||||
strcpy(fpga_type, "XC3S50A-4FTG256I bottom layer");
|
||||
} else if (signature == 0xa512) {
|
||||
strcpy(fpga_type, "XC3S200A-4FTG256I bottom layer");
|
||||
} else if (signature == 0x4004) {
|
||||
strcpy(fpga_type, "ICE40HX4K-CB132 NBHW17");
|
||||
} else if (signature == 0x4184) {
|
||||
strcpy(fpga_type, "ICE40HX4K-CB132 NBHW18");
|
||||
} else {
|
||||
priv->signature = 0;
|
||||
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:
|
||||
printf(" No FPGA detected! (Signature:%x)\n", signature);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
if (dm_gpio_get_value(&priv->cdone)) {
|
||||
printf("Error: Fpga does not signalize done\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
debug("FPGA signalizes done\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, u8** pStartAddr, int* pSize)
|
||||
{
|
||||
printf ("Checking Lattice FPGA bitstream\n");
|
||||
*pStartAddr = (u8*)fpgadata;
|
||||
/* Do not touch pSize should send the whole file for now */
|
||||
|
||||
if (priv->fpga_type != FPGA_LATTICE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((priv->fpga_id != 0x00000000) && (priv->fpga_id != 0xFFFFFFFF)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Legacy Xilinx check ported from PPC, seems to work */
|
||||
static int fpga_check_bitstream_xilinx(const struct nbhw_fpga_priv *priv, const u8* fpgadata, u8** pStartAddr, int* pSize)
|
||||
{
|
||||
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);
|
||||
|
||||
*pSize = 0;
|
||||
*pStartAddr = 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);
|
||||
|
||||
/* verify fpga identifier */
|
||||
if (priv->fpga_id == 0xFFFFFFFF) {
|
||||
return 0;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
*pStartAddr = (u8*)dataptr;
|
||||
*pSize = codesize;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fpga_check_bitstream(const struct nbhw_fpga_priv *priv, const u8* fpgadata, u8** pStartAddr, int* pSize)
|
||||
{
|
||||
debug("check bitstream %d, %0x, %0x\n", *pSize, 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, pStartAddr, pSize);
|
||||
}
|
||||
/* Xilinx header starts with 0x00 0x09 which means header with 0x09 length */
|
||||
else if(fpgadata[0] == 0x00 && fpgadata[1] == 0x09) {
|
||||
return fpga_check_bitstream_xilinx(priv, fpgadata, pStartAddr, pSize);
|
||||
}
|
||||
|
||||
printf("Unknown bitstream file format (not Xilinx nor Lattice)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int fpga_boot_buffer(const struct nbhw_fpga_priv *priv, const u8* data, int num_bytes)
|
||||
{
|
||||
int res;
|
||||
u8* raw_bitstream;
|
||||
int raw_num_bytes;
|
||||
|
||||
printf("Checking FPGA bitstream...\n");
|
||||
raw_num_bytes = num_bytes;
|
||||
if (fpga_check_bitstream(priv, data, &raw_bitstream, &raw_num_bytes)) {
|
||||
printf("Not a valid FPGA image at 0x%p\n", data);
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/* Write bit stream */
|
||||
res = fpga_load_bitstream(priv, raw_bitstream, raw_num_bytes);
|
||||
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 Regiseter (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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (gpio_request_by_name(dev, "fpga-reset", 0, &priv->reset, GPIOD_IS_OUT) != 0) {
|
||||
printf("Could not request fpga-reset\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
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-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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Make sure device bus works, becaus NBHW14 uses its gpios to do SPI */
|
||||
setup_device_bus();
|
||||
|
||||
|
||||
/* Verify, if FPGA is loaded successfully */
|
||||
res = fpga_verify(priv);
|
||||
if (!res)
|
||||
{
|
||||
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 *)dev_get_addr(dev);
|
||||
printf("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__);
|
||||
|
||||
if (argc < 4)
|
||||
return -1;
|
||||
|
||||
priv->fpga_type = get_fpga_type(argv[0]);
|
||||
priv->fpga_id = simple_strtoul(argv[1], NULL, 16);
|
||||
|
||||
return fpga_program(fpga_dev, simple_strtoul(argv[2], NULL, 16),
|
||||
simple_strtoul(argv[3], NULL, 16));
|
||||
}
|
||||
|
||||
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 tye lattice 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,
|
||||
};
|
||||
|
||||
|
|
@ -1,478 +0,0 @@
|
|||
/******************************************************************************
|
||||
* (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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm/device.h>
|
||||
#include <asm-generic/gpio.h>
|
||||
#include <asm/io.h>
|
||||
#include "config.h"
|
||||
|
||||
#include "nbhw_bd.h"
|
||||
#include "nbhw_fpga_prog.h"
|
||||
#include "nbhw_gpio.h"
|
||||
#include "nbhw_fileaccess.h"
|
||||
|
||||
static fpga_prog_operation *_operation = NULL;
|
||||
|
||||
struct nbhw_fpga_priv {
|
||||
int signature;
|
||||
struct gpio_desc *ss;
|
||||
struct gpio_desc *sck;
|
||||
struct gpio_desc *sdi;
|
||||
struct gpio_desc *sdo;
|
||||
struct mvebu_gpio_regs *regs;
|
||||
};
|
||||
|
||||
|
||||
static inline void spi_write(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);
|
||||
/* Read data on rising edge */
|
||||
dm_gpio_set_value(priv->sck, 1);
|
||||
data = data << 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int fpga_verify(void)
|
||||
{
|
||||
/* Check, if the FPGA is working */
|
||||
char fpga_type[30];
|
||||
u16 signature = *((volatile u16*)NBHW_BASE_ADDRESS_FPGA);
|
||||
u16 fpga_version = FPGA_REG(0x02);
|
||||
u8 fpga_major = (fpga_version >> 8) & 0xFF;
|
||||
u8 fpga_minor = (fpga_version >> 0) & 0xFF;
|
||||
|
||||
if (signature == 0xa501)
|
||||
{
|
||||
strcpy(fpga_type, "XC3S50A-4VQ100I top layer");
|
||||
} else if (signature == 0xa502) {
|
||||
strcpy(fpga_type, "XC3S200A-4VQ100I top layer");
|
||||
} else if (signature == 0xa511) {
|
||||
strcpy(fpga_type, "XC3S50A-4FTG256I bottom layer");
|
||||
} else if (signature == 0xa512) {
|
||||
strcpy(fpga_type, "XC3S200A-4FTG256I bottom layer");
|
||||
} else if (signature == 0x4004) {
|
||||
strcpy(fpga_type, "ICE40HX4K-CB132 NBHW17");
|
||||
} else if (signature == 0x4184) {
|
||||
strcpy(fpga_type, "ICE40HX4K-CB132 NBHW18");
|
||||
} else {
|
||||
goto abort;
|
||||
}
|
||||
|
||||
printf(" Detected %s FPGA (Version: %d.%d)\n", fpga_type, fpga_major, fpga_minor);
|
||||
return 1;
|
||||
|
||||
abort:
|
||||
printf(" No FPGA detected! (Signature:%x)\n", signature);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fpga_load_bitstream (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);
|
||||
printf(" Sending configuration bitstream...\n");
|
||||
|
||||
for ( i=0; i<num_bytes; i++ )
|
||||
{
|
||||
spi_write(priv, *(data++));
|
||||
}
|
||||
|
||||
/* Send some extra clocks for startup */
|
||||
|
||||
for ( i=0; i<100; i++)
|
||||
{
|
||||
spi_write(priv, 0);
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int fpga_check_bitstream_lattice(const u8* fpgadata, u8** pStartAddr, int* pSize)
|
||||
{
|
||||
printf ("Checking Lattice FPGA bitstream\n");
|
||||
*pStartAddr = (u8*)fpgadata;
|
||||
/* Do not touch pSize should send the whole file for now */
|
||||
|
||||
if (_operation->check_bitstream_compatibility(FPGA_TYPE_LATTICE, 0xFFFFFFFF) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: implement file check (e.g. UserID in comment section */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int fpga_check_bitstream_xilinx(const u8* fpgadata, u8** pStartAddr, int* pSize)
|
||||
{
|
||||
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);
|
||||
|
||||
*pSize = 0;
|
||||
*pStartAddr = 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 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* verify fpga identifier */
|
||||
if (bd_get_fpgainfo() == 0xFFFFFFFF) {
|
||||
printf("%s: Fpga info not defined in bd\n",
|
||||
__FUNCTION__ );
|
||||
return 0;
|
||||
}
|
||||
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 0;
|
||||
}
|
||||
}
|
||||
if (_operation->check_bitstream_compatibility(FPGA_TYPE_XILINX, fpgaid) != 0) {
|
||||
printf("%s: User identifier not matched in bitstream\n",
|
||||
__FUNCTION__ );
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
printf("%s: User identifier not found in bitstream\n",
|
||||
__FUNCTION__ );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get part number (identifier, length, string) */
|
||||
if (*dataptr++ != 0x62) {
|
||||
printf("%s: Part number identifier not recognized in bitstream\n",
|
||||
__FUNCTION__ );
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 0;
|
||||
}
|
||||
|
||||
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 0;
|
||||
}
|
||||
|
||||
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 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
*pStartAddr = (u8*)dataptr;
|
||||
*pSize = codesize;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int fpga_check_bitstream(const u8* fpgadata, u8** pStartAddr, int* pSize)
|
||||
{
|
||||
|
||||
/* Lattice header starts with 0xFF00 followed by comment */
|
||||
if (fpgadata[0] == 0xFF && fpgadata[1] == 0x00) {
|
||||
return fpga_check_bitstream_lattice(fpgadata, pStartAddr, pSize);
|
||||
}
|
||||
/* Xilinx header starts with 0x00 0x09 which means header with 0x09 length */
|
||||
else if(fpgadata[0] == 0x00 && fpgadata[1] == 0x09) {
|
||||
return fpga_check_bitstream_xilinx(fpgadata, pStartAddr, pSize);
|
||||
}
|
||||
|
||||
printf("Unknown bitstream file format (not Xilinx nor Lattice)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fpga_boot_buffer(struct nbhw_fpga_priv *priv, const u8* data, int num_bytes)
|
||||
{
|
||||
int res;
|
||||
u8* raw_bitstream;
|
||||
int raw_num_bytes;
|
||||
|
||||
printf("Checking FPGA bitstream...\n");
|
||||
raw_num_bytes = num_bytes;
|
||||
if (!fpga_check_bitstream(data, &raw_bitstream, &raw_num_bytes))
|
||||
{
|
||||
printf("Not a valid FPGA image at 0x%p\n", data);
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/* Write bit stream */
|
||||
res = fpga_load_bitstream(priv, raw_bitstream, raw_num_bytes );
|
||||
if (!res)
|
||||
{
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/* Verify, if FPGA is loaded successfully */
|
||||
res = fpga_verify();
|
||||
if (!res)
|
||||
{
|
||||
goto abort;
|
||||
}
|
||||
|
||||
printf(" FPGA programming done\n");
|
||||
|
||||
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 porper read write back function */
|
||||
|
||||
/* Device Bus Control Interface Register */
|
||||
val = readl(MV_DEV_BUS_REGS_OFFSET + 0xC0);
|
||||
debug("Device Bus Control Interface Regiseter (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);
|
||||
}
|
||||
|
||||
int fpga_program(struct nbhw_fpga_priv *priv)
|
||||
{
|
||||
const char *load_addr_str;
|
||||
unsigned long load_addr;
|
||||
int len_read;
|
||||
char boot_part[8];
|
||||
|
||||
if (abort_fpga_boot()) {
|
||||
printf("Aborted FPGA boot\n");
|
||||
udelay(1000000); /* 1s */
|
||||
return -1;
|
||||
}
|
||||
|
||||
load_addr_str = getenv("loadaddr");
|
||||
if (load_addr_str != NULL)
|
||||
load_addr = simple_strtoul(load_addr_str, NULL, 16);
|
||||
else
|
||||
load_addr = CONFIG_SYS_LOAD_ADDR;
|
||||
|
||||
/* Get boot partiton based on active boot partition set during settings_init() */
|
||||
snprintf(boot_part, sizeof(boot_part), "0:%s", getenv("root_part"));
|
||||
if (fs_set_blk_dev("mmc", boot_part, FS_TYPE_EXT)) {
|
||||
printf ("Can not select mmc%s for FPGA logic.\n", boot_part);
|
||||
return -2;
|
||||
}
|
||||
|
||||
len_read = read_file("/logic/LG00000000", (char*)load_addr, 0x01000000);
|
||||
|
||||
printf("FPGA logic file read successfully (%d Bytes)\n", len_read);
|
||||
|
||||
/* NBHW_BASE_ADDRESS_FPGA points to base of FPGA registers */
|
||||
if (!fpga_boot_buffer(priv, (const u8*)load_addr, len_read)) {
|
||||
printf ("Loading FPGA over SPI failed.\n");
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* Make sure device bus works, becaus NBHW14 uses it to do SPI */
|
||||
setup_device_bus();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nbhw_fpga_probe(struct udevice *dev)
|
||||
{
|
||||
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
struct nbhw_fpga_priv *priv = dev_get_priv(dev);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
priv->regs = (struct mvebu_gpio_regs *)dev_get_addr(dev);
|
||||
|
||||
return fpga_program(priv);
|
||||
}
|
||||
|
||||
//static const struct dm_gpio_ops nbhw_fpga_ops = {
|
||||
// .direction_input = nbhw_fpga_direction_input,
|
||||
// .direction_output = nbhw_fpga_direction_output,
|
||||
// .get_function = nbhw_fpga_get_function,
|
||||
// .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(nbhw_fpga_serial) = {
|
||||
// .name = "nbhw_fpga",
|
||||
// .id = UCLASS_GPIO,
|
||||
// .of_match = nbhw_fpga_ids,
|
||||
// .probe = nbhw_fpga_probe,
|
||||
// .ops = &nbhw_fpga_ops,
|
||||
// .priv_auto_alloc_size = sizeof(struct nbhw_fpga_priv),
|
||||
//};
|
||||
//
|
||||
|
|
@ -33,7 +33,6 @@ static void set_mac(int interface, uint8_t macaddress[], size_t size)
|
|||
if (interface == 0) {
|
||||
/* Serial number consists of 1st MAC address */
|
||||
setenv("ethaddr", macstring);
|
||||
printf("Serial Number: %s\n", macstring);
|
||||
}
|
||||
/* Always set eth%daddr because newer u-boots expect it */
|
||||
{
|
||||
|
|
@ -42,7 +41,6 @@ static void set_mac(int interface, uint8_t macaddress[], size_t size)
|
|||
sprintf(ethaddr, "eth%daddr", interface);
|
||||
/* The other ethaddrs are set trough ethaddr1,2,3 etc. */
|
||||
setenv(ethaddr, macstring);
|
||||
printf("MAC%d: %s\n", interface, macstring);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,17 @@
|
|||
commonobj = ../common/bdparser.o \
|
||||
../common/nbhw_bd.o \
|
||||
../common/nbhw_env.o \
|
||||
../common/nbhw_fpga_prog.o \
|
||||
../common/nbhw_init.o \
|
||||
../common/nbhw_fileaccess.o \
|
||||
../common/nbhw_fpga_gpio.o \
|
||||
../common/nbhw_partitions.o
|
||||
|
||||
ccflags-y := -I../common
|
||||
|
||||
obj-y := board.o $(commonobj)
|
||||
ifndef CONFIG_SPL_BUILD
|
||||
obj-y := board.o nbhw_gpio.o nbhw_fpga_config.o nbhw_pcie_fixup.o $(commonobj)
|
||||
else
|
||||
obj-y := board.o nbhw_gpio.o ../common/nbhw_bd.o ../common/nbhw_partitions.o
|
||||
endif
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#define DEBUG
|
||||
#include <common.h>
|
||||
#include <i2c.h>
|
||||
#include <miiphy.h>
|
||||
|
|
@ -13,9 +13,13 @@
|
|||
#include <asm/arch/soc.h>
|
||||
#include <mmc.h>
|
||||
#include <spl.h>
|
||||
#include <linux/mbus.h>
|
||||
#include <environment.h>
|
||||
|
||||
#include <asm/gpio.h>
|
||||
|
||||
#include "nbhw_gpio.h"
|
||||
|
||||
#include "../common/nbhw_init.h"
|
||||
#include "../common/nbhw_env.h"
|
||||
#include "../common/nbhw_bd.h"
|
||||
|
|
@ -43,6 +47,8 @@ DECLARE_GLOBAL_DATA_PTR;
|
|||
#define PD_ADDRESS (0x0200) /* Product descriptor */
|
||||
#define PARTITION_ADDRESS (0x0600) /* Partition Table */
|
||||
|
||||
#define DEV_CS0_BASE 0xfd000000
|
||||
|
||||
static struct serdes_map board_serdes_map[] = {
|
||||
{ PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0 },
|
||||
{ SGMII1, SERDES_SPEED_1_25_GBPS, SERDES_DEFAULT_MODE, 0, 0 },
|
||||
|
|
@ -112,8 +118,6 @@ int board_early_init_f(void)
|
|||
writel(GPP_OUT_ENA_LOW, MVEBU_GPIO0_BASE + 0x04);
|
||||
writel(GPP_OUT_ENA_MID, MVEBU_GPIO1_BASE + 0x04);
|
||||
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -150,7 +154,7 @@ static int _bd_init(void)
|
|||
}
|
||||
|
||||
if (bd_get_context(&bdctx[1], BD_EEPROM_ADDR, PD_ADDRESS) != 0) {
|
||||
printf("%s() no valid pd found (legacy support)\n", __func__);
|
||||
/* Ignore that, legacy boxes don't have a pd */
|
||||
}
|
||||
|
||||
if (bd_get_context(&bdctx[2], BD_EEPROM_ADDR, PARTITION_ADDRESS) != 0) {
|
||||
|
|
@ -166,26 +170,97 @@ static inline int __maybe_unused read_eeprom(void)
|
|||
return _bd_init();
|
||||
}
|
||||
|
||||
static void set_gpios(void)
|
||||
{
|
||||
init_gpios();
|
||||
}
|
||||
|
||||
static void pass_hw_rev(void)
|
||||
{
|
||||
int hw_ver = 0, hw_rev = 0;
|
||||
char *old_env;
|
||||
char hw_versions[128];
|
||||
char new_env[256];
|
||||
|
||||
bd_get_hw_version(&hw_ver, &hw_rev);
|
||||
|
||||
snprintf(hw_versions, sizeof(hw_versions), "CP=%d.%d",
|
||||
hw_ver, hw_rev);
|
||||
|
||||
old_env = getenv("add_version_bootargs");
|
||||
|
||||
/* Normaly add_version_bootargs should already be set (see board include file) */
|
||||
if (old_env != NULL) {
|
||||
snprintf(new_env, sizeof(new_env), "%s %s", old_env, hw_versions);
|
||||
}
|
||||
else {
|
||||
snprintf(new_env, sizeof(new_env), "setenv bootargs $bootargs %s\n", hw_versions);
|
||||
}
|
||||
|
||||
setenv("add_version_bootargs", new_env);
|
||||
}
|
||||
|
||||
int board_init(void)
|
||||
{
|
||||
/* adress of boot parameters */
|
||||
gd->bd->bi_boot_params = mvebu_sdram_bar(0) + 0x100;
|
||||
|
||||
/* Setup the MBUS mapping for Devicebus CS0 */
|
||||
mbus_dt_setup_win(&mbus_state, DEV_CS0_BASE, 16 << 20,
|
||||
CPU_TARGET_DEVICEBUS_BOOTROM_SPI, CPU_ATTR_DEV_CS0);
|
||||
|
||||
if (read_eeprom() < 0)
|
||||
puts("Could not get board ID.\n");
|
||||
|
||||
set_mac_addresses(2);
|
||||
find_and_set_active_partition();
|
||||
set_console();
|
||||
|
||||
gpio_request(21, "RST_ETH_PHY_N");
|
||||
gpio_direction_output(21, 1);
|
||||
set_gpios();
|
||||
|
||||
#if 0
|
||||
/* Init I2C IO expanders */
|
||||
for (i = 0; i < ARRAY_SIZE(io_exp); i++)
|
||||
i2c_write(io_exp[i].chip, io_exp[i].addr, 1, &io_exp[i].val, 1);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_phy_page(const char *miidev, int phy_addr, int page)
|
||||
{
|
||||
miiphy_write(miidev, phy_addr, 22, page);
|
||||
}
|
||||
|
||||
static void set_phy_fast_blink_mode(int phy_addr)
|
||||
{
|
||||
const char *miidev = miiphy_get_current_dev();
|
||||
debug ("miidev: %s\n", miidev);
|
||||
|
||||
set_phy_page(miidev, phy_addr, 3);
|
||||
miiphy_write(miidev, phy_addr, 16, 0x1032);
|
||||
miiphy_write(miidev, phy_addr, 17, 0x4405);
|
||||
miiphy_write(miidev, phy_addr, 18, 0x4A08);
|
||||
set_phy_page(miidev, phy_addr, 0);
|
||||
}
|
||||
|
||||
int board_late_init(void)
|
||||
{
|
||||
|
||||
gpio_request(21, "RST_ETH_PHY_N");
|
||||
gpio_direction_output(21, 0);
|
||||
|
||||
find_and_set_active_partition();
|
||||
pass_hw_rev();
|
||||
|
||||
/* Todo: It seems that something with the network is wrong */
|
||||
run_command("run load_fpga", CMD_FLAG_ENV);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int last_stage_init(void)
|
||||
{
|
||||
set_mac_addresses(2);
|
||||
|
||||
/* We want ethernet available much earlier than CONFIG_CMD_NET can
|
||||
* guarantee it */
|
||||
gpio_set_value(21, 1);
|
||||
|
||||
set_phy_fast_blink_mode(0);
|
||||
set_phy_fast_blink_mode(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -197,8 +272,5 @@ int checkboard(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int board_eth_init(bd_t *bis)
|
||||
{
|
||||
cpu_eth_init(bis); /* Built in controller(s) come first */
|
||||
return pci_eth_init(bis);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,28 @@
|
|||
#define DEBUG
|
||||
#include <common.h>
|
||||
#include <nbhw.h>
|
||||
#include <dm.h>
|
||||
#include <dm/device.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <asm/io.h>
|
||||
#include <errno.h>
|
||||
#include "../common/nbhw_bd.h"
|
||||
|
||||
#include "../nbhw_bd.h"
|
||||
#include "nbhw18_fpga_regs.h"
|
||||
#include "../nbhw_sim.h"
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct gpio_desc leds[16];
|
||||
struct udevice *driver_dev;
|
||||
|
||||
struct pcieslot {
|
||||
struct gpio_desc reset;
|
||||
struct gpio_desc power;
|
||||
struct gpio_desc wdis_out;
|
||||
struct gpio_desc wdis;
|
||||
};
|
||||
|
||||
struct config_priv {
|
||||
struct pcieslot pcieslots[4];
|
||||
u32 led_count;
|
||||
} priv;
|
||||
|
||||
typedef enum {
|
||||
TYPE_USB,
|
||||
|
|
@ -11,7 +30,7 @@ typedef enum {
|
|||
TYPE_PCIE,
|
||||
} slot_type_t;
|
||||
|
||||
slot_type_t get_type(const int slot)
|
||||
static slot_type_t get_pcie_slot_type(const int slot)
|
||||
{
|
||||
int module;
|
||||
char pdValue[200];
|
||||
|
|
@ -33,73 +52,306 @@ slot_type_t get_type(const int slot)
|
|||
return TYPE_USB;
|
||||
}
|
||||
|
||||
void configure_pcie_slots(void)
|
||||
static int serdes_en_hack(struct gpio_desc *gpio)
|
||||
{
|
||||
/* Make sure PCIe slots have been without power for at least 1.2s */
|
||||
FPGA_REG(PCIE_POWER) = 0x0000; /* Just to be sure, should have been disabled by reset already */
|
||||
udelay(1200000); /* 1.2 s */
|
||||
slot_type_t slot_type = get_pcie_slot_type(0);
|
||||
|
||||
/* Apply SATA power, enable internal MDIO, enable serdes switch,
|
||||
* disable external ethernet, disable hold power on */
|
||||
FPGA_REG(OUTPUT1) = OUTPUT1_RST_WLAN_MOD_N_MASK;
|
||||
/* Lucky for us pcie slot1 has some muxes, to workaround buggy Sierra Wireless Modules */
|
||||
if (slot_type == TYPE_USB) {
|
||||
/* Buggy Sierra Wireless Modules don't like to have USB3 enabled
|
||||
* because the TX of the USB3 Lane is attached to it's system
|
||||
* reset which is the default (see dts) */
|
||||
printf("Slot1: wwan\n");
|
||||
}
|
||||
else if (slot_type == TYPE_PCIE) {
|
||||
dm_gpio_set_value(gpio, 1);
|
||||
printf("Slot1: wlan\n");
|
||||
}
|
||||
else {
|
||||
/* If once we would use other buggy Sierra Wireless modules, where they have
|
||||
* decided to use USB3 too, then we have to enable the SERDES, the reset signal
|
||||
* was moved away from this pins again (bravo!!!) */
|
||||
printf("Slot1: wwan (usb3)\n");
|
||||
}
|
||||
|
||||
udelay(200000); /* 200 ms */
|
||||
return 0;
|
||||
|
||||
/* Apply power to all PCIe slots */
|
||||
FPGA_REG(PCIE_POWER) |= PCIE_POWER_PCIE1_PWR_MASK;
|
||||
udelay(200000); /* 200 ms */
|
||||
FPGA_REG(PCIE_POWER) |= PCIE_POWER_PCIE2_PWR_MASK;
|
||||
udelay(200000); /* 200 ms */
|
||||
FPGA_REG(PCIE_POWER) |= PCIE_POWER_PCIE3_PWR_MASK;
|
||||
udelay(200000); /* 200 ms */
|
||||
|
||||
/* Set PCIe signal directions */
|
||||
/* Inputs: COEX1, COEX2, LED_WLAN, LED_WWAN, WAKE~ */
|
||||
/* Outputs: WDISABLE~=1 */
|
||||
/* PCIe Slot 1 */
|
||||
FPGA_REG(PCIE_1_SLOT_DIR1) = PCIE_1_SLOT_DIR1_WDISABLE_N_DIR_MASK;
|
||||
FPGA_REG(PCIE_1_SLOT_CTRL1) = PCIE_1_SLOT_CTRL1_WDISABLE_N_MASK;
|
||||
|
||||
/* PCIe Slot 2 */
|
||||
FPGA_REG(PCIE_2_SLOT_DIR1) = PCIE_1_SLOT_DIR1_WDISABLE_N_DIR_MASK;
|
||||
FPGA_REG(PCIE_2_SLOT_CTRL1) = PCIE_1_SLOT_CTRL1_WDISABLE_N_MASK;
|
||||
|
||||
/* PCIe Slot 3 */
|
||||
FPGA_REG(PCIE_3_SLOT_DIR1) = PCIE_1_SLOT_DIR1_WDISABLE_N_DIR_MASK;
|
||||
FPGA_REG(PCIE_3_SLOT_CTRL1) = PCIE_1_SLOT_CTRL1_WDISABLE_N_MASK;
|
||||
|
||||
/* Get all PCIe slots out of reset */
|
||||
FPGA_REG(PCIE_RESET) = 0x00;
|
||||
udelay(12000); /* 12 ms */
|
||||
FPGA_REG(PCIE_RESET) =
|
||||
PCIE_RESET_PCIE1_RST_N_MASK |
|
||||
PCIE_RESET_PCIE2_RST_N_MASK |
|
||||
PCIE_RESET_PCIE3_RST_N_MASK;
|
||||
}
|
||||
|
||||
int nbhw_fgpa_init(void)
|
||||
struct pcie_slot_gpios {
|
||||
struct gpio_desc reset;
|
||||
struct gpio_desc power;
|
||||
struct gpio_desc wdis_out;
|
||||
struct gpio_desc wdis;
|
||||
};
|
||||
|
||||
#define PCIE_SLOT_COUNT 8
|
||||
static struct pcie_slot_gpios pcie_slots[PCIE_SLOT_COUNT];
|
||||
|
||||
static int pcie_slot_count = 0;
|
||||
|
||||
static int request_and_set_gpio_by_name(int node, const void *fdt,
|
||||
const char *name, struct gpio_desc *desc)
|
||||
{
|
||||
/* Disable all interrupts from FPGA and clear flags */
|
||||
FPGA_REG(INTMASK1) = 0xFFFF;
|
||||
FPGA_REG(INTMASK2) = 0xFFFF;
|
||||
/* Make sure all interrupts are received */
|
||||
udelay(10000);
|
||||
FPGA_REG(INTACK1) = 0xFFFF;
|
||||
FPGA_REG(INTACK2) = 0xFFFF;
|
||||
int offset;
|
||||
int default_value = 0;
|
||||
debug("%s\n", __func__);
|
||||
|
||||
/* Enable green LED0, disable all other LEDs */
|
||||
FPGA_REG(LED) = LED_LED0_GREEN_MASK;
|
||||
fdt_for_each_property_offset(offset, fdt, node) {
|
||||
u32 gpio_array[4];
|
||||
int len;
|
||||
const char *entry_name;
|
||||
|
||||
/* Enable SIM clocks */
|
||||
FPGA_REG(PCIE_SIM_CLOCK) = 0;
|
||||
/* if it's not the gpio we want (we need to iterate to get node index */
|
||||
fdt_getprop_by_offset(fdt, offset, &entry_name, &len);
|
||||
if (strcmp(entry_name, name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
configure_pcie_slots();
|
||||
/* Request the gpio described by the property */
|
||||
if (gpio_request_by_name_nodev(fdt, node, name, 0, desc,
|
||||
GPIOD_IS_OUT))
|
||||
{
|
||||
printf("Could not request gpio %s\n", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
configure_sim_slots(4);
|
||||
/* Get the gpio array, to find out its default value (4 index) */
|
||||
if (fdtdec_get_int_array(fdt, node, name,
|
||||
gpio_array, 4)) {
|
||||
printf("Could not request gpio array %s\n", name);
|
||||
return -1;
|
||||
|
||||
/* Enable SFP means set disable to zero */
|
||||
FPGA_REG(SFP_CONTROL) = 0;
|
||||
}
|
||||
default_value = gpio_array[3];
|
||||
debug("Set GPIO %s to %d\n", name, default_value);
|
||||
dm_gpio_set_value(desc, default_value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("GPIO %s not found\n", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int add_pcie_slot(int node, const void *fdt)
|
||||
{
|
||||
debug("%s\n", __func__);
|
||||
request_and_set_gpio_by_name(node, fdt, "reset",
|
||||
&pcie_slots[pcie_slot_count].reset);
|
||||
|
||||
request_and_set_gpio_by_name(node, fdt, "power",
|
||||
&pcie_slots[pcie_slot_count].power);
|
||||
|
||||
request_and_set_gpio_by_name(node, fdt, "wdis-out",
|
||||
&pcie_slots[pcie_slot_count].wdis_out);
|
||||
|
||||
request_and_set_gpio_by_name(node, fdt, "wdis",
|
||||
&pcie_slots[pcie_slot_count].wdis);
|
||||
pcie_slot_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int configure_pcie_slots(void)
|
||||
{
|
||||
int i;
|
||||
udelay(1200000); /* 1.2 s */
|
||||
|
||||
for (i = 0; i < pcie_slot_count;i ++) {
|
||||
dm_gpio_set_value(&pcie_slots[i].reset, 1);
|
||||
}
|
||||
|
||||
for (i = 0; i < pcie_slot_count;i ++) {
|
||||
dm_gpio_set_value(&pcie_slots[i].wdis_out, 1);
|
||||
dm_gpio_set_value(&pcie_slots[i].wdis, 0);
|
||||
udelay(1000); /* 1 ms */
|
||||
}
|
||||
|
||||
/* Apply power to all PCIe slots */
|
||||
for (i = 0; i < pcie_slot_count;i ++) {
|
||||
dm_gpio_set_value(&pcie_slots[i].power, 1);
|
||||
udelay(200000); /* 200 ms */
|
||||
}
|
||||
|
||||
for (i = 0; i < pcie_slot_count;i ++) {
|
||||
dm_gpio_set_value(&pcie_slots[i].wdis_out, 1);
|
||||
dm_gpio_set_value(&pcie_slots[i].wdis, 0);
|
||||
udelay(1000); /* 1 ms */
|
||||
}
|
||||
|
||||
udelay(12000); /* 12 ms */
|
||||
for (i = 0; i < pcie_slot_count;i ++) {
|
||||
dm_gpio_set_value(&pcie_slots[i].reset, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int configure_leds(void)
|
||||
{
|
||||
int i;
|
||||
int led_count;
|
||||
|
||||
led_count = gpio_request_list_by_name(driver_dev, "leds", leds,
|
||||
ARRAY_SIZE(leds), GPIOD_IS_OUT);
|
||||
|
||||
dm_gpio_set_value(&leds[0], 1);
|
||||
for (i = 1; i < ARRAY_SIZE(leds); i++) {
|
||||
dm_gpio_set_value(&leds[i], 0);
|
||||
}
|
||||
|
||||
priv.led_count = led_count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct hack_list_entry {
|
||||
const char* name;
|
||||
int (*fn)(struct gpio_desc *gpio);
|
||||
};
|
||||
|
||||
struct hack_list_entry hack_list[] = {
|
||||
{"serdes-en", serdes_en_hack}
|
||||
};
|
||||
|
||||
static int exec_hack_list_fn(const char *name, struct gpio_desc *gpio)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(hack_list); i++) {
|
||||
if (strcmp(hack_list[i]. name, name) == 0) {
|
||||
if (hack_list[i].fn(gpio)) {
|
||||
printf("Hack for %s failed\n", name);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not part of hacklist */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int request_and_set_gpios(int node, const void *fdt,
|
||||
struct gpio_desc *gpios, u32 max_gpios)
|
||||
{
|
||||
int offset;
|
||||
int i = 0;
|
||||
int default_value = 0;
|
||||
debug("%s\n", __func__);
|
||||
|
||||
fdt_for_each_property_offset(offset, fdt, node) {
|
||||
u32 gpio_array[4];
|
||||
int len;
|
||||
const char *name;
|
||||
|
||||
if (i >= max_gpios) {
|
||||
printf("Too many gpio entries (max %d)\n", max_gpios);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Get property at offset */
|
||||
fdt_getprop_by_offset(fdt, offset, &name, &len);
|
||||
if (name[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Request the gpio descriped by the property */
|
||||
if (gpio_request_by_name_nodev(fdt, node, name, 0, &gpios[i],
|
||||
GPIOD_IS_OUT))
|
||||
{
|
||||
printf("Could not request gpio %s\n", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get the gpio array, to find out its default value (4 index) */
|
||||
if (fdtdec_get_int_array(fdt, node, name,
|
||||
gpio_array, 4)) {
|
||||
printf("Could not request gpio array %s\n", name);
|
||||
return -1;
|
||||
|
||||
}
|
||||
default_value = gpio_array[3];
|
||||
debug("Set GPIO %s to %d\n", name, default_value);
|
||||
dm_gpio_set_value(&gpios[i], default_value);
|
||||
|
||||
if (exec_hack_list_fn(name, &gpios[i])) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
static int configure_misc(int node, const void *fdt)
|
||||
{
|
||||
struct gpio_desc misc_gpios[16];
|
||||
int gpio_count;
|
||||
debug("%s\n", __func__);
|
||||
|
||||
gpio_count = request_and_set_gpios(node, fdt,
|
||||
misc_gpios, ARRAY_SIZE(misc_gpios));
|
||||
if (gpio_count < 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
gpio_free_list_nodev(misc_gpios, gpio_count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nbhw_fpga_configure(void)
|
||||
{
|
||||
int subnode;
|
||||
int node = dev_of_offset(driver_dev);
|
||||
const void *fdt = gd->fdt_blob;
|
||||
debug("%s\n", __func__);
|
||||
|
||||
fdt_for_each_subnode(subnode, fdt, node) {
|
||||
int len;
|
||||
const char *name;
|
||||
|
||||
name = fdt_get_name(fdt, subnode, &len);
|
||||
|
||||
debug("Try to configure %s\n", name);
|
||||
if (!strncmp("misc", name, 4)) {
|
||||
configure_misc(subnode, fdt);
|
||||
}
|
||||
|
||||
if (!strncmp("pcieslot", name, 4)) {
|
||||
add_pcie_slot(subnode, fdt);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (configure_leds())
|
||||
return -1;
|
||||
|
||||
if (configure_pcie_slots()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nbhw_fpga_config_bind(struct udevice *dev)
|
||||
{
|
||||
debug("%s\n", __func__);
|
||||
|
||||
driver_dev = dev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id nbhw_fpga_config_ids[] = {
|
||||
{ .compatible = "nm,nbhw17-fpga-config" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(gpio_nbhw_fpga_config) = {
|
||||
.name = "nbhw17_fpga_config",
|
||||
.id = UCLASS_ROOT,
|
||||
.of_match = nbhw_fpga_config_ids,
|
||||
.bind = nbhw_fpga_config_bind,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
#include <nbhw.h>
|
||||
#include "board/mv_ebu/common/common/mvCommon.h"
|
||||
#include "board/mv_ebu/common/mv_hal/gpp/mvGpp.h"
|
||||
#include "../nbhw_gpio.h"
|
||||
#include "nbhw18_fpga_regs.h"
|
||||
#include <common.h>
|
||||
#include <asm/gpio.h>
|
||||
|
||||
#define SET_LOGIC(enable,group,bit) \
|
||||
if (enable) \
|
||||
|
|
@ -10,33 +7,37 @@ if (enable) \
|
|||
else \
|
||||
mvGppValueSet(group, bit, 0);
|
||||
|
||||
#define GPIO_RESET_BUTTON (14)
|
||||
|
||||
void init_gpios(void)
|
||||
{
|
||||
gpio_request(GPIO_RESET_BUTTON, "GPIO_RESET_BUTTON");
|
||||
gpio_direction_input(GPIO_RESET_BUTTON);
|
||||
}
|
||||
|
||||
void out_usb_power(int enable)
|
||||
{
|
||||
SET_LOGIC(enable, 1, BIT9);
|
||||
// SET_LOGIC(enable, 1, BIT9);
|
||||
}
|
||||
|
||||
void out_watchdog(int enable)
|
||||
{
|
||||
SET_LOGIC(enable, 0, BIT19);
|
||||
// SET_LOGIC(enable, 0, BIT19);
|
||||
}
|
||||
|
||||
void out_fpga_logic_reset(int enable)
|
||||
{
|
||||
SET_LOGIC(enable, 1, BIT12);
|
||||
// SET_LOGIC(enable, 1, BIT12);
|
||||
}
|
||||
|
||||
void out_ext_reset_en(int enable)
|
||||
{
|
||||
SET_LOGIC(enable, 0, BIT7);
|
||||
// SET_LOGIC(enable, 0, BIT7);
|
||||
}
|
||||
|
||||
int in_reset_button(void)
|
||||
{
|
||||
return mvGppValueGet(0, BIT14);
|
||||
return gpio_get_value(GPIO_RESET_BUTTON);
|
||||
}
|
||||
|
||||
int in_fpga_cdone(void)
|
||||
{
|
||||
return mvGppValueGet(0, BIT29);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
#ifndef NBHW18_GPIO_H
|
||||
#define NBHW18_GPIO_H
|
||||
#include "../nbhw_gpio.h"
|
||||
#include "../common/nbhw_gpio.h"
|
||||
|
||||
void init_gpios(void);
|
||||
|
||||
void out_usb_power(int enable);
|
||||
void out_watchdog(int enable);
|
||||
void out_fpga_logic_reset(int enable);
|
||||
|
||||
int in_reset_button(void);
|
||||
int in_fpga_cdone(void);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,75 +0,0 @@
|
|||
/******************************************************************************
|
||||
* (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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
/* #define DEBUG */
|
||||
#include <environment.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "../nbhw_init.h"
|
||||
#include "../nbhw_bd.h"
|
||||
|
||||
#include "board/mv_ebu/a38x/armada_38x_family/boardEnv/mvBoardEnvLib.h"
|
||||
#include "board/mv_ebu/common/mv_hal/gpp/mvGpp.h"
|
||||
|
||||
unsigned int CONFIG_SYS_DUART_CHAN = 0;
|
||||
|
||||
extern MV_STATUS mvEthPhyRegWrite(MV_U32 phyAddr, MV_U32 regOffs, MV_U16 data);
|
||||
|
||||
static void set_phy_page(int phy_addr, int page)
|
||||
{
|
||||
mvEthPhyRegWrite(phy_addr, 22, page);
|
||||
}
|
||||
|
||||
static void set_phy_fast_blink_mode(int phy_addr)
|
||||
{
|
||||
set_phy_page(phy_addr, 3);
|
||||
mvEthPhyRegWrite(phy_addr, 16, 0x1032);
|
||||
mvEthPhyRegWrite(phy_addr, 17, 0x4405);
|
||||
mvEthPhyRegWrite(phy_addr, 18, 0x4A08);
|
||||
set_phy_page(phy_addr, 0);
|
||||
}
|
||||
|
||||
static void configure_phys(void)
|
||||
{
|
||||
set_phy_fast_blink_mode(0);
|
||||
set_phy_fast_blink_mode(1);
|
||||
}
|
||||
|
||||
int nbhw_init(void)
|
||||
{
|
||||
|
||||
printf("NBHW18\n");
|
||||
|
||||
debug("Disable watchdog\n");
|
||||
out_watchdog(1);
|
||||
|
||||
debug("Enable USB Hub\n");
|
||||
out_usb_power(1);
|
||||
udelay(10000);
|
||||
|
||||
debug("Load environment\n");
|
||||
env_relocate_spec();
|
||||
|
||||
nbhw_settings_init(); /* Parse the board descriptor */
|
||||
|
||||
set_mac_addresses(3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nbhw_phy_init(void)
|
||||
{
|
||||
debug("Configure phys\n");
|
||||
configure_phys();
|
||||
}
|
||||
|
|
@ -0,0 +1,228 @@
|
|||
/******************************************************************************
|
||||
* (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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
/* #define DEBUG */
|
||||
#include <common.h>
|
||||
#include <environment.h>
|
||||
#include <asm/io.h>
|
||||
#include <u-boot/md5.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "../common/nbhw_bd.h"
|
||||
|
||||
static void pcie_fixup_modify(u32 reg, u32 val, u32 mask)
|
||||
{
|
||||
u32 regval;
|
||||
regval = readl(reg);
|
||||
regval &= ~mask;
|
||||
regval |= (val & mask);
|
||||
writel(regval, reg);
|
||||
}
|
||||
|
||||
/* We have a problem with the hardware during the detection phase this hack
|
||||
* reduces the detection timeout from 4ns to 0ns so a minmal impulse will
|
||||
* already trigger a detection. This is necessary to work with WLE600 modules. */
|
||||
static void pcie_fixup(void)
|
||||
{
|
||||
/* SERDES0 PCIE0 = SLOT1 */
|
||||
#define PCIE0_USB3_CONTROL_REG 0xF10A0120
|
||||
/* SERDES4 PCIE2 = SLOT0 */
|
||||
#define PCIE2_USB3_CONTROL_REG 0xF10A2120
|
||||
#define PCIE_USB3_CONTROL_TIMING_MASK 0xC0
|
||||
#define PCIE_USB3_CONTROL_TIMING_VALUE 0x00
|
||||
|
||||
pcie_fixup_modify(PCIE0_USB3_CONTROL_REG, PCIE_USB3_CONTROL_TIMING_VALUE, PCIE_USB3_CONTROL_TIMING_MASK);
|
||||
pcie_fixup_modify(PCIE2_USB3_CONTROL_REG, PCIE_USB3_CONTROL_TIMING_VALUE, PCIE_USB3_CONTROL_TIMING_MASK);
|
||||
}
|
||||
|
||||
static int has_slot_wlan(int slot)
|
||||
{
|
||||
int module;
|
||||
char slotDescr[20];
|
||||
char pdValue[200];
|
||||
|
||||
sprintf(slotDescr, "slot=%d", slot);
|
||||
for (module=0; module<4; module++) {
|
||||
strcpy(pdValue, "" ); /*init with an empty string*/
|
||||
if (bd_get_pd_module(module, pdValue, sizeof(pdValue))==0) {
|
||||
/* Wifi module needs PCIe */
|
||||
if ((strstr(pdValue, slotDescr)) && (strstr(pdValue, "wlan-")))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* PCIE0 is on slot 1 (sw)*/
|
||||
#define PCIE0_STATUS_REG 0xF1081A04
|
||||
/* PCIE2 is on slot 0 (sw)*/
|
||||
#define PCIE2_STATUS_REG 0xF1045A04
|
||||
|
||||
#define PCIE_DL_DOWN_MASK 0x01
|
||||
|
||||
static int has_pcie_link_on_slot(int slot)
|
||||
{
|
||||
/* If there is no module populated we say we have link (because we don't care) */
|
||||
switch (slot) {
|
||||
case 0:
|
||||
if (readw(PCIE2_STATUS_REG) & PCIE_DL_DOWN_MASK)
|
||||
return 0;
|
||||
break;
|
||||
case 1:
|
||||
if (readw(PCIE0_STATUS_REG) & PCIE_DL_DOWN_MASK)
|
||||
return 0;
|
||||
break;
|
||||
default:
|
||||
printf("Slot %d does not support PCIE\n", slot);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int check_pcie_slot_status(int slot){
|
||||
int ret = 1;
|
||||
/* If WLAN on slot we need to check if we have link */
|
||||
if (has_slot_wlan(slot)) {
|
||||
ret = has_pcie_link_on_slot(slot);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The reset counter is at 256 MB, make sure it is not overlapping the address of the reset-reason mechanim,
|
||||
* which is located at 1GB - 512kB. The reset reason counter is necessary to not try to reset
|
||||
* the system more than 3 times */
|
||||
#define RESET_COUNTER_ADDRESS ((int *)0x10000000)
|
||||
#define RESET_COUNTER_MD5 (RESET_COUNTER_ADDRESS + 4)
|
||||
int get_reset_counter(void)
|
||||
{
|
||||
volatile int *reset_counter = RESET_COUNTER_ADDRESS;
|
||||
unsigned char reset_counter_checksum[16];
|
||||
|
||||
debug("reset_counter before inc: %d\n", *reset_counter);
|
||||
|
||||
md5((unsigned char*) reset_counter, 4, reset_counter_checksum);
|
||||
debug("md5 before inc: 0x%08X\n", *(unsigned int*)reset_counter_checksum);
|
||||
/* Invalid checksum means it's the first boot. In this case we should see the counter
|
||||
* as zero */
|
||||
if (memcmp(reset_counter_checksum, RESET_COUNTER_MD5, 16) != 0) {
|
||||
*reset_counter = 0;
|
||||
}
|
||||
|
||||
return *reset_counter;
|
||||
}
|
||||
|
||||
int inc_reset_counter(void)
|
||||
{
|
||||
volatile int *reset_counter = RESET_COUNTER_ADDRESS;
|
||||
unsigned char reset_counter_checksum[16];
|
||||
|
||||
(*reset_counter)++;
|
||||
debug("reset_counter after inc: %d\n", *reset_counter);
|
||||
md5((unsigned char*) reset_counter, 4, reset_counter_checksum);
|
||||
debug("md5 after inc: 0x%08X\n", *(unsigned int*)reset_counter_checksum);
|
||||
memcpy(RESET_COUNTER_MD5, reset_counter_checksum, 16);
|
||||
|
||||
return *reset_counter;
|
||||
|
||||
}
|
||||
|
||||
int reset_reset_counter(void)
|
||||
{
|
||||
volatile int *reset_counter = RESET_COUNTER_ADDRESS;
|
||||
|
||||
/* We don't need to do anything with the checksum, because invalid checksum = 0 */
|
||||
(*reset_counter) = 0;
|
||||
|
||||
return *reset_counter;
|
||||
|
||||
}
|
||||
|
||||
static int is_module_check_abort(void) {
|
||||
char c;
|
||||
/* Check for abort */
|
||||
if (tstc()) {
|
||||
c = getc();
|
||||
if (c == 'a') {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if pcie link is detected for wlan modules, some WLE600 modules have
|
||||
* even with the pcie_fixup patch a problem that they don't appear during the first
|
||||
* boot */
|
||||
static int do_wlan_fixup(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
||||
{
|
||||
int i;
|
||||
#define MAX_RESET_COUNT 6
|
||||
int reset_counter = get_reset_counter();
|
||||
int ret;
|
||||
|
||||
pcie_fixup();
|
||||
|
||||
/* If we do a reboot it can happen that reset_counter is still 3, in this case
|
||||
* we need to restart the counter */
|
||||
if (reset_counter >= MAX_RESET_COUNT) {
|
||||
reset_counter = reset_reset_counter();
|
||||
}
|
||||
|
||||
puts("Checking PCIe WLAN modules (abort with a)\n");
|
||||
|
||||
/* Try it for at least one second */
|
||||
for (i = 0; i < 100; i++) {
|
||||
ret = 3;
|
||||
if (check_pcie_slot_status(0)) ret &= ~1;
|
||||
|
||||
if (check_pcie_slot_status(1)) ret &= ~2;
|
||||
|
||||
if (is_module_check_abort()) {
|
||||
puts("Abort WLAN module check\n");
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (ret == 0) break;
|
||||
|
||||
udelay(10000);
|
||||
}
|
||||
|
||||
/* One module that should have link does not have link */
|
||||
if (ret) {
|
||||
reset_counter = inc_reset_counter();
|
||||
if (reset_counter >= MAX_RESET_COUNT) {
|
||||
printf("WLAN Modules missing but reset counter reached %d, continue\n", reset_counter);
|
||||
return 0;
|
||||
}
|
||||
printf ("Not all WLAN Modules from the bd came up (ret=0x%08X), reset (try %d of %d)\n", ret, reset_counter, MAX_RESET_COUNT);
|
||||
printf ("Wait with reset for %ds\n", reset_counter * 10);
|
||||
udelay(10000000 * reset_counter);
|
||||
do_reset(NULL, 0, 0, NULL);
|
||||
}
|
||||
|
||||
/* Reset the counter in case of success */
|
||||
reset_reset_counter();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_CMD(
|
||||
nbhw_wlan_fixup, 1, 1, do_wlan_fixup,
|
||||
"NBHW WLAN fixup for some Wifi modules",
|
||||
"Checks if WLAN modules have been detected correctly, or resets\n"
|
||||
);
|
||||
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
|
|
|||
|
|
@ -78,16 +78,16 @@
|
|||
"root_part=1\0" /* Default root partition, overwritte in board file */ \
|
||||
"eth1addr=00:11:22:33:44:55\0" \
|
||||
"eth2addr=00:11:22:33:44:56\0" \
|
||||
"eth3addr=00:11:22:33:44:57\0" \
|
||||
"ethact=ethernet@34000\0" \
|
||||
"add_sd_bootargs=setenv bootargs $bootargs root=/dev/mmcblk0p$root_part rootfstype=ext4 console=ttyS1,115200 rootwait loglevel=4\0" \
|
||||
"ethact=ethernet@30000\0" \
|
||||
"add_sd_bootargs=setenv bootargs $bootargs root=/dev/mmcblk0p$root_part rootfstype=ext4 console=ttyS1,115200 rootwait\0" \
|
||||
"add_version_bootargs=setenv bootargs $bootargs\0" \
|
||||
"fdt_skip_update=yes\0" \
|
||||
"sdbringup=echo Try bringup boot && ext4load mmc 0:$root_part $kernel_addr /boot/zImage && " \
|
||||
"ext4load mmc 0:$root_part $fdt_addr /boot/$fdt_image && setenv bootargs $bootargs rw;\0" \
|
||||
"sdprod=ext4load mmc 0:$root_part $kernel_addr /boot/$kernel_image && " \
|
||||
"ext4load mmc 0:$root_part $fdt_addr /boot/$fdt_image && setenv bootargs $bootargs ro;\0" \
|
||||
"sdboot=if mmc dev 0; then echo Copying Linux from SD to RAM...; "\
|
||||
"sdboot=if mmc dev 0; then echo Load FPGA...; nbhw_wlan_fixup &&" \
|
||||
"echo Copying Linux from SD to RAM...; "\
|
||||
"if test -e mmc 0:$root_part /boot/$kernel_image; then run sdprod; " \
|
||||
"else run sdbringup; fi; " \
|
||||
"run add_sd_bootargs; run add_version_bootargs; " \
|
||||
|
|
@ -106,6 +106,7 @@
|
|||
"autoload=false\0" \
|
||||
"tftp_recovery=tftpboot $kernel_addr recovery-image; tftpboot $fdt_addr recovery-dtb; setenv bootargs rdinit=/etc/preinit console=ttyO1,115200 debug; bootz $kernel_addr - $fdt_addr\0" \
|
||||
"pxe_recovery=sleep 3 && dhcp && pxe get && pxe boot\0" \
|
||||
"load_fpga=ext4load mmc 0:$root_part $kernel_addr /logic/LG00000000 && nbhw_fpga program lattice 0xffffffff $kernel_addr $filesize && nbhw_fpga configure\0" \
|
||||
"recovery=run pxe_recovery || setenv ipaddr $ipaddr; setenv serverip $serverip; run tftp_recovery\0" /* setenv ipaddr and serverip is necessary, because dhclient can destroy the IPs inernally */
|
||||
|
||||
#endif
|
||||
|
|
@ -162,4 +163,16 @@
|
|||
*/
|
||||
#include "mv-common.h"
|
||||
|
||||
/*
|
||||
* NBHW needs md5 support
|
||||
*/
|
||||
#define CONFIG_MD5
|
||||
|
||||
/*
|
||||
* We need some functions to be called after environment setup
|
||||
*/
|
||||
#define CONFIG_BOARD_LATE_INIT
|
||||
|
||||
#define CONFIG_LAST_STAGE_INIT
|
||||
|
||||
#endif /* _CONFIG_DB_88F6820_GP_H */
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#define DEBUG
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <environment.h>
|
||||
|
|
@ -179,22 +179,33 @@ int eth_get_dev_index(void)
|
|||
return -1;
|
||||
}
|
||||
|
||||
|
||||
#define PR_DEBUG debug("%s:%d\n", __FILE__, __LINE__);
|
||||
|
||||
static int eth_write_hwaddr(struct udevice *dev)
|
||||
{
|
||||
struct eth_pdata *pdata = dev->platdata;
|
||||
int ret = 0;
|
||||
|
||||
PR_DEBUG
|
||||
if (!dev || !device_active(dev))
|
||||
return -EINVAL;
|
||||
|
||||
PR_DEBUG
|
||||
|
||||
debug("%p: %d\n", eth_get_ops(dev)->write_hwaddr,eth_mac_skip(dev->seq) );
|
||||
/* seq is valid since the device is active */
|
||||
if (eth_get_ops(dev)->write_hwaddr && !eth_mac_skip(dev->seq)) {
|
||||
PR_DEBUG
|
||||
if (!is_valid_ethaddr(pdata->enetaddr)) {
|
||||
printf("\nError: %s address %pM illegal value\n",
|
||||
dev->name, pdata->enetaddr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
debug("%s set %x:%x\n", dev->name, pdata->enetaddr[0], pdata->enetaddr[1] );
|
||||
|
||||
PR_DEBUG
|
||||
/*
|
||||
* Drivers are allowed to decide not to implement this at
|
||||
* run-time. E.g. Some devices may use it and some may not.
|
||||
|
|
@ -205,6 +216,7 @@ static int eth_write_hwaddr(struct udevice *dev)
|
|||
if (ret)
|
||||
printf("\nWarning: %s failed to set MAC address\n",
|
||||
dev->name);
|
||||
PR_DEBUG
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -246,6 +258,7 @@ int eth_init(void)
|
|||
struct udevice *old_current;
|
||||
int ret = -ENODEV;
|
||||
|
||||
PR_DEBUG
|
||||
/*
|
||||
* When 'ethrotate' variable is set to 'no' and 'ethact' variable
|
||||
* is already set to an ethernet device, we should stick to 'ethact'.
|
||||
|
|
@ -258,6 +271,7 @@ int eth_init(void)
|
|||
}
|
||||
}
|
||||
|
||||
PR_DEBUG
|
||||
if (!current) {
|
||||
current = eth_get_dev();
|
||||
if (!current) {
|
||||
|
|
@ -266,9 +280,11 @@ int eth_init(void)
|
|||
}
|
||||
}
|
||||
|
||||
PR_DEBUG
|
||||
old_current = current;
|
||||
do {
|
||||
if (current) {
|
||||
PR_DEBUG
|
||||
debug("Trying %s\n", current->name);
|
||||
|
||||
if (device_active(current)) {
|
||||
|
|
@ -289,6 +305,7 @@ int eth_init(void)
|
|||
debug("PROBE FAIL\n");
|
||||
}
|
||||
|
||||
PR_DEBUG
|
||||
/*
|
||||
* If ethrotate is enabled, this will change "current",
|
||||
* otherwise we will drop out of this while loop immediately
|
||||
|
|
@ -296,6 +313,7 @@ int eth_init(void)
|
|||
eth_try_another(0);
|
||||
/* This will ensure the new "current" attempted to probe */
|
||||
current = eth_get_dev();
|
||||
PR_DEBUG
|
||||
} while (old_current != current);
|
||||
|
||||
return ret;
|
||||
|
|
@ -387,6 +405,8 @@ int eth_initialize(void)
|
|||
int num_devices = 0;
|
||||
struct udevice *dev;
|
||||
|
||||
debug("%s\n", __func__);
|
||||
|
||||
eth_common_init();
|
||||
|
||||
/*
|
||||
|
|
@ -417,7 +437,7 @@ int eth_initialize(void)
|
|||
if (num_devices)
|
||||
printf(", ");
|
||||
|
||||
printf("eth%d: %s", dev->seq, dev->name);
|
||||
printf("%s", dev->name);
|
||||
|
||||
if (ethprime && dev == prime_dev)
|
||||
printf(" [PRIME]");
|
||||
|
|
|
|||
|
|
@ -26,16 +26,7 @@ void eth_parse_enetaddr(const char *addr, uchar *enetaddr)
|
|||
|
||||
int eth_getenv_enetaddr(const char *name, uchar *enetaddr)
|
||||
{
|
||||
printf("eth: %s\n", name);
|
||||
eth_parse_enetaddr(getenv(name), enetaddr);
|
||||
printf("enetaddr: %x:%x:%x:%x:%x:%x\n",
|
||||
enetaddr[0],
|
||||
enetaddr[1],
|
||||
enetaddr[2],
|
||||
enetaddr[3],
|
||||
enetaddr[4],
|
||||
enetaddr[5]);
|
||||
printf("is valid: %d\n", is_valid_ethaddr(enetaddr));
|
||||
return is_valid_ethaddr(enetaddr);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue