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 2013 (c) Joyent, Inc.  All rights reserved.
  14  */
  15 
  16 /*
  17  * A simple uart driver for the RPi.
  18  */
  19 #include <sys/types.h>
  20 
  21 extern uint32_t arm_reg_read(uint32_t);
  22 extern void arm_reg_write(uint32_t, uint32_t);
  23 
  24 /*
  25  * The primary serial console that we end up using is not in fact a normal UART,
  26  * but is instead actually a mini-uart that shares interrupts and registers with
  27  * the SPI masters as well. While the RPi also supports another more traditional
  28  * UART, that isn't what we are actually hooking up to generally with the
  29  * adafruit cable. We already wasted our time having to figure that out. -_-
  30  */
  31 
  32 #define AUX_BASE        0x20215000
  33 #define AUX_ENABLES     0x4
  34 #define AUX_MU_IO_REG   0x40
  35 #define AUX_MU_IER_REG  0x44
  36 #define AUX_MU_IIR_REG  0x48
  37 #define AUX_MU_LCR_REG  0x4C
  38 #define AUX_MU_MCR_REG  0x50
  39 #define AUX_MU_LSR_REG  0x54
  40 #define AUX_MU_CNTL_REG 0x60
  41 #define AUX_MU_BAUD     0x68
  42 
  43 #define AUX_MU_RX_READY 0x01
  44 #define AUX_MU_TX_READY 0x20
  45 
  46 /*
  47  * For the mini UART, all we care about are pins 14 and 15 for the UART.
  48  * Specifically, alt5 for GPIO14 is TXD1 and GPIO15 is RXD1. Those are
  49  * controlled by FSEL1.
  50  */
  51 #define GPIO_BASE       0x20200000
  52 #define GPIO_FSEL1      0x4
  53 #define GPIO_PUD        0x94
  54 #define GPIO_PUDCLK0    0x98
  55 
  56 #define GPIO_SEL_ALT5   0x2
  57 #define GPIO_UART_MASK  0xfffc0fff
  58 #define GPIO_UART_TX_SHIFT      12
  59 #define GPIO_UART_RX_SHIFT      15
  60 
  61 #define GPIO_PUD_DISABLE        0x0
  62 #define GPIO_PUDCLK_UART        0x0000c000
  63 
  64 /*
  65  * A simple nop
  66  */
  67 static void
  68 bcm2835_miniuart_nop(void)
  69 {
  70         __asm__ volatile("mov r0, r0\n" : : :);
  71 }
  72 
  73 void
  74 bcm2835_miniuart_init(void)
  75 {
  76         uint32_t v;
  77         int i;
  78 
  79         /* Enable the mini UAT */
  80         arm_reg_write(AUX_BASE + AUX_ENABLES, 0x1);
  81 
  82         /* Disable interrupts */
  83         arm_reg_write(AUX_BASE + AUX_MU_IER_REG, 0x0);
  84 
  85         /* Disable the RX and TX */
  86         arm_reg_write(AUX_BASE + AUX_MU_CNTL_REG, 0x0);
  87 
  88         /*
  89          * Enable 8-bit word length. External sources tell us the PRM is buggy
  90          * here and that even though bit 1 is reserved, we need to actually set
  91          * it to get 8-bit words.
  92          */
  93         arm_reg_write(AUX_BASE + AUX_MU_LCR_REG, 0x3);
  94 
  95         /* Set RTS high */
  96         arm_reg_write(AUX_BASE + AUX_MU_MCR_REG, 0x0);
  97 
  98         /* Disable interrupts */
  99         arm_reg_write(AUX_BASE + AUX_MU_IER_REG, 0x0);
 100 
 101         /* Set baud rate */
 102         arm_reg_write(AUX_BASE + AUX_MU_IIR_REG, 0xc6);
 103         arm_reg_write(AUX_BASE + AUX_MU_BAUD, 0x10e);
 104 
 105         /* TODO: Factor out the gpio bits */
 106         v = arm_reg_read(GPIO_BASE + GPIO_FSEL1);
 107         v &= GPIO_UART_MASK;
 108         v |= GPIO_SEL_ALT5 << GPIO_UART_RX_SHIFT;
 109         v |= GPIO_SEL_ALT5 << GPIO_UART_TX_SHIFT;
 110         arm_reg_write(GPIO_BASE + GPIO_FSEL1, v);
 111 
 112         arm_reg_write(GPIO_BASE + GPIO_PUD, GPIO_PUD_DISABLE);
 113         for (i = 0; i < 150; i++)
 114                 bcm2835_miniuart_nop();
 115         arm_reg_write(GPIO_BASE + GPIO_PUDCLK0, GPIO_PUDCLK_UART);
 116         for (i = 0; i < 150; i++)
 117                 bcm2835_miniuart_nop();
 118         arm_reg_write(GPIO_BASE + GPIO_PUDCLK0, 0);
 119 
 120         /* Finally, go back and enable RX and TX */
 121         arm_reg_write(AUX_BASE + AUX_MU_CNTL_REG, 0x3);
 122 }
 123 
 124 void
 125 bcm2835_miniuart_putc(uint8_t c)
 126 {
 127         for (;;) {
 128                 if (arm_reg_read(AUX_BASE + AUX_MU_LSR_REG) & AUX_MU_TX_READY)
 129                         break;
 130         }
 131         arm_reg_write(AUX_BASE + AUX_MU_IO_REG, c & 0x7f);
 132         if (c == '\n')
 133                 bcm2835_miniuart_putc('\r');
 134 }
 135 
 136 uint8_t
 137 bcm2835_miniuart_getc(void)
 138 {
 139         for (;;) {
 140                 if (arm_reg_read(AUX_BASE + AUX_MU_LSR_REG) & AUX_MU_RX_READY)
 141                         break;
 142         }
 143         return (arm_reg_read(AUX_BASE + AUX_MU_IO_REG) & 0x7f);
 144 }
 145 
 146 int
 147 bcm2835_miniuart_isc(void)
 148 {
 149         return (arm_reg_read(AUX_BASE + AUX_MU_LSR_REG) & AUX_MU_RX_READY);
 150 }