blob: 658756d9842dcca1cbc64ae694efe258ceaf936e [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2014
* Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc
*/
#include <common.h>
#include <command.h>
#include <console.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <gdsys_fpga.h>
#ifndef CONFIG_GDSYS_LEGACY_DRIVERS
#include <dm.h>
#include <misc.h>
#include <regmap.h>
#include <sysinfo.h>
#include "../../../drivers/misc/gdsys_soc.h"
#include "../../../drivers/misc/gdsys_ioep.h"
#include "../../../drivers/misc/ihs_fpga.h"
const int HEADER_WORDS = sizeof(struct io_generic_packet) / 2;
#endif /* !CONFIG_GDSYS_LEGACY_DRIVERS */
enum status_print_type {
STATUS_LOUD = 0,
STATUS_SILENT = 1,
};
#ifdef CONFIG_GDSYS_LEGACY_DRIVERS
enum {
STATE_TX_PACKET_BUILDING = BIT(0),
STATE_TX_TRANSMITTING = BIT(1),
STATE_TX_BUFFER_FULL = BIT(2),
STATE_TX_ERR = BIT(3),
STATE_RECEIVE_TIMEOUT = BIT(4),
STATE_PROC_RX_STORE_TIMEOUT = BIT(5),
STATE_PROC_RX_RECEIVE_TIMEOUT = BIT(6),
STATE_RX_DIST_ERR = BIT(7),
STATE_RX_LENGTH_ERR = BIT(8),
STATE_RX_FRAME_CTR_ERR = BIT(9),
STATE_RX_FCS_ERR = BIT(10),
STATE_RX_PACKET_DROPPED = BIT(11),
STATE_RX_DATA_LAST = BIT(12),
STATE_RX_DATA_FIRST = BIT(13),
STATE_RX_DATA_AVAILABLE = BIT(15),
};
enum {
IRQ_CPU_TRANSMITBUFFER_FREE_STATUS = BIT(5),
IRQ_CPU_PACKET_TRANSMITTED_EVENT = BIT(6),
IRQ_NEW_CPU_PACKET_RECEIVED_EVENT = BIT(7),
IRQ_CPU_RECEIVE_DATA_AVAILABLE_STATUS = BIT(8),
};
enum {
CTRL_PROC_RECEIVE_ENABLE = BIT(12),
CTRL_FLUSH_TRANSMIT_BUFFER = BIT(15),
};
struct io_generic_packet {
u16 target_address;
u16 source_address;
u8 packet_type;
u8 bc;
u16 packet_length;
} __attribute__((__packed__));
#endif /* CONFIG_GDSYS_LEGACY_DRIVERS */
unsigned long long rx_ctr;
unsigned long long tx_ctr;
unsigned long long err_ctr;
#ifndef CONFIG_GDSYS_LEGACY_DRIVERS
struct udevice *dev;
#endif /* !CONFIG_GDSYS_LEGACY_DRIVERS */
#ifdef CONFIG_GDSYS_LEGACY_DRIVERS
static void io_check_status(uint fpga, u16 status, enum status_print_type type)
{
u16 mask = STATE_RX_DIST_ERR | STATE_RX_LENGTH_ERR |
STATE_RX_FRAME_CTR_ERR | STATE_RX_FCS_ERR |
STATE_RX_PACKET_DROPPED | STATE_TX_ERR;
if (!(status & mask)) {
FPGA_SET_REG(fpga, ep.rx_tx_status, status);
return;
}
err_ctr++;
FPGA_SET_REG(fpga, ep.rx_tx_status, status);
if (type == STATUS_SILENT)
return;
if (status & STATE_RX_PACKET_DROPPED)
printf("RX_PACKET_DROPPED, status %04x\n", status);
if (status & STATE_RX_DIST_ERR)
printf("RX_DIST_ERR\n");
if (status & STATE_RX_LENGTH_ERR)
printf("RX_LENGTH_ERR\n");
if (status & STATE_RX_FRAME_CTR_ERR)
printf("RX_FRAME_CTR_ERR\n");
if (status & STATE_RX_FCS_ERR)
printf("RX_FCS_ERR\n");
if (status & STATE_TX_ERR)
printf("TX_ERR\n");
}
#else
static void io_check_status(struct udevice *dev, enum status_print_type type)
{
u16 status = 0;
int ret;
ret = misc_call(dev, 0, NULL, 0, &status, 0);
if (!ret)
return;
err_ctr++;
if (type != STATUS_LOUD)
return;
if (status & STATE_RX_PACKET_DROPPED)
printf("RX_PACKET_DROPPED, status %04x\n", status);
if (status & STATE_RX_DIST_ERR)
printf("RX_DIST_ERR\n");
if (status & STATE_RX_LENGTH_ERR)
printf("RX_LENGTH_ERR\n");
if (status & STATE_RX_FRAME_CTR_ERR)
printf("RX_FRAME_CTR_ERR\n");
if (status & STATE_RX_FCS_ERR)
printf("RX_FCS_ERR\n");
if (status & STATE_TX_ERR)
printf("TX_ERR\n");
}
#endif /* CONFIG_GDSYS_LEGACY_DRIVERS */
#ifdef CONFIG_GDSYS_LEGACY_DRIVERS
static void io_send(uint fpga, uint size)
{
uint k;
struct io_generic_packet packet = {
.source_address = 1,
.packet_type = 1,
.packet_length = size,
};
u16 *p = (u16 *)&packet;
for (k = 0; k < sizeof(packet) / 2; ++k)
FPGA_SET_REG(fpga, ep.transmit_data, *p++);
for (k = 0; k < (size + 1) / 2; ++k)
FPGA_SET_REG(fpga, ep.transmit_data, k);
FPGA_SET_REG(fpga, ep.rx_tx_control,
CTRL_PROC_RECEIVE_ENABLE | CTRL_FLUSH_TRANSMIT_BUFFER);
tx_ctr++;
}
#else
static void io_send(struct udevice *dev, uint size)
{
uint k;
u16 buffer[HEADER_WORDS + 128];
struct io_generic_packet header = {
.source_address = 1,
.packet_type = 1,
.packet_length = size,
};
const uint words = (size + 1) / 2;
memcpy(buffer, &header, 2 * HEADER_WORDS);
for (k = 0; k < words; ++k)
buffer[k + HEADER_WORDS] = (2 * k + 1) + ((2 * k) << 8);
misc_write(dev, 0, buffer, HEADER_WORDS + words);
tx_ctr++;
}
#endif /* CONFIG_GDSYS_LEGACY_DRIVERS */
#ifdef CONFIG_GDSYS_LEGACY_DRIVERS
static void io_receive(uint fpga)
{
u16 rx_tx_status;
FPGA_GET_REG(fpga, ep.rx_tx_status, &rx_tx_status);
while (rx_tx_status & STATE_RX_DATA_AVAILABLE) {
u16 rx;
if (rx_tx_status & STATE_RX_DATA_LAST)
rx_ctr++;
FPGA_GET_REG(fpga, ep.receive_data, &rx);
FPGA_GET_REG(fpga, ep.rx_tx_status, &rx_tx_status);
}
}
#else
static void io_receive(struct udevice *dev)
{
u16 buffer[HEADER_WORDS + 128];
if (!misc_read(dev, 0, buffer, 0))
rx_ctr++;
}
#endif /* CONFIG_GDSYS_LEGACY_DRIVERS */
#ifdef CONFIG_GDSYS_LEGACY_DRIVERS
static void io_reflect(uint fpga)
{
u16 buffer[128];
uint k = 0;
uint n;
u16 rx_tx_status;
FPGA_GET_REG(fpga, ep.rx_tx_status, &rx_tx_status);
while (rx_tx_status & STATE_RX_DATA_AVAILABLE) {
FPGA_GET_REG(fpga, ep.receive_data, &buffer[k++]);
if (rx_tx_status & STATE_RX_DATA_LAST)
break;
FPGA_GET_REG(fpga, ep.rx_tx_status, &rx_tx_status);
}
if (!k)
return;
for (n = 0; n < k; ++n)
FPGA_SET_REG(fpga, ep.transmit_data, buffer[n]);
FPGA_SET_REG(fpga, ep.rx_tx_control,
CTRL_PROC_RECEIVE_ENABLE | CTRL_FLUSH_TRANSMIT_BUFFER);
tx_ctr++;
}
#else
static void io_reflect(struct udevice *dev)
{
u16 buffer[HEADER_WORDS + 128];
struct io_generic_packet *header;
if (misc_read(dev, 0, buffer, 0))
return;
header = (struct io_generic_packet *)&buffer;
misc_write(dev, 0, buffer, HEADER_WORDS + header->packet_length);
}
#endif /* CONFIG_GDSYS_LEGACY_DRIVERS */
#ifdef CONFIG_GDSYS_LEGACY_DRIVERS
/*
* FPGA io-endpoint reflector
*
* Syntax:
* ioreflect {fpga} {reportrate}
*/
int do_ioreflect(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
uint fpga;
uint rate = 0;
unsigned long long last_seen = 0;
if (argc < 2)
return CMD_RET_USAGE;
fpga = simple_strtoul(argv[1], NULL, 10);
/*
* If another parameter, it is the report rate in packets.
*/
if (argc > 2)
rate = simple_strtoul(argv[2], NULL, 10);
/* Enable receive path */
FPGA_SET_REG(fpga, ep.rx_tx_control, CTRL_PROC_RECEIVE_ENABLE);
/* Set device address to dummy 1*/
FPGA_SET_REG(fpga, ep.device_address, 1);
rx_ctr = 0; tx_ctr = 0; err_ctr = 0;
while (1) {
u16 top_int;
u16 rx_tx_status;
FPGA_GET_REG(fpga, top_interrupt, &top_int);
FPGA_GET_REG(fpga, ep.rx_tx_status, &rx_tx_status);
io_check_status(fpga, rx_tx_status, STATUS_SILENT);
if ((top_int & IRQ_CPU_RECEIVE_DATA_AVAILABLE_STATUS) &&
(top_int & IRQ_CPU_TRANSMITBUFFER_FREE_STATUS))
io_reflect(fpga);
if (rate) {
if (!(tx_ctr % rate) && (tx_ctr != last_seen))
printf("refl %llu, err %llu\n", tx_ctr,
err_ctr);
last_seen = tx_ctr;
}
if (ctrlc())
break;
}
return 0;
}
#else
/*
* FPGA io-endpoint reflector
*
* Syntax:
* ioreflect {reportrate}
*/
int do_ioreflect(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
struct udevice *fpga;
struct regmap *map;
uint rate = 0;
unsigned long long last_seen = 0;
if (!dev) {
printf("No device selected\n");
return 1;
}
gdsys_soc_get_fpga(dev, &fpga);
regmap_init_mem(dev_ofnode(dev), &map);
/* Enable receive path */
misc_set_enabled(dev, true);
rx_ctr = 0; tx_ctr = 0; err_ctr = 0;
while (1) {
uint top_int;
ihs_fpga_get(map, top_interrupt, &top_int);
io_check_status(dev, STATUS_SILENT);
if ((top_int & IRQ_CPU_RECEIVE_DATA_AVAILABLE_STATUS) &&
(top_int & IRQ_CPU_TRANSMITBUFFER_FREE_STATUS))
io_reflect(dev);
if (rate) {
if (!(tx_ctr % rate) && (tx_ctr != last_seen))
printf("refl %llu, err %llu\n", tx_ctr,
err_ctr);
last_seen = tx_ctr;
}
if (ctrlc())
break;
}
return 0;
}
#endif /* CONFIG_GDSYS_LEGACY_DRIVERS */
#define DISP_LINE_LEN 16
#ifdef CONFIG_GDSYS_LEGACY_DRIVERS
/*
* FPGA io-endpoint looptest
*
* Syntax:
* ioloop {fpga} {size} {rate}
*/
int do_ioloop(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
uint fpga;
uint size;
uint rate = 0;
if (argc < 3)
return CMD_RET_USAGE;
/*
* FPGA is specified since argc > 2
*/
fpga = simple_strtoul(argv[1], NULL, 10);
/*
* packet size is specified since argc > 2
*/
size = simple_strtoul(argv[2], NULL, 10);
/*
* If another parameter, it is the test rate in packets per second.
*/
if (argc > 3)
rate = simple_strtoul(argv[3], NULL, 10);
/* enable receive path */
FPGA_SET_REG(fpga, ep.rx_tx_control, CTRL_PROC_RECEIVE_ENABLE);
/* set device address to dummy 1*/
FPGA_SET_REG(fpga, ep.device_address, 1);
rx_ctr = 0; tx_ctr = 0; err_ctr = 0;
while (1) {
u16 top_int;
u16 rx_tx_status;
FPGA_GET_REG(fpga, top_interrupt, &top_int);
FPGA_GET_REG(fpga, ep.rx_tx_status, &rx_tx_status);
io_check_status(fpga, rx_tx_status, STATUS_LOUD);
if (top_int & IRQ_CPU_TRANSMITBUFFER_FREE_STATUS)
io_send(fpga, size);
if (top_int & IRQ_CPU_RECEIVE_DATA_AVAILABLE_STATUS)
io_receive(fpga);
if (rate) {
if (ctrlc())
break;
udelay(1000000 / rate);
if (!(tx_ctr % rate))
printf("d %llu, tx %llu, rx %llu, err %llu\n",
tx_ctr - rx_ctr, tx_ctr, rx_ctr,
err_ctr);
}
}
return 0;
}
#else
/*
* FPGA io-endpoint looptest
*
* Syntax:
* ioloop {size} {rate}
*/
int do_ioloop(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
uint size;
uint rate = 0;
struct udevice *fpga;
struct regmap *map;
if (!dev) {
printf("No device selected\n");
return 1;
}
gdsys_soc_get_fpga(dev, &fpga);
regmap_init_mem(dev_ofnode(dev), &map);
if (argc < 2)
return CMD_RET_USAGE;
/*
* packet size is specified since argc > 1
*/
size = simple_strtoul(argv[2], NULL, 10);
/*
* If another parameter, it is the test rate in packets per second.
*/
if (argc > 2)
rate = simple_strtoul(argv[3], NULL, 10);
/* Enable receive path */
misc_set_enabled(dev, true);
rx_ctr = 0; tx_ctr = 0; err_ctr = 0;
while (1) {
uint top_int;
if (ctrlc())
break;
ihs_fpga_get(map, top_interrupt, &top_int);
io_check_status(dev, STATUS_LOUD);
if (top_int & IRQ_CPU_TRANSMITBUFFER_FREE_STATUS)
io_send(dev, size);
if (top_int & IRQ_CPU_RECEIVE_DATA_AVAILABLE_STATUS)
io_receive(dev);
if (rate) {
udelay(1000000 / rate);
if (!(tx_ctr % rate))
printf("d %llu, tx %llu, rx %llu, err %llu\n",
tx_ctr - rx_ctr, tx_ctr, rx_ctr,
err_ctr);
}
}
return 0;
}
#endif /* CONFIG_GDSYS_LEGACY_DRIVERS */
#ifndef CONFIG_GDSYS_LEGACY_DRIVERS
int do_iodev(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
struct udevice *ioep = NULL;
struct udevice *sysinfo;
char name[8];
int ret;
if (sysinfo_get(&sysinfo))
return CMD_RET_FAILURE;
if (argc > 1) {
int i = simple_strtoul(argv[1], NULL, 10);
snprintf(name, sizeof(name), "ioep%d", i);
ret = uclass_get_device_by_phandle(UCLASS_MISC, sysinfo, name,
&ioep);
if (ret || !ioep) {
printf("Invalid IOEP %d\n", i);
return CMD_RET_FAILURE;
}
dev = ioep;
} else {
int i = 0;
while (1) {
snprintf(name, sizeof(name), "ioep%d", i);
ret = uclass_get_device_by_phandle(UCLASS_MISC, sysinfo,
name, &ioep);
if (ret || !ioep)
break;
printf("IOEP %d:\t%s\n", i++, ioep->name);
}
if (dev)
printf("\nSelected IOEP: %s\n", dev->name);
else
puts("\nNo IOEP selected.\n");
}
return 0;
}
#endif /* !CONFIG_GDSYS_LEGACY_DRIVERS */
#ifdef CONFIG_GDSYS_LEGACY_DRIVERS
U_BOOT_CMD(
ioloop, 4, 0, do_ioloop,
"fpga io-endpoint looptest",
"fpga packetsize [packets/sec]"
);
U_BOOT_CMD(
ioreflect, 3, 0, do_ioreflect,
"fpga io-endpoint reflector",
"fpga reportrate"
);
#else
U_BOOT_CMD(
ioloop, 3, 0, do_ioloop,
"fpga io-endpoint looptest",
"packetsize [packets/sec]"
);
U_BOOT_CMD(
ioreflect, 2, 0, do_ioreflect,
"fpga io-endpoint reflector",
"reportrate"
);
U_BOOT_CMD(
iodev, 2, 0, do_iodev,
"fpga io-endpoint listing/selection",
"[ioep device to select]"
);
#endif /* CONFIG_GDSYS_LEGACY_DRIVERS */