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