cb_das16_cs.c revision 589cabe1b13576b85419d73ba87e50f7f80463fb
1/*
2    comedi/drivers/das16cs.c
3    Driver for Computer Boards PC-CARD DAS16/16.
4
5    COMEDI - Linux Control and Measurement Device Interface
6    Copyright (C) 2000, 2001, 2002 David A. Schleef <ds@schleef.org>
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22*/
23/*
24Driver: cb_das16_cs
25Description: Computer Boards PC-CARD DAS16/16
26Devices: [ComputerBoards] PC-CARD DAS16/16 (cb_das16_cs), PC-CARD DAS16/16-AO
27Author: ds
28Updated: Mon, 04 Nov 2002 20:04:21 -0800
29Status: experimental
30
31
32*/
33
34#include <linux/interrupt.h>
35#include <linux/slab.h>
36#include "../comedidev.h"
37#include <linux/delay.h>
38#include <linux/pci.h>
39
40#include <pcmcia/cistpl.h>
41#include <pcmcia/ds.h>
42
43#include "8253.h"
44
45#define DAS16CS_SIZE			18
46
47#define DAS16CS_ADC_DATA		0
48#define DAS16CS_DIO_MUX			2
49#define DAS16CS_MISC1			4
50#define DAS16CS_MISC2			6
51#define DAS16CS_CTR0			8
52#define DAS16CS_CTR1			10
53#define DAS16CS_CTR2			12
54#define DAS16CS_CTR_CONTROL		14
55#define DAS16CS_DIO			16
56
57struct das16cs_board {
58	const char *name;
59	int device_id;
60	int n_ao_chans;
61};
62static const struct das16cs_board das16cs_boards[] = {
63	{
64	 .device_id = 0x0000,	/* unknown */
65	 .name = "PC-CARD DAS16/16",
66	 .n_ao_chans = 0,
67	 },
68	{
69	 .device_id = 0x0039,
70	 .name = "PC-CARD DAS16/16-AO",
71	 .n_ao_chans = 2,
72	 },
73	{
74	 .device_id = 0x4009,
75	 .name = "PCM-DAS16s/16",
76	 .n_ao_chans = 0,
77	 },
78};
79
80#define n_boards ARRAY_SIZE(das16cs_boards)
81#define thisboard ((const struct das16cs_board *)dev->board_ptr)
82
83struct das16cs_private {
84	struct pcmcia_device *link;
85
86	unsigned int ao_readback[2];
87	unsigned short status1;
88	unsigned short status2;
89};
90#define devpriv ((struct das16cs_private *)dev->private)
91
92static int das16cs_attach(struct comedi_device *dev,
93			  struct comedi_devconfig *it);
94static int das16cs_detach(struct comedi_device *dev);
95static struct comedi_driver driver_das16cs = {
96	.driver_name = "cb_das16_cs",
97	.module = THIS_MODULE,
98	.attach = das16cs_attach,
99	.detach = das16cs_detach,
100};
101
102static struct pcmcia_device *cur_dev = NULL;
103
104static const struct comedi_lrange das16cs_ai_range = { 4, {
105							   RANGE(-10, 10),
106							   RANGE(-5, 5),
107							   RANGE(-2.5, 2.5),
108							   RANGE(-1.25, 1.25),
109							   }
110};
111
112static irqreturn_t das16cs_interrupt(int irq, void *d);
113static int das16cs_ai_rinsn(struct comedi_device *dev,
114			    struct comedi_subdevice *s,
115			    struct comedi_insn *insn, unsigned int *data);
116static int das16cs_ai_cmd(struct comedi_device *dev,
117			  struct comedi_subdevice *s);
118static int das16cs_ai_cmdtest(struct comedi_device *dev,
119			      struct comedi_subdevice *s,
120			      struct comedi_cmd *cmd);
121static int das16cs_ao_winsn(struct comedi_device *dev,
122			    struct comedi_subdevice *s,
123			    struct comedi_insn *insn, unsigned int *data);
124static int das16cs_ao_rinsn(struct comedi_device *dev,
125			    struct comedi_subdevice *s,
126			    struct comedi_insn *insn, unsigned int *data);
127static int das16cs_dio_insn_bits(struct comedi_device *dev,
128				 struct comedi_subdevice *s,
129				 struct comedi_insn *insn, unsigned int *data);
130static int das16cs_dio_insn_config(struct comedi_device *dev,
131				   struct comedi_subdevice *s,
132				   struct comedi_insn *insn,
133				   unsigned int *data);
134static int das16cs_timer_insn_read(struct comedi_device *dev,
135				   struct comedi_subdevice *s,
136				   struct comedi_insn *insn,
137				   unsigned int *data);
138static int das16cs_timer_insn_config(struct comedi_device *dev,
139				     struct comedi_subdevice *s,
140				     struct comedi_insn *insn,
141				     unsigned int *data);
142
143static const struct das16cs_board *das16cs_probe(struct comedi_device *dev,
144						 struct pcmcia_device *link)
145{
146	int i;
147
148	for (i = 0; i < n_boards; i++) {
149		if (das16cs_boards[i].device_id == link->card_id)
150			return das16cs_boards + i;
151	}
152
153	printk("unknown board!\n");
154
155	return NULL;
156}
157
158static int das16cs_attach(struct comedi_device *dev,
159			  struct comedi_devconfig *it)
160{
161	struct pcmcia_device *link;
162	struct comedi_subdevice *s;
163	int ret;
164	int i;
165
166	printk("comedi%d: cb_das16_cs: ", dev->minor);
167
168	link = cur_dev;		/* XXX hack */
169	if (!link)
170		return -EIO;
171
172	dev->iobase = link->resource[0]->start;
173	printk("I/O base=0x%04lx ", dev->iobase);
174
175	printk("fingerprint:\n");
176	for (i = 0; i < 48; i += 2)
177		printk("%04x ", inw(dev->iobase + i));
178
179	printk("\n");
180
181	ret = request_irq(link->irq, das16cs_interrupt,
182			  IRQF_SHARED, "cb_das16_cs", dev);
183	if (ret < 0)
184		return ret;
185
186	dev->irq = link->irq;
187
188	printk("irq=%u ", dev->irq);
189
190	dev->board_ptr = das16cs_probe(dev, link);
191	if (!dev->board_ptr)
192		return -EIO;
193
194	dev->board_name = thisboard->name;
195
196	if (alloc_private(dev, sizeof(struct das16cs_private)) < 0)
197		return -ENOMEM;
198
199	if (alloc_subdevices(dev, 4) < 0)
200		return -ENOMEM;
201
202	s = dev->subdevices + 0;
203	dev->read_subdev = s;
204	/* analog input subdevice */
205	s->type = COMEDI_SUBD_AI;
206	s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
207	s->n_chan = 16;
208	s->maxdata = 0xffff;
209	s->range_table = &das16cs_ai_range;
210	s->len_chanlist = 16;
211	s->insn_read = das16cs_ai_rinsn;
212	s->do_cmd = das16cs_ai_cmd;
213	s->do_cmdtest = das16cs_ai_cmdtest;
214
215	s = dev->subdevices + 1;
216	/* analog output subdevice */
217	if (thisboard->n_ao_chans) {
218		s->type = COMEDI_SUBD_AO;
219		s->subdev_flags = SDF_WRITABLE;
220		s->n_chan = thisboard->n_ao_chans;
221		s->maxdata = 0xffff;
222		s->range_table = &range_bipolar10;
223		s->insn_write = &das16cs_ao_winsn;
224		s->insn_read = &das16cs_ao_rinsn;
225	}
226
227	s = dev->subdevices + 2;
228	/* digital i/o subdevice */
229	if (1) {
230		s->type = COMEDI_SUBD_DIO;
231		s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
232		s->n_chan = 8;
233		s->maxdata = 1;
234		s->range_table = &range_digital;
235		s->insn_bits = das16cs_dio_insn_bits;
236		s->insn_config = das16cs_dio_insn_config;
237	} else {
238		s->type = COMEDI_SUBD_UNUSED;
239	}
240
241	s = dev->subdevices + 3;
242	/* timer subdevice */
243	if (0) {
244		s->type = COMEDI_SUBD_TIMER;
245		s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
246		s->n_chan = 1;
247		s->maxdata = 0xff;
248		s->range_table = &range_unknown;
249		s->insn_read = das16cs_timer_insn_read;
250		s->insn_config = das16cs_timer_insn_config;
251	} else {
252		s->type = COMEDI_SUBD_UNUSED;
253	}
254
255	printk("attached\n");
256
257	return 1;
258}
259
260static int das16cs_detach(struct comedi_device *dev)
261{
262	printk("comedi%d: das16cs: remove\n", dev->minor);
263
264	if (dev->irq)
265		free_irq(dev->irq, dev);
266
267
268	return 0;
269}
270
271static irqreturn_t das16cs_interrupt(int irq, void *d)
272{
273	/* struct comedi_device *dev = d; */
274	return IRQ_HANDLED;
275}
276
277/*
278 * "instructions" read/write data in "one-shot" or "software-triggered"
279 * mode.
280 */
281static int das16cs_ai_rinsn(struct comedi_device *dev,
282			    struct comedi_subdevice *s,
283			    struct comedi_insn *insn, unsigned int *data)
284{
285	int i;
286	int to;
287	int aref;
288	int range;
289	int chan;
290	static int range_bits[] = { 0x800, 0x000, 0x100, 0x200 };
291
292	chan = CR_CHAN(insn->chanspec);
293	aref = CR_AREF(insn->chanspec);
294	range = CR_RANGE(insn->chanspec);
295
296	outw(chan, dev->iobase + 2);
297
298	devpriv->status1 &= ~0xf320;
299	devpriv->status1 |= (aref == AREF_DIFF) ? 0 : 0x0020;
300	outw(devpriv->status1, dev->iobase + 4);
301
302	devpriv->status2 &= ~0xff00;
303	devpriv->status2 |= range_bits[range];
304	outw(devpriv->status2, dev->iobase + 6);
305
306	for (i = 0; i < insn->n; i++) {
307		outw(0, dev->iobase);
308
309#define TIMEOUT 1000
310		for (to = 0; to < TIMEOUT; to++) {
311			if (inw(dev->iobase + 4) & 0x0080)
312				break;
313		}
314		if (to == TIMEOUT) {
315			printk("cb_das16_cs: ai timeout\n");
316			return -ETIME;
317		}
318		data[i] = (unsigned short)inw(dev->iobase + 0);
319	}
320
321	return i;
322}
323
324static int das16cs_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
325{
326	return -EINVAL;
327}
328
329static int das16cs_ai_cmdtest(struct comedi_device *dev,
330			      struct comedi_subdevice *s,
331			      struct comedi_cmd *cmd)
332{
333	int err = 0;
334	int tmp;
335
336	/* cmdtest tests a particular command to see if it is valid.
337	 * Using the cmdtest ioctl, a user can create a valid cmd
338	 * and then have it executes by the cmd ioctl.
339	 *
340	 * cmdtest returns 1,2,3,4 or 0, depending on which tests
341	 * the command passes. */
342
343	/* step 1: make sure trigger sources are trivially valid */
344
345	tmp = cmd->start_src;
346	cmd->start_src &= TRIG_NOW;
347	if (!cmd->start_src || tmp != cmd->start_src)
348		err++;
349
350	tmp = cmd->scan_begin_src;
351	cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
352	if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
353		err++;
354
355	tmp = cmd->convert_src;
356	cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
357	if (!cmd->convert_src || tmp != cmd->convert_src)
358		err++;
359
360	tmp = cmd->scan_end_src;
361	cmd->scan_end_src &= TRIG_COUNT;
362	if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
363		err++;
364
365	tmp = cmd->stop_src;
366	cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
367	if (!cmd->stop_src || tmp != cmd->stop_src)
368		err++;
369
370	if (err)
371		return 1;
372
373	/* step 2: make sure trigger sources are unique and
374	 * mutually compatible */
375
376	/* note that mutual compatibility is not an issue here */
377	if (cmd->scan_begin_src != TRIG_TIMER &&
378	    cmd->scan_begin_src != TRIG_EXT)
379		err++;
380	if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
381		err++;
382	if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
383		err++;
384
385	if (err)
386		return 2;
387
388	/* step 3: make sure arguments are trivially compatible */
389
390	if (cmd->start_arg != 0) {
391		cmd->start_arg = 0;
392		err++;
393	}
394#define MAX_SPEED	10000	/* in nanoseconds */
395#define MIN_SPEED	1000000000	/* in nanoseconds */
396
397	if (cmd->scan_begin_src == TRIG_TIMER) {
398		if (cmd->scan_begin_arg < MAX_SPEED) {
399			cmd->scan_begin_arg = MAX_SPEED;
400			err++;
401		}
402		if (cmd->scan_begin_arg > MIN_SPEED) {
403			cmd->scan_begin_arg = MIN_SPEED;
404			err++;
405		}
406	} else {
407		/* external trigger */
408		/* should be level/edge, hi/lo specification here */
409		/* should specify multiple external triggers */
410		if (cmd->scan_begin_arg > 9) {
411			cmd->scan_begin_arg = 9;
412			err++;
413		}
414	}
415	if (cmd->convert_src == TRIG_TIMER) {
416		if (cmd->convert_arg < MAX_SPEED) {
417			cmd->convert_arg = MAX_SPEED;
418			err++;
419		}
420		if (cmd->convert_arg > MIN_SPEED) {
421			cmd->convert_arg = MIN_SPEED;
422			err++;
423		}
424	} else {
425		/* external trigger */
426		/* see above */
427		if (cmd->convert_arg > 9) {
428			cmd->convert_arg = 9;
429			err++;
430		}
431	}
432
433	if (cmd->scan_end_arg != cmd->chanlist_len) {
434		cmd->scan_end_arg = cmd->chanlist_len;
435		err++;
436	}
437	if (cmd->stop_src == TRIG_COUNT) {
438		if (cmd->stop_arg > 0x00ffffff) {
439			cmd->stop_arg = 0x00ffffff;
440			err++;
441		}
442	} else {
443		/* TRIG_NONE */
444		if (cmd->stop_arg != 0) {
445			cmd->stop_arg = 0;
446			err++;
447		}
448	}
449
450	if (err)
451		return 3;
452
453	/* step 4: fix up any arguments */
454
455	if (cmd->scan_begin_src == TRIG_TIMER) {
456		unsigned int div1 = 0, div2 = 0;
457
458		tmp = cmd->scan_begin_arg;
459		i8253_cascade_ns_to_timer(100, &div1, &div2,
460					  &cmd->scan_begin_arg,
461					  cmd->flags & TRIG_ROUND_MASK);
462		if (tmp != cmd->scan_begin_arg)
463			err++;
464	}
465	if (cmd->convert_src == TRIG_TIMER) {
466		unsigned int div1 = 0, div2 = 0;
467
468		tmp = cmd->convert_arg;
469		i8253_cascade_ns_to_timer(100, &div1, &div2,
470					  &cmd->scan_begin_arg,
471					  cmd->flags & TRIG_ROUND_MASK);
472		if (tmp != cmd->convert_arg)
473			err++;
474		if (cmd->scan_begin_src == TRIG_TIMER &&
475		    cmd->scan_begin_arg <
476		    cmd->convert_arg * cmd->scan_end_arg) {
477			cmd->scan_begin_arg =
478			    cmd->convert_arg * cmd->scan_end_arg;
479			err++;
480		}
481	}
482
483	if (err)
484		return 4;
485
486	return 0;
487}
488
489static int das16cs_ao_winsn(struct comedi_device *dev,
490			    struct comedi_subdevice *s,
491			    struct comedi_insn *insn, unsigned int *data)
492{
493	int i;
494	int chan = CR_CHAN(insn->chanspec);
495	unsigned short status1;
496	unsigned short d;
497	int bit;
498
499	for (i = 0; i < insn->n; i++) {
500		devpriv->ao_readback[chan] = data[i];
501		d = data[i];
502
503		outw(devpriv->status1, dev->iobase + 4);
504		udelay(1);
505
506		status1 = devpriv->status1 & ~0xf;
507		if (chan)
508			status1 |= 0x0001;
509		else
510			status1 |= 0x0008;
511
512/*		printk("0x%04x\n",status1);*/
513		outw(status1, dev->iobase + 4);
514		udelay(1);
515
516		for (bit = 15; bit >= 0; bit--) {
517			int b = (d >> bit) & 0x1;
518			b <<= 1;
519/*			printk("0x%04x\n",status1 | b | 0x0000);*/
520			outw(status1 | b | 0x0000, dev->iobase + 4);
521			udelay(1);
522/*			printk("0x%04x\n",status1 | b | 0x0004);*/
523			outw(status1 | b | 0x0004, dev->iobase + 4);
524			udelay(1);
525		}
526/*		make high both DAC0CS and DAC1CS to load
527		new data and update analog output*/
528		outw(status1 | 0x9, dev->iobase + 4);
529	}
530
531	return i;
532}
533
534/* AO subdevices should have a read insn as well as a write insn.
535 * Usually this means copying a value stored in devpriv. */
536static int das16cs_ao_rinsn(struct comedi_device *dev,
537			    struct comedi_subdevice *s,
538			    struct comedi_insn *insn, unsigned int *data)
539{
540	int i;
541	int chan = CR_CHAN(insn->chanspec);
542
543	for (i = 0; i < insn->n; i++)
544		data[i] = devpriv->ao_readback[chan];
545
546	return i;
547}
548
549/* DIO devices are slightly special.  Although it is possible to
550 * implement the insn_read/insn_write interface, it is much more
551 * useful to applications if you implement the insn_bits interface.
552 * This allows packed reading/writing of the DIO channels.  The
553 * comedi core can convert between insn_bits and insn_read/write */
554static int das16cs_dio_insn_bits(struct comedi_device *dev,
555				 struct comedi_subdevice *s,
556				 struct comedi_insn *insn, unsigned int *data)
557{
558	if (insn->n != 2)
559		return -EINVAL;
560
561	if (data[0]) {
562		s->state &= ~data[0];
563		s->state |= data[0] & data[1];
564
565		outw(s->state, dev->iobase + 16);
566	}
567
568	/* on return, data[1] contains the value of the digital
569	 * input and output lines. */
570	data[1] = inw(dev->iobase + 16);
571
572	return 2;
573}
574
575static int das16cs_dio_insn_config(struct comedi_device *dev,
576				   struct comedi_subdevice *s,
577				   struct comedi_insn *insn, unsigned int *data)
578{
579	int chan = CR_CHAN(insn->chanspec);
580	int bits;
581
582	if (chan < 4)
583		bits = 0x0f;
584	else
585		bits = 0xf0;
586
587	switch (data[0]) {
588	case INSN_CONFIG_DIO_OUTPUT:
589		s->io_bits |= bits;
590		break;
591	case INSN_CONFIG_DIO_INPUT:
592		s->io_bits &= bits;
593		break;
594	case INSN_CONFIG_DIO_QUERY:
595		data[1] =
596		    (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
597		return insn->n;
598		break;
599	default:
600		return -EINVAL;
601		break;
602	}
603
604	devpriv->status2 &= ~0x00c0;
605	devpriv->status2 |= (s->io_bits & 0xf0) ? 0x0080 : 0;
606	devpriv->status2 |= (s->io_bits & 0x0f) ? 0x0040 : 0;
607
608	outw(devpriv->status2, dev->iobase + 6);
609
610	return insn->n;
611}
612
613static int das16cs_timer_insn_read(struct comedi_device *dev,
614				   struct comedi_subdevice *s,
615				   struct comedi_insn *insn, unsigned int *data)
616{
617	return -EINVAL;
618}
619
620static int das16cs_timer_insn_config(struct comedi_device *dev,
621				     struct comedi_subdevice *s,
622				     struct comedi_insn *insn,
623				     unsigned int *data)
624{
625	return -EINVAL;
626}
627
628/* PCMCIA stuff */
629
630/*======================================================================
631
632    The following pcmcia code for the pcm-das08 is adapted from the
633    dummy_cs.c driver of the Linux PCMCIA Card Services package.
634
635    The initial developer of the original code is David A. Hinds
636    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
637    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
638
639======================================================================*/
640
641#if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE)
642
643static void das16cs_pcmcia_config(struct pcmcia_device *link);
644static void das16cs_pcmcia_release(struct pcmcia_device *link);
645static int das16cs_pcmcia_suspend(struct pcmcia_device *p_dev);
646static int das16cs_pcmcia_resume(struct pcmcia_device *p_dev);
647
648/*
649   The attach() and detach() entry points are used to create and destroy
650   "instances" of the driver, where each instance represents everything
651   needed to manage one actual PCMCIA card.
652*/
653
654static int das16cs_pcmcia_attach(struct pcmcia_device *);
655static void das16cs_pcmcia_detach(struct pcmcia_device *);
656
657/*
658   You'll also need to prototype all the functions that will actually
659   be used to talk to your device.  See 'memory_cs' for a good example
660   of a fully self-sufficient driver; the other drivers rely more or
661   less on other parts of the kernel.
662*/
663
664struct local_info_t {
665	struct pcmcia_device *link;
666	int stop;
667	struct bus_operations *bus;
668};
669
670/*======================================================================
671
672    das16cs_pcmcia_attach() creates an "instance" of the driver, allocating
673    local data structures for one device.  The device is registered
674    with Card Services.
675
676    The dev_link structure is initialized, but we don't actually
677    configure the card at this point -- we wait until we receive a
678    card insertion event.
679
680======================================================================*/
681
682static int das16cs_pcmcia_attach(struct pcmcia_device *link)
683{
684	struct local_info_t *local;
685
686	dev_dbg(&link->dev, "das16cs_pcmcia_attach()\n");
687
688	/* Allocate space for private device-specific data */
689	local = kzalloc(sizeof(struct local_info_t), GFP_KERNEL);
690	if (!local)
691		return -ENOMEM;
692	local->link = link;
693	link->priv = local;
694
695	cur_dev = link;
696
697	das16cs_pcmcia_config(link);
698
699	return 0;
700}				/* das16cs_pcmcia_attach */
701
702static void das16cs_pcmcia_detach(struct pcmcia_device *link)
703{
704	dev_dbg(&link->dev, "das16cs_pcmcia_detach\n");
705
706	((struct local_info_t *)link->priv)->stop = 1;
707	das16cs_pcmcia_release(link);
708	/* This points to the parent struct local_info_t struct */
709	kfree(link->priv);
710}				/* das16cs_pcmcia_detach */
711
712
713static int das16cs_pcmcia_config_loop(struct pcmcia_device *p_dev,
714				void *priv_data)
715{
716	if (p_dev->config_index == 0)
717		return -EINVAL;
718
719	return pcmcia_request_io(p_dev);
720}
721
722static void das16cs_pcmcia_config(struct pcmcia_device *link)
723{
724	int ret;
725
726	dev_dbg(&link->dev, "das16cs_pcmcia_config\n");
727
728	/* Do we need to allocate an interrupt? */
729	link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
730
731	ret = pcmcia_loop_config(link, das16cs_pcmcia_config_loop, NULL);
732	if (ret) {
733		dev_warn(&link->dev, "no configuration found\n");
734		goto failed;
735	}
736
737	if (!link->irq)
738		goto failed;
739
740	ret = pcmcia_enable_device(link);
741	if (ret)
742		goto failed;
743
744	return;
745
746failed:
747	das16cs_pcmcia_release(link);
748}				/* das16cs_pcmcia_config */
749
750static void das16cs_pcmcia_release(struct pcmcia_device *link)
751{
752	dev_dbg(&link->dev, "das16cs_pcmcia_release\n");
753	pcmcia_disable_device(link);
754}				/* das16cs_pcmcia_release */
755
756static int das16cs_pcmcia_suspend(struct pcmcia_device *link)
757{
758	struct local_info_t *local = link->priv;
759
760	/* Mark the device as stopped, to block IO until later */
761	local->stop = 1;
762
763	return 0;
764}				/* das16cs_pcmcia_suspend */
765
766static int das16cs_pcmcia_resume(struct pcmcia_device *link)
767{
768	struct local_info_t *local = link->priv;
769
770	local->stop = 0;
771	return 0;
772}				/* das16cs_pcmcia_resume */
773
774/*====================================================================*/
775
776static const struct pcmcia_device_id das16cs_id_table[] = {
777	PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0039),
778	PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4009),
779	PCMCIA_DEVICE_NULL
780};
781
782MODULE_DEVICE_TABLE(pcmcia, das16cs_id_table);
783MODULE_AUTHOR("David A. Schleef <ds@schleef.org>");
784MODULE_DESCRIPTION("Comedi driver for Computer Boards PC-CARD DAS16/16");
785MODULE_LICENSE("GPL");
786
787struct pcmcia_driver das16cs_driver = {
788	.probe = das16cs_pcmcia_attach,
789	.remove = das16cs_pcmcia_detach,
790	.suspend = das16cs_pcmcia_suspend,
791	.resume = das16cs_pcmcia_resume,
792	.id_table = das16cs_id_table,
793	.owner = THIS_MODULE,
794	.name = "cb_das16_cs",
795};
796
797static int __init init_das16cs_pcmcia_cs(void)
798{
799	pcmcia_register_driver(&das16cs_driver);
800	return 0;
801}
802
803static void __exit exit_das16cs_pcmcia_cs(void)
804{
805	pr_debug("das16cs_pcmcia_cs: unloading\n");
806	pcmcia_unregister_driver(&das16cs_driver);
807}
808
809int __init init_module(void)
810{
811	int ret;
812
813	ret = init_das16cs_pcmcia_cs();
814	if (ret < 0)
815		return ret;
816
817	return comedi_driver_register(&driver_das16cs);
818}
819
820void __exit cleanup_module(void)
821{
822	exit_das16cs_pcmcia_cs();
823	comedi_driver_unregister(&driver_das16cs);
824}
825
826#else
827static int __init driver_das16cs_init_module(void)
828{
829	return comedi_driver_register(&driver_das16cs);
830}
831
832static void __exit driver_das16cs_cleanup_module(void)
833{
834	comedi_driver_unregister(&driver_das16cs);
835}
836
837module_init(driver_das16cs_init_module);
838module_exit(driver_das16cs_cleanup_module);
839#endif /* CONFIG_PCMCIA */
840