1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Parse the EFI PCDP table to locate the console device.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * (c) Copyright 2002, 2003, 2004 Hewlett-Packard Development Company, L.P.
6*4882a593Smuzhiyun * Khalid Aziz <khalid.aziz@hp.com>
7*4882a593Smuzhiyun * Alex Williamson <alex.williamson@hp.com>
8*4882a593Smuzhiyun * Bjorn Helgaas <bjorn.helgaas@hp.com>
9*4882a593Smuzhiyun */
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <linux/acpi.h>
12*4882a593Smuzhiyun #include <linux/console.h>
13*4882a593Smuzhiyun #include <linux/efi.h>
14*4882a593Smuzhiyun #include <linux/serial.h>
15*4882a593Smuzhiyun #include <linux/serial_core.h>
16*4882a593Smuzhiyun #include <asm/vga.h>
17*4882a593Smuzhiyun #include "pcdp.h"
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun static int __init
setup_serial_console(struct pcdp_uart * uart)20*4882a593Smuzhiyun setup_serial_console(struct pcdp_uart *uart)
21*4882a593Smuzhiyun {
22*4882a593Smuzhiyun #ifdef CONFIG_SERIAL_8250_CONSOLE
23*4882a593Smuzhiyun int mmio;
24*4882a593Smuzhiyun static char options[64], *p = options;
25*4882a593Smuzhiyun char parity;
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun mmio = (uart->addr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY);
28*4882a593Smuzhiyun p += sprintf(p, "uart8250,%s,0x%llx",
29*4882a593Smuzhiyun mmio ? "mmio" : "io", uart->addr.address);
30*4882a593Smuzhiyun if (uart->baud) {
31*4882a593Smuzhiyun p += sprintf(p, ",%llu", uart->baud);
32*4882a593Smuzhiyun if (uart->bits) {
33*4882a593Smuzhiyun switch (uart->parity) {
34*4882a593Smuzhiyun case 0x2: parity = 'e'; break;
35*4882a593Smuzhiyun case 0x3: parity = 'o'; break;
36*4882a593Smuzhiyun default: parity = 'n';
37*4882a593Smuzhiyun }
38*4882a593Smuzhiyun p += sprintf(p, "%c%d", parity, uart->bits);
39*4882a593Smuzhiyun }
40*4882a593Smuzhiyun }
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun add_preferred_console("uart", 8250, &options[9]);
43*4882a593Smuzhiyun return setup_earlycon(options);
44*4882a593Smuzhiyun #else
45*4882a593Smuzhiyun return -ENODEV;
46*4882a593Smuzhiyun #endif
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun static int __init
setup_vga_console(struct pcdp_device * dev)50*4882a593Smuzhiyun setup_vga_console(struct pcdp_device *dev)
51*4882a593Smuzhiyun {
52*4882a593Smuzhiyun #if defined(CONFIG_VT) && defined(CONFIG_VGA_CONSOLE)
53*4882a593Smuzhiyun u8 *if_ptr;
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun if_ptr = ((u8 *)dev + sizeof(struct pcdp_device));
56*4882a593Smuzhiyun if (if_ptr[0] == PCDP_IF_PCI) {
57*4882a593Smuzhiyun struct pcdp_if_pci if_pci;
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun /* struct copy since ifptr might not be correctly aligned */
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun memcpy(&if_pci, if_ptr, sizeof(if_pci));
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun if (if_pci.trans & PCDP_PCI_TRANS_IOPORT)
64*4882a593Smuzhiyun vga_console_iobase = if_pci.ioport_tra;
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun if (if_pci.trans & PCDP_PCI_TRANS_MMIO)
67*4882a593Smuzhiyun vga_console_membase = if_pci.mmio_tra;
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun if (efi_mem_type(vga_console_membase + 0xA0000) == EFI_CONVENTIONAL_MEMORY) {
71*4882a593Smuzhiyun printk(KERN_ERR "PCDP: VGA selected, but frame buffer is not MMIO!\n");
72*4882a593Smuzhiyun return -ENODEV;
73*4882a593Smuzhiyun }
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun conswitchp = &vga_con;
76*4882a593Smuzhiyun printk(KERN_INFO "PCDP: VGA console\n");
77*4882a593Smuzhiyun return 0;
78*4882a593Smuzhiyun #else
79*4882a593Smuzhiyun return -ENODEV;
80*4882a593Smuzhiyun #endif
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun extern unsigned long hcdp_phys;
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun int __init
efi_setup_pcdp_console(char * cmdline)86*4882a593Smuzhiyun efi_setup_pcdp_console(char *cmdline)
87*4882a593Smuzhiyun {
88*4882a593Smuzhiyun struct pcdp *pcdp;
89*4882a593Smuzhiyun struct pcdp_uart *uart;
90*4882a593Smuzhiyun struct pcdp_device *dev, *end;
91*4882a593Smuzhiyun int i, serial = 0;
92*4882a593Smuzhiyun int rc = -ENODEV;
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun if (hcdp_phys == EFI_INVALID_TABLE_ADDR)
95*4882a593Smuzhiyun return -ENODEV;
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun pcdp = early_memremap(hcdp_phys, 4096);
98*4882a593Smuzhiyun printk(KERN_INFO "PCDP: v%d at 0x%lx\n", pcdp->rev, hcdp_phys);
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun if (strstr(cmdline, "console=hcdp")) {
101*4882a593Smuzhiyun if (pcdp->rev < 3)
102*4882a593Smuzhiyun serial = 1;
103*4882a593Smuzhiyun } else if (strstr(cmdline, "console=")) {
104*4882a593Smuzhiyun printk(KERN_INFO "Explicit \"console=\"; ignoring PCDP\n");
105*4882a593Smuzhiyun goto out;
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun if (pcdp->rev < 3 && efi_uart_console_only())
109*4882a593Smuzhiyun serial = 1;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun for (i = 0, uart = pcdp->uart; i < pcdp->num_uarts; i++, uart++) {
112*4882a593Smuzhiyun if (uart->flags & PCDP_UART_PRIMARY_CONSOLE || serial) {
113*4882a593Smuzhiyun if (uart->type == PCDP_CONSOLE_UART) {
114*4882a593Smuzhiyun rc = setup_serial_console(uart);
115*4882a593Smuzhiyun goto out;
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun end = (struct pcdp_device *) ((u8 *) pcdp + pcdp->length);
121*4882a593Smuzhiyun for (dev = (struct pcdp_device *) (pcdp->uart + pcdp->num_uarts);
122*4882a593Smuzhiyun dev < end;
123*4882a593Smuzhiyun dev = (struct pcdp_device *) ((u8 *) dev + dev->length)) {
124*4882a593Smuzhiyun if (dev->flags & PCDP_PRIMARY_CONSOLE) {
125*4882a593Smuzhiyun if (dev->type == PCDP_CONSOLE_VGA) {
126*4882a593Smuzhiyun rc = setup_vga_console(dev);
127*4882a593Smuzhiyun goto out;
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun out:
133*4882a593Smuzhiyun early_memunmap(pcdp, 4096);
134*4882a593Smuzhiyun return rc;
135*4882a593Smuzhiyun }
136