Staging: comedi: add comedi_rt_timer virtual driver
[linux-2.6] / drivers / staging / comedi / drivers / rti800.c
1 /*
2    comedi/drivers/rti800.c
3    Hardware driver for Analog Devices RTI-800/815 board
4
5    COMEDI - Linux Control and Measurement Device Interface
6    Copyright (C) 1998 David A. Schleef <ds@schleef.org>
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    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.
21
22  */
23 /*
24 Driver: rti800
25 Description: Analog Devices RTI-800/815
26 Author: ds
27 Status: unknown
28 Updated: Fri, 05 Sep 2008 14:50:44 +0100
29 Devices: [Analog Devices] RTI-800 (rti800), RTI-815 (rti815)
30
31 Configuration options:
32   [0] - I/O port base address
33   [1] - IRQ
34   [2] - A/D reference
35           0 = differential
36           1 = pseudodifferential (common)
37           2 = single-ended
38   [3] - A/D range
39           0 = [-10,10]
40           1 = [-5,5]
41           2 = [0,10]
42   [4] - A/D encoding
43           0 = two's complement
44           1 = straight binary
45   [5] - DAC 0 range
46           0 = [-10,10]
47           1 = [0,10]
48   [6] - DAC 0 encoding
49           0 = two's complement
50           1 = straight binary
51   [7] - DAC 1 range (same as DAC 0)
52   [8] - DAC 1 encoding (same as DAC 0)
53 */
54
55 #include "../comedidev.h"
56
57 #include <linux/ioport.h>
58
59 #define RTI800_SIZE 16
60
61 #define RTI800_CSR 0
62 #define RTI800_MUXGAIN 1
63 #define RTI800_CONVERT 2
64 #define RTI800_ADCLO 3
65 #define RTI800_ADCHI 4
66 #define RTI800_DAC0LO 5
67 #define RTI800_DAC0HI 6
68 #define RTI800_DAC1LO 7
69 #define RTI800_DAC1HI 8
70 #define RTI800_CLRFLAGS 9
71 #define RTI800_DI 10
72 #define RTI800_DO 11
73 #define RTI800_9513A_DATA 12
74 #define RTI800_9513A_CNTRL 13
75 #define RTI800_9513A_STATUS 13
76
77 /*
78  * flags for CSR register
79  */
80
81 #define RTI800_BUSY             0x80
82 #define RTI800_DONE             0x40
83 #define RTI800_OVERRUN          0x20
84 #define RTI800_TCR              0x10
85 #define RTI800_DMA_ENAB         0x08
86 #define RTI800_INTR_TC          0x04
87 #define RTI800_INTR_EC          0x02
88 #define RTI800_INTR_OVRN        0x01
89
90 #define Am9513_8BITBUS
91
92 #define Am9513_output_control(a)        outb(a,dev->iobase+RTI800_9513A_CNTRL)
93 #define Am9513_output_data(a)           outb(a,dev->iobase+RTI800_9513A_DATA)
94 #define Am9513_input_data()             inb(dev->iobase+RTI800_9513A_DATA)
95 #define Am9513_input_status()           inb(dev->iobase+RTI800_9513A_STATUS)
96
97 #include "am9513.h"
98
99 static const comedi_lrange range_rti800_ai_10_bipolar = { 4, {
100                         BIP_RANGE(10),
101                         BIP_RANGE(1),
102                         BIP_RANGE(0.1),
103                         BIP_RANGE(0.02)
104         }
105 };
106 static const comedi_lrange range_rti800_ai_5_bipolar = { 4, {
107                         BIP_RANGE(5),
108                         BIP_RANGE(0.5),
109                         BIP_RANGE(0.05),
110                         BIP_RANGE(0.01)
111         }
112 };
113 static const comedi_lrange range_rti800_ai_unipolar = { 4, {
114                         UNI_RANGE(10),
115                         UNI_RANGE(1),
116                         UNI_RANGE(0.1),
117                         UNI_RANGE(0.02)
118         }
119 };
120
121 typedef struct {
122         const char *name;
123         int has_ao;
124 } boardtype;
125 static const boardtype boardtypes[] = {
126         {"rti800", 0},
127         {"rti815", 1},
128 };
129
130 #define this_board ((const boardtype *)dev->board_ptr)
131
132 static int rti800_attach(comedi_device * dev, comedi_devconfig * it);
133 static int rti800_detach(comedi_device * dev);
134 static comedi_driver driver_rti800 = {
135       driver_name:"rti800",
136       module:THIS_MODULE,
137       attach:rti800_attach,
138       detach:rti800_detach,
139       num_names:sizeof(boardtypes) / sizeof(boardtype),
140       board_name:&boardtypes[0].name,
141       offset:sizeof(boardtype),
142 };
143
144 COMEDI_INITCLEANUP(driver_rti800);
145
146 static irqreturn_t rti800_interrupt(int irq, void *dev PT_REGS_ARG);
147
148 typedef struct {
149         enum {
150                 adc_diff, adc_pseudodiff, adc_singleended
151         } adc_mux;
152         enum {
153                 adc_bipolar10, adc_bipolar5, adc_unipolar10
154         } adc_range;
155         enum {
156                 adc_2comp, adc_straight
157         } adc_coding;
158         enum {
159                 dac_bipolar10, dac_unipolar10
160         } dac0_range, dac1_range;
161         enum {
162                 dac_2comp, dac_straight
163         } dac0_coding, dac1_coding;
164         const comedi_lrange *ao_range_type_list[2];
165         lsampl_t ao_readback[2];
166         int muxgain_bits;
167 } rti800_private;
168
169 #define devpriv ((rti800_private *)dev->private)
170
171 #define RTI800_TIMEOUT 100
172
173 static irqreturn_t rti800_interrupt(int irq, void *dev PT_REGS_ARG)
174 {
175         return IRQ_HANDLED;
176 }
177
178 // settling delay times in usec for different gains
179 static const int gaindelay[] = { 10, 20, 40, 80 };
180
181 static int rti800_ai_insn_read(comedi_device * dev, comedi_subdevice * s,
182         comedi_insn * insn, lsampl_t * data)
183 {
184         int i, t;
185         int status;
186         int chan = CR_CHAN(insn->chanspec);
187         unsigned gain = CR_RANGE(insn->chanspec);
188         unsigned muxgain_bits;
189
190         inb(dev->iobase + RTI800_ADCHI);
191         outb(0, dev->iobase + RTI800_CLRFLAGS);
192
193         muxgain_bits = chan | (gain << 5);
194         if (muxgain_bits != devpriv->muxgain_bits) {
195                 devpriv->muxgain_bits = muxgain_bits;
196                 outb(devpriv->muxgain_bits, dev->iobase + RTI800_MUXGAIN);
197                 /* without a delay here, the RTI_OVERRUN bit
198                  * gets set, and you will have an error. */
199                 if (insn->n > 0) {
200                         BUG_ON(gain >=
201                                 sizeof(gaindelay) / sizeof(gaindelay[0]));
202                         comedi_udelay(gaindelay[gain]);
203                 }
204         }
205
206         for (i = 0; i < insn->n; i++) {
207                 outb(0, dev->iobase + RTI800_CONVERT);
208                 for (t = RTI800_TIMEOUT; t; t--) {
209                         status = inb(dev->iobase + RTI800_CSR);
210                         if (status & RTI800_OVERRUN) {
211                                 rt_printk("rti800: a/d overrun\n");
212                                 outb(0, dev->iobase + RTI800_CLRFLAGS);
213                                 return -EIO;
214                         }
215                         if (status & RTI800_DONE)
216                                 break;
217                         comedi_udelay(1);
218                 }
219                 if (t == 0) {
220                         rt_printk("rti800: timeout\n");
221                         return -ETIME;
222                 }
223                 data[i] = inb(dev->iobase + RTI800_ADCLO);
224                 data[i] |= (0xf & inb(dev->iobase + RTI800_ADCHI)) << 8;
225
226                 if (devpriv->adc_coding == adc_2comp) {
227                         data[i] ^= 0x800;
228                 }
229         }
230
231         return i;
232 }
233
234 static int rti800_ao_insn_read(comedi_device * dev, comedi_subdevice * s,
235         comedi_insn * insn, lsampl_t * data)
236 {
237         int i;
238         int chan = CR_CHAN(insn->chanspec);
239
240         for (i = 0; i < insn->n; i++)
241                 data[i] = devpriv->ao_readback[chan];
242
243         return i;
244 }
245
246 static int rti800_ao_insn_write(comedi_device * dev, comedi_subdevice * s,
247         comedi_insn * insn, lsampl_t * data)
248 {
249         int chan = CR_CHAN(insn->chanspec);
250         int d;
251         int i;
252
253         for (i = 0; i < insn->n; i++) {
254                 devpriv->ao_readback[chan] = d = data[i];
255                 if (devpriv->dac0_coding == dac_2comp) {
256                         d ^= 0x800;
257                 }
258                 outb(d & 0xff,
259                         dev->iobase + (chan ? RTI800_DAC1LO : RTI800_DAC0LO));
260                 outb(d >> 8,
261                         dev->iobase + (chan ? RTI800_DAC1HI : RTI800_DAC0HI));
262         }
263         return i;
264 }
265
266 static int rti800_di_insn_bits(comedi_device * dev, comedi_subdevice * s,
267         comedi_insn * insn, lsampl_t * data)
268 {
269         if (insn->n != 2)
270                 return -EINVAL;
271         data[1] = inb(dev->iobase + RTI800_DI);
272         return 2;
273 }
274
275 static int rti800_do_insn_bits(comedi_device * dev, comedi_subdevice * s,
276         comedi_insn * insn, lsampl_t * data)
277 {
278         if (insn->n != 2)
279                 return -EINVAL;
280
281         if (data[0]) {
282                 s->state &= ~data[0];
283                 s->state |= data[0] & data[1];
284                 /* Outputs are inverted... */
285                 outb(s->state ^ 0xff, dev->iobase + RTI800_DO);
286         }
287
288         data[1] = s->state;
289
290         return 2;
291 }
292
293 /*
294    options[0] - I/O port
295    options[1] - irq
296    options[2] - a/d mux
297         0=differential, 1=pseudodiff, 2=single
298    options[3] - a/d range
299         0=bipolar10, 1=bipolar5, 2=unipolar10
300    options[4] - a/d coding
301         0=2's comp, 1=straight binary
302    options[5] - dac0 range
303         0=bipolar10, 1=unipolar10
304    options[6] - dac0 coding
305         0=2's comp, 1=straight binary
306    options[7] - dac1 range
307    options[8] - dac1 coding
308  */
309
310 static int rti800_attach(comedi_device * dev, comedi_devconfig * it)
311 {
312         unsigned int irq;
313         unsigned long iobase;
314         int ret;
315         comedi_subdevice *s;
316
317         iobase = it->options[0];
318         printk("comedi%d: rti800: 0x%04lx ", dev->minor, iobase);
319         if (!request_region(iobase, RTI800_SIZE, "rti800")) {
320                 printk("I/O port conflict\n");
321                 return -EIO;
322         }
323         dev->iobase = iobase;
324
325 #ifdef DEBUG
326         printk("fingerprint=%x,%x,%x,%x,%x ",
327                 inb(dev->iobase + 0),
328                 inb(dev->iobase + 1),
329                 inb(dev->iobase + 2),
330                 inb(dev->iobase + 3), inb(dev->iobase + 4));
331 #endif
332
333         outb(0, dev->iobase + RTI800_CSR);
334         inb(dev->iobase + RTI800_ADCHI);
335         outb(0, dev->iobase + RTI800_CLRFLAGS);
336
337         irq = it->options[1];
338         if (irq) {
339                 printk("( irq = %u )", irq);
340                 if ((ret = comedi_request_irq(irq, rti800_interrupt, 0,
341                                         "rti800", dev)) < 0) {
342                         printk(" Failed to allocate IRQ\n");
343                         return ret;
344                 }
345                 dev->irq = irq;
346         } else {
347                 printk("( no irq )");
348         }
349
350         dev->board_name = this_board->name;
351
352         if ((ret = alloc_subdevices(dev, 4)) < 0)
353                 return ret;
354         if ((ret = alloc_private(dev, sizeof(rti800_private))) < 0)
355                 return ret;
356
357         devpriv->adc_mux = it->options[2];
358         devpriv->adc_range = it->options[3];
359         devpriv->adc_coding = it->options[4];
360         devpriv->dac0_range = it->options[5];
361         devpriv->dac0_coding = it->options[6];
362         devpriv->dac1_range = it->options[7];
363         devpriv->dac1_coding = it->options[8];
364         devpriv->muxgain_bits = -1;
365
366         s = dev->subdevices + 0;
367         /* ai subdevice */
368         s->type = COMEDI_SUBD_AI;
369         s->subdev_flags = SDF_READABLE | SDF_GROUND;
370         s->n_chan = (devpriv->adc_mux ? 16 : 8);
371         s->insn_read = rti800_ai_insn_read;
372         s->maxdata = 0xfff;
373         switch (devpriv->adc_range) {
374         case adc_bipolar10:
375                 s->range_table = &range_rti800_ai_10_bipolar;
376                 break;
377         case adc_bipolar5:
378                 s->range_table = &range_rti800_ai_5_bipolar;
379                 break;
380         case adc_unipolar10:
381                 s->range_table = &range_rti800_ai_unipolar;
382                 break;
383         }
384
385         s++;
386         if (this_board->has_ao) {
387                 /* ao subdevice (only on rti815) */
388                 s->type = COMEDI_SUBD_AO;
389                 s->subdev_flags = SDF_WRITABLE;
390                 s->n_chan = 2;
391                 s->insn_read = rti800_ao_insn_read;
392                 s->insn_write = rti800_ao_insn_write;
393                 s->maxdata = 0xfff;
394                 s->range_table_list = devpriv->ao_range_type_list;
395                 switch (devpriv->dac0_range) {
396                 case dac_bipolar10:
397                         devpriv->ao_range_type_list[0] = &range_bipolar10;
398                         break;
399                 case dac_unipolar10:
400                         devpriv->ao_range_type_list[0] = &range_unipolar10;
401                         break;
402                 }
403                 switch (devpriv->dac1_range) {
404                 case dac_bipolar10:
405                         devpriv->ao_range_type_list[1] = &range_bipolar10;
406                         break;
407                 case dac_unipolar10:
408                         devpriv->ao_range_type_list[1] = &range_unipolar10;
409                         break;
410                 }
411         } else {
412                 s->type = COMEDI_SUBD_UNUSED;
413         }
414
415         s++;
416         /* di */
417         s->type = COMEDI_SUBD_DI;
418         s->subdev_flags = SDF_READABLE;
419         s->n_chan = 8;
420         s->insn_bits = rti800_di_insn_bits;
421         s->maxdata = 1;
422         s->range_table = &range_digital;
423
424         s++;
425         /* do */
426         s->type = COMEDI_SUBD_DO;
427         s->subdev_flags = SDF_WRITABLE;
428         s->n_chan = 8;
429         s->insn_bits = rti800_do_insn_bits;
430         s->maxdata = 1;
431         s->range_table = &range_digital;
432
433 /* don't yet know how to deal with counter/timers */
434 #if 0
435         s++;
436         /* do */
437         s->type = COMEDI_SUBD_TIMER;
438 #endif
439
440         printk("\n");
441
442         return 0;
443 }
444
445 static int rti800_detach(comedi_device * dev)
446 {
447         printk("comedi%d: rti800: remove\n", dev->minor);
448
449         if (dev->iobase)
450                 release_region(dev->iobase, RTI800_SIZE);
451
452         if (dev->irq)
453                 comedi_free_irq(dev->irq, dev);
454
455         return 0;
456 }