/* * Copyright (C) 2015, Intel Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "uart-16x50.h" #include #include "helpers.h" /* Refer to Intel Quark SoC X1000 Datasheet, Chapter 18 for more details on * UART operation. */ /* Divisor Latch Access Bit (DLAB) mask for Line Control Register (LCR). * * When bit is set, enables access to divisor registers to set baud rate. When * clear, enables access to other registers mapped to the same addresses as the * divisor registers. */ #define UART_LCR_7_DLAB BIT(7) /* Setting for LCR that configures the UART to operate with no parity, 1 stop * bit, and eight bits per character. */ #define UART_LCR_8BITS 0x03 /* FIFO Control Register (FCR) bitmasks */ #define UART_FCR_0_FIFOE BIT(0) /*< enable FIFOs */ #define UART_FCR_1_RFIFOR BIT(1) /*< reset RX FIFO */ #define UART_FCR_2_XFIFOR BIT(2) /*< reset TX FIFO */ /* Line Status Register (LSR) Transmit Holding Register Empty bitmask to check * whether the Transmit Holding Register (THR) or TX FIFO is empty. */ #define UART_LSR_5_THRE BIT(5) /* MMIO registers for UART */ typedef struct uart_16x50_regs { volatile uint32_t rbr_thr_dll, ier_dlh, iir_fcr, lcr; volatile uint32_t mcr, lsr, msr, scr, usr, htx, dmasa; } uart_16x50_regs_t; /*---------------------------------------------------------------------------*/ /** * \brief Initialize an MMIO-programmable 16X50 UART. * \param c_this Structure that will be initialized to represent the device. * \param pci_addr PCI address of device. * \param dl Divisor setting to configure the baud rate. */ void uart_16x50_init(uart_16x50_driver_t *c_this, pci_config_addr_t pci_addr, uint16_t dl) { /* This assumes that the UART had an MMIO range assigned to it by the * firmware during boot. */ pci_init_bar0(c_this, pci_addr); uart_16x50_regs_t *regs = (uart_16x50_regs_t *)c_this->mmio; /* Set the DLAB bit to enable access to divisor settings. */ regs->lcr = UART_LCR_7_DLAB; /* The divisor settings configure the baud rate, and may need to be defined * on a per-device basis. */ regs->rbr_thr_dll = dl & UINT8_MAX; regs->ier_dlh = dl >> 8; /* Clear the DLAB bit to enable access to other settings and configure other * UART parameters. */ regs->lcr = UART_LCR_8BITS; /* Enable the FIFOs. */ regs->iir_fcr = UART_FCR_0_FIFOE | UART_FCR_1_RFIFOR | UART_FCR_2_XFIFOR; } /*---------------------------------------------------------------------------*/ /** * \brief Transmit a character through a UART. * \param c_this Initialized structure representing the device. * \param c Character to be transmitted. * * This procedure will block indefinitely until the UART is ready * to accept the character to be transmitted. */ void uart_16x50_tx(uart_16x50_driver_t c_this, uint8_t c) { struct uart_16x50_regs *regs = (uart_16x50_regs_t *)c_this.mmio; /* Wait for space in TX FIFO. */ while((regs->lsr & UART_LSR_5_THRE) == 0); /* Add character to TX FIFO. */ regs->rbr_thr_dll = c; } /*---------------------------------------------------------------------------*/