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