ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / serial / 8250_acorn.c
1 /*
2  *  linux/drivers/serial/acorn.c
3  *
4  *  Copyright (C) 1996-2003 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 #include <linux/module.h>
11 #include <linux/types.h>
12 #include <linux/tty.h>
13 #include <linux/serial_core.h>
14 #include <linux/serial.h>
15 #include <linux/errno.h>
16 #include <linux/ioport.h>
17 #include <linux/slab.h>
18 #include <linux/device.h>
19 #include <linux/init.h>
20
21 #include <asm/io.h>
22 #include <asm/ecard.h>
23 #include <asm/string.h>
24
25 #define MAX_PORTS       3
26
27 struct serial_card_type {
28         unsigned int    num_ports;
29         unsigned int    baud_base;
30         unsigned int    type;
31         unsigned int    offset[MAX_PORTS];
32 };
33
34 struct serial_card_info {
35         unsigned int    num_ports;
36         int             ports[MAX_PORTS];
37 };
38
39 static inline int
40 serial_register_onedev(unsigned long baddr, void *vaddr, int irq, unsigned int baud_base)
41 {
42         struct serial_struct req;
43
44         memset(&req, 0, sizeof(req));
45         req.irq                 = irq;
46         req.flags               = UPF_AUTOPROBE | UPF_RESOURCES |
47                                   UPF_SHARE_IRQ;
48         req.baud_base           = baud_base;
49         req.io_type             = UPIO_MEM;
50         req.iomem_base          = vaddr;
51         req.iomem_reg_shift     = 2;
52         req.iomap_base          = baddr;
53
54         return register_serial(&req);
55 }
56
57 static int __devinit
58 serial_card_probe(struct expansion_card *ec, const struct ecard_id *id)
59 {
60         struct serial_card_info *info;
61         struct serial_card_type *type = id->data;
62         unsigned long bus_addr;
63         unsigned char *virt_addr;
64         unsigned int port;
65
66         info = kmalloc(sizeof(struct serial_card_info), GFP_KERNEL);
67         if (!info)
68                 return -ENOMEM;
69
70         memset(info, 0, sizeof(struct serial_card_info));
71         info->num_ports = type->num_ports;
72
73         ecard_set_drvdata(ec, info);
74
75         bus_addr = ec->resource[type->type].start;
76         virt_addr = ioremap(bus_addr, ec->resource[type->type].end - bus_addr + 1);
77         if (!virt_addr) {
78                 kfree(info);
79                 return -ENOMEM;
80         }
81
82         for (port = 0; port < info->num_ports; port ++) {
83                 unsigned long baddr = bus_addr + type->offset[port];
84                 unsigned char *vaddr = virt_addr + type->offset[port];
85
86                 info->ports[port] = serial_register_onedev(baddr, vaddr,
87                                                 ec->irq, type->baud_base);
88         }
89
90         return 0;
91 }
92
93 static void __devexit serial_card_remove(struct expansion_card *ec)
94 {
95         struct serial_card_info *info = ecard_get_drvdata(ec);
96         int i;
97
98         ecard_set_drvdata(ec, NULL);
99
100         for (i = 0; i < info->num_ports; i++)
101                 if (info->ports[i] > 0)
102                         unregister_serial(info->ports[i]);
103
104         kfree(info);
105 }
106
107 static struct serial_card_type atomwide_type = {
108         .num_ports      = 3,
109         .baud_base      = 7372800 / 16,
110         .type           = ECARD_RES_IOCSLOW,
111         .offset         = { 0x2800, 0x2400, 0x2000 },
112 };
113
114 static struct serial_card_type serport_type = {
115         .num_ports      = 2,
116         .baud_base      = 3686400 / 16,
117         .type           = ECARD_RES_IOCSLOW,
118         .offset         = { 0x2000, 0x2020 },
119 };
120
121 static const struct ecard_id serial_cids[] = {
122         { MANU_ATOMWIDE,        PROD_ATOMWIDE_3PSERIAL, &atomwide_type  },
123         { MANU_SERPORT,         PROD_SERPORT_DSPORT,    &serport_type   },
124         { 0xffff, 0xffff }
125 };
126
127 static struct ecard_driver serial_card_driver = {
128         .probe          = serial_card_probe,
129         .remove         = __devexit_p(serial_card_remove),
130         .id_table       = serial_cids,
131         .drv = {
132                 .name           = "8250_acorn",
133         },
134 };
135
136 static int __init serial_card_init(void)
137 {
138         return ecard_register_driver(&serial_card_driver);
139 }
140
141 static void __exit serial_card_exit(void)
142 {
143         ecard_remove_driver(&serial_card_driver);
144 }
145
146 MODULE_AUTHOR("Russell King");
147 MODULE_DESCRIPTION("Acorn 8250-compatible serial port expansion card driver");
148 MODULE_LICENSE("GPL");
149
150 module_init(serial_card_init);
151 module_exit(serial_card_exit);