2
comedi/drivers/das16cs.c
3
Driver for Computer Boards PC-CARD DAS16/16.
5
COMEDI - Linux Control and Measurement Device Interface
6
Copyright (C) 2000, 2001, 2002 David A. Schleef <ds@schleef.org>
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.
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.
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.
25
Description: Computer Boards PC-CARD DAS16/16
26
Devices: [ComputerBoards] PC-CARD DAS16/16 (cb_das16_cs), PC-CARD DAS16/16-AO
28
Updated: Mon, 04 Nov 2002 20:04:21 -0800
34
#include <linux/interrupt.h>
35
#include <linux/slab.h>
36
#include "../comedidev.h"
37
#include <linux/delay.h>
38
#include <linux/pci.h>
40
#include <pcmcia/cistpl.h>
41
#include <pcmcia/ds.h>
45
#define DAS16CS_SIZE 18
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
57
struct das16cs_board {
62
static const struct das16cs_board das16cs_boards[] = {
64
.device_id = 0x0000, /* unknown */
65
.name = "PC-CARD DAS16/16",
70
.name = "PC-CARD DAS16/16-AO",
75
.name = "PCM-DAS16s/16",
80
#define n_boards ARRAY_SIZE(das16cs_boards)
81
#define thisboard ((const struct das16cs_board *)dev->board_ptr)
83
struct das16cs_private {
84
struct pcmcia_device *link;
86
unsigned int ao_readback[2];
87
unsigned short status1;
88
unsigned short status2;
90
#define devpriv ((struct das16cs_private *)dev->private)
92
static int das16cs_attach(struct comedi_device *dev,
93
struct comedi_devconfig *it);
94
static int das16cs_detach(struct comedi_device *dev);
95
static struct comedi_driver driver_das16cs = {
96
.driver_name = "cb_das16_cs",
97
.module = THIS_MODULE,
98
.attach = das16cs_attach,
99
.detach = das16cs_detach,
102
static struct pcmcia_device *cur_dev = NULL;
104
static const struct comedi_lrange das16cs_ai_range = { 4, {
112
static irqreturn_t das16cs_interrupt(int irq, void *d);
113
static int das16cs_ai_rinsn(struct comedi_device *dev,
114
struct comedi_subdevice *s,
115
struct comedi_insn *insn, unsigned int *data);
116
static int das16cs_ai_cmd(struct comedi_device *dev,
117
struct comedi_subdevice *s);
118
static int das16cs_ai_cmdtest(struct comedi_device *dev,
119
struct comedi_subdevice *s,
120
struct comedi_cmd *cmd);
121
static int das16cs_ao_winsn(struct comedi_device *dev,
122
struct comedi_subdevice *s,
123
struct comedi_insn *insn, unsigned int *data);
124
static int das16cs_ao_rinsn(struct comedi_device *dev,
125
struct comedi_subdevice *s,
126
struct comedi_insn *insn, unsigned int *data);
127
static int das16cs_dio_insn_bits(struct comedi_device *dev,
128
struct comedi_subdevice *s,
129
struct comedi_insn *insn, unsigned int *data);
130
static int das16cs_dio_insn_config(struct comedi_device *dev,
131
struct comedi_subdevice *s,
132
struct comedi_insn *insn,
134
static int das16cs_timer_insn_read(struct comedi_device *dev,
135
struct comedi_subdevice *s,
136
struct comedi_insn *insn,
138
static int das16cs_timer_insn_config(struct comedi_device *dev,
139
struct comedi_subdevice *s,
140
struct comedi_insn *insn,
143
static const struct das16cs_board *das16cs_probe(struct comedi_device *dev,
144
struct pcmcia_device *link)
148
for (i = 0; i < n_boards; i++) {
149
if (das16cs_boards[i].device_id == link->card_id)
150
return das16cs_boards + i;
153
printk("unknown board!\n");
158
static int das16cs_attach(struct comedi_device *dev,
159
struct comedi_devconfig *it)
161
struct pcmcia_device *link;
162
struct comedi_subdevice *s;
166
printk("comedi%d: cb_das16_cs: ", dev->minor);
168
link = cur_dev; /* XXX hack */
172
dev->iobase = link->resource[0]->start;
173
printk("I/O base=0x%04lx ", dev->iobase);
175
printk("fingerprint:\n");
176
for (i = 0; i < 48; i += 2)
177
printk("%04x ", inw(dev->iobase + i));
181
ret = request_irq(link->irq, das16cs_interrupt,
182
IRQF_SHARED, "cb_das16_cs", dev);
186
dev->irq = link->irq;
188
printk("irq=%u ", dev->irq);
190
dev->board_ptr = das16cs_probe(dev, link);
194
dev->board_name = thisboard->name;
196
if (alloc_private(dev, sizeof(struct das16cs_private)) < 0)
199
if (alloc_subdevices(dev, 4) < 0)
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;
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;
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;
222
s->range_table = &range_bipolar10;
223
s->insn_write = &das16cs_ao_winsn;
224
s->insn_read = &das16cs_ao_rinsn;
227
s = dev->subdevices + 2;
228
/* digital i/o subdevice */
230
s->type = COMEDI_SUBD_DIO;
231
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
234
s->range_table = &range_digital;
235
s->insn_bits = das16cs_dio_insn_bits;
236
s->insn_config = das16cs_dio_insn_config;
238
s->type = COMEDI_SUBD_UNUSED;
241
s = dev->subdevices + 3;
242
/* timer subdevice */
244
s->type = COMEDI_SUBD_TIMER;
245
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
248
s->range_table = &range_unknown;
249
s->insn_read = das16cs_timer_insn_read;
250
s->insn_config = das16cs_timer_insn_config;
252
s->type = COMEDI_SUBD_UNUSED;
255
printk("attached\n");
260
static int das16cs_detach(struct comedi_device *dev)
262
printk("comedi%d: das16cs: remove\n", dev->minor);
265
free_irq(dev->irq, dev);
271
static irqreturn_t das16cs_interrupt(int irq, void *d)
273
/* struct comedi_device *dev = d; */
278
* "instructions" read/write data in "one-shot" or "software-triggered"
281
static int das16cs_ai_rinsn(struct comedi_device *dev,
282
struct comedi_subdevice *s,
283
struct comedi_insn *insn, unsigned int *data)
290
static int range_bits[] = { 0x800, 0x000, 0x100, 0x200 };
292
chan = CR_CHAN(insn->chanspec);
293
aref = CR_AREF(insn->chanspec);
294
range = CR_RANGE(insn->chanspec);
296
outw(chan, dev->iobase + 2);
298
devpriv->status1 &= ~0xf320;
299
devpriv->status1 |= (aref == AREF_DIFF) ? 0 : 0x0020;
300
outw(devpriv->status1, dev->iobase + 4);
302
devpriv->status2 &= ~0xff00;
303
devpriv->status2 |= range_bits[range];
304
outw(devpriv->status2, dev->iobase + 6);
306
for (i = 0; i < insn->n; i++) {
307
outw(0, dev->iobase);
310
for (to = 0; to < TIMEOUT; to++) {
311
if (inw(dev->iobase + 4) & 0x0080)
315
printk("cb_das16_cs: ai timeout\n");
318
data[i] = (unsigned short)inw(dev->iobase + 0);
324
static int das16cs_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
329
static int das16cs_ai_cmdtest(struct comedi_device *dev,
330
struct comedi_subdevice *s,
331
struct comedi_cmd *cmd)
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.
340
* cmdtest returns 1,2,3,4 or 0, depending on which tests
341
* the command passes. */
343
/* step 1: make sure trigger sources are trivially valid */
345
tmp = cmd->start_src;
346
cmd->start_src &= TRIG_NOW;
347
if (!cmd->start_src || tmp != cmd->start_src)
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)
355
tmp = cmd->convert_src;
356
cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
357
if (!cmd->convert_src || tmp != cmd->convert_src)
360
tmp = cmd->scan_end_src;
361
cmd->scan_end_src &= TRIG_COUNT;
362
if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
366
cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
367
if (!cmd->stop_src || tmp != cmd->stop_src)
373
/* step 2: make sure trigger sources are unique and
374
* mutually compatible */
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)
380
if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
382
if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
388
/* step 3: make sure arguments are trivially compatible */
390
if (cmd->start_arg != 0) {
394
#define MAX_SPEED 10000 /* in nanoseconds */
395
#define MIN_SPEED 1000000000 /* in nanoseconds */
397
if (cmd->scan_begin_src == TRIG_TIMER) {
398
if (cmd->scan_begin_arg < MAX_SPEED) {
399
cmd->scan_begin_arg = MAX_SPEED;
402
if (cmd->scan_begin_arg > MIN_SPEED) {
403
cmd->scan_begin_arg = MIN_SPEED;
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;
415
if (cmd->convert_src == TRIG_TIMER) {
416
if (cmd->convert_arg < MAX_SPEED) {
417
cmd->convert_arg = MAX_SPEED;
420
if (cmd->convert_arg > MIN_SPEED) {
421
cmd->convert_arg = MIN_SPEED;
425
/* external trigger */
427
if (cmd->convert_arg > 9) {
428
cmd->convert_arg = 9;
433
if (cmd->scan_end_arg != cmd->chanlist_len) {
434
cmd->scan_end_arg = cmd->chanlist_len;
437
if (cmd->stop_src == TRIG_COUNT) {
438
if (cmd->stop_arg > 0x00ffffff) {
439
cmd->stop_arg = 0x00ffffff;
444
if (cmd->stop_arg != 0) {
453
/* step 4: fix up any arguments */
455
if (cmd->scan_begin_src == TRIG_TIMER) {
456
unsigned int div1 = 0, div2 = 0;
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)
465
if (cmd->convert_src == TRIG_TIMER) {
466
unsigned int div1 = 0, div2 = 0;
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)
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;
489
static int das16cs_ao_winsn(struct comedi_device *dev,
490
struct comedi_subdevice *s,
491
struct comedi_insn *insn, unsigned int *data)
494
int chan = CR_CHAN(insn->chanspec);
495
unsigned short status1;
499
for (i = 0; i < insn->n; i++) {
500
devpriv->ao_readback[chan] = data[i];
503
outw(devpriv->status1, dev->iobase + 4);
506
status1 = devpriv->status1 & ~0xf;
512
/* printk("0x%04x\n",status1);*/
513
outw(status1, dev->iobase + 4);
516
for (bit = 15; bit >= 0; bit--) {
517
int b = (d >> bit) & 0x1;
519
/* printk("0x%04x\n",status1 | b | 0x0000);*/
520
outw(status1 | b | 0x0000, dev->iobase + 4);
522
/* printk("0x%04x\n",status1 | b | 0x0004);*/
523
outw(status1 | b | 0x0004, dev->iobase + 4);
526
/* make high both DAC0CS and DAC1CS to load
527
new data and update analog output*/
528
outw(status1 | 0x9, dev->iobase + 4);
534
/* AO subdevices should have a read insn as well as a write insn.
535
* Usually this means copying a value stored in devpriv. */
536
static int das16cs_ao_rinsn(struct comedi_device *dev,
537
struct comedi_subdevice *s,
538
struct comedi_insn *insn, unsigned int *data)
541
int chan = CR_CHAN(insn->chanspec);
543
for (i = 0; i < insn->n; i++)
544
data[i] = devpriv->ao_readback[chan];
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 */
554
static int das16cs_dio_insn_bits(struct comedi_device *dev,
555
struct comedi_subdevice *s,
556
struct comedi_insn *insn, unsigned int *data)
562
s->state &= ~data[0];
563
s->state |= data[0] & data[1];
565
outw(s->state, dev->iobase + 16);
568
/* on return, data[1] contains the value of the digital
569
* input and output lines. */
570
data[1] = inw(dev->iobase + 16);
575
static int das16cs_dio_insn_config(struct comedi_device *dev,
576
struct comedi_subdevice *s,
577
struct comedi_insn *insn, unsigned int *data)
579
int chan = CR_CHAN(insn->chanspec);
588
case INSN_CONFIG_DIO_OUTPUT:
591
case INSN_CONFIG_DIO_INPUT:
594
case INSN_CONFIG_DIO_QUERY:
596
(s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
604
devpriv->status2 &= ~0x00c0;
605
devpriv->status2 |= (s->io_bits & 0xf0) ? 0x0080 : 0;
606
devpriv->status2 |= (s->io_bits & 0x0f) ? 0x0040 : 0;
608
outw(devpriv->status2, dev->iobase + 6);
613
static int das16cs_timer_insn_read(struct comedi_device *dev,
614
struct comedi_subdevice *s,
615
struct comedi_insn *insn, unsigned int *data)
620
static int das16cs_timer_insn_config(struct comedi_device *dev,
621
struct comedi_subdevice *s,
622
struct comedi_insn *insn,
630
/*======================================================================
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.
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.
639
======================================================================*/
641
#if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE)
643
static void das16cs_pcmcia_config(struct pcmcia_device *link);
644
static void das16cs_pcmcia_release(struct pcmcia_device *link);
645
static int das16cs_pcmcia_suspend(struct pcmcia_device *p_dev);
646
static int das16cs_pcmcia_resume(struct pcmcia_device *p_dev);
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.
654
static int das16cs_pcmcia_attach(struct pcmcia_device *);
655
static void das16cs_pcmcia_detach(struct pcmcia_device *);
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.
664
struct local_info_t {
665
struct pcmcia_device *link;
667
struct bus_operations *bus;
670
/*======================================================================
672
das16cs_pcmcia_attach() creates an "instance" of the driver, allocating
673
local data structures for one device. The device is registered
676
The dev_link structure is initialized, but we don't actually
677
configure the card at this point -- we wait until we receive a
678
card insertion event.
680
======================================================================*/
682
static int das16cs_pcmcia_attach(struct pcmcia_device *link)
684
struct local_info_t *local;
686
dev_dbg(&link->dev, "das16cs_pcmcia_attach()\n");
688
/* Allocate space for private device-specific data */
689
local = kzalloc(sizeof(struct local_info_t), GFP_KERNEL);
697
das16cs_pcmcia_config(link);
700
} /* das16cs_pcmcia_attach */
702
static void das16cs_pcmcia_detach(struct pcmcia_device *link)
704
dev_dbg(&link->dev, "das16cs_pcmcia_detach\n");
706
((struct local_info_t *)link->priv)->stop = 1;
707
das16cs_pcmcia_release(link);
708
/* This points to the parent struct local_info_t struct */
710
} /* das16cs_pcmcia_detach */
713
static int das16cs_pcmcia_config_loop(struct pcmcia_device *p_dev,
716
if (p_dev->config_index == 0)
719
return pcmcia_request_io(p_dev);
722
static void das16cs_pcmcia_config(struct pcmcia_device *link)
726
dev_dbg(&link->dev, "das16cs_pcmcia_config\n");
728
/* Do we need to allocate an interrupt? */
729
link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
731
ret = pcmcia_loop_config(link, das16cs_pcmcia_config_loop, NULL);
733
dev_warn(&link->dev, "no configuration found\n");
740
ret = pcmcia_enable_device(link);
747
das16cs_pcmcia_release(link);
748
} /* das16cs_pcmcia_config */
750
static void das16cs_pcmcia_release(struct pcmcia_device *link)
752
dev_dbg(&link->dev, "das16cs_pcmcia_release\n");
753
pcmcia_disable_device(link);
754
} /* das16cs_pcmcia_release */
756
static int das16cs_pcmcia_suspend(struct pcmcia_device *link)
758
struct local_info_t *local = link->priv;
760
/* Mark the device as stopped, to block IO until later */
764
} /* das16cs_pcmcia_suspend */
766
static int das16cs_pcmcia_resume(struct pcmcia_device *link)
768
struct local_info_t *local = link->priv;
772
} /* das16cs_pcmcia_resume */
774
/*====================================================================*/
776
static const struct pcmcia_device_id das16cs_id_table[] = {
777
PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0039),
778
PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4009),
782
MODULE_DEVICE_TABLE(pcmcia, das16cs_id_table);
783
MODULE_AUTHOR("David A. Schleef <ds@schleef.org>");
784
MODULE_DESCRIPTION("Comedi driver for Computer Boards PC-CARD DAS16/16");
785
MODULE_LICENSE("GPL");
787
struct pcmcia_driver das16cs_driver = {
788
.probe = das16cs_pcmcia_attach,
789
.remove = das16cs_pcmcia_detach,
790
.suspend = das16cs_pcmcia_suspend,
791
.resume = das16cs_pcmcia_resume,
792
.id_table = das16cs_id_table,
793
.owner = THIS_MODULE,
794
.name = "cb_das16_cs",
797
static int __init init_das16cs_pcmcia_cs(void)
799
pcmcia_register_driver(&das16cs_driver);
803
static void __exit exit_das16cs_pcmcia_cs(void)
805
pr_debug("das16cs_pcmcia_cs: unloading\n");
806
pcmcia_unregister_driver(&das16cs_driver);
809
int __init init_module(void)
813
ret = init_das16cs_pcmcia_cs();
817
return comedi_driver_register(&driver_das16cs);
820
void __exit cleanup_module(void)
822
exit_das16cs_pcmcia_cs();
823
comedi_driver_unregister(&driver_das16cs);
827
static int __init driver_das16cs_init_module(void)
829
return comedi_driver_register(&driver_das16cs);
832
static void __exit driver_das16cs_cleanup_module(void)
834
comedi_driver_unregister(&driver_das16cs);
837
module_init(driver_das16cs_init_module);
838
module_exit(driver_das16cs_cleanup_module);
839
#endif /* CONFIG_PCMCIA */