]> bbs.cooldavid.org Git - net-next-2.6.git/blame - drivers/staging/comedi/drivers/adl_pci9111.c
Staging: comedi: Remove pci9111_private_data typedef
[net-next-2.6.git] / drivers / staging / comedi / drivers / adl_pci9111.c
CommitLineData
8cb9b9fb
EP
1/*
2
3 comedi/drivers/adl_pci9111.c
4
5 Hardware driver for PCI9111 ADLink cards:
6
7 PCI-9111HR
8
9 Copyright (C) 2002-2005 Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr>
10
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24*/
25
26/*
27Driver: adl_pci9111
28Description: Adlink PCI-9111HR
29Author: Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr>
30Devices: [ADLink] PCI-9111HR (adl_pci9111)
31Status: experimental
32
33Supports:
34
35 - ai_insn read
36 - ao_insn read/write
37 - di_insn read
38 - do_insn read/write
39 - ai_do_cmd mode with the following sources:
40
41 - start_src TRIG_NOW
42 - scan_begin_src TRIG_FOLLOW TRIG_TIMER TRIG_EXT
43 - convert_src TRIG_TIMER TRIG_EXT
44 - scan_end_src TRIG_COUNT
45 - stop_src TRIG_COUNT TRIG_NONE
46
47 The scanned channels must be consecutive and start from 0. They must
48 all have the same range and aref.
49
50Configuration options:
51
52 [0] - PCI bus number (optional)
53 [1] - PCI slot number (optional)
54
55 If bus/slot is not specified, the first available PCI
56 device will be used.
57
58*/
59
60/*
61CHANGELOG:
62
63 2005/02/17 Extend AI streaming capabilities. Now, scan_begin_arg can be
64 a multiple of chanlist_len*convert_arg.
65 2002/02/19 Fixed the two's complement conversion in pci9111_(hr_)ai_get_data.
66 2002/02/18 Added external trigger support for analog input.
67
68TODO:
69
70 - Really test implemented functionality.
71 - Add support for the PCI-9111DG with a probe routine to identify the card type
72 (perhaps with the help of the channel number readback of the A/D Data register).
73 - Add external multiplexer support.
74
75*/
76
77#include "../comedidev.h"
78
79#include <linux/delay.h>
80
81#include "8253.h"
82#include "comedi_pci.h"
83#include "comedi_fc.h"
84
85#define PCI9111_DRIVER_NAME "adl_pci9111"
86#define PCI9111_HR_DEVICE_ID 0x9111
87
88// TODO: Add other pci9111 board id
89
90#define PCI9111_IO_RANGE 0x0100
91
92#define PCI9111_FIFO_HALF_SIZE 512
93
94#define PCI9111_AI_CHANNEL_NBR 16
95
96#define PCI9111_AI_RESOLUTION 12
97#define PCI9111_AI_RESOLUTION_MASK 0x0FFF
98#define PCI9111_AI_RESOLUTION_2_CMP_BIT 0x0800
99
100#define PCI9111_HR_AI_RESOLUTION 16
101#define PCI9111_HR_AI_RESOLUTION_MASK 0xFFFF
102#define PCI9111_HR_AI_RESOLUTION_2_CMP_BIT 0x8000
103
104#define PCI9111_AI_ACQUISITION_PERIOD_MIN_NS 10000
105#define PCI9111_AO_CHANNEL_NBR 1
106#define PCI9111_AO_RESOLUTION 12
107#define PCI9111_AO_RESOLUTION_MASK 0x0FFF
108#define PCI9111_DI_CHANNEL_NBR 16
109#define PCI9111_DO_CHANNEL_NBR 16
110#define PCI9111_DO_MASK 0xFFFF
111
112#define PCI9111_RANGE_SETTING_DELAY 10
113#define PCI9111_AI_INSTANT_READ_UDELAY_US 2
114#define PCI9111_AI_INSTANT_READ_TIMEOUT 100
115
116#define PCI9111_8254_CLOCK_PERIOD_NS 500
117
118#define PCI9111_8254_COUNTER_0 0x00
119#define PCI9111_8254_COUNTER_1 0x40
120#define PCI9111_8254_COUNTER_2 0x80
121#define PCI9111_8254_COUNTER_LATCH 0x00
122#define PCI9111_8254_READ_LOAD_LSB_ONLY 0x10
123#define PCI9111_8254_READ_LOAD_MSB_ONLY 0x20
124#define PCI9111_8254_READ_LOAD_LSB_MSB 0x30
125#define PCI9111_8254_MODE_0 0x00
126#define PCI9111_8254_MODE_1 0x02
127#define PCI9111_8254_MODE_2 0x04
128#define PCI9111_8254_MODE_3 0x06
129#define PCI9111_8254_MODE_4 0x08
130#define PCI9111_8254_MODE_5 0x0A
131#define PCI9111_8254_BINARY_COUNTER 0x00
132#define PCI9111_8254_BCD_COUNTER 0x01
133
134/* IO address map */
135
136#define PCI9111_REGISTER_AD_FIFO_VALUE 0x00 // AD Data stored in FIFO
137#define PCI9111_REGISTER_DA_OUTPUT 0x00
138#define PCI9111_REGISTER_DIGITAL_IO 0x02
139#define PCI9111_REGISTER_EXTENDED_IO_PORTS 0x04
140#define PCI9111_REGISTER_AD_CHANNEL_CONTROL 0x06 // Channel selection
141#define PCI9111_REGISTER_AD_CHANNEL_READBACK 0x06
142#define PCI9111_REGISTER_INPUT_SIGNAL_RANGE 0x08
143#define PCI9111_REGISTER_RANGE_STATUS_READBACK 0x08
144#define PCI9111_REGISTER_TRIGGER_MODE_CONTROL 0x0A
145#define PCI9111_REGISTER_AD_MODE_INTERRUPT_READBACK 0x0A
146#define PCI9111_REGISTER_SOFTWARE_TRIGGER 0x0E
147#define PCI9111_REGISTER_INTERRUPT_CONTROL 0x0C
148#define PCI9111_REGISTER_8254_COUNTER_0 0x40
149#define PCI9111_REGISTER_8254_COUNTER_1 0x42
150#define PCI9111_REGISTER_8254_COUNTER_2 0X44
151#define PCI9111_REGISTER_8254_CONTROL 0x46
152#define PCI9111_REGISTER_INTERRUPT_CLEAR 0x48
153
154#define PCI9111_TRIGGER_MASK 0x0F
155#define PCI9111_PTRG_OFF (0 << 3)
156#define PCI9111_PTRG_ON (1 << 3)
157#define PCI9111_EITS_EXTERNAL (1 << 2)
158#define PCI9111_EITS_INTERNAL (0 << 2)
159#define PCI9111_TPST_SOFTWARE_TRIGGER (0 << 1)
160#define PCI9111_TPST_TIMER_PACER (1 << 1)
161#define PCI9111_ASCAN_ON (1 << 0)
162#define PCI9111_ASCAN_OFF (0 << 0)
163
164#define PCI9111_ISC0_SET_IRQ_ON_ENDING_OF_AD_CONVERSION (0 << 0)
165#define PCI9111_ISC0_SET_IRQ_ON_FIFO_HALF_FULL (1 << 0)
166#define PCI9111_ISC1_SET_IRQ_ON_TIMER_TICK (0 << 1)
167#define PCI9111_ISC1_SET_IRQ_ON_EXT_TRG (1 << 1)
168#define PCI9111_FFEN_SET_FIFO_ENABLE (0 << 2)
169#define PCI9111_FFEN_SET_FIFO_DISABLE (1 << 2)
170
171#define PCI9111_CHANNEL_MASK 0x0F
172
173#define PCI9111_RANGE_MASK 0x07
174#define PCI9111_FIFO_EMPTY_MASK 0x10
175#define PCI9111_FIFO_HALF_FULL_MASK 0x20
176#define PCI9111_FIFO_FULL_MASK 0x40
177#define PCI9111_AD_BUSY_MASK 0x80
178
179#define PCI9111_IO_BASE dev->iobase
180
181/*
182 * Define inlined function
183 */
184
185#define pci9111_trigger_and_autoscan_get() \
186 (inb(PCI9111_IO_BASE+PCI9111_REGISTER_AD_MODE_INTERRUPT_READBACK)&0x0F)
187
188#define pci9111_trigger_and_autoscan_set(flags) \
189 outb(flags,PCI9111_IO_BASE+PCI9111_REGISTER_TRIGGER_MODE_CONTROL)
190
191#define pci9111_interrupt_and_fifo_get() \
192 ((inb(PCI9111_IO_BASE+PCI9111_REGISTER_AD_MODE_INTERRUPT_READBACK) >> 4) &0x03)
193
194#define pci9111_interrupt_and_fifo_set(flags) \
195 outb(flags,PCI9111_IO_BASE+PCI9111_REGISTER_INTERRUPT_CONTROL)
196
197#define pci9111_interrupt_clear() \
198 outb(0,PCI9111_IO_BASE+PCI9111_REGISTER_INTERRUPT_CLEAR)
199
200#define pci9111_software_trigger() \
201 outb(0,PCI9111_IO_BASE+PCI9111_REGISTER_SOFTWARE_TRIGGER)
202
203#define pci9111_fifo_reset() \
204 outb(PCI9111_FFEN_SET_FIFO_ENABLE,PCI9111_IO_BASE+PCI9111_REGISTER_INTERRUPT_CONTROL); \
205 outb(PCI9111_FFEN_SET_FIFO_DISABLE,PCI9111_IO_BASE+PCI9111_REGISTER_INTERRUPT_CONTROL); \
206 outb(PCI9111_FFEN_SET_FIFO_ENABLE,PCI9111_IO_BASE+PCI9111_REGISTER_INTERRUPT_CONTROL)
207
208#define pci9111_is_fifo_full() \
209 ((inb(PCI9111_IO_BASE+PCI9111_REGISTER_RANGE_STATUS_READBACK)& \
210 PCI9111_FIFO_FULL_MASK)==0)
211
212#define pci9111_is_fifo_half_full() \
213 ((inb(PCI9111_IO_BASE+PCI9111_REGISTER_RANGE_STATUS_READBACK)& \
214 PCI9111_FIFO_HALF_FULL_MASK)==0)
215
216#define pci9111_is_fifo_empty() \
217 ((inb(PCI9111_IO_BASE+PCI9111_REGISTER_RANGE_STATUS_READBACK)& \
218 PCI9111_FIFO_EMPTY_MASK)==0)
219
220#define pci9111_ai_channel_set(channel) \
221 outb((channel)&PCI9111_CHANNEL_MASK,PCI9111_IO_BASE+PCI9111_REGISTER_AD_CHANNEL_CONTROL)
222
223#define pci9111_ai_channel_get() \
224 inb(PCI9111_IO_BASE+PCI9111_REGISTER_AD_CHANNEL_READBACK)&PCI9111_CHANNEL_MASK
225
226#define pci9111_ai_range_set(range) \
227 outb((range)&PCI9111_RANGE_MASK,PCI9111_IO_BASE+PCI9111_REGISTER_INPUT_SIGNAL_RANGE)
228
229#define pci9111_ai_range_get() \
230 inb(PCI9111_IO_BASE+PCI9111_REGISTER_RANGE_STATUS_READBACK)&PCI9111_RANGE_MASK
231
232#define pci9111_ai_get_data() \
233 ((inw(PCI9111_IO_BASE+PCI9111_REGISTER_AD_FIFO_VALUE)>>4)&PCI9111_AI_RESOLUTION_MASK) \
234 ^ PCI9111_AI_RESOLUTION_2_CMP_BIT
235
236#define pci9111_hr_ai_get_data() \
237 (inw(PCI9111_IO_BASE+PCI9111_REGISTER_AD_FIFO_VALUE) & PCI9111_HR_AI_RESOLUTION_MASK) \
238 ^ PCI9111_HR_AI_RESOLUTION_2_CMP_BIT
239
240#define pci9111_ao_set_data(data) \
241 outw(data&PCI9111_AO_RESOLUTION_MASK,PCI9111_IO_BASE+PCI9111_REGISTER_DA_OUTPUT)
242
243#define pci9111_di_get_bits() \
244 inw(PCI9111_IO_BASE+PCI9111_REGISTER_DIGITAL_IO)
245
246#define pci9111_do_set_bits(bits) \
247 outw(bits,PCI9111_IO_BASE+PCI9111_REGISTER_DIGITAL_IO)
248
249#define pci9111_8254_control_set(flags) \
250 outb(flags,PCI9111_IO_BASE+PCI9111_REGISTER_8254_CONTROL)
251
252#define pci9111_8254_counter_0_set(data) \
253 outb(data & 0xFF, PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_0); \
254 outb( (data >> 8) & 0xFF, PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_0)
255
256#define pci9111_8254_counter_1_set(data) \
257 outb(data & 0xFF, PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_1); \
258 outb( (data >> 8) & 0xFF, PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_1)
259
260#define pci9111_8254_counter_2_set(data) \
261 outb(data & 0xFF, PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_2); \
262 outb( (data >> 8) & 0xFF, PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_2)
263
264//
265// Function prototypes
266//
267
0707bb04 268static int pci9111_attach(struct comedi_device * dev, struct comedi_devconfig * it);
71b5f4f1 269static int pci9111_detach(struct comedi_device * dev);
34c43922 270static void pci9111_ai_munge(struct comedi_device * dev, struct comedi_subdevice * s,
8cb9b9fb
EP
271 void *data, unsigned int num_bytes, unsigned int start_chan_index);
272
9ced1de6 273static const struct comedi_lrange pci9111_hr_ai_range = {
8cb9b9fb
EP
274 5,
275 {
276 BIP_RANGE(10),
277 BIP_RANGE(5),
278 BIP_RANGE(2.5),
279 BIP_RANGE(1.25),
280 BIP_RANGE(0.625)
281 }
282};
283
284static DEFINE_PCI_DEVICE_TABLE(pci9111_pci_table) = {
285 {PCI_VENDOR_ID_ADLINK, PCI9111_HR_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0,
286 0, 0},
287 //{ PCI_VENDOR_ID_ADLINK, PCI9111_HG_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
288 {0}
289};
290
291MODULE_DEVICE_TABLE(pci, pci9111_pci_table);
292
293//
294// Board specification structure
295//
296
940579fb 297struct pci9111_board {
8cb9b9fb
EP
298 const char *name; // driver name
299 int device_id;
300 int ai_channel_nbr; // num of A/D chans
301 int ao_channel_nbr; // num of D/A chans
302 int ai_resolution; // resolution of A/D
303 int ai_resolution_mask;
304 int ao_resolution; // resolution of D/A
305 int ao_resolution_mask;
9ced1de6
BP
306 const struct comedi_lrange *ai_range_list; // rangelist for A/D
307 const struct comedi_lrange *ao_range_list; // rangelist for D/A
8cb9b9fb 308 unsigned int ai_acquisition_period_min_ns;
940579fb 309};
8cb9b9fb 310
940579fb 311static const struct pci9111_board pci9111_boards[] = {
8cb9b9fb
EP
312 {
313 name: "pci9111_hr",
314 device_id:PCI9111_HR_DEVICE_ID,
315 ai_channel_nbr:PCI9111_AI_CHANNEL_NBR,
316 ao_channel_nbr:PCI9111_AO_CHANNEL_NBR,
317 ai_resolution:PCI9111_HR_AI_RESOLUTION,
318 ai_resolution_mask:PCI9111_HR_AI_RESOLUTION_MASK,
319 ao_resolution:PCI9111_AO_RESOLUTION,
320 ao_resolution_mask:PCI9111_AO_RESOLUTION_MASK,
321 ai_range_list:&pci9111_hr_ai_range,
322 ao_range_list:&range_bipolar10,
323 ai_acquisition_period_min_ns:PCI9111_AI_ACQUISITION_PERIOD_MIN_NS}
324};
325
326#define pci9111_board_nbr \
940579fb 327 (sizeof(pci9111_boards)/sizeof(struct pci9111_board))
8cb9b9fb 328
139dfbdf 329static struct comedi_driver pci9111_driver = {
8cb9b9fb
EP
330 driver_name:PCI9111_DRIVER_NAME,
331 module:THIS_MODULE,
332 attach:pci9111_attach,
333 detach:pci9111_detach,
334};
335
336COMEDI_PCI_INITCLEANUP(pci9111_driver, pci9111_pci_table);
337
338//
339// Private data structure
340//
341
c350fa19 342struct pci9111_private_data {
8cb9b9fb
EP
343 struct pci_dev *pci_device;
344 unsigned long io_range; // PCI6503 io range
345
346 unsigned long lcr_io_base; // Local configuration register base address
347 unsigned long lcr_io_range;
348
349 int stop_counter;
350 int stop_is_none;
351
352 unsigned int scan_delay;
353 unsigned int chanlist_len;
354 unsigned int chunk_counter;
355 unsigned int chunk_num_samples;
356
357 int ao_readback; // Last written analog output data
358
359 int timer_divisor_1; // Divisor values for the 8254 timer pacer
360 int timer_divisor_2;
361
362 int is_valid; // Is device valid
363
790c5541 364 short ai_bounce_buffer[2 * PCI9111_FIFO_HALF_SIZE];
c350fa19 365};
8cb9b9fb 366
c350fa19 367#define dev_private ((struct pci9111_private_data *)dev->private)
8cb9b9fb
EP
368
369// ------------------------------------------------------------------
370//
371// PLX9050 SECTION
372//
373// ------------------------------------------------------------------
374
375#define PLX9050_REGISTER_INTERRUPT_CONTROL 0x4c
376
377#define PLX9050_LINTI1_ENABLE (1 << 0)
378#define PLX9050_LINTI1_ACTIVE_HIGH (1 << 1)
379#define PLX9050_LINTI1_STATUS (1 << 2)
380#define PLX9050_LINTI2_ENABLE (1 << 3)
381#define PLX9050_LINTI2_ACTIVE_HIGH (1 << 4)
382#define PLX9050_LINTI2_STATUS (1 << 5)
383#define PLX9050_PCI_INTERRUPT_ENABLE (1 << 6)
384#define PLX9050_SOFTWARE_INTERRUPT (1 << 7)
385
386static void plx9050_interrupt_control(unsigned long io_base,
387 bool LINTi1_enable,
388 bool LINTi1_active_high,
389 bool LINTi2_enable, bool LINTi2_active_high, bool interrupt_enable)
390{
391 int flags = 0;
392
393 if (LINTi1_enable)
394 flags |= PLX9050_LINTI1_ENABLE;
395 if (LINTi1_active_high)
396 flags |= PLX9050_LINTI1_ACTIVE_HIGH;
397 if (LINTi2_enable)
398 flags |= PLX9050_LINTI2_ENABLE;
399 if (LINTi2_active_high)
400 flags |= PLX9050_LINTI2_ACTIVE_HIGH;
401
402 if (interrupt_enable)
403 flags |= PLX9050_PCI_INTERRUPT_ENABLE;
404
405 outb(flags, io_base + PLX9050_REGISTER_INTERRUPT_CONTROL);
406}
407
408// ------------------------------------------------------------------
409//
410// MISCELLANEOUS SECTION
411//
412// ------------------------------------------------------------------
413
414//
415// 8254 timer
416//
417
71b5f4f1 418static void pci9111_timer_set(struct comedi_device * dev)
8cb9b9fb
EP
419{
420 pci9111_8254_control_set(PCI9111_8254_COUNTER_0 |
421 PCI9111_8254_READ_LOAD_LSB_MSB |
422 PCI9111_8254_MODE_0 | PCI9111_8254_BINARY_COUNTER);
423
424 pci9111_8254_control_set(PCI9111_8254_COUNTER_1 |
425 PCI9111_8254_READ_LOAD_LSB_MSB |
426 PCI9111_8254_MODE_2 | PCI9111_8254_BINARY_COUNTER);
427
428 pci9111_8254_control_set(PCI9111_8254_COUNTER_2 |
429 PCI9111_8254_READ_LOAD_LSB_MSB |
430 PCI9111_8254_MODE_2 | PCI9111_8254_BINARY_COUNTER);
431
432 comedi_udelay(1);
433
434 pci9111_8254_counter_2_set(dev_private->timer_divisor_2);
435 pci9111_8254_counter_1_set(dev_private->timer_divisor_1);
436}
437
438typedef enum {
439 software,
440 timer_pacer,
441 external
442} pci9111_trigger_sources;
443
71b5f4f1 444static void pci9111_trigger_source_set(struct comedi_device * dev,
8cb9b9fb
EP
445 pci9111_trigger_sources source)
446{
447 int flags;
448
449 flags = pci9111_trigger_and_autoscan_get() & 0x09;
450
451 switch (source) {
452 case software:
453 flags |= PCI9111_EITS_INTERNAL | PCI9111_TPST_SOFTWARE_TRIGGER;
454 break;
455
456 case timer_pacer:
457 flags |= PCI9111_EITS_INTERNAL | PCI9111_TPST_TIMER_PACER;
458 break;
459
460 case external:
461 flags |= PCI9111_EITS_EXTERNAL;
462 break;
463 }
464
465 pci9111_trigger_and_autoscan_set(flags);
466}
467
71b5f4f1 468static void pci9111_pretrigger_set(struct comedi_device * dev, bool pretrigger)
8cb9b9fb
EP
469{
470 int flags;
471
472 flags = pci9111_trigger_and_autoscan_get() & 0x07;
473
474 if (pretrigger)
475 flags |= PCI9111_PTRG_ON;
476
477 pci9111_trigger_and_autoscan_set(flags);
478}
479
71b5f4f1 480static void pci9111_autoscan_set(struct comedi_device * dev, bool autoscan)
8cb9b9fb
EP
481{
482 int flags;
483
484 flags = pci9111_trigger_and_autoscan_get() & 0x0e;
485
486 if (autoscan)
487 flags |= PCI9111_ASCAN_ON;
488
489 pci9111_trigger_and_autoscan_set(flags);
490}
491
492typedef enum {
493 irq_on_eoc,
494 irq_on_fifo_half_full
495} pci9111_ISC0_sources;
496
497typedef enum {
498 irq_on_timer_tick,
499 irq_on_external_trigger
500} pci9111_ISC1_sources;
501
71b5f4f1 502static void pci9111_interrupt_source_set(struct comedi_device * dev,
8cb9b9fb
EP
503 pci9111_ISC0_sources irq_0_source, pci9111_ISC1_sources irq_1_source)
504{
505 int flags;
506
507 flags = pci9111_interrupt_and_fifo_get() & 0x04;
508
509 if (irq_0_source == irq_on_fifo_half_full)
510 flags |= PCI9111_ISC0_SET_IRQ_ON_FIFO_HALF_FULL;
511
512 if (irq_1_source == irq_on_external_trigger)
513 flags |= PCI9111_ISC1_SET_IRQ_ON_EXT_TRG;
514
515 pci9111_interrupt_and_fifo_set(flags);
516}
517
518// ------------------------------------------------------------------
519//
520// HARDWARE TRIGGERED ANALOG INPUT SECTION
521//
522// ------------------------------------------------------------------
523
524//
525// Cancel analog input autoscan
526//
527
528#undef AI_DO_CMD_DEBUG
529
34c43922 530static int pci9111_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
8cb9b9fb
EP
531{
532 // Disable interrupts
533
534 plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true,
535 true, false);
536
537 pci9111_trigger_source_set(dev, software);
538
539 pci9111_autoscan_set(dev, false);
540
541 pci9111_fifo_reset();
542
543#ifdef AI_DO_CMD_DEBUG
544 printk(PCI9111_DRIVER_NAME ": ai_cancel\n");
545#endif
546
547 return 0;
548}
549
550//
551// Test analog input command
552//
553
554#define pci9111_check_trigger_src(src,flags) \
555 tmp = src; \
556 src &= flags; \
557 if (!src || tmp != src) error++
558
559static int
71b5f4f1 560pci9111_ai_do_cmd_test(struct comedi_device * dev,
ea6d0d4c 561 struct comedi_subdevice * s, struct comedi_cmd * cmd)
8cb9b9fb
EP
562{
563 int tmp;
564 int error = 0;
565 int range, reference;
566 int i;
940579fb 567 struct pci9111_board *board = (struct pci9111_board *) dev->board_ptr;
8cb9b9fb
EP
568
569 // Step 1 : check if trigger are trivialy valid
570
571 pci9111_check_trigger_src(cmd->start_src, TRIG_NOW);
572 pci9111_check_trigger_src(cmd->scan_begin_src,
573 TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT);
574 pci9111_check_trigger_src(cmd->convert_src, TRIG_TIMER | TRIG_EXT);
575 pci9111_check_trigger_src(cmd->scan_end_src, TRIG_COUNT);
576 pci9111_check_trigger_src(cmd->stop_src, TRIG_COUNT | TRIG_NONE);
577
578 if (error)
579 return 1;
580
581 // step 2 : make sure trigger sources are unique and mutually compatible
582
583 if (cmd->start_src != TRIG_NOW)
584 error++;
585
586 if ((cmd->scan_begin_src != TRIG_TIMER) &&
587 (cmd->scan_begin_src != TRIG_FOLLOW) &&
588 (cmd->scan_begin_src != TRIG_EXT))
589 error++;
590
591 if ((cmd->convert_src != TRIG_TIMER) && (cmd->convert_src != TRIG_EXT)) {
592 error++;
593 }
594 if ((cmd->convert_src == TRIG_TIMER) &&
595 !((cmd->scan_begin_src == TRIG_TIMER) ||
596 (cmd->scan_begin_src == TRIG_FOLLOW))) {
597 error++;
598 }
599 if ((cmd->convert_src == TRIG_EXT) &&
600 !((cmd->scan_begin_src == TRIG_EXT) ||
601 (cmd->scan_begin_src == TRIG_FOLLOW))) {
602 error++;
603 }
604
605 if (cmd->scan_end_src != TRIG_COUNT)
606 error++;
607 if ((cmd->stop_src != TRIG_COUNT) && (cmd->stop_src != TRIG_NONE))
608 error++;
609
610 if (error)
611 return 2;
612
613 // Step 3 : make sure arguments are trivialy compatible
614
615 if (cmd->chanlist_len < 1) {
616 cmd->chanlist_len = 1;
617 error++;
618 }
619
620 if (cmd->chanlist_len > board->ai_channel_nbr) {
621 cmd->chanlist_len = board->ai_channel_nbr;
622 error++;
623 }
624
625 if ((cmd->start_src == TRIG_NOW) && (cmd->start_arg != 0)) {
626 cmd->start_arg = 0;
627 error++;
628 }
629
630 if ((cmd->convert_src == TRIG_TIMER) &&
631 (cmd->convert_arg < board->ai_acquisition_period_min_ns)) {
632 cmd->convert_arg = board->ai_acquisition_period_min_ns;
633 error++;
634 }
635 if ((cmd->convert_src == TRIG_EXT) && (cmd->convert_arg != 0)) {
636 cmd->convert_arg = 0;
637 error++;
638 }
639
640 if ((cmd->scan_begin_src == TRIG_TIMER) &&
641 (cmd->scan_begin_arg < board->ai_acquisition_period_min_ns)) {
642 cmd->scan_begin_arg = board->ai_acquisition_period_min_ns;
643 error++;
644 }
645 if ((cmd->scan_begin_src == TRIG_FOLLOW) && (cmd->scan_begin_arg != 0)) {
646 cmd->scan_begin_arg = 0;
647 error++;
648 }
649 if ((cmd->scan_begin_src == TRIG_EXT) && (cmd->scan_begin_arg != 0)) {
650 cmd->scan_begin_arg = 0;
651 error++;
652 }
653
654 if ((cmd->scan_end_src == TRIG_COUNT) &&
655 (cmd->scan_end_arg != cmd->chanlist_len)) {
656 cmd->scan_end_arg = cmd->chanlist_len;
657 error++;
658 }
659
660 if ((cmd->stop_src == TRIG_COUNT) && (cmd->stop_arg < 1)) {
661 cmd->stop_arg = 1;
662 error++;
663 }
664 if ((cmd->stop_src == TRIG_NONE) && (cmd->stop_arg != 0)) {
665 cmd->stop_arg = 0;
666 error++;
667 }
668
669 if (error)
670 return 3;
671
672 // Step 4 : fix up any arguments
673
674 if (cmd->convert_src == TRIG_TIMER) {
675 tmp = cmd->convert_arg;
676 i8253_cascade_ns_to_timer_2div(PCI9111_8254_CLOCK_PERIOD_NS,
677 &(dev_private->timer_divisor_1),
678 &(dev_private->timer_divisor_2),
679 &(cmd->convert_arg), cmd->flags & TRIG_ROUND_MASK);
680 if (tmp != cmd->convert_arg)
681 error++;
682 }
683 // There's only one timer on this card, so the scan_begin timer must
684 // be a multiple of chanlist_len*convert_arg
685
686 if (cmd->scan_begin_src == TRIG_TIMER) {
687
688 unsigned int scan_begin_min;
689 unsigned int scan_begin_arg;
690 unsigned int scan_factor;
691
692 scan_begin_min = cmd->chanlist_len * cmd->convert_arg;
693
694 if (cmd->scan_begin_arg != scan_begin_min) {
695 if (scan_begin_min < cmd->scan_begin_arg) {
696 scan_factor =
697 cmd->scan_begin_arg / scan_begin_min;
698 scan_begin_arg = scan_factor * scan_begin_min;
699 if (cmd->scan_begin_arg != scan_begin_arg) {
700 cmd->scan_begin_arg = scan_begin_arg;
701 error++;
702 }
703 } else {
704 cmd->scan_begin_arg = scan_begin_min;
705 error++;
706 }
707 }
708 }
709
710 if (error)
711 return 4;
712
713 // Step 5 : check channel list
714
715 if (cmd->chanlist) {
716
717 range = CR_RANGE(cmd->chanlist[0]);
718 reference = CR_AREF(cmd->chanlist[0]);
719
720 if (cmd->chanlist_len > 1) {
721 for (i = 0; i < cmd->chanlist_len; i++) {
722 if (CR_CHAN(cmd->chanlist[i]) != i) {
723 comedi_error(dev,
724 "entries in chanlist must be consecutive "
725 "channels,counting upwards from 0\n");
726 error++;
727 }
728 if (CR_RANGE(cmd->chanlist[i]) != range) {
729 comedi_error(dev,
730 "entries in chanlist must all have the same gain\n");
731 error++;
732 }
733 if (CR_AREF(cmd->chanlist[i]) != reference) {
734 comedi_error(dev,
735 "entries in chanlist must all have the same reference\n");
736 error++;
737 }
738 }
739 } else {
740 if ((CR_CHAN(cmd->chanlist[0]) >
741 (board->ai_channel_nbr - 1))
742 || (CR_CHAN(cmd->chanlist[0]) < 0)) {
743 comedi_error(dev,
744 "channel number is out of limits\n");
745 error++;
746 }
747 }
748 }
749
750 if (error)
751 return 5;
752
753 return 0;
754
755}
756
757//
758// Analog input command
759//
760
34c43922 761static int pci9111_ai_do_cmd(struct comedi_device * dev, struct comedi_subdevice * subdevice)
8cb9b9fb 762{
ea6d0d4c 763 struct comedi_cmd *async_cmd = &subdevice->async->cmd;
8cb9b9fb
EP
764
765 if (!dev->irq) {
766 comedi_error(dev,
767 "no irq assigned for PCI9111, cannot do hardware conversion");
768 return -1;
769 }
770 // Set channel scan limit
771 //
772 // PCI9111 allows only scanning from channel 0 to channel n
773 //
774 // TODO: handle the case of an external multiplexer
775 //
776
777 if (async_cmd->chanlist_len > 1) {
778 pci9111_ai_channel_set((async_cmd->chanlist_len) - 1);
779 pci9111_autoscan_set(dev, true);
780 } else {
781 pci9111_ai_channel_set(CR_CHAN(async_cmd->chanlist[0]));
782 pci9111_autoscan_set(dev, false);
783 }
784
785 // Set gain
786 //
787 // This is the same gain on every channel
788 //
789
790 pci9111_ai_range_set(CR_RANGE(async_cmd->chanlist[0]));
791
792 /* Set counter */
793
794 switch (async_cmd->stop_src) {
795 case TRIG_COUNT:
796 dev_private->stop_counter =
797 async_cmd->stop_arg * async_cmd->chanlist_len;
798 dev_private->stop_is_none = 0;
799 break;
800
801 case TRIG_NONE:
802 dev_private->stop_counter = 0;
803 dev_private->stop_is_none = 1;
804 break;
805
806 default:
807 comedi_error(dev, "Invalid stop trigger");
808 return -1;
809 }
810
811 // Set timer pacer
812
813 dev_private->scan_delay = 0;
814 switch (async_cmd->convert_src) {
815 case TRIG_TIMER:
816 i8253_cascade_ns_to_timer_2div(PCI9111_8254_CLOCK_PERIOD_NS,
817 &(dev_private->timer_divisor_1),
818 &(dev_private->timer_divisor_2),
819 &(async_cmd->convert_arg),
820 async_cmd->flags & TRIG_ROUND_MASK);
821#ifdef AI_DO_CMD_DEBUG
822 printk(PCI9111_DRIVER_NAME ": divisors = %d, %d\n",
823 dev_private->timer_divisor_1,
824 dev_private->timer_divisor_2);
825#endif
826
827 pci9111_trigger_source_set(dev, software);
828 pci9111_timer_set(dev);
829 pci9111_fifo_reset();
830 pci9111_interrupt_source_set(dev, irq_on_fifo_half_full,
831 irq_on_timer_tick);
832 pci9111_trigger_source_set(dev, timer_pacer);
833 plx9050_interrupt_control(dev_private->lcr_io_base, true, true,
834 false, true, true);
835
836 dev_private->scan_delay =
837 (async_cmd->scan_begin_arg / (async_cmd->convert_arg *
838 async_cmd->chanlist_len)) - 1;
839
840 break;
841
842 case TRIG_EXT:
843
844 pci9111_trigger_source_set(dev, external);
845 pci9111_fifo_reset();
846 pci9111_interrupt_source_set(dev, irq_on_fifo_half_full,
847 irq_on_timer_tick);
848 plx9050_interrupt_control(dev_private->lcr_io_base, true, true,
849 false, true, true);
850
851 break;
852
853 default:
854 comedi_error(dev, "Invalid convert trigger");
855 return -1;
856 }
857
858 dev_private->stop_counter *= (1 + dev_private->scan_delay);
859 dev_private->chanlist_len = async_cmd->chanlist_len;
860 dev_private->chunk_counter = 0;
861 dev_private->chunk_num_samples =
862 dev_private->chanlist_len * (1 + dev_private->scan_delay);
863
864#ifdef AI_DO_CMD_DEBUG
865 printk(PCI9111_DRIVER_NAME ": start interruptions!\n");
866 printk(PCI9111_DRIVER_NAME ": trigger source = %2x\n",
867 pci9111_trigger_and_autoscan_get());
868 printk(PCI9111_DRIVER_NAME ": irq source = %2x\n",
869 pci9111_interrupt_and_fifo_get());
870 printk(PCI9111_DRIVER_NAME ": ai_do_cmd\n");
871 printk(PCI9111_DRIVER_NAME ": stop counter = %d\n",
872 dev_private->stop_counter);
873 printk(PCI9111_DRIVER_NAME ": scan delay = %d\n",
874 dev_private->scan_delay);
875 printk(PCI9111_DRIVER_NAME ": chanlist_len = %d\n",
876 dev_private->chanlist_len);
877 printk(PCI9111_DRIVER_NAME ": chunk num samples = %d\n",
878 dev_private->chunk_num_samples);
879#endif
880
881 return 0;
882}
883
34c43922 884static void pci9111_ai_munge(struct comedi_device * dev, struct comedi_subdevice * s,
8cb9b9fb
EP
885 void *data, unsigned int num_bytes, unsigned int start_chan_index)
886{
790c5541
BP
887 unsigned int i, num_samples = num_bytes / sizeof(short);
888 short *array = data;
8cb9b9fb 889 int resolution =
940579fb 890 ((struct pci9111_board *) dev->board_ptr)->ai_resolution;
8cb9b9fb
EP
891
892 for (i = 0; i < num_samples; i++) {
893 if (resolution == PCI9111_HR_AI_RESOLUTION)
894 array[i] =
895 (array[i] & PCI9111_HR_AI_RESOLUTION_MASK) ^
896 PCI9111_HR_AI_RESOLUTION_2_CMP_BIT;
897 else
898 array[i] =
899 ((array[i] >> 4) & PCI9111_AI_RESOLUTION_MASK) ^
900 PCI9111_AI_RESOLUTION_2_CMP_BIT;
901 }
902}
903
904// ------------------------------------------------------------------
905//
906// INTERRUPT SECTION
907//
908// ------------------------------------------------------------------
909
910#undef INTERRUPT_DEBUG
911
912static irqreturn_t pci9111_interrupt(int irq, void *p_device PT_REGS_ARG)
913{
71b5f4f1 914 struct comedi_device *dev = p_device;
34c43922 915 struct comedi_subdevice *subdevice = dev->read_subdev;
d163679c 916 struct comedi_async *async;
8cb9b9fb
EP
917 unsigned long irq_flags;
918 unsigned char intcsr;
919
920 if (!dev->attached) {
921 // Ignore interrupt before device fully attached.
922 // Might not even have allocated subdevices yet!
923 return IRQ_NONE;
924 }
925
926 async = subdevice->async;
927
928 comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
929
930 // Check if we are source of interrupt
931 intcsr = inb(dev_private->lcr_io_base +
932 PLX9050_REGISTER_INTERRUPT_CONTROL);
933 if (!(((intcsr & PLX9050_PCI_INTERRUPT_ENABLE) != 0)
934 && (((intcsr & (PLX9050_LINTI1_ENABLE |
935 PLX9050_LINTI1_STATUS))
936 ==
937 (PLX9050_LINTI1_ENABLE |
938 PLX9050_LINTI1_STATUS))
939 || ((intcsr & (PLX9050_LINTI2_ENABLE |
940 PLX9050_LINTI2_STATUS))
941 ==
942 (PLX9050_LINTI2_ENABLE |
943 PLX9050_LINTI2_STATUS))))) {
944 // Not the source of the interrupt.
945 // (N.B. not using PLX9050_SOFTWARE_INTERRUPT)
946 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
947 return IRQ_NONE;
948 }
949
950 if ((intcsr & (PLX9050_LINTI1_ENABLE | PLX9050_LINTI1_STATUS)) ==
951 (PLX9050_LINTI1_ENABLE | PLX9050_LINTI1_STATUS)) {
952 // Interrupt comes from fifo_half-full signal
953
954 if (pci9111_is_fifo_full()) {
955 comedi_spin_unlock_irqrestore(&dev->spinlock,
956 irq_flags);
957 comedi_error(dev, PCI9111_DRIVER_NAME " fifo overflow");
958 pci9111_interrupt_clear();
959 pci9111_ai_cancel(dev, subdevice);
960 async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
961 comedi_event(dev, subdevice);
962
963 return IRQ_HANDLED;
964 }
965
966 if (pci9111_is_fifo_half_full()) {
967 unsigned int num_samples;
968 unsigned int bytes_written = 0;
969
970#ifdef INTERRUPT_DEBUG
971 printk(PCI9111_DRIVER_NAME ": fifo is half full\n");
972#endif
973
974 num_samples =
975 PCI9111_FIFO_HALF_SIZE >
976 dev_private->stop_counter
977 && !dev_private->stop_is_none ? dev_private->
978 stop_counter : PCI9111_FIFO_HALF_SIZE;
979 insw(PCI9111_IO_BASE + PCI9111_REGISTER_AD_FIFO_VALUE,
980 dev_private->ai_bounce_buffer, num_samples);
981
982 if (dev_private->scan_delay < 1) {
983 bytes_written =
984 cfc_write_array_to_buffer(subdevice,
985 dev_private->ai_bounce_buffer,
790c5541 986 num_samples * sizeof(short));
8cb9b9fb
EP
987 } else {
988 int position = 0;
989 int to_read;
990
991 while (position < num_samples) {
992 if (dev_private->chunk_counter <
993 dev_private->chanlist_len) {
994 to_read =
995 dev_private->
996 chanlist_len -
997 dev_private->
998 chunk_counter;
999
1000 if (to_read >
1001 num_samples - position)
1002 to_read =
1003 num_samples -
1004 position;
1005
1006 bytes_written +=
1007 cfc_write_array_to_buffer
1008 (subdevice,
1009 dev_private->
1010 ai_bounce_buffer +
1011 position,
1012 to_read *
790c5541 1013 sizeof(short));
8cb9b9fb
EP
1014 } else {
1015 to_read =
1016 dev_private->
1017 chunk_num_samples -
1018 dev_private->
1019 chunk_counter;
1020 if (to_read >
1021 num_samples - position)
1022 to_read =
1023 num_samples -
1024 position;
1025
1026 bytes_written +=
790c5541 1027 sizeof(short) *
8cb9b9fb
EP
1028 to_read;
1029 }
1030
1031 position += to_read;
1032 dev_private->chunk_counter += to_read;
1033
1034 if (dev_private->chunk_counter >=
1035 dev_private->chunk_num_samples)
1036 dev_private->chunk_counter = 0;
1037 }
1038 }
1039
1040 dev_private->stop_counter -=
790c5541 1041 bytes_written / sizeof(short);
8cb9b9fb
EP
1042 }
1043 }
1044
1045 if ((dev_private->stop_counter == 0) && (!dev_private->stop_is_none)) {
1046 async->events |= COMEDI_CB_EOA;
1047 pci9111_ai_cancel(dev, subdevice);
1048 }
1049
1050 /* Very important, otherwise another interrupt request will be inserted
1051 * and will cause driver hangs on processing interrupt event. */
1052
1053 pci9111_interrupt_clear();
1054
1055 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
1056
1057 comedi_event(dev, subdevice);
1058
1059 return IRQ_HANDLED;
1060}
1061
1062// ------------------------------------------------------------------
1063//
1064// INSTANT ANALOG INPUT OUTPUT SECTION
1065//
1066// ------------------------------------------------------------------
1067
1068//
1069// analog instant input
1070//
1071
1072#undef AI_INSN_DEBUG
1073
71b5f4f1 1074static int pci9111_ai_insn_read(struct comedi_device * dev,
90035c08 1075 struct comedi_subdevice * subdevice, struct comedi_insn * insn, unsigned int * data)
8cb9b9fb
EP
1076{
1077 int resolution =
940579fb 1078 ((struct pci9111_board *) dev->board_ptr)->ai_resolution;
8cb9b9fb
EP
1079
1080 int timeout, i;
1081
1082#ifdef AI_INSN_DEBUG
1083 printk(PCI9111_DRIVER_NAME ": ai_insn set c/r/n = %2x/%2x/%2x\n",
1084 CR_CHAN((&insn->chanspec)[0]),
1085 CR_RANGE((&insn->chanspec)[0]), insn->n);
1086#endif
1087
1088 pci9111_ai_channel_set(CR_CHAN((&insn->chanspec)[0]));
1089
1090 if ((pci9111_ai_range_get()) != CR_RANGE((&insn->chanspec)[0])) {
1091 pci9111_ai_range_set(CR_RANGE((&insn->chanspec)[0]));
1092 }
1093
1094 pci9111_fifo_reset();
1095
1096 for (i = 0; i < insn->n; i++) {
1097 pci9111_software_trigger();
1098
1099 timeout = PCI9111_AI_INSTANT_READ_TIMEOUT;
1100
1101 while (timeout--) {
1102 if (!pci9111_is_fifo_empty())
1103 goto conversion_done;
1104 }
1105
1106 comedi_error(dev, "A/D read timeout");
1107 data[i] = 0;
1108 pci9111_fifo_reset();
1109 return -ETIME;
1110
1111 conversion_done:
1112
1113 if (resolution == PCI9111_HR_AI_RESOLUTION) {
1114 data[i] = pci9111_hr_ai_get_data();
1115 } else {
1116 data[i] = pci9111_ai_get_data();
1117 }
1118 }
1119
1120#ifdef AI_INSN_DEBUG
1121 printk(PCI9111_DRIVER_NAME ": ai_insn get c/r/t = %2x/%2x/%2x\n",
1122 pci9111_ai_channel_get(),
1123 pci9111_ai_range_get(), pci9111_trigger_and_autoscan_get());
1124#endif
1125
1126 return i;
1127}
1128
1129//
1130// Analog instant output
1131//
1132
1133static int
71b5f4f1 1134pci9111_ao_insn_write(struct comedi_device * dev,
90035c08 1135 struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
8cb9b9fb
EP
1136{
1137 int i;
1138
1139 for (i = 0; i < insn->n; i++) {
1140 pci9111_ao_set_data(data[i]);
1141 dev_private->ao_readback = data[i];
1142 }
1143
1144 return i;
1145}
1146
1147//
1148// Analog output readback
1149//
1150
71b5f4f1 1151static int pci9111_ao_insn_read(struct comedi_device * dev,
90035c08 1152 struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
8cb9b9fb
EP
1153{
1154 int i;
1155
1156 for (i = 0; i < insn->n; i++) {
1157 data[i] = dev_private->ao_readback & PCI9111_AO_RESOLUTION_MASK;
1158 }
1159
1160 return i;
1161}
1162
1163// ------------------------------------------------------------------
1164//
1165// DIGITAL INPUT OUTPUT SECTION
1166//
1167// ------------------------------------------------------------------
1168
1169//
1170// Digital inputs
1171//
1172
71b5f4f1 1173static int pci9111_di_insn_bits(struct comedi_device * dev,
90035c08 1174 struct comedi_subdevice * subdevice, struct comedi_insn * insn, unsigned int * data)
8cb9b9fb 1175{
790c5541 1176 unsigned int bits;
8cb9b9fb
EP
1177
1178 bits = pci9111_di_get_bits();
1179 data[1] = bits;
1180
1181 return 2;
1182}
1183
1184//
1185// Digital outputs
1186//
1187
71b5f4f1 1188static int pci9111_do_insn_bits(struct comedi_device * dev,
90035c08 1189 struct comedi_subdevice * subdevice, struct comedi_insn * insn, unsigned int * data)
8cb9b9fb 1190{
790c5541 1191 unsigned int bits;
8cb9b9fb
EP
1192
1193 // Only set bits that have been masked
1194 // data[0] = mask
1195 // data[1] = bit state
1196
1197 data[0] &= PCI9111_DO_MASK;
1198
1199 bits = subdevice->state;
1200 bits &= ~data[0];
1201 bits |= data[0] & data[1];
1202 subdevice->state = bits;
1203
1204 pci9111_do_set_bits(bits);
1205
1206 data[1] = bits;
1207
1208 return 2;
1209}
1210
1211// ------------------------------------------------------------------
1212//
1213// INITIALISATION SECTION
1214//
1215// ------------------------------------------------------------------
1216
1217//
1218// Reset device
1219//
1220
71b5f4f1 1221static int pci9111_reset(struct comedi_device * dev)
8cb9b9fb
EP
1222{
1223 // Set trigger source to software
1224
1225 plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true,
1226 true, false);
1227
1228 pci9111_trigger_source_set(dev, software);
1229 pci9111_pretrigger_set(dev, false);
1230 pci9111_autoscan_set(dev, false);
1231
1232 // Reset 8254 chip
1233
1234 dev_private->timer_divisor_1 = 0;
1235 dev_private->timer_divisor_2 = 0;
1236
1237 pci9111_timer_set(dev);
1238
1239 return 0;
1240}
1241
1242//
1243// Attach
1244//
1245// - Register PCI device
1246// - Declare device driver capability
1247//
1248
0707bb04 1249static int pci9111_attach(struct comedi_device * dev, struct comedi_devconfig * it)
8cb9b9fb 1250{
34c43922 1251 struct comedi_subdevice *subdevice;
8cb9b9fb
EP
1252 unsigned long io_base, io_range, lcr_io_base, lcr_io_range;
1253 struct pci_dev *pci_device;
1254 int error, i;
940579fb 1255 const struct pci9111_board *board;
8cb9b9fb 1256
c350fa19 1257 if (alloc_private(dev, sizeof(struct pci9111_private_data)) < 0) {
8cb9b9fb
EP
1258 return -ENOMEM;
1259 }
1260 //
1261 // Probe the device to determine what device in the series it is.
1262 //
1263
1264 printk("comedi%d: " PCI9111_DRIVER_NAME " driver\n", dev->minor);
1265
1266 for (pci_device = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
1267 pci_device != NULL;
1268 pci_device =
1269 pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pci_device)) {
1270 if (pci_device->vendor == PCI_VENDOR_ID_ADLINK) {
1271 for (i = 0; i < pci9111_board_nbr; i++) {
1272 if (pci9111_boards[i].device_id ==
1273 pci_device->device) {
1274 // was a particular bus/slot requested?
1275 if ((it->options[0] != 0)
1276 || (it->options[1] != 0)) {
1277 // are we on the wrong bus/slot?
1278 if (pci_device->bus->number !=
1279 it->options[0]
1280 || PCI_SLOT(pci_device->
1281 devfn) !=
1282 it->options[1]) {
1283 continue;
1284 }
1285 }
1286
1287 dev->board_ptr = pci9111_boards + i;
940579fb 1288 board = (struct pci9111_board *) dev->
8cb9b9fb
EP
1289 board_ptr;
1290 dev_private->pci_device = pci_device;
1291 goto found;
1292 }
1293 }
1294 }
1295 }
1296
1297 printk("comedi%d: no supported board found! (req. bus/slot : %d/%d)\n",
1298 dev->minor, it->options[0], it->options[1]);
1299 return -EIO;
1300
1301 found:
1302
1303 printk("comedi%d: found %s (b:s:f=%d:%d:%d) , irq=%d\n",
1304 dev->minor,
1305 pci9111_boards[i].name,
1306 pci_device->bus->number,
1307 PCI_SLOT(pci_device->devfn),
1308 PCI_FUNC(pci_device->devfn), pci_device->irq);
1309
1310 // TODO: Warn about non-tested boards.
1311
1312 switch (board->device_id) {
1313 };
1314
1315 // Read local configuration register base address [PCI_BASE_ADDRESS #1].
1316
1317 lcr_io_base = pci_resource_start(pci_device, 1);
1318 lcr_io_range = pci_resource_len(pci_device, 1);
1319
1320 printk("comedi%d: local configuration registers at address 0x%4lx [0x%4lx]\n", dev->minor, lcr_io_base, lcr_io_range);
1321
1322 // Enable PCI device and request regions
1323 if (comedi_pci_enable(pci_device, PCI9111_DRIVER_NAME) < 0) {
1324 printk("comedi%d: Failed to enable PCI device and request regions\n", dev->minor);
1325 return -EIO;
1326 }
1327 // Read PCI6308 register base address [PCI_BASE_ADDRESS #2].
1328
1329 io_base = pci_resource_start(pci_device, 2);
1330 io_range = pci_resource_len(pci_device, 2);
1331
1332 printk("comedi%d: 6503 registers at address 0x%4lx [0x%4lx]\n",
1333 dev->minor, io_base, io_range);
1334
1335 dev->iobase = io_base;
1336 dev->board_name = board->name;
1337 dev_private->io_range = io_range;
1338 dev_private->is_valid = 0;
1339 dev_private->lcr_io_base = lcr_io_base;
1340 dev_private->lcr_io_range = lcr_io_range;
1341
1342 pci9111_reset(dev);
1343
1344 // Irq setup
1345
1346 dev->irq = 0;
1347 if (pci_device->irq > 0) {
1348 if (comedi_request_irq(pci_device->irq,
1349 pci9111_interrupt,
1350 IRQF_SHARED, PCI9111_DRIVER_NAME, dev) != 0) {
1351 printk("comedi%d: unable to allocate irq %u\n",
1352 dev->minor, pci_device->irq);
1353 return -EINVAL;
1354 }
1355 }
1356 dev->irq = pci_device->irq;
1357
1358 //
1359 // TODO: Add external multiplexer setup (according to option[2]).
1360 //
1361
1362 if ((error = alloc_subdevices(dev, 4)) < 0)
1363 return error;
1364
1365 subdevice = dev->subdevices + 0;
1366 dev->read_subdev = subdevice;
1367
1368 subdevice->type = COMEDI_SUBD_AI;
1369 subdevice->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_CMD_READ;
1370
1371 //
1372 // TODO: Add external multiplexer data
1373 //
1374 // if (devpriv->usemux) { subdevice->n_chan = devpriv->usemux; }
1375 // else { subdevice->n_chan = this_board->n_aichan; }
1376 //
1377
1378 subdevice->n_chan = board->ai_channel_nbr;
1379 subdevice->maxdata = board->ai_resolution_mask;
1380 subdevice->len_chanlist = board->ai_channel_nbr;
1381 subdevice->range_table = board->ai_range_list;
1382 subdevice->cancel = pci9111_ai_cancel;
1383 subdevice->insn_read = pci9111_ai_insn_read;
1384 subdevice->do_cmdtest = pci9111_ai_do_cmd_test;
1385 subdevice->do_cmd = pci9111_ai_do_cmd;
1386 subdevice->munge = pci9111_ai_munge;
1387
1388 subdevice = dev->subdevices + 1;
1389 subdevice->type = COMEDI_SUBD_AO;
1390 subdevice->subdev_flags = SDF_WRITABLE | SDF_COMMON;
1391 subdevice->n_chan = board->ao_channel_nbr;
1392 subdevice->maxdata = board->ao_resolution_mask;
1393 subdevice->len_chanlist = board->ao_channel_nbr;
1394 subdevice->range_table = board->ao_range_list;
1395 subdevice->insn_write = pci9111_ao_insn_write;
1396 subdevice->insn_read = pci9111_ao_insn_read;
1397
1398 subdevice = dev->subdevices + 2;
1399 subdevice->type = COMEDI_SUBD_DI;
1400 subdevice->subdev_flags = SDF_READABLE;
1401 subdevice->n_chan = PCI9111_DI_CHANNEL_NBR;
1402 subdevice->maxdata = 1;
1403 subdevice->range_table = &range_digital;
1404 subdevice->insn_bits = pci9111_di_insn_bits;
1405
1406 subdevice = dev->subdevices + 3;
1407 subdevice->type = COMEDI_SUBD_DO;
1408 subdevice->subdev_flags = SDF_READABLE | SDF_WRITABLE;
1409 subdevice->n_chan = PCI9111_DO_CHANNEL_NBR;
1410 subdevice->maxdata = 1;
1411 subdevice->range_table = &range_digital;
1412 subdevice->insn_bits = pci9111_do_insn_bits;
1413
1414 dev_private->is_valid = 1;
1415
1416 return 0;
1417}
1418
1419//
1420// Detach
1421//
1422
71b5f4f1 1423static int pci9111_detach(struct comedi_device * dev)
8cb9b9fb
EP
1424{
1425 // Reset device
1426
1427 if (dev->private != 0) {
1428 if (dev_private->is_valid)
1429 pci9111_reset(dev);
1430
1431 }
1432 // Release previously allocated irq
1433
1434 if (dev->irq != 0) {
1435 comedi_free_irq(dev->irq, dev);
1436 }
1437
1438 if (dev_private != 0 && dev_private->pci_device != 0) {
1439 if (dev->iobase) {
1440 comedi_pci_disable(dev_private->pci_device);
1441 }
1442 pci_dev_put(dev_private->pci_device);
1443 }
1444
1445 return 0;
1446}