summaryrefslogtreecommitdiff
path: root/sys/src/9/imx8/uartimx.c
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@felloff.net>2022-05-08 16:50:29 +0000
committercinap_lenrek <cinap_lenrek@felloff.net>2022-05-08 16:50:29 +0000
commitfff070f2cbb01b7c0879e9dcb13ee4e3ed2497f0 (patch)
tree44899a9ae6e8143740a4b02e7d50a3b6db768008 /sys/src/9/imx8/uartimx.c
parent9126ee3eea90d639f4e877c01400248581d10f65 (diff)
imx8: add work in progress i.MX8MQ kernel for the mntreform2 laptop
This is a work in progress port to the mntreform2 laptop. Working so far: - mmu (same as raspberry pi 3b+) - arm generic timer - gicv3 - uart1 - enet With access to the uart, one can netboot this kernel in u-boot using the following commands: > dhcp > bootm
Diffstat (limited to 'sys/src/9/imx8/uartimx.c')
-rw-r--r--sys/src/9/imx8/uartimx.c383
1 files changed, 383 insertions, 0 deletions
diff --git a/sys/src/9/imx8/uartimx.c b/sys/src/9/imx8/uartimx.c
new file mode 100644
index 000000000..afc7d0096
--- /dev/null
+++ b/sys/src/9/imx8/uartimx.c
@@ -0,0 +1,383 @@
+#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;
+
+ if(u->blocked)
+ return;
+ while(regs[USR1] & SR1_TRDY){
+ if(u->op >= u->oe && uartstageoutput(u) == 0)
+ break;
+ regs[UTXD] = *(u->op++) & TX_DATA;
+ }
+}
+
+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 | 32<<FCR_TXTL_SHIFT | 32<<FCR_RXTL_SHIFT;
+ regs[UBIR] = ((16*u->baud) / 1600)-1;
+ regs[UBMR] = (u->freq / 1600)-1;
+
+ regs[UCR1] = CR1_UARTEN | CR1_TRDYEN | 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
+disable(Uart *u)
+{
+ u32int *regs = u->regs;
+ regs[UCR1] = 0;
+}
+
+static void
+enable(Uart *u, int ie)
+{
+ disable(u);
+ 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,
+};
+