Print this page
bcm2835: use the real uart instead of the mini-uart
The real uart is more capable.  We'll want to use it for the real console
eventually anyway, so let's bite the bullet now when no one will really
notice.  (For comparison, the Linux kernel uses the real uart and totally
lacks a driver for the miniuart.)

@@ -16,42 +16,45 @@
 
 #include <sys/elf.h>
 #include <sys/atag.h>
 
 /*
- * The primary serial console that we end up using is not in fact a normal UART,
- * but is instead actually a mini-uart that shares interrupts and registers with
- * the SPI masters as well. While the RPi also supports another more traditional
- * UART, that isn't what we are actually hooking up to generally with the
- * adafruit cable. We already wasted our time having to figure that out. -_-
+ * The primary serial console that we end up using is the normal UART, not
+ * the mini-uart that shares interrupts and registers with the SPI masters
+ * as well.
  */
 
-#define AUX_BASE        0x20215000
-#define AUX_ENABLES     0x4
-#define AUX_MU_IO_REG   0x40
-#define AUX_MU_IER_REG  0x44
-#define AUX_MU_IIR_REG  0x48
-#define AUX_MU_LCR_REG  0x4C
-#define AUX_MU_MCR_REG  0x50
-#define AUX_MU_LSR_REG  0x54
-#define AUX_MU_CNTL_REG 0x60
-#define AUX_MU_BAUD     0x68
+#define UART_BASE               0x20201000
+#define UART_DR                 0x0
+#define UART_FR                 0x18
+#define UART_IBRD               0x24
+#define UART_FBRD               0x28
+#define UART_LCRH               0x2c
+#define UART_CR                 0x30
+#define UART_ICR                0x44
+
+#define UART_FR_RXFE            0x10            /* RX fifo empty */
+#define UART_FR_TXFF            0x20            /* TX fifo full */
+
+#define UART_LCRH_FEN           0x00000010      /* fifo enable */
+#define UART_LCRH_WLEN_8        0x00000060      /* 8 bits */
+
+#define UART_CR_UARTEN          0x001           /* uart enable */
+#define UART_CR_TXE             0x100           /* TX enable */
+#define UART_CR_RXE             0x200           /* RX enable */
 
-#define AUX_MU_RX_READY 0x01
-#define AUX_MU_TX_READY 0x20
 
 /*
- * For the mini UART, all we care about are pins 14 and 15 for the UART.
- * Specifically, alt5 for GPIO14 is TXD1 and GPIO15 is RXD1. Those are
- * controlled by FSEL1.
+ * All we care about are pins 14 and 15 for the UART.  Specifically, alt0
+ * for GPIO14 is TXD0 and GPIO15 is RXD0. Those are controlled by FSEL1.
  */
 #define GPIO_BASE       0x20200000
 #define GPIO_FSEL1      0x4
 #define GPIO_PUD        0x94
 #define GPIO_PUDCLK0    0x98
 
-#define GPIO_SEL_ALT5   0x2
+#define GPIO_SEL_ALT0   0x4
 #define GPIO_UART_MASK  0xfffc0fff
 #define GPIO_UART_TX_SHIFT      12
 #define GPIO_UART_RX_SHIFT      15
 
 #define GPIO_PUD_DISABLE        0x0

@@ -73,11 +76,11 @@
 
 /*
  * A simple nop
  */
 static void
-bcm2835_miniuart_nop(void)
+uart_nop(void)
 {
         __asm__ volatile("mov r0, r0\n" : : :);
 }
 
 void fakeload_backend_putc(int);

@@ -95,67 +98,54 @@
 fakeload_backend_init(void)
 {
         uint32_t v;
         int i;
 
-        /* Enable the mini UAT */
-        arm_reg_write(AUX_BASE + AUX_ENABLES, 0x1);
-
-        /* Disable interrupts */
-        arm_reg_write(AUX_BASE + AUX_MU_IER_REG, 0x0);
-
-        /* Disable the RX and TX */
-        arm_reg_write(AUX_BASE + AUX_MU_CNTL_REG, 0x0);
-
-        /*
-         * Enable 8-bit word length. External sources tell us the PRM is buggy
-         * here and that even though bit 1 is reserved, we need to actually set
-         * it to get 8-bit words.
-         */
-        arm_reg_write(AUX_BASE + AUX_MU_LCR_REG, 0x3);
-
-        /* Set RTS high */
-        arm_reg_write(AUX_BASE + AUX_MU_MCR_REG, 0x0);
-
-        /* Disable interrupts */
-        arm_reg_write(AUX_BASE + AUX_MU_IER_REG, 0x0);
-
-        /* Set baud rate */
-        arm_reg_write(AUX_BASE + AUX_MU_IIR_REG, 0xc6);
-        arm_reg_write(AUX_BASE + AUX_MU_BAUD, 0x10e);
+        /* disable UART */
+        arm_reg_write(UART_BASE + UART_CR, 0);
 
         /* TODO: Factor out the gpio bits */
         v = arm_reg_read(GPIO_BASE + GPIO_FSEL1);
         v &= GPIO_UART_MASK;
-        v |= GPIO_SEL_ALT5 << GPIO_UART_RX_SHIFT;
-        v |= GPIO_SEL_ALT5 << GPIO_UART_TX_SHIFT;
+        v |= GPIO_SEL_ALT0 << GPIO_UART_RX_SHIFT;
+        v |= GPIO_SEL_ALT0 << GPIO_UART_TX_SHIFT;
         arm_reg_write(GPIO_BASE + GPIO_FSEL1, v);
 
         arm_reg_write(GPIO_BASE + GPIO_PUD, GPIO_PUD_DISABLE);
         for (i = 0; i < 150; i++)
-                bcm2835_miniuart_nop();
+                uart_nop();
         arm_reg_write(GPIO_BASE + GPIO_PUDCLK0, GPIO_PUDCLK_UART);
         for (i = 0; i < 150; i++)
-                bcm2835_miniuart_nop();
-        // XXX: GPIO_PUD_DISABLE again?
+                uart_nop();
         arm_reg_write(GPIO_BASE + GPIO_PUDCLK0, 0);
 
-        /* Finally, go back and enable RX and TX */
-        arm_reg_write(AUX_BASE + AUX_MU_CNTL_REG, 0x3);
+        /* clear all interrupts */
+        arm_reg_write(UART_BASE + UART_ICR, 0x7ff);
+
+        /* set the baud rate */
+        arm_reg_write(UART_BASE + UART_IBRD, 1);
+        arm_reg_write(UART_BASE + UART_FBRD, 40);
+
+        /* select 8-bit, enable FIFOs */
+        arm_reg_write(UART_BASE + UART_LCRH, UART_LCRH_WLEN_8 | UART_LCRH_FEN);
+
+        /* enable UART */
+        arm_reg_write(UART_BASE + UART_CR, UART_CR_UARTEN | UART_CR_TXE |
+            UART_CR_RXE);
 }
 
 void
 fakeload_backend_putc(int c)
 {
         if (c == '\n')
                 fakeload_backend_putc('\r');
 
-        for (;;) {
-                if (arm_reg_read(AUX_BASE + AUX_MU_LSR_REG) & AUX_MU_TX_READY)
-                        break;
-        }
-        arm_reg_write(AUX_BASE + AUX_MU_IO_REG, c & 0x7f);
+        while (arm_reg_read(UART_BASE + UART_FR) & UART_FR_TXFF)
+                ;
+        arm_reg_write(UART_BASE + UART_DR, c & 0x7f);
+        if (c == '\n')
+                fakeload_backend_putc('\r');
 }
 
 /*
  * Add a map for the uart.
  */

@@ -173,12 +163,12 @@
         aim.aim_mapflags = PF_R | PF_W | PF_NORELOC | PF_DEVICE;
         atag_append(chain, &aim.aim_header);
 
         aim.aim_header.ah_size = ATAG_ILLUMOS_MAPPING_SIZE;
         aim.aim_header.ah_tag = ATAG_ILLUMOS_MAPPING;
-        aim.aim_paddr = AUX_BASE;
-        aim.aim_vaddr = AUX_BASE;
+        aim.aim_paddr = UART_BASE;
+        aim.aim_vaddr = UART_BASE;
         aim.aim_vlen = 0x1000;
         aim.aim_plen = 0x1000;
         aim.aim_mapflags = PF_R | PF_W | PF_NORELOC | PF_DEVICE;
         atag_append(chain, &aim.aim_header);
 }