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