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.)
   1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright (c) 2013 Joyent, Inc.  All rights reserved.
  14  * Copyright (c) 2015 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
  15  */
  16 
  17 #include <sys/elf.h>
  18 #include <sys/atag.h>
  19 
  20 /*
  21  * The primary serial console that we end up using is not in fact a normal UART,
  22  * but is instead actually a mini-uart that shares interrupts and registers with
  23  * the SPI masters as well. While the RPi also supports another more traditional
  24  * UART, that isn't what we are actually hooking up to generally with the
  25  * adafruit cable. We already wasted our time having to figure that out. -_-
  26  */
  27 
  28 #define AUX_BASE        0x20215000
  29 #define AUX_ENABLES     0x4
  30 #define AUX_MU_IO_REG   0x40
  31 #define AUX_MU_IER_REG  0x44
  32 #define AUX_MU_IIR_REG  0x48
  33 #define AUX_MU_LCR_REG  0x4C
  34 #define AUX_MU_MCR_REG  0x50
  35 #define AUX_MU_LSR_REG  0x54
  36 #define AUX_MU_CNTL_REG 0x60
  37 #define AUX_MU_BAUD     0x68








  38 
  39 #define AUX_MU_RX_READY 0x01
  40 #define AUX_MU_TX_READY 0x20
  41 
  42 /*
  43  * For the mini UART, all we care about are pins 14 and 15 for the UART.
  44  * Specifically, alt5 for GPIO14 is TXD1 and GPIO15 is RXD1. Those are
  45  * controlled by FSEL1.
  46  */
  47 #define GPIO_BASE       0x20200000
  48 #define GPIO_FSEL1      0x4
  49 #define GPIO_PUD        0x94
  50 #define GPIO_PUDCLK0    0x98
  51 
  52 #define GPIO_SEL_ALT5   0x2
  53 #define GPIO_UART_MASK  0xfffc0fff
  54 #define GPIO_UART_TX_SHIFT      12
  55 #define GPIO_UART_RX_SHIFT      15
  56 
  57 #define GPIO_PUD_DISABLE        0x0
  58 #define GPIO_PUDCLK_UART        0x0000c000
  59 
  60 static __GNU_INLINE uint32_t arm_reg_read(uint32_t reg)
  61 {
  62         volatile uint32_t *ptr = (volatile uint32_t *)reg;
  63 
  64         return *ptr;
  65 }
  66 
  67 static __GNU_INLINE void arm_reg_write(uint32_t reg, uint32_t val)
  68 {
  69         volatile uint32_t *ptr = (volatile uint32_t *)reg;
  70 
  71         *ptr = val;
  72 }
  73 
  74 /*
  75  * A simple nop
  76  */
  77 static void
  78 bcm2835_miniuart_nop(void)
  79 {
  80         __asm__ volatile("mov r0, r0\n" : : :);
  81 }
  82 
  83 void fakeload_backend_putc(int);
  84 
  85 static void
  86 fakeload_puts(const char *str)
  87 {
  88         while (*str != '\0') {
  89                 fakeload_backend_putc(*str);
  90                 str++;
  91         }
  92 }
  93 
  94 void
  95 fakeload_backend_init(void)
  96 {
  97         uint32_t v;
  98         int i;
  99 
 100         /* Enable the mini UAT */
 101         arm_reg_write(AUX_BASE + AUX_ENABLES, 0x1);
 102 
 103         /* Disable interrupts */
 104         arm_reg_write(AUX_BASE + AUX_MU_IER_REG, 0x0);
 105 
 106         /* Disable the RX and TX */
 107         arm_reg_write(AUX_BASE + AUX_MU_CNTL_REG, 0x0);
 108 
 109         /*
 110          * Enable 8-bit word length. External sources tell us the PRM is buggy
 111          * here and that even though bit 1 is reserved, we need to actually set
 112          * it to get 8-bit words.
 113          */
 114         arm_reg_write(AUX_BASE + AUX_MU_LCR_REG, 0x3);
 115 
 116         /* Set RTS high */
 117         arm_reg_write(AUX_BASE + AUX_MU_MCR_REG, 0x0);
 118 
 119         /* Disable interrupts */
 120         arm_reg_write(AUX_BASE + AUX_MU_IER_REG, 0x0);
 121 
 122         /* Set baud rate */
 123         arm_reg_write(AUX_BASE + AUX_MU_IIR_REG, 0xc6);
 124         arm_reg_write(AUX_BASE + AUX_MU_BAUD, 0x10e);
 125 
 126         /* TODO: Factor out the gpio bits */
 127         v = arm_reg_read(GPIO_BASE + GPIO_FSEL1);
 128         v &= GPIO_UART_MASK;
 129         v |= GPIO_SEL_ALT5 << GPIO_UART_RX_SHIFT;
 130         v |= GPIO_SEL_ALT5 << GPIO_UART_TX_SHIFT;
 131         arm_reg_write(GPIO_BASE + GPIO_FSEL1, v);
 132 
 133         arm_reg_write(GPIO_BASE + GPIO_PUD, GPIO_PUD_DISABLE);
 134         for (i = 0; i < 150; i++)
 135                 bcm2835_miniuart_nop();
 136         arm_reg_write(GPIO_BASE + GPIO_PUDCLK0, GPIO_PUDCLK_UART);
 137         for (i = 0; i < 150; i++)
 138                 bcm2835_miniuart_nop();
 139         // XXX: GPIO_PUD_DISABLE again?
 140         arm_reg_write(GPIO_BASE + GPIO_PUDCLK0, 0);
 141 
 142         /* Finally, go back and enable RX and TX */
 143         arm_reg_write(AUX_BASE + AUX_MU_CNTL_REG, 0x3);











 144 }
 145 
 146 void
 147 fakeload_backend_putc(int c)
 148 {
 149         if (c == '\n')
 150                 fakeload_backend_putc('\r');
 151 
 152         for (;;) {
 153                 if (arm_reg_read(AUX_BASE + AUX_MU_LSR_REG) & AUX_MU_TX_READY)
 154                         break;
 155         }
 156         arm_reg_write(AUX_BASE + AUX_MU_IO_REG, c & 0x7f);
 157 }
 158 
 159 /*
 160  * Add a map for the uart.
 161  */
 162 void
 163 fakeload_backend_addmaps(atag_header_t *chain)
 164 {
 165         atag_illumos_mapping_t aim;
 166 
 167         aim.aim_header.ah_size = ATAG_ILLUMOS_MAPPING_SIZE;
 168         aim.aim_header.ah_tag = ATAG_ILLUMOS_MAPPING;
 169         aim.aim_paddr = GPIO_BASE;
 170         aim.aim_vaddr = GPIO_BASE;
 171         aim.aim_vlen = 0x1000;
 172         aim.aim_plen = 0x1000;
 173         aim.aim_mapflags = PF_R | PF_W | PF_NORELOC | PF_DEVICE;
 174         atag_append(chain, &aim.aim_header);
 175 
 176         aim.aim_header.ah_size = ATAG_ILLUMOS_MAPPING_SIZE;
 177         aim.aim_header.ah_tag = ATAG_ILLUMOS_MAPPING;
 178         aim.aim_paddr = AUX_BASE;
 179         aim.aim_vaddr = AUX_BASE;
 180         aim.aim_vlen = 0x1000;
 181         aim.aim_plen = 0x1000;
 182         aim.aim_mapflags = PF_R | PF_W | PF_NORELOC | PF_DEVICE;
 183         atag_append(chain, &aim.aim_header);
 184 }
   1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright (c) 2013 Joyent, Inc.  All rights reserved.
  14  * Copyright (c) 2015 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
  15  */
  16 
  17 #include <sys/elf.h>
  18 #include <sys/atag.h>
  19 
  20 /*
  21  * The primary serial console that we end up using is the normal UART, not
  22  * the mini-uart that shares interrupts and registers with the SPI masters
  23  * as well.


  24  */
  25 
  26 #define UART_BASE               0x20201000
  27 #define UART_DR                 0x0
  28 #define UART_FR                 0x18
  29 #define UART_IBRD               0x24
  30 #define UART_FBRD               0x28
  31 #define UART_LCRH               0x2c
  32 #define UART_CR                 0x30
  33 #define UART_ICR                0x44
  34 
  35 #define UART_FR_RXFE            0x10            /* RX fifo empty */
  36 #define UART_FR_TXFF            0x20            /* TX fifo full */
  37 
  38 #define UART_LCRH_FEN           0x00000010      /* fifo enable */
  39 #define UART_LCRH_WLEN_8        0x00000060      /* 8 bits */
  40 
  41 #define UART_CR_UARTEN          0x001           /* uart enable */
  42 #define UART_CR_TXE             0x100           /* TX enable */
  43 #define UART_CR_RXE             0x200           /* RX enable */
  44 


  45 
  46 /*
  47  * All we care about are pins 14 and 15 for the UART.  Specifically, alt0
  48  * for GPIO14 is TXD0 and GPIO15 is RXD0. Those are controlled by FSEL1.

  49  */
  50 #define GPIO_BASE       0x20200000
  51 #define GPIO_FSEL1      0x4
  52 #define GPIO_PUD        0x94
  53 #define GPIO_PUDCLK0    0x98
  54 
  55 #define GPIO_SEL_ALT0   0x4
  56 #define GPIO_UART_MASK  0xfffc0fff
  57 #define GPIO_UART_TX_SHIFT      12
  58 #define GPIO_UART_RX_SHIFT      15
  59 
  60 #define GPIO_PUD_DISABLE        0x0
  61 #define GPIO_PUDCLK_UART        0x0000c000
  62 
  63 static __GNU_INLINE uint32_t arm_reg_read(uint32_t reg)
  64 {
  65         volatile uint32_t *ptr = (volatile uint32_t *)reg;
  66 
  67         return *ptr;
  68 }
  69 
  70 static __GNU_INLINE void arm_reg_write(uint32_t reg, uint32_t val)
  71 {
  72         volatile uint32_t *ptr = (volatile uint32_t *)reg;
  73 
  74         *ptr = val;
  75 }
  76 
  77 /*
  78  * A simple nop
  79  */
  80 static void
  81 uart_nop(void)
  82 {
  83         __asm__ volatile("mov r0, r0\n" : : :);
  84 }
  85 
  86 void fakeload_backend_putc(int);
  87 
  88 static void
  89 fakeload_puts(const char *str)
  90 {
  91         while (*str != '\0') {
  92                 fakeload_backend_putc(*str);
  93                 str++;
  94         }
  95 }
  96 
  97 void
  98 fakeload_backend_init(void)
  99 {
 100         uint32_t v;
 101         int i;
 102 
 103         /* disable UART */
 104         arm_reg_write(UART_BASE + UART_CR, 0);























 105 
 106         /* TODO: Factor out the gpio bits */
 107         v = arm_reg_read(GPIO_BASE + GPIO_FSEL1);
 108         v &= GPIO_UART_MASK;
 109         v |= GPIO_SEL_ALT0 << GPIO_UART_RX_SHIFT;
 110         v |= GPIO_SEL_ALT0 << GPIO_UART_TX_SHIFT;
 111         arm_reg_write(GPIO_BASE + GPIO_FSEL1, v);
 112 
 113         arm_reg_write(GPIO_BASE + GPIO_PUD, GPIO_PUD_DISABLE);
 114         for (i = 0; i < 150; i++)
 115                 uart_nop();
 116         arm_reg_write(GPIO_BASE + GPIO_PUDCLK0, GPIO_PUDCLK_UART);
 117         for (i = 0; i < 150; i++)
 118                 uart_nop();

 119         arm_reg_write(GPIO_BASE + GPIO_PUDCLK0, 0);
 120 
 121         /* clear all interrupts */
 122         arm_reg_write(UART_BASE + UART_ICR, 0x7ff);
 123 
 124         /* set the baud rate */
 125         arm_reg_write(UART_BASE + UART_IBRD, 1);
 126         arm_reg_write(UART_BASE + UART_FBRD, 40);
 127 
 128         /* select 8-bit, enable FIFOs */
 129         arm_reg_write(UART_BASE + UART_LCRH, UART_LCRH_WLEN_8 | UART_LCRH_FEN);
 130 
 131         /* enable UART */
 132         arm_reg_write(UART_BASE + UART_CR, UART_CR_UARTEN | UART_CR_TXE |
 133             UART_CR_RXE);
 134 }
 135 
 136 void
 137 fakeload_backend_putc(int c)
 138 {
 139         if (c == '\n')
 140                 fakeload_backend_putc('\r');
 141 
 142         while (arm_reg_read(UART_BASE + UART_FR) & UART_FR_TXFF)
 143                 ;
 144         arm_reg_write(UART_BASE + UART_DR, c & 0x7f);
 145         if (c == '\n')
 146                 fakeload_backend_putc('\r');
 147 }
 148 
 149 /*
 150  * Add a map for the uart.
 151  */
 152 void
 153 fakeload_backend_addmaps(atag_header_t *chain)
 154 {
 155         atag_illumos_mapping_t aim;
 156 
 157         aim.aim_header.ah_size = ATAG_ILLUMOS_MAPPING_SIZE;
 158         aim.aim_header.ah_tag = ATAG_ILLUMOS_MAPPING;
 159         aim.aim_paddr = GPIO_BASE;
 160         aim.aim_vaddr = GPIO_BASE;
 161         aim.aim_vlen = 0x1000;
 162         aim.aim_plen = 0x1000;
 163         aim.aim_mapflags = PF_R | PF_W | PF_NORELOC | PF_DEVICE;
 164         atag_append(chain, &aim.aim_header);
 165 
 166         aim.aim_header.ah_size = ATAG_ILLUMOS_MAPPING_SIZE;
 167         aim.aim_header.ah_tag = ATAG_ILLUMOS_MAPPING;
 168         aim.aim_paddr = UART_BASE;
 169         aim.aim_vaddr = UART_BASE;
 170         aim.aim_vlen = 0x1000;
 171         aim.aim_plen = 0x1000;
 172         aim.aim_mapflags = PF_R | PF_W | PF_NORELOC | PF_DEVICE;
 173         atag_append(chain, &aim.aim_header);
 174 }