Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pcmuio.c
Go to the documentation of this file.
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 <[email protected]>
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 <[email protected]>
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
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 };
158 /* this structure is for data unique to this subdevice. */
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;
176  int continuous;
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. */
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;
193  } asics[MAX_ASICS];
195 };
196 
197 /*
198  * most drivers define the following macro to make it easy to
199  * access the private structure.
200  */
201 #define devpriv ((struct pcmuio_private *)dev->private)
202 #define subpriv ((struct pcmuio_subdev_private *)s->private)
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 */
209 static int pcmuio_dio_insn_bits(struct comedi_device *dev,
210  struct comedi_subdevice *s,
211  struct comedi_insn *insn, unsigned int *data)
212 {
213  int byte_no;
214 
215  /* NOTE:
216  reading a 0 means this channel was high
217  writine a 0 sets the channel high
218  reading a 1 means this channel was low
219  writing a 1 means set this channel low
220 
221  Therefore everything is always inverted. */
222 
223  /* The insn data is a mask in data[0] and the new data
224  * in data[1], each channel cooresponding to a bit. */
225 
226 #ifdef DAMMIT_ITS_BROKEN
227  /* DEBUG */
228  dev_dbg(dev->class_dev, "write mask: %08x data: %08x\n", data[0],
229  data[1]);
230 #endif
231 
232  s->state = 0;
233 
234  for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
235  /* address of 8-bit port */
236  unsigned long ioaddr = subpriv->iobases[byte_no],
237  /* bit offset of port in 32-bit doubleword */
238  offset = byte_no * 8;
239  /* this 8-bit port's data */
240  unsigned char byte = 0,
241  /* The write mask for this port (if any) */
242  write_mask_byte = (data[0] >> offset) & 0xff,
243  /* The data byte for this port */
244  data_byte = (data[1] >> offset) & 0xff;
245 
246  byte = inb(ioaddr); /* read all 8-bits for this port */
247 
248 #ifdef DAMMIT_ITS_BROKEN
249  /* DEBUG */
250  printk
251  ("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ",
252  byte_no, (unsigned)write_mask_byte, (unsigned)data_byte,
253  offset, ioaddr, (unsigned)byte);
254 #endif
255 
256  if (write_mask_byte) {
257  /* this byte has some write_bits -- so set the output lines */
258  byte &= ~write_mask_byte; /* clear bits for write mask */
259  byte |= ~data_byte & write_mask_byte; /* set to inverted data_byte */
260  /* Write out the new digital output state */
261  outb(byte, ioaddr);
262  }
263 #ifdef DAMMIT_ITS_BROKEN
264  /* DEBUG */
265  dev_dbg(dev->class_dev, "data_out_byte %02x\n", (unsigned)byte);
266 #endif
267  /* save the digital input lines for this byte.. */
268  s->state |= ((unsigned int)byte) << offset;
269  }
270 
271  /* now return the DIO lines to data[1] - note they came inverted! */
272  data[1] = ~s->state;
273 
274 #ifdef DAMMIT_ITS_BROKEN
275  /* DEBUG */
276  dev_dbg(dev->class_dev, "s->state %08x data_out %08x\n", s->state,
277  data[1]);
278 #endif
279 
280  return insn->n;
281 }
282 
283 /* The input or output configuration of each digital line is
284  * configured by a special insn_config instruction. chanspec
285  * contains the channel to be changed, and data[0] contains the
286  * value COMEDI_INPUT or COMEDI_OUTPUT. */
287 static int pcmuio_dio_insn_config(struct comedi_device *dev,
288  struct comedi_subdevice *s,
289  struct comedi_insn *insn, unsigned int *data)
290 {
291  int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
292  chan % 8;
293  unsigned long ioaddr;
294  unsigned char byte;
295 
296  /* Compute ioaddr for this channel */
297  ioaddr = subpriv->iobases[byte_no];
298 
299  /* NOTE:
300  writing a 0 an IO channel's bit sets the channel to INPUT
301  and pulls the line high as well
302 
303  writing a 1 to an IO channel's bit pulls the line low
304 
305  All channels are implicitly always in OUTPUT mode -- but when
306  they are high they can be considered to be in INPUT mode..
307 
308  Thus, we only force channels low if the config request was INPUT,
309  otherwise we do nothing to the hardware. */
310 
311  switch (data[0]) {
313  /* save to io_bits -- don't actually do anything since
314  all input channels are also output channels... */
315  s->io_bits |= 1 << chan;
316  break;
318  /* write a 0 to the actual register representing the channel
319  to set it to 'input'. 0 means "float high". */
320  byte = inb(ioaddr);
321  byte &= ~(1 << bit_no);
324  /* write out byte -- this is the only time we actually affect the
325  hardware as all channels are implicitly output -- but input
326  channels are set to float-high */
327  outb(byte, ioaddr);
328 
329  /* save to io_bits */
330  s->io_bits &= ~(1 << chan);
331  break;
332 
334  /* retrieve from shadow register */
335  data[1] =
336  (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
337  return insn->n;
338  break;
339 
340  default:
341  return -EINVAL;
342  break;
343  }
344 
345  return insn->n;
346 }
347 
348 static void switch_page(struct comedi_device *dev, int asic, int page)
349 {
350  const struct pcmuio_board *board = comedi_board(dev);
351 
352  if (asic < 0 || asic >= board->num_asics)
353  return; /* paranoia */
355  return; /* more paranoia */
356 
357  devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
358  devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
359 
360  /* now write out the shadow register */
361  outb(devpriv->asics[asic].pagelock,
362  dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
363 }
364 
365 static void init_asics(struct comedi_device *dev)
366 { /* sets up an
367  ASIC chip to defaults */
368  const struct pcmuio_board *board = comedi_board(dev);
369  int asic;
370 
371  for (asic = 0; asic < board->num_asics; ++asic) {
372  int port, page;
373  unsigned long baseaddr = dev->iobase + asic * ASIC_IOSIZE;
374 
375  switch_page(dev, asic, 0); /* switch back to page 0 */
376 
377  /* first, clear all the DIO port bits */
378  for (port = 0; port < PORTS_PER_ASIC; ++port)
379  outb(0, baseaddr + REG_PORT0 + port);
380 
381  /* Next, clear all the paged registers for each page */
382  for (page = 1; page < NUM_PAGES; ++page) {
383  int reg;
384  /* now clear all the paged registers */
385  switch_page(dev, asic, page);
386  for (reg = FIRST_PAGED_REG;
388  outb(0, baseaddr + reg);
389  }
390 
391  /* DEBUG set rising edge interrupts on port0 of both asics */
392  /*switch_page(dev, asic, PAGE_POL);
393  outb(0xff, baseaddr + REG_POL0);
394  switch_page(dev, asic, PAGE_ENAB);
395  outb(0xff, baseaddr + REG_ENAB0); */
396  /* END DEBUG */
397 
398  switch_page(dev, asic, 0); /* switch back to default page 0 */
399 
400  }
401 }
402 
403 #ifdef notused
404 static void lock_port(struct comedi_device *dev, int asic, int port)
405 {
406  const struct pcmuio_board *board = comedi_board(dev);
407 
408  if (asic < 0 || asic >= board->num_asics)
409  return; /* paranoia */
410  if (port < 0 || port >= PORTS_PER_ASIC)
411  return; /* more paranoia */
412 
413  devpriv->asics[asic].pagelock |= 0x1 << port;
414  /* now write out the shadow register */
415  outb(devpriv->asics[asic].pagelock,
416  dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
417 }
418 
419 static void unlock_port(struct comedi_device *dev, int asic, int port)
420 {
421  const struct pcmuio_board *board = comedi_board(dev);
422 
423  if (asic < 0 || asic >= board->num_asics)
424  return; /* paranoia */
425  if (port < 0 || port >= PORTS_PER_ASIC)
426  return; /* more paranoia */
427  devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
428  /* now write out the shadow register */
429  outb(devpriv->asics[asic].pagelock,
430  dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
431 }
432 #endif /* notused */
433 
434 static void pcmuio_stop_intr(struct comedi_device *dev,
435  struct comedi_subdevice *s)
436 {
437  int nports, firstport, asic, port;
438 
439  asic = subpriv->intr.asic;
440  if (asic < 0)
441  return; /* not an interrupt subdev */
442 
443  subpriv->intr.enabled_mask = 0;
444  subpriv->intr.active = 0;
445  s->async->inttrig = NULL;
446  nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
447  firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
448  switch_page(dev, asic, PAGE_ENAB);
449  for (port = firstport; port < firstport + nports; ++port) {
450  /* disable all intrs for this subdev.. */
451  outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
452  }
453 }
454 
455 static irqreturn_t interrupt_pcmuio(int irq, void *d)
456 {
457  int asic, got1 = 0;
458  struct comedi_device *dev = (struct comedi_device *)d;
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 
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  &&
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  if (!subpriv->intr.continuous && subpriv->intr.stop_count == 0) {
611  /* An empty acquisition! */
612  s->async->events |= COMEDI_CB_EOA;
613  subpriv->intr.active = 0;
614  return 1;
615  } else {
616  unsigned bits = 0, pol_bits = 0, n;
617  int nports, firstport, asic, port;
618  struct comedi_cmd *cmd = &s->async->cmd;
619 
620  asic = subpriv->intr.asic;
621  if (asic < 0)
622  return 1; /* not an interrupt
623  subdev */
624  subpriv->intr.enabled_mask = 0;
625  subpriv->intr.active = 1;
626  nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
627  firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
628  if (cmd->chanlist) {
629  for (n = 0; n < cmd->chanlist_len; n++) {
630  bits |= (1U << CR_CHAN(cmd->chanlist[n]));
631  pol_bits |= (CR_AREF(cmd->chanlist[n])
632  || CR_RANGE(cmd->
633  chanlist[n]) ? 1U : 0U)
634  << CR_CHAN(cmd->chanlist[n]);
635  }
636  }
637  bits &= ((0x1 << subpriv->intr.num_asic_chans) -
638  1) << subpriv->intr.first_chan;
639  subpriv->intr.enabled_mask = bits;
640 
641  switch_page(dev, asic, PAGE_ENAB);
642  for (port = firstport; port < firstport + nports; ++port) {
643  unsigned enab =
644  bits >> (subpriv->intr.first_chan + (port -
645  firstport) *
646  8) & 0xff, pol =
647  pol_bits >> (subpriv->intr.first_chan +
648  (port - firstport) * 8) & 0xff;
649  /* set enab intrs for this subdev.. */
650  outb(enab,
651  devpriv->asics[asic].iobase + REG_ENAB0 + port);
652  switch_page(dev, asic, PAGE_POL);
653  outb(pol,
654  devpriv->asics[asic].iobase + REG_ENAB0 + port);
655  }
656  }
657  return 0;
658 }
659 
660 static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
661 {
662  unsigned long flags;
663 
664  spin_lock_irqsave(&subpriv->intr.spinlock, flags);
665  if (subpriv->intr.active)
666  pcmuio_stop_intr(dev, s);
667  spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
668 
669  return 0;
670 }
671 
672 /*
673  * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
674  */
675 static int
676 pcmuio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
677  unsigned int trignum)
678 {
679  unsigned long flags;
680  int event = 0;
681 
682  if (trignum != 0)
683  return -EINVAL;
684 
685  spin_lock_irqsave(&subpriv->intr.spinlock, flags);
686  s->async->inttrig = NULL;
687  if (subpriv->intr.active)
688  event = pcmuio_start_intr(dev, s);
689 
690  spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
691 
692  if (event)
693  comedi_event(dev, s);
694 
695  return 1;
696 }
697 
698 /*
699  * 'do_cmd' function for an 'INTERRUPT' subdevice.
700  */
701 static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
702 {
703  struct comedi_cmd *cmd = &s->async->cmd;
704  unsigned long flags;
705  int event = 0;
706 
707  spin_lock_irqsave(&subpriv->intr.spinlock, flags);
708  subpriv->intr.active = 1;
709 
710  /* Set up end of acquisition. */
711  switch (cmd->stop_src) {
712  case TRIG_COUNT:
713  subpriv->intr.continuous = 0;
714  subpriv->intr.stop_count = cmd->stop_arg;
715  break;
716  default:
717  /* TRIG_NONE */
718  subpriv->intr.continuous = 1;
719  subpriv->intr.stop_count = 0;
720  break;
721  }
722 
723  /* Set up start of acquisition. */
724  switch (cmd->start_src) {
725  case TRIG_INT:
726  s->async->inttrig = pcmuio_inttrig_start_intr;
727  break;
728  default:
729  /* TRIG_NOW */
730  event = pcmuio_start_intr(dev, s);
731  break;
732  }
733  spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
734 
735  if (event)
736  comedi_event(dev, s);
737 
738  return 0;
739 }
740 
741 static int
742 pcmuio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
743  struct comedi_cmd *cmd)
744 {
745  return comedi_pcm_cmdtest(dev, s, cmd);
746 }
747 
748 static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
749 {
750  const struct pcmuio_board *board = comedi_board(dev);
751  struct comedi_subdevice *s;
752  int sdev_no, chans_left, n_subdevs, port, asic, thisasic_chanct = 0;
753  unsigned long iobase;
754  unsigned int irq[MAX_ASICS];
755  int ret;
756 
757  iobase = it->options[0];
758  irq[0] = it->options[1];
759  irq[1] = it->options[2];
760 
761  dev_dbg(dev->class_dev, "%s: io: %lx attach\n",
762  dev->driver->driver_name, iobase);
763 
764  dev->iobase = iobase;
765 
766  if (!iobase || !request_region(iobase,
767  board->num_asics * ASIC_IOSIZE,
768  dev->driver->driver_name)) {
769  dev_err(dev->class_dev, "I/O port conflict\n");
770  return -EIO;
771  }
772 
773  dev->board_name = board->name;
774 
775 /*
776  * Allocate the private structure area. alloc_private() is a
777  * convenient macro defined in comedidev.h.
778  */
779  if (alloc_private(dev, sizeof(struct pcmuio_private)) < 0) {
780  dev_warn(dev->class_dev,
781  "cannot allocate private data structure\n");
782  return -ENOMEM;
783  }
784 
785  for (asic = 0; asic < MAX_ASICS; ++asic) {
786  devpriv->asics[asic].num = asic;
787  devpriv->asics[asic].iobase = dev->iobase + asic * ASIC_IOSIZE;
788  devpriv->asics[asic].irq = 0; /* this gets actually set at the end of
789  this function when we
790  request_irqs */
791  spin_lock_init(&devpriv->asics[asic].spinlock);
792  }
793 
794  chans_left = CHANS_PER_ASIC * board->num_asics;
795  n_subdevs = CALC_N_SUBDEVS(chans_left);
796  devpriv->sprivs =
797  kcalloc(n_subdevs, sizeof(struct pcmuio_subdev_private),
798  GFP_KERNEL);
799  if (!devpriv->sprivs) {
800  dev_warn(dev->class_dev,
801  "cannot allocate subdevice private data structures\n");
802  return -ENOMEM;
803  }
804 
805  ret = comedi_alloc_subdevices(dev, n_subdevs);
806  if (ret)
807  return ret;
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;
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;
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,
877  IRQF_SHARED, board->name, dev)) {
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->class_dev, "irq: %u\n", irq[0]);
894  if (irq[1] && board->num_asics == 2)
895  dev_dbg(dev->class_dev, "second ASIC irq: %u\n",
896  irq[1]);
897  } else {
898  dev_dbg(dev->class_dev, "(IRQ mode disabled)\n");
899  }
900 
901 
902  return 1;
903 }
904 
905 static void pcmuio_detach(struct comedi_device *dev)
906 {
907  const struct pcmuio_board *board = comedi_board(dev);
908  int i;
909 
910  if (dev->iobase)
911  release_region(dev->iobase, ASIC_IOSIZE * board->num_asics);
912  for (i = 0; i < MAX_ASICS; ++i) {
913  if (devpriv->asics[i].irq)
914  free_irq(devpriv->asics[i].irq, dev);
915  }
916  if (devpriv && devpriv->sprivs)
917  kfree(devpriv->sprivs);
918 }
919 
920 static const struct pcmuio_board pcmuio_boards[] = {
921  {
922  .name = "pcmuio48",
923  .num_asics = 1,
924  .num_ports = 6,
925  }, {
926  .name = "pcmuio96",
927  .num_asics = 2,
928  .num_ports = 12,
929  },
930 };
931 
932 static struct comedi_driver pcmuio_driver = {
933  .driver_name = "pcmuio",
934  .module = THIS_MODULE,
935  .attach = pcmuio_attach,
936  .detach = pcmuio_detach,
937  .board_name = &pcmuio_boards[0].name,
938  .offset = sizeof(struct pcmuio_board),
939  .num_names = ARRAY_SIZE(pcmuio_boards),
940 };
941 module_comedi_driver(pcmuio_driver);
942 
943 MODULE_AUTHOR("Comedi http://www.comedi.org");
944 MODULE_DESCRIPTION("Comedi low-level driver");
945 MODULE_LICENSE("GPL");