Linux 3.18-rc3
[cascardo/linux.git] / drivers / staging / comedi / drivers / pcmmio.c
1 /*
2  * pcmmio.c
3  * Driver for Winsystems PC-104 based multifunction IO board.
4  *
5  * COMEDI - Linux Control and Measurement Device Interface
6  * Copyright (C) 2007 Calin A. Culianu <calin@ajvar.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  */
18
19 /*
20  * Driver: pcmmio
21  * Description: A driver for the PCM-MIO multifunction board
22  * Devices: (Winsystems) PCM-MIO [pcmmio]
23  * Author: Calin Culianu <calin@ajvar.org>
24  * Updated: Wed, May 16 2007 16:21:10 -0500
25  * Status: works
26  *
27  * A driver for the PCM-MIO multifunction board from Winsystems. This
28  * is a PC-104 based I/O board. It contains four subdevices:
29  *
30  *      subdevice 0 - 16 channels of 16-bit AI
31  *      subdevice 1 - 8 channels of 16-bit AO
32  *      subdevice 2 - first 24 channels of the 48 channel of DIO
33  *                      (with edge-triggered interrupt support)
34  *      subdevice 3 - last 24 channels of the 48 channel DIO
35  *                      (no interrupt support for this bank of channels)
36  *
37  * Some notes:
38  *
39  * Synchronous reads and writes are the only things implemented for analog
40  * input and output. The hardware itself can do streaming acquisition, etc.
41  *
42  * Asynchronous I/O for the DIO subdevices *is* implemented, however! They
43  * are basically edge-triggered interrupts for any configuration of the
44  * channels in subdevice 2.
45  *
46  * Also note that this interrupt support is untested.
47  *
48  * A few words about edge-detection IRQ support (commands on DIO):
49  *
50  * To use edge-detection IRQ support for the DIO subdevice, pass the IRQ
51  * of the board to the comedi_config command. The board IRQ is not jumpered
52  * but rather configured through software, so any IRQ from 1-15 is OK.
53  *
54  * Due to the genericity of the comedi API, you need to create a special
55  * comedi_command in order to use edge-triggered interrupts for DIO.
56  *
57  * Use comedi_commands with TRIG_NOW.  Your callback will be called each
58  * time an edge is detected on the specified DIO line(s), and the data
59  * values will be two sample_t's, which should be concatenated to form
60  * one 32-bit unsigned int. This value is the mask of channels that had
61  * edges detected from your channel list. Note that the bits positions
62  * in the mask correspond to positions in your chanlist when you
63  * specified the command and *not* channel id's!
64  *
65  * To set the polarity of the edge-detection interrupts pass a nonzero value
66  * for either CR_RANGE or CR_AREF for edge-up polarity, or a zero
67  * value for both CR_RANGE and CR_AREF if you want edge-down polarity.
68  *
69  * Configuration Options:
70  *   [0] - I/O port base address
71  *   [1] - IRQ (optional -- for edge-detect interrupt support only,
72  *              leave out if you don't need this feature)
73  */
74
75 #include <linux/module.h>
76 #include <linux/interrupt.h>
77 #include <linux/slab.h>
78
79 #include "../comedidev.h"
80
81 #include "comedi_fc.h"
82
83 /*
84  * Register I/O map
85  */
86 #define PCMMIO_AI_LSB_REG                       0x00
87 #define PCMMIO_AI_MSB_REG                       0x01
88 #define PCMMIO_AI_CMD_REG                       0x02
89 #define PCMMIO_AI_CMD_SE                        (1 << 7)
90 #define PCMMIO_AI_CMD_ODD_CHAN                  (1 << 6)
91 #define PCMMIO_AI_CMD_CHAN_SEL(x)               (((x) & 0x3) << 4)
92 #define PCMMIO_AI_CMD_RANGE(x)                  (((x) & 0x3) << 2)
93 #define PCMMIO_RESOURCE_REG                     0x02
94 #define PCMMIO_RESOURCE_IRQ(x)                  (((x) & 0xf) << 0)
95 #define PCMMIO_AI_STATUS_REG                    0x03
96 #define PCMMIO_AI_STATUS_DATA_READY             (1 << 7)
97 #define PCMMIO_AI_STATUS_DATA_DMA_PEND          (1 << 6)
98 #define PCMMIO_AI_STATUS_CMD_DMA_PEND           (1 << 5)
99 #define PCMMIO_AI_STATUS_IRQ_PEND               (1 << 4)
100 #define PCMMIO_AI_STATUS_DATA_DRQ_ENA           (1 << 2)
101 #define PCMMIO_AI_STATUS_REG_SEL                (1 << 3)
102 #define PCMMIO_AI_STATUS_CMD_DRQ_ENA            (1 << 1)
103 #define PCMMIO_AI_STATUS_IRQ_ENA                (1 << 0)
104 #define PCMMIO_AI_RES_ENA_REG                   0x03
105 #define PCMMIO_AI_RES_ENA_CMD_REG_ACCESS        (0 << 3)
106 #define PCMMIO_AI_RES_ENA_AI_RES_ACCESS         (1 << 3)
107 #define PCMMIO_AI_RES_ENA_DIO_RES_ACCESS        (1 << 4)
108 #define PCMMIO_AI_2ND_ADC_OFFSET                0x04
109
110 #define PCMMIO_AO_LSB_REG                       0x08
111 #define PCMMIO_AO_LSB_SPAN(x)                   (((x) & 0xf) << 0)
112 #define PCMMIO_AO_MSB_REG                       0x09
113 #define PCMMIO_AO_CMD_REG                       0x0a
114 #define PCMMIO_AO_CMD_WR_SPAN                   (0x2 << 4)
115 #define PCMMIO_AO_CMD_WR_CODE                   (0x3 << 4)
116 #define PCMMIO_AO_CMD_UPDATE                    (0x4 << 4)
117 #define PCMMIO_AO_CMD_UPDATE_ALL                (0x5 << 4)
118 #define PCMMIO_AO_CMD_WR_SPAN_UPDATE            (0x6 << 4)
119 #define PCMMIO_AO_CMD_WR_CODE_UPDATE            (0x7 << 4)
120 #define PCMMIO_AO_CMD_WR_SPAN_UPDATE_ALL        (0x8 << 4)
121 #define PCMMIO_AO_CMD_WR_CODE_UPDATE_ALL        (0x9 << 4)
122 #define PCMMIO_AO_CMD_RD_B1_SPAN                (0xa << 4)
123 #define PCMMIO_AO_CMD_RD_B1_CODE                (0xb << 4)
124 #define PCMMIO_AO_CMD_RD_B2_SPAN                (0xc << 4)
125 #define PCMMIO_AO_CMD_RD_B2_CODE                (0xd << 4)
126 #define PCMMIO_AO_CMD_NOP                       (0xf << 4)
127 #define PCMMIO_AO_CMD_CHAN_SEL(x)               (((x) & 0x03) << 1)
128 #define PCMMIO_AO_CMD_CHAN_SEL_ALL              (0x0f << 0)
129 #define PCMMIO_AO_STATUS_REG                    0x0b
130 #define PCMMIO_AO_STATUS_DATA_READY             (1 << 7)
131 #define PCMMIO_AO_STATUS_DATA_DMA_PEND          (1 << 6)
132 #define PCMMIO_AO_STATUS_CMD_DMA_PEND           (1 << 5)
133 #define PCMMIO_AO_STATUS_IRQ_PEND               (1 << 4)
134 #define PCMMIO_AO_STATUS_DATA_DRQ_ENA           (1 << 2)
135 #define PCMMIO_AO_STATUS_REG_SEL                (1 << 3)
136 #define PCMMIO_AO_STATUS_CMD_DRQ_ENA            (1 << 1)
137 #define PCMMIO_AO_STATUS_IRQ_ENA                (1 << 0)
138 #define PCMMIO_AO_RESOURCE_ENA_REG              0x0b
139 #define PCMMIO_AO_2ND_DAC_OFFSET                0x04
140
141 /*
142  * WinSystems WS16C48
143  *
144  * Offset    Page 0       Page 1       Page 2       Page 3
145  * ------  -----------  -----------  -----------  -----------
146  *  0x10   Port 0 I/O   Port 0 I/O   Port 0 I/O   Port 0 I/O
147  *  0x11   Port 1 I/O   Port 1 I/O   Port 1 I/O   Port 1 I/O
148  *  0x12   Port 2 I/O   Port 2 I/O   Port 2 I/O   Port 2 I/O
149  *  0x13   Port 3 I/O   Port 3 I/O   Port 3 I/O   Port 3 I/O
150  *  0x14   Port 4 I/O   Port 4 I/O   Port 4 I/O   Port 4 I/O
151  *  0x15   Port 5 I/O   Port 5 I/O   Port 5 I/O   Port 5 I/O
152  *  0x16   INT_PENDING  INT_PENDING  INT_PENDING  INT_PENDING
153  *  0x17    Page/Lock    Page/Lock    Page/Lock    Page/Lock
154  *  0x18       N/A         POL_0       ENAB_0       INT_ID0
155  *  0x19       N/A         POL_1       ENAB_1       INT_ID1
156  *  0x1a       N/A         POL_2       ENAB_2       INT_ID2
157  */
158 #define PCMMIO_PORT_REG(x)                      (0x10 + (x))
159 #define PCMMIO_INT_PENDING_REG                  0x16
160 #define PCMMIO_PAGE_LOCK_REG                    0x17
161 #define PCMMIO_LOCK_PORT(x)                     ((1 << (x)) & 0x3f)
162 #define PCMMIO_PAGE(x)                          (((x) & 0x3) << 6)
163 #define PCMMIO_PAGE_MASK                        PCMUIO_PAGE(3)
164 #define PCMMIO_PAGE_POL                         1
165 #define PCMMIO_PAGE_ENAB                        2
166 #define PCMMIO_PAGE_INT_ID                      3
167 #define PCMMIO_PAGE_REG(x)                      (0x18 + (x))
168
169 static const struct comedi_lrange pcmmio_ai_ranges = {
170         4, {
171                 BIP_RANGE(5),
172                 BIP_RANGE(10),
173                 UNI_RANGE(5),
174                 UNI_RANGE(10)
175         }
176 };
177
178 static const struct comedi_lrange pcmmio_ao_ranges = {
179         6, {
180                 UNI_RANGE(5),
181                 UNI_RANGE(10),
182                 BIP_RANGE(5),
183                 BIP_RANGE(10),
184                 BIP_RANGE(2.5),
185                 RANGE(-2.5, 7.5)
186         }
187 };
188
189 struct pcmmio_private {
190         spinlock_t pagelock;    /* protects the page registers */
191         spinlock_t spinlock;    /* protects the member variables */
192         unsigned int enabled_mask;
193         unsigned int stop_count;
194         unsigned int active:1;
195 };
196
197 static void pcmmio_dio_write(struct comedi_device *dev, unsigned int val,
198                              int page, int port)
199 {
200         struct pcmmio_private *devpriv = dev->private;
201         unsigned long iobase = dev->iobase;
202         unsigned long flags;
203
204         spin_lock_irqsave(&devpriv->pagelock, flags);
205         if (page == 0) {
206                 /* Port registers are valid for any page */
207                 outb(val & 0xff, iobase + PCMMIO_PORT_REG(port + 0));
208                 outb((val >> 8) & 0xff, iobase + PCMMIO_PORT_REG(port + 1));
209                 outb((val >> 16) & 0xff, iobase + PCMMIO_PORT_REG(port + 2));
210         } else {
211                 outb(PCMMIO_PAGE(page), iobase + PCMMIO_PAGE_LOCK_REG);
212                 outb(val & 0xff, iobase + PCMMIO_PAGE_REG(0));
213                 outb((val >> 8) & 0xff, iobase + PCMMIO_PAGE_REG(1));
214                 outb((val >> 16) & 0xff, iobase + PCMMIO_PAGE_REG(2));
215         }
216         spin_unlock_irqrestore(&devpriv->pagelock, flags);
217 }
218
219 static unsigned int pcmmio_dio_read(struct comedi_device *dev,
220                                     int page, int port)
221 {
222         struct pcmmio_private *devpriv = dev->private;
223         unsigned long iobase = dev->iobase;
224         unsigned long flags;
225         unsigned int val;
226
227         spin_lock_irqsave(&devpriv->pagelock, flags);
228         if (page == 0) {
229                 /* Port registers are valid for any page */
230                 val = inb(iobase + PCMMIO_PORT_REG(port + 0));
231                 val |= (inb(iobase + PCMMIO_PORT_REG(port + 1)) << 8);
232                 val |= (inb(iobase + PCMMIO_PORT_REG(port + 2)) << 16);
233         } else {
234                 outb(PCMMIO_PAGE(page), iobase + PCMMIO_PAGE_LOCK_REG);
235                 val = inb(iobase + PCMMIO_PAGE_REG(0));
236                 val |= (inb(iobase + PCMMIO_PAGE_REG(1)) << 8);
237                 val |= (inb(iobase + PCMMIO_PAGE_REG(2)) << 16);
238         }
239         spin_unlock_irqrestore(&devpriv->pagelock, flags);
240
241         return val;
242 }
243
244 /*
245  * Each channel can be individually programmed for input or output.
246  * Writing a '0' to a channel causes the corresponding output pin
247  * to go to a high-z state (pulled high by an external 10K resistor).
248  * This allows it to be used as an input. When used in the input mode,
249  * a read reflects the inverted state of the I/O pin, such that a
250  * high on the pin will read as a '0' in the register. Writing a '1'
251  * to a bit position causes the pin to sink current (up to 12mA),
252  * effectively pulling it low.
253  */
254 static int pcmmio_dio_insn_bits(struct comedi_device *dev,
255                                 struct comedi_subdevice *s,
256                                 struct comedi_insn *insn,
257                                 unsigned int *data)
258 {
259         /* subdevice 2 uses ports 0-2, subdevice 3 uses ports 3-5 */
260         int port = s->index == 2 ? 0 : 3;
261         unsigned int chanmask = (1 << s->n_chan) - 1;
262         unsigned int mask;
263         unsigned int val;
264
265         mask = comedi_dio_update_state(s, data);
266         if (mask) {
267                 /*
268                  * Outputs are inverted, invert the state and
269                  * update the channels.
270                  *
271                  * The s->io_bits mask makes sure the input channels
272                  * are '0' so that the outputs pins stay in a high
273                  * z-state.
274                  */
275                 val = ~s->state & chanmask;
276                 val &= s->io_bits;
277                 pcmmio_dio_write(dev, val, 0, port);
278         }
279
280         /* get inverted state of the channels from the port */
281         val = pcmmio_dio_read(dev, 0, port);
282
283         /* return the true state of the channels */
284         data[1] = ~val & chanmask;
285
286         return insn->n;
287 }
288
289 static int pcmmio_dio_insn_config(struct comedi_device *dev,
290                                   struct comedi_subdevice *s,
291                                   struct comedi_insn *insn,
292                                   unsigned int *data)
293 {
294         /* subdevice 2 uses ports 0-2, subdevice 3 uses ports 3-5 */
295         int port = s->index == 2 ? 0 : 3;
296         int ret;
297
298         ret = comedi_dio_insn_config(dev, s, insn, data, 0);
299         if (ret)
300                 return ret;
301
302         if (data[0] == INSN_CONFIG_DIO_INPUT)
303                 pcmmio_dio_write(dev, s->io_bits, 0, port);
304
305         return insn->n;
306 }
307
308 static void pcmmio_reset(struct comedi_device *dev)
309 {
310         /* Clear all the DIO port bits */
311         pcmmio_dio_write(dev, 0, 0, 0);
312         pcmmio_dio_write(dev, 0, 0, 3);
313
314         /* Clear all the paged registers */
315         pcmmio_dio_write(dev, 0, PCMMIO_PAGE_POL, 0);
316         pcmmio_dio_write(dev, 0, PCMMIO_PAGE_ENAB, 0);
317         pcmmio_dio_write(dev, 0, PCMMIO_PAGE_INT_ID, 0);
318 }
319
320 /* devpriv->spinlock is already locked */
321 static void pcmmio_stop_intr(struct comedi_device *dev,
322                              struct comedi_subdevice *s)
323 {
324         struct pcmmio_private *devpriv = dev->private;
325
326         devpriv->enabled_mask = 0;
327         devpriv->active = 0;
328         s->async->inttrig = NULL;
329
330         /* disable all dio interrupts */
331         pcmmio_dio_write(dev, 0, PCMMIO_PAGE_ENAB, 0);
332 }
333
334 static void pcmmio_handle_dio_intr(struct comedi_device *dev,
335                                    struct comedi_subdevice *s,
336                                    unsigned int triggered)
337 {
338         struct pcmmio_private *devpriv = dev->private;
339         struct comedi_cmd *cmd = &s->async->cmd;
340         unsigned int oldevents = s->async->events;
341         unsigned int val = 0;
342         unsigned long flags;
343         int i;
344
345         spin_lock_irqsave(&devpriv->spinlock, flags);
346
347         if (!devpriv->active)
348                 goto done;
349
350         if (!(triggered & devpriv->enabled_mask))
351                 goto done;
352
353         for (i = 0; i < cmd->chanlist_len; i++) {
354                 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
355
356                 if (triggered & (1 << chan))
357                         val |= (1 << i);
358         }
359
360         /* Write the scan to the buffer. */
361         if (comedi_buf_put(s, val) &&
362             comedi_buf_put(s, val >> 16)) {
363                 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
364         } else {
365                 /* Overflow! Stop acquisition!! */
366                 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
367                 pcmmio_stop_intr(dev, s);
368         }
369
370         /* Check for end of acquisition. */
371         if (cmd->stop_src == TRIG_COUNT && devpriv->stop_count > 0) {
372                 devpriv->stop_count--;
373                 if (devpriv->stop_count == 0) {
374                         s->async->events |= COMEDI_CB_EOA;
375                         /* TODO: STOP_ACQUISITION_CALL_HERE!! */
376                         pcmmio_stop_intr(dev, s);
377                 }
378         }
379
380 done:
381         spin_unlock_irqrestore(&devpriv->spinlock, flags);
382
383         if (oldevents != s->async->events)
384                 comedi_event(dev, s);
385 }
386
387 static irqreturn_t interrupt_pcmmio(int irq, void *d)
388 {
389         struct comedi_device *dev = d;
390         struct comedi_subdevice *s = dev->read_subdev;
391         unsigned int triggered;
392         unsigned char int_pend;
393
394         /* are there any interrupts pending */
395         int_pend = inb(dev->iobase + PCMMIO_INT_PENDING_REG) & 0x07;
396         if (!int_pend)
397                 return IRQ_NONE;
398
399         /* get, and clear, the pending interrupts */
400         triggered = pcmmio_dio_read(dev, PCMMIO_PAGE_INT_ID, 0);
401         pcmmio_dio_write(dev, 0, PCMMIO_PAGE_INT_ID, 0);
402
403         pcmmio_handle_dio_intr(dev, s, triggered);
404
405         return IRQ_HANDLED;
406 }
407
408 /* devpriv->spinlock is already locked */
409 static void pcmmio_start_intr(struct comedi_device *dev,
410                               struct comedi_subdevice *s)
411 {
412         struct pcmmio_private *devpriv = dev->private;
413         struct comedi_cmd *cmd = &s->async->cmd;
414         unsigned int bits = 0;
415         unsigned int pol_bits = 0;
416         int i;
417
418         devpriv->enabled_mask = 0;
419         devpriv->active = 1;
420         if (cmd->chanlist) {
421                 for (i = 0; i < cmd->chanlist_len; i++) {
422                         unsigned int chanspec = cmd->chanlist[i];
423                         unsigned int chan = CR_CHAN(chanspec);
424                         unsigned int range = CR_RANGE(chanspec);
425                         unsigned int aref = CR_AREF(chanspec);
426
427                         bits |= (1 << chan);
428                         pol_bits |= (((aref || range) ? 1 : 0) << chan);
429                 }
430         }
431         bits &= ((1 << s->n_chan) - 1);
432         devpriv->enabled_mask = bits;
433
434         /* set polarity and enable interrupts */
435         pcmmio_dio_write(dev, pol_bits, PCMMIO_PAGE_POL, 0);
436         pcmmio_dio_write(dev, bits, PCMMIO_PAGE_ENAB, 0);
437 }
438
439 static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
440 {
441         struct pcmmio_private *devpriv = dev->private;
442         unsigned long flags;
443
444         spin_lock_irqsave(&devpriv->spinlock, flags);
445         if (devpriv->active)
446                 pcmmio_stop_intr(dev, s);
447         spin_unlock_irqrestore(&devpriv->spinlock, flags);
448
449         return 0;
450 }
451
452 static int pcmmio_inttrig_start_intr(struct comedi_device *dev,
453                                      struct comedi_subdevice *s,
454                                      unsigned int trig_num)
455 {
456         struct pcmmio_private *devpriv = dev->private;
457         struct comedi_cmd *cmd = &s->async->cmd;
458         unsigned long flags;
459
460         if (trig_num != cmd->start_arg)
461                 return -EINVAL;
462
463         spin_lock_irqsave(&devpriv->spinlock, flags);
464         s->async->inttrig = NULL;
465         if (devpriv->active)
466                 pcmmio_start_intr(dev, s);
467         spin_unlock_irqrestore(&devpriv->spinlock, flags);
468
469         return 1;
470 }
471
472 /*
473  * 'do_cmd' function for an 'INTERRUPT' subdevice.
474  */
475 static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
476 {
477         struct pcmmio_private *devpriv = dev->private;
478         struct comedi_cmd *cmd = &s->async->cmd;
479         unsigned long flags;
480
481         spin_lock_irqsave(&devpriv->spinlock, flags);
482         devpriv->active = 1;
483
484         devpriv->stop_count = cmd->stop_arg;
485
486         /* Set up start of acquisition. */
487         if (cmd->start_src == TRIG_INT)
488                 s->async->inttrig = pcmmio_inttrig_start_intr;
489         else    /* TRIG_NOW */
490                 pcmmio_start_intr(dev, s);
491
492         spin_unlock_irqrestore(&devpriv->spinlock, flags);
493
494         return 0;
495 }
496
497 static int pcmmio_cmdtest(struct comedi_device *dev,
498                           struct comedi_subdevice *s,
499                           struct comedi_cmd *cmd)
500 {
501         int err = 0;
502
503         /* Step 1 : check if triggers are trivially valid */
504
505         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
506         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
507         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
508         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
509         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
510
511         if (err)
512                 return 1;
513
514         /* Step 2a : make sure trigger sources are unique */
515
516         err |= cfc_check_trigger_is_unique(cmd->start_src);
517         err |= cfc_check_trigger_is_unique(cmd->stop_src);
518
519         /* Step 2b : and mutually compatible */
520
521         if (err)
522                 return 2;
523
524         /* Step 3: check if arguments are trivially valid */
525
526         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
527         err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
528         err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
529         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
530
531         if (cmd->stop_src == TRIG_COUNT)
532                 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
533         else    /* TRIG_NONE */
534                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
535
536         if (err)
537                 return 3;
538
539         /* step 4: fix up any arguments */
540
541         /* if (err) return 4; */
542
543         return 0;
544 }
545
546 static int pcmmio_ai_eoc(struct comedi_device *dev,
547                          struct comedi_subdevice *s,
548                          struct comedi_insn *insn,
549                          unsigned long context)
550 {
551         unsigned char status;
552
553         status = inb(dev->iobase + PCMMIO_AI_STATUS_REG);
554         if (status & PCMMIO_AI_STATUS_DATA_READY)
555                 return 0;
556         return -EBUSY;
557 }
558
559 static int pcmmio_ai_insn_read(struct comedi_device *dev,
560                                struct comedi_subdevice *s,
561                                struct comedi_insn *insn,
562                                unsigned int *data)
563 {
564         unsigned long iobase = dev->iobase;
565         unsigned int chan = CR_CHAN(insn->chanspec);
566         unsigned int range = CR_RANGE(insn->chanspec);
567         unsigned int aref = CR_AREF(insn->chanspec);
568         unsigned char cmd = 0;
569         unsigned int val;
570         int ret;
571         int i;
572
573         /*
574          * The PCM-MIO uses two Linear Tech LTC1859CG 8-channel A/D converters.
575          * The devices use a full duplex serial interface which transmits and
576          * receives data simultaneously. An 8-bit command is shifted into the
577          * ADC interface to configure it for the next conversion. At the same
578          * time, the data from the previous conversion is shifted out of the
579          * device. Consequently, the conversion result is delayed by one
580          * conversion from the command word.
581          *
582          * Setup the cmd for the conversions then do a dummy conversion to
583          * flush the junk data. Then do each conversion requested by the
584          * comedi_insn. Note that the last conversion will leave junk data
585          * in ADC which will get flushed on the next comedi_insn.
586          */
587
588         if (chan > 7) {
589                 chan -= 8;
590                 iobase += PCMMIO_AI_2ND_ADC_OFFSET;
591         }
592
593         if (aref == AREF_GROUND)
594                 cmd |= PCMMIO_AI_CMD_SE;
595         if (chan % 2)
596                 cmd |= PCMMIO_AI_CMD_ODD_CHAN;
597         cmd |= PCMMIO_AI_CMD_CHAN_SEL(chan / 2);
598         cmd |= PCMMIO_AI_CMD_RANGE(range);
599
600         outb(cmd, iobase + PCMMIO_AI_CMD_REG);
601
602         ret = comedi_timeout(dev, s, insn, pcmmio_ai_eoc, 0);
603         if (ret)
604                 return ret;
605
606         val = inb(iobase + PCMMIO_AI_LSB_REG);
607         val |= inb(iobase + PCMMIO_AI_MSB_REG) << 8;
608
609         for (i = 0; i < insn->n; i++) {
610                 outb(cmd, iobase + PCMMIO_AI_CMD_REG);
611
612                 ret = comedi_timeout(dev, s, insn, pcmmio_ai_eoc, 0);
613                 if (ret)
614                         return ret;
615
616                 val = inb(iobase + PCMMIO_AI_LSB_REG);
617                 val |= inb(iobase + PCMMIO_AI_MSB_REG) << 8;
618
619                 /* bipolar data is two's complement */
620                 if (comedi_range_is_bipolar(s, range))
621                         val = comedi_offset_munge(s, val);
622
623                 data[i] = val;
624         }
625
626         return insn->n;
627 }
628
629 static int pcmmio_ao_eoc(struct comedi_device *dev,
630                          struct comedi_subdevice *s,
631                          struct comedi_insn *insn,
632                          unsigned long context)
633 {
634         unsigned char status;
635
636         status = inb(dev->iobase + PCMMIO_AO_STATUS_REG);
637         if (status & PCMMIO_AO_STATUS_DATA_READY)
638                 return 0;
639         return -EBUSY;
640 }
641
642 static int pcmmio_ao_insn_write(struct comedi_device *dev,
643                                 struct comedi_subdevice *s,
644                                 struct comedi_insn *insn,
645                                 unsigned int *data)
646 {
647         unsigned long iobase = dev->iobase;
648         unsigned int chan = CR_CHAN(insn->chanspec);
649         unsigned int range = CR_RANGE(insn->chanspec);
650         unsigned char cmd = 0;
651         int ret;
652         int i;
653
654         /*
655          * The PCM-MIO has two Linear Tech LTC2704 DAC devices. Each device
656          * is a 4-channel converter with software-selectable output range.
657          */
658
659         if (chan > 3) {
660                 cmd |= PCMMIO_AO_CMD_CHAN_SEL(chan - 4);
661                 iobase += PCMMIO_AO_2ND_DAC_OFFSET;
662         } else {
663                 cmd |= PCMMIO_AO_CMD_CHAN_SEL(chan);
664         }
665
666         /* set the range for the channel */
667         outb(PCMMIO_AO_LSB_SPAN(range), iobase + PCMMIO_AO_LSB_REG);
668         outb(0, iobase + PCMMIO_AO_MSB_REG);
669         outb(cmd | PCMMIO_AO_CMD_WR_SPAN_UPDATE, iobase + PCMMIO_AO_CMD_REG);
670
671         ret = comedi_timeout(dev, s, insn, pcmmio_ao_eoc, 0);
672         if (ret)
673                 return ret;
674
675         for (i = 0; i < insn->n; i++) {
676                 unsigned int val = data[i];
677
678                 /* write the data to the channel */
679                 outb(val & 0xff, iobase + PCMMIO_AO_LSB_REG);
680                 outb((val >> 8) & 0xff, iobase + PCMMIO_AO_MSB_REG);
681                 outb(cmd | PCMMIO_AO_CMD_WR_CODE_UPDATE,
682                      iobase + PCMMIO_AO_CMD_REG);
683
684                 ret = comedi_timeout(dev, s, insn, pcmmio_ao_eoc, 0);
685                 if (ret)
686                         return ret;
687
688                 s->readback[chan] = val;
689         }
690
691         return insn->n;
692 }
693
694 static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
695 {
696         struct pcmmio_private *devpriv;
697         struct comedi_subdevice *s;
698         int ret;
699
700         ret = comedi_request_region(dev, it->options[0], 32);
701         if (ret)
702                 return ret;
703
704         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
705         if (!devpriv)
706                 return -ENOMEM;
707
708         spin_lock_init(&devpriv->pagelock);
709         spin_lock_init(&devpriv->spinlock);
710
711         pcmmio_reset(dev);
712
713         if (it->options[1]) {
714                 ret = request_irq(it->options[1], interrupt_pcmmio, 0,
715                                   dev->board_name, dev);
716                 if (ret == 0) {
717                         dev->irq = it->options[1];
718
719                         /* configure the interrupt routing on the board */
720                         outb(PCMMIO_AI_RES_ENA_DIO_RES_ACCESS,
721                              dev->iobase + PCMMIO_AI_RES_ENA_REG);
722                         outb(PCMMIO_RESOURCE_IRQ(dev->irq),
723                              dev->iobase + PCMMIO_RESOURCE_REG);
724                 }
725         }
726
727         ret = comedi_alloc_subdevices(dev, 4);
728         if (ret)
729                 return ret;
730
731         /* Analog Input subdevice */
732         s = &dev->subdevices[0];
733         s->type         = COMEDI_SUBD_AI;
734         s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
735         s->n_chan       = 16;
736         s->maxdata      = 0xffff;
737         s->range_table  = &pcmmio_ai_ranges;
738         s->insn_read    = pcmmio_ai_insn_read;
739
740         /* initialize the resource enable register by clearing it */
741         outb(PCMMIO_AI_RES_ENA_CMD_REG_ACCESS,
742              dev->iobase + PCMMIO_AI_RES_ENA_REG);
743         outb(PCMMIO_AI_RES_ENA_CMD_REG_ACCESS,
744              dev->iobase + PCMMIO_AI_RES_ENA_REG + PCMMIO_AI_2ND_ADC_OFFSET);
745
746         /* Analog Output subdevice */
747         s = &dev->subdevices[1];
748         s->type         = COMEDI_SUBD_AO;
749         s->subdev_flags = SDF_READABLE;
750         s->n_chan       = 8;
751         s->maxdata      = 0xffff;
752         s->range_table  = &pcmmio_ao_ranges;
753         s->insn_write   = pcmmio_ao_insn_write;
754         s->insn_read    = comedi_readback_insn_read;
755
756         ret = comedi_alloc_subdev_readback(s);
757         if (ret)
758                 return ret;
759
760         /* initialize the resource enable register by clearing it */
761         outb(0, dev->iobase + PCMMIO_AO_RESOURCE_ENA_REG);
762         outb(0, dev->iobase + PCMMIO_AO_2ND_DAC_OFFSET +
763                 PCMMIO_AO_RESOURCE_ENA_REG);
764
765         /* Digital I/O subdevice with interrupt support */
766         s = &dev->subdevices[2];
767         s->type         = COMEDI_SUBD_DIO;
768         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
769         s->n_chan       = 24;
770         s->maxdata      = 1;
771         s->len_chanlist = 1;
772         s->range_table  = &range_digital;
773         s->insn_bits    = pcmmio_dio_insn_bits;
774         s->insn_config  = pcmmio_dio_insn_config;
775         if (dev->irq) {
776                 dev->read_subdev = s;
777                 s->subdev_flags |= SDF_CMD_READ;
778                 s->len_chanlist = s->n_chan;
779                 s->cancel       = pcmmio_cancel;
780                 s->do_cmd       = pcmmio_cmd;
781                 s->do_cmdtest   = pcmmio_cmdtest;
782         }
783
784         /* Digital I/O subdevice */
785         s = &dev->subdevices[3];
786         s->type         = COMEDI_SUBD_DIO;
787         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
788         s->n_chan       = 24;
789         s->maxdata      = 1;
790         s->range_table  = &range_digital;
791         s->insn_bits    = pcmmio_dio_insn_bits;
792         s->insn_config  = pcmmio_dio_insn_config;
793
794         return 0;
795 }
796
797 static struct comedi_driver pcmmio_driver = {
798         .driver_name    = "pcmmio",
799         .module         = THIS_MODULE,
800         .attach         = pcmmio_attach,
801         .detach         = comedi_legacy_detach,
802 };
803 module_comedi_driver(pcmmio_driver);
804
805 MODULE_AUTHOR("Comedi http://www.comedi.org");
806 MODULE_DESCRIPTION("Comedi driver for Winsystems PCM-MIO PC/104 board");
807 MODULE_LICENSE("GPL");