199461Sobrien/* 299461Sobrien * Copyright 2020, DornerWorks 399461Sobrien * 499461Sobrien * This software may be distributed and modified according to the terms of 599461Sobrien * the BSD 2-Clause license. Note that NO WARRANTY is provided. 699461Sobrien * See "LICENSE_BSD2.txt" for details. 799461Sobrien * 899461Sobrien * @TAG(DORNERWORKS_BSD) 999461Sobrien */ 1099461Sobrien 1199461Sobrien#include <autoconf.h> 1299461Sobrien 1399461Sobrien#include <stdlib.h> 1499461Sobrien#include <platsupport/serial.h> 1599461Sobrien#include <platsupport/plat/serial.h> 1699461Sobrien#include <platsupport/plat/icicle_mss.h> 1799461Sobrien#include <string.h> 1899461Sobrien 1999461Sobrien#include "../../chardev.h" 2099461Sobrien 2199461Sobrienstatic inline uart_regs_t *uart_get_priv(ps_chardevice_t *d) 2299461Sobrien{ 2399461Sobrien return (uart_regs_t *)d->vaddr; 2499461Sobrien} 2599461Sobrien 2699461Sobrienint uart_getchar(ps_chardevice_t *d) 2799461Sobrien{ 2899461Sobrien uart_regs_t *regs = uart_get_priv(d); 2999461Sobrien if (regs->line_status & LSR_DATA_READY_MASK) { 3099461Sobrien return regs->rx_buffer; 3199461Sobrien } 3299461Sobrien return -1; 3399461Sobrien} 3499461Sobrien 3599461Sobrienstatic void busy_wait_fifo_empty_and_tx_char(uart_regs_t *regs, int c) 3699461Sobrien{ 3799461Sobrien // wait until FIFO empty 3899461Sobrien while ((regs->line_status & LSR_TX_HOLD_REG_EMPTY_MASK) == 0) { 3999461Sobrien // busy loop 4099461Sobrien } 4199461Sobrien 4299461Sobrien regs->tx_buffer = c; 4399461Sobrien} 4499461Sobrien 4599461Sobrienint uart_putchar(ps_chardevice_t *d, int c) 4699461Sobrien{ 4799461Sobrien uart_regs_t *regs = uart_get_priv(d); 4899461Sobrien 4999461Sobrien // turn LF into CR+LF if SERIAL_AUTO_CR is active 5099461Sobrien if ((c == '\n') && (d->flags & SERIAL_AUTO_CR)) { 5199461Sobrien busy_wait_fifo_empty_and_tx_char(regs, '\r'); 5299461Sobrien } 5399461Sobrien 5499461Sobrien busy_wait_fifo_empty_and_tx_char(regs, c); 5599461Sobrien return c; 5699461Sobrien} 5799461Sobrien 5899461Sobrienstatic void uart_handle_irq(ps_chardevice_t *d UNUSED) 5999461Sobrien{ 6099461Sobrien // IRQs are cleared when the TX/RX watermark conditions are no longer met 6199461Sobrien // so there is nothing to do here. 6299461Sobrien} 6399461Sobrien 6499461Sobrienint uart_init(const struct dev_defn *defn, 6599461Sobrien const ps_io_ops_t *ops, 6699461Sobrien ps_chardevice_t *dev) 6799461Sobrien{ 6899461Sobrien uart_regs_t *regs; 6999461Sobrien uint32_t divisor; 7099461Sobrien uint32_t divisor_by_128; 7199461Sobrien uint32_t divisor_by_64; 7299461Sobrien uint32_t fractional_divisor; 7399461Sobrien /* Attempt to map the virtual address, assure this works */ 7499461Sobrien void *vaddr = chardev_map(defn, ops); 7599461Sobrien if (vaddr == NULL) { 7699461Sobrien return -1; 7799461Sobrien } 7899461Sobrien 7999461Sobrien memset(dev, 0, sizeof(*dev)); 8099461Sobrien 8199461Sobrien /* Set up all the device properties. */ 8299461Sobrien dev->id = defn->id; 8399461Sobrien dev->vaddr = (void *)vaddr; 8499461Sobrien dev->read = &uart_read; 8599461Sobrien dev->write = &uart_write; 8699461Sobrien dev->handle_irq = &uart_handle_irq; 8799461Sobrien dev->irqs = defn->irqs; 8899461Sobrien dev->ioops = *ops; 8999461Sobrien dev->flags = SERIAL_AUTO_CR; 9099461Sobrien 9199461Sobrien regs = uart_get_priv(dev); 9299461Sobrien 9399461Sobrien regs->line_control = LCR_WORD_LEN_8; 9499461Sobrien regs->modem_control = 0; 9599461Sobrien regs->multi_mode_control_0 = 0; 9699461Sobrien regs->multi_mode_control_1 = 0; 9799461Sobrien regs->multi_mode_control_2 = 0; 9899461Sobrien regs->glitch_filter = 0; 9999461Sobrien regs->transmitter_time_guard = 0; 10099461Sobrien regs->receiver_time_out = 0; 10199461Sobrien 10299461Sobrien /* 10399461Sobrien * Configure baud rate divisors. This uses the fractional baud rate divisor 10499461Sobrien * where possible to provide the most accurate baud rate possible. 10599461Sobrien * This algorithm is taken from the Microchip MSS UART Driver (LIC:MIT) 10699461Sobrien */ 10799461Sobrien divisor_by_128 = (uint32_t)((8UL * POLARFIRE_PCLK) / POLARFIRE_BAUD); 10899461Sobrien divisor_by_64 = divisor_by_128 / 2u; 10999461Sobrien divisor = divisor_by_64 / 64u; 11099461Sobrien fractional_divisor = divisor_by_64 - (divisor * 64u); 11199461Sobrien fractional_divisor += (divisor_by_128 - (divisor * 128u)) 11299461Sobrien - (fractional_divisor * 2u); 11399461Sobrien 11499461Sobrien // div: 81, frac: 24 => 115207 11599461Sobrien 11699461Sobrien regs->line_control |= LCR_DIV_LATCH_MASK; 11799461Sobrien regs->divisor_latch_msb = (uint8_t)(divisor >> 8); 11899461Sobrien regs->divisor_latch_lsb = (uint8_t)divisor; 11999461Sobrien regs->line_control &= ~LCR_DIV_LATCH_MASK; 12099461Sobrien 12199461Sobrien regs->multi_mode_control_0 |= MM0_ENABLE_FRAC_MASK; 12299461Sobrien regs->fractional_divisor = (uint8_t)fractional_divisor; 12399461Sobrien regs->line_control = LCR_WORD_LEN_8; 12499461Sobrien 12599461Sobrien return 0; 12699461Sobrien} 12799461Sobrien