/* 
 | 
 * (C) Copyright 2014 
 | 
 * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc 
 | 
 * 
 | 
 * SPDX-License-Identifier:    GPL-2.0+ 
 | 
 */ 
 | 
  
 | 
#include <common.h> 
 | 
#include <command.h> 
 | 
#include <console.h> 
 | 
  
 | 
#include <gdsys_fpga.h> 
 | 
  
 | 
enum { 
 | 
    STATE_TX_PACKET_BUILDING = 1<<0, 
 | 
    STATE_TX_TRANSMITTING = 1<<1, 
 | 
    STATE_TX_BUFFER_FULL = 1<<2, 
 | 
    STATE_TX_ERR = 1<<3, 
 | 
    STATE_RECEIVE_TIMEOUT = 1<<4, 
 | 
    STATE_PROC_RX_STORE_TIMEOUT = 1<<5, 
 | 
    STATE_PROC_RX_RECEIVE_TIMEOUT = 1<<6, 
 | 
    STATE_RX_DIST_ERR = 1<<7, 
 | 
    STATE_RX_LENGTH_ERR = 1<<8, 
 | 
    STATE_RX_FRAME_CTR_ERR = 1<<9, 
 | 
    STATE_RX_FCS_ERR = 1<<10, 
 | 
    STATE_RX_PACKET_DROPPED = 1<<11, 
 | 
    STATE_RX_DATA_LAST = 1<<12, 
 | 
    STATE_RX_DATA_FIRST = 1<<13, 
 | 
    STATE_RX_DATA_AVAILABLE = 1<<15, 
 | 
}; 
 | 
  
 | 
enum { 
 | 
    CTRL_PROC_RECEIVE_ENABLE = 1<<12, 
 | 
    CTRL_FLUSH_TRANSMIT_BUFFER = 1<<15, 
 | 
}; 
 | 
  
 | 
enum { 
 | 
    IRQ_CPU_TRANSMITBUFFER_FREE_STATUS = 1<<5, 
 | 
    IRQ_CPU_PACKET_TRANSMITTED_EVENT = 1<<6, 
 | 
    IRQ_NEW_CPU_PACKET_RECEIVED_EVENT = 1<<7, 
 | 
    IRQ_CPU_RECEIVE_DATA_AVAILABLE_STATUS = 1<<8, 
 | 
}; 
 | 
  
 | 
struct io_generic_packet { 
 | 
    u16 target_address; 
 | 
    u16 source_address; 
 | 
    u8 packet_type; 
 | 
    u8 bc; 
 | 
    u16 packet_length; 
 | 
} __attribute__((__packed__)); 
 | 
  
 | 
unsigned long long rx_ctr; 
 | 
unsigned long long tx_ctr; 
 | 
unsigned long long err_ctr; 
 | 
  
 | 
static void io_check_status(unsigned int fpga, u16 status, bool silent) 
 | 
{ 
 | 
    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 (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"); 
 | 
} 
 | 
  
 | 
static void io_send(unsigned int fpga, unsigned int size) 
 | 
{ 
 | 
    unsigned int 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++; 
 | 
} 
 | 
  
 | 
static void io_receive(unsigned int fpga) 
 | 
{ 
 | 
    unsigned int k = 0; 
 | 
    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); 
 | 
  
 | 
        ++k; 
 | 
    } 
 | 
} 
 | 
  
 | 
static void io_reflect(unsigned int fpga) 
 | 
{ 
 | 
    u16 buffer[128]; 
 | 
  
 | 
    unsigned int k = 0; 
 | 
    unsigned int 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++; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * FPGA io-endpoint reflector 
 | 
 * 
 | 
 * Syntax: 
 | 
 *    ioreflect {fpga} {reportrate} 
 | 
 */ 
 | 
int do_ioreflect(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 
 | 
{ 
 | 
    unsigned int fpga; 
 | 
    unsigned int 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, true); 
 | 
        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; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * FPGA io-endpoint looptest 
 | 
 * 
 | 
 * Syntax: 
 | 
 *    ioloop {fpga} {size} {rate} 
 | 
 */ 
 | 
#define DISP_LINE_LEN    16 
 | 
int do_ioloop(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 
 | 
{ 
 | 
    unsigned int fpga; 
 | 
    unsigned int size; 
 | 
    unsigned int 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, false); 
 | 
        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 %lld, tx %llu, rx %llu, err %llu\n", 
 | 
                       tx_ctr - rx_ctr, tx_ctr, rx_ctr, 
 | 
                       err_ctr); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
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" 
 | 
); 
 |