#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" typedef struct SPIparam SPIparam; struct SPIparam { ushort rbase; ushort tbase; uchar rfcr; uchar tfcr; ushort mrblr; ulong rstate; ulong rptr; ushort rbptr; ushort rcnt; ulong rtmp; ulong tstate; ulong tptr; ushort tbptr; ushort tcnt; ulong ttmp; }; enum { Nrdre = 2, /* receive descriptor ring entries */ Ntdre = 2, /* transmit descriptor ring entries */ Rbsize = 8, /* ring buffer size (+3 for PID+CRC16) */ Bufsize = (Rbsize+7)&~7, /* aligned */ }; enum { /* spi-specific BD flags */ BDContin= 1<<9, /* continuous mode */ BDrxov= 1<<1, /* overrun */ BDtxun= 1<<1, /* underflow */ BDme= 1<<0, /* multimaster error */ BDrxerr= BDrxov|BDme, BDtxerr= BDtxun|BDme, /* spmod */ MLoop= 1<<14, /* loopback mode */ MClockInv= 1<<13, /* inactive state of SPICLK is high */ MClockPhs= 1<<12, /* SPCLK starts toggling at beginning of transfer */ MDiv16= 1<<11, /* use BRGCLK/16 as input to SPI baud rate */ MRev= 1<<10, /* normal operation */ MMaster= 1<<9, MSlave= 0<<9, MEnable= 1<<8, /* LEN, PS fields */ /* spie */ MME = 1<<5, TXE = 1<<4, RES = 1<<3, BSY = 1<<2, TXB = 1<<1, RXB = 1<<0, }; /* * software structures */ typedef struct Ctlr Ctlr; struct Ctlr { QLock; int init; SPI* spi; SPIparam* sp; int rxintr; Rendez ir; Ring; }; static Ctlr spictlr[1]; static void interrupt(Ureg*, void*); /* * test driver for SPI, host mode */ void spireset(void) { IMM *io; SPI *spi; SPIparam *sp; Ctlr *ctlr; io = m->iomem; sp = (SPIparam*)cpmparam(SPIP, sizeof(*sp)); memset(sp, 0, sizeof(*sp)); if(sp == nil){ print("SPI: can't allocate new parameter memory\n"); return; } spi = (SPI*)((ulong)m->iomem+0xAA0); ctlr = spictlr; /* step 1: select port pins */ io->pbdir |= IBIT(30)|IBIT(29)|IBIT(28); io->pbpar |= IBIT(30)|IBIT(29)|IBIT(28); /* SPICLK, SPIMOSI, SPIMISO */ /* step 2: set CS pin - also disables SPISEL */ io->pbpar &= ~(IBIT(31)); io->pbdir |= IBIT(31); io->pbodr &= ~(IBIT(31)); io->pbdat &= ~(IBIT(31)); ctlr->spi = spi; ctlr->sp = sp; if(ioringinit(ctlr, Nrdre, Ntdre, Bufsize)<0) panic("spireset: ioringinit"); /* step 3: configure rbase and tbase */ sp->rbase = PADDR(ctlr->rdr); sp->tbase = PADDR(ctlr->tdr); /* step 4: do not issue InitRxTx command - due to microcode bug */ /* step 5: */ io->sdcr = 1; /* step 6: */ sp->rfcr = 0x10; sp->tfcr = 0x10; /* step 7: */ sp->mrblr = Bufsize; /* step 7.5: init other params because of bug in microcode */ sp->rstate = 0; sp->rbptr = sp->rbase; sp->rcnt = 0; sp->tstate = 0; sp->tbptr = sp->tbase; sp->tcnt = 0; /* step 8-9: done by ioringinit */ /* step 10: clear events */ spi->spie = ~0; /* step 11-12: enable interrupts */ intrenable(VectorCPIC+0x05, interrupt, spictlr, BUSUNKNOWN); spi->spim = ~0; /* step 13: enable in master mode, 8 bit chacters, slow clock */ spi->spmode = MMaster|MRev|MDiv16|MEnable|(0x7<<4)|0xf; } static void interrupt(Ureg*, void *arg) { int events; Ctlr *ctlr; SPI *spi; ctlr = arg; spi = ctlr->spi; events = spi->spie; spi->spie = events; // print("SPI#%x\n", events); } void spdump(SPIparam *sp) { print("rbase = %ux\n", sp->rbase); print("tbase = %ux\n", sp->tbase); print("rfcr = %ux\n", sp->rfcr); print("tfcr = %ux\n", sp->tfcr); print("mrblr = %ux\n", sp->mrblr); print("rstate = %lux\n", sp->rstate); print("rptr = %lux\n", sp->rptr); print("rbptr = %ux\n", sp->rbptr); print("rcnt = %ux\n", sp->rcnt); print("rtmp = %lux\n", sp->rtmp); print("tstate = %lux\n", sp->tstate); print("tptr = %lux\n", sp->tptr); print("tbptr = %ux\n", sp->tbptr); print("tcnt = %ux\n", sp->tcnt); print("ttmp = %lux\n", sp->ttmp); } void spitest(void) { BD *dre; Block *b; int len; Ctlr *ctlr; ulong status; uchar buf[2]; ctlr = spictlr; dre = &ctlr->tdr[ctlr->tdrh]; if(dre->status & BDReady) panic("spitest: txstart"); print("dre->status %ux %ux\n", dre[0].status, dre[1].status); b = allocb(4); b->wp[0] = 0xc0; b->wp[1] = 0x00; b->wp[2] = 0x00; b->wp[3] = 0x00; b->wp += 4; len = BLEN(b); print("len = %d\n", len); dcflush(b->rp, len); if(ctlr->txb[ctlr->tdrh] != nil) panic("scc/ether: txstart"); ctlr->txb[ctlr->tdrh] = b; dre->addr = PADDR(b->rp); print("addr = %lux\n", PADDR(b->rp)); dre->length = len; dre->status = (dre->status & BDWrap) | BDReady|BDInt|BDLast; spdump(ctlr->sp); m->iomem->pbdat |= IBIT(31); microdelay(1); ctlr->spi->spcom = 1<<7; /* transmit now */ print("cpcom = %p\n", &ctlr->spi->spcom); spdump(ctlr->sp); eieio(); ctlr->ntq++; ctlr->tdrh = NEXT(ctlr->tdrh, Ntdre); print("going into loop %ux %ux\n", dre->status, ctlr->spi->spcom); while(dre->status&BDReady) { print("ctrl->spi->spim = %ux %ux\n", ctlr->spi->spie, dre->status); spdump(ctlr->sp); delay(10000); } delay(100); dre = &ctlr->rdr[ctlr->rdrx]; status = dre->status; len = dre->length; print("%d status = %lux len=%d\n", ctlr->rdrx, status, len); b = iallocb(len); memmove(b->wp, KADDR(dre->addr), len); b->wp += len; print("%ux %ux %ux %ux\n", b->rp[0], b->rp[1], b->rp[2], b->rp[3]); /* get the data out of the receiving block the data begins at the 13th bit, and ends at the (13+16)th bit */ buf[0]=b->rp[1]<<4 | b->rp[2]>>4; buf[1]=b->rp[2]<<4 | b->rp[3]>>4; print("the value of address 0 in EEPROM is %ux %ux\n",buf[0],buf[1]); dcflush(KADDR(dre->addr), len); dre->status = (status & BDWrap) | BDEmpty | BDInt; ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre); m->iomem->pbdat &= ~(IBIT(31)); microdelay(1); } void spiinit(void) { spireset(); // spitest(); } void spicmdsend(Block *b, BD *dre, Ctlr *ctlr, int CSdown) { int len; if(dre->status & BDReady) panic("spicmdsend: txstart"); len = BLEN(b); dcflush(b->rp, len); ctlr->txb[ctlr->tdrh] = b; dre->addr = PADDR(b->rp); dre->length = len; dre->status = (dre->status & BDWrap) | BDReady|BDInt|BDLast; m->iomem->pbdat |= IBIT(31); ctlr->spi->spcom = 1<<7; /* transmit now */ //eieio(); ctlr->ntq++; ctlr->tdrh = NEXT(ctlr->tdrh, Ntdre); delay(1); while(dre->status&BDReady) microdelay(10); if(CSdown) m->iomem->pbdat &= ~(IBIT(31)); } void spiread(ulong addr,uchar * buf) { BD *dre; Block *b; int len; Ctlr *ctlr; ushort status; ctlr = spictlr; dre = &ctlr->tdr[ctlr->tdrh]; b = allocb(4); b->wp[0] = 0xc0 | addr>>3; b->wp[1] = addr<<5; b->wp[2] = 0x00; b->wp[3] = 0x00; b->wp += 4; spicmdsend(b, dre, ctlr, 0); //delay(10); dre = &ctlr->rdr[ctlr->rdrx]; status = dre->status; len = dre->length; while(dre->status&BDEmpty) microdelay(10); b = iallocb(len); memmove(b->wp, KADDR(dre->addr), len); b->wp += len; /* get the data out of the receiving block the data begins at the 13th bit, and ends at the (13+16)th bit */ buf[0]=b->rp[1]<<4 | b->rp[2]>>4; buf[1]=b->rp[2]<<4 | b->rp[3]>>4; // print("read out %ux %ux\n", buf[0],buf[1]); dcflush(KADDR(dre->addr), len); dre->status = (status & BDWrap) | BDEmpty | BDInt; ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre); freeb(b); m->iomem->pbdat &= ~(IBIT(31)); microdelay(5); return; } void spienwrite(void) { BD *dre; Block *b; Ctlr *ctlr; int len; ctlr = spictlr; b = allocb(4); b->wp[0] = 0x98; b->wp[1] = 0x00; b->wp[2] = 0x00; b->wp[3] = 0x00; b->wp += 4; dre = &ctlr->tdr[ctlr->tdrh]; spicmdsend(b,dre,ctlr,1); dre=&ctlr->rdr[ctlr->rdrx]; len=dre->length; dcflush(KADDR(dre->addr),len); dre->status=(dre->status&BDWrap)|BDEmpty|BDInt; ctlr->rdrx=NEXT(ctlr->rdrx,Nrdre); freeb(b); microdelay(5); return; } void spidiswrite(void) { BD *dre; Block *b; Ctlr *ctlr; int len; ctlr = spictlr; dre = &ctlr->tdr[ctlr->tdrh]; b = allocb(4); b->wp[0] = 0x80; b->wp[1] = 0x00; b->wp[2] = 0x00; b->wp[3] = 0x00; b->wp += 4; spicmdsend(b,dre,ctlr,1); dre=&ctlr->rdr[ctlr->rdrx]; len=dre->length; dcflush(KADDR(dre->addr),len); dre->status=(dre->status&BDWrap)|BDEmpty|BDInt; ctlr->rdrx=NEXT(ctlr->rdrx,Nrdre); freeb(b); microdelay(5); return; } void spiwrite(ulong addr, uchar *buf) { BD *dre; Block *b; Ctlr *ctlr; int len; ctlr = spictlr; dre = &ctlr->tdr[ctlr->tdrh]; b = allocb(4); b->wp[0] = 0x05 ; b->wp[1] = 0x7f & addr; b->wp+=2; memmove((uchar*)b->wp,buf,2); b->wp += 2; spicmdsend(b,dre,ctlr,1); dre=&ctlr->rdr[ctlr->rdrx]; len=dre->length; dcflush(KADDR(dre->addr),len); dre->status=(dre->status&BDWrap)|BDEmpty|BDInt; ctlr->rdrx=NEXT(ctlr->rdrx,Nrdre); freeb(b); delay(15); //sleep(15); //sleep for 15ms return; } void spiwriteall(uchar *buf) { BD *dre; Block *b; Ctlr *ctlr; int len; ctlr = spictlr; dre = &ctlr->tdr[ctlr->tdrh]; b = allocb(4); b->wp[0] = 0x04; b->wp[1] = 0x40; b->wp+=2; memmove((uchar*)b->wp,buf,2); //b->wp[2]=0xf0; //b->wp[3]=0xf0; b->wp += 2; spicmdsend(b,dre,ctlr,1); dre=&ctlr->rdr[ctlr->rdrx]; len=dre->length; dcflush(KADDR(dre->addr),len); dre->status=(dre->status&BDWrap)|BDEmpty|BDInt; ctlr->rdrx=NEXT(ctlr->rdrx,Nrdre); freeb(b); delay(15); //sleep(15000); //sleep for 15ms print("leave spiwriteall\n"); return; } void spierase(ulong addr) { BD *dre; Block *b; Ctlr *ctlr; int len; ctlr = spictlr; b = allocb(2); b->wp[0] = 0x07; b->wp[1] = addr&0x7f; b->wp+=2; dre = &ctlr->tdr[ctlr->tdrh]; spicmdsend(b,dre,ctlr,1); dre=&ctlr->rdr[ctlr->rdrx]; len=dre->length; dcflush(KADDR(dre->addr),len); dre->status=(dre->status&BDWrap)|BDEmpty|BDInt; ctlr->rdrx=NEXT(ctlr->rdrx,Nrdre); freeb(b); delay(15); return; } void spieraseall(void) { BD *dre; Block *b; Ctlr *ctlr; int len; ctlr = spictlr; b = allocb(2); b->wp[0] = 0x07; b->wp[1] = 0x80; b->wp+=2; dre = &ctlr->tdr[ctlr->tdrh]; spicmdsend(b,dre,ctlr,1); dre=&ctlr->rdr[ctlr->rdrx]; len=dre->length; dcflush(KADDR(dre->addr),len); dre->status=(dre->status&BDWrap)|BDEmpty|BDInt; ctlr->rdrx=NEXT(ctlr->rdrx,Nrdre); freeb(b); delay(15); return; }