/*
* This file is part of SIS.
*
* SIS, SPARC/RISCV instruction simulator. Copyright (C) 2020 Jiri Gaisler
*
* 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 3 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.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, see .
*
*/
/* Emulation of GRETH 10/100 Mbit network interface */
/* Based on grlib-gpl-2018.1-b4217/doc/grip.pdf */
/* Multicast not supported for now ... */
#include
#include
#include
#include
#include
//#include
#include "sis.h"
#include "grlib.h"
#define MDIO_WRITE 1
#define MDIO_READ 2
#define CTRL_SPEED 0x80
#define CTRL_RST 0x40
#define CTRL_RI 8
#define CTRL_TI 4
#define CTRL_RE 2
#define CTRL_TE 1
#define STATUS_TI 8
#define STATUS_RI 4
#define BASE_PNT 0x3f8
#define DESC_EN (1 << 11)
#define DESC_WRAP (1 << 12)
#define DESC_IE (1 << 13)
static uint32 greth_ctrl;
static uint32 greth_status;
static uint32 greth_macmsb;
static uint32 greth_maclsb;
static uint32 greth_mdio;
static uint32 greth_txbase;
static uint32 greth_txdesc;
static uint32 greth_txbuf;
static unsigned char *greth_txbufptr;
static uint32 greth_rxbase;
static uint32 greth_rxdesc;
static uint32 greth_rxbuf;
static unsigned char *greth_rxbufptr;
static unsigned char greth_mac[6];
static uint64 mac;
static const char broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
int greth_irq;
/* Simple emulation of Microchip KSZ8041NL/RNL PHY */
static uint32
mdio_read (uint32 address)
{
uint32 res;
switch (address & 0x1F)
{
case 0:
res = 0x3100;
break;
case 1:
res = 0x7865;
break;
case 2:
res = 0x0022;
break;
case 3:
res = 0x1512;
break;
case 4:
res = 0x01ef;
break;
case 5:
res = 0x41e1;
break;
default:
res = 0;
}
if (sis_verbose > 1)
printf ("%8lu cpu %d MDIO read a: %02x, d: %04x\n",
ebase.simtime, cpu, address, res);
return res;
}
static void
mdio_write (uint32 address, uint32 data)
{
if (sis_verbose > 1)
printf ("%8lu cpu %d MDIO write a: %02x, d: %04x\n",
ebase.simtime, cpu, address, data);
}
static void
greth_tx (void)
{
int32 ws;
uint32 tmpdesc;
unsigned char buffer[2048];
int len, wlen, i;
if (greth_ctrl & CTRL_TE)
{
ms->memory_read (greth_txbase, &greth_txdesc, &ws);
if (greth_txdesc & DESC_EN)
{
ms->memory_read (greth_txbase + 4, &greth_txbuf, &ws);
greth_txbufptr = ms->get_mem_ptr (greth_txbuf, 1536);
len = greth_txdesc & 0x7ff;
/* endian swap on host/target endian mismatch */
if (arch->bswap)
{
wlen = (len + 3) & ~3; // align up to 32-bit word
for (i = 0; i < wlen; i++)
buffer[i] = greth_txbufptr[arch->bswap ^ i];
sis_tap_write ((unsigned char *) buffer, len);
}
else
sis_tap_write (greth_txbufptr, greth_txdesc & 0x7ff);
greth_status |= STATUS_TI;
if ((greth_ctrl & CTRL_TI) && (greth_txdesc & DESC_IE))
grlib_set_irq (greth_irq);
if (sis_verbose)
printf ("packet transmitted, len %d, desc %d\n",
greth_txdesc & 0x7ff, (greth_txbase & BASE_PNT) >> 3);
tmpdesc = greth_txdesc & 0x7ff;
ms->memory_write (greth_txbase, &tmpdesc, 2, &ws);
if ((greth_txdesc & DESC_WRAP) ||
((greth_txbase & BASE_PNT) == BASE_PNT))
greth_txbase &= ~BASE_PNT;
else
greth_txbase += 8;
ms->memory_read (greth_txbase, &greth_txdesc, &ws);
}
}
event (greth_tx, 1, 5000);
}
/* Write GRETH APB registers */
void
greth_write (uint32 address, uint32 data)
{
int32 ws;
switch (address & 0xFC)
{
case 0:
if (data & CTRL_RST)
{
greth_ctrl = CTRL_SPEED;
}
else
{
if ((data & CTRL_RE) && !(greth_ctrl & CTRL_RE) && !mac)
{
mac = greth_macmsb;
mac <<= 32;
mac |= greth_maclsb;
sis_tap_init (mac);
mac = 1;
event (greth_tx, 1, 100);
sync_rt = 1;
}
greth_ctrl = data;
}
break;
case 4:
greth_status &= ~data;
break;
case 8:
greth_macmsb = data & 0x0ffff;
greth_mac[0] = (data >> 8) & 0x0ff;
greth_mac[1] = data & 0x0ff;
break;
case 0x0c:
greth_maclsb = data;
greth_mac[2] = (data >> 24) & 0x0ff;
greth_mac[3] = (data >> 16) & 0x0ff;
greth_mac[4] = (data >> 8) & 0x0ff;
greth_mac[5] = data & 0x0ff;
break;
case 0x10:
greth_mdio = data & 0xfffffff0;
if (data & MDIO_WRITE)
{
mdio_write ((data >> 6) & 0x1f, data >> 16);
}
else if (data & MDIO_READ)
{
greth_mdio = mdio_read ((data >> 6) & 0x1f) << 16;
}
break;
case 0x14:
greth_txbase = data & 0xfffffff8;
break;
case 0x18:
greth_rxbase = data & 0xfffffff8;
break;
}
if (sis_verbose > 1)
printf ("%8lu cpu %d APB write a: %08x, d: %08x\n",
ebase.simtime, cpu, address, data);
}
/* Read GRETH APB registers */
uint32
greth_read (uint32 address)
{
uint32 res;
switch (address & 0xFC)
{
case 0:
res = greth_ctrl;
break;
case 4:
res = greth_status;
break;
case 8:
res = greth_macmsb;
break;
case 0x0c:
res = greth_maclsb;
break;
case 0x10:
res = greth_mdio;
break;
case 0x14:
res = greth_txbase;
break;
case 0x18:
res = greth_rxbase;
break;
default:
res = 0;
}
return res;
}
void
greth_rxready (unsigned char *buffer, int len)
{
uint32 tmpdesc, ws;
int i, wlen;
if (sis_verbose > 1)
{
printf ("net: read %d bytes from device\n", len);
for (i = 0; i < len; i++)
printf ("%02x", buffer[i]);
printf ("\n");
}
/* accept only unicast or broadcast packets */
if (((strncmp (greth_mac, buffer, 6) == 0) ||
(strncmp (buffer, broadcast, 6) == 0)) && (greth_ctrl & CTRL_RE))
{
ms->memory_read (greth_rxbase, &greth_rxdesc, &ws);
if (greth_rxdesc & DESC_EN)
{
ms->memory_read (greth_rxbase + 4, &greth_rxbuf, &ws);
greth_rxbufptr = ms->get_mem_ptr (greth_rxbuf, 1536);
/* endian swap on host/target endian mismatch */
if (arch->bswap)
{
wlen = (len + 3) & ~3; // align up to 32-bit word
for (i = 0; i < wlen; i++)
greth_rxbufptr[i] = buffer[arch->bswap ^ i];
}
else
memcpy (greth_rxbufptr, buffer, len);
tmpdesc = len & 0x7ff;
ms->memory_write (greth_rxbase, &tmpdesc, 2, &ws);
greth_status |= STATUS_RI;
if ((greth_rxdesc & DESC_WRAP)
|| ((greth_rxbase & BASE_PNT) == BASE_PNT))
greth_rxbase &= ~BASE_PNT;
else
greth_rxbase += 8;
if ((greth_ctrl & CTRL_RI) && (greth_rxdesc & DESC_IE))
grlib_set_irq (greth_irq);
}
else if (sis_verbose > 1)
printf ("net: received packet dropped!\n");
}
}