1 #include "u.h" 2 #include "../port/lib.h" 3 #include "mem.h" 4 #include "dat.h" 5 #include "fns.h" 6 #include "io.h" 7 #include "../port/error.h" 8 9 /* 10 * basic read/write interface to mpc8xx Serial Peripheral Interface; 11 * used by devtouch.c and devspi.c 12 */ 13 14 typedef struct Ctlr Ctlr; 15 16 enum { 17 /* spi-specific BD flags */ 18 BDContin= 1<<9, /* continuous mode */ 19 RxeOV= 1<<1, /* overrun */ 20 TxeUN= 1<<1, /* underflow */ 21 BDme= 1<<0, /* multimaster error */ 22 BDrxerr= RxeOV|BDme, 23 BDtxerr= TxeUN|BDme, 24 25 /* spmod */ 26 MLoop= 1<<14, /* loopback mode */ 27 MClockInv= 1<<13, /* inactive state of SPICLK is high */ 28 MClockPhs= 1<<12, /* SPCLK starts toggling at beginning of transfer */ 29 MDiv16= 1<<11, /* use BRGCLK/16 as input to SPI baud rate */ 30 MRev= 1<<10, /* normal operation */ 31 MMaster= 1<<9, 32 MSlave= 0<<9, 33 MEnable= 1<<8, 34 /* LEN, PS fields */ 35 36 /* spcom */ 37 STR= 1<<7, /* start transmit */ 38 39 /* spie */ 40 MME = 1<<5, 41 TXE = 1<<4, 42 BSY = 1<<2, 43 TXB = 1<<1, 44 RXB = 1<<0, 45 46 /* port B bits */ 47 SPIMISO = IBIT(28), /* master mode input */ 48 SPIMOSI = IBIT(29), /* master mode output */ 49 SPICLK = IBIT(30), 50 51 /* maximum SPI I/O (can change) */ 52 Bufsize = 64, 53 }; 54 55 /* 56 * SPI software structures 57 */ 58 59 struct Ctlr { 60 Lock; 61 QLock io; 62 int init; 63 SPI* spi; 64 IOCparam* sp; 65 66 BD* rd; 67 BD* td; 68 int phase; 69 Rendez r; 70 char* txbuf; 71 char* rxbuf; 72 }; 73 74 static Ctlr spictlr[1]; 75 76 /* dcflush isn't needed if rxbuf and txbuf allocated in uncached IMMR memory */ 77 #define DCFLUSH(a,n) 78 79 static void interrupt(Ureg*, void*); 80 81 /* 82 * called by the reset routine of any driver using the SPI 83 */ 84 void 85 spireset(void) 86 { 87 IMM *io; 88 SPI *spi; 89 IOCparam *sp; 90 CPMdev *cpm; 91 Ctlr *ctlr; 92 93 ctlr = spictlr; 94 if(ctlr->init) 95 return; 96 ctlr->init = 1; 97 cpm = cpmdev(CPspi); 98 spi = cpm->regs; 99 ctlr->spi = spi; 100 sp = cpm->param; 101 if(sp == nil){ 102 print("SPI: can't allocate new parameter memory\n"); 103 return; 104 } 105 ctlr->sp = sp; 106 107 if(ctlr->rxbuf == nil) 108 ctlr->rxbuf = cpmalloc(Bufsize, 2); 109 if(ctlr->txbuf == nil) 110 ctlr->txbuf = cpmalloc(Bufsize, 2); 111 112 if(ctlr->rd == nil){ 113 ctlr->rd = bdalloc(1); 114 ctlr->rd->addr = PADDR(ctlr->rxbuf); 115 ctlr->rd->length = 0; 116 ctlr->rd->status = BDWrap; 117 } 118 if(ctlr->td == nil){ 119 ctlr->td = bdalloc(1); 120 ctlr->td->addr = PADDR(ctlr->txbuf); 121 ctlr->td->length = 0; 122 ctlr->td->status = BDWrap|BDLast; 123 } 124 125 /* select port pins */ 126 io = ioplock(); 127 io->pbdir |= SPICLK | SPIMOSI | SPIMISO; 128 io->pbpar |= SPICLK | SPIMOSI | SPIMISO; 129 iopunlock(); 130 131 /* explicitly initialise parameters, because InitRxTx can't be used (see i2c/spi relocation errata) */ 132 sp = ctlr->sp; 133 sp->rbase = PADDR(ctlr->rd); 134 sp->tbase = PADDR(ctlr->td); 135 sp->rfcr = 0x18; 136 sp->tfcr = 0x18; 137 sp->mrblr = Bufsize; 138 sp->rstate = 0; 139 sp->rptr = 0; 140 sp->rbptr = sp->rbase; 141 sp->rcnt = 0; 142 sp->tstate = 0; 143 sp->tbptr = sp->tbase; 144 sp->tptr = 0; 145 sp->tcnt = 0; 146 eieio(); 147 148 spi->spmode = MDiv16 | MRev | MMaster | ((8-1)<<4) | 1; /* 8 bit characters */ 149 if(0) 150 spi->spmode |= MLoop; /* internal loop back mode for testing */ 151 152 spi->spie = ~0; /* clear events */ 153 eieio(); 154 spi->spim = MME|TXE|BSY|TXB|RXB; 155 eieio(); 156 spi->spmode |= MEnable; 157 158 intrenable(VectorCPIC+cpm->irq, interrupt, spictlr, BUSUNKNOWN, "spi"); 159 160 } 161 162 enum { 163 Idling, 164 Waitval, 165 Readyval 166 }; 167 168 static void 169 interrupt(Ureg*, void *arg) 170 { 171 int events; 172 Ctlr *ctlr; 173 SPI *spi; 174 175 ctlr = arg; 176 spi = ctlr->spi; 177 events = spi->spie; 178 eieio(); 179 spi->spie = events; 180 if(events & (MME|BSY|TXE)){ 181 print("SPI#%x\n", events); 182 if(ctlr->phase != Idling){ 183 ctlr->phase = Idling; 184 wakeup(&ctlr->r); 185 } 186 }else if(events & RXB){ 187 if(ctlr->phase == Waitval){ 188 ctlr->phase = Readyval; 189 wakeup(&ctlr->r); 190 } 191 } 192 } 193 194 static int 195 done(void *a) 196 { 197 return ((Ctlr*)a)->phase != Waitval; 198 } 199 200 /* 201 * send `nout' bytes on SPI from `out' and read as many bytes of reply into buffer `in'; 202 * return the number of bytes received, or -1 on error. 203 */ 204 long 205 spioutin(void *out, long nout, void *in) 206 { 207 Ctlr *ctlr; 208 int nb, p; 209 210 ctlr = spictlr; 211 if(nout > Bufsize) 212 return -1; 213 qlock(&ctlr->io); 214 if(waserror()){ 215 qunlock(&ctlr->io); 216 return -1; 217 } 218 if(ctlr->phase != Idling) 219 sleep(&ctlr->r, done, ctlr); 220 memmove(ctlr->txbuf, out, nout); 221 DCFLUSH(ctlr->txbuf, Bufsize); 222 DCFLUSH(ctlr->rxbuf, Bufsize); 223 ilock(ctlr); 224 ctlr->phase = Waitval; 225 ctlr->td->length = nout; 226 ctlr->rd->status = BDEmpty|BDWrap|BDInt; 227 ctlr->td->status = BDReady|BDWrap|BDLast; 228 eieio(); 229 ctlr->spi->spcom = STR; 230 eieio(); 231 iunlock(ctlr); 232 sleep(&ctlr->r, done, ctlr); 233 nb = ctlr->rd->length; 234 if(nb > nout) 235 nb = nout; /* shouldn't happen */ 236 p = ctlr->phase; 237 poperror(); 238 qunlock(&ctlr->io); 239 if(p != Readyval) 240 return -1; 241 memmove(in, ctlr->rxbuf, nb); 242 return nb; 243 } 244