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.
22 PCMCIA support code for this driver is adapted from the dummy_cs.c
23 driver of the Linux PCMCIA Card Services package.
25 The initial developer of the original code is David A. Hinds
26 <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
27 are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
32 Description: Computer Boards PC-CARD DAS16/16
33 Devices: [ComputerBoards] PC-CARD DAS16/16 (cb_das16_cs), PC-CARD DAS16/16-AO
35 Updated: Mon, 04 Nov 2002 20:04:21 -0800
41 #include <linux/interrupt.h>
42 #include <linux/slab.h>
43 #include "../comedidev.h"
44 #include <linux/delay.h>
46 #include <pcmcia/cistpl.h>
47 #include <pcmcia/ds.h>
49 #include "comedi_fc.h"
52 #define DAS16CS_SIZE 18
54 #define DAS16CS_ADC_DATA 0
55 #define DAS16CS_DIO_MUX 2
56 #define DAS16CS_MISC1 4
57 #define DAS16CS_MISC2 6
58 #define DAS16CS_CTR0 8
59 #define DAS16CS_CTR1 10
60 #define DAS16CS_CTR2 12
61 #define DAS16CS_CTR_CONTROL 14
62 #define DAS16CS_DIO 16
64 struct das16cs_board
{
70 static const struct das16cs_board das16cs_boards
[] = {
72 .name
= "PC-CARD DAS16/16-AO",
76 .name
= "PCM-DAS16s/16",
80 .name
= "PC-CARD DAS16/16",
81 .device_id
= 0x0000, /* unknown */
86 struct das16cs_private
{
87 unsigned int ao_readback
[2];
88 unsigned short status1
;
89 unsigned short status2
;
92 static struct pcmcia_device
*cur_dev
;
94 static const struct comedi_lrange das16cs_ai_range
= {
103 static irqreturn_t
das16cs_interrupt(int irq
, void *d
)
105 /* struct comedi_device *dev = d; */
109 static int das16cs_ai_rinsn(struct comedi_device
*dev
,
110 struct comedi_subdevice
*s
,
111 struct comedi_insn
*insn
, unsigned int *data
)
113 struct das16cs_private
*devpriv
= dev
->private;
114 int chan
= CR_CHAN(insn
->chanspec
);
115 int range
= CR_RANGE(insn
->chanspec
);
116 int aref
= CR_AREF(insn
->chanspec
);
120 outw(chan
, dev
->iobase
+ DAS16CS_DIO_MUX
);
122 devpriv
->status1
&= ~0xf320;
123 devpriv
->status1
|= (aref
== AREF_DIFF
) ? 0 : 0x0020;
124 outw(devpriv
->status1
, dev
->iobase
+ DAS16CS_MISC1
);
126 devpriv
->status2
&= ~0xff00;
129 devpriv
->status2
|= 0x800;
132 devpriv
->status2
|= 0x000;
135 devpriv
->status2
|= 0x100;
138 devpriv
->status2
|= 0x200;
141 outw(devpriv
->status2
, dev
->iobase
+ DAS16CS_MISC2
);
143 for (i
= 0; i
< insn
->n
; i
++) {
144 outw(0, dev
->iobase
+ DAS16CS_ADC_DATA
);
147 for (to
= 0; to
< TIMEOUT
; to
++) {
148 if (inw(dev
->iobase
+ DAS16CS_MISC1
) & 0x0080)
152 dev_dbg(dev
->class_dev
, "cb_das16_cs: ai timeout\n");
155 data
[i
] = inw(dev
->iobase
+ DAS16CS_ADC_DATA
);
161 static int das16cs_ai_cmd(struct comedi_device
*dev
, struct comedi_subdevice
*s
)
166 static int das16cs_ai_cmdtest(struct comedi_device
*dev
,
167 struct comedi_subdevice
*s
,
168 struct comedi_cmd
*cmd
)
173 /* Step 1 : check if triggers are trivially valid */
175 err
|= cfc_check_trigger_src(&cmd
->start_src
, TRIG_NOW
);
176 err
|= cfc_check_trigger_src(&cmd
->scan_begin_src
,
177 TRIG_TIMER
| TRIG_EXT
);
178 err
|= cfc_check_trigger_src(&cmd
->convert_src
,
179 TRIG_TIMER
| TRIG_EXT
);
180 err
|= cfc_check_trigger_src(&cmd
->scan_end_src
, TRIG_COUNT
);
181 err
|= cfc_check_trigger_src(&cmd
->stop_src
, TRIG_COUNT
| TRIG_NONE
);
186 /* Step 2a : make sure trigger sources are unique */
188 err
|= cfc_check_trigger_is_unique(cmd
->scan_begin_src
);
189 err
|= cfc_check_trigger_is_unique(cmd
->convert_src
);
190 err
|= cfc_check_trigger_is_unique(cmd
->stop_src
);
192 /* Step 2b : and mutually compatible */
197 /* step 3: make sure arguments are trivially compatible */
199 if (cmd
->start_arg
!= 0) {
203 #define MAX_SPEED 10000 /* in nanoseconds */
204 #define MIN_SPEED 1000000000 /* in nanoseconds */
206 if (cmd
->scan_begin_src
== TRIG_TIMER
) {
207 if (cmd
->scan_begin_arg
< MAX_SPEED
) {
208 cmd
->scan_begin_arg
= MAX_SPEED
;
211 if (cmd
->scan_begin_arg
> MIN_SPEED
) {
212 cmd
->scan_begin_arg
= MIN_SPEED
;
216 /* external trigger */
217 /* should be level/edge, hi/lo specification here */
218 /* should specify multiple external triggers */
219 if (cmd
->scan_begin_arg
> 9) {
220 cmd
->scan_begin_arg
= 9;
224 if (cmd
->convert_src
== TRIG_TIMER
) {
225 if (cmd
->convert_arg
< MAX_SPEED
) {
226 cmd
->convert_arg
= MAX_SPEED
;
229 if (cmd
->convert_arg
> MIN_SPEED
) {
230 cmd
->convert_arg
= MIN_SPEED
;
234 /* external trigger */
236 if (cmd
->convert_arg
> 9) {
237 cmd
->convert_arg
= 9;
242 if (cmd
->scan_end_arg
!= cmd
->chanlist_len
) {
243 cmd
->scan_end_arg
= cmd
->chanlist_len
;
246 if (cmd
->stop_src
== TRIG_COUNT
) {
247 if (cmd
->stop_arg
> 0x00ffffff) {
248 cmd
->stop_arg
= 0x00ffffff;
253 if (cmd
->stop_arg
!= 0) {
262 /* step 4: fix up any arguments */
264 if (cmd
->scan_begin_src
== TRIG_TIMER
) {
265 unsigned int div1
= 0, div2
= 0;
267 tmp
= cmd
->scan_begin_arg
;
268 i8253_cascade_ns_to_timer(100, &div1
, &div2
,
269 &cmd
->scan_begin_arg
,
270 cmd
->flags
& TRIG_ROUND_MASK
);
271 if (tmp
!= cmd
->scan_begin_arg
)
274 if (cmd
->convert_src
== TRIG_TIMER
) {
275 unsigned int div1
= 0, div2
= 0;
277 tmp
= cmd
->convert_arg
;
278 i8253_cascade_ns_to_timer(100, &div1
, &div2
,
279 &cmd
->scan_begin_arg
,
280 cmd
->flags
& TRIG_ROUND_MASK
);
281 if (tmp
!= cmd
->convert_arg
)
283 if (cmd
->scan_begin_src
== TRIG_TIMER
&&
284 cmd
->scan_begin_arg
<
285 cmd
->convert_arg
* cmd
->scan_end_arg
) {
286 cmd
->scan_begin_arg
=
287 cmd
->convert_arg
* cmd
->scan_end_arg
;
298 static int das16cs_ao_winsn(struct comedi_device
*dev
,
299 struct comedi_subdevice
*s
,
300 struct comedi_insn
*insn
, unsigned int *data
)
302 struct das16cs_private
*devpriv
= dev
->private;
304 int chan
= CR_CHAN(insn
->chanspec
);
305 unsigned short status1
;
309 for (i
= 0; i
< insn
->n
; i
++) {
310 devpriv
->ao_readback
[chan
] = data
[i
];
313 outw(devpriv
->status1
, dev
->iobase
+ DAS16CS_MISC1
);
316 status1
= devpriv
->status1
& ~0xf;
322 outw(status1
, dev
->iobase
+ DAS16CS_MISC1
);
325 for (bit
= 15; bit
>= 0; bit
--) {
326 int b
= (d
>> bit
) & 0x1;
328 outw(status1
| b
| 0x0000, dev
->iobase
+ DAS16CS_MISC1
);
330 outw(status1
| b
| 0x0004, dev
->iobase
+ DAS16CS_MISC1
);
334 * Make both DAC0CS and DAC1CS high to load
335 * the new data and update analog the output
337 outw(status1
| 0x9, dev
->iobase
+ DAS16CS_MISC1
);
343 static int das16cs_ao_rinsn(struct comedi_device
*dev
,
344 struct comedi_subdevice
*s
,
345 struct comedi_insn
*insn
, unsigned int *data
)
347 struct das16cs_private
*devpriv
= dev
->private;
349 int chan
= CR_CHAN(insn
->chanspec
);
351 for (i
= 0; i
< insn
->n
; i
++)
352 data
[i
] = devpriv
->ao_readback
[chan
];
357 static int das16cs_dio_insn_bits(struct comedi_device
*dev
,
358 struct comedi_subdevice
*s
,
359 struct comedi_insn
*insn
, unsigned int *data
)
362 s
->state
&= ~data
[0];
363 s
->state
|= data
[0] & data
[1];
365 outw(s
->state
, dev
->iobase
+ DAS16CS_DIO
);
368 data
[1] = inw(dev
->iobase
+ DAS16CS_DIO
);
373 static int das16cs_dio_insn_config(struct comedi_device
*dev
,
374 struct comedi_subdevice
*s
,
375 struct comedi_insn
*insn
, unsigned int *data
)
377 struct das16cs_private
*devpriv
= dev
->private;
378 int chan
= CR_CHAN(insn
->chanspec
);
387 case INSN_CONFIG_DIO_OUTPUT
:
390 case INSN_CONFIG_DIO_INPUT
:
393 case INSN_CONFIG_DIO_QUERY
:
395 (s
->io_bits
& (1 << chan
)) ? COMEDI_OUTPUT
: COMEDI_INPUT
;
403 devpriv
->status2
&= ~0x00c0;
404 devpriv
->status2
|= (s
->io_bits
& 0xf0) ? 0x0080 : 0;
405 devpriv
->status2
|= (s
->io_bits
& 0x0f) ? 0x0040 : 0;
407 outw(devpriv
->status2
, dev
->iobase
+ DAS16CS_MISC2
);
412 static const struct das16cs_board
*das16cs_probe(struct comedi_device
*dev
,
413 struct pcmcia_device
*link
)
417 for (i
= 0; i
< ARRAY_SIZE(das16cs_boards
); i
++) {
418 if (das16cs_boards
[i
].device_id
== link
->card_id
)
419 return das16cs_boards
+ i
;
422 dev_dbg(dev
->class_dev
, "unknown board!\n");
427 static int das16cs_attach(struct comedi_device
*dev
,
428 struct comedi_devconfig
*it
)
430 const struct das16cs_board
*thisboard
;
431 struct pcmcia_device
*link
;
432 struct comedi_subdevice
*s
;
435 link
= cur_dev
; /* XXX hack */
439 dev
->board_ptr
= das16cs_probe(dev
, link
);
442 thisboard
= comedi_board(dev
);
444 dev
->board_name
= thisboard
->name
;
446 dev
->iobase
= link
->resource
[0]->start
;
448 ret
= request_irq(link
->irq
, das16cs_interrupt
,
449 IRQF_SHARED
, "cb_das16_cs", dev
);
452 dev
->irq
= link
->irq
;
454 if (alloc_private(dev
, sizeof(struct das16cs_private
)) < 0)
457 ret
= comedi_alloc_subdevices(dev
, 3);
461 s
= &dev
->subdevices
[0];
462 dev
->read_subdev
= s
;
463 /* analog input subdevice */
464 s
->type
= COMEDI_SUBD_AI
;
465 s
->subdev_flags
= SDF_READABLE
| SDF_GROUND
| SDF_DIFF
| SDF_CMD_READ
;
468 s
->range_table
= &das16cs_ai_range
;
469 s
->len_chanlist
= 16;
470 s
->insn_read
= das16cs_ai_rinsn
;
471 s
->do_cmd
= das16cs_ai_cmd
;
472 s
->do_cmdtest
= das16cs_ai_cmdtest
;
474 s
= &dev
->subdevices
[1];
475 /* analog output subdevice */
476 if (thisboard
->n_ao_chans
) {
477 s
->type
= COMEDI_SUBD_AO
;
478 s
->subdev_flags
= SDF_WRITABLE
;
479 s
->n_chan
= thisboard
->n_ao_chans
;
481 s
->range_table
= &range_bipolar10
;
482 s
->insn_write
= &das16cs_ao_winsn
;
483 s
->insn_read
= &das16cs_ao_rinsn
;
485 s
->type
= COMEDI_SUBD_UNUSED
;
488 s
= &dev
->subdevices
[2];
489 /* digital i/o subdevice */
490 s
->type
= COMEDI_SUBD_DIO
;
491 s
->subdev_flags
= SDF_READABLE
| SDF_WRITABLE
;
494 s
->range_table
= &range_digital
;
495 s
->insn_bits
= das16cs_dio_insn_bits
;
496 s
->insn_config
= das16cs_dio_insn_config
;
498 dev_info(dev
->class_dev
, "%s: %s, I/O base=0x%04lx, irq=%u\n",
499 dev
->driver
->driver_name
, dev
->board_name
,
500 dev
->iobase
, dev
->irq
);
505 static void das16cs_detach(struct comedi_device
*dev
)
508 free_irq(dev
->irq
, dev
);
511 static struct comedi_driver driver_das16cs
= {
512 .driver_name
= "cb_das16_cs",
513 .module
= THIS_MODULE
,
514 .attach
= das16cs_attach
,
515 .detach
= das16cs_detach
,
518 static int das16cs_pcmcia_config_loop(struct pcmcia_device
*p_dev
,
521 if (p_dev
->config_index
== 0)
524 return pcmcia_request_io(p_dev
);
527 static int das16cs_pcmcia_attach(struct pcmcia_device
*link
)
531 /* Do we need to allocate an interrupt? */
532 link
->config_flags
|= CONF_ENABLE_IRQ
| CONF_AUTO_SET_IO
;
534 ret
= pcmcia_loop_config(link
, das16cs_pcmcia_config_loop
, NULL
);
541 ret
= pcmcia_enable_device(link
);
549 pcmcia_disable_device(link
);
553 static void das16cs_pcmcia_detach(struct pcmcia_device
*link
)
555 pcmcia_disable_device(link
);
559 static const struct pcmcia_device_id das16cs_id_table
[] = {
560 PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0039),
561 PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4009),
564 MODULE_DEVICE_TABLE(pcmcia
, das16cs_id_table
);
566 static struct pcmcia_driver das16cs_driver
= {
567 .name
= "cb_das16_cs",
568 .owner
= THIS_MODULE
,
569 .probe
= das16cs_pcmcia_attach
,
570 .remove
= das16cs_pcmcia_detach
,
571 .id_table
= das16cs_id_table
,
574 static int __init
das16cs_init(void)
578 ret
= comedi_driver_register(&driver_das16cs
);
582 ret
= pcmcia_register_driver(&das16cs_driver
);
584 comedi_driver_unregister(&driver_das16cs
);
590 module_init(das16cs_init
);
592 static void __exit
das16cs_exit(void)
594 pcmcia_unregister_driver(&das16cs_driver
);
595 comedi_driver_unregister(&driver_das16cs
);
597 module_exit(das16cs_exit
);
599 MODULE_AUTHOR("David A. Schleef <ds@schleef.org>");
600 MODULE_DESCRIPTION("Comedi driver for Computer Boards PC-CARD DAS16/16");
601 MODULE_LICENSE("GPL");