staging: comedi: pcl816: simplify the dma->size calculations
[firefly-linux-kernel-4.4.55.git] / drivers / staging / comedi / drivers / pcl816.c
1 /*
2    comedi/drivers/pcl816.c
3
4    Author:  Juan Grigera <juan@grigera.com.ar>
5             based on pcl818 by Michal Dobes <dobes@tesnet.cz> and bits of pcl812
6
7    hardware driver for Advantech cards:
8     card:   PCL-816, PCL814B
9     driver: pcl816
10 */
11 /*
12 Driver: pcl816
13 Description: Advantech PCL-816 cards, PCL-814
14 Author: Juan Grigera <juan@grigera.com.ar>
15 Devices: [Advantech] PCL-816 (pcl816), PCL-814B (pcl814b)
16 Status: works
17 Updated: Tue,  2 Apr 2002 23:15:21 -0800
18
19 PCL 816 and 814B have 16 SE/DIFF ADCs, 16 DACs, 16 DI and 16 DO.
20 Differences are at resolution (16 vs 12 bits).
21
22 The driver support AI command mode, other subdevices not written.
23
24 Analog output and digital input and output are not supported.
25
26 Configuration Options:
27   [0] - IO Base
28   [1] - IRQ     (0=disable, 2, 3, 4, 5, 6, 7)
29   [2] - DMA     (0=disable, 1, 3)
30   [3] - 0, 10=10MHz clock for 8254
31             1= 1MHz clock for 8254
32
33 */
34
35 #include <linux/module.h>
36 #include <linux/gfp.h>
37 #include <linux/delay.h>
38 #include <linux/io.h>
39 #include <linux/interrupt.h>
40
41 #include "../comedidev.h"
42
43 #include "comedi_isadma.h"
44 #include "comedi_fc.h"
45 #include "8253.h"
46
47 /*
48  * Register I/O map
49  */
50 #define PCL816_DO_DI_LSB_REG                    0x00
51 #define PCL816_DO_DI_MSB_REG                    0x01
52 #define PCL816_TIMER_BASE                       0x04
53 #define PCL816_AI_LSB_REG                       0x08
54 #define PCL816_AI_MSB_REG                       0x09
55 #define PCL816_RANGE_REG                        0x09
56 #define PCL816_CLRINT_REG                       0x0a
57 #define PCL816_MUX_REG                          0x0b
58 #define PCL816_MUX_SCAN(_first, _last)          (((_last) << 4) | (_first))
59 #define PCL816_CTRL_REG                         0x0c
60 #define PCL816_CTRL_DISABLE_TRIG                (0 << 0)
61 #define PCL816_CTRL_SOFT_TRIG                   (1 << 0)
62 #define PCL816_CTRL_PACER_TRIG                  (1 << 1)
63 #define PCL816_CTRL_EXT_TRIG                    (1 << 2)
64 #define PCL816_CTRL_POE                         (1 << 3)
65 #define PCL816_CTRL_DMAEN                       (1 << 4)
66 #define PCL816_CTRL_INTEN                       (1 << 5)
67 #define PCL816_CTRL_DMASRC_SLOT0                (0 << 6)
68 #define PCL816_CTRL_DMASRC_SLOT1                (1 << 6)
69 #define PCL816_CTRL_DMASRC_SLOT2                (2 << 6)
70 #define PCL816_STATUS_REG                       0x0d
71 #define PCL816_STATUS_NEXT_CHAN_MASK            (0xf << 0)
72 #define PCL816_STATUS_INTSRC_MASK               (3 << 4)
73 #define PCL816_STATUS_INTSRC_SLOT0              (0 << 4)
74 #define PCL816_STATUS_INTSRC_SLOT1              (1 << 4)
75 #define PCL816_STATUS_INTSRC_SLOT2              (2 << 4)
76 #define PCL816_STATUS_INTSRC_DMA                (3 << 4)
77 #define PCL816_STATUS_INTACT                    (1 << 6)
78 #define PCL816_STATUS_DRDY                      (1 << 7)
79
80 #define MAGIC_DMA_WORD 0x5a5a
81
82 static const struct comedi_lrange range_pcl816 = {
83         8, {
84                 BIP_RANGE(10),
85                 BIP_RANGE(5),
86                 BIP_RANGE(2.5),
87                 BIP_RANGE(1.25),
88                 UNI_RANGE(10),
89                 UNI_RANGE(5),
90                 UNI_RANGE(2.5),
91                 UNI_RANGE(1.25)
92         }
93 };
94
95 struct pcl816_board {
96         const char *name;
97         int ai_maxdata;
98         int ao_maxdata;
99         int ai_chanlist;
100 };
101
102 static const struct pcl816_board boardtypes[] = {
103         {
104                 .name           = "pcl816",
105                 .ai_maxdata     = 0xffff,
106                 .ao_maxdata     = 0xffff,
107                 .ai_chanlist    = 1024,
108         }, {
109                 .name           = "pcl814b",
110                 .ai_maxdata     = 0x3fff,
111                 .ao_maxdata     = 0x3fff,
112                 .ai_chanlist    = 1024,
113         },
114 };
115
116 struct pcl816_private {
117         struct comedi_isadma *dma;
118         unsigned int ai_poll_ptr;       /*  how many sampes transfer poll */
119         unsigned int divisor1;
120         unsigned int divisor2;
121         unsigned int ai_cmd_running:1;
122         unsigned int ai_cmd_canceled:1;
123 };
124
125 static void pcl816_start_pacer(struct comedi_device *dev, bool load_counters)
126 {
127         struct pcl816_private *devpriv = dev->private;
128         unsigned long timer_base = dev->iobase + PCL816_TIMER_BASE;
129
130         i8254_set_mode(timer_base, 0, 0, I8254_MODE1 | I8254_BINARY);
131         i8254_write(timer_base, 0, 0, 0x00ff);
132         udelay(1);
133
134         i8254_set_mode(timer_base, 0, 2, I8254_MODE2 | I8254_BINARY);
135         i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY);
136         udelay(1);
137
138         if (load_counters) {
139                 i8254_write(timer_base, 0, 2, devpriv->divisor2);
140                 i8254_write(timer_base, 0, 1, devpriv->divisor1);
141         }
142 }
143
144 static void pcl816_ai_setup_dma(struct comedi_device *dev,
145                                 struct comedi_subdevice *s)
146 {
147         struct pcl816_private *devpriv = dev->private;
148         struct comedi_isadma *dma = devpriv->dma;
149         struct comedi_isadma_desc *desc = &dma->desc[0];
150         unsigned int nsamples;
151
152         dma->cur_dma = 0;
153
154         /*
155          * Determine dma size based on the buffer maxsize and the number of
156          * samples remaining in the command.
157          */
158         nsamples = comedi_bytes_to_samples(s, desc->maxsize);
159         nsamples = comedi_nsamples_left(s, nsamples);
160         desc->size = comedi_samples_to_bytes(s, nsamples);
161         comedi_isadma_program(desc);
162 }
163
164 static void pcl816_ai_setup_next_dma(struct comedi_device *dev,
165                                      struct comedi_subdevice *s,
166                                      unsigned int unread_samples)
167 {
168         struct pcl816_private *devpriv = dev->private;
169         struct comedi_isadma *dma = devpriv->dma;
170         struct comedi_isadma_desc *desc;
171         unsigned int max_samples;
172         unsigned int nsamples;
173
174         comedi_isadma_disable(dma->chan);
175
176         dma->cur_dma = 1 - dma->cur_dma;
177         desc = &dma->desc[dma->cur_dma];
178
179         /*
180          * Determine dma size based on the buffer maxsize plus the number of
181          * unread samples and the number of samples remaining in the command.
182          */
183         max_samples = comedi_bytes_to_samples(s, desc->maxsize);
184         nsamples = max_samples + unread_samples;
185         nsamples = comedi_nsamples_left(s, nsamples);
186         if (nsamples > unread_samples) {
187                 nsamples -= unread_samples;
188                 desc->size = comedi_samples_to_bytes(s, nsamples);
189                 comedi_isadma_program(desc);
190         }
191 }
192
193 static void pcl816_ai_set_chan_range(struct comedi_device *dev,
194                                      unsigned int chan,
195                                      unsigned int range)
196 {
197         outb(chan, dev->iobase + PCL816_MUX_REG);
198         outb(range, dev->iobase + PCL816_RANGE_REG);
199 }
200
201 static void pcl816_ai_set_chan_scan(struct comedi_device *dev,
202                                     unsigned int first_chan,
203                                     unsigned int last_chan)
204 {
205         outb(PCL816_MUX_SCAN(first_chan, last_chan),
206              dev->iobase + PCL816_MUX_REG);
207 }
208
209 static void pcl816_ai_setup_chanlist(struct comedi_device *dev,
210                                      unsigned int *chanlist,
211                                      unsigned int seglen)
212 {
213         unsigned int first_chan = CR_CHAN(chanlist[0]);
214         unsigned int last_chan;
215         unsigned int range;
216         unsigned int i;
217
218         /* store range list to card */
219         for (i = 0; i < seglen; i++) {
220                 last_chan = CR_CHAN(chanlist[i]);
221                 range = CR_RANGE(chanlist[i]);
222
223                 pcl816_ai_set_chan_range(dev, last_chan, range);
224         }
225
226         udelay(1);
227
228         pcl816_ai_set_chan_scan(dev, first_chan, last_chan);
229 }
230
231 static void pcl816_ai_clear_eoc(struct comedi_device *dev)
232 {
233         /* writing any value clears the interrupt request */
234         outb(0, dev->iobase + PCL816_CLRINT_REG);
235 }
236
237 static void pcl816_ai_soft_trig(struct comedi_device *dev)
238 {
239         /* writing any value triggers a software conversion */
240         outb(0, dev->iobase + PCL816_AI_LSB_REG);
241 }
242
243 static unsigned int pcl816_ai_get_sample(struct comedi_device *dev,
244                                          struct comedi_subdevice *s)
245 {
246         unsigned int val;
247
248         val = inb(dev->iobase + PCL816_AI_MSB_REG) << 8;
249         val |= inb(dev->iobase + PCL816_AI_LSB_REG);
250
251         return val & s->maxdata;
252 }
253
254 static int pcl816_ai_eoc(struct comedi_device *dev,
255                          struct comedi_subdevice *s,
256                          struct comedi_insn *insn,
257                          unsigned long context)
258 {
259         unsigned int status;
260
261         status = inb(dev->iobase + PCL816_STATUS_REG);
262         if ((status & PCL816_STATUS_DRDY) == 0)
263                 return 0;
264         return -EBUSY;
265 }
266
267 static bool pcl816_ai_next_chan(struct comedi_device *dev,
268                                 struct comedi_subdevice *s)
269 {
270         struct comedi_cmd *cmd = &s->async->cmd;
271
272         if (cmd->stop_src == TRIG_COUNT &&
273             s->async->scans_done >= cmd->stop_arg) {
274                 s->async->events |= COMEDI_CB_EOA;
275                 return false;
276         }
277
278         return true;
279 }
280
281 static void transfer_from_dma_buf(struct comedi_device *dev,
282                                   struct comedi_subdevice *s,
283                                   unsigned short *ptr,
284                                   unsigned int bufptr, unsigned int len)
285 {
286         unsigned short val;
287         int i;
288
289         for (i = 0; i < len; i++) {
290                 val = ptr[bufptr++];
291                 comedi_buf_write_samples(s, &val, 1);
292
293                 if (!pcl816_ai_next_chan(dev, s))
294                         return;
295         }
296 }
297
298 static irqreturn_t pcl816_interrupt(int irq, void *d)
299 {
300         struct comedi_device *dev = d;
301         struct comedi_subdevice *s = dev->read_subdev;
302         struct pcl816_private *devpriv = dev->private;
303         struct comedi_isadma *dma = devpriv->dma;
304         struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
305         unsigned int nsamples;
306         unsigned int bufptr;
307
308         if (!dev->attached || !devpriv->ai_cmd_running) {
309                 pcl816_ai_clear_eoc(dev);
310                 return IRQ_HANDLED;
311         }
312
313         if (devpriv->ai_cmd_canceled) {
314                 devpriv->ai_cmd_canceled = 0;
315                 pcl816_ai_clear_eoc(dev);
316                 return IRQ_HANDLED;
317         }
318
319         nsamples = comedi_bytes_to_samples(s, desc->size) -
320                    devpriv->ai_poll_ptr;
321         bufptr = devpriv->ai_poll_ptr;
322         devpriv->ai_poll_ptr = 0;
323
324         pcl816_ai_setup_next_dma(dev, s, nsamples);
325
326         transfer_from_dma_buf(dev, s, desc->virt_addr, bufptr, nsamples);
327
328         pcl816_ai_clear_eoc(dev);
329
330         comedi_handle_events(dev, s);
331         return IRQ_HANDLED;
332 }
333
334 static int check_channel_list(struct comedi_device *dev,
335                               struct comedi_subdevice *s,
336                               unsigned int *chanlist,
337                               unsigned int chanlen)
338 {
339         unsigned int chansegment[16];
340         unsigned int i, nowmustbechan, seglen, segpos;
341
342         /*  correct channel and range number check itself comedi/range.c */
343         if (chanlen < 1) {
344                 dev_err(dev->class_dev, "range/channel list is empty!\n");
345                 return 0;
346         }
347
348         if (chanlen > 1) {
349                 /*  first channel is every time ok */
350                 chansegment[0] = chanlist[0];
351                 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
352                         /*  we detect loop, this must by finish */
353                             if (chanlist[0] == chanlist[i])
354                                 break;
355                         nowmustbechan =
356                             (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
357                         if (nowmustbechan != CR_CHAN(chanlist[i])) {
358                                 /*  channel list isn't continuous :-( */
359                                 dev_dbg(dev->class_dev,
360                                         "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
361                                         i, CR_CHAN(chanlist[i]), nowmustbechan,
362                                         CR_CHAN(chanlist[0]));
363                                 return 0;
364                         }
365                         /*  well, this is next correct channel in list */
366                         chansegment[i] = chanlist[i];
367                 }
368
369                 /*  check whole chanlist */
370                 for (i = 0, segpos = 0; i < chanlen; i++) {
371                             if (chanlist[i] != chansegment[i % seglen]) {
372                                 dev_dbg(dev->class_dev,
373                                         "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
374                                         i, CR_CHAN(chansegment[i]),
375                                         CR_RANGE(chansegment[i]),
376                                         CR_AREF(chansegment[i]),
377                                         CR_CHAN(chanlist[i % seglen]),
378                                         CR_RANGE(chanlist[i % seglen]),
379                                         CR_AREF(chansegment[i % seglen]));
380                                 return 0;       /*  chan/gain list is strange */
381                         }
382                 }
383         } else {
384                 seglen = 1;
385         }
386
387         return seglen;  /*  we can serve this with MUX logic */
388 }
389
390 static int pcl816_ai_cmdtest(struct comedi_device *dev,
391                              struct comedi_subdevice *s, struct comedi_cmd *cmd)
392 {
393         struct pcl816_private *devpriv = dev->private;
394         int err = 0;
395         unsigned int arg;
396
397         /* Step 1 : check if triggers are trivially valid */
398
399         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
400         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
401         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_EXT | TRIG_TIMER);
402         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
403         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
404
405         if (err)
406                 return 1;
407
408         /* Step 2a : make sure trigger sources are unique */
409
410         err |= cfc_check_trigger_is_unique(cmd->convert_src);
411         err |= cfc_check_trigger_is_unique(cmd->stop_src);
412
413         /* Step 2b : and mutually compatible */
414
415         if (err)
416                 return 2;
417
418
419         /* Step 3: check if arguments are trivially valid */
420
421         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
422         err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
423
424         if (cmd->convert_src == TRIG_TIMER)
425                 err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 10000);
426         else    /* TRIG_EXT */
427                 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
428
429         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
430
431         if (cmd->stop_src == TRIG_COUNT)
432                 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
433         else    /* TRIG_NONE */
434                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
435
436         if (err)
437                 return 3;
438
439
440         /* step 4: fix up any arguments */
441         if (cmd->convert_src == TRIG_TIMER) {
442                 arg = cmd->convert_arg;
443                 i8253_cascade_ns_to_timer(I8254_OSC_BASE_10MHZ,
444                                           &devpriv->divisor1,
445                                           &devpriv->divisor2,
446                                           &arg, cmd->flags);
447                 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
448         }
449
450         if (err)
451                 return 4;
452
453
454         /* step 5: complain about special chanlist considerations */
455
456         if (cmd->chanlist) {
457                 if (!check_channel_list(dev, s, cmd->chanlist,
458                                         cmd->chanlist_len))
459                         return 5;       /*  incorrect channels list */
460         }
461
462         return 0;
463 }
464
465 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
466 {
467         struct pcl816_private *devpriv = dev->private;
468         struct comedi_isadma *dma = devpriv->dma;
469         struct comedi_cmd *cmd = &s->async->cmd;
470         unsigned int ctrl;
471         unsigned int seglen;
472
473         if (devpriv->ai_cmd_running)
474                 return -EBUSY;
475
476         pcl816_start_pacer(dev, false);
477
478         seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
479         if (seglen < 1)
480                 return -EINVAL;
481         pcl816_ai_setup_chanlist(dev, cmd->chanlist, seglen);
482         udelay(1);
483
484         devpriv->ai_cmd_running = 1;
485         devpriv->ai_poll_ptr = 0;
486         devpriv->ai_cmd_canceled = 0;
487
488         pcl816_ai_setup_dma(dev, s);
489
490         pcl816_start_pacer(dev, true);
491
492         ctrl = PCL816_CTRL_INTEN | PCL816_CTRL_DMAEN | PCL816_CTRL_DMASRC_SLOT0;
493         if (cmd->convert_src == TRIG_TIMER)
494                 ctrl |= PCL816_CTRL_PACER_TRIG;
495         else    /* TRIG_EXT */
496                 ctrl |= PCL816_CTRL_EXT_TRIG;
497
498         outb(ctrl, dev->iobase + PCL816_CTRL_REG);
499         outb((dma->chan << 4) | dev->irq,
500              dev->iobase + PCL816_STATUS_REG);
501
502         return 0;
503 }
504
505 static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
506 {
507         struct pcl816_private *devpriv = dev->private;
508         struct comedi_isadma *dma = devpriv->dma;
509         struct comedi_isadma_desc *desc;
510         unsigned long flags;
511         unsigned int poll;
512         int ret;
513
514         spin_lock_irqsave(&dev->spinlock, flags);
515
516         poll = comedi_isadma_poll(dma);
517         poll = comedi_bytes_to_samples(s, poll);
518         if (poll > devpriv->ai_poll_ptr) {
519                 desc = &dma->desc[dma->cur_dma];
520                 transfer_from_dma_buf(dev, s, desc->virt_addr,
521                                       devpriv->ai_poll_ptr,
522                                       poll - devpriv->ai_poll_ptr);
523                 /* new buffer position */
524                 devpriv->ai_poll_ptr = poll;
525
526                 comedi_handle_events(dev, s);
527
528                 ret = comedi_buf_n_bytes_ready(s);
529         } else {
530                 /* no new samples */
531                 ret = 0;
532         }
533         spin_unlock_irqrestore(&dev->spinlock, flags);
534
535         return ret;
536 }
537
538 static int pcl816_ai_cancel(struct comedi_device *dev,
539                             struct comedi_subdevice *s)
540 {
541         struct pcl816_private *devpriv = dev->private;
542
543         if (!devpriv->ai_cmd_running)
544                 return 0;
545
546         outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
547         pcl816_ai_clear_eoc(dev);
548
549         /* Stop pacer */
550         i8254_set_mode(dev->iobase + PCL816_TIMER_BASE, 0,
551                         2, I8254_MODE0 | I8254_BINARY);
552         i8254_set_mode(dev->iobase + PCL816_TIMER_BASE, 0,
553                         1, I8254_MODE0 | I8254_BINARY);
554
555         devpriv->ai_cmd_running = 0;
556         devpriv->ai_cmd_canceled = 1;
557
558         return 0;
559 }
560
561 static int pcl816_ai_insn_read(struct comedi_device *dev,
562                                struct comedi_subdevice *s,
563                                struct comedi_insn *insn,
564                                unsigned int *data)
565 {
566         unsigned int chan = CR_CHAN(insn->chanspec);
567         unsigned int range = CR_RANGE(insn->chanspec);
568         int ret = 0;
569         int i;
570
571         outb(PCL816_CTRL_SOFT_TRIG, dev->iobase + PCL816_CTRL_REG);
572
573         pcl816_ai_set_chan_range(dev, chan, range);
574         pcl816_ai_set_chan_scan(dev, chan, chan);
575
576         for (i = 0; i < insn->n; i++) {
577                 pcl816_ai_clear_eoc(dev);
578                 pcl816_ai_soft_trig(dev);
579
580                 ret = comedi_timeout(dev, s, insn, pcl816_ai_eoc, 0);
581                 if (ret)
582                         break;
583
584                 data[i] = pcl816_ai_get_sample(dev, s);
585         }
586         outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
587         pcl816_ai_clear_eoc(dev);
588
589         return ret ? ret : insn->n;
590 }
591
592 static int pcl816_di_insn_bits(struct comedi_device *dev,
593                                struct comedi_subdevice *s,
594                                struct comedi_insn *insn,
595                                unsigned int *data)
596 {
597         data[1] = inb(dev->iobase + PCL816_DO_DI_LSB_REG) |
598                   (inb(dev->iobase + PCL816_DO_DI_MSB_REG) << 8);
599
600         return insn->n;
601 }
602
603 static int pcl816_do_insn_bits(struct comedi_device *dev,
604                                struct comedi_subdevice *s,
605                                struct comedi_insn *insn,
606                                unsigned int *data)
607 {
608         if (comedi_dio_update_state(s, data)) {
609                 outb(s->state & 0xff, dev->iobase + PCL816_DO_DI_LSB_REG);
610                 outb((s->state >> 8), dev->iobase + PCL816_DO_DI_MSB_REG);
611         }
612
613         data[1] = s->state;
614
615         return insn->n;
616 }
617
618 static void pcl816_reset(struct comedi_device *dev)
619 {
620         unsigned long timer_base = dev->iobase + PCL816_TIMER_BASE;
621
622         outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
623         pcl816_ai_set_chan_range(dev, 0, 0);
624         pcl816_ai_clear_eoc(dev);
625
626         /* Stop pacer */
627         i8254_set_mode(timer_base, 0, 2, I8254_MODE0 | I8254_BINARY);
628         i8254_set_mode(timer_base, 0, 1, I8254_MODE0 | I8254_BINARY);
629         i8254_set_mode(timer_base, 0, 0, I8254_MODE0 | I8254_BINARY);
630
631         /* set all digital outputs low */
632         outb(0, dev->iobase + PCL816_DO_DI_LSB_REG);
633         outb(0, dev->iobase + PCL816_DO_DI_MSB_REG);
634 }
635
636 static void pcl816_alloc_irq_and_dma(struct comedi_device *dev,
637                                      struct comedi_devconfig *it)
638 {
639         struct pcl816_private *devpriv = dev->private;
640         unsigned int irq_num = it->options[1];
641         unsigned int dma_chan = it->options[2];
642
643         /* only IRQs 2-7 and DMA channels 3 and 1 are valid */
644         if (!(irq_num >= 2 && irq_num <= 7) ||
645             !(dma_chan == 3 || dma_chan == 1))
646                 return;
647
648         if (request_irq(irq_num, pcl816_interrupt, 0, dev->board_name, dev))
649                 return;
650
651         /* DMA uses two 16K buffers */
652         devpriv->dma = comedi_isadma_alloc(dev, 2, dma_chan, dma_chan,
653                                            PAGE_SIZE * 4, COMEDI_ISADMA_READ);
654         if (!devpriv->dma)
655                 free_irq(irq_num, dev);
656         else
657                 dev->irq = irq_num;
658 }
659
660 static void pcl816_free_dma(struct comedi_device *dev)
661 {
662         struct pcl816_private *devpriv = dev->private;
663
664         if (devpriv)
665                 comedi_isadma_free(devpriv->dma);
666 }
667
668 static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
669 {
670         const struct pcl816_board *board = dev->board_ptr;
671         struct pcl816_private *devpriv;
672         struct comedi_subdevice *s;
673         int ret;
674
675         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
676         if (!devpriv)
677                 return -ENOMEM;
678
679         ret = comedi_request_region(dev, it->options[0], 0x10);
680         if (ret)
681                 return ret;
682
683         /* an IRQ and DMA are required to support async commands */
684         pcl816_alloc_irq_and_dma(dev, it);
685
686         ret = comedi_alloc_subdevices(dev, 4);
687         if (ret)
688                 return ret;
689
690         s = &dev->subdevices[0];
691         s->type         = COMEDI_SUBD_AI;
692         s->subdev_flags = SDF_CMD_READ | SDF_DIFF;
693         s->n_chan       = 16;
694         s->maxdata      = board->ai_maxdata;
695         s->range_table  = &range_pcl816;
696         s->insn_read    = pcl816_ai_insn_read;
697         if (dev->irq) {
698                 dev->read_subdev = s;
699                 s->subdev_flags |= SDF_CMD_READ;
700                 s->len_chanlist = board->ai_chanlist;
701                 s->do_cmdtest   = pcl816_ai_cmdtest;
702                 s->do_cmd       = pcl816_ai_cmd;
703                 s->poll         = pcl816_ai_poll;
704                 s->cancel       = pcl816_ai_cancel;
705         }
706
707         /* Analog OUtput subdevice */
708         s = &dev->subdevices[2];
709         s->type         = COMEDI_SUBD_UNUSED;
710 #if 0
711         subdevs[1] = COMEDI_SUBD_AO;
712         s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
713         s->n_chan = 1;
714         s->maxdata = board->ao_maxdata;
715         s->range_table = &range_pcl816;
716 #endif
717
718         /* Digital Input subdevice */
719         s = &dev->subdevices[2];
720         s->type         = COMEDI_SUBD_DI;
721         s->subdev_flags = SDF_READABLE;
722         s->n_chan       = 16;
723         s->maxdata      = 1;
724         s->range_table  = &range_digital;
725         s->insn_bits    = pcl816_di_insn_bits;
726
727         /* Digital Output subdevice */
728         s = &dev->subdevices[3];
729         s->type         = COMEDI_SUBD_DO;
730         s->subdev_flags = SDF_WRITABLE;
731         s->n_chan       = 16;
732         s->maxdata      = 1;
733         s->range_table  = &range_digital;
734         s->insn_bits    = pcl816_do_insn_bits;
735
736         pcl816_reset(dev);
737
738         return 0;
739 }
740
741 static void pcl816_detach(struct comedi_device *dev)
742 {
743         if (dev->private) {
744                 pcl816_ai_cancel(dev, dev->read_subdev);
745                 pcl816_reset(dev);
746         }
747         pcl816_free_dma(dev);
748         comedi_legacy_detach(dev);
749 }
750
751 static struct comedi_driver pcl816_driver = {
752         .driver_name    = "pcl816",
753         .module         = THIS_MODULE,
754         .attach         = pcl816_attach,
755         .detach         = pcl816_detach,
756         .board_name     = &boardtypes[0].name,
757         .num_names      = ARRAY_SIZE(boardtypes),
758         .offset         = sizeof(struct pcl816_board),
759 };
760 module_comedi_driver(pcl816_driver);
761
762 MODULE_AUTHOR("Comedi http://www.comedi.org");
763 MODULE_DESCRIPTION("Comedi low-level driver");
764 MODULE_LICENSE("GPL");