Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
daqboard2000.c
Go to the documentation of this file.
1 /*
2  comedi/drivers/daqboard2000.c
3  hardware driver for IOtech DAQboard/2000
4 
5  COMEDI - Linux Control and Measurement Device Interface
6  Copyright (C) 1999 Anders Blomdell <[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 /*
24 Driver: daqboard2000
25 Description: IOTech DAQBoard/2000
26 Author: Anders Blomdell <[email protected]>
27 Status: works
28 Updated: Mon, 14 Apr 2008 15:28:52 +0100
29 Devices: [IOTech] DAQBoard/2000 (daqboard2000)
30 
31 Much of the functionality of this driver was determined from reading
32 the source code for the Windows driver.
33 
34 The FPGA on the board requires fimware, which is available from
35 http://www.comedi.org in the comedi_nonfree_firmware tarball.
36 
37 Configuration options: not applicable, uses PCI auto config
38 */
39 /*
40  This card was obviously never intended to leave the Windows world,
41  since it lacked all kind of hardware documentation (except for cable
42  pinouts, plug and pray has something to catch up with yet).
43 
44  With some help from our swedish distributor, we got the Windows sourcecode
45  for the card, and here are the findings so far.
46 
47  1. A good document that describes the PCI interface chip is 9080db-106.pdf
48  available from http://www.plxtech.com/products/io/pci9080
49 
50  2. The initialization done so far is:
51  a. program the FPGA (windows code sans a lot of error messages)
52  b.
53 
54  3. Analog out seems to work OK with DAC's disabled, if DAC's are enabled,
55  you have to output values to all enabled DAC's until result appears, I
56  guess that it has something to do with pacer clocks, but the source
57  gives me no clues. I'll keep it simple so far.
58 
59  4. Analog in.
60  Each channel in the scanlist seems to be controlled by four
61  control words:
62 
63  Word0:
64  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
65  ! | | | ! | | | ! | | | ! | | | !
66  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
67 
68  Word1:
69  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
70  ! | | | ! | | | ! | | | ! | | | !
71  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
72  | | | | | | |
73  +------+------+ | | | | +-- Digital input (??)
74  | | | | +---- 10 us settling time
75  | | | +------ Suspend acquisition (last to scan)
76  | | +-------- Simultaneous sample and hold
77  | +---------- Signed data format
78  +------------------------- Correction offset low
79 
80  Word2:
81  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
82  ! | | | ! | | | ! | | | ! | | | !
83  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
84  | | | | | | | | | |
85  +-----+ +--+--+ +++ +++ +--+--+
86  | | | | +----- Expansion channel
87  | | | +----------- Expansion gain
88  | | +--------------- Channel (low)
89  | +--------------------- Correction offset high
90  +----------------------------- Correction gain low
91  Word3:
92  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
93  ! | | | ! | | | ! | | | ! | | | !
94  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
95  | | | | | | | | |
96  +------+------+ | | +-+-+ | | +-- Low bank enable
97  | | | | | +---- High bank enable
98  | | | | +------ Hi/low select
99  | | | +---------- Gain (1,?,2,4,8,16,32,64)
100  | | +-------------- differential/single ended
101  | +---------------- Unipolar
102  +------------------------- Correction gain high
103 
104  999. The card seems to have an incredible amount of capabilities, but
105  trying to reverse engineer them from the Windows source is beyond my
106  patience.
107 
108  */
109 
110 #include "../comedidev.h"
111 
112 #include <linux/delay.h>
113 #include <linux/interrupt.h>
114 #include <linux/firmware.h>
115 
116 #include "8255.h"
117 
118 #define DAQBOARD2000_FIRMWARE "daqboard2000_firmware.bin"
119 
120 #define PCI_VENDOR_ID_IOTECH 0x1616
121 
122 #define DAQBOARD2000_SUBSYSTEM_IDS2 0x0002 /* Daqboard/2000 - 2 Dacs */
123 #define DAQBOARD2000_SUBSYSTEM_IDS4 0x0004 /* Daqboard/2000 - 4 Dacs */
124 
125 /* Initialization bits for the Serial EEPROM Control Register */
126 #define DAQBOARD2000_SECRProgPinHi 0x8001767e
127 #define DAQBOARD2000_SECRProgPinLo 0x8000767e
128 #define DAQBOARD2000_SECRLocalBusHi 0xc000767e
129 #define DAQBOARD2000_SECRLocalBusLo 0x8000767e
130 #define DAQBOARD2000_SECRReloadHi 0xa000767e
131 #define DAQBOARD2000_SECRReloadLo 0x8000767e
132 
133 /* SECR status bits */
134 #define DAQBOARD2000_EEPROM_PRESENT 0x10000000
135 
136 /* CPLD status bits */
137 #define DAQBOARD2000_CPLD_INIT 0x0002
138 #define DAQBOARD2000_CPLD_DONE 0x0004
139 
140 static const struct comedi_lrange range_daqboard2000_ai = {
141  13, {
142  BIP_RANGE(10),
143  BIP_RANGE(5),
144  BIP_RANGE(2.5),
145  BIP_RANGE(1.25),
146  BIP_RANGE(0.625),
147  BIP_RANGE(0.3125),
148  BIP_RANGE(0.156),
149  UNI_RANGE(10),
150  UNI_RANGE(5),
151  UNI_RANGE(2.5),
152  UNI_RANGE(1.25),
153  UNI_RANGE(0.625),
154  UNI_RANGE(0.3125)
155  }
156 };
157 
158 /*
159  * Register Memory Map
160  */
161 #define acqControl 0x00 /* u16 */
162 #define acqScanListFIFO 0x02 /* u16 */
163 #define acqPacerClockDivLow 0x04 /* u32 */
164 #define acqScanCounter 0x08 /* u16 */
165 #define acqPacerClockDivHigh 0x0a /* u16 */
166 #define acqTriggerCount 0x0c /* u16 */
167 #define acqResultsFIFO 0x10 /* u16 */
168 #define acqResultsShadow 0x14 /* u16 */
169 #define acqAdcResult 0x18 /* u16 */
170 #define dacScanCounter 0x1c /* u16 */
171 #define dacControl 0x20 /* u16 */
172 #define dacFIFO 0x24 /* s16 */
173 #define dacPacerClockDiv 0x2a /* u16 */
174 #define refDacs 0x2c /* u16 */
175 #define dioControl 0x30 /* u16 */
176 #define dioP3hsioData 0x32 /* s16 */
177 #define dioP3Control 0x34 /* u16 */
178 #define calEepromControl 0x36 /* u16 */
179 #define dacSetting(x) (0x38 + (x)*2) /* s16 */
180 #define dioP2ExpansionIO8Bit 0x40 /* s16 */
181 #define ctrTmrControl 0x80 /* u16 */
182 #define ctrInput(x) (0x88 + (x)*2) /* s16 */
183 #define timerDivisor(x) (0xa0 + (x)*2) /* u16 */
184 #define dmaControl 0xb0 /* u16 */
185 #define trigControl 0xb2 /* u16 */
186 #define calEeprom 0xb8 /* u16 */
187 #define acqDigitalMark 0xba /* u16 */
188 #define trigDacs 0xbc /* u16 */
189 #define dioP2ExpansionIO16Bit(x) (0xc0 + (x)*2) /* s16 */
190 
191 /* Scan Sequencer programming */
192 #define DAQBOARD2000_SeqStartScanList 0x0011
193 #define DAQBOARD2000_SeqStopScanList 0x0010
194 
195 /* Prepare for acquisition */
196 #define DAQBOARD2000_AcqResetScanListFifo 0x0004
197 #define DAQBOARD2000_AcqResetResultsFifo 0x0002
198 #define DAQBOARD2000_AcqResetConfigPipe 0x0001
199 
200 /* Acqusition status bits */
201 #define DAQBOARD2000_AcqResultsFIFOMore1Sample 0x0001
202 #define DAQBOARD2000_AcqResultsFIFOHasValidData 0x0002
203 #define DAQBOARD2000_AcqResultsFIFOOverrun 0x0004
204 #define DAQBOARD2000_AcqLogicScanning 0x0008
205 #define DAQBOARD2000_AcqConfigPipeFull 0x0010
206 #define DAQBOARD2000_AcqScanListFIFOEmpty 0x0020
207 #define DAQBOARD2000_AcqAdcNotReady 0x0040
208 #define DAQBOARD2000_ArbitrationFailure 0x0080
209 #define DAQBOARD2000_AcqPacerOverrun 0x0100
210 #define DAQBOARD2000_DacPacerOverrun 0x0200
211 #define DAQBOARD2000_AcqHardwareError 0x01c0
212 
213 /* Scan Sequencer programming */
214 #define DAQBOARD2000_SeqStartScanList 0x0011
215 #define DAQBOARD2000_SeqStopScanList 0x0010
216 
217 /* Pacer Clock Control */
218 #define DAQBOARD2000_AdcPacerInternal 0x0030
219 #define DAQBOARD2000_AdcPacerExternal 0x0032
220 #define DAQBOARD2000_AdcPacerEnable 0x0031
221 #define DAQBOARD2000_AdcPacerEnableDacPacer 0x0034
222 #define DAQBOARD2000_AdcPacerDisable 0x0030
223 #define DAQBOARD2000_AdcPacerNormalMode 0x0060
224 #define DAQBOARD2000_AdcPacerCompatibilityMode 0x0061
225 #define DAQBOARD2000_AdcPacerInternalOutEnable 0x0008
226 #define DAQBOARD2000_AdcPacerExternalRising 0x0100
227 
228 /* DAC status */
229 #define DAQBOARD2000_DacFull 0x0001
230 #define DAQBOARD2000_RefBusy 0x0002
231 #define DAQBOARD2000_TrgBusy 0x0004
232 #define DAQBOARD2000_CalBusy 0x0008
233 #define DAQBOARD2000_Dac0Busy 0x0010
234 #define DAQBOARD2000_Dac1Busy 0x0020
235 #define DAQBOARD2000_Dac2Busy 0x0040
236 #define DAQBOARD2000_Dac3Busy 0x0080
237 
238 /* DAC control */
239 #define DAQBOARD2000_Dac0Enable 0x0021
240 #define DAQBOARD2000_Dac1Enable 0x0031
241 #define DAQBOARD2000_Dac2Enable 0x0041
242 #define DAQBOARD2000_Dac3Enable 0x0051
243 #define DAQBOARD2000_DacEnableBit 0x0001
244 #define DAQBOARD2000_Dac0Disable 0x0020
245 #define DAQBOARD2000_Dac1Disable 0x0030
246 #define DAQBOARD2000_Dac2Disable 0x0040
247 #define DAQBOARD2000_Dac3Disable 0x0050
248 #define DAQBOARD2000_DacResetFifo 0x0004
249 #define DAQBOARD2000_DacPatternDisable 0x0060
250 #define DAQBOARD2000_DacPatternEnable 0x0061
251 #define DAQBOARD2000_DacSelectSignedData 0x0002
252 #define DAQBOARD2000_DacSelectUnsignedData 0x0000
253 
254 /* Trigger Control */
255 #define DAQBOARD2000_TrigAnalog 0x0000
256 #define DAQBOARD2000_TrigTTL 0x0010
257 #define DAQBOARD2000_TrigTransHiLo 0x0004
258 #define DAQBOARD2000_TrigTransLoHi 0x0000
259 #define DAQBOARD2000_TrigAbove 0x0000
260 #define DAQBOARD2000_TrigBelow 0x0004
261 #define DAQBOARD2000_TrigLevelSense 0x0002
262 #define DAQBOARD2000_TrigEdgeSense 0x0000
263 #define DAQBOARD2000_TrigEnable 0x0001
264 #define DAQBOARD2000_TrigDisable 0x0000
265 
266 /* Reference Dac Selection */
267 #define DAQBOARD2000_PosRefDacSelect 0x0100
268 #define DAQBOARD2000_NegRefDacSelect 0x0000
269 
271  const char *name;
272  int id;
273 };
274 static const struct daq200_boardtype boardtypes[] = {
275  {"ids2", DAQBOARD2000_SUBSYSTEM_IDS2},
276  {"ids4", DAQBOARD2000_SUBSYSTEM_IDS4},
277 };
278 
280  enum {
282  } card;
283  void __iomem *daq;
284  void __iomem *plx;
285  unsigned int ao_readback[2];
286 };
287 
288 static void writeAcqScanListEntry(struct comedi_device *dev, u16 entry)
289 {
290  struct daqboard2000_private *devpriv = dev->private;
291 
292  /* udelay(4); */
293  writew(entry & 0x00ff, devpriv->daq + acqScanListFIFO);
294  /* udelay(4); */
295  writew((entry >> 8) & 0x00ff, devpriv->daq + acqScanListFIFO);
296 }
297 
298 static void setup_sampling(struct comedi_device *dev, int chan, int gain)
299 {
300  u16 word0, word1, word2, word3;
301 
302  /* Channel 0-7 diff, channel 8-23 single ended */
303  word0 = 0;
304  word1 = 0x0004; /* Last scan */
305  word2 = (chan << 6) & 0x00c0;
306  switch (chan / 4) {
307  case 0:
308  word3 = 0x0001;
309  break;
310  case 1:
311  word3 = 0x0002;
312  break;
313  case 2:
314  word3 = 0x0005;
315  break;
316  case 3:
317  word3 = 0x0006;
318  break;
319  case 4:
320  word3 = 0x0041;
321  break;
322  case 5:
323  word3 = 0x0042;
324  break;
325  default:
326  word3 = 0;
327  break;
328  }
329 /*
330  dev->eeprom.correctionDACSE[i][j][k].offset = 0x800;
331  dev->eeprom.correctionDACSE[i][j][k].gain = 0xc00;
332 */
333  /* These should be read from EEPROM */
334  word2 |= 0x0800;
335  word3 |= 0xc000;
336  writeAcqScanListEntry(dev, word0);
337  writeAcqScanListEntry(dev, word1);
338  writeAcqScanListEntry(dev, word2);
339  writeAcqScanListEntry(dev, word3);
340 }
341 
342 static int daqboard2000_ai_insn_read(struct comedi_device *dev,
343  struct comedi_subdevice *s,
344  struct comedi_insn *insn,
345  unsigned int *data)
346 {
347  struct daqboard2000_private *devpriv = dev->private;
348  unsigned int val;
349  int gain, chan, timeout;
350  int i;
351 
355 
356  /*
357  * If pacer clock is not set to some high value (> 10 us), we
358  * risk multiple samples to be put into the result FIFO.
359  */
360  /* 1 second, should be long enough */
361  writel(1000000, devpriv->daq + acqPacerClockDivLow);
362  writew(0, devpriv->daq + acqPacerClockDivHigh);
363 
364  gain = CR_RANGE(insn->chanspec);
365  chan = CR_CHAN(insn->chanspec);
366 
367  /* This doesn't look efficient. I decided to take the conservative
368  * approach when I did the insn conversion. Perhaps it would be
369  * better to have broken it completely, then someone would have been
370  * forced to fix it. --ds */
371  for (i = 0; i < insn->n; i++) {
372  setup_sampling(dev, chan, gain);
373  /* Enable reading from the scanlist FIFO */
375  devpriv->daq + acqControl);
376  for (timeout = 0; timeout < 20; timeout++) {
377  val = readw(devpriv->daq + acqControl);
379  break;
380  /* udelay(2); */
381  }
383  for (timeout = 0; timeout < 20; timeout++) {
384  val = readw(devpriv->daq + acqControl);
386  break;
387  /* udelay(2); */
388  }
389  for (timeout = 0; timeout < 20; timeout++) {
390  val = readw(devpriv->daq + acqControl);
392  break;
393  /* udelay(2); */
394  }
395  data[i] = readw(devpriv->daq + acqResultsFIFO);
398  }
399 
400  return i;
401 }
402 
403 static int daqboard2000_ao_insn_read(struct comedi_device *dev,
404  struct comedi_subdevice *s,
405  struct comedi_insn *insn,
406  unsigned int *data)
407 {
408  struct daqboard2000_private *devpriv = dev->private;
409  int chan = CR_CHAN(insn->chanspec);
410  int i;
411 
412  for (i = 0; i < insn->n; i++)
413  data[i] = devpriv->ao_readback[chan];
414 
415  return i;
416 }
417 
418 static int daqboard2000_ao_insn_write(struct comedi_device *dev,
419  struct comedi_subdevice *s,
420  struct comedi_insn *insn,
421  unsigned int *data)
422 {
423  struct daqboard2000_private *devpriv = dev->private;
424  int chan = CR_CHAN(insn->chanspec);
425  unsigned int val;
426  int timeout;
427  int i;
428 
429  for (i = 0; i < insn->n; i++) {
430 #if 0
431  /*
432  * OK, since it works OK without enabling the DAC's,
433  * let's keep it as simple as possible...
434  */
435  writew((chan + 2) * 0x0010 | 0x0001,
436  devpriv->daq + dacControl);
437  udelay(1000);
438 #endif
439  writew(data[i], devpriv->daq + dacSetting(chan));
440  for (timeout = 0; timeout < 20; timeout++) {
441  val = readw(devpriv->daq + dacControl);
442  if ((val & ((chan + 1) * 0x0010)) == 0)
443  break;
444  /* udelay(2); */
445  }
446  devpriv->ao_readback[chan] = data[i];
447 #if 0
448  /*
449  * Since we never enabled the DAC's, we don't need
450  * to disable it...
451  */
452  writew((chan + 2) * 0x0010 | 0x0000,
453  devpriv->daq + dacControl);
454  udelay(1000);
455 #endif
456  }
457 
458  return i;
459 }
460 
461 static void daqboard2000_resetLocalBus(struct comedi_device *dev)
462 {
463  struct daqboard2000_private *devpriv = dev->private;
464 
465  writel(DAQBOARD2000_SECRLocalBusHi, devpriv->plx + 0x6c);
466  udelay(10000);
467  writel(DAQBOARD2000_SECRLocalBusLo, devpriv->plx + 0x6c);
468  udelay(10000);
469 }
470 
471 static void daqboard2000_reloadPLX(struct comedi_device *dev)
472 {
473  struct daqboard2000_private *devpriv = dev->private;
474 
475  writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
476  udelay(10000);
477  writel(DAQBOARD2000_SECRReloadHi, devpriv->plx + 0x6c);
478  udelay(10000);
479  writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
480  udelay(10000);
481 }
482 
483 static void daqboard2000_pulseProgPin(struct comedi_device *dev)
484 {
485  struct daqboard2000_private *devpriv = dev->private;
486 
487  writel(DAQBOARD2000_SECRProgPinHi, devpriv->plx + 0x6c);
488  udelay(10000);
489  writel(DAQBOARD2000_SECRProgPinLo, devpriv->plx + 0x6c);
490  udelay(10000); /* Not in the original code, but I like symmetry... */
491 }
492 
493 static int daqboard2000_pollCPLD(struct comedi_device *dev, int mask)
494 {
495  struct daqboard2000_private *devpriv = dev->private;
496  int result = 0;
497  int i;
498  int cpld;
499 
500  /* timeout after 50 tries -> 5ms */
501  for (i = 0; i < 50; i++) {
502  cpld = readw(devpriv->daq + 0x1000);
503  if ((cpld & mask) == mask) {
504  result = 1;
505  break;
506  }
507  udelay(100);
508  }
509  udelay(5);
510  return result;
511 }
512 
513 static int daqboard2000_writeCPLD(struct comedi_device *dev, int data)
514 {
515  struct daqboard2000_private *devpriv = dev->private;
516  int result = 0;
517 
518  udelay(10);
519  writew(data, devpriv->daq + 0x1000);
520  if ((readw(devpriv->daq + 0x1000) & DAQBOARD2000_CPLD_INIT) ==
521  DAQBOARD2000_CPLD_INIT) {
522  result = 1;
523  }
524  return result;
525 }
526 
527 static int initialize_daqboard2000(struct comedi_device *dev,
528  const u8 *cpld_array, size_t len)
529 {
530  struct daqboard2000_private *devpriv = dev->private;
531  int result = -EIO;
532  /* Read the serial EEPROM control register */
533  int secr;
534  int retry;
535  size_t i;
536 
537  /* Check to make sure the serial eeprom is present on the board */
538  secr = readl(devpriv->plx + 0x6c);
539  if (!(secr & DAQBOARD2000_EEPROM_PRESENT))
540  return -EIO;
541 
542  for (retry = 0; retry < 3; retry++) {
543  daqboard2000_resetLocalBus(dev);
544  daqboard2000_reloadPLX(dev);
545  daqboard2000_pulseProgPin(dev);
546  if (daqboard2000_pollCPLD(dev, DAQBOARD2000_CPLD_INIT)) {
547  for (i = 0; i < len; i++) {
548  if (cpld_array[i] == 0xff &&
549  cpld_array[i + 1] == 0x20)
550  break;
551  }
552  for (; i < len; i += 2) {
553  int data =
554  (cpld_array[i] << 8) + cpld_array[i + 1];
555  if (!daqboard2000_writeCPLD(dev, data))
556  break;
557  }
558  if (i >= len) {
559  daqboard2000_resetLocalBus(dev);
560  daqboard2000_reloadPLX(dev);
561  result = 0;
562  break;
563  }
564  }
565  }
566  return result;
567 }
568 
569 static int daqboard2000_upload_firmware(struct comedi_device *dev)
570 {
571  struct pci_dev *pcidev = comedi_to_pci_dev(dev);
572  const struct firmware *fw;
573  int ret;
574 
575  ret = request_firmware(&fw, DAQBOARD2000_FIRMWARE, &pcidev->dev);
576  if (ret)
577  return ret;
578 
579  ret = initialize_daqboard2000(dev, fw->data, fw->size);
580  release_firmware(fw);
581 
582  return ret;
583 }
584 
585 static void daqboard2000_adcStopDmaTransfer(struct comedi_device *dev)
586 {
587 }
588 
589 static void daqboard2000_adcDisarm(struct comedi_device *dev)
590 {
591  struct daqboard2000_private *devpriv = dev->private;
592 
593  /* Disable hardware triggers */
594  udelay(2);
596  devpriv->daq + trigControl);
597  udelay(2);
599  devpriv->daq + trigControl);
600 
601  /* Stop the scan list FIFO from loading the configuration pipe */
602  udelay(2);
604 
605  /* Stop the pacer clock */
606  udelay(2);
608 
609  /* Stop the input dma (abort channel 1) */
610  daqboard2000_adcStopDmaTransfer(dev);
611 }
612 
613 static void daqboard2000_activateReferenceDacs(struct comedi_device *dev)
614 {
615  struct daqboard2000_private *devpriv = dev->private;
616  unsigned int val;
617  int timeout;
618 
619  /* Set the + reference dac value in the FPGA */
620  writew(0x80 | DAQBOARD2000_PosRefDacSelect, devpriv->daq + refDacs);
621  for (timeout = 0; timeout < 20; timeout++) {
622  val = readw(devpriv->daq + dacControl);
623  if ((val & DAQBOARD2000_RefBusy) == 0)
624  break;
625  udelay(2);
626  }
627 
628  /* Set the - reference dac value in the FPGA */
629  writew(0x80 | DAQBOARD2000_NegRefDacSelect, devpriv->daq + refDacs);
630  for (timeout = 0; timeout < 20; timeout++) {
631  val = readw(devpriv->daq + dacControl);
632  if ((val & DAQBOARD2000_RefBusy) == 0)
633  break;
634  udelay(2);
635  }
636 }
637 
638 static void daqboard2000_initializeCtrs(struct comedi_device *dev)
639 {
640 }
641 
642 static void daqboard2000_initializeTmrs(struct comedi_device *dev)
643 {
644 }
645 
646 static void daqboard2000_dacDisarm(struct comedi_device *dev)
647 {
648 }
649 
650 static void daqboard2000_initializeAdc(struct comedi_device *dev)
651 {
652  daqboard2000_adcDisarm(dev);
653  daqboard2000_activateReferenceDacs(dev);
654  daqboard2000_initializeCtrs(dev);
655  daqboard2000_initializeTmrs(dev);
656 }
657 
658 static void daqboard2000_initializeDac(struct comedi_device *dev)
659 {
660  daqboard2000_dacDisarm(dev);
661 }
662 
663 static int daqboard2000_8255_cb(int dir, int port, int data,
664  unsigned long ioaddr)
665 {
666  void __iomem *mmio_base = (void __iomem *)ioaddr;
667 
668  if (dir) {
669  writew(data, mmio_base + port * 2);
670  return 0;
671  } else {
672  return readw(mmio_base + port * 2);
673  }
674 }
675 
676 static const void *daqboard2000_find_boardinfo(struct comedi_device *dev,
677  struct pci_dev *pcidev)
678 {
679  const struct daq200_boardtype *board;
680  int i;
681 
682  if (pcidev->subsystem_device != PCI_VENDOR_ID_IOTECH)
683  return NULL;
684 
685  for (i = 0; i < ARRAY_SIZE(boardtypes); i++) {
686  board = &boardtypes[i];
687  if (pcidev->subsystem_device == board->id)
688  return board;
689  }
690  return NULL;
691 }
692 
693 static int daqboard2000_attach_pci(struct comedi_device *dev,
694  struct pci_dev *pcidev)
695 {
696  const struct daq200_boardtype *board;
698  struct comedi_subdevice *s;
699  int result;
700 
701  comedi_set_hw_dev(dev, &pcidev->dev);
702 
703  board = daqboard2000_find_boardinfo(dev, pcidev);
704  if (!board)
705  return -ENODEV;
706  dev->board_ptr = board;
707  dev->board_name = board->name;
708 
709  result = alloc_private(dev, sizeof(*devpriv));
710  if (result < 0)
711  return -ENOMEM;
712  devpriv = dev->private;
713 
714  result = comedi_pci_enable(pcidev, dev->driver->driver_name);
715  if (result < 0)
716  return result;
717  dev->iobase = 1; /* the "detach" needs this */
718 
719  devpriv->plx = ioremap(pci_resource_start(pcidev, 0),
720  pci_resource_len(pcidev, 0));
721  devpriv->daq = ioremap(pci_resource_start(pcidev, 2),
722  pci_resource_len(pcidev, 2));
723  if (!devpriv->plx || !devpriv->daq)
724  return -ENOMEM;
725 
726  result = comedi_alloc_subdevices(dev, 3);
727  if (result)
728  return result;
729 
730  readl(devpriv->plx + 0x6c);
731 
732  result = daqboard2000_upload_firmware(dev);
733  if (result < 0)
734  return result;
735 
736  daqboard2000_initializeAdc(dev);
737  daqboard2000_initializeDac(dev);
738 
739  s = &dev->subdevices[0];
740  /* ai subdevice */
741  s->type = COMEDI_SUBD_AI;
743  s->n_chan = 24;
744  s->maxdata = 0xffff;
745  s->insn_read = daqboard2000_ai_insn_read;
746  s->range_table = &range_daqboard2000_ai;
747 
748  s = &dev->subdevices[1];
749  /* ao subdevice */
750  s->type = COMEDI_SUBD_AO;
752  s->n_chan = 2;
753  s->maxdata = 0xffff;
754  s->insn_read = daqboard2000_ao_insn_read;
755  s->insn_write = daqboard2000_ao_insn_write;
757 
758  s = &dev->subdevices[2];
759  result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
760  (unsigned long)(devpriv->daq + dioP2ExpansionIO8Bit));
761  if (result)
762  return result;
763 
764  dev_info(dev->class_dev, "%s: %s attached\n",
765  dev->driver->driver_name, dev->board_name);
766 
767  return 0;
768 }
769 
770 static void daqboard2000_detach(struct comedi_device *dev)
771 {
772  struct pci_dev *pcidev = comedi_to_pci_dev(dev);
773  struct daqboard2000_private *devpriv = dev->private;
774 
775  if (dev->subdevices)
776  subdev_8255_cleanup(dev, &dev->subdevices[2]);
777  if (dev->irq)
778  free_irq(dev->irq, dev);
779  if (devpriv) {
780  if (devpriv->daq)
781  iounmap(devpriv->daq);
782  if (devpriv->plx)
783  iounmap(devpriv->plx);
784  }
785  if (pcidev) {
786  if (dev->iobase)
787  comedi_pci_disable(pcidev);
788  pci_dev_put(pcidev);
789  }
790 }
791 
792 static struct comedi_driver daqboard2000_driver = {
793  .driver_name = "daqboard2000",
794  .module = THIS_MODULE,
795  .attach_pci = daqboard2000_attach_pci,
796  .detach = daqboard2000_detach,
797 };
798 
799 static int __devinit daqboard2000_pci_probe(struct pci_dev *dev,
800  const struct pci_device_id *ent)
801 {
802  return comedi_pci_auto_config(dev, &daqboard2000_driver);
803 }
804 
805 static void __devexit daqboard2000_pci_remove(struct pci_dev *dev)
806 {
808 }
809 
810 static DEFINE_PCI_DEVICE_TABLE(daqboard2000_pci_table) = {
811  { PCI_DEVICE(PCI_VENDOR_ID_IOTECH, 0x0409) },
812  { 0 }
813 };
814 MODULE_DEVICE_TABLE(pci, daqboard2000_pci_table);
815 
816 static struct pci_driver daqboard2000_pci_driver = {
817  .name = "daqboard2000",
818  .id_table = daqboard2000_pci_table,
819  .probe = daqboard2000_pci_probe,
820  .remove = __devexit_p(daqboard2000_pci_remove),
821 };
822 module_comedi_pci_driver(daqboard2000_driver, daqboard2000_pci_driver);
823 
824 MODULE_AUTHOR("Comedi http://www.comedi.org");
825 MODULE_DESCRIPTION("Comedi low-level driver");
826 MODULE_LICENSE("GPL");