Commit 1af22f6d authored by Bjorn Helgaas's avatar Bjorn Helgaas Committed by Linus Torvalds

[PATCH] move HCDP/PCDP to early uart console

This changes the HCDP/PCDP support to use the early uart console
rather than using early_serial_setup().

As a consequence, ia64 serial device names will now stay constant
regardless of firmware console settings.  (A serial device selected as
an EFI console device on HP ia64 boxes used to automatically become
ttyS0.)

This also removes the ia64 early-boot kludge of assuming legacy COM
ports at 0x3f8 and 0x2f8.  For boxes that have legacy ports but no
HCDP, "console=ttyS0" will still work, but the console won't start
working until after the serial driver initializes and discovers the
devices.

WARNING:

If you have an HP machine and you're using the MP serial console port
(the connector labelled "console" on the 3-headed cable), this patch
will break your console!

HOW TO FIX IT:

 1) The console device will change from /dev/ttyS0 to /dev/ttyS1,
    ttyS2, or ttyS3, so:

    1a) Edit /etc/inittab to add a getty entry for
           /dev/ttyS1 (rx4640, rx5670, rx7620, rx8620, Superdome),
           /dev/ttyS2 (rx1600), or
           /dev/ttyS3 (rx2600).

    1b) Edit /etc/securetty to add ttyS1, ttyS2, or ttyS3.

    1c) Leave the existing ttyS0 entries in /etc/inittab and
        /etc/securetty so you can still boot old kernels.

 2) Edit /etc/elilo.conf to remove any "console=" arguments (see [1]).

 3) Run elilo to install the bootloader with new configuration.

 4) Reboot and use the EFI boot option maintenance menu to select
    exactly one device for console output, input, and standard error.
    Then do a cold reset so the changes take effect.

    For the MP console, be careful to select the device with
    "Acpi(HWP0002,700)/Pci(...)/Uart" in the path (see [2]).

DETAILS:

  - Prior to this patch, serial device names depended on the HCDP,
    which in turn depends on EFI console settings.  After this patch,
    the naming always stays the same, regardless of firmware settings.

    For example, an rx1600 with a single built-in serial port plus
    an MP has these ports:
                                   Old             Old
                  MMIO         (EFI console    (EFI console
                 address        on builtin)     on MP port)      New
                ==========      ==========      ==========     ======
    builtin     0xff5e0000        ttyS0           ttyS1         ttyS0
    MP UPS      0xf8031000        ttyS1           ttyS2         ttyS1
    MP Console  0xf8030000        ttyS2           ttyS0         ttyS2
    MP 2        0xf8030010        ttyS3           ttyS3         ttyS3
    MP 3        0xf8030038        ttyS4           ttyS4         ttyS4

  - If you want to have multiple devices in the EFI console path, you
    can, but Linux won't be able to deduce which console to use, so it
    will default to using VGA.  You can use "console=hcdp" (the UART
    device from the EFI path) or "console=ttyS<n>" to select the
    device directly.

TROUBLESHOOTING:

  - No kernel output after "Uncompressing Linux... done":

        -> You're using an MP port as the console and specified
           "console=ttyS0".  This port is now named something else.
           Remove the "console=" option.

        -> Multiple UARTs selected as EFI console devices, and you're
           looking at the wrong one.  Make sure only one UART is
           selected (use the EFI Boot Manager "Boot option maintenance"
           menu).

        -> You're physically connected to the MP port but have a
           non-MP UART selected as EFI console device.  Either move
           the console cable to the non-MP UART, or change the EFI
           console path to the MP UART (the MP UART is the one with
           "Acpi(HWP0002,700)/Pci(...)/Uart" in it.)

  - Long pause (60+ seconds) between "Uncompressing Linux... done"
    and start of kernel output:

        -> No early console, probably because you used "console=ttyS<n>".
           Remove the "console=" option.

  - Kernel and init script output works fine, but no "login:" prompt:

        -> Add getty entry to /etc/inittab for console tty.  Use the table
           in (1a) above or look for the "Adding console on ttyS<n>" message
           that tells you which device is the console.

  - "login:" prompt, but can't login as root:

        -> Add entry to /etc/securetty for console tty.

[1] When the EFI console path contains exactly one device (either
    serial or VGA), 2.6.6 and newer kernels default to that device
    automatically.  So if you remove "console=" arguments, you can use
    the same elilo configuration to boot any 2.6.6 or newer kernel
    with or without this patch.

    If you need to boot kernels older than 2.6.6 (including RHEL3 and
    SLES9), keep an 'append="console=ttyS0"' line in those elilo.conf
    stanzas.

    Non-HP machines will still need "console=" for serial consoles
    because they don't supply the HCDP table.

[2] The HP management card (MP) causes confusion because it is always
    active as an EFI console, even if it doesn't appear in the EFI
    console path.  If your console path is set to a non-MP UART, and
    you happen to be attached to the MP UART, everything works in EFI,
    but the kernel will think the non-MP UART is the console, so you
    won't see any kernel output.
Signed-off-by: default avatarBjorn Helgaas <bjorn.helgaas@hp.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 5b6c5349
...@@ -258,25 +258,6 @@ io_port_init (void) ...@@ -258,25 +258,6 @@ io_port_init (void)
num_io_spaces = 1; num_io_spaces = 1;
} }
#ifdef CONFIG_SERIAL_8250_CONSOLE
static void __init
setup_serial_legacy (void)
{
struct uart_port port;
unsigned int i, iobase[] = {0x3f8, 0x2f8};
printk(KERN_INFO "Registering legacy COM ports for serial console\n");
memset(&port, 0, sizeof(port));
port.iotype = SERIAL_IO_PORT;
port.uartclk = BASE_BAUD * 16;
for (i = 0; i < ARRAY_SIZE(iobase); i++) {
port.line = i;
port.iobase = iobase[i];
early_serial_setup(&port);
}
}
#endif
/** /**
* early_console_setup - setup debugging console * early_console_setup - setup debugging console
* *
...@@ -287,15 +268,23 @@ setup_serial_legacy (void) ...@@ -287,15 +268,23 @@ setup_serial_legacy (void)
* Returns non-zero if a console couldn't be setup. * Returns non-zero if a console couldn't be setup.
*/ */
static inline int __init static inline int __init
early_console_setup (void) early_console_setup (char *cmdline)
{ {
#ifdef CONFIG_SERIAL_SGI_L1_CONSOLE #ifdef CONFIG_SERIAL_SGI_L1_CONSOLE
{ {
extern int sn_serial_console_early_setup(void); extern int sn_serial_console_early_setup(void);
if(!sn_serial_console_early_setup()) if (!sn_serial_console_early_setup())
return 0; return 0;
} }
#endif #endif
#ifdef CONFIG_EFI_PCDP
if (!efi_setup_pcdp_console(cmdline))
return 0;
#endif
#ifdef CONFIG_SERIAL_8250_CONSOLE
if (!early_serial_console_init(cmdline))
return 0;
#endif
return -1; return -1;
} }
...@@ -319,7 +308,7 @@ setup_arch (char **cmdline_p) ...@@ -319,7 +308,7 @@ setup_arch (char **cmdline_p)
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
/* If we register an early console, allow CPU 0 to printk */ /* If we register an early console, allow CPU 0 to printk */
if (!early_console_setup()) if (!early_console_setup(*cmdline_p))
cpu_set(smp_processor_id(), cpu_online_map); cpu_set(smp_processor_id(), cpu_online_map);
#endif #endif
...@@ -349,13 +338,6 @@ setup_arch (char **cmdline_p) ...@@ -349,13 +338,6 @@ setup_arch (char **cmdline_p)
#ifdef CONFIG_ACPI_BOOT #ifdef CONFIG_ACPI_BOOT
acpi_boot_init(); acpi_boot_init();
#endif #endif
#ifdef CONFIG_EFI_PCDP
efi_setup_pcdp_console(*cmdline_p);
#endif
#ifdef CONFIG_SERIAL_8250_CONSOLE
if (!efi.hcdp)
setup_serial_legacy();
#endif
#ifdef CONFIG_VT #ifdef CONFIG_VT
if (!conswitchp) { if (!conswitchp) {
......
...@@ -48,7 +48,11 @@ config EFI_PCDP ...@@ -48,7 +48,11 @@ config EFI_PCDP
use the first serial port it describes as the Linux console, use the first serial port it describes as the Linux console,
say Y here. If your EFI ConOut path contains only a UART say Y here. If your EFI ConOut path contains only a UART
device, it will become the console automatically. Otherwise, device, it will become the console automatically. Otherwise,
you must specify the "console=ttyS0" kernel boot argument. you must specify the "console=hcdp" kernel boot argument.
Neither the PCDP nor the HCDP affects naming of serial devices,
so a serial console may be /dev/ttyS0, /dev/ttyS1, etc, depending
on how the driver discovers devices.
You must also enable the appropriate drivers (serial, VGA, etc.) You must also enable the appropriate drivers (serial, VGA, etc.)
......
...@@ -14,127 +14,45 @@ ...@@ -14,127 +14,45 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/console.h> #include <linux/console.h>
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/tty.h>
#include <linux/serial.h> #include <linux/serial.h>
#include <linux/serial_core.h>
#include <asm/io.h>
#include <asm/serial.h>
#include "pcdp.h" #include "pcdp.h"
static inline int static int __init
uart_irq_supported(int rev, struct pcdp_uart *uart) setup_serial_console(struct pcdp_uart *uart)
{
if (rev < 3)
return uart->pci_func & PCDP_UART_IRQ;
return uart->flags & PCDP_UART_IRQ;
}
static inline int
uart_pci(int rev, struct pcdp_uart *uart)
{
if (rev < 3)
return uart->pci_func & PCDP_UART_PCI;
return uart->flags & PCDP_UART_PCI;
}
static inline int
uart_active_high_low(int rev, struct pcdp_uart *uart)
{
if (uart_pci(rev, uart) || uart->flags & PCDP_UART_ACTIVE_LOW)
return ACPI_ACTIVE_LOW;
return ACPI_ACTIVE_HIGH;
}
static inline int
uart_edge_level(int rev, struct pcdp_uart *uart)
{
if (uart_pci(rev, uart))
return ACPI_LEVEL_SENSITIVE;
if (rev < 3 || uart->flags & PCDP_UART_EDGE_SENSITIVE)
return ACPI_EDGE_SENSITIVE;
return ACPI_LEVEL_SENSITIVE;
}
static void __init
setup_serial_console(int rev, struct pcdp_uart *uart)
{ {
#ifdef CONFIG_SERIAL_8250_CONSOLE #ifdef CONFIG_SERIAL_8250_CONSOLE
struct uart_port port; int mmio;
static char options[16]; static char options[64];
int mapsize = 64;
memset(&port, 0, sizeof(port));
port.uartclk = uart->clock_rate;
if (!port.uartclk) /* some FW doesn't supply this */
port.uartclk = BASE_BAUD * 16;
if (uart->addr.address_space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
port.mapbase = uart->addr.address;
port.membase = ioremap(port.mapbase, mapsize);
if (!port.membase) {
printk(KERN_ERR "%s: couldn't ioremap 0x%lx-0x%lx\n",
__FUNCTION__, port.mapbase, port.mapbase + mapsize);
return;
}
port.iotype = UPIO_MEM;
} else if (uart->addr.address_space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
port.iobase = uart->addr.address;
port.iotype = UPIO_PORT;
} else
return;
switch (uart->pci_prog_intfc) {
case 0x0: port.type = PORT_8250; break;
case 0x1: port.type = PORT_16450; break;
case 0x2: port.type = PORT_16550; break;
case 0x3: port.type = PORT_16650; break;
case 0x4: port.type = PORT_16750; break;
case 0x5: port.type = PORT_16850; break;
case 0x6: port.type = PORT_16C950; break;
default: port.type = PORT_UNKNOWN; break;
}
port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
if (uart_irq_supported(rev, uart)) {
port.irq = acpi_register_gsi(uart->gsi,
uart_edge_level(rev, uart),
uart_active_high_low(rev, uart));
port.flags |= UPF_AUTO_IRQ; /* some FW reported wrong GSI */
if (uart_pci(rev, uart))
port.flags |= UPF_SHARE_IRQ;
}
if (early_serial_setup(&port) < 0)
return;
snprintf(options, sizeof(options), "%lun%d", uart->baud, mmio = (uart->addr.address_space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY);
snprintf(options, sizeof(options), "console=uart,%s,0x%lx,%lun%d",
mmio ? "mmio" : "io", uart->addr.address, uart->baud,
uart->bits ? uart->bits : 8); uart->bits ? uart->bits : 8);
add_preferred_console("ttyS", port.line, options);
printk(KERN_INFO "PCDP: serial console at %s 0x%lx (ttyS%d, options %s)\n", return early_serial_console_init(options);
port.iotype == UPIO_MEM ? "MMIO" : "I/O", #else
uart->addr.address, port.line, options); return -ENODEV;
#endif #endif
} }
static void __init static int __init
setup_vga_console(struct pcdp_vga *vga) setup_vga_console(struct pcdp_vga *vga)
{ {
#ifdef CONFIG_VT #if defined(CONFIG_VT) && defined(CONFIG_VGA_CONSOLE)
#ifdef CONFIG_VGA_CONSOLE
if (efi_mem_type(0xA0000) == EFI_CONVENTIONAL_MEMORY) { if (efi_mem_type(0xA0000) == EFI_CONVENTIONAL_MEMORY) {
printk(KERN_ERR "PCDP: VGA selected, but frame buffer is not MMIO!\n"); printk(KERN_ERR "PCDP: VGA selected, but frame buffer is not MMIO!\n");
return; return -ENODEV;
} }
conswitchp = &vga_con; conswitchp = &vga_con;
printk(KERN_INFO "PCDP: VGA console\n"); printk(KERN_INFO "PCDP: VGA console\n");
#endif return 0;
#else
return -ENODEV;
#endif #endif
} }
void __init int __init
efi_setup_pcdp_console(char *cmdline) efi_setup_pcdp_console(char *cmdline)
{ {
struct pcdp *pcdp; struct pcdp *pcdp;
...@@ -144,20 +62,25 @@ efi_setup_pcdp_console(char *cmdline) ...@@ -144,20 +62,25 @@ efi_setup_pcdp_console(char *cmdline)
pcdp = efi.hcdp; pcdp = efi.hcdp;
if (!pcdp) if (!pcdp)
return; return -ENODEV;
printk(KERN_INFO "PCDP: v%d at 0x%p\n", pcdp->rev, pcdp); printk(KERN_INFO "PCDP: v%d at 0x%lx\n", pcdp->rev, __pa(pcdp));
if (pcdp->rev < 3) { if (strstr(cmdline, "console=hcdp")) {
if (strstr(cmdline, "console=ttyS0") || efi_uart_console_only()) if (pcdp->rev < 3)
serial = 1; serial = 1;
} else if (strstr(cmdline, "console=")) {
printk(KERN_INFO "Explicit \"console=\"; ignoring PCDP\n");
return -ENODEV;
} }
if (pcdp->rev < 3 && efi_uart_console_only())
serial = 1;
for (i = 0, uart = pcdp->uart; i < pcdp->num_uarts; i++, uart++) { for (i = 0, uart = pcdp->uart; i < pcdp->num_uarts; i++, uart++) {
if (uart->flags & PCDP_UART_PRIMARY_CONSOLE || serial) { if (uart->flags & PCDP_UART_PRIMARY_CONSOLE || serial) {
if (uart->type == PCDP_CONSOLE_UART) { if (uart->type == PCDP_CONSOLE_UART) {
setup_serial_console(pcdp->rev, uart); return setup_serial_console(uart);
return;
} }
} }
} }
...@@ -168,11 +91,12 @@ efi_setup_pcdp_console(char *cmdline) ...@@ -168,11 +91,12 @@ efi_setup_pcdp_console(char *cmdline)
dev = (struct pcdp_device *) ((u8 *) dev + dev->length)) { dev = (struct pcdp_device *) ((u8 *) dev + dev->length)) {
if (dev->flags & PCDP_PRIMARY_CONSOLE) { if (dev->flags & PCDP_PRIMARY_CONSOLE) {
if (dev->type == PCDP_CONSOLE_VGA) { if (dev->type == PCDP_CONSOLE_VGA) {
setup_vga_console((struct pcdp_vga *) dev); return setup_vga_console((struct pcdp_vga *) dev);
return;
} }
} }
} }
return -ENODEV;
} }
#ifdef CONFIG_IA64_EARLY_PRINTK_UART #ifdef CONFIG_IA64_EARLY_PRINTK_UART
......
...@@ -306,7 +306,7 @@ extern int __init efi_set_rtc_mmss(unsigned long nowtime); ...@@ -306,7 +306,7 @@ extern int __init efi_set_rtc_mmss(unsigned long nowtime);
extern struct efi_memory_map memmap; extern struct efi_memory_map memmap;
#ifdef CONFIG_EFI_PCDP #ifdef CONFIG_EFI_PCDP
extern void __init efi_setup_pcdp_console(char *); extern int __init efi_setup_pcdp_console(char *);
#endif #endif
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment