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