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(0x01, 0x1d) 0 0xfff00000 0x100000
|
||||||
MBUS_ID(0x09, 0x19) 0 0xf1100000 0x10000 /* CESA 0 SRAM */
|
MBUS_ID(0x09, 0x19) 0 0xf1100000 0x10000 /* CESA 0 SRAM */
|
||||||
MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000 /* CESA 1 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 {
|
gpiofpga: gpio@fd0000000 {
|
||||||
status = "okay";
|
compatible = "nm,nbhw-fpga";
|
||||||
ranges = <0 MBUS_ID(0x01, 0x3e) 0 0x20000>;
|
reg = <MBUS_ID(0x01, 0x3e) 0 0x20000>; /* This translates to 0xfd000000 */
|
||||||
devbus,keep-config;
|
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 {
|
internal-regs {
|
||||||
|
|
@ -69,7 +132,6 @@
|
||||||
broken-cd;
|
broken-cd;
|
||||||
wp-inverted;
|
wp-inverted;
|
||||||
no-1-8-v;
|
no-1-8-v;
|
||||||
clock-freq-min-max=<0 50000000>;
|
|
||||||
bus-width = <8>;
|
bus-width = <8>;
|
||||||
status = "okay";
|
status = "okay";
|
||||||
};
|
};
|
||||||
|
|
@ -85,6 +147,7 @@
|
||||||
pinctrl-0 = <&uart1_pins>;
|
pinctrl-0 = <&uart1_pins>;
|
||||||
u-boot,dm-pre-reloc;
|
u-boot,dm-pre-reloc;
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pcie-controller {
|
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 {
|
ð0 {
|
||||||
|
|
@ -139,12 +194,12 @@
|
||||||
pinctrl-0 = <&mdio_pins>;
|
pinctrl-0 = <&mdio_pins>;
|
||||||
|
|
||||||
phy0: ethernet-phy@0 {
|
phy0: ethernet-phy@0 {
|
||||||
marvell,reg-init = <3 16 0 0x101e>;
|
marvell,reg-init = <3 16 0 0x101e>;
|
||||||
reg = <0>;
|
reg = <0>;
|
||||||
};
|
};
|
||||||
|
|
||||||
phy1: ethernet-phy@1 {
|
phy1: ethernet-phy@1 {
|
||||||
marvell,reg-init = <3 16 0 0x101e>;
|
marvell,reg-init = <3 16 0 0x101e>;
|
||||||
reg = <1>;
|
reg = <1>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -17,20 +17,15 @@
|
||||||
model = "NetModule Router NBHW17 with Armada A385 (NB2800)";
|
model = "NetModule Router NBHW17 with Armada A385 (NB2800)";
|
||||||
};
|
};
|
||||||
|
|
||||||
ð0 {
|
|
||||||
status = "okay";
|
|
||||||
};
|
|
||||||
|
|
||||||
// SFP-Port
|
|
||||||
ð1 {
|
ð1 {
|
||||||
status = "okay";
|
status = "okay";
|
||||||
fixed-link {
|
|
||||||
speed = <1000>;
|
|
||||||
full-duplex;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ð2 {
|
ð2 {
|
||||||
status = "okay";
|
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) */
|
/* Check whether this is a valid board descriptor (or empty EEPROM) */
|
||||||
rc = BD_CheckHeader( bdctx, bdHeader );
|
rc = BD_CheckHeader( bdctx, bdHeader );
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
printf("%s() No valid board descriptor found\n", __func__);
|
debug("%s() No valid board descriptor found\n", __func__);
|
||||||
goto exit1;
|
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 */
|
/* If we not have a Bootpartition entry, perhaps we have a partition table */
|
||||||
return try_partition_read();
|
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 "bdparser.h" /* tlv parser */
|
||||||
#include <linux/types.h>
|
#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 nbhw_settings_init(void);
|
||||||
void bd_register_context_list(const BD_Context *list, size_t count);
|
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);
|
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);
|
void bd_get_hw_version(int* ver, int* rev);
|
||||||
int bd_get_devicetree(char* devicetreename, size_t len);
|
int bd_get_devicetree(char* devicetreename, size_t len);
|
||||||
int bd_get_boot_partition(void);
|
int bd_get_boot_partition(void);
|
||||||
|
enum serdes_type bd_get_serdes_type(int serdes);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ void find_and_set_active_partition(void)
|
||||||
|
|
||||||
void set_console(void)
|
void set_console(void)
|
||||||
{
|
{
|
||||||
int len;
|
|
||||||
char buf[50] = "\n";
|
char buf[50] = "\n";
|
||||||
char *defaultconsole = getenv("defaultconsole");
|
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) {
|
if (interface == 0) {
|
||||||
/* Serial number consists of 1st MAC address */
|
/* Serial number consists of 1st MAC address */
|
||||||
setenv("ethaddr", macstring);
|
setenv("ethaddr", macstring);
|
||||||
printf("Serial Number: %s\n", macstring);
|
|
||||||
}
|
}
|
||||||
/* Always set eth%daddr because newer u-boots expect it */
|
/* 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);
|
sprintf(ethaddr, "eth%daddr", interface);
|
||||||
/* The other ethaddrs are set trough ethaddr1,2,3 etc. */
|
/* The other ethaddrs are set trough ethaddr1,2,3 etc. */
|
||||||
setenv(ethaddr, macstring);
|
setenv(ethaddr, macstring);
|
||||||
printf("MAC%d: %s\n", interface, macstring);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,17 @@
|
||||||
commonobj = ../common/bdparser.o \
|
commonobj = ../common/bdparser.o \
|
||||||
../common/nbhw_bd.o \
|
../common/nbhw_bd.o \
|
||||||
../common/nbhw_env.o \
|
../common/nbhw_env.o \
|
||||||
../common/nbhw_fpga_prog.o \
|
|
||||||
../common/nbhw_init.o \
|
../common/nbhw_init.o \
|
||||||
../common/nbhw_fileaccess.o \
|
../common/nbhw_fileaccess.o \
|
||||||
|
../common/nbhw_fpga_gpio.o \
|
||||||
../common/nbhw_partitions.o
|
../common/nbhw_partitions.o
|
||||||
|
|
||||||
ccflags-y := -I../common
|
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+
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
*/
|
*/
|
||||||
|
#define DEBUG
|
||||||
#include <common.h>
|
#include <common.h>
|
||||||
#include <i2c.h>
|
#include <i2c.h>
|
||||||
#include <miiphy.h>
|
#include <miiphy.h>
|
||||||
|
|
@ -13,9 +13,13 @@
|
||||||
#include <asm/arch/soc.h>
|
#include <asm/arch/soc.h>
|
||||||
#include <mmc.h>
|
#include <mmc.h>
|
||||||
#include <spl.h>
|
#include <spl.h>
|
||||||
|
#include <linux/mbus.h>
|
||||||
|
#include <environment.h>
|
||||||
|
|
||||||
#include <asm/gpio.h>
|
#include <asm/gpio.h>
|
||||||
|
|
||||||
|
#include "nbhw_gpio.h"
|
||||||
|
|
||||||
#include "../common/nbhw_init.h"
|
#include "../common/nbhw_init.h"
|
||||||
#include "../common/nbhw_env.h"
|
#include "../common/nbhw_env.h"
|
||||||
#include "../common/nbhw_bd.h"
|
#include "../common/nbhw_bd.h"
|
||||||
|
|
@ -43,6 +47,8 @@ DECLARE_GLOBAL_DATA_PTR;
|
||||||
#define PD_ADDRESS (0x0200) /* Product descriptor */
|
#define PD_ADDRESS (0x0200) /* Product descriptor */
|
||||||
#define PARTITION_ADDRESS (0x0600) /* Partition Table */
|
#define PARTITION_ADDRESS (0x0600) /* Partition Table */
|
||||||
|
|
||||||
|
#define DEV_CS0_BASE 0xfd000000
|
||||||
|
|
||||||
static struct serdes_map board_serdes_map[] = {
|
static struct serdes_map board_serdes_map[] = {
|
||||||
{ PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0 },
|
{ PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0 },
|
||||||
{ SGMII1, SERDES_SPEED_1_25_GBPS, SERDES_DEFAULT_MODE, 0, 0 },
|
{ SGMII1, SERDES_SPEED_1_25_GBPS, SERDES_DEFAULT_MODE, 0, 0 },
|
||||||
|
|
@ -68,16 +74,16 @@ static struct hws_topology_map board_topology_map = {
|
||||||
0x1, /* active interfaces */
|
0x1, /* active interfaces */
|
||||||
/* cs_mask, mirror, dqs_swap, ck_swap X PUPs */
|
/* cs_mask, mirror, dqs_swap, ck_swap X PUPs */
|
||||||
{ { { {0x1, 0, 0, 0},
|
{ { { {0x1, 0, 0, 0},
|
||||||
{0x1, 0, 0, 0},
|
{0x1, 0, 0, 0},
|
||||||
{0x1, 0, 0, 0},
|
{0x1, 0, 0, 0},
|
||||||
{0x1, 0, 0, 0},
|
{0x1, 0, 0, 0},
|
||||||
{0x1, 0, 0, 0} },
|
{0x1, 0, 0, 0} },
|
||||||
SPEED_BIN_DDR_1600K, /* speed_bin */
|
SPEED_BIN_DDR_1600K, /* speed_bin */
|
||||||
BUS_WIDTH_16, /* memory_width */
|
BUS_WIDTH_16, /* memory_width */
|
||||||
MEM_4G, /* mem_size */
|
MEM_4G, /* mem_size */
|
||||||
DDR_FREQ_667, /* frequency */
|
DDR_FREQ_667, /* frequency */
|
||||||
7, 9, /* cas_l cas_wl */
|
7, 9, /* cas_l cas_wl */
|
||||||
HWS_TEMP_HIGH} }, /* temperature */
|
HWS_TEMP_HIGH} }, /* temperature */
|
||||||
5, /* Num Of Bus Per Interface*/
|
5, /* Num Of Bus Per Interface*/
|
||||||
BUS_MASK_32BIT /* Busses mask */
|
BUS_MASK_32BIT /* Busses mask */
|
||||||
};
|
};
|
||||||
|
|
@ -112,32 +118,30 @@ int board_early_init_f(void)
|
||||||
writel(GPP_OUT_ENA_LOW, MVEBU_GPIO0_BASE + 0x04);
|
writel(GPP_OUT_ENA_LOW, MVEBU_GPIO0_BASE + 0x04);
|
||||||
writel(GPP_OUT_ENA_MID, MVEBU_GPIO1_BASE + 0x04);
|
writel(GPP_OUT_ENA_MID, MVEBU_GPIO1_BASE + 0x04);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 spl_boot_mode(const u32 boot_device)
|
u32 spl_boot_mode(const u32 boot_device)
|
||||||
{
|
{
|
||||||
return MMCSD_MODE_EMMCBOOT;
|
return MMCSD_MODE_EMMCBOOT;
|
||||||
}
|
}
|
||||||
|
|
||||||
void spl_board_init(void)
|
void spl_board_init(void)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
struct mmc *mmcp;
|
struct mmc *mmcp;
|
||||||
|
|
||||||
err = mmc_initialize(NULL);
|
err = mmc_initialize(NULL);
|
||||||
if (err)
|
if (err)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
puts("SPL: select partition\n");
|
puts("SPL: select partition\n");
|
||||||
mmcp = find_mmc_device(0);
|
mmcp = find_mmc_device(0);
|
||||||
mmc_init(mmcp);
|
mmc_init(mmcp);
|
||||||
printf("Partition found: %p\n", mmcp);
|
printf("Partition found: %p\n", mmcp);
|
||||||
/* select boot0 as bootpart */
|
/* select boot0 as bootpart */
|
||||||
mmcp->part_config |= (1 << 3);
|
mmcp->part_config |= (1 << 3);
|
||||||
puts("Partition switched to boot0\n");
|
puts("Partition switched to boot0\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static BD_Context bdctx[3]; /* The descriptor context */
|
static BD_Context bdctx[3]; /* The descriptor context */
|
||||||
|
|
@ -150,7 +154,7 @@ static int _bd_init(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bd_get_context(&bdctx[1], BD_EEPROM_ADDR, PD_ADDRESS) != 0) {
|
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) {
|
if (bd_get_context(&bdctx[2], BD_EEPROM_ADDR, PARTITION_ADDRESS) != 0) {
|
||||||
|
|
@ -158,12 +162,42 @@ static int _bd_init(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
bd_register_context_list(bdctx, ARRAY_SIZE(bdctx));
|
bd_register_context_list(bdctx, ARRAY_SIZE(bdctx));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int __maybe_unused read_eeprom(void)
|
static inline int __maybe_unused read_eeprom(void)
|
||||||
{
|
{
|
||||||
return _bd_init();
|
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)
|
int board_init(void)
|
||||||
|
|
@ -171,21 +205,62 @@ int board_init(void)
|
||||||
/* adress of boot parameters */
|
/* adress of boot parameters */
|
||||||
gd->bd->bi_boot_params = mvebu_sdram_bar(0) + 0x100;
|
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)
|
if (read_eeprom() < 0)
|
||||||
puts("Could not get board ID.\n");
|
puts("Could not get board ID.\n");
|
||||||
|
|
||||||
set_mac_addresses(2);
|
set_console();
|
||||||
find_and_set_active_partition();
|
|
||||||
set_console();
|
|
||||||
|
|
||||||
gpio_request(21, "RST_ETH_PHY_N");
|
set_gpios();
|
||||||
gpio_direction_output(21, 1);
|
|
||||||
|
|
||||||
#if 0
|
return 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);
|
static void set_phy_page(const char *miidev, int phy_addr, int page)
|
||||||
#endif
|
{
|
||||||
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -197,8 +272,5 @@ int checkboard(void)
|
||||||
return 0;
|
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 <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"
|
DECLARE_GLOBAL_DATA_PTR;
|
||||||
#include "nbhw18_fpga_regs.h"
|
|
||||||
#include "../nbhw_sim.h"
|
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 {
|
typedef enum {
|
||||||
TYPE_USB,
|
TYPE_USB,
|
||||||
|
|
@ -11,7 +30,7 @@ typedef enum {
|
||||||
TYPE_PCIE,
|
TYPE_PCIE,
|
||||||
} slot_type_t;
|
} slot_type_t;
|
||||||
|
|
||||||
slot_type_t get_type(const int slot)
|
static slot_type_t get_pcie_slot_type(const int slot)
|
||||||
{
|
{
|
||||||
int module;
|
int module;
|
||||||
char pdValue[200];
|
char pdValue[200];
|
||||||
|
|
@ -33,73 +52,306 @@ slot_type_t get_type(const int slot)
|
||||||
return TYPE_USB;
|
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 */
|
slot_type_t slot_type = get_pcie_slot_type(0);
|
||||||
FPGA_REG(PCIE_POWER) = 0x0000; /* Just to be sure, should have been disabled by reset already */
|
|
||||||
udelay(1200000); /* 1.2 s */
|
|
||||||
|
|
||||||
/* Apply SATA power, enable internal MDIO, enable serdes switch,
|
/* Lucky for us pcie slot1 has some muxes, to workaround buggy Sierra Wireless Modules */
|
||||||
* disable external ethernet, disable hold power on */
|
if (slot_type == TYPE_USB) {
|
||||||
FPGA_REG(OUTPUT1) = OUTPUT1_RST_WLAN_MOD_N_MASK;
|
/* 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 */
|
int offset;
|
||||||
FPGA_REG(INTMASK1) = 0xFFFF;
|
int default_value = 0;
|
||||||
FPGA_REG(INTMASK2) = 0xFFFF;
|
debug("%s\n", __func__);
|
||||||
/* Make sure all interrupts are received */
|
|
||||||
udelay(10000);
|
|
||||||
FPGA_REG(INTACK1) = 0xFFFF;
|
|
||||||
FPGA_REG(INTACK2) = 0xFFFF;
|
|
||||||
|
|
||||||
/* Enable green LED0, disable all other LEDs */
|
fdt_for_each_property_offset(offset, fdt, node) {
|
||||||
FPGA_REG(LED) = LED_LED0_GREEN_MASK;
|
u32 gpio_array[4];
|
||||||
|
int len;
|
||||||
|
const char *entry_name;
|
||||||
|
|
||||||
/* Enable SIM clocks */
|
/* if it's not the gpio we want (we need to iterate to get node index */
|
||||||
FPGA_REG(PCIE_SIM_CLOCK) = 0;
|
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;
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,26 +55,26 @@ static void write_spi_data(MV_U8 data)
|
||||||
static void put_in_prog_mode(void)
|
static void put_in_prog_mode(void)
|
||||||
{
|
{
|
||||||
/* Set CS firt to 1 */
|
/* Set CS firt to 1 */
|
||||||
mvGppValueSet(SPI_GROUP, SS_MPP , SS_MPP);
|
mvGppValueSet(SPI_GROUP, SS_MPP , SS_MPP);
|
||||||
udelay(10);
|
udelay(10);
|
||||||
|
|
||||||
/* Set SS to 0 and SCK to 1 */
|
/* Set SS to 0 and SCK to 1 */
|
||||||
mvGppValueSet(SPI_GROUP, SS_MPP | SCK_MPP, SCK_MPP);
|
mvGppValueSet(SPI_GROUP, SS_MPP | SCK_MPP, SCK_MPP);
|
||||||
/* Put FPGA in reset */
|
/* Put FPGA in reset */
|
||||||
mvGppValueSet(FPGA_RESET_GROUP, FPGA_RESET, 1);
|
mvGppValueSet(FPGA_RESET_GROUP, FPGA_RESET, 1);
|
||||||
|
|
||||||
/* Sleep at least 200ns according to lattice manual */
|
/* Sleep at least 200ns according to lattice manual */
|
||||||
udelay(1);
|
udelay(1);
|
||||||
|
|
||||||
/* Take FPGA out of reset */
|
/* Take FPGA out of reset */
|
||||||
mvGppValueSet(FPGA_RESET_GROUP, FPGA_RESET, 0);
|
mvGppValueSet(FPGA_RESET_GROUP, FPGA_RESET, 0);
|
||||||
/* Wait for at least 800 us according to lattice manual */
|
/* Wait for at least 800 us according to lattice manual */
|
||||||
udelay(800);
|
udelay(800);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pre_programming(void)
|
static void pre_programming(void)
|
||||||
{
|
{
|
||||||
debug("NBHW18 pre programming\n");
|
debug("NBHW18 pre programming\n");
|
||||||
MV_U32 reg_data;
|
MV_U32 reg_data;
|
||||||
mpp_group_4_init_value = readl(mvCtrlMppRegGet(4));
|
mpp_group_4_init_value = readl(mvCtrlMppRegGet(4));
|
||||||
mpp_group_5_init_value = readl(mvCtrlMppRegGet(5));
|
mpp_group_5_init_value = readl(mvCtrlMppRegGet(5));
|
||||||
|
|
@ -90,19 +90,19 @@ static void pre_programming(void)
|
||||||
/* Configure outputs (default input) 0=> output */
|
/* Configure outputs (default input) 0=> output */
|
||||||
mvGppTypeSet(SPI_GROUP, (SS_MPP | SCK_MPP | SDI_MPP), 0);
|
mvGppTypeSet(SPI_GROUP, (SS_MPP | SCK_MPP | SDI_MPP), 0);
|
||||||
|
|
||||||
debug("NBHW18 put in progmode\n");
|
debug("NBHW18 put in progmode\n");
|
||||||
put_in_prog_mode();
|
put_in_prog_mode();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void post_programming(void)
|
static void post_programming(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (in_fpga_cdone()) {
|
if (in_fpga_cdone()) {
|
||||||
printf("Error: Fpga does not signalize done\n");
|
printf("Error: Fpga does not signalize done\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
debug ("NBHW18 post programming\n");
|
debug ("NBHW18 post programming\n");
|
||||||
/* At least 49 SPI_SCK Cycles rising edge to rising edge */
|
/* At least 49 SPI_SCK Cycles rising edge to rising edge */
|
||||||
for (i = 0; i < 100; i++) {
|
for (i = 0; i < 100; i++) {
|
||||||
write_spi_data(0);
|
write_spi_data(0);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
#include <nbhw.h>
|
#include <common.h>
|
||||||
#include "board/mv_ebu/common/common/mvCommon.h"
|
#include <asm/gpio.h>
|
||||||
#include "board/mv_ebu/common/mv_hal/gpp/mvGpp.h"
|
|
||||||
#include "../nbhw_gpio.h"
|
|
||||||
#include "nbhw18_fpga_regs.h"
|
|
||||||
|
|
||||||
#define SET_LOGIC(enable,group,bit) \
|
#define SET_LOGIC(enable,group,bit) \
|
||||||
if (enable) \
|
if (enable) \
|
||||||
|
|
@ -10,33 +7,37 @@ if (enable) \
|
||||||
else \
|
else \
|
||||||
mvGppValueSet(group, bit, 0);
|
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)
|
void out_usb_power(int enable)
|
||||||
{
|
{
|
||||||
SET_LOGIC(enable, 1, BIT9);
|
// SET_LOGIC(enable, 1, BIT9);
|
||||||
}
|
}
|
||||||
|
|
||||||
void out_watchdog(int enable)
|
void out_watchdog(int enable)
|
||||||
{
|
{
|
||||||
SET_LOGIC(enable, 0, BIT19);
|
// SET_LOGIC(enable, 0, BIT19);
|
||||||
}
|
}
|
||||||
|
|
||||||
void out_fpga_logic_reset(int enable)
|
void out_fpga_logic_reset(int enable)
|
||||||
{
|
{
|
||||||
SET_LOGIC(enable, 1, BIT12);
|
// SET_LOGIC(enable, 1, BIT12);
|
||||||
}
|
}
|
||||||
|
|
||||||
void out_ext_reset_en(int enable)
|
void out_ext_reset_en(int enable)
|
||||||
{
|
{
|
||||||
SET_LOGIC(enable, 0, BIT7);
|
// SET_LOGIC(enable, 0, BIT7);
|
||||||
}
|
}
|
||||||
|
|
||||||
int in_reset_button(void)
|
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
|
#ifndef NBHW18_GPIO_H
|
||||||
#define 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_usb_power(int enable);
|
||||||
void out_watchdog(int enable);
|
void out_watchdog(int enable);
|
||||||
void out_fpga_logic_reset(int enable);
|
void out_fpga_logic_reset(int enable);
|
||||||
|
|
||||||
|
int in_reset_button(void);
|
||||||
int in_fpga_cdone(void);
|
int in_fpga_cdone(void);
|
||||||
|
|
||||||
#endif
|
#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+
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <common.h>
|
#include <common.h>
|
||||||
#include <dm.h>
|
#include <dm.h>
|
||||||
#include <dt-bindings/gpio/gpio.h>
|
#include <dt-bindings/gpio/gpio.h>
|
||||||
|
|
|
||||||
|
|
@ -76,18 +76,18 @@
|
||||||
"kernel_addr=" KERNEL_ADDR "\0" \
|
"kernel_addr=" KERNEL_ADDR "\0" \
|
||||||
"load_addr=" LOAD_ADDR "\0" \
|
"load_addr=" LOAD_ADDR "\0" \
|
||||||
"root_part=1\0" /* Default root partition, overwritte in board file */ \
|
"root_part=1\0" /* Default root partition, overwritte in board file */ \
|
||||||
"eth1addr=00:11:22:33:44:55\0" \
|
"eth1addr=00:11:22:33:44:55\0" \
|
||||||
"eth2addr=00:11:22:33:44:56\0" \
|
"eth2addr=00:11:22:33:44:56\0" \
|
||||||
"eth3addr=00:11:22:33:44:57\0" \
|
"ethact=ethernet@30000\0" \
|
||||||
"ethact=ethernet@34000\0" \
|
"add_sd_bootargs=setenv bootargs $bootargs root=/dev/mmcblk0p$root_part rootfstype=ext4 console=ttyS1,115200 rootwait\0" \
|
||||||
"add_sd_bootargs=setenv bootargs $bootargs root=/dev/mmcblk0p$root_part rootfstype=ext4 console=ttyS1,115200 rootwait loglevel=4\0" \
|
|
||||||
"add_version_bootargs=setenv bootargs $bootargs\0" \
|
"add_version_bootargs=setenv bootargs $bootargs\0" \
|
||||||
"fdt_skip_update=yes\0" \
|
"fdt_skip_update=yes\0" \
|
||||||
"sdbringup=echo Try bringup boot && ext4load mmc 0:$root_part $kernel_addr /boot/zImage && " \
|
"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" \
|
"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 && " \
|
"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" \
|
"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; " \
|
"if test -e mmc 0:$root_part /boot/$kernel_image; then run sdprod; " \
|
||||||
"else run sdbringup; fi; " \
|
"else run sdbringup; fi; " \
|
||||||
"run add_sd_bootargs; run add_version_bootargs; " \
|
"run add_sd_bootargs; run add_version_bootargs; " \
|
||||||
|
|
@ -106,6 +106,7 @@
|
||||||
"autoload=false\0" \
|
"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" \
|
"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" \
|
"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 */
|
"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
|
#endif
|
||||||
|
|
@ -162,4 +163,16 @@
|
||||||
*/
|
*/
|
||||||
#include "mv-common.h"
|
#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 */
|
#endif /* _CONFIG_DB_88F6820_GP_H */
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: GPL-2.0+
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
*/
|
*/
|
||||||
|
#define DEBUG
|
||||||
#include <common.h>
|
#include <common.h>
|
||||||
#include <dm.h>
|
#include <dm.h>
|
||||||
#include <environment.h>
|
#include <environment.h>
|
||||||
|
|
@ -179,22 +179,33 @@ int eth_get_dev_index(void)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define PR_DEBUG debug("%s:%d\n", __FILE__, __LINE__);
|
||||||
|
|
||||||
static int eth_write_hwaddr(struct udevice *dev)
|
static int eth_write_hwaddr(struct udevice *dev)
|
||||||
{
|
{
|
||||||
struct eth_pdata *pdata = dev->platdata;
|
struct eth_pdata *pdata = dev->platdata;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
PR_DEBUG
|
||||||
if (!dev || !device_active(dev))
|
if (!dev || !device_active(dev))
|
||||||
return -EINVAL;
|
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 */
|
/* seq is valid since the device is active */
|
||||||
if (eth_get_ops(dev)->write_hwaddr && !eth_mac_skip(dev->seq)) {
|
if (eth_get_ops(dev)->write_hwaddr && !eth_mac_skip(dev->seq)) {
|
||||||
|
PR_DEBUG
|
||||||
if (!is_valid_ethaddr(pdata->enetaddr)) {
|
if (!is_valid_ethaddr(pdata->enetaddr)) {
|
||||||
printf("\nError: %s address %pM illegal value\n",
|
printf("\nError: %s address %pM illegal value\n",
|
||||||
dev->name, pdata->enetaddr);
|
dev->name, pdata->enetaddr);
|
||||||
return -EINVAL;
|
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
|
* Drivers are allowed to decide not to implement this at
|
||||||
* run-time. E.g. Some devices may use it and some may not.
|
* 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)
|
if (ret)
|
||||||
printf("\nWarning: %s failed to set MAC address\n",
|
printf("\nWarning: %s failed to set MAC address\n",
|
||||||
dev->name);
|
dev->name);
|
||||||
|
PR_DEBUG
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -246,6 +258,7 @@ int eth_init(void)
|
||||||
struct udevice *old_current;
|
struct udevice *old_current;
|
||||||
int ret = -ENODEV;
|
int ret = -ENODEV;
|
||||||
|
|
||||||
|
PR_DEBUG
|
||||||
/*
|
/*
|
||||||
* When 'ethrotate' variable is set to 'no' and 'ethact' variable
|
* When 'ethrotate' variable is set to 'no' and 'ethact' variable
|
||||||
* is already set to an ethernet device, we should stick to 'ethact'.
|
* is already set to an ethernet device, we should stick to 'ethact'.
|
||||||
|
|
@ -258,6 +271,7 @@ int eth_init(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PR_DEBUG
|
||||||
if (!current) {
|
if (!current) {
|
||||||
current = eth_get_dev();
|
current = eth_get_dev();
|
||||||
if (!current) {
|
if (!current) {
|
||||||
|
|
@ -266,9 +280,11 @@ int eth_init(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PR_DEBUG
|
||||||
old_current = current;
|
old_current = current;
|
||||||
do {
|
do {
|
||||||
if (current) {
|
if (current) {
|
||||||
|
PR_DEBUG
|
||||||
debug("Trying %s\n", current->name);
|
debug("Trying %s\n", current->name);
|
||||||
|
|
||||||
if (device_active(current)) {
|
if (device_active(current)) {
|
||||||
|
|
@ -289,6 +305,7 @@ int eth_init(void)
|
||||||
debug("PROBE FAIL\n");
|
debug("PROBE FAIL\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PR_DEBUG
|
||||||
/*
|
/*
|
||||||
* If ethrotate is enabled, this will change "current",
|
* If ethrotate is enabled, this will change "current",
|
||||||
* otherwise we will drop out of this while loop immediately
|
* otherwise we will drop out of this while loop immediately
|
||||||
|
|
@ -296,6 +313,7 @@ int eth_init(void)
|
||||||
eth_try_another(0);
|
eth_try_another(0);
|
||||||
/* This will ensure the new "current" attempted to probe */
|
/* This will ensure the new "current" attempted to probe */
|
||||||
current = eth_get_dev();
|
current = eth_get_dev();
|
||||||
|
PR_DEBUG
|
||||||
} while (old_current != current);
|
} while (old_current != current);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -387,6 +405,8 @@ int eth_initialize(void)
|
||||||
int num_devices = 0;
|
int num_devices = 0;
|
||||||
struct udevice *dev;
|
struct udevice *dev;
|
||||||
|
|
||||||
|
debug("%s\n", __func__);
|
||||||
|
|
||||||
eth_common_init();
|
eth_common_init();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -417,7 +437,7 @@ int eth_initialize(void)
|
||||||
if (num_devices)
|
if (num_devices)
|
||||||
printf(", ");
|
printf(", ");
|
||||||
|
|
||||||
printf("eth%d: %s", dev->seq, dev->name);
|
printf("%s", dev->name);
|
||||||
|
|
||||||
if (ethprime && dev == prime_dev)
|
if (ethprime && dev == prime_dev)
|
||||||
printf(" [PRIME]");
|
printf(" [PRIME]");
|
||||||
|
|
|
||||||
|
|
@ -26,16 +26,7 @@ void eth_parse_enetaddr(const char *addr, uchar *enetaddr)
|
||||||
|
|
||||||
int eth_getenv_enetaddr(const char *name, uchar *enetaddr)
|
int eth_getenv_enetaddr(const char *name, uchar *enetaddr)
|
||||||
{
|
{
|
||||||
printf("eth: %s\n", name);
|
|
||||||
eth_parse_enetaddr(getenv(name), enetaddr);
|
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);
|
return is_valid_ethaddr(enetaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue