66e71d5193a2650629175fbb8e51553fcc799508
[cascardo/linux.git] / drivers / staging / comedi / drivers / addi-data / hwdrv_apci3120.c
1 /*
2  * This is a handler for the DMA interrupt.
3  * This function copies the data to Comedi Buffer.
4  * For continuous DMA it reinitializes the DMA operation.
5  * For single mode DMA it stop the acquisition.
6  */
7 static void apci3120_interrupt_dma(int irq, void *d)
8 {
9         struct comedi_device *dev = d;
10         struct apci3120_private *devpriv = dev->private;
11         struct comedi_subdevice *s = dev->read_subdev;
12         struct comedi_async *async = s->async;
13         struct comedi_cmd *cmd = &async->cmd;
14         struct apci3120_dmabuf *dmabuf;
15         unsigned int samplesinbuf;
16
17         dmabuf = &devpriv->dmabuf[devpriv->cur_dmabuf];
18
19         samplesinbuf = dmabuf->use_size - inl(devpriv->amcc + AMCC_OP_REG_MWTC);
20
21         if (samplesinbuf < dmabuf->use_size)
22                 dev_err(dev->class_dev, "Interrupted DMA transfer!\n");
23         if (samplesinbuf & 1) {
24                 dev_err(dev->class_dev, "Odd count of bytes in DMA ring!\n");
25                 async->events |= COMEDI_CB_ERROR;
26                 return;
27         }
28         samplesinbuf = samplesinbuf >> 1;       /*  number of received samples */
29
30         if (devpriv->use_double_buffer) {
31                 struct apci3120_dmabuf *next_dmabuf;
32
33                 next_dmabuf = &devpriv->dmabuf[!devpriv->cur_dmabuf];
34
35                 /* start DMA on next buffer */
36                 apci3120_init_dma(dev, next_dmabuf);
37         }
38
39         if (samplesinbuf) {
40                 comedi_buf_write_samples(s, dmabuf->virt, samplesinbuf);
41
42                 if (!(cmd->flags & CMDF_WAKE_EOS))
43                         async->events |= COMEDI_CB_EOS;
44         }
45
46         if ((async->events & COMEDI_CB_CANCEL_MASK) ||
47             (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg))
48                 return;
49
50         if (devpriv->use_double_buffer) {
51                 /* switch dma buffers for next interrupt */
52                 devpriv->cur_dmabuf = !devpriv->cur_dmabuf;
53         } else {
54                 /* restart DMA if is not using double buffering */
55                 apci3120_init_dma(dev, dmabuf);
56         }
57 }
58
59 static irqreturn_t apci3120_interrupt(int irq, void *d)
60 {
61         struct comedi_device *dev = d;
62         struct apci3120_private *devpriv = dev->private;
63         struct comedi_subdevice *s = dev->read_subdev;
64         struct comedi_async *async = s->async;
65         struct comedi_cmd *cmd = &async->cmd;
66         unsigned int status;
67         unsigned int int_amcc;
68
69         status = inw(dev->iobase + APCI3120_STATUS_REG);
70         int_amcc = inl(devpriv->amcc + AMCC_OP_REG_INTCSR);
71
72         if (!(status & APCI3120_STATUS_INT_MASK) &&
73             !(int_amcc & ANY_S593X_INT)) {
74                 dev_err(dev->class_dev, "IRQ from unknown source\n");
75                 return IRQ_NONE;
76         }
77
78         outl(int_amcc | AINT_INT_MASK, devpriv->amcc + AMCC_OP_REG_INTCSR);
79
80         if (devpriv->ctrl & APCI3120_CTRL_EXT_TRIG)
81                 apci3120_exttrig_enable(dev, false);
82
83         if (int_amcc & MASTER_ABORT_INT)
84                 dev_err(dev->class_dev, "AMCC IRQ - MASTER DMA ABORT!\n");
85         if (int_amcc & TARGET_ABORT_INT)
86                 dev_err(dev->class_dev, "AMCC IRQ - TARGET DMA ABORT!\n");
87
88         if ((status & APCI3120_STATUS_EOC_INT) == 0 &&
89             (devpriv->mode & APCI3120_MODE_EOC_IRQ_ENA)) {
90                 /* nothing to do... EOC mode is not currently used */
91         }
92
93         if ((status & APCI3120_STATUS_EOS_INT) &&
94             (devpriv->mode & APCI3120_MODE_EOS_IRQ_ENA)) {
95                 unsigned short val;
96                 int i;
97
98                 for (i = 0; i < cmd->chanlist_len; i++) {
99                         val = inw(dev->iobase + APCI3120_AI_FIFO_REG);
100                         comedi_buf_write_samples(s, &val, 1);
101                 }
102
103                 devpriv->mode |= APCI3120_MODE_EOS_IRQ_ENA;
104                 outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG);
105         }
106
107         if (status & APCI3120_STATUS_TIMER2_INT) {
108                 /*
109                  * for safety...
110                  * timer2 interrupts are not enabled in the driver
111                  */
112                 apci3120_clr_timer2_interrupt(dev);
113         }
114
115         if (status & APCI3120_STATUS_AMCC_INT) {
116                 /* AMCC- Clear write complete interrupt (DMA) */
117                 outl(AINT_WT_COMPLETE, devpriv->amcc + AMCC_OP_REG_INTCSR);
118
119                 /* do some data transfer */
120                 apci3120_interrupt_dma(irq, d);
121         }
122
123         if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg)
124                 async->events |= COMEDI_CB_EOA;
125
126         comedi_handle_events(dev, s);
127
128         return IRQ_HANDLED;
129 }