ref: a7b1ab8236b7ccd41e96e9e4008cbc3213294724
dir: /etherbbb.c/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/netif.h"
#include "etherif.h"
enum{
CPSW_SS,
SS_SOFT_RESET = 0x08/4,
CPSW_CPDMA = 0x0800/4,
TX_CONTROL = CPSW_CPDMA+0x04/4,
TX_EN = 1<<0,
RX_CONTROL = CPSW_CPDMA+0x14/4,
RX_EN = 1<<0,
CPDMA_SOFT_RESET = CPSW_CPDMA+0x1c/4,
DMASTATUS = CPSW_CPDMA+0x24/4,
RX_BUFFER_OFFSET = CPSW_CPDMA+0x28/4,
TX_INTMASK_SET = CPSW_CPDMA+0x88/4,
TX_INTMASK_CLEAR = CPSW_CPDMA+0x8c/4,
TX_MASK_ALL = 0xff,
CPDMA_EOI_VECTOR = CPSW_CPDMA+0x94/4,
RX_INTMASK_SET = CPSW_CPDMA+0xa8/4,
RX_MASK_ALL = 0xff,
DMA_INTMASK_SET = CPSW_CPDMA+0xb8/4,
STAT_INT_MASK = 1<<0,
HOST_ERR_INT_MASK = 1<<1,
CPSW_STATERAM = 0x0a00/4,
TX0_HDP = CPSW_STATERAM+0x00/4,
RX0_HDP = CPSW_STATERAM+0x20/4,
TX0_CP = CPSW_STATERAM+0x40/4,
RX0_CP = CPSW_STATERAM+0x60/4,
CPSW_ALE = 0x0d00/4,
ALE_CONTROL = CPSW_ALE+0x08/4,
BYPASS = 1<<4,
CLEAR_TABLE = 1<<30,
ENABLE_ALE = 1<<31,
PORTCTL0 = CPSW_ALE+0x40/4,
PORTCTL1 = CPSW_ALE+0x44/4,
PORT_STATE = 3,
CPSW_SL1 = 0x0d80/4,
MACCONTROL = CPSW_SL1+0x04/4,
IFCTL_A = 1<<15,
GIG = 1<<7,
GMII_EN = 1<<5,
FULLDUPLEX = 1<<0,
MACSTATUS = CPSW_SL1+0x08/4,
SL1_SOFT_RESET = CPSW_SL1+0x0c/4,
CPSW_WR = 0x1200/4,
WR_SOFT_RESET = CPSW_WR+0x04/4,
C0_RX_THRESH_EN = CPSW_WR+0x10/4,
C0_RX_EN = CPSW_WR+0x14/4,
C0_TX_EN = CPSW_WR+0x18/4,
C0_MISC_EN = CPSW_WR+0x1c/4,
MDIO_USERINT = 1<<0,
MDIO_LINKINT = 1<<1,
HOST_PEND = 1<<2,
STAT_PEND = 1<<3,
EVNT_PEND = 1<<4,
C0_RX_THRESH_STAT = CPSW_WR+0x40/4,
C0_RX_STAT = CPSW_WR+0x44/4,
C0_TX_STAT = CPSW_WR+0x48/4,
C0_MISC_STAT = CPSW_WR+0x4c/4,
RGMII_CTL = CPSW_WR+0x88/4,
RGMII1_LINK = 1<<0,
RGMII2_LINK = 1<<4,
MDIO = 0x1000/4,
MDIOCONTROL = MDIO+04/4,
MDIOALIVE = MDIO+0x08/4,
MDIOLINK = MDIO+0x0c/4,
MDIOLINKINTRAW = MDIO+0x10/4,
MDIOLINKINTMASKED = MDIO+0x14/4,
MDIOUSERPHYSEL0 = MDIO+0x84/4,
MDIOUSERPHYSEL1 = MDIO+0x88/4,
PHYADDRMON = 0x1f,
LINKINTENB = 1<<6,
LINKSEL = 1<<7,
};
#define TX_HDP(i) (TX0_HDP+i)
#define RX_HDP(i) (RX0_HDP+i)
#define TX_CP(i) (TX0_CP+i)
#define RX_CP(i) (RX0_CP+i)
enum{
EOQ = 1<<12,
OWNER = 1<<13,
EOP = 1<<14,
SOP = 1<<15,
};
typedef struct Desc Desc;
struct Desc
{
uintptr next;
uintptr bufptr;
ushort buflen;
ushort bufoff;
ushort pktlen;
ushort flags;
};
#define Rbsz ROUNDUP(sizeof(Etherpkt)+16, 64)
enum{
RXRING = 0x200,
TXRING = 0x200,
};
typedef struct Ctlr Ctlr;
struct Ctlr
{
ulong *r;
int attach;
int rxconsi;
int rxprodi;
Desc *rxd;
Block **rxb;
int txconsi;
int txprodi;
Desc *txd;
Block **txb;
Lock txlock;
};
static int ethinit(Ether*);
static int
replenish(Ctlr *c)
{
int n;
Block *b;
Desc *d;
for(;;){
n = (c->rxprodi+1) & (RXRING-1);
if(n == c->rxconsi){
c->rxd[c->rxprodi].next = 0;
break;
}
b = iallocb(Rbsz);
if(b == nil){
iprint("cpsw: out of memory for rx buffers\n");
return -1;
}
c->rxb[n] = b;
d = &c->rxd[n];
d->bufptr = PADDR(b->rp);
d->bufoff = 0;
d->buflen = BALLOC(b);
if((d->buflen & 0x7ff) != d->buflen)
d->buflen = 0x7ff;
d->flags = OWNER;
assert(d->buflen > 0);
//cleandse(d, d+1);
cleandse(b->base, b->lim);
//coherence();
assert(c->rxd[c->rxprodi].next == 0);
d = &c->rxd[c->rxprodi];
d->next = PADDR(&c->rxd[n]);
//cleandse(d, d+1);
c->rxprodi = n;
}
return 0;
}
static void
ethrxirq(Ureg*, void *arg)
{
static int x;
int n;
Ether *edev;
Ctlr *c;
Desc *d, *d0;
Block *b;
edev = arg;
c = edev->ctlr;
//iprint("rx enter (%d)!\n", x);
n = 0;
d0 = nil;
for(;;){
d = &c->rxd[c->rxconsi];
//invaldse(d, d+1);
if((d->flags & OWNER) != 0)
break;
if((d->flags & SOP) == 0 || (d->flags & EOP) == 0)
iprint("cpsw: partial frame received -- shouldn't happen\n");
if((d->flags & OWNER) == 0 && (d->flags & EOQ) != 0 && d->next != 0){
//iprint("cpsw: rx misqueue detected\n");
d0 = d;
}
b = c->rxb[c->rxconsi];
b->wp = b->rp + (d->pktlen & 0x7ff);
invaldse(b->rp, b->wp);
//coherence();
etheriq(edev, b, 1);
c->r[RX0_CP] = PADDR(d);
c->rxconsi = (c->rxconsi+1) & (RXRING-1);
replenish(c);
n++;
}
if(n == 0)
iprint("cpsw: rx buffer full\n");
if(d0 != nil){
assert(d0->next == PADDR(d));
c->r[RX0_HDP] = PADDR(d);
}
c->r[CPDMA_EOI_VECTOR] = 1;
//iprint("rx leave (%d)!\n", x++);
}
static void
ethtx(Ether *edev)
{
int i, n;
Block *b;
Ctlr *c;
Desc *d0, *dn, *dp;
c = edev->ctlr;
ilock(&c->txlock);
for(;;){
dp = &c->txd[c->txconsi];
if(c->txconsi == c->txprodi){
dp->flags |= EOQ;
break;
}
if((dp->flags & OWNER) != 0)
break;
c->txconsi = (c->txconsi+1) & (TXRING-1);
}
c->r[TX0_CP] = PADDR(&c->txd[c->txconsi]);
n = 0;
d0 = nil;
for(;;){
i = (c->txprodi+1) & (TXRING-1);
dn = &c->txd[i];
if((dn->flags & OWNER) != 0){
iprint("cpsw: tx buffer full\n");
break;
}
b = qget(edev->oq);
if(b == nil)
break;
if(c->txb[c->txprodi] != nil)
freeb(c->txb[c->txprodi]);
c->txb[c->txprodi] = b;
dp->next = PADDR(dn);
dn->next = 0;
dn->bufptr = PADDR(b->rp);
dn->buflen = BLEN(b);
dn->flags = SOP | EOP | OWNER;
dn->pktlen = dn->buflen;
if(d0 == nil)
d0 = dp;
dp = dn;
c->txprodi = i;
n++;
}
//iprint("tx: queued %d packets\n", n);
if(d0 != nil){ // queued at least 1 packet
if((d0->flags & OWNER) == 0 && (d0->flags & EOQ) != 0){
//iprint("tx: misqueue detected!\n");
c->r[TX0_HDP] = d0->next;
}
}
iunlock(&c->txlock);
}
static void
ethtxirq(Ureg*, void *arg)
{
Ether *edev;
Ctlr *c;
edev = arg;
c = edev->ctlr;
//assert(c->r[TX0_HDP] == 0);
ethtx(edev);
c->r[CPDMA_EOI_VECTOR] = 2;
}
static void
debugctlr(Ctlr *c)
{
int i;
uintptr hdp;
Desc *d;
hdp = c->r[RX0_HDP];
if(hdp != 0){
d = (Desc*)kaddr(hdp);
iprint("pa(RX0_HDP)=%#p\n", hdp);
iprint("va(RX0_HDP)=%#p\n", d);
iprint("ii(RX0_HDP)=%ld\n", d - &c->rxd[0]);
iprint("next=%#p\n", d->next);
}
for(i = 0; i < RXRING; i++){
d = &c->rxd[i];
if(d->buflen <= d->bufoff)
iprint("i=%d -> {buflen=%d, bufoff=%d, pktlen=%d }\n", i, d->buflen, d->bufoff, d->pktlen);
}
}
static void
ethmiscirq(Ureg*, void *arg)
{
Ether *edev;
Ctlr *c;
ulong r;
edev = arg;
c = edev->ctlr;
r = c->r[C0_MISC_STAT];
if((r & MDIO_LINKINT) != 0){
if((c->r[MDIOLINK] & 1) == 0){
edev->link = 0;
iprint("cpsw: no link\n");
}else{
edev->link = 1;
edev->mbps = c->r[MACCONTROL] & IFCTL_A ? 100 : 10;
iprint("cpsw: %dMbps %s duplex link\n", edev->mbps, c->r[MACCONTROL] & FULLDUPLEX ? "full" : "half");
}
c->r[MDIOLINKINTRAW] |= 1;
}
if((r & HOST_PEND) != 0){
debugctlr(c);
r = c->r[DMASTATUS];
iprint("cpsw: dma host error (tx=0x%ulx, rx=0x%ulx)\n", (r>>20)&0xf, (r>>12)&0xf);
ethinit(edev);
}
c->r[CPDMA_EOI_VECTOR] = 3;
}
static int
ethinit(Ether *edev)
{
int i;
Ctlr *c;
c = edev->ctlr;
c->r[SS_SOFT_RESET] |= 1;
c->r[SL1_SOFT_RESET] |= 1;
c->r[CPDMA_SOFT_RESET] |= 1;
while((c->r[SS_SOFT_RESET] & 1) != 0);
while((c->r[SL1_SOFT_RESET] & 1) != 0);
while((c->r[CPDMA_SOFT_RESET] & 1) != 0);
for(i = 0; i < 8; i++){
c->r[TX_HDP(i)] = 0;
c->r[RX_HDP(i)] = 0;
c->r[TX_CP(i)] = 0;
c->r[RX_CP(i)] = 0;
}
c->r[ALE_CONTROL] |= ENABLE_ALE | CLEAR_TABLE | BYPASS;
c->r[PORTCTL0] |= PORT_STATE;
c->r[PORTCTL1] |= PORT_STATE;
//intrenable(ETHIRQRXTHR, ethrxthrirq, edev, edev->name);
intrenable(ETHIRQRX, ethrxirq, edev, edev->name);
intrenable(ETHIRQTX, ethtxirq, edev, edev->name);
intrenable(ETHIRQMISC, ethmiscirq, edev, edev->name);
c->r[TX_INTMASK_SET] |= TX_MASK_ALL;
c->r[RX_INTMASK_SET] |= RX_MASK_ALL;
c->r[DMA_INTMASK_SET] |= STAT_INT_MASK | HOST_ERR_INT_MASK;
c->r[MDIOUSERPHYSEL0] |= LINKINTENB;
c->r[C0_RX_THRESH_EN] |= 1;
c->r[C0_RX_EN] |= 1;
c->r[C0_TX_EN] |= 1;
c->r[C0_MISC_EN] |= HOST_PEND | MDIO_LINKINT;
c->r[RX_CONTROL] |= RX_EN;
c->r[TX_CONTROL] |= TX_EN;
c->r[MACCONTROL] &= ~GIG;
c->r[MACCONTROL] |= GMII_EN | FULLDUPLEX | IFCTL_A;
if(c->rxd == nil)
c->rxd = ucalloc(RXRING*sizeof(Desc));
memset(c->rxd, 0, RXRING*sizeof(Desc));
if(c->rxb == nil)
c->rxb = ucalloc(RXRING*sizeof(Block*));
memset(c->rxb, 0, RXRING*sizeof(Block*));
if(c->txd == nil)
c->txd = ucalloc(TXRING*sizeof(Desc));
memset(c->txd, 0, TXRING*sizeof(Desc));
if(c->txb == nil)
c->txb = ucalloc(TXRING*sizeof(Block*));
memset(c->txb, 0, TXRING*sizeof(Block*));
#ifdef nope
ulong x;
x = va2pa(c->txd);
if((x&1) != 0)
iprint("ethinit: va2pa failed\n");
else
iprint("ethinit: %#p, SH=%ulx, OUTER=%ulx, INNER=%ulx\n", x, (x>>7)&1, (x>>2)&3, (x>>4)&7);
x = va2pa(c->txb);
if((x&1) != 0)
iprint("ethinit: va2pa failed\n");
else
iprint("ethinit: %#p, SH=%ulx, OUTER=%ulx, INNER=%ulx\n", x, (x>>7)&1, (x>>2)&3, (x>>4)&7);
#endif
c->rxprodi = RXRING-1;
c->rxconsi = RXRING-1;
replenish(c);
c->rxconsi = 0;
replenish(c);
c->r[RX0_HDP] = PADDR(&c->rxd[c->rxconsi]);
return 0;
}
static void
ethprom(void*, int)
{
}
static void
ethmcast(void*, uchar*, int)
{
}
static void
ethattach(Ether *edev)
{
Ctlr *c;
c = edev->ctlr;
if(c->attach)
return;
c->attach = 1;
}
static int
etherpnp(Ether *edev)
{
static Ctlr ct;
static uchar mac[] = {0x6c, 0xec, 0xeb, 0xaf, 0x1c, 0xed};
ulong x;
if(ct.r != nil)
return -1;
memmove(edev->ea, mac, 6);
edev->ctlr = &ct;
edev->port = ETH_BASE;
ct.r = vmap(edev->port, 2*BY2PG);
#ifdef nope
x = va2pa(ct.r);
if((x&1) != 0)
iprint("etherpnp: va2pa failed\n");
else
iprint("etherpnp: %#p, SH=%ulx, OUTER=%ulx, INNER=%ulx\n", x, (x>>7)&1, (x>>2)&3, (x>>4)&7);
#endif
edev->irq0 = ETHIRQRXTHR;
edev->irqn = ETHIRQMISC;
edev->transmit = ethtx;
edev->attach = ethattach;
edev->promiscuous = ethprom;
edev->multicast = ethmcast;
edev->mbps = 100;
edev->arg = edev;
if(ethinit(edev) < 0){
edev->ctlr = nil;
return -1;
}
return 0;
}
void
etherbbblink(void)
{
addethercard("cpsw", etherpnp);
}