cb_das16_cs.c revision 55a19b39acb8888af8e9cfe5b762d03c52fdb48c
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 "../comedidev.h"
36#include <linux/delay.h>
37#include <linux/pci.h>
38
39#include <pcmcia/cs_types.h>
40#include <pcmcia/cs.h>
41#include <pcmcia/cistpl.h>
42#include <pcmcia/ds.h>
43
44#include "8253.h"
45
46#define DAS16CS_SIZE			18
47
48#define DAS16CS_ADC_DATA		0
49#define DAS16CS_DIO_MUX			2
50#define DAS16CS_MISC1			4
51#define DAS16CS_MISC2			6
52#define DAS16CS_CTR0			8
53#define DAS16CS_CTR1			10
54#define DAS16CS_CTR2			12
55#define DAS16CS_CTR_CONTROL		14
56#define DAS16CS_DIO			16
57
58struct das16cs_board {
59	const char *name;
60	int device_id;
61	int n_ao_chans;
62};
63static const struct das16cs_board das16cs_boards[] = {
64	{
65	 .device_id = 0x0000,	/* unknown */
66	 .name = "PC-CARD DAS16/16",
67	 .n_ao_chans = 0,
68	 },
69	{
70	 .device_id = 0x0039,
71	 .name = "PC-CARD DAS16/16-AO",
72	 .n_ao_chans = 2,
73	 },
74	{
75	 .device_id = 0x4009,
76	 .name = "PCM-DAS16s/16",
77	 .n_ao_chans = 0,
78	 },
79};
80
81#define n_boards ARRAY_SIZE(das16cs_boards)
82#define thisboard ((const struct das16cs_board *)dev->board_ptr)
83
84struct das16cs_private {
85	struct pcmcia_device *link;
86
87	unsigned int ao_readback[2];
88	unsigned short status1;
89	unsigned short status2;
90};
91#define devpriv ((struct das16cs_private *)dev->private)
92
93static int das16cs_attach(struct comedi_device *dev,
94			  struct comedi_devconfig *it);
95static int das16cs_detach(struct comedi_device *dev);
96static struct comedi_driver driver_das16cs = {
97	.driver_name = "cb_das16_cs",
98	.module = THIS_MODULE,
99	.attach = das16cs_attach,
100	.detach = das16cs_detach,
101};
102
103static struct pcmcia_device *cur_dev = NULL;
104
105static const struct comedi_lrange das16cs_ai_range = { 4, {
106							   RANGE(-10, 10),
107							   RANGE(-5, 5),
108							   RANGE(-2.5, 2.5),
109							   RANGE(-1.25, 1.25),
110							   }
111};
112
113static irqreturn_t das16cs_interrupt(int irq, void *d);
114static int das16cs_ai_rinsn(struct comedi_device *dev,
115			    struct comedi_subdevice *s,
116			    struct comedi_insn *insn, unsigned int *data);
117static int das16cs_ai_cmd(struct comedi_device *dev,
118			  struct comedi_subdevice *s);
119static int das16cs_ai_cmdtest(struct comedi_device *dev,
120			      struct comedi_subdevice *s,
121			      struct comedi_cmd *cmd);
122static int das16cs_ao_winsn(struct comedi_device *dev,
123			    struct comedi_subdevice *s,
124			    struct comedi_insn *insn, unsigned int *data);
125static int das16cs_ao_rinsn(struct comedi_device *dev,
126			    struct comedi_subdevice *s,
127			    struct comedi_insn *insn, unsigned int *data);
128static int das16cs_dio_insn_bits(struct comedi_device *dev,
129				 struct comedi_subdevice *s,
130				 struct comedi_insn *insn, unsigned int *data);
131static int das16cs_dio_insn_config(struct comedi_device *dev,
132				   struct comedi_subdevice *s,
133				   struct comedi_insn *insn,
134				   unsigned int *data);
135static int das16cs_timer_insn_read(struct comedi_device *dev,
136				   struct comedi_subdevice *s,
137				   struct comedi_insn *insn,
138				   unsigned int *data);
139static int das16cs_timer_insn_config(struct comedi_device *dev,
140				     struct comedi_subdevice *s,
141				     struct comedi_insn *insn,
142				     unsigned int *data);
143
144static const struct das16cs_board *das16cs_probe(struct comedi_device *dev,
145						 struct pcmcia_device *link)
146{
147	int i;
148
149	for (i = 0; i < n_boards; i++) {
150		if (das16cs_boards[i].device_id == link->card_id)
151			return das16cs_boards + i;
152	}
153
154	printk("unknown board!\n");
155
156	return NULL;
157}
158
159static int das16cs_attach(struct comedi_device *dev,
160			  struct comedi_devconfig *it)
161{
162	struct pcmcia_device *link;
163	struct comedi_subdevice *s;
164	int ret;
165	int i;
166
167	printk("comedi%d: cb_das16_cs: ", dev->minor);
168
169	link = cur_dev;		/* XXX hack */
170	if (!link)
171		return -EIO;
172
173	dev->iobase = link->io.BasePort1;
174	printk("I/O base=0x%04lx ", dev->iobase);
175
176	printk("fingerprint:\n");
177	for (i = 0; i < 48; i += 2) {
178		printk("%04x ", inw(dev->iobase + i));
179	}
180	printk("\n");
181
182	ret = request_irq(link->irq.AssignedIRQ, das16cs_interrupt,
183			  IRQF_SHARED, "cb_das16_cs", dev);
184	if (ret < 0) {
185		return ret;
186	}
187	dev->irq = link->irq.AssignedIRQ;
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 mutually compatible */
374
375	/* note that mutual compatiblity is not an issue here */
376	if (cmd->scan_begin_src != TRIG_TIMER &&
377	    cmd->scan_begin_src != TRIG_EXT)
378		err++;
379	if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
380		err++;
381	if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
382		err++;
383
384	if (err)
385		return 2;
386
387	/* step 3: make sure arguments are trivially compatible */
388
389	if (cmd->start_arg != 0) {
390		cmd->start_arg = 0;
391		err++;
392	}
393#define MAX_SPEED	10000	/* in nanoseconds */
394#define MIN_SPEED	1000000000	/* in nanoseconds */
395
396	if (cmd->scan_begin_src == TRIG_TIMER) {
397		if (cmd->scan_begin_arg < MAX_SPEED) {
398			cmd->scan_begin_arg = MAX_SPEED;
399			err++;
400		}
401		if (cmd->scan_begin_arg > MIN_SPEED) {
402			cmd->scan_begin_arg = MIN_SPEED;
403			err++;
404		}
405	} else {
406		/* external trigger */
407		/* should be level/edge, hi/lo specification here */
408		/* should specify multiple external triggers */
409		if (cmd->scan_begin_arg > 9) {
410			cmd->scan_begin_arg = 9;
411			err++;
412		}
413	}
414	if (cmd->convert_src == TRIG_TIMER) {
415		if (cmd->convert_arg < MAX_SPEED) {
416			cmd->convert_arg = MAX_SPEED;
417			err++;
418		}
419		if (cmd->convert_arg > MIN_SPEED) {
420			cmd->convert_arg = MIN_SPEED;
421			err++;
422		}
423	} else {
424		/* external trigger */
425		/* see above */
426		if (cmd->convert_arg > 9) {
427			cmd->convert_arg = 9;
428			err++;
429		}
430	}
431
432	if (cmd->scan_end_arg != cmd->chanlist_len) {
433		cmd->scan_end_arg = cmd->chanlist_len;
434		err++;
435	}
436	if (cmd->stop_src == TRIG_COUNT) {
437		if (cmd->stop_arg > 0x00ffffff) {
438			cmd->stop_arg = 0x00ffffff;
439			err++;
440		}
441	} else {
442		/* TRIG_NONE */
443		if (cmd->stop_arg != 0) {
444			cmd->stop_arg = 0;
445			err++;
446		}
447	}
448
449	if (err)
450		return 3;
451
452	/* step 4: fix up any arguments */
453
454	if (cmd->scan_begin_src == TRIG_TIMER) {
455		unsigned int div1, div2;
456
457		tmp = cmd->scan_begin_arg;
458		i8253_cascade_ns_to_timer(100, &div1, &div2,
459					  &cmd->scan_begin_arg,
460					  cmd->flags & TRIG_ROUND_MASK);
461		if (tmp != cmd->scan_begin_arg)
462			err++;
463	}
464	if (cmd->convert_src == TRIG_TIMER) {
465		unsigned int div1, div2;
466
467		tmp = cmd->convert_arg;
468		i8253_cascade_ns_to_timer(100, &div1, &div2,
469					  &cmd->scan_begin_arg,
470					  cmd->flags & TRIG_ROUND_MASK);
471		if (tmp != cmd->convert_arg)
472			err++;
473		if (cmd->scan_begin_src == TRIG_TIMER &&
474		    cmd->scan_begin_arg <
475		    cmd->convert_arg * cmd->scan_end_arg) {
476			cmd->scan_begin_arg =
477			    cmd->convert_arg * cmd->scan_end_arg;
478			err++;
479		}
480	}
481
482	if (err)
483		return 4;
484
485	return 0;
486}
487
488static int das16cs_ao_winsn(struct comedi_device *dev,
489			    struct comedi_subdevice *s,
490			    struct comedi_insn *insn, unsigned int *data)
491{
492	int i;
493	int chan = CR_CHAN(insn->chanspec);
494	unsigned short status1;
495	unsigned short d;
496	int bit;
497
498	for (i = 0; i < insn->n; i++) {
499		devpriv->ao_readback[chan] = data[i];
500		d = data[i];
501
502		outw(devpriv->status1, dev->iobase + 4);
503		udelay(1);
504
505		status1 = devpriv->status1 & ~0xf;
506		if (chan)
507			status1 |= 0x0001;
508		else
509			status1 |= 0x0008;
510
511/* 		printk("0x%04x\n",status1);*/
512		outw(status1, dev->iobase + 4);
513		udelay(1);
514
515		for (bit = 15; bit >= 0; bit--) {
516			int b = (d >> bit) & 0x1;
517			b <<= 1;
518/*			printk("0x%04x\n",status1 | b | 0x0000);*/
519			outw(status1 | b | 0x0000, dev->iobase + 4);
520			udelay(1);
521/*			printk("0x%04x\n",status1 | b | 0x0004);*/
522			outw(status1 | b | 0x0004, dev->iobase + 4);
523			udelay(1);
524		}
525/*		make high both DAC0CS and DAC1CS to load
526		new data and update analog output*/
527		outw(status1 | 0x9, dev->iobase + 4);
528	}
529
530	return i;
531}
532
533/* AO subdevices should have a read insn as well as a write insn.
534 * Usually this means copying a value stored in devpriv. */
535static int das16cs_ao_rinsn(struct comedi_device *dev,
536			    struct comedi_subdevice *s,
537			    struct comedi_insn *insn, unsigned int *data)
538{
539	int i;
540	int chan = CR_CHAN(insn->chanspec);
541
542	for (i = 0; i < insn->n; i++)
543		data[i] = devpriv->ao_readback[chan];
544
545	return i;
546}
547
548/* DIO devices are slightly special.  Although it is possible to
549 * implement the insn_read/insn_write interface, it is much more
550 * useful to applications if you implement the insn_bits interface.
551 * This allows packed reading/writing of the DIO channels.  The
552 * comedi core can convert between insn_bits and insn_read/write */
553static int das16cs_dio_insn_bits(struct comedi_device *dev,
554				 struct comedi_subdevice *s,
555				 struct comedi_insn *insn, unsigned int *data)
556{
557	if (insn->n != 2)
558		return -EINVAL;
559
560	if (data[0]) {
561		s->state &= ~data[0];
562		s->state |= data[0] & data[1];
563
564		outw(s->state, dev->iobase + 16);
565	}
566
567	/* on return, data[1] contains the value of the digital
568	 * input and output lines. */
569	data[1] = inw(dev->iobase + 16);
570
571	return 2;
572}
573
574static int das16cs_dio_insn_config(struct comedi_device *dev,
575				   struct comedi_subdevice *s,
576				   struct comedi_insn *insn, unsigned int *data)
577{
578	int chan = CR_CHAN(insn->chanspec);
579	int bits;
580
581	if (chan < 4)
582		bits = 0x0f;
583	else
584		bits = 0xf0;
585
586	switch (data[0]) {
587	case INSN_CONFIG_DIO_OUTPUT:
588		s->io_bits |= bits;
589		break;
590	case INSN_CONFIG_DIO_INPUT:
591		s->io_bits &= bits;
592		break;
593	case INSN_CONFIG_DIO_QUERY:
594		data[1] =
595		    (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
596		return insn->n;
597		break;
598	default:
599		return -EINVAL;
600		break;
601	}
602
603	devpriv->status2 &= ~0x00c0;
604	devpriv->status2 |= (s->io_bits & 0xf0) ? 0x0080 : 0;
605	devpriv->status2 |= (s->io_bits & 0x0f) ? 0x0040 : 0;
606
607	outw(devpriv->status2, dev->iobase + 6);
608
609	return insn->n;
610}
611
612static int das16cs_timer_insn_read(struct comedi_device *dev,
613				   struct comedi_subdevice *s,
614				   struct comedi_insn *insn, unsigned int *data)
615{
616	return -EINVAL;
617}
618
619static int das16cs_timer_insn_config(struct comedi_device *dev,
620				     struct comedi_subdevice *s,
621				     struct comedi_insn *insn,
622				     unsigned int *data)
623{
624	return -EINVAL;
625}
626
627/* PCMCIA stuff */
628
629/*======================================================================
630
631    The following pcmcia code for the pcm-das08 is adapted from the
632    dummy_cs.c driver of the Linux PCMCIA Card Services package.
633
634    The initial developer of the original code is David A. Hinds
635    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
636    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
637
638======================================================================*/
639
640#if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE)
641
642static void das16cs_pcmcia_config(struct pcmcia_device *link);
643static void das16cs_pcmcia_release(struct pcmcia_device *link);
644static int das16cs_pcmcia_suspend(struct pcmcia_device *p_dev);
645static int das16cs_pcmcia_resume(struct pcmcia_device *p_dev);
646
647/*
648   The attach() and detach() entry points are used to create and destroy
649   "instances" of the driver, where each instance represents everything
650   needed to manage one actual PCMCIA card.
651*/
652
653static int das16cs_pcmcia_attach(struct pcmcia_device *);
654static void das16cs_pcmcia_detach(struct pcmcia_device *);
655
656/*
657   You'll also need to prototype all the functions that will actually
658   be used to talk to your device.  See 'memory_cs' for a good example
659   of a fully self-sufficient driver; the other drivers rely more or
660   less on other parts of the kernel.
661*/
662
663/*
664   The dev_info variable is the "key" that is used to match up this
665   device driver with appropriate cards, through the card configuration
666   database.
667*/
668
669static dev_info_t dev_info = "cb_das16_cs";
670
671struct local_info_t {
672	struct pcmcia_device *link;
673	dev_node_t node;
674	int stop;
675	struct bus_operations *bus;
676};
677
678/*======================================================================
679
680    das16cs_pcmcia_attach() creates an "instance" of the driver, allocating
681    local data structures for one device.  The device is registered
682    with Card Services.
683
684    The dev_link structure is initialized, but we don't actually
685    configure the card at this point -- we wait until we receive a
686    card insertion event.
687
688======================================================================*/
689
690static int das16cs_pcmcia_attach(struct pcmcia_device *link)
691{
692	struct local_info_t *local;
693
694	dev_dbg(&link->dev, "das16cs_pcmcia_attach()\n");
695
696	/* Allocate space for private device-specific data */
697	local = kzalloc(sizeof(struct local_info_t), GFP_KERNEL);
698	if (!local)
699		return -ENOMEM;
700	local->link = link;
701	link->priv = local;
702
703	/* Initialize the pcmcia_device structure */
704	/* Interrupt setup */
705	link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
706	link->irq.IRQInfo1 = IRQ_LEVEL_ID;
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