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 }