staging: comedi: remove inline alloc_private()
[deliverable/linux.git] / drivers / staging / comedi / drivers / pcmuio.c
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 /*
23 Driver: pcmuio
24 Description: A driver for the PCM-UIO48A and PCM-UIO96A boards from Winsystems.
25 Devices: [Winsystems] PCM-UIO48A (pcmuio48), PCM-UIO96A (pcmuio96)
26 Author: Calin Culianu <calin@ajvar.org>
27 Updated: Fri, 13 Jan 2006 12:01:01 -0500
28 Status: works
29
30 A driver for the relatively straightforward-to-program PCM-UIO48A and
31 PCM-UIO96A boards from Winsystems. These boards use either one or two
32 (in the 96-DIO version) WS16C48 ASIC HighDensity I/O Chips (HDIO).
33 This chip is interesting in that each I/O line is individually
34 programmable for INPUT or OUTPUT (thus comedi_dio_config can be done
35 on a per-channel basis). Also, each chip supports edge-triggered
36 interrupts for the first 24 I/O lines. Of course, since the
37 96-channel version of the board has two ASICs, it can detect polarity
38 changes on up to 48 I/O lines. Since this is essentially an (non-PnP)
39 ISA board, I/O Address and IRQ selection are done through jumpers on
40 the board. You need to pass that information to this driver as the
41 first and second comedi_config option, respectively. Note that the
42 48-channel version uses 16 bytes of IO memory and the 96-channel
43 version uses 32-bytes (in case you are worried about conflicts). The
44 48-channel board is split into two 24-channel comedi subdevices.
45 The 96-channel board is split into 4 24-channel DIO subdevices.
46
47 Note that IRQ support has been added, but it is untested.
48
49 To 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).
51 Then, use use comedi_commands with TRIG_NOW.
52 Your callback will be called each time an edge is triggered, and the data
53 values will be two sample_t's, which should be concatenated to form one
54 32-bit unsigned int. This value is the mask of channels that had
55 edges detected from your channel list. Note that the bits positions
56 in the mask correspond to positions in your chanlist when you specified
57 the command and *not* channel id's!
58
59 To set the polarity of the edge-detection interrupts pass a nonzero value for
60 either CR_RANGE or CR_AREF for edge-up polarity, or a zero value for both
61 CR_RANGE and CR_AREF if you want edge-down polarity.
62
63 In the 48-channel version:
64
65 On subdev 0, the first 24 channels channels are edge-detect channels.
66
67 In the 96-channel board you have the collowing channels that can do edge detection:
68
69 subdev 0, channels 0-24 (first 24 channels of 1st ASIC)
70 subdev 2, channels 0-24 (first 24 channels of 2nd ASIC)
71
72 Configuration 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
78 #include <linux/interrupt.h>
79 #include <linux/slab.h>
80 #include "../comedidev.h"
81 #include "pcm_common.h"
82
83 #include <linux/pci.h> /* for PCI devices */
84
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 */
151 struct pcmuio_board {
152 const char *name;
153 const int num_asics;
154 const int num_channels_per_port;
155 const int num_ports;
156 };
157
158 /* this structure is for data unique to this subdevice. */
159 struct pcmuio_subdev_private {
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;
179 };
180
181 /* this structure is for data unique to this hardware driver. If
182 several hardware drivers keep similar information in this structure,
183 feel free to suggest moving the variable to the struct comedi_device struct. */
184 struct pcmuio_private {
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];
194 struct pcmuio_subdev_private *sprivs;
195 };
196
197 #define subpriv ((struct pcmuio_subdev_private *)s->private)
198
199 /* DIO devices are slightly special. Although it is possible to
200 * implement the insn_read/insn_write interface, it is much more
201 * useful to applications if you implement the insn_bits interface.
202 * This allows packed reading/writing of the DIO channels. The
203 * comedi core can convert between insn_bits and insn_read/write */
204 static int pcmuio_dio_insn_bits(struct comedi_device *dev,
205 struct comedi_subdevice *s,
206 struct comedi_insn *insn, unsigned int *data)
207 {
208 int byte_no;
209
210 /* NOTE:
211 reading a 0 means this channel was high
212 writine a 0 sets the channel high
213 reading a 1 means this channel was low
214 writing a 1 means set this channel low
215
216 Therefore everything is always inverted. */
217
218 /* The insn data is a mask in data[0] and the new data
219 * in data[1], each channel cooresponding to a bit. */
220
221 #ifdef DAMMIT_ITS_BROKEN
222 /* DEBUG */
223 dev_dbg(dev->class_dev, "write mask: %08x data: %08x\n", data[0],
224 data[1]);
225 #endif
226
227 s->state = 0;
228
229 for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
230 /* address of 8-bit port */
231 unsigned long ioaddr = subpriv->iobases[byte_no],
232 /* bit offset of port in 32-bit doubleword */
233 offset = byte_no * 8;
234 /* this 8-bit port's data */
235 unsigned char byte = 0,
236 /* The write mask for this port (if any) */
237 write_mask_byte = (data[0] >> offset) & 0xff,
238 /* The data byte for this port */
239 data_byte = (data[1] >> offset) & 0xff;
240
241 byte = inb(ioaddr); /* read all 8-bits for this port */
242
243 #ifdef DAMMIT_ITS_BROKEN
244 /* DEBUG */
245 printk
246 ("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ",
247 byte_no, (unsigned)write_mask_byte, (unsigned)data_byte,
248 offset, ioaddr, (unsigned)byte);
249 #endif
250
251 if (write_mask_byte) {
252 /* this byte has some write_bits -- so set the output lines */
253 byte &= ~write_mask_byte; /* clear bits for write mask */
254 byte |= ~data_byte & write_mask_byte; /* set to inverted data_byte */
255 /* Write out the new digital output state */
256 outb(byte, ioaddr);
257 }
258 #ifdef DAMMIT_ITS_BROKEN
259 /* DEBUG */
260 dev_dbg(dev->class_dev, "data_out_byte %02x\n", (unsigned)byte);
261 #endif
262 /* save the digital input lines for this byte.. */
263 s->state |= ((unsigned int)byte) << offset;
264 }
265
266 /* now return the DIO lines to data[1] - note they came inverted! */
267 data[1] = ~s->state;
268
269 #ifdef DAMMIT_ITS_BROKEN
270 /* DEBUG */
271 dev_dbg(dev->class_dev, "s->state %08x data_out %08x\n", s->state,
272 data[1]);
273 #endif
274
275 return insn->n;
276 }
277
278 /* The input or output configuration of each digital line is
279 * configured by a special insn_config instruction. chanspec
280 * contains the channel to be changed, and data[0] contains the
281 * value COMEDI_INPUT or COMEDI_OUTPUT. */
282 static int pcmuio_dio_insn_config(struct comedi_device *dev,
283 struct comedi_subdevice *s,
284 struct comedi_insn *insn, unsigned int *data)
285 {
286 int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
287 chan % 8;
288 unsigned long ioaddr;
289 unsigned char byte;
290
291 /* Compute ioaddr for this channel */
292 ioaddr = subpriv->iobases[byte_no];
293
294 /* NOTE:
295 writing a 0 an IO channel's bit sets the channel to INPUT
296 and pulls the line high as well
297
298 writing a 1 to an IO channel's bit pulls the line low
299
300 All channels are implicitly always in OUTPUT mode -- but when
301 they are high they can be considered to be in INPUT mode..
302
303 Thus, we only force channels low if the config request was INPUT,
304 otherwise we do nothing to the hardware. */
305
306 switch (data[0]) {
307 case INSN_CONFIG_DIO_OUTPUT:
308 /* save to io_bits -- don't actually do anything since
309 all input channels are also output channels... */
310 s->io_bits |= 1 << chan;
311 break;
312 case INSN_CONFIG_DIO_INPUT:
313 /* write a 0 to the actual register representing the channel
314 to set it to 'input'. 0 means "float high". */
315 byte = inb(ioaddr);
316 byte &= ~(1 << bit_no);
317 /**< set input channel to '0' */
318
319 /* write out byte -- this is the only time we actually affect the
320 hardware as all channels are implicitly output -- but input
321 channels are set to float-high */
322 outb(byte, ioaddr);
323
324 /* save to io_bits */
325 s->io_bits &= ~(1 << chan);
326 break;
327
328 case INSN_CONFIG_DIO_QUERY:
329 /* retrieve from shadow register */
330 data[1] =
331 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
332 return insn->n;
333 break;
334
335 default:
336 return -EINVAL;
337 break;
338 }
339
340 return insn->n;
341 }
342
343 static void switch_page(struct comedi_device *dev, int asic, int page)
344 {
345 const struct pcmuio_board *board = comedi_board(dev);
346 struct pcmuio_private *devpriv = dev->private;
347
348 if (asic < 0 || asic >= board->num_asics)
349 return; /* paranoia */
350 if (page < 0 || page >= NUM_PAGES)
351 return; /* more paranoia */
352
353 devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
354 devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
355
356 /* now write out the shadow register */
357 outb(devpriv->asics[asic].pagelock,
358 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
359 }
360
361 static void init_asics(struct comedi_device *dev)
362 { /* sets up an
363 ASIC chip to defaults */
364 const struct pcmuio_board *board = comedi_board(dev);
365 int asic;
366
367 for (asic = 0; asic < board->num_asics; ++asic) {
368 int port, page;
369 unsigned long baseaddr = dev->iobase + asic * ASIC_IOSIZE;
370
371 switch_page(dev, asic, 0); /* switch back to page 0 */
372
373 /* first, clear all the DIO port bits */
374 for (port = 0; port < PORTS_PER_ASIC; ++port)
375 outb(0, baseaddr + REG_PORT0 + port);
376
377 /* Next, clear all the paged registers for each page */
378 for (page = 1; page < NUM_PAGES; ++page) {
379 int reg;
380 /* now clear all the paged registers */
381 switch_page(dev, asic, page);
382 for (reg = FIRST_PAGED_REG;
383 reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
384 outb(0, baseaddr + reg);
385 }
386
387 /* DEBUG set rising edge interrupts on port0 of both asics */
388 /*switch_page(dev, asic, PAGE_POL);
389 outb(0xff, baseaddr + REG_POL0);
390 switch_page(dev, asic, PAGE_ENAB);
391 outb(0xff, baseaddr + REG_ENAB0); */
392 /* END DEBUG */
393
394 switch_page(dev, asic, 0); /* switch back to default page 0 */
395
396 }
397 }
398
399 #ifdef notused
400 static void lock_port(struct comedi_device *dev, int asic, int port)
401 {
402 const struct pcmuio_board *board = comedi_board(dev);
403 struct pcmuio_private *devpriv = dev->private;
404
405 if (asic < 0 || asic >= board->num_asics)
406 return; /* paranoia */
407 if (port < 0 || port >= PORTS_PER_ASIC)
408 return; /* more paranoia */
409
410 devpriv->asics[asic].pagelock |= 0x1 << port;
411 /* now write out the shadow register */
412 outb(devpriv->asics[asic].pagelock,
413 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
414 }
415
416 static void unlock_port(struct comedi_device *dev, int asic, int port)
417 {
418 const struct pcmuio_board *board = comedi_board(dev);
419 struct pcmuio_private *devpriv = dev->private;
420
421 if (asic < 0 || asic >= board->num_asics)
422 return; /* paranoia */
423 if (port < 0 || port >= PORTS_PER_ASIC)
424 return; /* more paranoia */
425 devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
426 /* now write out the shadow register */
427 outb(devpriv->asics[asic].pagelock,
428 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
429 }
430 #endif /* notused */
431
432 static void pcmuio_stop_intr(struct comedi_device *dev,
433 struct comedi_subdevice *s)
434 {
435 int nports, firstport, asic, port;
436 struct pcmuio_private *devpriv = dev->private;
437
438 asic = subpriv->intr.asic;
439 if (asic < 0)
440 return; /* not an interrupt subdev */
441
442 subpriv->intr.enabled_mask = 0;
443 subpriv->intr.active = 0;
444 s->async->inttrig = NULL;
445 nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
446 firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
447 switch_page(dev, asic, PAGE_ENAB);
448 for (port = firstport; port < firstport + nports; ++port) {
449 /* disable all intrs for this subdev.. */
450 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
451 }
452 }
453
454 static irqreturn_t interrupt_pcmuio(int irq, void *d)
455 {
456 int asic, got1 = 0;
457 struct comedi_device *dev = (struct comedi_device *)d;
458 struct pcmuio_private *devpriv = dev->private;
459 int i;
460
461 for (asic = 0; asic < MAX_ASICS; ++asic) {
462 if (irq == devpriv->asics[asic].irq) {
463 unsigned long flags;
464 unsigned triggered = 0;
465 unsigned long iobase = devpriv->asics[asic].iobase;
466 /* it is an interrupt for ASIC #asic */
467 unsigned char int_pend;
468
469 spin_lock_irqsave(&devpriv->asics[asic].spinlock,
470 flags);
471
472 int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
473
474 if (int_pend) {
475 int port;
476 for (port = 0; port < INTR_PORTS_PER_ASIC;
477 ++port) {
478 if (int_pend & (0x1 << port)) {
479 unsigned char
480 io_lines_with_edges = 0;
481 switch_page(dev, asic,
482 PAGE_INT_ID);
483 io_lines_with_edges =
484 inb(iobase +
485 REG_INT_ID0 + port);
486
487 if (io_lines_with_edges)
488 /* clear pending interrupt */
489 outb(0, iobase +
490 REG_INT_ID0 +
491 port);
492
493 triggered |=
494 io_lines_with_edges <<
495 port * 8;
496 }
497 }
498
499 ++got1;
500 }
501
502 spin_unlock_irqrestore(&devpriv->asics[asic].spinlock,
503 flags);
504
505 if (triggered) {
506 struct comedi_subdevice *s;
507 /* TODO here: dispatch io lines to subdevs with commands.. */
508 printk
509 ("PCMUIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n",
510 irq, asic, triggered);
511 for (i = 0; i < dev->n_subdevices; i++) {
512 s = &dev->subdevices[i];
513 if (subpriv->intr.asic == asic) { /* this is an interrupt subdev, and it matches this asic! */
514 unsigned long flags;
515 unsigned oldevents;
516
517 spin_lock_irqsave(&subpriv->
518 intr.spinlock,
519 flags);
520
521 oldevents = s->async->events;
522
523 if (subpriv->intr.active) {
524 unsigned mytrig =
525 ((triggered >>
526 subpriv->intr.asic_chan)
527 &
528 ((0x1 << subpriv->
529 intr.
530 num_asic_chans) -
531 1)) << subpriv->
532 intr.first_chan;
533 if (mytrig &
534 subpriv->intr.enabled_mask)
535 {
536 unsigned int val
537 = 0;
538 unsigned int n,
539 ch, len;
540
541 len =
542 s->
543 async->cmd.chanlist_len;
544 for (n = 0;
545 n < len;
546 n++) {
547 ch = CR_CHAN(s->async->cmd.chanlist[n]);
548 if (mytrig & (1U << ch)) {
549 val |= (1U << n);
550 }
551 }
552 /* Write the scan to the buffer. */
553 if (comedi_buf_put(s->async, ((short *)&val)[0])
554 &&
555 comedi_buf_put
556 (s->async,
557 ((short *)
558 &val)[1]))
559 {
560 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
561 } else {
562 /* Overflow! Stop acquisition!! */
563 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
564 pcmuio_stop_intr
565 (dev,
566 s);
567 }
568
569 /* Check for end of acquisition. */
570 if (!subpriv->intr.continuous) {
571 /* stop_src == TRIG_COUNT */
572 if (subpriv->intr.stop_count > 0) {
573 subpriv->intr.stop_count--;
574 if (subpriv->intr.stop_count == 0) {
575 s->async->events |= COMEDI_CB_EOA;
576 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
577 pcmuio_stop_intr
578 (dev,
579 s);
580 }
581 }
582 }
583 }
584 }
585
586 spin_unlock_irqrestore
587 (&subpriv->intr.spinlock,
588 flags);
589
590 if (oldevents !=
591 s->async->events) {
592 comedi_event(dev, s);
593 }
594
595 }
596
597 }
598 }
599
600 }
601 }
602 if (!got1)
603 return IRQ_NONE; /* interrupt from other source */
604 return IRQ_HANDLED;
605 }
606
607 static int pcmuio_start_intr(struct comedi_device *dev,
608 struct comedi_subdevice *s)
609 {
610 struct pcmuio_private *devpriv = dev->private;
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;
620 struct comedi_cmd *cmd = &s->async->cmd;
621
622 asic = subpriv->intr.asic;
623 if (asic < 0)
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])
634 || CR_RANGE(cmd->
635 chanlist[n]) ? 1U : 0U)
636 << CR_CHAN(cmd->chanlist[n]);
637 }
638 }
639 bits &= ((0x1 << subpriv->intr.num_asic_chans) -
640 1) << subpriv->intr.first_chan;
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 =
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;
651 /* set enab intrs for this subdev.. */
652 outb(enab,
653 devpriv->asics[asic].iobase + REG_ENAB0 + port);
654 switch_page(dev, asic, PAGE_POL);
655 outb(pol,
656 devpriv->asics[asic].iobase + REG_ENAB0 + port);
657 }
658 }
659 return 0;
660 }
661
662 static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
663 {
664 unsigned long flags;
665
666 spin_lock_irqsave(&subpriv->intr.spinlock, flags);
667 if (subpriv->intr.active)
668 pcmuio_stop_intr(dev, s);
669 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
670
671 return 0;
672 }
673
674 /*
675 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
676 */
677 static int
678 pcmuio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
679 unsigned int trignum)
680 {
681 unsigned long flags;
682 int event = 0;
683
684 if (trignum != 0)
685 return -EINVAL;
686
687 spin_lock_irqsave(&subpriv->intr.spinlock, flags);
688 s->async->inttrig = NULL;
689 if (subpriv->intr.active)
690 event = pcmuio_start_intr(dev, s);
691
692 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
693
694 if (event)
695 comedi_event(dev, s);
696
697 return 1;
698 }
699
700 /*
701 * 'do_cmd' function for an 'INTERRUPT' subdevice.
702 */
703 static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
704 {
705 struct comedi_cmd *cmd = &s->async->cmd;
706 unsigned long flags;
707 int event = 0;
708
709 spin_lock_irqsave(&subpriv->intr.spinlock, flags);
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 }
735 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
736
737 if (event)
738 comedi_event(dev, s);
739
740 return 0;
741 }
742
743 static int
744 pcmuio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
745 struct comedi_cmd *cmd)
746 {
747 return comedi_pcm_cmdtest(dev, s, cmd);
748 }
749
750 static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
751 {
752 const struct pcmuio_board *board = comedi_board(dev);
753 struct pcmuio_private *devpriv;
754 struct comedi_subdevice *s;
755 int sdev_no, chans_left, n_subdevs, port, asic, thisasic_chanct = 0;
756 unsigned long iobase;
757 unsigned int irq[MAX_ASICS];
758 int ret;
759
760 iobase = it->options[0];
761 irq[0] = it->options[1];
762 irq[1] = it->options[2];
763
764 dev_dbg(dev->class_dev, "%s: io: %lx attach\n",
765 dev->driver->driver_name, iobase);
766
767 dev->iobase = iobase;
768
769 if (!iobase || !request_region(iobase,
770 board->num_asics * ASIC_IOSIZE,
771 dev->driver->driver_name)) {
772 dev_err(dev->class_dev, "I/O port conflict\n");
773 return -EIO;
774 }
775
776 dev->board_name = board->name;
777
778 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
779 if (!devpriv)
780 return -ENOMEM;
781 dev->private = devpriv;
782
783 for (asic = 0; asic < MAX_ASICS; ++asic) {
784 devpriv->asics[asic].num = asic;
785 devpriv->asics[asic].iobase = dev->iobase + asic * ASIC_IOSIZE;
786 devpriv->asics[asic].irq = 0; /* this gets actually set at the end of
787 this function when we
788 request_irqs */
789 spin_lock_init(&devpriv->asics[asic].spinlock);
790 }
791
792 chans_left = CHANS_PER_ASIC * board->num_asics;
793 n_subdevs = CALC_N_SUBDEVS(chans_left);
794 devpriv->sprivs =
795 kcalloc(n_subdevs, sizeof(struct pcmuio_subdev_private),
796 GFP_KERNEL);
797 if (!devpriv->sprivs) {
798 dev_warn(dev->class_dev,
799 "cannot allocate subdevice private data structures\n");
800 return -ENOMEM;
801 }
802
803 ret = comedi_alloc_subdevices(dev, n_subdevs);
804 if (ret)
805 return ret;
806
807 port = 0;
808 asic = 0;
809 for (sdev_no = 0; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
810 int byte_no;
811
812 s = &dev->subdevices[sdev_no];
813 s->private = &devpriv->sprivs[sdev_no];
814 s->maxdata = 1;
815 s->range_table = &range_digital;
816 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
817 s->type = COMEDI_SUBD_DIO;
818 s->insn_bits = pcmuio_dio_insn_bits;
819 s->insn_config = pcmuio_dio_insn_config;
820 s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
821 subpriv->intr.asic = -1;
822 subpriv->intr.first_chan = -1;
823 subpriv->intr.asic_chan = -1;
824 subpriv->intr.num_asic_chans = -1;
825 subpriv->intr.active = 0;
826 s->len_chanlist = 1;
827
828 /* save the ioport address for each 'port' of 8 channels in the
829 subdevice */
830 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
831 if (port >= PORTS_PER_ASIC) {
832 port = 0;
833 ++asic;
834 thisasic_chanct = 0;
835 }
836 subpriv->iobases[byte_no] =
837 devpriv->asics[asic].iobase + port;
838
839 if (thisasic_chanct <
840 CHANS_PER_PORT * INTR_PORTS_PER_ASIC
841 && subpriv->intr.asic < 0) {
842 /* this is an interrupt subdevice, so setup the struct */
843 subpriv->intr.asic = asic;
844 subpriv->intr.active = 0;
845 subpriv->intr.stop_count = 0;
846 subpriv->intr.first_chan = byte_no * 8;
847 subpriv->intr.asic_chan = thisasic_chanct;
848 subpriv->intr.num_asic_chans =
849 s->n_chan - subpriv->intr.first_chan;
850 dev->read_subdev = s;
851 s->subdev_flags |= SDF_CMD_READ;
852 s->cancel = pcmuio_cancel;
853 s->do_cmd = pcmuio_cmd;
854 s->do_cmdtest = pcmuio_cmdtest;
855 s->len_chanlist = subpriv->intr.num_asic_chans;
856 }
857 thisasic_chanct += CHANS_PER_PORT;
858 }
859 spin_lock_init(&subpriv->intr.spinlock);
860
861 chans_left -= s->n_chan;
862
863 if (!chans_left) {
864 asic = 0; /* reset the asic to our first asic, to do intr subdevs */
865 port = 0;
866 }
867
868 }
869
870 init_asics(dev); /* clear out all the registers, basically */
871
872 for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
873 if (irq[asic]
874 && request_irq(irq[asic], interrupt_pcmuio,
875 IRQF_SHARED, board->name, dev)) {
876 int i;
877 /* unroll the allocated irqs.. */
878 for (i = asic - 1; i >= 0; --i) {
879 free_irq(irq[i], dev);
880 devpriv->asics[i].irq = irq[i] = 0;
881 }
882 irq[asic] = 0;
883 }
884 devpriv->asics[asic].irq = irq[asic];
885 }
886
887 dev->irq = irq[0]; /* grr.. wish comedi dev struct supported multiple
888 irqs.. */
889
890 if (irq[0]) {
891 dev_dbg(dev->class_dev, "irq: %u\n", irq[0]);
892 if (irq[1] && board->num_asics == 2)
893 dev_dbg(dev->class_dev, "second ASIC irq: %u\n",
894 irq[1]);
895 } else {
896 dev_dbg(dev->class_dev, "(IRQ mode disabled)\n");
897 }
898
899
900 return 1;
901 }
902
903 static void pcmuio_detach(struct comedi_device *dev)
904 {
905 const struct pcmuio_board *board = comedi_board(dev);
906 struct pcmuio_private *devpriv = dev->private;
907 int i;
908
909 if (dev->iobase)
910 release_region(dev->iobase, ASIC_IOSIZE * board->num_asics);
911 for (i = 0; i < MAX_ASICS; ++i) {
912 if (devpriv->asics[i].irq)
913 free_irq(devpriv->asics[i].irq, dev);
914 }
915 if (devpriv && devpriv->sprivs)
916 kfree(devpriv->sprivs);
917 }
918
919 static 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
931 static struct comedi_driver pcmuio_driver = {
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 };
940 module_comedi_driver(pcmuio_driver);
941
942 MODULE_AUTHOR("Comedi http://www.comedi.org");
943 MODULE_DESCRIPTION("Comedi low-level driver");
944 MODULE_LICENSE("GPL");
This page took 0.11823 seconds and 6 git commands to generate.