import of upstream 2.4.34.4 from kernel.org
[linux-2.4.git] / drivers / acorn / char / serial-card.c
1 /*
2  *  linux/drivers/acorn/char/serial-card.c
3  *
4  *  Copyright (C) 1996-1999 Russell King.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * A generic handler of serial expansion cards that use 16550s or
11  * the like.
12  *
13  * Definitions:
14  *  MY_PRODS            Product numbers to identify this card by
15  *  MY_MANUS            Manufacturer numbers to identify this card by
16  *  MY_NUMPORTS         Number of ports per card
17  *  MY_BAUD_BASE        Baud base for the card
18  *  MY_INIT             Initialisation routine name
19  *  MY_BASE_ADDRESS(ec) Return base address for ports
20  *  MY_PORT_ADDRESS
21  *      (port,cardaddr) Return address for port using base address
22  *                      from above.
23  *
24  * Changelog:
25  *  30-07-1996  RMK     Created
26  *  22-04-1998  RMK     Removed old register_pre_init_serial
27  */
28 #include <linux/module.h>
29 #include <linux/types.h>
30 #include <linux/tty.h>
31 #include <linux/serial_core.h>
32 #include <linux/errno.h>
33 #include <linux/ioport.h>
34 #include <linux/slab.h>
35 #include <linux/init.h>
36
37 #include <asm/io.h>
38 #include <asm/ecard.h>
39 #include <asm/string.h>
40
41 struct serial_card_info {
42         unsigned int    num_ports;
43         int             ports[MAX_PORTS];
44 };
45
46 static inline int
47 serial_register_onedev(unsigned long baddr, void *vaddr, int irq, unsigned int baud_base)
48 {
49         struct serial_struct req;
50
51         memset(&req, 0, sizeof(req));
52         req.irq                 = irq;
53         req.flags               = UPF_AUTOPROBE | UPF_RESOURCES |
54                                   UPF_SHARE_IRQ;
55         req.baud_base           = baud_base;
56         req.io_type             = UPIO_MEM;
57         req.iomem_base          = vaddr;
58         req.iomem_reg_shift     = 2;
59         req.iomap_base          = baddr;
60
61         return register_serial(&req);
62 }
63
64 static int __devinit
65 serial_card_probe(struct expansion_card *ec, const struct ecard_id *id)
66 {
67         struct serial_card_info *info;
68         struct serial_card_type *type = id->data;
69         unsigned long bus_addr;
70         unsigned char *virt_addr;
71         unsigned int port;
72
73         info = kmalloc(sizeof(struct serial_card_info), GFP_KERNEL);
74         if (!info)
75                 return -ENOMEM;
76
77         memset(info, 0, sizeof(struct serial_card_info));
78         info->num_ports = type->num_ports;
79
80         ecard_set_drvdata(ec, info);
81
82         bus_addr = ec->resource[type->type].start;
83         virt_addr = ioremap(bus_addr, ec->resource[type->type].end - bus_addr + 1);
84         if (!virt_addr) {
85                 kfree(info);
86                 return -ENOMEM;
87         }
88
89         for (port = 0; port < info->num_ports; port ++) {
90                 unsigned long baddr = bus_addr + type->offset[port];
91                 unsigned char *vaddr = virt_addr + type->offset[port];
92
93                 info->ports[port] = serial_register_onedev(baddr, vaddr,
94                                                 ec->irq, type->baud_base);
95         }
96
97         return 0;
98 }
99
100 static void __devexit serial_card_remove(struct expansion_card *ec)
101 {
102         struct serial_card_info *info = ecard_get_drvdata(ec);
103         int i;
104
105         ecard_set_drvdata(ec, NULL);
106
107         for (i = 0; i < info->num_ports; i++)
108                 if (info->ports[i] > 0)
109                         unregister_serial(info->ports[i]);
110
111         kfree(info);
112 }
113
114 static struct ecard_driver serial_card_driver = {
115         .probe          = serial_card_probe,
116         .remove         = __devexit_p(serial_card_remove),
117         .id_table       = serial_cids,
118 };
119
120 static int __init serial_card_init(void)
121 {
122         return ecard_register_driver(&serial_card_driver);
123 }
124
125 static void __exit serial_card_exit(void)
126 {
127         ecard_remove_driver(&serial_card_driver);
128 }
129
130 EXPORT_NO_SYMBOLS;
131
132 MODULE_AUTHOR("Russell King");
133 MODULE_LICENSE("GPL");
134
135 module_init(serial_card_init);
136 module_exit(serial_card_exit);