135 lines
4.2 KiB
C
135 lines
4.2 KiB
C
/*
|
|
* Copyright (c) 2018 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <devicetree.h>
|
|
#include <kernel.h>
|
|
#include <sys/util.h>
|
|
|
|
#include <soc.h>
|
|
|
|
#if DT_PROP(DT_CHOSEN(zephyr_console), pcie)
|
|
BUILD_ASSERT(IS_ENABLED(CONFIG_PCIE), "NS16550(s) in DT need CONFIG_PCIE");
|
|
#define UART_NS16550_PCIE_ENABLED
|
|
#include <drivers/pcie/pcie.h>
|
|
|
|
#define UART_PCIE_BDF (DT_REG_ADDR(DT_CHOSEN(zephyr_console)))
|
|
#define UART_PCIE_ID (DT_REG_SIZE(DT_CHOSEN(zephyr_console)))
|
|
#endif
|
|
|
|
/* Super-primitive 8250/16550 serial output-only driver, 115200 8n1 */
|
|
#define REG_OFFSET_THR 0x00 /* Transmitter holding reg. */
|
|
#define REG_OFFSET_IER 0x01 /* Interrupt enable reg. */
|
|
#define REG_OFFSET_FCR 0x02 /* FIFO control reg. */
|
|
#define REG_OFFSET_LCR 0x03 /* Line control reg. */
|
|
#define REG_OFFSET_MCR 0x04 /* Modem control reg. */
|
|
#define REG_OFFSET_LSR 0x05 /* Line status reg. */
|
|
#define REG_OFFSET_BRDL 0x00 /* Baud rate divisor (LSB) */
|
|
#define REG_OFFSET_BRDH 0x01 /* Baud rate divisor (MSB) */
|
|
|
|
#define IER_DISABLE 0x00
|
|
#define LCR_8N1 (BIT(0) | BIT(1))
|
|
#define LCR_DLAB_SELECT BIT(7)
|
|
#define MCR_DTR BIT(0)
|
|
#define MCR_RTS BIT(1)
|
|
#define LSR_THRE BIT(5)
|
|
|
|
#define FCR_FIFO BIT(0) /* enable XMIT and RCVR FIFO */
|
|
#define FCR_RCVRCLR BIT(1) /* clear RCVR FIFO */
|
|
#define FCR_XMITCLR BIT(2) /* clear XMIT FIFO */
|
|
#define FCR_FIFO_1 0 /* 1 byte in RCVR FIFO */
|
|
|
|
/* convenience defines */
|
|
#define REG_THR(x) (x + REG_OFFSET_THR * UART_REG_ADDR_INTERVAL)
|
|
#define REG_IER(x) (x + REG_OFFSET_IER * UART_REG_ADDR_INTERVAL)
|
|
#define REG_FCR(x) (x + REG_OFFSET_FCR * UART_REG_ADDR_INTERVAL)
|
|
#define REG_LCR(x) (x + REG_OFFSET_LCR * UART_REG_ADDR_INTERVAL)
|
|
#define REG_MCR(x) (x + REG_OFFSET_MCR * UART_REG_ADDR_INTERVAL)
|
|
#define REG_LSR(x) (x + REG_OFFSET_LSR * UART_REG_ADDR_INTERVAL)
|
|
#define REG_BRDL(x) (x + REG_OFFSET_BRDL * UART_REG_ADDR_INTERVAL)
|
|
#define REG_BRDH(x) (x + REG_OFFSET_BRDH * UART_REG_ADDR_INTERVAL)
|
|
|
|
#if DT_NODE_HAS_PROP(DT_CHOSEN(zephyr_console), reg_shift)
|
|
#define UART_REG_ADDR_INTERVAL \
|
|
(1 << DT_PROP(DT_CHOSEN(zephyr_console), reg_shift))
|
|
#endif
|
|
|
|
#ifdef UART_NS16550_ACCESS_IOPORT
|
|
#define PORT ((io_port_t)DT_REG_ADDR(DT_CHOSEN(zephyr_console)))
|
|
#define INBYTE(x) sys_in8(x)
|
|
#define INWORD(x) sys_in32(x)
|
|
#define OUTBYTE(x, d) sys_out8(d, x)
|
|
#define OUTWORD(x, d) sys_out32(d, x)
|
|
#ifndef UART_REG_ADDR_INTERVAL
|
|
#define UART_REG_ADDR_INTERVAL 1 /* address diff of adjacent regs. */
|
|
#endif /* UART_REG_ADDR_INTERVAL */
|
|
#else
|
|
#define PORT ((mm_reg_t)DT_REG_ADDR(DT_CHOSEN(zephyr_console)))
|
|
#define INBYTE(x) sys_read8(x)
|
|
#define INWORD(x) sys_read32(x)
|
|
#define OUTBYTE(x, d) sys_write8(d, x)
|
|
#define OUTWORD(x, d) sys_write32(d, x)
|
|
#ifndef UART_REG_ADDR_INTERVAL
|
|
#define UART_REG_ADDR_INTERVAL 4 /* address diff of adjacent regs. */
|
|
#endif
|
|
#endif /* UART_NS16550_ACCESS_IOPORT */
|
|
|
|
#ifdef CONFIG_UART_NS16550_ACCESS_WORD_ONLY
|
|
#undef INBYTE
|
|
#define INBYTE(x) INWORD(x)
|
|
#undef OUTBYTE
|
|
#define OUTBYTE(x, d) OUTWORD(x, d)
|
|
#endif
|
|
|
|
#ifdef UART_NS16550_PCIE_ENABLED
|
|
static mm_reg_t base;
|
|
#else
|
|
#define base PORT
|
|
#endif
|
|
|
|
static void serout(int c)
|
|
{
|
|
while ((INBYTE(REG_LSR(base)) & LSR_THRE) == 0) {
|
|
}
|
|
OUTBYTE(REG_THR(base), c);
|
|
}
|
|
|
|
static int console_out(int c)
|
|
{
|
|
if (c == '\n') {
|
|
serout('\r');
|
|
}
|
|
serout(c);
|
|
return c;
|
|
}
|
|
|
|
extern void __printk_hook_install(int (*fn)(int));
|
|
|
|
void z_x86_early_serial_init(void)
|
|
{
|
|
#ifdef UART_NS16550_PCIE_ENABLED
|
|
if (!pcie_probe(UART_PCIE_BDF, UART_PCIE_ID)) {
|
|
return;
|
|
}
|
|
|
|
base = pcie_get_mbar(UART_PCIE_BDF, 0);
|
|
pcie_set_cmd(UART_PCIE_BDF, PCIE_CONF_CMDSTAT_MEM, true);
|
|
#endif
|
|
|
|
OUTBYTE(REG_IER(base), IER_DISABLE); /* Disable interrupts */
|
|
OUTBYTE(REG_LCR(base), LCR_DLAB_SELECT);/* DLAB select */
|
|
OUTBYTE(REG_BRDL(base), 1); /* Baud divisor = 1 */
|
|
OUTBYTE(REG_BRDH(base), 0);
|
|
OUTBYTE(REG_LCR(base), LCR_8N1); /* LCR = 8n1 + DLAB off */
|
|
OUTBYTE(REG_MCR(base), MCR_DTR | MCR_RTS);
|
|
|
|
/* Turn on FIFO. Some hardware needs this before transmitting */
|
|
OUTBYTE(REG_FCR(base),
|
|
FCR_FIFO | FCR_FIFO_1 | FCR_RCVRCLR | FCR_XMITCLR);
|
|
|
|
/* Will be replaced later when a real serial driver comes up */
|
|
__printk_hook_install(console_out);
|
|
}
|