Merge tag 'spi-v4.0-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
[linux-2.6-block.git] / drivers / staging / comedi / drivers / das800.c
CommitLineData
3726e56b
FMH
1/*
2 comedi/drivers/das800.c
3 Driver for Keitley das800 series boards and compatibles
4 Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net>
5
6 COMEDI - Linux Control and Measurement Device Interface
7 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
3726e56b
FMH
18*/
19/*
20Driver: das800
21Description: Keithley Metrabyte DAS800 (& compatibles)
22Author: Frank Mori Hess <fmhess@users.sourceforge.net>
23Devices: [Keithley Metrabyte] DAS-800 (das-800), DAS-801 (das-801),
24 DAS-802 (das-802),
25 [Measurement Computing] CIO-DAS800 (cio-das800),
26 CIO-DAS801 (cio-das801), CIO-DAS802 (cio-das802),
27 CIO-DAS802/16 (cio-das802/16)
28Status: works, cio-das802/16 untested - email me if you have tested it
29
30Configuration options:
31 [0] - I/O port base address
32 [1] - IRQ (optional, required for timed or externally triggered conversions)
33
34Notes:
35 IRQ can be omitted, although the cmd interface will not work without it.
36
37 All entries in the channel/gain list must use the same gain and be
38 consecutive channels counting upwards in channel number (these are
39 hardware limitations.)
40
41 I've never tested the gain setting stuff since I only have a
42 DAS-800 board with fixed gain.
43
44 The cio-das802/16 does not have a fifo-empty status bit! Therefore
45 only fifo-half-full transfers are possible with this card.
46*/
47/*
48
49cmd triggers supported:
50 start_src: TRIG_NOW | TRIG_EXT
51 scan_begin_src: TRIG_FOLLOW
52 scan_end_src: TRIG_COUNT
53 convert_src: TRIG_TIMER | TRIG_EXT
54 stop_src: TRIG_NONE | TRIG_COUNT
55
56
57*/
58
ce157f80 59#include <linux/module.h>
25436dc9 60#include <linux/interrupt.h>
3726e56b
FMH
61#include "../comedidev.h"
62
3726e56b
FMH
63#include <linux/delay.h>
64
65#include "8253.h"
66#include "comedi_fc.h"
67
2696fb57 68#define N_CHAN_AI 8 /* number of analog input channels */
3726e56b
FMH
69
70/* Registers for the das800 */
71
72#define DAS800_LSB 0
73#define FIFO_EMPTY 0x1
74#define FIFO_OVF 0x2
75#define DAS800_MSB 1
76#define DAS800_CONTROL1 2
77#define CONTROL1_INTE 0x8
78#define DAS800_CONV_CONTROL 2
79#define ITE 0x1
80#define CASC 0x2
81#define DTEN 0x4
82#define IEOC 0x8
83#define EACS 0x10
84#define CONV_HCEN 0x80
85#define DAS800_SCAN_LIMITS 2
86#define DAS800_STATUS 2
87#define IRQ 0x8
88#define BUSY 0x80
89#define DAS800_GAIN 3
eca7cf72
HS
90#define CIO_FFOV 0x8 /* cio-das802/16 fifo overflow */
91#define CIO_ENHF 0x90 /* cio-das802/16 fifo half full int ena */
3726e56b
FMH
92#define CONTROL1 0x80
93#define CONV_CONTROL 0xa0
94#define SCAN_LIMITS 0xc0
95#define ID 0xe0
96#define DAS800_8254 4
97#define DAS800_STATUS2 7
98#define STATUS2_HCEN 0x80
99#define STATUS2_INTE 0X20
100#define DAS800_ID 7
101
7f340859
HS
102#define DAS802_16_HALF_FIFO_SZ 128
103
febc2ed6 104struct das800_board {
3726e56b
FMH
105 const char *name;
106 int ai_speed;
9ced1de6 107 const struct comedi_lrange *ai_range;
3726e56b 108 int resolution;
febc2ed6 109};
3726e56b 110
9ced1de6 111static const struct comedi_lrange range_das801_ai = {
b678075f
HS
112 9, {
113 BIP_RANGE(5),
114 BIP_RANGE(10),
115 UNI_RANGE(10),
116 BIP_RANGE(0.5),
117 UNI_RANGE(1),
118 BIP_RANGE(0.05),
119 UNI_RANGE(0.1),
120 BIP_RANGE(0.01),
121 UNI_RANGE(0.02)
122 }
3726e56b
FMH
123};
124
9ced1de6 125static const struct comedi_lrange range_cio_das801_ai = {
b678075f
HS
126 9, {
127 BIP_RANGE(5),
128 BIP_RANGE(10),
129 UNI_RANGE(10),
130 BIP_RANGE(0.5),
131 UNI_RANGE(1),
132 BIP_RANGE(0.05),
133 UNI_RANGE(0.1),
134 BIP_RANGE(0.005),
135 UNI_RANGE(0.01)
136 }
3726e56b
FMH
137};
138
9ced1de6 139static const struct comedi_lrange range_das802_ai = {
b678075f
HS
140 9, {
141 BIP_RANGE(5),
142 BIP_RANGE(10),
143 UNI_RANGE(10),
144 BIP_RANGE(2.5),
145 UNI_RANGE(5),
146 BIP_RANGE(1.25),
147 UNI_RANGE(2.5),
148 BIP_RANGE(0.625),
149 UNI_RANGE(1.25)
150 }
3726e56b
FMH
151};
152
9ced1de6 153static const struct comedi_lrange range_das80216_ai = {
b678075f
HS
154 8, {
155 BIP_RANGE(10),
156 UNI_RANGE(10),
157 BIP_RANGE(5),
158 UNI_RANGE(5),
159 BIP_RANGE(2.5),
160 UNI_RANGE(2.5),
161 BIP_RANGE(1.25),
162 UNI_RANGE(1.25)
163 }
3726e56b
FMH
164};
165
c1a59171
HS
166enum das800_boardinfo {
167 BOARD_DAS800,
168 BOARD_CIODAS800,
169 BOARD_DAS801,
170 BOARD_CIODAS801,
171 BOARD_DAS802,
172 BOARD_CIODAS802,
173 BOARD_CIODAS80216,
174};
3726e56b 175
febc2ed6 176static const struct das800_board das800_boards[] = {
c1a59171
HS
177 [BOARD_DAS800] = {
178 .name = "das-800",
179 .ai_speed = 25000,
180 .ai_range = &range_bipolar5,
181 .resolution = 12,
182 },
183 [BOARD_CIODAS800] = {
184 .name = "cio-das800",
185 .ai_speed = 20000,
186 .ai_range = &range_bipolar5,
187 .resolution = 12,
188 },
189 [BOARD_DAS801] = {
190 .name = "das-801",
191 .ai_speed = 25000,
192 .ai_range = &range_das801_ai,
193 .resolution = 12,
194 },
195 [BOARD_CIODAS801] = {
196 .name = "cio-das801",
197 .ai_speed = 20000,
198 .ai_range = &range_cio_das801_ai,
199 .resolution = 12,
200 },
201 [BOARD_DAS802] = {
202 .name = "das-802",
203 .ai_speed = 25000,
204 .ai_range = &range_das802_ai,
205 .resolution = 12,
206 },
207 [BOARD_CIODAS802] = {
208 .name = "cio-das802",
209 .ai_speed = 20000,
210 .ai_range = &range_das802_ai,
211 .resolution = 12,
212 },
213 [BOARD_CIODAS80216] = {
214 .name = "cio-das802/16",
215 .ai_speed = 10000,
216 .ai_range = &range_das80216_ai,
217 .resolution = 16,
218 },
3726e56b
FMH
219};
220
938f185d 221struct das800_private {
808cea5a
HS
222 unsigned int divisor1; /* counter 1 value for timed conversions */
223 unsigned int divisor2; /* counter 2 value for timed conversions */
ad5774fc 224 unsigned int do_bits; /* digital output bits */
938f185d 225};
3726e56b 226
0a8fc089
HS
227static void das800_ind_write(struct comedi_device *dev,
228 unsigned val, unsigned reg)
229{
230 /*
231 * Select dev->iobase + 2 to be desired register
232 * then write to that register.
233 */
234 outb(reg, dev->iobase + DAS800_GAIN);
235 outb(val, dev->iobase + 2);
236}
237
238static unsigned das800_ind_read(struct comedi_device *dev, unsigned reg)
239{
240 /*
241 * Select dev->iobase + 7 to be desired register
242 * then read from that register.
243 */
244 outb(reg, dev->iobase + DAS800_GAIN);
245 return inb(dev->iobase + 7);
246}
247
57d1ebf7 248static void das800_enable(struct comedi_device *dev)
3726e56b 249{
b7793094 250 const struct das800_board *thisboard = dev->board_ptr;
9a1a6cf8 251 struct das800_private *devpriv = dev->private;
3726e56b 252 unsigned long irq_flags;
9a1a6cf8 253
5f74ea14 254 spin_lock_irqsave(&dev->spinlock, irq_flags);
2696fb57 255 /* enable fifo-half full interrupts for cio-das802/16 */
3726e56b
FMH
256 if (thisboard->resolution == 16)
257 outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
0a8fc089
HS
258 /* enable hardware triggering */
259 das800_ind_write(dev, CONV_HCEN, CONV_CONTROL);
260 /* enable card's interrupt */
261 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
5f74ea14 262 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
3726e56b
FMH
263}
264
57d1ebf7 265static void das800_disable(struct comedi_device *dev)
3726e56b
FMH
266{
267 unsigned long irq_flags;
0a8fc089 268
5f74ea14 269 spin_lock_irqsave(&dev->spinlock, irq_flags);
0a8fc089
HS
270 /* disable hardware triggering of conversions */
271 das800_ind_write(dev, 0x0, CONV_CONTROL);
5f74ea14 272 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
3726e56b
FMH
273}
274
52c9e62c 275static void das800_set_frequency(struct comedi_device *dev)
62c3b4bf
HS
276{
277 struct das800_private *devpriv = dev->private;
52c9e62c 278 unsigned long timer_base = dev->iobase + DAS800_8254;
62c3b4bf 279
52c9e62c
HS
280 i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY);
281 i8254_set_mode(timer_base, 0, 2, I8254_MODE2 | I8254_BINARY);
282 i8254_write(timer_base, 0, 1, devpriv->divisor1);
283 i8254_write(timer_base, 0, 2, devpriv->divisor2);
62c3b4bf
HS
284}
285
fe9b0850
HS
286static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
287{
57d1ebf7 288 das800_disable(dev);
fe9b0850
HS
289 return 0;
290}
291
1626657f
HS
292static int das800_ai_check_chanlist(struct comedi_device *dev,
293 struct comedi_subdevice *s,
294 struct comedi_cmd *cmd)
295{
296 unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
297 unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
298 int i;
299
300 for (i = 1; i < cmd->chanlist_len; i++) {
301 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
302 unsigned int range = CR_RANGE(cmd->chanlist[i]);
303
304 if (chan != (chan0 + i) % s->n_chan) {
305 dev_dbg(dev->class_dev,
306 "chanlist must be consecutive, counting upwards\n");
307 return -EINVAL;
308 }
309
310 if (range != range0) {
311 dev_dbg(dev->class_dev,
312 "chanlist must all have the same gain\n");
313 return -EINVAL;
314 }
315 }
316
317 return 0;
318}
319
0a85b6f0
MT
320static int das800_ai_do_cmdtest(struct comedi_device *dev,
321 struct comedi_subdevice *s,
322 struct comedi_cmd *cmd)
3726e56b 323{
b7793094 324 const struct das800_board *thisboard = dev->board_ptr;
9a1a6cf8 325 struct das800_private *devpriv = dev->private;
3726e56b 326 int err = 0;
58245cc9 327 unsigned int arg;
3726e56b 328
27020ffe 329 /* Step 1 : check if triggers are trivially valid */
3726e56b 330
27020ffe
HS
331 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
332 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
333 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
334 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
335 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
3726e56b
FMH
336
337 if (err)
338 return 1;
339
27020ffe 340 /* Step 2a : make sure trigger sources are unique */
3726e56b 341
27020ffe
HS
342 err |= cfc_check_trigger_is_unique(cmd->start_src);
343 err |= cfc_check_trigger_is_unique(cmd->convert_src);
344 err |= cfc_check_trigger_is_unique(cmd->stop_src);
345
346 /* Step 2b : and mutually compatible */
3726e56b
FMH
347
348 if (err)
349 return 2;
350
50b825ff 351 /* Step 3: check if arguments are trivially valid */
3726e56b 352
50b825ff
HS
353 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
354
355 if (cmd->convert_src == TRIG_TIMER)
356 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
357 thisboard->ai_speed);
358
359 err |= cfc_check_trigger_arg_min(&cmd->chanlist_len, 1);
360 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
361
362 if (cmd->stop_src == TRIG_COUNT)
363 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
364 else /* TRIG_NONE */
365 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
3726e56b
FMH
366
367 if (err)
368 return 3;
369
370 /* step 4: fix up any arguments */
371
372 if (cmd->convert_src == TRIG_TIMER) {
58245cc9 373 arg = cmd->convert_arg;
cb9cfd7e
HS
374 i8253_cascade_ns_to_timer(I8254_OSC_BASE_1MHZ,
375 &devpriv->divisor1,
376 &devpriv->divisor2,
58245cc9
HS
377 &arg, cmd->flags);
378 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
3726e56b
FMH
379 }
380
381 if (err)
382 return 4;
383
1626657f
HS
384 /* Step 5: check channel list if it exists */
385 if (cmd->chanlist && cmd->chanlist_len > 0)
386 err |= das800_ai_check_chanlist(dev, s, cmd);
3726e56b
FMH
387
388 if (err)
389 return 5;
390
391 return 0;
392}
393
0a85b6f0
MT
394static int das800_ai_do_cmd(struct comedi_device *dev,
395 struct comedi_subdevice *s)
3726e56b 396{
b7793094 397 const struct das800_board *thisboard = dev->board_ptr;
d24160f8 398 struct comedi_async *async = s->async;
8ffffae6
HS
399 struct comedi_cmd *cmd = &async->cmd;
400 unsigned int gain = CR_RANGE(cmd->chanlist[0]);
401 unsigned int start_chan = CR_CHAN(cmd->chanlist[0]);
402 unsigned int end_chan = (start_chan + cmd->chanlist_len - 1) % 8;
d24160f8 403 unsigned int scan_chans = (end_chan << 3) | start_chan;
3726e56b
FMH
404 int conv_bits;
405 unsigned long irq_flags;
3726e56b 406
57d1ebf7 407 das800_disable(dev);
3726e56b 408
5f74ea14 409 spin_lock_irqsave(&dev->spinlock, irq_flags);
0a8fc089 410 /* set scan limits */
d24160f8 411 das800_ind_write(dev, scan_chans, SCAN_LIMITS);
5f74ea14 412 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
3726e56b
FMH
413
414 /* set gain */
3726e56b
FMH
415 if (thisboard->resolution == 12 && gain > 0)
416 gain += 0x7;
417 gain &= 0xf;
418 outb(gain, dev->iobase + DAS800_GAIN);
419
3726e56b
FMH
420 /* enable auto channel scan, send interrupts on end of conversion
421 * and set clock source to internal or external
422 */
423 conv_bits = 0;
424 conv_bits |= EACS | IEOC;
8ffffae6 425 if (cmd->start_src == TRIG_EXT)
3726e56b 426 conv_bits |= DTEN;
8ffffae6 427 if (cmd->convert_src == TRIG_TIMER) {
3726e56b
FMH
428 conv_bits |= CASC | ITE;
429 /* set conversion frequency */
52c9e62c 430 das800_set_frequency(dev);
3726e56b
FMH
431 }
432
5f74ea14 433 spin_lock_irqsave(&dev->spinlock, irq_flags);
0a8fc089 434 das800_ind_write(dev, conv_bits, CONV_CONTROL);
5f74ea14 435 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
0a8fc089 436
57d1ebf7 437 das800_enable(dev);
3726e56b
FMH
438 return 0;
439}
440
b4780a3a
HS
441static unsigned int das800_ai_get_sample(struct comedi_device *dev)
442{
443 unsigned int lsb = inb(dev->iobase + DAS800_LSB);
444 unsigned int msb = inb(dev->iobase + DAS800_MSB);
445
446 return (msb << 8) | lsb;
447}
448
fe9b0850
HS
449static irqreturn_t das800_interrupt(int irq, void *d)
450{
fe9b0850 451 struct comedi_device *dev = d;
fe9b0850 452 struct das800_private *devpriv = dev->private;
7f340859 453 struct comedi_subdevice *s = dev->read_subdev;
9dad12fe
HS
454 struct comedi_async *async;
455 struct comedi_cmd *cmd;
fe9b0850 456 unsigned long irq_flags;
7f340859
HS
457 unsigned int status;
458 unsigned int val;
459 bool fifo_empty;
460 bool fifo_overflow;
461 int i;
fe9b0850
HS
462
463 status = inb(dev->iobase + DAS800_STATUS);
fe9b0850
HS
464 if (!(status & IRQ))
465 return IRQ_NONE;
7f340859 466 if (!dev->attached)
fe9b0850
HS
467 return IRQ_HANDLED;
468
9dad12fe
HS
469 async = s->async;
470 cmd = &async->cmd;
471
fe9b0850 472 spin_lock_irqsave(&dev->spinlock, irq_flags);
0a8fc089 473 status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN;
7f340859
HS
474 /*
475 * Don't release spinlock yet since we want to make sure
476 * no one else disables hardware conversions.
477 */
478
479 /* if hardware conversions are not enabled, then quit */
fe9b0850
HS
480 if (status == 0) {
481 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
482 return IRQ_HANDLED;
483 }
484
7f340859
HS
485 for (i = 0; i < DAS802_16_HALF_FIFO_SZ; i++) {
486 val = das800_ai_get_sample(dev);
487 if (s->maxdata == 0x0fff) {
488 fifo_empty = !!(val & FIFO_EMPTY);
489 fifo_overflow = !!(val & FIFO_OVF);
fe9b0850 490 } else {
7f340859
HS
491 /* cio-das802/16 has no fifo empty status bit */
492 fifo_empty = false;
493 fifo_overflow = !!(inb(dev->iobase + DAS800_GAIN) &
494 CIO_FFOV);
fe9b0850 495 }
7f340859 496 if (fifo_empty || fifo_overflow)
fe9b0850 497 break;
7f340859
HS
498
499 if (s->maxdata == 0x0fff)
500 val >>= 4; /* 12-bit sample */
501
5e62863a
HS
502 val &= s->maxdata;
503 comedi_buf_write_samples(s, &val, 1);
504
505 if (cmd->stop_src == TRIG_COUNT &&
506 async->scans_done >= cmd->stop_arg) {
507 async->events |= COMEDI_CB_EOA;
508 break;
fe9b0850
HS
509 }
510 }
7f340859 511
fe9b0850
HS
512 if (fifo_overflow) {
513 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
3e6cb74f 514 async->events |= COMEDI_CB_ERROR;
837a1264 515 comedi_handle_events(dev, s);
fe9b0850
HS
516 return IRQ_HANDLED;
517 }
7f340859 518
5e62863a
HS
519 if (!(async->events & COMEDI_CB_CANCEL_MASK)) {
520 /*
521 * Re-enable card's interrupt.
522 * We already have spinlock, so indirect addressing is safe
523 */
0a8fc089
HS
524 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
525 CONTROL1);
fe9b0850 526 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
fe9b0850 527 } else {
7f340859 528 /* otherwise, stop taking data */
fe9b0850 529 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
57d1ebf7 530 das800_disable(dev);
fe9b0850 531 }
837a1264 532 comedi_handle_events(dev, s);
fe9b0850
HS
533 return IRQ_HANDLED;
534}
535
90bc68ec
HS
536static int das800_ai_eoc(struct comedi_device *dev,
537 struct comedi_subdevice *s,
538 struct comedi_insn *insn,
539 unsigned long context)
b4780a3a 540{
90bc68ec 541 unsigned int status;
b4780a3a 542
90bc68ec
HS
543 status = inb(dev->iobase + DAS800_STATUS);
544 if ((status & BUSY) == 0)
545 return 0;
546 return -EBUSY;
b4780a3a
HS
547}
548
d7427345
HS
549static int das800_ai_insn_read(struct comedi_device *dev,
550 struct comedi_subdevice *s,
551 struct comedi_insn *insn,
552 unsigned int *data)
3726e56b 553{
9a1a6cf8 554 struct das800_private *devpriv = dev->private;
b4780a3a
HS
555 unsigned int chan = CR_CHAN(insn->chanspec);
556 unsigned int range = CR_RANGE(insn->chanspec);
3726e56b 557 unsigned long irq_flags;
b4780a3a
HS
558 unsigned int val;
559 int ret;
560 int i;
3726e56b 561
57d1ebf7 562 das800_disable(dev);
3726e56b
FMH
563
564 /* set multiplexer */
5f74ea14 565 spin_lock_irqsave(&dev->spinlock, irq_flags);
0a8fc089 566 das800_ind_write(dev, chan | devpriv->do_bits, CONTROL1);
5f74ea14 567 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
3726e56b
FMH
568
569 /* set gain / range */
b4780a3a 570 if (s->maxdata == 0x0fff && range)
3726e56b
FMH
571 range += 0x7;
572 range &= 0xf;
573 outb(range, dev->iobase + DAS800_GAIN);
574
5f74ea14 575 udelay(5);
3726e56b 576
b4780a3a 577 for (i = 0; i < insn->n; i++) {
3726e56b
FMH
578 /* trigger conversion */
579 outb_p(0, dev->iobase + DAS800_MSB);
580
90bc68ec 581 ret = comedi_timeout(dev, s, insn, das800_ai_eoc, 0);
b4780a3a
HS
582 if (ret)
583 return ret;
584
585 val = das800_ai_get_sample(dev);
586 if (s->maxdata == 0x0fff)
587 val >>= 4; /* 12-bit sample */
588 data[i] = val & s->maxdata;
3726e56b
FMH
589 }
590
b4780a3a 591 return insn->n;
3726e56b
FMH
592}
593
d7427345
HS
594static int das800_di_insn_bits(struct comedi_device *dev,
595 struct comedi_subdevice *s,
596 struct comedi_insn *insn,
597 unsigned int *data)
3726e56b 598{
e245b6d1 599 data[1] = (inb(dev->iobase + DAS800_STATUS) >> 4) & 0x7;
3726e56b 600
a2714e3e 601 return insn->n;
3726e56b
FMH
602}
603
d7427345
HS
604static int das800_do_insn_bits(struct comedi_device *dev,
605 struct comedi_subdevice *s,
606 struct comedi_insn *insn,
607 unsigned int *data)
3726e56b 608{
9a1a6cf8 609 struct das800_private *devpriv = dev->private;
3726e56b
FMH
610 unsigned long irq_flags;
611
97f4289a 612 if (comedi_dio_update_state(s, data)) {
26234771 613 devpriv->do_bits = s->state << 4;
3726e56b 614
26234771
HS
615 spin_lock_irqsave(&dev->spinlock, irq_flags);
616 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
617 CONTROL1);
618 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
619 }
3726e56b 620
26234771 621 data[1] = s->state;
3726e56b 622
a2714e3e 623 return insn->n;
3726e56b
FMH
624}
625
4f71ceeb
HS
626static int das800_probe(struct comedi_device *dev)
627{
b7793094 628 const struct das800_board *thisboard = dev->board_ptr;
c1a59171 629 int board = thisboard ? thisboard - das800_boards : -EINVAL;
4f71ceeb
HS
630 int id_bits;
631 unsigned long irq_flags;
4f71ceeb
HS
632
633 spin_lock_irqsave(&dev->spinlock, irq_flags);
0a8fc089 634 id_bits = das800_ind_read(dev, ID) & 0x3;
4f71ceeb
HS
635 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
636
4f71ceeb
HS
637 switch (id_bits) {
638 case 0x0:
c1a59171
HS
639 if (board == BOARD_DAS800 || board == BOARD_CIODAS800)
640 break;
4f71ceeb 641 dev_dbg(dev->class_dev, "Board model (probed): DAS-800\n");
c1a59171 642 board = BOARD_DAS800;
4f71ceeb
HS
643 break;
644 case 0x2:
c1a59171
HS
645 if (board == BOARD_DAS801 || board == BOARD_CIODAS801)
646 break;
4f71ceeb 647 dev_dbg(dev->class_dev, "Board model (probed): DAS-801\n");
c1a59171 648 board = BOARD_DAS801;
4f71ceeb
HS
649 break;
650 case 0x3:
c1a59171
HS
651 if (board == BOARD_DAS802 || board == BOARD_CIODAS802 ||
652 board == BOARD_CIODAS80216)
653 break;
4f71ceeb 654 dev_dbg(dev->class_dev, "Board model (probed): DAS-802\n");
c1a59171 655 board = BOARD_DAS802;
4f71ceeb
HS
656 break;
657 default:
c1a59171 658 dev_dbg(dev->class_dev, "Board model: 0x%x (unknown)\n",
4f71ceeb 659 id_bits);
c1a59171 660 board = -EINVAL;
4f71ceeb
HS
661 break;
662 }
c1a59171 663 return board;
4f71ceeb
HS
664}
665
92046ae4
HS
666static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
667{
1d57261c 668 const struct das800_board *thisboard;
92046ae4
HS
669 struct das800_private *devpriv;
670 struct comedi_subdevice *s;
671 unsigned int irq = it->options[1];
672 unsigned long irq_flags;
673 int board;
674 int ret;
675
0bdab509 676 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
92046ae4
HS
677 if (!devpriv)
678 return -ENOMEM;
92046ae4 679
862755ec 680 ret = comedi_request_region(dev, it->options[0], 0x8);
92046ae4
HS
681 if (ret)
682 return ret;
683
684 board = das800_probe(dev);
685 if (board < 0) {
686 dev_dbg(dev->class_dev, "unable to determine board type\n");
687 return -ENODEV;
688 }
689 dev->board_ptr = das800_boards + board;
b7793094 690 thisboard = dev->board_ptr;
f05ffb6e 691 dev->board_name = thisboard->name;
92046ae4 692
f05ffb6e
HS
693 if (irq > 1 && irq <= 7) {
694 ret = request_irq(irq, das800_interrupt, 0, dev->board_name,
695 dev);
696 if (ret == 0)
697 dev->irq = irq;
92046ae4 698 }
92046ae4
HS
699
700 ret = comedi_alloc_subdevices(dev, 3);
701 if (ret)
702 return ret;
703
d7427345 704 /* Analog Input subdevice */
92046ae4
HS
705 s = &dev->subdevices[0];
706 dev->read_subdev = s;
77630119
HS
707 s->type = COMEDI_SUBD_AI;
708 s->subdev_flags = SDF_READABLE | SDF_GROUND;
709 s->n_chan = 8;
710 s->maxdata = (1 << thisboard->resolution) - 1;
711 s->range_table = thisboard->ai_range;
d7427345 712 s->insn_read = das800_ai_insn_read;
77630119
HS
713 if (dev->irq) {
714 s->subdev_flags |= SDF_CMD_READ;
715 s->len_chanlist = 8;
716 s->do_cmdtest = das800_ai_do_cmdtest;
717 s->do_cmd = das800_ai_do_cmd;
718 s->cancel = das800_cancel;
719 }
92046ae4 720
d7427345 721 /* Digital Input subdevice */
92046ae4 722 s = &dev->subdevices[1];
d7427345
HS
723 s->type = COMEDI_SUBD_DI;
724 s->subdev_flags = SDF_READABLE;
725 s->n_chan = 3;
726 s->maxdata = 1;
727 s->range_table = &range_digital;
728 s->insn_bits = das800_di_insn_bits;
729
730 /* Digital Output subdevice */
92046ae4 731 s = &dev->subdevices[2];
d7427345 732 s->type = COMEDI_SUBD_DO;
453fd2b3 733 s->subdev_flags = SDF_WRITABLE;
d7427345
HS
734 s->n_chan = 4;
735 s->maxdata = 1;
736 s->range_table = &range_digital;
737 s->insn_bits = das800_do_insn_bits;
92046ae4 738
57d1ebf7 739 das800_disable(dev);
92046ae4
HS
740
741 /* initialize digital out channels */
742 spin_lock_irqsave(&dev->spinlock, irq_flags);
0a8fc089 743 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
92046ae4
HS
744 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
745
746 return 0;
747};
748
79a8c0e2
HS
749static struct comedi_driver driver_das800 = {
750 .driver_name = "das800",
751 .module = THIS_MODULE,
752 .attach = das800_attach,
753 .detach = comedi_legacy_detach,
754 .num_names = ARRAY_SIZE(das800_boards),
755 .board_name = &das800_boards[0].name,
756 .offset = sizeof(struct das800_board),
757};
758module_comedi_driver(driver_das800);
759
90f703d3
AT
760MODULE_AUTHOR("Comedi http://www.comedi.org");
761MODULE_DESCRIPTION("Comedi low-level driver");
762MODULE_LICENSE("GPL");