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