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 }