1/* 2 * Copyright 2020, DornerWorks 3 * 4 * This software may be distributed and modified according to the terms of 5 * the BSD 2-Clause license. Note that NO WARRANTY is provided. 6 * See "LICENSE_BSD2.txt" for details. 7 * 8 * @TAG(DORNERWORKS_BSD) 9 */ 10 11#include <autoconf.h> 12 13#include <stdlib.h> 14#include <platsupport/serial.h> 15#include <platsupport/plat/serial.h> 16#include <platsupport/plat/icicle_mss.h> 17#include <string.h> 18 19#include "../../chardev.h" 20 21static inline uart_regs_t *uart_get_priv(ps_chardevice_t *d) 22{ 23 return (uart_regs_t *)d->vaddr; 24} 25 26int uart_getchar(ps_chardevice_t *d) 27{ 28 uart_regs_t *regs = uart_get_priv(d); 29 if (regs->line_status & LSR_DATA_READY_MASK) { 30 return regs->rx_buffer; 31 } 32 return -1; 33} 34 35static void busy_wait_fifo_empty_and_tx_char(uart_regs_t *regs, int c) 36{ 37 // wait until FIFO empty 38 while ((regs->line_status & LSR_TX_HOLD_REG_EMPTY_MASK) == 0) { 39 // busy loop 40 } 41 42 regs->tx_buffer = c; 43} 44 45int uart_putchar(ps_chardevice_t *d, int c) 46{ 47 uart_regs_t *regs = uart_get_priv(d); 48 49 // turn LF into CR+LF if SERIAL_AUTO_CR is active 50 if ((c == '\n') && (d->flags & SERIAL_AUTO_CR)) { 51 busy_wait_fifo_empty_and_tx_char(regs, '\r'); 52 } 53 54 busy_wait_fifo_empty_and_tx_char(regs, c); 55 return c; 56} 57 58static void uart_handle_irq(ps_chardevice_t *d UNUSED) 59{ 60 // IRQs are cleared when the TX/RX watermark conditions are no longer met 61 // so there is nothing to do here. 62} 63 64int uart_init(const struct dev_defn *defn, 65 const ps_io_ops_t *ops, 66 ps_chardevice_t *dev) 67{ 68 uart_regs_t *regs; 69 uint32_t divisor; 70 uint32_t divisor_by_128; 71 uint32_t divisor_by_64; 72 uint32_t fractional_divisor; 73 /* Attempt to map the virtual address, assure this works */ 74 void *vaddr = chardev_map(defn, ops); 75 if (vaddr == NULL) { 76 return -1; 77 } 78 79 memset(dev, 0, sizeof(*dev)); 80 81 /* Set up all the device properties. */ 82 dev->id = defn->id; 83 dev->vaddr = (void *)vaddr; 84 dev->read = &uart_read; 85 dev->write = &uart_write; 86 dev->handle_irq = &uart_handle_irq; 87 dev->irqs = defn->irqs; 88 dev->ioops = *ops; 89 dev->flags = SERIAL_AUTO_CR; 90 91 regs = uart_get_priv(dev); 92 93 regs->line_control = LCR_WORD_LEN_8; 94 regs->modem_control = 0; 95 regs->multi_mode_control_0 = 0; 96 regs->multi_mode_control_1 = 0; 97 regs->multi_mode_control_2 = 0; 98 regs->glitch_filter = 0; 99 regs->transmitter_time_guard = 0; 100 regs->receiver_time_out = 0; 101 102 /* 103 * Configure baud rate divisors. This uses the fractional baud rate divisor 104 * where possible to provide the most accurate baud rate possible. 105 * This algorithm is taken from the Microchip MSS UART Driver (LIC:MIT) 106 */ 107 divisor_by_128 = (uint32_t)((8UL * POLARFIRE_PCLK) / POLARFIRE_BAUD); 108 divisor_by_64 = divisor_by_128 / 2u; 109 divisor = divisor_by_64 / 64u; 110 fractional_divisor = divisor_by_64 - (divisor * 64u); 111 fractional_divisor += (divisor_by_128 - (divisor * 128u)) 112 - (fractional_divisor * 2u); 113 114 // div: 81, frac: 24 => 115207 115 116 regs->line_control |= LCR_DIV_LATCH_MASK; 117 regs->divisor_latch_msb = (uint8_t)(divisor >> 8); 118 regs->divisor_latch_lsb = (uint8_t)divisor; 119 regs->line_control &= ~LCR_DIV_LATCH_MASK; 120 121 regs->multi_mode_control_0 |= MM0_ENABLE_FRAC_MASK; 122 regs->fractional_divisor = (uint8_t)fractional_divisor; 123 regs->line_control = LCR_WORD_LEN_8; 124 125 return 0; 126} 127