]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - drivers/staging/comedi/drivers/pcl818.c
staging: speakup: Remove unnecessary space
[mirror_ubuntu-hirsute-kernel.git] / drivers / staging / comedi / drivers / pcl818.c
CommitLineData
4da6a1d8
MD
1/*
2 comedi/drivers/pcl818.c
3
4 Author: Michal Dobes <dobes@tesnet.cz>
5
6 hardware driver for Advantech cards:
7 card: PCL-818L, PCL-818H, PCL-818HD, PCL-818HG, PCL-818, PCL-718
8 driver: pcl818l, pcl818h, pcl818hd, pcl818hg, pcl818, pcl718
9*/
10/*
11Driver: pcl818
12Description: Advantech PCL-818 cards, PCL-718
13Author: Michal Dobes <dobes@tesnet.cz>
14Devices: [Advantech] PCL-818L (pcl818l), PCL-818H (pcl818h),
15 PCL-818HD (pcl818hd), PCL-818HG (pcl818hg), PCL-818 (pcl818),
16 PCL-718 (pcl718)
17Status: works
18
19All cards have 16 SE/8 DIFF ADCs, one or two DACs, 16 DI and 16 DO.
20Differences are only at maximal sample speed, range list and FIFO
21support.
22The driver support AI mode 0, 1, 3 other subdevices (AO, DI, DO) support
23only mode 0. If DMA/FIFO/INT are disabled then AI support only mode 0.
24PCL-818HD and PCL-818HG support 1kword FIFO. Driver support this FIFO
25but this code is untested.
26A word or two about DMA. Driver support DMA operations at two ways:
271) DMA uses two buffers and after one is filled then is generated
28 INT and DMA restart with second buffer. With this mode I'm unable run
29 more that 80Ksamples/secs without data dropouts on K6/233.
302) DMA uses one buffer and run in autoinit mode and the data are
31 from DMA buffer moved on the fly with 2kHz interrupts from RTC.
32 This mode is used if the interrupt 8 is available for allocation.
33 If not, then first DMA mode is used. With this I can run at
34 full speed one card (100ksamples/secs) or two cards with
35 60ksamples/secs each (more is problem on account of ISA limitations).
36 To use this mode you must have compiled kernel with disabled
37 "Enhanced Real Time Clock Support".
38 Maybe you can have problems if you use xntpd or similar.
39 If you've data dropouts with DMA mode 2 then:
40 a) disable IDE DMA
41 b) switch text mode console to fb.
42
43 Options for PCL-818L:
44 [0] - IO Base
45 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
46 [2] - DMA (0=disable, 1, 3)
47 [3] - 0, 10=10MHz clock for 8254
48 1= 1MHz clock for 8254
49 [4] - 0, 5=A/D input -5V.. +5V
50 1, 10=A/D input -10V..+10V
51 [5] - 0, 5=D/A output 0-5V (internal reference -5V)
52 1, 10=D/A output 0-10V (internal reference -10V)
bbc9a991 53 2 =D/A output unknown (external reference)
4da6a1d8
MD
54
55 Options for PCL-818, PCL-818H:
56 [0] - IO Base
57 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
58 [2] - DMA (0=disable, 1, 3)
59 [3] - 0, 10=10MHz clock for 8254
60 1= 1MHz clock for 8254
61 [4] - 0, 5=D/A output 0-5V (internal reference -5V)
62 1, 10=D/A output 0-10V (internal reference -10V)
bbc9a991 63 2 =D/A output unknown (external reference)
4da6a1d8
MD
64
65 Options for PCL-818HD, PCL-818HG:
66 [0] - IO Base
67 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
68 [2] - DMA/FIFO (-1=use FIFO, 0=disable both FIFO and DMA,
69 1=use DMA ch 1, 3=use DMA ch 3)
70 [3] - 0, 10=10MHz clock for 8254
71 1= 1MHz clock for 8254
72 [4] - 0, 5=D/A output 0-5V (internal reference -5V)
73 1, 10=D/A output 0-10V (internal reference -10V)
bbc9a991 74 2 =D/A output unknown (external reference)
4da6a1d8
MD
75
76 Options for PCL-718:
77 [0] - IO Base
78 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
79 [2] - DMA (0=disable, 1, 3)
80 [3] - 0, 10=10MHz clock for 8254
81 1= 1MHz clock for 8254
82 [4] - 0=A/D Range is +/-10V
83 1= +/-5V
84 2= +/-2.5V
85 3= +/-1V
86 4= +/-0.5V
87 5= user defined bipolar
88 6= 0-10V
89 7= 0-5V
90 8= 0-2V
91 9= 0-1V
92 10= user defined unipolar
93 [5] - 0, 5=D/A outputs 0-5V (internal reference -5V)
94 1, 10=D/A outputs 0-10V (internal reference -10V)
bbc9a991 95 2=D/A outputs unknown (external reference)
4da6a1d8
MD
96 [6] - 0, 60=max 60kHz A/D sampling
97 1,100=max 100kHz A/D sampling (PCL-718 with Option 001 installed)
98
99*/
100
ce157f80 101#include <linux/module.h>
5a0e3ad6 102#include <linux/gfp.h>
4da6a1d8 103#include <linux/delay.h>
845d131e 104#include <linux/io.h>
aecfd1ec 105#include <linux/interrupt.h>
4da6a1d8 106
aecfd1ec
HS
107#include "../comedidev.h"
108
dd7c0f04 109#include "comedi_isadma.h"
27020ffe 110#include "comedi_fc.h"
4da6a1d8
MD
111#include "8253.h"
112
0109253d 113/* boards constants */
4da6a1d8
MD
114
115#define boardPCL818L 0
116#define boardPCL818H 1
117#define boardPCL818HD 2
118#define boardPCL818HG 3
119#define boardPCL818 4
120#define boardPCL718 5
121
1be6b015
HS
122/*
123 * Register I/O map
124 */
906a183f
HS
125#define PCL818_AI_LSB_REG 0x00
126#define PCL818_AI_MSB_REG 0x01
933ccd82
HS
127#define PCL818_RANGE_REG 0x01
128#define PCL818_MUX_REG 0x02
129#define PCL818_MUX_SCAN(_first, _last) (((_last) << 4) | (_first))
8d0b5e15 130#define PCL818_DO_DI_LSB_REG 0x03
93505573
HS
131#define PCL818_AO_LSB_REG(x) (0x04 + ((x) * 2))
132#define PCL818_AO_MSB_REG(x) (0x05 + ((x) * 2))
8d0b5e15
HS
133#define PCL818_STATUS_REG 0x08
134#define PCL818_STATUS_NEXT_CHAN_MASK (0xf << 0)
135#define PCL818_STATUS_INT (1 << 4)
136#define PCL818_STATUS_MUX (1 << 5)
137#define PCL818_STATUS_UNI (1 << 6)
138#define PCL818_STATUS_EOC (1 << 7)
5af366ff
HS
139#define PCL818_CTRL_REG 0x09
140#define PCL818_CTRL_DISABLE_TRIG (0 << 0)
141#define PCL818_CTRL_SOFT_TRIG (1 << 0)
142#define PCL818_CTRL_EXT_TRIG (2 << 0)
143#define PCL818_CTRL_PACER_TRIG (3 << 0)
144#define PCL818_CTRL_DMAE (1 << 2)
145#define PCL818_CTRL_IRQ(x) ((x) << 4)
146#define PCL818_CTRL_INTE (1 << 7)
1be6b015
HS
147#define PCL818_CNTENABLE_REG 0x0a
148#define PCL818_CNTENABLE_PACER_ENA (0 << 0)
149#define PCL818_CNTENABLE_PACER_TRIG0 (1 << 0)
150#define PCL818_CNTENABLE_CNT0_EXT_CLK (0 << 1)
151#define PCL818_CNTENABLE_CNT0_INT_CLK (1 << 1)
4ab490b3 152#define PCL818_DO_DI_MSB_REG 0x0b
833b458a 153#define PCL818_TIMER_BASE 0x0c
4da6a1d8 154
0109253d 155/* W: fifo enable/disable */
4da6a1d8 156#define PCL818_FI_ENABLE 6
0109253d 157/* W: fifo interrupt clear */
4da6a1d8 158#define PCL818_FI_INTCLR 20
0109253d 159/* W: fifo interrupt clear */
4da6a1d8 160#define PCL818_FI_FLUSH 25
0109253d 161/* R: fifo status */
4da6a1d8 162#define PCL818_FI_STATUS 25
0109253d 163/* R: one record from FIFO */
4da6a1d8 164#define PCL818_FI_DATALO 23
61ef4719 165#define PCL818_FI_DATAHI 24
4da6a1d8 166
4da6a1d8
MD
167#define MAGIC_DMA_WORD 0x5a5a
168
4cdd4eb2
HS
169static const struct comedi_lrange range_pcl818h_ai = {
170 9, {
171 BIP_RANGE(5),
172 BIP_RANGE(2.5),
173 BIP_RANGE(1.25),
174 BIP_RANGE(0.625),
175 UNI_RANGE(10),
176 UNI_RANGE(5),
177 UNI_RANGE(2.5),
178 UNI_RANGE(1.25),
179 BIP_RANGE(10)
180 }
4da6a1d8
MD
181};
182
4cdd4eb2
HS
183static const struct comedi_lrange range_pcl818hg_ai = {
184 10, {
185 BIP_RANGE(5),
186 BIP_RANGE(0.5),
187 BIP_RANGE(0.05),
188 BIP_RANGE(0.005),
189 UNI_RANGE(10),
190 UNI_RANGE(1),
191 UNI_RANGE(0.1),
192 UNI_RANGE(0.01),
193 BIP_RANGE(10),
194 BIP_RANGE(1),
195 BIP_RANGE(0.1),
196 BIP_RANGE(0.01)
197 }
4da6a1d8
MD
198};
199
4cdd4eb2
HS
200static const struct comedi_lrange range_pcl818l_l_ai = {
201 4, {
202 BIP_RANGE(5),
203 BIP_RANGE(2.5),
204 BIP_RANGE(1.25),
205 BIP_RANGE(0.625)
206 }
4da6a1d8
MD
207};
208
4cdd4eb2
HS
209static const struct comedi_lrange range_pcl818l_h_ai = {
210 4, {
211 BIP_RANGE(10),
212 BIP_RANGE(5),
213 BIP_RANGE(2.5),
214 BIP_RANGE(1.25)
215 }
216};
217
218static const struct comedi_lrange range718_bipolar1 = {
219 1, {
220 BIP_RANGE(1)
221 }
4da6a1d8
MD
222};
223
74c7c503 224static const struct comedi_lrange range718_bipolar0_5 = {
4cdd4eb2
HS
225 1, {
226 BIP_RANGE(0.5)
227 }
228};
229
230static const struct comedi_lrange range718_unipolar2 = {
231 1, {
232 UNI_RANGE(2)
233 }
234};
235
236static const struct comedi_lrange range718_unipolar1 = {
237 1, {
238 BIP_RANGE(1)
239 }
240};
4da6a1d8 241
4634b815 242struct pcl818_board {
43f1b6e9 243 const char *name;
43f1b6e9
HS
244 unsigned int ns_min;
245 int n_aochan;
43f1b6e9 246 const struct comedi_lrange *ai_range_type;
4ba4a2d3 247 unsigned int has_dma:1;
d6125588 248 unsigned int has_fifo:1;
9acf56f2 249 unsigned int is_818:1;
4634b815
BP
250};
251
43f1b6e9
HS
252static const struct pcl818_board boardtypes[] = {
253 {
254 .name = "pcl818l",
43f1b6e9
HS
255 .ns_min = 25000,
256 .n_aochan = 1,
43f1b6e9 257 .ai_range_type = &range_pcl818l_l_ai,
4ba4a2d3 258 .has_dma = 1,
43f1b6e9
HS
259 .is_818 = 1,
260 }, {
261 .name = "pcl818h",
43f1b6e9
HS
262 .ns_min = 10000,
263 .n_aochan = 1,
43f1b6e9 264 .ai_range_type = &range_pcl818h_ai,
4ba4a2d3 265 .has_dma = 1,
43f1b6e9
HS
266 .is_818 = 1,
267 }, {
268 .name = "pcl818hd",
43f1b6e9
HS
269 .ns_min = 10000,
270 .n_aochan = 1,
43f1b6e9 271 .ai_range_type = &range_pcl818h_ai,
4ba4a2d3 272 .has_dma = 1,
d6125588 273 .has_fifo = 1,
43f1b6e9
HS
274 .is_818 = 1,
275 }, {
276 .name = "pcl818hg",
43f1b6e9
HS
277 .ns_min = 10000,
278 .n_aochan = 1,
43f1b6e9 279 .ai_range_type = &range_pcl818hg_ai,
4ba4a2d3 280 .has_dma = 1,
d6125588 281 .has_fifo = 1,
43f1b6e9
HS
282 .is_818 = 1,
283 }, {
284 .name = "pcl818",
43f1b6e9
HS
285 .ns_min = 10000,
286 .n_aochan = 2,
43f1b6e9 287 .ai_range_type = &range_pcl818h_ai,
4ba4a2d3 288 .has_dma = 1,
43f1b6e9
HS
289 .is_818 = 1,
290 }, {
291 .name = "pcl718",
43f1b6e9
HS
292 .ns_min = 16000,
293 .n_aochan = 2,
43f1b6e9 294 .ai_range_type = &range_unipolar5,
4ba4a2d3 295 .has_dma = 1,
43f1b6e9
HS
296 }, {
297 .name = "pcm3718",
43f1b6e9 298 .ns_min = 10000,
43f1b6e9 299 .ai_range_type = &range_pcl818h_ai,
4ba4a2d3 300 .has_dma = 1,
43f1b6e9
HS
301 .is_818 = 1,
302 },
303};
304
087ea31b 305struct pcl818_private {
dd7c0f04 306 struct comedi_isadma *dma;
39eaedb6 307 unsigned int ns_min; /* manimal allowed delay between samples (in us) for actual card */
0109253d 308 int i8253_osc_base; /* 1/frequency of on board oscilator in ns */
0109253d
BP
309 unsigned int act_chanlist[16]; /* MUX setting for actual AI operations */
310 unsigned int act_chanlist_len; /* how long is actual MUX list */
311 unsigned int act_chanlist_pos; /* actual position in MUX list */
f4985a79
HS
312 unsigned int divisor1;
313 unsigned int divisor2;
e5143adb 314 unsigned int usefifo:1;
c8bc43ec 315 unsigned int ai_cmd_running:1;
905a8321 316 unsigned int ai_cmd_canceled:1;
087ea31b
BP
317};
318
f4985a79 319static void pcl818_start_pacer(struct comedi_device *dev, bool load_counters)
833b458a 320{
f4985a79 321 struct pcl818_private *devpriv = dev->private;
833b458a
HS
322 unsigned long timer_base = dev->iobase + PCL818_TIMER_BASE;
323
324 i8254_set_mode(timer_base, 0, 2, I8254_MODE2 | I8254_BINARY);
325 i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY);
326 udelay(1);
327
328 if (load_counters) {
f4985a79
HS
329 i8254_write(timer_base, 0, 2, devpriv->divisor2);
330 i8254_write(timer_base, 0, 1, devpriv->divisor1);
833b458a
HS
331 }
332}
4da6a1d8 333
ab72ef30 334static void pcl818_ai_setup_dma(struct comedi_device *dev,
421f1b68
HS
335 struct comedi_subdevice *s,
336 unsigned int unread_samples)
ab72ef30
HS
337{
338 struct pcl818_private *devpriv = dev->private;
dd7c0f04 339 struct comedi_isadma *dma = devpriv->dma;
421f1b68
HS
340 struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
341 unsigned int max_samples = comedi_bytes_to_samples(s, desc->maxsize);
296e3f18 342 unsigned int nsamples;
dd7c0f04
HS
343
344 comedi_isadma_disable(dma->chan);
9cf2765f 345
296e3f18
HS
346 /*
347 * Determine dma size based on the buffer maxsize plus the number of
348 * unread samples and the number of samples remaining in the command.
349 */
421f1b68 350 nsamples = comedi_nsamples_left(s, max_samples + unread_samples);
296e3f18
HS
351 if (nsamples > unread_samples) {
352 nsamples -= unread_samples;
353 desc->size = comedi_samples_to_bytes(s, nsamples);
dd7c0f04 354 comedi_isadma_program(desc);
ab72ef30 355 }
ab72ef30
HS
356}
357
933ccd82
HS
358static void pcl818_ai_set_chan_range(struct comedi_device *dev,
359 unsigned int chan,
360 unsigned int range)
361{
362 outb(chan, dev->iobase + PCL818_MUX_REG);
363 outb(range, dev->iobase + PCL818_RANGE_REG);
364}
365
366static void pcl818_ai_set_chan_scan(struct comedi_device *dev,
367 unsigned int first_chan,
368 unsigned int last_chan)
369{
370 outb(PCL818_MUX_SCAN(first_chan, last_chan),
371 dev->iobase + PCL818_MUX_REG);
372}
373
374static void pcl818_ai_setup_chanlist(struct comedi_device *dev,
375 unsigned int *chanlist,
376 unsigned int seglen)
377{
378 struct pcl818_private *devpriv = dev->private;
379 unsigned int first_chan = CR_CHAN(chanlist[0]);
380 unsigned int last_chan;
381 unsigned int range;
382 int i;
383
384 devpriv->act_chanlist_len = seglen;
385 devpriv->act_chanlist_pos = 0;
386
387 /* store range list to card */
388 for (i = 0; i < seglen; i++) {
389 last_chan = CR_CHAN(chanlist[i]);
390 range = CR_RANGE(chanlist[i]);
391
392 devpriv->act_chanlist[i] = last_chan;
393
394 pcl818_ai_set_chan_range(dev, last_chan, range);
395 }
396
397 udelay(1);
398
399 pcl818_ai_set_chan_scan(dev, first_chan, last_chan);
400}
401
9fd3effa
HS
402static void pcl818_ai_clear_eoc(struct comedi_device *dev)
403{
404 /* writing any value clears the interrupt request */
8d0b5e15 405 outb(0, dev->iobase + PCL818_STATUS_REG);
9fd3effa
HS
406}
407
9fdef9c8
HS
408static void pcl818_ai_soft_trig(struct comedi_device *dev)
409{
410 /* writing any value triggers a software conversion */
906a183f 411 outb(0, dev->iobase + PCL818_AI_LSB_REG);
9fdef9c8 412}
9fd3effa 413
4206e1be
HS
414static unsigned int pcl818_ai_get_fifo_sample(struct comedi_device *dev,
415 struct comedi_subdevice *s,
416 unsigned int *chan)
417{
418 unsigned int val;
419
420 val = inb(dev->iobase + PCL818_FI_DATALO);
421 val |= (inb(dev->iobase + PCL818_FI_DATAHI) << 8);
422
423 if (chan)
424 *chan = val & 0xf;
425
426 return (val >> 4) & s->maxdata;
427}
428
8fc9f652
HS
429static unsigned int pcl818_ai_get_sample(struct comedi_device *dev,
430 struct comedi_subdevice *s,
431 unsigned int *chan)
432{
433 unsigned int val;
434
906a183f
HS
435 val = inb(dev->iobase + PCL818_AI_MSB_REG) << 8;
436 val |= inb(dev->iobase + PCL818_AI_LSB_REG);
8fc9f652
HS
437
438 if (chan)
439 *chan = val & 0xf;
440
441 return (val >> 4) & s->maxdata;
442}
443
1d6f4af9
HS
444static int pcl818_ai_eoc(struct comedi_device *dev,
445 struct comedi_subdevice *s,
446 struct comedi_insn *insn,
447 unsigned long context)
448{
449 unsigned int status;
450
8d0b5e15
HS
451 status = inb(dev->iobase + PCL818_STATUS_REG);
452 if (status & PCL818_STATUS_INT)
1d6f4af9
HS
453 return 0;
454 return -EBUSY;
455}
456
d615416d
HS
457static bool pcl818_ai_write_sample(struct comedi_device *dev,
458 struct comedi_subdevice *s,
459 unsigned int chan, unsigned int val)
a8f461c2
HS
460{
461 struct pcl818_private *devpriv = dev->private;
d615416d 462 struct comedi_cmd *cmd = &s->async->cmd;
a8f461c2
HS
463 unsigned int expected_chan;
464
465 expected_chan = devpriv->act_chanlist[devpriv->act_chanlist_pos];
466 if (chan != expected_chan) {
467 dev_dbg(dev->class_dev,
468 "A/D mode1/3 %s - channel dropout %d!=%d !\n",
469 (devpriv->dma) ? "DMA" :
470 (devpriv->usefifo) ? "FIFO" : "IRQ",
471 chan, expected_chan);
3e6cb74f 472 s->async->events |= COMEDI_CB_ERROR;
d615416d 473 return false;
a8f461c2 474 }
a8f461c2 475
d615416d 476 comedi_buf_write_samples(s, &val, 1);
6d5b7858 477
6d5b7858
HS
478 devpriv->act_chanlist_pos++;
479 if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
480 devpriv->act_chanlist_pos = 0;
481
c76844e1
HS
482 if (cmd->stop_src == TRIG_COUNT &&
483 s->async->scans_done >= cmd->stop_arg) {
6d5b7858 484 s->async->events |= COMEDI_CB_EOA;
6d5b7858
HS
485 return false;
486 }
487
488 return true;
489}
490
e0946940
HS
491static void pcl818_handle_eoc(struct comedi_device *dev,
492 struct comedi_subdevice *s)
4da6a1d8 493{
8fc9f652 494 unsigned int chan;
a8f461c2 495 unsigned int val;
4da6a1d8 496
12700571 497 if (pcl818_ai_eoc(dev, s, NULL, 0)) {
f25a9ba2 498 dev_err(dev->class_dev, "A/D mode1/3 IRQ without DRDY!\n");
3e6cb74f 499 s->async->events |= COMEDI_CB_ERROR;
e0946940 500 return;
4da6a1d8 501 }
12700571 502
a8f461c2 503 val = pcl818_ai_get_sample(dev, s, &chan);
d615416d 504 pcl818_ai_write_sample(dev, s, chan, val);
4da6a1d8
MD
505}
506
e0946940
HS
507static void pcl818_handle_dma(struct comedi_device *dev,
508 struct comedi_subdevice *s)
4da6a1d8 509{
9a1a6cf8 510 struct pcl818_private *devpriv = dev->private;
dd7c0f04
HS
511 struct comedi_isadma *dma = devpriv->dma;
512 struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
513 unsigned short *ptr = desc->virt_addr;
514 unsigned int nsamples = comedi_bytes_to_samples(s, desc->size);
a8f461c2
HS
515 unsigned int chan;
516 unsigned int val;
1c53093a 517 int i;
4da6a1d8 518
421f1b68
HS
519 /* restart dma with the next buffer */
520 dma->cur_dma = 1 - dma->cur_dma;
521 pcl818_ai_setup_dma(dev, s, nsamples);
4da6a1d8 522
1c53093a
HS
523 for (i = 0; i < nsamples; i++) {
524 val = ptr[i];
a8f461c2
HS
525 chan = val & 0xf;
526 val = (val >> 4) & s->maxdata;
d615416d 527 if (!pcl818_ai_write_sample(dev, s, chan, val))
e0946940 528 break;
4da6a1d8 529 }
4da6a1d8
MD
530}
531
e0946940
HS
532static void pcl818_handle_fifo(struct comedi_device *dev,
533 struct comedi_subdevice *s)
4da6a1d8 534{
4206e1be
HS
535 unsigned int status;
536 unsigned int chan;
537 unsigned int val;
4bf59ce2 538 int i, len;
4da6a1d8 539
4206e1be 540 status = inb(dev->iobase + PCL818_FI_STATUS);
4da6a1d8 541
4206e1be 542 if (status & 4) {
f25a9ba2 543 dev_err(dev->class_dev, "A/D mode1/3 FIFO overflow!\n");
3e6cb74f 544 s->async->events |= COMEDI_CB_ERROR;
e0946940 545 return;
4da6a1d8
MD
546 }
547
4206e1be 548 if (status & 1) {
f25a9ba2
HS
549 dev_err(dev->class_dev,
550 "A/D mode1/3 FIFO interrupt without data!\n");
3e6cb74f 551 s->async->events |= COMEDI_CB_ERROR;
e0946940 552 return;
4da6a1d8
MD
553 }
554
4206e1be 555 if (status & 2)
4da6a1d8 556 len = 512;
fc950139 557 else
4da6a1d8 558 len = 0;
4da6a1d8
MD
559
560 for (i = 0; i < len; i++) {
4206e1be 561 val = pcl818_ai_get_fifo_sample(dev, s, &chan);
d615416d 562 if (!pcl818_ai_write_sample(dev, s, chan, val))
e0946940 563 break;
4da6a1d8 564 }
4da6a1d8
MD
565}
566
e0946940 567static irqreturn_t pcl818_interrupt(int irq, void *d)
4da6a1d8 568{
71b5f4f1 569 struct comedi_device *dev = d;
9a1a6cf8 570 struct pcl818_private *devpriv = dev->private;
6c42119d 571 struct comedi_subdevice *s = dev->read_subdev;
c76844e1 572 struct comedi_cmd *cmd = &s->async->cmd;
4da6a1d8 573
799f89ce 574 if (!dev->attached || !devpriv->ai_cmd_running) {
9fd3effa 575 pcl818_ai_clear_eoc(dev);
4da6a1d8
MD
576 return IRQ_HANDLED;
577 }
4da6a1d8 578
905a8321
HS
579 if (devpriv->ai_cmd_canceled) {
580 /*
581 * The cleanup from ai_cancel() has been delayed
582 * until now because the card doesn't seem to like
583 * being reprogrammed while a DMA transfer is in
584 * progress.
585 */
c76844e1 586 s->async->scans_done = cmd->stop_arg;
905a8321 587 s->cancel(dev, s);
e21de1a8
IA
588 return IRQ_HANDLED;
589 }
590
799f89ce 591 if (devpriv->dma)
e0946940 592 pcl818_handle_dma(dev, s);
799f89ce 593 else if (devpriv->usefifo)
e0946940 594 pcl818_handle_fifo(dev, s);
799f89ce 595 else
e0946940
HS
596 pcl818_handle_eoc(dev, s);
597
9fd3effa 598 pcl818_ai_clear_eoc(dev);
e0946940 599
11e7c09a 600 comedi_handle_events(dev, s);
e0946940 601 return IRQ_HANDLED;
4da6a1d8
MD
602}
603
0a85b6f0
MT
604static int check_channel_list(struct comedi_device *dev,
605 struct comedi_subdevice *s,
606 unsigned int *chanlist, unsigned int n_chan)
4da6a1d8
MD
607{
608 unsigned int chansegment[16];
609 unsigned int i, nowmustbechan, seglen, segpos;
610
611 /* correct channel and range number check itself comedi/range.c */
612 if (n_chan < 1) {
f25a9ba2 613 dev_err(dev->class_dev, "range/channel list is empty!\n");
4da6a1d8
MD
614 return 0;
615 }
616
617 if (n_chan > 1) {
25985edc 618 /* first channel is every time ok */
4da6a1d8 619 chansegment[0] = chanlist[0];
0109253d 620 /* build part of chanlist */
4da6a1d8 621 for (i = 1, seglen = 1; i < n_chan; i++, seglen++) {
0109253d
BP
622 /* we detect loop, this must by finish */
623
4da6a1d8
MD
624 if (chanlist[0] == chanlist[i])
625 break;
626 nowmustbechan =
0a85b6f0 627 (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
25985edc 628 if (nowmustbechan != CR_CHAN(chanlist[i])) { /* channel list isn't continuous :-( */
84f03cf1
HS
629 dev_dbg(dev->class_dev,
630 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
631 i, CR_CHAN(chanlist[i]), nowmustbechan,
632 CR_CHAN(chanlist[0]));
4da6a1d8
MD
633 return 0;
634 }
0109253d 635 /* well, this is next correct channel in list */
4da6a1d8
MD
636 chansegment[i] = chanlist[i];
637 }
638
0109253d 639 /* check whole chanlist */
4da6a1d8 640 for (i = 0, segpos = 0; i < n_chan; i++) {
4da6a1d8 641 if (chanlist[i] != chansegment[i % seglen]) {
84f03cf1
HS
642 dev_dbg(dev->class_dev,
643 "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
644 i, CR_CHAN(chansegment[i]),
645 CR_RANGE(chansegment[i]),
646 CR_AREF(chansegment[i]),
647 CR_CHAN(chanlist[i % seglen]),
648 CR_RANGE(chanlist[i % seglen]),
649 CR_AREF(chansegment[i % seglen]));
0109253d 650 return 0; /* chan/gain list is strange */
4da6a1d8
MD
651 }
652 }
653 } else {
654 seglen = 1;
655 }
4da6a1d8
MD
656 return seglen;
657}
658
4da6a1d8
MD
659static int check_single_ended(unsigned int port)
660{
8d0b5e15 661 if (inb(port + PCL818_STATUS_REG) & PCL818_STATUS_MUX)
4da6a1d8 662 return 1;
fc950139 663 return 0;
4da6a1d8
MD
664}
665
da91b269 666static int ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 667 struct comedi_cmd *cmd)
4da6a1d8 668{
dc453864 669 const struct pcl818_board *board = dev->board_ptr;
9a1a6cf8 670 struct pcl818_private *devpriv = dev->private;
4da6a1d8 671 int err = 0;
863731fd 672 unsigned int arg;
4da6a1d8 673
27020ffe 674 /* Step 1 : check if triggers are trivially valid */
4da6a1d8 675
27020ffe
HS
676 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
677 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
678 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
679 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
680 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
4da6a1d8 681
fc950139 682 if (err)
4da6a1d8 683 return 1;
4da6a1d8 684
27020ffe 685 /* Step 2a : make sure trigger sources are unique */
4da6a1d8 686
27020ffe
HS
687 err |= cfc_check_trigger_is_unique(cmd->convert_src);
688 err |= cfc_check_trigger_is_unique(cmd->stop_src);
4da6a1d8 689
27020ffe 690 /* Step 2b : and mutually compatible */
4da6a1d8 691
fc950139 692 if (err)
4da6a1d8 693 return 2;
4da6a1d8 694
8efdc1bf 695 /* Step 3: check if arguments are trivially valid */
4da6a1d8 696
8efdc1bf
HS
697 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
698 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
4da6a1d8 699
8efdc1bf
HS
700 if (cmd->convert_src == TRIG_TIMER)
701 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
702 board->ns_min);
703 else /* TRIG_EXT */
704 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
4da6a1d8 705
8efdc1bf 706 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
4da6a1d8 707
8efdc1bf
HS
708 if (cmd->stop_src == TRIG_COUNT)
709 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
710 else /* TRIG_NONE */
711 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
4da6a1d8 712
fc950139 713 if (err)
4da6a1d8 714 return 3;
4da6a1d8
MD
715
716 /* step 4: fix up any arguments */
717
718 if (cmd->convert_src == TRIG_TIMER) {
863731fd 719 arg = cmd->convert_arg;
cb9cfd7e 720 i8253_cascade_ns_to_timer(devpriv->i8253_osc_base,
f4985a79
HS
721 &devpriv->divisor1,
722 &devpriv->divisor2,
863731fd
HS
723 &arg, cmd->flags);
724 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
4da6a1d8
MD
725 }
726
fc950139 727 if (err)
4da6a1d8 728 return 4;
4da6a1d8
MD
729
730 /* step 5: complain about special chanlist considerations */
731
732 if (cmd->chanlist) {
733 if (!check_channel_list(dev, s, cmd->chanlist,
0a85b6f0 734 cmd->chanlist_len))
0109253d 735 return 5; /* incorrect channels list */
4da6a1d8
MD
736 }
737
738 return 0;
739}
740
d5f87436
HS
741static int pcl818_ai_cmd(struct comedi_device *dev,
742 struct comedi_subdevice *s)
4da6a1d8 743{
9a1a6cf8 744 struct pcl818_private *devpriv = dev->private;
421f1b68 745 struct comedi_isadma *dma = devpriv->dma;
ea6d0d4c 746 struct comedi_cmd *cmd = &s->async->cmd;
d5f87436
HS
747 unsigned int ctrl = 0;
748 unsigned int seglen;
749
750 if (devpriv->ai_cmd_running)
751 return -EBUSY;
752
753 pcl818_start_pacer(dev, false);
754
755 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
756 if (seglen < 1)
757 return -EINVAL;
758 pcl818_ai_setup_chanlist(dev, cmd->chanlist, seglen);
4da6a1d8 759
d5f87436
HS
760 devpriv->ai_cmd_running = 1;
761 devpriv->ai_cmd_canceled = 0;
762 devpriv->act_chanlist_pos = 0;
4da6a1d8 763
d5f87436
HS
764 if (cmd->convert_src == TRIG_TIMER)
765 ctrl |= PCL818_CTRL_PACER_TRIG;
766 else
767 ctrl |= PCL818_CTRL_EXT_TRIG;
768
1be6b015 769 outb(PCL818_CNTENABLE_PACER_ENA, dev->iobase + PCL818_CNTENABLE_REG);
d5f87436 770
421f1b68
HS
771 if (dma) {
772 /* setup and enable dma for the first buffer */
773 dma->cur_dma = 0;
774 pcl818_ai_setup_dma(dev, s, 0);
d5f87436
HS
775
776 ctrl |= PCL818_CTRL_INTE | PCL818_CTRL_IRQ(dev->irq) |
777 PCL818_CTRL_DMAE;
778 } else if (devpriv->usefifo) {
779 /* enable FIFO */
780 outb(1, dev->iobase + PCL818_FI_ENABLE);
781 } else {
782 ctrl |= PCL818_CTRL_INTE | PCL818_CTRL_IRQ(dev->irq);
4da6a1d8 783 }
d5f87436 784 outb(ctrl, dev->iobase + PCL818_CTRL_REG);
4da6a1d8 785
d5f87436
HS
786 if (cmd->convert_src == TRIG_TIMER)
787 pcl818_start_pacer(dev, true);
788
789 return 0;
4da6a1d8
MD
790}
791
0a85b6f0
MT
792static int pcl818_ai_cancel(struct comedi_device *dev,
793 struct comedi_subdevice *s)
4da6a1d8 794{
9a1a6cf8 795 struct pcl818_private *devpriv = dev->private;
dd7c0f04 796 struct comedi_isadma *dma = devpriv->dma;
00aba6e7 797 struct comedi_cmd *cmd = &s->async->cmd;
9a1a6cf8 798
2850b1c7
HS
799 if (!devpriv->ai_cmd_running)
800 return 0;
801
dd7c0f04 802 if (dma) {
2850b1c7 803 if (cmd->stop_src == TRIG_NONE ||
c76844e1
HS
804 (cmd->stop_src == TRIG_COUNT &&
805 s->async->scans_done < cmd->stop_arg)) {
905a8321
HS
806 if (!devpriv->ai_cmd_canceled) {
807 /*
808 * Wait for running dma transfer to end,
809 * do cleanup in interrupt.
810 */
811 devpriv->ai_cmd_canceled = 1;
812 return 0;
813 }
4da6a1d8 814 }
dd7c0f04 815 comedi_isadma_disable(dma->chan);
4da6a1d8
MD
816 }
817
5af366ff 818 outb(PCL818_CTRL_DISABLE_TRIG, dev->iobase + PCL818_CTRL_REG);
799f89ce 819 pcl818_start_pacer(dev, false);
9fd3effa
HS
820 pcl818_ai_clear_eoc(dev);
821
799f89ce
HS
822 if (devpriv->usefifo) { /* FIFO shutdown */
823 outb(0, dev->iobase + PCL818_FI_INTCLR);
824 outb(0, dev->iobase + PCL818_FI_FLUSH);
825 outb(0, dev->iobase + PCL818_FI_ENABLE);
826 }
827 devpriv->ai_cmd_running = 0;
828 devpriv->ai_cmd_canceled = 0;
829
4da6a1d8
MD
830 return 0;
831}
832
8916f5bc
HS
833static int pcl818_ai_insn_read(struct comedi_device *dev,
834 struct comedi_subdevice *s,
835 struct comedi_insn *insn,
836 unsigned int *data)
837{
838 unsigned int chan = CR_CHAN(insn->chanspec);
839 unsigned int range = CR_RANGE(insn->chanspec);
840 int ret = 0;
841 int i;
842
5af366ff 843 outb(PCL818_CTRL_SOFT_TRIG, dev->iobase + PCL818_CTRL_REG);
8916f5bc 844
933ccd82
HS
845 pcl818_ai_set_chan_range(dev, chan, range);
846 pcl818_ai_set_chan_scan(dev, chan, chan);
8916f5bc
HS
847
848 for (i = 0; i < insn->n; i++) {
9fd3effa 849 pcl818_ai_clear_eoc(dev);
9fdef9c8 850 pcl818_ai_soft_trig(dev);
8916f5bc
HS
851
852 ret = comedi_timeout(dev, s, insn, pcl818_ai_eoc, 0);
853 if (ret)
854 break;
855
856 data[i] = pcl818_ai_get_sample(dev, s, NULL);
857 }
9fd3effa 858 pcl818_ai_clear_eoc(dev);
8916f5bc
HS
859
860 return ret ? ret : insn->n;
861}
862
93505573
HS
863static int pcl818_ao_insn_write(struct comedi_device *dev,
864 struct comedi_subdevice *s,
865 struct comedi_insn *insn,
866 unsigned int *data)
867{
93505573 868 unsigned int chan = CR_CHAN(insn->chanspec);
073d4632 869 unsigned int val = s->readback[chan];
93505573
HS
870 int i;
871
872 for (i = 0; i < insn->n; i++) {
073d4632
HS
873 val = data[i];
874 outb((val & 0x000f) << 4,
93505573 875 dev->iobase + PCL818_AO_LSB_REG(chan));
073d4632 876 outb((val & 0x0ff0) >> 4,
93505573
HS
877 dev->iobase + PCL818_AO_MSB_REG(chan));
878 }
073d4632 879 s->readback[chan] = val;
93505573
HS
880
881 return insn->n;
882}
883
4ab490b3
HS
884static int pcl818_di_insn_bits(struct comedi_device *dev,
885 struct comedi_subdevice *s,
886 struct comedi_insn *insn,
887 unsigned int *data)
888{
889 data[1] = inb(dev->iobase + PCL818_DO_DI_LSB_REG) |
890 (inb(dev->iobase + PCL818_DO_DI_MSB_REG) << 8);
891
892 return insn->n;
893}
894
895static int pcl818_do_insn_bits(struct comedi_device *dev,
896 struct comedi_subdevice *s,
897 struct comedi_insn *insn,
898 unsigned int *data)
899{
900 if (comedi_dio_update_state(s, data)) {
901 outb(s->state & 0xff, dev->iobase + PCL818_DO_DI_LSB_REG);
902 outb((s->state >> 8), dev->iobase + PCL818_DO_DI_MSB_REG);
903 }
904
905 data[1] = s->state;
906
907 return insn->n;
908}
909
da91b269 910static void pcl818_reset(struct comedi_device *dev)
4da6a1d8 911{
dc453864 912 const struct pcl818_board *board = dev->board_ptr;
833b458a 913 unsigned long timer_base = dev->iobase + PCL818_TIMER_BASE;
bc7d7fc0 914 unsigned int chan;
dd8a4b47 915
80cc6486
HS
916 /* flush and disable the FIFO */
917 if (board->has_fifo) {
4da6a1d8
MD
918 outb(0, dev->iobase + PCL818_FI_INTCLR);
919 outb(0, dev->iobase + PCL818_FI_FLUSH);
920 outb(0, dev->iobase + PCL818_FI_ENABLE);
921 }
bc7d7fc0
HS
922
923 /* disable analog input trigger */
5af366ff 924 outb(PCL818_CTRL_DISABLE_TRIG, dev->iobase + PCL818_CTRL_REG);
9fd3effa 925 pcl818_ai_clear_eoc(dev);
833b458a 926
bc7d7fc0
HS
927 pcl818_ai_set_chan_range(dev, 0, 0);
928
929 /* stop pacer */
930 outb(PCL818_CNTENABLE_PACER_ENA, dev->iobase + PCL818_CNTENABLE_REG);
833b458a
HS
931 i8254_set_mode(timer_base, 0, 2, I8254_MODE0 | I8254_BINARY);
932 i8254_set_mode(timer_base, 0, 1, I8254_MODE0 | I8254_BINARY);
933 i8254_set_mode(timer_base, 0, 0, I8254_MODE0 | I8254_BINARY);
934
bc7d7fc0
HS
935 /* set analog output channels to 0V */
936 for (chan = 0; chan < board->n_aochan; chan++) {
937 outb(0, dev->iobase + PCL818_AO_LSB_REG(chan));
938 outb(0, dev->iobase + PCL818_AO_MSB_REG(chan));
4da6a1d8 939 }
bc7d7fc0
HS
940
941 /* set all digital outputs low */
942 outb(0, dev->iobase + PCL818_DO_DI_MSB_REG);
943 outb(0, dev->iobase + PCL818_DO_DI_LSB_REG);
4da6a1d8
MD
944}
945
f39b8ccf
HS
946static void pcl818_set_ai_range_table(struct comedi_device *dev,
947 struct comedi_subdevice *s,
948 struct comedi_devconfig *it)
949{
dc453864 950 const struct pcl818_board *board = dev->board_ptr;
f39b8ccf
HS
951
952 /* default to the range table from the boardinfo */
953 s->range_table = board->ai_range_type;
954
955 /* now check the user config option based on the boardtype */
956 if (board->is_818) {
957 if (it->options[4] == 1 || it->options[4] == 10) {
958 /* secondary range list jumper selectable */
959 s->range_table = &range_pcl818l_h_ai;
960 }
961 } else {
962 switch (it->options[4]) {
963 case 0:
964 s->range_table = &range_bipolar10;
965 break;
966 case 1:
967 s->range_table = &range_bipolar5;
968 break;
969 case 2:
970 s->range_table = &range_bipolar2_5;
971 break;
972 case 3:
973 s->range_table = &range718_bipolar1;
974 break;
975 case 4:
976 s->range_table = &range718_bipolar0_5;
977 break;
978 case 6:
979 s->range_table = &range_unipolar10;
980 break;
981 case 7:
982 s->range_table = &range_unipolar5;
983 break;
984 case 8:
985 s->range_table = &range718_unipolar2;
986 break;
987 case 9:
988 s->range_table = &range718_unipolar1;
989 break;
990 default:
991 s->range_table = &range_unknown;
992 break;
993 }
994 }
995}
996
dd7c0f04 997static void pcl818_alloc_dma(struct comedi_device *dev, unsigned int dma_chan)
655cd940
HS
998{
999 struct pcl818_private *devpriv = dev->private;
655cd940 1000
dd7c0f04 1001 /* only DMA channels 3 and 1 are valid */
655cd940 1002 if (!(dma_chan == 3 || dma_chan == 1))
dd7c0f04 1003 return;
655cd940 1004
dd7c0f04
HS
1005 /* DMA uses two 16K buffers */
1006 devpriv->dma = comedi_isadma_alloc(dev, 2, dma_chan, dma_chan,
1007 PAGE_SIZE * 4, COMEDI_ISADMA_READ);
655cd940
HS
1008}
1009
665ee190
HS
1010static void pcl818_free_dma(struct comedi_device *dev)
1011{
1012 struct pcl818_private *devpriv = dev->private;
665ee190 1013
dd7c0f04
HS
1014 if (devpriv)
1015 comedi_isadma_free(devpriv->dma);
665ee190
HS
1016}
1017
da91b269 1018static int pcl818_attach(struct comedi_device *dev, struct comedi_devconfig *it)
4da6a1d8 1019{
dc453864 1020 const struct pcl818_board *board = dev->board_ptr;
9a1a6cf8 1021 struct pcl818_private *devpriv;
34c43922 1022 struct comedi_subdevice *s;
f5cc425a 1023 int ret;
4da6a1d8 1024
0bdab509 1025 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
c34fa261
HS
1026 if (!devpriv)
1027 return -ENOMEM;
4da6a1d8 1028
d6125588 1029 ret = comedi_request_region(dev, it->options[0],
80cc6486 1030 board->has_fifo ? 0x20 : 0x10);
d6c5ec04
HS
1031 if (ret)
1032 return ret;
4da6a1d8 1033
8356d4b4
HS
1034 /* we can use IRQ 2-7 for async command support */
1035 if (it->options[1] >= 2 && it->options[1] <= 7) {
e0946940 1036 ret = request_irq(it->options[1], pcl818_interrupt, 0,
e30b22a9 1037 dev->board_name, dev);
35a8735d 1038 if (ret == 0)
e30b22a9 1039 dev->irq = it->options[1];
4da6a1d8
MD
1040 }
1041
80cc6486
HS
1042 /* should we use the FIFO? */
1043 if (dev->irq && board->has_fifo && it->options[2] == -1)
1044 devpriv->usefifo = 1;
1045
4ba4a2d3 1046 /* we need an IRQ to do DMA on channel 3 or 1 */
dd7c0f04
HS
1047 if (dev->irq && board->has_dma)
1048 pcl818_alloc_dma(dev, it->options[2]);
4da6a1d8 1049
2f0b9d08 1050 ret = comedi_alloc_subdevices(dev, 4);
8b6c5694 1051 if (ret)
4da6a1d8
MD
1052 return ret;
1053
9fab6123 1054 s = &dev->subdevices[0];
9c06c4e3
HS
1055 s->type = COMEDI_SUBD_AI;
1056 s->subdev_flags = SDF_READABLE;
1057 if (check_single_ended(dev->iobase)) {
1058 s->n_chan = 16;
1059 s->subdev_flags |= SDF_COMMON | SDF_GROUND;
4da6a1d8 1060 } else {
9c06c4e3
HS
1061 s->n_chan = 8;
1062 s->subdev_flags |= SDF_DIFF;
1063 }
bca1b594 1064 s->maxdata = 0x0fff;
9c06c4e3
HS
1065
1066 pcl818_set_ai_range_table(dev, s, it);
1067
1068 s->insn_read = pcl818_ai_insn_read;
1069 if (dev->irq) {
1070 dev->read_subdev = s;
1071 s->subdev_flags |= SDF_CMD_READ;
1072 s->len_chanlist = s->n_chan;
1073 s->do_cmdtest = ai_cmdtest;
d5f87436 1074 s->do_cmd = pcl818_ai_cmd;
9c06c4e3 1075 s->cancel = pcl818_ai_cancel;
4da6a1d8
MD
1076 }
1077
93505573 1078 /* Analog Output subdevice */
9fab6123 1079 s = &dev->subdevices[1];
93505573
HS
1080 if (board->n_aochan) {
1081 s->type = COMEDI_SUBD_AO;
1082 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1083 s->n_chan = board->n_aochan;
1084 s->maxdata = 0x0fff;
1085 s->range_table = &range_unipolar5;
dd8a4b47 1086 if (board->is_818) {
4da6a1d8
MD
1087 if ((it->options[4] == 1) || (it->options[4] == 10))
1088 s->range_table = &range_unipolar10;
1089 if (it->options[4] == 2)
1090 s->range_table = &range_unknown;
1091 } else {
1092 if ((it->options[5] == 1) || (it->options[5] == 10))
1093 s->range_table = &range_unipolar10;
1094 if (it->options[5] == 2)
1095 s->range_table = &range_unknown;
1096 }
073d4632 1097 s->insn_write = pcl818_ao_insn_write;
073d4632
HS
1098
1099 ret = comedi_alloc_subdev_readback(s);
1100 if (ret)
1101 return ret;
93505573
HS
1102 } else {
1103 s->type = COMEDI_SUBD_UNUSED;
4da6a1d8
MD
1104 }
1105
03d98e6c 1106 /* Digital Input subdevice */
9fab6123 1107 s = &dev->subdevices[2];
03d98e6c
HS
1108 s->type = COMEDI_SUBD_DI;
1109 s->subdev_flags = SDF_READABLE;
1110 s->n_chan = 16;
1111 s->maxdata = 1;
1112 s->range_table = &range_digital;
1113 s->insn_bits = pcl818_di_insn_bits;
1114
1115 /* Digital Output subdevice */
9fab6123 1116 s = &dev->subdevices[3];
03d98e6c
HS
1117 s->type = COMEDI_SUBD_DO;
1118 s->subdev_flags = SDF_WRITABLE;
1119 s->n_chan = 16;
1120 s->maxdata = 1;
1121 s->range_table = &range_digital;
1122 s->insn_bits = pcl818_do_insn_bits;
4da6a1d8
MD
1123
1124 /* select 1/10MHz oscilator */
fc950139 1125 if ((it->options[3] == 0) || (it->options[3] == 10))
cb9cfd7e 1126 devpriv->i8253_osc_base = I8254_OSC_BASE_10MHZ;
fc950139 1127 else
cb9cfd7e 1128 devpriv->i8253_osc_base = I8254_OSC_BASE_1MHZ;
4da6a1d8
MD
1129
1130 /* max sampling speed */
dd8a4b47 1131 devpriv->ns_min = board->ns_min;
4da6a1d8 1132
dd8a4b47 1133 if (!board->is_818) {
4da6a1d8
MD
1134 if ((it->options[6] == 1) || (it->options[6] == 100))
1135 devpriv->ns_min = 10000; /* extended PCL718 to 100kHz DAC */
1136 }
1137
1138 pcl818_reset(dev);
1139
4da6a1d8
MD
1140 return 0;
1141}
1142
484ecc95 1143static void pcl818_detach(struct comedi_device *dev)
4da6a1d8 1144{
9a1a6cf8
HS
1145 struct pcl818_private *devpriv = dev->private;
1146
1147 if (devpriv) {
89dac49e 1148 pcl818_ai_cancel(dev, dev->read_subdev);
484ecc95 1149 pcl818_reset(dev);
484ecc95 1150 }
665ee190 1151 pcl818_free_dma(dev);
a32c6d00 1152 comedi_legacy_detach(dev);
4da6a1d8 1153}
90f703d3 1154
294f930d 1155static struct comedi_driver pcl818_driver = {
f6aafa10
HS
1156 .driver_name = "pcl818",
1157 .module = THIS_MODULE,
1158 .attach = pcl818_attach,
1159 .detach = pcl818_detach,
1160 .board_name = &boardtypes[0].name,
1161 .num_names = ARRAY_SIZE(boardtypes),
1162 .offset = sizeof(struct pcl818_board),
1163};
294f930d 1164module_comedi_driver(pcl818_driver);
f6aafa10 1165
90f703d3
AT
1166MODULE_AUTHOR("Comedi http://www.comedi.org");
1167MODULE_DESCRIPTION("Comedi low-level driver");
1168MODULE_LICENSE("GPL");