Linux-libre 3.18.132-gnu
[librecmc/linux-libre.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 "../comedidev.h"
37
38 #include <linux/gfp.h>
39 #include <linux/delay.h>
40 #include <linux/io.h>
41 #include <linux/interrupt.h>
42 #include <asm/dma.h>
43
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         unsigned int dma;       /*  used DMA, 0=don't use DMA */
118         unsigned int dmapages;
119         unsigned int hwdmasize;
120         unsigned long dmabuf[2];        /*  pointers to begin of DMA buffers */
121         unsigned int hwdmaptr[2];       /*  hardware address of DMA buffers */
122         int next_dma_buf;       /*  which DMA buffer will be used next round */
123         long dma_runs_to_end;   /*  how many we must permorm DMA transfer to end of record */
124         unsigned long last_dma_run;     /*  how many bytes we must transfer on last DMA page */
125         int ai_act_scan;        /*  how many scans we finished */
126         unsigned int ai_poll_ptr;       /*  how many sampes transfer poll */
127         unsigned int divisor1;
128         unsigned int divisor2;
129         unsigned int ai_cmd_running:1;
130         unsigned int ai_cmd_canceled:1;
131 };
132
133 static void pcl816_start_pacer(struct comedi_device *dev, bool load_counters)
134 {
135         struct pcl816_private *devpriv = dev->private;
136         unsigned long timer_base = dev->iobase + PCL816_TIMER_BASE;
137
138         i8254_set_mode(timer_base, 0, 0, I8254_MODE1 | I8254_BINARY);
139         i8254_write(timer_base, 0, 0, 0x00ff);
140         udelay(1);
141
142         i8254_set_mode(timer_base, 0, 2, I8254_MODE2 | I8254_BINARY);
143         i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY);
144         udelay(1);
145
146         if (load_counters) {
147                 i8254_write(timer_base, 0, 2, devpriv->divisor2);
148                 i8254_write(timer_base, 0, 1, devpriv->divisor1);
149         }
150 }
151
152 static void pcl816_ai_setup_dma(struct comedi_device *dev,
153                                 struct comedi_subdevice *s)
154 {
155         struct pcl816_private *devpriv = dev->private;
156         struct comedi_cmd *cmd = &s->async->cmd;
157         unsigned int dma_flags;
158         unsigned int bytes;
159
160         bytes = devpriv->hwdmasize;
161         if (cmd->stop_src == TRIG_COUNT) {
162                 /*  how many */
163                 bytes = cmd->stop_arg * cfc_bytes_per_scan(s);
164
165                 /*  how many DMA pages we must fill */
166                 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize;
167
168                 /* on last dma transfer must be moved */
169                 devpriv->last_dma_run = bytes % devpriv->hwdmasize;
170                 devpriv->dma_runs_to_end--;
171                 if (devpriv->dma_runs_to_end >= 0)
172                         bytes = devpriv->hwdmasize;
173         } else
174                 devpriv->dma_runs_to_end = -1;
175
176         devpriv->next_dma_buf = 0;
177         set_dma_mode(devpriv->dma, DMA_MODE_READ);
178         dma_flags = claim_dma_lock();
179         clear_dma_ff(devpriv->dma);
180         set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
181         set_dma_count(devpriv->dma, bytes);
182         release_dma_lock(dma_flags);
183         enable_dma(devpriv->dma);
184 }
185
186 static void pcl816_ai_setup_next_dma(struct comedi_device *dev,
187                                      struct comedi_subdevice *s)
188 {
189         struct pcl816_private *devpriv = dev->private;
190         struct comedi_cmd *cmd = &s->async->cmd;
191         unsigned long dma_flags;
192
193         disable_dma(devpriv->dma);
194         if (devpriv->dma_runs_to_end > -1 || cmd->stop_src == TRIG_NONE) {
195                 /* switch dma bufs */
196                 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
197                 set_dma_mode(devpriv->dma, DMA_MODE_READ);
198                 dma_flags = claim_dma_lock();
199                 set_dma_addr(devpriv->dma,
200                              devpriv->hwdmaptr[devpriv->next_dma_buf]);
201                 if (devpriv->dma_runs_to_end)
202                         set_dma_count(devpriv->dma, devpriv->hwdmasize);
203                 else
204                         set_dma_count(devpriv->dma, devpriv->last_dma_run);
205                 release_dma_lock(dma_flags);
206                 enable_dma(devpriv->dma);
207         }
208
209         devpriv->dma_runs_to_end--;
210 }
211
212 static void pcl816_ai_set_chan_range(struct comedi_device *dev,
213                                      unsigned int chan,
214                                      unsigned int range)
215 {
216         outb(chan, dev->iobase + PCL816_MUX_REG);
217         outb(range, dev->iobase + PCL816_RANGE_REG);
218 }
219
220 static void pcl816_ai_set_chan_scan(struct comedi_device *dev,
221                                     unsigned int first_chan,
222                                     unsigned int last_chan)
223 {
224         outb(PCL816_MUX_SCAN(first_chan, last_chan),
225              dev->iobase + PCL816_MUX_REG);
226 }
227
228 static void pcl816_ai_setup_chanlist(struct comedi_device *dev,
229                                      unsigned int *chanlist,
230                                      unsigned int seglen)
231 {
232         unsigned int first_chan = CR_CHAN(chanlist[0]);
233         unsigned int last_chan;
234         unsigned int range;
235         unsigned int i;
236
237         /* store range list to card */
238         for (i = 0; i < seglen; i++) {
239                 last_chan = CR_CHAN(chanlist[i]);
240                 range = CR_RANGE(chanlist[i]);
241
242                 pcl816_ai_set_chan_range(dev, last_chan, range);
243         }
244
245         udelay(1);
246
247         pcl816_ai_set_chan_scan(dev, first_chan, last_chan);
248 }
249
250 static void pcl816_ai_clear_eoc(struct comedi_device *dev)
251 {
252         /* writing any value clears the interrupt request */
253         outb(0, dev->iobase + PCL816_CLRINT_REG);
254 }
255
256 static void pcl816_ai_soft_trig(struct comedi_device *dev)
257 {
258         /* writing any value triggers a software conversion */
259         outb(0, dev->iobase + PCL816_AI_LSB_REG);
260 }
261
262 static unsigned int pcl816_ai_get_sample(struct comedi_device *dev,
263                                          struct comedi_subdevice *s)
264 {
265         unsigned int val;
266
267         val = inb(dev->iobase + PCL816_AI_MSB_REG) << 8;
268         val |= inb(dev->iobase + PCL816_AI_LSB_REG);
269
270         return val & s->maxdata;
271 }
272
273 static int pcl816_ai_eoc(struct comedi_device *dev,
274                          struct comedi_subdevice *s,
275                          struct comedi_insn *insn,
276                          unsigned long context)
277 {
278         unsigned int status;
279
280         status = inb(dev->iobase + PCL816_STATUS_REG);
281         if ((status & PCL816_STATUS_DRDY) == 0)
282                 return 0;
283         return -EBUSY;
284 }
285
286 static bool pcl816_ai_next_chan(struct comedi_device *dev,
287                                 struct comedi_subdevice *s)
288 {
289         struct pcl816_private *devpriv = dev->private;
290         struct comedi_cmd *cmd = &s->async->cmd;
291
292         s->async->events |= COMEDI_CB_BLOCK;
293
294         s->async->cur_chan++;
295         if (s->async->cur_chan >= cmd->chanlist_len) {
296                 s->async->cur_chan = 0;
297                 devpriv->ai_act_scan++;
298                 s->async->events |= COMEDI_CB_EOS;
299         }
300
301         if (cmd->stop_src == TRIG_COUNT &&
302             devpriv->ai_act_scan >= cmd->stop_arg) {
303                 /* all data sampled */
304                 s->async->events |= COMEDI_CB_EOA;
305                 return false;
306         }
307
308         return true;
309 }
310
311 static void transfer_from_dma_buf(struct comedi_device *dev,
312                                   struct comedi_subdevice *s,
313                                   unsigned short *ptr,
314                                   unsigned int bufptr, unsigned int len)
315 {
316         int i;
317
318         for (i = 0; i < len; i++) {
319                 comedi_buf_put(s, ptr[bufptr++]);
320
321                 if (!pcl816_ai_next_chan(dev, s))
322                         return;
323         }
324 }
325
326 static irqreturn_t pcl816_interrupt(int irq, void *d)
327 {
328         struct comedi_device *dev = d;
329         struct comedi_subdevice *s = dev->read_subdev;
330         struct pcl816_private *devpriv = dev->private;
331         unsigned short *ptr;
332         unsigned int bufptr;
333         unsigned int len;
334
335         if (!dev->attached || !devpriv->ai_cmd_running) {
336                 pcl816_ai_clear_eoc(dev);
337                 return IRQ_HANDLED;
338         }
339
340         if (devpriv->ai_cmd_canceled) {
341                 devpriv->ai_cmd_canceled = 0;
342                 pcl816_ai_clear_eoc(dev);
343                 return IRQ_HANDLED;
344         }
345
346         ptr = (unsigned short *)devpriv->dmabuf[devpriv->next_dma_buf];
347
348         pcl816_ai_setup_next_dma(dev, s);
349
350         len = (devpriv->hwdmasize >> 1) - devpriv->ai_poll_ptr;
351         bufptr = devpriv->ai_poll_ptr;
352         devpriv->ai_poll_ptr = 0;
353
354         transfer_from_dma_buf(dev, s, ptr, bufptr, len);
355
356         pcl816_ai_clear_eoc(dev);
357
358         cfc_handle_events(dev, s);
359         return IRQ_HANDLED;
360 }
361
362 static int check_channel_list(struct comedi_device *dev,
363                               struct comedi_subdevice *s,
364                               unsigned int *chanlist,
365                               unsigned int chanlen)
366 {
367         unsigned int chansegment[16];
368         unsigned int i, nowmustbechan, seglen, segpos;
369
370         /*  correct channel and range number check itself comedi/range.c */
371         if (chanlen < 1) {
372                 dev_err(dev->class_dev, "range/channel list is empty!\n");
373                 return 0;
374         }
375
376         if (chanlen > 1) {
377                 /*  first channel is every time ok */
378                 chansegment[0] = chanlist[0];
379                 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
380                         /*  we detect loop, this must by finish */
381                             if (chanlist[0] == chanlist[i])
382                                 break;
383                         nowmustbechan =
384                             (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
385                         if (nowmustbechan != CR_CHAN(chanlist[i])) {
386                                 /*  channel list isn't continuous :-( */
387                                 dev_dbg(dev->class_dev,
388                                         "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
389                                         i, CR_CHAN(chanlist[i]), nowmustbechan,
390                                         CR_CHAN(chanlist[0]));
391                                 return 0;
392                         }
393                         /*  well, this is next correct channel in list */
394                         chansegment[i] = chanlist[i];
395                 }
396
397                 /*  check whole chanlist */
398                 for (i = 0, segpos = 0; i < chanlen; i++) {
399                             if (chanlist[i] != chansegment[i % seglen]) {
400                                 dev_dbg(dev->class_dev,
401                                         "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
402                                         i, CR_CHAN(chansegment[i]),
403                                         CR_RANGE(chansegment[i]),
404                                         CR_AREF(chansegment[i]),
405                                         CR_CHAN(chanlist[i % seglen]),
406                                         CR_RANGE(chanlist[i % seglen]),
407                                         CR_AREF(chansegment[i % seglen]));
408                                 return 0;       /*  chan/gain list is strange */
409                         }
410                 }
411         } else {
412                 seglen = 1;
413         }
414
415         return seglen;  /*  we can serve this with MUX logic */
416 }
417
418 static int pcl816_ai_cmdtest(struct comedi_device *dev,
419                              struct comedi_subdevice *s, struct comedi_cmd *cmd)
420 {
421         struct pcl816_private *devpriv = dev->private;
422         int err = 0;
423         unsigned int arg;
424
425         /* Step 1 : check if triggers are trivially valid */
426
427         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
428         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
429         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_EXT | TRIG_TIMER);
430         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
431         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
432
433         if (err)
434                 return 1;
435
436         /* Step 2a : make sure trigger sources are unique */
437
438         err |= cfc_check_trigger_is_unique(cmd->convert_src);
439         err |= cfc_check_trigger_is_unique(cmd->stop_src);
440
441         /* Step 2b : and mutually compatible */
442
443         if (err)
444                 return 2;
445
446
447         /* Step 3: check if arguments are trivially valid */
448
449         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
450         err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
451
452         if (cmd->convert_src == TRIG_TIMER)
453                 err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 10000);
454         else    /* TRIG_EXT */
455                 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
456
457         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
458
459         if (cmd->stop_src == TRIG_COUNT)
460                 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
461         else    /* TRIG_NONE */
462                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
463
464         if (err)
465                 return 3;
466
467
468         /* step 4: fix up any arguments */
469         if (cmd->convert_src == TRIG_TIMER) {
470                 arg = cmd->convert_arg;
471                 i8253_cascade_ns_to_timer(I8254_OSC_BASE_10MHZ,
472                                           &devpriv->divisor1,
473                                           &devpriv->divisor2,
474                                           &arg, cmd->flags);
475                 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
476         }
477
478         if (err)
479                 return 4;
480
481
482         /* step 5: complain about special chanlist considerations */
483
484         if (cmd->chanlist) {
485                 if (!check_channel_list(dev, s, cmd->chanlist,
486                                         cmd->chanlist_len))
487                         return 5;       /*  incorrect channels list */
488         }
489
490         return 0;
491 }
492
493 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
494 {
495         struct pcl816_private *devpriv = dev->private;
496         struct comedi_cmd *cmd = &s->async->cmd;
497         unsigned int ctrl;
498         unsigned int seglen;
499
500         if (devpriv->ai_cmd_running)
501                 return -EBUSY;
502
503         pcl816_start_pacer(dev, false);
504
505         seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
506         if (seglen < 1)
507                 return -EINVAL;
508         pcl816_ai_setup_chanlist(dev, cmd->chanlist, seglen);
509         udelay(1);
510
511         devpriv->ai_act_scan = 0;
512         s->async->cur_chan = 0;
513         devpriv->ai_cmd_running = 1;
514         devpriv->ai_poll_ptr = 0;
515         devpriv->ai_cmd_canceled = 0;
516
517         pcl816_ai_setup_dma(dev, s);
518
519         pcl816_start_pacer(dev, true);
520
521         ctrl = PCL816_CTRL_INTEN | PCL816_CTRL_DMAEN | PCL816_CTRL_DMASRC_SLOT0;
522         if (cmd->convert_src == TRIG_TIMER)
523                 ctrl |= PCL816_CTRL_PACER_TRIG;
524         else    /* TRIG_EXT */
525                 ctrl |= PCL816_CTRL_EXT_TRIG;
526
527         outb(ctrl, dev->iobase + PCL816_CTRL_REG);
528         outb((devpriv->dma << 4) | dev->irq, dev->iobase + PCL816_STATUS_REG);
529
530         return 0;
531 }
532
533 static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
534 {
535         struct pcl816_private *devpriv = dev->private;
536         unsigned long flags;
537         unsigned int top1, top2, i;
538
539         spin_lock_irqsave(&dev->spinlock, flags);
540
541         for (i = 0; i < 20; i++) {
542                 top1 = get_dma_residue(devpriv->dma);   /*  where is now DMA */
543                 top2 = get_dma_residue(devpriv->dma);
544                 if (top1 == top2)
545                         break;
546         }
547         if (top1 != top2) {
548                 spin_unlock_irqrestore(&dev->spinlock, flags);
549                 return 0;
550         }
551
552         /*  where is now DMA in buffer */
553         top1 = devpriv->hwdmasize - top1;
554         top1 >>= 1;             /*  sample position */
555         top2 = top1 - devpriv->ai_poll_ptr;
556         if (top2 < 1) {         /*  no new samples */
557                 spin_unlock_irqrestore(&dev->spinlock, flags);
558                 return 0;
559         }
560
561         transfer_from_dma_buf(dev, s,
562                               (unsigned short *)devpriv->dmabuf[devpriv->
563                                                                 next_dma_buf],
564                               devpriv->ai_poll_ptr, top2);
565
566         devpriv->ai_poll_ptr = top1;    /*  new buffer position */
567         spin_unlock_irqrestore(&dev->spinlock, flags);
568
569         cfc_handle_events(dev, s);
570
571         return comedi_buf_n_bytes_ready(s);
572 }
573
574 static int pcl816_ai_cancel(struct comedi_device *dev,
575                             struct comedi_subdevice *s)
576 {
577         struct pcl816_private *devpriv = dev->private;
578
579         if (!devpriv->ai_cmd_running)
580                 return 0;
581
582         outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
583         pcl816_ai_clear_eoc(dev);
584
585         /* Stop pacer */
586         i8254_set_mode(dev->iobase + PCL816_TIMER_BASE, 0,
587                         2, I8254_MODE0 | I8254_BINARY);
588         i8254_set_mode(dev->iobase + PCL816_TIMER_BASE, 0,
589                         1, I8254_MODE0 | I8254_BINARY);
590
591         devpriv->ai_cmd_running = 0;
592         devpriv->ai_cmd_canceled = 1;
593
594         return 0;
595 }
596
597 static int pcl816_ai_insn_read(struct comedi_device *dev,
598                                struct comedi_subdevice *s,
599                                struct comedi_insn *insn,
600                                unsigned int *data)
601 {
602         unsigned int chan = CR_CHAN(insn->chanspec);
603         unsigned int range = CR_RANGE(insn->chanspec);
604         int ret = 0;
605         int i;
606
607         outb(PCL816_CTRL_SOFT_TRIG, dev->iobase + PCL816_CTRL_REG);
608
609         pcl816_ai_set_chan_range(dev, chan, range);
610         pcl816_ai_set_chan_scan(dev, chan, chan);
611
612         for (i = 0; i < insn->n; i++) {
613                 pcl816_ai_clear_eoc(dev);
614                 pcl816_ai_soft_trig(dev);
615
616                 ret = comedi_timeout(dev, s, insn, pcl816_ai_eoc, 0);
617                 if (ret)
618                         break;
619
620                 data[i] = pcl816_ai_get_sample(dev, s);
621         }
622         outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
623         pcl816_ai_clear_eoc(dev);
624
625         return ret ? ret : insn->n;
626 }
627
628 static int pcl816_di_insn_bits(struct comedi_device *dev,
629                                struct comedi_subdevice *s,
630                                struct comedi_insn *insn,
631                                unsigned int *data)
632 {
633         data[1] = inb(dev->iobase + PCL816_DO_DI_LSB_REG) |
634                   (inb(dev->iobase + PCL816_DO_DI_MSB_REG) << 8);
635
636         return insn->n;
637 }
638
639 static int pcl816_do_insn_bits(struct comedi_device *dev,
640                                struct comedi_subdevice *s,
641                                struct comedi_insn *insn,
642                                unsigned int *data)
643 {
644         if (comedi_dio_update_state(s, data)) {
645                 outb(s->state & 0xff, dev->iobase + PCL816_DO_DI_LSB_REG);
646                 outb((s->state >> 8), dev->iobase + PCL816_DO_DI_MSB_REG);
647         }
648
649         data[1] = s->state;
650
651         return insn->n;
652 }
653
654 static void pcl816_reset(struct comedi_device *dev)
655 {
656         unsigned long timer_base = dev->iobase + PCL816_TIMER_BASE;
657
658         outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
659         pcl816_ai_set_chan_range(dev, 0, 0);
660         pcl816_ai_clear_eoc(dev);
661
662         /* Stop pacer */
663         i8254_set_mode(timer_base, 0, 2, I8254_MODE0 | I8254_BINARY);
664         i8254_set_mode(timer_base, 0, 1, I8254_MODE0 | I8254_BINARY);
665         i8254_set_mode(timer_base, 0, 0, I8254_MODE0 | I8254_BINARY);
666
667         /* set all digital outputs low */
668         outb(0, dev->iobase + PCL816_DO_DI_LSB_REG);
669         outb(0, dev->iobase + PCL816_DO_DI_MSB_REG);
670 }
671
672 static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
673 {
674         const struct pcl816_board *board = dev->board_ptr;
675         struct pcl816_private *devpriv;
676         struct comedi_subdevice *s;
677         int ret;
678         int i;
679
680         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
681         if (!devpriv)
682                 return -ENOMEM;
683
684         ret = comedi_request_region(dev, it->options[0], 0x10);
685         if (ret)
686                 return ret;
687
688         /* we can use IRQ 2-7 for async command support */
689         if (it->options[1] >= 2 && it->options[1] <= 7) {
690                 ret = request_irq(it->options[1], pcl816_interrupt, 0,
691                                   dev->board_name, dev);
692                 if (ret == 0)
693                         dev->irq = it->options[1];
694         }
695
696         /* we need an IRQ to do DMA on channel 3 or 1 */
697         if (dev->irq && (it->options[2] == 3 || it->options[2] == 1)) {
698                 ret = request_dma(it->options[2], dev->board_name);
699                 if (ret) {
700                         dev_err(dev->class_dev,
701                                 "unable to request DMA channel %d\n",
702                                 it->options[2]);
703                         return -EBUSY;
704                 }
705                 devpriv->dma = it->options[2];
706
707                 devpriv->dmapages = 2;  /* we need 16KB */
708                 devpriv->hwdmasize = (1 << devpriv->dmapages) * PAGE_SIZE;
709
710                 for (i = 0; i < 2; i++) {
711                         unsigned long dmabuf;
712
713                         dmabuf = __get_dma_pages(GFP_KERNEL, devpriv->dmapages);
714                         if (!dmabuf)
715                                 return -ENOMEM;
716
717                         devpriv->dmabuf[i] = dmabuf;
718                         devpriv->hwdmaptr[i] = virt_to_bus((void *)dmabuf);
719                 }
720         }
721
722         ret = comedi_alloc_subdevices(dev, 4);
723         if (ret)
724                 return ret;
725
726         s = &dev->subdevices[0];
727         s->type         = COMEDI_SUBD_AI;
728         s->subdev_flags = SDF_CMD_READ | SDF_DIFF;
729         s->n_chan       = 16;
730         s->maxdata      = board->ai_maxdata;
731         s->range_table  = &range_pcl816;
732         s->insn_read    = pcl816_ai_insn_read;
733         if (devpriv->dma) {
734                 dev->read_subdev = s;
735                 s->subdev_flags |= SDF_CMD_READ;
736                 s->len_chanlist = board->ai_chanlist;
737                 s->do_cmdtest   = pcl816_ai_cmdtest;
738                 s->do_cmd       = pcl816_ai_cmd;
739                 s->poll         = pcl816_ai_poll;
740                 s->cancel       = pcl816_ai_cancel;
741         }
742
743         /* Analog OUtput subdevice */
744         s = &dev->subdevices[2];
745         s->type         = COMEDI_SUBD_UNUSED;
746 #if 0
747         subdevs[1] = COMEDI_SUBD_AO;
748         s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
749         s->n_chan = 1;
750         s->maxdata = board->ao_maxdata;
751         s->range_table = &range_pcl816;
752 #endif
753
754         /* Digital Input subdevice */
755         s = &dev->subdevices[2];
756         s->type         = COMEDI_SUBD_DI;
757         s->subdev_flags = SDF_READABLE;
758         s->n_chan       = 16;
759         s->maxdata      = 1;
760         s->range_table  = &range_digital;
761         s->insn_bits    = pcl816_di_insn_bits;
762
763         /* Digital Output subdevice */
764         s = &dev->subdevices[3];
765         s->type         = COMEDI_SUBD_DO;
766         s->subdev_flags = SDF_WRITABLE;
767         s->n_chan       = 16;
768         s->maxdata      = 1;
769         s->range_table  = &range_digital;
770         s->insn_bits    = pcl816_do_insn_bits;
771
772         pcl816_reset(dev);
773
774         return 0;
775 }
776
777 static void pcl816_detach(struct comedi_device *dev)
778 {
779         struct pcl816_private *devpriv = dev->private;
780
781         if (dev->private) {
782                 pcl816_ai_cancel(dev, dev->read_subdev);
783                 pcl816_reset(dev);
784                 if (devpriv->dma)
785                         free_dma(devpriv->dma);
786                 if (devpriv->dmabuf[0])
787                         free_pages(devpriv->dmabuf[0], devpriv->dmapages);
788                 if (devpriv->dmabuf[1])
789                         free_pages(devpriv->dmabuf[1], devpriv->dmapages);
790         }
791         comedi_legacy_detach(dev);
792 }
793
794 static struct comedi_driver pcl816_driver = {
795         .driver_name    = "pcl816",
796         .module         = THIS_MODULE,
797         .attach         = pcl816_attach,
798         .detach         = pcl816_detach,
799         .board_name     = &boardtypes[0].name,
800         .num_names      = ARRAY_SIZE(boardtypes),
801         .offset         = sizeof(struct pcl816_board),
802 };
803 module_comedi_driver(pcl816_driver);
804
805 MODULE_AUTHOR("Comedi http://www.comedi.org");
806 MODULE_DESCRIPTION("Comedi low-level driver");
807 MODULE_LICENSE("GPL");