ref: ada1920a0e4787d46402dcdc3e9f3ad3ddae7f0e
dir: /sys/src/9/imx8/uartimx.c/
#include "u.h" #include "../port/lib.h" #include "../port/error.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" enum { URXD = 0x00/4, /* UART Receiver Register */ RX_CHARRDY = 1<<15, RX_ERR = 1<<14, RX_OVRRUN = 1<<13, RX_FRMERR = 1<<12, RX_BRK = 1<<11, RX_PRERR = 1<<10, RX_DATA = 0xFF, UTXD = 0x40/4, /* UART Transmitter Register */ TX_DATA = 0xFF, UCR1 = 0x80/4, /* UART Control Register 1 */ CR1_ADEN = 1<<15, /* Automatic Baud Rate Detection Interrupt Enable */ CR1_ADNR = 1<<14, /* Automatic Detection of Baud Rate */ CR1_TRDYEN = 1<<13, /* Transmitter Ready Interrupt Enable */ CR1_IDEN = 1<<12, /* Idle Condition Detected Interrupt Enable */ CR1_ICD_SHIFT = 10, /* Idle Condition Detect Mask */ CR1_ICD_MASK = 3<<CR1_ICD_SHIFT, CR1_RRDYEN = 1<<9, /* Receiver Ready Interrupt Enable */ CR1_RXDMAEN = 1<<8, /* Receive Ready DMA Enable */ CR1_IREN = 1<<7, /* Infrared Interface Enable */ CR1_TXMPTYEN = 1<<6, /* Transmitter Empty Interrupt Enable */ CR1_RTSDEN = 1<<5, /* RTS Delta Interrupt Enable */ CR1_SNDBRK = 1<<4, /* Send BREAK */ CR1_TXDMAEN = 1<<3, /* Transmitter Ready DMA Enable */ CR1_ATDMAEN = 1<<2, /* Aging DMA Timer Enable */ CR1_DOZE = 1<<1, /* DOZE */ CR1_UARTEN = 1<<0, /* Uart Enable */ UCR2 = 0x84/4, /* UART Control Register 2 */ CR2_ESCI = 1<<15, /* Escape Sequence Interrupt Enable */ CR2_IRTS = 1<<14, /* Ignore RTS Pin */ CR2_CTSC = 1<<13, /* CTS Pin Control */ CR2_CTS = 1<<12, /* Clear to Send */ CR2_ESCEN = 1<<11, /* Escape Enable */ CR2_RTEC_RAISING= 0<<9, CR2_RTEC_FALLING= 1<<9, CR2_RTEC_ANY = 2<<9, CR2_RTEC_MASK = 3<<9, CR2_PREN = 1<<8, /* Parity Enable */ CR2_PREVEN = 0<<7, /* Parity Even */ CR2_PRODD = 1<<7, /* Parity Odd */ CR2_STPB = 1<<6, /* Stop */ CR2_WS8 = 1<<5, /* Word Size */ CR2_WS7 = 0<<5, CR2_RTSEN = 1<<4, /* Request to Send Interrupt Enable */ CR2_ATEN = 1<<3, /* Aging Timer Enable */ CR2_TXEN = 1<<2, /* Transmitter Enable */ CR2_RXEN = 1<<1, /* Receiver Enable */ CR2_SRST = 1<<0, /* Software Reset */ UCR3 = 0x88/4, /* UART Control Register 3 */ CR3_PARERREN = 1<<12, /* Parity Error Interrupt Enable */ CR3_FRAERREN = 1<<11, /* Frame Error Interrupt Enable */ CR3_ADNIMP = 1<<7, /* Autobaud Detection Not Improved */ CR3_RXDSEN = 1<<6, /* Receive Status Interrupt Enable */ CR3_AIRINTEN = 1<<5, /* Asynchronous IR WAKE Interrupt Enable */ CR3_AWAKEN = 1<<4, /* Asynchronous WAKE Interrupt Enable */ CR3_RXDMUXSEL = 1<<2, /* RXD Muxed Input Selected */ CR3_INVT = 1<<1, /* Invert TXD output in RS-232/RS-485 mode */ CR3_ACIEN = 1<<0, /* Autobaud Counter Interrupt Enable */ UCR4 = 0x8C/4, /* UART Control Register 4 */ CR4_CTSTL_SHIFT = 10, /* CTS Trigger Level */ CR4_CTSTL_MASK = 0x3F<<CR4_CTSTL_SHIFT, CR4_INVR = 1<<9, /* Invert RXD Input in RS-232/RS-485 Mode */ CR4_ENIRI = 1<<8, /* Serial Infrared Interrupt Enable */ CR4_WKEN = 1<<7, /* WAKE Interrupt Enable */ CR4_IDDMAEN = 1<<6, /* DMA IDLE Condition Detected Interrupt Enable */ CR4_IRSC = 1<<5, /* IR Special Case */ CR4_LPBYP = 1<<4, /* Low Power Bypass */ CR4_TCEN = 1<<3, /* Transmit Complete Interrupt Enable */ CR4_BKEN = 1<<2, /* BREAK Condition Detected Interrupt Enable */ CR4_OREN = 1<<1, /* Receiver Overrun Interrupt Enable */ CR4_DREN = 1<<0, /* Receive Data Interrupt Enable */ UFCR = 0x90/4, /* UART FIFO Control Register */ FCR_TXTL_SHIFT = 10, /* Transmitter Trigger Level */ FCR_TXTL_MASK = 0x3F<<FCR_TXTL_SHIFT, FCR_RFDIV_SHIFT = 7, /* Reference Frequency Divider */ FCR_RFDIV_MASK = 0x7<<FCR_RFDIV_SHIFT, FCR_DCE = 0<<6, /* DCE/DTE mode select */ FCR_DTE = 1<<6, FCR_RXTL_SHIFT = 0, /* Receive Trigger Level */ FCR_RXTL_MASK = 0x3F<<FCR_RXTL_SHIFT, USR1 = 0x94/4, /* UART Status Register 1 */ SR1_PARITYERR = 1<<15, /* Parity Error Interrupt Flag */ SR1_RTSS = 1<<14, /* RTS_B Pin Status */ SR1_TRDY = 1<<13, /* Transmitter Ready Interrupt / DMA Flag */ SR1_RTSD = 1<<12, /* RTS Delta */ SR1_ESCF = 1<<11, /* Escape Sequence Interrupt Flag */ SR1_FRAMEERR = 1<<10, /* Frame Error Interrupt Flag */ SR1_RRDY = 1<<9, /* Receiver Ready Interrupt / DMA Flag */ SR1_AGTIM = 1<<8, /* Aging Timer Interrupt Flag */ SR1_DTRD = 1<<7, SR1_RXDS = 1<<6, /* Receiver IDLE Interrupt Flag */ SR1_AIRINT = 1<<5, /* Asynchronous IR WAKE Interrupt Flag */ SR1_AWAKE = 1<<4, /* Asynchronous WAKE Interrupt Flag */ SR1_SAD = 1<<3, /* RS-485 Slave Address Detected Interrupt Flag */ USR2 = 0x98/4, /* UART Status Register 2 */ SR2_ADET = 1<<15, /* Automatic Baud Rate Detected Complete */ SR2_TXFE = 1<<14, /* Transmit Buffer FIFO Empty */ SR2_DTRF = 1<<13, SR2_IDLE = 1<<12, /* Idle Condition */ SR2_ACST = 1<<11, /* Autobaud Counter Stopped */ SR2_RIDELT = 1<<10, SR2_RIIN = 1<<9, SR2_IRINT = 1<<8, /* Serial Infrared Interrupt Flag */ SR2_WAKE = 1<<7, /* Wake */ SR2_DCDDELT = 1<<6, SR2_DCDIN = 1<<5, SR2_RTSF = 1<<4, /* RTS Edge Triggered Interrupt Flag */ SR2_TXDC = 1<<3, /* Transmitter Complete */ SR2_BRCD = 1<<2, /* BREAK Condition Detected */ SR2_ORE = 1<<1, /* Overrun Error */ SR2_RDR = 1<<0, /* Receive Data Ready */ UESC = 0x9C/4, /* UART Escape Character Register */ UTIM = 0xA0/4, /* UART Escape Timer Register */ UBIR = 0xA4/4, /* UART BRM Incremental Modulator Register */ UBMR = 0xA8/4, /* UART BRM Modulator Register */ UBRC = 0xAC/4, /* UART Baud Rate Count Register */ ONEMS = 0xB0/4, /* UART One-Millisecond Register */ UTS = 0xB5/4, /* UART Test Register */ UMCR = 0xB8/4, /* UART RS-485 Mode Control Register */ }; extern PhysUart imxphysuart; static Uart uart1 = { .regs = (u32int*)(VIRTIO + 0x860000ULL), .name = "uart1", .baud = 115200, .freq = 25*Mhz, .phys = &imxphysuart, }; static Uart* pnp(void) { return &uart1; } static void kick(Uart *u) { u32int *regs = (u32int*)u->regs; while(u->op < u->oe || uartstageoutput(u)){ if(u->blocked) break; if((regs[USR1] & SR1_TRDY) == 0){ regs[UCR1] |= CR1_TRDYEN; return; } regs[UTXD] = *(u->op++) & TX_DATA; } regs[UCR1] &= ~CR1_TRDYEN; } static void config(Uart *u) { u32int cr2, *regs = u->regs; /* enable uart */ regs[UCR1] = CR1_UARTEN; cr2 = CR2_SRST | CR2_IRTS | CR2_RXEN | CR2_TXEN; switch(u->parity){ case 'e': cr2 |= CR2_PREN | CR2_PREVEN; break; case 'o': cr2 |= CR2_PREN | CR2_PRODD; break; } cr2 |= u->bits == 7 ? CR2_WS7 : CR2_WS8; if(u->stop == 2) cr2 |= CR2_STPB; regs[UCR2] = cr2; regs[UCR3] = 0x7<<8 | CR3_RXDMUXSEL; regs[UCR4] = 31<<CR4_CTSTL_SHIFT; /* baud = clock / (16 * (ubmr+1)/(ubir+1)) */ regs[UFCR] = (6 - 1)<<FCR_RFDIV_SHIFT | 16<<FCR_TXTL_SHIFT | 1<<FCR_RXTL_SHIFT; regs[UBIR] = ((16*u->baud) / 1600)-1; regs[UBMR] = (u->freq / 1600)-1; regs[UCR1] = CR1_UARTEN | CR1_RRDYEN; } static int bits(Uart *u, int n) { switch(n){ case 8: break; case 7: break; default: return -1; } u->bits = n; config(u); return 0; } static int stop(Uart *u, int n) { switch(n){ case 1: break; case 2: break; default: return -1; } u->stop = n; config(u); return 0; } static int parity(Uart *u, int n) { switch(n){ case 'n': break; case 'e': break; case 'o': break; default: return -1; } u->parity = n; config(u); return 0; } static int baud(Uart *u, int n) { if(u->freq == 0 || n <= 0) return -1; u->baud = n; config(u); return 0; } static void rts(Uart*, int) { } static void dobreak(Uart*, int) { } static long status(Uart *uart, void *buf, long n, long offset) { char *p; p = malloc(READSTR); if(p == nil) error(Enomem); snprint(p, READSTR, "b%d\n" "dev(%d) type(%d) framing(%d) overruns(%d) " "berr(%d) serr(%d)\n", uart->baud, uart->dev, uart->type, uart->ferr, uart->oerr, uart->berr, uart->serr ); n = readstr(offset, buf, n, p); free(p); return n; } static void interrupt(Ureg*, void *arg) { Uart *uart = arg; u32int v, *regs = (u32int*)uart->regs; while((v = regs[URXD]) & RX_CHARRDY) uartrecv(uart, v & RX_DATA); uartkick(uart); } static void clkenable(Uart *u, int on) { char clk[32]; snprint(clk, sizeof(clk), "%s.ipg_perclk", u->name); if(on) setclkrate(clk, "osc_25m_ref_clk", u->freq); setclkgate(clk, on); } static void disable(Uart *u) { u32int *regs = u->regs; if(u->console) return; /* avoid glitch */ regs[UCR1] = 0; clkenable(u, 0); } static void enable(Uart *u, int ie) { disable(u); clkenable(u, 1); if(ie) intrenable(IRQuart1, interrupt, u, BUSUNKNOWN, u->name); config(u); } static void donothing(Uart*, int) { } static void putc(Uart *u, int c) { u32int *regs = u->regs; while((regs[USR1] & SR1_TRDY) == 0) ; regs[UTXD] = c & TX_DATA; } static int getc(Uart *u) { u32int c, *regs = (u32int*)u->regs; do c = regs[URXD]; while((c & RX_CHARRDY) == 0); return c & RX_DATA; } void uartconsinit(void) { consuart = &uart1; consuart->console = 1; uartctl(consuart, "l8 pn s1"); uartputs(kmesg.buf, kmesg.n); } PhysUart imxphysuart = { .name = "imx", .pnp = pnp, .enable = enable, .disable = disable, .kick = kick, .dobreak = dobreak, .baud = baud, .bits = bits, .stop = stop, .parity = parity, .modemctl = donothing, .rts = rts, .dtr = donothing, .status = status, .fifo = donothing, .getc = getc, .putc = putc, };