Staging: speakup: fix an improperly-declared variable.
[deliverable/linux.git] / drivers / staging / comedi / drivers / pcmuio.c
CommitLineData
6baef150
CC
1/*
2 comedi/drivers/pcmuio.c
3 Driver for Winsystems PC-104 based 48-channel and 96-channel DIO boards.
4
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 2006 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 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21*/
22/*
23Driver: pcmuio
24Description: A driver for the PCM-UIO48A and PCM-UIO96A boards from Winsystems.
25Devices: [Winsystems] PCM-UIO48A (pcmuio48), PCM-UIO96A (pcmuio96)
26Author: Calin Culianu <calin@ajvar.org>
27Updated: Fri, 13 Jan 2006 12:01:01 -0500
28Status: works
29
30A driver for the relatively straightforward-to-program PCM-UIO48A and
31PCM-UIO96A boards from Winsystems. These boards use either one or two
32(in the 96-DIO version) WS16C48 ASIC HighDensity I/O Chips (HDIO).
33This chip is interesting in that each I/O line is individually
34programmable for INPUT or OUTPUT (thus comedi_dio_config can be done
35on a per-channel basis). Also, each chip supports edge-triggered
36interrupts for the first 24 I/O lines. Of course, since the
3796-channel version of the board has two ASICs, it can detect polarity
38changes on up to 48 I/O lines. Since this is essentially an (non-PnP)
39ISA board, I/O Address and IRQ selection are done through jumpers on
40the board. You need to pass that information to this driver as the
41first and second comedi_config option, respectively. Note that the
4248-channel version uses 16 bytes of IO memory and the 96-channel
43version uses 32-bytes (in case you are worried about conflicts). The
4448-channel board is split into two 24-channel comedi subdevices.
45The 96-channel board is split into 4 24-channel DIO subdevices.
46
47Note that IRQ support has been added, but it is untested.
48
49To use edge-detection IRQ support, pass the IRQs of both ASICS
50(for the 96 channel version) or just 1 ASIC (for 48-channel version).
51Then, use use comedi_commands with TRIG_NOW.
52Your callback will be called each time an edge is triggered, and the data
53values will be two sample_t's, which should be concatenated to form one
5432-bit unsigned int. This value is the mask of channels that had
55edges detected from your channel list. Note that the bits positions
56in the mask correspond to positions in your chanlist when you specified
57the command and *not* channel id's!
58
59To set the polarity of the edge-detection interrupts pass a nonzero value for
60either CR_RANGE or CR_AREF for edge-up polarity, or a zero value for both
61CR_RANGE and CR_AREF if you want edge-down polarity.
62
63In the 48-channel version:
64
65On subdev 0, the first 24 channels channels are edge-detect channels.
66
67In the 96-channel board you have the collowing channels that can do edge detection:
68
69subdev 0, channels 0-24 (first 24 channels of 1st ASIC)
70subdev 2, channels 0-24 (first 24 channels of 2nd ASIC)
71
72Configuration Options:
73 [0] - I/O port base address
74 [1] - IRQ (for first ASIC, or first 24 channels)
75 [2] - IRQ for second ASIC (pcmuio96 only - IRQ for chans 48-72 .. can be the same as first irq!)
76*/
77
25436dc9 78#include <linux/interrupt.h>
5a0e3ad6 79#include <linux/slab.h>
6baef150 80#include "../comedidev.h"
0b8f754a 81#include "pcm_common.h"
6baef150
CC
82
83#include <linux/pci.h> /* for PCI devices */
84
6baef150
CC
85#define CHANS_PER_PORT 8
86#define PORTS_PER_ASIC 6
87#define INTR_PORTS_PER_ASIC 3
88#define MAX_CHANS_PER_SUBDEV 24 /* number of channels per comedi subdevice */
89#define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT)
90#define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC)
91#define INTR_CHANS_PER_ASIC 24
92#define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT)
93#define MAX_DIO_CHANS (PORTS_PER_ASIC*2*CHANS_PER_PORT)
94#define MAX_ASICS (MAX_DIO_CHANS/CHANS_PER_ASIC)
95#define SDEV_NO ((int)(s - dev->subdevices))
96#define CALC_N_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) /*+ (nchans > INTR_CHANS_PER_ASIC ? 2 : 1)*/)
97/* IO Memory sizes */
98#define ASIC_IOSIZE (0x10)
99#define PCMUIO48_IOSIZE ASIC_IOSIZE
100#define PCMUIO96_IOSIZE (ASIC_IOSIZE*2)
101
102/* Some offsets - these are all in the 16byte IO memory offset from
103 the base address. Note that there is a paging scheme to swap out
104 offsets 0x8-0xA using the PAGELOCK register. See the table below.
105
106 Register(s) Pages R/W? Description
107 --------------------------------------------------------------
108 REG_PORTx All R/W Read/Write/Configure IO
109 REG_INT_PENDING All ReadOnly Quickly see which INT_IDx has int.
110 REG_PAGELOCK All WriteOnly Select a page
111 REG_POLx Pg. 1 only WriteOnly Select edge-detection polarity
112 REG_ENABx Pg. 2 only WriteOnly Enable/Disable edge-detect. int.
113 REG_INT_IDx Pg. 3 only R/W See which ports/bits have ints.
114 */
115#define REG_PORT0 0x0
116#define REG_PORT1 0x1
117#define REG_PORT2 0x2
118#define REG_PORT3 0x3
119#define REG_PORT4 0x4
120#define REG_PORT5 0x5
121#define REG_INT_PENDING 0x6
122#define REG_PAGELOCK 0x7 /* page selector register, upper 2 bits select a page
123 and bits 0-5 are used to 'lock down' a particular
124 port above to make it readonly. */
125#define REG_POL0 0x8
126#define REG_POL1 0x9
127#define REG_POL2 0xA
128#define REG_ENAB0 0x8
129#define REG_ENAB1 0x9
130#define REG_ENAB2 0xA
131#define REG_INT_ID0 0x8
132#define REG_INT_ID1 0x9
133#define REG_INT_ID2 0xA
134
135#define NUM_PAGED_REGS 3
136#define NUM_PAGES 4
137#define FIRST_PAGED_REG 0x8
138#define REG_PAGE_BITOFFSET 6
139#define REG_LOCK_BITOFFSET 0
140#define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1))
141#define REG_LOCK_MASK ~(REG_PAGE_MASK)
142#define PAGE_POL 1
143#define PAGE_ENAB 2
144#define PAGE_INT_ID 3
145
146/*
147 * Board descriptions for two imaginary boards. Describing the
148 * boards in this way is optional, and completely driver-dependent.
149 * Some drivers use arrays such as this, other do not.
150 */
70a6001a 151struct pcmuio_board {
6baef150
CC
152 const char *name;
153 const int num_asics;
154 const int num_channels_per_port;
155 const int num_ports;
70a6001a 156};
6baef150 157
6baef150 158/* this structure is for data unique to this subdevice. */
e15849e5 159struct pcmuio_subdev_private {
6baef150
CC
160 /* mapping of halfwords (bytes) in port/chanarray to iobase */
161 unsigned long iobases[PORTS_PER_SUBDEV];
162
163 /* The below is only used for intr subdevices */
164 struct {
165 int asic; /* if non-negative, this subdev has an interrupt asic */
166 int first_chan; /* if nonnegative, the first channel id for
167 interrupts. */
168 int num_asic_chans; /* the number of asic channels in this subdev
169 that have interrutps */
170 int asic_chan; /* if nonnegative, the first channel id with
171 respect to the asic that has interrupts */
172 int enabled_mask; /* subdev-relative channel mask for channels
173 we are interested in */
174 int active;
175 int stop_count;
176 int continuous;
177 spinlock_t spinlock;
178 } intr;
e15849e5 179};
6baef150
CC
180
181/* this structure is for data unique to this hardware driver. If
182 several hardware drivers keep similar information in this structure,
71b5f4f1 183 feel free to suggest moving the variable to the struct comedi_device struct. */
055f6636 184struct pcmuio_private {
6baef150
CC
185 struct {
186 unsigned char pagelock; /* current page and lock */
187 unsigned char pol[NUM_PAGED_REGS]; /* shadow of POLx registers */
188 unsigned char enab[NUM_PAGED_REGS]; /* shadow of ENABx registers */
189 int num;
190 unsigned long iobase;
191 unsigned int irq;
192 spinlock_t spinlock;
193 } asics[MAX_ASICS];
e15849e5 194 struct pcmuio_subdev_private *sprivs;
055f6636 195};
6baef150
CC
196
197/*
198 * most drivers define the following macro to make it easy to
199 * access the private structure.
200 */
055f6636 201#define devpriv ((struct pcmuio_private *)dev->private)
e15849e5 202#define subpriv ((struct pcmuio_subdev_private *)s->private)
6baef150
CC
203
204/* DIO devices are slightly special. Although it is possible to
205 * implement the insn_read/insn_write interface, it is much more
206 * useful to applications if you implement the insn_bits interface.
207 * This allows packed reading/writing of the DIO channels. The
208 * comedi core can convert between insn_bits and insn_read/write */
0a85b6f0
MT
209static int pcmuio_dio_insn_bits(struct comedi_device *dev,
210 struct comedi_subdevice *s,
211 struct comedi_insn *insn, unsigned int *data)
6baef150
CC
212{
213 int byte_no;
214 if (insn->n != 2)
215 return -EINVAL;
216
217 /* NOTE:
218 reading a 0 means this channel was high
219 writine a 0 sets the channel high
220 reading a 1 means this channel was low
221 writing a 1 means set this channel low
222
223 Therefore everything is always inverted. */
224
225 /* The insn data is a mask in data[0] and the new data
226 * in data[1], each channel cooresponding to a bit. */
227
228#ifdef DAMMIT_ITS_BROKEN
229 /* DEBUG */
976fe5ab
RM
230 dev_dbg(dev->hw_dev, "write mask: %08x data: %08x\n", data[0],
231 data[1]);
6baef150
CC
232#endif
233
234 s->state = 0;
235
236 for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
237 /* address of 8-bit port */
238 unsigned long ioaddr = subpriv->iobases[byte_no],
0a85b6f0
MT
239 /* bit offset of port in 32-bit doubleword */
240 offset = byte_no * 8;
6baef150
CC
241 /* this 8-bit port's data */
242 unsigned char byte = 0,
0a85b6f0
MT
243 /* The write mask for this port (if any) */
244 write_mask_byte = (data[0] >> offset) & 0xff,
245 /* The data byte for this port */
246 data_byte = (data[1] >> offset) & 0xff;
6baef150
CC
247
248 byte = inb(ioaddr); /* read all 8-bits for this port */
249
250#ifdef DAMMIT_ITS_BROKEN
251 /* DEBUG */
0a85b6f0
MT
252 printk
253 ("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ",
254 byte_no, (unsigned)write_mask_byte, (unsigned)data_byte,
255 offset, ioaddr, (unsigned)byte);
6baef150
CC
256#endif
257
258 if (write_mask_byte) {
259 /* this byte has some write_bits -- so set the output lines */
260 byte &= ~write_mask_byte; /* clear bits for write mask */
261 byte |= ~data_byte & write_mask_byte; /* set to inverted data_byte */
262 /* Write out the new digital output state */
263 outb(byte, ioaddr);
264 }
265#ifdef DAMMIT_ITS_BROKEN
266 /* DEBUG */
976fe5ab 267 dev_dbg(dev->hw_dev, "data_out_byte %02x\n", (unsigned)byte);
6baef150
CC
268#endif
269 /* save the digital input lines for this byte.. */
270 s->state |= ((unsigned int)byte) << offset;
271 }
272
273 /* now return the DIO lines to data[1] - note they came inverted! */
274 data[1] = ~s->state;
275
276#ifdef DAMMIT_ITS_BROKEN
277 /* DEBUG */
976fe5ab
RM
278 dev_dbg(dev->hw_dev, "s->state %08x data_out %08x\n", s->state,
279 data[1]);
6baef150
CC
280#endif
281
282 return 2;
283}
284
285/* The input or output configuration of each digital line is
286 * configured by a special insn_config instruction. chanspec
287 * contains the channel to be changed, and data[0] contains the
288 * value COMEDI_INPUT or COMEDI_OUTPUT. */
0a85b6f0
MT
289static int pcmuio_dio_insn_config(struct comedi_device *dev,
290 struct comedi_subdevice *s,
291 struct comedi_insn *insn, unsigned int *data)
6baef150
CC
292{
293 int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
0a85b6f0 294 chan % 8;
6baef150
CC
295 unsigned long ioaddr;
296 unsigned char byte;
297
298 /* Compute ioaddr for this channel */
299 ioaddr = subpriv->iobases[byte_no];
300
301 /* NOTE:
302 writing a 0 an IO channel's bit sets the channel to INPUT
303 and pulls the line high as well
304
305 writing a 1 to an IO channel's bit pulls the line low
306
307 All channels are implicitly always in OUTPUT mode -- but when
308 they are high they can be considered to be in INPUT mode..
309
310 Thus, we only force channels low if the config request was INPUT,
311 otherwise we do nothing to the hardware. */
312
313 switch (data[0]) {
314 case INSN_CONFIG_DIO_OUTPUT:
315 /* save to io_bits -- don't actually do anything since
316 all input channels are also output channels... */
317 s->io_bits |= 1 << chan;
318 break;
319 case INSN_CONFIG_DIO_INPUT:
320 /* write a 0 to the actual register representing the channel
321 to set it to 'input'. 0 means "float high". */
322 byte = inb(ioaddr);
323 byte &= ~(1 << bit_no);
324 /**< set input channel to '0' */
325
326 /* write out byte -- this is the only time we actually affect the
327 hardware as all channels are implicitly output -- but input
328 channels are set to float-high */
329 outb(byte, ioaddr);
330
331 /* save to io_bits */
332 s->io_bits &= ~(1 << chan);
333 break;
334
335 case INSN_CONFIG_DIO_QUERY:
25985edc 336 /* retrieve from shadow register */
6baef150 337 data[1] =
0a85b6f0 338 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
6baef150
CC
339 return insn->n;
340 break;
341
342 default:
343 return -EINVAL;
344 break;
345 }
346
347 return insn->n;
348}
349
6b19f9c6
HS
350static void switch_page(struct comedi_device *dev, int asic, int page)
351{
a28b5995
HS
352 const struct pcmuio_board *board = comedi_board(dev);
353
354 if (asic < 0 || asic >= board->num_asics)
6b19f9c6
HS
355 return; /* paranoia */
356 if (page < 0 || page >= NUM_PAGES)
357 return; /* more paranoia */
358
359 devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
360 devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
361
362 /* now write out the shadow register */
363 outb(devpriv->asics[asic].pagelock,
364 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
365}
366
da91b269 367static void init_asics(struct comedi_device *dev)
6baef150
CC
368{ /* sets up an
369 ASIC chip to defaults */
a28b5995 370 const struct pcmuio_board *board = comedi_board(dev);
6baef150
CC
371 int asic;
372
a28b5995 373 for (asic = 0; asic < board->num_asics; ++asic) {
6baef150
CC
374 int port, page;
375 unsigned long baseaddr = dev->iobase + asic * ASIC_IOSIZE;
376
377 switch_page(dev, asic, 0); /* switch back to page 0 */
378
379 /* first, clear all the DIO port bits */
380 for (port = 0; port < PORTS_PER_ASIC; ++port)
381 outb(0, baseaddr + REG_PORT0 + port);
382
383 /* Next, clear all the paged registers for each page */
384 for (page = 1; page < NUM_PAGES; ++page) {
385 int reg;
386 /* now clear all the paged registers */
387 switch_page(dev, asic, page);
388 for (reg = FIRST_PAGED_REG;
0a85b6f0 389 reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
6baef150
CC
390 outb(0, baseaddr + reg);
391 }
392
393 /* DEBUG set rising edge interrupts on port0 of both asics */
394 /*switch_page(dev, asic, PAGE_POL);
395 outb(0xff, baseaddr + REG_POL0);
396 switch_page(dev, asic, PAGE_ENAB);
397 outb(0xff, baseaddr + REG_ENAB0); */
398 /* END DEBUG */
399
400 switch_page(dev, asic, 0); /* switch back to default page 0 */
401
402 }
403}
404
6baef150 405#ifdef notused
da91b269 406static void lock_port(struct comedi_device *dev, int asic, int port)
6baef150 407{
a28b5995
HS
408 const struct pcmuio_board *board = comedi_board(dev);
409
410 if (asic < 0 || asic >= board->num_asics)
6baef150
CC
411 return; /* paranoia */
412 if (port < 0 || port >= PORTS_PER_ASIC)
413 return; /* more paranoia */
414
415 devpriv->asics[asic].pagelock |= 0x1 << port;
416 /* now write out the shadow register */
417 outb(devpriv->asics[asic].pagelock,
0a85b6f0 418 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
6baef150
CC
419}
420
da91b269 421static void unlock_port(struct comedi_device *dev, int asic, int port)
6baef150 422{
a28b5995
HS
423 const struct pcmuio_board *board = comedi_board(dev);
424
425 if (asic < 0 || asic >= board->num_asics)
6baef150
CC
426 return; /* paranoia */
427 if (port < 0 || port >= PORTS_PER_ASIC)
428 return; /* more paranoia */
429 devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
430 /* now write out the shadow register */
431 outb(devpriv->asics[asic].pagelock,
0a85b6f0 432 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
6baef150
CC
433}
434#endif /* notused */
435
6b19f9c6
HS
436static void pcmuio_stop_intr(struct comedi_device *dev,
437 struct comedi_subdevice *s)
438{
439 int nports, firstport, asic, port;
440
441 asic = subpriv->intr.asic;
442 if (asic < 0)
443 return; /* not an interrupt subdev */
444
445 subpriv->intr.enabled_mask = 0;
446 subpriv->intr.active = 0;
447 s->async->inttrig = 0;
448 nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
449 firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
450 switch_page(dev, asic, PAGE_ENAB);
451 for (port = firstport; port < firstport + nports; ++port) {
452 /* disable all intrs for this subdev.. */
453 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
454 }
455}
456
70265d24 457static irqreturn_t interrupt_pcmuio(int irq, void *d)
6baef150
CC
458{
459 int asic, got1 = 0;
0a85b6f0 460 struct comedi_device *dev = (struct comedi_device *)d;
6baef150
CC
461
462 for (asic = 0; asic < MAX_ASICS; ++asic) {
463 if (irq == devpriv->asics[asic].irq) {
464 unsigned long flags;
465 unsigned triggered = 0;
466 unsigned long iobase = devpriv->asics[asic].iobase;
467 /* it is an interrupt for ASIC #asic */
468 unsigned char int_pend;
469
0a85b6f0
MT
470 spin_lock_irqsave(&devpriv->asics[asic].spinlock,
471 flags);
6baef150
CC
472
473 int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
474
475 if (int_pend) {
476 int port;
477 for (port = 0; port < INTR_PORTS_PER_ASIC;
0a85b6f0 478 ++port) {
6baef150
CC
479 if (int_pend & (0x1 << port)) {
480 unsigned char
0a85b6f0 481 io_lines_with_edges = 0;
6baef150 482 switch_page(dev, asic,
0a85b6f0 483 PAGE_INT_ID);
6baef150 484 io_lines_with_edges =
0a85b6f0 485 inb(iobase +
6baef150
CC
486 REG_INT_ID0 + port);
487
488 if (io_lines_with_edges)
489 /* clear pending interrupt */
490 outb(0, iobase +
0a85b6f0
MT
491 REG_INT_ID0 +
492 port);
6baef150
CC
493
494 triggered |=
0a85b6f0
MT
495 io_lines_with_edges <<
496 port * 8;
6baef150
CC
497 }
498 }
499
500 ++got1;
501 }
502
0a85b6f0
MT
503 spin_unlock_irqrestore(&devpriv->asics[asic].spinlock,
504 flags);
6baef150
CC
505
506 if (triggered) {
34c43922 507 struct comedi_subdevice *s;
6baef150 508 /* TODO here: dispatch io lines to subdevs with commands.. */
0a85b6f0
MT
509 printk
510 ("PCMUIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n",
511 irq, asic, triggered);
6baef150 512 for (s = dev->subdevices;
0a85b6f0
MT
513 s < dev->subdevices + dev->n_subdevices;
514 ++s) {
6baef150
CC
515 if (subpriv->intr.asic == asic) { /* this is an interrupt subdev, and it matches this asic! */
516 unsigned long flags;
517 unsigned oldevents;
518
0a85b6f0
MT
519 spin_lock_irqsave(&subpriv->
520 intr.spinlock,
521 flags);
6baef150
CC
522
523 oldevents = s->async->events;
524
525 if (subpriv->intr.active) {
526 unsigned mytrig =
0a85b6f0
MT
527 ((triggered >>
528 subpriv->intr.asic_chan)
529 &
530 ((0x1 << subpriv->
531 intr.
532 num_asic_chans) -
533 1)) << subpriv->
534 intr.first_chan;
535 if (mytrig &
536 subpriv->intr.enabled_mask)
537 {
538 unsigned int val
539 = 0;
6baef150 540 unsigned int n,
0a85b6f0 541 ch, len;
6baef150 542
0a85b6f0
MT
543 len =
544 s->
545 async->cmd.chanlist_len;
6baef150 546 for (n = 0;
0a85b6f0
MT
547 n < len;
548 n++) {
6baef150
CC
549 ch = CR_CHAN(s->async->cmd.chanlist[n]);
550 if (mytrig & (1U << ch)) {
551 val |= (1U << n);
552 }
553 }
554 /* Write the scan to the buffer. */
0a85b6f0
MT
555 if (comedi_buf_put(s->async, ((short *)&val)[0])
556 &&
557 comedi_buf_put
558 (s->async,
559 ((short *)
560 &val)[1]))
561 {
6baef150
CC
562 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
563 } else {
564 /* Overflow! Stop acquisition!! */
565 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
566 pcmuio_stop_intr
0a85b6f0
MT
567 (dev,
568 s);
6baef150
CC
569 }
570
571 /* Check for end of acquisition. */
0a85b6f0 572 if (!subpriv->intr.continuous) {
6baef150
CC
573 /* stop_src == TRIG_COUNT */
574 if (subpriv->intr.stop_count > 0) {
0a85b6f0 575 subpriv->intr.stop_count--;
6baef150
CC
576 if (subpriv->intr.stop_count == 0) {
577 s->async->events |= COMEDI_CB_EOA;
578 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
579 pcmuio_stop_intr
0a85b6f0
MT
580 (dev,
581 s);
6baef150
CC
582 }
583 }
584 }
585 }
586 }
587
0a85b6f0
MT
588 spin_unlock_irqrestore
589 (&subpriv->intr.spinlock,
590 flags);
6baef150
CC
591
592 if (oldevents !=
0a85b6f0 593 s->async->events) {
6baef150
CC
594 comedi_event(dev, s);
595 }
596
597 }
598
599 }
600 }
601
602 }
603 }
604 if (!got1)
605 return IRQ_NONE; /* interrupt from other source */
606 return IRQ_HANDLED;
607}
608
0a85b6f0
MT
609static int pcmuio_start_intr(struct comedi_device *dev,
610 struct comedi_subdevice *s)
6baef150
CC
611{
612 if (!subpriv->intr.continuous && subpriv->intr.stop_count == 0) {
613 /* An empty acquisition! */
614 s->async->events |= COMEDI_CB_EOA;
615 subpriv->intr.active = 0;
616 return 1;
617 } else {
618 unsigned bits = 0, pol_bits = 0, n;
619 int nports, firstport, asic, port;
ea6d0d4c 620 struct comedi_cmd *cmd = &s->async->cmd;
6baef150 621
c3744138
BP
622 asic = subpriv->intr.asic;
623 if (asic < 0)
6baef150
CC
624 return 1; /* not an interrupt
625 subdev */
626 subpriv->intr.enabled_mask = 0;
627 subpriv->intr.active = 1;
628 nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
629 firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
630 if (cmd->chanlist) {
631 for (n = 0; n < cmd->chanlist_len; n++) {
632 bits |= (1U << CR_CHAN(cmd->chanlist[n]));
633 pol_bits |= (CR_AREF(cmd->chanlist[n])
0a85b6f0
MT
634 || CR_RANGE(cmd->
635 chanlist[n]) ? 1U : 0U)
636 << CR_CHAN(cmd->chanlist[n]);
6baef150
CC
637 }
638 }
639 bits &= ((0x1 << subpriv->intr.num_asic_chans) -
0a85b6f0 640 1) << subpriv->intr.first_chan;
6baef150
CC
641 subpriv->intr.enabled_mask = bits;
642
643 switch_page(dev, asic, PAGE_ENAB);
644 for (port = firstport; port < firstport + nports; ++port) {
645 unsigned enab =
0a85b6f0
MT
646 bits >> (subpriv->intr.first_chan + (port -
647 firstport) *
648 8) & 0xff, pol =
649 pol_bits >> (subpriv->intr.first_chan +
650 (port - firstport) * 8) & 0xff;
6baef150
CC
651 /* set enab intrs for this subdev.. */
652 outb(enab,
0a85b6f0 653 devpriv->asics[asic].iobase + REG_ENAB0 + port);
6baef150
CC
654 switch_page(dev, asic, PAGE_POL);
655 outb(pol,
0a85b6f0 656 devpriv->asics[asic].iobase + REG_ENAB0 + port);
6baef150
CC
657 }
658 }
659 return 0;
660}
661
da91b269 662static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
6baef150
CC
663{
664 unsigned long flags;
665
5f74ea14 666 spin_lock_irqsave(&subpriv->intr.spinlock, flags);
6baef150
CC
667 if (subpriv->intr.active)
668 pcmuio_stop_intr(dev, s);
5f74ea14 669 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
6baef150
CC
670
671 return 0;
672}
673
674/*
675 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
676 */
677static int
da91b269 678pcmuio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 679 unsigned int trignum)
6baef150
CC
680{
681 unsigned long flags;
682 int event = 0;
683
684 if (trignum != 0)
685 return -EINVAL;
686
5f74ea14 687 spin_lock_irqsave(&subpriv->intr.spinlock, flags);
6baef150 688 s->async->inttrig = 0;
0389245f 689 if (subpriv->intr.active)
6baef150 690 event = pcmuio_start_intr(dev, s);
0389245f 691
5f74ea14 692 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
6baef150 693
0389245f 694 if (event)
6baef150 695 comedi_event(dev, s);
6baef150
CC
696
697 return 1;
698}
699
700/*
701 * 'do_cmd' function for an 'INTERRUPT' subdevice.
702 */
da91b269 703static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
6baef150 704{
ea6d0d4c 705 struct comedi_cmd *cmd = &s->async->cmd;
6baef150
CC
706 unsigned long flags;
707 int event = 0;
708
5f74ea14 709 spin_lock_irqsave(&subpriv->intr.spinlock, flags);
6baef150
CC
710 subpriv->intr.active = 1;
711
712 /* Set up end of acquisition. */
713 switch (cmd->stop_src) {
714 case TRIG_COUNT:
715 subpriv->intr.continuous = 0;
716 subpriv->intr.stop_count = cmd->stop_arg;
717 break;
718 default:
719 /* TRIG_NONE */
720 subpriv->intr.continuous = 1;
721 subpriv->intr.stop_count = 0;
722 break;
723 }
724
725 /* Set up start of acquisition. */
726 switch (cmd->start_src) {
727 case TRIG_INT:
728 s->async->inttrig = pcmuio_inttrig_start_intr;
729 break;
730 default:
731 /* TRIG_NOW */
732 event = pcmuio_start_intr(dev, s);
733 break;
734 }
5f74ea14 735 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
6baef150 736
0389245f 737 if (event)
6baef150 738 comedi_event(dev, s);
6baef150
CC
739
740 return 0;
741}
742
6baef150 743static int
0a85b6f0
MT
744pcmuio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
745 struct comedi_cmd *cmd)
6baef150 746{
0b8f754a 747 return comedi_pcm_cmdtest(dev, s, cmd);
6baef150
CC
748}
749
6b19f9c6
HS
750static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
751{
a28b5995 752 const struct pcmuio_board *board = comedi_board(dev);
6b19f9c6
HS
753 struct comedi_subdevice *s;
754 int sdev_no, chans_left, n_subdevs, port, asic, thisasic_chanct = 0;
755 unsigned long iobase;
756 unsigned int irq[MAX_ASICS];
8b6c5694 757 int ret;
6b19f9c6
HS
758
759 iobase = it->options[0];
760 irq[0] = it->options[1];
761 irq[1] = it->options[2];
762
763 dev_dbg(dev->hw_dev, "comedi%d: %s: io: %lx attached\n", dev->minor,
2d2111ea 764 dev->driver->driver_name, iobase);
6b19f9c6
HS
765
766 dev->iobase = iobase;
767
768 if (!iobase || !request_region(iobase,
a28b5995 769 board->num_asics * ASIC_IOSIZE,
2d2111ea 770 dev->driver->driver_name)) {
6b19f9c6
HS
771 dev_err(dev->hw_dev, "I/O port conflict\n");
772 return -EIO;
773 }
774
a28b5995 775 dev->board_name = board->name;
6b19f9c6
HS
776
777/*
778 * Allocate the private structure area. alloc_private() is a
779 * convenient macro defined in comedidev.h.
6baef150 780 */
6b19f9c6
HS
781 if (alloc_private(dev, sizeof(struct pcmuio_private)) < 0) {
782 dev_warn(dev->hw_dev, "cannot allocate private data structure\n");
783 return -ENOMEM;
784 }
785
786 for (asic = 0; asic < MAX_ASICS; ++asic) {
787 devpriv->asics[asic].num = asic;
788 devpriv->asics[asic].iobase = dev->iobase + asic * ASIC_IOSIZE;
789 devpriv->asics[asic].irq = 0; /* this gets actually set at the end of
790 this function when we
791 request_irqs */
792 spin_lock_init(&devpriv->asics[asic].spinlock);
793 }
794
a28b5995 795 chans_left = CHANS_PER_ASIC * board->num_asics;
6b19f9c6
HS
796 n_subdevs = CALC_N_SUBDEVS(chans_left);
797 devpriv->sprivs =
798 kcalloc(n_subdevs, sizeof(struct pcmuio_subdev_private),
799 GFP_KERNEL);
800 if (!devpriv->sprivs) {
801 dev_warn(dev->hw_dev, "cannot allocate subdevice private data structures\n");
802 return -ENOMEM;
803 }
eea6838b 804
8b6c5694
HS
805 ret = comedi_alloc_subdevices(dev, n_subdevs);
806 if (ret)
807 return ret;
6b19f9c6
HS
808
809 port = 0;
810 asic = 0;
811 for (sdev_no = 0; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
812 int byte_no;
813
814 s = dev->subdevices + sdev_no;
815 s->private = devpriv->sprivs + sdev_no;
816 s->maxdata = 1;
817 s->range_table = &range_digital;
818 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
819 s->type = COMEDI_SUBD_DIO;
820 s->insn_bits = pcmuio_dio_insn_bits;
821 s->insn_config = pcmuio_dio_insn_config;
822 s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
823 subpriv->intr.asic = -1;
824 subpriv->intr.first_chan = -1;
825 subpriv->intr.asic_chan = -1;
826 subpriv->intr.num_asic_chans = -1;
827 subpriv->intr.active = 0;
828 s->len_chanlist = 1;
829
830 /* save the ioport address for each 'port' of 8 channels in the
831 subdevice */
832 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
833 if (port >= PORTS_PER_ASIC) {
834 port = 0;
835 ++asic;
836 thisasic_chanct = 0;
837 }
838 subpriv->iobases[byte_no] =
839 devpriv->asics[asic].iobase + port;
840
841 if (thisasic_chanct <
842 CHANS_PER_PORT * INTR_PORTS_PER_ASIC
843 && subpriv->intr.asic < 0) {
844 /* this is an interrupt subdevice, so setup the struct */
845 subpriv->intr.asic = asic;
846 subpriv->intr.active = 0;
847 subpriv->intr.stop_count = 0;
848 subpriv->intr.first_chan = byte_no * 8;
849 subpriv->intr.asic_chan = thisasic_chanct;
850 subpriv->intr.num_asic_chans =
851 s->n_chan - subpriv->intr.first_chan;
852 dev->read_subdev = s;
853 s->subdev_flags |= SDF_CMD_READ;
854 s->cancel = pcmuio_cancel;
855 s->do_cmd = pcmuio_cmd;
856 s->do_cmdtest = pcmuio_cmdtest;
857 s->len_chanlist = subpriv->intr.num_asic_chans;
858 }
859 thisasic_chanct += CHANS_PER_PORT;
860 }
861 spin_lock_init(&subpriv->intr.spinlock);
862
863 chans_left -= s->n_chan;
864
865 if (!chans_left) {
866 asic = 0; /* reset the asic to our first asic, to do intr subdevs */
867 port = 0;
868 }
869
870 }
871
872 init_asics(dev); /* clear out all the registers, basically */
873
874 for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
875 if (irq[asic]
876 && request_irq(irq[asic], interrupt_pcmuio,
a28b5995 877 IRQF_SHARED, board->name, dev)) {
6b19f9c6
HS
878 int i;
879 /* unroll the allocated irqs.. */
880 for (i = asic - 1; i >= 0; --i) {
881 free_irq(irq[i], dev);
882 devpriv->asics[i].irq = irq[i] = 0;
883 }
884 irq[asic] = 0;
885 }
886 devpriv->asics[asic].irq = irq[asic];
887 }
888
889 dev->irq = irq[0]; /* grr.. wish comedi dev struct supported multiple
890 irqs.. */
891
892 if (irq[0]) {
893 dev_dbg(dev->hw_dev, "irq: %u\n", irq[0]);
a28b5995 894 if (irq[1] && board->num_asics == 2)
6b19f9c6
HS
895 dev_dbg(dev->hw_dev, "second ASIC irq: %u\n", irq[1]);
896 } else {
897 dev_dbg(dev->hw_dev, "(IRQ mode disabled)\n");
898 }
899
900
901 return 1;
902}
903
484ecc95 904static void pcmuio_detach(struct comedi_device *dev)
6b19f9c6 905{
a28b5995 906 const struct pcmuio_board *board = comedi_board(dev);
6b19f9c6
HS
907 int i;
908
6b19f9c6 909 if (dev->iobase)
a28b5995 910 release_region(dev->iobase, ASIC_IOSIZE * board->num_asics);
6b19f9c6
HS
911 for (i = 0; i < MAX_ASICS; ++i) {
912 if (devpriv->asics[i].irq)
913 free_irq(devpriv->asics[i].irq, dev);
914 }
6b19f9c6
HS
915 if (devpriv && devpriv->sprivs)
916 kfree(devpriv->sprivs);
6b19f9c6
HS
917}
918
919static const struct pcmuio_board pcmuio_boards[] = {
920 {
921 .name = "pcmuio48",
922 .num_asics = 1,
923 .num_ports = 6,
924 }, {
925 .name = "pcmuio96",
926 .num_asics = 2,
927 .num_ports = 12,
928 },
929};
930
294f930d 931static struct comedi_driver pcmuio_driver = {
6b19f9c6
HS
932 .driver_name = "pcmuio",
933 .module = THIS_MODULE,
934 .attach = pcmuio_attach,
935 .detach = pcmuio_detach,
936 .board_name = &pcmuio_boards[0].name,
937 .offset = sizeof(struct pcmuio_board),
938 .num_names = ARRAY_SIZE(pcmuio_boards),
939};
294f930d 940module_comedi_driver(pcmuio_driver);
90f703d3
AT
941
942MODULE_AUTHOR("Comedi http://www.comedi.org");
943MODULE_DESCRIPTION("Comedi low-level driver");
944MODULE_LICENSE("GPL");
This page took 0.453415 seconds and 5 git commands to generate.