#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "m8260.h" #include "../port/error.h" /* * PowerPC 8260 SMC UART */ enum { Nuart = 1, /* Number of SMC Uarts */ /* SMC Mode Registers */ Clen = 0x7800, /* Character length */ Sl = 0x0400, /* Stop length, 0: one stop bit, 1: two */ Pen = 0x0200, /* Parity enable */ Pm = 0x0100, /* Parity mode, 0 is odd */ Sm = 0x0030, /* SMC mode, two bits */ SMUart = 0x0020, /* SMC mode, 0b10 is uart */ Dm = 0x000c, /* Diagnostic mode, 00 is normal */ Ten = 0x0002, /* Transmit enable, 1 is enabled */ Ren = 0x0001, /* Receive enable, 1 is enabled */ /* SMC Event/Mask Registers */ ce_Brke = 0x0040, /* Break end */ ce_Br = 0x0020, /* Break character received */ ce_Bsy = 0x0004, /* Busy condition */ ce_Txb = 0x0002, /* Tx buffer */ ce_Rxb = 0x0001, /* Rx buffer */ /* Receive/Transmit Buffer Descriptor Control bits */ BDContin= 1<<9, BDIdle= 1<<8, BDPreamble= 1<<8, BDBreak= 1<<5, BDFrame= 1<<4, BDParity= 1<<3, BDOverrun= 1<<1, /* Tx and Rx buffer sizes (32 bytes) */ Rxsize= CACHELINESZ, Txsize= CACHELINESZ, }; extern PhysUart smcphysuart; Uart smcuart[Nuart] = { { .name = "SMC1", .baud = 115200, .bits = 8, .stop = 1, .parity = 'n', .phys = &smcphysuart, .special = 0, }, /* Only configure SMC1 for now { .name = "SMC2", .baud = 115200, .bits = 8, .stop = 1, .parity = 'n', .phys = &smcphysuart, .special = 0, }, */ }; typedef struct UartData UartData; struct UartData { int smcno; /* smc number: 0 or 1 */ SMC *smc; Uartsmc *usmc; char *rxbuf; char *txbuf; BD* rxb; BD* txb; int initialized; int enabled; } uartdata[Nuart]; int uartinited = 0; static void smcinterrupt(Ureg*, void*); static void smcputc(Uart *uart, int c); static int baudgen(int baud) { int d; d = ((m->brghz+(baud>>1))/baud)>>4; if(d >= (1<<12)) return ((d+15)>>3)|1; return d<<1; } static Uart* smcpnp(void) { int i; for (i = 0; i < nelem(smcuart) - 1; i++) smcuart[i].next = smcuart + i + 1; return smcuart; } static void smcinit(Uart *uart) { Uartsmc *p; SMC *smc; UartData *ud; ulong lcr; int bits; ud = uart->regs; if (ud->initialized) return; /* magic addresses */ p = m->imap->uartsmc + ud->smcno; smc = iomem->smc + ud->smcno; /* SMC1 */ ud->smc = smc; ud->usmc = p; /* setup my uart structure */ if (ud->rxb == nil) ud->rxb = bdalloc(1); if (ud->txb == nil) ud->txb = bdalloc(1); /* step 0: disable rx/tx */ smc->smcmr &= ~3; ioplock(); /* step 1, Using Port D */ if (ud->smcno != 0) panic("Don't know how to set Port D bits"); iomem->port[SMC1PORT].ppar |= SMRXD1|SMTXD1; iomem->port[SMC1PORT].pdir |= SMTXD1; iomem->port[SMC1PORT].pdir &= ~SMRXD1; iomem->port[SMC1PORT].psor &= ~(SMRXD1|SMTXD1); /* step 2: set up brgc1 */ iomem->brgc[ud->smcno] = baudgen(uart->baud) | 0x10000; /* step 3: route clock to SMC1 */ iomem->cmxsmr &= (ud->smcno == 0) ? ~0xb0 : ~0xb; /* clear smcx and smcxcs */ iopunlock(); /* step 4: assign a pointer to the SMCparameter RAM */ m->imap->param[ud->smcno].smcbase = (ulong)p - INTMEM; /* step 5: set up buffer descriptors */ p->rbase = ((ulong)ud->rxb) - (ulong)INTMEM; p->tbase = ((ulong)ud->txb) - (ulong)INTMEM; /* step 6: issue command to CP */ if (ud->smcno == 0) cpmop(InitRxTx, SMC1ID, 0); else cpmop(InitRxTx, SMC2ID, 0); /* step 7: protocol parameters */ p->rfcr = 0x30; p->tfcr = 0x30; /* step 8: receive buffer size */ p->mrblr = Rxsize; /* step 9: */ p->maxidl = 15; /* step 10: */ p->brkln = 0; p->brkec = 0; /* step 11: */ p->brkcr = 0; /* step 12: setup receive buffer */ ud->rxb->status = BDEmpty|BDWrap|BDInt; ud->rxb->length = 0; ud->rxbuf = xspanalloc(Rxsize, 0, CACHELINESZ); ud->rxb->addr = PADDR(ud->rxbuf); /* step 13: step transmit buffer */ ud->txb->status = BDWrap|BDInt; ud->txb->length = 0; ud->txbuf = xspanalloc(Txsize, 0, CACHELINESZ); ud->txb->addr = PADDR(ud->txbuf); /* step 14: clear events */ smc->smce = ce_Brke | ce_Br | ce_Bsy | ce_Txb | ce_Rxb; /* * step 15: enable interrupts (done later) * smc->smcm = ce_Brke | ce_Br | ce_Bsy | ce_Txb | ce_Rxb; * intrenable(4 + ud->smcno, smcinterrupt, up, 0, uart->name); */ /* step 17: set parity, no of bits, UART mode, ... */ lcr = SMUart; bits = uart->bits + 1; switch(uart->parity){ case 'e': lcr |= (Pen|Pm); bits +=1; break; case 'o': lcr |= Pen; bits +=1; break; case 'n': default: break; } if(uart->stop == 2){ lcr |= Sl; bits += 1; } /* Set new value and reenable if device was previously enabled */ smc->smcmr = lcr | bits <<11 | 0x3; ud->initialized = 1; } static void smcenable(Uart *uart, int intenb) { UartData *ud; SMC *smc; int nr; nr = uart - smcuart; if (nr < 0 || nr > Nuart) panic("No SMC %d", nr); ud = uartdata + nr; ud->smcno = nr; uart->regs = ud; if (ud->initialized == 0) smcinit(uart); if (ud->enabled || intenb == 0) return; smc = ud->smc; /* clear events */ smc->smce = ce_Brke | ce_Br | ce_Bsy | ce_Txb | ce_Rxb; /* enable interrupts */ smc->smcm = ce_Brke | ce_Br | ce_Bsy | ce_Txb | ce_Rxb; intrenable(4 + ud->smcno, smcinterrupt, uart, uart->name); ud->enabled = 1; } static long smcstatus(Uart* uart, void* buf, long n, long offset) { SMC *sp; char p[128]; sp = ((UartData*)uart->regs)->smc; snprint(p, sizeof p, "b%d c%d e%d l%d m0 p%c s%d i1\n" "dev(%d) type(%d) framing(%d) overruns(%d)\n", uart->baud, uart->hup_dcd, uart->hup_dsr, ((sp->smcmr & Clen) >>11) - ((sp->smcmr&Pen) ? 1 : 0) - ((sp->smcmr&Sl) ? 2 : 1), (sp->smcmr & Pen) ? ((sp->smcmr & Pm) ? 'e': 'o'): 'n', (sp->smcmr & Sl) ? 2: 1, uart->dev, uart->type, uart->ferr, uart->oerr ); n = readstr(offset, buf, n, p); free(p); return n; } static void smcfifo(Uart*, int) { /* * Toggle FIFOs: * if none, do nothing; * reset the Rx and Tx FIFOs; * empty the Rx buffer and clear any interrupt conditions; * if enabling, try to turn them on. */ return; } static void smcdtr(Uart*, int) { } static void smcrts(Uart*, int) { } static void smcmodemctl(Uart*, int) { } static int smcparity(Uart* uart, int parity) { int lcr; SMC *sp; sp = ((UartData*)uart->regs)->smc; lcr = sp->smcmr & ~(Pen|Pm); /* Disable transmitter/receiver. */ sp->smcmr &= ~(Ren | Ten); switch(parity){ case 'e': lcr |= (Pen|Pm); break; case 'o': lcr |= Pen; break; case 'n': default: break; } /* Set new value and reenable if device was previously enabled */ sp->smcmr = lcr; uart->parity = parity; return 0; } static int smcstop(Uart* uart, int stop) { int lcr, bits; SMC *sp; sp = ((UartData*)uart->regs)->smc; lcr = sp->smcmr & ~(Sl | Clen); /* Disable transmitter/receiver. */ sp->smcmr &= ~(Ren | Ten); switch(stop){ case 1: break; case 2: lcr |= Sl; break; default: return -1; } bits = uart->bits + ((lcr & Pen) ? 1 : 0) + ((lcr & Sl) ? 2 : 1); lcr |= bits<<11; /* Set new value and reenable if device was previously enabled */ sp->smcmr = lcr; uart->stop = stop; return 0; } static int smcbits(Uart* uart, int bits) { int lcr, b; SMC *sp; if (bits < 5 || bits > 14) return -1; sp = ((UartData*)uart->regs)->smc; lcr = sp->smcmr & ~Clen; b = bits + ((sp->smcmr & Pen) ? 1 : 0) + ((sp->smcmr & Sl) ? 2 : 1); if (b > 15) return -1; /* Disable transmitter/receiver */ sp->smcmr &= ~(Ren | Ten); /* Set new value and reenable if device was previously enabled */ sp->smcmr = lcr | b<<11; uart->bits = bits; return 0; } static int smcbaud(Uart* uart, int baud) { int i; SMC *sp; if (uart->enabled){ sp = ((UartData*)uart->regs)->smc; if(uart->freq == 0 || baud <= 0) return -1; i = sp - iomem->smc; iomem->brgc[i] = (((m->brghz >> 4) / baud) << 1) | 0x00010000; } uart->baud = baud; return 0; } static void smcbreak(Uart*, int) { } static void smckick(Uart *uart) { BD *txb; UartData *ud; int i; if(uart->blocked) return; ud = uart->regs; txb = ud->txb; if (txb->status & BDReady) return; /* Still busy */ for(i = 0; i < Txsize; i++){ if(uart->op >= uart->oe && uartstageoutput(uart) == 0) break; ud->txbuf[i] = *(uart->op++); } if (i == 0) return; dcflush(ud->txbuf, Txsize); txb->length = i; sync(); txb->status |= BDReady|BDInt; } static void smcinterrupt(Ureg*, void* u) { int i, nc; char *buf; BD *rxb; UartData *ud; Uart *uart; uchar events; uart = u; if (uart == nil) panic("uart is nil"); ud = uart->regs; if (ud == nil) panic("ud is nil"); events = ud->smc->smce; ud->smc->smce = events; /* Clear events */ if (events & 0x10) iprint("smc%d: break\n", ud->smcno); if (events & 0x4) uart->oerr++; if (events & 0x1){ /* Receive characters */ rxb = ud->rxb; buf = ud->rxbuf; dczap(buf, Rxsize); /* invalidate data cache before copying */ if ((rxb->status & BDEmpty) == 0){ nc = rxb->length; for (i=0; istatus |= BDEmpty; }else{ iprint("uartsmc: unexpected receive event\n"); } } if (events & 0x2){ if ((ud->txb->status & BDReady) == 0) uartkick(uart); } } static void smcdisable(Uart* uart) { SMC *sp; sp = ((UartData*)uart->regs)->smc; sp->smcmr &= ~(Ren | Ten); } static int getchars(Uart *uart, uchar *cbuf) { int i, nc; char *buf; BD *rxb; UartData *ud; ud = uart->regs; rxb = ud->rxb; /* Wait for character to show up. */ buf = ud->rxbuf; while (rxb->status & BDEmpty) ; nc = rxb->length; for (i=0; istatus |= BDEmpty; return(nc); } static int smcgetc(Uart *uart) { static uchar buf[128], *p; static int cnt; char c; if (cnt <= 0) { cnt = getchars(uart, buf); p = buf; } c = *p++; cnt--; return(c); } static void smcputc(Uart *uart, int c) { BD *txb; UartData *ud; SMC *smc; ud = uart->regs; txb = ud->txb; smc = ud->smc; smc->smcm = 0; /* Wait for last character to go. */ while (txb->status & BDReady) ; ud->txbuf[0] = c; dcflush(ud->txbuf, 1); txb->length = 1; sync(); txb->status |= BDReady; while (txb->status & BDReady) ; } PhysUart smcphysuart = { .name = "smc", .pnp = smcpnp, .enable = smcenable, .disable = smcdisable, .kick = smckick, .dobreak = smcbreak, .baud = smcbaud, .bits = smcbits, .stop = smcstop, .parity = smcparity, .modemctl = smcmodemctl, .rts = smcrts, .dtr = smcdtr, .status = smcstatus, .fifo = smcfifo, .getc = smcgetc, .putc = smcputc, }; void console(void) { Uart *uart; int n; char *cmd, *p; if((p = getconf("console")) == nil) return; n = strtoul(p, &cmd, 0); if(p == cmd) return; if(n < 0 || n >= nelem(smcuart)) return; uart = smcuart + n; /* uartctl(uart, "b115200 l8 pn s1"); */ if(*cmd != '\0') uartctl(uart, cmd); (*uart->phys->enable)(uart, 0); consuart = uart; uart->console = 1; } void dbgputc(int c) { Uartsmc *su; BD *tbdf; Imap *imap; char *addr; /* Should work as long as Imap is mapped at 0xf0000000 (INTMEM) */ imap = (Imap*)INTMEM; su = (Uartsmc *)(INTMEM | imap->param[0].smcbase); tbdf = (BD *)(INTMEM | su->tbase); /* Wait for last character to go. */ while (tbdf->status & BDReady) ; addr = KADDR(tbdf->addr); *addr = c; tbdf->length = 1; sync(); tbdf->status |= BDReady; while (tbdf->status & BDReady) ; delay(300); }