]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/comedi/drivers/multiq3.c
Merge git://git.infradead.org/users/eparis/selinux
[karo-tx-linux.git] / drivers / staging / comedi / drivers / multiq3.c
1 /*
2    comedi/drivers/multiq3.c
3    Hardware driver for Quanser Consulting MultiQ-3 board
4
5    COMEDI - Linux Control and Measurement Device Interface
6    Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
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 /*
19 Driver: multiq3
20 Description: Quanser Consulting MultiQ-3
21 Author: Anders Blomdell <anders.blomdell@control.lth.se>
22 Status: works
23 Devices: [Quanser Consulting] MultiQ-3 (multiq3)
24
25 */
26
27 #include <linux/interrupt.h>
28 #include "../comedidev.h"
29
30 #include <linux/ioport.h>
31
32 #define MULTIQ3_SIZE 16
33
34 /*
35  * MULTIQ-3 port offsets
36  */
37 #define MULTIQ3_DIGIN_PORT 0
38 #define MULTIQ3_DIGOUT_PORT 0
39 #define MULTIQ3_DAC_DATA 2
40 #define MULTIQ3_AD_DATA 4
41 #define MULTIQ3_AD_CS 4
42 #define MULTIQ3_STATUS 6
43 #define MULTIQ3_CONTROL 6
44 #define MULTIQ3_CLK_DATA 8
45 #define MULTIQ3_ENC_DATA 12
46 #define MULTIQ3_ENC_CONTROL 14
47
48 /*
49  * flags for CONTROL register
50  */
51 #define MULTIQ3_AD_MUX_EN      0x0040
52 #define MULTIQ3_AD_AUTOZ       0x0080
53 #define MULTIQ3_AD_AUTOCAL     0x0100
54 #define MULTIQ3_AD_SH          0x0200
55 #define MULTIQ3_AD_CLOCK_4M    0x0400
56 #define MULTIQ3_DA_LOAD                0x1800
57
58 #define MULTIQ3_CONTROL_MUST    0x0600
59
60 /*
61  * flags for STATUS register
62  */
63 #define MULTIQ3_STATUS_EOC      0x008
64 #define MULTIQ3_STATUS_EOC_I    0x010
65
66 /*
67  * flags for encoder control
68  */
69 #define MULTIQ3_CLOCK_DATA      0x00
70 #define MULTIQ3_CLOCK_SETUP     0x18
71 #define MULTIQ3_INPUT_SETUP     0x41
72 #define MULTIQ3_QUAD_X4         0x38
73 #define MULTIQ3_BP_RESET        0x01
74 #define MULTIQ3_CNTR_RESET      0x02
75 #define MULTIQ3_TRSFRPR_CTR     0x08
76 #define MULTIQ3_TRSFRCNTR_OL    0x10
77 #define MULTIQ3_EFLAG_RESET     0x06
78
79 #define MULTIQ3_TIMEOUT 30
80
81 struct multiq3_private {
82         unsigned int ao_readback[2];
83 };
84
85 static int multiq3_ai_insn_read(struct comedi_device *dev,
86                                 struct comedi_subdevice *s,
87                                 struct comedi_insn *insn, unsigned int *data)
88 {
89         int i, n;
90         int chan;
91         unsigned int hi, lo;
92
93         chan = CR_CHAN(insn->chanspec);
94         outw(MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan << 3),
95              dev->iobase + MULTIQ3_CONTROL);
96
97         for (i = 0; i < MULTIQ3_TIMEOUT; i++) {
98                 if (inw(dev->iobase + MULTIQ3_STATUS) & MULTIQ3_STATUS_EOC)
99                         break;
100         }
101         if (i == MULTIQ3_TIMEOUT)
102                 return -ETIMEDOUT;
103
104         for (n = 0; n < insn->n; n++) {
105                 outw(0, dev->iobase + MULTIQ3_AD_CS);
106                 for (i = 0; i < MULTIQ3_TIMEOUT; i++) {
107                         if (inw(dev->iobase +
108                                 MULTIQ3_STATUS) & MULTIQ3_STATUS_EOC_I)
109                                 break;
110                 }
111                 if (i == MULTIQ3_TIMEOUT)
112                         return -ETIMEDOUT;
113
114                 hi = inb(dev->iobase + MULTIQ3_AD_CS);
115                 lo = inb(dev->iobase + MULTIQ3_AD_CS);
116                 data[n] = (((hi << 8) | lo) + 0x1000) & 0x1fff;
117         }
118
119         return n;
120 }
121
122 static int multiq3_ao_insn_read(struct comedi_device *dev,
123                                 struct comedi_subdevice *s,
124                                 struct comedi_insn *insn, unsigned int *data)
125 {
126         struct multiq3_private *devpriv = dev->private;
127         int i;
128         int chan = CR_CHAN(insn->chanspec);
129
130         for (i = 0; i < insn->n; i++)
131                 data[i] = devpriv->ao_readback[chan];
132
133         return i;
134 }
135
136 static int multiq3_ao_insn_write(struct comedi_device *dev,
137                                  struct comedi_subdevice *s,
138                                  struct comedi_insn *insn, unsigned int *data)
139 {
140         struct multiq3_private *devpriv = dev->private;
141         int i;
142         int chan = CR_CHAN(insn->chanspec);
143
144         for (i = 0; i < insn->n; i++) {
145                 outw(MULTIQ3_CONTROL_MUST | MULTIQ3_DA_LOAD | chan,
146                      dev->iobase + MULTIQ3_CONTROL);
147                 outw(data[i], dev->iobase + MULTIQ3_DAC_DATA);
148                 outw(MULTIQ3_CONTROL_MUST, dev->iobase + MULTIQ3_CONTROL);
149
150                 devpriv->ao_readback[chan] = data[i];
151         }
152
153         return i;
154 }
155
156 static int multiq3_di_insn_bits(struct comedi_device *dev,
157                                 struct comedi_subdevice *s,
158                                 struct comedi_insn *insn, unsigned int *data)
159 {
160         data[1] = inw(dev->iobase + MULTIQ3_DIGIN_PORT);
161
162         return insn->n;
163 }
164
165 static int multiq3_do_insn_bits(struct comedi_device *dev,
166                                 struct comedi_subdevice *s,
167                                 struct comedi_insn *insn, unsigned int *data)
168 {
169         s->state &= ~data[0];
170         s->state |= (data[0] & data[1]);
171         outw(s->state, dev->iobase + MULTIQ3_DIGOUT_PORT);
172
173         data[1] = s->state;
174
175         return insn->n;
176 }
177
178 static int multiq3_encoder_insn_read(struct comedi_device *dev,
179                                      struct comedi_subdevice *s,
180                                      struct comedi_insn *insn,
181                                      unsigned int *data)
182 {
183         int n;
184         int chan = CR_CHAN(insn->chanspec);
185         int control = MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan << 3);
186
187         for (n = 0; n < insn->n; n++) {
188                 int value;
189                 outw(control, dev->iobase + MULTIQ3_CONTROL);
190                 outb(MULTIQ3_BP_RESET, dev->iobase + MULTIQ3_ENC_CONTROL);
191                 outb(MULTIQ3_TRSFRCNTR_OL, dev->iobase + MULTIQ3_ENC_CONTROL);
192                 value = inb(dev->iobase + MULTIQ3_ENC_DATA);
193                 value |= (inb(dev->iobase + MULTIQ3_ENC_DATA) << 8);
194                 value |= (inb(dev->iobase + MULTIQ3_ENC_DATA) << 16);
195                 data[n] = (value + 0x800000) & 0xffffff;
196         }
197
198         return n;
199 }
200
201 static void encoder_reset(struct comedi_device *dev)
202 {
203         struct comedi_subdevice *s = &dev->subdevices[4];
204         int chan;
205
206         for (chan = 0; chan < s->n_chan; chan++) {
207                 int control =
208                     MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan << 3);
209                 outw(control, dev->iobase + MULTIQ3_CONTROL);
210                 outb(MULTIQ3_EFLAG_RESET, dev->iobase + MULTIQ3_ENC_CONTROL);
211                 outb(MULTIQ3_BP_RESET, dev->iobase + MULTIQ3_ENC_CONTROL);
212                 outb(MULTIQ3_CLOCK_DATA, dev->iobase + MULTIQ3_ENC_DATA);
213                 outb(MULTIQ3_CLOCK_SETUP, dev->iobase + MULTIQ3_ENC_CONTROL);
214                 outb(MULTIQ3_INPUT_SETUP, dev->iobase + MULTIQ3_ENC_CONTROL);
215                 outb(MULTIQ3_QUAD_X4, dev->iobase + MULTIQ3_ENC_CONTROL);
216                 outb(MULTIQ3_CNTR_RESET, dev->iobase + MULTIQ3_ENC_CONTROL);
217         }
218 }
219
220 static int multiq3_attach(struct comedi_device *dev,
221                           struct comedi_devconfig *it)
222 {
223         struct multiq3_private *devpriv;
224         struct comedi_subdevice *s;
225         int ret;
226
227         ret = comedi_request_region(dev, it->options[0], MULTIQ3_SIZE);
228         if (ret)
229                 return ret;
230
231         ret = comedi_alloc_subdevices(dev, 5);
232         if (ret)
233                 return ret;
234
235         devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
236         if (!devpriv)
237                 return -ENOMEM;
238         dev->private = devpriv;
239
240         s = &dev->subdevices[0];
241         /* ai subdevice */
242         s->type = COMEDI_SUBD_AI;
243         s->subdev_flags = SDF_READABLE | SDF_GROUND;
244         s->n_chan = 8;
245         s->insn_read = multiq3_ai_insn_read;
246         s->maxdata = 0x1fff;
247         s->range_table = &range_bipolar5;
248
249         s = &dev->subdevices[1];
250         /* ao subdevice */
251         s->type = COMEDI_SUBD_AO;
252         s->subdev_flags = SDF_WRITABLE;
253         s->n_chan = 8;
254         s->insn_read = multiq3_ao_insn_read;
255         s->insn_write = multiq3_ao_insn_write;
256         s->maxdata = 0xfff;
257         s->range_table = &range_bipolar5;
258
259         s = &dev->subdevices[2];
260         /* di subdevice */
261         s->type = COMEDI_SUBD_DI;
262         s->subdev_flags = SDF_READABLE;
263         s->n_chan = 16;
264         s->insn_bits = multiq3_di_insn_bits;
265         s->maxdata = 1;
266         s->range_table = &range_digital;
267
268         s = &dev->subdevices[3];
269         /* do subdevice */
270         s->type = COMEDI_SUBD_DO;
271         s->subdev_flags = SDF_WRITABLE;
272         s->n_chan = 16;
273         s->insn_bits = multiq3_do_insn_bits;
274         s->maxdata = 1;
275         s->range_table = &range_digital;
276         s->state = 0;
277
278         s = &dev->subdevices[4];
279         /* encoder (counter) subdevice */
280         s->type = COMEDI_SUBD_COUNTER;
281         s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
282         s->n_chan = it->options[2] * 2;
283         s->insn_read = multiq3_encoder_insn_read;
284         s->maxdata = 0xffffff;
285         s->range_table = &range_unknown;
286
287         encoder_reset(dev);
288
289         return 0;
290 }
291
292 static struct comedi_driver multiq3_driver = {
293         .driver_name    = "multiq3",
294         .module         = THIS_MODULE,
295         .attach         = multiq3_attach,
296         .detach         = comedi_legacy_detach,
297 };
298 module_comedi_driver(multiq3_driver);
299
300 MODULE_AUTHOR("Comedi http://www.comedi.org");
301 MODULE_DESCRIPTION("Comedi low-level driver");
302 MODULE_LICENSE("GPL");