// OctalUART.cc /********************COPYRIGHT NOTIFICATION********************************** This software was developed under a United States Government license described on the COPYRIGHT_UniversityOfChicago file included as part of this distribution. ****************************************************************************/ /* Current Author: Marty Kraimer Original Author: Jim Kowalkowski (for Hideos) Date: 09JUL98 */ #include #include #include #include #include #include #include #include #include #include #include "WatchDog.h" #include "Reboot.h" #include "mpfType.h" #include "DLList.h" #include "IndustryPackModule.h" #include "SerialPort.h" #define GREEN_SPRING_ID 0xf0 #define GSIP_QUAD_SERIAL 0x37 #define GSIP_OCTAL232 0x22 #define GSIP_OCTAL422 0x2a #define GSIP_OCTAL485 0x48 // following necessary to delay ip accesses; OK for mv172 #define IP_DELAY_LOOPS 1 //Define following outside class so name munging not necessary static DLList *pOctalUARTList = 0; // (R)=read access, (W)=write access (R/W) read/write access // This structure can be used to index to each port whent the SCC is set // up in the Quad or Octal configuation. struct scc2698_chan { union { struct { unsigned short mr; // a mode register 1/2 (R/W) unsigned short sr; // a status (R), a clock select (W) unsigned short r1; // a command (W) unsigned short rhr; // a receiver hold (R), a transmitter hold (W) unsigned short junk[4];// other crap for block control } r; struct { unsigned short mr; // a mode register 1/2 (R/W) unsigned short csr; // a status (R), a clock select (W) unsigned short cr; // a command (W) unsigned short thr; // a receiver hold (R), a transmitter hold (W) unsigned short junk[4];// other crap for block control } w; } u; }; typedef volatile struct scc2698_chan SCC2698_CHAN; // This is the entire structure of the SCC. Note that there are really // only four control blocks, each containing two ports. struct scc2698 { union { struct { unsigned short mra; // a mode register 1/2 (R/W) unsigned short sra; // a status (R), a clock select (W) unsigned short r1; // reserved unsigned short rhra;// a receiver hold (R), a transmitter hold (W) unsigned short ipcr;// a Aux cntl (W), a input port change (R) unsigned short isr; // a Interrupt status (R), a Interrupt mask (W) unsigned short ctur;// a Counter timer upper (R/W) unsigned short ctlr;// a Counter timer upper (R/W) unsigned short mrb; // b mode register 1/2 (R/W) unsigned short srb; // b status (R), b clock select (W) unsigned short r2; // reserved unsigned short rhrb;// b receiver hold (R), b transmitter hold (W) unsigned short r3; // reserved unsigned short ip; // a output port conf (W), a input port (R) unsigned short ctg; // start counter timer a (R) unsigned short cts; // stop counter timer a (R) } r; struct { unsigned short mra; // a mode register 1/2 (R/W) unsigned short csra;// a status (R), a clock select (W) unsigned short cra; // a command (W) unsigned short thra;// a receiver hold (R), a transmitter hold (W) unsigned short acr; // a Aux cntl (W), a input port change (R) unsigned short imr; // a Interrupt status (R), a Interrupt mask (W) unsigned short ctu; // a Counter timer upper (R/W) unsigned short ctl; // a Counter timer upper (R/W) unsigned short mrb; // b mode register 1/2 (R/W) unsigned short csrb;// b status (R), b clock select (W) unsigned short crb; // b command (W) unsigned short thrb;// b receiver hold (R), b receiver hold (W) unsigned short r3; // reserved unsigned short opcr;// a output port conf (W), a input port (R) unsigned short r4; // start counter timer a (R) unsigned short r5; // start counter timer a (R) } w; } u; }; typedef volatile struct scc2698 SCC2698; class OctalUARTPort; class OctalUART { public: OctalUART(const char *moduleName, IndustryPackModule *pIndustryPackModule, int nports, int intVec); static OctalUART *findOctalUART(const char *moduleName); OctalUARTPort *findOctalUARTPort(int port); private: static void intFunc(void*); // slow down access to control registers by calling subroutine static void writeIpReg(volatile unsigned short& dest, unsigned short val); static unsigned short readIpReg(volatile unsigned short& src); static void rebootCallback(void *); IndustryPackModule *pIPM; const char *name; int nports; // number of ports (4 or 8) int intVec; SCC2698* regs; // list of regs, one for each port OctalUARTPort** ports; // list of ports, one per port unsigned short imr[4]; // one per block friend class OctalUARTPort; }; enum RSmode { RS485,RS232 }; enum TYPE_SIZE { QUAD, OCTAL }; class OctalUARTPort : public SerialPort { public: OctalUARTPort(int port,int block,OctalUART *pOctalUART); virtual serialPortStatus write( unsigned char *data, int nbytes, int timeout); virtual serialPortStatus read(int timeout); virtual serialPortStatus writeRead( unsigned char *data, int nbytes, int timeout); virtual bool config( int baud, int stopBits, int bitsPerChar, char parity, char flowControl); void setModeRS485(); void setModeRS232(); private: unsigned char *outputBuffer; int outputLength; int outputPosition; int port,block; OctalUART* pOctalUART; RSmode mode; SCC2698* regs; SCC2698_CHAN* chan; unsigned short opcr,mr1,mr2,imr; TYPE_SIZE ip_type; friend class OctalUART; }; int initOctalUART(const char *moduleName, const char *carrierName, const char *siteName, int nports, int intVec) { if(OctalUART::findOctalUART(moduleName)) { printf("initOctalUART %s already exists\n",moduleName); return(0); } if(nports!=4 && nports!=8) { printf("initOctalUART %d is illegal number ports\n",nports); return(0); } IndustryPackModule *pIPM = IndustryPackModule::createIndustryPackModule( moduleName,carrierName,siteName); if(!pIPM) return(0); unsigned char manufacturer = pIPM->getManufacturer(); unsigned char model = pIPM->getModel(); if(manufacturer!=GREEN_SPRING_ID) { printf("initOctalUART manufacturer 0x%x not GREEN_SPRING_ID\n", manufacturer); return(0); } if(nports==4) { if(model!=GSIP_QUAD_SERIAL) { printf("initOctalUART model 0x%x not a quadUart\n",model); return(0); } } else { if(model!=GSIP_OCTAL232 && model!=GSIP_OCTAL422 && model!=GSIP_OCTAL485){ printf("initOctalUART model 0x%x not a OctalUart\n",model); return(0); } } OctalUART *pOctalUART = new OctalUART(moduleName,pIPM,nports,intVec); DLNode *node = DLList::allocNode(); node->obj = pOctalUART; pOctalUARTList->addEnd(node); return(0); } OctalUART::OctalUART(const char *moduleName, IndustryPackModule *pIndustryPackModule, int nports, int intVec) : pIPM(pIndustryPackModule), name(moduleName), nports(nports), intVec(intVec) { short *ms = (short *)pIPM->allocMemMapIP(1*64*1024); regs = (SCC2698*)pIPM->getMemBaseIO(); memset(imr,0,sizeof(imr)); pIPM->intConfig(0); pIPM->intConfig(1); if(intConnect(INUM_TO_IVEC(intVec),(VOIDFUNCPTR)intFunc,(int)this)==ERROR){ printf("OctalUART %s intConnect failed\n",moduleName); } *ms = (short)intVec; // set up the single interrupt vector // (2) create the ports // choose set 2 BRG int i; int block,port; for(i=0;i<4;i++) regs[i].u.w.acr=0x80; ports = new OctalUARTPort*[nports]; for(i=0;iintEnable(0); pIPM->intEnable(1); Reboot::rebootHookAdd(rebootCallback,(void *)this); return; } OctalUART *OctalUART::findOctalUART(const char *moduleName) { if(pOctalUARTList==0) pOctalUARTList = new DLList(); DLNode *node = pOctalUARTList->getFirst(); while(node) { OctalUART *pcard = (OctalUART *)node->obj; if(::strcmp(moduleName,pcard->name)==0) { return(pcard); } node = pOctalUARTList->getNext(node); } return(0); } OctalUARTPort *OctalUART::findOctalUARTPort(int port) { if(port<0 || port>=nports) { printf("OctalUART::findOctal illegal port number\n"); return(0); } return(ports[port]); } void OctalUART::writeIpReg(volatile unsigned short& dest,unsigned short value) { volatile int counter=0; for(int i=0; ifindOctalUARTPort(port); if(pport==0) { printf("initOctalUARTPort port %d not found\n",port); return(0); } if(!pport->config(baud,stop_bits, bits_char, parity[0], flow_type[0])) { printf("initOctalUARTPort config failed for %s\n",moduleName); return(0); } pport->registerName(portName); return(0); } OctalUARTPort::OctalUARTPort(int p, int b, OctalUART *pou) : SerialPort(), outputBuffer(0), outputLength(0), outputPosition(0), port(p), block(b), pOctalUART(pou), mode(RS232) // NOTE: opcr,mr1,mr2,imr all set by config { SCC2698* r = (SCC2698*)pOctalUART->pIPM->getMemBaseIO(); regs=&r[block]; SCC2698_CHAN* c = (SCC2698_CHAN*)pOctalUART->pIPM->getMemBaseIO(); chan=&c[port]; ip_type=(pOctalUART->nports==4)?QUAD:OCTAL; } bool OctalUARTPort::config( int baud, int stopBits, int bitsPerChar, char parity, char flowControl) { // accesses to the upper 4 bits of command reg must be separated by 3 // edges of the X1 clock // reset everything pOctalUART->writeIpReg(chan->u.w.cr,0x0a); // disable trans/recv pOctalUART->writeIpReg(chan->u.w.cr,0x20); // reset recv pOctalUART->writeIpReg(chan->u.w.cr,0x30); // reset trans pOctalUART->writeIpReg(chan->u.w.cr,0x40); // reset error status // mode registers pOctalUART->writeIpReg(chan->u.w.cr,0x10); // point MR to MR1 // ------------------------------------------- mr1=0x00; // RxRTS=No, RxINT=RxRDY, Error=char mr2=0x00; // normal, TxRTS=No, CTS=No, stop-bit-length=0.563 switch(parity) // parity { case 'E': break; // leave zero for even parity case 'O': mr1|=0x04; break; // odd parity case 'N': mr1|=0x10; break; // no parity case 0: break; // dont configure default: printf("Illegal parity"); return(false); } switch(bitsPerChar) // per character { case 5: break; // leave alone case 6: mr1|=0x01; break; case 7: mr1|=0x02; break; case 8: mr1|=0x03; break; case 0: break; default: printf("Illegal number of bits\n"); return(false); } switch(stopBits) // number of stop bits { case 2: mr2|=0x0f; break; case 1: mr2|=0x07; break; case 0: break; default: printf("Illegal number of stop bits\n"); return(false); } // ------------------------------------------- chan->u.w.mr=mr1; chan->u.w.mr=mr2; switch(baud) // clock select { case 1200: chan->u.w.csr=0x66; break; case 2400: chan->u.w.csr=0x88; break; case 4800: chan->u.w.csr=0x99; break; case 9600: chan->u.w.csr=0xbb; break; case 38400: chan->u.w.csr=0x22; break; case 19200: chan->u.w.csr=0xcc; break; case 0: break; default: printf("Illegal baud rate\n"); return(false); } switch(flowControl) // set up flow control { // case 'H': mr1|=0x80; mr2|=0x30; break; case 'H': mr1|=0x80; mr2|=0x10; break; case 'N': break; case 0: break; default: printf("Illegal flow control\n"); return(false); } opcr=0x80; if(pOctalUART->pIPM->getModel()==GSIP_OCTAL485) setModeRS485(); else setModeRS232(); // enable everything, really only Rx interrupts if(ip_type==QUAD) { imr=0x01; // save the value needed to enable transmitter interrupt pOctalUART->imr[block]=0x02; // channel a of block only for quad } else { if(port%2) // value indicates odd channel imr=0x10; // save the value needed to enable transmitter interrupt else imr=0x01; // save the value needed to enable transmitter interrupt pOctalUART->imr[block]=0x22; // channel a and b of block } regs->u.w.imr=pOctalUART->imr[block]; // enable RxRDY interrupt pOctalUART->writeIpReg(chan->u.w.cr,0x05); // enable Tx,Rx return(true); } // unsolicited data will cause the function to be entered // enters here when tranmit buffer empty void OctalUART::intFunc(void* v) { OctalUART* pOU = (OctalUART*)v; OctalUARTPort* pPort; volatile unsigned char sr; volatile unsigned char data; int i; byteHandlerRC rc; for(i=0;inports;i++) { pPort=pOU->ports[i]; sr=pPort->chan->u.r.sr; // does a byte needs to be read if(sr&0x01) { data=readIpReg(pPort->chan->u.r.rhr); if(pPort->ph) (*pPort->ph)(pPort->phParm,true,data); // run the user function if present if(pPort->bh) { rc=(*pPort->bh)(pPort->bhParm,(unsigned char)data); if(rc!=byteHandlerOK) { if(pPort->request==requestRead || pPort->request==requestWriteRead) { pPort->request = requestNone; pPort->ioStatus = (rc==byteHandlerEndRead) ? serialPortSuccess : serialPortFailure; semGive(pPort->ioComplete); } } } } // does a byte needs to be sent if((pOU->imr[pPort->block]&pPort->imr) && (sr&0x04)) { if(pPort->outputBuffer) { if(pPort->outputPositionoutputLength) { if(pPort->ph) { (*pPort->ph)(pPort->phParm,false, pPort->outputBuffer[pPort->outputPosition]); } writeIpReg(pPort->chan->u.w.thr, pPort->outputBuffer[pPort->outputPosition++]); } else { // disable Tx INT pOU->imr[pPort->block]&=~pPort->imr; // deactivate Tx INT pPort->regs->u.w.imr=pOU->imr[pPort->block]; pPort->outputBuffer=0; // clear buffer, signal completion if(pPort->request==requestWrite) { pPort->request = requestNone; pPort->ioStatus = serialPortSuccess; semGive(pPort->ioComplete); } } } else { pOU->imr[pPort->block]&=~pPort->imr; // deactivate Tx INT pPort->regs->u.w.imr=pOU->imr[pPort->block]; // disable Tx INT } } if(sr&0xf0) { // error condition present logMsg("E"); pPort->chan->u.w.cr=0x40; // reset error status } } return; } serialPortStatus OctalUARTPort::write( unsigned char *data, int nbytes, int timeout) { if(request!=requestWriteRead) { if(request!=requestNone) { printf("OctalUARTPort::write called but request already active\n"); return(serialPortFailure); } request = requestWrite; } startIoTimeout(timeout); outputLength = nbytes; outputBuffer = data; outputPosition=0; if(mode==RS485) pOctalUART->writeIpReg(chan->u.w.cr,0x82); // disable recv,1000=assert RTSN (low) int lockKey = ::intLock(); pOctalUART->imr[block]|=imr; // activate Tx interrupt regs->u.w.imr=pOctalUART->imr[block]; // enable Tx interrupt ::intUnlock(lockKey); waitIo(); if(mode==RS485) { // make sure all data sent while(!(chan->u.r.sr&0x08)); // TxEMT pOctalUART->writeIpReg(chan->u.w.cr,0x91); // enable recv, 1001=negate RTSN (high) } return(ioStatus); } serialPortStatus OctalUARTPort::read(int timeout) { if(request!=requestNone) { printf("OctalUARTPort::read called but request already active\n"); return(serialPortFailure); } int lockKey = intLock(); request = requestRead; startIoTimeout(timeout); intUnlock(lockKey); waitIo(); return(ioStatus); } serialPortStatus OctalUARTPort::writeRead( unsigned char *data, int nbytes, int timeout) { if(request!=requestNone) { printf("OctalUARTPort::writeRead called but request already active\n"); return(serialPortFailure); } request = requestWriteRead; return(write(data,nbytes,timeout)); } void OctalUARTPort::setModeRS232() { mode=RS232; // allow RTS (MPOa) to be turned on/off automatically if(ip_type==QUAD) regs->u.w.opcr=0x87; // out,MPOb=RTSN,MPOa=FIFO full else { if(port%2) // odd port is b opcr|=0x70; // MPOb=FIFO full else opcr|=0x07; // MPOa=FIFO full regs->u.w.opcr=opcr; } pOctalUART->writeIpReg(chan->u.w.cr,0x10); // point MR to MR1 mr1|=0x80; pOctalUART->writeIpReg(chan->u.w.mr,mr1); // use RxRTS (auto mode) mr2|=0x20; pOctalUART->writeIpReg(chan->u.w.mr,mr2); // use TxRTS (auto mode),CTS enable Tx } void OctalUARTPort::setModeRS485() { mode=RS485; // allow RTS (MPOa) to be turned on/off manually through control reg if(ip_type==QUAD) regs->u.w.opcr=0x80; // out,MPOb=RTSN,MPOa=RTSN else { if(port%2) // odd port is b opcr&=~0x70; // MPOb=RTSN else opcr&=~0x07; // MPOa=RTSN regs->u.w.opcr=opcr; // out,MPOb=RTSN,MPOa=RTSN } pOctalUART->writeIpReg(chan->u.w.cr,0x10); // point MR to MR1 mr1&=0x7f; pOctalUART->writeIpReg(chan->u.w.mr,mr1); // no auto RxRTS mr2&=0xcf; pOctalUART->writeIpReg(chan->u.w.mr,mr2); // no auto TxRTS and no CTS enable Tx } void OctalUART:: rebootCallback(void *v) { OctalUART *pOctalUART= (OctalUART*)v; pOctalUART->pIPM->intDisable(0); pOctalUART->pIPM->intDisable(1); }