]>
Commit | Line | Data |
---|---|---|
f4bd8abb RB |
1 | /* |
2 | comedi/drivers/cb_pcimdas.c | |
3 | Comedi driver for Computer Boards PCIM-DAS1602/16 | |
4 | ||
5 | COMEDI - Linux Control and Measurement Device Interface | |
6 | Copyright (C) 2000 David A. Schleef <ds@schleef.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 | /* | |
24 | Driver: cb_pcimdas | |
25 | Description: Measurement Computing PCI Migration series boards | |
26 | Devices: [ComputerBoards] PCIM-DAS1602/16 (cb_pcimdas) | |
27 | Author: Richard Bytheway | |
28 | Updated: Wed, 13 Nov 2002 12:34:56 +0000 | |
29 | Status: experimental | |
30 | ||
31 | Written to support the PCIM-DAS1602/16 on a 2.4 series kernel. | |
32 | ||
33 | Configuration Options: | |
34 | [0] - PCI bus number | |
35 | [1] - PCI slot number | |
36 | ||
37 | Developed from cb_pcidas and skel by Richard Bytheway (mocelet@sucs.org). | |
38 | Only supports DIO, AO and simple AI in it's present form. | |
39 | No interrupts, multi channel or FIFO AI, although the card looks like it could support this. | |
40 | See http://www.measurementcomputing.com/PDFManuals/pcim-das1602_16.pdf for more details. | |
41 | */ | |
42 | ||
43 | #include "../comedidev.h" | |
44 | ||
45 | #include <linux/delay.h> | |
46 | ||
47 | #include "comedi_pci.h" | |
48 | #include "plx9052.h" | |
49 | #include "8255.h" | |
50 | ||
51 | //#define CBPCIMDAS_DEBUG | |
52 | #undef CBPCIMDAS_DEBUG | |
53 | ||
54 | /* Registers for the PCIM-DAS1602/16 */ | |
55 | ||
56 | // sizes of io regions (bytes) | |
57 | #define BADR0_SIZE 2 //?? | |
58 | #define BADR1_SIZE 4 | |
59 | #define BADR2_SIZE 6 | |
60 | #define BADR3_SIZE 16 | |
61 | #define BADR4_SIZE 4 | |
62 | ||
63 | //DAC Offsets | |
64 | #define ADC_TRIG 0 | |
65 | #define DAC0_OFFSET 2 | |
66 | #define DAC1_OFFSET 4 | |
67 | ||
68 | //AI and Counter Constants | |
69 | #define MUX_LIMITS 0 | |
70 | #define MAIN_CONN_DIO 1 | |
71 | #define ADC_STAT 2 | |
72 | #define ADC_CONV_STAT 3 | |
73 | #define ADC_INT 4 | |
74 | #define ADC_PACER 5 | |
75 | #define BURST_MODE 6 | |
76 | #define PROG_GAIN 7 | |
77 | #define CLK8254_1_DATA 8 | |
78 | #define CLK8254_2_DATA 9 | |
79 | #define CLK8254_3_DATA 10 | |
80 | #define CLK8254_CONTROL 11 | |
81 | #define USER_COUNTER 12 | |
82 | #define RESID_COUNT_H 13 | |
83 | #define RESID_COUNT_L 14 | |
84 | ||
85 | /* Board description */ | |
86 | typedef struct cb_pcimdas_board_struct { | |
87 | const char *name; | |
88 | unsigned short device_id; | |
89 | int ai_se_chans; // Inputs in single-ended mode | |
90 | int ai_diff_chans; // Inputs in differential mode | |
91 | int ai_bits; // analog input resolution | |
92 | int ai_speed; // fastest conversion period in ns | |
93 | int ao_nchan; // number of analog out channels | |
94 | int ao_bits; // analogue output resolution | |
95 | int has_ao_fifo; // analog output has fifo | |
96 | int ao_scan_speed; // analog output speed for 1602 series (for a scan, not conversion) | |
97 | int fifo_size; // number of samples fifo can hold | |
98 | int dio_bits; // number of dio bits | |
99 | int has_dio; // has DIO | |
100 | const comedi_lrange *ranges; | |
101 | } cb_pcimdas_board; | |
102 | ||
103 | static const cb_pcimdas_board cb_pcimdas_boards[] = { | |
104 | { | |
105 | name: "PCIM-DAS1602/16", | |
106 | device_id:0x56, | |
107 | ai_se_chans:16, | |
108 | ai_diff_chans:8, | |
109 | ai_bits: 16, | |
110 | ai_speed:10000, //?? | |
111 | ao_nchan:2, | |
112 | ao_bits: 12, | |
113 | has_ao_fifo:0, //?? | |
114 | ao_scan_speed:10000, | |
115 | //?? | |
116 | fifo_size:1024, | |
117 | dio_bits:24, | |
118 | has_dio: 1, | |
119 | // ranges: &cb_pcimdas_ranges, | |
120 | }, | |
121 | }; | |
122 | ||
123 | /* This is used by modprobe to translate PCI IDs to drivers. Should | |
124 | * only be used for PCI and ISA-PnP devices */ | |
125 | static DEFINE_PCI_DEVICE_TABLE(cb_pcimdas_pci_table) = { | |
126 | {PCI_VENDOR_ID_COMPUTERBOARDS, 0x0056, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | |
127 | {0} | |
128 | }; | |
129 | ||
130 | MODULE_DEVICE_TABLE(pci, cb_pcimdas_pci_table); | |
131 | ||
132 | #define N_BOARDS 1 // Max number of boards supported | |
133 | ||
134 | /* | |
135 | * Useful for shorthand access to the particular board structure | |
136 | */ | |
137 | #define thisboard ((const cb_pcimdas_board *)dev->board_ptr) | |
138 | ||
139 | /* this structure is for data unique to this hardware driver. If | |
140 | several hardware drivers keep similar information in this structure, | |
71b5f4f1 | 141 | feel free to suggest moving the variable to the struct comedi_device struct. */ |
f4bd8abb RB |
142 | typedef struct { |
143 | int data; | |
144 | ||
145 | // would be useful for a PCI device | |
146 | struct pci_dev *pci_dev; | |
147 | ||
148 | //base addresses | |
149 | unsigned long BADR0; | |
150 | unsigned long BADR1; | |
151 | unsigned long BADR2; | |
152 | unsigned long BADR3; | |
153 | unsigned long BADR4; | |
154 | ||
155 | /* Used for AO readback */ | |
790c5541 | 156 | unsigned int ao_readback[2]; |
f4bd8abb RB |
157 | |
158 | // Used for DIO | |
159 | unsigned short int port_a; // copy of BADR4+0 | |
160 | unsigned short int port_b; // copy of BADR4+1 | |
161 | unsigned short int port_c; // copy of BADR4+2 | |
162 | unsigned short int dio_mode; // copy of BADR4+3 | |
163 | ||
164 | } cb_pcimdas_private; | |
165 | ||
166 | /* | |
167 | * most drivers define the following macro to make it easy to | |
168 | * access the private structure. | |
169 | */ | |
170 | #define devpriv ((cb_pcimdas_private *)dev->private) | |
171 | ||
172 | /* | |
173 | * The comedi_driver structure tells the Comedi core module | |
174 | * which functions to call to configure/deconfigure (attach/detach) | |
175 | * the board, and also about the kernel module that contains | |
176 | * the device code. | |
177 | */ | |
71b5f4f1 BP |
178 | static int cb_pcimdas_attach(struct comedi_device * dev, comedi_devconfig * it); |
179 | static int cb_pcimdas_detach(struct comedi_device * dev); | |
f4bd8abb RB |
180 | static comedi_driver driver_cb_pcimdas = { |
181 | driver_name:"cb_pcimdas", | |
182 | module:THIS_MODULE, | |
183 | attach:cb_pcimdas_attach, | |
184 | detach:cb_pcimdas_detach, | |
185 | }; | |
186 | ||
71b5f4f1 | 187 | static int cb_pcimdas_ai_rinsn(struct comedi_device * dev, comedi_subdevice * s, |
790c5541 | 188 | comedi_insn * insn, unsigned int * data); |
71b5f4f1 | 189 | static int cb_pcimdas_ao_winsn(struct comedi_device * dev, comedi_subdevice * s, |
790c5541 | 190 | comedi_insn * insn, unsigned int * data); |
71b5f4f1 | 191 | static int cb_pcimdas_ao_rinsn(struct comedi_device * dev, comedi_subdevice * s, |
790c5541 | 192 | comedi_insn * insn, unsigned int * data); |
f4bd8abb RB |
193 | |
194 | /* | |
195 | * Attach is called by the Comedi core to configure the driver | |
196 | * for a particular board. If you specified a board_name array | |
197 | * in the driver structure, dev->board_ptr contains that | |
198 | * address. | |
199 | */ | |
71b5f4f1 | 200 | static int cb_pcimdas_attach(struct comedi_device * dev, comedi_devconfig * it) |
f4bd8abb RB |
201 | { |
202 | comedi_subdevice *s; | |
203 | struct pci_dev *pcidev; | |
204 | int index; | |
205 | //int i; | |
206 | ||
207 | printk("comedi%d: cb_pcimdas: ", dev->minor); | |
208 | ||
209 | /* | |
210 | * Allocate the private structure area. | |
211 | */ | |
212 | if (alloc_private(dev, sizeof(cb_pcimdas_private)) < 0) | |
213 | return -ENOMEM; | |
214 | ||
215 | /* | |
216 | * Probe the device to determine what device in the series it is. | |
217 | */ | |
218 | printk("\n"); | |
219 | ||
220 | for (pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); | |
221 | pcidev != NULL; | |
222 | pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) { | |
223 | // is it not a computer boards card? | |
224 | if (pcidev->vendor != PCI_VENDOR_ID_COMPUTERBOARDS) | |
225 | continue; | |
226 | // loop through cards supported by this driver | |
227 | for (index = 0; index < N_BOARDS; index++) { | |
228 | if (cb_pcimdas_boards[index].device_id != | |
229 | pcidev->device) | |
230 | continue; | |
231 | // was a particular bus/slot requested? | |
232 | if (it->options[0] || it->options[1]) { | |
233 | // are we on the wrong bus/slot? | |
234 | if (pcidev->bus->number != it->options[0] || | |
235 | PCI_SLOT(pcidev->devfn) != | |
236 | it->options[1]) { | |
237 | continue; | |
238 | } | |
239 | } | |
240 | devpriv->pci_dev = pcidev; | |
241 | dev->board_ptr = cb_pcimdas_boards + index; | |
242 | goto found; | |
243 | } | |
244 | } | |
245 | ||
246 | printk("No supported ComputerBoards/MeasurementComputing card found on " | |
247 | "requested position\n"); | |
248 | return -EIO; | |
249 | ||
250 | found: | |
251 | ||
252 | printk("Found %s on bus %i, slot %i\n", cb_pcimdas_boards[index].name, | |
253 | pcidev->bus->number, PCI_SLOT(pcidev->devfn)); | |
254 | ||
255 | // Warn about non-tested features | |
256 | switch (thisboard->device_id) { | |
257 | case 0x56: | |
258 | break; | |
259 | default: | |
260 | printk("THIS CARD IS UNSUPPORTED.\n" | |
261 | "PLEASE REPORT USAGE TO <mocelet@sucs.org>\n"); | |
262 | }; | |
263 | ||
264 | if (comedi_pci_enable(pcidev, "cb_pcimdas")) { | |
265 | printk(" Failed to enable PCI device and request regions\n"); | |
266 | return -EIO; | |
267 | } | |
268 | ||
269 | devpriv->BADR0 = pci_resource_start(devpriv->pci_dev, 0); | |
270 | devpriv->BADR1 = pci_resource_start(devpriv->pci_dev, 1); | |
271 | devpriv->BADR2 = pci_resource_start(devpriv->pci_dev, 2); | |
272 | devpriv->BADR3 = pci_resource_start(devpriv->pci_dev, 3); | |
273 | devpriv->BADR4 = pci_resource_start(devpriv->pci_dev, 4); | |
274 | ||
275 | #ifdef CBPCIMDAS_DEBUG | |
276 | printk("devpriv->BADR0 = 0x%lx\n", devpriv->BADR0); | |
277 | printk("devpriv->BADR1 = 0x%lx\n", devpriv->BADR1); | |
278 | printk("devpriv->BADR2 = 0x%lx\n", devpriv->BADR2); | |
279 | printk("devpriv->BADR3 = 0x%lx\n", devpriv->BADR3); | |
280 | printk("devpriv->BADR4 = 0x%lx\n", devpriv->BADR4); | |
281 | #endif | |
282 | ||
283 | // Dont support IRQ yet | |
284 | // // get irq | |
285 | // if(comedi_request_irq(devpriv->pci_dev->irq, cb_pcimdas_interrupt, IRQF_SHARED, "cb_pcimdas", dev )) | |
286 | // { | |
287 | // printk(" unable to allocate irq %u\n", devpriv->pci_dev->irq); | |
288 | // return -EINVAL; | |
289 | // } | |
290 | // dev->irq = devpriv->pci_dev->irq; | |
291 | ||
292 | //Initialize dev->board_name | |
293 | dev->board_name = thisboard->name; | |
294 | ||
295 | /* | |
296 | * Allocate the subdevice structures. alloc_subdevice() is a | |
297 | * convenient macro defined in comedidev.h. | |
298 | */ | |
299 | if (alloc_subdevices(dev, 3) < 0) | |
300 | return -ENOMEM; | |
301 | ||
302 | s = dev->subdevices + 0; | |
303 | //dev->read_subdev=s; | |
304 | // analog input subdevice | |
305 | s->type = COMEDI_SUBD_AI; | |
306 | s->subdev_flags = SDF_READABLE | SDF_GROUND; | |
307 | s->n_chan = thisboard->ai_se_chans; | |
308 | s->maxdata = (1 << thisboard->ai_bits) - 1; | |
309 | s->range_table = &range_unknown; | |
310 | s->len_chanlist = 1; // This is the maximum chanlist length that | |
311 | // the board can handle | |
312 | s->insn_read = cb_pcimdas_ai_rinsn; | |
313 | ||
314 | s = dev->subdevices + 1; | |
315 | // analog output subdevice | |
316 | s->type = COMEDI_SUBD_AO; | |
317 | s->subdev_flags = SDF_WRITABLE; | |
318 | s->n_chan = thisboard->ao_nchan; | |
319 | s->maxdata = 1 << thisboard->ao_bits; | |
320 | s->range_table = &range_unknown; //ranges are hardware settable, but not software readable. | |
321 | s->insn_write = &cb_pcimdas_ao_winsn; | |
322 | s->insn_read = &cb_pcimdas_ao_rinsn; | |
323 | ||
324 | s = dev->subdevices + 2; | |
325 | /* digital i/o subdevice */ | |
326 | if (thisboard->has_dio) { | |
327 | subdev_8255_init(dev, s, NULL, devpriv->BADR4); | |
328 | } else { | |
329 | s->type = COMEDI_SUBD_UNUSED; | |
330 | } | |
331 | ||
332 | printk("attached\n"); | |
333 | ||
334 | return 1; | |
335 | } | |
336 | ||
337 | /* | |
338 | * _detach is called to deconfigure a device. It should deallocate | |
339 | * resources. | |
340 | * This function is also called when _attach() fails, so it should be | |
341 | * careful not to release resources that were not necessarily | |
342 | * allocated by _attach(). dev->private and dev->subdevices are | |
343 | * deallocated automatically by the core. | |
344 | */ | |
71b5f4f1 | 345 | static int cb_pcimdas_detach(struct comedi_device * dev) |
f4bd8abb RB |
346 | { |
347 | #ifdef CBPCIMDAS_DEBUG | |
348 | if (devpriv) { | |
349 | printk("devpriv->BADR0 = 0x%lx\n", devpriv->BADR0); | |
350 | printk("devpriv->BADR1 = 0x%lx\n", devpriv->BADR1); | |
351 | printk("devpriv->BADR2 = 0x%lx\n", devpriv->BADR2); | |
352 | printk("devpriv->BADR3 = 0x%lx\n", devpriv->BADR3); | |
353 | printk("devpriv->BADR4 = 0x%lx\n", devpriv->BADR4); | |
354 | } | |
355 | #endif | |
356 | printk("comedi%d: cb_pcimdas: remove\n", dev->minor); | |
357 | if (dev->irq) | |
358 | comedi_free_irq(dev->irq, dev); | |
359 | if (devpriv) { | |
360 | if (devpriv->pci_dev) { | |
361 | if (devpriv->BADR0) { | |
362 | comedi_pci_disable(devpriv->pci_dev); | |
363 | } | |
364 | pci_dev_put(devpriv->pci_dev); | |
365 | } | |
366 | } | |
367 | ||
368 | return 0; | |
369 | } | |
370 | ||
371 | /* | |
372 | * "instructions" read/write data in "one-shot" or "software-triggered" | |
373 | * mode. | |
374 | */ | |
71b5f4f1 | 375 | static int cb_pcimdas_ai_rinsn(struct comedi_device * dev, comedi_subdevice * s, |
790c5541 | 376 | comedi_insn * insn, unsigned int * data) |
f4bd8abb RB |
377 | { |
378 | int n, i; | |
379 | unsigned int d; | |
380 | unsigned int busy; | |
381 | int chan = CR_CHAN(insn->chanspec); | |
382 | unsigned short chanlims; | |
383 | int maxchans; | |
384 | ||
385 | // only support sw initiated reads from a single channel | |
386 | ||
387 | //check channel number | |
388 | if ((inb(devpriv->BADR3 + 2) & 0x20) == 0) //differential mode | |
389 | maxchans = thisboard->ai_diff_chans; | |
390 | else | |
391 | maxchans = thisboard->ai_se_chans; | |
392 | ||
393 | if (chan > (maxchans - 1)) | |
394 | return -ETIMEDOUT; //*** Wrong error code. Fixme. | |
395 | ||
396 | //configure for sw initiated read | |
397 | d = inb(devpriv->BADR3 + 5); | |
398 | if ((d & 0x03) > 0) { //only reset if needed. | |
399 | d = d & 0xfd; | |
400 | outb(d, devpriv->BADR3 + 5); | |
401 | } | |
402 | outb(0x01, devpriv->BADR3 + 6); //set bursting off, conversions on | |
403 | outb(0x00, devpriv->BADR3 + 7); //set range to 10V. UP/BP is controlled by a switch on the board | |
404 | ||
405 | // write channel limits to multiplexer, set Low (bits 0-3) and High (bits 4-7) channels to chan. | |
406 | chanlims = chan | (chan << 4); | |
407 | outb(chanlims, devpriv->BADR3 + 0); | |
408 | ||
409 | /* convert n samples */ | |
410 | for (n = 0; n < insn->n; n++) { | |
411 | /* trigger conversion */ | |
412 | outw(0, devpriv->BADR2 + 0); | |
413 | ||
414 | #define TIMEOUT 1000 //typically takes 5 loops on a lightly loaded Pentium 100MHz, | |
415 | //this is likely to be 100 loops on a 2GHz machine, so set 1000 as the limit. | |
416 | ||
417 | /* wait for conversion to end */ | |
418 | for (i = 0; i < TIMEOUT; i++) { | |
419 | busy = inb(devpriv->BADR3 + 2) & 0x80; | |
420 | if (!busy) | |
421 | break; | |
422 | } | |
423 | if (i == TIMEOUT) { | |
424 | printk("timeout\n"); | |
425 | return -ETIMEDOUT; | |
426 | } | |
427 | /* read data */ | |
428 | d = inw(devpriv->BADR2 + 0); | |
429 | ||
430 | /* mangle the data as necessary */ | |
431 | //d ^= 1<<(thisboard->ai_bits-1); // 16 bit data from ADC, so no mangle needed. | |
432 | ||
433 | data[n] = d; | |
434 | } | |
435 | ||
436 | /* return the number of samples read/written */ | |
437 | return n; | |
438 | } | |
439 | ||
71b5f4f1 | 440 | static int cb_pcimdas_ao_winsn(struct comedi_device * dev, comedi_subdevice * s, |
790c5541 | 441 | comedi_insn * insn, unsigned int * data) |
f4bd8abb RB |
442 | { |
443 | int i; | |
444 | int chan = CR_CHAN(insn->chanspec); | |
445 | ||
446 | /* Writing a list of values to an AO channel is probably not | |
447 | * very useful, but that's how the interface is defined. */ | |
448 | for (i = 0; i < insn->n; i++) { | |
449 | switch (chan) { | |
450 | case 0: | |
451 | outw(data[i] & 0x0FFF, devpriv->BADR2 + DAC0_OFFSET); | |
452 | break; | |
453 | case 1: | |
454 | outw(data[i] & 0x0FFF, devpriv->BADR2 + DAC1_OFFSET); | |
455 | break; | |
456 | default: | |
457 | return -1; | |
458 | } | |
459 | devpriv->ao_readback[chan] = data[i]; | |
460 | } | |
461 | ||
462 | /* return the number of samples read/written */ | |
463 | return i; | |
464 | } | |
465 | ||
466 | /* AO subdevices should have a read insn as well as a write insn. | |
467 | * Usually this means copying a value stored in devpriv. */ | |
71b5f4f1 | 468 | static int cb_pcimdas_ao_rinsn(struct comedi_device * dev, comedi_subdevice * s, |
790c5541 | 469 | comedi_insn * insn, unsigned int * data) |
f4bd8abb RB |
470 | { |
471 | int i; | |
472 | int chan = CR_CHAN(insn->chanspec); | |
473 | ||
474 | for (i = 0; i < insn->n; i++) | |
475 | data[i] = devpriv->ao_readback[chan]; | |
476 | ||
477 | return i; | |
478 | } | |
479 | ||
480 | /* | |
481 | * A convenient macro that defines init_module() and cleanup_module(), | |
482 | * as necessary. | |
483 | */ | |
484 | COMEDI_PCI_INITCLEANUP(driver_cb_pcimdas, cb_pcimdas_pci_table); |