staging: rtl8723bs: remove sdio_drv_priv structure
[sfrench/cifs-2.6.git] / drivers / comedi / drivers / adv_pci1710.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * adv_pci1710.c
4  * Comedi driver for Advantech PCI-1710 series boards
5  * Author: Michal Dobes <dobes@tesnet.cz>
6  *
7  * Thanks to ZhenGang Shang <ZhenGang.Shang@Advantech.com.cn>
8  * for testing and information.
9  */
10
11 /*
12  * Driver: adv_pci1710
13  * Description: Comedi driver for Advantech PCI-1710 series boards
14  * Devices: [Advantech] PCI-1710 (adv_pci1710), PCI-1710HG, PCI-1711,
15  *   PCI-1713, PCI-1731
16  * Author: Michal Dobes <dobes@tesnet.cz>
17  * Updated: Fri, 29 Oct 2015 17:19:35 -0700
18  * Status: works
19  *
20  * Configuration options: not applicable, uses PCI auto config
21  *
22  * This driver supports AI, AO, DI and DO subdevices.
23  * AI subdevice supports cmd and insn interface,
24  * other subdevices support only insn interface.
25  *
26  * The PCI-1710 and PCI-1710HG have the same PCI device ID, so the
27  * driver cannot distinguish between them, as would be normal for a
28  * PCI driver.
29  */
30
31 #include <linux/module.h>
32 #include <linux/interrupt.h>
33
34 #include "../comedi_pci.h"
35
36 #include "comedi_8254.h"
37 #include "amcc_s5933.h"
38
39 /*
40  * PCI BAR2 Register map (dev->iobase)
41  */
42 #define PCI171X_AD_DATA_REG     0x00    /* R:   A/D data */
43 #define PCI171X_SOFTTRG_REG     0x00    /* W:   soft trigger for A/D */
44 #define PCI171X_RANGE_REG       0x02    /* W:   A/D gain/range register */
45 #define PCI171X_RANGE_DIFF      BIT(5)
46 #define PCI171X_RANGE_UNI       BIT(4)
47 #define PCI171X_RANGE_GAIN(x)   (((x) & 0x7) << 0)
48 #define PCI171X_MUX_REG         0x04    /* W:   A/D multiplexor control */
49 #define PCI171X_MUX_CHANH(x)    (((x) & 0xff) << 8)
50 #define PCI171X_MUX_CHANL(x)    (((x) & 0xff) << 0)
51 #define PCI171X_MUX_CHAN(x)     (PCI171X_MUX_CHANH(x) | PCI171X_MUX_CHANL(x))
52 #define PCI171X_STATUS_REG      0x06    /* R:   status register */
53 #define PCI171X_STATUS_IRQ      BIT(11) /* 1=IRQ occurred */
54 #define PCI171X_STATUS_FF       BIT(10) /* 1=FIFO is full, fatal error */
55 #define PCI171X_STATUS_FH       BIT(9)  /* 1=FIFO is half full */
56 #define PCI171X_STATUS_FE       BIT(8)  /* 1=FIFO is empty */
57 #define PCI171X_CTRL_REG        0x06    /* W:   control register */
58 #define PCI171X_CTRL_CNT0       BIT(6)  /* 1=ext. clk, 0=int. 100kHz clk */
59 #define PCI171X_CTRL_ONEFH      BIT(5)  /* 1=on FIFO half full, 0=on sample */
60 #define PCI171X_CTRL_IRQEN      BIT(4)  /* 1=enable IRQ */
61 #define PCI171X_CTRL_GATE       BIT(3)  /* 1=enable ext. trigger GATE (8254?) */
62 #define PCI171X_CTRL_EXT        BIT(2)  /* 1=enable ext. trigger source */
63 #define PCI171X_CTRL_PACER      BIT(1)  /* 1=enable int. 8254 trigger source */
64 #define PCI171X_CTRL_SW         BIT(0)  /* 1=enable software trigger source */
65 #define PCI171X_CLRINT_REG      0x08    /* W:   clear interrupts request */
66 #define PCI171X_CLRFIFO_REG     0x09    /* W:   clear FIFO */
67 #define PCI171X_DA_REG(x)       (0x0a + ((x) * 2)) /* W:   D/A register */
68 #define PCI171X_DAREF_REG       0x0e    /* W:   D/A reference control */
69 #define PCI171X_DAREF(c, r)     (((r) & 0x3) << ((c) * 2))
70 #define PCI171X_DAREF_MASK(c)   PCI171X_DAREF((c), 0x3)
71 #define PCI171X_DI_REG          0x10    /* R:   digital inputs */
72 #define PCI171X_DO_REG          0x10    /* W:   digital outputs */
73 #define PCI171X_TIMER_BASE      0x18    /* R/W: 8254 timer */
74
75 static const struct comedi_lrange pci1710_ai_range = {
76         9, {
77                 BIP_RANGE(5),           /* gain 1   (0x00) */
78                 BIP_RANGE(2.5),         /* gain 2   (0x01) */
79                 BIP_RANGE(1.25),        /* gain 4   (0x02) */
80                 BIP_RANGE(0.625),       /* gain 8   (0x03) */
81                 BIP_RANGE(10),          /* gain 0.5 (0x04) */
82                 UNI_RANGE(10),          /* gain 1   (0x00 | UNI) */
83                 UNI_RANGE(5),           /* gain 2   (0x01 | UNI) */
84                 UNI_RANGE(2.5),         /* gain 4   (0x02 | UNI) */
85                 UNI_RANGE(1.25)         /* gain 8   (0x03 | UNI) */
86         }
87 };
88
89 static const struct comedi_lrange pci1710hg_ai_range = {
90         12, {
91                 BIP_RANGE(5),           /* gain 1    (0x00) */
92                 BIP_RANGE(0.5),         /* gain 10   (0x01) */
93                 BIP_RANGE(0.05),        /* gain 100  (0x02) */
94                 BIP_RANGE(0.005),       /* gain 1000 (0x03) */
95                 BIP_RANGE(10),          /* gain 0.5  (0x04) */
96                 BIP_RANGE(1),           /* gain 5    (0x05) */
97                 BIP_RANGE(0.1),         /* gain 50   (0x06) */
98                 BIP_RANGE(0.01),        /* gain 500  (0x07) */
99                 UNI_RANGE(10),          /* gain 1    (0x00 | UNI) */
100                 UNI_RANGE(1),           /* gain 10   (0x01 | UNI) */
101                 UNI_RANGE(0.1),         /* gain 100  (0x02 | UNI) */
102                 UNI_RANGE(0.01)         /* gain 1000 (0x03 | UNI) */
103         }
104 };
105
106 static const struct comedi_lrange pci1711_ai_range = {
107         5, {
108                 BIP_RANGE(10),          /* gain 1  (0x00) */
109                 BIP_RANGE(5),           /* gain 2  (0x01) */
110                 BIP_RANGE(2.5),         /* gain 4  (0x02) */
111                 BIP_RANGE(1.25),        /* gain 8  (0x03) */
112                 BIP_RANGE(0.625)        /* gain 16 (0x04) */
113         }
114 };
115
116 static const struct comedi_lrange pci171x_ao_range = {
117         3, {
118                 UNI_RANGE(5),           /* internal -5V ref */
119                 UNI_RANGE(10),          /* internal -10V ref */
120                 RANGE_ext(0, 1)         /* external -Vref (+/-10V max) */
121         }
122 };
123
124 enum pci1710_boardid {
125         BOARD_PCI1710,
126         BOARD_PCI1710HG,
127         BOARD_PCI1711,
128         BOARD_PCI1713,
129         BOARD_PCI1731,
130 };
131
132 struct boardtype {
133         const char *name;
134         const struct comedi_lrange *ai_range;
135         unsigned int is_pci1711:1;
136         unsigned int is_pci1713:1;
137         unsigned int has_ao:1;
138 };
139
140 static const struct boardtype boardtypes[] = {
141         [BOARD_PCI1710] = {
142                 .name           = "pci1710",
143                 .ai_range       = &pci1710_ai_range,
144                 .has_ao         = 1,
145         },
146         [BOARD_PCI1710HG] = {
147                 .name           = "pci1710hg",
148                 .ai_range       = &pci1710hg_ai_range,
149                 .has_ao         = 1,
150         },
151         [BOARD_PCI1711] = {
152                 .name           = "pci1711",
153                 .ai_range       = &pci1711_ai_range,
154                 .is_pci1711     = 1,
155                 .has_ao         = 1,
156         },
157         [BOARD_PCI1713] = {
158                 .name           = "pci1713",
159                 .ai_range       = &pci1710_ai_range,
160                 .is_pci1713     = 1,
161         },
162         [BOARD_PCI1731] = {
163                 .name           = "pci1731",
164                 .ai_range       = &pci1711_ai_range,
165                 .is_pci1711     = 1,
166         },
167 };
168
169 struct pci1710_private {
170         unsigned int max_samples;
171         unsigned int ctrl;      /* control register value */
172         unsigned int ctrl_ext;  /* used to switch from TRIG_EXT to TRIG_xxx */
173         unsigned int mux_scan;  /* used to set the channel interval to scan */
174         unsigned char ai_et;
175         unsigned int act_chanlist[32];  /*  list of scanned channel */
176         unsigned char saved_seglen;     /* len of the non-repeating chanlist */
177         unsigned char da_ranges;        /*  copy of D/A outpit range register */
178         unsigned char unipolar_gain;    /* adjust for unipolar gain codes */
179 };
180
181 static int pci1710_ai_check_chanlist(struct comedi_device *dev,
182                                      struct comedi_subdevice *s,
183                                      struct comedi_cmd *cmd)
184 {
185         struct pci1710_private *devpriv = dev->private;
186         unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
187         unsigned int last_aref = CR_AREF(cmd->chanlist[0]);
188         unsigned int next_chan = (chan0 + 1) % s->n_chan;
189         unsigned int chansegment[32];
190         unsigned int seglen;
191         int i;
192
193         if (cmd->chanlist_len == 1) {
194                 devpriv->saved_seglen = cmd->chanlist_len;
195                 return 0;
196         }
197
198         /* first channel is always ok */
199         chansegment[0] = cmd->chanlist[0];
200
201         for (i = 1; i < cmd->chanlist_len; i++) {
202                 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
203                 unsigned int aref = CR_AREF(cmd->chanlist[i]);
204
205                 if (cmd->chanlist[0] == cmd->chanlist[i])
206                         break;  /*  we detected a loop, stop */
207
208                 if (aref == AREF_DIFF && (chan & 1)) {
209                         dev_err(dev->class_dev,
210                                 "Odd channel cannot be differential input!\n");
211                         return -EINVAL;
212                 }
213
214                 if (last_aref == AREF_DIFF)
215                         next_chan = (next_chan + 1) % s->n_chan;
216                 if (chan != next_chan) {
217                         dev_err(dev->class_dev,
218                                 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
219                                 i, chan, next_chan, chan0);
220                         return -EINVAL;
221                 }
222
223                 /* next correct channel in list */
224                 chansegment[i] = cmd->chanlist[i];
225                 last_aref = aref;
226         }
227         seglen = i;
228
229         for (i = 0; i < cmd->chanlist_len; i++) {
230                 if (cmd->chanlist[i] != chansegment[i % seglen]) {
231                         dev_err(dev->class_dev,
232                                 "bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
233                                 i, CR_CHAN(chansegment[i]),
234                                 CR_RANGE(chansegment[i]),
235                                 CR_AREF(chansegment[i]),
236                                 CR_CHAN(cmd->chanlist[i % seglen]),
237                                 CR_RANGE(cmd->chanlist[i % seglen]),
238                                 CR_AREF(chansegment[i % seglen]));
239                         return -EINVAL;
240                 }
241         }
242         devpriv->saved_seglen = seglen;
243
244         return 0;
245 }
246
247 static void pci1710_ai_setup_chanlist(struct comedi_device *dev,
248                                       struct comedi_subdevice *s,
249                                       unsigned int *chanlist,
250                                       unsigned int n_chan,
251                                       unsigned int seglen)
252 {
253         struct pci1710_private *devpriv = dev->private;
254         unsigned int first_chan = CR_CHAN(chanlist[0]);
255         unsigned int last_chan = CR_CHAN(chanlist[seglen - 1]);
256         unsigned int i;
257
258         for (i = 0; i < seglen; i++) {  /*  store range list to card */
259                 unsigned int chan = CR_CHAN(chanlist[i]);
260                 unsigned int range = CR_RANGE(chanlist[i]);
261                 unsigned int aref = CR_AREF(chanlist[i]);
262                 unsigned int rangeval = 0;
263
264                 if (aref == AREF_DIFF)
265                         rangeval |= PCI171X_RANGE_DIFF;
266                 if (comedi_range_is_unipolar(s, range)) {
267                         rangeval |= PCI171X_RANGE_UNI;
268                         range -= devpriv->unipolar_gain;
269                 }
270                 rangeval |= PCI171X_RANGE_GAIN(range);
271
272                 /* select channel and set range */
273                 outw(PCI171X_MUX_CHAN(chan), dev->iobase + PCI171X_MUX_REG);
274                 outw(rangeval, dev->iobase + PCI171X_RANGE_REG);
275
276                 devpriv->act_chanlist[i] = chan;
277         }
278         for ( ; i < n_chan; i++)        /* store remainder of channel list */
279                 devpriv->act_chanlist[i] = CR_CHAN(chanlist[i]);
280
281         /* select channel interval to scan */
282         devpriv->mux_scan = PCI171X_MUX_CHANL(first_chan) |
283                             PCI171X_MUX_CHANH(last_chan);
284         outw(devpriv->mux_scan, dev->iobase + PCI171X_MUX_REG);
285 }
286
287 static int pci1710_ai_eoc(struct comedi_device *dev,
288                           struct comedi_subdevice *s,
289                           struct comedi_insn *insn,
290                           unsigned long context)
291 {
292         unsigned int status;
293
294         status = inw(dev->iobase + PCI171X_STATUS_REG);
295         if ((status & PCI171X_STATUS_FE) == 0)
296                 return 0;
297         return -EBUSY;
298 }
299
300 static int pci1710_ai_read_sample(struct comedi_device *dev,
301                                   struct comedi_subdevice *s,
302                                   unsigned int cur_chan,
303                                   unsigned short *val)
304 {
305         const struct boardtype *board = dev->board_ptr;
306         struct pci1710_private *devpriv = dev->private;
307         unsigned short sample;
308         unsigned int chan;
309
310         sample = inw(dev->iobase + PCI171X_AD_DATA_REG);
311         if (!board->is_pci1713) {
312                 /*
313                  * The upper 4 bits of the 16-bit sample are the channel number
314                  * that the sample was acquired from. Verify that this channel
315                  * number matches the expected channel number.
316                  */
317                 chan = sample >> 12;
318                 if (chan != devpriv->act_chanlist[cur_chan]) {
319                         dev_err(dev->class_dev,
320                                 "A/D data dropout: received from channel %d, expected %d\n",
321                                 chan, devpriv->act_chanlist[cur_chan]);
322                         return -ENODATA;
323                 }
324         }
325         *val = sample & s->maxdata;
326         return 0;
327 }
328
329 static int pci1710_ai_insn_read(struct comedi_device *dev,
330                                 struct comedi_subdevice *s,
331                                 struct comedi_insn *insn,
332                                 unsigned int *data)
333 {
334         struct pci1710_private *devpriv = dev->private;
335         int ret = 0;
336         int i;
337
338         /* enable software trigger */
339         devpriv->ctrl |= PCI171X_CTRL_SW;
340         outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
341
342         outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
343         outb(0, dev->iobase + PCI171X_CLRINT_REG);
344
345         pci1710_ai_setup_chanlist(dev, s, &insn->chanspec, 1, 1);
346
347         for (i = 0; i < insn->n; i++) {
348                 unsigned short val;
349
350                 /* start conversion */
351                 outw(0, dev->iobase + PCI171X_SOFTTRG_REG);
352
353                 ret = comedi_timeout(dev, s, insn, pci1710_ai_eoc, 0);
354                 if (ret)
355                         break;
356
357                 ret = pci1710_ai_read_sample(dev, s, 0, &val);
358                 if (ret)
359                         break;
360
361                 data[i] = val;
362         }
363
364         /* disable software trigger */
365         devpriv->ctrl &= ~PCI171X_CTRL_SW;
366         outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
367
368         outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
369         outb(0, dev->iobase + PCI171X_CLRINT_REG);
370
371         return ret ? ret : insn->n;
372 }
373
374 static int pci1710_ai_cancel(struct comedi_device *dev,
375                              struct comedi_subdevice *s)
376 {
377         struct pci1710_private *devpriv = dev->private;
378
379         /* disable A/D triggers and interrupt sources */
380         devpriv->ctrl &= PCI171X_CTRL_CNT0;     /* preserve counter 0 clk src */
381         outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
382
383         /* disable pacer */
384         comedi_8254_pacer_enable(dev->pacer, 1, 2, false);
385
386         /* clear A/D FIFO and any pending interrutps */
387         outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
388         outb(0, dev->iobase + PCI171X_CLRINT_REG);
389
390         return 0;
391 }
392
393 static void pci1710_handle_every_sample(struct comedi_device *dev,
394                                         struct comedi_subdevice *s)
395 {
396         struct comedi_cmd *cmd = &s->async->cmd;
397         unsigned int status;
398         unsigned short val;
399         int ret;
400
401         status = inw(dev->iobase + PCI171X_STATUS_REG);
402         if (status & PCI171X_STATUS_FE) {
403                 dev_dbg(dev->class_dev, "A/D FIFO empty (%4x)\n", status);
404                 s->async->events |= COMEDI_CB_ERROR;
405                 return;
406         }
407         if (status & PCI171X_STATUS_FF) {
408                 dev_dbg(dev->class_dev,
409                         "A/D FIFO Full status (Fatal Error!) (%4x)\n", status);
410                 s->async->events |= COMEDI_CB_ERROR;
411                 return;
412         }
413
414         outb(0, dev->iobase + PCI171X_CLRINT_REG);
415
416         for (; !(inw(dev->iobase + PCI171X_STATUS_REG) & PCI171X_STATUS_FE);) {
417                 ret = pci1710_ai_read_sample(dev, s, s->async->cur_chan, &val);
418                 if (ret) {
419                         s->async->events |= COMEDI_CB_ERROR;
420                         break;
421                 }
422
423                 comedi_buf_write_samples(s, &val, 1);
424
425                 if (cmd->stop_src == TRIG_COUNT &&
426                     s->async->scans_done >= cmd->stop_arg) {
427                         s->async->events |= COMEDI_CB_EOA;
428                         break;
429                 }
430         }
431
432         outb(0, dev->iobase + PCI171X_CLRINT_REG);
433 }
434
435 static void pci1710_handle_fifo(struct comedi_device *dev,
436                                 struct comedi_subdevice *s)
437 {
438         struct pci1710_private *devpriv = dev->private;
439         struct comedi_async *async = s->async;
440         struct comedi_cmd *cmd = &async->cmd;
441         unsigned int status;
442         int i;
443
444         status = inw(dev->iobase + PCI171X_STATUS_REG);
445         if (!(status & PCI171X_STATUS_FH)) {
446                 dev_dbg(dev->class_dev, "A/D FIFO not half full!\n");
447                 async->events |= COMEDI_CB_ERROR;
448                 return;
449         }
450         if (status & PCI171X_STATUS_FF) {
451                 dev_dbg(dev->class_dev,
452                         "A/D FIFO Full status (Fatal Error!)\n");
453                 async->events |= COMEDI_CB_ERROR;
454                 return;
455         }
456
457         for (i = 0; i < devpriv->max_samples; i++) {
458                 unsigned short val;
459                 int ret;
460
461                 ret = pci1710_ai_read_sample(dev, s, s->async->cur_chan, &val);
462                 if (ret) {
463                         s->async->events |= COMEDI_CB_ERROR;
464                         break;
465                 }
466
467                 if (!comedi_buf_write_samples(s, &val, 1))
468                         break;
469
470                 if (cmd->stop_src == TRIG_COUNT &&
471                     async->scans_done >= cmd->stop_arg) {
472                         async->events |= COMEDI_CB_EOA;
473                         break;
474                 }
475         }
476
477         outb(0, dev->iobase + PCI171X_CLRINT_REG);
478 }
479
480 static irqreturn_t pci1710_irq_handler(int irq, void *d)
481 {
482         struct comedi_device *dev = d;
483         struct pci1710_private *devpriv = dev->private;
484         struct comedi_subdevice *s;
485         struct comedi_cmd *cmd;
486
487         if (!dev->attached)     /*  is device attached? */
488                 return IRQ_NONE;        /*  no, exit */
489
490         s = dev->read_subdev;
491         cmd = &s->async->cmd;
492
493         /*  is this interrupt from our board? */
494         if (!(inw(dev->iobase + PCI171X_STATUS_REG) & PCI171X_STATUS_IRQ))
495                 return IRQ_NONE;        /*  no, exit */
496
497         if (devpriv->ai_et) {   /*  Switch from initial TRIG_EXT to TRIG_xxx. */
498                 devpriv->ai_et = 0;
499                 devpriv->ctrl &= PCI171X_CTRL_CNT0;
500                 devpriv->ctrl |= PCI171X_CTRL_SW; /* set software trigger */
501                 outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
502                 devpriv->ctrl = devpriv->ctrl_ext;
503                 outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
504                 outb(0, dev->iobase + PCI171X_CLRINT_REG);
505                 /* no sample on this interrupt; reset the channel interval */
506                 outw(devpriv->mux_scan, dev->iobase + PCI171X_MUX_REG);
507                 outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
508                 comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
509                 return IRQ_HANDLED;
510         }
511
512         if (cmd->flags & CMDF_WAKE_EOS)
513                 pci1710_handle_every_sample(dev, s);
514         else
515                 pci1710_handle_fifo(dev, s);
516
517         comedi_handle_events(dev, s);
518
519         return IRQ_HANDLED;
520 }
521
522 static int pci1710_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
523 {
524         struct pci1710_private *devpriv = dev->private;
525         struct comedi_cmd *cmd = &s->async->cmd;
526
527         pci1710_ai_setup_chanlist(dev, s, cmd->chanlist, cmd->chanlist_len,
528                                   devpriv->saved_seglen);
529
530         outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
531         outb(0, dev->iobase + PCI171X_CLRINT_REG);
532
533         devpriv->ctrl &= PCI171X_CTRL_CNT0;
534         if ((cmd->flags & CMDF_WAKE_EOS) == 0)
535                 devpriv->ctrl |= PCI171X_CTRL_ONEFH;
536
537         if (cmd->convert_src == TRIG_TIMER) {
538                 comedi_8254_update_divisors(dev->pacer);
539
540                 devpriv->ctrl |= PCI171X_CTRL_PACER | PCI171X_CTRL_IRQEN;
541                 if (cmd->start_src == TRIG_EXT) {
542                         devpriv->ctrl_ext = devpriv->ctrl;
543                         devpriv->ctrl &= ~(PCI171X_CTRL_PACER |
544                                            PCI171X_CTRL_ONEFH |
545                                            PCI171X_CTRL_GATE);
546                         devpriv->ctrl |= PCI171X_CTRL_EXT;
547                         devpriv->ai_et = 1;
548                 } else {        /* TRIG_NOW */
549                         devpriv->ai_et = 0;
550                 }
551                 outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
552
553                 if (cmd->start_src == TRIG_NOW)
554                         comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
555         } else {        /* TRIG_EXT */
556                 devpriv->ctrl |= PCI171X_CTRL_EXT | PCI171X_CTRL_IRQEN;
557                 outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
558         }
559
560         return 0;
561 }
562
563 static int pci1710_ai_cmdtest(struct comedi_device *dev,
564                               struct comedi_subdevice *s,
565                               struct comedi_cmd *cmd)
566 {
567         int err = 0;
568
569         /* Step 1 : check if triggers are trivially valid */
570
571         err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
572         err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
573         err |= comedi_check_trigger_src(&cmd->convert_src,
574                                         TRIG_TIMER | TRIG_EXT);
575         err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
576         err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
577
578         if (err)
579                 return 1;
580
581         /* step 2a: make sure trigger sources are unique */
582
583         err |= comedi_check_trigger_is_unique(cmd->start_src);
584         err |= comedi_check_trigger_is_unique(cmd->convert_src);
585         err |= comedi_check_trigger_is_unique(cmd->stop_src);
586
587         /* step 2b: and mutually compatible */
588
589         if (err)
590                 return 2;
591
592         /* Step 3: check if arguments are trivially valid */
593
594         err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
595         err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
596
597         if (cmd->convert_src == TRIG_TIMER)
598                 err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000);
599         else    /* TRIG_FOLLOW */
600                 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
601
602         err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
603                                            cmd->chanlist_len);
604
605         if (cmd->stop_src == TRIG_COUNT)
606                 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
607         else    /* TRIG_NONE */
608                 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
609
610         if (err)
611                 return 3;
612
613         /* step 4: fix up any arguments */
614
615         if (cmd->convert_src == TRIG_TIMER) {
616                 unsigned int arg = cmd->convert_arg;
617
618                 comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
619                 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
620         }
621
622         if (err)
623                 return 4;
624
625         /* Step 5: check channel list */
626
627         err |= pci1710_ai_check_chanlist(dev, s, cmd);
628
629         if (err)
630                 return 5;
631
632         return 0;
633 }
634
635 static int pci1710_ao_insn_write(struct comedi_device *dev,
636                                  struct comedi_subdevice *s,
637                                  struct comedi_insn *insn,
638                                  unsigned int *data)
639 {
640         struct pci1710_private *devpriv = dev->private;
641         unsigned int chan = CR_CHAN(insn->chanspec);
642         unsigned int range = CR_RANGE(insn->chanspec);
643         unsigned int val = s->readback[chan];
644         int i;
645
646         devpriv->da_ranges &= ~PCI171X_DAREF_MASK(chan);
647         devpriv->da_ranges |= PCI171X_DAREF(chan, range);
648         outw(devpriv->da_ranges, dev->iobase + PCI171X_DAREF_REG);
649
650         for (i = 0; i < insn->n; i++) {
651                 val = data[i];
652                 outw(val, dev->iobase + PCI171X_DA_REG(chan));
653         }
654
655         s->readback[chan] = val;
656
657         return insn->n;
658 }
659
660 static int pci1710_di_insn_bits(struct comedi_device *dev,
661                                 struct comedi_subdevice *s,
662                                 struct comedi_insn *insn,
663                                 unsigned int *data)
664 {
665         data[1] = inw(dev->iobase + PCI171X_DI_REG);
666
667         return insn->n;
668 }
669
670 static int pci1710_do_insn_bits(struct comedi_device *dev,
671                                 struct comedi_subdevice *s,
672                                 struct comedi_insn *insn,
673                                 unsigned int *data)
674 {
675         if (comedi_dio_update_state(s, data))
676                 outw(s->state, dev->iobase + PCI171X_DO_REG);
677
678         data[1] = s->state;
679
680         return insn->n;
681 }
682
683 static int pci1710_counter_insn_config(struct comedi_device *dev,
684                                        struct comedi_subdevice *s,
685                                        struct comedi_insn *insn,
686                                        unsigned int *data)
687 {
688         struct pci1710_private *devpriv = dev->private;
689
690         switch (data[0]) {
691         case INSN_CONFIG_SET_CLOCK_SRC:
692                 switch (data[1]) {
693                 case 0: /* internal */
694                         devpriv->ctrl_ext &= ~PCI171X_CTRL_CNT0;
695                         break;
696                 case 1: /* external */
697                         devpriv->ctrl_ext |= PCI171X_CTRL_CNT0;
698                         break;
699                 default:
700                         return -EINVAL;
701                 }
702                 outw(devpriv->ctrl_ext, dev->iobase + PCI171X_CTRL_REG);
703                 break;
704         case INSN_CONFIG_GET_CLOCK_SRC:
705                 if (devpriv->ctrl_ext & PCI171X_CTRL_CNT0) {
706                         data[1] = 1;
707                         data[2] = 0;
708                 } else {
709                         data[1] = 0;
710                         data[2] = I8254_OSC_BASE_1MHZ;
711                 }
712                 break;
713         default:
714                 return -EINVAL;
715         }
716
717         return insn->n;
718 }
719
720 static void pci1710_reset(struct comedi_device *dev)
721 {
722         const struct boardtype *board = dev->board_ptr;
723
724         /*
725          * Disable A/D triggers and interrupt sources, set counter 0
726          * to use internal 1 MHz clock.
727          */
728         outw(0, dev->iobase + PCI171X_CTRL_REG);
729
730         /* clear A/D FIFO and any pending interrutps */
731         outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
732         outb(0, dev->iobase + PCI171X_CLRINT_REG);
733
734         if (board->has_ao) {
735                 /* set DACs to 0..5V and outputs to 0V */
736                 outb(0, dev->iobase + PCI171X_DAREF_REG);
737                 outw(0, dev->iobase + PCI171X_DA_REG(0));
738                 outw(0, dev->iobase + PCI171X_DA_REG(1));
739         }
740
741         /* set digital outputs to 0 */
742         outw(0, dev->iobase + PCI171X_DO_REG);
743 }
744
745 static int pci1710_auto_attach(struct comedi_device *dev,
746                                unsigned long context)
747 {
748         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
749         const struct boardtype *board = NULL;
750         struct pci1710_private *devpriv;
751         struct comedi_subdevice *s;
752         int ret, subdev, n_subdevices;
753         int i;
754
755         if (context < ARRAY_SIZE(boardtypes))
756                 board = &boardtypes[context];
757         if (!board)
758                 return -ENODEV;
759         dev->board_ptr = board;
760         dev->board_name = board->name;
761
762         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
763         if (!devpriv)
764                 return -ENOMEM;
765
766         ret = comedi_pci_enable(dev);
767         if (ret)
768                 return ret;
769         dev->iobase = pci_resource_start(pcidev, 2);
770
771         dev->pacer = comedi_8254_init(dev->iobase + PCI171X_TIMER_BASE,
772                                       I8254_OSC_BASE_10MHZ, I8254_IO16, 0);
773         if (!dev->pacer)
774                 return -ENOMEM;
775
776         n_subdevices = 1;       /* all boards have analog inputs */
777         if (board->has_ao)
778                 n_subdevices++;
779         if (!board->is_pci1713) {
780                 /*
781                  * All other boards have digital inputs and outputs as
782                  * well as a user counter.
783                  */
784                 n_subdevices += 3;
785         }
786
787         ret = comedi_alloc_subdevices(dev, n_subdevices);
788         if (ret)
789                 return ret;
790
791         pci1710_reset(dev);
792
793         if (pcidev->irq) {
794                 ret = request_irq(pcidev->irq, pci1710_irq_handler,
795                                   IRQF_SHARED, dev->board_name, dev);
796                 if (ret == 0)
797                         dev->irq = pcidev->irq;
798         }
799
800         subdev = 0;
801
802         /* Analog Input subdevice */
803         s = &dev->subdevices[subdev++];
804         s->type         = COMEDI_SUBD_AI;
805         s->subdev_flags = SDF_READABLE | SDF_GROUND;
806         if (!board->is_pci1711)
807                 s->subdev_flags |= SDF_DIFF;
808         s->n_chan       = board->is_pci1713 ? 32 : 16;
809         s->maxdata      = 0x0fff;
810         s->range_table  = board->ai_range;
811         s->insn_read    = pci1710_ai_insn_read;
812         if (dev->irq) {
813                 dev->read_subdev = s;
814                 s->subdev_flags |= SDF_CMD_READ;
815                 s->len_chanlist = s->n_chan;
816                 s->do_cmdtest   = pci1710_ai_cmdtest;
817                 s->do_cmd       = pci1710_ai_cmd;
818                 s->cancel       = pci1710_ai_cancel;
819         }
820
821         /* find the value needed to adjust for unipolar gain codes */
822         for (i = 0; i < s->range_table->length; i++) {
823                 if (comedi_range_is_unipolar(s, i)) {
824                         devpriv->unipolar_gain = i;
825                         break;
826                 }
827         }
828
829         if (board->has_ao) {
830                 /* Analog Output subdevice */
831                 s = &dev->subdevices[subdev++];
832                 s->type         = COMEDI_SUBD_AO;
833                 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
834                 s->n_chan       = 2;
835                 s->maxdata      = 0x0fff;
836                 s->range_table  = &pci171x_ao_range;
837                 s->insn_write   = pci1710_ao_insn_write;
838
839                 ret = comedi_alloc_subdev_readback(s);
840                 if (ret)
841                         return ret;
842         }
843
844         if (!board->is_pci1713) {
845                 /* Digital Input subdevice */
846                 s = &dev->subdevices[subdev++];
847                 s->type         = COMEDI_SUBD_DI;
848                 s->subdev_flags = SDF_READABLE;
849                 s->n_chan       = 16;
850                 s->maxdata      = 1;
851                 s->range_table  = &range_digital;
852                 s->insn_bits    = pci1710_di_insn_bits;
853
854                 /* Digital Output subdevice */
855                 s = &dev->subdevices[subdev++];
856                 s->type         = COMEDI_SUBD_DO;
857                 s->subdev_flags = SDF_WRITABLE;
858                 s->n_chan       = 16;
859                 s->maxdata      = 1;
860                 s->range_table  = &range_digital;
861                 s->insn_bits    = pci1710_do_insn_bits;
862
863                 /* Counter subdevice (8254) */
864                 s = &dev->subdevices[subdev++];
865                 comedi_8254_subdevice_init(s, dev->pacer);
866
867                 dev->pacer->insn_config = pci1710_counter_insn_config;
868
869                 /* counters 1 and 2 are used internally for the pacer */
870                 comedi_8254_set_busy(dev->pacer, 1, true);
871                 comedi_8254_set_busy(dev->pacer, 2, true);
872         }
873
874         /* max_samples is half the FIFO size (2 bytes/sample) */
875         devpriv->max_samples = (board->is_pci1711) ? 512 : 2048;
876
877         return 0;
878 }
879
880 static struct comedi_driver adv_pci1710_driver = {
881         .driver_name    = "adv_pci1710",
882         .module         = THIS_MODULE,
883         .auto_attach    = pci1710_auto_attach,
884         .detach         = comedi_pci_detach,
885 };
886
887 static int adv_pci1710_pci_probe(struct pci_dev *dev,
888                                  const struct pci_device_id *id)
889 {
890         return comedi_pci_auto_config(dev, &adv_pci1710_driver,
891                                       id->driver_data);
892 }
893
894 static const struct pci_device_id adv_pci1710_pci_table[] = {
895         {
896                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
897                                PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050),
898                 .driver_data = BOARD_PCI1710,
899         }, {
900                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
901                                PCI_VENDOR_ID_ADVANTECH, 0x0000),
902                 .driver_data = BOARD_PCI1710,
903         }, {
904                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
905                                PCI_VENDOR_ID_ADVANTECH, 0xb100),
906                 .driver_data = BOARD_PCI1710,
907         }, {
908                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
909                                PCI_VENDOR_ID_ADVANTECH, 0xb200),
910                 .driver_data = BOARD_PCI1710,
911         }, {
912                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
913                                PCI_VENDOR_ID_ADVANTECH, 0xc100),
914                 .driver_data = BOARD_PCI1710,
915         }, {
916                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
917                                PCI_VENDOR_ID_ADVANTECH, 0xc200),
918                 .driver_data = BOARD_PCI1710,
919         }, {
920                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd100),
921                 .driver_data = BOARD_PCI1710,
922         }, {
923                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
924                                PCI_VENDOR_ID_ADVANTECH, 0x0002),
925                 .driver_data = BOARD_PCI1710HG,
926         }, {
927                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
928                                PCI_VENDOR_ID_ADVANTECH, 0xb102),
929                 .driver_data = BOARD_PCI1710HG,
930         }, {
931                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
932                                PCI_VENDOR_ID_ADVANTECH, 0xb202),
933                 .driver_data = BOARD_PCI1710HG,
934         }, {
935                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
936                                PCI_VENDOR_ID_ADVANTECH, 0xc102),
937                 .driver_data = BOARD_PCI1710HG,
938         }, {
939                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
940                                PCI_VENDOR_ID_ADVANTECH, 0xc202),
941                 .driver_data = BOARD_PCI1710HG,
942         }, {
943                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd102),
944                 .driver_data = BOARD_PCI1710HG,
945         },
946         { PCI_VDEVICE(ADVANTECH, 0x1711), BOARD_PCI1711 },
947         { PCI_VDEVICE(ADVANTECH, 0x1713), BOARD_PCI1713 },
948         { PCI_VDEVICE(ADVANTECH, 0x1731), BOARD_PCI1731 },
949         { 0 }
950 };
951 MODULE_DEVICE_TABLE(pci, adv_pci1710_pci_table);
952
953 static struct pci_driver adv_pci1710_pci_driver = {
954         .name           = "adv_pci1710",
955         .id_table       = adv_pci1710_pci_table,
956         .probe          = adv_pci1710_pci_probe,
957         .remove         = comedi_pci_auto_unconfig,
958 };
959 module_comedi_pci_driver(adv_pci1710_driver, adv_pci1710_pci_driver);
960
961 MODULE_AUTHOR("Comedi https://www.comedi.org");
962 MODULE_DESCRIPTION("Comedi: Advantech PCI-1710 Series Multifunction DAS Cards");
963 MODULE_LICENSE("GPL");