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