mirror of
https://github.com/ipxe/ipxe
synced 2026-01-23 12:38:58 +03:00
Some systems (observed on an AWS EC2 m7i.metal-24xl instance in eu-south-2) use the newer "16550-compatible with parameters defined in Generic Address Structure" type value. (There does not appear to be any particular reason why the newer value needs to be used: the UART is still a standard 16550 with single-byte registers.) Accept this additional type value for a 16550-compatible UART. Signed-off-by: Michael Brown <mcb30@ipxe.org>
164 lines
4.5 KiB
C
164 lines
4.5 KiB
C
/*
|
|
* Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA.
|
|
*
|
|
* You can also choose to distribute this program under the terms of
|
|
* the Unmodified Binary Distribution Licence (as given in the file
|
|
* COPYING.UBDL), provided that you have satisfied its requirements.
|
|
*/
|
|
|
|
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|
|
|
#include <errno.h>
|
|
#include <ipxe/serial.h>
|
|
#include <ipxe/pci.h>
|
|
#include <ipxe/ns16550.h>
|
|
#include <ipxe/spcr.h>
|
|
|
|
/** @file
|
|
*
|
|
* ACPI Serial Port Console Redirection (SPCR)
|
|
*
|
|
*/
|
|
|
|
#ifdef SERIAL_SPCR
|
|
#define SERIAL_PREFIX_spcr
|
|
#else
|
|
#define SERIAL_PREFIX_spcr __spcr_
|
|
#endif
|
|
|
|
/** SPCR-defined UART */
|
|
static struct uart spcr_uart = {
|
|
.refcnt = REF_INIT ( ref_no_free ),
|
|
.name = "SPCR",
|
|
};
|
|
|
|
/** SPCR-defined 16550 UART */
|
|
static struct ns16550_uart spcr_ns16550 = {
|
|
.clock = NS16550_CLK_DEFAULT,
|
|
};
|
|
|
|
/** Base baud rate for SPCR divisors */
|
|
#define SPCR_BAUD_BASE 115200
|
|
|
|
/** SPCR baud rate divisors */
|
|
static const uint8_t spcr_baud_divisor[SPCR_BAUD_MAX] = {
|
|
[SPCR_BAUD_2400] = ( SPCR_BAUD_BASE / 2400 ),
|
|
[SPCR_BAUD_4800] = ( SPCR_BAUD_BASE / 4800 ),
|
|
[SPCR_BAUD_9600] = ( SPCR_BAUD_BASE / 9600 ),
|
|
[SPCR_BAUD_19200] = ( SPCR_BAUD_BASE / 19200 ),
|
|
[SPCR_BAUD_38400] = ( SPCR_BAUD_BASE / 38400 ),
|
|
[SPCR_BAUD_57600] = ( SPCR_BAUD_BASE / 57600 ),
|
|
[SPCR_BAUD_115200] = ( SPCR_BAUD_BASE / 115200 ),
|
|
};
|
|
|
|
/**
|
|
* Configure 16550-based serial console
|
|
*
|
|
* @v spcr SPCR table
|
|
* @v uart UART to configure
|
|
* @ret rc Return status code
|
|
*/
|
|
static int spcr_16550 ( struct spcr_table *spcr, struct uart *uart ) {
|
|
struct ns16550_uart *ns16550 = &spcr_ns16550;
|
|
|
|
/* Set base address */
|
|
ns16550->base = acpi_ioremap ( &spcr->base, NS16550_LEN );
|
|
if ( ! ns16550->base ) {
|
|
DBGC ( uart, "SPCR could not map registers\n" );
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* Set clock frequency, if specified */
|
|
if ( spcr->clock )
|
|
ns16550->clock = le32_to_cpu ( spcr->clock );
|
|
|
|
/* Configure UART as a 16550 */
|
|
uart->op = &ns16550_operations;
|
|
uart->priv = ns16550;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Identify default serial console
|
|
*
|
|
* @ret uart Default serial console UART, or NULL
|
|
*/
|
|
static struct uart * spcr_console ( void ) {
|
|
struct uart *uart = &spcr_uart;
|
|
struct spcr_table *spcr;
|
|
unsigned int baud;
|
|
int rc;
|
|
|
|
/* Locate SPCR table */
|
|
spcr = container_of ( acpi_table ( SPCR_SIGNATURE, 0 ),
|
|
struct spcr_table, acpi );
|
|
if ( ! spcr ) {
|
|
DBGC ( uart, "SPCR found no table\n" );
|
|
goto err_table;
|
|
}
|
|
DBGC2 ( uart, "SPCR found table:\n" );
|
|
DBGC2_HDA ( uart, 0, spcr, sizeof ( *spcr ) );
|
|
DBGC ( uart, "SPCR is type %d at %02x:%08llx\n",
|
|
spcr->type, spcr->base.type,
|
|
( ( unsigned long long ) le64_to_cpu ( spcr->base.address ) ) );
|
|
if ( spcr->pci_vendor_id != cpu_to_le16 ( PCI_ANY_ID ) ) {
|
|
DBGC ( uart, "SPCR is PCI " PCI_FMT " (%04x:%04x)\n",
|
|
spcr->pci_segment, spcr->pci_bus, spcr->pci_dev,
|
|
spcr->pci_func, le16_to_cpu ( spcr->pci_vendor_id ),
|
|
le16_to_cpu ( spcr->pci_device_id ) );
|
|
}
|
|
|
|
/* Get baud rate */
|
|
baud = 0;
|
|
if ( le32_to_cpu ( spcr->acpi.length ) >=
|
|
( offsetof ( typeof ( *spcr ), precise ) +
|
|
sizeof ( spcr->precise ) ) ) {
|
|
baud = le32_to_cpu ( spcr->precise );
|
|
if ( baud )
|
|
DBGC ( uart, "SPCR has precise baud rate %d\n", baud );
|
|
}
|
|
if ( ( ! baud ) && spcr->baud && ( spcr->baud < SPCR_BAUD_MAX ) ) {
|
|
baud = ( SPCR_BAUD_BASE / spcr_baud_divisor[spcr->baud] );
|
|
DBGC ( uart, "SPCR has baud rate %d\n", baud );
|
|
}
|
|
uart->baud = baud;
|
|
|
|
/* Initialise according to type */
|
|
switch ( spcr->type ) {
|
|
case SPCR_TYPE_16550:
|
|
case SPCR_TYPE_16450:
|
|
case SPCR_TYPE_16550_GAS:
|
|
if ( ( rc = spcr_16550 ( spcr, uart ) ) != 0 )
|
|
goto err_type;
|
|
break;
|
|
default:
|
|
DBGC ( uart, "SPCR unsupported type %d\n", spcr->type );
|
|
goto err_type;
|
|
}
|
|
|
|
return uart;
|
|
|
|
err_type:
|
|
err_table:
|
|
/* Fall back to using fixed serial console */
|
|
return fixed_serial_console();
|
|
}
|
|
|
|
PROVIDE_SERIAL ( spcr, default_serial_console, spcr_console );
|