/*!***************************************************************************
*!
*! FILE NAME  : net_boot.c
*!
*! DESCRIPTION: Initial Network Boot Loader
*!
*! FUNCTIONS  : initial_net_boot
*! (EXTERNAL)
*!
*! FUNCTIONS  : wait_for_packet
*! (LOCAL)      send_nack
*! ---------------------------------------------------------------------------
*! HISTORY
*!
*! DATE         NAME               CHANGES
*! ----         ----               -------
*! Feb 23 1998  Pontus Soderstrom  Initial version
*! Mar 16 1998  Mikael Starvik     Changed tag and sequence number to udword
*!                                 Changed order of source and destination
*!                                 addresses in packet_header and nack_packet
*!                                 Changed order of the arguments to NODE_COPY
*!                                 Added nack_addr
*!                                 Changed excessive colission handling
*! Mar 25 1998  Mikael Starvik     Modifications after review. Introduced
*!                                 HILO and LHILO. Rewrote send_nack.
*!                                 Changed jump address to 0x40000002
*! ---------------------------------------------------------------------------
*! (C) Copyright 1998, Axis Communications AB, LUND, SWEDEN
*!***************************************************************************/
/* %Z% %M% %I% %G% */

#if 0
#define LOADADDRESS 0x88000500
#define BOOTADDRESS "0x88000500"
#else
#define LOADADDRESS 0x40000500
#define BOOTADDRESS "0x40000500"
#endif

/********************** INCLUDE FILES SECTION *******************************/

#include "compiler.h"
#include <asm/sv_addr_ag.h>
#include "globs.h"
#include "dmadescr.h"
#include "net_boot.h"
#include "svinto_boot.h"

/********************** CONSTANT AND MACRO SECTION **************************/

#define CRC_LEN 4
#define INITIAL_BOOT_SIZE 1484
#define MIN_ETHERNET_PACKET_SIZE 0x40
#define SNAP_LEN 16

/********************** TYPE DEFINITION SECTION *****************************/

typedef struct packet_header
{  
  node_addr dest;
  node_addr source;
  uword     length;
  udword    snap1;
  udword    snap2;
  udword    tag;
  udword    seq_number; 
} packet_header;

typedef struct nack_packet 
{
  node_addr dest;
  node_addr source;
  uword     length;
  udword    snap1;
  udword    snap2;
  udword    tag;
  udword    seq_number; 
} nack_packet;

/********************** LOCAL FUNCTION DECLARATION SECTION ******************/

void initial_net_boot(void);
void wait_for_packet(void);
void send_nack(void);

/********************** GLOBAL VARIABLE DECLARATION SECTION *****************/
extern udword boot_file_size;
udword first_image_seq_nbr;  /* Will be used by loader */

/********************** LOCAL VARIABLE DECLARATION SECTION ******************/

udword wanted_packet_nbr = 1;
packet_header  packet;
nack_packet    packet_nack;
dma_descr      desc_header;
dma_descr      desc_data;
dma_descr      desc_nack;
node_addr      target_addr = TARGET_ADDRESS;
node_addr      nack_addr   = NACK_ADDRESS;
node_addr      host_addr;     /* Address of the loading host */

/********************** FUNCTION DEFINITION SECTION *************************/



/*#***************************************************************************
*# FUNCTION NAME: initial_net_boot
*#
*# PARAMETERS   : None
*#
*# RETURNS      : Nothing
*#
*# SIDE EFFECTS : 
*#
*# DESCRIPTION  : Receives the boot loader from network
*#
*#----------------------------------------------------------------------------
*# HISTORY
*# 
*# DATE         NAME               CHANGES
*# ----         ----               -------
*# Mar 13 1998  Pontus Soderstrom  Initial version
*#              Mikael Starvik 
*# Mar 25 1998  Mikael Starvik     Updates after review
*#***************************************************************************/
void initial_net_boot(void)
{
  udword         loaded_size = 0;
  udword         mem_start;
  volatile udword *temp;
  mem_start = MEM_NON_CACHEABLE | LOADADDRESS;
  /* Want to find the address of the host which was given */
  NODE_COPY(host_addr,(*(node_addr*)0x380000DC));

  *R_NETWORK_GA_1 = *R_NETWORK_GA_0 = 0;
  
  SET_ETHER_ADDR(0x01,0x40,0x8c,0x00,0x01,0x00,0x01,0x40,0x8c,0x00,0x01,0x00);

  *R_NETWORK_REC_CONFIG =  
    IO_STATE (R_NETWORK_REC_CONFIG, duplex,     half)    |
    IO_STATE (R_NETWORK_REC_CONFIG, bad_crc,    discard) |
    IO_STATE (R_NETWORK_REC_CONFIG, oversize,   discard) |
    IO_STATE (R_NETWORK_REC_CONFIG, undersize,  discard) |
    IO_STATE (R_NETWORK_REC_CONFIG, all_roots,  discard) |
    IO_STATE (R_NETWORK_REC_CONFIG, broadcast,  discard) |
    IO_STATE (R_NETWORK_REC_CONFIG, individual, discard) |
    IO_STATE (R_NETWORK_REC_CONFIG, ma1,        disable) |
    IO_STATE (R_NETWORK_REC_CONFIG, ma0,        enable);

  *R_NETWORK_MGM_CTRL =  
    IO_FIELD(R_NETWORK_MGM_CTRL, txd_pins, 0)       |
    IO_FIELD(R_NETWORK_MGM_CTRL, txer_pin, 0)       |
    IO_FIELD(R_NETWORK_MGM_CTRL, mdck,     0)       |
    IO_STATE(R_NETWORK_MGM_CTRL, mdoe,     disable) |
    IO_FIELD(R_NETWORK_MGM_CTRL, mdio,     0);
  
  *R_NETWORK_TR_CTRL = 
    (IO_STATE (R_NETWORK_TR_CTRL, clr_error, clr)     |
     IO_STATE (R_NETWORK_TR_CTRL, delay,     none)    |
     IO_STATE (R_NETWORK_TR_CTRL, cancel,    dont)    |
     IO_STATE (R_NETWORK_TR_CTRL, cd,        enable)  |
     IO_STATE (R_NETWORK_TR_CTRL, pad,       enable) |
     IO_STATE (R_NETWORK_TR_CTRL, crc,       enable)  |
     IO_STATE (R_NETWORK_TR_CTRL, retry,     enable));
     
  *R_NETWORK_GEN_CONFIG =  
    IO_STATE (R_NETWORK_GEN_CONFIG, loopback, off)   |
    IO_STATE (R_NETWORK_GEN_CONFIG, frame,    ether) |
    IO_STATE (R_NETWORK_GEN_CONFIG, vg,       off)   |
    IO_STATE (R_NETWORK_GEN_CONFIG, phy,      mii_clk)   |
    IO_STATE (R_NETWORK_GEN_CONFIG, enable,   on);

  *R_DMA_CH0_CMD = IO_STATE(R_DMA_CH0_CMD, cmd, reset);
  *R_DMA_CH1_CMD = IO_STATE(R_DMA_CH1_CMD, cmd, reset);
  
  desc_header.sw_len = sizeof(packet);  
  desc_header.flags  = 0;
  desc_header.next   = &desc_data;
  desc_header.buf    = &packet;
  desc_header.status = 0;

  desc_data.sw_len   = 1500;
  desc_data.flags    = d_eol;
  desc_data.next     = 0;
  desc_data.buf      = (byte*) mem_start;
  desc_data.status   = 0;

  desc_nack.sw_len   = MIN_ETHERNET_PACKET_SIZE;
  desc_nack.flags    = d_eop | d_eol | d_wait;
  desc_nack.next     = 0;
  desc_nack.buf      = &packet_nack;
  desc_nack.status   = 0;

  while (1)
  {
    wait_for_packet();
    if (packet.seq_number == wanted_packet_nbr)
    {
      if (packet.tag == LHILO(NORMAL_PACKET_TAG))
      {
        loaded_size += desc_data.hw_len - CRC_LEN;
        if (loaded_size >= boot_file_size)
        {
          /* We have loaded as much as we wanted. */
          break;
        }
        (byte *)desc_data.buf += desc_data.hw_len-CRC_LEN;
        wanted_packet_nbr++;
        desc_data.status=0;
        desc_header.status=0;
      }
      else if (packet.tag == LHILO(LAST_PACKET_TAG))
      {
        break;  
      }
    }
    else if(LHILO(packet.seq_number) > wanted_packet_nbr)
    {
      send_nack();
      desc_data.status=0;
    }
    /* else ignore packet and wait for next. */
  }

  *R_PORT_PA_DATA = -1;

  asm volatile ("jump " BOOTADDRESS);  
}


/*#***************************************************************************
*# FUNCTION NAME: wait_for_packet
*#
*# PARAMETERS   : None
*#
*# RETURNS      : nothing
*#
*# SIDE EFFECTS : 
*#
*# DESCRIPTION  : Waits for an ethernet packet
*#
*#----------------------------------------------------------------------------
*# HISTORY
*# 
*# DATE         NAME               CHANGES
*# ----         ----               -------
*# Mar 13 1998  Pontus Soderstrom  Initial version.
*#              Mikael Starvik
*# Mar 25 1998  Mikael Starvik     Updates after review
*# 
*#***************************************************************************/       
void wait_for_packet(void)
{
  udword timer = TIMER_VAL;
  *R_DMA_CH1_FIRST = (udword)&desc_header;
  *R_DMA_CH1_CMD=IO_STATE(R_DMA_CH1_CMD,cmd,start);
 
  while (1)
  {
    if (desc_data.status & d_eop) /* Use polling */
    {      
      if (NODE_COMP(host_addr, packet.source) && 
          NODE_COMP(target_addr, packet.dest) &&
          packet.snap1 == LHILO(SNAP1) && 
          packet.snap2 == LHILO(SNAP2) )
      {
        return;
      }
      desc_data.status = 0;       /* Clear eop status */
      START_DMA(1, &desc_header); /* Start the DMA again. */
    }
    if (!timer--)
    {
       send_nack();
       timer = TIMER_VAL;
    }
  }
}


/*#***************************************************************************
*# FUNCTION NAME: send_nack
*#
*# PARAMETERS   : None
*#
*# RETURNS      : Nothing
*#
*# SIDE EFFECTS : 
*#
*# DESCRIPTION  : Sends a nack to the flash host program
*#
*#----------------------------------------------------------------------------
*# HISTORY
*# 
*# DATE         NAME               CHANGES
*# ----         ----               -------
*# Mar 13 1998  Pontus Soderstrom  Initial version.
*#              Mikael Starvik     
*# Mar 25 1998  Mikael Starvik     Updates after review
*#
*#***************************************************************************/
void send_nack(void)
{
  NODE_COPY(packet_nack.dest, host_addr);
  NODE_COPY(packet_nack.source, nack_addr);
  packet_nack.length            = HILO(SNAP_LEN);
  packet_nack.snap1             = LHILO(SNAP1);
  packet_nack.snap2             = LHILO(SNAP2);
  packet_nack.tag               = LHILO(NACK_PACKET_TAG);
  packet_nack.seq_number        = (wanted_packet_nbr);
  START_DMA(0, &desc_nack);
  while (*R_DMA_CH0_FIRST!=0)
  {
    if (((*R_NETWORK_STAT & IO_MASK(R_NETWORK_STAT, exc_col)) ==
         IO_STATE(R_NETWORK_STAT, exc_col, yes)) ||
        ((*R_NETWORK_STAT & IO_MASK(R_NETWORK_STAT, underrun)) ==
         IO_STATE(R_NETWORK_STAT, underrun, yes)))
    {
      /* Clear excessive colission or underrun */
      *R_NETWORK_TR_CTRL=(*R_NETWORK_TR_CTRL)&~
         IO_MASK(R_NETWORK_TR_CTRL,clr_error);
      START_DMA(0, &desc_nack); /* Restart DMA */ 
    }
  }
}

/********************** END OF FILE net_boot.c ******************************/




