Skip to content
Snippets Groups Projects
minic.c 9.17 KiB
Newer Older
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
#include <stdio.h>
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
#include <string.h>
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed

#include "types.h"
#include "board.h"
#include "pps_gen.h" /* for pps_gen_get_time() */
#include "minic.h"

#include <hw/minic_regs.h>

#define MINIC_DMA_TX_BUF_SIZE 1024
#define MINIC_DMA_RX_BUF_SIZE 2048

#define MINIC_MTU 256
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed

#define F_COUNTER_BITS 4
#define F_COUNTER_MASK ((1<<F_COUNTER_BITS)-1)

#define RX_DESC_VALID(d) ((d) & (1<<31) ? 1 : 0)
#define RX_DESC_ERROR(d) ((d) & (1<<30) ? 1 : 0)
#define RX_DESC_HAS_OOB(d)  ((d) & (1<<29) ? 1 : 0)
#define RX_DESC_SIZE(d)  (((d) & (1<<0) ? -1 : 0) + (d & 0xffe))
#define RXOOB_TS_INCORRECT (1<<11)

Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
#define TX_DESC_VALID (1<<31)
#define TX_DESC_WITH_OOB (1<<30)
#define TX_DESC_HAS_OWN_MAC (1<<28)

#define RX_OOB_SIZE 6

#define ETH_HEADER_SIZE 14

// extracts the values of TS rising and falling edge counters from the descriptor header

#define EXPLODE_WR_TIMESTAMP(raw, rc, fc) \
  rc = (raw) & 0xfffffff;		  \
  fc = (raw >> 28) & 0xf;


Grzegorz Daniluk's avatar
Grzegorz Daniluk committed
static volatile uint32_t dma_tx_buf[MINIC_DMA_TX_BUF_SIZE / 4];
static volatile uint32_t dma_rx_buf[MINIC_DMA_RX_BUF_SIZE / 4];
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed

struct wr_minic {
  volatile uint32_t *rx_head, *rx_base; 
  uint32_t rx_avail, rx_size;
  volatile uint32_t *tx_head, *tx_base;
  uint32_t tx_avail, tx_size;
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
};

static struct wr_minic minic;

static inline void minic_writel(uint32_t reg,uint32_t data)
{
  *(volatile uint32_t *) (BASE_MINIC + reg) = data;
}

static inline uint32_t minic_readl(uint32_t reg)
{
  return *(volatile uint32_t *)(BASE_MINIC + reg);
}

static uint8_t * minic_rx_memcpy( uint8_t *dst, uint8_t *src, uint32_t size)
  uint32_t part;
  //if src is outside the circular buffer, bring it back to the beginning
  src = (uint8_t *) ((uint32_t)minic.rx_base + ((uint32_t)src - (uint32_t)minic.rx_base) % (minic.rx_size<<2));
  if((uint32_t)src + size <= (uint32_t)minic.rx_base + (minic.rx_size<<2))
  part = (uint32_t)minic.rx_base + (minic.rx_size<<2) - (uint32_t)src;
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
  memcpy((void*) (dst+part), (void*)minic.rx_base, size - part);
static uint8_t *minic_rx_memset( uint8_t *mem, uint8_t c, uint32_t size)
{
  uint32_t part;
  //if src is outside the circular buffer, bring it back to the beginning
  src = (uint32_t*)((uint32_t)minic.rx_base + ((uint32_t)mem - (uint32_t)minic.rx_base) % (minic.rx_size<<2));
  if((uint32_t)src + size <= (uint32_t)minic.rx_base + (minic.rx_size<<2))
    return memset((void*)src, c, size);
  part = (uint32_t)minic.rx_base + (minic.rx_size<<2) - (uint32_t)src;
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
  memset((void*)minic.rx_base, c, size - part);
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
  minic_writel(MINIC_REG_MCR, 0);
	minic.rx_base = dma_rx_buf;
	minic.rx_size = MINIC_DMA_RX_BUF_SIZE/4;
  minic_rx_memset((uint8_t*)minic.rx_base, 0x00, minic.rx_size<<2);
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
  minic_writel(MINIC_REG_RX_ADDR, (uint32_t) minic.rx_base);
  minic_writel(MINIC_REG_RX_SIZE, minic.rx_size);
  //new buffer allocated, clear any old RX interrupts
  minic_writel(MINIC_REG_EIC_ISR, MINIC_EIC_ISR_RX);
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
  minic_writel(MINIC_REG_MCR, MINIC_MCR_RX_EN);
}

static void minic_rxbuf_free(uint32_t words)
{
  minic_rx_memset((uint8_t*)minic.rx_head, 0x00, words<<2);
  minic_writel(MINIC_REG_RX_AVAIL, words);
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
}

static void minic_new_tx_buffer()
{
	minic.tx_base = dma_tx_buf;
	minic.tx_size = MINIC_DMA_TX_BUF_SIZE>>2;

Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
  minic.tx_head = minic.tx_base;
  minic.tx_avail = minic.tx_size;
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
	
  minic_writel(MINIC_REG_TX_ADDR, (uint32_t) minic.tx_base);
}

void minic_init()
{
Grzegorz Daniluk's avatar
Grzegorz Daniluk committed
    minic_writel(MINIC_REG_EIC_IDR, MINIC_EIC_IDR_RX);
    minic_writel(MINIC_REG_EIC_ISR, MINIC_EIC_ISR_RX);
	minic.rx_base = dma_rx_buf;
	minic.rx_size = sizeof(dma_rx_buf);
/* FIXME: now we have a temporary HW protection against accidentally overwriting the memory - there's some
   very well hidden bug in Minic's RX logic which sometimes causes an overwrite of the memory outside
   the buffer. */
   
	hi = ((uint32_t)minic.rx_base >> 2) + (sizeof(dma_rx_buf)>>2) - 1;
	minic_writel(MINIC_REG_MPROT, MINIC_MPROT_LO_W(lo) | MINIC_MPROT_HI_W(hi));
	minic.tx_size = MINIC_DMA_TX_BUF_SIZE>>2;
	minic.tx_count = 0;
	minic.rx_count = 0;
	minic_new_rx_buffer();
	minic_writel(MINIC_REG_EIC_IER, MINIC_EIC_IER_RX);
Grzegorz Daniluk's avatar
Grzegorz Daniluk committed
void minic_disable()
{
  minic_writel(MINIC_REG_MCR, 0);
}

Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
int minic_poll_rx()
{
  uint32_t isr;

  isr = minic_readl(MINIC_REG_EIC_ISR);

  return (isr & MINIC_EIC_ISR_RX) ? 1 : 0;
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
}

int minic_rx_frame(uint8_t *hdr, uint8_t *payload, uint32_t buf_size, struct hw_timestamp *hwts)
{
  uint32_t payload_size, num_words;
  uint32_t desc_hdr;
  uint32_t raw_ts;
  uint32_t rx_addr_cur, cur_avail;
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
  int n_recvd;

  if(! (minic_readl(MINIC_REG_EIC_ISR) & MINIC_EIC_ISR_RX))
    return 0;

  desc_hdr = *minic.rx_head;
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
  if(!RX_DESC_VALID(desc_hdr)) /* invalid descriptor? Weird, the RX_ADDR seems to be saying something different. Ignore the packet and purge the RX buffer. */
		//invalid descriptor ? then probably the interrupt was generated by full rx buffer
    if(minic_readl(MINIC_REG_MCR) & MINIC_MCR_RX_FULL)
			minic_new_rx_buffer();
		else
		{
			//otherwise, weird !!
    	mprintf("invalid descriptor @%x = %x\n", (uint32_t)minic.rx_head, desc_hdr);
			minic_new_rx_buffer();
		}	
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
  payload_size = RX_DESC_SIZE(desc_hdr);
  num_words = ((payload_size + 3) >> 2) + 1;

Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
  /* valid packet */	
  if(!RX_DESC_ERROR(desc_hdr))
    if(RX_DESC_HAS_OOB(desc_hdr) && hwts != NULL)
    {
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
      uint32_t counter_r, counter_f, counter_ppsg;
      int cntr_diff;
      uint16_t dhdr;
      payload_size -= RX_OOB_SIZE;
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
      minic_rx_memcpy((uint8_t *)&raw_ts, (uint8_t *)minic.rx_head + payload_size + 6, 4); /* fixme: ugly way of doing unaligned read */
      minic_rx_memcpy((uint8_t *)&dhdr, (uint8_t *)minic.rx_head + payload_size + 4, 2);
      EXPLODE_WR_TIMESTAMP(raw_ts, counter_r, counter_f);
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
      pps_gen_get_time(&sec, &counter_ppsg);
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
      if(counter_r > 3*REF_CLOCK_FREQ_HZ/4 && counter_ppsg < 250000000)
        sec--; 
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
      hwts->sec = sec & 0x7fffffff ;
      cntr_diff = (counter_r & F_COUNTER_MASK) - counter_f;
      if(cntr_diff == 1 || cntr_diff == (-F_COUNTER_MASK))
        hwts->ahead = 1;
      else
        hwts->ahead = 0;
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
      hwts->nsec = counter_r * (REF_CLOCK_PERIOD_PS/1000);
      hwts->valid = (dhdr & RXOOB_TS_INCORRECT) ? 0 : 1;
    }
    n_recvd = (buf_size < payload_size ? buf_size : payload_size);
    minic.rx_count++;

    minic_rx_memcpy(hdr, (void*)minic.rx_head + 4, ETH_HEADER_SIZE);
    minic_rx_memcpy(payload, (void*)minic.rx_head + 4 + ETH_HEADER_SIZE, n_recvd - ETH_HEADER_SIZE);
	minic_rxbuf_free(num_words);
  minic.rx_head = (uint32_t *)((uint32_t)minic.rx_base + ((uint32_t)minic.rx_head+(num_words<<2) - (uint32_t)minic.rx_base) % (minic.rx_size<<2));
  cur_avail = minic_readl(MINIC_REG_RX_AVAIL) & 0xFFFFFF; /* 24-bit field */
  /*empty buffer->no more received packets, or packet reception in progress but not done*/
	if( !RX_DESC_VALID(*minic.rx_head))
  {
    if(minic_readl(MINIC_REG_MCR) & MINIC_MCR_RX_FULL)
      minic_new_rx_buffer();
    minic_writel(MINIC_REG_EIC_ISR, MINIC_EIC_ISR_RX);
  }
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed

  return n_recvd;
}


static uint16_t tx_oob_val = 0;

int minic_tx_frame(uint8_t *hdr, uint8_t *payload, uint32_t size, struct hw_timestamp *hwts)
{
  uint32_t d_hdr, mcr, nwords;
  uint8_t ts_valid;
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
  minic_new_tx_buffer();
Grzegorz Daniluk's avatar
Grzegorz Daniluk committed

Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
  memset((void*)minic.tx_head, 0x0, size + 16);
Grzegorz Daniluk's avatar
Grzegorz Daniluk committed
  memset((void*)minic.tx_head + 4, 0, size < 60 ? 60 : size);
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
  memcpy((void*)minic.tx_head + 4, hdr, ETH_HEADER_SIZE);
  memcpy((void*)minic.tx_head + 4 + ETH_HEADER_SIZE, payload, size - ETH_HEADER_SIZE);

Grzegorz Daniluk's avatar
Grzegorz Daniluk committed
	size = 60;

Grzegorz Daniluk's avatar
Grzegorz Daniluk committed

Grzegorz Daniluk's avatar
Grzegorz Daniluk committed

Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
  if(hwts)
    d_hdr = TX_DESC_WITH_OOB | (tx_oob_val << 12);
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed

  d_hdr |= TX_DESC_VALID | nwords;
  
  *(volatile uint32_t *)(minic.tx_head) = d_hdr;
  *(volatile uint32_t *)(minic.tx_head + nwords) = 0;

  mcr = minic_readl(MINIC_REG_MCR);
  minic_writel(MINIC_REG_MCR, mcr | MINIC_MCR_TX_START);

  while((minic_readl(MINIC_REG_MCR) & MINIC_MCR_TX_IDLE) == 0);
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed

  if(hwts) /* wait for the timestamp */
    {
      uint32_t raw_ts;
      uint16_t fid;
      uint32_t counter_r, counter_f;
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
      uint32_t nsec;

      ts_valid = (uint8_t) (minic_readl(MINIC_REG_TSR0) & MINIC_TSR0_VALID);
      raw_ts = minic_readl(MINIC_REG_TSR1);
      fid = MINIC_TSR0_FID_R(minic_readl(MINIC_REG_TSR0));
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
			if(fid != tx_oob_val)
			{
			 	TRACE_DEV("minic_tx_frame: unmatched fid %d vs %d\n", fid, tx_oob_val);
			}

Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
      EXPLODE_WR_TIMESTAMP(raw_ts, counter_r, counter_f);
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
      if(counter_r > 3*REF_CLOCK_FREQ_HZ/4 && nsec < 250000000)
      hwts->valid = ts_valid;
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
      hwts->ahead = 0;
      hwts->nsec = counter_r * 8;
Grzegorz Daniluk's avatar
Grzegorz Daniluk committed

//	  TRACE_DEV("minic_tx_frame [%d bytes] TS: %d.%d valid %d\n", size, hwts->utc, hwts->nsec, hwts->valid);
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
  tx_oob_val++;

  return size;
}

void minic_get_stats(int *tx_frames, int *rx_frames)
{
 	*tx_frames = minic.tx_count;
 	*rx_frames = minic.rx_count;