diff options
| author | Linus Torvalds <torvalds@home.transmeta.com> | 2003-06-13 21:49:55 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.transmeta.com> | 2003-06-13 21:49:55 -0700 |
| commit | 758361cb4addbd37bcb45cc15b3c3fff1ca661c9 (patch) | |
| tree | d98207b63d31dbdaa1f9af1b1e9c650ae86d88a5 /drivers/serial | |
| parent | f3d844bce28bb95b18af48cd634f75ea6454530b (diff) | |
| parent | c5ff7ff54c11212a594301d5373e803bb874659c (diff) | |
Merge bk://bk.arm.linux.org.uk/linux-2.5-serial
into home.transmeta.com:/home/torvalds/v2.5/linux
Diffstat (limited to 'drivers/serial')
| -rw-r--r-- | drivers/serial/8250.c | 6 | ||||
| -rw-r--r-- | drivers/serial/8250_acpi.c | 110 | ||||
| -rw-r--r-- | drivers/serial/8250_hcdp.c | 249 | ||||
| -rw-r--r-- | drivers/serial/8250_hcdp.h | 79 | ||||
| -rw-r--r-- | drivers/serial/Kconfig | 9 | ||||
| -rw-r--r-- | drivers/serial/Makefile | 3 |
6 files changed, 454 insertions, 2 deletions
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 5aa42b8842ec..ea3f6bedf239 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -2061,9 +2061,11 @@ int register_serial(struct serial_struct *req) return __register_serial(req, -1); } -int __init early_serial_setup(struct serial_struct *req) +int __init early_serial_setup(struct uart_port *port) { - __register_serial(req, req->line); + serial8250_isa_init_ports(); + serial8250_ports[port->line].port = *port; + serial8250_ports[port->line].port.ops = &serial8250_pops; return 0; } diff --git a/drivers/serial/8250_acpi.c b/drivers/serial/8250_acpi.c new file mode 100644 index 000000000000..7692b54a9ad6 --- /dev/null +++ b/drivers/serial/8250_acpi.c @@ -0,0 +1,110 @@ +/* + * serial/acpi.c + * Copyright (c) 2002-2003 Matthew Wilcox for Hewlett-Packard + * + * 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. + */ + +#include <linux/acpi.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/serial.h> + +#include <acpi/acpi_bus.h> + +#include <asm/io.h> +#include <asm/serial.h> + +static void acpi_serial_address(struct serial_struct *req, + struct acpi_resource_address32 *addr32) +{ + unsigned long size; + + size = addr32->max_address_range - addr32->min_address_range + 1; + req->iomap_base = addr32->min_address_range; + req->iomem_base = ioremap(req->iomap_base, size); + req->io_type = SERIAL_IO_MEM; +} + +static void acpi_serial_irq(struct serial_struct *req, + struct acpi_resource_ext_irq *ext_irq) +{ + if (ext_irq->number_of_interrupts > 0) { +#ifdef CONFIG_IA64 + req->irq = acpi_register_irq(ext_irq->interrupts[0], + ext_irq->active_high_low, ext_irq->edge_level); +#else + req->irq = ext_irq->interrupts[0]; +#endif + } +} + +static int acpi_serial_add(struct acpi_device *device) +{ + acpi_status result; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct serial_struct serial_req; + int line, offset = 0; + + memset(&serial_req, 0, sizeof(serial_req)); + result = acpi_get_current_resources(device->handle, &buffer); + if (ACPI_FAILURE(result)) { + result = -ENODEV; + goto out; + } + + while (offset <= buffer.length) { + struct acpi_resource *res = buffer.pointer + offset; + if (res->length == 0) + break; + offset += res->length; + if (res->id == ACPI_RSTYPE_ADDRESS32) { + acpi_serial_address(&serial_req, &res->data.address32); + } else if (res->id == ACPI_RSTYPE_EXT_IRQ) { + acpi_serial_irq(&serial_req, &res->data.extended_irq); + } + } + + serial_req.baud_base = BASE_BAUD; + serial_req.flags = ASYNC_SKIP_TEST|ASYNC_BOOT_AUTOCONF|ASYNC_AUTO_IRQ; + + result = 0; + line = register_serial(&serial_req); + if (line < 0) + result = -ENODEV; + + out: + acpi_os_free(buffer.pointer); + return result; +} + +static int acpi_serial_remove(struct acpi_device *device, int type) +{ + return 0; +} + +static struct acpi_driver acpi_serial_driver = { + .name = "serial", + .class = "", + .ids = "PNP0501", + .ops = { + .add = acpi_serial_add, + .remove = acpi_serial_remove, + }, +}; + +static int __init acpi_serial_init(void) +{ + return acpi_bus_register_driver(&acpi_serial_driver); +} + +static void __exit acpi_serial_exit(void) +{ + acpi_bus_unregister_driver(&acpi_serial_driver); +} + +module_init(acpi_serial_init); +module_exit(acpi_serial_exit); diff --git a/drivers/serial/8250_hcdp.c b/drivers/serial/8250_hcdp.c new file mode 100644 index 000000000000..dbc39d6bcd6b --- /dev/null +++ b/drivers/serial/8250_hcdp.c @@ -0,0 +1,249 @@ +/* + * linux/drivers/char/hcdp_serial.c + * + * Copyright (C) 2002 Hewlett-Packard Co. + * Khalid Aziz <khalid_aziz@hp.com> + * + * Parse the EFI HCDP table to locate serial console and debug ports and + * initialize them. + * + * 2002/08/29 davidm Adjust it to new 2.5 serial driver infrastructure. + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/efi.h> +#include <linux/init.h> +#include <linux/tty.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/types.h> +#include <linux/acpi.h> + +#include <asm/io.h> +#include <asm/serial.h> +#include <asm/acpi.h> + +#include "8250_hcdp.h" + +#undef SERIAL_DEBUG_HCDP + +/* + * Parse the HCDP table to find descriptions for headless console and debug + * serial ports and add them to rs_table[]. A pointer to HCDP table is + * passed as parameter. This function should be called before + * serial_console_init() is called to make sure the HCDP serial console will + * be available for use. IA-64 kernel calls this function from setup_arch() + * after the EFI and ACPI tables have been parsed. + */ +void __init +setup_serial_hcdp(void *tablep) +{ + hcdp_dev_t *hcdp_dev; + struct uart_port port; + unsigned long iobase; + hcdp_t hcdp; + int gsi, nr; +#if 0 + static int shift_once = 1; +#endif + +#ifdef SERIAL_DEBUG_HCDP + printk("Entering setup_serial_hcdp()\n"); +#endif + + /* Verify we have a valid table pointer */ + if (!tablep) + return; + + memset(&port, 0, sizeof(port)); + + /* + * Don't trust firmware to give us a table starting at an aligned + * address. Make a local copy of the HCDP table with aligned + * structures. + */ + memcpy(&hcdp, tablep, sizeof(hcdp)); + + /* + * Perform a sanity check on the table. Table should have a signature + * of "HCDP" and it should be atleast 82 bytes long to have any + * useful information. + */ + if ((strncmp(hcdp.signature, HCDP_SIGNATURE, HCDP_SIG_LEN) != 0)) + return; + if (hcdp.len < 82) + return; + +#ifdef SERIAL_DEBUG_HCDP + printk("setup_serial_hcdp(): table pointer = 0x%p, sig = '%.4s'\n", + tablep, hcdp.signature); + printk(" length = %d, rev = %d, ", hcdp.len, hcdp.rev); + printk("OEM ID = %.6s, # of entries = %d\n", hcdp.oemid, + hcdp.num_entries); +#endif + + /* + * Parse each device entry + */ + for (nr = 0; nr < hcdp.num_entries; nr++) { + hcdp_dev = hcdp.hcdp_dev + nr; + /* + * We will parse only the primary console device which is + * the first entry for these devices. We will ignore rest + * of the entries for the same type device that has already + * been parsed and initialized + */ + if (hcdp_dev->type != HCDP_DEV_CONSOLE) + continue; + + iobase = ((u64) hcdp_dev->base_addr.addrhi << 32) | + hcdp_dev->base_addr.addrlo; + gsi = hcdp_dev->global_int; + + /* See PCI spec v2.2, Appendix D (Class Codes): */ + switch (hcdp_dev->pci_prog_intfc) { + case 0x00: + port.type = PORT_8250; + break; + case 0x01: + port.type = PORT_16450; + break; + case 0x02: + port.type = PORT_16550; + break; + case 0x03: + port.type = PORT_16650; + break; + case 0x04: + port.type = PORT_16750; + break; + case 0x05: + port.type = PORT_16850; + break; + case 0x06: + port.type = PORT_16C950; + break; + default: + printk(KERN_WARNING "warning: EFI HCDP table reports " + "unknown serial programming interface 0x%02x; " + "will autoprobe.\n", hcdp_dev->pci_prog_intfc); + port.type = PORT_UNKNOWN; + break; + } + +#ifdef SERIAL_DEBUG_HCDP + printk(" type = %s, uart = %d\n", + ((hcdp_dev->type == HCDP_DEV_CONSOLE) ? + "Headless Console" : + ((hcdp_dev->type == HCDP_DEV_DEBUG) ? + "Debug port" : "Huh????")), port.type); + printk(" base address space = %s, base address = 0x%lx\n", + ((hcdp_dev->base_addr.space_id == ACPI_MEM_SPACE) ? + "Memory Space" : + ((hcdp_dev->base_addr.space_id == ACPI_IO_SPACE) ? + "I/O space" : "PCI space")), + iobase); + printk(" gsi = %d, baud rate = %lu, bits = %d, clock = %d\n", + gsi, (unsigned long) hcdp_dev->baud, hcdp_dev->bits, + hcdp_dev->clock_rate); + if (hcdp_dev->base_addr.space_id == ACPI_PCICONF_SPACE) + printk(" PCI id: %02x:%02x:%02x, vendor ID=0x%x, " + "dev ID=0x%x\n", hcdp_dev->pci_seg, + hcdp_dev->pci_bus, hcdp_dev->pci_dev, + hcdp_dev->pci_vendor_id, hcdp_dev->pci_dev_id); +#endif + /* + * Now fill in a port structure to update the 8250 port table.. + */ + if (hcdp_dev->clock_rate) + port.uartclk = hcdp_dev->clock_rate; + else + port.uartclk = BASE_BAUD * 16; + + /* + * Check if this is an I/O mapped address or a memory mapped + * address + */ + if (hcdp_dev->base_addr.space_id == ACPI_MEM_SPACE) { + port.iobase = 0; + port.mapbase = iobase; + port.membase = ioremap(iobase, 64); + port.iotype = SERIAL_IO_MEM; + } else if (hcdp_dev->base_addr.space_id == ACPI_IO_SPACE) { + port.iobase = iobase; + port.mapbase = 0; + port.membase = NULL; + port.iotype = SERIAL_IO_PORT; + } else if (hcdp_dev->base_addr.space_id == ACPI_PCICONF_SPACE) { + printk(KERN_WARNING"warning: No support for PCI serial console\n"); + return; + } +#ifdef CONFIG_IA64 + port.irq = acpi_register_irq(gsi, ACPI_ACTIVE_HIGH, + ACPI_EDGE_SENSITIVE); +#else + port.irq = gsi; +#endif + port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; + if (gsi) + port.flags |= ASYNC_AUTO_IRQ; + + /* + * Note: the above memset() initializes port.line to 0, + * so we register this port as ttyS0. + */ + if (early_serial_setup(&port) < 0) { + printk("setup_serial_hcdp(): early_serial_setup() " + "for HCDP serial console port failed. " + "Will try any additional consoles in HCDP.\n"); + continue; + } + break; + } + +#ifdef SERIAL_DEBUG_HCDP + printk("Leaving setup_serial_hcdp()\n"); +#endif +} + +#ifdef CONFIG_IA64_EARLY_PRINTK_UART +unsigned long +hcdp_early_uart (void) +{ + efi_system_table_t *systab; + efi_config_table_t *config_tables; + unsigned long addr = 0; + hcdp_t *hcdp = 0; + hcdp_dev_t *dev; + int i; + + systab = (efi_system_table_t *) ia64_boot_param->efi_systab; + if (!systab) + return 0; + systab = __va(systab); + + config_tables = (efi_config_table_t *) systab->tables; + if (!config_tables) + return 0; + config_tables = __va(config_tables); + + for (i = 0; i < systab->nr_tables; i++) { + if (efi_guidcmp(config_tables[i].guid, HCDP_TABLE_GUID) == 0) { + hcdp = (hcdp_t *) config_tables[i].table; + break; + } + } + if (!hcdp) + return 0; + hcdp = __va(hcdp); + + for (i = 0, dev = hcdp->hcdp_dev; i < hcdp->num_entries; i++, dev++) { + if (dev->type == HCDP_DEV_CONSOLE) { + addr = (u64) dev->base_addr.addrhi << 32 | dev->base_addr.addrlo; + break; + } + } + return addr; +} +#endif /* CONFIG_IA64_EARLY_PRINTK_UART */ diff --git a/drivers/serial/8250_hcdp.h b/drivers/serial/8250_hcdp.h new file mode 100644 index 000000000000..8ee1b60bc430 --- /dev/null +++ b/drivers/serial/8250_hcdp.h @@ -0,0 +1,79 @@ +/* + * drivers/serial/8250_hcdp.h + * + * Copyright (C) 2002 Hewlett-Packard Co. + * Khalid Aziz <khalid_aziz@hp.com> + * + * Definitions for HCDP defined serial ports (Serial console and debug + * ports) + */ + +/* ACPI table signatures */ +#define HCDP_SIG_LEN 4 +#define HCDP_SIGNATURE "HCDP" + +/* Space ID as defined in ACPI generic address structure */ +#define ACPI_MEM_SPACE 0 +#define ACPI_IO_SPACE 1 +#define ACPI_PCICONF_SPACE 2 + +/* + * Maximum number of HCDP devices we want to read in + */ +#define MAX_HCDP_DEVICES 6 + +/* + * Default UART clock rate if clock rate is 0 in HCDP table. + */ +#define DEFAULT_UARTCLK 115200 + +/* + * ACPI Generic Address Structure + */ +typedef struct { + u8 space_id; + u8 bit_width; + u8 bit_offset; + u8 resv; + u32 addrlo; + u32 addrhi; +} acpi_gen_addr; + +/* HCDP Device descriptor entry types */ +#define HCDP_DEV_CONSOLE 0 +#define HCDP_DEV_DEBUG 1 + +/* HCDP Device descriptor type */ +typedef struct { + u8 type; + u8 bits; + u8 parity; + u8 stop_bits; + u8 pci_seg; + u8 pci_bus; + u8 pci_dev; + u8 pci_func; + u64 baud; + acpi_gen_addr base_addr; + u16 pci_dev_id; + u16 pci_vendor_id; + u32 global_int; + u32 clock_rate; + u8 pci_prog_intfc; + u8 resv; +} hcdp_dev_t; + +/* HCDP Table format */ +typedef struct { + u8 signature[4]; + u32 len; + u8 rev; + u8 chksum; + u8 oemid[6]; + u8 oem_tabid[8]; + u32 oem_rev; + u8 creator_id[4]; + u32 creator_rev; + u32 num_entries; + hcdp_dev_t hcdp_dev[MAX_HCDP_DEVICES]; +} hcdp_t; diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 48aec4efe562..17920b217f92 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -77,6 +77,15 @@ config SERIAL_8250_CS a module, say M here and read <file:Documentation/modules.txt>. If unsure, say N. +config SERIAL_HCDP + bool "8250/16550 device discovery support via EFI HCDP table" + depends on IA64 + ---help--- + If you wish to make the serial console port described by the EFI + HCDP table available for use as serial console or general + purpose port, say Y here. See + <http://www.dig64.org/specifications/DIG64_HCDPv10a_01.pdf>. + config SERIAL_8250_EXTENDED bool "Extended 8250/16550 serial driver options" depends on SERIAL_8250 diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 35631fa10366..61b393b41d2a 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -8,6 +8,9 @@ serial-8250-y := serial-8250-$(CONFIG_GSC) += 8250_gsc.o serial-8250-$(CONFIG_PCI) += 8250_pci.o serial-8250-$(CONFIG_PNP) += 8250_pnp.o +serial-8250-$(CONFIG_ACPI) += acpi.o +serial-8250-$(CONFIG_SERIAL_HCDP) += 8250_hcdp.o + obj-$(CONFIG_SERIAL_CORE) += core.o obj-$(CONFIG_SERIAL_21285) += 21285.o obj-$(CONFIG_SERIAL_8250) += 8250.o $(serial-8250-y) |
