diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2022-05-08 16:50:29 +0000 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2022-05-08 16:50:29 +0000 |
commit | fff070f2cbb01b7c0879e9dcb13ee4e3ed2497f0 (patch) | |
tree | 44899a9ae6e8143740a4b02e7d50a3b6db768008 /sys/src/9/imx8/etherimx.c | |
parent | 9126ee3eea90d639f4e877c01400248581d10f65 (diff) |
imx8: add work in progress i.MX8MQ kernel for the mntreform2 laptop
This is a work in progress port to the mntreform2 laptop.
Working so far:
- mmu (same as raspberry pi 3b+)
- arm generic timer
- gicv3
- uart1
- enet
With access to the uart, one can netboot this kernel in u-boot
using the following commands:
> dhcp
> bootm
Diffstat (limited to 'sys/src/9/imx8/etherimx.c')
-rw-r--r-- | sys/src/9/imx8/etherimx.c | 706 |
1 files changed, 706 insertions, 0 deletions
diff --git a/sys/src/9/imx8/etherimx.c b/sys/src/9/imx8/etherimx.c new file mode 100644 index 000000000..7a78c4c9d --- /dev/null +++ b/sys/src/9/imx8/etherimx.c @@ -0,0 +1,706 @@ +#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" +#include "../port/ethermii.h" + +enum { + Moduleclk = 125000000, /* 125Mhz */ + Maxtu = 1518, + + R_BUF_SIZE = ((Maxtu+BLOCKALIGN-1)&~BLOCKALIGN), +}; + +enum { + ENET_EIR = 0x004/4, /* Interrupt Event Register */ + ENET_EIMR = 0x008/4, /* Interrupt Mask Register */ + INT_BABR =1<<30, /* Babbling Receive Error */ + INT_BABT =1<<31, /* Babbling Transmit Error */ + INT_GRA =1<<28, /* Graceful Stop Complete */ + INT_TXF =1<<27, /* Transmit Frame Interrupt */ + INT_TXB =1<<26, /* Transmit Buffer Interrupt */ + INT_RXF =1<<25, /* Receive Frame Interrupt */ + INT_RXB =1<<24, /* Receive Buffer Interrupt */ + INT_MII =1<<23, /* MII Interrupt */ + INT_EBERR =1<<22, /* Ethernet Bus Error */ + INT_LC =1<<21, /* Late Collision */ + INT_RL =1<<20, /* Collision Retry Limit */ + INT_UN =1<<19, /* Transmit FIFO Underrun */ + INT_PLR =1<<18, /* Payload Receive Error */ + INT_WAKEUP =1<<17, /* Node Wakeup Request Indication */ + INT_TS_AVAIL =1<<16, /* Transmit Timestamp Available */ + INT_TS_TIMER =1<<15, /* Timestamp Timer */ + INT_RXFLUSH_2 =1<<14, /* RX DMA Ring 2 flush indication */ + INT_RXFLUSH_1 =1<<13, /* RX DMA Ring 1 flush indication */ + INT_RXFLUSH_0 =1<<12, /* RX DMA Ring 0 flush indication */ + INT_TXF2 =1<<7, /* Transmit frame interrupt, class 2 */ + INT_TXB2 =1<<6, /* Transmit buffer interrupt, class 2 */ + INT_RXF2 =1<<5, /* Receive frame interrupt, class 2 */ + INT_RXB2 =1<<4, /* Receive buffer interrupt, class 2 */ + INT_TXF1 =1<<3, /* Transmit frame interrupt, class 1 */ + INT_TXB1 =1<<2, /* Transmit buffer interrupt, class 1 */ + INT_RXF1 =1<<1, /* Receive frame interrupt, class 1 */ + INT_RXB1 =1<<0, /* Receive buffer interrupt, class 1 */ + + ENET_RDAR = 0x010/4, /* Receive Descriptor Active Register */ + RDAR_ACTIVE =1<<24, /* Descriptor Active */ + ENET_TDAR = 0x014/4, /* Transmit Descriptor Active Register */ + TDAR_ACTIVE =1<<24, /* Descriptor Active */ + + ENET_ECR = 0x024/4, /* Ethernet Control Register */ + ECR_RESERVED =7<<28, + ECR_SVLANDBL =1<<11, /* S-VLAN double tag */ + ECR_VLANUSE2ND =1<<10, /* VLAN use second tag */ + ECR_SVLANEN =1<<9, /* S-VLAN enable */ + ECR_DBSWP =1<<8, /* Descriptor Byte Swapping Enable */ + ECR_DBGEN =1<<6, /* Debug Enable */ + ECR_SPEED_100M =0<<5, + ECR_SPEED_1000M =1<<5, + ECR_EN1588 =1<<4, /* Enables enhanced functionality of the MAC */ + ECR_SLEEP =1<<3, /* Sleep Mode Enable */ + ECR_MAGICEN =1<<2, /* Magic Packet Detection Enable */ + ECR_ETHEREN =1<<1, /* Ethernet Enable */ + ECR_RESET =1<<0, /* Ethernet MAC Reset */ + + ENET_MMFR = 0x040/4, /* MII Management Frame Register */ + MMFR_ST =1<<30, + MMFR_RD =2<<28, + MMFR_WR =1<<28, + MMFR_PA_SHIFT =23, + MMFR_TA =2<<16, + MMFR_RA_SHIFT =18, + + ENET_MSCR = 0x044/4, /* MII Speed Control Register */ + MSCR_SPEED_SHIFT=1, /* MII speed = module_clock/((SPEED+1)*2) */ + MSCR_DIS_PRE =1<<7, /* disable preamble */ + MSCR_HOLD_SHIFT =8, /* hold cycles in module_clock */ + + ENET_MIBC = 0x064/4, /* MIB Control Register */ + ENET_RCR = 0x084/4, /* Receive Control Register */ + RCR_GRS =1<<31, /* Gracefull Receive Stopped */ + RCR_NLC =1<<30, /* Payload Length Check Disable */ + RCR_MAX_FL_SHIFT=16, /* Maximum Frame Length */ + RCR_CFEN =1<<15, /* MAC Control Frame Enable */ + RCR_CRCFWD =1<<14, /* Forward Received CRC */ + RCR_PAUFWD =1<<13, /* Forward Pause Frames */ + RCR_PADEN =1<<12, /* Enable Frame Padding Remove */ + RCR_RMII_10T =1<<9, /* Enables 10-Mbit/s mode of the RMII/RGMII */ + RCR_RMII_MODE =1<<8, /* RMII Mode Enable */ + RCR_RGMII_EN =1<<6, /* RGMII Mode Enable */ + RCR_FCE =1<<5, /* Flow Control Enable */ + RCR_REJ =1<<4, /* Broadcast Frame Reject */ + RCR_PROM =1<<3, /* Promiscuous Mode */ + RCR_MII_MODE =1<<2, /* Media Independent Interface Mode (must always be set) */ + RCR_DRT =1<<1, /* Disable Receive On Timeout */ + RCR_LOOP =1<<0, /* Internal Loopback */ + + ENET_TCR = 0x0C4/4, /* Transmit Control Register */ + TCR_CRCFWD =1<<9, /* Foward Frame From Application With CRC */ + TCR_ADDINS =1<<8, /* Set MAC Address on Transmit */ + TCR_RFC_PAUSE =1<<4, /* Receive Frame Control Pause */ + TCR_TFC_PAUSE =1<<3, /* Transmit Frame Control Pause */ + TCR_FDEN =1<<2, /* Full-Duplex Enable */ + TCR_GTS =1<<0, /* Graceful Transmit Stop */ + + ENET_PALR = 0x0E4/4, /* Physical Address Lower Register */ + ENET_PAUR = 0x0E8/4, /* Physical Address Upper Register */ + + ENET_OPD = 0x0EC/4, /* Opcode/Pause Duration Register */ + + ENET_TXIC0 = 0x0F0/4, /* Transmit Interrupt Coalescing Register */ + ENET_TXIC1 = 0x0F4/4, /* Transmit Interrupt Coalescing Register */ + ENET_TXIC2 = 0x0F8/4, /* Transmit Interrupt Coalescing Register */ + ENET_RXIC0 = 0x100/4, /* Receive Interrupt Coalescing Register */ + ENET_RXIC1 = 0x104/4, /* Receive Interrupt Coalescing Register */ + ENET_RXIC2 = 0x108/4, /* Receive Interrupt Coalescing Register */ + IC_EN = 1<<31, + IC_CS = 1<<30, + IC_FT_SHIFT = 20, + IC_TT_SHIFT = 0, + + ENET_IAUR = 0x118/4, /* Descriptor Individual Upper Address Register */ + ENET_IALR = 0x11C/4, /* Descriptor Individual Lower Address Register */ + ENET_GAUR = 0x120/4, /* Descriptor Group Upper Address Register */ + ENET_GALR = 0x124/4, /* Descriptor Group Lower Address Register */ + ENET_TFWR = 0x144/4, /* Transmit FIFO Watermark Register */ + TFWR_STRFWD = 1<<8, + + ENET_RDSR1 = 0x160/4, /* Receive Descriptor Ring 1 Start Register */ + ENET_TDSR1 = 0x164/4, /* Transmit Buffer Descriptor Ring 1 Start Register */ + ENET_MRBR1 = 0x168/4, /* Maximum Receive Buffer Size Register Ring 1 */ + + ENET_RDSR2 = 0x16C/4, /* Receive Descriptor Ring 2 Start Register */ + ENET_TDSR2 = 0x170/4, /* Transmit Buffer Descriptor Ring 2 Start Register */ + ENET_MRBR2 = 0x174/4, /* Maximum Receive Buffer Size Register Ring 2 */ + + ENET_RDSR = 0x180/4, /* Receive Descriptor Ring 0 Start Register */ + ENET_TDSR = 0x184/4, /* Transmit Buffer Descriptor Ring 0 Start Register */ + ENET_MRBR = 0x188/4, /* Maximum Receive Buffer Size Register Ring 0 */ + + ENET_RSFL = 0x190/4, /* Receive FIFO Section Full Threshold */ + ENET_RSEM = 0x194/4, /* Receive FIFO Section Empty Threshold */ + ENET_RAEM = 0x198/4, /* Receive FIFO Almost Empty Threshold */ + ENET_RAFL = 0x19C/4, /* Receive FIFO Almost Full Threshold */ + + ENET_TSEM = 0x1A0/4, /* Transmit FIFO Section Empty Threshold */ + ENET_TAEM = 0x1A4/4, /* Transmit FIFO Almost Empty Threshold */ + ENET_TAFL = 0x1A8/4, /* Transmit FIFO Almost Full Threshold */ + + ENET_TIPG = 0x1AC/4, /* Transmit Inter-Packet Gap */ + ENET_FTRL = 0x1B0/4, /* Frame Truncation Length */ + ENET_TACC = 0x1C0/4, /* Transmit Accelerator Function Configuration */ + ENET_RACC = 0x1C4/4, /* Receive Accelerator Function Configuration */ + + ENET_RCMR1 = 0x1C8/4, /* Receive Classification Match Register */ + ENET_RCMR2 = 0x1CC/4, /* Receive Classification Match Register */ + + ENET_DMA1CFG = 0x1D8/4, /* DMA Class Based Configuration */ + ENET_DMA2CFG = 0x1DC/4, /* DMA Class Based Configuration */ + + ENET_RDAR1 = 0x1E0/4, /* Receive Descriptor Active Register - Ring 1 */ + ENET_TDAR1 = 0x1E4/4, /* Transmit Descriptor Active Register - Ring 1 */ + ENET_RDAR2 = 0x1E8/4, /* Receive Descriptor Active Register - Ring 2 */ + ENET_TDAR2 = 0x1EC/4, /* Transmit Descriptor Active Register - Ring 2 */ + + ENET_QOS = 0x1F0/4, /* QOS Scheme */ +}; + +enum { + /* transmit descriptor status bits */ + TD_R = 1<<(15+16), /* Ready */ + TD_OWN = 1<<(14+16), /* Ownership */ + TD_W = 1<<(13+16), /* Wrap */ + TD_L = 1<<(11+16), /* Last in a frame */ + + TD_TC = 1<<(10+16), /* Transmit CRC */ + TD_ERR = TD_TC, + + TD_LEN = 0xFFFF, + + /* receive desctriptor status bits */ + RD_E = 1<<(15+16), /* Empty */ + RD_W = 1<<(13+16), /* Wrap */ + RD_L = 1<<(11+16), /* Last in a frame */ + + RD_M = 1<<(8+16), /* Miss */ + RD_BC = 1<<(7+16), /* broadcast */ + RD_MC = 1<<(6+16), /* multicast */ + + RD_LG = 1<<(5+16), /* length violation */ + RD_NO = 1<<(4+16), /* non octet aligned frame */ + RD_CR = 1<<(2+16), /* crc error */ + RD_OV = 1<<(1+16), /* overrun */ + RD_TR = 1<<(0+16), /* truncated */ + RD_ERR = RD_LG | RD_NO | RD_CR | RD_OV | RD_TR, + + RD_LEN = 0xFFFF, +}; + +typedef struct Descr Descr; +struct Descr +{ + u32int status; + u32int addr; +}; + +typedef struct Ctlr Ctlr; +struct Ctlr +{ + u32int *regs; + u32int intmask; + + struct { + Block *b[256]; + Descr *d; + Rendez; + } rx[1]; + + struct { + Block *b[256]; + Descr *d; + Rendez; + } tx[1]; + + struct { + Rendez; + } free[1]; + + struct { + Mii; + Rendez; + } mii[1]; + + int attached; + QLock; +}; + +#define rr(c, r) ((c)->regs[r]) +#define wr(c, r, v) ((c)->regs[r] = (v)) + +static int +mdiodone(void *arg) +{ + Ctlr *ctlr = arg; + return rr(ctlr, ENET_EIR) & INT_MII; +} +static int +mdiowait(Ctlr *ctlr) +{ + int i; + + for(i = 0; i < 200; i++){ + tsleep(ctlr->mii, mdiodone, ctlr, 5); + if(mdiodone(ctlr)) + return 0; + } + return -1; +} +static int +mdiow(Mii* mii, int phy, int addr, int data) +{ + Ctlr *ctlr = mii->ctlr; + + data &= 0xFFFF; + wr(ctlr, ENET_EIR, INT_MII); + wr(ctlr, ENET_MMFR, MMFR_WR | MMFR_ST | MMFR_TA | phy<<MMFR_PA_SHIFT | addr<<MMFR_RA_SHIFT | data); + if(mdiowait(ctlr) < 0) return -1; + return data; +} +static int +mdior(Mii* mii, int phy, int addr) +{ + Ctlr *ctlr = mii->ctlr; + + wr(ctlr, ENET_EIR, INT_MII); + wr(ctlr, ENET_MMFR, MMFR_RD | MMFR_ST | MMFR_TA | phy<<MMFR_PA_SHIFT | addr<<MMFR_RA_SHIFT); + if(mdiowait(ctlr) < 0) return -1; + return rr(ctlr, ENET_MMFR) & 0xFFFF; +} + +static void +interrupt(Ureg*, void *arg) +{ + Ether *edev = arg; + Ctlr *ctlr = edev->ctlr; + u32int e; + + e = rr(ctlr, ENET_EIR); + wr(ctlr, ENET_EIR, e); + + if(e & INT_RXF) wakeup(ctlr->rx); + if(e & INT_TXF) wakeup(ctlr->tx); + if(e & INT_MII) wakeup(ctlr->mii); +} + +static void +shutdown(Ether *edev) +{ + Ctlr *ctlr = edev->ctlr; + coherence(); + + wr(ctlr, ENET_ECR, ECR_RESERVED | ECR_RESET); + while(rr(ctlr, ENET_ECR) & ECR_RESET) delay(1); + + /* mask and clear interrupt events */ + wr(ctlr, ENET_EIMR, 0); + wr(ctlr, ENET_EIR, ~0); +} + +static int +tdfree(void *arg) +{ + Descr *d = arg; + return (d->status & (TD_OWN|TD_R)) == 0; +} + +static void +txproc(void *arg) +{ + Ether *edev = arg; + Ctlr *ctlr = edev->ctlr; + Block *b; + Descr *d; + uint i = 0; + + while(waserror()) + ; + + for(;;){ + if((b = qbread(edev->oq, 100000)) == nil) + break; + + d = &ctlr->tx->d[i]; + while(!tdfree(d)) + sleep(ctlr->free, tdfree, d); + + ctlr->tx->b[i] = b; + + dmaflush(1, b->rp, BLEN(b)); + d->addr = PADDR(b->rp); + coherence(); + if(i == nelem(ctlr->tx->b)-1){ + d->status = BLEN(b) | TD_OWN | TD_R | TD_L | TD_TC | TD_W; + i = 0; + } else { + d->status = BLEN(b) | TD_OWN | TD_R | TD_L | TD_TC; + i++; + } + wr(ctlr, ENET_TDAR, TDAR_ACTIVE); + } +} + +static int +tddone(void *arg) +{ + Descr *d = arg; + return (d->status & (TD_OWN|TD_R)) == TD_OWN; +} + +static void +frproc(void *arg) +{ + Ether *edev = arg; + Ctlr *ctlr = edev->ctlr; + Block *b; + Descr *d; + uint i = 0; + + while(waserror()) + ; + + for(;;){ + d = &ctlr->tx->d[i]; + while(!tddone(d)) + sleep(ctlr->tx, tddone, d); + + b = ctlr->tx->b[i]; + ctlr->tx->b[i] = nil; + coherence(); + + if(i == nelem(ctlr->tx->b)-1){ + d->status = TD_W; + i = 0; + } else { + d->status = 0; + i++; + } + + wakeup(ctlr->free); + freeb(b); + } +} + +static int +rdfull(void *arg) +{ + Descr *d = arg; + return (d->status & RD_E) == 0; +} + +static void +rxproc(void *arg) +{ + Ether *edev = arg; + Ctlr *ctlr = edev->ctlr; + Block *b; + Descr *d; + uint s, i = 0; + + while(waserror()) + ; + + for(;;){ + d = &ctlr->rx->d[i]; + s = d->status; + if(s & RD_E){ + sleep(ctlr->rx, rdfull, d); + continue; + } + if(((s^RD_L) & (RD_L|RD_ERR)) == 0){ + b = ctlr->rx->b[i]; + b->wp = b->rp + (s & RD_LEN); + dmaflush(0, b->rp, BLEN(b)); + etheriq(edev, b); + + /* replenish */ + b = allocb(R_BUF_SIZE); + ctlr->rx->b[i] = b; + dmaflush(1, b->rp, R_BUF_SIZE); + d->addr = PADDR(b->rp); + coherence(); + } + if(i == nelem(ctlr->rx->b)-1) { + d->status = RD_E | RD_W; + i = 0; + } else { + d->status = RD_E; + i++; + } + wr(ctlr, ENET_RDAR, RDAR_ACTIVE); + } +} + +static void +linkproc(void *arg) +{ + Ether *edev = arg; + Ctlr *ctlr = edev->ctlr; + MiiPhy *phy; + int link = -1; + + while(waserror()) + ; + + miiane(ctlr->mii, ~0, AnaAP|AnaP, ~0); + + for(;;){ + miistatus(ctlr->mii); + phy = ctlr->mii->curphy; + if(phy->link == link){ + tsleep(ctlr->mii, return0, nil, 5000); + continue; + } + link = phy->link; + if(link){ + u32int ecr = rr(ctlr, ENET_ECR) & ~ECR_SPEED_1000M; + u32int rcr = rr(ctlr, ENET_RCR) & ~(RCR_RMII_10T|RCR_FCE); + u32int tcr = rr(ctlr, ENET_TCR) & ~(TCR_RFC_PAUSE|TCR_TFC_PAUSE|TCR_FDEN); + + switch(phy->speed){ + case 1000: + ecr |= ECR_SPEED_1000M; + rcr |= RCR_FCE; + + /* receive fifo thresholds */ + wr(ctlr, ENET_RSFL, 16); + wr(ctlr, ENET_RSEM, 132); + wr(ctlr, ENET_RAEM, 8); + wr(ctlr, ENET_RAFL, 8); + + /* opcode/pause duration */ + wr(ctlr, ENET_OPD, 0xFFF0); + break; + case 100: + ecr |= ECR_SPEED_100M; + break; + case 10: + rcr |= RCR_RMII_10T; + break; + } + if(phy->fd) + tcr |= TCR_FDEN; + if(phy->rfc) + tcr |= TCR_RFC_PAUSE; + if(phy->tfc) + tcr |= TCR_TFC_PAUSE; + + wr(ctlr, ENET_ECR, ecr); + wr(ctlr, ENET_RCR, rcr); + wr(ctlr, ENET_TCR, tcr); + + edev->mbps = phy->speed; + + wr(ctlr, ENET_RDAR, RDAR_ACTIVE); + } + edev->link = link; + print("#l%d: link %d speed %d\n", edev->ctlrno, edev->link, edev->mbps); + } +} + +static void +attach(Ether *edev) +{ + Ctlr *ctlr = edev->ctlr; + Descr *d; + int i; + + eqlock(ctlr); + if(ctlr->attached){ + qunlock(ctlr); + return; + } + if(waserror()){ + qunlock(ctlr); + nexterror(); + } + + /* RGMII mode, max frame length */ + wr(ctlr, ENET_RCR, RCR_MII_MODE | RCR_RGMII_EN | Maxtu<<RCR_MAX_FL_SHIFT); + + /* set MII clock to 2.5Mhz, 10ns hold time */ + wr(ctlr, ENET_MSCR, ((Moduleclk/(2*2500000))-1)<<MSCR_SPEED_SHIFT | ((Moduleclk/10000000)-1)<<MSCR_HOLD_SHIFT); + + ctlr->intmask |= INT_MII; + wr(ctlr, ENET_EIMR, ctlr->intmask); + mii(ctlr->mii, ~0); + + if(ctlr->mii->curphy == nil) + error("no phy"); + + print("#l%d: phy%d id %.8ux oui %x\n", + edev->ctlrno, ctlr->mii->curphy->phyno, + ctlr->mii->curphy->id, ctlr->mii->curphy->oui); + + /* clear mac filter hash table */ + wr(ctlr, ENET_IALR, 0); + wr(ctlr, ENET_IAUR, 0); + wr(ctlr, ENET_GALR, 0); + wr(ctlr, ENET_GAUR, 0); + + /* set MAC address */ + wr(ctlr, ENET_PALR, (u32int)edev->ea[0]<<24 | (u32int)edev->ea[1]<<16 | (u32int)edev->ea[2]<<8 | edev->ea[3]<<0); + wr(ctlr, ENET_PAUR, (u32int)edev->ea[4]<<24 | (u32int)edev->ea[5]<<16); + + if(ctlr->rx->d == nil) + ctlr->rx->d = ucalloc(sizeof(Descr) * nelem(ctlr->rx->b)); + for(i=0; i<nelem(ctlr->rx->b); i++){ + Block *b = allocb(R_BUF_SIZE); + ctlr->rx->b[i] = b; + d = &ctlr->rx->d[i]; + dmaflush(1, b->rp, R_BUF_SIZE); + d->addr = PADDR(b->rp); + d->status = RD_E; + } + ctlr->rx->d[nelem(ctlr->rx->b)-1].status = RD_E | RD_W; + wr(ctlr, ENET_MRBR, R_BUF_SIZE); + coherence(); + wr(ctlr, ENET_RDSR, PADDR(ctlr->rx->d)); + + if(ctlr->tx->d == nil) + ctlr->tx->d = ucalloc(sizeof(Descr) * nelem(ctlr->tx->b)); + for(i=0; i<nelem(ctlr->tx->b); i++){ + ctlr->tx->b[i] = nil; + d = &ctlr->tx->d[i]; + d->addr = 0; + d->status = 0; + } + ctlr->tx->d[nelem(ctlr->tx->b)-1].status = TD_W; + coherence(); + wr(ctlr, ENET_TDSR, PADDR(ctlr->tx->d)); + + /* store and forward tx fifo */ + wr(ctlr, ENET_TFWR, TFWR_STRFWD); + + /* interrupt coalescing: 200 pkts, 1000 µs */ + wr(ctlr, ENET_RXIC0, IC_EN | 200<<IC_FT_SHIFT | ((1000*Moduleclk)/64000000)<<IC_TT_SHIFT); + wr(ctlr, ENET_TXIC0, IC_EN | 200<<IC_FT_SHIFT | ((1000*Moduleclk)/64000000)<<IC_TT_SHIFT); + + ctlr->intmask |= INT_TXF | INT_RXF; + wr(ctlr, ENET_EIMR, ctlr->intmask); + + /* enable ethernet */ + wr(ctlr, ENET_ECR, rr(ctlr, ENET_ECR) | ECR_ETHEREN | ECR_DBSWP); + + ctlr->attached = 1; + + kproc("ether-rx", rxproc, edev); + kproc("ether-tx", txproc, edev); + kproc("ether-fr", frproc, edev); + + kproc("ether-link", linkproc, edev); + + qunlock(ctlr); + poperror(); +} + +static void +prom(void *arg, int on) +{ + Ether *edev = arg; + Ctlr *ctlr = edev->ctlr; + + if(on) + wr(ctlr, ENET_RCR, rr(ctlr, ENET_RCR) | RCR_PROM); + else + wr(ctlr, ENET_RCR, rr(ctlr, ENET_RCR) & ~RCR_PROM); +} + +static void +multi(void *arg, uchar*, int) +{ + Ether *edev = arg; + Ctlr *ctlr = edev->ctlr; + Netaddr *a; + u64int hash; + + hash = 0; + for(a = edev->maddr; a != nil; a = a->next) + hash |= 1ULL << ((ethercrc(a->addr, edev->alen) >> (32 - 6)) & 0x3F); + + wr(ctlr, ENET_GALR, hash & 0xFFFFFFFF); + wr(ctlr, ENET_GAUR, hash >> 32); +} + +static long +ctl(Ether*, void*, long len) +{ + return len; +} + +static int +reset(Ether *edev) +{ + Ctlr *ctlr = edev->ctlr; + u32int paddr1, paddr2; + + /* steal mac address from uboot */ + paddr1 = rr(ctlr, ENET_PALR); + paddr2 = rr(ctlr, ENET_PAUR); + edev->ea[0] = paddr1>>24; + edev->ea[1] = paddr1>>16; + edev->ea[2] = paddr1>>8; + edev->ea[3] = paddr1>>0; + edev->ea[4] = paddr2>>24; + edev->ea[5] = paddr2>>16; + + shutdown(edev); + + return 0; +} + +static int +pnp(Ether *edev) +{ + static Ctlr ctlr[1]; + + if(ctlr->regs != nil) + return -1; + + ctlr->regs = (u32int*)(VIRTIO + 0xbe0000); + + ctlr->mii->ctlr = ctlr; + ctlr->mii->mir = mdior; + ctlr->mii->miw = mdiow; + + edev->port = (uintptr)ctlr->regs - KZERO; + edev->irq = IRQenet1; + edev->ctlr = ctlr; + edev->attach = attach; + edev->shutdown = shutdown; + edev->promiscuous = prom; + edev->multicast = multi; + edev->ctl = ctl; + edev->arg = edev; + edev->mbps = 1000; + edev->maxmtu = Maxtu; + + if(reset(edev) < 0) + return -1; + + intrenable(edev->irq+0, interrupt, edev, BUSUNKNOWN, edev->name); + intrenable(edev->irq+1, interrupt, edev, BUSUNKNOWN, edev->name); + intrenable(edev->irq+2, interrupt, edev, BUSUNKNOWN, edev->name); + intrenable(edev->irq+3, interrupt, edev, BUSUNKNOWN, edev->name); + + return 0; +} + +void +etherimxlink(void) +{ + addethercard("imx", pnp); +} |