- Separate out show_stack changes into own patch.
[linux-flexiantxendom0-3.2.10.git] / drivers / serial / acpi.c
1 /*
2  * serial/acpi.c
3  * Copyright (c) 2002-2003 Matthew Wilcox for Hewlett-Packard
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  */
10
11 #include <linux/acpi.h>
12 #include <linux/init.h>
13 #include <linux/module.h>
14 #include <linux/serial.h>
15
16 #include <acpi/acpi_bus.h>
17
18 #include <asm/io.h>
19 #include <asm/serial.h>
20
21 static void acpi_serial_address(struct serial_struct *req, struct acpi_resource_address32 *addr32)
22 {
23         unsigned long size;
24
25         size = addr32->max_address_range - addr32->min_address_range + 1;
26         req->iomap_base = addr32->min_address_range;
27         req->iomem_base = ioremap(req->iomap_base, size);
28         req->io_type = SERIAL_IO_MEM;
29 }
30
31 static void acpi_serial_irq(struct serial_struct *req, struct acpi_resource_ext_irq *ext_irq)
32 {
33         if (ext_irq->number_of_interrupts > 0) {
34 #ifdef CONFIG_IA64
35                 req->irq = acpi_register_irq(ext_irq->interrupts[0],
36                           ext_irq->active_high_low, ext_irq->edge_level);
37 #else
38                 req->irq = ext_irq->interrupts[0];
39 #endif
40         }
41 }
42
43 static int acpi_serial_add(struct acpi_device *device)
44 {
45         acpi_status result;
46         struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
47         struct serial_struct serial_req;
48         int line, offset = 0;
49
50         memset(&serial_req, 0, sizeof(serial_req));
51         result = acpi_get_current_resources(device->handle, &buffer);
52         if (ACPI_FAILURE(result)) {
53                 result = -ENODEV;
54                 goto out;
55         }
56
57         while (offset <= buffer.length) {
58                 struct acpi_resource *res = buffer.pointer + offset;
59                 if (res->length == 0)
60                         break;
61                 offset += res->length;
62                 if (res->id == ACPI_RSTYPE_ADDRESS32) {
63                         acpi_serial_address(&serial_req, &res->data.address32);
64                 } else if (res->id == ACPI_RSTYPE_EXT_IRQ) {
65                         acpi_serial_irq(&serial_req, &res->data.extended_irq);
66                 }
67         }
68
69         serial_req.baud_base = BASE_BAUD;
70         serial_req.flags = ASYNC_SKIP_TEST|ASYNC_BOOT_AUTOCONF|ASYNC_AUTO_IRQ;
71
72         result = 0;
73         line = register_serial(&serial_req);
74         if (line < 0)
75                 result = -ENODEV;
76
77  out:
78         acpi_os_free(buffer.pointer);
79         return result;
80 }
81
82 static int acpi_serial_remove(struct acpi_device *device, int type)
83 {
84         return 0;
85 }
86
87 static struct acpi_driver acpi_serial_driver = {
88         .name =         "serial",
89         .class =        "",
90         .ids =          "PNP0501",
91         .ops =  {
92                 .add =          acpi_serial_add,
93                 .remove =       acpi_serial_remove,
94         },
95 };
96
97 static int __init acpi_serial_init(void)
98 {
99         acpi_bus_register_driver(&acpi_serial_driver);
100         return 0;
101 }
102
103 static void __exit acpi_serial_exit(void)
104 {
105         acpi_bus_unregister_driver(&acpi_serial_driver);
106 }
107
108 module_init(acpi_serial_init);
109 module_exit(acpi_serial_exit);