diff options
author | aiju <devnull@localhost> | 2020-01-10 18:49:33 +0000 |
---|---|---|
committer | aiju <devnull@localhost> | 2020-01-10 18:49:33 +0000 |
commit | d64f76c96c5ecfedf6c2a3fcf4b5ce6fa53df714 (patch) | |
tree | c8060d5736b448ff49bd58e90f546c90a0102ca7 /sys/src/9/cycv | |
parent | 17ebe55031ae6945ad1f671b69267a672328e4b1 (diff) |
add cycv ethernet driver
Diffstat (limited to 'sys/src/9/cycv')
-rw-r--r-- | sys/src/9/cycv/cycv | 1 | ||||
-rw-r--r-- | sys/src/9/cycv/dat.h | 2 | ||||
-rw-r--r-- | sys/src/9/cycv/ethercycv.c | 438 | ||||
-rw-r--r-- | sys/src/9/cycv/io.h | 10 | ||||
-rw-r--r-- | sys/src/9/cycv/l.s | 10 | ||||
-rw-r--r-- | sys/src/9/cycv/mmu.c | 23 |
6 files changed, 482 insertions, 2 deletions
diff --git a/sys/src/9/cycv/cycv b/sys/src/9/cycv/cycv index cc2e0aeaa..61aa7eabb 100644 --- a/sys/src/9/cycv/cycv +++ b/sys/src/9/cycv/cycv @@ -24,6 +24,7 @@ dev segment link + ethercycv ethermedium loopbackmedium netdevmedium diff --git a/sys/src/9/cycv/dat.h b/sys/src/9/cycv/dat.h index 7195e7ad7..ca1dff54c 100644 --- a/sys/src/9/cycv/dat.h +++ b/sys/src/9/cycv/dat.h @@ -200,3 +200,5 @@ struct DevConf }; #define mpcore ((ulong*)MPCORE_BASE) +#define resetmgr ((ulong*)RESETMGR_BASE) +#define sysmgr ((ulong*)SYSMGR_BASE) diff --git a/sys/src/9/cycv/ethercycv.c b/sys/src/9/cycv/ethercycv.c new file mode 100644 index 000000000..576eae63d --- /dev/null +++ b/sys/src/9/cycv/ethercycv.c @@ -0,0 +1,438 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/netif.h" +#include "../port/etherif.h" + +#define Rbsz ROUNDUP(sizeof(Etherpkt)+16, 64) + +enum { + Linkdelay = 500, + RXRING = 512, + TXRING = 512 +}; + +enum { + PERMODRST_EMAC1 = 1<<1, + + SYSMGR_EMAC_CTRL = 0x60/4, + + MAC_CONFIG = 0x0/4, + MAC_FRAME_FILTER = 0x4/4, + + GMII_ADDRESS = 0x10/4, + GMII_DATA = 0x14/4, + + INTERRUPT_STATUS = 0x38/4, + INTERRUPT_MASK = 0x3C/4, + + MAC_ADDRESS = 0x40/4, + HASH_TABLE = 0x500/4, + + DMA_BUS_MODE = 0x1000/4, + DMA_BUS_MODE_SWR = 1<<0, + DMA_TX_POLL = 0x1004/4, + DMA_RX_POLL = 0x1008/4, + DMA_STATUS = 0x1014/4, + DMA_OPERATION_MODE = 0x1018/4, + DMA_INTERRUPT_ENABLE = 0x101C/4, + DMA_AXI_STATUS = 0x102C/4, + + RXRING_ADDRESS = 0x100C/4, + TXRING_ADDRESS = 0x1010/4, + +}; + +enum { + MDCTRL, + MDSTATUS, + MDID1, + MDID2, + MDAUTOADV, + MDAUTOPART, + MDAUTOEX, + MDAUTONEXT, + MDAUTOLINK, + MDGCTRL, + MDGSTATUS, + MDPHYCTRL = 0x1f, + + /* MDCTRL */ + MDRESET = 1<<15, + AUTONEG = 1<<12, + FULLDUP = 1<<8, + /* MDSTATUS */ + LINK = 1<<2, + /* MDGSTATUS */ + RECVOK = 3<<12, +}; + + +typedef struct Ctlr Ctlr; + +struct Ctlr { + ulong *r; + ulong *rxr, *txr; + Block **rxs, **txs; + int rxprodi, rxconsi, txi; + int attach; + Lock txlock; + uchar (*mc)[6]; + int nmc; +}; + +static void +mdwrite(Ctlr *c, int r, u16int v) +{ + while((c->r[GMII_ADDRESS] & 1<<0) != 0); + c->r[GMII_DATA] = v; + c->r[GMII_ADDRESS] = 1<<11 | (r&31)<<6 | 1<<1 | 1<<0; + while((c->r[GMII_ADDRESS] & 1<<0) != 0); +} + +static u16int +mdread(Ctlr *c, int r) +{ + while((c->r[GMII_ADDRESS] & 1<<0) != 0); + c->r[GMII_ADDRESS] = 1<<11 | (r&31)<<6 | 1<<0; + while((c->r[GMII_ADDRESS] & 1<<0) != 0); + return c->r[GMII_DATA]; +} + +static void +ethproc(void *ved) +{ + Ether *edev; + Ctlr *c; + char *sp, *dpl; + u16int v; + + edev = ved; + c = edev->ctlr; + + mdwrite(c, MDCTRL, AUTONEG); + for(;;){ + if((mdread(c, MDSTATUS) & LINK) == 0){ + edev->link = 0; + print("eth: no link\n"); + while((mdread(c, MDSTATUS) & LINK) == 0) + tsleep(&up->sleep, return0, nil, Linkdelay); + } + v = mdread(c, MDPHYCTRL); + if((v & 0x40) != 0){ + sp = "1000BASE-T"; + while((mdread(c, MDGSTATUS) & RECVOK) != RECVOK) + ; + edev->mbps = 1000; + c->r[MAC_CONFIG] &= ~(1<<15); + + }else if((v & 0x20) != 0){ + sp = "100BASE-TX"; + edev->mbps = 100; + c->r[MAC_CONFIG] = c->r[MAC_CONFIG] | (1<<15|1<<14); + }else if((v & 0x10) != 0){ + sp = "10BASE-T"; + edev->mbps = 10; + c->r[MAC_CONFIG] = c->r[MAC_CONFIG] & ~(1<<14) | 1<<15; + }else + sp = "???"; + if((v & 0x08) != 0){ + dpl = "full"; + c->r[MAC_CONFIG] |= 1<<11; + }else{ + dpl = "half"; + c->r[MAC_CONFIG] &= ~(1<<11); + } + edev->link = 1; + print("eth: %s %s duplex link\n", sp, dpl); + c->r[MAC_CONFIG] |= 1<<3 | 1<<2; + while((mdread(c, MDSTATUS) & LINK) != 0) + tsleep(&up->sleep, return0, nil, Linkdelay); + } +} + +static int +replenish(Ctlr *c) +{ + Block *bp; + int i; + ulong *r; + + while(c->rxprodi != c->rxconsi){ + i = c->rxprodi; + bp = iallocb(Rbsz); + if(bp == nil){ + print("eth: out of memory for receive buffers\n"); + return -1; + } + c->rxs[i] = bp; + r = &c->rxr[4 * i]; + r[0] = 0; + cleandse(bp->base, bp->lim); + r[1] = Rbsz; + if(i == RXRING - 1) r[1] |= 1<<15; + r[2] = PADDR(bp->rp); + r[3] = 0; + r[0] |= 1<<31; + c->rxprodi = (c->rxprodi + 1) & (RXRING - 1); + } + c->r[DMA_RX_POLL] = 0xBA5EBA11; + return 0; +} + +static void +ethrx(Ether *edev) +{ + Ctlr *c; + ulong *r; + Block *bp; + + c = edev->ctlr; + for(;;){ + r = &c->rxr[4 * c->rxconsi]; + if((r[0] >> 31) != 0) + break; + if((r[0] & 1<<15) != 0) + iprint("eth: error frame\n"); + if((r[0] & (3<<8)) != (3<<8)) + iprint("eth: lilu dallas multidescriptor\n"); + bp = c->rxs[c->rxconsi]; + bp->wp = bp->rp + (r[0] >> 16 & 0x3fff); + invaldse(bp->rp, bp->wp); + etheriq(edev, bp); + c->rxconsi = (c->rxconsi + 1) & (RXRING - 1); + replenish(c); + } +} + +static void +ethtx(Ether *edev) +{ + Ctlr *c; + ulong *r; + Block *bp; + + c = edev->ctlr; + ilock(&c->txlock); + for(;;){ + r = &c->txr[4 * c->txi]; + if((r[0] >> 31) != 0){ + print("eth: transmit buffer full\n"); + break; + } + bp = qget(edev->oq); + if(bp == nil) + break; + if(c->txs[c->txi] != nil) + freeb(c->txs[c->txi]); + c->txs[c->txi] = bp; + cleandse(bp->rp, bp->wp); + r[0] = 1<<30 | 1<<29 | 1<<28; + if(c->txi == TXRING - 1) + r[0] |= 1<<21; + r[1] = BLEN(bp); + r[2] = PADDR(bp->rp); + r[3] = 0; + r[0] |= 1<<31; + coherence(); + c->r[DMA_TX_POLL] = 0xBA5EBA11; + c->txi = (c->txi + 1) & (TXRING - 1); + } + iunlock(&c->txlock); +} + +static void +ethirq(Ureg *, void *arg) +{ + Ether *edev; + Ctlr *c; + ulong fl; + + edev = arg; + c = edev->ctlr; + fl = c->r[DMA_STATUS]; + c->r[DMA_STATUS] = fl; + if((fl & 1<<1) != 0) + iprint("eth: transmit stop\n"); + if((fl & (1<<0|1<<2)) != 0) + ethtx(edev); + if((fl & 1<<4) != 0) + iprint("eth: receive overflow\n"); + if((fl & (1<<6|1<<7)) != 0) + ethrx(edev); + if((fl & 1<<8) != 0) + iprint("eth: receive stop\n"); +} + +static int +ethinit(Ether *edev) +{ + Ctlr *c; + + c = edev->ctlr; + resetmgr[PERMODRST] |= PERMODRST_EMAC1; + /* assume bootloader set up clock */ + sysmgr[SYSMGR_EMAC_CTRL] = 1<<2 | 1; /* RGMII */ + sysmgr[FPGA_MODULE] = sysmgr[FPGA_MODULE] & ~(1<<2 | 1<<3); + microdelay(1); + resetmgr[PERMODRST] &= ~PERMODRST_EMAC1; + + /* reset DMA */ + c->r[DMA_BUS_MODE] |= DMA_BUS_MODE_SWR; + do microdelay(1); + while((c->r[DMA_BUS_MODE] & DMA_BUS_MODE_SWR) != 0); + /* wait for AXI transactions to finish */ + while((c->r[DMA_AXI_STATUS] & 3) != 0) + microdelay(1); + /* set up bus mode (32 beat bursts) */ + c->r[DMA_BUS_MODE] |= 32 << 8; + + c->r[MAC_ADDRESS] = 1<<31 | edev->ea[5] << 8 | edev->ea[4]; + c->r[MAC_ADDRESS+1] = edev->ea[3] << 24 | edev->ea[2] << 16 | edev->ea[1] << 8 | edev->ea[0]; + c->r[MAC_FRAME_FILTER] = 0; + + c->rxr = ucalloc(16 * RXRING); + c->txr = ucalloc(16 * TXRING); + c->rxs = xspanalloc(4 * RXRING, 4, 0); + c->txs = xspanalloc(4 * TXRING, 4, 0); + memset(c->rxr, 0, 16 * RXRING); + memset(c->txr, 0, 16 * TXRING); + c->rxconsi = 1; + replenish(c); + c->rxconsi = 0; + replenish(c); + + c->r[RXRING_ADDRESS] = PADDR(c->rxr); + c->r[TXRING_ADDRESS] = PADDR(c->txr); + c->r[DMA_STATUS] = -1; + c->r[INTERRUPT_MASK] = -1; + c->r[DMA_INTERRUPT_ENABLE] = 1<<16 | 1<<15 | 1<<8 | 1<<6 | 1<<2 | 1<<1 | 1<<0; + c->r[DMA_OPERATION_MODE] = 1<<1 | 1<<13; + return 0; +} + +static void +ethattach(Ether *edev) +{ + Ctlr *c; + + c = edev->ctlr; + if(c->attach) + return; + c->attach = 1; + kproc("ethproc", ethproc, edev); +} + +static void +ethprom(void *arg, int on) +{ + Ether *edev; + Ctlr *c; + + edev = arg; + c = edev->ctlr; + if(on) + c->r[MAC_FRAME_FILTER] |= 0x80000001; + else + c->r[MAC_FRAME_FILTER] &= ~0x80000001; +} + +static void +sethash(uchar *ea, ulong *hash) +{ + ulong crc; + int i; + uchar n; + + crc = ethercrc(ea, 6); + n = 0; + for(i = 0; i < 8; i++){ + n = n << 1 | crc & 1; + crc >>= 1; + } + n ^= 0xff; + hash[n>>5] |= (1<<(n & 31)); +} + +static void +ethmcast(void *arg, uchar *ea, int on) +{ + enum { MCSlots = 31 }; + Ether *edev; + Ctlr *c; + int i, p; + ulong hash[8]; + + edev = arg; + c = edev->ctlr; + if(on){ + c->mc = realloc(c->mc, (c->nmc + 1) * 6); + memmove(c->mc[c->nmc++], ea, 6); + }else{ + for(i = 0; i < c->nmc; i++) + if(memcmp(c->mc[i], ea, 6) == 0) + break; + if(i == c->nmc) + return; + memmove(c->mc[i], c->mc[i+1], (c->nmc - i - 1) * 6); + } + p = c->r[MAC_FRAME_FILTER]; + /* set promiscuous in order to not lose packets while updating */ + c->r[MAC_FRAME_FILTER] = p | 0x80000001; + if(c->nmc <= MCSlots){ + for(i = 0; i < c->nmc; i++){ + c->r[MAC_ADDRESS + 2 * (i + 1)] = 1<<31 | c->mc[i][5] << 8 | c->mc[i][4]; + c->r[MAC_ADDRESS + 2 * (i + 1) + 1] = c->mc[i][3] << 24 | c->mc[i][2] << 16 | c->mc[i][1] << 8 | c->mc[i][0]; + } + for(i = 2 * i; i < 2*MCSlots; i++) + c->r[MAC_ADDRESS + 2 + i] = 0; + c->r[MAC_FRAME_FILTER] = p & ~(1<<2); + }else{ + memset(hash, 0, sizeof(hash)); + for(i = 0; i < c->nmc; i++) + sethash(c->mc[i], hash); + for(i = 0; i < 8; i++) + c->r[HASH_TABLE + i] = hash[i]; + c->r[MAC_FRAME_FILTER] = p | 1<<2; + } +} + +static int +etherpnp(Ether *edev) +{ + static Ctlr ct; + static uchar mac[] = {0x0e, 0xa7, 0xde, 0xad, 0xca, 0xfe}; + + if(ct.r != nil) + return -1; + + memmove(edev->ea, mac, 6); + edev->ctlr = &ct; + edev->port = EMAC1_BASE; + ct.r = (ulong *) edev->port; + edev->irq = EMAC1IRQ; + edev->ctlr = &ct; + edev->attach = ethattach; + edev->transmit = ethtx; + edev->arg = edev; + edev->mbps = 1000; + edev->promiscuous = ethprom; + edev->multicast = ethmcast; + + if(ethinit(edev) < 0){ + edev->ctlr = nil; + return -1; + } + + intrenable(edev->irq, ethirq, edev, LEVEL, edev->name); + return 0; +} + +void +ethercycvlink(void) +{ + addethercard("eth", etherpnp); +} diff --git a/sys/src/9/cycv/io.h b/sys/src/9/cycv/io.h index dd0d90019..88be84978 100644 --- a/sys/src/9/cycv/io.h +++ b/sys/src/9/cycv/io.h @@ -2,11 +2,21 @@ #define MPCORE_BASE 0xFFFEC000 #define L2_BASE 0xFFFEF000 #define CLOCKMGR_BASE 0xFFD04000 +#define EMAC1_BASE 0xFF702000 +#define RESETMGR_BASE 0xFFD05000 +#define SYSMGR_BASE 0xFFD08000 +#define OCRAM 0xFFFF0000 + +/*RESETMGR*/ +#define PERMODRST (0x14/4) +/*SYSMGR*/ +#define FPGA_MODULE (0x28/4) #define HPS_CLK 25 #define TIMERIRQ 29 #define UART0IRQ 194 +#define EMAC1IRQ 152 #define LEVEL 0 #define EDGE 1 diff --git a/sys/src/9/cycv/l.s b/sys/src/9/cycv/l.s index 2bbe00a89..a2a1f48d0 100644 --- a/sys/src/9/cycv/l.s +++ b/sys/src/9/cycv/l.s @@ -7,6 +7,12 @@ TEXT _start(SB), $-4 MOVW $(KTZERO-KZERO), R13 MOVW $(UART_BASE), R8 + /* disable watchdog */ + MOVW $(RESETMGR_BASE + 4*PERMODRST), R1 + MOVW (R1), R0 + ORR $(3<<6), R0 + MOVW R0, (R1) + /* disable L2 cache */ MOVW $0, R0 MOVW $(L2_BASE+0x100), R1 @@ -24,10 +30,10 @@ TEXT _start0(SB), $-4 ISB PUTC('l') - /* clean up to KTZERO */ + /* clean up to CONFADDR */ MOVW $0, R0 MOVW R0, R1 - MOVW $(KTZERO-KZERO), R2 + MOVW $(CONFADDR-KZERO), R2 _clrstart: MOVW.P R0, 4(R1) CMP.S R1, R2 diff --git a/sys/src/9/cycv/mmu.c b/sys/src/9/cycv/mmu.c index d7fef9540..8a449e70b 100644 --- a/sys/src/9/cycv/mmu.c +++ b/sys/src/9/cycv/mmu.c @@ -374,3 +374,26 @@ tmpunmap(void *v) coherence(); flushpg(v); } + +void * +ucalloc(ulong len) +{ + static Lock l; + static uchar *free = nil; + uchar *va; + + if(len == 0) + panic("ucalloc: len == 0"); + ilock(&l); + if(free == nil) + free = (uchar*)-BY2PG; + len = PGROUND(len); + free -= len; + if(free < (uchar*)OCRAM) + panic("ucalloc: out of uncached memory"); + va = free; + iunlock(&l); + + invaldse(va, va + len); + return (void *) va; +} |