Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@home.transmeta.com>2003-06-13 21:49:55 -0700
committerLinus Torvalds <torvalds@home.transmeta.com>2003-06-13 21:49:55 -0700
commit758361cb4addbd37bcb45cc15b3c3fff1ca661c9 (patch)
treed98207b63d31dbdaa1f9af1b1e9c650ae86d88a5
parentf3d844bce28bb95b18af48cd634f75ea6454530b (diff)
parentc5ff7ff54c11212a594301d5373e803bb874659c (diff)
Merge bk://bk.arm.linux.org.uk/linux-2.5-serial
into home.transmeta.com:/home/torvalds/v2.5/linux
-rw-r--r--drivers/serial/8250.c6
-rw-r--r--drivers/serial/8250_acpi.c110
-rw-r--r--drivers/serial/8250_hcdp.c249
-rw-r--r--drivers/serial/8250_hcdp.h79
-rw-r--r--drivers/serial/Kconfig9
-rw-r--r--drivers/serial/Makefile3
-rw-r--r--include/linux/acpi_serial.h2
-rw-r--r--include/linux/serial.h11
8 files changed, 459 insertions, 10 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)
diff --git a/include/linux/acpi_serial.h b/include/linux/acpi_serial.h
index 07fb16f5a2b3..e4b87c50ff3a 100644
--- a/include/linux/acpi_serial.h
+++ b/include/linux/acpi_serial.h
@@ -9,6 +9,8 @@
*
*/
+#include <linux/serial.h>
+
extern void setup_serial_acpi(void *);
#define ACPI_SIG_LEN 4
diff --git a/include/linux/serial.h b/include/linux/serial.h
index aceee0c46cd8..677e67b18220 100644
--- a/include/linux/serial.h
+++ b/include/linux/serial.h
@@ -180,14 +180,9 @@ struct serial_icounter_struct {
extern int register_serial(struct serial_struct *req);
extern void unregister_serial(int line);
-/* Allow complicated architectures to specify rs_table[] at run time */
-extern int early_serial_setup(struct serial_struct *req);
-
-#ifdef CONFIG_ACPI
-/* tty ports reserved for the ACPI serial console port and debug port */
-#define ACPI_SERIAL_CONSOLE_PORT 4
-#define ACPI_SERIAL_DEBUG_PORT 5
-#endif
+/* Allow architectures to override entries in serial8250_ports[] at run time: */
+struct uart_port; /* forward declaration */
+extern int early_serial_setup(struct uart_port *port);
#endif /* __KERNEL__ */
#endif /* _LINUX_SERIAL_H */