diff options
Diffstat (limited to 'drivers/serial/serial_cs.c')
-rw-r--r-- | drivers/serial/serial_cs.c | 747 |
1 files changed, 747 insertions, 0 deletions
diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c new file mode 100644 index 000000000000..9034f9ad37c7 --- /dev/null +++ b/drivers/serial/serial_cs.c | |||
@@ -0,0 +1,747 @@ | |||
1 | /*====================================================================== | ||
2 | |||
3 | A driver for PCMCIA serial devices | ||
4 | |||
5 | serial_cs.c 1.134 2002/05/04 05:48:53 | ||
6 | |||
7 | The contents of this file are subject to the Mozilla Public | ||
8 | License Version 1.1 (the "License"); you may not use this file | ||
9 | except in compliance with the License. You may obtain a copy of | ||
10 | the License at http://www.mozilla.org/MPL/ | ||
11 | |||
12 | Software distributed under the License is distributed on an "AS | ||
13 | IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | ||
14 | implied. See the License for the specific language governing | ||
15 | rights and limitations under the License. | ||
16 | |||
17 | The initial developer of the original code is David A. Hinds | ||
18 | <dahinds@users.sourceforge.net>. Portions created by David A. Hinds | ||
19 | are Copyright (C) 1999 David A. Hinds. All Rights Reserved. | ||
20 | |||
21 | Alternatively, the contents of this file may be used under the | ||
22 | terms of the GNU General Public License version 2 (the "GPL"), in which | ||
23 | case the provisions of the GPL are applicable instead of the | ||
24 | above. If you wish to allow the use of your version of this file | ||
25 | only under the terms of the GPL and not to allow others to use | ||
26 | your version of this file under the MPL, indicate your decision | ||
27 | by deleting the provisions above and replace them with the notice | ||
28 | and other provisions required by the GPL. If you do not delete | ||
29 | the provisions above, a recipient may use your version of this | ||
30 | file under either the MPL or the GPL. | ||
31 | |||
32 | ======================================================================*/ | ||
33 | |||
34 | #include <linux/module.h> | ||
35 | #include <linux/moduleparam.h> | ||
36 | #include <linux/kernel.h> | ||
37 | #include <linux/init.h> | ||
38 | #include <linux/sched.h> | ||
39 | #include <linux/ptrace.h> | ||
40 | #include <linux/slab.h> | ||
41 | #include <linux/string.h> | ||
42 | #include <linux/timer.h> | ||
43 | #include <linux/serial_core.h> | ||
44 | #include <linux/major.h> | ||
45 | #include <asm/io.h> | ||
46 | #include <asm/system.h> | ||
47 | |||
48 | #include <pcmcia/version.h> | ||
49 | #include <pcmcia/cs_types.h> | ||
50 | #include <pcmcia/cs.h> | ||
51 | #include <pcmcia/cistpl.h> | ||
52 | #include <pcmcia/ciscode.h> | ||
53 | #include <pcmcia/ds.h> | ||
54 | #include <pcmcia/cisreg.h> | ||
55 | |||
56 | #include "8250.h" | ||
57 | |||
58 | #ifdef PCMCIA_DEBUG | ||
59 | static int pc_debug = PCMCIA_DEBUG; | ||
60 | module_param(pc_debug, int, 0644); | ||
61 | #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) | ||
62 | static char *version = "serial_cs.c 1.134 2002/05/04 05:48:53 (David Hinds)"; | ||
63 | #else | ||
64 | #define DEBUG(n, args...) | ||
65 | #endif | ||
66 | |||
67 | /*====================================================================*/ | ||
68 | |||
69 | /* Parameters that can be set with 'insmod' */ | ||
70 | |||
71 | /* Enable the speaker? */ | ||
72 | static int do_sound = 1; | ||
73 | /* Skip strict UART tests? */ | ||
74 | static int buggy_uart; | ||
75 | |||
76 | module_param(do_sound, int, 0444); | ||
77 | module_param(buggy_uart, int, 0444); | ||
78 | |||
79 | /*====================================================================*/ | ||
80 | |||
81 | /* Table of multi-port card ID's */ | ||
82 | |||
83 | struct multi_id { | ||
84 | u_short manfid; | ||
85 | u_short prodid; | ||
86 | int multi; /* 1 = multifunction, > 1 = # ports */ | ||
87 | }; | ||
88 | |||
89 | static struct multi_id multi_id[] = { | ||
90 | { MANFID_OMEGA, PRODID_OMEGA_QSP_100, 4 }, | ||
91 | { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232, 2 }, | ||
92 | { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232_D1, 2 }, | ||
93 | { MANFID_QUATECH, PRODID_QUATECH_QUAD_RS232, 4 }, | ||
94 | { MANFID_SOCKET, PRODID_SOCKET_DUAL_RS232, 2 }, | ||
95 | { MANFID_INTEL, PRODID_INTEL_DUAL_RS232, 2 }, | ||
96 | { MANFID_NATINST, PRODID_NATINST_QUAD_RS232, 4 } | ||
97 | }; | ||
98 | #define MULTI_COUNT (sizeof(multi_id)/sizeof(struct multi_id)) | ||
99 | |||
100 | struct serial_info { | ||
101 | dev_link_t link; | ||
102 | int ndev; | ||
103 | int multi; | ||
104 | int slave; | ||
105 | int manfid; | ||
106 | dev_node_t node[4]; | ||
107 | int line[4]; | ||
108 | }; | ||
109 | |||
110 | static void serial_config(dev_link_t * link); | ||
111 | static int serial_event(event_t event, int priority, | ||
112 | event_callback_args_t * args); | ||
113 | |||
114 | static dev_info_t dev_info = "serial_cs"; | ||
115 | |||
116 | static dev_link_t *serial_attach(void); | ||
117 | static void serial_detach(dev_link_t *); | ||
118 | |||
119 | static dev_link_t *dev_list = NULL; | ||
120 | |||
121 | /*====================================================================== | ||
122 | |||
123 | After a card is removed, serial_remove() will unregister | ||
124 | the serial device(s), and release the PCMCIA configuration. | ||
125 | |||
126 | ======================================================================*/ | ||
127 | |||
128 | static void serial_remove(dev_link_t *link) | ||
129 | { | ||
130 | struct serial_info *info = link->priv; | ||
131 | int i; | ||
132 | |||
133 | link->state &= ~DEV_PRESENT; | ||
134 | |||
135 | DEBUG(0, "serial_release(0x%p)\n", link); | ||
136 | |||
137 | /* | ||
138 | * Recheck to see if the device is still configured. | ||
139 | */ | ||
140 | if (info->link.state & DEV_CONFIG) { | ||
141 | for (i = 0; i < info->ndev; i++) | ||
142 | serial8250_unregister_port(info->line[i]); | ||
143 | |||
144 | info->link.dev = NULL; | ||
145 | |||
146 | if (!info->slave) { | ||
147 | pcmcia_release_configuration(info->link.handle); | ||
148 | pcmcia_release_io(info->link.handle, &info->link.io); | ||
149 | pcmcia_release_irq(info->link.handle, &info->link.irq); | ||
150 | } | ||
151 | |||
152 | info->link.state &= ~DEV_CONFIG; | ||
153 | } | ||
154 | } | ||
155 | |||
156 | static void serial_suspend(dev_link_t *link) | ||
157 | { | ||
158 | link->state |= DEV_SUSPEND; | ||
159 | |||
160 | if (link->state & DEV_CONFIG) { | ||
161 | struct serial_info *info = link->priv; | ||
162 | int i; | ||
163 | |||
164 | for (i = 0; i < info->ndev; i++) | ||
165 | serial8250_suspend_port(info->line[i]); | ||
166 | |||
167 | if (!info->slave) | ||
168 | pcmcia_release_configuration(link->handle); | ||
169 | } | ||
170 | } | ||
171 | |||
172 | static void serial_resume(dev_link_t *link) | ||
173 | { | ||
174 | link->state &= ~DEV_SUSPEND; | ||
175 | |||
176 | if (DEV_OK(link)) { | ||
177 | struct serial_info *info = link->priv; | ||
178 | int i; | ||
179 | |||
180 | if (!info->slave) | ||
181 | pcmcia_request_configuration(link->handle, &link->conf); | ||
182 | |||
183 | for (i = 0; i < info->ndev; i++) | ||
184 | serial8250_resume_port(info->line[i]); | ||
185 | } | ||
186 | } | ||
187 | |||
188 | /*====================================================================== | ||
189 | |||
190 | serial_attach() creates an "instance" of the driver, allocating | ||
191 | local data structures for one device. The device is registered | ||
192 | with Card Services. | ||
193 | |||
194 | ======================================================================*/ | ||
195 | |||
196 | static dev_link_t *serial_attach(void) | ||
197 | { | ||
198 | struct serial_info *info; | ||
199 | client_reg_t client_reg; | ||
200 | dev_link_t *link; | ||
201 | int ret; | ||
202 | |||
203 | DEBUG(0, "serial_attach()\n"); | ||
204 | |||
205 | /* Create new serial device */ | ||
206 | info = kmalloc(sizeof (*info), GFP_KERNEL); | ||
207 | if (!info) | ||
208 | return NULL; | ||
209 | memset(info, 0, sizeof (*info)); | ||
210 | link = &info->link; | ||
211 | link->priv = info; | ||
212 | |||
213 | link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; | ||
214 | link->io.NumPorts1 = 8; | ||
215 | link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; | ||
216 | link->irq.IRQInfo1 = IRQ_LEVEL_ID; | ||
217 | link->conf.Attributes = CONF_ENABLE_IRQ; | ||
218 | if (do_sound) { | ||
219 | link->conf.Attributes |= CONF_ENABLE_SPKR; | ||
220 | link->conf.Status = CCSR_AUDIO_ENA; | ||
221 | } | ||
222 | link->conf.IntType = INT_MEMORY_AND_IO; | ||
223 | |||
224 | /* Register with Card Services */ | ||
225 | link->next = dev_list; | ||
226 | dev_list = link; | ||
227 | client_reg.dev_info = &dev_info; | ||
228 | client_reg.EventMask = | ||
229 | CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | | ||
230 | CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | | ||
231 | CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; | ||
232 | client_reg.event_handler = &serial_event; | ||
233 | client_reg.Version = 0x0210; | ||
234 | client_reg.event_callback_args.client_data = link; | ||
235 | ret = pcmcia_register_client(&link->handle, &client_reg); | ||
236 | if (ret != CS_SUCCESS) { | ||
237 | cs_error(link->handle, RegisterClient, ret); | ||
238 | serial_detach(link); | ||
239 | return NULL; | ||
240 | } | ||
241 | |||
242 | return link; | ||
243 | } | ||
244 | |||
245 | /*====================================================================== | ||
246 | |||
247 | This deletes a driver "instance". The device is de-registered | ||
248 | with Card Services. If it has been released, all local data | ||
249 | structures are freed. Otherwise, the structures will be freed | ||
250 | when the device is released. | ||
251 | |||
252 | ======================================================================*/ | ||
253 | |||
254 | static void serial_detach(dev_link_t * link) | ||
255 | { | ||
256 | struct serial_info *info = link->priv; | ||
257 | dev_link_t **linkp; | ||
258 | int ret; | ||
259 | |||
260 | DEBUG(0, "serial_detach(0x%p)\n", link); | ||
261 | |||
262 | /* Locate device structure */ | ||
263 | for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) | ||
264 | if (*linkp == link) | ||
265 | break; | ||
266 | if (*linkp == NULL) | ||
267 | return; | ||
268 | |||
269 | /* | ||
270 | * Ensure any outstanding scheduled tasks are completed. | ||
271 | */ | ||
272 | flush_scheduled_work(); | ||
273 | |||
274 | /* | ||
275 | * Ensure that the ports have been released. | ||
276 | */ | ||
277 | serial_remove(link); | ||
278 | |||
279 | if (link->handle) { | ||
280 | ret = pcmcia_deregister_client(link->handle); | ||
281 | if (ret != CS_SUCCESS) | ||
282 | cs_error(link->handle, DeregisterClient, ret); | ||
283 | } | ||
284 | |||
285 | /* Unlink device structure, free bits */ | ||
286 | *linkp = link->next; | ||
287 | kfree(info); | ||
288 | } | ||
289 | |||
290 | /*====================================================================*/ | ||
291 | |||
292 | static int setup_serial(client_handle_t handle, struct serial_info * info, | ||
293 | kio_addr_t iobase, int irq) | ||
294 | { | ||
295 | struct uart_port port; | ||
296 | int line; | ||
297 | |||
298 | memset(&port, 0, sizeof (struct uart_port)); | ||
299 | port.iobase = iobase; | ||
300 | port.irq = irq; | ||
301 | port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ; | ||
302 | port.uartclk = 1843200; | ||
303 | port.dev = &handle_to_dev(handle); | ||
304 | if (buggy_uart) | ||
305 | port.flags |= UPF_BUGGY_UART; | ||
306 | line = serial8250_register_port(&port); | ||
307 | if (line < 0) { | ||
308 | printk(KERN_NOTICE "serial_cs: serial8250_register_port() at " | ||
309 | "0x%04lx, irq %d failed\n", (u_long)iobase, irq); | ||
310 | return -EINVAL; | ||
311 | } | ||
312 | |||
313 | info->line[info->ndev] = line; | ||
314 | sprintf(info->node[info->ndev].dev_name, "ttyS%d", line); | ||
315 | info->node[info->ndev].major = TTY_MAJOR; | ||
316 | info->node[info->ndev].minor = 0x40 + line; | ||
317 | if (info->ndev > 0) | ||
318 | info->node[info->ndev - 1].next = &info->node[info->ndev]; | ||
319 | info->ndev++; | ||
320 | |||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | /*====================================================================*/ | ||
325 | |||
326 | static int | ||
327 | first_tuple(client_handle_t handle, tuple_t * tuple, cisparse_t * parse) | ||
328 | { | ||
329 | int i; | ||
330 | i = pcmcia_get_first_tuple(handle, tuple); | ||
331 | if (i != CS_SUCCESS) | ||
332 | return CS_NO_MORE_ITEMS; | ||
333 | i = pcmcia_get_tuple_data(handle, tuple); | ||
334 | if (i != CS_SUCCESS) | ||
335 | return i; | ||
336 | return pcmcia_parse_tuple(handle, tuple, parse); | ||
337 | } | ||
338 | |||
339 | static int | ||
340 | next_tuple(client_handle_t handle, tuple_t * tuple, cisparse_t * parse) | ||
341 | { | ||
342 | int i; | ||
343 | i = pcmcia_get_next_tuple(handle, tuple); | ||
344 | if (i != CS_SUCCESS) | ||
345 | return CS_NO_MORE_ITEMS; | ||
346 | i = pcmcia_get_tuple_data(handle, tuple); | ||
347 | if (i != CS_SUCCESS) | ||
348 | return i; | ||
349 | return pcmcia_parse_tuple(handle, tuple, parse); | ||
350 | } | ||
351 | |||
352 | /*====================================================================*/ | ||
353 | |||
354 | static int simple_config(dev_link_t *link) | ||
355 | { | ||
356 | static kio_addr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; | ||
357 | static int size_table[2] = { 8, 16 }; | ||
358 | client_handle_t handle = link->handle; | ||
359 | struct serial_info *info = link->priv; | ||
360 | tuple_t tuple; | ||
361 | u_char buf[256]; | ||
362 | cisparse_t parse; | ||
363 | cistpl_cftable_entry_t *cf = &parse.cftable_entry; | ||
364 | config_info_t config; | ||
365 | int i, j, try; | ||
366 | int s; | ||
367 | |||
368 | /* If the card is already configured, look up the port and irq */ | ||
369 | i = pcmcia_get_configuration_info(handle, &config); | ||
370 | if ((i == CS_SUCCESS) && (config.Attributes & CONF_VALID_CLIENT)) { | ||
371 | kio_addr_t port = 0; | ||
372 | if ((config.BasePort2 != 0) && (config.NumPorts2 == 8)) { | ||
373 | port = config.BasePort2; | ||
374 | info->slave = 1; | ||
375 | } else if ((info->manfid == MANFID_OSITECH) && | ||
376 | (config.NumPorts1 == 0x40)) { | ||
377 | port = config.BasePort1 + 0x28; | ||
378 | info->slave = 1; | ||
379 | } | ||
380 | if (info->slave) | ||
381 | return setup_serial(handle, info, port, config.AssignedIRQ); | ||
382 | } | ||
383 | link->conf.Vcc = config.Vcc; | ||
384 | |||
385 | /* First pass: look for a config entry that looks normal. */ | ||
386 | tuple.TupleData = (cisdata_t *) buf; | ||
387 | tuple.TupleOffset = 0; | ||
388 | tuple.TupleDataMax = 255; | ||
389 | tuple.Attributes = 0; | ||
390 | tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; | ||
391 | /* Two tries: without IO aliases, then with aliases */ | ||
392 | for (s = 0; s < 2; s++) { | ||
393 | for (try = 0; try < 2; try++) { | ||
394 | i = first_tuple(handle, &tuple, &parse); | ||
395 | while (i != CS_NO_MORE_ITEMS) { | ||
396 | if (i != CS_SUCCESS) | ||
397 | goto next_entry; | ||
398 | if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM)) | ||
399 | link->conf.Vpp1 = link->conf.Vpp2 = | ||
400 | cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; | ||
401 | if ((cf->io.nwin > 0) && (cf->io.win[0].len == size_table[s]) && | ||
402 | (cf->io.win[0].base != 0)) { | ||
403 | link->conf.ConfigIndex = cf->index; | ||
404 | link->io.BasePort1 = cf->io.win[0].base; | ||
405 | link->io.IOAddrLines = (try == 0) ? | ||
406 | 16 : cf->io.flags & CISTPL_IO_LINES_MASK; | ||
407 | i = pcmcia_request_io(link->handle, &link->io); | ||
408 | if (i == CS_SUCCESS) | ||
409 | goto found_port; | ||
410 | } | ||
411 | next_entry: | ||
412 | i = next_tuple(handle, &tuple, &parse); | ||
413 | } | ||
414 | } | ||
415 | } | ||
416 | /* Second pass: try to find an entry that isn't picky about | ||
417 | its base address, then try to grab any standard serial port | ||
418 | address, and finally try to get any free port. */ | ||
419 | i = first_tuple(handle, &tuple, &parse); | ||
420 | while (i != CS_NO_MORE_ITEMS) { | ||
421 | if ((i == CS_SUCCESS) && (cf->io.nwin > 0) && | ||
422 | ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) { | ||
423 | link->conf.ConfigIndex = cf->index; | ||
424 | for (j = 0; j < 5; j++) { | ||
425 | link->io.BasePort1 = base[j]; | ||
426 | link->io.IOAddrLines = base[j] ? 16 : 3; | ||
427 | i = pcmcia_request_io(link->handle, &link->io); | ||
428 | if (i == CS_SUCCESS) | ||
429 | goto found_port; | ||
430 | } | ||
431 | } | ||
432 | i = next_tuple(handle, &tuple, &parse); | ||
433 | } | ||
434 | |||
435 | found_port: | ||
436 | if (i != CS_SUCCESS) { | ||
437 | printk(KERN_NOTICE | ||
438 | "serial_cs: no usable port range found, giving up\n"); | ||
439 | cs_error(link->handle, RequestIO, i); | ||
440 | return -1; | ||
441 | } | ||
442 | |||
443 | i = pcmcia_request_irq(link->handle, &link->irq); | ||
444 | if (i != CS_SUCCESS) { | ||
445 | cs_error(link->handle, RequestIRQ, i); | ||
446 | link->irq.AssignedIRQ = 0; | ||
447 | } | ||
448 | if (info->multi && (info->manfid == MANFID_3COM)) | ||
449 | link->conf.ConfigIndex &= ~(0x08); | ||
450 | i = pcmcia_request_configuration(link->handle, &link->conf); | ||
451 | if (i != CS_SUCCESS) { | ||
452 | cs_error(link->handle, RequestConfiguration, i); | ||
453 | return -1; | ||
454 | } | ||
455 | |||
456 | return setup_serial(handle, info, link->io.BasePort1, link->irq.AssignedIRQ); | ||
457 | } | ||
458 | |||
459 | static int multi_config(dev_link_t * link) | ||
460 | { | ||
461 | client_handle_t handle = link->handle; | ||
462 | struct serial_info *info = link->priv; | ||
463 | tuple_t tuple; | ||
464 | u_char buf[256]; | ||
465 | cisparse_t parse; | ||
466 | cistpl_cftable_entry_t *cf = &parse.cftable_entry; | ||
467 | config_info_t config; | ||
468 | int i, base2 = 0; | ||
469 | |||
470 | i = pcmcia_get_configuration_info(handle, &config); | ||
471 | if (i != CS_SUCCESS) { | ||
472 | cs_error(handle, GetConfigurationInfo, i); | ||
473 | return -1; | ||
474 | } | ||
475 | link->conf.Vcc = config.Vcc; | ||
476 | |||
477 | tuple.TupleData = (cisdata_t *) buf; | ||
478 | tuple.TupleOffset = 0; | ||
479 | tuple.TupleDataMax = 255; | ||
480 | tuple.Attributes = 0; | ||
481 | tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; | ||
482 | |||
483 | /* First, look for a generic full-sized window */ | ||
484 | link->io.NumPorts1 = info->multi * 8; | ||
485 | i = first_tuple(handle, &tuple, &parse); | ||
486 | while (i != CS_NO_MORE_ITEMS) { | ||
487 | /* The quad port cards have bad CIS's, so just look for a | ||
488 | window larger than 8 ports and assume it will be right */ | ||
489 | if ((i == CS_SUCCESS) && (cf->io.nwin == 1) && | ||
490 | (cf->io.win[0].len > 8)) { | ||
491 | link->conf.ConfigIndex = cf->index; | ||
492 | link->io.BasePort1 = cf->io.win[0].base; | ||
493 | link->io.IOAddrLines = | ||
494 | cf->io.flags & CISTPL_IO_LINES_MASK; | ||
495 | i = pcmcia_request_io(link->handle, &link->io); | ||
496 | base2 = link->io.BasePort1 + 8; | ||
497 | if (i == CS_SUCCESS) | ||
498 | break; | ||
499 | } | ||
500 | i = next_tuple(handle, &tuple, &parse); | ||
501 | } | ||
502 | |||
503 | /* If that didn't work, look for two windows */ | ||
504 | if (i != CS_SUCCESS) { | ||
505 | link->io.NumPorts1 = link->io.NumPorts2 = 8; | ||
506 | info->multi = 2; | ||
507 | i = first_tuple(handle, &tuple, &parse); | ||
508 | while (i != CS_NO_MORE_ITEMS) { | ||
509 | if ((i == CS_SUCCESS) && (cf->io.nwin == 2)) { | ||
510 | link->conf.ConfigIndex = cf->index; | ||
511 | link->io.BasePort1 = cf->io.win[0].base; | ||
512 | link->io.BasePort2 = cf->io.win[1].base; | ||
513 | link->io.IOAddrLines = | ||
514 | cf->io.flags & CISTPL_IO_LINES_MASK; | ||
515 | i = pcmcia_request_io(link->handle, &link->io); | ||
516 | base2 = link->io.BasePort2; | ||
517 | if (i == CS_SUCCESS) | ||
518 | break; | ||
519 | } | ||
520 | i = next_tuple(handle, &tuple, &parse); | ||
521 | } | ||
522 | } | ||
523 | |||
524 | if (i != CS_SUCCESS) { | ||
525 | cs_error(link->handle, RequestIO, i); | ||
526 | return -1; | ||
527 | } | ||
528 | |||
529 | i = pcmcia_request_irq(link->handle, &link->irq); | ||
530 | if (i != CS_SUCCESS) { | ||
531 | printk(KERN_NOTICE | ||
532 | "serial_cs: no usable port range found, giving up\n"); | ||
533 | cs_error(link->handle, RequestIRQ, i); | ||
534 | link->irq.AssignedIRQ = 0; | ||
535 | } | ||
536 | /* Socket Dual IO: this enables irq's for second port */ | ||
537 | if (info->multi && (info->manfid == MANFID_SOCKET)) { | ||
538 | link->conf.Present |= PRESENT_EXT_STATUS; | ||
539 | link->conf.ExtStatus = ESR_REQ_ATTN_ENA; | ||
540 | } | ||
541 | i = pcmcia_request_configuration(link->handle, &link->conf); | ||
542 | if (i != CS_SUCCESS) { | ||
543 | cs_error(link->handle, RequestConfiguration, i); | ||
544 | return -1; | ||
545 | } | ||
546 | |||
547 | /* The Oxford Semiconductor OXCF950 cards are in fact single-port: | ||
548 | 8 registers are for the UART, the others are extra registers */ | ||
549 | if (info->manfid == MANFID_OXSEMI) { | ||
550 | if (cf->index == 1 || cf->index == 3) { | ||
551 | setup_serial(handle, info, base2, link->irq.AssignedIRQ); | ||
552 | outb(12, link->io.BasePort1 + 1); | ||
553 | } else { | ||
554 | setup_serial(handle, info, link->io.BasePort1, link->irq.AssignedIRQ); | ||
555 | outb(12, base2 + 1); | ||
556 | } | ||
557 | return 0; | ||
558 | } | ||
559 | |||
560 | setup_serial(handle, info, link->io.BasePort1, link->irq.AssignedIRQ); | ||
561 | /* The Nokia cards are not really multiport cards */ | ||
562 | if (info->manfid == MANFID_NOKIA) | ||
563 | return 0; | ||
564 | for (i = 0; i < info->multi - 1; i++) | ||
565 | setup_serial(handle, info, base2 + (8 * i), link->irq.AssignedIRQ); | ||
566 | |||
567 | return 0; | ||
568 | } | ||
569 | |||
570 | /*====================================================================== | ||
571 | |||
572 | serial_config() is scheduled to run after a CARD_INSERTION event | ||
573 | is received, to configure the PCMCIA socket, and to make the | ||
574 | serial device available to the system. | ||
575 | |||
576 | ======================================================================*/ | ||
577 | |||
578 | void serial_config(dev_link_t * link) | ||
579 | { | ||
580 | client_handle_t handle = link->handle; | ||
581 | struct serial_info *info = link->priv; | ||
582 | tuple_t tuple; | ||
583 | u_short buf[128]; | ||
584 | cisparse_t parse; | ||
585 | cistpl_cftable_entry_t *cf = &parse.cftable_entry; | ||
586 | int i, last_ret, last_fn; | ||
587 | |||
588 | DEBUG(0, "serial_config(0x%p)\n", link); | ||
589 | |||
590 | tuple.TupleData = (cisdata_t *) buf; | ||
591 | tuple.TupleOffset = 0; | ||
592 | tuple.TupleDataMax = 255; | ||
593 | tuple.Attributes = 0; | ||
594 | /* Get configuration register information */ | ||
595 | tuple.DesiredTuple = CISTPL_CONFIG; | ||
596 | last_ret = first_tuple(handle, &tuple, &parse); | ||
597 | if (last_ret != CS_SUCCESS) { | ||
598 | last_fn = ParseTuple; | ||
599 | goto cs_failed; | ||
600 | } | ||
601 | link->conf.ConfigBase = parse.config.base; | ||
602 | link->conf.Present = parse.config.rmask[0]; | ||
603 | |||
604 | /* Configure card */ | ||
605 | link->state |= DEV_CONFIG; | ||
606 | |||
607 | /* Is this a compliant multifunction card? */ | ||
608 | tuple.DesiredTuple = CISTPL_LONGLINK_MFC; | ||
609 | tuple.Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK; | ||
610 | info->multi = (first_tuple(handle, &tuple, &parse) == CS_SUCCESS); | ||
611 | |||
612 | /* Is this a multiport card? */ | ||
613 | tuple.DesiredTuple = CISTPL_MANFID; | ||
614 | if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) { | ||
615 | info->manfid = le16_to_cpu(buf[0]); | ||
616 | for (i = 0; i < MULTI_COUNT; i++) | ||
617 | if ((info->manfid == multi_id[i].manfid) && | ||
618 | (le16_to_cpu(buf[1]) == multi_id[i].prodid)) | ||
619 | break; | ||
620 | if (i < MULTI_COUNT) | ||
621 | info->multi = multi_id[i].multi; | ||
622 | } | ||
623 | |||
624 | /* Another check for dual-serial cards: look for either serial or | ||
625 | multifunction cards that ask for appropriate IO port ranges */ | ||
626 | tuple.DesiredTuple = CISTPL_FUNCID; | ||
627 | if ((info->multi == 0) && | ||
628 | ((first_tuple(handle, &tuple, &parse) != CS_SUCCESS) || | ||
629 | (parse.funcid.func == CISTPL_FUNCID_MULTI) || | ||
630 | (parse.funcid.func == CISTPL_FUNCID_SERIAL))) { | ||
631 | tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; | ||
632 | if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) { | ||
633 | if ((cf->io.nwin == 1) && (cf->io.win[0].len % 8 == 0)) | ||
634 | info->multi = cf->io.win[0].len >> 3; | ||
635 | if ((cf->io.nwin == 2) && (cf->io.win[0].len == 8) && | ||
636 | (cf->io.win[1].len == 8)) | ||
637 | info->multi = 2; | ||
638 | } | ||
639 | } | ||
640 | |||
641 | if (info->multi > 1) | ||
642 | multi_config(link); | ||
643 | else | ||
644 | simple_config(link); | ||
645 | |||
646 | if (info->ndev == 0) | ||
647 | goto failed; | ||
648 | |||
649 | if (info->manfid == MANFID_IBM) { | ||
650 | conf_reg_t reg = { 0, CS_READ, 0x800, 0 }; | ||
651 | last_ret = pcmcia_access_configuration_register(link->handle, ®); | ||
652 | if (last_ret) { | ||
653 | last_fn = AccessConfigurationRegister; | ||
654 | goto cs_failed; | ||
655 | } | ||
656 | reg.Action = CS_WRITE; | ||
657 | reg.Value = reg.Value | 1; | ||
658 | last_ret = pcmcia_access_configuration_register(link->handle, ®); | ||
659 | if (last_ret) { | ||
660 | last_fn = AccessConfigurationRegister; | ||
661 | goto cs_failed; | ||
662 | } | ||
663 | } | ||
664 | |||
665 | link->dev = &info->node[0]; | ||
666 | link->state &= ~DEV_CONFIG_PENDING; | ||
667 | return; | ||
668 | |||
669 | cs_failed: | ||
670 | cs_error(link->handle, last_fn, last_ret); | ||
671 | failed: | ||
672 | serial_remove(link); | ||
673 | link->state &= ~DEV_CONFIG_PENDING; | ||
674 | } | ||
675 | |||
676 | /*====================================================================== | ||
677 | |||
678 | The card status event handler. Mostly, this schedules other | ||
679 | stuff to run after an event is received. A CARD_REMOVAL event | ||
680 | also sets some flags to discourage the serial drivers from | ||
681 | talking to the ports. | ||
682 | |||
683 | ======================================================================*/ | ||
684 | |||
685 | static int | ||
686 | serial_event(event_t event, int priority, event_callback_args_t * args) | ||
687 | { | ||
688 | dev_link_t *link = args->client_data; | ||
689 | struct serial_info *info = link->priv; | ||
690 | |||
691 | DEBUG(1, "serial_event(0x%06x)\n", event); | ||
692 | |||
693 | switch (event) { | ||
694 | case CS_EVENT_CARD_REMOVAL: | ||
695 | serial_remove(link); | ||
696 | break; | ||
697 | |||
698 | case CS_EVENT_CARD_INSERTION: | ||
699 | link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; | ||
700 | serial_config(link); | ||
701 | break; | ||
702 | |||
703 | case CS_EVENT_PM_SUSPEND: | ||
704 | serial_suspend(link); | ||
705 | break; | ||
706 | |||
707 | case CS_EVENT_RESET_PHYSICAL: | ||
708 | if ((link->state & DEV_CONFIG) && !info->slave) | ||
709 | pcmcia_release_configuration(link->handle); | ||
710 | break; | ||
711 | |||
712 | case CS_EVENT_PM_RESUME: | ||
713 | serial_resume(link); | ||
714 | break; | ||
715 | |||
716 | case CS_EVENT_CARD_RESET: | ||
717 | if (DEV_OK(link) && !info->slave) | ||
718 | pcmcia_request_configuration(link->handle, &link->conf); | ||
719 | break; | ||
720 | } | ||
721 | return 0; | ||
722 | } | ||
723 | |||
724 | static struct pcmcia_driver serial_cs_driver = { | ||
725 | .owner = THIS_MODULE, | ||
726 | .drv = { | ||
727 | .name = "serial_cs", | ||
728 | }, | ||
729 | .attach = serial_attach, | ||
730 | .detach = serial_detach, | ||
731 | }; | ||
732 | |||
733 | static int __init init_serial_cs(void) | ||
734 | { | ||
735 | return pcmcia_register_driver(&serial_cs_driver); | ||
736 | } | ||
737 | |||
738 | static void __exit exit_serial_cs(void) | ||
739 | { | ||
740 | pcmcia_unregister_driver(&serial_cs_driver); | ||
741 | BUG_ON(dev_list != NULL); | ||
742 | } | ||
743 | |||
744 | module_init(init_serial_cs); | ||
745 | module_exit(exit_serial_cs); | ||
746 | |||
747 | MODULE_LICENSE("GPL"); | ||