/*
* Copyright (c) 2017 Richard Braun.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*
* TODO Make serial line parameters configurable.
* TODO 16550A support.
* TODO Interrupt probing and handling.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define UART_BDA_COM1_OFFSET 0
#define UART_REG_DAT 0
#define UART_REG_DLL 0
#define UART_REG_IER 1
#define UART_REG_DLH 1
#define UART_REG_LCR 3
#define UART_REG_MCR 4
#define UART_REG_LSR 5
#define UART_NR_REGS 6
#define UART_THR_EMPTY 0x20
#define UART_LCR_8BITS 0x03
#define UART_LCR_1S 0x00
#define UART_LCR_NP 0x00
#define UART_LCR_BEN 0x40
#define UART_LCR_DLAB 0x80
#define UART_MCR_DTR 0x01
#define UART_MCR_RTS 0x02
#define UART_MCR_AUX2 0x04
#define UART_MAX_DEVS 4
struct uart {
struct console console;
uint16_t port;
uint16_t intr;
};
static struct uart uart_devs[UART_MAX_DEVS];
static uint16_t uart_intrs[UART_MAX_DEVS] = { 4, 3, 4, 3 };
static size_t
uart_get_id(const struct uart *uart)
{
size_t id;
id = uart - uart_devs;
assert(id < ARRAY_SIZE(uart_devs));
return id;
}
static uint16_t
uart_get_addr(const struct uart *uart, uint16_t reg)
{
assert(reg < UART_NR_REGS);
return uart->port + reg;
}
static uint8_t
uart_read(struct uart *uart, uint16_t reg)
{
return io_read_byte(uart_get_addr(uart, reg));
}
static void
uart_write(struct uart *uart, uint16_t reg, uint8_t byte)
{
io_write_byte(uart_get_addr(uart, reg), byte);
}
static void
uart_set(struct uart *uart, uint16_t reg, uint8_t mask)
{
uint16_t addr;
uint8_t byte;
addr = uart_get_addr(uart, reg);
byte = io_read_byte(addr);
byte |= mask;
io_write_byte(addr, byte);
}
static void
uart_clear(struct uart *uart, uint16_t reg, uint8_t mask)
{
uint16_t addr;
uint8_t byte;
addr = uart_get_addr(uart, reg);
byte = io_read_byte(addr);
byte &= ~mask;
io_write_byte(addr, byte);
}
static void
uart_tx_wait(struct uart *uart)
{
uint8_t byte;
for (;;) {
byte = uart_read(uart, UART_REG_LSR);
if (byte & UART_THR_EMPTY) {
break;
}
}
}
static struct uart *
uart_get_dev(size_t i)
{
assert(i < ARRAY_SIZE(uart_devs));
return &uart_devs[i];
}
static struct uart *
uart_get_from_console(struct console *console)
{
return structof(console, struct uart, console);
}
static void
uart_write_char_common(struct uart *uart, char c)
{
uart_tx_wait(uart);
uart_write(uart, UART_REG_DAT, (uint8_t)c);
}
static void
uart_write_char(struct uart *uart, char c)
{
if (c == '\n') {
uart_write_char_common(uart, '\r');
}
uart_write_char_common(uart, c);
}
static void
uart_console_putc(struct console *console, char c)
{
uart_write_char(uart_get_from_console(console), c);
}
static struct console_ops uart_console_ops = {
.putc = uart_console_putc,
};
static void __init
uart_init(struct uart *uart, uint16_t port, uint16_t intr)
{
char name[CONSOLE_NAME_SIZE];
uint8_t byte;
uart->port = port;
uart->intr = intr;
uart_write(uart, UART_REG_IER, 0);
uart_write(uart, UART_REG_MCR, UART_MCR_AUX2 | UART_MCR_RTS | UART_MCR_DTR);
uart_set(uart, UART_REG_LCR, UART_LCR_DLAB);
uart_write(uart, UART_REG_DLH, 0);
uart_write(uart, UART_REG_DLL, 1);
uart_clear(uart, UART_REG_LCR, UART_LCR_DLAB);
byte = UART_LCR_8BITS | UART_LCR_NP | UART_LCR_1S | UART_LCR_BEN;
uart_write(uart, UART_REG_LCR, byte);
snprintf(name, sizeof(name), "uart%zu", uart_get_id(uart));
console_init(&uart->console, name, &uart_console_ops);
console_register(&uart->console);
}
void __init
uart_bootstrap(void)
{
const uint16_t *ptr;
size_t i;
ptr = biosmem_get_bda() + UART_BDA_COM1_OFFSET;
for (i = 0; i < UART_MAX_DEVS; i++) {
if (ptr[i] == 0) {
continue;
}
uart_init(uart_get_dev(i), ptr[i], uart_intrs[i]);
}
}
static int
uart_intr(void *arg)
{
struct uart *uart;
uint8_t byte;
uart = arg;
byte = uart_read(uart, UART_REG_DAT);
printf("uart: intr:%u byte:%hhu (%c)\n", uart->intr, byte, byte);
return 0;
}
void __init
uart_setup(void)
{
struct uart *uart;
int error;
size_t i;
for (i = 0; i < ARRAY_SIZE(uart_devs); i++) {
uart = uart_get_dev(i);
if (uart->port == 0) {
continue;
}
error = intr_register(uart->intr, uart_intr, uart);
if (error) {
printf("uart%zu: unable to register interrupt %u\n", i, uart->intr);
}
uart_write(uart, UART_REG_IER, 1);
}
}
void __init
uart_info(void)
{
const struct uart *uart;
size_t i;
for (i = 0; i < ARRAY_SIZE(uart_devs); i++) {
uart = uart_get_dev(i);
if (uart->port != 0) {
printf("uart%zu: port:%#x irq:%u\n", i, (unsigned int)uart->port,
(unsigned int)uart->intr);
}
}
}