2 comedi/drivers/multiq3.c
3 Hardware driver for Quanser Consulting MultiQ-3 board
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
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: Quanser Consulting MultiQ-3
26 Author: Anders Blomdell <anders.blomdell@control.lth.se>
28 Devices: [Quanser Consulting] MultiQ-3 (multiq3)
32 #include "../comedidev.h"
34 #include <linux/ioport.h>
36 #define MULTIQ3_SIZE 16
39 * MULTIQ-3 port offsets
41 #define MULTIQ3_DIGIN_PORT 0
42 #define MULTIQ3_DIGOUT_PORT 0
43 #define MULTIQ3_DAC_DATA 2
44 #define MULTIQ3_AD_DATA 4
45 #define MULTIQ3_AD_CS 4
46 #define MULTIQ3_STATUS 6
47 #define MULTIQ3_CONTROL 6
48 #define MULTIQ3_CLK_DATA 8
49 #define MULTIQ3_ENC_DATA 12
50 #define MULTIQ3_ENC_CONTROL 14
53 * flags for CONTROL register
55 #define MULTIQ3_AD_MUX_EN 0x0040
56 #define MULTIQ3_AD_AUTOZ 0x0080
57 #define MULTIQ3_AD_AUTOCAL 0x0100
58 #define MULTIQ3_AD_SH 0x0200
59 #define MULTIQ3_AD_CLOCK_4M 0x0400
60 #define MULTIQ3_DA_LOAD 0x1800
62 #define MULTIQ3_CONTROL_MUST 0x0600
65 * flags for STATUS register
67 #define MULTIQ3_STATUS_EOC 0x008
68 #define MULTIQ3_STATUS_EOC_I 0x010
71 * flags for encoder control
73 #define MULTIQ3_CLOCK_DATA 0x00
74 #define MULTIQ3_CLOCK_SETUP 0x18
75 #define MULTIQ3_INPUT_SETUP 0x41
76 #define MULTIQ3_QUAD_X4 0x38
77 #define MULTIQ3_BP_RESET 0x01
78 #define MULTIQ3_CNTR_RESET 0x02
79 #define MULTIQ3_TRSFRPR_CTR 0x08
80 #define MULTIQ3_TRSFRCNTR_OL 0x10
81 #define MULTIQ3_EFLAG_RESET 0x06
83 #define MULTIQ3_TIMEOUT 30
85 static int multiq3_attach(struct comedi_device
* dev
, comedi_devconfig
* it
);
86 static int multiq3_detach(struct comedi_device
* dev
);
87 static struct comedi_driver driver_multiq3
= {
88 driver_name
:"multiq3",
90 attach
:multiq3_attach
,
91 detach
:multiq3_detach
,
94 COMEDI_INITCLEANUP(driver_multiq3
);
96 struct multiq3_private
{
97 unsigned int ao_readback
[2];
99 #define devpriv ((struct multiq3_private *)dev->private)
101 static int multiq3_ai_insn_read(struct comedi_device
* dev
, struct comedi_subdevice
* s
,
102 comedi_insn
* insn
, unsigned int * data
)
108 chan
= CR_CHAN(insn
->chanspec
);
109 outw(MULTIQ3_CONTROL_MUST
| MULTIQ3_AD_MUX_EN
| (chan
<< 3),
110 dev
->iobase
+ MULTIQ3_CONTROL
);
112 for (i
= 0; i
< MULTIQ3_TIMEOUT
; i
++) {
113 if (inw(dev
->iobase
+ MULTIQ3_STATUS
) & MULTIQ3_STATUS_EOC
)
116 if (i
== MULTIQ3_TIMEOUT
)
119 for (n
= 0; n
< insn
->n
; n
++) {
120 outw(0, dev
->iobase
+ MULTIQ3_AD_CS
);
121 for (i
= 0; i
< MULTIQ3_TIMEOUT
; i
++) {
122 if (inw(dev
->iobase
+
123 MULTIQ3_STATUS
) & MULTIQ3_STATUS_EOC_I
)
126 if (i
== MULTIQ3_TIMEOUT
)
129 hi
= inb(dev
->iobase
+ MULTIQ3_AD_CS
);
130 lo
= inb(dev
->iobase
+ MULTIQ3_AD_CS
);
131 data
[n
] = (((hi
<< 8) | lo
) + 0x1000) & 0x1fff;
137 static int multiq3_ao_insn_read(struct comedi_device
* dev
, struct comedi_subdevice
* s
,
138 comedi_insn
* insn
, unsigned int * data
)
141 int chan
= CR_CHAN(insn
->chanspec
);
143 for (i
= 0; i
< insn
->n
; i
++) {
144 data
[i
] = devpriv
->ao_readback
[chan
];
150 static int multiq3_ao_insn_write(struct comedi_device
* dev
, struct comedi_subdevice
* s
,
151 comedi_insn
* insn
, unsigned int * data
)
154 int chan
= CR_CHAN(insn
->chanspec
);
156 for (i
= 0; i
< insn
->n
; i
++) {
157 outw(MULTIQ3_CONTROL_MUST
| MULTIQ3_DA_LOAD
| chan
,
158 dev
->iobase
+ MULTIQ3_CONTROL
);
159 outw(data
[i
], dev
->iobase
+ MULTIQ3_DAC_DATA
);
160 outw(MULTIQ3_CONTROL_MUST
, dev
->iobase
+ MULTIQ3_CONTROL
);
162 devpriv
->ao_readback
[chan
] = data
[i
];
168 static int multiq3_di_insn_bits(struct comedi_device
* dev
, struct comedi_subdevice
* s
,
169 comedi_insn
* insn
, unsigned int * data
)
174 data
[1] = inw(dev
->iobase
+ MULTIQ3_DIGIN_PORT
);
179 static int multiq3_do_insn_bits(struct comedi_device
* dev
, struct comedi_subdevice
* s
,
180 comedi_insn
* insn
, unsigned int * data
)
185 s
->state
&= ~data
[0];
186 s
->state
|= (data
[0] & data
[1]);
187 outw(s
->state
, dev
->iobase
+ MULTIQ3_DIGOUT_PORT
);
194 static int multiq3_encoder_insn_read(struct comedi_device
* dev
, struct comedi_subdevice
* s
,
195 comedi_insn
* insn
, unsigned int * data
)
198 int chan
= CR_CHAN(insn
->chanspec
);
199 int control
= MULTIQ3_CONTROL_MUST
| MULTIQ3_AD_MUX_EN
| (chan
<< 3);
201 for (n
= 0; n
< insn
->n
; n
++) {
203 outw(control
, dev
->iobase
+ MULTIQ3_CONTROL
);
204 outb(MULTIQ3_BP_RESET
, dev
->iobase
+ MULTIQ3_ENC_CONTROL
);
205 outb(MULTIQ3_TRSFRCNTR_OL
, dev
->iobase
+ MULTIQ3_ENC_CONTROL
);
206 value
= inb(dev
->iobase
+ MULTIQ3_ENC_DATA
);
207 value
|= (inb(dev
->iobase
+ MULTIQ3_ENC_DATA
) << 8);
208 value
|= (inb(dev
->iobase
+ MULTIQ3_ENC_DATA
) << 16);
209 data
[n
] = (value
+ 0x800000) & 0xffffff;
215 static void encoder_reset(struct comedi_device
* dev
)
218 for (chan
= 0; chan
< dev
->subdevices
[4].n_chan
; chan
++) {
220 MULTIQ3_CONTROL_MUST
| MULTIQ3_AD_MUX_EN
| (chan
<< 3);
221 outw(control
, dev
->iobase
+ MULTIQ3_CONTROL
);
222 outb(MULTIQ3_EFLAG_RESET
, dev
->iobase
+ MULTIQ3_ENC_CONTROL
);
223 outb(MULTIQ3_BP_RESET
, dev
->iobase
+ MULTIQ3_ENC_CONTROL
);
224 outb(MULTIQ3_CLOCK_DATA
, dev
->iobase
+ MULTIQ3_ENC_DATA
);
225 outb(MULTIQ3_CLOCK_SETUP
, dev
->iobase
+ MULTIQ3_ENC_CONTROL
);
226 outb(MULTIQ3_INPUT_SETUP
, dev
->iobase
+ MULTIQ3_ENC_CONTROL
);
227 outb(MULTIQ3_QUAD_X4
, dev
->iobase
+ MULTIQ3_ENC_CONTROL
);
228 outb(MULTIQ3_CNTR_RESET
, dev
->iobase
+ MULTIQ3_ENC_CONTROL
);
233 options[0] - I/O port
235 options[2] - number of encoder chips installed
238 static int multiq3_attach(struct comedi_device
* dev
, comedi_devconfig
* it
)
241 unsigned long iobase
;
243 struct comedi_subdevice
*s
;
245 iobase
= it
->options
[0];
246 printk("comedi%d: multiq3: 0x%04lx ", dev
->minor
, iobase
);
247 if (!request_region(iobase
, MULTIQ3_SIZE
, "multiq3")) {
248 printk("comedi%d: I/O port conflict\n", dev
->minor
);
252 dev
->iobase
= iobase
;
254 irq
= it
->options
[1];
256 printk("comedi%d: irq = %u ignored\n", dev
->minor
, irq
);
258 printk("comedi%d: no irq\n", dev
->minor
);
260 dev
->board_name
= "multiq3";
261 result
= alloc_subdevices(dev
, 5);
265 result
= alloc_private(dev
, sizeof(struct multiq3_private
));
269 s
= dev
->subdevices
+ 0;
271 s
->type
= COMEDI_SUBD_AI
;
272 s
->subdev_flags
= SDF_READABLE
| SDF_GROUND
;
274 s
->insn_read
= multiq3_ai_insn_read
;
276 s
->range_table
= &range_bipolar5
;
278 s
= dev
->subdevices
+ 1;
280 s
->type
= COMEDI_SUBD_AO
;
281 s
->subdev_flags
= SDF_WRITABLE
;
283 s
->insn_read
= multiq3_ao_insn_read
;
284 s
->insn_write
= multiq3_ao_insn_write
;
286 s
->range_table
= &range_bipolar5
;
288 s
= dev
->subdevices
+ 2;
290 s
->type
= COMEDI_SUBD_DI
;
291 s
->subdev_flags
= SDF_READABLE
;
293 s
->insn_bits
= multiq3_di_insn_bits
;
295 s
->range_table
= &range_digital
;
297 s
= dev
->subdevices
+ 3;
299 s
->type
= COMEDI_SUBD_DO
;
300 s
->subdev_flags
= SDF_WRITABLE
;
302 s
->insn_bits
= multiq3_do_insn_bits
;
304 s
->range_table
= &range_digital
;
307 s
= dev
->subdevices
+ 4;
308 /* encoder (counter) subdevice */
309 s
->type
= COMEDI_SUBD_COUNTER
;
310 s
->subdev_flags
= SDF_READABLE
| SDF_LSAMPL
;
311 s
->n_chan
= it
->options
[2] * 2;
312 s
->insn_read
= multiq3_encoder_insn_read
;
313 s
->maxdata
= 0xffffff;
314 s
->range_table
= &range_unknown
;
321 static int multiq3_detach(struct comedi_device
* dev
)
323 printk("comedi%d: multiq3: remove\n", dev
->minor
);
326 release_region(dev
->iobase
, MULTIQ3_SIZE
);
329 free_irq(dev
->irq
, dev
);