blob: fd58284fc2ca2ff6788dea059988f1ce50699724 [file] [log] [blame]
/*
* Copyright 2016 Google Inc.
* Copyright (C) 2008 Advanced Micro Devices, Inc.
* Copyright (C) 2008 Ulf Jordan <jordan@chalmers.se>
*
* 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. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 <stdint.h>
#include "arch/io.h"
#include "base/container_of.h"
#include "base/xalloc.h"
#include "drivers/uart/8250.h"
enum {
THR = 0, // Transmitter holding buffer.
RBR = 0, // Receiver buffer.
DLL = 0, // Divisor latch low byte.
IER = 1, // Interrupt enable register.
DLH = 1, // Divisor latch high byte.
IIR = 2, // Interrupt identification register.
FCR = 2, // FIFO control register.
LCR = 3, // Line control register.
MCR = 4, // Modem control register.
LSR = 5, // Line status register.
MSR = 6, // Modem status register.
SR = 7 // Scratch register.
};
enum {
LSR_DR = 0x1 << 0, // Data ready.
LSR_OE = 0x1 << 1, // Overrun.
LSR_PE = 0x1 << 2, // Parity error.
LSR_FE = 0x1 << 3, // Framing error.
LSR_BI = 0x1 << 4, // Break.
LSR_THRE = 0x1 << 5, // Xmit holding register empty.
LSR_TEMT = 0x1 << 6, // Xmitter empty.
LSR_ERR = 0x1 << 7 // Error.
};
enum {
LCR_WORD_LENGTH_MASK = 0x3 << 0,
LCR_5_BITS = 0x0 << 0,
LCR_6_BITS = 0x1 << 0,
LCR_7_BITS = 0x2 << 0,
LCR_8_BITS = 0x3 << 0,
LCR_ONE_STOP_BIT = 0x0 << 2,
LCR_TWO_STOP_BITS = 0x1 << 2,
LCR_PARITY_MASK = 0x7 << 3,
LCR_NO_PARITY = 0x0 << 3,
LCR_ODD_PARITY = 0x1 << 3,
LCR_EVEN_PARITY = 0x3 << 3,
LCR_MARK = 0x5 << 3,
LCR_SPACE = 0x7 << 3,
LCR_BREAK_ENABLE = 0x1 << 6,
LCR_DIVISOR_LATCH = 0x1 << 7
};
static void uart8250_init(Uart8250 *uart, int speed, int word_bits,
int parity, int stop_bits)
{
uint8_t reg;
if (uart->read_reg(uart, LSR) == 0xff &&
uart->read_reg(uart, MSR) == 0xff) {
uart->state = Uart8250NotPresent;
return;
} else {
uart->state = Uart8250Present;
}
if (!CONFIG_SERIAL_SET_SPEED)
return;
// Disable interrupts.
uart->write_reg(uart, 0, IER);
// Assert RTS and DTR.
uart->write_reg(uart, 3, MCR);
reg = uart->read_reg(uart, LCR);
uart->write_reg(uart, reg | LCR_DIVISOR_LATCH, LCR);
// Write the divisor.
uint16_t divisor = 115200 / speed;
uart->write_reg(uart, divisor & 0xFF, DLL);
uart->write_reg(uart, divisor >> 8, DLH);
uart->write_reg(uart, (reg & ~LCR_DIVISOR_LATCH) | LCR_8_BITS, LCR);
}
static void put_char(UartOps *me, uint8_t c)
{
Uart8250 *uart = container_of(me, Uart8250, ops);
if (uart->state == Uart8250Uninitialized)
uart8250_init(uart, CONFIG_SERIAL_BAUD_RATE, 8, 0, 1);
if (uart->state != Uart8250Present)
return;
while ((uart->read_reg(uart, LSR) & LSR_THRE) == 0)
{;}
uart->write_reg(uart, c, THR);
if (c == '\n')
me->put_char(me, '\r');
}
static int have_char(UartOps *me)
{
Uart8250 *uart = container_of(me, Uart8250, ops);
if (uart->state == Uart8250Uninitialized)
uart8250_init(uart, CONFIG_SERIAL_BAUD_RATE, 8, 0, 1);
if (uart->state != Uart8250Present)
return 0;
return uart->read_reg(uart, LSR) & LSR_DR;
}
static int get_char(UartOps *me)
{
Uart8250 *uart = container_of(me, Uart8250, ops);
if (uart->state == Uart8250Uninitialized)
uart8250_init(uart, CONFIG_SERIAL_BAUD_RATE, 8, 0, 1);
if (uart->state != Uart8250Present)
return -1;
while (!me->have_char(me))
{;}
return uart->read_reg(uart, RBR);
}
static void uart_8250_fill_in(Uart8250 *uart, Uart8250ReadRegFunc read_reg,
Uart8250WriteRegFunc write_reg)
{
uart->ops.put_char = &put_char;
uart->ops.have_char = &have_char;
uart->ops.get_char = &get_char;
uart->read_reg = read_reg;
uart->write_reg = write_reg;
}
#if CONFIG_IO_ADDRESS_SPACE
static uint8_t read_reg_io(Uart8250 *me, int index)
{
Uart8250Io *uart = container_of(me, Uart8250Io, uart);
return inb(uart->base + index);
}
static void write_reg_io(Uart8250 *me, uint8_t val, int index)
{
Uart8250Io *uart = container_of(me, Uart8250Io, uart);
outb(val, uart->base + index);
}
Uart8250Io *new_uart_8250_io(uint16_t base)
{
Uart8250Io *uart = xzalloc(sizeof(*uart));
uart_8250_fill_in(&uart->uart, &read_reg_io, &write_reg_io);
uart->base = base;
return uart;
}
#endif
static uint8_t read_reg_mem(Uart8250 *me, int index)
{
Uart8250Mem *uart = container_of(me, Uart8250Mem, uart);
return read8((uint8_t *)uart->base + index * uart->stride);
}
static void write_reg_mem(Uart8250 *me, uint8_t val, int index)
{
Uart8250Mem *uart = container_of(me, Uart8250Mem, uart);
write8((uint8_t *)uart->base + index * uart->stride, val);
}
Uart8250Mem *new_uart_8250_mem(uintptr_t base, int stride)
{
Uart8250Mem *uart = xzalloc(sizeof(*uart));
uart_8250_fill_in(&uart->uart, &read_reg_mem, &write_reg_mem);
uart->base = base;
uart->stride = stride;
return uart;
}
static uint8_t read_reg_mem32(Uart8250 *me, int index)
{
Uart8250Mem32 *uart = container_of(me, Uart8250Mem32, uart);
return (uint8_t)read32((uint8_t *)uart->base + index * sizeof(uint32_t));
}
static void write_reg_mem32(Uart8250 *me, uint8_t val, int index)
{
Uart8250Mem32 *uart = container_of(me, Uart8250Mem32, uart);
write32((uint8_t *)uart->base + index * sizeof(uint32_t), val);
}
Uart8250Mem32 *new_uart_8250_mem32(uintptr_t base)
{
Uart8250Mem32 *uart = xzalloc(sizeof(*uart));
uart_8250_fill_in(&uart->uart, &read_reg_mem32, &write_reg_mem32);
uart->base = base;
return uart;
}