/* * 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 . */ #include #include #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_IIR 2 #define UART_REG_LCR 3 #define UART_REG_MCR 4 #define UART_REG_LSR 5 #define UART_REG_MSR 6 #define UART_NR_REGS 7 #define UART_IER_RX 0x1 #define UART_IIR_NOT_PENDING 0x1 #define UART_IIR_SRC_RX 0x4 #define UART_IIR_SRC_MASK 0xe #define UART_LCR_8BITS 0x03 #define UART_LCR_1S 0x00 #define UART_LCR_NP 0x00 #define UART_LCR_OP 0x08 #define UART_LCR_EP 0x18 #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_LSR_DATA_READY 0x01 #define UART_LSR_TX_EMPTY 0x20 #define UART_MAX_DEVS 4 #define UART_SPEED_MAX 115200 #define UART_SPEED_DEFAULT UART_SPEED_MAX enum { UART_PARITY_NONE, UART_PARITY_ODD, UART_PARITY_EVEN, }; #define UART_PARITY_DEFAULT UART_PARITY_NONE #define UART_DATA_BITS_DEFAULT 8 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_recv_intr(struct uart *uart) { uint8_t byte; char tmp[2]; tmp[1] = '\0'; for (;;) { byte = uart_read(uart, UART_REG_LSR); if (!(byte & UART_LSR_DATA_READY)) { break; } byte = uart_read(uart, UART_REG_DAT); tmp[0] = (char)byte; console_intr(&uart->console, tmp); } } static int uart_intr(void *arg) { struct uart *uart; uint8_t byte; uart = arg; byte = uart_read(uart, UART_REG_IIR); if (byte & UART_IIR_NOT_PENDING) { return EAGAIN; } byte &= UART_IIR_SRC_MASK; if (byte == UART_IIR_SRC_RX) { uart_recv_intr(uart); } return 0; } static void __init uart_enable_intr(struct uart *uart) { int error; error = intr_register(uart->intr, uart_intr, uart); if (error) { log_err("uart%zu: unable to register interrupt %u", uart_get_id(uart), uart->intr); return; } uart_write(uart, UART_REG_IER, UART_IER_RX); } static void uart_tx_wait(struct uart *uart) { uint8_t byte; for (;;) { byte = uart_read(uart, UART_REG_LSR); if (byte & UART_LSR_TX_EMPTY) { break; } } } 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 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_console_putc(struct console *console, char c) { uart_write_char(uart_get_from_console(console), c); } static const struct console_ops uart_console_ops = { .putc = uart_console_putc, }; static void __init uart_init_default(unsigned int *speed, unsigned int *parity, unsigned int *data_bits) { *speed = UART_SPEED_DEFAULT; *parity = UART_PARITY_DEFAULT; *data_bits = UART_DATA_BITS_DEFAULT; } static int __init uart_init_check_speed(unsigned int speed) { if (speed > UART_SPEED_MAX) { return EINVAL; } return 0; } static int __init uart_init_convert_parity_char(char c, unsigned int *parity) { switch (c) { case 'n': *parity = UART_PARITY_NONE; break; case 'o': *parity = UART_PARITY_ODD; break; case 'e': *parity = UART_PARITY_EVEN; break; default: return EINVAL; } return 0; } static int __init uart_init_check_data_bits(unsigned int data_bits) { switch (data_bits) { case 5 ... 8: break; default: return EINVAL; } return 0; } static void __init uart_init_args(const struct uart *uart, const char *arg_str, unsigned int *speed, unsigned int *parity, unsigned int *data_bits) { char parity_char; int ret, error; ret = sscanf(arg_str, "%u%c%1u", speed, &parity_char, data_bits); if (ret < 1) { goto set_defaults; } error = uart_init_check_speed(*speed); if (error) { goto set_defaults; } else if (ret < 2) { return; } error = uart_init_convert_parity_char(parity_char, parity); if (error) { goto set_defaults; } else if (ret < 3) { return; } error = uart_init_check_data_bits(*data_bits); if (error) { goto set_defaults; } return; set_defaults: log_warning("uart%zu: invalid serial configuration, using defaults", uart_get_id(uart)); uart_init_default(speed, parity, data_bits); } static void __init uart_init(struct uart *uart, uint16_t port, uint16_t intr) { unsigned int speed, parity, data_bits; const char *arg_str; char name[CONSOLE_NAME_SIZE]; uint16_t divisor; uint8_t byte; snprintf(name, sizeof(name), "uart%zu", uart_get_id(uart)); arg_str = arg_value(name); uart_init_default(&speed, &parity, &data_bits); if (arg_str != NULL) { uart_init_args(uart, arg_str, &speed, &parity, &data_bits); } log_debug("uart%zu: speed:%u parity:%u data_bits:%u", uart_get_id(uart), speed, parity, data_bits); uart->port = port; uart->intr = intr; uart_write(uart, UART_REG_IER, 0); divisor = UART_SPEED_MAX / speed; uart_set(uart, UART_REG_LCR, UART_LCR_DLAB); uart_write(uart, UART_REG_DLH, divisor >> 8); uart_write(uart, UART_REG_DLL, divisor & 0xff); uart_clear(uart, UART_REG_LCR, UART_LCR_DLAB); uart_write(uart, UART_REG_MCR, UART_MCR_AUX2 | UART_MCR_RTS | UART_MCR_DTR); byte = UART_LCR_1S; switch (parity) { case UART_PARITY_NONE: byte |= UART_LCR_NP; break; case UART_PARITY_ODD: byte |= UART_LCR_OP; break; case UART_PARITY_EVEN: byte |= UART_LCR_EP; break; } byte |= (data_bits - 5); uart_write(uart, UART_REG_LCR, byte); console_init(&uart->console, name, &uart_console_ops); console_register(&uart->console); } static void __init uart_log_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) { log_info("uart%zu: port:%#x irq:%u", i, (unsigned int)uart->port, (unsigned int)uart->intr); } } } static int __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]); } uart_log_info(); return 0; } INIT_OP_DEFINE(uart_bootstrap, INIT_OP_DEP(arg_setup, true), INIT_OP_DEP(console_bootstrap, true), INIT_OP_DEP(log_setup, true)); static int __init uart_setup(void) { struct uart *uart; size_t i; for (i = 0; i < ARRAY_SIZE(uart_devs); i++) { uart = uart_get_dev(i); if (uart->port == 0) { continue; } uart_enable_intr(uart); } return 0; } INIT_OP_DEFINE(uart_setup, INIT_OP_DEP(intr_setup, true), INIT_OP_DEP(uart_bootstrap, true));