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 <linux/interrupt.h>
33 #include "../comedidev.h"
35 #include <linux/ioport.h>
37 #define MULTIQ3_SIZE 16
40 * MULTIQ-3 port offsets
42 #define MULTIQ3_DIGIN_PORT 0
43 #define MULTIQ3_DIGOUT_PORT 0
44 #define MULTIQ3_DAC_DATA 2
45 #define MULTIQ3_AD_DATA 4
46 #define MULTIQ3_AD_CS 4
47 #define MULTIQ3_STATUS 6
48 #define MULTIQ3_CONTROL 6
49 #define MULTIQ3_CLK_DATA 8
50 #define MULTIQ3_ENC_DATA 12
51 #define MULTIQ3_ENC_CONTROL 14
54 * flags for CONTROL register
56 #define MULTIQ3_AD_MUX_EN 0x0040
57 #define MULTIQ3_AD_AUTOZ 0x0080
58 #define MULTIQ3_AD_AUTOCAL 0x0100
59 #define MULTIQ3_AD_SH 0x0200
60 #define MULTIQ3_AD_CLOCK_4M 0x0400
61 #define MULTIQ3_DA_LOAD 0x1800
63 #define MULTIQ3_CONTROL_MUST 0x0600
66 * flags for STATUS register
68 #define MULTIQ3_STATUS_EOC 0x008
69 #define MULTIQ3_STATUS_EOC_I 0x010
72 * flags for encoder control
74 #define MULTIQ3_CLOCK_DATA 0x00
75 #define MULTIQ3_CLOCK_SETUP 0x18
76 #define MULTIQ3_INPUT_SETUP 0x41
77 #define MULTIQ3_QUAD_X4 0x38
78 #define MULTIQ3_BP_RESET 0x01
79 #define MULTIQ3_CNTR_RESET 0x02
80 #define MULTIQ3_TRSFRPR_CTR 0x08
81 #define MULTIQ3_TRSFRCNTR_OL 0x10
82 #define MULTIQ3_EFLAG_RESET 0x06
84 #define MULTIQ3_TIMEOUT 30
86 static int multiq3_attach(struct comedi_device
*dev
,
87 struct comedi_devconfig
*it
);
88 static int multiq3_detach(struct comedi_device
*dev
);
89 static struct comedi_driver driver_multiq3
= {
90 .driver_name
= "multiq3",
91 .module
= THIS_MODULE
,
92 .attach
= multiq3_attach
,
93 .detach
= multiq3_detach
,
96 static int __init
driver_multiq3_init_module(void)
98 return comedi_driver_register(&driver_multiq3
);
101 static void __exit
driver_multiq3_cleanup_module(void)
103 comedi_driver_unregister(&driver_multiq3
);
106 module_init(driver_multiq3_init_module
);
107 module_exit(driver_multiq3_cleanup_module
);
109 struct multiq3_private
{
110 unsigned int ao_readback
[2];
112 #define devpriv ((struct multiq3_private *)dev->private)
114 static int multiq3_ai_insn_read(struct comedi_device
*dev
,
115 struct comedi_subdevice
*s
,
116 struct comedi_insn
*insn
, unsigned int *data
)
122 chan
= CR_CHAN(insn
->chanspec
);
123 outw(MULTIQ3_CONTROL_MUST
| MULTIQ3_AD_MUX_EN
| (chan
<< 3),
124 dev
->iobase
+ MULTIQ3_CONTROL
);
126 for (i
= 0; i
< MULTIQ3_TIMEOUT
; i
++) {
127 if (inw(dev
->iobase
+ MULTIQ3_STATUS
) & MULTIQ3_STATUS_EOC
)
130 if (i
== MULTIQ3_TIMEOUT
)
133 for (n
= 0; n
< insn
->n
; n
++) {
134 outw(0, dev
->iobase
+ MULTIQ3_AD_CS
);
135 for (i
= 0; i
< MULTIQ3_TIMEOUT
; i
++) {
136 if (inw(dev
->iobase
+
137 MULTIQ3_STATUS
) & MULTIQ3_STATUS_EOC_I
)
140 if (i
== MULTIQ3_TIMEOUT
)
143 hi
= inb(dev
->iobase
+ MULTIQ3_AD_CS
);
144 lo
= inb(dev
->iobase
+ MULTIQ3_AD_CS
);
145 data
[n
] = (((hi
<< 8) | lo
) + 0x1000) & 0x1fff;
151 static int multiq3_ao_insn_read(struct comedi_device
*dev
,
152 struct comedi_subdevice
*s
,
153 struct comedi_insn
*insn
, unsigned int *data
)
156 int chan
= CR_CHAN(insn
->chanspec
);
158 for (i
= 0; i
< insn
->n
; i
++)
159 data
[i
] = devpriv
->ao_readback
[chan
];
164 static int multiq3_ao_insn_write(struct comedi_device
*dev
,
165 struct comedi_subdevice
*s
,
166 struct comedi_insn
*insn
, unsigned int *data
)
169 int chan
= CR_CHAN(insn
->chanspec
);
171 for (i
= 0; i
< insn
->n
; i
++) {
172 outw(MULTIQ3_CONTROL_MUST
| MULTIQ3_DA_LOAD
| chan
,
173 dev
->iobase
+ MULTIQ3_CONTROL
);
174 outw(data
[i
], dev
->iobase
+ MULTIQ3_DAC_DATA
);
175 outw(MULTIQ3_CONTROL_MUST
, dev
->iobase
+ MULTIQ3_CONTROL
);
177 devpriv
->ao_readback
[chan
] = data
[i
];
183 static int multiq3_di_insn_bits(struct comedi_device
*dev
,
184 struct comedi_subdevice
*s
,
185 struct comedi_insn
*insn
, unsigned int *data
)
190 data
[1] = inw(dev
->iobase
+ MULTIQ3_DIGIN_PORT
);
195 static int multiq3_do_insn_bits(struct comedi_device
*dev
,
196 struct comedi_subdevice
*s
,
197 struct comedi_insn
*insn
, unsigned int *data
)
202 s
->state
&= ~data
[0];
203 s
->state
|= (data
[0] & data
[1]);
204 outw(s
->state
, dev
->iobase
+ MULTIQ3_DIGOUT_PORT
);
211 static int multiq3_encoder_insn_read(struct comedi_device
*dev
,
212 struct comedi_subdevice
*s
,
213 struct comedi_insn
*insn
,
217 int chan
= CR_CHAN(insn
->chanspec
);
218 int control
= MULTIQ3_CONTROL_MUST
| MULTIQ3_AD_MUX_EN
| (chan
<< 3);
220 for (n
= 0; n
< insn
->n
; n
++) {
222 outw(control
, dev
->iobase
+ MULTIQ3_CONTROL
);
223 outb(MULTIQ3_BP_RESET
, dev
->iobase
+ MULTIQ3_ENC_CONTROL
);
224 outb(MULTIQ3_TRSFRCNTR_OL
, dev
->iobase
+ MULTIQ3_ENC_CONTROL
);
225 value
= inb(dev
->iobase
+ MULTIQ3_ENC_DATA
);
226 value
|= (inb(dev
->iobase
+ MULTIQ3_ENC_DATA
) << 8);
227 value
|= (inb(dev
->iobase
+ MULTIQ3_ENC_DATA
) << 16);
228 data
[n
] = (value
+ 0x800000) & 0xffffff;
234 static void encoder_reset(struct comedi_device
*dev
)
237 for (chan
= 0; chan
< dev
->subdevices
[4].n_chan
; chan
++) {
239 MULTIQ3_CONTROL_MUST
| MULTIQ3_AD_MUX_EN
| (chan
<< 3);
240 outw(control
, dev
->iobase
+ MULTIQ3_CONTROL
);
241 outb(MULTIQ3_EFLAG_RESET
, dev
->iobase
+ MULTIQ3_ENC_CONTROL
);
242 outb(MULTIQ3_BP_RESET
, dev
->iobase
+ MULTIQ3_ENC_CONTROL
);
243 outb(MULTIQ3_CLOCK_DATA
, dev
->iobase
+ MULTIQ3_ENC_DATA
);
244 outb(MULTIQ3_CLOCK_SETUP
, dev
->iobase
+ MULTIQ3_ENC_CONTROL
);
245 outb(MULTIQ3_INPUT_SETUP
, dev
->iobase
+ MULTIQ3_ENC_CONTROL
);
246 outb(MULTIQ3_QUAD_X4
, dev
->iobase
+ MULTIQ3_ENC_CONTROL
);
247 outb(MULTIQ3_CNTR_RESET
, dev
->iobase
+ MULTIQ3_ENC_CONTROL
);
252 options[0] - I/O port
254 options[2] - number of encoder chips installed
257 static int multiq3_attach(struct comedi_device
*dev
,
258 struct comedi_devconfig
*it
)
261 unsigned long iobase
;
263 struct comedi_subdevice
*s
;
265 iobase
= it
->options
[0];
266 printk(KERN_INFO
"comedi%d: multiq3: 0x%04lx ", dev
->minor
, iobase
);
267 if (!request_region(iobase
, MULTIQ3_SIZE
, "multiq3")) {
268 printk(KERN_ERR
"comedi%d: I/O port conflict\n", dev
->minor
);
272 dev
->iobase
= iobase
;
274 irq
= it
->options
[1];
276 printk(KERN_WARNING
"comedi%d: irq = %u ignored\n",
279 printk(KERN_WARNING
"comedi%d: no irq\n", dev
->minor
);
280 dev
->board_name
= "multiq3";
281 result
= alloc_subdevices(dev
, 5);
285 result
= alloc_private(dev
, sizeof(struct multiq3_private
));
289 s
= dev
->subdevices
+ 0;
291 s
->type
= COMEDI_SUBD_AI
;
292 s
->subdev_flags
= SDF_READABLE
| SDF_GROUND
;
294 s
->insn_read
= multiq3_ai_insn_read
;
296 s
->range_table
= &range_bipolar5
;
298 s
= dev
->subdevices
+ 1;
300 s
->type
= COMEDI_SUBD_AO
;
301 s
->subdev_flags
= SDF_WRITABLE
;
303 s
->insn_read
= multiq3_ao_insn_read
;
304 s
->insn_write
= multiq3_ao_insn_write
;
306 s
->range_table
= &range_bipolar5
;
308 s
= dev
->subdevices
+ 2;
310 s
->type
= COMEDI_SUBD_DI
;
311 s
->subdev_flags
= SDF_READABLE
;
313 s
->insn_bits
= multiq3_di_insn_bits
;
315 s
->range_table
= &range_digital
;
317 s
= dev
->subdevices
+ 3;
319 s
->type
= COMEDI_SUBD_DO
;
320 s
->subdev_flags
= SDF_WRITABLE
;
322 s
->insn_bits
= multiq3_do_insn_bits
;
324 s
->range_table
= &range_digital
;
327 s
= dev
->subdevices
+ 4;
328 /* encoder (counter) subdevice */
329 s
->type
= COMEDI_SUBD_COUNTER
;
330 s
->subdev_flags
= SDF_READABLE
| SDF_LSAMPL
;
331 s
->n_chan
= it
->options
[2] * 2;
332 s
->insn_read
= multiq3_encoder_insn_read
;
333 s
->maxdata
= 0xffffff;
334 s
->range_table
= &range_unknown
;
341 static int multiq3_detach(struct comedi_device
*dev
)
343 printk(KERN_INFO
"comedi%d: multiq3: remove\n", dev
->minor
);
346 release_region(dev
->iobase
, MULTIQ3_SIZE
);
348 free_irq(dev
->irq
, dev
);
353 MODULE_AUTHOR("Comedi http://www.comedi.org");
354 MODULE_DESCRIPTION("Comedi low-level driver");
355 MODULE_LICENSE("GPL");