1/* 2 * Copyright 2017, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 119 230. 5 * 6 * This software may be distributed and modified according to the terms of 7 * the BSD 2-Clause license. Note that NO WARRANTY is provided. 8 * See "LICENSE_BSD2.txt" for details. 9 * 10 * @TAG(DATA61_BSD) 11 */ 12 13#include <stdlib.h> 14#include <string.h> 15#include <utils/util.h> 16 17#include "../../chardev.h" 18 19/* 20 * Port offsets 21 * W - write 22 * R - read 23 * RW - read and write 24 * DLAB - Alternate register function bit 25 */ 26 27#define SERIAL_THR 0 /* Transmitter Holding Buffer (W ) DLAB = 0 */ 28#define SERIAL_RBR 0 /* Receiver Buffer (R ) DLAB = 0 */ 29#define SERIAL_DLL 0 /* Divisor Latch Low Byte (RW) DLAB = 1 */ 30#define SERIAL_IER 1 /* Interrupt Enable Register (RW) DLAB = 0 */ 31#define SERIAL_DLH 1 /* Divisor Latch High Byte (RW) DLAB = 1 */ 32#define SERIAL_IIR 2 /* Interrupt Identification (R ) */ 33#define SERIAL_FCR 2 /* FIFO Control Register (W ) */ 34#define SERIAL_LCR 3 /* Line Control Register (RW) */ 35#define SERIAL_MCR 4 /* Modem Control Register (RW) */ 36#define SERIAL_LSR 5 /* Line Status Register (R ) */ 37#define SERIAL_MSR 6 /* Modem Status Register (R ) */ 38#define SERIAL_SR 7 /* Scratch Register (RW) */ 39#define CONSOLE(port, label) ((port) + (SERIAL_##label)) 40#define SERIAL_DLAB BIT(7) 41#define SERIAL_LSR_DATA_READY BIT(0) 42#define SERIAL_LSR_TRANSMITTER_EMPTY BIT(5) 43 44int uart_getchar(ps_chardevice_t *device) 45{ 46 uint32_t res; 47 uint32_t io_port = (uint32_t) (uintptr_t)device->vaddr; 48 49 /* Check if character is available. */ 50 int error = ps_io_port_in(&device->ioops.io_port_ops, CONSOLE(io_port, LSR), 1, &res); 51 if (error != 0) { 52 return -1; 53 } 54 if (!(res & SERIAL_LSR_DATA_READY)) { 55 return -1; 56 } 57 58 /* retrieve character */ 59 error = ps_io_port_in(&device->ioops.io_port_ops, CONSOLE(io_port, RBR), 1, &res); 60 if (error != 0) { 61 return -1; 62 } 63 64 return (int) res; 65} 66 67static int serial_ready(ps_chardevice_t* device) 68{ 69 uint32_t io_port = (uint32_t) (uintptr_t)device->vaddr; 70 uint32_t res; 71 int error = ps_io_port_in(&device->ioops.io_port_ops, CONSOLE(io_port, LSR), 1, &res); 72 if (error != 0) { 73 return 0; 74 } 75 return res & SERIAL_LSR_TRANSMITTER_EMPTY; 76} 77 78int uart_putchar(ps_chardevice_t* device, int c) 79{ 80 uint32_t io_port = (uint32_t) (uintptr_t)device->vaddr; 81 82 /* Check if serial is ready. */ 83 if (!serial_ready(device)) { 84 return -1; 85 } 86 87 /* Write out the next character. */ 88 ps_io_port_out(&device->ioops.io_port_ops, CONSOLE(io_port, THR), 1, c); 89 90 if (c == '\n') { 91 /* If we output immediately then odds are the transmit buffer 92 * will be full, so we have to wait */ 93 while (!serial_ready(device)); 94 uart_putchar(device, '\r'); 95 } 96 97 return c; 98} 99 100static void uart_handle_irq(ps_chardevice_t* device UNUSED) 101{ 102 /* No IRQ handling required here. */ 103} 104 105int 106uart_init(const struct dev_defn* defn, const ps_io_ops_t* ops, ps_chardevice_t* dev) 107{ 108 memset(dev, 0, sizeof(*dev)); 109 /* Set up all the device properties. */ 110 dev->id = defn->id; 111 dev->vaddr = (void*) defn->paddr; /* Save the IO port base number. */ 112 dev->read = &uart_read; 113 dev->write = &uart_write; 114 dev->handle_irq = &uart_handle_irq; 115 dev->irqs = defn->irqs; 116 dev->ioops = *ops; 117 118 /* Initialise the device. */ 119 uint32_t io_port = (uint32_t) (uintptr_t)dev->vaddr; 120 121 /* clear DLAB - Divisor Latch Access Bit */ 122 if (ps_io_port_out(&dev->ioops.io_port_ops, CONSOLE(io_port, LCR), 1, 0x00 & ~SERIAL_DLAB) != 0) { 123 return -1; 124 } 125 126 /* disable generating interrupts */ 127 if (ps_io_port_out(&dev->ioops.io_port_ops, CONSOLE(io_port, IER), 1, 0x00) != 0) { 128 return -1; 129 } 130 131 /* set DLAB to*/ 132 if (ps_io_port_out(&dev->ioops.io_port_ops, CONSOLE(io_port, LCR), 1, 0x00 | SERIAL_DLAB) != 0) { 133 return -1; 134 } 135 /* set low byte of divisor to 0x01 = 115200 baud */ 136 if (ps_io_port_out(&dev->ioops.io_port_ops, CONSOLE(io_port, DLL), 1, 0x01) != 0) { 137 return -1; 138 } 139 /* set high byte of divisor to 0x00 */ 140 if (ps_io_port_out(&dev->ioops.io_port_ops, CONSOLE(io_port, DLH), 1, 0x00) != 0) { 141 return -1; 142 } 143 144 /* line control register: set 8 bit, no parity, 1 stop bit; clear DLAB */ 145 if (ps_io_port_out(&dev->ioops.io_port_ops, CONSOLE(io_port, LCR), 1, 0x03 & ~SERIAL_DLAB) != 0) { 146 return -1; 147 } 148 /* modem control register: set DTR/RTS/OUT2 */ 149 if (ps_io_port_out(&dev->ioops.io_port_ops, CONSOLE(io_port, MCR), 1, 0x0b) != 0) { 150 return -1; 151 } 152 153 uint32_t temp; 154 /* clear receiver port */ 155 if (ps_io_port_in(&dev->ioops.io_port_ops, CONSOLE(io_port, RBR), 1, &temp) != 0) { 156 return -1; 157 } 158 /* clear line status port */ 159 if (ps_io_port_in(&dev->ioops.io_port_ops, CONSOLE(io_port, LSR), 1, &temp) != 0) { 160 return -1; 161 } 162 /* clear modem status port */ 163 if (ps_io_port_in(&dev->ioops.io_port_ops, CONSOLE(io_port, MSR), 1, &temp) != 0) { 164 return -1; 165 } 166 167 /* Enable the receiver interrupt. */ 168 if (ps_io_port_out(&dev->ioops.io_port_ops, CONSOLE(io_port, IER), 1, 0x01) != 0) { 169 return -1; 170 } 171 172 return 0; 173} 174