blob: c4d9cc4f29a12b9152e7db419fcf400b0d2b5ef0 [file] [log] [blame]
/*
* 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);
}