1/** 2 * \file 3 * \brief PC16550 low-level kernel UART driver 4 * 5 * AB's quick-and-nasty serial driver. 6 */ 7 8/* 9 * Copyright (c) 2007, 2008, 2010, 2012, ETH Zurich. 10 * All rights reserved. 11 * 12 * This file is distributed under the terms in the attached LICENSE file. 13 * If you do not find this file, copies can be found by writing to: 14 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group. 15 */ 16 17#include <kernel.h> 18#include <x86.h> 19#include <serial.h> 20#include <dev/pc16550d_dev.h> 21 22int serial_portbase = 0x3f8; // COM1 default, can be changed via command-line arg 23 24#define NIBBLE 4 25#define HEX2ASCII 0x30 26#define HEXLETTER 0x3a 27#define HEXCORRECTION 0x7 28 29#define NUM_PORTS 2 30unsigned serial_console_port = 0; 31unsigned serial_debug_port = 0; 32unsigned serial_num_physical_ports = NUM_PORTS; 33 34// Note: hardwired for PC hardware 35static const uint32_t portbases[NUM_PORTS] = { 0x3f8, 0x2f8 }; 36static pc16550d_t ports[NUM_PORTS]; 37 38/** 39 * \brief Initialise the serial driver. 40 * 41 * \param port Serial Port number. 42 * \param initialize_hw Should the Hardware be initialized or just 43 * mackerel representation. 44 */ 45errval_t serial_init(unsigned port, bool initialize_hw) 46{ 47 if (port >= NUM_PORTS) { 48 return SYS_ERR_SERIAL_PORT_INVALID; 49 }; 50 51 // XXX Backwards compatibility! 52 if (serial_portbase != 0x3f8 && port == 0) { 53 // We're trying to initialize the console port on a machine 54 // which uses the second UART, but the CPU driver doesn't 55 // understand the new serial interface yet. Kluge this into 56 // using the second UART, and set the console and debug ports 57 // accordingly. 58 serial_console_port = 1; 59 serial_debug_port = 1; 60 port = 1; 61 } 62 63 pc16550d_t *uart = &ports[port]; 64 pc16550d_initialize(uart, portbases[port]); 65 66 if (!initialize_hw) { 67 // HW is already initialized, usually APP cores come here 68 // while the BSP core do the initialization once. 69 // If the initialization happens twice, 70 // it can manifest in the keyboard not working 71 // because we disable uart interrupts and 72 // the serial driver was started in the meantime 73 return SYS_ERR_OK; 74 } 75 76 // Initialize UART 77 // disable interrupt 78 pc16550d_ier_t ier = pc16550d_ier_default; 79 ier = pc16550d_ier_erbfi_insert(ier, 0); 80 pc16550d_ier_wr(uart, ier); 81 82 // enable FIFOs 83 pc16550d_fcr_t fcr = pc16550d_fcr_default; 84 fcr = pc16550d_fcr_fifoe_insert(fcr, 1); 85 // FIFOs hold 14 bytes 86 fcr = pc16550d_fcr_rtrigger_insert(fcr, pc16550d_bytes14); 87 pc16550d_fcr_wr(uart, fcr); 88 89 pc16550d_lcr_t lcr = pc16550d_lcr_default; 90 lcr = pc16550d_lcr_wls_insert(lcr, pc16550d_bits8); // 8 data bits 91 lcr = pc16550d_lcr_stb_insert(lcr, 1); // 1 stop bit 92 lcr = pc16550d_lcr_pen_insert(lcr, 0); // no parity 93 pc16550d_lcr_wr(uart, lcr); 94 95 // set data terminal ready 96 pc16550d_mcr_t mcr = pc16550d_mcr_default; 97 mcr = pc16550d_mcr_dtr_insert(mcr, 1); 98 mcr = pc16550d_mcr_out_insert(mcr, 2); 99 pc16550d_mcr_wr(uart, mcr); 100 101 // Set baudrate (XXX: hard-coded to 115200) 102 if (!CPU_IS_M5_SIMULATOR) { 103 pc16550d_lcr_dlab_wrf(uart, 1); 104 pc16550d_dl_wr(uart, pc16550d_baud115200); 105 pc16550d_lcr_dlab_wrf(uart, 0); 106 } 107 108 return SYS_ERR_OK; 109} 110 111errval_t serial_early_init(unsigned port) 112{ 113 return SYS_ERR_OK; 114} 115 116/** \brief Prints a single character to the default serial port. */ 117void serial_putchar(unsigned port, char c) 118{ 119 assert(port < NUM_PORTS); 120 assert(ports[port].base != 0); 121 // Wait until FIFO can hold more characters 122 while(!pc16550d_lsr_thre_rdf(&ports[port])); 123 // Write character 124 pc16550d_thr_wr(&ports[port], c); 125} 126 127/** \brief Reads a single character from the default serial port. 128 * This function spins waiting for a character to arrive. 129 */ 130char serial_getchar(unsigned port) 131{ 132 assert(port < NUM_PORTS); 133 assert(ports[port].base != 0); 134 135 // Read a character from FIFO 136 while( !pc16550d_lsr_dr_rdf(&ports[port])); 137 return pc16550d_rbr_rd(&ports[port]); 138} 139