1 /*
2 * Copyright (c) 2020 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/sys/device_mmio.h>
9 #include <zephyr/sys/util.h>
10 #include <zephyr/drivers/pcie/pcie.h>
11 #include <soc.h>
12
13
14 #if DT_PROP_OR(DT_CHOSEN(zephyr_console), io_mapped, 0) != 0
15 #define UART_IS_IOPORT_ACCESS 1
16 #endif
17
18 #if defined(UART_IS_IOPORT_ACCESS)
19 /* Legacy I/O Port Access to a NS16550 UART */
20 #define IN(reg) sys_in8(reg + DT_REG_ADDR(DT_CHOSEN(zephyr_console)))
21 #define OUT(reg, val) sys_out8(val, reg + DT_REG_ADDR(DT_CHOSEN(zephyr_console)))
22 #elif defined(X86_SOC_EARLY_SERIAL_PCIDEV)
23 /* "Modern" mapping of a UART into a PCI MMIO device. The registers
24 * are still bytes, but spaced at a 32 bit stride instead of packed
25 * together.
26 */
27 static mm_reg_t mmio;
28 #define IN(reg) (sys_read32(mmio + (reg) * 4) & 0xff)
29 #define OUT(reg, val) sys_write32((val) & 0xff, mmio + (reg) * 4)
30 #elif defined(X86_SOC_EARLY_SERIAL_MMIO8_ADDR)
31 /* Still other devices use a MMIO region containing packed byte
32 * registers
33 */
34 #ifdef DEVICE_MMIO_IS_IN_RAM
35 static mm_reg_t mmio;
36 #define BASE mmio
37 #else
38 #define BASE X86_SOC_EARLY_SERIAL_MMIO8_ADDR
39 #endif /* DEVICE_MMIO_IS_IN_RAM */
40 #define IN(reg) sys_read8(BASE + reg)
41 #define OUT(reg, val) sys_write8(val, BASE + reg)
42 #else
43 #error "Unsupported configuration"
44 #endif
45
46 #define REG_THR 0x00 /* Transmitter holding reg. */
47 #define REG_IER 0x01 /* Interrupt enable reg. */
48 #define REG_FCR 0x02 /* FIFO control reg. */
49 #define REG_LCR 0x03 /* Line control reg. */
50 #define REG_MCR 0x04 /* Modem control reg. */
51 #define REG_LSR 0x05 /* Line status reg. */
52 #define REG_BRDL 0x00 /* Baud rate divisor (LSB) */
53 #define REG_BRDH 0x01 /* Baud rate divisor (MSB) */
54
55 #define IER_DISABLE 0x00
56 #define LCR_8N1 (BIT(0) | BIT(1))
57 #define LCR_DLAB_SELECT BIT(7)
58 #define MCR_DTR BIT(0)
59 #define MCR_RTS BIT(1)
60 #define LSR_THRE BIT(5)
61
62 #define FCR_FIFO BIT(0) /* enable XMIT and RCVR FIFO */
63 #define FCR_RCVRCLR BIT(1) /* clear RCVR FIFO */
64 #define FCR_XMITCLR BIT(2) /* clear XMIT FIFO */
65 #define FCR_FIFO_1 0 /* 1 byte in RCVR FIFO */
66
67 static bool early_serial_init_done;
68 static uint32_t suppressed_chars;
69
serout(int c)70 static void serout(int c)
71 {
72 while ((IN(REG_LSR) & LSR_THRE) == 0) {
73 }
74 OUT(REG_THR, c);
75 }
76
arch_printk_char_out(int c)77 int arch_printk_char_out(int c)
78 {
79 if (!early_serial_init_done) {
80 suppressed_chars++;
81 return c;
82 }
83
84 if (c == '\n') {
85 serout('\r');
86 }
87 serout(c);
88 return c;
89 }
90
z_x86_early_serial_init(void)91 void z_x86_early_serial_init(void)
92 {
93 #if defined(DEVICE_MMIO_IS_IN_RAM) && !defined(UART_IS_IOPORT_ACCESS)
94 #ifdef X86_SOC_EARLY_SERIAL_PCIDEV
95 struct pcie_bar mbar;
96 pcie_get_mbar(X86_SOC_EARLY_SERIAL_PCIDEV, 0, &mbar);
97 pcie_set_cmd(X86_SOC_EARLY_SERIAL_PCIDEV, PCIE_CONF_CMDSTAT_MEM, true);
98 device_map(&mmio, mbar.phys_addr, mbar.size, K_MEM_CACHE_NONE);
99 #else
100 device_map(&mmio, X86_SOC_EARLY_SERIAL_MMIO8_ADDR, 0x1000, K_MEM_CACHE_NONE);
101 #endif
102
103 #endif /* DEVICE_MMIO_IS_IN_RAM */
104
105 OUT(REG_IER, IER_DISABLE); /* Disable interrupts */
106 OUT(REG_LCR, LCR_DLAB_SELECT); /* DLAB select */
107 OUT(REG_BRDL, 1); /* Baud divisor = 1 */
108 OUT(REG_BRDH, 0);
109 OUT(REG_LCR, LCR_8N1); /* LCR = 8n1 + DLAB off */
110 OUT(REG_MCR, MCR_DTR | MCR_RTS);
111
112 /* Turn on FIFO. Some hardware needs this before transmitting */
113 OUT(REG_FCR, FCR_FIFO | FCR_FIFO_1 | FCR_RCVRCLR | FCR_XMITCLR);
114
115 early_serial_init_done = true;
116
117 if (suppressed_chars != 0U) {
118 printk("WARNING: %u chars lost before early serial init\n",
119 suppressed_chars);
120 }
121 }
122