162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Parse the EFI PCDP table to locate the console device.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * (c) Copyright 2002, 2003, 2004 Hewlett-Packard Development Company, L.P.
662306a36Sopenharmony_ci *	Khalid Aziz <khalid.aziz@hp.com>
762306a36Sopenharmony_ci *	Alex Williamson <alex.williamson@hp.com>
862306a36Sopenharmony_ci *	Bjorn Helgaas <bjorn.helgaas@hp.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/acpi.h>
1262306a36Sopenharmony_ci#include <linux/console.h>
1362306a36Sopenharmony_ci#include <linux/efi.h>
1462306a36Sopenharmony_ci#include <linux/serial.h>
1562306a36Sopenharmony_ci#include <linux/serial_core.h>
1662306a36Sopenharmony_ci#include <asm/vga.h>
1762306a36Sopenharmony_ci#include "pcdp.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic int __init
2062306a36Sopenharmony_cisetup_serial_console(struct pcdp_uart *uart)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci#ifdef CONFIG_SERIAL_8250_CONSOLE
2362306a36Sopenharmony_ci	int mmio;
2462306a36Sopenharmony_ci	static char options[64], *p = options;
2562306a36Sopenharmony_ci	char parity;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	mmio = (uart->addr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY);
2862306a36Sopenharmony_ci	p += sprintf(p, "uart8250,%s,0x%llx",
2962306a36Sopenharmony_ci		mmio ? "mmio" : "io", uart->addr.address);
3062306a36Sopenharmony_ci	if (uart->baud) {
3162306a36Sopenharmony_ci		p += sprintf(p, ",%llu", uart->baud);
3262306a36Sopenharmony_ci		if (uart->bits) {
3362306a36Sopenharmony_ci			switch (uart->parity) {
3462306a36Sopenharmony_ci			    case 0x2: parity = 'e'; break;
3562306a36Sopenharmony_ci			    case 0x3: parity = 'o'; break;
3662306a36Sopenharmony_ci			    default:  parity = 'n';
3762306a36Sopenharmony_ci			}
3862306a36Sopenharmony_ci			p += sprintf(p, "%c%d", parity, uart->bits);
3962306a36Sopenharmony_ci		}
4062306a36Sopenharmony_ci	}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	add_preferred_console("uart", 8250, &options[9]);
4362306a36Sopenharmony_ci	return setup_earlycon(options);
4462306a36Sopenharmony_ci#else
4562306a36Sopenharmony_ci	return -ENODEV;
4662306a36Sopenharmony_ci#endif
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic int __init
5062306a36Sopenharmony_cisetup_vga_console(struct pcdp_device *dev)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci#if defined(CONFIG_VT) && defined(CONFIG_VGA_CONSOLE)
5362306a36Sopenharmony_ci	u8 *if_ptr;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	if_ptr = ((u8 *)dev + sizeof(struct pcdp_device));
5662306a36Sopenharmony_ci	if (if_ptr[0] == PCDP_IF_PCI) {
5762306a36Sopenharmony_ci		struct pcdp_if_pci if_pci;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci		/* struct copy since ifptr might not be correctly aligned */
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci		memcpy(&if_pci, if_ptr, sizeof(if_pci));
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci		if (if_pci.trans & PCDP_PCI_TRANS_IOPORT)
6462306a36Sopenharmony_ci			vga_console_iobase = if_pci.ioport_tra;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci		if (if_pci.trans & PCDP_PCI_TRANS_MMIO)
6762306a36Sopenharmony_ci			vga_console_membase = if_pci.mmio_tra;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if (efi_mem_type(vga_console_membase + 0xA0000) == EFI_CONVENTIONAL_MEMORY) {
7162306a36Sopenharmony_ci		printk(KERN_ERR "PCDP: VGA selected, but frame buffer is not MMIO!\n");
7262306a36Sopenharmony_ci		return -ENODEV;
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	conswitchp = &vga_con;
7662306a36Sopenharmony_ci	printk(KERN_INFO "PCDP: VGA console\n");
7762306a36Sopenharmony_ci	return 0;
7862306a36Sopenharmony_ci#else
7962306a36Sopenharmony_ci	return -ENODEV;
8062306a36Sopenharmony_ci#endif
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ciextern unsigned long hcdp_phys;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ciint __init
8662306a36Sopenharmony_ciefi_setup_pcdp_console(char *cmdline)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct pcdp *pcdp;
8962306a36Sopenharmony_ci	struct pcdp_uart *uart;
9062306a36Sopenharmony_ci	struct pcdp_device *dev, *end;
9162306a36Sopenharmony_ci	int i, serial = 0;
9262306a36Sopenharmony_ci	int rc = -ENODEV;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (hcdp_phys == EFI_INVALID_TABLE_ADDR)
9562306a36Sopenharmony_ci		return -ENODEV;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	pcdp = early_memremap(hcdp_phys, 4096);
9862306a36Sopenharmony_ci	printk(KERN_INFO "PCDP: v%d at 0x%lx\n", pcdp->rev, hcdp_phys);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (strstr(cmdline, "console=hcdp")) {
10162306a36Sopenharmony_ci		if (pcdp->rev < 3)
10262306a36Sopenharmony_ci			serial = 1;
10362306a36Sopenharmony_ci	} else if (strstr(cmdline, "console=")) {
10462306a36Sopenharmony_ci		printk(KERN_INFO "Explicit \"console=\"; ignoring PCDP\n");
10562306a36Sopenharmony_ci		goto out;
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	if (pcdp->rev < 3 && efi_uart_console_only())
10962306a36Sopenharmony_ci		serial = 1;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	for (i = 0, uart = pcdp->uart; i < pcdp->num_uarts; i++, uart++) {
11262306a36Sopenharmony_ci		if (uart->flags & PCDP_UART_PRIMARY_CONSOLE || serial) {
11362306a36Sopenharmony_ci			if (uart->type == PCDP_CONSOLE_UART) {
11462306a36Sopenharmony_ci				rc = setup_serial_console(uart);
11562306a36Sopenharmony_ci				goto out;
11662306a36Sopenharmony_ci			}
11762306a36Sopenharmony_ci		}
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	end = (struct pcdp_device *) ((u8 *) pcdp + pcdp->length);
12162306a36Sopenharmony_ci	for (dev = (struct pcdp_device *) (pcdp->uart + pcdp->num_uarts);
12262306a36Sopenharmony_ci	     dev < end;
12362306a36Sopenharmony_ci	     dev = (struct pcdp_device *) ((u8 *) dev + dev->length)) {
12462306a36Sopenharmony_ci		if (dev->flags & PCDP_PRIMARY_CONSOLE) {
12562306a36Sopenharmony_ci			if (dev->type == PCDP_CONSOLE_VGA) {
12662306a36Sopenharmony_ci				rc = setup_vga_console(dev);
12762306a36Sopenharmony_ci				goto out;
12862306a36Sopenharmony_ci			}
12962306a36Sopenharmony_ci		}
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ciout:
13362306a36Sopenharmony_ci	early_memunmap(pcdp, 4096);
13462306a36Sopenharmony_ci	return rc;
13562306a36Sopenharmony_ci}
136