cb_das16_cs.c revision 5a0e3ad6af8660be21ca98a971cd00f331318c05
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.AssignedIRQ, das16cs_interrupt,
184			  IRQF_SHARED, "cb_das16_cs", dev);
185	if (ret < 0) {
186		return ret;
187	}
188	dev->irq = link->irq.AssignedIRQ;
189	printk("irq=%u ", dev->irq);
190
191	dev->board_ptr = das16cs_probe(dev, link);
192	if (!dev->board_ptr)
193		return -EIO;
194
195	dev->board_name = thisboard->name;
196
197	if (alloc_private(dev, sizeof(struct das16cs_private)) < 0)
198		return -ENOMEM;
199
200	if (alloc_subdevices(dev, 4) < 0)
201		return -ENOMEM;
202
203	s = dev->subdevices + 0;
204	dev->read_subdev = s;
205	/* analog input subdevice */
206	s->type = COMEDI_SUBD_AI;
207	s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
208	s->n_chan = 16;
209	s->maxdata = 0xffff;
210	s->range_table = &das16cs_ai_range;
211	s->len_chanlist = 16;
212	s->insn_read = das16cs_ai_rinsn;
213	s->do_cmd = das16cs_ai_cmd;
214	s->do_cmdtest = das16cs_ai_cmdtest;
215
216	s = dev->subdevices + 1;
217	/* analog output subdevice */
218	if (thisboard->n_ao_chans) {
219		s->type = COMEDI_SUBD_AO;
220		s->subdev_flags = SDF_WRITABLE;
221		s->n_chan = thisboard->n_ao_chans;
222		s->maxdata = 0xffff;
223		s->range_table = &range_bipolar10;
224		s->insn_write = &das16cs_ao_winsn;
225		s->insn_read = &das16cs_ao_rinsn;
226	}
227
228	s = dev->subdevices + 2;
229	/* digital i/o subdevice */
230	if (1) {
231		s->type = COMEDI_SUBD_DIO;
232		s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
233		s->n_chan = 8;
234		s->maxdata = 1;
235		s->range_table = &range_digital;
236		s->insn_bits = das16cs_dio_insn_bits;
237		s->insn_config = das16cs_dio_insn_config;
238	} else {
239		s->type = COMEDI_SUBD_UNUSED;
240	}
241
242	s = dev->subdevices + 3;
243	/* timer subdevice */
244	if (0) {
245		s->type = COMEDI_SUBD_TIMER;
246		s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
247		s->n_chan = 1;
248		s->maxdata = 0xff;
249		s->range_table = &range_unknown;
250		s->insn_read = das16cs_timer_insn_read;
251		s->insn_config = das16cs_timer_insn_config;
252	} else {
253		s->type = COMEDI_SUBD_UNUSED;
254	}
255
256	printk("attached\n");
257
258	return 1;
259}
260
261static int das16cs_detach(struct comedi_device *dev)
262{
263	printk("comedi%d: das16cs: remove\n", dev->minor);
264
265	if (dev->irq) {
266		free_irq(dev->irq, dev);
267	}
268
269	return 0;
270}
271
272static irqreturn_t das16cs_interrupt(int irq, void *d)
273{
274	/* struct comedi_device *dev = d; */
275	return IRQ_HANDLED;
276}
277
278/*
279 * "instructions" read/write data in "one-shot" or "software-triggered"
280 * mode.
281 */
282static int das16cs_ai_rinsn(struct comedi_device *dev,
283			    struct comedi_subdevice *s,
284			    struct comedi_insn *insn, unsigned int *data)
285{
286	int i;
287	int to;
288	int aref;
289	int range;
290	int chan;
291	static int range_bits[] = { 0x800, 0x000, 0x100, 0x200 };
292
293	chan = CR_CHAN(insn->chanspec);
294	aref = CR_AREF(insn->chanspec);
295	range = CR_RANGE(insn->chanspec);
296
297	outw(chan, dev->iobase + 2);
298
299	devpriv->status1 &= ~0xf320;
300	devpriv->status1 |= (aref == AREF_DIFF) ? 0 : 0x0020;
301	outw(devpriv->status1, dev->iobase + 4);
302
303	devpriv->status2 &= ~0xff00;
304	devpriv->status2 |= range_bits[range];
305	outw(devpriv->status2, dev->iobase + 6);
306
307	for (i = 0; i < insn->n; i++) {
308		outw(0, dev->iobase);
309
310#define TIMEOUT 1000
311		for (to = 0; to < TIMEOUT; to++) {
312			if (inw(dev->iobase + 4) & 0x0080)
313				break;
314		}
315		if (to == TIMEOUT) {
316			printk("cb_das16_cs: ai timeout\n");
317			return -ETIME;
318		}
319		data[i] = (unsigned short)inw(dev->iobase + 0);
320	}
321
322	return i;
323}
324
325static int das16cs_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
326{
327	return -EINVAL;
328}
329
330static int das16cs_ai_cmdtest(struct comedi_device *dev,
331			      struct comedi_subdevice *s,
332			      struct comedi_cmd *cmd)
333{
334	int err = 0;
335	int tmp;
336
337	/* cmdtest tests a particular command to see if it is valid.
338	 * Using the cmdtest ioctl, a user can create a valid cmd
339	 * and then have it executes by the cmd ioctl.
340	 *
341	 * cmdtest returns 1,2,3,4 or 0, depending on which tests
342	 * the command passes. */
343
344	/* step 1: make sure trigger sources are trivially valid */
345
346	tmp = cmd->start_src;
347	cmd->start_src &= TRIG_NOW;
348	if (!cmd->start_src || tmp != cmd->start_src)
349		err++;
350
351	tmp = cmd->scan_begin_src;
352	cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
353	if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
354		err++;
355
356	tmp = cmd->convert_src;
357	cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
358	if (!cmd->convert_src || tmp != cmd->convert_src)
359		err++;
360
361	tmp = cmd->scan_end_src;
362	cmd->scan_end_src &= TRIG_COUNT;
363	if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
364		err++;
365
366	tmp = cmd->stop_src;
367	cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
368	if (!cmd->stop_src || tmp != cmd->stop_src)
369		err++;
370
371	if (err)
372		return 1;
373
374	/* step 2: make sure trigger sources are unique and 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
664/*
665   The dev_info variable is the "key" that is used to match up this
666   device driver with appropriate cards, through the card configuration
667   database.
668*/
669
670static dev_info_t dev_info = "cb_das16_cs";
671
672struct local_info_t {
673	struct pcmcia_device *link;
674	dev_node_t node;
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	/* Interrupt setup */
706	link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
707	link->irq.Handler = NULL;
708
709	link->conf.Attributes = 0;
710	link->conf.IntType = INT_MEMORY_AND_IO;
711
712	cur_dev = link;
713
714	das16cs_pcmcia_config(link);
715
716	return 0;
717}				/* das16cs_pcmcia_attach */
718
719static void das16cs_pcmcia_detach(struct pcmcia_device *link)
720{
721	dev_dbg(&link->dev, "das16cs_pcmcia_detach\n");
722
723	if (link->dev_node) {
724		((struct local_info_t *)link->priv)->stop = 1;
725		das16cs_pcmcia_release(link);
726	}
727	/* This points to the parent struct local_info_t struct */
728	if (link->priv)
729		kfree(link->priv);
730}				/* das16cs_pcmcia_detach */
731
732
733static int das16cs_pcmcia_config_loop(struct pcmcia_device *p_dev,
734				cistpl_cftable_entry_t *cfg,
735				cistpl_cftable_entry_t *dflt,
736				unsigned int vcc,
737				void *priv_data)
738{
739	if (cfg->index == 0)
740		return -EINVAL;
741
742	/* Do we need to allocate an interrupt? */
743	if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1)
744		p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
745
746	/* IO window settings */
747	p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
748	if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
749		cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
750		p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
751		if (!(io->flags & CISTPL_IO_8BIT))
752			p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
753		if (!(io->flags & CISTPL_IO_16BIT))
754			p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
755		p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
756		p_dev->io.BasePort1 = io->win[0].base;
757		p_dev->io.NumPorts1 = io->win[0].len;
758		if (io->nwin > 1) {
759			p_dev->io.Attributes2 = p_dev->io.Attributes1;
760			p_dev->io.BasePort2 = io->win[1].base;
761			p_dev->io.NumPorts2 = io->win[1].len;
762		}
763		/* This reserves IO space but doesn't actually enable it */
764		return pcmcia_request_io(p_dev, &p_dev->io);
765	}
766
767	return 0;
768}
769
770static void das16cs_pcmcia_config(struct pcmcia_device *link)
771{
772	struct local_info_t *dev = link->priv;
773	int ret;
774
775	dev_dbg(&link->dev, "das16cs_pcmcia_config\n");
776
777	ret = pcmcia_loop_config(link, das16cs_pcmcia_config_loop, NULL);
778	if (ret) {
779		dev_warn(&link->dev, "no configuration found\n");
780		goto failed;
781	}
782
783	/*
784	   Allocate an interrupt line.  Note that this does not assign a
785	   handler to the interrupt, unless the 'Handler' member of the
786	   irq structure is initialized.
787	 */
788	if (link->conf.Attributes & CONF_ENABLE_IRQ) {
789		ret = pcmcia_request_irq(link, &link->irq);
790		if (ret)
791			goto failed;
792	}
793	/*
794	   This actually configures the PCMCIA socket -- setting up
795	   the I/O windows and the interrupt mapping, and putting the
796	   card and host interface into "Memory and IO" mode.
797	 */
798	ret = pcmcia_request_configuration(link, &link->conf);
799	if (ret)
800		goto failed;
801
802	/*
803	   At this point, the dev_node_t structure(s) need to be
804	   initialized and arranged in a linked list at link->dev.
805	 */
806	sprintf(dev->node.dev_name, "cb_das16_cs");
807	dev->node.major = dev->node.minor = 0;
808	link->dev_node = &dev->node;
809
810	/* Finally, report what we've done */
811	printk(KERN_INFO "%s: index 0x%02x",
812	       dev->node.dev_name, link->conf.ConfigIndex);
813	if (link->conf.Attributes & CONF_ENABLE_IRQ)
814		printk(", irq %u", link->irq.AssignedIRQ);
815	if (link->io.NumPorts1)
816		printk(", io 0x%04x-0x%04x", link->io.BasePort1,
817		       link->io.BasePort1 + link->io.NumPorts1 - 1);
818	if (link->io.NumPorts2)
819		printk(" & 0x%04x-0x%04x", link->io.BasePort2,
820		       link->io.BasePort2 + link->io.NumPorts2 - 1);
821	printk("\n");
822
823	return;
824
825failed:
826	das16cs_pcmcia_release(link);
827}				/* das16cs_pcmcia_config */
828
829static void das16cs_pcmcia_release(struct pcmcia_device *link)
830{
831	dev_dbg(&link->dev, "das16cs_pcmcia_release\n");
832	pcmcia_disable_device(link);
833}				/* das16cs_pcmcia_release */
834
835static int das16cs_pcmcia_suspend(struct pcmcia_device *link)
836{
837	struct local_info_t *local = link->priv;
838
839	/* Mark the device as stopped, to block IO until later */
840	local->stop = 1;
841
842	return 0;
843}				/* das16cs_pcmcia_suspend */
844
845static int das16cs_pcmcia_resume(struct pcmcia_device *link)
846{
847	struct local_info_t *local = link->priv;
848
849	local->stop = 0;
850	return 0;
851}				/* das16cs_pcmcia_resume */
852
853/*====================================================================*/
854
855static struct pcmcia_device_id das16cs_id_table[] = {
856	PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0039),
857	PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4009),
858	PCMCIA_DEVICE_NULL
859};
860
861MODULE_DEVICE_TABLE(pcmcia, das16cs_id_table);
862
863struct pcmcia_driver das16cs_driver = {
864	.probe = das16cs_pcmcia_attach,
865	.remove = das16cs_pcmcia_detach,
866	.suspend = das16cs_pcmcia_suspend,
867	.resume = das16cs_pcmcia_resume,
868	.id_table = das16cs_id_table,
869	.owner = THIS_MODULE,
870	.drv = {
871		.name = dev_info,
872		},
873};
874
875static int __init init_das16cs_pcmcia_cs(void)
876{
877	pcmcia_register_driver(&das16cs_driver);
878	return 0;
879}
880
881static void __exit exit_das16cs_pcmcia_cs(void)
882{
883	pr_debug("das16cs_pcmcia_cs: unloading\n");
884	pcmcia_unregister_driver(&das16cs_driver);
885}
886
887int __init init_module(void)
888{
889	int ret;
890
891	ret = init_das16cs_pcmcia_cs();
892	if (ret < 0)
893		return ret;
894
895	return comedi_driver_register(&driver_das16cs);
896}
897
898void __exit cleanup_module(void)
899{
900	exit_das16cs_pcmcia_cs();
901	comedi_driver_unregister(&driver_das16cs);
902}
903
904#else
905COMEDI_INITCLEANUP(driver_das16cs);
906#endif /* CONFIG_PCMCIA */
907